2 * WinBoard.c -- Windows NT front end to XBoard
\r
4 * Copyright 1991 by Digital Equipment Corporation, Maynard,
\r
7 * Enhancements Copyright 1992-2001, 2002, 2003, 2004, 2005, 2006,
\r
8 * 2007, 2008, 2009, 2010 Free Software Foundation, Inc.
\r
10 * Enhancements Copyright 2005 Alessandro Scotti
\r
12 * XBoard borrows its colors and the bitmaps.xchess bitmap set from XChess,
\r
13 * which was written and is copyrighted by Wayne Christopher.
\r
15 * The following terms apply to Digital Equipment Corporation's copyright
\r
16 * interest in XBoard:
\r
17 * ------------------------------------------------------------------------
\r
18 * All Rights Reserved
\r
20 * Permission to use, copy, modify, and distribute this software and its
\r
21 * documentation for any purpose and without fee is hereby granted,
\r
22 * provided that the above copyright notice appear in all copies and that
\r
23 * both that copyright notice and this permission notice appear in
\r
24 * supporting documentation, and that the name of Digital not be
\r
25 * used in advertising or publicity pertaining to distribution of the
\r
26 * software without specific, written prior permission.
\r
28 * DIGITAL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
\r
29 * ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL
\r
30 * DIGITAL BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
\r
31 * ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
\r
32 * WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
\r
33 * ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
\r
35 * ------------------------------------------------------------------------
\r
37 * The following terms apply to the enhanced version of XBoard
\r
38 * distributed by the Free Software Foundation:
\r
39 * ------------------------------------------------------------------------
\r
41 * GNU XBoard is free software: you can redistribute it and/or modify
\r
42 * it under the terms of the GNU General Public License as published by
\r
43 * the Free Software Foundation, either version 3 of the License, or (at
\r
44 * your option) any later version.
\r
46 * GNU XBoard is distributed in the hope that it will be useful, but
\r
47 * WITHOUT ANY WARRANTY; without even the implied warranty of
\r
48 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
\r
49 * General Public License for more details.
\r
51 * You should have received a copy of the GNU General Public License
\r
52 * along with this program. If not, see http://www.gnu.org/licenses/. *
\r
54 *------------------------------------------------------------------------
\r
55 ** See the file ChangeLog for a revision history. */
\r
59 #include <windows.h>
\r
60 #include <winuser.h>
\r
61 #include <winsock.h>
\r
62 #include <commctrl.h>
\r
68 #include <sys/stat.h>
\r
71 #include <commdlg.h>
\r
73 #include <richedit.h>
\r
74 #include <mmsystem.h>
\r
83 #include "frontend.h"
\r
84 #include "backend.h"
\r
85 #include "winboard.h"
\r
87 #include "wclipbrd.h"
\r
88 #include "woptions.h"
\r
89 #include "wsockerr.h"
\r
90 #include "defaults.h"
\r
94 //void InitEngineUCI( const char * iniDir, ChessProgramState * cps );
\r
97 void mysrandom(unsigned int seed);
\r
99 extern int whiteFlag, blackFlag;
\r
100 Boolean flipClock = FALSE;
\r
101 extern HANDLE chatHandle[];
\r
102 extern int ics_type;
\r
104 void DisplayHoldingsCount(HDC hdc, int x, int y, int align, int copyNumber);
\r
105 VOID NewVariantPopup(HWND hwnd);
\r
106 int FinishMove P((ChessMove moveType, int fromX, int fromY, int toX, int toY,
\r
107 /*char*/int promoChar));
\r
108 void AnimateAtomicCapture(int fromX, int fromY, int toX, int toY, int nFrames);
\r
109 void DisplayMove P((int moveNumber));
\r
110 Boolean ParseFEN P((Board board, int *blackPlaysFirst, char *fen));
\r
111 void ChatPopUp P((char *s));
\r
113 ChessSquare piece;
\r
114 POINT pos; /* window coordinates of current pos */
\r
115 POINT lastpos; /* window coordinates of last pos - used for clipping */
\r
116 POINT from; /* board coordinates of the piece's orig pos */
\r
117 POINT to; /* board coordinates of the piece's new pos */
\r
120 static AnimInfo animInfo = { EmptySquare, {-1,-1}, {-1,-1}, {-1,-1} };
\r
123 POINT start; /* window coordinates of start pos */
\r
124 POINT pos; /* window coordinates of current pos */
\r
125 POINT lastpos; /* window coordinates of last pos - used for clipping */
\r
126 POINT from; /* board coordinates of the piece's orig pos */
\r
129 static DragInfo dragInfo = { {-1,-1}, {-1,-1}, {-1,-1}, {-1,-1} };
\r
132 POINT sq[2]; /* board coordinates of from, to squares */
\r
135 static HighlightInfo highlightInfo = { {{-1, -1}, {-1, -1}} };
\r
136 static HighlightInfo premoveHighlightInfo = { {{-1, -1}, {-1, -1}} };
\r
137 static HighlightInfo partnerHighlightInfo = { {{-1, -1}, {-1, -1}} };
\r
138 static HighlightInfo oldPartnerHighlight = { {{-1, -1}, {-1, -1}} };
\r
140 typedef struct { // [HGM] atomic
\r
141 int fromX, fromY, toX, toY, radius;
\r
144 static ExplodeInfo explodeInfo;
\r
146 /* Window class names */
\r
147 char szAppName[] = "WinBoard";
\r
148 char szConsoleName[] = "WBConsole";
\r
150 /* Title bar text */
\r
151 char szTitle[] = "WinBoard";
\r
152 char szConsoleTitle[] = "I C S Interaction";
\r
155 char *settingsFileName;
\r
156 Boolean saveSettingsOnExit;
\r
157 char installDir[MSG_SIZ];
\r
158 int errorExitStatus;
\r
160 BoardSize boardSize;
\r
161 Boolean chessProgram;
\r
162 //static int boardX, boardY;
\r
163 int minX, minY; // [HGM] placement: volatile limits on upper-left corner
\r
164 int squareSize, lineGap, minorSize;
\r
165 static int winW, winH;
\r
166 static RECT messageRect, whiteRect, blackRect, leftLogoRect, rightLogoRect; // [HGM] logo
\r
167 static int logoHeight = 0;
\r
168 static char messageText[MESSAGE_TEXT_MAX];
\r
169 static int clockTimerEvent = 0;
\r
170 static int loadGameTimerEvent = 0;
\r
171 static int analysisTimerEvent = 0;
\r
172 static DelayedEventCallback delayedTimerCallback;
\r
173 static int delayedTimerEvent = 0;
\r
174 static int buttonCount = 2;
\r
175 char *icsTextMenuString;
\r
177 char *firstChessProgramNames;
\r
178 char *secondChessProgramNames;
\r
180 #define PALETTESIZE 256
\r
182 HINSTANCE hInst; /* current instance */
\r
183 Boolean alwaysOnTop = FALSE;
\r
185 COLORREF lightSquareColor, darkSquareColor, whitePieceColor,
\r
186 blackPieceColor, highlightSquareColor, premoveHighlightColor;
\r
188 ColorClass currentColorClass;
\r
190 HWND hCommPort = NULL; /* currently open comm port */
\r
191 static HWND hwndPause; /* pause button */
\r
192 static HBITMAP pieceBitmap[3][(int) BlackPawn]; /* [HGM] nr of bitmaps referred to bP in stead of wK */
\r
193 static HBRUSH lightSquareBrush, darkSquareBrush,
\r
194 blackSquareBrush, /* [HGM] for band between board and holdings */
\r
195 explodeBrush, /* [HGM] atomic */
\r
196 markerBrush, /* [HGM] markers */
\r
197 whitePieceBrush, blackPieceBrush, iconBkgndBrush /*, outlineBrush*/;
\r
198 static POINT gridEndpoints[(BOARD_RANKS + BOARD_FILES + 2) * 2];
\r
199 static DWORD gridVertexCounts[BOARD_RANKS + BOARD_FILES + 2];
\r
200 static HPEN gridPen = NULL;
\r
201 static HPEN highlightPen = NULL;
\r
202 static HPEN premovePen = NULL;
\r
203 static NPLOGPALETTE pLogPal;
\r
204 static BOOL paletteChanged = FALSE;
\r
205 static HICON iconWhite, iconBlack, iconCurrent;
\r
206 static int doingSizing = FALSE;
\r
207 static int lastSizing = 0;
\r
208 static int prevStderrPort;
\r
209 static HBITMAP userLogo;
\r
211 static HBITMAP liteBackTexture = NULL;
\r
212 static HBITMAP darkBackTexture = NULL;
\r
213 static int liteBackTextureMode = BACK_TEXTURE_MODE_PLAIN;
\r
214 static int darkBackTextureMode = BACK_TEXTURE_MODE_PLAIN;
\r
215 static int backTextureSquareSize = 0;
\r
216 static struct { int x; int y; int mode; } backTextureSquareInfo[BOARD_RANKS][BOARD_FILES];
\r
218 #if __GNUC__ && !defined(_winmajor)
\r
219 #define oldDialog 0 /* cygwin doesn't define _winmajor; mingw does */
\r
221 #if defined(_winmajor)
\r
222 #define oldDialog (_winmajor < 4)
\r
224 #define oldDialog 0
\r
234 int cliWidth, cliHeight;
\r
237 SizeInfo sizeInfo[] =
\r
239 { "tiny", 21, 0, 1, 1, 0, 0 },
\r
240 { "teeny", 25, 1, 1, 1, 0, 0 },
\r
241 { "dinky", 29, 1, 1, 1, 0, 0 },
\r
242 { "petite", 33, 1, 1, 1, 0, 0 },
\r
243 { "slim", 37, 2, 1, 0, 0, 0 },
\r
244 { "small", 40, 2, 1, 0, 0, 0 },
\r
245 { "mediocre", 45, 2, 1, 0, 0, 0 },
\r
246 { "middling", 49, 2, 0, 0, 0, 0 },
\r
247 { "average", 54, 2, 0, 0, 0, 0 },
\r
248 { "moderate", 58, 3, 0, 0, 0, 0 },
\r
249 { "medium", 64, 3, 0, 0, 0, 0 },
\r
250 { "bulky", 72, 3, 0, 0, 0, 0 },
\r
251 { "large", 80, 3, 0, 0, 0, 0 },
\r
252 { "big", 87, 3, 0, 0, 0, 0 },
\r
253 { "huge", 95, 3, 0, 0, 0, 0 },
\r
254 { "giant", 108, 3, 0, 0, 0, 0 },
\r
255 { "colossal", 116, 4, 0, 0, 0, 0 },
\r
256 { "titanic", 129, 4, 0, 0, 0, 0 },
\r
257 { NULL, 0, 0, 0, 0, 0, 0 }
\r
260 #define MF(x) {x, {{0,}, 0. }, {0, }, 0}
\r
261 MyFont fontRec[NUM_SIZES][NUM_FONTS] =
\r
263 { MF(CLOCK_FONT_TINY), MF(MESSAGE_FONT_TINY), MF(COORD_FONT_TINY), MF(CONSOLE_FONT_TINY), MF(COMMENT_FONT_TINY), MF(EDITTAGS_FONT_TINY), MF(MOVEHISTORY_FONT_ALL) },
\r
264 { MF(CLOCK_FONT_TEENY), MF(MESSAGE_FONT_TEENY), MF(COORD_FONT_TEENY), MF(CONSOLE_FONT_TEENY), MF(COMMENT_FONT_TEENY), MF(EDITTAGS_FONT_TEENY), MF(MOVEHISTORY_FONT_ALL) },
\r
265 { MF(CLOCK_FONT_DINKY), MF(MESSAGE_FONT_DINKY), MF(COORD_FONT_DINKY), MF(CONSOLE_FONT_DINKY), MF(COMMENT_FONT_DINKY), MF(EDITTAGS_FONT_DINKY), MF(MOVEHISTORY_FONT_ALL) },
\r
266 { MF(CLOCK_FONT_PETITE), MF(MESSAGE_FONT_PETITE), MF(COORD_FONT_PETITE), MF(CONSOLE_FONT_PETITE), MF(COMMENT_FONT_PETITE), MF(EDITTAGS_FONT_PETITE), MF(MOVEHISTORY_FONT_ALL) },
\r
267 { MF(CLOCK_FONT_SLIM), MF(MESSAGE_FONT_SLIM), MF(COORD_FONT_SLIM), MF(CONSOLE_FONT_SLIM), MF(COMMENT_FONT_SLIM), MF(EDITTAGS_FONT_SLIM), MF(MOVEHISTORY_FONT_ALL) },
\r
268 { MF(CLOCK_FONT_SMALL), MF(MESSAGE_FONT_SMALL), MF(COORD_FONT_SMALL), MF(CONSOLE_FONT_SMALL), MF(COMMENT_FONT_SMALL), MF(EDITTAGS_FONT_SMALL), MF(MOVEHISTORY_FONT_ALL) },
\r
269 { MF(CLOCK_FONT_MEDIOCRE), MF(MESSAGE_FONT_MEDIOCRE), MF(COORD_FONT_MEDIOCRE), MF(CONSOLE_FONT_MEDIOCRE), MF(COMMENT_FONT_MEDIOCRE), MF(EDITTAGS_FONT_MEDIOCRE), MF(MOVEHISTORY_FONT_ALL) },
\r
270 { MF(CLOCK_FONT_MIDDLING), MF(MESSAGE_FONT_MIDDLING), MF(COORD_FONT_MIDDLING), MF(CONSOLE_FONT_MIDDLING), MF(COMMENT_FONT_MIDDLING), MF(EDITTAGS_FONT_MIDDLING), MF(MOVEHISTORY_FONT_ALL) },
\r
271 { MF(CLOCK_FONT_AVERAGE), MF(MESSAGE_FONT_AVERAGE), MF(COORD_FONT_AVERAGE), MF(CONSOLE_FONT_AVERAGE), MF(COMMENT_FONT_AVERAGE), MF(EDITTAGS_FONT_AVERAGE), MF(MOVEHISTORY_FONT_ALL) },
\r
272 { MF(CLOCK_FONT_MODERATE), MF(MESSAGE_FONT_MODERATE), MF(COORD_FONT_MODERATE), MF(CONSOLE_FONT_MODERATE), MF(COMMENT_FONT_MODERATE), MF(EDITTAGS_FONT_MODERATE), MF(MOVEHISTORY_FONT_ALL) },
\r
273 { MF(CLOCK_FONT_MEDIUM), MF(MESSAGE_FONT_MEDIUM), MF(COORD_FONT_MEDIUM), MF(CONSOLE_FONT_MEDIUM), MF(COMMENT_FONT_MEDIUM), MF(EDITTAGS_FONT_MEDIUM), MF(MOVEHISTORY_FONT_ALL) },
\r
274 { MF(CLOCK_FONT_BULKY), MF(MESSAGE_FONT_BULKY), MF(COORD_FONT_BULKY), MF(CONSOLE_FONT_BULKY), MF(COMMENT_FONT_BULKY), MF(EDITTAGS_FONT_BULKY), MF(MOVEHISTORY_FONT_ALL) },
\r
275 { MF(CLOCK_FONT_LARGE), MF(MESSAGE_FONT_LARGE), MF(COORD_FONT_LARGE), MF(CONSOLE_FONT_LARGE), MF(COMMENT_FONT_LARGE), MF(EDITTAGS_FONT_LARGE), MF(MOVEHISTORY_FONT_ALL) },
\r
276 { MF(CLOCK_FONT_BIG), MF(MESSAGE_FONT_BIG), MF(COORD_FONT_BIG), MF(CONSOLE_FONT_BIG), MF(COMMENT_FONT_BIG), MF(EDITTAGS_FONT_BIG), MF(MOVEHISTORY_FONT_ALL) },
\r
277 { MF(CLOCK_FONT_HUGE), MF(MESSAGE_FONT_HUGE), MF(COORD_FONT_HUGE), MF(CONSOLE_FONT_HUGE), MF(COMMENT_FONT_HUGE), MF(EDITTAGS_FONT_HUGE), MF(MOVEHISTORY_FONT_ALL) },
\r
278 { MF(CLOCK_FONT_GIANT), MF(MESSAGE_FONT_GIANT), MF(COORD_FONT_GIANT), MF(CONSOLE_FONT_GIANT), MF(COMMENT_FONT_GIANT), MF(EDITTAGS_FONT_GIANT), MF(MOVEHISTORY_FONT_ALL) },
\r
279 { MF(CLOCK_FONT_COLOSSAL), MF(MESSAGE_FONT_COLOSSAL), MF(COORD_FONT_COLOSSAL), MF(CONSOLE_FONT_COLOSSAL), MF(COMMENT_FONT_COLOSSAL), MF(EDITTAGS_FONT_COLOSSAL), MF(MOVEHISTORY_FONT_ALL) },
\r
280 { MF(CLOCK_FONT_TITANIC), MF(MESSAGE_FONT_TITANIC), MF(COORD_FONT_TITANIC), MF(CONSOLE_FONT_TITANIC), MF(COMMENT_FONT_TITANIC), MF(EDITTAGS_FONT_TITANIC), MF(MOVEHISTORY_FONT_ALL) },
\r
283 MyFont *font[NUM_SIZES][NUM_FONTS];
\r
292 #define BUTTON_WIDTH (tinyLayout ? 16 : 32)
\r
293 #define N_BUTTONS 5
\r
295 MyButtonDesc buttonDesc[N_BUTTONS] =
\r
297 {"<<", IDM_ToStart, NULL, NULL},
\r
298 {"<", IDM_Backward, NULL, NULL},
\r
299 {"P", IDM_Pause, NULL, NULL},
\r
300 {">", IDM_Forward, NULL, NULL},
\r
301 {">>", IDM_ToEnd, NULL, NULL},
\r
304 int tinyLayout = 0, smallLayout = 0;
\r
305 #define MENU_BAR_ITEMS 7
\r
306 char *menuBarText[2][MENU_BAR_ITEMS+1] = {
\r
307 { "&File", "&Mode", "&Action", "&Step", "&Options", "&Help", NULL },
\r
308 { "&F", "&M", "&A", "&S", "&O", "&H", NULL },
\r
312 MySound sounds[(int)NSoundClasses];
\r
313 MyTextAttribs textAttribs[(int)NColorClasses];
\r
315 MyColorizeAttribs colorizeAttribs[] = {
\r
316 { (COLORREF)0, 0, "Shout Text" },
\r
317 { (COLORREF)0, 0, "SShout/CShout" },
\r
318 { (COLORREF)0, 0, "Channel 1 Text" },
\r
319 { (COLORREF)0, 0, "Channel Text" },
\r
320 { (COLORREF)0, 0, "Kibitz Text" },
\r
321 { (COLORREF)0, 0, "Tell Text" },
\r
322 { (COLORREF)0, 0, "Challenge Text" },
\r
323 { (COLORREF)0, 0, "Request Text" },
\r
324 { (COLORREF)0, 0, "Seek Text" },
\r
325 { (COLORREF)0, 0, "Normal Text" },
\r
326 { (COLORREF)0, 0, "None" }
\r
331 static char *commentTitle;
\r
332 static char *commentText;
\r
333 static int commentIndex;
\r
334 static Boolean editComment = FALSE;
\r
337 char errorTitle[MSG_SIZ];
\r
338 char errorMessage[2*MSG_SIZ];
\r
339 HWND errorDialog = NULL;
\r
340 BOOLEAN moveErrorMessageUp = FALSE;
\r
341 BOOLEAN consoleEcho = TRUE;
\r
342 CHARFORMAT consoleCF;
\r
343 COLORREF consoleBackgroundColor;
\r
345 char *programVersion;
\r
351 typedef int CPKind;
\r
360 SOCKET sock2; /* stderr socket for OpenRcmd */
\r
363 #define INPUT_SOURCE_BUF_SIZE 4096
\r
365 typedef struct _InputSource {
\r
372 char buf[INPUT_SOURCE_BUF_SIZE];
\r
376 InputCallback func;
\r
377 struct _InputSource *second; /* for stderr thread on CPRcmd */
\r
381 InputSource *consoleInputSource;
\r
386 VOID ConsoleOutput(char* data, int length, int forceVisible);
\r
387 VOID ConsoleCreate();
\r
389 ConsoleWndProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam);
\r
390 VOID ColorizeTextPopup(HWND hwnd, ColorClass cc);
\r
391 VOID PrintCommSettings(FILE *f, char *name, DCB *dcb);
\r
392 VOID ParseCommSettings(char *arg, DCB *dcb);
\r
394 StartupDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam);
\r
395 VOID APIENTRY MenuPopup(HWND hwnd, POINT pt, HMENU hmenu, UINT def);
\r
396 void ParseIcsTextMenu(char *icsTextMenuString);
\r
397 VOID PopUpMoveDialog(char firstchar);
\r
398 VOID PopUpNameDialog(char firstchar);
\r
399 VOID UpdateSampleText(HWND hDlg, int id, MyColorizeAttribs *mca);
\r
403 int GameListOptions();
\r
405 int dummy; // [HGM] for obsolete args
\r
407 HWND hwndMain = NULL; /* root window*/
\r
408 HWND hwndConsole = NULL;
\r
409 HWND commentDialog = NULL;
\r
410 HWND moveHistoryDialog = NULL;
\r
411 HWND evalGraphDialog = NULL;
\r
412 HWND engineOutputDialog = NULL;
\r
413 HWND gameListDialog = NULL;
\r
414 HWND editTagsDialog = NULL;
\r
416 int commentUp = FALSE;
\r
418 WindowPlacement wpMain;
\r
419 WindowPlacement wpConsole;
\r
420 WindowPlacement wpComment;
\r
421 WindowPlacement wpMoveHistory;
\r
422 WindowPlacement wpEvalGraph;
\r
423 WindowPlacement wpEngineOutput;
\r
424 WindowPlacement wpGameList;
\r
425 WindowPlacement wpTags;
\r
427 VOID EngineOptionsPopup(); // [HGM] settings
\r
429 VOID GothicPopUp(char *title, VariantClass variant);
\r
431 * Setting "frozen" should disable all user input other than deleting
\r
432 * the window. We do this while engines are initializing themselves.
\r
434 static int frozen = 0;
\r
435 static int oldMenuItemState[MENU_BAR_ITEMS];
\r
441 if (frozen) return;
\r
443 hmenu = GetMenu(hwndMain);
\r
444 for (i=0; i<MENU_BAR_ITEMS; i++) {
\r
445 oldMenuItemState[i] = EnableMenuItem(hmenu, i, MF_BYPOSITION|MF_GRAYED);
\r
447 DrawMenuBar(hwndMain);
\r
450 /* Undo a FreezeUI */
\r
456 if (!frozen) return;
\r
458 hmenu = GetMenu(hwndMain);
\r
459 for (i=0; i<MENU_BAR_ITEMS; i++) {
\r
460 EnableMenuItem(hmenu, i, MF_BYPOSITION|oldMenuItemState[i]);
\r
462 DrawMenuBar(hwndMain);
\r
465 /*static*/ int fromX = -1, fromY = -1, toX, toY; // [HGM] moved upstream, so JAWS can use them
\r
467 /* JAWS preparation patch (WinBoard for the sight impaired). Define required insertions as empty */
\r
473 #define JAWS_ALT_INTERCEPT
\r
474 #define JAWS_KB_NAVIGATION
\r
475 #define JAWS_MENU_ITEMS
\r
476 #define JAWS_SILENCE
\r
477 #define JAWS_REPLAY
\r
479 #define JAWS_COPYRIGHT
\r
480 #define JAWS_DELETE(X) X
\r
481 #define SAYMACHINEMOVE()
\r
485 /*---------------------------------------------------------------------------*\
\r
489 \*---------------------------------------------------------------------------*/
\r
492 WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
\r
493 LPSTR lpCmdLine, int nCmdShow)
\r
496 HANDLE hAccelMain, hAccelNoAlt, hAccelNoICS;
\r
497 // INITCOMMONCONTROLSEX ex;
\r
501 LoadLibrary("RICHED32.DLL");
\r
502 consoleCF.cbSize = sizeof(CHARFORMAT);
\r
504 if (!InitApplication(hInstance)) {
\r
507 if (!InitInstance(hInstance, nCmdShow, lpCmdLine)) {
\r
513 // InitCommonControlsEx(&ex);
\r
514 InitCommonControls();
\r
516 hAccelMain = LoadAccelerators (hInstance, szAppName);
\r
517 hAccelNoAlt = LoadAccelerators (hInstance, "NO_ALT");
\r
518 hAccelNoICS = LoadAccelerators( hInstance, "NO_ICS"); /* [AS] No Ctrl-V on ICS!!! */
\r
520 /* Acquire and dispatch messages until a WM_QUIT message is received. */
\r
522 while (GetMessage(&msg, /* message structure */
\r
523 NULL, /* handle of window receiving the message */
\r
524 0, /* lowest message to examine */
\r
525 0)) /* highest message to examine */
\r
528 if(msg.message == WM_CHAR && msg.wParam == '\t') {
\r
529 // [HGM] navigate: switch between all windows with tab
\r
530 HWND e1 = NULL, e2 = NULL, mh = NULL, hInput = NULL, hText = NULL;
\r
531 int i, currentElement = 0;
\r
533 // first determine what element of the chain we come from (if any)
\r
534 if(appData.icsActive) {
\r
535 hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
536 hText = GetDlgItem(hwndConsole, OPT_ConsoleText);
\r
538 if(engineOutputDialog && EngineOutputIsUp()) {
\r
539 e1 = GetDlgItem(engineOutputDialog, IDC_EngineMemo1);
\r
540 e2 = GetDlgItem(engineOutputDialog, IDC_EngineMemo2);
\r
542 if(moveHistoryDialog && MoveHistoryIsUp()) {
\r
543 mh = GetDlgItem(moveHistoryDialog, IDC_MoveHistory);
\r
545 if(msg.hwnd == hwndMain) currentElement = 7 ; else
\r
546 if(msg.hwnd == engineOutputDialog) currentElement = 2; else
\r
547 if(msg.hwnd == e1) currentElement = 2; else
\r
548 if(msg.hwnd == e2) currentElement = 3; else
\r
549 if(msg.hwnd == moveHistoryDialog) currentElement = 4; else
\r
550 if(msg.hwnd == mh) currentElement = 4; else
\r
551 if(msg.hwnd == evalGraphDialog) currentElement = 6; else
\r
552 if(msg.hwnd == hText) currentElement = 5; else
\r
553 if(msg.hwnd == hInput) currentElement = 6; else
\r
554 for (i = 0; i < N_BUTTONS; i++) {
\r
555 if (buttonDesc[i].hwnd == msg.hwnd) { currentElement = 1; break; }
\r
558 // determine where to go to
\r
559 if(currentElement) { HWND h = NULL; int direction = GetKeyState(VK_SHIFT) < 0 ? -1 : 1;
\r
561 currentElement = (currentElement + direction) % 7;
\r
562 switch(currentElement) {
\r
564 h = hwndMain; break; // passing this case always makes the loop exit
\r
566 h = buttonDesc[0].hwnd; break; // could be NULL
\r
568 if(!EngineOutputIsUp()) continue; // skip closed auxiliary windows
\r
571 if(!EngineOutputIsUp()) continue;
\r
574 if(!MoveHistoryIsUp()) continue;
\r
576 // case 6: // input to eval graph does not seem to get here!
\r
577 // if(!EvalGraphIsUp()) continue;
\r
578 // h = evalGraphDialog; break;
\r
580 if(!appData.icsActive) continue;
\r
584 if(!appData.icsActive) continue;
\r
590 if(currentElement > 4 && IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);
\r
591 if(currentElement < 5 && IsIconic(hwndMain)) ShowWindow(hwndMain, SW_RESTORE); // all open together
\r
594 continue; // this message now has been processed
\r
598 if (!(commentDialog && IsDialogMessage(commentDialog, &msg)) &&
\r
599 !(moveHistoryDialog && IsDialogMessage(moveHistoryDialog, &msg)) &&
\r
600 !(evalGraphDialog && IsDialogMessage(evalGraphDialog, &msg)) &&
\r
601 !(engineOutputDialog && IsDialogMessage(engineOutputDialog, &msg)) &&
\r
602 !(editTagsDialog && IsDialogMessage(editTagsDialog, &msg)) &&
\r
603 !(gameListDialog && IsDialogMessage(gameListDialog, &msg)) &&
\r
604 !(errorDialog && IsDialogMessage(errorDialog, &msg)) &&
\r
605 !(!frozen && TranslateAccelerator(hwndMain, hAccelMain, &msg)) && JAWS_ACCEL
\r
606 !(!hwndConsole && TranslateAccelerator(hwndMain, hAccelNoICS, &msg)) &&
\r
607 !(!hwndConsole && TranslateAccelerator(hwndMain, hAccelNoAlt, &msg))) {
\r
608 int done = 0, i; // [HGM] chat: dispatch cat-box messages
\r
609 for(i=0; i<MAX_CHAT; i++)
\r
610 if(chatHandle[i] && IsDialogMessage(chatHandle[i], &msg)) {
\r
613 if(done) continue; // [HGM] chat: end patch
\r
614 TranslateMessage(&msg); /* Translates virtual key codes */
\r
615 DispatchMessage(&msg); /* Dispatches message to window */
\r
620 return (msg.wParam); /* Returns the value from PostQuitMessage */
\r
623 /*---------------------------------------------------------------------------*\
\r
625 * Initialization functions
\r
627 \*---------------------------------------------------------------------------*/
\r
631 { // update user logo if necessary
\r
632 static char oldUserName[MSG_SIZ], *curName;
\r
634 if(appData.autoLogo) {
\r
635 curName = UserName();
\r
636 if(strcmp(curName, oldUserName)) {
\r
637 sprintf(oldUserName, "logos\\%s.bmp", curName);
\r
638 userLogo = LoadImage( 0, oldUserName, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );
\r
639 strcpy(oldUserName, curName);
\r
645 InitApplication(HINSTANCE hInstance)
\r
649 /* Fill in window class structure with parameters that describe the */
\r
652 wc.style = CS_HREDRAW | CS_VREDRAW; /* Class style(s). */
\r
653 wc.lpfnWndProc = (WNDPROC)WndProc; /* Window Procedure */
\r
654 wc.cbClsExtra = 0; /* No per-class extra data. */
\r
655 wc.cbWndExtra = 0; /* No per-window extra data. */
\r
656 wc.hInstance = hInstance; /* Owner of this class */
\r
657 wc.hIcon = LoadIcon(hInstance, "icon_white");
\r
658 wc.hCursor = LoadCursor(NULL, IDC_ARROW); /* Cursor */
\r
659 wc.hbrBackground = (HBRUSH)(COLOR_WINDOW+1); /* Default color */
\r
660 wc.lpszMenuName = szAppName; /* Menu name from .RC */
\r
661 wc.lpszClassName = szAppName; /* Name to register as */
\r
663 /* Register the window class and return success/failure code. */
\r
664 if (!RegisterClass(&wc)) return FALSE;
\r
666 wc.style = CS_HREDRAW | CS_VREDRAW;
\r
667 wc.lpfnWndProc = (WNDPROC)ConsoleWndProc;
\r
669 wc.cbWndExtra = DLGWINDOWEXTRA;
\r
670 wc.hInstance = hInstance;
\r
671 wc.hIcon = LoadIcon(hInstance, "icon_white");
\r
672 wc.hCursor = LoadCursor(NULL, IDC_ARROW);
\r
673 wc.hbrBackground = (HBRUSH)(COLOR_MENU+1);
\r
674 wc.lpszMenuName = NULL;
\r
675 wc.lpszClassName = szConsoleName;
\r
677 if (!RegisterClass(&wc)) return FALSE;
\r
682 /* Set by InitInstance, used by EnsureOnScreen */
\r
683 int screenHeight, screenWidth;
\r
686 EnsureOnScreen(int *x, int *y, int minX, int minY)
\r
688 // int gap = GetSystemMetrics(SM_CYFRAME) + GetSystemMetrics(SM_CYCAPTION);
\r
689 /* Be sure window at (x,y) is not off screen (or even mostly off screen) */
\r
690 if (*x > screenWidth - 32) *x = 0;
\r
691 if (*y > screenHeight - 32) *y = 0;
\r
692 if (*x < minX) *x = minX;
\r
693 if (*y < minY) *y = minY;
\r
697 InitInstance(HINSTANCE hInstance, int nCmdShow, LPSTR lpCmdLine)
\r
699 HWND hwnd; /* Main window handle. */
\r
701 WINDOWPLACEMENT wp;
\r
704 hInst = hInstance; /* Store instance handle in our global variable */
\r
705 programName = szAppName;
\r
707 if (SearchPath(NULL, "WinBoard.exe", NULL, MSG_SIZ, installDir, &filepart)) {
\r
708 *filepart = NULLCHAR;
\r
710 GetCurrentDirectory(MSG_SIZ, installDir);
\r
712 gameInfo.boardWidth = gameInfo.boardHeight = 8; // [HGM] won't have open window otherwise
\r
713 screenWidth = screenHeight = 1000; // [HGM] placement: kludge to allow calling EnsureOnScreen from InitAppData
\r
714 InitAppData(lpCmdLine); /* Get run-time parameters */
\r
715 /* xboard, and older WinBoards, controlled the move sound with the
\r
716 appData.ringBellAfterMoves option. In the current WinBoard, we
\r
717 always turn the option on (so that the backend will call us),
\r
718 then let the user turn the sound off by setting it to silence if
\r
719 desired. To accommodate old winboard.ini files saved by old
\r
720 versions of WinBoard, we also turn off the sound if the option
\r
721 was initially set to false. [HGM] taken out of InitAppData */
\r
722 if (!appData.ringBellAfterMoves) {
\r
723 sounds[(int)SoundMove].name = strdup("");
\r
724 appData.ringBellAfterMoves = TRUE;
\r
726 if (appData.debugMode) {
\r
727 debugFP = fopen(appData.nameOfDebugFile, "w");
\r
728 setbuf(debugFP, NULL);
\r
733 // InitEngineUCI( installDir, &first ); // [HGM] incorporated in InitBackEnd1()
\r
734 // InitEngineUCI( installDir, &second );
\r
736 /* Create a main window for this application instance. */
\r
737 hwnd = CreateWindow(szAppName, szTitle,
\r
738 (WS_OVERLAPPEDWINDOW & ~WS_MAXIMIZEBOX),
\r
739 CW_USEDEFAULT, 0, CW_USEDEFAULT, 0,
\r
740 NULL, NULL, hInstance, NULL);
\r
743 /* If window could not be created, return "failure" */
\r
748 /* [HGM] logo: Load logos if specified (must be done before InitDrawingSizes) */
\r
749 if( appData.firstLogo && appData.firstLogo[0] != NULLCHAR) {
\r
750 first.programLogo = LoadImage( 0, appData.firstLogo, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );
\r
752 if (first.programLogo == NULL && appData.debugMode) {
\r
753 fprintf( debugFP, "Unable to load logo bitmap '%s'\n", appData.firstLogo );
\r
755 } else if(appData.autoLogo) {
\r
756 if(appData.firstDirectory && appData.firstDirectory[0]) {
\r
758 sprintf(buf, "%s/logo.bmp", appData.firstDirectory);
\r
759 first.programLogo = LoadImage( 0, buf, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );
\r
763 if( appData.secondLogo && appData.secondLogo[0] != NULLCHAR) {
\r
764 second.programLogo = LoadImage( 0, appData.secondLogo, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );
\r
766 if (second.programLogo == NULL && appData.debugMode) {
\r
767 fprintf( debugFP, "Unable to load logo bitmap '%s'\n", appData.secondLogo );
\r
769 } else if(appData.autoLogo) {
\r
771 if(appData.icsActive) { // [HGM] logo: in ICS mode second can be used for ICS
\r
772 sprintf(buf, "logos\\%s.bmp", appData.icsHost);
\r
773 second.programLogo = LoadImage( 0, buf, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );
\r
775 if(appData.secondDirectory && appData.secondDirectory[0]) {
\r
776 sprintf(buf, "%s\\logo.bmp", appData.secondDirectory);
\r
777 second.programLogo = LoadImage( 0, buf, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );
\r
783 iconWhite = LoadIcon(hInstance, "icon_white");
\r
784 iconBlack = LoadIcon(hInstance, "icon_black");
\r
785 iconCurrent = iconWhite;
\r
786 InitDrawingColors();
\r
787 screenHeight = GetSystemMetrics(SM_CYSCREEN);
\r
788 screenWidth = GetSystemMetrics(SM_CXSCREEN);
\r
789 for (ibs = (int) NUM_SIZES - 1; ibs >= 0; ibs--) {
\r
790 /* Compute window size for each board size, and use the largest
\r
791 size that fits on this screen as the default. */
\r
792 InitDrawingSizes((BoardSize)(ibs+1000), 0);
\r
793 if (boardSize == (BoardSize)-1 &&
\r
794 winH <= screenHeight
\r
795 - GetSystemMetrics(SM_CYFRAME) - GetSystemMetrics(SM_CYCAPTION) - 10
\r
796 && winW <= screenWidth) {
\r
797 boardSize = (BoardSize)ibs;
\r
801 InitDrawingSizes(boardSize, 0);
\r
803 buttonCount = GetSystemMetrics(SM_CMOUSEBUTTONS);
\r
805 /* [AS] Load textures if specified */
\r
806 ZeroMemory( &backTextureSquareInfo, sizeof(backTextureSquareInfo) );
\r
808 if( appData.liteBackTextureFile && appData.liteBackTextureFile[0] != NULLCHAR && appData.liteBackTextureFile[0] != '*' ) {
\r
809 liteBackTexture = LoadImage( 0, appData.liteBackTextureFile, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );
\r
810 liteBackTextureMode = appData.liteBackTextureMode;
\r
812 if (liteBackTexture == NULL && appData.debugMode) {
\r
813 fprintf( debugFP, "Unable to load lite texture bitmap '%s'\n", appData.liteBackTextureFile );
\r
817 if( appData.darkBackTextureFile && appData.darkBackTextureFile[0] != NULLCHAR && appData.darkBackTextureFile[0] != '*' ) {
\r
818 darkBackTexture = LoadImage( 0, appData.darkBackTextureFile, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );
\r
819 darkBackTextureMode = appData.darkBackTextureMode;
\r
821 if (darkBackTexture == NULL && appData.debugMode) {
\r
822 fprintf( debugFP, "Unable to load dark texture bitmap '%s'\n", appData.darkBackTextureFile );
\r
826 mysrandom( (unsigned) time(NULL) );
\r
828 /* [AS] Restore layout */
\r
829 if( wpMoveHistory.visible ) {
\r
830 MoveHistoryPopUp();
\r
833 if( wpEvalGraph.visible ) {
\r
837 if( wpEngineOutput.visible ) {
\r
838 EngineOutputPopUp();
\r
841 /* Make the window visible; update its client area; and return "success" */
\r
842 EnsureOnScreen(&wpMain.x, &wpMain.y, minX, minY);
\r
843 wp.length = sizeof(WINDOWPLACEMENT);
\r
845 wp.showCmd = nCmdShow;
\r
846 wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;
\r
847 wp.rcNormalPosition.left = wpMain.x;
\r
848 wp.rcNormalPosition.right = wpMain.x + wpMain.width;
\r
849 wp.rcNormalPosition.top = wpMain.y;
\r
850 wp.rcNormalPosition.bottom = wpMain.y + wpMain.height;
\r
851 SetWindowPlacement(hwndMain, &wp);
\r
853 InitBackEnd2(); // [HGM] moved until after all windows placed, to save correct position if fatal error on engine start
\r
855 if(!appData.noGUI) SetWindowPos(hwndMain, alwaysOnTop ? HWND_TOPMOST : HWND_NOTOPMOST,
\r
856 0, 0, 0, 0, SWP_NOMOVE|SWP_NOSIZE);
\r
860 SetWindowPos(hwndConsole, alwaysOnTop ? HWND_TOPMOST : HWND_NOTOPMOST,
\r
861 0, 0, 0, 0, SWP_NOMOVE|SWP_NOSIZE);
\r
863 ShowWindow(hwndConsole, nCmdShow);
\r
864 if(appData.chatBoxes) { // [HGM] chat: open chat boxes
\r
865 char buf[MSG_SIZ], *p = buf, *q;
\r
866 strcpy(buf, appData.chatBoxes);
\r
868 q = strchr(p, ';');
\r
870 if(*p) ChatPopUp(p);
\r
873 SetActiveWindow(hwndConsole);
\r
875 if(!appData.noGUI) UpdateWindow(hwnd); else ShowWindow(hwnd, SW_MINIMIZE);
\r
876 if(gameListDialog) SetFocus(gameListDialog); // [HGM] jaws: for if we clicked multi-game game file
\r
885 HMENU hmenu = GetMenu(hwndMain);
\r
887 (void) EnableMenuItem(hmenu, IDM_CommPort,
\r
888 MF_BYCOMMAND|((appData.icsActive &&
\r
889 *appData.icsCommPort != NULLCHAR) ?
\r
890 MF_ENABLED : MF_GRAYED));
\r
891 (void) CheckMenuItem(hmenu, IDM_SaveSettingsOnExit,
\r
892 MF_BYCOMMAND|(saveSettingsOnExit ?
\r
893 MF_CHECKED : MF_UNCHECKED));
\r
896 //---------------------------------------------------------------------------------------------------------
\r
898 #define ICS_TEXT_MENU_SIZE (IDM_CommandXLast - IDM_CommandX + 1)
\r
899 #define XBOARD FALSE
\r
901 #define OPTCHAR "/"
\r
902 #define SEPCHAR "="
\r
906 // front-end part of option handling
\r
909 LFfromMFP(LOGFONT* lf, MyFontParams *mfp)
\r
911 HDC hdc = CreateDC("DISPLAY", NULL, NULL, NULL);
\r
912 lf->lfHeight = -(int)(mfp->pointSize * GetDeviceCaps(hdc, LOGPIXELSY) / 72.0 + 0.5);
\r
915 lf->lfEscapement = 0;
\r
916 lf->lfOrientation = 0;
\r
917 lf->lfWeight = mfp->bold ? FW_BOLD : FW_NORMAL;
\r
918 lf->lfItalic = mfp->italic;
\r
919 lf->lfUnderline = mfp->underline;
\r
920 lf->lfStrikeOut = mfp->strikeout;
\r
921 lf->lfCharSet = mfp->charset;
\r
922 lf->lfOutPrecision = OUT_DEFAULT_PRECIS;
\r
923 lf->lfClipPrecision = CLIP_DEFAULT_PRECIS;
\r
924 lf->lfQuality = DEFAULT_QUALITY;
\r
925 lf->lfPitchAndFamily = DEFAULT_PITCH|FF_DONTCARE;
\r
926 strcpy(lf->lfFaceName, mfp->faceName);
\r
930 CreateFontInMF(MyFont *mf)
\r
932 LFfromMFP(&mf->lf, &mf->mfp);
\r
933 if (mf->hf) DeleteObject(mf->hf);
\r
934 mf->hf = CreateFontIndirect(&mf->lf);
\r
937 // [HGM] This platform-dependent table provides the location for storing the color info
\r
939 colorVariable[] = {
\r
944 &highlightSquareColor,
\r
945 &premoveHighlightColor,
\r
947 &consoleBackgroundColor,
\r
948 &appData.fontForeColorWhite,
\r
949 &appData.fontBackColorWhite,
\r
950 &appData.fontForeColorBlack,
\r
951 &appData.fontBackColorBlack,
\r
952 &appData.evalHistColorWhite,
\r
953 &appData.evalHistColorBlack,
\r
954 &appData.highlightArrowColor,
\r
957 /* Command line font name parser. NULL name means do nothing.
\r
958 Syntax like "Courier New:10.0 bi" or "Arial:10" or "Arial:10b"
\r
959 For backward compatibility, syntax without the colon is also
\r
960 accepted, but font names with digits in them won't work in that case.
\r
963 ParseFontName(char *name, MyFontParams *mfp)
\r
966 if (name == NULL) return;
\r
968 q = strchr(p, ':');
\r
970 if (q - p >= sizeof(mfp->faceName))
\r
971 ExitArgError("Font name too long:", name);
\r
972 memcpy(mfp->faceName, p, q - p);
\r
973 mfp->faceName[q - p] = NULLCHAR;
\r
977 while (*p && !isdigit(*p)) {
\r
979 if (q - mfp->faceName >= sizeof(mfp->faceName))
\r
980 ExitArgError("Font name too long:", name);
\r
982 while (q > mfp->faceName && q[-1] == ' ') q--;
\r
985 if (!*p) ExitArgError("Font point size missing:", name);
\r
986 mfp->pointSize = (float) atof(p);
\r
987 mfp->bold = (strchr(p, 'b') != NULL);
\r
988 mfp->italic = (strchr(p, 'i') != NULL);
\r
989 mfp->underline = (strchr(p, 'u') != NULL);
\r
990 mfp->strikeout = (strchr(p, 's') != NULL);
\r
991 mfp->charset = DEFAULT_CHARSET;
\r
992 q = strchr(p, 'c');
\r
994 mfp->charset = (BYTE) atoi(q+1);
\r
998 ParseFont(char *name, int number)
\r
999 { // wrapper to shield back-end from 'font'
\r
1000 ParseFontName(name, &font[boardSize][number]->mfp);
\r
1005 { // in WB we have a 2D array of fonts; this initializes their description
\r
1007 /* Point font array elements to structures and
\r
1008 parse default font names */
\r
1009 for (i=0; i<NUM_FONTS; i++) {
\r
1010 for (j=0; j<NUM_SIZES; j++) {
\r
1011 font[j][i] = &fontRec[j][i];
\r
1012 ParseFontName(font[j][i]->def, &font[j][i]->mfp);
\r
1019 { // here we create the actual fonts from the selected descriptions
\r
1021 for (i=0; i<NUM_FONTS; i++) {
\r
1022 for (j=0; j<NUM_SIZES; j++) {
\r
1023 CreateFontInMF(font[j][i]);
\r
1027 /* Color name parser.
\r
1028 X version accepts X color names, but this one
\r
1029 handles only the #rrggbb form (hex) or rrr,ggg,bbb (decimal) */
\r
1031 ParseColorName(char *name)
\r
1033 int red, green, blue, count;
\r
1034 char buf[MSG_SIZ];
\r
1036 count = sscanf(name, "#%2x%2x%2x", &red, &green, &blue);
\r
1038 count = sscanf(name, "%3d%*[^0-9]%3d%*[^0-9]%3d",
\r
1039 &red, &green, &blue);
\r
1042 sprintf(buf, "Can't parse color name %s", name);
\r
1043 DisplayError(buf, 0);
\r
1044 return RGB(0, 0, 0);
\r
1046 return PALETTERGB(red, green, blue);
\r
1050 ParseColor(int n, char *name)
\r
1051 { // for WinBoard the color is an int, which needs to be derived from the string
\r
1052 if(colorVariable[n]) *(int*)colorVariable[n] = ParseColorName(name);
\r
1056 ParseAttribs(COLORREF *color, int *effects, char* argValue)
\r
1058 char *e = argValue;
\r
1062 if (*e == 'b') eff |= CFE_BOLD;
\r
1063 else if (*e == 'i') eff |= CFE_ITALIC;
\r
1064 else if (*e == 'u') eff |= CFE_UNDERLINE;
\r
1065 else if (*e == 's') eff |= CFE_STRIKEOUT;
\r
1066 else if (*e == '#' || isdigit(*e)) break;
\r
1070 *color = ParseColorName(e);
\r
1074 ParseTextAttribs(ColorClass cc, char *s)
\r
1075 { // [HGM] front-end wrapper that does the platform-dependent call
\r
1076 // for XBoard we would set (&appData.colorShout)[cc] = strdup(s);
\r
1077 ParseAttribs(&textAttribs[cc].color, &textAttribs[cc].effects, s);
\r
1081 ParseBoardSize(void *addr, char *name)
\r
1082 { // [HGM] rewritten with return-value ptr to shield back-end from BoardSize
\r
1083 BoardSize bs = SizeTiny;
\r
1084 while (sizeInfo[bs].name != NULL) {
\r
1085 if (StrCaseCmp(name, sizeInfo[bs].name) == 0) {
\r
1086 *(BoardSize *)addr = bs;
\r
1091 ExitArgError("Unrecognized board size value", name);
\r
1096 { // [HGM] import name from appData first
\r
1099 for (cc = (ColorClass)0; cc < ColorNormal; cc++) {
\r
1100 textAttribs[cc].sound.name = strdup((&appData.soundShout)[cc]);
\r
1101 textAttribs[cc].sound.data = NULL;
\r
1102 MyLoadSound(&textAttribs[cc].sound);
\r
1104 for (cc = ColorNormal; cc < NColorClasses; cc++) {
\r
1105 textAttribs[cc].sound.name = strdup("");
\r
1106 textAttribs[cc].sound.data = NULL;
\r
1108 for (sc = (SoundClass)0; sc < NSoundClasses; sc++) {
\r
1109 sounds[sc].name = strdup((&appData.soundMove)[sc]);
\r
1110 sounds[sc].data = NULL;
\r
1111 MyLoadSound(&sounds[sc]);
\r
1116 SetCommPortDefaults()
\r
1118 memset(&dcb, 0, sizeof(DCB)); // required by VS 2002 +
\r
1119 dcb.DCBlength = sizeof(DCB);
\r
1120 dcb.BaudRate = 9600;
\r
1121 dcb.fBinary = TRUE;
\r
1122 dcb.fParity = FALSE;
\r
1123 dcb.fOutxCtsFlow = FALSE;
\r
1124 dcb.fOutxDsrFlow = FALSE;
\r
1125 dcb.fDtrControl = DTR_CONTROL_ENABLE;
\r
1126 dcb.fDsrSensitivity = FALSE;
\r
1127 dcb.fTXContinueOnXoff = TRUE;
\r
1128 dcb.fOutX = FALSE;
\r
1130 dcb.fNull = FALSE;
\r
1131 dcb.fRtsControl = RTS_CONTROL_ENABLE;
\r
1132 dcb.fAbortOnError = FALSE;
\r
1134 dcb.Parity = SPACEPARITY;
\r
1135 dcb.StopBits = ONESTOPBIT;
\r
1138 // [HGM] args: these three cases taken out to stay in front-end
\r
1140 SaveFontArg(FILE *f, ArgDescriptor *ad)
\r
1141 { // in WinBoard every board size has its own font, and the "argLoc" identifies the table,
\r
1142 // while the curent board size determines the element. This system should be ported to XBoard.
\r
1143 // What the table contains pointers to, and how to print the font description, remains platform-dependent
\r
1145 for (bs=0; bs<NUM_SIZES; bs++) {
\r
1146 MyFontParams *mfp = &font[bs][(int) ad->argLoc]->mfp;
\r
1147 fprintf(f, "/size=%s ", sizeInfo[bs].name);
\r
1148 fprintf(f, "/%s=\"%s:%g%s%s%s%s%sc%d\"\n",
\r
1149 ad->argName, mfp->faceName, mfp->pointSize,
\r
1150 mfp->bold || mfp->italic || mfp->underline || mfp->strikeout ? " " : "",
\r
1151 mfp->bold ? "b" : "",
\r
1152 mfp->italic ? "i" : "",
\r
1153 mfp->underline ? "u" : "",
\r
1154 mfp->strikeout ? "s" : "",
\r
1155 (int)mfp->charset);
\r
1161 { // [HGM] copy the names from the internal WB variables to appData
\r
1164 for (cc = (ColorClass)0; cc < ColorNormal; cc++)
\r
1165 (&appData.soundShout)[cc] = textAttribs[cc].sound.name;
\r
1166 for (sc = (SoundClass)0; sc < NSoundClasses; sc++)
\r
1167 (&appData.soundMove)[sc] = sounds[sc].name;
\r
1171 SaveAttribsArg(FILE *f, ArgDescriptor *ad)
\r
1172 { // here the "argLoc" defines a table index. It could have contained the 'ta' pointer itself, though
\r
1173 MyTextAttribs* ta = &textAttribs[(ColorClass)ad->argLoc];
\r
1174 fprintf(f, "/%s=\"%s%s%s%s%s#%02lx%02lx%02lx\"\n", ad->argName,
\r
1175 (ta->effects & CFE_BOLD) ? "b" : "",
\r
1176 (ta->effects & CFE_ITALIC) ? "i" : "",
\r
1177 (ta->effects & CFE_UNDERLINE) ? "u" : "",
\r
1178 (ta->effects & CFE_STRIKEOUT) ? "s" : "",
\r
1179 (ta->effects) ? " " : "",
\r
1180 ta->color&0xff, (ta->color >> 8)&0xff, (ta->color >> 16)&0xff);
\r
1184 SaveColor(FILE *f, ArgDescriptor *ad)
\r
1185 { // in WinBoard the color is an int and has to be converted to text. In X it would be a string already?
\r
1186 COLORREF color = *(COLORREF *)colorVariable[(int)ad->argLoc];
\r
1187 fprintf(f, "/%s=#%02lx%02lx%02lx\n", ad->argName,
\r
1188 color&0xff, (color>>8)&0xff, (color>>16)&0xff);
\r
1192 SaveBoardSize(FILE *f, char *name, void *addr)
\r
1193 { // wrapper to shield back-end from BoardSize & sizeInfo
\r
1194 fprintf(f, "/%s=%s\n", name, sizeInfo[*(BoardSize *)addr].name);
\r
1198 ParseCommPortSettings(char *s)
\r
1199 { // wrapper to keep dcb from back-end
\r
1200 ParseCommSettings(s, &dcb);
\r
1205 { // wrapper to shield use of window handles from back-end (make addressible by number?)
\r
1206 GetActualPlacement(hwndMain, &wpMain);
\r
1207 GetActualPlacement(hwndConsole, &wpConsole);
\r
1208 GetActualPlacement(commentDialog, &wpComment);
\r
1209 GetActualPlacement(editTagsDialog, &wpTags);
\r
1210 GetActualPlacement(gameListDialog, &wpGameList);
\r
1211 GetActualPlacement(moveHistoryDialog, &wpMoveHistory);
\r
1212 GetActualPlacement(evalGraphDialog, &wpEvalGraph);
\r
1213 GetActualPlacement(engineOutputDialog, &wpEngineOutput);
\r
1217 PrintCommPortSettings(FILE *f, char *name)
\r
1218 { // wrapper to shield back-end from DCB
\r
1219 PrintCommSettings(f, name, &dcb);
\r
1223 MySearchPath(char *installDir, char *name, char *fullname)
\r
1225 char *dummy, buf[MSG_SIZ], *p = name, *q;
\r
1226 if(name[0]== '%') {
\r
1227 fullname[0] = 0; // [HGM] first expand any environment variables in the given name
\r
1228 while(*p == '%' && (q = strchr(p+1, '%'))) { // [HGM] recognize %*% as environment variable
\r
1230 *strchr(buf, '%') = 0;
\r
1231 strcat(fullname, getenv(buf));
\r
1232 p = q+1; while(*p == '\\') { strcat(fullname, "\\"); p++; }
\r
1234 strcat(fullname, p); // after environment variables (if any), take the remainder of the given name
\r
1235 if(appData.debugMode) fprintf(debugFP, "name = '%s', expanded name = '%s'\n", name, fullname);
\r
1236 return (int) strlen(fullname);
\r
1238 return (int) SearchPath(installDir, name, NULL, MSG_SIZ, fullname, &dummy);
\r
1242 MyGetFullPathName(char *name, char *fullname)
\r
1245 return (int) GetFullPathName(name, MSG_SIZ, fullname, &dummy);
\r
1250 { // [HGM] args: allows testing if main window is realized from back-end
\r
1251 return hwndMain != NULL;
\r
1255 PopUpStartupDialog()
\r
1259 lpProc = MakeProcInstance((FARPROC)StartupDialog, hInst);
\r
1260 DialogBox(hInst, MAKEINTRESOURCE(DLG_Startup), NULL, (DLGPROC)lpProc);
\r
1261 FreeProcInstance(lpProc);
\r
1264 /*---------------------------------------------------------------------------*\
\r
1266 * GDI board drawing routines
\r
1268 \*---------------------------------------------------------------------------*/
\r
1270 /* [AS] Draw square using background texture */
\r
1271 static void DrawTile( int dx, int dy, int dw, int dh, HDC dst, HDC src, int mode, int sx, int sy )
\r
1276 return; /* Should never happen! */
\r
1279 SetGraphicsMode( dst, GM_ADVANCED );
\r
1286 /* X reflection */
\r
1291 x.eDx = (FLOAT) dw + dx - 1;
\r
1294 SetWorldTransform( dst, &x );
\r
1297 /* Y reflection */
\r
1303 x.eDy = (FLOAT) dh + dy - 1;
\r
1305 SetWorldTransform( dst, &x );
\r
1313 x.eDx = (FLOAT) dx;
\r
1314 x.eDy = (FLOAT) dy;
\r
1317 SetWorldTransform( dst, &x );
\r
1321 BitBlt( dst, dx, dy, dw, dh, src, sx, sy, SRCCOPY );
\r
1329 SetWorldTransform( dst, &x );
\r
1331 ModifyWorldTransform( dst, 0, MWT_IDENTITY );
\r
1334 /* [AS] [HGM] Make room for more piece types, so all pieces can be different */
\r
1336 PM_WP = (int) WhitePawn,
\r
1337 PM_WN = (int) WhiteKnight,
\r
1338 PM_WB = (int) WhiteBishop,
\r
1339 PM_WR = (int) WhiteRook,
\r
1340 PM_WQ = (int) WhiteQueen,
\r
1341 PM_WF = (int) WhiteFerz,
\r
1342 PM_WW = (int) WhiteWazir,
\r
1343 PM_WE = (int) WhiteAlfil,
\r
1344 PM_WM = (int) WhiteMan,
\r
1345 PM_WO = (int) WhiteCannon,
\r
1346 PM_WU = (int) WhiteUnicorn,
\r
1347 PM_WH = (int) WhiteNightrider,
\r
1348 PM_WA = (int) WhiteAngel,
\r
1349 PM_WC = (int) WhiteMarshall,
\r
1350 PM_WAB = (int) WhiteCardinal,
\r
1351 PM_WD = (int) WhiteDragon,
\r
1352 PM_WL = (int) WhiteLance,
\r
1353 PM_WS = (int) WhiteCobra,
\r
1354 PM_WV = (int) WhiteFalcon,
\r
1355 PM_WSG = (int) WhiteSilver,
\r
1356 PM_WG = (int) WhiteGrasshopper,
\r
1357 PM_WK = (int) WhiteKing,
\r
1358 PM_BP = (int) BlackPawn,
\r
1359 PM_BN = (int) BlackKnight,
\r
1360 PM_BB = (int) BlackBishop,
\r
1361 PM_BR = (int) BlackRook,
\r
1362 PM_BQ = (int) BlackQueen,
\r
1363 PM_BF = (int) BlackFerz,
\r
1364 PM_BW = (int) BlackWazir,
\r
1365 PM_BE = (int) BlackAlfil,
\r
1366 PM_BM = (int) BlackMan,
\r
1367 PM_BO = (int) BlackCannon,
\r
1368 PM_BU = (int) BlackUnicorn,
\r
1369 PM_BH = (int) BlackNightrider,
\r
1370 PM_BA = (int) BlackAngel,
\r
1371 PM_BC = (int) BlackMarshall,
\r
1372 PM_BG = (int) BlackGrasshopper,
\r
1373 PM_BAB = (int) BlackCardinal,
\r
1374 PM_BD = (int) BlackDragon,
\r
1375 PM_BL = (int) BlackLance,
\r
1376 PM_BS = (int) BlackCobra,
\r
1377 PM_BV = (int) BlackFalcon,
\r
1378 PM_BSG = (int) BlackSilver,
\r
1379 PM_BK = (int) BlackKing
\r
1382 static HFONT hPieceFont = NULL;
\r
1383 static HBITMAP hPieceMask[(int) EmptySquare];
\r
1384 static HBITMAP hPieceFace[(int) EmptySquare];
\r
1385 static int fontBitmapSquareSize = 0;
\r
1386 static char pieceToFontChar[(int) EmptySquare] =
\r
1387 { 'p', 'n', 'b', 'r', 'q',
\r
1388 'n', 'b', 'p', 'n', 'b', 'r', 'b', 'r', 'q', 'k',
\r
1389 'k', 'o', 'm', 'v', 't', 'w',
\r
1390 'v', 't', 'o', 'm', 'v', 't', 'v', 't', 'w', 'l',
\r
1393 extern BOOL SetCharTable( char *table, const char * map );
\r
1394 /* [HGM] moved to backend.c */
\r
1396 static void SetPieceBackground( HDC hdc, COLORREF color, int mode )
\r
1399 BYTE r1 = GetRValue( color );
\r
1400 BYTE g1 = GetGValue( color );
\r
1401 BYTE b1 = GetBValue( color );
\r
1407 /* Create a uniform background first */
\r
1408 hbrush = CreateSolidBrush( color );
\r
1409 SetRect( &rc, 0, 0, squareSize, squareSize );
\r
1410 FillRect( hdc, &rc, hbrush );
\r
1411 DeleteObject( hbrush );
\r
1414 /* Vertical gradient, good for pawn, knight and rook, less for queen and king */
\r
1415 int steps = squareSize / 2;
\r
1418 for( i=0; i<steps; i++ ) {
\r
1419 BYTE r = r1 - (r1-r2) * i / steps;
\r
1420 BYTE g = g1 - (g1-g2) * i / steps;
\r
1421 BYTE b = b1 - (b1-b2) * i / steps;
\r
1423 hbrush = CreateSolidBrush( RGB(r,g,b) );
\r
1424 SetRect( &rc, i + squareSize - steps, 0, i + squareSize - steps + 1, squareSize );
\r
1425 FillRect( hdc, &rc, hbrush );
\r
1426 DeleteObject(hbrush);
\r
1429 else if( mode == 2 ) {
\r
1430 /* Diagonal gradient, good more or less for every piece */
\r
1431 POINT triangle[3];
\r
1432 HPEN hpen = SelectObject( hdc, GetStockObject(NULL_PEN) );
\r
1433 HBRUSH hbrush_old;
\r
1434 int steps = squareSize;
\r
1437 triangle[0].x = squareSize - steps;
\r
1438 triangle[0].y = squareSize;
\r
1439 triangle[1].x = squareSize;
\r
1440 triangle[1].y = squareSize;
\r
1441 triangle[2].x = squareSize;
\r
1442 triangle[2].y = squareSize - steps;
\r
1444 for( i=0; i<steps; i++ ) {
\r
1445 BYTE r = r1 - (r1-r2) * i / steps;
\r
1446 BYTE g = g1 - (g1-g2) * i / steps;
\r
1447 BYTE b = b1 - (b1-b2) * i / steps;
\r
1449 hbrush = CreateSolidBrush( RGB(r,g,b) );
\r
1450 hbrush_old = SelectObject( hdc, hbrush );
\r
1451 Polygon( hdc, triangle, 3 );
\r
1452 SelectObject( hdc, hbrush_old );
\r
1453 DeleteObject(hbrush);
\r
1458 SelectObject( hdc, hpen );
\r
1463 [AS] The method I use to create the bitmaps it a bit tricky, but it
\r
1464 seems to work ok. The main problem here is to find the "inside" of a chess
\r
1465 piece: follow the steps as explained below.
\r
1467 static void CreatePieceMaskFromFont( HDC hdc_window, HDC hdc, int index )
\r
1471 COLORREF chroma = RGB(0xFF,0x00,0xFF);
\r
1475 int backColor = whitePieceColor;
\r
1476 int foreColor = blackPieceColor;
\r
1478 if( index < (int)BlackPawn && appData.fontBackColorWhite != appData.fontForeColorWhite ) {
\r
1479 backColor = appData.fontBackColorWhite;
\r
1480 foreColor = appData.fontForeColorWhite;
\r
1482 else if( index >= (int)BlackPawn && appData.fontBackColorBlack != appData.fontForeColorBlack ) {
\r
1483 backColor = appData.fontBackColorBlack;
\r
1484 foreColor = appData.fontForeColorBlack;
\r
1488 hbm = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );
\r
1490 hbm_old = SelectObject( hdc, hbm );
\r
1494 rc.right = squareSize;
\r
1495 rc.bottom = squareSize;
\r
1497 /* Step 1: background is now black */
\r
1498 FillRect( hdc, &rc, GetStockObject(BLACK_BRUSH) );
\r
1500 GetTextExtentPoint32( hdc, &pieceToFontChar[index], 1, &sz );
\r
1502 pt.x = (squareSize - sz.cx) / 2;
\r
1503 pt.y = (squareSize - sz.cy) / 2;
\r
1505 SetBkMode( hdc, TRANSPARENT );
\r
1506 SetTextColor( hdc, chroma );
\r
1507 /* Step 2: the piece has been drawn in purple, there are now black and purple in this bitmap */
\r
1508 TextOut( hdc, pt.x, pt.y, &pieceToFontChar[appData.allWhite && index >= (int)BlackPawn ? index - (int)BlackPawn : index], 1 );
\r
1510 SelectObject( hdc, GetStockObject(WHITE_BRUSH) );
\r
1511 /* Step 3: the area outside the piece is filled with white */
\r
1512 // FloodFill( hdc, 0, 0, chroma );
\r
1513 ExtFloodFill( hdc, 0, 0, 0, FLOODFILLSURFACE );
\r
1514 ExtFloodFill( hdc, 0, squareSize-1, 0, FLOODFILLSURFACE ); // [HGM] fill from all 4 corners, for if piece too big
\r
1515 ExtFloodFill( hdc, squareSize-1, 0, 0, FLOODFILLSURFACE );
\r
1516 ExtFloodFill( hdc, squareSize-1, squareSize-1, 0, FLOODFILLSURFACE );
\r
1517 SelectObject( hdc, GetStockObject(BLACK_BRUSH) );
\r
1519 Step 4: this is the tricky part, the area inside the piece is filled with black,
\r
1520 but if the start point is not inside the piece we're lost!
\r
1521 There should be a better way to do this... if we could create a region or path
\r
1522 from the fill operation we would be fine for example.
\r
1524 // FloodFill( hdc, squareSize / 2, squareSize / 2, RGB(0xFF,0xFF,0xFF) );
\r
1525 ExtFloodFill( hdc, squareSize / 2, squareSize / 2, RGB(0xFF,0xFF,0xFF), FLOODFILLBORDER );
\r
1527 { /* [HGM] shave off edges of mask, in an attempt to correct for the fact that FloodFill does not work correctly under Win XP */
\r
1528 HDC dc2 = CreateCompatibleDC( hdc_window );
\r
1529 HBITMAP bm2 = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );
\r
1531 SelectObject( dc2, bm2 );
\r
1532 BitBlt( dc2, 0, 0, squareSize, squareSize, hdc, 0, 0, SRCCOPY ); // make copy
\r
1533 BitBlt( hdc, 0, 1, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );
\r
1534 BitBlt( hdc, 2, 1, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );
\r
1535 BitBlt( hdc, 1, 0, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );
\r
1536 BitBlt( hdc, 1, 2, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );
\r
1539 DeleteObject( bm2 );
\r
1542 SetTextColor( hdc, 0 );
\r
1544 Step 5: some fonts have "disconnected" areas that are skipped by the fill:
\r
1545 draw the piece again in black for safety.
\r
1547 TextOut( hdc, pt.x, pt.y, &pieceToFontChar[appData.allWhite && index >= (int)BlackPawn ? index - (int)BlackPawn : index], 1 );
\r
1549 SelectObject( hdc, hbm_old );
\r
1551 if( hPieceMask[index] != NULL ) {
\r
1552 DeleteObject( hPieceMask[index] );
\r
1555 hPieceMask[index] = hbm;
\r
1558 hbm = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );
\r
1560 SelectObject( hdc, hbm );
\r
1563 HDC dc1 = CreateCompatibleDC( hdc_window );
\r
1564 HDC dc2 = CreateCompatibleDC( hdc_window );
\r
1565 HBITMAP bm2 = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );
\r
1567 SelectObject( dc1, hPieceMask[index] );
\r
1568 SelectObject( dc2, bm2 );
\r
1569 FillRect( dc2, &rc, GetStockObject(WHITE_BRUSH) );
\r
1570 BitBlt( dc2, 0, 0, squareSize, squareSize, dc1, 0, 0, SRCINVERT );
\r
1573 Now dc2 contains the inverse of the piece mask, i.e. a mask that preserves
\r
1574 the piece background and deletes (makes transparent) the rest.
\r
1575 Thanks to that mask, we are free to paint the background with the greates
\r
1576 freedom, as we'll be able to mask off the unwanted parts when finished.
\r
1577 We use this, to make gradients and give the pieces a "roundish" look.
\r
1579 SetPieceBackground( hdc, backColor, 2 );
\r
1580 BitBlt( hdc, 0, 0, squareSize, squareSize, dc2, 0, 0, SRCAND );
\r
1584 DeleteObject( bm2 );
\r
1587 SetTextColor( hdc, foreColor );
\r
1588 TextOut( hdc, pt.x, pt.y, &pieceToFontChar[appData.allWhite && index >= (int)BlackPawn ? index - (int)BlackPawn : index], 1 );
\r
1590 SelectObject( hdc, hbm_old );
\r
1592 if( hPieceFace[index] != NULL ) {
\r
1593 DeleteObject( hPieceFace[index] );
\r
1596 hPieceFace[index] = hbm;
\r
1599 static int TranslatePieceToFontPiece( int piece )
\r
1629 case BlackMarshall:
\r
1633 case BlackNightrider:
\r
1639 case BlackUnicorn:
\r
1643 case BlackGrasshopper:
\r
1655 case BlackCardinal:
\r
1662 case WhiteMarshall:
\r
1666 case WhiteNightrider:
\r
1672 case WhiteUnicorn:
\r
1676 case WhiteGrasshopper:
\r
1688 case WhiteCardinal:
\r
1697 void CreatePiecesFromFont()
\r
1700 HDC hdc_window = NULL;
\r
1706 if( fontBitmapSquareSize < 0 ) {
\r
1707 /* Something went seriously wrong in the past: do not try to recreate fonts! */
\r
1711 if( appData.renderPiecesWithFont == NULL || appData.renderPiecesWithFont[0] == NULLCHAR || appData.renderPiecesWithFont[0] == '*' ) {
\r
1712 fontBitmapSquareSize = -1;
\r
1716 if( fontBitmapSquareSize != squareSize ) {
\r
1717 hdc_window = GetDC( hwndMain );
\r
1718 hdc = CreateCompatibleDC( hdc_window );
\r
1720 if( hPieceFont != NULL ) {
\r
1721 DeleteObject( hPieceFont );
\r
1724 for( i=0; i<=(int)BlackKing; i++ ) {
\r
1725 hPieceMask[i] = NULL;
\r
1726 hPieceFace[i] = NULL;
\r
1732 if( appData.fontPieceSize >= 50 && appData.fontPieceSize <= 150 ) {
\r
1733 fontHeight = appData.fontPieceSize;
\r
1736 fontHeight = (fontHeight * squareSize) / 100;
\r
1738 lf.lfHeight = -MulDiv( fontHeight, GetDeviceCaps(hdc, LOGPIXELSY), 72 );
\r
1740 lf.lfEscapement = 0;
\r
1741 lf.lfOrientation = 0;
\r
1742 lf.lfWeight = FW_NORMAL;
\r
1744 lf.lfUnderline = 0;
\r
1745 lf.lfStrikeOut = 0;
\r
1746 lf.lfCharSet = DEFAULT_CHARSET;
\r
1747 lf.lfOutPrecision = OUT_DEFAULT_PRECIS;
\r
1748 lf.lfClipPrecision = CLIP_DEFAULT_PRECIS;
\r
1749 lf.lfQuality = PROOF_QUALITY;
\r
1750 lf.lfPitchAndFamily = DEFAULT_PITCH | FF_DONTCARE;
\r
1751 strncpy( lf.lfFaceName, appData.renderPiecesWithFont, sizeof(lf.lfFaceName) );
\r
1752 lf.lfFaceName[ sizeof(lf.lfFaceName) - 1 ] = '\0';
\r
1754 hPieceFont = CreateFontIndirect( &lf );
\r
1756 if( hPieceFont == NULL ) {
\r
1757 fontBitmapSquareSize = -2;
\r
1760 /* Setup font-to-piece character table */
\r
1761 if( ! SetCharTable(pieceToFontChar, appData.fontToPieceTable) ) {
\r
1762 /* No (or wrong) global settings, try to detect the font */
\r
1763 if( strstr(lf.lfFaceName,"Alpha") != NULL ) {
\r
1765 SetCharTable(pieceToFontChar, "phbrqkojntwl");
\r
1767 else if( strstr(lf.lfFaceName,"DiagramTT") != NULL ) {
\r
1768 /* DiagramTT* family */
\r
1769 SetCharTable(pieceToFontChar, "PNLRQKpnlrqk");
\r
1771 else if( strstr(lf.lfFaceName,"WinboardF") != NULL ) {
\r
1772 /* Fairy symbols */
\r
1773 SetCharTable(pieceToFontChar, "PNBRQFEACWMOHIJGDVSLUKpnbrqfeacwmohijgdvsluk");
\r
1775 else if( strstr(lf.lfFaceName,"GC2004D") != NULL ) {
\r
1776 /* Good Companion (Some characters get warped as literal :-( */
\r
1777 char s[] = "1cmWG0??S??oYI23wgQU";
\r
1778 s[0]=0xB9; s[1]=0xA9; s[6]=0xB1; s[11]=0xBB; s[12]=0xAB; s[17]=0xB3;
\r
1779 SetCharTable(pieceToFontChar, s);
\r
1782 /* Cases, Condal, Leipzig, Lucena, Marroquin, Merida, Usual */
\r
1783 SetCharTable(pieceToFontChar, "pnbrqkomvtwl");
\r
1787 /* Create bitmaps */
\r
1788 hfont_old = SelectObject( hdc, hPieceFont );
\r
1789 for(i=(int)WhitePawn; i<(int)EmptySquare; i++) /* [HGM] made a loop for this */
\r
1790 if(PieceToChar((ChessSquare)i) != '.') /* skip unused pieces */
\r
1791 CreatePieceMaskFromFont( hdc_window, hdc, i );
\r
1793 SelectObject( hdc, hfont_old );
\r
1795 fontBitmapSquareSize = squareSize;
\r
1799 if( hdc != NULL ) {
\r
1803 if( hdc_window != NULL ) {
\r
1804 ReleaseDC( hwndMain, hdc_window );
\r
1809 DoLoadBitmap(HINSTANCE hinst, char *piece, int squareSize, char *suffix)
\r
1813 sprintf(name, "%s%d%s", piece, squareSize, suffix);
\r
1814 if (gameInfo.event &&
\r
1815 strcmp(gameInfo.event, "Easter Egg Hunt") == 0 &&
\r
1816 strcmp(name, "k80s") == 0) {
\r
1817 strcpy(name, "tim");
\r
1819 return LoadBitmap(hinst, name);
\r
1823 /* Insert a color into the program's logical palette
\r
1824 structure. This code assumes the given color is
\r
1825 the result of the RGB or PALETTERGB macro, and it
\r
1826 knows how those macros work (which is documented).
\r
1829 InsertInPalette(COLORREF color)
\r
1831 LPPALETTEENTRY pe = &(pLogPal->palPalEntry[pLogPal->palNumEntries]);
\r
1833 if (pLogPal->palNumEntries++ >= PALETTESIZE) {
\r
1834 DisplayFatalError("Too many colors", 0, 1);
\r
1835 pLogPal->palNumEntries--;
\r
1839 pe->peFlags = (char) 0;
\r
1840 pe->peRed = (char) (0xFF & color);
\r
1841 pe->peGreen = (char) (0xFF & (color >> 8));
\r
1842 pe->peBlue = (char) (0xFF & (color >> 16));
\r
1848 InitDrawingColors()
\r
1850 if (pLogPal == NULL) {
\r
1851 /* Allocate enough memory for a logical palette with
\r
1852 * PALETTESIZE entries and set the size and version fields
\r
1853 * of the logical palette structure.
\r
1855 pLogPal = (NPLOGPALETTE)
\r
1856 LocalAlloc(LMEM_FIXED, (sizeof(LOGPALETTE) +
\r
1857 (sizeof(PALETTEENTRY) * (PALETTESIZE))));
\r
1858 pLogPal->palVersion = 0x300;
\r
1860 pLogPal->palNumEntries = 0;
\r
1862 InsertInPalette(lightSquareColor);
\r
1863 InsertInPalette(darkSquareColor);
\r
1864 InsertInPalette(whitePieceColor);
\r
1865 InsertInPalette(blackPieceColor);
\r
1866 InsertInPalette(highlightSquareColor);
\r
1867 InsertInPalette(premoveHighlightColor);
\r
1869 /* create a logical color palette according the information
\r
1870 * in the LOGPALETTE structure.
\r
1872 hPal = CreatePalette((LPLOGPALETTE) pLogPal);
\r
1874 lightSquareBrush = CreateSolidBrush(lightSquareColor);
\r
1875 blackSquareBrush = CreateSolidBrush(blackPieceColor);
\r
1876 darkSquareBrush = CreateSolidBrush(darkSquareColor);
\r
1877 whitePieceBrush = CreateSolidBrush(whitePieceColor);
\r
1878 blackPieceBrush = CreateSolidBrush(blackPieceColor);
\r
1879 iconBkgndBrush = CreateSolidBrush(GetSysColor(COLOR_BACKGROUND));
\r
1880 explodeBrush = CreateSolidBrush(highlightSquareColor); // [HGM] atomic
\r
1881 markerBrush = CreateSolidBrush(premoveHighlightColor); // [HGM] markers
\r
1882 /* [AS] Force rendering of the font-based pieces */
\r
1883 if( fontBitmapSquareSize > 0 ) {
\r
1884 fontBitmapSquareSize = 0;
\r
1890 BoardWidth(int boardSize, int n)
\r
1891 { /* [HGM] argument n added to allow different width and height */
\r
1892 int lineGap = sizeInfo[boardSize].lineGap;
\r
1894 if( appData.overrideLineGap >= 0 && appData.overrideLineGap <= 5 ) {
\r
1895 lineGap = appData.overrideLineGap;
\r
1898 return (n + 1) * lineGap +
\r
1899 n * sizeInfo[boardSize].squareSize;
\r
1902 /* Respond to board resize by dragging edge */
\r
1904 ResizeBoard(int newSizeX, int newSizeY, int flags)
\r
1906 BoardSize newSize = NUM_SIZES - 1;
\r
1907 static int recurse = 0;
\r
1908 if (IsIconic(hwndMain)) return;
\r
1909 if (recurse > 0) return;
\r
1911 while (newSize > 0) {
\r
1912 InitDrawingSizes(newSize+1000, 0); // [HGM] kludge to update sizeInfo without visible effects
\r
1913 if(newSizeX >= sizeInfo[newSize].cliWidth &&
\r
1914 newSizeY >= sizeInfo[newSize].cliHeight) break;
\r
1917 boardSize = newSize;
\r
1918 InitDrawingSizes(boardSize, flags);
\r
1923 extern Boolean twoBoards, partnerUp; // [HGM] dual
\r
1926 InitDrawingSizes(BoardSize boardSize, int flags)
\r
1928 int i, boardWidth, boardHeight; /* [HGM] height treated separately */
\r
1929 ChessSquare piece;
\r
1930 static int oldBoardSize = -1, oldTinyLayout = 0;
\r
1932 SIZE clockSize, messageSize;
\r
1934 char buf[MSG_SIZ];
\r
1936 HMENU hmenu = GetMenu(hwndMain);
\r
1937 RECT crect, wrect, oldRect;
\r
1939 LOGBRUSH logbrush;
\r
1941 int suppressVisibleEffects = 0; // [HGM] kludge to request updating sizeInfo only
\r
1942 if((int)boardSize >= 1000 ) { boardSize -= 1000; suppressVisibleEffects = 1; }
\r
1944 /* [HGM] call with -2 uses old size (for if nr of files, ranks changes) */
\r
1945 if(boardSize == (BoardSize)(-2) ) boardSize = oldBoardSize;
\r
1947 oldRect.left = wpMain.x; //[HGM] placement: remember previous window params
\r
1948 oldRect.top = wpMain.y;
\r
1949 oldRect.right = wpMain.x + wpMain.width;
\r
1950 oldRect.bottom = wpMain.y + wpMain.height;
\r
1952 tinyLayout = sizeInfo[boardSize].tinyLayout;
\r
1953 smallLayout = sizeInfo[boardSize].smallLayout;
\r
1954 squareSize = sizeInfo[boardSize].squareSize;
\r
1955 lineGap = sizeInfo[boardSize].lineGap;
\r
1956 minorSize = 0; /* [HGM] Kludge to see if demagnified pieces need to be shifted */
\r
1958 if( appData.overrideLineGap >= 0 && appData.overrideLineGap <= 5 ) {
\r
1959 lineGap = appData.overrideLineGap;
\r
1962 if (tinyLayout != oldTinyLayout) {
\r
1963 long style = GetWindowLong(hwndMain, GWL_STYLE);
\r
1965 style &= ~WS_SYSMENU;
\r
1966 InsertMenu(hmenu, IDM_Exit, MF_BYCOMMAND, IDM_Minimize,
\r
1967 "&Minimize\tCtrl+F4");
\r
1969 style |= WS_SYSMENU;
\r
1970 RemoveMenu(hmenu, IDM_Minimize, MF_BYCOMMAND);
\r
1972 SetWindowLong(hwndMain, GWL_STYLE, style);
\r
1974 for (i=0; menuBarText[tinyLayout][i]; i++) {
\r
1975 ModifyMenu(hmenu, i, MF_STRING|MF_BYPOSITION|MF_POPUP,
\r
1976 (UINT)GetSubMenu(hmenu, i), menuBarText[tinyLayout][i]);
\r
1978 DrawMenuBar(hwndMain);
\r
1981 boardWidth = BoardWidth(boardSize, BOARD_WIDTH);
\r
1982 boardHeight = BoardWidth(boardSize, BOARD_HEIGHT);
\r
1984 /* Get text area sizes */
\r
1985 hdc = GetDC(hwndMain);
\r
1986 if (appData.clockMode) {
\r
1987 sprintf(buf, "White: %s", TimeString(23*60*60*1000L));
\r
1989 sprintf(buf, "White");
\r
1991 oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);
\r
1992 GetTextExtentPoint(hdc, buf, strlen(buf), &clockSize);
\r
1993 SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);
\r
1994 str = "We only care about the height here";
\r
1995 GetTextExtentPoint(hdc, str, strlen(str), &messageSize);
\r
1996 SelectObject(hdc, oldFont);
\r
1997 ReleaseDC(hwndMain, hdc);
\r
1999 /* Compute where everything goes */
\r
2000 if((first.programLogo || second.programLogo) && !tinyLayout) {
\r
2001 /* [HGM] logo: if either logo is on, reserve space for it */
\r
2002 logoHeight = 2*clockSize.cy;
\r
2003 leftLogoRect.left = OUTER_MARGIN;
\r
2004 leftLogoRect.right = leftLogoRect.left + 4*clockSize.cy;
\r
2005 leftLogoRect.top = OUTER_MARGIN;
\r
2006 leftLogoRect.bottom = OUTER_MARGIN + logoHeight;
\r
2008 rightLogoRect.right = OUTER_MARGIN + boardWidth;
\r
2009 rightLogoRect.left = rightLogoRect.right - 4*clockSize.cy;
\r
2010 rightLogoRect.top = OUTER_MARGIN;
\r
2011 rightLogoRect.bottom = OUTER_MARGIN + logoHeight;
\r
2014 whiteRect.left = leftLogoRect.right;
\r
2015 whiteRect.right = OUTER_MARGIN + boardWidth/2 - INNER_MARGIN/2;
\r
2016 whiteRect.top = OUTER_MARGIN;
\r
2017 whiteRect.bottom = whiteRect.top + logoHeight;
\r
2019 blackRect.right = rightLogoRect.left;
\r
2020 blackRect.left = whiteRect.right + INNER_MARGIN;
\r
2021 blackRect.top = whiteRect.top;
\r
2022 blackRect.bottom = whiteRect.bottom;
\r
2024 whiteRect.left = OUTER_MARGIN;
\r
2025 whiteRect.right = whiteRect.left + boardWidth/2 - INNER_MARGIN/2;
\r
2026 whiteRect.top = OUTER_MARGIN;
\r
2027 whiteRect.bottom = whiteRect.top + clockSize.cy;
\r
2029 blackRect.left = whiteRect.right + INNER_MARGIN;
\r
2030 blackRect.right = blackRect.left + boardWidth/2 - 1;
\r
2031 blackRect.top = whiteRect.top;
\r
2032 blackRect.bottom = whiteRect.bottom;
\r
2034 logoHeight = 0; // [HGM] logo: suppress logo after change to tiny layout!
\r
2037 messageRect.left = OUTER_MARGIN + MESSAGE_LINE_LEFTMARGIN;
\r
2038 if (appData.showButtonBar) {
\r
2039 messageRect.right = OUTER_MARGIN + boardWidth // [HGM] logo: expressed independent of clock placement
\r
2040 - N_BUTTONS*BUTTON_WIDTH - MESSAGE_LINE_LEFTMARGIN;
\r
2042 messageRect.right = OUTER_MARGIN + boardWidth;
\r
2044 messageRect.top = whiteRect.bottom + INNER_MARGIN;
\r
2045 messageRect.bottom = messageRect.top + messageSize.cy;
\r
2047 boardRect.left = OUTER_MARGIN;
\r
2048 boardRect.right = boardRect.left + boardWidth;
\r
2049 boardRect.top = messageRect.bottom + INNER_MARGIN;
\r
2050 boardRect.bottom = boardRect.top + boardHeight;
\r
2052 sizeInfo[boardSize].cliWidth = boardRect.right + OUTER_MARGIN;
\r
2053 sizeInfo[boardSize].cliHeight = boardRect.bottom + OUTER_MARGIN;
\r
2054 oldBoardSize = boardSize;
\r
2055 oldTinyLayout = tinyLayout;
\r
2056 winW = 2 * GetSystemMetrics(SM_CXFRAME) + boardRect.right + OUTER_MARGIN;
\r
2057 winH = 2 * GetSystemMetrics(SM_CYFRAME) + GetSystemMetrics(SM_CYMENU) +
\r
2058 GetSystemMetrics(SM_CYCAPTION) + boardRect.bottom + OUTER_MARGIN;
\r
2059 winW *= 1 + twoBoards;
\r
2060 if(suppressVisibleEffects) return; // [HGM] when called for filling sizeInfo only
\r
2061 wpMain.width = winW; // [HGM] placement: set through temporary which can used by initial sizing choice
\r
2062 wpMain.height = winH; // without disturbing window attachments
\r
2063 GetWindowRect(hwndMain, &wrect);
\r
2064 SetWindowPos(hwndMain, NULL, 0, 0, wpMain.width, wpMain.height,
\r
2065 SWP_NOCOPYBITS|SWP_NOZORDER|SWP_NOMOVE);
\r
2067 // [HGM] placement: let attached windows follow size change.
\r
2068 ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, moveHistoryDialog, &wpMoveHistory );
\r
2069 ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, evalGraphDialog, &wpEvalGraph );
\r
2070 ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, engineOutputDialog, &wpEngineOutput );
\r
2071 ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, gameListDialog, &wpGameList );
\r
2072 ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, hwndConsole, &wpConsole );
\r
2074 /* compensate if menu bar wrapped */
\r
2075 GetClientRect(hwndMain, &crect);
\r
2076 offby = boardRect.bottom + OUTER_MARGIN - crect.bottom;
\r
2077 wpMain.height += offby;
\r
2079 case WMSZ_TOPLEFT:
\r
2080 SetWindowPos(hwndMain, NULL,
\r
2081 wrect.right - wpMain.width, wrect.bottom - wpMain.height,
\r
2082 wpMain.width, wpMain.height, SWP_NOCOPYBITS|SWP_NOZORDER);
\r
2085 case WMSZ_TOPRIGHT:
\r
2087 SetWindowPos(hwndMain, NULL,
\r
2088 wrect.left, wrect.bottom - wpMain.height,
\r
2089 wpMain.width, wpMain.height, SWP_NOCOPYBITS|SWP_NOZORDER);
\r
2092 case WMSZ_BOTTOMLEFT:
\r
2094 SetWindowPos(hwndMain, NULL,
\r
2095 wrect.right - wpMain.width, wrect.top,
\r
2096 wpMain.width, wpMain.height, SWP_NOCOPYBITS|SWP_NOZORDER);
\r
2099 case WMSZ_BOTTOMRIGHT:
\r
2103 SetWindowPos(hwndMain, NULL, 0, 0, wpMain.width, wpMain.height,
\r
2104 SWP_NOCOPYBITS|SWP_NOZORDER|SWP_NOMOVE);
\r
2109 for (i = 0; i < N_BUTTONS; i++) {
\r
2110 if (buttonDesc[i].hwnd != NULL) {
\r
2111 DestroyWindow(buttonDesc[i].hwnd);
\r
2112 buttonDesc[i].hwnd = NULL;
\r
2114 if (appData.showButtonBar) {
\r
2115 buttonDesc[i].hwnd =
\r
2116 CreateWindow("BUTTON", buttonDesc[i].label,
\r
2117 WS_VISIBLE | WS_CHILD | BS_PUSHBUTTON,
\r
2118 boardRect.right - BUTTON_WIDTH*(N_BUTTONS-i),
\r
2119 messageRect.top, BUTTON_WIDTH, messageSize.cy, hwndMain,
\r
2120 (HMENU) buttonDesc[i].id,
\r
2121 (HINSTANCE) GetWindowLong(hwndMain, GWL_HINSTANCE), NULL);
\r
2123 SendMessage(buttonDesc[i].hwnd, WM_SETFONT,
\r
2124 (WPARAM)font[boardSize][MESSAGE_FONT]->hf,
\r
2125 MAKELPARAM(FALSE, 0));
\r
2127 if (buttonDesc[i].id == IDM_Pause)
\r
2128 hwndPause = buttonDesc[i].hwnd;
\r
2129 buttonDesc[i].wndproc = (WNDPROC)
\r
2130 SetWindowLong(buttonDesc[i].hwnd, GWL_WNDPROC, (LONG) ButtonProc);
\r
2133 if (gridPen != NULL) DeleteObject(gridPen);
\r
2134 if (highlightPen != NULL) DeleteObject(highlightPen);
\r
2135 if (premovePen != NULL) DeleteObject(premovePen);
\r
2136 if (lineGap != 0) {
\r
2137 logbrush.lbStyle = BS_SOLID;
\r
2138 logbrush.lbColor = RGB(0, 0, 0); /* grid pen color = black */
\r
2140 ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,
\r
2141 lineGap, &logbrush, 0, NULL);
\r
2142 logbrush.lbColor = highlightSquareColor;
\r
2144 ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,
\r
2145 lineGap, &logbrush, 0, NULL);
\r
2147 logbrush.lbColor = premoveHighlightColor;
\r
2149 ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,
\r
2150 lineGap, &logbrush, 0, NULL);
\r
2152 /* [HGM] Loop had to be split in part for vert. and hor. lines */
\r
2153 for (i = 0; i < BOARD_HEIGHT + 1; i++) {
\r
2154 gridEndpoints[i*2].x = boardRect.left + lineGap / 2;
\r
2155 gridEndpoints[i*2].y = gridEndpoints[i*2 + 1].y =
\r
2156 boardRect.top + lineGap / 2 + (i * (squareSize + lineGap));
\r
2157 gridEndpoints[i*2 + 1].x = boardRect.left + lineGap / 2 +
\r
2158 BOARD_WIDTH * (squareSize + lineGap);
\r
2159 gridVertexCounts[i*2] = gridVertexCounts[i*2 + 1] = 2;
\r
2161 for (i = 0; i < BOARD_WIDTH + 1; i++) {
\r
2162 gridEndpoints[i*2 + BOARD_HEIGHT*2 + 2].y = boardRect.top + lineGap / 2;
\r
2163 gridEndpoints[i*2 + BOARD_HEIGHT*2 + 2].x =
\r
2164 gridEndpoints[i*2 + 1 + BOARD_HEIGHT*2 + 2].x = boardRect.left +
\r
2165 lineGap / 2 + (i * (squareSize + lineGap));
\r
2166 gridEndpoints[i*2 + 1 + BOARD_HEIGHT*2 + 2].y =
\r
2167 boardRect.top + BOARD_HEIGHT * (squareSize + lineGap);
\r
2168 gridVertexCounts[i*2] = gridVertexCounts[i*2 + 1] = 2;
\r
2172 /* [HGM] Licensing requirement */
\r
2174 if(gameInfo.variant == VariantGothic) GothicPopUp( GOTHIC, VariantGothic); else
\r
2177 if(gameInfo.variant == VariantFalcon) GothicPopUp( FALCON, VariantFalcon); else
\r
2179 GothicPopUp( "", VariantNormal);
\r
2182 /* if (boardSize == oldBoardSize) return; [HGM] variant might have changed */
\r
2184 /* Load piece bitmaps for this board size */
\r
2185 for (i=0; i<=2; i++) {
\r
2186 for (piece = WhitePawn;
\r
2187 (int) piece < (int) BlackPawn;
\r
2188 piece = (ChessSquare) ((int) piece + 1)) {
\r
2189 if (pieceBitmap[i][piece] != NULL)
\r
2190 DeleteObject(pieceBitmap[i][piece]);
\r
2194 fontBitmapSquareSize = 0; /* [HGM] render: make sure pieces will be recreated, as we might need others now */
\r
2195 // Orthodox Chess pieces
\r
2196 pieceBitmap[0][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "s");
\r
2197 pieceBitmap[0][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "s");
\r
2198 pieceBitmap[0][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "s");
\r
2199 pieceBitmap[0][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "s");
\r
2200 pieceBitmap[0][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "s");
\r
2201 pieceBitmap[1][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "o");
\r
2202 pieceBitmap[1][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "o");
\r
2203 pieceBitmap[1][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "o");
\r
2204 pieceBitmap[1][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "o");
\r
2205 pieceBitmap[1][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "o");
\r
2206 pieceBitmap[2][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "w");
\r
2207 pieceBitmap[2][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "w");
\r
2208 pieceBitmap[2][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "w");
\r
2209 pieceBitmap[2][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "w");
\r
2210 pieceBitmap[2][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "w");
\r
2211 if( !strcmp(appData.variant, "shogi") && (squareSize==72 || squareSize==49)) {
\r
2212 // in Shogi, Hijack the unused Queen for Lance
\r
2213 pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "s");
\r
2214 pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "o");
\r
2215 pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "w");
\r
2217 pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "s");
\r
2218 pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "o");
\r
2219 pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "w");
\r
2222 if(squareSize <= 72 && squareSize >= 33) {
\r
2223 /* A & C are available in most sizes now */
\r
2224 if(squareSize != 49 && squareSize != 72 && squareSize != 33) { // Vortex-like
\r
2225 pieceBitmap[0][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "s");
\r
2226 pieceBitmap[1][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "o");
\r
2227 pieceBitmap[2][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "w");
\r
2228 pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "s");
\r
2229 pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "o");
\r
2230 pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "w");
\r
2231 pieceBitmap[0][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "s");
\r
2232 pieceBitmap[1][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "o");
\r
2233 pieceBitmap[2][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "w");
\r
2234 pieceBitmap[0][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "s");
\r
2235 pieceBitmap[1][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "o");
\r
2236 pieceBitmap[2][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "w");
\r
2237 } else { // Smirf-like
\r
2238 pieceBitmap[0][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "s");
\r
2239 pieceBitmap[1][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "o");
\r
2240 pieceBitmap[2][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "w");
\r
2242 if(gameInfo.variant == VariantGothic) { // Vortex-like
\r
2243 pieceBitmap[0][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "s");
\r
2244 pieceBitmap[1][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "o");
\r
2245 pieceBitmap[2][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "w");
\r
2246 } else { // WinBoard standard
\r
2247 pieceBitmap[0][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "s");
\r
2248 pieceBitmap[1][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "o");
\r
2249 pieceBitmap[2][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "w");
\r
2254 if(squareSize==72 || squareSize==49 || squareSize==33) { /* experiment with some home-made bitmaps */
\r
2255 pieceBitmap[0][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "s");
\r
2256 pieceBitmap[1][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "o");
\r
2257 pieceBitmap[2][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "w");
\r
2258 pieceBitmap[0][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "s");
\r
2259 pieceBitmap[1][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "o");
\r
2260 pieceBitmap[2][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "w");
\r
2261 pieceBitmap[0][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "s");
\r
2262 pieceBitmap[1][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "o");
\r
2263 pieceBitmap[2][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "w");
\r
2264 pieceBitmap[0][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "s");
\r
2265 pieceBitmap[1][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "o");
\r
2266 pieceBitmap[2][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "w");
\r
2267 pieceBitmap[0][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "s");
\r
2268 pieceBitmap[1][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "o");
\r
2269 pieceBitmap[2][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "w");
\r
2270 pieceBitmap[0][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "s");
\r
2271 pieceBitmap[1][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "o");
\r
2272 pieceBitmap[2][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "w");
\r
2273 pieceBitmap[0][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "s");
\r
2274 pieceBitmap[1][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "o");
\r
2275 pieceBitmap[2][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "w");
\r
2276 pieceBitmap[0][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "s");
\r
2277 pieceBitmap[1][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "o");
\r
2278 pieceBitmap[2][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "w");
\r
2279 pieceBitmap[0][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "s");
\r
2280 pieceBitmap[1][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "o");
\r
2281 pieceBitmap[2][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "w");
\r
2282 pieceBitmap[0][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "s");
\r
2283 pieceBitmap[1][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "o");
\r
2284 pieceBitmap[2][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "w");
\r
2286 if(gameInfo.variant == VariantShogi) { /* promoted Gold represemtations */
\r
2287 pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "wp", squareSize, "s");
\r
2288 pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "wp", squareSize, "o");
\r
2289 pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "w", squareSize, "w");
\r
2290 pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "wn", squareSize, "s");
\r
2291 pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "wn", squareSize, "o");
\r
2292 pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "w", squareSize, "w");
\r
2293 pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "ws", squareSize, "s");
\r
2294 pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "ws", squareSize, "o");
\r
2295 pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "w", squareSize, "w");
\r
2296 pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "wl", squareSize, "s");
\r
2297 pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "wl", squareSize, "o");
\r
2298 pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "w", squareSize, "w");
\r
2300 pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "s");
\r
2301 pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "o");
\r
2302 pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "w");
\r
2303 pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "s");
\r
2304 pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "o");
\r
2305 pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "w");
\r
2306 pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "s");
\r
2307 pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "o");
\r
2308 pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "w");
\r
2309 pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "s");
\r
2310 pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "o");
\r
2311 pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "w");
\r
2314 } else { /* other size, no special bitmaps available. Use smaller symbols */
\r
2315 if((int)boardSize < 2) minorSize = sizeInfo[0].squareSize;
\r
2316 else minorSize = sizeInfo[(int)boardSize - 2].squareSize;
\r
2317 pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "s");
\r
2318 pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "o");
\r
2319 pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "w");
\r
2320 pieceBitmap[0][WhiteCardinal] = DoLoadBitmap(hInst, "b", minorSize, "s");
\r
2321 pieceBitmap[1][WhiteCardinal] = DoLoadBitmap(hInst, "b", minorSize, "o");
\r
2322 pieceBitmap[2][WhiteCardinal] = DoLoadBitmap(hInst, "b", minorSize, "w");
\r
2323 pieceBitmap[0][WhiteDragon] = DoLoadBitmap(hInst, "r", minorSize, "s");
\r
2324 pieceBitmap[1][WhiteDragon] = DoLoadBitmap(hInst, "r", minorSize, "o");
\r
2325 pieceBitmap[2][WhiteDragon] = DoLoadBitmap(hInst, "r", minorSize, "w");
\r
2326 pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "s");
\r
2327 pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "o");
\r
2328 pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "w");
\r
2332 if(gameInfo.variant == VariantShogi && squareSize == 58)
\r
2333 /* special Shogi support in this size */
\r
2334 { for (i=0; i<=2; i++) { /* replace all bitmaps */
\r
2335 for (piece = WhitePawn;
\r
2336 (int) piece < (int) BlackPawn;
\r
2337 piece = (ChessSquare) ((int) piece + 1)) {
\r
2338 if (pieceBitmap[i][piece] != NULL)
\r
2339 DeleteObject(pieceBitmap[i][piece]);
\r
2342 pieceBitmap[0][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "o");
\r
2343 pieceBitmap[0][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "o");
\r
2344 pieceBitmap[0][WhiteBishop] = DoLoadBitmap(hInst, "sb", squareSize, "o");
\r
2345 pieceBitmap[0][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "o");
\r
2346 pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "o");
\r
2347 pieceBitmap[0][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "o");
\r
2348 pieceBitmap[0][WhiteFerz] = DoLoadBitmap(hInst, "sf", squareSize, "o");
\r
2349 pieceBitmap[0][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "o");
\r
2350 pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "su", squareSize, "o");
\r
2351 pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "sh", squareSize, "o");
\r
2352 pieceBitmap[0][WhiteCardinal] = DoLoadBitmap(hInst, "sa", squareSize, "o");
\r
2353 pieceBitmap[0][WhiteDragon] = DoLoadBitmap(hInst, "sc", squareSize, "o");
\r
2354 pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "sg", squareSize, "o");
\r
2355 pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "ss", squareSize, "o");
\r
2356 pieceBitmap[1][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "o");
\r
2357 pieceBitmap[1][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "o");
\r
2358 pieceBitmap[1][WhiteBishop] = DoLoadBitmap(hInst, "sb", squareSize, "o");
\r
2359 pieceBitmap[1][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "o");
\r
2360 pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "o");
\r
2361 pieceBitmap[1][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "o");
\r
2362 pieceBitmap[1][WhiteFerz] = DoLoadBitmap(hInst, "sf", squareSize, "o");
\r
2363 pieceBitmap[1][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "o");
\r
2364 pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "su", squareSize, "o");
\r
2365 pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "sh", squareSize, "o");
\r
2366 pieceBitmap[1][WhiteCardinal] = DoLoadBitmap(hInst, "sa", squareSize, "o");
\r
2367 pieceBitmap[1][WhiteDragon] = DoLoadBitmap(hInst, "sc", squareSize, "o");
\r
2368 pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "sg", squareSize, "o");
\r
2369 pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "ss", squareSize, "o");
\r
2370 pieceBitmap[2][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "w");
\r
2371 pieceBitmap[2][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "w");
\r
2372 pieceBitmap[2][WhiteBishop] = DoLoadBitmap(hInst, "sr", squareSize, "w");
\r
2373 pieceBitmap[2][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "w");
\r
2374 pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "w");
\r
2375 pieceBitmap[2][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "w");
\r
2376 pieceBitmap[2][WhiteFerz] = DoLoadBitmap(hInst, "sw", squareSize, "w");
\r
2377 pieceBitmap[2][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "w");
\r
2378 pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "sp", squareSize, "w");
\r
2379 pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "sn", squareSize, "w");
\r
2380 pieceBitmap[2][WhiteCardinal] = DoLoadBitmap(hInst, "sr", squareSize, "w");
\r
2381 pieceBitmap[2][WhiteDragon] = DoLoadBitmap(hInst, "sr", squareSize, "w");
\r
2382 pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "sl", squareSize, "w");
\r
2383 pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "sw", squareSize, "w");
\r
2389 PieceBitmap(ChessSquare p, int kind)
\r
2391 if ((int) p >= (int) BlackPawn)
\r
2392 p = (ChessSquare) ((int) p - (int) BlackPawn + (int) WhitePawn);
\r
2394 return pieceBitmap[kind][(int) p];
\r
2397 /***************************************************************/
\r
2399 #define MIN(a,b) (((a) < (b)) ? (a) : (b))
\r
2400 #define MAX(a,b) (((a) > (b)) ? (a) : (b))
\r
2402 #define MIN3(a,b,c) (((a) < (b) && (a) < (c)) ? (a) : (((b) < (a) && (b) < (c)) ? (b) : (c)))
\r
2403 #define MAX3(a,b,c) (((a) > (b) && (a) > (c)) ? (a) : (((b) > (a) && (b) > (c)) ? (b) : (c)))
\r
2407 SquareToPos(int row, int column, int * x, int * y)
\r
2410 *x = boardRect.left + lineGap + ((BOARD_WIDTH-1)-column) * (squareSize + lineGap);
\r
2411 *y = boardRect.top + lineGap + row * (squareSize + lineGap);
\r
2413 *x = boardRect.left + lineGap + column * (squareSize + lineGap);
\r
2414 *y = boardRect.top + lineGap + ((BOARD_HEIGHT-1)-row) * (squareSize + lineGap);
\r
2419 DrawCoordsOnDC(HDC hdc)
\r
2421 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
2422 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
2423 char str[2] = { NULLCHAR, NULLCHAR };
\r
2424 int oldMode, oldAlign, x, y, start, i;
\r
2428 if (!appData.showCoords)
\r
2431 start = flipView ? 1-(ONE!='1') : 23+(ONE!='1')-BOARD_HEIGHT;
\r
2433 oldBrush = SelectObject(hdc, GetStockObject(BLACK_BRUSH));
\r
2434 oldMode = SetBkMode(hdc, (appData.monoMode ? OPAQUE : TRANSPARENT));
\r
2435 oldAlign = GetTextAlign(hdc);
\r
2436 oldFont = SelectObject(hdc, font[boardSize][COORD_FONT]->hf);
\r
2438 y = boardRect.top + lineGap;
\r
2439 x = boardRect.left + lineGap + gameInfo.holdingsWidth*(squareSize + lineGap);
\r
2441 SetTextAlign(hdc, TA_LEFT|TA_TOP);
\r
2442 for (i = 0; i < BOARD_HEIGHT; i++) {
\r
2443 str[0] = files[start + i];
\r
2444 ExtTextOut(hdc, x + 2, y + 1, 0, NULL, str, 1, NULL);
\r
2445 y += squareSize + lineGap;
\r
2448 start = flipView ? 12-(BOARD_RGHT-BOARD_LEFT) : 12;
\r
2450 SetTextAlign(hdc, TA_RIGHT|TA_BOTTOM);
\r
2451 for (i = 0; i < BOARD_RGHT - BOARD_LEFT; i++) {
\r
2452 str[0] = ranks[start + i];
\r
2453 ExtTextOut(hdc, x + squareSize - 2, y - 1, 0, NULL, str, 1, NULL);
\r
2454 x += squareSize + lineGap;
\r
2457 SelectObject(hdc, oldBrush);
\r
2458 SetBkMode(hdc, oldMode);
\r
2459 SetTextAlign(hdc, oldAlign);
\r
2460 SelectObject(hdc, oldFont);
\r
2464 DrawGridOnDC(HDC hdc)
\r
2468 if (lineGap != 0) {
\r
2469 oldPen = SelectObject(hdc, gridPen);
\r
2470 PolyPolyline(hdc, gridEndpoints, gridVertexCounts, BOARD_WIDTH+BOARD_HEIGHT + 2);
\r
2471 SelectObject(hdc, oldPen);
\r
2475 #define HIGHLIGHT_PEN 0
\r
2476 #define PREMOVE_PEN 1
\r
2479 DrawHighlightOnDC(HDC hdc, BOOLEAN on, int x, int y, int pen)
\r
2482 HPEN oldPen, hPen;
\r
2483 if (lineGap == 0) return;
\r
2485 x1 = boardRect.left +
\r
2486 lineGap/2 + ((BOARD_WIDTH-1)-x) * (squareSize + lineGap);
\r
2487 y1 = boardRect.top +
\r
2488 lineGap/2 + y * (squareSize + lineGap);
\r
2490 x1 = boardRect.left +
\r
2491 lineGap/2 + x * (squareSize + lineGap);
\r
2492 y1 = boardRect.top +
\r
2493 lineGap/2 + ((BOARD_HEIGHT-1)-y) * (squareSize + lineGap);
\r
2495 hPen = pen ? premovePen : highlightPen;
\r
2496 oldPen = SelectObject(hdc, on ? hPen : gridPen);
\r
2497 MoveToEx(hdc, x1, y1, NULL);
\r
2498 LineTo(hdc, x1 + squareSize + lineGap, y1);
\r
2499 LineTo(hdc, x1 + squareSize + lineGap, y1 + squareSize + lineGap);
\r
2500 LineTo(hdc, x1, y1 + squareSize + lineGap);
\r
2501 LineTo(hdc, x1, y1);
\r
2502 SelectObject(hdc, oldPen);
\r
2506 DrawHighlightsOnDC(HDC hdc, HighlightInfo *h, int pen)
\r
2509 for (i=0; i<2; i++) {
\r
2510 if (h->sq[i].x >= 0 && h->sq[i].y >= 0)
\r
2511 DrawHighlightOnDC(hdc, TRUE,
\r
2512 h->sq[i].x, h->sq[i].y,
\r
2517 /* Note: sqcolor is used only in monoMode */
\r
2518 /* Note that this code is largely duplicated in woptions.c,
\r
2519 function DrawSampleSquare, so that needs to be updated too */
\r
2521 DrawPieceOnDC(HDC hdc, ChessSquare piece, int color, int sqcolor, int x, int y, HDC tmphdc)
\r
2523 HBITMAP oldBitmap;
\r
2527 if (appData.blindfold) return;
\r
2529 /* [AS] Use font-based pieces if needed */
\r
2530 if( fontBitmapSquareSize >= 0 && (squareSize > 32 || gameInfo.variant >= VariantShogi)) {
\r
2531 /* Create piece bitmaps, or do nothing if piece set is up to date */
\r
2532 CreatePiecesFromFont();
\r
2534 if( fontBitmapSquareSize == squareSize ) {
\r
2535 int index = TranslatePieceToFontPiece(piece);
\r
2537 SelectObject( tmphdc, hPieceMask[ index ] );
\r
2539 if(appData.upsideDown ? color==flipView : (flipView && gameInfo.variant == VariantShogi))
\r
2540 StretchBlt(hdc, x+squareSize, y+squareSize, -squareSize, -squareSize, tmphdc, 0, 0, squareSize, squareSize, SRCAND);
\r
2544 squareSize, squareSize,
\r
2549 SelectObject( tmphdc, hPieceFace[ index ] );
\r
2551 if(appData.upsideDown ? color==flipView : (flipView && gameInfo.variant == VariantShogi))
\r
2552 StretchBlt(hdc, x+squareSize, y+squareSize, -squareSize, -squareSize, tmphdc, 0, 0, squareSize, squareSize, SRCPAINT);
\r
2556 squareSize, squareSize,
\r
2565 if (appData.monoMode) {
\r
2566 SelectObject(tmphdc, PieceBitmap(piece,
\r
2567 color == sqcolor ? OUTLINE_PIECE : SOLID_PIECE));
\r
2568 BitBlt(hdc, x, y, squareSize, squareSize, tmphdc, 0, 0,
\r
2569 sqcolor ? SRCCOPY : NOTSRCCOPY);
\r
2571 tmpSize = squareSize;
\r
2573 ((piece >= (int)WhiteNightrider && piece <= WhiteGrasshopper) ||
\r
2574 (piece >= (int)BlackNightrider && piece <= BlackGrasshopper)) ) {
\r
2575 /* [HGM] no bitmap available for promoted pieces in Crazyhouse */
\r
2576 /* Bitmaps of smaller size are substituted, but we have to align them */
\r
2577 x += (squareSize - minorSize)>>1;
\r
2578 y += squareSize - minorSize - 2;
\r
2579 tmpSize = minorSize;
\r
2581 if (color || appData.allWhite ) {
\r
2582 oldBitmap = SelectObject(tmphdc, PieceBitmap(piece, WHITE_PIECE));
\r
2584 oldBrush = SelectObject(hdc, whitePieceBrush);
\r
2585 else oldBrush = SelectObject(hdc, blackPieceBrush);
\r
2586 if(appData.upsideDown && color==flipView)
\r
2587 StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, 0x00B8074A);
\r
2589 BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, 0x00B8074A);
\r
2590 /* Use black for outline of white pieces */
\r
2591 SelectObject(tmphdc, PieceBitmap(piece, OUTLINE_PIECE));
\r
2592 if(appData.upsideDown && color==flipView)
\r
2593 StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, SRCAND);
\r
2595 BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, SRCAND);
\r
2597 /* Use square color for details of black pieces */
\r
2598 oldBitmap = SelectObject(tmphdc, PieceBitmap(piece, SOLID_PIECE));
\r
2599 oldBrush = SelectObject(hdc, blackPieceBrush);
\r
2600 if(appData.upsideDown && !flipView)
\r
2601 StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, 0x00B8074A);
\r
2603 BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, 0x00B8074A);
\r
2605 SelectObject(hdc, oldBrush);
\r
2606 SelectObject(tmphdc, oldBitmap);
\r
2610 /* [AS] Compute a drawing mode for a square, based on specified settings (see DrawTile) */
\r
2611 int GetBackTextureMode( int algo )
\r
2613 int result = BACK_TEXTURE_MODE_DISABLED;
\r
2617 case BACK_TEXTURE_MODE_PLAIN:
\r
2618 result = 1; /* Always use identity map */
\r
2620 case BACK_TEXTURE_MODE_FULL_RANDOM:
\r
2621 result = 1 + (myrandom() % 3); /* Pick a transformation at random */
\r
2629 [AS] Compute and save texture drawing info, otherwise we may not be able
\r
2630 to handle redraws cleanly (as random numbers would always be different).
\r
2632 VOID RebuildTextureSquareInfo()
\r
2642 ZeroMemory( &backTextureSquareInfo, sizeof(backTextureSquareInfo) );
\r
2644 if( liteBackTexture != NULL ) {
\r
2645 if( GetObject( liteBackTexture, sizeof(bi), &bi ) > 0 ) {
\r
2646 lite_w = bi.bmWidth;
\r
2647 lite_h = bi.bmHeight;
\r
2651 if( darkBackTexture != NULL ) {
\r
2652 if( GetObject( darkBackTexture, sizeof(bi), &bi ) > 0 ) {
\r
2653 dark_w = bi.bmWidth;
\r
2654 dark_h = bi.bmHeight;
\r
2658 for( row=0; row<BOARD_HEIGHT; row++ ) {
\r
2659 for( col=0; col<BOARD_WIDTH; col++ ) {
\r
2660 if( (col + row) & 1 ) {
\r
2662 if( lite_w >= squareSize && lite_h >= squareSize ) {
\r
2663 if( lite_w >= squareSize*BOARD_WIDTH )
\r
2664 backTextureSquareInfo[row][col].x = (2*col+1)*lite_w/(2*BOARD_WIDTH) - squareSize/2; /* [HGM] cut out of center of virtual square */
\r
2666 backTextureSquareInfo[row][col].x = col * (lite_w - squareSize) / (BOARD_WIDTH-1); /* [HGM] divide by size-1 in stead of size! */
\r
2667 if( lite_h >= squareSize*BOARD_HEIGHT )
\r
2668 backTextureSquareInfo[row][col].y = (2*(BOARD_HEIGHT-row)-1)*lite_h/(2*BOARD_HEIGHT) - squareSize/2;
\r
2670 backTextureSquareInfo[row][col].y = (BOARD_HEIGHT-1-row) * (lite_h - squareSize) / (BOARD_HEIGHT-1);
\r
2671 backTextureSquareInfo[row][col].mode = GetBackTextureMode(liteBackTextureMode);
\r
2676 if( dark_w >= squareSize && dark_h >= squareSize ) {
\r
2677 if( dark_w >= squareSize*BOARD_WIDTH )
\r
2678 backTextureSquareInfo[row][col].x = (2*col+1) * dark_w / (2*BOARD_WIDTH) - squareSize/2;
\r
2680 backTextureSquareInfo[row][col].x = col * (dark_w - squareSize) / (BOARD_WIDTH-1);
\r
2681 if( dark_h >= squareSize*BOARD_HEIGHT )
\r
2682 backTextureSquareInfo[row][col].y = (2*(BOARD_HEIGHT-row)-1) * dark_h / (2*BOARD_HEIGHT) - squareSize/2;
\r
2684 backTextureSquareInfo[row][col].y = (BOARD_HEIGHT-1-row) * (dark_h - squareSize) / (BOARD_HEIGHT-1);
\r
2685 backTextureSquareInfo[row][col].mode = GetBackTextureMode(darkBackTextureMode);
\r
2692 /* [AS] Arrow highlighting support */
\r
2694 static int A_WIDTH = 5; /* Width of arrow body */
\r
2696 #define A_HEIGHT_FACTOR 6 /* Length of arrow "point", relative to body width */
\r
2697 #define A_WIDTH_FACTOR 3 /* Width of arrow "point", relative to body width */
\r
2699 static double Sqr( double x )
\r
2704 static int Round( double x )
\r
2706 return (int) (x + 0.5);
\r
2709 /* Draw an arrow between two points using current settings */
\r
2710 VOID DrawArrowBetweenPoints( HDC hdc, int s_x, int s_y, int d_x, int d_y )
\r
2713 double dx, dy, j, k, x, y;
\r
2715 if( d_x == s_x ) {
\r
2716 int h = (d_y > s_y) ? +A_WIDTH*A_HEIGHT_FACTOR : -A_WIDTH*A_HEIGHT_FACTOR;
\r
2718 arrow[0].x = s_x + A_WIDTH;
\r
2721 arrow[1].x = s_x + A_WIDTH;
\r
2722 arrow[1].y = d_y - h;
\r
2724 arrow[2].x = s_x + A_WIDTH*A_WIDTH_FACTOR;
\r
2725 arrow[2].y = d_y - h;
\r
2730 arrow[4].x = s_x - A_WIDTH*A_WIDTH_FACTOR;
\r
2731 arrow[4].y = d_y - h;
\r
2733 arrow[5].x = s_x - A_WIDTH;
\r
2734 arrow[5].y = d_y - h;
\r
2736 arrow[6].x = s_x - A_WIDTH;
\r
2739 else if( d_y == s_y ) {
\r
2740 int w = (d_x > s_x) ? +A_WIDTH*A_HEIGHT_FACTOR : -A_WIDTH*A_HEIGHT_FACTOR;
\r
2743 arrow[0].y = s_y + A_WIDTH;
\r
2745 arrow[1].x = d_x - w;
\r
2746 arrow[1].y = s_y + A_WIDTH;
\r
2748 arrow[2].x = d_x - w;
\r
2749 arrow[2].y = s_y + A_WIDTH*A_WIDTH_FACTOR;
\r
2754 arrow[4].x = d_x - w;
\r
2755 arrow[4].y = s_y - A_WIDTH*A_WIDTH_FACTOR;
\r
2757 arrow[5].x = d_x - w;
\r
2758 arrow[5].y = s_y - A_WIDTH;
\r
2761 arrow[6].y = s_y - A_WIDTH;
\r
2764 /* [AS] Needed a lot of paper for this! :-) */
\r
2765 dy = (double) (d_y - s_y) / (double) (d_x - s_x);
\r
2766 dx = (double) (s_x - d_x) / (double) (s_y - d_y);
\r
2768 j = sqrt( Sqr(A_WIDTH) / (1.0 + Sqr(dx)) );
\r
2770 k = sqrt( Sqr(A_WIDTH*A_HEIGHT_FACTOR) / (1.0 + Sqr(dy)) );
\r
2775 arrow[0].x = Round(x - j);
\r
2776 arrow[0].y = Round(y + j*dx);
\r
2778 arrow[1].x = Round(x + j);
\r
2779 arrow[1].y = Round(y - j*dx);
\r
2782 x = (double) d_x - k;
\r
2783 y = (double) d_y - k*dy;
\r
2786 x = (double) d_x + k;
\r
2787 y = (double) d_y + k*dy;
\r
2790 arrow[2].x = Round(x + j);
\r
2791 arrow[2].y = Round(y - j*dx);
\r
2793 arrow[3].x = Round(x + j*A_WIDTH_FACTOR);
\r
2794 arrow[3].y = Round(y - j*A_WIDTH_FACTOR*dx);
\r
2799 arrow[5].x = Round(x - j*A_WIDTH_FACTOR);
\r
2800 arrow[5].y = Round(y + j*A_WIDTH_FACTOR*dx);
\r
2802 arrow[6].x = Round(x - j);
\r
2803 arrow[6].y = Round(y + j*dx);
\r
2806 Polygon( hdc, arrow, 7 );
\r
2809 /* [AS] Draw an arrow between two squares */
\r
2810 VOID DrawArrowBetweenSquares( HDC hdc, int s_col, int s_row, int d_col, int d_row )
\r
2812 int s_x, s_y, d_x, d_y;
\r
2819 if( s_col == d_col && s_row == d_row ) {
\r
2823 /* Get source and destination points */
\r
2824 SquareToPos( s_row, s_col, &s_x, &s_y);
\r
2825 SquareToPos( d_row, d_col, &d_x, &d_y);
\r
2828 d_y += squareSize / 4;
\r
2830 else if( d_y < s_y ) {
\r
2831 d_y += 3 * squareSize / 4;
\r
2834 d_y += squareSize / 2;
\r
2838 d_x += squareSize / 4;
\r
2840 else if( d_x < s_x ) {
\r
2841 d_x += 3 * squareSize / 4;
\r
2844 d_x += squareSize / 2;
\r
2847 s_x += squareSize / 2;
\r
2848 s_y += squareSize / 2;
\r
2850 /* Adjust width */
\r
2851 A_WIDTH = squareSize / 14;
\r
2854 stLB.lbStyle = BS_SOLID;
\r
2855 stLB.lbColor = appData.highlightArrowColor;
\r
2858 hpen = CreatePen( PS_SOLID, 2, RGB(0x00,0x00,0x00) );
\r
2859 holdpen = SelectObject( hdc, hpen );
\r
2860 hbrush = CreateBrushIndirect( &stLB );
\r
2861 holdbrush = SelectObject( hdc, hbrush );
\r
2863 DrawArrowBetweenPoints( hdc, s_x, s_y, d_x, d_y );
\r
2865 SelectObject( hdc, holdpen );
\r
2866 SelectObject( hdc, holdbrush );
\r
2867 DeleteObject( hpen );
\r
2868 DeleteObject( hbrush );
\r
2871 BOOL HasHighlightInfo()
\r
2873 BOOL result = FALSE;
\r
2875 if( highlightInfo.sq[0].x >= 0 && highlightInfo.sq[0].y >= 0 &&
\r
2876 highlightInfo.sq[1].x >= 0 && highlightInfo.sq[1].y >= 0 )
\r
2884 BOOL IsDrawArrowEnabled()
\r
2886 BOOL result = FALSE;
\r
2888 if( appData.highlightMoveWithArrow && squareSize >= 32 ) {
\r
2895 VOID DrawArrowHighlight( HDC hdc )
\r
2897 if( IsDrawArrowEnabled() && HasHighlightInfo() ) {
\r
2898 DrawArrowBetweenSquares( hdc,
\r
2899 highlightInfo.sq[0].x, highlightInfo.sq[0].y,
\r
2900 highlightInfo.sq[1].x, highlightInfo.sq[1].y );
\r
2904 HRGN GetArrowHighlightClipRegion( HDC hdc )
\r
2906 HRGN result = NULL;
\r
2908 if( HasHighlightInfo() ) {
\r
2909 int x1, y1, x2, y2;
\r
2910 int sx, sy, dx, dy;
\r
2912 SquareToPos(highlightInfo.sq[0].y, highlightInfo.sq[0].x, &x1, &y1 );
\r
2913 SquareToPos(highlightInfo.sq[1].y, highlightInfo.sq[1].x, &x2, &y2 );
\r
2915 sx = MIN( x1, x2 );
\r
2916 sy = MIN( y1, y2 );
\r
2917 dx = MAX( x1, x2 ) + squareSize;
\r
2918 dy = MAX( y1, y2 ) + squareSize;
\r
2920 result = CreateRectRgn( sx, sy, dx, dy );
\r
2927 Warning: this function modifies the behavior of several other functions.
\r
2929 Basically, Winboard is optimized to avoid drawing the whole board if not strictly
\r
2930 needed. Unfortunately, the decision whether or not to perform a full or partial
\r
2931 repaint is scattered all over the place, which is not good for features such as
\r
2932 "arrow highlighting" that require a full repaint of the board.
\r
2934 So, I've tried to patch the code where I thought it made sense (e.g. after or during
\r
2935 user interaction, when speed is not so important) but especially to avoid errors
\r
2936 in the displayed graphics.
\r
2938 In such patched places, I always try refer to this function so there is a single
\r
2939 place to maintain knowledge.
\r
2941 To restore the original behavior, just return FALSE unconditionally.
\r
2943 BOOL IsFullRepaintPreferrable()
\r
2945 BOOL result = FALSE;
\r
2947 if( (appData.highlightLastMove || appData.highlightDragging) && IsDrawArrowEnabled() ) {
\r
2948 /* Arrow may appear on the board */
\r
2956 This function is called by DrawPosition to know whether a full repaint must
\r
2959 Only DrawPosition may directly call this function, which makes use of
\r
2960 some state information. Other function should call DrawPosition specifying
\r
2961 the repaint flag, and can use IsFullRepaintPreferrable if needed.
\r
2963 BOOL DrawPositionNeedsFullRepaint()
\r
2965 BOOL result = FALSE;
\r
2968 Probably a slightly better policy would be to trigger a full repaint
\r
2969 when animInfo.piece changes state (i.e. empty -> non-empty and viceversa),
\r
2970 but animation is fast enough that it's difficult to notice.
\r
2972 if( animInfo.piece == EmptySquare ) {
\r
2973 if( (appData.highlightLastMove || appData.highlightDragging) && IsDrawArrowEnabled() /*&& HasHighlightInfo()*/ ) {
\r
2982 DrawBoardOnDC(HDC hdc, Board board, HDC tmphdc)
\r
2984 int row, column, x, y, square_color, piece_color;
\r
2985 ChessSquare piece;
\r
2987 HDC texture_hdc = NULL;
\r
2989 /* [AS] Initialize background textures if needed */
\r
2990 if( liteBackTexture != NULL || darkBackTexture != NULL ) {
\r
2991 static int backTextureBoardSize; /* [HGM] boardsize: also new texture if board format changed */
\r
2992 if( backTextureSquareSize != squareSize
\r
2993 || backTextureBoardSize != BOARD_WIDTH+BOARD_FILES*BOARD_HEIGHT) {
\r
2994 backTextureBoardSize = BOARD_WIDTH+BOARD_FILES*BOARD_HEIGHT;
\r
2995 backTextureSquareSize = squareSize;
\r
2996 RebuildTextureSquareInfo();
\r
2999 texture_hdc = CreateCompatibleDC( hdc );
\r
3002 for (row = 0; row < BOARD_HEIGHT; row++) {
\r
3003 for (column = 0; column < BOARD_WIDTH; column++) {
\r
3005 SquareToPos(row, column, &x, &y);
\r
3007 piece = board[row][column];
\r
3009 square_color = ((column + row) % 2) == 1;
\r
3010 if( gameInfo.variant == VariantXiangqi ) {
\r
3011 square_color = !InPalace(row, column);
\r
3012 if(BOARD_HEIGHT&1) { if(row==BOARD_HEIGHT/2) square_color ^= 1; }
\r
3013 else if(row < BOARD_HEIGHT/2) square_color ^= 1;
\r
3015 piece_color = (int) piece < (int) BlackPawn;
\r
3018 /* [HGM] holdings file: light square or black */
\r
3019 if(column == BOARD_LEFT-2) {
\r
3020 if( row > BOARD_HEIGHT - gameInfo.holdingsSize - 1 )
\r
3023 DisplayHoldingsCount(hdc, x, y, 0, 0); /* black out */
\r
3027 if(column == BOARD_RGHT + 1 ) {
\r
3028 if( row < gameInfo.holdingsSize )
\r
3031 DisplayHoldingsCount(hdc, x, y, 0, 0);
\r
3035 if(column == BOARD_LEFT-1 ) /* left align */
\r
3036 DisplayHoldingsCount(hdc, x, y, flipView, (int) board[row][column]);
\r
3037 else if( column == BOARD_RGHT) /* right align */
\r
3038 DisplayHoldingsCount(hdc, x, y, !flipView, (int) board[row][column]);
\r
3040 if (appData.monoMode) {
\r
3041 if (piece == EmptySquare) {
\r
3042 BitBlt(hdc, x, y, squareSize, squareSize, 0, 0, 0,
\r
3043 square_color ? WHITENESS : BLACKNESS);
\r
3045 DrawPieceOnDC(hdc, piece, piece_color, square_color, x, y, tmphdc);
\r
3048 else if( backTextureSquareInfo[row][column].mode > 0 ) {
\r
3049 /* [AS] Draw the square using a texture bitmap */
\r
3050 HBITMAP hbm = SelectObject( texture_hdc, square_color ? liteBackTexture : darkBackTexture );
\r
3051 int r = row, c = column; // [HGM] do not flip board in flipView
\r
3052 if(flipView) { r = BOARD_HEIGHT-1 - r; c = BOARD_WIDTH-1 - c; }
\r
3055 squareSize, squareSize,
\r
3058 backTextureSquareInfo[r][c].mode,
\r
3059 backTextureSquareInfo[r][c].x,
\r
3060 backTextureSquareInfo[r][c].y );
\r
3062 SelectObject( texture_hdc, hbm );
\r
3064 if (piece != EmptySquare) {
\r
3065 DrawPieceOnDC(hdc, piece, piece_color, -1, x, y, tmphdc);
\r
3069 HBRUSH brush = square_color ? lightSquareBrush : darkSquareBrush;
\r
3071 oldBrush = SelectObject(hdc, brush );
\r
3072 BitBlt(hdc, x, y, squareSize, squareSize, 0, 0, 0, PATCOPY);
\r
3073 SelectObject(hdc, oldBrush);
\r
3074 if (piece != EmptySquare)
\r
3075 DrawPieceOnDC(hdc, piece, piece_color, -1, x, y, tmphdc);
\r
3080 if( texture_hdc != NULL ) {
\r
3081 DeleteDC( texture_hdc );
\r
3085 int saveDiagFlag = 0; FILE *diagFile; // [HGM] diag
\r
3086 void fputDW(FILE *f, int x)
\r
3088 fputc(x & 255, f);
\r
3089 fputc(x>>8 & 255, f);
\r
3090 fputc(x>>16 & 255, f);
\r
3091 fputc(x>>24 & 255, f);
\r
3094 #define MAX_CLIPS 200 /* more than enough */
\r
3097 DrawLogoOnDC(HDC hdc, RECT logoRect, HBITMAP logo)
\r
3099 // HBITMAP bufferBitmap;
\r
3104 int w = 100, h = 50;
\r
3106 if(logo == NULL) return;
\r
3107 // GetClientRect(hwndMain, &Rect);
\r
3108 // bufferBitmap = CreateCompatibleBitmap(hdc, Rect.right-Rect.left+1,
\r
3109 // Rect.bottom-Rect.top+1);
\r
3110 tmphdc = CreateCompatibleDC(hdc);
\r
3111 hbm = SelectObject(tmphdc, logo);
\r
3112 if( GetObject( logo, sizeof(bi), &bi ) > 0 ) {
\r
3116 StretchBlt(hdc, logoRect.left, logoRect.top, logoRect.right - logoRect.left,
\r
3117 logoRect.bottom - logoRect.top, tmphdc, 0, 0, w, h, SRCCOPY);
\r
3118 SelectObject(tmphdc, hbm);
\r
3122 static HDC hdcSeek;
\r
3124 // [HGM] seekgraph
\r
3125 void DrawSeekAxis( int x, int y, int xTo, int yTo )
\r
3128 HPEN hp = SelectObject( hdcSeek, gridPen );
\r
3129 MoveToEx( hdcSeek, boardRect.left+x, boardRect.top+y, &stPt );
\r
3130 LineTo( hdcSeek, boardRect.left+xTo, boardRect.top+yTo );
\r
3131 SelectObject( hdcSeek, hp );
\r
3134 // front-end wrapper for drawing functions to do rectangles
\r
3135 void DrawSeekBackground( int left, int top, int right, int bottom )
\r
3140 if (hdcSeek == NULL) {
\r
3141 hdcSeek = GetDC(hwndMain);
\r
3142 if (!appData.monoMode) {
\r
3143 SelectPalette(hdcSeek, hPal, FALSE);
\r
3144 RealizePalette(hdcSeek);
\r
3147 hp = SelectObject( hdcSeek, gridPen );
\r
3148 rc.top = boardRect.top+top; rc.left = boardRect.left+left;
\r
3149 rc.bottom = boardRect.top+bottom; rc.right = boardRect.left+right;
\r
3150 FillRect( hdcSeek, &rc, lightSquareBrush );
\r
3151 SelectObject( hdcSeek, hp );
\r
3154 // front-end wrapper for putting text in graph
\r
3155 void DrawSeekText(char *buf, int x, int y)
\r
3158 SetBkMode( hdcSeek, TRANSPARENT );
\r
3159 GetTextExtentPoint32( hdcSeek, buf, strlen(buf), &stSize );
\r
3160 TextOut( hdcSeek, boardRect.left+x-3, boardRect.top+y-stSize.cy/2, buf, strlen(buf) );
\r
3163 void DrawSeekDot(int x, int y, int color)
\r
3165 int square = color & 0x80;
\r
3166 HBRUSH oldBrush = SelectObject(hdcSeek,
\r
3167 color == 0 ? markerBrush : color == 1 ? darkSquareBrush : explodeBrush);
\r
3170 Rectangle(hdcSeek, boardRect.left+x - squareSize/9, boardRect.top+y - squareSize/9,
\r
3171 boardRect.left+x + squareSize/9, boardRect.top+y + squareSize/9);
\r
3173 Ellipse(hdcSeek, boardRect.left+x - squareSize/8, boardRect.top+y - squareSize/8,
\r
3174 boardRect.left+x + squareSize/8, boardRect.top+y + squareSize/8);
\r
3175 SelectObject(hdcSeek, oldBrush);
\r
3179 HDCDrawPosition(HDC hdc, BOOLEAN repaint, Board board)
\r
3181 static Board lastReq[2], lastDrawn[2];
\r
3182 static HighlightInfo lastDrawnHighlight, lastDrawnPremove;
\r
3183 static int lastDrawnFlipView = 0;
\r
3184 static int lastReqValid[2] = {0, 0}, lastDrawnValid[2] = {0, 0};
\r
3185 int releaseDC, x, y, x2, y2, row, column, num_clips = 0, i;
\r
3188 HBITMAP bufferBitmap;
\r
3189 HBITMAP oldBitmap;
\r
3191 HRGN clips[MAX_CLIPS];
\r
3192 ChessSquare dragged_piece = EmptySquare;
\r
3193 int nr = twoBoards*partnerUp;
\r
3195 /* I'm undecided on this - this function figures out whether a full
\r
3196 * repaint is necessary on its own, so there's no real reason to have the
\r
3197 * caller tell it that. I think this can safely be set to FALSE - but
\r
3198 * if we trust the callers not to request full repaints unnessesarily, then
\r
3199 * we could skip some clipping work. In other words, only request a full
\r
3200 * redraw when the majority of pieces have changed positions (ie. flip,
\r
3201 * gamestart and similar) --Hawk
\r
3203 Boolean fullrepaint = repaint;
\r
3205 if(DrawSeekGraph()) return; // [HG} seekgraph: suppress printing board if seek graph up
\r
3207 if( DrawPositionNeedsFullRepaint() ) {
\r
3208 fullrepaint = TRUE;
\r
3211 if (board == NULL) {
\r
3212 if (!lastReqValid[nr]) {
\r
3215 board = lastReq[nr];
\r
3217 CopyBoard(lastReq[nr], board);
\r
3218 lastReqValid[nr] = 1;
\r
3221 if (doingSizing) {
\r
3225 if (IsIconic(hwndMain)) {
\r
3229 if (hdc == NULL) {
\r
3230 hdc = GetDC(hwndMain);
\r
3231 if (!appData.monoMode) {
\r
3232 SelectPalette(hdc, hPal, FALSE);
\r
3233 RealizePalette(hdc);
\r
3237 releaseDC = FALSE;
\r
3240 /* Create some work-DCs */
\r
3241 hdcmem = CreateCompatibleDC(hdc);
\r
3242 tmphdc = CreateCompatibleDC(hdc);
\r
3244 /* If dragging is in progress, we temporarely remove the piece */
\r
3245 /* [HGM] or temporarily decrease count if stacked */
\r
3246 /* !! Moved to before board compare !! */
\r
3247 if (dragInfo.from.x >= 0 && dragInfo.pos.x >= 0) {
\r
3248 dragged_piece = board[dragInfo.from.y][dragInfo.from.x];
\r
3249 if(dragInfo.from.x == BOARD_LEFT-2 ) {
\r
3250 if(--board[dragInfo.from.y][dragInfo.from.x+1] == 0 )
\r
3251 board[dragInfo.from.y][dragInfo.from.x] = EmptySquare;
\r
3253 if(dragInfo.from.x == BOARD_RGHT+1) {
\r
3254 if(--board[dragInfo.from.y][dragInfo.from.x-1] == 0 )
\r
3255 board[dragInfo.from.y][dragInfo.from.x] = EmptySquare;
\r
3257 board[dragInfo.from.y][dragInfo.from.x] = EmptySquare;
\r
3260 /* Figure out which squares need updating by comparing the
\r
3261 * newest board with the last drawn board and checking if
\r
3262 * flipping has changed.
\r
3264 if (!fullrepaint && lastDrawnValid[nr] && (nr == 1 || lastDrawnFlipView == flipView)) {
\r
3265 for (row = 0; row < BOARD_HEIGHT; row++) { /* [HGM] true size, not 8 */
\r
3266 for (column = 0; column < BOARD_WIDTH; column++) {
\r
3267 if (lastDrawn[nr][row][column] != board[row][column]) {
\r
3268 SquareToPos(row, column, &x, &y);
\r
3269 clips[num_clips++] =
\r
3270 CreateRectRgn(x, y, x + squareSize, y + squareSize);
\r
3274 if(nr == 0) { // [HGM] dual: no highlights on second board
\r
3275 for (i=0; i<2; i++) {
\r
3276 if (lastDrawnHighlight.sq[i].x != highlightInfo.sq[i].x ||
\r
3277 lastDrawnHighlight.sq[i].y != highlightInfo.sq[i].y) {
\r
3278 if (lastDrawnHighlight.sq[i].x >= 0 &&
\r
3279 lastDrawnHighlight.sq[i].y >= 0) {
\r
3280 SquareToPos(lastDrawnHighlight.sq[i].y,
\r
3281 lastDrawnHighlight.sq[i].x, &x, &y);
\r
3282 clips[num_clips++] =
\r
3283 CreateRectRgn(x - lineGap, y - lineGap,
\r
3284 x + squareSize + lineGap, y + squareSize + lineGap);
\r
3286 if (highlightInfo.sq[i].x >= 0 && highlightInfo.sq[i].y >= 0) {
\r
3287 SquareToPos(highlightInfo.sq[i].y, highlightInfo.sq[i].x, &x, &y);
\r
3288 clips[num_clips++] =
\r
3289 CreateRectRgn(x - lineGap, y - lineGap,
\r
3290 x + squareSize + lineGap, y + squareSize + lineGap);
\r
3294 for (i=0; i<2; i++) {
\r
3295 if (lastDrawnPremove.sq[i].x != premoveHighlightInfo.sq[i].x ||
\r
3296 lastDrawnPremove.sq[i].y != premoveHighlightInfo.sq[i].y) {
\r
3297 if (lastDrawnPremove.sq[i].x >= 0 &&
\r
3298 lastDrawnPremove.sq[i].y >= 0) {
\r
3299 SquareToPos(lastDrawnPremove.sq[i].y,
\r
3300 lastDrawnPremove.sq[i].x, &x, &y);
\r
3301 clips[num_clips++] =
\r
3302 CreateRectRgn(x - lineGap, y - lineGap,
\r
3303 x + squareSize + lineGap, y + squareSize + lineGap);
\r
3305 if (premoveHighlightInfo.sq[i].x >= 0 &&
\r
3306 premoveHighlightInfo.sq[i].y >= 0) {
\r
3307 SquareToPos(premoveHighlightInfo.sq[i].y,
\r
3308 premoveHighlightInfo.sq[i].x, &x, &y);
\r
3309 clips[num_clips++] =
\r
3310 CreateRectRgn(x - lineGap, y - lineGap,
\r
3311 x + squareSize + lineGap, y + squareSize + lineGap);
\r
3315 } else { // nr == 1
\r
3316 partnerHighlightInfo.sq[0].y = board[EP_STATUS-4];
\r
3317 partnerHighlightInfo.sq[0].x = board[EP_STATUS-3];
\r
3318 partnerHighlightInfo.sq[1].y = board[EP_STATUS-2];
\r
3319 partnerHighlightInfo.sq[1].x = board[EP_STATUS-1];
\r
3320 for (i=0; i<2; i++) {
\r
3321 if (partnerHighlightInfo.sq[i].x >= 0 &&
\r
3322 partnerHighlightInfo.sq[i].y >= 0) {
\r
3323 SquareToPos(partnerHighlightInfo.sq[i].y,
\r
3324 partnerHighlightInfo.sq[i].x, &x, &y);
\r
3325 clips[num_clips++] =
\r
3326 CreateRectRgn(x - lineGap, y - lineGap,
\r
3327 x + squareSize + lineGap, y + squareSize + lineGap);
\r
3329 if (oldPartnerHighlight.sq[i].x >= 0 &&
\r
3330 oldPartnerHighlight.sq[i].y >= 0) {
\r
3331 SquareToPos(oldPartnerHighlight.sq[i].y,
\r
3332 oldPartnerHighlight.sq[i].x, &x, &y);
\r
3333 clips[num_clips++] =
\r
3334 CreateRectRgn(x - lineGap, y - lineGap,
\r
3335 x + squareSize + lineGap, y + squareSize + lineGap);
\r
3340 fullrepaint = TRUE;
\r
3343 /* Create a buffer bitmap - this is the actual bitmap
\r
3344 * being written to. When all the work is done, we can
\r
3345 * copy it to the real DC (the screen). This avoids
\r
3346 * the problems with flickering.
\r
3348 GetClientRect(hwndMain, &Rect);
\r
3349 bufferBitmap = CreateCompatibleBitmap(hdc, Rect.right-Rect.left+1,
\r
3350 Rect.bottom-Rect.top+1);
\r
3351 oldBitmap = SelectObject(hdcmem, bufferBitmap);
\r
3352 if (!appData.monoMode) {
\r
3353 SelectPalette(hdcmem, hPal, FALSE);
\r
3356 /* Create clips for dragging */
\r
3357 if (!fullrepaint) {
\r
3358 if (dragInfo.from.x >= 0) {
\r
3359 SquareToPos(dragInfo.from.y, dragInfo.from.x, &x, &y);
\r
3360 clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);
\r
3362 if (dragInfo.start.x >= 0) {
\r
3363 SquareToPos(dragInfo.start.y, dragInfo.start.x, &x, &y);
\r
3364 clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);
\r
3366 if (dragInfo.pos.x >= 0) {
\r
3367 x = dragInfo.pos.x - squareSize / 2;
\r
3368 y = dragInfo.pos.y - squareSize / 2;
\r
3369 clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);
\r
3371 if (dragInfo.lastpos.x >= 0) {
\r
3372 x = dragInfo.lastpos.x - squareSize / 2;
\r
3373 y = dragInfo.lastpos.y - squareSize / 2;
\r
3374 clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);
\r
3378 /* Are we animating a move?
\r
3380 * - remove the piece from the board (temporarely)
\r
3381 * - calculate the clipping region
\r
3383 if (!fullrepaint) {
\r
3384 if (animInfo.piece != EmptySquare) {
\r
3385 board[animInfo.from.y][animInfo.from.x] = EmptySquare;
\r
3386 x = boardRect.left + animInfo.lastpos.x;
\r
3387 y = boardRect.top + animInfo.lastpos.y;
\r
3388 x2 = boardRect.left + animInfo.pos.x;
\r
3389 y2 = boardRect.top + animInfo.pos.y;
\r
3390 clips[num_clips++] = CreateRectRgn(MIN(x,x2), MIN(y,y2), MAX(x,x2)+squareSize, MAX(y,y2)+squareSize);
\r
3391 /* Slight kludge. The real problem is that after AnimateMove is
\r
3392 done, the position on the screen does not match lastDrawn.
\r
3393 This currently causes trouble only on e.p. captures in
\r
3394 atomic, where the piece moves to an empty square and then
\r
3395 explodes. The old and new positions both had an empty square
\r
3396 at the destination, but animation has drawn a piece there and
\r
3397 we have to remember to erase it. [HGM] moved until after setting lastDrawn */
\r
3398 lastDrawn[0][animInfo.to.y][animInfo.to.x] = animInfo.piece;
\r
3402 /* No clips? Make sure we have fullrepaint set to TRUE */
\r
3403 if (num_clips == 0)
\r
3404 fullrepaint = TRUE;
\r
3406 /* Set clipping on the memory DC */
\r
3407 if (!fullrepaint) {
\r
3408 SelectClipRgn(hdcmem, clips[0]);
\r
3409 for (x = 1; x < num_clips; x++) {
\r
3410 if (ExtSelectClipRgn(hdcmem, clips[x], RGN_OR) == ERROR)
\r
3411 abort(); // this should never ever happen!
\r
3415 /* Do all the drawing to the memory DC */
\r
3416 if(explodeInfo.radius) { // [HGM] atomic
\r
3418 int x, y, r=(explodeInfo.radius * squareSize)/100;
\r
3419 board[explodeInfo.fromY][explodeInfo.fromX] = EmptySquare; // suppress display of capturer
\r
3420 SquareToPos(explodeInfo.toY, explodeInfo.toX, &x, &y);
\r
3421 x += squareSize/2;
\r
3422 y += squareSize/2;
\r
3423 if(!fullrepaint) {
\r
3424 clips[num_clips] = CreateRectRgn(x-r, y-r, x+r, y+r);
\r
3425 ExtSelectClipRgn(hdcmem, clips[num_clips++], RGN_OR);
\r
3427 DrawGridOnDC(hdcmem);
\r
3428 DrawHighlightsOnDC(hdcmem, &highlightInfo, HIGHLIGHT_PEN);
\r
3429 DrawHighlightsOnDC(hdcmem, &premoveHighlightInfo, PREMOVE_PEN);
\r
3430 DrawBoardOnDC(hdcmem, board, tmphdc);
\r
3431 oldBrush = SelectObject(hdcmem, explodeBrush);
\r
3432 Ellipse(hdcmem, x-r, y-r, x+r, y+r);
\r
3433 SelectObject(hdcmem, oldBrush);
\r
3435 DrawGridOnDC(hdcmem);
\r
3436 if(nr == 0) { // [HGM] dual: decide which highlights to draw
\r
3437 DrawHighlightsOnDC(hdcmem, &highlightInfo, HIGHLIGHT_PEN);
\r
3438 DrawHighlightsOnDC(hdcmem, &premoveHighlightInfo, PREMOVE_PEN);
\r
3440 DrawHighlightsOnDC(hdcmem, &partnerHighlightInfo, HIGHLIGHT_PEN);
\r
3441 oldPartnerHighlight = partnerHighlightInfo;
\r
3443 DrawBoardOnDC(hdcmem, board, tmphdc);
\r
3445 if(nr == 0) // [HGM] dual: markers only on left board
\r
3446 for (row = 0; row < BOARD_HEIGHT; row++) {
\r
3447 for (column = 0; column < BOARD_WIDTH; column++) {
\r
3448 if (marker[row][column]) { // marker changes only occur with full repaint!
\r
3449 HBRUSH oldBrush = SelectObject(hdcmem,
\r
3450 marker[row][column] == 2 ? markerBrush : explodeBrush);
\r
3451 SquareToPos(row, column, &x, &y);
\r
3452 Ellipse(hdcmem, x + squareSize/4, y + squareSize/4,
\r
3453 x + 3*squareSize/4, y + 3*squareSize/4);
\r
3454 SelectObject(hdcmem, oldBrush);
\r
3459 HBITMAP whiteLogo = (HBITMAP) first.programLogo, blackLogo = (HBITMAP) second.programLogo;
\r
3460 if(appData.autoLogo) {
\r
3462 switch(gameMode) { // pick logos based on game mode
\r
3463 case IcsObserving:
\r
3464 whiteLogo = second.programLogo; // ICS logo
\r
3465 blackLogo = second.programLogo;
\r
3468 case IcsPlayingWhite:
\r
3469 if(!appData.zippyPlay) whiteLogo = userLogo;
\r
3470 blackLogo = second.programLogo; // ICS logo
\r
3472 case IcsPlayingBlack:
\r
3473 whiteLogo = second.programLogo; // ICS logo
\r
3474 blackLogo = appData.zippyPlay ? first.programLogo : userLogo;
\r
3476 case TwoMachinesPlay:
\r
3477 if(first.twoMachinesColor[0] == 'b') {
\r
3478 whiteLogo = second.programLogo;
\r
3479 blackLogo = first.programLogo;
\r
3482 case MachinePlaysWhite:
\r
3483 blackLogo = userLogo;
\r
3485 case MachinePlaysBlack:
\r
3486 whiteLogo = userLogo;
\r
3487 blackLogo = first.programLogo;
\r
3490 DrawLogoOnDC(hdc, leftLogoRect, flipClock ? blackLogo : whiteLogo);
\r
3491 DrawLogoOnDC(hdc, rightLogoRect, flipClock ? whiteLogo : blackLogo);
\r
3494 if( appData.highlightMoveWithArrow ) {
\r
3495 DrawArrowHighlight(hdcmem);
\r
3498 DrawCoordsOnDC(hdcmem);
\r
3500 CopyBoard(lastDrawn[nr], board); /* [HGM] Moved to here from end of routine, */
\r
3501 /* to make sure lastDrawn contains what is actually drawn */
\r
3503 /* Put the dragged piece back into place and draw it (out of place!) */
\r
3504 if (dragged_piece != EmptySquare) {
\r
3505 /* [HGM] or restack */
\r
3506 if(dragInfo.from.x == BOARD_LEFT-2 )
\r
3507 board[dragInfo.from.y][dragInfo.from.x+1]++;
\r
3509 if(dragInfo.from.x == BOARD_RGHT+1 )
\r
3510 board[dragInfo.from.y][dragInfo.from.x-1]++;
\r
3511 board[dragInfo.from.y][dragInfo.from.x] = dragged_piece;
\r
3512 x = dragInfo.pos.x - squareSize / 2;
\r
3513 y = dragInfo.pos.y - squareSize / 2;
\r
3514 DrawPieceOnDC(hdcmem, dragged_piece,
\r
3515 ((int) dragged_piece < (int) BlackPawn),
\r
3516 (dragInfo.from.y + dragInfo.from.x) % 2, x, y, tmphdc);
\r
3519 /* Put the animated piece back into place and draw it */
\r
3520 if (animInfo.piece != EmptySquare) {
\r
3521 board[animInfo.from.y][animInfo.from.x] = animInfo.piece;
\r
3522 x = boardRect.left + animInfo.pos.x;
\r
3523 y = boardRect.top + animInfo.pos.y;
\r
3524 DrawPieceOnDC(hdcmem, animInfo.piece,
\r
3525 ((int) animInfo.piece < (int) BlackPawn),
\r
3526 (animInfo.from.y + animInfo.from.x) % 2, x, y, tmphdc);
\r
3529 /* Release the bufferBitmap by selecting in the old bitmap
\r
3530 * and delete the memory DC
\r
3532 SelectObject(hdcmem, oldBitmap);
\r
3535 /* Set clipping on the target DC */
\r
3536 if (!fullrepaint) {
\r
3537 if(nr == 1) for (x = 0; x < num_clips; x++) { // [HGM] dual: translate clips
\r
3539 GetRgnBox(clips[x], &rect);
\r
3540 DeleteObject(clips[x]);
\r
3541 clips[x] = CreateRectRgn(rect.left + wpMain.width/2, rect.top,
\r
3542 rect.right + wpMain.width/2, rect.bottom);
\r
3544 SelectClipRgn(hdc, clips[0]);
\r
3545 for (x = 1; x < num_clips; x++) {
\r
3546 if (ExtSelectClipRgn(hdc, clips[x], RGN_OR) == ERROR)
\r
3547 abort(); // this should never ever happen!
\r
3551 /* Copy the new bitmap onto the screen in one go.
\r
3552 * This way we avoid any flickering
\r
3554 oldBitmap = SelectObject(tmphdc, bufferBitmap);
\r
3555 BitBlt(hdc, boardRect.left + twoBoards*partnerUp*wpMain.width/2, boardRect.top, // [HGM] dual
\r
3556 boardRect.right - boardRect.left,
\r
3557 boardRect.bottom - boardRect.top,
\r
3558 tmphdc, boardRect.left, boardRect.top, SRCCOPY);
\r
3559 if(saveDiagFlag) {
\r
3560 BITMAP b; int i, j=0, m, w, wb, fac=0; char pData[1000000];
\r
3561 BITMAPINFOHEADER bih; int color[16], nrColors=0;
\r
3563 GetObject(bufferBitmap, sizeof(b), &b);
\r
3564 if(b.bmWidthBytes*b.bmHeight <= 990000) {
\r
3565 bih.biSize = sizeof(BITMAPINFOHEADER);
\r
3566 bih.biWidth = b.bmWidth;
\r
3567 bih.biHeight = b.bmHeight;
\r
3569 bih.biBitCount = b.bmBitsPixel;
\r
3570 bih.biCompression = 0;
\r
3571 bih.biSizeImage = b.bmWidthBytes*b.bmHeight;
\r
3572 bih.biXPelsPerMeter = 0;
\r
3573 bih.biYPelsPerMeter = 0;
\r
3574 bih.biClrUsed = 0;
\r
3575 bih.biClrImportant = 0;
\r
3576 // fprintf(diagFile, "t=%d\nw=%d\nh=%d\nB=%d\nP=%d\nX=%d\n",
\r
3577 // b.bmType, b.bmWidth, b.bmHeight, b.bmWidthBytes, b.bmPlanes, b.bmBitsPixel);
\r
3578 GetDIBits(tmphdc,bufferBitmap,0,b.bmHeight,pData,(BITMAPINFO*)&bih,DIB_RGB_COLORS);
\r
3579 // fprintf(diagFile, "%8x\n", (int) pData);
\r
3581 wb = b.bmWidthBytes;
\r
3583 for(i=0; i<wb*(b.bmHeight - boardRect.top + OUTER_MARGIN)>>2; i++) {
\r
3584 int k = ((int*) pData)[i];
\r
3585 for(j=0; j<nrColors; j++) if(color[j] == k) break;
\r
3586 if(j >= 16) break;
\r
3588 if(j >= nrColors) nrColors = j+1;
\r
3590 if(j<16) { // 16 colors is enough. Compress to 4 bits per pixel
\r
3592 for(i=0; i<b.bmHeight - boardRect.top + OUTER_MARGIN; i++) {
\r
3593 for(w=0; w<(wb>>2); w+=2) {
\r
3594 int k = ((int*) pData)[(wb*i>>2) + w];
\r
3595 for(j=0; j<nrColors; j++) if(color[j] == k) break;
\r
3596 k = ((int*) pData)[(wb*i>>2) + w + 1];
\r
3597 for(m=0; m<nrColors; m++) if(color[m] == k) break;
\r
3598 pData[p++] = m | j<<4;
\r
3600 while(p&3) pData[p++] = 0;
\r
3603 wb = ((wb+31)>>5)<<2;
\r
3605 // write BITMAPFILEHEADER
\r
3606 fprintf(diagFile, "BM");
\r
3607 fputDW(diagFile, wb*(b.bmHeight - boardRect.top + OUTER_MARGIN)+0x36 + (fac?64:0));
\r
3608 fputDW(diagFile, 0);
\r
3609 fputDW(diagFile, 0x36 + (fac?64:0));
\r
3610 // write BITMAPINFOHEADER
\r
3611 fputDW(diagFile, 40);
\r
3612 fputDW(diagFile, b.bmWidth);
\r
3613 fputDW(diagFile, b.bmHeight - boardRect.top + OUTER_MARGIN);
\r
3614 if(fac) fputDW(diagFile, 0x040001); // planes and bits/pixel
\r
3615 else fputDW(diagFile, 0x200001); // planes and bits/pixel
\r
3616 fputDW(diagFile, 0);
\r
3617 fputDW(diagFile, 0);
\r
3618 fputDW(diagFile, 0);
\r
3619 fputDW(diagFile, 0);
\r
3620 fputDW(diagFile, 0);
\r
3621 fputDW(diagFile, 0);
\r
3622 // write color table
\r
3624 for(i=0; i<16; i++) fputDW(diagFile, color[i]);
\r
3625 // write bitmap data
\r
3626 for(i=0; i<wb*(b.bmHeight - boardRect.top + OUTER_MARGIN); i++)
\r
3627 fputc(pData[i], diagFile);
\r
3631 SelectObject(tmphdc, oldBitmap);
\r
3633 /* Massive cleanup */
\r
3634 for (x = 0; x < num_clips; x++)
\r
3635 DeleteObject(clips[x]);
\r
3638 DeleteObject(bufferBitmap);
\r
3641 ReleaseDC(hwndMain, hdc);
\r
3643 if (lastDrawnFlipView != flipView && nr == 0) {
\r
3645 CheckMenuItem(GetMenu(hwndMain),IDM_FlipView, MF_BYCOMMAND|MF_CHECKED);
\r
3647 CheckMenuItem(GetMenu(hwndMain),IDM_FlipView, MF_BYCOMMAND|MF_UNCHECKED);
\r
3650 /* CopyBoard(lastDrawn, board);*/
\r
3651 lastDrawnHighlight = highlightInfo;
\r
3652 lastDrawnPremove = premoveHighlightInfo;
\r
3653 lastDrawnFlipView = flipView;
\r
3654 lastDrawnValid[nr] = 1;
\r
3657 /* [HGM] diag: Save the current board display to the given open file and close the file */
\r
3662 saveDiagFlag = 1; diagFile = f;
\r
3663 HDCDrawPosition(NULL, TRUE, NULL);
\r
3667 // if(f != NULL) fprintf(f, "Sorry, but this feature is still in preparation\n");
\r
3674 /*---------------------------------------------------------------------------*\
\r
3675 | CLIENT PAINT PROCEDURE
\r
3676 | This is the main event-handler for the WM_PAINT message.
\r
3678 \*--------------------------------------------------