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 static HWND savedHwnd;
\r
190 HWND hCommPort = NULL; /* currently open comm port */
\r
191 static HWND hwndPause; /* pause button */
\r
192 static HBITMAP pieceBitmap[3][(int) BlackPawn]; /* [HGM] nr of bitmaps referred to bP in stead of wK */
\r
193 static HBRUSH lightSquareBrush, darkSquareBrush,
\r
194 blackSquareBrush, /* [HGM] for band between board and holdings */
\r
195 explodeBrush, /* [HGM] atomic */
\r
196 markerBrush, /* [HGM] markers */
\r
197 whitePieceBrush, blackPieceBrush, iconBkgndBrush /*, outlineBrush*/;
\r
198 static POINT gridEndpoints[(BOARD_RANKS + BOARD_FILES + 2) * 2];
\r
199 static DWORD gridVertexCounts[BOARD_RANKS + BOARD_FILES + 2];
\r
200 static HPEN gridPen = NULL;
\r
201 static HPEN highlightPen = NULL;
\r
202 static HPEN premovePen = NULL;
\r
203 static NPLOGPALETTE pLogPal;
\r
204 static BOOL paletteChanged = FALSE;
\r
205 static HICON iconWhite, iconBlack, iconCurrent;
\r
206 static int doingSizing = FALSE;
\r
207 static int lastSizing = 0;
\r
208 static int prevStderrPort;
\r
209 static HBITMAP userLogo;
\r
211 static HBITMAP liteBackTexture = NULL;
\r
212 static HBITMAP darkBackTexture = NULL;
\r
213 static int liteBackTextureMode = BACK_TEXTURE_MODE_PLAIN;
\r
214 static int darkBackTextureMode = BACK_TEXTURE_MODE_PLAIN;
\r
215 static int backTextureSquareSize = 0;
\r
216 static struct { int x; int y; int mode; } backTextureSquareInfo[BOARD_RANKS][BOARD_FILES];
\r
218 #if __GNUC__ && !defined(_winmajor)
\r
219 #define oldDialog 0 /* cygwin doesn't define _winmajor; mingw does */
\r
221 #if defined(_winmajor)
\r
222 #define oldDialog (_winmajor < 4)
\r
224 #define oldDialog 0
\r
228 #define INTERNATIONAL
\r
230 #ifdef INTERNATIONAL
\r
231 # define _(s) T_(s)
\r
237 # define Translate(x, y)
\r
238 # define LoadLanguageFile(s)
\r
241 #ifdef INTERNATIONAL
\r
243 Boolean barbaric; // flag indicating if translation is needed
\r
245 // list of item numbers used in each dialog (used to alter language at run time)
\r
247 #define ABOUTBOX -1 /* not sure why these are needed */
\r
248 #define ABOUTBOX2 -1
\r
250 int dialogItems[][40] = {
\r
251 { ABOUTBOX, IDOK, OPT_MESS, 400 },
\r
252 { DLG_TimeControl, IDC_Babble, OPT_TCUseMoves, OPT_TCUseInc, OPT_TCUseFixed,
\r
253 OPT_TCtext1, OPT_TCtext2, OPT_TCitext1, OPT_TCitext2, OPT_TCftext, GPB_Factors, IDC_Factor1, IDC_Factor2, IDOK, IDCANCEL },
\r
254 { DLG_LoadOptions, OPT_Autostep, OPT_AStext1, IDOK, IDCANCEL },
\r
255 { DLG_SaveOptions, OPT_Autosave, OPT_AVPrompt, OPT_AVToFile, OPT_AVBrowse,
\r
256 801, OPT_PGN, OPT_Old, OPT_OutOfBookInfo, IDOK, IDCANCEL },
\r
257 { 1536, 1090, IDC_Directories, 1089, 1091, IDOK, IDCANCEL, 1038, IDC_IndexNr, 1037 },
\r
258 { DLG_CommPort, IDOK, IDCANCEL, IDC_Port, IDC_Rate, IDC_Bits, IDC_Parity,
\r
259 IDC_Stop, IDC_Flow, OPT_SerialHelp },
\r
260 { DLG_EditComment, IDOK, OPT_CancelComment, OPT_ClearComment, OPT_EditComment },
\r
261 { DLG_PromotionKing, PB_Chancellor, PB_Archbishop, PB_Queen, PB_Rook,
\r
262 PB_Bishop, PB_Knight, PB_King, IDCANCEL, IDC_Yes, IDC_No, IDC_Centaur },
\r
263 { ABOUTBOX2, IDC_ChessBoard },
\r
264 { DLG_GameList, OPT_GameListLoad, OPT_GameListPrev, OPT_GameListNext,
\r
265 OPT_GameListClose, IDC_GameListDoFilter },
\r
266 { DLG_EditTags, IDOK, OPT_TagsCancel, OPT_EditTags },
\r
267 { DLG_Error, IDOK },
\r
268 { DLG_Colorize, IDOK, IDCANCEL, OPT_ChooseColor, OPT_Bold, OPT_Italic,
\r
269 OPT_Underline, OPT_Strikeout, OPT_Sample },
\r
270 { DLG_Question, IDOK, IDCANCEL, OPT_QuestionText },
\r
271 { DLG_Startup, IDC_Welcome, OPT_ChessEngine, OPT_ChessServer, OPT_View,
\r
272 IDC_SPECIFY_ENG_STATIC, IDC_SPECIFY_SERVER_STATIC, OPT_AnyAdditional,
\r
273 IDOK, IDCANCEL, IDM_HELPCONTENTS },
\r
274 { DLG_IndexNumber, IDC_Index },
\r
275 { DLG_TypeInMove, IDOK, IDCANCEL },
\r
276 { DLG_TypeInName, IDOK, IDCANCEL },
\r
277 { DLG_Sound, IDC_Event, OPT_NoSound, OPT_DefaultBeep, OPT_BuiltInSound,
\r
278 OPT_WavFile, OPT_BrowseSound, OPT_DefaultSounds, IDOK, IDCANCEL, OPT_PlaySound },
\r
279 { DLG_GeneralOptions, IDOK, IDCANCEL, OPT_AlwaysOnTop, OPT_HighlightLastMove,
\r
280 OPT_AlwaysQueen, OPT_PeriodicUpdates, OPT_AnimateDragging, OPT_PonderNextMove,
\r
281 OPT_AnimateMoving, OPT_PopupExitMessage, OPT_AutoFlag, OPT_PopupMoveErrors,
\r
282 OPT_AutoFlipView, OPT_ShowButtonBar, OPT_AutoRaiseBoard, OPT_ShowCoordinates,
\r
283 OPT_Blindfold, OPT_ShowThinking, OPT_HighlightDragging, OPT_TestLegality,
\r
284 OPT_SaveExtPGN, OPT_HideThinkFromHuman, OPT_ExtraInfoInMoveHistory,
\r
285 OPT_HighlightMoveArrow, OPT_AutoLogo ,OPT_SmartMove },
\r
286 { DLG_IcsOptions, IDOK, IDCANCEL, OPT_AutoComment, OPT_AutoKibitz, OPT_AutoObserve,
\r
287 OPT_GetMoveList, OPT_LocalLineEditing, OPT_QuietPlay, OPT_SeekGraph, OPT_AutoRefresh,
\r
288 OPT_BgObserve, OPT_DualBoard, OPT_Premove, OPT_PremoveWhite, OPT_PremoveBlack,
\r
289 OPT_SmartMove, OPT_IcsAlarm, IDC_Sec, OPT_ChooseShoutColor, OPT_ChooseSShoutColor,
\r
290 OPT_ChooseChannel1Color, OPT_ChooseChannelColor, OPT_ChooseKibitzColor,
\r
291 OPT_ChooseTellColor, OPT_ChooseChallengeColor, OPT_ChooseRequestColor,
\r
292 OPT_ChooseSeekColor, OPT_ChooseNormalColor, OPT_ChooseBackgroundColor,
\r
293 OPT_DefaultColors, OPT_DontColorize, IDC_Boxes, GPB_Colors, GPB_Premove,
\r
294 GPB_General, GPB_Alarm },
\r
295 { DLG_BoardOptions, IDOK, IDCANCEL, OPT_SizeTiny, OPT_SizeTeeny, OPT_SizeDinky,
\r
296 OPT_SizePetite, OPT_SizeSlim, OPT_SizeSmall, OPT_SizeMediocre, OPT_SizeMiddling,
\r
297 OPT_SizeAverage, OPT_SizeModerate, OPT_SizeMedium, OPT_SizeBulky, OPT_SizeLarge,
\r
298 OPT_SizeBig, OPT_SizeHuge, OPT_SizeGiant, OPT_SizeColossal, OPT_SizeTitanic,
\r
299 OPT_ChooseLightSquareColor, OPT_ChooseDarkSquareColor, OPT_ChooseWhitePieceColor,
\r
300 OPT_ChooseBlackPieceColor, OPT_ChooseHighlightSquareColor, OPT_ChoosePremoveHighlightColor,
\r
301 OPT_Monochrome, OPT_AllWhite, OPT_UpsideDown, OPT_DefaultBoardColors, GPB_Colors,
\r
302 IDC_Light, IDC_Dark, IDC_White, IDC_Black, IDC_High, IDC_PreHigh, GPB_Size },
\r
303 { DLG_NewVariant, IDOK, IDCANCEL, OPT_VariantNormal, OPT_VariantFRC, OPT_VariantWildcastle,
\r
304 OPT_VariantNocastle, OPT_VariantLosers, OPT_VariantGiveaway, OPT_VariantSuicide,
\r
305 OPT_Variant3Check, OPT_VariantTwoKings, OPT_VariantAtomic, OPT_VariantCrazyhouse,
\r
306 OPT_VariantBughouse, OPT_VariantTwilight, OPT_VariantShogi, OPT_VariantSuper,
\r
307 OPT_VariantKnightmate, OPT_VariantBerolina, OPT_VariantCylinder, OPT_VariantFairy,
\r
308 OPT_VariantMakruk, OPT_VariantGothic, OPT_VariantCapablanca, OPT_VariantJanus,
\r
309 OPT_VariantCRC, OPT_VariantFalcon, OPT_VariantCourier, OPT_VariantGreat, OPT_VariantSChess,
\r
310 OPT_VariantShatranj, OPT_VariantXiangqi, GPB_Variant, GPB_Board, IDC_Height,
\r
311 IDC_Width, IDC_Hand, IDC_Pieces, IDC_Def },
\r
312 { DLG_Fonts, IDOK, IDCANCEL, OPT_ChooseClockFont, OPT_ChooseMessageFont,
\r
313 OPT_ChooseCoordFont, OPT_ChooseTagFont, OPT_ChooseCommentsFont, OPT_ChooseConsoleFont, OPT_ChooseMoveHistoryFont, OPT_DefaultFonts,
\r
314 OPT_ClockFont, OPT_MessageFont, OPT_CoordFont, OPT_EditTagsFont,
\r
315 OPT_CommentsFont, OPT_MessageFont5, GPB_Current, GPB_All, OPT_MessageFont6 },
\r
316 { DLG_NewGameFRC, IDC_NFG_Label, IDC_NFG_Random, IDOK, IDCANCEL },
\r
317 { DLG_GameListOptions, IDC_GLT, IDC_GLT_Up, IDC_GLT_Down, IDC_GLT_Restore,
\r
318 IDC_GLT_Default, IDOK, IDCANCEL, IDC_GLT_RestoreTo },
\r
319 { DLG_MoveHistory },
\r
320 { DLG_EvalGraph },
\r
321 { DLG_EngineOutput, IDC_EngineLabel1, IDC_Engine1_NPS, IDC_EngineLabel2, IDC_Engine2_NPS },
\r
322 { DLG_Chat, IDC_Partner, IDC_Clear, IDC_Send, },
\r
323 { DLG_EnginePlayOptions, IDC_EpPonder, IDC_EpShowThinking, IDC_EpHideThinkingHuman,
\r
324 IDC_EpPeriodicUpdates, GPB_Adjudications, IDC_Draw, IDC_Moves, IDC_Threshold,
\r
325 IDC_Centi, IDC_TestClaims, IDC_DetectMates, IDC_MaterialDraws, IDC_TrivialDraws,
\r
326 GPB_Apply, IDC_Rule, IDC_Repeats, IDC_ScoreAbs1, IDC_ScoreAbs2, IDOK, IDCANCEL },
\r
327 { DLG_OptionsUCI, IDC_PolyDir, IDC_BrowseForPolyglotDir, IDC_Hash, IDC_Path,
\r
328 IDC_BrowseForEGTB, IDC_Cache, IDC_UseBook, IDC_BrowseForBook, IDC_CPU, IDC_OwnBook1,
\r
329 IDC_OwnBook2, IDC_Depth, IDC_Variation, IDC_DefGames, IDOK, IDCANCEL },
\r
333 static char languageBuf[50000], *foreign[1000], *english[1000], *languageFile[MSG_SIZ];
\r
334 static int lastChecked;
\r
335 static char oldLanguage[MSG_SIZ], *menuText[10][30];
\r
336 extern int tinyLayout;
\r
337 extern char * menuBarText[][10];
\r
340 LoadLanguageFile(char *name)
\r
341 { //load the file with translations, and make a list of the strings to be translated, and their translations
\r
343 int i=0, j=0, n=0, k;
\r
346 if(!name || name[0] == NULLCHAR) return;
\r
347 snprintf(buf, MSG_SIZ, "%s%s", name, strchr(name, '.') ? "" : ".lng"); // auto-append lng extension
\r
348 if(!strcmp(buf, oldLanguage)) { barbaric = 1; return; } // this language already loaded; just switch on
\r
349 if((f = fopen(buf, "r")) == NULL) return;
\r
350 while((k = fgetc(f)) != EOF) {
\r
351 if(i >= sizeof(languageBuf)) { DisplayError("Language file too big", 0); return; }
\r
352 languageBuf[i] = k;
\r
354 if(languageBuf[n] == '"' && languageBuf[i-1] == '"') {
\r
356 if(p = strstr(languageBuf + n + 1, "\" === \"")) {
\r
357 if(p > languageBuf+n+2 && p+8 < languageBuf+i) {
\r
358 if(j >= sizeof(english)) { DisplayError("Too many translated strings", 0); return; }
\r
359 english[j] = languageBuf + n + 1; *p = 0;
\r
360 foreign[j++] = p + 7; languageBuf[i-1] = 0;
\r
361 //if(appData.debugMode) fprintf(debugFP, "translation: replace '%s' by '%s'\n", english[j-1], foreign[j-1]);
\r
366 } else if(i > 0 && languageBuf[i-1] == '\\') {
\r
368 case 'n': k = '\n'; break;
\r
369 case 'r': k = '\r'; break;
\r
370 case 't': k = '\t'; break;
\r
372 languageBuf[--i] = k;
\r
377 barbaric = (j != 0);
\r
378 safeStrCpy(oldLanguage, buf, sizeof(oldLanguage)/sizeof(oldLanguage[0]) );
\r
383 { // return the translation of the given string
\r
384 // efficiency can be improved a lot...
\r
386 //if(appData.debugMode) fprintf(debugFP, "T_(%s)\n", s);
\r
387 if(!barbaric) return s;
\r
388 if(!s) return ""; // sanity
\r
389 while(english[i]) {
\r
390 if(!strcmp(s, english[i])) return foreign[i];
\r
397 Translate(HWND hDlg, int dialogID)
\r
398 { // translate all text items in the given dialog
\r
400 char buf[MSG_SIZ], *s;
\r
401 //if(appData.debugMode) fprintf(debugFP, "Translate(%d)\n", dialogID);
\r
402 if(!barbaric) return;
\r
403 while(dialogItems[i][0] && dialogItems[i][0] != dialogID) i++; // find the dialog description
\r
404 if(dialogItems[i][0] != dialogID) return; // unknown dialog, should not happen
\r
405 GetWindowText( hDlg, buf, MSG_SIZ );
\r
407 //if(appData.debugMode) fprintf(debugFP, "WindowText '%s' -> '%s'\n", buf, s);
\r
408 if(strcmp(buf, s)) SetWindowText(hDlg, s); // replace by translated string (if different)
\r
409 for(j=1; k=dialogItems[i][j]; j++) { // translate all listed dialog items
\r
410 GetDlgItemText(hDlg, k, buf, MSG_SIZ);
\r
411 if(strlen(buf) == 0) continue;
\r
413 if(strcmp(buf, s)) SetDlgItemText(hDlg, k, s); // replace by translated string (if different)
\r
418 TranslateMenus(int addLanguage)
\r
421 WIN32_FIND_DATA fileData;
\r
423 #define IDM_English 1895
\r
425 HMENU mainMenu = GetMenu(hwndMain);
\r
426 for (i=GetMenuItemCount(mainMenu)-1; i>=0; i--) {
\r
427 HMENU subMenu = GetSubMenu(mainMenu, i);
\r
428 ModifyMenu(mainMenu, i, MF_STRING|MF_BYPOSITION|MF_POPUP|EnableMenuItem(mainMenu, i, MF_BYPOSITION),
\r
429 (UINT) subMenu, T_(menuBarText[tinyLayout][i]));
\r
430 for(j=GetMenuItemCount(subMenu)-1; j>=0; j--){
\r
432 UINT k = GetMenuItemID(subMenu, j);
\r
434 safeStrCpy(buf, menuText[i][j], sizeof(buf)/sizeof(buf[0]) ); else {
\r
435 GetMenuString(subMenu, j, buf, MSG_SIZ, MF_BYPOSITION);
\r
436 menuText[i][j] = strdup(buf); // remember original on first change
\r
438 if(buf[0] == NULLCHAR) continue;
\r
439 //fprintf(debugFP, "menu(%d,%d) = %s (%08x, %08x) %d\n", i, j, buf, mainMenu, subMenu, k);
\r
440 ModifyMenu(subMenu, j, MF_STRING|MF_BYPOSITION
\r
441 |CheckMenuItem(subMenu, j, MF_BYPOSITION)
\r
442 |EnableMenuItem(subMenu, j, MF_BYPOSITION), k, T_(buf));
\r
445 DrawMenuBar(hwndMain);
\r
448 if(!addLanguage) return;
\r
449 if((hFind = FindFirstFile("*.LNG", &fileData)) != INVALID_HANDLE_VALUE) {
\r
450 HMENU mainMenu = GetMenu(hwndMain);
\r
451 HMENU subMenu = GetSubMenu(mainMenu, GetMenuItemCount(mainMenu)-1);
\r
452 AppendMenu(subMenu, MF_SEPARATOR, (UINT_PTR) 0, NULL);
\r
453 AppendMenu(subMenu, MF_ENABLED|MF_STRING|(barbaric?MF_UNCHECKED:MF_CHECKED), (UINT_PTR) IDM_English, (LPCTSTR) "English");
\r
454 i = 0; lastChecked = IDM_English;
\r
456 char *p, *q = fileData.cFileName;
\r
457 int checkFlag = MF_UNCHECKED;
\r
458 languageFile[i] = strdup(q);
\r
459 if(barbaric && !strcmp(oldLanguage, q)) {
\r
460 checkFlag = MF_CHECKED;
\r
461 lastChecked = IDM_English + i + 1;
\r
462 CheckMenuItem(mainMenu, IDM_English, MF_BYCOMMAND|MF_UNCHECKED);
\r
464 *q = ToUpper(*q); while(*++q) *q = ToLower(*q);
\r
465 p = strstr(fileData.cFileName, ".lng");
\r
467 AppendMenu(subMenu, MF_ENABLED|MF_STRING|checkFlag, (UINT_PTR) IDM_English + ++i, (LPCTSTR) fileData.cFileName);
\r
468 } while(FindNextFile(hFind, &fileData));
\r
481 int cliWidth, cliHeight;
\r
484 SizeInfo sizeInfo[] =
\r
486 { "tiny", 21, 0, 1, 1, 0, 0 },
\r
487 { "teeny", 25, 1, 1, 1, 0, 0 },
\r
488 { "dinky", 29, 1, 1, 1, 0, 0 },
\r
489 { "petite", 33, 1, 1, 1, 0, 0 },
\r
490 { "slim", 37, 2, 1, 0, 0, 0 },
\r
491 { "small", 40, 2, 1, 0, 0, 0 },
\r
492 { "mediocre", 45, 2, 1, 0, 0, 0 },
\r
493 { "middling", 49, 2, 0, 0, 0, 0 },
\r
494 { "average", 54, 2, 0, 0, 0, 0 },
\r
495 { "moderate", 58, 3, 0, 0, 0, 0 },
\r
496 { "medium", 64, 3, 0, 0, 0, 0 },
\r
497 { "bulky", 72, 3, 0, 0, 0, 0 },
\r
498 { "large", 80, 3, 0, 0, 0, 0 },
\r
499 { "big", 87, 3, 0, 0, 0, 0 },
\r
500 { "huge", 95, 3, 0, 0, 0, 0 },
\r
501 { "giant", 108, 3, 0, 0, 0, 0 },
\r
502 { "colossal", 116, 4, 0, 0, 0, 0 },
\r
503 { "titanic", 129, 4, 0, 0, 0, 0 },
\r
504 { NULL, 0, 0, 0, 0, 0, 0 }
\r
507 #define MF(x) {x, {{0,}, 0. }, {0, }, 0}
\r
508 MyFont fontRec[NUM_SIZES][NUM_FONTS] =
\r
510 { 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
511 { 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
512 { 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
513 { 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
514 { 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
515 { 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
516 { 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
517 { 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
518 { 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
519 { 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
520 { 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
521 { 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
522 { 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
523 { 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
524 { 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
525 { 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
526 { 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
527 { 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
530 MyFont *font[NUM_SIZES][NUM_FONTS];
\r
539 #define BUTTON_WIDTH (tinyLayout ? 16 : 32)
\r
540 #define N_BUTTONS 5
\r
542 MyButtonDesc buttonDesc[N_BUTTONS] =
\r
544 {"<<", IDM_ToStart, NULL, NULL},
\r
545 {"<", IDM_Backward, NULL, NULL},
\r
546 {"P", IDM_Pause, NULL, NULL},
\r
547 {">", IDM_Forward, NULL, NULL},
\r
548 {">>", IDM_ToEnd, NULL, NULL},
\r
551 int tinyLayout = 0, smallLayout = 0;
\r
552 #define MENU_BAR_ITEMS 9
\r
553 char *menuBarText[2][MENU_BAR_ITEMS+1] = {
\r
554 { N_("&File"), N_("&Edit"), N_("&View"), N_("&Mode"), N_("&Action"), N_("E&ngine"), N_("&Options"), N_("&Help"), NULL },
\r
555 { N_("&F"), N_("&E"), N_("&V"), N_("&M"), N_("&A"), N_("&N"), N_("&O"), N_("&H"), NULL },
\r
559 MySound sounds[(int)NSoundClasses];
\r
560 MyTextAttribs textAttribs[(int)NColorClasses];
\r
562 MyColorizeAttribs colorizeAttribs[] = {
\r
563 { (COLORREF)0, 0, N_("Shout Text") },
\r
564 { (COLORREF)0, 0, N_("SShout/CShout") },
\r
565 { (COLORREF)0, 0, N_("Channel 1 Text") },
\r
566 { (COLORREF)0, 0, N_("Channel Text") },
\r
567 { (COLORREF)0, 0, N_("Kibitz Text") },
\r
568 { (COLORREF)0, 0, N_("Tell Text") },
\r
569 { (COLORREF)0, 0, N_("Challenge Text") },
\r
570 { (COLORREF)0, 0, N_("Request Text") },
\r
571 { (COLORREF)0, 0, N_("Seek Text") },
\r
572 { (COLORREF)0, 0, N_("Normal Text") },
\r
573 { (COLORREF)0, 0, N_("None") }
\r
578 static char *commentTitle;
\r
579 static char *commentText;
\r
580 static int commentIndex;
\r
581 static Boolean editComment = FALSE;
\r
584 char errorTitle[MSG_SIZ];
\r
585 char errorMessage[2*MSG_SIZ];
\r
586 HWND errorDialog = NULL;
\r
587 BOOLEAN moveErrorMessageUp = FALSE;
\r
588 BOOLEAN consoleEcho = TRUE;
\r
589 CHARFORMAT consoleCF;
\r
590 COLORREF consoleBackgroundColor;
\r
592 char *programVersion;
\r
598 typedef int CPKind;
\r
607 SOCKET sock2; /* stderr socket for OpenRcmd */
\r
610 #define INPUT_SOURCE_BUF_SIZE 4096
\r
612 typedef struct _InputSource {
\r
619 char buf[INPUT_SOURCE_BUF_SIZE];
\r
623 InputCallback func;
\r
624 struct _InputSource *second; /* for stderr thread on CPRcmd */
\r
628 InputSource *consoleInputSource;
\r
633 VOID ConsoleOutput(char* data, int length, int forceVisible);
\r
634 VOID ConsoleCreate();
\r
636 ConsoleWndProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam);
\r
637 VOID ColorizeTextPopup(HWND hwnd, ColorClass cc);
\r
638 VOID PrintCommSettings(FILE *f, char *name, DCB *dcb);
\r
639 VOID ParseCommSettings(char *arg, DCB *dcb);
\r
641 StartupDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam);
\r
642 VOID APIENTRY MenuPopup(HWND hwnd, POINT pt, HMENU hmenu, UINT def);
\r
643 void ParseIcsTextMenu(char *icsTextMenuString);
\r
644 VOID PopUpMoveDialog(char firstchar);
\r
645 VOID PopUpNameDialog(char firstchar);
\r
646 VOID UpdateSampleText(HWND hDlg, int id, MyColorizeAttribs *mca);
\r
650 int GameListOptions();
\r
652 int dummy; // [HGM] for obsolete args
\r
654 HWND hwndMain = NULL; /* root window*/
\r
655 HWND hwndConsole = NULL;
\r
656 HWND commentDialog = NULL;
\r
657 HWND moveHistoryDialog = NULL;
\r
658 HWND evalGraphDialog = NULL;
\r
659 HWND engineOutputDialog = NULL;
\r
660 HWND gameListDialog = NULL;
\r
661 HWND editTagsDialog = NULL;
\r
663 int commentUp = FALSE;
\r
665 WindowPlacement wpMain;
\r
666 WindowPlacement wpConsole;
\r
667 WindowPlacement wpComment;
\r
668 WindowPlacement wpMoveHistory;
\r
669 WindowPlacement wpEvalGraph;
\r
670 WindowPlacement wpEngineOutput;
\r
671 WindowPlacement wpGameList;
\r
672 WindowPlacement wpTags;
\r
674 VOID EngineOptionsPopup(); // [HGM] settings
\r
676 VOID GothicPopUp(char *title, VariantClass variant);
\r
678 * Setting "frozen" should disable all user input other than deleting
\r
679 * the window. We do this while engines are initializing themselves.
\r
681 static int frozen = 0;
\r
682 static int oldMenuItemState[MENU_BAR_ITEMS];
\r
688 if (frozen) return;
\r
690 hmenu = GetMenu(hwndMain);
\r
691 for (i=0; i<MENU_BAR_ITEMS; i++) {
\r
692 oldMenuItemState[i] = EnableMenuItem(hmenu, i, MF_BYPOSITION|MF_GRAYED);
\r
694 DrawMenuBar(hwndMain);
\r
697 /* Undo a FreezeUI */
\r
703 if (!frozen) return;
\r
705 hmenu = GetMenu(hwndMain);
\r
706 for (i=0; i<MENU_BAR_ITEMS; i++) {
\r
707 EnableMenuItem(hmenu, i, MF_BYPOSITION|oldMenuItemState[i]);
\r
709 DrawMenuBar(hwndMain);
\r
712 /*static*/ int fromX = -1, fromY = -1, toX, toY; // [HGM] moved upstream, so JAWS can use them
\r
714 /* JAWS preparation patch (WinBoard for the sight impaired). Define required insertions as empty */
\r
720 #define JAWS_ALT_INTERCEPT
\r
721 #define JAWS_KB_NAVIGATION
\r
722 #define JAWS_MENU_ITEMS
\r
723 #define JAWS_SILENCE
\r
724 #define JAWS_REPLAY
\r
726 #define JAWS_COPYRIGHT
\r
727 #define JAWS_DELETE(X) X
\r
728 #define SAYMACHINEMOVE()
\r
732 /*---------------------------------------------------------------------------*\
\r
736 \*---------------------------------------------------------------------------*/
\r
739 WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
\r
740 LPSTR lpCmdLine, int nCmdShow)
\r
743 HANDLE hAccelMain, hAccelNoAlt, hAccelNoICS;
\r
744 // INITCOMMONCONTROLSEX ex;
\r
748 LoadLibrary("RICHED32.DLL");
\r
749 consoleCF.cbSize = sizeof(CHARFORMAT);
\r
751 if (!InitApplication(hInstance)) {
\r
754 if (!InitInstance(hInstance, nCmdShow, lpCmdLine)) {
\r
760 // InitCommonControlsEx(&ex);
\r
761 InitCommonControls();
\r
763 hAccelMain = LoadAccelerators (hInstance, szAppName);
\r
764 hAccelNoAlt = LoadAccelerators (hInstance, "NO_ALT");
\r
765 hAccelNoICS = LoadAccelerators( hInstance, "NO_ICS"); /* [AS] No Ctrl-V on ICS!!! */
\r
767 /* Acquire and dispatch messages until a WM_QUIT message is received. */
\r
769 while (GetMessage(&msg, /* message structure */
\r
770 NULL, /* handle of window receiving the message */
\r
771 0, /* lowest message to examine */
\r
772 0)) /* highest message to examine */
\r
775 if(msg.message == WM_CHAR && msg.wParam == '\t') {
\r
776 // [HGM] navigate: switch between all windows with tab
\r
777 HWND e1 = NULL, e2 = NULL, mh = NULL, hInput = NULL, hText = NULL;
\r
778 int i, currentElement = 0;
\r
780 // first determine what element of the chain we come from (if any)
\r
781 if(appData.icsActive) {
\r
782 hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
783 hText = GetDlgItem(hwndConsole, OPT_ConsoleText);
\r
785 if(engineOutputDialog && EngineOutputIsUp()) {
\r
786 e1 = GetDlgItem(engineOutputDialog, IDC_EngineMemo1);
\r
787 e2 = GetDlgItem(engineOutputDialog, IDC_EngineMemo2);
\r
789 if(moveHistoryDialog && MoveHistoryIsUp()) {
\r
790 mh = GetDlgItem(moveHistoryDialog, IDC_MoveHistory);
\r
792 if(msg.hwnd == hwndMain) currentElement = 7 ; else
\r
793 if(msg.hwnd == engineOutputDialog) currentElement = 2; else
\r
794 if(msg.hwnd == e1) currentElement = 2; else
\r
795 if(msg.hwnd == e2) currentElement = 3; else
\r
796 if(msg.hwnd == moveHistoryDialog) currentElement = 4; else
\r
797 if(msg.hwnd == mh) currentElement = 4; else
\r
798 if(msg.hwnd == evalGraphDialog) currentElement = 6; else
\r
799 if(msg.hwnd == hText) currentElement = 5; else
\r
800 if(msg.hwnd == hInput) currentElement = 6; else
\r
801 for (i = 0; i < N_BUTTONS; i++) {
\r
802 if (buttonDesc[i].hwnd == msg.hwnd) { currentElement = 1; break; }
\r
805 // determine where to go to
\r
806 if(currentElement) { HWND h = NULL; int direction = GetKeyState(VK_SHIFT) < 0 ? -1 : 1;
\r
808 currentElement = (currentElement + direction) % 7;
\r
809 switch(currentElement) {
\r
811 h = hwndMain; break; // passing this case always makes the loop exit
\r
813 h = buttonDesc[0].hwnd; break; // could be NULL
\r
815 if(!EngineOutputIsUp()) continue; // skip closed auxiliary windows
\r
818 if(!EngineOutputIsUp()) continue;
\r
821 if(!MoveHistoryIsUp()) continue;
\r
823 // case 6: // input to eval graph does not seem to get here!
\r
824 // if(!EvalGraphIsUp()) continue;
\r
825 // h = evalGraphDialog; break;
\r
827 if(!appData.icsActive) continue;
\r
831 if(!appData.icsActive) continue;
\r
837 if(currentElement > 4 && IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);
\r
838 if(currentElement < 5 && IsIconic(hwndMain)) ShowWindow(hwndMain, SW_RESTORE); // all open together
\r
841 continue; // this message now has been processed
\r
845 if (!(commentDialog && IsDialogMessage(commentDialog, &msg)) &&
\r
846 !(moveHistoryDialog && IsDialogMessage(moveHistoryDialog, &msg)) &&
\r
847 !(evalGraphDialog && IsDialogMessage(evalGraphDialog, &msg)) &&
\r
848 !(engineOutputDialog && IsDialogMessage(engineOutputDialog, &msg)) &&
\r
849 !(editTagsDialog && IsDialogMessage(editTagsDialog, &msg)) &&
\r
850 !(gameListDialog && IsDialogMessage(gameListDialog, &msg)) &&
\r
851 !(errorDialog && IsDialogMessage(errorDialog, &msg)) &&
\r
852 !(!frozen && TranslateAccelerator(hwndMain, hAccelMain, &msg)) && JAWS_ACCEL
\r
853 !(!hwndConsole && TranslateAccelerator(hwndMain, hAccelNoICS, &msg)) &&
\r
854 !(!hwndConsole && TranslateAccelerator(hwndMain, hAccelNoAlt, &msg))) {
\r
855 int done = 0, i; // [HGM] chat: dispatch cat-box messages
\r
856 for(i=0; i<MAX_CHAT; i++)
\r
857 if(chatHandle[i] && IsDialogMessage(chatHandle[i], &msg)) {
\r
860 if(done) continue; // [HGM] chat: end patch
\r
861 TranslateMessage(&msg); /* Translates virtual key codes */
\r
862 DispatchMessage(&msg); /* Dispatches message to window */
\r
867 return (msg.wParam); /* Returns the value from PostQuitMessage */
\r
870 /*---------------------------------------------------------------------------*\
\r
872 * Initialization functions
\r
874 \*---------------------------------------------------------------------------*/
\r
878 { // update user logo if necessary
\r
879 static char oldUserName[MSG_SIZ], *curName;
\r
881 if(appData.autoLogo) {
\r
882 curName = UserName();
\r
883 if(strcmp(curName, oldUserName)) {
\r
884 snprintf(oldUserName, MSG_SIZ, "logos\\%s.bmp", curName);
\r
885 userLogo = LoadImage( 0, oldUserName, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );
\r
886 safeStrCpy(oldUserName, curName, sizeof(oldUserName)/sizeof(oldUserName[0]) );
\r
887 if(userLogo == NULL)
\r
888 userLogo = LoadImage( 0, "logos\\dummy.bmp", IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );
\r
894 InitApplication(HINSTANCE hInstance)
\r
898 /* Fill in window class structure with parameters that describe the */
\r
901 wc.style = CS_HREDRAW | CS_VREDRAW; /* Class style(s). */
\r
902 wc.lpfnWndProc = (WNDPROC)WndProc; /* Window Procedure */
\r
903 wc.cbClsExtra = 0; /* No per-class extra data. */
\r
904 wc.cbWndExtra = 0; /* No per-window extra data. */
\r
905 wc.hInstance = hInstance; /* Owner of this class */
\r
906 wc.hIcon = LoadIcon(hInstance, "icon_white");
\r
907 wc.hCursor = LoadCursor(NULL, IDC_ARROW); /* Cursor */
\r
908 wc.hbrBackground = (HBRUSH)(COLOR_WINDOW+1); /* Default color */
\r
909 wc.lpszMenuName = szAppName; /* Menu name from .RC */
\r
910 wc.lpszClassName = szAppName; /* Name to register as */
\r
912 /* Register the window class and return success/failure code. */
\r
913 if (!RegisterClass(&wc)) return FALSE;
\r
915 wc.style = CS_HREDRAW | CS_VREDRAW;
\r
916 wc.lpfnWndProc = (WNDPROC)ConsoleWndProc;
\r
918 wc.cbWndExtra = DLGWINDOWEXTRA;
\r
919 wc.hInstance = hInstance;
\r
920 wc.hIcon = LoadIcon(hInstance, "icon_white");
\r
921 wc.hCursor = LoadCursor(NULL, IDC_ARROW);
\r
922 wc.hbrBackground = (HBRUSH)(COLOR_MENU+1);
\r
923 wc.lpszMenuName = NULL;
\r
924 wc.lpszClassName = szConsoleName;
\r
926 if (!RegisterClass(&wc)) return FALSE;
\r
931 /* Set by InitInstance, used by EnsureOnScreen */
\r
932 int screenHeight, screenWidth;
\r
935 EnsureOnScreen(int *x, int *y, int minX, int minY)
\r
937 // int gap = GetSystemMetrics(SM_CYFRAME) + GetSystemMetrics(SM_CYCAPTION);
\r
938 /* Be sure window at (x,y) is not off screen (or even mostly off screen) */
\r
939 if (*x > screenWidth - 32) *x = 0;
\r
940 if (*y > screenHeight - 32) *y = 0;
\r
941 if (*x < minX) *x = minX;
\r
942 if (*y < minY) *y = minY;
\r
946 InitInstance(HINSTANCE hInstance, int nCmdShow, LPSTR lpCmdLine)
\r
948 HWND hwnd; /* Main window handle. */
\r
950 WINDOWPLACEMENT wp;
\r
953 hInst = hInstance; /* Store instance handle in our global variable */
\r
954 programName = szAppName;
\r
956 if (SearchPath(NULL, "WinBoard.exe", NULL, MSG_SIZ, installDir, &filepart)) {
\r
957 *filepart = NULLCHAR;
\r
959 GetCurrentDirectory(MSG_SIZ, installDir);
\r
961 gameInfo.boardWidth = gameInfo.boardHeight = 8; // [HGM] won't have open window otherwise
\r
962 screenWidth = screenHeight = 1000; // [HGM] placement: kludge to allow calling EnsureOnScreen from InitAppData
\r
963 InitAppData(lpCmdLine); /* Get run-time parameters */
\r
964 /* xboard, and older WinBoards, controlled the move sound with the
\r
965 appData.ringBellAfterMoves option. In the current WinBoard, we
\r
966 always turn the option on (so that the backend will call us),
\r
967 then let the user turn the sound off by setting it to silence if
\r
968 desired. To accommodate old winboard.ini files saved by old
\r
969 versions of WinBoard, we also turn off the sound if the option
\r
970 was initially set to false. [HGM] taken out of InitAppData */
\r
971 if (!appData.ringBellAfterMoves) {
\r
972 sounds[(int)SoundMove].name = strdup("");
\r
973 appData.ringBellAfterMoves = TRUE;
\r
975 if (appData.debugMode) {
\r
976 debugFP = fopen(appData.nameOfDebugFile, "w");
\r
977 setbuf(debugFP, NULL);
\r
980 LoadLanguageFile(appData.language);
\r
984 // InitEngineUCI( installDir, &first ); // [HGM] incorporated in InitBackEnd1()
\r
985 // InitEngineUCI( installDir, &second );
\r
987 /* Create a main window for this application instance. */
\r
988 hwnd = CreateWindow(szAppName, szTitle,
\r
989 (WS_OVERLAPPEDWINDOW & ~WS_MAXIMIZEBOX),
\r
990 CW_USEDEFAULT, 0, CW_USEDEFAULT, 0,
\r
991 NULL, NULL, hInstance, NULL);
\r
994 /* If window could not be created, return "failure" */
\r
999 /* [HGM] logo: Load logos if specified (must be done before InitDrawingSizes) */
\r
1000 if( appData.firstLogo && appData.firstLogo[0] != NULLCHAR) {
\r
1001 first.programLogo = LoadImage( 0, appData.firstLogo, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );
\r
1003 if (first.programLogo == NULL && appData.debugMode) {
\r
1004 fprintf( debugFP, "Unable to load logo bitmap '%s'\n", appData.firstLogo );
\r
1006 } else if(appData.autoLogo) {
\r
1007 if(appData.firstDirectory && appData.firstDirectory[0]) {
\r
1008 char buf[MSG_SIZ];
\r
1009 snprintf(buf, MSG_SIZ, "%s/logo.bmp", appData.firstDirectory);
\r
1010 first.programLogo = LoadImage( 0, buf, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );
\r
1014 if( appData.secondLogo && appData.secondLogo[0] != NULLCHAR) {
\r
1015 second.programLogo = LoadImage( 0, appData.secondLogo, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );
\r
1017 if (second.programLogo == NULL && appData.debugMode) {
\r
1018 fprintf( debugFP, "Unable to load logo bitmap '%s'\n", appData.secondLogo );
\r
1020 } else if(appData.autoLogo) {
\r
1021 char buf[MSG_SIZ];
\r
1022 if(appData.icsActive) { // [HGM] logo: in ICS mode second can be used for ICS
\r
1023 snprintf(buf, MSG_SIZ, "logos\\%s.bmp", appData.icsHost);
\r
1024 second.programLogo = LoadImage( 0, buf, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );
\r
1026 if(appData.secondDirectory && appData.secondDirectory[0]) {
\r
1027 snprintf(buf, MSG_SIZ, "%s\\logo.bmp", appData.secondDirectory);
\r
1028 second.programLogo = LoadImage( 0, buf, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );
\r
1034 iconWhite = LoadIcon(hInstance, "icon_white");
\r
1035 iconBlack = LoadIcon(hInstance, "icon_black");
\r
1036 iconCurrent = iconWhite;
\r
1037 InitDrawingColors();
\r
1038 screenHeight = GetSystemMetrics(SM_CYSCREEN);
\r
1039 screenWidth = GetSystemMetrics(SM_CXSCREEN);
\r
1040 for (ibs = (int) NUM_SIZES - 1; ibs >= 0; ibs--) {
\r
1041 /* Compute window size for each board size, and use the largest
\r
1042 size that fits on this screen as the default. */
\r
1043 InitDrawingSizes((BoardSize)(ibs+1000), 0);
\r
1044 if (boardSize == (BoardSize)-1 &&
\r
1045 winH <= screenHeight
\r
1046 - GetSystemMetrics(SM_CYFRAME) - GetSystemMetrics(SM_CYCAPTION) - 10
\r
1047 && winW <= screenWidth) {
\r
1048 boardSize = (BoardSize)ibs;
\r
1052 InitDrawingSizes(boardSize, 0);
\r
1053 TranslateMenus(1);
\r
1055 buttonCount = GetSystemMetrics(SM_CMOUSEBUTTONS);
\r
1057 /* [AS] Load textures if specified */
\r
1058 ZeroMemory( &backTextureSquareInfo, sizeof(backTextureSquareInfo) );
\r
1060 if( appData.liteBackTextureFile && appData.liteBackTextureFile[0] != NULLCHAR && appData.liteBackTextureFile[0] != '*' ) {
\r
1061 liteBackTexture = LoadImage( 0, appData.liteBackTextureFile, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );
\r
1062 liteBackTextureMode = appData.liteBackTextureMode;
\r
1064 if (liteBackTexture == NULL && appData.debugMode) {
\r
1065 fprintf( debugFP, "Unable to load lite texture bitmap '%s'\n", appData.liteBackTextureFile );
\r
1069 if( appData.darkBackTextureFile && appData.darkBackTextureFile[0] != NULLCHAR && appData.darkBackTextureFile[0] != '*' ) {
\r
1070 darkBackTexture = LoadImage( 0, appData.darkBackTextureFile, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );
\r
1071 darkBackTextureMode = appData.darkBackTextureMode;
\r
1073 if (darkBackTexture == NULL && appData.debugMode) {
\r
1074 fprintf( debugFP, "Unable to load dark texture bitmap '%s'\n", appData.darkBackTextureFile );
\r
1078 mysrandom( (unsigned) time(NULL) );
\r
1080 /* [AS] Restore layout */
\r
1081 if( wpMoveHistory.visible ) {
\r
1082 MoveHistoryPopUp();
\r
1085 if( wpEvalGraph.visible ) {
\r
1089 if( wpEngineOutput.visible ) {
\r
1090 EngineOutputPopUp();
\r
1093 /* Make the window visible; update its client area; and return "success" */
\r
1094 EnsureOnScreen(&wpMain.x, &wpMain.y, minX, minY);
\r
1095 wp.length = sizeof(WINDOWPLACEMENT);
\r
1097 wp.showCmd = nCmdShow;
\r
1098 wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;
\r
1099 wp.rcNormalPosition.left = wpMain.x;
\r
1100 wp.rcNormalPosition.right = wpMain.x + wpMain.width;
\r
1101 wp.rcNormalPosition.top = wpMain.y;
\r
1102 wp.rcNormalPosition.bottom = wpMain.y + wpMain.height;
\r
1103 SetWindowPlacement(hwndMain, &wp);
\r
1105 InitBackEnd2(); // [HGM] moved until after all windows placed, to save correct position if fatal error on engine start
\r
1107 if(!appData.noGUI) SetWindowPos(hwndMain, alwaysOnTop ? HWND_TOPMOST : HWND_NOTOPMOST,
\r
1108 0, 0, 0, 0, SWP_NOMOVE|SWP_NOSIZE);
\r
1110 if (hwndConsole) {
\r
1112 SetWindowPos(hwndConsole, alwaysOnTop ? HWND_TOPMOST : HWND_NOTOPMOST,
\r
1113 0, 0, 0, 0, SWP_NOMOVE|SWP_NOSIZE);
\r
1115 ShowWindow(hwndConsole, nCmdShow);
\r
1116 if(appData.chatBoxes) { // [HGM] chat: open chat boxes
\r
1117 char buf[MSG_SIZ], *p = buf, *q;
\r
1118 safeStrCpy(buf, appData.chatBoxes, sizeof(buf)/sizeof(buf[0]) );
\r
1120 q = strchr(p, ';');
\r
1122 if(*p) ChatPopUp(p);
\r
1125 SetActiveWindow(hwndConsole);
\r
1127 if(!appData.noGUI) UpdateWindow(hwnd); else ShowWindow(hwnd, SW_MINIMIZE);
\r
1128 if(gameListDialog) SetFocus(gameListDialog); // [HGM] jaws: for if we clicked multi-game game file
\r
1137 HMENU hmenu = GetMenu(hwndMain);
\r
1139 (void) EnableMenuItem(hmenu, IDM_CommPort,
\r
1140 MF_BYCOMMAND|((appData.icsActive &&
\r
1141 *appData.icsCommPort != NULLCHAR) ?
\r
1142 MF_ENABLED : MF_GRAYED));
\r
1143 (void) CheckMenuItem(hmenu, IDM_SaveSettingsOnExit,
\r
1144 MF_BYCOMMAND|(saveSettingsOnExit ?
\r
1145 MF_CHECKED : MF_UNCHECKED));
\r
1148 //---------------------------------------------------------------------------------------------------------
\r
1150 #define ICS_TEXT_MENU_SIZE (IDM_CommandXLast - IDM_CommandX + 1)
\r
1151 #define XBOARD FALSE
\r
1153 #define OPTCHAR "/"
\r
1154 #define SEPCHAR "="
\r
1158 // front-end part of option handling
\r
1161 LFfromMFP(LOGFONT* lf, MyFontParams *mfp)
\r
1163 HDC hdc = CreateDC("DISPLAY", NULL, NULL, NULL);
\r
1164 lf->lfHeight = -(int)(mfp->pointSize * GetDeviceCaps(hdc, LOGPIXELSY) / 72.0 + 0.5);
\r
1167 lf->lfEscapement = 0;
\r
1168 lf->lfOrientation = 0;
\r
1169 lf->lfWeight = mfp->bold ? FW_BOLD : FW_NORMAL;
\r
1170 lf->lfItalic = mfp->italic;
\r
1171 lf->lfUnderline = mfp->underline;
\r
1172 lf->lfStrikeOut = mfp->strikeout;
\r
1173 lf->lfCharSet = mfp->charset;
\r
1174 lf->lfOutPrecision = OUT_DEFAULT_PRECIS;
\r
1175 lf->lfClipPrecision = CLIP_DEFAULT_PRECIS;
\r
1176 lf->lfQuality = DEFAULT_QUALITY;
\r
1177 lf->lfPitchAndFamily = DEFAULT_PITCH|FF_DONTCARE;
\r
1178 safeStrCpy(lf->lfFaceName, mfp->faceName, sizeof(lf->lfFaceName)/sizeof(lf->lfFaceName[0]) );
\r
1182 CreateFontInMF(MyFont *mf)
\r
1184 LFfromMFP(&mf->lf, &mf->mfp);
\r
1185 if (mf->hf) DeleteObject(mf->hf);
\r
1186 mf->hf = CreateFontIndirect(&mf->lf);
\r
1189 // [HGM] This platform-dependent table provides the location for storing the color info
\r
1191 colorVariable[] = {
\r
1192 &whitePieceColor,
\r
1193 &blackPieceColor,
\r
1194 &lightSquareColor,
\r
1195 &darkSquareColor,
\r
1196 &highlightSquareColor,
\r
1197 &premoveHighlightColor,
\r
1199 &consoleBackgroundColor,
\r
1200 &appData.fontForeColorWhite,
\r
1201 &appData.fontBackColorWhite,
\r
1202 &appData.fontForeColorBlack,
\r
1203 &appData.fontBackColorBlack,
\r
1204 &appData.evalHistColorWhite,
\r
1205 &appData.evalHistColorBlack,
\r
1206 &appData.highlightArrowColor,
\r
1209 /* Command line font name parser. NULL name means do nothing.
\r
1210 Syntax like "Courier New:10.0 bi" or "Arial:10" or "Arial:10b"
\r
1211 For backward compatibility, syntax without the colon is also
\r
1212 accepted, but font names with digits in them won't work in that case.
\r
1215 ParseFontName(char *name, MyFontParams *mfp)
\r
1218 if (name == NULL) return;
\r
1220 q = strchr(p, ':');
\r
1222 if (q - p >= sizeof(mfp->faceName))
\r
1223 ExitArgError(_("Font name too long:"), name);
\r
1224 memcpy(mfp->faceName, p, q - p);
\r
1225 mfp->faceName[q - p] = NULLCHAR;
\r
1228 q = mfp->faceName;
\r
1229 while (*p && !isdigit(*p)) {
\r
1231 if (q - mfp->faceName >= sizeof(mfp->faceName))
\r
1232 ExitArgError(_("Font name too long:"), name);
\r
1234 while (q > mfp->faceName && q[-1] == ' ') q--;
\r
1237 if (!*p) ExitArgError(_("Font point size missing:"), name);
\r
1238 mfp->pointSize = (float) atof(p);
\r
1239 mfp->bold = (strchr(p, 'b') != NULL);
\r
1240 mfp->italic = (strchr(p, 'i') != NULL);
\r
1241 mfp->underline = (strchr(p, 'u') != NULL);
\r
1242 mfp->strikeout = (strchr(p, 's') != NULL);
\r
1243 mfp->charset = DEFAULT_CHARSET;
\r
1244 q = strchr(p, 'c');
\r
1246 mfp->charset = (BYTE) atoi(q+1);
\r
1250 ParseFont(char *name, int number)
\r
1251 { // wrapper to shield back-end from 'font'
\r
1252 ParseFontName(name, &font[boardSize][number]->mfp);
\r
1257 { // in WB we have a 2D array of fonts; this initializes their description
\r
1259 /* Point font array elements to structures and
\r
1260 parse default font names */
\r
1261 for (i=0; i<NUM_FONTS; i++) {
\r
1262 for (j=0; j<NUM_SIZES; j++) {
\r
1263 font[j][i] = &fontRec[j][i];
\r
1264 ParseFontName(font[j][i]->def, &font[j][i]->mfp);
\r
1271 { // here we create the actual fonts from the selected descriptions
\r
1273 for (i=0; i<NUM_FONTS; i++) {
\r
1274 for (j=0; j<NUM_SIZES; j++) {
\r
1275 CreateFontInMF(font[j][i]);
\r
1279 /* Color name parser.
\r
1280 X version accepts X color names, but this one
\r
1281 handles only the #rrggbb form (hex) or rrr,ggg,bbb (decimal) */
\r
1283 ParseColorName(char *name)
\r
1285 int red, green, blue, count;
\r
1286 char buf[MSG_SIZ];
\r
1288 count = sscanf(name, "#%2x%2x%2x", &red, &green, &blue);
\r
1290 count = sscanf(name, "%3d%*[^0-9]%3d%*[^0-9]%3d",
\r
1291 &red, &green, &blue);
\r
1294 snprintf(buf, MSG_SIZ, _("Can't parse color name %s"), name);
\r
1295 DisplayError(buf, 0);
\r
1296 return RGB(0, 0, 0);
\r
1298 return PALETTERGB(red, green, blue);
\r
1302 ParseColor(int n, char *name)
\r
1303 { // for WinBoard the color is an int, which needs to be derived from the string
\r
1304 if(colorVariable[n]) *(int*)colorVariable[n] = ParseColorName(name);
\r
1308 ParseAttribs(COLORREF *color, int *effects, char* argValue)
\r
1310 char *e = argValue;
\r
1314 if (*e == 'b') eff |= CFE_BOLD;
\r
1315 else if (*e == 'i') eff |= CFE_ITALIC;
\r
1316 else if (*e == 'u') eff |= CFE_UNDERLINE;
\r
1317 else if (*e == 's') eff |= CFE_STRIKEOUT;
\r
1318 else if (*e == '#' || isdigit(*e)) break;
\r
1322 *color = ParseColorName(e);
\r
1326 ParseTextAttribs(ColorClass cc, char *s)
\r
1327 { // [HGM] front-end wrapper that does the platform-dependent call
\r
1328 // for XBoard we would set (&appData.colorShout)[cc] = strdup(s);
\r
1329 ParseAttribs(&textAttribs[cc].color, &textAttribs[cc].effects, s);
\r
1333 ParseBoardSize(void *addr, char *name)
\r
1334 { // [HGM] rewritten with return-value ptr to shield back-end from BoardSize
\r
1335 BoardSize bs = SizeTiny;
\r
1336 while (sizeInfo[bs].name != NULL) {
\r
1337 if (StrCaseCmp(name, sizeInfo[bs].name) == 0) {
\r
1338 *(BoardSize *)addr = bs;
\r
1343 ExitArgError(_("Unrecognized board size value"), name);
\r
1348 { // [HGM] import name from appData first
\r
1351 for (cc = (ColorClass)0; cc < ColorNormal; cc++) {
\r
1352 textAttribs[cc].sound.name = strdup((&appData.soundShout)[cc]);
\r
1353 textAttribs[cc].sound.data = NULL;
\r
1354 MyLoadSound(&textAttribs[cc].sound);
\r
1356 for (cc = ColorNormal; cc < NColorClasses; cc++) {
\r
1357 textAttribs[cc].sound.name = strdup("");
\r
1358 textAttribs[cc].sound.data = NULL;
\r
1360 for (sc = (SoundClass)0; sc < NSoundClasses; sc++) {
\r
1361 sounds[sc].name = strdup((&appData.soundMove)[sc]);
\r
1362 sounds[sc].data = NULL;
\r
1363 MyLoadSound(&sounds[sc]);
\r
1368 SetCommPortDefaults()
\r
1370 memset(&dcb, 0, sizeof(DCB)); // required by VS 2002 +
\r
1371 dcb.DCBlength = sizeof(DCB);
\r
1372 dcb.BaudRate = 9600;
\r
1373 dcb.fBinary = TRUE;
\r
1374 dcb.fParity = FALSE;
\r
1375 dcb.fOutxCtsFlow = FALSE;
\r
1376 dcb.fOutxDsrFlow = FALSE;
\r
1377 dcb.fDtrControl = DTR_CONTROL_ENABLE;
\r
1378 dcb.fDsrSensitivity = FALSE;
\r
1379 dcb.fTXContinueOnXoff = TRUE;
\r
1380 dcb.fOutX = FALSE;
\r
1382 dcb.fNull = FALSE;
\r
1383 dcb.fRtsControl = RTS_CONTROL_ENABLE;
\r
1384 dcb.fAbortOnError = FALSE;
\r
1386 dcb.Parity = SPACEPARITY;
\r
1387 dcb.StopBits = ONESTOPBIT;
\r
1390 // [HGM] args: these three cases taken out to stay in front-end
\r
1392 SaveFontArg(FILE *f, ArgDescriptor *ad)
\r
1393 { // in WinBoard every board size has its own font, and the "argLoc" identifies the table,
\r
1394 // while the curent board size determines the element. This system should be ported to XBoard.
\r
1395 // What the table contains pointers to, and how to print the font description, remains platform-dependent
\r
1397 for (bs=0; bs<NUM_SIZES; bs++) {
\r
1398 MyFontParams *mfp = &font[bs][(int) ad->argLoc]->mfp;
\r
1399 fprintf(f, "/size=%s ", sizeInfo[bs].name);
\r
1400 fprintf(f, "/%s=\"%s:%g%s%s%s%s%sc%d\"\n",
\r
1401 ad->argName, mfp->faceName, mfp->pointSize,
\r
1402 mfp->bold || mfp->italic || mfp->underline || mfp->strikeout ? " " : "",
\r
1403 mfp->bold ? "b" : "",
\r
1404 mfp->italic ? "i" : "",
\r
1405 mfp->underline ? "u" : "",
\r
1406 mfp->strikeout ? "s" : "",
\r
1407 (int)mfp->charset);
\r
1413 { // [HGM] copy the names from the internal WB variables to appData
\r
1416 for (cc = (ColorClass)0; cc < ColorNormal; cc++)
\r
1417 (&appData.soundShout)[cc] = textAttribs[cc].sound.name;
\r
1418 for (sc = (SoundClass)0; sc < NSoundClasses; sc++)
\r
1419 (&appData.soundMove)[sc] = sounds[sc].name;
\r
1423 SaveAttribsArg(FILE *f, ArgDescriptor *ad)
\r
1424 { // here the "argLoc" defines a table index. It could have contained the 'ta' pointer itself, though
\r
1425 MyTextAttribs* ta = &textAttribs[(ColorClass)ad->argLoc];
\r
1426 fprintf(f, "/%s=\"%s%s%s%s%s#%02lx%02lx%02lx\"\n", ad->argName,
\r
1427 (ta->effects & CFE_BOLD) ? "b" : "",
\r
1428 (ta->effects & CFE_ITALIC) ? "i" : "",
\r
1429 (ta->effects & CFE_UNDERLINE) ? "u" : "",
\r
1430 (ta->effects & CFE_STRIKEOUT) ? "s" : "",
\r
1431 (ta->effects) ? " " : "",
\r
1432 ta->color&0xff, (ta->color >> 8)&0xff, (ta->color >> 16)&0xff);
\r
1436 SaveColor(FILE *f, ArgDescriptor *ad)
\r
1437 { // in WinBoard the color is an int and has to be converted to text. In X it would be a string already?
\r
1438 COLORREF color = *(COLORREF *)colorVariable[(int)ad->argLoc];
\r
1439 fprintf(f, "/%s=#%02lx%02lx%02lx\n", ad->argName,
\r
1440 color&0xff, (color>>8)&0xff, (color>>16)&0xff);
\r
1444 SaveBoardSize(FILE *f, char *name, void *addr)
\r
1445 { // wrapper to shield back-end from BoardSize & sizeInfo
\r
1446 fprintf(f, "/%s=%s\n", name, sizeInfo[*(BoardSize *)addr].name);
\r
1450 ParseCommPortSettings(char *s)
\r
1451 { // wrapper to keep dcb from back-end
\r
1452 ParseCommSettings(s, &dcb);
\r
1457 { // wrapper to shield use of window handles from back-end (make addressible by number?)
\r
1458 GetActualPlacement(hwndMain, &wpMain);
\r
1459 GetActualPlacement(hwndConsole, &wpConsole);
\r
1460 GetActualPlacement(commentDialog, &wpComment);
\r
1461 GetActualPlacement(editTagsDialog, &wpTags);
\r
1462 GetActualPlacement(gameListDialog, &wpGameList);
\r
1463 GetActualPlacement(moveHistoryDialog, &wpMoveHistory);
\r
1464 GetActualPlacement(evalGraphDialog, &wpEvalGraph);
\r
1465 GetActualPlacement(engineOutputDialog, &wpEngineOutput);
\r
1469 PrintCommPortSettings(FILE *f, char *name)
\r
1470 { // wrapper to shield back-end from DCB
\r
1471 PrintCommSettings(f, name, &dcb);
\r
1475 MySearchPath(char *installDir, char *name, char *fullname)
\r
1477 char *dummy, buf[MSG_SIZ], *p = name, *q;
\r
1478 if(name[0]== '%') {
\r
1479 fullname[0] = 0; // [HGM] first expand any environment variables in the given name
\r
1480 while(*p == '%' && (q = strchr(p+1, '%'))) { // [HGM] recognize %*% as environment variable
\r
1481 safeStrCpy(buf, p+1, sizeof(buf)/sizeof(buf[0]) );
\r
1482 *strchr(buf, '%') = 0;
\r
1483 strcat(fullname, getenv(buf));
\r
1484 p = q+1; while(*p == '\\') { strcat(fullname, "\\"); p++; }
\r
1486 strcat(fullname, p); // after environment variables (if any), take the remainder of the given name
\r
1487 if(appData.debugMode) fprintf(debugFP, "name = '%s', expanded name = '%s'\n", name, fullname);
\r
1488 return (int) strlen(fullname);
\r
1490 return (int) SearchPath(installDir, name, NULL, MSG_SIZ, fullname, &dummy);
\r
1494 MyGetFullPathName(char *name, char *fullname)
\r
1497 return (int) GetFullPathName(name, MSG_SIZ, fullname, &dummy);
\r
1502 { // [HGM] args: allows testing if main window is realized from back-end
\r
1503 return hwndMain != NULL;
\r
1507 PopUpStartupDialog()
\r
1511 LoadLanguageFile(appData.language);
\r
1512 lpProc = MakeProcInstance((FARPROC)StartupDialog, hInst);
\r
1513 DialogBox(hInst, MAKEINTRESOURCE(DLG_Startup), NULL, (DLGPROC)lpProc);
\r
1514 FreeProcInstance(lpProc);
\r
1517 /*---------------------------------------------------------------------------*\
\r
1519 * GDI board drawing routines
\r
1521 \*---------------------------------------------------------------------------*/
\r
1523 /* [AS] Draw square using background texture */
\r
1524 static void DrawTile( int dx, int dy, int dw, int dh, HDC dst, HDC src, int mode, int sx, int sy )
\r
1529 return; /* Should never happen! */
\r
1532 SetGraphicsMode( dst, GM_ADVANCED );
\r
1539 /* X reflection */
\r
1544 x.eDx = (FLOAT) dw + dx - 1;
\r
1547 SetWorldTransform( dst, &x );
\r
1550 /* Y reflection */
\r
1556 x.eDy = (FLOAT) dh + dy - 1;
\r
1558 SetWorldTransform( dst, &x );
\r
1566 x.eDx = (FLOAT) dx;
\r
1567 x.eDy = (FLOAT) dy;
\r
1570 SetWorldTransform( dst, &x );
\r
1574 BitBlt( dst, dx, dy, dw, dh, src, sx, sy, SRCCOPY );
\r
1582 SetWorldTransform( dst, &x );
\r
1584 ModifyWorldTransform( dst, 0, MWT_IDENTITY );
\r
1587 /* [AS] [HGM] Make room for more piece types, so all pieces can be different */
\r
1589 PM_WP = (int) WhitePawn,
\r
1590 PM_WN = (int) WhiteKnight,
\r
1591 PM_WB = (int) WhiteBishop,
\r
1592 PM_WR = (int) WhiteRook,
\r
1593 PM_WQ = (int) WhiteQueen,
\r
1594 PM_WF = (int) WhiteFerz,
\r
1595 PM_WW = (int) WhiteWazir,
\r
1596 PM_WE = (int) WhiteAlfil,
\r
1597 PM_WM = (int) WhiteMan,
\r
1598 PM_WO = (int) WhiteCannon,
\r
1599 PM_WU = (int) WhiteUnicorn,
\r
1600 PM_WH = (int) WhiteNightrider,
\r
1601 PM_WA = (int) WhiteAngel,
\r
1602 PM_WC = (int) WhiteMarshall,
\r
1603 PM_WAB = (int) WhiteCardinal,
\r
1604 PM_WD = (int) WhiteDragon,
\r
1605 PM_WL = (int) WhiteLance,
\r
1606 PM_WS = (int) WhiteCobra,
\r
1607 PM_WV = (int) WhiteFalcon,
\r
1608 PM_WSG = (int) WhiteSilver,
\r
1609 PM_WG = (int) WhiteGrasshopper,
\r
1610 PM_WK = (int) WhiteKing,
\r
1611 PM_BP = (int) BlackPawn,
\r
1612 PM_BN = (int) BlackKnight,
\r
1613 PM_BB = (int) BlackBishop,
\r
1614 PM_BR = (int) BlackRook,
\r
1615 PM_BQ = (int) BlackQueen,
\r
1616 PM_BF = (int) BlackFerz,
\r
1617 PM_BW = (int) BlackWazir,
\r
1618 PM_BE = (int) BlackAlfil,
\r
1619 PM_BM = (int) BlackMan,
\r
1620 PM_BO = (int) BlackCannon,
\r
1621 PM_BU = (int) BlackUnicorn,
\r
1622 PM_BH = (int) BlackNightrider,
\r
1623 PM_BA = (int) BlackAngel,
\r
1624 PM_BC = (int) BlackMarshall,
\r
1625 PM_BG = (int) BlackGrasshopper,
\r
1626 PM_BAB = (int) BlackCardinal,
\r
1627 PM_BD = (int) BlackDragon,
\r
1628 PM_BL = (int) BlackLance,
\r
1629 PM_BS = (int) BlackCobra,
\r
1630 PM_BV = (int) BlackFalcon,
\r
1631 PM_BSG = (int) BlackSilver,
\r
1632 PM_BK = (int) BlackKing
\r
1635 static HFONT hPieceFont = NULL;
\r
1636 static HBITMAP hPieceMask[(int) EmptySquare];
\r
1637 static HBITMAP hPieceFace[(int) EmptySquare];
\r
1638 static int fontBitmapSquareSize = 0;
\r
1639 static char pieceToFontChar[(int) EmptySquare] =
\r
1640 { 'p', 'n', 'b', 'r', 'q',
\r
1641 'n', 'b', 'p', 'n', 'b', 'r', 'b', 'r', 'q', 'k',
\r
1642 'k', 'o', 'm', 'v', 't', 'w',
\r
1643 'v', 't', 'o', 'm', 'v', 't', 'v', 't', 'w', 'l',
\r
1646 extern BOOL SetCharTable( char *table, const char * map );
\r
1647 /* [HGM] moved to backend.c */
\r
1649 static void SetPieceBackground( HDC hdc, COLORREF color, int mode )
\r
1652 BYTE r1 = GetRValue( color );
\r
1653 BYTE g1 = GetGValue( color );
\r
1654 BYTE b1 = GetBValue( color );
\r
1660 /* Create a uniform background first */
\r
1661 hbrush = CreateSolidBrush( color );
\r
1662 SetRect( &rc, 0, 0, squareSize, squareSize );
\r
1663 FillRect( hdc, &rc, hbrush );
\r
1664 DeleteObject( hbrush );
\r
1667 /* Vertical gradient, good for pawn, knight and rook, less for queen and king */
\r
1668 int steps = squareSize / 2;
\r
1671 for( i=0; i<steps; i++ ) {
\r
1672 BYTE r = r1 - (r1-r2) * i / steps;
\r
1673 BYTE g = g1 - (g1-g2) * i / steps;
\r
1674 BYTE b = b1 - (b1-b2) * i / steps;
\r
1676 hbrush = CreateSolidBrush( RGB(r,g,b) );
\r
1677 SetRect( &rc, i + squareSize - steps, 0, i + squareSize - steps + 1, squareSize );
\r
1678 FillRect( hdc, &rc, hbrush );
\r
1679 DeleteObject(hbrush);
\r
1682 else if( mode == 2 ) {
\r
1683 /* Diagonal gradient, good more or less for every piece */
\r
1684 POINT triangle[3];
\r
1685 HPEN hpen = SelectObject( hdc, GetStockObject(NULL_PEN) );
\r
1686 HBRUSH hbrush_old;
\r
1687 int steps = squareSize;
\r
1690 triangle[0].x = squareSize - steps;
\r
1691 triangle[0].y = squareSize;
\r
1692 triangle[1].x = squareSize;
\r
1693 triangle[1].y = squareSize;
\r
1694 triangle[2].x = squareSize;
\r
1695 triangle[2].y = squareSize - steps;
\r
1697 for( i=0; i<steps; i++ ) {
\r
1698 BYTE r = r1 - (r1-r2) * i / steps;
\r
1699 BYTE g = g1 - (g1-g2) * i / steps;
\r
1700 BYTE b = b1 - (b1-b2) * i / steps;
\r
1702 hbrush = CreateSolidBrush( RGB(r,g,b) );
\r
1703 hbrush_old = SelectObject( hdc, hbrush );
\r
1704 Polygon( hdc, triangle, 3 );
\r
1705 SelectObject( hdc, hbrush_old );
\r
1706 DeleteObject(hbrush);
\r
1711 SelectObject( hdc, hpen );
\r
1716 [AS] The method I use to create the bitmaps it a bit tricky, but it
\r
1717 seems to work ok. The main problem here is to find the "inside" of a chess
\r
1718 piece: follow the steps as explained below.
\r
1720 static void CreatePieceMaskFromFont( HDC hdc_window, HDC hdc, int index )
\r
1724 COLORREF chroma = RGB(0xFF,0x00,0xFF);
\r
1728 int backColor = whitePieceColor;
\r
1729 int foreColor = blackPieceColor;
\r
1731 if( index < (int)BlackPawn && appData.fontBackColorWhite != appData.fontForeColorWhite ) {
\r
1732 backColor = appData.fontBackColorWhite;
\r
1733 foreColor = appData.fontForeColorWhite;
\r
1735 else if( index >= (int)BlackPawn && appData.fontBackColorBlack != appData.fontForeColorBlack ) {
\r
1736 backColor = appData.fontBackColorBlack;
\r
1737 foreColor = appData.fontForeColorBlack;
\r
1741 hbm = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );
\r
1743 hbm_old = SelectObject( hdc, hbm );
\r
1747 rc.right = squareSize;
\r
1748 rc.bottom = squareSize;
\r
1750 /* Step 1: background is now black */
\r
1751 FillRect( hdc, &rc, GetStockObject(BLACK_BRUSH) );
\r
1753 GetTextExtentPoint32( hdc, &pieceToFontChar[index], 1, &sz );
\r
1755 pt.x = (squareSize - sz.cx) / 2;
\r
1756 pt.y = (squareSize - sz.cy) / 2;
\r
1758 SetBkMode( hdc, TRANSPARENT );
\r
1759 SetTextColor( hdc, chroma );
\r
1760 /* Step 2: the piece has been drawn in purple, there are now black and purple in this bitmap */
\r
1761 TextOut( hdc, pt.x, pt.y, &pieceToFontChar[appData.allWhite && index >= (int)BlackPawn ? index - (int)BlackPawn : index], 1 );
\r
1763 SelectObject( hdc, GetStockObject(WHITE_BRUSH) );
\r
1764 /* Step 3: the area outside the piece is filled with white */
\r
1765 // FloodFill( hdc, 0, 0, chroma );
\r
1766 ExtFloodFill( hdc, 0, 0, 0, FLOODFILLSURFACE );
\r
1767 ExtFloodFill( hdc, 0, squareSize-1, 0, FLOODFILLSURFACE ); // [HGM] fill from all 4 corners, for if piece too big
\r
1768 ExtFloodFill( hdc, squareSize-1, 0, 0, FLOODFILLSURFACE );
\r
1769 ExtFloodFill( hdc, squareSize-1, squareSize-1, 0, FLOODFILLSURFACE );
\r
1770 SelectObject( hdc, GetStockObject(BLACK_BRUSH) );
\r
1772 Step 4: this is the tricky part, the area inside the piece is filled with black,
\r
1773 but if the start point is not inside the piece we're lost!
\r
1774 There should be a better way to do this... if we could create a region or path
\r
1775 from the fill operation we would be fine for example.
\r
1777 // FloodFill( hdc, squareSize / 2, squareSize / 2, RGB(0xFF,0xFF,0xFF) );
\r
1778 ExtFloodFill( hdc, squareSize / 2, squareSize / 2, RGB(0xFF,0xFF,0xFF), FLOODFILLBORDER );
\r
1780 { /* [HGM] shave off edges of mask, in an attempt to correct for the fact that FloodFill does not work correctly under Win XP */
\r
1781 HDC dc2 = CreateCompatibleDC( hdc_window );
\r
1782 HBITMAP bm2 = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );
\r
1784 SelectObject( dc2, bm2 );
\r
1785 BitBlt( dc2, 0, 0, squareSize, squareSize, hdc, 0, 0, SRCCOPY ); // make copy
\r
1786 BitBlt( hdc, 0, 1, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );
\r
1787 BitBlt( hdc, 2, 1, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );
\r
1788 BitBlt( hdc, 1, 0, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );
\r
1789 BitBlt( hdc, 1, 2, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );
\r
1792 DeleteObject( bm2 );
\r
1795 SetTextColor( hdc, 0 );
\r
1797 Step 5: some fonts have "disconnected" areas that are skipped by the fill:
\r
1798 draw the piece again in black for safety.
\r
1800 TextOut( hdc, pt.x, pt.y, &pieceToFontChar[appData.allWhite && index >= (int)BlackPawn ? index - (int)BlackPawn : index], 1 );
\r
1802 SelectObject( hdc, hbm_old );
\r
1804 if( hPieceMask[index] != NULL ) {
\r
1805 DeleteObject( hPieceMask[index] );
\r
1808 hPieceMask[index] = hbm;
\r
1811 hbm = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );
\r
1813 SelectObject( hdc, hbm );
\r
1816 HDC dc1 = CreateCompatibleDC( hdc_window );
\r
1817 HDC dc2 = CreateCompatibleDC( hdc_window );
\r
1818 HBITMAP bm2 = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );
\r
1820 SelectObject( dc1, hPieceMask[index] );
\r
1821 SelectObject( dc2, bm2 );
\r
1822 FillRect( dc2, &rc, GetStockObject(WHITE_BRUSH) );
\r
1823 BitBlt( dc2, 0, 0, squareSize, squareSize, dc1, 0, 0, SRCINVERT );
\r
1826 Now dc2 contains the inverse of the piece mask, i.e. a mask that preserves
\r
1827 the piece background and deletes (makes transparent) the rest.
\r
1828 Thanks to that mask, we are free to paint the background with the greates
\r
1829 freedom, as we'll be able to mask off the unwanted parts when finished.
\r
1830 We use this, to make gradients and give the pieces a "roundish" look.
\r
1832 SetPieceBackground( hdc, backColor, 2 );
\r
1833 BitBlt( hdc, 0, 0, squareSize, squareSize, dc2, 0, 0, SRCAND );
\r
1837 DeleteObject( bm2 );
\r
1840 SetTextColor( hdc, foreColor );
\r
1841 TextOut( hdc, pt.x, pt.y, &pieceToFontChar[appData.allWhite && index >= (int)BlackPawn ? index - (int)BlackPawn : index], 1 );
\r
1843 SelectObject( hdc, hbm_old );
\r
1845 if( hPieceFace[index] != NULL ) {
\r
1846 DeleteObject( hPieceFace[index] );
\r
1849 hPieceFace[index] = hbm;
\r
1852 static int TranslatePieceToFontPiece( int piece )
\r
1882 case BlackMarshall:
\r
1886 case BlackNightrider:
\r
1892 case BlackUnicorn:
\r
1896 case BlackGrasshopper:
\r
1908 case BlackCardinal:
\r
1915 case WhiteMarshall:
\r
1919 case WhiteNightrider:
\r
1925 case WhiteUnicorn:
\r
1929 case WhiteGrasshopper:
\r
1941 case WhiteCardinal:
\r
1950 void CreatePiecesFromFont()
\r
1953 HDC hdc_window = NULL;
\r
1959 if( fontBitmapSquareSize < 0 ) {
\r
1960 /* Something went seriously wrong in the past: do not try to recreate fonts! */
\r
1964 if( appData.renderPiecesWithFont == NULL || appData.renderPiecesWithFont[0] == NULLCHAR || appData.renderPiecesWithFont[0] == '*' ) {
\r
1965 fontBitmapSquareSize = -1;
\r
1969 if( fontBitmapSquareSize != squareSize ) {
\r
1970 hdc_window = GetDC( hwndMain );
\r
1971 hdc = CreateCompatibleDC( hdc_window );
\r
1973 if( hPieceFont != NULL ) {
\r
1974 DeleteObject( hPieceFont );
\r
1977 for( i=0; i<=(int)BlackKing; i++ ) {
\r
1978 hPieceMask[i] = NULL;
\r
1979 hPieceFace[i] = NULL;
\r
1985 if( appData.fontPieceSize >= 50 && appData.fontPieceSize <= 150 ) {
\r
1986 fontHeight = appData.fontPieceSize;
\r
1989 fontHeight = (fontHeight * squareSize) / 100;
\r
1991 lf.lfHeight = -MulDiv( fontHeight, GetDeviceCaps(hdc, LOGPIXELSY), 72 );
\r
1993 lf.lfEscapement = 0;
\r
1994 lf.lfOrientation = 0;
\r
1995 lf.lfWeight = FW_NORMAL;
\r
1997 lf.lfUnderline = 0;
\r
1998 lf.lfStrikeOut = 0;
\r
1999 lf.lfCharSet = DEFAULT_CHARSET;
\r
2000 lf.lfOutPrecision = OUT_DEFAULT_PRECIS;
\r
2001 lf.lfClipPrecision = CLIP_DEFAULT_PRECIS;
\r
2002 lf.lfQuality = PROOF_QUALITY;
\r
2003 lf.lfPitchAndFamily = DEFAULT_PITCH | FF_DONTCARE;
\r
2004 strncpy( lf.lfFaceName, appData.renderPiecesWithFont, sizeof(lf.lfFaceName) );
\r
2005 lf.lfFaceName[ sizeof(lf.lfFaceName) - 1 ] = '\0';
\r
2007 hPieceFont = CreateFontIndirect( &lf );
\r
2009 if( hPieceFont == NULL ) {
\r
2010 fontBitmapSquareSize = -2;
\r
2013 /* Setup font-to-piece character table */
\r
2014 if( ! SetCharTable(pieceToFontChar, appData.fontToPieceTable) ) {
\r
2015 /* No (or wrong) global settings, try to detect the font */
\r
2016 if( strstr(lf.lfFaceName,"Alpha") != NULL ) {
\r
2018 SetCharTable(pieceToFontChar, "phbrqkojntwl");
\r
2020 else if( strstr(lf.lfFaceName,"DiagramTT") != NULL ) {
\r
2021 /* DiagramTT* family */
\r
2022 SetCharTable(pieceToFontChar, "PNLRQKpnlrqk");
\r
2024 else if( strstr(lf.lfFaceName,"WinboardF") != NULL ) {
\r
2025 /* Fairy symbols */
\r
2026 SetCharTable(pieceToFontChar, "PNBRQFEACWMOHIJGDVSLUKpnbrqfeacwmohijgdvsluk");
\r
2028 else if( strstr(lf.lfFaceName,"GC2004D") != NULL ) {
\r
2029 /* Good Companion (Some characters get warped as literal :-( */
\r
2030 char s[] = "1cmWG0??S??oYI23wgQU";
\r
2031 s[0]=0xB9; s[1]=0xA9; s[6]=0xB1; s[11]=0xBB; s[12]=0xAB; s[17]=0xB3;
\r
2032 SetCharTable(pieceToFontChar, s);
\r
2035 /* Cases, Condal, Leipzig, Lucena, Marroquin, Merida, Usual */
\r
2036 SetCharTable(pieceToFontChar, "pnbrqkomvtwl");
\r
2040 /* Create bitmaps */
\r
2041 hfont_old = SelectObject( hdc, hPieceFont );
\r
2042 for(i=(int)WhitePawn; i<(int)EmptySquare; i++) /* [HGM] made a loop for this */
\r
2043 if(PieceToChar((ChessSquare)i) != '.') /* skip unused pieces */
\r
2044 CreatePieceMaskFromFont( hdc_window, hdc, i );
\r
2046 SelectObject( hdc, hfont_old );
\r
2048 fontBitmapSquareSize = squareSize;
\r
2052 if( hdc != NULL ) {
\r
2056 if( hdc_window != NULL ) {
\r
2057 ReleaseDC( hwndMain, hdc_window );
\r
2062 DoLoadBitmap(HINSTANCE hinst, char *piece, int squareSize, char *suffix)
\r
2066 snprintf(name, sizeof(name)/sizeof(name[0]), "%s%d%s", piece, squareSize, suffix);
\r
2067 if (gameInfo.event &&
\r
2068 strcmp(gameInfo.event, "Easter Egg Hunt") == 0 &&
\r
2069 strcmp(name, "k80s") == 0) {
\r
2070 safeStrCpy(name, "tim", sizeof(name)/sizeof(name[0]) );
\r
2072 return LoadBitmap(hinst, name);
\r
2076 /* Insert a color into the program's logical palette
\r
2077 structure. This code assumes the given color is
\r
2078 the result of the RGB or PALETTERGB macro, and it
\r
2079 knows how those macros work (which is documented).
\r
2082 InsertInPalette(COLORREF color)
\r
2084 LPPALETTEENTRY pe = &(pLogPal->palPalEntry[pLogPal->palNumEntries]);
\r
2086 if (pLogPal->palNumEntries++ >= PALETTESIZE) {
\r
2087 DisplayFatalError(_("Too many colors"), 0, 1);
\r
2088 pLogPal->palNumEntries--;
\r
2092 pe->peFlags = (char) 0;
\r
2093 pe->peRed = (char) (0xFF & color);
\r
2094 pe->peGreen = (char) (0xFF & (color >> 8));
\r
2095 pe->peBlue = (char) (0xFF & (color >> 16));
\r
2101 InitDrawingColors()
\r
2103 if (pLogPal == NULL) {
\r
2104 /* Allocate enough memory for a logical palette with
\r
2105 * PALETTESIZE entries and set the size and version fields
\r
2106 * of the logical palette structure.
\r
2108 pLogPal = (NPLOGPALETTE)
\r
2109 LocalAlloc(LMEM_FIXED, (sizeof(LOGPALETTE) +
\r
2110 (sizeof(PALETTEENTRY) * (PALETTESIZE))));
\r
2111 pLogPal->palVersion = 0x300;
\r
2113 pLogPal->palNumEntries = 0;
\r
2115 InsertInPalette(lightSquareColor);
\r
2116 InsertInPalette(darkSquareColor);
\r
2117 InsertInPalette(whitePieceColor);
\r
2118 InsertInPalette(blackPieceColor);
\r
2119 InsertInPalette(highlightSquareColor);
\r
2120 InsertInPalette(premoveHighlightColor);
\r
2122 /* create a logical color palette according the information
\r
2123 * in the LOGPALETTE structure.
\r
2125 hPal = CreatePalette((LPLOGPALETTE) pLogPal);
\r
2127 lightSquareBrush = CreateSolidBrush(lightSquareColor);
\r
2128 blackSquareBrush = CreateSolidBrush(blackPieceColor);
\r
2129 darkSquareBrush = CreateSolidBrush(darkSquareColor);
\r
2130 whitePieceBrush = CreateSolidBrush(whitePieceColor);
\r
2131 blackPieceBrush = CreateSolidBrush(blackPieceColor);
\r
2132 iconBkgndBrush = CreateSolidBrush(GetSysColor(COLOR_BACKGROUND));
\r
2133 explodeBrush = CreateSolidBrush(highlightSquareColor); // [HGM] atomic
\r
2134 markerBrush = CreateSolidBrush(premoveHighlightColor); // [HGM] markers
\r
2135 /* [AS] Force rendering of the font-based pieces */
\r
2136 if( fontBitmapSquareSize > 0 ) {
\r
2137 fontBitmapSquareSize = 0;
\r
2143 BoardWidth(int boardSize, int n)
\r
2144 { /* [HGM] argument n added to allow different width and height */
\r
2145 int lineGap = sizeInfo[boardSize].lineGap;
\r
2147 if( appData.overrideLineGap >= 0 && appData.overrideLineGap <= 5 ) {
\r
2148 lineGap = appData.overrideLineGap;
\r
2151 return (n + 1) * lineGap +
\r
2152 n * sizeInfo[boardSize].squareSize;
\r
2155 /* Respond to board resize by dragging edge */
\r
2157 ResizeBoard(int newSizeX, int newSizeY, int flags)
\r
2159 BoardSize newSize = NUM_SIZES - 1;
\r
2160 static int recurse = 0;
\r
2161 if (IsIconic(hwndMain)) return;
\r
2162 if (recurse > 0) return;
\r
2164 while (newSize > 0) {
\r
2165 InitDrawingSizes(newSize+1000, 0); // [HGM] kludge to update sizeInfo without visible effects
\r
2166 if(newSizeX >= sizeInfo[newSize].cliWidth &&
\r
2167 newSizeY >= sizeInfo[newSize].cliHeight) break;
\r
2170 boardSize = newSize;
\r
2171 InitDrawingSizes(boardSize, flags);
\r
2176 extern Boolean twoBoards, partnerUp; // [HGM] dual
\r
2179 InitDrawingSizes(BoardSize boardSize, int flags)
\r
2181 int i, boardWidth, boardHeight; /* [HGM] height treated separately */
\r
2182 ChessSquare piece;
\r
2183 static int oldBoardSize = -1, oldTinyLayout = 0;
\r
2185 SIZE clockSize, messageSize;
\r
2187 char buf[MSG_SIZ];
\r
2189 HMENU hmenu = GetMenu(hwndMain);
\r
2190 RECT crect, wrect, oldRect;
\r
2192 LOGBRUSH logbrush;
\r
2194 int suppressVisibleEffects = 0; // [HGM] kludge to request updating sizeInfo only
\r
2195 if((int)boardSize >= 1000 ) { boardSize -= 1000; suppressVisibleEffects = 1; }
\r
2197 /* [HGM] call with -2 uses old size (for if nr of files, ranks changes) */
\r
2198 if(boardSize == (BoardSize)(-2) ) boardSize = oldBoardSize;
\r
2200 oldRect.left = wpMain.x; //[HGM] placement: remember previous window params
\r
2201 oldRect.top = wpMain.y;
\r
2202 oldRect.right = wpMain.x + wpMain.width;
\r
2203 oldRect.bottom = wpMain.y + wpMain.height;
\r
2205 tinyLayout = sizeInfo[boardSize].tinyLayout;
\r
2206 smallLayout = sizeInfo[boardSize].smallLayout;
\r
2207 squareSize = sizeInfo[boardSize].squareSize;
\r
2208 lineGap = sizeInfo[boardSize].lineGap;
\r
2209 minorSize = 0; /* [HGM] Kludge to see if demagnified pieces need to be shifted */
\r
2211 if( appData.overrideLineGap >= 0 && appData.overrideLineGap <= 5 ) {
\r
2212 lineGap = appData.overrideLineGap;
\r
2215 if (tinyLayout != oldTinyLayout) {
\r
2216 long style = GetWindowLong(hwndMain, GWL_STYLE);
\r
2218 style &= ~WS_SYSMENU;
\r
2219 InsertMenu(hmenu, IDM_Exit, MF_BYCOMMAND, IDM_Minimize,
\r
2220 "&Minimize\tCtrl+F4");
\r
2222 style |= WS_SYSMENU;
\r
2223 RemoveMenu(hmenu, IDM_Minimize, MF_BYCOMMAND);
\r
2225 SetWindowLong(hwndMain, GWL_STYLE, style);
\r
2227 for (i=0; menuBarText[tinyLayout][i]; i++) {
\r
2228 ModifyMenu(hmenu, i, MF_STRING|MF_BYPOSITION|MF_POPUP,
\r
2229 (UINT)GetSubMenu(hmenu, i), T_(menuBarText[tinyLayout][i]));
\r
2231 DrawMenuBar(hwndMain);
\r
2234 boardWidth = BoardWidth(boardSize, BOARD_WIDTH);
\r
2235 boardHeight = BoardWidth(boardSize, BOARD_HEIGHT);
\r
2237 /* Get text area sizes */
\r
2238 hdc = GetDC(hwndMain);
\r
2239 if (appData.clockMode) {
\r
2240 snprintf(buf, MSG_SIZ, _("White: %s"), TimeString(23*60*60*1000L));
\r
2242 snprintf(buf, MSG_SIZ, _("White"));
\r
2244 oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);
\r
2245 GetTextExtentPoint(hdc, buf, strlen(buf), &clockSize);
\r
2246 SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);
\r
2247 str = _("We only care about the height here");
\r
2248 GetTextExtentPoint(hdc, str, strlen(str), &messageSize);
\r
2249 SelectObject(hdc, oldFont);
\r
2250 ReleaseDC(hwndMain, hdc);
\r
2252 /* Compute where everything goes */
\r
2253 if((first.programLogo || second.programLogo) && !tinyLayout) {
\r
2254 /* [HGM] logo: if either logo is on, reserve space for it */
\r
2255 logoHeight = 2*clockSize.cy;
\r
2256 leftLogoRect.left = OUTER_MARGIN;
\r
2257 leftLogoRect.right = leftLogoRect.left + 4*clockSize.cy;
\r
2258 leftLogoRect.top = OUTER_MARGIN;
\r
2259 leftLogoRect.bottom = OUTER_MARGIN + logoHeight;
\r
2261 rightLogoRect.right = OUTER_MARGIN + boardWidth;
\r
2262 rightLogoRect.left = rightLogoRect.right - 4*clockSize.cy;
\r
2263 rightLogoRect.top = OUTER_MARGIN;
\r
2264 rightLogoRect.bottom = OUTER_MARGIN + logoHeight;
\r
2267 whiteRect.left = leftLogoRect.right;
\r
2268 whiteRect.right = OUTER_MARGIN + boardWidth/2 - INNER_MARGIN/2;
\r
2269 whiteRect.top = OUTER_MARGIN;
\r
2270 whiteRect.bottom = whiteRect.top + logoHeight;
\r
2272 blackRect.right = rightLogoRect.left;
\r
2273 blackRect.left = whiteRect.right + INNER_MARGIN;
\r
2274 blackRect.top = whiteRect.top;
\r
2275 blackRect.bottom = whiteRect.bottom;
\r
2277 whiteRect.left = OUTER_MARGIN;
\r
2278 whiteRect.right = whiteRect.left + boardWidth/2 - INNER_MARGIN/2;
\r
2279 whiteRect.top = OUTER_MARGIN;
\r
2280 whiteRect.bottom = whiteRect.top + clockSize.cy;
\r
2282 blackRect.left = whiteRect.right + INNER_MARGIN;
\r
2283 blackRect.right = blackRect.left + boardWidth/2 - 1;
\r
2284 blackRect.top = whiteRect.top;
\r
2285 blackRect.bottom = whiteRect.bottom;
\r
2287 logoHeight = 0; // [HGM] logo: suppress logo after change to tiny layout!
\r
2290 messageRect.left = OUTER_MARGIN + MESSAGE_LINE_LEFTMARGIN;
\r
2291 if (appData.showButtonBar) {
\r
2292 messageRect.right = OUTER_MARGIN + boardWidth // [HGM] logo: expressed independent of clock placement
\r
2293 - N_BUTTONS*BUTTON_WIDTH - MESSAGE_LINE_LEFTMARGIN;
\r
2295 messageRect.right = OUTER_MARGIN + boardWidth;
\r
2297 messageRect.top = whiteRect.bottom + INNER_MARGIN;
\r
2298 messageRect.bottom = messageRect.top + messageSize.cy;
\r
2300 boardRect.left = OUTER_MARGIN;
\r
2301 boardRect.right = boardRect.left + boardWidth;
\r
2302 boardRect.top = messageRect.bottom + INNER_MARGIN;
\r
2303 boardRect.bottom = boardRect.top + boardHeight;
\r
2305 sizeInfo[boardSize].cliWidth = boardRect.right + OUTER_MARGIN;
\r
2306 sizeInfo[boardSize].cliHeight = boardRect.bottom + OUTER_MARGIN;
\r
2307 oldBoardSize = boardSize;
\r
2308 oldTinyLayout = tinyLayout;
\r
2309 winW = 2 * GetSystemMetrics(SM_CXFRAME) + boardRect.right + OUTER_MARGIN;
\r
2310 winH = 2 * GetSystemMetrics(SM_CYFRAME) + GetSystemMetrics(SM_CYMENU) +
\r
2311 GetSystemMetrics(SM_CYCAPTION) + boardRect.bottom + OUTER_MARGIN;
\r
2312 winW *= 1 + twoBoards;
\r
2313 if(suppressVisibleEffects) return; // [HGM] when called for filling sizeInfo only
\r
2314 wpMain.width = winW; // [HGM] placement: set through temporary which can used by initial sizing choice
\r
2315 wpMain.height = winH; // without disturbing window attachments
\r
2316 GetWindowRect(hwndMain, &wrect);
\r
2317 SetWindowPos(hwndMain, NULL, 0, 0, wpMain.width, wpMain.height,
\r
2318 SWP_NOCOPYBITS|SWP_NOZORDER|SWP_NOMOVE);
\r
2320 // [HGM] placement: let attached windows follow size change.
\r
2321 ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, moveHistoryDialog, &wpMoveHistory );
\r
2322 ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, evalGraphDialog, &wpEvalGraph );
\r
2323 ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, engineOutputDialog, &wpEngineOutput );
\r
2324 ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, gameListDialog, &wpGameList );
\r
2325 ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, hwndConsole, &wpConsole );
\r
2327 /* compensate if menu bar wrapped */
\r
2328 GetClientRect(hwndMain, &crect);
\r
2329 offby = boardRect.bottom + OUTER_MARGIN - crect.bottom;
\r
2330 wpMain.height += offby;
\r
2332 case WMSZ_TOPLEFT:
\r
2333 SetWindowPos(hwndMain, NULL,
\r
2334 wrect.right - wpMain.width, wrect.bottom - wpMain.height,
\r
2335 wpMain.width, wpMain.height, SWP_NOCOPYBITS|SWP_NOZORDER);
\r
2338 case WMSZ_TOPRIGHT:
\r
2340 SetWindowPos(hwndMain, NULL,
\r
2341 wrect.left, wrect.bottom - wpMain.height,
\r
2342 wpMain.width, wpMain.height, SWP_NOCOPYBITS|SWP_NOZORDER);
\r
2345 case WMSZ_BOTTOMLEFT:
\r
2347 SetWindowPos(hwndMain, NULL,
\r
2348 wrect.right - wpMain.width, wrect.top,
\r
2349 wpMain.width, wpMain.height, SWP_NOCOPYBITS|SWP_NOZORDER);
\r
2352 case WMSZ_BOTTOMRIGHT:
\r
2356 SetWindowPos(hwndMain, NULL, 0, 0, wpMain.width, wpMain.height,
\r
2357 SWP_NOCOPYBITS|SWP_NOZORDER|SWP_NOMOVE);
\r
2362 for (i = 0; i < N_BUTTONS; i++) {
\r
2363 if (buttonDesc[i].hwnd != NULL) {
\r
2364 DestroyWindow(buttonDesc[i].hwnd);
\r
2365 buttonDesc[i].hwnd = NULL;
\r
2367 if (appData.showButtonBar) {
\r
2368 buttonDesc[i].hwnd =
\r
2369 CreateWindow("BUTTON", buttonDesc[i].label,
\r
2370 WS_VISIBLE | WS_CHILD | BS_PUSHBUTTON,
\r
2371 boardRect.right - BUTTON_WIDTH*(N_BUTTONS-i),
\r
2372 messageRect.top, BUTTON_WIDTH, messageSize.cy, hwndMain,
\r
2373 (HMENU) buttonDesc[i].id,
\r
2374 (HINSTANCE) GetWindowLong(hwndMain, GWL_HINSTANCE), NULL);
\r
2376 SendMessage(buttonDesc[i].hwnd, WM_SETFONT,
\r
2377 (WPARAM)font[boardSize][MESSAGE_FONT]->hf,
\r
2378 MAKELPARAM(FALSE, 0));
\r
2380 if (buttonDesc[i].id == IDM_Pause)
\r
2381 hwndPause = buttonDesc[i].hwnd;
\r
2382 buttonDesc[i].wndproc = (WNDPROC)
\r
2383 SetWindowLong(buttonDesc[i].hwnd, GWL_WNDPROC, (LONG) ButtonProc);
\r
2386 if (gridPen != NULL) DeleteObject(gridPen);
\r
2387 if (highlightPen != NULL) DeleteObject(highlightPen);
\r
2388 if (premovePen != NULL) DeleteObject(premovePen);
\r
2389 if (lineGap != 0) {
\r
2390 logbrush.lbStyle = BS_SOLID;
\r
2391 logbrush.lbColor = RGB(0, 0, 0); /* grid pen color = black */
\r
2393 ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,
\r
2394 lineGap, &logbrush, 0, NULL);
\r
2395 logbrush.lbColor = highlightSquareColor;
\r
2397 ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,
\r
2398 lineGap, &logbrush, 0, NULL);
\r
2400 logbrush.lbColor = premoveHighlightColor;
\r
2402 ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,
\r
2403 lineGap, &logbrush, 0, NULL);
\r
2405 /* [HGM] Loop had to be split in part for vert. and hor. lines */
\r
2406 for (i = 0; i < BOARD_HEIGHT + 1; i++) {
\r
2407 gridEndpoints[i*2].x = boardRect.left + lineGap / 2;
\r
2408 gridEndpoints[i*2].y = gridEndpoints[i*2 + 1].y =
\r
2409 boardRect.top + lineGap / 2 + (i * (squareSize + lineGap));
\r
2410 gridEndpoints[i*2 + 1].x = boardRect.left + lineGap / 2 +
\r
2411 BOARD_WIDTH * (squareSize + lineGap);
\r
2412 gridVertexCounts[i*2] = gridVertexCounts[i*2 + 1] = 2;
\r
2414 for (i = 0; i < BOARD_WIDTH + 1; i++) {
\r
2415 gridEndpoints[i*2 + BOARD_HEIGHT*2 + 2].y = boardRect.top + lineGap / 2;
\r
2416 gridEndpoints[i*2 + BOARD_HEIGHT*2 + 2].x =
\r
2417 gridEndpoints[i*2 + 1 + BOARD_HEIGHT*2 + 2].x = boardRect.left +
\r
2418 lineGap / 2 + (i * (squareSize + lineGap));
\r
2419 gridEndpoints[i*2 + 1 + BOARD_HEIGHT*2 + 2].y =
\r
2420 boardRect.top + BOARD_HEIGHT * (squareSize + lineGap);
\r
2421 gridVertexCounts[i*2] = gridVertexCounts[i*2 + 1] = 2;
\r
2425 /* [HGM] Licensing requirement */
\r
2427 if(gameInfo.variant == VariantGothic) GothicPopUp( GOTHIC, VariantGothic); else
\r
2430 if(gameInfo.variant == VariantFalcon) GothicPopUp( FALCON, VariantFalcon); else
\r
2432 GothicPopUp( "", VariantNormal);
\r
2435 /* if (boardSize == oldBoardSize) return; [HGM] variant might have changed */
\r
2437 /* Load piece bitmaps for this board size */
\r
2438 for (i=0; i<=2; i++) {
\r
2439 for (piece = WhitePawn;
\r
2440 (int) piece < (int) BlackPawn;
\r
2441 piece = (ChessSquare) ((int) piece + 1)) {
\r
2442 if (pieceBitmap[i][piece] != NULL)
\r
2443 DeleteObject(pieceBitmap[i][piece]);
\r
2447 fontBitmapSquareSize = 0; /* [HGM] render: make sure pieces will be recreated, as we might need others now */
\r
2448 // Orthodox Chess pieces
\r
2449 pieceBitmap[0][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "s");
\r
2450 pieceBitmap[0][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "s");
\r
2451 pieceBitmap[0][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "s");
\r
2452 pieceBitmap[0][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "s");
\r
2453 pieceBitmap[0][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "s");
\r
2454 pieceBitmap[1][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "o");
\r
2455 pieceBitmap[1][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "o");
\r
2456 pieceBitmap[1][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "o");
\r
2457 pieceBitmap[1][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "o");
\r
2458 pieceBitmap[1][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "o");
\r
2459 pieceBitmap[2][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "w");
\r
2460 pieceBitmap[2][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "w");
\r
2461 pieceBitmap[2][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "w");
\r
2462 pieceBitmap[2][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "w");
\r
2463 pieceBitmap[2][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "w");
\r
2464 if( gameInfo.variant == VariantShogi && squareSize <= 72 && squareSize >= 33) {
\r
2465 // in Shogi, Hijack the unused Queen for Lance
\r
2466 pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "s");
\r
2467 pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "o");
\r
2468 pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "w");
\r
2470 pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "s");
\r
2471 pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "o");
\r
2472 pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "w");
\r
2475 if(squareSize <= 72 && squareSize >= 33) {
\r
2476 /* A & C are available in most sizes now */
\r
2477 if(squareSize != 49 && squareSize != 72 && squareSize != 33) { // Vortex-like
\r
2478 pieceBitmap[0][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "s");
\r
2479 pieceBitmap[1][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "o");
\r
2480 pieceBitmap[2][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "w");
\r
2481 pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "s");
\r
2482 pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "o");
\r
2483 pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "w");
\r
2484 pieceBitmap[0][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "s");
\r
2485 pieceBitmap[1][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "o");
\r
2486 pieceBitmap[2][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "w");
\r
2487 pieceBitmap[0][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "s");
\r
2488 pieceBitmap[1][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "o");
\r
2489 pieceBitmap[2][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "w");
\r
2490 } else { // Smirf-like
\r
2491 if(gameInfo.variant == VariantSChess) {
\r
2492 pieceBitmap[0][WhiteAngel] = DoLoadBitmap(hInst, "v", squareSize, "s");
\r
2493 pieceBitmap[1][WhiteAngel] = DoLoadBitmap(hInst, "v", squareSize, "o");
\r
2494 pieceBitmap[2][WhiteAngel] = DoLoadBitmap(hInst, "v", squareSize, "w");
\r
2496 pieceBitmap[0][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "s");
\r
2497 pieceBitmap[1][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "o");
\r
2498 pieceBitmap[2][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "w");
\r
2501 if(gameInfo.variant == VariantGothic) { // Vortex-like
\r
2502 pieceBitmap[0][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "s");
\r
2503 pieceBitmap[1][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "o");
\r
2504 pieceBitmap[2][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "w");
\r
2505 } else if(gameInfo.variant == VariantSChess && (squareSize == 49 || squareSize == 72)) {
\r
2506 pieceBitmap[0][WhiteMarshall] = DoLoadBitmap(hInst, "e", squareSize, "s");
\r
2507 pieceBitmap[1][WhiteMarshall] = DoLoadBitmap(hInst, "e", squareSize, "o");
\r
2508 pieceBitmap[2][WhiteMarshall] = DoLoadBitmap(hInst, "e", squareSize, "w");
\r
2509 } else { // WinBoard standard
\r
2510 pieceBitmap[0][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "s");
\r
2511 pieceBitmap[1][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "o");
\r
2512 pieceBitmap[2][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "w");
\r
2517 if(squareSize==72 || squareSize==49 || squareSize==33) { /* experiment with some home-made bitmaps */
\r
2518 pieceBitmap[0][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "s");
\r
2519 pieceBitmap[1][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "o");
\r
2520 pieceBitmap[2][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "w");
\r
2521 pieceBitmap[0][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "s");
\r
2522 pieceBitmap[1][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "o");
\r
2523 pieceBitmap[2][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "w");
\r
2524 pieceBitmap[0][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "s");
\r
2525 pieceBitmap[1][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "o");
\r
2526 pieceBitmap[2][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "w");
\r
2527 pieceBitmap[0][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "s");
\r
2528 pieceBitmap[1][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "o");
\r
2529 pieceBitmap[2][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "w");
\r
2530 pieceBitmap[0][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "s");
\r
2531 pieceBitmap[1][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "o");
\r
2532 pieceBitmap[2][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "w");
\r
2533 pieceBitmap[0][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "s");
\r
2534 pieceBitmap[1][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "o");
\r
2535 pieceBitmap[2][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "w");
\r
2536 pieceBitmap[0][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "s");
\r
2537 pieceBitmap[1][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "o");
\r
2538 pieceBitmap[2][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "w");
\r
2539 pieceBitmap[0][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "s");
\r
2540 pieceBitmap[1][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "o");
\r
2541 pieceBitmap[2][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "w");
\r
2542 pieceBitmap[0][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "s");
\r
2543 pieceBitmap[1][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "o");
\r
2544 pieceBitmap[2][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "w");
\r
2545 pieceBitmap[0][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "s");
\r
2546 pieceBitmap[1][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "o");
\r
2547 pieceBitmap[2][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "w");
\r
2549 if(gameInfo.variant == VariantShogi) { /* promoted Gold represemtations */
\r
2550 pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "wp", squareSize, "s");
\r
2551 pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "wp", squareSize, "o");
\r
2552 pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "w", squareSize, "w");
\r
2553 pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "wn", squareSize, "s");
\r
2554 pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "wn", squareSize, "o");
\r
2555 pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "w", squareSize, "w");
\r
2556 pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "ws", squareSize, "s");
\r
2557 pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "ws", squareSize, "o");
\r
2558 pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "w", squareSize, "w");
\r
2559 pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "wl", squareSize, "s");
\r
2560 pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "wl", squareSize, "o");
\r
2561 pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "w", squareSize, "w");
\r
2563 pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "s");
\r
2564 pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "o");
\r
2565 pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "w");
\r
2566 pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "s");
\r
2567 pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "o");
\r
2568 pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "w");
\r
2569 pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "s");
\r
2570 pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "o");
\r
2571 pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "w");
\r
2572 pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "s");
\r
2573 pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "o");
\r
2574 pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "w");
\r
2577 } else { /* other size, no special bitmaps available. Use smaller symbols */
\r
2578 if((int)boardSize < 2) minorSize = sizeInfo[0].squareSize;
\r
2579 else minorSize = sizeInfo[(int)boardSize - 2].squareSize;
\r
2580 pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "s");
\r
2581 pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "o");
\r
2582 pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "w");
\r
2583 pieceBitmap[0][WhiteCardinal] = DoLoadBitmap(hInst, "b", minorSize, "s");
\r
2584 pieceBitmap[1][WhiteCardinal] = DoLoadBitmap(hInst, "b", minorSize, "o");
\r
2585 pieceBitmap[2][WhiteCardinal] = DoLoadBitmap(hInst, "b", minorSize, "w");
\r
2586 pieceBitmap[0][WhiteDragon] = DoLoadBitmap(hInst, "r", minorSize, "s");
\r
2587 pieceBitmap[1][WhiteDragon] = DoLoadBitmap(hInst, "r", minorSize, "o");
\r
2588 pieceBitmap[2][WhiteDragon] = DoLoadBitmap(hInst, "r", minorSize, "w");
\r
2589 pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "s");
\r
2590 pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "o");
\r
2591 pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "w");
\r
2595 if(gameInfo.variant == VariantShogi && squareSize == 58)
\r
2596 /* special Shogi support in this size */
\r
2597 { for (i=0; i<=2; i++) { /* replace all bitmaps */
\r
2598 for (piece = WhitePawn;
\r
2599 (int) piece < (int) BlackPawn;
\r
2600 piece = (ChessSquare) ((int) piece + 1)) {
\r
2601 if (pieceBitmap[i][piece] != NULL)
\r
2602 DeleteObject(pieceBitmap[i][piece]);
\r
2605 pieceBitmap[0][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "o");
\r
2606 pieceBitmap[0][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "o");
\r
2607 pieceBitmap[0][WhiteBishop] = DoLoadBitmap(hInst, "sb", squareSize, "o");
\r
2608 pieceBitmap[0][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "o");
\r
2609 pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "o");
\r
2610 pieceBitmap[0][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "o");
\r
2611 pieceBitmap[0][WhiteFerz] = DoLoadBitmap(hInst, "sf", squareSize, "o");
\r
2612 pieceBitmap[0][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "o");
\r
2613 pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "su", squareSize, "o");
\r
2614 pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "sh", squareSize, "o");
\r
2615 pieceBitmap[0][WhiteCardinal] = DoLoadBitmap(hInst, "sa", squareSize, "o");
\r
2616 pieceBitmap[0][WhiteDragon] = DoLoadBitmap(hInst, "sc", squareSize, "o");
\r
2617 pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "sg", squareSize, "o");
\r
2618 pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "ss", squareSize, "o");
\r
2619 pieceBitmap[1][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "o");
\r
2620 pieceBitmap[1][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "o");
\r
2621 pieceBitmap[1][WhiteBishop] = DoLoadBitmap(hInst, "sb", squareSize, "o");
\r
2622 pieceBitmap[1][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "o");
\r
2623 pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "o");
\r
2624 pieceBitmap[1][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "o");
\r
2625 pieceBitmap[1][WhiteFerz] = DoLoadBitmap(hInst, "sf", squareSize, "o");
\r
2626 pieceBitmap[1][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "o");
\r
2627 pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "su", squareSize, "o");
\r
2628 pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "sh", squareSize, "o");
\r
2629 pieceBitmap[1][WhiteCardinal] = DoLoadBitmap(hInst, "sa", squareSize, "o");
\r
2630 pieceBitmap[1][WhiteDragon] = DoLoadBitmap(hInst, "sc", squareSize, "o");
\r
2631 pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "sg", squareSize, "o");
\r
2632 pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "ss", squareSize, "o");
\r
2633 pieceBitmap[2][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "w");
\r
2634 pieceBitmap[2][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "w");
\r
2635 pieceBitmap[2][WhiteBishop] = DoLoadBitmap(hInst, "sr", squareSize, "w");
\r
2636 pieceBitmap[2][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "w");
\r
2637 pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "w");
\r
2638 pieceBitmap[2][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "w");
\r
2639 pieceBitmap[2][WhiteFerz] = DoLoadBitmap(hInst, "sw", squareSize, "w");
\r
2640 pieceBitmap[2][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "w");
\r
2641 pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "sp", squareSize, "w");
\r
2642 pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "sn", squareSize, "w");
\r
2643 pieceBitmap[2][WhiteCardinal] = DoLoadBitmap(hInst, "sr", squareSize, "w");
\r
2644 pieceBitmap[2][WhiteDragon] = DoLoadBitmap(hInst, "sr", squareSize, "w");
\r
2645 pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "sl", squareSize, "w");
\r
2646 pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "sw", squareSize, "w");
\r
2652 PieceBitmap(ChessSquare p, int kind)
\r
2654 if ((int) p >= (int) BlackPawn)
\r
2655 p = (ChessSquare) ((int) p - (int) BlackPawn + (int) WhitePawn);
\r
2657 return pieceBitmap[kind][(int) p];
\r
2660 /***************************************************************/
\r
2662 #define MIN(a,b) (((a) < (b)) ? (a) : (b))
\r
2663 #define MAX(a,b) (((a) > (b)) ? (a) : (b))
\r
2665 #define MIN3(a,b,c) (((a) < (b) && (a) < (c)) ? (a) : (((b) < (a) && (b) < (c)) ? (b) : (c)))
\r
2666 #define MAX3(a,b,c) (((a) > (b) && (a) > (c)) ? (a) : (((b) > (a) && (b) > (c)) ? (b) : (c)))
\r
2670 SquareToPos(int row, int column, int * x, int * y)
\r
2673 *x = boardRect.left + lineGap + ((BOARD_WIDTH-1)-column) * (squareSize + lineGap);
\r
2674 *y = boardRect.top + lineGap + row * (squareSize + lineGap);
\r
2676 *x = boardRect.left + lineGap + column * (squareSize + lineGap);
\r
2677 *y = boardRect.top + lineGap + ((BOARD_HEIGHT-1)-row) * (squareSize + lineGap);
\r
2682 DrawCoordsOnDC(HDC hdc)
\r
2684 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
2685 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
2686 char str[2] = { NULLCHAR, NULLCHAR };
\r
2687 int oldMode, oldAlign, x, y, start, i;
\r
2691 if (!appData.showCoords)
\r
2694 start = flipView ? 1-(ONE!='1') : 23+(ONE!='1')-BOARD_HEIGHT;
\r
2696 oldBrush = SelectObject(hdc, GetStockObject(BLACK_BRUSH));
\r
2697 oldMode = SetBkMode(hdc, (appData.monoMode ? OPAQUE : TRANSPARENT));
\r
2698 oldAlign = GetTextAlign(hdc);
\r
2699 oldFont = SelectObject(hdc, font[boardSize][COORD_FONT]->hf);
\r
2701 y = boardRect.top + lineGap;
\r
2702 x = boardRect.left + lineGap + gameInfo.holdingsWidth*(squareSize + lineGap);
\r
2704 SetTextAlign(hdc, TA_LEFT|TA_TOP);
\r
2705 for (i = 0; i < BOARD_HEIGHT; i++) {
\r
2706 str[0] = files[start + i];
\r
2707 ExtTextOut(hdc, x + 2, y + 1, 0, NULL, str, 1, NULL);
\r
2708 y += squareSize + lineGap;
\r
2711 start = flipView ? 12-(BOARD_RGHT-BOARD_LEFT) : 12;
\r
2713 SetTextAlign(hdc, TA_RIGHT|TA_BOTTOM);
\r
2714 for (i = 0; i < BOARD_RGHT - BOARD_LEFT; i++) {
\r
2715 str[0] = ranks[start + i];
\r
2716 ExtTextOut(hdc, x + squareSize - 2, y - 1, 0, NULL, str, 1, NULL);
\r
2717 x += squareSize + lineGap;
\r
2720 SelectObject(hdc, oldBrush);
\r
2721 SetBkMode(hdc, oldMode);
\r
2722 SetTextAlign(hdc, oldAlign);
\r
2723 SelectObject(hdc, oldFont);
\r
2727 DrawGridOnDC(HDC hdc)
\r
2731 if (lineGap != 0) {
\r
2732 oldPen = SelectObject(hdc, gridPen);
\r
2733 PolyPolyline(hdc, gridEndpoints, gridVertexCounts, BOARD_WIDTH+BOARD_HEIGHT + 2);
\r
2734 SelectObject(hdc, oldPen);
\r
2738 #define HIGHLIGHT_PEN 0
\r
2739 #define PREMOVE_PEN 1
\r
2742 DrawHighlightOnDC(HDC hdc, BOOLEAN on, int x, int y, int pen)
\r
2745 HPEN oldPen, hPen;
\r
2746 if (lineGap == 0) return;
\r
2748 x1 = boardRect.left +
\r
2749 lineGap/2 + ((BOARD_WIDTH-1)-x) * (squareSize + lineGap);
\r
2750 y1 = boardRect.top +
\r
2751 lineGap/2 + y * (squareSize + lineGap);
\r
2753 x1 = boardRect.left +
\r
2754 lineGap/2 + x * (squareSize + lineGap);
\r
2755 y1 = boardRect.top +
\r
2756 lineGap/2 + ((BOARD_HEIGHT-1)-y) * (squareSize + lineGap);
\r
2758 hPen = pen ? premovePen : highlightPen;
\r
2759 oldPen = SelectObject(hdc, on ? hPen : gridPen);
\r
2760 MoveToEx(hdc, x1, y1, NULL);
\r
2761 LineTo(hdc, x1 + squareSize + lineGap, y1);
\r
2762 LineTo(hdc, x1 + squareSize + lineGap, y1 + squareSize + lineGap);
\r
2763 LineTo(hdc, x1, y1 + squareSize + lineGap);
\r
2764 LineTo(hdc, x1, y1);
\r
2765 SelectObject(hdc, oldPen);
\r
2769 DrawHighlightsOnDC(HDC hdc, HighlightInfo *h, int pen)
\r
2772 for (i=0; i<2; i++) {
\r
2773 if (h->sq[i].x >= 0 && h->sq[i].y >= 0)
\r
2774 DrawHighlightOnDC(hdc, TRUE,
\r
2775 h->sq[i].x, h->sq[i].y,
\r
2780 /* Note: sqcolor is used only in monoMode */
\r
2781 /* Note that this code is largely duplicated in woptions.c,
\r
2782 function DrawSampleSquare, so that needs to be updated too */
\r
2784 DrawPieceOnDC(HDC hdc, ChessSquare piece, int color, int sqcolor, int x, int y, HDC tmphdc)
\r
2786 HBITMAP oldBitmap;
\r
2790 if (appData.blindfold) return;
\r
2792 /* [AS] Use font-based pieces if needed */
\r
2793 if( fontBitmapSquareSize >= 0 && (squareSize > 32 || gameInfo.variant >= VariantShogi)) {
\r
2794 /* Create piece bitmaps, or do nothing if piece set is up to date */
\r
2795 CreatePiecesFromFont();
\r
2797 if( fontBitmapSquareSize == squareSize ) {
\r
2798 int index = TranslatePieceToFontPiece(piece);
\r
2800 SelectObject( tmphdc, hPieceMask[ index ] );
\r
2802 if(appData.upsideDown ? color==flipView : (flipView && gameInfo.variant == VariantShogi))
\r
2803 StretchBlt(hdc, x+squareSize, y+squareSize, -squareSize, -squareSize, tmphdc, 0, 0, squareSize, squareSize, SRCAND);
\r
2807 squareSize, squareSize,
\r
2812 SelectObject( tmphdc, hPieceFace[ index ] );
\r
2814 if(appData.upsideDown ? color==flipView : (flipView && gameInfo.variant == VariantShogi))
\r
2815 StretchBlt(hdc, x+squareSize, y+squareSize, -squareSize, -squareSize, tmphdc, 0, 0, squareSize, squareSize, SRCPAINT);
\r
2819 squareSize, squareSize,
\r
2828 if (appData.monoMode) {
\r
2829 SelectObject(tmphdc, PieceBitmap(piece,
\r
2830 color == sqcolor ? OUTLINE_PIECE : SOLID_PIECE));
\r
2831 BitBlt(hdc, x, y, squareSize, squareSize, tmphdc, 0, 0,
\r
2832 sqcolor ? SRCCOPY : NOTSRCCOPY);
\r
2834 tmpSize = squareSize;
\r
2836 ((piece >= (int)WhiteNightrider && piece <= WhiteGrasshopper) ||
\r
2837 (piece >= (int)BlackNightrider && piece <= BlackGrasshopper)) ) {
\r
2838 /* [HGM] no bitmap available for promoted pieces in Crazyhouse */
\r
2839 /* Bitmaps of smaller size are substituted, but we have to align them */
\r
2840 x += (squareSize - minorSize)>>1;
\r
2841 y += squareSize - minorSize - 2;
\r
2842 tmpSize = minorSize;
\r
2844 if (color || appData.allWhite ) {
\r
2845 oldBitmap = SelectObject(tmphdc, PieceBitmap(piece, WHITE_PIECE));
\r
2847 oldBrush = SelectObject(hdc, whitePieceBrush);
\r
2848 else oldBrush = SelectObject(hdc, blackPieceBrush);
\r
2849 if(appData.upsideDown && color==flipView)
\r
2850 StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, 0x00B8074A);
\r
2852 BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, 0x00B8074A);
\r
2853 /* Use black for outline of white pieces */
\r
2854 SelectObject(tmphdc, PieceBitmap(piece, OUTLINE_PIECE));
\r
2855 if(appData.upsideDown && color==flipView)
\r
2856 StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, SRCAND);
\r
2858 BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, SRCAND);
\r
2860 /* Use square color for details of black pieces */
\r
2861 oldBitmap = SelectObject(tmphdc, PieceBitmap(piece, SOLID_PIECE));
\r
2862 oldBrush = SelectObject(hdc, blackPieceBrush);
\r
2863 if(appData.upsideDown && !flipView)
\r
2864 StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, 0x00B8074A);
\r
2866 BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, 0x00B8074A);
\r
2868 SelectObject(hdc, oldBrush);
\r
2869 SelectObject(tmphdc, oldBitmap);
\r
2873 /* [AS] Compute a drawing mode for a square, based on specified settings (see DrawTile) */
\r
2874 int GetBackTextureMode( int algo )
\r
2876 int result = BACK_TEXTURE_MODE_DISABLED;
\r
2880 case BACK_TEXTURE_MODE_PLAIN:
\r
2881 result = 1; /* Always use identity map */
\r
2883 case BACK_TEXTURE_MODE_FULL_RANDOM:
\r
2884 result = 1 + (myrandom() % 3); /* Pick a transformation at random */
\r
2892 [AS] Compute and save texture drawing info, otherwise we may not be able
\r
2893 to handle redraws cleanly (as random numbers would always be different).
\r
2895 VOID RebuildTextureSquareInfo()
\r
2905 ZeroMemory( &backTextureSquareInfo, sizeof(backTextureSquareInfo) );
\r
2907 if( liteBackTexture != NULL ) {
\r
2908 if( GetObject( liteBackTexture, sizeof(bi), &bi ) > 0 ) {
\r
2909 lite_w = bi.bmWidth;
\r
2910 lite_h = bi.bmHeight;
\r
2914 if( darkBackTexture != NULL ) {
\r
2915 if( GetObject( darkBackTexture, sizeof(bi), &bi ) > 0 ) {
\r
2916 dark_w = bi.bmWidth;
\r
2917 dark_h = bi.bmHeight;
\r
2921 for( row=0; row<BOARD_HEIGHT; row++ ) {
\r
2922 for( col=0; col<BOARD_WIDTH; col++ ) {
\r
2923 if( (col + row) & 1 ) {
\r
2925 if( lite_w >= squareSize && lite_h >= squareSize ) {
\r
2926 if( lite_w >= squareSize*BOARD_WIDTH )
\r
2927 backTextureSquareInfo[row][col].x = (2*col+1)*lite_w/(2*BOARD_WIDTH) - squareSize/2; /* [HGM] cut out of center of virtual square */
\r
2929 backTextureSquareInfo[row][col].x = col * (lite_w - squareSize) / (BOARD_WIDTH-1); /* [HGM] divide by size-1 in stead of size! */
\r
2930 if( lite_h >= squareSize*BOARD_HEIGHT )
\r
2931 backTextureSquareInfo[row][col].y = (2*(BOARD_HEIGHT-row)-1)*lite_h/(2*BOARD_HEIGHT) - squareSize/2;
\r
2933 backTextureSquareInfo[row][col].y = (BOARD_HEIGHT-1-row) * (lite_h - squareSize) / (BOARD_HEIGHT-1);
\r
2934 backTextureSquareInfo[row][col].mode = GetBackTextureMode(liteBackTextureMode);
\r
2939 if( dark_w >= squareSize && dark_h >= squareSize ) {
\r
2940 if( dark_w >= squareSize*BOARD_WIDTH )
\r
2941 backTextureSquareInfo[row][col].x = (2*col+1) * dark_w / (2*BOARD_WIDTH) - squareSize/2;
\r
2943 backTextureSquareInfo[row][col].x = col * (dark_w - squareSize) / (BOARD_WIDTH-1);
\r
2944 if( dark_h >= squareSize*BOARD_HEIGHT )
\r
2945 backTextureSquareInfo[row][col].y = (2*(BOARD_HEIGHT-row)-1) * dark_h / (2*BOARD_HEIGHT) - squareSize/2;
\r
2947 backTextureSquareInfo[row][col].y = (BOARD_HEIGHT-1-row) * (dark_h - squareSize) / (BOARD_HEIGHT-1);
\r
2948 backTextureSquareInfo[row][col].mode = GetBackTextureMode(darkBackTextureMode);
\r
2955 /* [AS] Arrow highlighting support */
\r
2957 static double A_WIDTH = 5; /* Width of arrow body */
\r
2959 #define A_HEIGHT_FACTOR 6 /* Length of arrow "point", relative to body width */
\r
2960 #define A_WIDTH_FACTOR 3 /* Width of arrow "point", relative to body width */
\r
2962 static double Sqr( double x )
\r
2967 static int Round( double x )
\r
2969 return (int) (x + 0.5);
\r
2972 /* Draw an arrow between two points using current settings */
\r
2973 VOID DrawArrowBetweenPoints( HDC hdc, int s_x, int s_y, int d_x, int d_y )
\r
2976 double dx, dy, j, k, x, y;
\r
2978 if( d_x == s_x ) {
\r
2979 int h = (d_y > s_y) ? +A_WIDTH*A_HEIGHT_FACTOR : -A_WIDTH*A_HEIGHT_FACTOR;
\r
2981 arrow[0].x = s_x + A_WIDTH + 0.5;
\r
2984 arrow[1].x = s_x + A_WIDTH + 0.5;
\r
2985 arrow[1].y = d_y - h;
\r
2987 arrow[2].x = arrow[1].x + A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;
\r
2988 arrow[2].y = d_y - h;
\r
2993 arrow[5].x = arrow[1].x - 2*A_WIDTH + 0.5;
\r
2994 arrow[5].y = d_y - h;
\r
2996 arrow[4].x = arrow[5].x - A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;
\r
2997 arrow[4].y = d_y - h;
\r
2999 arrow[6].x = arrow[1].x - 2*A_WIDTH + 0.5;
\r
3002 else if( d_y == s_y ) {
\r
3003 int w = (d_x > s_x) ? +A_WIDTH*A_HEIGHT_FACTOR : -A_WIDTH*A_HEIGHT_FACTOR;
\r
3006 arrow[0].y = s_y + A_WIDTH + 0.5;
\r
3008 arrow[1].x = d_x - w;
\r
3009 arrow[1].y = s_y + A_WIDTH + 0.5;
\r
3011 arrow[2].x = d_x - w;
\r
3012 arrow[2].y = arrow[1].y + A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;
\r
3017 arrow[5].x = d_x - w;
\r
3018 arrow[5].y = arrow[1].y - 2*A_WIDTH + 0.5;
\r
3020 arrow[4].x = d_x - w;
\r
3021 arrow[4].y = arrow[5].y - A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;
\r
3024 arrow[6].y = arrow[1].y - 2*A_WIDTH + 0.5;
\r
3027 /* [AS] Needed a lot of paper for this! :-) */
\r
3028 dy = (double) (d_y - s_y) / (double) (d_x - s_x);
\r
3029 dx = (double) (s_x - d_x) / (double) (s_y - d_y);
\r
3031 j = sqrt( Sqr(A_WIDTH) / (1.0 + Sqr(dx)) );
\r
3033 k = sqrt( Sqr(A_WIDTH*A_HEIGHT_FACTOR) / (1.0 + Sqr(dy)) );
\r
3038 arrow[0].x = Round(x - j);
\r
3039 arrow[0].y = Round(y + j*dx);
\r
3041 arrow[1].x = Round(arrow[0].x + 2*j); // [HGM] prevent width to be affected by rounding twice
\r
3042 arrow[1].y = Round(arrow[0].y - 2*j*dx);
\r
3045 x = (double) d_x - k;
\r
3046 y = (double) d_y - k*dy;
\r
3049 x = (double) d_x + k;
\r
3050 y = (double) d_y + k*dy;
\r
3053 x = Round(x); y = Round(y); // [HGM] make sure width of shaft is rounded the same way on both ends
\r
3055 arrow[6].x = Round(x - j);
\r
3056 arrow[6].y = Round(y + j*dx);
\r
3058 arrow[2].x = Round(arrow[6].x + 2*j);
\r
3059 arrow[2].y = Round(arrow[6].y - 2*j*dx);
\r
3061 arrow[3].x = Round(arrow[2].x + j*(A_WIDTH_FACTOR-1));
\r
3062 arrow[3].y = Round(arrow[2].y - j*(A_WIDTH_FACTOR-1)*dx);
\r
3067 arrow[5].x = Round(arrow[6].x - j*(A_WIDTH_FACTOR-1));
\r
3068 arrow[5].y = Round(arrow[6].y + j*(A_WIDTH_FACTOR-1)*dx);
\r
3071 Polygon( hdc, arrow, 7 );
\r
3074 /* [AS] Draw an arrow between two squares */
\r
3075 VOID DrawArrowBetweenSquares( HDC hdc, int s_col, int s_row, int d_col, int d_row )
\r
3077 int s_x, s_y, d_x, d_y;
\r
3084 if( s_col == d_col && s_row == d_row ) {
\r
3088 /* Get source and destination points */
\r
3089 SquareToPos( s_row, s_col, &s_x, &s_y);
\r
3090 SquareToPos( d_row, d_col, &d_x, &d_y);
\r
3093 d_y += squareSize / 2 - squareSize / 4; // [HGM] round towards same centers on all sides!
\r
3095 else if( d_y < s_y ) {
\r
3096 d_y += squareSize / 2 + squareSize / 4;
\r
3099 d_y += squareSize / 2;
\r
3103 d_x += squareSize / 2 - squareSize / 4;
\r
3105 else if( d_x < s_x ) {
\r
3106 d_x += squareSize / 2 + squareSize / 4;
\r
3109 d_x += squareSize / 2;
\r
3112 s_x += squareSize / 2;
\r
3113 s_y += squareSize / 2;
\r
3115 /* Adjust width */
\r
3116 A_WIDTH = squareSize / 14.; //[HGM] make float
\r
3119 stLB.lbStyle = BS_SOLID;
\r
3120 stLB.lbColor = appData.highlightArrowColor;
\r
3123 hpen = CreatePen( PS_SOLID, 2, RGB(0x00,0x00,0x00) );
\r
3124 holdpen = SelectObject( hdc, hpen );
\r
3125 hbrush = CreateBrushIndirect( &stLB );
\r
3126 holdbrush = SelectObject( hdc, hbrush );
\r
3128 DrawArrowBetweenPoints( hdc, s_x, s_y, d_x, d_y );
\r
3130 SelectObject( hdc, holdpen );
\r
3131 SelectObject( hdc, holdbrush );
\r
3132 DeleteObject( hpen );
\r
3133 DeleteObject( hbrush );
\r
3136 BOOL HasHighlightInfo()
\r
3138 BOOL result = FALSE;
\r
3140 if( highlightInfo.sq[0].x >= 0 && highlightInfo.sq[0].y >= 0 &&
\r
3141 highlightInfo.sq[1].x >= 0 && highlightInfo.sq[1].y >= 0 )
\r
3149 BOOL IsDrawArrowEnabled()
\r
3151 BOOL result = FALSE;
\r
3153 if( appData.highlightMoveWithArrow && squareSize >= 32 ) {
\r
3160 VOID DrawArrowHighlight( HDC hdc )
\r
3162 if( IsDrawArrowEnabled() && HasHighlightInfo() ) {
\r
3163 DrawArrowBetweenSquares( hdc,
\r
3164 highlightInfo.sq[0].x, highlightInfo.sq[0].y,
\r
3165 highlightInfo.sq[1].x, highlightInfo.sq[1].y );
\r
3169 HRGN GetArrowHighlightClipRegion( HDC hdc )
\r
3171 HRGN result = NULL;
\r
3173 if( HasHighlightInfo() ) {
\r
3174 int x1, y1, x2, y2;
\r
3175 int sx, sy, dx, dy;
\r
3177 SquareToPos(highlightInfo.sq[0].y, highlightInfo.sq[0].x, &x1, &y1 );
\r
3178 SquareToPos(highlightInfo.sq[1].y, highlightInfo.sq[1].x, &x2, &y2 );
\r
3180 sx = MIN( x1, x2 );
\r
3181 sy = MIN( y1, y2 );
\r
3182 dx = MAX( x1, x2 ) + squareSize;
\r
3183 dy = MAX( y1, y2 ) + squareSize;
\r
3185 result = CreateRectRgn( sx, sy, dx, dy );
\r
3192 Warning: this function modifies the behavior of several other functions.
\r
3194 Basically, Winboard is optimized to avoid drawing the whole board if not strictly
\r
3195 needed. Unfortunately, the decision whether or not to perform a full or partial
\r
3196 repaint is scattered all over the place, which is not good for features such as
\r
3197 "arrow highlighting" that require a full repaint of the board.
\r
3199 So, I've tried to patch the code where I thought it made sense (e.g. after or during
\r
3200 user interaction, when speed is not so important) but especially to avoid errors
\r
3201 in the displayed graphics.
\r
3203 In such patched places, I always try refer to this function so there is a single
\r
3204 place to maintain knowledge.
\r
3206 To restore the original behavior, just return FALSE unconditionally.
\r
3208 BOOL IsFullRepaintPreferrable()
\r
3210 BOOL result = FALSE;
\r
3212 if( (appData.highlightLastMove || appData.highlightDragging) && IsDrawArrowEnabled() ) {
\r
3213 /* Arrow may appear on the board */
\r
3221 This function is called by DrawPosition to know whether a full repaint must
\r
3224 Only DrawPosition may directly call this function, which makes use of
\r
3225 some state information. Other function should call DrawPosition specifying
\r
3226 the repaint flag, and can use IsFullRepaintPreferrable if needed.
\r
3228 BOOL DrawPositionNeedsFullRepaint()
\r
3230 BOOL result = FALSE;
\r
3233 Probably a slightly better policy would be to trigger a full repaint
\r
3234 when animInfo.piece changes state (i.e. empty -> non-empty and viceversa),
\r
3235 but animation is fast enough that it's difficult to notice.
\r
3237 if( animInfo.piece == EmptySquare ) {
\r
3238 if( (appData.highlightLastMove || appData.highlightDragging) && IsDrawArrowEnabled() /*&& HasHighlightInfo()*/ ) {
\r
3247 DrawBoardOnDC(HDC hdc, Board board, HDC tmphdc)
\r
3249 int row, column, x, y, square_color, piece_color;
\r
3250 ChessSquare piece;
\r
3252 HDC texture_hdc = NULL;
\r
3254 /* [AS] Initialize background textures if needed */
\r
3255 if( liteBackTexture != NULL || darkBackTexture != NULL ) {
\r
3256 static int backTextureBoardSize; /* [HGM] boardsize: also new texture if board format changed */
\r
3257 if( backTextureSquareSize != squareSize
\r
3258 || backTextureBoardSize != BOARD_WIDTH+BOARD_FILES*BOARD_HEIGHT) {
\r
3259 backTextureBoardSize = BOARD_WIDTH+BOARD_FILES*BOARD_HEIGHT;
\r
3260 backTextureSquareSize = squareSize;
\r
3261 RebuildTextureSquareInfo();
\r
3264 texture_hdc = CreateCompatibleDC( hdc );
\r
3267 for (row = 0; row < BOARD_HEIGHT; row++) {
\r
3268 for (column = 0; column < BOARD_WIDTH; column++) {
\r
3270 SquareToPos(row, column, &x, &y);
\r
3272 piece = board[row][column];
\r
3274 square_color = ((column + row) % 2) == 1;
\r
3275 if( gameInfo.variant == VariantXiangqi ) {
\r
3276 square_color = !InPalace(row, column);
\r
3277 if(BOARD_HEIGHT&1) { if(row==BOARD_HEIGHT/2) square_color ^= 1; }
\r
3278 else if(row < BOARD_HEIGHT/2) square_color ^= 1;
\r
3280 piece_color = (int) piece < (int) BlackPawn;
\r
3283 /* [HGM] holdings file: light square or black */
\r
3284 if(column == BOARD_LEFT-2) {
\r
3285 if( row > BOARD_HEIGHT - gameInfo.holdingsSize - 1 )
\r
3288 DisplayHoldingsCount(hdc, x, y, 0, 0); /* black out */
\r
3292 if(column == BOARD_RGHT + 1 ) {
\r
3293 if( row < gameInfo.holdingsSize )
\r
3296 DisplayHoldingsCount(hdc, x, y, 0, 0);
\r
3300 if(column == BOARD_LEFT-1 ) /* left align */
\r
3301 DisplayHoldingsCount(hdc, x, y, flipView, (int) board[row][column]);
\r
3302 else if( column == BOARD_RGHT) /* right align */
\r
3303 DisplayHoldingsCount(hdc, x, y, !flipView, (int) board[row][column]);
\r
3305 if (appData.monoMode) {
\r
3306 if (piece == EmptySquare) {
\r
3307 BitBlt(hdc, x, y, squareSize, squareSize, 0, 0, 0,
\r
3308 square_color ? WHITENESS : BLACKNESS);
\r
3310 DrawPieceOnDC(hdc, piece, piece_color, square_color, x, y, tmphdc);
\r
3313 else if( backTextureSquareInfo[row][column].mode > 0 ) {
\r
3314 /* [AS] Draw the square using a texture bitmap */
\r
3315 HBITMAP hbm = SelectObject( texture_hdc, square_color ? liteBackTexture : darkBackTexture );
\r
3316 int r = row, c = column; // [HGM] do not flip board in flipView
\r
3317 if(flipView) { r = BOARD_HEIGHT-1 - r; c = BOARD_WIDTH-1 - c; }
\r
3320 squareSize, squareSize,
\r
3323 backTextureSquareInfo[r][c].mode,
\r
3324 backTextureSquareInfo[r][c].x,
\r
3325 backTextureSquareInfo[r][c].y );
\r
3327 SelectObject( texture_hdc, hbm );
\r
3329 if (piece != EmptySquare) {
\r
3330 DrawPieceOnDC(hdc, piece, piece_color, -1, x, y, tmphdc);
\r
3334 HBRUSH brush = square_color ? lightSquareBrush : darkSquareBrush;
\r
3336 oldBrush = SelectObject(hdc, brush );
\r
3337 BitBlt(hdc, x, y, squareSize, squareSize, 0, 0, 0, PATCOPY);
\r
3338 SelectObject(hdc, oldBrush);
\r
3339 if (piece != EmptySquare)
\r
3340 DrawPieceOnDC(hdc, piece, piece_color, -1, x, y, tmphdc);
\r
3345 if( texture_hdc != NULL ) {
\r
3346 DeleteDC( texture_hdc );
\r
3350 int saveDiagFlag = 0; FILE *diagFile; // [HGM] diag
\r
3351 void fputDW(FILE *f, int x)
\r
3353 fputc(x & 255, f);
\r
3354 fputc(x>>8 & 255, f);
\r
3355 fputc(x>>16 & 255, f);
\r
3356 fputc(x>>24 & 255, f);
\r
3359 #define MAX_CLIPS 200 /* more than enough */
\r
3362 DrawLogoOnDC(HDC hdc, RECT logoRect, HBITMAP logo)
\r
3364 // HBITMAP bufferBitmap;
\r
3369 int w = 100, h = 50;
\r
3371 if(logo == NULL) return;
\r
3372 // GetClientRect(hwndMain, &Rect);
\r
3373 // bufferBitmap = CreateCompatibleBitmap(hdc, Rect.right-Rect.left+1,
\r
3374 // Rect.bottom-Rect.top+1);
\r
3375 tmphdc = CreateCompatibleDC(hdc);
\r
3376 hbm = SelectObject(tmphdc, logo);
\r
3377 if( GetObject( logo, sizeof(bi), &bi ) > 0 ) {
\r
3381 StretchBlt(hdc, logoRect.left, logoRect.top, logoRect.right - logoRect.left,
\r
3382 logoRect.bottom - logoRect.top, tmphdc, 0, 0, w, h, SRCCOPY);
\r
3383 SelectObject(tmphdc, hbm);
\r
3387 static HDC hdcSeek;
\r
3389 // [HGM] seekgraph
\r
3390 void DrawSeekAxis( int x, int y, int xTo, int yTo )
\r
3393 HPEN hp = SelectObject( hdcSeek, gridPen );
\r
3394 MoveToEx( hdcSeek, boardRect.left+x, boardRect.top+y, &stPt );
\r
3395 LineTo( hdcSeek, boardRect.left+xTo, boardRect.top+yTo );
\r
3396 SelectObject( hdcSeek, hp );
\r
3399 // front-end wrapper for drawing functions to do rectangles
\r
3400 void DrawSeekBackground( int left, int top, int right, int bottom )
\r
3405 if (hdcSeek == NULL) {
\r
3406 hdcSeek = GetDC(hwndMain);
\r
3407 if (!appData.monoMode) {
\r
3408 SelectPalette(hdcSeek, hPal, FALSE);
\r
3409 RealizePalette(hdcSeek);
\r
3412 hp = SelectObject( hdcSeek, gridPen );
\r
3413 rc.top = boardRect.top+top; rc.left = boardRect.left+left;
\r
3414 rc.bottom = boardRect.top+bottom; rc.right = boardRect.left+right;
\r
3415 FillRect( hdcSeek, &rc, lightSquareBrush );
\r
3416 SelectObject( hdcSeek, hp );
\r
3419 // front-end wrapper for putting text in graph
\r
3420 void DrawSeekText(char *buf, int x, int y)
\r
3423 SetBkMode( hdcSeek, TRANSPARENT );
\r
3424 GetTextExtentPoint32( hdcSeek, buf, strlen(buf), &stSize );
\r
3425 TextOut( hdcSeek, boardRect.left+x-3, boardRect.top+y-stSize.cy/2, buf, strlen(buf) );
\r
3428 void DrawSeekDot(int x, int y, int color)
\r
3430 int square = color & 0x80;
\r
3431 HBRUSH oldBrush = SelectObject(hdcSeek,
\r
3432 color == 0 ? markerBrush : color == 1 ? darkSquareBrush : explodeBrush);
\r
3435 Rectangle(hdcSeek, boardRect.left+x - squareSize/9, boardRect.top+y - squareSize/9,
\r
3436 boardRect.left+x + squareSize/9, boardRect.top+y + squareSize/9);
\r
3438 Ellipse(hdcSeek, boardRect.left+x - squareSize/8, boardRect.top+y - squareSize/8,
\r
3439 boardRect.left+x + squareSize/8, boardRect.top+y + squareSize/8);
\r
3440 SelectObject(hdcSeek, oldBrush);
\r
3444 HDCDrawPosition(HDC hdc, BOOLEAN repaint, Board board)
\r
3446 static Board lastReq[2], lastDrawn[2];
\r
3447 static HighlightInfo lastDrawnHighlight, lastDrawnPremove;
\r
3448 static int lastDrawnFlipView = 0;
\r
3449 static int lastReqValid[2] = {0, 0}, lastDrawnValid[2] = {0, 0};
\r
3450 int releaseDC, x, y, x2, y2, row, column, num_clips = 0, i;
\r
3453 HBITMAP bufferBitmap;
\r
3454 HBITMAP oldBitmap;
\r
3456 HRGN clips[MAX_CLIPS];
\r
3457 ChessSquare dragged_piece = EmptySquare;
\r
3458 int nr = twoBoards*partnerUp;
\r
3460 /* I'm undecided on this - this function figures out whether a full
\r
3461 * repaint is necessary on its own, so there's no real reason to have the
\r
3462 * caller tell it that. I think this can safely be set to FALSE - but
\r
3463 * if we trust the callers not to request full repaints unnessesarily, then
\r
3464 * we could skip some clipping work. In other words, only request a full
\r
3465 * redraw when the majority of pieces have changed positions (ie. flip,
\r
3466 * gamestart and similar) --Hawk
\r
3468 Boolean fullrepaint = repaint;
\r
3470 if(DrawSeekGraph()) return; // [HG} seekgraph: suppress printing board if seek graph up
\r
3472 if( DrawPositionNeedsFullRepaint() ) {
\r
3473 fullrepaint = TRUE;
\r
3476 if (board == NULL) {
\r
3477 if (!lastReqValid[nr]) {
\r
3480 board = lastReq[nr];
\r
3482 CopyBoard(lastReq[nr], board);
\r
3483 lastReqValid[nr] = 1;
\r
3486 if (doingSizing) {
\r
3490 if (IsIconic(hwndMain)) {
\r
3494 if (hdc == NULL) {
\r
3495 hdc = GetDC(hwndMain);
\r
3496 if (!appData.monoMode) {
\r
3497 SelectPalette(hdc, hPal, FALSE);
\r
3498 RealizePalette(hdc);
\r
3502 releaseDC = FALSE;
\r
3505 /* Create some work-DCs */
\r
3506 hdcmem = CreateCompatibleDC(hdc);
\r
3507 tmphdc = CreateCompatibleDC(hdc);
\r
3509 /* If dragging is in progress, we temporarely remove the piece */
\r
3510 /* [HGM] or temporarily decrease count if stacked */
\r
3511 /* !! Moved to before board compare !! */
\r
3512 if (dragInfo.from.x >= 0 && dragInfo.pos.x >= 0) {
\r
3513 dragged_piece = board[dragInfo.from.y][dragInfo.from.x];
\r
3514 if(dragInfo.from.x == BOARD_LEFT-2 ) {
\r
3515 if(--board[dragInfo.from.y][dragInfo.from.x+1] == 0 )
\r
3516 board[dragInfo.from.y][dragInfo.from.x] = EmptySquare;
\r
3518 if(dragInfo.from.x == BOARD_RGHT+1) {
\r
3519 if(--board[dragInfo.from.y][dragInfo.from.x-1] == 0 )
\r
3520 board[dragInfo.from.y][dragInfo.from.x] = EmptySquare;
\r
3522 board[dragInfo.from.y][dragInfo.from.x] = gatingPiece;
\r
3525 /* Figure out which squares need updating by comparing the
\r
3526 * newest board with the last drawn board and checking if
\r
3527 * flipping has changed.
\r
3529 if (!fullrepaint && lastDrawnValid[nr] && (nr == 1 || lastDrawnFlipView == flipView)) {
\r
3530 for (row = 0; row < BOARD_HEIGHT; row++) { /* [HGM] true size, not 8 */
\r
3531 for (column = 0; column < BOARD_WIDTH; column++) {
\r
3532 if (lastDrawn[nr][row][column] != board[row][column]) {
\r
3533 SquareToPos(row, column, &x, &y);
\r
3534 clips[num_clips++] =
\r
3535 CreateRectRgn(x, y, x + squareSize, y + squareSize);
\r
3539 if(nr == 0) { // [HGM] dual: no highlights on second board
\r
3540 for (i=0; i<2; i++) {
\r
3541 if (lastDrawnHighlight.sq[i].x != highlightInfo.sq[i].x ||
\r
3542 lastDrawnHighlight.sq[i].y != highlightInfo.sq[i].y) {
\r
3543 if (lastDrawnHighlight.sq[i].x >= 0 &&
\r
3544 lastDrawnHighlight.sq[i].y >= 0) {
\r
3545 SquareToPos(lastDrawnHighlight.sq[i].y,
\r
3546 lastDrawnHighlight.sq[i].x, &x, &y);
\r
3547 clips[num_clips++] =
\r
3548 CreateRectRgn(x - lineGap, y - lineGap,
\r
3549 x + squareSize + lineGap, y + squareSize + lineGap);
\r
3551 if (highlightInfo.sq[i].x >= 0 && highlightInfo.sq[i].y >= 0) {
\r
3552 SquareToPos(highlightInfo.sq[i].y, highlightInfo.sq[i].x, &x, &y);
\r
3553 clips[num_clips++] =
\r
3554 CreateRectRgn(x - lineGap, y - lineGap,
\r
3555 x + squareSize + lineGap, y + squareSize + lineGap);
\r
3559 for (i=0; i<2; i++) {
\r
3560 if (lastDrawnPremove.sq[i].x != premoveHighlightInfo.sq[i].x ||
\r
3561 lastDrawnPremove.sq[i].y != premoveHighlightInfo.sq[i].y) {
\r
3562 if (lastDrawnPremove.sq[i].x >= 0 &&
\r
3563 lastDrawnPremove.sq[i].y >= 0) {
\r
3564 SquareToPos(lastDrawnPremove.sq[i].y,
\r
3565 lastDrawnPremove.sq[i].x, &x, &y);
\r
3566 clips[num_clips++] =
\r
3567 CreateRectRgn(x - lineGap, y - lineGap,
\r
3568 x + squareSize + lineGap, y + squareSize + lineGap);
\r
3570 if (premoveHighlightInfo.sq[i].x >= 0 &&
\r
3571 premoveHighlightInfo.sq[i].y >= 0) {
\r
3572 SquareToPos(premoveHighlightInfo.sq[i].y,
\r
3573 premoveHighlightInfo.sq[i].x, &x, &y);
\r
3574 clips[num_clips++] =
\r
3575 CreateRectRgn(x - lineGap, y - lineGap,
\r
3576 x + squareSize + lineGap, y + squareSize + lineGap);
\r
3580 } else { // nr == 1
\r
3581 partnerHighlightInfo.sq[0].y = board[EP_STATUS-4];
\r
3582 partnerHighlightInfo.sq[0].x = board[EP_STATUS-3];
\r
3583 partnerHighlightInfo.sq[1].y = board[EP_STATUS-2];
\r
3584 partnerHighlightInfo.sq[1].x = board[EP_STATUS-1];
\r
3585 for (i=0; i<2; i++) {
\r
3586 if (partnerHighlightInfo.sq[i].x >= 0 &&
\r
3587 partnerHighlightInfo.sq[i].y >= 0) {
\r
3588 SquareToPos(partnerHighlightInfo.sq[i].y,
\r
3589 partnerHighlightInfo.sq[i].x, &x, &y);
\r
3590 clips[num_clips++] =
\r
3591 CreateRectRgn(x - lineGap, y - lineGap,
\r
3592 x + squareSize + lineGap, y + squareSize + lineGap);
\r
3594 if (oldPartnerHighlight.sq[i].x >= 0 &&
\r
3595 oldPartnerHighlight.sq[i].y >= 0) {
\r
3596 SquareToPos(oldPartnerHighlight.sq[i].y,
\r
3597 oldPartnerHighlight.sq[i].x, &x, &y);
\r
3598 clips[num_clips++] =
\r
3599 CreateRectRgn(x - lineGap, y - lineGap,
\r
3600 x + squareSize + lineGap, y + squareSize + lineGap);
\r
3605 fullrepaint = TRUE;
\r
3608 /* Create a buffer bitmap - this is the actual bitmap
\r
3609 * being written to. When all the work is done, we can
\r
3610 * copy it to the real DC (the screen). This avoids
\r
3611 * the problems with flickering.
\r
3613 GetClientRect(hwndMain, &Rect);
\r
3614 bufferBitmap = CreateCompatibleBitmap(hdc, Rect.right-Rect.left+1,
\r
3615 Rect.bottom-Rect.top+1);
\r
3616 oldBitmap = SelectObject(hdcmem, bufferBitmap);
\r
3617 if (!appData.monoMode) {
\r
3618 SelectPalette(hdcmem, hPal, FALSE);
\r
3621 /* Create clips for dragging */
\r
3622 if (!fullrepaint) {
\r
3623 if (dragInfo.from.x >= 0) {
\r
3624 SquareToPos(dragInfo.from.y, dragInfo.from.x, &x, &y);
\r
3625 clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);
\r
3627 if (dragInfo.start.x >= 0) {
\r
3628 SquareToPos(dragInfo.start.y, dragInfo.start.x, &x, &y);
\r
3629 clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);
\r
3631 if (dragInfo.pos.x >= 0) {
\r
3632 x = dragInfo.pos.x - squareSize / 2;
\r
3633 y = dragInfo.pos.y - squareSize / 2;
\r
3634 clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);
\r
3636 if (dragInfo.lastpos.x >= 0) {
\r
3637 x = dragInfo.lastpos.x - squareSize / 2;
\r
3638 y = dragInfo.lastpos.y - squareSize / 2;
\r
3639 clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);
\r
3643 /* Are we animating a move?
\r
3645 * - remove the piece from the board (temporarely)
\r
3646 * - calculate the clipping region
\r
3648 if (!fullrepaint) {
\r
3649 if (animInfo.piece != EmptySquare) {
\r
3650 board[animInfo.from.y][animInfo.from.x] = EmptySquare;
\r
3651 x = boardRect.left + animInfo.lastpos.x;
\r
3652 y = boardRect.top + animInfo.lastpos.y;
\r
3653 x2 = boardRect.left + animInfo.pos.x;
\r
3654 y2 = boardRect.top + animInfo.pos.y;
\r
3655 clips[num_clips++] = CreateRectRgn(MIN(x,x2), MIN(y,y2), MAX(x,x2)+squareSize, MAX(y,y2)+squareSize);
\r
3656 /* Slight kludge. The real problem is that after AnimateMove is
\r
3657 done, the position on the screen does not match lastDrawn.
\r
3658 This currently causes trouble only on e.p. captures in
\r
3659 atomic, where the piece moves to an empty square and then
\r
3660 explodes. The old and new positions both had an empty square
\r
3661 at the destination, but animation has drawn a piece there and
\r
3662 we have to remember to erase it. [HGM] moved until after setting lastDrawn */
\r
3663 lastDrawn[0][animInfo.to.y][animInfo.to.x] = animInfo.piece;
\r
3667 /* No clips? Make sure we have fullrepaint set to TRUE */
\r
3668 if (num_clips == 0)
\r
3669 fullrepaint = TRUE;
\r
3671 /* Set clipping on the memory DC */
\r
3672 if (!fullrepaint) {
\r
3673 SelectClipRgn(hdcmem, clips[0]);
\r
3674 for (x = 1; x < num_clips; x++) {
\r
3675 if (ExtSelectClipRgn(hdcmem, clips[x], RGN_OR) == ERROR)
\r
3676 abort(); // this should never ever happen!
\r
3680 /* Do all the drawing to the memory DC */
\r
3681 if(explodeInfo.radius) { // [HGM] atomic
\r
3683 int x, y, r=(explodeInfo.radius * squareSize)/100;
\r
3684 ChessSquare piece = board[explodeInfo.fromY][explodeInfo.fromX];
\r
3685 board[explodeInfo.fromY][explodeInfo.fromX] = EmptySquare; // suppress display of capturer
\r