X-Git-Url: http://winboard.nl/cgi-bin?a=blobdiff_plain;f=winboard%2Fwinboard.c;h=73695204307be95151b6220d1d020708ae53e52b;hb=062fb2bd92324706eed1fba8d5f7b4015ef2b058;hp=2ad3bcf2bc6ec540d2c50832cdfb08a2ee38452a;hpb=a180888cfea059c10e147b2357571c421cb4346f;p=xboard.git diff --git a/winboard/winboard.c b/winboard/winboard.c index 2ad3bcf..7369520 100644 --- a/winboard/winboard.c +++ b/winboard/winboard.c @@ -1,6 +1,6 @@ -/* +/* * WinBoard.c -- Windows NT front end to XBoard - * $Id: winboard.c,v 2.3 2003/11/25 05:25:20 mann Exp $ + * $Id: winboard.c,v 2.3 2003/11/25 05:25:20 mann Exp $ * * Copyright 1991 by Digital Equipment Corporation, Maynard, Massachusetts. * Enhancements Copyright 1992-2001 Free Software Foundation, Inc. @@ -57,7 +57,7 @@ #include #include -#include +#include #include #include #include @@ -84,9 +84,18 @@ #include "wsockerr.h" #include "defaults.h" -int myrandom(void); -void mysrandom(unsigned int seed); - +#include "wsnap.h" + +void InitEngineUCI( const char * iniDir, ChessProgramState * cps ); + + int myrandom(void); + void mysrandom(unsigned int seed); + +extern int whiteFlag, blackFlag; +Boolean flipClock = FALSE; + +void DisplayHoldingsCount(HDC hdc, int x, int y, int align, int copyNumber); + typedef struct { ChessSquare piece; POINT pos; /* window coordinates of current pos */ @@ -129,7 +138,7 @@ char installDir[MSG_SIZ]; BoardSize boardSize; BOOLEAN chessProgram; static int boardX, boardY, consoleX, consoleY, consoleW, consoleH; -static int squareSize, lineGap; +static int squareSize, lineGap, minorSize; static int winWidth, winHeight; static RECT messageRect, whiteRect, blackRect; static char messageText[MESSAGE_TEXT_MAX]; @@ -144,7 +153,7 @@ char *icsNames; char *firstChessProgramNames; char *secondChessProgramNames; -#define ARG_MAX 64*1024 /* [AS] For Roger Brown's very long list! */ +#define ARG_MAX 128*1024 /* [AS] For Roger Brown's very long list! */ #define PALETTESIZE 256 @@ -160,8 +169,9 @@ ColorClass currentColorClass; HWND hCommPort = NULL; /* currently open comm port */ static HWND hwndPause; /* pause button */ -static HBITMAP pieceBitmap[3][(int) WhiteKing + 1]; +static HBITMAP pieceBitmap[3][(int) BlackPawn]; /* [HGM] nr of bitmaps referred to bP in stead of wK */ static HBRUSH lightSquareBrush, darkSquareBrush, + blackSquareBrush, /* [HGM] for band between board and holdings */ whitePieceBrush, blackPieceBrush, iconBkgndBrush, outlineBrush; static POINT gridEndpoints[(BOARD_SIZE + 1) * 4]; static DWORD gridVertexCounts[(BOARD_SIZE + 1) * 2]; @@ -175,18 +185,18 @@ static int doingSizing = FALSE; static int lastSizing = 0; static int prevStderrPort; -/* [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]; - +/* [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) #define oldDialog 0 /* cygwin doesn't define _winmajor; mingw does */ #else @@ -235,60 +245,24 @@ SizeInfo sizeInfo[] = #define MF(x) {x, {0, }, {0, }, 0} MyFont fontRec[NUM_SIZES][NUM_FONTS] = { - { MF(CLOCK_FONT_TINY), MF(MESSAGE_FONT_TINY), - MF(COORD_FONT_TINY), MF(CONSOLE_FONT_TINY), - MF(COMMENT_FONT_TINY), MF(EDITTAGS_FONT_TINY) }, - { MF(CLOCK_FONT_TEENY), MF(MESSAGE_FONT_TEENY), - MF(COORD_FONT_TEENY), MF(CONSOLE_FONT_TEENY), - MF(COMMENT_FONT_TEENY), MF(EDITTAGS_FONT_TEENY) }, - { MF(CLOCK_FONT_DINKY), MF(MESSAGE_FONT_DINKY), - MF(COORD_FONT_DINKY), MF(CONSOLE_FONT_DINKY), - MF(COMMENT_FONT_DINKY), MF(EDITTAGS_FONT_DINKY) }, - { MF(CLOCK_FONT_PETITE), MF(MESSAGE_FONT_PETITE), - MF(COORD_FONT_PETITE), MF(CONSOLE_FONT_PETITE), - MF(COMMENT_FONT_PETITE), MF(EDITTAGS_FONT_PETITE) }, - { MF(CLOCK_FONT_SLIM), MF(MESSAGE_FONT_SLIM), - MF(COORD_FONT_SLIM), MF(CONSOLE_FONT_SLIM), - MF(COMMENT_FONT_SLIM), MF(EDITTAGS_FONT_SLIM) }, - { MF(CLOCK_FONT_SMALL), MF(MESSAGE_FONT_SMALL), - MF(COORD_FONT_SMALL), MF(CONSOLE_FONT_SMALL), - MF(COMMENT_FONT_SMALL), MF(EDITTAGS_FONT_SMALL) }, - { MF(CLOCK_FONT_MEDIOCRE), MF(MESSAGE_FONT_MEDIOCRE), - MF(COORD_FONT_MEDIOCRE), MF(CONSOLE_FONT_MEDIOCRE), - MF(COMMENT_FONT_MEDIOCRE), MF(EDITTAGS_FONT_MEDIOCRE) }, - { MF(CLOCK_FONT_MIDDLING), MF(MESSAGE_FONT_MIDDLING), - MF(COORD_FONT_MIDDLING), MF(CONSOLE_FONT_MIDDLING), - MF(COMMENT_FONT_MIDDLING), MF(EDITTAGS_FONT_MIDDLING) }, - { MF(CLOCK_FONT_AVERAGE), MF(MESSAGE_FONT_AVERAGE), - MF(COORD_FONT_AVERAGE), MF(CONSOLE_FONT_AVERAGE), - MF(COMMENT_FONT_AVERAGE), MF(EDITTAGS_FONT_AVERAGE) }, - { MF(CLOCK_FONT_MODERATE), MF(MESSAGE_FONT_MODERATE), - MF(COORD_FONT_MODERATE), MF(CONSOLE_FONT_MODERATE), - MF(COMMENT_FONT_MODERATE), MF(EDITTAGS_FONT_MODERATE) }, - { MF(CLOCK_FONT_MEDIUM), MF(MESSAGE_FONT_MEDIUM), - MF(COORD_FONT_MEDIUM), MF(CONSOLE_FONT_MEDIUM), - MF(COMMENT_FONT_MEDIUM), MF(EDITTAGS_FONT_MEDIUM) }, - { MF(CLOCK_FONT_BULKY), MF(MESSAGE_FONT_BULKY), - MF(COORD_FONT_BULKY), MF(CONSOLE_FONT_BULKY), - MF(COMMENT_FONT_BULKY), MF(EDITTAGS_FONT_BULKY) }, - { MF(CLOCK_FONT_LARGE), MF(MESSAGE_FONT_LARGE), - MF(COORD_FONT_LARGE), MF(CONSOLE_FONT_LARGE), - MF(COMMENT_FONT_LARGE), MF(EDITTAGS_FONT_LARGE) }, - { MF(CLOCK_FONT_BIG), MF(MESSAGE_FONT_BIG), - MF(COORD_FONT_BIG), MF(CONSOLE_FONT_BIG), - MF(COMMENT_FONT_BIG), MF(EDITTAGS_FONT_BIG) }, - { MF(CLOCK_FONT_HUGE), MF(MESSAGE_FONT_HUGE), - MF(COORD_FONT_HUGE), MF(CONSOLE_FONT_HUGE), - MF(COMMENT_FONT_HUGE), MF(EDITTAGS_FONT_HUGE) }, - { MF(CLOCK_FONT_GIANT), MF(MESSAGE_FONT_GIANT), - MF(COORD_FONT_GIANT), MF(CONSOLE_FONT_GIANT), - MF(COMMENT_FONT_GIANT), MF(EDITTAGS_FONT_GIANT) }, - { MF(CLOCK_FONT_COLOSSAL), MF(MESSAGE_FONT_COLOSSAL), - MF(COORD_FONT_COLOSSAL), MF(CONSOLE_FONT_COLOSSAL), - MF(COMMENT_FONT_COLOSSAL), MF(EDITTAGS_FONT_COLOSSAL) }, - { MF(CLOCK_FONT_TITANIC), MF(MESSAGE_FONT_TITANIC), - MF(COORD_FONT_TITANIC), MF(CONSOLE_FONT_TITANIC), - MF(COMMENT_FONT_TITANIC), MF(EDITTAGS_FONT_TITANIC) }, + { MF(CLOCK_FONT_TINY), MF(MESSAGE_FONT_TINY), MF(COORD_FONT_TINY), MF(CONSOLE_FONT_TINY), MF(COMMENT_FONT_TINY), MF(EDITTAGS_FONT_TINY), MF(MOVEHISTORY_FONT_ALL) }, + { MF(CLOCK_FONT_TEENY), MF(MESSAGE_FONT_TEENY), MF(COORD_FONT_TEENY), MF(CONSOLE_FONT_TEENY), MF(COMMENT_FONT_TEENY), MF(EDITTAGS_FONT_TEENY), MF(MOVEHISTORY_FONT_ALL) }, + { MF(CLOCK_FONT_DINKY), MF(MESSAGE_FONT_DINKY), MF(COORD_FONT_DINKY), MF(CONSOLE_FONT_DINKY), MF(COMMENT_FONT_DINKY), MF(EDITTAGS_FONT_DINKY), MF(MOVEHISTORY_FONT_ALL) }, + { MF(CLOCK_FONT_PETITE), MF(MESSAGE_FONT_PETITE), MF(COORD_FONT_PETITE), MF(CONSOLE_FONT_PETITE), MF(COMMENT_FONT_PETITE), MF(EDITTAGS_FONT_PETITE), MF(MOVEHISTORY_FONT_ALL) }, + { MF(CLOCK_FONT_SLIM), MF(MESSAGE_FONT_SLIM), MF(COORD_FONT_SLIM), MF(CONSOLE_FONT_SLIM), MF(COMMENT_FONT_SLIM), MF(EDITTAGS_FONT_SLIM), MF(MOVEHISTORY_FONT_ALL) }, + { MF(CLOCK_FONT_SMALL), MF(MESSAGE_FONT_SMALL), MF(COORD_FONT_SMALL), MF(CONSOLE_FONT_SMALL), MF(COMMENT_FONT_SMALL), MF(EDITTAGS_FONT_SMALL), MF(MOVEHISTORY_FONT_ALL) }, + { MF(CLOCK_FONT_MEDIOCRE), MF(MESSAGE_FONT_MEDIOCRE), MF(COORD_FONT_MEDIOCRE), MF(CONSOLE_FONT_MEDIOCRE), MF(COMMENT_FONT_MEDIOCRE), MF(EDITTAGS_FONT_MEDIOCRE), MF(MOVEHISTORY_FONT_ALL) }, + { MF(CLOCK_FONT_MIDDLING), MF(MESSAGE_FONT_MIDDLING), MF(COORD_FONT_MIDDLING), MF(CONSOLE_FONT_MIDDLING), MF(COMMENT_FONT_MIDDLING), MF(EDITTAGS_FONT_MIDDLING), MF(MOVEHISTORY_FONT_ALL) }, + { MF(CLOCK_FONT_AVERAGE), MF(MESSAGE_FONT_AVERAGE), MF(COORD_FONT_AVERAGE), MF(CONSOLE_FONT_AVERAGE), MF(COMMENT_FONT_AVERAGE), MF(EDITTAGS_FONT_AVERAGE), MF(MOVEHISTORY_FONT_ALL) }, + { MF(CLOCK_FONT_MODERATE), MF(MESSAGE_FONT_MODERATE), MF(COORD_FONT_MODERATE), MF(CONSOLE_FONT_MODERATE), MF(COMMENT_FONT_MODERATE), MF(EDITTAGS_FONT_MODERATE), MF(MOVEHISTORY_FONT_ALL) }, + { MF(CLOCK_FONT_MEDIUM), MF(MESSAGE_FONT_MEDIUM), MF(COORD_FONT_MEDIUM), MF(CONSOLE_FONT_MEDIUM), MF(COMMENT_FONT_MEDIUM), MF(EDITTAGS_FONT_MEDIUM), MF(MOVEHISTORY_FONT_ALL) }, + { MF(CLOCK_FONT_BULKY), MF(MESSAGE_FONT_BULKY), MF(COORD_FONT_BULKY), MF(CONSOLE_FONT_BULKY), MF(COMMENT_FONT_BULKY), MF(EDITTAGS_FONT_BULKY), MF(MOVEHISTORY_FONT_ALL) }, + { MF(CLOCK_FONT_LARGE), MF(MESSAGE_FONT_LARGE), MF(COORD_FONT_LARGE), MF(CONSOLE_FONT_LARGE), MF(COMMENT_FONT_LARGE), MF(EDITTAGS_FONT_LARGE), MF(MOVEHISTORY_FONT_ALL) }, + { MF(CLOCK_FONT_BIG), MF(MESSAGE_FONT_BIG), MF(COORD_FONT_BIG), MF(CONSOLE_FONT_BIG), MF(COMMENT_FONT_BIG), MF(EDITTAGS_FONT_BIG), MF(MOVEHISTORY_FONT_ALL) }, + { MF(CLOCK_FONT_HUGE), MF(MESSAGE_FONT_HUGE), MF(COORD_FONT_HUGE), MF(CONSOLE_FONT_HUGE), MF(COMMENT_FONT_HUGE), MF(EDITTAGS_FONT_HUGE), MF(MOVEHISTORY_FONT_ALL) }, + { MF(CLOCK_FONT_GIANT), MF(MESSAGE_FONT_GIANT), MF(COORD_FONT_GIANT), MF(CONSOLE_FONT_GIANT), MF(COMMENT_FONT_GIANT), MF(EDITTAGS_FONT_GIANT), MF(MOVEHISTORY_FONT_ALL) }, + { MF(CLOCK_FONT_COLOSSAL), MF(MESSAGE_FONT_COLOSSAL), MF(COORD_FONT_COLOSSAL), MF(CONSOLE_FONT_COLOSSAL), MF(COMMENT_FONT_COLOSSAL), MF(EDITTAGS_FONT_COLOSSAL), MF(MOVEHISTORY_FONT_ALL) }, + { MF(CLOCK_FONT_TITANIC), MF(MESSAGE_FONT_TITANIC), MF(COORD_FONT_TITANIC), MF(CONSOLE_FONT_TITANIC), MF(COMMENT_FONT_TITANIC), MF(EDITTAGS_FONT_TITANIC), MF(MOVEHISTORY_FONT_ALL) }, }; MyFont *font[NUM_SIZES][NUM_FONTS]; @@ -416,6 +390,41 @@ void ParseIcsTextMenu(char *icsTextMenuString); VOID PopUpMoveDialog(char firstchar); VOID UpdateSampleText(HWND hDlg, int id, MyColorizeAttribs *mca); +/* [AS] */ +int NewGameFRC(); +int GameListOptions(); + +HWND moveHistoryDialog = NULL; +BOOLEAN moveHistoryDialogUp = FALSE; + +WindowPlacement wpMoveHistory; + +HWND evalGraphDialog = NULL; +BOOLEAN evalGraphDialogUp = FALSE; + +WindowPlacement wpEvalGraph; + +HWND engineOutputDialog = NULL; +BOOLEAN engineOutputDialogUp = FALSE; + +WindowPlacement wpEngineOutput; + +VOID MoveHistoryPopUp(); +VOID MoveHistoryPopDown(); +VOID MoveHistorySet( char movelist[][2*MOVE_LEN], int first, int last, int current, ChessProgramStats_Move * pvInfo ); +BOOL MoveHistoryIsUp(); + +VOID EvalGraphSet( int first, int last, int current, ChessProgramStats_Move * pvInfo ); +VOID EvalGraphPopUp(); +VOID EvalGraphPopDown(); +BOOL EvalGraphIsUp(); + +VOID EngineOutputPopUp(); +VOID EngineOutputPopDown(); +BOOL EngineOutputIsUp(); +VOID EngineOutputUpdate( FrontEndProgramStats * stats ); + +VOID GothicPopUp(char *title, char up); /* * Setting "frozen" should disable all user input other than deleting * the window. We do this while engines are initializing themselves. @@ -462,7 +471,7 @@ WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) { MSG msg; - HANDLE hAccelMain, hAccelNoAlt; + HANDLE hAccelMain, hAccelNoAlt, hAccelNoICS; debugFP = stderr; @@ -478,6 +487,7 @@ WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, hAccelMain = LoadAccelerators (hInstance, szAppName); hAccelNoAlt = LoadAccelerators (hInstance, "NO_ALT"); + hAccelNoICS = LoadAccelerators( hInstance, "NO_ICS"); /* [AS] No Ctrl-V on ICS!!! */ /* Acquire and dispatch messages until a WM_QUIT message is received. */ @@ -487,10 +497,14 @@ WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, 0)) /* highest message to examine */ { if (!(commentDialog && IsDialogMessage(commentDialog, &msg)) && + !(moveHistoryDialog && IsDialogMessage(moveHistoryDialog, &msg)) && + !(evalGraphDialog && IsDialogMessage(evalGraphDialog, &msg)) && + !(engineOutputDialog && IsDialogMessage(engineOutputDialog, &msg)) && !(editTagsDialog && IsDialogMessage(editTagsDialog, &msg)) && !(gameListDialog && IsDialogMessage(gameListDialog, &msg)) && !(errorDialog && IsDialogMessage(errorDialog, &msg)) && !(!frozen && TranslateAccelerator(hwndMain, hAccelMain, &msg)) && + !(!hwndConsole && TranslateAccelerator(hwndMain, hAccelNoICS, &msg)) && !(!hwndConsole && TranslateAccelerator(hwndMain, hAccelNoAlt, &msg))) { TranslateMessage(&msg); /* Translates virtual key codes */ DispatchMessage(&msg); /* Dispatches message to window */ @@ -551,9 +565,12 @@ int screenHeight, screenWidth; void EnsureOnScreen(int *x, int *y) { + int gap = GetSystemMetrics(SM_CYFRAME) + GetSystemMetrics(SM_CYCAPTION); /* Be sure window at (x,y) is not off screen (or even mostly off screen) */ if (*x > screenWidth - 32) *x = 0; if (*y > screenHeight - 32) *y = 0; + if (*x < 10) *x = 10; + if (*y < gap) *y = gap; } BOOL @@ -571,14 +588,18 @@ InitInstance(HINSTANCE hInstance, int nCmdShow, LPSTR lpCmdLine) } else { GetCurrentDirectory(MSG_SIZ, installDir); } + gameInfo.boardWidth = gameInfo.boardHeight = 8; // [HGM] won't have open window otherwise InitAppData(lpCmdLine); /* Get run-time parameters */ if (appData.debugMode) { - debugFP = fopen(appData.nameOfDebugFile, "w"); + debugFP = fopen(appData.nameOfDebugFile, "w"); setbuf(debugFP, NULL); } InitBackEnd1(); + InitEngineUCI( installDir, &first ); + InitEngineUCI( installDir, &second ); + /* Create a main window for this application instance. */ hwnd = CreateWindow(szAppName, szTitle, (WS_OVERLAPPEDWINDOW & ~WS_MAXIMIZEBOX), @@ -602,7 +623,9 @@ InitInstance(HINSTANCE hInstance, int nCmdShow, LPSTR lpCmdLine) size that fits on this screen as the default. */ InitDrawingSizes((BoardSize)ibs, 0); if (boardSize == (BoardSize)-1 && - winHeight <= screenHeight && winWidth <= screenWidth) { + winHeight <= screenHeight + - GetSystemMetrics(SM_CYFRAME) - GetSystemMetrics(SM_CYCAPTION) - 10 + && winWidth <= screenWidth) { boardSize = (BoardSize)ibs; } } @@ -610,34 +633,47 @@ InitInstance(HINSTANCE hInstance, int nCmdShow, LPSTR lpCmdLine) InitMenuChecks(); buttonCount = GetSystemMetrics(SM_CMOUSEBUTTONS); - /* [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) ); - + /* [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 */ if (appData.icsActive) { ConsoleCreate(); } + /* [AS] Restore layout */ + if( wpMoveHistory.visible ) { + MoveHistoryPopUp(); + } + + if( wpEvalGraph.visible ) { + EvalGraphPopUp(); + } + + if( wpEngineOutput.visible ) { + EngineOutputPopUp(); + } + InitBackEnd2(); /* Make the window visible; update its client area; and return "success" */ @@ -653,7 +689,13 @@ InitInstance(HINSTANCE hInstance, int nCmdShow, LPSTR lpCmdLine) SetWindowPlacement(hwndMain, &wp); SetWindowPos(hwndMain, alwaysOnTop ? HWND_TOPMOST : HWND_NOTOPMOST, - 0, 0, 0, 0, SWP_NOMOVE|SWP_NOSIZE); + 0, 0, 0, 0, SWP_NOMOVE|SWP_NOSIZE); + + /* [AS] Disable the FRC stuff if not playing the proper variant */ + if( gameInfo.variant != VariantFischeRandom ) { + EnableMenuItem( GetMenu(hwndMain), IDM_NewGameFRC, MF_GRAYED ); + } + if (hwndConsole) { #if AOT_CONSOLE SetWindowPos(hwndConsole, alwaysOnTop ? HWND_TOPMOST : HWND_NOTOPMOST, @@ -847,6 +889,7 @@ ArgDescriptor argDescriptors[] = { { "tagsFont", ArgFont, (LPVOID) EDITTAGS_FONT, TRUE }, { "commentFont", ArgFont, (LPVOID) COMMENT_FONT, TRUE }, { "icsFont", ArgFont, (LPVOID) CONSOLE_FONT, TRUE }, + { "moveHistoryFont", ArgFont, (LPVOID) MOVEHISTORY_FONT, TRUE }, /* [AS] */ { "boardSize", ArgBoardSize, (LPVOID) &boardSize, TRUE }, /* must come after all fonts */ { "size", ArgBoardSize, (LPVOID) &boardSize, FALSE }, @@ -1044,35 +1087,97 @@ ArgDescriptor argDescriptors[] = { { "initialMode", ArgString, (LPVOID) &appData.initialMode, FALSE }, { "mode", ArgString, (LPVOID) &appData.initialMode, FALSE }, { "variant", ArgString, (LPVOID) &appData.variant, FALSE }, - { "firstProtocolVersion", ArgInt, (LPVOID) &appData.firstProtocolVersion, FALSE }, - { "secondProtocolVersion", ArgInt, (LPVOID) &appData.secondProtocolVersion,FALSE }, + { "firstProtocolVersion", ArgInt, (LPVOID) &appData.firstProtocolVersion, FALSE }, + { "secondProtocolVersion", ArgInt, (LPVOID) &appData.secondProtocolVersion,FALSE }, { "showButtonBar", ArgBoolean, (LPVOID) &appData.showButtonBar, TRUE }, { "buttons", ArgTrue, (LPVOID) &appData.showButtonBar, FALSE }, { "xbuttons", ArgFalse, (LPVOID) &appData.showButtonBar, FALSE }, { "-buttons", ArgFalse, (LPVOID) &appData.showButtonBar, FALSE }, - /* [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 }, - { "pgnEventHeader", ArgString, (LPVOID) &appData.pgnEventHeader, TRUE }, - { "debugfile", ArgFilename, (LPVOID) &appData.nameOfDebugFile, FALSE }, + /* [AS] New features */ + { "firstScoreAbs", ArgBoolean, (LPVOID) &appData.firstScoreIsAbsolute, FALSE }, + { "secondScoreAbs", ArgBoolean, (LPVOID) &appData.secondScoreIsAbsolute, FALSE }, + { "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 }, + { "gameListTags", ArgString, (LPVOID) &appData.gameListTags, TRUE }, + { "saveOutOfBookInfo", ArgBoolean, (LPVOID) &appData.saveOutOfBookInfo, TRUE }, + { "showEvalInMoveHistory", ArgBoolean, (LPVOID) &appData.showEvalInMoveHistory, TRUE }, + { "evalHistColorWhite", ArgColor, (LPVOID) &appData.evalHistColorWhite, TRUE }, + { "evalHistColorBlack", ArgColor, (LPVOID) &appData.evalHistColorBlack, TRUE }, + { "highlightMoveWithArrow", ArgBoolean, (LPVOID) &appData.highlightMoveWithArrow, TRUE }, + { "highlightArrowColor", ArgColor, (LPVOID) &appData.highlightArrowColor, TRUE }, + { "stickyWindows", ArgBoolean, (LPVOID) &appData.useStickyWindows, TRUE }, + { "adjudicateDrawMoves", ArgInt, (LPVOID) &appData.adjudicateDrawMoves, TRUE }, + { "autoDisplayComment", ArgBoolean, (LPVOID) &appData.autoDisplayComment, TRUE }, + { "autoDisplayTags", ArgBoolean, (LPVOID) &appData.autoDisplayTags, TRUE }, + { "firstIsUCI", ArgBoolean, (LPVOID) &appData.firstIsUCI, FALSE }, + { "fUCI", ArgTrue, (LPVOID) &appData.firstIsUCI, FALSE }, + { "secondIsUCI", ArgBoolean, (LPVOID) &appData.secondIsUCI, FALSE }, + { "sUCI", ArgTrue, (LPVOID) &appData.secondIsUCI, FALSE }, + { "firstHasOwnBookUCI", ArgBoolean, (LPVOID) &appData.firstHasOwnBookUCI, FALSE }, + { "fNoOwnBookUCI", ArgFalse, (LPVOID) &appData.firstHasOwnBookUCI, FALSE }, + { "secondHasOwnBookUCI", ArgBoolean, (LPVOID) &appData.secondHasOwnBookUCI, FALSE }, + { "sNoOwnBookUCI", ArgFalse, (LPVOID) &appData.secondHasOwnBookUCI, FALSE }, + { "polyglotDir", ArgFilename, (LPVOID) &appData.polyglotDir, TRUE }, + { "usePolyglotBook", ArgBoolean, (LPVOID) &appData.usePolyglotBook, TRUE }, + { "polyglotBook", ArgFilename, (LPVOID) &appData.polyglotBook, TRUE }, + { "defaultHashSize", ArgInt, (LPVOID) &appData.defaultHashSize, TRUE }, + { "defaultCacheSizeEGTB", ArgInt, (LPVOID) &appData.defaultCacheSizeEGTB, TRUE }, + { "defaultPathEGTB", ArgFilename, (LPVOID) &appData.defaultPathEGTB, TRUE }, + + /* [AS] Layout stuff */ + { "moveHistoryUp", ArgBoolean, (LPVOID) &wpMoveHistory.visible, TRUE }, + { "moveHistoryX", ArgInt, (LPVOID) &wpMoveHistory.x, TRUE }, + { "moveHistoryY", ArgInt, (LPVOID) &wpMoveHistory.y, TRUE }, + { "moveHistoryW", ArgInt, (LPVOID) &wpMoveHistory.width, TRUE }, + { "moveHistoryH", ArgInt, (LPVOID) &wpMoveHistory.height, TRUE }, + + { "evalGraphUp", ArgBoolean, (LPVOID) &wpEvalGraph.visible, TRUE }, + { "evalGraphX", ArgInt, (LPVOID) &wpEvalGraph.x, TRUE }, + { "evalGraphY", ArgInt, (LPVOID) &wpEvalGraph.y, TRUE }, + { "evalGraphW", ArgInt, (LPVOID) &wpEvalGraph.width, TRUE }, + { "evalGraphH", ArgInt, (LPVOID) &wpEvalGraph.height, TRUE }, + + { "engineOutputUp", ArgBoolean, (LPVOID) &wpEngineOutput.visible, TRUE }, + { "engineOutputX", ArgInt, (LPVOID) &wpEngineOutput.x, TRUE }, + { "engineOutputY", ArgInt, (LPVOID) &wpEngineOutput.y, TRUE }, + { "engineOutputW", ArgInt, (LPVOID) &wpEngineOutput.width, TRUE }, + { "engineOutputH", ArgInt, (LPVOID) &wpEngineOutput.height, TRUE }, + + /* [HGM] board-size, adjudication and misc. options */ + { "boardWidth", ArgInt, (LPVOID) &appData.NrFiles, TRUE }, + { "boardHeight", ArgInt, (LPVOID) &appData.NrRanks, TRUE }, + { "holdingsSize", ArgInt, (LPVOID) &appData.holdingsSize, TRUE }, + { "matchPause", ArgInt, (LPVOID) &appData.matchPause, TRUE }, + { "pieceToCharTable", ArgString, (LPVOID) &appData.pieceToCharTable, FALSE }, + { "flipBlack", ArgBoolean, (LPVOID) &appData.allWhite, TRUE }, + { "allWhite", ArgBoolean, (LPVOID) &appData.allWhite, TRUE }, + { "alphaRank", ArgBoolean, (LPVOID) &appData.alphaRank, FALSE }, + { "testClaims", ArgBoolean, (LPVOID) &appData.testClaims, TRUE }, + { "checkMates", ArgBoolean, (LPVOID) &appData.checkMates, TRUE }, + { "materialDraws", ArgBoolean, (LPVOID) &appData.materialDraws, TRUE }, + { "trivialDraws", ArgBoolean, (LPVOID) &appData.trivialDraws, TRUE }, + { "ruleMoves", ArgInt, (LPVOID) &appData.ruleMoves, TRUE }, + { "repeatsToDraw", ArgInt, (LPVOID) &appData.drawRepeats, TRUE }, + #ifdef ZIPPY { "zippyTalk", ArgBoolean, (LPVOID) &appData.zippyTalk, FALSE }, { "zt", ArgTrue, (LPVOID) &appData.zippyTalk, FALSE }, @@ -1727,30 +1832,71 @@ InitAppData(LPSTR lpCmdLine) appData.firstProtocolVersion = PROTOVER; appData.secondProtocolVersion = PROTOVER; appData.showButtonBar = TRUE; - - /* [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"; - + + /* [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; + appData.gameListTags = GLT_DEFAULT_TAGS; + appData.saveOutOfBookInfo = TRUE; + appData.showEvalInMoveHistory = TRUE; + appData.evalHistColorWhite = ParseColorName( "#FFFFB0" ); + appData.evalHistColorBlack = ParseColorName( "#AD5D3D" ); + appData.highlightMoveWithArrow = FALSE; + appData.highlightArrowColor = ParseColorName( "#FFFF80" ); + appData.useStickyWindows = TRUE; + appData.adjudicateDrawMoves = 0; + appData.autoDisplayComment = TRUE; + appData.autoDisplayTags = TRUE; + appData.firstIsUCI = FALSE; + appData.secondIsUCI = FALSE; + appData.firstHasOwnBookUCI = TRUE; + appData.secondHasOwnBookUCI = TRUE; + appData.polyglotDir = ""; + appData.usePolyglotBook = FALSE; + appData.polyglotBook = ""; + appData.defaultHashSize = 64; + appData.defaultCacheSizeEGTB = 4; + appData.defaultPathEGTB = "c:\\egtb"; + + InitWindowPlacement( &wpMoveHistory ); + InitWindowPlacement( &wpEvalGraph ); + InitWindowPlacement( &wpEngineOutput ); + + /* [HGM] User-selectable board size, adjudication control, miscellaneous */ + appData.NrFiles = -1; + appData.NrRanks = -1; + appData.holdingsSize = -1; + appData.testClaims = FALSE; + appData.checkMates = FALSE; + appData.materialDraws= FALSE; + appData.trivialDraws = FALSE; + appData.ruleMoves = 51; + appData.drawRepeats = 6; + appData.matchPause = 10000; + appData.alphaRank = FALSE; + appData.allWhite = FALSE; + appData.upsideDown = FALSE; + #ifdef ZIPPY appData.zippyTalk = ZIPPY_TALK; appData.zippyPlay = ZIPPY_PLAY; @@ -1789,6 +1935,11 @@ InitAppData(LPSTR lpCmdLine) /* Parse command line */ ParseArgs(StringGet, &lpCmdLine); + /* [HGM] make sure board size is acceptable */ + if(appData.NrFiles > BOARD_SIZE || + appData.NrRanks > BOARD_SIZE ) + DisplayFatalError("Recompile with BOARD_SIZE > 12, to support this size", 0, 2); + /* Propagate options that affect others */ if (appData.matchMode || appData.matchGames) chessProgram = TRUE; if (appData.icsActive || appData.noChessProgram) { @@ -1939,6 +2090,39 @@ SaveSettings(char* name) gameListH = wp.rcNormalPosition.bottom - wp.rcNormalPosition.top; } + /* [AS] Move history */ + wpMoveHistory.visible = MoveHistoryIsUp(); + + if( moveHistoryDialog ) { + GetWindowPlacement(moveHistoryDialog, &wp); + wpMoveHistory.x = wp.rcNormalPosition.left; + wpMoveHistory.y = wp.rcNormalPosition.top; + wpMoveHistory.width = wp.rcNormalPosition.right - wp.rcNormalPosition.left; + wpMoveHistory.height = wp.rcNormalPosition.bottom - wp.rcNormalPosition.top; + } + + /* [AS] Eval graph */ + wpEvalGraph.visible = EvalGraphIsUp(); + + if( evalGraphDialog ) { + GetWindowPlacement(evalGraphDialog, &wp); + wpEvalGraph.x = wp.rcNormalPosition.left; + wpEvalGraph.y = wp.rcNormalPosition.top; + wpEvalGraph.width = wp.rcNormalPosition.right - wp.rcNormalPosition.left; + wpEvalGraph.height = wp.rcNormalPosition.bottom - wp.rcNormalPosition.top; + } + + /* [AS] Engine output */ + wpEngineOutput.visible = EngineOutputIsUp(); + + if( engineOutputDialog ) { + GetWindowPlacement(engineOutputDialog, &wp); + wpEngineOutput.x = wp.rcNormalPosition.left; + wpEngineOutput.y = wp.rcNormalPosition.top; + wpEngineOutput.width = wp.rcNormalPosition.right - wp.rcNormalPosition.left; + wpEngineOutput.height = wp.rcNormalPosition.bottom - wp.rcNormalPosition.top; + } + for (ad = argDescriptors; ad->argName != NULL; ad++) { if (!ad->save) continue; switch (ad->argType) { @@ -2045,431 +2229,521 @@ SaveSettings(char* name) * \*---------------------------------------------------------------------------*/ -/* [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= 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 ); - } -} - +/* [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] [HGM] Make room for more piece types, so all pieces can be different */ +enum { + PM_WP = (int) WhitePawn, + PM_WN = (int) WhiteKnight, + PM_WB = (int) WhiteBishop, + PM_WR = (int) WhiteRook, + PM_WQ = (int) WhiteQueen, + PM_WF = (int) WhiteFerz, + PM_WW = (int) WhiteWazir, + PM_WE = (int) WhiteAlfil, + PM_WM = (int) WhiteMan, + PM_WO = (int) WhiteCannon, + PM_WU = (int) WhiteUnicorn, + PM_WH = (int) WhiteNightrider, + PM_WA = (int) WhiteCardinal, + PM_WC = (int) WhiteMarshall, + PM_WG = (int) WhiteGrasshopper, + PM_WK = (int) WhiteKing, + PM_BP = (int) BlackPawn, + PM_BN = (int) BlackKnight, + PM_BB = (int) BlackBishop, + PM_BR = (int) BlackRook, + PM_BQ = (int) BlackQueen, + PM_BF = (int) BlackFerz, + PM_BW = (int) BlackWazir, + PM_BE = (int) BlackAlfil, + PM_BM = (int) BlackMan, + PM_BO = (int) BlackCannon, + PM_BU = (int) BlackUnicorn, + PM_BH = (int) BlackNightrider, + PM_BA = (int) BlackCardinal, + PM_BC = (int) BlackMarshall, + PM_BG = (int) BlackGrasshopper, + PM_BK = (int) BlackKing +}; + +static HFONT hPieceFont = NULL; +static HBITMAP hPieceMask[(int) EmptySquare]; +static HBITMAP hPieceFace[(int) EmptySquare]; +static int fontBitmapSquareSize = 0; +static char pieceToFontChar[(int) EmptySquare] = + { 'p', 'n', 'b', 'r', 'q', + 'n', 'b', 'p', 'n', 'b', 'r', 'b', 'r', 'q', 'k', + 'k', 'o', 'm', 'v', 't', 'w', + 'v', 't', 'o', 'm', 'v', 't', 'v', 't', 'w', 'l', + 'l' }; + +extern BOOL SetCharTable( char *table, const char * map ); +/* [HGM] moved to backend.c */ + +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= 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 ); + + if( hPieceFace[index] != NULL ) { + DeleteObject( hPieceFace[index] ); + } + + 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; +#ifdef FAIRY + case BlackCardinal: + return PM_BA; + case BlackMarshall: + return PM_BC; + case BlackFerz: + return PM_BF; + case BlackNightrider: + return PM_BH; + case BlackAlfil: + return PM_BE; + case BlackWazir: + return PM_BW; + case BlackUnicorn: + return PM_BU; + case BlackCannon: + return PM_BO; + case BlackGrasshopper: + return PM_BG; + case BlackMan: + return PM_BM; + case WhiteCardinal: + return PM_WA; + case WhiteMarshall: + return PM_WC; + case WhiteFerz: + return PM_WF; + case WhiteNightrider: + return PM_WH; + case WhiteAlfil: + return PM_WE; + case WhiteWazir: + return PM_WW; + case WhiteUnicorn: + return PM_WU; + case WhiteCannon: + return PM_WO; + case WhiteGrasshopper: + return PM_WG; + case WhiteMan: + return PM_WM; +#endif + } + + 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; + hPieceFace[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( ! SetCharTable(pieceToFontChar, appData.fontToPieceTable) ) { + /* No (or wrong) global settings, try to detect the font */ + if( strstr(lf.lfFaceName,"Alpha") != NULL ) { + /* Alpha */ + SetCharTable(pieceToFontChar, "phbrqkojntwl"); + } + else if( strstr(lf.lfFaceName,"DiagramTT") != NULL ) { + /* DiagramTT* family */ + SetCharTable(pieceToFontChar, "PNLRQKpnlrqk"); + } + else if( strstr(lf.lfFaceName,"WinboardF") != NULL ) { + /* Fairy symbols */ + SetCharTable(pieceToFontChar, "PNBRQFWEMOUHACGSKpnbrqfwemouhacgsk"); + } + else if( strstr(lf.lfFaceName,"GC2004D") != NULL ) { + /* Good Companion (Some characters get warped as literal :-( */ + char s[] = "1cmWG0ñueOS¯®oYI23wgQU"; + s[0]=0xB9; s[1]=0xA9; s[6]=0xB1; s[11]=0xBB; s[12]=0xAB; s[17]=0xB3; + SetCharTable(pieceToFontChar, s); + } + else { + /* Cases, Condal, Leipzig, Lucena, Marroquin, Merida, Usual */ + SetCharTable(pieceToFontChar, "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 ); +#ifdef FAIRY + CreatePieceMaskFromFont( hdc_window, hdc, PM_WA ); + CreatePieceMaskFromFont( hdc_window, hdc, PM_WC ); + CreatePieceMaskFromFont( hdc_window, hdc, PM_WF ); + CreatePieceMaskFromFont( hdc_window, hdc, PM_WH ); + CreatePieceMaskFromFont( hdc_window, hdc, PM_WE ); + CreatePieceMaskFromFont( hdc_window, hdc, PM_WW ); + CreatePieceMaskFromFont( hdc_window, hdc, PM_WU ); + CreatePieceMaskFromFont( hdc_window, hdc, PM_WO ); + CreatePieceMaskFromFont( hdc_window, hdc, PM_WG ); + CreatePieceMaskFromFont( hdc_window, hdc, PM_WM ); + CreatePieceMaskFromFont( hdc_window, hdc, PM_BA ); + CreatePieceMaskFromFont( hdc_window, hdc, PM_BC ); + CreatePieceMaskFromFont( hdc_window, hdc, PM_BF ); + CreatePieceMaskFromFont( hdc_window, hdc, PM_BH ); + CreatePieceMaskFromFont( hdc_window, hdc, PM_BE ); + CreatePieceMaskFromFont( hdc_window, hdc, PM_BW ); + CreatePieceMaskFromFont( hdc_window, hdc, PM_BU ); + CreatePieceMaskFromFont( hdc_window, hdc, PM_BO ); + CreatePieceMaskFromFont( hdc_window, hdc, PM_BG ); + CreatePieceMaskFromFont( hdc_window, hdc, PM_BM ); +#endif + + SelectObject( hdc, hfont_old ); + + fontBitmapSquareSize = squareSize; + } + } + + if( hdc != NULL ) { + DeleteDC( hdc ); + } + + if( hdc_window != NULL ) { + ReleaseDC( hwndMain, hdc_window ); + } +} + HBITMAP DoLoadBitmap(HINSTANCE hinst, char *piece, int squareSize, char *suffix) { @@ -2537,29 +2811,30 @@ InitDrawingColors() hPal = CreatePalette((LPLOGPALETTE) pLogPal); lightSquareBrush = CreateSolidBrush(lightSquareColor); + blackSquareBrush = CreateSolidBrush(blackPieceColor); darkSquareBrush = CreateSolidBrush(darkSquareColor); whitePieceBrush = CreateSolidBrush(whitePieceColor); blackPieceBrush = CreateSolidBrush(blackPieceColor); iconBkgndBrush = CreateSolidBrush(GetSysColor(COLOR_BACKGROUND)); - - /* [AS] Force rendering of the font-based pieces */ - if( fontBitmapSquareSize > 0 ) { - fontBitmapSquareSize = 0; - } + + /* [AS] Force rendering of the font-based pieces */ + if( fontBitmapSquareSize > 0 ) { + fontBitmapSquareSize = 0; + } } int -BoardWidth(int boardSize) -{ - 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; +BoardWidth(int boardSize, int n) +{ /* [HGM] argument n added to allow different width and height */ + int lineGap = sizeInfo[boardSize].lineGap; + + if( appData.overrideLineGap >= 0 && appData.overrideLineGap <= 5 ) { + lineGap = appData.overrideLineGap; + } + + return (n + 1) * lineGap + + n * sizeInfo[boardSize].squareSize; } /* Respond to board resize by dragging edge */ @@ -2586,7 +2861,7 @@ ResizeBoard(int newSizeX, int newSizeY, int flags) VOID InitDrawingSizes(BoardSize boardSize, int flags) { - int i, boardWidth; + int i, boardWidth, boardHeight; /* [HGM] height treated separately */ ChessSquare piece; static int oldBoardSize = -1, oldTinyLayout = 0; HDC hdc; @@ -2599,15 +2874,19 @@ InitDrawingSizes(BoardSize boardSize, int flags) int offby; LOGBRUSH logbrush; + /* [HGM] call with -1 uses old size (for if nr of files, ranks changes) */ + if(boardSize == (BoardSize)(-2) ) boardSize = oldBoardSize; + tinyLayout = sizeInfo[boardSize].tinyLayout; smallLayout = sizeInfo[boardSize].smallLayout; squareSize = sizeInfo[boardSize].squareSize; lineGap = sizeInfo[boardSize].lineGap; + minorSize = 0; /* [HGM] Kludge to see if demagnified pieces need to be shifted */ + + if( appData.overrideLineGap >= 0 && appData.overrideLineGap <= 5 ) { + lineGap = appData.overrideLineGap; + } - if( appData.overrideLineGap >= 0 && appData.overrideLineGap <= 5 ) { - lineGap = appData.overrideLineGap; - } - if (tinyLayout != oldTinyLayout) { long style = GetWindowLong(hwndMain, GWL_STYLE); if (tinyLayout) { @@ -2627,7 +2906,8 @@ InitDrawingSizes(BoardSize boardSize, int flags) DrawMenuBar(hwndMain); } - boardWidth = BoardWidth(boardSize); + boardWidth = BoardWidth(boardSize, BOARD_WIDTH); + boardHeight = BoardWidth(boardSize, BOARD_HEIGHT); /* Get text area sizes */ hdc = GetDC(hwndMain); @@ -2668,7 +2948,7 @@ InitDrawingSizes(BoardSize boardSize, int flags) boardRect.left = whiteRect.left; boardRect.right = boardRect.left + boardWidth; boardRect.top = messageRect.bottom + INNER_MARGIN; - boardRect.bottom = boardRect.top + boardWidth; + boardRect.bottom = boardRect.top + boardHeight; sizeInfo[boardSize].cliWidth = boardRect.right + OUTER_MARGIN; sizeInfo[boardSize].cliHeight = boardRect.bottom + OUTER_MARGIN; @@ -2756,30 +3036,40 @@ InitDrawingSizes(BoardSize boardSize, int flags) ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER, lineGap, &logbrush, 0, NULL); - for (i = 0; i < BOARD_SIZE + 1; i++) { + /* [HGM] Loop had to be split in part for vert. and hor. lines */ + for (i = 0; i < BOARD_HEIGHT + 1; i++) { gridEndpoints[i*2].x = boardRect.left + lineGap / 2; - gridEndpoints[i*2 + BOARD_SIZE*2 + 2].y = boardRect.top + lineGap / 2; gridEndpoints[i*2].y = gridEndpoints[i*2 + 1].y = boardRect.top + lineGap / 2 + (i * (squareSize + lineGap)); gridEndpoints[i*2 + 1].x = boardRect.left + lineGap / 2 + - BOARD_SIZE * (squareSize + lineGap); - gridEndpoints[i*2 + BOARD_SIZE*2 + 2].x = - gridEndpoints[i*2 + 1 + BOARD_SIZE*2 + 2].x = boardRect.left + + BOARD_WIDTH * (squareSize + lineGap); + lineGap / 2 + (i * (squareSize + lineGap)); + gridVertexCounts[i*2] = gridVertexCounts[i*2 + 1] = 2; + } + for (i = 0; i < BOARD_WIDTH + 1; i++) { + gridEndpoints[i*2 + BOARD_HEIGHT*2 + 2].y = boardRect.top + lineGap / 2; + gridEndpoints[i*2 + BOARD_HEIGHT*2 + 2].x = + gridEndpoints[i*2 + 1 + BOARD_HEIGHT*2 + 2].x = boardRect.left + lineGap / 2 + (i * (squareSize + lineGap)); - gridEndpoints[i*2 + 1 + BOARD_SIZE*2 + 2].y = - boardRect.top + BOARD_SIZE * (squareSize + lineGap); + gridEndpoints[i*2 + 1 + BOARD_HEIGHT*2 + 2].y = + boardRect.top + BOARD_HEIGHT * (squareSize + lineGap); gridVertexCounts[i*2] = gridVertexCounts[i*2 + 1] = 2; } } - if (boardSize == oldBoardSize) return; +#ifdef GOTHIC + /* [HGM] Gothic licensing requirement */ + GothicPopUp( GOTHIC, gameInfo.variant == VariantGothic ); +#endif + +/* if (boardSize == oldBoardSize) return; [HGM] variant might have changed */ oldBoardSize = boardSize; oldTinyLayout = tinyLayout; /* Load piece bitmaps for this board size */ for (i=0; i<=2; i++) { for (piece = WhitePawn; - (int) piece <= (int) WhiteKing; + (int) piece < (int) BlackPawn; piece = (ChessSquare) ((int) piece + 1)) { if (pieceBitmap[i][piece] != NULL) DeleteObject(pieceBitmap[i][piece]); @@ -2790,21 +3080,97 @@ InitDrawingSizes(BoardSize boardSize, int flags) pieceBitmap[0][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "s"); pieceBitmap[0][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "s"); pieceBitmap[0][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "s"); - pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "s"); pieceBitmap[0][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "s"); pieceBitmap[1][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "o"); pieceBitmap[1][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "o"); pieceBitmap[1][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "o"); pieceBitmap[1][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "o"); - pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "o"); pieceBitmap[1][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "o"); pieceBitmap[2][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "w"); pieceBitmap[2][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "w"); pieceBitmap[2][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "w"); pieceBitmap[2][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "w"); - pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "w"); pieceBitmap[2][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "w"); - + if( !strcmp(appData.variant, "shogi") && (squareSize==72 || squareSize==49)) { + pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "s"); + pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "o"); + pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "w"); + } else { + pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "s"); + pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "o"); + pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "w"); + } + if(squareSize==72 || squareSize==49) { /* experiment with some home-made bitmaps */ + pieceBitmap[0][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "s"); + pieceBitmap[1][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "o"); + pieceBitmap[2][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "w"); + pieceBitmap[0][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "s"); + pieceBitmap[1][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "o"); + pieceBitmap[2][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "w"); + pieceBitmap[0][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "s"); + pieceBitmap[1][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "o"); + pieceBitmap[2][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "w"); + pieceBitmap[0][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "s"); + pieceBitmap[1][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "o"); + pieceBitmap[2][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "w"); + pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "s"); + pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "o"); + pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "w"); + pieceBitmap[0][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "s"); + pieceBitmap[1][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "o"); + pieceBitmap[2][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "w"); + if(gameInfo.variant == VariantShogi) { /* promoted Gold represemtations */ + pieceBitmap[0][WhiteUnicorn] = DoLoadBitmap(hInst, "wp", squareSize, "s"); + pieceBitmap[1][WhiteUnicorn] = DoLoadBitmap(hInst, "wp", squareSize, "o"); + pieceBitmap[2][WhiteUnicorn] = DoLoadBitmap(hInst, "w", squareSize, "w"); + pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "wn", squareSize, "s"); + pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "wn", squareSize, "o"); + pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "w", squareSize, "w"); + pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "ws", squareSize, "s"); + pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "ws", squareSize, "o"); + pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "w", squareSize, "w"); + pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "wl", squareSize, "s"); + pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "wl", squareSize, "o"); + pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "w", squareSize, "w"); + } else { + pieceBitmap[0][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "s"); + pieceBitmap[1][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "o"); + pieceBitmap[2][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "w"); + pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "s"); + pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "o"); + pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "w"); + pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "s"); + pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "o"); + pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "w"); + pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "l", squareSize, "s"); + pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "l", squareSize, "o"); + pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "l", squareSize, "w"); + } + if(gameInfo.variant != VariantCrazyhouse && gameInfo.variant != VariantShogi) { + pieceBitmap[0][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "s"); + pieceBitmap[1][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "o"); + pieceBitmap[2][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "w"); + } else { + pieceBitmap[0][WhiteMarshall] = DoLoadBitmap(hInst, "dk", squareSize, "s"); + pieceBitmap[1][WhiteMarshall] = DoLoadBitmap(hInst, "dk", squareSize, "o"); + pieceBitmap[2][WhiteMarshall] = DoLoadBitmap(hInst, "dk", squareSize, "w"); + } + } else { /* other size, no special bitmaps available. Use smaller symbols */ + if((int)boardSize < 2) minorSize = sizeInfo[0].squareSize; + else minorSize = sizeInfo[(int)boardSize - 2].squareSize; + pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "s"); + pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "o"); + pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "w"); + pieceBitmap[0][WhiteCardinal] = DoLoadBitmap(hInst, "b", minorSize, "s"); + pieceBitmap[1][WhiteCardinal] = DoLoadBitmap(hInst, "b", minorSize, "o"); + pieceBitmap[2][WhiteCardinal] = DoLoadBitmap(hInst, "b", minorSize, "w"); + pieceBitmap[0][WhiteMarshall] = DoLoadBitmap(hInst, "r", minorSize, "s"); + pieceBitmap[1][WhiteMarshall] = DoLoadBitmap(hInst, "r", minorSize, "o"); + pieceBitmap[2][WhiteMarshall] = DoLoadBitmap(hInst, "r", minorSize, "w"); + pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "s"); + pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "o"); + pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "w"); + } } HBITMAP @@ -2829,19 +3195,19 @@ VOID SquareToPos(int row, int column, int * x, int * y) { if (flipView) { - *x = boardRect.left + lineGap + ((BOARD_SIZE-1)-column) * (squareSize + lineGap); + *x = boardRect.left + lineGap + ((BOARD_WIDTH-1)-column) * (squareSize + lineGap); *y = boardRect.top + lineGap + row * (squareSize + lineGap); } else { *x = boardRect.left + lineGap + column * (squareSize + lineGap); - *y = boardRect.top + lineGap + ((BOARD_SIZE-1)-row) * (squareSize + lineGap); + *y = boardRect.top + lineGap + ((BOARD_HEIGHT-1)-row) * (squareSize + lineGap); } } VOID DrawCoordsOnDC(HDC hdc) { - static char files[16] = {'1','2','3','4','5','6','7','8','8','7','6','5','4','3','2','1'}; - static char ranks[16] = {'h','g','f','e','d','c','b','a','a','b','c','d','e','f','g','h'}; + static char files[24] = {'0', '1','2','3','4','5','6','7','8','9','0','1','1','0','9','8','7','6','5','4','3','2','1','0'}; + static char ranks[24] = {'l', 'k','j','i','h','g','f','e','d','c','b','a','a','b','c','d','e','f','g','h','i','j','k','l'}; char str[2] = { NULLCHAR, NULLCHAR }; int oldMode, oldAlign, x, y, start, i; HFONT oldFont; @@ -2850,7 +3216,7 @@ DrawCoordsOnDC(HDC hdc) if (!appData.showCoords) return; - start = flipView ? 0 : 8; + start = flipView ? 1-(ONE!='1') : 23+(ONE!='1')-BOARD_HEIGHT; oldBrush = SelectObject(hdc, GetStockObject(BLACK_BRUSH)); oldMode = SetBkMode(hdc, (appData.monoMode ? OPAQUE : TRANSPARENT)); @@ -2861,14 +3227,16 @@ DrawCoordsOnDC(HDC hdc) x = boardRect.left + lineGap; SetTextAlign(hdc, TA_LEFT|TA_TOP); - for (i = 0; i < 8; i++) { + for (i = 0; i < BOARD_HEIGHT; i++) { str[0] = files[start + i]; ExtTextOut(hdc, x + 2, y + 1, 0, NULL, str, 1, NULL); y += squareSize + lineGap; } + start = flipView ? 12-BOARD_WIDTH : 12; + SetTextAlign(hdc, TA_RIGHT|TA_BOTTOM); - for (i = 0; i < 8; i++) { + for (i = 0; i < BOARD_WIDTH; i++) { str[0] = ranks[start + i]; ExtTextOut(hdc, x + squareSize - 2, y - 1, 0, NULL, str, 1, NULL); x += squareSize + lineGap; @@ -2887,7 +3255,7 @@ DrawGridOnDC(HDC hdc) if (lineGap != 0) { oldPen = SelectObject(hdc, gridPen); - PolyPolyline(hdc, gridEndpoints, gridVertexCounts, BOARD_SIZE*2 + 2); + PolyPolyline(hdc, gridEndpoints, gridVertexCounts, BOARD_WIDTH+BOARD_HEIGHT + 2); SelectObject(hdc, oldPen); } } @@ -2903,14 +3271,14 @@ DrawHighlightOnDC(HDC hdc, BOOLEAN on, int x, int y, int pen) if (lineGap == 0) return; if (flipView) { x1 = boardRect.left + - lineGap/2 + ((BOARD_SIZE-1)-x) * (squareSize + lineGap); + lineGap/2 + ((BOARD_WIDTH-1)-x) * (squareSize + lineGap); y1 = boardRect.top + lineGap/2 + y * (squareSize + lineGap); } else { x1 = boardRect.left + lineGap/2 + x * (squareSize + lineGap); y1 = boardRect.top + - lineGap/2 + ((BOARD_SIZE-1)-y) * (squareSize + lineGap); + lineGap/2 + ((BOARD_HEIGHT-1)-y) * (squareSize + lineGap); } hPen = pen ? premovePen : highlightPen; oldPen = SelectObject(hdc, on ? hPen : gridPen); @@ -2954,45 +3322,55 @@ DrawPieceOnDC(HDC hdc, ChessSquare piece, int color, int sqcolor, int x, int y, if (appData.blindfold) return; - /* [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; - } - } - + /* [AS] Use font-based pieces if needed */ + if( fontBitmapSquareSize >= 0 && squareSize > 32 ) { + /* Create piece bitmaps, or do nothing if piece set is up to date */ + 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) { SelectObject(tmphdc, PieceBitmap(piece, color == sqcolor ? OUTLINE_PIECE : SOLID_PIECE)); BitBlt(hdc, x, y, squareSize, squareSize, tmphdc, 0, 0, sqcolor ? SRCCOPY : NOTSRCCOPY); } else { - if (color) { + if(minorSize && + (piece >= (int)WhiteNightrider && piece <= WhiteGrasshopper || + piece >= (int)BlackNightrider && piece <= BlackGrasshopper) ) { + /* [HGM] no bitmap available for promoted pieces in Crazyhouse */ + /* Bitmaps of smaller size are substituted, but we have to align them */ + x += (squareSize - minorSize)>>1; + y += squareSize - minorSize - 2; + } + if (color || appData.allWhite ) { oldBitmap = SelectObject(tmphdc, PieceBitmap(piece, WHITE_PIECE)); - oldBrush = SelectObject(hdc, whitePieceBrush); + if( color ) + oldBrush = SelectObject(hdc, whitePieceBrush); + else oldBrush = SelectObject(hdc, blackPieceBrush); BitBlt(hdc, x, y, squareSize, squareSize, tmphdc, 0, 0, 0x00B8074A); #if 0 /* Use black piece color for outline of white pieces */ @@ -3031,104 +3409,423 @@ DrawPieceOnDC(HDC hdc, ChessSquare piece, int color, int sqcolor, int x, int y, } } -/* [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= 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); - } - } - } - } -} - +/* [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= squareSize && lite_h >= squareSize ) { + backTextureSquareInfo[row][col].x = col * (lite_w - squareSize) / BOARD_WIDTH; + backTextureSquareInfo[row][col].y = row * (lite_h - squareSize) / BOARD_HEIGHT; + 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_WIDTH; + backTextureSquareInfo[row][col].y = row * (dark_h - squareSize) / BOARD_HEIGHT; + backTextureSquareInfo[row][col].mode = GetBackTextureMode(darkBackTextureMode); + } + } + } + } +} + +/* [AS] Arrow highlighting support */ + +static int A_WIDTH = 5; /* Width of arrow body */ + +#define A_HEIGHT_FACTOR 6 /* Length of arrow "point", relative to body width */ +#define A_WIDTH_FACTOR 3 /* Width of arrow "point", relative to body width */ + +static double Sqr( double x ) +{ + return x*x; +} + +static int Round( double x ) +{ + return (int) (x + 0.5); +} + +/* Draw an arrow between two points using current settings */ +VOID DrawArrowBetweenPoints( HDC hdc, int s_x, int s_y, int d_x, int d_y ) +{ + POINT arrow[7]; + double dx, dy, j, k, x, y; + + if( d_x == s_x ) { + int h = (d_y > s_y) ? +A_WIDTH*A_HEIGHT_FACTOR : -A_WIDTH*A_HEIGHT_FACTOR; + + arrow[0].x = s_x + A_WIDTH; + arrow[0].y = s_y; + + arrow[1].x = s_x + A_WIDTH; + arrow[1].y = d_y - h; + + arrow[2].x = s_x + A_WIDTH*A_WIDTH_FACTOR; + arrow[2].y = d_y - h; + + arrow[3].x = d_x; + arrow[3].y = d_y; + + arrow[4].x = s_x - A_WIDTH*A_WIDTH_FACTOR; + arrow[4].y = d_y - h; + + arrow[5].x = s_x - A_WIDTH; + arrow[5].y = d_y - h; + + arrow[6].x = s_x - A_WIDTH; + arrow[6].y = s_y; + } + else if( d_y == s_y ) { + int w = (d_x > s_x) ? +A_WIDTH*A_HEIGHT_FACTOR : -A_WIDTH*A_HEIGHT_FACTOR; + + arrow[0].x = s_x; + arrow[0].y = s_y + A_WIDTH; + + arrow[1].x = d_x - w; + arrow[1].y = s_y + A_WIDTH; + + arrow[2].x = d_x - w; + arrow[2].y = s_y + A_WIDTH*A_WIDTH_FACTOR; + + arrow[3].x = d_x; + arrow[3].y = d_y; + + arrow[4].x = d_x - w; + arrow[4].y = s_y - A_WIDTH*A_WIDTH_FACTOR; + + arrow[5].x = d_x - w; + arrow[5].y = s_y - A_WIDTH; + + arrow[6].x = s_x; + arrow[6].y = s_y - A_WIDTH; + } + else { + /* [AS] Needed a lot of paper for this! :-) */ + dy = (double) (d_y - s_y) / (double) (d_x - s_x); + dx = (double) (s_x - d_x) / (double) (s_y - d_y); + + j = sqrt( Sqr(A_WIDTH) / (1.0 + Sqr(dx)) ); + + k = sqrt( Sqr(A_WIDTH*A_HEIGHT_FACTOR) / (1.0 + Sqr(dy)) ); + + x = s_x; + y = s_y; + + arrow[0].x = Round(x - j); + arrow[0].y = Round(y + j*dx); + + arrow[1].x = Round(x + j); + arrow[1].y = Round(y - j*dx); + + if( d_x > s_x ) { + x = (double) d_x - k; + y = (double) d_y - k*dy; + } + else { + x = (double) d_x + k; + y = (double) d_y + k*dy; + } + + arrow[2].x = Round(x + j); + arrow[2].y = Round(y - j*dx); + + arrow[3].x = Round(x + j*A_WIDTH_FACTOR); + arrow[3].y = Round(y - j*A_WIDTH_FACTOR*dx); + + arrow[4].x = d_x; + arrow[4].y = d_y; + + arrow[5].x = Round(x - j*A_WIDTH_FACTOR); + arrow[5].y = Round(y + j*A_WIDTH_FACTOR*dx); + + arrow[6].x = Round(x - j); + arrow[6].y = Round(y + j*dx); + } + + Polygon( hdc, arrow, 7 ); +} + +/* [AS] Draw an arrow between two squares */ +VOID DrawArrowBetweenSquares( HDC hdc, int s_col, int s_row, int d_col, int d_row ) +{ + int s_x, s_y, d_x, d_y; + HPEN hpen; + HPEN holdpen; + HBRUSH hbrush; + HBRUSH holdbrush; + LOGBRUSH stLB; + + if( s_col == d_col && s_row == d_row ) { + return; + } + + /* Get source and destination points */ + SquareToPos( s_row, s_col, &s_x, &s_y); + SquareToPos( d_row, d_col, &d_x, &d_y); + + if( d_y > s_y ) { + d_y += squareSize / 4; + } + else if( d_y < s_y ) { + d_y += 3 * squareSize / 4; + } + else { + d_y += squareSize / 2; + } + + if( d_x > s_x ) { + d_x += squareSize / 4; + } + else if( d_x < s_x ) { + d_x += 3 * squareSize / 4; + } + else { + d_x += squareSize / 2; + } + + s_x += squareSize / 2; + s_y += squareSize / 2; + + /* Adjust width */ + A_WIDTH = squareSize / 14; + + /* Draw */ + stLB.lbStyle = BS_SOLID; + stLB.lbColor = appData.highlightArrowColor; + stLB.lbHatch = 0; + + hpen = CreatePen( PS_SOLID, 2, RGB(0x00,0x00,0x00) ); + holdpen = SelectObject( hdc, hpen ); + hbrush = CreateBrushIndirect( &stLB ); + holdbrush = SelectObject( hdc, hbrush ); + + DrawArrowBetweenPoints( hdc, s_x, s_y, d_x, d_y ); + + SelectObject( hdc, holdpen ); + SelectObject( hdc, holdbrush ); + DeleteObject( hpen ); + DeleteObject( hbrush ); +} + +BOOL HasHighlightInfo() +{ + BOOL result = FALSE; + + if( highlightInfo.sq[0].x >= 0 && highlightInfo.sq[0].y >= 0 && + highlightInfo.sq[1].x >= 0 && highlightInfo.sq[1].y >= 0 ) + { + result = TRUE; + } + + return result; +} + +BOOL IsDrawArrowEnabled() +{ + BOOL result = FALSE; + + if( appData.highlightMoveWithArrow && squareSize >= 32 ) { + result = TRUE; + } + + return result; +} + +VOID DrawArrowHighlight( HDC hdc ) +{ + if( IsDrawArrowEnabled() && HasHighlightInfo() ) { + DrawArrowBetweenSquares( hdc, + highlightInfo.sq[0].x, highlightInfo.sq[0].y, + highlightInfo.sq[1].x, highlightInfo.sq[1].y ); + } +} + +HRGN GetArrowHighlightClipRegion( HDC hdc ) +{ + HRGN result = NULL; + + if( HasHighlightInfo() ) { + int x1, y1, x2, y2; + int sx, sy, dx, dy; + + SquareToPos(highlightInfo.sq[0].y, highlightInfo.sq[0].x, &x1, &y1 ); + SquareToPos(highlightInfo.sq[1].y, highlightInfo.sq[1].x, &x2, &y2 ); + + sx = MIN( x1, x2 ); + sy = MIN( y1, y2 ); + dx = MAX( x1, x2 ) + squareSize; + dy = MAX( y1, y2 ) + squareSize; + + result = CreateRectRgn( sx, sy, dx, dy ); + } + + return result; +} + +/* + Warning: this function modifies the behavior of several other functions. + + Basically, Winboard is optimized to avoid drawing the whole board if not strictly + needed. Unfortunately, the decision whether or not to perform a full or partial + repaint is scattered all over the place, which is not good for features such as + "arrow highlighting" that require a full repaint of the board. + + So, I've tried to patch the code where I thought it made sense (e.g. after or during + user interaction, when speed is not so important) but especially to avoid errors + in the displayed graphics. + + In such patched places, I always try refer to this function so there is a single + place to maintain knowledge. + + To restore the original behavior, just return FALSE unconditionally. +*/ +BOOL IsFullRepaintPreferrable() +{ + BOOL result = FALSE; + + if( (appData.highlightLastMove || appData.highlightDragging) && IsDrawArrowEnabled() ) { + /* Arrow may appear on the board */ + result = TRUE; + } + + return result; +} + +/* + This function is called by DrawPosition to know whether a full repaint must + be forced or not. + + Only DrawPosition may directly call this function, which makes use of + some state information. Other function should call DrawPosition specifying + the repaint flag, and can use IsFullRepaintPreferrable if needed. +*/ +BOOL DrawPositionNeedsFullRepaint() +{ + BOOL result = FALSE; + + /* + Probably a slightly better policy would be to trigger a full repaint + when animInfo.piece changes state (i.e. empty -> non-empty and viceversa), + but animation is fast enough that it's difficult to notice. + */ + if( animInfo.piece == EmptySquare ) { + if( (appData.highlightLastMove || appData.highlightDragging) && IsDrawArrowEnabled() && HasHighlightInfo() ) { + result = TRUE; + } + } + + return result; +} + VOID DrawBoardOnDC(HDC hdc, Board board, HDC tmphdc) { int row, column, x, y, square_color, piece_color; ChessSquare piece; HBRUSH oldBrush; - 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 ); - } - - for (row = 0; row < BOARD_SIZE; row++) { - for (column = 0; column < BOARD_SIZE; column++) { + 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 ); + } + + for (row = 0; row < BOARD_HEIGHT; row++) { + for (column = 0; column < BOARD_WIDTH; column++) { SquareToPos(row, column, &x, &y); piece = board[row][column]; square_color = ((column + row) % 2) == 1; + if(!strcmp(appData.variant, "xiangqi") ) { + square_color = !InPalace(row, column); + if(BOARD_HEIGHT&1) { if(row==BOARD_HEIGHT/2) square_color ^= 1; } + else if(row < BOARD_HEIGHT/2) square_color ^= 1; + } piece_color = (int) piece < (int) BlackPawn; + +#ifdef FAIRY + /* [HGM] holdings file: light square or black */ + if(column == BOARD_LEFT-2) { + if( row > BOARD_HEIGHT - gameInfo.holdingsSize - 1 ) + square_color = 1; + else { + DisplayHoldingsCount(hdc, x, y, 0, 0); /* black out */ + continue; + } + } else + if(column == BOARD_RGHT + 1 ) { + if( row < gameInfo.holdingsSize ) + square_color = 1; + else { + DisplayHoldingsCount(hdc, x, y, 0, 0); + continue; + } + } + if(column == BOARD_LEFT-1 ) /* left align */ + DisplayHoldingsCount(hdc, x, y, 0, (int) board[row][column]); + else if( column == BOARD_RGHT) /* right align */ + DisplayHoldingsCount(hdc, x, y, 1, (int) board[row][column]); + else +#endif if (appData.monoMode) { if (piece == EmptySquare) { BitBlt(hdc, x, y, squareSize, squareSize, 0, 0, 0, @@ -3136,28 +3833,29 @@ DrawBoardOnDC(HDC hdc, Board board, HDC tmphdc) } else { DrawPieceOnDC(hdc, piece, piece_color, square_color, x, y, tmphdc); } - } - 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 ? - lightSquareBrush : darkSquareBrush); + } + 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 { + HBRUSH brush = square_color ? lightSquareBrush : darkSquareBrush; + + oldBrush = SelectObject(hdc, brush ); BitBlt(hdc, x, y, squareSize, squareSize, 0, 0, 0, PATCOPY); SelectObject(hdc, oldBrush); if (piece != EmptySquare) @@ -3165,10 +3863,10 @@ DrawBoardOnDC(HDC hdc, Board board, HDC tmphdc) } } } - - if( texture_hdc != NULL ) { - DeleteDC( texture_hdc ); - } + + if( texture_hdc != NULL ) { + DeleteDC( texture_hdc ); + } } #define MAX_CLIPS 200 /* more than enough */ @@ -3199,6 +3897,21 @@ HDCDrawPosition(HDC hdc, BOOLEAN repaint, Board board) */ Boolean fullrepaint = repaint; + if( DrawPositionNeedsFullRepaint() ) { + fullrepaint = TRUE; + } + +#if 0 + if( fullrepaint ) { + static int repaint_count = 0; + char buf[128]; + + repaint_count++; + sprintf( buf, "FULL repaint: %d\n", repaint_count ); + OutputDebugString( buf ); + } +#endif + if (board == NULL) { if (!lastReqValid) { return; @@ -3241,15 +3954,15 @@ HDCDrawPosition(HDC hdc, BOOLEAN repaint, Board board) dragInfo.pos.x, dragInfo.pos.y, dragInfo.lastpos.x, dragInfo.lastpos.y); fprintf(debugFP, "prev: "); - for (row = 0; row < 8; row++) { - for (column = 0; column < 8; column++) { + for (row = 0; row < BOARD_HEIGHT; row++) { + for (column = 0; column < BOARD_WIDTH; column++) { fprintf(debugFP, "%d ", lastDrawn[row][column]); } } fprintf(debugFP, "\n"); fprintf(debugFP, "board: "); - for (row = 0; row < 8; row++) { - for (column = 0; column < 8; column++) { + for (row = 0; row < BOARD_HEIGHT; row++) { + for (column = 0; column < BOARD_WIDTH; column++) { fprintf(debugFP, "%d ", board[row][column]); } } @@ -3261,13 +3974,29 @@ HDCDrawPosition(HDC hdc, BOOLEAN repaint, Board board) hdcmem = CreateCompatibleDC(hdc); tmphdc = CreateCompatibleDC(hdc); + /* If dragging is in progress, we temporarely remove the piece */ + /* [HGM] or temporarily decrease count if stacked */ + /* !! Moved to before board compare !! */ + if (dragInfo.from.x >= 0 && dragInfo.pos.x >= 0) { + dragged_piece = board[dragInfo.from.y][dragInfo.from.x]; + if(dragInfo.from.x == BOARD_LEFT-2 ) { + if(--board[dragInfo.from.y][dragInfo.from.x+1] == 0 ) + board[dragInfo.from.y][dragInfo.from.x] = EmptySquare; + } else + if(dragInfo.from.x == BOARD_RGHT+1) { + if(--board[dragInfo.from.y][dragInfo.from.x-1] == 0 ) + board[dragInfo.from.y][dragInfo.from.x] = EmptySquare; + } else + board[dragInfo.from.y][dragInfo.from.x] = EmptySquare; + } + /* Figure out which squares need updating by comparing the * newest board with the last drawn board and checking if * flipping has changed. */ if (!fullrepaint && lastDrawnValid && lastDrawnFlipView == flipView) { - for (row = 0; row < 8; row++) { - for (column = 0; column < 8; column++) { + for (row = 0; row < BOARD_HEIGHT; row++) { /* [HGM] true size, not 8 */ + for (column = 0; column < BOARD_WIDTH; column++) { if (lastDrawn[row][column] != board[row][column]) { SquareToPos(row, column, &x, &y); clips[num_clips++] = @@ -3354,12 +4083,6 @@ HDCDrawPosition(HDC hdc, BOOLEAN repaint, Board board) } } - /* If dragging is in progress, we temporarely remove the piece */ - if (dragInfo.from.x >= 0 && dragInfo.pos.x >= 0) { - dragged_piece = board[dragInfo.from.y][dragInfo.from.x]; - board[dragInfo.from.y][dragInfo.from.x] = EmptySquare; - } - /* Are we animating a move? * If so, * - remove the piece from the board (temporarely) @@ -3401,10 +4124,24 @@ HDCDrawPosition(HDC hdc, BOOLEAN repaint, Board board) DrawGridOnDC(hdcmem); DrawHighlightsOnDC(hdcmem); DrawBoardOnDC(hdcmem, board, tmphdc); + + if( appData.highlightMoveWithArrow ) { + DrawArrowHighlight(hdcmem); + } + DrawCoordsOnDC(hdcmem); - /* Put the dragged piece back into place and draw it */ - if (dragged_piece != EmptySquare) { + CopyBoard(lastDrawn, board); /* [HGM] Moved to here from end of routine, */ + /* to make sure lastDrawn contains what is actually drawn */ + + /* Put the dragged piece back into place and draw it (out of place!) */ + if (dragged_piece != EmptySquare) { + /* [HGM] or restack */ + if(dragInfo.from.x == BOARD_LEFT-2 ) + board[dragInfo.from.y][dragInfo.from.x+1]++; + else + if(dragInfo.from.x == BOARD_RGHT+1 ) + board[dragInfo.from.y][dragInfo.from.x-1]++; board[dragInfo.from.y][dragInfo.from.x] = dragged_piece; x = dragInfo.pos.x - squareSize / 2; y = dragInfo.pos.y - squareSize / 2; @@ -3465,7 +4202,7 @@ HDCDrawPosition(HDC hdc, BOOLEAN repaint, Board board) CheckMenuItem(GetMenu(hwndMain),IDM_FlipView, MF_BYCOMMAND|MF_UNCHECKED); } - CopyBoard(lastDrawn, board); +/* CopyBoard(lastDrawn, board);*/ lastDrawnHighlight = highlightInfo; lastDrawnPremove = premoveHighlightInfo; lastDrawnFlipView = flipView; @@ -3578,8 +4315,11 @@ MouseEvent(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) POINT pt; static int recursive = 0; HMENU hmenu; + BOOLEAN needsRedraw = FALSE; BOOLEAN saveAnimate; + BOOLEAN forceFullRepaint = IsFullRepaintPreferrable(); /* [AS] */ static BOOLEAN sameAgain = FALSE; + ChessMove moveType; if (recursive) { if (message == WM_MBUTTONUP) { @@ -3598,10 +4338,10 @@ MouseEvent(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) x = EventToSquare(pt.x - boardRect.left); y = EventToSquare(pt.y - boardRect.top); if (!flipView && y >= 0) { - y = BOARD_SIZE - 1 - y; + y = BOARD_HEIGHT - 1 - y; } if (flipView && x >= 0) { - x = BOARD_SIZE - 1 - x; + x = BOARD_WIDTH - 1 - x; } switch (message) { @@ -3611,79 +4351,93 @@ MouseEvent(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) if (y == -2) { /* Downclick vertically off board; check if on clock */ if (PtInRect((LPRECT) &whiteRect, pt)) { - if (gameMode == EditPosition) { + if (gameMode == EditPosition) { SetWhiteToPlayEvent(); } else if (gameMode == IcsPlayingBlack || gameMode == MachinePlaysWhite) { CallFlagEvent(); - } + } else if (gameMode == EditGame) { + AdjustClock(flipClock, -1); + } } else if (PtInRect((LPRECT) &blackRect, pt)) { if (gameMode == EditPosition) { SetBlackToPlayEvent(); } else if (gameMode == IcsPlayingWhite || gameMode == MachinePlaysBlack) { CallFlagEvent(); + } else if (gameMode == EditGame) { + AdjustClock(!flipClock, -1); } } if (!appData.highlightLastMove) { ClearHighlights(); - DrawPosition(FALSE, NULL); + DrawPosition(forceFullRepaint || FALSE, NULL); } fromX = fromY = -1; dragInfo.start.x = dragInfo.start.y = -1; dragInfo.from = dragInfo.start; break; - } else if (x < 0 || y < 0) { + } else if (x < 0 || y < 0 + /* [HGM] block clicks between board and holdings */ + || x == BOARD_LEFT-1 || x == BOARD_RGHT + || x == BOARD_LEFT-2 && y < BOARD_HEIGHT-gameInfo.holdingsSize + || x == BOARD_RGHT+1 && y >= gameInfo.holdingsSize + /* EditPosition, empty square, or different color piece; + click-click move is possible */ + ) { break; } else if (fromX == x && fromY == y) { /* Downclick on same square again */ ClearHighlights(); - DrawPosition(FALSE, NULL); + DrawPosition(forceFullRepaint || FALSE, NULL); sameAgain = TRUE; - } else if (fromX != -1) { - /* Downclick on different square */ - ChessSquare pdown, pup; - pdown = boards[currentMove][fromY][fromX]; - pup = boards[currentMove][y][x]; - if (gameMode == EditPosition || - !((WhitePawn <= pdown && pdown <= WhiteKing && - WhitePawn <= pup && pup <= WhiteKing) || - (BlackPawn <= pdown && pdown <= BlackKing && - BlackPawn <= pup && pup <= BlackKing))) { - /* EditPosition, empty square, or different color piece; - click-click move is possible */ + } else if (fromX != -1 && + x != BOARD_LEFT-2 && x != BOARD_RGHT+1 + ) { + /* Downclick on different square. */ + /* [HGM] if on holdings file, should count as new first click ! */ + { /* [HGM] now always do UserMoveTest(), and check colors there */ toX = x; toY = y; - if (IsPromotion(fromX, fromY, toX, toY)) { - if (appData.alwaysPromoteToQueen) { - UserMoveEvent(fromX, fromY, toX, toY, 'q'); - if (!appData.highlightLastMove) { - ClearHighlights(); - DrawPosition(FALSE, NULL); - } - } else { - SetHighlights(fromX, fromY, toX, toY); - DrawPosition(FALSE, NULL); - PromotionPopup(hwnd); - } - } else { /* not a promotion */ - if (appData.animate || appData.highlightLastMove) { - SetHighlights(fromX, fromY, toX, toY); - } else { - ClearHighlights(); - } - UserMoveEvent(fromX, fromY, toX, toY, NULLCHAR); - if (appData.animate && !appData.highlightLastMove) { - ClearHighlights(); - DrawPosition(FALSE, NULL); - } - } + /* [HGM] UserMoveEvent requires two calls now, + to make sure move is legal before showing promotion popup */ + moveType = UserMoveTest(fromX, fromY, toX, toY, NULLCHAR); + if(moveType != ImpossibleMove) { + /* [HGM] We use PromotionToKnight in Shogi to indicate frorced promotion */ + if (moveType == WhitePromotionKnight || moveType == BlackPromotionKnight || + (moveType == WhitePromotionQueen || moveType == BlackPromotionQueen) && + appData.alwaysPromoteToQueen) { + FinishMove(moveType, fromX, fromY, toX, toY, 'q'); + if (!appData.highlightLastMove) { + ClearHighlights(); + DrawPosition(forceFullRepaint || FALSE, NULL); + } + } else + if (moveType == WhitePromotionQueen || moveType == BlackPromotionQueen ) { + SetHighlights(fromX, fromY, toX, toY); + DrawPosition(forceFullRepaint || FALSE, NULL); + /* [HGM] Popup calls FinishMove now. + If promotion to Q is legal, all are legal! */ + PromotionPopup(hwnd); + } else { /* not a promotion */ + if (appData.animate || appData.highlightLastMove) { + SetHighlights(fromX, fromY, toX, toY); + } else { + ClearHighlights(); + } + FinishMove(moveType, fromX, fromY, toX, toY, NULLCHAR); + if (appData.animate && !appData.highlightLastMove) { + ClearHighlights(); + DrawPosition(forceFullRepaint || FALSE, NULL); + } + } + } if (gotPremove) SetPremoveHighlights(fromX, fromY, toX, toY); fromX = fromY = -1; break; } ClearHighlights(); - DrawPosition(FALSE, NULL); + DrawPosition(forceFullRepaint || FALSE, NULL); } /* First downclick, or restart on a square with same color piece */ if (!frozen && OKToStartUserMove(x, y)) { @@ -3698,6 +4452,7 @@ MouseEvent(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) fromX = fromY = -1; dragInfo.start.x = dragInfo.start.y = -1; dragInfo.from = dragInfo.start; + DrawPosition(forceFullRepaint || FALSE, NULL); /* [AS] */ } break; @@ -3705,7 +4460,6 @@ MouseEvent(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) ReleaseCapture(); if (fromX == -1) break; if (x == fromX && y == fromY) { - dragInfo.from.x = dragInfo.from.y = -1; /* Upclick on same square */ if (sameAgain) { /* Clicked same square twice: abort click-click move */ @@ -3716,26 +4470,34 @@ MouseEvent(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) /* First square clicked: start click-click move */ SetHighlights(fromX, fromY, -1, -1); } - DrawPosition(FALSE, NULL); + dragInfo.from.x = dragInfo.from.y = -1; + DrawPosition(forceFullRepaint || FALSE, NULL); } else if (dragInfo.from.x < 0 || dragInfo.from.y < 0) { /* Errant click; ignore */ break; } else { - /* Finish drag move */ + /* Finish drag move. */ + if (appData.debugMode) { + fprintf(debugFP, "release\n"); + } dragInfo.from.x = dragInfo.from.y = -1; toX = x; toY = y; saveAnimate = appData.animate; /* sorry, Hawk :) */ appData.animate = appData.animate && !appData.animateDragging; - if (IsPromotion(fromX, fromY, toX, toY)) { - if (appData.alwaysPromoteToQueen) { - UserMoveEvent(fromX, fromY, toX, toY, 'q'); - } else { - DrawPosition(FALSE, NULL); - PromotionPopup(hwnd); - } - } else { - UserMoveEvent(fromX, fromY, toX, toY, NULLCHAR); + moveType = UserMoveTest(fromX, fromY, toX, toY, NULLCHAR); + if(moveType != ImpossibleMove) { + /* [HGM] use move type to determine if move is promotion. + Knight is Shogi kludge for mandatory promotion, Queen means choice */ + if (moveType == WhitePromotionKnight || moveType == BlackPromotionKnight || + (moveType == WhitePromotionQueen || moveType == BlackPromotionQueen) && + appData.alwaysPromoteToQueen) + FinishMove(moveType, fromX, fromY, toX, toY, 'q'); + else + if (moveType == WhitePromotionQueen || moveType == BlackPromotionQueen ) { + DrawPosition(forceFullRepaint || FALSE, NULL); + PromotionPopup(hwnd); /* [HGM] Popup now calls FinishMove */ + } else FinishMove(moveType, fromX, fromY, toX, toY, NULLCHAR); } if (gotPremove) SetPremoveHighlights(fromX, fromY, toX, toY); appData.animate = saveAnimate; @@ -3745,7 +4507,7 @@ MouseEvent(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) } if (appData.animate || appData.animateDragging || appData.highlightDragging || gotPremove) { - DrawPosition(FALSE, NULL); + DrawPosition(forceFullRepaint || FALSE, NULL); } } dragInfo.start.x = dragInfo.start.y = -1; @@ -3755,14 +4517,23 @@ MouseEvent(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) case WM_MOUSEMOVE: if ((appData.animateDragging || appData.highlightDragging) && (wParam & MK_LBUTTON) - && dragInfo.from.x >= 0) { + && dragInfo.from.x >= 0) + { + BOOL full_repaint = FALSE; + + sameAgain = FALSE; /* [HGM] if we drag something around, do keep square selected */ if (appData.animateDragging) { dragInfo.pos = pt; } if (appData.highlightDragging) { SetHighlights(fromX, fromY, x, y); + if( IsDrawArrowEnabled() && (x < 0 || x >= BOARD_WIDTH || y < 0 || y >= BOARD_HEIGHT) ) { + full_repaint = TRUE; + } } - DrawPosition(FALSE, NULL); + + DrawPosition( full_repaint, NULL); + dragInfo.lastpos = dragInfo.pos; } break; @@ -3779,6 +4550,14 @@ MouseEvent(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) if (appData.highlightDragging) { ClearHighlights(); } + if(y == -2) { + /* [HGM] right mouse button in clock area edit-game mode ups clock */ + if (PtInRect((LPRECT) &whiteRect, pt)) { + if (gameMode == EditGame) AdjustClock(flipClock, 1); + } else if (PtInRect((LPRECT) &blackRect, pt)) { + if (gameMode == EditGame) AdjustClock(!flipClock, 1); + } + } DrawPosition(TRUE, NULL); switch (gameMode) { @@ -3807,7 +4586,10 @@ MouseEvent(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) /* Just have one menu, on the right button. Windows users don't think to try the middle one, and sometimes other software steals it, or it doesn't really exist. */ - MenuPopup(hwnd, pt, LoadMenu(hInst, "PieceMenu"), -1); + if(gameInfo.variant != VariantShogi) + MenuPopup(hwnd, pt, LoadMenu(hInst, "PieceMenu"), -1); + else + MenuPopup(hwnd, pt, LoadMenu(hInst, "ShogiPieceMenu"), -1); #endif } break; @@ -3908,6 +4690,22 @@ Promotion(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam) (!appData.testLegality || gameInfo.variant == VariantSuicide || gameInfo.variant == VariantGiveaway) ? SW_SHOW : SW_HIDE); + /* [HGM] Only allow C & A promotions if these pieces are defined */ + ShowWindow(GetDlgItem(hDlg, PB_Archbishop), + (PieceToChar(WhiteCardinal) != '.' || + PieceToChar(BlackCardinal) != '.' ) ? + SW_SHOW : SW_HIDE); + ShowWindow(GetDlgItem(hDlg, PB_Chancellor), + (PieceToChar(WhiteMarshall) != '.' || + PieceToChar(BlackMarshall) != '.' ) ? + SW_SHOW : SW_HIDE); + /* [HGM] Hide B & R button in Shogi, use Q as promote, N as defer */ + ShowWindow(GetDlgItem(hDlg, PB_Rook), + gameInfo.variant != VariantShogi ? + SW_SHOW : SW_HIDE); + ShowWindow(GetDlgItem(hDlg, PB_Bishop), + gameInfo.variant != VariantShogi ? + SW_SHOW : SW_HIDE); return TRUE; case WM_COMMAND: /* message: received a command */ @@ -3918,25 +4716,35 @@ Promotion(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam) DrawPosition(FALSE, NULL); return TRUE; case PB_King: - promoChar = 'k'; + promoChar = PieceToChar(BlackKing); break; case PB_Queen: - promoChar = 'q'; + promoChar = gameInfo.variant == VariantShogi ? '+' : PieceToChar(BlackQueen); break; case PB_Rook: - promoChar = 'r'; + promoChar = PieceToChar(BlackRook); break; case PB_Bishop: - promoChar = 'b'; + promoChar = PieceToChar(BlackBishop); + break; + case PB_Chancellor: + promoChar = PieceToChar(BlackMarshall); + break; + case PB_Archbishop: + promoChar = PieceToChar(BlackCardinal); break; case PB_Knight: - promoChar = 'n'; + promoChar = gameInfo.variant == VariantShogi ? '=' : PieceToChar(BlackKnight); break; default: return FALSE; } EndDialog(hDlg, TRUE); /* Exit the dialog */ - UserMoveEvent(fromX, fromY, toX, toY, promoChar); + /* [HGM] Call FinishMove rather than UserMoveEvent, as we + only show the popup when we are already sure the move is valid or + legal. We pass a faulty move type, but the kludge is that FinishMove + will figure out it is a promotion from the promoChar. */ + FinishMove(NormalMove, fromX, fromY, toX, toY, promoChar); if (!appData.highlightLastMove) { ClearHighlights(); DrawPosition(FALSE, NULL); @@ -4050,6 +4858,7 @@ WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) FILE *f; UINT number; char fileTitle[MSG_SIZ]; + static SnapData sd; switch (message) { @@ -4145,6 +4954,17 @@ WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) AnalysisPopDown(); break; + case IDM_NewGameFRC: + if( NewGameFRC() == 0 ) { + ResetGameEvent(); + AnalysisPopDown(); + } + break; + + case IDM_NewVariant: + NewVariantPopup(hwnd); + break; + case IDM_LoadGame: LoadGameDialog(hwnd, "Load Game from File"); break; @@ -4217,24 +5037,63 @@ WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) PasteGameFromClipboard(); break; - /* [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_CopyGameListToClipboard: + CopyGameListToClipboard(); + break; + + /* [AS] Autodetect FEN or PGN data */ + case IDM_PasteAny: + PasteGameOrFENFromClipboard(); + break; + + /* [AS] Move history */ + case IDM_ShowMoveHistory: + if( MoveHistoryIsUp() ) { + MoveHistoryPopDown(); + } + else { + MoveHistoryPopUp(); + } + break; + + /* [AS] Eval graph */ + case IDM_ShowEvalGraph: + if( EvalGraphIsUp() ) { + EvalGraphPopDown(); + } + else { + EvalGraphPopUp(); + } + break; + + /* [AS] Engine output */ + case IDM_ShowEngineOutput: + if( EngineOutputIsUp() ) { + EngineOutputPopDown(); + } + else { + EngineOutputPopUp(); + } + 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; + + /* [AS] Game list options dialog */ + case IDM_GameListOptions: + GameListOptions(); + break; + case IDM_CopyPosition: CopyFENToClipboard(); break; @@ -4444,14 +5303,28 @@ WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) DrawPosition(FALSE, NULL); break; + case IDM_FlipClock: + flipClock = !flipClock; + DisplayBothClocks(); + break; + case IDM_GeneralOptions: GeneralOptionsPopup(hwnd); + DrawPosition(TRUE, NULL); break; case IDM_BoardOptions: BoardOptionsPopup(hwnd); break; + case IDM_EnginePlayOptions: + EnginePlayOptionsPopup(hwnd); + break; + + case IDM_OptionsUCI: + UciOptionsPopup(hwnd); + break; + case IDM_IcsOptions: IcsOptionsPopup(hwnd); break; @@ -4509,7 +5382,7 @@ WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) char dir[MSG_SIZ]; GetCurrentDirectory(MSG_SIZ, dir); SetCurrentDirectory(installDir); - debugFP = fopen(appData.nameOfDebugFile, "w"); + debugFP = fopen(appData.nameOfDebugFile, "w"); SetCurrentDirectory(dir); setbuf(debugFP, NULL); } else { @@ -4584,6 +5457,36 @@ WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) fromX = fromY = -1; break; + case EP_WhiteFerz: + EditPositionMenuEvent(WhiteFerz, fromX, fromY); + fromX = fromY = -1; + break; + + case EP_WhiteWazir: + EditPositionMenuEvent(WhiteWazir, fromX, fromY); + fromX = fromY = -1; + break; + + case EP_WhiteAlfil: + EditPositionMenuEvent(WhiteAlfil, fromX, fromY); + fromX = fromY = -1; + break; + + case EP_WhiteCannon: + EditPositionMenuEvent(WhiteCannon, fromX, fromY); + fromX = fromY = -1; + break; + + case EP_WhiteCardinal: + EditPositionMenuEvent(WhiteCardinal, fromX, fromY); + fromX = fromY = -1; + break; + + case EP_WhiteMarshall: + EditPositionMenuEvent(WhiteMarshall, fromX, fromY); + fromX = fromY = -1; + break; + case EP_WhiteKing: EditPositionMenuEvent(WhiteKing, fromX, fromY); fromX = fromY = -1; @@ -4614,6 +5517,36 @@ WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) fromX = fromY = -1; break; + case EP_BlackFerz: + EditPositionMenuEvent(BlackFerz, fromX, fromY); + fromX = fromY = -1; + break; + + case EP_BlackWazir: + EditPositionMenuEvent(BlackWazir, fromX, fromY); + fromX = fromY = -1; + break; + + case EP_BlackAlfil: + EditPositionMenuEvent(BlackAlfil, fromX, fromY); + fromX = fromY = -1; + break; + + case EP_BlackCannon: + EditPositionMenuEvent(BlackCannon, fromX, fromY); + fromX = fromY = -1; + break; + + case EP_BlackCardinal: + EditPositionMenuEvent(BlackCardinal, fromX, fromY); + fromX = fromY = -1; + break; + + case EP_BlackMarshall: + EditPositionMenuEvent(BlackMarshall, fromX, fromY); + fromX = fromY = -1; + break; + case EP_BlackKing: EditPositionMenuEvent(BlackKing, fromX, fromY); fromX = fromY = -1; @@ -4639,6 +5572,16 @@ WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) fromX = fromY = -1; break; + case EP_Promote: + EditPositionMenuEvent(PromotePiece, fromX, fromY); + fromX = fromY = -1; + break; + + case EP_Demote: + EditPositionMenuEvent(DemotePiece, fromX, fromY); + fromX = fromY = -1; + break; + case DP_Pawn: DropMenuEvent(WhitePawn, fromX, fromY); fromX = fromY = -1; @@ -4702,11 +5645,31 @@ WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) InputEvent(hwnd, message, wParam, lParam); break; + /* [AS] Also move "attached" child windows */ + case WM_WINDOWPOSCHANGING: + if( hwnd == hwndMain && appData.useStickyWindows ) { + LPWINDOWPOS lpwp = (LPWINDOWPOS) lParam; + + if( ((lpwp->flags & SWP_NOMOVE) == 0) && ((lpwp->flags & SWP_NOSIZE) != 0) ) { + /* Window is moving */ + RECT rcMain; + + GetWindowRect( hwnd, &rcMain ); + + ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, moveHistoryDialog, &wpMoveHistory ); + ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, evalGraphDialog, &wpEvalGraph ); + ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, engineOutputDialog, &wpEngineOutput ); + } + } + break; + + /* [AS] Snapping */ case WM_ENTERSIZEMOVE: if (hwnd == hwndMain) { doingSizing = TRUE; lastSizing = 0; } + return OnEnterSizeMove( &sd, hwnd, wParam, lParam ); break; case WM_SIZING: @@ -4715,6 +5678,9 @@ WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) } break; + case WM_MOVING: + return OnMoving( &sd, hwnd, wParam, lParam ); + case WM_EXITSIZEMOVE: if (hwnd == hwndMain) { RECT client; @@ -4724,6 +5690,7 @@ WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) ResizeBoard(client.right, client.bottom, lastSizing); lastSizing = 0; } + return OnExitSizeMove( &sd, hwnd, wParam, lParam ); break; case WM_DESTROY: /* message: window being destroyed */ @@ -5116,8 +6083,7 @@ ResizeEditPlusButtons(HWND hDlg, HWND hText, int sizeX, int sizeY, int newSizeX, EndDeferWindowPos(cl.hdwp); } -/* Center one window over another */ -BOOL CenterWindow (HWND hwndChild, HWND hwndParent) +BOOL CenterWindowEx(HWND hwndChild, HWND hwndParent, int mode) { RECT rChild, rParent; int wChild, hChild, wParent, hParent; @@ -5149,7 +6115,13 @@ BOOL CenterWindow (HWND hwndChild, HWND hwndParent) } /* Calculate new Y position, then adjust for screen */ - yNew = rParent.top + ((hParent - hChild) /2); + if( mode == 0 ) { + yNew = rParent.top + ((hParent - hChild) /2); + } + else { + yNew = rParent.top + GetSystemMetrics( SM_CYCAPTION ) * 2 / 3; + } + if (yNew < 0) { yNew = 0; } else if ((yNew+hChild) > hScreen) { @@ -5161,6 +6133,12 @@ BOOL CenterWindow (HWND hwndChild, HWND hwndParent) xNew, yNew, 0, 0, SWP_NOSIZE | SWP_NOZORDER); } +/* Center one window over another */ +BOOL CenterWindow (HWND hwndChild, HWND hwndParent) +{ + return CenterWindowEx( hwndChild, hwndParent, 0 ); +} + /*---------------------------------------------------------------------------*\ * * Startup Dialog functions @@ -5296,17 +6274,17 @@ StartupDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam) SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) -1, (LPARAM) 0); SendMessage(hwndCombo, WM_SETTEXT, (WPARAM) 0, (LPARAM) buf); } - - if (appData.icsActive) { + + if (appData.icsActive) { CheckDlgButton(hDlg, OPT_ChessServer, BST_CHECKED); - } - else if (appData.noChessProgram) { + } + else if (appData.noChessProgram) { CheckDlgButton(hDlg, OPT_View, BST_CHECKED); } - else { - CheckDlgButton(hDlg, OPT_ChessEngine, BST_CHECKED); - } - + else { + CheckDlgButton(hDlg, OPT_ChessEngine, BST_CHECKED); + } + SetStartupDialogEnables(hDlg); return TRUE; @@ -5581,7 +6559,7 @@ TypeInMoveDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam) case WM_INITDIALOG: move[0] = (char) lParam; move[1] = NULLCHAR; - CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER)); + CenterWindowEx(hDlg, GetWindow(hDlg, GW_OWNER), 1 ); hInput = GetDlgItem(hDlg, OPT_Move); SetWindowText(hInput, move); SetFocus(hInput); @@ -5700,9 +6678,21 @@ ErrorDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam) switch (message) { case WM_INITDIALOG: GetWindowRect(hDlg, &rChild); + + /* SetWindowPos(hDlg, NULL, rChild.left, rChild.top + boardRect.top - (rChild.bottom - rChild.top), 0, 0, SWP_NOZORDER|SWP_NOSIZE); + */ + + /* + [AS] It seems that the above code wants to move the dialog up in the "caption + area" of the main window, but it uses the dialog height as an hard-coded constant, + and it doesn't work when you resize the dialog. + For now, just give it a default position. + */ + SetWindowPos(hDlg, NULL, boardRect.left+8, boardRect.top+8, 0, 0, SWP_NOZORDER|SWP_NOSIZE); + errorDialog = hDlg; SetWindowText(hDlg, errorTitle); hwndText = GetDlgItem(hDlg, OPT_ErrorText); @@ -5725,6 +6715,73 @@ ErrorDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam) return FALSE; } +#ifdef GOTHIC +HWND gothicDialog = NULL; + +LRESULT CALLBACK +GothicDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam) +{ + HANDLE hwndText; + RECT rChild; + int height = GetSystemMetrics(SM_CYCAPTION)+GetSystemMetrics(SM_CYFRAME); + + switch (message) { + case WM_INITDIALOG: + GetWindowRect(hDlg, &rChild); + + SetWindowPos(hDlg, NULL, boardX, boardY-height, winWidth, height, + SWP_NOZORDER); + + /* + [AS] It seems that the above code wants to move the dialog up in the "caption + area" of the main window, but it uses the dialog height as an hard-coded constant, + and it doesn't work when you resize the dialog. + For now, just give it a default position. + */ + gothicDialog = hDlg; + SetWindowText(hDlg, errorTitle); + hwndText = GetDlgItem(hDlg, OPT_ErrorText); + SetDlgItemText(hDlg, OPT_ErrorText, errorMessage); + return FALSE; + + case WM_COMMAND: + switch (LOWORD(wParam)) { + case IDOK: + case IDCANCEL: + if (errorDialog == hDlg) errorDialog = NULL; + DestroyWindow(hDlg); + return TRUE; + + default: + break; + } + break; + } + return FALSE; +} + +VOID +GothicPopUp(char *title, char up) +{ + FARPROC lpProc; + char *p, *q; + BOOLEAN modal = hwndMain == NULL; + + strncpy(errorTitle, title, sizeof(errorTitle)); + errorTitle[sizeof(errorTitle) - 1] = '\0'; + + if(up && gothicDialog == NULL) { + lpProc = MakeProcInstance((FARPROC)GothicDialog, hInst); + CreateDialog(hInst, MAKEINTRESOURCE(DLG_Error), + hwndMain, (DLGPROC)lpProc); + FreeProcInstance(lpProc); + } else if(!up && gothicDialog != NULL) { + DestroyWindow(gothicDialog); + gothicDialog = NULL; + } +} +#endif + /*---------------------------------------------------------------------------*\ * * Ics Interaction console functions @@ -6228,6 +7285,7 @@ ConsoleInputSubclass(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) LRESULT CALLBACK ConsoleWndProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam) { + static SnapData sd; static HWND hText, hInput, hFocus; InputSource *is = consoleInputSource; RECT rect; @@ -6314,7 +7372,21 @@ ConsoleWndProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam) mmi->ptMinTrackSize.x = 100; mmi->ptMinTrackSize.y = 100; break; + + /* [AS] Snapping */ + case WM_ENTERSIZEMOVE: + return OnEnterSizeMove( &sd, hDlg, wParam, lParam ); + + case WM_SIZING: + return OnSizing( &sd, hDlg, wParam, lParam ); + + case WM_MOVING: + return OnMoving( &sd, hDlg, wParam, lParam ); + + case WM_EXITSIZEMOVE: + return OnExitSizeMove( &sd, hDlg, wParam, lParam ); } + return DefWindowProc(hDlg, message, wParam, lParam); } @@ -6422,8 +7494,39 @@ ConsoleOutput(char* data, int length, int forceVisible) void +DisplayHoldingsCount(HDC hdc, int x, int y, int rightAlign, int copyNumber) +{ + char buf[100]; + char *str; + COLORREF oldFg, oldBg; + HFONT oldFont; + RECT rect; + + if(copyNumber > 1) sprintf(buf, "%d", copyNumber); else buf[0] = 0; + + oldFg = SetTextColor(hdc, RGB(255, 255, 255)); /* white */ + oldBg = SetBkColor(hdc, RGB(0, 0, 0)); /* black */ + oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf); + + rect.left = x; + rect.right = x + squareSize; + rect.top = y; + rect.bottom = y + squareSize; + str = buf; + + ExtTextOut(hdc, x + MESSAGE_LINE_LEFTMARGIN + + (rightAlign ? (squareSize*2)/3 : 0), + y, ETO_CLIPPED|ETO_OPAQUE, + &rect, str, strlen(str), NULL); + + (void) SetTextColor(hdc, oldFg); + (void) SetBkColor(hdc, oldBg); + (void) SelectObject(hdc, oldFont); +} + +void DisplayAClock(HDC hdc, int timeRemaining, int highlight, - RECT *rect, char *color) + RECT *rect, char *color, char *flagFell) { char buf[100]; char *str; @@ -6432,9 +7535,9 @@ DisplayAClock(HDC hdc, int timeRemaining, int highlight, if (appData.clockMode) { if (tinyLayout) - sprintf(buf, "%c %s", color[0], TimeString(timeRemaining)); + sprintf(buf, "%c %s %s %s", color[0], TimeString(timeRemaining), flagFell); else - sprintf(buf, "%s: %s", color, TimeString(timeRemaining)); + sprintf(buf, "%s: %s %s", color, TimeString(timeRemaining), flagFell); str = buf; } else { str = color; @@ -6465,6 +7568,15 @@ DoReadFile(HANDLE hFile, char *buf, int count, DWORD *outCount, { int ok, err; + /* [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); ovl->Offset = ovl->OffsetHigh = 0; ok = ReadFile(hFile, buf, count, outCount, ovl); @@ -6507,28 +7619,28 @@ DoWriteFile(HANDLE hFile, char *buf, int count, DWORD *outCount, return err; } -/* [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 a non-successful code! */ - is->count = (DWORD) -2; - is->next = is->buf; - } - } -} +/* [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; + } + } +} DWORD InputThread(LPVOID arg) @@ -6551,16 +7663,27 @@ InputThread(LPVOID arg) is->count = 0; } else { is->count = (DWORD) -1; + /* [AS] The (is->count <= 0) check below is not useful for unsigned values! */ + break; } } - - CheckForInputBufferFull( is ); - + + CheckForInputBufferFull( is ); + SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is); + + if( is->count == ((DWORD) -1) ) break; /* [AS] */ + if (is->count <= 0) break; /* Quit on EOF or error */ } + CloseHandle(ovl.hEvent); CloseHandle(is->hFile); + + if (appData.debugMode) { + fprintf( debugFP, "Input thread terminated (id=%u, error=%d, count=%d)\n", is->id, is->error, is->count ); + } + return 0; } @@ -6610,10 +7733,13 @@ NonOvlInputThread(LPVOID arg) is->count = (DWORD) -1; } } - - CheckForInputBufferFull( is ); - + + CheckForInputBufferFull( is ); + SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is); + + if( is->count == ((DWORD) -1) ) break; /* [AS] */ + if (is->count < 0) break; /* Quit on error */ } CloseHandle(is->hFile); @@ -6640,6 +7766,9 @@ SocketInputThread(LPVOID arg) } } SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is); + + if( is->count == ((DWORD) -1) ) break; /* [AS] */ + if (is->count <= 0) break; /* Quit on EOF or error */ } return 0; @@ -6661,14 +7790,14 @@ InputEvent(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) p = q; } } - + /* Move any partial line to the start of the buffer */ q = is->buf; while (p < is->next) { *q++ = *p++; } is->next = q; - + if (is->error != NO_ERROR || is->count == 0) { /* Notify backend of the error. Note: If there was a partial line at the end, it is not flushed through. */ @@ -7213,6 +8342,271 @@ AskQuestion(char* title, char *question, char *replyPrefix, ProcRef pr) FreeProcInstance(lpProc); } +/* [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; +} + +/* [AS] Game list options */ +typedef struct { + char id; + char * name; +} GLT_Item; + +static GLT_Item GLT_ItemInfo[] = { + { GLT_EVENT, "Event" }, + { GLT_SITE, "Site" }, + { GLT_DATE, "Date" }, + { GLT_ROUND, "Round" }, + { GLT_PLAYERS, "Players" }, + { GLT_RESULT, "Result" }, + { GLT_WHITE_ELO, "White Rating" }, + { GLT_BLACK_ELO, "Black Rating" }, + { GLT_TIME_CONTROL,"Time Control" }, + { GLT_VARIANT, "Variant" }, + { GLT_OUT_OF_BOOK,PGN_OUT_OF_BOOK }, + { 0, 0 } +}; + +const char * GLT_FindItem( char id ) +{ + const char * result = 0; + + GLT_Item * list = GLT_ItemInfo; + + while( list->id != 0 ) { + if( list->id == id ) { + result = list->name; + break; + } + + list++; + } + + return result; +} + +void GLT_AddToList( HWND hDlg, int iDlgItem, char id, int index ) +{ + const char * name = GLT_FindItem( id ); + + if( name != 0 ) { + if( index >= 0 ) { + SendDlgItemMessage( hDlg, iDlgItem, LB_INSERTSTRING, index, (LPARAM) name ); + } + else { + SendDlgItemMessage( hDlg, iDlgItem, LB_ADDSTRING, 0, (LPARAM) name ); + } + } +} + +void GLT_TagsToList( HWND hDlg, char * tags ) +{ + char * pc = tags; + + SendDlgItemMessage( hDlg, IDC_GameListTags, LB_RESETCONTENT, 0, 0 ); + + while( *pc ) { + GLT_AddToList( hDlg, IDC_GameListTags, *pc, -1 ); + pc++; + } + + SendDlgItemMessage( hDlg, IDC_GameListTags, LB_ADDSTRING, 0, (LPARAM) "\t --- Hidden tags ---" ); + + pc = GLT_ALL_TAGS; + + while( *pc ) { + if( strchr( tags, *pc ) == 0 ) { + GLT_AddToList( hDlg, IDC_GameListTags, *pc, -1 ); + } + pc++; + } + + SendDlgItemMessage( hDlg, IDC_GameListTags, LB_SETCURSEL, 0, 0 ); +} + +char GLT_ListItemToTag( HWND hDlg, int index ) +{ + char result = '\0'; + char name[128]; + + GLT_Item * list = GLT_ItemInfo; + + if( SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETTEXT, index, (LPARAM) name ) != LB_ERR ) { + while( list->id != 0 ) { + if( strcmp( list->name, name ) == 0 ) { + result = list->id; + break; + } + + list++; + } + } + + return result; +} + +void GLT_MoveSelection( HWND hDlg, int delta ) +{ + int idx1 = (int) SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETCURSEL, 0, 0 ); + int idx2 = idx1 + delta; + int count = (int) SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETCOUNT, 0, 0 ); + + if( idx1 >=0 && idx1 < count && idx2 >= 0 && idx2 < count ) { + char buf[128]; + + SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETTEXT, idx1, (LPARAM) buf ); + SendDlgItemMessage( hDlg, IDC_GameListTags, LB_DELETESTRING, idx1, 0 ); + SendDlgItemMessage( hDlg, IDC_GameListTags, LB_INSERTSTRING, idx2, (LPARAM) buf ); + SendDlgItemMessage( hDlg, IDC_GameListTags, LB_SETCURSEL, idx2, 0 ); + } +} + +LRESULT CALLBACK GameListOptions_Proc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam) +{ + static char glt[64]; + static char * lpUserGLT; + + switch( message ) + { + case WM_INITDIALOG: + lpUserGLT = (char *) lParam; + + strcpy( glt, lpUserGLT ); + + CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER)); + + /* Initialize list */ + GLT_TagsToList( hDlg, glt ); + + SetFocus( GetDlgItem(hDlg, IDC_GameListTags) ); + + break; + + case WM_COMMAND: + switch( LOWORD(wParam) ) { + case IDOK: + { + char * pc = lpUserGLT; + int idx = 0; + int cnt = (int) SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETCOUNT, 0, 0 ); + char id; + + do { + id = GLT_ListItemToTag( hDlg, idx ); + + *pc++ = id; + idx++; + } while( id != '\0' ); + } + EndDialog( hDlg, 0 ); + return TRUE; + case IDCANCEL: + EndDialog( hDlg, 1 ); + return TRUE; + + case IDC_GLT_Default: + strcpy( glt, GLT_DEFAULT_TAGS ); + GLT_TagsToList( hDlg, glt ); + return TRUE; + + case IDC_GLT_Restore: + strcpy( glt, lpUserGLT ); + GLT_TagsToList( hDlg, glt ); + return TRUE; + + case IDC_GLT_Up: + GLT_MoveSelection( hDlg, -1 ); + return TRUE; + + case IDC_GLT_Down: + GLT_MoveSelection( hDlg, +1 ); + return TRUE; + } + + break; + } + + return FALSE; +} + +int GameListOptions() +{ + char glt[64]; + int result; + FARPROC lpProc = MakeProcInstance( (FARPROC) GameListOptions_Proc, hInst ); + + strcpy( glt, appData.gameListTags ); + + result = DialogBoxParam( hInst, MAKEINTRESOURCE(DLG_GameListOptions), hwndMain, (DLGPROC)lpProc, (LPARAM)glt ); + + if( result == 0 ) { + /* [AS] Memory leak here! */ + appData.gameListTags = strdup( glt ); + } + + return result; +} + VOID DisplayIcsInteractionTitle(char *str) @@ -7394,8 +8788,10 @@ DisplayWhiteClock(long timeRemaining, int highlight) { HDC hdc; hdc = GetDC(hwndMain); + char *flag = whiteFlag && gameMode == TwoMachinesPlay ? "(!)" : ""; + if (!IsIconic(hwndMain)) { - DisplayAClock(hdc, timeRemaining, highlight, &whiteRect, "White"); + DisplayAClock(hdc, timeRemaining, highlight, flipClock ? &blackRect : &whiteRect, "White", flag); } if (highlight && iconCurrent == iconBlack) { iconCurrent = iconWhite; @@ -7413,9 +8809,11 @@ void DisplayBlackClock(long timeRemaining, int highlight) { HDC hdc; + char *flag = blackFlag && gameMode == TwoMachinesPlay ? "(!)" : ""; + hdc = GetDC(hwndMain); if (!IsIconic(hwndMain)) { - DisplayAClock(hdc, timeRemaining, highlight, &blackRect, "Black"); + DisplayAClock(hdc, timeRemaining, highlight, flipClock ? &whiteRect : &blackRect, "Black", flag); } if (highlight && iconCurrent == iconWhite) { iconCurrent = iconBlack; @@ -7666,27 +9064,27 @@ DestroyChildProcess(ProcRef pr, int/*boolean*/ signal) we could arrange for this even though neither WinBoard nor the chess program uses a console for stdio? */ /*!!if (signal) GenerateConsoleCtrlEvent(CTRL_BREAK_EVENT, cp->pid);*/ - - /* [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 ); - } - } - + + /* [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); break; @@ -8073,7 +9471,7 @@ InputSourceRef AddInputSource(ProcRef pr, int lineByLine, InputCallback func, VOIDSTAR closure) { - InputSource *is, *is2 = NULL; + InputSource *is, *is2 = NULL; ChildProc *cp = (ChildProc *) pr; is = (InputSource *) calloc(1, sizeof(InputSource)); @@ -8087,18 +9485,18 @@ AddInputSource(ProcRef pr, int lineByLine, consoleInputSource = is; } else { is->kind = cp->kind; - /* - [AS] Try to avoid a race condition if the thread is given control too early: - we create all threads suspended to that the is->hThread variable can be - safely assigned, then let the threads start with ResumeThread. - */ + /* + [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) { case CPReal: is->hFile = cp->hFrom; cp->hFrom = NULL; /* now owned by InputThread */ is->hThread = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) NonOvlInputThread, - (LPVOID) is, CREATE_SUSPENDED, &is->id); + (LPVOID) is, CREATE_SUSPENDED, &is->id); break; case CPComm: @@ -8106,14 +9504,14 @@ AddInputSource(ProcRef pr, int lineByLine, cp->hFrom = NULL; /* now owned by InputThread */ is->hThread = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) InputThread, - (LPVOID) is, CREATE_SUSPENDED, &is->id); + (LPVOID) is, CREATE_SUSPENDED, &is->id); break; case CPSock: is->sock = cp->sock; is->hThread = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread, - (LPVOID) is, CREATE_SUSPENDED, &is->id); + (LPVOID) is, CREATE_SUSPENDED, &is->id); break; case CPRcmd: @@ -8125,22 +9523,22 @@ AddInputSource(ProcRef pr, int lineByLine, is2->second = is2; is->hThread = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread, - (LPVOID) is, CREATE_SUSPENDED, &is->id); + (LPVOID) is, CREATE_SUSPENDED, &is->id); is2->hThread = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread, - (LPVOID) is2, CREATE_SUSPENDED, &is2->id); + (LPVOID) is2, CREATE_SUSPENDED, &is2->id); break; } - - if( is->hThread != NULL ) { - ResumeThread( is->hThread ); + + if( is->hThread != NULL ) { + ResumeThread( is->hThread ); + } + + if( is2 != NULL && is2->hThread != NULL ) { + ResumeThread( is2->hThread ); + } } - - if( is2 != NULL && is2->hThread != NULL ) { - ResumeThread( is2->hThread ); - } - } - + return (InputSourceRef) is; } @@ -8335,6 +9733,10 @@ AnalysisPopUp(char* title, char* str) FARPROC lpProc; char *p, *q; + /* [AS] */ + EngineOutputPopUp(); + return; + if (str == NULL) str = ""; p = (char *) malloc(2 * strlen(str) + 2); q = p; @@ -8495,11 +9897,11 @@ ScreenSquare(column, row, pt) int column; int row; POINT * pt; { if (flipView) { - pt->x = lineGap + ((BOARD_SIZE-1)-column) * (squareSize + lineGap); + pt->x = lineGap + ((BOARD_WIDTH-1)-column) * (squareSize + lineGap); pt->y = lineGap + row * (squareSize + lineGap); } else { pt->x = lineGap + column * (squareSize + lineGap); - pt->y = lineGap + ((BOARD_SIZE-1)-row) * (squareSize + lineGap); + pt->y = lineGap + ((BOARD_HEIGHT-1)-row) * (squareSize + lineGap); } } @@ -8547,9 +9949,32 @@ Tween(start, mid, finish, factor, frames, nFrames) } void -HistorySet(char movelist[][2*MOVE_LEN], int first, int last, int current) +HistorySet( char movelist[][2*MOVE_LEN], int first, int last, int current ) { - /* Currently not implemented in WinBoard */ +#if 0 + char buf[256]; + + sprintf( buf, "HistorySet: first=%d, last=%d, current=%d (%s)\n", + first, last, current, current >= 0 ? movelist[current] : "n/a" ); + + OutputDebugString( buf ); +#endif + + MoveHistorySet( movelist, first, last, current, pvInfoList ); + + EvalGraphSet( first, last, current, pvInfoList ); } +void SetProgramStats( FrontEndProgramStats * stats ) +{ +#if 0 + char buf[1024]; + + sprintf( buf, "SetStats for %d: depth=%d, nodes=%lu, score=%5.2f, time=%5.2f, pv=%s\n", + stats->which, stats->depth, stats->nodes, stats->score / 100.0, stats->time / 100.0, stats->pv == 0 ? "n/a" : stats->pv ); + OutputDebugString( buf ); +#endif + + EngineOutputUpdate( stats ); +}