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 DisplayMove P((int moveNumber));
\r
109 Boolean ParseFEN P((Board board, int *blackPlaysFirst, char *fen));
\r
110 void ChatPopUp P((char *s));
\r
112 ChessSquare piece;
\r
113 POINT pos; /* window coordinates of current pos */
\r
114 POINT lastpos; /* window coordinates of last pos - used for clipping */
\r
115 POINT from; /* board coordinates of the piece's orig pos */
\r
116 POINT to; /* board coordinates of the piece's new pos */
\r
119 static AnimInfo animInfo = { EmptySquare, {-1,-1}, {-1,-1}, {-1,-1} };
\r
122 POINT start; /* window coordinates of start pos */
\r
123 POINT pos; /* window coordinates of current pos */
\r
124 POINT lastpos; /* window coordinates of last pos - used for clipping */
\r
125 POINT from; /* board coordinates of the piece's orig pos */
\r
128 static DragInfo dragInfo = { {-1,-1}, {-1,-1}, {-1,-1}, {-1,-1} };
\r
131 POINT sq[2]; /* board coordinates of from, to squares */
\r
134 static HighlightInfo highlightInfo = { {{-1, -1}, {-1, -1}} };
\r
135 static HighlightInfo premoveHighlightInfo = { {{-1, -1}, {-1, -1}} };
\r
136 static HighlightInfo partnerHighlightInfo = { {{-1, -1}, {-1, -1}} };
\r
137 static HighlightInfo oldPartnerHighlight = { {{-1, -1}, {-1, -1}} };
\r
139 typedef struct { // [HGM] atomic
\r
140 int fromX, fromY, toX, toY, radius;
\r
143 static ExplodeInfo explodeInfo;
\r
145 /* Window class names */
\r
146 char szAppName[] = "WinBoard";
\r
147 char szConsoleName[] = "WBConsole";
\r
149 /* Title bar text */
\r
150 char szTitle[] = "WinBoard";
\r
151 char szConsoleTitle[] = "I C S Interaction";
\r
154 char *settingsFileName;
\r
155 Boolean saveSettingsOnExit;
\r
156 char installDir[MSG_SIZ];
\r
157 int errorExitStatus;
\r
159 BoardSize boardSize;
\r
160 Boolean chessProgram;
\r
161 //static int boardX, boardY;
\r
162 int minX, minY; // [HGM] placement: volatile limits on upper-left corner
\r
163 int squareSize, lineGap, minorSize;
\r
164 static int winW, winH;
\r
165 static RECT messageRect, whiteRect, blackRect, leftLogoRect, rightLogoRect; // [HGM] logo
\r
166 static int logoHeight = 0;
\r
167 static char messageText[MESSAGE_TEXT_MAX];
\r
168 static int clockTimerEvent = 0;
\r
169 static int loadGameTimerEvent = 0;
\r
170 static int analysisTimerEvent = 0;
\r
171 static DelayedEventCallback delayedTimerCallback;
\r
172 static int delayedTimerEvent = 0;
\r
173 static int buttonCount = 2;
\r
174 char *icsTextMenuString;
\r
176 char *firstChessProgramNames;
\r
177 char *secondChessProgramNames;
\r
179 #define PALETTESIZE 256
\r
181 HINSTANCE hInst; /* current instance */
\r
182 Boolean alwaysOnTop = FALSE;
\r
184 COLORREF lightSquareColor, darkSquareColor, whitePieceColor,
\r
185 blackPieceColor, highlightSquareColor, premoveHighlightColor;
\r
187 ColorClass currentColorClass;
\r
189 HWND hCommPort = NULL; /* currently open comm port */
\r
190 static HWND hwndPause; /* pause button */
\r
191 static HBITMAP pieceBitmap[3][(int) BlackPawn]; /* [HGM] nr of bitmaps referred to bP in stead of wK */
\r
192 static HBRUSH lightSquareBrush, darkSquareBrush,
\r
193 blackSquareBrush, /* [HGM] for band between board and holdings */
\r
194 explodeBrush, /* [HGM] atomic */
\r
195 markerBrush, /* [HGM] markers */
\r
196 whitePieceBrush, blackPieceBrush, iconBkgndBrush /*, outlineBrush*/;
\r
197 static POINT gridEndpoints[(BOARD_RANKS + BOARD_FILES + 2) * 2];
\r
198 static DWORD gridVertexCounts[BOARD_RANKS + BOARD_FILES + 2];
\r
199 static HPEN gridPen = NULL;
\r
200 static HPEN highlightPen = NULL;
\r
201 static HPEN premovePen = NULL;
\r
202 static NPLOGPALETTE pLogPal;
\r
203 static BOOL paletteChanged = FALSE;
\r
204 static HICON iconWhite, iconBlack, iconCurrent;
\r
205 static int doingSizing = FALSE;
\r
206 static int lastSizing = 0;
\r
207 static int prevStderrPort;
\r
208 static HBITMAP userLogo;
\r
210 static HBITMAP liteBackTexture = NULL;
\r
211 static HBITMAP darkBackTexture = NULL;
\r
212 static int liteBackTextureMode = BACK_TEXTURE_MODE_PLAIN;
\r
213 static int darkBackTextureMode = BACK_TEXTURE_MODE_PLAIN;
\r
214 static int backTextureSquareSize = 0;
\r
215 static struct { int x; int y; int mode; } backTextureSquareInfo[BOARD_RANKS][BOARD_FILES];
\r
217 #if __GNUC__ && !defined(_winmajor)
\r
218 #define oldDialog 0 /* cygwin doesn't define _winmajor; mingw does */
\r
220 #if defined(_winmajor)
\r
221 #define oldDialog (_winmajor < 4)
\r
223 #define oldDialog 0
\r
227 #define INTERNATIONAL
\r
229 #ifdef INTERNATIONAL
\r
230 # define _(s) T_(s)
\r
236 # define Translate(x, y)
\r
237 # define LoadLanguageFile(s)
\r
240 #ifdef INTERNATIONAL
\r
242 Boolean barbaric; // flag indicating if translation is needed
\r
244 // list of item numbers used in each dialog (used to alter language at run time)
\r
246 #define ABOUTBOX -1 /* not sure why these are needed */
\r
247 #define ABOUTBOX2 -1
\r
249 int dialogItems[][40] = {
\r
250 { ABOUTBOX, IDOK, 400 },
\r
251 { DLG_TimeControl, IDC_Babble, OPT_TCUseMoves, OPT_TCUseInc, OPT_TCUseFixed,
\r
252 OPT_TCtext1, OPT_TCtext2, OPT_TCitext1, OPT_TCitext2, OPT_TCftext, GPB_Factors, IDC_Factor1, IDC_Factor2, IDOK, IDCANCEL },
\r
253 { DLG_LoadOptions, OPT_Autostep, OPT_AStext1, IDOK, IDCANCEL },
\r
254 { DLG_SaveOptions, OPT_Autosave, OPT_AVPrompt, OPT_AVToFile, OPT_AVBrowse,
\r
255 801, OPT_PGN, OPT_Old, OPT_OutOfBookInfo, IDOK, IDCANCEL },
\r
256 { 1536, 1090, IDC_Directories, 1089, 1091, IDOK, IDCANCEL, 1038, IDC_IndexNr, 1037 },
\r
257 { DLG_CommPort, IDOK, IDCANCEL, IDC_Port, IDC_Rate, IDC_Bits, IDC_Parity,
\r
258 IDC_Stop, IDC_Flow, OPT_SerialHelp },
\r
259 { DLG_EditComment, IDOK, OPT_CancelComment, OPT_ClearComment, OPT_EditComment },
\r
260 { DLG_PromotionKing, PB_Chancellor, PB_Archbishop, PB_Queen, PB_Rook,
\r
261 PB_Bishop, PB_Knight, PB_King, IDCANCEL, IDC_Yes, IDC_No, IDC_Centaur },
\r
262 { ABOUTBOX2, IDC_ChessBoard },
\r
263 { DLG_GameList, OPT_GameListLoad, OPT_GameListPrev, OPT_GameListNext,
\r
264 OPT_GameListClose, IDC_GameListDoFilter },
\r
265 { DLG_EditTags, IDOK, OPT_TagsCancel, OPT_EditTags },
\r
266 { DLG_Error, IDOK },
\r
267 { DLG_Colorize, IDOK, IDCANCEL, OPT_ChooseColor, OPT_Bold, OPT_Italic,
\r
268 OPT_Underline, OPT_Strikeout, OPT_Sample },
\r
269 { DLG_Question, IDOK, IDCANCEL, OPT_QuestionText },
\r
270 { DLG_Startup, IDC_Welcome, OPT_ChessEngine, OPT_ChessServer, OPT_View,
\r
271 IDC_SPECIFY_ENG_STATIC, IDC_SPECIFY_SERVER_STATIC, OPT_AnyAdditional,
\r
272 IDOK, IDCANCEL, IDM_HELPCONTENTS },
\r
273 { DLG_IndexNumber, IDC_Index },
\r
274 { DLG_TypeInMove, IDOK, IDCANCEL },
\r
275 { DLG_TypeInName, IDOK, IDCANCEL },
\r
276 { DLG_Sound, IDC_Event, OPT_NoSound, OPT_DefaultBeep, OPT_BuiltInSound,
\r
277 OPT_WavFile, OPT_BrowseSound, OPT_DefaultSounds, IDOK, IDCANCEL, OPT_PlaySound },
\r
278 { DLG_GeneralOptions, IDOK, IDCANCEL, OPT_AlwaysOnTop, OPT_HighlightLastMove,
\r
279 OPT_AlwaysQueen, OPT_PeriodicUpdates, OPT_AnimateDragging, OPT_PonderNextMove,
\r
280 OPT_AnimateMoving, OPT_PopupExitMessage, OPT_AutoFlag, OPT_PopupMoveErrors,
\r
281 OPT_AutoFlipView, OPT_ShowButtonBar, OPT_AutoRaiseBoard, OPT_ShowCoordinates,
\r
282 OPT_Blindfold, OPT_ShowThinking, OPT_HighlightDragging, OPT_TestLegality,
\r
283 OPT_SaveExtPGN, OPT_HideThinkFromHuman, OPT_ExtraInfoInMoveHistory,
\r
284 OPT_HighlightMoveArrow, OPT_AutoLogo },
\r
285 { DLG_IcsOptions, IDOK, IDCANCEL, OPT_AutoComment, OPT_AutoKibitz, OPT_AutoObserve,
\r
286 OPT_GetMoveList, OPT_LocalLineEditing, OPT_QuietPlay, OPT_SeekGraph, OPT_AutoRefresh,
\r
287 OPT_BgObserve, OPT_DualBoard, OPT_Premove, OPT_PremoveWhite, OPT_PremoveBlack,
\r
288 OPT_SmartMove, OPT_IcsAlarm, IDC_Sec, OPT_ChooseShoutColor, OPT_ChooseSShoutColor,
\r
289 OPT_ChooseChannel1Color, OPT_ChooseChannelColor, OPT_ChooseKibitzColor,
\r
290 OPT_ChooseTellColor, OPT_ChooseChallengeColor, OPT_ChooseRequestColor,
\r
291 OPT_ChooseSeekColor, OPT_ChooseNormalColor, OPT_ChooseBackgroundColor,
\r
292 OPT_DefaultColors, OPT_DontColorize, IDC_Boxes, GPB_Colors, GPB_Premove,
\r
293 GPB_General, GPB_Alarm },
\r
294 { DLG_BoardOptions, IDOK, IDCANCEL, OPT_SizeTiny, OPT_SizeTeeny, OPT_SizeDinky,
\r
295 OPT_SizePetite, OPT_SizeSlim, OPT_SizeSmall, OPT_SizeMediocre, OPT_SizeMiddling,
\r
296 OPT_SizeAverage, OPT_SizeModerate, OPT_SizeMedium, OPT_SizeBulky, OPT_SizeLarge,
\r
297 OPT_SizeBig, OPT_SizeHuge, OPT_SizeGiant, OPT_SizeColossal, OPT_SizeTitanic,
\r
298 OPT_ChooseLightSquareColor, OPT_ChooseDarkSquareColor, OPT_ChooseWhitePieceColor,
\r
299 OPT_ChooseBlackPieceColor, OPT_ChooseHighlightSquareColor, OPT_ChoosePremoveHighlightColor,
\r
300 OPT_Monochrome, OPT_AllWhite, OPT_UpsideDown, OPT_DefaultBoardColors, GPB_Colors,
\r
301 IDC_Light, IDC_Dark, IDC_White, IDC_Black, IDC_High, IDC_PreHigh, GPB_Size },
\r
302 { DLG_NewVariant, IDOK, IDCANCEL, OPT_VariantNormal, OPT_VariantFRC, OPT_VariantWildcastle,
\r
303 OPT_VariantNocastle, OPT_VariantLosers, OPT_VariantGiveaway, OPT_VariantSuicide,
\r
304 OPT_Variant3Check, OPT_VariantTwoKings, OPT_VariantAtomic, OPT_VariantCrazyhouse,
\r
305 OPT_VariantBughouse, OPT_VariantTwilight, OPT_VariantShogi, OPT_VariantSuper,
\r
306 OPT_VariantKnightmate, OPT_VariantBerolina, OPT_VariantCylinder, OPT_VariantFairy,
\r
307 OPT_VariantMakruk, OPT_VariantGothic, OPT_VariantCapablanca, OPT_VariantJanus,
\r
308 OPT_VariantCRC, OPT_VariantFalcon, OPT_VariantCourier, OPT_VariantGreat,
\r
309 OPT_VariantShatranj, OPT_VariantXiangqi, GPB_Variant, GPB_Board, IDC_Height,
\r
310 IDC_Width, IDC_Hand, IDC_Pieces, IDC_Def },
\r
311 { DLG_Fonts, IDOK, IDCANCEL, OPT_ChooseClockFont, OPT_ChooseMessageFont,
\r
312 OPT_ChooseCoordFont, OPT_ChooseTagFont, OPT_ChooseCommentsFont, OPT_ChooseConsoleFont, OPT_ChooseMoveHistoryFont, OPT_DefaultFonts,
\r
313 OPT_ClockFont, OPT_MessageFont, OPT_CoordFont, OPT_EditTagsFont,
\r
314 OPT_CommentsFont, OPT_MessageFont5, GPB_Current, GPB_All, OPT_MessageFont6 },
\r
315 { DLG_NewGameFRC, IDC_NFG_Label, IDC_NFG_Random, IDOK, IDCANCEL },
\r
316 { DLG_GameListOptions, IDC_GLT, IDC_GLT_Up, IDC_GLT_Down, IDC_GLT_Restore,
\r
317 IDC_GLT_Default, IDOK, IDCANCEL, IDC_GLT_RestoreTo },
\r
318 { DLG_MoveHistory },
\r
319 { DLG_EvalGraph },
\r
320 { DLG_EngineOutput, IDC_EngineLabel1, IDC_Engine1_NPS, IDC_EngineLabel2, IDC_Engine2_NPS },
\r
321 { DLG_Chat, IDC_Partner, IDC_Clear, IDC_Send, },
\r
322 { DLG_EnginePlayOptions, IDC_EpPonder, IDC_EpShowThinking, IDC_EpHideThinkingHuman,
\r
323 IDC_EpPeriodicUpdates, GPB_Adjudications, IDC_Draw, IDC_Moves, IDC_Threshold,
\r
324 IDC_Centi, IDC_TestClaims, IDC_DetectMates, IDC_MaterialDraws, IDC_TrivialDraws,
\r
325 GPB_Apply, IDC_Rule, IDC_Repeats, IDC_ScoreAbs1, IDC_ScoreAbs2, IDOK, IDCANCEL },
\r
326 { DLG_OptionsUCI, IDC_PolyDir, IDC_BrowseForPolyglotDir, IDC_Hash, IDC_Path,
\r
327 IDC_BrowseForEGTB, IDC_Cache, IDC_UseBook, IDC_BrowseForBook, IDC_CPU, IDC_OwnBook1,
\r
328 IDC_OwnBook2, IDC_Depth, IDC_Variation, IDC_DefGames, IDOK, IDCANCEL },
\r
332 static char languageBuf[50000], *foreign[1000], *english[1000], *languageFile[MSG_SIZ];
\r
333 static int lastChecked;
\r
334 static char oldLanguage[MSG_SIZ], *menuText[10][30];
\r
335 extern int tinyLayout;
\r
336 extern char * menuBarText[][8];
\r
339 LoadLanguageFile(char *name)
\r
340 { //load the file with translations, and make a list of the strings to be translated, and their translations
\r
342 int i=0, j=0, n=0, k;
\r
345 if(!name || name[0] == NULLCHAR) return;
\r
346 snprintf(buf, MSG_SIZ, "%s%s", name, strchr(name, '.') ? "" : ".lng"); // auto-append lng extension
\r
347 if(!strcmp(buf, oldLanguage)) { barbaric = 1; return; } // this language already loaded; just switch on
\r
348 if((f = fopen(buf, "r")) == NULL) return;
\r
349 while((k = fgetc(f)) != EOF) {
\r
350 if(i >= sizeof(languageBuf)) { DisplayError("Language file too big", 0); return; }
\r
351 languageBuf[i] = k;
\r
353 if(languageBuf[n] == '"' && languageBuf[i-1] == '"') {
\r
355 if(p = strstr(languageBuf + n + 1, "\" === \"")) {
\r
356 if(p > languageBuf+n+2 && p+8 < languageBuf+i) {
\r
357 if(j >= sizeof(english)) { DisplayError("Too many translated strings", 0); return; }
\r
358 english[j] = languageBuf + n + 1; *p = 0;
\r
359 foreign[j++] = p + 7; languageBuf[i-1] = 0;
\r
360 //if(appData.debugMode) fprintf(debugFP, "translation: replace '%s' by '%s'\n", english[j-1], foreign[j-1]);
\r
365 } else if(i > 0 && languageBuf[i-1] == '\\') {
\r
367 case 'n': k = '\n'; break;
\r
368 case 'r': k = '\r'; break;
\r
369 case 't': k = '\t'; break;
\r
371 languageBuf[--i] = k;
\r
376 barbaric = (j != 0);
\r
377 safeStrCpy(oldLanguage, buf, sizeof(oldLanguage)/sizeof(oldLanguage[0]) );
\r
382 { // return the translation of the given string
\r
383 // efficiency can be improved a lot...
\r
385 //if(appData.debugMode) fprintf(debugFP, "T_(%s)\n", s);
\r
386 if(!barbaric) return s;
\r
387 if(!s) return ""; // sanity
\r
388 while(english[i]) {
\r
389 if(!strcmp(s, english[i])) return foreign[i];
\r
396 Translate(HWND hDlg, int dialogID)
\r
397 { // translate all text items in the given dialog
\r
399 char buf[MSG_SIZ], *s;
\r
400 //if(appData.debugMode) fprintf(debugFP, "Translate(%d)\n", dialogID);
\r
401 if(!barbaric) return;
\r
402 while(dialogItems[i][0] && dialogItems[i][0] != dialogID) i++; // find the dialog description
\r
403 if(dialogItems[i][0] != dialogID) return; // unknown dialog, should not happen
\r
404 GetWindowText( hDlg, buf, MSG_SIZ );
\r
406 //if(appData.debugMode) fprintf(debugFP, "WindowText '%s' -> '%s'\n", buf, s);
\r
407 if(strcmp(buf, s)) SetWindowText(hDlg, s); // replace by translated string (if different)
\r
408 for(j=1; k=dialogItems[i][j]; j++) { // translate all listed dialog items
\r
409 GetDlgItemText(hDlg, k, buf, MSG_SIZ);
\r
410 if(strlen(buf) == 0) continue;
\r
412 if(strcmp(buf, s)) SetDlgItemText(hDlg, k, s); // replace by translated string (if different)
\r
417 TranslateMenus(int addLanguage)
\r
420 WIN32_FIND_DATA fileData;
\r
422 #define IDM_English 1895
\r
424 HMENU mainMenu = GetMenu(hwndMain);
\r
425 for (i=GetMenuItemCount(mainMenu)-1; i>=0; i--) {
\r
426 HMENU subMenu = GetSubMenu(mainMenu, i);
\r
427 ModifyMenu(mainMenu, i, MF_STRING|MF_BYPOSITION|MF_POPUP|EnableMenuItem(mainMenu, i, MF_BYPOSITION),
\r
428 (UINT) subMenu, T_(menuBarText[tinyLayout][i]));
\r
429 for(j=GetMenuItemCount(subMenu)-1; j>=0; j--){
\r
431 UINT k = GetMenuItemID(subMenu, j);
\r
433 safeStrCpy(buf, menuText[i][j], sizeof(buf)/sizeof(buf[0]) ); else {
\r
434 GetMenuString(subMenu, j, buf, MSG_SIZ, MF_BYPOSITION);
\r
435 menuText[i][j] = strdup(buf); // remember original on first change
\r
437 if(buf[0] == NULLCHAR) continue;
\r
438 //fprintf(debugFP, "menu(%d,%d) = %s (%08x, %08x) %d\n", i, j, buf, mainMenu, subMenu, k);
\r
439 ModifyMenu(subMenu, j, MF_STRING|MF_BYPOSITION
\r
440 |CheckMenuItem(subMenu, j, MF_BYPOSITION)
\r
441 |EnableMenuItem(subMenu, j, MF_BYPOSITION), k, T_(buf));
\r
444 DrawMenuBar(hwndMain);
\r
447 if(!addLanguage) return;
\r
448 if((hFind = FindFirstFile("*.LNG", &fileData)) != INVALID_HANDLE_VALUE) {
\r
449 HMENU mainMenu = GetMenu(hwndMain);
\r
450 HMENU subMenu = GetSubMenu(mainMenu, GetMenuItemCount(mainMenu)-1);
\r
451 AppendMenu(subMenu, MF_SEPARATOR, (UINT_PTR) 0, NULL);
\r
452 AppendMenu(subMenu, MF_ENABLED|MF_STRING|(barbaric?MF_UNCHECKED:MF_CHECKED), (UINT_PTR) IDM_English, (LPCTSTR) "English");
\r
453 i = 0; lastChecked = IDM_English;
\r
455 char *p, *q = fileData.cFileName;
\r
456 int checkFlag = MF_UNCHECKED;
\r
457 languageFile[i] = strdup(q);
\r
458 if(barbaric && !strcmp(oldLanguage, q)) {
\r
459 checkFlag = MF_CHECKED;
\r
460 lastChecked = IDM_English + i + 1;
\r
461 CheckMenuItem(mainMenu, IDM_English, MF_BYCOMMAND|MF_UNCHECKED);
\r
463 *q = ToUpper(*q); while(*++q) *q = ToLower(*q);
\r
464 p = strstr(fileData.cFileName, ".lng");
\r
466 AppendMenu(subMenu, MF_ENABLED|MF_STRING|checkFlag, (UINT_PTR) IDM_English + ++i, (LPCTSTR) fileData.cFileName);
\r
467 } while(FindNextFile(hFind, &fileData));
\r
480 int cliWidth, cliHeight;
\r
483 SizeInfo sizeInfo[] =
\r
485 { "tiny", 21, 0, 1, 1, 0, 0 },
\r
486 { "teeny", 25, 1, 1, 1, 0, 0 },
\r
487 { "dinky", 29, 1, 1, 1, 0, 0 },
\r
488 { "petite", 33, 1, 1, 1, 0, 0 },
\r
489 { "slim", 37, 2, 1, 0, 0, 0 },
\r
490 { "small", 40, 2, 1, 0, 0, 0 },
\r
491 { "mediocre", 45, 2, 1, 0, 0, 0 },
\r
492 { "middling", 49, 2, 0, 0, 0, 0 },
\r
493 { "average", 54, 2, 0, 0, 0, 0 },
\r
494 { "moderate", 58, 3, 0, 0, 0, 0 },
\r
495 { "medium", 64, 3, 0, 0, 0, 0 },
\r
496 { "bulky", 72, 3, 0, 0, 0, 0 },
\r
497 { "large", 80, 3, 0, 0, 0, 0 },
\r
498 { "big", 87, 3, 0, 0, 0, 0 },
\r
499 { "huge", 95, 3, 0, 0, 0, 0 },
\r
500 { "giant", 108, 3, 0, 0, 0, 0 },
\r
501 { "colossal", 116, 4, 0, 0, 0, 0 },
\r
502 { "titanic", 129, 4, 0, 0, 0, 0 },
\r
503 { NULL, 0, 0, 0, 0, 0, 0 }
\r
506 #define MF(x) {x, {{0,}, 0. }, {0, }, 0}
\r
507 MyFont fontRec[NUM_SIZES][NUM_FONTS] =
\r
509 { 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
510 { 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
511 { 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
512 { 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
513 { 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
514 { 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
515 { 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
516 { 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
517 { 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
518 { 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
519 { 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
520 { 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
521 { 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
522 { 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
523 { 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
524 { 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
525 { 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
526 { 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
529 MyFont *font[NUM_SIZES][NUM_FONTS];
\r
538 #define BUTTON_WIDTH (tinyLayout ? 16 : 32)
\r
539 #define N_BUTTONS 5
\r
541 MyButtonDesc buttonDesc[N_BUTTONS] =
\r
543 {"<<", IDM_ToStart, NULL, NULL},
\r
544 {"<", IDM_Backward, NULL, NULL},
\r
545 {"P", IDM_Pause, NULL, NULL},
\r
546 {">", IDM_Forward, NULL, NULL},
\r
547 {">>", IDM_ToEnd, NULL, NULL},
\r
550 int tinyLayout = 0, smallLayout = 0;
\r
551 #define MENU_BAR_ITEMS 7
\r
552 char *menuBarText[2][MENU_BAR_ITEMS+1] = {
\r
553 { N_("&File"), N_("&Mode"), N_("&Action"), N_("&Step"), N_("&Options"), N_("&Help"), NULL },
\r
554 { N_("&F"), N_("&M"), N_("&A"), N_("&S"), N_("&O"), N_("&H"), NULL },
\r
558 MySound sounds[(int)NSoundClasses];
\r
559 MyTextAttribs textAttribs[(int)NColorClasses];
\r
561 MyColorizeAttribs colorizeAttribs[] = {
\r
562 { (COLORREF)0, 0, N_("Shout Text") },
\r
563 { (COLORREF)0, 0, N_("SShout/CShout") },
\r
564 { (COLORREF)0, 0, N_("Channel 1 Text") },
\r
565 { (COLORREF)0, 0, N_("Channel Text") },
\r
566 { (COLORREF)0, 0, N_("Kibitz Text") },
\r
567 { (COLORREF)0, 0, N_("Tell Text") },
\r
568 { (COLORREF)0, 0, N_("Challenge Text") },
\r
569 { (COLORREF)0, 0, N_("Request Text") },
\r
570 { (COLORREF)0, 0, N_("Seek Text") },
\r
571 { (COLORREF)0, 0, N_("Normal Text") },
\r
572 { (COLORREF)0, 0, N_("None") }
\r
577 static char *commentTitle;
\r
578 static char *commentText;
\r
579 static int commentIndex;
\r
580 static Boolean editComment = FALSE;
\r
583 char errorTitle[MSG_SIZ];
\r
584 char errorMessage[2*MSG_SIZ];
\r
585 HWND errorDialog = NULL;
\r
586 BOOLEAN moveErrorMessageUp = FALSE;
\r
587 BOOLEAN consoleEcho = TRUE;
\r
588 CHARFORMAT consoleCF;
\r
589 COLORREF consoleBackgroundColor;
\r
591 char *programVersion;
\r
597 typedef int CPKind;
\r
606 SOCKET sock2; /* stderr socket for OpenRcmd */
\r
609 #define INPUT_SOURCE_BUF_SIZE 4096
\r
611 typedef struct _InputSource {
\r
618 char buf[INPUT_SOURCE_BUF_SIZE];
\r
622 InputCallback func;
\r
623 struct _InputSource *second; /* for stderr thread on CPRcmd */
\r
627 InputSource *consoleInputSource;
\r
632 VOID ConsoleOutput(char* data, int length, int forceVisible);
\r
633 VOID ConsoleCreate();
\r
635 ConsoleWndProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam);
\r
636 VOID ColorizeTextPopup(HWND hwnd, ColorClass cc);
\r
637 VOID PrintCommSettings(FILE *f, char *name, DCB *dcb);
\r
638 VOID ParseCommSettings(char *arg, DCB *dcb);
\r
640 StartupDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam);
\r
641 VOID APIENTRY MenuPopup(HWND hwnd, POINT pt, HMENU hmenu, UINT def);
\r
642 void ParseIcsTextMenu(char *icsTextMenuString);
\r
643 VOID PopUpMoveDialog(char firstchar);
\r
644 VOID PopUpNameDialog(char firstchar);
\r
645 VOID UpdateSampleText(HWND hDlg, int id, MyColorizeAttribs *mca);
\r
649 int GameListOptions();
\r
651 int dummy; // [HGM] for obsolete args
\r
653 HWND hwndMain = NULL; /* root window*/
\r
654 HWND hwndConsole = NULL;
\r
655 HWND commentDialog = NULL;
\r
656 HWND moveHistoryDialog = NULL;
\r
657 HWND evalGraphDialog = NULL;
\r
658 HWND engineOutputDialog = NULL;
\r
659 HWND gameListDialog = NULL;
\r
660 HWND editTagsDialog = NULL;
\r
662 int commentUp = FALSE;
\r
664 WindowPlacement wpMain;
\r
665 WindowPlacement wpConsole;
\r
666 WindowPlacement wpComment;
\r
667 WindowPlacement wpMoveHistory;
\r
668 WindowPlacement wpEvalGraph;
\r
669 WindowPlacement wpEngineOutput;
\r
670 WindowPlacement wpGameList;
\r
671 WindowPlacement wpTags;
\r
673 VOID EngineOptionsPopup(); // [HGM] settings
\r
675 VOID GothicPopUp(char *title, VariantClass variant);
\r
677 * Setting "frozen" should disable all user input other than deleting
\r
678 * the window. We do this while engines are initializing themselves.
\r
680 static int frozen = 0;
\r
681 static int oldMenuItemState[MENU_BAR_ITEMS];
\r
687 if (frozen) return;
\r
689 hmenu = GetMenu(hwndMain);
\r
690 for (i=0; i<MENU_BAR_ITEMS; i++) {
\r
691 oldMenuItemState[i] = EnableMenuItem(hmenu, i, MF_BYPOSITION|MF_GRAYED);
\r
693 DrawMenuBar(hwndMain);
\r
696 /* Undo a FreezeUI */
\r
702 if (!frozen) return;
\r
704 hmenu = GetMenu(hwndMain);
\r
705 for (i=0; i<MENU_BAR_ITEMS; i++) {
\r
706 EnableMenuItem(hmenu, i, MF_BYPOSITION|oldMenuItemState[i]);
\r
708 DrawMenuBar(hwndMain);
\r
711 /*static*/ int fromX = -1, fromY = -1, toX, toY; // [HGM] moved upstream, so JAWS can use them
\r
713 /* JAWS preparation patch (WinBoard for the sight impaired). Define required insertions as empty */
\r
719 #define JAWS_ALT_INTERCEPT
\r
720 #define JAWS_KB_NAVIGATION
\r
721 #define JAWS_MENU_ITEMS
\r
722 #define JAWS_SILENCE
\r
723 #define JAWS_REPLAY
\r
725 #define JAWS_COPYRIGHT
\r
726 #define JAWS_DELETE(X) X
\r
727 #define SAYMACHINEMOVE()
\r
731 /*---------------------------------------------------------------------------*\
\r
735 \*---------------------------------------------------------------------------*/
\r
738 WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
\r
739 LPSTR lpCmdLine, int nCmdShow)
\r
742 HANDLE hAccelMain, hAccelNoAlt, hAccelNoICS;
\r
743 // INITCOMMONCONTROLSEX ex;
\r
747 LoadLibrary("RICHED32.DLL");
\r
748 consoleCF.cbSize = sizeof(CHARFORMAT);
\r
750 if (!InitApplication(hInstance)) {
\r
753 if (!InitInstance(hInstance, nCmdShow, lpCmdLine)) {
\r
759 // InitCommonControlsEx(&ex);
\r
760 InitCommonControls();
\r
762 hAccelMain = LoadAccelerators (hInstance, szAppName);
\r
763 hAccelNoAlt = LoadAccelerators (hInstance, "NO_ALT");
\r
764 hAccelNoICS = LoadAccelerators( hInstance, "NO_ICS"); /* [AS] No Ctrl-V on ICS!!! */
\r
766 /* Acquire and dispatch messages until a WM_QUIT message is received. */
\r
768 while (GetMessage(&msg, /* message structure */
\r
769 NULL, /* handle of window receiving the message */
\r
770 0, /* lowest message to examine */
\r
771 0)) /* highest message to examine */
\r
774 if(msg.message == WM_CHAR && msg.wParam == '\t') {
\r
775 // [HGM] navigate: switch between all windows with tab
\r
776 HWND e1 = NULL, e2 = NULL, mh = NULL, hInput = NULL, hText = NULL;
\r
777 int i, currentElement = 0;
\r
779 // first determine what element of the chain we come from (if any)
\r
780 if(appData.icsActive) {
\r
781 hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
782 hText = GetDlgItem(hwndConsole, OPT_ConsoleText);
\r
784 if(engineOutputDialog && EngineOutputIsUp()) {
\r
785 e1 = GetDlgItem(engineOutputDialog, IDC_EngineMemo1);
\r
786 e2 = GetDlgItem(engineOutputDialog, IDC_EngineMemo2);
\r
788 if(moveHistoryDialog && MoveHistoryIsUp()) {
\r
789 mh = GetDlgItem(moveHistoryDialog, IDC_MoveHistory);
\r
791 if(msg.hwnd == hwndMain) currentElement = 7 ; else
\r
792 if(msg.hwnd == engineOutputDialog) currentElement = 2; else
\r
793 if(msg.hwnd == e1) currentElement = 2; else
\r
794 if(msg.hwnd == e2) currentElement = 3; else
\r
795 if(msg.hwnd == moveHistoryDialog) currentElement = 4; else
\r
796 if(msg.hwnd == mh) currentElement = 4; else
\r
797 if(msg.hwnd == evalGraphDialog) currentElement = 6; else
\r
798 if(msg.hwnd == hText) currentElement = 5; else
\r
799 if(msg.hwnd == hInput) currentElement = 6; else
\r
800 for (i = 0; i < N_BUTTONS; i++) {
\r
801 if (buttonDesc[i].hwnd == msg.hwnd) { currentElement = 1; break; }
\r
804 // determine where to go to
\r
805 if(currentElement) { HWND h = NULL; int direction = GetKeyState(VK_SHIFT) < 0 ? -1 : 1;
\r
807 currentElement = (currentElement + direction) % 7;
\r
808 switch(currentElement) {
\r
810 h = hwndMain; break; // passing this case always makes the loop exit
\r
812 h = buttonDesc[0].hwnd; break; // could be NULL
\r
814 if(!EngineOutputIsUp()) continue; // skip closed auxiliary windows
\r
817 if(!EngineOutputIsUp()) continue;
\r
820 if(!MoveHistoryIsUp()) continue;
\r
822 // case 6: // input to eval graph does not seem to get here!
\r
823 // if(!EvalGraphIsUp()) continue;
\r
824 // h = evalGraphDialog; break;
\r
826 if(!appData.icsActive) continue;
\r
830 if(!appData.icsActive) continue;
\r
836 if(currentElement > 4 && IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);
\r
837 if(currentElement < 5 && IsIconic(hwndMain)) ShowWindow(hwndMain, SW_RESTORE); // all open together
\r
840 continue; // this message now has been processed
\r
844 if (!(commentDialog && IsDialogMessage(commentDialog, &msg)) &&
\r
845 !(moveHistoryDialog && IsDialogMessage(moveHistoryDialog, &msg)) &&
\r
846 !(evalGraphDialog && IsDialogMessage(evalGraphDialog, &msg)) &&
\r
847 !(engineOutputDialog && IsDialogMessage(engineOutputDialog, &msg)) &&
\r
848 !(editTagsDialog && IsDialogMessage(editTagsDialog, &msg)) &&
\r
849 !(gameListDialog && IsDialogMessage(gameListDialog, &msg)) &&
\r
850 !(errorDialog && IsDialogMessage(errorDialog, &msg)) &&
\r
851 !(!frozen && TranslateAccelerator(hwndMain, hAccelMain, &msg)) && JAWS_ACCEL
\r
852 !(!hwndConsole && TranslateAccelerator(hwndMain, hAccelNoICS, &msg)) &&
\r
853 !(!hwndConsole && TranslateAccelerator(hwndMain, hAccelNoAlt, &msg))) {
\r
854 int done = 0, i; // [HGM] chat: dispatch cat-box messages
\r
855 for(i=0; i<MAX_CHAT; i++)
\r
856 if(chatHandle[i] && IsDialogMessage(chatHandle[i], &msg)) {
\r
859 if(done) continue; // [HGM] chat: end patch
\r
860 TranslateMessage(&msg); /* Translates virtual key codes */
\r
861 DispatchMessage(&msg); /* Dispatches message to window */
\r
866 return (msg.wParam); /* Returns the value from PostQuitMessage */
\r
869 /*---------------------------------------------------------------------------*\
\r
871 * Initialization functions
\r
873 \*---------------------------------------------------------------------------*/
\r
877 { // update user logo if necessary
\r
878 static char oldUserName[MSG_SIZ], *curName;
\r
880 if(appData.autoLogo) {
\r
881 curName = UserName();
\r
882 if(strcmp(curName, oldUserName)) {
\r
883 snprintf(oldUserName, MSG_SIZ, "logos\\%s.bmp", curName);
\r
884 userLogo = LoadImage( 0, oldUserName, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );
\r
885 safeStrCpy(oldUserName, curName, sizeof(oldUserName)/sizeof(oldUserName[0]) );
\r
891 InitApplication(HINSTANCE hInstance)
\r
895 /* Fill in window class structure with parameters that describe the */
\r
898 wc.style = CS_HREDRAW | CS_VREDRAW; /* Class style(s). */
\r
899 wc.lpfnWndProc = (WNDPROC)WndProc; /* Window Procedure */
\r
900 wc.cbClsExtra = 0; /* No per-class extra data. */
\r
901 wc.cbWndExtra = 0; /* No per-window extra data. */
\r
902 wc.hInstance = hInstance; /* Owner of this class */
\r
903 wc.hIcon = LoadIcon(hInstance, "icon_white");
\r
904 wc.hCursor = LoadCursor(NULL, IDC_ARROW); /* Cursor */
\r
905 wc.hbrBackground = (HBRUSH)(COLOR_WINDOW+1); /* Default color */
\r
906 wc.lpszMenuName = szAppName; /* Menu name from .RC */
\r
907 wc.lpszClassName = szAppName; /* Name to register as */
\r
909 /* Register the window class and return success/failure code. */
\r
910 if (!RegisterClass(&wc)) return FALSE;
\r
912 wc.style = CS_HREDRAW | CS_VREDRAW;
\r
913 wc.lpfnWndProc = (WNDPROC)ConsoleWndProc;
\r
915 wc.cbWndExtra = DLGWINDOWEXTRA;
\r
916 wc.hInstance = hInstance;
\r
917 wc.hIcon = LoadIcon(hInstance, "icon_white");
\r
918 wc.hCursor = LoadCursor(NULL, IDC_ARROW);
\r
919 wc.hbrBackground = (HBRUSH)(COLOR_MENU+1);
\r
920 wc.lpszMenuName = NULL;
\r
921 wc.lpszClassName = szConsoleName;
\r
923 if (!RegisterClass(&wc)) return FALSE;
\r
928 /* Set by InitInstance, used by EnsureOnScreen */
\r
929 int screenHeight, screenWidth;
\r
932 EnsureOnScreen(int *x, int *y, int minX, int minY)
\r
934 // int gap = GetSystemMetrics(SM_CYFRAME) + GetSystemMetrics(SM_CYCAPTION);
\r
935 /* Be sure window at (x,y) is not off screen (or even mostly off screen) */
\r
936 if (*x > screenWidth - 32) *x = 0;
\r
937 if (*y > screenHeight - 32) *y = 0;
\r
938 if (*x < minX) *x = minX;
\r
939 if (*y < minY) *y = minY;
\r
943 InitInstance(HINSTANCE hInstance, int nCmdShow, LPSTR lpCmdLine)
\r
945 HWND hwnd; /* Main window handle. */
\r
947 WINDOWPLACEMENT wp;
\r
950 hInst = hInstance; /* Store instance handle in our global variable */
\r
951 programName = szAppName;
\r
953 if (SearchPath(NULL, "WinBoard.exe", NULL, MSG_SIZ, installDir, &filepart)) {
\r
954 *filepart = NULLCHAR;
\r
956 GetCurrentDirectory(MSG_SIZ, installDir);
\r
958 gameInfo.boardWidth = gameInfo.boardHeight = 8; // [HGM] won't have open window otherwise
\r
959 screenWidth = screenHeight = 1000; // [HGM] placement: kludge to allow calling EnsureOnScreen from InitAppData
\r
960 InitAppData(lpCmdLine); /* Get run-time parameters */
\r
961 /* xboard, and older WinBoards, controlled the move sound with the
\r
962 appData.ringBellAfterMoves option. In the current WinBoard, we
\r
963 always turn the option on (so that the backend will call us),
\r
964 then let the user turn the sound off by setting it to silence if
\r
965 desired. To accommodate old winboard.ini files saved by old
\r
966 versions of WinBoard, we also turn off the sound if the option
\r
967 was initially set to false. [HGM] taken out of InitAppData */
\r
968 if (!appData.ringBellAfterMoves) {
\r
969 sounds[(int)SoundMove].name = strdup("");
\r
970 appData.ringBellAfterMoves = TRUE;
\r
972 if (appData.debugMode) {
\r
973 debugFP = fopen(appData.nameOfDebugFile, "w");
\r
974 setbuf(debugFP, NULL);
\r
977 LoadLanguageFile(appData.language);
\r
981 // InitEngineUCI( installDir, &first ); // [HGM] incorporated in InitBackEnd1()
\r
982 // InitEngineUCI( installDir, &second );
\r
984 /* Create a main window for this application instance. */
\r
985 hwnd = CreateWindow(szAppName, szTitle,
\r
986 (WS_OVERLAPPEDWINDOW & ~WS_MAXIMIZEBOX),
\r
987 CW_USEDEFAULT, 0, CW_USEDEFAULT, 0,
\r
988 NULL, NULL, hInstance, NULL);
\r
991 /* If window could not be created, return "failure" */
\r
996 /* [HGM] logo: Load logos if specified (must be done before InitDrawingSizes) */
\r
997 if( appData.firstLogo && appData.firstLogo[0] != NULLCHAR) {
\r
998 first.programLogo = LoadImage( 0, appData.firstLogo, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );
\r
1000 if (first.programLogo == NULL && appData.debugMode) {
\r
1001 fprintf( debugFP, "Unable to load logo bitmap '%s'\n", appData.firstLogo );
\r
1003 } else if(appData.autoLogo) {
\r
1004 if(appData.firstDirectory && appData.firstDirectory[0]) {
\r
1005 char buf[MSG_SIZ];
\r
1006 snprintf(buf, MSG_SIZ, "%s/logo.bmp", appData.firstDirectory);
\r
1007 first.programLogo = LoadImage( 0, buf, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );
\r
1011 if( appData.secondLogo && appData.secondLogo[0] != NULLCHAR) {
\r
1012 second.programLogo = LoadImage( 0, appData.secondLogo, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );
\r
1014 if (second.programLogo == NULL && appData.debugMode) {
\r
1015 fprintf( debugFP, "Unable to load logo bitmap '%s'\n", appData.secondLogo );
\r
1017 } else if(appData.autoLogo) {
\r
1018 char buf[MSG_SIZ];
\r
1019 if(appData.icsActive) { // [HGM] logo: in ICS mode second can be used for ICS
\r
1020 snprintf(buf, MSG_SIZ, "logos\\%s.bmp", appData.icsHost);
\r
1021 second.programLogo = LoadImage( 0, buf, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );
\r
1023 if(appData.secondDirectory && appData.secondDirectory[0]) {
\r
1024 snprintf(buf, MSG_SIZ, "%s\\logo.bmp", appData.secondDirectory);
\r
1025 second.programLogo = LoadImage( 0, buf, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );
\r
1031 iconWhite = LoadIcon(hInstance, "icon_white");
\r
1032 iconBlack = LoadIcon(hInstance, "icon_black");
\r
1033 iconCurrent = iconWhite;
\r
1034 InitDrawingColors();
\r
1035 screenHeight = GetSystemMetrics(SM_CYSCREEN);
\r
1036 screenWidth = GetSystemMetrics(SM_CXSCREEN);
\r
1037 for (ibs = (int) NUM_SIZES - 1; ibs >= 0; ibs--) {
\r
1038 /* Compute window size for each board size, and use the largest
\r
1039 size that fits on this screen as the default. */
\r
1040 InitDrawingSizes((BoardSize)(ibs+1000), 0);
\r
1041 if (boardSize == (BoardSize)-1 &&
\r
1042 winH <= screenHeight
\r
1043 - GetSystemMetrics(SM_CYFRAME) - GetSystemMetrics(SM_CYCAPTION) - 10
\r
1044 && winW <= screenWidth) {
\r
1045 boardSize = (BoardSize)ibs;
\r
1049 InitDrawingSizes(boardSize, 0);
\r
1050 TranslateMenus(1);
\r
1052 buttonCount = GetSystemMetrics(SM_CMOUSEBUTTONS);
\r
1054 /* [AS] Load textures if specified */
\r
1055 ZeroMemory( &backTextureSquareInfo, sizeof(backTextureSquareInfo) );
\r
1057 if( appData.liteBackTextureFile && appData.liteBackTextureFile[0] != NULLCHAR && appData.liteBackTextureFile[0] != '*' ) {
\r
1058 liteBackTexture = LoadImage( 0, appData.liteBackTextureFile, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );
\r
1059 liteBackTextureMode = appData.liteBackTextureMode;
\r
1061 if (liteBackTexture == NULL && appData.debugMode) {
\r
1062 fprintf( debugFP, "Unable to load lite texture bitmap '%s'\n", appData.liteBackTextureFile );
\r
1066 if( appData.darkBackTextureFile && appData.darkBackTextureFile[0] != NULLCHAR && appData.darkBackTextureFile[0] != '*' ) {
\r
1067 darkBackTexture = LoadImage( 0, appData.darkBackTextureFile, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );
\r
1068 darkBackTextureMode = appData.darkBackTextureMode;
\r
1070 if (darkBackTexture == NULL && appData.debugMode) {
\r
1071 fprintf( debugFP, "Unable to load dark texture bitmap '%s'\n", appData.darkBackTextureFile );
\r
1075 mysrandom( (unsigned) time(NULL) );
\r
1077 /* [AS] Restore layout */
\r
1078 if( wpMoveHistory.visible ) {
\r
1079 MoveHistoryPopUp();
\r
1082 if( wpEvalGraph.visible ) {
\r
1086 if( wpEngineOutput.visible ) {
\r
1087 EngineOutputPopUp();
\r
1090 /* Make the window visible; update its client area; and return "success" */
\r
1091 EnsureOnScreen(&wpMain.x, &wpMain.y, minX, minY);
\r
1092 wp.length = sizeof(WINDOWPLACEMENT);
\r
1094 wp.showCmd = nCmdShow;
\r
1095 wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;
\r
1096 wp.rcNormalPosition.left = wpMain.x;
\r
1097 wp.rcNormalPosition.right = wpMain.x + wpMain.width;
\r
1098 wp.rcNormalPosition.top = wpMain.y;
\r
1099 wp.rcNormalPosition.bottom = wpMain.y + wpMain.height;
\r
1100 SetWindowPlacement(hwndMain, &wp);
\r
1102 InitBackEnd2(); // [HGM] moved until after all windows placed, to save correct position if fatal error on engine start
\r
1104 if(!appData.noGUI) SetWindowPos(hwndMain, alwaysOnTop ? HWND_TOPMOST : HWND_NOTOPMOST,
\r
1105 0, 0, 0, 0, SWP_NOMOVE|SWP_NOSIZE);
\r
1107 if (hwndConsole) {
\r
1109 SetWindowPos(hwndConsole, alwaysOnTop ? HWND_TOPMOST : HWND_NOTOPMOST,
\r
1110 0, 0, 0, 0, SWP_NOMOVE|SWP_NOSIZE);
\r
1112 ShowWindow(hwndConsole, nCmdShow);
\r
1113 if(appData.chatBoxes) { // [HGM] chat: open chat boxes
\r
1114 char buf[MSG_SIZ], *p = buf, *q;
\r
1115 safeStrCpy(buf, appData.chatBoxes, sizeof(buf)/sizeof(buf[0]) );
\r
1117 q = strchr(p, ';');
\r
1119 if(*p) ChatPopUp(p);
\r
1122 SetActiveWindow(hwndConsole);
\r
1124 if(!appData.noGUI) UpdateWindow(hwnd); else ShowWindow(hwnd, SW_MINIMIZE);
\r
1125 if(gameListDialog) SetFocus(gameListDialog); // [HGM] jaws: for if we clicked multi-game game file
\r
1134 HMENU hmenu = GetMenu(hwndMain);
\r
1136 (void) EnableMenuItem(hmenu, IDM_CommPort,
\r
1137 MF_BYCOMMAND|((appData.icsActive &&
\r
1138 *appData.icsCommPort != NULLCHAR) ?
\r
1139 MF_ENABLED : MF_GRAYED));
\r
1140 (void) CheckMenuItem(hmenu, IDM_SaveSettingsOnExit,
\r
1141 MF_BYCOMMAND|(saveSettingsOnExit ?
\r
1142 MF_CHECKED : MF_UNCHECKED));
\r
1145 //---------------------------------------------------------------------------------------------------------
\r
1147 #define ICS_TEXT_MENU_SIZE (IDM_CommandXLast - IDM_CommandX + 1)
\r
1148 #define XBOARD FALSE
\r
1150 #define OPTCHAR "/"
\r
1151 #define SEPCHAR "="
\r
1155 // front-end part of option handling
\r
1158 LFfromMFP(LOGFONT* lf, MyFontParams *mfp)
\r
1160 HDC hdc = CreateDC("DISPLAY", NULL, NULL, NULL);
\r
1161 lf->lfHeight = -(int)(mfp->pointSize * GetDeviceCaps(hdc, LOGPIXELSY) / 72.0 + 0.5);
\r
1164 lf->lfEscapement = 0;
\r
1165 lf->lfOrientation = 0;
\r
1166 lf->lfWeight = mfp->bold ? FW_BOLD : FW_NORMAL;
\r
1167 lf->lfItalic = mfp->italic;
\r
1168 lf->lfUnderline = mfp->underline;
\r
1169 lf->lfStrikeOut = mfp->strikeout;
\r
1170 lf->lfCharSet = mfp->charset;
\r
1171 lf->lfOutPrecision = OUT_DEFAULT_PRECIS;
\r
1172 lf->lfClipPrecision = CLIP_DEFAULT_PRECIS;
\r
1173 lf->lfQuality = DEFAULT_QUALITY;
\r
1174 lf->lfPitchAndFamily = DEFAULT_PITCH|FF_DONTCARE;
\r
1175 safeStrCpy(lf->lfFaceName, mfp->faceName, sizeof(lf->lfFaceName)/sizeof(lf->lfFaceName[0]) );
\r
1179 CreateFontInMF(MyFont *mf)
\r
1181 LFfromMFP(&mf->lf, &mf->mfp);
\r
1182 if (mf->hf) DeleteObject(mf->hf);
\r
1183 mf->hf = CreateFontIndirect(&mf->lf);
\r
1186 // [HGM] This platform-dependent table provides the location for storing the color info
\r
1188 colorVariable[] = {
\r
1189 &whitePieceColor,
\r
1190 &blackPieceColor,
\r
1191 &lightSquareColor,
\r
1192 &darkSquareColor,
\r
1193 &highlightSquareColor,
\r
1194 &premoveHighlightColor,
\r
1196 &consoleBackgroundColor,
\r
1197 &appData.fontForeColorWhite,
\r
1198 &appData.fontBackColorWhite,
\r
1199 &appData.fontForeColorBlack,
\r
1200 &appData.fontBackColorBlack,
\r
1201 &appData.evalHistColorWhite,
\r
1202 &appData.evalHistColorBlack,
\r
1203 &appData.highlightArrowColor,
\r
1206 /* Command line font name parser. NULL name means do nothing.
\r
1207 Syntax like "Courier New:10.0 bi" or "Arial:10" or "Arial:10b"
\r
1208 For backward compatibility, syntax without the colon is also
\r
1209 accepted, but font names with digits in them won't work in that case.
\r
1212 ParseFontName(char *name, MyFontParams *mfp)
\r
1215 if (name == NULL) return;
\r
1217 q = strchr(p, ':');
\r
1219 if (q - p >= sizeof(mfp->faceName))
\r
1220 ExitArgError(_("Font name too long:"), name);
\r
1221 memcpy(mfp->faceName, p, q - p);
\r
1222 mfp->faceName[q - p] = NULLCHAR;
\r
1225 q = mfp->faceName;
\r
1226 while (*p && !isdigit(*p)) {
\r
1228 if (q - mfp->faceName >= sizeof(mfp->faceName))
\r
1229 ExitArgError(_("Font name too long:"), name);
\r
1231 while (q > mfp->faceName && q[-1] == ' ') q--;
\r
1234 if (!*p) ExitArgError(_("Font point size missing:"), name);
\r
1235 mfp->pointSize = (float) atof(p);
\r
1236 mfp->bold = (strchr(p, 'b') != NULL);
\r
1237 mfp->italic = (strchr(p, 'i') != NULL);
\r
1238 mfp->underline = (strchr(p, 'u') != NULL);
\r
1239 mfp->strikeout = (strchr(p, 's') != NULL);
\r
1240 mfp->charset = DEFAULT_CHARSET;
\r
1241 q = strchr(p, 'c');
\r
1243 mfp->charset = (BYTE) atoi(q+1);
\r
1247 ParseFont(char *name, int number)
\r
1248 { // wrapper to shield back-end from 'font'
\r
1249 ParseFontName(name, &font[boardSize][number]->mfp);
\r
1254 { // in WB we have a 2D array of fonts; this initializes their description
\r
1256 /* Point font array elements to structures and
\r
1257 parse default font names */
\r
1258 for (i=0; i<NUM_FONTS; i++) {
\r
1259 for (j=0; j<NUM_SIZES; j++) {
\r
1260 font[j][i] = &fontRec[j][i];
\r
1261 ParseFontName(font[j][i]->def, &font[j][i]->mfp);
\r
1268 { // here we create the actual fonts from the selected descriptions
\r
1270 for (i=0; i<NUM_FONTS; i++) {
\r
1271 for (j=0; j<NUM_SIZES; j++) {
\r
1272 CreateFontInMF(font[j][i]);
\r
1276 /* Color name parser.
\r
1277 X version accepts X color names, but this one
\r
1278 handles only the #rrggbb form (hex) or rrr,ggg,bbb (decimal) */
\r
1280 ParseColorName(char *name)
\r
1282 int red, green, blue, count;
\r
1283 char buf[MSG_SIZ];
\r
1285 count = sscanf(name, "#%2x%2x%2x", &red, &green, &blue);
\r
1287 count = sscanf(name, "%3d%*[^0-9]%3d%*[^0-9]%3d",
\r
1288 &red, &green, &blue);
\r
1291 snprintf(buf, MSG_SIZ, _("Can't parse color name %s"), name);
\r
1292 DisplayError(buf, 0);
\r
1293 return RGB(0, 0, 0);
\r
1295 return PALETTERGB(red, green, blue);
\r
1299 ParseColor(int n, char *name)
\r
1300 { // for WinBoard the color is an int, which needs to be derived from the string
\r
1301 if(colorVariable[n]) *(int*)colorVariable[n] = ParseColorName(name);
\r
1305 ParseAttribs(COLORREF *color, int *effects, char* argValue)
\r
1307 char *e = argValue;
\r
1311 if (*e == 'b') eff |= CFE_BOLD;
\r
1312 else if (*e == 'i') eff |= CFE_ITALIC;
\r
1313 else if (*e == 'u') eff |= CFE_UNDERLINE;
\r
1314 else if (*e == 's') eff |= CFE_STRIKEOUT;
\r
1315 else if (*e == '#' || isdigit(*e)) break;
\r
1319 *color = ParseColorName(e);
\r
1323 ParseTextAttribs(ColorClass cc, char *s)
\r
1324 { // [HGM] front-end wrapper that does the platform-dependent call
\r
1325 // for XBoard we would set (&appData.colorShout)[cc] = strdup(s);
\r
1326 ParseAttribs(&textAttribs[cc].color, &textAttribs[cc].effects, s);
\r
1330 ParseBoardSize(void *addr, char *name)
\r
1331 { // [HGM] rewritten with return-value ptr to shield back-end from BoardSize
\r
1332 BoardSize bs = SizeTiny;
\r
1333 while (sizeInfo[bs].name != NULL) {
\r
1334 if (StrCaseCmp(name, sizeInfo[bs].name) == 0) {
\r
1335 *(BoardSize *)addr = bs;
\r
1340 ExitArgError(_("Unrecognized board size value"), name);
\r
1345 { // [HGM] import name from appData first
\r
1348 for (cc = (ColorClass)0; cc < ColorNormal; cc++) {
\r
1349 textAttribs[cc].sound.name = strdup((&appData.soundShout)[cc]);
\r
1350 textAttribs[cc].sound.data = NULL;
\r
1351 MyLoadSound(&textAttribs[cc].sound);
\r
1353 for (cc = ColorNormal; cc < NColorClasses; cc++) {
\r
1354 textAttribs[cc].sound.name = strdup("");
\r
1355 textAttribs[cc].sound.data = NULL;
\r
1357 for (sc = (SoundClass)0; sc < NSoundClasses; sc++) {
\r
1358 sounds[sc].name = strdup((&appData.soundMove)[sc]);
\r
1359 sounds[sc].data = NULL;
\r
1360 MyLoadSound(&sounds[sc]);
\r
1365 SetCommPortDefaults()
\r
1367 memset(&dcb, 0, sizeof(DCB)); // required by VS 2002 +
\r
1368 dcb.DCBlength = sizeof(DCB);
\r
1369 dcb.BaudRate = 9600;
\r
1370 dcb.fBinary = TRUE;
\r
1371 dcb.fParity = FALSE;
\r
1372 dcb.fOutxCtsFlow = FALSE;
\r
1373 dcb.fOutxDsrFlow = FALSE;
\r
1374 dcb.fDtrControl = DTR_CONTROL_ENABLE;
\r
1375 dcb.fDsrSensitivity = FALSE;
\r
1376 dcb.fTXContinueOnXoff = TRUE;
\r
1377 dcb.fOutX = FALSE;
\r
1379 dcb.fNull = FALSE;
\r
1380 dcb.fRtsControl = RTS_CONTROL_ENABLE;
\r
1381 dcb.fAbortOnError = FALSE;
\r
1383 dcb.Parity = SPACEPARITY;
\r
1384 dcb.StopBits = ONESTOPBIT;
\r
1387 // [HGM] args: these three cases taken out to stay in front-end
\r
1389 SaveFontArg(FILE *f, ArgDescriptor *ad)
\r
1390 { // in WinBoard every board size has its own font, and the "argLoc" identifies the table,
\r
1391 // while the curent board size determines the element. This system should be ported to XBoard.
\r
1392 // What the table contains pointers to, and how to print the font description, remains platform-dependent
\r
1394 for (bs=0; bs<NUM_SIZES; bs++) {
\r
1395 MyFontParams *mfp = &font[bs][(int) ad->argLoc]->mfp;
\r
1396 fprintf(f, "/size=%s ", sizeInfo[bs].name);
\r
1397 fprintf(f, "/%s=\"%s:%g%s%s%s%s%sc%d\"\n",
\r
1398 ad->argName, mfp->faceName, mfp->pointSize,
\r
1399 mfp->bold || mfp->italic || mfp->underline || mfp->strikeout ? " " : "",
\r
1400 mfp->bold ? "b" : "",
\r
1401 mfp->italic ? "i" : "",
\r
1402 mfp->underline ? "u" : "",
\r
1403 mfp->strikeout ? "s" : "",
\r
1404 (int)mfp->charset);
\r
1410 { // [HGM] copy the names from the internal WB variables to appData
\r
1413 for (cc = (ColorClass)0; cc < ColorNormal; cc++)
\r
1414 (&appData.soundShout)[cc] = textAttribs[cc].sound.name;
\r
1415 for (sc = (SoundClass)0; sc < NSoundClasses; sc++)
\r
1416 (&appData.soundMove)[sc] = sounds[sc].name;
\r
1420 SaveAttribsArg(FILE *f, ArgDescriptor *ad)
\r
1421 { // here the "argLoc" defines a table index. It could have contained the 'ta' pointer itself, though
\r
1422 MyTextAttribs* ta = &textAttribs[(ColorClass)ad->argLoc];
\r
1423 fprintf(f, "/%s=\"%s%s%s%s%s#%02lx%02lx%02lx\"\n", ad->argName,
\r
1424 (ta->effects & CFE_BOLD) ? "b" : "",
\r
1425 (ta->effects & CFE_ITALIC) ? "i" : "",
\r
1426 (ta->effects & CFE_UNDERLINE) ? "u" : "",
\r
1427 (ta->effects & CFE_STRIKEOUT) ? "s" : "",
\r
1428 (ta->effects) ? " " : "",
\r
1429 ta->color&0xff, (ta->color >> 8)&0xff, (ta->color >> 16)&0xff);
\r
1433 SaveColor(FILE *f, ArgDescriptor *ad)
\r
1434 { // in WinBoard the color is an int and has to be converted to text. In X it would be a string already?
\r
1435 COLORREF color = *(COLORREF *)colorVariable[(int)ad->argLoc];
\r
1436 fprintf(f, "/%s=#%02lx%02lx%02lx\n", ad->argName,
\r
1437 color&0xff, (color>>8)&0xff, (color>>16)&0xff);
\r
1441 SaveBoardSize(FILE *f, char *name, void *addr)
\r
1442 { // wrapper to shield back-end from BoardSize & sizeInfo
\r
1443 fprintf(f, "/%s=%s\n", name, sizeInfo[*(BoardSize *)addr].name);
\r
1447 ParseCommPortSettings(char *s)
\r
1448 { // wrapper to keep dcb from back-end
\r
1449 ParseCommSettings(s, &dcb);
\r
1454 { // wrapper to shield use of window handles from back-end (make addressible by number?)
\r
1455 GetActualPlacement(hwndMain, &wpMain);
\r
1456 GetActualPlacement(hwndConsole, &wpConsole);
\r
1457 GetActualPlacement(commentDialog, &wpComment);
\r
1458 GetActualPlacement(editTagsDialog, &wpTags);
\r
1459 GetActualPlacement(gameListDialog, &wpGameList);
\r
1460 GetActualPlacement(moveHistoryDialog, &wpMoveHistory);
\r
1461 GetActualPlacement(evalGraphDialog, &wpEvalGraph);
\r
1462 GetActualPlacement(engineOutputDialog, &wpEngineOutput);
\r
1466 PrintCommPortSettings(FILE *f, char *name)
\r
1467 { // wrapper to shield back-end from DCB
\r
1468 PrintCommSettings(f, name, &dcb);
\r
1472 MySearchPath(char *installDir, char *name, char *fullname)
\r
1474 char *dummy, buf[MSG_SIZ], *p = name, *q;
\r
1475 if(name[0]== '%') {
\r
1476 fullname[0] = 0; // [HGM] first expand any environment variables in the given name
\r
1477 while(*p == '%' && (q = strchr(p+1, '%'))) { // [HGM] recognize %*% as environment variable
\r
1478 safeStrCpy(buf, p+1, sizeof(buf)/sizeof(buf[0]) );
\r
1479 *strchr(buf, '%') = 0;
\r
1480 strcat(fullname, getenv(buf));
\r
1481 p = q+1; while(*p == '\\') { strcat(fullname, "\\"); p++; }
\r
1483 strcat(fullname, p); // after environment variables (if any), take the remainder of the given name
\r
1484 if(appData.debugMode) fprintf(debugFP, "name = '%s', expanded name = '%s'\n", name, fullname);
\r
1485 return (int) strlen(fullname);
\r
1487 return (int) SearchPath(installDir, name, NULL, MSG_SIZ, fullname, &dummy);
\r
1491 MyGetFullPathName(char *name, char *fullname)
\r
1494 return (int) GetFullPathName(name, MSG_SIZ, fullname, &dummy);
\r
1499 { // [HGM] args: allows testing if main window is realized from back-end
\r
1500 return hwndMain != NULL;
\r
1504 PopUpStartupDialog()
\r
1508 LoadLanguageFile(appData.language);
\r
1509 lpProc = MakeProcInstance((FARPROC)StartupDialog, hInst);
\r
1510 DialogBox(hInst, MAKEINTRESOURCE(DLG_Startup), NULL, (DLGPROC)lpProc);
\r
1511 FreeProcInstance(lpProc);
\r
1514 /*---------------------------------------------------------------------------*\
\r
1516 * GDI board drawing routines
\r
1518 \*---------------------------------------------------------------------------*/
\r
1520 /* [AS] Draw square using background texture */
\r
1521 static void DrawTile( int dx, int dy, int dw, int dh, HDC dst, HDC src, int mode, int sx, int sy )
\r
1526 return; /* Should never happen! */
\r
1529 SetGraphicsMode( dst, GM_ADVANCED );
\r
1536 /* X reflection */
\r
1541 x.eDx = (FLOAT) dw + dx - 1;
\r
1544 SetWorldTransform( dst, &x );
\r
1547 /* Y reflection */
\r
1553 x.eDy = (FLOAT) dh + dy - 1;
\r
1555 SetWorldTransform( dst, &x );
\r
1563 x.eDx = (FLOAT) dx;
\r
1564 x.eDy = (FLOAT) dy;
\r
1567 SetWorldTransform( dst, &x );
\r
1571 BitBlt( dst, dx, dy, dw, dh, src, sx, sy, SRCCOPY );
\r
1579 SetWorldTransform( dst, &x );
\r
1581 ModifyWorldTransform( dst, 0, MWT_IDENTITY );
\r
1584 /* [AS] [HGM] Make room for more piece types, so all pieces can be different */
\r
1586 PM_WP = (int) WhitePawn,
\r
1587 PM_WN = (int) WhiteKnight,
\r
1588 PM_WB = (int) WhiteBishop,
\r
1589 PM_WR = (int) WhiteRook,
\r
1590 PM_WQ = (int) WhiteQueen,
\r
1591 PM_WF = (int) WhiteFerz,
\r
1592 PM_WW = (int) WhiteWazir,
\r
1593 PM_WE = (int) WhiteAlfil,
\r
1594 PM_WM = (int) WhiteMan,
\r
1595 PM_WO = (int) WhiteCannon,
\r
1596 PM_WU = (int) WhiteUnicorn,
\r
1597 PM_WH = (int) WhiteNightrider,
\r
1598 PM_WA = (int) WhiteAngel,
\r
1599 PM_WC = (int) WhiteMarshall,
\r
1600 PM_WAB = (int) WhiteCardinal,
\r
1601 PM_WD = (int) WhiteDragon,
\r
1602 PM_WL = (int) WhiteLance,
\r
1603 PM_WS = (int) WhiteCobra,
\r
1604 PM_WV = (int) WhiteFalcon,
\r
1605 PM_WSG = (int) WhiteSilver,
\r
1606 PM_WG = (int) WhiteGrasshopper,
\r
1607 PM_WK = (int) WhiteKing,
\r
1608 PM_BP = (int) BlackPawn,
\r
1609 PM_BN = (int) BlackKnight,
\r
1610 PM_BB = (int) BlackBishop,
\r
1611 PM_BR = (int) BlackRook,
\r
1612 PM_BQ = (int) BlackQueen,
\r
1613 PM_BF = (int) BlackFerz,
\r
1614 PM_BW = (int) BlackWazir,
\r
1615 PM_BE = (int) BlackAlfil,
\r
1616 PM_BM = (int) BlackMan,
\r
1617 PM_BO = (int) BlackCannon,
\r
1618 PM_BU = (int) BlackUnicorn,
\r
1619 PM_BH = (int) BlackNightrider,
\r
1620 PM_BA = (int) BlackAngel,
\r
1621 PM_BC = (int) BlackMarshall,
\r
1622 PM_BG = (int) BlackGrasshopper,
\r
1623 PM_BAB = (int) BlackCardinal,
\r
1624 PM_BD = (int) BlackDragon,
\r
1625 PM_BL = (int) BlackLance,
\r
1626 PM_BS = (int) BlackCobra,
\r
1627 PM_BV = (int) BlackFalcon,
\r
1628 PM_BSG = (int) BlackSilver,
\r
1629 PM_BK = (int) BlackKing
\r
1632 static HFONT hPieceFont = NULL;
\r
1633 static HBITMAP hPieceMask[(int) EmptySquare];
\r
1634 static HBITMAP hPieceFace[(int) EmptySquare];
\r
1635 static int fontBitmapSquareSize = 0;
\r
1636 static char pieceToFontChar[(int) EmptySquare] =
\r
1637 { 'p', 'n', 'b', 'r', 'q',
\r
1638 'n', 'b', 'p', 'n', 'b', 'r', 'b', 'r', 'q', 'k',
\r
1639 'k', 'o', 'm', 'v', 't', 'w',
\r
1640 'v', 't', 'o', 'm', 'v', 't', 'v', 't', 'w', 'l',
\r
1643 extern BOOL SetCharTable( char *table, const char * map );
\r
1644 /* [HGM] moved to backend.c */
\r
1646 static void SetPieceBackground( HDC hdc, COLORREF color, int mode )
\r
1649 BYTE r1 = GetRValue( color );
\r
1650 BYTE g1 = GetGValue( color );
\r
1651 BYTE b1 = GetBValue( color );
\r
1657 /* Create a uniform background first */
\r
1658 hbrush = CreateSolidBrush( color );
\r
1659 SetRect( &rc, 0, 0, squareSize, squareSize );
\r
1660 FillRect( hdc, &rc, hbrush );
\r
1661 DeleteObject( hbrush );
\r
1664 /* Vertical gradient, good for pawn, knight and rook, less for queen and king */
\r
1665 int steps = squareSize / 2;
\r
1668 for( i=0; i<steps; i++ ) {
\r
1669 BYTE r = r1 - (r1-r2) * i / steps;
\r
1670 BYTE g = g1 - (g1-g2) * i / steps;
\r
1671 BYTE b = b1 - (b1-b2) * i / steps;
\r
1673 hbrush = CreateSolidBrush( RGB(r,g,b) );
\r
1674 SetRect( &rc, i + squareSize - steps, 0, i + squareSize - steps + 1, squareSize );
\r
1675 FillRect( hdc, &rc, hbrush );
\r
1676 DeleteObject(hbrush);
\r
1679 else if( mode == 2 ) {
\r
1680 /* Diagonal gradient, good more or less for every piece */
\r
1681 POINT triangle[3];
\r
1682 HPEN hpen = SelectObject( hdc, GetStockObject(NULL_PEN) );
\r
1683 HBRUSH hbrush_old;
\r
1684 int steps = squareSize;
\r
1687 triangle[0].x = squareSize - steps;
\r
1688 triangle[0].y = squareSize;
\r
1689 triangle[1].x = squareSize;
\r
1690 triangle[1].y = squareSize;
\r
1691 triangle[2].x = squareSize;
\r
1692 triangle[2].y = squareSize - steps;
\r
1694 for( i=0; i<steps; i++ ) {
\r
1695 BYTE r = r1 - (r1-r2) * i / steps;
\r
1696 BYTE g = g1 - (g1-g2) * i / steps;
\r
1697 BYTE b = b1 - (b1-b2) * i / steps;
\r
1699 hbrush = CreateSolidBrush( RGB(r,g,b) );
\r
1700 hbrush_old = SelectObject( hdc, hbrush );
\r
1701 Polygon( hdc, triangle, 3 );
\r
1702 SelectObject( hdc, hbrush_old );
\r
1703 DeleteObject(hbrush);
\r
1708 SelectObject( hdc, hpen );
\r
1713 [AS] The method I use to create the bitmaps it a bit tricky, but it
\r
1714 seems to work ok. The main problem here is to find the "inside" of a chess
\r
1715 piece: follow the steps as explained below.
\r
1717 static void CreatePieceMaskFromFont( HDC hdc_window, HDC hdc, int index )
\r
1721 COLORREF chroma = RGB(0xFF,0x00,0xFF);
\r
1725 int backColor = whitePieceColor;
\r
1726 int foreColor = blackPieceColor;
\r
1728 if( index < (int)BlackPawn && appData.fontBackColorWhite != appData.fontForeColorWhite ) {
\r
1729 backColor = appData.fontBackColorWhite;
\r
1730 foreColor = appData.fontForeColorWhite;
\r
1732 else if( index >= (int)BlackPawn && appData.fontBackColorBlack != appData.fontForeColorBlack ) {
\r
1733 backColor = appData.fontBackColorBlack;
\r
1734 foreColor = appData.fontForeColorBlack;
\r
1738 hbm = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );
\r
1740 hbm_old = SelectObject( hdc, hbm );
\r
1744 rc.right = squareSize;
\r
1745 rc.bottom = squareSize;
\r
1747 /* Step 1: background is now black */
\r
1748 FillRect( hdc, &rc, GetStockObject(BLACK_BRUSH) );
\r
1750 GetTextExtentPoint32( hdc, &pieceToFontChar[index], 1, &sz );
\r
1752 pt.x = (squareSize - sz.cx) / 2;
\r
1753 pt.y = (squareSize - sz.cy) / 2;
\r
1755 SetBkMode( hdc, TRANSPARENT );
\r
1756 SetTextColor( hdc, chroma );
\r
1757 /* Step 2: the piece has been drawn in purple, there are now black and purple in this bitmap */
\r
1758 TextOut( hdc, pt.x, pt.y, &pieceToFontChar[appData.allWhite && index >= (int)BlackPawn ? index - (int)BlackPawn : index], 1 );
\r
1760 SelectObject( hdc, GetStockObject(WHITE_BRUSH) );
\r
1761 /* Step 3: the area outside the piece is filled with white */
\r
1762 // FloodFill( hdc, 0, 0, chroma );
\r
1763 ExtFloodFill( hdc, 0, 0, 0, FLOODFILLSURFACE );
\r
1764 ExtFloodFill( hdc, 0, squareSize-1, 0, FLOODFILLSURFACE ); // [HGM] fill from all 4 corners, for if piece too big
\r
1765 ExtFloodFill( hdc, squareSize-1, 0, 0, FLOODFILLSURFACE );
\r
1766 ExtFloodFill( hdc, squareSize-1, squareSize-1, 0, FLOODFILLSURFACE );
\r
1767 SelectObject( hdc, GetStockObject(BLACK_BRUSH) );
\r
1769 Step 4: this is the tricky part, the area inside the piece is filled with black,
\r
1770 but if the start point is not inside the piece we're lost!
\r
1771 There should be a better way to do this... if we could create a region or path
\r
1772 from the fill operation we would be fine for example.
\r
1774 // FloodFill( hdc, squareSize / 2, squareSize / 2, RGB(0xFF,0xFF,0xFF) );
\r
1775 ExtFloodFill( hdc, squareSize / 2, squareSize / 2, RGB(0xFF,0xFF,0xFF), FLOODFILLBORDER );
\r
1777 { /* [HGM] shave off edges of mask, in an attempt to correct for the fact that FloodFill does not work correctly under Win XP */
\r
1778 HDC dc2 = CreateCompatibleDC( hdc_window );
\r
1779 HBITMAP bm2 = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );
\r
1781 SelectObject( dc2, bm2 );
\r
1782 BitBlt( dc2, 0, 0, squareSize, squareSize, hdc, 0, 0, SRCCOPY ); // make copy
\r
1783 BitBlt( hdc, 0, 1, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );
\r
1784 BitBlt( hdc, 2, 1, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );
\r
1785 BitBlt( hdc, 1, 0, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );
\r
1786 BitBlt( hdc, 1, 2, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );
\r
1789 DeleteObject( bm2 );
\r
1792 SetTextColor( hdc, 0 );
\r
1794 Step 5: some fonts have "disconnected" areas that are skipped by the fill:
\r
1795 draw the piece again in black for safety.
\r
1797 TextOut( hdc, pt.x, pt.y, &pieceToFontChar[appData.allWhite && index >= (int)BlackPawn ? index - (int)BlackPawn : index], 1 );
\r
1799 SelectObject( hdc, hbm_old );
\r
1801 if( hPieceMask[index] != NULL ) {
\r
1802 DeleteObject( hPieceMask[index] );
\r
1805 hPieceMask[index] = hbm;
\r
1808 hbm = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );
\r
1810 SelectObject( hdc, hbm );
\r
1813 HDC dc1 = CreateCompatibleDC( hdc_window );
\r
1814 HDC dc2 = CreateCompatibleDC( hdc_window );
\r
1815 HBITMAP bm2 = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );
\r
1817 SelectObject( dc1, hPieceMask[index] );
\r
1818 SelectObject( dc2, bm2 );
\r
1819 FillRect( dc2, &rc, GetStockObject(WHITE_BRUSH) );
\r
1820 BitBlt( dc2, 0, 0, squareSize, squareSize, dc1, 0, 0, SRCINVERT );
\r
1823 Now dc2 contains the inverse of the piece mask, i.e. a mask that preserves
\r
1824 the piece background and deletes (makes transparent) the rest.
\r
1825 Thanks to that mask, we are free to paint the background with the greates
\r
1826 freedom, as we'll be able to mask off the unwanted parts when finished.
\r
1827 We use this, to make gradients and give the pieces a "roundish" look.
\r
1829 SetPieceBackground( hdc, backColor, 2 );
\r
1830 BitBlt( hdc, 0, 0, squareSize, squareSize, dc2, 0, 0, SRCAND );
\r
1834 DeleteObject( bm2 );
\r
1837 SetTextColor( hdc, foreColor );
\r
1838 TextOut( hdc, pt.x, pt.y, &pieceToFontChar[appData.allWhite && index >= (int)BlackPawn ? index - (int)BlackPawn : index], 1 );
\r
1840 SelectObject( hdc, hbm_old );
\r
1842 if( hPieceFace[index] != NULL ) {
\r
1843 DeleteObject( hPieceFace[index] );
\r
1846 hPieceFace[index] = hbm;
\r
1849 static int TranslatePieceToFontPiece( int piece )
\r
1879 case BlackMarshall:
\r
1883 case BlackNightrider:
\r
1889 case BlackUnicorn:
\r
1893 case BlackGrasshopper:
\r
1905 case BlackCardinal:
\r
1912 case WhiteMarshall:
\r
1916 case WhiteNightrider:
\r
1922 case WhiteUnicorn:
\r
1926 case WhiteGrasshopper:
\r
1938 case WhiteCardinal:
\r
1947 void CreatePiecesFromFont()
\r
1950 HDC hdc_window = NULL;
\r
1956 if( fontBitmapSquareSize < 0 ) {
\r
1957 /* Something went seriously wrong in the past: do not try to recreate fonts! */
\r
1961 if( appData.renderPiecesWithFont == NULL || appData.renderPiecesWithFont[0] == NULLCHAR || appData.renderPiecesWithFont[0] == '*' ) {
\r
1962 fontBitmapSquareSize = -1;
\r
1966 if( fontBitmapSquareSize != squareSize ) {
\r
1967 hdc_window = GetDC( hwndMain );
\r
1968 hdc = CreateCompatibleDC( hdc_window );
\r
1970 if( hPieceFont != NULL ) {
\r
1971 DeleteObject( hPieceFont );
\r
1974 for( i=0; i<=(int)BlackKing; i++ ) {
\r
1975 hPieceMask[i] = NULL;
\r
1976 hPieceFace[i] = NULL;
\r
1982 if( appData.fontPieceSize >= 50 && appData.fontPieceSize <= 150 ) {
\r
1983 fontHeight = appData.fontPieceSize;
\r
1986 fontHeight = (fontHeight * squareSize) / 100;
\r
1988 lf.lfHeight = -MulDiv( fontHeight, GetDeviceCaps(hdc, LOGPIXELSY), 72 );
\r
1990 lf.lfEscapement = 0;
\r
1991 lf.lfOrientation = 0;
\r
1992 lf.lfWeight = FW_NORMAL;
\r
1994 lf.lfUnderline = 0;
\r
1995 lf.lfStrikeOut = 0;
\r
1996 lf.lfCharSet = DEFAULT_CHARSET;
\r
1997 lf.lfOutPrecision = OUT_DEFAULT_PRECIS;
\r
1998 lf.lfClipPrecision = CLIP_DEFAULT_PRECIS;
\r
1999 lf.lfQuality = PROOF_QUALITY;
\r
2000 lf.lfPitchAndFamily = DEFAULT_PITCH | FF_DONTCARE;
\r
2001 strncpy( lf.lfFaceName, appData.renderPiecesWithFont, sizeof(lf.lfFaceName) );
\r
2002 lf.lfFaceName[ sizeof(lf.lfFaceName) - 1 ] = '\0';
\r
2004 hPieceFont = CreateFontIndirect( &lf );
\r
2006 if( hPieceFont == NULL ) {
\r
2007 fontBitmapSquareSize = -2;
\r
2010 /* Setup font-to-piece character table */
\r
2011 if( ! SetCharTable(pieceToFontChar, appData.fontToPieceTable) ) {
\r
2012 /* No (or wrong) global settings, try to detect the font */
\r
2013 if( strstr(lf.lfFaceName,"Alpha") != NULL ) {
\r
2015 SetCharTable(pieceToFontChar, "phbrqkojntwl");
\r
2017 else if( strstr(lf.lfFaceName,"DiagramTT") != NULL ) {
\r
2018 /* DiagramTT* family */
\r
2019 SetCharTable(pieceToFontChar, "PNLRQKpnlrqk");
\r
2021 else if( strstr(lf.lfFaceName,"WinboardF") != NULL ) {
\r
2022 /* Fairy symbols */
\r
2023 SetCharTable(pieceToFontChar, "PNBRQFEACWMOHIJGDVSLUKpnbrqfeacwmohijgdvsluk");
\r
2025 else if( strstr(lf.lfFaceName,"GC2004D") != NULL ) {
\r
2026 /* Good Companion (Some characters get warped as literal :-( */
\r
2027 char s[] = "1cmWG0??S??oYI23wgQU";
\r
2028 s[0]=0xB9; s[1]=0xA9; s[6]=0xB1; s[11]=0xBB; s[12]=0xAB; s[17]=0xB3;
\r
2029 SetCharTable(pieceToFontChar, s);
\r
2032 /* Cases, Condal, Leipzig, Lucena, Marroquin, Merida, Usual */
\r
2033 SetCharTable(pieceToFontChar, "pnbrqkomvtwl");
\r
2037 /* Create bitmaps */
\r
2038 hfont_old = SelectObject( hdc, hPieceFont );
\r
2039 for(i=(int)WhitePawn; i<(int)EmptySquare; i++) /* [HGM] made a loop for this */
\r
2040 if(PieceToChar((ChessSquare)i) != '.') /* skip unused pieces */
\r
2041 CreatePieceMaskFromFont( hdc_window, hdc, i );
\r
2043 SelectObject( hdc, hfont_old );
\r
2045 fontBitmapSquareSize = squareSize;
\r
2049 if( hdc != NULL ) {
\r
2053 if( hdc_window != NULL ) {
\r
2054 ReleaseDC( hwndMain, hdc_window );
\r
2059 DoLoadBitmap(HINSTANCE hinst, char *piece, int squareSize, char *suffix)
\r
2063 snprintf(name, sizeof(name)/sizeof(name[0]), "%s%d%s", piece, squareSize, suffix);
\r
2064 if (gameInfo.event &&
\r
2065 strcmp(gameInfo.event, "Easter Egg Hunt") == 0 &&
\r
2066 strcmp(name, "k80s") == 0) {
\r
2067 safeStrCpy(name, "tim", sizeof(name)/sizeof(name[0]) );
\r
2069 return LoadBitmap(hinst, name);
\r
2073 /* Insert a color into the program's logical palette
\r
2074 structure. This code assumes the given color is
\r
2075 the result of the RGB or PALETTERGB macro, and it
\r
2076 knows how those macros work (which is documented).
\r
2079 InsertInPalette(COLORREF color)
\r
2081 LPPALETTEENTRY pe = &(pLogPal->palPalEntry[pLogPal->palNumEntries]);
\r
2083 if (pLogPal->palNumEntries++ >= PALETTESIZE) {
\r
2084 DisplayFatalError(_("Too many colors"), 0, 1);
\r
2085 pLogPal->palNumEntries--;
\r
2089 pe->peFlags = (char) 0;
\r
2090 pe->peRed = (char) (0xFF & color);
\r
2091 pe->peGreen = (char) (0xFF & (color >> 8));
\r
2092 pe->peBlue = (char) (0xFF & (color >> 16));
\r
2098 InitDrawingColors()
\r
2100 if (pLogPal == NULL) {
\r
2101 /* Allocate enough memory for a logical palette with
\r
2102 * PALETTESIZE entries and set the size and version fields
\r
2103 * of the logical palette structure.
\r
2105 pLogPal = (NPLOGPALETTE)
\r
2106 LocalAlloc(LMEM_FIXED, (sizeof(LOGPALETTE) +
\r
2107 (sizeof(PALETTEENTRY) * (PALETTESIZE))));
\r
2108 pLogPal->palVersion = 0x300;
\r
2110 pLogPal->palNumEntries = 0;
\r
2112 InsertInPalette(lightSquareColor);
\r
2113 InsertInPalette(darkSquareColor);
\r
2114 InsertInPalette(whitePieceColor);
\r
2115 InsertInPalette(blackPieceColor);
\r
2116 InsertInPalette(highlightSquareColor);
\r
2117 InsertInPalette(premoveHighlightColor);
\r
2119 /* create a logical color palette according the information
\r
2120 * in the LOGPALETTE structure.
\r
2122 hPal = CreatePalette((LPLOGPALETTE) pLogPal);
\r
2124 lightSquareBrush = CreateSolidBrush(lightSquareColor);
\r
2125 blackSquareBrush = CreateSolidBrush(blackPieceColor);
\r
2126 darkSquareBrush = CreateSolidBrush(darkSquareColor);
\r
2127 whitePieceBrush = CreateSolidBrush(whitePieceColor);
\r
2128 blackPieceBrush = CreateSolidBrush(blackPieceColor);
\r
2129 iconBkgndBrush = CreateSolidBrush(GetSysColor(COLOR_BACKGROUND));
\r
2130 explodeBrush = CreateSolidBrush(highlightSquareColor); // [HGM] atomic
\r
2131 markerBrush = CreateSolidBrush(premoveHighlightColor); // [HGM] markers
\r
2132 /* [AS] Force rendering of the font-based pieces */
\r
2133 if( fontBitmapSquareSize > 0 ) {
\r
2134 fontBitmapSquareSize = 0;
\r
2140 BoardWidth(int boardSize, int n)
\r
2141 { /* [HGM] argument n added to allow different width and height */
\r
2142 int lineGap = sizeInfo[boardSize].lineGap;
\r
2144 if( appData.overrideLineGap >= 0 && appData.overrideLineGap <= 5 ) {
\r
2145 lineGap = appData.overrideLineGap;
\r
2148 return (n + 1) * lineGap +
\r
2149 n * sizeInfo[boardSize].squareSize;
\r
2152 /* Respond to board resize by dragging edge */
\r
2154 ResizeBoard(int newSizeX, int newSizeY, int flags)
\r
2156 BoardSize newSize = NUM_SIZES - 1;
\r
2157 static int recurse = 0;
\r
2158 if (IsIconic(hwndMain)) return;
\r
2159 if (recurse > 0) return;
\r
2161 while (newSize > 0) {
\r
2162 InitDrawingSizes(newSize+1000, 0); // [HGM] kludge to update sizeInfo without visible effects
\r
2163 if(newSizeX >= sizeInfo[newSize].cliWidth &&
\r
2164 newSizeY >= sizeInfo[newSize].cliHeight) break;
\r
2167 boardSize = newSize;
\r
2168 InitDrawingSizes(boardSize, flags);
\r
2173 extern Boolean twoBoards, partnerUp; // [HGM] dual
\r
2176 InitDrawingSizes(BoardSize boardSize, int flags)
\r
2178 int i, boardWidth, boardHeight; /* [HGM] height treated separately */
\r
2179 ChessSquare piece;
\r
2180 static int oldBoardSize = -1, oldTinyLayout = 0;
\r
2182 SIZE clockSize, messageSize;
\r
2184 char buf[MSG_SIZ];
\r
2186 HMENU hmenu = GetMenu(hwndMain);
\r
2187 RECT crect, wrect, oldRect;
\r
2189 LOGBRUSH logbrush;
\r
2191 int suppressVisibleEffects = 0; // [HGM] kludge to request updating sizeInfo only
\r
2192 if((int)boardSize >= 1000 ) { boardSize -= 1000; suppressVisibleEffects = 1; }
\r
2194 /* [HGM] call with -2 uses old size (for if nr of files, ranks changes) */
\r
2195 if(boardSize == (BoardSize)(-2) ) boardSize = oldBoardSize;
\r
2197 oldRect.left = wpMain.x; //[HGM] placement: remember previous window params
\r
2198 oldRect.top = wpMain.y;
\r
2199 oldRect.right = wpMain.x + wpMain.width;
\r
2200 oldRect.bottom = wpMain.y + wpMain.height;
\r
2202 tinyLayout = sizeInfo[boardSize].tinyLayout;
\r
2203 smallLayout = sizeInfo[boardSize].smallLayout;
\r
2204 squareSize = sizeInfo[boardSize].squareSize;
\r
2205 lineGap = sizeInfo[boardSize].lineGap;
\r
2206 minorSize = 0; /* [HGM] Kludge to see if demagnified pieces need to be shifted */
\r
2208 if( appData.overrideLineGap >= 0 && appData.overrideLineGap <= 5 ) {
\r
2209 lineGap = appData.overrideLineGap;
\r
2212 if (tinyLayout != oldTinyLayout) {
\r
2213 long style = GetWindowLong(hwndMain, GWL_STYLE);
\r
2215 style &= ~WS_SYSMENU;
\r
2216 InsertMenu(hmenu, IDM_Exit, MF_BYCOMMAND, IDM_Minimize,
\r
2217 "&Minimize\tCtrl+F4");
\r
2219 style |= WS_SYSMENU;
\r
2220 RemoveMenu(hmenu, IDM_Minimize, MF_BYCOMMAND);
\r
2222 SetWindowLong(hwndMain, GWL_STYLE, style);
\r
2224 for (i=0; menuBarText[tinyLayout][i]; i++) {
\r
2225 ModifyMenu(hmenu, i, MF_STRING|MF_BYPOSITION|MF_POPUP,
\r
2226 (UINT)GetSubMenu(hmenu, i), T_(menuBarText[tinyLayout][i]));
\r
2228 DrawMenuBar(hwndMain);
\r
2231 boardWidth = BoardWidth(boardSize, BOARD_WIDTH);
\r
2232 boardHeight = BoardWidth(boardSize, BOARD_HEIGHT);
\r
2234 /* Get text area sizes */
\r
2235 hdc = GetDC(hwndMain);
\r
2236 if (appData.clockMode) {
\r
2237 snprintf(buf, MSG_SIZ, _("White: %s"), TimeString(23*60*60*1000L));
\r
2239 snprintf(buf, MSG_SIZ, _("White"));
\r
2241 oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);
\r
2242 GetTextExtentPoint(hdc, buf, strlen(buf), &clockSize);
\r
2243 SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);
\r
2244 str = _("We only care about the height here");
\r
2245 GetTextExtentPoint(hdc, str, strlen(str), &messageSize);
\r
2246 SelectObject(hdc, oldFont);
\r
2247 ReleaseDC(hwndMain, hdc);
\r
2249 /* Compute where everything goes */
\r
2250 if((first.programLogo || second.programLogo) && !tinyLayout) {
\r
2251 /* [HGM] logo: if either logo is on, reserve space for it */
\r
2252 logoHeight = 2*clockSize.cy;
\r
2253 leftLogoRect.left = OUTER_MARGIN;
\r
2254 leftLogoRect.right = leftLogoRect.left + 4*clockSize.cy;
\r
2255 leftLogoRect.top = OUTER_MARGIN;
\r
2256 leftLogoRect.bottom = OUTER_MARGIN + logoHeight;
\r
2258 rightLogoRect.right = OUTER_MARGIN + boardWidth;
\r
2259 rightLogoRect.left = rightLogoRect.right - 4*clockSize.cy;
\r
2260 rightLogoRect.top = OUTER_MARGIN;
\r
2261 rightLogoRect.bottom = OUTER_MARGIN + logoHeight;
\r
2264 whiteRect.left = leftLogoRect.right;
\r
2265 whiteRect.right = OUTER_MARGIN + boardWidth/2 - INNER_MARGIN/2;
\r
2266 whiteRect.top = OUTER_MARGIN;
\r
2267 whiteRect.bottom = whiteRect.top + logoHeight;
\r
2269 blackRect.right = rightLogoRect.left;
\r
2270 blackRect.left = whiteRect.right + INNER_MARGIN;
\r
2271 blackRect.top = whiteRect.top;
\r
2272 blackRect.bottom = whiteRect.bottom;
\r
2274 whiteRect.left = OUTER_MARGIN;
\r
2275 whiteRect.right = whiteRect.left + boardWidth/2 - INNER_MARGIN/2;
\r
2276 whiteRect.top = OUTER_MARGIN;
\r
2277 whiteRect.bottom = whiteRect.top + clockSize.cy;
\r
2279 blackRect.left = whiteRect.right + INNER_MARGIN;
\r
2280 blackRect.right = blackRect.left + boardWidth/2 - 1;
\r
2281 blackRect.top = whiteRect.top;
\r
2282 blackRect.bottom = whiteRect.bottom;
\r
2284 logoHeight = 0; // [HGM] logo: suppress logo after change to tiny layout!
\r
2287 messageRect.left = OUTER_MARGIN + MESSAGE_LINE_LEFTMARGIN;
\r
2288 if (appData.showButtonBar) {
\r
2289 messageRect.right = OUTER_MARGIN + boardWidth // [HGM] logo: expressed independent of clock placement
\r
2290 - N_BUTTONS*BUTTON_WIDTH - MESSAGE_LINE_LEFTMARGIN;
\r
2292 messageRect.right = OUTER_MARGIN + boardWidth;
\r
2294 messageRect.top = whiteRect.bottom + INNER_MARGIN;
\r
2295 messageRect.bottom = messageRect.top + messageSize.cy;
\r
2297 boardRect.left = OUTER_MARGIN;
\r
2298 boardRect.right = boardRect.left + boardWidth;
\r
2299 boardRect.top = messageRect.bottom + INNER_MARGIN;
\r
2300 boardRect.bottom = boardRect.top + boardHeight;
\r
2302 sizeInfo[boardSize].cliWidth = boardRect.right + OUTER_MARGIN;
\r
2303 sizeInfo[boardSize].cliHeight = boardRect.bottom + OUTER_MARGIN;
\r
2304 oldBoardSize = boardSize;
\r
2305 oldTinyLayout = tinyLayout;
\r
2306 winW = 2 * GetSystemMetrics(SM_CXFRAME) + boardRect.right + OUTER_MARGIN;
\r
2307 winH = 2 * GetSystemMetrics(SM_CYFRAME) + GetSystemMetrics(SM_CYMENU) +
\r
2308 GetSystemMetrics(SM_CYCAPTION) + boardRect.bottom + OUTER_MARGIN;
\r
2309 winW *= 1 + twoBoards;
\r
2310 if(suppressVisibleEffects) return; // [HGM] when called for filling sizeInfo only
\r
2311 wpMain.width = winW; // [HGM] placement: set through temporary which can used by initial sizing choice
\r
2312 wpMain.height = winH; // without disturbing window attachments
\r
2313 GetWindowRect(hwndMain, &wrect);
\r
2314 SetWindowPos(hwndMain, NULL, 0, 0, wpMain.width, wpMain.height,
\r
2315 SWP_NOCOPYBITS|SWP_NOZORDER|SWP_NOMOVE);
\r
2317 // [HGM] placement: let attached windows follow size change.
\r
2318 ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, moveHistoryDialog, &wpMoveHistory );
\r
2319 ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, evalGraphDialog, &wpEvalGraph );
\r
2320 ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, engineOutputDialog, &wpEngineOutput );
\r
2321 ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, gameListDialog, &wpGameList );
\r
2322 ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, hwndConsole, &wpConsole );
\r
2324 /* compensate if menu bar wrapped */
\r
2325 GetClientRect(hwndMain, &crect);
\r
2326 offby = boardRect.bottom + OUTER_MARGIN - crect.bottom;
\r
2327 wpMain.height += offby;
\r
2329 case WMSZ_TOPLEFT:
\r
2330 SetWindowPos(hwndMain, NULL,
\r
2331 wrect.right - wpMain.width, wrect.bottom - wpMain.height,
\r
2332 wpMain.width, wpMain.height, SWP_NOCOPYBITS|SWP_NOZORDER);
\r
2335 case WMSZ_TOPRIGHT:
\r
2337 SetWindowPos(hwndMain, NULL,
\r
2338 wrect.left, wrect.bottom - wpMain.height,
\r
2339 wpMain.width, wpMain.height, SWP_NOCOPYBITS|SWP_NOZORDER);
\r
2342 case WMSZ_BOTTOMLEFT:
\r
2344 SetWindowPos(hwndMain, NULL,
\r
2345 wrect.right - wpMain.width, wrect.top,
\r
2346 wpMain.width, wpMain.height, SWP_NOCOPYBITS|SWP_NOZORDER);
\r
2349 case WMSZ_BOTTOMRIGHT:
\r
2353 SetWindowPos(hwndMain, NULL, 0, 0, wpMain.width, wpMain.height,
\r
2354 SWP_NOCOPYBITS|SWP_NOZORDER|SWP_NOMOVE);
\r
2359 for (i = 0; i < N_BUTTONS; i++) {
\r
2360 if (buttonDesc[i].hwnd != NULL) {
\r
2361 DestroyWindow(buttonDesc[i].hwnd);
\r
2362 buttonDesc[i].hwnd = NULL;
\r
2364 if (appData.showButtonBar) {
\r
2365 buttonDesc[i].hwnd =
\r
2366 CreateWindow("BUTTON", buttonDesc[i].label,
\r
2367 WS_VISIBLE | WS_CHILD | BS_PUSHBUTTON,
\r
2368 boardRect.right - BUTTON_WIDTH*(N_BUTTONS-i),
\r
2369 messageRect.top, BUTTON_WIDTH, messageSize.cy, hwndMain,
\r
2370 (HMENU) buttonDesc[i].id,
\r
2371 (HINSTANCE) GetWindowLong(hwndMain, GWL_HINSTANCE), NULL);
\r
2373 SendMessage(buttonDesc[i].hwnd, WM_SETFONT,
\r
2374 (WPARAM)font[boardSize][MESSAGE_FONT]->hf,
\r
2375 MAKELPARAM(FALSE, 0));
\r
2377 if (buttonDesc[i].id == IDM_Pause)
\r
2378 hwndPause = buttonDesc[i].hwnd;
\r
2379 buttonDesc[i].wndproc = (WNDPROC)
\r
2380 SetWindowLong(buttonDesc[i].hwnd, GWL_WNDPROC, (LONG) ButtonProc);
\r
2383 if (gridPen != NULL) DeleteObject(gridPen);
\r
2384 if (highlightPen != NULL) DeleteObject(highlightPen);
\r
2385 if (premovePen != NULL) DeleteObject(premovePen);
\r
2386 if (lineGap != 0) {
\r
2387 logbrush.lbStyle = BS_SOLID;
\r
2388 logbrush.lbColor = RGB(0, 0, 0); /* grid pen color = black */
\r
2390 ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,
\r
2391 lineGap, &logbrush, 0, NULL);
\r
2392 logbrush.lbColor = highlightSquareColor;
\r
2394 ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,
\r
2395 lineGap, &logbrush, 0, NULL);
\r
2397 logbrush.lbColor = premoveHighlightColor;
\r
2399 ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,
\r
2400 lineGap, &logbrush, 0, NULL);
\r
2402 /* [HGM] Loop had to be split in part for vert. and hor. lines */
\r
2403 for (i = 0; i < BOARD_HEIGHT + 1; i++) {
\r
2404 gridEndpoints[i*2].x = boardRect.left + lineGap / 2;
\r
2405 gridEndpoints[i*2].y = gridEndpoints[i*2 + 1].y =
\r
2406 boardRect.top + lineGap / 2 + (i * (squareSize + lineGap));
\r
2407 gridEndpoints[i*2 + 1].x = boardRect.left + lineGap / 2 +
\r
2408 BOARD_WIDTH * (squareSize + lineGap);
\r
2409 gridVertexCounts[i*2] = gridVertexCounts[i*2 + 1] = 2;
\r
2411 for (i = 0; i < BOARD_WIDTH + 1; i++) {
\r
2412 gridEndpoints[i*2 + BOARD_HEIGHT*2 + 2].y = boardRect.top + lineGap / 2;
\r
2413 gridEndpoints[i*2 + BOARD_HEIGHT*2 + 2].x =
\r
2414 gridEndpoints[i*2 + 1 + BOARD_HEIGHT*2 + 2].x = boardRect.left +
\r
2415 lineGap / 2 + (i * (squareSize + lineGap));
\r
2416 gridEndpoints[i*2 + 1 + BOARD_HEIGHT*2 + 2].y =
\r
2417 boardRect.top + BOARD_HEIGHT * (squareSize + lineGap);
\r
2418 gridVertexCounts[i*2] = gridVertexCounts[i*2 + 1] = 2;
\r
2422 /* [HGM] Licensing requirement */
\r
2424 if(gameInfo.variant == VariantGothic) GothicPopUp( GOTHIC, VariantGothic); else
\r
2427 if(gameInfo.variant == VariantFalcon) GothicPopUp( FALCON, VariantFalcon); else
\r
2429 GothicPopUp( "", VariantNormal);
\r
2432 /* if (boardSize == oldBoardSize) return; [HGM] variant might have changed */
\r
2434 /* Load piece bitmaps for this board size */
\r
2435 for (i=0; i<=2; i++) {
\r
2436 for (piece = WhitePawn;
\r
2437 (int) piece < (int) BlackPawn;
\r
2438 piece = (ChessSquare) ((int) piece + 1)) {
\r
2439 if (pieceBitmap[i][piece] != NULL)
\r
2440 DeleteObject(pieceBitmap[i][piece]);
\r
2444 fontBitmapSquareSize = 0; /* [HGM] render: make sure pieces will be recreated, as we might need others now */
\r
2445 // Orthodox Chess pieces
\r
2446 pieceBitmap[0][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "s");
\r
2447 pieceBitmap[0][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "s");
\r
2448 pieceBitmap[0][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "s");
\r
2449 pieceBitmap[0][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "s");
\r
2450 pieceBitmap[0][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "s");
\r
2451 pieceBitmap[1][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "o");
\r
2452 pieceBitmap[1][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "o");
\r
2453 pieceBitmap[1][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "o");
\r
2454 pieceBitmap[1][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "o");
\r
2455 pieceBitmap[1][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "o");
\r
2456 pieceBitmap[2][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "w");
\r
2457 pieceBitmap[2][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "w");
\r
2458 pieceBitmap[2][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "w");
\r
2459 pieceBitmap[2][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "w");
\r
2460 pieceBitmap[2][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "w");
\r
2461 if( gameInfo.variant == VariantShogi && squareSize <= 72 && squareSize >= 33) {
\r
2462 // in Shogi, Hijack the unused Queen for Lance
\r
2463 pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "s");
\r
2464 pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "o");
\r
2465 pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "w");
\r
2467 pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "s");
\r
2468 pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "o");
\r
2469 pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "w");
\r
2472 if(squareSize <= 72 && squareSize >= 33) {
\r
2473 /* A & C are available in most sizes now */
\r
2474 if(squareSize != 49 && squareSize != 72 && squareSize != 33) { // Vortex-like
\r
2475 pieceBitmap[0][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "s");
\r
2476 pieceBitmap[1][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "o");
\r
2477 pieceBitmap[2][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "w");
\r
2478 pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "s");
\r
2479 pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "o");
\r
2480 pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "w");
\r
2481 pieceBitmap[0][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "s");
\r
2482 pieceBitmap[1][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "o");
\r
2483 pieceBitmap[2][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "w");
\r
2484 pieceBitmap[0][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "s");
\r
2485 pieceBitmap[1][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "o");
\r
2486 pieceBitmap[2][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "w");
\r
2487 } else { // Smirf-like
\r
2488 pieceBitmap[0][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "s");
\r
2489 pieceBitmap[1][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "o");
\r
2490 pieceBitmap[2][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "w");
\r
2492 if(gameInfo.variant == VariantGothic) { // Vortex-like
\r
2493 pieceBitmap[0][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "s");
\r
2494 pieceBitmap[1][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "o");
\r
2495 pieceBitmap[2][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "w");
\r
2496 } else { // WinBoard standard
\r
2497 pieceBitmap[0][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "s");
\r
2498 pieceBitmap[1][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "o");
\r
2499 pieceBitmap[2][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "w");
\r
2504 if(squareSize==72 || squareSize==49 || squareSize==33) { /* experiment with some home-made bitmaps */
\r
2505 pieceBitmap[0][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "s");
\r
2506 pieceBitmap[1][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "o");
\r
2507 pieceBitmap[2][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "w");
\r
2508 pieceBitmap[0][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "s");
\r
2509 pieceBitmap[1][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "o");
\r
2510 pieceBitmap[2][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "w");
\r
2511 pieceBitmap[0][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "s");
\r
2512 pieceBitmap[1][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "o");
\r
2513 pieceBitmap[2][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "w");
\r
2514 pieceBitmap[0][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "s");
\r
2515 pieceBitmap[1][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "o");
\r
2516 pieceBitmap[2][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "w");
\r
2517 pieceBitmap[0][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "s");
\r
2518 pieceBitmap[1][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "o");
\r
2519 pieceBitmap[2][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "w");
\r
2520 pieceBitmap[0][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "s");
\r
2521 pieceBitmap[1][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "o");
\r
2522 pieceBitmap[2][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "w");
\r
2523 pieceBitmap[0][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "s");
\r
2524 pieceBitmap[1][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "o");
\r
2525 pieceBitmap[2][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "w");
\r
2526 pieceBitmap[0][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "s");
\r
2527 pieceBitmap[1][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "o");
\r
2528 pieceBitmap[2][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "w");
\r
2529 pieceBitmap[0][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "s");
\r
2530 pieceBitmap[1][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "o");
\r
2531 pieceBitmap[2][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "w");
\r
2532 pieceBitmap[0][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "s");
\r
2533 pieceBitmap[1][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "o");
\r
2534 pieceBitmap[2][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "w");
\r
2536 if(gameInfo.variant == VariantShogi) { /* promoted Gold represemtations */
\r
2537 pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "wp", squareSize, "s");
\r
2538 pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "wp", squareSize, "o");
\r
2539 pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "w", squareSize, "w");
\r
2540 pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "wn", squareSize, "s");
\r
2541 pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "wn", squareSize, "o");
\r
2542 pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "w", squareSize, "w");
\r
2543 pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "ws", squareSize, "s");
\r
2544 pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "ws", squareSize, "o");
\r
2545 pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "w", squareSize, "w");
\r
2546 pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "wl", squareSize, "s");
\r
2547 pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "wl", squareSize, "o");
\r
2548 pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "w", squareSize, "w");
\r
2550 pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "s");
\r
2551 pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "o");
\r
2552 pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "w");
\r
2553 pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "s");
\r
2554 pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "o");
\r
2555 pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "w");
\r
2556 pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "s");
\r
2557 pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "o");
\r
2558 pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "w");
\r
2559 pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "s");
\r
2560 pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "o");
\r
2561 pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "w");
\r
2564 } else { /* other size, no special bitmaps available. Use smaller symbols */
\r
2565 if((int)boardSize < 2) minorSize = sizeInfo[0].squareSize;
\r
2566 else minorSize = sizeInfo[(int)boardSize - 2].squareSize;
\r
2567 pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "s");
\r
2568 pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "o");
\r
2569 pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "w");
\r
2570 pieceBitmap[0][WhiteCardinal] = DoLoadBitmap(hInst, "b", minorSize, "s");
\r
2571 pieceBitmap[1][WhiteCardinal] = DoLoadBitmap(hInst, "b", minorSize, "o");
\r
2572 pieceBitmap[2][WhiteCardinal] = DoLoadBitmap(hInst, "b", minorSize, "w");
\r
2573 pieceBitmap[0][WhiteDragon] = DoLoadBitmap(hInst, "r", minorSize, "s");
\r
2574 pieceBitmap[1][WhiteDragon] = DoLoadBitmap(hInst, "r", minorSize, "o");
\r
2575 pieceBitmap[2][WhiteDragon] = DoLoadBitmap(hInst, "r", minorSize, "w");
\r
2576 pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "s");
\r
2577 pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "o");
\r
2578 pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "w");
\r
2582 if(gameInfo.variant == VariantShogi && squareSize == 58)
\r
2583 /* special Shogi support in this size */
\r
2584 { for (i=0; i<=2; i++) { /* replace all bitmaps */
\r
2585 for (piece = WhitePawn;
\r
2586 (int) piece < (int) BlackPawn;
\r
2587 piece = (ChessSquare) ((int) piece + 1)) {
\r
2588 if (pieceBitmap[i][piece] != NULL)
\r
2589 DeleteObject(pieceBitmap[i][piece]);
\r
2592 pieceBitmap[0][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "o");
\r
2593 pieceBitmap[0][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "o");
\r
2594 pieceBitmap[0][WhiteBishop] = DoLoadBitmap(hInst, "sb", squareSize, "o");
\r
2595 pieceBitmap[0][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "o");
\r
2596 pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "o");
\r
2597 pieceBitmap[0][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "o");
\r
2598 pieceBitmap[0][WhiteFerz] = DoLoadBitmap(hInst, "sf", squareSize, "o");
\r
2599 pieceBitmap[0][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "o");
\r
2600 pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "su", squareSize, "o");
\r
2601 pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "sh", squareSize, "o");
\r
2602 pieceBitmap[0][WhiteCardinal] = DoLoadBitmap(hInst, "sa", squareSize, "o");
\r
2603 pieceBitmap[0][WhiteDragon] = DoLoadBitmap(hInst, "sc", squareSize, "o");
\r
2604 pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "sg", squareSize, "o");
\r
2605 pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "ss", squareSize, "o");
\r
2606 pieceBitmap[1][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "o");
\r
2607 pieceBitmap[1][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "o");
\r
2608 pieceBitmap[1][WhiteBishop] = DoLoadBitmap(hInst, "sb", squareSize, "o");
\r
2609 pieceBitmap[1][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "o");
\r
2610 pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "o");
\r
2611 pieceBitmap[1][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "o");
\r
2612 pieceBitmap[1][WhiteFerz] = DoLoadBitmap(hInst, "sf", squareSize, "o");
\r
2613 pieceBitmap[1][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "o");
\r
2614 pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "su", squareSize, "o");
\r
2615 pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "sh", squareSize, "o");
\r
2616 pieceBitmap[1][WhiteCardinal] = DoLoadBitmap(hInst, "sa", squareSize, "o");
\r
2617 pieceBitmap[1][WhiteDragon] = DoLoadBitmap(hInst, "sc", squareSize, "o");
\r
2618 pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "sg", squareSize, "o");
\r
2619 pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "ss", squareSize, "o");
\r
2620 pieceBitmap[2][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "w");
\r
2621 pieceBitmap[2][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "w");
\r
2622 pieceBitmap[2][WhiteBishop] = DoLoadBitmap(hInst, "sr", squareSize, "w");
\r
2623 pieceBitmap[2][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "w");
\r
2624 pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "w");
\r
2625 pieceBitmap[2][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "w");
\r
2626 pieceBitmap[2][WhiteFerz] = DoLoadBitmap(hInst, "sw", squareSize, "w");
\r
2627 pieceBitmap[2][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "w");
\r
2628 pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "sp", squareSize, "w");
\r
2629 pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "sn", squareSize, "w");
\r
2630 pieceBitmap[2][WhiteCardinal] = DoLoadBitmap(hInst, "sr", squareSize, "w");
\r
2631 pieceBitmap[2][WhiteDragon] = DoLoadBitmap(hInst, "sr", squareSize, "w");
\r
2632 pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "sl", squareSize, "w");
\r
2633 pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "sw", squareSize, "w");
\r
2639 PieceBitmap(ChessSquare p, int kind)
\r
2641 if ((int) p >= (int) BlackPawn)
\r
2642 p = (ChessSquare) ((int) p - (int) BlackPawn + (int) WhitePawn);
\r
2644 return pieceBitmap[kind][(int) p];
\r
2647 /***************************************************************/
\r
2649 #define MIN(a,b) (((a) < (b)) ? (a) : (b))
\r
2650 #define MAX(a,b) (((a) > (b)) ? (a) : (b))
\r
2652 #define MIN3(a,b,c) (((a) < (b) && (a) < (c)) ? (a) : (((b) < (a) && (b) < (c)) ? (b) : (c)))
\r
2653 #define MAX3(a,b,c) (((a) > (b) && (a) > (c)) ? (a) : (((b) > (a) && (b) > (c)) ? (b) : (c)))
\r
2657 SquareToPos(int row, int column, int * x, int * y)
\r
2660 *x = boardRect.left + lineGap + ((BOARD_WIDTH-1)-column) * (squareSize + lineGap);
\r
2661 *y = boardRect.top + lineGap + row * (squareSize + lineGap);
\r
2663 *x = boardRect.left + lineGap + column * (squareSize + lineGap);
\r
2664 *y = boardRect.top + lineGap + ((BOARD_HEIGHT-1)-row) * (squareSize + lineGap);
\r
2669 DrawCoordsOnDC(HDC hdc)
\r
2671 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
2672 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
2673 char str[2] = { NULLCHAR, NULLCHAR };
\r
2674 int oldMode, oldAlign, x, y, start, i;
\r
2678 if (!appData.showCoords)
\r
2681 start = flipView ? 1-(ONE!='1') : 23+(ONE!='1')-BOARD_HEIGHT;
\r
2683 oldBrush = SelectObject(hdc, GetStockObject(BLACK_BRUSH));
\r
2684 oldMode = SetBkMode(hdc, (appData.monoMode ? OPAQUE : TRANSPARENT));
\r
2685 oldAlign = GetTextAlign(hdc);
\r
2686 oldFont = SelectObject(hdc, font[boardSize][COORD_FONT]->hf);
\r
2688 y = boardRect.top + lineGap;
\r
2689 x = boardRect.left + lineGap + gameInfo.holdingsWidth*(squareSize + lineGap);
\r
2691 SetTextAlign(hdc, TA_LEFT|TA_TOP);
\r
2692 for (i = 0; i < BOARD_HEIGHT; i++) {
\r
2693 str[0] = files[start + i];
\r
2694 ExtTextOut(hdc, x + 2, y + 1, 0, NULL, str, 1, NULL);
\r
2695 y += squareSize + lineGap;
\r
2698 start = flipView ? 12-(BOARD_RGHT-BOARD_LEFT) : 12;
\r
2700 SetTextAlign(hdc, TA_RIGHT|TA_BOTTOM);
\r
2701 for (i = 0; i < BOARD_RGHT - BOARD_LEFT; i++) {
\r
2702 str[0] = ranks[start + i];
\r
2703 ExtTextOut(hdc, x + squareSize - 2, y - 1, 0, NULL, str, 1, NULL);
\r
2704 x += squareSize + lineGap;
\r
2707 SelectObject(hdc, oldBrush);
\r
2708 SetBkMode(hdc, oldMode);
\r
2709 SetTextAlign(hdc, oldAlign);
\r
2710 SelectObject(hdc, oldFont);
\r
2714 DrawGridOnDC(HDC hdc)
\r
2718 if (lineGap != 0) {
\r
2719 oldPen = SelectObject(hdc, gridPen);
\r
2720 PolyPolyline(hdc, gridEndpoints, gridVertexCounts, BOARD_WIDTH+BOARD_HEIGHT + 2);
\r
2721 SelectObject(hdc, oldPen);
\r
2725 #define HIGHLIGHT_PEN 0
\r
2726 #define PREMOVE_PEN 1
\r
2729 DrawHighlightOnDC(HDC hdc, BOOLEAN on, int x, int y, int pen)
\r
2732 HPEN oldPen, hPen;
\r
2733 if (lineGap == 0) return;
\r
2735 x1 = boardRect.left +
\r
2736 lineGap/2 + ((BOARD_WIDTH-1)-x) * (squareSize + lineGap);
\r
2737 y1 = boardRect.top +
\r
2738 lineGap/2 + y * (squareSize + lineGap);
\r
2740 x1 = boardRect.left +
\r
2741 lineGap/2 + x * (squareSize + lineGap);
\r
2742 y1 = boardRect.top +
\r
2743 lineGap/2 + ((BOARD_HEIGHT-1)-y) * (squareSize + lineGap);
\r
2745 hPen = pen ? premovePen : highlightPen;
\r
2746 oldPen = SelectObject(hdc, on ? hPen : gridPen);
\r
2747 MoveToEx(hdc, x1, y1, NULL);
\r
2748 LineTo(hdc, x1 + squareSize + lineGap, y1);
\r
2749 LineTo(hdc, x1 + squareSize + lineGap, y1 + squareSize + lineGap);
\r
2750 LineTo(hdc, x1, y1 + squareSize + lineGap);
\r
2751 LineTo(hdc, x1, y1);
\r
2752 SelectObject(hdc, oldPen);
\r
2756 DrawHighlightsOnDC(HDC hdc, HighlightInfo *h, int pen)
\r
2759 for (i=0; i<2; i++) {
\r
2760 if (h->sq[i].x >= 0 && h->sq[i].y >= 0)
\r
2761 DrawHighlightOnDC(hdc, TRUE,
\r
2762 h->sq[i].x, h->sq[i].y,
\r
2767 /* Note: sqcolor is used only in monoMode */
\r
2768 /* Note that this code is largely duplicated in woptions.c,
\r
2769 function DrawSampleSquare, so that needs to be updated too */
\r
2771 DrawPieceOnDC(HDC hdc, ChessSquare piece, int color, int sqcolor, int x, int y, HDC tmphdc)
\r
2773 HBITMAP oldBitmap;
\r
2777 if (appData.blindfold) return;
\r
2779 /* [AS] Use font-based pieces if needed */
\r
2780 if( fontBitmapSquareSize >= 0 && (squareSize > 32 || gameInfo.variant >= VariantShogi)) {
\r
2781 /* Create piece bitmaps, or do nothing if piece set is up to date */
\r
2782 CreatePiecesFromFont();
\r
2784 if( fontBitmapSquareSize == squareSize ) {
\r
2785 int index = TranslatePieceToFontPiece(piece);
\r
2787 SelectObject( tmphdc, hPieceMask[ index ] );
\r
2789 if(appData.upsideDown ? color==flipView : (flipView && gameInfo.variant == VariantShogi))
\r
2790 StretchBlt(hdc, x+squareSize, y+squareSize, -squareSize, -squareSize, tmphdc, 0, 0, squareSize, squareSize, SRCAND);
\r
2794 squareSize, squareSize,
\r
2799 SelectObject( tmphdc, hPieceFace[ index ] );
\r
2801 if(appData.upsideDown ? color==flipView : (flipView && gameInfo.variant == VariantShogi))
\r
2802 StretchBlt(hdc, x+squareSize, y+squareSize, -squareSize, -squareSize, tmphdc, 0, 0, squareSize, squareSize, SRCPAINT);
\r
2806 squareSize, squareSize,
\r
2815 if (appData.monoMode) {
\r
2816 SelectObject(tmphdc, PieceBitmap(piece,
\r
2817 color == sqcolor ? OUTLINE_PIECE : SOLID_PIECE));
\r
2818 BitBlt(hdc, x, y, squareSize, squareSize, tmphdc, 0, 0,
\r
2819 sqcolor ? SRCCOPY : NOTSRCCOPY);
\r
2821 tmpSize = squareSize;
\r
2823 ((piece >= (int)WhiteNightrider && piece <= WhiteGrasshopper) ||
\r
2824 (piece >= (int)BlackNightrider && piece <= BlackGrasshopper)) ) {
\r
2825 /* [HGM] no bitmap available for promoted pieces in Crazyhouse */
\r
2826 /* Bitmaps of smaller size are substituted, but we have to align them */
\r
2827 x += (squareSize - minorSize)>>1;
\r
2828 y += squareSize - minorSize - 2;
\r
2829 tmpSize = minorSize;
\r
2831 if (color || appData.allWhite ) {
\r
2832 oldBitmap = SelectObject(tmphdc, PieceBitmap(piece, WHITE_PIECE));
\r
2834 oldBrush = SelectObject(hdc, whitePieceBrush);
\r
2835 else oldBrush = SelectObject(hdc, blackPieceBrush);
\r
2836 if(appData.upsideDown && color==flipView)
\r
2837 StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, 0x00B8074A);
\r
2839 BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, 0x00B8074A);
\r
2840 /* Use black for outline of white pieces */
\r
2841 SelectObject(tmphdc, PieceBitmap(piece, OUTLINE_PIECE));
\r
2842 if(appData.upsideDown && color==flipView)
\r
2843 StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, SRCAND);
\r
2845 BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, SRCAND);
\r
2847 /* Use square color for details of black pieces */
\r
2848 oldBitmap = SelectObject(tmphdc, PieceBitmap(piece, SOLID_PIECE));
\r
2849 oldBrush = SelectObject(hdc, blackPieceBrush);
\r
2850 if(appData.upsideDown && !flipView)
\r
2851 StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, 0x00B8074A);
\r
2853 BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, 0x00B8074A);
\r
2855 SelectObject(hdc, oldBrush);
\r
2856 SelectObject(tmphdc, oldBitmap);
\r
2860 /* [AS] Compute a drawing mode for a square, based on specified settings (see DrawTile) */
\r
2861 int GetBackTextureMode( int algo )
\r
2863 int result = BACK_TEXTURE_MODE_DISABLED;
\r
2867 case BACK_TEXTURE_MODE_PLAIN:
\r
2868 result = 1; /* Always use identity map */
\r
2870 case BACK_TEXTURE_MODE_FULL_RANDOM:
\r
2871 result = 1 + (myrandom() % 3); /* Pick a transformation at random */
\r
2879 [AS] Compute and save texture drawing info, otherwise we may not be able
\r
2880 to handle redraws cleanly (as random numbers would always be different).
\r
2882 VOID RebuildTextureSquareInfo()
\r
2892 ZeroMemory( &backTextureSquareInfo, sizeof(backTextureSquareInfo) );
\r
2894 if( liteBackTexture != NULL ) {
\r
2895 if( GetObject( liteBackTexture, sizeof(bi), &bi ) > 0 ) {
\r
2896 lite_w = bi.bmWidth;
\r
2897 lite_h = bi.bmHeight;
\r
2901 if( darkBackTexture != NULL ) {
\r
2902 if( GetObject( darkBackTexture, sizeof(bi), &bi ) > 0 ) {
\r
2903 dark_w = bi.bmWidth;
\r
2904 dark_h = bi.bmHeight;
\r
2908 for( row=0; row<BOARD_HEIGHT; row++ ) {
\r
2909 for( col=0; col<BOARD_WIDTH; col++ ) {
\r
2910 if( (col + row) & 1 ) {
\r
2912 if( lite_w >= squareSize && lite_h >= squareSize ) {
\r
2913 if( lite_w >= squareSize*BOARD_WIDTH )
\r
2914 backTextureSquareInfo[row][col].x = (2*col+1)*lite_w/(2*BOARD_WIDTH) - squareSize/2; /* [HGM] cut out of center of virtual square */
\r
2916 backTextureSquareInfo[row][col].x = col * (lite_w - squareSize) / (BOARD_WIDTH-1); /* [HGM] divide by size-1 in stead of size! */
\r
2917 if( lite_h >= squareSize*BOARD_HEIGHT )
\r
2918 backTextureSquareInfo[row][col].y = (2*(BOARD_HEIGHT-row)-1)*lite_h/(2*BOARD_HEIGHT) - squareSize/2;
\r
2920 backTextureSquareInfo[row][col].y = (BOARD_HEIGHT-1-row) * (lite_h - squareSize) / (BOARD_HEIGHT-1);
\r
2921 backTextureSquareInfo[row][col].mode = GetBackTextureMode(liteBackTextureMode);
\r
2926 if( dark_w >= squareSize && dark_h >= squareSize ) {
\r
2927 if( dark_w >= squareSize*BOARD_WIDTH )
\r
2928 backTextureSquareInfo[row][col].x = (2*col+1) * dark_w / (2*BOARD_WIDTH) - squareSize/2;
\r
2930 backTextureSquareInfo[row][col].x = col * (dark_w - squareSize) / (BOARD_WIDTH-1);
\r
2931 if( dark_h >= squareSize*BOARD_HEIGHT )
\r
2932 backTextureSquareInfo[row][col].y = (2*(BOARD_HEIGHT-row)-1) * dark_h / (2*BOARD_HEIGHT) - squareSize/2;
\r
2934 backTextureSquareInfo[row][col].y = (BOARD_HEIGHT-1-row) * (dark_h - squareSize) / (BOARD_HEIGHT-1);
\r
2935 backTextureSquareInfo[row][col].mode = GetBackTextureMode(darkBackTextureMode);
\r
2942 /* [AS] Arrow highlighting support */
\r
2944 static int A_WIDTH = 5; /* Width of arrow body */
\r
2946 #define A_HEIGHT_FACTOR 6 /* Length of arrow "point", relative to body width */
\r
2947 #define A_WIDTH_FACTOR 3 /* Width of arrow "point", relative to body width */
\r
2949 static double Sqr( double x )
\r
2954 static int Round( double x )
\r
2956 return (int) (x + 0.5);
\r
2959 /* Draw an arrow between two points using current settings */
\r
2960 VOID DrawArrowBetweenPoints( HDC hdc, int s_x, int s_y, int d_x, int d_y )
\r
2963 double dx, dy, j, k, x, y;
\r
2965 if( d_x == s_x ) {
\r
2966 int h = (d_y > s_y) ? +A_WIDTH*A_HEIGHT_FACTOR : -A_WIDTH*A_HEIGHT_FACTOR;
\r
2968 arrow[0].x = s_x + A_WIDTH;
\r
2971 arrow[1].x = s_x + A_WIDTH;
\r
2972 arrow[1].y = d_y - h;
\r
2974 arrow[2].x = s_x + A_WIDTH*A_WIDTH_FACTOR;
\r
2975 arrow[2].y = d_y - h;
\r
2980 arrow[4].x = s_x - A_WIDTH*A_WIDTH_FACTOR;
\r
2981 arrow[4].y = d_y - h;
\r
2983 arrow[5].x = s_x - A_WIDTH;
\r
2984 arrow[5].y = d_y - h;
\r
2986 arrow[6].x = s_x - A_WIDTH;
\r
2989 else if( d_y == s_y ) {
\r
2990 int w = (d_x > s_x) ? +A_WIDTH*A_HEIGHT_FACTOR : -A_WIDTH*A_HEIGHT_FACTOR;
\r
2993 arrow[0].y = s_y + A_WIDTH;
\r
2995 arrow[1].x = d_x - w;
\r
2996 arrow[1].y = s_y + A_WIDTH;
\r
2998 arrow[2].x = d_x - w;
\r
2999 arrow[2].y = s_y + A_WIDTH*A_WIDTH_FACTOR;
\r
3004 arrow[4].x = d_x - w;
\r
3005 arrow[4].y = s_y - A_WIDTH*A_WIDTH_FACTOR;
\r
3007 arrow[5].x = d_x - w;
\r
3008 arrow[5].y = s_y - A_WIDTH;
\r
3011 arrow[6].y = s_y - A_WIDTH;
\r
3014 /* [AS] Needed a lot of paper for this! :-) */
\r
3015 dy = (double) (d_y - s_y) / (double) (d_x - s_x);
\r
3016 dx = (double) (s_x - d_x) / (double) (s_y - d_y);
\r
3018 j = sqrt( Sqr(A_WIDTH) / (1.0 + Sqr(dx)) );
\r
3020 k = sqrt( Sqr(A_WIDTH*A_HEIGHT_FACTOR) / (1.0 + Sqr(dy)) );
\r
3025 arrow[0].x = Round(x - j);
\r
3026 arrow[0].y = Round(y + j*dx);
\r
3028 arrow[1].x = Round(x + j);
\r
3029 arrow[1].y = Round(y - j*dx);
\r
3032 x = (double) d_x - k;
\r
3033 y = (double) d_y - k*dy;
\r
3036 x = (double) d_x + k;
\r
3037 y = (double) d_y + k*dy;
\r
3040 arrow[2].x = Round(x + j);
\r
3041 arrow[2].y = Round(y - j*dx);
\r
3043 arrow[3].x = Round(x + j*A_WIDTH_FACTOR);
\r
3044 arrow[3].y = Round(y - j*A_WIDTH_FACTOR*dx);
\r
3049 arrow[5].x = Round(x - j*A_WIDTH_FACTOR);
\r
3050 arrow[5].y = Round(y + j*A_WIDTH_FACTOR*dx);
\r
3052 arrow[6].x = Round(x - j);
\r
3053 arrow[6].y = Round(y + j*dx);
\r
3056 Polygon( hdc, arrow, 7 );
\r
3059 /* [AS] Draw an arrow between two squares */
\r
3060 VOID DrawArrowBetweenSquares( HDC hdc, int s_col, int s_row, int d_col, int d_row )
\r
3062 int s_x, s_y, d_x, d_y;
\r
3069 if( s_col == d_col && s_row == d_row ) {
\r
3073 /* Get source and destination points */
\r
3074 SquareToPos( s_row, s_col, &s_x, &s_y);
\r
3075 SquareToPos( d_row, d_col, &d_x, &d_y);
\r
3078 d_y += squareSize / 4;
\r
3080 else if( d_y < s_y ) {
\r
3081 d_y += 3 * squareSize / 4;
\r
3084 d_y += squareSize / 2;
\r
3088 d_x += squareSize / 4;
\r
3090 else if( d_x < s_x ) {
\r
3091 d_x += 3 * squareSize / 4;
\r
3094 d_x += squareSize / 2;
\r
3097 s_x += squareSize / 2;
\r
3098 s_y += squareSize / 2;
\r
3100 /* Adjust width */
\r
3101 A_WIDTH = squareSize / 14;
\r
3104 stLB.lbStyle = BS_SOLID;
\r
3105 stLB.lbColor = appData.highlightArrowColor;
\r
3108 hpen = CreatePen( PS_SOLID, 2, RGB(0x00,0x00,0x00) );
\r
3109 holdpen = SelectObject( hdc, hpen );
\r
3110 hbrush = CreateBrushIndirect( &stLB );
\r
3111 holdbrush = SelectObject( hdc, hbrush );
\r
3113 DrawArrowBetweenPoints( hdc, s_x, s_y, d_x, d_y );
\r
3115 SelectObject( hdc, holdpen );
\r
3116 SelectObject( hdc, holdbrush );
\r
3117 DeleteObject( hpen );
\r
3118 DeleteObject( hbrush );
\r
3121 BOOL HasHighlightInfo()
\r
3123 BOOL result = FALSE;
\r
3125 if( highlightInfo.sq[0].x >= 0 && highlightInfo.sq[0].y >= 0 &&
\r
3126 highlightInfo.sq[1].x >= 0 && highlightInfo.sq[1].y >= 0 )
\r
3134 BOOL IsDrawArrowEnabled()
\r
3136 BOOL result = FALSE;
\r
3138 if( appData.highlightMoveWithArrow && squareSize >= 32 ) {
\r
3145 VOID DrawArrowHighlight( HDC hdc )
\r
3147 if( IsDrawArrowEnabled() && HasHighlightInfo() ) {
\r
3148 DrawArrowBetweenSquares( hdc,
\r
3149 highlightInfo.sq[0].x, highlightInfo.sq[0].y,
\r
3150 highlightInfo.sq[1].x, highlightInfo.sq[1].y );
\r
3154 HRGN GetArrowHighlightClipRegion( HDC hdc )
\r
3156 HRGN result = NULL;
\r
3158 if( HasHighlightInfo() ) {
\r
3159 int x1, y1, x2, y2;
\r
3160 int sx, sy, dx, dy;
\r
3162 SquareToPos(highlightInfo.sq[0].y, highlightInfo.sq[0].x, &x1, &y1 );
\r
3163 SquareToPos(highlightInfo.sq[1].y, highlightInfo.sq[1].x, &x2, &y2 );
\r
3165 sx = MIN( x1, x2 );
\r
3166 sy = MIN( y1, y2 );
\r
3167 dx = MAX( x1, x2 ) + squareSize;
\r
3168 dy = MAX( y1, y2 ) + squareSize;
\r
3170 result = CreateRectRgn( sx, sy, dx, dy );
\r
3177 Warning: this function modifies the behavior of several other functions.
\r
3179 Basically, Winboard is optimized to avoid drawing the whole board if not strictly
\r
3180 needed. Unfortunately, the decision whether or not to perform a full or partial
\r
3181 repaint is scattered all over the place, which is not good for features such as
\r
3182 "arrow highlighting" that require a full repaint of the board.
\r
3184 So, I've tried to patch the code where I thought it made sense (e.g. after or during
\r
3185 user interaction, when speed is not so important) but especially to avoid errors
\r
3186 in the displayed graphics.
\r
3188 In such patched places, I always try refer to this function so there is a single
\r
3189 place to maintain knowledge.
\r
3191 To restore the original behavior, just return FALSE unconditionally.
\r
3193 BOOL IsFullRepaintPreferrable()
\r
3195 BOOL result = FALSE;
\r
3197 if( (appData.highlightLastMove || appData.highlightDragging) && IsDrawArrowEnabled() ) {
\r
3198 /* Arrow may appear on the board */
\r
3206 This function is called by DrawPosition to know whether a full repaint must
\r
3209 Only DrawPosition may directly call this function, which makes use of
\r
3210 some state information. Other function should call DrawPosition specifying
\r
3211 the repaint flag, and can use IsFullRepaintPreferrable if needed.
\r
3213 BOOL DrawPositionNeedsFullRepaint()
\r
3215 BOOL result = FALSE;
\r
3218 Probably a slightly better policy would be to trigger a full repaint
\r
3219 when animInfo.piece changes state (i.e. empty -> non-empty and viceversa),
\r
3220 but animation is fast enough that it's difficult to notice.
\r
3222 if( animInfo.piece == EmptySquare ) {
\r
3223 if( (appData.highlightLastMove || appData.highlightDragging) && IsDrawArrowEnabled() /*&& HasHighlightInfo()*/ ) {
\r
3232 DrawBoardOnDC(HDC hdc, Board board, HDC tmphdc)
\r
3234 int row, column, x, y, square_color, piece_color;
\r
3235 ChessSquare piece;
\r
3237 HDC texture_hdc = NULL;
\r
3239 /* [AS] Initialize background textures if needed */
\r
3240 if( liteBackTexture != NULL || darkBackTexture != NULL ) {
\r
3241 static int backTextureBoardSize; /* [HGM] boardsize: also new texture if board format changed */
\r
3242 if( backTextureSquareSize != squareSize
\r
3243 || backTextureBoardSize != BOARD_WIDTH+BOARD_FILES*BOARD_HEIGHT) {
\r
3244 backTextureBoardSize = BOARD_WIDTH+BOARD_FILES*BOARD_HEIGHT;
\r
3245 backTextureSquareSize = squareSize;
\r
3246 RebuildTextureSquareInfo();
\r
3249 texture_hdc = CreateCompatibleDC( hdc );
\r
3252 for (row = 0; row < BOARD_HEIGHT; row++) {
\r
3253 for (column = 0; column < BOARD_WIDTH; column++) {
\r
3255 SquareToPos(row, column, &x, &y);
\r
3257 piece = board[row][column];
\r
3259 square_color = ((column + row) % 2) == 1;
\r
3260 if( gameInfo.variant == VariantXiangqi ) {
\r
3261 square_color = !InPalace(row, column);
\r
3262 if(BOARD_HEIGHT&1) { if(row==BOARD_HEIGHT/2) square_color ^= 1; }
\r
3263 else if(row < BOARD_HEIGHT/2) square_color ^= 1;
\r
3265 piece_color = (int) piece < (int) BlackPawn;
\r
3268 /* [HGM] holdings file: light square or black */
\r
3269 if(column == BOARD_LEFT-2) {
\r
3270 if( row > BOARD_HEIGHT - gameInfo.holdingsSize - 1 )
\r
3273 DisplayHoldingsCount(hdc, x, y, 0, 0); /* black out */
\r
3277 if(column == BOARD_RGHT + 1 ) {
\r
3278 if( row < gameInfo.holdingsSize )
\r
3281 DisplayHoldingsCount(hdc, x, y, 0, 0);
\r
3285 if(column == BOARD_LEFT-1 ) /* left align */
\r
3286 DisplayHoldingsCount(hdc, x, y, flipView, (int) board[row][column]);
\r
3287 else if( column == BOARD_RGHT) /* right align */
\r
3288 DisplayHoldingsCount(hdc, x, y, !flipView, (int) board[row][column]);
\r
3290 if (appData.monoMode) {
\r
3291 if (piece == EmptySquare) {
\r
3292 BitBlt(hdc, x, y, squareSize, squareSize, 0, 0, 0,
\r
3293 square_color ? WHITENESS : BLACKNESS);
\r
3295 DrawPieceOnDC(hdc, piece, piece_color, square_color, x, y, tmphdc);
\r
3298 else if( backTextureSquareInfo[row][column].mode > 0 ) {
\r
3299 /* [AS] Draw the square using a texture bitmap */
\r
3300 HBITMAP hbm = SelectObject( texture_hdc, square_color ? liteBackTexture : darkBackTexture );
\r
3301 int r = row, c = column; // [HGM] do not flip board in flipView
\r
3302 if(flipView) { r = BOARD_HEIGHT-1 - r; c = BOARD_WIDTH-1 - c; }
\r
3305 squareSize, squareSize,
\r
3308 backTextureSquareInfo[r][c].mode,
\r
3309 backTextureSquareInfo[r][c].x,
\r
3310 backTextureSquareInfo[r][c].y );
\r
3312 SelectObject( texture_hdc, hbm );
\r
3314 if (piece != EmptySquare) {
\r
3315 DrawPieceOnDC(hdc, piece, piece_color, -1, x, y, tmphdc);
\r
3319 HBRUSH brush = square_color ? lightSquareBrush : darkSquareBrush;
\r
3321 oldBrush = SelectObject(hdc, brush );
\r
3322 BitBlt(hdc, x, y, squareSize, squareSize, 0, 0, 0, PATCOPY);
\r
3323 SelectObject(hdc, oldBrush);
\r
3324 if (piece != EmptySquare)
\r
3325 DrawPieceOnDC(hdc, piece, piece_color, -1, x, y, tmphdc);
\r
3330 if( texture_hdc != NULL ) {
\r
3331 DeleteDC( texture_hdc );
\r
3335 int saveDiagFlag = 0; FILE *diagFile; // [HGM] diag
\r
3336 void fputDW(FILE *f, int x)
\r
3338 fputc(x & 255, f);
\r
3339 fputc(x>>8 & 255, f);
\r
3340 fputc(x>>16 & 255, f);
\r
3341 fputc(x>>24 & 255, f);
\r
3344 #define MAX_CLIPS 200 /* more than enough */
\r
3347 DrawLogoOnDC(HDC hdc, RECT logoRect, HBITMAP logo)
\r
3349 // HBITMAP bufferBitmap;
\r
3354 int w = 100, h = 50;
\r
3356 if(logo == NULL) return;
\r
3357 // GetClientRect(hwndMain, &Rect);
\r
3358 // bufferBitmap = CreateCompatibleBitmap(hdc, Rect.right-Rect.left+1,
\r
3359 // Rect.bottom-Rect.top+1);
\r
3360 tmphdc = CreateCompatibleDC(hdc);
\r
3361 hbm = SelectObject(tmphdc, logo);
\r
3362 if( GetObject( logo, sizeof(bi), &bi ) > 0 ) {
\r
3366 StretchBlt(hdc, logoRect.left, logoRect.top, logoRect.right - logoRect.left,
\r
3367 logoRect.bottom - logoRect.top, tmphdc, 0, 0, w, h, SRCCOPY);
\r
3368 SelectObject(tmphdc, hbm);
\r
3372 static HDC hdcSeek;
\r
3374 // [HGM] seekgraph
\r
3375 void DrawSeekAxis( int x, int y, int xTo, int yTo )
\r
3378 HPEN hp = SelectObject( hdcSeek, gridPen );
\r
3379 MoveToEx( hdcSeek, boardRect.left+x, boardRect.top+y, &stPt );
\r
3380 LineTo( hdcSeek, boardRect.left+xTo, boardRect.top+yTo );
\r
3381 SelectObject( hdcSeek, hp );
\r
3384 // front-end wrapper for drawing functions to do rectangles
\r
3385 void DrawSeekBackground( int left, int top, int right, int bottom )
\r
3390 if (hdcSeek == NULL) {
\r
3391 hdcSeek = GetDC(hwndMain);
\r
3392 if (!appData.monoMode) {
\r
3393 SelectPalette(hdcSeek, hPal, FALSE);
\r
3394 RealizePalette(hdcSeek);
\r
3397 hp = SelectObject( hdcSeek, gridPen );
\r
3398 rc.top = boardRect.top+top; rc.left = boardRect.left+left;
\r
3399 rc.bottom = boardRect.top+bottom; rc.right = boardRect.left+right;
\r
3400 FillRect( hdcSeek, &rc, lightSquareBrush );
\r
3401 SelectObject( hdcSeek, hp );
\r
3404 // front-end wrapper for putting text in graph
\r
3405 void DrawSeekText(char *buf, int x, int y)
\r
3408 SetBkMode( hdcSeek, TRANSPARENT );
\r
3409 GetTextExtentPoint32( hdcSeek, buf, strlen(buf), &stSize );
\r
3410 TextOut( hdcSeek, boardRect.left+x-3, boardRect.top+y-stSize.cy/2, buf, strlen(buf) );
\r
3413 void DrawSeekDot(int x, int y, int color)
\r
3415 int square = color & 0x80;
\r
3416 HBRUSH oldBrush = SelectObject(hdcSeek,
\r
3417 color == 0 ? markerBrush : color == 1 ? darkSquareBrush : explodeBrush);
\r
3420 Rectangle(hdcSeek, boardRect.left+x - squareSize/9, boardRect.top+y - squareSize/9,
\r
3421 boardRect.left+x + squareSize/9, boardRect.top+y + squareSize/9);
\r
3423 Ellipse(hdcSeek, boardRect.left+x - squareSize/8, boardRect.top+y - squareSize/8,
\r
3424 boardRect.left+x + squareSize/8, boardRect.top+y + squareSize/8);
\r
3425 SelectObject(hdcSeek, oldBrush);
\r
3429 HDCDrawPosition(HDC hdc, BOOLEAN repaint, Board board)
\r
3431 static Board lastReq[2], lastDrawn[2];
\r
3432 static HighlightInfo lastDrawnHighlight, lastDrawnPremove;
\r
3433 static int lastDrawnFlipView = 0;
\r
3434 static int lastReqValid[2] = {0, 0}, lastDrawnValid[2] = {0, 0};
\r
3435 int releaseDC, x, y, x2, y2, row, column, num_clips = 0, i;
\r
3438 HBITMAP bufferBitmap;
\r
3439 HBITMAP oldBitmap;
\r
3441 HRGN clips[MAX_CLIPS];
\r
3442 ChessSquare dragged_piece = EmptySquare;
\r
3443 int nr = twoBoards*partnerUp;
\r
3445 /* I'm undecided on this - this function figures out whether a full
\r
3446 * repaint is necessary on its own, so there's no real reason to have the
\r
3447 * caller tell it that. I think this can safely be set to FALSE - but
\r
3448 * if we trust the callers not to request full repaints unnessesarily, then
\r
3449 * we could skip some clipping work. In other words, only request a full
\r
3450 * redraw when the majority of pieces have changed positions (ie. flip,
\r
3451 * gamestart and similar) --Hawk
\r
3453 Boolean fullrepaint = repaint;
\r
3455 if(DrawSeekGraph()) return; // [HG} seekgraph: suppress printing board if seek graph up
\r
3457 if( DrawPositionNeedsFullRepaint() ) {
\r
3458 fullrepaint = TRUE;
\r
3461 if (board == NULL) {
\r
3462 if (!lastReqValid[nr]) {
\r
3465 board = lastReq[nr];
\r
3467 CopyBoard(lastReq[nr], board);
\r
3468 lastReqValid[nr] = 1;
\r
3471 if (doingSizing) {
\r
3475 if (IsIconic(hwndMain)) {
\r
3479 if (hdc == NULL) {
\r
3480 hdc = GetDC(hwndMain);
\r
3481 if (!appData.monoMode) {
\r
3482 SelectPalette(hdc, hPal, FALSE);
\r
3483 RealizePalette(hdc);
\r
3487 releaseDC = FALSE;
\r
3490 /* Create some work-DCs */
\r
3491 hdcmem = CreateCompatibleDC(hdc);
\r
3492 tmphdc = CreateCompatibleDC(hdc);
\r
3494 /* If dragging is in progress, we temporarely remove the piece */
\r
3495 /* [HGM] or temporarily decrease count if stacked */
\r
3496 /* !! Moved to before board compare !! */
\r
3497 if (dragInfo.from.x >= 0 && dragInfo.pos.x >= 0) {
\r
3498 dragged_piece = board[dragInfo.from.y][dragInfo.from.x];
\r
3499 if(dragInfo.from.x == BOARD_LEFT-2 ) {
\r
3500 if(--board[dragInfo.from.y][dragInfo.from.x+1] == 0 )
\r
3501 board[dragInfo.from.y][dragInfo.from.x] = EmptySquare;
\r
3503 if(dragInfo.from.x == BOARD_RGHT+1) {
\r
3504 if(--board[dragInfo.from.y][dragInfo.from.x-1] == 0 )
\r
3505 board[dragInfo.from.y][dragInfo.from.x] = EmptySquare;
\r
3507 board[dragInfo.from.y][dragInfo.from.x] = EmptySquare;
\r
3510 /* Figure out which squares need updating by comparing the
\r
3511 * newest board with the last drawn board and checking if
\r
3512 * flipping has changed.
\r
3514 if (!fullrepaint && lastDrawnValid[nr] && (nr == 1 || lastDrawnFlipView == flipView)) {
\r
3515 for (row = 0; row < BOARD_HEIGHT; row++) { /* [HGM] true size, not 8 */
\r
3516 for (column = 0; column < BOARD_WIDTH; column++) {
\r
3517 if (lastDrawn[nr][row][column] != board[row][column]) {
\r
3518 SquareToPos(row, column, &x, &y);
\r
3519 clips[num_clips++] =
\r
3520 CreateRectRgn(x, y, x + squareSize, y + squareSize);
\r
3524 if(nr == 0) { // [HGM] dual: no highlights on second board
\r
3525 for (i=0; i<2; i++) {
\r
3526 if (lastDrawnHighlight.sq[i].x != highlightInfo.sq[i].x ||
\r
3527 lastDrawnHighlight.sq[i].y != highlightInfo.sq[i].y) {
\r
3528 if (lastDrawnHighlight.sq[i].x >= 0 &&
\r
3529 lastDrawnHighlight.sq[i].y >= 0) {
\r
3530 SquareToPos(lastDrawnHighlight.sq[i].y,
\r
3531 lastDrawnHighlight.sq[i].x, &x, &y);
\r
3532 clips[num_clips++] =
\r
3533 CreateRectRgn(x - lineGap, y - lineGap,
\r
3534 x + squareSize + lineGap, y + squareSize + lineGap);
\r
3536 if (highlightInfo.sq[i].x >= 0 && highlightInfo.sq[i].y >= 0) {
\r
3537 SquareToPos(highlightInfo.sq[i].y, highlightInfo.sq[i].x, &x, &y);
\r
3538 clips[num_clips++] =
\r
3539 CreateRectRgn(x - lineGap, y - lineGap,
\r
3540 x + squareSize + lineGap, y + squareSize + lineGap);
\r
3544 for (i=0; i<2; i++) {
\r
3545 if (lastDrawnPremove.sq[i].x != premoveHighlightInfo.sq[i].x ||
\r
3546 lastDrawnPremove.sq[i].y != premoveHighlightInfo.sq[i].y) {
\r
3547 if (lastDrawnPremove.sq[i].x >= 0 &&
\r
3548 lastDrawnPremove.sq[i].y >= 0) {
\r
3549 SquareToPos(lastDrawnPremove.sq[i].y,
\r
3550 lastDrawnPremove.sq[i].x, &x, &y);
\r
3551 clips[num_clips++] =
\r
3552 CreateRectRgn(x - lineGap, y - lineGap,
\r
3553 x + squareSize + lineGap, y + squareSize + lineGap);
\r
3555 if (premoveHighlightInfo.sq[i].x >= 0 &&
\r
3556 premoveHighlightInfo.sq[i].y >= 0) {
\r
3557 SquareToPos(premoveHighlightInfo.sq[i].y,
\r
3558 premoveHighlightInfo.sq[i].x, &x, &y);
\r
3559 clips[num_clips++] =
\r
3560 CreateRectRgn(x - lineGap, y - lineGap,
\r
3561 x + squareSize + lineGap, y + squareSize + lineGap);
\r
3565 } else { // nr == 1
\r
3566 partnerHighlightInfo.sq[0].y = board[EP_STATUS-4];
\r
3567 partnerHighlightInfo.sq[0].x = board[EP_STATUS-3];
\r
3568 partnerHighlightInfo.sq[1].y = board[EP_STATUS-2];
\r
3569 partnerHighlightInfo.sq[1].x = board[EP_STATUS-1];
\r
3570 for (i=0; i<2; i++) {
\r
3571 if (partnerHighlightInfo.sq[i].x >= 0 &&
\r
3572 partnerHighlightInfo.sq[i].y >= 0) {
\r
3573 SquareToPos(partnerHighlightInfo.sq[i].y,
\r
3574 partnerHighlightInfo.sq[i].x, &x, &y);
\r
3575 clips[num_clips++] =
\r
3576 CreateRectRgn(x - lineGap, y - lineGap,
\r
3577 x + squareSize + lineGap, y + squareSize + lineGap);
\r
3579 if (oldPartnerHighlight.sq[i].x >= 0 &&
\r
3580 oldPartnerHighlight.sq[i].y >= 0) {
\r
3581 SquareToPos(oldPartnerHighlight.sq[i].y,
\r
3582 oldPartnerHighlight.sq[i].x, &x, &y);
\r
3583 clips[num_clips++] =
\r
3584 CreateRectRgn(x - lineGap, y - lineGap,
\r
3585 x + squareSize + lineGap, y + squareSize + lineGap);
\r
3590 fullrepaint = TRUE;
\r
3593 /* Create a buffer bitmap - this is the actual bitmap
\r
3594 * being written to. When all the work is done, we can
\r
3595 * copy it to the real DC (the screen). This avoids
\r
3596 * the problems with flickering.
\r
3598 GetClientRect(hwndMain, &Rect);
\r
3599 bufferBitmap = CreateCompatibleBitmap(hdc, Rect.right-Rect.left+1,
\r
3600 Rect.bottom-Rect.top+1);
\r
3601 oldBitmap = SelectObject(hdcmem, bufferBitmap);
\r
3602 if (!appData.monoMode) {
\r
3603 SelectPalette(hdcmem, hPal, FALSE);
\r
3606 /* Create clips for dragging */
\r
3607 if (!fullrepaint) {
\r
3608 if (dragInfo.from.x >= 0) {
\r
3609 SquareToPos(dragInfo.from.y, dragInfo.from.x, &x, &y);
\r
3610 clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);
\r
3612 if (dragInfo.start.x >= 0) {
\r
3613 SquareToPos(dragInfo.start.y, dragInfo.start.x, &x, &y);
\r
3614 clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);
\r
3616 if (dragInfo.pos.x >= 0) {
\r
3617 x = dragInfo.pos.x - squareSize / 2;
\r
3618 y = dragInfo.pos.y - squareSize / 2;
\r
3619 clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);
\r
3621 if (dragInfo.lastpos.x >= 0) {
\r
3622 x = dragInfo.lastpos.x - squareSize / 2;
\r
3623 y = dragInfo.lastpos.y - squareSize / 2;
\r
3624 clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);
\r
3628 /* Are we animating a move?
\r
3630 * - remove the piece from the board (temporarely)
\r
3631 * - calculate the clipping region
\r
3633 if (!fullrepaint) {
\r
3634 if (animInfo.piece != EmptySquare) {
\r
3635 board[animInfo.from.y][animInfo.from.x] = EmptySquare;
\r
3636 x = boardRect.left + animInfo.lastpos.x;
\r
3637 y = boardRect.top + animInfo.lastpos.y;
\r
3638 x2 = boardRect.left + animInfo.pos.x;
\r
3639 y2 = boardRect.top + animInfo.pos.y;
\r
3640 clips[num_clips++] = CreateRectRgn(MIN(x,x2), MIN(y,y2), MAX(x,x2)+squareSize, MAX(y,y2)+squareSize);
\r
3641 /* Slight kludge. The real problem is that after AnimateMove is
\r
3642 done, the position on the screen does not match lastDrawn.
\r
3643 This currently causes trouble only on e.p. captures in
\r
3644 atomic, where the piece moves to an empty square and then
\r
3645 explodes. The old and new positions both had an empty square
\r
3646 at the destination, but animation has drawn a piece there and
\r
3647 we have to remember to erase it. [HGM] moved until after setting lastDrawn */
\r
3648 lastDrawn[0][animInfo.to.y][animInfo.to.x] = animInfo.piece;
\r
3652 /* No clips? Make sure we have fullrepaint set to TRUE */
\r
3653 if (num_clips == 0)
\r
3654 fullrepaint = TRUE;
\r
3656 /* Set clipping on the memory DC */
\r
3657 if (!fullrepaint) {
\r
3658 SelectClipRgn(hdcmem, clips[0]);
\r
3659 for (x = 1; x < num_clips; x++) {
\r
3660 if (ExtSelectClipRgn(hdcmem, clips[x], RGN_OR) == ERROR)
\r
3661 abort(); // this should never ever happen!
\r
3665 /* Do all the drawing to the memory DC */
\r
3666 if(explodeInfo.radius) { // [HGM] atomic
\r
3668 int x, y, r=(explodeInfo.radius * squareSize)/100;
\r
3669 ChessSquare piece = board[explodeInfo.fromY][explodeInfo.fromX];
\r
3670 board[explodeInfo.fromY][explodeInfo.fromX] = EmptySquare; // suppress display of capturer
\r
3671 SquareToPos(explodeInfo.toY, explodeInfo.toX, &x, &y);
\r
3672 x += squareSize/2;
\r
3673 y += squareSize/2;
\r
3674 if(!fullrepaint) {
\r
3675 clips[num_clips] = CreateRect