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, 2010 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((char *s));
\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
137 static HighlightInfo partnerHighlightInfo = { {{-1, -1}, {-1, -1}} };
\r
138 static HighlightInfo oldPartnerHighlight = { {{-1, -1}, {-1, -1}} };
\r
140 typedef struct { // [HGM] atomic
\r
141 int fromX, fromY, toX, toY, radius;
\r
144 static ExplodeInfo explodeInfo;
\r
146 /* Window class names */
\r
147 char szAppName[] = "WinBoard";
\r
148 char szConsoleName[] = "WBConsole";
\r
150 /* Title bar text */
\r
151 char szTitle[] = "WinBoard";
\r
152 char szConsoleTitle[] = "I C S Interaction";
\r
155 char *settingsFileName;
\r
156 Boolean saveSettingsOnExit;
\r
157 char installDir[MSG_SIZ];
\r
158 int errorExitStatus;
\r
160 BoardSize boardSize;
\r
161 Boolean chessProgram;
\r
162 //static int boardX, boardY;
\r
163 int minX, minY; // [HGM] placement: volatile limits on upper-left corner
\r
164 int squareSize, lineGap, minorSize;
\r
165 static int winW, winH;
\r
166 static RECT messageRect, whiteRect, blackRect, leftLogoRect, rightLogoRect; // [HGM] logo
\r
167 static int logoHeight = 0;
\r
168 static char messageText[MESSAGE_TEXT_MAX];
\r
169 static int clockTimerEvent = 0;
\r
170 static int loadGameTimerEvent = 0;
\r
171 static int analysisTimerEvent = 0;
\r
172 static DelayedEventCallback delayedTimerCallback;
\r
173 static int delayedTimerEvent = 0;
\r
174 static int buttonCount = 2;
\r
175 char *icsTextMenuString;
\r
177 char *firstChessProgramNames;
\r
178 char *secondChessProgramNames;
\r
180 #define PALETTESIZE 256
\r
182 HINSTANCE hInst; /* current instance */
\r
183 Boolean alwaysOnTop = FALSE;
\r
185 COLORREF lightSquareColor, darkSquareColor, whitePieceColor,
\r
186 blackPieceColor, highlightSquareColor, premoveHighlightColor;
\r
188 ColorClass currentColorClass;
\r
190 HWND hCommPort = NULL; /* currently open comm port */
\r
191 static HWND hwndPause; /* pause button */
\r
192 static HBITMAP pieceBitmap[3][(int) BlackPawn]; /* [HGM] nr of bitmaps referred to bP in stead of wK */
\r
193 static HBRUSH lightSquareBrush, darkSquareBrush,
\r
194 blackSquareBrush, /* [HGM] for band between board and holdings */
\r
195 explodeBrush, /* [HGM] atomic */
\r
196 markerBrush, /* [HGM] markers */
\r
197 whitePieceBrush, blackPieceBrush, iconBkgndBrush /*, outlineBrush*/;
\r
198 static POINT gridEndpoints[(BOARD_RANKS + BOARD_FILES + 2) * 2];
\r
199 static DWORD gridVertexCounts[BOARD_RANKS + BOARD_FILES + 2];
\r
200 static HPEN gridPen = NULL;
\r
201 static HPEN highlightPen = NULL;
\r
202 static HPEN premovePen = NULL;
\r
203 static NPLOGPALETTE pLogPal;
\r
204 static BOOL paletteChanged = FALSE;
\r
205 static HICON iconWhite, iconBlack, iconCurrent;
\r
206 static int doingSizing = FALSE;
\r
207 static int lastSizing = 0;
\r
208 static int prevStderrPort;
\r
209 static HBITMAP userLogo;
\r
211 static HBITMAP liteBackTexture = NULL;
\r
212 static HBITMAP darkBackTexture = NULL;
\r
213 static int liteBackTextureMode = BACK_TEXTURE_MODE_PLAIN;
\r
214 static int darkBackTextureMode = BACK_TEXTURE_MODE_PLAIN;
\r
215 static int backTextureSquareSize = 0;
\r
216 static struct { int x; int y; int mode; } backTextureSquareInfo[BOARD_RANKS][BOARD_FILES];
\r
218 #if __GNUC__ && !defined(_winmajor)
\r
219 #define oldDialog 0 /* cygwin doesn't define _winmajor; mingw does */
\r
221 #if defined(_winmajor)
\r
222 #define oldDialog (_winmajor < 4)
\r
224 #define oldDialog 0
\r
234 int cliWidth, cliHeight;
\r
237 SizeInfo sizeInfo[] =
\r
239 { "tiny", 21, 0, 1, 1, 0, 0 },
\r
240 { "teeny", 25, 1, 1, 1, 0, 0 },
\r
241 { "dinky", 29, 1, 1, 1, 0, 0 },
\r
242 { "petite", 33, 1, 1, 1, 0, 0 },
\r
243 { "slim", 37, 2, 1, 0, 0, 0 },
\r
244 { "small", 40, 2, 1, 0, 0, 0 },
\r
245 { "mediocre", 45, 2, 1, 0, 0, 0 },
\r
246 { "middling", 49, 2, 0, 0, 0, 0 },
\r
247 { "average", 54, 2, 0, 0, 0, 0 },
\r
248 { "moderate", 58, 3, 0, 0, 0, 0 },
\r
249 { "medium", 64, 3, 0, 0, 0, 0 },
\r
250 { "bulky", 72, 3, 0, 0, 0, 0 },
\r
251 { "large", 80, 3, 0, 0, 0, 0 },
\r
252 { "big", 87, 3, 0, 0, 0, 0 },
\r
253 { "huge", 95, 3, 0, 0, 0, 0 },
\r
254 { "giant", 108, 3, 0, 0, 0, 0 },
\r
255 { "colossal", 116, 4, 0, 0, 0, 0 },
\r
256 { "titanic", 129, 4, 0, 0, 0, 0 },
\r
257 { NULL, 0, 0, 0, 0, 0, 0 }
\r
260 #define MF(x) {x, {{0,}, 0. }, {0, }, 0}
\r
261 MyFont fontRec[NUM_SIZES][NUM_FONTS] =
\r
263 { 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
264 { 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
265 { 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
266 { 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
267 { 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
268 { 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
269 { 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
270 { 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
271 { 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
272 { 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
273 { 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
274 { 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
275 { 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
276 { 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
277 { 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
278 { 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
279 { 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
280 { 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
283 MyFont *font[NUM_SIZES][NUM_FONTS];
\r
292 #define BUTTON_WIDTH (tinyLayout ? 16 : 32)
\r
293 #define N_BUTTONS 5
\r
295 MyButtonDesc buttonDesc[N_BUTTONS] =
\r
297 {"<<", IDM_ToStart, NULL, NULL},
\r
298 {"<", IDM_Backward, NULL, NULL},
\r
299 {"P", IDM_Pause, NULL, NULL},
\r
300 {">", IDM_Forward, NULL, NULL},
\r
301 {">>", IDM_ToEnd, NULL, NULL},
\r
304 int tinyLayout = 0, smallLayout = 0;
\r
305 #define MENU_BAR_ITEMS 7
\r
306 char *menuBarText[2][MENU_BAR_ITEMS+1] = {
\r
307 { "&File", "&Mode", "&Action", "&Step", "&Options", "&Help", NULL },
\r
308 { "&F", "&M", "&A", "&S", "&O", "&H", NULL },
\r
312 MySound sounds[(int)NSoundClasses];
\r
313 MyTextAttribs textAttribs[(int)NColorClasses];
\r
315 MyColorizeAttribs colorizeAttribs[] = {
\r
316 { (COLORREF)0, 0, "Shout Text" },
\r
317 { (COLORREF)0, 0, "SShout/CShout" },
\r
318 { (COLORREF)0, 0, "Channel 1 Text" },
\r
319 { (COLORREF)0, 0, "Channel Text" },
\r
320 { (COLORREF)0, 0, "Kibitz Text" },
\r
321 { (COLORREF)0, 0, "Tell Text" },
\r
322 { (COLORREF)0, 0, "Challenge Text" },
\r
323 { (COLORREF)0, 0, "Request Text" },
\r
324 { (COLORREF)0, 0, "Seek Text" },
\r
325 { (COLORREF)0, 0, "Normal Text" },
\r
326 { (COLORREF)0, 0, "None" }
\r
331 static char *commentTitle;
\r
332 static char *commentText;
\r
333 static int commentIndex;
\r
334 static Boolean editComment = FALSE;
\r
337 char errorTitle[MSG_SIZ];
\r
338 char errorMessage[2*MSG_SIZ];
\r
339 HWND errorDialog = NULL;
\r
340 BOOLEAN moveErrorMessageUp = FALSE;
\r
341 BOOLEAN consoleEcho = TRUE;
\r
342 CHARFORMAT consoleCF;
\r
343 COLORREF consoleBackgroundColor;
\r
345 char *programVersion;
\r
351 typedef int CPKind;
\r
360 SOCKET sock2; /* stderr socket for OpenRcmd */
\r
363 #define INPUT_SOURCE_BUF_SIZE 4096
\r
365 typedef struct _InputSource {
\r
372 char buf[INPUT_SOURCE_BUF_SIZE];
\r
376 InputCallback func;
\r
377 struct _InputSource *second; /* for stderr thread on CPRcmd */
\r
381 InputSource *consoleInputSource;
\r
386 VOID ConsoleOutput(char* data, int length, int forceVisible);
\r
387 VOID ConsoleCreate();
\r
389 ConsoleWndProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam);
\r
390 VOID ColorizeTextPopup(HWND hwnd, ColorClass cc);
\r
391 VOID PrintCommSettings(FILE *f, char *name, DCB *dcb);
\r
392 VOID ParseCommSettings(char *arg, DCB *dcb);
\r
394 StartupDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam);
\r
395 VOID APIENTRY MenuPopup(HWND hwnd, POINT pt, HMENU hmenu, UINT def);
\r
396 void ParseIcsTextMenu(char *icsTextMenuString);
\r
397 VOID PopUpMoveDialog(char firstchar);
\r
398 VOID PopUpNameDialog(char firstchar);
\r
399 VOID UpdateSampleText(HWND hDlg, int id, MyColorizeAttribs *mca);
\r
403 int GameListOptions();
\r
405 int dummy; // [HGM] for obsolete args
\r
407 HWND hwndMain = NULL; /* root window*/
\r
408 HWND hwndConsole = NULL;
\r
409 HWND commentDialog = NULL;
\r
410 HWND moveHistoryDialog = NULL;
\r
411 HWND evalGraphDialog = NULL;
\r
412 HWND engineOutputDialog = NULL;
\r
413 HWND gameListDialog = NULL;
\r
414 HWND editTagsDialog = NULL;
\r
416 int commentUp = FALSE;
\r
418 WindowPlacement wpMain;
\r
419 WindowPlacement wpConsole;
\r
420 WindowPlacement wpComment;
\r
421 WindowPlacement wpMoveHistory;
\r
422 WindowPlacement wpEvalGraph;
\r
423 WindowPlacement wpEngineOutput;
\r
424 WindowPlacement wpGameList;
\r
425 WindowPlacement wpTags;
\r
427 VOID EngineOptionsPopup(); // [HGM] settings
\r
429 VOID GothicPopUp(char *title, VariantClass variant);
\r
431 * Setting "frozen" should disable all user input other than deleting
\r
432 * the window. We do this while engines are initializing themselves.
\r
434 static int frozen = 0;
\r
435 static int oldMenuItemState[MENU_BAR_ITEMS];
\r
441 if (frozen) return;
\r
443 hmenu = GetMenu(hwndMain);
\r
444 for (i=0; i<MENU_BAR_ITEMS; i++) {
\r
445 oldMenuItemState[i] = EnableMenuItem(hmenu, i, MF_BYPOSITION|MF_GRAYED);
\r
447 DrawMenuBar(hwndMain);
\r
450 /* Undo a FreezeUI */
\r
456 if (!frozen) return;
\r
458 hmenu = GetMenu(hwndMain);
\r
459 for (i=0; i<MENU_BAR_ITEMS; i++) {
\r
460 EnableMenuItem(hmenu, i, MF_BYPOSITION|oldMenuItemState[i]);
\r
462 DrawMenuBar(hwndMain);
\r
465 /*static*/ int fromX = -1, fromY = -1, toX, toY; // [HGM] moved upstream, so JAWS can use them
\r
467 /* JAWS preparation patch (WinBoard for the sight impaired). Define required insertions as empty */
\r
473 #define JAWS_ALT_INTERCEPT
\r
474 #define JAWS_KB_NAVIGATION
\r
475 #define JAWS_MENU_ITEMS
\r
476 #define JAWS_SILENCE
\r
477 #define JAWS_REPLAY
\r
479 #define JAWS_COPYRIGHT
\r
480 #define JAWS_DELETE(X) X
\r
481 #define SAYMACHINEMOVE()
\r
485 /*---------------------------------------------------------------------------*\
\r
489 \*---------------------------------------------------------------------------*/
\r
492 WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
\r
493 LPSTR lpCmdLine, int nCmdShow)
\r
496 HANDLE hAccelMain, hAccelNoAlt, hAccelNoICS;
\r
497 // INITCOMMONCONTROLSEX ex;
\r
501 LoadLibrary("RICHED32.DLL");
\r
502 consoleCF.cbSize = sizeof(CHARFORMAT);
\r
504 if (!InitApplication(hInstance)) {
\r
507 if (!InitInstance(hInstance, nCmdShow, lpCmdLine)) {
\r
513 // InitCommonControlsEx(&ex);
\r
514 InitCommonControls();
\r
516 hAccelMain = LoadAccelerators (hInstance, szAppName);
\r
517 hAccelNoAlt = LoadAccelerators (hInstance, "NO_ALT");
\r
518 hAccelNoICS = LoadAccelerators( hInstance, "NO_ICS"); /* [AS] No Ctrl-V on ICS!!! */
\r
520 /* Acquire and dispatch messages until a WM_QUIT message is received. */
\r
522 while (GetMessage(&msg, /* message structure */
\r
523 NULL, /* handle of window receiving the message */
\r
524 0, /* lowest message to examine */
\r
525 0)) /* highest message to examine */
\r
528 if(msg.message == WM_CHAR && msg.wParam == '\t') {
\r
529 // [HGM] navigate: switch between all windows with tab
\r
530 HWND e1 = NULL, e2 = NULL, mh = NULL, hInput = NULL, hText = NULL;
\r
531 int i, currentElement = 0;
\r
533 // first determine what element of the chain we come from (if any)
\r
534 if(appData.icsActive) {
\r
535 hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
536 hText = GetDlgItem(hwndConsole, OPT_ConsoleText);
\r
538 if(engineOutputDialog && EngineOutputIsUp()) {
\r
539 e1 = GetDlgItem(engineOutputDialog, IDC_EngineMemo1);
\r
540 e2 = GetDlgItem(engineOutputDialog, IDC_EngineMemo2);
\r
542 if(moveHistoryDialog && MoveHistoryIsUp()) {
\r
543 mh = GetDlgItem(moveHistoryDialog, IDC_MoveHistory);
\r
545 if(msg.hwnd == hwndMain) currentElement = 7 ; else
\r
546 if(msg.hwnd == engineOutputDialog) currentElement = 2; else
\r
547 if(msg.hwnd == e1) currentElement = 2; else
\r
548 if(msg.hwnd == e2) currentElement = 3; else
\r
549 if(msg.hwnd == moveHistoryDialog) currentElement = 4; else
\r
550 if(msg.hwnd == mh) currentElement = 4; else
\r
551 if(msg.hwnd == evalGraphDialog) currentElement = 6; else
\r
552 if(msg.hwnd == hText) currentElement = 5; else
\r
553 if(msg.hwnd == hInput) currentElement = 6; else
\r
554 for (i = 0; i < N_BUTTONS; i++) {
\r
555 if (buttonDesc[i].hwnd == msg.hwnd) { currentElement = 1; break; }
\r
558 // determine where to go to
\r
559 if(currentElement) { HWND h = NULL; int direction = GetKeyState(VK_SHIFT) < 0 ? -1 : 1;
\r
561 currentElement = (currentElement + direction) % 7;
\r
562 switch(currentElement) {
\r
564 h = hwndMain; break; // passing this case always makes the loop exit
\r
566 h = buttonDesc[0].hwnd; break; // could be NULL
\r
568 if(!EngineOutputIsUp()) continue; // skip closed auxiliary windows
\r
571 if(!EngineOutputIsUp()) continue;
\r
574 if(!MoveHistoryIsUp()) continue;
\r
576 // case 6: // input to eval graph does not seem to get here!
\r
577 // if(!EvalGraphIsUp()) continue;
\r
578 // h = evalGraphDialog; break;
\r
580 if(!appData.icsActive) continue;
\r
584 if(!appData.icsActive) continue;
\r
590 if(currentElement > 4 && IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);
\r
591 if(currentElement < 5 && IsIconic(hwndMain)) ShowWindow(hwndMain, SW_RESTORE); // all open together
\r
594 continue; // this message now has been processed
\r
598 if (!(commentDialog && IsDialogMessage(commentDialog, &msg)) &&
\r
599 !(moveHistoryDialog && IsDialogMessage(moveHistoryDialog, &msg)) &&
\r
600 !(evalGraphDialog && IsDialogMessage(evalGraphDialog, &msg)) &&
\r
601 !(engineOutputDialog && IsDialogMessage(engineOutputDialog, &msg)) &&
\r
602 !(editTagsDialog && IsDialogMessage(editTagsDialog, &msg)) &&
\r
603 !(gameListDialog && IsDialogMessage(gameListDialog, &msg)) &&
\r
604 !(errorDialog && IsDialogMessage(errorDialog, &msg)) &&
\r
605 !(!frozen && TranslateAccelerator(hwndMain, hAccelMain, &msg)) && JAWS_ACCEL
\r
606 !(!hwndConsole && TranslateAccelerator(hwndMain, hAccelNoICS, &msg)) &&
\r
607 !(!hwndConsole && TranslateAccelerator(hwndMain, hAccelNoAlt, &msg))) {
\r
608 int done = 0, i; // [HGM] chat: dispatch cat-box messages
\r
609 for(i=0; i<MAX_CHAT; i++)
\r
610 if(chatHandle[i] && IsDialogMessage(chatHandle[i], &msg)) {
\r
613 if(done) continue; // [HGM] chat: end patch
\r
614 TranslateMessage(&msg); /* Translates virtual key codes */
\r
615 DispatchMessage(&msg); /* Dispatches message to window */
\r
620 return (msg.wParam); /* Returns the value from PostQuitMessage */
\r
623 /*---------------------------------------------------------------------------*\
\r
625 * Initialization functions
\r
627 \*---------------------------------------------------------------------------*/
\r
631 { // update user logo if necessary
\r
632 static char oldUserName[MSG_SIZ], *curName;
\r
634 if(appData.autoLogo) {
\r
635 curName = UserName();
\r
636 if(strcmp(curName, oldUserName)) {
\r
637 sprintf(oldUserName, "logos\\%s.bmp", curName);
\r
638 userLogo = LoadImage( 0, oldUserName, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );
\r
639 strcpy(oldUserName, curName);
\r
645 InitApplication(HINSTANCE hInstance)
\r
649 /* Fill in window class structure with parameters that describe the */
\r
652 wc.style = CS_HREDRAW | CS_VREDRAW; /* Class style(s). */
\r
653 wc.lpfnWndProc = (WNDPROC)WndProc; /* Window Procedure */
\r
654 wc.cbClsExtra = 0; /* No per-class extra data. */
\r
655 wc.cbWndExtra = 0; /* No per-window extra data. */
\r
656 wc.hInstance = hInstance; /* Owner of this class */
\r
657 wc.hIcon = LoadIcon(hInstance, "icon_white");
\r
658 wc.hCursor = LoadCursor(NULL, IDC_ARROW); /* Cursor */
\r
659 wc.hbrBackground = (HBRUSH)(COLOR_WINDOW+1); /* Default color */
\r
660 wc.lpszMenuName = szAppName; /* Menu name from .RC */
\r
661 wc.lpszClassName = szAppName; /* Name to register as */
\r
663 /* Register the window class and return success/failure code. */
\r
664 if (!RegisterClass(&wc)) return FALSE;
\r
666 wc.style = CS_HREDRAW | CS_VREDRAW;
\r
667 wc.lpfnWndProc = (WNDPROC)ConsoleWndProc;
\r
669 wc.cbWndExtra = DLGWINDOWEXTRA;
\r
670 wc.hInstance = hInstance;
\r
671 wc.hIcon = LoadIcon(hInstance, "icon_white");
\r
672 wc.hCursor = LoadCursor(NULL, IDC_ARROW);
\r
673 wc.hbrBackground = (HBRUSH)(COLOR_MENU+1);
\r
674 wc.lpszMenuName = NULL;
\r
675 wc.lpszClassName = szConsoleName;
\r
677 if (!RegisterClass(&wc)) return FALSE;
\r
682 /* Set by InitInstance, used by EnsureOnScreen */
\r
683 int screenHeight, screenWidth;
\r
686 EnsureOnScreen(int *x, int *y, int minX, int minY)
\r
688 // int gap = GetSystemMetrics(SM_CYFRAME) + GetSystemMetrics(SM_CYCAPTION);
\r
689 /* Be sure window at (x,y) is not off screen (or even mostly off screen) */
\r
690 if (*x > screenWidth - 32) *x = 0;
\r
691 if (*y > screenHeight - 32) *y = 0;
\r
692 if (*x < minX) *x = minX;
\r
693 if (*y < minY) *y = minY;
\r
697 InitInstance(HINSTANCE hInstance, int nCmdShow, LPSTR lpCmdLine)
\r
699 HWND hwnd; /* Main window handle. */
\r
701 WINDOWPLACEMENT wp;
\r
704 hInst = hInstance; /* Store instance handle in our global variable */
\r
705 programName = szAppName;
\r
707 if (SearchPath(NULL, "WinBoard.exe", NULL, MSG_SIZ, installDir, &filepart)) {
\r
708 *filepart = NULLCHAR;
\r
710 GetCurrentDirectory(MSG_SIZ, installDir);
\r
712 gameInfo.boardWidth = gameInfo.boardHeight = 8; // [HGM] won't have open window otherwise
\r
713 screenWidth = screenHeight = 1000; // [HGM] placement: kludge to allow calling EnsureOnScreen from InitAppData
\r
714 InitAppData(lpCmdLine); /* Get run-time parameters */
\r
715 /* xboard, and older WinBoards, controlled the move sound with the
\r
716 appData.ringBellAfterMoves option. In the current WinBoard, we
\r
717 always turn the option on (so that the backend will call us),
\r
718 then let the user turn the sound off by setting it to silence if
\r
719 desired. To accommodate old winboard.ini files saved by old
\r
720 versions of WinBoard, we also turn off the sound if the option
\r
721 was initially set to false. [HGM] taken out of InitAppData */
\r
722 if (!appData.ringBellAfterMoves) {
\r
723 sounds[(int)SoundMove].name = strdup("");
\r
724 appData.ringBellAfterMoves = TRUE;
\r
726 if (appData.debugMode) {
\r
727 debugFP = fopen(appData.nameOfDebugFile, "w");
\r
728 setbuf(debugFP, NULL);
\r
733 // InitEngineUCI( installDir, &first ); // [HGM] incorporated in InitBackEnd1()
\r
734 // InitEngineUCI( installDir, &second );
\r
736 /* Create a main window for this application instance. */
\r
737 hwnd = CreateWindow(szAppName, szTitle,
\r
738 (WS_OVERLAPPEDWINDOW & ~WS_MAXIMIZEBOX),
\r
739 CW_USEDEFAULT, 0, CW_USEDEFAULT, 0,
\r
740 NULL, NULL, hInstance, NULL);
\r
743 /* If window could not be created, return "failure" */
\r
748 /* [HGM] logo: Load logos if specified (must be done before InitDrawingSizes) */
\r
749 if( appData.firstLogo && appData.firstLogo[0] != NULLCHAR) {
\r
750 first.programLogo = LoadImage( 0, appData.firstLogo, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );
\r
752 if (first.programLogo == NULL && appData.debugMode) {
\r
753 fprintf( debugFP, "Unable to load logo bitmap '%s'\n", appData.firstLogo );
\r
755 } else if(appData.autoLogo) {
\r
756 if(appData.firstDirectory && appData.firstDirectory[0]) {
\r
758 sprintf(buf, "%s/logo.bmp", appData.firstDirectory);
\r
759 first.programLogo = LoadImage( 0, buf, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );
\r
763 if( appData.secondLogo && appData.secondLogo[0] != NULLCHAR) {
\r
764 second.programLogo = LoadImage( 0, appData.secondLogo, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );
\r
766 if (second.programLogo == NULL && appData.debugMode) {
\r
767 fprintf( debugFP, "Unable to load logo bitmap '%s'\n", appData.secondLogo );
\r
769 } else if(appData.autoLogo) {
\r
771 if(appData.icsActive) { // [HGM] logo: in ICS mode second can be used for ICS
\r
772 sprintf(buf, "logos\\%s.bmp", appData.icsHost);
\r
773 second.programLogo = LoadImage( 0, buf, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );
\r
775 if(appData.secondDirectory && appData.secondDirectory[0]) {
\r
776 sprintf(buf, "%s\\logo.bmp", appData.secondDirectory);
\r
777 second.programLogo = LoadImage( 0, buf, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );
\r
783 iconWhite = LoadIcon(hInstance, "icon_white");
\r
784 iconBlack = LoadIcon(hInstance, "icon_black");
\r
785 iconCurrent = iconWhite;
\r
786 InitDrawingColors();
\r
787 screenHeight = GetSystemMetrics(SM_CYSCREEN);
\r
788 screenWidth = GetSystemMetrics(SM_CXSCREEN);
\r
789 for (ibs = (int) NUM_SIZES - 1; ibs >= 0; ibs--) {
\r
790 /* Compute window size for each board size, and use the largest
\r
791 size that fits on this screen as the default. */
\r
792 InitDrawingSizes((BoardSize)(ibs+1000), 0);
\r
793 if (boardSize == (BoardSize)-1 &&
\r
794 winH <= screenHeight
\r
795 - GetSystemMetrics(SM_CYFRAME) - GetSystemMetrics(SM_CYCAPTION) - 10
\r
796 && winW <= screenWidth) {
\r
797 boardSize = (BoardSize)ibs;
\r
801 InitDrawingSizes(boardSize, 0);
\r
803 buttonCount = GetSystemMetrics(SM_CMOUSEBUTTONS);
\r
805 /* [AS] Load textures if specified */
\r
806 ZeroMemory( &backTextureSquareInfo, sizeof(backTextureSquareInfo) );
\r
808 if( appData.liteBackTextureFile && appData.liteBackTextureFile[0] != NULLCHAR && appData.liteBackTextureFile[0] != '*' ) {
\r
809 liteBackTexture = LoadImage( 0, appData.liteBackTextureFile, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );
\r
810 liteBackTextureMode = appData.liteBackTextureMode;
\r
812 if (liteBackTexture == NULL && appData.debugMode) {
\r
813 fprintf( debugFP, "Unable to load lite texture bitmap '%s'\n", appData.liteBackTextureFile );
\r
817 if( appData.darkBackTextureFile && appData.darkBackTextureFile[0] != NULLCHAR && appData.darkBackTextureFile[0] != '*' ) {
\r
818 darkBackTexture = LoadImage( 0, appData.darkBackTextureFile, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );
\r
819 darkBackTextureMode = appData.darkBackTextureMode;
\r
821 if (darkBackTexture == NULL && appData.debugMode) {
\r
822 fprintf( debugFP, "Unable to load dark texture bitmap '%s'\n", appData.darkBackTextureFile );
\r
826 mysrandom( (unsigned) time(NULL) );
\r
828 /* [AS] Restore layout */
\r
829 if( wpMoveHistory.visible ) {
\r
830 MoveHistoryPopUp();
\r
833 if( wpEvalGraph.visible ) {
\r
837 if( wpEngineOutput.visible ) {
\r
838 EngineOutputPopUp();
\r
841 /* Make the window visible; update its client area; and return "success" */
\r
842 EnsureOnScreen(&wpMain.x, &wpMain.y, minX, minY);
\r
843 wp.length = sizeof(WINDOWPLACEMENT);
\r
845 wp.showCmd = nCmdShow;
\r
846 wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;
\r
847 wp.rcNormalPosition.left = wpMain.x;
\r
848 wp.rcNormalPosition.right = wpMain.x + wpMain.width;
\r
849 wp.rcNormalPosition.top = wpMain.y;
\r
850 wp.rcNormalPosition.bottom = wpMain.y + wpMain.height;
\r
851 SetWindowPlacement(hwndMain, &wp);
\r
853 InitBackEnd2(); // [HGM] moved until after all windows placed, to save correct position if fatal error on engine start
\r
855 if(!appData.noGUI) SetWindowPos(hwndMain, alwaysOnTop ? HWND_TOPMOST : HWND_NOTOPMOST,
\r
856 0, 0, 0, 0, SWP_NOMOVE|SWP_NOSIZE);
\r
860 SetWindowPos(hwndConsole, alwaysOnTop ? HWND_TOPMOST : HWND_NOTOPMOST,
\r
861 0, 0, 0, 0, SWP_NOMOVE|SWP_NOSIZE);
\r
863 ShowWindow(hwndConsole, nCmdShow);
\r
864 if(appData.chatBoxes) { // [HGM] chat: open chat boxes
\r
865 char buf[MSG_SIZ], *p = buf, *q;
\r
866 strcpy(buf, appData.chatBoxes);
\r
868 q = strchr(p, ';');
\r
870 if(*p) ChatPopUp(p);
\r
873 SetActiveWindow(hwndConsole);
\r
875 if(!appData.noGUI) UpdateWindow(hwnd); else ShowWindow(hwnd, SW_MINIMIZE);
\r
876 if(gameListDialog) SetFocus(gameListDialog); // [HGM] jaws: for if we clicked multi-game game file
\r
885 HMENU hmenu = GetMenu(hwndMain);
\r
887 (void) EnableMenuItem(hmenu, IDM_CommPort,
\r
888 MF_BYCOMMAND|((appData.icsActive &&
\r
889 *appData.icsCommPort != NULLCHAR) ?
\r
890 MF_ENABLED : MF_GRAYED));
\r
891 (void) CheckMenuItem(hmenu, IDM_SaveSettingsOnExit,
\r
892 MF_BYCOMMAND|(saveSettingsOnExit ?
\r
893 MF_CHECKED : MF_UNCHECKED));
\r
896 //---------------------------------------------------------------------------------------------------------
\r
898 #define ICS_TEXT_MENU_SIZE (IDM_CommandXLast - IDM_CommandX + 1)
\r
899 #define XBOARD FALSE
\r
901 #define OPTCHAR "/"
\r
902 #define SEPCHAR "="
\r
906 // front-end part of option handling
\r
909 LFfromMFP(LOGFONT* lf, MyFontParams *mfp)
\r
911 HDC hdc = CreateDC("DISPLAY", NULL, NULL, NULL);
\r
912 lf->lfHeight = -(int)(mfp->pointSize * GetDeviceCaps(hdc, LOGPIXELSY) / 72.0 + 0.5);
\r
915 lf->lfEscapement = 0;
\r
916 lf->lfOrientation = 0;
\r
917 lf->lfWeight = mfp->bold ? FW_BOLD : FW_NORMAL;
\r
918 lf->lfItalic = mfp->italic;
\r
919 lf->lfUnderline = mfp->underline;
\r
920 lf->lfStrikeOut = mfp->strikeout;
\r
921 lf->lfCharSet = mfp->charset;
\r
922 lf->lfOutPrecision = OUT_DEFAULT_PRECIS;
\r
923 lf->lfClipPrecision = CLIP_DEFAULT_PRECIS;
\r
924 lf->lfQuality = DEFAULT_QUALITY;
\r
925 lf->lfPitchAndFamily = DEFAULT_PITCH|FF_DONTCARE;
\r
926 strcpy(lf->lfFaceName, mfp->faceName);
\r
930 CreateFontInMF(MyFont *mf)
\r
932 LFfromMFP(&mf->lf, &mf->mfp);
\r
933 if (mf->hf) DeleteObject(mf->hf);
\r
934 mf->hf = CreateFontIndirect(&mf->lf);
\r
937 // [HGM] This platform-dependent table provides the location for storing the color info
\r
939 colorVariable[] = {
\r
944 &highlightSquareColor,
\r
945 &premoveHighlightColor,
\r
947 &consoleBackgroundColor,
\r
948 &appData.fontForeColorWhite,
\r
949 &appData.fontBackColorWhite,
\r
950 &appData.fontForeColorBlack,
\r
951 &appData.fontBackColorBlack,
\r
952 &appData.evalHistColorWhite,
\r
953 &appData.evalHistColorBlack,
\r
954 &appData.highlightArrowColor,
\r
957 /* Command line font name parser. NULL name means do nothing.
\r
958 Syntax like "Courier New:10.0 bi" or "Arial:10" or "Arial:10b"
\r
959 For backward compatibility, syntax without the colon is also
\r
960 accepted, but font names with digits in them won't work in that case.
\r
963 ParseFontName(char *name, MyFontParams *mfp)
\r
966 if (name == NULL) return;
\r
968 q = strchr(p, ':');
\r
970 if (q - p >= sizeof(mfp->faceName))
\r
971 ExitArgError("Font name too long:", name);
\r
972 memcpy(mfp->faceName, p, q - p);
\r
973 mfp->faceName[q - p] = NULLCHAR;
\r
977 while (*p && !isdigit(*p)) {
\r
979 if (q - mfp->faceName >= sizeof(mfp->faceName))
\r
980 ExitArgError("Font name too long:", name);
\r
982 while (q > mfp->faceName && q[-1] == ' ') q--;
\r
985 if (!*p) ExitArgError("Font point size missing:", name);
\r
986 mfp->pointSize = (float) atof(p);
\r
987 mfp->bold = (strchr(p, 'b') != NULL);
\r
988 mfp->italic = (strchr(p, 'i') != NULL);
\r
989 mfp->underline = (strchr(p, 'u') != NULL);
\r
990 mfp->strikeout = (strchr(p, 's') != NULL);
\r
991 mfp->charset = DEFAULT_CHARSET;
\r
992 q = strchr(p, 'c');
\r
994 mfp->charset = (BYTE) atoi(q+1);
\r
998 ParseFont(char *name, int number)
\r
999 { // wrapper to shield back-end from 'font'
\r
1000 ParseFontName(name, &font[boardSize][number]->mfp);
\r
1005 { // in WB we have a 2D array of fonts; this initializes their description
\r
1007 /* Point font array elements to structures and
\r
1008 parse default font names */
\r
1009 for (i=0; i<NUM_FONTS; i++) {
\r
1010 for (j=0; j<NUM_SIZES; j++) {
\r
1011 font[j][i] = &fontRec[j][i];
\r
1012 ParseFontName(font[j][i]->def, &font[j][i]->mfp);
\r
1019 { // here we create the actual fonts from the selected descriptions
\r
1021 for (i=0; i<NUM_FONTS; i++) {
\r
1022 for (j=0; j<NUM_SIZES; j++) {
\r
1023 CreateFontInMF(font[j][i]);
\r
1027 /* Color name parser.
\r
1028 X version accepts X color names, but this one
\r
1029 handles only the #rrggbb form (hex) or rrr,ggg,bbb (decimal) */
\r
1031 ParseColorName(char *name)
\r
1033 int red, green, blue, count;
\r
1034 char buf[MSG_SIZ];
\r
1036 count = sscanf(name, "#%2x%2x%2x", &red, &green, &blue);
\r
1038 count = sscanf(name, "%3d%*[^0-9]%3d%*[^0-9]%3d",
\r
1039 &red, &green, &blue);
\r
1042 sprintf(buf, "Can't parse color name %s", name);
\r
1043 DisplayError(buf, 0);
\r
1044 return RGB(0, 0, 0);
\r
1046 return PALETTERGB(red, green, blue);
\r
1050 ParseColor(int n, char *name)
\r
1051 { // for WinBoard the color is an int, which needs to be derived from the string
\r
1052 if(colorVariable[n]) *(int*)colorVariable[n] = ParseColorName(name);
\r
1056 ParseAttribs(COLORREF *color, int *effects, char* argValue)
\r
1058 char *e = argValue;
\r
1062 if (*e == 'b') eff |= CFE_BOLD;
\r
1063 else if (*e == 'i') eff |= CFE_ITALIC;
\r
1064 else if (*e == 'u') eff |= CFE_UNDERLINE;
\r
1065 else if (*e == 's') eff |= CFE_STRIKEOUT;
\r
1066 else if (*e == '#' || isdigit(*e)) break;
\r
1070 *color = ParseColorName(e);
\r
1074 ParseTextAttribs(ColorClass cc, char *s)
\r
1075 { // [HGM] front-end wrapper that does the platform-dependent call
\r
1076 // for XBoard we would set (&appData.colorShout)[cc] = strdup(s);
\r
1077 ParseAttribs(&textAttribs[cc].color, &textAttribs[cc].effects, s);
\r
1081 ParseBoardSize(void *addr, char *name)
\r
1082 { // [HGM] rewritten with return-value ptr to shield back-end from BoardSize
\r
1083 BoardSize bs = SizeTiny;
\r
1084 while (sizeInfo[bs].name != NULL) {
\r
1085 if (StrCaseCmp(name, sizeInfo[bs].name) == 0) {
\r
1086 *(BoardSize *)addr = bs;
\r
1091 ExitArgError("Unrecognized board size value", name);
\r
1096 { // [HGM] import name from appData first
\r
1099 for (cc = (ColorClass)0; cc < ColorNormal; cc++) {
\r
1100 textAttribs[cc].sound.name = strdup((&appData.soundShout)[cc]);
\r
1101 textAttribs[cc].sound.data = NULL;
\r
1102 MyLoadSound(&textAttribs[cc].sound);
\r
1104 for (cc = ColorNormal; cc < NColorClasses; cc++) {
\r
1105 textAttribs[cc].sound.name = strdup("");
\r
1106 textAttribs[cc].sound.data = NULL;
\r
1108 for (sc = (SoundClass)0; sc < NSoundClasses; sc++) {
\r
1109 sounds[sc].name = strdup((&appData.soundMove)[sc]);
\r
1110 sounds[sc].data = NULL;
\r
1111 MyLoadSound(&sounds[sc]);
\r
1116 SetCommPortDefaults()
\r
1118 memset(&dcb, 0, sizeof(DCB)); // required by VS 2002 +
\r
1119 dcb.DCBlength = sizeof(DCB);
\r
1120 dcb.BaudRate = 9600;
\r
1121 dcb.fBinary = TRUE;
\r
1122 dcb.fParity = FALSE;
\r
1123 dcb.fOutxCtsFlow = FALSE;
\r
1124 dcb.fOutxDsrFlow = FALSE;
\r
1125 dcb.fDtrControl = DTR_CONTROL_ENABLE;
\r
1126 dcb.fDsrSensitivity = FALSE;
\r
1127 dcb.fTXContinueOnXoff = TRUE;
\r
1128 dcb.fOutX = FALSE;
\r
1130 dcb.fNull = FALSE;
\r
1131 dcb.fRtsControl = RTS_CONTROL_ENABLE;
\r
1132 dcb.fAbortOnError = FALSE;
\r
1134 dcb.Parity = SPACEPARITY;
\r
1135 dcb.StopBits = ONESTOPBIT;
\r
1138 // [HGM] args: these three cases taken out to stay in front-end
\r
1140 SaveFontArg(FILE *f, ArgDescriptor *ad)
\r
1141 { // in WinBoard every board size has its own font, and the "argLoc" identifies the table,
\r
1142 // while the curent board size determines the element. This system should be ported to XBoard.
\r
1143 // What the table contains pointers to, and how to print the font description, remains platform-dependent
\r
1145 for (bs=0; bs<NUM_SIZES; bs++) {
\r
1146 MyFontParams *mfp = &font[bs][(int) ad->argLoc]->mfp;
\r
1147 fprintf(f, "/size=%s ", sizeInfo[bs].name);
\r
1148 fprintf(f, "/%s=\"%s:%g%s%s%s%s%sc%d\"\n",
\r
1149 ad->argName, mfp->faceName, mfp->pointSize,
\r
1150 mfp->bold || mfp->italic || mfp->underline || mfp->strikeout ? " " : "",
\r
1151 mfp->bold ? "b" : "",
\r
1152 mfp->italic ? "i" : "",
\r
1153 mfp->underline ? "u" : "",
\r
1154 mfp->strikeout ? "s" : "",
\r
1155 (int)mfp->charset);
\r
1161 { // [HGM] copy the names from the internal WB variables to appData
\r
1164 for (cc = (ColorClass)0; cc < ColorNormal; cc++)
\r
1165 (&appData.soundShout)[cc] = textAttribs[cc].sound.name;
\r
1166 for (sc = (SoundClass)0; sc < NSoundClasses; sc++)
\r
1167 (&appData.soundMove)[sc] = sounds[sc].name;
\r
1171 SaveAttribsArg(FILE *f, ArgDescriptor *ad)
\r
1172 { // here the "argLoc" defines a table index. It could have contained the 'ta' pointer itself, though
\r
1173 MyTextAttribs* ta = &textAttribs[(ColorClass)ad->argLoc];
\r
1174 fprintf(f, "/%s=\"%s%s%s%s%s#%02lx%02lx%02lx\"\n", ad->argName,
\r
1175 (ta->effects & CFE_BOLD) ? "b" : "",
\r
1176 (ta->effects & CFE_ITALIC) ? "i" : "",
\r
1177 (ta->effects & CFE_UNDERLINE) ? "u" : "",
\r
1178 (ta->effects & CFE_STRIKEOUT) ? "s" : "",
\r
1179 (ta->effects) ? " " : "",
\r
1180 ta->color&0xff, (ta->color >> 8)&0xff, (ta->color >> 16)&0xff);
\r
1184 SaveColor(FILE *f, ArgDescriptor *ad)
\r
1185 { // in WinBoard the color is an int and has to be converted to text. In X it would be a string already?
\r
1186 COLORREF color = *(COLORREF *)colorVariable[(int)ad->argLoc];
\r
1187 fprintf(f, "/%s=#%02lx%02lx%02lx\n", ad->argName,
\r
1188 color&0xff, (color>>8)&0xff, (color>>16)&0xff);
\r
1192 SaveBoardSize(FILE *f, char *name, void *addr)
\r
1193 { // wrapper to shield back-end from BoardSize & sizeInfo
\r
1194 fprintf(f, "/%s=%s\n", name, sizeInfo[*(BoardSize *)addr].name);
\r
1198 ParseCommPortSettings(char *s)
\r
1199 { // wrapper to keep dcb from back-end
\r
1200 ParseCommSettings(s, &dcb);
\r
1205 { // wrapper to shield use of window handles from back-end (make addressible by number?)
\r
1206 GetActualPlacement(hwndMain, &wpMain);
\r
1207 GetActualPlacement(hwndConsole, &wpConsole);
\r
1208 GetActualPlacement(commentDialog, &wpComment);
\r
1209 GetActualPlacement(editTagsDialog, &wpTags);
\r
1210 GetActualPlacement(gameListDialog, &wpGameList);
\r
1211 GetActualPlacement(moveHistoryDialog, &wpMoveHistory);
\r
1212 GetActualPlacement(evalGraphDialog, &wpEvalGraph);
\r
1213 GetActualPlacement(engineOutputDialog, &wpEngineOutput);
\r
1217 PrintCommPortSettings(FILE *f, char *name)
\r
1218 { // wrapper to shield back-end from DCB
\r
1219 PrintCommSettings(f, name, &dcb);
\r
1223 MySearchPath(char *installDir, char *name, char *fullname)
\r
1225 char *dummy, buf[MSG_SIZ];
\r
1226 if(name[0] == '%' && strchr(name+1, '%')) { // [HGM] recognize %*% as environment variable
\r
1227 strcpy(buf, name+1);
\r
1228 *strchr(buf, '%') = 0;
\r
1229 installDir = getenv(buf);
\r
1230 sprintf(fullname, "%s\\%s", installDir, strchr(name+1, '%')+1);
\r
1231 return strlen(fullname);
\r
1233 return (int) SearchPath(installDir, name, NULL, MSG_SIZ, fullname, &dummy);
\r
1237 MyGetFullPathName(char *name, char *fullname)
\r
1240 return (int) GetFullPathName(name, MSG_SIZ, fullname, &dummy);
\r
1245 { // [HGM] args: allows testing if main window is realized from back-end
\r
1246 return hwndMain != NULL;
\r
1250 PopUpStartupDialog()
\r
1254 lpProc = MakeProcInstance((FARPROC)StartupDialog, hInst);
\r
1255 DialogBox(hInst, MAKEINTRESOURCE(DLG_Startup), NULL, (DLGPROC)lpProc);
\r
1256 FreeProcInstance(lpProc);
\r
1259 /*---------------------------------------------------------------------------*\
\r
1261 * GDI board drawing routines
\r
1263 \*---------------------------------------------------------------------------*/
\r
1265 /* [AS] Draw square using background texture */
\r
1266 static void DrawTile( int dx, int dy, int dw, int dh, HDC dst, HDC src, int mode, int sx, int sy )
\r
1271 return; /* Should never happen! */
\r
1274 SetGraphicsMode( dst, GM_ADVANCED );
\r
1281 /* X reflection */
\r
1286 x.eDx = (FLOAT) dw + dx - 1;
\r
1289 SetWorldTransform( dst, &x );
\r
1292 /* Y reflection */
\r
1298 x.eDy = (FLOAT) dh + dy - 1;
\r
1300 SetWorldTransform( dst, &x );
\r
1308 x.eDx = (FLOAT) dx;
\r
1309 x.eDy = (FLOAT) dy;
\r
1312 SetWorldTransform( dst, &x );
\r
1316 BitBlt( dst, dx, dy, dw, dh, src, sx, sy, SRCCOPY );
\r
1324 SetWorldTransform( dst, &x );
\r
1326 ModifyWorldTransform( dst, 0, MWT_IDENTITY );
\r
1329 /* [AS] [HGM] Make room for more piece types, so all pieces can be different */
\r
1331 PM_WP = (int) WhitePawn,
\r
1332 PM_WN = (int) WhiteKnight,
\r
1333 PM_WB = (int) WhiteBishop,
\r
1334 PM_WR = (int) WhiteRook,
\r
1335 PM_WQ = (int) WhiteQueen,
\r
1336 PM_WF = (int) WhiteFerz,
\r
1337 PM_WW = (int) WhiteWazir,
\r
1338 PM_WE = (int) WhiteAlfil,
\r
1339 PM_WM = (int) WhiteMan,
\r
1340 PM_WO = (int) WhiteCannon,
\r
1341 PM_WU = (int) WhiteUnicorn,
\r
1342 PM_WH = (int) WhiteNightrider,
\r
1343 PM_WA = (int) WhiteAngel,
\r
1344 PM_WC = (int) WhiteMarshall,
\r
1345 PM_WAB = (int) WhiteCardinal,
\r
1346 PM_WD = (int) WhiteDragon,
\r
1347 PM_WL = (int) WhiteLance,
\r
1348 PM_WS = (int) WhiteCobra,
\r
1349 PM_WV = (int) WhiteFalcon,
\r
1350 PM_WSG = (int) WhiteSilver,
\r
1351 PM_WG = (int) WhiteGrasshopper,
\r
1352 PM_WK = (int) WhiteKing,
\r
1353 PM_BP = (int) BlackPawn,
\r
1354 PM_BN = (int) BlackKnight,
\r
1355 PM_BB = (int) BlackBishop,
\r
1356 PM_BR = (int) BlackRook,
\r
1357 PM_BQ = (int) BlackQueen,
\r
1358 PM_BF = (int) BlackFerz,
\r
1359 PM_BW = (int) BlackWazir,
\r
1360 PM_BE = (int) BlackAlfil,
\r
1361 PM_BM = (int) BlackMan,
\r
1362 PM_BO = (int) BlackCannon,
\r
1363 PM_BU = (int) BlackUnicorn,
\r
1364 PM_BH = (int) BlackNightrider,
\r
1365 PM_BA = (int) BlackAngel,
\r
1366 PM_BC = (int) BlackMarshall,
\r
1367 PM_BG = (int) BlackGrasshopper,
\r
1368 PM_BAB = (int) BlackCardinal,
\r
1369 PM_BD = (int) BlackDragon,
\r
1370 PM_BL = (int) BlackLance,
\r
1371 PM_BS = (int) BlackCobra,
\r
1372 PM_BV = (int) BlackFalcon,
\r
1373 PM_BSG = (int) BlackSilver,
\r
1374 PM_BK = (int) BlackKing
\r
1377 static HFONT hPieceFont = NULL;
\r
1378 static HBITMAP hPieceMask[(int) EmptySquare];
\r
1379 static HBITMAP hPieceFace[(int) EmptySquare];
\r
1380 static int fontBitmapSquareSize = 0;
\r
1381 static char pieceToFontChar[(int) EmptySquare] =
\r
1382 { 'p', 'n', 'b', 'r', 'q',
\r
1383 'n', 'b', 'p', 'n', 'b', 'r', 'b', 'r', 'q', 'k',
\r
1384 'k', 'o', 'm', 'v', 't', 'w',
\r
1385 'v', 't', 'o', 'm', 'v', 't', 'v', 't', 'w', 'l',
\r
1388 extern BOOL SetCharTable( char *table, const char * map );
\r
1389 /* [HGM] moved to backend.c */
\r
1391 static void SetPieceBackground( HDC hdc, COLORREF color, int mode )
\r
1394 BYTE r1 = GetRValue( color );
\r
1395 BYTE g1 = GetGValue( color );
\r
1396 BYTE b1 = GetBValue( color );
\r
1402 /* Create a uniform background first */
\r
1403 hbrush = CreateSolidBrush( color );
\r
1404 SetRect( &rc, 0, 0, squareSize, squareSize );
\r
1405 FillRect( hdc, &rc, hbrush );
\r
1406 DeleteObject( hbrush );
\r
1409 /* Vertical gradient, good for pawn, knight and rook, less for queen and king */
\r
1410 int steps = squareSize / 2;
\r
1413 for( i=0; i<steps; i++ ) {
\r
1414 BYTE r = r1 - (r1-r2) * i / steps;
\r
1415 BYTE g = g1 - (g1-g2) * i / steps;
\r
1416 BYTE b = b1 - (b1-b2) * i / steps;
\r
1418 hbrush = CreateSolidBrush( RGB(r,g,b) );
\r
1419 SetRect( &rc, i + squareSize - steps, 0, i + squareSize - steps + 1, squareSize );
\r
1420 FillRect( hdc, &rc, hbrush );
\r
1421 DeleteObject(hbrush);
\r
1424 else if( mode == 2 ) {
\r
1425 /* Diagonal gradient, good more or less for every piece */
\r
1426 POINT triangle[3];
\r
1427 HPEN hpen = SelectObject( hdc, GetStockObject(NULL_PEN) );
\r
1428 HBRUSH hbrush_old;
\r
1429 int steps = squareSize;
\r
1432 triangle[0].x = squareSize - steps;
\r
1433 triangle[0].y = squareSize;
\r
1434 triangle[1].x = squareSize;
\r
1435 triangle[1].y = squareSize;
\r
1436 triangle[2].x = squareSize;
\r
1437 triangle[2].y = squareSize - steps;
\r
1439 for( i=0; i<steps; i++ ) {
\r
1440 BYTE r = r1 - (r1-r2) * i / steps;
\r
1441 BYTE g = g1 - (g1-g2) * i / steps;
\r
1442 BYTE b = b1 - (b1-b2) * i / steps;
\r
1444 hbrush = CreateSolidBrush( RGB(r,g,b) );
\r
1445 hbrush_old = SelectObject( hdc, hbrush );
\r
1446 Polygon( hdc, triangle, 3 );
\r
1447 SelectObject( hdc, hbrush_old );
\r
1448 DeleteObject(hbrush);
\r
1453 SelectObject( hdc, hpen );
\r
1458 [AS] The method I use to create the bitmaps it a bit tricky, but it
\r
1459 seems to work ok. The main problem here is to find the "inside" of a chess
\r
1460 piece: follow the steps as explained below.
\r
1462 static void CreatePieceMaskFromFont( HDC hdc_window, HDC hdc, int index )
\r
1466 COLORREF chroma = RGB(0xFF,0x00,0xFF);
\r
1470 int backColor = whitePieceColor;
\r
1471 int foreColor = blackPieceColor;
\r
1473 if( index < (int)BlackPawn && appData.fontBackColorWhite != appData.fontForeColorWhite ) {
\r
1474 backColor = appData.fontBackColorWhite;
\r
1475 foreColor = appData.fontForeColorWhite;
\r
1477 else if( index >= (int)BlackPawn && appData.fontBackColorBlack != appData.fontForeColorBlack ) {
\r
1478 backColor = appData.fontBackColorBlack;
\r
1479 foreColor = appData.fontForeColorBlack;
\r
1483 hbm = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );
\r
1485 hbm_old = SelectObject( hdc, hbm );
\r
1489 rc.right = squareSize;
\r
1490 rc.bottom = squareSize;
\r
1492 /* Step 1: background is now black */
\r
1493 FillRect( hdc, &rc, GetStockObject(BLACK_BRUSH) );
\r
1495 GetTextExtentPoint32( hdc, &pieceToFontChar[index], 1, &sz );
\r
1497 pt.x = (squareSize - sz.cx) / 2;
\r
1498 pt.y = (squareSize - sz.cy) / 2;
\r
1500 SetBkMode( hdc, TRANSPARENT );
\r
1501 SetTextColor( hdc, chroma );
\r
1502 /* Step 2: the piece has been drawn in purple, there are now black and purple in this bitmap */
\r
1503 TextOut( hdc, pt.x, pt.y, &pieceToFontChar[appData.allWhite && index >= (int)BlackPawn ? index - (int)BlackPawn : index], 1 );
\r
1505 SelectObject( hdc, GetStockObject(WHITE_BRUSH) );
\r
1506 /* Step 3: the area outside the piece is filled with white */
\r
1507 // FloodFill( hdc, 0, 0, chroma );
\r
1508 ExtFloodFill( hdc, 0, 0, 0, FLOODFILLSURFACE );
\r
1509 ExtFloodFill( hdc, 0, squareSize-1, 0, FLOODFILLSURFACE ); // [HGM] fill from all 4 corners, for if piece too big
\r
1510 ExtFloodFill( hdc, squareSize-1, 0, 0, FLOODFILLSURFACE );
\r
1511 ExtFloodFill( hdc, squareSize-1, squareSize-1, 0, FLOODFILLSURFACE );
\r
1512 SelectObject( hdc, GetStockObject(BLACK_BRUSH) );
\r
1514 Step 4: this is the tricky part, the area inside the piece is filled with black,
\r
1515 but if the start point is not inside the piece we're lost!
\r
1516 There should be a better way to do this... if we could create a region or path
\r
1517 from the fill operation we would be fine for example.
\r
1519 // FloodFill( hdc, squareSize / 2, squareSize / 2, RGB(0xFF,0xFF,0xFF) );
\r
1520 ExtFloodFill( hdc, squareSize / 2, squareSize / 2, RGB(0xFF,0xFF,0xFF), FLOODFILLBORDER );
\r
1522 { /* [HGM] shave off edges of mask, in an attempt to correct for the fact that FloodFill does not work correctly under Win XP */
\r
1523 HDC dc2 = CreateCompatibleDC( hdc_window );
\r
1524 HBITMAP bm2 = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );
\r
1526 SelectObject( dc2, bm2 );
\r
1527 BitBlt( dc2, 0, 0, squareSize, squareSize, hdc, 0, 0, SRCCOPY ); // make copy
\r
1528 BitBlt( hdc, 0, 1, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );
\r
1529 BitBlt( hdc, 2, 1, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );
\r
1530 BitBlt( hdc, 1, 0, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );
\r
1531 BitBlt( hdc, 1, 2, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );
\r
1534 DeleteObject( bm2 );
\r
1537 SetTextColor( hdc, 0 );
\r
1539 Step 5: some fonts have "disconnected" areas that are skipped by the fill:
\r
1540 draw the piece again in black for safety.
\r
1542 TextOut( hdc, pt.x, pt.y, &pieceToFontChar[appData.allWhite && index >= (int)BlackPawn ? index - (int)BlackPawn : index], 1 );
\r
1544 SelectObject( hdc, hbm_old );
\r
1546 if( hPieceMask[index] != NULL ) {
\r
1547 DeleteObject( hPieceMask[index] );
\r
1550 hPieceMask[index] = hbm;
\r
1553 hbm = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );
\r
1555 SelectObject( hdc, hbm );
\r
1558 HDC dc1 = CreateCompatibleDC( hdc_window );
\r
1559 HDC dc2 = CreateCompatibleDC( hdc_window );
\r
1560 HBITMAP bm2 = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );
\r
1562 SelectObject( dc1, hPieceMask[index] );
\r
1563 SelectObject( dc2, bm2 );
\r
1564 FillRect( dc2, &rc, GetStockObject(WHITE_BRUSH) );
\r
1565 BitBlt( dc2, 0, 0, squareSize, squareSize, dc1, 0, 0, SRCINVERT );
\r
1568 Now dc2 contains the inverse of the piece mask, i.e. a mask that preserves
\r
1569 the piece background and deletes (makes transparent) the rest.
\r
1570 Thanks to that mask, we are free to paint the background with the greates
\r
1571 freedom, as we'll be able to mask off the unwanted parts when finished.
\r
1572 We use this, to make gradients and give the pieces a "roundish" look.
\r
1574 SetPieceBackground( hdc, backColor, 2 );
\r
1575 BitBlt( hdc, 0, 0, squareSize, squareSize, dc2, 0, 0, SRCAND );
\r
1579 DeleteObject( bm2 );
\r
1582 SetTextColor( hdc, foreColor );
\r
1583 TextOut( hdc, pt.x, pt.y, &pieceToFontChar[appData.allWhite && index >= (int)BlackPawn ? index - (int)BlackPawn : index], 1 );
\r
1585 SelectObject( hdc, hbm_old );
\r
1587 if( hPieceFace[index] != NULL ) {
\r
1588 DeleteObject( hPieceFace[index] );
\r
1591 hPieceFace[index] = hbm;
\r
1594 static int TranslatePieceToFontPiece( int piece )
\r
1624 case BlackMarshall:
\r
1628 case BlackNightrider:
\r
1634 case BlackUnicorn:
\r
1638 case BlackGrasshopper:
\r
1650 case BlackCardinal:
\r
1657 case WhiteMarshall:
\r
1661 case WhiteNightrider:
\r
1667 case WhiteUnicorn:
\r
1671 case WhiteGrasshopper:
\r
1683 case WhiteCardinal:
\r
1692 void CreatePiecesFromFont()
\r
1695 HDC hdc_window = NULL;
\r
1701 if( fontBitmapSquareSize < 0 ) {
\r
1702 /* Something went seriously wrong in the past: do not try to recreate fonts! */
\r
1706 if( appData.renderPiecesWithFont == NULL || appData.renderPiecesWithFont[0] == NULLCHAR || appData.renderPiecesWithFont[0] == '*' ) {
\r
1707 fontBitmapSquareSize = -1;
\r
1711 if( fontBitmapSquareSize != squareSize ) {
\r
1712 hdc_window = GetDC( hwndMain );
\r
1713 hdc = CreateCompatibleDC( hdc_window );
\r
1715 if( hPieceFont != NULL ) {
\r
1716 DeleteObject( hPieceFont );
\r
1719 for( i=0; i<=(int)BlackKing; i++ ) {
\r
1720 hPieceMask[i] = NULL;
\r
1721 hPieceFace[i] = NULL;
\r
1727 if( appData.fontPieceSize >= 50 && appData.fontPieceSize <= 150 ) {
\r
1728 fontHeight = appData.fontPieceSize;
\r
1731 fontHeight = (fontHeight * squareSize) / 100;
\r
1733 lf.lfHeight = -MulDiv( fontHeight, GetDeviceCaps(hdc, LOGPIXELSY), 72 );
\r
1735 lf.lfEscapement = 0;
\r
1736 lf.lfOrientation = 0;
\r
1737 lf.lfWeight = FW_NORMAL;
\r
1739 lf.lfUnderline = 0;
\r
1740 lf.lfStrikeOut = 0;
\r
1741 lf.lfCharSet = DEFAULT_CHARSET;
\r
1742 lf.lfOutPrecision = OUT_DEFAULT_PRECIS;
\r
1743 lf.lfClipPrecision = CLIP_DEFAULT_PRECIS;
\r
1744 lf.lfQuality = PROOF_QUALITY;
\r
1745 lf.lfPitchAndFamily = DEFAULT_PITCH | FF_DONTCARE;
\r
1746 strncpy( lf.lfFaceName, appData.renderPiecesWithFont, sizeof(lf.lfFaceName) );
\r
1747 lf.lfFaceName[ sizeof(lf.lfFaceName) - 1 ] = '\0';
\r
1749 hPieceFont = CreateFontIndirect( &lf );
\r
1751 if( hPieceFont == NULL ) {
\r
1752 fontBitmapSquareSize = -2;
\r
1755 /* Setup font-to-piece character table */
\r
1756 if( ! SetCharTable(pieceToFontChar, appData.fontToPieceTable) ) {
\r
1757 /* No (or wrong) global settings, try to detect the font */
\r
1758 if( strstr(lf.lfFaceName,"Alpha") != NULL ) {
\r
1760 SetCharTable(pieceToFontChar, "phbrqkojntwl");
\r
1762 else if( strstr(lf.lfFaceName,"DiagramTT") != NULL ) {
\r
1763 /* DiagramTT* family */
\r
1764 SetCharTable(pieceToFontChar, "PNLRQKpnlrqk");
\r
1766 else if( strstr(lf.lfFaceName,"WinboardF") != NULL ) {
\r
1767 /* Fairy symbols */
\r
1768 SetCharTable(pieceToFontChar, "PNBRQFEACWMOHIJGDVSLUKpnbrqfeacwmohijgdvsluk");
\r
1770 else if( strstr(lf.lfFaceName,"GC2004D") != NULL ) {
\r
1771 /* Good Companion (Some characters get warped as literal :-( */
\r
1772 char s[] = "1cmWG0??S??oYI23wgQU";
\r
1773 s[0]=0xB9; s[1]=0xA9; s[6]=0xB1; s[11]=0xBB; s[12]=0xAB; s[17]=0xB3;
\r
1774 SetCharTable(pieceToFontChar, s);
\r
1777 /* Cases, Condal, Leipzig, Lucena, Marroquin, Merida, Usual */
\r
1778 SetCharTable(pieceToFontChar, "pnbrqkomvtwl");
\r
1782 /* Create bitmaps */
\r
1783 hfont_old = SelectObject( hdc, hPieceFont );
\r
1784 for(i=(int)WhitePawn; i<(int)EmptySquare; i++) /* [HGM] made a loop for this */
\r
1785 if(PieceToChar((ChessSquare)i) != '.') /* skip unused pieces */
\r
1786 CreatePieceMaskFromFont( hdc_window, hdc, i );
\r
1788 SelectObject( hdc, hfont_old );
\r
1790 fontBitmapSquareSize = squareSize;
\r
1794 if( hdc != NULL ) {
\r
1798 if( hdc_window != NULL ) {
\r
1799 ReleaseDC( hwndMain, hdc_window );
\r
1804 DoLoadBitmap(HINSTANCE hinst, char *piece, int squareSize, char *suffix)
\r
1808 sprintf(name, "%s%d%s", piece, squareSize, suffix);
\r
1809 if (gameInfo.event &&
\r
1810 strcmp(gameInfo.event, "Easter Egg Hunt") == 0 &&
\r
1811 strcmp(name, "k80s") == 0) {
\r
1812 strcpy(name, "tim");
\r
1814 return LoadBitmap(hinst, name);
\r
1818 /* Insert a color into the program's logical palette
\r
1819 structure. This code assumes the given color is
\r
1820 the result of the RGB or PALETTERGB macro, and it
\r
1821 knows how those macros work (which is documented).
\r
1824 InsertInPalette(COLORREF color)
\r
1826 LPPALETTEENTRY pe = &(pLogPal->palPalEntry[pLogPal->palNumEntries]);
\r
1828 if (pLogPal->palNumEntries++ >= PALETTESIZE) {
\r
1829 DisplayFatalError("Too many colors", 0, 1);
\r
1830 pLogPal->palNumEntries--;
\r
1834 pe->peFlags = (char) 0;
\r
1835 pe->peRed = (char) (0xFF & color);
\r
1836 pe->peGreen = (char) (0xFF & (color >> 8));
\r
1837 pe->peBlue = (char) (0xFF & (color >> 16));
\r
1843 InitDrawingColors()
\r
1845 if (pLogPal == NULL) {
\r
1846 /* Allocate enough memory for a logical palette with
\r
1847 * PALETTESIZE entries and set the size and version fields
\r
1848 * of the logical palette structure.
\r
1850 pLogPal = (NPLOGPALETTE)
\r
1851 LocalAlloc(LMEM_FIXED, (sizeof(LOGPALETTE) +
\r
1852 (sizeof(PALETTEENTRY) * (PALETTESIZE))));
\r
1853 pLogPal->palVersion = 0x300;
\r
1855 pLogPal->palNumEntries = 0;
\r
1857 InsertInPalette(lightSquareColor);
\r
1858 InsertInPalette(darkSquareColor);
\r
1859 InsertInPalette(whitePieceColor);
\r
1860 InsertInPalette(blackPieceColor);
\r
1861 InsertInPalette(highlightSquareColor);
\r
1862 InsertInPalette(premoveHighlightColor);
\r
1864 /* create a logical color palette according the information
\r
1865 * in the LOGPALETTE structure.
\r
1867 hPal = CreatePalette((LPLOGPALETTE) pLogPal);
\r
1869 lightSquareBrush = CreateSolidBrush(lightSquareColor);
\r
1870 blackSquareBrush = CreateSolidBrush(blackPieceColor);
\r
1871 darkSquareBrush = CreateSolidBrush(darkSquareColor);
\r
1872 whitePieceBrush = CreateSolidBrush(whitePieceColor);
\r
1873 blackPieceBrush = CreateSolidBrush(blackPieceColor);
\r
1874 iconBkgndBrush = CreateSolidBrush(GetSysColor(COLOR_BACKGROUND));
\r
1875 explodeBrush = CreateSolidBrush(highlightSquareColor); // [HGM] atomic
\r
1876 markerBrush = CreateSolidBrush(premoveHighlightColor); // [HGM] markers
\r
1877 /* [AS] Force rendering of the font-based pieces */
\r
1878 if( fontBitmapSquareSize > 0 ) {
\r
1879 fontBitmapSquareSize = 0;
\r
1885 BoardWidth(int boardSize, int n)
\r
1886 { /* [HGM] argument n added to allow different width and height */
\r
1887 int lineGap = sizeInfo[boardSize].lineGap;
\r
1889 if( appData.overrideLineGap >= 0 && appData.overrideLineGap <= 5 ) {
\r
1890 lineGap = appData.overrideLineGap;
\r
1893 return (n + 1) * lineGap +
\r
1894 n * sizeInfo[boardSize].squareSize;
\r
1897 /* Respond to board resize by dragging edge */
\r
1899 ResizeBoard(int newSizeX, int newSizeY, int flags)
\r
1901 BoardSize newSize = NUM_SIZES - 1;
\r
1902 static int recurse = 0;
\r
1903 if (IsIconic(hwndMain)) return;
\r
1904 if (recurse > 0) return;
\r
1906 while (newSize > 0) {
\r
1907 InitDrawingSizes(newSize+1000, 0); // [HGM] kludge to update sizeInfo without visible effects
\r
1908 if(newSizeX >= sizeInfo[newSize].cliWidth &&
\r
1909 newSizeY >= sizeInfo[newSize].cliHeight) break;
\r
1912 boardSize = newSize;
\r
1913 InitDrawingSizes(boardSize, flags);
\r
1918 extern Boolean twoBoards, partnerUp; // [HGM] dual
\r
1921 InitDrawingSizes(BoardSize boardSize, int flags)
\r
1923 int i, boardWidth, boardHeight; /* [HGM] height treated separately */
\r
1924 ChessSquare piece;
\r
1925 static int oldBoardSize = -1, oldTinyLayout = 0;
\r
1927 SIZE clockSize, messageSize;
\r
1929 char buf[MSG_SIZ];
\r
1931 HMENU hmenu = GetMenu(hwndMain);
\r
1932 RECT crect, wrect, oldRect;
\r
1934 LOGBRUSH logbrush;
\r
1936 int suppressVisibleEffects = 0; // [HGM] kludge to request updating sizeInfo only
\r
1937 if((int)boardSize >= 1000 ) { boardSize -= 1000; suppressVisibleEffects = 1; }
\r
1939 /* [HGM] call with -2 uses old size (for if nr of files, ranks changes) */
\r
1940 if(boardSize == (BoardSize)(-2) ) boardSize = oldBoardSize;
\r
1942 oldRect.left = wpMain.x; //[HGM] placement: remember previous window params
\r
1943 oldRect.top = wpMain.y;
\r
1944 oldRect.right = wpMain.x + wpMain.width;
\r
1945 oldRect.bottom = wpMain.y + wpMain.height;
\r
1947 tinyLayout = sizeInfo[boardSize].tinyLayout;
\r
1948 smallLayout = sizeInfo[boardSize].smallLayout;
\r
1949 squareSize = sizeInfo[boardSize].squareSize;
\r
1950 lineGap = sizeInfo[boardSize].lineGap;
\r
1951 minorSize = 0; /* [HGM] Kludge to see if demagnified pieces need to be shifted */
\r
1953 if( appData.overrideLineGap >= 0 && appData.overrideLineGap <= 5 ) {
\r
1954 lineGap = appData.overrideLineGap;
\r
1957 if (tinyLayout != oldTinyLayout) {
\r
1958 long style = GetWindowLong(hwndMain, GWL_STYLE);
\r
1960 style &= ~WS_SYSMENU;
\r
1961 InsertMenu(hmenu, IDM_Exit, MF_BYCOMMAND, IDM_Minimize,
\r
1962 "&Minimize\tCtrl+F4");
\r
1964 style |= WS_SYSMENU;
\r
1965 RemoveMenu(hmenu, IDM_Minimize, MF_BYCOMMAND);
\r
1967 SetWindowLong(hwndMain, GWL_STYLE, style);
\r
1969 for (i=0; menuBarText[tinyLayout][i]; i++) {
\r
1970 ModifyMenu(hmenu, i, MF_STRING|MF_BYPOSITION|MF_POPUP,
\r
1971 (UINT)GetSubMenu(hmenu, i), menuBarText[tinyLayout][i]);
\r
1973 DrawMenuBar(hwndMain);
\r
1976 boardWidth = BoardWidth(boardSize, BOARD_WIDTH);
\r
1977 boardHeight = BoardWidth(boardSize, BOARD_HEIGHT);
\r
1979 /* Get text area sizes */
\r
1980 hdc = GetDC(hwndMain);
\r
1981 if (appData.clockMode) {
\r
1982 sprintf(buf, "White: %s", TimeString(23*60*60*1000L));
\r
1984 sprintf(buf, "White");
\r
1986 oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);
\r
1987 GetTextExtentPoint(hdc, buf, strlen(buf), &clockSize);
\r
1988 SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);
\r
1989 str = "We only care about the height here";
\r
1990 GetTextExtentPoint(hdc, str, strlen(str), &messageSize);
\r
1991 SelectObject(hdc, oldFont);
\r
1992 ReleaseDC(hwndMain, hdc);
\r
1994 /* Compute where everything goes */
\r
1995 if((first.programLogo || second.programLogo) && !tinyLayout) {
\r
1996 /* [HGM] logo: if either logo is on, reserve space for it */
\r
1997 logoHeight = 2*clockSize.cy;
\r
1998 leftLogoRect.left = OUTER_MARGIN;
\r
1999 leftLogoRect.right = leftLogoRect.left + 4*clockSize.cy;
\r
2000 leftLogoRect.top = OUTER_MARGIN;
\r
2001 leftLogoRect.bottom = OUTER_MARGIN + logoHeight;
\r
2003 rightLogoRect.right = OUTER_MARGIN + boardWidth;
\r
2004 rightLogoRect.left = rightLogoRect.right - 4*clockSize.cy;
\r
2005 rightLogoRect.top = OUTER_MARGIN;
\r
2006 rightLogoRect.bottom = OUTER_MARGIN + logoHeight;
\r
2009 whiteRect.left = leftLogoRect.right;
\r
2010 whiteRect.right = OUTER_MARGIN + boardWidth/2 - INNER_MARGIN/2;
\r
2011 whiteRect.top = OUTER_MARGIN;
\r
2012 whiteRect.bottom = whiteRect.top + logoHeight;
\r
2014 blackRect.right = rightLogoRect.left;
\r
2015 blackRect.left = whiteRect.right + INNER_MARGIN;
\r
2016 blackRect.top = whiteRect.top;
\r
2017 blackRect.bottom = whiteRect.bottom;
\r
2019 whiteRect.left = OUTER_MARGIN;
\r
2020 whiteRect.right = whiteRect.left + boardWidth/2 - INNER_MARGIN/2;
\r
2021 whiteRect.top = OUTER_MARGIN;
\r
2022 whiteRect.bottom = whiteRect.top + clockSize.cy;
\r
2024 blackRect.left = whiteRect.right + INNER_MARGIN;
\r
2025 blackRect.right = blackRect.left + boardWidth/2 - 1;
\r
2026 blackRect.top = whiteRect.top;
\r
2027 blackRect.bottom = whiteRect.bottom;
\r
2029 logoHeight = 0; // [HGM] logo: suppress logo after change to tiny layout!
\r
2032 messageRect.left = OUTER_MARGIN + MESSAGE_LINE_LEFTMARGIN;
\r
2033 if (appData.showButtonBar) {
\r
2034 messageRect.right = OUTER_MARGIN + boardWidth // [HGM] logo: expressed independent of clock placement
\r
2035 - N_BUTTONS*BUTTON_WIDTH - MESSAGE_LINE_LEFTMARGIN;
\r
2037 messageRect.right = OUTER_MARGIN + boardWidth;
\r
2039 messageRect.top = whiteRect.bottom + INNER_MARGIN;
\r
2040 messageRect.bottom = messageRect.top + messageSize.cy;
\r
2042 boardRect.left = OUTER_MARGIN;
\r
2043 boardRect.right = boardRect.left + boardWidth;
\r
2044 boardRect.top = messageRect.bottom + INNER_MARGIN;
\r
2045 boardRect.bottom = boardRect.top + boardHeight;
\r
2047 sizeInfo[boardSize].cliWidth = boardRect.right + OUTER_MARGIN;
\r
2048 sizeInfo[boardSize].cliHeight = boardRect.bottom + OUTER_MARGIN;
\r
2049 oldBoardSize = boardSize;
\r
2050 oldTinyLayout = tinyLayout;
\r
2051 winW = 2 * GetSystemMetrics(SM_CXFRAME) + boardRect.right + OUTER_MARGIN;
\r
2052 winH = 2 * GetSystemMetrics(SM_CYFRAME) + GetSystemMetrics(SM_CYMENU) +
\r
2053 GetSystemMetrics(SM_CYCAPTION) + boardRect.bottom + OUTER_MARGIN;
\r
2054 winW *= 1 + twoBoards;
\r
2055 if(suppressVisibleEffects) return; // [HGM] when called for filling sizeInfo only
\r
2056 wpMain.width = winW; // [HGM] placement: set through temporary which can used by initial sizing choice
\r
2057 wpMain.height = winH; // without disturbing window attachments
\r
2058 GetWindowRect(hwndMain, &wrect);
\r
2059 SetWindowPos(hwndMain, NULL, 0, 0, wpMain.width, wpMain.height,
\r
2060 SWP_NOCOPYBITS|SWP_NOZORDER|SWP_NOMOVE);
\r
2062 // [HGM] placement: let attached windows follow size change.
\r
2063 ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, moveHistoryDialog, &wpMoveHistory );
\r
2064 ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, evalGraphDialog, &wpEvalGraph );
\r
2065 ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, engineOutputDialog, &wpEngineOutput );
\r
2066 ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, gameListDialog, &wpGameList );
\r
2067 ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, hwndConsole, &wpConsole );
\r
2069 /* compensate if menu bar wrapped */
\r
2070 GetClientRect(hwndMain, &crect);
\r
2071 offby = boardRect.bottom + OUTER_MARGIN - crect.bottom;
\r
2072 wpMain.height += offby;
\r
2074 case WMSZ_TOPLEFT:
\r
2075 SetWindowPos(hwndMain, NULL,
\r
2076 wrect.right - wpMain.width, wrect.bottom - wpMain.height,
\r
2077 wpMain.width, wpMain.height, SWP_NOCOPYBITS|SWP_NOZORDER);
\r
2080 case WMSZ_TOPRIGHT:
\r
2082 SetWindowPos(hwndMain, NULL,
\r
2083 wrect.left, wrect.bottom - wpMain.height,
\r
2084 wpMain.width, wpMain.height, SWP_NOCOPYBITS|SWP_NOZORDER);
\r
2087 case WMSZ_BOTTOMLEFT:
\r
2089 SetWindowPos(hwndMain, NULL,
\r
2090 wrect.right - wpMain.width, wrect.top,
\r
2091 wpMain.width, wpMain.height, SWP_NOCOPYBITS|SWP_NOZORDER);
\r
2094 case WMSZ_BOTTOMRIGHT:
\r
2098 SetWindowPos(hwndMain, NULL, 0, 0, wpMain.width, wpMain.height,
\r
2099 SWP_NOCOPYBITS|SWP_NOZORDER|SWP_NOMOVE);
\r
2104 for (i = 0; i < N_BUTTONS; i++) {
\r
2105 if (buttonDesc[i].hwnd != NULL) {
\r
2106 DestroyWindow(buttonDesc[i].hwnd);
\r
2107 buttonDesc[i].hwnd = NULL;
\r
2109 if (appData.showButtonBar) {
\r
2110 buttonDesc[i].hwnd =
\r
2111 CreateWindow("BUTTON", buttonDesc[i].label,
\r
2112 WS_VISIBLE | WS_CHILD | BS_PUSHBUTTON,
\r
2113 boardRect.right - BUTTON_WIDTH*(N_BUTTONS-i),
\r
2114 messageRect.top, BUTTON_WIDTH, messageSize.cy, hwndMain,
\r
2115 (HMENU) buttonDesc[i].id,
\r
2116 (HINSTANCE) GetWindowLong(hwndMain, GWL_HINSTANCE), NULL);
\r
2118 SendMessage(buttonDesc[i].hwnd, WM_SETFONT,
\r
2119 (WPARAM)font[boardSize][MESSAGE_FONT]->hf,
\r
2120 MAKELPARAM(FALSE, 0));
\r
2122 if (buttonDesc[i].id == IDM_Pause)
\r
2123 hwndPause = buttonDesc[i].hwnd;
\r
2124 buttonDesc[i].wndproc = (WNDPROC)
\r
2125 SetWindowLong(buttonDesc[i].hwnd, GWL_WNDPROC, (LONG) ButtonProc);
\r
2128 if (gridPen != NULL) DeleteObject(gridPen);
\r
2129 if (highlightPen != NULL) DeleteObject(highlightPen);
\r
2130 if (premovePen != NULL) DeleteObject(premovePen);
\r
2131 if (lineGap != 0) {
\r
2132 logbrush.lbStyle = BS_SOLID;
\r
2133 logbrush.lbColor = RGB(0, 0, 0); /* grid pen color = black */
\r
2135 ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,
\r
2136 lineGap, &logbrush, 0, NULL);
\r
2137 logbrush.lbColor = highlightSquareColor;
\r
2139 ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,
\r
2140 lineGap, &logbrush, 0, NULL);
\r
2142 logbrush.lbColor = premoveHighlightColor;
\r
2144 ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,
\r
2145 lineGap, &logbrush, 0, NULL);
\r
2147 /* [HGM] Loop had to be split in part for vert. and hor. lines */
\r
2148 for (i = 0; i < BOARD_HEIGHT + 1; i++) {
\r
2149 gridEndpoints[i*2].x = boardRect.left + lineGap / 2;
\r
2150 gridEndpoints[i*2].y = gridEndpoints[i*2 + 1].y =
\r
2151 boardRect.top + lineGap / 2 + (i * (squareSize + lineGap));
\r
2152 gridEndpoints[i*2 + 1].x = boardRect.left + lineGap / 2 +
\r
2153 BOARD_WIDTH * (squareSize + lineGap);
\r
2154 gridVertexCounts[i*2] = gridVertexCounts[i*2 + 1] = 2;
\r
2156 for (i = 0; i < BOARD_WIDTH + 1; i++) {
\r
2157 gridEndpoints[i*2 + BOARD_HEIGHT*2 + 2].y = boardRect.top + lineGap / 2;
\r
2158 gridEndpoints[i*2 + BOARD_HEIGHT*2 + 2].x =
\r
2159 gridEndpoints[i*2 + 1 + BOARD_HEIGHT*2 + 2].x = boardRect.left +
\r
2160 lineGap / 2 + (i * (squareSize + lineGap));
\r
2161 gridEndpoints[i*2 + 1 + BOARD_HEIGHT*2 + 2].y =
\r
2162 boardRect.top + BOARD_HEIGHT * (squareSize + lineGap);
\r
2163 gridVertexCounts[i*2] = gridVertexCounts[i*2 + 1] = 2;
\r
2167 /* [HGM] Licensing requirement */
\r
2169 if(gameInfo.variant == VariantGothic) GothicPopUp( GOTHIC, VariantGothic); else
\r
2172 if(gameInfo.variant == VariantFalcon) GothicPopUp( FALCON, VariantFalcon); else
\r
2174 GothicPopUp( "", VariantNormal);
\r
2177 /* if (boardSize == oldBoardSize) return; [HGM] variant might have changed */
\r
2179 /* Load piece bitmaps for this board size */
\r
2180 for (i=0; i<=2; i++) {
\r
2181 for (piece = WhitePawn;
\r
2182 (int) piece < (int) BlackPawn;
\r
2183 piece = (ChessSquare) ((int) piece + 1)) {
\r
2184 if (pieceBitmap[i][piece] != NULL)
\r
2185 DeleteObject(pieceBitmap[i][piece]);
\r
2189 fontBitmapSquareSize = 0; /* [HGM] render: make sure pieces will be recreated, as we might need others now */
\r
2190 // Orthodox Chess pieces
\r
2191 pieceBitmap[0][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "s");
\r
2192 pieceBitmap[0][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "s");
\r
2193 pieceBitmap[0][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "s");
\r
2194 pieceBitmap[0][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "s");
\r
2195 pieceBitmap[0][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "s");
\r
2196 pieceBitmap[1][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "o");
\r
2197 pieceBitmap[1][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "o");
\r
2198 pieceBitmap[1][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "o");
\r
2199 pieceBitmap[1][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "o");
\r
2200 pieceBitmap[1][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "o");
\r
2201 pieceBitmap[2][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "w");
\r
2202 pieceBitmap[2][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "w");
\r
2203 pieceBitmap[2][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "w");
\r
2204 pieceBitmap[2][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "w");
\r
2205 pieceBitmap[2][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "w");
\r
2206 if( !strcmp(appData.variant, "shogi") && (squareSize==72 || squareSize==49)) {
\r
2207 // in Shogi, Hijack the unused Queen for Lance
\r
2208 pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "s");
\r
2209 pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "o");
\r
2210 pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "w");
\r
2212 pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "s");
\r
2213 pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "o");
\r
2214 pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "w");
\r
2217 if(squareSize <= 72 && squareSize >= 33) {
\r
2218 /* A & C are available in most sizes now */
\r
2219 if(squareSize != 49 && squareSize != 72 && squareSize != 33) { // Vortex-like
\r
2220 pieceBitmap[0][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "s");
\r
2221 pieceBitmap[1][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "o");
\r
2222 pieceBitmap[2][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "w");
\r
2223 pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "s");
\r
2224 pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "o");
\r
2225 pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "w");
\r
2226 pieceBitmap[0][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "s");
\r
2227 pieceBitmap[1][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "o");
\r
2228 pieceBitmap[2][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "w");
\r
2229 pieceBitmap[0][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "s");
\r
2230 pieceBitmap[1][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "o");
\r
2231 pieceBitmap[2][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "w");
\r
2232 } else { // Smirf-like
\r
2233 pieceBitmap[0][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "s");
\r
2234 pieceBitmap[1][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "o");
\r
2235 pieceBitmap[2][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "w");
\r
2237 if(gameInfo.variant == VariantGothic) { // Vortex-like
\r
2238 pieceBitmap[0][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "s");
\r
2239 pieceBitmap[1][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "o");
\r
2240 pieceBitmap[2][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "w");
\r
2241 } else { // WinBoard standard
\r
2242 pieceBitmap[0][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "s");
\r
2243 pieceBitmap[1][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "o");
\r
2244 pieceBitmap[2][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "w");
\r
2249 if(squareSize==72 || squareSize==49 || squareSize==33) { /* experiment with some home-made bitmaps */
\r
2250 pieceBitmap[0][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "s");
\r
2251 pieceBitmap[1][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "o");
\r
2252 pieceBitmap[2][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "w");
\r
2253 pieceBitmap[0][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "s");
\r
2254 pieceBitmap[1][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "o");
\r
2255 pieceBitmap[2][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "w");
\r
2256 pieceBitmap[0][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "s");
\r
2257 pieceBitmap[1][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "o");
\r
2258 pieceBitmap[2][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "w");
\r
2259 pieceBitmap[0][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "s");
\r
2260 pieceBitmap[1][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "o");
\r
2261 pieceBitmap[2][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "w");
\r
2262 pieceBitmap[0][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "s");
\r
2263 pieceBitmap[1][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "o");
\r
2264 pieceBitmap[2][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "w");
\r
2265 pieceBitmap[0][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "s");
\r
2266 pieceBitmap[1][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "o");
\r
2267 pieceBitmap[2][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "w");
\r
2268 pieceBitmap[0][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "s");
\r
2269 pieceBitmap[1][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "o");
\r
2270 pieceBitmap[2][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "w");
\r
2271 pieceBitmap[0][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "s");
\r
2272 pieceBitmap[1][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "o");
\r
2273 pieceBitmap[2][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "w");
\r
2274 pieceBitmap[0][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "s");
\r
2275 pieceBitmap[1][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "o");
\r
2276 pieceBitmap[2][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "w");
\r
2277 pieceBitmap[0][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "s");
\r
2278 pieceBitmap[1][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "o");
\r
2279 pieceBitmap[2][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "w");
\r
2281 if(gameInfo.variant == VariantShogi) { /* promoted Gold represemtations */
\r
2282 pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "wp", squareSize, "s");
\r
2283 pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "wp", squareSize, "o");
\r
2284 pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "w", squareSize, "w");
\r
2285 pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "wn", squareSize, "s");
\r
2286 pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "wn", squareSize, "o");
\r
2287 pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "w", squareSize, "w");
\r
2288 pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "ws", squareSize, "s");
\r
2289 pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "ws", squareSize, "o");
\r
2290 pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "w", squareSize, "w");
\r
2291 pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "wl", squareSize, "s");
\r
2292 pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "wl", squareSize, "o");
\r
2293 pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "w", squareSize, "w");
\r
2295 pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "s");
\r
2296 pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "o");
\r
2297 pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "w");
\r
2298 pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "s");
\r
2299 pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "o");
\r
2300 pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "w");
\r
2301 pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "s");
\r
2302 pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "o");
\r
2303 pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "w");
\r
2304 pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "s");
\r
2305 pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "o");
\r
2306 pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "w");
\r
2309 } else { /* other size, no special bitmaps available. Use smaller symbols */
\r
2310 if((int)boardSize < 2) minorSize = sizeInfo[0].squareSize;
\r
2311 else minorSize = sizeInfo[(int)boardSize - 2].squareSize;
\r
2312 pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "s");
\r
2313 pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "o");
\r
2314 pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "w");
\r
2315 pieceBitmap[0][WhiteCardinal] = DoLoadBitmap(hInst, "b", minorSize, "s");
\r
2316 pieceBitmap[1][WhiteCardinal] = DoLoadBitmap(hInst, "b", minorSize, "o");
\r
2317 pieceBitmap[2][WhiteCardinal] = DoLoadBitmap(hInst, "b", minorSize, "w");
\r
2318 pieceBitmap[0][WhiteDragon] = DoLoadBitmap(hInst, "r", minorSize, "s");
\r
2319 pieceBitmap[1][WhiteDragon] = DoLoadBitmap(hInst, "r", minorSize, "o");
\r
2320 pieceBitmap[2][WhiteDragon] = DoLoadBitmap(hInst, "r", minorSize, "w");
\r
2321 pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "s");
\r
2322 pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "o");
\r
2323 pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "w");
\r
2327 if(gameInfo.variant == VariantShogi && squareSize == 58)
\r
2328 /* special Shogi support in this size */
\r
2329 { for (i=0; i<=2; i++) { /* replace all bitmaps */
\r
2330 for (piece = WhitePawn;
\r
2331 (int) piece < (int) BlackPawn;
\r
2332 piece = (ChessSquare) ((int) piece + 1)) {
\r
2333 if (pieceBitmap[i][piece] != NULL)
\r
2334 DeleteObject(pieceBitmap[i][piece]);
\r
2337 pieceBitmap[0][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "o");
\r
2338 pieceBitmap[0][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "o");
\r
2339 pieceBitmap[0][WhiteBishop] = DoLoadBitmap(hInst, "sb", squareSize, "o");
\r
2340 pieceBitmap[0][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "o");
\r
2341 pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "o");
\r
2342 pieceBitmap[0][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "o");
\r
2343 pieceBitmap[0][WhiteFerz] = DoLoadBitmap(hInst, "sf", squareSize, "o");
\r
2344 pieceBitmap[0][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "o");
\r
2345 pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "su", squareSize, "o");
\r
2346 pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "sh", squareSize, "o");
\r
2347 pieceBitmap[0][WhiteCardinal] = DoLoadBitmap(hInst, "sa", squareSize, "o");
\r
2348 pieceBitmap[0][WhiteDragon] = DoLoadBitmap(hInst, "sc", squareSize, "o");
\r
2349 pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "sg", squareSize, "o");
\r
2350 pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "ss", squareSize, "o");
\r
2351 pieceBitmap[1][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "o");
\r
2352 pieceBitmap[1][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "o");
\r
2353 pieceBitmap[1][WhiteBishop] = DoLoadBitmap(hInst, "sb", squareSize, "o");
\r
2354 pieceBitmap[1][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "o");
\r
2355 pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "o");
\r
2356 pieceBitmap[1][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "o");
\r
2357 pieceBitmap[1][WhiteFerz] = DoLoadBitmap(hInst, "sf", squareSize, "o");
\r
2358 pieceBitmap[1][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "o");
\r
2359 pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "su", squareSize, "o");
\r
2360 pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "sh", squareSize, "o");
\r
2361 pieceBitmap[1][WhiteCardinal] = DoLoadBitmap(hInst, "sa", squareSize, "o");
\r
2362 pieceBitmap[1][WhiteDragon] = DoLoadBitmap(hInst, "sc", squareSize, "o");
\r
2363 pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "sg", squareSize, "o");
\r
2364 pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "ss", squareSize, "o");
\r
2365 pieceBitmap[2][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "w");
\r
2366 pieceBitmap[2][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "w");
\r
2367 pieceBitmap[2][WhiteBishop] = DoLoadBitmap(hInst, "sr", squareSize, "w");
\r
2368 pieceBitmap[2][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "w");
\r
2369 pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "w");
\r
2370 pieceBitmap[2][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "w");
\r
2371 pieceBitmap[2][WhiteFerz] = DoLoadBitmap(hInst, "sw", squareSize, "w");
\r
2372 pieceBitmap[2][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "w");
\r
2373 pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "sp", squareSize, "w");
\r
2374 pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "sn", squareSize, "w");
\r
2375 pieceBitmap[2][WhiteCardinal] = DoLoadBitmap(hInst, "sr", squareSize, "w");
\r
2376 pieceBitmap[2][WhiteDragon] = DoLoadBitmap(hInst, "sr", squareSize, "w");
\r
2377 pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "sl", squareSize, "w");
\r
2378 pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "sw", squareSize, "w");
\r
2384 PieceBitmap(ChessSquare p, int kind)
\r
2386 if ((int) p >= (int) BlackPawn)
\r
2387 p = (ChessSquare) ((int) p - (int) BlackPawn + (int) WhitePawn);
\r
2389 return pieceBitmap[kind][(int) p];
\r
2392 /***************************************************************/
\r
2394 #define MIN(a,b) (((a) < (b)) ? (a) : (b))
\r
2395 #define MAX(a,b) (((a) > (b)) ? (a) : (b))
\r
2397 #define MIN3(a,b,c) (((a) < (b) && (a) < (c)) ? (a) : (((b) < (a) && (b) < (c)) ? (b) : (c)))
\r
2398 #define MAX3(a,b,c) (((a) > (b) && (a) > (c)) ? (a) : (((b) > (a) && (b) > (c)) ? (b) : (c)))
\r
2402 SquareToPos(int row, int column, int * x, int * y)
\r
2405 *x = boardRect.left + lineGap + ((BOARD_WIDTH-1)-column) * (squareSize + lineGap);
\r
2406 *y = boardRect.top + lineGap + row * (squareSize + lineGap);
\r
2408 *x = boardRect.left + lineGap + column * (squareSize + lineGap);
\r
2409 *y = boardRect.top + lineGap + ((BOARD_HEIGHT-1)-row) * (squareSize + lineGap);
\r
2414 DrawCoordsOnDC(HDC hdc)
\r
2416 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
2417 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
2418 char str[2] = { NULLCHAR, NULLCHAR };
\r
2419 int oldMode, oldAlign, x, y, start, i;
\r
2423 if (!appData.showCoords)
\r
2426 start = flipView ? 1-(ONE!='1') : 23+(ONE!='1')-BOARD_HEIGHT;
\r
2428 oldBrush = SelectObject(hdc, GetStockObject(BLACK_BRUSH));
\r
2429 oldMode = SetBkMode(hdc, (appData.monoMode ? OPAQUE : TRANSPARENT));
\r
2430 oldAlign = GetTextAlign(hdc);
\r
2431 oldFont = SelectObject(hdc, font[boardSize][COORD_FONT]->hf);
\r
2433 y = boardRect.top + lineGap;
\r
2434 x = boardRect.left + lineGap + gameInfo.holdingsWidth*(squareSize + lineGap);
\r
2436 SetTextAlign(hdc, TA_LEFT|TA_TOP);
\r
2437 for (i = 0; i < BOARD_HEIGHT; i++) {
\r
2438 str[0] = files[start + i];
\r
2439 ExtTextOut(hdc, x + 2, y + 1, 0, NULL, str, 1, NULL);
\r
2440 y += squareSize + lineGap;
\r
2443 start = flipView ? 12-(BOARD_RGHT-BOARD_LEFT) : 12;
\r
2445 SetTextAlign(hdc, TA_RIGHT|TA_BOTTOM);
\r
2446 for (i = 0; i < BOARD_RGHT - BOARD_LEFT; i++) {
\r
2447 str[0] = ranks[start + i];
\r
2448 ExtTextOut(hdc, x + squareSize - 2, y - 1, 0, NULL, str, 1, NULL);
\r
2449 x += squareSize + lineGap;
\r
2452 SelectObject(hdc, oldBrush);
\r
2453 SetBkMode(hdc, oldMode);
\r
2454 SetTextAlign(hdc, oldAlign);
\r
2455 SelectObject(hdc, oldFont);
\r
2459 DrawGridOnDC(HDC hdc)
\r
2463 if (lineGap != 0) {
\r
2464 oldPen = SelectObject(hdc, gridPen);
\r
2465 PolyPolyline(hdc, gridEndpoints, gridVertexCounts, BOARD_WIDTH+BOARD_HEIGHT + 2);
\r
2466 SelectObject(hdc, oldPen);
\r
2470 #define HIGHLIGHT_PEN 0
\r
2471 #define PREMOVE_PEN 1
\r
2474 DrawHighlightOnDC(HDC hdc, BOOLEAN on, int x, int y, int pen)
\r
2477 HPEN oldPen, hPen;
\r
2478 if (lineGap == 0) return;
\r
2480 x1 = boardRect.left +
\r
2481 lineGap/2 + ((BOARD_WIDTH-1)-x) * (squareSize + lineGap);
\r
2482 y1 = boardRect.top +
\r
2483 lineGap/2 + y * (squareSize + lineGap);
\r
2485 x1 = boardRect.left +
\r
2486 lineGap/2 + x * (squareSize + lineGap);
\r
2487 y1 = boardRect.top +
\r
2488 lineGap/2 + ((BOARD_HEIGHT-1)-y) * (squareSize + lineGap);
\r
2490 hPen = pen ? premovePen : highlightPen;
\r
2491 oldPen = SelectObject(hdc, on ? hPen : gridPen);
\r
2492 MoveToEx(hdc, x1, y1, NULL);
\r
2493 LineTo(hdc, x1 + squareSize + lineGap, y1);
\r
2494 LineTo(hdc, x1 + squareSize + lineGap, y1 + squareSize + lineGap);
\r
2495 LineTo(hdc, x1, y1 + squareSize + lineGap);
\r
2496 LineTo(hdc, x1, y1);
\r
2497 SelectObject(hdc, oldPen);
\r
2501 DrawHighlightsOnDC(HDC hdc, HighlightInfo *h, int pen)
\r
2504 for (i=0; i<2; i++) {
\r
2505 if (h->sq[i].x >= 0 && h->sq[i].y >= 0)
\r
2506 DrawHighlightOnDC(hdc, TRUE,
\r
2507 h->sq[i].x, h->sq[i].y,
\r
2512 /* Note: sqcolor is used only in monoMode */
\r
2513 /* Note that this code is largely duplicated in woptions.c,
\r
2514 function DrawSampleSquare, so that needs to be updated too */
\r
2516 DrawPieceOnDC(HDC hdc, ChessSquare piece, int color, int sqcolor, int x, int y, HDC tmphdc)
\r
2518 HBITMAP oldBitmap;
\r
2522 if (appData.blindfold) return;
\r
2524 /* [AS] Use font-based pieces if needed */
\r
2525 if( fontBitmapSquareSize >= 0 && (squareSize > 32 || gameInfo.variant >= VariantShogi)) {
\r
2526 /* Create piece bitmaps, or do nothing if piece set is up to date */
\r
2527 CreatePiecesFromFont();
\r
2529 if( fontBitmapSquareSize == squareSize ) {
\r
2530 int index = TranslatePieceToFontPiece(piece);
\r
2532 SelectObject( tmphdc, hPieceMask[ index ] );
\r
2534 if(appData.upsideDown ? color==flipView : (flipView && gameInfo.variant == VariantShogi))
\r
2535 StretchBlt(hdc, x+squareSize, y+squareSize, -squareSize, -squareSize, tmphdc, 0, 0, squareSize, squareSize, SRCAND);
\r
2539 squareSize, squareSize,
\r
2544 SelectObject( tmphdc, hPieceFace[ index ] );
\r
2546 if(appData.upsideDown ? color==flipView : (flipView && gameInfo.variant == VariantShogi))
\r
2547 StretchBlt(hdc, x+squareSize, y+squareSize, -squareSize, -squareSize, tmphdc, 0, 0, squareSize, squareSize, SRCPAINT);
\r
2551 squareSize, squareSize,
\r
2560 if (appData.monoMode) {
\r
2561 SelectObject(tmphdc, PieceBitmap(piece,
\r
2562 color == sqcolor ? OUTLINE_PIECE : SOLID_PIECE));
\r
2563 BitBlt(hdc, x, y, squareSize, squareSize, tmphdc, 0, 0,
\r
2564 sqcolor ? SRCCOPY : NOTSRCCOPY);
\r
2566 tmpSize = squareSize;
\r
2568 ((piece >= (int)WhiteNightrider && piece <= WhiteGrasshopper) ||
\r
2569 (piece >= (int)BlackNightrider && piece <= BlackGrasshopper)) ) {
\r
2570 /* [HGM] no bitmap available for promoted pieces in Crazyhouse */
\r
2571 /* Bitmaps of smaller size are substituted, but we have to align them */
\r
2572 x += (squareSize - minorSize)>>1;
\r
2573 y += squareSize - minorSize - 2;
\r
2574 tmpSize = minorSize;
\r
2576 if (color || appData.allWhite ) {
\r
2577 oldBitmap = SelectObject(tmphdc, PieceBitmap(piece, WHITE_PIECE));
\r
2579 oldBrush = SelectObject(hdc, whitePieceBrush);
\r
2580 else oldBrush = SelectObject(hdc, blackPieceBrush);
\r
2581 if(appData.upsideDown && color==flipView)
\r
2582 StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, 0x00B8074A);
\r
2584 BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, 0x00B8074A);
\r
2585 /* Use black for outline of white pieces */
\r
2586 SelectObject(tmphdc, PieceBitmap(piece, OUTLINE_PIECE));
\r
2587 if(appData.upsideDown && color==flipView)
\r
2588 StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, SRCAND);
\r
2590 BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, SRCAND);
\r
2592 /* Use square color for details of black pieces */
\r
2593 oldBitmap = SelectObject(tmphdc, PieceBitmap(piece, SOLID_PIECE));
\r
2594 oldBrush = SelectObject(hdc, blackPieceBrush);
\r
2595 if(appData.upsideDown && !flipView)
\r
2596 StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, 0x00B8074A);
\r
2598 BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, 0x00B8074A);
\r
2600 SelectObject(hdc, oldBrush);
\r
2601 SelectObject(tmphdc, oldBitmap);
\r
2605 /* [AS] Compute a drawing mode for a square, based on specified settings (see DrawTile) */
\r
2606 int GetBackTextureMode( int algo )
\r
2608 int result = BACK_TEXTURE_MODE_DISABLED;
\r
2612 case BACK_TEXTURE_MODE_PLAIN:
\r
2613 result = 1; /* Always use identity map */
\r
2615 case BACK_TEXTURE_MODE_FULL_RANDOM:
\r
2616 result = 1 + (myrandom() % 3); /* Pick a transformation at random */
\r
2624 [AS] Compute and save texture drawing info, otherwise we may not be able
\r
2625 to handle redraws cleanly (as random numbers would always be different).
\r
2627 VOID RebuildTextureSquareInfo()
\r
2637 ZeroMemory( &backTextureSquareInfo, sizeof(backTextureSquareInfo) );
\r
2639 if( liteBackTexture != NULL ) {
\r
2640 if( GetObject( liteBackTexture, sizeof(bi), &bi ) > 0 ) {
\r
2641 lite_w = bi.bmWidth;
\r
2642 lite_h = bi.bmHeight;
\r
2646 if( darkBackTexture != NULL ) {
\r
2647 if( GetObject( darkBackTexture, sizeof(bi), &bi ) > 0 ) {
\r
2648 dark_w = bi.bmWidth;
\r
2649 dark_h = bi.bmHeight;
\r
2653 for( row=0; row<BOARD_HEIGHT; row++ ) {
\r
2654 for( col=0; col<BOARD_WIDTH; col++ ) {
\r
2655 if( (col + row) & 1 ) {
\r
2657 if( lite_w >= squareSize && lite_h >= squareSize ) {
\r
2658 if( lite_w >= squareSize*BOARD_WIDTH )
\r
2659 backTextureSquareInfo[row][col].x = (2*col+1)*lite_w/(2*BOARD_WIDTH) - squareSize/2; /* [HGM] cut out of center of virtual square */
\r
2661 backTextureSquareInfo[row][col].x = col * (lite_w - squareSize) / (BOARD_WIDTH-1); /* [HGM] divide by size-1 in stead of size! */
\r
2662 if( lite_h >= squareSize*BOARD_HEIGHT )
\r
2663 backTextureSquareInfo[row][col].y = (2*(BOARD_HEIGHT-row)-1)*lite_h/(2*BOARD_HEIGHT) - squareSize/2;
\r
2665 backTextureSquareInfo[row][col].y = (BOARD_HEIGHT-1-row) * (lite_h - squareSize) / (BOARD_HEIGHT-1);
\r
2666 backTextureSquareInfo[row][col].mode = GetBackTextureMode(liteBackTextureMode);
\r
2671 if( dark_w >= squareSize && dark_h >= squareSize ) {
\r
2672 if( dark_w >= squareSize*BOARD_WIDTH )
\r
2673 backTextureSquareInfo[row][col].x = (2*col+1) * dark_w / (2*BOARD_WIDTH) - squareSize/2;
\r
2675 backTextureSquareInfo[row][col].x = col * (dark_w - squareSize) / (BOARD_WIDTH-1);
\r
2676 if( dark_h >= squareSize*BOARD_HEIGHT )
\r
2677 backTextureSquareInfo[row][col].y = (2*(BOARD_HEIGHT-row)-1) * dark_h / (2*BOARD_HEIGHT) - squareSize/2;
\r
2679 backTextureSquareInfo[row][col].y = (BOARD_HEIGHT-1-row) * (dark_h - squareSize) / (BOARD_HEIGHT-1);
\r
2680 backTextureSquareInfo[row][col].mode = GetBackTextureMode(darkBackTextureMode);
\r
2687 /* [AS] Arrow highlighting support */
\r
2689 static int A_WIDTH = 5; /* Width of arrow body */
\r
2691 #define A_HEIGHT_FACTOR 6 /* Length of arrow "point", relative to body width */
\r
2692 #define A_WIDTH_FACTOR 3 /* Width of arrow "point", relative to body width */
\r
2694 static double Sqr( double x )
\r
2699 static int Round( double x )
\r
2701 return (int) (x + 0.5);
\r
2704 /* Draw an arrow between two points using current settings */
\r
2705 VOID DrawArrowBetweenPoints( HDC hdc, int s_x, int s_y, int d_x, int d_y )
\r
2708 double dx, dy, j, k, x, y;
\r
2710 if( d_x == s_x ) {
\r
2711 int h = (d_y > s_y) ? +A_WIDTH*A_HEIGHT_FACTOR : -A_WIDTH*A_HEIGHT_FACTOR;
\r
2713 arrow[0].x = s_x + A_WIDTH;
\r
2716 arrow[1].x = s_x + A_WIDTH;
\r
2717 arrow[1].y = d_y - h;
\r
2719 arrow[2].x = s_x + A_WIDTH*A_WIDTH_FACTOR;
\r
2720 arrow[2].y = d_y - h;
\r
2725 arrow[4].x = s_x - A_WIDTH*A_WIDTH_FACTOR;
\r
2726 arrow[4].y = d_y - h;
\r
2728 arrow[5].x = s_x - A_WIDTH;
\r
2729 arrow[5].y = d_y - h;
\r
2731 arrow[6].x = s_x - A_WIDTH;
\r
2734 else if( d_y == s_y ) {
\r
2735 int w = (d_x > s_x) ? +A_WIDTH*A_HEIGHT_FACTOR : -A_WIDTH*A_HEIGHT_FACTOR;
\r
2738 arrow[0].y = s_y + A_WIDTH;
\r
2740 arrow[1].x = d_x - w;
\r
2741 arrow[1].y = s_y + A_WIDTH;
\r
2743 arrow[2].x = d_x - w;
\r
2744 arrow[2].y = s_y + A_WIDTH*A_WIDTH_FACTOR;
\r
2749 arrow[4].x = d_x - w;
\r
2750 arrow[4].y = s_y - A_WIDTH*A_WIDTH_FACTOR;
\r
2752 arrow[5].x = d_x - w;
\r
2753 arrow[5].y = s_y - A_WIDTH;
\r
2756 arrow[6].y = s_y - A_WIDTH;
\r
2759 /* [AS] Needed a lot of paper for this! :-) */
\r
2760 dy = (double) (d_y - s_y) / (double) (d_x - s_x);
\r
2761 dx = (double) (s_x - d_x) / (double) (s_y - d_y);
\r
2763 j = sqrt( Sqr(A_WIDTH) / (1.0 + Sqr(dx)) );
\r
2765 k = sqrt( Sqr(A_WIDTH*A_HEIGHT_FACTOR) / (1.0 + Sqr(dy)) );
\r
2770 arrow[0].x = Round(x - j);
\r
2771 arrow[0].y = Round(y + j*dx);
\r
2773 arrow[1].x = Round(x + j);
\r
2774 arrow[1].y = Round(y - j*dx);
\r
2777 x = (double) d_x - k;
\r
2778 y = (double) d_y - k*dy;
\r
2781 x = (double) d_x + k;
\r
2782 y = (double) d_y + k*dy;
\r
2785 arrow[2].x = Round(x + j);
\r
2786 arrow[2].y = Round(y - j*dx);
\r
2788 arrow[3].x = Round(x + j*A_WIDTH_FACTOR);
\r
2789 arrow[3].y = Round(y - j*A_WIDTH_FACTOR*dx);
\r
2794 arrow[5].x = Round(x - j*A_WIDTH_FACTOR);
\r
2795 arrow[5].y = Round(y + j*A_WIDTH_FACTOR*dx);
\r
2797 arrow[6].x = Round(x - j);
\r
2798 arrow[6].y = Round(y + j*dx);
\r
2801 Polygon( hdc, arrow, 7 );
\r
2804 /* [AS] Draw an arrow between two squares */
\r
2805 VOID DrawArrowBetweenSquares( HDC hdc, int s_col, int s_row, int d_col, int d_row )
\r
2807 int s_x, s_y, d_x, d_y;
\r
2814 if( s_col == d_col && s_row == d_row ) {
\r
2818 /* Get source and destination points */
\r
2819 SquareToPos( s_row, s_col, &s_x, &s_y);
\r
2820 SquareToPos( d_row, d_col, &d_x, &d_y);
\r
2823 d_y += squareSize / 4;
\r
2825 else if( d_y < s_y ) {
\r
2826 d_y += 3 * squareSize / 4;
\r
2829 d_y += squareSize / 2;
\r
2833 d_x += squareSize / 4;
\r
2835 else if( d_x < s_x ) {
\r
2836 d_x += 3 * squareSize / 4;
\r
2839 d_x += squareSize / 2;
\r
2842 s_x += squareSize / 2;
\r
2843 s_y += squareSize / 2;
\r
2845 /* Adjust width */
\r
2846 A_WIDTH = squareSize / 14;
\r
2849 stLB.lbStyle = BS_SOLID;
\r
2850 stLB.lbColor = appData.highlightArrowColor;
\r
2853 hpen = CreatePen( PS_SOLID, 2, RGB(0x00,0x00,0x00) );
\r
2854 holdpen = SelectObject( hdc, hpen );
\r
2855 hbrush = CreateBrushIndirect( &stLB );
\r
2856 holdbrush = SelectObject( hdc, hbrush );
\r
2858 DrawArrowBetweenPoints( hdc, s_x, s_y, d_x, d_y );
\r
2860 SelectObject( hdc, holdpen );
\r
2861 SelectObject( hdc, holdbrush );
\r
2862 DeleteObject( hpen );
\r
2863 DeleteObject( hbrush );
\r
2866 BOOL HasHighlightInfo()
\r
2868 BOOL result = FALSE;
\r
2870 if( highlightInfo.sq[0].x >= 0 && highlightInfo.sq[0].y >= 0 &&
\r
2871 highlightInfo.sq[1].x >= 0 && highlightInfo.sq[1].y >= 0 )
\r
2879 BOOL IsDrawArrowEnabled()
\r
2881 BOOL result = FALSE;
\r
2883 if( appData.highlightMoveWithArrow && squareSize >= 32 ) {
\r
2890 VOID DrawArrowHighlight( HDC hdc )
\r
2892 if( IsDrawArrowEnabled() && HasHighlightInfo() ) {
\r
2893 DrawArrowBetweenSquares( hdc,
\r
2894 highlightInfo.sq[0].x, highlightInfo.sq[0].y,
\r
2895 highlightInfo.sq[1].x, highlightInfo.sq[1].y );
\r
2899 HRGN GetArrowHighlightClipRegion( HDC hdc )
\r
2901 HRGN result = NULL;
\r
2903 if( HasHighlightInfo() ) {
\r
2904 int x1, y1, x2, y2;
\r
2905 int sx, sy, dx, dy;
\r
2907 SquareToPos(highlightInfo.sq[0].y, highlightInfo.sq[0].x, &x1, &y1 );
\r
2908 SquareToPos(highlightInfo.sq[1].y, highlightInfo.sq[1].x, &x2, &y2 );
\r
2910 sx = MIN( x1, x2 );
\r
2911 sy = MIN( y1, y2 );
\r
2912 dx = MAX( x1, x2 ) + squareSize;
\r
2913 dy = MAX( y1, y2 ) + squareSize;
\r
2915 result = CreateRectRgn( sx, sy, dx, dy );
\r
2922 Warning: this function modifies the behavior of several other functions.
\r
2924 Basically, Winboard is optimized to avoid drawing the whole board if not strictly
\r
2925 needed. Unfortunately, the decision whether or not to perform a full or partial
\r
2926 repaint is scattered all over the place, which is not good for features such as
\r
2927 "arrow highlighting" that require a full repaint of the board.
\r
2929 So, I've tried to patch the code where I thought it made sense (e.g. after or during
\r
2930 user interaction, when speed is not so important) but especially to avoid errors
\r
2931 in the displayed graphics.
\r
2933 In such patched places, I always try refer to this function so there is a single
\r
2934 place to maintain knowledge.
\r
2936 To restore the original behavior, just return FALSE unconditionally.
\r
2938 BOOL IsFullRepaintPreferrable()
\r
2940 BOOL result = FALSE;
\r
2942 if( (appData.highlightLastMove || appData.highlightDragging) && IsDrawArrowEnabled() ) {
\r
2943 /* Arrow may appear on the board */
\r
2951 This function is called by DrawPosition to know whether a full repaint must
\r
2954 Only DrawPosition may directly call this function, which makes use of
\r
2955 some state information. Other function should call DrawPosition specifying
\r
2956 the repaint flag, and can use IsFullRepaintPreferrable if needed.
\r
2958 BOOL DrawPositionNeedsFullRepaint()
\r
2960 BOOL result = FALSE;
\r
2963 Probably a slightly better policy would be to trigger a full repaint
\r
2964 when animInfo.piece changes state (i.e. empty -> non-empty and viceversa),
\r
2965 but animation is fast enough that it's difficult to notice.
\r
2967 if( animInfo.piece == EmptySquare ) {
\r
2968 if( (appData.highlightLastMove || appData.highlightDragging) && IsDrawArrowEnabled() /*&& HasHighlightInfo()*/ ) {
\r
2977 DrawBoardOnDC(HDC hdc, Board board, HDC tmphdc)
\r
2979 int row, column, x, y, square_color, piece_color;
\r
2980 ChessSquare piece;
\r
2982 HDC texture_hdc = NULL;
\r
2984 /* [AS] Initialize background textures if needed */
\r
2985 if( liteBackTexture != NULL || darkBackTexture != NULL ) {
\r
2986 static int backTextureBoardSize; /* [HGM] boardsize: also new texture if board format changed */
\r
2987 if( backTextureSquareSize != squareSize
\r
2988 || backTextureBoardSize != BOARD_WIDTH+BOARD_FILES*BOARD_HEIGHT) {
\r
2989 backTextureBoardSize = BOARD_WIDTH+BOARD_FILES*BOARD_HEIGHT;
\r
2990 backTextureSquareSize = squareSize;
\r
2991 RebuildTextureSquareInfo();
\r
2994 texture_hdc = CreateCompatibleDC( hdc );
\r
2997 for (row = 0; row < BOARD_HEIGHT; row++) {
\r
2998 for (column = 0; column < BOARD_WIDTH; column++) {
\r
3000 SquareToPos(row, column, &x, &y);
\r
3002 piece = board[row][column];
\r
3004 square_color = ((column + row) % 2) == 1;
\r
3005 if( gameInfo.variant == VariantXiangqi ) {
\r
3006 square_color = !InPalace(row, column);
\r
3007 if(BOARD_HEIGHT&1) { if(row==BOARD_HEIGHT/2) square_color ^= 1; }
\r
3008 else if(row < BOARD_HEIGHT/2) square_color ^= 1;
\r
3010 piece_color = (int) piece < (int) BlackPawn;
\r
3013 /* [HGM] holdings file: light square or black */
\r
3014 if(column == BOARD_LEFT-2) {
\r
3015 if( row > BOARD_HEIGHT - gameInfo.holdingsSize - 1 )
\r
3018 DisplayHoldingsCount(hdc, x, y, 0, 0); /* black out */
\r
3022 if(column == BOARD_RGHT + 1 ) {
\r
3023 if( row < gameInfo.holdingsSize )
\r
3026 DisplayHoldingsCount(hdc, x, y, 0, 0);
\r
3030 if(column == BOARD_LEFT-1 ) /* left align */
\r
3031 DisplayHoldingsCount(hdc, x, y, flipView, (int) board[row][column]);
\r
3032 else if( column == BOARD_RGHT) /* right align */
\r
3033 DisplayHoldingsCount(hdc, x, y, !flipView, (int) board[row][column]);
\r
3035 if (appData.monoMode) {
\r
3036 if (piece == EmptySquare) {
\r
3037 BitBlt(hdc, x, y, squareSize, squareSize, 0, 0, 0,
\r
3038 square_color ? WHITENESS : BLACKNESS);
\r
3040 DrawPieceOnDC(hdc, piece, piece_color, square_color, x, y, tmphdc);
\r
3043 else if( backTextureSquareInfo[row][column].mode > 0 ) {
\r
3044 /* [AS] Draw the square using a texture bitmap */
\r
3045 HBITMAP hbm = SelectObject( texture_hdc, square_color ? liteBackTexture : darkBackTexture );
\r
3046 int r = row, c = column; // [HGM] do not flip board in flipView
\r
3047 if(flipView) { r = BOARD_HEIGHT-1 - r; c = BOARD_WIDTH-1 - c; }
\r
3050 squareSize, squareSize,
\r
3053 backTextureSquareInfo[r][c].mode,
\r
3054 backTextureSquareInfo[r][c].x,
\r
3055 backTextureSquareInfo[r][c].y );
\r
3057 SelectObject( texture_hdc, hbm );
\r
3059 if (piece != EmptySquare) {
\r
3060 DrawPieceOnDC(hdc, piece, piece_color, -1, x, y, tmphdc);
\r
3064 HBRUSH brush = square_color ? lightSquareBrush : darkSquareBrush;
\r
3066 oldBrush = SelectObject(hdc, brush );
\r
3067 BitBlt(hdc, x, y, squareSize, squareSize, 0, 0, 0, PATCOPY);
\r
3068 SelectObject(hdc, oldBrush);
\r
3069 if (piece != EmptySquare)
\r
3070 DrawPieceOnDC(hdc, piece, piece_color, -1, x, y, tmphdc);
\r
3075 if( texture_hdc != NULL ) {
\r
3076 DeleteDC( texture_hdc );
\r
3080 int saveDiagFlag = 0; FILE *diagFile; // [HGM] diag
\r
3081 void fputDW(FILE *f, int x)
\r
3083 fputc(x & 255, f);
\r
3084 fputc(x>>8 & 255, f);
\r
3085 fputc(x>>16 & 255, f);
\r
3086 fputc(x>>24 & 255, f);
\r
3089 #define MAX_CLIPS 200 /* more than enough */
\r
3092 DrawLogoOnDC(HDC hdc, RECT logoRect, HBITMAP logo)
\r
3094 // HBITMAP bufferBitmap;
\r
3099 int w = 100, h = 50;
\r
3101 if(logo == NULL) return;
\r
3102 // GetClientRect(hwndMain, &Rect);
\r
3103 // bufferBitmap = CreateCompatibleBitmap(hdc, Rect.right-Rect.left+1,
\r
3104 // Rect.bottom-Rect.top+1);
\r
3105 tmphdc = CreateCompatibleDC(hdc);
\r
3106 hbm = SelectObject(tmphdc, logo);
\r
3107 if( GetObject( logo, sizeof(bi), &bi ) > 0 ) {
\r
3111 StretchBlt(hdc, logoRect.left, logoRect.top, logoRect.right - logoRect.left,
\r
3112 logoRect.bottom - logoRect.top, tmphdc, 0, 0, w, h, SRCCOPY);
\r
3113 SelectObject(tmphdc, hbm);
\r
3117 static HDC hdcSeek;
\r
3119 // [HGM] seekgraph
\r
3120 void DrawSeekAxis( int x, int y, int xTo, int yTo )
\r
3123 HPEN hp = SelectObject( hdcSeek, gridPen );
\r
3124 MoveToEx( hdcSeek, boardRect.left+x, boardRect.top+y, &stPt );
\r
3125 LineTo( hdcSeek, boardRect.left+xTo, boardRect.top+yTo );
\r
3126 SelectObject( hdcSeek, hp );
\r
3129 // front-end wrapper for drawing functions to do rectangles
\r
3130 void DrawSeekBackground( int left, int top, int right, int bottom )
\r
3135 if (hdcSeek == NULL) {
\r
3136 hdcSeek = GetDC(hwndMain);
\r
3137 if (!appData.monoMode) {
\r
3138 SelectPalette(hdcSeek, hPal, FALSE);
\r
3139 RealizePalette(hdcSeek);
\r
3142 hp = SelectObject( hdcSeek, gridPen );
\r
3143 rc.top = boardRect.top+top; rc.left = boardRect.left+left;
\r
3144 rc.bottom = boardRect.top+bottom; rc.right = boardRect.left+right;
\r
3145 FillRect( hdcSeek, &rc, lightSquareBrush );
\r
3146 SelectObject( hdcSeek, hp );
\r
3149 // front-end wrapper for putting text in graph
\r
3150 void DrawSeekText(char *buf, int x, int y)
\r
3153 SetBkMode( hdcSeek, TRANSPARENT );
\r
3154 GetTextExtentPoint32( hdcSeek, buf, strlen(buf), &stSize );
\r
3155 TextOut( hdcSeek, boardRect.left+x-3, boardRect.top+y-stSize.cy/2, buf, strlen(buf) );
\r
3158 void DrawSeekDot(int x, int y, int color)
\r
3160 int square = color & 0x80;
\r
3162 HBRUSH oldBrush = SelectObject(hdcSeek,
\r
3163 color == 0 ? markerBrush : color == 1 ? darkSquareBrush : explodeBrush);
\r
3165 Rectangle(hdcSeek, boardRect.left+x - squareSize/9, boardRect.top+y - squareSize/9,
\r
3166 boardRect.left+x + squareSize/9, boardRect.top+y + squareSize/9);
\r
3168 Ellipse(hdcSeek, boardRect.left+x - squareSize/8, boardRect.top+y - squareSize/8,
\r
3169 boardRect.left+x + squareSize/8, boardRect.top+y + squareSize/8);
\r
3170 SelectObject(hdcSeek, oldBrush);
\r
3174 HDCDrawPosition(HDC hdc, BOOLEAN repaint, Board board)
\r
3176 static Board lastReq[2], lastDrawn[2];
\r
3177 static HighlightInfo lastDrawnHighlight, lastDrawnPremove;
\r
3178 static int lastDrawnFlipView = 0;
\r
3179 static int lastReqValid[2] = {0, 0}, lastDrawnValid[2] = {0, 0};
\r
3180 int releaseDC, x, y, x2, y2, row, column, num_clips = 0, i;
\r
3183 HBITMAP bufferBitmap;
\r
3184 HBITMAP oldBitmap;
\r
3186 HRGN clips[MAX_CLIPS];
\r
3187 ChessSquare dragged_piece = EmptySquare;
\r
3188 int nr = twoBoards*partnerUp;
\r
3190 /* I'm undecided on this - this function figures out whether a full
\r
3191 * repaint is necessary on its own, so there's no real reason to have the
\r
3192 * caller tell it that. I think this can safely be set to FALSE - but
\r
3193 * if we trust the callers not to request full repaints unnessesarily, then
\r
3194 * we could skip some clipping work. In other words, only request a full
\r
3195 * redraw when the majority of pieces have changed positions (ie. flip,
\r
3196 * gamestart and similar) --Hawk
\r
3198 Boolean fullrepaint = repaint;
\r
3200 if(DrawSeekGraph()) return; // [HG} seekgraph: suppress printing board if seek graph up
\r
3202 if( DrawPositionNeedsFullRepaint() ) {
\r
3203 fullrepaint = TRUE;
\r
3206 if (board == NULL) {
\r
3207 if (!lastReqValid[nr]) {
\r
3210 board = lastReq[nr];
\r
3212 CopyBoard(lastReq[nr], board);
\r
3213 lastReqValid[nr] = 1;
\r
3216 if (doingSizing) {
\r
3220 if (IsIconic(hwndMain)) {
\r
3224 if (hdc == NULL) {
\r
3225 hdc = GetDC(hwndMain);
\r
3226 if (!appData.monoMode) {
\r
3227 SelectPalette(hdc, hPal, FALSE);
\r
3228 RealizePalette(hdc);
\r
3232 releaseDC = FALSE;
\r
3235 /* Create some work-DCs */
\r
3236 hdcmem = CreateCompatibleDC(hdc);
\r
3237 tmphdc = CreateCompatibleDC(hdc);
\r
3239 /* If dragging is in progress, we temporarely remove the piece */
\r
3240 /* [HGM] or temporarily decrease count if stacked */
\r
3241 /* !! Moved to before board compare !! */
\r
3242 if (dragInfo.from.x >= 0 && dragInfo.pos.x >= 0) {
\r
3243 dragged_piece = board[dragInfo.from.y][dragInfo.from.x];
\r
3244 if(dragInfo.from.x == BOARD_LEFT-2 ) {
\r
3245 if(--board[dragInfo.from.y][dragInfo.from.x+1] == 0 )
\r
3246 board[dragInfo.from.y][dragInfo.from.x] = EmptySquare;
\r
3248 if(dragInfo.from.x == BOARD_RGHT+1) {
\r
3249 if(--board[dragInfo.from.y][dragInfo.from.x-1] == 0 )
\r
3250 board[dragInfo.from.y][dragInfo.from.x] = EmptySquare;
\r
3252 board[dragInfo.from.y][dragInfo.from.x] = EmptySquare;
\r
3255 /* Figure out which squares need updating by comparing the
\r
3256 * newest board with the last drawn board and checking if
\r
3257 * flipping has changed.
\r
3259 if (!fullrepaint && lastDrawnValid[nr] && (nr == 1 || lastDrawnFlipView == flipView)) {
\r
3260 for (row = 0; row < BOARD_HEIGHT; row++) { /* [HGM] true size, not 8 */
\r
3261 for (column = 0; column < BOARD_WIDTH; column++) {
\r
3262 if (lastDrawn[nr][row][column] != board[row][column]) {
\r
3263 SquareToPos(row, column, &x, &y);
\r
3264 clips[num_clips++] =
\r
3265 CreateRectRgn(x, y, x + squareSize, y + squareSize);
\r
3269 if(nr == 0) { // [HGM] dual: no highlights on second board
\r
3270 for (i=0; i<2; i++) {
\r
3271 if (lastDrawnHighlight.sq[i].x != highlightInfo.sq[i].x ||
\r
3272 lastDrawnHighlight.sq[i].y != highlightInfo.sq[i].y) {
\r
3273 if (lastDrawnHighlight.sq[i].x >= 0 &&
\r
3274 lastDrawnHighlight.sq[i].y >= 0) {
\r
3275 SquareToPos(lastDrawnHighlight.sq[i].y,
\r
3276 lastDrawnHighlight.sq[i].x, &x, &y);
\r
3277 clips[num_clips++] =
\r
3278 CreateRectRgn(x - lineGap, y - lineGap,
\r
3279 x + squareSize + lineGap, y + squareSize + lineGap);
\r
3281 if (highlightInfo.sq[i].x >= 0 && highlightInfo.sq[i].y >= 0) {
\r
3282 SquareToPos(highlightInfo.sq[i].y, highlightInfo.sq[i].x, &x, &y);
\r
3283 clips[num_clips++] =
\r
3284 CreateRectRgn(x - lineGap, y - lineGap,
\r
3285 x + squareSize + lineGap, y + squareSize + lineGap);
\r
3289 for (i=0; i<2; i++) {
\r
3290 if (lastDrawnPremove.sq[i].x != premoveHighlightInfo.sq[i].x ||
\r
3291 lastDrawnPremove.sq[i].y != premoveHighlightInfo.sq[i].y) {
\r
3292 if (lastDrawnPremove.sq[i].x >= 0 &&
\r
3293 lastDrawnPremove.sq[i].y >= 0) {
\r
3294 SquareToPos(lastDrawnPremove.sq[i].y,
\r
3295 lastDrawnPremove.sq[i].x, &x, &y);
\r
3296 clips[num_clips++] =
\r
3297 CreateRectRgn(x - lineGap, y - lineGap,
\r
3298 x + squareSize + lineGap, y + squareSize + lineGap);
\r
3300 if (premoveHighlightInfo.sq[i].x >= 0 &&
\r
3301 premoveHighlightInfo.sq[i].y >= 0) {
\r
3302 SquareToPos(premoveHighlightInfo.sq[i].y,
\r
3303 premoveHighlightInfo.sq[i].x, &x, &y);
\r
3304 clips[num_clips++] =
\r
3305 CreateRectRgn(x - lineGap, y - lineGap,
\r
3306 x + squareSize + lineGap, y + squareSize + lineGap);
\r
3310 } else { // nr == 1
\r
3311 partnerHighlightInfo.sq[0].y = board[EP_STATUS-4];
\r
3312 partnerHighlightInfo.sq[0].x = board[EP_STATUS-3];
\r
3313 partnerHighlightInfo.sq[1].y = board[EP_STATUS-2];
\r
3314 partnerHighlightInfo.sq[1].x = board[EP_STATUS-1];
\r
3315 for (i=0; i<2; i++) {
\r
3316 if (partnerHighlightInfo.sq[i].x >= 0 &&
\r
3317 partnerHighlightInfo.sq[i].y >= 0) {
\r
3318 SquareToPos(partnerHighlightInfo.sq[i].y,
\r
3319 partnerHighlightInfo.sq[i].x, &x, &y);
\r
3320 clips[num_clips++] =
\r
3321 CreateRectRgn(x - lineGap, y - lineGap,
\r
3322 x + squareSize + lineGap, y + squareSize + lineGap);
\r
3324 if (oldPartnerHighlight.sq[i].x >= 0 &&
\r
3325 oldPartnerHighlight.sq[i].y >= 0) {
\r
3326 SquareToPos(oldPartnerHighlight.sq[i].y,
\r
3327 oldPartnerHighlight.sq[i].x, &x, &y);
\r
3328 clips[num_clips++] =
\r
3329 CreateRectRgn(x - lineGap, y - lineGap,
\r
3330 x + squareSize + lineGap, y + squareSize + lineGap);
\r
3335 fullrepaint = TRUE;
\r
3338 /* Create a buffer bitmap - this is the actual bitmap
\r
3339 * being written to. When all the work is done, we can
\r
3340 * copy it to the real DC (the screen). This avoids
\r
3341 * the problems with flickering.
\r
3343 GetClientRect(hwndMain, &Rect);
\r
3344 bufferBitmap = CreateCompatibleBitmap(hdc, Rect.right-Rect.left+1,
\r
3345 Rect.bottom-Rect.top+1);
\r
3346 oldBitmap = SelectObject(hdcmem, bufferBitmap);
\r
3347 if (!appData.monoMode) {
\r
3348 SelectPalette(hdcmem, hPal, FALSE);
\r
3351 /* Create clips for dragging */
\r
3352 if (!fullrepaint) {
\r
3353 if (dragInfo.from.x >= 0) {
\r
3354 SquareToPos(dragInfo.from.y, dragInfo.from.x, &x, &y);
\r
3355 clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);
\r
3357 if (dragInfo.start.x >= 0) {
\r
3358 SquareToPos(dragInfo.start.y, dragInfo.start.x, &x, &y);
\r
3359 clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);
\r
3361 if (dragInfo.pos.x >= 0) {
\r
3362 x = dragInfo.pos.x - squareSize / 2;
\r
3363 y = dragInfo.pos.y - squareSize / 2;
\r
3364 clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);
\r
3366 if (dragInfo.lastpos.x >= 0) {
\r
3367 x = dragInfo.lastpos.x - squareSize / 2;
\r
3368 y = dragInfo.lastpos.y - squareSize / 2;
\r
3369 clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);
\r
3373 /* Are we animating a move?
\r
3375 * - remove the piece from the board (temporarely)
\r
3376 * - calculate the clipping region
\r
3378 if (!fullrepaint) {
\r
3379 if (animInfo.piece != EmptySquare) {
\r
3380 board[animInfo.from.y][animInfo.from.x] = EmptySquare;
\r
3381 x = boardRect.left + animInfo.lastpos.x;
\r
3382 y = boardRect.top + animInfo.lastpos.y;
\r
3383 x2 = boardRect.left + animInfo.pos.x;
\r
3384 y2 = boardRect.top + animInfo.pos.y;
\r
3385 clips[num_clips++] = CreateRectRgn(MIN(x,x2), MIN(y,y2), MAX(x,x2)+squareSize, MAX(y,y2)+squareSize);
\r
3386 /* Slight kludge. The real problem is that after AnimateMove is
\r
3387 done, the position on the screen does not match lastDrawn.
\r
3388 This currently causes trouble only on e.p. captures in
\r
3389 atomic, where the piece moves to an empty square and then
\r
3390 explodes. The old and new positions both had an empty square
\r
3391 at the destination, but animation has drawn a piece there and
\r
3392 we have to remember to erase it. [HGM] moved until after setting lastDrawn */
\r
3393 lastDrawn[0][animInfo.to.y][animInfo.to.x] = animInfo.piece;
\r
3397 /* No clips? Make sure we have fullrepaint set to TRUE */
\r
3398 if (num_clips == 0)
\r
3399 fullrepaint = TRUE;
\r
3401 /* Set clipping on the memory DC */
\r
3402 if (!fullrepaint) {
\r
3403 SelectClipRgn(hdcmem, clips[0]);
\r
3404 for (x = 1; x < num_clips; x++) {
\r
3405 if (ExtSelectClipRgn(hdcmem, clips[x], RGN_OR) == ERROR)
\r
3406 abort(); // this should never ever happen!
\r
3410 /* Do all the drawing to the memory DC */
\r
3411 if(explodeInfo.radius) { // [HGM] atomic
\r
3413 int x, y, r=(explodeInfo.radius * squareSize)/100;
\r
3414 board[explodeInfo.fromY][explodeInfo.fromX] = EmptySquare; // suppress display of capturer
\r
3415 SquareToPos(explodeInfo.toY, explodeInfo.toX, &x, &y);
\r
3416 x += squareSize/2;
\r
3417 y += squareSize/2;
\r
3418 if(!fullrepaint) {
\r
3419 clips[num_clips] = CreateRectRgn(x-r, y-r, x+r, y+r);
\r
3420 ExtSelectClipRgn(hdcmem, clips[num_clips++], RGN_OR);
\r
3422 DrawGridOnDC(hdcmem);
\r
3423 DrawHighlightsOnDC(hdcmem, &highlightInfo, HIGHLIGHT_PEN);
\r
3424 DrawHighlightsOnDC(hdcmem, &premoveHighlightInfo, PREMOVE_PEN);
\r
3425 DrawBoardOnDC(hdcmem, board, tmphdc);
\r
3426 oldBrush = SelectObject(hdcmem, explodeBrush);
\r
3427 Ellipse(hdcmem, x-r, y-r, x+r, y+r);
\r
3428 SelectObject(hdcmem, oldBrush);
\r
3430 DrawGridOnDC(hdcmem);
\r
3431 if(nr == 0) { // [HGM] dual: decide which highlights to draw
\r
3432 DrawHighlightsOnDC(hdcmem, &highlightInfo, HIGHLIGHT_PEN);
\r
3433 DrawHighlightsOnDC(hdcmem, &premoveHighlightInfo, PREMOVE_PEN);
\r
3435 DrawHighlightsOnDC(hdcmem, &partnerHighlightInfo, HIGHLIGHT_PEN);
\r
3436 oldPartnerHighlight = partnerHighlightInfo;
\r
3438 DrawBoardOnDC(hdcmem, board, tmphdc);
\r
3440 if(nr == 0) // [HGM] dual: markers only on left board
\r
3441 for (row = 0; row < BOARD_HEIGHT; row++) {
\r
3442 for (column = 0; column < BOARD_WIDTH; column++) {
\r
3443 if (marker[row][column]) { // marker changes only occur with full repaint!
\r
3444 HBRUSH oldBrush = SelectObject(hdcmem,
\r
3445 marker[row][column] == 2 ? markerBrush : explodeBrush);
\r
3446 SquareToPos(row, column, &x, &y);
\r
3447 Ellipse(hdcmem, x + squareSize/4, y + squareSize/4,
\r
3448 x + 3*squareSize/4, y + 3*squareSize/4);
\r
3449 SelectObject(hdcmem, oldBrush);
\r
3454 HBITMAP whiteLogo = (HBITMAP) first.programLogo, blackLogo = (HBITMAP) second.programLogo;
\r
3455 if(appData.autoLogo) {
\r
3457 switch(gameMode) { // pick logos based on game mode
\r
3458 case IcsObserving:
\r
3459 whiteLogo = second.programLogo; // ICS logo
\r
3460 blackLogo = second.programLogo;
\r
3463 case IcsPlayingWhite:
\r
3464 if(!appData.zippyPlay) whiteLogo = userLogo;
\r
3465 blackLogo = second.programLogo; // ICS logo
\r
3467 case IcsPlayingBlack:
\r
3468 whiteLogo = second.programLogo; // ICS logo
\r
3469 blackLogo = appData.zippyPlay ? first.programLogo : userLogo;
\r
3471 case TwoMachinesPlay:
\r
3472 if(first.twoMachinesColor[0] == 'b') {
\r
3473 whiteLogo = second.programLogo;
\r
3474 blackLogo = first.programLogo;
\r
3477 case MachinePlaysWhite:
\r
3478 blackLogo = userLogo;
\r
3480 case MachinePlaysBlack:
\r
3481 whiteLogo = userLogo;
\r
3482 blackLogo = first.programLogo;
\r
3485 DrawLogoOnDC(hdc, leftLogoRect, flipClock ? blackLogo : whiteLogo);
\r
3486 DrawLogoOnDC(hdc, rightLogoRect, flipClock ? whiteLogo : blackLogo);
\r
3489 if( appData.highlightMoveWithArrow ) {
\r
3490 DrawArrowHighlight(hdcmem);
\r
3493 DrawCoordsOnDC(hdcmem);
\r
3495 CopyBoard(lastDrawn[nr], board); /* [HGM] Moved to here from end of routine, */
\r
3496 /* to make sure lastDrawn contains what is actually drawn */
\r
3498 /* Put the dragged piece back into place and draw it (out of place!) */
\r
3499 if (dragged_piece != EmptySquare) {
\r
3500 /* [HGM] or restack */
\r
3501 if(dragInfo.from.x == BOARD_LEFT-2 )
\r
3502 board[dragInfo.from.y][dragInfo.from.x+1]++;
\r
3504 if(dragInfo.from.x == BOARD_RGHT+1 )
\r
3505 board[dragInfo.from.y][dragInfo.from.x-1]++;
\r
3506 board[dragInfo.from.y][dragInfo.from.x] = dragged_piece;
\r
3507 x = dragInfo.pos.x - squareSize / 2;
\r
3508 y = dragInfo.pos.y - squareSize / 2;
\r
3509 DrawPieceOnDC(hdcmem, dragged_piece,
\r
3510 ((int) dragged_piece < (int) BlackPawn),
\r
3511 (dragInfo.from.y + dragInfo.from.x) % 2, x, y, tmphdc);
\r
3514 /* Put the animated piece back into place and draw it */
\r
3515 if (animInfo.piece != EmptySquare) {
\r
3516 board[animInfo.from.y][animInfo.from.x] = animInfo.piece;
\r
3517 x = boardRect.left + animInfo.pos.x;
\r
3518 y = boardRect.top + animInfo.pos.y;
\r
3519 DrawPieceOnDC(hdcmem, animInfo.piece,
\r
3520 ((int) animInfo.piece < (int) BlackPawn),
\r
3521 (animInfo.from.y + animInfo.from.x) % 2, x, y, tmphdc);
\r
3524 /* Release the bufferBitmap by selecting in the old bitmap
\r
3525 * and delete the memory DC
\r
3527 SelectObject(hdcmem, oldBitmap);
\r
3530 /* Set clipping on the target DC */
\r
3531 if (!fullrepaint) {
\r
3532 if(nr == 1) for (x = 0; x < num_clips; x++) { // [HGM] dual: translate clips
\r
3534 GetRgnBox(clips[x], &rect);
\r
3535 DeleteObject(clips[x]);
\r
3536 clips[x] = CreateRectRgn(rect.left + wpMain.width/2, rect.top,
\r
3537 rect.right + wpMain.width/2, rect.bottom);
\r
3539 SelectClipRgn(hdc, clips[0]);
\r
3540 for (x = 1; x < num_clips; x++) {
\r
3541 if (ExtSelectClipRgn(hdc, clips[x], RGN_OR) == ERROR)
\r
3542 abort(); // this should never ever happen!
\r
3546 /* Copy the new bitmap onto the screen in one go.
\r
3547 * This way we avoid any flickering
\r
3549 oldBitmap = SelectObject(tmphdc, bufferBitmap);
\r
3550 BitBlt(hdc, boardRect.left + twoBoards*partnerUp*wpMain.width/2, boardRect.top, // [HGM] dual
\r
3551 boardRect.right - boardRect.left,
\r
3552 boardRect.bottom - boardRect.top,
\r
3553 tmphdc, boardRect.left, boardRect.top, SRCCOPY);
\r
3554 if(saveDiagFlag) {
\r
3555 BITMAP b; int i, j=0, m, w, wb, fac=0; char pData[1000000];
\r
3556 BITMAPINFOHEADER bih; int color[16], nrColors=0;
\r
3558 GetObject(bufferBitmap, sizeof(b), &b);
\r
3559 if(b.bmWidthBytes*b.bmHeight <= 990000) {
\r
3560 bih.biSize = sizeof(BITMAPINFOHEADER);
\r
3561 bih.biWidth = b.bmWidth;
\r
3562 bih.biHeight = b.bmHeight;
\r
3564 bih.biBitCount = b.bmBitsPixel;
\r
3565 bih.biCompression = 0;
\r
3566 bih.biSizeImage = b.bmWidthBytes*b.bmHeight;
\r
3567 bih.biXPelsPerMeter = 0;
\r
3568 bih.biYPelsPerMeter = 0;
\r
3569 bih.biClrUsed = 0;
\r
3570 bih.biClrImportant = 0;
\r
3571 // fprintf(diagFile, "t=%d\nw=%d\nh=%d\nB=%d\nP=%d\nX=%d\n",
\r
3572 // b.bmType, b.bmWidth, b.bmHeight, b.bmWidthBytes, b.bmPlanes, b.bmBitsPixel);
\r
3573 GetDIBits(tmphdc,bufferBitmap,0,b.bmHeight,pData,(BITMAPINFO*)&bih,DIB_RGB_COLORS);
\r
3574 // fprintf(diagFile, "%8x\n", (int) pData);
\r
3576 wb = b.bmWidthBytes;
\r
3578 for(i=0; i<wb*(b.bmHeight - boardRect.top + OUTER_MARGIN)>>2; i++) {
\r
3579 int k = ((int*) pData)[i];
\r
3580 for(j=0; j<nrColors; j++) if(color[j] == k) break;
\r
3581 if(j >= 16) break;
\r
3583 if(j >= nrColors) nrColors = j+1;
\r
3585 if(j<16) { // 16 colors is enough. Compress to 4 bits per pixel
\r
3587 for(i=0; i<b.bmHeight - boardRect.top + OUTER_MARGIN; i++) {
\r
3588 for(w=0; w<(wb>>2); w+=2) {
\r
3589 int k = ((int*) pData)[(wb*i>>2) + w];
\r
3590 for(j=0; j<nrColors; j++) if(color[j] == k) break;
\r
3591 k = ((int*) pData)[(wb*i>>2) + w + 1];
\r
3592 for(m=0; m<nrColors; m++) if(color[m] == k) break;
\r
3593 pData[p++] = m | j<<4;
\r
3595 while(p&3) pData[p++] = 0;
\r
3598 wb = ((wb+31)>>5)<<2;
\r
3600 // write BITMAPFILEHEADER
\r
3601 fprintf(diagFile, "BM");
\r
3602 fputDW(diagFile, wb*(b.bmHeight - boardRect.top + OUTER_MARGIN)+0x36 + (fac?64:0));
\r
3603 fputDW(diagFile, 0);
\r
3604 fputDW(diagFile, 0x36 + (fac?64:0));
\r
3605 // write BITMAPINFOHEADER
\r
3606 fputDW(diagFile, 40);
\r
3607 fputDW(diagFile, b.bmWidth);
\r
3608 fputDW(diagFile, b.bmHeight - boardRect.top + OUTER_MARGIN);
\r
3609 if(fac) fputDW(diagFile, 0x040001); // planes and bits/pixel
\r
3610 else fputDW(diagFile, 0x200001); // planes and bits/pixel
\r
3611 fputDW(diagFile, 0);
\r
3612 fputDW(diagFile, 0);
\r
3613 fputDW(diagFile, 0);
\r
3614 fputDW(diagFile, 0);
\r
3615 fputDW(diagFile, 0);
\r
3616 fputDW(diagFile, 0);
\r
3617 // write color table
\r
3619 for(i=0; i<16; i++) fputDW(diagFile, color[i]);
\r
3620 // write bitmap data
\r
3621 for(i=0; i<wb*(b.bmHeight - boardRect.top + OUTER_MARGIN); i++)
\r
3622 fputc(pData[i], diagFile);
\r
3626 SelectObject(tmphdc, oldBitmap);
\r
3628 /* Massive cleanup */
\r
3629 for (x = 0; x < num_clips; x++)
\r
3630 DeleteObject(clips[x]);
\r
3633 DeleteObject(bufferBitmap);
\r
3636 ReleaseDC(hwndMain, hdc);
\r
3638 if (lastDrawnFlipView != flipView && nr == 0) {
\r
3640 CheckMenuItem(GetMenu(hwndMain),IDM_FlipView, MF_BYCOMMAND|MF_CHECKED);
\r
3642 CheckMenuItem(GetMenu(hwndMain),IDM_FlipView, MF_BYCOMMAND|MF_UNCHECKED);
\r
3645 /* CopyBoard(lastDrawn, board);*/
\r
3646 lastDrawnHighlight = highlightInfo;
\r
3647 lastDrawnPremove = premoveHighlightInfo;
\r
3648 lastDrawnFlipView = flipView;
\r
3649 lastDrawnValid[nr] = 1;
\r
3652 /* [HGM] diag: Save the current board display to the given open file and close the file */
\r
3657 saveDiagFlag = 1; diagFile = f;
\r
3658 HDCDrawPosition(NULL, TRUE, NULL);
\r
3662 // if(f != NULL) fprintf(f, "Sorry, but this feature is still in preparation\n");
\r
3669 /*---------------------------------------------------------------------------*\
\r
3670 | CLIENT PAINT PROCEDURE
\r
3671 | This is the main event-handler for the WM_PAINT message.
\r
3673 \*---------------------------------------------------------------------------*/
\r
3675 PaintProc(HWND hwnd)
\r