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
138 typedef struct { // [HGM] atomic
\r
139 int fromX, fromY, toX, toY, radius;
\r
142 static ExplodeInfo explodeInfo;
\r
144 /* Window class names */
\r
145 char szAppName[] = "WinBoard";
\r
146 char szConsoleName[] = "WBConsole";
\r
148 /* Title bar text */
\r
149 char szTitle[] = "WinBoard";
\r
150 char szConsoleTitle[] = "I C S Interaction";
\r
153 char *settingsFileName;
\r
154 Boolean saveSettingsOnExit;
\r
155 char installDir[MSG_SIZ];
\r
156 int errorExitStatus;
\r
158 BoardSize boardSize;
\r
159 Boolean chessProgram;
\r
160 //static int boardX, boardY;
\r
161 int minX, minY; // [HGM] placement: volatile limits on upper-left corner
\r
162 int squareSize, lineGap, minorSize;
\r
163 static int winW, winH;
\r
164 static RECT messageRect, whiteRect, blackRect, leftLogoRect, rightLogoRect; // [HGM] logo
\r
165 static int logoHeight = 0;
\r
166 static char messageText[MESSAGE_TEXT_MAX];
\r
167 static int clockTimerEvent = 0;
\r
168 static int loadGameTimerEvent = 0;
\r
169 static int analysisTimerEvent = 0;
\r
170 static DelayedEventCallback delayedTimerCallback;
\r
171 static int delayedTimerEvent = 0;
\r
172 static int buttonCount = 2;
\r
173 char *icsTextMenuString;
\r
175 char *firstChessProgramNames;
\r
176 char *secondChessProgramNames;
\r
178 #define PALETTESIZE 256
\r
180 HINSTANCE hInst; /* current instance */
\r
181 Boolean alwaysOnTop = FALSE;
\r
183 COLORREF lightSquareColor, darkSquareColor, whitePieceColor,
\r
184 blackPieceColor, highlightSquareColor, premoveHighlightColor;
\r
186 ColorClass currentColorClass;
\r
188 HWND hCommPort = NULL; /* currently open comm port */
\r
189 static HWND hwndPause; /* pause button */
\r
190 static HBITMAP pieceBitmap[3][(int) BlackPawn]; /* [HGM] nr of bitmaps referred to bP in stead of wK */
\r
191 static HBRUSH lightSquareBrush, darkSquareBrush,
\r
192 blackSquareBrush, /* [HGM] for band between board and holdings */
\r
193 explodeBrush, /* [HGM] atomic */
\r
194 markerBrush, /* [HGM] markers */
\r
195 whitePieceBrush, blackPieceBrush, iconBkgndBrush /*, outlineBrush*/;
\r
196 static POINT gridEndpoints[(BOARD_RANKS + BOARD_FILES + 2) * 2];
\r
197 static DWORD gridVertexCounts[BOARD_RANKS + BOARD_FILES + 2];
\r
198 static HPEN gridPen = NULL;
\r
199 static HPEN highlightPen = NULL;
\r
200 static HPEN premovePen = NULL;
\r
201 static NPLOGPALETTE pLogPal;
\r
202 static BOOL paletteChanged = FALSE;
\r
203 static HICON iconWhite, iconBlack, iconCurrent;
\r
204 static int doingSizing = FALSE;
\r
205 static int lastSizing = 0;
\r
206 static int prevStderrPort;
\r
207 static HBITMAP userLogo;
\r
209 static HBITMAP liteBackTexture = NULL;
\r
210 static HBITMAP darkBackTexture = NULL;
\r
211 static int liteBackTextureMode = BACK_TEXTURE_MODE_PLAIN;
\r
212 static int darkBackTextureMode = BACK_TEXTURE_MODE_PLAIN;
\r
213 static int backTextureSquareSize = 0;
\r
214 static struct { int x; int y; int mode; } backTextureSquareInfo[BOARD_RANKS][BOARD_FILES];
\r
216 #if __GNUC__ && !defined(_winmajor)
\r
217 #define oldDialog 0 /* cygwin doesn't define _winmajor; mingw does */
\r
219 #if defined(_winmajor)
\r
220 #define oldDialog (_winmajor < 4)
\r
222 #define oldDialog 0
\r
232 int cliWidth, cliHeight;
\r
235 SizeInfo sizeInfo[] =
\r
237 { "tiny", 21, 0, 1, 1, 0, 0 },
\r
238 { "teeny", 25, 1, 1, 1, 0, 0 },
\r
239 { "dinky", 29, 1, 1, 1, 0, 0 },
\r
240 { "petite", 33, 1, 1, 1, 0, 0 },
\r
241 { "slim", 37, 2, 1, 0, 0, 0 },
\r
242 { "small", 40, 2, 1, 0, 0, 0 },
\r
243 { "mediocre", 45, 2, 1, 0, 0, 0 },
\r
244 { "middling", 49, 2, 0, 0, 0, 0 },
\r
245 { "average", 54, 2, 0, 0, 0, 0 },
\r
246 { "moderate", 58, 3, 0, 0, 0, 0 },
\r
247 { "medium", 64, 3, 0, 0, 0, 0 },
\r
248 { "bulky", 72, 3, 0, 0, 0, 0 },
\r
249 { "large", 80, 3, 0, 0, 0, 0 },
\r
250 { "big", 87, 3, 0, 0, 0, 0 },
\r
251 { "huge", 95, 3, 0, 0, 0, 0 },
\r
252 { "giant", 108, 3, 0, 0, 0, 0 },
\r
253 { "colossal", 116, 4, 0, 0, 0, 0 },
\r
254 { "titanic", 129, 4, 0, 0, 0, 0 },
\r
255 { NULL, 0, 0, 0, 0, 0, 0 }
\r
258 #define MF(x) {x, {{0,}, 0. }, {0, }, 0}
\r
259 MyFont fontRec[NUM_SIZES][NUM_FONTS] =
\r
261 { 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
262 { 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
263 { 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
264 { 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
265 { 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
266 { 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
267 { 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
268 { 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
269 { 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
270 { 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
271 { 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
272 { 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
273 { 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
274 { 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
275 { 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
276 { 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
277 { 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
278 { 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
281 MyFont *font[NUM_SIZES][NUM_FONTS];
\r
290 #define BUTTON_WIDTH (tinyLayout ? 16 : 32)
\r
291 #define N_BUTTONS 5
\r
293 MyButtonDesc buttonDesc[N_BUTTONS] =
\r
295 {"<<", IDM_ToStart, NULL, NULL},
\r
296 {"<", IDM_Backward, NULL, NULL},
\r
297 {"P", IDM_Pause, NULL, NULL},
\r
298 {">", IDM_Forward, NULL, NULL},
\r
299 {">>", IDM_ToEnd, NULL, NULL},
\r
302 int tinyLayout = 0, smallLayout = 0;
\r
303 #define MENU_BAR_ITEMS 7
\r
304 char *menuBarText[2][MENU_BAR_ITEMS+1] = {
\r
305 { "&File", "&Mode", "&Action", "&Step", "&Options", "&Help", NULL },
\r
306 { "&F", "&M", "&A", "&S", "&O", "&H", NULL },
\r
310 MySound sounds[(int)NSoundClasses];
\r
311 MyTextAttribs textAttribs[(int)NColorClasses];
\r
313 MyColorizeAttribs colorizeAttribs[] = {
\r
314 { (COLORREF)0, 0, "Shout Text" },
\r
315 { (COLORREF)0, 0, "SShout/CShout" },
\r
316 { (COLORREF)0, 0, "Channel 1 Text" },
\r
317 { (COLORREF)0, 0, "Channel Text" },
\r
318 { (COLORREF)0, 0, "Kibitz Text" },
\r
319 { (COLORREF)0, 0, "Tell Text" },
\r
320 { (COLORREF)0, 0, "Challenge Text" },
\r
321 { (COLORREF)0, 0, "Request Text" },
\r
322 { (COLORREF)0, 0, "Seek Text" },
\r
323 { (COLORREF)0, 0, "Normal Text" },
\r
324 { (COLORREF)0, 0, "None" }
\r
329 static char *commentTitle;
\r
330 static char *commentText;
\r
331 static int commentIndex;
\r
332 static Boolean editComment = FALSE;
\r
335 char errorTitle[MSG_SIZ];
\r
336 char errorMessage[2*MSG_SIZ];
\r
337 HWND errorDialog = NULL;
\r
338 BOOLEAN moveErrorMessageUp = FALSE;
\r
339 BOOLEAN consoleEcho = TRUE;
\r
340 CHARFORMAT consoleCF;
\r
341 COLORREF consoleBackgroundColor;
\r
343 char *programVersion;
\r
349 typedef int CPKind;
\r
358 SOCKET sock2; /* stderr socket for OpenRcmd */
\r
361 #define INPUT_SOURCE_BUF_SIZE 4096
\r
363 typedef struct _InputSource {
\r
370 char buf[INPUT_SOURCE_BUF_SIZE];
\r
374 InputCallback func;
\r
375 struct _InputSource *second; /* for stderr thread on CPRcmd */
\r
379 InputSource *consoleInputSource;
\r
384 VOID ConsoleOutput(char* data, int length, int forceVisible);
\r
385 VOID ConsoleCreate();
\r
387 ConsoleWndProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam);
\r
388 VOID ColorizeTextPopup(HWND hwnd, ColorClass cc);
\r
389 VOID PrintCommSettings(FILE *f, char *name, DCB *dcb);
\r
390 VOID ParseCommSettings(char *arg, DCB *dcb);
\r
392 StartupDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam);
\r
393 VOID APIENTRY MenuPopup(HWND hwnd, POINT pt, HMENU hmenu, UINT def);
\r
394 void ParseIcsTextMenu(char *icsTextMenuString);
\r
395 VOID PopUpMoveDialog(char firstchar);
\r
396 VOID PopUpNameDialog(char firstchar);
\r
397 VOID UpdateSampleText(HWND hDlg, int id, MyColorizeAttribs *mca);
\r
401 int GameListOptions();
\r
403 int dummy; // [HGM] for obsolete args
\r
405 HWND hwndMain = NULL; /* root window*/
\r
406 HWND hwndConsole = NULL;
\r
407 HWND commentDialog = NULL;
\r
408 HWND moveHistoryDialog = NULL;
\r
409 HWND evalGraphDialog = NULL;
\r
410 HWND engineOutputDialog = NULL;
\r
411 HWND gameListDialog = NULL;
\r
412 HWND editTagsDialog = NULL;
\r
414 int commentUp = FALSE;
\r
416 WindowPlacement wpMain;
\r
417 WindowPlacement wpConsole;
\r
418 WindowPlacement wpComment;
\r
419 WindowPlacement wpMoveHistory;
\r
420 WindowPlacement wpEvalGraph;
\r
421 WindowPlacement wpEngineOutput;
\r
422 WindowPlacement wpGameList;
\r
423 WindowPlacement wpTags;
\r
425 VOID EngineOptionsPopup(); // [HGM] settings
\r
427 VOID GothicPopUp(char *title, VariantClass variant);
\r
429 * Setting "frozen" should disable all user input other than deleting
\r
430 * the window. We do this while engines are initializing themselves.
\r
432 static int frozen = 0;
\r
433 static int oldMenuItemState[MENU_BAR_ITEMS];
\r
439 if (frozen) return;
\r
441 hmenu = GetMenu(hwndMain);
\r
442 for (i=0; i<MENU_BAR_ITEMS; i++) {
\r
443 oldMenuItemState[i] = EnableMenuItem(hmenu, i, MF_BYPOSITION|MF_GRAYED);
\r
445 DrawMenuBar(hwndMain);
\r
448 /* Undo a FreezeUI */
\r
454 if (!frozen) return;
\r
456 hmenu = GetMenu(hwndMain);
\r
457 for (i=0; i<MENU_BAR_ITEMS; i++) {
\r
458 EnableMenuItem(hmenu, i, MF_BYPOSITION|oldMenuItemState[i]);
\r
460 DrawMenuBar(hwndMain);
\r
463 /*static*/ int fromX = -1, fromY = -1, toX, toY; // [HGM] moved upstream, so JAWS can use them
\r
465 /* JAWS preparation patch (WinBoard for the sight impaired). Define required insertions as empty */
\r
471 #define JAWS_ALT_INTERCEPT
\r
472 #define JAWS_KB_NAVIGATION
\r
473 #define JAWS_MENU_ITEMS
\r
474 #define JAWS_SILENCE
\r
475 #define JAWS_REPLAY
\r
477 #define JAWS_COPYRIGHT
\r
478 #define JAWS_DELETE(X) X
\r
479 #define SAYMACHINEMOVE()
\r
483 /*---------------------------------------------------------------------------*\
\r
487 \*---------------------------------------------------------------------------*/
\r
490 WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
\r
491 LPSTR lpCmdLine, int nCmdShow)
\r
494 HANDLE hAccelMain, hAccelNoAlt, hAccelNoICS;
\r
495 // INITCOMMONCONTROLSEX ex;
\r
499 LoadLibrary("RICHED32.DLL");
\r
500 consoleCF.cbSize = sizeof(CHARFORMAT);
\r
502 if (!InitApplication(hInstance)) {
\r
505 if (!InitInstance(hInstance, nCmdShow, lpCmdLine)) {
\r
511 // InitCommonControlsEx(&ex);
\r
512 InitCommonControls();
\r
514 hAccelMain = LoadAccelerators (hInstance, szAppName);
\r
515 hAccelNoAlt = LoadAccelerators (hInstance, "NO_ALT");
\r
516 hAccelNoICS = LoadAccelerators( hInstance, "NO_ICS"); /* [AS] No Ctrl-V on ICS!!! */
\r
518 /* Acquire and dispatch messages until a WM_QUIT message is received. */
\r
520 while (GetMessage(&msg, /* message structure */
\r
521 NULL, /* handle of window receiving the message */
\r
522 0, /* lowest message to examine */
\r
523 0)) /* highest message to examine */
\r
526 if(msg.message == WM_CHAR && msg.wParam == '\t') {
\r
527 // [HGM] navigate: switch between all windows with tab
\r
528 HWND e1 = NULL, e2 = NULL, mh = NULL, hInput = NULL, hText = NULL;
\r
529 int i, currentElement = 0;
\r
531 // first determine what element of the chain we come from (if any)
\r
532 if(appData.icsActive) {
\r
533 hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
534 hText = GetDlgItem(hwndConsole, OPT_ConsoleText);
\r
536 if(engineOutputDialog && EngineOutputIsUp()) {
\r
537 e1 = GetDlgItem(engineOutputDialog, IDC_EngineMemo1);
\r
538 e2 = GetDlgItem(engineOutputDialog, IDC_EngineMemo2);
\r
540 if(moveHistoryDialog && MoveHistoryIsUp()) {
\r
541 mh = GetDlgItem(moveHistoryDialog, IDC_MoveHistory);
\r
543 if(msg.hwnd == hwndMain) currentElement = 7 ; else
\r
544 if(msg.hwnd == engineOutputDialog) currentElement = 2; else
\r
545 if(msg.hwnd == e1) currentElement = 2; else
\r
546 if(msg.hwnd == e2) currentElement = 3; else
\r
547 if(msg.hwnd == moveHistoryDialog) currentElement = 4; else
\r
548 if(msg.hwnd == mh) currentElement = 4; else
\r
549 if(msg.hwnd == evalGraphDialog) currentElement = 6; else
\r
550 if(msg.hwnd == hText) currentElement = 5; else
\r
551 if(msg.hwnd == hInput) currentElement = 6; else
\r
552 for (i = 0; i < N_BUTTONS; i++) {
\r
553 if (buttonDesc[i].hwnd == msg.hwnd) { currentElement = 1; break; }
\r
556 // determine where to go to
\r
557 if(currentElement) { HWND h = NULL; int direction = GetKeyState(VK_SHIFT) < 0 ? -1 : 1;
\r
559 currentElement = (currentElement + direction) % 7;
\r
560 switch(currentElement) {
\r
562 h = hwndMain; break; // passing this case always makes the loop exit
\r
564 h = buttonDesc[0].hwnd; break; // could be NULL
\r
566 if(!EngineOutputIsUp()) continue; // skip closed auxiliary windows
\r
569 if(!EngineOutputIsUp()) continue;
\r
572 if(!MoveHistoryIsUp()) continue;
\r
574 // case 6: // input to eval graph does not seem to get here!
\r
575 // if(!EvalGraphIsUp()) continue;
\r
576 // h = evalGraphDialog; break;
\r
578 if(!appData.icsActive) continue;
\r
582 if(!appData.icsActive) continue;
\r
588 if(currentElement > 4 && IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);
\r
589 if(currentElement < 5 && IsIconic(hwndMain)) ShowWindow(hwndMain, SW_RESTORE); // all open together
\r
592 continue; // this message now has been processed
\r
596 if (!(commentDialog && IsDialogMessage(commentDialog, &msg)) &&
\r
597 !(moveHistoryDialog && IsDialogMessage(moveHistoryDialog, &msg)) &&
\r
598 !(evalGraphDialog && IsDialogMessage(evalGraphDialog, &msg)) &&
\r
599 !(engineOutputDialog && IsDialogMessage(engineOutputDialog, &msg)) &&
\r
600 !(editTagsDialog && IsDialogMessage(editTagsDialog, &msg)) &&
\r
601 !(gameListDialog && IsDialogMessage(gameListDialog, &msg)) &&
\r
602 !(errorDialog && IsDialogMessage(errorDialog, &msg)) &&
\r
603 !(!frozen && TranslateAccelerator(hwndMain, hAccelMain, &msg)) && JAWS_ACCEL
\r
604 !(!hwndConsole && TranslateAccelerator(hwndMain, hAccelNoICS, &msg)) &&
\r
605 !(!hwndConsole && TranslateAccelerator(hwndMain, hAccelNoAlt, &msg))) {
\r
606 int done = 0, i; // [HGM] chat: dispatch cat-box messages
\r
607 for(i=0; i<MAX_CHAT; i++)
\r
608 if(chatHandle[i] && IsDialogMessage(chatHandle[i], &msg)) {
\r
611 if(done) continue; // [HGM] chat: end patch
\r
612 TranslateMessage(&msg); /* Translates virtual key codes */
\r
613 DispatchMessage(&msg); /* Dispatches message to window */
\r
618 return (msg.wParam); /* Returns the value from PostQuitMessage */
\r
621 /*---------------------------------------------------------------------------*\
\r
623 * Initialization functions
\r
625 \*---------------------------------------------------------------------------*/
\r
629 { // update user logo if necessary
\r
630 static char oldUserName[MSG_SIZ], *curName;
\r
632 if(appData.autoLogo) {
\r
633 curName = UserName();
\r
634 if(strcmp(curName, oldUserName)) {
\r
635 sprintf(oldUserName, "logos\\%s.bmp", curName);
\r
636 userLogo = LoadImage( 0, oldUserName, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );
\r
637 strcpy(oldUserName, curName);
\r
643 InitApplication(HINSTANCE hInstance)
\r
647 /* Fill in window class structure with parameters that describe the */
\r
650 wc.style = CS_HREDRAW | CS_VREDRAW; /* Class style(s). */
\r
651 wc.lpfnWndProc = (WNDPROC)WndProc; /* Window Procedure */
\r
652 wc.cbClsExtra = 0; /* No per-class extra data. */
\r
653 wc.cbWndExtra = 0; /* No per-window extra data. */
\r
654 wc.hInstance = hInstance; /* Owner of this class */
\r
655 wc.hIcon = LoadIcon(hInstance, "icon_white");
\r
656 wc.hCursor = LoadCursor(NULL, IDC_ARROW); /* Cursor */
\r
657 wc.hbrBackground = (HBRUSH)(COLOR_WINDOW+1); /* Default color */
\r
658 wc.lpszMenuName = szAppName; /* Menu name from .RC */
\r
659 wc.lpszClassName = szAppName; /* Name to register as */
\r
661 /* Register the window class and return success/failure code. */
\r
662 if (!RegisterClass(&wc)) return FALSE;
\r
664 wc.style = CS_HREDRAW | CS_VREDRAW;
\r
665 wc.lpfnWndProc = (WNDPROC)ConsoleWndProc;
\r
667 wc.cbWndExtra = DLGWINDOWEXTRA;
\r
668 wc.hInstance = hInstance;
\r
669 wc.hIcon = LoadIcon(hInstance, "icon_white");
\r
670 wc.hCursor = LoadCursor(NULL, IDC_ARROW);
\r
671 wc.hbrBackground = (HBRUSH)(COLOR_MENU+1);
\r
672 wc.lpszMenuName = NULL;
\r
673 wc.lpszClassName = szConsoleName;
\r
675 if (!RegisterClass(&wc)) return FALSE;
\r
680 /* Set by InitInstance, used by EnsureOnScreen */
\r
681 int screenHeight, screenWidth;
\r
684 EnsureOnScreen(int *x, int *y, int minX, int minY)
\r
686 // int gap = GetSystemMetrics(SM_CYFRAME) + GetSystemMetrics(SM_CYCAPTION);
\r
687 /* Be sure window at (x,y) is not off screen (or even mostly off screen) */
\r
688 if (*x > screenWidth - 32) *x = 0;
\r
689 if (*y > screenHeight - 32) *y = 0;
\r
690 if (*x < minX) *x = minX;
\r
691 if (*y < minY) *y = minY;
\r
695 InitInstance(HINSTANCE hInstance, int nCmdShow, LPSTR lpCmdLine)
\r
697 HWND hwnd; /* Main window handle. */
\r
699 WINDOWPLACEMENT wp;
\r
702 hInst = hInstance; /* Store instance handle in our global variable */
\r
703 programName = szAppName;
\r
705 if (SearchPath(NULL, "WinBoard.exe", NULL, MSG_SIZ, installDir, &filepart)) {
\r
706 *filepart = NULLCHAR;
\r
708 GetCurrentDirectory(MSG_SIZ, installDir);
\r
710 gameInfo.boardWidth = gameInfo.boardHeight = 8; // [HGM] won't have open window otherwise
\r
711 screenWidth = screenHeight = 1000; // [HGM] placement: kludge to allow calling EnsureOnScreen from InitAppData
\r
712 InitAppData(lpCmdLine); /* Get run-time parameters */
\r
713 /* xboard, and older WinBoards, controlled the move sound with the
\r
714 appData.ringBellAfterMoves option. In the current WinBoard, we
\r
715 always turn the option on (so that the backend will call us),
\r
716 then let the user turn the sound off by setting it to silence if
\r
717 desired. To accommodate old winboard.ini files saved by old
\r
718 versions of WinBoard, we also turn off the sound if the option
\r
719 was initially set to false. [HGM] taken out of InitAppData */
\r
720 if (!appData.ringBellAfterMoves) {
\r
721 sounds[(int)SoundMove].name = strdup("");
\r
722 appData.ringBellAfterMoves = TRUE;
\r
724 if (appData.debugMode) {
\r
725 debugFP = fopen(appData.nameOfDebugFile, "w");
\r
726 setbuf(debugFP, NULL);
\r
731 // InitEngineUCI( installDir, &first ); // [HGM] incorporated in InitBackEnd1()
\r
732 // InitEngineUCI( installDir, &second );
\r
734 /* Create a main window for this application instance. */
\r
735 hwnd = CreateWindow(szAppName, szTitle,
\r
736 (WS_OVERLAPPEDWINDOW & ~WS_MAXIMIZEBOX),
\r
737 CW_USEDEFAULT, 0, CW_USEDEFAULT, 0,
\r
738 NULL, NULL, hInstance, NULL);
\r
741 /* If window could not be created, return "failure" */
\r
746 /* [HGM] logo: Load logos if specified (must be done before InitDrawingSizes) */
\r
747 if( appData.firstLogo && appData.firstLogo[0] != NULLCHAR) {
\r
748 first.programLogo = LoadImage( 0, appData.firstLogo, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );
\r
750 if (first.programLogo == NULL && appData.debugMode) {
\r
751 fprintf( debugFP, "Unable to load logo bitmap '%s'\n", appData.firstLogo );
\r
753 } else if(appData.autoLogo) {
\r
754 if(appData.firstDirectory && appData.firstDirectory[0]) {
\r
756 sprintf(buf, "%s/logo.bmp", appData.firstDirectory);
\r
757 first.programLogo = LoadImage( 0, buf, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );
\r
761 if( appData.secondLogo && appData.secondLogo[0] != NULLCHAR) {
\r
762 second.programLogo = LoadImage( 0, appData.secondLogo, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );
\r
764 if (second.programLogo == NULL && appData.debugMode) {
\r
765 fprintf( debugFP, "Unable to load logo bitmap '%s'\n", appData.secondLogo );
\r
767 } else if(appData.autoLogo) {
\r
769 if(appData.icsActive) { // [HGM] logo: in ICS mode second can be used for ICS
\r
770 sprintf(buf, "logos\\%s.bmp", appData.icsHost);
\r
771 second.programLogo = LoadImage( 0, buf, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );
\r
773 if(appData.secondDirectory && appData.secondDirectory[0]) {
\r
774 sprintf(buf, "%s\\logo.bmp", appData.secondDirectory);
\r
775 second.programLogo = LoadImage( 0, buf, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );
\r
781 iconWhite = LoadIcon(hInstance, "icon_white");
\r
782 iconBlack = LoadIcon(hInstance, "icon_black");
\r
783 iconCurrent = iconWhite;
\r
784 InitDrawingColors();
\r
785 screenHeight = GetSystemMetrics(SM_CYSCREEN);
\r
786 screenWidth = GetSystemMetrics(SM_CXSCREEN);
\r
787 for (ibs = (int) NUM_SIZES - 1; ibs >= 0; ibs--) {
\r
788 /* Compute window size for each board size, and use the largest
\r
789 size that fits on this screen as the default. */
\r
790 InitDrawingSizes((BoardSize)(ibs+1000), 0);
\r
791 if (boardSize == (BoardSize)-1 &&
\r
792 winH <= screenHeight
\r
793 - GetSystemMetrics(SM_CYFRAME) - GetSystemMetrics(SM_CYCAPTION) - 10
\r
794 && winW <= screenWidth) {
\r
795 boardSize = (BoardSize)ibs;
\r
799 InitDrawingSizes(boardSize, 0);
\r
801 buttonCount = GetSystemMetrics(SM_CMOUSEBUTTONS);
\r
803 /* [AS] Load textures if specified */
\r
804 ZeroMemory( &backTextureSquareInfo, sizeof(backTextureSquareInfo) );
\r
806 if( appData.liteBackTextureFile && appData.liteBackTextureFile[0] != NULLCHAR && appData.liteBackTextureFile[0] != '*' ) {
\r
807 liteBackTexture = LoadImage( 0, appData.liteBackTextureFile, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );
\r
808 liteBackTextureMode = appData.liteBackTextureMode;
\r
810 if (liteBackTexture == NULL && appData.debugMode) {
\r
811 fprintf( debugFP, "Unable to load lite texture bitmap '%s'\n", appData.liteBackTextureFile );
\r
815 if( appData.darkBackTextureFile && appData.darkBackTextureFile[0] != NULLCHAR && appData.darkBackTextureFile[0] != '*' ) {
\r
816 darkBackTexture = LoadImage( 0, appData.darkBackTextureFile, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );
\r
817 darkBackTextureMode = appData.darkBackTextureMode;
\r
819 if (darkBackTexture == NULL && appData.debugMode) {
\r
820 fprintf( debugFP, "Unable to load dark texture bitmap '%s'\n", appData.darkBackTextureFile );
\r
824 mysrandom( (unsigned) time(NULL) );
\r
826 /* [AS] Restore layout */
\r
827 if( wpMoveHistory.visible ) {
\r
828 MoveHistoryPopUp();
\r
831 if( wpEvalGraph.visible ) {
\r
835 if( wpEngineOutput.visible ) {
\r
836 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 if(!appData.noGUI) SetWindowPos(hwndMain, alwaysOnTop ? HWND_TOPMOST : HWND_NOTOPMOST,
\r
854 0, 0, 0, 0, SWP_NOMOVE|SWP_NOSIZE);
\r
858 SetWindowPos(hwndConsole, alwaysOnTop ? HWND_TOPMOST : HWND_NOTOPMOST,
\r
859 0, 0, 0, 0, SWP_NOMOVE|SWP_NOSIZE);
\r
861 ShowWindow(hwndConsole, nCmdShow);
\r
862 if(appData.chatBoxes) { // [HGM] chat: open chat boxes
\r
863 char buf[MSG_SIZ], *p = buf, *q;
\r
864 strcpy(buf, appData.chatBoxes);
\r
866 q = strchr(p, ';');
\r
868 if(*p) ChatPopUp(p);
\r
872 SetActiveWindow(hwndConsole);
\r
874 if(!appData.noGUI) UpdateWindow(hwnd); else ShowWindow(hwnd, SW_MINIMIZE);
\r
875 if(gameListDialog) SetFocus(gameListDialog); // [HGM] jaws: for if we clicked multi-game game file
\r
884 HMENU hmenu = GetMenu(hwndMain);
\r
886 (void) EnableMenuItem(hmenu, IDM_CommPort,
\r
887 MF_BYCOMMAND|((appData.icsActive &&
\r
888 *appData.icsCommPort != NULLCHAR) ?
\r
889 MF_ENABLED : MF_GRAYED));
\r
890 (void) CheckMenuItem(hmenu, IDM_SaveSettingsOnExit,
\r
891 MF_BYCOMMAND|(saveSettingsOnExit ?
\r
892 MF_CHECKED : MF_UNCHECKED));
\r
895 //---------------------------------------------------------------------------------------------------------
\r
897 #define ICS_TEXT_MENU_SIZE (IDM_CommandXLast - IDM_CommandX + 1)
\r
898 #define XBOARD FALSE
\r
900 #define OPTCHAR "/"
\r
901 #define SEPCHAR "="
\r
905 // front-end part of option handling
\r
908 LFfromMFP(LOGFONT* lf, MyFontParams *mfp)
\r
910 HDC hdc = CreateDC("DISPLAY", NULL, NULL, NULL);
\r
911 lf->lfHeight = -(int)(mfp->pointSize * GetDeviceCaps(hdc, LOGPIXELSY) / 72.0 + 0.5);
\r
914 lf->lfEscapement = 0;
\r
915 lf->lfOrientation = 0;
\r
916 lf->lfWeight = mfp->bold ? FW_BOLD : FW_NORMAL;
\r
917 lf->lfItalic = mfp->italic;
\r
918 lf->lfUnderline = mfp->underline;
\r
919 lf->lfStrikeOut = mfp->strikeout;
\r
920 lf->lfCharSet = mfp->charset;
\r
921 lf->lfOutPrecision = OUT_DEFAULT_PRECIS;
\r
922 lf->lfClipPrecision = CLIP_DEFAULT_PRECIS;
\r
923 lf->lfQuality = DEFAULT_QUALITY;
\r
924 lf->lfPitchAndFamily = DEFAULT_PITCH|FF_DONTCARE;
\r
925 strcpy(lf->lfFaceName, mfp->faceName);
\r
929 CreateFontInMF(MyFont *mf)
\r
931 LFfromMFP(&mf->lf, &mf->mfp);
\r
932 if (mf->hf) DeleteObject(mf->hf);
\r
933 mf->hf = CreateFontIndirect(&mf->lf);
\r
936 // [HGM] This platform-dependent table provides the location for storing the color info
\r
938 colorVariable[] = {
\r
943 &highlightSquareColor,
\r
944 &premoveHighlightColor,
\r
946 &consoleBackgroundColor,
\r
947 &appData.fontForeColorWhite,
\r
948 &appData.fontBackColorWhite,
\r
949 &appData.fontForeColorBlack,
\r
950 &appData.fontBackColorBlack,
\r
951 &appData.evalHistColorWhite,
\r
952 &appData.evalHistColorBlack,
\r
953 &appData.highlightArrowColor,
\r
956 /* Command line font name parser. NULL name means do nothing.
\r
957 Syntax like "Courier New:10.0 bi" or "Arial:10" or "Arial:10b"
\r
958 For backward compatibility, syntax without the colon is also
\r
959 accepted, but font names with digits in them won't work in that case.
\r
962 ParseFontName(char *name, MyFontParams *mfp)
\r
965 if (name == NULL) return;
\r
967 q = strchr(p, ':');
\r
969 if (q - p >= sizeof(mfp->faceName))
\r
970 ExitArgError("Font name too long:", name);
\r
971 memcpy(mfp->faceName, p, q - p);
\r
972 mfp->faceName[q - p] = NULLCHAR;
\r
976 while (*p && !isdigit(*p)) {
\r
978 if (q - mfp->faceName >= sizeof(mfp->faceName))
\r
979 ExitArgError("Font name too long:", name);
\r
981 while (q > mfp->faceName && q[-1] == ' ') q--;
\r
984 if (!*p) ExitArgError("Font point size missing:", name);
\r
985 mfp->pointSize = (float) atof(p);
\r
986 mfp->bold = (strchr(p, 'b') != NULL);
\r
987 mfp->italic = (strchr(p, 'i') != NULL);
\r
988 mfp->underline = (strchr(p, 'u') != NULL);
\r
989 mfp->strikeout = (strchr(p, 's') != NULL);
\r
990 mfp->charset = DEFAULT_CHARSET;
\r
991 q = strchr(p, 'c');
\r
993 mfp->charset = (BYTE) atoi(q+1);
\r
997 ParseFont(char *name, int number)
\r
998 { // wrapper to shield back-end from 'font'
\r
999 ParseFontName(name, &font[boardSize][number]->mfp);
\r
1004 { // in WB we have a 2D array of fonts; this initializes their description
\r
1006 /* Point font array elements to structures and
\r
1007 parse default font names */
\r
1008 for (i=0; i<NUM_FONTS; i++) {
\r
1009 for (j=0; j<NUM_SIZES; j++) {
\r
1010 font[j][i] = &fontRec[j][i];
\r
1011 ParseFontName(font[j][i]->def, &font[j][i]->mfp);
\r
1018 { // here we create the actual fonts from the selected descriptions
\r
1020 for (i=0; i<NUM_FONTS; i++) {
\r
1021 for (j=0; j<NUM_SIZES; j++) {
\r
1022 CreateFontInMF(font[j][i]);
\r
1026 /* Color name parser.
\r
1027 X version accepts X color names, but this one
\r
1028 handles only the #rrggbb form (hex) or rrr,ggg,bbb (decimal) */
\r
1030 ParseColorName(char *name)
\r
1032 int red, green, blue, count;
\r
1033 char buf[MSG_SIZ];
\r
1035 count = sscanf(name, "#%2x%2x%2x", &red, &green, &blue);
\r
1037 count = sscanf(name, "%3d%*[^0-9]%3d%*[^0-9]%3d",
\r
1038 &red, &green, &blue);
\r
1041 sprintf(buf, "Can't parse color name %s", name);
\r
1042 DisplayError(buf, 0);
\r
1043 return RGB(0, 0, 0);
\r
1045 return PALETTERGB(red, green, blue);
\r
1049 ParseColor(int n, char *name)
\r
1050 { // for WinBoard the color is an int, which needs to be derived from the string
\r
1051 if(colorVariable[n]) *(int*)colorVariable[n] = ParseColorName(name);
\r
1055 ParseAttribs(COLORREF *color, int *effects, char* argValue)
\r
1057 char *e = argValue;
\r
1061 if (*e == 'b') eff |= CFE_BOLD;
\r
1062 else if (*e == 'i') eff |= CFE_ITALIC;
\r
1063 else if (*e == 'u') eff |= CFE_UNDERLINE;
\r
1064 else if (*e == 's') eff |= CFE_STRIKEOUT;
\r
1065 else if (*e == '#' || isdigit(*e)) break;
\r
1069 *color = ParseColorName(e);
\r
1073 ParseTextAttribs(ColorClass cc, char *s)
\r
1074 { // [HGM] front-end wrapper that does the platform-dependent call
\r
1075 // for XBoard we would set (&appData.colorShout)[cc] = strdup(s);
\r
1076 ParseAttribs(&textAttribs[cc].color, &textAttribs[cc].effects, s);
\r
1080 ParseBoardSize(void *addr, char *name)
\r
1081 { // [HGM] rewritten with return-value ptr to shield back-end from BoardSize
\r
1082 BoardSize bs = SizeTiny;
\r
1083 while (sizeInfo[bs].name != NULL) {
\r
1084 if (StrCaseCmp(name, sizeInfo[bs].name) == 0) {
\r
1085 *(BoardSize *)addr = bs;
\r
1090 ExitArgError("Unrecognized board size value", name);
\r
1095 { // [HGM] import name from appData first
\r
1098 for (cc = (ColorClass)0; cc < ColorNormal; cc++) {
\r
1099 textAttribs[cc].sound.name = strdup((&appData.soundShout)[cc]);
\r
1100 textAttribs[cc].sound.data = NULL;
\r
1101 MyLoadSound(&textAttribs[cc].sound);
\r
1103 for (cc = ColorNormal; cc < NColorClasses; cc++) {
\r
1104 textAttribs[cc].sound.name = strdup("");
\r
1105 textAttribs[cc].sound.data = NULL;
\r
1107 for (sc = (SoundClass)0; sc < NSoundClasses; sc++) {
\r
1108 sounds[sc].name = strdup((&appData.soundMove)[sc]);
\r
1109 sounds[sc].data = NULL;
\r
1110 MyLoadSound(&sounds[sc]);
\r
1115 SetCommPortDefaults()
\r
1117 memset(&dcb, 0, sizeof(DCB)); // required by VS 2002 +
\r
1118 dcb.DCBlength = sizeof(DCB);
\r
1119 dcb.BaudRate = 9600;
\r
1120 dcb.fBinary = TRUE;
\r
1121 dcb.fParity = FALSE;
\r
1122 dcb.fOutxCtsFlow = FALSE;
\r
1123 dcb.fOutxDsrFlow = FALSE;
\r
1124 dcb.fDtrControl = DTR_CONTROL_ENABLE;
\r
1125 dcb.fDsrSensitivity = FALSE;
\r
1126 dcb.fTXContinueOnXoff = TRUE;
\r
1127 dcb.fOutX = FALSE;
\r
1129 dcb.fNull = FALSE;
\r
1130 dcb.fRtsControl = RTS_CONTROL_ENABLE;
\r
1131 dcb.fAbortOnError = FALSE;
\r
1133 dcb.Parity = SPACEPARITY;
\r
1134 dcb.StopBits = ONESTOPBIT;
\r
1137 // [HGM] args: these three cases taken out to stay in front-end
\r
1139 SaveFontArg(FILE *f, ArgDescriptor *ad)
\r
1140 { // in WinBoard every board size has its own font, and the "argLoc" identifies the table,
\r
1141 // while the curent board size determines the element. This system should be ported to XBoard.
\r
1142 // What the table contains pointers to, and how to print the font description, remains platform-dependent
\r
1144 for (bs=0; bs<NUM_SIZES; bs++) {
\r
1145 MyFontParams *mfp = &font[bs][(int) ad->argLoc]->mfp;
\r
1146 fprintf(f, "/size=%s ", sizeInfo[bs].name);
\r
1147 fprintf(f, "/%s=\"%s:%g%s%s%s%s%sc%d\"\n",
\r
1148 ad->argName, mfp->faceName, mfp->pointSize,
\r
1149 mfp->bold || mfp->italic || mfp->underline || mfp->strikeout ? " " : "",
\r
1150 mfp->bold ? "b" : "",
\r
1151 mfp->italic ? "i" : "",
\r
1152 mfp->underline ? "u" : "",
\r
1153 mfp->strikeout ? "s" : "",
\r
1154 (int)mfp->charset);
\r
1160 { // [HGM] copy the names from the internal WB variables to appData
\r
1163 for (cc = (ColorClass)0; cc < ColorNormal; cc++)
\r
1164 (&appData.soundShout)[cc] = textAttribs[cc].sound.name;
\r
1165 for (sc = (SoundClass)0; sc < NSoundClasses; sc++)
\r
1166 (&appData.soundMove)[sc] = sounds[sc].name;
\r
1170 SaveAttribsArg(FILE *f, ArgDescriptor *ad)
\r
1171 { // here the "argLoc" defines a table index. It could have contained the 'ta' pointer itself, though
\r
1172 MyTextAttribs* ta = &textAttribs[(ColorClass)ad->argLoc];
\r
1173 fprintf(f, "/%s=\"%s%s%s%s%s#%02lx%02lx%02lx\"\n", ad->argName,
\r
1174 (ta->effects & CFE_BOLD) ? "b" : "",
\r
1175 (ta->effects & CFE_ITALIC) ? "i" : "",
\r
1176 (ta->effects & CFE_UNDERLINE) ? "u" : "",
\r
1177 (ta->effects & CFE_STRIKEOUT) ? "s" : "",
\r
1178 (ta->effects) ? " " : "",
\r
1179 ta->color&0xff, (ta->color >> 8)&0xff, (ta->color >> 16)&0xff);
\r
1183 SaveColor(FILE *f, ArgDescriptor *ad)
\r
1184 { // in WinBoard the color is an int and has to be converted to text. In X it would be a string already?
\r
1185 COLORREF color = *(COLORREF *)colorVariable[(int)ad->argLoc];
\r
1186 fprintf(f, "/%s=#%02lx%02lx%02lx\n", ad->argName,
\r
1187 color&0xff, (color>>8)&0xff, (color>>16)&0xff);
\r
1191 SaveBoardSize(FILE *f, char *name, void *addr)
\r
1192 { // wrapper to shield back-end from BoardSize & sizeInfo
\r
1193 fprintf(f, "/%s=%s\n", name, sizeInfo[*(BoardSize *)addr].name);
\r
1197 ParseCommPortSettings(char *s)
\r
1198 { // wrapper to keep dcb from back-end
\r
1199 ParseCommSettings(s, &dcb);
\r
1204 { // wrapper to shield use of window handles from back-end (make addressible by number?)
\r
1205 GetActualPlacement(hwndMain, &wpMain);
\r
1206 GetActualPlacement(hwndConsole, &wpConsole);
\r
1207 GetActualPlacement(commentDialog, &wpComment);
\r
1208 GetActualPlacement(editTagsDialog, &wpTags);
\r
1209 GetActualPlacement(gameListDialog, &wpGameList);
\r
1210 GetActualPlacement(moveHistoryDialog, &wpMoveHistory);
\r
1211 GetActualPlacement(evalGraphDialog, &wpEvalGraph);
\r
1212 GetActualPlacement(engineOutputDialog, &wpEngineOutput);
\r
1216 PrintCommPortSettings(FILE *f, char *name)
\r
1217 { // wrapper to shield back-end from DCB
\r
1218 PrintCommSettings(f, name, &dcb);
\r
1222 MySearchPath(char *installDir, char *name, char *fullname)
\r
1225 return (int) SearchPath(installDir, name, NULL, MSG_SIZ, fullname, &dummy);
\r
1229 MyGetFullPathName(char *name, char *fullname)
\r
1232 return (int) GetFullPathName(name, MSG_SIZ, fullname, &dummy);
\r
1237 { // [HGM] args: allows testing if main window is realized from back-end
\r
1238 return hwndMain != NULL;
\r
1242 PopUpStartupDialog()
\r
1246 lpProc = MakeProcInstance((FARPROC)StartupDialog, hInst);
\r
1247 DialogBox(hInst, MAKEINTRESOURCE(DLG_Startup), NULL, (DLGPROC)lpProc);
\r
1248 FreeProcInstance(lpProc);
\r
1251 /*---------------------------------------------------------------------------*\
\r
1253 * GDI board drawing routines
\r
1255 \*---------------------------------------------------------------------------*/
\r
1257 /* [AS] Draw square using background texture */
\r
1258 static void DrawTile( int dx, int dy, int dw, int dh, HDC dst, HDC src, int mode, int sx, int sy )
\r
1263 return; /* Should never happen! */
\r
1266 SetGraphicsMode( dst, GM_ADVANCED );
\r
1273 /* X reflection */
\r
1278 x.eDx = (FLOAT) dw + dx - 1;
\r
1281 SetWorldTransform( dst, &x );
\r
1284 /* Y reflection */
\r
1290 x.eDy = (FLOAT) dh + dy - 1;
\r
1292 SetWorldTransform( dst, &x );
\r
1300 x.eDx = (FLOAT) dx;
\r
1301 x.eDy = (FLOAT) dy;
\r
1304 SetWorldTransform( dst, &x );
\r
1308 BitBlt( dst, dx, dy, dw, dh, src, sx, sy, SRCCOPY );
\r
1316 SetWorldTransform( dst, &x );
\r
1318 ModifyWorldTransform( dst, 0, MWT_IDENTITY );
\r
1321 /* [AS] [HGM] Make room for more piece types, so all pieces can be different */
\r
1323 PM_WP = (int) WhitePawn,
\r
1324 PM_WN = (int) WhiteKnight,
\r
1325 PM_WB = (int) WhiteBishop,
\r
1326 PM_WR = (int) WhiteRook,
\r
1327 PM_WQ = (int) WhiteQueen,
\r
1328 PM_WF = (int) WhiteFerz,
\r
1329 PM_WW = (int) WhiteWazir,
\r
1330 PM_WE = (int) WhiteAlfil,
\r
1331 PM_WM = (int) WhiteMan,
\r
1332 PM_WO = (int) WhiteCannon,
\r
1333 PM_WU = (int) WhiteUnicorn,
\r
1334 PM_WH = (int) WhiteNightrider,
\r
1335 PM_WA = (int) WhiteAngel,
\r
1336 PM_WC = (int) WhiteMarshall,
\r
1337 PM_WAB = (int) WhiteCardinal,
\r
1338 PM_WD = (int) WhiteDragon,
\r
1339 PM_WL = (int) WhiteLance,
\r
1340 PM_WS = (int) WhiteCobra,
\r
1341 PM_WV = (int) WhiteFalcon,
\r
1342 PM_WSG = (int) WhiteSilver,
\r
1343 PM_WG = (int) WhiteGrasshopper,
\r
1344 PM_WK = (int) WhiteKing,
\r
1345 PM_BP = (int) BlackPawn,
\r
1346 PM_BN = (int) BlackKnight,
\r
1347 PM_BB = (int) BlackBishop,
\r
1348 PM_BR = (int) BlackRook,
\r
1349 PM_BQ = (int) BlackQueen,
\r
1350 PM_BF = (int) BlackFerz,
\r
1351 PM_BW = (int) BlackWazir,
\r
1352 PM_BE = (int) BlackAlfil,
\r
1353 PM_BM = (int) BlackMan,
\r
1354 PM_BO = (int) BlackCannon,
\r
1355 PM_BU = (int) BlackUnicorn,
\r
1356 PM_BH = (int) BlackNightrider,
\r
1357 PM_BA = (int) BlackAngel,
\r
1358 PM_BC = (int) BlackMarshall,
\r
1359 PM_BG = (int) BlackGrasshopper,
\r
1360 PM_BAB = (int) BlackCardinal,
\r
1361 PM_BD = (int) BlackDragon,
\r
1362 PM_BL = (int) BlackLance,
\r
1363 PM_BS = (int) BlackCobra,
\r
1364 PM_BV = (int) BlackFalcon,
\r
1365 PM_BSG = (int) BlackSilver,
\r
1366 PM_BK = (int) BlackKing
\r
1369 static HFONT hPieceFont = NULL;
\r
1370 static HBITMAP hPieceMask[(int) EmptySquare];
\r
1371 static HBITMAP hPieceFace[(int) EmptySquare];
\r
1372 static int fontBitmapSquareSize = 0;
\r
1373 static char pieceToFontChar[(int) EmptySquare] =
\r
1374 { 'p', 'n', 'b', 'r', 'q',
\r
1375 'n', 'b', 'p', 'n', 'b', 'r', 'b', 'r', 'q', 'k',
\r
1376 'k', 'o', 'm', 'v', 't', 'w',
\r
1377 'v', 't', 'o', 'm', 'v', 't', 'v', 't', 'w', 'l',
\r
1380 extern BOOL SetCharTable( char *table, const char * map );
\r
1381 /* [HGM] moved to backend.c */
\r
1383 static void SetPieceBackground( HDC hdc, COLORREF color, int mode )
\r
1386 BYTE r1 = GetRValue( color );
\r
1387 BYTE g1 = GetGValue( color );
\r
1388 BYTE b1 = GetBValue( color );
\r
1394 /* Create a uniform background first */
\r
1395 hbrush = CreateSolidBrush( color );
\r
1396 SetRect( &rc, 0, 0, squareSize, squareSize );
\r
1397 FillRect( hdc, &rc, hbrush );
\r
1398 DeleteObject( hbrush );
\r
1401 /* Vertical gradient, good for pawn, knight and rook, less for queen and king */
\r
1402 int steps = squareSize / 2;
\r
1405 for( i=0; i<steps; i++ ) {
\r
1406 BYTE r = r1 - (r1-r2) * i / steps;
\r
1407 BYTE g = g1 - (g1-g2) * i / steps;
\r
1408 BYTE b = b1 - (b1-b2) * i / steps;
\r
1410 hbrush = CreateSolidBrush( RGB(r,g,b) );
\r
1411 SetRect( &rc, i + squareSize - steps, 0, i + squareSize - steps + 1, squareSize );
\r
1412 FillRect( hdc, &rc, hbrush );
\r
1413 DeleteObject(hbrush);
\r
1416 else if( mode == 2 ) {
\r
1417 /* Diagonal gradient, good more or less for every piece */
\r
1418 POINT triangle[3];
\r
1419 HPEN hpen = SelectObject( hdc, GetStockObject(NULL_PEN) );
\r
1420 HBRUSH hbrush_old;
\r
1421 int steps = squareSize;
\r
1424 triangle[0].x = squareSize - steps;
\r
1425 triangle[0].y = squareSize;
\r
1426 triangle[1].x = squareSize;
\r
1427 triangle[1].y = squareSize;
\r
1428 triangle[2].x = squareSize;
\r
1429 triangle[2].y = squareSize - steps;
\r
1431 for( i=0; i<steps; i++ ) {
\r
1432 BYTE r = r1 - (r1-r2) * i / steps;
\r
1433 BYTE g = g1 - (g1-g2) * i / steps;
\r
1434 BYTE b = b1 - (b1-b2) * i / steps;
\r
1436 hbrush = CreateSolidBrush( RGB(r,g,b) );
\r
1437 hbrush_old = SelectObject( hdc, hbrush );
\r
1438 Polygon( hdc, triangle, 3 );
\r
1439 SelectObject( hdc, hbrush_old );
\r
1440 DeleteObject(hbrush);
\r
1445 SelectObject( hdc, hpen );
\r
1450 [AS] The method I use to create the bitmaps it a bit tricky, but it
\r
1451 seems to work ok. The main problem here is to find the "inside" of a chess
\r
1452 piece: follow the steps as explained below.
\r
1454 static void CreatePieceMaskFromFont( HDC hdc_window, HDC hdc, int index )
\r
1458 COLORREF chroma = RGB(0xFF,0x00,0xFF);
\r
1462 int backColor = whitePieceColor;
\r
1463 int foreColor = blackPieceColor;
\r
1465 if( index < (int)BlackPawn && appData.fontBackColorWhite != appData.fontForeColorWhite ) {
\r
1466 backColor = appData.fontBackColorWhite;
\r
1467 foreColor = appData.fontForeColorWhite;
\r
1469 else if( index >= (int)BlackPawn && appData.fontBackColorBlack != appData.fontForeColorBlack ) {
\r
1470 backColor = appData.fontBackColorBlack;
\r
1471 foreColor = appData.fontForeColorBlack;
\r
1475 hbm = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );
\r
1477 hbm_old = SelectObject( hdc, hbm );
\r
1481 rc.right = squareSize;
\r
1482 rc.bottom = squareSize;
\r
1484 /* Step 1: background is now black */
\r
1485 FillRect( hdc, &rc, GetStockObject(BLACK_BRUSH) );
\r
1487 GetTextExtentPoint32( hdc, &pieceToFontChar[index], 1, &sz );
\r
1489 pt.x = (squareSize - sz.cx) / 2;
\r
1490 pt.y = (squareSize - sz.cy) / 2;
\r
1492 SetBkMode( hdc, TRANSPARENT );
\r
1493 SetTextColor( hdc, chroma );
\r
1494 /* Step 2: the piece has been drawn in purple, there are now black and purple in this bitmap */
\r
1495 TextOut( hdc, pt.x, pt.y, &pieceToFontChar[appData.allWhite && index >= (int)BlackPawn ? index - (int)BlackPawn : index], 1 );
\r
1497 SelectObject( hdc, GetStockObject(WHITE_BRUSH) );
\r
1498 /* Step 3: the area outside the piece is filled with white */
\r
1499 // FloodFill( hdc, 0, 0, chroma );
\r
1500 ExtFloodFill( hdc, 0, 0, 0, FLOODFILLSURFACE );
\r
1501 ExtFloodFill( hdc, 0, squareSize-1, 0, FLOODFILLSURFACE ); // [HGM] fill from all 4 corners, for if piece too big
\r
1502 ExtFloodFill( hdc, squareSize-1, 0, 0, FLOODFILLSURFACE );
\r
1503 ExtFloodFill( hdc, squareSize-1, squareSize-1, 0, FLOODFILLSURFACE );
\r
1504 SelectObject( hdc, GetStockObject(BLACK_BRUSH) );
\r
1506 Step 4: this is the tricky part, the area inside the piece is filled with black,
\r
1507 but if the start point is not inside the piece we're lost!
\r
1508 There should be a better way to do this... if we could create a region or path
\r
1509 from the fill operation we would be fine for example.
\r
1511 // FloodFill( hdc, squareSize / 2, squareSize / 2, RGB(0xFF,0xFF,0xFF) );
\r
1512 ExtFloodFill( hdc, squareSize / 2, squareSize / 2, RGB(0xFF,0xFF,0xFF), FLOODFILLBORDER );
\r
1514 { /* [HGM] shave off edges of mask, in an attempt to correct for the fact that FloodFill does not work correctly under Win XP */
\r
1515 HDC dc2 = CreateCompatibleDC( hdc_window );
\r
1516 HBITMAP bm2 = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );
\r
1518 SelectObject( dc2, bm2 );
\r
1519 BitBlt( dc2, 0, 0, squareSize, squareSize, hdc, 0, 0, SRCCOPY ); // make copy
\r
1520 BitBlt( hdc, 0, 1, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );
\r
1521 BitBlt( hdc, 2, 1, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );
\r
1522 BitBlt( hdc, 1, 0, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );
\r
1523 BitBlt( hdc, 1, 2, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );
\r
1526 DeleteObject( bm2 );
\r
1529 SetTextColor( hdc, 0 );
\r
1531 Step 5: some fonts have "disconnected" areas that are skipped by the fill:
\r
1532 draw the piece again in black for safety.
\r
1534 TextOut( hdc, pt.x, pt.y, &pieceToFontChar[appData.allWhite && index >= (int)BlackPawn ? index - (int)BlackPawn : index], 1 );
\r
1536 SelectObject( hdc, hbm_old );
\r
1538 if( hPieceMask[index] != NULL ) {
\r
1539 DeleteObject( hPieceMask[index] );
\r
1542 hPieceMask[index] = hbm;
\r
1545 hbm = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );
\r
1547 SelectObject( hdc, hbm );
\r
1550 HDC dc1 = CreateCompatibleDC( hdc_window );
\r
1551 HDC dc2 = CreateCompatibleDC( hdc_window );
\r
1552 HBITMAP bm2 = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );
\r
1554 SelectObject( dc1, hPieceMask[index] );
\r
1555 SelectObject( dc2, bm2 );
\r
1556 FillRect( dc2, &rc, GetStockObject(WHITE_BRUSH) );
\r
1557 BitBlt( dc2, 0, 0, squareSize, squareSize, dc1, 0, 0, SRCINVERT );
\r
1560 Now dc2 contains the inverse of the piece mask, i.e. a mask that preserves
\r
1561 the piece background and deletes (makes transparent) the rest.
\r
1562 Thanks to that mask, we are free to paint the background with the greates
\r
1563 freedom, as we'll be able to mask off the unwanted parts when finished.
\r
1564 We use this, to make gradients and give the pieces a "roundish" look.
\r
1566 SetPieceBackground( hdc, backColor, 2 );
\r
1567 BitBlt( hdc, 0, 0, squareSize, squareSize, dc2, 0, 0, SRCAND );
\r
1571 DeleteObject( bm2 );
\r
1574 SetTextColor( hdc, foreColor );
\r
1575 TextOut( hdc, pt.x, pt.y, &pieceToFontChar[appData.allWhite && index >= (int)BlackPawn ? index - (int)BlackPawn : index], 1 );
\r
1577 SelectObject( hdc, hbm_old );
\r
1579 if( hPieceFace[index] != NULL ) {
\r
1580 DeleteObject( hPieceFace[index] );
\r
1583 hPieceFace[index] = hbm;
\r
1586 static int TranslatePieceToFontPiece( int piece )
\r
1616 case BlackMarshall:
\r
1620 case BlackNightrider:
\r
1626 case BlackUnicorn:
\r
1630 case BlackGrasshopper:
\r
1642 case BlackCardinal:
\r
1649 case WhiteMarshall:
\r
1653 case WhiteNightrider:
\r
1659 case WhiteUnicorn:
\r
1663 case WhiteGrasshopper:
\r
1675 case WhiteCardinal:
\r
1684 void CreatePiecesFromFont()
\r
1687 HDC hdc_window = NULL;
\r
1693 if( fontBitmapSquareSize < 0 ) {
\r
1694 /* Something went seriously wrong in the past: do not try to recreate fonts! */
\r
1698 if( appData.renderPiecesWithFont == NULL || appData.renderPiecesWithFont[0] == NULLCHAR || appData.renderPiecesWithFont[0] == '*' ) {
\r
1699 fontBitmapSquareSize = -1;
\r
1703 if( fontBitmapSquareSize != squareSize ) {
\r
1704 hdc_window = GetDC( hwndMain );
\r
1705 hdc = CreateCompatibleDC( hdc_window );
\r
1707 if( hPieceFont != NULL ) {
\r
1708 DeleteObject( hPieceFont );
\r
1711 for( i=0; i<=(int)BlackKing; i++ ) {
\r
1712 hPieceMask[i] = NULL;
\r
1713 hPieceFace[i] = NULL;
\r
1719 if( appData.fontPieceSize >= 50 && appData.fontPieceSize <= 150 ) {
\r
1720 fontHeight = appData.fontPieceSize;
\r
1723 fontHeight = (fontHeight * squareSize) / 100;
\r
1725 lf.lfHeight = -MulDiv( fontHeight, GetDeviceCaps(hdc, LOGPIXELSY), 72 );
\r
1727 lf.lfEscapement = 0;
\r
1728 lf.lfOrientation = 0;
\r
1729 lf.lfWeight = FW_NORMAL;
\r
1731 lf.lfUnderline = 0;
\r
1732 lf.lfStrikeOut = 0;
\r
1733 lf.lfCharSet = DEFAULT_CHARSET;
\r
1734 lf.lfOutPrecision = OUT_DEFAULT_PRECIS;
\r
1735 lf.lfClipPrecision = CLIP_DEFAULT_PRECIS;
\r
1736 lf.lfQuality = PROOF_QUALITY;
\r
1737 lf.lfPitchAndFamily = DEFAULT_PITCH | FF_DONTCARE;
\r
1738 strncpy( lf.lfFaceName, appData.renderPiecesWithFont, sizeof(lf.lfFaceName) );
\r
1739 lf.lfFaceName[ sizeof(lf.lfFaceName) - 1 ] = '\0';
\r
1741 hPieceFont = CreateFontIndirect( &lf );
\r
1743 if( hPieceFont == NULL ) {
\r
1744 fontBitmapSquareSize = -2;
\r
1747 /* Setup font-to-piece character table */
\r
1748 if( ! SetCharTable(pieceToFontChar, appData.fontToPieceTable) ) {
\r
1749 /* No (or wrong) global settings, try to detect the font */
\r
1750 if( strstr(lf.lfFaceName,"Alpha") != NULL ) {
\r
1752 SetCharTable(pieceToFontChar, "phbrqkojntwl");
\r
1754 else if( strstr(lf.lfFaceName,"DiagramTT") != NULL ) {
\r
1755 /* DiagramTT* family */
\r
1756 SetCharTable(pieceToFontChar, "PNLRQKpnlrqk");
\r
1758 else if( strstr(lf.lfFaceName,"WinboardF") != NULL ) {
\r
1759 /* Fairy symbols */
\r
1760 SetCharTable(pieceToFontChar, "PNBRQFEACWMOHIJGDVSLUKpnbrqfeacwmohijgdvsluk");
\r
1762 else if( strstr(lf.lfFaceName,"GC2004D") != NULL ) {
\r
1763 /* Good Companion (Some characters get warped as literal :-( */
\r
1764 char s[] = "1cmWG0??S??oYI23wgQU";
\r
1765 s[0]=0xB9; s[1]=0xA9; s[6]=0xB1; s[11]=0xBB; s[12]=0xAB; s[17]=0xB3;
\r
1766 SetCharTable(pieceToFontChar, s);
\r
1769 /* Cases, Condal, Leipzig, Lucena, Marroquin, Merida, Usual */
\r
1770 SetCharTable(pieceToFontChar, "pnbrqkomvtwl");
\r
1774 /* Create bitmaps */
\r
1775 hfont_old = SelectObject( hdc, hPieceFont );
\r
1776 for(i=(int)WhitePawn; i<(int)EmptySquare; i++) /* [HGM] made a loop for this */
\r
1777 if(PieceToChar((ChessSquare)i) != '.') /* skip unused pieces */
\r
1778 CreatePieceMaskFromFont( hdc_window, hdc, i );
\r
1780 SelectObject( hdc, hfont_old );
\r
1782 fontBitmapSquareSize = squareSize;
\r
1786 if( hdc != NULL ) {
\r
1790 if( hdc_window != NULL ) {
\r
1791 ReleaseDC( hwndMain, hdc_window );
\r
1796 DoLoadBitmap(HINSTANCE hinst, char *piece, int squareSize, char *suffix)
\r
1800 sprintf(name, "%s%d%s", piece, squareSize, suffix);
\r
1801 if (gameInfo.event &&
\r
1802 strcmp(gameInfo.event, "Easter Egg Hunt") == 0 &&
\r
1803 strcmp(name, "k80s") == 0) {
\r
1804 strcpy(name, "tim");
\r
1806 return LoadBitmap(hinst, name);
\r
1810 /* Insert a color into the program's logical palette
\r
1811 structure. This code assumes the given color is
\r
1812 the result of the RGB or PALETTERGB macro, and it
\r
1813 knows how those macros work (which is documented).
\r
1816 InsertInPalette(COLORREF color)
\r
1818 LPPALETTEENTRY pe = &(pLogPal->palPalEntry[pLogPal->palNumEntries]);
\r
1820 if (pLogPal->palNumEntries++ >= PALETTESIZE) {
\r
1821 DisplayFatalError("Too many colors", 0, 1);
\r
1822 pLogPal->palNumEntries--;
\r
1826 pe->peFlags = (char) 0;
\r
1827 pe->peRed = (char) (0xFF & color);
\r
1828 pe->peGreen = (char) (0xFF & (color >> 8));
\r
1829 pe->peBlue = (char) (0xFF & (color >> 16));
\r
1835 InitDrawingColors()
\r
1837 if (pLogPal == NULL) {
\r
1838 /* Allocate enough memory for a logical palette with
\r
1839 * PALETTESIZE entries and set the size and version fields
\r
1840 * of the logical palette structure.
\r
1842 pLogPal = (NPLOGPALETTE)
\r
1843 LocalAlloc(LMEM_FIXED, (sizeof(LOGPALETTE) +
\r
1844 (sizeof(PALETTEENTRY) * (PALETTESIZE))));
\r
1845 pLogPal->palVersion = 0x300;
\r
1847 pLogPal->palNumEntries = 0;
\r
1849 InsertInPalette(lightSquareColor);
\r
1850 InsertInPalette(darkSquareColor);
\r
1851 InsertInPalette(whitePieceColor);
\r
1852 InsertInPalette(blackPieceColor);
\r
1853 InsertInPalette(highlightSquareColor);
\r
1854 InsertInPalette(premoveHighlightColor);
\r
1856 /* create a logical color palette according the information
\r
1857 * in the LOGPALETTE structure.
\r
1859 hPal = CreatePalette((LPLOGPALETTE) pLogPal);
\r
1861 lightSquareBrush = CreateSolidBrush(lightSquareColor);
\r
1862 blackSquareBrush = CreateSolidBrush(blackPieceColor);
\r
1863 darkSquareBrush = CreateSolidBrush(darkSquareColor);
\r
1864 whitePieceBrush = CreateSolidBrush(whitePieceColor);
\r
1865 blackPieceBrush = CreateSolidBrush(blackPieceColor);
\r
1866 iconBkgndBrush = CreateSolidBrush(GetSysColor(COLOR_BACKGROUND));
\r
1867 explodeBrush = CreateSolidBrush(highlightSquareColor); // [HGM] atomic
\r
1868 markerBrush = CreateSolidBrush(premoveHighlightColor); // [HGM] markers
\r
1869 /* [AS] Force rendering of the font-based pieces */
\r
1870 if( fontBitmapSquareSize > 0 ) {
\r
1871 fontBitmapSquareSize = 0;
\r
1877 BoardWidth(int boardSize, int n)
\r
1878 { /* [HGM] argument n added to allow different width and height */
\r
1879 int lineGap = sizeInfo[boardSize].lineGap;
\r
1881 if( appData.overrideLineGap >= 0 && appData.overrideLineGap <= 5 ) {
\r
1882 lineGap = appData.overrideLineGap;
\r
1885 return (n + 1) * lineGap +
\r
1886 n * sizeInfo[boardSize].squareSize;
\r
1889 /* Respond to board resize by dragging edge */
\r
1891 ResizeBoard(int newSizeX, int newSizeY, int flags)
\r
1893 BoardSize newSize = NUM_SIZES - 1;
\r
1894 static int recurse = 0;
\r
1895 if (IsIconic(hwndMain)) return;
\r
1896 if (recurse > 0) return;
\r
1898 while (newSize > 0) {
\r
1899 InitDrawingSizes(newSize+1000, 0); // [HGM] kludge to update sizeInfo without visible effects
\r
1900 if(newSizeX >= sizeInfo[newSize].cliWidth &&
\r
1901 newSizeY >= sizeInfo[newSize].cliHeight) break;
\r
1904 boardSize = newSize;
\r
1905 InitDrawingSizes(boardSize, flags);
\r
1912 InitDrawingSizes(BoardSize boardSize, int flags)
\r
1914 int i, boardWidth, boardHeight; /* [HGM] height treated separately */
\r
1915 ChessSquare piece;
\r
1916 static int oldBoardSize = -1, oldTinyLayout = 0;
\r
1918 SIZE clockSize, messageSize;
\r
1920 char buf[MSG_SIZ];
\r
1922 HMENU hmenu = GetMenu(hwndMain);
\r
1923 RECT crect, wrect, oldRect;
\r
1925 LOGBRUSH logbrush;
\r
1927 int suppressVisibleEffects = 0; // [HGM] kludge to request updating sizeInfo only
\r
1928 if((int)boardSize >= 1000 ) { boardSize -= 1000; suppressVisibleEffects = 1; }
\r
1930 /* [HGM] call with -2 uses old size (for if nr of files, ranks changes) */
\r
1931 if(boardSize == (BoardSize)(-2) ) boardSize = oldBoardSize;
\r
1933 oldRect.left = wpMain.x; //[HGM] placement: remember previous window params
\r
1934 oldRect.top = wpMain.y;
\r
1935 oldRect.right = wpMain.x + wpMain.width;
\r
1936 oldRect.bottom = wpMain.y + wpMain.height;
\r
1938 tinyLayout = sizeInfo[boardSize].tinyLayout;
\r
1939 smallLayout = sizeInfo[boardSize].smallLayout;
\r
1940 squareSize = sizeInfo[boardSize].squareSize;
\r
1941 lineGap = sizeInfo[boardSize].lineGap;
\r
1942 minorSize = 0; /* [HGM] Kludge to see if demagnified pieces need to be shifted */
\r
1944 if( appData.overrideLineGap >= 0 && appData.overrideLineGap <= 5 ) {
\r
1945 lineGap = appData.overrideLineGap;
\r
1948 if (tinyLayout != oldTinyLayout) {
\r
1949 long style = GetWindowLong(hwndMain, GWL_STYLE);
\r
1951 style &= ~WS_SYSMENU;
\r
1952 InsertMenu(hmenu, IDM_Exit, MF_BYCOMMAND, IDM_Minimize,
\r
1953 "&Minimize\tCtrl+F4");
\r
1955 style |= WS_SYSMENU;
\r
1956 RemoveMenu(hmenu, IDM_Minimize, MF_BYCOMMAND);
\r
1958 SetWindowLong(hwndMain, GWL_STYLE, style);
\r
1960 for (i=0; menuBarText[tinyLayout][i]; i++) {
\r
1961 ModifyMenu(hmenu, i, MF_STRING|MF_BYPOSITION|MF_POPUP,
\r
1962 (UINT)GetSubMenu(hmenu, i), menuBarText[tinyLayout][i]);
\r
1964 DrawMenuBar(hwndMain);
\r
1967 boardWidth = BoardWidth(boardSize, BOARD_WIDTH);
\r
1968 boardHeight = BoardWidth(boardSize, BOARD_HEIGHT);
\r
1970 /* Get text area sizes */
\r
1971 hdc = GetDC(hwndMain);
\r
1972 if (appData.clockMode) {
\r
1973 sprintf(buf, "White: %s", TimeString(23*60*60*1000L));
\r
1975 sprintf(buf, "White");
\r
1977 oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);
\r
1978 GetTextExtentPoint(hdc, buf, strlen(buf), &clockSize);
\r
1979 SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);
\r
1980 str = "We only care about the height here";
\r
1981 GetTextExtentPoint(hdc, str, strlen(str), &messageSize);
\r
1982 SelectObject(hdc, oldFont);
\r
1983 ReleaseDC(hwndMain, hdc);
\r
1985 /* Compute where everything goes */
\r
1986 if((first.programLogo || second.programLogo) && !tinyLayout) {
\r
1987 /* [HGM] logo: if either logo is on, reserve space for it */
\r
1988 logoHeight = 2*clockSize.cy;
\r
1989 leftLogoRect.left = OUTER_MARGIN;
\r
1990 leftLogoRect.right = leftLogoRect.left + 4*clockSize.cy;
\r
1991 leftLogoRect.top = OUTER_MARGIN;
\r
1992 leftLogoRect.bottom = OUTER_MARGIN + logoHeight;
\r
1994 rightLogoRect.right = OUTER_MARGIN + boardWidth;
\r
1995 rightLogoRect.left = rightLogoRect.right - 4*clockSize.cy;
\r
1996 rightLogoRect.top = OUTER_MARGIN;
\r
1997 rightLogoRect.bottom = OUTER_MARGIN + logoHeight;
\r
2000 whiteRect.left = leftLogoRect.right;
\r
2001 whiteRect.right = OUTER_MARGIN + boardWidth/2 - INNER_MARGIN/2;
\r
2002 whiteRect.top = OUTER_MARGIN;
\r
2003 whiteRect.bottom = whiteRect.top + logoHeight;
\r
2005 blackRect.right = rightLogoRect.left;
\r
2006 blackRect.left = whiteRect.right + INNER_MARGIN;
\r
2007 blackRect.top = whiteRect.top;
\r
2008 blackRect.bottom = whiteRect.bottom;
\r
2010 whiteRect.left = OUTER_MARGIN;
\r
2011 whiteRect.right = whiteRect.left + boardWidth/2 - INNER_MARGIN/2;
\r
2012 whiteRect.top = OUTER_MARGIN;
\r
2013 whiteRect.bottom = whiteRect.top + clockSize.cy;
\r
2015 blackRect.left = whiteRect.right + INNER_MARGIN;
\r
2016 blackRect.right = blackRect.left + boardWidth/2 - 1;
\r
2017 blackRect.top = whiteRect.top;
\r
2018 blackRect.bottom = whiteRect.bottom;
\r
2020 logoHeight = 0; // [HGM] logo: suppress logo after change to tiny layout!
\r
2023 messageRect.left = OUTER_MARGIN + MESSAGE_LINE_LEFTMARGIN;
\r
2024 if (appData.showButtonBar) {
\r
2025 messageRect.right = OUTER_MARGIN + boardWidth // [HGM] logo: expressed independent of clock placement
\r
2026 - N_BUTTONS*BUTTON_WIDTH - MESSAGE_LINE_LEFTMARGIN;
\r
2028 messageRect.right = OUTER_MARGIN + boardWidth;
\r
2030 messageRect.top = whiteRect.bottom + INNER_MARGIN;
\r
2031 messageRect.bottom = messageRect.top + messageSize.cy;
\r
2033 boardRect.left = OUTER_MARGIN;
\r
2034 boardRect.right = boardRect.left + boardWidth;
\r
2035 boardRect.top = messageRect.bottom + INNER_MARGIN;
\r
2036 boardRect.bottom = boardRect.top + boardHeight;
\r
2038 sizeInfo[boardSize].cliWidth = boardRect.right + OUTER_MARGIN;
\r
2039 sizeInfo[boardSize].cliHeight = boardRect.bottom + OUTER_MARGIN;
\r
2040 oldBoardSize = boardSize;
\r
2041 oldTinyLayout = tinyLayout;
\r
2042 winW = 2 * GetSystemMetrics(SM_CXFRAME) + boardRect.right + OUTER_MARGIN;
\r
2043 winH = 2 * GetSystemMetrics(SM_CYFRAME) + GetSystemMetrics(SM_CYMENU) +
\r
2044 GetSystemMetrics(SM_CYCAPTION) + boardRect.bottom + OUTER_MARGIN;
\r
2045 if(suppressVisibleEffects) return; // [HGM] when called for filling sizeInfo only
\r
2046 wpMain.width = winW; // [HGM] placement: set through temporary which can used by initial sizing choice
\r
2047 wpMain.height = winH; // without disturbing window attachments
\r
2048 GetWindowRect(hwndMain, &wrect);
\r
2049 SetWindowPos(hwndMain, NULL, 0, 0, wpMain.width, wpMain.height,
\r
2050 SWP_NOCOPYBITS|SWP_NOZORDER|SWP_NOMOVE);
\r
2052 // [HGM] placement: let attached windows follow size change.
\r
2053 ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, moveHistoryDialog, &wpMoveHistory );
\r
2054 ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, evalGraphDialog, &wpEvalGraph );
\r
2055 ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, engineOutputDialog, &wpEngineOutput );
\r
2056 ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, gameListDialog, &wpGameList );
\r
2057 ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, hwndConsole, &wpConsole );
\r
2059 /* compensate if menu bar wrapped */
\r
2060 GetClientRect(hwndMain, &crect);
\r
2061 offby = boardRect.bottom + OUTER_MARGIN - crect.bottom;
\r
2062 wpMain.height += offby;
\r
2064 case WMSZ_TOPLEFT:
\r
2065 SetWindowPos(hwndMain, NULL,
\r
2066 wrect.right - wpMain.width, wrect.bottom - wpMain.height,
\r
2067 wpMain.width, wpMain.height, SWP_NOCOPYBITS|SWP_NOZORDER);
\r
2070 case WMSZ_TOPRIGHT:
\r
2072 SetWindowPos(hwndMain, NULL,
\r
2073 wrect.left, wrect.bottom - wpMain.height,
\r
2074 wpMain.width, wpMain.height, SWP_NOCOPYBITS|SWP_NOZORDER);
\r
2077 case WMSZ_BOTTOMLEFT:
\r
2079 SetWindowPos(hwndMain, NULL,
\r
2080 wrect.right - wpMain.width, wrect.top,
\r
2081 wpMain.width, wpMain.height, SWP_NOCOPYBITS|SWP_NOZORDER);
\r
2084 case WMSZ_BOTTOMRIGHT:
\r
2088 SetWindowPos(hwndMain, NULL, 0, 0, wpMain.width, wpMain.height,
\r
2089 SWP_NOCOPYBITS|SWP_NOZORDER|SWP_NOMOVE);
\r
2094 for (i = 0; i < N_BUTTONS; i++) {
\r
2095 if (buttonDesc[i].hwnd != NULL) {
\r
2096 DestroyWindow(buttonDesc[i].hwnd);
\r
2097 buttonDesc[i].hwnd = NULL;
\r
2099 if (appData.showButtonBar) {
\r
2100 buttonDesc[i].hwnd =
\r
2101 CreateWindow("BUTTON", buttonDesc[i].label,
\r
2102 WS_VISIBLE | WS_CHILD | BS_PUSHBUTTON,
\r
2103 boardRect.right - BUTTON_WIDTH*(N_BUTTONS-i),
\r
2104 messageRect.top, BUTTON_WIDTH, messageSize.cy, hwndMain,
\r
2105 (HMENU) buttonDesc[i].id,
\r
2106 (HINSTANCE) GetWindowLong(hwndMain, GWL_HINSTANCE), NULL);
\r
2108 SendMessage(buttonDesc[i].hwnd, WM_SETFONT,
\r
2109 (WPARAM)font[boardSize][MESSAGE_FONT]->hf,
\r
2110 MAKELPARAM(FALSE, 0));
\r
2112 if (buttonDesc[i].id == IDM_Pause)
\r
2113 hwndPause = buttonDesc[i].hwnd;
\r
2114 buttonDesc[i].wndproc = (WNDPROC)
\r
2115 SetWindowLong(buttonDesc[i].hwnd, GWL_WNDPROC, (LONG) ButtonProc);
\r
2118 if (gridPen != NULL) DeleteObject(gridPen);
\r
2119 if (highlightPen != NULL) DeleteObject(highlightPen);
\r
2120 if (premovePen != NULL) DeleteObject(premovePen);
\r
2121 if (lineGap != 0) {
\r
2122 logbrush.lbStyle = BS_SOLID;
\r
2123 logbrush.lbColor = RGB(0, 0, 0); /* grid pen color = black */
\r
2125 ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,
\r
2126 lineGap, &logbrush, 0, NULL);
\r
2127 logbrush.lbColor = highlightSquareColor;
\r
2129 ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,
\r
2130 lineGap, &logbrush, 0, NULL);
\r
2132 logbrush.lbColor = premoveHighlightColor;
\r
2134 ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,
\r
2135 lineGap, &logbrush, 0, NULL);
\r
2137 /* [HGM] Loop had to be split in part for vert. and hor. lines */
\r
2138 for (i = 0; i < BOARD_HEIGHT + 1; i++) {
\r
2139 gridEndpoints[i*2].x = boardRect.left + lineGap / 2;
\r
2140 gridEndpoints[i*2].y = gridEndpoints[i*2 + 1].y =
\r
2141 boardRect.top + lineGap / 2 + (i * (squareSize + lineGap));
\r
2142 gridEndpoints[i*2 + 1].x = boardRect.left + lineGap / 2 +
\r
2143 BOARD_WIDTH * (squareSize + lineGap);
\r
2144 gridVertexCounts[i*2] = gridVertexCounts[i*2 + 1] = 2;
\r
2146 for (i = 0; i < BOARD_WIDTH + 1; i++) {
\r
2147 gridEndpoints[i*2 + BOARD_HEIGHT*2 + 2].y = boardRect.top + lineGap / 2;
\r
2148 gridEndpoints[i*2 + BOARD_HEIGHT*2 + 2].x =
\r
2149 gridEndpoints[i*2 + 1 + BOARD_HEIGHT*2 + 2].x = boardRect.left +
\r
2150 lineGap / 2 + (i * (squareSize + lineGap));
\r
2151 gridEndpoints[i*2 + 1 + BOARD_HEIGHT*2 + 2].y =
\r
2152 boardRect.top + BOARD_HEIGHT * (squareSize + lineGap);
\r
2153 gridVertexCounts[i*2] = gridVertexCounts[i*2 + 1] = 2;
\r
2157 /* [HGM] Licensing requirement */
\r
2159 if(gameInfo.variant == VariantGothic) GothicPopUp( GOTHIC, VariantGothic); else
\r
2162 if(gameInfo.variant == VariantFalcon) GothicPopUp( FALCON, VariantFalcon); else
\r
2164 GothicPopUp( "", VariantNormal);
\r
2167 /* if (boardSize == oldBoardSize) return; [HGM] variant might have changed */
\r
2169 /* Load piece bitmaps for this board size */
\r
2170 for (i=0; i<=2; i++) {
\r
2171 for (piece = WhitePawn;
\r
2172 (int) piece < (int) BlackPawn;
\r
2173 piece = (ChessSquare) ((int) piece + 1)) {
\r
2174 if (pieceBitmap[i][piece] != NULL)
\r
2175 DeleteObject(pieceBitmap[i][piece]);
\r
2179 fontBitmapSquareSize = 0; /* [HGM] render: make sure pieces will be recreated, as we might need others now */
\r
2180 // Orthodox Chess pieces
\r
2181 pieceBitmap[0][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "s");
\r
2182 pieceBitmap[0][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "s");
\r
2183 pieceBitmap[0][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "s");
\r
2184 pieceBitmap[0][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "s");
\r
2185 pieceBitmap[0][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "s");
\r
2186 pieceBitmap[1][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "o");
\r
2187 pieceBitmap[1][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "o");
\r
2188 pieceBitmap[1][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "o");
\r
2189 pieceBitmap[1][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "o");
\r
2190 pieceBitmap[1][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "o");
\r
2191 pieceBitmap[2][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "w");
\r
2192 pieceBitmap[2][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "w");
\r
2193 pieceBitmap[2][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "w");
\r
2194 pieceBitmap[2][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "w");
\r
2195 pieceBitmap[2][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "w");
\r
2196 if( !strcmp(appData.variant, "shogi") && (squareSize==72 || squareSize==49)) {
\r
2197 // in Shogi, Hijack the unused Queen for Lance
\r
2198 pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "s");
\r
2199 pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "o");
\r
2200 pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "w");
\r
2202 pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "s");
\r
2203 pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "o");
\r
2204 pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "w");
\r
2207 if(squareSize <= 72 && squareSize >= 33) {
\r
2208 /* A & C are available in most sizes now */
\r
2209 if(squareSize != 49 && squareSize != 72 && squareSize != 33) { // Vortex-like
\r
2210 pieceBitmap[0][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "s");
\r
2211 pieceBitmap[1][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "o");
\r
2212 pieceBitmap[2][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "w");
\r
2213 pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "s");
\r
2214 pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "o");
\r
2215 pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "w");
\r
2216 pieceBitmap[0][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "s");
\r
2217 pieceBitmap[1][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "o");
\r
2218 pieceBitmap[2][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "w");
\r
2219 pieceBitmap[0][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "s");
\r
2220 pieceBitmap[1][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "o");
\r
2221 pieceBitmap[2][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "w");
\r
2222 } else { // Smirf-like
\r
2223 pieceBitmap[0][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "s");
\r
2224 pieceBitmap[1][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "o");
\r
2225 pieceBitmap[2][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "w");
\r
2227 if(gameInfo.variant == VariantGothic) { // Vortex-like
\r
2228 pieceBitmap[0][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "s");
\r
2229 pieceBitmap[1][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "o");
\r
2230 pieceBitmap[2][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "w");
\r
2231 } else { // WinBoard standard
\r
2232 pieceBitmap[0][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "s");
\r
2233 pieceBitmap[1][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "o");
\r
2234 pieceBitmap[2][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "w");
\r
2239 if(squareSize==72 || squareSize==49 || squareSize==33) { /* experiment with some home-made bitmaps */
\r
2240 pieceBitmap[0][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "s");
\r
2241 pieceBitmap[1][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "o");
\r
2242 pieceBitmap[2][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "w");
\r
2243 pieceBitmap[0][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "s");
\r
2244 pieceBitmap[1][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "o");
\r
2245 pieceBitmap[2][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "w");
\r
2246 pieceBitmap[0][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "s");
\r
2247 pieceBitmap[1][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "o");
\r
2248 pieceBitmap[2][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "w");
\r
2249 pieceBitmap[0][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "s");
\r
2250 pieceBitmap[1][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "o");
\r
2251 pieceBitmap[2][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "w");
\r
2252 pieceBitmap[0][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "s");
\r
2253 pieceBitmap[1][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "o");
\r
2254 pieceBitmap[2][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "w");
\r
2255 pieceBitmap[0][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "s");
\r
2256 pieceBitmap[1][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "o");
\r
2257 pieceBitmap[2][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "w");
\r
2258 pieceBitmap[0][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "s");
\r
2259 pieceBitmap[1][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "o");
\r
2260 pieceBitmap[2][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "w");
\r
2261 pieceBitmap[0][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "s");
\r
2262 pieceBitmap[1][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "o");
\r
2263 pieceBitmap[2][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "w");
\r
2264 pieceBitmap[0][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "s");
\r
2265 pieceBitmap[1][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "o");
\r
2266 pieceBitmap[2][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "w");
\r
2267 pieceBitmap[0][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "s");
\r
2268 pieceBitmap[1][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "o");
\r
2269 pieceBitmap[2][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "w");
\r
2271 if(gameInfo.variant == VariantShogi) { /* promoted Gold represemtations */
\r
2272 pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "wp", squareSize, "s");
\r
2273 pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "wp", squareSize, "o");
\r
2274 pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "w", squareSize, "w");
\r
2275 pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "wn", squareSize, "s");
\r
2276 pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "wn", squareSize, "o");
\r
2277 pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "w", squareSize, "w");
\r
2278 pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "ws", squareSize, "s");
\r
2279 pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "ws", squareSize, "o");
\r
2280 pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "w", squareSize, "w");
\r
2281 pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "wl", squareSize, "s");
\r
2282 pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "wl", squareSize, "o");
\r
2283 pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "w", squareSize, "w");
\r
2285 pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "s");
\r
2286 pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "o");
\r
2287 pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "w");
\r
2288 pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "s");
\r
2289 pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "o");
\r
2290 pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "w");
\r
2291 pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "s");
\r
2292 pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "o");
\r
2293 pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "w");
\r
2294 pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "s");
\r
2295 pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "o");
\r
2296 pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "w");
\r
2299 } else { /* other size, no special bitmaps available. Use smaller symbols */
\r
2300 if((int)boardSize < 2) minorSize = sizeInfo[0].squareSize;
\r
2301 else minorSize = sizeInfo[(int)boardSize - 2].squareSize;
\r
2302 pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "s");
\r
2303 pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "o");
\r
2304 pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "w");
\r
2305 pieceBitmap[0][WhiteCardinal] = DoLoadBitmap(hInst, "b", minorSize, "s");
\r
2306 pieceBitmap[1][WhiteCardinal] = DoLoadBitmap(hInst, "b", minorSize, "o");
\r
2307 pieceBitmap[2][WhiteCardinal] = DoLoadBitmap(hInst, "b", minorSize, "w");
\r
2308 pieceBitmap[0][WhiteDragon] = DoLoadBitmap(hInst, "r", minorSize, "s");
\r
2309 pieceBitmap[1][WhiteDragon] = DoLoadBitmap(hInst, "r", minorSize, "o");
\r
2310 pieceBitmap[2][WhiteDragon] = DoLoadBitmap(hInst, "r", minorSize, "w");
\r
2311 pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "s");
\r
2312 pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "o");
\r
2313 pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "w");
\r
2317 if(gameInfo.variant == VariantShogi && squareSize == 58)
\r
2318 /* special Shogi support in this size */
\r
2319 { for (i=0; i<=2; i++) { /* replace all bitmaps */
\r
2320 for (piece = WhitePawn;
\r
2321 (int) piece < (int) BlackPawn;
\r
2322 piece = (ChessSquare) ((int) piece + 1)) {
\r
2323 if (pieceBitmap[i][piece] != NULL)
\r
2324 DeleteObject(pieceBitmap[i][piece]);
\r
2327 pieceBitmap[0][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "o");
\r
2328 pieceBitmap[0][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "o");
\r
2329 pieceBitmap[0][WhiteBishop] = DoLoadBitmap(hInst, "sb", squareSize, "o");
\r
2330 pieceBitmap[0][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "o");
\r
2331 pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "o");
\r
2332 pieceBitmap[0][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "o");
\r
2333 pieceBitmap[0][WhiteFerz] = DoLoadBitmap(hInst, "sf", squareSize, "o");
\r
2334 pieceBitmap[0][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "o");
\r
2335 pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "su", squareSize, "o");
\r
2336 pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "sh", squareSize, "o");
\r
2337 pieceBitmap[0][WhiteCardinal] = DoLoadBitmap(hInst, "sa", squareSize, "o");
\r
2338 pieceBitmap[0][WhiteDragon] = DoLoadBitmap(hInst, "sc", squareSize, "o");
\r
2339 pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "sg", squareSize, "o");
\r
2340 pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "ss", squareSize, "o");
\r
2341 pieceBitmap[1][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "o");
\r
2342 pieceBitmap[1][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "o");
\r
2343 pieceBitmap[1][WhiteBishop] = DoLoadBitmap(hInst, "sb", squareSize, "o");
\r
2344 pieceBitmap[1][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "o");
\r
2345 pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "o");
\r
2346 pieceBitmap[1][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "o");
\r
2347 pieceBitmap[1][WhiteFerz] = DoLoadBitmap(hInst, "sf", squareSize, "o");
\r
2348 pieceBitmap[1][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "o");
\r
2349 pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "su", squareSize, "o");
\r
2350 pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "sh", squareSize, "o");
\r
2351 pieceBitmap[1][WhiteCardinal] = DoLoadBitmap(hInst, "sa", squareSize, "o");
\r
2352 pieceBitmap[1][WhiteDragon] = DoLoadBitmap(hInst, "sc", squareSize, "o");
\r
2353 pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "sg", squareSize, "o");
\r
2354 pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "ss", squareSize, "o");
\r
2355 pieceBitmap[2][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "w");
\r
2356 pieceBitmap[2][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "w");
\r
2357 pieceBitmap[2][WhiteBishop] = DoLoadBitmap(hInst, "sr", squareSize, "w");
\r
2358 pieceBitmap[2][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "w");
\r
2359 pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "w");
\r
2360 pieceBitmap[2][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "w");
\r
2361 pieceBitmap[2][WhiteFerz] = DoLoadBitmap(hInst, "sw", squareSize, "w");
\r
2362 pieceBitmap[2][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "w");
\r
2363 pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "sp", squareSize, "w");
\r
2364 pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "sn", squareSize, "w");
\r
2365 pieceBitmap[2][WhiteCardinal] = DoLoadBitmap(hInst, "sr", squareSize, "w");
\r
2366 pieceBitmap[2][WhiteDragon] = DoLoadBitmap(hInst, "sr", squareSize, "w");
\r
2367 pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "sl", squareSize, "w");
\r
2368 pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "sw", squareSize, "w");
\r
2374 PieceBitmap(ChessSquare p, int kind)
\r
2376 if ((int) p >= (int) BlackPawn)
\r
2377 p = (ChessSquare) ((int) p - (int) BlackPawn + (int) WhitePawn);
\r
2379 return pieceBitmap[kind][(int) p];
\r
2382 /***************************************************************/
\r
2384 #define MIN(a,b) (((a) < (b)) ? (a) : (b))
\r
2385 #define MAX(a,b) (((a) > (b)) ? (a) : (b))
\r
2387 #define MIN3(a,b,c) (((a) < (b) && (a) < (c)) ? (a) : (((b) < (a) && (b) < (c)) ? (b) : (c)))
\r
2388 #define MAX3(a,b,c) (((a) > (b) && (a) > (c)) ? (a) : (((b) > (a) && (b) > (c)) ? (b) : (c)))
\r
2392 SquareToPos(int row, int column, int * x, int * y)
\r
2395 *x = boardRect.left + lineGap + ((BOARD_WIDTH-1)-column) * (squareSize + lineGap);
\r
2396 *y = boardRect.top + lineGap + row * (squareSize + lineGap);
\r
2398 *x = boardRect.left + lineGap + column * (squareSize + lineGap);
\r
2399 *y = boardRect.top + lineGap + ((BOARD_HEIGHT-1)-row) * (squareSize + lineGap);
\r
2404 DrawCoordsOnDC(HDC hdc)
\r
2406 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
2407 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
2408 char str[2] = { NULLCHAR, NULLCHAR };
\r
2409 int oldMode, oldAlign, x, y, start, i;
\r
2413 if (!appData.showCoords)
\r
2416 start = flipView ? 1-(ONE!='1') : 23+(ONE!='1')-BOARD_HEIGHT;
\r
2418 oldBrush = SelectObject(hdc, GetStockObject(BLACK_BRUSH));
\r
2419 oldMode = SetBkMode(hdc, (appData.monoMode ? OPAQUE : TRANSPARENT));
\r
2420 oldAlign = GetTextAlign(hdc);
\r
2421 oldFont = SelectObject(hdc, font[boardSize][COORD_FONT]->hf);
\r
2423 y = boardRect.top + lineGap;
\r
2424 x = boardRect.left + lineGap + gameInfo.holdingsWidth*(squareSize + lineGap);
\r
2426 SetTextAlign(hdc, TA_LEFT|TA_TOP);
\r
2427 for (i = 0; i < BOARD_HEIGHT; i++) {
\r
2428 str[0] = files[start + i];
\r
2429 ExtTextOut(hdc, x + 2, y + 1, 0, NULL, str, 1, NULL);
\r
2430 y += squareSize + lineGap;
\r
2433 start = flipView ? 12-(BOARD_RGHT-BOARD_LEFT) : 12;
\r
2435 SetTextAlign(hdc, TA_RIGHT|TA_BOTTOM);
\r
2436 for (i = 0; i < BOARD_RGHT - BOARD_LEFT; i++) {
\r
2437 str[0] = ranks[start + i];
\r
2438 ExtTextOut(hdc, x + squareSize - 2, y - 1, 0, NULL, str, 1, NULL);
\r
2439 x += squareSize + lineGap;
\r
2442 SelectObject(hdc, oldBrush);
\r
2443 SetBkMode(hdc, oldMode);
\r
2444 SetTextAlign(hdc, oldAlign);
\r
2445 SelectObject(hdc, oldFont);
\r
2449 DrawGridOnDC(HDC hdc)
\r
2453 if (lineGap != 0) {
\r
2454 oldPen = SelectObject(hdc, gridPen);
\r
2455 PolyPolyline(hdc, gridEndpoints, gridVertexCounts, BOARD_WIDTH+BOARD_HEIGHT + 2);
\r
2456 SelectObject(hdc, oldPen);
\r
2460 #define HIGHLIGHT_PEN 0
\r
2461 #define PREMOVE_PEN 1
\r
2464 DrawHighlightOnDC(HDC hdc, BOOLEAN on, int x, int y, int pen)
\r
2467 HPEN oldPen, hPen;
\r
2468 if (lineGap == 0) return;
\r
2470 x1 = boardRect.left +
\r
2471 lineGap/2 + ((BOARD_WIDTH-1)-x) * (squareSize + lineGap);
\r
2472 y1 = boardRect.top +
\r
2473 lineGap/2 + y * (squareSize + lineGap);
\r
2475 x1 = boardRect.left +
\r
2476 lineGap/2 + x * (squareSize + lineGap);
\r
2477 y1 = boardRect.top +
\r
2478 lineGap/2 + ((BOARD_HEIGHT-1)-y) * (squareSize + lineGap);
\r
2480 hPen = pen ? premovePen : highlightPen;
\r
2481 oldPen = SelectObject(hdc, on ? hPen : gridPen);
\r
2482 MoveToEx(hdc, x1, y1, NULL);
\r
2483 LineTo(hdc, x1 + squareSize + lineGap, y1);
\r
2484 LineTo(hdc, x1 + squareSize + lineGap, y1 + squareSize + lineGap);
\r
2485 LineTo(hdc, x1, y1 + squareSize + lineGap);
\r
2486 LineTo(hdc, x1, y1);
\r
2487 SelectObject(hdc, oldPen);
\r
2491 DrawHighlightsOnDC(HDC hdc)
\r
2494 for (i=0; i<2; i++) {
\r
2495 if (highlightInfo.sq[i].x >= 0 && highlightInfo.sq[i].y >= 0)
\r
2496 DrawHighlightOnDC(hdc, TRUE,
\r
2497 highlightInfo.sq[i].x, highlightInfo.sq[i].y,
\r
2500 for (i=0; i<2; i++) {
\r
2501 if (premoveHighlightInfo.sq[i].x >= 0 &&
\r
2502 premoveHighlightInfo.sq[i].y >= 0) {
\r
2503 DrawHighlightOnDC(hdc, TRUE,
\r
2504 premoveHighlightInfo.sq[i].x,
\r
2505 premoveHighlightInfo.sq[i].y,
\r
2511 /* Note: sqcolor is used only in monoMode */
\r
2512 /* Note that this code is largely duplicated in woptions.c,
\r
2513 function DrawSampleSquare, so that needs to be updated too */
\r
2515 DrawPieceOnDC(HDC hdc, ChessSquare piece, int color, int sqcolor, int x, int y, HDC tmphdc)
\r
2517 HBITMAP oldBitmap;
\r
2521 if (appData.blindfold) return;
\r
2523 /* [AS] Use font-based pieces if needed */
\r
2524 if( fontBitmapSquareSize >= 0 && squareSize > 32 ) {
\r
2525 /* Create piece bitmaps, or do nothing if piece set is up to date */
\r
2526 CreatePiecesFromFont();
\r
2528 if( fontBitmapSquareSize == squareSize ) {
\r
2529 int index = TranslatePieceToFontPiece(piece);
\r
2531 SelectObject( tmphdc, hPieceMask[ index ] );
\r
2535 squareSize, squareSize,
\r
2540 SelectObject( tmphdc, hPieceFace[ index ] );
\r
2544 squareSize, squareSize,
\r
2553 if (appData.monoMode) {
\r
2554 SelectObject(tmphdc, PieceBitmap(piece,
\r
2555 color == sqcolor ? OUTLINE_PIECE : SOLID_PIECE));
\r
2556 BitBlt(hdc, x, y, squareSize, squareSize, tmphdc, 0, 0,
\r
2557 sqcolor ? SRCCOPY : NOTSRCCOPY);
\r
2559 tmpSize = squareSize;
\r
2561 ((piece >= (int)WhiteNightrider && piece <= WhiteGrasshopper) ||
\r
2562 (piece >= (int)BlackNightrider && piece <= BlackGrasshopper)) ) {
\r
2563 /* [HGM] no bitmap available for promoted pieces in Crazyhouse */
\r
2564 /* Bitmaps of smaller size are substituted, but we have to align them */
\r
2565 x += (squareSize - minorSize)>>1;
\r
2566 y += squareSize - minorSize - 2;
\r
2567 tmpSize = minorSize;
\r
2569 if (color || appData.allWhite ) {
\r
2570 oldBitmap = SelectObject(tmphdc, PieceBitmap(piece, WHITE_PIECE));
\r
2572 oldBrush = SelectObject(hdc, whitePieceBrush);
\r
2573 else oldBrush = SelectObject(hdc, blackPieceBrush);
\r
2574 if(appData.upsideDown && color==flipView)
\r
2575 StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, 0x00B8074A);
\r
2577 BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, 0x00B8074A);
\r
2578 /* Use black for outline of white pieces */
\r
2579 SelectObject(tmphdc, PieceBitmap(piece, OUTLINE_PIECE));
\r
2580 if(appData.upsideDown && color==flipView)
\r
2581 StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, SRCAND);
\r
2583 BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, SRCAND);
\r
2585 /* Use square color for details of black pieces */
\r
2586 oldBitmap = SelectObject(tmphdc, PieceBitmap(piece, SOLID_PIECE));
\r
2587 oldBrush = SelectObject(hdc, blackPieceBrush);
\r
2588 if(appData.upsideDown && !flipView)
\r
2589 StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, 0x00B8074A);
\r
2591 BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, 0x00B8074A);
\r
2593 SelectObject(hdc, oldBrush);
\r
2594 SelectObject(tmphdc, oldBitmap);
\r
2598 /* [AS] Compute a drawing mode for a square, based on specified settings (see DrawTile) */
\r
2599 int GetBackTextureMode( int algo )
\r
2601 int result = BACK_TEXTURE_MODE_DISABLED;
\r
2605 case BACK_TEXTURE_MODE_PLAIN:
\r
2606 result = 1; /* Always use identity map */
\r
2608 case BACK_TEXTURE_MODE_FULL_RANDOM:
\r
2609 result = 1 + (myrandom() % 3); /* Pick a transformation at random */
\r
2617 [AS] Compute and save texture drawing info, otherwise we may not be able
\r
2618 to handle redraws cleanly (as random numbers would always be different).
\r
2620 VOID RebuildTextureSquareInfo()
\r
2630 ZeroMemory( &backTextureSquareInfo, sizeof(backTextureSquareInfo) );
\r
2632 if( liteBackTexture != NULL ) {
\r
2633 if( GetObject( liteBackTexture, sizeof(bi), &bi ) > 0 ) {
\r
2634 lite_w = bi.bmWidth;
\r
2635 lite_h = bi.bmHeight;
\r
2639 if( darkBackTexture != NULL ) {
\r
2640 if( GetObject( darkBackTexture, sizeof(bi), &bi ) > 0 ) {
\r
2641 dark_w = bi.bmWidth;
\r
2642 dark_h = bi.bmHeight;
\r
2646 for( row=0; row<BOARD_HEIGHT; row++ ) {
\r
2647 for( col=0; col<BOARD_WIDTH; col++ ) {
\r
2648 if( (col + row) & 1 ) {
\r
2650 if( lite_w >= squareSize && lite_h >= squareSize ) {
\r
2651 backTextureSquareInfo[row][col].x = col * (lite_w - squareSize) / (BOARD_WIDTH-1); /* [HGM] divide by size-1 in stead of size! */
\r
2652 backTextureSquareInfo[row][col].y = (BOARD_HEIGHT-1-row) * (lite_h - squareSize) / (BOARD_HEIGHT-1);
\r
2653 backTextureSquareInfo[row][col].mode = GetBackTextureMode(liteBackTextureMode);
\r
2658 if( dark_w >= squareSize && dark_h >= squareSize ) {
\r
2659 backTextureSquareInfo[row][col].x = col * (dark_w - squareSize) / (BOARD_WIDTH-1);
\r
2660 backTextureSquareInfo[row][col].y = (BOARD_HEIGHT-1-row) * (dark_h - squareSize) / (BOARD_HEIGHT-1);
\r
2661 backTextureSquareInfo[row][col].mode = GetBackTextureMode(darkBackTextureMode);
\r
2668 /* [AS] Arrow highlighting support */
\r
2670 static int A_WIDTH = 5; /* Width of arrow body */
\r
2672 #define A_HEIGHT_FACTOR 6 /* Length of arrow "point", relative to body width */
\r
2673 #define A_WIDTH_FACTOR 3 /* Width of arrow "point", relative to body width */
\r
2675 static double Sqr( double x )
\r
2680 static int Round( double x )
\r
2682 return (int) (x + 0.5);
\r
2685 /* Draw an arrow between two points using current settings */
\r
2686 VOID DrawArrowBetweenPoints( HDC hdc, int s_x, int s_y, int d_x, int d_y )
\r
2689 double dx, dy, j, k, x, y;
\r
2691 if( d_x == s_x ) {
\r
2692 int h = (d_y > s_y) ? +A_WIDTH*A_HEIGHT_FACTOR : -A_WIDTH*A_HEIGHT_FACTOR;
\r
2694 arrow[0].x = s_x + A_WIDTH;
\r
2697 arrow[1].x = s_x + A_WIDTH;
\r
2698 arrow[1].y = d_y - h;
\r
2700 arrow[2].x = s_x + A_WIDTH*A_WIDTH_FACTOR;
\r
2701 arrow[2].y = d_y - h;
\r
2706 arrow[4].x = s_x - A_WIDTH*A_WIDTH_FACTOR;
\r
2707 arrow[4].y = d_y - h;
\r
2709 arrow[5].x = s_x - A_WIDTH;
\r
2710 arrow[5].y = d_y - h;
\r
2712 arrow[6].x = s_x - A_WIDTH;
\r
2715 else if( d_y == s_y ) {
\r
2716 int w = (d_x > s_x) ? +A_WIDTH*A_HEIGHT_FACTOR : -A_WIDTH*A_HEIGHT_FACTOR;
\r
2719 arrow[0].y = s_y + A_WIDTH;
\r
2721 arrow[1].x = d_x - w;
\r
2722 arrow[1].y = s_y + A_WIDTH;
\r
2724 arrow[2].x = d_x - w;
\r
2725 arrow[2].y = s_y + A_WIDTH*A_WIDTH_FACTOR;
\r
2730 arrow[4].x = d_x - w;
\r
2731 arrow[4].y = s_y - A_WIDTH*A_WIDTH_FACTOR;
\r
2733 arrow[5].x = d_x - w;
\r
2734 arrow[5].y = s_y - A_WIDTH;
\r
2737 arrow[6].y = s_y - A_WIDTH;
\r
2740 /* [AS] Needed a lot of paper for this! :-) */
\r
2741 dy = (double) (d_y - s_y) / (double) (d_x - s_x);
\r
2742 dx = (double) (s_x - d_x) / (double) (s_y - d_y);
\r
2744 j = sqrt( Sqr(A_WIDTH) / (1.0 + Sqr(dx)) );
\r
2746 k = sqrt( Sqr(A_WIDTH*A_HEIGHT_FACTOR) / (1.0 + Sqr(dy)) );
\r
2751 arrow[0].x = Round(x - j);
\r
2752 arrow[0].y = Round(y + j*dx);
\r
2754 arrow[1].x = Round(x + j);
\r
2755 arrow[1].y = Round(y - j*dx);
\r
2758 x = (double) d_x - k;
\r
2759 y = (double) d_y - k*dy;
\r
2762 x = (double) d_x + k;
\r
2763 y = (double) d_y + k*dy;
\r
2766 arrow[2].x = Round(x + j);
\r
2767 arrow[2].y = Round(y - j*dx);
\r
2769 arrow[3].x = Round(x + j*A_WIDTH_FACTOR);
\r
2770 arrow[3].y = Round(y - j*A_WIDTH_FACTOR*dx);
\r
2775 arrow[5].x = Round(x - j*A_WIDTH_FACTOR);
\r
2776 arrow[5].y = Round(y + j*A_WIDTH_FACTOR*dx);
\r
2778 arrow[6].x = Round(x - j);
\r
2779 arrow[6].y = Round(y + j*dx);
\r
2782 Polygon( hdc, arrow, 7 );
\r
2785 /* [AS] Draw an arrow between two squares */
\r
2786 VOID DrawArrowBetweenSquares( HDC hdc, int s_col, int s_row, int d_col, int d_row )
\r
2788 int s_x, s_y, d_x, d_y;
\r
2795 if( s_col == d_col && s_row == d_row ) {
\r
2799 /* Get source and destination points */
\r
2800 SquareToPos( s_row, s_col, &s_x, &s_y);
\r
2801 SquareToPos( d_row, d_col, &d_x, &d_y);
\r
2804 d_y += squareSize / 4;
\r
2806 else if( d_y < s_y ) {
\r
2807 d_y += 3 * squareSize / 4;
\r
2810 d_y += squareSize / 2;
\r
2814 d_x += squareSize / 4;
\r
2816 else if( d_x < s_x ) {
\r
2817 d_x += 3 * squareSize / 4;
\r
2820 d_x += squareSize / 2;
\r
2823 s_x += squareSize / 2;
\r
2824 s_y += squareSize / 2;
\r
2826 /* Adjust width */
\r
2827 A_WIDTH = squareSize / 14;
\r
2830 stLB.lbStyle = BS_SOLID;
\r
2831 stLB.lbColor = appData.highlightArrowColor;
\r
2834 hpen = CreatePen( PS_SOLID, 2, RGB(0x00,0x00,0x00) );
\r
2835 holdpen = SelectObject( hdc, hpen );
\r
2836 hbrush = CreateBrushIndirect( &stLB );
\r
2837 holdbrush = SelectObject( hdc, hbrush );
\r
2839 DrawArrowBetweenPoints( hdc, s_x, s_y, d_x, d_y );
\r
2841 SelectObject( hdc, holdpen );
\r
2842 SelectObject( hdc, holdbrush );
\r
2843 DeleteObject( hpen );
\r
2844 DeleteObject( hbrush );
\r
2847 BOOL HasHighlightInfo()
\r
2849 BOOL result = FALSE;
\r
2851 if( highlightInfo.sq[0].x >= 0 && highlightInfo.sq[0].y >= 0 &&
\r
2852 highlightInfo.sq[1].x >= 0 && highlightInfo.sq[1].y >= 0 )
\r
2860 BOOL IsDrawArrowEnabled()
\r
2862 BOOL result = FALSE;
\r
2864 if( appData.highlightMoveWithArrow && squareSize >= 32 ) {
\r
2871 VOID DrawArrowHighlight( HDC hdc )
\r
2873 if( IsDrawArrowEnabled() && HasHighlightInfo() ) {
\r
2874 DrawArrowBetweenSquares( hdc,
\r
2875 highlightInfo.sq[0].x, highlightInfo.sq[0].y,
\r
2876 highlightInfo.sq[1].x, highlightInfo.sq[1].y );
\r
2880 HRGN GetArrowHighlightClipRegion( HDC hdc )
\r
2882 HRGN result = NULL;
\r
2884 if( HasHighlightInfo() ) {
\r
2885 int x1, y1, x2, y2;
\r
2886 int sx, sy, dx, dy;
\r
2888 SquareToPos(highlightInfo.sq[0].y, highlightInfo.sq[0].x, &x1, &y1 );
\r
2889 SquareToPos(highlightInfo.sq[1].y, highlightInfo.sq[1].x, &x2, &y2 );
\r
2891 sx = MIN( x1, x2 );
\r
2892 sy = MIN( y1, y2 );
\r
2893 dx = MAX( x1, x2 ) + squareSize;
\r
2894 dy = MAX( y1, y2 ) + squareSize;
\r
2896 result = CreateRectRgn( sx, sy, dx, dy );
\r
2903 Warning: this function modifies the behavior of several other functions.
\r
2905 Basically, Winboard is optimized to avoid drawing the whole board if not strictly
\r
2906 needed. Unfortunately, the decision whether or not to perform a full or partial
\r
2907 repaint is scattered all over the place, which is not good for features such as
\r
2908 "arrow highlighting" that require a full repaint of the board.
\r
2910 So, I've tried to patch the code where I thought it made sense (e.g. after or during
\r
2911 user interaction, when speed is not so important) but especially to avoid errors
\r
2912 in the displayed graphics.
\r
2914 In such patched places, I always try refer to this function so there is a single
\r
2915 place to maintain knowledge.
\r
2917 To restore the original behavior, just return FALSE unconditionally.
\r
2919 BOOL IsFullRepaintPreferrable()
\r
2921 BOOL result = FALSE;
\r
2923 if( (appData.highlightLastMove || appData.highlightDragging) && IsDrawArrowEnabled() ) {
\r
2924 /* Arrow may appear on the board */
\r
2932 This function is called by DrawPosition to know whether a full repaint must
\r
2935 Only DrawPosition may directly call this function, which makes use of
\r
2936 some state information. Other function should call DrawPosition specifying
\r
2937 the repaint flag, and can use IsFullRepaintPreferrable if needed.
\r
2939 BOOL DrawPositionNeedsFullRepaint()
\r
2941 BOOL result = FALSE;
\r
2944 Probably a slightly better policy would be to trigger a full repaint
\r
2945 when animInfo.piece changes state (i.e. empty -> non-empty and viceversa),
\r
2946 but animation is fast enough that it's difficult to notice.
\r
2948 if( animInfo.piece == EmptySquare ) {
\r
2949 if( (appData.highlightLastMove || appData.highlightDragging) && IsDrawArrowEnabled() /*&& HasHighlightInfo()*/ ) {
\r
2958 DrawBoardOnDC(HDC hdc, Board board, HDC tmphdc)
\r
2960 int row, column, x, y, square_color, piece_color;
\r
2961 ChessSquare piece;
\r
2963 HDC texture_hdc = NULL;
\r
2965 /* [AS] Initialize background textures if needed */
\r
2966 if( liteBackTexture != NULL || darkBackTexture != NULL ) {
\r
2967 static int backTextureBoardSize; /* [HGM] boardsize: also new texture if board format changed */
\r
2968 if( backTextureSquareSize != squareSize
\r
2969 || backTextureBoardSize != BOARD_WIDTH+BOARD_FILES*BOARD_HEIGHT) {
\r
2970 backTextureBoardSize = BOARD_WIDTH+BOARD_FILES*BOARD_HEIGHT;
\r
2971 backTextureSquareSize = squareSize;
\r
2972 RebuildTextureSquareInfo();
\r
2975 texture_hdc = CreateCompatibleDC( hdc );
\r
2978 for (row = 0; row < BOARD_HEIGHT; row++) {
\r
2979 for (column = 0; column < BOARD_WIDTH; column++) {
\r
2981 SquareToPos(row, column, &x, &y);
\r
2983 piece = board[row][column];
\r
2985 square_color = ((column + row) % 2) == 1;
\r
2986 if( gameInfo.variant == VariantXiangqi ) {
\r
2987 square_color = !InPalace(row, column);
\r
2988 if(BOARD_HEIGHT&1) { if(row==BOARD_HEIGHT/2) square_color ^= 1; }
\r
2989 else if(row < BOARD_HEIGHT/2) square_color ^= 1;
\r
2991 piece_color = (int) piece < (int) BlackPawn;
\r
2994 /* [HGM] holdings file: light square or black */
\r
2995 if(column == BOARD_LEFT-2) {
\r
2996 if( row > BOARD_HEIGHT - gameInfo.holdingsSize - 1 )
\r
2999 DisplayHoldingsCount(hdc, x, y, 0, 0); /* black out */
\r
3003 if(column == BOARD_RGHT + 1 ) {
\r
3004 if( row < gameInfo.holdingsSize )
\r
3007 DisplayHoldingsCount(hdc, x, y, 0, 0);
\r
3011 if(column == BOARD_LEFT-1 ) /* left align */
\r
3012 DisplayHoldingsCount(hdc, x, y, flipView, (int) board[row][column]);
\r
3013 else if( column == BOARD_RGHT) /* right align */
\r
3014 DisplayHoldingsCount(hdc, x, y, !flipView, (int) board[row][column]);
\r
3016 if (appData.monoMode) {
\r
3017 if (piece == EmptySquare) {
\r
3018 BitBlt(hdc, x, y, squareSize, squareSize, 0, 0, 0,
\r
3019 square_color ? WHITENESS : BLACKNESS);
\r
3021 DrawPieceOnDC(hdc, piece, piece_color, square_color, x, y, tmphdc);
\r
3024 else if( backTextureSquareInfo[row][column].mode > 0 ) {
\r
3025 /* [AS] Draw the square using a texture bitmap */
\r
3026 HBITMAP hbm = SelectObject( texture_hdc, square_color ? liteBackTexture : darkBackTexture );
\r
3027 int r = row, c = column; // [HGM] do not flip board in flipView
\r
3028 if(flipView) { r = BOARD_HEIGHT-1 - r; c = BOARD_WIDTH-1 - c; }
\r
3031 squareSize, squareSize,
\r
3034 backTextureSquareInfo[r][c].mode,
\r
3035 backTextureSquareInfo[r][c].x,
\r
3036 backTextureSquareInfo[r][c].y );
\r
3038 SelectObject( texture_hdc, hbm );
\r
3040 if (piece != EmptySquare) {
\r
3041 DrawPieceOnDC(hdc, piece, piece_color, -1, x, y, tmphdc);
\r
3045 HBRUSH brush = square_color ? lightSquareBrush : darkSquareBrush;
\r
3047 oldBrush = SelectObject(hdc, brush );
\r
3048 BitBlt(hdc, x, y, squareSize, squareSize, 0, 0, 0, PATCOPY);
\r
3049 SelectObject(hdc, oldBrush);
\r
3050 if (piece != EmptySquare)
\r
3051 DrawPieceOnDC(hdc, piece, piece_color, -1, x, y, tmphdc);
\r
3056 if( texture_hdc != NULL ) {
\r
3057 DeleteDC( texture_hdc );
\r
3061 int saveDiagFlag = 0; FILE *diagFile; // [HGM] diag
\r
3062 void fputDW(FILE *f, int x)
\r
3064 fputc(x & 255, f);
\r
3065 fputc(x>>8 & 255, f);
\r
3066 fputc(x>>16 & 255, f);
\r
3067 fputc(x>>24 & 255, f);
\r
3070 #define MAX_CLIPS 200 /* more than enough */
\r
3073 DrawLogoOnDC(HDC hdc, RECT logoRect, HBITMAP logo)
\r
3075 // HBITMAP bufferBitmap;
\r
3080 int w = 100, h = 50;
\r
3082 if(logo == NULL) return;
\r
3083 // GetClientRect(hwndMain, &Rect);
\r
3084 // bufferBitmap = CreateCompatibleBitmap(hdc, Rect.right-Rect.left+1,
\r
3085 // Rect.bottom-Rect.top+1);
\r
3086 tmphdc = CreateCompatibleDC(hdc);
\r
3087 hbm = SelectObject(tmphdc, logo);
\r
3088 if( GetObject( logo, sizeof(bi), &bi ) > 0 ) {
\r
3092 StretchBlt(hdc, logoRect.left, logoRect.top, logoRect.right - logoRect.left,
\r
3093 logoRect.bottom - logoRect.top, tmphdc, 0, 0, w, h, SRCCOPY);
\r
3094 SelectObject(tmphdc, hbm);
\r
3098 static HDC hdcSeek;
\r
3100 // [HGM] seekgraph
\r
3101 void DrawSeekAxis( int x, int y, int xTo, int yTo )
\r
3104 HPEN hp = SelectObject( hdcSeek, gridPen );
\r
3105 MoveToEx( hdcSeek, boardRect.left+x, boardRect.top+y, &stPt );
\r
3106 LineTo( hdcSeek, boardRect.left+xTo, boardRect.top+yTo );
\r
3107 SelectObject( hdcSeek, hp );
\r
3110 // front-end wrapper for drawing functions to do rectangles
\r
3111 void DrawSeekBackground( int left, int top, int right, int bottom )
\r
3116 if (hdcSeek == NULL) {
\r
3117 hdcSeek = GetDC(hwndMain);
\r
3118 if (!appData.monoMode) {
\r
3119 SelectPalette(hdcSeek, hPal, FALSE);
\r
3120 RealizePalette(hdcSeek);
\r
3123 hp = SelectObject( hdcSeek, gridPen );
\r
3124 rc.top = boardRect.top+top; rc.left = boardRect.left+left;
\r
3125 rc.bottom = boardRect.top+bottom; rc.right = boardRect.left+right;
\r
3126 FillRect( hdcSeek, &rc, lightSquareBrush );
\r
3127 SelectObject( hdcSeek, hp );
\r
3130 // front-end wrapper for putting text in graph
\r
3131 void DrawSeekText(char *buf, int x, int y)
\r
3134 SetBkMode( hdcSeek, TRANSPARENT );
\r
3135 GetTextExtentPoint32( hdcSeek, buf, strlen(buf), &stSize );
\r
3136 TextOut( hdcSeek, boardRect.left+x-3, boardRect.top+y-stSize.cy/2, buf, strlen(buf) );
\r
3139 void DrawSeekDot(int x, int y, int color)
\r
3141 int square = color & 0x80;
\r
3143 HBRUSH oldBrush = SelectObject(hdcSeek,
\r
3144 color == 0 ? markerBrush : color == 1 ? darkSquareBrush : explodeBrush);
\r
3146 Rectangle(hdcSeek, boardRect.left+x - squareSize/9, boardRect.top+y - squareSize/9,
\r
3147 boardRect.left+x + squareSize/9, boardRect.top+y + squareSize/9);
\r
3149 Ellipse(hdcSeek, boardRect.left+x - squareSize/8, boardRect.top+y - squareSize/8,
\r
3150 boardRect.left+x + squareSize/8, boardRect.top+y + squareSize/8);
\r
3151 SelectObject(hdcSeek, oldBrush);
\r
3155 HDCDrawPosition(HDC hdc, BOOLEAN repaint, Board board)
\r
3157 static Board lastReq, lastDrawn;
\r
3158 static HighlightInfo lastDrawnHighlight, lastDrawnPremove;
\r
3159 static int lastDrawnFlipView = 0;
\r
3160 static int lastReqValid = 0, lastDrawnValid = 0;
\r
3161 int releaseDC, x, y, x2, y2, row, column, num_clips = 0, i;
\r
3164 HBITMAP bufferBitmap;
\r
3165 HBITMAP oldBitmap;
\r
3167 HRGN clips[MAX_CLIPS];
\r
3168 ChessSquare dragged_piece = EmptySquare;
\r
3170 /* I'm undecided on this - this function figures out whether a full
\r
3171 * repaint is necessary on its own, so there's no real reason to have the
\r
3172 * caller tell it that. I think this can safely be set to FALSE - but
\r
3173 * if we trust the callers not to request full repaints unnessesarily, then
\r
3174 * we could skip some clipping work. In other words, only request a full
\r
3175 * redraw when the majority of pieces have changed positions (ie. flip,
\r
3176 * gamestart and similar) --Hawk
\r
3178 Boolean fullrepaint = repaint;
\r
3180 if(DrawSeekGraph()) return; // [HG} seekgraph: suppress printing board if seek graph up
\r
3182 if( DrawPositionNeedsFullRepaint() ) {
\r
3183 fullrepaint = TRUE;
\r
3186 if (board == NULL) {
\r
3187 if (!lastReqValid) {
\r
3192 CopyBoard(lastReq, board);
\r
3196 if (doingSizing) {
\r
3200 if (IsIconic(hwndMain)) {
\r
3204 if (hdc == NULL) {
\r
3205 hdc = GetDC(hwndMain);
\r
3206 if (!appData.monoMode) {
\r
3207 SelectPalette(hdc, hPal, FALSE);
\r
3208 RealizePalette(hdc);
\r
3212 releaseDC = FALSE;
\r
3215 /* Create some work-DCs */
\r
3216 hdcmem = CreateCompatibleDC(hdc);
\r
3217 tmphdc = CreateCompatibleDC(hdc);
\r
3219 /* If dragging is in progress, we temporarely remove the piece */
\r
3220 /* [HGM] or temporarily decrease count if stacked */
\r
3221 /* !! Moved to before board compare !! */
\r
3222 if (dragInfo.from.x >= 0 && dragInfo.pos.x >= 0) {
\r
3223 dragged_piece = board[dragInfo.from.y][dragInfo.from.x];
\r
3224 if(dragInfo.from.x == BOARD_LEFT-2 ) {
\r
3225 if(--board[dragInfo.from.y][dragInfo.from.x+1] == 0 )
\r
3226 board[dragInfo.from.y][dragInfo.from.x] = EmptySquare;
\r
3228 if(dragInfo.from.x == BOARD_RGHT+1) {
\r
3229 if(--board[dragInfo.from.y][dragInfo.from.x-1] == 0 )
\r
3230 board[dragInfo.from.y][dragInfo.from.x] = EmptySquare;
\r
3232 board[dragInfo.from.y][dragInfo.from.x] = EmptySquare;
\r
3235 /* Figure out which squares need updating by comparing the
\r
3236 * newest board with the last drawn board and checking if
\r
3237 * flipping has changed.
\r
3239 if (!fullrepaint && lastDrawnValid && lastDrawnFlipView == flipView) {
\r
3240 for (row = 0; row < BOARD_HEIGHT; row++) { /* [HGM] true size, not 8 */
\r
3241 for (column = 0; column < BOARD_WIDTH; column++) {
\r
3242 if (lastDrawn[row][column] != board[row][column]) {
\r
3243 SquareToPos(row, column, &x, &y);
\r
3244 clips[num_clips++] =
\r
3245 CreateRectRgn(x, y, x + squareSize, y + squareSize);
\r
3249 for (i=0; i<2; i++) {
\r
3250 if (lastDrawnHighlight.sq[i].x != highlightInfo.sq[i].x ||
\r
3251 lastDrawnHighlight.sq[i].y != highlightInfo.sq[i].y) {
\r
3252 if (lastDrawnHighlight.sq[i].x >= 0 &&
\r
3253 lastDrawnHighlight.sq[i].y >= 0) {
\r
3254 SquareToPos(lastDrawnHighlight.sq[i].y,
\r
3255 lastDrawnHighlight.sq[i].x, &x, &y);
\r
3256 clips[num_clips++] =
\r
3257 CreateRectRgn(x - lineGap, y - lineGap,
\r
3258 x + squareSize + lineGap, y + squareSize + lineGap);
\r
3260 if (highlightInfo.sq[i].x >= 0 && highlightInfo.sq[i].y >= 0) {
\r
3261 SquareToPos(highlightInfo.sq[i].y, highlightInfo.sq[i].x, &x, &y);
\r
3262 clips[num_clips++] =
\r
3263 CreateRectRgn(x - lineGap, y - lineGap,
\r
3264 x + squareSize + lineGap, y + squareSize + lineGap);
\r
3268 for (i=0; i<2; i++) {
\r
3269 if (lastDrawnPremove.sq[i].x != premoveHighlightInfo.sq[i].x ||
\r
3270 lastDrawnPremove.sq[i].y != premoveHighlightInfo.sq[i].y) {
\r
3271 if (lastDrawnPremove.sq[i].x >= 0 &&
\r
3272 lastDrawnPremove.sq[i].y >= 0) {
\r
3273 SquareToPos(lastDrawnPremove.sq[i].y,
\r
3274 lastDrawnPremove.sq[i].x, &x, &y);
\r
3275 clips[num_clips++] =
\r
3276 CreateRectRgn(x - lineGap, y - lineGap,
\r
3277 x + squareSize + lineGap, y + squareSize + lineGap);
\r
3279 if (premoveHighlightInfo.sq[i].x >= 0 &&
\r
3280 premoveHighlightInfo.sq[i].y >= 0) {
\r
3281 SquareToPos(premoveHighlightInfo.sq[i].y,
\r
3282 premoveHighlightInfo.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
3290 fullrepaint = TRUE;
\r
3293 /* Create a buffer bitmap - this is the actual bitmap
\r
3294 * being written to. When all the work is done, we can
\r
3295 * copy it to the real DC (the screen). This avoids
\r
3296 * the problems with flickering.
\r
3298 GetClientRect(hwndMain, &Rect);
\r
3299 bufferBitmap = CreateCompatibleBitmap(hdc, Rect.right-Rect.left+1,
\r
3300 Rect.bottom-Rect.top+1);
\r
3301 oldBitmap = SelectObject(hdcmem, bufferBitmap);
\r
3302 if (!appData.monoMode) {
\r
3303 SelectPalette(hdcmem, hPal, FALSE);
\r
3306 /* Create clips for dragging */
\r
3307 if (!fullrepaint) {
\r
3308 if (dragInfo.from.x >= 0) {
\r
3309 SquareToPos(dragInfo.from.y, dragInfo.from.x, &x, &y);
\r
3310 clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);
\r
3312 if (dragInfo.start.x >= 0) {
\r
3313 SquareToPos(dragInfo.start.y, dragInfo.start.x, &x, &y);
\r
3314 clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);
\r
3316 if (dragInfo.pos.x >= 0) {
\r
3317 x = dragInfo.pos.x - squareSize / 2;
\r
3318 y = dragInfo.pos.y - squareSize / 2;
\r
3319 clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);
\r
3321 if (dragInfo.lastpos.x >= 0) {
\r
3322 x = dragInfo.lastpos.x - squareSize / 2;
\r
3323 y = dragInfo.lastpos.y - squareSize / 2;
\r
3324 clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);
\r
3328 /* Are we animating a move?
\r
3330 * - remove the piece from the board (temporarely)
\r
3331 * - calculate the clipping region
\r
3333 if (!fullrepaint) {
\r
3334 if (animInfo.piece != EmptySquare) {
\r
3335 board[animInfo.from.y][animInfo.from.x] = EmptySquare;
\r
3336 x = boardRect.left + animInfo.lastpos.x;
\r
3337 y = boardRect.top + animInfo.lastpos.y;
\r
3338 x2 = boardRect.left + animInfo.pos.x;
\r
3339 y2 = boardRect.top + animInfo.pos.y;
\r
3340 clips[num_clips++] = CreateRectRgn(MIN(x,x2), MIN(y,y2), MAX(x,x2)+squareSize, MAX(y,y2)+squareSize);
\r
3341 /* Slight kludge. The real problem is that after AnimateMove is
\r
3342 done, the position on the screen does not match lastDrawn.
\r
3343 This currently causes trouble only on e.p. captures in
\r
3344 atomic, where the piece moves to an empty square and then
\r
3345 explodes. The old and new positions both had an empty square
\r
3346 at the destination, but animation has drawn a piece there and
\r
3347 we have to remember to erase it. [HGM] moved until after setting lastDrawn */
\r
3348 lastDrawn[animInfo.to.y][animInfo.to.x] = animInfo.piece;
\r
3352 /* No clips? Make sure we have fullrepaint set to TRUE */
\r
3353 if (num_clips == 0)
\r
3354 fullrepaint = TRUE;
\r
3356 /* Set clipping on the memory DC */
\r
3357 if (!fullrepaint) {
\r
3358 SelectClipRgn(hdcmem, clips[0]);
\r
3359 for (x = 1; x < num_clips; x++) {
\r
3360 if (ExtSelectClipRgn(hdcmem, clips[x], RGN_OR) == ERROR)
\r
3361 abort(); // this should never ever happen!
\r
3365 /* Do all the drawing to the memory DC */
\r
3366 if(explodeInfo.radius) { // [HGM] atomic
\r
3368 int x, y, r=(explodeInfo.radius * squareSize)/100;
\r
3369 board[explodeInfo.fromY][explodeInfo.fromX] = EmptySquare; // suppress display of capturer
\r
3370 SquareToPos(explodeInfo.toY, explodeInfo.toX, &x, &y);
\r
3371 x += squareSize/2;
\r
3372 y += squareSize/2;
\r
3373 if(!fullrepaint) {
\r
3374 clips[num_clips] = CreateRectRgn(x-r, y-r, x+r, y+r);
\r
3375 ExtSelectClipRgn(hdcmem, clips[num_clips++], RGN_OR);
\r
3377 DrawGridOnDC(hdcmem);
\r
3378 DrawHighlightsOnDC(hdcmem);
\r
3379 DrawBoardOnDC(hdcmem, board, tmphdc);
\r
3380 oldBrush = SelectObject(hdcmem, explodeBrush);
\r
3381 Ellipse(hdcmem, x-r, y-r, x+r, y+r);
\r
3382 SelectObject(hdcmem, oldBrush);
\r
3384 DrawGridOnDC(hdcmem);
\r
3385 DrawHighlightsOnDC(hdcmem);
\r
3386 DrawBoardOnDC(hdcmem, board, tmphdc);
\r
3388 for (row = 0; row < BOARD_HEIGHT; row++) {
\r
3389 for (column = 0; column < BOARD_WIDTH; column++) {
\r
3390 if (marker[row][column]) { // marker changes only occur with full repaint!
\r
3391 HBRUSH oldBrush = SelectObject(hdcmem,
\r
3392 marker[row][column] == 2 ? markerBrush : explodeBrush);
\r
3393 SquareToPos(row, column, &x, &y);
\r
3394 Ellipse(hdcmem, x + squareSize/4, y + squareSize/4,
\r
3395 x + 3*squareSize/4, y + 3*squareSize/4);
\r
3396 SelectObject(hdcmem, oldBrush);
\r
3401 HBITMAP whiteLogo = (HBITMAP) first.programLogo, blackLogo = (HBITMAP) second.programLogo;
\r
3402 if(appData.autoLogo) {
\r
3404 switch(gameMode) { // pick logos based on game mode
\r
3405 case IcsObserving:
\r
3406 whiteLogo = second.programLogo; // ICS logo
\r
3407 blackLogo = second.programLogo;
\r
3410 case IcsPlayingWhite:
\r
3411 if(!appData.zippyPlay) whiteLogo = userLogo;
\r
3412 blackLogo = second.programLogo; // ICS logo
\r
3414 case IcsPlayingBlack:
\r
3415 whiteLogo = second.programLogo; // ICS logo
\r
3416 blackLogo = appData.zippyPlay ? first.programLogo : userLogo;
\r
3418 case TwoMachinesPlay:
\r
3419 if(first.twoMachinesColor[0] == 'b') {
\r
3420 whiteLogo = second.programLogo;
\r
3421 blackLogo = first.programLogo;
\r
3424 case MachinePlaysWhite:
\r
3425 blackLogo = userLogo;
\r
3427 case MachinePlaysBlack:
\r
3428 whiteLogo = userLogo;
\r
3429 blackLogo = first.programLogo;
\r
3432 DrawLogoOnDC(hdc, leftLogoRect, flipClock ? blackLogo : whiteLogo);
\r
3433 DrawLogoOnDC(hdc, rightLogoRect, flipClock ? whiteLogo : blackLogo);
\r
3436 if( appData.highlightMoveWithArrow ) {
\r
3437 DrawArrowHighlight(hdcmem);
\r
3440 DrawCoordsOnDC(hdcmem);
\r
3442 CopyBoard(lastDrawn, board); /* [HGM] Moved to here from end of routine, */
\r
3443 /* to make sure lastDrawn contains what is actually drawn */
\r
3445 /* Put the dragged piece back into place and draw it (out of place!) */
\r
3446 if (dragged_piece != EmptySquare) {
\r
3447 /* [HGM] or restack */
\r
3448 if(dragInfo.from.x == BOARD_LEFT-2 )
\r
3449 board[dragInfo.from.y][dragInfo.from.x+1]++;
\r
3451 if(dragInfo.from.x == BOARD_RGHT+1 )
\r
3452 board[dragInfo.from.y][dragInfo.from.x-1]++;
\r
3453 board[dragInfo.from.y][dragInfo.from.x] = dragged_piece;
\r
3454 x = dragInfo.pos.x - squareSize / 2;
\r
3455 y = dragInfo.pos.y - squareSize / 2;
\r
3456 DrawPieceOnDC(hdcmem, dragged_piece,
\r
3457 ((int) dragged_piece < (int) BlackPawn),
\r
3458 (dragInfo.from.y + dragInfo.from.x) % 2, x, y, tmphdc);
\r
3461 /* Put the animated piece back into place and draw it */
\r
3462 if (animInfo.piece != EmptySquare) {
\r
3463 board[animInfo.from.y][animInfo.from.x] = animInfo.piece;
\r
3464 x = boardRect.left + animInfo.pos.x;
\r
3465 y = boardRect.top + animInfo.pos.y;
\r
3466 DrawPieceOnDC(hdcmem, animInfo.piece,
\r
3467 ((int) animInfo.piece < (int) BlackPawn),
\r
3468 (animInfo.from.y + animInfo.from.x) % 2, x, y, tmphdc);
\r
3471 /* Release the bufferBitmap by selecting in the old bitmap
\r
3472 * and delete the memory DC
\r
3474 SelectObject(hdcmem, oldBitmap);
\r
3477 /* Set clipping on the target DC */
\r
3478 if (!fullrepaint) {
\r
3479 SelectClipRgn(hdc, clips[0]);
\r
3480 for (x = 1; x < num_clips; x++) {
\r
3481 if (ExtSelectClipRgn(hdc, clips[x], RGN_OR) == ERROR)
\r
3482 abort(); // this should never ever happen!
\r
3486 /* Copy the new bitmap onto the screen in one go.
\r
3487 * This way we avoid any flickering
\r
3489 oldBitmap = SelectObject(tmphdc, bufferBitmap);
\r
3490 BitBlt(hdc, boardRect.left, boardRect.top,
\r
3491 boardRect.right - boardRect.left,
\r
3492 boardRect.bottom - boardRect.top,
\r
3493 tmphdc, boardRect.left, boardRect.top, SRCCOPY);
\r
3494 if(saveDiagFlag) {
\r
3495 BITMAP b; int i, j=0, m, w, wb, fac=0; char pData[1000000];
\r
3496 BITMAPINFOHEADER bih; int color[16], nrColors=0;
\r
3498 GetObject(bufferBitmap, sizeof(b), &b);
\r
3499 if(b.bmWidthBytes*b.bmHeight <= 990000) {
\r
3500 bih.biSize = sizeof(BITMAPINFOHEADER);
\r
3501 bih.biWidth = b.bmWidth;
\r
3502 bih.biHeight = b.bmHeight;
\r
3504 bih.biBitCount = b.bmBitsPixel;
\r
3505 bih.biCompression = 0;
\r
3506 bih.biSizeImage = b.bmWidthBytes*b.bmHeight;
\r
3507 bih.biXPelsPerMeter = 0;
\r
3508 bih.biYPelsPerMeter = 0;
\r
3509 bih.biClrUsed = 0;
\r
3510 bih.biClrImportant = 0;
\r
3511 // fprintf(diagFile, "t=%d\nw=%d\nh=%d\nB=%d\nP=%d\nX=%d\n",
\r
3512 // b.bmType, b.bmWidth, b.bmHeight, b.bmWidthBytes, b.bmPlanes, b.bmBitsPixel);
\r
3513 GetDIBits(tmphdc,bufferBitmap,0,b.bmHeight,pData,(BITMAPINFO*)&bih,DIB_RGB_COLORS);
\r
3514 // fprintf(diagFile, "%8x\n", (int) pData);
\r
3516 wb = b.bmWidthBytes;
\r
3518 for(i=0; i<wb*(b.bmHeight - boardRect.top + OUTER_MARGIN)>>2; i++) {
\r
3519 int k = ((int*) pData)[i];
\r
3520 for(j=0; j<nrColors; j++) if(color[j] == k) break;
\r
3521 if(j >= 16) break;
\r
3523 if(j >= nrColors) nrColors = j+1;
\r
3525 if(j<16) { // 16 colors is enough. Compress to 4 bits per pixel
\r
3527 for(i=0; i<b.bmHeight - boardRect.top + OUTER_MARGIN; i++) {
\r
3528 for(w=0; w<(wb>>2); w+=2) {
\r
3529 int k = ((int*) pData)[(wb*i>>2) + w];
\r
3530 for(j=0; j<nrColors; j++) if(color[j] == k) break;
\r
3531 k = ((int*) pData)[(wb*i>>2) + w + 1];
\r
3532 for(m=0; m<nrColors; m++) if(color[m] == k) break;
\r
3533 pData[p++] = m | j<<4;
\r
3535 while(p&3) pData[p++] = 0;
\r
3538 wb = ((wb+31)>>5)<<2;
\r
3540 // write BITMAPFILEHEADER
\r
3541 fprintf(diagFile, "BM");
\r
3542 fputDW(diagFile, wb*(b.bmHeight - boardRect.top + OUTER_MARGIN)+0x36 + (fac?64:0));
\r
3543 fputDW(diagFile, 0);
\r
3544 fputDW(diagFile, 0x36 + (fac?64:0));
\r
3545 // write BITMAPINFOHEADER
\r
3546 fputDW(diagFile, 40);
\r
3547 fputDW(diagFile, b.bmWidth);
\r
3548 fputDW(diagFile, b.bmHeight - boardRect.top + OUTER_MARGIN);
\r
3549 if(fac) fputDW(diagFile, 0x040001); // planes and bits/pixel
\r
3550 else fputDW(diagFile, 0x200001); // planes and bits/pixel
\r
3551 fputDW(diagFile, 0);
\r
3552 fputDW(diagFile, 0);
\r
3553 fputDW(diagFile, 0);
\r
3554 fputDW(diagFile, 0);
\r
3555 fputDW(diagFile, 0);
\r
3556 fputDW(diagFile, 0);
\r
3557 // write color table
\r
3559 for(i=0; i<16; i++) fputDW(diagFile, color[i]);
\r
3560 // write bitmap data
\r
3561 for(i=0; i<wb*(b.bmHeight - boardRect.top + OUTER_MARGIN); i++)
\r
3562 fputc(pData[i], diagFile);
\r
3566 SelectObject(tmphdc, oldBitmap);
\r
3568 /* Massive cleanup */
\r
3569 for (x = 0; x < num_clips; x++)
\r
3570 DeleteObject(clips[x]);
\r
3573 DeleteObject(bufferBitmap);
\r
3576 ReleaseDC(hwndMain, hdc);
\r
3578 if (lastDrawnFlipView != flipView) {
\r
3580 CheckMenuItem(GetMenu(hwndMain),IDM_FlipView, MF_BYCOMMAND|MF_CHECKED);
\r
3582 CheckMenuItem(GetMenu(hwndMain),IDM_FlipView, MF_BYCOMMAND|MF_UNCHECKED);
\r
3585 /* CopyBoard(lastDrawn, board);*/
\r
3586 lastDrawnHighlight = highlightInfo;
\r
3587 lastDrawnPremove = premoveHighlightInfo;
\r
3588 lastDrawnFlipView = flipView;
\r
3589 lastDrawnValid = 1;
\r
3592 /* [HGM] diag: Save the current board display to the given open file and close the file */
\r
3597 saveDiagFlag = 1; diagFile = f;
\r
3598 HDCDrawPosition(NULL, TRUE, NULL);
\r
3602 // if(f != NULL) fprintf(f, "Sorry, but this feature is still in preparation\n");
\r
3609 /*---------------------------------------------------------------------------*\
\r
3610 | CLIENT PAINT PROCEDURE
\r
3611 | This is the main event-handler for the WM_PAINT message.
\r
3613 \*---------------------------------------------------------------------------*/
\r
3615 PaintProc(HWND hwnd)
\r
3621 if((hdc = BeginPaint(hwnd, &ps))) {
\r
3622 if (IsIconic(hwnd)) {
\r
3623 DrawIcon(hdc, 2, 2, iconCurrent);
\r
3625 if (!appData.monoMode) {
\r
3626 SelectPalette(hdc, hPal, FALSE);
\r
3627 RealizePalette(hdc);
\r
3629 HDCDrawPosition(hdc, 1, NULL);
\r
3631 SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);
\r
3632 ExtTextOut(hdc, messageRect.left, messageRect.top,
\r
3633 ETO_CLIPPED|ETO_OPAQUE,
\r
3634 &messageRect, messageText, strlen(messageText), NULL);
\r
3635 SelectObject(hdc, oldFont);
\r
3636 DisplayBothClocks();
\r
3638 EndPaint(hwnd,&ps);
\r
3646 * If the user selects on a border boundary, return -1; if off the board,
\r
3647 * return -2. Otherwise map the event coordinate to the square.
\r
3648 * The offset boardRect.left or boardRect.top must already have been
\r
3649 * subtracted from x.
\r
3651 int EventToSquare(x, limit)
\r
3659 if ((x % (squareSize + lineGap)) >= squareSize)
\r
3661 x /= (squareSize + lineGap);
\r
3673 DropEnable dropEnables[] = {
\r
3674 { 'P', DP_Pawn, "Pawn" },
\r
3675 { 'N', DP_Knight, "Knight" },
\r
3676 { 'B', DP_Bishop, "Bishop" },
\r
3677 { 'R', DP_Rook, "Rook" },
\r
3678 { 'Q', DP_Queen, "Queen" },
\r
3682 SetupDropMenu(HMENU hmenu)
\r
3684 int i, count, enable;
\r
3686 extern char white_holding[], black_holding[];
\r
3687 char item[MSG_SIZ];
\r
3689 for (i=0; i<sizeof(dropEnables)/sizeof(DropEnable); i++) {
\r
3690 p = strchr(gameMode == IcsPlayingWhite ? white_holding : black_holding,
\r
3691 dropEnables[i].piece);
\r
3693 while (p && *p++ == dropEnables[i].piece) count++;
\r
3694 sprintf(item, "%s %d", dropEnables[i].name, count);
\r
3695 enable = count > 0 || !appData.testLegality
\r
3696 /*!!temp:*/ || (gameInfo.variant == VariantCrazyhouse
\r
3697 && !appData.icsActive);
\r
3698 ModifyMenu(hmenu, dropEnables[i].command,
\r