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
871 SetActiveWindow(hwndConsole);
\r
873 if(!appData.noGUI) UpdateWindow(hwnd); else ShowWindow(hwnd, SW_MINIMIZE);
\r
874 if(gameListDialog) SetFocus(gameListDialog); // [HGM] jaws: for if we clicked multi-game game file
\r
883 HMENU hmenu = GetMenu(hwndMain);
\r
885 (void) EnableMenuItem(hmenu, IDM_CommPort,
\r
886 MF_BYCOMMAND|((appData.icsActive &&
\r
887 *appData.icsCommPort != NULLCHAR) ?
\r
888 MF_ENABLED : MF_GRAYED));
\r
889 (void) CheckMenuItem(hmenu, IDM_SaveSettingsOnExit,
\r
890 MF_BYCOMMAND|(saveSettingsOnExit ?
\r
891 MF_CHECKED : MF_UNCHECKED));
\r
894 //---------------------------------------------------------------------------------------------------------
\r
896 #define ICS_TEXT_MENU_SIZE (IDM_CommandXLast - IDM_CommandX + 1)
\r
897 #define XBOARD FALSE
\r
899 #define OPTCHAR "/"
\r
900 #define SEPCHAR "="
\r
904 // front-end part of option handling
\r
907 LFfromMFP(LOGFONT* lf, MyFontParams *mfp)
\r
909 HDC hdc = CreateDC("DISPLAY", NULL, NULL, NULL);
\r
910 lf->lfHeight = -(int)(mfp->pointSize * GetDeviceCaps(hdc, LOGPIXELSY) / 72.0 + 0.5);
\r
913 lf->lfEscapement = 0;
\r
914 lf->lfOrientation = 0;
\r
915 lf->lfWeight = mfp->bold ? FW_BOLD : FW_NORMAL;
\r
916 lf->lfItalic = mfp->italic;
\r
917 lf->lfUnderline = mfp->underline;
\r
918 lf->lfStrikeOut = mfp->strikeout;
\r
919 lf->lfCharSet = mfp->charset;
\r
920 lf->lfOutPrecision = OUT_DEFAULT_PRECIS;
\r
921 lf->lfClipPrecision = CLIP_DEFAULT_PRECIS;
\r
922 lf->lfQuality = DEFAULT_QUALITY;
\r
923 lf->lfPitchAndFamily = DEFAULT_PITCH|FF_DONTCARE;
\r
924 strcpy(lf->lfFaceName, mfp->faceName);
\r
928 CreateFontInMF(MyFont *mf)
\r
930 LFfromMFP(&mf->lf, &mf->mfp);
\r
931 if (mf->hf) DeleteObject(mf->hf);
\r
932 mf->hf = CreateFontIndirect(&mf->lf);
\r
935 // [HGM] This platform-dependent table provides the location for storing the color info
\r
937 colorVariable[] = {
\r
942 &highlightSquareColor,
\r
943 &premoveHighlightColor,
\r
945 &consoleBackgroundColor,
\r
946 &appData.fontForeColorWhite,
\r
947 &appData.fontBackColorWhite,
\r
948 &appData.fontForeColorBlack,
\r
949 &appData.fontBackColorBlack,
\r
950 &appData.evalHistColorWhite,
\r
951 &appData.evalHistColorBlack,
\r
952 &appData.highlightArrowColor,
\r
955 /* Command line font name parser. NULL name means do nothing.
\r
956 Syntax like "Courier New:10.0 bi" or "Arial:10" or "Arial:10b"
\r
957 For backward compatibility, syntax without the colon is also
\r
958 accepted, but font names with digits in them won't work in that case.
\r
961 ParseFontName(char *name, MyFontParams *mfp)
\r
964 if (name == NULL) return;
\r
966 q = strchr(p, ':');
\r
968 if (q - p >= sizeof(mfp->faceName))
\r
969 ExitArgError("Font name too long:", name);
\r
970 memcpy(mfp->faceName, p, q - p);
\r
971 mfp->faceName[q - p] = NULLCHAR;
\r
975 while (*p && !isdigit(*p)) {
\r
977 if (q - mfp->faceName >= sizeof(mfp->faceName))
\r
978 ExitArgError("Font name too long:", name);
\r
980 while (q > mfp->faceName && q[-1] == ' ') q--;
\r
983 if (!*p) ExitArgError("Font point size missing:", name);
\r
984 mfp->pointSize = (float) atof(p);
\r
985 mfp->bold = (strchr(p, 'b') != NULL);
\r
986 mfp->italic = (strchr(p, 'i') != NULL);
\r
987 mfp->underline = (strchr(p, 'u') != NULL);
\r
988 mfp->strikeout = (strchr(p, 's') != NULL);
\r
989 mfp->charset = DEFAULT_CHARSET;
\r
990 q = strchr(p, 'c');
\r
992 mfp->charset = (BYTE) atoi(q+1);
\r
996 ParseFont(char *name, int number)
\r
997 { // wrapper to shield back-end from 'font'
\r
998 ParseFontName(name, &font[boardSize][number]->mfp);
\r
1003 { // in WB we have a 2D array of fonts; this initializes their description
\r
1005 /* Point font array elements to structures and
\r
1006 parse default font names */
\r
1007 for (i=0; i<NUM_FONTS; i++) {
\r
1008 for (j=0; j<NUM_SIZES; j++) {
\r
1009 font[j][i] = &fontRec[j][i];
\r
1010 ParseFontName(font[j][i]->def, &font[j][i]->mfp);
\r
1017 { // here we create the actual fonts from the selected descriptions
\r
1019 for (i=0; i<NUM_FONTS; i++) {
\r
1020 for (j=0; j<NUM_SIZES; j++) {
\r
1021 CreateFontInMF(font[j][i]);
\r
1025 /* Color name parser.
\r
1026 X version accepts X color names, but this one
\r
1027 handles only the #rrggbb form (hex) or rrr,ggg,bbb (decimal) */
\r
1029 ParseColorName(char *name)
\r
1031 int red, green, blue, count;
\r
1032 char buf[MSG_SIZ];
\r
1034 count = sscanf(name, "#%2x%2x%2x", &red, &green, &blue);
\r
1036 count = sscanf(name, "%3d%*[^0-9]%3d%*[^0-9]%3d",
\r
1037 &red, &green, &blue);
\r
1040 sprintf(buf, "Can't parse color name %s", name);
\r
1041 DisplayError(buf, 0);
\r
1042 return RGB(0, 0, 0);
\r
1044 return PALETTERGB(red, green, blue);
\r
1048 ParseColor(int n, char *name)
\r
1049 { // for WinBoard the color is an int, which needs to be derived from the string
\r
1050 if(colorVariable[n]) *(int*)colorVariable[n] = ParseColorName(name);
\r
1054 ParseAttribs(COLORREF *color, int *effects, char* argValue)
\r
1056 char *e = argValue;
\r
1060 if (*e == 'b') eff |= CFE_BOLD;
\r
1061 else if (*e == 'i') eff |= CFE_ITALIC;
\r
1062 else if (*e == 'u') eff |= CFE_UNDERLINE;
\r
1063 else if (*e == 's') eff |= CFE_STRIKEOUT;
\r
1064 else if (*e == '#' || isdigit(*e)) break;
\r
1068 *color = ParseColorName(e);
\r
1072 ParseTextAttribs(ColorClass cc, char *s)
\r
1073 { // [HGM] front-end wrapper that does the platform-dependent call
\r
1074 // for XBoard we would set (&appData.colorShout)[cc] = strdup(s);
\r
1075 ParseAttribs(&textAttribs[cc].color, &textAttribs[cc].effects, s);
\r
1079 ParseBoardSize(void *addr, char *name)
\r
1080 { // [HGM] rewritten with return-value ptr to shield back-end from BoardSize
\r
1081 BoardSize bs = SizeTiny;
\r
1082 while (sizeInfo[bs].name != NULL) {
\r
1083 if (StrCaseCmp(name, sizeInfo[bs].name) == 0) {
\r
1084 *(BoardSize *)addr = bs;
\r
1089 ExitArgError("Unrecognized board size value", name);
\r
1094 { // [HGM] import name from appData first
\r
1097 for (cc = (ColorClass)0; cc < ColorNormal; cc++) {
\r
1098 textAttribs[cc].sound.name = strdup((&appData.soundShout)[cc]);
\r
1099 textAttribs[cc].sound.data = NULL;
\r
1100 MyLoadSound(&textAttribs[cc].sound);
\r
1102 for (cc = ColorNormal; cc < NColorClasses; cc++) {
\r
1103 textAttribs[cc].sound.name = strdup("");
\r
1104 textAttribs[cc].sound.data = NULL;
\r
1106 for (sc = (SoundClass)0; sc < NSoundClasses; sc++) {
\r
1107 sounds[sc].name = strdup((&appData.soundMove)[sc]);
\r
1108 sounds[sc].data = NULL;
\r
1109 MyLoadSound(&sounds[sc]);
\r
1114 SetCommPortDefaults()
\r
1116 memset(&dcb, 0, sizeof(DCB)); // required by VS 2002 +
\r
1117 dcb.DCBlength = sizeof(DCB);
\r
1118 dcb.BaudRate = 9600;
\r
1119 dcb.fBinary = TRUE;
\r
1120 dcb.fParity = FALSE;
\r
1121 dcb.fOutxCtsFlow = FALSE;
\r
1122 dcb.fOutxDsrFlow = FALSE;
\r
1123 dcb.fDtrControl = DTR_CONTROL_ENABLE;
\r
1124 dcb.fDsrSensitivity = FALSE;
\r
1125 dcb.fTXContinueOnXoff = TRUE;
\r
1126 dcb.fOutX = FALSE;
\r
1128 dcb.fNull = FALSE;
\r
1129 dcb.fRtsControl = RTS_CONTROL_ENABLE;
\r
1130 dcb.fAbortOnError = FALSE;
\r
1132 dcb.Parity = SPACEPARITY;
\r
1133 dcb.StopBits = ONESTOPBIT;
\r
1136 // [HGM] args: these three cases taken out to stay in front-end
\r
1138 SaveFontArg(FILE *f, ArgDescriptor *ad)
\r
1139 { // in WinBoard every board size has its own font, and the "argLoc" identifies the table,
\r
1140 // while the curent board size determines the element. This system should be ported to XBoard.
\r
1141 // What the table contains pointers to, and how to print the font description, remains platform-dependent
\r
1143 for (bs=0; bs<NUM_SIZES; bs++) {
\r
1144 MyFontParams *mfp = &font[bs][(int) ad->argLoc]->mfp;
\r
1145 fprintf(f, "/size=%s ", sizeInfo[bs].name);
\r
1146 fprintf(f, "/%s=\"%s:%g%s%s%s%s%sc%d\"\n",
\r
1147 ad->argName, mfp->faceName, mfp->pointSize,
\r
1148 mfp->bold || mfp->italic || mfp->underline || mfp->strikeout ? " " : "",
\r
1149 mfp->bold ? "b" : "",
\r
1150 mfp->italic ? "i" : "",
\r
1151 mfp->underline ? "u" : "",
\r
1152 mfp->strikeout ? "s" : "",
\r
1153 (int)mfp->charset);
\r
1159 { // [HGM] copy the names from the internal WB variables to appData
\r
1162 for (cc = (ColorClass)0; cc < ColorNormal; cc++)
\r
1163 (&appData.soundShout)[cc] = textAttribs[cc].sound.name;
\r
1164 for (sc = (SoundClass)0; sc < NSoundClasses; sc++)
\r
1165 (&appData.soundMove)[sc] = sounds[sc].name;
\r
1169 SaveAttribsArg(FILE *f, ArgDescriptor *ad)
\r
1170 { // here the "argLoc" defines a table index. It could have contained the 'ta' pointer itself, though
\r
1171 MyTextAttribs* ta = &textAttribs[(ColorClass)ad->argLoc];
\r
1172 fprintf(f, "/%s=\"%s%s%s%s%s#%02lx%02lx%02lx\"\n", ad->argName,
\r
1173 (ta->effects & CFE_BOLD) ? "b" : "",
\r
1174 (ta->effects & CFE_ITALIC) ? "i" : "",
\r
1175 (ta->effects & CFE_UNDERLINE) ? "u" : "",
\r
1176 (ta->effects & CFE_STRIKEOUT) ? "s" : "",
\r
1177 (ta->effects) ? " " : "",
\r
1178 ta->color&0xff, (ta->color >> 8)&0xff, (ta->color >> 16)&0xff);
\r
1182 SaveColor(FILE *f, ArgDescriptor *ad)
\r
1183 { // in WinBoard the color is an int and has to be converted to text. In X it would be a string already?
\r
1184 COLORREF color = *(COLORREF *)colorVariable[(int)ad->argLoc];
\r
1185 fprintf(f, "/%s=#%02lx%02lx%02lx\n", ad->argName,
\r
1186 color&0xff, (color>>8)&0xff, (color>>16)&0xff);
\r
1190 SaveBoardSize(FILE *f, char *name, void *addr)
\r
1191 { // wrapper to shield back-end from BoardSize & sizeInfo
\r
1192 fprintf(f, "/%s=%s\n", name, sizeInfo[*(BoardSize *)addr].name);
\r
1196 ParseCommPortSettings(char *s)
\r
1197 { // wrapper to keep dcb from back-end
\r
1198 ParseCommSettings(s, &dcb);
\r
1203 { // wrapper to shield use of window handles from back-end (make addressible by number?)
\r
1204 GetActualPlacement(hwndMain, &wpMain);
\r
1205 GetActualPlacement(hwndConsole, &wpConsole);
\r
1206 GetActualPlacement(commentDialog, &wpComment);
\r
1207 GetActualPlacement(editTagsDialog, &wpTags);
\r
1208 GetActualPlacement(gameListDialog, &wpGameList);
\r
1209 GetActualPlacement(moveHistoryDialog, &wpMoveHistory);
\r
1210 GetActualPlacement(evalGraphDialog, &wpEvalGraph);
\r
1211 GetActualPlacement(engineOutputDialog, &wpEngineOutput);
\r
1215 PrintCommPortSettings(FILE *f, char *name)
\r
1216 { // wrapper to shield back-end from DCB
\r
1217 PrintCommSettings(f, name, &dcb);
\r
1221 MySearchPath(char *installDir, char *name, char *fullname)
\r
1224 return (int) SearchPath(installDir, name, NULL, MSG_SIZ, fullname, &dummy);
\r
1228 MyGetFullPathName(char *name, char *fullname)
\r
1231 return (int) GetFullPathName(name, MSG_SIZ, fullname, &dummy);
\r
1236 { // [HGM] args: allows testing if main window is realized from back-end
\r
1237 return hwndMain != NULL;
\r
1241 PopUpStartupDialog()
\r
1245 lpProc = MakeProcInstance((FARPROC)StartupDialog, hInst);
\r
1246 DialogBox(hInst, MAKEINTRESOURCE(DLG_Startup), NULL, (DLGPROC)lpProc);
\r
1247 FreeProcInstance(lpProc);
\r
1250 /*---------------------------------------------------------------------------*\
\r
1252 * GDI board drawing routines
\r
1254 \*---------------------------------------------------------------------------*/
\r
1256 /* [AS] Draw square using background texture */
\r
1257 static void DrawTile( int dx, int dy, int dw, int dh, HDC dst, HDC src, int mode, int sx, int sy )
\r
1262 return; /* Should never happen! */
\r
1265 SetGraphicsMode( dst, GM_ADVANCED );
\r
1272 /* X reflection */
\r
1277 x.eDx = (FLOAT) dw + dx - 1;
\r
1280 SetWorldTransform( dst, &x );
\r
1283 /* Y reflection */
\r
1289 x.eDy = (FLOAT) dh + dy - 1;
\r
1291 SetWorldTransform( dst, &x );
\r
1299 x.eDx = (FLOAT) dx;
\r
1300 x.eDy = (FLOAT) dy;
\r
1303 SetWorldTransform( dst, &x );
\r
1307 BitBlt( dst, dx, dy, dw, dh, src, sx, sy, SRCCOPY );
\r
1315 SetWorldTransform( dst, &x );
\r
1317 ModifyWorldTransform( dst, 0, MWT_IDENTITY );
\r
1320 /* [AS] [HGM] Make room for more piece types, so all pieces can be different */
\r
1322 PM_WP = (int) WhitePawn,
\r
1323 PM_WN = (int) WhiteKnight,
\r
1324 PM_WB = (int) WhiteBishop,
\r
1325 PM_WR = (int) WhiteRook,
\r
1326 PM_WQ = (int) WhiteQueen,
\r
1327 PM_WF = (int) WhiteFerz,
\r
1328 PM_WW = (int) WhiteWazir,
\r
1329 PM_WE = (int) WhiteAlfil,
\r
1330 PM_WM = (int) WhiteMan,
\r
1331 PM_WO = (int) WhiteCannon,
\r
1332 PM_WU = (int) WhiteUnicorn,
\r
1333 PM_WH = (int) WhiteNightrider,
\r
1334 PM_WA = (int) WhiteAngel,
\r
1335 PM_WC = (int) WhiteMarshall,
\r
1336 PM_WAB = (int) WhiteCardinal,
\r
1337 PM_WD = (int) WhiteDragon,
\r
1338 PM_WL = (int) WhiteLance,
\r
1339 PM_WS = (int) WhiteCobra,
\r
1340 PM_WV = (int) WhiteFalcon,
\r
1341 PM_WSG = (int) WhiteSilver,
\r
1342 PM_WG = (int) WhiteGrasshopper,
\r
1343 PM_WK = (int) WhiteKing,
\r
1344 PM_BP = (int) BlackPawn,
\r
1345 PM_BN = (int) BlackKnight,
\r
1346 PM_BB = (int) BlackBishop,
\r
1347 PM_BR = (int) BlackRook,
\r
1348 PM_BQ = (int) BlackQueen,
\r
1349 PM_BF = (int) BlackFerz,
\r
1350 PM_BW = (int) BlackWazir,
\r
1351 PM_BE = (int) BlackAlfil,
\r
1352 PM_BM = (int) BlackMan,
\r
1353 PM_BO = (int) BlackCannon,
\r
1354 PM_BU = (int) BlackUnicorn,
\r
1355 PM_BH = (int) BlackNightrider,
\r
1356 PM_BA = (int) BlackAngel,
\r
1357 PM_BC = (int) BlackMarshall,
\r
1358 PM_BG = (int) BlackGrasshopper,
\r
1359 PM_BAB = (int) BlackCardinal,
\r
1360 PM_BD = (int) BlackDragon,
\r
1361 PM_BL = (int) BlackLance,
\r
1362 PM_BS = (int) BlackCobra,
\r
1363 PM_BV = (int) BlackFalcon,
\r
1364 PM_BSG = (int) BlackSilver,
\r
1365 PM_BK = (int) BlackKing
\r
1368 static HFONT hPieceFont = NULL;
\r
1369 static HBITMAP hPieceMask[(int) EmptySquare];
\r
1370 static HBITMAP hPieceFace[(int) EmptySquare];
\r
1371 static int fontBitmapSquareSize = 0;
\r
1372 static char pieceToFontChar[(int) EmptySquare] =
\r
1373 { 'p', 'n', 'b', 'r', 'q',
\r
1374 'n', 'b', 'p', 'n', 'b', 'r', 'b', 'r', 'q', 'k',
\r
1375 'k', 'o', 'm', 'v', 't', 'w',
\r
1376 'v', 't', 'o', 'm', 'v', 't', 'v', 't', 'w', 'l',
\r
1379 extern BOOL SetCharTable( char *table, const char * map );
\r
1380 /* [HGM] moved to backend.c */
\r
1382 static void SetPieceBackground( HDC hdc, COLORREF color, int mode )
\r
1385 BYTE r1 = GetRValue( color );
\r
1386 BYTE g1 = GetGValue( color );
\r
1387 BYTE b1 = GetBValue( color );
\r
1393 /* Create a uniform background first */
\r
1394 hbrush = CreateSolidBrush( color );
\r
1395 SetRect( &rc, 0, 0, squareSize, squareSize );
\r
1396 FillRect( hdc, &rc, hbrush );
\r
1397 DeleteObject( hbrush );
\r
1400 /* Vertical gradient, good for pawn, knight and rook, less for queen and king */
\r
1401 int steps = squareSize / 2;
\r
1404 for( i=0; i<steps; i++ ) {
\r
1405 BYTE r = r1 - (r1-r2) * i / steps;
\r
1406 BYTE g = g1 - (g1-g2) * i / steps;
\r
1407 BYTE b = b1 - (b1-b2) * i / steps;
\r
1409 hbrush = CreateSolidBrush( RGB(r,g,b) );
\r
1410 SetRect( &rc, i + squareSize - steps, 0, i + squareSize - steps + 1, squareSize );
\r
1411 FillRect( hdc, &rc, hbrush );
\r
1412 DeleteObject(hbrush);
\r
1415 else if( mode == 2 ) {
\r
1416 /* Diagonal gradient, good more or less for every piece */
\r
1417 POINT triangle[3];
\r
1418 HPEN hpen = SelectObject( hdc, GetStockObject(NULL_PEN) );
\r
1419 HBRUSH hbrush_old;
\r
1420 int steps = squareSize;
\r
1423 triangle[0].x = squareSize - steps;
\r
1424 triangle[0].y = squareSize;
\r
1425 triangle[1].x = squareSize;
\r
1426 triangle[1].y = squareSize;
\r
1427 triangle[2].x = squareSize;
\r
1428 triangle[2].y = squareSize - steps;
\r
1430 for( i=0; i<steps; i++ ) {
\r
1431 BYTE r = r1 - (r1-r2) * i / steps;
\r
1432 BYTE g = g1 - (g1-g2) * i / steps;
\r
1433 BYTE b = b1 - (b1-b2) * i / steps;
\r
1435 hbrush = CreateSolidBrush( RGB(r,g,b) );
\r
1436 hbrush_old = SelectObject( hdc, hbrush );
\r
1437 Polygon( hdc, triangle, 3 );
\r
1438 SelectObject( hdc, hbrush_old );
\r
1439 DeleteObject(hbrush);
\r
1444 SelectObject( hdc, hpen );
\r
1449 [AS] The method I use to create the bitmaps it a bit tricky, but it
\r
1450 seems to work ok. The main problem here is to find the "inside" of a chess
\r
1451 piece: follow the steps as explained below.
\r
1453 static void CreatePieceMaskFromFont( HDC hdc_window, HDC hdc, int index )
\r
1457 COLORREF chroma = RGB(0xFF,0x00,0xFF);
\r
1461 int backColor = whitePieceColor;
\r
1462 int foreColor = blackPieceColor;
\r
1464 if( index < (int)BlackPawn && appData.fontBackColorWhite != appData.fontForeColorWhite ) {
\r
1465 backColor = appData.fontBackColorWhite;
\r
1466 foreColor = appData.fontForeColorWhite;
\r
1468 else if( index >= (int)BlackPawn && appData.fontBackColorBlack != appData.fontForeColorBlack ) {
\r
1469 backColor = appData.fontBackColorBlack;
\r
1470 foreColor = appData.fontForeColorBlack;
\r
1474 hbm = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );
\r
1476 hbm_old = SelectObject( hdc, hbm );
\r
1480 rc.right = squareSize;
\r
1481 rc.bottom = squareSize;
\r
1483 /* Step 1: background is now black */
\r
1484 FillRect( hdc, &rc, GetStockObject(BLACK_BRUSH) );
\r
1486 GetTextExtentPoint32( hdc, &pieceToFontChar[index], 1, &sz );
\r
1488 pt.x = (squareSize - sz.cx) / 2;
\r
1489 pt.y = (squareSize - sz.cy) / 2;
\r
1491 SetBkMode( hdc, TRANSPARENT );
\r
1492 SetTextColor( hdc, chroma );
\r
1493 /* Step 2: the piece has been drawn in purple, there are now black and purple in this bitmap */
\r
1494 TextOut( hdc, pt.x, pt.y, &pieceToFontChar[appData.allWhite && index >= (int)BlackPawn ? index - (int)BlackPawn : index], 1 );
\r
1496 SelectObject( hdc, GetStockObject(WHITE_BRUSH) );
\r
1497 /* Step 3: the area outside the piece is filled with white */
\r
1498 // FloodFill( hdc, 0, 0, chroma );
\r
1499 ExtFloodFill( hdc, 0, 0, 0, FLOODFILLSURFACE );
\r
1500 ExtFloodFill( hdc, 0, squareSize-1, 0, FLOODFILLSURFACE ); // [HGM] fill from all 4 corners, for if piece too big
\r
1501 ExtFloodFill( hdc, squareSize-1, 0, 0, FLOODFILLSURFACE );
\r
1502 ExtFloodFill( hdc, squareSize-1, squareSize-1, 0, FLOODFILLSURFACE );
\r
1503 SelectObject( hdc, GetStockObject(BLACK_BRUSH) );
\r
1505 Step 4: this is the tricky part, the area inside the piece is filled with black,
\r
1506 but if the start point is not inside the piece we're lost!
\r
1507 There should be a better way to do this... if we could create a region or path
\r
1508 from the fill operation we would be fine for example.
\r
1510 // FloodFill( hdc, squareSize / 2, squareSize / 2, RGB(0xFF,0xFF,0xFF) );
\r
1511 ExtFloodFill( hdc, squareSize / 2, squareSize / 2, RGB(0xFF,0xFF,0xFF), FLOODFILLBORDER );
\r
1513 { /* [HGM] shave off edges of mask, in an attempt to correct for the fact that FloodFill does not work correctly under Win XP */
\r
1514 HDC dc2 = CreateCompatibleDC( hdc_window );
\r
1515 HBITMAP bm2 = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );
\r
1517 SelectObject( dc2, bm2 );
\r
1518 BitBlt( dc2, 0, 0, squareSize, squareSize, hdc, 0, 0, SRCCOPY ); // make copy
\r
1519 BitBlt( hdc, 0, 1, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );
\r
1520 BitBlt( hdc, 2, 1, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );
\r
1521 BitBlt( hdc, 1, 0, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );
\r
1522 BitBlt( hdc, 1, 2, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );
\r
1525 DeleteObject( bm2 );
\r
1528 SetTextColor( hdc, 0 );
\r
1530 Step 5: some fonts have "disconnected" areas that are skipped by the fill:
\r
1531 draw the piece again in black for safety.
\r
1533 TextOut( hdc, pt.x, pt.y, &pieceToFontChar[appData.allWhite && index >= (int)BlackPawn ? index - (int)BlackPawn : index], 1 );
\r
1535 SelectObject( hdc, hbm_old );
\r
1537 if( hPieceMask[index] != NULL ) {
\r
1538 DeleteObject( hPieceMask[index] );
\r
1541 hPieceMask[index] = hbm;
\r
1544 hbm = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );
\r
1546 SelectObject( hdc, hbm );
\r
1549 HDC dc1 = CreateCompatibleDC( hdc_window );
\r
1550 HDC dc2 = CreateCompatibleDC( hdc_window );
\r
1551 HBITMAP bm2 = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );
\r
1553 SelectObject( dc1, hPieceMask[index] );
\r
1554 SelectObject( dc2, bm2 );
\r
1555 FillRect( dc2, &rc, GetStockObject(WHITE_BRUSH) );
\r
1556 BitBlt( dc2, 0, 0, squareSize, squareSize, dc1, 0, 0, SRCINVERT );
\r
1559 Now dc2 contains the inverse of the piece mask, i.e. a mask that preserves
\r
1560 the piece background and deletes (makes transparent) the rest.
\r
1561 Thanks to that mask, we are free to paint the background with the greates
\r
1562 freedom, as we'll be able to mask off the unwanted parts when finished.
\r
1563 We use this, to make gradients and give the pieces a "roundish" look.
\r
1565 SetPieceBackground( hdc, backColor, 2 );
\r
1566 BitBlt( hdc, 0, 0, squareSize, squareSize, dc2, 0, 0, SRCAND );
\r
1570 DeleteObject( bm2 );
\r
1573 SetTextColor( hdc, foreColor );
\r
1574 TextOut( hdc, pt.x, pt.y, &pieceToFontChar[appData.allWhite && index >= (int)BlackPawn ? index - (int)BlackPawn : index], 1 );
\r
1576 SelectObject( hdc, hbm_old );
\r
1578 if( hPieceFace[index] != NULL ) {
\r
1579 DeleteObject( hPieceFace[index] );
\r
1582 hPieceFace[index] = hbm;
\r
1585 static int TranslatePieceToFontPiece( int piece )
\r
1615 case BlackMarshall:
\r
1619 case BlackNightrider:
\r
1625 case BlackUnicorn:
\r
1629 case BlackGrasshopper:
\r
1641 case BlackCardinal:
\r
1648 case WhiteMarshall:
\r
1652 case WhiteNightrider:
\r
1658 case WhiteUnicorn:
\r
1662 case WhiteGrasshopper:
\r
1674 case WhiteCardinal:
\r
1683 void CreatePiecesFromFont()
\r
1686 HDC hdc_window = NULL;
\r
1692 if( fontBitmapSquareSize < 0 ) {
\r
1693 /* Something went seriously wrong in the past: do not try to recreate fonts! */
\r
1697 if( appData.renderPiecesWithFont == NULL || appData.renderPiecesWithFont[0] == NULLCHAR || appData.renderPiecesWithFont[0] == '*' ) {
\r
1698 fontBitmapSquareSize = -1;
\r
1702 if( fontBitmapSquareSize != squareSize ) {
\r
1703 hdc_window = GetDC( hwndMain );
\r
1704 hdc = CreateCompatibleDC( hdc_window );
\r
1706 if( hPieceFont != NULL ) {
\r
1707 DeleteObject( hPieceFont );
\r
1710 for( i=0; i<=(int)BlackKing; i++ ) {
\r
1711 hPieceMask[i] = NULL;
\r
1712 hPieceFace[i] = NULL;
\r
1718 if( appData.fontPieceSize >= 50 && appData.fontPieceSize <= 150 ) {
\r
1719 fontHeight = appData.fontPieceSize;
\r
1722 fontHeight = (fontHeight * squareSize) / 100;
\r
1724 lf.lfHeight = -MulDiv( fontHeight, GetDeviceCaps(hdc, LOGPIXELSY), 72 );
\r
1726 lf.lfEscapement = 0;
\r
1727 lf.lfOrientation = 0;
\r
1728 lf.lfWeight = FW_NORMAL;
\r
1730 lf.lfUnderline = 0;
\r
1731 lf.lfStrikeOut = 0;
\r
1732 lf.lfCharSet = DEFAULT_CHARSET;
\r
1733 lf.lfOutPrecision = OUT_DEFAULT_PRECIS;
\r
1734 lf.lfClipPrecision = CLIP_DEFAULT_PRECIS;
\r
1735 lf.lfQuality = PROOF_QUALITY;
\r
1736 lf.lfPitchAndFamily = DEFAULT_PITCH | FF_DONTCARE;
\r
1737 strncpy( lf.lfFaceName, appData.renderPiecesWithFont, sizeof(lf.lfFaceName) );
\r
1738 lf.lfFaceName[ sizeof(lf.lfFaceName) - 1 ] = '\0';
\r
1740 hPieceFont = CreateFontIndirect( &lf );
\r
1742 if( hPieceFont == NULL ) {
\r
1743 fontBitmapSquareSize = -2;
\r
1746 /* Setup font-to-piece character table */
\r
1747 if( ! SetCharTable(pieceToFontChar, appData.fontToPieceTable) ) {
\r
1748 /* No (or wrong) global settings, try to detect the font */
\r
1749 if( strstr(lf.lfFaceName,"Alpha") != NULL ) {
\r
1751 SetCharTable(pieceToFontChar, "phbrqkojntwl");
\r
1753 else if( strstr(lf.lfFaceName,"DiagramTT") != NULL ) {
\r
1754 /* DiagramTT* family */
\r
1755 SetCharTable(pieceToFontChar, "PNLRQKpnlrqk");
\r
1757 else if( strstr(lf.lfFaceName,"WinboardF") != NULL ) {
\r
1758 /* Fairy symbols */
\r
1759 SetCharTable(pieceToFontChar, "PNBRQFEACWMOHIJGDVSLUKpnbrqfeacwmohijgdvsluk");
\r
1761 else if( strstr(lf.lfFaceName,"GC2004D") != NULL ) {
\r
1762 /* Good Companion (Some characters get warped as literal :-( */
\r
1763 char s[] = "1cmWG0??S??oYI23wgQU";
\r
1764 s[0]=0xB9; s[1]=0xA9; s[6]=0xB1; s[11]=0xBB; s[12]=0xAB; s[17]=0xB3;
\r
1765 SetCharTable(pieceToFontChar, s);
\r
1768 /* Cases, Condal, Leipzig, Lucena, Marroquin, Merida, Usual */
\r
1769 SetCharTable(pieceToFontChar, "pnbrqkomvtwl");
\r
1773 /* Create bitmaps */
\r
1774 hfont_old = SelectObject( hdc, hPieceFont );
\r
1775 for(i=(int)WhitePawn; i<(int)EmptySquare; i++) /* [HGM] made a loop for this */
\r
1776 if(PieceToChar((ChessSquare)i) != '.') /* skip unused pieces */
\r
1777 CreatePieceMaskFromFont( hdc_window, hdc, i );
\r
1779 SelectObject( hdc, hfont_old );
\r
1781 fontBitmapSquareSize = squareSize;
\r
1785 if( hdc != NULL ) {
\r
1789 if( hdc_window != NULL ) {
\r
1790 ReleaseDC( hwndMain, hdc_window );
\r
1795 DoLoadBitmap(HINSTANCE hinst, char *piece, int squareSize, char *suffix)
\r
1799 sprintf(name, "%s%d%s", piece, squareSize, suffix);
\r
1800 if (gameInfo.event &&
\r
1801 strcmp(gameInfo.event, "Easter Egg Hunt") == 0 &&
\r
1802 strcmp(name, "k80s") == 0) {
\r
1803 strcpy(name, "tim");
\r
1805 return LoadBitmap(hinst, name);
\r
1809 /* Insert a color into the program's logical palette
\r
1810 structure. This code assumes the given color is
\r
1811 the result of the RGB or PALETTERGB macro, and it
\r
1812 knows how those macros work (which is documented).
\r
1815 InsertInPalette(COLORREF color)
\r
1817 LPPALETTEENTRY pe = &(pLogPal->palPalEntry[pLogPal->palNumEntries]);
\r
1819 if (pLogPal->palNumEntries++ >= PALETTESIZE) {
\r
1820 DisplayFatalError("Too many colors", 0, 1);
\r
1821 pLogPal->palNumEntries--;
\r
1825 pe->peFlags = (char) 0;
\r
1826 pe->peRed = (char) (0xFF & color);
\r
1827 pe->peGreen = (char) (0xFF & (color >> 8));
\r
1828 pe->peBlue = (char) (0xFF & (color >> 16));
\r
1834 InitDrawingColors()
\r
1836 if (pLogPal == NULL) {
\r
1837 /* Allocate enough memory for a logical palette with
\r
1838 * PALETTESIZE entries and set the size and version fields
\r
1839 * of the logical palette structure.
\r
1841 pLogPal = (NPLOGPALETTE)
\r
1842 LocalAlloc(LMEM_FIXED, (sizeof(LOGPALETTE) +
\r
1843 (sizeof(PALETTEENTRY) * (PALETTESIZE))));
\r
1844 pLogPal->palVersion = 0x300;
\r
1846 pLogPal->palNumEntries = 0;
\r
1848 InsertInPalette(lightSquareColor);
\r
1849 InsertInPalette(darkSquareColor);
\r
1850 InsertInPalette(whitePieceColor);
\r
1851 InsertInPalette(blackPieceColor);
\r
1852 InsertInPalette(highlightSquareColor);
\r
1853 InsertInPalette(premoveHighlightColor);
\r
1855 /* create a logical color palette according the information
\r
1856 * in the LOGPALETTE structure.
\r
1858 hPal = CreatePalette((LPLOGPALETTE) pLogPal);
\r
1860 lightSquareBrush = CreateSolidBrush(lightSquareColor);
\r
1861 blackSquareBrush = CreateSolidBrush(blackPieceColor);
\r
1862 darkSquareBrush = CreateSolidBrush(darkSquareColor);
\r
1863 whitePieceBrush = CreateSolidBrush(whitePieceColor);
\r
1864 blackPieceBrush = CreateSolidBrush(blackPieceColor);
\r
1865 iconBkgndBrush = CreateSolidBrush(GetSysColor(COLOR_BACKGROUND));
\r
1866 explodeBrush = CreateSolidBrush(highlightSquareColor); // [HGM] atomic
\r
1867 markerBrush = CreateSolidBrush(premoveHighlightColor); // [HGM] markers
\r
1868 /* [AS] Force rendering of the font-based pieces */
\r
1869 if( fontBitmapSquareSize > 0 ) {
\r
1870 fontBitmapSquareSize = 0;
\r
1876 BoardWidth(int boardSize, int n)
\r
1877 { /* [HGM] argument n added to allow different width and height */
\r
1878 int lineGap = sizeInfo[boardSize].lineGap;
\r
1880 if( appData.overrideLineGap >= 0 && appData.overrideLineGap <= 5 ) {
\r
1881 lineGap = appData.overrideLineGap;
\r
1884 return (n + 1) * lineGap +
\r
1885 n * sizeInfo[boardSize].squareSize;
\r
1888 /* Respond to board resize by dragging edge */
\r
1890 ResizeBoard(int newSizeX, int newSizeY, int flags)
\r
1892 BoardSize newSize = NUM_SIZES - 1;
\r
1893 static int recurse = 0;
\r
1894 if (IsIconic(hwndMain)) return;
\r
1895 if (recurse > 0) return;
\r
1897 while (newSize > 0) {
\r
1898 InitDrawingSizes(newSize+1000, 0); // [HGM] kludge to update sizeInfo without visible effects
\r
1899 if(newSizeX >= sizeInfo[newSize].cliWidth &&
\r
1900 newSizeY >= sizeInfo[newSize].cliHeight) break;
\r
1903 boardSize = newSize;
\r
1904 InitDrawingSizes(boardSize, flags);
\r
1911 InitDrawingSizes(BoardSize boardSize, int flags)
\r
1913 int i, boardWidth, boardHeight; /* [HGM] height treated separately */
\r
1914 ChessSquare piece;
\r
1915 static int oldBoardSize = -1, oldTinyLayout = 0;
\r
1917 SIZE clockSize, messageSize;
\r
1919 char buf[MSG_SIZ];
\r
1921 HMENU hmenu = GetMenu(hwndMain);
\r
1922 RECT crect, wrect, oldRect;
\r
1924 LOGBRUSH logbrush;
\r
1926 int suppressVisibleEffects = 0; // [HGM] kludge to request updating sizeInfo only
\r
1927 if((int)boardSize >= 1000 ) { boardSize -= 1000; suppressVisibleEffects = 1; }
\r
1929 /* [HGM] call with -2 uses old size (for if nr of files, ranks changes) */
\r
1930 if(boardSize == (BoardSize)(-2) ) boardSize = oldBoardSize;
\r
1932 oldRect.left = wpMain.x; //[HGM] placement: remember previous window params
\r
1933 oldRect.top = wpMain.y;
\r
1934 oldRect.right = wpMain.x + wpMain.width;
\r
1935 oldRect.bottom = wpMain.y + wpMain.height;
\r
1937 tinyLayout = sizeInfo[boardSize].tinyLayout;
\r
1938 smallLayout = sizeInfo[boardSize].smallLayout;
\r
1939 squareSize = sizeInfo[boardSize].squareSize;
\r
1940 lineGap = sizeInfo[boardSize].lineGap;
\r
1941 minorSize = 0; /* [HGM] Kludge to see if demagnified pieces need to be shifted */
\r
1943 if( appData.overrideLineGap >= 0 && appData.overrideLineGap <= 5 ) {
\r
1944 lineGap = appData.overrideLineGap;
\r
1947 if (tinyLayout != oldTinyLayout) {
\r
1948 long style = GetWindowLong(hwndMain, GWL_STYLE);
\r
1950 style &= ~WS_SYSMENU;
\r
1951 InsertMenu(hmenu, IDM_Exit, MF_BYCOMMAND, IDM_Minimize,
\r
1952 "&Minimize\tCtrl+F4");
\r
1954 style |= WS_SYSMENU;
\r
1955 RemoveMenu(hmenu, IDM_Minimize, MF_BYCOMMAND);
\r
1957 SetWindowLong(hwndMain, GWL_STYLE, style);
\r
1959 for (i=0; menuBarText[tinyLayout][i]; i++) {
\r
1960 ModifyMenu(hmenu, i, MF_STRING|MF_BYPOSITION|MF_POPUP,
\r
1961 (UINT)GetSubMenu(hmenu, i), menuBarText[tinyLayout][i]);
\r
1963 DrawMenuBar(hwndMain);
\r
1966 boardWidth = BoardWidth(boardSize, BOARD_WIDTH);
\r
1967 boardHeight = BoardWidth(boardSize, BOARD_HEIGHT);
\r
1969 /* Get text area sizes */
\r
1970 hdc = GetDC(hwndMain);
\r
1971 if (appData.clockMode) {
\r
1972 sprintf(buf, "White: %s", TimeString(23*60*60*1000L));
\r
1974 sprintf(buf, "White");
\r
1976 oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);
\r
1977 GetTextExtentPoint(hdc, buf, strlen(buf), &clockSize);
\r
1978 SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);
\r
1979 str = "We only care about the height here";
\r
1980 GetTextExtentPoint(hdc, str, strlen(str), &messageSize);
\r
1981 SelectObject(hdc, oldFont);
\r
1982 ReleaseDC(hwndMain, hdc);
\r
1984 /* Compute where everything goes */
\r
1985 if((first.programLogo || second.programLogo) && !tinyLayout) {
\r
1986 /* [HGM] logo: if either logo is on, reserve space for it */
\r
1987 logoHeight = 2*clockSize.cy;
\r
1988 leftLogoRect.left = OUTER_MARGIN;
\r
1989 leftLogoRect.right = leftLogoRect.left + 4*clockSize.cy;
\r
1990 leftLogoRect.top = OUTER_MARGIN;
\r
1991 leftLogoRect.bottom = OUTER_MARGIN + logoHeight;
\r
1993 rightLogoRect.right = OUTER_MARGIN + boardWidth;
\r
1994 rightLogoRect.left = rightLogoRect.right - 4*clockSize.cy;
\r
1995 rightLogoRect.top = OUTER_MARGIN;
\r
1996 rightLogoRect.bottom = OUTER_MARGIN + logoHeight;
\r
1999 whiteRect.left = leftLogoRect.right;
\r
2000 whiteRect.right = OUTER_MARGIN + boardWidth/2 - INNER_MARGIN/2;
\r
2001 whiteRect.top = OUTER_MARGIN;
\r
2002 whiteRect.bottom = whiteRect.top + logoHeight;
\r
2004 blackRect.right = rightLogoRect.left;
\r
2005 blackRect.left = whiteRect.right + INNER_MARGIN;
\r
2006 blackRect.top = whiteRect.top;
\r
2007 blackRect.bottom = whiteRect.bottom;
\r
2009 whiteRect.left = OUTER_MARGIN;
\r
2010 whiteRect.right = whiteRect.left + boardWidth/2 - INNER_MARGIN/2;
\r
2011 whiteRect.top = OUTER_MARGIN;
\r
2012 whiteRect.bottom = whiteRect.top + clockSize.cy;
\r
2014 blackRect.left = whiteRect.right + INNER_MARGIN;
\r
2015 blackRect.right = blackRect.left + boardWidth/2 - 1;
\r
2016 blackRect.top = whiteRect.top;
\r
2017 blackRect.bottom = whiteRect.bottom;
\r
2019 logoHeight = 0; // [HGM] logo: suppress logo after change to tiny layout!
\r
2022 messageRect.left = OUTER_MARGIN + MESSAGE_LINE_LEFTMARGIN;
\r
2023 if (appData.showButtonBar) {
\r
2024 messageRect.right = OUTER_MARGIN + boardWidth // [HGM] logo: expressed independent of clock placement
\r
2025 - N_BUTTONS*BUTTON_WIDTH - MESSAGE_LINE_LEFTMARGIN;
\r
2027 messageRect.right = OUTER_MARGIN + boardWidth;
\r
2029 messageRect.top = whiteRect.bottom + INNER_MARGIN;
\r
2030 messageRect.bottom = messageRect.top + messageSize.cy;
\r
2032 boardRect.left = OUTER_MARGIN;
\r
2033 boardRect.right = boardRect.left + boardWidth;
\r
2034 boardRect.top = messageRect.bottom + INNER_MARGIN;
\r
2035 boardRect.bottom = boardRect.top + boardHeight;
\r
2037 sizeInfo[boardSize].cliWidth = boardRect.right + OUTER_MARGIN;
\r
2038 sizeInfo[boardSize].cliHeight = boardRect.bottom + OUTER_MARGIN;
\r
2039 oldBoardSize = boardSize;
\r
2040 oldTinyLayout = tinyLayout;
\r
2041 winW = 2 * GetSystemMetrics(SM_CXFRAME) + boardRect.right + OUTER_MARGIN;
\r
2042 winH = 2 * GetSystemMetrics(SM_CYFRAME) + GetSystemMetrics(SM_CYMENU) +
\r
2043 GetSystemMetrics(SM_CYCAPTION) + boardRect.bottom + OUTER_MARGIN;
\r
2044 if(suppressVisibleEffects) return; // [HGM] when called for filling sizeInfo only
\r
2045 wpMain.width = winW; // [HGM] placement: set through temporary which can used by initial sizing choice
\r
2046 wpMain.height = winH; // without disturbing window attachments
\r
2047 GetWindowRect(hwndMain, &wrect);
\r
2048 SetWindowPos(hwndMain, NULL, 0, 0, wpMain.width, wpMain.height,
\r
2049 SWP_NOCOPYBITS|SWP_NOZORDER|SWP_NOMOVE);
\r
2051 // [HGM] placement: let attached windows follow size change.
\r
2052 ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, moveHistoryDialog, &wpMoveHistory );
\r
2053 ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, evalGraphDialog, &wpEvalGraph );
\r
2054 ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, engineOutputDialog, &wpEngineOutput );
\r
2055 ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, gameListDialog, &wpGameList );
\r
2056 ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, hwndConsole, &wpConsole );
\r
2058 /* compensate if menu bar wrapped */
\r
2059 GetClientRect(hwndMain, &crect);
\r
2060 offby = boardRect.bottom + OUTER_MARGIN - crect.bottom;
\r
2061 wpMain.height += offby;
\r
2063 case WMSZ_TOPLEFT:
\r
2064 SetWindowPos(hwndMain, NULL,
\r
2065 wrect.right - wpMain.width, wrect.bottom - wpMain.height,
\r
2066 wpMain.width, wpMain.height, SWP_NOCOPYBITS|SWP_NOZORDER);
\r
2069 case WMSZ_TOPRIGHT:
\r
2071 SetWindowPos(hwndMain, NULL,
\r
2072 wrect.left, wrect.bottom - wpMain.height,
\r
2073 wpMain.width, wpMain.height, SWP_NOCOPYBITS|SWP_NOZORDER);
\r
2076 case WMSZ_BOTTOMLEFT:
\r
2078 SetWindowPos(hwndMain, NULL,
\r
2079 wrect.right - wpMain.width, wrect.top,
\r
2080 wpMain.width, wpMain.height, SWP_NOCOPYBITS|SWP_NOZORDER);
\r
2083 case WMSZ_BOTTOMRIGHT:
\r
2087 SetWindowPos(hwndMain, NULL, 0, 0, wpMain.width, wpMain.height,
\r
2088 SWP_NOCOPYBITS|SWP_NOZORDER|SWP_NOMOVE);
\r
2093 for (i = 0; i < N_BUTTONS; i++) {
\r
2094 if (buttonDesc[i].hwnd != NULL) {
\r
2095 DestroyWindow(buttonDesc[i].hwnd);
\r
2096 buttonDesc[i].hwnd = NULL;
\r
2098 if (appData.showButtonBar) {
\r
2099 buttonDesc[i].hwnd =
\r
2100 CreateWindow("BUTTON", buttonDesc[i].label,
\r
2101 WS_VISIBLE | WS_CHILD | BS_PUSHBUTTON,
\r
2102 boardRect.right - BUTTON_WIDTH*(N_BUTTONS-i),
\r
2103 messageRect.top, BUTTON_WIDTH, messageSize.cy, hwndMain,
\r
2104 (HMENU) buttonDesc[i].id,
\r
2105 (HINSTANCE) GetWindowLong(hwndMain, GWL_HINSTANCE), NULL);
\r
2107 SendMessage(buttonDesc[i].hwnd, WM_SETFONT,
\r
2108 (WPARAM)font[boardSize][MESSAGE_FONT]->hf,
\r
2109 MAKELPARAM(FALSE, 0));
\r
2111 if (buttonDesc[i].id == IDM_Pause)
\r
2112 hwndPause = buttonDesc[i].hwnd;
\r
2113 buttonDesc[i].wndproc = (WNDPROC)
\r
2114 SetWindowLong(buttonDesc[i].hwnd, GWL_WNDPROC, (LONG) ButtonProc);
\r
2117 if (gridPen != NULL) DeleteObject(gridPen);
\r
2118 if (highlightPen != NULL) DeleteObject(highlightPen);
\r
2119 if (premovePen != NULL) DeleteObject(premovePen);
\r
2120 if (lineGap != 0) {
\r
2121 logbrush.lbStyle = BS_SOLID;
\r
2122 logbrush.lbColor = RGB(0, 0, 0); /* grid pen color = black */
\r
2124 ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,
\r
2125 lineGap, &logbrush, 0, NULL);
\r
2126 logbrush.lbColor = highlightSquareColor;
\r
2128 ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,
\r
2129 lineGap, &logbrush, 0, NULL);
\r
2131 logbrush.lbColor = premoveHighlightColor;
\r
2133 ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,
\r
2134 lineGap, &logbrush, 0, NULL);
\r
2136 /* [HGM] Loop had to be split in part for vert. and hor. lines */
\r
2137 for (i = 0; i < BOARD_HEIGHT + 1; i++) {
\r
2138 gridEndpoints[i*2].x = boardRect.left + lineGap / 2;
\r
2139 gridEndpoints[i*2].y = gridEndpoints[i*2 + 1].y =
\r
2140 boardRect.top + lineGap / 2 + (i * (squareSize + lineGap));
\r
2141 gridEndpoints[i*2 + 1].x = boardRect.left + lineGap / 2 +
\r
2142 BOARD_WIDTH * (squareSize + lineGap);
\r
2143 gridVertexCounts[i*2] = gridVertexCounts[i*2 + 1] = 2;
\r
2145 for (i = 0; i < BOARD_WIDTH + 1; i++) {
\r
2146 gridEndpoints[i*2 + BOARD_HEIGHT*2 + 2].y = boardRect.top + lineGap / 2;
\r
2147 gridEndpoints[i*2 + BOARD_HEIGHT*2 + 2].x =
\r
2148 gridEndpoints[i*2 + 1 + BOARD_HEIGHT*2 + 2].x = boardRect.left +
\r
2149 lineGap / 2 + (i * (squareSize + lineGap));
\r
2150 gridEndpoints[i*2 + 1 + BOARD_HEIGHT*2 + 2].y =
\r
2151 boardRect.top + BOARD_HEIGHT * (squareSize + lineGap);
\r
2152 gridVertexCounts[i*2] = gridVertexCounts[i*2 + 1] = 2;
\r
2156 /* [HGM] Licensing requirement */
\r
2158 if(gameInfo.variant == VariantGothic) GothicPopUp( GOTHIC, VariantGothic); else
\r
2161 if(gameInfo.variant == VariantFalcon) GothicPopUp( FALCON, VariantFalcon); else
\r
2163 GothicPopUp( "", VariantNormal);
\r
2166 /* if (boardSize == oldBoardSize) return; [HGM] variant might have changed */
\r
2168 /* Load piece bitmaps for this board size */
\r
2169 for (i=0; i<=2; i++) {
\r
2170 for (piece = WhitePawn;
\r
2171 (int) piece < (int) BlackPawn;
\r
2172 piece = (ChessSquare) ((int) piece + 1)) {
\r
2173 if (pieceBitmap[i][piece] != NULL)
\r
2174 DeleteObject(pieceBitmap[i][piece]);
\r
2178 fontBitmapSquareSize = 0; /* [HGM] render: make sure pieces will be recreated, as we might need others now */
\r
2179 // Orthodox Chess pieces
\r
2180 pieceBitmap[0][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "s");
\r
2181 pieceBitmap[0][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "s");
\r
2182 pieceBitmap[0][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "s");
\r
2183 pieceBitmap[0][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "s");
\r
2184 pieceBitmap[0][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "s");
\r
2185 pieceBitmap[1][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "o");
\r
2186 pieceBitmap[1][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "o");
\r
2187 pieceBitmap[1][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "o");
\r
2188 pieceBitmap[1][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "o");
\r
2189 pieceBitmap[1][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "o");
\r
2190 pieceBitmap[2][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "w");
\r
2191 pieceBitmap[2][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "w");
\r
2192 pieceBitmap[2][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "w");
\r
2193 pieceBitmap[2][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "w");
\r
2194 pieceBitmap[2][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "w");
\r
2195 if( !strcmp(appData.variant, "shogi") && (squareSize==72 || squareSize==49)) {
\r
2196 // in Shogi, Hijack the unused Queen for Lance
\r
2197 pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "s");
\r
2198 pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "o");
\r
2199 pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "w");
\r
2201 pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "s");
\r
2202 pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "o");
\r
2203 pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "w");
\r
2206 if(squareSize <= 72 && squareSize >= 33) {
\r
2207 /* A & C are available in most sizes now */
\r
2208 if(squareSize != 49 && squareSize != 72 && squareSize != 33) { // Vortex-like
\r
2209 pieceBitmap[0][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "s");
\r
2210 pieceBitmap[1][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "o");
\r
2211 pieceBitmap[2][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "w");
\r
2212 pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "s");
\r
2213 pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "o");
\r
2214 pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "w");
\r
2215 pieceBitmap[0][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "s");
\r
2216 pieceBitmap[1][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "o");
\r
2217 pieceBitmap[2][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "w");
\r
2218 pieceBitmap[0][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "s");
\r
2219 pieceBitmap[1][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "o");
\r
2220 pieceBitmap[2][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "w");
\r
2221 } else { // Smirf-like
\r
2222 pieceBitmap[0][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "s");
\r
2223 pieceBitmap[1][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "o");
\r
2224 pieceBitmap[2][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "w");
\r
2226 if(gameInfo.variant == VariantGothic) { // Vortex-like
\r
2227 pieceBitmap[0][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "s");
\r
2228 pieceBitmap[1][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "o");
\r
2229 pieceBitmap[2][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "w");
\r
2230 } else { // WinBoard standard
\r
2231 pieceBitmap[0][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "s");
\r
2232 pieceBitmap[1][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "o");
\r
2233 pieceBitmap[2][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "w");
\r
2238 if(squareSize==72 || squareSize==49 || squareSize==33) { /* experiment with some home-made bitmaps */
\r
2239 pieceBitmap[0][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "s");
\r
2240 pieceBitmap[1][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "o");
\r
2241 pieceBitmap[2][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "w");
\r
2242 pieceBitmap[0][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "s");
\r
2243 pieceBitmap[1][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "o");
\r
2244 pieceBitmap[2][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "w");
\r
2245 pieceBitmap[0][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "s");
\r
2246 pieceBitmap[1][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "o");
\r
2247 pieceBitmap[2][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "w");
\r
2248 pieceBitmap[0][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "s");
\r
2249 pieceBitmap[1][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "o");
\r
2250 pieceBitmap[2][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "w");
\r
2251 pieceBitmap[0][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "s");
\r
2252 pieceBitmap[1][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "o");
\r
2253 pieceBitmap[2][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "w");
\r
2254 pieceBitmap[0][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "s");
\r
2255 pieceBitmap[1][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "o");
\r
2256 pieceBitmap[2][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "w");
\r
2257 pieceBitmap[0][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "s");
\r
2258 pieceBitmap[1][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "o");
\r
2259 pieceBitmap[2][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "w");
\r
2260 pieceBitmap[0][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "s");
\r
2261 pieceBitmap[1][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "o");
\r
2262 pieceBitmap[2][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "w");
\r
2263 pieceBitmap[0][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "s");
\r
2264 pieceBitmap[1][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "o");
\r
2265 pieceBitmap[2][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "w");
\r
2266 pieceBitmap[0][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "s");
\r
2267 pieceBitmap[1][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "o");
\r
2268 pieceBitmap[2][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "w");
\r
2270 if(gameInfo.variant == VariantShogi) { /* promoted Gold represemtations */
\r
2271 pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "wp", squareSize, "s");
\r
2272 pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "wp", squareSize, "o");
\r
2273 pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "w", squareSize, "w");
\r
2274 pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "wn", squareSize, "s");
\r
2275 pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "wn", squareSize, "o");
\r
2276 pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "w", squareSize, "w");
\r
2277 pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "ws", squareSize, "s");
\r
2278 pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "ws", squareSize, "o");
\r
2279 pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "w", squareSize, "w");
\r
2280 pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "wl", squareSize, "s");
\r
2281 pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "wl", squareSize, "o");
\r
2282 pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "w", squareSize, "w");
\r
2284 pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "s");
\r
2285 pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "o");
\r
2286 pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "w");
\r
2287 pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "s");
\r
2288 pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "o");
\r
2289 pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "w");
\r
2290 pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "s");
\r
2291 pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "o");
\r
2292 pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "w");
\r
2293 pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "s");
\r
2294 pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "o");
\r
2295 pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "w");
\r
2298 } else { /* other size, no special bitmaps available. Use smaller symbols */
\r
2299 if((int)boardSize < 2) minorSize = sizeInfo[0].squareSize;
\r
2300 else minorSize = sizeInfo[(int)boardSize - 2].squareSize;
\r
2301 pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "s");
\r
2302 pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "o");
\r
2303 pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "w");
\r
2304 pieceBitmap[0][WhiteCardinal] = DoLoadBitmap(hInst, "b", minorSize, "s");
\r
2305 pieceBitmap[1][WhiteCardinal] = DoLoadBitmap(hInst, "b", minorSize, "o");
\r
2306 pieceBitmap[2][WhiteCardinal] = DoLoadBitmap(hInst, "b", minorSize, "w");
\r
2307 pieceBitmap[0][WhiteDragon] = DoLoadBitmap(hInst, "r", minorSize, "s");
\r
2308 pieceBitmap[1][WhiteDragon] = DoLoadBitmap(hInst, "r", minorSize, "o");
\r
2309 pieceBitmap[2][WhiteDragon] = DoLoadBitmap(hInst, "r", minorSize, "w");
\r
2310 pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "s");
\r
2311 pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "o");
\r
2312 pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "w");
\r
2316 if(gameInfo.variant == VariantShogi && squareSize == 58)
\r
2317 /* special Shogi support in this size */
\r
2318 { for (i=0; i<=2; i++) { /* replace all bitmaps */
\r
2319 for (piece = WhitePawn;
\r
2320 (int) piece < (int) BlackPawn;
\r
2321 piece = (ChessSquare) ((int) piece + 1)) {
\r
2322 if (pieceBitmap[i][piece] != NULL)
\r
2323 DeleteObject(pieceBitmap[i][piece]);
\r
2326 pieceBitmap[0][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "o");
\r
2327 pieceBitmap[0][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "o");
\r
2328 pieceBitmap[0][WhiteBishop] = DoLoadBitmap(hInst, "sb", squareSize, "o");
\r
2329 pieceBitmap[0][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "o");
\r
2330 pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "o");
\r
2331 pieceBitmap[0][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "o");
\r
2332 pieceBitmap[0][WhiteFerz] = DoLoadBitmap(hInst, "sf", squareSize, "o");
\r
2333 pieceBitmap[0][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "o");
\r
2334 pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "su", squareSize, "o");
\r
2335 pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "sh", squareSize, "o");
\r
2336 pieceBitmap[0][WhiteCardinal] = DoLoadBitmap(hInst, "sa", squareSize, "o");
\r
2337 pieceBitmap[0][WhiteDragon] = DoLoadBitmap(hInst, "sc", squareSize, "o");
\r
2338 pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "sg", squareSize, "o");
\r
2339 pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "ss", squareSize, "o");
\r
2340 pieceBitmap[1][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "o");
\r
2341 pieceBitmap[1][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "o");
\r
2342 pieceBitmap[1][WhiteBishop] = DoLoadBitmap(hInst, "sb", squareSize, "o");
\r
2343 pieceBitmap[1][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "o");
\r
2344 pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "o");
\r
2345 pieceBitmap[1][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "o");
\r
2346 pieceBitmap[1][WhiteFerz] = DoLoadBitmap(hInst, "sf", squareSize, "o");
\r
2347 pieceBitmap[1][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "o");
\r
2348 pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "su", squareSize, "o");
\r
2349 pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "sh", squareSize, "o");
\r
2350 pieceBitmap[1][WhiteCardinal] = DoLoadBitmap(hInst, "sa", squareSize, "o");
\r
2351 pieceBitmap[1][WhiteDragon] = DoLoadBitmap(hInst, "sc", squareSize, "o");
\r
2352 pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "sg", squareSize, "o");
\r
2353 pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "ss", squareSize, "o");
\r
2354 pieceBitmap[2][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "w");
\r
2355 pieceBitmap[2][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "w");
\r
2356 pieceBitmap[2][WhiteBishop] = DoLoadBitmap(hInst, "sr", squareSize, "w");
\r
2357 pieceBitmap[2][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "w");
\r
2358 pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "w");
\r
2359 pieceBitmap[2][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "w");
\r
2360 pieceBitmap[2][WhiteFerz] = DoLoadBitmap(hInst, "sw", squareSize, "w");
\r
2361 pieceBitmap[2][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "w");
\r
2362 pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "sp", squareSize, "w");
\r
2363 pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "sn", squareSize, "w");
\r
2364 pieceBitmap[2][WhiteCardinal] = DoLoadBitmap(hInst, "sr", squareSize, "w");
\r
2365 pieceBitmap[2][WhiteDragon] = DoLoadBitmap(hInst, "sr", squareSize, "w");
\r
2366 pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "sl", squareSize, "w");
\r
2367 pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "sw", squareSize, "w");
\r
2373 PieceBitmap(ChessSquare p, int kind)
\r
2375 if ((int) p >= (int) BlackPawn)
\r
2376 p = (ChessSquare) ((int) p - (int) BlackPawn + (int) WhitePawn);
\r
2378 return pieceBitmap[kind][(int) p];
\r
2381 /***************************************************************/
\r
2383 #define MIN(a,b) (((a) < (b)) ? (a) : (b))
\r
2384 #define MAX(a,b) (((a) > (b)) ? (a) : (b))
\r
2386 #define MIN3(a,b,c) (((a) < (b) && (a) < (c)) ? (a) : (((b) < (a) && (b) < (c)) ? (b) : (c)))
\r
2387 #define MAX3(a,b,c) (((a) > (b) && (a) > (c)) ? (a) : (((b) > (a) && (b) > (c)) ? (b) : (c)))
\r
2391 SquareToPos(int row, int column, int * x, int * y)
\r
2394 *x = boardRect.left + lineGap + ((BOARD_WIDTH-1)-column) * (squareSize + lineGap);
\r
2395 *y = boardRect.top + lineGap + row * (squareSize + lineGap);
\r
2397 *x = boardRect.left + lineGap + column * (squareSize + lineGap);
\r
2398 *y = boardRect.top + lineGap + ((BOARD_HEIGHT-1)-row) * (squareSize + lineGap);
\r
2403 DrawCoordsOnDC(HDC hdc)
\r
2405 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
2406 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
2407 char str[2] = { NULLCHAR, NULLCHAR };
\r
2408 int oldMode, oldAlign, x, y, start, i;
\r
2412 if (!appData.showCoords)
\r
2415 start = flipView ? 1-(ONE!='1') : 23+(ONE!='1')-BOARD_HEIGHT;
\r
2417 oldBrush = SelectObject(hdc, GetStockObject(BLACK_BRUSH));
\r
2418 oldMode = SetBkMode(hdc, (appData.monoMode ? OPAQUE : TRANSPARENT));
\r
2419 oldAlign = GetTextAlign(hdc);
\r
2420 oldFont = SelectObject(hdc, font[boardSize][COORD_FONT]->hf);
\r
2422 y = boardRect.top + lineGap;
\r
2423 x = boardRect.left + lineGap + gameInfo.holdingsWidth*(squareSize + lineGap);
\r
2425 SetTextAlign(hdc, TA_LEFT|TA_TOP);
\r
2426 for (i = 0; i < BOARD_HEIGHT; i++) {
\r
2427 str[0] = files[start + i];
\r
2428 ExtTextOut(hdc, x + 2, y + 1, 0, NULL, str, 1, NULL);
\r
2429 y += squareSize + lineGap;
\r
2432 start = flipView ? 12-(BOARD_RGHT-BOARD_LEFT) : 12;
\r
2434 SetTextAlign(hdc, TA_RIGHT|TA_BOTTOM);
\r
2435 for (i = 0; i < BOARD_RGHT - BOARD_LEFT; i++) {
\r
2436 str[0] = ranks[start + i];
\r
2437 ExtTextOut(hdc, x + squareSize - 2, y - 1, 0, NULL, str, 1, NULL);
\r
2438 x += squareSize + lineGap;
\r
2441 SelectObject(hdc, oldBrush);
\r
2442 SetBkMode(hdc, oldMode);
\r
2443 SetTextAlign(hdc, oldAlign);
\r
2444 SelectObject(hdc, oldFont);
\r
2448 DrawGridOnDC(HDC hdc)
\r
2452 if (lineGap != 0) {
\r
2453 oldPen = SelectObject(hdc, gridPen);
\r
2454 PolyPolyline(hdc, gridEndpoints, gridVertexCounts, BOARD_WIDTH+BOARD_HEIGHT + 2);
\r
2455 SelectObject(hdc, oldPen);
\r
2459 #define HIGHLIGHT_PEN 0
\r
2460 #define PREMOVE_PEN 1
\r
2463 DrawHighlightOnDC(HDC hdc, BOOLEAN on, int x, int y, int pen)
\r
2466 HPEN oldPen, hPen;
\r
2467 if (lineGap == 0) return;
\r
2469 x1 = boardRect.left +
\r
2470 lineGap/2 + ((BOARD_WIDTH-1)-x) * (squareSize + lineGap);
\r
2471 y1 = boardRect.top +
\r
2472 lineGap/2 + y * (squareSize + lineGap);
\r
2474 x1 = boardRect.left +
\r
2475 lineGap/2 + x * (squareSize + lineGap);
\r
2476 y1 = boardRect.top +
\r
2477 lineGap/2 + ((BOARD_HEIGHT-1)-y) * (squareSize + lineGap);
\r
2479 hPen = pen ? premovePen : highlightPen;
\r
2480 oldPen = SelectObject(hdc, on ? hPen : gridPen);
\r
2481 MoveToEx(hdc, x1, y1, NULL);
\r
2482 LineTo(hdc, x1 + squareSize + lineGap, y1);
\r
2483 LineTo(hdc, x1 + squareSize + lineGap, y1 + squareSize + lineGap);
\r
2484 LineTo(hdc, x1, y1 + squareSize + lineGap);
\r
2485 LineTo(hdc, x1, y1);
\r
2486 SelectObject(hdc, oldPen);
\r
2490 DrawHighlightsOnDC(HDC hdc)
\r
2493 for (i=0; i<2; i++) {
\r
2494 if (highlightInfo.sq[i].x >= 0 && highlightInfo.sq[i].y >= 0)
\r
2495 DrawHighlightOnDC(hdc, TRUE,
\r
2496 highlightInfo.sq[i].x, highlightInfo.sq[i].y,
\r
2499 for (i=0; i<2; i++) {
\r
2500 if (premoveHighlightInfo.sq[i].x >= 0 &&
\r
2501 premoveHighlightInfo.sq[i].y >= 0) {
\r
2502 DrawHighlightOnDC(hdc, TRUE,
\r
2503 premoveHighlightInfo.sq[i].x,
\r
2504 premoveHighlightInfo.sq[i].y,
\r
2510 /* Note: sqcolor is used only in monoMode */
\r
2511 /* Note that this code is largely duplicated in woptions.c,
\r
2512 function DrawSampleSquare, so that needs to be updated too */
\r
2514 DrawPieceOnDC(HDC hdc, ChessSquare piece, int color, int sqcolor, int x, int y, HDC tmphdc)
\r
2516 HBITMAP oldBitmap;
\r
2520 if (appData.blindfold) return;
\r
2522 /* [AS] Use font-based pieces if needed */
\r
2523 if( fontBitmapSquareSize >= 0 && squareSize > 32 ) {
\r
2524 /* Create piece bitmaps, or do nothing if piece set is up to date */
\r
2525 CreatePiecesFromFont();
\r
2527 if( fontBitmapSquareSize == squareSize ) {
\r
2528 int index = TranslatePieceToFontPiece(piece);
\r
2530 SelectObject( tmphdc, hPieceMask[ index ] );
\r
2534 squareSize, squareSize,
\r
2539 SelectObject( tmphdc, hPieceFace[ index ] );
\r
2543 squareSize, squareSize,
\r
2552 if (appData.monoMode) {
\r
2553 SelectObject(tmphdc, PieceBitmap(piece,
\r
2554 color == sqcolor ? OUTLINE_PIECE : SOLID_PIECE));
\r
2555 BitBlt(hdc, x, y, squareSize, squareSize, tmphdc, 0, 0,
\r
2556 sqcolor ? SRCCOPY : NOTSRCCOPY);
\r
2558 tmpSize = squareSize;
\r
2560 ((piece >= (int)WhiteNightrider && piece <= WhiteGrasshopper) ||
\r
2561 (piece >= (int)BlackNightrider && piece <= BlackGrasshopper)) ) {
\r
2562 /* [HGM] no bitmap available for promoted pieces in Crazyhouse */
\r
2563 /* Bitmaps of smaller size are substituted, but we have to align them */
\r
2564 x += (squareSize - minorSize)>>1;
\r
2565 y += squareSize - minorSize - 2;
\r
2566 tmpSize = minorSize;
\r
2568 if (color || appData.allWhite ) {
\r
2569 oldBitmap = SelectObject(tmphdc, PieceBitmap(piece, WHITE_PIECE));
\r
2571 oldBrush = SelectObject(hdc, whitePieceBrush);
\r
2572 else oldBrush = SelectObject(hdc, blackPieceBrush);
\r
2573 if(appData.upsideDown && color==flipView)
\r
2574 StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, 0x00B8074A);
\r
2576 BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, 0x00B8074A);
\r
2577 /* Use black for outline of white pieces */
\r
2578 SelectObject(tmphdc, PieceBitmap(piece, OUTLINE_PIECE));
\r
2579 if(appData.upsideDown && color==flipView)
\r
2580 StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, SRCAND);
\r
2582 BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, SRCAND);
\r
2584 /* Use square color for details of black pieces */
\r
2585 oldBitmap = SelectObject(tmphdc, PieceBitmap(piece, SOLID_PIECE));
\r
2586 oldBrush = SelectObject(hdc, blackPieceBrush);
\r
2587 if(appData.upsideDown && !flipView)
\r
2588 StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, 0x00B8074A);
\r
2590 BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, 0x00B8074A);
\r
2592 SelectObject(hdc, oldBrush);
\r
2593 SelectObject(tmphdc, oldBitmap);
\r
2597 /* [AS] Compute a drawing mode for a square, based on specified settings (see DrawTile) */
\r
2598 int GetBackTextureMode( int algo )
\r
2600 int result = BACK_TEXTURE_MODE_DISABLED;
\r
2604 case BACK_TEXTURE_MODE_PLAIN:
\r
2605 result = 1; /* Always use identity map */
\r
2607 case BACK_TEXTURE_MODE_FULL_RANDOM:
\r
2608 result = 1 + (myrandom() % 3); /* Pick a transformation at random */
\r
2616 [AS] Compute and save texture drawing info, otherwise we may not be able
\r
2617 to handle redraws cleanly (as random numbers would always be different).
\r
2619 VOID RebuildTextureSquareInfo()
\r
2629 ZeroMemory( &backTextureSquareInfo, sizeof(backTextureSquareInfo) );
\r
2631 if( liteBackTexture != NULL ) {
\r
2632 if( GetObject( liteBackTexture, sizeof(bi), &bi ) > 0 ) {
\r
2633 lite_w = bi.bmWidth;
\r
2634 lite_h = bi.bmHeight;
\r
2638 if( darkBackTexture != NULL ) {
\r
2639 if( GetObject( darkBackTexture, sizeof(bi), &bi ) > 0 ) {
\r
2640 dark_w = bi.bmWidth;
\r
2641 dark_h = bi.bmHeight;
\r
2645 for( row=0; row<BOARD_HEIGHT; row++ ) {
\r
2646 for( col=0; col<BOARD_WIDTH; col++ ) {
\r
2647 if( (col + row) & 1 ) {
\r
2649 if( lite_w >= squareSize && lite_h >= squareSize ) {
\r
2650 backTextureSquareInfo[row][col].x = col * (lite_w - squareSize) / (BOARD_WIDTH-1); /* [HGM] divide by size-1 in stead of size! */
\r
2651 backTextureSquareInfo[row][col].y = (BOARD_HEIGHT-1-row) * (lite_h - squareSize) / (BOARD_HEIGHT-1);
\r
2652 backTextureSquareInfo[row][col].mode = GetBackTextureMode(liteBackTextureMode);
\r
2657 if( dark_w >= squareSize && dark_h >= squareSize ) {
\r
2658 backTextureSquareInfo[row][col].x = col * (dark_w - squareSize) / (BOARD_WIDTH-1);
\r
2659 backTextureSquareInfo[row][col].y = (BOARD_HEIGHT-1-row) * (dark_h - squareSize) / (BOARD_HEIGHT-1);
\r
2660 backTextureSquareInfo[row][col].mode = GetBackTextureMode(darkBackTextureMode);
\r
2667 /* [AS] Arrow highlighting support */
\r
2669 static int A_WIDTH = 5; /* Width of arrow body */
\r
2671 #define A_HEIGHT_FACTOR 6 /* Length of arrow "point", relative to body width */
\r
2672 #define A_WIDTH_FACTOR 3 /* Width of arrow "point", relative to body width */
\r
2674 static double Sqr( double x )
\r
2679 static int Round( double x )
\r
2681 return (int) (x + 0.5);
\r
2684 /* Draw an arrow between two points using current settings */
\r
2685 VOID DrawArrowBetweenPoints( HDC hdc, int s_x, int s_y, int d_x, int d_y )
\r
2688 double dx, dy, j, k, x, y;
\r
2690 if( d_x == s_x ) {
\r
2691 int h = (d_y > s_y) ? +A_WIDTH*A_HEIGHT_FACTOR : -A_WIDTH*A_HEIGHT_FACTOR;
\r
2693 arrow[0].x = s_x + A_WIDTH;
\r
2696 arrow[1].x = s_x + A_WIDTH;
\r
2697 arrow[1].y = d_y - h;
\r
2699 arrow[2].x = s_x + A_WIDTH*A_WIDTH_FACTOR;
\r
2700 arrow[2].y = d_y - h;
\r
2705 arrow[4].x = s_x - A_WIDTH*A_WIDTH_FACTOR;
\r
2706 arrow[4].y = d_y - h;
\r
2708 arrow[5].x = s_x - A_WIDTH;
\r
2709 arrow[5].y = d_y - h;
\r
2711 arrow[6].x = s_x - A_WIDTH;
\r
2714 else if( d_y == s_y ) {
\r
2715 int w = (d_x > s_x) ? +A_WIDTH*A_HEIGHT_FACTOR : -A_WIDTH*A_HEIGHT_FACTOR;
\r
2718 arrow[0].y = s_y + A_WIDTH;
\r
2720 arrow[1].x = d_x - w;
\r
2721 arrow[1].y = s_y + A_WIDTH;
\r
2723 arrow[2].x = d_x - w;
\r
2724 arrow[2].y = s_y + A_WIDTH*A_WIDTH_FACTOR;
\r
2729 arrow[4].x = d_x - w;
\r
2730 arrow[4].y = s_y - A_WIDTH*A_WIDTH_FACTOR;
\r
2732 arrow[5].x = d_x - w;
\r
2733 arrow[5].y = s_y - A_WIDTH;
\r
2736 arrow[6].y = s_y - A_WIDTH;
\r
2739 /* [AS] Needed a lot of paper for this! :-) */
\r
2740 dy = (double) (d_y - s_y) / (double) (d_x - s_x);
\r
2741 dx = (double) (s_x - d_x) / (double) (s_y - d_y);
\r
2743 j = sqrt( Sqr(A_WIDTH) / (1.0 + Sqr(dx)) );
\r
2745 k = sqrt( Sqr(A_WIDTH*A_HEIGHT_FACTOR) / (1.0 + Sqr(dy)) );
\r
2750 arrow[0].x = Round(x - j);
\r
2751 arrow[0].y = Round(y + j*dx);
\r
2753 arrow[1].x = Round(x + j);
\r
2754 arrow[1].y = Round(y - j*dx);
\r
2757 x = (double) d_x - k;
\r
2758 y = (double) d_y - k*dy;
\r
2761 x = (double) d_x + k;
\r
2762 y = (double) d_y + k*dy;
\r
2765 arrow[2].x = Round(x + j);
\r
2766 arrow[2].y = Round(y - j*dx);
\r
2768 arrow[3].x = Round(x + j*A_WIDTH_FACTOR);
\r
2769 arrow[3].y = Round(y - j*A_WIDTH_FACTOR*dx);
\r
2774 arrow[5].x = Round(x - j*A_WIDTH_FACTOR);
\r
2775 arrow[5].y = Round(y + j*A_WIDTH_FACTOR*dx);
\r
2777 arrow[6].x = Round(x - j);
\r
2778 arrow[6].y = Round(y + j*dx);
\r
2781 Polygon( hdc, arrow, 7 );
\r
2784 /* [AS] Draw an arrow between two squares */
\r
2785 VOID DrawArrowBetweenSquares( HDC hdc, int s_col, int s_row, int d_col, int d_row )
\r
2787 int s_x, s_y, d_x, d_y;
\r
2794 if( s_col == d_col && s_row == d_row ) {
\r
2798 /* Get source and destination points */
\r
2799 SquareToPos( s_row, s_col, &s_x, &s_y);
\r
2800 SquareToPos( d_row, d_col, &d_x, &d_y);
\r
2803 d_y += squareSize / 4;
\r
2805 else if( d_y < s_y ) {
\r
2806 d_y += 3 * squareSize / 4;
\r
2809 d_y += squareSize / 2;
\r
2813 d_x += squareSize / 4;
\r
2815 else if( d_x < s_x ) {
\r
2816 d_x += 3 * squareSize / 4;
\r
2819 d_x += squareSize / 2;
\r
2822 s_x += squareSize / 2;
\r
2823 s_y += squareSize / 2;
\r
2825 /* Adjust width */
\r
2826 A_WIDTH = squareSize / 14;
\r
2829 stLB.lbStyle = BS_SOLID;
\r
2830 stLB.lbColor = appData.highlightArrowColor;
\r
2833 hpen = CreatePen( PS_SOLID, 2, RGB(0x00,0x00,0x00) );
\r
2834 holdpen = SelectObject( hdc, hpen );
\r
2835 hbrush = CreateBrushIndirect( &stLB );
\r
2836 holdbrush = SelectObject( hdc, hbrush );
\r
2838 DrawArrowBetweenPoints( hdc, s_x, s_y, d_x, d_y );
\r
2840 SelectObject( hdc, holdpen );
\r
2841 SelectObject( hdc, holdbrush );
\r
2842 DeleteObject( hpen );
\r
2843 DeleteObject( hbrush );
\r
2846 BOOL HasHighlightInfo()
\r
2848 BOOL result = FALSE;
\r
2850 if( highlightInfo.sq[0].x >= 0 && highlightInfo.sq[0].y >= 0 &&
\r
2851 highlightInfo.sq[1].x >= 0 && highlightInfo.sq[1].y >= 0 )
\r
2859 BOOL IsDrawArrowEnabled()
\r
2861 BOOL result = FALSE;
\r
2863 if( appData.highlightMoveWithArrow && squareSize >= 32 ) {
\r
2870 VOID DrawArrowHighlight( HDC hdc )
\r
2872 if( IsDrawArrowEnabled() && HasHighlightInfo() ) {
\r
2873 DrawArrowBetweenSquares( hdc,
\r
2874 highlightInfo.sq[0].x, highlightInfo.sq[0].y,
\r
2875 highlightInfo.sq[1].x, highlightInfo.sq[1].y );
\r
2879 HRGN GetArrowHighlightClipRegion( HDC hdc )
\r
2881 HRGN result = NULL;
\r
2883 if( HasHighlightInfo() ) {
\r
2884 int x1, y1, x2, y2;
\r
2885 int sx, sy, dx, dy;
\r
2887 SquareToPos(highlightInfo.sq[0].y, highlightInfo.sq[0].x, &x1, &y1 );
\r
2888 SquareToPos(highlightInfo.sq[1].y, highlightInfo.sq[1].x, &x2, &y2 );
\r
2890 sx = MIN( x1, x2 );
\r
2891 sy = MIN( y1, y2 );
\r
2892 dx = MAX( x1, x2 ) + squareSize;
\r
2893 dy = MAX( y1, y2 ) + squareSize;
\r
2895 result = CreateRectRgn( sx, sy, dx, dy );
\r
2902 Warning: this function modifies the behavior of several other functions.
\r
2904 Basically, Winboard is optimized to avoid drawing the whole board if not strictly
\r
2905 needed. Unfortunately, the decision whether or not to perform a full or partial
\r
2906 repaint is scattered all over the place, which is not good for features such as
\r
2907 "arrow highlighting" that require a full repaint of the board.
\r
2909 So, I've tried to patch the code where I thought it made sense (e.g. after or during
\r
2910 user interaction, when speed is not so important) but especially to avoid errors
\r
2911 in the displayed graphics.
\r
2913 In such patched places, I always try refer to this function so there is a single
\r
2914 place to maintain knowledge.
\r
2916 To restore the original behavior, just return FALSE unconditionally.
\r
2918 BOOL IsFullRepaintPreferrable()
\r
2920 BOOL result = FALSE;
\r
2922 if( (appData.highlightLastMove || appData.highlightDragging) && IsDrawArrowEnabled() ) {
\r
2923 /* Arrow may appear on the board */
\r
2931 This function is called by DrawPosition to know whether a full repaint must
\r
2934 Only DrawPosition may directly call this function, which makes use of
\r
2935 some state information. Other function should call DrawPosition specifying
\r
2936 the repaint flag, and can use IsFullRepaintPreferrable if needed.
\r
2938 BOOL DrawPositionNeedsFullRepaint()
\r
2940 BOOL result = FALSE;
\r
2943 Probably a slightly better policy would be to trigger a full repaint
\r
2944 when animInfo.piece changes state (i.e. empty -> non-empty and viceversa),
\r
2945 but animation is fast enough that it's difficult to notice.
\r
2947 if( animInfo.piece == EmptySquare ) {
\r
2948 if( (appData.highlightLastMove || appData.highlightDragging) && IsDrawArrowEnabled() /*&& HasHighlightInfo()*/ ) {
\r
2957 DrawBoardOnDC(HDC hdc, Board board, HDC tmphdc)
\r
2959 int row, column, x, y, square_color, piece_color;
\r
2960 ChessSquare piece;
\r
2962 HDC texture_hdc = NULL;
\r
2964 /* [AS] Initialize background textures if needed */
\r
2965 if( liteBackTexture != NULL || darkBackTexture != NULL ) {
\r
2966 static int backTextureBoardSize; /* [HGM] boardsize: also new texture if board format changed */
\r
2967 if( backTextureSquareSize != squareSize
\r
2968 || backTextureBoardSize != BOARD_WIDTH+BOARD_FILES*BOARD_HEIGHT) {
\r
2969 backTextureBoardSize = BOARD_WIDTH+BOARD_FILES*BOARD_HEIGHT;
\r
2970 backTextureSquareSize = squareSize;
\r
2971 RebuildTextureSquareInfo();
\r
2974 texture_hdc = CreateCompatibleDC( hdc );
\r
2977 for (row = 0; row < BOARD_HEIGHT; row++) {
\r
2978 for (column = 0; column < BOARD_WIDTH; column++) {
\r
2980 SquareToPos(row, column, &x, &y);
\r
2982 piece = board[row][column];
\r
2984 square_color = ((column + row) % 2) == 1;
\r
2985 if( gameInfo.variant == VariantXiangqi ) {
\r
2986 square_color = !InPalace(row, column);
\r
2987 if(BOARD_HEIGHT&1) { if(row==BOARD_HEIGHT/2) square_color ^= 1; }
\r
2988 else if(row < BOARD_HEIGHT/2) square_color ^= 1;
\r
2990 piece_color = (int) piece < (int) BlackPawn;
\r
2993 /* [HGM] holdings file: light square or black */
\r
2994 if(column == BOARD_LEFT-2) {
\r
2995 if( row > BOARD_HEIGHT - gameInfo.holdingsSize - 1 )
\r
2998 DisplayHoldingsCount(hdc, x, y, 0, 0); /* black out */
\r
3002 if(column == BOARD_RGHT + 1 ) {
\r
3003 if( row < gameInfo.holdingsSize )
\r
3006 DisplayHoldingsCount(hdc, x, y, 0, 0);
\r
3010 if(column == BOARD_LEFT-1 ) /* left align */
\r
3011 DisplayHoldingsCount(hdc, x, y, flipView, (int) board[row][column]);
\r
3012 else if( column == BOARD_RGHT) /* right align */
\r
3013 DisplayHoldingsCount(hdc, x, y, !flipView, (int) board[row][column]);
\r
3015 if (appData.monoMode) {
\r
3016 if (piece == EmptySquare) {
\r
3017 BitBlt(hdc, x, y, squareSize, squareSize, 0, 0, 0,
\r
3018 square_color ? WHITENESS : BLACKNESS);
\r
3020 DrawPieceOnDC(hdc, piece, piece_color, square_color, x, y, tmphdc);
\r
3023 else if( backTextureSquareInfo[row][column].mode > 0 ) {
\r
3024 /* [AS] Draw the square using a texture bitmap */
\r
3025 HBITMAP hbm = SelectObject( texture_hdc, square_color ? liteBackTexture : darkBackTexture );
\r
3026 int r = row, c = column; // [HGM] do not flip board in flipView
\r
3027 if(flipView) { r = BOARD_HEIGHT-1 - r; c = BOARD_WIDTH-1 - c; }
\r
3030 squareSize, squareSize,
\r
3033 backTextureSquareInfo[r][c].mode,
\r
3034 backTextureSquareInfo[r][c].x,
\r
3035 backTextureSquareInfo[r][c].y );
\r
3037 SelectObject( texture_hdc, hbm );
\r
3039 if (piece != EmptySquare) {
\r
3040 DrawPieceOnDC(hdc, piece, piece_color, -1, x, y, tmphdc);
\r
3044 HBRUSH brush = square_color ? lightSquareBrush : darkSquareBrush;
\r
3046 oldBrush = SelectObject(hdc, brush );
\r
3047 BitBlt(hdc, x, y, squareSize, squareSize, 0, 0, 0, PATCOPY);
\r
3048 SelectObject(hdc, oldBrush);
\r
3049 if (piece != EmptySquare)
\r
3050 DrawPieceOnDC(hdc, piece, piece_color, -1, x, y, tmphdc);
\r
3055 if( texture_hdc != NULL ) {
\r
3056 DeleteDC( texture_hdc );
\r
3060 int saveDiagFlag = 0; FILE *diagFile; // [HGM] diag
\r
3061 void fputDW(FILE *f, int x)
\r
3063 fputc(x & 255, f);
\r
3064 fputc(x>>8 & 255, f);
\r
3065 fputc(x>>16 & 255, f);
\r
3066 fputc(x>>24 & 255, f);
\r
3069 #define MAX_CLIPS 200 /* more than enough */
\r
3072 DrawLogoOnDC(HDC hdc, RECT logoRect, HBITMAP logo)
\r
3074 // HBITMAP bufferBitmap;
\r
3079 int w = 100, h = 50;
\r
3081 if(logo == NULL) return;
\r
3082 // GetClientRect(hwndMain, &Rect);
\r
3083 // bufferBitmap = CreateCompatibleBitmap(hdc, Rect.right-Rect.left+1,
\r
3084 // Rect.bottom-Rect.top+1);
\r
3085 tmphdc = CreateCompatibleDC(hdc);
\r
3086 hbm = SelectObject(tmphdc, logo);
\r
3087 if( GetObject( logo, sizeof(bi), &bi ) > 0 ) {
\r
3091 StretchBlt(hdc, logoRect.left, logoRect.top, logoRect.right - logoRect.left,
\r
3092 logoRect.bottom - logoRect.top, tmphdc, 0, 0, w, h, SRCCOPY);
\r
3093 SelectObject(tmphdc, hbm);
\r
3097 static HDC hdcSeek;
\r
3099 // [HGM] seekgraph
\r
3100 void DrawSeekAxis( int x, int y, int xTo, int yTo )
\r
3103 HPEN hp = SelectObject( hdcSeek, gridPen );
\r
3104 MoveToEx( hdcSeek, boardRect.left+x, boardRect.top+y, &stPt );
\r
3105 LineTo( hdcSeek, boardRect.left+xTo, boardRect.top+yTo );
\r
3106 SelectObject( hdcSeek, hp );
\r
3109 // front-end wrapper for drawing functions to do rectangles
\r
3110 void DrawSeekBackground( int left, int top, int right, int bottom )
\r
3115 if (hdcSeek == NULL) {
\r
3116 hdcSeek = GetDC(hwndMain);
\r
3117 if (!appData.monoMode) {
\r
3118 SelectPalette(hdcSeek, hPal, FALSE);
\r
3119 RealizePalette(hdcSeek);
\r
3122 hp = SelectObject( hdcSeek, gridPen );
\r
3123 rc.top = boardRect.top+top; rc.left = boardRect.left+left;
\r
3124 rc.bottom = boardRect.top+bottom; rc.right = boardRect.left+right;
\r
3125 FillRect( hdcSeek, &rc, lightSquareBrush );
\r
3126 SelectObject( hdcSeek, hp );
\r
3129 // front-end wrapper for putting text in graph
\r
3130 void DrawSeekText(char *buf, int x, int y)
\r
3133 SetBkMode( hdcSeek, TRANSPARENT );
\r
3134 GetTextExtentPoint32( hdcSeek, buf, strlen(buf), &stSize );
\r
3135 TextOut( hdcSeek, boardRect.left+x-3, boardRect.top+y-stSize.cy/2, buf, strlen(buf) );
\r
3138 void DrawSeekDot(int x, int y, int color)
\r
3140 int square = color & 0x80;
\r
3142 HBRUSH oldBrush = SelectObject(hdcSeek,
\r
3143 color == 0 ? markerBrush : color == 1 ? darkSquareBrush : explodeBrush);
\r
3145 Rectangle(hdcSeek, boardRect.left+x - squareSize/9, boardRect.top+y - squareSize/9,
\r
3146 boardRect.left+x + squareSize/9, boardRect.top+y + squareSize/9);
\r
3148 Ellipse(hdcSeek, boardRect.left+x - squareSize/8, boardRect.top+y - squareSize/8,
\r
3149 boardRect.left+x + squareSize/8, boardRect.top+y + squareSize/8);
\r
3150 SelectObject(hdcSeek, oldBrush);
\r
3154 HDCDrawPosition(HDC hdc, BOOLEAN repaint, Board board)
\r
3156 static Board lastReq, lastDrawn;
\r
3157 static HighlightInfo lastDrawnHighlight, lastDrawnPremove;
\r
3158 static int lastDrawnFlipView = 0;
\r
3159 static int lastReqValid = 0, lastDrawnValid = 0;
\r
3160 int releaseDC, x, y, x2, y2, row, column, num_clips = 0, i;
\r
3163 HBITMAP bufferBitmap;
\r
3164 HBITMAP oldBitmap;
\r
3166 HRGN clips[MAX_CLIPS];
\r
3167 ChessSquare dragged_piece = EmptySquare;
\r
3169 /* I'm undecided on this - this function figures out whether a full
\r
3170 * repaint is necessary on its own, so there's no real reason to have the
\r
3171 * caller tell it that. I think this can safely be set to FALSE - but
\r
3172 * if we trust the callers not to request full repaints unnessesarily, then
\r
3173 * we could skip some clipping work. In other words, only request a full
\r
3174 * redraw when the majority of pieces have changed positions (ie. flip,
\r
3175 * gamestart and similar) --Hawk
\r
3177 Boolean fullrepaint = repaint;
\r
3179 if(DrawSeekGraph()) return; // [HG} seekgraph: suppress printing board if seek graph up
\r
3181 if( DrawPositionNeedsFullRepaint() ) {
\r
3182 fullrepaint = TRUE;
\r
3185 if (board == NULL) {
\r
3186 if (!lastReqValid) {
\r
3191 CopyBoard(lastReq, board);
\r
3195 if (doingSizing) {
\r
3199 if (IsIconic(hwndMain)) {
\r
3203 if (hdc == NULL) {
\r
3204 hdc = GetDC(hwndMain);
\r
3205 if (!appData.monoMode) {
\r
3206 SelectPalette(hdc, hPal, FALSE);
\r
3207 RealizePalette(hdc);
\r
3211 releaseDC = FALSE;
\r
3214 /* Create some work-DCs */
\r
3215 hdcmem = CreateCompatibleDC(hdc);
\r
3216 tmphdc = CreateCompatibleDC(hdc);
\r
3218 /* If dragging is in progress, we temporarely remove the piece */
\r
3219 /* [HGM] or temporarily decrease count if stacked */
\r
3220 /* !! Moved to before board compare !! */
\r
3221 if (dragInfo.from.x >= 0 && dragInfo.pos.x >= 0) {
\r
3222 dragged_piece = board[dragInfo.from.y][dragInfo.from.x];
\r
3223 if(dragInfo.from.x == BOARD_LEFT-2 ) {
\r
3224 if(--board[dragInfo.from.y][dragInfo.from.x+1] == 0 )
\r