2 * WinBoard.c -- Windows NT front end to XBoard
\r
4 * Copyright 1991 by Digital Equipment Corporation, Maynard,
\r
7 * Enhancements Copyright 1992-2001, 2002, 2003, 2004, 2005, 2006,
\r
8 * 2007, 2008, 2009 Free Software Foundation, Inc.
\r
10 * Enhancements Copyright 2005 Alessandro Scotti
\r
12 * XBoard borrows its colors and the bitmaps.xchess bitmap set from XChess,
\r
13 * which was written and is copyrighted by Wayne Christopher.
\r
15 * The following terms apply to Digital Equipment Corporation's copyright
\r
16 * interest in XBoard:
\r
17 * ------------------------------------------------------------------------
\r
18 * All Rights Reserved
\r
20 * Permission to use, copy, modify, and distribute this software and its
\r
21 * documentation for any purpose and without fee is hereby granted,
\r
22 * provided that the above copyright notice appear in all copies and that
\r
23 * both that copyright notice and this permission notice appear in
\r
24 * supporting documentation, and that the name of Digital not be
\r
25 * used in advertising or publicity pertaining to distribution of the
\r
26 * software without specific, written prior permission.
\r
28 * DIGITAL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
\r
29 * ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL
\r
30 * DIGITAL BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
\r
31 * ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
\r
32 * WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
\r
33 * ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
\r
35 * ------------------------------------------------------------------------
\r
37 * The following terms apply to the enhanced version of XBoard
\r
38 * distributed by the Free Software Foundation:
\r
39 * ------------------------------------------------------------------------
\r
41 * GNU XBoard is free software: you can redistribute it and/or modify
\r
42 * it under the terms of the GNU General Public License as published by
\r
43 * the Free Software Foundation, either version 3 of the License, or (at
\r
44 * your option) any later version.
\r
46 * GNU XBoard is distributed in the hope that it will be useful, but
\r
47 * WITHOUT ANY WARRANTY; without even the implied warranty of
\r
48 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
\r
49 * General Public License for more details.
\r
51 * You should have received a copy of the GNU General Public License
\r
52 * along with this program. If not, see http://www.gnu.org/licenses/. *
\r
54 *------------------------------------------------------------------------
\r
55 ** See the file ChangeLog for a revision history. */
\r
59 #include <windows.h>
\r
60 #include <winuser.h>
\r
61 #include <winsock.h>
\r
62 #include <commctrl.h>
\r
68 #include <sys/stat.h>
\r
71 #include <commdlg.h>
\r
73 #include <richedit.h>
\r
74 #include <mmsystem.h>
\r
83 #include "frontend.h"
\r
84 #include "backend.h"
\r
85 #include "winboard.h"
\r
87 #include "wclipbrd.h"
\r
88 #include "woptions.h"
\r
89 #include "wsockerr.h"
\r
90 #include "defaults.h"
\r
94 //void InitEngineUCI( const char * iniDir, ChessProgramState * cps );
\r
97 void mysrandom(unsigned int seed);
\r
99 extern int whiteFlag, blackFlag;
\r
100 Boolean flipClock = FALSE;
\r
101 extern HANDLE chatHandle[];
\r
102 extern int ics_type;
\r
104 void DisplayHoldingsCount(HDC hdc, int x, int y, int align, int copyNumber);
\r
105 VOID NewVariantPopup(HWND hwnd);
\r
106 int FinishMove P((ChessMove moveType, int fromX, int fromY, int toX, int toY,
\r
107 /*char*/int promoChar));
\r
108 void AnimateAtomicCapture(int fromX, int fromY, int toX, int toY, int nFrames);
\r
109 void DisplayMove P((int moveNumber));
\r
110 Boolean ParseFEN P((Board board, int *blackPlaysFirst, char *fen));
\r
111 void ChatPopUp P(());
\r
113 ChessSquare piece;
\r
114 POINT pos; /* window coordinates of current pos */
\r
115 POINT lastpos; /* window coordinates of last pos - used for clipping */
\r
116 POINT from; /* board coordinates of the piece's orig pos */
\r
117 POINT to; /* board coordinates of the piece's new pos */
\r
120 static AnimInfo animInfo = { EmptySquare, {-1,-1}, {-1,-1}, {-1,-1} };
\r
123 POINT start; /* window coordinates of start pos */
\r
124 POINT pos; /* window coordinates of current pos */
\r
125 POINT lastpos; /* window coordinates of last pos - used for clipping */
\r
126 POINT from; /* board coordinates of the piece's orig pos */
\r
129 static DragInfo dragInfo = { {-1,-1}, {-1,-1}, {-1,-1}, {-1,-1} };
\r
132 POINT sq[2]; /* board coordinates of from, to squares */
\r
135 static HighlightInfo highlightInfo = { {{-1, -1}, {-1, -1}} };
\r
136 static HighlightInfo premoveHighlightInfo = { {{-1, -1}, {-1, -1}} };
\r
138 typedef struct { // [HGM] atomic
\r
139 int fromX, fromY, toX, toY, radius;
\r
142 static ExplodeInfo explodeInfo;
\r
144 /* Window class names */
\r
145 char szAppName[] = "WinBoard";
\r
146 char szConsoleName[] = "WBConsole";
\r
148 /* Title bar text */
\r
149 char szTitle[] = "WinBoard";
\r
150 char szConsoleTitle[] = "I C S Interaction";
\r
153 char *settingsFileName;
\r
154 Boolean saveSettingsOnExit;
\r
155 char installDir[MSG_SIZ];
\r
157 BoardSize boardSize;
\r
158 Boolean chessProgram;
\r
159 //static int boardX, boardY;
\r
160 int minX, minY; // [HGM] placement: volatile limits on upper-left corner
\r
161 static int squareSize, lineGap, minorSize;
\r
162 static int winW, winH;
\r
163 static RECT messageRect, whiteRect, blackRect, leftLogoRect, rightLogoRect; // [HGM] logo
\r
164 static int logoHeight = 0;
\r
165 static char messageText[MESSAGE_TEXT_MAX];
\r
166 static int clockTimerEvent = 0;
\r
167 static int loadGameTimerEvent = 0;
\r
168 static int analysisTimerEvent = 0;
\r
169 static DelayedEventCallback delayedTimerCallback;
\r
170 static int delayedTimerEvent = 0;
\r
171 static int buttonCount = 2;
\r
172 char *icsTextMenuString;
\r
174 char *firstChessProgramNames;
\r
175 char *secondChessProgramNames;
\r
177 #define PALETTESIZE 256
\r
179 HINSTANCE hInst; /* current instance */
\r
180 Boolean alwaysOnTop = FALSE;
\r
182 COLORREF lightSquareColor, darkSquareColor, whitePieceColor,
\r
183 blackPieceColor, highlightSquareColor, premoveHighlightColor;
\r
185 ColorClass currentColorClass;
\r
187 HWND hCommPort = NULL; /* currently open comm port */
\r
188 static HWND hwndPause; /* pause button */
\r
189 static HBITMAP pieceBitmap[3][(int) BlackPawn]; /* [HGM] nr of bitmaps referred to bP in stead of wK */
\r
190 static HBRUSH lightSquareBrush, darkSquareBrush,
\r
191 blackSquareBrush, /* [HGM] for band between board and holdings */
\r
192 explodeBrush, /* [HGM] atomic */
\r
193 whitePieceBrush, blackPieceBrush, iconBkgndBrush /*, outlineBrush*/;
\r
194 static POINT gridEndpoints[(BOARD_RANKS + BOARD_FILES + 2) * 2];
\r
195 static DWORD gridVertexCounts[BOARD_RANKS + BOARD_FILES + 2];
\r
196 static HPEN gridPen = NULL;
\r
197 static HPEN highlightPen = NULL;
\r
198 static HPEN premovePen = NULL;
\r
199 static NPLOGPALETTE pLogPal;
\r
200 static BOOL paletteChanged = FALSE;
\r
201 static HICON iconWhite, iconBlack, iconCurrent;
\r
202 static int doingSizing = FALSE;
\r
203 static int lastSizing = 0;
\r
204 static int prevStderrPort;
\r
205 static HBITMAP userLogo;
\r
207 static HBITMAP liteBackTexture = NULL;
\r
208 static HBITMAP darkBackTexture = NULL;
\r
209 static int liteBackTextureMode = BACK_TEXTURE_MODE_PLAIN;
\r
210 static int darkBackTextureMode = BACK_TEXTURE_MODE_PLAIN;
\r
211 static int backTextureSquareSize = 0;
\r
212 static struct { int x; int y; int mode; } backTextureSquareInfo[BOARD_RANKS][BOARD_FILES];
\r
214 #if __GNUC__ && !defined(_winmajor)
\r
215 #define oldDialog 0 /* cygwin doesn't define _winmajor; mingw does */
\r
217 #if defined(_winmajor)
\r
218 #define oldDialog (_winmajor < 4)
\r
220 #define oldDialog 0
\r
230 int cliWidth, cliHeight;
\r
233 SizeInfo sizeInfo[] =
\r
235 { "tiny", 21, 0, 1, 1, 0, 0 },
\r
236 { "teeny", 25, 1, 1, 1, 0, 0 },
\r
237 { "dinky", 29, 1, 1, 1, 0, 0 },
\r
238 { "petite", 33, 1, 1, 1, 0, 0 },
\r
239 { "slim", 37, 2, 1, 0, 0, 0 },
\r
240 { "small", 40, 2, 1, 0, 0, 0 },
\r
241 { "mediocre", 45, 2, 1, 0, 0, 0 },
\r
242 { "middling", 49, 2, 0, 0, 0, 0 },
\r
243 { "average", 54, 2, 0, 0, 0, 0 },
\r
244 { "moderate", 58, 3, 0, 0, 0, 0 },
\r
245 { "medium", 64, 3, 0, 0, 0, 0 },
\r
246 { "bulky", 72, 3, 0, 0, 0, 0 },
\r
247 { "large", 80, 3, 0, 0, 0, 0 },
\r
248 { "big", 87, 3, 0, 0, 0, 0 },
\r
249 { "huge", 95, 3, 0, 0, 0, 0 },
\r
250 { "giant", 108, 3, 0, 0, 0, 0 },
\r
251 { "colossal", 116, 4, 0, 0, 0, 0 },
\r
252 { "titanic", 129, 4, 0, 0, 0, 0 },
\r
253 { NULL, 0, 0, 0, 0, 0, 0 }
\r
256 #define MF(x) {x, {{0,}, 0. }, {0, }, 0}
\r
257 MyFont fontRec[NUM_SIZES][NUM_FONTS] =
\r
259 { 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) },
\r
260 { 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) },
\r
261 { 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) },
\r
262 { 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) },
\r
263 { 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) },
\r
264 { 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) },
\r
265 { 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) },
\r
266 { 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) },
\r
267 { 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) },
\r
268 { 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) },
\r
269 { 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) },
\r
270 { 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) },
\r
271 { 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) },
\r
272 { 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) },
\r
273 { 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) },
\r
274 { 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) },
\r
275 { 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) },
\r
276 { 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) },
\r
279 MyFont *font[NUM_SIZES][NUM_FONTS];
\r
288 #define BUTTON_WIDTH (tinyLayout ? 16 : 32)
\r
289 #define N_BUTTONS 5
\r
291 MyButtonDesc buttonDesc[N_BUTTONS] =
\r
293 {"<<", IDM_ToStart, NULL, NULL},
\r
294 {"<", IDM_Backward, NULL, NULL},
\r
295 {"P", IDM_Pause, NULL, NULL},
\r
296 {">", IDM_Forward, NULL, NULL},
\r
297 {">>", IDM_ToEnd, NULL, NULL},
\r
300 int tinyLayout = 0, smallLayout = 0;
\r
301 #define MENU_BAR_ITEMS 7
\r
302 char *menuBarText[2][MENU_BAR_ITEMS+1] = {
\r
303 { "&File", "&Mode", "&Action", "&Step", "&Options", "&Help", NULL },
\r
304 { "&F", "&M", "&A", "&S", "&O", "&H", NULL },
\r
308 MySound sounds[(int)NSoundClasses];
\r
309 MyTextAttribs textAttribs[(int)NColorClasses];
\r
311 MyColorizeAttribs colorizeAttribs[] = {
\r
312 { (COLORREF)0, 0, "Shout Text" },
\r
313 { (COLORREF)0, 0, "SShout/CShout" },
\r
314 { (COLORREF)0, 0, "Channel 1 Text" },
\r
315 { (COLORREF)0, 0, "Channel Text" },
\r
316 { (COLORREF)0, 0, "Kibitz Text" },
\r
317 { (COLORREF)0, 0, "Tell Text" },
\r
318 { (COLORREF)0, 0, "Challenge Text" },
\r
319 { (COLORREF)0, 0, "Request Text" },
\r
320 { (COLORREF)0, 0, "Seek Text" },
\r
321 { (COLORREF)0, 0, "Normal Text" },
\r
322 { (COLORREF)0, 0, "None" }
\r
327 static char *commentTitle;
\r
328 static char *commentText;
\r
329 static int commentIndex;
\r
330 static Boolean editComment = FALSE;
\r
333 char errorTitle[MSG_SIZ];
\r
334 char errorMessage[2*MSG_SIZ];
\r
335 HWND errorDialog = NULL;
\r
336 BOOLEAN moveErrorMessageUp = FALSE;
\r
337 BOOLEAN consoleEcho = TRUE;
\r
338 CHARFORMAT consoleCF;
\r
339 COLORREF consoleBackgroundColor;
\r
341 char *programVersion;
\r
347 typedef int CPKind;
\r
356 SOCKET sock2; /* stderr socket for OpenRcmd */
\r
359 #define INPUT_SOURCE_BUF_SIZE 4096
\r
361 typedef struct _InputSource {
\r
368 char buf[INPUT_SOURCE_BUF_SIZE];
\r
372 InputCallback func;
\r
373 struct _InputSource *second; /* for stderr thread on CPRcmd */
\r
377 InputSource *consoleInputSource;
\r
382 VOID ConsoleOutput(char* data, int length, int forceVisible);
\r
383 VOID ConsoleCreate();
\r
385 ConsoleWndProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam);
\r
386 VOID ColorizeTextPopup(HWND hwnd, ColorClass cc);
\r
387 VOID PrintCommSettings(FILE *f, char *name, DCB *dcb);
\r
388 VOID ParseCommSettings(char *arg, DCB *dcb);
\r
390 StartupDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam);
\r
391 VOID APIENTRY MenuPopup(HWND hwnd, POINT pt, HMENU hmenu, UINT def);
\r
392 void ParseIcsTextMenu(char *icsTextMenuString);
\r
393 VOID PopUpMoveDialog(char firstchar);
\r
394 VOID PopUpNameDialog(char firstchar);
\r
395 VOID UpdateSampleText(HWND hDlg, int id, MyColorizeAttribs *mca);
\r
399 int GameListOptions();
\r
401 int dummy; // [HGM] for obsolete args
\r
403 HWND hwndMain = NULL; /* root window*/
\r
404 HWND hwndConsole = NULL;
\r
405 HWND commentDialog = NULL;
\r
406 HWND moveHistoryDialog = NULL;
\r
407 HWND evalGraphDialog = NULL;
\r
408 HWND engineOutputDialog = NULL;
\r
409 HWND gameListDialog = NULL;
\r
410 HWND editTagsDialog = NULL;
\r
412 int commentUp = FALSE;
\r
414 WindowPlacement wpMain;
\r
415 WindowPlacement wpConsole;
\r
416 WindowPlacement wpComment;
\r
417 WindowPlacement wpMoveHistory;
\r
418 WindowPlacement wpEvalGraph;
\r
419 WindowPlacement wpEngineOutput;
\r
420 WindowPlacement wpGameList;
\r
421 WindowPlacement wpTags;
\r
423 VOID EngineOptionsPopup(); // [HGM] settings
\r
425 VOID GothicPopUp(char *title, VariantClass variant);
\r
427 * Setting "frozen" should disable all user input other than deleting
\r
428 * the window. We do this while engines are initializing themselves.
\r
430 static int frozen = 0;
\r
431 static int oldMenuItemState[MENU_BAR_ITEMS];
\r
437 if (frozen) return;
\r
439 hmenu = GetMenu(hwndMain);
\r
440 for (i=0; i<MENU_BAR_ITEMS; i++) {
\r
441 oldMenuItemState[i] = EnableMenuItem(hmenu, i, MF_BYPOSITION|MF_GRAYED);
\r
443 DrawMenuBar(hwndMain);
\r
446 /* Undo a FreezeUI */
\r
452 if (!frozen) return;
\r
454 hmenu = GetMenu(hwndMain);
\r
455 for (i=0; i<MENU_BAR_ITEMS; i++) {
\r
456 EnableMenuItem(hmenu, i, MF_BYPOSITION|oldMenuItemState[i]);
\r
458 DrawMenuBar(hwndMain);
\r
461 /*static*/ int fromX = -1, fromY = -1, toX, toY; // [HGM] moved upstream, so JAWS can use them
\r
463 /* JAWS preparation patch (WinBoard for the sight impaired). Define required insertions as empty */
\r
469 #define JAWS_ALT_INTERCEPT
\r
470 #define JAWS_KB_NAVIGATION
\r
471 #define JAWS_MENU_ITEMS
\r
472 #define JAWS_SILENCE
\r
473 #define JAWS_REPLAY
\r
475 #define JAWS_COPYRIGHT
\r
476 #define JAWS_DELETE(X) X
\r
477 #define SAYMACHINEMOVE()
\r
481 /*---------------------------------------------------------------------------*\
\r
485 \*---------------------------------------------------------------------------*/
\r
488 WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
\r
489 LPSTR lpCmdLine, int nCmdShow)
\r
492 HANDLE hAccelMain, hAccelNoAlt, hAccelNoICS;
\r
493 // INITCOMMONCONTROLSEX ex;
\r
497 LoadLibrary("RICHED32.DLL");
\r
498 consoleCF.cbSize = sizeof(CHARFORMAT);
\r
500 if (!InitApplication(hInstance)) {
\r
503 if (!InitInstance(hInstance, nCmdShow, lpCmdLine)) {
\r
509 // InitCommonControlsEx(&ex);
\r
510 InitCommonControls();
\r
512 hAccelMain = LoadAccelerators (hInstance, szAppName);
\r
513 hAccelNoAlt = LoadAccelerators (hInstance, "NO_ALT");
\r
514 hAccelNoICS = LoadAccelerators( hInstance, "NO_ICS"); /* [AS] No Ctrl-V on ICS!!! */
\r
516 /* Acquire and dispatch messages until a WM_QUIT message is received. */
\r
518 while (GetMessage(&msg, /* message structure */
\r
519 NULL, /* handle of window receiving the message */
\r
520 0, /* lowest message to examine */
\r
521 0)) /* highest message to examine */
\r
524 if(msg.message == WM_CHAR && msg.wParam == '\t') {
\r
525 // [HGM] navigate: switch between all windows with tab
\r
526 HWND e1 = NULL, e2 = NULL, mh = NULL, hInput = NULL, hText = NULL;
\r
527 int i, currentElement = 0;
\r
529 // first determine what element of the chain we come from (if any)
\r
530 if(appData.icsActive) {
\r
531 hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
532 hText = GetDlgItem(hwndConsole, OPT_ConsoleText);
\r
534 if(engineOutputDialog && EngineOutputIsUp()) {
\r
535 e1 = GetDlgItem(engineOutputDialog, IDC_EngineMemo1);
\r
536 e2 = GetDlgItem(engineOutputDialog, IDC_EngineMemo2);
\r
538 if(moveHistoryDialog && MoveHistoryIsUp()) {
\r
539 mh = GetDlgItem(moveHistoryDialog, IDC_MoveHistory);
\r
541 if(msg.hwnd == hwndMain) currentElement = 7 ; else
\r
542 if(msg.hwnd == engineOutputDialog) currentElement = 2; else
\r
543 if(msg.hwnd == e1) currentElement = 2; else
\r
544 if(msg.hwnd == e2) currentElement = 3; else
\r
545 if(msg.hwnd == moveHistoryDialog) currentElement = 4; else
\r
546 if(msg.hwnd == mh) currentElement = 4; else
\r
547 if(msg.hwnd == evalGraphDialog) currentElement = 6; else
\r
548 if(msg.hwnd == hText) currentElement = 5; else
\r
549 if(msg.hwnd == hInput) currentElement = 6; else
\r
550 for (i = 0; i < N_BUTTONS; i++) {
\r
551 if (buttonDesc[i].hwnd == msg.hwnd) { currentElement = 1; break; }
\r
554 // determine where to go to
\r
555 if(currentElement) { HWND h = NULL; int direction = GetKeyState(VK_SHIFT) < 0 ? -1 : 1;
\r
557 currentElement = (currentElement + direction) % 7;
\r
558 switch(currentElement) {
\r
560 h = hwndMain; break; // passing this case always makes the loop exit
\r
562 h = buttonDesc[0].hwnd; break; // could be NULL
\r
564 if(!EngineOutputIsUp()) continue; // skip closed auxiliary windows
\r
567 if(!EngineOutputIsUp()) continue;
\r
570 if(!MoveHistoryIsUp()) continue;
\r
572 // case 6: // input to eval graph does not seem to get here!
\r
573 // if(!EvalGraphIsUp()) continue;
\r
574 // h = evalGraphDialog; break;
\r
576 if(!appData.icsActive) continue;
\r
580 if(!appData.icsActive) continue;
\r
586 if(currentElement > 4 && IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);
\r
587 if(currentElement < 5 && IsIconic(hwndMain)) ShowWindow(hwndMain, SW_RESTORE); // all open together
\r
590 continue; // this message now has been processed
\r
594 if (!(commentDialog && IsDialogMessage(commentDialog, &msg)) &&
\r
595 !(moveHistoryDialog && IsDialogMessage(moveHistoryDialog, &msg)) &&
\r
596 !(evalGraphDialog && IsDialogMessage(evalGraphDialog, &msg)) &&
\r
597 !(engineOutputDialog && IsDialogMessage(engineOutputDialog, &msg)) &&
\r
598 !(editTagsDialog && IsDialogMessage(editTagsDialog, &msg)) &&
\r
599 !(gameListDialog && IsDialogMessage(gameListDialog, &msg)) &&
\r
600 !(errorDialog && IsDialogMessage(errorDialog, &msg)) &&
\r
601 !(!frozen && TranslateAccelerator(hwndMain, hAccelMain, &msg)) && JAWS_ACCEL
\r
602 !(!hwndConsole && TranslateAccelerator(hwndMain, hAccelNoICS, &msg)) &&
\r
603 !(!hwndConsole && TranslateAccelerator(hwndMain, hAccelNoAlt, &msg))) {
\r
604 int done = 0, i; // [HGM] chat: dispatch cat-box messages
\r
605 for(i=0; i<MAX_CHAT; i++)
\r
606 if(chatHandle[i] && IsDialogMessage(chatHandle[i], &msg)) {
\r
609 if(done) continue; // [HGM] chat: end patch
\r
610 TranslateMessage(&msg); /* Translates virtual key codes */
\r
611 DispatchMessage(&msg); /* Dispatches message to window */
\r
616 return (msg.wParam); /* Returns the value from PostQuitMessage */
\r
619 /*---------------------------------------------------------------------------*\
\r
621 * Initialization functions
\r
623 \*---------------------------------------------------------------------------*/
\r
627 { // update user logo if necessary
\r
628 static char oldUserName[MSG_SIZ], *curName;
\r
630 if(appData.autoLogo) {
\r
631 curName = UserName();
\r
632 if(strcmp(curName, oldUserName)) {
\r
633 sprintf(oldUserName, "logos\\%s.bmp", curName);
\r
634 userLogo = LoadImage( 0, oldUserName, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );
\r
635 strcpy(oldUserName, curName);
\r
641 InitApplication(HINSTANCE hInstance)
\r
645 /* Fill in window class structure with parameters that describe the */
\r
648 wc.style = CS_HREDRAW | CS_VREDRAW; /* Class style(s). */
\r
649 wc.lpfnWndProc = (WNDPROC)WndProc; /* Window Procedure */
\r
650 wc.cbClsExtra = 0; /* No per-class extra data. */
\r
651 wc.cbWndExtra = 0; /* No per-window extra data. */
\r
652 wc.hInstance = hInstance; /* Owner of this class */
\r
653 wc.hIcon = LoadIcon(hInstance, "icon_white");
\r
654 wc.hCursor = LoadCursor(NULL, IDC_ARROW); /* Cursor */
\r
655 wc.hbrBackground = (HBRUSH)(COLOR_WINDOW+1); /* Default color */
\r
656 wc.lpszMenuName = szAppName; /* Menu name from .RC */
\r
657 wc.lpszClassName = szAppName; /* Name to register as */
\r
659 /* Register the window class and return success/failure code. */
\r
660 if (!RegisterClass(&wc)) return FALSE;
\r
662 wc.style = CS_HREDRAW | CS_VREDRAW;
\r
663 wc.lpfnWndProc = (WNDPROC)ConsoleWndProc;
\r
665 wc.cbWndExtra = DLGWINDOWEXTRA;
\r
666 wc.hInstance = hInstance;
\r
667 wc.hIcon = LoadIcon(hInstance, "icon_white");
\r
668 wc.hCursor = LoadCursor(NULL, IDC_ARROW);
\r
669 wc.hbrBackground = (HBRUSH)(COLOR_MENU+1);
\r
670 wc.lpszMenuName = NULL;
\r
671 wc.lpszClassName = szConsoleName;
\r
673 if (!RegisterClass(&wc)) return FALSE;
\r
678 /* Set by InitInstance, used by EnsureOnScreen */
\r
679 int screenHeight, screenWidth;
\r
682 EnsureOnScreen(int *x, int *y, int minX, int minY)
\r
684 // int gap = GetSystemMetrics(SM_CYFRAME) + GetSystemMetrics(SM_CYCAPTION);
\r
685 /* Be sure window at (x,y) is not off screen (or even mostly off screen) */
\r
686 if (*x > screenWidth - 32) *x = 0;
\r
687 if (*y > screenHeight - 32) *y = 0;
\r
688 if (*x < minX) *x = minX;
\r
689 if (*y < minY) *y = minY;
\r
693 InitInstance(HINSTANCE hInstance, int nCmdShow, LPSTR lpCmdLine)
\r
695 HWND hwnd; /* Main window handle. */
\r
697 WINDOWPLACEMENT wp;
\r
700 hInst = hInstance; /* Store instance handle in our global variable */
\r
701 programName = szAppName;
\r
703 if (SearchPath(NULL, "WinBoard.exe", NULL, MSG_SIZ, installDir, &filepart)) {
\r
704 *filepart = NULLCHAR;
\r
706 GetCurrentDirectory(MSG_SIZ, installDir);
\r
708 gameInfo.boardWidth = gameInfo.boardHeight = 8; // [HGM] won't have open window otherwise
\r
709 screenWidth = screenHeight = 1000; // [HGM] placement: kludge to allow calling EnsureOnScreen from InitAppData
\r
710 InitAppData(lpCmdLine); /* Get run-time parameters */
\r
711 /* xboard, and older WinBoards, controlled the move sound with the
\r
712 appData.ringBellAfterMoves option. In the current WinBoard, we
\r
713 always turn the option on (so that the backend will call us),
\r
714 then let the user turn the sound off by setting it to silence if
\r
715 desired. To accommodate old winboard.ini files saved by old
\r
716 versions of WinBoard, we also turn off the sound if the option
\r
717 was initially set to false. [HGM] taken out of InitAppData */
\r
718 if (!appData.ringBellAfterMoves) {
\r
719 sounds[(int)SoundMove].name = strdup("");
\r
720 appData.ringBellAfterMoves = TRUE;
\r
722 if (appData.debugMode) {
\r
723 debugFP = fopen(appData.nameOfDebugFile, "w");
\r
724 setbuf(debugFP, NULL);
\r
729 // InitEngineUCI( installDir, &first ); // [HGM] incorporated in InitBackEnd1()
\r
730 // InitEngineUCI( installDir, &second );
\r
732 /* Create a main window for this application instance. */
\r
733 hwnd = CreateWindow(szAppName, szTitle,
\r
734 (WS_OVERLAPPEDWINDOW & ~WS_MAXIMIZEBOX),
\r
735 CW_USEDEFAULT, 0, CW_USEDEFAULT, 0,
\r
736 NULL, NULL, hInstance, NULL);
\r
739 /* If window could not be created, return "failure" */
\r
744 /* [HGM] logo: Load logos if specified (must be done before InitDrawingSizes) */
\r
745 if( appData.firstLogo && appData.firstLogo[0] != NULLCHAR) {
\r
746 first.programLogo = LoadImage( 0, appData.firstLogo, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );
\r
748 if (first.programLogo == NULL && appData.debugMode) {
\r
749 fprintf( debugFP, "Unable to load logo bitmap '%s'\n", appData.firstLogo );
\r
751 } else if(appData.autoLogo) {
\r
752 if(appData.firstDirectory && appData.firstDirectory[0]) {
\r
754 sprintf(buf, "%s/logo.bmp", appData.firstDirectory);
\r
755 first.programLogo = LoadImage( 0, buf, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );
\r
759 if( appData.secondLogo && appData.secondLogo[0] != NULLCHAR) {
\r
760 second.programLogo = LoadImage( 0, appData.secondLogo, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );
\r
762 if (second.programLogo == NULL && appData.debugMode) {
\r
763 fprintf( debugFP, "Unable to load logo bitmap '%s'\n", appData.secondLogo );
\r
765 } else if(appData.autoLogo) {
\r
767 if(appData.icsActive) { // [HGM] logo: in ICS mode second can be used for ICS
\r
768 sprintf(buf, "logos\\%s.bmp", appData.icsHost);
\r
769 second.programLogo = LoadImage( 0, buf, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );
\r
771 if(appData.secondDirectory && appData.secondDirectory[0]) {
\r
772 sprintf(buf, "%s\\logo.bmp", appData.secondDirectory);
\r
773 second.programLogo = LoadImage( 0, buf, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );
\r
779 iconWhite = LoadIcon(hInstance, "icon_white");
\r
780 iconBlack = LoadIcon(hInstance, "icon_black");
\r
781 iconCurrent = iconWhite;
\r
782 InitDrawingColors();
\r
783 screenHeight = GetSystemMetrics(SM_CYSCREEN);
\r
784 screenWidth = GetSystemMetrics(SM_CXSCREEN);
\r
785 for (ibs = (int) NUM_SIZES - 1; ibs >= 0; ibs--) {
\r
786 /* Compute window size for each board size, and use the largest
\r
787 size that fits on this screen as the default. */
\r
788 InitDrawingSizes((BoardSize)(ibs+1000), 0);
\r
789 if (boardSize == (BoardSize)-1 &&
\r
790 winH <= screenHeight
\r
791 - GetSystemMetrics(SM_CYFRAME) - GetSystemMetrics(SM_CYCAPTION) - 10
\r
792 && winW <= screenWidth) {
\r
793 boardSize = (BoardSize)ibs;
\r
797 InitDrawingSizes(boardSize, 0);
\r
799 buttonCount = GetSystemMetrics(SM_CMOUSEBUTTONS);
\r
801 /* [AS] Load textures if specified */
\r
802 ZeroMemory( &backTextureSquareInfo, sizeof(backTextureSquareInfo) );
\r
804 if( appData.liteBackTextureFile && appData.liteBackTextureFile[0] != NULLCHAR && appData.liteBackTextureFile[0] != '*' ) {
\r
805 liteBackTexture = LoadImage( 0, appData.liteBackTextureFile, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );
\r
806 liteBackTextureMode = appData.liteBackTextureMode;
\r
808 if (liteBackTexture == NULL && appData.debugMode) {
\r
809 fprintf( debugFP, "Unable to load lite texture bitmap '%s'\n", appData.liteBackTextureFile );
\r
813 if( appData.darkBackTextureFile && appData.darkBackTextureFile[0] != NULLCHAR && appData.darkBackTextureFile[0] != '*' ) {
\r
814 darkBackTexture = LoadImage( 0, appData.darkBackTextureFile, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );
\r
815 darkBackTextureMode = appData.darkBackTextureMode;
\r
817 if (darkBackTexture == NULL && appData.debugMode) {
\r
818 fprintf( debugFP, "Unable to load dark texture bitmap '%s'\n", appData.darkBackTextureFile );
\r
822 mysrandom( (unsigned) time(NULL) );
\r
824 /* [AS] Restore layout */
\r
825 if( wpMoveHistory.visible ) {
\r
826 MoveHistoryPopUp();
\r
829 if( wpEvalGraph.visible ) {
\r
833 if( wpEngineOutput.visible ) {
\r
834 EngineOutputPopUp();
\r
839 /* Make the window visible; update its client area; and return "success" */
\r
840 EnsureOnScreen(&wpMain.x, &wpMain.y, minX, minY);
\r
841 wp.length = sizeof(WINDOWPLACEMENT);
\r
843 wp.showCmd = nCmdShow;
\r
844 wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;
\r
845 wp.rcNormalPosition.left = wpMain.x;
\r
846 wp.rcNormalPosition.right = wpMain.x + wpMain.width;
\r
847 wp.rcNormalPosition.top = wpMain.y;
\r
848 wp.rcNormalPosition.bottom = wpMain.y + wpMain.height;
\r
849 SetWindowPlacement(hwndMain, &wp);
\r
851 if(!appData.noGUI) SetWindowPos(hwndMain, alwaysOnTop ? HWND_TOPMOST : HWND_NOTOPMOST,
\r
852 0, 0, 0, 0, SWP_NOMOVE|SWP_NOSIZE);
\r
856 SetWindowPos(hwndConsole, alwaysOnTop ? HWND_TOPMOST : HWND_NOTOPMOST,
\r
857 0, 0, 0, 0, SWP_NOMOVE|SWP_NOSIZE);
\r
859 ShowWindow(hwndConsole, nCmdShow);
\r
861 if(!appData.noGUI) UpdateWindow(hwnd); else ShowWindow(hwnd, SW_MINIMIZE);
\r
862 if(gameListDialog) SetFocus(gameListDialog); // [HGM] jaws: for if we clicked multi-game game file
\r
871 HMENU hmenu = GetMenu(hwndMain);
\r
873 (void) EnableMenuItem(hmenu, IDM_CommPort,
\r
874 MF_BYCOMMAND|((appData.icsActive &&
\r
875 *appData.icsCommPort != NULLCHAR) ?
\r
876 MF_ENABLED : MF_GRAYED));
\r
877 (void) CheckMenuItem(hmenu, IDM_SaveSettingsOnExit,
\r
878 MF_BYCOMMAND|(saveSettingsOnExit ?
\r
879 MF_CHECKED : MF_UNCHECKED));
\r
882 //---------------------------------------------------------------------------------------------------------
\r
884 #define ICS_TEXT_MENU_SIZE (IDM_CommandXLast - IDM_CommandX + 1)
\r
885 #define XBOARD FALSE
\r
887 #define OPTCHAR "/"
\r
888 #define SEPCHAR "="
\r
892 // front-end part of option handling
\r
895 LFfromMFP(LOGFONT* lf, MyFontParams *mfp)
\r
897 HDC hdc = CreateDC("DISPLAY", NULL, NULL, NULL);
\r
898 lf->lfHeight = -(int)(mfp->pointSize * GetDeviceCaps(hdc, LOGPIXELSY) / 72.0 + 0.5);
\r
901 lf->lfEscapement = 0;
\r
902 lf->lfOrientation = 0;
\r
903 lf->lfWeight = mfp->bold ? FW_BOLD : FW_NORMAL;
\r
904 lf->lfItalic = mfp->italic;
\r
905 lf->lfUnderline = mfp->underline;
\r
906 lf->lfStrikeOut = mfp->strikeout;
\r
907 lf->lfCharSet = mfp->charset;
\r
908 lf->lfOutPrecision = OUT_DEFAULT_PRECIS;
\r
909 lf->lfClipPrecision = CLIP_DEFAULT_PRECIS;
\r
910 lf->lfQuality = DEFAULT_QUALITY;
\r
911 lf->lfPitchAndFamily = DEFAULT_PITCH|FF_DONTCARE;
\r
912 strcpy(lf->lfFaceName, mfp->faceName);
\r
916 CreateFontInMF(MyFont *mf)
\r
918 LFfromMFP(&mf->lf, &mf->mfp);
\r
919 if (mf->hf) DeleteObject(mf->hf);
\r
920 mf->hf = CreateFontIndirect(&mf->lf);
\r
923 // [HGM] This platform-dependent table provides the location for storing the color info
\r
925 colorVariable[] = {
\r
930 &highlightSquareColor,
\r
931 &premoveHighlightColor,
\r
933 &consoleBackgroundColor,
\r
934 &appData.fontForeColorWhite,
\r
935 &appData.fontBackColorWhite,
\r
936 &appData.fontForeColorBlack,
\r
937 &appData.fontBackColorBlack,
\r
938 &appData.evalHistColorWhite,
\r
939 &appData.evalHistColorBlack,
\r
940 &appData.highlightArrowColor,
\r
943 /* Command line font name parser. NULL name means do nothing.
\r
944 Syntax like "Courier New:10.0 bi" or "Arial:10" or "Arial:10b"
\r
945 For backward compatibility, syntax without the colon is also
\r
946 accepted, but font names with digits in them won't work in that case.
\r
949 ParseFontName(char *name, MyFontParams *mfp)
\r
952 if (name == NULL) return;
\r
954 q = strchr(p, ':');
\r
956 if (q - p >= sizeof(mfp->faceName))
\r
957 ExitArgError("Font name too long:", name);
\r
958 memcpy(mfp->faceName, p, q - p);
\r
959 mfp->faceName[q - p] = NULLCHAR;
\r
963 while (*p && !isdigit(*p)) {
\r
965 if (q - mfp->faceName >= sizeof(mfp->faceName))
\r
966 ExitArgError("Font name too long:", name);
\r
968 while (q > mfp->faceName && q[-1] == ' ') q--;
\r
971 if (!*p) ExitArgError("Font point size missing:", name);
\r
972 mfp->pointSize = (float) atof(p);
\r
973 mfp->bold = (strchr(p, 'b') != NULL);
\r
974 mfp->italic = (strchr(p, 'i') != NULL);
\r
975 mfp->underline = (strchr(p, 'u') != NULL);
\r
976 mfp->strikeout = (strchr(p, 's') != NULL);
\r
977 mfp->charset = DEFAULT_CHARSET;
\r
978 q = strchr(p, 'c');
\r
980 mfp->charset = (BYTE) atoi(q+1);
\r
984 ParseFont(char *name, int number)
\r
985 { // wrapper to shield back-end from 'font'
\r
986 ParseFontName(name, &font[boardSize][number]->mfp);
\r
991 { // in WB we have a 2D array of fonts; this initializes their description
\r
993 /* Point font array elements to structures and
\r
994 parse default font names */
\r
995 for (i=0; i<NUM_FONTS; i++) {
\r
996 for (j=0; j<NUM_SIZES; j++) {
\r
997 font[j][i] = &fontRec[j][i];
\r
998 ParseFontName(font[j][i]->def, &font[j][i]->mfp);
\r
1005 { // here we create the actual fonts from the selected descriptions
\r
1007 for (i=0; i<NUM_FONTS; i++) {
\r
1008 for (j=0; j<NUM_SIZES; j++) {
\r
1009 CreateFontInMF(font[j][i]);
\r
1013 /* Color name parser.
\r
1014 X version accepts X color names, but this one
\r
1015 handles only the #rrggbb form (hex) or rrr,ggg,bbb (decimal) */
\r
1017 ParseColorName(char *name)
\r
1019 int red, green, blue, count;
\r
1020 char buf[MSG_SIZ];
\r
1022 count = sscanf(name, "#%2x%2x%2x", &red, &green, &blue);
\r
1024 count = sscanf(name, "%3d%*[^0-9]%3d%*[^0-9]%3d",
\r
1025 &red, &green, &blue);
\r
1028 sprintf(buf, "Can't parse color name %s", name);
\r
1029 DisplayError(buf, 0);
\r
1030 return RGB(0, 0, 0);
\r
1032 return PALETTERGB(red, green, blue);
\r
1036 ParseColor(int n, char *name)
\r
1037 { // for WinBoard the color is an int, which needs to be derived from the string
\r
1038 if(colorVariable[n]) *(int*)colorVariable[n] = ParseColorName(name);
\r
1042 ParseAttribs(COLORREF *color, int *effects, char* argValue)
\r
1044 char *e = argValue;
\r
1048 if (*e == 'b') eff |= CFE_BOLD;
\r
1049 else if (*e == 'i') eff |= CFE_ITALIC;
\r
1050 else if (*e == 'u') eff |= CFE_UNDERLINE;
\r
1051 else if (*e == 's') eff |= CFE_STRIKEOUT;
\r
1052 else if (*e == '#' || isdigit(*e)) break;
\r
1056 *color = ParseColorName(e);
\r
1060 ParseTextAttribs(ColorClass cc, char *s)
\r
1061 { // [HGM] front-end wrapper that does the platform-dependent call
\r
1062 // for XBoard we would set (&appData.colorShout)[cc] = strdup(s);
\r
1063 ParseAttribs(&textAttribs[cc].color, &textAttribs[cc].effects, s);
\r
1067 ParseBoardSize(void *addr, char *name)
\r
1068 { // [HGM] rewritten with return-value ptr to shield back-end from BoardSize
\r
1069 BoardSize bs = SizeTiny;
\r
1070 while (sizeInfo[bs].name != NULL) {
\r
1071 if (StrCaseCmp(name, sizeInfo[bs].name) == 0) {
\r
1072 *(BoardSize *)addr = bs;
\r
1077 ExitArgError("Unrecognized board size value", name);
\r
1082 { // [HGM] import name from appData first
\r
1085 for (cc = (ColorClass)0; cc < ColorNormal; cc++) {
\r
1086 textAttribs[cc].sound.name = strdup((&appData.soundShout)[cc]);
\r
1087 textAttribs[cc].sound.data = NULL;
\r
1088 MyLoadSound(&textAttribs[cc].sound);
\r
1090 for (cc = ColorNormal; cc < NColorClasses; cc++) {
\r
1091 textAttribs[cc].sound.name = strdup("");
\r
1092 textAttribs[cc].sound.data = NULL;
\r
1094 for (sc = (SoundClass)0; sc < NSoundClasses; sc++) {
\r
1095 sounds[sc].name = strdup((&appData.soundMove)[sc]);
\r
1096 sounds[sc].data = NULL;
\r
1097 MyLoadSound(&sounds[sc]);
\r
1102 SetCommPortDefaults()
\r
1104 memset(&dcb, 0, sizeof(DCB)); // required by VS 2002 +
\r
1105 dcb.DCBlength = sizeof(DCB);
\r
1106 dcb.BaudRate = 9600;
\r
1107 dcb.fBinary = TRUE;
\r
1108 dcb.fParity = FALSE;
\r
1109 dcb.fOutxCtsFlow = FALSE;
\r
1110 dcb.fOutxDsrFlow = FALSE;
\r
1111 dcb.fDtrControl = DTR_CONTROL_ENABLE;
\r
1112 dcb.fDsrSensitivity = FALSE;
\r
1113 dcb.fTXContinueOnXoff = TRUE;
\r
1114 dcb.fOutX = FALSE;
\r
1116 dcb.fNull = FALSE;
\r
1117 dcb.fRtsControl = RTS_CONTROL_ENABLE;
\r
1118 dcb.fAbortOnError = FALSE;
\r
1120 dcb.Parity = SPACEPARITY;
\r
1121 dcb.StopBits = ONESTOPBIT;
\r
1124 // [HGM] args: these three cases taken out to stay in front-end
\r
1126 SaveFontArg(FILE *f, ArgDescriptor *ad)
\r
1127 { // in WinBoard every board size has its own font, and the "argLoc" identifies the table,
\r
1128 // while the curent board size determines the element. This system should be ported to XBoard.
\r
1129 // What the table contains pointers to, and how to print the font description, remains platform-dependent
\r
1131 for (bs=0; bs<NUM_SIZES; bs++) {
\r
1132 MyFontParams *mfp = &font[bs][(int) ad->argLoc]->mfp;
\r
1133 fprintf(f, "/size=%s ", sizeInfo[bs].name);
\r
1134 fprintf(f, "/%s=\"%s:%g%s%s%s%s%sc%d\"\n",
\r
1135 ad->argName, mfp->faceName, mfp->pointSize,
\r
1136 mfp->bold || mfp->italic || mfp->underline || mfp->strikeout ? " " : "",
\r
1137 mfp->bold ? "b" : "",
\r
1138 mfp->italic ? "i" : "",
\r
1139 mfp->underline ? "u" : "",
\r
1140 mfp->strikeout ? "s" : "",
\r
1141 (int)mfp->charset);
\r
1147 { // [HGM] copy the names from the internal WB variables to appData
\r
1150 for (cc = (ColorClass)0; cc < ColorNormal; cc++)
\r
1151 (&appData.soundShout)[cc] = textAttribs[cc].sound.name;
\r
1152 for (sc = (SoundClass)0; sc < NSoundClasses; sc++)
\r
1153 (&appData.soundMove)[sc] = sounds[sc].name;
\r
1157 SaveAttribsArg(FILE *f, ArgDescriptor *ad)
\r
1158 { // here the "argLoc" defines a table index. It could have contained the 'ta' pointer itself, though
\r
1159 MyTextAttribs* ta = &textAttribs[(ColorClass)ad->argLoc];
\r
1160 fprintf(f, "/%s=\"%s%s%s%s%s#%02lx%02lx%02lx\"\n", ad->argName,
\r
1161 (ta->effects & CFE_BOLD) ? "b" : "",
\r
1162 (ta->effects & CFE_ITALIC) ? "i" : "",
\r
1163 (ta->effects & CFE_UNDERLINE) ? "u" : "",
\r
1164 (ta->effects & CFE_STRIKEOUT) ? "s" : "",
\r
1165 (ta->effects) ? " " : "",
\r
1166 ta->color&0xff, (ta->color >> 8)&0xff, (ta->color >> 16)&0xff);
\r
1170 SaveColor(FILE *f, ArgDescriptor *ad)
\r
1171 { // in WinBoard the color is an int and has to be converted to text. In X it would be a string already?
\r
1172 COLORREF color = *(COLORREF *)colorVariable[(int)ad->argLoc];
\r
1173 fprintf(f, "/%s=#%02lx%02lx%02lx\n", ad->argName,
\r
1174 color&0xff, (color>>8)&0xff, (color>>16)&0xff);
\r
1178 SaveBoardSize(FILE *f, char *name, void *addr)
\r
1179 { // wrapper to shield back-end from BoardSize & sizeInfo
\r
1180 fprintf(f, "/%s=%s\n", name, sizeInfo[*(BoardSize *)addr].name);
\r
1184 ParseCommPortSettings(char *s)
\r
1185 { // wrapper to keep dcb from back-end
\r
1186 ParseCommSettings(s, &dcb);
\r
1191 { // wrapper to shield use of window handles from back-end (make addressible by number?)
\r
1192 GetActualPlacement(hwndMain, &wpMain);
\r
1193 GetActualPlacement(hwndConsole, &wpConsole);
\r
1194 GetActualPlacement(commentDialog, &wpComment);
\r
1195 GetActualPlacement(editTagsDialog, &wpTags);
\r
1196 GetActualPlacement(gameListDialog, &wpGameList);
\r
1197 GetActualPlacement(moveHistoryDialog, &wpMoveHistory);
\r
1198 GetActualPlacement(evalGraphDialog, &wpEvalGraph);
\r
1199 GetActualPlacement(engineOutputDialog, &wpEngineOutput);
\r
1203 PrintCommPortSettings(FILE *f, char *name)
\r
1204 { // wrapper to shield back-end from DCB
\r
1205 PrintCommSettings(f, name, &dcb);
\r
1209 MySearchPath(char *installDir, char *name, char *fullname)
\r
1212 return (int) SearchPath(installDir, name, NULL, MSG_SIZ, fullname, &dummy);
\r
1216 MyGetFullPathName(char *name, char *fullname)
\r
1219 return (int) GetFullPathName(name, MSG_SIZ, fullname, &dummy);
\r
1224 { // [HGM] args: allows testing if main window is realized from back-end
\r
1225 return hwndMain != NULL;
\r
1229 PopUpStartupDialog()
\r
1233 lpProc = MakeProcInstance((FARPROC)StartupDialog, hInst);
\r
1234 DialogBox(hInst, MAKEINTRESOURCE(DLG_Startup), NULL, (DLGPROC)lpProc);
\r
1235 FreeProcInstance(lpProc);
\r
1238 /*---------------------------------------------------------------------------*\
\r
1240 * GDI board drawing routines
\r
1242 \*---------------------------------------------------------------------------*/
\r
1244 /* [AS] Draw square using background texture */
\r
1245 static void DrawTile( int dx, int dy, int dw, int dh, HDC dst, HDC src, int mode, int sx, int sy )
\r
1250 return; /* Should never happen! */
\r
1253 SetGraphicsMode( dst, GM_ADVANCED );
\r
1260 /* X reflection */
\r
1265 x.eDx = (FLOAT) dw + dx - 1;
\r
1268 SetWorldTransform( dst, &x );
\r
1271 /* Y reflection */
\r
1277 x.eDy = (FLOAT) dh + dy - 1;
\r
1279 SetWorldTransform( dst, &x );
\r
1287 x.eDx = (FLOAT) dx;
\r
1288 x.eDy = (FLOAT) dy;
\r
1291 SetWorldTransform( dst, &x );
\r
1295 BitBlt( dst, dx, dy, dw, dh, src, sx, sy, SRCCOPY );
\r
1303 SetWorldTransform( dst, &x );
\r
1305 ModifyWorldTransform( dst, 0, MWT_IDENTITY );
\r
1308 /* [AS] [HGM] Make room for more piece types, so all pieces can be different */
\r
1310 PM_WP = (int) WhitePawn,
\r
1311 PM_WN = (int) WhiteKnight,
\r
1312 PM_WB = (int) WhiteBishop,
\r
1313 PM_WR = (int) WhiteRook,
\r
1314 PM_WQ = (int) WhiteQueen,
\r
1315 PM_WF = (int) WhiteFerz,
\r
1316 PM_WW = (int) WhiteWazir,
\r
1317 PM_WE = (int) WhiteAlfil,
\r
1318 PM_WM = (int) WhiteMan,
\r
1319 PM_WO = (int) WhiteCannon,
\r
1320 PM_WU = (int) WhiteUnicorn,
\r
1321 PM_WH = (int) WhiteNightrider,
\r
1322 PM_WA = (int) WhiteAngel,
\r
1323 PM_WC = (int) WhiteMarshall,
\r
1324 PM_WAB = (int) WhiteCardinal,
\r
1325 PM_WD = (int) WhiteDragon,
\r
1326 PM_WL = (int) WhiteLance,
\r
1327 PM_WS = (int) WhiteCobra,
\r
1328 PM_WV = (int) WhiteFalcon,
\r
1329 PM_WSG = (int) WhiteSilver,
\r
1330 PM_WG = (int) WhiteGrasshopper,
\r
1331 PM_WK = (int) WhiteKing,
\r
1332 PM_BP = (int) BlackPawn,
\r
1333 PM_BN = (int) BlackKnight,
\r
1334 PM_BB = (int) BlackBishop,
\r
1335 PM_BR = (int) BlackRook,
\r
1336 PM_BQ = (int) BlackQueen,
\r
1337 PM_BF = (int) BlackFerz,
\r
1338 PM_BW = (int) BlackWazir,
\r
1339 PM_BE = (int) BlackAlfil,
\r
1340 PM_BM = (int) BlackMan,
\r
1341 PM_BO = (int) BlackCannon,
\r
1342 PM_BU = (int) BlackUnicorn,
\r
1343 PM_BH = (int) BlackNightrider,
\r
1344 PM_BA = (int) BlackAngel,
\r
1345 PM_BC = (int) BlackMarshall,
\r
1346 PM_BG = (int) BlackGrasshopper,
\r
1347 PM_BAB = (int) BlackCardinal,
\r
1348 PM_BD = (int) BlackDragon,
\r
1349 PM_BL = (int) BlackLance,
\r
1350 PM_BS = (int) BlackCobra,
\r
1351 PM_BV = (int) BlackFalcon,
\r
1352 PM_BSG = (int) BlackSilver,
\r
1353 PM_BK = (int) BlackKing
\r
1356 static HFONT hPieceFont = NULL;
\r
1357 static HBITMAP hPieceMask[(int) EmptySquare];
\r
1358 static HBITMAP hPieceFace[(int) EmptySquare];
\r
1359 static int fontBitmapSquareSize = 0;
\r
1360 static char pieceToFontChar[(int) EmptySquare] =
\r
1361 { 'p', 'n', 'b', 'r', 'q',
\r
1362 'n', 'b', 'p', 'n', 'b', 'r', 'b', 'r', 'q', 'k',
\r
1363 'k', 'o', 'm', 'v', 't', 'w',
\r
1364 'v', 't', 'o', 'm', 'v', 't', 'v', 't', 'w', 'l',
\r
1367 extern BOOL SetCharTable( char *table, const char * map );
\r
1368 /* [HGM] moved to backend.c */
\r
1370 static void SetPieceBackground( HDC hdc, COLORREF color, int mode )
\r
1373 BYTE r1 = GetRValue( color );
\r
1374 BYTE g1 = GetGValue( color );
\r
1375 BYTE b1 = GetBValue( color );
\r
1381 /* Create a uniform background first */
\r
1382 hbrush = CreateSolidBrush( color );
\r
1383 SetRect( &rc, 0, 0, squareSize, squareSize );
\r
1384 FillRect( hdc, &rc, hbrush );
\r
1385 DeleteObject( hbrush );
\r
1388 /* Vertical gradient, good for pawn, knight and rook, less for queen and king */
\r
1389 int steps = squareSize / 2;
\r
1392 for( i=0; i<steps; i++ ) {
\r
1393 BYTE r = r1 - (r1-r2) * i / steps;
\r
1394 BYTE g = g1 - (g1-g2) * i / steps;
\r
1395 BYTE b = b1 - (b1-b2) * i / steps;
\r
1397 hbrush = CreateSolidBrush( RGB(r,g,b) );
\r
1398 SetRect( &rc, i + squareSize - steps, 0, i + squareSize - steps + 1, squareSize );
\r
1399 FillRect( hdc, &rc, hbrush );
\r
1400 DeleteObject(hbrush);
\r
1403 else if( mode == 2 ) {
\r
1404 /* Diagonal gradient, good more or less for every piece */
\r
1405 POINT triangle[3];
\r
1406 HPEN hpen = SelectObject( hdc, GetStockObject(NULL_PEN) );
\r
1407 HBRUSH hbrush_old;
\r
1408 int steps = squareSize;
\r
1411 triangle[0].x = squareSize - steps;
\r
1412 triangle[0].y = squareSize;
\r
1413 triangle[1].x = squareSize;
\r
1414 triangle[1].y = squareSize;
\r
1415 triangle[2].x = squareSize;
\r
1416 triangle[2].y = squareSize - steps;
\r
1418 for( i=0; i<steps; i++ ) {
\r
1419 BYTE r = r1 - (r1-r2) * i / steps;
\r
1420 BYTE g = g1 - (g1-g2) * i / steps;
\r
1421 BYTE b = b1 - (b1-b2) * i / steps;
\r
1423 hbrush = CreateSolidBrush( RGB(r,g,b) );
\r
1424 hbrush_old = SelectObject( hdc, hbrush );
\r
1425 Polygon( hdc, triangle, 3 );
\r
1426 SelectObject( hdc, hbrush_old );
\r
1427 DeleteObject(hbrush);
\r
1432 SelectObject( hdc, hpen );
\r
1437 [AS] The method I use to create the bitmaps it a bit tricky, but it
\r
1438 seems to work ok. The main problem here is to find the "inside" of a chess
\r
1439 piece: follow the steps as explained below.
\r
1441 static void CreatePieceMaskFromFont( HDC hdc_window, HDC hdc, int index )
\r
1445 COLORREF chroma = RGB(0xFF,0x00,0xFF);
\r
1449 int backColor = whitePieceColor;
\r
1450 int foreColor = blackPieceColor;
\r
1452 if( index < (int)BlackPawn && appData.fontBackColorWhite != appData.fontForeColorWhite ) {
\r
1453 backColor = appData.fontBackColorWhite;
\r
1454 foreColor = appData.fontForeColorWhite;
\r
1456 else if( index >= (int)BlackPawn && appData.fontBackColorBlack != appData.fontForeColorBlack ) {
\r
1457 backColor = appData.fontBackColorBlack;
\r
1458 foreColor = appData.fontForeColorBlack;
\r
1462 hbm = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );
\r
1464 hbm_old = SelectObject( hdc, hbm );
\r
1468 rc.right = squareSize;
\r
1469 rc.bottom = squareSize;
\r
1471 /* Step 1: background is now black */
\r
1472 FillRect( hdc, &rc, GetStockObject(BLACK_BRUSH) );
\r
1474 GetTextExtentPoint32( hdc, &pieceToFontChar[index], 1, &sz );
\r
1476 pt.x = (squareSize - sz.cx) / 2;
\r
1477 pt.y = (squareSize - sz.cy) / 2;
\r
1479 SetBkMode( hdc, TRANSPARENT );
\r
1480 SetTextColor( hdc, chroma );
\r
1481 /* Step 2: the piece has been drawn in purple, there are now black and purple in this bitmap */
\r
1482 TextOut( hdc, pt.x, pt.y, &pieceToFontChar[appData.allWhite && index >= (int)BlackPawn ? index - (int)BlackPawn : index], 1 );
\r
1484 SelectObject( hdc, GetStockObject(WHITE_BRUSH) );
\r
1485 /* Step 3: the area outside the piece is filled with white */
\r
1486 // FloodFill( hdc, 0, 0, chroma );
\r
1487 ExtFloodFill( hdc, 0, 0, 0, FLOODFILLSURFACE );
\r
1488 ExtFloodFill( hdc, 0, squareSize-1, 0, FLOODFILLSURFACE ); // [HGM] fill from all 4 corners, for if piece too big
\r
1489 ExtFloodFill( hdc, squareSize-1, 0, 0, FLOODFILLSURFACE );
\r
1490 ExtFloodFill( hdc, squareSize-1, squareSize-1, 0, FLOODFILLSURFACE );
\r
1491 SelectObject( hdc, GetStockObject(BLACK_BRUSH) );
\r
1493 Step 4: this is the tricky part, the area inside the piece is filled with black,
\r
1494 but if the start point is not inside the piece we're lost!
\r
1495 There should be a better way to do this... if we could create a region or path
\r
1496 from the fill operation we would be fine for example.
\r
1498 // FloodFill( hdc, squareSize / 2, squareSize / 2, RGB(0xFF,0xFF,0xFF) );
\r
1499 ExtFloodFill( hdc, squareSize / 2, squareSize / 2, RGB(0xFF,0xFF,0xFF), FLOODFILLBORDER );
\r
1501 { /* [HGM] shave off edges of mask, in an attempt to correct for the fact that FloodFill does not work correctly under Win XP */
\r
1502 HDC dc2 = CreateCompatibleDC( hdc_window );
\r
1503 HBITMAP bm2 = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );
\r
1505 SelectObject( dc2, bm2 );
\r
1506 BitBlt( dc2, 0, 0, squareSize, squareSize, hdc, 0, 0, SRCCOPY ); // make copy
\r
1507 BitBlt( hdc, 0, 1, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );
\r
1508 BitBlt( hdc, 2, 1, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );
\r
1509 BitBlt( hdc, 1, 0, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );
\r
1510 BitBlt( hdc, 1, 2, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );
\r
1513 DeleteObject( bm2 );
\r
1516 SetTextColor( hdc, 0 );
\r
1518 Step 5: some fonts have "disconnected" areas that are skipped by the fill:
\r
1519 draw the piece again in black for safety.
\r
1521 TextOut( hdc, pt.x, pt.y, &pieceToFontChar[appData.allWhite && index >= (int)BlackPawn ? index - (int)BlackPawn : index], 1 );
\r
1523 SelectObject( hdc, hbm_old );
\r
1525 if( hPieceMask[index] != NULL ) {
\r
1526 DeleteObject( hPieceMask[index] );
\r
1529 hPieceMask[index] = hbm;
\r
1532 hbm = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );
\r
1534 SelectObject( hdc, hbm );
\r
1537 HDC dc1 = CreateCompatibleDC( hdc_window );
\r
1538 HDC dc2 = CreateCompatibleDC( hdc_window );
\r
1539 HBITMAP bm2 = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );
\r
1541 SelectObject( dc1, hPieceMask[index] );
\r
1542 SelectObject( dc2, bm2 );
\r
1543 FillRect( dc2, &rc, GetStockObject(WHITE_BRUSH) );
\r
1544 BitBlt( dc2, 0, 0, squareSize, squareSize, dc1, 0, 0, SRCINVERT );
\r
1547 Now dc2 contains the inverse of the piece mask, i.e. a mask that preserves
\r
1548 the piece background and deletes (makes transparent) the rest.
\r
1549 Thanks to that mask, we are free to paint the background with the greates
\r
1550 freedom, as we'll be able to mask off the unwanted parts when finished.
\r
1551 We use this, to make gradients and give the pieces a "roundish" look.
\r
1553 SetPieceBackground( hdc, backColor, 2 );
\r
1554 BitBlt( hdc, 0, 0, squareSize, squareSize, dc2, 0, 0, SRCAND );
\r
1558 DeleteObject( bm2 );
\r
1561 SetTextColor( hdc, foreColor );
\r
1562 TextOut( hdc, pt.x, pt.y, &pieceToFontChar[appData.allWhite && index >= (int)BlackPawn ? index - (int)BlackPawn : index], 1 );
\r
1564 SelectObject( hdc, hbm_old );
\r
1566 if( hPieceFace[index] != NULL ) {
\r
1567 DeleteObject( hPieceFace[index] );
\r
1570 hPieceFace[index] = hbm;
\r
1573 static int TranslatePieceToFontPiece( int piece )
\r
1603 case BlackMarshall:
\r
1607 case BlackNightrider:
\r
1613 case BlackUnicorn:
\r
1617 case BlackGrasshopper:
\r
1629 case BlackCardinal:
\r
1636 case WhiteMarshall:
\r
1640 case WhiteNightrider:
\r
1646 case WhiteUnicorn:
\r
1650 case WhiteGrasshopper:
\r
1662 case WhiteCardinal:
\r
1671 void CreatePiecesFromFont()
\r
1674 HDC hdc_window = NULL;
\r
1680 if( fontBitmapSquareSize < 0 ) {
\r
1681 /* Something went seriously wrong in the past: do not try to recreate fonts! */
\r
1685 if( appData.renderPiecesWithFont == NULL || appData.renderPiecesWithFont[0] == NULLCHAR || appData.renderPiecesWithFont[0] == '*' ) {
\r
1686 fontBitmapSquareSize = -1;
\r
1690 if( fontBitmapSquareSize != squareSize ) {
\r
1691 hdc_window = GetDC( hwndMain );
\r
1692 hdc = CreateCompatibleDC( hdc_window );
\r
1694 if( hPieceFont != NULL ) {
\r
1695 DeleteObject( hPieceFont );
\r
1698 for( i=0; i<=(int)BlackKing; i++ ) {
\r
1699 hPieceMask[i] = NULL;
\r
1700 hPieceFace[i] = NULL;
\r
1706 if( appData.fontPieceSize >= 50 && appData.fontPieceSize <= 150 ) {
\r
1707 fontHeight = appData.fontPieceSize;
\r
1710 fontHeight = (fontHeight * squareSize) / 100;
\r
1712 lf.lfHeight = -MulDiv( fontHeight, GetDeviceCaps(hdc, LOGPIXELSY), 72 );
\r
1714 lf.lfEscapement = 0;
\r
1715 lf.lfOrientation = 0;
\r
1716 lf.lfWeight = FW_NORMAL;
\r
1718 lf.lfUnderline = 0;
\r
1719 lf.lfStrikeOut = 0;
\r
1720 lf.lfCharSet = DEFAULT_CHARSET;
\r
1721 lf.lfOutPrecision = OUT_DEFAULT_PRECIS;
\r
1722 lf.lfClipPrecision = CLIP_DEFAULT_PRECIS;
\r
1723 lf.lfQuality = PROOF_QUALITY;
\r
1724 lf.lfPitchAndFamily = DEFAULT_PITCH | FF_DONTCARE;
\r
1725 strncpy( lf.lfFaceName, appData.renderPiecesWithFont, sizeof(lf.lfFaceName) );
\r
1726 lf.lfFaceName[ sizeof(lf.lfFaceName) - 1 ] = '\0';
\r
1728 hPieceFont = CreateFontIndirect( &lf );
\r
1730 if( hPieceFont == NULL ) {
\r
1731 fontBitmapSquareSize = -2;
\r
1734 /* Setup font-to-piece character table */
\r
1735 if( ! SetCharTable(pieceToFontChar, appData.fontToPieceTable) ) {
\r
1736 /* No (or wrong) global settings, try to detect the font */
\r
1737 if( strstr(lf.lfFaceName,"Alpha") != NULL ) {
\r
1739 SetCharTable(pieceToFontChar, "phbrqkojntwl");
\r
1741 else if( strstr(lf.lfFaceName,"DiagramTT") != NULL ) {
\r
1742 /* DiagramTT* family */
\r
1743 SetCharTable(pieceToFontChar, "PNLRQKpnlrqk");
\r
1745 else if( strstr(lf.lfFaceName,"WinboardF") != NULL ) {
\r
1746 /* Fairy symbols */
\r
1747 SetCharTable(pieceToFontChar, "PNBRQFEACWMOHIJGDVSLUKpnbrqfeacwmohijgdvsluk");
\r
1749 else if( strstr(lf.lfFaceName,"GC2004D") != NULL ) {
\r
1750 /* Good Companion (Some characters get warped as literal :-( */
\r
1751 char s[] = "1cmWG0??S??oYI23wgQU";
\r
1752 s[0]=0xB9; s[1]=0xA9; s[6]=0xB1; s[11]=0xBB; s[12]=0xAB; s[17]=0xB3;
\r
1753 SetCharTable(pieceToFontChar, s);
\r
1756 /* Cases, Condal, Leipzig, Lucena, Marroquin, Merida, Usual */
\r
1757 SetCharTable(pieceToFontChar, "pnbrqkomvtwl");
\r
1761 /* Create bitmaps */
\r
1762 hfont_old = SelectObject( hdc, hPieceFont );
\r
1763 for(i=(int)WhitePawn; i<(int)EmptySquare; i++) /* [HGM] made a loop for this */
\r
1764 if(PieceToChar((ChessSquare)i) != '.') /* skip unused pieces */
\r
1765 CreatePieceMaskFromFont( hdc_window, hdc, i );
\r
1767 SelectObject( hdc, hfont_old );
\r
1769 fontBitmapSquareSize = squareSize;
\r
1773 if( hdc != NULL ) {
\r
1777 if( hdc_window != NULL ) {
\r
1778 ReleaseDC( hwndMain, hdc_window );
\r
1783 DoLoadBitmap(HINSTANCE hinst, char *piece, int squareSize, char *suffix)
\r
1787 sprintf(name, "%s%d%s", piece, squareSize, suffix);
\r
1788 if (gameInfo.event &&
\r
1789 strcmp(gameInfo.event, "Easter Egg Hunt") == 0 &&
\r
1790 strcmp(name, "k80s") == 0) {
\r
1791 strcpy(name, "tim");
\r
1793 return LoadBitmap(hinst, name);
\r
1797 /* Insert a color into the program's logical palette
\r
1798 structure. This code assumes the given color is
\r
1799 the result of the RGB or PALETTERGB macro, and it
\r
1800 knows how those macros work (which is documented).
\r
1803 InsertInPalette(COLORREF color)
\r
1805 LPPALETTEENTRY pe = &(pLogPal->palPalEntry[pLogPal->palNumEntries]);
\r
1807 if (pLogPal->palNumEntries++ >= PALETTESIZE) {
\r
1808 DisplayFatalError("Too many colors", 0, 1);
\r
1809 pLogPal->palNumEntries--;
\r
1813 pe->peFlags = (char) 0;
\r
1814 pe->peRed = (char) (0xFF & color);
\r
1815 pe->peGreen = (char) (0xFF & (color >> 8));
\r
1816 pe->peBlue = (char) (0xFF & (color >> 16));
\r
1822 InitDrawingColors()
\r
1824 if (pLogPal == NULL) {
\r
1825 /* Allocate enough memory for a logical palette with
\r
1826 * PALETTESIZE entries and set the size and version fields
\r
1827 * of the logical palette structure.
\r
1829 pLogPal = (NPLOGPALETTE)
\r
1830 LocalAlloc(LMEM_FIXED, (sizeof(LOGPALETTE) +
\r
1831 (sizeof(PALETTEENTRY) * (PALETTESIZE))));
\r
1832 pLogPal->palVersion = 0x300;
\r
1834 pLogPal->palNumEntries = 0;
\r
1836 InsertInPalette(lightSquareColor);
\r
1837 InsertInPalette(darkSquareColor);
\r
1838 InsertInPalette(whitePieceColor);
\r
1839 InsertInPalette(blackPieceColor);
\r
1840 InsertInPalette(highlightSquareColor);
\r
1841 InsertInPalette(premoveHighlightColor);
\r
1843 /* create a logical color palette according the information
\r
1844 * in the LOGPALETTE structure.
\r
1846 hPal = CreatePalette((LPLOGPALETTE) pLogPal);
\r
1848 lightSquareBrush = CreateSolidBrush(lightSquareColor);
\r
1849 blackSquareBrush = CreateSolidBrush(blackPieceColor);
\r
1850 darkSquareBrush = CreateSolidBrush(darkSquareColor);
\r
1851 whitePieceBrush = CreateSolidBrush(whitePieceColor);
\r
1852 blackPieceBrush = CreateSolidBrush(blackPieceColor);
\r
1853 iconBkgndBrush = CreateSolidBrush(GetSysColor(COLOR_BACKGROUND));
\r
1854 explodeBrush = CreateSolidBrush(highlightSquareColor); // [HGM] atomic
\r
1855 /* [AS] Force rendering of the font-based pieces */
\r
1856 if( fontBitmapSquareSize > 0 ) {
\r
1857 fontBitmapSquareSize = 0;
\r
1863 BoardWidth(int boardSize, int n)
\r
1864 { /* [HGM] argument n added to allow different width and height */
\r
1865 int lineGap = sizeInfo[boardSize].lineGap;
\r
1867 if( appData.overrideLineGap >= 0 && appData.overrideLineGap <= 5 ) {
\r
1868 lineGap = appData.overrideLineGap;
\r
1871 return (n + 1) * lineGap +
\r
1872 n * sizeInfo[boardSize].squareSize;
\r
1875 /* Respond to board resize by dragging edge */
\r
1877 ResizeBoard(int newSizeX, int newSizeY, int flags)
\r
1879 BoardSize newSize = NUM_SIZES - 1;
\r
1880 static int recurse = 0;
\r
1881 if (IsIconic(hwndMain)) return;
\r
1882 if (recurse > 0) return;
\r
1884 while (newSize > 0) {
\r
1885 InitDrawingSizes(newSize+1000, 0); // [HGM] kludge to update sizeInfo without visible effects
\r
1886 if(newSizeX >= sizeInfo[newSize].cliWidth &&
\r
1887 newSizeY >= sizeInfo[newSize].cliHeight) break;
\r
1890 boardSize = newSize;
\r
1891 InitDrawingSizes(boardSize, flags);
\r
1898 InitDrawingSizes(BoardSize boardSize, int flags)
\r
1900 int i, boardWidth, boardHeight; /* [HGM] height treated separately */
\r
1901 ChessSquare piece;
\r
1902 static int oldBoardSize = -1, oldTinyLayout = 0;
\r
1904 SIZE clockSize, messageSize;
\r
1906 char buf[MSG_SIZ];
\r
1908 HMENU hmenu = GetMenu(hwndMain);
\r
1909 RECT crect, wrect, oldRect;
\r
1911 LOGBRUSH logbrush;
\r
1913 int suppressVisibleEffects = 0; // [HGM] kludge to request updating sizeInfo only
\r
1914 if((int)boardSize >= 1000 ) { boardSize -= 1000; suppressVisibleEffects = 1; }
\r
1916 /* [HGM] call with -2 uses old size (for if nr of files, ranks changes) */
\r
1917 if(boardSize == (BoardSize)(-2) ) boardSize = oldBoardSize;
\r
1919 oldRect.left = wpMain.x; //[HGM] placement: remember previous window params
\r
1920 oldRect.top = wpMain.y;
\r
1921 oldRect.right = wpMain.x + wpMain.width;
\r
1922 oldRect.bottom = wpMain.y + wpMain.height;
\r
1924 tinyLayout = sizeInfo[boardSize].tinyLayout;
\r
1925 smallLayout = sizeInfo[boardSize].smallLayout;
\r
1926 squareSize = sizeInfo[boardSize].squareSize;
\r
1927 lineGap = sizeInfo[boardSize].lineGap;
\r
1928 minorSize = 0; /* [HGM] Kludge to see if demagnified pieces need to be shifted */
\r
1930 if( appData.overrideLineGap >= 0 && appData.overrideLineGap <= 5 ) {
\r
1931 lineGap = appData.overrideLineGap;
\r
1934 if (tinyLayout != oldTinyLayout) {
\r
1935 long style = GetWindowLong(hwndMain, GWL_STYLE);
\r
1937 style &= ~WS_SYSMENU;
\r
1938 InsertMenu(hmenu, IDM_Exit, MF_BYCOMMAND, IDM_Minimize,
\r
1939 "&Minimize\tCtrl+F4");
\r
1941 style |= WS_SYSMENU;
\r
1942 RemoveMenu(hmenu, IDM_Minimize, MF_BYCOMMAND);
\r
1944 SetWindowLong(hwndMain, GWL_STYLE, style);
\r
1946 for (i=0; menuBarText[tinyLayout][i]; i++) {
\r
1947 ModifyMenu(hmenu, i, MF_STRING|MF_BYPOSITION|MF_POPUP,
\r
1948 (UINT)GetSubMenu(hmenu, i), menuBarText[tinyLayout][i]);
\r
1950 DrawMenuBar(hwndMain);
\r
1953 boardWidth = BoardWidth(boardSize, BOARD_WIDTH);
\r
1954 boardHeight = BoardWidth(boardSize, BOARD_HEIGHT);
\r
1956 /* Get text area sizes */
\r
1957 hdc = GetDC(hwndMain);
\r
1958 if (appData.clockMode) {
\r
1959 sprintf(buf, "White: %s", TimeString(23*60*60*1000L));
\r
1961 sprintf(buf, "White");
\r
1963 oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);
\r
1964 GetTextExtentPoint(hdc, buf, strlen(buf), &clockSize);
\r
1965 SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);
\r
1966 str = "We only care about the height here";
\r
1967 GetTextExtentPoint(hdc, str, strlen(str), &messageSize);
\r
1968 SelectObject(hdc, oldFont);
\r
1969 ReleaseDC(hwndMain, hdc);
\r
1971 /* Compute where everything goes */
\r
1972 if((first.programLogo || second.programLogo) && !tinyLayout) {
\r
1973 /* [HGM] logo: if either logo is on, reserve space for it */
\r
1974 logoHeight = 2*clockSize.cy;
\r
1975 leftLogoRect.left = OUTER_MARGIN;
\r
1976 leftLogoRect.right = leftLogoRect.left + 4*clockSize.cy;
\r
1977 leftLogoRect.top = OUTER_MARGIN;
\r
1978 leftLogoRect.bottom = OUTER_MARGIN + logoHeight;
\r
1980 rightLogoRect.right = OUTER_MARGIN + boardWidth;
\r
1981 rightLogoRect.left = rightLogoRect.right - 4*clockSize.cy;
\r
1982 rightLogoRect.top = OUTER_MARGIN;
\r
1983 rightLogoRect.bottom = OUTER_MARGIN + logoHeight;
\r
1986 whiteRect.left = leftLogoRect.right;
\r
1987 whiteRect.right = OUTER_MARGIN + boardWidth/2 - INNER_MARGIN/2;
\r
1988 whiteRect.top = OUTER_MARGIN;
\r
1989 whiteRect.bottom = whiteRect.top + logoHeight;
\r
1991 blackRect.right = rightLogoRect.left;
\r
1992 blackRect.left = whiteRect.right + INNER_MARGIN;
\r
1993 blackRect.top = whiteRect.top;
\r
1994 blackRect.bottom = whiteRect.bottom;
\r
1996 whiteRect.left = OUTER_MARGIN;
\r
1997 whiteRect.right = whiteRect.left + boardWidth/2 - INNER_MARGIN/2;
\r
1998 whiteRect.top = OUTER_MARGIN;
\r
1999 whiteRect.bottom = whiteRect.top + clockSize.cy;
\r
2001 blackRect.left = whiteRect.right + INNER_MARGIN;
\r
2002 blackRect.right = blackRect.left + boardWidth/2 - 1;
\r
2003 blackRect.top = whiteRect.top;
\r
2004 blackRect.bottom = whiteRect.bottom;
\r
2007 messageRect.left = OUTER_MARGIN + MESSAGE_LINE_LEFTMARGIN;
\r
2008 if (appData.showButtonBar) {
\r
2009 messageRect.right = OUTER_MARGIN + boardWidth // [HGM] logo: expressed independent of clock placement
\r
2010 - N_BUTTONS*BUTTON_WIDTH - MESSAGE_LINE_LEFTMARGIN;
\r
2012 messageRect.right = OUTER_MARGIN + boardWidth;
\r
2014 messageRect.top = whiteRect.bottom + INNER_MARGIN;
\r
2015 messageRect.bottom = messageRect.top + messageSize.cy;
\r
2017 boardRect.left = OUTER_MARGIN;
\r
2018 boardRect.right = boardRect.left + boardWidth;
\r
2019 boardRect.top = messageRect.bottom + INNER_MARGIN;
\r
2020 boardRect.bottom = boardRect.top + boardHeight;
\r
2022 sizeInfo[boardSize].cliWidth = boardRect.right + OUTER_MARGIN;
\r
2023 sizeInfo[boardSize].cliHeight = boardRect.bottom + OUTER_MARGIN;
\r
2024 oldBoardSize = boardSize;
\r
2025 oldTinyLayout = tinyLayout;
\r
2026 winW = 2 * GetSystemMetrics(SM_CXFRAME) + boardRect.right + OUTER_MARGIN;
\r
2027 winH = 2 * GetSystemMetrics(SM_CYFRAME) + GetSystemMetrics(SM_CYMENU) +
\r
2028 GetSystemMetrics(SM_CYCAPTION) + boardRect.bottom + OUTER_MARGIN;
\r
2029 if(suppressVisibleEffects) return; // [HGM] when called for filling sizeInfo only
\r
2030 wpMain.width = winW; // [HGM] placement: set through temporary which can used by initial sizing choice
\r
2031 wpMain.height = winH; // without disturbing window attachments
\r
2032 GetWindowRect(hwndMain, &wrect);
\r
2033 SetWindowPos(hwndMain, NULL, 0, 0, wpMain.width, wpMain.height,
\r
2034 SWP_NOCOPYBITS|SWP_NOZORDER|SWP_NOMOVE);
\r
2036 // [HGM] placement: let attached windows follow size change.
\r
2037 ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, moveHistoryDialog, &wpMoveHistory );
\r
2038 ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, evalGraphDialog, &wpEvalGraph );
\r
2039 ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, engineOutputDialog, &wpEngineOutput );
\r
2040 ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, gameListDialog, &wpGameList );
\r
2041 ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, hwndConsole, &wpConsole );
\r
2043 /* compensate if menu bar wrapped */
\r
2044 GetClientRect(hwndMain, &crect);
\r
2045 offby = boardRect.bottom + OUTER_MARGIN - crect.bottom;
\r
2046 wpMain.height += offby;
\r
2048 case WMSZ_TOPLEFT:
\r
2049 SetWindowPos(hwndMain, NULL,
\r
2050 wrect.right - wpMain.width, wrect.bottom - wpMain.height,
\r
2051 wpMain.width, wpMain.height, SWP_NOCOPYBITS|SWP_NOZORDER);
\r
2054 case WMSZ_TOPRIGHT:
\r
2056 SetWindowPos(hwndMain, NULL,
\r
2057 wrect.left, wrect.bottom - wpMain.height,
\r
2058 wpMain.width, wpMain.height, SWP_NOCOPYBITS|SWP_NOZORDER);
\r
2061 case WMSZ_BOTTOMLEFT:
\r
2063 SetWindowPos(hwndMain, NULL,
\r
2064 wrect.right - wpMain.width, wrect.top,
\r
2065 wpMain.width, wpMain.height, SWP_NOCOPYBITS|SWP_NOZORDER);
\r
2068 case WMSZ_BOTTOMRIGHT:
\r
2072 SetWindowPos(hwndMain, NULL, 0, 0, wpMain.width, wpMain.height,
\r
2073 SWP_NOCOPYBITS|SWP_NOZORDER|SWP_NOMOVE);
\r
2078 for (i = 0; i < N_BUTTONS; i++) {
\r
2079 if (buttonDesc[i].hwnd != NULL) {
\r
2080 DestroyWindow(buttonDesc[i].hwnd);
\r
2081 buttonDesc[i].hwnd = NULL;
\r
2083 if (appData.showButtonBar) {
\r
2084 buttonDesc[i].hwnd =
\r
2085 CreateWindow("BUTTON", buttonDesc[i].label,
\r
2086 WS_VISIBLE | WS_CHILD | BS_PUSHBUTTON,
\r
2087 boardRect.right - BUTTON_WIDTH*(N_BUTTONS-i),
\r
2088 messageRect.top, BUTTON_WIDTH, messageSize.cy, hwndMain,
\r
2089 (HMENU) buttonDesc[i].id,
\r
2090 (HINSTANCE) GetWindowLong(hwndMain, GWL_HINSTANCE), NULL);
\r
2092 SendMessage(buttonDesc[i].hwnd, WM_SETFONT,
\r
2093 (WPARAM)font[boardSize][MESSAGE_FONT]->hf,
\r
2094 MAKELPARAM(FALSE, 0));
\r
2096 if (buttonDesc[i].id == IDM_Pause)
\r
2097 hwndPause = buttonDesc[i].hwnd;
\r
2098 buttonDesc[i].wndproc = (WNDPROC)
\r
2099 SetWindowLong(buttonDesc[i].hwnd, GWL_WNDPROC, (LONG) ButtonProc);
\r
2102 if (gridPen != NULL) DeleteObject(gridPen);
\r
2103 if (highlightPen != NULL) DeleteObject(highlightPen);
\r
2104 if (premovePen != NULL) DeleteObject(premovePen);
\r
2105 if (lineGap != 0) {
\r
2106 logbrush.lbStyle = BS_SOLID;
\r
2107 logbrush.lbColor = RGB(0, 0, 0); /* grid pen color = black */
\r
2109 ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,
\r
2110 lineGap, &logbrush, 0, NULL);
\r
2111 logbrush.lbColor = highlightSquareColor;
\r
2113 ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,
\r
2114 lineGap, &logbrush, 0, NULL);
\r
2116 logbrush.lbColor = premoveHighlightColor;
\r
2118 ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,
\r
2119 lineGap, &logbrush, 0, NULL);
\r
2121 /* [HGM] Loop had to be split in part for vert. and hor. lines */
\r
2122 for (i = 0; i < BOARD_HEIGHT + 1; i++) {
\r
2123 gridEndpoints[i*2].x = boardRect.left + lineGap / 2;
\r
2124 gridEndpoints[i*2].y = gridEndpoints[i*2 + 1].y =
\r
2125 boardRect.top + lineGap / 2 + (i * (squareSize + lineGap));
\r
2126 gridEndpoints[i*2 + 1].x = boardRect.left + lineGap / 2 +
\r
2127 BOARD_WIDTH * (squareSize + lineGap);
\r
2128 gridVertexCounts[i*2] = gridVertexCounts[i*2 + 1] = 2;
\r
2130 for (i = 0; i < BOARD_WIDTH + 1; i++) {
\r
2131 gridEndpoints[i*2 + BOARD_HEIGHT*2 + 2].y = boardRect.top + lineGap / 2;
\r
2132 gridEndpoints[i*2 + BOARD_HEIGHT*2 + 2].x =
\r
2133 gridEndpoints[i*2 + 1 + BOARD_HEIGHT*2 + 2].x = boardRect.left +
\r
2134 lineGap / 2 + (i * (squareSize + lineGap));
\r
2135 gridEndpoints[i*2 + 1 + BOARD_HEIGHT*2 + 2].y =
\r
2136 boardRect.top + BOARD_HEIGHT * (squareSize + lineGap);
\r
2137 gridVertexCounts[i*2] = gridVertexCounts[i*2 + 1] = 2;
\r
2141 /* [HGM] Licensing requirement */
\r
2143 if(gameInfo.variant == VariantGothic) GothicPopUp( GOTHIC, VariantGothic); else
\r
2146 if(gameInfo.variant == VariantFalcon) GothicPopUp( FALCON, VariantFalcon); else
\r
2148 GothicPopUp( "", VariantNormal);
\r
2151 /* if (boardSize == oldBoardSize) return; [HGM] variant might have changed */
\r
2153 /* Load piece bitmaps for this board size */
\r
2154 for (i=0; i<=2; i++) {
\r
2155 for (piece = WhitePawn;
\r
2156 (int) piece < (int) BlackPawn;
\r
2157 piece = (ChessSquare) ((int) piece + 1)) {
\r
2158 if (pieceBitmap[i][piece] != NULL)
\r
2159 DeleteObject(pieceBitmap[i][piece]);
\r
2163 fontBitmapSquareSize = 0; /* [HGM] render: make sure pieces will be recreated, as we might need others now */
\r
2164 // Orthodox Chess pieces
\r
2165 pieceBitmap[0][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "s");
\r
2166 pieceBitmap[0][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "s");
\r
2167 pieceBitmap[0][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "s");
\r
2168 pieceBitmap[0][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "s");
\r
2169 pieceBitmap[0][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "s");
\r
2170 pieceBitmap[1][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "o");
\r
2171 pieceBitmap[1][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "o");
\r
2172 pieceBitmap[1][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "o");
\r
2173 pieceBitmap[1][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "o");
\r
2174 pieceBitmap[1][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "o");
\r
2175 pieceBitmap[2][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "w");
\r
2176 pieceBitmap[2][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "w");
\r
2177 pieceBitmap[2][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "w");
\r
2178 pieceBitmap[2][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "w");
\r
2179 pieceBitmap[2][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "w");
\r
2180 if( !strcmp(appData.variant, "shogi") && (squareSize==72 || squareSize==49)) {
\r
2181 // in Shogi, Hijack the unused Queen for Lance
\r
2182 pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "s");
\r
2183 pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "o");
\r
2184 pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "w");
\r
2186 pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "s");
\r
2187 pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "o");
\r
2188 pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "w");
\r
2191 if(squareSize <= 72 && squareSize >= 33) {
\r
2192 /* A & C are available in most sizes now */
\r
2193 if(squareSize != 49 && squareSize != 72 && squareSize != 33) { // Vortex-like
\r
2194 pieceBitmap[0][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "s");
\r
2195 pieceBitmap[1][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "o");
\r
2196 pieceBitmap[2][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "w");
\r
2197 pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "s");
\r
2198 pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "o");
\r
2199 pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "w");
\r
2200 pieceBitmap[0][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "s");
\r
2201 pieceBitmap[1][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "o");
\r
2202 pieceBitmap[2][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "w");
\r
2203 pieceBitmap[0][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "s");
\r
2204 pieceBitmap[1][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "o");
\r
2205 pieceBitmap[2][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "w");
\r
2206 } else { // Smirf-like
\r
2207 pieceBitmap[0][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "s");
\r
2208 pieceBitmap[1][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "o");
\r
2209 pieceBitmap[2][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "w");
\r
2211 if(gameInfo.variant == VariantGothic) { // Vortex-like
\r
2212 pieceBitmap[0][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "s");
\r
2213 pieceBitmap[1][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "o");
\r
2214 pieceBitmap[2][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "w");
\r
2215 } else { // WinBoard standard
\r
2216 pieceBitmap[0][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "s");
\r
2217 pieceBitmap[1][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "o");
\r
2218 pieceBitmap[2][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "w");
\r
2223 if(squareSize==72 || squareSize==49 || squareSize==33) { /* experiment with some home-made bitmaps */
\r
2224 pieceBitmap[0][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "s");
\r
2225 pieceBitmap[1][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "o");
\r
2226 pieceBitmap[2][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "w");
\r
2227 pieceBitmap[0][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "s");
\r
2228 pieceBitmap[1][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "o");
\r
2229 pieceBitmap[2][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "w");
\r
2230 pieceBitmap[0][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "s");
\r
2231 pieceBitmap[1][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "o");
\r
2232 pieceBitmap[2][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "w");
\r
2233 pieceBitmap[0][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "s");
\r
2234 pieceBitmap[1][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "o");
\r
2235 pieceBitmap[2][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "w");
\r
2236 pieceBitmap[0][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "s");
\r
2237 pieceBitmap[1][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "o");
\r
2238 pieceBitmap[2][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "w");
\r
2239 pieceBitmap[0][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "s");
\r
2240 pieceBitmap[1][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "o");
\r
2241 pieceBitmap[2][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "w");
\r
2242 pieceBitmap[0][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "s");
\r
2243 pieceBitmap[1][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "o");
\r
2244 pieceBitmap[2][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "w");
\r
2245 pieceBitmap[0][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "s");
\r
2246 pieceBitmap[1][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "o");
\r
2247 pieceBitmap[2][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "w");
\r
2248 pieceBitmap[0][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "s");
\r
2249 pieceBitmap[1][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "o");
\r
2250 pieceBitmap[2][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "w");
\r
2251 pieceBitmap[0][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "s");
\r
2252 pieceBitmap[1][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "o");
\r
2253 pieceBitmap[2][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "w");
\r
2255 if(gameInfo.variant == VariantShogi) { /* promoted Gold represemtations */
\r
2256 pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "wp", squareSize, "s");
\r
2257 pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "wp", squareSize, "o");
\r
2258 pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "w", squareSize, "w");
\r
2259 pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "wn", squareSize, "s");
\r
2260 pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "wn", squareSize, "o");
\r
2261 pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "w", squareSize, "w");
\r
2262 pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "ws", squareSize, "s");
\r
2263 pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "ws", squareSize, "o");
\r
2264 pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "w", squareSize, "w");
\r
2265 pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "wl", squareSize, "s");
\r
2266 pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "wl", squareSize, "o");
\r
2267 pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "w", squareSize, "w");
\r
2269 pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "s");
\r
2270 pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "o");
\r
2271 pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "w");
\r
2272 pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "s");
\r
2273 pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "o");
\r
2274 pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "w");
\r
2275 pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "s");
\r
2276 pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "o");
\r
2277 pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "w");
\r
2278 pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "s");
\r
2279 pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "o");
\r
2280 pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "w");
\r
2283 } else { /* other size, no special bitmaps available. Use smaller symbols */
\r
2284 if((int)boardSize < 2) minorSize = sizeInfo[0].squareSize;
\r
2285 else minorSize = sizeInfo[(int)boardSize - 2].squareSize;
\r
2286 pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "s");
\r
2287 pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "o");
\r
2288 pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "w");
\r
2289 pieceBitmap[0][WhiteCardinal] = DoLoadBitmap(hInst, "b", minorSize, "s");
\r
2290 pieceBitmap[1][WhiteCardinal] = DoLoadBitmap(hInst, "b", minorSize, "o");
\r
2291 pieceBitmap[2][WhiteCardinal] = DoLoadBitmap(hInst, "b", minorSize, "w");
\r
2292 pieceBitmap[0][WhiteDragon] = DoLoadBitmap(hInst, "r", minorSize, "s");
\r
2293 pieceBitmap[1][WhiteDragon] = DoLoadBitmap(hInst, "r", minorSize, "o");
\r
2294 pieceBitmap[2][WhiteDragon] = DoLoadBitmap(hInst, "r", minorSize, "w");
\r
2295 pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "s");
\r
2296 pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "o");
\r
2297 pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "w");
\r
2301 if(gameInfo.variant == VariantShogi && squareSize == 58)
\r
2302 /* special Shogi support in this size */
\r
2303 { for (i=0; i<=2; i++) { /* replace all bitmaps */
\r
2304 for (piece = WhitePawn;
\r
2305 (int) piece < (int) BlackPawn;
\r
2306 piece = (ChessSquare) ((int) piece + 1)) {
\r
2307 if (pieceBitmap[i][piece] != NULL)
\r
2308 DeleteObject(pieceBitmap[i][piece]);
\r
2311 pieceBitmap[0][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "o");
\r
2312 pieceBitmap[0][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "o");
\r
2313 pieceBitmap[0][WhiteBishop] = DoLoadBitmap(hInst, "sb", squareSize, "o");
\r
2314 pieceBitmap[0][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "o");
\r
2315 pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "o");
\r
2316 pieceBitmap[0][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "o");
\r
2317 pieceBitmap[0][WhiteFerz] = DoLoadBitmap(hInst, "sf", squareSize, "o");
\r
2318 pieceBitmap[0][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "o");
\r
2319 pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "su", squareSize, "o");
\r
2320 pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "sh", squareSize, "o");
\r
2321 pieceBitmap[0][WhiteCardinal] = DoLoadBitmap(hInst, "sa", squareSize, "o");
\r
2322 pieceBitmap[0][WhiteDragon] = DoLoadBitmap(hInst, "sc", squareSize, "o");
\r
2323 pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "sg", squareSize, "o");
\r
2324 pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "ss", squareSize, "o");
\r
2325 pieceBitmap[1][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "o");
\r
2326 pieceBitmap[1][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "o");
\r
2327 pieceBitmap[1][WhiteBishop] = DoLoadBitmap(hInst, "sb", squareSize, "o");
\r
2328 pieceBitmap[1][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "o");
\r
2329 pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "o");
\r
2330 pieceBitmap[1][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "o");
\r
2331 pieceBitmap[1][WhiteFerz] = DoLoadBitmap(hInst, "sf", squareSize, "o");
\r
2332 pieceBitmap[1][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "o");
\r
2333 pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "su", squareSize, "o");
\r
2334 pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "sh", squareSize, "o");
\r
2335 pieceBitmap[1][WhiteCardinal] = DoLoadBitmap(hInst, "sa", squareSize, "o");
\r
2336 pieceBitmap[1][WhiteDragon] = DoLoadBitmap(hInst, "sc", squareSize, "o");
\r
2337 pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "sg", squareSize, "o");
\r
2338 pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "ss", squareSize, "o");
\r
2339 pieceBitmap[2][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "w");
\r
2340 pieceBitmap[2][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "w");
\r
2341 pieceBitmap[2][WhiteBishop] = DoLoadBitmap(hInst, "sr", squareSize, "w");
\r
2342 pieceBitmap[2][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "w");
\r
2343 pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "w");
\r
2344 pieceBitmap[2][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "w");
\r
2345 pieceBitmap[2][WhiteFerz] = DoLoadBitmap(hInst, "sw", squareSize, "w");
\r
2346 pieceBitmap[2][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "w");
\r
2347 pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "sp", squareSize, "w");
\r
2348 pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "sn", squareSize, "w");
\r
2349 pieceBitmap[2][WhiteCardinal] = DoLoadBitmap(hInst, "sr", squareSize, "w");
\r
2350 pieceBitmap[2][WhiteDragon] = DoLoadBitmap(hInst, "sr", squareSize, "w");
\r
2351 pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "sl", squareSize, "w");
\r
2352 pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "sw", squareSize, "w");
\r
2358 PieceBitmap(ChessSquare p, int kind)
\r
2360 if ((int) p >= (int) BlackPawn)
\r
2361 p = (ChessSquare) ((int) p - (int) BlackPawn + (int) WhitePawn);
\r
2363 return pieceBitmap[kind][(int) p];
\r
2366 /***************************************************************/
\r
2368 #define MIN(a,b) (((a) < (b)) ? (a) : (b))
\r
2369 #define MAX(a,b) (((a) > (b)) ? (a) : (b))
\r
2371 #define MIN3(a,b,c) (((a) < (b) && (a) < (c)) ? (a) : (((b) < (a) && (b) < (c)) ? (b) : (c)))
\r
2372 #define MAX3(a,b,c) (((a) > (b) && (a) > (c)) ? (a) : (((b) > (a) && (b) > (c)) ? (b) : (c)))
\r
2376 SquareToPos(int row, int column, int * x, int * y)
\r
2379 *x = boardRect.left + lineGap + ((BOARD_WIDTH-1)-column) * (squareSize + lineGap);
\r
2380 *y = boardRect.top + lineGap + row * (squareSize + lineGap);
\r
2382 *x = boardRect.left + lineGap + column * (squareSize + lineGap);
\r
2383 *y = boardRect.top + lineGap + ((BOARD_HEIGHT-1)-row) * (squareSize + lineGap);
\r
2388 DrawCoordsOnDC(HDC hdc)
\r
2390 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'};
\r
2391 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'};
\r
2392 char str[2] = { NULLCHAR, NULLCHAR };
\r
2393 int oldMode, oldAlign, x, y, start, i;
\r
2397 if (!appData.showCoords)
\r
2400 start = flipView ? 1-(ONE!='1') : 23+(ONE!='1')-BOARD_HEIGHT;
\r
2402 oldBrush = SelectObject(hdc, GetStockObject(BLACK_BRUSH));
\r
2403 oldMode = SetBkMode(hdc, (appData.monoMode ? OPAQUE : TRANSPARENT));
\r
2404 oldAlign = GetTextAlign(hdc);
\r
2405 oldFont = SelectObject(hdc, font[boardSize][COORD_FONT]->hf);
\r
2407 y = boardRect.top + lineGap;
\r
2408 x = boardRect.left + lineGap + gameInfo.holdingsWidth*(squareSize + lineGap);
\r
2410 SetTextAlign(hdc, TA_LEFT|TA_TOP);
\r
2411 for (i = 0; i < BOARD_HEIGHT; i++) {
\r
2412 str[0] = files[start + i];
\r
2413 ExtTextOut(hdc, x + 2, y + 1, 0, NULL, str, 1, NULL);
\r
2414 y += squareSize + lineGap;
\r
2417 start = flipView ? 12-(BOARD_RGHT-BOARD_LEFT) : 12;
\r
2419 SetTextAlign(hdc, TA_RIGHT|TA_BOTTOM);
\r
2420 for (i = 0; i < BOARD_RGHT - BOARD_LEFT; i++) {
\r
2421 str[0] = ranks[start + i];
\r
2422 ExtTextOut(hdc, x + squareSize - 2, y - 1, 0, NULL, str, 1, NULL);
\r
2423 x += squareSize + lineGap;
\r
2426 SelectObject(hdc, oldBrush);
\r
2427 SetBkMode(hdc, oldMode);
\r
2428 SetTextAlign(hdc, oldAlign);
\r
2429 SelectObject(hdc, oldFont);
\r
2433 DrawGridOnDC(HDC hdc)
\r
2437 if (lineGap != 0) {
\r
2438 oldPen = SelectObject(hdc, gridPen);
\r
2439 PolyPolyline(hdc, gridEndpoints, gridVertexCounts, BOARD_WIDTH+BOARD_HEIGHT + 2);
\r
2440 SelectObject(hdc, oldPen);
\r
2444 #define HIGHLIGHT_PEN 0
\r
2445 #define PREMOVE_PEN 1
\r
2448 DrawHighlightOnDC(HDC hdc, BOOLEAN on, int x, int y, int pen)
\r
2451 HPEN oldPen, hPen;
\r
2452 if (lineGap == 0) return;
\r
2454 x1 = boardRect.left +
\r
2455 lineGap/2 + ((BOARD_WIDTH-1)-x) * (squareSize + lineGap);
\r
2456 y1 = boardRect.top +
\r
2457 lineGap/2 + y * (squareSize + lineGap);
\r
2459 x1 = boardRect.left +
\r
2460 lineGap/2 + x * (squareSize + lineGap);
\r
2461 y1 = boardRect.top +
\r
2462 lineGap/2 + ((BOARD_HEIGHT-1)-y) * (squareSize + lineGap);
\r
2464 hPen = pen ? premovePen : highlightPen;
\r
2465 oldPen = SelectObject(hdc, on ? hPen : gridPen);
\r
2466 MoveToEx(hdc, x1, y1, NULL);
\r
2467 LineTo(hdc, x1 + squareSize + lineGap, y1);
\r
2468 LineTo(hdc, x1 + squareSize + lineGap, y1 + squareSize + lineGap);
\r
2469 LineTo(hdc, x1, y1 + squareSize + lineGap);
\r
2470 LineTo(hdc, x1, y1);
\r
2471 SelectObject(hdc, oldPen);
\r
2475 DrawHighlightsOnDC(HDC hdc)
\r
2478 for (i=0; i<2; i++) {
\r
2479 if (highlightInfo.sq[i].x >= 0 && highlightInfo.sq[i].y >= 0)
\r
2480 DrawHighlightOnDC(hdc, TRUE,
\r
2481 highlightInfo.sq[i].x, highlightInfo.sq[i].y,
\r
2484 for (i=0; i<2; i++) {
\r
2485 if (premoveHighlightInfo.sq[i].x >= 0 &&
\r
2486 premoveHighlightInfo.sq[i].y >= 0) {
\r
2487 DrawHighlightOnDC(hdc, TRUE,
\r
2488 premoveHighlightInfo.sq[i].x,
\r
2489 premoveHighlightInfo.sq[i].y,
\r
2495 /* Note: sqcolor is used only in monoMode */
\r
2496 /* Note that this code is largely duplicated in woptions.c,
\r
2497 function DrawSampleSquare, so that needs to be updated too */
\r
2499 DrawPieceOnDC(HDC hdc, ChessSquare piece, int color, int sqcolor, int x, int y, HDC tmphdc)
\r
2501 HBITMAP oldBitmap;
\r
2505 if (appData.blindfold) return;
\r
2507 /* [AS] Use font-based pieces if needed */
\r
2508 if( fontBitmapSquareSize >= 0 && squareSize > 32 ) {
\r
2509 /* Create piece bitmaps, or do nothing if piece set is up to date */
\r
2510 CreatePiecesFromFont();
\r
2512 if( fontBitmapSquareSize == squareSize ) {
\r
2513 int index = TranslatePieceToFontPiece(piece);
\r
2515 SelectObject( tmphdc, hPieceMask[ index ] );
\r
2519 squareSize, squareSize,
\r
2524 SelectObject( tmphdc, hPieceFace[ index ] );
\r
2528 squareSize, squareSize,
\r
2537 if (appData.monoMode) {
\r
2538 SelectObject(tmphdc, PieceBitmap(piece,
\r
2539 color == sqcolor ? OUTLINE_PIECE : SOLID_PIECE));
\r
2540 BitBlt(hdc, x, y, squareSize, squareSize, tmphdc, 0, 0,
\r
2541 sqcolor ? SRCCOPY : NOTSRCCOPY);
\r
2543 tmpSize = squareSize;
\r
2545 ((piece >= (int)WhiteNightrider && piece <= WhiteGrasshopper) ||
\r
2546 (piece >= (int)BlackNightrider && piece <= BlackGrasshopper)) ) {
\r
2547 /* [HGM] no bitmap available for promoted pieces in Crazyhouse */
\r
2548 /* Bitmaps of smaller size are substituted, but we have to align them */
\r
2549 x += (squareSize - minorSize)>>1;
\r
2550 y += squareSize - minorSize - 2;
\r
2551 tmpSize = minorSize;
\r
2553 if (color || appData.allWhite ) {
\r
2554 oldBitmap = SelectObject(tmphdc, PieceBitmap(piece, WHITE_PIECE));
\r
2556 oldBrush = SelectObject(hdc, whitePieceBrush);
\r
2557 else oldBrush = SelectObject(hdc, blackPieceBrush);
\r
2558 if(appData.upsideDown && color==flipView)
\r
2559 StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, 0x00B8074A);
\r
2561 BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, 0x00B8074A);
\r
2562 /* Use black for outline of white pieces */
\r
2563 SelectObject(tmphdc, PieceBitmap(piece, OUTLINE_PIECE));
\r
2564 if(appData.upsideDown && color==flipView)
\r
2565 StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, SRCAND);
\r
2567 BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, SRCAND);
\r
2569 /* Use square color for details of black pieces */
\r
2570 oldBitmap = SelectObject(tmphdc, PieceBitmap(piece, SOLID_PIECE));
\r
2571 oldBrush = SelectObject(hdc, blackPieceBrush);
\r
2572 if(appData.upsideDown && !flipView)
\r
2573 StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, 0x00B8074A);
\r
2575 BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, 0x00B8074A);
\r
2577 SelectObject(hdc, oldBrush);
\r
2578 SelectObject(tmphdc, oldBitmap);
\r
2582 /* [AS] Compute a drawing mode for a square, based on specified settings (see DrawTile) */
\r
2583 int GetBackTextureMode( int algo )
\r
2585 int result = BACK_TEXTURE_MODE_DISABLED;
\r
2589 case BACK_TEXTURE_MODE_PLAIN:
\r
2590 result = 1; /* Always use identity map */
\r
2592 case BACK_TEXTURE_MODE_FULL_RANDOM:
\r
2593 result = 1 + (myrandom() % 3); /* Pick a transformation at random */
\r
2601 [AS] Compute and save texture drawing info, otherwise we may not be able
\r
2602 to handle redraws cleanly (as random numbers would always be different).
\r
2604 VOID RebuildTextureSquareInfo()
\r
2614 ZeroMemory( &backTextureSquareInfo, sizeof(backTextureSquareInfo) );
\r
2616 if( liteBackTexture != NULL ) {
\r
2617 if( GetObject( liteBackTexture, sizeof(bi), &bi ) > 0 ) {
\r
2618 lite_w = bi.bmWidth;
\r
2619 lite_h = bi.bmHeight;
\r
2623 if( darkBackTexture != NULL ) {
\r
2624 if( GetObject( darkBackTexture, sizeof(bi), &bi ) > 0 ) {
\r
2625 dark_w = bi.bmWidth;
\r
2626 dark_h = bi.bmHeight;
\r
2630 for( row=0; row<BOARD_HEIGHT; row++ ) {
\r
2631 for( col=0; col<BOARD_WIDTH; col++ ) {
\r
2632 if( (col + row) & 1 ) {
\r
2634 if( lite_w >= squareSize && lite_h >= squareSize ) {
\r
2635 backTextureSquareInfo[row][col].x = col * (lite_w - squareSize) / (BOARD_WIDTH-1); /* [HGM] divide by size-1 in stead of size! */
\r
2636 backTextureSquareInfo[row][col].y = (BOARD_HEIGHT-1-row) * (lite_h - squareSize) / (BOARD_HEIGHT-1);
\r
2637 backTextureSquareInfo[row][col].mode = GetBackTextureMode(liteBackTextureMode);
\r
2642 if( dark_w >= squareSize && dark_h >= squareSize ) {
\r
2643 backTextureSquareInfo[row][col].x = col * (dark_w - squareSize) / (BOARD_WIDTH-1);
\r
2644 backTextureSquareInfo[row][col].y = (BOARD_HEIGHT-1-row) * (dark_h - squareSize) / (BOARD_HEIGHT-1);
\r
2645 backTextureSquareInfo[row][col].mode = GetBackTextureMode(darkBackTextureMode);
\r
2652 /* [AS] Arrow highlighting support */
\r
2654 static int A_WIDTH = 5; /* Width of arrow body */
\r
2656 #define A_HEIGHT_FACTOR 6 /* Length of arrow "point", relative to body width */
\r
2657 #define A_WIDTH_FACTOR 3 /* Width of arrow "point", relative to body width */
\r
2659 static double Sqr( double x )
\r
2664 static int Round( double x )
\r
2666 return (int) (x + 0.5);
\r
2669 /* Draw an arrow between two points using current settings */
\r
2670 VOID DrawArrowBetweenPoints( HDC hdc, int s_x, int s_y, int d_x, int d_y )
\r
2673 double dx, dy, j, k, x, y;
\r
2675 if( d_x == s_x ) {
\r
2676 int h = (d_y > s_y) ? +A_WIDTH*A_HEIGHT_FACTOR : -A_WIDTH*A_HEIGHT_FACTOR;
\r
2678 arrow[0].x = s_x + A_WIDTH;
\r
2681 arrow[1].x = s_x + A_WIDTH;
\r
2682 arrow[1].y = d_y - h;
\r
2684 arrow[2].x = s_x + A_WIDTH*A_WIDTH_FACTOR;
\r
2685 arrow[2].y = d_y - h;
\r
2690 arrow[4].x = s_x - A_WIDTH*A_WIDTH_FACTOR;
\r
2691 arrow[4].y = d_y - h;
\r
2693 arrow[5].x = s_x - A_WIDTH;
\r
2694 arrow[5].y = d_y - h;
\r
2696 arrow[6].x = s_x - A_WIDTH;
\r
2699 else if( d_y == s_y ) {
\r
2700 int w = (d_x > s_x) ? +A_WIDTH*A_HEIGHT_FACTOR : -A_WIDTH*A_HEIGHT_FACTOR;
\r
2703 arrow[0].y = s_y + A_WIDTH;
\r
2705 arrow[1].x = d_x - w;
\r
2706 arrow[1].y = s_y + A_WIDTH;
\r
2708 arrow[2].x = d_x - w;
\r
2709 arrow[2].y = s_y + A_WIDTH*A_WIDTH_FACTOR;
\r
2714 arrow[4].x = d_x - w;
\r
2715 arrow[4].y = s_y - A_WIDTH*A_WIDTH_FACTOR;
\r
2717 arrow[5].x = d_x - w;
\r
2718 arrow[5].y = s_y - A_WIDTH;
\r
2721 arrow[6].y = s_y - A_WIDTH;
\r
2724 /* [AS] Needed a lot of paper for this! :-) */
\r
2725 dy = (double) (d_y - s_y) / (double) (d_x - s_x);
\r
2726 dx = (double) (s_x - d_x) / (double) (s_y - d_y);
\r
2728 j = sqrt( Sqr(A_WIDTH) / (1.0 + Sqr(dx)) );
\r
2730 k = sqrt( Sqr(A_WIDTH*A_HEIGHT_FACTOR) / (1.0 + Sqr(dy)) );
\r
2735 arrow[0].x = Round(x - j);
\r
2736 arrow[0].y = Round(y + j*dx);
\r
2738 arrow[1].x = Round(x + j);
\r
2739 arrow[1].y = Round(y - j*dx);
\r
2742 x = (double) d_x - k;
\r
2743 y = (double) d_y - k*dy;
\r
2746 x = (double) d_x + k;
\r
2747 y = (double) d_y + k*dy;
\r
2750 arrow[2].x = Round(x + j);
\r
2751 arrow[2].y = Round(y - j*dx);
\r
2753 arrow[3].x = Round(x + j*A_WIDTH_FACTOR);
\r
2754 arrow[3].y = Round(y - j*A_WIDTH_FACTOR*dx);
\r
2759 arrow[5].x = Round(x - j*A_WIDTH_FACTOR);
\r
2760 arrow[5].y = Round(y + j*A_WIDTH_FACTOR*dx);
\r
2762 arrow[6].x = Round(x - j);
\r
2763 arrow[6].y = Round(y + j*dx);
\r
2766 Polygon( hdc, arrow, 7 );
\r
2769 /* [AS] Draw an arrow between two squares */
\r
2770 VOID DrawArrowBetweenSquares( HDC hdc, int s_col, int s_row, int d_col, int d_row )
\r
2772 int s_x, s_y, d_x, d_y;
\r
2779 if( s_col == d_col && s_row == d_row ) {
\r
2783 /* Get source and destination points */
\r
2784 SquareToPos( s_row, s_col, &s_x, &s_y);
\r
2785 SquareToPos( d_row, d_col, &d_x, &d_y);
\r
2788 d_y += squareSize / 4;
\r
2790 else if( d_y < s_y ) {
\r
2791 d_y += 3 * squareSize / 4;
\r
2794 d_y += squareSize / 2;
\r
2798 d_x += squareSize / 4;
\r
2800 else if( d_x < s_x ) {
\r
2801 d_x += 3 * squareSize / 4;
\r
2804 d_x += squareSize / 2;
\r
2807 s_x += squareSize / 2;
\r
2808 s_y += squareSize / 2;
\r
2810 /* Adjust width */
\r
2811 A_WIDTH = squareSize / 14;
\r
2814 stLB.lbStyle = BS_SOLID;
\r
2815 stLB.lbColor = appData.highlightArrowColor;
\r
2818 hpen = CreatePen( PS_SOLID, 2, RGB(0x00,0x00,0x00) );
\r
2819 holdpen = SelectObject( hdc, hpen );
\r
2820 hbrush = CreateBrushIndirect( &stLB );
\r
2821 holdbrush = SelectObject( hdc, hbrush );
\r
2823 DrawArrowBetweenPoints( hdc, s_x, s_y, d_x, d_y );
\r
2825 SelectObject( hdc, holdpen );
\r
2826 SelectObject( hdc, holdbrush );
\r
2827 DeleteObject( hpen );
\r
2828 DeleteObject( hbrush );
\r
2831 BOOL HasHighlightInfo()
\r
2833 BOOL result = FALSE;
\r
2835 if( highlightInfo.sq[0].x >= 0 && highlightInfo.sq[0].y >= 0 &&
\r
2836 highlightInfo.sq[1].x >= 0 && highlightInfo.sq[1].y >= 0 )
\r
2844 BOOL IsDrawArrowEnabled()
\r
2846 BOOL result = FALSE;
\r
2848 if( appData.highlightMoveWithArrow && squareSize >= 32 ) {
\r
2855 VOID DrawArrowHighlight( HDC hdc )
\r
2857 if( IsDrawArrowEnabled() && HasHighlightInfo() ) {
\r
2858 DrawArrowBetweenSquares( hdc,
\r
2859 highlightInfo.sq[0].x, highlightInfo.sq[0].y,
\r
2860 highlightInfo.sq[1].x, highlightInfo.sq[1].y );
\r
2864 HRGN GetArrowHighlightClipRegion( HDC hdc )
\r
2866 HRGN result = NULL;
\r
2868 if( HasHighlightInfo() ) {
\r
2869 int x1, y1, x2, y2;
\r
2870 int sx, sy, dx, dy;
\r
2872 SquareToPos(highlightInfo.sq[0].y, highlightInfo.sq[0].x, &x1, &y1 );
\r
2873 SquareToPos(highlightInfo.sq[1].y, highlightInfo.sq[1].x, &x2, &y2 );
\r
2875 sx = MIN( x1, x2 );
\r
2876 sy = MIN( y1, y2 );
\r
2877 dx = MAX( x1, x2 ) + squareSize;
\r
2878 dy = MAX( y1, y2 ) + squareSize;
\r
2880 result = CreateRectRgn( sx, sy, dx, dy );
\r
2887 Warning: this function modifies the behavior of several other functions.
\r
2889 Basically, Winboard is optimized to avoid drawing the whole board if not strictly
\r
2890 needed. Unfortunately, the decision whether or not to perform a full or partial
\r
2891 repaint is scattered all over the place, which is not good for features such as
\r
2892 "arrow highlighting" that require a full repaint of the board.
\r
2894 So, I've tried to patch the code where I thought it made sense (e.g. after or during
\r
2895 user interaction, when speed is not so important) but especially to avoid errors
\r
2896 in the displayed graphics.
\r
2898 In such patched places, I always try refer to this function so there is a single
\r
2899 place to maintain knowledge.
\r
2901 To restore the original behavior, just return FALSE unconditionally.
\r
2903 BOOL IsFullRepaintPreferrable()
\r
2905 BOOL result = FALSE;
\r
2907 if( (appData.highlightLastMove || appData.highlightDragging) && IsDrawArrowEnabled() ) {
\r
2908 /* Arrow may appear on the board */
\r
2916 This function is called by DrawPosition to know whether a full repaint must
\r
2919 Only DrawPosition may directly call this function, which makes use of
\r
2920 some state information. Other function should call DrawPosition specifying
\r
2921 the repaint flag, and can use IsFullRepaintPreferrable if needed.
\r
2923 BOOL DrawPositionNeedsFullRepaint()
\r
2925 BOOL result = FALSE;
\r
2928 Probably a slightly better policy would be to trigger a full repaint
\r
2929 when animInfo.piece changes state (i.e. empty -> non-empty and viceversa),
\r
2930 but animation is fast enough that it's difficult to notice.
\r
2932 if( animInfo.piece == EmptySquare ) {
\r
2933 if( (appData.highlightLastMove || appData.highlightDragging) && IsDrawArrowEnabled() /*&& HasHighlightInfo()*/ ) {
\r
2942 DrawBoardOnDC(HDC hdc, Board board, HDC tmphdc)
\r
2944 int row, column, x, y, square_color, piece_color;
\r
2945 ChessSquare piece;
\r
2947 HDC texture_hdc = NULL;
\r
2949 /* [AS] Initialize background textures if needed */
\r
2950 if( liteBackTexture != NULL || darkBackTexture != NULL ) {
\r
2951 static int backTextureBoardSize; /* [HGM] boardsize: also new texture if board format changed */
\r
2952 if( backTextureSquareSize != squareSize
\r
2953 || backTextureBoardSize != BOARD_WIDTH+BOARD_FILES*BOARD_HEIGHT) {
\r
2954 backTextureBoardSize = BOARD_WIDTH+BOARD_FILES*BOARD_HEIGHT;
\r
2955 backTextureSquareSize = squareSize;
\r
2956 RebuildTextureSquareInfo();
\r
2959 texture_hdc = CreateCompatibleDC( hdc );
\r
2962 for (row = 0; row < BOARD_HEIGHT; row++) {
\r
2963 for (column = 0; column < BOARD_WIDTH; column++) {
\r
2965 SquareToPos(row, column, &x, &y);
\r
2967 piece = board[row][column];
\r
2969 square_color = ((column + row) % 2) == 1;
\r
2970 if( gameInfo.variant == VariantXiangqi ) {
\r
2971 square_color = !InPalace(row, column);
\r
2972 if(BOARD_HEIGHT&1) { if(row==BOARD_HEIGHT/2) square_color ^= 1; }
\r
2973 else if(row < BOARD_HEIGHT/2) square_color ^= 1;
\r
2975 piece_color = (int) piece < (int) BlackPawn;
\r
2978 /* [HGM] holdings file: light square or black */
\r
2979 if(column == BOARD_LEFT-2) {
\r
2980 if( row > BOARD_HEIGHT - gameInfo.holdingsSize - 1 )
\r
2983 DisplayHoldingsCount(hdc, x, y, 0, 0); /* black out */
\r
2987 if(column == BOARD_RGHT + 1 ) {
\r
2988 if( row < gameInfo.holdingsSize )
\r
2991 DisplayHoldingsCount(hdc, x, y, 0, 0);
\r
2995 if(column == BOARD_LEFT-1 ) /* left align */
\r
2996 DisplayHoldingsCount(hdc, x, y, flipView, (int) board[row][column]);
\r
2997 else if( column == BOARD_RGHT) /* right align */
\r
2998 DisplayHoldingsCount(hdc, x, y, !flipView, (int) board[row][column]);
\r
3000 if (appData.monoMode) {
\r
3001 if (piece == EmptySquare) {
\r
3002 BitBlt(hdc, x, y, squareSize, squareSize, 0, 0, 0,
\r
3003 square_color ? WHITENESS : BLACKNESS);
\r
3005 DrawPieceOnDC(hdc, piece, piece_color, square_color, x, y, tmphdc);
\r
3008 else if( backTextureSquareInfo[row][column].mode > 0 ) {
\r
3009 /* [AS] Draw the square using a texture bitmap */
\r
3010 HBITMAP hbm = SelectObject( texture_hdc, square_color ? liteBackTexture : darkBackTexture );
\r
3011 int r = row, c = column; // [HGM] do not flip board in flipView
\r
3012 if(flipView) { r = BOARD_HEIGHT-1 - r; c = BOARD_WIDTH-1 - c; }
\r
3015 squareSize, squareSize,
\r
3018 backTextureSquareInfo[r][c].mode,
\r
3019 backTextureSquareInfo[r][c].x,
\r
3020 backTextureSquareInfo[r][c].y );
\r
3022 SelectObject( texture_hdc, hbm );
\r
3024 if (piece != EmptySquare) {
\r
3025 DrawPieceOnDC(hdc, piece, piece_color, -1, x, y, tmphdc);
\r
3029 HBRUSH brush = square_color ? lightSquareBrush : darkSquareBrush;
\r
3031 oldBrush = SelectObject(hdc, brush );
\r
3032 BitBlt(hdc, x, y, squareSize, squareSize, 0, 0, 0, PATCOPY);
\r
3033 SelectObject(hdc, oldBrush);
\r
3034 if (piece != EmptySquare)
\r
3035 DrawPieceOnDC(hdc, piece, piece_color, -1, x, y, tmphdc);
\r
3040 if( texture_hdc != NULL ) {
\r
3041 DeleteDC( texture_hdc );
\r
3045 int saveDiagFlag = 0; FILE *diagFile; // [HGM] diag
\r
3046 void fputDW(FILE *f, int x)
\r
3048 fputc(x & 255, f);
\r
3049 fputc(x>>8 & 255, f);
\r
3050 fputc(x>>16 & 255, f);
\r
3051 fputc(x>>24 & 255, f);
\r
3054 #define MAX_CLIPS 200 /* more than enough */
\r
3057 DrawLogoOnDC(HDC hdc, RECT logoRect, HBITMAP logo)
\r
3059 // HBITMAP bufferBitmap;
\r
3064 int w = 100, h = 50;
\r
3066 if(logo == NULL) return;
\r
3067 // GetClientRect(hwndMain, &Rect);
\r
3068 // bufferBitmap = CreateCompatibleBitmap(hdc, Rect.right-Rect.left+1,
\r
3069 // Rect.bottom-Rect.top+1);
\r
3070 tmphdc = CreateCompatibleDC(hdc);
\r
3071 hbm = SelectObject(tmphdc, logo);
\r
3072 if( GetObject( logo, sizeof(bi), &bi ) > 0 ) {
\r
3076 StretchBlt(hdc, logoRect.left, logoRect.top, logoRect.right - logoRect.left,
\r
3077 logoRect.bottom - logoRect.top, tmphdc, 0, 0, w, h, SRCCOPY);
\r
3078 SelectObject(tmphdc, hbm);
\r
3083 HDCDrawPosition(HDC hdc, BOOLEAN repaint, Board board)
\r
3085 static Board lastReq, lastDrawn;
\r
3086 static HighlightInfo lastDrawnHighlight, lastDrawnPremove;
\r
3087 static int lastDrawnFlipView = 0;
\r
3088 static int lastReqValid = 0, lastDrawnValid = 0;
\r
3089 int releaseDC, x, y, x2, y2, row, column, num_clips = 0, i;
\r
3092 HBITMAP bufferBitmap;
\r
3093 HBITMAP oldBitmap;
\r
3095 HRGN clips[MAX_CLIPS];
\r
3096 ChessSquare dragged_piece = EmptySquare;
\r
3098 /* I'm undecided on this - this function figures out whether a full
\r
3099 * repaint is necessary on its own, so there's no real reason to have the
\r
3100 * caller tell it that. I think this can safely be set to FALSE - but
\r
3101 * if we trust the callers not to request full repaints unnessesarily, then
\r
3102 * we could skip some clipping work. In other words, only request a full
\r
3103 * redraw when the majority of pieces have changed positions (ie. flip,
\r
3104 * gamestart and similar) --Hawk
\r
3106 Boolean fullrepaint = repaint;
\r
3108 if( DrawPositionNeedsFullRepaint() ) {
\r
3109 fullrepaint = TRUE;
\r
3112 if (board == NULL) {
\r
3113 if (!lastReqValid) {
\r
3118 CopyBoard(lastReq, board);
\r
3122 if (doingSizing) {
\r
3126 if (IsIconic(hwndMain)) {
\r
3130 if (hdc == NULL) {
\r
3131 hdc = GetDC(hwndMain);
\r
3132 if (!appData.monoMode) {
\r
3133 SelectPalette(hdc, hPal, FALSE);
\r
3134 RealizePalette(hdc);
\r
3138 releaseDC = FALSE;
\r
3141 /* Create some work-DCs */
\r
3142 hdcmem = CreateCompatibleDC(hdc);
\r
3143 tmphdc = CreateCompatibleDC(hdc);
\r
3145 /* If dragging is in progress, we temporarely remove the piece */
\r
3146 /* [HGM] or temporarily decrease count if stacked */
\r
3147 /* !! Moved to before board compare !! */
\r
3148 if (dragInfo.from.x >= 0 && dragInfo.pos.x >= 0) {
\r
3149 dragged_piece = board[dragInfo.from.y][dragInfo.from.x];
\r
3150 if(dragInfo.from.x == BOARD_LEFT-2 ) {
\r
3151 if(--board[dragInfo.from.y][dragInfo.from.x+1] == 0 )
\r
3152 board[dragInfo.from.y][dragInfo.from.x] = EmptySquare;
\r
3154 if(dragInfo.from.x == BOARD_RGHT+1) {
\r
3155 if(--board[dragInfo.from.y][dragInfo.from.x-1] == 0 )
\r
3156 board[dragInfo.from.y][dragInfo.from.x] = EmptySquare;
\r
3158 board[dragInfo.from.y][dragInfo.from.x] = EmptySquare;
\r
3161 /* Figure out which squares need updating by comparing the
\r
3162 * newest board with the last drawn board and checking if
\r
3163 * flipping has changed.
\r
3165 if (!fullrepaint && lastDrawnValid && lastDrawnFlipView == flipView) {
\r
3166 for (row = 0; row < BOARD_HEIGHT; row++) { /* [HGM] true size, not 8 */
\r
3167 for (column = 0; column < BOARD_WIDTH; column++) {
\r
3168 if (lastDrawn[row][column] != board[row][column]) {
\r
3169 SquareToPos(row, column, &x, &y);
\r
3170 clips[num_clips++] =
\r
3171 CreateRectRgn(x, y, x + squareSize, y + squareSize);
\r
3175 for (i=0; i<2; i++) {
\r
3176 if (lastDrawnHighlight.sq[i].x != highlightInfo.sq[i].x ||
\r
3177 lastDrawnHighlight.sq[i].y != highlightInfo.sq[i].y) {
\r
3178 if (lastDrawnHighlight.sq[i].x >= 0 &&
\r
3179 lastDrawnHighlight.sq[i].y >= 0) {
\r
3180 SquareToPos(lastDrawnHighlight.sq[i].y,
\r
3181 lastDrawnHighlight.sq[i].x, &x, &y);
\r
3182 clips[num_clips++] =
\r
3183 CreateRectRgn(x - lineGap, y - lineGap,
\r
3184 x + squareSize + lineGap, y + squareSize + lineGap);
\r
3186 if (highlightInfo.sq[i].x >= 0 && highlightInfo.sq[i].y >= 0) {
\r
3187 SquareToPos(highlightInfo.sq[i].y, highlightInfo.sq[i].x, &x, &y);
\r
3188 clips[num_clips++] =
\r
3189 CreateRectRgn(x - lineGap, y - lineGap,
\r
3190 x + squareSize + lineGap, y + squareSize + lineGap);
\r
3194 for (i=0; i<2; i++) {
\r
3195 if (lastDrawnPremove.sq[i].x != premoveHighlightInfo.sq[i].x ||
\r
3196 lastDrawnPremove.sq[i].y != premoveHighlightInfo.sq[i].y) {
\r
3197 if (lastDrawnPremove.sq[i].x >= 0 &&
\r
3198 lastDrawnPremove.sq[i].y >= 0) {
\r
3199 SquareToPos(lastDrawnPremove.sq[i].y,
\r
3200 lastDrawnPremove.sq[i].x, &x, &y);
\r
3201 clips[num_clips++] =
\r
3202 CreateRectRgn(x - lineGap, y - lineGap,
\r
3203 x + squareSize + lineGap, y + squareSize + lineGap);
\r
3205 if (premoveHighlightInfo.sq[i].x >= 0 &&
\r
3206 premoveHighlightInfo.sq[i].y >= 0) {
\r
3207 SquareToPos(premoveHighlightInfo.sq[i].y,
\r
3208 premoveHighlightInfo.sq[i].x, &x, &y);
\r
3209 clips[num_clips++] =
\r
3210 CreateRectRgn(x - lineGap, y - lineGap,
\r
3211 x + squareSize + lineGap, y + squareSize + lineGap);
\r
3216 fullrepaint = TRUE;
\r
3219 /* Create a buffer bitmap - this is the actual bitmap
\r
3220 * being written to. When all the work is done, we can
\r
3221 * copy it to the real DC (the screen). This avoids
\r
3222 * the problems with flickering.
\r
3224 GetClientRect(hwndMain, &Rect);
\r
3225 bufferBitmap = CreateCompatibleBitmap(hdc, Rect.right-Rect.left+1,
\r
3226 Rect.bottom-Rect.top+1);
\r
3227 oldBitmap = SelectObject(hdcmem, bufferBitmap);
\r
3228 if (!appData.monoMode) {
\r
3229 SelectPalette(hdcmem, hPal, FALSE);
\r
3232 /* Create clips for dragging */
\r
3233 if (!fullrepaint) {
\r
3234 if (dragInfo.from.x >= 0) {
\r
3235 SquareToPos(dragInfo.from.y, dragInfo.from.x, &x, &y);
\r
3236 clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);
\r
3238 if (dragInfo.start.x >= 0) {
\r
3239 SquareToPos(dragInfo.start.y, dragInfo.start.x, &x, &y);
\r
3240 clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);
\r
3242 if (dragInfo.pos.x >= 0) {
\r
3243 x = dragInfo.pos.x - squareSize / 2;
\r
3244 y = dragInfo.pos.y - squareSize / 2;
\r
3245 clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);
\r
3247 if (dragInfo.lastpos.x >= 0) {
\r
3248 x = dragInfo.lastpos.x - squareSize / 2;
\r
3249 y = dragInfo.lastpos.y - squareSize / 2;
\r
3250 clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);
\r
3254 /* Are we animating a move?
\r
3256 * - remove the piece from the board (temporarely)
\r
3257 * - calculate the clipping region
\r
3259 if (!fullrepaint) {
\r
3260 if (animInfo.piece != EmptySquare) {
\r
3261 board[animInfo.from.y][animInfo.from.x] = EmptySquare;
\r
3262 x = boardRect.left + animInfo.lastpos.x;
\r
3263 y = boardRect.top + animInfo.lastpos.y;
\r
3264 x2 = boardRect.left + animInfo.pos.x;
\r
3265 y2 = boardRect.top + animInfo.pos.y;
\r
3266 clips[num_clips++] = CreateRectRgn(MIN(x,x2), MIN(y,y2), MAX(x,x2)+squareSize, MAX(y,y2)+squareSize);
\r
3267 /* Slight kludge. The real problem is that after AnimateMove is
\r
3268 done, the position on the screen does not match lastDrawn.
\r
3269 This currently causes trouble only on e.p. captures in
\r
3270 atomic, where the piece moves to an empty square and then
\r
3271 explodes. The old and new positions both had an empty square
\r
3272 at the destination, but animation has drawn a piece there and
\r
3273 we have to remember to erase it. [HGM] moved until after setting lastDrawn */
\r
3274 lastDrawn[animInfo.to.y][animInfo.to.x] = animInfo.piece;
\r
3278 /* No clips? Make sure we have fullrepaint set to TRUE */
\r
3279 if (num_clips == 0)
\r
3280 fullrepaint = TRUE;
\r
3282 /* Set clipping on the memory DC */
\r
3283 if (!fullrepaint) {
\r
3284 SelectClipRgn(hdcmem, clips[0]);
\r
3285 for (x = 1; x < num_clips; x++) {
\r
3286 if (ExtSelectClipRgn(hdcmem, clips[x], RGN_OR) == ERROR)
\r
3287 abort(); // this should never ever happen!
\r
3291 /* Do all the drawing to the memory DC */
\r
3292 if(explodeInfo.radius) { // [HGM] atomic
\r
3294 int x, y, r=(explodeInfo.radius * squareSize)/100;
\r
3295 board[explodeInfo.fromY][explodeInfo.fromX] = EmptySquare; // suppress display of capturer
\r
3296 SquareToPos(explodeInfo.toY, explodeInfo.toX, &x, &y);
\r
3297 x += squareSize/2;
\r
3298 y += squareSize/2;
\r
3299 if(!fullrepaint) {
\r
3300 clips[num_clips] = CreateRectRgn(x-r, y-r, x+r, y+r);
\r
3301 ExtSelectClipRgn(hdcmem, clips[num_clips++], RGN_OR);
\r
3303 DrawGridOnDC(hdcmem);
\r
3304 DrawHighlightsOnDC(hdcmem);
\r
3305 DrawBoardOnDC(hdcmem, board, tmphdc);
\r
3306 oldBrush = SelectObject(hdcmem, explodeBrush);
\r
3307 Ellipse(hdcmem, x-r, y-r, x+r, y+r);
\r
3308 SelectObject(hdcmem, oldBrush);
\r
3310 DrawGridOnDC(hdcmem);
\r
3311 DrawHighlightsOnDC(hdcmem);
\r
3312 DrawBoardOnDC(hdcmem, board, tmphdc);
\r
3315 HBITMAP whiteLogo = (HBITMAP) first.programLogo, blackLogo = (HBITMAP) second.programLogo;
\r
3316 if(appData.autoLogo) {
\r
3318 switch(gameMode) { // pick logos based on game mode
\r
3319 case IcsObserving:
\r
3320 whiteLogo = second.programLogo; // ICS logo
\r
3321 blackLogo = second.programLogo;
\r
3324 case IcsPlayingWhite:
\r
3325 if(!appData.zippyPlay) whiteLogo = userLogo;
\r
3326 blackLogo = second.programLogo; // ICS logo
\r
3328 case IcsPlayingBlack:
\r
3329 whiteLogo = second.programLogo; // ICS logo
\r
3330 blackLogo = appData.zippyPlay ? first.programLogo : userLogo;
\r
3332 case TwoMachinesPlay:
\r
3333 if(first.twoMachinesColor[0] == 'b') {
\r
3334 whiteLogo = second.programLogo;
\r
3335 blackLogo = first.programLogo;
\r
3338 case MachinePlaysWhite:
\r
3339 blackLogo = userLogo;
\r
3341 case MachinePlaysBlack:
\r
3342 whiteLogo = userLogo;
\r
3343 blackLogo = first.programLogo;
\r
3346 DrawLogoOnDC(hdc, leftLogoRect, flipClock ? blackLogo : whiteLogo);
\r
3347 DrawLogoOnDC(hdc, rightLogoRect, flipClock ? whiteLogo : blackLogo);
\r
3350 if( appData.highlightMoveWithArrow ) {
\r
3351 DrawArrowHighlight(hdcmem);
\r
3354 DrawCoordsOnDC(hdcmem);
\r
3356 CopyBoard(lastDrawn, board); /* [HGM] Moved to here from end of routine, */
\r
3357 /* to make sure lastDrawn contains what is actually drawn */
\r
3359 /* Put the dragged piece back into place and draw it (out of place!) */
\r
3360 if (dragged_piece != EmptySquare) {
\r
3361 /* [HGM] or restack */
\r
3362 if(dragInfo.from.x == BOARD_LEFT-2 )
\r
3363 board[dragInfo.from.y][dragInfo.from.x+1]++;
\r
3365 if(dragInfo.from.x == BOARD_RGHT+1 )
\r
3366 board[dragInfo.from.y][dragInfo.from.x-1]++;
\r
3367 board[dragInfo.from.y][dragInfo.from.x] = dragged_piece;
\r
3368 x = dragInfo.pos.x - squareSize / 2;
\r
3369 y = dragInfo.pos.y - squareSize / 2;
\r
3370 DrawPieceOnDC(hdcmem, dragged_piece,
\r
3371 ((int) dragged_piece < (int) BlackPawn),
\r
3372 (dragInfo.from.y + dragInfo.from.x) % 2, x, y, tmphdc);
\r
3375 /* Put the animated piece back into place and draw it */
\r
3376 if (animInfo.piece != EmptySquare) {
\r
3377 board[animInfo.from.y][animInfo.from.x] = animInfo.piece;
\r
3378 x = boardRect.left + animInfo.pos.x;
\r
3379 y = boardRect.top + animInfo.pos.y;
\r
3380 DrawPieceOnDC(hdcmem, animInfo.piece,
\r
3381 ((int) animInfo.piece < (int) BlackPawn),
\r
3382 (animInfo.from.y + animInfo.from.x) % 2, x, y, tmphdc);
\r
3385 /* Release the bufferBitmap by selecting in the old bitmap
\r
3386 * and delete the memory DC
\r
3388 SelectObject(hdcmem, oldBitmap);
\r
3391 /* Set clipping on the target DC */
\r
3392 if (!fullrepaint) {
\r
3393 SelectClipRgn(hdc, clips[0]);
\r
3394 for (x = 1; x < num_clips; x++) {
\r
3395 if (ExtSelectClipRgn(hdc, clips[x], RGN_OR) == ERROR)
\r
3396 abort(); // this should never ever happen!
\r
3400 /* Copy the new bitmap onto the screen in one go.
\r
3401 * This way we avoid any flickering
\r
3403 oldBitmap = SelectObject(tmphdc, bufferBitmap);
\r
3404 BitBlt(hdc, boardRect.left, boardRect.top,
\r
3405 boardRect.right - boardRect.left,
\r
3406 boardRect.bottom - boardRect.top,
\r
3407 tmphdc, boardRect.left, boardRect.top, SRCCOPY);
\r
3408 if(saveDiagFlag) {
\r
3409 BITMAP b; int i, j=0, m, w, wb, fac=0; char pData[1000000];
\r
3410 BITMAPINFOHEADER bih; int color[16], nrColors=0;
\r
3412 GetObject(bufferBitmap, sizeof(b), &b);
\r
3413 if(b.bmWidthBytes*b.bmHeight <= 990000) {
\r
3414 bih.biSize = sizeof(BITMAPINFOHEADER);
\r
3415 bih.biWidth = b.bmWidth;
\r
3416 bih.biHeight = b.bmHeight;
\r
3418 bih.biBitCount = b.bmBitsPixel;
\r
3419 bih.biCompression = 0;
\r
3420 bih.biSizeImage = b.bmWidthBytes*b.bmHeight;
\r
3421 bih.biXPelsPerMeter = 0;
\r
3422 bih.biYPelsPerMeter = 0;
\r
3423 bih.biClrUsed = 0;
\r
3424 bih.biClrImportant = 0;
\r
3425 // fprintf(diagFile, "t=%d\nw=%d\nh=%d\nB=%d\nP=%d\nX=%d\n",
\r
3426 // b.bmType, b.bmWidth, b.bmHeight, b.bmWidthBytes, b.bmPlanes, b.bmBitsPixel);
\r
3427 GetDIBits(tmphdc,bufferBitmap,0,b.bmHeight,pData,(BITMAPINFO*)&bih,DIB_RGB_COLORS);
\r
3428 // fprintf(diagFile, "%8x\n", (int) pData);
\r
3430 wb = b.bmWidthBytes;
\r
3432 for(i=0; i<wb*(b.bmHeight - boardRect.top + OUTER_MARGIN)>>2; i++) {
\r
3433 int k = ((int*) pData)[i];
\r
3434 for(j=0; j<nrColors; j++) if(color[j] == k) break;
\r
3435 if(j >= 16) break;
\r
3437 if(j >= nrColors) nrColors = j+1;
\r
3439 if(j<16) { // 16 colors is enough. Compress to 4 bits per pixel
\r
3441 for(i=0; i<b.bmHeight - boardRect.top + OUTER_MARGIN; i++) {
\r
3442 for(w=0; w<(wb>>2); w+=2) {
\r
3443 int k = ((int*) pData)[(wb*i>>2) + w];
\r
3444 for(j=0; j<nrColors; j++) if(color[j] == k) break;
\r
3445 k = ((int*) pData)[(wb*i>>2) + w + 1];
\r
3446 for(m=0; m<nrColors; m++) if(color[m] == k) break;
\r
3447 pData[p++] = m | j<<4;
\r
3449 while(p&3) pData[p++] = 0;
\r
3452 wb = ((wb+31)>>5)<<2;
\r
3454 // write BITMAPFILEHEADER
\r
3455 fprintf(diagFile, "BM");
\r
3456 fputDW(diagFile, wb*(b.bmHeight - boardRect.top + OUTER_MARGIN)+0x36 + (fac?64:0));
\r
3457 fputDW(diagFile, 0);
\r
3458 fputDW(diagFile, 0x36 + (fac?64:0));
\r
3459 // write BITMAPINFOHEADER
\r
3460 fputDW(diagFile, 40);
\r
3461 fputDW(diagFile, b.bmWidth);
\r
3462 fputDW(diagFile, b.bmHeight - boardRect.top + OUTER_MARGIN);
\r
3463 if(fac) fputDW(diagFile, 0x040001); // planes and bits/pixel
\r
3464 else fputDW(diagFile, 0x200001); // planes and bits/pixel
\r
3465 fputDW(diagFile, 0);
\r
3466 fputDW(diagFile, 0);
\r
3467 fputDW(diagFile, 0);
\r
3468 fputDW(diagFile, 0);
\r
3469 fputDW(diagFile, 0);
\r
3470 fputDW(diagFile, 0);
\r
3471 // write color table
\r
3473 for(i=0; i<16; i++) fputDW(diagFile, color[i]);
\r
3474 // write bitmap data
\r
3475 for(i=0; i<wb*(b.bmHeight - boardRect.top + OUTER_MARGIN); i++)
\r
3476 fputc(pData[i], diagFile);
\r
3480 SelectObject(tmphdc, oldBitmap);
\r
3482 /* Massive cleanup */
\r
3483 for (x = 0; x < num_clips; x++)
\r
3484 DeleteObject(clips[x]);
\r
3487 DeleteObject(bufferBitmap);
\r
3490 ReleaseDC(hwndMain, hdc);
\r
3492 if (lastDrawnFlipView != flipView) {
\r
3494 CheckMenuItem(GetMenu(hwndMain),IDM_FlipView, MF_BYCOMMAND|MF_CHECKED);
\r
3496 CheckMenuItem(GetMenu(hwndMain),IDM_FlipView, MF_BYCOMMAND|MF_UNCHECKED);
\r
3499 /* CopyBoard(lastDrawn, board);*/
\r
3500 lastDrawnHighlight = highlightInfo;
\r
3501 lastDrawnPremove = premoveHighlightInfo;
\r
3502 lastDrawnFlipView = flipView;
\r
3503 lastDrawnValid = 1;
\r
3506 /* [HGM] diag: Save the current board display to the given open file and close the file */
\r
3511 saveDiagFlag = 1; diagFile = f;
\r
3512 HDCDrawPosition(NULL, TRUE, NULL);
\r
3516 // if(f != NULL) fprintf(f, "Sorry, but this feature is still in preparation\n");
\r
3523 /*---------------------------------------------------------------------------*\
\r
3524 | CLIENT PAINT PROCEDURE
\r
3525 | This is the main event-handler for the WM_PAINT message.
\r
3527 \*---------------------------------------------------------------------------*/
\r
3529 PaintProc(HWND hwnd)
\r
3535 if((hdc = BeginPaint(hwnd, &ps))) {
\r
3536 if (IsIconic(hwnd)) {
\r
3537 DrawIcon(hdc, 2, 2, iconCurrent);
\r
3539 if (!appData.monoMode) {
\r
3540 SelectPalette(hdc, hPal, FALSE);
\r
3541 RealizePalette(hdc);
\r
3543 HDCDrawPosition(hdc, 1, NULL);
\r
3545 SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);
\r
3546 ExtTextOut(hdc, messageRect.left, messageRect.top,
\r
3547 ETO_CLIPPED|ETO_OPAQUE,
\r
3548 &messageRect, messageText, strlen(messageText), NULL);
\r
3549 SelectObject(hdc, oldFont);
\r
3550 DisplayBothClocks();
\r
3552 EndPaint(hwnd,&ps);
\r
3560 * If the user selects on a border boundary, return -1; if off the board,
\r
3561 * return -2. Otherwise map the event coordinate to the square.
\r
3562 * The offset boardRect.left or boardRect.top must already have been
\r
3563 * subtracted from x.
\r
3565 int EventToSquare(x, limit)
\r
3573 if ((x % (squareSize + lineGap)) >= squareSize)
\r
3575 x /= (squareSize + lineGap);
\r
3587 DropEnable dropEnables[] = {
\r
3588 { 'P', DP_Pawn, "Pawn" },
\r
3589 { 'N', DP_Knight, "Knight" },
\r
3590 { 'B', DP_Bishop, "Bishop" },
\r
3591 { 'R', DP_Rook, "Rook" },
\r
3592 { 'Q', DP_Queen, "Queen" },
\r
3596 SetupDropMenu(HMENU hmenu)
\r
3598 int i, count, enable;
\r
3600 extern char white_holding[], black_holding[];
\r
3601 char item[MSG_SIZ];
\r
3603 for (i=0; i<sizeof(dropEnables)/sizeof(DropEnable); i++) {
\r
3604 p = strchr(gameMode == IcsPlayingWhite ? white_holding : black_holding,
\r
3605 dropEnables[i].piece);
\r
3607 while (p && *p++ == dropEnables[i].piece) count++;
\r
3608 sprintf(item, "%s %d", dropEnables[i].name, count);
\r
3609 enable = count > 0 || !appData.testLegality
\r
3610 /*!!temp:*/ || (gameInfo.variant == VariantCrazyhouse
\r
3611 && !appData.icsActive);
\r
3612 ModifyMenu(hmenu, dropEnables[i].command,
\r
3613 MF_BYCOMMAND | (enable ? MF_ENABLED : MF_GRAYED) | MF_STRING,
\r
3614 dropEnables[i].command, item);
\r
3618 void DragPieceBegin(int x, int y)
\r
3620 dragInfo.lastpos.x = boardRect.left + x;
\r
3621 dragInfo.lastpos.y = boardRect.top + y;
\r
3622 dragInfo.from.x = fromX;
\r
3623 dragInfo.from.y = fromY;
\r
3624 dragInfo.start = dragInfo.from;
\r
3625 SetCapture(hwndMain);
\r
3628 void DragPieceEnd(int x, int y)
\r
3631 dragInfo.start.x = dragInfo.start.y = -1;
\r
3632 dragInfo.from = dragInfo.start;
\r
3633 dragInfo.pos = dragInfo.lastpos = dragInfo.start;
\r
3636 /* Event handler for mouse messages */
\r
3638 MouseEvent(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
\r
3642 static int recursive = 0;
\r
3644 BOOLEAN forceFullRepaint = IsFullRepaintPreferrable(); /* [AS] */
\r
3647 if (message == WM_MBUTTONUP) {
\r
3648 /* Hideous kludge to fool TrackPopupMenu into paying attention
\r
3649 to the middle button: we simulate pressing the left button too!
\r
3651 PostMessage(hwnd, WM_LBUTTONDOWN, wParam, lParam);
\r
3652 PostMessage(hwnd, WM_LBUTTONUP, wParam, lParam);
\r
3658 pt.x = LOWORD(lParam);
\r
3659 pt.y = HIWORD(lParam);
\r
3660 x = EventToSquare(pt.x - boardRect.left, BOARD_WIDTH);
\r
3661 y = EventToSquare(pt.y - boardRect.top, BOARD_HEIGHT);
\r
3662 if (!flipView && y >= 0) {
\r
3663 y = BOARD_HEIGHT - 1 - y;
\r
3665 if (flipView && x >= 0) {
\r
3666 x = BOARD_WIDTH - 1 - x;
\r
3669 switch (message) {
\r
3670 case WM_LBUTTONDOWN:
\r
3671 if (PtInRect((LPRECT) &whiteRect, pt)) {
\r
3672 if (gameMode == EditPosition) {
\r
3673 SetWhiteToPlayEvent();
\r
3674 } else if (gameMode == IcsPlayingBlack ||
\r
3675 gameMode == MachinePlaysWhite) {
\r
3677 } else if (gameMode == EditGame) {
\r
3678 AdjustClock(flipClock, -1);
\r
3680 } else if (PtInRect((LPRECT) &blackRect, pt)) {
\r
3681 if (gameMode == EditPosition) {
\r
3682 SetBlackToPlayEvent();
\r
3683 } else if (gameMode == IcsPlayingWhite ||
\r
3684 gameMode == MachinePlaysBlack) {
\r
3686 } else if (gameMode == EditGame) {
\r
3687 AdjustClock(!flipClock, -1);
\r
3690 dragInfo.start.x = dragInfo.start.y = -1;
\r
3691 dragInfo.from = dragInfo.start;
\r
3692 if(fromX == -1 && frozen) { // not sure where this is for
\r
3693 fromX = fromY = -1;
\r
3694 DrawPosition(forceFullRepaint || FALSE, NULL); /* [AS] */
\r
3697 LeftClick(Press, pt.x - boardRect.left, pt.y - boardRect.top);
\r
3698 DrawPosition(TRUE, NULL);
\r
3701 case WM_LBUTTONUP:
\r
3702 LeftClick(Release, pt.x - boardRect.left, pt.y - boardRect.top);
\r
3703 DrawPosition(TRUE, NULL