2 * WinBoard.c -- Windows NT front end to XBoard
\r
4 * Copyright 1991 by Digital Equipment Corporation, Maynard,
\r
7 * Enhancements Copyright 1992-2001, 2002, 2003, 2004, 2005, 2006,
\r
8 * 2007, 2008, 2009, 2010 Free Software Foundation, Inc.
\r
10 * Enhancements Copyright 2005 Alessandro Scotti
\r
12 * XBoard borrows its colors and the bitmaps.xchess bitmap set from XChess,
\r
13 * which was written and is copyrighted by Wayne Christopher.
\r
15 * The following terms apply to Digital Equipment Corporation's copyright
\r
16 * interest in XBoard:
\r
17 * ------------------------------------------------------------------------
\r
18 * All Rights Reserved
\r
20 * Permission to use, copy, modify, and distribute this software and its
\r
21 * documentation for any purpose and without fee is hereby granted,
\r
22 * provided that the above copyright notice appear in all copies and that
\r
23 * both that copyright notice and this permission notice appear in
\r
24 * supporting documentation, and that the name of Digital not be
\r
25 * used in advertising or publicity pertaining to distribution of the
\r
26 * software without specific, written prior permission.
\r
28 * DIGITAL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
\r
29 * ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL
\r
30 * DIGITAL BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
\r
31 * ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
\r
32 * WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
\r
33 * ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
\r
35 * ------------------------------------------------------------------------
\r
37 * The following terms apply to the enhanced version of XBoard
\r
38 * distributed by the Free Software Foundation:
\r
39 * ------------------------------------------------------------------------
\r
41 * GNU XBoard is free software: you can redistribute it and/or modify
\r
42 * it under the terms of the GNU General Public License as published by
\r
43 * the Free Software Foundation, either version 3 of the License, or (at
\r
44 * your option) any later version.
\r
46 * GNU XBoard is distributed in the hope that it will be useful, but
\r
47 * WITHOUT ANY WARRANTY; without even the implied warranty of
\r
48 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
\r
49 * General Public License for more details.
\r
51 * You should have received a copy of the GNU General Public License
\r
52 * along with this program. If not, see http://www.gnu.org/licenses/. *
\r
54 *------------------------------------------------------------------------
\r
55 ** See the file ChangeLog for a revision history. */
\r
59 #include <windows.h>
\r
60 #include <winuser.h>
\r
61 #include <winsock.h>
\r
62 #include <commctrl.h>
\r
68 #include <sys/stat.h>
\r
71 #include <commdlg.h>
\r
73 #include <richedit.h>
\r
74 #include <mmsystem.h>
\r
83 #include "frontend.h"
\r
84 #include "backend.h"
\r
85 #include "winboard.h"
\r
87 #include "wclipbrd.h"
\r
88 #include "woptions.h"
\r
89 #include "wsockerr.h"
\r
90 #include "defaults.h"
\r
94 //void InitEngineUCI( const char * iniDir, ChessProgramState * cps );
\r
97 void mysrandom(unsigned int seed);
\r
99 extern int whiteFlag, blackFlag;
\r
100 Boolean flipClock = FALSE;
\r
101 extern HANDLE chatHandle[];
\r
102 extern int ics_type;
\r
104 void DisplayHoldingsCount(HDC hdc, int x, int y, int align, int copyNumber);
\r
105 VOID NewVariantPopup(HWND hwnd);
\r
106 int FinishMove P((ChessMove moveType, int fromX, int fromY, int toX, int toY,
\r
107 /*char*/int promoChar));
\r
108 void AnimateAtomicCapture(int fromX, int fromY, int toX, int toY, int nFrames);
\r
109 void DisplayMove P((int moveNumber));
\r
110 Boolean ParseFEN P((Board board, int *blackPlaysFirst, char *fen));
\r
111 void ChatPopUp P((char *s));
\r
113 ChessSquare piece;
\r
114 POINT pos; /* window coordinates of current pos */
\r
115 POINT lastpos; /* window coordinates of last pos - used for clipping */
\r
116 POINT from; /* board coordinates of the piece's orig pos */
\r
117 POINT to; /* board coordinates of the piece's new pos */
\r
120 static AnimInfo animInfo = { EmptySquare, {-1,-1}, {-1,-1}, {-1,-1} };
\r
123 POINT start; /* window coordinates of start pos */
\r
124 POINT pos; /* window coordinates of current pos */
\r
125 POINT lastpos; /* window coordinates of last pos - used for clipping */
\r
126 POINT from; /* board coordinates of the piece's orig pos */
\r
129 static DragInfo dragInfo = { {-1,-1}, {-1,-1}, {-1,-1}, {-1,-1} };
\r
132 POINT sq[2]; /* board coordinates of from, to squares */
\r
135 static HighlightInfo highlightInfo = { {{-1, -1}, {-1, -1}} };
\r
136 static HighlightInfo premoveHighlightInfo = { {{-1, -1}, {-1, -1}} };
\r
137 static HighlightInfo partnerHighlightInfo = { {{-1, -1}, {-1, -1}} };
\r
138 static HighlightInfo oldPartnerHighlight = { {{-1, -1}, {-1, -1}} };
\r
140 typedef struct { // [HGM] atomic
\r
141 int fromX, fromY, toX, toY, radius;
\r
144 static ExplodeInfo explodeInfo;
\r
146 /* Window class names */
\r
147 char szAppName[] = "WinBoard";
\r
148 char szConsoleName[] = "WBConsole";
\r
150 /* Title bar text */
\r
151 char szTitle[] = "WinBoard";
\r
152 char szConsoleTitle[] = "I C S Interaction";
\r
155 char *settingsFileName;
\r
156 Boolean saveSettingsOnExit;
\r
157 char installDir[MSG_SIZ];
\r
158 int errorExitStatus;
\r
160 BoardSize boardSize;
\r
161 Boolean chessProgram;
\r
162 //static int boardX, boardY;
\r
163 int minX, minY; // [HGM] placement: volatile limits on upper-left corner
\r
164 int squareSize, lineGap, minorSize;
\r
165 static int winW, winH;
\r
166 static RECT messageRect, whiteRect, blackRect, leftLogoRect, rightLogoRect; // [HGM] logo
\r
167 static int logoHeight = 0;
\r
168 static char messageText[MESSAGE_TEXT_MAX];
\r
169 static int clockTimerEvent = 0;
\r
170 static int loadGameTimerEvent = 0;
\r
171 static int analysisTimerEvent = 0;
\r
172 static DelayedEventCallback delayedTimerCallback;
\r
173 static int delayedTimerEvent = 0;
\r
174 static int buttonCount = 2;
\r
175 char *icsTextMenuString;
\r
177 char *firstChessProgramNames;
\r
178 char *secondChessProgramNames;
\r
180 #define PALETTESIZE 256
\r
182 HINSTANCE hInst; /* current instance */
\r
183 Boolean alwaysOnTop = FALSE;
\r
185 COLORREF lightSquareColor, darkSquareColor, whitePieceColor,
\r
186 blackPieceColor, highlightSquareColor, premoveHighlightColor;
\r
188 ColorClass currentColorClass;
\r
190 HWND hCommPort = NULL; /* currently open comm port */
\r
191 static HWND hwndPause; /* pause button */
\r
192 static HBITMAP pieceBitmap[3][(int) BlackPawn]; /* [HGM] nr of bitmaps referred to bP in stead of wK */
\r
193 static HBRUSH lightSquareBrush, darkSquareBrush,
\r
194 blackSquareBrush, /* [HGM] for band between board and holdings */
\r
195 explodeBrush, /* [HGM] atomic */
\r
196 markerBrush, /* [HGM] markers */
\r
197 whitePieceBrush, blackPieceBrush, iconBkgndBrush /*, outlineBrush*/;
\r
198 static POINT gridEndpoints[(BOARD_RANKS + BOARD_FILES + 2) * 2];
\r
199 static DWORD gridVertexCounts[BOARD_RANKS + BOARD_FILES + 2];
\r
200 static HPEN gridPen = NULL;
\r
201 static HPEN highlightPen = NULL;
\r
202 static HPEN premovePen = NULL;
\r
203 static NPLOGPALETTE pLogPal;
\r
204 static BOOL paletteChanged = FALSE;
\r
205 static HICON iconWhite, iconBlack, iconCurrent;
\r
206 static int doingSizing = FALSE;
\r
207 static int lastSizing = 0;
\r
208 static int prevStderrPort;
\r
209 static HBITMAP userLogo;
\r
211 static HBITMAP liteBackTexture = NULL;
\r
212 static HBITMAP darkBackTexture = NULL;
\r
213 static int liteBackTextureMode = BACK_TEXTURE_MODE_PLAIN;
\r
214 static int darkBackTextureMode = BACK_TEXTURE_MODE_PLAIN;
\r
215 static int backTextureSquareSize = 0;
\r
216 static struct { int x; int y; int mode; } backTextureSquareInfo[BOARD_RANKS][BOARD_FILES];
\r
218 #if __GNUC__ && !defined(_winmajor)
\r
219 #define oldDialog 0 /* cygwin doesn't define _winmajor; mingw does */
\r
221 #if defined(_winmajor)
\r
222 #define oldDialog (_winmajor < 4)
\r
224 #define oldDialog 0
\r
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, 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, OPT_AnimateMoving, OPT_PopupExitMessage, OPT_AutoFlag, OPT_PopupMoveErrors,
\r
281 OPT_AutoFlipView, OPT_ShowButtonBar, OPT_AutoRaiseBoard, OPT_ShowCoordinates,
\r
282 OPT_Blindfold, OPT_ShowThinking, OPT_HighlightDragging, OPT_TestLegality,
\r
283 OPT_SaveExtPGN, OPT_HideThinkFromHuman, OPT_ExtraInfoInMoveHistory,
\r
284 OPT_HighlightMoveArrow, OPT_AutoLogo },
\r
285 { DLG_IcsOptions, IDOK, IDCANCEL, OPT_AutoComment, OPT_AutoKibitz, OPT_AutoObserve,
\r
286 OPT_GetMoveList, OPT_LocalLineEditing, OPT_QuietPlay, OPT_SeekGraph, OPT_AutoRefresh,
\r
287 OPT_BgObserve, OPT_DualBoard, OPT_Premove, OPT_PremoveWhite, OPT_PremoveBlack,
\r
288 OPT_SmartMove, OPT_IcsAlarm, IDC_Sec, OPT_ChooseShoutColor, OPT_ChooseSShoutColor,
\r
289 OPT_ChooseChannel1Color, OPT_ChooseChannelColor, OPT_ChooseKibitzColor,
\r
290 OPT_ChooseTellColor, OPT_ChooseChallengeColor, OPT_ChooseRequestColor,
\r
291 OPT_ChooseSeekColor, OPT_ChooseNormalColor, OPT_ChooseBackgroundColor,
\r
292 OPT_DefaultColors, OPT_DontColorize, IDC_Boxes, GPB_Colors, GPB_Premove,
\r
293 GPB_General, GPB_Alarm },
\r
294 { DLG_BoardOptions, IDOK, IDCANCEL, OPT_SizeTiny, OPT_SizeTeeny, OPT_SizeDinky,
\r
295 OPT_SizePetite, OPT_SizeSlim, OPT_SizeSmall, OPT_SizeMediocre, OPT_SizeMiddling,
\r
296 OPT_SizeAverage, OPT_SizeModerate, OPT_SizeMedium, OPT_SizeBulky, OPT_SizeLarge,
\r
297 OPT_SizeBig, OPT_SizeHuge, OPT_SizeGiant, OPT_SizeColossal, OPT_SizeTitanic,
\r
298 OPT_ChooseLightSquareColor, OPT_ChooseDarkSquareColor, OPT_ChooseWhitePieceColor,
\r
299 OPT_ChooseBlackPieceColor, OPT_ChooseHighlightSquareColor, OPT_ChoosePremoveHighlightColor,
\r
300 OPT_Monochrome, OPT_AllWhite, OPT_UpsideDown, OPT_DefaultBoardColors, GPB_Colors,
\r
301 IDC_Light, IDC_Dark, IDC_White, IDC_Black, IDC_High, IDC_PreHigh, GPB_Size },
\r
302 { DLG_NewVariant, IDOK, IDCANCEL, OPT_VariantNormal, OPT_VariantFRC, OPT_VariantWildcastle,
\r
303 OPT_VariantNocastle, OPT_VariantLosers, OPT_VariantGiveaway, OPT_VariantSuicide,
\r
304 OPT_Variant3Check, OPT_VariantTwoKings, OPT_VariantAtomic, OPT_VariantCrazyhouse,
\r
305 OPT_VariantBughouse, OPT_VariantTwilight, OPT_VariantShogi, OPT_VariantSuper,
\r
306 OPT_VariantKnightmate, OPT_VariantBerolina, OPT_VariantCylinder, OPT_VariantFairy,
\r
307 OPT_VariantMakruk, OPT_VariantGothic, OPT_VariantCapablanca, OPT_VariantJanus,
\r
308 OPT_VariantCRC, OPT_VariantFalcon, OPT_VariantCourier, OPT_VariantGreat,
\r
309 OPT_VariantShatranj, OPT_VariantXiangqi, GPB_Variant, GPB_Board, IDC_Height,
\r
310 IDC_Width, IDC_Hand, IDC_Pieces, IDC_Def },
\r
311 { DLG_Fonts, IDOK, IDCANCEL, OPT_ChooseClockFont, OPT_ChooseMessageFont,
\r
312 OPT_ChooseCoordFont, OPT_ChooseTagFont, OPT_ChooseCommentsFont, OPT_ChooseConsoleFont, OPT_ChooseMoveHistoryFont, OPT_DefaultFonts,
\r
313 OPT_ClockFont, OPT_MessageFont, OPT_CoordFont, OPT_EditTagsFont,
\r
314 OPT_CommentsFont, OPT_MessageFont5, GPB_Current, GPB_All, OPT_MessageFont6 },
\r
315 { DLG_NewGameFRC, IDC_NFG_Label, IDC_NFG_Random, IDOK, IDCANCEL },
\r
316 { DLG_GameListOptions, IDC_GLT, IDC_GLT_Up, IDC_GLT_Down, IDC_GLT_Restore,
\r
317 IDC_GLT_Default, IDOK, IDCANCEL, IDC_GLT_RestoreTo },
\r
318 { DLG_MoveHistory },
\r
319 { DLG_EvalGraph },
\r
320 { DLG_EngineOutput, IDC_EngineLabel1, IDC_Engine1_NPS, IDC_EngineLabel2, IDC_Engine2_NPS },
\r
321 { DLG_Chat, IDC_Partner, IDC_Clear, IDC_Send, },
\r
322 { DLG_EnginePlayOptions, IDC_EpPonder, IDC_EpShowThinking, IDC_EpHideThinkingHuman,
\r
323 IDC_EpPeriodicUpdates, GPB_Adjudications, IDC_Draw, IDC_Moves, IDC_Threshold,
\r
324 IDC_Centi, IDC_TestClaims, IDC_DetectMates, IDC_MaterialDraws, IDC_TrivialDraws,
\r
325 GPB_Apply, IDC_Rule, IDC_Repeats, IDC_ScoreAbs1, IDC_ScoreAbs2, IDOK, IDCANCEL },
\r
326 { DLG_OptionsUCI, IDC_PolyDir, IDC_BrowseForPolyglotDir, IDC_Hash, IDC_Path,
\r
327 IDC_BrowseForEGTB, IDC_Cache, IDC_UseBook, IDC_BrowseForBook, IDC_CPU, IDC_OwnBook1,
\r
328 IDC_OwnBook2, IDC_Depth, IDC_Variation, IDC_DefGames, IDOK, IDCANCEL },
\r
332 char languageBuf[40000], *foreign[1000], *english[1000];
\r
335 LoadLanguageFile(char *name)
\r
336 { //load the file with translations, and make a list of the strings to be translated, and their translations
\r
338 int i=0, j=0, n=0, k;
\r
339 static char oldLanguage[MSG_SIZ];
\r
340 if(!strcmp(name, oldLanguage)) return;
\r
341 if(!name || name[0] == NULLCHAR) return;
\r
342 if((f = fopen(name, "r")) == NULL) return;
\r
343 while((k = fgetc(f)) != EOF) {
\r
344 if(i >= sizeof(languageBuf)) { DisplayError("Language file too big", 0); return; }
\r
345 languageBuf[i] = k;
\r
347 if(languageBuf[n] == '"' && languageBuf[i-1] == '"') {
\r
349 if(p = strstr(languageBuf + n + 1, "\" === \"")) {
\r
350 if(p > languageBuf+n+2 && p+8 < languageBuf+i) {
\r
351 if(j >= sizeof(english)) { DisplayError("Too many translated strings", 0); return; }
\r
352 english[j] = languageBuf + n + 1; *p = 0;
\r
353 foreign[j++] = p + 7; languageBuf[i-1] = 0;
\r
354 if(appData.debugMode) fprintf(debugFP, "translation: replace '%s' by '%s'\n", english[j-1], foreign[j-1]);
\r
359 } else if(i > 0 && languageBuf[i-1] == '\\') {
\r
361 case 'n': k = '\n'; break;
\r
362 case 'r': k = '\r'; break;
\r
363 case 't': k = '\t'; break;
\r
365 languageBuf[--i] = k;
\r
370 barbaric = (j != 0);
\r
371 if(barbaric) strcpy(oldLanguage, name); else oldLanguage[0] = NULLCHAR;
\r
376 { // return the translation of the given string
\r
377 // efficiency can be improved a lot...
\r
379 if(appData.debugMode) fprintf(debugFP, "T_(%s)\n", s);
\r
380 if(!barbaric) return s;
\r
381 if(!s) return ""; // sanity
\r
382 while(english[i]) {
\r
383 if(!strcmp(s, english[i])) return foreign[i];
\r
390 Translate(HANDLE hDlg, int dialogID)
\r
391 { // translate all text items in the given dialog
\r
393 char buf[MSG_SIZ], *s;
\r
394 //if(appData.debugMode) fprintf(debugFP, "Translate(%d)\n", dialogID);
\r
395 if(!barbaric) return;
\r
396 while(dialogItems[i][0] && dialogItems[i][0] != dialogID) i++; // find the dialog description
\r
397 if(dialogItems[i][0] != dialogID) return; // unknown dialog, should not happen
\r
398 GetWindowText( hDlg, buf, MSG_SIZ );
\r
400 if(appData.debugMode) fprintf(debugFP, "WindowText '%s' -> '%s'\n", buf, s);
\r
401 if(strcmp(buf, s)) SetWindowText(hDlg, s); // replace by translated string (if different)
\r
402 for(j=1; k=dialogItems[i][j]; j++) { // translate all listed dialog items
\r
403 GetDlgItemText(hDlg, k, buf, MSG_SIZ);
\r
404 if(strlen(buf) == 0) continue;
\r
406 if(strcmp(buf, s)) SetDlgItemText(hDlg, k, s); // replace by translated string (if different)
\r
415 HMENU mainMenu = GetMenu(hwndMain);
\r
416 for (i=GetMenuItemCount(mainMenu)-1; i>=0; i--) {
\r
417 HMENU subMenu = GetSubMenu(mainMenu, i);
\r
418 for(j=GetMenuItemCount(subMenu)-1; j>=0; j--){
\r
420 UINT k = GetMenuItemID(subMenu, j);
\r
421 GetMenuString(subMenu, j, buf, MSG_SIZ, MF_BYPOSITION);
\r
422 if(buf[0] == NULLCHAR) continue;
\r
423 //fprintf(debugFP, "menu(%d,%d) = %s (%08x, %08x) %d\n", i, j, buf, mainMenu, subMenu, k);
\r
424 ModifyMenu(subMenu, j, MF_STRING|MF_BYPOSITION,
\r
440 int cliWidth, cliHeight;
\r
443 SizeInfo sizeInfo[] =
\r
445 { "tiny", 21, 0, 1, 1, 0, 0 },
\r
446 { "teeny", 25, 1, 1, 1, 0, 0 },
\r
447 { "dinky", 29, 1, 1, 1, 0, 0 },
\r
448 { "petite", 33, 1, 1, 1, 0, 0 },
\r
449 { "slim", 37, 2, 1, 0, 0, 0 },
\r
450 { "small", 40, 2, 1, 0, 0, 0 },
\r
451 { "mediocre", 45, 2, 1, 0, 0, 0 },
\r
452 { "middling", 49, 2, 0, 0, 0, 0 },
\r
453 { "average", 54, 2, 0, 0, 0, 0 },
\r
454 { "moderate", 58, 3, 0, 0, 0, 0 },
\r
455 { "medium", 64, 3, 0, 0, 0, 0 },
\r
456 { "bulky", 72, 3, 0, 0, 0, 0 },
\r
457 { "large", 80, 3, 0, 0, 0, 0 },
\r
458 { "big", 87, 3, 0, 0, 0, 0 },
\r
459 { "huge", 95, 3, 0, 0, 0, 0 },
\r
460 { "giant", 108, 3, 0, 0, 0, 0 },
\r
461 { "colossal", 116, 4, 0, 0, 0, 0 },
\r
462 { "titanic", 129, 4, 0, 0, 0, 0 },
\r
463 { NULL, 0, 0, 0, 0, 0, 0 }
\r
466 #define MF(x) {x, {{0,}, 0. }, {0, }, 0}
\r
467 MyFont fontRec[NUM_SIZES][NUM_FONTS] =
\r
469 { 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
470 { 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
471 { 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
472 { 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
473 { 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
474 { 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
475 { 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
476 { 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
477 { 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
478 { 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
479 { 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
480 { 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
481 { 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
482 { 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
483 { 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
484 { 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
485 { 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
486 { 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
489 MyFont *font[NUM_SIZES][NUM_FONTS];
\r
498 #define BUTTON_WIDTH (tinyLayout ? 16 : 32)
\r
499 #define N_BUTTONS 5
\r
501 MyButtonDesc buttonDesc[N_BUTTONS] =
\r
503 {"<<", IDM_ToStart, NULL, NULL},
\r
504 {"<", IDM_Backward, NULL, NULL},
\r
505 {"P", IDM_Pause, NULL, NULL},
\r
506 {">", IDM_Forward, NULL, NULL},
\r
507 {">>", IDM_ToEnd, NULL, NULL},
\r
510 int tinyLayout = 0, smallLayout = 0;
\r
511 #define MENU_BAR_ITEMS 7
\r
512 char *menuBarText[2][MENU_BAR_ITEMS+1] = {
\r
513 { N_("&File"), N_("&Mode"), N_("&Action"), N_("&Step"), N_("&Options"), N_("&Help"), NULL },
\r
514 { N_("&F"), N_("&M"), N_("&A"), N_("&S"), N_("&O"), N_("&H"), NULL },
\r
518 MySound sounds[(int)NSoundClasses];
\r
519 MyTextAttribs textAttribs[(int)NColorClasses];
\r
521 MyColorizeAttribs colorizeAttribs[] = {
\r
522 { (COLORREF)0, 0, N_("Shout Text") },
\r
523 { (COLORREF)0, 0, N_("SShout/CShout") },
\r
524 { (COLORREF)0, 0, N_("Channel 1 Text") },
\r
525 { (COLORREF)0, 0, N_("Channel Text") },
\r
526 { (COLORREF)0, 0, N_("Kibitz Text") },
\r
527 { (COLORREF)0, 0, N_("Tell Text") },
\r
528 { (COLORREF)0, 0, N_("Challenge Text") },
\r
529 { (COLORREF)0, 0, N_("Request Text") },
\r
530 { (COLORREF)0, 0, N_("Seek Text") },
\r
531 { (COLORREF)0, 0, N_("Normal Text") },
\r
532 { (COLORREF)0, 0, N_("None") }
\r
537 static char *commentTitle;
\r
538 static char *commentText;
\r
539 static int commentIndex;
\r
540 static Boolean editComment = FALSE;
\r
543 char errorTitle[MSG_SIZ];
\r
544 char errorMessage[2*MSG_SIZ];
\r
545 HWND errorDialog = NULL;
\r
546 BOOLEAN moveErrorMessageUp = FALSE;
\r
547 BOOLEAN consoleEcho = TRUE;
\r
548 CHARFORMAT consoleCF;
\r
549 COLORREF consoleBackgroundColor;
\r
551 char *programVersion;
\r
557 typedef int CPKind;
\r
566 SOCKET sock2; /* stderr socket for OpenRcmd */
\r
569 #define INPUT_SOURCE_BUF_SIZE 4096
\r
571 typedef struct _InputSource {
\r
578 char buf[INPUT_SOURCE_BUF_SIZE];
\r
582 InputCallback func;
\r
583 struct _InputSource *second; /* for stderr thread on CPRcmd */
\r
587 InputSource *consoleInputSource;
\r
592 VOID ConsoleOutput(char* data, int length, int forceVisible);
\r
593 VOID ConsoleCreate();
\r
595 ConsoleWndProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam);
\r
596 VOID ColorizeTextPopup(HWND hwnd, ColorClass cc);
\r
597 VOID PrintCommSettings(FILE *f, char *name, DCB *dcb);
\r
598 VOID ParseCommSettings(char *arg, DCB *dcb);
\r
600 StartupDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam);
\r
601 VOID APIENTRY MenuPopup(HWND hwnd, POINT pt, HMENU hmenu, UINT def);
\r
602 void ParseIcsTextMenu(char *icsTextMenuString);
\r
603 VOID PopUpMoveDialog(char firstchar);
\r
604 VOID PopUpNameDialog(char firstchar);
\r
605 VOID UpdateSampleText(HWND hDlg, int id, MyColorizeAttribs *mca);
\r
609 int GameListOptions();
\r
611 int dummy; // [HGM] for obsolete args
\r
613 HWND hwndMain = NULL; /* root window*/
\r
614 HWND hwndConsole = NULL;
\r
615 HWND commentDialog = NULL;
\r
616 HWND moveHistoryDialog = NULL;
\r
617 HWND evalGraphDialog = NULL;
\r
618 HWND engineOutputDialog = NULL;
\r
619 HWND gameListDialog = NULL;
\r
620 HWND editTagsDialog = NULL;
\r
622 int commentUp = FALSE;
\r
624 WindowPlacement wpMain;
\r
625 WindowPlacement wpConsole;
\r
626 WindowPlacement wpComment;
\r
627 WindowPlacement wpMoveHistory;
\r
628 WindowPlacement wpEvalGraph;
\r
629 WindowPlacement wpEngineOutput;
\r
630 WindowPlacement wpGameList;
\r
631 WindowPlacement wpTags;
\r
633 VOID EngineOptionsPopup(); // [HGM] settings
\r
635 VOID GothicPopUp(char *title, VariantClass variant);
\r
637 * Setting "frozen" should disable all user input other than deleting
\r
638 * the window. We do this while engines are initializing themselves.
\r
640 static int frozen = 0;
\r
641 static int oldMenuItemState[MENU_BAR_ITEMS];
\r
647 if (frozen) return;
\r
649 hmenu = GetMenu(hwndMain);
\r
650 for (i=0; i<MENU_BAR_ITEMS; i++) {
\r
651 oldMenuItemState[i] = EnableMenuItem(hmenu, i, MF_BYPOSITION|MF_GRAYED);
\r
653 DrawMenuBar(hwndMain);
\r
656 /* Undo a FreezeUI */
\r
662 if (!frozen) return;
\r
664 hmenu = GetMenu(hwndMain);
\r
665 for (i=0; i<MENU_BAR_ITEMS; i++) {
\r
666 EnableMenuItem(hmenu, i, MF_BYPOSITION|oldMenuItemState[i]);
\r
668 DrawMenuBar(hwndMain);
\r
671 /*static*/ int fromX = -1, fromY = -1, toX, toY; // [HGM] moved upstream, so JAWS can use them
\r
673 /* JAWS preparation patch (WinBoard for the sight impaired). Define required insertions as empty */
\r
679 #define JAWS_ALT_INTERCEPT
\r
680 #define JAWS_KB_NAVIGATION
\r
681 #define JAWS_MENU_ITEMS
\r
682 #define JAWS_SILENCE
\r
683 #define JAWS_REPLAY
\r
685 #define JAWS_COPYRIGHT
\r
686 #define JAWS_DELETE(X) X
\r
687 #define SAYMACHINEMOVE()
\r
691 /*---------------------------------------------------------------------------*\
\r
695 \*---------------------------------------------------------------------------*/
\r
698 WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
\r
699 LPSTR lpCmdLine, int nCmdShow)
\r
702 HANDLE hAccelMain, hAccelNoAlt, hAccelNoICS;
\r
703 // INITCOMMONCONTROLSEX ex;
\r
707 LoadLibrary("RICHED32.DLL");
\r
708 consoleCF.cbSize = sizeof(CHARFORMAT);
\r
710 if (!InitApplication(hInstance)) {
\r
713 if (!InitInstance(hInstance, nCmdShow, lpCmdLine)) {
\r
719 // InitCommonControlsEx(&ex);
\r
720 InitCommonControls();
\r
722 hAccelMain = LoadAccelerators (hInstance, szAppName);
\r
723 hAccelNoAlt = LoadAccelerators (hInstance, "NO_ALT");
\r
724 hAccelNoICS = LoadAccelerators( hInstance, "NO_ICS"); /* [AS] No Ctrl-V on ICS!!! */
\r
726 /* Acquire and dispatch messages until a WM_QUIT message is received. */
\r
728 while (GetMessage(&msg, /* message structure */
\r
729 NULL, /* handle of window receiving the message */
\r
730 0, /* lowest message to examine */
\r
731 0)) /* highest message to examine */
\r
734 if(msg.message == WM_CHAR && msg.wParam == '\t') {
\r
735 // [HGM] navigate: switch between all windows with tab
\r
736 HWND e1 = NULL, e2 = NULL, mh = NULL, hInput = NULL, hText = NULL;
\r
737 int i, currentElement = 0;
\r
739 // first determine what element of the chain we come from (if any)
\r
740 if(appData.icsActive) {
\r
741 hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
742 hText = GetDlgItem(hwndConsole, OPT_ConsoleText);
\r
744 if(engineOutputDialog && EngineOutputIsUp()) {
\r
745 e1 = GetDlgItem(engineOutputDialog, IDC_EngineMemo1);
\r
746 e2 = GetDlgItem(engineOutputDialog, IDC_EngineMemo2);
\r
748 if(moveHistoryDialog && MoveHistoryIsUp()) {
\r
749 mh = GetDlgItem(moveHistoryDialog, IDC_MoveHistory);
\r
751 if(msg.hwnd == hwndMain) currentElement = 7 ; else
\r
752 if(msg.hwnd == engineOutputDialog) currentElement = 2; else
\r
753 if(msg.hwnd == e1) currentElement = 2; else
\r
754 if(msg.hwnd == e2) currentElement = 3; else
\r
755 if(msg.hwnd == moveHistoryDialog) currentElement = 4; else
\r
756 if(msg.hwnd == mh) currentElement = 4; else
\r
757 if(msg.hwnd == evalGraphDialog) currentElement = 6; else
\r
758 if(msg.hwnd == hText) currentElement = 5; else
\r
759 if(msg.hwnd == hInput) currentElement = 6; else
\r
760 for (i = 0; i < N_BUTTONS; i++) {
\r
761 if (buttonDesc[i].hwnd == msg.hwnd) { currentElement = 1; break; }
\r
764 // determine where to go to
\r
765 if(currentElement) { HWND h = NULL; int direction = GetKeyState(VK_SHIFT) < 0 ? -1 : 1;
\r
767 currentElement = (currentElement + direction) % 7;
\r
768 switch(currentElement) {
\r
770 h = hwndMain; break; // passing this case always makes the loop exit
\r
772 h = buttonDesc[0].hwnd; break; // could be NULL
\r
774 if(!EngineOutputIsUp()) continue; // skip closed auxiliary windows
\r
777 if(!EngineOutputIsUp()) continue;
\r
780 if(!MoveHistoryIsUp()) continue;
\r
782 // case 6: // input to eval graph does not seem to get here!
\r
783 // if(!EvalGraphIsUp()) continue;
\r
784 // h = evalGraphDialog; break;
\r
786 if(!appData.icsActive) continue;
\r
790 if(!appData.icsActive) continue;
\r
796 if(currentElement > 4 && IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);
\r
797 if(currentElement < 5 && IsIconic(hwndMain)) ShowWindow(hwndMain, SW_RESTORE); // all open together
\r
800 continue; // this message now has been processed
\r
804 if (!(commentDialog && IsDialogMessage(commentDialog, &msg)) &&
\r
805 !(moveHistoryDialog && IsDialogMessage(moveHistoryDialog, &msg)) &&
\r
806 !(evalGraphDialog && IsDialogMessage(evalGraphDialog, &msg)) &&
\r
807 !(engineOutputDialog && IsDialogMessage(engineOutputDialog, &msg)) &&
\r
808 !(editTagsDialog && IsDialogMessage(editTagsDialog, &msg)) &&
\r
809 !(gameListDialog && IsDialogMessage(gameListDialog, &msg)) &&
\r
810 !(errorDialog && IsDialogMessage(errorDialog, &msg)) &&
\r
811 !(!frozen && TranslateAccelerator(hwndMain, hAccelMain, &msg)) && JAWS_ACCEL
\r
812 !(!hwndConsole && TranslateAccelerator(hwndMain, hAccelNoICS, &msg)) &&
\r
813 !(!hwndConsole && TranslateAccelerator(hwndMain, hAccelNoAlt, &msg))) {
\r
814 int done = 0, i; // [HGM] chat: dispatch cat-box messages
\r
815 for(i=0; i<MAX_CHAT; i++)
\r
816 if(chatHandle[i] && IsDialogMessage(chatHandle[i], &msg)) {
\r
819 if(done) continue; // [HGM] chat: end patch
\r
820 TranslateMessage(&msg); /* Translates virtual key codes */
\r
821 DispatchMessage(&msg); /* Dispatches message to window */
\r
826 return (msg.wParam); /* Returns the value from PostQuitMessage */
\r
829 /*---------------------------------------------------------------------------*\
\r
831 * Initialization functions
\r
833 \*---------------------------------------------------------------------------*/
\r
837 { // update user logo if necessary
\r
838 static char oldUserName[MSG_SIZ], *curName;
\r
840 if(appData.autoLogo) {
\r
841 curName = UserName();
\r
842 if(strcmp(curName, oldUserName)) {
\r
843 sprintf(oldUserName, "logos\\%s.bmp", curName);
\r
844 userLogo = LoadImage( 0, oldUserName, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );
\r
845 strcpy(oldUserName, curName);
\r
851 InitApplication(HINSTANCE hInstance)
\r
855 /* Fill in window class structure with parameters that describe the */
\r
858 wc.style = CS_HREDRAW | CS_VREDRAW; /* Class style(s). */
\r
859 wc.lpfnWndProc = (WNDPROC)WndProc; /* Window Procedure */
\r
860 wc.cbClsExtra = 0; /* No per-class extra data. */
\r
861 wc.cbWndExtra = 0; /* No per-window extra data. */
\r
862 wc.hInstance = hInstance; /* Owner of this class */
\r
863 wc.hIcon = LoadIcon(hInstance, "icon_white");
\r
864 wc.hCursor = LoadCursor(NULL, IDC_ARROW); /* Cursor */
\r
865 wc.hbrBackground = (HBRUSH)(COLOR_WINDOW+1); /* Default color */
\r
866 wc.lpszMenuName = szAppName; /* Menu name from .RC */
\r
867 wc.lpszClassName = szAppName; /* Name to register as */
\r
869 /* Register the window class and return success/failure code. */
\r
870 if (!RegisterClass(&wc)) return FALSE;
\r
872 wc.style = CS_HREDRAW | CS_VREDRAW;
\r
873 wc.lpfnWndProc = (WNDPROC)ConsoleWndProc;
\r
875 wc.cbWndExtra = DLGWINDOWEXTRA;
\r
876 wc.hInstance = hInstance;
\r
877 wc.hIcon = LoadIcon(hInstance, "icon_white");
\r
878 wc.hCursor = LoadCursor(NULL, IDC_ARROW);
\r
879 wc.hbrBackground = (HBRUSH)(COLOR_MENU+1);
\r
880 wc.lpszMenuName = NULL;
\r
881 wc.lpszClassName = szConsoleName;
\r
883 if (!RegisterClass(&wc)) return FALSE;
\r
888 /* Set by InitInstance, used by EnsureOnScreen */
\r
889 int screenHeight, screenWidth;
\r
892 EnsureOnScreen(int *x, int *y, int minX, int minY)
\r
894 // int gap = GetSystemMetrics(SM_CYFRAME) + GetSystemMetrics(SM_CYCAPTION);
\r
895 /* Be sure window at (x,y) is not off screen (or even mostly off screen) */
\r
896 if (*x > screenWidth - 32) *x = 0;
\r
897 if (*y > screenHeight - 32) *y = 0;
\r
898 if (*x < minX) *x = minX;
\r
899 if (*y < minY) *y = minY;
\r
903 InitInstance(HINSTANCE hInstance, int nCmdShow, LPSTR lpCmdLine)
\r
905 HWND hwnd; /* Main window handle. */
\r
907 WINDOWPLACEMENT wp;
\r
910 hInst = hInstance; /* Store instance handle in our global variable */
\r
911 programName = szAppName;
\r
913 if (SearchPath(NULL, "WinBoard.exe", NULL, MSG_SIZ, installDir, &filepart)) {
\r
914 *filepart = NULLCHAR;
\r
916 GetCurrentDirectory(MSG_SIZ, installDir);
\r
918 gameInfo.boardWidth = gameInfo.boardHeight = 8; // [HGM] won't have open window otherwise
\r
919 screenWidth = screenHeight = 1000; // [HGM] placement: kludge to allow calling EnsureOnScreen from InitAppData
\r
920 InitAppData(lpCmdLine); /* Get run-time parameters */
\r
921 /* xboard, and older WinBoards, controlled the move sound with the
\r
922 appData.ringBellAfterMoves option. In the current WinBoard, we
\r
923 always turn the option on (so that the backend will call us),
\r
924 then let the user turn the sound off by setting it to silence if
\r
925 desired. To accommodate old winboard.ini files saved by old
\r
926 versions of WinBoard, we also turn off the sound if the option
\r
927 was initially set to false. [HGM] taken out of InitAppData */
\r
928 if (!appData.ringBellAfterMoves) {
\r
929 sounds[(int)SoundMove].name = strdup("");
\r
930 appData.ringBellAfterMoves = TRUE;
\r
932 if (appData.debugMode) {
\r
933 debugFP = fopen(appData.nameOfDebugFile, "w");
\r
934 setbuf(debugFP, NULL);
\r
937 LoadLanguageFile(appData.language);
\r
941 // InitEngineUCI( installDir, &first ); // [HGM] incorporated in InitBackEnd1()
\r
942 // InitEngineUCI( installDir, &second );
\r
944 /* Create a main window for this application instance. */
\r
945 hwnd = CreateWindow(szAppName, szTitle,
\r
946 (WS_OVERLAPPEDWINDOW & ~WS_MAXIMIZEBOX),
\r
947 CW_USEDEFAULT, 0, CW_USEDEFAULT, 0,
\r
948 NULL, NULL, hInstance, NULL);
\r
951 /* If window could not be created, return "failure" */
\r
956 /* [HGM] logo: Load logos if specified (must be done before InitDrawingSizes) */
\r
957 if( appData.firstLogo && appData.firstLogo[0] != NULLCHAR) {
\r
958 first.programLogo = LoadImage( 0, appData.firstLogo, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );
\r
960 if (first.programLogo == NULL && appData.debugMode) {
\r
961 fprintf( debugFP, "Unable to load logo bitmap '%s'\n", appData.firstLogo );
\r
963 } else if(appData.autoLogo) {
\r
964 if(appData.firstDirectory && appData.firstDirectory[0]) {
\r
966 sprintf(buf, "%s/logo.bmp", appData.firstDirectory);
\r
967 first.programLogo = LoadImage( 0, buf, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );
\r
971 if( appData.secondLogo && appData.secondLogo[0] != NULLCHAR) {
\r
972 second.programLogo = LoadImage( 0, appData.secondLogo, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );
\r
974 if (second.programLogo == NULL && appData.debugMode) {
\r
975 fprintf( debugFP, "Unable to load logo bitmap '%s'\n", appData.secondLogo );
\r
977 } else if(appData.autoLogo) {
\r
979 if(appData.icsActive) { // [HGM] logo: in ICS mode second can be used for ICS
\r
980 sprintf(buf, "logos\\%s.bmp", appData.icsHost);
\r
981 second.programLogo = LoadImage( 0, buf, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );
\r
983 if(appData.secondDirectory && appData.secondDirectory[0]) {
\r
984 sprintf(buf, "%s\\logo.bmp", appData.secondDirectory);
\r
985 second.programLogo = LoadImage( 0, buf, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );
\r
991 iconWhite = LoadIcon(hInstance, "icon_white");
\r
992 iconBlack = LoadIcon(hInstance, "icon_black");
\r
993 iconCurrent = iconWhite;
\r
994 InitDrawingColors();
\r
995 screenHeight = GetSystemMetrics(SM_CYSCREEN);
\r
996 screenWidth = GetSystemMetrics(SM_CXSCREEN);
\r
997 for (ibs = (int) NUM_SIZES - 1; ibs >= 0; ibs--) {
\r
998 /* Compute window size for each board size, and use the largest
\r
999 size that fits on this screen as the default. */
\r
1000 InitDrawingSizes((BoardSize)(ibs+1000), 0);
\r
1001 if (boardSize == (BoardSize)-1 &&
\r
1002 winH <= screenHeight
\r
1003 - GetSystemMetrics(SM_CYFRAME) - GetSystemMetrics(SM_CYCAPTION) - 10
\r
1004 && winW <= screenWidth) {
\r
1005 boardSize = (BoardSize)ibs;
\r
1009 InitDrawingSizes(boardSize, 0);
\r
1012 buttonCount = GetSystemMetrics(SM_CMOUSEBUTTONS);
\r
1014 /* [AS] Load textures if specified */
\r
1015 ZeroMemory( &backTextureSquareInfo, sizeof(backTextureSquareInfo) );
\r
1017 if( appData.liteBackTextureFile && appData.liteBackTextureFile[0] != NULLCHAR && appData.liteBackTextureFile[0] != '*' ) {
\r
1018 liteBackTexture = LoadImage( 0, appData.liteBackTextureFile, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );
\r
1019 liteBackTextureMode = appData.liteBackTextureMode;
\r
1021 if (liteBackTexture == NULL && appData.debugMode) {
\r
1022 fprintf( debugFP, "Unable to load lite texture bitmap '%s'\n", appData.liteBackTextureFile );
\r
1026 if( appData.darkBackTextureFile && appData.darkBackTextureFile[0] != NULLCHAR && appData.darkBackTextureFile[0] != '*' ) {
\r
1027 darkBackTexture = LoadImage( 0, appData.darkBackTextureFile, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );
\r
1028 darkBackTextureMode = appData.darkBackTextureMode;
\r
1030 if (darkBackTexture == NULL && appData.debugMode) {
\r
1031 fprintf( debugFP, "Unable to load dark texture bitmap '%s'\n", appData.darkBackTextureFile );
\r
1035 mysrandom( (unsigned) time(NULL) );
\r
1037 /* [AS] Restore layout */
\r
1038 if( wpMoveHistory.visible ) {
\r
1039 MoveHistoryPopUp();
\r
1042 if( wpEvalGraph.visible ) {
\r
1046 if( wpEngineOutput.visible ) {
\r
1047 EngineOutputPopUp();
\r
1050 /* Make the window visible; update its client area; and return "success" */
\r
1051 EnsureOnScreen(&wpMain.x, &wpMain.y, minX, minY);
\r
1052 wp.length = sizeof(WINDOWPLACEMENT);
\r
1054 wp.showCmd = nCmdShow;
\r
1055 wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;
\r
1056 wp.rcNormalPosition.left = wpMain.x;
\r
1057 wp.rcNormalPosition.right = wpMain.x + wpMain.width;
\r
1058 wp.rcNormalPosition.top = wpMain.y;
\r
1059 wp.rcNormalPosition.bottom = wpMain.y + wpMain.height;
\r
1060 SetWindowPlacement(hwndMain, &wp);
\r
1062 InitBackEnd2(); // [HGM] moved until after all windows placed, to save correct position if fatal error on engine start
\r
1064 if(!appData.noGUI) SetWindowPos(hwndMain, alwaysOnTop ? HWND_TOPMOST : HWND_NOTOPMOST,
\r
1065 0, 0, 0, 0, SWP_NOMOVE|SWP_NOSIZE);
\r
1067 if (hwndConsole) {
\r
1069 SetWindowPos(hwndConsole, alwaysOnTop ? HWND_TOPMOST : HWND_NOTOPMOST,
\r
1070 0, 0, 0, 0, SWP_NOMOVE|SWP_NOSIZE);
\r
1072 ShowWindow(hwndConsole, nCmdShow);
\r
1073 if(appData.chatBoxes) { // [HGM] chat: open chat boxes
\r
1074 char buf[MSG_SIZ], *p = buf, *q;
\r
1075 strcpy(buf, appData.chatBoxes);
\r
1077 q = strchr(p, ';');
\r
1079 if(*p) ChatPopUp(p);
\r
1082 SetActiveWindow(hwndConsole);
\r
1084 if(!appData.noGUI) UpdateWindow(hwnd); else ShowWindow(hwnd, SW_MINIMIZE);
\r
1085 if(gameListDialog) SetFocus(gameListDialog); // [HGM] jaws: for if we clicked multi-game game file
\r
1094 HMENU hmenu = GetMenu(hwndMain);
\r
1096 (void) EnableMenuItem(hmenu, IDM_CommPort,
\r
1097 MF_BYCOMMAND|((appData.icsActive &&
\r
1098 *appData.icsCommPort != NULLCHAR) ?
\r
1099 MF_ENABLED : MF_GRAYED));
\r
1100 (void) CheckMenuItem(hmenu, IDM_SaveSettingsOnExit,
\r
1101 MF_BYCOMMAND|(saveSettingsOnExit ?
\r
1102 MF_CHECKED : MF_UNCHECKED));
\r
1105 //---------------------------------------------------------------------------------------------------------
\r
1107 #define ICS_TEXT_MENU_SIZE (IDM_CommandXLast - IDM_CommandX + 1)
\r
1108 #define XBOARD FALSE
\r
1110 #define OPTCHAR "/"
\r
1111 #define SEPCHAR "="
\r
1115 // front-end part of option handling
\r
1118 LFfromMFP(LOGFONT* lf, MyFontParams *mfp)
\r
1120 HDC hdc = CreateDC("DISPLAY", NULL, NULL, NULL);
\r
1121 lf->lfHeight = -(int)(mfp->pointSize * GetDeviceCaps(hdc, LOGPIXELSY) / 72.0 + 0.5);
\r
1124 lf->lfEscapement = 0;
\r
1125 lf->lfOrientation = 0;
\r
1126 lf->lfWeight = mfp->bold ? FW_BOLD : FW_NORMAL;
\r
1127 lf->lfItalic = mfp->italic;
\r
1128 lf->lfUnderline = mfp->underline;
\r
1129 lf->lfStrikeOut = mfp->strikeout;
\r
1130 lf->lfCharSet = mfp->charset;
\r
1131 lf->lfOutPrecision = OUT_DEFAULT_PRECIS;
\r
1132 lf->lfClipPrecision = CLIP_DEFAULT_PRECIS;
\r
1133 lf->lfQuality = DEFAULT_QUALITY;
\r
1134 lf->lfPitchAndFamily = DEFAULT_PITCH|FF_DONTCARE;
\r
1135 strcpy(lf->lfFaceName, mfp->faceName);
\r
1139 CreateFontInMF(MyFont *mf)
\r
1141 LFfromMFP(&mf->lf, &mf->mfp);
\r
1142 if (mf->hf) DeleteObject(mf->hf);
\r
1143 mf->hf = CreateFontIndirect(&mf->lf);
\r
1146 // [HGM] This platform-dependent table provides the location for storing the color info
\r
1148 colorVariable[] = {
\r
1149 &whitePieceColor,
\r
1150 &blackPieceColor,
\r
1151 &lightSquareColor,
\r
1152 &darkSquareColor,
\r
1153 &highlightSquareColor,
\r
1154 &premoveHighlightColor,
\r
1156 &consoleBackgroundColor,
\r
1157 &appData.fontForeColorWhite,
\r
1158 &appData.fontBackColorWhite,
\r
1159 &appData.fontForeColorBlack,
\r
1160 &appData.fontBackColorBlack,
\r
1161 &appData.evalHistColorWhite,
\r
1162 &appData.evalHistColorBlack,
\r
1163 &appData.highlightArrowColor,
\r
1166 /* Command line font name parser. NULL name means do nothing.
\r
1167 Syntax like "Courier New:10.0 bi" or "Arial:10" or "Arial:10b"
\r
1168 For backward compatibility, syntax without the colon is also
\r
1169 accepted, but font names with digits in them won't work in that case.
\r
1172 ParseFontName(char *name, MyFontParams *mfp)
\r
1175 if (name == NULL) return;
\r
1177 q = strchr(p, ':');
\r
1179 if (q - p >= sizeof(mfp->faceName))
\r
1180 ExitArgError(_("Font name too long:"), name);
\r
1181 memcpy(mfp->faceName, p, q - p);
\r
1182 mfp->faceName[q - p] = NULLCHAR;
\r
1185 q = mfp->faceName;
\r
1186 while (*p && !isdigit(*p)) {
\r
1188 if (q - mfp->faceName >= sizeof(mfp->faceName))
\r
1189 ExitArgError(_("Font name too long:"), name);
\r
1191 while (q > mfp->faceName && q[-1] == ' ') q--;
\r
1194 if (!*p) ExitArgError(_("Font point size missing:"), name);
\r
1195 mfp->pointSize = (float) atof(p);
\r
1196 mfp->bold = (strchr(p, 'b') != NULL);
\r
1197 mfp->italic = (strchr(p, 'i') != NULL);
\r
1198 mfp->underline = (strchr(p, 'u') != NULL);
\r
1199 mfp->strikeout = (strchr(p, 's') != NULL);
\r
1200 mfp->charset = DEFAULT_CHARSET;
\r
1201 q = strchr(p, 'c');
\r
1203 mfp->charset = (BYTE) atoi(q+1);
\r
1207 ParseFont(char *name, int number)
\r
1208 { // wrapper to shield back-end from 'font'
\r
1209 ParseFontName(name, &font[boardSize][number]->mfp);
\r
1214 { // in WB we have a 2D array of fonts; this initializes their description
\r
1216 /* Point font array elements to structures and
\r
1217 parse default font names */
\r
1218 for (i=0; i<NUM_FONTS; i++) {
\r
1219 for (j=0; j<NUM_SIZES; j++) {
\r
1220 font[j][i] = &fontRec[j][i];
\r
1221 ParseFontName(font[j][i]->def, &font[j][i]->mfp);
\r
1228 { // here we create the actual fonts from the selected descriptions
\r
1230 for (i=0; i<NUM_FONTS; i++) {
\r
1231 for (j=0; j<NUM_SIZES; j++) {
\r
1232 CreateFontInMF(font[j][i]);
\r
1236 /* Color name parser.
\r
1237 X version accepts X color names, but this one
\r
1238 handles only the #rrggbb form (hex) or rrr,ggg,bbb (decimal) */
\r
1240 ParseColorName(char *name)
\r
1242 int red, green, blue, count;
\r
1243 char buf[MSG_SIZ];
\r
1245 count = sscanf(name, "#%2x%2x%2x", &red, &green, &blue);
\r
1247 count = sscanf(name, "%3d%*[^0-9]%3d%*[^0-9]%3d",
\r
1248 &red, &green, &blue);
\r
1251 sprintf(buf, _("Can't parse color name %s"), name);
\r
1252 DisplayError(buf, 0);
\r
1253 return RGB(0, 0, 0);
\r
1255 return PALETTERGB(red, green, blue);
\r
1259 ParseColor(int n, char *name)
\r
1260 { // for WinBoard the color is an int, which needs to be derived from the string
\r
1261 if(colorVariable[n]) *(int*)colorVariable[n] = ParseColorName(name);
\r
1265 ParseAttribs(COLORREF *color, int *effects, char* argValue)
\r
1267 char *e = argValue;
\r
1271 if (*e == 'b') eff |= CFE_BOLD;
\r
1272 else if (*e == 'i') eff |= CFE_ITALIC;
\r
1273 else if (*e == 'u') eff |= CFE_UNDERLINE;
\r
1274 else if (*e == 's') eff |= CFE_STRIKEOUT;
\r
1275 else if (*e == '#' || isdigit(*e)) break;
\r
1279 *color = ParseColorName(e);
\r
1283 ParseTextAttribs(ColorClass cc, char *s)
\r
1284 { // [HGM] front-end wrapper that does the platform-dependent call
\r
1285 // for XBoard we would set (&appData.colorShout)[cc] = strdup(s);
\r
1286 ParseAttribs(&textAttribs[cc].color, &textAttribs[cc].effects, s);
\r
1290 ParseBoardSize(void *addr, char *name)
\r
1291 { // [HGM] rewritten with return-value ptr to shield back-end from BoardSize
\r
1292 BoardSize bs = SizeTiny;
\r
1293 while (sizeInfo[bs].name != NULL) {
\r
1294 if (StrCaseCmp(name, sizeInfo[bs].name) == 0) {
\r
1295 *(BoardSize *)addr = bs;
\r
1300 ExitArgError(_("Unrecognized board size value"), name);
\r
1305 { // [HGM] import name from appData first
\r
1308 for (cc = (ColorClass)0; cc < ColorNormal; cc++) {
\r
1309 textAttribs[cc].sound.name = strdup((&appData.soundShout)[cc]);
\r
1310 textAttribs[cc].sound.data = NULL;
\r
1311 MyLoadSound(&textAttribs[cc].sound);
\r
1313 for (cc = ColorNormal; cc < NColorClasses; cc++) {
\r
1314 textAttribs[cc].sound.name = strdup("");
\r
1315 textAttribs[cc].sound.data = NULL;
\r
1317 for (sc = (SoundClass)0; sc < NSoundClasses; sc++) {
\r
1318 sounds[sc].name = strdup((&appData.soundMove)[sc]);
\r
1319 sounds[sc].data = NULL;
\r
1320 MyLoadSound(&sounds[sc]);
\r
1325 SetCommPortDefaults()
\r
1327 memset(&dcb, 0, sizeof(DCB)); // required by VS 2002 +
\r
1328 dcb.DCBlength = sizeof(DCB);
\r
1329 dcb.BaudRate = 9600;
\r
1330 dcb.fBinary = TRUE;
\r
1331 dcb.fParity = FALSE;
\r
1332 dcb.fOutxCtsFlow = FALSE;
\r
1333 dcb.fOutxDsrFlow = FALSE;
\r
1334 dcb.fDtrControl = DTR_CONTROL_ENABLE;
\r
1335 dcb.fDsrSensitivity = FALSE;
\r
1336 dcb.fTXContinueOnXoff = TRUE;
\r
1337 dcb.fOutX = FALSE;
\r
1339 dcb.fNull = FALSE;
\r
1340 dcb.fRtsControl = RTS_CONTROL_ENABLE;
\r
1341 dcb.fAbortOnError = FALSE;
\r
1343 dcb.Parity = SPACEPARITY;
\r
1344 dcb.StopBits = ONESTOPBIT;
\r
1347 // [HGM] args: these three cases taken out to stay in front-end
\r
1349 SaveFontArg(FILE *f, ArgDescriptor *ad)
\r
1350 { // in WinBoard every board size has its own font, and the "argLoc" identifies the table,
\r
1351 // while the curent board size determines the element. This system should be ported to XBoard.
\r
1352 // What the table contains pointers to, and how to print the font description, remains platform-dependent
\r
1354 for (bs=0; bs<NUM_SIZES; bs++) {
\r
1355 MyFontParams *mfp = &font[bs][(int) ad->argLoc]->mfp;
\r
1356 fprintf(f, "/size=%s ", sizeInfo[bs].name);
\r
1357 fprintf(f, "/%s=\"%s:%g%s%s%s%s%sc%d\"\n",
\r
1358 ad->argName, mfp->faceName, mfp->pointSize,
\r
1359 mfp->bold || mfp->italic || mfp->underline || mfp->strikeout ? " " : "",
\r
1360 mfp->bold ? "b" : "",
\r
1361 mfp->italic ? "i" : "",
\r
1362 mfp->underline ? "u" : "",
\r
1363 mfp->strikeout ? "s" : "",
\r
1364 (int)mfp->charset);
\r
1370 { // [HGM] copy the names from the internal WB variables to appData
\r
1373 for (cc = (ColorClass)0; cc < ColorNormal; cc++)
\r
1374 (&appData.soundShout)[cc] = textAttribs[cc].sound.name;
\r
1375 for (sc = (SoundClass)0; sc < NSoundClasses; sc++)
\r
1376 (&appData.soundMove)[sc] = sounds[sc].name;
\r
1380 SaveAttribsArg(FILE *f, ArgDescriptor *ad)
\r
1381 { // here the "argLoc" defines a table index. It could have contained the 'ta' pointer itself, though
\r
1382 MyTextAttribs* ta = &textAttribs[(ColorClass)ad->argLoc];
\r
1383 fprintf(f, "/%s=\"%s%s%s%s%s#%02lx%02lx%02lx\"\n", ad->argName,
\r
1384 (ta->effects & CFE_BOLD) ? "b" : "",
\r
1385 (ta->effects & CFE_ITALIC) ? "i" : "",
\r
1386 (ta->effects & CFE_UNDERLINE) ? "u" : "",
\r
1387 (ta->effects & CFE_STRIKEOUT) ? "s" : "",
\r
1388 (ta->effects) ? " " : "",
\r
1389 ta->color&0xff, (ta->color >> 8)&0xff, (ta->color >> 16)&0xff);
\r
1393 SaveColor(FILE *f, ArgDescriptor *ad)
\r
1394 { // in WinBoard the color is an int and has to be converted to text. In X it would be a string already?
\r
1395 COLORREF color = *(COLORREF *)colorVariable[(int)ad->argLoc];
\r
1396 fprintf(f, "/%s=#%02lx%02lx%02lx\n", ad->argName,
\r
1397 color&0xff, (color>>8)&0xff, (color>>16)&0xff);
\r
1401 SaveBoardSize(FILE *f, char *name, void *addr)
\r
1402 { // wrapper to shield back-end from BoardSize & sizeInfo
\r
1403 fprintf(f, "/%s=%s\n", name, sizeInfo[*(BoardSize *)addr].name);
\r
1407 ParseCommPortSettings(char *s)
\r
1408 { // wrapper to keep dcb from back-end
\r
1409 ParseCommSettings(s, &dcb);
\r
1414 { // wrapper to shield use of window handles from back-end (make addressible by number?)
\r
1415 GetActualPlacement(hwndMain, &wpMain);
\r
1416 GetActualPlacement(hwndConsole, &wpConsole);
\r
1417 GetActualPlacement(commentDialog, &wpComment);
\r
1418 GetActualPlacement(editTagsDialog, &wpTags);
\r
1419 GetActualPlacement(gameListDialog, &wpGameList);
\r
1420 GetActualPlacement(moveHistoryDialog, &wpMoveHistory);
\r
1421 GetActualPlacement(evalGraphDialog, &wpEvalGraph);
\r
1422 GetActualPlacement(engineOutputDialog, &wpEngineOutput);
\r
1426 PrintCommPortSettings(FILE *f, char *name)
\r
1427 { // wrapper to shield back-end from DCB
\r
1428 PrintCommSettings(f, name, &dcb);
\r
1432 MySearchPath(char *installDir, char *name, char *fullname)
\r
1434 char *dummy, buf[MSG_SIZ], *p = name, *q;
\r
1435 if(name[0]== '%') {
\r
1436 fullname[0] = 0; // [HGM] first expand any environment variables in the given name
\r
1437 while(*p == '%' && (q = strchr(p+1, '%'))) { // [HGM] recognize %*% as environment variable
\r
1439 *strchr(buf, '%') = 0;
\r
1440 strcat(fullname, getenv(buf));
\r
1441 p = q+1; while(*p == '\\') { strcat(fullname, "\\"); p++; }
\r
1443 strcat(fullname, p); // after environment variables (if any), take the remainder of the given name
\r
1444 if(appData.debugMode) fprintf(debugFP, "name = '%s', expanded name = '%s'\n", name, fullname);
\r
1445 return (int) strlen(fullname);
\r
1447 return (int) SearchPath(installDir, name, NULL, MSG_SIZ, fullname, &dummy);
\r
1451 MyGetFullPathName(char *name, char *fullname)
\r
1454 return (int) GetFullPathName(name, MSG_SIZ, fullname, &dummy);
\r
1459 { // [HGM] args: allows testing if main window is realized from back-end
\r
1460 return hwndMain != NULL;
\r
1464 PopUpStartupDialog()
\r
1468 LoadLanguageFile(appData.language);
\r
1469 lpProc = MakeProcInstance((FARPROC)StartupDialog, hInst);
\r
1470 DialogBox(hInst, MAKEINTRESOURCE(DLG_Startup), NULL, (DLGPROC)lpProc);
\r
1471 FreeProcInstance(lpProc);
\r
1474 /*---------------------------------------------------------------------------*\
\r
1476 * GDI board drawing routines
\r
1478 \*---------------------------------------------------------------------------*/
\r
1480 /* [AS] Draw square using background texture */
\r
1481 static void DrawTile( int dx, int dy, int dw, int dh, HDC dst, HDC src, int mode, int sx, int sy )
\r
1486 return; /* Should never happen! */
\r
1489 SetGraphicsMode( dst, GM_ADVANCED );
\r
1496 /* X reflection */
\r
1501 x.eDx = (FLOAT) dw + dx - 1;
\r
1504 SetWorldTransform( dst, &x );
\r
1507 /* Y reflection */
\r
1513 x.eDy = (FLOAT) dh + dy - 1;
\r
1515 SetWorldTransform( dst, &x );
\r
1523 x.eDx = (FLOAT) dx;
\r
1524 x.eDy = (FLOAT) dy;
\r
1527 SetWorldTransform( dst, &x );
\r
1531 BitBlt( dst, dx, dy, dw, dh, src, sx, sy, SRCCOPY );
\r
1539 SetWorldTransform( dst, &x );
\r
1541 ModifyWorldTransform( dst, 0, MWT_IDENTITY );
\r
1544 /* [AS] [HGM] Make room for more piece types, so all pieces can be different */
\r
1546 PM_WP = (int) WhitePawn,
\r
1547 PM_WN = (int) WhiteKnight,
\r
1548 PM_WB = (int) WhiteBishop,
\r
1549 PM_WR = (int) WhiteRook,
\r
1550 PM_WQ = (int) WhiteQueen,
\r
1551 PM_WF = (int) WhiteFerz,
\r
1552 PM_WW = (int) WhiteWazir,
\r
1553 PM_WE = (int) WhiteAlfil,
\r
1554 PM_WM = (int) WhiteMan,
\r
1555 PM_WO = (int) WhiteCannon,
\r
1556 PM_WU = (int) WhiteUnicorn,
\r
1557 PM_WH = (int) WhiteNightrider,
\r
1558 PM_WA = (int) WhiteAngel,
\r
1559 PM_WC = (int) WhiteMarshall,
\r
1560 PM_WAB = (int) WhiteCardinal,
\r
1561 PM_WD = (int) WhiteDragon,
\r
1562 PM_WL = (int) WhiteLance,
\r
1563 PM_WS = (int) WhiteCobra,
\r
1564 PM_WV = (int) WhiteFalcon,
\r
1565 PM_WSG = (int) WhiteSilver,
\r
1566 PM_WG = (int) WhiteGrasshopper,
\r
1567 PM_WK = (int) WhiteKing,
\r
1568 PM_BP = (int) BlackPawn,
\r
1569 PM_BN = (int) BlackKnight,
\r
1570 PM_BB = (int) BlackBishop,
\r
1571 PM_BR = (int) BlackRook,
\r
1572 PM_BQ = (int) BlackQueen,
\r
1573 PM_BF = (int) BlackFerz,
\r
1574 PM_BW = (int) BlackWazir,
\r
1575 PM_BE = (int) BlackAlfil,
\r
1576 PM_BM = (int) BlackMan,
\r
1577 PM_BO = (int) BlackCannon,
\r
1578 PM_BU = (int) BlackUnicorn,
\r
1579 PM_BH = (int) BlackNightrider,
\r
1580 PM_BA = (int) BlackAngel,
\r
1581 PM_BC = (int) BlackMarshall,
\r
1582 PM_BG = (int) BlackGrasshopper,
\r
1583 PM_BAB = (int) BlackCardinal,
\r
1584 PM_BD = (int) BlackDragon,
\r
1585 PM_BL = (int) BlackLance,
\r
1586 PM_BS = (int) BlackCobra,
\r
1587 PM_BV = (int) BlackFalcon,
\r
1588 PM_BSG = (int) BlackSilver,
\r
1589 PM_BK = (int) BlackKing
\r
1592 static HFONT hPieceFont = NULL;
\r
1593 static HBITMAP hPieceMask[(int) EmptySquare];
\r
1594 static HBITMAP hPieceFace[(int) EmptySquare];
\r
1595 static int fontBitmapSquareSize = 0;
\r
1596 static char pieceToFontChar[(int) EmptySquare] =
\r
1597 { 'p', 'n', 'b', 'r', 'q',
\r
1598 'n', 'b', 'p', 'n', 'b', 'r', 'b', 'r', 'q', 'k',
\r
1599 'k', 'o', 'm', 'v', 't', 'w',
\r
1600 'v', 't', 'o', 'm', 'v', 't', 'v', 't', 'w', 'l',
\r
1603 extern BOOL SetCharTable( char *table, const char * map );
\r
1604 /* [HGM] moved to backend.c */
\r
1606 static void SetPieceBackground( HDC hdc, COLORREF color, int mode )
\r
1609 BYTE r1 = GetRValue( color );
\r
1610 BYTE g1 = GetGValue( color );
\r
1611 BYTE b1 = GetBValue( color );
\r
1617 /* Create a uniform background first */
\r
1618 hbrush = CreateSolidBrush( color );
\r
1619 SetRect( &rc, 0, 0, squareSize, squareSize );
\r
1620 FillRect( hdc, &rc, hbrush );
\r
1621 DeleteObject( hbrush );
\r
1624 /* Vertical gradient, good for pawn, knight and rook, less for queen and king */
\r
1625 int steps = squareSize / 2;
\r
1628 for( i=0; i<steps; i++ ) {
\r
1629 BYTE r = r1 - (r1-r2) * i / steps;
\r
1630 BYTE g = g1 - (g1-g2) * i / steps;
\r
1631 BYTE b = b1 - (b1-b2) * i / steps;
\r
1633 hbrush = CreateSolidBrush( RGB(r,g,b) );
\r
1634 SetRect( &rc, i + squareSize - steps, 0, i + squareSize - steps + 1, squareSize );
\r
1635 FillRect( hdc, &rc, hbrush );
\r
1636 DeleteObject(hbrush);
\r
1639 else if( mode == 2 ) {
\r
1640 /* Diagonal gradient, good more or less for every piece */
\r
1641 POINT triangle[3];
\r
1642 HPEN hpen = SelectObject( hdc, GetStockObject(NULL_PEN) );
\r
1643 HBRUSH hbrush_old;
\r
1644 int steps = squareSize;
\r
1647 triangle[0].x = squareSize - steps;
\r
1648 triangle[0].y = squareSize;
\r
1649 triangle[1].x = squareSize;
\r
1650 triangle[1].y = squareSize;
\r
1651 triangle[2].x = squareSize;
\r
1652 triangle[2].y = squareSize - steps;
\r
1654 for( i=0; i<steps; i++ ) {
\r
1655 BYTE r = r1 - (r1-r2) * i / steps;
\r
1656 BYTE g = g1 - (g1-g2) * i / steps;
\r
1657 BYTE b = b1 - (b1-b2) * i / steps;
\r
1659 hbrush = CreateSolidBrush( RGB(r,g,b) );
\r
1660 hbrush_old = SelectObject( hdc, hbrush );
\r
1661 Polygon( hdc, triangle, 3 );
\r
1662 SelectObject( hdc, hbrush_old );
\r
1663 DeleteObject(hbrush);
\r
1668 SelectObject( hdc, hpen );
\r
1673 [AS] The method I use to create the bitmaps it a bit tricky, but it
\r
1674 seems to work ok. The main problem here is to find the "inside" of a chess
\r
1675 piece: follow the steps as explained below.
\r
1677 static void CreatePieceMaskFromFont( HDC hdc_window, HDC hdc, int index )
\r
1681 COLORREF chroma = RGB(0xFF,0x00,0xFF);
\r
1685 int backColor = whitePieceColor;
\r
1686 int foreColor = blackPieceColor;
\r
1688 if( index < (int)BlackPawn && appData.fontBackColorWhite != appData.fontForeColorWhite ) {
\r
1689 backColor = appData.fontBackColorWhite;
\r
1690 foreColor = appData.fontForeColorWhite;
\r
1692 else if( index >= (int)BlackPawn && appData.fontBackColorBlack != appData.fontForeColorBlack ) {
\r
1693 backColor = appData.fontBackColorBlack;
\r
1694 foreColor = appData.fontForeColorBlack;
\r
1698 hbm = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );
\r
1700 hbm_old = SelectObject( hdc, hbm );
\r
1704 rc.right = squareSize;
\r
1705 rc.bottom = squareSize;
\r
1707 /* Step 1: background is now black */
\r
1708 FillRect( hdc, &rc, GetStockObject(BLACK_BRUSH) );
\r
1710 GetTextExtentPoint32( hdc, &pieceToFontChar[index], 1, &sz );
\r
1712 pt.x = (squareSize - sz.cx) / 2;
\r
1713 pt.y = (squareSize - sz.cy) / 2;
\r
1715 SetBkMode( hdc, TRANSPARENT );
\r
1716 SetTextColor( hdc, chroma );
\r
1717 /* Step 2: the piece has been drawn in purple, there are now black and purple in this bitmap */
\r
1718 TextOut( hdc, pt.x, pt.y, &pieceToFontChar[appData.allWhite && index >= (int)BlackPawn ? index - (int)BlackPawn : index], 1 );
\r
1720 SelectObject( hdc, GetStockObject(WHITE_BRUSH) );
\r
1721 /* Step 3: the area outside the piece is filled with white */
\r
1722 // FloodFill( hdc, 0, 0, chroma );
\r
1723 ExtFloodFill( hdc, 0, 0, 0, FLOODFILLSURFACE );
\r
1724 ExtFloodFill( hdc, 0, squareSize-1, 0, FLOODFILLSURFACE ); // [HGM] fill from all 4 corners, for if piece too big
\r
1725 ExtFloodFill( hdc, squareSize-1, 0, 0, FLOODFILLSURFACE );
\r
1726 ExtFloodFill( hdc, squareSize-1, squareSize-1, 0, FLOODFILLSURFACE );
\r
1727 SelectObject( hdc, GetStockObject(BLACK_BRUSH) );
\r
1729 Step 4: this is the tricky part, the area inside the piece is filled with black,
\r
1730 but if the start point is not inside the piece we're lost!
\r
1731 There should be a better way to do this... if we could create a region or path
\r
1732 from the fill operation we would be fine for example.
\r
1734 // FloodFill( hdc, squareSize / 2, squareSize / 2, RGB(0xFF,0xFF,0xFF) );
\r
1735 ExtFloodFill( hdc, squareSize / 2, squareSize / 2, RGB(0xFF,0xFF,0xFF), FLOODFILLBORDER );
\r
1737 { /* [HGM] shave off edges of mask, in an attempt to correct for the fact that FloodFill does not work correctly under Win XP */
\r
1738 HDC dc2 = CreateCompatibleDC( hdc_window );
\r
1739 HBITMAP bm2 = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );
\r
1741 SelectObject( dc2, bm2 );
\r
1742 BitBlt( dc2, 0, 0, squareSize, squareSize, hdc, 0, 0, SRCCOPY ); // make copy
\r
1743 BitBlt( hdc, 0, 1, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );
\r
1744 BitBlt( hdc, 2, 1, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );
\r
1745 BitBlt( hdc, 1, 0, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );
\r
1746 BitBlt( hdc, 1, 2, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );
\r
1749 DeleteObject( bm2 );
\r
1752 SetTextColor( hdc, 0 );
\r
1754 Step 5: some fonts have "disconnected" areas that are skipped by the fill:
\r
1755 draw the piece again in black for safety.
\r
1757 TextOut( hdc, pt.x, pt.y, &pieceToFontChar[appData.allWhite && index >= (int)BlackPawn ? index - (int)BlackPawn : index], 1 );
\r
1759 SelectObject( hdc, hbm_old );
\r
1761 if( hPieceMask[index] != NULL ) {
\r
1762 DeleteObject( hPieceMask[index] );
\r
1765 hPieceMask[index] = hbm;
\r
1768 hbm = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );
\r
1770 SelectObject( hdc, hbm );
\r
1773 HDC dc1 = CreateCompatibleDC( hdc_window );
\r
1774 HDC dc2 = CreateCompatibleDC( hdc_window );
\r
1775 HBITMAP bm2 = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );
\r
1777 SelectObject( dc1, hPieceMask[index] );
\r
1778 SelectObject( dc2, bm2 );
\r
1779 FillRect( dc2, &rc, GetStockObject(WHITE_BRUSH) );
\r
1780 BitBlt( dc2, 0, 0, squareSize, squareSize, dc1, 0, 0, SRCINVERT );
\r
1783 Now dc2 contains the inverse of the piece mask, i.e. a mask that preserves
\r
1784 the piece background and deletes (makes transparent) the rest.
\r
1785 Thanks to that mask, we are free to paint the background with the greates
\r
1786 freedom, as we'll be able to mask off the unwanted parts when finished.
\r
1787 We use this, to make gradients and give the pieces a "roundish" look.
\r
1789 SetPieceBackground( hdc, backColor, 2 );
\r
1790 BitBlt( hdc, 0, 0, squareSize, squareSize, dc2, 0, 0, SRCAND );
\r
1794 DeleteObject( bm2 );
\r
1797 SetTextColor( hdc, foreColor );
\r
1798 TextOut( hdc, pt.x, pt.y, &pieceToFontChar[appData.allWhite && index >= (int)BlackPawn ? index - (int)BlackPawn : index], 1 );
\r
1800 SelectObject( hdc, hbm_old );
\r
1802 if( hPieceFace[index] != NULL ) {
\r
1803 DeleteObject( hPieceFace[index] );
\r
1806 hPieceFace[index] = hbm;
\r
1809 static int TranslatePieceToFontPiece( int piece )
\r
1839 case BlackMarshall:
\r
1843 case BlackNightrider:
\r
1849 case BlackUnicorn:
\r
1853 case BlackGrasshopper:
\r
1865 case BlackCardinal:
\r
1872 case WhiteMarshall:
\r
1876 case WhiteNightrider:
\r
1882 case WhiteUnicorn:
\r
1886 case WhiteGrasshopper:
\r
1898 case WhiteCardinal:
\r
1907 void CreatePiecesFromFont()
\r
1910 HDC hdc_window = NULL;
\r
1916 if( fontBitmapSquareSize < 0 ) {
\r
1917 /* Something went seriously wrong in the past: do not try to recreate fonts! */
\r
1921 if( appData.renderPiecesWithFont == NULL || appData.renderPiecesWithFont[0] == NULLCHAR || appData.renderPiecesWithFont[0] == '*' ) {
\r
1922 fontBitmapSquareSize = -1;
\r
1926 if( fontBitmapSquareSize != squareSize ) {
\r
1927 hdc_window = GetDC( hwndMain );
\r
1928 hdc = CreateCompatibleDC( hdc_window );
\r
1930 if( hPieceFont != NULL ) {
\r
1931 DeleteObject( hPieceFont );
\r
1934 for( i=0; i<=(int)BlackKing; i++ ) {
\r
1935 hPieceMask[i] = NULL;
\r
1936 hPieceFace[i] = NULL;
\r
1942 if( appData.fontPieceSize >= 50 && appData.fontPieceSize <= 150 ) {
\r
1943 fontHeight = appData.fontPieceSize;
\r
1946 fontHeight = (fontHeight * squareSize) / 100;
\r
1948 lf.lfHeight = -MulDiv( fontHeight, GetDeviceCaps(hdc, LOGPIXELSY), 72 );
\r
1950 lf.lfEscapement = 0;
\r
1951 lf.lfOrientation = 0;
\r
1952 lf.lfWeight = FW_NORMAL;
\r
1954 lf.lfUnderline = 0;
\r
1955 lf.lfStrikeOut = 0;
\r
1956 lf.lfCharSet = DEFAULT_CHARSET;
\r
1957 lf.lfOutPrecision = OUT_DEFAULT_PRECIS;
\r
1958 lf.lfClipPrecision = CLIP_DEFAULT_PRECIS;
\r
1959 lf.lfQuality = PROOF_QUALITY;
\r
1960 lf.lfPitchAndFamily = DEFAULT_PITCH | FF_DONTCARE;
\r
1961 strncpy( lf.lfFaceName, appData.renderPiecesWithFont, sizeof(lf.lfFaceName) );
\r
1962 lf.lfFaceName[ sizeof(lf.lfFaceName) - 1 ] = '\0';
\r
1964 hPieceFont = CreateFontIndirect( &lf );
\r
1966 if( hPieceFont == NULL ) {
\r
1967 fontBitmapSquareSize = -2;
\r
1970 /* Setup font-to-piece character table */
\r
1971 if( ! SetCharTable(pieceToFontChar, appData.fontToPieceTable) ) {
\r
1972 /* No (or wrong) global settings, try to detect the font */
\r
1973 if( strstr(lf.lfFaceName,"Alpha") != NULL ) {
\r
1975 SetCharTable(pieceToFontChar, "phbrqkojntwl");
\r
1977 else if( strstr(lf.lfFaceName,"DiagramTT") != NULL ) {
\r
1978 /* DiagramTT* family */
\r
1979 SetCharTable(pieceToFontChar, "PNLRQKpnlrqk");
\r
1981 else if( strstr(lf.lfFaceName,"WinboardF") != NULL ) {
\r
1982 /* Fairy symbols */
\r
1983 SetCharTable(pieceToFontChar, "PNBRQFEACWMOHIJGDVSLUKpnbrqfeacwmohijgdvsluk");
\r
1985 else if( strstr(lf.lfFaceName,"GC2004D") != NULL ) {
\r
1986 /* Good Companion (Some characters get warped as literal :-( */
\r
1987 char s[] = "1cmWG0??S??oYI23wgQU";
\r
1988 s[0]=0xB9; s[1]=0xA9; s[6]=0xB1; s[11]=0xBB; s[12]=0xAB; s[17]=0xB3;
\r
1989 SetCharTable(pieceToFontChar, s);
\r
1992 /* Cases, Condal, Leipzig, Lucena, Marroquin, Merida, Usual */
\r
1993 SetCharTable(pieceToFontChar, "pnbrqkomvtwl");
\r
1997 /* Create bitmaps */
\r
1998 hfont_old = SelectObject( hdc, hPieceFont );
\r
1999 for(i=(int)WhitePawn; i<(int)EmptySquare; i++) /* [HGM] made a loop for this */
\r
2000 if(PieceToChar((ChessSquare)i) != '.') /* skip unused pieces */
\r
2001 CreatePieceMaskFromFont( hdc_window, hdc, i );
\r
2003 SelectObject( hdc, hfont_old );
\r
2005 fontBitmapSquareSize = squareSize;
\r
2009 if( hdc != NULL ) {
\r
2013 if( hdc_window != NULL ) {
\r
2014 ReleaseDC( hwndMain, hdc_window );
\r
2019 DoLoadBitmap(HINSTANCE hinst, char *piece, int squareSize, char *suffix)
\r
2023 sprintf(name, "%s%d%s", piece, squareSize, suffix);
\r
2024 if (gameInfo.event &&
\r
2025 strcmp(gameInfo.event, "Easter Egg Hunt") == 0 &&
\r
2026 strcmp(name, "k80s") == 0) {
\r
2027 strcpy(name, "tim");
\r
2029 return LoadBitmap(hinst, name);
\r
2033 /* Insert a color into the program's logical palette
\r
2034 structure. This code assumes the given color is
\r
2035 the result of the RGB or PALETTERGB macro, and it
\r
2036 knows how those macros work (which is documented).
\r
2039 InsertInPalette(COLORREF color)
\r
2041 LPPALETTEENTRY pe = &(pLogPal->palPalEntry[pLogPal->palNumEntries]);
\r
2043 if (pLogPal->palNumEntries++ >= PALETTESIZE) {
\r
2044 DisplayFatalError(_("Too many colors"), 0, 1);
\r
2045 pLogPal->palNumEntries--;
\r
2049 pe->peFlags = (char) 0;
\r
2050 pe->peRed = (char) (0xFF & color);
\r
2051 pe->peGreen = (char) (0xFF & (color >> 8));
\r
2052 pe->peBlue = (char) (0xFF & (color >> 16));
\r
2058 InitDrawingColors()
\r
2060 if (pLogPal == NULL) {
\r
2061 /* Allocate enough memory for a logical palette with
\r
2062 * PALETTESIZE entries and set the size and version fields
\r
2063 * of the logical palette structure.
\r
2065 pLogPal = (NPLOGPALETTE)
\r
2066 LocalAlloc(LMEM_FIXED, (sizeof(LOGPALETTE) +
\r
2067 (sizeof(PALETTEENTRY) * (PALETTESIZE))));
\r
2068 pLogPal->palVersion = 0x300;
\r
2070 pLogPal->palNumEntries = 0;
\r
2072 InsertInPalette(lightSquareColor);
\r
2073 InsertInPalette(darkSquareColor);
\r
2074 InsertInPalette(whitePieceColor);
\r
2075 InsertInPalette(blackPieceColor);
\r
2076 InsertInPalette(highlightSquareColor);
\r
2077 InsertInPalette(premoveHighlightColor);
\r
2079 /* create a logical color palette according the information
\r
2080 * in the LOGPALETTE structure.
\r
2082 hPal = CreatePalette((LPLOGPALETTE) pLogPal);
\r
2084 lightSquareBrush = CreateSolidBrush(lightSquareColor);
\r
2085 blackSquareBrush = CreateSolidBrush(blackPieceColor);
\r
2086 darkSquareBrush = CreateSolidBrush(darkSquareColor);
\r
2087 whitePieceBrush = CreateSolidBrush(whitePieceColor);
\r
2088 blackPieceBrush = CreateSolidBrush(blackPieceColor);
\r
2089 iconBkgndBrush = CreateSolidBrush(GetSysColor(COLOR_BACKGROUND));
\r
2090 explodeBrush = CreateSolidBrush(highlightSquareColor); // [HGM] atomic
\r
2091 markerBrush = CreateSolidBrush(premoveHighlightColor); // [HGM] markers
\r
2092 /* [AS] Force rendering of the font-based pieces */
\r
2093 if( fontBitmapSquareSize > 0 ) {
\r
2094 fontBitmapSquareSize = 0;
\r
2100 BoardWidth(int boardSize, int n)
\r
2101 { /* [HGM] argument n added to allow different width and height */
\r
2102 int lineGap = sizeInfo[boardSize].lineGap;
\r
2104 if( appData.overrideLineGap >= 0 && appData.overrideLineGap <= 5 ) {
\r
2105 lineGap = appData.overrideLineGap;
\r
2108 return (n + 1) * lineGap +
\r
2109 n * sizeInfo[boardSize].squareSize;
\r
2112 /* Respond to board resize by dragging edge */
\r
2114 ResizeBoard(int newSizeX, int newSizeY, int flags)
\r
2116 BoardSize newSize = NUM_SIZES - 1;
\r
2117 static int recurse = 0;
\r
2118 if (IsIconic(hwndMain)) return;
\r
2119 if (recurse > 0) return;
\r
2121 while (newSize > 0) {
\r
2122 InitDrawingSizes(newSize+1000, 0); // [HGM] kludge to update sizeInfo without visible effects
\r
2123 if(newSizeX >= sizeInfo[newSize].cliWidth &&
\r
2124 newSizeY >= sizeInfo[newSize].cliHeight) break;
\r
2127 boardSize = newSize;
\r
2128 InitDrawingSizes(boardSize, flags);
\r
2133 extern Boolean twoBoards, partnerUp; // [HGM] dual
\r
2136 InitDrawingSizes(BoardSize boardSize, int flags)
\r
2138 int i, boardWidth, boardHeight; /* [HGM] height treated separately */
\r
2139 ChessSquare piece;
\r
2140 static int oldBoardSize = -1, oldTinyLayout = 0;
\r
2142 SIZE clockSize, messageSize;
\r
2144 char buf[MSG_SIZ];
\r
2146 HMENU hmenu = GetMenu(hwndMain);
\r
2147 RECT crect, wrect, oldRect;
\r
2149 LOGBRUSH logbrush;
\r
2151 int suppressVisibleEffects = 0; // [HGM] kludge to request updating sizeInfo only
\r
2152 if((int)boardSize >= 1000 ) { boardSize -= 1000; suppressVisibleEffects = 1; }
\r
2154 /* [HGM] call with -2 uses old size (for if nr of files, ranks changes) */
\r
2155 if(boardSize == (BoardSize)(-2) ) boardSize = oldBoardSize;
\r
2157 oldRect.left = wpMain.x; //[HGM] placement: remember previous window params
\r
2158 oldRect.top = wpMain.y;
\r
2159 oldRect.right = wpMain.x + wpMain.width;
\r
2160 oldRect.bottom = wpMain.y + wpMain.height;
\r
2162 tinyLayout = sizeInfo[boardSize].tinyLayout;
\r
2163 smallLayout = sizeInfo[boardSize].smallLayout;
\r
2164 squareSize = sizeInfo[boardSize].squareSize;
\r
2165 lineGap = sizeInfo[boardSize].lineGap;
\r
2166 minorSize = 0; /* [HGM] Kludge to see if demagnified pieces need to be shifted */
\r
2168 if( appData.overrideLineGap >= 0 && appData.overrideLineGap <= 5 ) {
\r
2169 lineGap = appData.overrideLineGap;
\r
2172 if (tinyLayout != oldTinyLayout) {
\r
2173 long style = GetWindowLong(hwndMain, GWL_STYLE);
\r
2175 style &= ~WS_SYSMENU;
\r
2176 InsertMenu(hmenu, IDM_Exit, MF_BYCOMMAND, IDM_Minimize,
\r
2177 "&Minimize\tCtrl+F4");
\r
2179 style |= WS_SYSMENU;
\r
2180 RemoveMenu(hmenu, IDM_Minimize, MF_BYCOMMAND);
\r
2182 SetWindowLong(hwndMain, GWL_STYLE, style);
\r
2184 for (i=0; menuBarText[tinyLayout][i]; i++) {
\r
2185 ModifyMenu(hmenu, i, MF_STRING|MF_BYPOSITION|MF_POPUP,
\r
2186 (UINT)GetSubMenu(hmenu, i), T_(menuBarText[tinyLayout][i]));
\r
2188 DrawMenuBar(hwndMain);
\r
2191 boardWidth = BoardWidth(boardSize, BOARD_WIDTH);
\r
2192 boardHeight = BoardWidth(boardSize, BOARD_HEIGHT);
\r
2194 /* Get text area sizes */
\r
2195 hdc = GetDC(hwndMain);
\r
2196 if (appData.clockMode) {
\r
2197 sprintf(buf, _("White: %s"), TimeString(23*60*60*1000L));
\r
2199 sprintf(buf, _("White"));
\r
2201 oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);
\r
2202 GetTextExtentPoint(hdc, buf, strlen(buf), &clockSize);
\r
2203 SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);
\r
2204 str = _("We only care about the height here");
\r
2205 GetTextExtentPoint(hdc, str, strlen(str), &messageSize);
\r
2206 SelectObject(hdc, oldFont);
\r
2207 ReleaseDC(hwndMain, hdc);
\r
2209 /* Compute where everything goes */
\r
2210 if((first.programLogo || second.programLogo) && !tinyLayout) {
\r
2211 /* [HGM] logo: if either logo is on, reserve space for it */
\r
2212 logoHeight = 2*clockSize.cy;
\r
2213 leftLogoRect.left = OUTER_MARGIN;
\r
2214 leftLogoRect.right = leftLogoRect.left + 4*clockSize.cy;
\r
2215 leftLogoRect.top = OUTER_MARGIN;
\r
2216 leftLogoRect.bottom = OUTER_MARGIN + logoHeight;
\r
2218 rightLogoRect.right = OUTER_MARGIN + boardWidth;
\r
2219 rightLogoRect.left = rightLogoRect.right - 4*clockSize.cy;
\r
2220 rightLogoRect.top = OUTER_MARGIN;
\r
2221 rightLogoRect.bottom = OUTER_MARGIN + logoHeight;
\r
2224 whiteRect.left = leftLogoRect.right;
\r
2225 whiteRect.right = OUTER_MARGIN + boardWidth/2 - INNER_MARGIN/2;
\r
2226 whiteRect.top = OUTER_MARGIN;
\r
2227 whiteRect.bottom = whiteRect.top + logoHeight;
\r
2229 blackRect.right = rightLogoRect.left;
\r
2230 blackRect.left = whiteRect.right + INNER_MARGIN;
\r
2231 blackRect.top = whiteRect.top;
\r
2232 blackRect.bottom = whiteRect.bottom;
\r
2234 whiteRect.left = OUTER_MARGIN;
\r
2235 whiteRect.right = whiteRect.left + boardWidth/2 - INNER_MARGIN/2;
\r
2236 whiteRect.top = OUTER_MARGIN;
\r
2237 whiteRect.bottom = whiteRect.top + clockSize.cy;
\r
2239 blackRect.left = whiteRect.right + INNER_MARGIN;
\r
2240 blackRect.right = blackRect.left + boardWidth/2 - 1;
\r
2241 blackRect.top = whiteRect.top;
\r
2242 blackRect.bottom = whiteRect.bottom;
\r
2244 logoHeight = 0; // [HGM] logo: suppress logo after change to tiny layout!
\r
2247 messageRect.left = OUTER_MARGIN + MESSAGE_LINE_LEFTMARGIN;
\r
2248 if (appData.showButtonBar) {
\r
2249 messageRect.right = OUTER_MARGIN + boardWidth // [HGM] logo: expressed independent of clock placement
\r
2250 - N_BUTTONS*BUTTON_WIDTH - MESSAGE_LINE_LEFTMARGIN;
\r
2252 messageRect.right = OUTER_MARGIN + boardWidth;
\r
2254 messageRect.top = whiteRect.bottom + INNER_MARGIN;
\r
2255 messageRect.bottom = messageRect.top + messageSize.cy;
\r
2257 boardRect.left = OUTER_MARGIN;
\r
2258 boardRect.right = boardRect.left + boardWidth;
\r
2259 boardRect.top = messageRect.bottom + INNER_MARGIN;
\r
2260 boardRect.bottom = boardRect.top + boardHeight;
\r
2262 sizeInfo[boardSize].cliWidth = boardRect.right + OUTER_MARGIN;
\r
2263 sizeInfo[boardSize].cliHeight = boardRect.bottom + OUTER_MARGIN;
\r
2264 oldBoardSize = boardSize;
\r
2265 oldTinyLayout = tinyLayout;
\r
2266 winW = 2 * GetSystemMetrics(SM_CXFRAME) + boardRect.right + OUTER_MARGIN;
\r
2267 winH = 2 * GetSystemMetrics(SM_CYFRAME) + GetSystemMetrics(SM_CYMENU) +
\r
2268 GetSystemMetrics(SM_CYCAPTION) + boardRect.bottom + OUTER_MARGIN;
\r
2269 winW *= 1 + twoBoards;
\r
2270 if(suppressVisibleEffects) return; // [HGM] when called for filling sizeInfo only
\r
2271 wpMain.width = winW; // [HGM] placement: set through temporary which can used by initial sizing choice
\r
2272 wpMain.height = winH; // without disturbing window attachments
\r
2273 GetWindowRect(hwndMain, &wrect);
\r
2274 SetWindowPos(hwndMain, NULL, 0, 0, wpMain.width, wpMain.height,
\r
2275 SWP_NOCOPYBITS|SWP_NOZORDER|SWP_NOMOVE);
\r
2277 // [HGM] placement: let attached windows follow size change.
\r
2278 ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, moveHistoryDialog, &wpMoveHistory );
\r
2279 ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, evalGraphDialog, &wpEvalGraph );
\r
2280 ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, engineOutputDialog, &wpEngineOutput );
\r
2281 ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, gameListDialog, &wpGameList );
\r
2282 ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, hwndConsole, &wpConsole );
\r
2284 /* compensate if menu bar wrapped */
\r
2285 GetClientRect(hwndMain, &crect);
\r
2286 offby = boardRect.bottom + OUTER_MARGIN - crect.bottom;
\r
2287 wpMain.height += offby;
\r
2289 case WMSZ_TOPLEFT:
\r
2290 SetWindowPos(hwndMain, NULL,
\r
2291 wrect.right - wpMain.width, wrect.bottom - wpMain.height,
\r
2292 wpMain.width, wpMain.height, SWP_NOCOPYBITS|SWP_NOZORDER);
\r
2295 case WMSZ_TOPRIGHT:
\r
2297 SetWindowPos(hwndMain, NULL,
\r
2298 wrect.left, wrect.bottom - wpMain.height,
\r
2299 wpMain.width, wpMain.height, SWP_NOCOPYBITS|SWP_NOZORDER);
\r
2302 case WMSZ_BOTTOMLEFT:
\r
2304 SetWindowPos(hwndMain, NULL,
\r
2305 wrect.right - wpMain.width, wrect.top,
\r
2306 wpMain.width, wpMain.height, SWP_NOCOPYBITS|SWP_NOZORDER);
\r
2309 case WMSZ_BOTTOMRIGHT:
\r
2313 SetWindowPos(hwndMain, NULL, 0, 0, wpMain.width, wpMain.height,
\r
2314 SWP_NOCOPYBITS|SWP_NOZORDER|SWP_NOMOVE);
\r
2319 for (i = 0; i < N_BUTTONS; i++) {
\r
2320 if (buttonDesc[i].hwnd != NULL) {
\r
2321 DestroyWindow(buttonDesc[i].hwnd);
\r
2322 buttonDesc[i].hwnd = NULL;
\r
2324 if (appData.showButtonBar) {
\r
2325 buttonDesc[i].hwnd =
\r
2326 CreateWindow("BUTTON", buttonDesc[i].label,
\r
2327 WS_VISIBLE | WS_CHILD | BS_PUSHBUTTON,
\r
2328 boardRect.right - BUTTON_WIDTH*(N_BUTTONS-i),
\r
2329 messageRect.top, BUTTON_WIDTH, messageSize.cy, hwndMain,
\r
2330 (HMENU) buttonDesc[i].id,
\r
2331 (HINSTANCE) GetWindowLong(hwndMain, GWL_HINSTANCE), NULL);
\r
2333 SendMessage(buttonDesc[i].hwnd, WM_SETFONT,
\r
2334 (WPARAM)font[boardSize][MESSAGE_FONT]->hf,
\r
2335 MAKELPARAM(FALSE, 0));
\r
2337 if (buttonDesc[i].id == IDM_Pause)
\r
2338 hwndPause = buttonDesc[i].hwnd;
\r
2339 buttonDesc[i].wndproc = (WNDPROC)
\r
2340 SetWindowLong(buttonDesc[i].hwnd, GWL_WNDPROC, (LONG) ButtonProc);
\r
2343 if (gridPen != NULL) DeleteObject(gridPen);
\r
2344 if (highlightPen != NULL) DeleteObject(highlightPen);
\r
2345 if (premovePen != NULL) DeleteObject(premovePen);
\r
2346 if (lineGap != 0) {
\r
2347 logbrush.lbStyle = BS_SOLID;
\r
2348 logbrush.lbColor = RGB(0, 0, 0); /* grid pen color = black */
\r
2350 ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,
\r
2351 lineGap, &logbrush, 0, NULL);
\r
2352 logbrush.lbColor = highlightSquareColor;
\r
2354 ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,
\r
2355 lineGap, &logbrush, 0, NULL);
\r
2357 logbrush.lbColor = premoveHighlightColor;
\r
2359 ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,
\r
2360 lineGap, &logbrush, 0, NULL);
\r
2362 /* [HGM] Loop had to be split in part for vert. and hor. lines */
\r
2363 for (i = 0; i < BOARD_HEIGHT + 1; i++) {
\r
2364 gridEndpoints[i*2].x = boardRect.left + lineGap / 2;
\r
2365 gridEndpoints[i*2].y = gridEndpoints[i*2 + 1].y =
\r
2366 boardRect.top + lineGap / 2 + (i * (squareSize + lineGap));
\r
2367 gridEndpoints[i*2 + 1].x = boardRect.left + lineGap / 2 +
\r
2368 BOARD_WIDTH * (squareSize + lineGap);
\r
2369 gridVertexCounts[i*2] = gridVertexCounts[i*2 + 1] = 2;
\r
2371 for (i = 0; i < BOARD_WIDTH + 1; i++) {
\r
2372 gridEndpoints[i*2 + BOARD_HEIGHT*2 + 2].y = boardRect.top + lineGap / 2;
\r
2373 gridEndpoints[i*2 + BOARD_HEIGHT*2 + 2].x =
\r
2374 gridEndpoints[i*2 + 1 + BOARD_HEIGHT*2 + 2].x = boardRect.left +
\r
2375 lineGap / 2 + (i * (squareSize + lineGap));
\r
2376 gridEndpoints[i*2 + 1 + BOARD_HEIGHT*2 + 2].y =
\r
2377 boardRect.top + BOARD_HEIGHT * (squareSize + lineGap);
\r
2378 gridVertexCounts[i*2] = gridVertexCounts[i*2 + 1] = 2;
\r
2382 /* [HGM] Licensing requirement */
\r
2384 if(gameInfo.variant == VariantGothic) GothicPopUp( GOTHIC, VariantGothic); else
\r
2387 if(gameInfo.variant == VariantFalcon) GothicPopUp( FALCON, VariantFalcon); else
\r
2389 GothicPopUp( "", VariantNormal);
\r
2392 /* if (boardSize == oldBoardSize) return; [HGM] variant might have changed */
\r
2394 /* Load piece bitmaps for this board size */
\r
2395 for (i=0; i<=2; i++) {
\r
2396 for (piece = WhitePawn;
\r
2397 (int) piece < (int) BlackPawn;
\r
2398 piece = (ChessSquare) ((int) piece + 1)) {
\r
2399 if (pieceBitmap[i][piece] != NULL)
\r
2400 DeleteObject(pieceBitmap[i][piece]);
\r
2404 fontBitmapSquareSize = 0; /* [HGM] render: make sure pieces will be recreated, as we might need others now */
\r
2405 // Orthodox Chess pieces
\r
2406 pieceBitmap[0][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "s");
\r
2407 pieceBitmap[0][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "s");
\r
2408 pieceBitmap[0][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "s");
\r
2409 pieceBitmap[0][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "s");
\r
2410 pieceBitmap[0][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "s");
\r
2411 pieceBitmap[1][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "o");
\r
2412 pieceBitmap[1][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "o");
\r
2413 pieceBitmap[1][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "o");
\r
2414 pieceBitmap[1][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "o");
\r
2415 pieceBitmap[1][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "o");
\r
2416 pieceBitmap[2][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "w");
\r
2417 pieceBitmap[2][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "w");
\r
2418 pieceBitmap[2][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "w");
\r
2419 pieceBitmap[2][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "w");
\r
2420 pieceBitmap[2][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "w");
\r
2421 if( !strcmp(appData.variant, "shogi") && (squareSize==72 || squareSize==49)) {
\r
2422 // in Shogi, Hijack the unused Queen for Lance
\r
2423 pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "s");
\r
2424 pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "o");
\r
2425 pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "w");
\r
2427 pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "s");
\r
2428 pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "o");
\r
2429 pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "w");
\r
2432 if(squareSize <= 72 && squareSize >= 33) {
\r
2433 /* A & C are available in most sizes now */
\r
2434 if(squareSize != 49 && squareSize != 72 && squareSize != 33) { // Vortex-like
\r
2435 pieceBitmap[0][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "s");
\r
2436 pieceBitmap[1][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "o");
\r
2437 pieceBitmap[2][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "w");
\r
2438 pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "s");
\r
2439 pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "o");
\r
2440 pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "w");
\r
2441 pieceBitmap[0][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "s");
\r
2442 pieceBitmap[1][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "o");
\r
2443 pieceBitmap[2][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "w");
\r
2444 pieceBitmap[0][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "s");
\r
2445 pieceBitmap[1][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "o");
\r
2446 pieceBitmap[2][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "w");
\r
2447 } else { // Smirf-like
\r
2448 pieceBitmap[0][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "s");
\r
2449 pieceBitmap[1][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "o");
\r
2450 pieceBitmap[2][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "w");
\r
2452 if(gameInfo.variant == VariantGothic) { // Vortex-like
\r
2453 pieceBitmap[0][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "s");
\r
2454 pieceBitmap[1][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "o");
\r
2455 pieceBitmap[2][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "w");
\r
2456 } else { // WinBoard standard
\r
2457 pieceBitmap[0][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "s");
\r
2458 pieceBitmap[1][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "o");
\r
2459 pieceBitmap[2][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "w");
\r
2464 if(squareSize==72 || squareSize==49 || squareSize==33) { /* experiment with some home-made bitmaps */
\r
2465 pieceBitmap[0][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "s");
\r
2466 pieceBitmap[1][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "o");
\r
2467 pieceBitmap[2][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "w");
\r
2468 pieceBitmap[0][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "s");
\r
2469 pieceBitmap[1][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "o");
\r
2470 pieceBitmap[2][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "w");
\r
2471 pieceBitmap[0][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "s");
\r
2472 pieceBitmap[1][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "o");
\r
2473 pieceBitmap[2][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "w");
\r
2474 pieceBitmap[0][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "s");
\r
2475 pieceBitmap[1][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "o");
\r
2476 pieceBitmap[2][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "w");
\r
2477 pieceBitmap[0][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "s");
\r
2478 pieceBitmap[1][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "o");
\r
2479 pieceBitmap[2][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "w");
\r
2480 pieceBitmap[0][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "s");
\r
2481 pieceBitmap[1][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "o");
\r
2482 pieceBitmap[2][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "w");
\r
2483 pieceBitmap[0][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "s");
\r
2484 pieceBitmap[1][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "o");
\r
2485 pieceBitmap[2][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "w");
\r
2486 pieceBitmap[0][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "s");
\r
2487 pieceBitmap[1][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "o");
\r
2488 pieceBitmap[2][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "w");
\r
2489 pieceBitmap[0][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "s");
\r
2490 pieceBitmap[1][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "o");
\r
2491 pieceBitmap[2][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "w");
\r
2492 pieceBitmap[0][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "s");
\r
2493 pieceBitmap[1][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "o");
\r
2494 pieceBitmap[2][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "w");
\r
2496 if(gameInfo.variant == VariantShogi) { /* promoted Gold represemtations */
\r
2497 pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "wp", squareSize, "s");
\r
2498 pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "wp", squareSize, "o");
\r
2499 pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "w", squareSize, "w");
\r
2500 pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "wn", squareSize, "s");
\r
2501 pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "wn", squareSize, "o");
\r
2502 pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "w", squareSize, "w");
\r
2503 pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "ws", squareSize, "s");
\r
2504 pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "ws", squareSize, "o");
\r
2505 pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "w", squareSize, "w");
\r
2506 pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "wl", squareSize, "s");
\r
2507 pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "wl", squareSize, "o");
\r
2508 pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "w", squareSize, "w");
\r
2510 pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "s");
\r
2511 pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "o");
\r
2512 pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "w");
\r
2513 pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "s");
\r
2514 pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "o");
\r
2515 pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "w");
\r
2516 pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "s");
\r
2517 pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "o");
\r
2518 pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "w");
\r
2519 pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "s");
\r
2520 pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "o");
\r
2521 pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "w");
\r
2524 } else { /* other size, no special bitmaps available. Use smaller symbols */
\r
2525 if((int)boardSize < 2) minorSize = sizeInfo[0].squareSize;
\r
2526 else minorSize = sizeInfo[(int)boardSize - 2].squareSize;
\r
2527 pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "s");
\r
2528 pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "o");
\r
2529 pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "w");
\r
2530 pieceBitmap[0][WhiteCardinal] = DoLoadBitmap(hInst, "b", minorSize, "s");
\r
2531 pieceBitmap[1][WhiteCardinal] = DoLoadBitmap(hInst, "b", minorSize, "o");
\r
2532 pieceBitmap[2][WhiteCardinal] = DoLoadBitmap(hInst, "b", minorSize, "w");
\r
2533 pieceBitmap[0][WhiteDragon] = DoLoadBitmap(hInst, "r", minorSize, "s");
\r
2534 pieceBitmap[1][WhiteDragon] = DoLoadBitmap(hInst, "r", minorSize, "o");
\r
2535 pieceBitmap[2][WhiteDragon] = DoLoadBitmap(hInst, "r", minorSize, "w");
\r
2536 pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "s");
\r
2537 pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "o");
\r
2538 pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "w");
\r
2542 if(gameInfo.variant == VariantShogi && squareSize == 58)
\r
2543 /* special Shogi support in this size */
\r
2544 { for (i=0; i<=2; i++) { /* replace all bitmaps */
\r
2545 for (piece = WhitePawn;
\r
2546 (int) piece < (int) BlackPawn;
\r
2547 piece = (ChessSquare) ((int) piece + 1)) {
\r
2548 if (pieceBitmap[i][piece] != NULL)
\r
2549 DeleteObject(pieceBitmap[i][piece]);
\r
2552 pieceBitmap[0][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "o");
\r
2553 pieceBitmap[0][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "o");
\r
2554 pieceBitmap[0][WhiteBishop] = DoLoadBitmap(hInst, "sb", squareSize, "o");
\r
2555 pieceBitmap[0][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "o");
\r
2556 pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "o");
\r
2557 pieceBitmap[0][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "o");
\r
2558 pieceBitmap[0][WhiteFerz] = DoLoadBitmap(hInst, "sf", squareSize, "o");
\r
2559 pieceBitmap[0][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "o");
\r
2560 pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "su", squareSize, "o");
\r
2561 pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "sh", squareSize, "o");
\r
2562 pieceBitmap[0][WhiteCardinal] = DoLoadBitmap(hInst, "sa", squareSize, "o");
\r
2563 pieceBitmap[0][WhiteDragon] = DoLoadBitmap(hInst, "sc", squareSize, "o");
\r
2564 pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "sg", squareSize, "o");
\r
2565 pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "ss", squareSize, "o");
\r
2566 pieceBitmap[1][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "o");
\r
2567 pieceBitmap[1][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "o");
\r
2568 pieceBitmap[1][WhiteBishop] = DoLoadBitmap(hInst, "sb", squareSize, "o");
\r
2569 pieceBitmap[1][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "o");
\r
2570 pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "o");
\r
2571 pieceBitmap[1][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "o");
\r
2572 pieceBitmap[1][WhiteFerz] = DoLoadBitmap(hInst, "sf", squareSize, "o");
\r
2573 pieceBitmap[1][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "o");
\r
2574 pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "su", squareSize, "o");
\r
2575 pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "sh", squareSize, "o");
\r
2576 pieceBitmap[1][WhiteCardinal] = DoLoadBitmap(hInst, "sa", squareSize, "o");
\r
2577 pieceBitmap[1][WhiteDragon] = DoLoadBitmap(hInst, "sc", squareSize, "o");
\r
2578 pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "sg", squareSize, "o");
\r
2579 pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "ss", squareSize, "o");
\r
2580 pieceBitmap[2][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "w");
\r
2581 pieceBitmap[2][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "w");
\r
2582 pieceBitmap[2][WhiteBishop] = DoLoadBitmap(hInst, "sr", squareSize, "w");
\r
2583 pieceBitmap[2][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "w");
\r
2584 pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "w");
\r
2585 pieceBitmap[2][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "w");
\r
2586 pieceBitmap[2][WhiteFerz] = DoLoadBitmap(hInst, "sw", squareSize, "w");
\r
2587 pieceBitmap[2][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "w");
\r
2588 pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "sp", squareSize, "w");
\r
2589 pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "sn", squareSize, "w");
\r
2590 pieceBitmap[2][WhiteCardinal] = DoLoadBitmap(hInst, "sr", squareSize, "w");
\r
2591 pieceBitmap[2][WhiteDragon] = DoLoadBitmap(hInst, "sr", squareSize, "w");
\r
2592 pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "sl", squareSize, "w");
\r
2593 pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "sw", squareSize, "w");
\r
2599 PieceBitmap(ChessSquare p, int kind)
\r
2601 if ((int) p >= (int) BlackPawn)
\r
2602 p = (ChessSquare) ((int) p - (int) BlackPawn + (int) WhitePawn);
\r
2604 return pieceBitmap[kind][(int) p];
\r
2607 /***************************************************************/
\r
2609 #define MIN(a,b) (((a) < (b)) ? (a) : (b))
\r
2610 #define MAX(a,b) (((a) > (b)) ? (a) : (b))
\r
2612 #define MIN3(a,b,c) (((a) < (b) && (a) < (c)) ? (a) : (((b) < (a) && (b) < (c)) ? (b) : (c)))
\r
2613 #define MAX3(a,b,c) (((a) > (b) && (a) > (c)) ? (a) : (((b) > (a) && (b) > (c)) ? (b) : (c)))
\r
2617 SquareToPos(int row, int column, int * x, int * y)
\r
2620 *x = boardRect.left + lineGap + ((BOARD_WIDTH-1)-column) * (squareSize + lineGap);
\r
2621 *y = boardRect.top + lineGap + row * (squareSize + lineGap);
\r
2623 *x = boardRect.left + lineGap + column * (squareSize + lineGap);
\r
2624 *y = boardRect.top + lineGap + ((BOARD_HEIGHT-1)-row) * (squareSize + lineGap);
\r
2629 DrawCoordsOnDC(HDC hdc)
\r
2631 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
2632 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
2633 char str[2] = { NULLCHAR, NULLCHAR };
\r
2634 int oldMode, oldAlign, x, y, start, i;
\r
2638 if (!appData.showCoords)
\r
2641 start = flipView ? 1-(ONE!='1') : 23+(ONE!='1')-BOARD_HEIGHT;
\r
2643 oldBrush = SelectObject(hdc, GetStockObject(BLACK_BRUSH));
\r
2644 oldMode = SetBkMode(hdc, (appData.monoMode ? OPAQUE : TRANSPARENT));
\r
2645 oldAlign = GetTextAlign(hdc);
\r
2646 oldFont = SelectObject(hdc, font[boardSize][COORD_FONT]->hf);
\r
2648 y = boardRect.top + lineGap;
\r
2649 x = boardRect.left + lineGap + gameInfo.holdingsWidth*(squareSize + lineGap);
\r
2651 SetTextAlign(hdc, TA_LEFT|TA_TOP);
\r
2652 for (i = 0; i < BOARD_HEIGHT; i++) {
\r
2653 str[0] = files[start + i];
\r
2654 ExtTextOut(hdc, x + 2, y + 1, 0, NULL, str, 1, NULL);
\r
2655 y += squareSize + lineGap;
\r
2658 start = flipView ? 12-(BOARD_RGHT-BOARD_LEFT) : 12;
\r
2660 SetTextAlign(hdc, TA_RIGHT|TA_BOTTOM);
\r
2661 for (i = 0; i < BOARD_RGHT - BOARD_LEFT; i++) {
\r
2662 str[0] = ranks[start + i];
\r
2663 ExtTextOut(hdc, x + squareSize - 2, y - 1, 0, NULL, str, 1, NULL);
\r
2664 x += squareSize + lineGap;
\r
2667 SelectObject(hdc, oldBrush);
\r
2668 SetBkMode(hdc, oldMode);
\r
2669 SetTextAlign(hdc, oldAlign);
\r
2670 SelectObject(hdc, oldFont);
\r
2674 DrawGridOnDC(HDC hdc)
\r
2678 if (lineGap != 0) {
\r
2679 oldPen = SelectObject(hdc, gridPen);
\r
2680 PolyPolyline(hdc, gridEndpoints, gridVertexCounts, BOARD_WIDTH+BOARD_HEIGHT + 2);
\r
2681 SelectObject(hdc, oldPen);
\r
2685 #define HIGHLIGHT_PEN 0
\r
2686 #define PREMOVE_PEN 1
\r
2689 DrawHighlightOnDC(HDC hdc, BOOLEAN on, int x, int y, int pen)
\r
2692 HPEN oldPen, hPen;
\r
2693 if (lineGap == 0) return;
\r
2695 x1 = boardRect.left +
\r
2696 lineGap/2 + ((BOARD_WIDTH-1)-x) * (squareSize + lineGap);
\r
2697 y1 = boardRect.top +
\r
2698 lineGap/2 + y * (squareSize + lineGap);
\r
2700 x1 = boardRect.left +
\r
2701 lineGap/2 + x * (squareSize + lineGap);
\r
2702 y1 = boardRect.top +
\r
2703 lineGap/2 + ((BOARD_HEIGHT-1)-y) * (squareSize + lineGap);
\r
2705 hPen = pen ? premovePen : highlightPen;
\r
2706 oldPen = SelectObject(hdc, on ? hPen : gridPen);
\r
2707 MoveToEx(hdc, x1, y1, NULL);
\r
2708 LineTo(hdc, x1 + squareSize + lineGap, y1);
\r
2709 LineTo(hdc, x1 + squareSize + lineGap, y1 + squareSize + lineGap);
\r
2710 LineTo(hdc, x1, y1 + squareSize + lineGap);
\r
2711 LineTo(hdc, x1, y1);
\r
2712 SelectObject(hdc, oldPen);
\r
2716 DrawHighlightsOnDC(HDC hdc, HighlightInfo *h, int pen)
\r
2719 for (i=0; i<2; i++) {
\r
2720 if (h->sq[i].x >= 0 && h->sq[i].y >= 0)
\r
2721 DrawHighlightOnDC(hdc, TRUE,
\r
2722 h->sq[i].x, h->sq[i].y,
\r
2727 /* Note: sqcolor is used only in monoMode */
\r
2728 /* Note that this code is largely duplicated in woptions.c,
\r
2729 function DrawSampleSquare, so that needs to be updated too */
\r
2731 DrawPieceOnDC(HDC hdc, ChessSquare piece, int color, int sqcolor, int x, int y, HDC tmphdc)
\r
2733 HBITMAP oldBitmap;
\r
2737 if (appData.blindfold) return;
\r
2739 /* [AS] Use font-based pieces if needed */
\r
2740 if( fontBitmapSquareSize >= 0 && (squareSize > 32 || gameInfo.variant >= VariantShogi)) {
\r
2741 /* Create piece bitmaps, or do nothing if piece set is up to date */
\r
2742 CreatePiecesFromFont();
\r
2744 if( fontBitmapSquareSize == squareSize ) {
\r
2745 int index = TranslatePieceToFontPiece(piece);
\r
2747 SelectObject( tmphdc, hPieceMask[ index ] );
\r
2749 if(appData.upsideDown ? color==flipView : (flipView && gameInfo.variant == VariantShogi))
\r
2750 StretchBlt(hdc, x+squareSize, y+squareSize, -squareSize, -squareSize, tmphdc, 0, 0, squareSize, squareSize, SRCAND);
\r
2754 squareSize, squareSize,
\r
2759 SelectObject( tmphdc, hPieceFace[ index ] );
\r
2761 if(appData.upsideDown ? color==flipView : (flipView && gameInfo.variant == VariantShogi))
\r
2762 StretchBlt(hdc, x+squareSize, y+squareSize, -squareSize, -squareSize, tmphdc, 0, 0, squareSize, squareSize, SRCPAINT);
\r
2766 squareSize, squareSize,
\r
2775 if (appData.monoMode) {
\r
2776 SelectObject(tmphdc, PieceBitmap(piece,
\r
2777 color == sqcolor ? OUTLINE_PIECE : SOLID_PIECE));
\r
2778 BitBlt(hdc, x, y, squareSize, squareSize, tmphdc, 0, 0,
\r
2779 sqcolor ? SRCCOPY : NOTSRCCOPY);
\r
2781 tmpSize = squareSize;
\r
2783 ((piece >= (int)WhiteNightrider && piece <= WhiteGrasshopper) ||
\r
2784 (piece >= (int)BlackNightrider && piece <= BlackGrasshopper)) ) {
\r
2785 /* [HGM] no bitmap available for promoted pieces in Crazyhouse */
\r
2786 /* Bitmaps of smaller size are substituted, but we have to align them */
\r
2787 x += (squareSize - minorSize)>>1;
\r
2788 y += squareSize - minorSize - 2;
\r
2789 tmpSize = minorSize;
\r
2791 if (color || appData.allWhite ) {
\r
2792 oldBitmap = SelectObject(tmphdc, PieceBitmap(piece, WHITE_PIECE));
\r
2794 oldBrush = SelectObject(hdc, whitePieceBrush);
\r
2795 else oldBrush = SelectObject(hdc, blackPieceBrush);
\r
2796 if(appData.upsideDown && color==flipView)
\r
2797 StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, 0x00B8074A);
\r
2799 BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, 0x00B8074A);
\r
2800 /* Use black for outline of white pieces */
\r
2801 SelectObject(tmphdc, PieceBitmap(piece, OUTLINE_PIECE));
\r
2802 if(appData.upsideDown && color==flipView)
\r
2803 StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, SRCAND);
\r
2805 BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, SRCAND);
\r
2807 /* Use square color for details of black pieces */
\r
2808 oldBitmap = SelectObject(tmphdc, PieceBitmap(piece, SOLID_PIECE));
\r
2809 oldBrush = SelectObject(hdc, blackPieceBrush);
\r
2810 if(appData.upsideDown && !flipView)
\r
2811 StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, 0x00B8074A);
\r
2813 BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, 0x00B8074A);
\r
2815 SelectObject(hdc, oldBrush);
\r
2816 SelectObject(tmphdc, oldBitmap);
\r
2820 /* [AS] Compute a drawing mode for a square, based on specified settings (see DrawTile) */
\r
2821 int GetBackTextureMode( int algo )
\r
2823 int result = BACK_TEXTURE_MODE_DISABLED;
\r
2827 case BACK_TEXTURE_MODE_PLAIN:
\r
2828 result = 1; /* Always use identity map */
\r
2830 case BACK_TEXTURE_MODE_FULL_RANDOM:
\r
2831 result = 1 + (myrandom() % 3); /* Pick a transformation at random */
\r
2839 [AS] Compute and save texture drawing info, otherwise we may not be able
\r
2840 to handle redraws cleanly (as random numbers would always be different).
\r
2842 VOID RebuildTextureSquareInfo()
\r
2852 ZeroMemory( &backTextureSquareInfo, sizeof(backTextureSquareInfo) );
\r
2854 if( liteBackTexture != NULL ) {
\r
2855 if( GetObject( liteBackTexture, sizeof(bi), &bi ) > 0 ) {
\r
2856 lite_w = bi.bmWidth;
\r
2857 lite_h = bi.bmHeight;
\r
2861 if( darkBackTexture != NULL ) {
\r
2862 if( GetObject( darkBackTexture, sizeof(bi), &bi ) > 0 ) {
\r
2863 dark_w = bi.bmWidth;
\r
2864 dark_h = bi.bmHeight;
\r
2868 for( row=0; row<BOARD_HEIGHT; row++ ) {
\r
2869 for( col=0; col<BOARD_WIDTH; col++ ) {
\r
2870 if( (col + row) & 1 ) {
\r
2872 if( lite_w >= squareSize && lite_h >= squareSize ) {
\r
2873 if( lite_w >= squareSize*BOARD_WIDTH )
\r
2874 backTextureSquareInfo[row][col].x = (2*col+1)*lite_w/(2*BOARD_WIDTH) - squareSize/2; /* [HGM] cut out of center of virtual square */
\r
2876 backTextureSquareInfo[row][col].x = col * (lite_w - squareSize) / (BOARD_WIDTH-1); /* [HGM] divide by size-1 in stead of size! */
\r
2877 if( lite_h >= squareSize*BOARD_HEIGHT )
\r
2878 backTextureSquareInfo[row][col].y = (2*(BOARD_HEIGHT-row)-1)*lite_h/(2*BOARD_HEIGHT) - squareSize/2;
\r
2880 backTextureSquareInfo[row][col].y = (BOARD_HEIGHT-1-row) * (lite_h - squareSize) / (BOARD_HEIGHT-1);
\r
2881 backTextureSquareInfo[row][col].mode = GetBackTextureMode(liteBackTextureMode);
\r
2886 if( dark_w >= squareSize && dark_h >= squareSize ) {
\r
2887 if( dark_w >= squareSize*BOARD_WIDTH )
\r
2888 backTextureSquareInfo[row][col].x = (2*col+1) * dark_w / (2*BOARD_WIDTH) - squareSize/2;
\r
2890 backTextureSquareInfo[row][col].x = col * (dark_w - squareSize) / (BOARD_WIDTH-1);
\r
2891 if( dark_h >= squareSize*BOARD_HEIGHT )
\r
2892 backTextureSquareInfo[row][col].y = (2*(BOARD_HEIGHT-row)-1) * dark_h / (2*BOARD_HEIGHT) - squareSize/2;
\r
2894 backTextureSquareInfo[row][col].y = (BOARD_HEIGHT-1-row) * (dark_h - squareSize) / (BOARD_HEIGHT-1);
\r
2895 backTextureSquareInfo[row][col].mode = GetBackTextureMode(darkBackTextureMode);
\r
2902 /* [AS] Arrow highlighting support */
\r
2904 static int A_WIDTH = 5; /* Width of arrow body */
\r
2906 #define A_HEIGHT_FACTOR 6 /* Length of arrow "point", relative to body width */
\r
2907 #define A_WIDTH_FACTOR 3 /* Width of arrow "point", relative to body width */
\r
2909 static double Sqr( double x )
\r
2914 static int Round( double x )
\r
2916 return (int) (x + 0.5);
\r
2919 /* Draw an arrow between two points using current settings */
\r
2920 VOID DrawArrowBetweenPoints( HDC hdc, int s_x, int s_y, int d_x, int d_y )
\r
2923 double dx, dy, j, k, x, y;
\r
2925 if( d_x == s_x ) {
\r
2926 int h = (d_y > s_y) ? +A_WIDTH*A_HEIGHT_FACTOR : -A_WIDTH*A_HEIGHT_FACTOR;
\r
2928 arrow[0].x = s_x + A_WIDTH;
\r
2931 arrow[1].x = s_x + A_WIDTH;
\r
2932 arrow[1].y = d_y - h;
\r
2934 arrow[2].x = s_x + A_WIDTH*A_WIDTH_FACTOR;
\r
2935 arrow[2].y = d_y - h;
\r
2940 arrow[4].x = s_x - A_WIDTH*A_WIDTH_FACTOR;
\r
2941 arrow[4].y = d_y - h;
\r
2943 arrow[5].x = s_x - A_WIDTH;
\r
2944 arrow[5].y = d_y - h;
\r
2946 arrow[6].x = s_x - A_WIDTH;
\r
2949 else if( d_y == s_y ) {
\r
2950 int w = (d_x > s_x) ? +A_WIDTH*A_HEIGHT_FACTOR : -A_WIDTH*A_HEIGHT_FACTOR;
\r
2953 arrow[0].y = s_y + A_WIDTH;
\r
2955 arrow[1].x = d_x - w;
\r
2956 arrow[1].y = s_y + A_WIDTH;
\r
2958 arrow[2].x = d_x - w;
\r
2959 arrow[2].y = s_y + A_WIDTH*A_WIDTH_FACTOR;
\r
2964 arrow[4].x = d_x - w;
\r
2965 arrow[4].y = s_y - A_WIDTH*A_WIDTH_FACTOR;
\r
2967 arrow[5].x = d_x - w;
\r
2968 arrow[5].y = s_y - A_WIDTH;
\r
2971 arrow[6].y = s_y - A_WIDTH;
\r
2974 /* [AS] Needed a lot of paper for this! :-) */
\r
2975 dy = (double) (d_y - s_y) / (double) (d_x - s_x);
\r
2976 dx = (double) (s_x - d_x) / (double) (s_y - d_y);
\r
2978 j = sqrt( Sqr(A_WIDTH) / (1.0 + Sqr(dx)) );
\r
2980 k = sqrt( Sqr(A_WIDTH*A_HEIGHT_FACTOR) / (1.0 + Sqr(dy)) );
\r
2985 arrow[0].x = Round(x - j);
\r
2986 arrow[0].y = Round(y + j*dx);
\r
2988 arrow[1].x = Round(x + j);
\r
2989 arrow[1].y = Round(y - j*dx);
\r
2992 x = (double) d_x - k;
\r
2993 y = (double) d_y - k*dy;
\r
2996 x = (double) d_x + k;
\r
2997 y = (double) d_y + k*dy;
\r
3000 arrow[2].x = Round(x + j);
\r
3001 arrow[2].y = Round(y - j*dx);
\r
3003 arrow[3].x = Round(x + j*A_WIDTH_FACTOR);
\r
3004 arrow[3].y = Round(y - j*A_WIDTH_FACTOR*dx);
\r
3009 arrow[5].x = Round(x - j*A_WIDTH_FACTOR);
\r
3010 arrow[5].y = Round(y + j*A_WIDTH_FACTOR*dx);
\r
3012 arrow[6].x = Round(x - j);
\r
3013 arrow[6].y = Round(y + j*dx);
\r
3016 Polygon( hdc, arrow, 7 );
\r
3019 /* [AS] Draw an arrow between two squares */
\r
3020 VOID DrawArrowBetweenSquares( HDC hdc, int s_col, int s_row, int d_col, int d_row )
\r
3022 int s_x, s_y, d_x, d_y;
\r
3029 if( s_col == d_col && s_row == d_row ) {
\r
3033 /* Get source and destination points */
\r
3034 SquareToPos( s_row, s_col, &s_x, &s_y);
\r
3035 SquareToPos( d_row, d_col, &d_x, &d_y);
\r
3038 d_y += squareSize / 4;
\r
3040 else if( d_y < s_y ) {
\r
3041 d_y += 3 * squareSize / 4;
\r
3044 d_y += squareSize / 2;
\r
3048 d_x += squareSize / 4;
\r
3050 else if( d_x < s_x ) {
\r
3051 d_x += 3 * squareSize / 4;
\r
3054 d_x += squareSize / 2;
\r
3057 s_x += squareSize / 2;
\r
3058 s_y += squareSize / 2;
\r
3060 /* Adjust width */
\r
3061 A_WIDTH = squareSize / 14;
\r
3064 stLB.lbStyle = BS_SOLID;
\r
3065 stLB.lbColor = appData.highlightArrowColor;
\r
3068 hpen = CreatePen( PS_SOLID, 2, RGB(0x00,0x00,0x00) );
\r
3069 holdpen = SelectObject( hdc, hpen );
\r
3070 hbrush = CreateBrushIndirect( &stLB );
\r
3071 holdbrush = SelectObject( hdc, hbrush );
\r
3073 DrawArrowBetweenPoints( hdc, s_x, s_y, d_x, d_y );
\r
3075 SelectObject( hdc, holdpen );
\r
3076 SelectObject( hdc, holdbrush );
\r
3077 DeleteObject( hpen );
\r
3078 DeleteObject( hbrush );
\r
3081 BOOL HasHighlightInfo()
\r
3083 BOOL result = FALSE;
\r
3085 if( highlightInfo.sq[0].x >= 0 && highlightInfo.sq[0].y >= 0 &&
\r
3086 highlightInfo.sq[1].x >= 0 && highlightInfo.sq[1].y >= 0 )
\r
3094 BOOL IsDrawArrowEnabled()
\r
3096 BOOL result = FALSE;
\r
3098 if( appData.highlightMoveWithArrow && squareSize >= 32 ) {
\r
3105 VOID DrawArrowHighlight( HDC hdc )
\r
3107 if( IsDrawArrowEnabled() && HasHighlightInfo() ) {
\r
3108 DrawArrowBetweenSquares( hdc,
\r
3109 highlightInfo.sq[0].x, highlightInfo.sq[0].y,
\r
3110 highlightInfo.sq[1].x, highlightInfo.sq[1].y );
\r
3114 HRGN GetArrowHighlightClipRegion( HDC hdc )
\r
3116 HRGN result = NULL;
\r
3118 if( HasHighlightInfo() ) {
\r
3119 int x1, y1, x2, y2;
\r
3120 int sx, sy, dx, dy;
\r
3122 SquareToPos(highlightInfo.sq[0].y, highlightInfo.sq[0].x, &x1, &y1 );
\r
3123 SquareToPos(highlightInfo.sq[1].y, highlightInfo.sq[1].x, &x2, &y2 );
\r
3125 sx = MIN( x1, x2 );
\r
3126 sy = MIN( y1, y2 );
\r
3127 dx = MAX( x1, x2 ) + squareSize;
\r
3128 dy = MAX( y1, y2 ) + squareSize;
\r
3130 result = CreateRectRgn( sx, sy, dx, dy );
\r
3137 Warning: this function modifies the behavior of several other functions.
\r
3139 Basically, Winboard is optimized to avoid drawing the whole board if not strictly
\r
3140 needed. Unfortunately, the decision whether or not to perform a full or partial
\r
3141 repaint is scattered all over the place, which is not good for features such as
\r
3142 "arrow highlighting" that require a full repaint of the board.
\r
3144 So, I've tried to patch the code where I thought it made sense (e.g. after or during
\r
3145 user interaction, when speed is not so important) but especially to avoid errors
\r
3146 in the displayed graphics.
\r
3148 In such patched places, I always try refer to this function so there is a single
\r
3149 place to maintain knowledge.
\r
3151 To restore the original behavior, just return FALSE unconditionally.
\r
3153 BOOL IsFullRepaintPreferrable()
\r
3155 BOOL result = FALSE;
\r
3157 if( (appData.highlightLastMove || appData.highlightDragging) && IsDrawArrowEnabled() ) {
\r
3158 /* Arrow may appear on the board */
\r
3166 This function is called by DrawPosition to know whether a full repaint must
\r
3169 Only DrawPosition may directly call this function, which makes use of
\r
3170 some state information. Other function should call DrawPosition specifying
\r
3171 the repaint flag, and can use IsFullRepaintPreferrable if needed.
\r
3173 BOOL DrawPositionNeedsFullRepaint()
\r
3175 BOOL result = FALSE;
\r
3178 Probably a slightly better policy would be to trigger a full repaint
\r
3179 when animInfo.piece changes state (i.e. empty -> non-empty and viceversa),
\r
3180 but animation is fast enough that it's difficult to notice.
\r
3182 if( animInfo.piece == EmptySquare ) {
\r
3183 if( (appData.highlightLastMove || appData.highlightDragging) && IsDrawArrowEnabled() /*&& HasHighlightInfo()*/ ) {
\r
3192 DrawBoardOnDC(HDC hdc, Board board, HDC tmphdc)
\r
3194 int row, column, x, y, square_color, piece_color;
\r
3195 ChessSquare piece;
\r
3197 HDC texture_hdc = NULL;
\r
3199 /* [AS] Initialize background textures if needed */
\r
3200 if( liteBackTexture != NULL || darkBackTexture != NULL ) {
\r
3201 static int backTextureBoardSize; /* [HGM] boardsize: also new texture if board format changed */
\r
3202 if( backTextureSquareSize != squareSize
\r
3203 || backTextureBoardSize != BOARD_WIDTH+BOARD_FILES*BOARD_HEIGHT) {
\r
3204 backTextureBoardSize = BOARD_WIDTH+BOARD_FILES*BOARD_HEIGHT;
\r
3205 backTextureSquareSize = squareSize;
\r
3206 RebuildTextureSquareInfo();
\r
3209 texture_hdc = CreateCompatibleDC( hdc );
\r
3212 for (row = 0; row < BOARD_HEIGHT; row++) {
\r
3213 for (column = 0; column < BOARD_WIDTH; column++) {
\r
3215 SquareToPos(row, column, &x, &y);
\r
3217 piece = board[row][column];
\r
3219 square_color = ((column + row) % 2) == 1;
\r
3220 if( gameInfo.variant == VariantXiangqi ) {
\r
3221 square_color = !InPalace(row, column);
\r
3222 if(BOARD_HEIGHT&1) { if(row==BOARD_HEIGHT/2) square_color ^= 1; }
\r
3223 else if(row < BOARD_HEIGHT/2) square_color ^= 1;
\r
3225 piece_color = (int) piece < (int) BlackPawn;
\r
3228 /* [HGM] holdings file: light square or black */
\r
3229 if(column == BOARD_LEFT-2) {
\r
3230 if( row > BOARD_HEIGHT - gameInfo.holdingsSize - 1 )
\r
3233 DisplayHoldingsCount(hdc, x, y, 0, 0); /* black out */
\r
3237 if(column == BOARD_RGHT + 1 ) {
\r
3238 if( row < gameInfo.holdingsSize )
\r
3241 DisplayHoldingsCount(hdc, x, y, 0, 0);
\r
3245 if(column == BOARD_LEFT-1 ) /* left align */
\r
3246 DisplayHoldingsCount(hdc, x, y, flipView, (int) board[row][column]);
\r
3247 else if( column == BOARD_RGHT) /* right align */
\r
3248 DisplayHoldingsCount(hdc, x, y, !flipView, (int) board[row][column]);
\r
3250 if (appData.monoMode) {
\r
3251 if (piece == EmptySquare) {
\r
3252 BitBlt(hdc, x, y, squareSize, squareSize, 0, 0, 0,
\r
3253 square_color ? WHITENESS : BLACKNESS);
\r
3255 DrawPieceOnDC(hdc, piece, piece_color, square_color, x, y, tmphdc);
\r
3258 else if( backTextureSquareInfo[row][column].mode > 0 ) {
\r
3259 /* [AS] Draw the square using a texture bitmap */
\r
3260 HBITMAP hbm = SelectObject( texture_hdc, square_color ? liteBackTexture : darkBackTexture );
\r
3261 int r = row, c = column; // [HGM] do not flip board in flipView
\r
3262 if(flipView) { r = BOARD_HEIGHT-1 - r; c = BOARD_WIDTH-1 - c; }
\r
3265 squareSize, squareSize,
\r
3268 backTextureSquareInfo[r][c].mode,
\r
3269 backTextureSquareInfo[r][c].x,
\r
3270 backTextureSquareInfo[r][c].y );
\r
3272 SelectObject( texture_hdc, hbm );
\r
3274 if (piece != EmptySquare) {
\r
3275 DrawPieceOnDC(hdc, piece, piece_color, -1, x, y, tmphdc);
\r
3279 HBRUSH brush = square_color ? lightSquareBrush : darkSquareBrush;
\r
3281 oldBrush = SelectObject(hdc, brush );
\r
3282 BitBlt(hdc, x, y, squareSize, squareSize, 0, 0, 0, PATCOPY);
\r
3283 SelectObject(hdc, oldBrush);
\r
3284 if (piece != EmptySquare)
\r
3285 DrawPieceOnDC(hdc, piece, piece_color, -1, x, y, tmphdc);
\r
3290 if( texture_hdc != NULL ) {
\r
3291 DeleteDC( texture_hdc );
\r
3295 int saveDiagFlag = 0; FILE *diagFile; // [HGM] diag
\r
3296 void fputDW(FILE *f, int x)
\r
3298 fputc(x & 255, f);
\r
3299 fputc(x>>8 & 255, f);
\r
3300 fputc(x>>16 & 255, f);
\r
3301 fputc(x>>24 & 255, f);
\r
3304 #define MAX_CLIPS 200 /* more than enough */
\r
3307 DrawLogoOnDC(HDC hdc, RECT logoRect, HBITMAP logo)
\r
3309 // HBITMAP bufferBitmap;
\r
3314 int w = 100, h = 50;
\r
3316 if(logo == NULL) return;
\r
3317 // GetClientRect(hwndMain, &Rect);
\r
3318 // bufferBitmap = CreateCompatibleBitmap(hdc, Rect.right-Rect.left+1,
\r
3319 // Rect.bottom-Rect.top+1);
\r
3320 tmphdc = CreateCompatibleDC(hdc);
\r
3321 hbm = SelectObject(tmphdc, logo);
\r
3322 if( GetObject( logo, sizeof(bi), &bi ) > 0 ) {
\r
3326 StretchBlt(hdc, logoRect.left, logoRect.top, logoRect.right - logoRect.left,
\r
3327 logoRect.bottom - logoRect.top, tmphdc, 0, 0, w, h, SRCCOPY);
\r
3328 SelectObject(tmphdc, hbm);
\r
3332 static HDC hdcSeek;
\r
3334 // [HGM] seekgraph
\r
3335 void DrawSeekAxis( int x, int y, int xTo, int yTo )
\r
3338 HPEN hp = SelectObject( hdcSeek, gridPen );
\r
3339 MoveToEx( hdcSeek, boardRect.left+x, boardRect.top+y, &stPt );
\r
3340 LineTo( hdcSeek, boardRect.left+xTo, boardRect.top+yTo );
\r
3341 SelectObject( hdcSeek, hp );
\r
3344 // front-end wrapper for drawing functions to do rectangles
\r
3345 void DrawSeekBackground( int left, int top, int right, int bottom )
\r
3350 if (hdcSeek == NULL) {
\r
3351 hdcSeek = GetDC(hwndMain);
\r
3352 if (!appData.monoMode) {
\r
3353 SelectPalette(hdcSeek, hPal, FALSE);
\r
3354 RealizePalette(hdcSeek);
\r
3357 hp = SelectObject( hdcSeek, gridPen );
\r
3358 rc.top = boardRect.top+top; rc.left = boardRect.left+left;
\r
3359 rc.bottom = boardRect.top+bottom; rc.right = boardRect.left+right;
\r
3360 FillRect( hdcSeek, &rc, lightSquareBrush );
\r
3361 SelectObject( hdcSeek, hp );
\r
3364 // front-end wrapper for putting text in graph
\r
3365 void DrawSeekText(char *buf, int x, int y)
\r
3368 SetBkMode( hdcSeek, TRANSPARENT );
\r
3369 GetTextExtentPoint32( hdcSeek, buf, strlen(buf), &stSize );
\r
3370 TextOut( hdcSeek, boardRect.left+x-3, boardRect.top+y-stSize.cy/2, buf, strlen(buf) );
\r
3373 void DrawSeekDot(int x, int y, int color)
\r
3375 int square = color & 0x80;
\r
3376 HBRUSH oldBrush = SelectObject(hdcSeek,
\r
3377 color == 0 ? markerBrush : color == 1 ? darkSquareBrush : explodeBrush);
\r
3380 Rectangle(hdcSeek, boardRect.left+x - squareSize/9, boardRect.top+y - squareSize/9,
\r
3381 boardRect.left+x + squareSize/9, boardRect.top+y + squareSize/9);
\r
3383 Ellipse(hdcSeek, boardRect.left+x - squareSize/8, boardRect.top+y - squareSize/8,
\r
3384 boardRect.left+x + squareSize/8, boardRect.top+y + squareSize/8);
\r
3385 SelectObject(hdcSeek, oldBrush);
\r
3389 HDCDrawPosition(HDC hdc, BOOLEAN repaint, Board board)
\r
3391 static Board lastReq[2], lastDrawn[2];
\r
3392 static HighlightInfo lastDrawnHighlight, lastDrawnPremove;
\r
3393 static int lastDrawnFlipView = 0;
\r
3394 static int lastReqValid[2] = {0, 0}, lastDrawnValid[2] = {0, 0};
\r
3395 int releaseDC, x, y, x2, y2, row, column, num_clips = 0, i;
\r
3398 HBITMAP bufferBitmap;
\r
3399 HBITMAP oldBitmap;
\r
3401 HRGN clips[MAX_CLIPS];
\r
3402 ChessSquare dragged_piece = EmptySquare;
\r
3403 int nr = twoBoards*partnerUp;
\r
3405 /* I'm undecided on this - this function figures out whether a full
\r
3406 * repaint is necessary on its own, so there's no real reason to have the
\r
3407 * caller tell it that. I think this can safely be set to FALSE - but
\r
3408 * if we trust the callers not to request full repaints unnessesarily, then
\r
3409 * we could skip some clipping work. In other words, only request a full
\r
3410 * redraw when the majority of pieces have changed positions (ie. flip,
\r
3411 * gamestart and similar) --Hawk
\r
3413 Boolean fullrepaint = repaint;
\r
3415 if(DrawSeekGraph()) return; // [HG} seekgraph: suppress printing board if seek graph up
\r
3417 if( DrawPositionNeedsFullRepaint() ) {
\r
3418 fullrepaint = TRUE;
\r
3421 if (board == NULL) {
\r
3422 if (!lastReqValid[nr]) {
\r
3425 board = lastReq[nr];
\r
3427 CopyBoard(lastReq[nr], board);
\r
3428 lastReqValid[nr] = 1;
\r
3431 if (doingSizing) {
\r
3435 if (IsIconic(hwndMain)) {
\r
3439 if (hdc == NULL) {
\r
3440 hdc = GetDC(hwndMain);
\r
3441 if (!appData.monoMode) {
\r
3442 SelectPalette(hdc, hPal, FALSE);
\r
3443 RealizePalette(hdc);
\r
3447 releaseDC = FALSE;
\r
3450 /* Create some work-DCs */
\r
3451 hdcmem = CreateCompatibleDC(hdc);
\r
3452 tmphdc = CreateCompatibleDC(hdc);
\r
3454 /* If dragging is in progress, we temporarely remove the piece */
\r
3455 /* [HGM] or temporarily decrease count if stacked */
\r
3456 /* !! Moved to before board compare !! */
\r
3457 if (dragInfo.from.x >= 0 && dragInfo.pos.x >= 0) {
\r
3458 dragged_piece = board[dragInfo.from.y][dragInfo.from.x];
\r
3459 if(dragInfo.from.x == BOARD_LEFT-2 ) {
\r
3460 if(--board[dragInfo.from.y][dragInfo.from.x+1] == 0 )
\r
3461 board[dragInfo.from.y][dragInfo.from.x] = EmptySquare;
\r
3463 if(dragInfo.from.x == BOARD_RGHT+1) {
\r
3464 if(--board[dragInfo.from.y][dragInfo.from.x-1] == 0 )
\r
3465 board[dragInfo.from.y][dragInfo.from.x] = EmptySquare;
\r
3467 board[dragInfo.from.y][dragInfo.from.x] = EmptySquare;
\r
3470 /* Figure out which squares need updating by comparing the
\r
3471 * newest board with the last drawn board and checking if
\r
3472 * flipping has changed.
\r
3474 if (!fullrepaint && lastDrawnValid[nr] && (nr == 1 || lastDrawnFlipView == flipView)) {
\r
3475 for (row = 0; row < BOARD_HEIGHT; row++) { /* [HGM] true size, not 8 */
\r
3476 for (column = 0; column < BOARD_WIDTH; column++) {
\r
3477 if (lastDrawn[nr][row][column] != board[row][column]) {
\r
3478 SquareToPos(row, column, &x, &y);
\r
3479 clips[num_clips++] =
\r
3480 CreateRectRgn(x, y, x + squareSize, y + squareSize);
\r
3484 if(nr == 0) { // [HGM] dual: no highlights on second board
\r
3485 for (i=0; i<2; i++) {
\r
3486 if (lastDrawnHighlight.sq[i].x != highlightInfo.sq[i].x ||
\r
3487 lastDrawnHighlight.sq[i].y != highlightInfo.sq[i].y) {
\r
3488 if (lastDrawnHighlight.sq[i].x >= 0 &&
\r
3489 lastDrawnHighlight.sq[i].y >= 0) {
\r
3490 SquareToPos(lastDrawnHighlight.sq[i].y,
\r
3491 lastDrawnHighlight.sq[i].x, &x, &y);
\r
3492 clips[num_clips++] =
\r
3493 CreateRectRgn(x - lineGap, y - lineGap,
\r
3494 x + squareSize + lineGap, y + squareSize + lineGap);
\r
3496 if (highlightInfo.sq[i].x >= 0 && highlightInfo.sq[i].y >= 0) {
\r
3497 SquareToPos(highlightInfo.sq[i].y, highlightInfo.sq[i].x, &x, &y);
\r
3498 clips[num_clips++] =
\r
3499 CreateRectRgn(x - lineGap, y - lineGap,
\r
3500 x + squareSize + lineGap, y + squareSize + lineGap);
\r
3504 for (i=0; i<2; i++) {
\r
3505 if (lastDrawnPremove.sq[i].x != premoveHighlightInfo.sq[i].x ||
\r
3506 lastDrawnPremove.sq[i].y != premoveHighlightInfo.sq[i].y) {
\r
3507 if (lastDrawnPremove.sq[i].x >= 0 &&
\r
3508 lastDrawnPremove.sq[i].y >= 0) {
\r
3509 SquareToPos(lastDrawnPremove.sq[i].y,
\r
3510 lastDrawnPremove.sq[i].x, &x, &y);
\r
3511 clips[num_clips++] =
\r
3512 CreateRectRgn(x - lineGap, y - lineGap,
\r
3513 x + squareSize + lineGap, y + squareSize + lineGap);
\r
3515 if (premoveHighlightInfo.sq[i].x >= 0 &&
\r
3516 premoveHighlightInfo.sq[i].y >= 0) {
\r
3517 SquareToPos(premoveHighlightInfo.sq[i].y,
\r
3518 premoveHighlightInfo.sq[i].x, &x, &y);
\r
3519 clips[num_clips++] =
\r
3520 CreateRectRgn(x - lineGap, y - lineGap,
\r
3521 x + squareSize + lineGap, y + squareSize + lineGap);
\r
3525 } else { // nr == 1
\r
3526 partnerHighlightInfo.sq[0].y = board[EP_STATUS-4];
\r
3527 partnerHighlightInfo.sq[0].x = board[EP_STATUS-3];
\r
3528 partnerHighlightInfo.sq[1].y = board[EP_STATUS-2];
\r
3529 partnerHighlightInfo.sq[1].x = board[EP_STATUS-1];
\r
3530 for (i=0; i<2; i++) {
\r
3531 if (partnerHighlightInfo.sq[i].x >= 0 &&
\r
3532 partnerHighlightInfo.sq[i].y >= 0) {
\r
3533 SquareToPos(partnerHighlightInfo.sq[i].y,
\r
3534 partnerHighlightInfo.sq[i].x, &x, &y);
\r
3535 clips[num_clips++] =
\r
3536 CreateRectRgn(x - lineGap, y - lineGap,
\r
3537 x + squareSize + lineGap, y + squareSize + lineGap);
\r
3539 if (oldPartnerHighlight.sq[i].x >= 0 &&
\r
3540 oldPartnerHighlight.sq[i].y >= 0) {
\r
3541 SquareToPos(oldPartnerHighlight.sq[i].y,
\r
3542 oldPartnerHighlight.sq[i].x, &x, &y);
\r
3543 clips[num_clips++] =
\r
3544 CreateRectRgn(x - lineGap, y - lineGap,
\r
3545 x + squareSize + lineGap, y + squareSize + lineGap);
\r
3550 fullrepaint = TRUE;
\r
3553 /* Create a buffer bitmap - this is the actual bitmap
\r
3554 * being written to. When all the work is done, we can
\r
3555 * copy it to the real DC (the screen). This avoids
\r
3556 * the problems with flickering.
\r
3558 GetClientRect(hwndMain, &Rect);
\r
3559 bufferBitmap = CreateCompatibleBitmap(hdc, Rect.right-Rect.left+1,
\r
3560 Rect.bottom-Rect.top+1);
\r
3561 oldBitmap = SelectObject(hdcmem, bufferBitmap);
\r
3562 if (!appData.monoMode) {
\r
3563 SelectPalette(hdcmem, hPal, FALSE);
\r
3566 /* Create clips for dragging */
\r
3567 if (!fullrepaint) {
\r
3568 if (dragInfo.from.x >= 0) {
\r
3569 SquareToPos(dragInfo.from.y, dragInfo.from.x, &x, &y);
\r
3570 clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);
\r
3572 if (dragInfo.start.x >= 0) {
\r
3573 SquareToPos(dragInfo.start.y, dragInfo.start.x, &x, &y);
\r
3574 clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);
\r
3576 if (dragInfo.pos.x >= 0) {
\r
3577 x = dragInfo.pos.x - squareSize / 2;
\r
3578 y = dragInfo.pos.y - squareSize / 2;
\r
3579 clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);
\r
3581 if (dragInfo.lastpos.x >= 0) {
\r
3582 x = dragInfo.lastpos.x - squareSize / 2;
\r
3583 y = dragInfo.lastpos.y - squareSize / 2;
\r
3584 clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);
\r
3588 /* Are we animating a move?
\r
3590 * - remove the piece from the board (temporarely)
\r
3591 * - calculate the clipping region
\r
3593 if (!fullrepaint) {
\r
3594 if (animInfo.piece != EmptySquare) {
\r
3595 board[animInfo.from.y][animInfo.from.x] = EmptySquare;
\r
3596 x = boardRect.left + animInfo.lastpos.x;
\r
3597 y = boardRect.top + animInfo.lastpos.y;
\r
3598 x2 = boardRect.left + animInfo.pos.x;
\r
3599 y2 = boardRect.top + animInfo.pos.y;
\r
3600 clips[num_clips++] = CreateRectRgn(MIN(x,x2), MIN(y,y2), MAX(x,x2)+squareSize, MAX(y,y2)+squareSize);
\r
3601 /* Slight kludge. The real problem is that after AnimateMove is
\r
3602 done, the position on the screen does not match lastDrawn.
\r
3603 This currently causes trouble only on e.p. captures in
\r
3604 atomic, where the piece moves to an empty square and then
\r
3605 explodes. The old and new positions both had an empty square
\r
3606 at the destination, but animation has drawn a piece there and
\r
3607 we have to remember to erase it. [HGM] moved until after setting lastDrawn */
\r
3608 lastDrawn[0][animInfo.to.y][animInfo.to.x] = animInfo.piece;
\r
3612 /* No clips? Make sure we have fullrepaint set to TRUE */
\r
3613 if (num_clips == 0)
\r
3614 fullrepaint = TRUE;
\r
3616 /* Set clipping on the memory DC */
\r
3617 if (!fullrepaint) {
\r
3618 SelectClipRgn(hdcmem, clips[0]);
\r
3619 for (x = 1; x < num_clips; x++) {
\r
3620 if (ExtSelectClipRgn(hdcmem, clips[x], RGN_OR) == ERROR)
\r
3621 abort(); // this should never ever happen!
\r
3625 /* Do all the drawing to the memory DC */
\r
3626 if(explodeInfo.radius) { // [HGM] atomic
\r
3628 int x, y, r=(explodeInfo.radius * squareSize)/100;
\r
3629 board[explodeInfo.fromY][explodeInfo.fromX] = EmptySquare; // suppress display of capturer
\r
3630 SquareToPos(explodeInfo.toY, explodeInfo.toX, &x, &y);
\r
3631 x += squareSize/2;
\r
3632 y += squareSize/2;
\r
3633 if(!fullrepaint) {
\r
3634 clips[num_clips] = CreateRectRgn(x-r, y-r, x+r, y+r);
\r
3635 ExtSelectClipRgn(hdcmem, clips[num_clips++], RGN_OR);
\r
3637 DrawGridOnDC(hdcmem);
\r
3638 DrawHighlightsOnDC(hdcmem, &highlightInfo, HIGHLIGHT_PEN);
\r
3639 DrawHighlightsOnDC(hdcmem, &premoveHighlightInfo, PREMOVE_PEN);
\r
3640 DrawBoardOnDC(hdcmem, board, tmphdc);
\r
3641 oldBrush = SelectObject(hdcmem, explodeBrush);
\r
3642 Ellipse(hdcmem, x-r, y-r, x+r, y+r);
\r
3643 SelectObject(hdcmem, oldBrush);
\r
3645 DrawGridOnDC(hdcmem);
\r
3646 if(nr == 0) { // [HGM] dual: decide which highlights to draw
\r
3647 DrawHighlightsOnDC(hdcmem, &highlightInfo, HIGHLIGHT_PEN);
\r
3648 DrawHighlightsOnDC(hdcmem, &premoveHighlightInfo, PREMOVE_PEN);
\r
3650 DrawHighlightsOnDC(hdcmem, &partnerHighlightInfo, HIGHLIGHT_PEN);
\r
3651 oldPartnerHighlight = partnerHighlightInfo;
\r
3653 DrawBoardOnDC(hdcmem, board, tmphdc);
\r
3655 if(nr == 0) // [HGM] dual: markers only on left board
\r
3656 for (row = 0; row < BOARD_HEIGHT; row++) {
\r
3657 for (column = 0; column < BOARD_WIDTH; column++) {
\r
3658 if (marker[row][column]) { // marker changes only occur with full repaint!
\r
3659 HBRUSH oldBrush = SelectObject(hdcmem,
\r
3660 marker[row][column] == 2 ? markerBrush : explodeBrush);
\r
3661 SquareToPos(row, column, &x, &y);
\r
3662 Ellipse(hdcmem, x + squareSize/4, y + squareSize/4,
\r
3663 x + 3*squareSize/4, y + 3*squareSize/4);
\r
3664 SelectObject(hdcmem, oldBrush);
\r
3669 HBITMAP whiteLogo = (HBITMAP) first.programLogo, blackLogo = (HBITMAP) second.programLogo;
\r
3670 if(appData.autoLogo) {
\r
3672 switch(gameMode) { // pick logos based on game mode
\r
3673 case IcsObserving:
\r
3674 whiteLogo = second.programLogo; // ICS logo
\r
3675 blackLogo = second.programLogo;
\r
3678 case IcsPlayingWhite:
\r
3679 if(!appData.zippyPlay) whiteLogo = userLogo;
\r