/* \r
* WinBoard.c -- Windows NT front end to XBoard\r
- * $Id$\r
+ * $Id: winboard.c,v 2.3 2003/11/25 05:25:20 mann Exp $
*\r
* Copyright 1991 by Digital Equipment Corporation, Maynard, Massachusetts.\r
* Enhancements Copyright 1992-2001 Free Software Foundation, Inc.\r
\r
#include <stdio.h>\r
#include <stdlib.h>\r
+#include <time.h>
#include <malloc.h>\r
#include <sys/stat.h>\r
#include <fcntl.h>\r
#include "wsockerr.h"\r
#include "defaults.h"\r
\r
+int myrandom(void);
+void mysrandom(unsigned int seed);
+
typedef struct {\r
ChessSquare piece; \r
POINT pos; /* window coordinates of current pos */\r
char *firstChessProgramNames;\r
char *secondChessProgramNames;\r
\r
-#define ARG_MAX 20000\r
+#define ARG_MAX 64*1024 /* [AS] For Roger Brown's very long list! */
\r
#define PALETTESIZE 256\r
\r
static int lastSizing = 0;\r
static int prevStderrPort;\r
\r
+/* [AS] Support for background textures */
+#define BACK_TEXTURE_MODE_DISABLED 0
+#define BACK_TEXTURE_MODE_PLAIN 1
+#define BACK_TEXTURE_MODE_FULL_RANDOM 2
+
+static HBITMAP liteBackTexture = NULL;
+static HBITMAP darkBackTexture = NULL;
+static int liteBackTextureMode = BACK_TEXTURE_MODE_PLAIN;
+static int darkBackTextureMode = BACK_TEXTURE_MODE_PLAIN;
+static int backTextureSquareSize = 0;
+static struct { int x; int y; int mode; } backTextureSquareInfo[BOARD_SIZE][BOARD_SIZE];
+
#if __GNUC__ && !defined(_winmajor)\r
#define oldDialog 0 /* cygwin doesn't define _winmajor; mingw does */\r
#else\r
void ParseIcsTextMenu(char *icsTextMenuString);\r
VOID PopUpMoveDialog(char firstchar);\r
VOID UpdateSampleText(HWND hDlg, int id, MyColorizeAttribs *mca);\r
+int NewGameFRC();
\r
/*\r
* Setting "frozen" should disable all user input other than deleting\r
}\r
InitAppData(lpCmdLine); /* Get run-time parameters */\r
if (appData.debugMode) {\r
- debugFP = fopen("winboard.debug", "w");\r
+ debugFP = fopen(appData.nameOfDebugFile, "w");
setbuf(debugFP, NULL);\r
}\r
\r
InitMenuChecks();\r
buttonCount = GetSystemMetrics(SM_CMOUSEBUTTONS);\r
\r
+ /* [AS] Load textures if specified */
+ ZeroMemory( &backTextureSquareInfo, sizeof(backTextureSquareInfo) );
+
+ if( appData.liteBackTextureFile && appData.liteBackTextureFile[0] != NULLCHAR && appData.liteBackTextureFile[0] != '*' ) {
+ liteBackTexture = LoadImage( 0, appData.liteBackTextureFile, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );
+ liteBackTextureMode = appData.liteBackTextureMode;
+
+ if (liteBackTexture == NULL && appData.debugMode) {
+ fprintf( debugFP, "Unable to load lite texture bitmap '%s'\n", appData.liteBackTextureFile );
+ }
+ }
+
+ if( appData.darkBackTextureFile && appData.darkBackTextureFile[0] != NULLCHAR && appData.darkBackTextureFile[0] != '*' ) {
+ darkBackTexture = LoadImage( 0, appData.darkBackTextureFile, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );
+ darkBackTextureMode = appData.darkBackTextureMode;
+
+ if (darkBackTexture == NULL && appData.debugMode) {
+ fprintf( debugFP, "Unable to load dark texture bitmap '%s'\n", appData.darkBackTextureFile );
+ }
+ }
+
+ mysrandom( (unsigned) time(NULL) );
+
/* Make a console window if needed */\r
if (appData.icsActive) {\r
ConsoleCreate();\r
\r
SetWindowPos(hwndMain, alwaysOnTop ? HWND_TOPMOST : HWND_NOTOPMOST,\r
0, 0, 0, 0, SWP_NOMOVE|SWP_NOSIZE);\r
+
+ /* [AS] Disable the FRC stuff if not playing the proper variant */
+ if( gameInfo.variant != VariantFischeRandom ) {
+ EnableMenuItem( GetMenu(hwndMain), IDM_NewGameFRC, MF_GRAYED );
+ }
+
if (hwndConsole) {\r
#if AOT_CONSOLE\r
SetWindowPos(hwndConsole, alwaysOnTop ? HWND_TOPMOST : HWND_NOTOPMOST,\r
{ "initialMode", ArgString, (LPVOID) &appData.initialMode, FALSE },\r
{ "mode", ArgString, (LPVOID) &appData.initialMode, FALSE },\r
{ "variant", ArgString, (LPVOID) &appData.variant, FALSE },\r
- { "firstProtocolVersion", ArgInt, (LPVOID) &appData.firstProtocolVersion,\r
- FALSE },\r
- { "secondProtocolVersion", ArgInt, (LPVOID) &appData.secondProtocolVersion,\r
- FALSE },\r
+ { "firstProtocolVersion", ArgInt, (LPVOID) &appData.firstProtocolVersion, FALSE },
+ { "secondProtocolVersion", ArgInt, (LPVOID) &appData.secondProtocolVersion,FALSE },
{ "showButtonBar", ArgBoolean, (LPVOID) &appData.showButtonBar, TRUE },\r
{ "buttons", ArgTrue, (LPVOID) &appData.showButtonBar, FALSE },\r
{ "xbuttons", ArgFalse, (LPVOID) &appData.showButtonBar, FALSE },\r
{ "-buttons", ArgFalse, (LPVOID) &appData.showButtonBar, FALSE },\r
+ /* [AS] New features */
+ { "firstScoreAbs", ArgBoolean, (LPVOID) &appData.firstScoreIsAbsolute, TRUE },
+ { "secondScoreAbs", ArgBoolean, (LPVOID) &appData.secondScoreIsAbsolute, TRUE },
+ { "pgnExtendedInfo", ArgBoolean, (LPVOID) &appData.saveExtendedInfoInPGN, TRUE },
+ { "hideThinkingFromHuman", ArgBoolean, (LPVOID) &appData.hideThinkingFromHuman, TRUE },
+ { "liteBackTextureFile", ArgString, (LPVOID) &appData.liteBackTextureFile, TRUE },
+ { "darkBackTextureFile", ArgString, (LPVOID) &appData.darkBackTextureFile, TRUE },
+ { "liteBackTextureMode", ArgInt, (LPVOID) &appData.liteBackTextureMode, TRUE },
+ { "darkBackTextureMode", ArgInt, (LPVOID) &appData.darkBackTextureMode, TRUE },
+ { "renderPiecesWithFont", ArgString, (LPVOID) &appData.renderPiecesWithFont, TRUE },
+ { "fontPieceToCharTable", ArgString, (LPVOID) &appData.fontToPieceTable, TRUE },
+ { "fontPieceBackColorWhite", ArgColor, (LPVOID) &appData.fontBackColorWhite, TRUE },
+ { "fontPieceForeColorWhite", ArgColor, (LPVOID) &appData.fontForeColorWhite, TRUE },
+ { "fontPieceBackColorBlack", ArgColor, (LPVOID) &appData.fontBackColorBlack, TRUE },
+ { "fontPieceForeColorBlack", ArgColor, (LPVOID) &appData.fontForeColorBlack, TRUE },
+ { "fontPieceSize", ArgInt, (LPVOID) &appData.fontPieceSize, TRUE },
+ { "overrideLineGap", ArgInt, (LPVOID) &appData.overrideLineGap, TRUE },
+ { "adjudicateLossThreshold", ArgInt, (LPVOID) &appData.adjudicateLossThreshold, TRUE },
+ { "delayBeforeQuit", ArgInt, (LPVOID) &appData.delayBeforeQuit, TRUE },
+ { "delayAfterQuit", ArgInt, (LPVOID) &appData.delayAfterQuit, TRUE },
+ { "nameOfDebugFile", ArgFilename, (LPVOID) &appData.nameOfDebugFile, FALSE },
+ { "debugfile", ArgFilename, (LPVOID) &appData.nameOfDebugFile, FALSE },
+ { "pgnEventHeader", ArgString, (LPVOID) &appData.pgnEventHeader, TRUE },
+ { "defaultFrcPosition", ArgInt, (LPVOID) &appData.defaultFrcPosition, TRUE },
#ifdef ZIPPY\r
{ "zippyTalk", ArgBoolean, (LPVOID) &appData.zippyTalk, FALSE },\r
{ "zt", ArgTrue, (LPVOID) &appData.zippyTalk, FALSE },\r
appData.firstProtocolVersion = PROTOVER;\r
appData.secondProtocolVersion = PROTOVER;\r
appData.showButtonBar = TRUE;\r
+
+ /* [AS] New properties (see comments in header file) */
+ appData.firstScoreIsAbsolute = FALSE;
+ appData.secondScoreIsAbsolute = FALSE;
+ appData.saveExtendedInfoInPGN = FALSE;
+ appData.hideThinkingFromHuman = FALSE;
+ appData.liteBackTextureFile = "";
+ appData.liteBackTextureMode = BACK_TEXTURE_MODE_PLAIN;
+ appData.darkBackTextureFile = "";
+ appData.darkBackTextureMode = BACK_TEXTURE_MODE_PLAIN;
+ appData.renderPiecesWithFont = "";
+ appData.fontToPieceTable = "";
+ appData.fontBackColorWhite = 0;
+ appData.fontForeColorWhite = 0;
+ appData.fontBackColorBlack = 0;
+ appData.fontForeColorBlack = 0;
+ appData.fontPieceSize = 80;
+ appData.overrideLineGap = 1;
+ appData.adjudicateLossThreshold = 0;
+ appData.delayBeforeQuit = 0;
+ appData.delayAfterQuit = 0;
+ appData.nameOfDebugFile = "winboard.debug";
+ appData.pgnEventHeader = "Computer Chess Game";
+ appData.defaultFrcPosition = -1;
+
#ifdef ZIPPY\r
appData.zippyTalk = ZIPPY_TALK;\r
appData.zippyPlay = ZIPPY_PLAY;\r
*\r
\*---------------------------------------------------------------------------*/\r
\r
+/* [AS] Draw square using background texture */
+static void DrawTile( int dx, int dy, int dw, int dh, HDC dst, HDC src, int mode, int sx, int sy )
+{
+ XFORM x;
+
+ if( mode == 0 ) {
+ return; /* Should never happen! */
+ }
+
+ SetGraphicsMode( dst, GM_ADVANCED );
+
+ switch( mode ) {
+ case 1:
+ /* Identity */
+ break;
+ case 2:
+ /* X reflection */
+ x.eM11 = -1.0;
+ x.eM12 = 0;
+ x.eM21 = 0;
+ x.eM22 = 1.0;
+ x.eDx = (FLOAT) dw + dx - 1;
+ x.eDy = 0;
+ dx = 0;
+ SetWorldTransform( dst, &x );
+ break;
+ case 3:
+ /* Y reflection */
+ x.eM11 = 1.0;
+ x.eM12 = 0;
+ x.eM21 = 0;
+ x.eM22 = -1.0;
+ x.eDx = 0;
+ x.eDy = (FLOAT) dh + dy - 1;
+ dy = 0;
+ SetWorldTransform( dst, &x );
+ break;
+ case 4:
+ /* X/Y flip */
+ x.eM11 = 0;
+ x.eM12 = 1.0;
+ x.eM21 = 1.0;
+ x.eM22 = 0;
+ x.eDx = (FLOAT) dx;
+ x.eDy = (FLOAT) dy;
+ dx = 0;
+ dy = 0;
+ SetWorldTransform( dst, &x );
+ break;
+ }
+
+ BitBlt( dst, dx, dy, dw, dh, src, sx, sy, SRCCOPY );
+
+ x.eM11 = 1.0;
+ x.eM12 = 0;
+ x.eM21 = 0;
+ x.eM22 = 1.0;
+ x.eDx = 0;
+ x.eDy = 0;
+ SetWorldTransform( dst, &x );
+
+ ModifyWorldTransform( dst, 0, MWT_IDENTITY );
+}
+
+/* [AS] */
+enum {
+ PM_WP = 0,
+ PM_WN = 1,
+ PM_WB = 2,
+ PM_WR = 3,
+ PM_WQ = 4,
+ PM_WK = 5,
+ PM_BP = 6,
+ PM_BN = 7,
+ PM_BB = 8,
+ PM_BR = 9,
+ PM_BQ = 10,
+ PM_BK = 11
+};
+
+static HFONT hPieceFont = NULL;
+static HBITMAP hPieceMask[12];
+static HBITMAP hPieceFace[12];
+static int fontBitmapSquareSize = 0;
+static char pieceToFontChar[12] = { 'p', 'n', 'b', 'r', 'q', 'k', 'o', 'm', 'v', 't', 'w', 'l' };
+
+static BOOL SetPieceToFontCharTable( const char * map )
+{
+ BOOL result = FALSE;
+
+ if( map != NULL && strlen(map) == 12 ) {
+ int i;
+
+ for( i=0; i<12; i++ ) {
+ pieceToFontChar[i] = map[i];
+ }
+
+ result = TRUE;
+ }
+
+ return result;
+}
+
+static void SetPieceBackground( HDC hdc, COLORREF color, int mode )
+{
+ HBRUSH hbrush;
+ BYTE r1 = GetRValue( color );
+ BYTE g1 = GetGValue( color );
+ BYTE b1 = GetBValue( color );
+ BYTE r2 = r1 / 2;
+ BYTE g2 = g1 / 2;
+ BYTE b2 = b1 / 2;
+ RECT rc;
+
+ /* Create a uniform background first */
+ hbrush = CreateSolidBrush( color );
+ SetRect( &rc, 0, 0, squareSize, squareSize );
+ FillRect( hdc, &rc, hbrush );
+ DeleteObject( hbrush );
+
+ if( mode == 1 ) {
+ /* Vertical gradient, good for pawn, knight and rook, less for queen and king */
+ int steps = squareSize / 2;
+ int i;
+
+ for( i=0; i<steps; i++ ) {
+ BYTE r = r1 - (r1-r2) * i / steps;
+ BYTE g = g1 - (g1-g2) * i / steps;
+ BYTE b = b1 - (b1-b2) * i / steps;
+
+ hbrush = CreateSolidBrush( RGB(r,g,b) );
+ SetRect( &rc, i + squareSize - steps, 0, i + squareSize - steps + 1, squareSize );
+ FillRect( hdc, &rc, hbrush );
+ DeleteObject(hbrush);
+ }
+ }
+ else if( mode == 2 ) {
+ /* Diagonal gradient, good more or less for every piece */
+ POINT triangle[3];
+ HPEN hpen = SelectObject( hdc, GetStockObject(NULL_PEN) );
+ HBRUSH hbrush_old;
+ int steps = squareSize;
+ int i;
+
+ triangle[0].x = squareSize - steps;
+ triangle[0].y = squareSize;
+ triangle[1].x = squareSize;
+ triangle[1].y = squareSize;
+ triangle[2].x = squareSize;
+ triangle[2].y = squareSize - steps;
+
+ for( i=0; i<steps; i++ ) {
+ BYTE r = r1 - (r1-r2) * i / steps;
+ BYTE g = g1 - (g1-g2) * i / steps;
+ BYTE b = b1 - (b1-b2) * i / steps;
+
+ hbrush = CreateSolidBrush( RGB(r,g,b) );
+ hbrush_old = SelectObject( hdc, hbrush );
+ Polygon( hdc, triangle, 3 );
+ SelectObject( hdc, hbrush_old );
+ DeleteObject(hbrush);
+ triangle[0].x++;
+ triangle[2].y++;
+ }
+
+ SelectObject( hdc, hpen );
+ }
+}
+
+/*
+ [AS] The method I use to create the bitmaps it a bit tricky, but it
+ seems to work ok. The main problem here is to find the "inside" of a chess
+ piece: follow the steps as explained below.
+*/
+static void CreatePieceMaskFromFont( HDC hdc_window, HDC hdc, int index )
+{
+ HBITMAP hbm;
+ HBITMAP hbm_old;
+ COLORREF chroma = RGB(0xFF,0x00,0xFF);
+ RECT rc;
+ SIZE sz;
+ POINT pt;
+ int backColor = whitePieceColor;
+ int foreColor = blackPieceColor;
+ int shapeIndex = index < 6 ? index+6 : index;
+
+ if( index < 6 && appData.fontBackColorWhite != appData.fontForeColorWhite ) {
+ backColor = appData.fontBackColorWhite;
+ foreColor = appData.fontForeColorWhite;
+ }
+ else if( index >= 6 && appData.fontBackColorBlack != appData.fontForeColorBlack ) {
+ backColor = appData.fontBackColorBlack;
+ foreColor = appData.fontForeColorBlack;
+ }
+
+ /* Mask */
+ hbm = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );
+
+ hbm_old = SelectObject( hdc, hbm );
+
+ rc.left = 0;
+ rc.top = 0;
+ rc.right = squareSize;
+ rc.bottom = squareSize;
+
+ /* Step 1: background is now black */
+ FillRect( hdc, &rc, GetStockObject(BLACK_BRUSH) );
+
+ GetTextExtentPoint32( hdc, &pieceToFontChar[index], 1, &sz );
+
+ pt.x = (squareSize - sz.cx) / 2;
+ pt.y = (squareSize - sz.cy) / 2;
+
+ SetBkMode( hdc, TRANSPARENT );
+ SetTextColor( hdc, chroma );
+ /* Step 2: the piece has been drawn in purple, there are now black and purple in this bitmap */
+ TextOut( hdc, pt.x, pt.y, &pieceToFontChar[index], 1 );
+
+ SelectObject( hdc, GetStockObject(WHITE_BRUSH) );
+ /* Step 3: the area outside the piece is filled with white */
+ FloodFill( hdc, 0, 0, chroma );
+ SelectObject( hdc, GetStockObject(BLACK_BRUSH) );
+ /*
+ Step 4: this is the tricky part, the area inside the piece is filled with black,
+ but if the start point is not inside the piece we're lost!
+ There should be a better way to do this... if we could create a region or path
+ from the fill operation we would be fine for example.
+ */
+ FloodFill( hdc, squareSize / 2, squareSize / 2, RGB(0xFF,0xFF,0xFF) );
+
+ SetTextColor( hdc, 0 );
+ /*
+ Step 5: some fonts have "disconnected" areas that are skipped by the fill:
+ draw the piece again in black for safety.
+ */
+ TextOut( hdc, pt.x, pt.y, &pieceToFontChar[index], 1 );
+
+ SelectObject( hdc, hbm_old );
+
+ if( hPieceMask[index] != NULL ) {
+ DeleteObject( hPieceMask[index] );
+ }
+
+ hPieceMask[index] = hbm;
+
+ /* Face */
+ hbm = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );
+
+ SelectObject( hdc, hbm );
+
+ {
+ HDC dc1 = CreateCompatibleDC( hdc_window );
+ HDC dc2 = CreateCompatibleDC( hdc_window );
+ HBITMAP bm2 = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );
+
+ SelectObject( dc1, hPieceMask[index] );
+ SelectObject( dc2, bm2 );
+ FillRect( dc2, &rc, GetStockObject(WHITE_BRUSH) );
+ BitBlt( dc2, 0, 0, squareSize, squareSize, dc1, 0, 0, SRCINVERT );
+
+ /*
+ Now dc2 contains the inverse of the piece mask, i.e. a mask that preserves
+ the piece background and deletes (makes transparent) the rest.
+ Thanks to that mask, we are free to paint the background with the greates
+ freedom, as we'll be able to mask off the unwanted parts when finished.
+ We use this, to make gradients and give the pieces a "roundish" look.
+ */
+ SetPieceBackground( hdc, backColor, 2 );
+ BitBlt( hdc, 0, 0, squareSize, squareSize, dc2, 0, 0, SRCAND );
+
+ DeleteDC( dc2 );
+ DeleteDC( dc1 );
+ DeleteObject( bm2 );
+ }
+
+ SetTextColor( hdc, foreColor );
+ TextOut( hdc, pt.x, pt.y, &pieceToFontChar[index], 1 );
+
+ SelectObject( hdc, hbm_old );
+
+ hPieceFace[index] = hbm;
+}
+
+static int TranslatePieceToFontPiece( int piece )
+{
+ switch( piece ) {
+ case BlackPawn:
+ return PM_BP;
+ case BlackKnight:
+ return PM_BN;
+ case BlackBishop:
+ return PM_BB;
+ case BlackRook:
+ return PM_BR;
+ case BlackQueen:
+ return PM_BQ;
+ case BlackKing:
+ return PM_BK;
+ case WhitePawn:
+ return PM_WP;
+ case WhiteKnight:
+ return PM_WN;
+ case WhiteBishop:
+ return PM_WB;
+ case WhiteRook:
+ return PM_WR;
+ case WhiteQueen:
+ return PM_WQ;
+ case WhiteKing:
+ return PM_WK;
+ }
+
+ return 0;
+}
+
+void CreatePiecesFromFont()
+{
+ LOGFONT lf;
+ HDC hdc_window = NULL;
+ HDC hdc = NULL;
+ HFONT hfont_old;
+ int fontHeight;
+ int i;
+
+ if( fontBitmapSquareSize < 0 ) {
+ /* Something went seriously wrong in the past: do not try to recreate fonts! */
+ return;
+ }
+
+ if( appData.renderPiecesWithFont == NULL || appData.renderPiecesWithFont[0] == NULLCHAR || appData.renderPiecesWithFont[0] == '*' ) {
+ fontBitmapSquareSize = -1;
+ return;
+ }
+
+ if( fontBitmapSquareSize != squareSize ) {
+ hdc_window = GetDC( hwndMain );
+ hdc = CreateCompatibleDC( hdc_window );
+
+ if( hPieceFont != NULL ) {
+ DeleteObject( hPieceFont );
+ }
+ else {
+ for( i=0; i<12; i++ ) {
+ hPieceMask[i] = NULL;
+ }
+ }
+
+ fontHeight = 75;
+
+ if( appData.fontPieceSize >= 50 && appData.fontPieceSize <= 150 ) {
+ fontHeight = appData.fontPieceSize;
+ }
+
+ fontHeight = (fontHeight * squareSize) / 100;
+
+ lf.lfHeight = -MulDiv( fontHeight, GetDeviceCaps(hdc, LOGPIXELSY), 72 );
+ lf.lfWidth = 0;
+ lf.lfEscapement = 0;
+ lf.lfOrientation = 0;
+ lf.lfWeight = FW_NORMAL;
+ lf.lfItalic = 0;
+ lf.lfUnderline = 0;
+ lf.lfStrikeOut = 0;
+ lf.lfCharSet = DEFAULT_CHARSET;
+ lf.lfOutPrecision = OUT_DEFAULT_PRECIS;
+ lf.lfClipPrecision = CLIP_DEFAULT_PRECIS;
+ lf.lfQuality = PROOF_QUALITY;
+ lf.lfPitchAndFamily = DEFAULT_PITCH | FF_DONTCARE;
+ strncpy( lf.lfFaceName, appData.renderPiecesWithFont, sizeof(lf.lfFaceName) );
+ lf.lfFaceName[ sizeof(lf.lfFaceName) - 1 ] = '\0';
+
+ hPieceFont = CreateFontIndirect( &lf );
+
+ if( hPieceFont == NULL ) {
+ fontBitmapSquareSize = -2;
+ }
+ else {
+ /* Setup font-to-piece character table */
+ if( ! SetPieceToFontCharTable(appData.fontToPieceTable) ) {
+ /* No (or wrong) global settings, try to detect the font */
+ if( strstr(lf.lfFaceName,"Alpha") != NULL ) {
+ /* Alpha */
+ SetPieceToFontCharTable("phbrqkojntwl");
+ }
+ else if( strstr(lf.lfFaceName,"DiagramTT") != NULL ) {
+ /* DiagramTT* family */
+ SetPieceToFontCharTable("PNLRQKpnlrqk");
+ }
+ else {
+ /* Cases, Condal, Leipzig, Lucena, Marroquin, Merida, Usual */
+ SetPieceToFontCharTable("pnbrqkomvtwl");
+ }
+ }
+
+ /* Create bitmaps */
+ hfont_old = SelectObject( hdc, hPieceFont );
+
+ CreatePieceMaskFromFont( hdc_window, hdc, PM_WP );
+ CreatePieceMaskFromFont( hdc_window, hdc, PM_WN );
+ CreatePieceMaskFromFont( hdc_window, hdc, PM_WB );
+ CreatePieceMaskFromFont( hdc_window, hdc, PM_WR );
+ CreatePieceMaskFromFont( hdc_window, hdc, PM_WQ );
+ CreatePieceMaskFromFont( hdc_window, hdc, PM_WK );
+ CreatePieceMaskFromFont( hdc_window, hdc, PM_BP );
+ CreatePieceMaskFromFont( hdc_window, hdc, PM_BN );
+ CreatePieceMaskFromFont( hdc_window, hdc, PM_BB );
+ CreatePieceMaskFromFont( hdc_window, hdc, PM_BR );
+ CreatePieceMaskFromFont( hdc_window, hdc, PM_BQ );
+ CreatePieceMaskFromFont( hdc_window, hdc, PM_BK );
+
+ SelectObject( hdc, hfont_old );
+
+ fontBitmapSquareSize = squareSize;
+ }
+ }
+
+ if( hdc != NULL ) {
+ DeleteDC( hdc );
+ }
+
+ if( hdc_window != NULL ) {
+ ReleaseDC( hwndMain, hdc_window );
+ }
+}
+
HBITMAP\r
DoLoadBitmap(HINSTANCE hinst, char *piece, int squareSize, char *suffix)\r
{\r
whitePieceBrush = CreateSolidBrush(whitePieceColor);\r
blackPieceBrush = CreateSolidBrush(blackPieceColor);\r
iconBkgndBrush = CreateSolidBrush(GetSysColor(COLOR_BACKGROUND));\r
+
+ /* [AS] Force rendering of the font-based pieces */
+ if( fontBitmapSquareSize > 0 ) {
+ fontBitmapSquareSize = 0;
+ }
}\r
\r
\r
int\r
BoardWidth(int boardSize)\r
{\r
- return (BOARD_SIZE + 1) * sizeInfo[boardSize].lineGap +\r
+ int lineGap = sizeInfo[boardSize].lineGap;
+
+ if( appData.overrideLineGap >= 0 && appData.overrideLineGap <= 5 ) {
+ lineGap = appData.overrideLineGap;
+ }
+
+ return (BOARD_SIZE + 1) * lineGap +
BOARD_SIZE * sizeInfo[boardSize].squareSize;\r
}\r
\r
squareSize = sizeInfo[boardSize].squareSize;\r
lineGap = sizeInfo[boardSize].lineGap;\r
\r
+ if( appData.overrideLineGap >= 0 && appData.overrideLineGap <= 5 ) {
+ lineGap = appData.overrideLineGap;
+ }
+
if (tinyLayout != oldTinyLayout) {\r
long style = GetWindowLong(hwndMain, GWL_STYLE);\r
if (tinyLayout) {\r
\r
if (appData.blindfold) return;\r
\r
+ /* [AS] Use font-based pieces if needed */
+ if( fontBitmapSquareSize >= 0 && squareSize > 32 ) {
+ /* Create piece bitmaps, or do nothing if piece set is up to data */
+ CreatePiecesFromFont();
+
+ if( fontBitmapSquareSize == squareSize ) {
+ int index = TranslatePieceToFontPiece( piece );
+
+ SelectObject( tmphdc, hPieceMask[ index ] );
+
+ BitBlt( hdc,
+ x, y,
+ squareSize, squareSize,
+ tmphdc,
+ 0, 0,
+ SRCAND );
+
+ SelectObject( tmphdc, hPieceFace[ index ] );
+
+ BitBlt( hdc,
+ x, y,
+ squareSize, squareSize,
+ tmphdc,
+ 0, 0,
+ SRCPAINT );
+
+ return;
+ }
+ }
+
if (appData.monoMode) {\r
SelectObject(tmphdc, PieceBitmap(piece, \r
color == sqcolor ? OUTLINE_PIECE : SOLID_PIECE));\r
}\r
}\r
\r
+/* [AS] Compute a drawing mode for a square, based on specified settings (see DrawTile) */
+int GetBackTextureMode( int algo )
+{
+ int result = BACK_TEXTURE_MODE_DISABLED;
+
+ switch( algo )
+ {
+ case BACK_TEXTURE_MODE_PLAIN:
+ result = 1; /* Always use identity map */
+ break;
+ case BACK_TEXTURE_MODE_FULL_RANDOM:
+ result = 1 + (myrandom() % 3); /* Pick a transformation at random */
+ break;
+ }
+
+ return result;
+}
+
+/*
+ [AS] Compute and save texture drawing info, otherwise we may not be able
+ to handle redraws cleanly (as random numbers would always be different).
+*/
+VOID RebuildTextureSquareInfo()
+{
+ BITMAP bi;
+ int lite_w = 0;
+ int lite_h = 0;
+ int dark_w = 0;
+ int dark_h = 0;
+ int row;
+ int col;
+
+ ZeroMemory( &backTextureSquareInfo, sizeof(backTextureSquareInfo) );
+
+ if( liteBackTexture != NULL ) {
+ if( GetObject( liteBackTexture, sizeof(bi), &bi ) > 0 ) {
+ lite_w = bi.bmWidth;
+ lite_h = bi.bmHeight;
+ }
+ }
+
+ if( darkBackTexture != NULL ) {
+ if( GetObject( darkBackTexture, sizeof(bi), &bi ) > 0 ) {
+ dark_w = bi.bmWidth;
+ dark_h = bi.bmHeight;
+ }
+ }
+
+ for( row=0; row<BOARD_SIZE; row++ ) {
+ for( col=0; col<BOARD_SIZE; col++ ) {
+ if( (col + row) & 1 ) {
+ /* Lite square */
+ if( lite_w >= squareSize && lite_h >= squareSize ) {
+ backTextureSquareInfo[row][col].x = col * (lite_w - squareSize) / BOARD_SIZE;
+ backTextureSquareInfo[row][col].y = row * (lite_h - squareSize) / BOARD_SIZE;
+ backTextureSquareInfo[row][col].mode = GetBackTextureMode(liteBackTextureMode);
+ }
+ }
+ else {
+ /* Dark square */
+ if( dark_w >= squareSize && dark_h >= squareSize ) {
+ backTextureSquareInfo[row][col].x = col * (dark_w - squareSize) / BOARD_SIZE;
+ backTextureSquareInfo[row][col].y = row * (dark_h - squareSize) / BOARD_SIZE;
+ backTextureSquareInfo[row][col].mode = GetBackTextureMode(darkBackTextureMode);
+ }
+ }
+ }
+ }
+}
+
VOID\r
DrawBoardOnDC(HDC hdc, Board board, HDC tmphdc)\r
{\r
int row, column, x, y, square_color, piece_color;\r
ChessSquare piece;\r
HBRUSH oldBrush;\r
+ HDC texture_hdc = NULL;
+
+ /* [AS] Initialize background textures if needed */
+ if( liteBackTexture != NULL || darkBackTexture != NULL ) {
+ if( backTextureSquareSize != squareSize ) {
+ backTextureSquareSize = squareSize;
+ RebuildTextureSquareInfo();
+ }
+
+ texture_hdc = CreateCompatibleDC( hdc );
+ }
\r
for (row = 0; row < BOARD_SIZE; row++) {\r
for (column = 0; column < BOARD_SIZE; column++) {\r
} else {\r
DrawPieceOnDC(hdc, piece, piece_color, square_color, x, y, tmphdc);\r
}\r
- } else {\r
+ }
+ else if( backTextureSquareInfo[row][column].mode > 0 ) {
+ /* [AS] Draw the square using a texture bitmap */
+ HBITMAP hbm = SelectObject( texture_hdc, square_color ? liteBackTexture : darkBackTexture );
+
+ DrawTile( x, y,
+ squareSize, squareSize,
+ hdc,
+ texture_hdc,
+ backTextureSquareInfo[row][column].mode,
+ backTextureSquareInfo[row][column].x,
+ backTextureSquareInfo[row][column].y );
+
+ SelectObject( texture_hdc, hbm );
+
+ if (piece != EmptySquare) {
+ DrawPieceOnDC(hdc, piece, piece_color, -1, x, y, tmphdc);
+ }
+ }
+ else {
oldBrush = SelectObject(hdc, square_color ?\r
lightSquareBrush : darkSquareBrush);\r
BitBlt(hdc, x, y, squareSize, squareSize, 0, 0, 0, PATCOPY);\r
}\r
}\r
}\r
+
+ if( texture_hdc != NULL ) {
+ DeleteDC( texture_hdc );
+ }
}\r
\r
#define MAX_CLIPS 200 /* more than enough */\r
AnalysisPopDown();\r
break;\r
\r
+ case IDM_NewGameFRC:
+ if( NewGameFRC() == 0 ) {
+ ResetGameEvent();
+ AnalysisPopDown();
+ }
+ break;
+
case IDM_LoadGame:\r
LoadGameDialog(hwnd, "Load Game from File");\r
break;\r
PasteGameFromClipboard();\r
break;\r
\r
+ /* [AS] Autodetect FEN or PGN data */
+ case IDM_Paste:
+ PasteGameOrFENFromClipboard();
+ break;
+
+ /* [AS] User adjudication */
+ case IDM_UserAdjudication_White:
+ UserAdjudicationEvent( +1 );
+ break;
+
+ case IDM_UserAdjudication_Black:
+ UserAdjudicationEvent( -1 );
+ break;
+
+ case IDM_UserAdjudication_Draw:
+ UserAdjudicationEvent( 0 );
+ break;
+
case IDM_CopyPosition:\r
CopyFENToClipboard();\r
break;\r
char dir[MSG_SIZ];\r
GetCurrentDirectory(MSG_SIZ, dir);\r
SetCurrentDirectory(installDir);\r
- debugFP = fopen("WinBoard.debug", "w");\r
+ debugFP = fopen(appData.nameOfDebugFile, "w");
SetCurrentDirectory(dir);\r
setbuf(debugFP, NULL);\r
} else {\r
SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) -1, (LPARAM) 0);\r
SendMessage(hwndCombo, WM_SETTEXT, (WPARAM) 0, (LPARAM) buf);\r
}\r
- if (chessProgram) {\r
- CheckDlgButton(hDlg, OPT_ChessEngine, BST_CHECKED);\r
- } else if (appData.icsActive) {\r
+
+ if (appData.icsActive) {
CheckDlgButton(hDlg, OPT_ChessServer, BST_CHECKED);\r
- } else if (appData.noChessProgram) {\r
+ }
+ else if (appData.noChessProgram) {
CheckDlgButton(hDlg, OPT_View, BST_CHECKED);\r
}\r
+ else {
+ CheckDlgButton(hDlg, OPT_ChessEngine, BST_CHECKED);
+ }
+
SetStartupDialogEnables(hDlg);\r
return TRUE;\r
\r
{\r
int ok, err;\r
\r
+ /* [AS] */
+ if( count <= 0 ) {
+ if (appData.debugMode) {
+ fprintf( debugFP, "DoReadFile: trying to read past end of buffer, overflow = %d\n", count );
+ }
+
+ return ERROR_INVALID_USER_BUFFER;
+ }
+
ResetEvent(ovl->hEvent);\r
ovl->Offset = ovl->OffsetHigh = 0;\r
ok = ReadFile(hFile, buf, count, outCount, ovl);\r
return err;\r
}\r
\r
+/* [AS] If input is line by line and a line exceed the buffer size, force an error */
+void CheckForInputBufferFull( InputSource * is )
+{
+ if( is->lineByLine && (is->next - is->buf) >= INPUT_SOURCE_BUF_SIZE ) {
+ /* Look for end of line */
+ char * p = is->buf;
+
+ while( p < is->next && *p != '\n' ) {
+ p++;
+ }
+
+ if( p >= is->next ) {
+ if (appData.debugMode) {
+ fprintf( debugFP, "Input line exceeded buffer size (source id=%u)\n", is->id );
+ }
+
+ is->error = ERROR_BROKEN_PIPE; /* [AS] Just any non-successful code! */
+ is->count = (DWORD) -1;
+ is->next = is->buf;
+ }
+ }
+}
\r
DWORD\r
InputThread(LPVOID arg)\r
is->count = 0;\r
} else {\r
is->count = (DWORD) -1;\r
+ /* [AS] The (is->count <= 0) check below is not useful for unsigned values! */
+ break;
}\r
}\r
+
+ CheckForInputBufferFull( is );
+
SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
+
+ if( is->count == ((DWORD) -1) ) break; /* [AS] */
+
if (is->count <= 0) break; /* Quit on EOF or error */\r
}\r
+
CloseHandle(ovl.hEvent);\r
CloseHandle(is->hFile);\r
+
+ if (appData.debugMode) {
+ fprintf( debugFP, "Input thread terminated (id=%u, error=%d, count=%d)\n", is->id, is->error, is->count );
+ }
+
return 0;\r
}\r
\r
is->count = (DWORD) -1;\r
}\r
}\r
+
+ CheckForInputBufferFull( is );
+
SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
+
+ if( is->count == ((DWORD) -1) ) break; /* [AS] */
+
if (is->count < 0) break; /* Quit on error */\r
}\r
CloseHandle(is->hFile);\r
}\r
}\r
SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
+
+ if( is->count == ((DWORD) -1) ) break; /* [AS] */
+
if (is->count <= 0) break; /* Quit on EOF or error */\r
}\r
return 0;\r
p = q;\r
}\r
}\r
+
/* Move any partial line to the start of the buffer */\r
q = is->buf;\r
while (p < is->next) {\r
*q++ = *p++;\r
}\r
is->next = q;\r
+
if (is->error != NO_ERROR || is->count == 0) {\r
/* Notify backend of the error. Note: If there was a partial\r
line at the end, it is not flushed through. */\r
VOID\r
DisplayError(char *str, int error)\r
{\r
- FARPROC lpProc;\r
char buf[MSG_SIZ*2], buf2[MSG_SIZ];\r
int len;\r
- char *p, *q;\r
\r
if (error == 0) {\r
strcpy(buf, str);\r
FreeProcInstance(lpProc);\r
}\r
\r
+/* [AS] Pick FRC position */
+LRESULT CALLBACK NewGameFRC_Proc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
+{
+ static int * lpIndexFRC;
+ BOOL index_is_ok;
+ char buf[16];
+
+ switch( message )
+ {
+ case WM_INITDIALOG:
+ lpIndexFRC = (int *) lParam;
+
+ CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));
+
+ SendDlgItemMessage( hDlg, IDC_NFG_Edit, EM_SETLIMITTEXT, sizeof(buf)-1, 0 );
+ SetDlgItemInt( hDlg, IDC_NFG_Edit, *lpIndexFRC, TRUE );
+ SendDlgItemMessage( hDlg, IDC_NFG_Edit, EM_SETSEL, 0, -1 );
+ SetFocus(GetDlgItem(hDlg, IDC_NFG_Edit));
+
+ break;
+
+ case WM_COMMAND:
+ switch( LOWORD(wParam) ) {
+ case IDOK:
+ *lpIndexFRC = GetDlgItemInt(hDlg, IDC_NFG_Edit, &index_is_ok, TRUE );
+ EndDialog( hDlg, 0 );
+ return TRUE;
+ case IDCANCEL:
+ EndDialog( hDlg, 1 );
+ return TRUE;
+ case IDC_NFG_Edit:
+ if( HIWORD(wParam) == EN_CHANGE ) {
+ GetDlgItemInt(hDlg, IDC_NFG_Edit, &index_is_ok, TRUE );
+
+ EnableWindow( GetDlgItem(hDlg, IDOK), index_is_ok );
+ }
+ return TRUE;
+ case IDC_NFG_Random:
+ sprintf( buf, "%d", myrandom() % 960 );
+ SetDlgItemText(hDlg, IDC_NFG_Edit, buf );
+ return TRUE;
+ }
+
+ break;
+ }
+
+ return FALSE;
+}
+
+int NewGameFRC()
+{
+ int result;
+ int index = appData.defaultFrcPosition;
+ FARPROC lpProc = MakeProcInstance( (FARPROC) NewGameFRC_Proc, hInst );
+
+ result = DialogBoxParam( hInst, MAKEINTRESOURCE(DLG_NewGameFRC), hwndMain, (DLGPROC)lpProc, (LPARAM)&index );
+
+ if( result == 0 ) {
+ appData.defaultFrcPosition = index;
+ }
+
+ return result;
+}
+
\r
VOID\r
DisplayIcsInteractionTitle(char *str)\r
we could arrange for this even though neither WinBoard\r
nor the chess program uses a console for stdio? */\r
/*!!if (signal) GenerateConsoleCtrlEvent(CTRL_BREAK_EVENT, cp->pid);*/\r
+
+ /* [AS] Special termination modes for misbehaving programs... */
+ if( signal == 9 ) {
+ if ( appData.debugMode) {
+ fprintf( debugFP, "Terminating process %u\n", cp->pid );
+ }
+
+ TerminateProcess( cp->hProcess, 0 );
+ }
+ else if( signal == 10 ) {
+ DWORD dw = WaitForSingleObject( cp->hProcess, 3*1000 ); // Wait 3 seconds at most
+
+ if( dw != WAIT_OBJECT_0 ) {
+ if ( appData.debugMode) {
+ fprintf( debugFP, "Process %u still alive after timeout, killing...\n", cp->pid );
+ }
+
+ TerminateProcess( cp->hProcess, 0 );
+ }
+ }
+
CloseHandle(cp->hProcess);\r
break;\r
\r
AddInputSource(ProcRef pr, int lineByLine,\r
InputCallback func, VOIDSTAR closure)\r
{\r
- InputSource *is, *is2;\r
+ InputSource *is, *is2 = NULL;
ChildProc *cp = (ChildProc *) pr;\r
\r
is = (InputSource *) calloc(1, sizeof(InputSource));\r
consoleInputSource = is;\r
} else {\r
is->kind = cp->kind;\r
+ /*
+ [AS] Try to avoid a race condition if the thread is given control too early:
+ we create all threads suspended so that the is->hThread variable can be
+ safely assigned, then let the threads start with ResumeThread.
+ */
switch (cp->kind) {\r
case CPReal:\r
is->hFile = cp->hFrom;\r
cp->hFrom = NULL; /* now owned by InputThread */\r
is->hThread =\r
CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) NonOvlInputThread,\r
- (LPVOID) is, 0, &is->id);\r
+ (LPVOID) is, CREATE_SUSPENDED, &is->id);
break;\r
\r
case CPComm:\r
cp->hFrom = NULL; /* now owned by InputThread */\r
is->hThread =\r
CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) InputThread,\r
- (LPVOID) is, 0, &is->id);\r
+ (LPVOID) is, CREATE_SUSPENDED, &is->id);
break;\r
\r
case CPSock:\r
is->sock = cp->sock;\r
is->hThread =\r
CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,\r
- (LPVOID) is, 0, &is->id);\r
+ (LPVOID) is, CREATE_SUSPENDED, &is->id);
break;\r
\r
case CPRcmd:\r
is2->second = is2;\r
is->hThread =\r
CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,\r
- (LPVOID) is, 0, &is->id);\r
+ (LPVOID) is, CREATE_SUSPENDED, &is->id);
is2->hThread =\r
CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,\r
- (LPVOID) is2, 0, &is2->id);\r
+ (LPVOID) is2, CREATE_SUSPENDED, &is2->id);
break;\r
}\r
+
+ if( is->hThread != NULL ) {
+ ResumeThread( is->hThread );
}\r
+
+ if( is2 != NULL && is2->hThread != NULL ) {
+ ResumeThread( is2->hThread );
+ }
+ }
+
return (InputSourceRef) is;\r
}\r
\r
HistorySet(char movelist[][2*MOVE_LEN], int first, int last, int current)\r
{\r
/* Currently not implemented in WinBoard */\r
-}\r
+#if 1
+ /* [AS] Let's see what this function is for... */
+ char buf[256];
\r
+ sprintf( buf, "HistorySet: first=%d, last=%d, current=%d (%s)\n",
+ first, last, current, current >= 0 ? movelist[current] : "n/a" );
\r
+ OutputDebugString( buf );
+#endif
+}