X-Git-Url: http://winboard.nl/cgi-bin?a=blobdiff_plain;f=winboard%2Fwinboard.c;h=252e9b032fa681cc99a23ab182d00a5949e8d127;hb=46491b331dd7c853879132374e4ee30c1e758606;hp=69146fc98f332c7ca56bc2754d5129565e227ada;hpb=9d33882a8307d8e00849941e30032d99fc75de43;p=xboard.git diff --git a/winboard/winboard.c b/winboard/winboard.c index 69146fc..252e9b0 100644 --- a/winboard/winboard.c +++ b/winboard/winboard.c @@ -1,9 +1,13 @@ -/* +/* * WinBoard.c -- Windows NT front end to XBoard - * $Id$ * - * Copyright 1991 by Digital Equipment Corporation, Maynard, Massachusetts. - * Enhancements Copyright 1992-2001 Free Software Foundation, Inc. + * Copyright 1991 by Digital Equipment Corporation, Maynard, + * Massachusetts. + * + * Enhancements Copyright 1992-2001, 2002, 2003, 2004, 2005, 2006, + * 2007, 2008, 2009 Free Software Foundation, Inc. + * + * Enhancements Copyright 2005 Alessandro Scotti * * XBoard borrows its colors and the bitmaps.xchess bitmap set from XChess, * which was written and is copyrighted by Wayne Christopher. @@ -30,33 +34,36 @@ * SOFTWARE. * ------------------------------------------------------------------------ * - * The following terms apply to the enhanced version of XBoard distributed - * by the Free Software Foundation: + * The following terms apply to the enhanced version of XBoard + * distributed by the Free Software Foundation: * ------------------------------------------------------------------------ - * This program is free software; you can redistribute it and/or modify + * + * GNU XBoard is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. + * the Free Software Foundation, either version 3 of the License, or (at + * your option) any later version. * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. + * GNU XBoard is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. * * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - * ------------------------------------------------------------------------ - */ + * along with this program. If not, see http://www.gnu.org/licenses/. * + * + *------------------------------------------------------------------------ + ** See the file ChangeLog for a revision history. */ #include "config.h" #include #include #include +#include #include #include +#include #include #include #include @@ -65,6 +72,7 @@ #include #include #include +#include #if __GNUC__ #include @@ -82,9 +90,29 @@ #include "woptions.h" #include "wsockerr.h" #include "defaults.h" - +#include "help.h" +#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; +extern HANDLE chatHandle[]; +extern int ics_type; + +void DisplayHoldingsCount(HDC hdc, int x, int y, int align, int copyNumber); +VOID NewVariantPopup(HWND hwnd); +int FinishMove P((ChessMove moveType, int fromX, int fromY, int toX, int toY, + /*char*/int promoChar)); +void AnimateAtomicCapture(int fromX, int fromY, int toX, int toY, int nFrames); +void DisplayMove P((int moveNumber)); +Boolean ParseFEN P((Board board, int *blackPlaysFirst, char *fen)); +void ChatPopUp P(()); typedef struct { - ChessSquare piece; + ChessSquare piece; POINT pos; /* window coordinates of current pos */ POINT lastpos; /* window coordinates of last pos - used for clipping */ POINT from; /* board coordinates of the piece's orig pos */ @@ -103,19 +131,25 @@ typedef struct { static DragInfo dragInfo = { {-1,-1}, {-1,-1}, {-1,-1}, {-1,-1} }; typedef struct { - POINT sq[2]; /* board coordinates of from, to squares */ + POINT sq[2]; /* board coordinates of from, to squares */ } HighlightInfo; static HighlightInfo highlightInfo = { {{-1, -1}, {-1, -1}} }; static HighlightInfo premoveHighlightInfo = { {{-1, -1}, {-1, -1}} }; +typedef struct { // [HGM] atomic + int fromX, fromY, toX, toY, radius; +} ExplodeInfo; + +static ExplodeInfo explodeInfo; + /* Window class names */ char szAppName[] = "WinBoard"; char szConsoleName[] = "WBConsole"; /* Title bar text */ char szTitle[] = "WinBoard"; -char szConsoleTitle[] = "ICS Interaction"; +char szConsoleTitle[] = "I C S Interaction"; char *programName; char *settingsFileName; @@ -124,10 +158,12 @@ char installDir[MSG_SIZ]; BoardSize boardSize; BOOLEAN chessProgram; -static int boardX, boardY, consoleX, consoleY, consoleW, consoleH; -static int squareSize, lineGap; -static int winWidth, winHeight; -static RECT messageRect, whiteRect, blackRect; +static int boardX, boardY; +int minX, minY; // [HGM] placement: volatile limits on upper-left corner +static int squareSize, lineGap, minorSize; +static int winWidth, winHeight, winW, winH; +static RECT messageRect, whiteRect, blackRect, leftLogoRect, rightLogoRect; // [HGM] logo +static int logoHeight = 0; static char messageText[MESSAGE_TEXT_MAX]; static int clockTimerEvent = 0; static int loadGameTimerEvent = 0; @@ -140,7 +176,7 @@ char *icsNames; char *firstChessProgramNames; char *secondChessProgramNames; -#define ARG_MAX 20000 +#define ARG_MAX 128*1024 /* [AS] For Roger Brown's very long list! */ #define PALETTESIZE 256 @@ -149,16 +185,18 @@ HWND hwndMain = NULL; /* root window*/ HWND hwndConsole = NULL; BOOLEAN alwaysOnTop = FALSE; RECT boardRect; -COLORREF lightSquareColor, darkSquareColor, whitePieceColor, +COLORREF lightSquareColor, darkSquareColor, whitePieceColor, blackPieceColor, highlightSquareColor, premoveHighlightColor; HPALETTE hPal; 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, - whitePieceBrush, blackPieceBrush, iconBkgndBrush, outlineBrush; + blackSquareBrush, /* [HGM] for band between board and holdings */ + explodeBrush, /* [HGM] atomic */ + whitePieceBrush, blackPieceBrush, iconBkgndBrush /*, outlineBrush*/; static POINT gridEndpoints[(BOARD_SIZE + 1) * 4]; static DWORD gridVertexCounts[(BOARD_SIZE + 1) * 2]; static HPEN gridPen = NULL; @@ -170,14 +208,31 @@ static HICON iconWhite, iconBlack, iconCurrent; static int doingSizing = FALSE; static int lastSizing = 0; static int prevStderrPort; +static HBITMAP userLogo; + +/* [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 +#if defined(_winmajor) #define oldDialog (_winmajor < 4) +#else +#define oldDialog 0 +#endif #endif -char *defaultTextAttribs[] = +char *defaultTextAttribs[] = { COLOR_SHOUT, COLOR_SSHOUT, COLOR_CHANNEL1, COLOR_CHANNEL, COLOR_KIBITZ, COLOR_TELL, COLOR_CHALLENGE, COLOR_REQUEST, COLOR_SEEK, COLOR_NORMAL, @@ -193,7 +248,7 @@ typedef struct { int cliWidth, cliHeight; } SizeInfo; -SizeInfo sizeInfo[] = +SizeInfo sizeInfo[] = { { "tiny", 21, 0, 1, 1, 0, 0 }, { "teeny", 25, 1, 1, 1, 0, 0 }, @@ -216,63 +271,27 @@ SizeInfo sizeInfo[] = { NULL, 0, 0, 0, 0, 0, 0 } }; -#define MF(x) {x, {0, }, {0, }, 0} +#define MF(x) {x, {{0,}, 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]; @@ -297,7 +316,7 @@ MyButtonDesc buttonDesc[N_BUTTONS] = }; int tinyLayout = 0, smallLayout = 0; -#define MENU_BAR_ITEMS 6 +#define MENU_BAR_ITEMS 7 char *menuBarText[2][MENU_BAR_ITEMS+1] = { { "&File", "&Mode", "&Action", "&Step", "&Options", "&Help", NULL }, { "&F", "&M", "&A", "&S", "&O", "&H", NULL }, @@ -398,8 +417,48 @@ LRESULT CALLBACK VOID APIENTRY MenuPopup(HWND hwnd, POINT pt, HMENU hmenu, UINT def); void ParseIcsTextMenu(char *icsTextMenuString); VOID PopUpMoveDialog(char firstchar); +VOID PopUpNameDialog(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; +WindowPlacement wpGameList; +WindowPlacement wpConsole; + +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 EngineOptionsPopup(); // [HGM] settings + +VOID GothicPopUp(char *title, VariantClass variant); /* * Setting "frozen" should disable all user input other than deleting * the window. We do this while engines are initializing themselves. @@ -435,6 +494,26 @@ void ThawUI() DrawMenuBar(hwndMain); } +/*static*/ int fromX = -1, fromY = -1, toX, toY; // [HGM] moved upstream, so JAWS can use them + +/* JAWS preparation patch (WinBoard for the sight impaired). Define required insertions as empty */ +#ifdef JAWS +#include "jaws.c" +#else +#define JAWS_INIT +#define JAWS_ARGS +#define JAWS_ALT_INTERCEPT +#define JAWS_KB_NAVIGATION +#define JAWS_MENU_ITEMS +#define JAWS_SILENCE +#define JAWS_REPLAY +#define JAWS_ACCEL +#define JAWS_COPYRIGHT +#define JAWS_DELETE(X) X +#define SAYMACHINEMOVE() +#define SAY(X) +#endif + /*---------------------------------------------------------------------------*\ * * WinMain @@ -443,10 +522,11 @@ void ThawUI() int APIENTRY WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, - LPSTR lpCmdLine, int nCmdShow) + LPSTR lpCmdLine, int nCmdShow) { MSG msg; - HANDLE hAccelMain, hAccelNoAlt; + HANDLE hAccelMain, hAccelNoAlt, hAccelNoICS; +// INITCOMMONCONTROLSEX ex; debugFP = stderr; @@ -460,29 +540,116 @@ WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, return (FALSE); } + JAWS_INIT + +// InitCommonControlsEx(&ex); + InitCommonControls(); + 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. */ while (GetMessage(&msg, /* message structure */ - NULL, /* handle of window receiving the message */ - 0, /* lowest message to examine */ - 0)) /* highest message to examine */ + NULL, /* handle of window receiving the message */ + 0, /* lowest message to examine */ + 0)) /* highest message to examine */ { + + if(msg.message == WM_CHAR && msg.wParam == '\t') { + // [HGM] navigate: switch between all windows with tab + HWND e1 = NULL, e2 = NULL, mh = NULL, hInput = NULL, hText = NULL; + int i, currentElement = 0; + + // first determine what element of the chain we come from (if any) + if(appData.icsActive) { + hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput); + hText = GetDlgItem(hwndConsole, OPT_ConsoleText); + } + if(engineOutputDialog && EngineOutputIsUp()) { + e1 = GetDlgItem(engineOutputDialog, IDC_EngineMemo1); + e2 = GetDlgItem(engineOutputDialog, IDC_EngineMemo2); + } + if(moveHistoryDialog && MoveHistoryIsUp()) { + mh = GetDlgItem(moveHistoryDialog, IDC_MoveHistory); + } + if(msg.hwnd == hwndMain) currentElement = 7 ; else + if(msg.hwnd == engineOutputDialog) currentElement = 2; else + if(msg.hwnd == e1) currentElement = 2; else + if(msg.hwnd == e2) currentElement = 3; else + if(msg.hwnd == moveHistoryDialog) currentElement = 4; else + if(msg.hwnd == mh) currentElement = 4; else + if(msg.hwnd == evalGraphDialog) currentElement = 6; else + if(msg.hwnd == hText) currentElement = 5; else + if(msg.hwnd == hInput) currentElement = 6; else + for (i = 0; i < N_BUTTONS; i++) { + if (buttonDesc[i].hwnd == msg.hwnd) { currentElement = 1; break; } + } + + // determine where to go to + if(currentElement) { HWND h = NULL; int direction = GetKeyState(VK_SHIFT) < 0 ? -1 : 1; + do { + currentElement = (currentElement + direction) % 7; + switch(currentElement) { + case 0: + h = hwndMain; break; // passing this case always makes the loop exit + case 1: + h = buttonDesc[0].hwnd; break; // could be NULL + case 2: + if(!EngineOutputIsUp()) continue; // skip closed auxiliary windows + h = e1; break; + case 3: + if(!EngineOutputIsUp()) continue; + h = e2; break; + case 4: + if(!MoveHistoryIsUp()) continue; + h = mh; break; +// case 6: // input to eval graph does not seem to get here! +// if(!EvalGraphIsUp()) continue; +// h = evalGraphDialog; break; + case 5: + if(!appData.icsActive) continue; + SAY("display"); + h = hText; break; + case 6: + if(!appData.icsActive) continue; + SAY("input"); + h = hInput; break; + } + } while(h == 0); + + if(currentElement > 4 && IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE); + if(currentElement < 5 && IsIconic(hwndMain)) ShowWindow(hwndMain, SW_RESTORE); // all open together + SetFocus(h); + + continue; // this message now has been processed + } + } + if (!(commentDialog && IsDialogMessage(commentDialog, &msg)) && - !(editTagsDialog && IsDialogMessage(editTagsDialog, &msg)) && - !(gameListDialog && IsDialogMessage(gameListDialog, &msg)) && - !(errorDialog && IsDialogMessage(errorDialog, &msg)) && - !(!frozen && TranslateAccelerator(hwndMain, hAccelMain, &msg)) && - !(!hwndConsole && TranslateAccelerator(hwndMain, hAccelNoAlt, &msg))) { - TranslateMessage(&msg); /* Translates virtual key codes */ - DispatchMessage(&msg); /* Dispatches message to window */ + !(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)) && JAWS_ACCEL + !(!hwndConsole && TranslateAccelerator(hwndMain, hAccelNoICS, &msg)) && + !(!hwndConsole && TranslateAccelerator(hwndMain, hAccelNoAlt, &msg))) { + int done = 0, i; // [HGM] chat: dispatch cat-box messages + for(i=0; i screenWidth - 32) *x = 0; if (*y > screenHeight - 32) *y = 0; + if (*x < minX) *x = minX; + if (*y < minY) *y = minY; } BOOL @@ -548,26 +733,31 @@ InitInstance(HINSTANCE hInstance, int nCmdShow, LPSTR lpCmdLine) WINDOWPLACEMENT wp; char *filepart; - hInst = hInstance; /* Store instance handle in our global variable */ + hInst = hInstance; /* Store instance handle in our global variable */ if (SearchPath(NULL, "WinBoard.exe", NULL, MSG_SIZ, installDir, &filepart)) { *filepart = NULLCHAR; } else { GetCurrentDirectory(MSG_SIZ, installDir); } + gameInfo.boardWidth = gameInfo.boardHeight = 8; // [HGM] won't have open window otherwise + screenWidth = screenHeight = 1000; // [HGM] placement: kludge to allow calling EnsureOnScreen from InitAppData InitAppData(lpCmdLine); /* Get run-time parameters */ if (appData.debugMode) { - debugFP = fopen("winboard.debug", "w"); + debugFP = fopen(appData.nameOfDebugFile, "w"); setbuf(debugFP, NULL); } InitBackEnd1(); +// InitEngineUCI( installDir, &first ); // [HGM] incorporated in InitBackEnd1() +// InitEngineUCI( installDir, &second ); + /* Create a main window for this application instance. */ hwnd = CreateWindow(szAppName, szTitle, - (WS_OVERLAPPEDWINDOW & ~WS_MAXIMIZEBOX), - CW_USEDEFAULT, 0, CW_USEDEFAULT, 0, - NULL, NULL, hInstance, NULL); + (WS_OVERLAPPEDWINDOW & ~WS_MAXIMIZEBOX), + CW_USEDEFAULT, 0, CW_USEDEFAULT, 0, + NULL, NULL, hInstance, NULL); hwndMain = hwnd; /* If window could not be created, return "failure" */ @@ -575,6 +765,41 @@ InitInstance(HINSTANCE hInstance, int nCmdShow, LPSTR lpCmdLine) return (FALSE); } + /* [HGM] logo: Load logos if specified (must be done before InitDrawingSizes) */ + if( appData.firstLogo && appData.firstLogo[0] != NULLCHAR) { + first.programLogo = LoadImage( 0, appData.firstLogo, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE ); + + if (first.programLogo == NULL && appData.debugMode) { + fprintf( debugFP, "Unable to load logo bitmap '%s'\n", appData.firstLogo ); + } + } else if(appData.autoLogo) { + if(appData.firstDirectory && appData.firstDirectory[0]) { + char buf[MSG_SIZ]; + sprintf(buf, "%s/logo.bmp", appData.firstDirectory); + first.programLogo = LoadImage( 0, buf, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE ); + } + } + + if( appData.secondLogo && appData.secondLogo[0] != NULLCHAR) { + second.programLogo = LoadImage( 0, appData.secondLogo, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE ); + + if (second.programLogo == NULL && appData.debugMode) { + fprintf( debugFP, "Unable to load logo bitmap '%s'\n", appData.secondLogo ); + } + } else if(appData.autoLogo) { + char buf[MSG_SIZ]; + if(appData.icsActive) { // [HGM] logo: in ICS mode second can be used for ICS + sprintf(buf, "logos\\%s.bmp", appData.icsHost); + second.programLogo = LoadImage( 0, buf, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE ); + } else + if(appData.secondDirectory && appData.secondDirectory[0]) { + sprintf(buf, "%s\\logo.bmp", appData.secondDirectory); + second.programLogo = LoadImage( 0, buf, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE ); + } + } + + SetUserLogo(); + iconWhite = LoadIcon(hInstance, "icon_white"); iconBlack = LoadIcon(hInstance, "icon_black"); iconCurrent = iconWhite; @@ -584,25 +809,59 @@ InitInstance(HINSTANCE hInstance, int nCmdShow, LPSTR lpCmdLine) for (ibs = (int) NUM_SIZES - 1; ibs >= 0; ibs--) { /* Compute window size for each board size, and use the largest size that fits on this screen as the default. */ - InitDrawingSizes((BoardSize)ibs, 0); + InitDrawingSizes((BoardSize)(ibs+1000), 0); if (boardSize == (BoardSize)-1 && - winHeight <= screenHeight && winWidth <= screenWidth) { + winH <= screenHeight + - GetSystemMetrics(SM_CYFRAME) - GetSystemMetrics(SM_CYCAPTION) - 10 + && winW <= screenWidth) { boardSize = (BoardSize)ibs; } } + InitDrawingSizes(boardSize, 0); InitMenuChecks(); buttonCount = GetSystemMetrics(SM_CMOUSEBUTTONS); - /* Make a console window if needed */ - if (appData.icsActive) { - ConsoleCreate(); + /* [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] 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" */ - EnsureOnScreen(&boardX, &boardY); + EnsureOnScreen(&boardX, &boardY, minX, minY); wp.length = sizeof(WINDOWPLACEMENT); wp.flags = 0; wp.showCmd = nCmdShow; @@ -613,8 +872,9 @@ InitInstance(HINSTANCE hInstance, int nCmdShow, LPSTR lpCmdLine) wp.rcNormalPosition.bottom = boardY + winHeight; SetWindowPlacement(hwndMain, &wp); - SetWindowPos(hwndMain, alwaysOnTop ? HWND_TOPMOST : HWND_NOTOPMOST, - 0, 0, 0, 0, SWP_NOMOVE|SWP_NOSIZE); + if(!appData.noGUI) SetWindowPos(hwndMain, alwaysOnTop ? HWND_TOPMOST : HWND_NOTOPMOST, + 0, 0, 0, 0, SWP_NOMOVE|SWP_NOSIZE); + if (hwndConsole) { #if AOT_CONSOLE SetWindowPos(hwndConsole, alwaysOnTop ? HWND_TOPMOST : HWND_NOTOPMOST, @@ -622,7 +882,8 @@ InitInstance(HINSTANCE hInstance, int nCmdShow, LPSTR lpCmdLine) #endif ShowWindow(hwndConsole, nCmdShow); } - UpdateWindow(hwnd); + if(!appData.noGUI) UpdateWindow(hwnd); else ShowWindow(hwnd, SW_MINIMIZE); + if(gameListDialog) SetFocus(gameListDialog); // [HGM] jaws: for if we clicked multi-game game file return TRUE; @@ -630,9 +891,10 @@ InitInstance(HINSTANCE hInstance, int nCmdShow, LPSTR lpCmdLine) typedef enum { - ArgString, ArgInt, ArgFloat, ArgBoolean, ArgTrue, ArgFalse, ArgNone, + ArgString, ArgInt, ArgFloat, ArgBoolean, ArgTrue, ArgFalse, ArgNone, ArgColor, ArgAttribs, ArgFilename, ArgBoardSize, ArgFont, ArgCommSettings, - ArgSettingsFilename + ArgSettingsFilename, + ArgX, ArgY, ArgZ // [HGM] placement: for window-placement options stored relative to main window } ArgType; typedef struct { @@ -663,6 +925,7 @@ ArgDescriptor argDescriptors[] = { { "loadGameFile", ArgFilename, (LPVOID) &appData.loadGameFile, FALSE }, { "", ArgNone, NULL }, /* keyword arguments */ + JAWS_ARGS { "whitePieceColor", ArgColor, (LPVOID) &whitePieceColor, TRUE }, { "wpc", ArgColor, (LPVOID) &whitePieceColor, FALSE }, { "blackPieceColor", ArgColor, (LPVOID) &blackPieceColor, TRUE }, @@ -800,7 +1063,7 @@ ArgDescriptor argDescriptors[] = { { "popup", ArgTrue, (LPVOID) &appData.popupMoveErrors, FALSE }, { "xpopup", ArgFalse, (LPVOID) &appData.popupMoveErrors, FALSE }, { "-popup", ArgFalse, (LPVOID) &appData.popupMoveErrors, FALSE }, - { "popUpErrors", ArgBoolean, (LPVOID) &appData.popupMoveErrors, + { "popUpErrors", ArgBoolean, (LPVOID) &appData.popupMoveErrors, FALSE }, /* only so that old WinBoard.ini files from betas can be read */ { "clockFont", ArgFont, (LPVOID) CLOCK_FONT, TRUE }, { "messageFont", ArgFont, (LPVOID) MESSAGE_FONT, TRUE }, @@ -808,6 +1071,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 }, @@ -948,13 +1212,13 @@ ArgDescriptor argDescriptors[] = { { "soundMove", ArgFilename, (LPVOID) &sounds[(int)SoundMove].name, TRUE }, { "soundBell", ArgFilename, (LPVOID) &sounds[(int)SoundBell].name, TRUE }, { "soundIcsWin", ArgFilename, (LPVOID) &sounds[(int)SoundIcsWin].name,TRUE }, - { "soundIcsLoss", ArgFilename, + { "soundIcsLoss", ArgFilename, (LPVOID) &sounds[(int)SoundIcsLoss].name, TRUE }, - { "soundIcsDraw", ArgFilename, + { "soundIcsDraw", ArgFilename, (LPVOID) &sounds[(int)SoundIcsDraw].name, TRUE }, - { "soundIcsUnfinished", ArgFilename, + { "soundIcsUnfinished", ArgFilename, (LPVOID) &sounds[(int)SoundIcsUnfinished].name, TRUE}, - { "soundIcsAlarm", ArgFilename, + { "soundIcsAlarm", ArgFilename, (LPVOID) &sounds[(int)SoundAlarm].name, TRUE }, { "reuseFirst", ArgBoolean, (LPVOID) &appData.reuseFirst, FALSE }, { "reuse", ArgTrue, (LPVOID) &appData.reuseFirst, FALSE }, @@ -967,28 +1231,6 @@ ArgDescriptor argDescriptors[] = { { "xreuse2", ArgFalse, (LPVOID) &appData.reuseSecond, FALSE }, { "-reuse2", ArgFalse, (LPVOID) &appData.reuseSecond, FALSE }, { "comPortSettings", ArgCommSettings, (LPVOID) &dcb, TRUE }, - { "x", ArgInt, (LPVOID) &boardX, TRUE }, - { "y", ArgInt, (LPVOID) &boardY, TRUE }, - { "icsX", ArgInt, (LPVOID) &consoleX, TRUE }, - { "icsY", ArgInt, (LPVOID) &consoleY, TRUE }, - { "icsW", ArgInt, (LPVOID) &consoleW, TRUE }, - { "icsH", ArgInt, (LPVOID) &consoleH, TRUE }, - { "analysisX", ArgInt, (LPVOID) &analysisX, TRUE }, - { "analysisY", ArgInt, (LPVOID) &analysisY, TRUE }, - { "analysisW", ArgInt, (LPVOID) &analysisW, TRUE }, - { "analysisH", ArgInt, (LPVOID) &analysisH, TRUE }, - { "commentX", ArgInt, (LPVOID) &commentX, TRUE }, - { "commentY", ArgInt, (LPVOID) &commentY, TRUE }, - { "commentW", ArgInt, (LPVOID) &commentW, TRUE }, - { "commentH", ArgInt, (LPVOID) &commentH, TRUE }, - { "tagsX", ArgInt, (LPVOID) &editTagsX, TRUE }, - { "tagsY", ArgInt, (LPVOID) &editTagsY, TRUE }, - { "tagsW", ArgInt, (LPVOID) &editTagsW, TRUE }, - { "tagsH", ArgInt, (LPVOID) &editTagsH, TRUE }, - { "gameListX", ArgInt, (LPVOID) &gameListX, TRUE }, - { "gameListY", ArgInt, (LPVOID) &gameListY, TRUE }, - { "gameListW", ArgInt, (LPVOID) &gameListW, TRUE }, - { "gameListH", ArgInt, (LPVOID) &gameListH, TRUE }, { "settingsFile", ArgSettingsFilename, (LPVOID) &settingsFileName, FALSE }, { "ini", ArgSettingsFilename, (LPVOID) &settingsFileName, FALSE }, { "saveSettingsOnExit", ArgBoolean, (LPVOID) &saveSettingsOnExit, TRUE }, @@ -1005,14 +1247,100 @@ 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, 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 }, + { "firstXBook", ArgFalse, (LPVOID) &appData.firstHasOwnBookUCI, FALSE }, + { "secondHasOwnBookUCI", ArgBoolean, (LPVOID) &appData.secondHasOwnBookUCI, FALSE }, + { "sNoOwnBookUCI", ArgFalse, (LPVOID) &appData.secondHasOwnBookUCI, FALSE }, + { "secondXBook", 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 }, + + /* [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.upsideDown, TRUE }, + { "allWhite", ArgBoolean, (LPVOID) &appData.allWhite, TRUE }, + { "alphaRank", ArgBoolean, (LPVOID) &appData.alphaRank, FALSE }, + { "firstAlphaRank", ArgBoolean, (LPVOID) &first.alphaRank, FALSE }, + { "secondAlphaRank", ArgBoolean, (LPVOID) &second.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 }, + { "autoKibitz", ArgTrue, (LPVOID) &appData.autoKibitz, FALSE }, + { "engineDebugOutput", ArgInt, (LPVOID) &appData.engineComments, FALSE }, + { "userName", ArgString, (LPVOID) &appData.userName, FALSE }, + { "rewindIndex", ArgInt, (LPVOID) &appData.rewindIndex, FALSE }, + { "sameColorGames", ArgInt, (LPVOID) &appData.sameColorGames, FALSE }, + { "smpCores", ArgInt, (LPVOID) &appData.smpCores, TRUE }, + { "egtFormats", ArgString, (LPVOID) &appData.egtFormats, TRUE }, + { "niceEngines", ArgInt, (LPVOID) &appData.niceEngines, TRUE }, + { "firstLogo", ArgFilename, (LPVOID) &appData.firstLogo, FALSE }, + { "secondLogo", ArgFilename, (LPVOID) &appData.secondLogo, FALSE }, + { "autoLogo", ArgBoolean, (LPVOID) &appData.autoLogo, TRUE }, + { "firstOptions", ArgString, (LPVOID) &appData.firstOptions, FALSE }, + { "secondOptions", ArgString, (LPVOID) &appData.secondOptions, FALSE }, + { "firstNeedsNoncompliantFEN", ArgString, (LPVOID) &appData.fenOverride1, FALSE }, + { "secondNeedsNoncompliantFEN", ArgString, (LPVOID) &appData.fenOverride2, FALSE }, + { "keepAlive", ArgInt, (LPVOID) &appData.keepAlive, FALSE }, + { "icstype", ArgInt, (LPVOID) &ics_type, FALSE }, + { "forceIllegalMoves", ArgTrue, (LPVOID) &appData.forceIllegal, FALSE }, + #ifdef ZIPPY { "zippyTalk", ArgBoolean, (LPVOID) &appData.zippyTalk, FALSE }, { "zt", ArgTrue, (LPVOID) &appData.zippyTalk, FALSE }, @@ -1052,9 +1380,69 @@ ArgDescriptor argDescriptors[] = { { "zippyVariants", ArgString, (LPVOID) &appData.zippyVariants, FALSE }, { "zippyMaxGames", ArgInt, (LPVOID)&appData.zippyMaxGames, FALSE }, { "zippyReplayTimeout", ArgInt, (LPVOID)&appData.zippyReplayTimeout, FALSE }, + { "zippyShortGame", ArgInt, (LPVOID)&appData.zippyShortGame, FALSE }, /* Kludge to allow winboard.ini files from buggy 4.0.4 to be read: */ { "zippyReplyTimeout", ArgInt, (LPVOID)&junk, FALSE }, #endif + /* [HGM] options for broadcasting and time odds */ + { "serverMoves", ArgString, (LPVOID) &appData.serverMovesName, FALSE }, + { "suppressLoadMoves", ArgBoolean, (LPVOID) &appData.suppressLoadMoves, FALSE }, + { "serverPause", ArgInt, (LPVOID) &appData.serverPause, FALSE }, + { "firstTimeOdds", ArgInt, (LPVOID) &appData.firstTimeOdds, FALSE }, + { "secondTimeOdds", ArgInt, (LPVOID) &appData.secondTimeOdds, FALSE }, + { "timeOddsMode", ArgInt, (LPVOID) &appData.timeOddsMode, TRUE }, + { "firstAccumulateTC", ArgInt, (LPVOID) &appData.firstAccumulateTC, FALSE }, + { "secondAccumulateTC", ArgInt, (LPVOID) &appData.secondAccumulateTC, FALSE }, + { "firstNPS", ArgInt, (LPVOID) &appData.firstNPS, FALSE }, + { "secondNPS", ArgInt, (LPVOID) &appData.secondNPS, FALSE }, + { "noGUI", ArgTrue, (LPVOID) &appData.noGUI, FALSE }, + + // [HGM] placement: put all window layouts last in ini file, but man X,Y before all others + { "minX", ArgZ, (LPVOID) &minX, FALSE }, // [HGM] placement: to make suer auxialary windows can be placed + { "minY", ArgZ, (LPVOID) &minY, FALSE }, + { "winWidth", ArgInt, (LPVOID) &winWidth, TRUE }, // [HGM] placement: dummies to remember right & bottom + { "winHeight", ArgInt, (LPVOID) &winHeight, TRUE }, // for attaching auxiliary windows to them + { "x", ArgInt, (LPVOID) &boardX, TRUE }, + { "y", ArgInt, (LPVOID) &boardY, TRUE }, + { "icsX", ArgX, (LPVOID) &wpConsole.x, TRUE }, + { "icsY", ArgY, (LPVOID) &wpConsole.y, TRUE }, + { "icsW", ArgInt, (LPVOID) &wpConsole.width, TRUE }, + { "icsH", ArgInt, (LPVOID) &wpConsole.height, TRUE }, + { "analysisX", ArgX, (LPVOID) &analysisX, FALSE }, // [HGM] placement: analysis window no longer exists + { "analysisY", ArgY, (LPVOID) &analysisY, FALSE }, // provided for compatibility with old ini files + { "analysisW", ArgInt, (LPVOID) &analysisW, FALSE }, + { "analysisH", ArgInt, (LPVOID) &analysisH, FALSE }, + { "commentX", ArgX, (LPVOID) &commentX, TRUE }, + { "commentY", ArgY, (LPVOID) &commentY, TRUE }, + { "commentW", ArgInt, (LPVOID) &commentW, TRUE }, + { "commentH", ArgInt, (LPVOID) &commentH, TRUE }, + { "tagsX", ArgX, (LPVOID) &editTagsX, TRUE }, + { "tagsY", ArgY, (LPVOID) &editTagsY, TRUE }, + { "tagsW", ArgInt, (LPVOID) &editTagsW, TRUE }, + { "tagsH", ArgInt, (LPVOID) &editTagsH, TRUE }, + { "gameListX", ArgX, (LPVOID) &wpGameList.x, TRUE }, + { "gameListY", ArgY, (LPVOID) &wpGameList.y, TRUE }, + { "gameListW", ArgInt, (LPVOID) &wpGameList.width, TRUE }, + { "gameListH", ArgInt, (LPVOID) &wpGameList.height, TRUE }, + /* [AS] Layout stuff */ + { "moveHistoryUp", ArgBoolean, (LPVOID) &wpMoveHistory.visible, TRUE }, + { "moveHistoryX", ArgX, (LPVOID) &wpMoveHistory.x, TRUE }, + { "moveHistoryY", ArgY, (LPVOID) &wpMoveHistory.y, TRUE }, + { "moveHistoryW", ArgInt, (LPVOID) &wpMoveHistory.width, TRUE }, + { "moveHistoryH", ArgInt, (LPVOID) &wpMoveHistory.height, TRUE }, + + { "evalGraphUp", ArgBoolean, (LPVOID) &wpEvalGraph.visible, TRUE }, + { "evalGraphX", ArgX, (LPVOID) &wpEvalGraph.x, TRUE }, + { "evalGraphY", ArgY, (LPVOID) &wpEvalGraph.y, TRUE }, + { "evalGraphW", ArgInt, (LPVOID) &wpEvalGraph.width, TRUE }, + { "evalGraphH", ArgInt, (LPVOID) &wpEvalGraph.height, TRUE }, + + { "engineOutputUp", ArgBoolean, (LPVOID) &wpEngineOutput.visible, TRUE }, + { "engineOutputX", ArgX, (LPVOID) &wpEngineOutput.x, TRUE }, + { "engineOutputY", ArgY, (LPVOID) &wpEngineOutput.y, TRUE }, + { "engineOutputW", ArgInt, (LPVOID) &wpEngineOutput.width, TRUE }, + { "engineOutputH", ArgInt, (LPVOID) &wpEngineOutput.height, TRUE }, + { NULL, ArgNone, NULL, FALSE } }; @@ -1098,7 +1486,7 @@ ParseFontName(char *name, MyFontParams *mfp) while (*p && !isdigit(*p)) { *q++ = *p++; if (q - mfp->faceName >= sizeof(mfp->faceName)) - ExitArgError("Font name too long:", name); + ExitArgError("Font name too long:", name); } while (q > mfp->faceName && q[-1] == ' ') q--; *q = NULLCHAR; @@ -1122,7 +1510,7 @@ ParseColorName(char *name) count = sscanf(name, "#%2x%2x%2x", &red, &green, &blue); if (count != 3) { - count = sscanf(name, "%3d%*[^0-9]%3d%*[^0-9]%3d", + count = sscanf(name, "%3d%*[^0-9]%3d%*[^0-9]%3d", &red, &green, &blue); } if (count != 3) { @@ -1179,6 +1567,7 @@ FileGet(void *getClosure) FILE* f = (FILE*) getClosure; c = getc(f); + if (c == '\r') c = getc(f); // work around DOS format files by bypassing the '\r' completely if (c == EOF) return NULLCHAR; else @@ -1192,8 +1581,14 @@ ParseSettingsFile(char *name, char fullname[MSG_SIZ]) { char *dummy; FILE *f; + int ok; char buf[MSG_SIZ]; - if (SearchPath(installDir, name, NULL, MSG_SIZ, fullname, &dummy)) { + ok = SearchPath(installDir, name, NULL, MSG_SIZ, fullname, &dummy); + if(!ok && strchr(name, '.') == NULL) { // [HGM] append default file-name extension '.ini' when needed + sprintf(buf, "%s.ini", name); + ok = SearchPath(installDir, buf, NULL, MSG_SIZ, fullname, &dummy); + } + if (ok) { f = fopen(fullname, "r"); if (f != NULL) { ParseArgs(FileGet, f); @@ -1229,17 +1624,17 @@ ParseArgs(GetFunc get, void *cl) /* Switch */ q = argName; while (ch != ' ' && ch != '=' && ch != ':' && ch != NULLCHAR && - ch != '\n' && ch != '\t') { - *q++ = ch; - ch = get(cl); + ch != '\n' && ch != '\t') { + *q++ = ch; + ch = get(cl); } *q = NULLCHAR; for (ad = argDescriptors; ad->argName != NULL; ad++) - if (strcmp(ad->argName, argName + 1) == 0) break; + if (strcmp(ad->argName, argName + 1) == 0) break; if (ad->argName == NULL) - ExitArgError("Unrecognized argument", argName); + ExitArgError("Unrecognized argument", argName); } else if (ch == '@') { /* Indirection file */ @@ -1271,104 +1666,104 @@ ParseArgs(GetFunc get, void *cl) start = ch; ch = get(cl); while (start) { - switch (ch) { - case NULLCHAR: - start = NULLCHAR; - break; - - case '}': - ch = get(cl); - start = NULLCHAR; - break; - - default: - *q++ = ch; - ch = get(cl); - break; - } - } + switch (ch) { + case NULLCHAR: + start = NULLCHAR; + break; + + case '}': + ch = get(cl); + start = NULLCHAR; + break; + + default: + *q++ = ch; + ch = get(cl); + break; + } + } } else if (ch == '\'' || ch == '"') { // Quoting with ' ' or " ", with \ as escape character. // Inconvenient for long strings that may contain Windows filenames. start = ch; ch = get(cl); while (start) { - switch (ch) { - case NULLCHAR: - start = NULLCHAR; - break; + switch (ch) { + case NULLCHAR: + start = NULLCHAR; + break; - default: + default: not_special: - *q++ = ch; - ch = get(cl); - break; - - case '\'': - case '\"': - if (ch == start) { - ch = get(cl); - start = NULLCHAR; - break; - } else { - goto not_special; - } - - case '\\': + *q++ = ch; + ch = get(cl); + break; + + case '\'': + case '\"': + if (ch == start) { + ch = get(cl); + start = NULLCHAR; + break; + } else { + goto not_special; + } + + case '\\': if (ad->argType == ArgFilename - || ad->argType == ArgSettingsFilename) { - goto not_special; - } - ch = get(cl); - switch (ch) { - case NULLCHAR: - ExitArgError("Incomplete \\ escape in value for", argName); - break; - case 'n': - *q++ = '\n'; - ch = get(cl); - break; - case 'r': - *q++ = '\r'; - ch = get(cl); - break; - case 't': - *q++ = '\t'; - ch = get(cl); - break; - case 'b': - *q++ = '\b'; - ch = get(cl); - break; - case 'f': - *q++ = '\f'; - ch = get(cl); - break; - default: - octval = 0; - for (i = 0; i < 3; i++) { - if (ch >= '0' && ch <= '7') { - octval = octval*8 + (ch - '0'); - ch = get(cl); - } else { - break; - } - } - if (i > 0) { - *q++ = (char) octval; - } else { - *q++ = ch; - ch = get(cl); - } - break; - } - break; - } + || ad->argType == ArgSettingsFilename) { + goto not_special; + } + ch = get(cl); + switch (ch) { + case NULLCHAR: + ExitArgError("Incomplete \\ escape in value for", argName); + break; + case 'n': + *q++ = '\n'; + ch = get(cl); + break; + case 'r': + *q++ = '\r'; + ch = get(cl); + break; + case 't': + *q++ = '\t'; + ch = get(cl); + break; + case 'b': + *q++ = '\b'; + ch = get(cl); + break; + case 'f': + *q++ = '\f'; + ch = get(cl); + break; + default: + octval = 0; + for (i = 0; i < 3; i++) { + if (ch >= '0' && ch <= '7') { + octval = octval*8 + (ch - '0'); + ch = get(cl); + } else { + break; + } + } + if (i > 0) { + *q++ = (char) octval; + } else { + *q++ = ch; + ch = get(cl); + } + break; + } + break; + } } } else { while (ch != ' ' && ch != NULLCHAR && ch != '\t' && ch != '\n') { - *q++ = ch; - ch = get(cl); + *q++ = ch; + ch = get(cl); } } *q = NULLCHAR; @@ -1378,6 +1773,19 @@ ParseArgs(GetFunc get, void *cl) *(int *) ad->argLoc = atoi(argValue); break; + case ArgX: + *(int *) ad->argLoc = atoi(argValue) + boardX; // [HGM] placement: translate stored relative to absolute + break; + + case ArgY: + *(int *) ad->argLoc = atoi(argValue) + boardY; // (this is really kludgey, it should be done where used...) + break; + + case ArgZ: + *(int *) ad->argLoc = atoi(argValue); + EnsureOnScreen(&boardX, &boardY, minX, minY); + break; + case ArgFloat: *(float *) ad->argLoc = (float) atof(argValue); break; @@ -1389,17 +1797,17 @@ ParseArgs(GetFunc get, void *cl) case ArgSettingsFilename: { - char fullname[MSG_SIZ]; - if (ParseSettingsFile(argValue, fullname)) { - if (ad->argLoc != NULL) { - *(char **) ad->argLoc = strdup(fullname); - } - } else { - if (ad->argLoc != NULL) { - } else { - ExitArgError("Failed to open indirection file", argValue); - } - } + char fullname[MSG_SIZ]; + if (ParseSettingsFile(argValue, fullname)) { + if (ad->argLoc != NULL) { + *(char **) ad->argLoc = strdup(fullname); + } + } else { + if (ad->argLoc != NULL) { + } else { + ExitArgError("Failed to open indirection file", argValue); + } + } } break; @@ -1407,15 +1815,15 @@ ParseArgs(GetFunc get, void *cl) switch (argValue[0]) { case 't': case 'T': - *(Boolean *) ad->argLoc = TRUE; - break; + *(Boolean *) ad->argLoc = TRUE; + break; case 'f': case 'F': - *(Boolean *) ad->argLoc = FALSE; - break; + *(Boolean *) ad->argLoc = FALSE; + break; default: - ExitArgError("Unrecognized boolean argument value", argValue); - break; + ExitArgError("Unrecognized boolean argument value", argValue); + break; } break; @@ -1428,7 +1836,7 @@ ParseArgs(GetFunc get, void *cl) ParseAttribs(&textAttribs[cc].color, &textAttribs[cc].effects, argValue); } break; - + case ArgBoardSize: *(BoardSize *)ad->argLoc = ParseBoardSize(argValue); break; @@ -1444,6 +1852,8 @@ ParseArgs(GetFunc get, void *cl) case ArgNone: ExitArgError("Unrecognized argument", argValue); break; + case ArgTrue: + case ArgFalse: ; } } } @@ -1482,9 +1892,9 @@ SetDefaultTextAttribs() { ColorClass cc; for (cc = (ColorClass)0; cc < NColorClasses; cc++) { - ParseAttribs(&textAttribs[cc].color, - &textAttribs[cc].effects, - defaultTextAttribs[cc]); + ParseAttribs(&textAttribs[cc].color, + &textAttribs[cc].effects, + defaultTextAttribs[cc]); } } @@ -1616,6 +2026,8 @@ InitAppData(LPSTR lpCmdLine) appData.reuseFirst = TRUE; appData.reuseSecond = TRUE; appData.blindfold = FALSE; + appData.icsEngineAnalyze = FALSE; + memset(&dcb, 0, sizeof(DCB)); // required by VS 2002 + dcb.DCBlength = sizeof(DCB); dcb.BaudRate = 9600; dcb.fBinary = TRUE; @@ -1630,7 +2042,6 @@ InitAppData(LPSTR lpCmdLine) dcb.fNull = FALSE; dcb.fRtsControl = RTS_CONTROL_ENABLE; dcb.fAbortOnError = FALSE; - dcb.wReserved = 0; dcb.ByteSize = 7; dcb.Parity = SPACEPARITY; dcb.StopBits = ONESTOPBIT; @@ -1638,26 +2049,18 @@ InitAppData(LPSTR lpCmdLine) saveSettingsOnExit = TRUE; boardX = CW_USEDEFAULT; boardY = CW_USEDEFAULT; - consoleX = CW_USEDEFAULT; - consoleY = CW_USEDEFAULT; - consoleW = CW_USEDEFAULT; - consoleH = CW_USEDEFAULT; - analysisX = CW_USEDEFAULT; - analysisY = CW_USEDEFAULT; + analysisX = CW_USEDEFAULT; + analysisY = CW_USEDEFAULT; analysisW = CW_USEDEFAULT; analysisH = CW_USEDEFAULT; - commentX = CW_USEDEFAULT; - commentY = CW_USEDEFAULT; + commentX = CW_USEDEFAULT; + commentY = CW_USEDEFAULT; commentW = CW_USEDEFAULT; commentH = CW_USEDEFAULT; - editTagsX = CW_USEDEFAULT; - editTagsY = CW_USEDEFAULT; + editTagsX = CW_USEDEFAULT; + editTagsY = CW_USEDEFAULT; editTagsW = CW_USEDEFAULT; editTagsH = CW_USEDEFAULT; - gameListX = CW_USEDEFAULT; - gameListY = CW_USEDEFAULT; - gameListW = CW_USEDEFAULT; - gameListH = CW_USEDEFAULT; icsTextMenuString = ICS_TEXT_MENU_DEFAULT; icsNames = ICS_NAMES; firstChessProgramNames = FCP_NAMES; @@ -1667,6 +2070,87 @@ 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"; + 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"; + appData.firstOptions = ""; + appData.secondOptions = ""; + + InitWindowPlacement( &wpGameList ); + InitWindowPlacement( &wpMoveHistory ); + InitWindowPlacement( &wpEvalGraph ); + InitWindowPlacement( &wpEngineOutput ); + InitWindowPlacement( &wpConsole ); + + /* [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; + appData.serverPause = 15; + appData.serverMovesName = NULL; + appData.suppressLoadMoves = FALSE; + appData.firstTimeOdds = 1; + appData.secondTimeOdds = 1; + appData.firstAccumulateTC = 1; // combine previous and current sessions + appData.secondAccumulateTC = 1; + appData.firstNPS = -1; // [HGM] nps: use wall-clock time + appData.secondNPS = -1; + appData.engineComments = 1; + appData.smpCores = 1; // [HGM] SMP: max nr of cores + appData.egtFormats = ""; + #ifdef ZIPPY appData.zippyTalk = ZIPPY_TALK; appData.zippyPlay = ZIPPY_PLAY; @@ -1696,7 +2180,7 @@ InitAppData(LPSTR lpCmdLine) ParseFontName(font[j][i]->def, &font[j][i]->mfp); } } - + /* Parse default settings file if any */ if (ParseSettingsFile(settingsFileName, buf)) { settingsFileName = strdup(buf); @@ -1705,6 +2189,40 @@ 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); + + /* [HGM] After parsing the options from the .ini file, and overruling them + * with options from the command line, we now make an even higher priority + * overrule by WB options attached to the engine command line. This so that + * tournament managers can use WB options (such as /timeOdds) that follow + * the engines. + */ + if(appData.firstChessProgram != NULL) { + char *p = StrStr(appData.firstChessProgram, "WBopt"); + static char *f = "first"; + char buf[MSG_SIZ], *q = buf; + if(p != NULL) { // engine command line contains WinBoard options + sprintf(buf, p+6, f, f, f, f, f, f, f, f, f, f); // replace %s in them by "first" + ParseArgs(StringGet, &q); + p[-1] = 0; // cut them offengine command line + } + } + // now do same for second chess program + if(appData.secondChessProgram != NULL) { + char *p = StrStr(appData.secondChessProgram, "WBopt"); + static char *s = "second"; + char buf[MSG_SIZ], *q = buf; + if(p != NULL) { // engine command line contains WinBoard options + sprintf(buf, p+6, s, s, s, s, s, s, s, s, s, s); // replace %s in them by "first" + ParseArgs(StringGet, &q); + p[-1] = 0; // cut them offengine command line + } + } + + /* Propagate options that affect others */ if (appData.matchMode || appData.matchGames) chessProgram = TRUE; if (appData.icsActive || appData.noChessProgram) { @@ -1717,7 +2235,7 @@ InitAppData(LPSTR lpCmdLine) (chessProgram && (*appData.firstChessProgram == NULLCHAR || *appData.secondChessProgram == NULLCHAR))) { FARPROC lpProc; - + lpProc = MakeProcInstance((FARPROC)StartupDialog, hInst); DialogBox(hInst, MAKEINTRESOURCE(DLG_Startup), NULL, (DLGPROC)lpProc); FreeProcInstance(lpProc); @@ -1775,12 +2293,12 @@ InitMenuChecks() HMENU hmenu = GetMenu(hwndMain); (void) EnableMenuItem(hmenu, IDM_CommPort, - MF_BYCOMMAND|((appData.icsActive && - *appData.icsCommPort != NULLCHAR) ? - MF_ENABLED : MF_GRAYED)); + MF_BYCOMMAND|((appData.icsActive && + *appData.icsCommPort != NULLCHAR) ? + MF_ENABLED : MF_GRAYED)); (void) CheckMenuItem(hmenu, IDM_SaveSettingsOnExit, - MF_BYCOMMAND|(saveSettingsOnExit ? - MF_CHECKED : MF_UNCHECKED)); + MF_BYCOMMAND|(saveSettingsOnExit ? + MF_CHECKED : MF_UNCHECKED)); } @@ -1803,7 +2321,7 @@ SaveSettings(char* name) return; } fprintf(f, ";\n"); - fprintf(f, "; %s %s.%s Save Settings file\n", PRODUCT, VERSION, PATCHLEVEL); + fprintf(f, "; %s Save Settings file\n", PACKAGE_STRING); fprintf(f, ";\n"); fprintf(f, "; You can edit the values of options that are already set in this file,\n"); fprintf(f, "; but if you add other options, the next Save Settings will not save them.\n"); @@ -1817,10 +2335,10 @@ SaveSettings(char* name) if (hwndConsole) { GetWindowPlacement(hwndConsole, &wp); - consoleX = wp.rcNormalPosition.left; - consoleY = wp.rcNormalPosition.top; - consoleW = wp.rcNormalPosition.right - wp.rcNormalPosition.left; - consoleH = wp.rcNormalPosition.bottom - wp.rcNormalPosition.top; + wpConsole.x = wp.rcNormalPosition.left; + wpConsole.y = wp.rcNormalPosition.top; + wpConsole.width = wp.rcNormalPosition.right - wp.rcNormalPosition.left; + wpConsole.height = wp.rcNormalPosition.bottom - wp.rcNormalPosition.top; } if (analysisDialog) { @@ -1849,10 +2367,43 @@ SaveSettings(char* name) if (gameListDialog) { GetWindowPlacement(gameListDialog, &wp); - gameListX = wp.rcNormalPosition.left; - gameListY = wp.rcNormalPosition.top; - gameListW = wp.rcNormalPosition.right - wp.rcNormalPosition.left; - gameListH = wp.rcNormalPosition.bottom - wp.rcNormalPosition.top; + wpGameList.x = wp.rcNormalPosition.left; + wpGameList.y = wp.rcNormalPosition.top; + wpGameList.width = wp.rcNormalPosition.right - wp.rcNormalPosition.left; + wpGameList.height = 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++) { @@ -1860,39 +2411,46 @@ SaveSettings(char* name) switch (ad->argType) { case ArgString: { - char *p = *(char **)ad->argLoc; - if ((strchr(p, '\\') || strchr(p, '\n')) && !strchr(p, '}')) { - /* Quote multiline values or \-containing values - with { } if possible */ - fprintf(f, "/%s={%s}\n", ad->argName, p); - } else { - /* Else quote with " " */ - fprintf(f, "/%s=\"", ad->argName); - while (*p) { - if (*p == '\n') fprintf(f, "\n"); - else if (*p == '\r') fprintf(f, "\\r"); - else if (*p == '\t') fprintf(f, "\\t"); - else if (*p == '\b') fprintf(f, "\\b"); - else if (*p == '\f') fprintf(f, "\\f"); - else if (*p < ' ') fprintf(f, "\\%03o", *p); - else if (*p == '\"') fprintf(f, "\\\""); - else if (*p == '\\') fprintf(f, "\\\\"); - else putc(*p, f); - p++; - } - fprintf(f, "\"\n"); - } + char *p = *(char **)ad->argLoc; + if ((strchr(p, '\\') || strchr(p, '\n')) && !strchr(p, '}')) { + /* Quote multiline values or \-containing values + with { } if possible */ + fprintf(f, "/%s={%s}\n", ad->argName, p); + } else { + /* Else quote with " " */ + fprintf(f, "/%s=\"", ad->argName); + while (*p) { + if (*p == '\n') fprintf(f, "\n"); + else if (*p == '\r') fprintf(f, "\\r"); + else if (*p == '\t') fprintf(f, "\\t"); + else if (*p == '\b') fprintf(f, "\\b"); + else if (*p == '\f') fprintf(f, "\\f"); + else if (*p < ' ') fprintf(f, "\\%03o", *p); + else if (*p == '\"') fprintf(f, "\\\""); + else if (*p == '\\') fprintf(f, "\\\\"); + else putc(*p, f); + p++; + } + fprintf(f, "\"\n"); + } } break; case ArgInt: + case ArgZ: fprintf(f, "/%s=%d\n", ad->argName, *(int *)ad->argLoc); break; + case ArgX: + fprintf(f, "/%s=%d\n", ad->argName, *(int *)ad->argLoc - boardX); // [HGM] placement: stor relative value + break; + case ArgY: + fprintf(f, "/%s=%d\n", ad->argName, *(int *)ad->argLoc - boardY); + break; case ArgFloat: fprintf(f, "/%s=%g\n", ad->argName, *(float *)ad->argLoc); break; case ArgBoolean: - fprintf(f, "/%s=%s\n", ad->argName, - (*(Boolean *)ad->argLoc) ? "true" : "false"); + fprintf(f, "/%s=%s\n", ad->argName, + (*(Boolean *)ad->argLoc) ? "true" : "false"); break; case ArgTrue: if (*(Boolean *)ad->argLoc) fprintf(f, "/%s\n", ad->argName); @@ -1902,52 +2460,54 @@ SaveSettings(char* name) break; case ArgColor: { - COLORREF color = *(COLORREF *)ad->argLoc; - fprintf(f, "/%s=#%02x%02x%02x\n", ad->argName, - color&0xff, (color>>8)&0xff, (color>>16)&0xff); + COLORREF color = *(COLORREF *)ad->argLoc; + fprintf(f, "/%s=#%02lx%02lx%02lx\n", ad->argName, + color&0xff, (color>>8)&0xff, (color>>16)&0xff); } break; case ArgAttribs: { - MyTextAttribs* ta = &textAttribs[(ColorClass)ad->argLoc]; - fprintf(f, "/%s=\"%s%s%s%s%s#%02x%02x%02x\"\n", ad->argName, + MyTextAttribs* ta = &textAttribs[(ColorClass)ad->argLoc]; + fprintf(f, "/%s=\"%s%s%s%s%s#%02lx%02lx%02lx\"\n", ad->argName, (ta->effects & CFE_BOLD) ? "b" : "", (ta->effects & CFE_ITALIC) ? "i" : "", (ta->effects & CFE_UNDERLINE) ? "u" : "", (ta->effects & CFE_STRIKEOUT) ? "s" : "", (ta->effects) ? " " : "", - ta->color&0xff, (ta->color >> 8)&0xff, (ta->color >> 16)&0xff); + ta->color&0xff, (ta->color >> 8)&0xff, (ta->color >> 16)&0xff); } break; case ArgFilename: if (strchr(*(char **)ad->argLoc, '\"')) { - fprintf(f, "/%s='%s'\n", ad->argName, *(char **)ad->argLoc); + fprintf(f, "/%s='%s'\n", ad->argName, *(char **)ad->argLoc); } else { - fprintf(f, "/%s=\"%s\"\n", ad->argName, *(char **)ad->argLoc); + fprintf(f, "/%s=\"%s\"\n", ad->argName, *(char **)ad->argLoc); } break; case ArgBoardSize: fprintf(f, "/%s=%s\n", ad->argName, - sizeInfo[*(BoardSize *)ad->argLoc].name); + sizeInfo[*(BoardSize *)ad->argLoc].name); break; case ArgFont: { int bs; - for (bs=0; bsargLoc]->mfp; + for (bs=0; bsargLoc]->mfp; fprintf(f, "/size=%s ", sizeInfo[bs].name); - fprintf(f, "/%s=\"%s:%g%s%s%s%s%s\"\n", - ad->argName, mfp->faceName, mfp->pointSize, + fprintf(f, "/%s=\"%s:%g%s%s%s%s%s\"\n", + ad->argName, mfp->faceName, mfp->pointSize, mfp->bold || mfp->italic || mfp->underline || mfp->strikeout ? " " : "", - mfp->bold ? "b" : "", - mfp->italic ? "i" : "", - mfp->underline ? "u" : "", - mfp->strikeout ? "s" : ""); - } + mfp->bold ? "b" : "", + mfp->italic ? "i" : "", + mfp->underline ? "u" : "", + mfp->strikeout ? "s" : ""); + } } break; case ArgCommSettings: PrintCommSettings(f, ad->argName, (DCB *)ad->argLoc); + case ArgNone: + case ArgSettingsFilename: ; } } fclose(f); @@ -1961,6 +2521,591 @@ 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] [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) WhiteAngel, + PM_WC = (int) WhiteMarshall, + PM_WAB = (int) WhiteCardinal, + PM_WD = (int) WhiteDragon, + PM_WL = (int) WhiteLance, + PM_WS = (int) WhiteCobra, + PM_WV = (int) WhiteFalcon, + PM_WSG = (int) WhiteSilver, + 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) BlackAngel, + PM_BC = (int) BlackMarshall, + PM_BG = (int) BlackGrasshopper, + PM_BAB = (int) BlackCardinal, + PM_BD = (int) BlackDragon, + PM_BL = (int) BlackLance, + PM_BS = (int) BlackCobra, + PM_BV = (int) BlackFalcon, + PM_BSG = (int) BlackSilver, + 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= (int)BlackPawn && 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[appData.allWhite && index >= (int)BlackPawn ? index - (int)BlackPawn : index], 1 ); + + SelectObject( hdc, GetStockObject(WHITE_BRUSH) ); + /* Step 3: the area outside the piece is filled with white */ +// FloodFill( hdc, 0, 0, chroma ); + ExtFloodFill( hdc, 0, 0, 0, FLOODFILLSURFACE ); + ExtFloodFill( hdc, 0, squareSize-1, 0, FLOODFILLSURFACE ); // [HGM] fill from all 4 corners, for if piece too big + ExtFloodFill( hdc, squareSize-1, 0, 0, FLOODFILLSURFACE ); + ExtFloodFill( hdc, squareSize-1, squareSize-1, 0, FLOODFILLSURFACE ); + 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) ); + ExtFloodFill( hdc, squareSize / 2, squareSize / 2, RGB(0xFF,0xFF,0xFF), FLOODFILLBORDER ); + + { /* [HGM] shave off edges of mask, in an attempt to correct for the fact that FloodFill does not work correctly under Win XP */ + HDC dc2 = CreateCompatibleDC( hdc_window ); + HBITMAP bm2 = CreateCompatibleBitmap( hdc_window, squareSize, squareSize ); + + SelectObject( dc2, bm2 ); + BitBlt( dc2, 0, 0, squareSize, squareSize, hdc, 0, 0, SRCCOPY ); // make copy + BitBlt( hdc, 0, 1, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT ); + BitBlt( hdc, 2, 1, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT ); + BitBlt( hdc, 1, 0, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT ); + BitBlt( hdc, 1, 2, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT ); + + DeleteDC( dc2 ); + DeleteObject( bm2 ); + } + + 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[appData.allWhite && index >= (int)BlackPawn ? index - (int)BlackPawn : 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[appData.allWhite && index >= (int)BlackPawn ? index - (int)BlackPawn : 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; + + case BlackAngel: + 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 BlackSilver: + return PM_BSG; + case BlackLance: + return PM_BL; + case BlackFalcon: + return PM_BV; + case BlackCobra: + return PM_BS; + case BlackCardinal: + return PM_BAB; + case BlackDragon: + return PM_BD; + + case WhiteAngel: + 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; + case WhiteSilver: + return PM_WSG; + case WhiteLance: + return PM_WL; + case WhiteFalcon: + return PM_WV; + case WhiteCobra: + return PM_WS; + case WhiteCardinal: + return PM_WAB; + case WhiteDragon: + return PM_WD; + } + + 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<=(int)BlackKing; 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, "PNBRQFEACWMOHIJGDVSLUKpnbrqfeacwmohijgdvsluk"); + } + 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 ); +#if 0 + 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 ); + + 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_WSG ); + CreatePieceMaskFromFont( hdc_window, hdc, PM_WV ); + CreatePieceMaskFromFont( hdc_window, hdc, PM_WAB ); + CreatePieceMaskFromFont( hdc_window, hdc, PM_WD ); + CreatePieceMaskFromFont( hdc_window, hdc, PM_WL ); + CreatePieceMaskFromFont( hdc_window, hdc, PM_WS ); + 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 ); + CreatePieceMaskFromFont( hdc_window, hdc, PM_BSG ); + CreatePieceMaskFromFont( hdc_window, hdc, PM_BV ); + CreatePieceMaskFromFont( hdc_window, hdc, PM_BAB ); + CreatePieceMaskFromFont( hdc_window, hdc, PM_BD ); + CreatePieceMaskFromFont( hdc_window, hdc, PM_BL ); + CreatePieceMaskFromFont( hdc_window, hdc, PM_BS ); +#else + for(i=(int)WhitePawn; i<(int)EmptySquare; i++) /* [HGM] made a loop for this */ + if(PieceToChar((ChessSquare)i) != '.') /* skip unused pieces */ + CreatePieceMaskFromFont( hdc_window, hdc, i ); +#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) { @@ -2010,7 +3155,7 @@ InitDrawingColors() */ pLogPal = (NPLOGPALETTE) LocalAlloc(LMEM_FIXED, (sizeof(LOGPALETTE) + - (sizeof(PALETTEENTRY) * (PALETTESIZE)))); + (sizeof(PALETTEENTRY) * (PALETTESIZE)))); pLogPal->palVersion = 0x300; } pLogPal->palNumEntries = 0; @@ -2028,18 +3173,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)); + explodeBrush = CreateSolidBrush(highlightSquareColor); // [HGM] atomic + /* [AS] Force rendering of the font-based pieces */ + if( fontBitmapSquareSize > 0 ) { + fontBitmapSquareSize = 0; + } } int -BoardWidth(int boardSize) -{ - return (BOARD_SIZE + 1) * sizeInfo[boardSize].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 */ @@ -2051,11 +3208,12 @@ ResizeBoard(int newSizeX, int newSizeY, int flags) if (IsIconic(hwndMain)) return; if (recurse > 0) return; recurse++; - while (newSize > 0 && - (newSizeX < sizeInfo[newSize].cliWidth || - newSizeY < sizeInfo[newSize].cliHeight)) { + while (newSize > 0) { + InitDrawingSizes(newSize+1000, 0); // [HGM] kludge to update sizeInfo without visible effects + if(newSizeX >= sizeInfo[newSize].cliWidth && + newSizeY >= sizeInfo[newSize].cliHeight) break; newSize--; - } + } boardSize = newSize; InitDrawingSizes(boardSize, flags); recurse--; @@ -2066,7 +3224,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; @@ -2075,21 +3233,37 @@ InitDrawingSizes(BoardSize boardSize, int flags) char buf[MSG_SIZ]; char *str; HMENU hmenu = GetMenu(hwndMain); - RECT crect, wrect; + RECT crect, wrect, oldRect; int offby; LOGBRUSH logbrush; + int suppressVisibleEffects = 0; // [HGM] kludge to request updating sizeInfo only + if((int)boardSize >= 1000 ) { boardSize -= 1000; suppressVisibleEffects = 1; } + + /* [HGM] call with -2 uses old size (for if nr of files, ranks changes) */ + if(boardSize == (BoardSize)(-2) ) boardSize = oldBoardSize; + + oldRect.left = boardX; //[HGM] placement: remember previous window params + oldRect.top = boardY; + oldRect.right = boardX + winWidth; + oldRect.bottom = boardY + winHeight; + 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 (tinyLayout != oldTinyLayout) { long style = GetWindowLong(hwndMain, GWL_STYLE); if (tinyLayout) { style &= ~WS_SYSMENU; InsertMenu(hmenu, IDM_Exit, MF_BYCOMMAND, IDM_Minimize, - "&Minimize\tCtrl+F4"); + "&Minimize\tCtrl+F4"); } else { style |= WS_SYSMENU; RemoveMenu(hmenu, IDM_Minimize, MF_BYCOMMAND); @@ -2097,13 +3271,14 @@ InitDrawingSizes(BoardSize boardSize, int flags) SetWindowLong(hwndMain, GWL_STYLE, style); for (i=0; menuBarText[tinyLayout][i]; i++) { - ModifyMenu(hmenu, i, MF_STRING|MF_BYPOSITION|MF_POPUP, - (UINT)GetSubMenu(hmenu, i), menuBarText[tinyLayout][i]); + ModifyMenu(hmenu, i, MF_STRING|MF_BYPOSITION|MF_POPUP, + (UINT)GetSubMenu(hmenu, i), menuBarText[tinyLayout][i]); } DrawMenuBar(hwndMain); } - boardWidth = BoardWidth(boardSize); + boardWidth = BoardWidth(boardSize, BOARD_WIDTH); + boardHeight = BoardWidth(boardSize, BOARD_HEIGHT); /* Get text area sizes */ hdc = GetDC(hwndMain); @@ -2121,61 +3296,99 @@ InitDrawingSizes(BoardSize boardSize, int flags) ReleaseDC(hwndMain, hdc); /* Compute where everything goes */ - whiteRect.left = OUTER_MARGIN; - whiteRect.right = whiteRect.left + boardWidth/2 - INNER_MARGIN/2; - whiteRect.top = OUTER_MARGIN; - whiteRect.bottom = whiteRect.top + clockSize.cy; + if((first.programLogo || second.programLogo) && !tinyLayout) { + /* [HGM] logo: if either logo is on, reserve space for it */ + logoHeight = 2*clockSize.cy; + leftLogoRect.left = OUTER_MARGIN; + leftLogoRect.right = leftLogoRect.left + 4*clockSize.cy; + leftLogoRect.top = OUTER_MARGIN; + leftLogoRect.bottom = OUTER_MARGIN + logoHeight; + + rightLogoRect.right = OUTER_MARGIN + boardWidth; + rightLogoRect.left = rightLogoRect.right - 4*clockSize.cy; + rightLogoRect.top = OUTER_MARGIN; + rightLogoRect.bottom = OUTER_MARGIN + logoHeight; + + + whiteRect.left = leftLogoRect.right; + whiteRect.right = OUTER_MARGIN + boardWidth/2 - INNER_MARGIN/2; + whiteRect.top = OUTER_MARGIN; + whiteRect.bottom = whiteRect.top + logoHeight; + + blackRect.right = rightLogoRect.left; + blackRect.left = whiteRect.right + INNER_MARGIN; + blackRect.top = whiteRect.top; + blackRect.bottom = whiteRect.bottom; + } else { + whiteRect.left = OUTER_MARGIN; + whiteRect.right = whiteRect.left + boardWidth/2 - INNER_MARGIN/2; + whiteRect.top = OUTER_MARGIN; + whiteRect.bottom = whiteRect.top + clockSize.cy; - blackRect.left = whiteRect.right + INNER_MARGIN; - blackRect.right = blackRect.left + boardWidth/2 - 1; - blackRect.top = whiteRect.top; - blackRect.bottom = whiteRect.bottom; + blackRect.left = whiteRect.right + INNER_MARGIN; + blackRect.right = blackRect.left + boardWidth/2 - 1; + blackRect.top = whiteRect.top; + blackRect.bottom = whiteRect.bottom; + } - messageRect.left = whiteRect.left + MESSAGE_LINE_LEFTMARGIN; + messageRect.left = OUTER_MARGIN + MESSAGE_LINE_LEFTMARGIN; if (appData.showButtonBar) { - messageRect.right = blackRect.right + messageRect.right = OUTER_MARGIN + boardWidth // [HGM] logo: expressed independent of clock placement - N_BUTTONS*BUTTON_WIDTH - MESSAGE_LINE_LEFTMARGIN; } else { - messageRect.right = blackRect.right; + messageRect.right = OUTER_MARGIN + boardWidth; } messageRect.top = whiteRect.bottom + INNER_MARGIN; messageRect.bottom = messageRect.top + messageSize.cy; - boardRect.left = whiteRect.left; + boardRect.left = OUTER_MARGIN; 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; - winWidth = 2 * GetSystemMetrics(SM_CXFRAME) + boardRect.right + OUTER_MARGIN; - winHeight = 2 * GetSystemMetrics(SM_CYFRAME) + GetSystemMetrics(SM_CYMENU) + + oldBoardSize = boardSize; + oldTinyLayout = tinyLayout; + winW = 2 * GetSystemMetrics(SM_CXFRAME) + boardRect.right + OUTER_MARGIN; + winH = 2 * GetSystemMetrics(SM_CYFRAME) + GetSystemMetrics(SM_CYMENU) + GetSystemMetrics(SM_CYCAPTION) + boardRect.bottom + OUTER_MARGIN; + if(suppressVisibleEffects) return; // [HGM] when called for filling sizeInfo only + winWidth = winW; // [HGM] placement: set through temporary which can used by initial sizing choice + winHeight = winH; // without disturbing window attachments GetWindowRect(hwndMain, &wrect); SetWindowPos(hwndMain, NULL, 0, 0, winWidth, winHeight, - SWP_NOCOPYBITS|SWP_NOZORDER|SWP_NOMOVE); + SWP_NOCOPYBITS|SWP_NOZORDER|SWP_NOMOVE); + + // [HGM] placement: let attached windows follow size change. + ReattachAfterSize( &oldRect, winWidth, winHeight, moveHistoryDialog, &wpMoveHistory ); + ReattachAfterSize( &oldRect, winWidth, winHeight, evalGraphDialog, &wpEvalGraph ); + ReattachAfterSize( &oldRect, winWidth, winHeight, engineOutputDialog, &wpEngineOutput ); + ReattachAfterSize( &oldRect, winWidth, winHeight, gameListDialog, &wpGameList ); + ReattachAfterSize( &oldRect, winWidth, winHeight, hwndConsole, &wpConsole ); + /* compensate if menu bar wrapped */ GetClientRect(hwndMain, &crect); offby = boardRect.bottom + OUTER_MARGIN - crect.bottom; winHeight += offby; switch (flags) { case WMSZ_TOPLEFT: - SetWindowPos(hwndMain, NULL, - wrect.right - winWidth, wrect.bottom - winHeight, + SetWindowPos(hwndMain, NULL, + wrect.right - winWidth, wrect.bottom - winHeight, winWidth, winHeight, SWP_NOCOPYBITS|SWP_NOZORDER); break; case WMSZ_TOPRIGHT: case WMSZ_TOP: - SetWindowPos(hwndMain, NULL, - wrect.left, wrect.bottom - winHeight, + SetWindowPos(hwndMain, NULL, + wrect.left, wrect.bottom - winHeight, winWidth, winHeight, SWP_NOCOPYBITS|SWP_NOZORDER); break; case WMSZ_BOTTOMLEFT: case WMSZ_LEFT: - SetWindowPos(hwndMain, NULL, - wrect.right - winWidth, wrect.top, + SetWindowPos(hwndMain, NULL, + wrect.right - winWidth, wrect.top, winWidth, winHeight, SWP_NOCOPYBITS|SWP_NOZORDER); break; @@ -2196,21 +3409,21 @@ InitDrawingSizes(BoardSize boardSize, int flags) } if (appData.showButtonBar) { buttonDesc[i].hwnd = - CreateWindow("BUTTON", buttonDesc[i].label, - WS_VISIBLE | WS_CHILD | BS_PUSHBUTTON, - boardRect.right - BUTTON_WIDTH*(N_BUTTONS-i), - messageRect.top, BUTTON_WIDTH, messageSize.cy, hwndMain, - (HMENU) buttonDesc[i].id, - (HINSTANCE) GetWindowLong(hwndMain, GWL_HINSTANCE), NULL); + CreateWindow("BUTTON", buttonDesc[i].label, + WS_VISIBLE | WS_CHILD | BS_PUSHBUTTON, + boardRect.right - BUTTON_WIDTH*(N_BUTTONS-i), + messageRect.top, BUTTON_WIDTH, messageSize.cy, hwndMain, + (HMENU) buttonDesc[i].id, + (HINSTANCE) GetWindowLong(hwndMain, GWL_HINSTANCE), NULL); if (tinyLayout) { - SendMessage(buttonDesc[i].hwnd, WM_SETFONT, - (WPARAM)font[boardSize][MESSAGE_FONT]->hf, - MAKELPARAM(FALSE, 0)); + SendMessage(buttonDesc[i].hwnd, WM_SETFONT, + (WPARAM)font[boardSize][MESSAGE_FONT]->hf, + MAKELPARAM(FALSE, 0)); } if (buttonDesc[i].id == IDM_Pause) - hwndPause = buttonDesc[i].hwnd; + hwndPause = buttonDesc[i].hwnd; buttonDesc[i].wndproc = (WNDPROC) - SetWindowLong(buttonDesc[i].hwnd, GWL_WNDPROC, (LONG) ButtonProc); + SetWindowLong(buttonDesc[i].hwnd, GWL_WNDPROC, (LONG) ButtonProc); } } if (gridPen != NULL) DeleteObject(gridPen); @@ -2227,60 +3440,245 @@ InitDrawingSizes(BoardSize boardSize, int flags) ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER, lineGap, &logbrush, 0, NULL); - logbrush.lbColor = premoveHighlightColor; + logbrush.lbColor = premoveHighlightColor; premovePen = 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)); + 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 + - lineGap / 2 + (i * (squareSize + lineGap)); - gridEndpoints[i*2 + 1 + BOARD_SIZE*2 + 2].y = - boardRect.top + BOARD_SIZE * (squareSize + lineGap); + BOARD_WIDTH * (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_HEIGHT*2 + 2].y = + boardRect.top + BOARD_HEIGHT * (squareSize + lineGap); gridVertexCounts[i*2] = gridVertexCounts[i*2 + 1] = 2; } } - if (boardSize == oldBoardSize) return; - oldBoardSize = boardSize; - oldTinyLayout = tinyLayout; + /* [HGM] Licensing requirement */ +#ifdef GOTHIC + if(gameInfo.variant == VariantGothic) GothicPopUp( GOTHIC, VariantGothic); else +#endif +#ifdef FALCON + if(gameInfo.variant == VariantFalcon) GothicPopUp( FALCON, VariantFalcon); else +#endif + GothicPopUp( "", VariantNormal); + + +/* if (boardSize == oldBoardSize) return; [HGM] variant might have changed */ /* Load piece bitmaps for this board size */ for (i=0; i<=2; i++) { for (piece = WhitePawn; - (int) piece <= (int) WhiteKing; - piece = (ChessSquare) ((int) piece + 1)) { + (int) piece < (int) BlackPawn; + piece = (ChessSquare) ((int) piece + 1)) { if (pieceBitmap[i][piece] != NULL) - DeleteObject(pieceBitmap[i][piece]); + DeleteObject(pieceBitmap[i][piece]); } } + fontBitmapSquareSize = 0; /* [HGM] render: make sure pieces will be recreated, as we might need others now */ + // Orthodox Chess pieces pieceBitmap[0][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "s"); 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)) { + // in Shogi, Hijack the unused Queen for Lance + 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 >= 33) { + /* A & C are available in most sizes now */ + if(squareSize != 49 && squareSize != 72 && squareSize != 33) { // Vortex-like + pieceBitmap[0][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "s"); + pieceBitmap[1][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "o"); + pieceBitmap[2][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "w"); + pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "s"); + pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "o"); + pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "w"); + pieceBitmap[0][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "s"); + pieceBitmap[1][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "o"); + pieceBitmap[2][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "w"); + pieceBitmap[0][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "s"); + pieceBitmap[1][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "o"); + pieceBitmap[2][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "w"); + } else { // Smirf-like + pieceBitmap[0][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "s"); + pieceBitmap[1][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "o"); + pieceBitmap[2][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "w"); + } + if(gameInfo.variant == VariantGothic) { // Vortex-like + pieceBitmap[0][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "s"); + pieceBitmap[1][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "o"); + pieceBitmap[2][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "w"); + } else { // WinBoard standard + pieceBitmap[0][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "s"); + pieceBitmap[1][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "o"); + pieceBitmap[2][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "w"); + } + } + + + if(squareSize==72 || squareSize==49 || squareSize==33) { /* 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][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "s"); + pieceBitmap[1][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "o"); + pieceBitmap[2][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "w"); + pieceBitmap[0][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "s"); + pieceBitmap[1][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "o"); + pieceBitmap[2][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "w"); + pieceBitmap[0][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "s"); + pieceBitmap[1][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "o"); + pieceBitmap[2][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "w"); + pieceBitmap[0][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "s"); + pieceBitmap[1][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "o"); + pieceBitmap[2][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "w"); + pieceBitmap[0][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "s"); + pieceBitmap[1][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "o"); + pieceBitmap[2][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "w"); + pieceBitmap[0][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "s"); + pieceBitmap[1][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "o"); + pieceBitmap[2][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "w"); + + if(gameInfo.variant == VariantShogi) { /* promoted Gold represemtations */ + pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "wp", squareSize, "s"); + pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "wp", squareSize, "o"); + pieceBitmap[2][WhiteCannon] = 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][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "s"); + pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "o"); + pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "o", 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][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "s"); + pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "o"); + pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "cv", 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"); + } + + } 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][WhiteDragon] = DoLoadBitmap(hInst, "r", minorSize, "s"); + pieceBitmap[1][WhiteDragon] = DoLoadBitmap(hInst, "r", minorSize, "o"); + pieceBitmap[2][WhiteDragon] = 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"); + } + + + if(gameInfo.variant == VariantShogi && squareSize == 58) + /* special Shogi support in this size */ + { for (i=0; i<=2; i++) { /* replace all bitmaps */ + for (piece = WhitePawn; + (int) piece < (int) BlackPawn; + piece = (ChessSquare) ((int) piece + 1)) { + if (pieceBitmap[i][piece] != NULL) + DeleteObject(pieceBitmap[i][piece]); + } + } + pieceBitmap[0][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "o"); + pieceBitmap[0][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "o"); + pieceBitmap[0][WhiteBishop] = DoLoadBitmap(hInst, "sb", squareSize, "o"); + pieceBitmap[0][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "o"); + pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "o"); + pieceBitmap[0][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "o"); + pieceBitmap[0][WhiteFerz] = DoLoadBitmap(hInst, "sf", squareSize, "o"); + pieceBitmap[0][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "o"); + pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "su", squareSize, "o"); + pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "sh", squareSize, "o"); + pieceBitmap[0][WhiteCardinal] = DoLoadBitmap(hInst, "sa", squareSize, "o"); + pieceBitmap[0][WhiteDragon] = DoLoadBitmap(hInst, "sc", squareSize, "o"); + pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "sg", squareSize, "o"); + pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "ss", squareSize, "o"); + pieceBitmap[1][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "o"); + pieceBitmap[1][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "o"); + pieceBitmap[1][WhiteBishop] = DoLoadBitmap(hInst, "sb", squareSize, "o"); + pieceBitmap[1][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "o"); + pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "o"); + pieceBitmap[1][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "o"); + pieceBitmap[1][WhiteFerz] = DoLoadBitmap(hInst, "sf", squareSize, "o"); + pieceBitmap[1][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "o"); + pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "su", squareSize, "o"); + pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "sh", squareSize, "o"); + pieceBitmap[1][WhiteCardinal] = DoLoadBitmap(hInst, "sa", squareSize, "o"); + pieceBitmap[1][WhiteDragon] = DoLoadBitmap(hInst, "sc", squareSize, "o"); + pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "sg", squareSize, "o"); + pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "ss", squareSize, "o"); + pieceBitmap[2][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "w"); + pieceBitmap[2][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "w"); + pieceBitmap[2][WhiteBishop] = DoLoadBitmap(hInst, "sr", squareSize, "w"); + pieceBitmap[2][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "w"); + pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "w"); + pieceBitmap[2][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "w"); + pieceBitmap[2][WhiteFerz] = DoLoadBitmap(hInst, "sw", squareSize, "w"); + pieceBitmap[2][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "w"); + pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "sp", squareSize, "w"); + pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "sn", squareSize, "w"); + pieceBitmap[2][WhiteCardinal] = DoLoadBitmap(hInst, "sr", squareSize, "w"); + pieceBitmap[2][WhiteDragon] = DoLoadBitmap(hInst, "sr", squareSize, "w"); + pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "sl", squareSize, "w"); + pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "sw", squareSize, "w"); + minorSize = 0; + } } HBITMAP @@ -2305,19 +3703,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; @@ -2326,7 +3724,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)); @@ -2334,21 +3732,23 @@ DrawCoordsOnDC(HDC hdc) oldFont = SelectObject(hdc, font[boardSize][COORD_FONT]->hf); y = boardRect.top + lineGap; - x = boardRect.left + lineGap; + x = boardRect.left + lineGap + gameInfo.holdingsWidth*(squareSize + 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_RGHT-BOARD_LEFT) : 12; + SetTextAlign(hdc, TA_RIGHT|TA_BOTTOM); - for (i = 0; i < 8; i++) { + for (i = 0; i < BOARD_RGHT - BOARD_LEFT; i++) { str[0] = ranks[start + i]; ExtTextOut(hdc, x + squareSize - 2, y - 1, 0, NULL, str, 1, NULL); x += squareSize + lineGap; - } + } SelectObject(hdc, oldBrush); SetBkMode(hdc, oldMode); @@ -2360,10 +3760,10 @@ VOID DrawGridOnDC(HDC hdc) { HPEN oldPen; - + 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); } } @@ -2379,14 +3779,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); @@ -2403,18 +3803,18 @@ DrawHighlightsOnDC(HDC hdc) { int i; for (i=0; i<2; i++) { - if (highlightInfo.sq[i].x >= 0 && highlightInfo.sq[i].y >= 0) + if (highlightInfo.sq[i].x >= 0 && highlightInfo.sq[i].y >= 0) DrawHighlightOnDC(hdc, TRUE, - highlightInfo.sq[i].x, highlightInfo.sq[i].y, - HIGHLIGHT_PEN); + highlightInfo.sq[i].x, highlightInfo.sq[i].y, + HIGHLIGHT_PEN); } for (i=0; i<2; i++) { - if (premoveHighlightInfo.sq[i].x >= 0 && - premoveHighlightInfo.sq[i].y >= 0) { - DrawHighlightOnDC(hdc, TRUE, - premoveHighlightInfo.sq[i].x, - premoveHighlightInfo.sq[i].y, - PREMOVE_PEN); + if (premoveHighlightInfo.sq[i].x >= 0 && + premoveHighlightInfo.sq[i].y >= 0) { + DrawHighlightOnDC(hdc, TRUE, + premoveHighlightInfo.sq[i].x, + premoveHighlightInfo.sq[i].y, + PREMOVE_PEN); } } } @@ -2427,49 +3827,101 @@ DrawPieceOnDC(HDC hdc, ChessSquare piece, int color, int sqcolor, int x, int y, { HBITMAP oldBitmap; HBRUSH oldBrush; + int tmpSize; 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 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, + SelectObject(tmphdc, PieceBitmap(piece, color == sqcolor ? OUTLINE_PIECE : SOLID_PIECE)); BitBlt(hdc, x, y, squareSize, squareSize, tmphdc, 0, 0, - sqcolor ? SRCCOPY : NOTSRCCOPY); + sqcolor ? SRCCOPY : NOTSRCCOPY); } else { - if (color) { + tmpSize = squareSize; + 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; + tmpSize = minorSize; + } + if (color || appData.allWhite ) { oldBitmap = SelectObject(tmphdc, PieceBitmap(piece, WHITE_PIECE)); - oldBrush = SelectObject(hdc, whitePieceBrush); - BitBlt(hdc, x, y, squareSize, squareSize, tmphdc, 0, 0, 0x00B8074A); + if( color ) + oldBrush = SelectObject(hdc, whitePieceBrush); + else oldBrush = SelectObject(hdc, blackPieceBrush); + if(appData.upsideDown && color==flipView) + StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, 0x00B8074A); + else + BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, 0x00B8074A); #if 0 /* Use black piece color for outline of white pieces */ /* Not sure this looks really good (though xboard does it). - Maybe better to have another selectable color, default black */ + Maybe better to have another selectable color, default black */ SelectObject(hdc, blackPieceBrush); /* could have own brush */ SelectObject(tmphdc, PieceBitmap(piece, OUTLINE_PIECE)); - BitBlt(hdc, x, y, squareSize, squareSize, tmphdc, 0, 0, 0x00B8074A); + BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, 0x00B8074A); #else /* Use black for outline of white pieces */ SelectObject(tmphdc, PieceBitmap(piece, OUTLINE_PIECE)); - BitBlt(hdc, x, y, squareSize, squareSize, tmphdc, 0, 0, SRCAND); + if(appData.upsideDown && color==flipView) + StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, SRCAND); + else + BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, SRCAND); #endif } else { #if 0 /* Use white piece color for details of black pieces */ /* Requires filled-in solid bitmaps (BLACK_PIECE class); the - WHITE_PIECE ones aren't always the right shape. */ + WHITE_PIECE ones aren't always the right shape. */ /* Not sure this looks really good (though xboard does it). - Maybe better to have another selectable color, default medium gray? */ + Maybe better to have another selectable color, default medium gray? */ oldBitmap = SelectObject(tmphdc, PieceBitmap(piece, BLACK_PIECE)); oldBrush = SelectObject(hdc, whitePieceBrush); /* could have own brush */ - BitBlt(hdc, x, y, squareSize, squareSize, tmphdc, 0, 0, 0x00B8074A); + BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, 0x00B8074A); SelectObject(tmphdc, PieceBitmap(piece, SOLID_PIECE)); SelectObject(hdc, blackPieceBrush); - BitBlt(hdc, x, y, squareSize, squareSize, tmphdc, 0, 0, 0x00B8074A); + BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, 0x00B8074A); #else /* Use square color for details of black pieces */ oldBitmap = SelectObject(tmphdc, PieceBitmap(piece, SOLID_PIECE)); oldBrush = SelectObject(hdc, blackPieceBrush); - BitBlt(hdc, x, y, squareSize, squareSize, tmphdc, 0, 0, 0x00B8074A); + if(appData.upsideDown && !flipView) + StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, 0x00B8074A); + else + BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, 0x00B8074A); #endif } SelectObject(hdc, oldBrush); @@ -2477,33 +3929,456 @@ 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_WIDTH-1); /* [HGM] divide by size-1 in stead of size! */ + backTextureSquareInfo[row][col].y = (BOARD_HEIGHT-1-row) * (lite_h - squareSize) / (BOARD_HEIGHT-1); + 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-1); + backTextureSquareInfo[row][col].y = (BOARD_HEIGHT-1-row) * (dark_h - squareSize) / (BOARD_HEIGHT-1); + 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 ) { + static int backTextureBoardSize; /* [HGM] boardsize: also new texture if board format changed */ + if( backTextureSquareSize != squareSize + || backTextureBoardSize != BOARD_WIDTH+BOARD_SIZE*BOARD_HEIGHT) { + backTextureBoardSize = BOARD_WIDTH+BOARD_SIZE*BOARD_HEIGHT; + backTextureSquareSize = squareSize; + RebuildTextureSquareInfo(); + } + + texture_hdc = CreateCompatibleDC( hdc ); + } + + for (row = 0; row < BOARD_HEIGHT; row++) { + for (column = 0; column < BOARD_WIDTH; column++) { - for (row = 0; row < BOARD_SIZE; row++) { - for (column = 0; column < BOARD_SIZE; column++) { - SquareToPos(row, column, &x, &y); piece = board[row][column]; square_color = ((column + row) % 2) == 1; + if( gameInfo.variant == VariantXiangqi ) { + 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; + + /* [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, flipView, (int) board[row][column]); + else if( column == BOARD_RGHT) /* right align */ + DisplayHoldingsCount(hdc, x, y, !flipView, (int) board[row][column]); + else if (appData.monoMode) { if (piece == EmptySquare) { BitBlt(hdc, x, y, squareSize, squareSize, 0, 0, 0, - square_color ? WHITENESS : BLACKNESS); + square_color ? WHITENESS : BLACKNESS); } else { DrawPieceOnDC(hdc, piece, piece_color, square_color, 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 ); + int r = row, c = column; // [HGM] do not flip board in flipView + if(flipView) { r = BOARD_HEIGHT-1 - r; c = BOARD_WIDTH-1 - c; } + + DrawTile( x, y, + squareSize, squareSize, + hdc, + texture_hdc, + backTextureSquareInfo[r][c].mode, + backTextureSquareInfo[r][c].x, + backTextureSquareInfo[r][c].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) @@ -2511,11 +4386,50 @@ DrawBoardOnDC(HDC hdc, Board board, HDC tmphdc) } } } + + if( texture_hdc != NULL ) { + DeleteDC( texture_hdc ); + } +} + +int saveDiagFlag = 0; FILE *diagFile; // [HGM] diag +void fputDW(FILE *f, int x) +{ + fputc(x & 255, f); + fputc(x>>8 & 255, f); + fputc(x>>16 & 255, f); + fputc(x>>24 & 255, f); } #define MAX_CLIPS 200 /* more than enough */ VOID +DrawLogoOnDC(HDC hdc, RECT logoRect, HBITMAP logo) +{ +// HBITMAP bufferBitmap; + BITMAP bi; +// RECT Rect; + HDC tmphdc; + HBITMAP hbm; + int w = 100, h = 50; + + if(logo == NULL) return; +// GetClientRect(hwndMain, &Rect); +// bufferBitmap = CreateCompatibleBitmap(hdc, Rect.right-Rect.left+1, +// Rect.bottom-Rect.top+1); + tmphdc = CreateCompatibleDC(hdc); + hbm = SelectObject(tmphdc, logo); + if( GetObject( logo, sizeof(bi), &bi ) > 0 ) { + w = bi.bmWidth; + h = bi.bmHeight; + } + StretchBlt(hdc, logoRect.left, logoRect.top, logoRect.right - logoRect.left, + logoRect.bottom - logoRect.top, tmphdc, 0, 0, w, h, SRCCOPY); + SelectObject(tmphdc, hbm); + DeleteDC(tmphdc); +} + +VOID HDCDrawPosition(HDC hdc, BOOLEAN repaint, Board board) { static Board lastReq, lastDrawn; @@ -2536,11 +4450,26 @@ HDCDrawPosition(HDC hdc, BOOLEAN repaint, Board board) * caller tell it that. I think this can safely be set to FALSE - but * if we trust the callers not to request full repaints unnessesarily, then * we could skip some clipping work. In other words, only request a full - * redraw when the majority of pieces have changed positions (ie. flip, + * redraw when the majority of pieces have changed positions (ie. flip, * gamestart and similar) --Hawk */ 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; @@ -2576,22 +4505,22 @@ HDCDrawPosition(HDC hdc, BOOLEAN repaint, Board board) "dragInfo.from (%d,%d)\n" "dragInfo.start (%d,%d)\n" "dragInfo.pos (%d,%d)\n" - "dragInfo.lastpos (%d,%d)\n", + "dragInfo.lastpos (%d,%d)\n", repaint ? "TRUE" : "FALSE", - dragInfo.from.x, dragInfo.from.y, + dragInfo.from.x, dragInfo.from.y, dragInfo.start.x, dragInfo.start.y, 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]); } } @@ -2603,58 +4532,74 @@ HDCDrawPosition(HDC hdc, BOOLEAN repaint, Board board) hdcmem = CreateCompatibleDC(hdc); tmphdc = CreateCompatibleDC(hdc); - /* Figure out which squares need updating by comparing the + /* 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++) { - if (lastDrawn[row][column] != board[row][column]) { - SquareToPos(row, column, &x, &y); - clips[num_clips++] = - CreateRectRgn(x, y, x + squareSize, y + squareSize); - } + 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++] = + CreateRectRgn(x, y, x + squareSize, y + squareSize); + } } } for (i=0; i<2; i++) { if (lastDrawnHighlight.sq[i].x != highlightInfo.sq[i].x || - lastDrawnHighlight.sq[i].y != highlightInfo.sq[i].y) { - if (lastDrawnHighlight.sq[i].x >= 0 && - lastDrawnHighlight.sq[i].y >= 0) { - SquareToPos(lastDrawnHighlight.sq[i].y, - lastDrawnHighlight.sq[i].x, &x, &y); - clips[num_clips++] = - CreateRectRgn(x - lineGap, y - lineGap, - x + squareSize + lineGap, y + squareSize + lineGap); - } - if (highlightInfo.sq[i].x >= 0 && highlightInfo.sq[i].y >= 0) { - SquareToPos(highlightInfo.sq[i].y, highlightInfo.sq[i].x, &x, &y); - clips[num_clips++] = - CreateRectRgn(x - lineGap, y - lineGap, - x + squareSize + lineGap, y + squareSize + lineGap); - } + lastDrawnHighlight.sq[i].y != highlightInfo.sq[i].y) { + if (lastDrawnHighlight.sq[i].x >= 0 && + lastDrawnHighlight.sq[i].y >= 0) { + SquareToPos(lastDrawnHighlight.sq[i].y, + lastDrawnHighlight.sq[i].x, &x, &y); + clips[num_clips++] = + CreateRectRgn(x - lineGap, y - lineGap, + x + squareSize + lineGap, y + squareSize + lineGap); + } + if (highlightInfo.sq[i].x >= 0 && highlightInfo.sq[i].y >= 0) { + SquareToPos(highlightInfo.sq[i].y, highlightInfo.sq[i].x, &x, &y); + clips[num_clips++] = + CreateRectRgn(x - lineGap, y - lineGap, + x + squareSize + lineGap, y + squareSize + lineGap); + } } } for (i=0; i<2; i++) { if (lastDrawnPremove.sq[i].x != premoveHighlightInfo.sq[i].x || - lastDrawnPremove.sq[i].y != premoveHighlightInfo.sq[i].y) { - if (lastDrawnPremove.sq[i].x >= 0 && - lastDrawnPremove.sq[i].y >= 0) { - SquareToPos(lastDrawnPremove.sq[i].y, - lastDrawnPremove.sq[i].x, &x, &y); - clips[num_clips++] = - CreateRectRgn(x - lineGap, y - lineGap, - x + squareSize + lineGap, y + squareSize + lineGap); - } - if (premoveHighlightInfo.sq[i].x >= 0 && - premoveHighlightInfo.sq[i].y >= 0) { - SquareToPos(premoveHighlightInfo.sq[i].y, - premoveHighlightInfo.sq[i].x, &x, &y); - clips[num_clips++] = - CreateRectRgn(x - lineGap, y - lineGap, - x + squareSize + lineGap, y + squareSize + lineGap); - } + lastDrawnPremove.sq[i].y != premoveHighlightInfo.sq[i].y) { + if (lastDrawnPremove.sq[i].x >= 0 && + lastDrawnPremove.sq[i].y >= 0) { + SquareToPos(lastDrawnPremove.sq[i].y, + lastDrawnPremove.sq[i].x, &x, &y); + clips[num_clips++] = + CreateRectRgn(x - lineGap, y - lineGap, + x + squareSize + lineGap, y + squareSize + lineGap); + } + if (premoveHighlightInfo.sq[i].x >= 0 && + premoveHighlightInfo.sq[i].y >= 0) { + SquareToPos(premoveHighlightInfo.sq[i].y, + premoveHighlightInfo.sq[i].x, &x, &y); + clips[num_clips++] = + CreateRectRgn(x - lineGap, y - lineGap, + x + squareSize + lineGap, y + squareSize + lineGap); + } } } } else { @@ -2668,7 +4613,7 @@ HDCDrawPosition(HDC hdc, BOOLEAN repaint, Board board) */ GetClientRect(hwndMain, &Rect); bufferBitmap = CreateCompatibleBitmap(hdc, Rect.right-Rect.left+1, - Rect.bottom-Rect.top+1); + Rect.bottom-Rect.top+1); oldBitmap = SelectObject(hdcmem, bufferBitmap); if (!appData.monoMode) { SelectPalette(hdcmem, hPal, FALSE); @@ -2696,14 +4641,8 @@ 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, + /* Are we animating a move? + * If so, * - remove the piece from the board (temporarely) * - calculate the clipping region */ @@ -2716,12 +4655,12 @@ HDCDrawPosition(HDC hdc, BOOLEAN repaint, Board board) y2 = boardRect.top + animInfo.pos.y; clips[num_clips++] = CreateRectRgn(MIN(x,x2), MIN(y,y2), MAX(x,x2)+squareSize, MAX(y,y2)+squareSize); /* Slight kludge. The real problem is that after AnimateMove is - done, the position on the screen does not match lastDrawn. - This currently causes trouble only on e.p. captures in - atomic, where the piece moves to an empty square and then - explodes. The old and new positions both had an empty square - at the destination, but animation has drawn a piece there and - we have to remember to erase it. */ + done, the position on the screen does not match lastDrawn. + This currently causes trouble only on e.p. captures in + atomic, where the piece moves to an empty square and then + explodes. The old and new positions both had an empty square + at the destination, but animation has drawn a piece there and + we have to remember to erase it. [HGM] moved until after setting lastDrawn */ lastDrawn[animInfo.to.y][animInfo.to.x] = animInfo.piece; } } @@ -2740,32 +4679,100 @@ HDCDrawPosition(HDC hdc, BOOLEAN repaint, Board board) } /* Do all the drawing to the memory DC */ - DrawGridOnDC(hdcmem); - DrawHighlightsOnDC(hdcmem); - DrawBoardOnDC(hdcmem, board, tmphdc); + if(explodeInfo.radius) { // [HGM] atomic + HBRUSH oldBrush; + int x, y, r=(explodeInfo.radius * squareSize)/100; + board[explodeInfo.fromY][explodeInfo.fromX] = EmptySquare; // suppress display of capturer + SquareToPos(explodeInfo.toY, explodeInfo.toX, &x, &y); + x += squareSize/2; + y += squareSize/2; + if(!fullrepaint) { + clips[num_clips] = CreateRectRgn(x-r, y-r, x+r, y+r); + ExtSelectClipRgn(hdcmem, clips[num_clips++], RGN_OR); + } + DrawGridOnDC(hdcmem); + DrawHighlightsOnDC(hdcmem); + DrawBoardOnDC(hdcmem, board, tmphdc); + oldBrush = SelectObject(hdcmem, explodeBrush); + Ellipse(hdcmem, x-r, y-r, x+r, y+r); + SelectObject(hdcmem, oldBrush); + } else { + DrawGridOnDC(hdcmem); + DrawHighlightsOnDC(hdcmem); + DrawBoardOnDC(hdcmem, board, tmphdc); + } + if(logoHeight) { + HBITMAP whiteLogo = (HBITMAP) first.programLogo, blackLogo = (HBITMAP) second.programLogo; + if(appData.autoLogo) { + + switch(gameMode) { // pick logos based on game mode + case IcsObserving: + whiteLogo = second.programLogo; // ICS logo + blackLogo = second.programLogo; + default: + break; + case IcsPlayingWhite: + if(!appData.zippyPlay) whiteLogo = userLogo; + blackLogo = second.programLogo; // ICS logo + break; + case IcsPlayingBlack: + whiteLogo = second.programLogo; // ICS logo + blackLogo = appData.zippyPlay ? first.programLogo : userLogo; + break; + case TwoMachinesPlay: + if(first.twoMachinesColor[0] == 'b') { + whiteLogo = second.programLogo; + blackLogo = first.programLogo; + } + break; + case MachinePlaysWhite: + blackLogo = userLogo; + break; + case MachinePlaysBlack: + whiteLogo = userLogo; + blackLogo = first.programLogo; + } + } + DrawLogoOnDC(hdc, leftLogoRect, flipClock ? blackLogo : whiteLogo); + DrawLogoOnDC(hdc, rightLogoRect, flipClock ? whiteLogo : blackLogo); + } + + 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; DrawPieceOnDC(hdcmem, dragged_piece, - ((int) dragged_piece < (int) BlackPawn), + ((int) dragged_piece < (int) BlackPawn), (dragInfo.from.y + dragInfo.from.x) % 2, x, y, tmphdc); - } - + } + /* Put the animated piece back into place and draw it */ if (animInfo.piece != EmptySquare) { board[animInfo.from.y][animInfo.from.x] = animInfo.piece; x = boardRect.left + animInfo.pos.x; y = boardRect.top + animInfo.pos.y; DrawPieceOnDC(hdcmem, animInfo.piece, - ((int) animInfo.piece < (int) BlackPawn), + ((int) animInfo.piece < (int) BlackPawn), (animInfo.from.y + animInfo.from.x) % 2, x, y, tmphdc); } - /* Release the bufferBitmap by selecting in the old bitmap + /* Release the bufferBitmap by selecting in the old bitmap * and delete the memory DC */ SelectObject(hdcmem, oldBitmap); @@ -2777,7 +4784,7 @@ HDCDrawPosition(HDC hdc, BOOLEAN repaint, Board board) for (x = 1; x < num_clips; x++) { if (ExtSelectClipRgn(hdc, clips[x], RGN_OR) == ERROR) abort(); // this should never ever happen! - } + } } /* Copy the new bitmap onto the screen in one go. @@ -2785,9 +4792,83 @@ HDCDrawPosition(HDC hdc, BOOLEAN repaint, Board board) */ oldBitmap = SelectObject(tmphdc, bufferBitmap); BitBlt(hdc, boardRect.left, boardRect.top, - boardRect.right - boardRect.left, - boardRect.bottom - boardRect.top, - tmphdc, boardRect.left, boardRect.top, SRCCOPY); + boardRect.right - boardRect.left, + boardRect.bottom - boardRect.top, + tmphdc, boardRect.left, boardRect.top, SRCCOPY); + if(saveDiagFlag) { + BITMAP b; int i, j=0, m, w, wb, fac=0; char pData[1000000]; + BITMAPINFOHEADER bih; int color[16], nrColors=0; + + GetObject(bufferBitmap, sizeof(b), &b); + if(b.bmWidthBytes*b.bmHeight <= 990000) { + bih.biSize = sizeof(BITMAPINFOHEADER); + bih.biWidth = b.bmWidth; + bih.biHeight = b.bmHeight; + bih.biPlanes = 1; + bih.biBitCount = b.bmBitsPixel; + bih.biCompression = 0; + bih.biSizeImage = b.bmWidthBytes*b.bmHeight; + bih.biXPelsPerMeter = 0; + bih.biYPelsPerMeter = 0; + bih.biClrUsed = 0; + bih.biClrImportant = 0; +// fprintf(diagFile, "t=%d\nw=%d\nh=%d\nB=%d\nP=%d\nX=%d\n", +// b.bmType, b.bmWidth, b.bmHeight, b.bmWidthBytes, b.bmPlanes, b.bmBitsPixel); + GetDIBits(tmphdc,bufferBitmap,0,b.bmHeight,pData,(BITMAPINFO*)&bih,DIB_RGB_COLORS); +// fprintf(diagFile, "%8x\n", (int) pData); + +#if 1 + wb = b.bmWidthBytes; + // count colors + for(i=0; i>2; i++) { + int k = ((int*) pData)[i]; + for(j=0; j= 16) break; + color[j] = k; + if(j >= nrColors) nrColors = j+1; + } + if(j<16) { // 16 colors is enough. Compress to 4 bits per pixel + INT p = 0; + for(i=0; i>2); w+=2) { + int k = ((int*) pData)[(wb*i>>2) + w]; + for(j=0; j>2) + w + 1]; + for(m=0; m>5)<<2; + } + // write BITMAPFILEHEADER + fprintf(diagFile, "BM"); + fputDW(diagFile, wb*(b.bmHeight - boardRect.top + OUTER_MARGIN)+0x36 + (fac?64:0)); + fputDW(diagFile, 0); + fputDW(diagFile, 0x36 + (fac?64:0)); + // write BITMAPINFOHEADER + fputDW(diagFile, 40); + fputDW(diagFile, b.bmWidth); + fputDW(diagFile, b.bmHeight - boardRect.top + OUTER_MARGIN); + if(fac) fputDW(diagFile, 0x040001); // planes and bits/pixel + else fputDW(diagFile, 0x200001); // planes and bits/pixel + fputDW(diagFile, 0); + fputDW(diagFile, 0); + fputDW(diagFile, 0); + fputDW(diagFile, 0); + fputDW(diagFile, 0); + fputDW(diagFile, 0); + // write color table + if(fac) + for(i=0; i<16; i++) fputDW(diagFile, color[i]); + // write bitmap data + for(i=0; ihf); + SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf); ExtTextOut(hdc, messageRect.left, messageRect.top, - ETO_CLIPPED|ETO_OPAQUE, - &messageRect, messageText, strlen(messageText), NULL); + ETO_CLIPPED|ETO_OPAQUE, + &messageRect, messageText, strlen(messageText), NULL); SelectObject(hdc, oldFont); DisplayBothClocks(); } @@ -2897,21 +4994,19 @@ SetupDropMenu(HMENU hmenu) for (i=0; i 0 || !appData.testLegality /*!!temp:*/ || (gameInfo.variant == VariantCrazyhouse - && !appData.icsActive); + && !appData.icsActive); ModifyMenu(hmenu, dropEnables[i].command, - MF_BYCOMMAND | (enable ? MF_ENABLED : MF_GRAYED) | MF_STRING, - dropEnables[i].command, item); + MF_BYCOMMAND | (enable ? MF_ENABLED : MF_GRAYED) | MF_STRING, + dropEnables[i].command, item); } } -static int fromX = -1, fromY = -1, toX, toY; - /* Event handler for mouse messages */ VOID MouseEvent(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) @@ -2920,112 +5015,170 @@ MouseEvent(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) POINT pt; static int recursive = 0; HMENU hmenu; +// BOOLEAN needsRedraw = FALSE; BOOLEAN saveAnimate; - static BOOLEAN sameAgain = FALSE; + BOOLEAN forceFullRepaint = IsFullRepaintPreferrable(); /* [AS] */ + static BOOLEAN sameAgain = FALSE, promotionChoice = FALSE; + ChessMove moveType; if (recursive) { if (message == WM_MBUTTONUP) { /* Hideous kludge to fool TrackPopupMenu into paying attention - to the middle button: we simulate pressing the left button too! - */ + to the middle button: we simulate pressing the left button too! + */ PostMessage(hwnd, WM_LBUTTONDOWN, wParam, lParam); PostMessage(hwnd, WM_LBUTTONUP, wParam, lParam); } return; } recursive++; - + pt.x = LOWORD(lParam); pt.y = HIWORD(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) { case WM_LBUTTONDOWN: + if(promotionChoice) { // we are waiting for a click to indicate promotion piece + promotionChoice = FALSE; // only one chance: if click not OK it is interpreted as cancel + if(appData.debugMode) fprintf(debugFP, "promotion click, x=%d, y=%d\n", x, y); + if(gameInfo.holdingsWidth && + (WhiteOnMove(currentMove) + ? x == BOARD_WIDTH-1 && y < gameInfo.holdingsSize && y > 0 + : x == 0 && y >= BOARD_HEIGHT - gameInfo.holdingsSize && y < BOARD_HEIGHT-1) ) { + // click in right holdings, for determining promotion piece + ChessSquare p = boards[currentMove][y][x]; + if(appData.debugMode) fprintf(debugFP, "square contains %d\n", (int)p); + if(p != EmptySquare) { + FinishMove(WhitePromotionQueen, fromX, fromY, toX, toY, ToLower(PieceToChar(p))); + fromX = fromY = -1; + break; + } + } + DrawPosition(FALSE, boards[currentMove]); + break; + } ErrorPopDown(); sameAgain = FALSE; if (y == -2) { /* Downclick vertically off board; check if on clock */ if (PtInRect((LPRECT) &whiteRect, pt)) { - if (gameMode == EditPosition) { - SetWhiteToPlayEvent(); - } else if (gameMode == IcsPlayingBlack || - gameMode == MachinePlaysWhite) { - CallFlagEvent(); - } + 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(); - } + 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((int) (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); - 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 */ - 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); - } - } - if (gotPremove) SetPremoveHighlights(fromX, fromY, toX, toY); - fromX = fromY = -1; - break; - } - ClearHighlights(); - DrawPosition(FALSE, NULL); + DrawPosition(forceFullRepaint || FALSE, NULL); + sameAgain = TRUE; + } 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; + /* [HGM] UserMoveEvent requires two calls now, + to make sure move is legal before showing promotion popup */ + moveType = UserMoveTest(fromX, fromY, toX, toY, NULLCHAR, FALSE); + if(moveType == AmbiguousMove) { /* [HGM] Edit-Position move executed */ + fromX = fromY = -1; + ClearHighlights(); + DrawPosition(FALSE, boards[currentMove]); + break; + } else + if(moveType != ImpossibleMove && moveType != Comment) { + /* [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! */ + if(gameInfo.variant == VariantSuper || gameInfo.variant == VariantGreat) + { ChessSquare p = boards[currentMove][fromY][fromX], q = boards[currentMove][toY][toX]; + // kludge to temporarily execute move on display, without promoting yet + promotionChoice = TRUE; + boards[currentMove][fromY][fromX] = EmptySquare; // move Pawn to 8th rank + boards[currentMove][toY][toX] = p; + DrawPosition(FALSE, boards[currentMove]); + boards[currentMove][fromY][fromX] = p; // take back, but display stays + boards[currentMove][toY][toX] = q; + DisplayMessage("Select piece from holdings", ""); + } else + PromotionPopup(hwnd); + goto noClear; + } else { // not a promotion. Move can be illegal if testLegality off, and should be made then. + 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); + } + } + fromX = fromY = -1; + noClear: + break; + } + if (gotPremove && moveType != Comment) { + SetPremoveHighlights(fromX, fromY, toX, toY); +// DrawPosition(forceFullRepaint || FALSE, NULL); + } else ClearHighlights(); + fromX = fromY = -1; + DrawPosition(forceFullRepaint || FALSE, NULL); + if(moveType != Comment) break; } /* First downclick, or restart on a square with same color piece */ if (!frozen && OKToStartUserMove(x, y)) { @@ -3040,6 +5193,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; @@ -3050,74 +5204,123 @@ MouseEvent(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) dragInfo.from.x = dragInfo.from.y = -1; /* Upclick on same square */ if (sameAgain) { - /* Clicked same square twice: abort click-click move */ - fromX = fromY = -1; - gotPremove = 0; - ClearPremoveHighlights(); + /* Clicked same square twice: abort click-click move */ + fromX = fromY = -1; + gotPremove = 0; + ClearPremoveHighlights(); } else { - /* First square clicked: start click-click move */ - SetHighlights(fromX, fromY, -1, -1); + /* First square clicked: start click-click move */ + SetHighlights(fromX, fromY, -1, -1); } - DrawPosition(FALSE, NULL); + 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, TRUE); + if(moveType == AmbiguousMove) { /* [HGM] Edit-Position move executed */ + fromX = fromY = -1; + ClearHighlights(); + DrawPosition(FALSE, boards[currentMove]); + appData.animate = saveAnimate; + break; + } else + 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); + if(gameInfo.variant == VariantSuper || gameInfo.variant == VariantGreat) + { ChessSquare p = boards[currentMove][fromY][fromX], q = boards[currentMove][toY][toX]; + // kludge to temporarily execute move on display, wthout promotng yet + promotionChoice = TRUE; + boards[currentMove][fromY][fromX] = EmptySquare; // move Pawn to 8th rank + boards[currentMove][toY][toX] = p; + DrawPosition(FALSE, boards[currentMove]); + boards[currentMove][fromY][fromX] = p; // take back, but display stays + boards[currentMove][toY][toX] = q; + appData.animate = saveAnimate; + DisplayMessage("Select piece from holdings", ""); + break; + } else + PromotionPopup(hwnd); /* [HGM] Popup now calls FinishMove */ + } else { + if(saveAnimate /* ^$!%@#$!$ */ && gameInfo.variant == VariantAtomic + && (boards[currentMove][toY][toX] != EmptySquare || + moveType == WhiteCapturesEnPassant || + moveType == BlackCapturesEnPassant ) ) + AnimateAtomicCapture(fromX, fromY, toX, toY, 20); + FinishMove(moveType, fromX, fromY, toX, toY, NULLCHAR); + } } if (gotPremove) SetPremoveHighlights(fromX, fromY, toX, toY); appData.animate = saveAnimate; fromX = fromY = -1; if (appData.highlightDragging && !appData.highlightLastMove) { - ClearHighlights(); + ClearHighlights(); } if (appData.animate || appData.animateDragging || - appData.highlightDragging || gotPremove) { - DrawPosition(FALSE, NULL); + appData.highlightDragging || gotPremove) { + DrawPosition(forceFullRepaint || FALSE, NULL); } } - dragInfo.start.x = dragInfo.start.y = -1; + dragInfo.start.x = dragInfo.start.y = -1; dragInfo.pos = dragInfo.lastpos = dragInfo.start; break; case WM_MOUSEMOVE: if ((appData.animateDragging || appData.highlightDragging) - && (wParam & MK_LBUTTON) - && dragInfo.from.x >= 0) { + && (wParam & MK_LBUTTON) + && 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; + dragInfo.pos = pt; } if (appData.highlightDragging) { - SetHighlights(fromX, fromY, x, y); + 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; - case WM_MOUSEWHEEL: - /* Mouse Wheel is being rolled forward - * Play moves forward - */ - if((short)HIWORD(wParam) > 0 ) ForwardEvent(); - /* Mouse Wheel is being rolled backward - * Play moves backward - */ - if((short)HIWORD(wParam) < 0 ) BackwardEvent(); - break; + + case WM_MOUSEWHEEL: // [DM] + { static int lastDir = 0; // [HGM] build in some hysteresis to avoid spurious events + /* Mouse Wheel is being rolled forward + * Play moves forward + */ + if((short)HIWORD(wParam) > 0 && currentMove < forwardMostMove) + { if(lastDir == 1) ForwardEvent(); else lastDir = 1; } // [HGM] suppress first event in direction + /* Mouse Wheel is being rolled backward + * Play moves backward + */ + if((short)HIWORD(wParam) < 0 && currentMove > backwardMostMove) + { if(lastDir == -1) BackwardEvent(); else lastDir = -1; } + } + break; + case WM_MBUTTONDOWN: case WM_RBUTTONDOWN: ErrorPopDown(); @@ -3130,6 +5333,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) { @@ -3139,26 +5350,29 @@ MouseEvent(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) fromX = x; fromY = y; if (message == WM_MBUTTONDOWN) { - buttonCount = 3; /* even if system didn't think so */ - if (wParam & MK_SHIFT) - MenuPopup(hwnd, pt, LoadMenu(hInst, "BlackPieceMenu"), -1); - else - MenuPopup(hwnd, pt, LoadMenu(hInst, "WhitePieceMenu"), -1); + buttonCount = 3; /* even if system didn't think so */ + if (wParam & MK_SHIFT) + MenuPopup(hwnd, pt, LoadMenu(hInst, "BlackPieceMenu"), -1); + else + MenuPopup(hwnd, pt, LoadMenu(hInst, "WhitePieceMenu"), -1); } else { /* message == WM_RBUTTONDOWN */ #if 0 - if (buttonCount == 3) { - if (wParam & MK_SHIFT) - MenuPopup(hwnd, pt, LoadMenu(hInst, "WhitePieceMenu"), -1); - else - MenuPopup(hwnd, pt, LoadMenu(hInst, "BlackPieceMenu"), -1); - } else { - MenuPopup(hwnd, pt, LoadMenu(hInst, "PieceMenu"), -1); - } + if (buttonCount == 3) { + if (wParam & MK_SHIFT) + MenuPopup(hwnd, pt, LoadMenu(hInst, "WhitePieceMenu"), -1); + else + MenuPopup(hwnd, pt, LoadMenu(hInst, "BlackPieceMenu"), -1); + } else { + MenuPopup(hwnd, pt, LoadMenu(hInst, "PieceMenu"), -1); + } #else - /* 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); + /* 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. */ + if(gameInfo.variant != VariantShogi) + MenuPopup(hwnd, pt, LoadMenu(hInst, "PieceMenu"), -1); + else + MenuPopup(hwnd, pt, LoadMenu(hInst, "ShogiPieceMenu"), -1); #endif } break; @@ -3168,8 +5382,8 @@ MouseEvent(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) case MachinePlaysWhite: case MachinePlaysBlack: if (appData.testLegality && - gameInfo.variant != VariantBughouse && - gameInfo.variant != VariantCrazyhouse) break; + gameInfo.variant != VariantBughouse && + gameInfo.variant != VariantCrazyhouse) break; if (x < 0 || y < 0) break; fromX = x; fromY = y; @@ -3212,31 +5426,16 @@ ButtonProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) case '\r': SendMessage(hwndMain, WM_COMMAND, MAKEWPARAM(buttonDesc[i].id, 0), 0); return TRUE; - case '\t': - if (appData.icsActive) { - if (GetKeyState(VK_SHIFT) < 0) { - /* shifted */ - HWND h = GetDlgItem(hwndConsole, OPT_ConsoleInput); - if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE); - SetFocus(h); - } else { - /* unshifted */ - HWND h = GetDlgItem(hwndConsole, OPT_ConsoleText); - if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE); - SetFocus(h); - } - return TRUE; - } - break; default: - if (appData.icsActive) { + if (appData.icsActive && (isalpha((char)wParam) || wParam == '0')) { + // [HGM] movenum: only letters or leading zero should go to ICS input HWND h = GetDlgItem(hwndConsole, OPT_ConsoleInput); - if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE); - SetFocus(h); - SendMessage(h, WM_CHAR, wParam, lParam); - return TRUE; + if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE); + SetFocus(h); + SendMessage(h, WM_CHAR, wParam, lParam); + return TRUE; } else if (isalpha((char)wParam) || isdigit((char)wParam)){ - PopUpMoveDialog((char)wParam); + PopUpMoveDialog((char)wParam); } break; } @@ -3255,10 +5454,39 @@ Promotion(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam) case WM_INITDIALOG: /* message: initialize dialog box */ /* Center the dialog over the application window */ CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER)); - ShowWindow(GetDlgItem(hDlg, PB_King), + ShowWindow(GetDlgItem(hDlg, PB_King), (!appData.testLegality || gameInfo.variant == VariantSuicide || - gameInfo.variant == VariantGiveaway) ? - SW_SHOW : SW_HIDE); + gameInfo.variant == VariantGiveaway || gameInfo.variant == VariantSuper ) ? + SW_SHOW : SW_HIDE); + /* [HGM] Only allow C & A promotions if these pieces are defined */ + ShowWindow(GetDlgItem(hDlg, PB_Archbishop), + ((PieceToChar(WhiteAngel) >= 'A' && + PieceToChar(WhiteAngel) != '~') || + (PieceToChar(BlackAngel) >= 'A' && + PieceToChar(BlackAngel) != '~') ) ? + SW_SHOW : SW_HIDE); + ShowWindow(GetDlgItem(hDlg, PB_Chancellor), + ((PieceToChar(WhiteMarshall) >= 'A' && + PieceToChar(WhiteMarshall) != '~') || + (PieceToChar(BlackMarshall) >= 'A' && + 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); + ShowWindow(GetDlgItem(hDlg, IDC_Yes), + gameInfo.variant == VariantShogi ? + SW_SHOW : SW_HIDE); + ShowWindow(GetDlgItem(hDlg, IDC_No), + gameInfo.variant == VariantShogi ? + SW_SHOW : SW_HIDE); + ShowWindow(GetDlgItem(hDlg, IDC_Centaur), + gameInfo.variant == VariantSuper ? + SW_SHOW : SW_HIDE); return TRUE; case WM_COMMAND: /* message: received a command */ @@ -3269,25 +5497,35 @@ Promotion(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam) DrawPosition(FALSE, NULL); return TRUE; case PB_King: - promoChar = 'k'; + promoChar = gameInfo.variant == VariantSuper ? PieceToChar(BlackSilver) : 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(BlackAngel); 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); @@ -3313,7 +5551,8 @@ PromotionPopup(HWND hwnd) VOID ToggleShowThinking() { - ShowThinkingEvent(!appData.showThinking); + appData.showThinking = !appData.showThinking; + ShowThinkingEvent(); } VOID @@ -3322,10 +5561,10 @@ LoadGameDialog(HWND hwnd, char* title) UINT number = 0; FILE *f; char fileTitle[MSG_SIZ]; - f = OpenFileDialog(hwnd, FALSE, "", - appData.oldSaveStyle ? "gam" : "pgn", - GAME_FILT, - title, &number, fileTitle, NULL); + f = OpenFileDialog(hwnd, "rb", "", + appData.oldSaveStyle ? "gam" : "pgn", + GAME_FILT, + title, &number, fileTitle, NULL); if (f != NULL) { cmailMsgLoaded = FALSE; if (number == 0) { @@ -3334,7 +5573,7 @@ LoadGameDialog(HWND hwnd, char* title) DisplayError("Cannot build game list", error); } else if (!ListEmpty(&gameList) && ((ListGame *) gameList.tailPred)->number > 1) { - GameListPopUp(f, fileTitle); + GameListPopUp(f, fileTitle); return; } GameListDestroy(); @@ -3364,16 +5603,16 @@ ChangedConsoleFont() cfmt.yHeight = (int)(f->mfp.pointSize * 20.0 + 0.5); cfmt.bCharSet = f->lf.lfCharSet; cfmt.bPitchAndFamily = f->lf.lfPitchAndFamily; - SendMessage(hText, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &cfmt); - SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &cfmt); + SendMessage(hText, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &cfmt); + SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &cfmt); /* Why are the following seemingly needed too? */ - SendMessage(hText, EM_SETCHARFORMAT, SCF_DEFAULT, (LPARAM) &cfmt); - SendMessage(hInput, EM_SETCHARFORMAT, SCF_DEFAULT, (LPARAM) &cfmt); + SendMessage(hText, EM_SETCHARFORMAT, SCF_DEFAULT, (LPARAM) &cfmt); + SendMessage(hInput, EM_SETCHARFORMAT, SCF_DEFAULT, (LPARAM) &cfmt); SendMessage(hText, EM_EXGETSEL, 0, (LPARAM)&sel); tmpsel.cpMin = 0; tmpsel.cpMax = -1; /*999999?*/ SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&tmpsel); - SendMessage(hText, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cfmt); + SendMessage(hText, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cfmt); /* Trying putting this here too. It still seems to tickle a RichEdit * bug: sometimes RichEdit indents the first line of a paragraph too. */ @@ -3401,6 +5640,8 @@ WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) FILE *f; UINT number; char fileTitle[MSG_SIZ]; + char buf[MSG_SIZ]; + static SnapData sd; switch (message) { @@ -3428,30 +5669,25 @@ WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) MouseEvent(hwnd, message, wParam, lParam); break; + JAWS_KB_NAVIGATION + case WM_CHAR: - - if (appData.icsActive) { - if (wParam == '\t') { - if (GetKeyState(VK_SHIFT) < 0) { - /* shifted */ - HWND h = GetDlgItem(hwndConsole, OPT_ConsoleInput); - if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE); - SetFocus(h); - } else { - /* unshifted */ - HWND h = GetDlgItem(hwndConsole, OPT_ConsoleText); - if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE); - SetFocus(h); - } - } else { - HWND h = GetDlgItem(hwndConsole, OPT_ConsoleInput); - if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE); - SetFocus(h); - SendMessage(h, message, wParam, lParam); - } - } else if (isalpha((char)wParam) || isdigit((char)wParam)) { - PopUpMoveDialog((char)wParam); + + JAWS_ALT_INTERCEPT + + if (appData.icsActive && (char)wParam > ' ' && !((char)wParam >= '1' && (char)wParam <= '9')) { + // [HGM] movenum: for non-zero digits we always do type-in dialog + HWND h = GetDlgItem(hwndConsole, OPT_ConsoleInput); + if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE); + SetFocus(h); + SendMessage(h, message, wParam, lParam); + } else if(lParam != KF_REPEAT) { + if (isalpha((char)wParam) || isdigit((char)wParam)) { + PopUpMoveDialog((char)wParam); + } else if((char)wParam == 003) CopyGameToClipboard(); + else if((char)wParam == 026) PasteGameOrFENFromClipboard(); } + break; case WM_PALETTECHANGED: @@ -3461,7 +5697,7 @@ WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) SelectPalette(hdc, hPal, TRUE); nnew = RealizePalette(hdc); if (nnew > 0) { - paletteChanged = TRUE; + paletteChanged = TRUE; #if 0 UpdateColors(hdc); #else @@ -3480,7 +5716,7 @@ WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) SelectPalette(hdc, hPal, FALSE); nnew = RealizePalette(hdc); if (nnew > 0) { - InvalidateRect(hwnd, &boardRect, FALSE); + InvalidateRect(hwnd, &boardRect, FALSE); } ReleaseDC(hwnd, hdc); return TRUE; @@ -3495,6 +5731,18 @@ WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) case IDM_NewGame: ResetGameEvent(); AnalysisPopDown(); + SAY("new game enter a move to play against the computer with white"); + break; + + case IDM_NewGameFRC: + if( NewGameFRC() == 0 ) { + ResetGameEvent(); + AnalysisPopDown(); + } + break; + + case IDM_NewVariant: + NewVariantPopup(hwnd); break; case IDM_LoadGame: @@ -3518,12 +5766,12 @@ WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) Reset(FALSE, TRUE); } number = 1; - f = OpenFileDialog(hwnd, FALSE, "", - appData.oldSaveStyle ? "pos" : "fen", - POSITION_FILT, - "Load Position from File", &number, fileTitle, NULL); + f = OpenFileDialog(hwnd, "rb", "", + appData.oldSaveStyle ? "pos" : "fen", + POSITION_FILT, + "Load Position from File", &number, fileTitle, NULL); if (f != NULL) { - LoadPosition(f, number, fileTitle); + LoadPosition(f, number, fileTitle); } break; @@ -3541,23 +5789,34 @@ WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) case IDM_SaveGame: defName = DefaultFileName(appData.oldSaveStyle ? "gam" : "pgn"); - f = OpenFileDialog(hwnd, TRUE, defName, - appData.oldSaveStyle ? "gam" : "pgn", - GAME_FILT, - "Save Game to File", NULL, fileTitle, NULL); + f = OpenFileDialog(hwnd, "a", defName, + appData.oldSaveStyle ? "gam" : "pgn", + GAME_FILT, + "Save Game to File", NULL, fileTitle, NULL); if (f != NULL) { - SaveGame(f, 0, ""); + SaveGame(f, 0, ""); } break; case IDM_SavePosition: defName = DefaultFileName(appData.oldSaveStyle ? "pos" : "fen"); - f = OpenFileDialog(hwnd, TRUE, defName, - appData.oldSaveStyle ? "pos" : "fen", - POSITION_FILT, - "Save Position to File", NULL, fileTitle, NULL); + f = OpenFileDialog(hwnd, "a", defName, + appData.oldSaveStyle ? "pos" : "fen", + POSITION_FILT, + "Save Position to File", NULL, fileTitle, NULL); + if (f != NULL) { + SavePosition(f, 0, ""); + } + break; + + case IDM_SaveDiagram: + defName = "diagram"; + f = OpenFileDialog(hwnd, "wb", defName, + "bmp", + DIAGRAM_FILT, + "Save Diagram to File", NULL, fileTitle, NULL); if (f != NULL) { - SavePosition(f, 0, ""); + SaveDiagram(f); } break; @@ -3569,6 +5828,68 @@ WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) PasteGameFromClipboard(); 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(); + SetFocus(hwndMain); + } + 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_NewChat: + ChatPopUp(); + break; + case IDM_CopyPosition: CopyFENToClipboard(); break; @@ -3600,11 +5921,12 @@ WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) * refresh the tags dialog only if it's visible */ if (gameMode == MachinePlaysWhite && IsWindowVisible(editTagsDialog)) { - char *tags; - tags = PGNTags(&gameInfo); - TagsPopUp(tags, CmailMsg()); - free(tags); + char *tags; + tags = PGNTags(&gameInfo); + TagsPopUp(tags, CmailMsg()); + free(tags); } + SAY("computer starts playing white"); break; case IDM_MachineBlack: @@ -3613,11 +5935,12 @@ WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) * refresh the tags dialog only if it's visible */ if (gameMode == MachinePlaysBlack && IsWindowVisible(editTagsDialog)) { - char *tags; - tags = PGNTags(&gameInfo); - TagsPopUp(tags, CmailMsg()); - free(tags); + char *tags; + tags = PGNTags(&gameInfo); + TagsPopUp(tags, CmailMsg()); + free(tags); } + SAY("computer starts playing black"); break; case IDM_TwoMachines: @@ -3626,21 +5949,47 @@ WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) * refresh the tags dialog only if it's visible */ if (gameMode == TwoMachinesPlay && IsWindowVisible(editTagsDialog)) { - char *tags; - tags = PGNTags(&gameInfo); - TagsPopUp(tags, CmailMsg()); - free(tags); + char *tags; + tags = PGNTags(&gameInfo); + TagsPopUp(tags, CmailMsg()); + free(tags); } + SAY("programs start playing each other"); break; case IDM_AnalysisMode: if (!first.analysisSupport) { - char buf[MSG_SIZ]; sprintf(buf, "%s does not support analysis", first.tidy); DisplayError(buf, 0); } else { - if (!appData.showThinking) ToggleShowThinking(); - AnalyzeModeEvent(); + SAY("analyzing current position"); + /* [DM] icsEngineAnlyze [HGM] Why is this front-end??? */ + if (appData.icsActive) { + if (gameMode != IcsObserving) { + sprintf(buf, "You are not observing a game"); + DisplayError(buf, 0); + /* secure check */ + if (appData.icsEngineAnalyze) { + if (appData.debugMode) + fprintf(debugFP, "Found unexpected active ICS engine analyze \n"); + ExitAnalyzeMode(); + ModeHighlight(); + break; + } + break; + } else { + /* if enable, user want disable icsEngineAnalyze */ + if (appData.icsEngineAnalyze) { + ExitAnalyzeMode(); + ModeHighlight(); + break; + } + appData.icsEngineAnalyze = TRUE; + if (appData.debugMode) fprintf(debugFP, "ICS engine analyze starting...\n"); + } + } + if (!appData.showThinking) ToggleShowThinking(); + AnalyzeModeEvent(); } break; @@ -3650,10 +5999,10 @@ WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) sprintf(buf, "%s does not support analysis", first.tidy); DisplayError(buf, 0); } else { - if (!appData.showThinking) ToggleShowThinking(); - AnalyzeFileEvent(); - LoadGameDialog(hwnd, "Analyze Game from File"); - AnalysisPeriodicEvent(1); + if (!appData.showThinking) ToggleShowThinking(); + AnalyzeFileEvent(); + LoadGameDialog(hwnd, "Analyze Game from File"); + AnalysisPeriodicEvent(1); } break; @@ -3663,10 +6012,12 @@ WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) case IDM_EditGame: EditGameEvent(); + SAY("edit game"); break; case IDM_EditPosition: EditPositionEvent(); + SAY("to set up a position type a FEN"); break; case IDM_Training: @@ -3683,9 +6034,9 @@ WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) case IDM_EditComment: if (commentDialogUp && editComment) { - CommentPopDown(); + CommentPopDown(); } else { - EditCommentEvent(); + EditCommentEvent(); } break; @@ -3737,11 +6088,17 @@ WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) PopUpMoveDialog('\000'); break; + case IDM_TypeInName: + PopUpNameDialog('\000'); + break; + case IDM_Backward: BackwardEvent(); SetFocus(hwndMain); break; + JAWS_MENU_ITEMS + case IDM_Forward: ForwardEvent(); SetFocus(hwndMain); @@ -3778,14 +6135,43 @@ WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) DrawPosition(FALSE, NULL); break; + case IDM_FlipClock: + flipClock = !flipClock; + DisplayBothClocks(); + DrawPosition(FALSE, NULL); + break; + + case IDM_MuteSounds: + mute = !mute; // [HGM] mute: keep track of global muting variable + CheckMenuItem(GetMenu(hwndMain),IDM_MuteSounds, + MF_BYCOMMAND|(mute?MF_CHECKED:MF_UNCHECKED)); + break; + case IDM_GeneralOptions: GeneralOptionsPopup(hwnd); + DrawPosition(TRUE, NULL); break; case IDM_BoardOptions: BoardOptionsPopup(hwnd); break; + case IDM_EnginePlayOptions: + EnginePlayOptionsPopup(hwnd); + break; + + case IDM_Engine1Options: + EngineOptionsPopup(hwnd, &first); + break; + + case IDM_Engine2Options: + EngineOptionsPopup(hwnd, &second); + break; + + case IDM_OptionsUCI: + UciOptionsPopup(hwnd); + break; + case IDM_IcsOptions: IcsOptionsPopup(hwnd); break; @@ -3821,8 +6207,8 @@ WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) case IDM_SaveSettingsOnExit: saveSettingsOnExit = !saveSettingsOnExit; (void) CheckMenuItem(GetMenu(hwndMain), IDM_SaveSettingsOnExit, - MF_BYCOMMAND|(saveSettingsOnExit ? - MF_CHECKED : MF_UNCHECKED)); + MF_BYCOMMAND|(saveSettingsOnExit ? + MF_CHECKED : MF_UNCHECKED)); break; case IDM_Hint: @@ -3840,57 +6226,59 @@ WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) case IDM_Debug: appData.debugMode = !appData.debugMode; if (appData.debugMode) { - char dir[MSG_SIZ]; - GetCurrentDirectory(MSG_SIZ, dir); - SetCurrentDirectory(installDir); - debugFP = fopen("WinBoard.debug", "w"); + char dir[MSG_SIZ]; + GetCurrentDirectory(MSG_SIZ, dir); + SetCurrentDirectory(installDir); + debugFP = fopen(appData.nameOfDebugFile, "w"); SetCurrentDirectory(dir); setbuf(debugFP, NULL); } else { - fclose(debugFP); + fclose(debugFP); debugFP = NULL; } break; case IDM_HELPCONTENTS: - if (!WinHelp (hwnd, "winboard.hlp", HELP_KEY,(DWORD)(LPSTR)"CONTENTS")) { - MessageBox (GetFocus(), - "Unable to activate help", - szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND); + if (!MyHelp (hwnd, "winboard.hlp", HELP_KEY,(DWORD)(LPSTR)"CONTENTS") && + !HtmlHelp(hwnd, "winboard.chm", 0, 0) ) { + MessageBox (GetFocus(), + "Unable to activate help", + szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND); } break; case IDM_HELPSEARCH: - if (!WinHelp(hwnd, "winboard.hlp", HELP_PARTIALKEY, (DWORD)(LPSTR)"")) { - MessageBox (GetFocus(), - "Unable to activate help", - szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND); + if (!MyHelp (hwnd, "winboard.hlp", HELP_PARTIALKEY, (DWORD)(LPSTR)"") && + !HtmlHelp(hwnd, "winboard.chm", 0, 0) ) { + MessageBox (GetFocus(), + "Unable to activate help", + szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND); } break; case IDM_HELPHELP: if(!WinHelp(hwnd, (LPSTR)NULL, HELP_HELPONHELP, 0)) { - MessageBox (GetFocus(), - "Unable to activate help", - szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND); + MessageBox (GetFocus(), + "Unable to activate help", + szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND); } break; case IDM_ABOUT: lpProc = MakeProcInstance((FARPROC)About, hInst); - DialogBox(hInst, - (gameInfo.event && strcmp(gameInfo.event, "Easter Egg Hunt") == 0) ? - "AboutBox2" : "AboutBox", hwnd, (DLGPROC)lpProc); + DialogBox(hInst, + (gameInfo.event && strcmp(gameInfo.event, "Easter Egg Hunt") == 0) ? + "AboutBox2" : "AboutBox", hwnd, (DLGPROC)lpProc); FreeProcInstance(lpProc); break; case IDM_DirectCommand1: AskQuestionEvent("Direct Command", - "Send to chess program:", "", "1"); + "Send to chess program:", "", "1"); break; case IDM_DirectCommand2: AskQuestionEvent("Direct Command", - "Send to second chess program:", "", "2"); + "Send to second chess program:", "", "2"); break; case EP_WhitePawn: @@ -3918,6 +6306,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(WhiteAngel, 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; @@ -3948,6 +6366,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(BlackAngel, 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; @@ -3973,6 +6421,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; @@ -4016,12 +6474,12 @@ WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) AutoPlayGameLoop(); /* call into back end */ break; case ANALYSIS_TIMER_ID: - if ((gameMode == AnalyzeMode || gameMode == AnalyzeFile) && - appData.periodicUpdates) { - AnalysisPeriodicEvent(0); + if ((gameMode == AnalyzeMode || gameMode == AnalyzeFile + || appData.icsEngineAnalyze) && appData.periodicUpdates) { + AnalysisPeriodicEvent(0); } else { - KillTimer(hwnd, analysisTimerEvent); - analysisTimerEvent = 0; + KillTimer(hwnd, analysisTimerEvent); + analysisTimerEvent = 0; } break; case DELAYED_TIMER_ID: @@ -4036,20 +6494,56 @@ 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 ); //[HGM] sticky: in XP this returned new position, not old + rcMain.left = boardX; // replace by these 4 lines to reconstruct old rect + rcMain.right = boardX + winWidth; + rcMain.top = boardY; + rcMain.bottom = boardY + winHeight; + + ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, moveHistoryDialog, &wpMoveHistory ); + ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, evalGraphDialog, &wpEvalGraph ); + ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, engineOutputDialog, &wpEngineOutput ); + ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, gameListDialog, &wpGameList ); + ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, hwndConsole, &wpConsole ); + boardX = lpwp->x; + boardY = lpwp->y; + } + } + break; + + /* [AS] Snapping */ case WM_ENTERSIZEMOVE: + if(appData.debugMode) { fprintf(debugFP, "size-move\n"); } if (hwnd == hwndMain) { doingSizing = TRUE; lastSizing = 0; } + return OnEnterSizeMove( &sd, hwnd, wParam, lParam ); break; case WM_SIZING: + if(appData.debugMode) { fprintf(debugFP, "sizing\n"); } if (hwnd == hwndMain) { lastSizing = wParam; } break; + case WM_MOVING: + if(appData.debugMode) { fprintf(debugFP, "moving\n"); } + return OnMoving( &sd, hwnd, wParam, lParam ); + case WM_EXITSIZEMOVE: + if(appData.debugMode) { fprintf(debugFP, "exit size-move, size = %d\n", squareSize); } if (hwnd == hwndMain) { RECT client; doingSizing = FALSE; @@ -4057,7 +6551,9 @@ WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) GetClientRect(hwnd, &client); ResizeBoard(client.right, client.bottom, lastSizing); lastSizing = 0; + if(appData.debugMode) { fprintf(debugFP, "square size = %d\n", squareSize); } } + return OnExitSizeMove( &sd, hwnd, wParam, lParam ); break; case WM_DESTROY: /* message: window being destroyed */ @@ -4070,7 +6566,7 @@ WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) } break; - default: /* Passes it on if unprocessed */ + default: /* Passes it on if unprocessed */ return (DefWindowProc(hwnd, message, wParam, lParam)); } return 0; @@ -4102,8 +6598,8 @@ mysrandom(unsigned int seed) } -/* - * returns TRUE if user selects a different color, FALSE otherwise +/* + * returns TRUE if user selects a different color, FALSE otherwise */ BOOL @@ -4209,8 +6705,11 @@ BOOLEAN MyPlaySound(MySound *ms) { BOOLEAN ok = FALSE; + + if(mute) return TRUE; // [HGM] mute: suppress all sound play when muted switch (ms->name[0]) { case NULLCHAR: + if(appData.debugMode) fprintf(debugFP, "silence\n"); /* Silence */ ok = TRUE; break; @@ -4221,7 +6720,7 @@ MyPlaySound(MySound *ms) ok = PlaySound(ms->name + 1, NULL, SND_ALIAS|SND_ASYNC); } if (!ok) ok = MessageBeep(MB_OK); - break; + break; case '!': /* Builtin wave resource, or "!" alone for silence */ if (ms->name[1]) { @@ -4303,9 +6802,9 @@ OpenFileHook(HWND hdlg, UINT uiMsg, WPARAM wParam, LPARAM lParam) FILE * -OpenFileDialog(HWND hwnd, BOOL write, char *defName, char *defExt, - char *nameFilt, char *dlgTitle, UINT *number, - char fileTitle[MSG_SIZ], char fileName[MSG_SIZ]) +OpenFileDialog(HWND hwnd, char *write, char *defName, char *defExt, // [HGM] diag: type of 'write' now string + char *nameFilt, char *dlgTitle, UINT *number, + char fileTitle[MSG_SIZ], char fileName[MSG_SIZ]) { OPENFILENAME openFileName; char buf1[MSG_SIZ]; @@ -4334,8 +6833,8 @@ OpenFileDialog(HWND hwnd, BOOL write, char *defName, char *defExt, openFileName.nMaxFileTitle = fileTitle ? MSG_SIZ : 0; openFileName.lpstrInitialDir = NULL; openFileName.lpstrTitle = dlgTitle; - openFileName.Flags = OFN_PATHMUSTEXIST | OFN_HIDEREADONLY - | (write ? 0 : OFN_FILEMUSTEXIST) + openFileName.Flags = OFN_PATHMUSTEXIST | OFN_HIDEREADONLY + | (write[0] != 'r' ? 0 : OFN_FILEMUSTEXIST) | (number ? OFN_ENABLETEMPLATE | OFN_ENABLEHOOK: 0) | (oldDialog ? 0 : OFN_EXPLORER); openFileName.nFileOffset = 0; @@ -4346,13 +6845,13 @@ OpenFileDialog(HWND hwnd, BOOL write, char *defName, char *defExt, (LPOFNHOOKPROC) OldOpenFileHook : (LPOFNHOOKPROC) OpenFileHook; openFileName.lpTemplateName = (LPSTR)(oldDialog ? 1536 : DLG_IndexNumber); - if (write ? GetSaveFileName(&openFileName) : - GetOpenFileName(&openFileName)) { + if (write[0] != 'r' ? GetSaveFileName(&openFileName) : + GetOpenFileName(&openFileName)) { /* open the file */ - f = fopen(openFileName.lpstrFile, write ? "a" : "rb"); + f = fopen(openFileName.lpstrFile, write); if (f == NULL) { MessageBox(hwnd, "File open failed", NULL, - MB_OK|MB_ICONEXCLAMATION); + MB_OK|MB_ICONEXCLAMATION); return NULL; } } else { @@ -4368,7 +6867,7 @@ OpenFileDialog(HWND hwnd, BOOL write, char *defName, char *defExt, VOID APIENTRY MenuPopup(HWND hwnd, POINT pt, HMENU hmenu, UINT def) { - HMENU hmenuTrackPopup; /* floating pop-up menu */ + HMENU hmenuTrackPopup; /* floating pop-up menu */ /* * Get the first pop-up menu in the menu template. This is the @@ -4386,12 +6885,12 @@ MenuPopup(HWND hwnd, POINT pt, HMENU hmenu, UINT def) /* Draw and track the floating pop-up menu. */ TrackPopupMenu(hmenuTrackPopup, TPM_CENTERALIGN | TPM_RIGHTBUTTON, - pt.x, pt.y, 0, hwnd, NULL); + pt.x, pt.y, 0, hwnd, NULL); /* Destroy the menu.*/ DestroyMenu(hmenu); } - + typedef struct { HWND hDlg, hText; int sizeX, sizeY, newSizeX, newSizeY; @@ -4410,7 +6909,7 @@ ResizeEditPlusButtonsCallback(HWND hChild, LPARAM lparam) pt.x = rect.left + (cl->newSizeX - cl->sizeX)/2; pt.y = rect.top + cl->newSizeY - cl->sizeY; ScreenToClient(cl->hDlg, &pt); - cl->hdwp = DeferWindowPos(cl->hdwp, hChild, NULL, + cl->hdwp = DeferWindowPos(cl->hdwp, hChild, NULL, pt.x, pt.y, rect.right - rect.left, rect.bottom - rect.top, SWP_NOZORDER); return TRUE; } @@ -4423,10 +6922,10 @@ ResizeEditPlusButtons(HWND hDlg, HWND hText, int sizeX, int sizeY, int newSizeX, RECT rectText; int newTextHeight, newTextWidth; ResizeEditPlusButtonsClosure cl; - + /*if (IsIconic(hDlg)) return;*/ if (newSizeX == sizeX && newSizeY == sizeY) return; - + cl.hdwp = BeginDeferWindowPos(8); GetWindowRect(hText, &rectText); /* gives screen coords */ @@ -4436,7 +6935,7 @@ ResizeEditPlusButtons(HWND hDlg, HWND hText, int sizeX, int sizeY, int newSizeX, newSizeY += -newTextHeight; newTextHeight = 0; } - cl.hdwp = DeferWindowPos(cl.hdwp, hText, NULL, 0, 0, + cl.hdwp = DeferWindowPos(cl.hdwp, hText, NULL, 0, 0, newTextWidth, newTextHeight, SWP_NOZORDER|SWP_NOMOVE); cl.hDlg = hDlg; @@ -4450,8 +6949,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; @@ -4477,22 +6975,34 @@ BOOL CenterWindow (HWND hwndChild, HWND hwndParent) /* Calculate new X position, then adjust for screen */ xNew = rParent.left + ((wParent - wChild) /2); if (xNew < 0) { - xNew = 0; + xNew = 0; } else if ((xNew+wChild) > wScreen) { - xNew = wScreen - wChild; + xNew = wScreen - wChild; } /* 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; + yNew = 0; } else if ((yNew+hChild) > hScreen) { - yNew = hScreen - hChild; + yNew = hScreen - hChild; } /* Set it, and return */ return SetWindowPos (hwndChild, NULL, - xNew, yNew, 0, 0, SWP_NOSIZE | SWP_NOZORDER); + xNew, yNew, 0, 0, SWP_NOSIZE | SWP_NOZORDER); +} + +/* Center one window over another */ +BOOL CenterWindow (HWND hwndChild, HWND hwndParent) +{ + return CenterWindowEx( hwndChild, hwndParent, 0 ); } /*---------------------------------------------------------------------------*\ @@ -4547,7 +7057,7 @@ SetStartupDialogEnables(HWND hDlg) { EnableWindow(GetDlgItem(hDlg, OPT_ChessEngineName), IsDlgButtonChecked(hDlg, OPT_ChessEngine) || - appData.zippyPlay && IsDlgButtonChecked(hDlg, OPT_ChessServer)); + (appData.zippyPlay && IsDlgButtonChecked(hDlg, OPT_ChessServer))); EnableWindow(GetDlgItem(hDlg, OPT_SecondChessEngineName), IsDlgButtonChecked(hDlg, OPT_ChessEngine)); EnableWindow(GetDlgItem(hDlg, OPT_ChessServerName), @@ -4611,13 +7121,13 @@ StartupDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam) CenterWindow (hDlg, GetDesktopWindow()); /* Initialize the dialog items */ InitEngineBox(hDlg, GetDlgItem(hDlg, OPT_ChessEngineName), - appData.firstChessProgram, "fd", appData.firstDirectory, - firstChessProgramNames); + appData.firstChessProgram, "fd", appData.firstDirectory, + firstChessProgramNames); InitEngineBox(hDlg, GetDlgItem(hDlg, OPT_SecondChessEngineName), - appData.secondChessProgram, "sd", appData.secondDirectory, - secondChessProgramNames); + appData.secondChessProgram, "sd", appData.secondDirectory, + secondChessProgramNames); hwndCombo = GetDlgItem(hDlg, OPT_ChessServerName); - InitComboStringsFromOption(hwndCombo, icsNames); + InitComboStringsFromOption(hwndCombo, icsNames); sprintf(buf, "%s /icsport=%s", appData.icsHost, appData.icsPort); if (*appData.icsHelper != NULLCHAR) { char *q = QuoteForFilename(appData.icsHelper); @@ -4630,13 +7140,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 (chessProgram) { - CheckDlgButton(hDlg, OPT_ChessEngine, BST_CHECKED); - } else 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); + } + SetStartupDialogEnables(hDlg); return TRUE; @@ -4645,38 +7159,38 @@ StartupDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam) case IDOK: if (IsDlgButtonChecked(hDlg, OPT_ChessEngine)) { strcpy(buf, "/fcp="); - GetDlgItemText(hDlg, OPT_ChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf)); + GetDlgItemText(hDlg, OPT_ChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf)); p = buf; - ParseArgs(StringGet, &p); + ParseArgs(StringGet, &p); strcpy(buf, "/scp="); - GetDlgItemText(hDlg, OPT_SecondChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf)); + GetDlgItemText(hDlg, OPT_SecondChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf)); p = buf; - ParseArgs(StringGet, &p); - appData.noChessProgram = FALSE; - appData.icsActive = FALSE; + ParseArgs(StringGet, &p); + appData.noChessProgram = FALSE; + appData.icsActive = FALSE; } else if (IsDlgButtonChecked(hDlg, OPT_ChessServer)) { strcpy(buf, "/ics /icshost="); - GetDlgItemText(hDlg, OPT_ChessServerName, buf + strlen(buf), sizeof(buf) - strlen(buf)); + GetDlgItemText(hDlg, OPT_ChessServerName, buf + strlen(buf), sizeof(buf) - strlen(buf)); p = buf; - ParseArgs(StringGet, &p); - if (appData.zippyPlay) { - strcpy(buf, "/fcp="); - GetDlgItemText(hDlg, OPT_ChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf)); - p = buf; - ParseArgs(StringGet, &p); - } + ParseArgs(StringGet, &p); + if (appData.zippyPlay) { + strcpy(buf, "/fcp="); + GetDlgItemText(hDlg, OPT_ChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf)); + p = buf; + ParseArgs(StringGet, &p); + } } else if (IsDlgButtonChecked(hDlg, OPT_View)) { - appData.noChessProgram = TRUE; - appData.icsActive = FALSE; + appData.noChessProgram = TRUE; + appData.icsActive = FALSE; } else { - MessageBox(hDlg, "Choose an option, or cancel to exit", - "Option Error", MB_OK|MB_ICONEXCLAMATION); - return TRUE; + MessageBox(hDlg, "Choose an option, or cancel to exit", + "Option Error", MB_OK|MB_ICONEXCLAMATION); + return TRUE; } if (IsDlgButtonChecked(hDlg, OPT_AnyAdditional)) { - GetDlgItemText(hDlg, OPT_AdditionalOptions, buf, sizeof(buf)); - p = buf; - ParseArgs(StringGet, &p); + GetDlgItemText(hDlg, OPT_AdditionalOptions, buf, sizeof(buf)); + p = buf; + ParseArgs(StringGet, &p); } EndDialog(hDlg, TRUE); return TRUE; @@ -4687,9 +7201,9 @@ StartupDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam) case IDM_HELPCONTENTS: if (!WinHelp (hDlg, "winboard.hlp", HELP_KEY,(DWORD)(LPSTR)"CONTENTS")) { - MessageBox (GetFocus(), - "Unable to activate help", - szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND); + MessageBox (GetFocus(), + "Unable to activate help", + szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND); } break; @@ -4717,11 +7231,12 @@ About(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam) /* Center the dialog over the application window */ CenterWindow (hDlg, GetWindow (hDlg, GW_OWNER)); SetDlgItemText(hDlg, ABOUTBOX_Version, programVersion); + JAWS_COPYRIGHT return (TRUE); case WM_COMMAND: /* message: received a command */ if (LOWORD(wParam) == IDOK /* "OK" box selected? */ - || LOWORD(wParam) == IDCANCEL) { /* System menu close command? */ + || LOWORD(wParam) == IDCANCEL) { /* System menu close command? */ EndDialog(hDlg, TRUE); /* Exit the dialog */ return (TRUE); } @@ -4762,8 +7277,8 @@ CommentDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam) SetFocus(GetDlgItem(hDlg, IDOK)); } SendMessage(GetDlgItem(hDlg, OPT_CommentText), - WM_SETFONT, (WPARAM)font[boardSize][COMMENT_FONT]->hf, - MAKELPARAM(FALSE, 0)); + WM_SETFONT, (WPARAM)font[boardSize][COMMENT_FONT]->hf, + MAKELPARAM(FALSE, 0)); /* Size and position the dialog */ if (!commentDialog) { commentDialog = hDlg; @@ -4772,26 +7287,26 @@ CommentDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam) sizeX = rect.right; sizeY = rect.bottom; if (commentX != CW_USEDEFAULT && commentY != CW_USEDEFAULT && - commentW != CW_USEDEFAULT && commentH != CW_USEDEFAULT) { - WINDOWPLACEMENT wp; - EnsureOnScreen(&commentX, &commentY); - wp.length = sizeof(WINDOWPLACEMENT); - wp.flags = 0; - wp.showCmd = SW_SHOW; - wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0; - wp.rcNormalPosition.left = commentX; - wp.rcNormalPosition.right = commentX + commentW; - wp.rcNormalPosition.top = commentY; - wp.rcNormalPosition.bottom = commentY + commentH; - SetWindowPlacement(hDlg, &wp); - - GetClientRect(hDlg, &rect); - newSizeX = rect.right; - newSizeY = rect.bottom; + commentW != CW_USEDEFAULT && commentH != CW_USEDEFAULT) { + WINDOWPLACEMENT wp; + EnsureOnScreen(&commentX, &commentY, 0, 0); + wp.length = sizeof(WINDOWPLACEMENT); + wp.flags = 0; + wp.showCmd = SW_SHOW; + wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0; + wp.rcNormalPosition.left = commentX; + wp.rcNormalPosition.right = commentX + commentW; + wp.rcNormalPosition.top = commentY; + wp.rcNormalPosition.bottom = commentY + commentH; + SetWindowPlacement(hDlg, &wp); + + GetClientRect(hDlg, &rect); + newSizeX = rect.right; + newSizeY = rect.bottom; ResizeEditPlusButtons(hDlg, hwndText, sizeX, sizeY, - newSizeX, newSizeY); - sizeX = newSizeX; - sizeY = newSizeY; + newSizeX, newSizeY); + sizeX = newSizeX; + sizeY = newSizeY; } } return FALSE; @@ -4800,22 +7315,22 @@ CommentDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam) switch (LOWORD(wParam)) { case IDOK: if (editComment) { - char *p, *q; - /* Read changed options from the dialog box */ - hwndText = GetDlgItem(hDlg, OPT_CommentText); - len = GetWindowTextLength(hwndText); - str = (char *) malloc(len + 1); - GetWindowText(hwndText, str, len + 1); - p = q = str; - while (*q) { - if (*q == '\r') - q++; - else - *p++ = *q++; - } - *p = NULLCHAR; - ReplaceComment(commentIndex, str); - free(str); + char *p, *q; + /* Read changed options from the dialog box */ + hwndText = GetDlgItem(hDlg, OPT_CommentText); + len = GetWindowTextLength(hwndText); + str = (char *) malloc(len + 1); + GetWindowText(hwndText, str, len + 1); + p = q = str; + while (*q) { + if (*q == '\r') + q++; + else + *p++ = *q++; + } + *p = NULLCHAR; + ReplaceComment(commentIndex, str); + free(str); } CommentPopDown(); return TRUE; @@ -4885,7 +7400,7 @@ EitherCommentPopUp(int index, char *title, char *str, BOOLEAN edit) } else { lpProc = MakeProcInstance((FARPROC)CommentDialog, hInst); CreateDialog(hInst, MAKEINTRESOURCE(DLG_EditComment), - hwndMain, (DLGPROC)lpProc); + hwndMain, (DLGPROC)lpProc); FreeProcInstance(lpProc); } commentDialogUp = TRUE; @@ -4895,7 +7410,7 @@ EitherCommentPopUp(int index, char *title, char *str, BOOLEAN edit) /*---------------------------------------------------------------------------*\ * * Type-in move dialog functions - * + * \*---------------------------------------------------------------------------*/ LRESULT CALLBACK @@ -4911,7 +7426,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); @@ -4921,19 +7436,42 @@ TypeInMoveDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam) case WM_COMMAND: switch (LOWORD(wParam)) { case IDOK: - if (gameMode != EditGame && currentMove != forwardMostMove && - gameMode != Training) { - DisplayMoveError("Displayed move is not current"); + GetDlgItemText(hDlg, OPT_Move, move, sizeof(move)); + { int n; Board board; + // [HGM] FENedit + if(gameMode == EditPosition && ParseFEN(board, &n, move) ) { + EditPositionPasteFEN(move); + EndDialog(hDlg, TRUE); + return TRUE; + } + // [HGM] movenum: allow move number to be typed in any mode + if(sscanf(move, "%d", &n) == 1 && n != 0 ) { + currentMove = 2*n-1; + if(currentMove > forwardMostMove) currentMove = forwardMostMove; + if(currentMove < backwardMostMove) currentMove = backwardMostMove; + EndDialog(hDlg, TRUE); + DrawPosition(TRUE, boards[currentMove]); + if(currentMove > backwardMostMove) DisplayMove(currentMove - 1); + else DisplayMessage("", ""); + return TRUE; + } + } + if (gameMode != EditGame && currentMove != forwardMostMove && + gameMode != Training) { + DisplayMoveError("Displayed move is not current"); } else { - GetDlgItemText(hDlg, OPT_Move, move, sizeof(move)); - if (ParseOneMove(move, gameMode == EditPosition ? blackPlaysFirst : currentMove, - &moveType, &fromX, &fromY, &toX, &toY, &promoChar)) { - if (gameMode != Training) - forwardMostMove = currentMove; - UserMoveEvent(fromX, fromY, toX, toY, promoChar); - } else { - DisplayMoveError("Could not parse move"); - } +// GetDlgItemText(hDlg, OPT_Move, move, sizeof(move)); // moved upstream + int ok = ParseOneMove(move, gameMode == EditPosition ? blackPlaysFirst : currentMove, + &moveType, &fromX, &fromY, &toX, &toY, &promoChar); + if(!ok && move[0] >= 'a') { move[0] += 'A' - 'a'; ok = 2; } // [HGM] try also capitalized + if (ok==1 || ok && ParseOneMove(move, gameMode == EditPosition ? blackPlaysFirst : currentMove, + &moveType, &fromX, &fromY, &toX, &toY, &promoChar)) { + if (gameMode != Training) + forwardMostMove = currentMove; + UserMoveEvent(fromX, fromY, toX, toY, promoChar); + } else { + DisplayMoveError("Could not parse move"); + } } EndDialog(hDlg, TRUE); return TRUE; @@ -4952,29 +7490,86 @@ VOID PopUpMoveDialog(char firstchar) { FARPROC lpProc; - - if ((gameMode == BeginningOfGame && !appData.icsActive) || + + if ((gameMode == BeginningOfGame && !appData.icsActive) || gameMode == MachinePlaysWhite || gameMode == MachinePlaysBlack || - gameMode == AnalyzeMode || gameMode == EditGame || - gameMode == EditPosition || gameMode == IcsExamining || - gameMode == IcsPlayingWhite || gameMode == IcsPlayingBlack || - gameMode == Training) { + gameMode == AnalyzeMode || gameMode == EditGame || + gameMode == EditPosition || gameMode == IcsExamining || + gameMode == IcsPlayingWhite || gameMode == IcsPlayingBlack || + isdigit(firstchar) && // [HGM] movenum: allow typing in of move nr in 'passive' modes + ( gameMode == AnalyzeFile || gameMode == PlayFromGameFile || + gameMode == IcsObserving || gameMode == TwoMachinesPlay ) || + gameMode == Training) { lpProc = MakeProcInstance((FARPROC)TypeInMoveDialog, hInst); DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_TypeInMove), - hwndMain, (DLGPROC)lpProc, (LPARAM)firstchar); + hwndMain, (DLGPROC)lpProc, (LPARAM)firstchar); FreeProcInstance(lpProc); } } /*---------------------------------------------------------------------------*\ * + * Type-in name dialog functions + * +\*---------------------------------------------------------------------------*/ + +LRESULT CALLBACK +TypeInNameDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam) +{ + char move[MSG_SIZ]; + HWND hInput; + + switch (message) { + case WM_INITDIALOG: + move[0] = (char) lParam; + move[1] = NULLCHAR; + CenterWindowEx(hDlg, GetWindow(hDlg, GW_OWNER), 1 ); + hInput = GetDlgItem(hDlg, OPT_Name); + SetWindowText(hInput, move); + SetFocus(hInput); + SendMessage(hInput, EM_SETSEL, (WPARAM)9999, (LPARAM)9999); + return FALSE; + + case WM_COMMAND: + switch (LOWORD(wParam)) { + case IDOK: + GetDlgItemText(hDlg, OPT_Name, move, sizeof(move)); + appData.userName = strdup(move); + SetUserLogo(); + + EndDialog(hDlg, TRUE); + return TRUE; + case IDCANCEL: + EndDialog(hDlg, FALSE); + return TRUE; + default: + break; + } + break; + } + return FALSE; +} + +VOID +PopUpNameDialog(char firstchar) +{ + FARPROC lpProc; + + lpProc = MakeProcInstance((FARPROC)TypeInNameDialog, hInst); + DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_TypeInName), + hwndMain, (DLGPROC)lpProc, (LPARAM)firstchar); + FreeProcInstance(lpProc); +} + +/*---------------------------------------------------------------------------*\ + * * Error dialogs - * + * \*---------------------------------------------------------------------------*/ /* Nonmodal error box */ LRESULT CALLBACK ErrorDialog(HWND hDlg, UINT message, - WPARAM wParam, LPARAM lParam); + WPARAM wParam, LPARAM lParam); VOID ErrorPopUp(char *title, char *content) @@ -4988,11 +7583,11 @@ ErrorPopUp(char *title, char *content) while (*p) { if (*p == '\n') { if (modal) { - *q++ = ' '; - p++; + *q++ = ' '; + p++; } else { - *q++ = '\r'; - *q++ = *p++; + *q++ = '\r'; + *q++ = *p++; } } else { *q++ = *p++; @@ -5001,13 +7596,13 @@ ErrorPopUp(char *title, char *content) *q = NULLCHAR; strncpy(errorTitle, title, sizeof(errorTitle)); errorTitle[sizeof(errorTitle) - 1] = '\0'; - + if (modal) { MessageBox(NULL, errorMessage, errorTitle, MB_OK|MB_ICONEXCLAMATION); } else { lpProc = MakeProcInstance((FARPROC)ErrorDialog, hInst); CreateDialog(hInst, MAKEINTRESOURCE(DLG_Error), - hwndMain, (DLGPROC)lpProc); + hwndMain, (DLGPROC)lpProc); FreeProcInstance(lpProc); } } @@ -5030,9 +7625,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), + 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); @@ -5055,6 +7662,74 @@ 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, VariantClass variant) +{ + FARPROC lpProc; + static char *lastTitle; + + strncpy(errorTitle, title, sizeof(errorTitle)); + errorTitle[sizeof(errorTitle) - 1] = '\0'; + + if(lastTitle != title && gothicDialog != NULL) { + DestroyWindow(gothicDialog); + gothicDialog = NULL; + } + if(variant != VariantNormal && gothicDialog == NULL) { + title = lastTitle; + lpProc = MakeProcInstance((FARPROC)GothicDialog, hInst); + CreateDialog(hInst, MAKEINTRESOURCE(DLG_Error), + hwndMain, (DLGPROC)lpProc); + FreeProcInstance(lpProc); + } +} +#endif + /*---------------------------------------------------------------------------*\ * * Ics Interaction console functions @@ -5116,7 +7791,7 @@ IcsTextMenuEntry icsTextMenuEntry[ICS_TEXT_MENU_SIZE]; void ParseIcsTextMenu(char *icsTextMenuString) { - int flags = 0; +// int flags = 0; IcsTextMenuEntry *e = icsTextMenuEntry; char *p = icsTextMenuString; while (e->item != NULL && e < icsTextMenuEntry + ICS_TEXT_MENU_SIZE) { @@ -5154,8 +7829,8 @@ ParseIcsTextMenu(char *icsTextMenuString) c = ';'; t = strchr(s + 1, c); if (t == NULL) { - c = '\n'; - t = strchr(s + 1, c); + c = '\n'; + t = strchr(s + 1, c); } if (t != NULL) *t = NULLCHAR; e->item = strdup(p); @@ -5170,7 +7845,7 @@ ParseIcsTextMenu(char *icsTextMenuString) p = t + 1; } e++; - } + } } HMENU @@ -5185,15 +7860,15 @@ LoadIcsTextMenu(IcsTextMenuEntry *e) AppendMenu(h, MF_SEPARATOR, 0, 0); } else { if (e->item[0] == '|') { - AppendMenu(h, MF_STRING|MF_MENUBARBREAK, - IDM_CommandX + i, &e->item[1]); + AppendMenu(h, MF_STRING|MF_MENUBARBREAK, + IDM_CommandX + i, &e->item[1]); } else { - AppendMenu(h, MF_STRING, IDM_CommandX + i, e->item); + AppendMenu(h, MF_STRING, IDM_CommandX + i, e->item); } } e++; i++; - } + } return hmenu; } @@ -5217,7 +7892,7 @@ CommandX(HWND hwnd, char *command, BOOLEAN getname, BOOLEAN immediate) SetFocus(hInput); } return; - } + } SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel); if (sel.cpMin == sel.cpMax) { /* Expand to surrounding word */ @@ -5267,7 +7942,7 @@ CommandX(HWND hwnd, char *command, BOOLEAN getname, BOOLEAN immediate) } } -LRESULT CALLBACK +LRESULT CALLBACK ConsoleTextSubclass(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) { HWND hInput; @@ -5289,49 +7964,30 @@ ConsoleTextSubclass(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) } break; case WM_CHAR: + if(wParam != '\022') { if (wParam == '\t') { if (GetKeyState(VK_SHIFT) < 0) { - /* shifted */ - if (IsIconic(hwndMain)) ShowWindow(hwndMain, SW_RESTORE); - if (buttonDesc[0].hwnd) { - SetFocus(buttonDesc[0].hwnd); - } else { - SetFocus(hwndMain); - } + /* shifted */ + if (IsIconic(hwndMain)) ShowWindow(hwndMain, SW_RESTORE); + if (buttonDesc[0].hwnd) { + SetFocus(buttonDesc[0].hwnd); + } else { + SetFocus(hwndMain); + } } else { - /* unshifted */ - SetFocus(GetDlgItem(hwndConsole, OPT_ConsoleInput)); + /* unshifted */ + SetFocus(GetDlgItem(hwndConsole, OPT_ConsoleInput)); } } else { hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput); - SetFocus(hInput); + JAWS_DELETE( SetFocus(hInput); ) SendMessage(hInput, message, wParam, lParam); } return 0; - case WM_PASTE: - hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput); - SetFocus(hInput); - return SendMessage(hInput, message, wParam, lParam); - case WM_MBUTTONDOWN: - return SendMessage(hwnd, WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0); - case WM_RBUTTONDOWN: - if (!(GetKeyState(VK_SHIFT) & ~1)) { - /* Move selection here if it was empty */ - POINT pt; - pt.x = LOWORD(lParam); - pt.y = HIWORD(lParam); - SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel); - if (sel.cpMin == sel.cpMax) { - sel.cpMin = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&pt); /*doc is wrong*/ - sel.cpMax = sel.cpMin; - SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel); - } - SendMessage(hwnd, EM_HIDESELECTION, FALSE, FALSE); - } - return 0; + } // [HGM] navigate: for Ctrl+R, flow into nex case (moved up here) to summon up menu case WM_RBUTTONUP: if (GetKeyState(VK_SHIFT) & ~1) { - SendDlgItemMessage(hwndConsole, OPT_ConsoleText, + SendDlgItemMessage(hwndConsole, OPT_ConsoleText, WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0); } else { POINT pt; @@ -5349,17 +8005,38 @@ ConsoleTextSubclass(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) MenuPopup(hwnd, pt, hmenu, -1); } return 0; + case WM_PASTE: + hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput); + SetFocus(hInput); + return SendMessage(hInput, message, wParam, lParam); + case WM_MBUTTONDOWN: + return SendMessage(hwnd, WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0); + case WM_RBUTTONDOWN: + if (!(GetKeyState(VK_SHIFT) & ~1)) { + /* Move selection here if it was empty */ + POINT pt; + pt.x = LOWORD(lParam); + pt.y = HIWORD(lParam); + SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel); + if (sel.cpMin == sel.cpMax) { + sel.cpMin = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&pt); /*doc is wrong*/ + sel.cpMax = sel.cpMin; + SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel); + } + SendMessage(hwnd, EM_HIDESELECTION, FALSE, FALSE); + } + return 0; case WM_COMMAND: switch (LOWORD(wParam)) { case IDM_QuickPaste: { SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel); - if (sel.cpMin == sel.cpMax) { - MessageBeep(MB_ICONEXCLAMATION); + if (sel.cpMin == sel.cpMax) { + MessageBeep(MB_ICONEXCLAMATION); return 0; - } - SendMessage(hwnd, WM_COPY, 0, 0); - hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput); + } + SendMessage(hwnd, WM_COPY, 0, 0); + hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput); SendMessage(hInput, WM_PASTE, 0, 0); SetFocus(hInput); return 0; @@ -5375,14 +8052,14 @@ ConsoleTextSubclass(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) return 0; default: { - int i = LOWORD(wParam) - IDM_CommandX; - if (i >= 0 && i < ICS_TEXT_MENU_SIZE && - icsTextMenuEntry[i].command != NULL) { - CommandX(hwnd, icsTextMenuEntry[i].command, - icsTextMenuEntry[i].getname, - icsTextMenuEntry[i].immediate); - return 0; - } + int i = LOWORD(wParam) - IDM_CommandX; + if (i >= 0 && i < ICS_TEXT_MENU_SIZE && + icsTextMenuEntry[i].command != NULL) { + CommandX(hwnd, icsTextMenuEntry[i].command, + icsTextMenuEntry[i].getname, + icsTextMenuEntry[i].immediate); + return 0; + } } break; } @@ -5422,15 +8099,15 @@ ConsoleInputSubclass(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) } switch (wParam) { case '\r': /* Enter key */ - is->count = GetWindowText(hwnd, is->buf, INPUT_SOURCE_BUF_SIZE-1); + is->count = GetWindowText(hwnd, is->buf, INPUT_SOURCE_BUF_SIZE-1); if (consoleEcho) SaveInHistory(is->buf); is->buf[is->count++] = '\n'; is->buf[is->count] = NULLCHAR; SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is); if (consoleEcho) { - ConsoleOutput(is->buf, is->count, TRUE); + ConsoleOutput(is->buf, is->count, TRUE); } else if (appData.localLineEditing) { - ConsoleOutput("\n", 1, TRUE); + ConsoleOutput("\n", 1, TRUE); } /* fall thru */ case '\033': /* Escape key */ @@ -5440,23 +8117,23 @@ ConsoleInputSubclass(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) if (consoleEcho) { cf.crTextColor = textAttribs[ColorNormal].color; } else { - cf.crTextColor = COLOR_ECHOOFF; + cf.crTextColor = COLOR_ECHOOFF; } cf.dwEffects = textAttribs[ColorNormal].effects; SendMessage(hwnd, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf); return 0; case '\t': /* Tab key */ if (GetKeyState(VK_SHIFT) < 0) { - /* shifted */ - SetFocus(GetDlgItem(hwndConsole, OPT_ConsoleText)); + /* shifted */ + SetFocus(GetDlgItem(hwndConsole, OPT_ConsoleText)); } else { - /* unshifted */ - if (IsIconic(hwndMain)) ShowWindow(hwndMain, SW_RESTORE); - if (buttonDesc[0].hwnd) { - SetFocus(buttonDesc[0].hwnd); - } else { - SetFocus(hwndMain); - } + /* unshifted */ + if (IsIconic(hwndMain)) ShowWindow(hwndMain, SW_RESTORE); + if (buttonDesc[0].hwnd) { + SetFocus(buttonDesc[0].hwnd); + } else { + SetFocus(hwndMain); + } } return 0; case '\023': /* Ctrl+S */ @@ -5465,6 +8142,7 @@ ConsoleInputSubclass(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) case '\021': /* Ctrl+Q */ quoteNextChar = TRUE; return 0; + JAWS_REPLAY default: break; } @@ -5475,20 +8153,20 @@ ConsoleInputSubclass(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) GetWindowText(hwnd, buf, MSG_SIZ); p = PrevInHistory(buf); if (p != NULL) { - SetWindowText(hwnd, p); - sel.cpMin = 999999; - sel.cpMax = 999999; - SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel); + SetWindowText(hwnd, p); + sel.cpMin = 999999; + sel.cpMax = 999999; + SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel); return 0; } break; case VK_DOWN: p = NextInHistory(); if (p != NULL) { - SetWindowText(hwnd, p); - sel.cpMin = 999999; - sel.cpMax = 999999; - SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel); + SetWindowText(hwnd, p); + sel.cpMin = 999999; + sel.cpMax = 999999; + SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel); return 0; } break; @@ -5503,12 +8181,12 @@ ConsoleInputSubclass(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) } break; case WM_MBUTTONDOWN: - SendDlgItemMessage(hwndConsole, OPT_ConsoleText, + SendDlgItemMessage(hwndConsole, OPT_ConsoleText, WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0); break; case WM_RBUTTONUP: if (GetKeyState(VK_SHIFT) & ~1) { - SendDlgItemMessage(hwndConsole, OPT_ConsoleText, + SendDlgItemMessage(hwndConsole, OPT_ConsoleText, WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0); } else { POINT pt; @@ -5528,7 +8206,7 @@ ConsoleInputSubclass(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) } return 0; case WM_COMMAND: - switch (LOWORD(wParam)) { + switch (LOWORD(wParam)) { case IDM_Undo: SendMessage(hwnd, EM_UNDO, 0, 0); return 0; @@ -5558,14 +8236,32 @@ ConsoleInputSubclass(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) LRESULT CALLBACK ConsoleWndProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam) { - static HWND hText, hInput, hFocus; - InputSource *is = consoleInputSource; + static SnapData sd; + static HWND hText, hInput /*, hFocus*/; +// InputSource *is = consoleInputSource; RECT rect; static int sizeX, sizeY; int newSizeX, newSizeY; MINMAXINFO *mmi; switch (message) { + case WM_NOTIFY: + if (((NMHDR*)lParam)->code == EN_LINK) + { + ENLINK *pLink = (ENLINK*)lParam; + if (pLink->msg == WM_LBUTTONUP) + { + TEXTRANGE tr; + + tr.chrg = pLink->chrg; + tr.lpstrText = malloc(1+tr.chrg.cpMax-tr.chrg.cpMin); + hText = GetDlgItem(hDlg, OPT_ConsoleText); + SendMessage(hText, EM_GETTEXTRANGE, 0, (LPARAM)&tr); + ShellExecute(NULL, "open", tr.lpstrText, NULL, NULL, SW_SHOW); + free(tr.lpstrText); + } + } + break; case WM_INITDIALOG: /* message: initialize dialog box */ hwndConsole = hDlg; hText = GetDlgItem(hDlg, OPT_ConsoleText); @@ -5583,20 +8279,40 @@ ConsoleWndProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam) GetClientRect(hDlg, &rect); sizeX = rect.right; sizeY = rect.bottom; - if (consoleX != CW_USEDEFAULT && consoleY != CW_USEDEFAULT && - consoleW != CW_USEDEFAULT && consoleH != CW_USEDEFAULT) { + if (wpConsole.x != CW_USEDEFAULT && wpConsole.y != CW_USEDEFAULT && + wpConsole.width != CW_USEDEFAULT && wpConsole.height != CW_USEDEFAULT) { WINDOWPLACEMENT wp; - EnsureOnScreen(&consoleX, &consoleY); + EnsureOnScreen(&wpConsole.x, &wpConsole.y, 0, 0); wp.length = sizeof(WINDOWPLACEMENT); wp.flags = 0; wp.showCmd = SW_SHOW; wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0; - wp.rcNormalPosition.left = consoleX; - wp.rcNormalPosition.right = consoleX + consoleW; - wp.rcNormalPosition.top = consoleY; - wp.rcNormalPosition.bottom = consoleY + consoleH; + wp.rcNormalPosition.left = wpConsole.x; + wp.rcNormalPosition.right = wpConsole.x + wpConsole.width; + wp.rcNormalPosition.top = wpConsole.y; + wp.rcNormalPosition.bottom = wpConsole.y + wpConsole.height; SetWindowPlacement(hDlg, &wp); } +#if 1 + // [HGM] Chessknight's change 2004-07-13 + else { /* Determine Defaults */ + WINDOWPLACEMENT wp; + wpConsole.x = winWidth + 1; + wpConsole.y = boardY; + wpConsole.width = screenWidth - winWidth; + wpConsole.height = winHeight; + EnsureOnScreen(&wpConsole.x, &wpConsole.y, 0, 0); + wp.length = sizeof(WINDOWPLACEMENT); + wp.flags = 0; + wp.showCmd = SW_SHOW; + wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0; + wp.rcNormalPosition.left = wpConsole.x; + wp.rcNormalPosition.right = wpConsole.x + wpConsole.width; + wp.rcNormalPosition.top = wpConsole.y; + wp.rcNormalPosition.bottom = wpConsole.y + wpConsole.height; + SetWindowPlacement(hDlg, &wp); + } +#endif return FALSE; case WM_SETFOCUS: @@ -5620,19 +8336,19 @@ ConsoleWndProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam) newTextWidth = rectText.right - rectText.left + newSizeX - sizeX; newTextHeight = rectText.bottom - rectText.top + newSizeY - sizeY; if (newTextHeight < 0) { - newSizeY += -newTextHeight; + newSizeY += -newTextHeight; newTextHeight = 0; } SetWindowPos(hText, NULL, 0, 0, - newTextWidth, newTextHeight, SWP_NOZORDER|SWP_NOMOVE); + newTextWidth, newTextHeight, SWP_NOZORDER|SWP_NOMOVE); GetWindowRect(hInput, &rectInput); /* gives screen coords */ pt.x = rectInput.left; pt.y = rectInput.top + newSizeY - sizeY; ScreenToClient(hDlg, &pt); - SetWindowPos(hInput, NULL, - pt.x, pt.y, /* needs client coords */ - rectInput.right - rectInput.left + newSizeX - sizeX, - rectInput.bottom - rectInput.top, SWP_NOZORDER); + SetWindowPos(hInput, NULL, + pt.x, pt.y, /* needs client coords */ + rectInput.right - rectInput.left + newSizeX - sizeX, + rectInput.bottom - rectInput.top, SWP_NOZORDER); } sizeX = newSizeX; sizeY = newSizeY; @@ -5644,7 +8360,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); } @@ -5652,10 +8382,17 @@ ConsoleWndProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam) VOID ConsoleCreate() { - HWND hCons; + HWND hCons, hText; + WORD wMask; if (hwndConsole) return; hCons = CreateDialog(hInst, szConsoleName, 0, NULL); SendMessage(hCons, WM_INITDIALOG, 0, 0); + + // make the text item in the console do URL links + hText = GetDlgItem(hCons, OPT_ConsoleText); + wMask = SendMessage(hText, EM_GETEVENTMASK, 0, 0L); + SendMessage(hText, EM_SETEVENTMASK, 0, wMask | ENM_LINK); + SendMessage(hText, EM_AUTOURLDETECT, TRUE, 0L); } @@ -5682,10 +8419,10 @@ ConsoleOutput(char* data, int length, int forceVisible) while (length--) { if (*p == '\n') { if (*++p) { - *q++ = '\r'; - *q++ = '\n'; + *q++ = '\r'; + *q++ = '\n'; } else { - delayLF = 1; + delayLF = 1; } } else if (*p == '\007') { MyPlaySound(&sounds[(int)SoundBell]); @@ -5752,8 +8489,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; @@ -5762,9 +8530,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", color[0], TimeString(timeRemaining), flagFell); else - sprintf(buf, "%s: %s", color, TimeString(timeRemaining)); + sprintf(buf, "%s:%c%s %s", color, (logoHeight>0 ? 0 : ' '), TimeString(timeRemaining), flagFell); str = buf; } else { str = color; @@ -5779,10 +8547,22 @@ DisplayAClock(HDC hdc, int timeRemaining, int highlight, } oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf); - ExtTextOut(hdc, rect->left + MESSAGE_LINE_LEFTMARGIN, - rect->top, ETO_CLIPPED|ETO_OPAQUE, - rect, str, strlen(str), NULL); + JAWS_SILENCE + ExtTextOut(hdc, rect->left + MESSAGE_LINE_LEFTMARGIN, + rect->top, ETO_CLIPPED|ETO_OPAQUE, + rect, str, strlen(str), NULL); + if(logoHeight > 0 && appData.clockMode) { + RECT r; + sprintf(buf, "%s %s", buf+7, flagFell); + r.top = rect->top + logoHeight/2; + r.left = rect->left; + r.right = rect->right; + r.bottom = rect->bottom; + ExtTextOut(hdc, rect->left + MESSAGE_LINE_LEFTMARGIN, + r.top, ETO_CLIPPED|ETO_OPAQUE, + &r, str, strlen(str), NULL); + } (void) SetTextColor(hdc, oldFg); (void) SetBkColor(hdc, oldBg); (void) SelectObject(hdc, oldFont); @@ -5791,10 +8571,19 @@ DisplayAClock(HDC hdc, int timeRemaining, int highlight, int DoReadFile(HANDLE hFile, char *buf, int count, DWORD *outCount, - OVERLAPPED *ovl) + OVERLAPPED *ovl) { 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); @@ -5805,9 +8594,9 @@ DoReadFile(HANDLE hFile, char *buf, int count, DWORD *outCount, if (err == ERROR_IO_PENDING) { ok = GetOverlappedResult(hFile, ovl, outCount, TRUE); if (ok) - err = NO_ERROR; + err = NO_ERROR; else - err = GetLastError(); + err = GetLastError(); } } return err; @@ -5815,7 +8604,7 @@ DoReadFile(HANDLE hFile, char *buf, int count, DWORD *outCount, int DoWriteFile(HANDLE hFile, char *buf, int count, DWORD *outCount, - OVERLAPPED *ovl) + OVERLAPPED *ovl) { int ok, err; @@ -5829,14 +8618,36 @@ DoWriteFile(HANDLE hFile, char *buf, int count, DWORD *outCount, if (err == ERROR_IO_PENDING) { ok = GetOverlappedResult(hFile, ovl, outCount, TRUE); if (ok) - err = NO_ERROR; + err = NO_ERROR; else - err = GetLastError(); + err = GetLastError(); } } 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=%lu)\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) @@ -5849,23 +8660,37 @@ InputThread(LPVOID arg) ovl.Internal = ovl.InternalHigh = ovl.Offset = ovl.OffsetHigh = 0; while (is->hThread != NULL) { is->error = DoReadFile(is->hFile, is->next, - INPUT_SOURCE_BUF_SIZE - (is->next - is->buf), - &is->count, &ovl); + INPUT_SOURCE_BUF_SIZE - (is->next - is->buf), + &is->count, &ovl); if (is->error == NO_ERROR) { is->next += is->count; } else { if (is->error == ERROR_BROKEN_PIPE) { - /* Correct for MS brain damage. EOF reading a pipe is not an error. */ - is->count = 0; + /* Correct for MS brain damage. EOF reading a pipe is not an error. */ + is->count = 0; } else { - is->count = (DWORD) -1; + is->count = (DWORD) -1; + /* [AS] The (is->count <= 0) check below is not useful for unsigned values! */ + break; } } + + 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=%lu, error=%d, count=%ld)\n", is->id, is->error, is->count ); + } + return 0; } @@ -5882,40 +8707,46 @@ NonOvlInputThread(LPVOID arg) is = (InputSource *) arg; while (is->hThread != NULL) { is->error = ReadFile(is->hFile, is->next, - INPUT_SOURCE_BUF_SIZE - (is->next - is->buf), - &is->count, NULL) ? NO_ERROR : GetLastError(); + INPUT_SOURCE_BUF_SIZE - (is->next - is->buf), + &is->count, NULL) ? NO_ERROR : GetLastError(); if (is->error == NO_ERROR) { /* Change CRLF to LF */ if (is->next > is->buf) { - p = is->next - 1; - i = is->count + 1; + p = is->next - 1; + i = is->count + 1; } else { - p = is->next; - i = is->count; + p = is->next; + i = is->count; } q = p; prev = NULLCHAR; while (i > 0) { - if (prev == '\r' && *p == '\n') { - *(q-1) = '\n'; - is->count--; - } else { - *q++ = *p; - } - prev = *p++; - i--; + if (prev == '\r' && *p == '\n') { + *(q-1) = '\n'; + is->count--; + } else { + *q++ = *p; + } + prev = *p++; + i--; } *q = NULLCHAR; is->next = q; } else { if (is->error == ERROR_BROKEN_PIPE) { - /* Correct for MS brain damage. EOF reading a pipe is not an error. */ - is->count = 0; + /* Correct for MS brain damage. EOF reading a pipe is not an error. */ + is->count = 0; } else { - is->count = (DWORD) -1; + is->count = (DWORD) -1; } } + + 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); @@ -5937,11 +8768,14 @@ SocketInputThread(LPVOID arg) is->error = NO_ERROR; is->next += is->count; if (is->count == 0 && is->second == is) { - /* End of file on stderr; quit with no message */ - break; + /* End of file on stderr; quit with no message */ + break; } } 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; @@ -5959,20 +8793,22 @@ InputEvent(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) char *q = p; while (q < is->next) { if (*q++ == '\n') { - (is->func)(is, is->closure, p, q - p, NO_ERROR); - p = q; + (is->func)(is, is->closure, p, q - p, NO_ERROR); + 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. */ - (is->func)(is, is->closure, is->buf, is->count, is->error); + line at the end, it is not flushed through. */ + (is->func)(is, is->closure, is->buf, is->count, is->error); } } else { /* Feed in the whole chunk of input at once */ @@ -6021,7 +8857,7 @@ Enables icsEnables[] = { { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED }, { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED }, { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED }, - { IDM_AnalysisMode, MF_BYCOMMAND|MF_GRAYED }, + { IDM_AnalysisMode, MF_BYCOMMAND|MF_ENABLED }, { IDM_AnalyzeFile, MF_BYCOMMAND|MF_GRAYED }, { IDM_TimeControl, MF_BYCOMMAND|MF_GRAYED }, { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED }, @@ -6137,7 +8973,7 @@ Enables userThinkingEnables[] = { * * Front-end interface functions exported by XBoard. * Functions appear in same order as prototypes in frontend.h. - * + * \*---------------------------------------------------------------------------*/ VOID ModeHighlight() @@ -6149,7 +8985,7 @@ ModeHighlight() if (pausing != prevPausing) { prevPausing = pausing; (void) CheckMenuItem(GetMenu(hwndMain), IDM_Pause, - MF_BYCOMMAND|(pausing ? MF_CHECKED : MF_UNCHECKED)); + MF_BYCOMMAND|(pausing ? MF_CHECKED : MF_UNCHECKED)); if (hwndPause) SetWindowText(hwndPause, pausing ? "C" : "P"); } @@ -6202,20 +9038,31 @@ ModeHighlight() } if (prevChecked != 0) (void) CheckMenuItem(GetMenu(hwndMain), - prevChecked, MF_BYCOMMAND|MF_UNCHECKED); + prevChecked, MF_BYCOMMAND|MF_UNCHECKED); if (nowChecked != 0) (void) CheckMenuItem(GetMenu(hwndMain), - nowChecked, MF_BYCOMMAND|MF_CHECKED); + nowChecked, MF_BYCOMMAND|MF_CHECKED); if (nowChecked == IDM_LoadGame || nowChecked == IDM_Training) { - (void) EnableMenuItem(GetMenu(hwndMain), IDM_Training, - MF_BYCOMMAND|MF_ENABLED); + (void) EnableMenuItem(GetMenu(hwndMain), IDM_Training, + MF_BYCOMMAND|MF_ENABLED); } else { - (void) EnableMenuItem(GetMenu(hwndMain), - IDM_Training, MF_BYCOMMAND|MF_GRAYED); + (void) EnableMenuItem(GetMenu(hwndMain), + IDM_Training, MF_BYCOMMAND|MF_GRAYED); } prevChecked = nowChecked; + + /* [DM] icsEngineAnalyze - Do a sceure check too */ + if (appData.icsActive) { + if (appData.icsEngineAnalyze) { + (void) CheckMenuItem(GetMenu(hwndMain), IDM_AnalysisMode, + MF_BYCOMMAND|MF_CHECKED); + } else { + (void) CheckMenuItem(GetMenu(hwndMain), IDM_AnalysisMode, + MF_BYCOMMAND|MF_UNCHECKED); + } + } } VOID @@ -6228,6 +9075,9 @@ SetICSMode() #ifdef ZIPPY if (appData.zippyPlay) { SetMenuEnables(hmenu, zippyEnables); + if (!appData.noChessProgram) /* [DM] icsEngineAnalyze */ + (void) EnableMenuItem(GetMenu(hwndMain), IDM_AnalysisMode, + MF_BYCOMMAND|MF_ENABLED); } #endif } @@ -6254,7 +9104,7 @@ SetCmailMode() SetMenuEnables(GetMenu(hwndMain), cmailEnables); } -VOID +VOID SetTrainingModeOn() { int i; @@ -6310,7 +9160,7 @@ DisplayTitle(char *str) } else if (appData.icsActive) { if (appData.icsCommPort[0] != NULLCHAR) host = "ICS"; - else + else host = appData.icsHost; sprintf(title, "%s: %s", szTitle, host); } else if (appData.noChessProgram) { @@ -6352,11 +9202,14 @@ DisplayMessage(char *str1, char *str2) } messageText[MESSAGE_TEXT_MAX - 1] = NULLCHAR; - if (IsIconic(hwndMain)) return; + if (hwndMain == NULL || IsIconic(hwndMain)) return; + + SAYMACHINEMOVE(); + hdc = GetDC(hwndMain); oldFont = SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf); ExtTextOut(hdc, messageRect.left, messageRect.top, ETO_CLIPPED|ETO_OPAQUE, - &messageRect, messageText, strlen(messageText), NULL); + &messageRect, messageText, strlen(messageText), NULL); (void) SelectObject(hdc, oldFont); (void) ReleaseDC(hwndMain, hdc); } @@ -6366,26 +9219,26 @@ DisplayError(char *str, int error) { char buf[MSG_SIZ*2], buf2[MSG_SIZ]; int len; - + if (error == 0) { strcpy(buf, str); } else { len = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, - NULL, error, LANG_NEUTRAL, - (LPSTR) buf2, MSG_SIZ, NULL); + NULL, error, LANG_NEUTRAL, + (LPSTR) buf2, MSG_SIZ, NULL); if (len > 0) { sprintf(buf, "%s:\n%s", str, buf2); } else { ErrorMap *em = errmap; while (em->err != 0 && em->err != error) em++; if (em->err != 0) { - sprintf(buf, "%s:\n%s", str, em->msg); + sprintf(buf, "%s:\n%s", str, em->msg); } else { - sprintf(buf, "%s:\nError code %d", str, error); + sprintf(buf, "%s:\nError code %d", str, error); } } } - + ErrorPopUp("Error", buf); } @@ -6413,17 +9266,17 @@ DisplayFatalError(char *str, int error, int exitStatus) if (error != 0) { len = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, - NULL, error, LANG_NEUTRAL, - (LPSTR) buf2, MSG_SIZ, NULL); + NULL, error, LANG_NEUTRAL, + (LPSTR) buf2, MSG_SIZ, NULL); if (len > 0) { sprintf(buf, "%s:\n%s", str, buf2); } else { ErrorMap *em = errmap; while (em->err != 0 && em->err != error) em++; if (em->err != 0) { - sprintf(buf, "%s:\n%s", str, em->msg); + sprintf(buf, "%s:\n%s", str, em->msg); } else { - sprintf(buf, "%s:\nError code %d", str, error); + sprintf(buf, "%s:\nError code %d", str, error); } } str = buf; @@ -6433,7 +9286,7 @@ DisplayFatalError(char *str, int error, int exitStatus) } if (appData.popupExitMessage) { (void) MessageBox(hwndMain, str, label, MB_OK| - (exitStatus ? MB_ICONSTOP : MB_ICONINFORMATION)); + (exitStatus ? MB_ICONSTOP : MB_ICONINFORMATION)); } ExitEvent(exitStatus); } @@ -6502,7 +9355,7 @@ AskQuestion(char* title, char *question, char *replyPrefix, ProcRef pr) { QuestionParams qp; FARPROC lpProc; - + qp.title = title; qp.question = question; qp.replyPrefix = replyPrefix; @@ -6513,6 +9366,273 @@ 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 ); + shuffleOpenings = TRUE; /* [HGM] shuffle: switch shuffling on for as long as we stay in current variant */ + 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() ); /* [HGM] shuffle: no longer limit to 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 }, + { GLT_RESULT_COMMENT, "Result Comment" }, // [HGM] rescom + { 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) @@ -6526,7 +9646,7 @@ DisplayIcsInteractionTitle(char *str) void DrawPosition(int fullRedraw, Board board) { - HDCDrawPosition(NULL, (BOOLEAN) fullRedraw, board); + HDCDrawPosition(NULL, (BOOLEAN) fullRedraw, board); } @@ -6551,6 +9671,7 @@ CommentPopUp(char *title, char *str) { HWND hwnd = GetActiveWindow(); EitherCommentPopUp(0, title, str, FALSE); + SAY(str); SetActiveWindow(hwnd); } @@ -6646,6 +9767,9 @@ UserName() static char buf[MSG_SIZ]; DWORD bufsiz = MSG_SIZ; + if(appData.userName != NULL && appData.userName[0] != 0) { + return appData.userName; /* [HGM] username: prefer name selected by user over his system login */ + } if (!GetUserName(buf, &bufsiz)) { /*DisplayError("Error getting user name", GetLastError());*/ strcpy(buf, "User"); @@ -6686,16 +9810,20 @@ void StartClockTimer(long millisec) { clockTimerEvent = SetTimer(hwndMain, (UINT) CLOCK_TIMER_ID, - (UINT) millisec, NULL); + (UINT) millisec, NULL); } void DisplayWhiteClock(long timeRemaining, int highlight) { HDC hdc; + char *flag = whiteFlag && gameMode == TwoMachinesPlay ? "(!)" : ""; + + if(appData.noGUI) return; hdc = GetDC(hwndMain); if (!IsIconic(hwndMain)) { - DisplayAClock(hdc, timeRemaining, highlight, &whiteRect, "White"); + DisplayAClock(hdc, timeRemaining, highlight, + flipClock ? &blackRect : &whiteRect, "White", flag); } if (highlight && iconCurrent == iconBlack) { iconCurrent = iconWhite; @@ -6713,9 +9841,13 @@ void DisplayBlackClock(long timeRemaining, int highlight) { HDC hdc; + char *flag = blackFlag && gameMode == TwoMachinesPlay ? "(!)" : ""; + + if(appData.noGUI) return; 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; @@ -6749,7 +9881,7 @@ void StartLoadGameTimer(long millisec) { loadGameTimerEvent = SetTimer(hwndMain, (UINT) LOAD_GAME_TIMER_ID, - (UINT) millisec, NULL); + (UINT) millisec, NULL); } void @@ -6760,10 +9892,10 @@ AutoSaveGame() char fileTitle[MSG_SIZ]; defName = DefaultFileName(appData.oldSaveStyle ? "gam" : "pgn"); - f = OpenFileDialog(hwndMain, TRUE, defName, - appData.oldSaveStyle ? "gam" : "pgn", - GAME_FILT, - "Save Game to File", NULL, fileTitle, NULL); + f = OpenFileDialog(hwndMain, "a", defName, + appData.oldSaveStyle ? "gam" : "pgn", + GAME_FILT, + "Save Game to File", NULL, fileTitle, NULL); if (f != NULL) { SaveGame(f, 0, ""); fclose(f); @@ -6775,16 +9907,17 @@ void ScheduleDelayedEvent(DelayedEventCallback cb, long millisec) { if (delayedTimerEvent != 0) { - if (appData.debugMode) { + if (appData.debugMode && cb != delayedTimerCallback) { // [HGM] alive: not too much debug fprintf(debugFP, "ScheduleDelayedEvent: event already scheduled\n"); } KillTimer(hwndMain, delayedTimerEvent); delayedTimerEvent = 0; + if(delayedTimerCallback != cb) // [HGM] alive: do not "flush" same event, just postpone it delayedTimerCallback(); } delayedTimerCallback = cb; delayedTimerEvent = SetTimer(hwndMain, (UINT) DELAYED_TIMER_ID, - (UINT) millisec, NULL); + (UINT) millisec, NULL); } DelayedEventCallback @@ -6806,6 +9939,23 @@ CancelDelayedEvent() } } +DWORD GetWin32Priority(int nice) +{ // [HGM] nice: translate Unix nice() value to indows priority class. (Code stolen from Polyglot 1.4w11) +/* +REALTIME_PRIORITY_CLASS 0x00000100 +HIGH_PRIORITY_CLASS 0x00000080 +ABOVE_NORMAL_PRIORITY_CLASS 0x00008000 +NORMAL_PRIORITY_CLASS 0x00000020 +BELOW_NORMAL_PRIORITY_CLASS 0x00004000 +IDLE_PRIORITY_CLASS 0x00000040 +*/ + if (nice < -15) return 0x00000080; + if (nice < 0) return 0x00008000; + if (nice == 0) return 0x00000020; + if (nice < 15) return 0x00004000; + return 0x00000040; +} + /* Start a child process running the given program. The process's standard output can be read from "from", and its standard input can be written to "to". @@ -6854,9 +10004,9 @@ StartChildProcess(char *cmdLine, char *dir, ProcRef *pr) /* Duplicate the read handle to the pipe, so it is not inherited. */ fSuccess = DuplicateHandle(GetCurrentProcess(), hChildStdoutRd, - GetCurrentProcess(), &hChildStdoutRdDup, 0, - FALSE, /* not inherited */ - DUPLICATE_SAME_ACCESS); + GetCurrentProcess(), &hChildStdoutRdDup, 0, + FALSE, /* not inherited */ + DUPLICATE_SAME_ACCESS); if (! fSuccess) { return GetLastError(); } @@ -6876,9 +10026,9 @@ StartChildProcess(char *cmdLine, char *dir, ProcRef *pr) /* Duplicate the write handle to the pipe, so it is not inherited. */ fSuccess = DuplicateHandle(GetCurrentProcess(), hChildStdinWr, - GetCurrentProcess(), &hChildStdinWrDup, 0, - FALSE, /* not inherited */ - DUPLICATE_SAME_ACCESS); + GetCurrentProcess(), &hChildStdinWrDup, 0, + FALSE, /* not inherited */ + DUPLICATE_SAME_ACCESS); if (! fSuccess) { return GetLastError(); } @@ -6905,15 +10055,15 @@ StartChildProcess(char *cmdLine, char *dir, ProcRef *pr) siStartInfo.hStdError = hChildStdoutWr; fSuccess = CreateProcess(NULL, - cmdLine, /* command line */ - NULL, /* process security attributes */ - NULL, /* primary thread security attrs */ - TRUE, /* handles are inherited */ - DETACHED_PROCESS|CREATE_NEW_PROCESS_GROUP, - NULL, /* use parent's environment */ - NULL, - &siStartInfo, /* STARTUPINFO pointer */ - &piProcInfo); /* receives PROCESS_INFORMATION */ + cmdLine, /* command line */ + NULL, /* process security attributes */ + NULL, /* primary thread security attrs */ + TRUE, /* handles are inherited */ + DETACHED_PROCESS|CREATE_NEW_PROCESS_GROUP, + NULL, /* use parent's environment */ + NULL, + &siStartInfo, /* STARTUPINFO pointer */ + &piProcInfo); /* receives PROCESS_INFORMATION */ err = GetLastError(); SetCurrentDirectory(buf); /* return to prev directory */ @@ -6921,6 +10071,11 @@ StartChildProcess(char *cmdLine, char *dir, ProcRef *pr) return err; } + if (appData.niceEngines){ // [HGM] nice: adjust engine proc priority + if(appData.debugMode) fprintf(debugFP, "nice engine proc to %d\n", appData.niceEngines); + SetPriorityClass(piProcInfo.hProcess, GetWin32Priority(appData.niceEngines)); + } + /* Close the handles we don't need in the parent */ CloseHandle(piProcInfo.hThread); CloseHandle(hChildStdinRd); @@ -6951,7 +10106,7 @@ StartChildProcess(char *cmdLine, char *dir, ProcRef *pr) void DestroyChildProcess(ProcRef pr, int/*boolean*/ signal) { - ChildProc *cp; + ChildProc *cp; int result; cp = (ChildProc *) pr; if (cp == NULL) return; @@ -6966,6 +10121,28 @@ 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 ) { + result = TerminateProcess( cp->hProcess, 0 ); + + if ( appData.debugMode) { + fprintf( debugFP, "Terminating process %lu, result=%d\n", cp->pid, result ); + } + } + else if( signal == 10 ) { + DWORD dw = WaitForSingleObject( cp->hProcess, 3*1000 ); // Wait 3 seconds at most + + if( dw != WAIT_OBJECT_0 ) { + result = TerminateProcess( cp->hProcess, 0 ); + + if ( appData.debugMode) { + fprintf( debugFP, "Process %lu still alive after timeout, killing... result=%d\n", cp->pid, result ); + } + + } + } + CloseHandle(cp->hProcess); break; @@ -7099,7 +10276,7 @@ OpenTCP(char *host, char *port, ProcRef *pr) /* Make connection */ if (connect(s, (struct sockaddr *) &sa, - sizeof(struct sockaddr_in)) == SOCKET_ERROR) { + sizeof(struct sockaddr_in)) == SOCKET_ERROR) { err = WSAGetLastError(); WSACleanup(); return err; @@ -7128,7 +10305,7 @@ OpenCommPort(char *name, ProcRef *pr) strcpy(fullname, name); h = CreateFile(name, GENERIC_READ | GENERIC_WRITE, - 0, NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL); + 0, NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL); if (h == (HANDLE) -1) { return GetLastError(); } @@ -7221,15 +10398,15 @@ OpenRcmd(char* host, char* user, char* cmd, ProcRef* pr) } if (s == INVALID_SOCKET) { if ((s = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) { - err = WSAGetLastError(); - WSACleanup(); - return err; + err = WSAGetLastError(); + WSACleanup(); + return err; } } uport = (unsigned short) fromPort; mysa.sin_port = htons(uport); if (bind(s, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in)) - == SOCKET_ERROR) { + == SOCKET_ERROR) { err = WSAGetLastError(); if (err == WSAEADDRINUSE) continue; WSACleanup(); @@ -7239,9 +10416,9 @@ OpenRcmd(char* host, char* user, char* cmd, ProcRef* pr) sizeof(struct sockaddr_in)) == SOCKET_ERROR) { err = WSAGetLastError(); if (err == WSAEADDRINUSE) { - closesocket(s); + closesocket(s); s = -1; - continue; + continue; } WSACleanup(); return err; @@ -7264,16 +10441,16 @@ OpenRcmd(char* host, char* user, char* cmd, ProcRef* pr) } if (s2 == INVALID_SOCKET) { if ((s2 = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) { - err = WSAGetLastError(); - closesocket(s); - WSACleanup(); - return err; + err = WSAGetLastError(); + closesocket(s); + WSACleanup(); + return err; } } uport = (unsigned short) fromPort; mysa.sin_port = htons(uport); if (bind(s2, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in)) - == SOCKET_ERROR) { + == SOCKET_ERROR) { err = WSAGetLastError(); if (err == WSAEADDRINUSE) continue; (void) closesocket(s); @@ -7283,9 +10460,9 @@ OpenRcmd(char* host, char* user, char* cmd, ProcRef* pr) if (listen(s2, 1) == SOCKET_ERROR) { err = WSAGetLastError(); if (err == WSAEADDRINUSE) { - closesocket(s2); - s2 = INVALID_SOCKET; - continue; + closesocket(s2); + s2 = INVALID_SOCKET; + continue; } (void) closesocket(s); (void) closesocket(s2); @@ -7350,9 +10527,9 @@ OpenRcmd(char* host, char* user, char* cmd, ProcRef* pr) InputSourceRef AddInputSource(ProcRef pr, int lineByLine, - InputCallback func, VOIDSTAR closure) + InputCallback func, VOIDSTAR closure) { - InputSource *is, *is2; + InputSource *is, *is2 = NULL; ChildProc *cp = (ChildProc *) pr; is = (InputSource *) calloc(1, sizeof(InputSource)); @@ -7366,28 +10543,33 @@ 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 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, 0, &is->id); + CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) NonOvlInputThread, + (LPVOID) is, CREATE_SUSPENDED, &is->id); break; case CPComm: is->hFile = cp->hFrom; cp->hFrom = NULL; /* now owned by InputThread */ is->hThread = - CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) InputThread, - (LPVOID) is, 0, &is->id); + CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) InputThread, + (LPVOID) is, CREATE_SUSPENDED, &is->id); break; case CPSock: is->sock = cp->sock; is->hThread = - CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread, - (LPVOID) is, 0, &is->id); + CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread, + (LPVOID) is, CREATE_SUSPENDED, &is->id); break; case CPRcmd: @@ -7398,14 +10580,23 @@ AddInputSource(ProcRef pr, int lineByLine, is2->sock = cp->sock2; is2->second = is2; is->hThread = - CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread, - (LPVOID) is, 0, &is->id); + CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread, + (LPVOID) is, CREATE_SUSPENDED, &is->id); is2->hThread = - CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread, - (LPVOID) is2, 0, &is2->id); + CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread, + (LPVOID) is2, CREATE_SUSPENDED, &is2->id); break; } + + if( is->hThread != NULL ) { + ResumeThread( is->hThread ); + } + + if( is2 != NULL && is2->hThread != NULL ) { + ResumeThread( is2->hThread ); + } } + return (InputSourceRef) is; } @@ -7435,7 +10626,7 @@ OutputToProcess(ProcRef pr, char *message, int count, int *outError) if (pr == NoProc) { ConsoleOutput(message, count, FALSE); return count; - } + } if (ovl.hEvent == NULL) { ovl.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL); @@ -7455,7 +10646,7 @@ OutputToProcess(ProcRef pr, char *message, int count, int *outError) case CPReal: if (WriteFile(((ChildProc *)pr)->hTo, message, count, - &dOutCount, NULL)) { + &dOutCount, NULL)) { *outError = NO_ERROR; outCount = (int) dOutCount; } else { @@ -7465,7 +10656,7 @@ OutputToProcess(ProcRef pr, char *message, int count, int *outError) case CPComm: *outError = DoWriteFile(((ChildProc *)pr)->hTo, message, count, - &dOutCount, &ovl); + &dOutCount, &ovl); if (*outError == NO_ERROR) { outCount = (int) dOutCount; } @@ -7476,7 +10667,7 @@ OutputToProcess(ProcRef pr, char *message, int count, int *outError) int OutputToProcessDelayed(ProcRef pr, char *message, int count, int *outError, - long msdelay) + long msdelay) { /* Ignore delay, not implemented for WinBoard */ return OutputToProcess(pr, message, count, outError); @@ -7485,7 +10676,7 @@ OutputToProcessDelayed(ProcRef pr, char *message, int count, int *outError, void CmailSigHandlerCallBack(InputSourceRef isr, VOIDSTAR closure, - char *buf, int count, int error) + char *buf, int count, int error) { DisplayFatalError("Not implemented", 0, 1); } @@ -7516,7 +10707,7 @@ StartAnalysisClock() { if (analysisTimerEvent) return; analysisTimerEvent = SetTimer(hwndMain, (UINT) ANALYSIS_TIMER_ID, - (UINT) 2000, NULL); + (UINT) 2000, NULL); } LRESULT CALLBACK @@ -7542,26 +10733,26 @@ AnalysisDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam) sizeX = rect.right; sizeY = rect.bottom; if (analysisX != CW_USEDEFAULT && analysisY != CW_USEDEFAULT && - analysisW != CW_USEDEFAULT && analysisH != CW_USEDEFAULT) { - WINDOWPLACEMENT wp; - EnsureOnScreen(&analysisX, &analysisY); - wp.length = sizeof(WINDOWPLACEMENT); - wp.flags = 0; - wp.showCmd = SW_SHOW; - wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0; - wp.rcNormalPosition.left = analysisX; - wp.rcNormalPosition.right = analysisX + analysisW; - wp.rcNormalPosition.top = analysisY; - wp.rcNormalPosition.bottom = analysisY + analysisH; - SetWindowPlacement(hDlg, &wp); - - GetClientRect(hDlg, &rect); - newSizeX = rect.right; - newSizeY = rect.bottom; + analysisW != CW_USEDEFAULT && analysisH != CW_USEDEFAULT) { + WINDOWPLACEMENT wp; + EnsureOnScreen(&analysisX, &analysisY, 0, 0); + wp.length = sizeof(WINDOWPLACEMENT); + wp.flags = 0; + wp.showCmd = SW_SHOW; + wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0; + wp.rcNormalPosition.left = analysisX; + wp.rcNormalPosition.right = analysisX + analysisW; + wp.rcNormalPosition.top = analysisY; + wp.rcNormalPosition.bottom = analysisY + analysisH; + SetWindowPlacement(hDlg, &wp); + + GetClientRect(hDlg, &rect); + newSizeX = rect.right; + newSizeY = rect.bottom; ResizeEditPlusButtons(hDlg, hwndText, sizeX, sizeY, - newSizeX, newSizeY); - sizeX = newSizeX; - sizeY = newSizeY; + newSizeX, newSizeY); + sizeX = newSizeX; + sizeY = newSizeY; } } return FALSE; @@ -7569,6 +10760,11 @@ AnalysisDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam) case WM_COMMAND: /* message: received a command */ switch (LOWORD(wParam)) { case IDCANCEL: + if (appData.icsActive && appData.icsEngineAnalyze) { /* [DM] icsEngineAnalyze */ + ExitAnalyzeMode(); + ModeHighlight(); + return TRUE; + } EditGameEvent(); return TRUE; default: @@ -7600,6 +10796,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; @@ -7619,10 +10819,10 @@ AnalysisPopUp(char* title, char* str) analysisTitle = title; lpProc = MakeProcInstance((FARPROC)AnalysisDialog, hInst); CreateDialog(hInst, MAKEINTRESOURCE(DLG_Analysis), - hwndMain, (DLGPROC)lpProc); + hwndMain, (DLGPROC)lpProc); FreeProcInstance(lpProc); } - analysisDialogUp = TRUE; + analysisDialogUp = TRUE; } VOID @@ -7631,7 +10831,7 @@ AnalysisPopDown() if (analysisDialog) { ShowWindow(analysisDialog, SW_HIDE); } - analysisDialogUp = FALSE; + analysisDialogUp = FALSE; } @@ -7647,7 +10847,7 @@ SetHighlights(int fromX, int fromY, int toX, int toY) VOID ClearHighlights() { - highlightInfo.sq[0].x = highlightInfo.sq[0].y = + highlightInfo.sq[0].x = highlightInfo.sq[0].y = highlightInfo.sq[1].x = highlightInfo.sq[1].y = -1; } @@ -7663,7 +10863,7 @@ SetPremoveHighlights(int fromX, int fromY, int toX, int toY) VOID ClearPremoveHighlights() { - premoveHighlightInfo.sq[0].x = premoveHighlightInfo.sq[0].y = + premoveHighlightInfo.sq[0].x = premoveHighlightInfo.sq[0].y = premoveHighlightInfo.sq[1].x = premoveHighlightInfo.sq[1].y = -1; } @@ -7691,6 +10891,24 @@ static void Tween( POINT * start, POINT * mid, POINT * finish, int factor, POINT frames[], int * nFrames); +void +AnimateAtomicCapture(int fromX, int fromY, int toX, int toY, int nFrames) +{ // [HGM] atomic: animate blast wave + int i; +if(appData.debugMode) fprintf(debugFP, "exploding (%d,%d)\n", toX, toY); + explodeInfo.fromX = fromX; + explodeInfo.fromY = fromY; + explodeInfo.toX = toX; + explodeInfo.toY = toY; + for(i=1; ix = 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); } } @@ -7795,11 +11016,11 @@ Tween(start, mid, finish, factor, frames, nFrames) count ++; fraction = fraction / 2; } - + /* Midpoint */ frames[count] = *mid; count ++; - + /* Slow out, stepping 1/2, then 1/4, ... */ fraction = 2; for (n = 0; n < factor; n++) { @@ -7812,9 +11033,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 ); +}