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, 2011 Free Software Foundation, Inc.
\r
10 * Enhancements Copyright 2005 Alessandro Scotti
\r
12 * XBoard borrows its colors and the bitmaps.xchess bitmap set from XChess,
\r
13 * which was written and is copyrighted by Wayne Christopher.
\r
15 * The following terms apply to Digital Equipment Corporation's copyright
\r
16 * interest in XBoard:
\r
17 * ------------------------------------------------------------------------
\r
18 * All Rights Reserved
\r
20 * Permission to use, copy, modify, and distribute this software and its
\r
21 * documentation for any purpose and without fee is hereby granted,
\r
22 * provided that the above copyright notice appear in all copies and that
\r
23 * both that copyright notice and this permission notice appear in
\r
24 * supporting documentation, and that the name of Digital not be
\r
25 * used in advertising or publicity pertaining to distribution of the
\r
26 * software without specific, written prior permission.
\r
28 * DIGITAL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
\r
29 * ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL
\r
30 * DIGITAL BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
\r
31 * ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
\r
32 * WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
\r
33 * ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
\r
35 * ------------------------------------------------------------------------
\r
37 * The following terms apply to the enhanced version of XBoard
\r
38 * distributed by the Free Software Foundation:
\r
39 * ------------------------------------------------------------------------
\r
41 * GNU XBoard is free software: you can redistribute it and/or modify
\r
42 * it under the terms of the GNU General Public License as published by
\r
43 * the Free Software Foundation, either version 3 of the License, or (at
\r
44 * your option) any later version.
\r
46 * GNU XBoard is distributed in the hope that it will be useful, but
\r
47 * WITHOUT ANY WARRANTY; without even the implied warranty of
\r
48 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
\r
49 * General Public License for more details.
\r
51 * You should have received a copy of the GNU General Public License
\r
52 * along with this program. If not, see http://www.gnu.org/licenses/. *
\r
54 *------------------------------------------------------------------------
\r
55 ** See the file ChangeLog for a revision history. */
\r
59 #include <windows.h>
\r
60 #include <winuser.h>
\r
61 #include <winsock.h>
\r
62 #include <commctrl.h>
\r
68 #include <sys/stat.h>
\r
71 #include <commdlg.h>
\r
73 #include <richedit.h>
\r
74 #include <mmsystem.h>
\r
83 #include "frontend.h"
\r
84 #include "backend.h"
\r
85 #include "winboard.h"
\r
87 #include "wclipbrd.h"
\r
88 #include "woptions.h"
\r
89 #include "wsockerr.h"
\r
90 #include "defaults.h"
\r
94 //void InitEngineUCI( const char * iniDir, ChessProgramState * cps );
\r
97 void mysrandom(unsigned int seed);
\r
99 extern int whiteFlag, blackFlag;
\r
100 Boolean flipClock = FALSE;
\r
101 extern HANDLE chatHandle[];
\r
102 extern int ics_type;
\r
104 void DisplayHoldingsCount(HDC hdc, int x, int y, int align, int copyNumber);
\r
105 VOID NewVariantPopup(HWND hwnd);
\r
106 int FinishMove P((ChessMove moveType, int fromX, int fromY, int toX, int toY,
\r
107 /*char*/int promoChar));
\r
108 void DisplayMove P((int moveNumber));
\r
109 Boolean ParseFEN P((Board board, int *blackPlaysFirst, char *fen));
\r
110 void ChatPopUp P((char *s));
\r
112 ChessSquare piece;
\r
113 POINT pos; /* window coordinates of current pos */
\r
114 POINT lastpos; /* window coordinates of last pos - used for clipping */
\r
115 POINT from; /* board coordinates of the piece's orig pos */
\r
116 POINT to; /* board coordinates of the piece's new pos */
\r
119 static AnimInfo animInfo = { EmptySquare, {-1,-1}, {-1,-1}, {-1,-1} };
\r
122 POINT start; /* window coordinates of start pos */
\r
123 POINT pos; /* window coordinates of current pos */
\r
124 POINT lastpos; /* window coordinates of last pos - used for clipping */
\r
125 POINT from; /* board coordinates of the piece's orig pos */
\r
129 static DragInfo dragInfo = { {-1,-1}, {-1,-1}, {-1,-1}, {-1,-1}, EmptySquare };
\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 static HWND savedHwnd;
\r
191 HWND hCommPort = NULL; /* currently open comm port */
\r
192 static HWND hwndPause; /* pause button */
\r
193 static HBITMAP pieceBitmap[3][(int) BlackPawn]; /* [HGM] nr of bitmaps referred to bP in stead of wK */
\r
194 static HBRUSH lightSquareBrush, darkSquareBrush,
\r
195 blackSquareBrush, /* [HGM] for band between board and holdings */
\r
196 explodeBrush, /* [HGM] atomic */
\r
197 markerBrush, /* [HGM] markers */
\r
198 whitePieceBrush, blackPieceBrush, iconBkgndBrush /*, outlineBrush*/;
\r
199 static POINT gridEndpoints[(BOARD_RANKS + BOARD_FILES + 2) * 2];
\r
200 static DWORD gridVertexCounts[BOARD_RANKS + BOARD_FILES + 2];
\r
201 static HPEN gridPen = NULL;
\r
202 static HPEN highlightPen = NULL;
\r
203 static HPEN premovePen = NULL;
\r
204 static NPLOGPALETTE pLogPal;
\r
205 static BOOL paletteChanged = FALSE;
\r
206 static HICON iconWhite, iconBlack, iconCurrent;
\r
207 static int doingSizing = FALSE;
\r
208 static int lastSizing = 0;
\r
209 static int prevStderrPort;
\r
210 static HBITMAP userLogo;
\r
212 static HBITMAP liteBackTexture = NULL;
\r
213 static HBITMAP darkBackTexture = NULL;
\r
214 static int liteBackTextureMode = BACK_TEXTURE_MODE_PLAIN;
\r
215 static int darkBackTextureMode = BACK_TEXTURE_MODE_PLAIN;
\r
216 static int backTextureSquareSize = 0;
\r
217 static struct { int x; int y; int mode; } backTextureSquareInfo[BOARD_RANKS][BOARD_FILES];
\r
219 #if __GNUC__ && !defined(_winmajor)
\r
220 #define oldDialog 0 /* cygwin doesn't define _winmajor; mingw does */
\r
222 #if defined(_winmajor)
\r
223 #define oldDialog (_winmajor < 4)
\r
225 #define oldDialog 0
\r
229 #define INTERNATIONAL
\r
231 #ifdef INTERNATIONAL
\r
232 # define _(s) T_(s)
\r
238 # define Translate(x, y)
\r
239 # define LoadLanguageFile(s)
\r
242 #ifdef INTERNATIONAL
\r
244 Boolean barbaric; // flag indicating if translation is needed
\r
246 // list of item numbers used in each dialog (used to alter language at run time)
\r
248 #define ABOUTBOX -1 /* not sure why these are needed */
\r
249 #define ABOUTBOX2 -1
\r
251 int dialogItems[][40] = {
\r
252 { ABOUTBOX, IDOK, OPT_MESS, 400 },
\r
253 { DLG_TimeControl, IDC_Babble, OPT_TCUseMoves, OPT_TCUseInc, OPT_TCUseFixed,
\r
254 OPT_TCtext1, OPT_TCtext2, OPT_TCitext1, OPT_TCitext2, OPT_TCftext, GPB_Factors, IDC_Factor1, IDC_Factor2, IDOK, IDCANCEL },
\r
255 { DLG_LoadOptions, OPT_Autostep, OPT_AStext1, IDOK, IDCANCEL },
\r
256 { DLG_SaveOptions, OPT_Autosave, OPT_AVPrompt, OPT_AVToFile, OPT_AVBrowse,
\r
257 801, OPT_PGN, OPT_Old, OPT_OutOfBookInfo, IDOK, IDCANCEL },
\r
258 { 1536, 1090, IDC_Directories, 1089, 1091, IDOK, IDCANCEL, 1038, IDC_IndexNr, 1037 },
\r
259 { DLG_CommPort, IDOK, IDCANCEL, IDC_Port, IDC_Rate, IDC_Bits, IDC_Parity,
\r
260 IDC_Stop, IDC_Flow, OPT_SerialHelp },
\r
261 { DLG_EditComment, IDOK, OPT_CancelComment, OPT_ClearComment, OPT_EditComment },
\r
262 { DLG_PromotionKing, PB_Chancellor, PB_Archbishop, PB_Queen, PB_Rook,
\r
263 PB_Bishop, PB_Knight, PB_King, IDCANCEL, IDC_Yes, IDC_No, IDC_Centaur },
\r
264 { ABOUTBOX2, IDC_ChessBoard },
\r
265 { DLG_GameList, OPT_GameListLoad, OPT_GameListPrev, OPT_GameListNext,
\r
266 OPT_GameListClose, IDC_GameListDoFilter },
\r
267 { DLG_EditTags, IDOK, OPT_TagsCancel, OPT_EditTags },
\r
268 { DLG_Error, IDOK },
\r
269 { DLG_Colorize, IDOK, IDCANCEL, OPT_ChooseColor, OPT_Bold, OPT_Italic,
\r
270 OPT_Underline, OPT_Strikeout, OPT_Sample },
\r
271 { DLG_Question, IDOK, IDCANCEL, OPT_QuestionText },
\r
272 { DLG_Startup, IDC_Welcome, OPT_ChessEngine, OPT_ChessServer, OPT_View,
\r
273 IDC_SPECIFY_ENG_STATIC, IDC_SPECIFY_SERVER_STATIC, OPT_AnyAdditional,
\r
274 IDOK, IDCANCEL, IDM_HELPCONTENTS },
\r
275 { DLG_IndexNumber, IDC_Index },
\r
276 { DLG_TypeInMove, IDOK, IDCANCEL },
\r
277 { DLG_TypeInName, IDOK, IDCANCEL },
\r
278 { DLG_Sound, IDC_Event, OPT_NoSound, OPT_DefaultBeep, OPT_BuiltInSound,
\r
279 OPT_WavFile, OPT_BrowseSound, OPT_DefaultSounds, IDOK, IDCANCEL, OPT_PlaySound },
\r
280 { DLG_GeneralOptions, IDOK, IDCANCEL, OPT_AlwaysOnTop, OPT_HighlightLastMove,
\r
281 OPT_AlwaysQueen, OPT_PeriodicUpdates, OPT_AnimateDragging, OPT_PonderNextMove,
\r
282 OPT_AnimateMoving, OPT_PopupExitMessage, OPT_AutoFlag, OPT_PopupMoveErrors,
\r
283 OPT_AutoFlipView, OPT_ShowButtonBar, OPT_AutoRaiseBoard, OPT_ShowCoordinates,
\r
284 OPT_Blindfold, OPT_ShowThinking, OPT_HighlightDragging, OPT_TestLegality,
\r
285 OPT_SaveExtPGN, OPT_HideThinkFromHuman, OPT_ExtraInfoInMoveHistory,
\r
286 OPT_HighlightMoveArrow, OPT_AutoLogo ,OPT_SmartMove },
\r
287 { DLG_IcsOptions, IDOK, IDCANCEL, OPT_AutoComment, OPT_AutoKibitz, OPT_AutoObserve,
\r
288 OPT_GetMoveList, OPT_LocalLineEditing, OPT_QuietPlay, OPT_SeekGraph, OPT_AutoRefresh,
\r
289 OPT_BgObserve, OPT_DualBoard, OPT_Premove, OPT_PremoveWhite, OPT_PremoveBlack,
\r
290 OPT_SmartMove, OPT_IcsAlarm, IDC_Sec, OPT_ChooseShoutColor, OPT_ChooseSShoutColor,
\r
291 OPT_ChooseChannel1Color, OPT_ChooseChannelColor, OPT_ChooseKibitzColor,
\r
292 OPT_ChooseTellColor, OPT_ChooseChallengeColor, OPT_ChooseRequestColor,
\r
293 OPT_ChooseSeekColor, OPT_ChooseNormalColor, OPT_ChooseBackgroundColor,
\r
294 OPT_DefaultColors, OPT_DontColorize, IDC_Boxes, GPB_Colors, GPB_Premove,
\r
295 GPB_General, GPB_Alarm },
\r
296 { DLG_BoardOptions, IDOK, IDCANCEL, OPT_SizeTiny, OPT_SizeTeeny, OPT_SizeDinky,
\r
297 OPT_SizePetite, OPT_SizeSlim, OPT_SizeSmall, OPT_SizeMediocre, OPT_SizeMiddling,
\r
298 OPT_SizeAverage, OPT_SizeModerate, OPT_SizeMedium, OPT_SizeBulky, OPT_SizeLarge,
\r
299 OPT_SizeBig, OPT_SizeHuge, OPT_SizeGiant, OPT_SizeColossal, OPT_SizeTitanic,
\r
300 OPT_ChooseLightSquareColor, OPT_ChooseDarkSquareColor, OPT_ChooseWhitePieceColor,
\r
301 OPT_ChooseBlackPieceColor, OPT_ChooseHighlightSquareColor, OPT_ChoosePremoveHighlightColor,
\r
302 OPT_Monochrome, OPT_AllWhite, OPT_UpsideDown, OPT_DefaultBoardColors, GPB_Colors,
\r
303 IDC_Light, IDC_Dark, IDC_White, IDC_Black, IDC_High, IDC_PreHigh, GPB_Size },
\r
304 { DLG_NewVariant, IDOK, IDCANCEL, OPT_VariantNormal, OPT_VariantFRC, OPT_VariantWildcastle,
\r
305 OPT_VariantNocastle, OPT_VariantLosers, OPT_VariantGiveaway, OPT_VariantSuicide,
\r
306 OPT_Variant3Check, OPT_VariantTwoKings, OPT_VariantAtomic, OPT_VariantCrazyhouse,
\r
307 OPT_VariantBughouse, OPT_VariantTwilight, OPT_VariantShogi, OPT_VariantSuper,
\r
308 OPT_VariantKnightmate, OPT_VariantBerolina, OPT_VariantCylinder, OPT_VariantFairy,
\r
309 OPT_VariantMakruk, OPT_VariantGothic, OPT_VariantCapablanca, OPT_VariantJanus,
\r
310 OPT_VariantCRC, OPT_VariantFalcon, OPT_VariantCourier, OPT_VariantGreat, OPT_VariantSChess,
\r
311 OPT_VariantShatranj, OPT_VariantXiangqi, GPB_Variant, GPB_Board, IDC_Height,
\r
312 IDC_Width, IDC_Hand, IDC_Pieces, IDC_Def },
\r
313 { DLG_Fonts, IDOK, IDCANCEL, OPT_ChooseClockFont, OPT_ChooseMessageFont,
\r
314 OPT_ChooseCoordFont, OPT_ChooseTagFont, OPT_ChooseCommentsFont, OPT_ChooseConsoleFont, OPT_ChooseMoveHistoryFont, OPT_DefaultFonts,
\r
315 OPT_ClockFont, OPT_MessageFont, OPT_CoordFont, OPT_EditTagsFont,
\r
316 OPT_CommentsFont, OPT_MessageFont5, GPB_Current, GPB_All, OPT_MessageFont6 },
\r
317 { DLG_NewGameFRC, IDC_NFG_Label, IDC_NFG_Random, IDOK, IDCANCEL },
\r
318 { DLG_GameListOptions, IDC_GLT, IDC_GLT_Up, IDC_GLT_Down, IDC_GLT_Restore,
\r
319 IDC_GLT_Default, IDOK, IDCANCEL, IDC_GLT_RestoreTo },
\r
320 { DLG_MoveHistory },
\r
321 { DLG_EvalGraph },
\r
322 { DLG_EngineOutput, IDC_EngineLabel1, IDC_Engine1_NPS, IDC_EngineLabel2, IDC_Engine2_NPS },
\r
323 { DLG_Chat, IDC_Partner, IDC_Clear, IDC_Send, },
\r
324 { DLG_EnginePlayOptions, IDC_EpPonder, IDC_EpShowThinking, IDC_EpHideThinkingHuman,
\r
325 IDC_EpPeriodicUpdates, GPB_Adjudications, IDC_Draw, IDC_Moves, IDC_Threshold,
\r
326 IDC_Centi, IDC_TestClaims, IDC_DetectMates, IDC_MaterialDraws, IDC_TrivialDraws,
\r
327 GPB_Apply, IDC_Rule, IDC_Repeats, IDC_ScoreAbs1, IDC_ScoreAbs2, IDOK, IDCANCEL },
\r
328 { DLG_OptionsUCI, IDC_PolyDir, IDC_BrowseForPolyglotDir, IDC_Hash, IDC_Path,
\r
329 IDC_BrowseForEGTB, IDC_Cache, IDC_UseBook, IDC_BrowseForBook, IDC_CPU, IDC_OwnBook1,
\r
330 IDC_OwnBook2, IDC_Depth, IDC_Variation, IDC_DefGames, IDOK, IDCANCEL },
\r
334 static char languageBuf[50000], *foreign[1000], *english[1000], *languageFile[MSG_SIZ];
\r
335 static int lastChecked;
\r
336 static char oldLanguage[MSG_SIZ], *menuText[10][30];
\r
337 extern int tinyLayout;
\r
338 extern char * menuBarText[][10];
\r
341 LoadLanguageFile(char *name)
\r
342 { //load the file with translations, and make a list of the strings to be translated, and their translations
\r
344 int i=0, j=0, n=0, k;
\r
347 if(!name || name[0] == NULLCHAR) return;
\r
348 snprintf(buf, MSG_SIZ, "%s%s", name, strchr(name, '.') ? "" : ".lng"); // auto-append lng extension
\r
349 appData.language = oldLanguage;
\r
350 if(!strcmp(buf, oldLanguage)) { barbaric = 1; return; } // this language already loaded; just switch on
\r
351 if((f = fopen(buf, "r")) == NULL) return;
\r
352 while((k = fgetc(f)) != EOF) {
\r
353 if(i >= sizeof(languageBuf)) { DisplayError("Language file too big", 0); return; }
\r
354 languageBuf[i] = k;
\r
356 if(languageBuf[n] == '"' && languageBuf[i-1] == '"') {
\r
358 if(p = strstr(languageBuf + n + 1, "\" === \"")) {
\r
359 if(p > languageBuf+n+2 && p+8 < languageBuf+i) {
\r
360 if(j >= sizeof(english)) { DisplayError("Too many translated strings", 0); return; }
\r
361 english[j] = languageBuf + n + 1; *p = 0;
\r
362 foreign[j++] = p + 7; languageBuf[i-1] = 0;
\r
363 //if(appData.debugMode) fprintf(debugFP, "translation: replace '%s' by '%s'\n", english[j-1], foreign[j-1]);
\r
368 } else if(i > 0 && languageBuf[i-1] == '\\') {
\r
370 case 'n': k = '\n'; break;
\r
371 case 'r': k = '\r'; break;
\r
372 case 't': k = '\t'; break;
\r
374 languageBuf[--i] = k;
\r
379 barbaric = (j != 0);
\r
380 safeStrCpy(oldLanguage, buf, sizeof(oldLanguage)/sizeof(oldLanguage[0]) );
\r
385 { // return the translation of the given string
\r
386 // efficiency can be improved a lot...
\r
388 //if(appData.debugMode) fprintf(debugFP, "T_(%s)\n", s);
\r
389 if(!barbaric) return s;
\r
390 if(!s) return ""; // sanity
\r
391 while(english[i]) {
\r
392 if(!strcmp(s, english[i])) return foreign[i];
\r
399 Translate(HWND hDlg, int dialogID)
\r
400 { // translate all text items in the given dialog
\r
402 char buf[MSG_SIZ], *s;
\r
403 if(!barbaric) return;
\r
404 while(dialogItems[i][0] && dialogItems[i][0] != dialogID) i++; // find the dialog description
\r
405 if(dialogItems[i][0] != dialogID) return; // unknown dialog, should not happen
\r
406 GetWindowText( hDlg, buf, MSG_SIZ );
\r
408 if(strcmp(buf, s)) SetWindowText(hDlg, s); // replace by translated string (if different)
\r
409 for(j=1; k=dialogItems[i][j]; j++) { // translate all listed dialog items
\r
410 GetDlgItemText(hDlg, k, buf, MSG_SIZ);
\r
411 if(strlen(buf) == 0) continue;
\r
413 if(strcmp(buf, s)) SetDlgItemText(hDlg, k, s); // replace by translated string (if different)
\r
418 TranslateOneMenu(int i, HMENU subMenu)
\r
421 static MENUITEMINFO info;
\r
423 info.cbSize = sizeof(MENUITEMINFO);
\r
424 info.fMask = MIIM_STATE | MIIM_TYPE;
\r
425 for(j=GetMenuItemCount(subMenu)-1; j>=0; j--){
\r
427 info.dwTypeData = buf;
\r
428 info.cch = sizeof(buf);
\r
429 GetMenuItemInfo(subMenu, j, TRUE, &info);
\r
431 if(menuText[i][j]) safeStrCpy(buf, menuText[i][j], sizeof(buf)/sizeof(buf[0]) );
\r
432 else menuText[i][j] = strdup(buf); // remember original on first change
\r
434 if(buf[0] == NULLCHAR) continue;
\r
435 info.dwTypeData = T_(buf);
\r
436 info.cch = strlen(buf)+1;
\r
437 SetMenuItemInfo(subMenu, j, TRUE, &info);
\r
443 TranslateMenus(int addLanguage)
\r
446 WIN32_FIND_DATA fileData;
\r
448 #define IDM_English 1970
\r
450 HMENU mainMenu = GetMenu(hwndMain);
\r
451 for (i=GetMenuItemCount(mainMenu)-1; i>=0; i--) {
\r
452 HMENU subMenu = GetSubMenu(mainMenu, i);
\r
453 ModifyMenu(mainMenu, i, MF_STRING|MF_BYPOSITION|MF_POPUP|EnableMenuItem(mainMenu, i, MF_BYPOSITION),
\r
454 (UINT) subMenu, T_(menuBarText[tinyLayout][i]));
\r
455 TranslateOneMenu(i, subMenu);
\r
457 DrawMenuBar(hwndMain);
\r
460 if(!addLanguage) return;
\r
461 if((hFind = FindFirstFile("*.LNG", &fileData)) != INVALID_HANDLE_VALUE) {
\r
462 HMENU mainMenu = GetMenu(hwndMain);
\r
463 HMENU subMenu = GetSubMenu(mainMenu, GetMenuItemCount(mainMenu)-1);
\r
464 AppendMenu(subMenu, MF_SEPARATOR, (UINT_PTR) 0, NULL);
\r
465 AppendMenu(subMenu, MF_ENABLED|MF_STRING|(barbaric?MF_UNCHECKED:MF_CHECKED), (UINT_PTR) IDM_English, (LPCTSTR) "English");
\r
466 i = 0; lastChecked = IDM_English;
\r
468 char *p, *q = fileData.cFileName;
\r
469 int checkFlag = MF_UNCHECKED;
\r
470 languageFile[i] = strdup(q);
\r
471 if(barbaric && !strcmp(oldLanguage, q)) {
\r
472 checkFlag = MF_CHECKED;
\r
473 lastChecked = IDM_English + i + 1;
\r
474 CheckMenuItem(mainMenu, IDM_English, MF_BYCOMMAND|MF_UNCHECKED);
\r
476 *q = ToUpper(*q); while(*++q) *q = ToLower(*q);
\r
477 p = strstr(fileData.cFileName, ".lng");
\r
479 AppendMenu(subMenu, MF_ENABLED|MF_STRING|checkFlag, (UINT_PTR) IDM_English + ++i, (LPCTSTR) fileData.cFileName);
\r
480 } while(FindNextFile(hFind, &fileData));
\r
493 int cliWidth, cliHeight;
\r
496 SizeInfo sizeInfo[] =
\r
498 { "tiny", 21, 0, 1, 1, 0, 0 },
\r
499 { "teeny", 25, 1, 1, 1, 0, 0 },
\r
500 { "dinky", 29, 1, 1, 1, 0, 0 },
\r
501 { "petite", 33, 1, 1, 1, 0, 0 },
\r
502 { "slim", 37, 2, 1, 0, 0, 0 },
\r
503 { "small", 40, 2, 1, 0, 0, 0 },
\r
504 { "mediocre", 45, 2, 1, 0, 0, 0 },
\r
505 { "middling", 49, 2, 0, 0, 0, 0 },
\r
506 { "average", 54, 2, 0, 0, 0, 0 },
\r
507 { "moderate", 58, 3, 0, 0, 0, 0 },
\r
508 { "medium", 64, 3, 0, 0, 0, 0 },
\r
509 { "bulky", 72, 3, 0, 0, 0, 0 },
\r
510 { "large", 80, 3, 0, 0, 0, 0 },
\r
511 { "big", 87, 3, 0, 0, 0, 0 },
\r
512 { "huge", 95, 3, 0, 0, 0, 0 },
\r
513 { "giant", 108, 3, 0, 0, 0, 0 },
\r
514 { "colossal", 116, 4, 0, 0, 0, 0 },
\r
515 { "titanic", 129, 4, 0, 0, 0, 0 },
\r
516 { NULL, 0, 0, 0, 0, 0, 0 }
\r
519 #define MF(x) {x, {{0,}, 0. }, {0, }, 0}
\r
520 MyFont fontRec[NUM_SIZES][NUM_FONTS] =
\r
522 { 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
523 { 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
524 { 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
525 { 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
526 { 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
527 { 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
528 { 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
529 { 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
530 { 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
531 { 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
532 { 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
533 { 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
534 { 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
535 { 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
536 { 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
537 { 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
538 { 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
539 { 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
542 MyFont *font[NUM_SIZES][NUM_FONTS];
\r
551 #define BUTTON_WIDTH (tinyLayout ? 16 : 32)
\r
552 #define N_BUTTONS 5
\r
554 MyButtonDesc buttonDesc[N_BUTTONS] =
\r
556 {"<<", IDM_ToStart, NULL, NULL},
\r
557 {"<", IDM_Backward, NULL, NULL},
\r
558 {"P", IDM_Pause, NULL, NULL},
\r
559 {">", IDM_Forward, NULL, NULL},
\r
560 {">>", IDM_ToEnd, NULL, NULL},
\r
563 int tinyLayout = 0, smallLayout = 0;
\r
564 #define MENU_BAR_ITEMS 9
\r
565 char *menuBarText[2][MENU_BAR_ITEMS+1] = {
\r
566 { N_("&File"), N_("&Edit"), N_("&View"), N_("&Mode"), N_("&Action"), N_("E&ngine"), N_("&Options"), N_("&Help"), NULL },
\r
567 { N_("&F"), N_("&E"), N_("&V"), N_("&M"), N_("&A"), N_("&N"), N_("&O"), N_("&H"), NULL },
\r
571 MySound sounds[(int)NSoundClasses];
\r
572 MyTextAttribs textAttribs[(int)NColorClasses];
\r
574 MyColorizeAttribs colorizeAttribs[] = {
\r
575 { (COLORREF)0, 0, N_("Shout Text") },
\r
576 { (COLORREF)0, 0, N_("SShout/CShout") },
\r
577 { (COLORREF)0, 0, N_("Channel 1 Text") },
\r
578 { (COLORREF)0, 0, N_("Channel Text") },
\r
579 { (COLORREF)0, 0, N_("Kibitz Text") },
\r
580 { (COLORREF)0, 0, N_("Tell Text") },
\r
581 { (COLORREF)0, 0, N_("Challenge Text") },
\r
582 { (COLORREF)0, 0, N_("Request Text") },
\r
583 { (COLORREF)0, 0, N_("Seek Text") },
\r
584 { (COLORREF)0, 0, N_("Normal Text") },
\r
585 { (COLORREF)0, 0, N_("None") }
\r
590 static char *commentTitle;
\r
591 static char *commentText;
\r
592 static int commentIndex;
\r
593 static Boolean editComment = FALSE;
\r
596 char errorTitle[MSG_SIZ];
\r
597 char errorMessage[2*MSG_SIZ];
\r
598 HWND errorDialog = NULL;
\r
599 BOOLEAN moveErrorMessageUp = FALSE;
\r
600 BOOLEAN consoleEcho = TRUE;
\r
601 CHARFORMAT consoleCF;
\r
602 COLORREF consoleBackgroundColor;
\r
604 char *programVersion;
\r
610 typedef int CPKind;
\r
619 SOCKET sock2; /* stderr socket for OpenRcmd */
\r
622 #define INPUT_SOURCE_BUF_SIZE 4096
\r
624 typedef struct _InputSource {
\r
631 char buf[INPUT_SOURCE_BUF_SIZE];
\r
635 InputCallback func;
\r
636 struct _InputSource *second; /* for stderr thread on CPRcmd */
\r
640 InputSource *consoleInputSource;
\r
645 VOID ConsoleOutput(char* data, int length, int forceVisible);
\r
646 VOID ConsoleCreate();
\r
648 ConsoleWndProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam);
\r
649 VOID ColorizeTextPopup(HWND hwnd, ColorClass cc);
\r
650 VOID PrintCommSettings(FILE *f, char *name, DCB *dcb);
\r
651 VOID ParseCommSettings(char *arg, DCB *dcb);
\r
653 StartupDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam);
\r
654 VOID APIENTRY MenuPopup(HWND hwnd, POINT pt, HMENU hmenu, UINT def);
\r
655 void ParseIcsTextMenu(char *icsTextMenuString);
\r
656 VOID PopUpMoveDialog(char firstchar);
\r
657 VOID PopUpNameDialog(char firstchar);
\r
658 VOID UpdateSampleText(HWND hDlg, int id, MyColorizeAttribs *mca);
\r
662 int GameListOptions();
\r
664 int dummy; // [HGM] for obsolete args
\r
666 HWND hwndMain = NULL; /* root window*/
\r
667 HWND hwndConsole = NULL;
\r
668 HWND commentDialog = NULL;
\r
669 HWND moveHistoryDialog = NULL;
\r
670 HWND evalGraphDialog = NULL;
\r
671 HWND engineOutputDialog = NULL;
\r
672 HWND gameListDialog = NULL;
\r
673 HWND editTagsDialog = NULL;
\r
675 int commentUp = FALSE;
\r
677 WindowPlacement wpMain;
\r
678 WindowPlacement wpConsole;
\r
679 WindowPlacement wpComment;
\r
680 WindowPlacement wpMoveHistory;
\r
681 WindowPlacement wpEvalGraph;
\r
682 WindowPlacement wpEngineOutput;
\r
683 WindowPlacement wpGameList;
\r
684 WindowPlacement wpTags;
\r
686 VOID EngineOptionsPopup(); // [HGM] settings
\r
688 VOID GothicPopUp(char *title, VariantClass variant);
\r
690 * Setting "frozen" should disable all user input other than deleting
\r
691 * the window. We do this while engines are initializing themselves.
\r
693 static int frozen = 0;
\r
694 static int oldMenuItemState[MENU_BAR_ITEMS];
\r
700 if (frozen) return;
\r
702 hmenu = GetMenu(hwndMain);
\r
703 for (i=0; i<MENU_BAR_ITEMS; i++) {
\r
704 oldMenuItemState[i] = EnableMenuItem(hmenu, i, MF_BYPOSITION|MF_GRAYED);
\r
706 DrawMenuBar(hwndMain);
\r
709 /* Undo a FreezeUI */
\r
715 if (!frozen) return;
\r
717 hmenu = GetMenu(hwndMain);
\r
718 for (i=0; i<MENU_BAR_ITEMS; i++) {
\r
719 EnableMenuItem(hmenu, i, MF_BYPOSITION|oldMenuItemState[i]);
\r
721 DrawMenuBar(hwndMain);
\r
724 /*static*/ int fromX = -1, fromY = -1, toX, toY; // [HGM] moved upstream, so JAWS can use them
\r
726 /* JAWS preparation patch (WinBoard for the sight impaired). Define required insertions as empty */
\r
732 #define JAWS_ALT_INTERCEPT
\r
733 #define JAWS_KB_NAVIGATION
\r
734 #define JAWS_MENU_ITEMS
\r
735 #define JAWS_SILENCE
\r
736 #define JAWS_REPLAY
\r
738 #define JAWS_COPYRIGHT
\r
739 #define JAWS_DELETE(X) X
\r
740 #define SAYMACHINEMOVE()
\r
744 /*---------------------------------------------------------------------------*\
\r
748 \*---------------------------------------------------------------------------*/
\r
751 WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
\r
752 LPSTR lpCmdLine, int nCmdShow)
\r
755 HANDLE hAccelMain, hAccelNoAlt, hAccelNoICS;
\r
756 // INITCOMMONCONTROLSEX ex;
\r
760 LoadLibrary("RICHED32.DLL");
\r
761 consoleCF.cbSize = sizeof(CHARFORMAT);
\r
763 if (!InitApplication(hInstance)) {
\r
766 if (!InitInstance(hInstance, nCmdShow, lpCmdLine)) {
\r
773 // InitCommonControlsEx(&ex);
\r
774 InitCommonControls();
\r
776 hAccelMain = LoadAccelerators (hInstance, szAppName);
\r
777 hAccelNoAlt = LoadAccelerators (hInstance, "NO_ALT");
\r
778 hAccelNoICS = LoadAccelerators( hInstance, "NO_ICS"); /* [AS] No Ctrl-V on ICS!!! */
\r
780 /* Acquire and dispatch messages until a WM_QUIT message is received. */
\r
782 while (GetMessage(&msg, /* message structure */
\r
783 NULL, /* handle of window receiving the message */
\r
784 0, /* lowest message to examine */
\r
785 0)) /* highest message to examine */
\r
788 if(msg.message == WM_CHAR && msg.wParam == '\t') {
\r
789 // [HGM] navigate: switch between all windows with tab
\r
790 HWND e1 = NULL, e2 = NULL, mh = NULL, hInput = NULL, hText = NULL;
\r
791 int i, currentElement = 0;
\r
793 // first determine what element of the chain we come from (if any)
\r
794 if(appData.icsActive) {
\r
795 hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
796 hText = GetDlgItem(hwndConsole, OPT_ConsoleText);
\r
798 if(engineOutputDialog && EngineOutputIsUp()) {
\r
799 e1 = GetDlgItem(engineOutputDialog, IDC_EngineMemo1);
\r
800 e2 = GetDlgItem(engineOutputDialog, IDC_EngineMemo2);
\r
802 if(moveHistoryDialog && MoveHistoryIsUp()) {
\r
803 mh = GetDlgItem(moveHistoryDialog, IDC_MoveHistory);
\r
805 if(msg.hwnd == hwndMain) currentElement = 7 ; else
\r
806 if(msg.hwnd == engineOutputDialog) currentElement = 2; else
\r
807 if(msg.hwnd == e1) currentElement = 2; else
\r
808 if(msg.hwnd == e2) currentElement = 3; else
\r
809 if(msg.hwnd == moveHistoryDialog) currentElement = 4; else
\r
810 if(msg.hwnd == mh) currentElement = 4; else
\r
811 if(msg.hwnd == evalGraphDialog) currentElement = 6; else
\r
812 if(msg.hwnd == hText) currentElement = 5; else
\r
813 if(msg.hwnd == hInput) currentElement = 6; else
\r
814 for (i = 0; i < N_BUTTONS; i++) {
\r
815 if (buttonDesc[i].hwnd == msg.hwnd) { currentElement = 1; break; }
\r
818 // determine where to go to
\r
819 if(currentElement) { HWND h = NULL; int direction = GetKeyState(VK_SHIFT) < 0 ? -1 : 1;
\r
821 currentElement = (currentElement + direction) % 7;
\r
822 switch(currentElement) {
\r
824 h = hwndMain; break; // passing this case always makes the loop exit
\r
826 h = buttonDesc[0].hwnd; break; // could be NULL
\r
828 if(!EngineOutputIsUp()) continue; // skip closed auxiliary windows
\r
831 if(!EngineOutputIsUp()) continue;
\r
834 if(!MoveHistoryIsUp()) continue;
\r
836 // case 6: // input to eval graph does not seem to get here!
\r
837 // if(!EvalGraphIsUp()) continue;
\r
838 // h = evalGraphDialog; break;
\r
840 if(!appData.icsActive) continue;
\r
844 if(!appData.icsActive) continue;
\r
850 if(currentElement > 4 && IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);
\r
851 if(currentElement < 5 && IsIconic(hwndMain)) ShowWindow(hwndMain, SW_RESTORE); // all open together
\r
854 continue; // this message now has been processed
\r
858 if (!(commentDialog && IsDialogMessage(commentDialog, &msg)) &&
\r
859 !(moveHistoryDialog && IsDialogMessage(moveHistoryDialog, &msg)) &&
\r
860 !(evalGraphDialog && IsDialogMessage(evalGraphDialog, &msg)) &&
\r
861 !(engineOutputDialog && IsDialogMessage(engineOutputDialog, &msg)) &&
\r
862 !(editTagsDialog && IsDialogMessage(editTagsDialog, &msg)) &&
\r
863 !(gameListDialog && IsDialogMessage(gameListDialog, &msg)) &&
\r
864 !(errorDialog && IsDialogMessage(errorDialog, &msg)) &&
\r
865 !(!frozen && TranslateAccelerator(hwndMain, hAccelMain, &msg)) && JAWS_ACCEL
\r
866 !(!hwndConsole && TranslateAccelerator(hwndMain, hAccelNoICS, &msg)) &&
\r
867 !(!hwndConsole && TranslateAccelerator(hwndMain, hAccelNoAlt, &msg))) {
\r
868 int done = 0, i; // [HGM] chat: dispatch cat-box messages
\r
869 for(i=0; i<MAX_CHAT; i++)
\r
870 if(chatHandle[i] && IsDialogMessage(chatHandle[i], &msg)) {
\r
873 if(done) continue; // [HGM] chat: end patch
\r
874 TranslateMessage(&msg); /* Translates virtual key codes */
\r
875 DispatchMessage(&msg); /* Dispatches message to window */
\r
880 return (msg.wParam); /* Returns the value from PostQuitMessage */
\r
883 /*---------------------------------------------------------------------------*\
\r
885 * Initialization functions
\r
887 \*---------------------------------------------------------------------------*/
\r
891 { // update user logo if necessary
\r
892 static char oldUserName[MSG_SIZ], *curName;
\r
894 if(appData.autoLogo) {
\r
895 curName = UserName();
\r
896 if(strcmp(curName, oldUserName)) {
\r
897 snprintf(oldUserName, MSG_SIZ, "logos\\%s.bmp", curName);
\r
898 userLogo = LoadImage( 0, oldUserName, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );
\r
899 safeStrCpy(oldUserName, curName, sizeof(oldUserName)/sizeof(oldUserName[0]) );
\r
900 if(userLogo == NULL)
\r
901 userLogo = LoadImage( 0, "logos\\dummy.bmp", IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );
\r
907 InitApplication(HINSTANCE hInstance)
\r
911 /* Fill in window class structure with parameters that describe the */
\r
914 wc.style = CS_HREDRAW | CS_VREDRAW; /* Class style(s). */
\r
915 wc.lpfnWndProc = (WNDPROC)WndProc; /* Window Procedure */
\r
916 wc.cbClsExtra = 0; /* No per-class extra data. */
\r
917 wc.cbWndExtra = 0; /* No per-window extra data. */
\r
918 wc.hInstance = hInstance; /* Owner of this class */
\r
919 wc.hIcon = LoadIcon(hInstance, "icon_white");
\r
920 wc.hCursor = LoadCursor(NULL, IDC_ARROW); /* Cursor */
\r
921 wc.hbrBackground = (HBRUSH)(COLOR_WINDOW+1); /* Default color */
\r
922 wc.lpszMenuName = szAppName; /* Menu name from .RC */
\r
923 wc.lpszClassName = szAppName; /* Name to register as */
\r
925 /* Register the window class and return success/failure code. */
\r
926 if (!RegisterClass(&wc)) return FALSE;
\r
928 wc.style = CS_HREDRAW | CS_VREDRAW;
\r
929 wc.lpfnWndProc = (WNDPROC)ConsoleWndProc;
\r
931 wc.cbWndExtra = DLGWINDOWEXTRA;
\r
932 wc.hInstance = hInstance;
\r
933 wc.hIcon = LoadIcon(hInstance, "icon_white");
\r
934 wc.hCursor = LoadCursor(NULL, IDC_ARROW);
\r
935 wc.hbrBackground = (HBRUSH)(COLOR_MENU+1);
\r
936 wc.lpszMenuName = NULL;
\r
937 wc.lpszClassName = szConsoleName;
\r
939 if (!RegisterClass(&wc)) return FALSE;
\r
944 /* Set by InitInstance, used by EnsureOnScreen */
\r
945 int screenHeight, screenWidth;
\r
948 EnsureOnScreen(int *x, int *y, int minX, int minY)
\r
950 // int gap = GetSystemMetrics(SM_CYFRAME) + GetSystemMetrics(SM_CYCAPTION);
\r
951 /* Be sure window at (x,y) is not off screen (or even mostly off screen) */
\r
952 if (*x > screenWidth - 32) *x = 0;
\r
953 if (*y > screenHeight - 32) *y = 0;
\r
954 if (*x < minX) *x = minX;
\r
955 if (*y < minY) *y = minY;
\r
959 InitInstance(HINSTANCE hInstance, int nCmdShow, LPSTR lpCmdLine)
\r
961 HWND hwnd; /* Main window handle. */
\r
963 WINDOWPLACEMENT wp;
\r
966 hInst = hInstance; /* Store instance handle in our global variable */
\r
967 programName = szAppName;
\r
969 if (SearchPath(NULL, "WinBoard.exe", NULL, MSG_SIZ, installDir, &filepart)) {
\r
970 *filepart = NULLCHAR;
\r
972 GetCurrentDirectory(MSG_SIZ, installDir);
\r
974 gameInfo.boardWidth = gameInfo.boardHeight = 8; // [HGM] won't have open window otherwise
\r
975 screenWidth = screenHeight = 1000; // [HGM] placement: kludge to allow calling EnsureOnScreen from InitAppData
\r
976 InitAppData(lpCmdLine); /* Get run-time parameters */
\r
977 /* xboard, and older WinBoards, controlled the move sound with the
\r
978 appData.ringBellAfterMoves option. In the current WinBoard, we
\r
979 always turn the option on (so that the backend will call us),
\r
980 then let the user turn the sound off by setting it to silence if
\r
981 desired. To accommodate old winboard.ini files saved by old
\r
982 versions of WinBoard, we also turn off the sound if the option
\r
983 was initially set to false. [HGM] taken out of InitAppData */
\r
984 if (!appData.ringBellAfterMoves) {
\r
985 sounds[(int)SoundMove].name = strdup("");
\r
986 appData.ringBellAfterMoves = TRUE;
\r
988 if (appData.debugMode) {
\r
989 debugFP = fopen(appData.nameOfDebugFile, "w");
\r
990 setbuf(debugFP, NULL);
\r
993 LoadLanguageFile(appData.language);
\r
997 // InitEngineUCI( installDir, &first ); // [HGM] incorporated in InitBackEnd1()
\r
998 // InitEngineUCI( installDir, &second );
\r
1000 /* Create a main window for this application instance. */
\r
1001 hwnd = CreateWindow(szAppName, szTitle,
\r
1002 (WS_OVERLAPPEDWINDOW & ~WS_MAXIMIZEBOX),
\r
1003 CW_USEDEFAULT, 0, CW_USEDEFAULT, 0,
\r
1004 NULL, NULL, hInstance, NULL);
\r
1007 /* If window could not be created, return "failure" */
\r
1012 /* [HGM] logo: Load logos if specified (must be done before InitDrawingSizes) */
\r
1013 if( appData.firstLogo && appData.firstLogo[0] != NULLCHAR) {
\r
1014 first.programLogo = LoadImage( 0, appData.firstLogo, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );
\r
1016 if (first.programLogo == NULL && appData.debugMode) {
\r
1017 fprintf( debugFP, "Unable to load logo bitmap '%s'\n", appData.firstLogo );
\r
1019 } else if(appData.autoLogo) {
\r
1020 if(appData.firstDirectory && appData.firstDirectory[0]) {
\r
1021 char buf[MSG_SIZ];
\r
1022 snprintf(buf, MSG_SIZ, "%s/logo.bmp", appData.firstDirectory);
\r
1023 first.programLogo = LoadImage( 0, buf, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );
\r
1027 if( appData.secondLogo && appData.secondLogo[0] != NULLCHAR) {
\r
1028 second.programLogo = LoadImage( 0, appData.secondLogo, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );
\r
1030 if (second.programLogo == NULL && appData.debugMode) {
\r
1031 fprintf( debugFP, "Unable to load logo bitmap '%s'\n", appData.secondLogo );
\r
1033 } else if(appData.autoLogo) {
\r
1034 char buf[MSG_SIZ];
\r
1035 if(appData.icsActive) { // [HGM] logo: in ICS mode second can be used for ICS
\r
1036 snprintf(buf, MSG_SIZ, "logos\\%s.bmp", appData.icsHost);
\r
1037 second.programLogo = LoadImage( 0, buf, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );
\r
1039 if(appData.secondDirectory && appData.secondDirectory[0]) {
\r
1040 snprintf(buf, MSG_SIZ, "%s\\logo.bmp", appData.secondDirectory);
\r
1041 second.programLogo = LoadImage( 0, buf, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );
\r
1047 iconWhite = LoadIcon(hInstance, "icon_white");
\r
1048 iconBlack = LoadIcon(hInstance, "icon_black");
\r
1049 iconCurrent = iconWhite;
\r
1050 InitDrawingColors();
\r
1051 screenHeight = GetSystemMetrics(SM_CYSCREEN);
\r
1052 screenWidth = GetSystemMetrics(SM_CXSCREEN);
\r
1053 for (ibs = (int) NUM_SIZES - 1; ibs >= 0; ibs--) {
\r
1054 /* Compute window size for each board size, and use the largest
\r
1055 size that fits on this screen as the default. */
\r
1056 InitDrawingSizes((BoardSize)(ibs+1000), 0);
\r
1057 if (boardSize == (BoardSize)-1 &&
\r
1058 winH <= screenHeight
\r
1059 - GetSystemMetrics(SM_CYFRAME) - GetSystemMetrics(SM_CYCAPTION) - 10
\r
1060 && winW <= screenWidth) {
\r
1061 boardSize = (BoardSize)ibs;
\r
1065 InitDrawingSizes(boardSize, 0);
\r
1067 buttonCount = GetSystemMetrics(SM_CMOUSEBUTTONS);
\r
1069 /* [AS] Load textures if specified */
\r
1070 ZeroMemory( &backTextureSquareInfo, sizeof(backTextureSquareInfo) );
\r
1072 if( appData.liteBackTextureFile && appData.liteBackTextureFile[0] != NULLCHAR && appData.liteBackTextureFile[0] != '*' ) {
\r
1073 liteBackTexture = LoadImage( 0, appData.liteBackTextureFile, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );
\r
1074 liteBackTextureMode = appData.liteBackTextureMode;
\r
1076 if (liteBackTexture == NULL && appData.debugMode) {
\r
1077 fprintf( debugFP, "Unable to load lite texture bitmap '%s'\n", appData.liteBackTextureFile );
\r
1081 if( appData.darkBackTextureFile && appData.darkBackTextureFile[0] != NULLCHAR && appData.darkBackTextureFile[0] != '*' ) {
\r
1082 darkBackTexture = LoadImage( 0, appData.darkBackTextureFile, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );
\r
1083 darkBackTextureMode = appData.darkBackTextureMode;
\r
1085 if (darkBackTexture == NULL && appData.debugMode) {
\r
1086 fprintf( debugFP, "Unable to load dark texture bitmap '%s'\n", appData.darkBackTextureFile );
\r
1090 mysrandom( (unsigned) time(NULL) );
\r
1092 /* [AS] Restore layout */
\r
1093 if( wpMoveHistory.visible ) {
\r
1094 MoveHistoryPopUp();
\r
1097 if( wpEvalGraph.visible ) {
\r
1101 if( wpEngineOutput.visible ) {
\r
1102 EngineOutputPopUp();
\r
1105 /* Make the window visible; update its client area; and return "success" */
\r
1106 EnsureOnScreen(&wpMain.x, &wpMain.y, minX, minY);
\r
1107 wp.length = sizeof(WINDOWPLACEMENT);
\r
1109 wp.showCmd = nCmdShow;
\r
1110 wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;
\r
1111 wp.rcNormalPosition.left = wpMain.x;
\r
1112 wp.rcNormalPosition.right = wpMain.x + wpMain.width;
\r
1113 wp.rcNormalPosition.top = wpMain.y;
\r
1114 wp.rcNormalPosition.bottom = wpMain.y + wpMain.height;
\r
1115 SetWindowPlacement(hwndMain, &wp);
\r
1117 InitBackEnd2(); // [HGM] moved until after all windows placed, to save correct position if fatal error on engine start
\r
1119 if(!appData.noGUI) SetWindowPos(hwndMain, alwaysOnTop ? HWND_TOPMOST : HWND_NOTOPMOST,
\r
1120 0, 0, 0, 0, SWP_NOMOVE|SWP_NOSIZE);
\r
1122 if (hwndConsole) {
\r
1124 SetWindowPos(hwndConsole, alwaysOnTop ? HWND_TOPMOST : HWND_NOTOPMOST,
\r
1125 0, 0, 0, 0, SWP_NOMOVE|SWP_NOSIZE);
\r
1127 ShowWindow(hwndConsole, nCmdShow);
\r
1128 if(appData.chatBoxes) { // [HGM] chat: open chat boxes
\r
1129 char buf[MSG_SIZ], *p = buf, *q;
\r
1130 safeStrCpy(buf, appData.chatBoxes, sizeof(buf)/sizeof(buf[0]) );
\r
1132 q = strchr(p, ';');
\r
1134 if(*p) ChatPopUp(p);
\r
1137 SetActiveWindow(hwndConsole);
\r
1139 if(!appData.noGUI) UpdateWindow(hwnd); else ShowWindow(hwnd, SW_MINIMIZE);
\r
1140 if(gameListDialog) SetFocus(gameListDialog); // [HGM] jaws: for if we clicked multi-game game file
\r
1149 HMENU hmenu = GetMenu(hwndMain);
\r
1151 (void) EnableMenuItem(hmenu, IDM_CommPort,
\r
1152 MF_BYCOMMAND|((appData.icsActive &&
\r
1153 *appData.icsCommPort != NULLCHAR) ?
\r
1154 MF_ENABLED : MF_GRAYED));
\r
1155 (void) CheckMenuItem(hmenu, IDM_SaveSettingsOnExit,
\r
1156 MF_BYCOMMAND|(saveSettingsOnExit ?
\r
1157 MF_CHECKED : MF_UNCHECKED));
\r
1160 //---------------------------------------------------------------------------------------------------------
\r
1162 #define ICS_TEXT_MENU_SIZE (IDM_CommandXLast - IDM_CommandX + 1)
\r
1163 #define XBOARD FALSE
\r
1165 #define OPTCHAR "/"
\r
1166 #define SEPCHAR "="
\r
1170 // front-end part of option handling
\r
1173 LFfromMFP(LOGFONT* lf, MyFontParams *mfp)
\r
1175 HDC hdc = CreateDC("DISPLAY", NULL, NULL, NULL);
\r
1176 lf->lfHeight = -(int)(mfp->pointSize * GetDeviceCaps(hdc, LOGPIXELSY) / 72.0 + 0.5);
\r
1179 lf->lfEscapement = 0;
\r
1180 lf->lfOrientation = 0;
\r
1181 lf->lfWeight = mfp->bold ? FW_BOLD : FW_NORMAL;
\r
1182 lf->lfItalic = mfp->italic;
\r
1183 lf->lfUnderline = mfp->underline;
\r
1184 lf->lfStrikeOut = mfp->strikeout;
\r
1185 lf->lfCharSet = mfp->charset;
\r
1186 lf->lfOutPrecision = OUT_DEFAULT_PRECIS;
\r
1187 lf->lfClipPrecision = CLIP_DEFAULT_PRECIS;
\r
1188 lf->lfQuality = DEFAULT_QUALITY;
\r
1189 lf->lfPitchAndFamily = DEFAULT_PITCH|FF_DONTCARE;
\r
1190 safeStrCpy(lf->lfFaceName, mfp->faceName, sizeof(lf->lfFaceName)/sizeof(lf->lfFaceName[0]) );
\r
1194 CreateFontInMF(MyFont *mf)
\r
1196 LFfromMFP(&mf->lf, &mf->mfp);
\r
1197 if (mf->hf) DeleteObject(mf->hf);
\r
1198 mf->hf = CreateFontIndirect(&mf->lf);
\r
1201 // [HGM] This platform-dependent table provides the location for storing the color info
\r
1203 colorVariable[] = {
\r
1204 &whitePieceColor,
\r
1205 &blackPieceColor,
\r
1206 &lightSquareColor,
\r
1207 &darkSquareColor,
\r
1208 &highlightSquareColor,
\r
1209 &premoveHighlightColor,
\r
1211 &consoleBackgroundColor,
\r
1212 &appData.fontForeColorWhite,
\r
1213 &appData.fontBackColorWhite,
\r
1214 &appData.fontForeColorBlack,
\r
1215 &appData.fontBackColorBlack,
\r
1216 &appData.evalHistColorWhite,
\r
1217 &appData.evalHistColorBlack,
\r
1218 &appData.highlightArrowColor,
\r
1221 /* Command line font name parser. NULL name means do nothing.
\r
1222 Syntax like "Courier New:10.0 bi" or "Arial:10" or "Arial:10b"
\r
1223 For backward compatibility, syntax without the colon is also
\r
1224 accepted, but font names with digits in them won't work in that case.
\r
1227 ParseFontName(char *name, MyFontParams *mfp)
\r
1230 if (name == NULL) return;
\r
1232 q = strchr(p, ':');
\r
1234 if (q - p >= sizeof(mfp->faceName))
\r
1235 ExitArgError(_("Font name too long:"), name);
\r
1236 memcpy(mfp->faceName, p, q - p);
\r
1237 mfp->faceName[q - p] = NULLCHAR;
\r
1240 q = mfp->faceName;
\r
1241 while (*p && !isdigit(*p)) {
\r
1243 if (q - mfp->faceName >= sizeof(mfp->faceName))
\r
1244 ExitArgError(_("Font name too long:"), name);
\r
1246 while (q > mfp->faceName && q[-1] == ' ') q--;
\r
1249 if (!*p) ExitArgError(_("Font point size missing:"), name);
\r
1250 mfp->pointSize = (float) atof(p);
\r
1251 mfp->bold = (strchr(p, 'b') != NULL);
\r
1252 mfp->italic = (strchr(p, 'i') != NULL);
\r
1253 mfp->underline = (strchr(p, 'u') != NULL);
\r
1254 mfp->strikeout = (strchr(p, 's') != NULL);
\r
1255 mfp->charset = DEFAULT_CHARSET;
\r
1256 q = strchr(p, 'c');
\r
1258 mfp->charset = (BYTE) atoi(q+1);
\r
1262 ParseFont(char *name, int number)
\r
1263 { // wrapper to shield back-end from 'font'
\r
1264 ParseFontName(name, &font[boardSize][number]->mfp);
\r
1269 { // in WB we have a 2D array of fonts; this initializes their description
\r
1271 /* Point font array elements to structures and
\r
1272 parse default font names */
\r
1273 for (i=0; i<NUM_FONTS; i++) {
\r
1274 for (j=0; j<NUM_SIZES; j++) {
\r
1275 font[j][i] = &fontRec[j][i];
\r
1276 ParseFontName(font[j][i]->def, &font[j][i]->mfp);
\r
1283 { // here we create the actual fonts from the selected descriptions
\r
1285 for (i=0; i<NUM_FONTS; i++) {
\r
1286 for (j=0; j<NUM_SIZES; j++) {
\r
1287 CreateFontInMF(font[j][i]);
\r
1291 /* Color name parser.
\r
1292 X version accepts X color names, but this one
\r
1293 handles only the #rrggbb form (hex) or rrr,ggg,bbb (decimal) */
\r
1295 ParseColorName(char *name)
\r
1297 int red, green, blue, count;
\r
1298 char buf[MSG_SIZ];
\r
1300 count = sscanf(name, "#%2x%2x%2x", &red, &green, &blue);
\r
1302 count = sscanf(name, "%3d%*[^0-9]%3d%*[^0-9]%3d",
\r
1303 &red, &green, &blue);
\r
1306 snprintf(buf, MSG_SIZ, _("Can't parse color name %s"), name);
\r
1307 DisplayError(buf, 0);
\r
1308 return RGB(0, 0, 0);
\r
1310 return PALETTERGB(red, green, blue);
\r
1314 ParseColor(int n, char *name)
\r
1315 { // for WinBoard the color is an int, which needs to be derived from the string
\r
1316 if(colorVariable[n]) *(int*)colorVariable[n] = ParseColorName(name);
\r
1320 ParseAttribs(COLORREF *color, int *effects, char* argValue)
\r
1322 char *e = argValue;
\r
1326 if (*e == 'b') eff |= CFE_BOLD;
\r
1327 else if (*e == 'i') eff |= CFE_ITALIC;
\r
1328 else if (*e == 'u') eff |= CFE_UNDERLINE;
\r
1329 else if (*e == 's') eff |= CFE_STRIKEOUT;
\r
1330 else if (*e == '#' || isdigit(*e)) break;
\r
1334 *color = ParseColorName(e);
\r
1338 ParseTextAttribs(ColorClass cc, char *s)
\r
1339 { // [HGM] front-end wrapper that does the platform-dependent call
\r
1340 // for XBoard we would set (&appData.colorShout)[cc] = strdup(s);
\r
1341 ParseAttribs(&textAttribs[cc].color, &textAttribs[cc].effects, s);
\r
1345 ParseBoardSize(void *addr, char *name)
\r
1346 { // [HGM] rewritten with return-value ptr to shield back-end from BoardSize
\r
1347 BoardSize bs = SizeTiny;
\r
1348 while (sizeInfo[bs].name != NULL) {
\r
1349 if (StrCaseCmp(name, sizeInfo[bs].name) == 0) {
\r
1350 *(BoardSize *)addr = bs;
\r
1355 ExitArgError(_("Unrecognized board size value"), name);
\r
1360 { // [HGM] import name from appData first
\r
1363 for (cc = (ColorClass)0; cc < ColorNormal; cc++) {
\r
1364 textAttribs[cc].sound.name = strdup((&appData.soundShout)[cc]);
\r
1365 textAttribs[cc].sound.data = NULL;
\r
1366 MyLoadSound(&textAttribs[cc].sound);
\r
1368 for (cc = ColorNormal; cc < NColorClasses; cc++) {
\r
1369 textAttribs[cc].sound.name = strdup("");
\r
1370 textAttribs[cc].sound.data = NULL;
\r
1372 for (sc = (SoundClass)0; sc < NSoundClasses; sc++) {
\r
1373 sounds[sc].name = strdup((&appData.soundMove)[sc]);
\r
1374 sounds[sc].data = NULL;
\r
1375 MyLoadSound(&sounds[sc]);
\r
1380 SetCommPortDefaults()
\r
1382 memset(&dcb, 0, sizeof(DCB)); // required by VS 2002 +
\r
1383 dcb.DCBlength = sizeof(DCB);
\r
1384 dcb.BaudRate = 9600;
\r
1385 dcb.fBinary = TRUE;
\r
1386 dcb.fParity = FALSE;
\r
1387 dcb.fOutxCtsFlow = FALSE;
\r
1388 dcb.fOutxDsrFlow = FALSE;
\r
1389 dcb.fDtrControl = DTR_CONTROL_ENABLE;
\r
1390 dcb.fDsrSensitivity = FALSE;
\r
1391 dcb.fTXContinueOnXoff = TRUE;
\r
1392 dcb.fOutX = FALSE;
\r
1394 dcb.fNull = FALSE;
\r
1395 dcb.fRtsControl = RTS_CONTROL_ENABLE;
\r
1396 dcb.fAbortOnError = FALSE;
\r
1398 dcb.Parity = SPACEPARITY;
\r
1399 dcb.StopBits = ONESTOPBIT;
\r
1402 // [HGM] args: these three cases taken out to stay in front-end
\r
1404 SaveFontArg(FILE *f, ArgDescriptor *ad)
\r
1405 { // in WinBoard every board size has its own font, and the "argLoc" identifies the table,
\r
1406 // while the curent board size determines the element. This system should be ported to XBoard.
\r
1407 // What the table contains pointers to, and how to print the font description, remains platform-dependent
\r
1409 for (bs=0; bs<NUM_SIZES; bs++) {
\r
1410 MyFontParams *mfp = &font[bs][(int) ad->argLoc]->mfp;
\r
1411 fprintf(f, "/size=%s ", sizeInfo[bs].name);
\r
1412 fprintf(f, "/%s=\"%s:%g%s%s%s%s%sc%d\"\n",
\r
1413 ad->argName, mfp->faceName, mfp->pointSize,
\r
1414 mfp->bold || mfp->italic || mfp->underline || mfp->strikeout ? " " : "",
\r
1415 mfp->bold ? "b" : "",
\r
1416 mfp->italic ? "i" : "",
\r
1417 mfp->underline ? "u" : "",
\r
1418 mfp->strikeout ? "s" : "",
\r
1419 (int)mfp->charset);
\r
1425 { // [HGM] copy the names from the internal WB variables to appData
\r
1428 for (cc = (ColorClass)0; cc < ColorNormal; cc++)
\r
1429 (&appData.soundShout)[cc] = textAttribs[cc].sound.name;
\r
1430 for (sc = (SoundClass)0; sc < NSoundClasses; sc++)
\r
1431 (&appData.soundMove)[sc] = sounds[sc].name;
\r
1435 SaveAttribsArg(FILE *f, ArgDescriptor *ad)
\r
1436 { // here the "argLoc" defines a table index. It could have contained the 'ta' pointer itself, though
\r
1437 MyTextAttribs* ta = &textAttribs[(ColorClass)ad->argLoc];
\r
1438 fprintf(f, "/%s=\"%s%s%s%s%s#%02lx%02lx%02lx\"\n", ad->argName,
\r
1439 (ta->effects & CFE_BOLD) ? "b" : "",
\r
1440 (ta->effects & CFE_ITALIC) ? "i" : "",
\r
1441 (ta->effects & CFE_UNDERLINE) ? "u" : "",
\r
1442 (ta->effects & CFE_STRIKEOUT) ? "s" : "",
\r
1443 (ta->effects) ? " " : "",
\r
1444 ta->color&0xff, (ta->color >> 8)&0xff, (ta->color >> 16)&0xff);
\r
1448 SaveColor(FILE *f, ArgDescriptor *ad)
\r
1449 { // in WinBoard the color is an int and has to be converted to text. In X it would be a string already?
\r
1450 COLORREF color = *(COLORREF *)colorVariable[(int)ad->argLoc];
\r
1451 fprintf(f, "/%s=#%02lx%02lx%02lx\n", ad->argName,
\r
1452 color&0xff, (color>>8)&0xff, (color>>16)&0xff);
\r
1456 SaveBoardSize(FILE *f, char *name, void *addr)
\r
1457 { // wrapper to shield back-end from BoardSize & sizeInfo
\r
1458 fprintf(f, "/%s=%s\n", name, sizeInfo[*(BoardSize *)addr].name);
\r
1462 ParseCommPortSettings(char *s)
\r
1463 { // wrapper to keep dcb from back-end
\r
1464 ParseCommSettings(s, &dcb);
\r
1469 { // wrapper to shield use of window handles from back-end (make addressible by number?)
\r
1470 GetActualPlacement(hwndMain, &wpMain);
\r
1471 GetActualPlacement(hwndConsole, &wpConsole);
\r
1472 GetActualPlacement(commentDialog, &wpComment);
\r
1473 GetActualPlacement(editTagsDialog, &wpTags);
\r
1474 GetActualPlacement(gameListDialog, &wpGameList);
\r
1475 GetActualPlacement(moveHistoryDialog, &wpMoveHistory);
\r
1476 GetActualPlacement(evalGraphDialog, &wpEvalGraph);
\r
1477 GetActualPlacement(engineOutputDialog, &wpEngineOutput);
\r
1481 PrintCommPortSettings(FILE *f, char *name)
\r
1482 { // wrapper to shield back-end from DCB
\r
1483 PrintCommSettings(f, name, &dcb);
\r
1487 MySearchPath(char *installDir, char *name, char *fullname)
\r
1489 char *dummy, buf[MSG_SIZ], *p = name, *q;
\r
1490 if(name[0]== '%') {
\r
1491 fullname[0] = 0; // [HGM] first expand any environment variables in the given name
\r
1492 while(*p == '%' && (q = strchr(p+1, '%'))) { // [HGM] recognize %*% as environment variable
\r
1493 safeStrCpy(buf, p+1, sizeof(buf)/sizeof(buf[0]) );
\r
1494 *strchr(buf, '%') = 0;
\r
1495 strcat(fullname, getenv(buf));
\r
1496 p = q+1; while(*p == '\\') { strcat(fullname, "\\"); p++; }
\r
1498 strcat(fullname, p); // after environment variables (if any), take the remainder of the given name
\r
1499 if(appData.debugMode) fprintf(debugFP, "name = '%s', expanded name = '%s'\n", name, fullname);
\r
1500 return (int) strlen(fullname);
\r
1502 return (int) SearchPath(installDir, name, NULL, MSG_SIZ, fullname, &dummy);
\r
1506 MyGetFullPathName(char *name, char *fullname)
\r
1509 return (int) GetFullPathName(name, MSG_SIZ, fullname, &dummy);
\r
1514 { // [HGM] args: allows testing if main window is realized from back-end
\r
1515 return hwndMain != NULL;
\r
1519 PopUpStartupDialog()
\r
1523 LoadLanguageFile(appData.language);
\r
1524 lpProc = MakeProcInstance((FARPROC)StartupDialog, hInst);
\r
1525 DialogBox(hInst, MAKEINTRESOURCE(DLG_Startup), NULL, (DLGPROC)lpProc);
\r
1526 FreeProcInstance(lpProc);
\r
1529 /*---------------------------------------------------------------------------*\
\r
1531 * GDI board drawing routines
\r
1533 \*---------------------------------------------------------------------------*/
\r
1535 /* [AS] Draw square using background texture */
\r
1536 static void DrawTile( int dx, int dy, int dw, int dh, HDC dst, HDC src, int mode, int sx, int sy )
\r
1541 return; /* Should never happen! */
\r
1544 SetGraphicsMode( dst, GM_ADVANCED );
\r
1551 /* X reflection */
\r
1556 x.eDx = (FLOAT) dw + dx - 1;
\r
1559 SetWorldTransform( dst, &x );
\r
1562 /* Y reflection */
\r
1568 x.eDy = (FLOAT) dh + dy - 1;
\r
1570 SetWorldTransform( dst, &x );
\r
1578 x.eDx = (FLOAT) dx;
\r
1579 x.eDy = (FLOAT) dy;
\r
1582 SetWorldTransform( dst, &x );
\r
1586 BitBlt( dst, dx, dy, dw, dh, src, sx, sy, SRCCOPY );
\r
1594 SetWorldTransform( dst, &x );
\r
1596 ModifyWorldTransform( dst, 0, MWT_IDENTITY );
\r
1599 /* [AS] [HGM] Make room for more piece types, so all pieces can be different */
\r
1601 PM_WP = (int) WhitePawn,
\r
1602 PM_WN = (int) WhiteKnight,
\r
1603 PM_WB = (int) WhiteBishop,
\r
1604 PM_WR = (int) WhiteRook,
\r
1605 PM_WQ = (int) WhiteQueen,
\r
1606 PM_WF = (int) WhiteFerz,
\r
1607 PM_WW = (int) WhiteWazir,
\r
1608 PM_WE = (int) WhiteAlfil,
\r
1609 PM_WM = (int) WhiteMan,
\r
1610 PM_WO = (int) WhiteCannon,
\r
1611 PM_WU = (int) WhiteUnicorn,
\r
1612 PM_WH = (int) WhiteNightrider,
\r
1613 PM_WA = (int) WhiteAngel,
\r
1614 PM_WC = (int) WhiteMarshall,
\r
1615 PM_WAB = (int) WhiteCardinal,
\r
1616 PM_WD = (int) WhiteDragon,
\r
1617 PM_WL = (int) WhiteLance,
\r
1618 PM_WS = (int) WhiteCobra,
\r
1619 PM_WV = (int) WhiteFalcon,
\r
1620 PM_WSG = (int) WhiteSilver,
\r
1621 PM_WG = (int) WhiteGrasshopper,
\r
1622 PM_WK = (int) WhiteKing,
\r
1623 PM_BP = (int) BlackPawn,
\r
1624 PM_BN = (int) BlackKnight,
\r
1625 PM_BB = (int) BlackBishop,
\r
1626 PM_BR = (int) BlackRook,
\r
1627 PM_BQ = (int) BlackQueen,
\r
1628 PM_BF = (int) BlackFerz,
\r
1629 PM_BW = (int) BlackWazir,
\r
1630 PM_BE = (int) BlackAlfil,
\r
1631 PM_BM = (int) BlackMan,
\r
1632 PM_BO = (int) BlackCannon,
\r
1633 PM_BU = (int) BlackUnicorn,
\r
1634 PM_BH = (int) BlackNightrider,
\r
1635 PM_BA = (int) BlackAngel,
\r
1636 PM_BC = (int) BlackMarshall,
\r
1637 PM_BG = (int) BlackGrasshopper,
\r
1638 PM_BAB = (int) BlackCardinal,
\r
1639 PM_BD = (int) BlackDragon,
\r
1640 PM_BL = (int) BlackLance,
\r
1641 PM_BS = (int) BlackCobra,
\r
1642 PM_BV = (int) BlackFalcon,
\r
1643 PM_BSG = (int) BlackSilver,
\r
1644 PM_BK = (int) BlackKing
\r
1647 static HFONT hPieceFont = NULL;
\r
1648 static HBITMAP hPieceMask[(int) EmptySquare];
\r
1649 static HBITMAP hPieceFace[(int) EmptySquare];
\r
1650 static int fontBitmapSquareSize = 0;
\r
1651 static char pieceToFontChar[(int) EmptySquare] =
\r
1652 { 'p', 'n', 'b', 'r', 'q',
\r
1653 'n', 'b', 'p', 'n', 'b', 'r', 'b', 'r', 'q', 'k',
\r
1654 'k', 'o', 'm', 'v', 't', 'w',
\r
1655 'v', 't', 'o', 'm', 'v', 't', 'v', 't', 'w', 'l',
\r
1658 extern BOOL SetCharTable( char *table, const char * map );
\r
1659 /* [HGM] moved to backend.c */
\r
1661 static void SetPieceBackground( HDC hdc, COLORREF color, int mode )
\r
1664 BYTE r1 = GetRValue( color );
\r
1665 BYTE g1 = GetGValue( color );
\r
1666 BYTE b1 = GetBValue( color );
\r
1672 /* Create a uniform background first */
\r
1673 hbrush = CreateSolidBrush( color );
\r
1674 SetRect( &rc, 0, 0, squareSize, squareSize );
\r
1675 FillRect( hdc, &rc, hbrush );
\r
1676 DeleteObject( hbrush );
\r
1679 /* Vertical gradient, good for pawn, knight and rook, less for queen and king */
\r
1680 int steps = squareSize / 2;
\r
1683 for( i=0; i<steps; i++ ) {
\r
1684 BYTE r = r1 - (r1-r2) * i / steps;
\r
1685 BYTE g = g1 - (g1-g2) * i / steps;
\r
1686 BYTE b = b1 - (b1-b2) * i / steps;
\r
1688 hbrush = CreateSolidBrush( RGB(r,g,b) );
\r
1689 SetRect( &rc, i + squareSize - steps, 0, i + squareSize - steps + 1, squareSize );
\r
1690 FillRect( hdc, &rc, hbrush );
\r
1691 DeleteObject(hbrush);
\r
1694 else if( mode == 2 ) {
\r
1695 /* Diagonal gradient, good more or less for every piece */
\r
1696 POINT triangle[3];
\r
1697 HPEN hpen = SelectObject( hdc, GetStockObject(NULL_PEN) );
\r
1698 HBRUSH hbrush_old;
\r
1699 int steps = squareSize;
\r
1702 triangle[0].x = squareSize - steps;
\r
1703 triangle[0].y = squareSize;
\r
1704 triangle[1].x = squareSize;
\r
1705 triangle[1].y = squareSize;
\r
1706 triangle[2].x = squareSize;
\r
1707 triangle[2].y = squareSize - steps;
\r
1709 for( i=0; i<steps; i++ ) {
\r
1710 BYTE r = r1 - (r1-r2) * i / steps;
\r
1711 BYTE g = g1 - (g1-g2) * i / steps;
\r
1712 BYTE b = b1 - (b1-b2) * i / steps;
\r
1714 hbrush = CreateSolidBrush( RGB(r,g,b) );
\r
1715 hbrush_old = SelectObject( hdc, hbrush );
\r
1716 Polygon( hdc, triangle, 3 );
\r
1717 SelectObject( hdc, hbrush_old );
\r
1718 DeleteObject(hbrush);
\r
1723 SelectObject( hdc, hpen );
\r
1728 [AS] The method I use to create the bitmaps it a bit tricky, but it
\r
1729 seems to work ok. The main problem here is to find the "inside" of a chess
\r
1730 piece: follow the steps as explained below.
\r
1732 static void CreatePieceMaskFromFont( HDC hdc_window, HDC hdc, int index )
\r
1736 COLORREF chroma = RGB(0xFF,0x00,0xFF);
\r
1740 int backColor = whitePieceColor;
\r
1741 int foreColor = blackPieceColor;
\r
1743 if( index < (int)BlackPawn && appData.fontBackColorWhite != appData.fontForeColorWhite ) {
\r
1744 backColor = appData.fontBackColorWhite;
\r
1745 foreColor = appData.fontForeColorWhite;
\r
1747 else if( index >= (int)BlackPawn && appData.fontBackColorBlack != appData.fontForeColorBlack ) {
\r
1748 backColor = appData.fontBackColorBlack;
\r
1749 foreColor = appData.fontForeColorBlack;
\r
1753 hbm = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );
\r
1755 hbm_old = SelectObject( hdc, hbm );
\r
1759 rc.right = squareSize;
\r
1760 rc.bottom = squareSize;
\r
1762 /* Step 1: background is now black */
\r
1763 FillRect( hdc, &rc, GetStockObject(BLACK_BRUSH) );
\r
1765 GetTextExtentPoint32( hdc, &pieceToFontChar[index], 1, &sz );
\r
1767 pt.x = (squareSize - sz.cx) / 2;
\r
1768 pt.y = (squareSize - sz.cy) / 2;
\r
1770 SetBkMode( hdc, TRANSPARENT );
\r
1771 SetTextColor( hdc, chroma );
\r
1772 /* Step 2: the piece has been drawn in purple, there are now black and purple in this bitmap */
\r
1773 TextOut( hdc, pt.x, pt.y, &pieceToFontChar[appData.allWhite && index >= (int)BlackPawn ? index - (int)BlackPawn : index], 1 );
\r
1775 SelectObject( hdc, GetStockObject(WHITE_BRUSH) );
\r
1776 /* Step 3: the area outside the piece is filled with white */
\r
1777 // FloodFill( hdc, 0, 0, chroma );
\r
1778 ExtFloodFill( hdc, 0, 0, 0, FLOODFILLSURFACE );
\r
1779 ExtFloodFill( hdc, 0, squareSize-1, 0, FLOODFILLSURFACE ); // [HGM] fill from all 4 corners, for if piece too big
\r
1780 ExtFloodFill( hdc, squareSize-1, 0, 0, FLOODFILLSURFACE );
\r
1781 ExtFloodFill( hdc, squareSize-1, squareSize-1, 0, FLOODFILLSURFACE );
\r
1782 SelectObject( hdc, GetStockObject(BLACK_BRUSH) );
\r
1784 Step 4: this is the tricky part, the area inside the piece is filled with black,
\r
1785 but if the start point is not inside the piece we're lost!
\r
1786 There should be a better way to do this... if we could create a region or path
\r
1787 from the fill operation we would be fine for example.
\r
1789 // FloodFill( hdc, squareSize / 2, squareSize / 2, RGB(0xFF,0xFF,0xFF) );
\r
1790 ExtFloodFill( hdc, squareSize / 2, squareSize / 2, RGB(0xFF,0xFF,0xFF), FLOODFILLBORDER );
\r
1792 { /* [HGM] shave off edges of mask, in an attempt to correct for the fact that FloodFill does not work correctly under Win XP */
\r
1793 HDC dc2 = CreateCompatibleDC( hdc_window );
\r
1794 HBITMAP bm2 = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );
\r
1796 SelectObject( dc2, bm2 );
\r
1797 BitBlt( dc2, 0, 0, squareSize, squareSize, hdc, 0, 0, SRCCOPY ); // make copy
\r
1798 BitBlt( hdc, 0, 1, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );
\r
1799 BitBlt( hdc, 2, 1, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );
\r
1800 BitBlt( hdc, 1, 0, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );
\r
1801 BitBlt( hdc, 1, 2, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );
\r
1804 DeleteObject( bm2 );
\r
1807 SetTextColor( hdc, 0 );
\r
1809 Step 5: some fonts have "disconnected" areas that are skipped by the fill:
\r
1810 draw the piece again in black for safety.
\r
1812 TextOut( hdc, pt.x, pt.y, &pieceToFontChar[appData.allWhite && index >= (int)BlackPawn ? index - (int)BlackPawn : index], 1 );
\r
1814 SelectObject( hdc, hbm_old );
\r
1816 if( hPieceMask[index] != NULL ) {
\r
1817 DeleteObject( hPieceMask[index] );
\r
1820 hPieceMask[index] = hbm;
\r
1823 hbm = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );
\r
1825 SelectObject( hdc, hbm );
\r
1828 HDC dc1 = CreateCompatibleDC( hdc_window );
\r
1829 HDC dc2 = CreateCompatibleDC( hdc_window );
\r
1830 HBITMAP bm2 = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );
\r
1832 SelectObject( dc1, hPieceMask[index] );
\r
1833 SelectObject( dc2, bm2 );
\r
1834 FillRect( dc2, &rc, GetStockObject(WHITE_BRUSH) );
\r
1835 BitBlt( dc2, 0, 0, squareSize, squareSize, dc1, 0, 0, SRCINVERT );
\r
1838 Now dc2 contains the inverse of the piece mask, i.e. a mask that preserves
\r
1839 the piece background and deletes (makes transparent) the rest.
\r
1840 Thanks to that mask, we are free to paint the background with the greates
\r
1841 freedom, as we'll be able to mask off the unwanted parts when finished.
\r
1842 We use this, to make gradients and give the pieces a "roundish" look.
\r
1844 SetPieceBackground( hdc, backColor, 2 );
\r
1845 BitBlt( hdc, 0, 0, squareSize, squareSize, dc2, 0, 0, SRCAND );
\r
1849 DeleteObject( bm2 );
\r
1852 SetTextColor( hdc, foreColor );
\r
1853 TextOut( hdc, pt.x, pt.y, &pieceToFontChar[appData.allWhite && index >= (int)BlackPawn ? index - (int)BlackPawn : index], 1 );
\r
1855 SelectObject( hdc, hbm_old );
\r
1857 if( hPieceFace[index] != NULL ) {
\r
1858 DeleteObject( hPieceFace[index] );
\r
1861 hPieceFace[index] = hbm;
\r
1864 static int TranslatePieceToFontPiece( int piece )
\r
1894 case BlackMarshall:
\r
1898 case BlackNightrider:
\r
1904 case BlackUnicorn:
\r
1908 case BlackGrasshopper:
\r
1920 case BlackCardinal:
\r
1927 case WhiteMarshall:
\r
1931 case WhiteNightrider:
\r
1937 case WhiteUnicorn:
\r
1941 case WhiteGrasshopper:
\r
1953 case WhiteCardinal:
\r
1962 void CreatePiecesFromFont()
\r
1965 HDC hdc_window = NULL;
\r
1971 if( fontBitmapSquareSize < 0 ) {
\r
1972 /* Something went seriously wrong in the past: do not try to recreate fonts! */
\r
1976 if( appData.renderPiecesWithFont == NULL || appData.renderPiecesWithFont[0] == NULLCHAR || appData.renderPiecesWithFont[0] == '*' ) {
\r
1977 fontBitmapSquareSize = -1;
\r
1981 if( fontBitmapSquareSize != squareSize ) {
\r
1982 hdc_window = GetDC( hwndMain );
\r
1983 hdc = CreateCompatibleDC( hdc_window );
\r
1985 if( hPieceFont != NULL ) {
\r
1986 DeleteObject( hPieceFont );
\r
1989 for( i=0; i<=(int)BlackKing; i++ ) {
\r
1990 hPieceMask[i] = NULL;
\r
1991 hPieceFace[i] = NULL;
\r
1997 if( appData.fontPieceSize >= 50 && appData.fontPieceSize <= 150 ) {
\r
1998 fontHeight = appData.fontPieceSize;
\r
2001 fontHeight = (fontHeight * squareSize) / 100;
\r
2003 lf.lfHeight = -MulDiv( fontHeight, GetDeviceCaps(hdc, LOGPIXELSY), 72 );
\r
2005 lf.lfEscapement = 0;
\r
2006 lf.lfOrientation = 0;
\r
2007 lf.lfWeight = FW_NORMAL;
\r
2009 lf.lfUnderline = 0;
\r
2010 lf.lfStrikeOut = 0;
\r
2011 lf.lfCharSet = DEFAULT_CHARSET;
\r
2012 lf.lfOutPrecision = OUT_DEFAULT_PRECIS;
\r
2013 lf.lfClipPrecision = CLIP_DEFAULT_PRECIS;
\r
2014 lf.lfQuality = PROOF_QUALITY;
\r
2015 lf.lfPitchAndFamily = DEFAULT_PITCH | FF_DONTCARE;
\r
2016 strncpy( lf.lfFaceName, appData.renderPiecesWithFont, sizeof(lf.lfFaceName) );
\r
2017 lf.lfFaceName[ sizeof(lf.lfFaceName) - 1 ] = '\0';
\r
2019 hPieceFont = CreateFontIndirect( &lf );
\r
2021 if( hPieceFont == NULL ) {
\r
2022 fontBitmapSquareSize = -2;
\r
2025 /* Setup font-to-piece character table */
\r
2026 if( ! SetCharTable(pieceToFontChar, appData.fontToPieceTable) ) {
\r
2027 /* No (or wrong) global settings, try to detect the font */
\r
2028 if( strstr(lf.lfFaceName,"Alpha") != NULL ) {
\r
2030 SetCharTable(pieceToFontChar, "phbrqkojntwl");
\r
2032 else if( strstr(lf.lfFaceName,"DiagramTT") != NULL ) {
\r
2033 /* DiagramTT* family */
\r
2034 SetCharTable(pieceToFontChar, "PNLRQKpnlrqk");
\r
2036 else if( strstr(lf.lfFaceName,"WinboardF") != NULL ) {
\r
2037 /* Fairy symbols */
\r
2038 SetCharTable(pieceToFontChar, "PNBRQFEACWMOHIJGDVSLUKpnbrqfeacwmohijgdvsluk");
\r
2040 else if( strstr(lf.lfFaceName,"GC2004D") != NULL ) {
\r
2041 /* Good Companion (Some characters get warped as literal :-( */
\r
2042 char s[] = "1cmWG0??S??oYI23wgQU";
\r
2043 s[0]=0xB9; s[1]=0xA9; s[6]=0xB1; s[11]=0xBB; s[12]=0xAB; s[17]=0xB3;
\r
2044 SetCharTable(pieceToFontChar, s);
\r
2047 /* Cases, Condal, Leipzig, Lucena, Marroquin, Merida, Usual */
\r
2048 SetCharTable(pieceToFontChar, "pnbrqkomvtwl");
\r
2052 /* Create bitmaps */
\r
2053 hfont_old = SelectObject( hdc, hPieceFont );
\r
2054 for(i=(int)WhitePawn; i<(int)EmptySquare; i++) /* [HGM] made a loop for this */
\r
2055 if(PieceToChar((ChessSquare)i) != '.') /* skip unused pieces */
\r
2056 CreatePieceMaskFromFont( hdc_window, hdc, i );
\r
2058 SelectObject( hdc, hfont_old );
\r
2060 fontBitmapSquareSize = squareSize;
\r
2064 if( hdc != NULL ) {
\r
2068 if( hdc_window != NULL ) {
\r
2069 ReleaseDC( hwndMain, hdc_window );
\r
2074 DoLoadBitmap(HINSTANCE hinst, char *piece, int squareSize, char *suffix)
\r
2078 snprintf(name, sizeof(name)/sizeof(name[0]), "%s%d%s", piece, squareSize, suffix);
\r
2079 if (gameInfo.event &&
\r
2080 strcmp(gameInfo.event, "Easter Egg Hunt") == 0 &&
\r
2081 strcmp(name, "k80s") == 0) {
\r
2082 safeStrCpy(name, "tim", sizeof(name)/sizeof(name[0]) );
\r
2084 return LoadBitmap(hinst, name);
\r
2088 /* Insert a color into the program's logical palette
\r
2089 structure. This code assumes the given color is
\r
2090 the result of the RGB or PALETTERGB macro, and it
\r
2091 knows how those macros work (which is documented).
\r
2094 InsertInPalette(COLORREF color)
\r
2096 LPPALETTEENTRY pe = &(pLogPal->palPalEntry[pLogPal->palNumEntries]);
\r
2098 if (pLogPal->palNumEntries++ >= PALETTESIZE) {
\r
2099 DisplayFatalError(_("Too many colors"), 0, 1);
\r
2100 pLogPal->palNumEntries--;
\r
2104 pe->peFlags = (char) 0;
\r
2105 pe->peRed = (char) (0xFF & color);
\r
2106 pe->peGreen = (char) (0xFF & (color >> 8));
\r
2107 pe->peBlue = (char) (0xFF & (color >> 16));
\r
2113 InitDrawingColors()
\r
2115 if (pLogPal == NULL) {
\r
2116 /* Allocate enough memory for a logical palette with
\r
2117 * PALETTESIZE entries and set the size and version fields
\r
2118 * of the logical palette structure.
\r
2120 pLogPal = (NPLOGPALETTE)
\r
2121 LocalAlloc(LMEM_FIXED, (sizeof(LOGPALETTE) +
\r
2122 (sizeof(PALETTEENTRY) * (PALETTESIZE))));
\r
2123 pLogPal->palVersion = 0x300;
\r
2125 pLogPal->palNumEntries = 0;
\r
2127 InsertInPalette(lightSquareColor);
\r
2128 InsertInPalette(darkSquareColor);
\r
2129 InsertInPalette(whitePieceColor);
\r
2130 InsertInPalette(blackPieceColor);
\r
2131 InsertInPalette(highlightSquareColor);
\r
2132 InsertInPalette(premoveHighlightColor);
\r
2134 /* create a logical color palette according the information
\r
2135 * in the LOGPALETTE structure.
\r
2137 hPal = CreatePalette((LPLOGPALETTE) pLogPal);
\r
2139 lightSquareBrush = CreateSolidBrush(lightSquareColor);
\r
2140 blackSquareBrush = CreateSolidBrush(blackPieceColor);
\r
2141 darkSquareBrush = CreateSolidBrush(darkSquareColor);
\r
2142 whitePieceBrush = CreateSolidBrush(whitePieceColor);
\r
2143 blackPieceBrush = CreateSolidBrush(blackPieceColor);
\r
2144 iconBkgndBrush = CreateSolidBrush(GetSysColor(COLOR_BACKGROUND));
\r
2145 explodeBrush = CreateSolidBrush(highlightSquareColor); // [HGM] atomic
\r
2146 markerBrush = CreateSolidBrush(premoveHighlightColor); // [HGM] markers
\r
2147 /* [AS] Force rendering of the font-based pieces */
\r
2148 if( fontBitmapSquareSize > 0 ) {
\r
2149 fontBitmapSquareSize = 0;
\r
2155 BoardWidth(int boardSize, int n)
\r
2156 { /* [HGM] argument n added to allow different width and height */
\r
2157 int lineGap = sizeInfo[boardSize].lineGap;
\r
2159 if( appData.overrideLineGap >= 0 && appData.overrideLineGap <= 5 ) {
\r
2160 lineGap = appData.overrideLineGap;
\r
2163 return (n + 1) * lineGap +
\r
2164 n * sizeInfo[boardSize].squareSize;
\r
2167 /* Respond to board resize by dragging edge */
\r
2169 ResizeBoard(int newSizeX, int newSizeY, int flags)
\r
2171 BoardSize newSize = NUM_SIZES - 1;
\r
2172 static int recurse = 0;
\r
2173 if (IsIconic(hwndMain)) return;
\r
2174 if (recurse > 0) return;
\r
2176 while (newSize > 0) {
\r
2177 InitDrawingSizes(newSize+1000, 0); // [HGM] kludge to update sizeInfo without visible effects
\r
2178 if(newSizeX >= sizeInfo[newSize].cliWidth &&
\r
2179 newSizeY >= sizeInfo[newSize].cliHeight) break;
\r
2182 boardSize = newSize;
\r
2183 InitDrawingSizes(boardSize, flags);
\r
2188 extern Boolean twoBoards, partnerUp; // [HGM] dual
\r
2191 InitDrawingSizes(BoardSize boardSize, int flags)
\r
2193 int i, boardWidth, boardHeight; /* [HGM] height treated separately */
\r
2194 ChessSquare piece;
\r
2195 static int oldBoardSize = -1, oldTinyLayout = 0;
\r
2197 SIZE clockSize, messageSize;
\r
2199 char buf[MSG_SIZ];
\r
2201 HMENU hmenu = GetMenu(hwndMain);
\r
2202 RECT crect, wrect, oldRect;
\r
2204 LOGBRUSH logbrush;
\r
2206 int suppressVisibleEffects = 0; // [HGM] kludge to request updating sizeInfo only
\r
2207 if((int)boardSize >= 1000 ) { boardSize -= 1000; suppressVisibleEffects = 1; }
\r
2209 /* [HGM] call with -2 uses old size (for if nr of files, ranks changes) */
\r
2210 if(boardSize == (BoardSize)(-2) ) boardSize = oldBoardSize;
\r
2212 oldRect.left = wpMain.x; //[HGM] placement: remember previous window params
\r
2213 oldRect.top = wpMain.y;
\r
2214 oldRect.right = wpMain.x + wpMain.width;
\r
2215 oldRect.bottom = wpMain.y + wpMain.height;
\r
2217 tinyLayout = sizeInfo[boardSize].tinyLayout;
\r
2218 smallLayout = sizeInfo[boardSize].smallLayout;
\r
2219 squareSize = sizeInfo[boardSize].squareSize;
\r
2220 lineGap = sizeInfo[boardSize].lineGap;
\r
2221 minorSize = 0; /* [HGM] Kludge to see if demagnified pieces need to be shifted */
\r
2223 if( appData.overrideLineGap >= 0 && appData.overrideLineGap <= 5 ) {
\r
2224 lineGap = appData.overrideLineGap;
\r
2227 if (tinyLayout != oldTinyLayout) {
\r
2228 long style = GetWindowLongPtr(hwndMain, GWL_STYLE);
\r
2230 style &= ~WS_SYSMENU;
\r
2231 InsertMenu(hmenu, IDM_Exit, MF_BYCOMMAND, IDM_Minimize,
\r
2232 "&Minimize\tCtrl+F4");
\r
2234 style |= WS_SYSMENU;
\r
2235 RemoveMenu(hmenu, IDM_Minimize, MF_BYCOMMAND);
\r
2237 SetWindowLongPtr(hwndMain, GWL_STYLE, style);
\r
2239 for (i=0; menuBarText[tinyLayout][i]; i++) {
\r
2240 ModifyMenu(hmenu, i, MF_STRING|MF_BYPOSITION|MF_POPUP,
\r
2241 (UINT)GetSubMenu(hmenu, i), T_(menuBarText[tinyLayout][i]));
\r
2243 DrawMenuBar(hwndMain);
\r
2246 boardWidth = BoardWidth(boardSize, BOARD_WIDTH);
\r
2247 boardHeight = BoardWidth(boardSize, BOARD_HEIGHT);
\r
2249 /* Get text area sizes */
\r
2250 hdc = GetDC(hwndMain);
\r
2251 if (appData.clockMode) {
\r
2252 snprintf(buf, MSG_SIZ, _("White: %s"), TimeString(23*60*60*1000L));
\r
2254 snprintf(buf, MSG_SIZ, _("White"));
\r
2256 oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);
\r
2257 GetTextExtentPoint(hdc, buf, strlen(buf), &clockSize);
\r
2258 SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);
\r
2259 str = _("We only care about the height here");
\r
2260 GetTextExtentPoint(hdc, str, strlen(str), &messageSize);
\r
2261 SelectObject(hdc, oldFont);
\r
2262 ReleaseDC(hwndMain, hdc);
\r
2264 /* Compute where everything goes */
\r
2265 if((first.programLogo || second.programLogo) && !tinyLayout) {
\r
2266 /* [HGM] logo: if either logo is on, reserve space for it */
\r
2267 logoHeight = 2*clockSize.cy;
\r
2268 leftLogoRect.left = OUTER_MARGIN;
\r
2269 leftLogoRect.right = leftLogoRect.left + 4*clockSize.cy;
\r
2270 leftLogoRect.top = OUTER_MARGIN;
\r
2271 leftLogoRect.bottom = OUTER_MARGIN + logoHeight;
\r
2273 rightLogoRect.right = OUTER_MARGIN + boardWidth;
\r
2274 rightLogoRect.left = rightLogoRect.right - 4*clockSize.cy;
\r
2275 rightLogoRect.top = OUTER_MARGIN;
\r
2276 rightLogoRect.bottom = OUTER_MARGIN + logoHeight;
\r
2279 whiteRect.left = leftLogoRect.right;
\r
2280 whiteRect.right = OUTER_MARGIN + boardWidth/2 - INNER_MARGIN/2;
\r
2281 whiteRect.top = OUTER_MARGIN;
\r
2282 whiteRect.bottom = whiteRect.top + logoHeight;
\r
2284 blackRect.right = rightLogoRect.left;
\r
2285 blackRect.left = whiteRect.right + INNER_MARGIN;
\r
2286 blackRect.top = whiteRect.top;
\r
2287 blackRect.bottom = whiteRect.bottom;
\r
2289 whiteRect.left = OUTER_MARGIN;
\r
2290 whiteRect.right = whiteRect.left + boardWidth/2 - INNER_MARGIN/2;
\r
2291 whiteRect.top = OUTER_MARGIN;
\r
2292 whiteRect.bottom = whiteRect.top + clockSize.cy;
\r
2294 blackRect.left = whiteRect.right + INNER_MARGIN;
\r
2295 blackRect.right = blackRect.left + boardWidth/2 - 1;
\r
2296 blackRect.top = whiteRect.top;
\r
2297 blackRect.bottom = whiteRect.bottom;
\r
2299 logoHeight = 0; // [HGM] logo: suppress logo after change to tiny layout!
\r
2302 messageRect.left = OUTER_MARGIN + MESSAGE_LINE_LEFTMARGIN;
\r
2303 if (appData.showButtonBar) {
\r
2304 messageRect.right = OUTER_MARGIN + boardWidth // [HGM] logo: expressed independent of clock placement
\r
2305 - N_BUTTONS*BUTTON_WIDTH - MESSAGE_LINE_LEFTMARGIN;
\r
2307 messageRect.right = OUTER_MARGIN + boardWidth;
\r
2309 messageRect.top = whiteRect.bottom + INNER_MARGIN;
\r
2310 messageRect.bottom = messageRect.top + messageSize.cy;
\r
2312 boardRect.left = OUTER_MARGIN;
\r
2313 boardRect.right = boardRect.left + boardWidth;
\r
2314 boardRect.top = messageRect.bottom + INNER_MARGIN;
\r
2315 boardRect.bottom = boardRect.top + boardHeight;
\r
2317 sizeInfo[boardSize].cliWidth = boardRect.right + OUTER_MARGIN;
\r
2318 sizeInfo[boardSize].cliHeight = boardRect.bottom + OUTER_MARGIN;
\r
2319 oldBoardSize = boardSize;
\r
2320 oldTinyLayout = tinyLayout;
\r
2321 winW = 2 * GetSystemMetrics(SM_CXFRAME) + boardRect.right + OUTER_MARGIN;
\r
2322 winH = 2 * GetSystemMetrics(SM_CYFRAME) + GetSystemMetrics(SM_CYMENU) +
\r
2323 GetSystemMetrics(SM_CYCAPTION) + boardRect.bottom + OUTER_MARGIN;
\r
2324 winW *= 1 + twoBoards;
\r
2325 if(suppressVisibleEffects) return; // [HGM] when called for filling sizeInfo only
\r
2326 wpMain.width = winW; // [HGM] placement: set through temporary which can used by initial sizing choice
\r
2327 wpMain.height = winH; // without disturbing window attachments
\r
2328 GetWindowRect(hwndMain, &wrect);
\r
2329 SetWindowPos(hwndMain, NULL, 0, 0, wpMain.width, wpMain.height,
\r
2330 SWP_NOCOPYBITS|SWP_NOZORDER|SWP_NOMOVE);
\r
2332 // [HGM] placement: let attached windows follow size change.
\r
2333 ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, moveHistoryDialog, &wpMoveHistory );
\r
2334 ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, evalGraphDialog, &wpEvalGraph );
\r
2335 ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, engineOutputDialog, &wpEngineOutput );
\r
2336 ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, gameListDialog, &wpGameList );
\r
2337 ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, hwndConsole, &wpConsole );
\r
2339 /* compensate if menu bar wrapped */
\r
2340 GetClientRect(hwndMain, &crect);
\r
2341 offby = boardRect.bottom + OUTER_MARGIN - crect.bottom;
\r
2342 wpMain.height += offby;
\r
2344 case WMSZ_TOPLEFT:
\r
2345 SetWindowPos(hwndMain, NULL,
\r
2346 wrect.right - wpMain.width, wrect.bottom - wpMain.height,
\r
2347 wpMain.width, wpMain.height, SWP_NOCOPYBITS|SWP_NOZORDER);
\r
2350 case WMSZ_TOPRIGHT:
\r
2352 SetWindowPos(hwndMain, NULL,
\r
2353 wrect.left, wrect.bottom - wpMain.height,
\r
2354 wpMain.width, wpMain.height, SWP_NOCOPYBITS|SWP_NOZORDER);
\r
2357 case WMSZ_BOTTOMLEFT:
\r
2359 SetWindowPos(hwndMain, NULL,
\r
2360 wrect.right - wpMain.width, wrect.top,
\r
2361 wpMain.width, wpMain.height, SWP_NOCOPYBITS|SWP_NOZORDER);
\r
2364 case WMSZ_BOTTOMRIGHT:
\r
2368 SetWindowPos(hwndMain, NULL, 0, 0, wpMain.width, wpMain.height,
\r
2369 SWP_NOCOPYBITS|SWP_NOZORDER|SWP_NOMOVE);
\r
2374 for (i = 0; i < N_BUTTONS; i++) {
\r
2375 if (buttonDesc[i].hwnd != NULL) {
\r
2376 DestroyWindow(buttonDesc[i].hwnd);
\r
2377 buttonDesc[i].hwnd = NULL;
\r
2379 if (appData.showButtonBar) {
\r
2380 buttonDesc[i].hwnd =
\r
2381 CreateWindow("BUTTON", buttonDesc[i].label,
\r
2382 WS_VISIBLE | WS_CHILD | BS_PUSHBUTTON,
\r
2383 boardRect.right - BUTTON_WIDTH*(N_BUTTONS-i),
\r
2384 messageRect.top, BUTTON_WIDTH, messageSize.cy, hwndMain,
\r
2385 (HMENU) buttonDesc[i].id,
\r
2386 (HINSTANCE) GetWindowLongPtr(hwndMain, GWLP_HINSTANCE), NULL);
\r
2388 SendMessage(buttonDesc[i].hwnd, WM_SETFONT,
\r
2389 (WPARAM)font[boardSize][MESSAGE_FONT]->hf,
\r
2390 MAKELPARAM(FALSE, 0));
\r
2392 if (buttonDesc[i].id == IDM_Pause)
\r
2393 hwndPause = buttonDesc[i].hwnd;
\r
2394 buttonDesc[i].wndproc = (WNDPROC)
\r
2395 SetWindowLongPtr(buttonDesc[i].hwnd, GWLP_WNDPROC, (LONG_PTR) ButtonProc);
\r
2398 if (gridPen != NULL) DeleteObject(gridPen);
\r
2399 if (highlightPen != NULL) DeleteObject(highlightPen);
\r
2400 if (premovePen != NULL) DeleteObject(premovePen);
\r
2401 if (lineGap != 0) {
\r
2402 logbrush.lbStyle = BS_SOLID;
\r
2403 logbrush.lbColor = RGB(0, 0, 0); /* grid pen color = black */
\r
2405 ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,
\r
2406 lineGap, &logbrush, 0, NULL);
\r
2407 logbrush.lbColor = highlightSquareColor;
\r
2409 ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,
\r
2410 lineGap, &logbrush, 0, NULL);
\r
2412 logbrush.lbColor = premoveHighlightColor;
\r
2414 ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,
\r
2415 lineGap, &logbrush, 0, NULL);
\r
2417 /* [HGM] Loop had to be split in part for vert. and hor. lines */
\r
2418 for (i = 0; i < BOARD_HEIGHT + 1; i++) {
\r
2419 gridEndpoints[i*2].x = boardRect.left + lineGap / 2;
\r
2420 gridEndpoints[i*2].y = gridEndpoints[i*2 + 1].y =
\r
2421 boardRect.top + lineGap / 2 + (i * (squareSize + lineGap));
\r
2422 gridEndpoints[i*2 + 1].x = boardRect.left + lineGap / 2 +
\r
2423 BOARD_WIDTH * (squareSize + lineGap);
\r
2424 gridVertexCounts[i*2] = gridVertexCounts[i*2 + 1] = 2;
\r
2426 for (i = 0; i < BOARD_WIDTH + 1; i++) {
\r
2427 gridEndpoints[i*2 + BOARD_HEIGHT*2 + 2].y = boardRect.top + lineGap / 2;
\r
2428 gridEndpoints[i*2 + BOARD_HEIGHT*2 + 2].x =
\r
2429 gridEndpoints[i*2 + 1 + BOARD_HEIGHT*2 + 2].x = boardRect.left +
\r
2430 lineGap / 2 + (i * (squareSize + lineGap));
\r
2431 gridEndpoints[i*2 + 1 + BOARD_HEIGHT*2 + 2].y =
\r
2432 boardRect.top + BOARD_HEIGHT * (squareSize + lineGap);
\r
2433 gridVertexCounts[i*2] = gridVertexCounts[i*2 + 1] = 2;
\r
2437 /* [HGM] Licensing requirement */
\r
2439 if(gameInfo.variant == VariantGothic) GothicPopUp( GOTHIC, VariantGothic); else
\r
2442 if(gameInfo.variant == VariantFalcon) GothicPopUp( FALCON, VariantFalcon); else
\r
2444 GothicPopUp( "", VariantNormal);
\r
2447 /* if (boardSize == oldBoardSize) return; [HGM] variant might have changed */
\r
2449 /* Load piece bitmaps for this board size */
\r
2450 for (i=0; i<=2; i++) {
\r
2451 for (piece = WhitePawn;
\r
2452 (int) piece < (int) BlackPawn;
\r
2453 piece = (ChessSquare) ((int) piece + 1)) {
\r
2454 if (pieceBitmap[i][piece] != NULL)
\r
2455 DeleteObject(pieceBitmap[i][piece]);
\r
2459 fontBitmapSquareSize = 0; /* [HGM] render: make sure pieces will be recreated, as we might need others now */
\r
2460 // Orthodox Chess pieces
\r
2461 pieceBitmap[0][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "s");
\r
2462 pieceBitmap[0][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "s");
\r
2463 pieceBitmap[0][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "s");
\r
2464 pieceBitmap[0][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "s");
\r
2465 pieceBitmap[0][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "s");
\r
2466 pieceBitmap[1][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "o");
\r
2467 pieceBitmap[1][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "o");
\r
2468 pieceBitmap[1][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "o");
\r
2469 pieceBitmap[1][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "o");
\r
2470 pieceBitmap[1][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "o");
\r
2471 pieceBitmap[2][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "w");
\r
2472 pieceBitmap[2][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "w");
\r
2473 pieceBitmap[2][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "w");
\r
2474 pieceBitmap[2][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "w");
\r
2475 pieceBitmap[2][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "w");
\r
2476 if( gameInfo.variant == VariantShogi && squareSize <= 72 && squareSize >= 33) {
\r
2477 // in Shogi, Hijack the unused Queen for Lance
\r
2478 pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "s");
\r
2479 pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "o");
\r
2480 pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "w");
\r
2482 pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "s");
\r
2483 pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "o");
\r
2484 pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "w");
\r
2487 if(squareSize <= 72 && squareSize >= 33) {
\r
2488 /* A & C are available in most sizes now */
\r
2489 if(squareSize != 49 && squareSize != 72 && squareSize != 33) { // Vortex-like
\r
2490 pieceBitmap[0][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "s");
\r
2491 pieceBitmap[1][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "o");
\r
2492 pieceBitmap[2][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "w");
\r
2493 pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "s");
\r
2494 pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "o");
\r
2495 pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "w");
\r
2496 pieceBitmap[0][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "s");
\r
2497 pieceBitmap[1][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "o");
\r
2498 pieceBitmap[2][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "w");
\r
2499 pieceBitmap[0][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "s");
\r
2500 pieceBitmap[1][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "o");
\r
2501 pieceBitmap[2][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "w");
\r
2502 } else { // Smirf-like
\r
2503 if(gameInfo.variant == VariantSChess) {
\r
2504 pieceBitmap[0][WhiteAngel] = DoLoadBitmap(hInst, "v", squareSize, "s");
\r
2505 pieceBitmap[1][WhiteAngel] = DoLoadBitmap(hInst, "v", squareSize, "o");
\r
2506 pieceBitmap[2][WhiteAngel] = DoLoadBitmap(hInst, "v", squareSize, "w");
\r
2508 pieceBitmap[0][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "s");
\r
2509 pieceBitmap[1][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "o");
\r
2510 pieceBitmap[2][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "w");
\r
2513 if(gameInfo.variant == VariantGothic) { // Vortex-like
\r
2514 pieceBitmap[0][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "s");
\r
2515 pieceBitmap[1][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "o");
\r
2516 pieceBitmap[2][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "w");
\r
2517 } else if(gameInfo.variant == VariantSChess && (squareSize == 49 || squareSize == 72)) {
\r
2518 pieceBitmap[0][WhiteMarshall] = DoLoadBitmap(hInst, "e", squareSize, "s");
\r
2519 pieceBitmap[1][WhiteMarshall] = DoLoadBitmap(hInst, "e", squareSize, "o");
\r
2520 pieceBitmap[2][WhiteMarshall] = DoLoadBitmap(hInst, "e", squareSize, "w");
\r
2521 } else { // WinBoard standard
\r
2522 pieceBitmap[0][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "s");
\r
2523 pieceBitmap[1][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "o");
\r
2524 pieceBitmap[2][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "w");
\r
2529 if(squareSize==72 || squareSize==49 || squareSize==33) { /* experiment with some home-made bitmaps */
\r
2530 pieceBitmap[0][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "s");
\r
2531 pieceBitmap[1][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "o");
\r
2532 pieceBitmap[2][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "w");
\r
2533 pieceBitmap[0][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "s");
\r
2534 pieceBitmap[1][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "o");
\r
2535 pieceBitmap[2][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "w");
\r
2536 pieceBitmap[0][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "s");
\r
2537 pieceBitmap[1][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "o");
\r
2538 pieceBitmap[2][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "w");
\r
2539 pieceBitmap[0][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "s");
\r
2540 pieceBitmap[1][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "o");
\r
2541 pieceBitmap[2][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "w");
\r
2542 pieceBitmap[0][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "s");
\r
2543 pieceBitmap[1][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "o");
\r
2544 pieceBitmap[2][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "w");
\r
2545 pieceBitmap[0][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "s");
\r
2546 pieceBitmap[1][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "o");
\r
2547 pieceBitmap[2][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "w");
\r
2548 pieceBitmap[0][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "s");
\r
2549 pieceBitmap[1][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "o");
\r
2550 pieceBitmap[2][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "w");
\r
2551 pieceBitmap[0][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "s");
\r
2552 pieceBitmap[1][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "o");
\r
2553 pieceBitmap[2][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "w");
\r
2554 pieceBitmap[0][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "s");
\r
2555 pieceBitmap[1][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "o");
\r
2556 pieceBitmap[2][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "w");
\r
2557 pieceBitmap[0][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "s");
\r
2558 pieceBitmap[1][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "o");
\r
2559 pieceBitmap[2][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "w");
\r
2561 if(gameInfo.variant == VariantShogi) { /* promoted Gold represemtations */
\r
2562 pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "wp", squareSize, "s");
\r
2563 pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "wp", squareSize, "o");
\r
2564 pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "w", squareSize, "w");
\r
2565 pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "wn", squareSize, "s");
\r
2566 pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "wn", squareSize, "o");
\r
2567 pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "w", squareSize, "w");
\r
2568 pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "ws", squareSize, "s");
\r
2569 pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "ws", squareSize, "o");
\r
2570 pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "w", squareSize, "w");
\r
2571 pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "wl", squareSize, "s");
\r
2572 pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "wl", squareSize, "o");
\r
2573 pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "w", squareSize, "w");
\r
2575 pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "s");
\r
2576 pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "o");
\r
2577 pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "w");
\r
2578 pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "s");
\r
2579 pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "o");
\r
2580 pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "w");
\r
2581 pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "s");
\r
2582 pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "o");
\r
2583 pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "w");
\r
2584 pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "s");
\r
2585 pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "o");
\r
2586 pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "w");
\r
2589 } else { /* other size, no special bitmaps available. Use smaller symbols */
\r
2590 if((int)boardSize < 2) minorSize = sizeInfo[0].squareSize;
\r
2591 else minorSize = sizeInfo[(int)boardSize - 2].squareSize;
\r
2592 pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "s");
\r
2593 pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "o");
\r
2594 pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "w");
\r
2595 pieceBitmap[0][WhiteCardinal] = DoLoadBitmap(hInst, "b", minorSize, "s");
\r
2596 pieceBitmap[1][WhiteCardinal] = DoLoadBitmap(hInst, "b", minorSize, "o");
\r
2597 pieceBitmap[2][WhiteCardinal] = DoLoadBitmap(hInst, "b", minorSize, "w");
\r
2598 pieceBitmap[0][WhiteDragon] = DoLoadBitmap(hInst, "r", minorSize, "s");
\r
2599 pieceBitmap[1][WhiteDragon] = DoLoadBitmap(hInst, "r", minorSize, "o");
\r
2600 pieceBitmap[2][WhiteDragon] = DoLoadBitmap(hInst, "r", minorSize, "w");
\r
2601 pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "s");
\r
2602 pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "o");
\r
2603 pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "w");
\r
2607 if(gameInfo.variant == VariantShogi && squareSize == 58)
\r
2608 /* special Shogi support in this size */
\r
2609 { for (i=0; i<=2; i++) { /* replace all bitmaps */
\r
2610 for (piece = WhitePawn;
\r
2611 (int) piece < (int) BlackPawn;
\r
2612 piece = (ChessSquare) ((int) piece + 1)) {
\r
2613 if (pieceBitmap[i][piece] != NULL)
\r
2614 DeleteObject(pieceBitmap[i][piece]);
\r
2617 pieceBitmap[0][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "o");
\r
2618 pieceBitmap[0][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "o");
\r
2619 pieceBitmap[0][WhiteBishop] = DoLoadBitmap(hInst, "sb", squareSize, "o");
\r
2620 pieceBitmap[0][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "o");
\r
2621 pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "o");
\r
2622 pieceBitmap[0][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "o");
\r
2623 pieceBitmap[0][WhiteFerz] = DoLoadBitmap(hInst, "sf", squareSize, "o");
\r
2624 pieceBitmap[0][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "o");
\r
2625 pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "su", squareSize, "o");
\r
2626 pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "sh", squareSize, "o");
\r
2627 pieceBitmap[0][WhiteCardinal] = DoLoadBitmap(hInst, "sa", squareSize, "o");
\r
2628 pieceBitmap[0][WhiteDragon] = DoLoadBitmap(hInst, "sc", squareSize, "o");
\r
2629 pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "sg", squareSize, "o");
\r
2630 pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "ss", squareSize, "o");
\r
2631 pieceBitmap[1][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "o");
\r
2632 pieceBitmap[1][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "o");
\r
2633 pieceBitmap[1][WhiteBishop] = DoLoadBitmap(hInst, "sb", squareSize, "o");
\r
2634 pieceBitmap[1][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "o");
\r
2635 pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "o");
\r
2636 pieceBitmap[1][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "o");
\r
2637 pieceBitmap[1][WhiteFerz] = DoLoadBitmap(hInst, "sf", squareSize, "o");
\r
2638 pieceBitmap[1][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "o");
\r
2639 pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "su", squareSize, "o");
\r
2640 pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "sh", squareSize, "o");
\r
2641 pieceBitmap[1][WhiteCardinal] = DoLoadBitmap(hInst, "sa", squareSize, "o");
\r
2642 pieceBitmap[1][WhiteDragon] = DoLoadBitmap(hInst, "sc", squareSize, "o");
\r
2643 pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "sg", squareSize, "o");
\r
2644 pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "ss", squareSize, "o");
\r
2645 pieceBitmap[2][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "w");
\r
2646 pieceBitmap[2][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "w");
\r
2647 pieceBitmap[2][WhiteBishop] = DoLoadBitmap(hInst, "sr", squareSize, "w");
\r
2648 pieceBitmap[2][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "w");
\r
2649 pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "w");
\r
2650 pieceBitmap[2][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "w");
\r
2651 pieceBitmap[2][WhiteFerz] = DoLoadBitmap(hInst, "sw", squareSize, "w");
\r
2652 pieceBitmap[2][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "w");
\r
2653 pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "sp", squareSize, "w");
\r
2654 pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "sn", squareSize, "w");
\r
2655 pieceBitmap[2][WhiteCardinal] = DoLoadBitmap(hInst, "sr", squareSize, "w");
\r
2656 pieceBitmap[2][WhiteDragon] = DoLoadBitmap(hInst, "sr", squareSize, "w");
\r
2657 pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "sl", squareSize, "w");
\r
2658 pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "sw", squareSize, "w");
\r
2664 PieceBitmap(ChessSquare p, int kind)
\r
2666 if ((int) p >= (int) BlackPawn)
\r
2667 p = (ChessSquare) ((int) p - (int) BlackPawn + (int) WhitePawn);
\r
2669 return pieceBitmap[kind][(int) p];
\r
2672 /***************************************************************/
\r
2674 #define MIN(a,b) (((a) < (b)) ? (a) : (b))
\r
2675 #define MAX(a,b) (((a) > (b)) ? (a) : (b))
\r
2677 #define MIN3(a,b,c) (((a) < (b) && (a) < (c)) ? (a) : (((b) < (a) && (b) < (c)) ? (b) : (c)))
\r
2678 #define MAX3(a,b,c) (((a) > (b) && (a) > (c)) ? (a) : (((b) > (a) && (b) > (c)) ? (b) : (c)))
\r
2682 SquareToPos(int row, int column, int * x, int * y)
\r
2685 *x = boardRect.left + lineGap + ((BOARD_WIDTH-1)-column) * (squareSize + lineGap);
\r
2686 *y = boardRect.top + lineGap + row * (squareSize + lineGap);
\r
2688 *x = boardRect.left + lineGap + column * (squareSize + lineGap);
\r
2689 *y = boardRect.top + lineGap + ((BOARD_HEIGHT-1)-row) * (squareSize + lineGap);
\r
2694 DrawCoordsOnDC(HDC hdc)
\r
2696 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
2697 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
2698 char str[2] = { NULLCHAR, NULLCHAR };
\r
2699 int oldMode, oldAlign, x, y, start, i;
\r
2703 if (!appData.showCoords)
\r
2706 start = flipView ? 1-(ONE!='1') : 23+(ONE!='1')-BOARD_HEIGHT;
\r
2708 oldBrush = SelectObject(hdc, GetStockObject(BLACK_BRUSH));
\r
2709 oldMode = SetBkMode(hdc, (appData.monoMode ? OPAQUE : TRANSPARENT));
\r
2710 oldAlign = GetTextAlign(hdc);
\r
2711 oldFont = SelectObject(hdc, font[boardSize][COORD_FONT]->hf);
\r
2713 y = boardRect.top + lineGap;
\r
2714 x = boardRect.left + lineGap + gameInfo.holdingsWidth*(squareSize + lineGap);
\r
2716 SetTextAlign(hdc, TA_LEFT|TA_TOP);
\r
2717 for (i = 0; i < BOARD_HEIGHT; i++) {
\r
2718 str[0] = files[start + i];
\r
2719 ExtTextOut(hdc, x + 2, y + 1, 0, NULL, str, 1, NULL);
\r
2720 y += squareSize + lineGap;
\r
2723 start = flipView ? 12-(BOARD_RGHT-BOARD_LEFT) : 12;
\r
2725 SetTextAlign(hdc, TA_RIGHT|TA_BOTTOM);
\r
2726 for (i = 0; i < BOARD_RGHT - BOARD_LEFT; i++) {
\r
2727 str[0] = ranks[start + i];
\r
2728 ExtTextOut(hdc, x + squareSize - 2, y - 1, 0, NULL, str, 1, NULL);
\r
2729 x += squareSize + lineGap;
\r
2732 SelectObject(hdc, oldBrush);
\r
2733 SetBkMode(hdc, oldMode);
\r
2734 SetTextAlign(hdc, oldAlign);
\r
2735 SelectObject(hdc, oldFont);
\r
2739 DrawGridOnDC(HDC hdc)
\r
2743 if (lineGap != 0) {
\r
2744 oldPen = SelectObject(hdc, gridPen);
\r
2745 PolyPolyline(hdc, gridEndpoints, gridVertexCounts, BOARD_WIDTH+BOARD_HEIGHT + 2);
\r
2746 SelectObject(hdc, oldPen);
\r
2750 #define HIGHLIGHT_PEN 0
\r
2751 #define PREMOVE_PEN 1
\r
2754 DrawHighlightOnDC(HDC hdc, BOOLEAN on, int x, int y, int pen)
\r
2757 HPEN oldPen, hPen;
\r
2758 if (lineGap == 0) return;
\r
2760 x1 = boardRect.left +
\r
2761 lineGap/2 + ((BOARD_WIDTH-1)-x) * (squareSize + lineGap);
\r
2762 y1 = boardRect.top +
\r
2763 lineGap/2 + y * (squareSize + lineGap);
\r
2765 x1 = boardRect.left +
\r
2766 lineGap/2 + x * (squareSize + lineGap);
\r
2767 y1 = boardRect.top +
\r
2768 lineGap/2 + ((BOARD_HEIGHT-1)-y) * (squareSize + lineGap);
\r
2770 hPen = pen ? premovePen : highlightPen;
\r
2771 oldPen = SelectObject(hdc, on ? hPen : gridPen);
\r
2772 MoveToEx(hdc, x1, y1, NULL);
\r
2773 LineTo(hdc, x1 + squareSize + lineGap, y1);
\r
2774 LineTo(hdc, x1 + squareSize + lineGap, y1 + squareSize + lineGap);
\r
2775 LineTo(hdc, x1, y1 + squareSize + lineGap);
\r
2776 LineTo(hdc, x1, y1);
\r
2777 SelectObject(hdc, oldPen);
\r
2781 DrawHighlightsOnDC(HDC hdc, HighlightInfo *h, int pen)
\r
2784 for (i=0; i<2; i++) {
\r
2785 if (h->sq[i].x >= 0 && h->sq[i].y >= 0)
\r
2786 DrawHighlightOnDC(hdc, TRUE,
\r
2787 h->sq[i].x, h->sq[i].y,
\r
2792 /* Note: sqcolor is used only in monoMode */
\r
2793 /* Note that this code is largely duplicated in woptions.c,
\r
2794 function DrawSampleSquare, so that needs to be updated too */
\r
2796 DrawPieceOnDC(HDC hdc, ChessSquare piece, int color, int sqcolor, int x, int y, HDC tmphdc)
\r
2798 HBITMAP oldBitmap;
\r
2802 if (appData.blindfold) return;
\r
2804 /* [AS] Use font-based pieces if needed */
\r
2805 if( fontBitmapSquareSize >= 0 && (squareSize > 32 || gameInfo.variant >= VariantShogi)) {
\r
2806 /* Create piece bitmaps, or do nothing if piece set is up to date */
\r
2807 CreatePiecesFromFont();
\r
2809 if( fontBitmapSquareSize == squareSize ) {
\r
2810 int index = TranslatePieceToFontPiece(piece);
\r
2812 SelectObject( tmphdc, hPieceMask[ index ] );
\r
2814 if(appData.upsideDown ? color==flipView : (flipView && gameInfo.variant == VariantShogi))
\r
2815 StretchBlt(hdc, x+squareSize, y+squareSize, -squareSize, -squareSize, tmphdc, 0, 0, squareSize, squareSize, SRCAND);
\r
2819 squareSize, squareSize,
\r
2824 SelectObject( tmphdc, hPieceFace[ index ] );
\r
2826 if(appData.upsideDown ? color==flipView : (flipView && gameInfo.variant == VariantShogi))
\r
2827 StretchBlt(hdc, x+squareSize, y+squareSize, -squareSize, -squareSize, tmphdc, 0, 0, squareSize, squareSize, SRCPAINT);
\r
2831 squareSize, squareSize,
\r
2840 if (appData.monoMode) {
\r
2841 SelectObject(tmphdc, PieceBitmap(piece,
\r
2842 color == sqcolor ? OUTLINE_PIECE : SOLID_PIECE));
\r
2843 BitBlt(hdc, x, y, squareSize, squareSize, tmphdc, 0, 0,
\r
2844 sqcolor ? SRCCOPY : NOTSRCCOPY);
\r
2846 tmpSize = squareSize;
\r
2848 ((piece >= (int)WhiteNightrider && piece <= WhiteGrasshopper) ||
\r
2849 (piece >= (int)BlackNightrider && piece <= BlackGrasshopper)) ) {
\r
2850 /* [HGM] no bitmap available for promoted pieces in Crazyhouse */
\r
2851 /* Bitmaps of smaller size are substituted, but we have to align them */
\r
2852 x += (squareSize - minorSize)>>1;
\r
2853 y += squareSize - minorSize - 2;
\r
2854 tmpSize = minorSize;
\r
2856 if (color || appData.allWhite ) {
\r
2857 oldBitmap = SelectObject(tmphdc, PieceBitmap(piece, WHITE_PIECE));
\r
2859 oldBrush = SelectObject(hdc, whitePieceBrush);
\r
2860 else oldBrush = SelectObject(hdc, blackPieceBrush);
\r
2861 if(appData.upsideDown && color==flipView)
\r
2862 StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, 0x00B8074A);
\r
2864 BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, 0x00B8074A);
\r
2865 /* Use black for outline of white pieces */
\r
2866 SelectObject(tmphdc, PieceBitmap(piece, OUTLINE_PIECE));
\r
2867 if(appData.upsideDown && color==flipView)
\r
2868 StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, SRCAND);
\r
2870 BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, SRCAND);
\r
2872 /* Use square color for details of black pieces */
\r
2873 oldBitmap = SelectObject(tmphdc, PieceBitmap(piece, SOLID_PIECE));
\r
2874 oldBrush = SelectObject(hdc, blackPieceBrush);
\r
2875 if(appData.upsideDown && !flipView)
\r
2876 StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, 0x00B8074A);
\r
2878 BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, 0x00B8074A);
\r
2880 SelectObject(hdc, oldBrush);
\r
2881 SelectObject(tmphdc, oldBitmap);
\r
2885 /* [AS] Compute a drawing mode for a square, based on specified settings (see DrawTile) */
\r
2886 int GetBackTextureMode( int algo )
\r
2888 int result = BACK_TEXTURE_MODE_DISABLED;
\r
2892 case BACK_TEXTURE_MODE_PLAIN:
\r
2893 result = 1; /* Always use identity map */
\r
2895 case BACK_TEXTURE_MODE_FULL_RANDOM:
\r
2896 result = 1 + (myrandom() % 3); /* Pick a transformation at random */
\r
2904 [AS] Compute and save texture drawing info, otherwise we may not be able
\r
2905 to handle redraws cleanly (as random numbers would always be different).
\r
2907 VOID RebuildTextureSquareInfo()
\r
2917 ZeroMemory( &backTextureSquareInfo, sizeof(backTextureSquareInfo) );
\r
2919 if( liteBackTexture != NULL ) {
\r
2920 if( GetObject( liteBackTexture, sizeof(bi), &bi ) > 0 ) {
\r
2921 lite_w = bi.bmWidth;
\r
2922 lite_h = bi.bmHeight;
\r
2926 if( darkBackTexture != NULL ) {
\r
2927 if( GetObject( darkBackTexture, sizeof(bi), &bi ) > 0 ) {
\r
2928 dark_w = bi.bmWidth;
\r
2929 dark_h = bi.bmHeight;
\r
2933 for( row=0; row<BOARD_HEIGHT; row++ ) {
\r
2934 for( col=0; col<BOARD_WIDTH; col++ ) {
\r
2935 if( (col + row) & 1 ) {
\r
2937 if( lite_w >= squareSize && lite_h >= squareSize ) {
\r
2938 if( lite_w >= squareSize*BOARD_WIDTH )
\r
2939 backTextureSquareInfo[row][col].x = (2*col+1)*lite_w/(2*BOARD_WIDTH) - squareSize/2; /* [HGM] cut out of center of virtual square */
\r
2941 backTextureSquareInfo[row][col].x = col * (lite_w - squareSize) / (BOARD_WIDTH-1); /* [HGM] divide by size-1 in stead of size! */
\r
2942 if( lite_h >= squareSize*BOARD_HEIGHT )
\r
2943 backTextureSquareInfo[row][col].y = (2*(BOARD_HEIGHT-row)-1)*lite_h/(2*BOARD_HEIGHT) - squareSize/2;
\r
2945 backTextureSquareInfo[row][col].y = (BOARD_HEIGHT-1-row) * (lite_h - squareSize) / (BOARD_HEIGHT-1);
\r
2946 backTextureSquareInfo[row][col].mode = GetBackTextureMode(liteBackTextureMode);
\r
2951 if( dark_w >= squareSize && dark_h >= squareSize ) {
\r
2952 if( dark_w >= squareSize*BOARD_WIDTH )
\r
2953 backTextureSquareInfo[row][col].x = (2*col+1) * dark_w / (2*BOARD_WIDTH) - squareSize/2;
\r
2955 backTextureSquareInfo[row][col].x = col * (dark_w - squareSize) / (BOARD_WIDTH-1);
\r
2956 if( dark_h >= squareSize*BOARD_HEIGHT )
\r
2957 backTextureSquareInfo[row][col].y = (2*(BOARD_HEIGHT-row)-1) * dark_h / (2*BOARD_HEIGHT) - squareSize/2;
\r
2959 backTextureSquareInfo[row][col].y = (BOARD_HEIGHT-1-row) * (dark_h - squareSize) / (BOARD_HEIGHT-1);
\r
2960 backTextureSquareInfo[row][col].mode = GetBackTextureMode(darkBackTextureMode);
\r
2967 /* [AS] Arrow highlighting support */
\r
2969 static double A_WIDTH = 5; /* Width of arrow body */
\r
2971 #define A_HEIGHT_FACTOR 6 /* Length of arrow "point", relative to body width */
\r
2972 #define A_WIDTH_FACTOR 3 /* Width of arrow "point", relative to body width */
\r
2974 static double Sqr( double x )
\r
2979 static int Round( double x )
\r
2981 return (int) (x + 0.5);
\r
2984 /* Draw an arrow between two points using current settings */
\r
2985 VOID DrawArrowBetweenPoints( HDC hdc, int s_x, int s_y, int d_x, int d_y )
\r
2988 double dx, dy, j, k, x, y;
\r
2990 if( d_x == s_x ) {
\r
2991 int h = (d_y > s_y) ? +A_WIDTH*A_HEIGHT_FACTOR : -A_WIDTH*A_HEIGHT_FACTOR;
\r
2993 arrow[0].x = s_x + A_WIDTH + 0.5;
\r
2996 arrow[1].x = s_x + A_WIDTH + 0.5;
\r
2997 arrow[1].y = d_y - h;
\r
2999 arrow[2].x = arrow[1].x + A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;
\r
3000 arrow[2].y = d_y - h;
\r
3005 arrow[5].x = arrow[1].x - 2*A_WIDTH + 0.5;
\r
3006 arrow[5].y = d_y - h;
\r
3008 arrow[4].x = arrow[5].x - A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;
\r
3009 arrow[4].y = d_y - h;
\r
3011 arrow[6].x = arrow[1].x - 2*A_WIDTH + 0.5;
\r
3014 else if( d_y == s_y ) {
\r
3015 int w = (d_x > s_x) ? +A_WIDTH*A_HEIGHT_FACTOR : -A_WIDTH*A_HEIGHT_FACTOR;
\r
3018 arrow[0].y = s_y + A_WIDTH + 0.5;
\r
3020 arrow[1].x = d_x - w;
\r
3021 arrow[1].y = s_y + A_WIDTH + 0.5;
\r
3023 arrow[2].x = d_x - w;
\r
3024 arrow[2].y = arrow[1].y + A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;
\r
3029 arrow[5].x = d_x - w;
\r
3030 arrow[5].y = arrow[1].y - 2*A_WIDTH + 0.5;
\r
3032 arrow[4].x = d_x - w;
\r
3033 arrow[4].y = arrow[5].y - A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;
\r
3036 arrow[6].y = arrow[1].y - 2*A_WIDTH + 0.5;
\r
3039 /* [AS] Needed a lot of paper for this! :-) */
\r
3040 dy = (double) (d_y - s_y) / (double) (d_x - s_x);
\r
3041 dx = (double) (s_x - d_x) / (double) (s_y - d_y);
\r
3043 j = sqrt( Sqr(A_WIDTH) / (1.0 + Sqr(dx)) );
\r
3045 k = sqrt( Sqr(A_WIDTH*A_HEIGHT_FACTOR) / (1.0 + Sqr(dy)) );
\r
3050 arrow[0].x = Round(x - j);
\r
3051 arrow[0].y = Round(y + j*dx);
\r
3053 arrow[1].x = Round(arrow[0].x + 2*j); // [HGM] prevent width to be affected by rounding twice
\r
3054 arrow[1].y = Round(arrow[0].y - 2*j*dx);
\r
3057 x = (double) d_x - k;
\r
3058 y = (double) d_y - k*dy;
\r
3061 x = (double) d_x + k;
\r
3062 y = (double) d_y + k*dy;
\r
3065 x = Round(x); y = Round(y); // [HGM] make sure width of shaft is rounded the same way on both ends
\r
3067 arrow[6].x = Round(x - j);
\r
3068 arrow[6].y = Round(y + j*dx);
\r
3070 arrow[2].x = Round(arrow[6].x + 2*j);
\r
3071 arrow[2].y = Round(arrow[6].y - 2*j*dx);
\r
3073 arrow[3].x = Round(arrow[2].x + j*(A_WIDTH_FACTOR-1));
\r
3074 arrow[3].y = Round(arrow[2].y - j*(A_WIDTH_FACTOR-1)*dx);
\r
3079 arrow[5].x = Round(arrow[6].x - j*(A_WIDTH_FACTOR-1));
\r
3080 arrow[5].y = Round(arrow[6].y + j*(A_WIDTH_FACTOR-1)*dx);
\r
3083 Polygon( hdc, arrow, 7 );
\r
3086 /* [AS] Draw an arrow between two squares */
\r
3087 VOID DrawArrowBetweenSquares( HDC hdc, int s_col, int s_row, int d_col, int d_row )
\r
3089 int s_x, s_y, d_x, d_y;
\r
3096 if( s_col == d_col && s_row == d_row ) {
\r
3100 /* Get source and destination points */
\r
3101 SquareToPos( s_row, s_col, &s_x, &s_y);
\r
3102 SquareToPos( d_row, d_col, &d_x, &d_y);
\r
3105 d_y += squareSize / 2 - squareSize / 4; // [HGM] round towards same centers on all sides!
\r
3107 else if( d_y < s_y ) {
\r
3108 d_y += squareSize / 2 + squareSize / 4;
\r
3111 d_y += squareSize / 2;
\r
3115 d_x += squareSize / 2 - squareSize / 4;
\r
3117 else if( d_x < s_x ) {
\r
3118 d_x += squareSize / 2 + squareSize / 4;
\r
3121 d_x += squareSize / 2;
\r
3124 s_x += squareSize / 2;
\r
3125 s_y += squareSize / 2;
\r
3127 /* Adjust width */
\r
3128 A_WIDTH = squareSize / 14.; //[HGM] make float
\r
3131 stLB.lbStyle = BS_SOLID;
\r
3132 stLB.lbColor = appData.highlightArrowColor;
\r
3135 hpen = CreatePen( PS_SOLID, 2, RGB(0x00,0x00,0x00) );
\r
3136 holdpen = SelectObject( hdc, hpen );
\r
3137 hbrush = CreateBrushIndirect( &stLB );
\r
3138 holdbrush = SelectObject( hdc, hbrush );
\r
3140 DrawArrowBetweenPoints( hdc, s_x, s_y, d_x, d_y );
\r
3142 SelectObject( hdc, holdpen );
\r
3143 SelectObject( hdc, holdbrush );
\r
3144 DeleteObject( hpen );
\r
3145 DeleteObject( hbrush );
\r
3148 BOOL HasHighlightInfo()
\r
3150 BOOL result = FALSE;
\r
3152 if( highlightInfo.sq[0].x >= 0 && highlightInfo.sq[0].y >= 0 &&
\r
3153 highlightInfo.sq[1].x >= 0 && highlightInfo.sq[1].y >= 0 )
\r
3161 BOOL IsDrawArrowEnabled()
\r
3163 BOOL result = FALSE;
\r
3165 if( appData.highlightMoveWithArrow && squareSize >= 32 ) {
\r
3172 VOID DrawArrowHighlight( HDC hdc )
\r
3174 if( IsDrawArrowEnabled() && HasHighlightInfo() ) {
\r
3175 DrawArrowBetweenSquares( hdc,
\r
3176 highlightInfo.sq[0].x, highlightInfo.sq[0].y,
\r
3177 highlightInfo.sq[1].x, highlightInfo.sq[1].y );
\r
3181 HRGN GetArrowHighlightClipRegion( HDC hdc )
\r
3183 HRGN result = NULL;
\r
3185 if( HasHighlightInfo() ) {
\r
3186 int x1, y1, x2, y2;
\r
3187 int sx, sy, dx, dy;
\r
3189 SquareToPos(highlightInfo.sq[0].y, highlightInfo.sq[0].x, &x1, &y1 );
\r
3190 SquareToPos(highlightInfo.sq[1].y, highlightInfo.sq[1].x, &x2, &y2 );
\r
3192 sx = MIN( x1, x2 );
\r
3193 sy = MIN( y1, y2 );
\r
3194 dx = MAX( x1, x2 ) + squareSize;
\r
3195 dy = MAX( y1, y2 ) + squareSize;
\r
3197 result = CreateRectRgn( sx, sy, dx, dy );
\r
3204 Warning: this function modifies the behavior of several other functions.
\r
3206 Basically, Winboard is optimized to avoid drawing the whole board if not strictly
\r
3207 needed. Unfortunately, the decision whether or not to perform a full or partial
\r
3208 repaint is scattered all over the place, which is not good for features such as
\r
3209 "arrow highlighting" that require a full repaint of the board.
\r
3211 So, I've tried to patch the code where I thought it made sense (e.g. after or during
\r
3212 user interaction, when speed is not so important) but especially to avoid errors
\r
3213 in the displayed graphics.
\r
3215 In such patched places, I always try refer to this function so there is a single
\r
3216 place to maintain knowledge.
\r
3218 To restore the original behavior, just return FALSE unconditionally.
\r
3220 BOOL IsFullRepaintPreferrable()
\r
3222 BOOL result = FALSE;
\r
3224 if( (appData.highlightLastMove || appData.highlightDragging) && IsDrawArrowEnabled() ) {
\r
3225 /* Arrow may appear on the board */
\r
3233 This function is called by DrawPosition to know whether a full repaint must
\r
3236 Only DrawPosition may directly call this function, which makes use of
\r
3237 some state information. Other function should call DrawPosition specifying
\r
3238 the repaint flag, and can use IsFullRepaintPreferrable if needed.
\r
3240 BOOL DrawPositionNeedsFullRepaint()
\r
3242 BOOL result = FALSE;
\r
3245 Probably a slightly better policy would be to trigger a full repaint
\r
3246 when animInfo.piece changes state (i.e. empty -> non-empty and viceversa),
\r
3247 but animation is fast enough that it's difficult to notice.
\r
3249 if( animInfo.piece == EmptySquare ) {
\r
3250 if( (appData.highlightLastMove || appData.highlightDragging) && IsDrawArrowEnabled() /*&& HasHighlightInfo()*/ ) {
\r
3259 DrawBoardOnDC(HDC hdc, Board board, HDC tmphdc)
\r
3261 int row, column, x, y, square_color, piece_color;
\r
3262 ChessSquare piece;
\r
3264 HDC texture_hdc = NULL;
\r
3266 /* [AS] Initialize background textures if needed */
\r
3267 if( liteBackTexture != NULL || darkBackTexture != NULL ) {
\r
3268 static int backTextureBoardSize; /* [HGM] boardsize: also new texture if board format changed */
\r
3269 if( backTextureSquareSize != squareSize
\r
3270 || backTextureBoardSize != BOARD_WIDTH+BOARD_FILES*BOARD_HEIGHT) {
\r
3271 backTextureBoardSize = BOARD_WIDTH+BOARD_FILES*BOARD_HEIGHT;
\r
3272 backTextureSquareSize = squareSize;
\r
3273 RebuildTextureSquareInfo();
\r
3276 texture_hdc = CreateCompatibleDC( hdc );
\r
3279 for (row = 0; row < BOARD_HEIGHT; row++) {
\r
3280 for (column = 0; column < BOARD_WIDTH; column++) {
\r
3282 SquareToPos(row, column, &x, &y);
\r
3284 piece = board[row][column];
\r
3286 square_color = ((column + row) % 2) == 1;
\r
3287 if( gameInfo.variant == VariantXiangqi ) {
\r
3288 square_color = !InPalace(row, column);
\r
3289 if(BOARD_HEIGHT&1) { if(row==BOARD_HEIGHT/2) square_color ^= 1; }
\r
3290 else if(row < BOARD_HEIGHT/2) square_color ^= 1;
\r
3292 piece_color = (int) piece < (int) BlackPawn;
\r
3295 /* [HGM] holdings file: light square or black */
\r
3296 if(column == BOARD_LEFT-2) {
\r
3297 if( row > BOARD_HEIGHT - gameInfo.holdingsSize - 1 )
\r
3300 DisplayHoldingsCount(hdc, x, y, 0, 0); /* black out */
\r
3304 if(column == BOARD_RGHT + 1 ) {
\r
3305 if( row < gameInfo.holdingsSize )
\r
3308 DisplayHoldingsCount(hdc, x, y, 0, 0);
\r
3312 if(column == BOARD_LEFT-1 ) /* left align */
\r
3313 DisplayHoldingsCount(hdc, x, y, flipView, (int) board[row][column]);
\r
3314 else if( column == BOARD_RGHT) /* right align */
\r
3315 DisplayHoldingsCount(hdc, x, y, !flipView, (int) board[row][column]);
\r
3317 if (appData.monoMode) {
\r
3318 if (piece == EmptySquare) {
\r
3319 BitBlt(hdc, x, y, squareSize, squareSize, 0, 0, 0,
\r
3320 square_color ? WHITENESS : BLACKNESS);
\r
3322 DrawPieceOnDC(hdc, piece, piece_color, square_color, x, y, tmphdc);
\r
3325 else if( backTextureSquareInfo[row][column].mode > 0 ) {
\r
3326 /* [AS] Draw the square using a texture bitmap */
\r
3327 HBITMAP hbm = SelectObject( texture_hdc, square_color ? liteBackTexture : darkBackTexture );
\r
3328 int r = row, c = column; // [HGM] do not flip board in flipView
\r
3329 if(flipView) { r = BOARD_HEIGHT-1 - r; c = BOARD_WIDTH-1 - c; }
\r
3332 squareSize, squareSize,
\r
3335 backTextureSquareInfo[r][c].mode,
\r
3336 backTextureSquareInfo[r][c].x,
\r
3337 backTextureSquareInfo[r][c].y );
\r
3339 SelectObject( texture_hdc, hbm );
\r
3341 if (piece != EmptySquare) {
\r
3342 DrawPieceOnDC(hdc, piece, piece_color, -1, x, y, tmphdc);
\r
3346 HBRUSH brush = square_color ? lightSquareBrush : darkSquareBrush;
\r
3348 oldBrush = SelectObject(hdc, brush );
\r
3349 BitBlt(hdc, x, y, squareSize, squareSize, 0, 0, 0, PATCOPY);
\r
3350 SelectObject(hdc, oldBrush);
\r
3351 if (piece != EmptySquare)
\r
3352 DrawPieceOnDC(hdc, piece, piece_color, -1, x, y, tmphdc);
\r
3357 if( texture_hdc != NULL ) {
\r
3358 DeleteDC( texture_hdc );
\r
3362 int saveDiagFlag = 0; FILE *diagFile; // [HGM] diag
\r
3363 void fputDW(FILE *f, int x)
\r
3365 fputc(x & 255, f);
\r
3366 fputc(x>>8 & 255, f);
\r
3367 fputc(x>>16 & 255, f);
\r
3368 fputc(x>>24 & 255, f);
\r
3371 #define MAX_CLIPS 200 /* more than enough */
\r
3374 DrawLogoOnDC(HDC hdc, RECT logoRect, HBITMAP logo)
\r
3376 // HBITMAP bufferBitmap;
\r
3381 int w = 100, h = 50;
\r
3383 if(logo == NULL) return;
\r
3384 // GetClientRect(hwndMain, &Rect);
\r
3385 // bufferBitmap = CreateCompatibleBitmap(hdc, Rect.right-Rect.left+1,
\r
3386 // Rect.bottom-Rect.top+1);
\r
3387 tmphdc = CreateCompatibleDC(hdc);
\r
3388 hbm = SelectObject(tmphdc, logo);
\r
3389 if( GetObject( logo, sizeof(bi), &bi ) > 0 ) {
\r
3393 StretchBlt(hdc, logoRect.left, logoRect.top, logoRect.right - logoRect.left,
\r
3394 logoRect.bottom - logoRect.top, tmphdc, 0, 0, w, h, SRCCOPY);
\r
3395 SelectObject(tmphdc, hbm);
\r
3403 HDC hdc = GetDC(hwndMain);
\r
3404 HBITMAP whiteLogo = (HBITMAP) first.programLogo, blackLogo = (HBITMAP) second.programLogo;
\r
3405 if(appData.autoLogo) {
\r
3407 switch(gameMode) { // pick logos based on game mode
\r
3408 case IcsObserving:
\r
3409 whiteLogo = second.programLogo; // ICS logo
\r
3410 blackLogo = second.programLogo;
\r
3413 case IcsPlayingWhite:
\r
3414 if(!appData.zippyPlay) whiteLogo = userLogo;
\r
3415 blackLogo = second.programLogo; // ICS logo
\r
3417 case IcsPlayingBlack:
\r
3418 whiteLogo = second.programLogo; // ICS logo
\r
3419 blackLogo = appData.zippyPlay ? first.programLogo : userLogo;
\r
3421 case TwoMachinesPlay:
\r
3422 if(first.twoMachinesColor[0] == 'b') {
\r
3423 whiteLogo = second.programLogo;
\r
3424 blackLogo = first.programLogo;
\r
3427 case MachinePlaysWhite:
\r
3428 blackLogo = userLogo;
\r
3430 case MachinePlaysBlack:
\r
3431 whiteLogo = userLogo;
\r
3432 blackLogo = first.programLogo;
\r
3435 DrawLogoOnDC(hdc, leftLogoRect, flipClock ? blackLogo : whiteLogo);
\r
3436 DrawLogoOnDC(hdc, rightLogoRect, flipClock ? whiteLogo : blackLogo);
\r
3437 ReleaseDC(hwndMain, hdc);
\r
3441 static HDC hdcSeek;
\r
3443 // [HGM] seekgraph
\r
3444 void DrawSeekAxis( int x, int y, int xTo, int yTo )
\r
3447 HPEN hp = SelectObject( hdcSeek, gridPen );
\r
3448 MoveToEx( hdcSeek, boardRect.left+x, boardRect.top+y, &stPt );
\r
3449 LineTo( hdcSeek, boardRect.left+xTo, boardRect.top+yTo );
\r
3450 SelectObject( hdcSeek, hp );
\r
3453 // front-end wrapper for drawing functions to do rectangles
\r
3454 void DrawSeekBackground( int left, int top, int right, int bottom )
\r
3459 if (hdcSeek == NULL) {
\r
3460 hdcSeek = GetDC(hwndMain);
\r
3461 if (!appData.monoMode) {
\r
3462 SelectPalette(hdcSeek, hPal, FALSE);
\r
3463 RealizePalette(hdcSeek);
\r
3466 hp = SelectObject( hdcSeek, gridPen );
\r
3467 rc.top = boardRect.top+top; rc.left = boardRect.left+left;
\r
3468 rc.bottom = boardRect.top+bottom; rc.right = boardRect.left+right;
\r
3469 FillRect( hdcSeek, &rc, lightSquareBrush );
\r
3470 SelectObject( hdcSeek, hp );
\r
3473 // front-end wrapper for putting text in graph
\r
3474 void DrawSeekText(char *buf, int x, int y)
\r
3477 SetBkMode( hdcSeek, TRANSPARENT );
\r
3478 GetTextExtentPoint32( hdcSeek, buf, strlen(buf), &stSize );
\r
3479 TextOut( hdcSeek, boardRect.left+x-3, boardRect.top+y-stSize.cy/2, buf, strlen(buf) );
\r
3482 void DrawSeekDot(int x, int y, int color)
\r
3484 int square = color & 0x80;
\r
3485 HBRUSH oldBrush = SelectObject(hdcSeek,
\r
3486 color == 0 ? markerBrush : color == 1 ? darkSquareBrush : explodeBrush);
\r
3489 Rectangle(hdcSeek, boardRect.left+x - squareSize/9, boardRect.top+y - squareSize/9,
\r
3490 boardRect.left+x + squareSize/9, boardRect.top+y + squareSize/9);
\r
3492 Ellipse(hdcSeek, boardRect.left+x - squareSize/8, boardRect.top+y - squareSize/8,
\r
3493 boardRect.left+x + squareSize/8, boardRect.top+y + squareSize/8);
\r
3494 SelectObject(hdcSeek, oldBrush);
\r
3498 HDCDrawPosition(HDC hdc, BOOLEAN repaint, Board board)
\r
3500 static Board lastReq[2], lastDrawn[2];
\r
3501 static HighlightInfo lastDrawnHighlight, lastDrawnPremove;
\r
3502 static int lastDrawnFlipView = 0;
\r
3503 static int lastReqValid[2] = {0, 0}, lastDrawnValid[2] = {0, 0};
\r
3504 int releaseDC, x, y, x2, y2, row, column, num_clips = 0, i;
\r
3507 HBITMAP bufferBitmap;
\r
3508 HBITMAP oldBitmap;
\r
3510 HRGN clips[MAX_CLIPS];
\r
3511 ChessSquare dragged_piece = EmptySquare;
\r
3512 int nr = twoBoards*partnerUp;
\r
3514 /* I'm undecided on this - this function figures out whether a full
\r
3515 * repaint is necessary on its own, so there's no real reason to have the
\r
3516 * caller tell it that. I think this can safely be set to FALSE - but
\r
3517 * if we trust the callers not to request full repaints unnessesarily, then
\r
3518 * we could skip some clipping work. In other words, only request a full
\r
3519 * redraw when the majority of pieces have changed positions (ie. flip,
\r
3520 * gamestart and similar) --Hawk
\r
3522 Boolean fullrepaint = repaint;
\r
3524 if(DrawSeekGraph()) return; // [HG} seekgraph: suppress printing board if seek graph up
\r
3526 if( DrawPositionNeedsFullRepaint() ) {
\r
3527 fullrepaint = TRUE;
\r
3530 if (board == NULL) {
\r
3531 if (!lastReqValid[nr]) {
\r
3534 board = lastReq[nr];
\r
3536 CopyBoard(lastReq[nr], board);
\r
3537 lastReqValid[nr] = 1;
\r
3540 if (doingSizing) {
\r
3544 if (IsIconic(hwndMain)) {
\r
3548 if (hdc == NULL) {
\r
3549 hdc = GetDC(hwndMain);
\r
3550 if (!appData.monoMode) {
\r
3551 SelectPalette(hdc, hPal, FALSE);
\r
3552 RealizePalette(hdc);
\r
3556 releaseDC = FALSE;
\r
3559 /* Create some work-DCs */
\r
3560 hdcmem = CreateCompatibleDC(hdc);
\r
3561 tmphdc = CreateCompatibleDC(hdc);
\r
3563 /* If dragging is in progress, we temporarely remove the piece */
\r
3564 /* [HGM] or temporarily decrease count if stacked */
\r
3565 /* !! Moved to before board compare !! */
\r
3566 if (dragInfo.from.x >= 0 && dragInfo.pos.x >= 0) {
\r
3567 dragged_piece = board[dragInfo.from.y][dragInfo.from.x];
\r
3568 if(dragInfo.from.x == BOARD_LEFT-2 ) {
\r
3569 if(--board[dragInfo.from.y][dragInfo.from.x+1] == 0 )
\r
3570 board[dragInfo.from.y][dragInfo.from.x] = EmptySquare;
\r
3572 if(dragInfo.from.x == BOARD_RGHT+1) {
\r
3573 if(--board[dragInfo.from.y][dragInfo.from.x-1] == 0 )
\r
3574 board[dragInfo.from.y][dragInfo.from.x] = EmptySquare;
\r
3576 board[dragInfo.from.y][dragInfo.from.x] = gatingPiece;
\r
3579 /* Figure out which squares need updating by comparing the
\r
3580 * newest board with the last drawn board and checking if
\r
3581 * flipping has changed.
\r
3583 if (!fullrepaint && lastDrawnValid[nr] && (nr == 1 || lastDrawnFlipView == flipView)) {
\r
3584 for (row = 0; row < BOARD_HEIGHT; row++) { /* [HGM] true size, not 8 */
\r
3585 for (column = 0; column < BOARD_WIDTH; column++) {
\r
3586 if (lastDrawn[nr][row][column] != board[row][column]) {
\r
3587 SquareToPos(row, column, &x, &y);
\r
3588 clips[num_clips++] =
\r
3589 CreateRectRgn(x, y, x + squareSize, y + squareSize);
\r
3593 if(nr == 0) { // [HGM] dual: no highlights on second board
\r
3594 for (i=0; i<2; i++) {
\r
3595 if (lastDrawnHighlight.sq[i].x != highlightInfo.sq[i].x ||
\r
3596 lastDrawnHighlight.sq[i].y != highlightInfo.sq[i].y) {
\r
3597 if (lastDrawnHighlight.sq[i].x >= 0 &&
\r
3598 lastDrawnHighlight.sq[i].y >= 0) {
\r
3599 SquareToPos(lastDrawnHighlight.sq[i].y,
\r
3600 lastDrawnHighlight.sq[i].x, &x, &y);
\r
3601 clips[num_clips++] =
\r
3602 CreateRectRgn(x - lineGap, y - lineGap,
\r
3603 x + squareSize + lineGap, y + squareSize + lineGap);
\r
3605 if (highlightInfo.sq[i].x >= 0 && highlightInfo.sq[i].y >= 0) {
\r
3606 SquareToPos(highlightInfo.sq[i].y, highlightInfo.sq[i].x, &x, &y);
\r
3607 clips[num_clips++] =
\r
3608 CreateRectRgn(x - lineGap, y - lineGap,
\r
3609 x + squareSize + lineGap, y + squareSize + lineGap);
\r
3613 for (i=0; i<2; i++) {
\r
3614 if (lastDrawnPremove.sq[i].x != premoveHighlightInfo.sq[i].x ||
\r
3615 lastDrawnPremove.sq[i].y != premoveHighlightInfo.sq[i].y) {
\r
3616 if (lastDrawnPremove.sq[i].x >= 0 &&
\r
3617 lastDrawnPremove.sq[i].y >= 0) {
\r
3618 SquareToPos(lastDrawnPremove.sq[i].y,
\r
3619 lastDrawnPremove.sq[i].x, &x, &y);
\r
3620 clips[num_clips++] =
\r
3621 CreateRectRgn(x - lineGap, y - lineGap,
\r
3622 x + squareSize + lineGap, y + squareSize + lineGap);
\r
3624 if (premoveHighlightInfo.sq[i].x >= 0 &&
\r
3625 premoveHighlightInfo.sq[i].y >= 0) {
\r
3626 SquareToPos(premoveHighlightInfo.sq[i].y,
\r
3627 premoveHighlightInfo.sq[i].x, &x, &y);
\r
3628 clips[num_clips++] =
\r
3629 CreateRectRgn(x - lineGap, y - lineGap,
\r
3630 x + squareSize + lineGap, y + squareSize + lineGap);
\r
3634 } else { // nr == 1
\r
3635 partnerHighlightInfo.sq[0].y = board[EP_STATUS-4];
\r
3636 partnerHighlightInfo.sq[0].x = board[EP_STATUS-3];
\r
3637 partnerHighlightInfo.sq[1].y = board[EP_STATUS-2];
\r
3638 partnerHighlightInfo.sq[1].x = board[EP_STATUS-1];
\r
3639 for (i=0; i<2; i++) {
\r
3640 if (partnerHighlightInfo.sq[i].x >= 0 &&
\r
3641 partnerHighlightInfo.sq[i].y >= 0) {
\r
3642 SquareToPos(partnerHighlightInfo.sq[i].y,
\r
3643 partnerHighlightInfo.sq[i].x, &x, &y);
\r
3644 clips[num_clips++] =
\r
3645 CreateRectRgn(x - lineGap, y - lineGap,
\r
3646 x + squareSize + lineGap, y + squareSize + lineGap);
\r
3648 if (oldPartnerHighlight.sq[i].x >= 0 &&
\r
3649 oldPartnerHighlight.sq[i].y >= 0) {
\r
3650 SquareToPos(oldPartnerHighlight.sq[i].y,
\r
3651 oldPartnerHighlight.sq[i].x, &x, &y);
\r
3652 clips[num_clips++] =
\r
3653 CreateRectRgn(x - lineGap, y - lineGap,
\r
3654 x + squareSize + lineGap, y + squareSize + lineGap);
\r
3659 fullrepaint = TRUE;
\r
3662 /* Create a buffer bitmap - this is the actual bitmap
\r
3663 * being written to. When all the work is done, we can
\r
3664 * copy it to the real DC (the screen). This avoids
\r
3665 * the problems with flickering.
\r
3667 GetClientRect(hwndMain, &Rect);
\r
3668 bufferBitmap = CreateCompatibleBitmap(hdc, Rect.right-Rect.left+1,
\r
3669 Rect.bottom-Rect.top+1);
\r
3670 oldBitmap = SelectObject(hdcmem, bufferBitmap);
\r
3671 if (!appData.monoMode) {
\r
3672 SelectPalette(hdcmem, hPal, FALSE);
\r
3675 /* Create clips for dragging */
\r
3676 if (!fullrepaint) {
\r
3677 if (dragInfo.from.x >= 0) {
\r
3678 SquareToPos(dragInfo.from.y, dragInfo.from.x, &x, &y);
\r
3679 clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);
\r
3681 if (dragInfo.start.x >= 0) {
\r
3682 SquareToPos(dragInfo.start.y, dragInfo.start.x, &x, &y);
\r
3683 clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);
\r
3685 if (dragInfo.pos.x >= 0) {
\r
3686 x = dragInfo.pos.x - squareSize / 2;
\r
3687 y = dragInfo.pos.y - squareSize / 2;
\r
3688 clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);
\r
3690 if (dragInfo.lastpos.x >= 0) {
\r
3691 x = dragInfo.lastpos.x - squareSize / 2;
\r
3692 y = dragInfo.lastpos.y - squareSize / 2;
\r
3693 clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);
\r
3697 /* Are we animating a move?
\r
3699 * - remove the piece from the board (temporarely)
\r
3700 * - calculate the clipping region
\r
3702 if (!fullrepaint) {
\r
3703 if (animInfo.piece != EmptySquare) {
\r
3704 board[animInfo.from.y][animInfo.from.x] = EmptySquare;
\r
3705 x = boardRect.left + animInfo.lastpos.x;
\r
3706 y = boardRect.top + animInfo.lastpos.y;
\r
3707 x2 = boardRect.left + animInfo.pos.x;
\r
3708 y2 = boardRect.top + animInfo.pos.y;
\r
3709 clips[num_clips++] = CreateRectRgn(MIN(x,x2), MIN(y,y2), MAX(x,x2)+squareSize, MAX(y,y2)+squareSize);
\r
3710 /* Slight kludge. The real problem is that after AnimateMove is
\r
3711 done, the position on the screen does not match lastDrawn.
\r
3712 This currently causes trouble only on e.p. captures in
\r
3713 atomic, where the piece moves to an empty square and then
\r
3714 explodes. The old and new positions both had an empty square
\r
3715 at the destination, but animation has drawn a piece there and
\r
3716 we have to remember to erase it. [HGM] moved until after setting lastDrawn */
\r
3717 lastDrawn[0][animInfo.to.y][animInfo.to.x] = animInfo.piece;
\r
3721 /* No clips? Make sure we have fullrepaint set to TRUE */
\r
3722 if (num_clips == 0)
\r
3723 fullrepaint = TRUE;
\r
3725 /* Set clipping on the memory DC */
\r
3726 if (!fullrepaint) {
\r
3727 SelectClipRgn(hdcmem, clips[0]);
\r
3728 for (x = 1; x < num_clips; x++) {
\r
3729 if (ExtSelectClipRgn(hdcmem, clips[x], RGN_OR) == ERROR)
\r
3730 abort(); // this should never ever happen!
\r
3734 /* Do all the drawing to the memory DC */
\r
3735 if(explodeInfo.radius) { // [HGM] atomic
\r
3737 int x, y, r=(explodeInfo.radius * squareSize)/100;
\r
3738 ChessSquare piece = board[explodeInfo.fromY][explodeInfo.fromX];
\r
3739 board[explodeInfo.fromY][explodeInfo.fromX] = EmptySquare; // suppress display of capturer
\r
3740 SquareToPos(explodeInfo.toY, explodeInfo.toX, &x, &y);
\r
3741 x += squareSize/2;
\r
3742 y += squareSize/2;
\r
3743 if(!fullrepaint) {
\r
3744 clips[num_clips] = CreateRectRgn(x-r, y-r, x+r, y+r);
\r
3745 ExtSelectClipRgn(hdcmem, clips[num_clips++], RGN_OR);
\r
3747 DrawGridOnDC(hdcmem);
\r
3748 DrawHighlightsOnDC(hdcmem, &highlightInfo, HIGHLIGHT_PEN);
\r
3749 DrawHighlightsOnDC(hdcmem, &premoveHighlightInfo, PREMOVE_PEN);
\r
3750 DrawBoardOnDC(hdcmem, board, tmphdc);
\r
3751 board[explodeInfo.fromY][explodeInfo.fromX] = piece;
\r
3752 oldBrush = SelectObject(hdcmem, explodeBrush);
\r
3753 Ellipse(hdcmem, x-r, y-r, x+r, y+r);
\r
3754 SelectObject(hdcmem, oldBrush);
\r
3756 DrawGridOnDC(hdcmem);
\r
3757 if(nr == 0) { // [HGM] dual: decide which highlights to draw
\r
3758 DrawHighlightsOnDC(hdcmem, &highlightInfo, HIGHLIGHT_PEN);
\r
3759 DrawHighlightsOnDC(hdcmem, &premoveHighlightInfo, PREMOVE_PEN);
\r
3761 DrawHighlightsOnDC(hdcmem, &partnerHighlightInfo, HIGHLIGHT_PEN);
\r
3762 oldPartnerHighlight = partnerHighlightInfo;
\r
3764 DrawBoardOnDC(hdcmem, board, tmphdc);
\r
3766 if(nr == 0) // [HGM] dual: markers only on left board
\r
3767 for (row = 0; row < BOARD_HEIGHT; row++) {
\r
3768 for (column = 0; column < BOARD_WIDTH; column++) {
\r
3769 if (marker[row][column]) { // marker changes only occur with full repaint!
\r
3770 HBRUSH oldBrush = SelectObject(hdcmem,
\r
3771 marker[row][column] == 2 ? markerBrush : explodeBrush);
\r
3772 SquareToPos(row, column, &x, &y);
\r
3773 Ellipse(hdcmem, x + squareSize/4, y + squareSize/4,
\r
3774 x + 3*squareSize/4, y + 3*squareSize/4);
\r
3775 SelectObject(hdcmem, oldBrush);
\r
3780 if( appData.highlightMoveWithArrow ) {
\r
3781 DrawArrowHighlight(hdcmem);
\r
3784 DrawCoordsOnDC(hdcmem);
\r
3786 CopyBoard(lastDrawn[nr], board); /* [HGM] Moved to here from end of routine, */
\r
3787 /* to make sure lastDrawn contains what is actually drawn */
\r
3789 /* Put the dragged piece back into place and draw it (out of place!) */
\r
3790 if (dragged_piece != EmptySquare) {
\r
3791 /* [HGM] or restack */
\r
3792 if(dragInfo.from.x == BOARD_LEFT-2 )
\r
3793 board[dragInfo.from.y][dragInfo.from.x+1]++;
\r
3795 if(dragInfo.from.x == BOARD_RGHT+1 )
\r
3796 board[dragInfo.from.y][dragInfo.from.x-1]++;
\r
3797 board[dragInfo.from.y][dragInfo.from.x] = dragged_piece;
\r
3798 x = dragInfo.pos.x - squareSize / 2;
\r
3799 y = dragInfo.pos.y - squareSize / 2;
\r
3800 DrawPieceOnDC(hdcmem, dragInfo.piece,
\r
3801 ((int) dragInfo.piece < (int) BlackPawn),
\r
3802 (dragInfo.from.y + dragInfo.from.x) % 2, x, y, tmphdc);
\r
3805 /* Put the animated piece back into place and draw it */
\r
3806 if (animInfo.piece != EmptySquare) {
\r
3807 board[animInfo.from.y][animInfo.from.x] = animInfo.piece;
\r
3808 x = boardRect.left + animInfo.pos.x;
\r
3809 y = boardRect.top + animInfo.pos.y;
\r
3810 DrawPieceOnDC(hdcmem, animInfo.piece,
\r
3811 ((int) animInfo.piece < (int) BlackPawn),
\r
3812 (animInfo.from.y + animInfo.from.x) % 2, x, y, tmphdc);
\r
3815 /* Release the bufferBitmap by selecting in the old bitmap
\r
3816 * and delete the memory DC
\r
3818 SelectObject(hdcmem, oldBitmap);
\r
3821 /* Set clipping on the target DC */
\r
3822 if (!fullrepaint) {
\r
3823 if(nr == 1) for (x = 0; x < num_clips; x++) { // [HGM] dual: translate clips
\r
3825 GetRgnBox(clips[x], &rect);
\r
3826 DeleteObject(clips[x]);
\r
3827 clips[x] = CreateRectRgn(rect.left + wpMain.width/2, rect.top,
\r
3828 rect.right + wpMain.width/2, rect.bottom);
\r
3830 SelectClipRgn(hdc, clips[0]);
\r
3831 for (x = 1; x < num_clips; x++) {
\r
3832 if (ExtSelectClipRgn(hdc, clips[x], RGN_OR) == ERROR)
\r
3833 abort(); // this should never ever happen!
\r
3837 /* Copy the new bitmap onto the screen in one go.
\r
3838 * This way we avoid any flickering
\r
3840 oldBitmap = SelectObject(tmphdc, bufferBitmap);
\r
3841 BitBlt(hdc, boardRect.left + twoBoards*partnerUp*wpMain.width/2, boardRect.top, // [HGM] dual
\r
3842 boardRect.right - boardRect.left,
\r
3843 boardRect.bottom - boardRect.top,
\r
3844 tmphdc, boardRect.left, boardRect.top, SRCCOPY);
\r
3845 if(saveDiagFlag) {
\r
3846 BITMAP b; int i, j=0, m, w, wb, fac=0; char pData[1000000];
\r
3847 BITMAPINFOHEADER bih; int color[16], nrColors=0;
\r
3849 GetObject(bufferBitmap, sizeof(b), &b);
\r
3850 if(b.bmWidthBytes*b.bmHeight <= 990000) {
\r
3851 bih.biSize = sizeof(BITMAPINFOHEADER);
\r
3852 bih.biWidth = b.bmWidth;
\r
3853 bih.biHeight = b.bmHeight;
\r
3855 bih.biBitCount = b.bmBitsPixel;
\r
3856 bih.biCompression = 0;
\r
3857 bih.biSizeImage = b.bmWidthBytes*b.bmHeight;
\r
3858 bih.biXPelsPerMeter = 0;
\r
3859 bih.biYPelsPerMeter = 0;
\r
3860 bih.biClrUsed = 0;
\r
3861 bih.biClrImportant = 0;
\r
3862 // fprintf(diagFile, "t=%d\nw=%d\nh=%d\nB=%d\nP=%d\nX=%d\n",
\r
3863 // b.bmType, b.bmWidth, b.bmHeight, b.bmWidthBytes, b.bmPlanes, b.bmBitsPixel);
\r
3864 GetDIBits(tmphdc,bufferBitmap,0,b.bmHeight,pData,(BITMAPINFO*)&bih,DIB_RGB_COLORS);
\r
3865 // fprintf(diagFile, "%8x\n", (int) pData);
\r
3867 wb = b.bmWidthBytes;
\r
3869 for(i=0; i<wb*(b.bmHeight - boardRect.top + OUTER_MARGIN)>>2; i++) {
\r
3870 int k = ((int*) pData)[i];
\r
3871 for(j=0; j<nrColors; j++) if(color[j] == k) break;
\r
3872 if(j >= 16) break;
\r
3874 if(j >= nrColors) nrColors = j+1;
\r
3876 if(j<16) { // 16 colors is enough. Compress to 4 bits per pixel
\r
3878 for(i=0; i<b.bmHeight - boardRect.top + OUTER_MARGIN; i++) {
\r
3879 for(w=0; w<(wb>>2); w+=2) {
\r
3880 int k = ((int*) pData)[(wb*i>>2) + w];
\r
3881 for(j=0; j<nrColors; j++) if(color[j] == k) break;
\r
3882 k = ((int*) pData)[(wb*i>>2) + w + 1];
\r
3883 for(m=0; m<nrColors; m++) if(color[m] == k) break;
\r
3884 pData[p++] = m | j<<4;
\r
3886 while(p&3) pData[p++] = 0;
\r
3889 wb = ((wb+31)>>5)<<2;
\r
3891 // write BITMAPFILEHEADER
\r
3892 fprintf(diagFile, "BM");
\r
3893 fputDW(diagFile, wb*(b.bmHeight - boardRect.top + OUTER_MARGIN)+0x36 + (fac?64:0));
\r
3894 fputDW(diagFile, 0);
\r
3895 fputDW(diagFile, 0x36 + (fac?64:0));
\r
3896 // write BITMAPINFOHEADER
\r
3897 fputDW(diagFile, 40);
\r
3898 fputDW(diagFile, b.bmWidth);
\r
3899 fputDW(diagFile, b.bmHeight - boardRect.top + OUTER_MARGIN);
\r
3900 if(fac) fputDW(diagFile, 0x040001); // planes and bits/pixel
\r
3901 else fputDW(diagFile, 0x200001); // planes and bits/pixel
\r
3902 fputDW(diagFile, 0);
\r
3903 fputDW(diagFile, 0);
\r
3904 fputDW(diagFile, 0);
\r
3905 fputDW(diagFile, 0);
\r
3906 fputDW(diagFile, 0);
\r
3907 fputDW(diagFile, 0);
\r
3908 // write color table
\r
3910 for(i=0; i<16; i++) fputDW(diagFile, color[i]);
\r
3911 // write bitmap data
\r
3912 for(i=0; i<wb*(b.bmHeight - boardRect.top + OUTER_MARGIN); i++)
\r
3913 fputc(pData[i], diagFile);
\r
3917 SelectObject(tmphdc, oldBitmap);
\r
3919 /* Massive cleanup */
\r
3920 for (x = 0; x < num_clips; x++)
\r
3921 DeleteObject(clips[x]);
\r
3924 DeleteObject(bufferBitmap);
\r
3927 ReleaseDC(hwndMain, hdc);
\r
3929 if (lastDrawnFlipView != flipView && nr == 0) {
\r
3931 CheckMenuItem(GetMenu(hwndMain),IDM_FlipView, MF_BYCOMMAND|MF_CHECKED);
\r
3933 CheckMenuItem(GetMenu(hwndMain),IDM_FlipView, MF_BYCOMMAND|MF_UNCHECKED);
\r
3936 /* CopyBoard(lastDrawn, board);*/
\r
3937 lastDrawnHighlight = highlightInfo;
\r
3938 lastDrawnPremove = premoveHighlightInfo;
\r
3939 lastDrawnFlipView = flipView;
\r
3940 lastDrawnValid[nr] = 1;
\r
3943 /* [HGM] diag: Save the current board display to the given open file and close the file */
\r
3948 saveDiagFlag = 1; diagFile = f;
\r
3949 HDCDrawPosition(NULL, TRUE, NULL);
\r
3953 // if(f != NULL) fprintf(f, "Sorry, but this feature is still in preparation\n");
\r
3960 /*---------------------------------------------------------------------------*\
\r
3961 | CLIENT PAINT PROCEDURE
\r
3962 | This is the main event-handler for the WM_PAINT message.
\r
3964 \*---------------------------------------------------------------------------*/
\r
3966 PaintProc(HWND hwnd)
\r
3972 if((hdc = BeginPaint(hwnd, &ps))) {
\r
3973 if (IsIconic(hwnd)) {
\r
3974 DrawIcon(hdc, 2, 2, iconCurrent);
\r
3976 if (!appData.monoMode) {
\r
3977 SelectPalette(hdc, hPal, FALSE);
\r
3978 RealizePalette(hdc);
\r
3980 HDCDrawPosition(hdc, 1, NULL);
\r
3981 if(twoBoards) { // [HGM] dual: also redraw other board in other orientation
\r
3982 flipView = !flipView; partnerUp = !partnerUp;
\r
3983 HDCDrawPosition(hdc, 1, NULL);
\r
3984 flipView = !flipView; partnerUp = !partnerUp;
\r
3987 SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);
\r
3988 ExtTextOut(hdc, messageRect.left, messageRect.top,
\r
3989 ETO_CLIPPED|ETO_OPAQUE,
\r
3990 &messageRect, messageText, strlen(messageText), NULL);
\r
3991 SelectObject(hdc, oldFont);
\r
3992 DisplayBothClocks();
\r
3995 EndPaint(hwnd,&ps);
\r
4003 * If the user selects on a border boundary, return -1; if off the board,
\r
4004 * return -2. Otherwise map the event coordinate to the square.
\r
4005 * The offset boardRect.left or boardRect.top must already have been
\r
4006 * subtracted from x.
\r
4008 int EventToSquare(x, limit)
\r
4016 if ((x % (squareSize + lineGap)) >= squareSize)
\r
4018 x /= (squareSize + lineGap);
\r
4030 DropEnable dropEnables[] = {
\r
4031 { 'P', DP_Pawn, N_("Pawn") },
\r
4032 { 'N', DP_Knight, N_("Knight") },
\r
4033 { 'B', DP_Bishop, N_("Bishop") },
\r
4034 { 'R', DP_Rook, N_("Rook") },
\r
4035 { 'Q', DP_Queen, N_("Queen") },
\r
4039 SetupDropMenu(HMENU hmenu)
\r
4041 int i, count, enable;
\r
4043 extern char white_holding[], black_holding[];
\r
4044 char item[MSG_SIZ];
\r
4046 for (i=0; i<sizeof(dropEnables)/sizeof(DropEnable); i++) {
\r
4047 p = strchr(gameMode == IcsPlayingWhite ? white_holding : black_holding,
\r
4048 dropEnables[i].piece);
\r
4050 while (p && *p++ == dropEnables[i].piece) count++;
\r
4051 snprintf(item, MSG_SIZ, "%s %d", T_(dropEnables[i].name), count);
\r
4052 enable = count > 0 || !appData.testLegality
\r
4053 /*!!temp:*/ || (gameInfo.variant == VariantCrazyhouse
\r
4054 && !appData.icsActive);
\r
4055 ModifyMenu(hmenu, dropEnables[i].command,
\r
4056 MF_BYCOMMAND | (enable ? MF_ENABLED : MF_GRAYED) | MF_STRING,
\r
4057 dropEnables[i].command, item);
\r
4061 void DragPieceBegin(int x, int y)
\r
4063 dragInfo.lastpos.x = boardRect.left + x;
\r
4064 dragInfo.lastpos.y = boardRect.top + y;
\r
4065 dragInfo.from.x = fromX;
\r
4066 dragInfo.from.y = fromY;
\r
4067 dragInfo.piece = boards[currentMove][fromY][fromX];
\r
4068 dragInfo.start = dragInfo.from;
\r
4069 SetCapture(hwndMain);
\r
4072 void DragPieceEnd(int x, int y)
\r
4075 dragInfo.start.x = dragInfo.start.y = -1;
\r
4076 dragInfo.from = dragInfo.start;
\r
4077 dragInfo.pos = dragInfo.lastpos = dragInfo.start;
\r
4080 void ChangeDragPiece(ChessSquare piece)
\r
4082 dragInfo.piece = piece;
\r
4085 /* Event handler for mouse messages */
\r
4087 MouseEvent(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
\r
4091 static int recursive = 0;
\r
4093 BOOLEAN forceFullRepaint = IsFullRepaintPreferrable(); /* [AS] */
\r
4096 if (message == WM_MBUTTONUP) {
\r
4097 /* Hideous kludge to fool TrackPopupMenu into paying attention
\r
4098 to the middle button: we simulate pressing the left button too!
\r
4100 PostMessage(hwnd, WM_LBUTTONDOWN, wParam, lParam);
\r
4101 PostMessage(hwnd, WM_LBUTTONUP, wParam, lParam);
\r
4107 pt.x = LOWORD(lParam);
\r
4108 pt.y = HIWORD(lParam);
\r
4109 x = EventToSquare(pt.x - boardRect.left, BOARD_WIDTH);
\r
4110 y = EventToSquare(pt.y - boardRect.top, BOARD_HEIGHT);
\r
4111 if (!flipView && y >= 0) {
\r
4112 y = BOARD_HEIGHT - 1 - y;
\r
4114 if (flipView && x >= 0) {
\r
4115 x = BOARD_WIDTH - 1 - x;
\r
4118 shiftKey = GetKeyState(VK_SHIFT) < 0; // [HGM] remember last shift status
\r
4120 switch (message) {
\r
4121 case WM_LBUTTONDOWN:
\r
4122 if (PtInRect((LPRECT) &whiteRect, pt)) {
\r
4123 ClockClick(flipClock);
\r
4124 } else if (PtInRect((LPRECT) &blackRect, pt)) {
\r
4125 ClockClick(!flipClock);
\r
4127 dragInfo.start.x = dragInfo.start.y = -1;
\r
4128 dragInfo.from = dragInfo.start;
\r
4129 if(fromX == -1 && frozen) { // not sure where this is for
\r
4130 fromX = fromY = -1;
\r
4131 DrawPosition(forceFullRepaint || FALSE, NULL); /* [AS] */
\r
4134 LeftClick(Press, pt.x - boardRect.left, pt.y - boardRect.top);
\r
4135 DrawPosition(TRUE, NULL);
\r
4138 case WM_LBUTTONUP:
\r
4139 LeftClick(Release, pt.x - boardRect.left, pt.y - boardRect.top);
\r
4140 DrawPosition(TRUE, NULL);
\r
4143 case WM_MOUSEMOVE:
\r
4144 if(SeekGraphClick(Press, pt.x - boardRect.left, pt.y - boardRect.top, 1)) break;
\r
4145 if(PromoScroll(pt.x - boardRect.left, pt.y - boardRect.top)) break;
\r
4146 MovePV(pt.x - boardRect.left, pt.y - boardRect.top, boardRect.bottom - boardRect.top);
\r
4147 if ((appData.animateDragging || appData.highlightDragging)
\r
4148 && (wParam & MK_LBUTTON)
\r
4149 && dragInfo.from.x >= 0)
\r
4151 BOOL full_repaint = FALSE;
\r
4153 if (appData.animateDragging) {
\r
4154 dragInfo.pos = pt;
\r
4156 if (appData.highlightDragging) {
\r
4157 SetHighlights(fromX, fromY, x, y);
\r
4158 if( IsDrawArrowEnabled() && (x < 0 || x >= BOARD_WIDTH || y < 0 || y >= BOARD_HEIGHT) ) {
\r
4159 full_repaint = TRUE;
\r
4163 DrawPosition( full_repaint, NULL);
\r
4165 dragInfo.lastpos = dragInfo.pos;
\r
4169 case WM_MOUSEWHEEL: // [DM]
\r
4170 { static int lastDir = 0; // [HGM] build in some hysteresis to avoid spurious events
\r
4171 /* Mouse Wheel is being rolled forward
\r
4172 * Play moves forward
\r
4174 if((short)HIWORD(wParam) < 0 && currentMove < forwardMostMove)
\r
4175 { if(lastDir == 1) ForwardEvent(); else lastDir = 1; } // [HGM] suppress first event in direction
\r
4176 /* Mouse Wheel is being rolled backward
\r
4177 * Play moves backward
\r
4179 if((short)HIWORD(wParam) > 0 && currentMove > backwardMostMove)
\r
4180 { if(lastDir == -1) BackwardEvent(); else lastDir = -1; }
\r
4184 case WM_MBUTTONUP:
\r
4185 case WM_RBUTTONUP:
\r
4187 RightClick(Release, pt.x - boardRect.left, pt.y - boardRect.top, &fromX, &fromY);
\r
4190 case WM_MBUTTONDOWN:
\r
4191 case WM_RBUTTONDOWN:
\r
4194 fromX = fromY = -1;
\r
4195 dragInfo.pos.x = dragInfo.pos.y = -1;
\r
4196 dragInfo.start.x = dragInfo.start.y = -1;
\r
4197 dragInfo.from = dragInfo.start;
\r
4198 dragInfo.lastpos = dragInfo.pos;
\r
4199 if (appData.highlightDragging) {
\r
4200 ClearHighlights();
\r
4203 /* [HGM] right mouse button in clock area edit-game mode ups clock */
\r
4204 if (PtInRect((LPRECT) &whiteRect, pt)) {
\r
4205 if (gameMode == EditGame || GetKeyState(VK_SHIFT) < 0) AdjustClock(flipClock, 1);
\r
4206 } else if (PtInRect((LPRECT) &blackRect, pt)) {
\r
4207 if (gameMode == EditGame || GetKeyState(VK_SHIFT) < 0) AdjustClock(!flipClock, 1);
\r
4211 DrawPosition(TRUE, NULL);
\r
4213 menuNr = RightClick(Press, pt.x - boardRect.left, pt.y - boardRect.top, &fromX, &fromY);
\r
4216 if (message == WM_MBUTTONDOWN) {
\r
4217 buttonCount = 3; /* even if system didn't think so */
\r
4218 if (wParam & MK_SHIFT)
\r
4219 MenuPopup(hwnd, pt, LoadMenu(hInst, "BlackPieceMenu"), -1);
\r
4221 MenuPopup(hwnd, pt, LoadMenu(hInst, "WhitePieceMenu"), -1);
\r
4222 } else { /* message == WM_RBUTTONDOWN */
\r
4223 /* Just have one menu, on the right button. Windows users don't
\r
4224 think to try the middle one, and sometimes other software steals
\r
4225 it, or it doesn't really exist. */
\r
4226 if(gameInfo.variant != VariantShogi)
\r
4227 MenuPopup(hwnd, pt, LoadMenu(hInst, "PieceMenu"), -1);
\r
4229 MenuPopup(hwnd, pt, LoadMenu(hInst, "ShogiPieceMenu"), -1);
\r
4233 SetCapture(hwndMain);
4236 hmenu = LoadMenu(hInst, "DropPieceMenu");
\r
4237 SetupDropMenu(hmenu);
\r
4238 MenuPopup(hwnd, pt, hmenu, -1);
\r
4248 /* Preprocess messages for buttons in main window */
\r
4250 ButtonProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
\r
4252 int id = GetWindowLongPtr(hwnd, GWLP_ID);
\r
4255 for (i=0; i<N_BUTTONS; i++) {
\r
4256 if (buttonDesc[i].id == id) break;
\r
4258 if (i == N_BUTTONS) return 0;
\r
4259 switch (message) {
\r
4264 dir = (wParam == VK_LEFT) ? -1 : 1;
\r
4265 SetFocus(buttonDesc[(i + dir + N_BUTTONS) % N_BUTTONS].hwnd);
\r
4272 SendMessage(hwndMain, WM_COMMAND, MAKEWPARAM(buttonDesc[i].id, 0), 0);
\r
4275 if (appData.icsActive && (isalpha((char)wParam) || wParam == '0')) {
\r
4276 // [HGM] movenum: only letters or leading zero should go to ICS input
\r
4277 HWND h = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
4278 if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);
\r
4280 SendMessage(h, WM_CHAR, wParam, lParam);
\r
4282 } else if (isalpha((char)wParam) || isdigit((char)wParam)){
\r
4283 PopUpMoveDialog((char)wParam);
\r
4289 return CallWindowProc(buttonDesc[i].wndproc, hwnd, message, wParam, lParam);
\r
4292 /* Process messages for Promotion dialog box */
\r
4294 Promotion(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
4298 switch (message) {
\r
4299 case WM_INITDIALOG: /* message: initialize dialog box */
\r
4300 /* Center the dialog over the application window */
\r
4301 CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));
\r
4302 Translate(hDlg, DLG_PromotionKing);
\r
4303 ShowWindow(GetDlgItem(hDlg, PB_King),
\r
4304 (!appData.testLegality || gameInfo.variant == VariantSuicide ||
\r
4305 gameInfo.variant == VariantSpartan && !WhiteOnMove(currentMove) ||
\r
4306 gameInfo.variant == VariantGiveaway || gameInfo.variant == VariantSuper ) ?
\r
4307 SW_SHOW : SW_HIDE);
\r
4308 /* [HGM] Only allow C & A promotions if these pieces are defined */
\r
4309 ShowWindow(GetDlgItem(hDlg, PB_Archbishop),
\r
4310 ((PieceToChar(WhiteAngel) >= 'A' && WhiteOnMove(currentMove) &&
\r
4311 PieceToChar(WhiteAngel) != '~') ||
\r
4312 (PieceToChar(BlackAngel) >= 'A' && !WhiteOnMove(currentMove) &&
\r
4313 PieceToChar(BlackAngel) != '~') ) ?
\r
4314 SW_SHOW : SW_HIDE);
\r
4315 ShowWindow(GetDlgItem(hDlg, PB_Chancellor),
\r
4316 ((PieceToChar(WhiteMarshall) >= 'A' && WhiteOnMove(currentMove) &&
\r
4317 PieceToChar(WhiteMarshall) != '~') ||
\r
4318 (PieceToChar(BlackMarshall) >= 'A' && !WhiteOnMove(currentMove) &&
\r
4319 PieceToChar(BlackMarshall) != '~') ) ?
\r
4320 SW_SHOW : SW_HIDE);
\r
4321 /* [HGM] Hide B & R button in Shogi, use Q as promote, N as defer */
\r
4322 ShowWindow(GetDlgItem(hDlg, PB_Rook),
\r
4323 gameInfo.variant != VariantShogi ?
\r
4324 SW_SHOW : SW_HIDE);
\r
4325 ShowWindow(GetDlgItem(hDlg, PB_Bishop),
\r
4326 gameInfo.variant != VariantShogi ?
\r
4327 SW_SHOW : SW_HIDE);
\r
4328 if(gameInfo.variant == VariantShogi) {
\r
4329 SetDlgItemText(hDlg, PB_Queen, "YES");
\r
4330 SetDlgItemText(hDlg, PB_Knight, "NO");
\r
4331 SetWindowText(hDlg, "Promote?");
\r
4333 ShowWindow(GetDlgItem(hDlg, IDC_Centaur),
\r
4334 gameInfo.variant == VariantSuper ?
\r
4335 SW_SHOW : SW_HIDE);
\r
4338 case WM_COMMAND: /* message: received a command */
\r
4339 switch (LOWORD(wParam)) {
\r
4341 EndDialog(hDlg, TRUE); /* Exit the dialog */
\r
4342 ClearHighlights();
\r
4343 DrawPosition(FALSE, NULL);
\r
4346 promoChar = gameInfo.variant == VariantSuper ? PieceToChar(BlackSilver) : PieceToChar(BlackKing);
\r
4349 promoChar = gameInfo.variant == VariantShogi ? '+' : ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteQueen : BlackQueen));
\r
4352 promoChar = ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteRook : BlackRook));
\r
4353 if(gameInfo.variant == VariantSpartan && !WhiteOnMove(currentMove)) promoChar = PieceToChar(BlackDragon);
\r
4356 promoChar = ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteBishop : BlackBishop));
\r
4357 if(gameInfo.variant == VariantSpartan && !WhiteOnMove(currentMove)) promoChar = PieceToChar(BlackAlfil);
\r
4359 case PB_Chancellor:
\r
4360 promoChar = ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteMarshall : BlackMarshall));
\r
4362 case PB_Archbishop:
\r
4363 promoChar = ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteAngel : BlackAngel));
\r
4366 promoChar = gameInfo.variant == VariantShogi ? '=' : PieceToChar(WhiteOnMove(currentMove) ? WhiteKnight : BlackKnight);
\r
4371 if(promoChar == '.') return FALSE; // invalid piece chosen
\r
4372 EndDialog(hDlg, TRUE); /* Exit the dialog */
\r
4373 UserMoveEvent(fromX, fromY, toX, toY, promoChar);
\r
4374 fromX = fromY = -1;
\r
4375 if (!appData.highlightLastMove) {
\r
4376 ClearHighlights();
\r
4377 DrawPosition(FALSE, NULL);
\r
4384 /* Pop up promotion dialog */
\r
4386 PromotionPopup(HWND hwnd)
\r
4390 lpProc = MakeProcInstance((FARPROC)Promotion, hInst);
\r
4391 DialogBox(hInst, MAKEINTRESOURCE(DLG_PromotionKing),
\r
4392 hwnd, (DLGPROC)lpProc);
\r
4393 FreeProcInstance(lpProc);
\r
4399 DrawPosition(TRUE, NULL);
\r
4400 PromotionPopup(hwndMain);
\r
4403 /* Toggle ShowThinking */
\r
4405 ToggleShowThinking()
\r
4407 appData.showThinking = !appData.showThinking;
\r
4408 ShowThinkingEvent();
\r
4412 LoadGameDialog(HWND hwnd, char* title)
\r
4416 char fileTitle[MSG_SIZ];
\r
4417 f = OpenFileDialog(hwnd, "rb", "",
\r
4418 appData.oldSaveStyle ? "gam" : "pgn",
\r
4420 title, &number, fileTitle, NULL);
\r
4422 cmailMsgLoaded = FALSE;
\r
4423 if (number == 0) {
\r
4424 int error = GameListBuild(f);
\r
4426 DisplayError(_("Cannot build game list"), error);
\r
4427 } else if (!ListEmpty(&gameList) &&
\r
4428 ((ListGame *) gameList.tailPred)->number > 1) {
\r
4429 GameListPopUp(f, fileTitle);
\r
4432 GameListDestroy();
\r
4435 LoadGame(f, number, fileTitle, FALSE);
\r
4439 int get_term_width()
\r
4444 HFONT hfont, hold_font;
\r
4449 hText = GetDlgItem(hwndConsole, OPT_ConsoleText);
\r
4453 // get the text metrics
\r
4454 hdc = GetDC(hText);
\r
4455 lf = font[boardSize][CONSOLE_FONT]->lf;
\r
4456 if (consoleCF.dwEffects & CFE_BOLD)
\r
4457 lf.lfWeight = FW_BOLD;
\r
4458 if (consoleCF.dwEffects & CFE_ITALIC)
\r
4459 lf.lfItalic = TRUE;
\r
4460 if (consoleCF.dwEffects & CFE_STRIKEOUT)
\r
4461 lf.lfStrikeOut = TRUE;
\r
4462 if (consoleCF.dwEffects & CFE_UNDERLINE)
\r
4463 lf.lfUnderline = TRUE;
\r
4464 hfont = CreateFontIndirect(&lf);
\r
4465 hold_font = SelectObject(hdc, hfont);
\r
4466 GetTextMetrics(hdc, &tm);
\r
4467 SelectObject(hdc, hold_font);
\r
4468 DeleteObject(hfont);
\r
4469 ReleaseDC(hText, hdc);
\r
4471 // get the rectangle
\r
4472 SendMessage(hText, EM_GETRECT, 0, (LPARAM)&rc);
\r
4474 return (rc.right-rc.left) / tm.tmAveCharWidth;
\r
4477 void UpdateICSWidth(HWND hText)
\r
4479 LONG old_width, new_width;
\r
4481 new_width = get_term_width(hText, FALSE);
\r
4482 old_width = GetWindowLongPtr(hText, GWLP_USERDATA);
\r
4483 if (new_width != old_width)
\r
4485 ics_update_width(new_width);
\r
4486 SetWindowLongPtr(hText, GWLP_USERDATA, new_width);
\r
4491 ChangedConsoleFont()
\r
4494 CHARRANGE tmpsel, sel;
\r
4495 MyFont *f = font[boardSize][CONSOLE_FONT];
\r
4496 HWND hText = GetDlgItem(hwndConsole, OPT_ConsoleText);
\r
4497 HWND hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
4500 cfmt.cbSize = sizeof(CHARFORMAT);
\r
4501 cfmt.dwMask = CFM_FACE|CFM_SIZE|CFM_CHARSET;
\r
4502 safeStrCpy(cfmt.szFaceName, font[boardSize][CONSOLE_FONT]->mfp.faceName,
\r
4503 sizeof(cfmt.szFaceName)/sizeof(cfmt.szFaceName[0]) );
\r
4504 /* yHeight is expressed in twips. A twip is 1/20 of a font's point
\r
4505 * size. This was undocumented in the version of MSVC++ that I had
\r
4506 * when I wrote the code, but is apparently documented now.
\r
4508 cfmt.yHeight = (int)(f->mfp.pointSize * 20.0 + 0.5);
\r
4509 cfmt.bCharSet = f->lf.lfCharSet;
\r
4510 cfmt.bPitchAndFamily = f->lf.lfPitchAndFamily;
\r
4511 SendMessage(hText, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &cfmt);
\r
4512 SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &cfmt);
\r
4513 /* Why are the following seemingly needed too? */
\r
4514 SendMessage(hText, EM_SETCHARFORMAT, SCF_DEFAULT, (LPARAM) &cfmt);
\r
4515 SendMessage(hInput, EM_SETCHARFORMAT, SCF_DEFAULT, (LPARAM) &cfmt);
\r
4516 SendMessage(hText, EM_EXGETSEL, 0, (LPARAM)&sel);
\r
4518 tmpsel.cpMax = -1; /*999999?*/
\r
4519 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&tmpsel);
\r
4520 SendMessage(hText, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cfmt);
\r
4521 /* Trying putting this here too. It still seems to tickle a RichEdit
\r
4522 * bug: sometimes RichEdit indents the first line of a paragraph too.
\r
4524 paraf.cbSize = sizeof(paraf);
\r
4525 paraf.dwMask = PFM_OFFSET | PFM_STARTINDENT;
\r
4526 paraf.dxStartIndent = 0;
\r
4527 paraf.dxOffset = WRAP_INDENT;
\r
4528 SendMessage(hText, EM_SETPARAFORMAT, 0, (LPARAM) ¶f);
\r
4529 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
4530 UpdateICSWidth(hText);
\r
4533 /*---------------------------------------------------------------------------*\
\r
4535 * Window Proc for main window
\r
4537 \*---------------------------------------------------------------------------*/
\r
4539 /* Process messages for main window, etc. */
\r
4541 WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
\r
4544 int wmId, wmEvent;
\r
4548 char fileTitle[MSG_SIZ];
\r
4549 char buf[MSG_SIZ];
\r
4550 static SnapData sd;
\r
4552 switch (message) {
\r
4554 case WM_PAINT: /* message: repaint portion of window */
\r
4558 case WM_ERASEBKGND:
\r
4559 if (IsIconic(hwnd)) {
\r
4560 /* Cheat; change the message */
\r
4561 return (DefWindowProc(hwnd, WM_ICONERASEBKGND, wParam, lParam));
\r
4563 return (DefWindowProc(hwnd, message, wParam, lParam));
\r
4567 case WM_LBUTTONDOWN:
\r
4568 case WM_MBUTTONDOWN:
\r
4569 case WM_RBUTTONDOWN:
\r
4570 case WM_LBUTTONUP:
\r
4571 case WM_MBUTTONUP:
\r
4572 case WM_RBUTTONUP:
\r
4573 case WM_MOUSEMOVE:
\r
4574 case WM_MOUSEWHEEL:
\r
4575 MouseEvent(hwnd, message, wParam, lParam);
\r
4578 JAWS_KB_NAVIGATION
\r
4582 JAWS_ALT_INTERCEPT
\r
4584 if (appData.icsActive && ((char)wParam == '\r' || (char)wParam > ' ' && !((char)wParam >= '1' && (char)wParam <= '9'))) {
\r
4585 // [HGM] movenum: for non-zero digits we always do type-in dialog
\r
4586 HWND h = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
4587 if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);
\r
4589 SendMessage(h, message, wParam, lParam);
\r
4590 } else if(lParam != KF_REPEAT) {
\r
4591 if (isalpha((char)wParam) || isdigit((char)wParam)) {
\r
4592 PopUpMoveDialog((char)wParam);
\r
4593 } else if((char)wParam == 003) CopyGameToClipboard();
\r
4594 else if((char)wParam == 026) PasteGameOrFENFromClipboard();
\r
4599 case WM_PALETTECHANGED:
\r
4600 if (hwnd != (HWND)wParam && !appData.monoMode) {
\r
4602 HDC hdc = GetDC(hwndMain);
\r
4603 SelectPalette(hdc, hPal, TRUE);
\r
4604 nnew = RealizePalette(hdc);
\r
4606 paletteChanged = TRUE;
\r
4607 InvalidateRect(hwnd, &boardRect, FALSE);
\r
4609 ReleaseDC(hwnd, hdc);
\r
4613 case WM_QUERYNEWPALETTE:
\r
4614 if (!appData.monoMode /*&& paletteChanged*/) {
\r
4616 HDC hdc = GetDC(hwndMain);
\r
4617 paletteChanged = FALSE;
\r
4618 SelectPalette(hdc, hPal, FALSE);
\r
4619 nnew = RealizePalette(hdc);
\r
4621 InvalidateRect(hwnd, &boardRect, FALSE);
\r
4623 ReleaseDC(hwnd, hdc);
\r
4628 case WM_COMMAND: /* message: command from application menu */
\r
4629 wmId = LOWORD(wParam);
\r
4630 wmEvent = HIWORD(wParam);
\r
4635 SAY("new game enter a move to play against the computer with white");
\r
4638 case IDM_NewGameFRC:
\r
4639 if( NewGameFRC() == 0 ) {
\r
4644 case IDM_NewVariant:
\r
4645 NewVariantPopup(hwnd);
\r
4648 case IDM_LoadGame:
\r
4649 LoadGameDialog(hwnd, _("Load Game from File"));
\r
4652 case IDM_LoadNextGame:
\r
4656 case IDM_LoadPrevGame:
\r
4660 case IDM_ReloadGame:
\r
4664 case IDM_LoadPosition:
\r
4665 if (gameMode == AnalyzeMode || gameMode == AnalyzeFile) {
\r
4666 Reset(FALSE, TRUE);
\r
4669 f = OpenFileDialog(hwnd, "rb", "",
\r
4670 appData.oldSaveStyle ? "pos" : "fen",
\r
4672 _("Load Position from File"), &number, fileTitle, NULL);
\r
4674 LoadPosition(f, number, fileTitle);
\r
4678 case IDM_LoadNextPosition:
\r
4679 ReloadPosition(1);
\r
4682 case IDM_LoadPrevPosition:
\r
4683 ReloadPosition(-1);
\r
4686 case IDM_ReloadPosition:
\r
4687 ReloadPosition(0);
\r
4690 case IDM_SaveGame:
\r
4691 defName = DefaultFileName(appData.oldSaveStyle ? "gam" : "pgn");
\r
4692 f = OpenFileDialog(hwnd, "a", defName,
\r
4693 appData.oldSaveStyle ? "gam" : "pgn",
\r
4695 _("Save Game to File"), NULL, fileTitle, NULL);
\r
4697 SaveGame(f, 0, "");
\r
4701 case IDM_SavePosition:
\r
4702 defName = DefaultFileName(appData.oldSaveStyle ? "pos" : "fen");
\r
4703 f = OpenFileDialog(hwnd, "a", defName,
\r
4704 appData.oldSaveStyle ? "pos" : "fen",
\r
4706 _("Save Position to File"), NULL, fileTitle, NULL);
\r
4708 SavePosition(f, 0, "");
\r
4712 case IDM_SaveDiagram:
\r
4713 defName = "diagram";
\r
4714 f = OpenFileDialog(hwnd, "wb", defName,
\r
4717 _("Save Diagram to File"), NULL, fileTitle, NULL);
\r
4723 case IDM_CopyGame:
\r
4724 CopyGameToClipboard();
\r
4727 case IDM_PasteGame:
\r
4728 PasteGameFromClipboard();
\r
4731 case IDM_CopyGameListToClipboard:
\r
4732 CopyGameListToClipboard();
\r
4735 /* [AS] Autodetect FEN or PGN data */
\r
4736 case IDM_PasteAny:
\r
4737 PasteGameOrFENFromClipboard();
\r
4740 /* [AS] Move history */
\r
4741 case IDM_ShowMoveHistory:
\r
4742 if( MoveHistoryIsUp() ) {
\r
4743 MoveHistoryPopDown();
\r
4746 MoveHistoryPopUp();
\r
4750 /* [AS] Eval graph */
\r
4751 case IDM_ShowEvalGraph:
\r
4752 if( EvalGraphIsUp() ) {
\r
4753 EvalGraphPopDown();
\r
4757 SetFocus(hwndMain);
\r
4761 /* [AS] Engine output */
\r
4762 case IDM_ShowEngineOutput:
\r
4763 if( EngineOutputIsUp() ) {
\r
4764 EngineOutputPopDown();
\r
4767 EngineOutputPopUp();
\r
4771 /* [AS] User adjudication */
\r
4772 case IDM_UserAdjudication_White:
\r
4773 UserAdjudicationEvent( +1 );
\r
4776 case IDM_UserAdjudication_Black:
\r
4777 UserAdjudicationEvent( -1 );
\r
4780 case IDM_UserAdjudication_Draw:
\r
4781 UserAdjudicationEvent( 0 );
\r
4784 /* [AS] Game list options dialog */
\r
4785 case IDM_GameListOptions:
\r
4786 GameListOptions();
\r
4793 case IDM_CopyPosition:
\r
4794 CopyFENToClipboard();
\r
4797 case IDM_PastePosition:
\r
4798 PasteFENFromClipboard();
\r
4801 case IDM_MailMove:
\r
4805 case IDM_ReloadCMailMsg:
\r
4806 Reset(TRUE, TRUE);
\r
4807 ReloadCmailMsgEvent(FALSE);
\r
4810 case IDM_Minimize:
\r
4811 ShowWindow(hwnd, SW_MINIMIZE);
\r
4818 case IDM_MachineWhite:
\r
4819 MachineWhiteEvent();
\r
4821 * refresh the tags dialog only if it's visible
\r
4823 if (gameMode == MachinePlaysWhite && IsWindowVisible(editTagsDialog)) {
\r
4825 tags = PGNTags(&gameInfo);
\r
4826 TagsPopUp(tags, CmailMsg());
\r
4829 SAY("computer starts playing white");
\r
4832 case IDM_MachineBlack:
\r
4833 MachineBlackEvent();
\r
4835 * refresh the tags dialog only if it's visible
\r
4837 if (gameMode == MachinePlaysBlack && IsWindowVisible(editTagsDialog)) {
\r
4839 tags = PGNTags(&gameInfo);
\r
4840 TagsPopUp(tags, CmailMsg());
\r
4843 SAY("computer starts playing black");
\r
4846 case IDM_Match: // [HGM] match: flows into next case, after setting Match Mode and nr of Games
\r
4847 if(gameMode != BeginningOfGame) { // allow menu item to remain enabled for better mode highligting
\r
4848 DisplayError(_("You can only start a match from the initial position."), 0); break;
\r
4850 matchMode = 2;// distinguish from command-line-triggered case (matchMode=1)
\r
4851 appData.matchGames = appData.defaultMatchGames;
\r
4853 first.matchWins = second.matchWins = 0;
\r
4855 case IDM_TwoMachines:
\r
4856 TwoMachinesEvent();
\r
4858 * refresh the tags dialog only if it's visible
\r
4860 if (gameMode == TwoMachinesPlay && IsWindowVisible(editTagsDialog)) {
\r
4862 tags = PGNTags(&gameInfo);
\r
4863 TagsPopUp(tags, CmailMsg());
\r
4866 SAY("computer starts playing both sides");
\r
4869 case IDM_AnalysisMode:
\r
4870 if (!first.analysisSupport) {
\r
4871 snprintf(buf, MSG_SIZ, _("%s does not support analysis"), first.tidy);
\r
4872 DisplayError(buf, 0);
\r
4874 SAY("analyzing current position");
\r
4875 /* [DM] icsEngineAnlyze [HGM] Why is this front-end??? */
\r
4876 if (appData.icsActive) {
\r
4877 if (gameMode != IcsObserving) {
\r
4878 snprintf(buf, MSG_SIZ, "You are not observing a game");
\r
4879 DisplayError(buf, 0);
\r
4880 /* secure check */
\r
4881 if (appData.icsEngineAnalyze) {
\r
4882 if (appData.debugMode)
\r
4883 fprintf(debugFP, "Found unexpected active ICS engine analyze \n");
\r
4884 ExitAnalyzeMode();
\r
4890 /* if enable, user want disable icsEngineAnalyze */
\r
4891 if (appData.icsEngineAnalyze) {
\r
4892 ExitAnalyzeMode();
\r
4896 appData.icsEngineAnalyze = TRUE;
\r
4897 if (appData.debugMode) fprintf(debugFP, "ICS engine analyze starting...\n");
\r
4900 if (!appData.showThinking) ToggleShowThinking();
\r
4901 AnalyzeModeEvent();
\r
4905 case IDM_AnalyzeFile:
\r
4906 if (!first.analysisSupport) {
\r
4907 char buf[MSG_SIZ];
\r
4908 snprintf(buf, MSG_SIZ, _("%s does not support analysis"), first.tidy);
\r
4909 DisplayError(buf, 0);
\r
4911 if (!appData.showThinking) ToggleShowThinking();
\r
4912 AnalyzeFileEvent();
\r
4913 LoadGameDialog(hwnd, _("Analyze Game from File"));
\r
4914 AnalysisPeriodicEvent(1);
\r
4918 case IDM_IcsClient:
\r
4922 case IDM_EditGame:
\r
4923 case IDM_EditGame2:
\r
4928 case IDM_EditPosition:
\r
4929 case IDM_EditPosition2:
\r
4930 EditPositionEvent();
\r
4931 SAY("enter a FEN string or setup a position on the board using the control R pop up menu");
\r
4934 case IDM_Training:
\r
4938 case IDM_ShowGameList:
\r
4939 ShowGameListProc();
\r
4942 case IDM_EditProgs1:
\r
4943 EditTagsPopUp(firstChessProgramNames, &firstChessProgramNames);
\r
4946 case IDM_EditProgs2:
\r
4947 EditTagsPopUp(secondChessProgramNames, &secondChessProgramNames);
\r
4950 case IDM_EditServers:
\r
4951 EditTagsPopUp(icsNames, &icsNames);
\r
4954 case IDM_EditTags:
\r
4959 case IDM_EditComment:
\r
4961 if (commentUp && editComment) {
\r
4964 EditCommentEvent();
\r
4984 case IDM_CallFlag:
\r
5004 case IDM_StopObserving:
\r
5005 StopObservingEvent();
\r
5008 case IDM_StopExamining:
\r
5009 StopExaminingEvent();
\r
5013 UploadGameEvent();
\r
5016 case IDM_TypeInMove:
\r
5017 PopUpMoveDialog('\000');
\r
5020 case IDM_TypeInName:
\r
5021 PopUpNameDialog('\000');
\r
5024 case IDM_Backward:
\r
5026 SetFocus(hwndMain);
\r
5033 SetFocus(hwndMain);
\r
5038 SetFocus(hwndMain);
\r
5043 SetFocus(hwndMain);
\r
5047 RevertEvent(FALSE);
\r
5050 case IDM_Annotate: // [HGM] vari: revert with annotation
\r
5051 RevertEvent(TRUE);
\r
5054 case IDM_TruncateGame:
\r
5055 TruncateGameEvent();
\r
5062 case IDM_RetractMove:
\r
5063 RetractMoveEvent();
\r
5066 case IDM_FlipView:
\r
5067 flipView = !flipView;
\r
5068 DrawPosition(FALSE, NULL);
\r
5071 case IDM_FlipClock:
\r
5072 flipClock = !flipClock;
\r
5073 DisplayBothClocks();
\r
5077 case IDM_MuteSounds:
\r
5078 mute = !mute; // [HGM] mute: keep track of global muting variable
\r
5079 CheckMenuItem(GetMenu(hwndMain),IDM_MuteSounds,
\r
5080 MF_BYCOMMAND|(mute?MF_CHECKED:MF_UNCHECKED));
\r
5083 case IDM_GeneralOptions:
\r
5084 GeneralOptionsPopup(hwnd);
\r
5085 DrawPosition(TRUE, NULL);
\r
5088 case IDM_BoardOptions:
\r
5089 BoardOptionsPopup(hwnd);
\r
5092 case IDM_EnginePlayOptions:
\r
5093 EnginePlayOptionsPopup(hwnd);
\r
5096 case IDM_Engine1Options:
\r
5097 EngineOptionsPopup(hwnd, &first);
\r
5100 case IDM_Engine2Options:
\r
5102 if(WaitForSecond(SettingsMenuIfReady)) break;
\r
5103 EngineOptionsPopup(hwnd, &second);
\r
5106 case IDM_OptionsUCI:
\r
5107 UciOptionsPopup(hwnd);
\r
5110 case IDM_IcsOptions:
\r
5111 IcsOptionsPopup(hwnd);
\r
5115 FontsOptionsPopup(hwnd);
\r
5119 SoundOptionsPopup(hwnd);
\r
5122 case IDM_CommPort:
\r
5123 CommPortOptionsPopup(hwnd);
\r
5126 case IDM_LoadOptions:
\r
5127 LoadOptionsPopup(hwnd);
\r
5130 case IDM_SaveOptions:
\r
5131 SaveOptionsPopup(hwnd);
\r
5134 case IDM_TimeControl:
\r
5135 TimeControlOptionsPopup(hwnd);
\r
5138 case IDM_SaveSettings:
\r
5139 SaveSettings(settingsFileName);
\r
5142 case IDM_SaveSettingsOnExit:
\r
5143 saveSettingsOnExit = !saveSettingsOnExit;
\r
5144 (void) CheckMenuItem(GetMenu(hwndMain), IDM_SaveSettingsOnExit,
\r
5145 MF_BYCOMMAND|(saveSettingsOnExit ?
\r
5146 MF_CHECKED : MF_UNCHECKED));
\r
5157 case IDM_AboutGame:
\r
5162 appData.debugMode = !appData.debugMode;
\r
5163 if (appData.debugMode) {
\r
5164 char dir[MSG_SIZ];
\r
5165 GetCurrentDirectory(MSG_SIZ, dir);
\r
5166 SetCurrentDirectory(installDir);
\r
5167 debugFP = fopen(appData.nameOfDebugFile, "w");
\r
5168 SetCurrentDirectory(dir);
\r
5169 setbuf(debugFP, NULL);
\r
5176 case IDM_HELPCONTENTS:
\r
5177 if (!MyHelp (hwnd, "winboard.hlp", HELP_KEY,(DWORD)(LPSTR)"CONTENTS") &&
\r
5178 !HtmlHelp(hwnd, "winboard.chm", 0, 0) ) {
\r
5179 MessageBox (GetFocus(),
\r
5180 _("Unable to activate help"),
\r
5181 szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);
\r
5185 case IDM_HELPSEARCH:
\r
5186 if (!MyHelp (hwnd, "winboard.hlp", HELP_PARTIALKEY, (DWORD)(LPSTR)"") &&
\r
5187 !HtmlHelp(hwnd, "winboard.chm", 0, 0) ) {
\r
5188 MessageBox (GetFocus(),
\r
5189 _("Unable to activate help"),
\r
5190 szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);
\r
5194 case IDM_HELPHELP:
\r
5195 if(!WinHelp(hwnd, (LPSTR)NULL, HELP_HELPONHELP, 0)) {
\r
5196 MessageBox (GetFocus(),
\r
5197 _("Unable to activate help"),
\r
5198 szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);
\r
5203 lpProc = MakeProcInstance((FARPROC)About, hInst);
\r
5205 (gameInfo.event && strcmp(gameInfo.event, "Easter Egg Hunt") == 0) ?
\r
5206 "AboutBox2" : "AboutBox", hwnd, (DLGPROC)lpProc);
\r
5207 FreeProcInstance(lpProc);
\r
5210 case IDM_DirectCommand1:
\r
5211 AskQuestionEvent(_("Direct Command"),
\r
5212 _("Send to chess program:"), "", "1");
\r
5214 case IDM_DirectCommand2:
\r
5215 AskQuestionEvent(_("Direct Command"),
\r
5216 _("Send to second chess program:"), "", "2");
\r
5219 case EP_WhitePawn:
\r
5220 EditPositionMenuEvent(WhitePawn, fromX, fromY);
\r
5221 fromX = fromY = -1;
\r
5224 case EP_WhiteKnight:
\r
5225 EditPositionMenuEvent(WhiteKnight, fromX, fromY);
\r
5226 fromX = fromY = -1;
\r
5229 case EP_WhiteBishop:
\r
5230 EditPositionMenuEvent(WhiteBishop, fromX, fromY);
\r
5231 fromX = fromY = -1;
\r
5234 case EP_WhiteRook:
\r
5235 EditPositionMenuEvent(WhiteRook, fromX, fromY);
\r
5236 fromX = fromY = -1;
\r
5239 case EP_WhiteQueen:
\r
5240 EditPositionMenuEvent(WhiteQueen, fromX, fromY);
\r
5241 fromX = fromY = -1;
\r
5244 case EP_WhiteFerz:
\r
5245 EditPositionMenuEvent(WhiteFerz, fromX, fromY);
\r
5246 fromX = fromY = -1;
\r
5249 case EP_WhiteWazir:
\r
5250 EditPositionMenuEvent(WhiteWazir, fromX, fromY);
\r
5251 fromX = fromY = -1;
\r
5254 case EP_WhiteAlfil:
\r
5255 EditPositionMenuEvent(WhiteAlfil, fromX, fromY);
\r
5256 fromX = fromY = -1;
\r
5259 case EP_WhiteCannon:
\r
5260 EditPositionMenuEvent(WhiteCannon, fromX, fromY);
\r
5261 fromX = fromY = -1;
\r
5264 case EP_WhiteCardinal:
\r
5265 EditPositionMenuEvent(WhiteAngel, fromX, fromY);
\r
5266 fromX = fromY = -1;
\r
5269 case EP_WhiteMarshall:
\r
5270 EditPositionMenuEvent(WhiteMarshall, fromX, fromY);
\r
5271 fromX = fromY = -1;
\r
5274 case EP_WhiteKing:
\r
5275 EditPositionMenuEvent(WhiteKing, fromX, fromY);
\r
5276 fromX = fromY = -1;
\r
5279 case EP_BlackPawn:
\r
5280 EditPositionMenuEvent(BlackPawn, fromX, fromY);
\r
5281 fromX = fromY = -1;
\r
5284 case EP_BlackKnight:
\r
5285 EditPositionMenuEvent(BlackKnight, fromX, fromY);
\r
5286 fromX = fromY = -1;
\r
5289 case EP_BlackBishop:
\r
5290 EditPositionMenuEvent(BlackBishop, fromX, fromY);
\r
5291 fromX = fromY = -1;
\r
5294 case EP_BlackRook:
\r
5295 EditPositionMenuEvent(BlackRook, fromX, fromY);
\r
5296 fromX = fromY = -1;
\r
5299 case EP_BlackQueen:
\r
5300 EditPositionMenuEvent(BlackQueen, fromX, fromY);
\r
5301 fromX = fromY = -1;
\r
5304 case EP_BlackFerz:
\r
5305 EditPositionMenuEvent(BlackFerz, fromX, fromY);
\r
5306 fromX = fromY = -1;
\r
5309 case EP_BlackWazir:
\r
5310 EditPositionMenuEvent(BlackWazir, fromX, fromY);
\r
5311 fromX = fromY = -1;
\r
5314 case EP_BlackAlfil:
\r
5315 EditPositionMenuEvent(BlackAlfil, fromX, fromY);
\r
5316 fromX = fromY = -1;
\r
5319 case EP_BlackCannon:
\r
5320 EditPositionMenuEvent(BlackCannon, fromX, fromY);
\r
5321 fromX = fromY = -1;
\r
5324 case EP_BlackCardinal:
\r
5325 EditPositionMenuEvent(BlackAngel, fromX, fromY);
\r
5326 fromX = fromY = -1;
\r
5329 case EP_BlackMarshall:
\r
5330 EditPositionMenuEvent(BlackMarshall, fromX, fromY);
\r
5331 fromX = fromY = -1;
\r
5334 case EP_BlackKing:
\r
5335 EditPositionMenuEvent(BlackKing, fromX, fromY);
\r
5336 fromX = fromY = -1;
\r
5339 case EP_EmptySquare:
\r
5340 EditPositionMenuEvent(EmptySquare, fromX, fromY);
\r
5341 fromX = fromY = -1;
\r
5344 case EP_ClearBoard:
\r
5345 EditPositionMenuEvent(ClearBoard, fromX, fromY);
\r
5346 fromX = fromY = -1;
\r
5350 EditPositionMenuEvent(WhitePlay, fromX, fromY);
\r
5351 fromX = fromY = -1;
\r
5355 EditPositionMenuEvent(BlackPlay, fromX, fromY);
\r
5356 fromX = fromY = -1;
\r
5360 EditPositionMenuEvent(PromotePiece, fromX, fromY);
\r
5361 fromX = fromY = -1;
\r
5365 EditPositionMenuEvent(DemotePiece, fromX, fromY);
\r
5366 fromX = fromY = -1;
\r
5370 DropMenuEvent(WhitePawn, fromX, fromY);
\r
5371 fromX = fromY = -1;
\r
5375 DropMenuEvent(WhiteKnight, fromX, fromY);
\r
5376 fromX = fromY = -1;
\r
5380 DropMenuEvent(WhiteBishop, fromX, fromY);
\r
5381 fromX = fromY = -1;
\r
5385 DropMenuEvent(WhiteRook, fromX, fromY);
\r
5386 fromX = fromY = -1;
\r
5390 DropMenuEvent(WhiteQueen, fromX, fromY);
\r
5391 fromX = fromY = -1;
\r
5395 barbaric = 0; appData.language = "";
\r
5396 TranslateMenus(0);
\r
5397 CheckMenuItem(GetMenu(hwndMain), lastChecked, MF_BYCOMMAND|MF_UNCHECKED);
\r
5398 CheckMenuItem(GetMenu(hwndMain), IDM_English, MF_BYCOMMAND|MF_CHECKED);
\r
5399 lastChecked = wmId;
\r
5403 if(wmId > IDM_English && wmId < IDM_English+20) {
\r
5404 LoadLanguageFile(languageFile[wmId - IDM_English - 1]);
\r
5405 TranslateMenus(0);
\r
5406 CheckMenuItem(GetMenu(hwndMain), lastChecked, MF_BYCOMMAND|MF_UNCHECKED);
\r
5407 CheckMenuItem(GetMenu(hwndMain), wmId, MF_BYCOMMAND|MF_CHECKED);
\r
5408 lastChecked = wmId;
\r
5411 return (DefWindowProc(hwnd, message, wParam, lParam));
\r
5417 case CLOCK_TIMER_ID:
\r
5418 KillTimer(hwnd, clockTimerEvent); /* Simulate one-shot timer as in X */
\r
5419 clockTimerEvent = 0;
\r
5420 DecrementClocks(); /* call into back end */
\r
5422 case LOAD_GAME_TIMER_ID:
\r
5423 KillTimer(hwnd, loadGameTimerEvent); /* Simulate one-shot timer as in X*/
\r
5424 loadGameTimerEvent = 0;
\r
5425 AutoPlayGameLoop(); /* call into back end */
\r
5427 case ANALYSIS_TIMER_ID:
\r
5428 if ((gameMode == AnalyzeMode || gameMode == AnalyzeFile
\r
5429 || appData.icsEngineAnalyze) && appData.periodicUpdates) {
\r
5430 AnalysisPeriodicEvent(0);
\r
5432 KillTimer(hwnd, analysisTimerEvent);
\r
5433 analysisTimerEvent = 0;
\r
5436 case DELAYED_TIMER_ID:
\r
5437 KillTimer(hwnd, delayedTimerEvent);
\r
5438 delayedTimerEvent = 0;
\r
5439 delayedTimerCallback();
\r
5444 case WM_USER_Input:
\r
5445 InputEvent(hwnd, message, wParam, lParam);
\r
5448 /* [AS] Also move "attached" child windows */
\r
5449 case WM_WINDOWPOSCHANGING:
\r
5451 if( hwnd == hwndMain && appData.useStickyWindows ) {
\r
5452 LPWINDOWPOS lpwp = (LPWINDOWPOS) lParam;
\r
5454 if( ((lpwp->flags & SWP_NOMOVE) == 0) && ((lpwp->flags & SWP_NOSIZE) != 0) ) {
\r
5455 /* Window is moving */
\r
5458 // GetWindowRect( hwnd, &rcMain ); //[HGM] sticky: in XP this returned new position, not old
\r
5459 rcMain.left = wpMain.x; // replace by these 4 lines to reconstruct old rect
\r
5460 rcMain.right = wpMain.x + wpMain.width;
\r
5461 rcMain.top = wpMain.y;
\r
5462 rcMain.bottom = wpMain.y + wpMain.height;
\r
5464 ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, moveHistoryDialog, &wpMoveHistory );
\r
5465 ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, evalGraphDialog, &wpEvalGraph );
\r
5466 ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, engineOutputDialog, &wpEngineOutput );
\r
5467 ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, gameListDialog, &wpGameList );
\r
5468 ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, hwndConsole, &wpConsole );
\r
5469 wpMain.x = lpwp->x;
\r
5470 wpMain.y = lpwp->y;
\r
5475 /* [AS] Snapping */
\r
5476 case WM_ENTERSIZEMOVE:
\r
5477 if(appData.debugMode) { fprintf(debugFP, "size-move\n"); }
\r
5478 if (hwnd == hwndMain) {
\r
5479 doingSizing = TRUE;
\r
5482 return OnEnterSizeMove( &sd, hwnd, wParam, lParam );
\r
5486 if(appData.debugMode) { fprintf(debugFP, "sizing\n"); }
\r
5487 if (hwnd == hwndMain) {
\r
5488 lastSizing = wParam;
\r
5493 if(appData.debugMode) { fprintf(debugFP, "moving\n"); }
\r
5494 return OnMoving( &sd, hwnd, wParam, lParam );
\r
5496 case WM_EXITSIZEMOVE:
\r
5497 if(appData.debugMode) { fprintf(debugFP, "exit size-move, size = %d\n", squareSize); }
\r
5498 if (hwnd == hwndMain) {
\r
5500 doingSizing = FALSE;
\r
5501 InvalidateRect(hwnd, &boardRect, FALSE);
\r
5502 GetClientRect(hwnd, &client);
\r
5503 ResizeBoard(client.right, client.bottom, lastSizing);
\r
5505 if(appData.debugMode) { fprintf(debugFP, "square size = %d\n", squareSize); }
\r
5507 return OnExitSizeMove( &sd, hwnd, wParam, lParam );
\r
5510 case WM_DESTROY: /* message: window being destroyed */
\r
5511 PostQuitMessage(0);
\r
5515 if (hwnd == hwndMain) {
\r
5520 default: /* Passes it on if unprocessed */
\r
5521 return (DefWindowProc(hwnd, message, wParam, lParam));
\r
5526 /*---------------------------------------------------------------------------*\
\r
5528 * Misc utility routines
\r
5530 \*---------------------------------------------------------------------------*/
\r
5533 * Decent random number generator, at least not as bad as Windows
\r
5534 * standard rand, which returns a value in the range 0 to 0x7fff.
\r
5536 unsigned int randstate;
\r
5541 randstate = randstate * 1664525 + 1013904223;
\r
5542 return (int) randstate & 0x7fffffff;
\r
5546 mysrandom(unsigned int seed)
\r
5553 * returns TRUE if user selects a different color, FALSE otherwise
\r
5557 ChangeColor(HWND hwnd, COLORREF *which)
\r
5559 static BOOL firstTime = TRUE;
\r
5560 static DWORD customColors[16];
\r
5562 COLORREF newcolor;
\r
5567 /* Make initial colors in use available as custom colors */
\r
5568 /* Should we put the compiled-in defaults here instead? */
\r
5570 customColors[i++] = lightSquareColor & 0xffffff;
\r
5571 customColors[i++] = darkSquareColor & 0xffffff;
\r
5572 customColors[i++] = whitePieceColor & 0xffffff;
\r
5573 customColors[i++] = blackPieceColor & 0xffffff;
\r
5574 customColors[i++] = highlightSquareColor & 0xffffff;
\r
5575 customColors[i++] = premoveHighlightColor & 0xffffff;
\r
5577 for (ccl = (ColorClass) 0; ccl < NColorClasses && i < 16; ccl++) {
\r
5578 customColors[i++] = textAttribs[ccl].color;
\r
5580 while (i < 16) customColors[i++] = RGB(255, 255, 255);
\r
5581 firstTime = FALSE;
\r
5584 cc.lStructSize = sizeof(cc);
\r
5585 cc.hwndOwner = hwnd;
\r
5586 cc.hInstance = NULL;
\r
5587 cc.rgbResult = (DWORD) (*which & 0xffffff);
\r
5588 cc.lpCustColors = (LPDWORD) customColors;
\r
5589 cc.Flags = CC_RGBINIT|CC_FULLOPEN;
\r
5591 if (!ChooseColor(&cc)) return FALSE;
\r
5593 newcolor = (COLORREF) (0x2000000 | cc.rgbResult);
\r
5594 if (newcolor == *which) return FALSE;
\r
5595 *which = newcolor;
\r
5599 InitDrawingColors();
\r
5600 InvalidateRect(hwnd, &boardRect, FALSE);
\r
5605 MyLoadSound(MySound *ms)
\r
5611 if (ms->data) free(ms->data);
\r
5614 switch (ms->name[0]) {
\r
5620 /* System sound from Control Panel. Don't preload here. */
\r
5624 if (ms->name[1] == NULLCHAR) {
\r
5625 /* "!" alone = silence */
\r
5628 /* Builtin wave resource. Error if not found. */
\r
5629 HANDLE h = FindResource(hInst, ms->name + 1, "WAVE");
\r
5630 if (h == NULL) break;
\r
5631 ms->data = (void *)LoadResource(hInst, h);
\r
5632 if (h == NULL) break;
\r
5637 /* .wav file. Error if not found. */
\r
5638 f = fopen(ms->name, "rb");
\r
5639 if (f == NULL) break;
\r
5640 if (fstat(fileno(f), &st) < 0) break;
\r
5641 ms->data = malloc(st.st_size);
\r
5642 if (fread(ms->data, st.st_size, 1, f) < 1) break;
\r
5648 char buf[MSG_SIZ];
\r
5649 snprintf(buf, MSG_SIZ, _("Error loading sound %s"), ms->name);
\r
5650 DisplayError(buf, GetLastError());
\r
5656 MyPlaySound(MySound *ms)
\r
5658 BOOLEAN ok = FALSE;
\r
5660 if(mute) return TRUE; // [HGM] mute: suppress all sound play when muted
\r
5661 switch (ms->name[0]) {
\r
5663 if(appData.debugMode) fprintf(debugFP, "silence\n");
\r
5668 /* System sound from Control Panel (deprecated feature).
\r
5669 "$" alone or an unset sound name gets default beep (still in use). */
\r
5670 if (ms->name[1]) {
\r
5671 ok = PlaySound(ms->name + 1, NULL, SND_ALIAS|SND_ASYNC);
\r
5673 if (!ok) ok = MessageBeep(MB_OK);
\r
5676 /* Builtin wave resource, or "!" alone for silence */
\r
5677 if (ms->name[1]) {
\r
5678 if (ms->data == NULL) return FALSE;
\r
5679 ok = PlaySound(ms->data, NULL, SND_MEMORY|SND_ASYNC);
\r
5685 /* .wav file. Error if not found. */
\r
5686 if (ms->data == NULL) return FALSE;
\r
5687 ok = PlaySound(ms->data, NULL, SND_MEMORY|SND_ASYNC);
\r
5690 /* Don't print an error: this can happen innocently if the sound driver
\r
5691 is busy; for instance, if another instance of WinBoard is playing
\r
5692 a sound at about the same time. */
\r
5698 OldOpenFileHook(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
5701 OPENFILENAME *ofn;
\r
5702 static UINT *number; /* gross that this is static */
\r
5704 switch (message) {
\r
5705 case WM_INITDIALOG: /* message: initialize dialog box */
\r
5706 /* Center the dialog over the application window */
\r
5707 ofn = (OPENFILENAME *) lParam;
\r
5708 if (ofn->Flags & OFN_ENABLETEMPLATE) {
\r
5709 number = (UINT *) ofn->lCustData;
\r
5710 SendMessage(GetDlgItem(hDlg, edt2), WM_SETTEXT, 0, (LPARAM) "");
\r
5714 CenterWindow (hDlg, GetWindow (hDlg, GW_OWNER));
\r
5715 Translate(hDlg, 1536);
\r
5716 return FALSE; /* Allow for further processing */
\r
5719 if ((LOWORD(wParam) == IDOK) && (number != NULL)) {
\r
5720 *number = GetDlgItemInt(hDlg, OPT_IndexNumberOld, &ok, FALSE);
\r
5722 return FALSE; /* Allow for further processing */
\r
5728 OpenFileHook(HWND hdlg, UINT uiMsg, WPARAM wParam, LPARAM lParam)
\r
5730 static UINT *number;
\r
5731 OPENFILENAME *ofname;
\r
5734 case WM_INITDIALOG:
\r
5735 Translate(hdlg, DLG_IndexNumber);
\r
5736 ofname = (OPENFILENAME *)lParam;
\r
5737 number = (UINT *)(ofname->lCustData);
\r
5740 ofnot = (OFNOTIFY *)lParam;
\r
5741 if (ofnot->hdr.code == CDN_FILEOK) {
\r
5742 *number = GetDlgItemInt(hdlg, OPT_IndexNumber, NULL, FALSE);
\r
5751 OpenFileDialog(HWND hwnd, char *write, char *defName, char *defExt, // [HGM] diag: type of 'write' now string
\r
5752 char *nameFilt, char *dlgTitle, UINT *number,
\r
5753 char fileTitle[MSG_SIZ], char fileName[MSG_SIZ])
\r
5755 OPENFILENAME openFileName;
\r
5756 char buf1[MSG_SIZ];
\r
5759 if (fileName == NULL) fileName = buf1;
\r
5760 if (defName == NULL) {
\r
5761 safeStrCpy(fileName, "*.", 3 );
\r
5762 strcat(fileName, defExt);
\r
5764 safeStrCpy(fileName, defName, MSG_SIZ );
\r
5766 if (fileTitle) safeStrCpy(fileTitle, "", MSG_SIZ );
\r
5767 if (number) *number = 0;
\r
5769 openFileName.lStructSize = sizeof(OPENFILENAME);
\r
5770 openFileName.hwndOwner = hwnd;
\r
5771 openFileName.hInstance = (HANDLE) hInst;
\r
5772 openFileName.lpstrFilter = nameFilt;
\r
5773 openFileName.lpstrCustomFilter = (LPSTR) NULL;
\r
5774 openFileName.nMaxCustFilter = 0L;
\r
5775 openFileName.nFilterIndex = 1L;
\r
5776 openFileName.lpstrFile = fileName;
\r
5777 openFileName.nMaxFile = MSG_SIZ;
\r
5778 openFileName.lpstrFileTitle = fileTitle;
\r
5779 openFileName.nMaxFileTitle = fileTitle ? MSG_SIZ : 0;
\r
5780 openFileName.lpstrInitialDir = NULL;
\r
5781 openFileName.lpstrTitle = dlgTitle;
\r
5782 openFileName.Flags = OFN_PATHMUSTEXIST | OFN_HIDEREADONLY
\r
5783 | (write[0] != 'r' ? 0 : OFN_FILEMUSTEXIST)
\r
5784 | (number ? OFN_ENABLETEMPLATE | OFN_ENABLEHOOK: 0)
\r
5785 | (oldDialog ? 0 : OFN_EXPLORER);
\r
5786 openFileName.nFileOffset = 0;
\r
5787 openFileName.nFileExtension = 0;
\r
5788 openFileName.lpstrDefExt = defExt;
\r
5789 openFileName.lCustData = (LONG) number;
\r
5790 openFileName.lpfnHook = oldDialog ?
\r
5791 (LPOFNHOOKPROC) OldOpenFileHook : (LPOFNHOOKPROC) OpenFileHook;
\r
5792 openFileName.lpTemplateName = (LPSTR)(oldDialog ? 1536 : DLG_IndexNumber);
\r
5794 if (write[0] != 'r' ? GetSaveFileName(&openFileName) :
\r
5795 GetOpenFileName(&openFileName)) {
\r
5796 /* open the file */
\r
5797 f = fopen(openFileName.lpstrFile, write);
\r
5799 MessageBox(hwnd, _("File open failed"), NULL,
\r
5800 MB_OK|MB_ICONEXCLAMATION);
\r
5804 int err = CommDlgExtendedError();
\r
5805 if (err != 0) DisplayError(_("Internal error in file dialog box"), err);
\r
5814 MenuPopup(HWND hwnd, POINT pt, HMENU hmenu, UINT def)
\r
5816 HMENU hmenuTrackPopup; /* floating pop-up menu */
\r
5819 * Get the first pop-up menu in the menu template. This is the
\r
5820 * menu that TrackPopupMenu displays.
\r
5822 hmenuTrackPopup = GetSubMenu(hmenu, 0);
\r
5823 TranslateOneMenu(10, hmenuTrackPopup);
\r
5825 SetMenuDefaultItem(hmenuTrackPopup, def, FALSE);
\r
5828 * TrackPopup uses screen coordinates, so convert the
\r
5829 * coordinates of the mouse click to screen coordinates.
\r
5831 ClientToScreen(hwnd, (LPPOINT) &pt);
\r
5833 /* Draw and track the floating pop-up menu. */
\r
5834 TrackPopupMenu(hmenuTrackPopup, TPM_CENTERALIGN | TPM_RIGHTBUTTON,
\r
5835 pt.x, pt.y, 0, hwnd, NULL);
\r
5837 /* Destroy the menu.*/
\r
5838 DestroyMenu(hmenu);
\r
5843 int sizeX, sizeY, newSizeX, newSizeY;
\r
5845 } ResizeEditPlusButtonsClosure;
\r
5848 ResizeEditPlusButtonsCallback(HWND hChild, LPARAM lparam)
\r
5850 ResizeEditPlusButtonsClosure *cl = (ResizeEditPlusButtonsClosure *)lparam;
\r
5854 if (hChild == cl->hText) return TRUE;
\r
5855 GetWindowRect(hChild, &rect); /* gives screen coords */
\r
5856 pt.x = rect.left + (cl->newSizeX - cl->sizeX)/2;
\r
5857 pt.y = rect.top + cl->newSizeY - cl->sizeY;
\r
5858 ScreenToClient(cl->hDlg, &pt);
\r
5859 cl->hdwp = DeferWindowPos(cl->hdwp, hChild, NULL,
\r
5860 pt.x, pt.y, rect.right - rect.left, rect.bottom - rect.top, SWP_NOZORDER);
\r
5864 /* Resize a dialog that has a (rich) edit field filling most of
\r
5865 the top, with a row of buttons below */
\r
5867 ResizeEditPlusButtons(HWND hDlg, HWND hText, int sizeX, int sizeY, int newSizeX, int newSizeY)
\r
5870 int newTextHeight, newTextWidth;
\r
5871 ResizeEditPlusButtonsClosure cl;
\r
5873 /*if (IsIconic(hDlg)) return;*/
\r
5874 if (newSizeX == sizeX && newSizeY == sizeY) return;
\r
5876 cl.hdwp = BeginDeferWindowPos(8);
\r
5878 GetWindowRect(hText, &rectText); /* gives screen coords */
\r
5879 newTextWidth = rectText.right - rectText.left + newSizeX - sizeX;
\r
5880 newTextHeight = rectText.bottom - rectText.top + newSizeY - sizeY;
\r
5881 if (newTextHeight < 0) {
\r
5882 newSizeY += -newTextHeight;
\r
5883 newTextHeight = 0;
\r
5885 cl.hdwp = DeferWindowPos(cl.hdwp, hText, NULL, 0, 0,
\r
5886 newTextWidth, newTextHeight, SWP_NOZORDER|SWP_NOMOVE);
\r
5892 cl.newSizeX = newSizeX;
\r
5893 cl.newSizeY = newSizeY;
\r
5894 EnumChildWindows(hDlg, ResizeEditPlusButtonsCallback, (LPARAM)&cl);
\r
5896 EndDeferWindowPos(cl.hdwp);
\r
5899 BOOL CenterWindowEx(HWND hwndChild, HWND hwndParent, int mode)
\r
5901 RECT rChild, rParent;
\r
5902 int wChild, hChild, wParent, hParent;
\r
5903 int wScreen, hScreen, xNew, yNew;
\r
5906 /* Get the Height and Width of the child window */
\r
5907 GetWindowRect (hwndChild, &rChild);
\r
5908 wChild = rChild.right - rChild.left;
\r
5909 hChild = rChild.bottom - rChild.top;
\r
5911 /* Get the Height and Width of the parent window */
\r
5912 GetWindowRect (hwndParent, &rParent);
\r
5913 wParent = rParent.right - rParent.left;
\r
5914 hParent = rParent.bottom - rParent.top;
\r
5916 /* Get the display limits */
\r
5917 hdc = GetDC (hwndChild);
\r
5918 wScreen = GetDeviceCaps (hdc, HORZRES);
\r
5919 hScreen = GetDeviceCaps (hdc, VERTRES);
\r
5920 ReleaseDC(hwndChild, hdc);
\r
5922 /* Calculate new X position, then adjust for screen */
\r
5923 xNew = rParent.left + ((wParent - wChild) /2);
\r
5926 } else if ((xNew+wChild) > wScreen) {
\r
5927 xNew = wScreen - wChild;
\r
5930 /* Calculate new Y position, then adjust for screen */
\r
5932 yNew = rParent.top + ((hParent - hChild) /2);
\r
5935 yNew = rParent.top + GetSystemMetrics( SM_CYCAPTION ) * 2 / 3;
\r
5940 } else if ((yNew+hChild) > hScreen) {
\r
5941 yNew = hScreen - hChild;
\r
5944 /* Set it, and return */
\r
5945 return SetWindowPos (hwndChild, NULL,
\r
5946 xNew, yNew, 0, 0, SWP_NOSIZE | SWP_NOZORDER);
\r
5949 /* Center one window over another */
\r
5950 BOOL CenterWindow (HWND hwndChild, HWND hwndParent)
\r
5952 return CenterWindowEx( hwndChild, hwndParent, 0 );
\r
5955 /*---------------------------------------------------------------------------*\
\r
5957 * Startup Dialog functions
\r
5959 \*---------------------------------------------------------------------------*/
\r
5961 InitComboStrings(HANDLE hwndCombo, char **cd)
\r
5963 SendMessage(hwndCombo, CB_RESETCONTENT, 0, 0);
\r
5965 while (*cd != NULL) {
\r
5966 SendMessage(hwndCombo, CB_ADDSTRING, 0, (LPARAM) T_(*cd));
\r
5972 InitComboStringsFromOption(HANDLE hwndCombo, char *str)
\r
5974 char buf1[MAX_ARG_LEN];
\r
5977 if (str[0] == '@') {
\r
5978 FILE* f = fopen(str + 1, "r");
\r
5980 DisplayFatalError(str + 1, errno, 2);
\r
5983 len = fread(buf1, 1, sizeof(buf1)-1, f);
\r
5985 buf1[len] = NULLCHAR;
\r
5989 SendMessage(hwndCombo, CB_RESETCONTENT, 0, 0);
\r
5992 char buf[MSG_SIZ];
\r
5993 char *end = strchr(str, '\n');
\r
5994 if (end == NULL) return;
\r
5995 memcpy(buf, str, end - str);
\r
5996 buf[end - str] = NULLCHAR;
\r
5997 SendMessage(hwndCombo, CB_ADDSTRING, 0, (LPARAM) buf);
\r
6003 SetStartupDialogEnables(HWND hDlg)
\r
6005 EnableWindow(GetDlgItem(hDlg, OPT_ChessEngineName),
\r
6006 IsDlgButtonChecked(hDlg, OPT_ChessEngine) ||
\r
6007 (appData.zippyPlay && IsDlgButtonChecked(hDlg, OPT_ChessServer)));
\r
6008 EnableWindow(GetDlgItem(hDlg, OPT_SecondChessEngineName),
\r
6009 IsDlgButtonChecked(hDlg, OPT_ChessEngine));
\r
6010 EnableWindow(GetDlgItem(hDlg, OPT_ChessServerName),
\r
6011 IsDlgButtonChecked(hDlg, OPT_ChessServer));
\r
6012 EnableWindow(GetDlgItem(hDlg, OPT_AdditionalOptions),
\r
6013 IsDlgButtonChecked(hDlg, OPT_AnyAdditional));
\r
6014 EnableWindow(GetDlgItem(hDlg, IDOK),
\r
6015 IsDlgButtonChecked(hDlg, OPT_ChessEngine) ||
\r
6016 IsDlgButtonChecked(hDlg, OPT_ChessServer) ||
\r
6017 IsDlgButtonChecked(hDlg, OPT_View));
\r
6021 QuoteForFilename(char *filename)
\r
6023 int dquote, space;
\r
6024 dquote = strchr(filename, '"') != NULL;
\r
6025 space = strchr(filename, ' ') != NULL;
\r
6026 if (dquote || space) {
\r
6038 InitEngineBox(HWND hDlg, HWND hwndCombo, char* nthcp, char* nthd, char* nthdir, char *nthnames)
\r
6040 char buf[MSG_SIZ];
\r
6043 InitComboStringsFromOption(hwndCombo, nthnames);
\r
6044 q = QuoteForFilename(nthcp);
\r
6045 snprintf(buf, MSG_SIZ, "%s%s%s", q, nthcp, q);
\r
6046 if (*nthdir != NULLCHAR) {
\r
6047 q = QuoteForFilename(nthdir);
\r
6048 snprintf(buf + strlen(buf), MSG_SIZ, " /%s=%s%s%s", nthd, q, nthdir, q);
\r
6050 if (*nthcp == NULLCHAR) {
\r
6051 SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) 0, (LPARAM) 0);
\r
6052 } else if (SendMessage(hwndCombo, CB_SELECTSTRING, (WPARAM) -1, (LPARAM) buf) == CB_ERR) {
\r
6053 SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) -1, (LPARAM) 0);
\r
6054 SendMessage(hwndCombo, WM_SETTEXT, (WPARAM) 0, (LPARAM) buf);
\r
6059 StartupDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
6061 char buf[MSG_SIZ];
\r
6065 switch (message) {
\r
6066 case WM_INITDIALOG:
\r
6067 /* Center the dialog */
\r
6068 CenterWindow (hDlg, GetDesktopWindow());
\r
6069 Translate(hDlg, DLG_Startup);
\r
6070 /* Initialize the dialog items */
\r
6071 InitEngineBox(hDlg, GetDlgItem(hDlg, OPT_ChessEngineName),
\r
6072 appData.firstChessProgram, "fd", appData.firstDirectory,
\r
6073 firstChessProgramNames);
\r
6074 InitEngineBox(hDlg, GetDlgItem(hDlg, OPT_SecondChessEngineName),
\r
6075 appData.secondChessProgram, "sd", appData.secondDirectory,
\r
6076 secondChessProgramNames);
\r
6077 hwndCombo = GetDlgItem(hDlg, OPT_ChessServerName);
\r
6078 InitComboStringsFromOption(hwndCombo, icsNames);
\r
6079 snprintf(buf, MSG_SIZ, "%s /icsport=%s", appData.icsHost, appData.icsPort);
\r
6080 if (*appData.icsHelper != NULLCHAR) {
\r
6081 char *q = QuoteForFilename(appData.icsHelper);
\r
6082 sprintf(buf + strlen(buf), " /icshelper=%s%s%s", q, appData.icsHelper, q);
\r
6084 if (*appData.icsHost == NULLCHAR) {
\r
6085 SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) 0, (LPARAM) 0);
\r
6086 /*SendMessage(hwndCombo, CB_SHOWDROPDOWN, (WPARAM) TRUE, (LPARAM) 0); !!too soon */
\r
6087 } else if (SendMessage(hwndCombo, CB_SELECTSTRING, (WPARAM) -1, (LPARAM) buf) == CB_ERR) {
\r
6088 SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) -1, (LPARAM) 0);
\r
6089 SendMessage(hwndCombo, WM_SETTEXT, (WPARAM) 0, (LPARAM) buf);
\r
6092 if (appData.icsActive) {
\r
6093 CheckDlgButton(hDlg, OPT_ChessServer, BST_CHECKED);
\r
6095 else if (appData.noChessProgram) {
\r
6096 CheckDlgButton(hDlg, OPT_View, BST_CHECKED);
\r
6099 CheckDlgButton(hDlg, OPT_ChessEngine, BST_CHECKED);
\r
6102 SetStartupDialogEnables(hDlg);
\r
6106 switch (LOWORD(wParam)) {
\r
6108 if (IsDlgButtonChecked(hDlg, OPT_ChessEngine)) {
\r
6109 safeStrCpy(buf, "/fcp=", sizeof(buf)/sizeof(buf[0]) );
\r
6110 GetDlgItemText(hDlg, OPT_ChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));
\r
6112 ParseArgs(StringGet, &p);
\r
6113 safeStrCpy(buf, "/scp=", sizeof(buf)/sizeof(buf[0]) );
\r
6114 GetDlgItemText(hDlg, OPT_SecondChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));
\r
6116 ParseArgs(StringGet, &p);
\r
6117 appData.noChessProgram = FALSE;
\r
6118 appData.icsActive = FALSE;
\r
6119 } else if (IsDlgButtonChecked(hDlg, OPT_ChessServer)) {
\r
6120 safeStrCpy(buf, "/ics /icshost=", sizeof(buf)/sizeof(buf[0]) );
\r
6121 GetDlgItemText(hDlg, OPT_ChessServerName, buf + strlen(buf), sizeof(buf) - strlen(buf));
\r
6123 ParseArgs(StringGet, &p);
\r
6124 if (appData.zippyPlay) {
\r
6125 safeStrCpy(buf, "/fcp=", sizeof(buf)/sizeof(buf[0]) );
\r
6126 GetDlgItemText(hDlg, OPT_ChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));
\r
6128 ParseArgs(StringGet, &p);
\r
6130 } else if (IsDlgButtonChecked(hDlg, OPT_View)) {
\r
6131 appData.noChessProgram = TRUE;
\r
6132 appData.icsActive = FALSE;
\r
6134 MessageBox(hDlg, _("Choose an option, or cancel to exit"),
\r
6135 _("Option Error"), MB_OK|MB_ICONEXCLAMATION);
\r
6138 if (IsDlgButtonChecked(hDlg, OPT_AnyAdditional)) {
\r
6139 GetDlgItemText(hDlg, OPT_AdditionalOptions, buf, sizeof(buf));
\r
6141 ParseArgs(StringGet, &p);
\r
6143 EndDialog(hDlg, TRUE);
\r
6150 case IDM_HELPCONTENTS:
\r
6151 if (!WinHelp (hDlg, "winboard.hlp", HELP_KEY,(DWORD)(LPSTR)"CONTENTS")) {
\r
6152 MessageBox (GetFocus(),
\r
6153 _("Unable to activate help"),
\r
6154 szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);
\r
6159 SetStartupDialogEnables(hDlg);
\r
6167 /*---------------------------------------------------------------------------*\
\r
6169 * About box dialog functions
\r
6171 \*---------------------------------------------------------------------------*/
\r
6173 /* Process messages for "About" dialog box */
\r
6175 About(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
6177 switch (message) {
\r
6178 case WM_INITDIALOG: /* message: initialize dialog box */
\r
6179 /* Center the dialog over the application window */
\r
6180 CenterWindow (hDlg, GetWindow (hDlg, GW_OWNER));
\r
6181 SetDlgItemText(hDlg, ABOUTBOX_Version, programVersion);
\r
6182 Translate(hDlg, ABOUTBOX);
\r
6186 case WM_COMMAND: /* message: received a command */
\r
6187 if (LOWORD(wParam) == IDOK /* "OK" box selected? */
\r
6188 || LOWORD(wParam) == IDCANCEL) { /* System menu close command? */
\r
6189 EndDialog(hDlg, TRUE); /* Exit the dialog */
\r
6197 /*---------------------------------------------------------------------------*\
\r
6199 * Comment Dialog functions
\r
6201 \*---------------------------------------------------------------------------*/
\r
6204 CommentDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
6206 static HANDLE hwndText = NULL;
\r
6207 int len, newSizeX, newSizeY, flags;
\r
6208 static int sizeX, sizeY;
\r
6213 switch (message) {
\r
6214 case WM_INITDIALOG: /* message: initialize dialog box */
\r
6215 /* Initialize the dialog items */
\r
6216 Translate(hDlg, DLG_EditComment);
\r
6217 hwndText = GetDlgItem(hDlg, OPT_CommentText);
\r
6218 SetDlgItemText(hDlg, OPT_CommentText, commentText);
\r
6219 EnableWindow(GetDlgItem(hDlg, OPT_CancelComment), editComment);
\r
6220 EnableWindow(GetDlgItem(hDlg, OPT_ClearComment), editComment);
\r
6221 EnableWindow(GetDlgItem(hDlg, OPT_EditComment), !editComment);
\r
6222 SendMessage(hwndText, EM_SETREADONLY, !editComment, 0);
\r
6223 SetWindowText(hDlg, commentTitle);
\r
6224 if (editComment) {
\r
6225 SetFocus(hwndText);
\r
6227 SetFocus(GetDlgItem(hDlg, IDOK));
\r
6229 SendMessage(GetDlgItem(hDlg, OPT_CommentText),
\r
6230 WM_SETFONT, (WPARAM)font[boardSize][COMMENT_FONT]->hf,
\r
6231 MAKELPARAM(FALSE, 0));
\r
6232 /* Size and position the dialog */
\r
6233 if (!commentDialog) {
\r
6234 commentDialog = hDlg;
\r
6235 flags = SWP_NOZORDER;
\r
6236 GetClientRect(hDlg, &rect);
\r
6237 sizeX = rect.right;
\r
6238 sizeY = rect.bottom;
\r
6239 if (wpComment.x != CW_USEDEFAULT && wpComment.y != CW_USEDEFAULT &&
\r
6240 wpComment.width != CW_USEDEFAULT && wpComment.height != CW_USEDEFAULT) {
\r
6241 WINDOWPLACEMENT wp;
\r
6242 EnsureOnScreen(&wpComment.x, &wpComment.y, 0, 0);
\r
6243 wp.length = sizeof(WINDOWPLACEMENT);
\r
6245 wp.showCmd = SW_SHOW;
\r
6246 wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;
\r
6247 wp.rcNormalPosition.left = wpComment.x;
\r
6248 wp.rcNormalPosition.right = wpComment.x + wpComment.width;
\r
6249 wp.rcNormalPosition.top = wpComment.y;
\r
6250 wp.rcNormalPosition.bottom = wpComment.y + wpComment.height;
\r
6251 SetWindowPlacement(hDlg, &wp);
\r
6253 GetClientRect(hDlg, &rect);
\r
6254 newSizeX = rect.right;
\r
6255 newSizeY = rect.bottom;
\r
6256 ResizeEditPlusButtons(hDlg, hwndText, sizeX, sizeY,
\r
6257 newSizeX, newSizeY);
\r
6262 SendDlgItemMessage( hDlg, OPT_CommentText, EM_SETEVENTMASK, 0, ENM_MOUSEEVENTS | ENM_KEYEVENTS );
\r
6265 case WM_COMMAND: /* message: received a command */
\r
6266 switch (LOWORD(wParam)) {
\r
6268 if (editComment) {
\r
6270 /* Read changed options from the dialog box */
\r
6271 hwndText = GetDlgItem(hDlg, OPT_CommentText);
\r
6272 len = GetWindowTextLength(hwndText);
\r
6273 str = (char *) malloc(len + 1);
\r
6274 GetWindowText(hwndText, str, len + 1);
\r
6283 ReplaceComment(commentIndex, str);
\r
6290 case OPT_CancelComment:
\r
6294 case OPT_ClearComment:
\r
6295 SetDlgItemText(hDlg, OPT_CommentText, "");
\r
6298 case OPT_EditComment:
\r
6299 EditCommentEvent();
\r
6307 case WM_NOTIFY: // [HGM] vari: cloned from whistory.c
\r
6308 if( wParam == OPT_CommentText ) {
\r
6309 MSGFILTER * lpMF = (MSGFILTER *) lParam;
\r
6311 if( lpMF->msg == WM_RBUTTONDOWN && (lpMF->wParam & (MK_CONTROL | MK_SHIFT)) == 0 ||
\r
6312 lpMF->msg == WM_CHAR && lpMF->wParam == '\022' ) {
\r
6316 pt.x = LOWORD( lpMF->lParam );
\r
6317 pt.y = HIWORD( lpMF->lParam );
\r
6319 if(lpMF->msg == WM_CHAR) {
\r
6321 SendDlgItemMessage( hDlg, OPT_CommentText, EM_EXGETSEL, 0, (LPARAM) &sel );
\r
6322 index = sel.cpMin;
\r
6324 index = SendDlgItemMessage( hDlg, OPT_CommentText, EM_CHARFROMPOS, 0, (LPARAM) &pt );
\r
6326 hwndText = GetDlgItem(hDlg, OPT_CommentText); // cloned from above
\r
6327 len = GetWindowTextLength(hwndText);
\r
6328 str = (char *) malloc(len + 1);
\r
6329 GetWindowText(hwndText, str, len + 1);
\r
6330 ReplaceComment(commentIndex, str);
\r
6331 if(commentIndex != currentMove) ToNrEvent(commentIndex);
\r
6332 LoadVariation( index, str ); // [HGM] also does the actual moving to it, now
\r
6335 /* Zap the message for good: apparently, returning non-zero is not enough */
\r
6336 lpMF->msg = WM_USER;
\r
6344 newSizeX = LOWORD(lParam);
\r
6345 newSizeY = HIWORD(lParam);
\r
6346 ResizeEditPlusButtons(hDlg, hwndText, sizeX, sizeY, newSizeX, newSizeY);
\r
6351 case WM_GETMINMAXINFO:
\r
6352 /* Prevent resizing window too small */
\r
6353 mmi = (MINMAXINFO *) lParam;
\r
6354 mmi->ptMinTrackSize.x = 100;
\r
6355 mmi->ptMinTrackSize.y = 100;
\r
6362 EitherCommentPopUp(int index, char *title, char *str, BOOLEAN edit)
\r
6367 CheckMenuItem(GetMenu(hwndMain), IDM_Comment, edit ? MF_CHECKED : MF_UNCHECKED);
\r
6369 if (str == NULL) str = "";
\r
6370 p = (char *) malloc(2 * strlen(str) + 2);
\r
6373 if (*str == '\n') *q++ = '\r';
\r
6377 if (commentText != NULL) free(commentText);
\r
6379 commentIndex = index;
\r
6380 commentTitle = title;
\r
6382 editComment = edit;
\r
6384 if (commentDialog) {
\r
6385 SendMessage(commentDialog, WM_INITDIALOG, 0, 0);
\r
6386 if (!commentUp) ShowWindow(commentDialog, SW_SHOW);
\r
6388 lpProc = MakeProcInstance((FARPROC)CommentDialog, hInst);
\r
6389 CreateDialog(hInst, MAKEINTRESOURCE(DLG_EditComment),
\r
6390 hwndMain, (DLGPROC)lpProc);
\r
6391 FreeProcInstance(lpProc);
\r
6397 /*---------------------------------------------------------------------------*\
\r
6399 * Type-in move dialog functions
\r
6401 \*---------------------------------------------------------------------------*/
\r
6404 TypeInMoveDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
6406 char move[MSG_SIZ];
\r
6408 ChessMove moveType;
\r
6409 int fromX, fromY, toX, toY;
\r
6412 switch (message) {
\r
6413 case WM_INITDIALOG:
\r
6414 move[0] = (char) lParam;
\r
6415 move[1] = NULLCHAR;
\r
6416 CenterWindowEx(hDlg, GetWindow(hDlg, GW_OWNER), 1 );
\r
6417 Translate(hDlg, DLG_TypeInMove);
\r
6418 hInput = GetDlgItem(hDlg, OPT_Move);
\r
6419 SetWindowText(hInput, move);
\r
6421 SendMessage(hInput, EM_SETSEL, (WPARAM)9999, (LPARAM)9999);
\r
6425 switch (LOWORD(wParam)) {
\r
6428 shiftKey = GetKeyState(VK_SHIFT) < 0; // [HGM] remember last shift status
\r
6429 GetDlgItemText(hDlg, OPT_Move, move, sizeof(move));
\r
6430 { int n; Board board;
\r
6432 if(gameMode == EditPosition && ParseFEN(board, &n, move) ) {
\r
6433 EditPositionPasteFEN(move);
\r
6434 EndDialog(hDlg, TRUE);
\r
6437 // [HGM] movenum: allow move number to be typed in any mode
\r
6438 if(sscanf(move, "%d", &n) == 1 && n != 0 ) {
\r
6440 EndDialog(hDlg, TRUE);
\r
6444 if (gameMode != EditGame && currentMove != forwardMostMove &&
\r
6445 gameMode != Training) {
\r
6446 DisplayMoveError(_("Displayed move is not current"));
\r
6448 // GetDlgItemText(hDlg, OPT_Move, move, sizeof(move)); // moved upstream
\r
6449 int ok = ParseOneMove(move, gameMode == EditPosition ? blackPlaysFirst : currentMove,
\r
6450 &moveType, &fromX, &fromY, &toX, &toY, &promoChar);
\r
6451 if(!ok && move[0] >= 'a') { move[0] += 'A' - 'a'; ok = 2; } // [HGM] try also capitalized
\r
6452 if (ok==1 || ok && ParseOneMove(move, gameMode == EditPosition ? blackPlaysFirst : currentMove,
\r
6453 &moveType, &fromX, &fromY, &toX, &toY, &promoChar)) {
\r
6454 UserMoveEvent(fromX, fromY, toX, toY, promoChar);
\r
6456 DisplayMoveError(_("Could not parse move"));
\r
6459 EndDialog(hDlg, TRUE);
\r
6462 EndDialog(hDlg, FALSE);
\r
6473 PopUpMoveDialog(char firstchar)
\r
6477 if ((gameMode == BeginningOfGame && !appData.icsActive) ||
\r
6478 gameMode == MachinePlaysWhite || gameMode == MachinePlaysBlack ||
\r
6479 gameMode == AnalyzeMode || gameMode == EditGame ||
\r
6480 gameMode == EditPosition || gameMode == IcsExamining ||
\r
6481 gameMode == IcsPlayingWhite || gameMode == IcsPlayingBlack ||
\r
6482 isdigit(firstchar) && // [HGM] movenum: allow typing in of move nr in 'passive' modes
\r
6483 ( gameMode == AnalyzeFile || gameMode == PlayFromGameFile ||
\r
6484 gameMode == IcsObserving || gameMode == TwoMachinesPlay ) ||
\r
6485 gameMode == Training) {
\r
6486 lpProc = MakeProcInstance((FARPROC)TypeInMoveDialog, hInst);
\r
6487 DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_TypeInMove),
\r
6488 hwndMain, (DLGPROC)lpProc, (LPARAM)firstchar);
\r
6489 FreeProcInstance(lpProc);
\r
6493 /*---------------------------------------------------------------------------*\
\r
6495 * Type-in name dialog functions
\r
6497 \*---------------------------------------------------------------------------*/
\r
6500 TypeInNameDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
6502 char move[MSG_SIZ];
\r
6505 switch (message) {
\r
6506 case WM_INITDIALOG:
\r
6507 move[0] = (char) lParam;
\r
6508 move[1] = NULLCHAR;
\r
6509 CenterWindowEx(hDlg, GetWindow(hDlg, GW_OWNER), 1 );
\r
6510 Translate(hDlg, DLG_TypeInName);
\r
6511 hInput = GetDlgItem(hDlg, OPT_Name);
\r
6512 SetWindowText(hInput, move);
\r
6514 SendMessage(hInput, EM_SETSEL, (WPARAM)9999, (LPARAM)9999);
\r
6518 switch (LOWORD(wParam)) {
\r
6520 GetDlgItemText(hDlg, OPT_Name, move, sizeof(move));
\r
6521 appData.userName = strdup(move);
\r
6524 if(gameMode == MachinePlaysWhite || gameMode == MachinePlaysBlack) {
\r
6525 snprintf(move, MSG_SIZ, "%s vs. %s", gameInfo.white, gameInfo.black);
\r
6526 DisplayTitle(move);
\r
6530 EndDialog(hDlg, TRUE);
\r
6533 EndDialog(hDlg, FALSE);
\r
6544 PopUpNameDialog(char firstchar)
\r
6548 lpProc = MakeProcInstance((FARPROC)TypeInNameDialog, hInst);
\r
6549 DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_TypeInName),
\r
6550 hwndMain, (DLGPROC)lpProc, (LPARAM)firstchar);
\r
6551 FreeProcInstance(lpProc);
\r
6554 /*---------------------------------------------------------------------------*\
\r
6558 \*---------------------------------------------------------------------------*/
\r
6560 /* Nonmodal error box */
\r
6561 LRESULT CALLBACK ErrorDialog(HWND hDlg, UINT message,
\r
6562 WPARAM wParam, LPARAM lParam);
\r
6565 ErrorPopUp(char *title, char *content)
\r
6569 BOOLEAN modal = hwndMain == NULL;
\r
6587 strncpy(errorTitle, title, sizeof(errorTitle));
\r
6588 errorTitle[sizeof(errorTitle) - 1] = '\0';
\r
6591 MessageBox(NULL, errorMessage, errorTitle, MB_OK|MB_ICONEXCLAMATION);
\r
6593 lpProc = MakeProcInstance((FARPROC)ErrorDialog, hInst);
\r
6594 CreateDialog(hInst, MAKEINTRESOURCE(DLG_Error),
\r
6595 hwndMain, (DLGPROC)lpProc);
\r
6596 FreeProcInstance(lpProc);
\r
6603 if (!appData.popupMoveErrors && moveErrorMessageUp) DisplayMessage("", "");
\r
6604 if (errorDialog == NULL) return;
\r
6605 DestroyWindow(errorDialog);
\r
6606 errorDialog = NULL;
\r
6607 if(errorExitStatus) ExitEvent(errorExitStatus);
\r
6611 ErrorDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
6616 switch (message) {
\r
6617 case WM_INITDIALOG:
\r
6618 GetWindowRect(hDlg, &rChild);
\r
6621 SetWindowPos(hDlg, NULL, rChild.left,
\r
6622 rChild.top + boardRect.top - (rChild.bottom - rChild.top),
\r
6623 0, 0, SWP_NOZORDER|SWP_NOSIZE);
\r
6627 [AS] It seems that the above code wants to move the dialog up in the "caption
\r
6628 area" of the main window, but it uses the dialog height as an hard-coded constant,
\r
6629 and it doesn't work when you resize the dialog.
\r
6630 For now, just give it a default position.
\r
6632 SetWindowPos(hDlg, NULL, boardRect.left+8, boardRect.top+8, 0, 0, SWP_NOZORDER|SWP_NOSIZE);
\r
6633 Translate(hDlg, DLG_Error);
\r
6635 errorDialog = hDlg;
\r
6636 SetWindowText(hDlg, errorTitle);
\r
6637 hwndText = GetDlgItem(hDlg, OPT_ErrorText);
\r
6638 SetDlgItemText(hDlg, OPT_ErrorText, errorMessage);
\r
6642 switch (LOWORD(wParam)) {
\r
6645 if (errorDialog == hDlg) errorDialog = NULL;
\r
6646 DestroyWindow(hDlg);
\r
6658 HWND gothicDialog = NULL;
\r
6661 GothicDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
6665 int height = GetSystemMetrics(SM_CYCAPTION)+GetSystemMetrics(SM_CYFRAME);
\r
6667 switch (message) {
\r
6668 case WM_INITDIALOG:
\r
6669 GetWindowRect(hDlg, &rChild);
\r
6671 SetWindowPos(hDlg, NULL, wpMain.x, wpMain.y-height, wpMain.width, height,
\r
6675 [AS] It seems that the above code wants to move the dialog up in the "caption
\r
6676 area" of the main window, but it uses the dialog height as an hard-coded constant,
\r
6677 and it doesn't work when you resize the dialog.
\r
6678 For now, just give it a default position.
\r
6680 gothicDialog = hDlg;
\r
6681 SetWindowText(hDlg, errorTitle);
\r
6682 hwndText = GetDlgItem(hDlg, OPT_ErrorText);
\r
6683 SetDlgItemText(hDlg, OPT_ErrorText, errorMessage);
\r
6687 switch (LOWORD(wParam)) {
\r
6690 if (errorDialog == hDlg) errorDialog = NULL;
\r
6691 DestroyWindow(hDlg);
\r
6703 GothicPopUp(char *title, VariantClass variant)
\r
6706 static char *lastTitle;
\r
6708 strncpy(errorTitle, title, sizeof(errorTitle));
\r
6709 errorTitle[sizeof(errorTitle) - 1] = '\0';
\r
6711 if(lastTitle != title && gothicDialog != NULL) {
\r
6712 DestroyWindow(gothicDialog);
\r
6713 gothicDialog = NULL;
\r
6715 if(variant != VariantNormal && gothicDialog == NULL) {
\r
6716 title = lastTitle;
\r
6717 lpProc = MakeProcInstance((FARPROC)GothicDialog, hInst);
\r
6718 CreateDialog(hInst, MAKEINTRESOURCE(DLG_Error),
\r
6719 hwndMain, (DLGPROC)lpProc);
\r
6720 FreeProcInstance(lpProc);
\r
6725 /*---------------------------------------------------------------------------*\
\r
6727 * Ics Interaction console functions
\r
6729 \*---------------------------------------------------------------------------*/
\r
6731 #define HISTORY_SIZE 64
\r
6732 static char *history[HISTORY_SIZE];
\r
6733 int histIn = 0, histP = 0;
\r
6736 SaveInHistory(char *cmd)
\r
6738 if (history[histIn] != NULL) {
\r
6739 free(history[histIn]);
\r
6740 history[histIn] = NULL;
\r
6742 if (*cmd == NULLCHAR) return;
\r
6743 history[histIn] = StrSave(cmd);
\r
6744 histIn = (histIn + 1) % HISTORY_SIZE;
\r
6745 if (history[histIn] != NULL) {
\r
6746 free(history[histIn]);
\r
6747 history[histIn] = NULL;
\r
6753 PrevInHistory(char *cmd)
\r
6756 if (histP == histIn) {
\r
6757 if (history[histIn] != NULL) free(history[histIn]);
\r
6758 history[histIn] = StrSave(cmd);
\r
6760 newhp = (histP - 1 + HISTORY_SIZE) % HISTORY_SIZE;
\r
6761 if (newhp == histIn || history[newhp] == NULL) return NULL;
\r
6763 return history[histP];
\r
6769 if (histP == histIn) return NULL;
\r
6770 histP = (histP + 1) % HISTORY_SIZE;
\r
6771 return history[histP];
\r
6775 LoadIcsTextMenu(IcsTextMenuEntry *e)
\r
6779 hmenu = LoadMenu(hInst, "TextMenu");
\r
6780 h = GetSubMenu(hmenu, 0);
\r
6782 if (strcmp(e->item, "-") == 0) {
\r
6783 AppendMenu(h, MF_SEPARATOR, 0, 0);
\r
6784 } else { // [HGM] re-written a bit to use only one AppendMenu call for both cases (| or no |)
\r
6785 int flags = MF_STRING, j = 0;
\r
6786 if (e->item[0] == '|') {
\r
6787 flags |= MF_MENUBARBREAK;
\r
6790 if(!strcmp(e->command, "none")) flags |= MF_GRAYED; // [HGM] chatclick: provide inactive dummy
\r
6791 AppendMenu(h, flags, IDM_CommandX + i, e->item + j);
\r
6799 WNDPROC consoleTextWindowProc;
\r
6802 CommandX(HWND hwnd, char *command, BOOLEAN getname, BOOLEAN immediate)
\r
6804 char buf[MSG_SIZ], name[MSG_SIZ];
\r
6805 HWND hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
6809 SetWindowText(hInput, command);
\r
6811 SendMessage(hInput, WM_CHAR, '\r', 0);
\r
6813 sel.cpMin = 999999;
\r
6814 sel.cpMax = 999999;
\r
6815 SendMessage(hInput, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
6820 SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);
\r
6821 if (sel.cpMin == sel.cpMax) {
\r
6822 /* Expand to surrounding word */
\r
6825 tr.chrg.cpMax = sel.cpMin;
\r
6826 tr.chrg.cpMin = --sel.cpMin;
\r
6827 if (sel.cpMin < 0) break;
\r
6828 tr.lpstrText = name;
\r
6829 SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr);
\r
6830 } while (isalpha(name[0]) || isdigit(name[0]) || name[0] == '-');
\r
6834 tr.chrg.cpMin = sel.cpMax;
\r
6835 tr.chrg.cpMax = ++sel.cpMax;
\r
6836 tr.lpstrText = name;
\r
6837 if (SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr) < 1) break;
\r
6838 } while (isalpha(name[0]) || isdigit(name[0]) || name[0] == '-');
\r
6841 if (sel.cpMax == sel.cpMin || sel.cpMax - sel.cpMin > MSG_SIZ/2) {
\r
6842 MessageBeep(MB_ICONEXCLAMATION);
\r
6846 tr.lpstrText = name;
\r
6847 SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr);
\r
6849 if (sel.cpMax - sel.cpMin > MSG_SIZ/2) {
\r
6850 MessageBeep(MB_ICONEXCLAMATION);
\r
6853 SendMessage(hwnd, EM_GETSELTEXT, 0, (LPARAM) name);
\r
6856 if(strstr(command, "%s")) snprintf(buf, MSG_SIZ, command, name); else
\r
6857 snprintf(buf, MSG_SIZ, "%s %s", command, name);
\r
6858 SetWindowText(hInput, buf);
\r
6859 SendMessage(hInput, WM_CHAR, '\r', 0);
\r
6861 if(!strcmp(command, "chat")) { ChatPopUp(name); return; }
\r
6862 snprintf(buf, MSG_SIZ, "%s %s ", command, name); /* trailing space */
\r
6863 SetWindowText(hInput, buf);
\r
6864 sel.cpMin = 999999;
\r
6865 sel.cpMax = 999999;
\r
6866 SendMessage(hInput, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
6872 ConsoleTextSubclass(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
\r
6877 switch (message) {
\r
6879 if (!(GetKeyState(VK_CONTROL) & ~1)) break;
\r
6880 if(wParam=='R') return 0;
\r
6883 SendMessage(hwnd, EM_LINESCROLL, 0, -999999);
\r
6886 sel.cpMin = 999999;
\r
6887 sel.cpMax = 999999;
\r
6888 SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
6889 SendMessage(hwnd, EM_SCROLLCARET, 0, 0);
\r
6894 if(wParam != '\022') {
\r
6895 if (wParam == '\t') {
\r
6896 if (GetKeyState(VK_SHIFT) < 0) {
\r
6898 if (IsIconic(hwndMain)) ShowWindow(hwndMain, SW_RESTORE);
\r
6899 if (buttonDesc[0].hwnd) {
\r
6900 SetFocus(buttonDesc[0].hwnd);
\r
6902 SetFocus(hwndMain);
\r
6906 SetFocus(GetDlgItem(hwndConsole, OPT_ConsoleInput));
\r
6909 hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
6910 JAWS_DELETE( SetFocus(hInput); )
\r
6911 SendMessage(hInput, message, wParam, lParam);
\r
6914 } // [HGM] navigate: for Ctrl+R, flow into next case (moved up here) to summon up menu
\r
6916 case WM_RBUTTONDOWN:
\r
6917 if (!(GetKeyState(VK_SHIFT) & ~1)) {
\r
6918 /* Move selection here if it was empty */
\r
6920 pt.x = LOWORD(lParam);
\r
6921 pt.y = HIWORD(lParam);
\r
6922 SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);
\r
6923 if (sel.cpMin == sel.cpMax) {
\r
6924 if(lParam != -1) sel.cpMin = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&pt); /*doc is wrong*/
\r
6925 sel.cpMax = sel.cpMin;
\r
6926 SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
6928 SendMessage(hwnd, EM_HIDESELECTION, FALSE, FALSE);
\r
6929 { // [HGM] chatclick: code moved here from WM_RBUTTONUP case, to have menu appear on down-click
\r
6931 HMENU hmenu = LoadIcsTextMenu(icsTextMenuEntry);
\r
6932 SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);
\r
6933 if (sel.cpMin == sel.cpMax) {
\r
6934 EnableMenuItem(hmenu, IDM_Copy, MF_BYCOMMAND|MF_GRAYED);
\r
6935 EnableMenuItem(hmenu, IDM_QuickPaste, MF_BYCOMMAND|MF_GRAYED);
\r
6937 if (!IsClipboardFormatAvailable(CF_TEXT)) {
\r
6938 EnableMenuItem(hmenu, IDM_Paste, MF_BYCOMMAND|MF_GRAYED);
\r
6940 pt.x = LOWORD(lParam)-30; // [HGM] chatclick: make menu pop up with pointer above upper-right item
\r
6941 pt.y = HIWORD(lParam)-10; // make it appear as if mouse moved there, so it will be selected on up-click
\r
6942 PostMessage(hwnd, WM_MOUSEMOVE, wParam, lParam+5);
\r
6943 MenuPopup(hwnd, pt, hmenu, -1);
\r
6947 case WM_RBUTTONUP:
\r
6948 if (GetKeyState(VK_SHIFT) & ~1) {
\r
6949 SendDlgItemMessage(hwndConsole, OPT_ConsoleText,
\r
6950 WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);
\r
6954 hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
6956 return SendMessage(hInput, message, wParam, lParam);
\r
6957 case WM_MBUTTONDOWN:
\r
6958 return SendMessage(hwnd, WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);
\r
6960 switch (LOWORD(wParam)) {
\r
6961 case IDM_QuickPaste:
\r
6963 SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);
\r
6964 if (sel.cpMin == sel.cpMax) {
\r
6965 MessageBeep(MB_ICONEXCLAMATION);
\r
6968 SendMessage(hwnd, WM_COPY, 0, 0);
\r
6969 hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
6970 SendMessage(hInput, WM_PASTE, 0, 0);
\r
6975 SendMessage(hwnd, WM_CUT, 0, 0);
\r
6978 SendMessage(hwnd, WM_PASTE, 0, 0);
\r
6981 SendMessage(hwnd, WM_COPY, 0, 0);
\r
6985 int i = LOWORD(wParam) - IDM_CommandX;
\r
6986 if (i >= 0 && i < ICS_TEXT_MENU_SIZE &&
\r
6987 icsTextMenuEntry[i].command != NULL) {
\r
6988 CommandX(hwnd, icsTextMenuEntry[i].command,
\r
6989 icsTextMenuEntry[i].getname,
\r
6990 icsTextMenuEntry[i].immediate);
\r
6998 return (*consoleTextWindowProc)(hwnd, message, wParam, lParam);
\r
7001 WNDPROC consoleInputWindowProc;
\r
7004 ConsoleInputSubclass(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
\r
7006 char buf[MSG_SIZ];
\r
7008 static BOOL sendNextChar = FALSE;
\r
7009 static BOOL quoteNextChar = FALSE;
\r
7010 InputSource *is = consoleInputSource;
\r
7014 switch (message) {
\r
7016 if (!appData.localLineEditing || sendNextChar) {
\r
7017 is->buf[0] = (CHAR) wParam;
\r
7019 SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);
\r
7020 sendNextChar = FALSE;
\r
7023 if (quoteNextChar) {
\r
7024 buf[0] = (char) wParam;
\r
7025 buf[1] = NULLCHAR;
\r
7026 SendMessage(hwnd, EM_REPLACESEL, TRUE, (LPARAM) buf);
\r
7027 quoteNextChar = FALSE;
\r
7031 case '\r': /* Enter key */
\r
7032 is->count = GetWindowText(hwnd, is->buf, INPUT_SOURCE_BUF_SIZE-1);
\r
7033 if (consoleEcho) SaveInHistory(is->buf);
\r
7034 is->buf[is->count++] = '\n';
\r
7035 is->buf[is->count] = NULLCHAR;
\r
7036 SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);
\r
7037 if (consoleEcho) {
\r
7038 ConsoleOutput(is->buf, is->count, TRUE);
\r
7039 } else if (appData.localLineEditing) {
\r
7040 ConsoleOutput("\n", 1, TRUE);
\r
7043 case '\033': /* Escape key */
\r
7044 SetWindowText(hwnd, "");
\r
7045 cf.cbSize = sizeof(CHARFORMAT);
\r
7046 cf.dwMask = CFM_COLOR|CFM_BOLD|CFM_ITALIC|CFM_UNDERLINE|CFM_STRIKEOUT;
\r
7047 if (consoleEcho) {
\r
7048 cf.crTextColor = textAttribs[ColorNormal].color;
\r
7050 cf.crTextColor = COLOR_ECHOOFF;
\r
7052 cf.dwEffects = textAttribs[ColorNormal].effects;
\r
7053 SendMessage(hwnd, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf);
\r
7055 case '\t': /* Tab key */
\r
7056 if (GetKeyState(VK_SHIFT) < 0) {
\r
7058 SetFocus(GetDlgItem(hwndConsole, OPT_ConsoleText));
\r
7061 if (IsIconic(hwndMain)) ShowWindow(hwndMain, SW_RESTORE);
\r
7062 if (buttonDesc[0].hwnd) {
\r
7063 SetFocus(buttonDesc[0].hwnd);
\r
7065 SetFocus(hwndMain);
\r
7069 case '\023': /* Ctrl+S */
\r
7070 sendNextChar = TRUE;
\r
7072 case '\021': /* Ctrl+Q */
\r
7073 quoteNextChar = TRUE;
\r
7083 GetWindowText(hwnd, buf, MSG_SIZ);
\r
7084 p = PrevInHistory(buf);
\r
7086 SetWindowText(hwnd, p);
\r
7087 sel.cpMin = 999999;
\r
7088 sel.cpMax = 999999;
\r
7089 SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
7094 p = NextInHistory();
\r
7096 SetWindowText(hwnd, p);
\r
7097 sel.cpMin = 999999;
\r
7098 sel.cpMax = 999999;
\r
7099 SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
7105 if (!(GetKeyState(VK_CONTROL) & ~1)) break;
\r
7109 SendDlgItemMessage(hwndConsole, OPT_ConsoleText, message, wParam, lParam);
\r
7113 case WM_MBUTTONDOWN:
\r
7114 SendDlgItemMessage(hwndConsole, OPT_ConsoleText,
\r
7115 WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);
\r
7117 case WM_RBUTTONUP:
\r
7118 if (GetKeyState(VK_SHIFT) & ~1) {
\r
7119 SendDlgItemMessage(hwndConsole, OPT_ConsoleText,
\r
7120 WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);
\r
7124 hmenu = LoadMenu(hInst, "InputMenu");
\r
7125 SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);
\r
7126 if (sel.cpMin == sel.cpMax) {
\r
7127 EnableMenuItem(hmenu, IDM_Copy, MF_BYCOMMAND|MF_GRAYED);
\r
7128 EnableMenuItem(hmenu, IDM_Cut, MF_BYCOMMAND|MF_GRAYED);
\r
7130 if (!IsClipboardFormatAvailable(CF_TEXT)) {
\r
7131 EnableMenuItem(hmenu, IDM_Paste, MF_BYCOMMAND|MF_GRAYED);
\r
7133 pt.x = LOWORD(lParam);
\r
7134 pt.y = HIWORD(lParam);
\r
7135 MenuPopup(hwnd, pt, hmenu, -1);
\r
7139 switch (LOWORD(wParam)) {
\r
7141 SendMessage(hwnd, EM_UNDO, 0, 0);
\r
7143 case IDM_SelectAll:
\r
7145 sel.cpMax = -1; /*999999?*/
\r
7146 SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
7149 SendMessage(hwnd, WM_CUT, 0, 0);
\r
7152 SendMessage(hwnd, WM_PASTE, 0, 0);
\r
7155 SendMessage(hwnd, WM_COPY, 0, 0);
\r
7160 return (*consoleInputWindowProc)(hwnd, message, wParam, lParam);
\r
7163 #define CO_MAX 100000
\r
7164 #define CO_TRIM 1000
\r
7167 ConsoleWndProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
7169 static SnapData sd;
\r
7170 HWND hText, hInput;
\r
7172 static int sizeX, sizeY;
\r
7173 int newSizeX, newSizeY;
\r
7177 hText = GetDlgItem(hDlg, OPT_ConsoleText);
\r
7178 hInput = GetDlgItem(hDlg, OPT_ConsoleInput);
\r
7180 switch (message) {
\r
7182 if (((NMHDR*)lParam)->code == EN_LINK)
\r
7184 ENLINK *pLink = (ENLINK*)lParam;
\r
7185 if (pLink->msg == WM_LBUTTONUP)
\r
7189 tr.chrg = pLink->chrg;
\r
7190 tr.lpstrText = malloc(1+tr.chrg.cpMax-tr.chrg.cpMin);
\r
7191 SendMessage(hText, EM_GETTEXTRANGE, 0, (LPARAM)&tr);
\r
7192 ShellExecute(NULL, "open", tr.lpstrText, NULL, NULL, SW_SHOW);
\r
7193 free(tr.lpstrText);
\r
7197 case WM_INITDIALOG: /* message: initialize dialog box */
\r
7198 hwndConsole = hDlg;
\r
7200 consoleTextWindowProc = (WNDPROC)
\r
7201 SetWindowLongPtr(hText, GWLP_WNDPROC, (LONG_PTR) ConsoleTextSubclass);
\r
7202 SendMessage(hText, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);
\r
7203 consoleInputWindowProc = (WNDPROC)
\r
7204 SetWindowLongPtr(hInput, GWLP_WNDPROC, (LONG_PTR) ConsoleInputSubclass);
\r
7205 SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);
\r
7206 Colorize(ColorNormal, TRUE);
\r
7207 SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &consoleCF);
\r
7208 ChangedConsoleFont();
\r
7209 GetClientRect(hDlg, &rect);
\r
7210 sizeX = rect.right;
\r
7211 sizeY = rect.bottom;
\r
7212 if (wpConsole.x != CW_USEDEFAULT && wpConsole.y != CW_USEDEFAULT &&
\r
7213 wpConsole.width != CW_USEDEFAULT && wpConsole.height != CW_USEDEFAULT) {
\r
7214 WINDOWPLACEMENT wp;
\r
7215 EnsureOnScreen(&wpConsole.x, &wpConsole.y, 0, 0);
\r
7216 wp.length = sizeof(WINDOWPLACEMENT);
\r
7218 wp.showCmd = SW_SHOW;
\r
7219 wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;
\r
7220 wp.rcNormalPosition.left = wpConsole.x;
\r
7221 wp.rcNormalPosition.right = wpConsole.x + wpConsole.width;
\r
7222 wp.rcNormalPosition.top = wpConsole.y;
\r
7223 wp.rcNormalPosition.bottom = wpConsole.y + wpConsole.height;
\r
7224 SetWindowPlacement(hDlg, &wp);
\r
7227 // [HGM] Chessknight's change 2004-07-13
\r
7228 else { /* Determine Defaults */
\r
7229 WINDOWPLACEMENT wp;
\r
7230 wpConsole.x = wpMain.width + 1;
\r
7231 wpConsole.y = wpMain.y;
\r
7232 wpConsole.width = screenWidth - wpMain.width;
\r
7233 wpConsole.height = wpMain.height;
\r
7234 EnsureOnScreen(&wpConsole.x, &wpConsole.y, 0, 0);
\r
7235 wp.length = sizeof(WINDOWPLACEMENT);
\r
7237 wp.showCmd = SW_SHOW;
\r
7238 wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;
\r
7239 wp.rcNormalPosition.left = wpConsole.x;
\r
7240 wp.rcNormalPosition.right = wpConsole.x + wpConsole.width;
\r
7241 wp.rcNormalPosition.top = wpConsole.y;
\r
7242 wp.rcNormalPosition.bottom = wpConsole.y + wpConsole.height;
\r
7243 SetWindowPlacement(hDlg, &wp);
\r
7246 // Allow hText to highlight URLs and send notifications on them
\r
7247 wMask = (WORD) SendMessage(hText, EM_GETEVENTMASK, 0, 0L);
\r
7248 SendMessage(hText, EM_SETEVENTMASK, 0, wMask | ENM_LINK);
\r
7249 SendMessage(hText, EM_AUTOURLDETECT, TRUE, 0L);
\r
7250 SetWindowLongPtr(hText, GWLP_USERDATA, 79); // initialize the text window's width
\r
7264 if (IsIconic(hDlg)) break;
\r
7265 newSizeX = LOWORD(lParam);
\r
7266 newSizeY = HIWORD(lParam);
\r
7267 if (sizeX != newSizeX || sizeY != newSizeY) {
\r
7268 RECT rectText, rectInput;
\r
7270 int newTextHeight, newTextWidth;
\r
7271 GetWindowRect(hText, &rectText);
\r
7272 newTextWidth = rectText.right - rectText.left + newSizeX - sizeX;
\r
7273 newTextHeight = rectText.bottom - rectText.top + newSizeY - sizeY;
\r
7274 if (newTextHeight < 0) {
\r
7275 newSizeY += -newTextHeight;
\r
7276 newTextHeight = 0;
\r
7278 SetWindowPos(hText, NULL, 0, 0,
\r
7279 newTextWidth, newTextHeight, SWP_NOZORDER|SWP_NOMOVE);
\r
7280 GetWindowRect(hInput, &rectInput); /* gives screen coords */
\r
7281 pt.x = rectInput.left;
\r
7282 pt.y = rectInput.top + newSizeY - sizeY;
\r
7283 ScreenToClient(hDlg, &pt);
\r
7284 SetWindowPos(hInput, NULL,
\r
7285 pt.x, pt.y, /* needs client coords */
\r
7286 rectInput.right - rectInput.left + newSizeX - sizeX,
\r
7287 rectInput.bottom - rectInput.top, SWP_NOZORDER);
\r
7293 case WM_GETMINMAXINFO:
\r
7294 /* Prevent resizing window too small */
\r
7295 mmi = (MINMAXINFO *) lParam;
\r
7296 mmi->ptMinTrackSize.x = 100;
\r
7297 mmi->ptMinTrackSize.y = 100;
\r
7300 /* [AS] Snapping */
\r
7301 case WM_ENTERSIZEMOVE:
\r
7302 return OnEnterSizeMove( &sd, hDlg, wParam, lParam );
\r
7305 return OnSizing( &sd, hDlg, wParam, lParam );
\r
7308 return OnMoving( &sd, hDlg, wParam, lParam );
\r
7310 case WM_EXITSIZEMOVE:
\r
7311 UpdateICSWidth(hText);
\r
7312 return OnExitSizeMove( &sd, hDlg, wParam, lParam );
\r
7315 return DefWindowProc(hDlg, message, wParam, lParam);
\r
7323 if (hwndConsole) return;
\r
7324 hCons = CreateDialog(hInst, szConsoleName, 0, NULL);
\r
7325 SendMessage(hCons, WM_INITDIALOG, 0, 0);
\r
7330 ConsoleOutput(char* data, int length, int forceVisible)
\r
7335 char buf[CO_MAX+1];
\r
7338 static int delayLF = 0;
\r
7339 CHARRANGE savesel, sel;
\r
7341 if (hwndConsole == NULL || length > CO_MAX-100 || length == 0) return;
\r
7349 while (length--) {
\r
7357 } else if (*p == '\007') {
\r
7358 MyPlaySound(&sounds[(int)SoundBell]);
\r
7365 hText = GetDlgItem(hwndConsole, OPT_ConsoleText);
\r
7366 SendMessage(hText, EM_HIDESELECTION, TRUE, FALSE);
\r
7367 /* Save current selection */
\r
7368 SendMessage(hText, EM_EXGETSEL, 0, (LPARAM)&savesel);
\r
7369 exlen = GetWindowTextLength(hText);
\r
7370 /* Find out whether current end of text is visible */
\r
7371 SendMessage(hText, EM_GETRECT, 0, (LPARAM) &rect);
\r
7372 SendMessage(hText, EM_POSFROMCHAR, (WPARAM) &pEnd, exlen);
\r
7373 /* Trim existing text if it's too long */
\r
7374 if (exlen + (q - buf) > CO_MAX) {
\r
7375 trim = (CO_TRIM > (q - buf)) ? CO_TRIM : (q - buf);
\r
7378 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
7379 SendMessage(hText, EM_REPLACESEL, 0, (LPARAM)"");
\r
7381 savesel.cpMin -= trim;
\r
7382 savesel.cpMax -= trim;
\r
7383 if (exlen < 0) exlen = 0;
\r
7384 if (savesel.cpMin < 0) savesel.cpMin = 0;
\r
7385 if (savesel.cpMax < savesel.cpMin) savesel.cpMax = savesel.cpMin;
\r
7387 /* Append the new text */
\r
7388 sel.cpMin = exlen;
\r
7389 sel.cpMax = exlen;
\r
7390 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
7391 SendMessage(hText, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&consoleCF);
\r
7392 SendMessage(hText, EM_REPLACESEL, 0, (LPARAM) buf);
\r
7393 if (forceVisible || exlen == 0 ||
\r
7394 (rect.left <= pEnd.x && pEnd.x < rect.right &&
\r
7395 rect.top <= pEnd.y && pEnd.y < rect.bottom)) {
\r
7396 /* Scroll to make new end of text visible if old end of text
\r
7397 was visible or new text is an echo of user typein */
\r
7398 sel.cpMin = 9999999;
\r
7399 sel.cpMax = 9999999;
\r
7400 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
7401 SendMessage(hText, EM_HIDESELECTION, FALSE, FALSE);
\r
7402 SendMessage(hText, EM_SCROLLCARET, 0, 0);
\r
7403 SendMessage(hText, EM_HIDESELECTION, TRUE, FALSE);
\r
7405 if (savesel.cpMax == exlen || forceVisible) {
\r
7406 /* Move insert point to new end of text if it was at the old
\r
7407 end of text or if the new text is an echo of user typein */
\r
7408 sel.cpMin = 9999999;
\r
7409 sel.cpMax = 9999999;
\r
7410 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
7412 /* Restore previous selection */
\r
7413 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&savesel);
\r
7415 SendMessage(hText, EM_HIDESELECTION, FALSE, FALSE);
\r
7422 DisplayHoldingsCount(HDC hdc, int x, int y, int rightAlign, int copyNumber)
\r
7426 COLORREF oldFg, oldBg;
\r
7431 snprintf(buf, sizeof(buf)/sizeof(buf[0]),"%d", copyNumber); else buf[0] = 0;
\r
7433 oldFg = SetTextColor(hdc, RGB(255, 255, 255)); /* white */
\r
7434 oldBg = SetBkColor(hdc, RGB(0, 0, 0)); /* black */
\r
7435 oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);
\r
7438 rect.right = x + squareSize;
\r
7440 rect.bottom = y + squareSize;
\r
7443 ExtTextOut(hdc, x + MESSAGE_LINE_LEFTMARGIN
\r
7444 + (rightAlign ? (squareSize*2)/3 : 0),
\r
7445 y, ETO_CLIPPED|ETO_OPAQUE,
\r
7446 &rect, str, strlen(str), NULL);
\r
7448 (void) SetTextColor(hdc, oldFg);
\r
7449 (void) SetBkColor(hdc, oldBg);
\r
7450 (void) SelectObject(hdc, oldFont);
\r
7454 DisplayAClock(HDC hdc, int timeRemaining, int highlight,
\r
7455 RECT *rect, char *color, char *flagFell)
\r
7459 COLORREF oldFg, oldBg;
\r
7462 if (appData.clockMode) {
\r
7464 snprintf(buf, sizeof(buf)/sizeof(buf[0]), "%c %s %s", color[0], TimeString(timeRemaining), flagFell);
\r
7466 snprintf(buf, sizeof(buf)/sizeof(buf[0]), "%s:%c%s %s", color, (logoHeight>0 ? 0 : ' '), TimeString(timeRemaining), flagFell);
\r
7473 oldFg = SetTextColor(hdc, RGB(255, 255, 255)); /* white */
\r
7474 oldBg = SetBkColor(hdc, RGB(0, 0, 0)); /* black */
\r
7476 oldFg = SetTextColor(hdc, RGB(0, 0, 0)); /* black */
\r
7477 oldBg = SetBkColor(hdc, RGB(255, 255, 255)); /* white */
\r
7479 oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);
\r
7483 ExtTextOut(hdc, rect->left + MESSAGE_LINE_LEFTMARGIN,
\r
7484 rect->top, ETO_CLIPPED|ETO_OPAQUE,
\r
7485 rect, str, strlen(str), NULL);
\r
7486 if(logoHeight > 0 && appData.clockMode) {
\r
7488 str += strlen(color)+2;
\r
7489 r.top = rect->top + logoHeight/2;
\r
7490 r.left = rect->left;
\r
7491 r.right = rect->right;
\r
7492 r.bottom = rect->bottom;
\r
7493 ExtTextOut(hdc, rect->left + MESSAGE_LINE_LEFTMARGIN,
\r
7494 r.top, ETO_CLIPPED|ETO_OPAQUE,
\r
7495 &r, str, strlen(str), NULL);
\r
7497 (void) SetTextColor(hdc, oldFg);
\r
7498 (void) SetBkColor(hdc, oldBg);
\r
7499 (void) SelectObject(hdc, oldFont);
\r
7504 DoReadFile(HANDLE hFile, char *buf, int count, DWORD *outCount,
\r
7510 if( count <= 0 ) {
\r
7511 if (appData.debugMode) {
\r
7512 fprintf( debugFP, "DoReadFile: trying to read past end of buffer, overflow = %d\n", count );
\r
7515 return ERROR_INVALID_USER_BUFFER;
\r
7518 ResetEvent(ovl->hEvent);
\r
7519 ovl->Offset = ovl->OffsetHigh = 0;
\r
7520 ok = ReadFile(hFile, buf, count, outCount, ovl);
\r
7524 err = GetLastError();
\r
7525 if (err == ERROR_IO_PENDING) {
\r
7526 ok = GetOverlappedResult(hFile, ovl, outCount, TRUE);
\r
7530 err = GetLastError();
\r
7537 DoWriteFile(HANDLE hFile, char *buf, int count, DWORD *outCount,
\r
7542 ResetEvent(ovl->hEvent);
\r
7543 ovl->Offset = ovl->OffsetHigh = 0;
\r
7544 ok = WriteFile(hFile, buf, count, outCount, ovl);
\r
7548 err = GetLastError();
\r
7549 if (err == ERROR_IO_PENDING) {
\r
7550 ok = GetOverlappedResult(hFile, ovl, outCount, TRUE);
\r
7554 err = GetLastError();
\r
7560 /* [AS] If input is line by line and a line exceed the buffer size, force an error */
\r
7561 void CheckForInputBufferFull( InputSource * is )
\r
7563 if( is->lineByLine && (is->next - is->buf) >= INPUT_SOURCE_BUF_SIZE ) {
\r
7564 /* Look for end of line */
\r
7565 char * p = is->buf;
\r
7567 while( p < is->next && *p != '\n' ) {
\r
7571 if( p >= is->next ) {
\r
7572 if (appData.debugMode) {
\r
7573 fprintf( debugFP, "Input line exceeded buffer size (source id=%lu)\n", is->id );
\r
7576 is->error = ERROR_BROKEN_PIPE; /* [AS] Just any non-successful code! */
\r
7577 is->count = (DWORD) -1;
\r
7578 is->next = is->buf;
\r
7584 InputThread(LPVOID arg)
\r
7589 is = (InputSource *) arg;
\r
7590 ovl.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
\r
7591 ovl.Internal = ovl.InternalHigh = ovl.Offset = ovl.OffsetHigh = 0;
\r
7592 while (is->hThread != NULL) {
\r
7593 is->error = DoReadFile(is->hFile, is->next,
\r
7594 INPUT_SOURCE_BUF_SIZE - (is->next - is->buf),
\r
7595 &is->count, &ovl);
\r
7596 if (is->error == NO_ERROR) {
\r
7597 is->next += is->count;
\r
7599 if (is->error == ERROR_BROKEN_PIPE) {
\r
7600 /* Correct for MS brain damage. EOF reading a pipe is not an error. */
\r
7603 is->count = (DWORD) -1;
\r
7604 /* [AS] The (is->count <= 0) check below is not useful for unsigned values! */
\r
7609 CheckForInputBufferFull( is );
\r
7611 SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);
\r
7613 if( is->count == ((DWORD) -1) ) break; /* [AS] */
\r
7615 if (is->count <= 0) break; /* Quit on EOF or error */
\r
7618 CloseHandle(ovl.hEvent);
\r
7619 CloseHandle(is->hFile);
\r
7621 if (appData.debugMode) {
\r
7622 fprintf( debugFP, "Input thread terminated (id=%lu, error=%d, count=%ld)\n", is->id, is->error, is->count );
\r
7629 /* Windows 95 beta 2 won't let you do overlapped i/o on a console or pipe */
\r
7631 NonOvlInputThread(LPVOID arg)
\r
7638 is = (InputSource *) arg;
\r
7639 while (is->hThread != NULL) {
\r
7640 is->error = ReadFile(is->hFile, is->next,
\r
7641 INPUT_SOURCE_BUF_SIZE - (is->next - is->buf),
\r
7642 &is->count, NULL) ? NO_ERROR : GetLastError();
\r
7643 if (is->error == NO_ERROR) {
\r
7644 /* Change CRLF to LF */
\r
7645 if (is->next > is->buf) {
\r
7647 i = is->count + 1;
\r
7655 if (prev == '\r' && *p == '\n') {
\r
7667 if (is->error == ERROR_BROKEN_PIPE) {
\r
7668 /* Correct for MS brain damage. EOF reading a pipe is not an error. */
\r
7671 is->count = (DWORD) -1;
\r
7675 CheckForInputBufferFull( is );
\r
7677 SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);
\r
7679 if( is->count == ((DWORD) -1) ) break; /* [AS] */
\r
7681 if (is->count < 0) break; /* Quit on error */
\r
7683 CloseHandle(is->hFile);
\r
7688 SocketInputThread(LPVOID arg)
\r
7692 is = (InputSource *) arg;
\r
7693 while (is->hThread != NULL) {
\r
7694 is->count = recv(is->sock, is->buf, INPUT_SOURCE_BUF_SIZE, 0);
\r
7695 if ((int)is->count == SOCKET_ERROR) {
\r
7696 is->count = (DWORD) -1;
\r
7697 is->error = WSAGetLastError();
\r
7699 is->error = NO_ERROR;
\r
7700 is->next += is->count;
\r
7701 if (is->count == 0 && is->second == is) {
\r
7702 /* End of file on stderr; quit with no message */
\r
7706 SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);
\r
7708 if( is->count == ((DWORD) -1) ) break; /* [AS] */
\r
7710 if (is->count <= 0) break; /* Quit on EOF or error */
\r
7716 InputEvent(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
\r
7720 is = (InputSource *) lParam;
\r
7721 if (is->lineByLine) {
\r
7722 /* Feed in lines one by one */
\r
7723 char *p = is->buf;
\r
7725 while (q < is->next) {
\r
7726 if (*q++ == '\n') {
\r
7727 (is->func)(is, is->closure, p, q - p, NO_ERROR);
\r
7732 /* Move any partial line to the start of the buffer */
\r
7734 while (p < is->next) {
\r
7739 if (is->error != NO_ERROR || is->count == 0) {
\r
7740 /* Notify backend of the error. Note: If there was a partial
\r
7741 line at the end, it is not flushed through. */
\r
7742 (is->func)(is, is->closure, is->buf, is->count, is->error);
\r
7745 /* Feed in the whole chunk of input at once */
\r
7746 (is->func)(is, is->closure, is->buf, is->count, is->error);
\r
7747 is->next = is->buf;
\r
7751 /*---------------------------------------------------------------------------*\
\r
7753 * Menu enables. Used when setting various modes.
\r
7755 \*---------------------------------------------------------------------------*/
\r
7763 GreyRevert(Boolean grey)
\r
7764 { // [HGM] vari: for retracting variations in local mode
\r
7765 HMENU hmenu = GetMenu(hwndMain);
\r
7766 EnableMenuItem(hmenu, IDM_Revert, MF_BYCOMMAND|(grey ? MF_GRAYED : MF_ENABLED));
\r
7767 EnableMenuItem(hmenu, IDM_Annotate, MF_BYCOMMAND|(grey ? MF_GRAYED : MF_ENABLED));
\r
7771 SetMenuEnables(HMENU hmenu, Enables *enab)
\r
7773 while (enab->item > 0) {
\r
7774 (void) EnableMenuItem(hmenu, enab->item, enab->flags);
\r
7779 Enables gnuEnables[] = {
\r
7780 { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },
\r
7781 { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },
\r
7782 { IDM_IcsClient, MF_BYCOMMAND|MF_GRAYED },
\r
7783 { IDM_Accept, MF_BYCOMMAND|MF_GRAYED },
\r
7784 { IDM_Decline, MF_BYCOMMAND|MF_GRAYED },
\r
7785 { IDM_Rematch, MF_BYCOMMAND|MF_GRAYED },
\r
7786 { IDM_Adjourn, MF_BYCOMMAND|MF_GRAYED },
\r
7787 { IDM_StopExamining, MF_BYCOMMAND|MF_GRAYED },
\r
7788 { IDM_StopObserving, MF_BYCOMMAND|MF_GRAYED },
\r
7789 { IDM_Upload, MF_BYCOMMAND|MF_GRAYED },
\r
7790 { IDM_Revert, MF_BYCOMMAND|MF_GRAYED },
\r
7791 { IDM_Annotate, MF_BYCOMMAND|MF_GRAYED },
\r
7792 { IDM_NewChat, MF_BYCOMMAND|MF_GRAYED },
\r
7796 Enables icsEnables[] = {
\r
7797 { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },
\r
7798 { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },
\r
7799 { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },
\r
7800 { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },
\r
7801 { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },
\r
7802 { IDM_Match, MF_BYCOMMAND|MF_GRAYED },
\r
7803 { IDM_MachineBoth, MF_BYCOMMAND|MF_GRAYED },
\r
7804 { IDM_AnalysisMode, MF_BYCOMMAND|MF_ENABLED },
\r
7805 { IDM_AnalyzeFile, MF_BYCOMMAND|MF_GRAYED },
\r
7806 { IDM_TimeControl, MF_BYCOMMAND|MF_GRAYED },
\r
7807 { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },
\r
7808 { IDM_Hint, MF_BYCOMMAND|MF_GRAYED },
\r
7809 { IDM_Book, MF_BYCOMMAND|MF_GRAYED },
\r
7810 { IDM_IcsOptions, MF_BYCOMMAND|MF_ENABLED },
\r
7811 { IDM_Engine1Options, MF_BYCOMMAND|MF_GRAYED },
\r
7812 { IDM_Engine2Options, MF_BYCOMMAND|MF_GRAYED },
\r
7813 { IDM_Annotate, MF_BYCOMMAND|MF_GRAYED },
\r
7818 Enables zippyEnables[] = {
\r
7819 { IDM_MoveNow, MF_BYCOMMAND|MF_ENABLED },
\r
7820 { IDM_Hint, MF_BYCOMMAND|MF_ENABLED },
\r
7821 { IDM_Book, MF_BYCOMMAND|MF_ENABLED },
\r
7822 { IDM_Engine1Options, MF_BYCOMMAND|MF_ENABLED },
\r
7827 Enables ncpEnables[] = {
\r
7828 { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },
\r
7829 { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },
\r
7830 { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },
\r
7831 { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },
\r
7832 { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },
\r
7833 { IDM_Match, MF_BYCOMMAND|MF_GRAYED },
\r
7834 { IDM_AnalysisMode, MF_BYCOMMAND|MF_GRAYED },
\r
7835 { IDM_AnalyzeFile, MF_BYCOMMAND|MF_GRAYED },
\r
7836 { IDM_IcsClient, MF_BYCOMMAND|MF_GRAYED },
\r
7837 { ACTION_POS, MF_BYPOSITION|MF_GRAYED },
\r
7838 { IDM_Revert, MF_BYCOMMAND|MF_GRAYED },
\r
7839 { IDM_Annotate, MF_BYCOMMAND|MF_GRAYED },
\r
7840 { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },
\r
7841 { IDM_RetractMove, MF_BYCOMMAND|MF_GRAYED },
\r
7842 { IDM_TimeControl, MF_BYCOMMAND|MF_GRAYED },
\r
7843 { IDM_Hint, MF_BYCOMMAND|MF_GRAYED },
\r
7844 { IDM_Book, MF_BYCOMMAND|MF_GRAYED },
\r
7845 { IDM_MachineBoth, MF_BYCOMMAND|MF_GRAYED },
\r
7846 { IDM_NewChat, MF_BYCOMMAND|MF_GRAYED },
\r
7847 { IDM_Engine1Options, MF_BYCOMMAND|MF_GRAYED },
\r
7848 { IDM_Engine2Options, MF_BYCOMMAND|MF_GRAYED },
\r
7849 { IDM_Sounds, MF_BYCOMMAND|MF_GRAYED },
\r
7853 Enables trainingOnEnables[] = {
\r
7854 { IDM_EditComment, MF_BYCOMMAND|MF_GRAYED },
\r
7855 { IDM_Comment, MF_BYCOMMAND|MF_GRAYED },
\r
7856 { IDM_Pause, MF_BYCOMMAND|MF_GRAYED },
\r
7857 { IDM_Forward, MF_BYCOMMAND|MF_GRAYED },
\r
7858 { IDM_Backward, MF_BYCOMMAND|MF_GRAYED },
\r
7859 { IDM_ToEnd, MF_BYCOMMAND|MF_GRAYED },
\r
7860 { IDM_ToStart, MF_BYCOMMAND|MF_GRAYED },
\r
7861 { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },
\r
7862 { IDM_TruncateGame, MF_BYCOMMAND|MF_GRAYED },
\r
7866 Enables trainingOffEnables[] = {
\r
7867 { IDM_EditComment, MF_BYCOMMAND|MF_ENABLED },
\r
7868 { IDM_Comment, MF_BYCOMMAND|MF_ENABLED },
\r
7869 { IDM_Pause, MF_BYCOMMAND|MF_ENABLED },
\r
7870 { IDM_Forward, MF_BYCOMMAND|MF_ENABLED },
\r
7871 { IDM_Backward, MF_BYCOMMAND|MF_ENABLED },
\r
7872 { IDM_ToEnd, MF_BYCOMMAND|MF_ENABLED },
\r
7873 { IDM_ToStart, MF_BYCOMMAND|MF_ENABLED },
\r
7874 { IDM_MoveNow, MF_BYCOMMAND|MF_ENABLED },
\r
7875 { IDM_TruncateGame, MF_BYCOMMAND|MF_ENABLED },
\r
7879 /* These modify either ncpEnables or gnuEnables */
\r
7880 Enables cmailEnables[] = {
\r
7881 { IDM_MailMove, MF_BYCOMMAND|MF_ENABLED },
\r
7882 { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_ENABLED },
\r
7883 { ACTION_POS, MF_BYPOSITION|MF_ENABLED },
\r
7884 { IDM_CallFlag, MF_BYCOMMAND|MF_GRAYED },
\r
7885 { IDM_Draw, MF_BYCOMMAND|MF_ENABLED },
\r
7886 { IDM_Adjourn, MF_BYCOMMAND|MF_GRAYED },
\r
7887 { IDM_Abort, MF_BYCOMMAND|MF_GRAYED },
\r
7891 Enables machineThinkingEnables[] = {
\r
7892 { IDM_LoadGame, MF_BYCOMMAND|MF_GRAYED },
\r
7893 { IDM_LoadNextGame, MF_BYCOMMAND|MF_GRAYED },
\r
7894 { IDM_LoadPrevGame, MF_BYCOMMAND|MF_GRAYED },
\r
7895 { IDM_ReloadGame, MF_BYCOMMAND|MF_GRAYED },
\r
7896 { IDM_PasteGame, MF_BYCOMMAND|MF_GRAYED },
\r
7897 { IDM_LoadPosition, MF_BYCOMMAND|MF_GRAYED },
\r
7898 { IDM_LoadNextPosition, MF_BYCOMMAND|MF_GRAYED },
\r
7899 { IDM_LoadPrevPosition, MF_BYCOMMAND|MF_GRAYED },
\r
7900 { IDM_ReloadPosition, MF_BYCOMMAND|MF_GRAYED },
\r
7901 { IDM_PastePosition, MF_BYCOMMAND|MF_GRAYED },
\r
7902 { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },
\r
7903 { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },
\r
7904 { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },
\r
7905 { IDM_Match, MF_BYCOMMAND|MF_GRAYED },
\r
7906 { IDM_TypeInMove, MF_BYCOMMAND|MF_GRAYED },
\r
7907 { IDM_RetractMove, MF_BYCOMMAND|MF_GRAYED },
\r
7911 Enables userThinkingEnables[] = {
\r
7912 { IDM_LoadGame, MF_BYCOMMAND|MF_ENABLED },
\r
7913 { IDM_LoadNextGame, MF_BYCOMMAND|MF_ENABLED },
\r
7914 { IDM_LoadPrevGame, MF_BYCOMMAND|MF_ENABLED },
\r
7915 { IDM_ReloadGame, MF_BYCOMMAND|MF_ENABLED },
\r
7916 { IDM_PasteGame, MF_BYCOMMAND|MF_ENABLED },
\r
7917 { IDM_LoadPosition, MF_BYCOMMAND|MF_ENABLED },
\r
7918 { IDM_LoadNextPosition, MF_BYCOMMAND|MF_ENABLED },
\r
7919 { IDM_LoadPrevPosition, MF_BYCOMMAND|MF_ENABLED },
\r
7920 { IDM_ReloadPosition, MF_BYCOMMAND|MF_ENABLED },
\r
7921 { IDM_PastePosition, MF_BYCOMMAND|MF_ENABLED },
\r
7922 { IDM_MachineWhite, MF_BYCOMMAND|MF_ENABLED },
\r
7923 { IDM_MachineBlack, MF_BYCOMMAND|MF_ENABLED },
\r
7924 { IDM_TwoMachines, MF_BYCOMMAND|MF_ENABLED },
\r
7925 { IDM_Match, MF_BYCOMMAND|MF_ENABLED },
\r
7926 { IDM_TypeInMove, MF_BYCOMMAND|MF_ENABLED },
\r
7927 { IDM_RetractMove, MF_BYCOMMAND|MF_ENABLED },
\r
7931 /*---------------------------------------------------------------------------*\
\r
7933 * Front-end interface functions exported by XBoard.
\r
7934 * Functions appear in same order as prototypes in frontend.h.
\r
7936 \*---------------------------------------------------------------------------*/
\r
7940 static UINT prevChecked = 0;
\r
7941 static int prevPausing = 0;
\r
7944 if (pausing != prevPausing) {
\r
7945 prevPausing = pausing;
\r
7946 (void) CheckMenuItem(GetMenu(hwndMain), IDM_Pause,
\r
7947 MF_BYCOMMAND|(pausing ? MF_CHECKED : MF_UNCHECKED));
\r
7948 if (hwndPause) SetWindowText(hwndPause, pausing ? "C" : "P");
\r
7951 switch (gameMode) {
\r
7952 case BeginningOfGame:
\r
7953 if (appData.icsActive)
\r
7954 nowChecked = IDM_IcsClient;
\r
7955 else if (appData.noChessProgram)
\r
7956 nowChecked = IDM_EditGame;
\r
7958 nowChecked = IDM_MachineBlack;
\r
7960 case MachinePlaysBlack:
\r
7961 nowChecked = IDM_MachineBlack;
\r
7963 case MachinePlaysWhite:
\r
7964 nowChecked = IDM_MachineWhite;
\r
7966 case TwoMachinesPlay:
\r
7967 nowChecked = matchMode ? IDM_Match : IDM_TwoMachines; // [HGM] match
\r
7970 nowChecked = IDM_AnalysisMode;
\r
7973 nowChecked = IDM_AnalyzeFile;
\r
7976 nowChecked = IDM_EditGame;
\r
7978 case PlayFromGameFile:
\r
7979 nowChecked = IDM_LoadGame;
\r
7981 case EditPosition:
\r
7982 nowChecked = IDM_EditPosition;
\r
7985 nowChecked = IDM_Training;
\r
7987 case IcsPlayingWhite:
\r
7988 case IcsPlayingBlack:
\r
7989 case IcsObserving:
\r
7991 nowChecked = IDM_IcsClient;
\r
7998 if (prevChecked != 0)
\r
7999 (void) CheckMenuItem(GetMenu(hwndMain),
\r
8000 prevChecked, MF_BYCOMMAND|MF_UNCHECKED);
\r
8001 if (nowChecked != 0)
\r
8002 (void) CheckMenuItem(GetMenu(hwndMain),
\r
8003 nowChecked, MF_BYCOMMAND|MF_CHECKED);
\r
8005 if (nowChecked == IDM_LoadGame || nowChecked == IDM_Training) {
\r
8006 (void) EnableMenuItem(GetMenu(hwndMain), IDM_Training,
\r
8007 MF_BYCOMMAND|MF_ENABLED);
\r
8009 (void) EnableMenuItem(GetMenu(hwndMain),
\r
8010 IDM_Training, MF_BYCOMMAND|MF_GRAYED);
\r
8013 prevChecked = nowChecked;
\r
8015 /* [DM] icsEngineAnalyze - Do a sceure check too */
\r
8016 if (appData.icsActive) {
\r
8017 if (appData.icsEngineAnalyze) {
\r
8018 (void) CheckMenuItem(GetMenu(hwndMain), IDM_AnalysisMode,
\r
8019 MF_BYCOMMAND|MF_CHECKED);
\r
8021 (void) CheckMenuItem(GetMenu(hwndMain), IDM_AnalysisMode,
\r
8022 MF_BYCOMMAND|MF_UNCHECKED);
\r
8025 DisplayLogos(); // [HGM] logos: mode change could have altered logos
\r
8031 HMENU hmenu = GetMenu(hwndMain);
\r
8032 SetMenuEnables(hmenu, icsEnables);
\r
8033 EnableMenuItem(GetSubMenu(hmenu, OPTIONS_POS), IDM_IcsOptions,
\r
8034 MF_BYCOMMAND|MF_ENABLED);
\r
8036 if (appData.zippyPlay) {
\r
8037 SetMenuEnables(hmenu, zippyEnables);
\r
8038 if (!appData.noChessProgram) /* [DM] icsEngineAnalyze */
\r
8039 (void) EnableMenuItem(GetMenu(hwndMain), IDM_AnalysisMode,
\r
8040 MF_BYCOMMAND|MF_ENABLED);
\r
8048 SetMenuEnables(GetMenu(hwndMain), gnuEnables);
\r
8054 HMENU hmenu = GetMenu(hwndMain);
\r
8055 SetMenuEnables(hmenu, ncpEnables);
\r
8056 DrawMenuBar(hwndMain);
\r
8062 SetMenuEnables(GetMenu(hwndMain), cmailEnables);
\r
8066 SetTrainingModeOn()
\r
8069 SetMenuEnables(GetMenu(hwndMain), trainingOnEnables);
\r
8070 for (i = 0; i < N_BUTTONS; i++) {
\r
8071 if (buttonDesc[i].hwnd != NULL)
\r
8072 EnableWindow(buttonDesc[i].hwnd, FALSE);
\r
8077 VOID SetTrainingModeOff()
\r
8080 SetMenuEnables(GetMenu(hwndMain), trainingOffEnables);
\r
8081 for (i = 0; i < N_BUTTONS; i++) {
\r
8082 if (buttonDesc[i].hwnd != NULL)
\r
8083 EnableWindow(buttonDesc[i].hwnd, TRUE);
\r
8089 SetUserThinkingEnables()
\r
8091 SetMenuEnables(GetMenu(hwndMain), userThinkingEnables);
\r
8095 SetMachineThinkingEnables()
\r
8097 HMENU hMenu = GetMenu(hwndMain);
\r
8098 int flags = MF_BYCOMMAND|MF_ENABLED;
\r
8100 SetMenuEnables(hMenu, machineThinkingEnables);
\r
8102 if (gameMode == MachinePlaysBlack) {
\r
8103 (void)EnableMenuItem(hMenu, IDM_MachineBlack, flags);
\r
8104 } else if (gameMode == MachinePlaysWhite) {
\r
8105 (void)EnableMenuItem(hMenu, IDM_MachineWhite, flags);
\r
8106 } else if (gameMode == TwoMachinesPlay) {
\r
8107 (void)EnableMenuItem(hMenu, matchMode ? IDM_Match : IDM_TwoMachines, flags); // [HGM] match
\r
8113 DisplayTitle(char *str)
\r
8115 char title[MSG_SIZ], *host;
\r
8116 if (str[0] != NULLCHAR) {
\r
8117 safeStrCpy(title, str, sizeof(title)/sizeof(title[0]) );
\r
8118 } else if (appData.icsActive) {
\r
8119 if (appData.icsCommPort[0] != NULLCHAR)
\r
8122 host = appData.icsHost;
\r
8123 snprintf(title, MSG_SIZ, "%s: %s", szTitle, host);
\r
8124 } else if (appData.noChessProgram) {
\r
8125 safeStrCpy(title, szTitle, sizeof(title)/sizeof(title[0]) );
\r
8127 safeStrCpy(title, szTitle, sizeof(title)/sizeof(title[0]) );
\r
8128 strcat(title, ": ");
\r
8129 strcat(title, first.tidy);
\r
8131 SetWindowText(hwndMain, title);
\r
8136 DisplayMessage(char *str1, char *str2)
\r
8140 int remain = MESSAGE_TEXT_MAX - 1;
\r
8143 moveErrorMessageUp = FALSE; /* turned on later by caller if needed */
\r
8144 messageText[0] = NULLCHAR;
\r
8146 len = strlen(str1);
\r
8147 if (len > remain) len = remain;
\r
8148 strncpy(messageText, str1, len);
\r
8149 messageText[len] = NULLCHAR;
\r
8152 if (*str2 && remain >= 2) {
\r
8154 strcat(messageText, " ");
\r
8157 len = strlen(str2);
\r
8158 if (len > remain) len = remain;
\r
8159 strncat(messageText, str2, len);
\r
8161 messageText[MESSAGE_TEXT_MAX - 1] = NULLCHAR;
\r
8163 if (hwndMain == NULL || IsIconic(hwndMain)) return;
\r
8167 hdc = GetDC(hwndMain);
\r
8168 oldFont = SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);
\r
8169 ExtTextOut(hdc, messageRect.left, messageRect.top, ETO_CLIPPED|ETO_OPAQUE,
\r
8170 &messageRect, messageText, strlen(messageText), NULL);
\r
8171 (void) SelectObject(hdc, oldFont);
\r
8172 (void) ReleaseDC(hwndMain, hdc);
\r
8176 DisplayError(char *str, int error)
\r
8178 char buf[MSG_SIZ*2], buf2[MSG_SIZ];
\r
8182 safeStrCpy(buf, str, sizeof(buf)/sizeof(buf[0]) );
\r
8184 len = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,
\r
8185 NULL, error, LANG_NEUTRAL,
\r
8186 (LPSTR) buf2, MSG_SIZ, NULL);
\r
8188 snprintf(buf, 2*MSG_SIZ, "%s:\n%s", str, buf2);
\r
8190 ErrorMap *em = errmap;
\r
8191 while (em->err != 0 && em->err != error) em++;
\r
8192 if (em->err != 0) {
\r
8193 snprintf(buf, 2*MSG_SIZ, "%s:\n%s", str, em->msg);
\r
8195 snprintf(buf, 2*MSG_SIZ, "%s:\nError code %d", str, error);
\r
8200 ErrorPopUp(_("Error"), buf);
\r
8205 DisplayMoveError(char *str)
\r
8207 fromX = fromY = -1;
\r
8208 ClearHighlights();
\r
8209 DrawPosition(FALSE, NULL);
\r
8210 if (appData.popupMoveErrors) {
\r
8211 ErrorPopUp(_("Error"), str);
\r
8213 DisplayMessage(str, "");
\r
8214 moveErrorMessageUp = TRUE;
\r
8219 DisplayFatalError(char *str, int error, int exitStatus)
\r
8221 char buf[2*MSG_SIZ], buf2[MSG_SIZ];
\r
8223 char *label = exitStatus ? _("Fatal Error") : _("Exiting");
\r
8226 len = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,
\r
8227 NULL, error, LANG_NEUTRAL,
\r
8228 (LPSTR) buf2, MSG_SIZ, NULL);
\r
8230 snprintf(buf, 2*MSG_SIZ, "%s:\n%s", str, buf2);
\r
8232 ErrorMap *em = errmap;
\r
8233 while (em->err != 0 && em->err != error) em++;
\r
8234 if (em->err != 0) {
\r
8235 snprintf(buf, 2*MSG_SIZ, "%s:\n%s", str, em->msg);
\r
8237 snprintf(buf, 2*MSG_SIZ, "%s:\nError code %d", str, error);
\r
8242 if (appData.debugMode) {
\r
8243 fprintf(debugFP, "%s: %s\n", label, str);
\r
8245 if (appData.popupExitMessage) {
\r
8246 (void) MessageBox(hwndMain, str, label, MB_OK|
\r
8247 (exitStatus ? MB_ICONSTOP : MB_ICONINFORMATION));
\r
8249 ExitEvent(exitStatus);
\r
8254 DisplayInformation(char *str)
\r
8256 (void) MessageBox(hwndMain, str, _("Information"), MB_OK|MB_ICONINFORMATION);
\r
8261 DisplayNote(char *str)
\r
8263 ErrorPopUp(_("Note"), str);
\r
8268 char *title, *question, *replyPrefix;
\r
8273 QuestionDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
8275 static QuestionParams *qp;
\r
8276 char reply[MSG_SIZ];
\r
8279 switch (message) {
\r
8280 case WM_INITDIALOG:
\r
8281 qp = (QuestionParams *) lParam;
\r
8282 CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));
\r
8283 Translate(hDlg, DLG_Question);
\r
8284 SetWindowText(hDlg, qp->title);
\r
8285 SetDlgItemText(hDlg, OPT_QuestionText, qp->question);
\r
8286 SetFocus(GetDlgItem(hDlg, OPT_QuestionInput));
\r
8290 switch (LOWORD(wParam)) {
\r
8292 safeStrCpy(reply, qp->replyPrefix, sizeof(reply)/sizeof(reply[0]) );
\r
8293 if (*reply) strcat(reply, " ");
\r
8294 len = strlen(reply);
\r
8295 GetDlgItemText(hDlg, OPT_QuestionInput, reply + len, sizeof(reply) - len);
\r
8296 strcat(reply, "\n");
\r
8297 OutputToProcess(qp->pr, reply, strlen(reply), &err);
\r
8298 EndDialog(hDlg, TRUE);
\r
8299 if (err) DisplayFatalError(_("Error writing to chess program"), err, 1);
\r
8302 EndDialog(hDlg, FALSE);
\r
8313 AskQuestion(char* title, char *question, char *replyPrefix, ProcRef pr)
\r
8315 QuestionParams qp;
\r
8319 qp.question = question;
\r
8320 qp.replyPrefix = replyPrefix;
\r
8322 lpProc = MakeProcInstance((FARPROC)QuestionDialog, hInst);
\r
8323 DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_Question),
\r
8324 hwndMain, (DLGPROC)lpProc, (LPARAM)&qp);
\r
8325 FreeProcInstance(lpProc);
\r
8328 /* [AS] Pick FRC position */
\r
8329 LRESULT CALLBACK NewGameFRC_Proc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
8331 static int * lpIndexFRC;
\r
8337 case WM_INITDIALOG:
\r
8338 lpIndexFRC = (int *) lParam;
\r
8340 CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));
\r
8341 Translate(hDlg, DLG_NewGameFRC);
\r
8343 SendDlgItemMessage( hDlg, IDC_NFG_Edit, EM_SETLIMITTEXT, sizeof(buf)-1, 0 );
\r
8344 SetDlgItemInt( hDlg, IDC_NFG_Edit, *lpIndexFRC, TRUE );
\r
8345 SendDlgItemMessage( hDlg, IDC_NFG_Edit, EM_SETSEL, 0, -1 );
\r
8346 SetFocus(GetDlgItem(hDlg, IDC_NFG_Edit));
\r
8351 switch( LOWORD(wParam) ) {
\r
8353 *lpIndexFRC = GetDlgItemInt(hDlg, IDC_NFG_Edit, &index_is_ok, TRUE );
\r
8354 EndDialog( hDlg, 0 );
\r
8355 shuffleOpenings = TRUE; /* [HGM] shuffle: switch shuffling on for as long as we stay in current variant */
\r
8358 EndDialog( hDlg, 1 );
\r
8360 case IDC_NFG_Edit:
\r
8361 if( HIWORD(wParam) == EN_CHANGE ) {
\r
8362 GetDlgItemInt(hDlg, IDC_NFG_Edit, &index_is_ok, TRUE );
\r
8364 EnableWindow( GetDlgItem(hDlg, IDOK), index_is_ok );
\r
8367 case IDC_NFG_Random:
\r
8368 snprintf( buf, sizeof(buf)/sizeof(buf[0]), "%d", myrandom() ); /* [HGM] shuffle: no longer limit to 960 */
\r
8369 SetDlgItemText(hDlg, IDC_NFG_Edit, buf );
\r
8382 int index = appData.defaultFrcPosition;
\r
8383 FARPROC lpProc = MakeProcInstance( (FARPROC) NewGameFRC_Proc, hInst );
\r
8385 result = DialogBoxParam( hInst, MAKEINTRESOURCE(DLG_NewGameFRC), hwndMain, (DLGPROC)lpProc, (LPARAM)&index );
\r
8387 if( result == 0 ) {
\r
8388 appData.defaultFrcPosition = index;
\r
8394 /* [AS] Game list options. Refactored by HGM */
\r
8396 HWND gameListOptionsDialog;
\r
8398 // low-level front-end: clear text edit / list widget
\r
8402 SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_RESETCONTENT, 0, 0 );
\r
8405 // low-level front-end: clear text edit / list widget
\r
8407 GLT_DeSelectList()
\r
8409 SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_SETCURSEL, 0, 0 );
\r
8412 // low-level front-end: append line to text edit / list widget
\r
8414 GLT_AddToList( char *name )
\r
8417 SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_ADDSTRING, 0, (LPARAM) name );
\r
8421 // low-level front-end: get line from text edit / list widget
\r
8423 GLT_GetFromList( int index, char *name )
\r
8426 if( SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_GETTEXT, index, (LPARAM) name ) != LB_ERR )
\r
8432 void GLT_MoveSelection( HWND hDlg, int delta )
\r
8434 int idx1 = (int) SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETCURSEL, 0, 0 );
\r
8435 int idx2 = idx1 + delta;
\r
8436 int count = (int) SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETCOUNT, 0, 0 );
\r
8438 if( idx1 >=0 && idx1 < count && idx2 >= 0 && idx2 < count ) {
\r
8441 SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETTEXT, idx1, (LPARAM) buf );
\r
8442 SendDlgItemMessage( hDlg, IDC_GameListTags, LB_DELETESTRING, idx1, 0 );
\r
8443 SendDlgItemMessage( hDlg, IDC_GameListTags, LB_INSERTSTRING, idx2, (LPARAM) buf );
\r
8444 SendDlgItemMessage( hDlg, IDC_GameListTags, LB_SETCURSEL, idx2, 0 );
\r
8448 LRESULT CALLBACK GameListOptions_Proc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
8452 case WM_INITDIALOG:
\r
8453 gameListOptionsDialog = hDlg; // [HGM] pass through global to keep out off back-end
\r
8455 CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));
\r
8456 Translate(hDlg, DLG_GameListOptions);
\r
8458 /* Initialize list */
\r
8459 GLT_TagsToList( lpUserGLT );
\r
8461 SetFocus( GetDlgItem(hDlg, IDC_GameListTags) );
\r
8466 switch( LOWORD(wParam) ) {
\r
8469 EndDialog( hDlg, 0 );
\r
8472 EndDialog( hDlg, 1 );
\r
8475 case IDC_GLT_Default:
\r
8476 GLT_TagsToList( GLT_DEFAULT_TAGS );
\r
8479 case IDC_GLT_Restore:
\r
8480 GLT_TagsToList( appData.gameListTags );
\r
8484 GLT_MoveSelection( hDlg, -1 );
\r
8487 case IDC_GLT_Down:
\r
8488 GLT_MoveSelection( hDlg, +1 );
\r
8498 int GameListOptions()
\r
8501 FARPROC lpProc = MakeProcInstance( (FARPROC) GameListOptions_Proc, hInst );
\r
8503 safeStrCpy( lpUserGLT, appData.gameListTags ,LPUSERGLT_SIZE );
\r
8505 result = DialogBoxParam( hInst, MAKEINTRESOURCE(DLG_GameListOptions), hwndMain, (DLGPROC)lpProc, (LPARAM)lpUserGLT );
\r
8507 if( result == 0 ) {
\r
8508 /* [AS] Memory leak here! */
\r
8509 appData.gameListTags = strdup( lpUserGLT );
\r
8516 DisplayIcsInteractionTitle(char *str)
\r
8518 char consoleTitle[MSG_SIZ];
\r
8520 snprintf(consoleTitle, MSG_SIZ, "%s: %s", szConsoleTitle, str);
\r
8521 SetWindowText(hwndConsole, consoleTitle);
\r
8525 DrawPosition(int fullRedraw, Board board)
\r
8527 HDCDrawPosition(NULL, (BOOLEAN) fullRedraw, board);
\r
8530 void NotifyFrontendLogin()
\r
8533 UpdateICSWidth(GetDlgItem(hwndConsole, OPT_ConsoleText));
\r
8539 fromX = fromY = -1;
\r
8540 if (dragInfo.pos.x != -1 || dragInfo.pos.y != -1) {
\r
8541 dragInfo.pos.x = dragInfo.pos.y = -1;
\r
8542 dragInfo.pos.x = dragInfo.pos.y = -1;
\r
8543 dragInfo.lastpos = dragInfo.pos;
\r
8544 dragInfo.start.x = dragInfo.start.y = -1;
\r
8545 dragInfo.from = dragInfo.start;
\r
8547 DrawPosition(TRUE, NULL);
\r
8554 CommentPopUp(char *title, char *str)
\r
8556 HWND hwnd = GetActiveWindow();
\r
8557 EitherCommentPopUp(currentMove, title, str, FALSE); // [HGM] vari: fake move index, rather than 0
\r
8559 SetActiveWindow(hwnd);
\r
8563 CommentPopDown(void)
\r
8565 CheckMenuItem(GetMenu(hwndMain), IDM_Comment, MF_UNCHECKED);
\r
8566 if (commentDialog) {
\r
8567 ShowWindow(commentDialog, SW_HIDE);
\r
8569 commentUp = FALSE;
\r
8573 EditCommentPopUp(int index, char *title, char *str)
\r
8575 EitherCommentPopUp(index, title, str, TRUE);
\r
8582 MyPlaySound(&sounds[(int)SoundMove]);
\r
8585 VOID PlayIcsWinSound()
\r
8587 MyPlaySound(&sounds[(int)SoundIcsWin]);
\r
8590 VOID PlayIcsLossSound()
\r
8592 MyPlaySound(&sounds[(int)SoundIcsLoss]);
\r
8595 VOID PlayIcsDrawSound()
\r
8597 MyPlaySound(&sounds[(int)SoundIcsDraw]);
\r
8600 VOID PlayIcsUnfinishedSound()
\r
8602 MyPlaySound(&sounds[(int)SoundIcsUnfinished]);
\r
8608 MyPlaySound(&sounds[(int)SoundAlarm]);
\r
8616 consoleEcho = TRUE;
\r
8617 hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
8618 SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&consoleCF);
\r
8619 SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);
\r
8628 consoleEcho = FALSE;
\r
8629 hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
8630 /* This works OK: set text and background both to the same color */
\r
8632 cf.crTextColor = COLOR_ECHOOFF;
\r
8633 SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf);
\r
8634 SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, cf.crTextColor);
\r
8637 /* No Raw()...? */
\r
8639 void Colorize(ColorClass cc, int continuation)
\r
8641 currentColorClass = cc;
\r
8642 consoleCF.dwMask = CFM_COLOR|CFM_BOLD|CFM_ITALIC|CFM_UNDERLINE|CFM_STRIKEOUT;
\r
8643 consoleCF.crTextColor = textAttribs[cc].color;
\r
8644 consoleCF.dwEffects = textAttribs[cc].effects;
\r
8645 if (!continuation) MyPlaySound(&textAttribs[cc].sound);
\r
8651 static char buf[MSG_SIZ];
\r
8652 DWORD bufsiz = MSG_SIZ;
\r
8654 if(appData.userName != NULL && appData.userName[0] != 0) {
\r
8655 return appData.userName; /* [HGM] username: prefer name selected by user over his system login */
\r
8657 if (!GetUserName(buf, &bufsiz)) {
\r
8658 /*DisplayError("Error getting user name", GetLastError());*/
\r
8659 safeStrCpy(buf, _("User"), sizeof(buf)/sizeof(buf[0]) );
\r
8667 static char buf[MSG_SIZ];
\r
8668 DWORD bufsiz = MSG_SIZ;
\r
8670 if (!GetComputerName(buf, &bufsiz)) {
\r
8671 /*DisplayError("Error getting host name", GetLastError());*/
\r
8672 safeStrCpy(buf, _("Unknown"), sizeof(buf)/sizeof(buf[0]) );
\r
8679 ClockTimerRunning()
\r
8681 return clockTimerEvent != 0;
\r
8687 if (clockTimerEvent == 0) return FALSE;
\r
8688 KillTimer(hwndMain, clockTimerEvent);
\r
8689 clockTimerEvent = 0;
\r
8694 StartClockTimer(long millisec)
\r
8696 clockTimerEvent = SetTimer(hwndMain, (UINT) CLOCK_TIMER_ID,
\r
8697 (UINT) millisec, NULL);
\r
8701 DisplayWhiteClock(long timeRemaining, int highlight)
\r
8704 char *flag = whiteFlag && gameMode == TwoMachinesPlay ? "(!)" : "";
\r
8706 if(appData.noGUI) return;
\r
8707 hdc = GetDC(hwndMain);
\r
8708 if (!IsIconic(hwndMain)) {
\r
8709 DisplayAClock(hdc, timeRemaining, highlight,
\r
8710 flipClock ? &blackRect : &whiteRect, _("White"), flag);
\r
8712 if (highlight && iconCurrent == iconBlack) {
\r
8713 iconCurrent = iconWhite;
\r
8714 PostMessage(hwndMain, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);
\r
8715 if (IsIconic(hwndMain)) {
\r
8716 DrawIcon(hdc, 2, 2, iconCurrent);
\r
8719 (void) ReleaseDC(hwndMain, hdc);
\r
8721 PostMessage(hwndConsole, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);
\r
8725 DisplayBlackClock(long timeRemaining, int highlight)
\r
8728 char *flag = blackFlag && gameMode == TwoMachinesPlay ? "(!)" : "";
\r
8730 if(appData.noGUI) return;
\r
8731 hdc = GetDC(hwndMain);
\r
8732 if (!IsIconic(hwndMain)) {
\r
8733 DisplayAClock(hdc, timeRemaining, highlight,
\r
8734 flipClock ? &whiteRect : &blackRect, _("Black"), flag);
\r
8736 if (highlight && iconCurrent == iconWhite) {
\r
8737 iconCurrent = iconBlack;
\r
8738 PostMessage(hwndMain, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);
\r
8739 if (IsIconic(hwndMain)) {
\r
8740 DrawIcon(hdc, 2, 2, iconCurrent);
\r
8743 (void) ReleaseDC(hwndMain, hdc);
\r
8745 PostMessage(hwndConsole, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);
\r
8750 LoadGameTimerRunning()
\r
8752 return loadGameTimerEvent != 0;
\r
8756 StopLoadGameTimer()
\r
8758 if (loadGameTimerEvent == 0) return FALSE;
\r
8759 KillTimer(hwndMain, loadGameTimerEvent);
\r
8760 loadGameTimerEvent = 0;
\r
8765 StartLoadGameTimer(long millisec)
\r
8767 loadGameTimerEvent = SetTimer(hwndMain, (UINT) LOAD_GAME_TIMER_ID,
\r
8768 (UINT) millisec, NULL);
\r
8776 char fileTitle[MSG_SIZ];
\r
8778 defName = DefaultFileName(appData.oldSaveStyle ? "gam" : "pgn");
\r
8779 f = OpenFileDialog(hwndMain, "a", defName,
\r
8780 appData.oldSaveStyle ? "gam" : "pgn",
\r
8782 _("Save Game to File"), NULL, fileTitle, NULL);
\r
8784 SaveGame(f, 0, "");
\r
8791 ScheduleDelayedEvent(DelayedEventCallback cb, long millisec)
\r
8793 if (delayedTimerEvent != 0) {
\r
8794 if (appData.debugMode && cb != delayedTimerCallback) { // [HGM] alive: not too much debug
\r
8795 fprintf(debugFP, "ScheduleDelayedEvent: event already scheduled\n");
\r
8797 KillTimer(hwndMain, delayedTimerEvent);
\r
8798 delayedTimerEvent = 0;
\r
8799 if(delayedTimerCallback != cb) // [HGM] alive: do not "flush" same event, just postpone it
\r
8800 delayedTimerCallback();
\r
8802 delayedTimerCallback = cb;
\r
8803 delayedTimerEvent = SetTimer(hwndMain, (UINT) DELAYED_TIMER_ID,
\r
8804 (UINT) millisec, NULL);
\r
8807 DelayedEventCallback
\r
8810 if (delayedTimerEvent) {
\r
8811 return delayedTimerCallback;
\r
8818 CancelDelayedEvent()
\r
8820 if (delayedTimerEvent) {
\r
8821 KillTimer(hwndMain, delayedTimerEvent);
\r
8822 delayedTimerEvent = 0;
\r
8826 DWORD GetWin32Priority(int nice)
\r
8827 { // [HGM] nice: translate Unix nice() value to indows priority class. (Code stolen from Polyglot 1.4w11)
\r
8829 REALTIME_PRIORITY_CLASS 0x00000100
\r
8830 HIGH_PRIORITY_CLASS 0x00000080
\r
8831 ABOVE_NORMAL_PRIORITY_CLASS 0x00008000
\r
8832 NORMAL_PRIORITY_CLASS 0x00000020
\r
8833 BELOW_NORMAL_PRIORITY_CLASS 0x00004000
\r
8834 IDLE_PRIORITY_CLASS 0x00000040
\r
8836 if (nice < -15) return 0x00000080;
\r
8837 if (nice < 0) return 0x00008000;
\r
8838 if (nice == 0) return 0x00000020;
\r
8839 if (nice < 15) return 0x00004000;
\r
8840 return 0x00000040;
\r
8843 /* Start a child process running the given program.
\r
8844 The process's standard output can be read from "from", and its
\r
8845 standard input can be written to "to".
\r
8846 Exit with fatal error if anything goes wrong.
\r
8847 Returns an opaque pointer that can be used to destroy the process
\r
8851 StartChildProcess(char *cmdLine, char *dir, ProcRef *pr)
\r
8853 #define BUFSIZE 4096
\r
8855 HANDLE hChildStdinRd, hChildStdinWr,
\r
8856 hChildStdoutRd, hChildStdoutWr;
\r
8857 HANDLE hChildStdinWrDup, hChildStdoutRdDup;
\r
8858 SECURITY_ATTRIBUTES saAttr;
\r
8860 PROCESS_INFORMATION piProcInfo;
\r
8861 STARTUPINFO siStartInfo;
\r
8863 char buf[MSG_SIZ];
\r
8866 if (appData.debugMode) {
\r
8867 fprintf(debugFP, "StartChildProcess (dir=\"%s\") %s\n", dir, cmdLine);
\r
8872 /* Set the bInheritHandle flag so pipe handles are inherited. */
\r
8873 saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);
\r
8874 saAttr.bInheritHandle = TRUE;
\r
8875 saAttr.lpSecurityDescriptor = NULL;
\r
8878 * The steps for redirecting child's STDOUT:
\r
8879 * 1. Create anonymous pipe to be STDOUT for child.
\r
8880 * 2. Create a noninheritable duplicate of read handle,
\r
8881 * and close the inheritable read handle.
\r
8884 /* Create a pipe for the child's STDOUT. */
\r
8885 if (! CreatePipe(&hChildStdoutRd, &hChildStdoutWr, &saAttr, 0)) {
\r
8886 return GetLastError();
\r
8889 /* Duplicate the read handle to the pipe, so it is not inherited. */
\r
8890 fSuccess = DuplicateHandle(GetCurrentProcess(), hChildStdoutRd,
\r
8891 GetCurrentProcess(), &hChildStdoutRdDup, 0,
\r
8892 FALSE, /* not inherited */
\r
8893 DUPLICATE_SAME_ACCESS);
\r
8895 return GetLastError();
\r
8897 CloseHandle(hChildStdoutRd);
\r
8900 * The steps for redirecting child's STDIN:
\r
8901 * 1. Create anonymous pipe to be STDIN for child.
\r
8902 * 2. Create a noninheritable duplicate of write handle,
\r
8903 * and close the inheritable write handle.
\r
8906 /* Create a pipe for the child's STDIN. */
\r
8907 if (! CreatePipe(&hChildStdinRd, &hChildStdinWr, &saAttr, 0)) {
\r
8908 return GetLastError();
\r
8911 /* Duplicate the write handle to the pipe, so it is not inherited. */
\r
8912 fSuccess = DuplicateHandle(GetCurrentProcess(), hChildStdinWr,
\r
8913 GetCurrentProcess(), &hChildStdinWrDup, 0,
\r
8914 FALSE, /* not inherited */
\r
8915 DUPLICATE_SAME_ACCESS);
\r
8917 return GetLastError();
\r
8919 CloseHandle(hChildStdinWr);
\r
8921 /* Arrange to (1) look in dir for the child .exe file, and
\r
8922 * (2) have dir be the child's working directory. Interpret
\r
8923 * dir relative to the directory WinBoard loaded from. */
\r
8924 GetCurrentDirectory(MSG_SIZ, buf);
\r
8925 SetCurrentDirectory(installDir);
\r
8926 SetCurrentDirectory(dir);
\r
8928 /* Now create the child process. */
\r
8930 siStartInfo.cb = sizeof(STARTUPINFO);
\r
8931 siStartInfo.lpReserved = NULL;
\r
8932 siStartInfo.lpDesktop = NULL;
\r
8933 siStartInfo.lpTitle = NULL;
\r
8934 siStartInfo.dwFlags = STARTF_USESTDHANDLES;
\r
8935 siStartInfo.cbReserved2 = 0;
\r
8936 siStartInfo.lpReserved2 = NULL;
\r
8937 siStartInfo.hStdInput = hChildStdinRd;
\r
8938 siStartInfo.hStdOutput = hChildStdoutWr;
\r
8939 siStartInfo.hStdError = hChildStdoutWr;
\r
8941 fSuccess = CreateProcess(NULL,
\r
8942 cmdLine, /* command line */
\r
8943 NULL, /* process security attributes */
\r
8944 NULL, /* primary thread security attrs */
\r
8945 TRUE, /* handles are inherited */
\r
8946 DETACHED_PROCESS|CREATE_NEW_PROCESS_GROUP,
\r
8947 NULL, /* use parent's environment */
\r
8949 &siStartInfo, /* STARTUPINFO pointer */
\r
8950 &piProcInfo); /* receives PROCESS_INFORMATION */
\r
8952 err = GetLastError();
\r
8953 SetCurrentDirectory(buf); /* return to prev directory */
\r
8958 if (appData.niceEngines){ // [HGM] nice: adjust engine proc priority
\r
8959 if(appData.debugMode) fprintf(debugFP, "nice engine proc to %d\n", appData.niceEngines);
\r
8960 SetPriorityClass(piProcInfo.hProcess, GetWin32Priority(appData.niceEngines));
\r
8963 /* Close the handles we don't need in the parent */
\r
8964 CloseHandle(piProcInfo.hThread);
\r
8965 CloseHandle(hChildStdinRd);
\r
8966 CloseHandle(hChildStdoutWr);
\r
8968 /* Prepare return value */
\r
8969 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
\r
8970 cp->kind = CPReal;
\r
8971 cp->hProcess = piProcInfo.hProcess;
\r
8972 cp->pid = piProcInfo.dwProcessId;
\r
8973 cp->hFrom = hChildStdoutRdDup;
\r
8974 cp->hTo = hChildStdinWrDup;
\r
8976 *pr = (void *) cp;
\r
8978 /* Klaus Friedel says that this Sleep solves a problem under Windows
\r
8979 2000 where engines sometimes don't see the initial command(s)
\r
8980 from WinBoard and hang. I don't understand how that can happen,
\r
8981 but the Sleep is harmless, so I've put it in. Others have also
\r
8982 reported what may be the same problem, so hopefully this will fix
\r
8983 it for them too. */
\r
8991 DestroyChildProcess(ProcRef pr, int/*boolean*/ signal)
\r
8993 ChildProc *cp; int result;
\r
8995 cp = (ChildProc *) pr;
\r
8996 if (cp == NULL) return;
\r
8998 switch (cp->kind) {
\r
9000 /* TerminateProcess is considered harmful, so... */
\r
9001 CloseHandle(cp->hTo); /* Closing this will give the child an EOF and hopefully kill it */
\r
9002 if (cp->hFrom) CloseHandle(cp->hFrom); /* if NULL, InputThread will close it */
\r
9003 /* The following doesn't work because the chess program
\r
9004 doesn't "have the same console" as WinBoard. Maybe
\r
9005 we could arrange for this even though neither WinBoard
\r
9006 nor the chess program uses a console for stdio? */
\r
9007 /*!!if (signal) GenerateConsoleCtrlEvent(CTRL_BREAK_EVENT, cp->pid);*/
\r
9009 /* [AS] Special termination modes for misbehaving programs... */
\r
9010 if( signal == 9 ) {
\r
9011 result = TerminateProcess( cp->hProcess, 0 );
\r
9013 if ( appData.debugMode) {
\r
9014 fprintf( debugFP, "Terminating process %lu, result=%d\n", cp->pid, result );
\r
9017 else if( signal == 10 ) {
\r
9018 DWORD dw = WaitForSingleObject( cp->hProcess, 3*1000 ); // Wait 3 seconds at most
\r
9020 if( dw != WAIT_OBJECT_0 ) {
\r
9021 result = TerminateProcess( cp->hProcess, 0 );
\r
9023 if ( appData.debugMode) {
\r
9024 fprintf( debugFP, "Process %lu still alive after timeout, killing... result=%d\n", cp->pid, result );
\r
9030 CloseHandle(cp->hProcess);
\r
9034 if (cp->hFrom) CloseHandle(cp->hFrom);
\r
9038 closesocket(cp->sock);
\r
9043 if (signal) send(cp->sock2, "\017", 1, 0); /* 017 = 15 = SIGTERM */
\r
9044 closesocket(cp->sock);
\r
9045 closesocket(cp->sock2);
\r
9053 InterruptChildProcess(ProcRef pr)
\r
9057 cp = (ChildProc *) pr;
\r
9058 if (cp == NULL) return;
\r
9059 switch (cp->kind) {
\r
9061 /* The following doesn't work because the chess program
\r
9062 doesn't "have the same console" as WinBoard. Maybe
\r
9063 we could arrange for this even though neither WinBoard
\r
9064 nor the chess program uses a console for stdio */
\r
9065 /*!!GenerateConsoleCtrlEvent(CTRL_C_EVENT, cp->pid);*/
\r
9070 /* Can't interrupt */
\r
9074 send(cp->sock2, "\002", 1, 0); /* 2 = SIGINT */
\r
9081 OpenTelnet(char *host, char *port, ProcRef *pr)
\r
9083 char cmdLine[MSG_SIZ];
\r
9085 if (port[0] == NULLCHAR) {
\r
9086 snprintf(cmdLine, MSG_SIZ, "%s %s", appData.telnetProgram, host);
\r
9088 snprintf(cmdLine, MSG_SIZ, "%s %s %s", appData.telnetProgram, host, port);
\r
9090 return StartChildProcess(cmdLine, "", pr);
\r
9094 /* Code to open TCP sockets */
\r
9097 OpenTCP(char *host, char *port, ProcRef *pr)
\r
9102 struct sockaddr_in sa, mysa;
\r
9103 struct hostent FAR *hp;
\r
9104 unsigned short uport;
\r
9105 WORD wVersionRequested;
\r
9108 /* Initialize socket DLL */
\r
9109 wVersionRequested = MAKEWORD(1, 1);
\r
9110 err = WSAStartup(wVersionRequested, &wsaData);
\r
9111 if (err != 0) return err;
\r
9114 if ((s = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {
\r
9115 err = WSAGetLastError();
\r
9120 /* Bind local address using (mostly) don't-care values.
\r
9122 memset((char *) &mysa, 0, sizeof(struct sockaddr_in));
\r
9123 mysa.sin_family = AF_INET;
\r
9124 mysa.sin_addr.s_addr = INADDR_ANY;
\r
9125 uport = (unsigned short) 0;
\r
9126 mysa.sin_port = htons(uport);
\r
9127 if (bind(s, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))
\r
9128 == SOCKET_ERROR) {
\r
9129 err = WSAGetLastError();
\r
9134 /* Resolve remote host name */
\r
9135 memset((char *) &sa, 0, sizeof(struct sockaddr_in));
\r
9136 if (!(hp = gethostbyname(host))) {
\r
9137 unsigned int b0, b1, b2, b3;
\r
9139 err = WSAGetLastError();
\r
9141 if (sscanf(host, "%u.%u.%u.%u", &b0, &b1, &b2, &b3) == 4) {
\r
9142 hp = (struct hostent *) calloc(1, sizeof(struct hostent));
\r
9143 hp->h_addrtype = AF_INET;
\r
9145 hp->h_addr_list = (char **) calloc(2, sizeof(char *));
\r
9146 hp->h_addr_list[0] = (char *) malloc(4);
\r
9147 hp->h_addr_list[0][0] = (char) b0;
\r
9148 hp->h_addr_list[0][1] = (char) b1;
\r
9149 hp->h_addr_list[0][2] = (char) b2;
\r
9150 hp->h_addr_list[0][3] = (char) b3;
\r
9156 sa.sin_family = hp->h_addrtype;
\r
9157 uport = (unsigned short) atoi(port);
\r
9158 sa.sin_port = htons(uport);
\r
9159 memcpy((char *) &sa.sin_addr, hp->h_addr, hp->h_length);
\r
9161 /* Make connection */
\r
9162 if (connect(s, (struct sockaddr *) &sa,
\r
9163 sizeof(struct sockaddr_in)) == SOCKET_ERROR) {
\r
9164 err = WSAGetLastError();
\r
9169 /* Prepare return value */
\r
9170 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
\r
9171 cp->kind = CPSock;
\r
9173 *pr = (ProcRef *) cp;
\r
9179 OpenCommPort(char *name, ProcRef *pr)
\r
9184 char fullname[MSG_SIZ];
\r
9186 if (*name != '\\')
\r
9187 snprintf(fullname, MSG_SIZ, "\\\\.\\%s", name);
\r
9189 safeStrCpy(fullname, name, sizeof(fullname)/sizeof(fullname[0]) );
\r
9191 h = CreateFile(name, GENERIC_READ | GENERIC_WRITE,
\r
9192 0, NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL);
\r
9193 if (h == (HANDLE) -1) {
\r
9194 return GetLastError();
\r
9198 if (!SetCommState(h, (LPDCB) &dcb)) return GetLastError();
\r
9200 /* Accumulate characters until a 100ms pause, then parse */
\r
9201 ct.ReadIntervalTimeout = 100;
\r
9202 ct.ReadTotalTimeoutMultiplier = 0;
\r
9203 ct.ReadTotalTimeoutConstant = 0;
\r
9204 ct.WriteTotalTimeoutMultiplier = 0;
\r
9205 ct.WriteTotalTimeoutConstant = 0;
\r
9206 if (!SetCommTimeouts(h, (LPCOMMTIMEOUTS) &ct)) return GetLastError();
\r
9208 /* Prepare return value */
\r
9209 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
\r
9210 cp->kind = CPComm;
\r
9213 *pr = (ProcRef *) cp;
\r
9219 OpenLoopback(ProcRef *pr)
\r
9221 DisplayFatalError(_("Not implemented"), 0, 1);
\r
9227 OpenRcmd(char* host, char* user, char* cmd, ProcRef* pr)
\r
9232 struct sockaddr_in sa, mysa;
\r
9233 struct hostent FAR *hp;
\r
9234 unsigned short uport;
\r
9235 WORD wVersionRequested;
\r
9238 char stderrPortStr[MSG_SIZ];
\r
9240 /* Initialize socket DLL */
\r
9241 wVersionRequested = MAKEWORD(1, 1);
\r
9242 err = WSAStartup(wVersionRequested, &wsaData);
\r
9243 if (err != 0) return err;
\r
9245 /* Resolve remote host name */
\r
9246 memset((char *) &sa, 0, sizeof(struct sockaddr_in));
\r
9247 if (!(hp = gethostbyname(host))) {
\r
9248 unsigned int b0, b1, b2, b3;
\r
9250 err = WSAGetLastError();
\r
9252 if (sscanf(host, "%u.%u.%u.%u", &b0, &b1, &b2, &b3) == 4) {
\r
9253 hp = (struct hostent *) calloc(1, sizeof(struct hostent));
\r
9254 hp->h_addrtype = AF_INET;
\r
9256 hp->h_addr_list = (char **) calloc(2, sizeof(char *));
\r
9257 hp->h_addr_list[0] = (char *) malloc(4);
\r
9258 hp->h_addr_list[0][0] = (char) b0;
\r
9259 hp->h_addr_list[0][1] = (char) b1;
\r
9260 hp->h_addr_list[0][2] = (char) b2;
\r
9261 hp->h_addr_list[0][3] = (char) b3;
\r
9267 sa.sin_family = hp->h_addrtype;
\r
9268 uport = (unsigned short) 514;
\r
9269 sa.sin_port = htons(uport);
\r
9270 memcpy((char *) &sa.sin_addr, hp->h_addr, hp->h_length);
\r
9272 /* Bind local socket to unused "privileged" port address
\r
9274 s = INVALID_SOCKET;
\r
9275 memset((char *) &mysa, 0, sizeof(struct sockaddr_in));
\r
9276 mysa.sin_family = AF_INET;
\r
9277 mysa.sin_addr.s_addr = INADDR_ANY;
\r
9278 for (fromPort = 1023;; fromPort--) {
\r
9279 if (fromPort < 0) {
\r
9281 return WSAEADDRINUSE;
\r
9283 if (s == INVALID_SOCKET) {
\r
9284 if ((s = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {
\r
9285 err = WSAGetLastError();
\r
9290 uport = (unsigned short) fromPort;
\r
9291 mysa.sin_port = htons(uport);
\r
9292 if (bind(s, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))
\r
9293 == SOCKET_ERROR) {
\r
9294 err = WSAGetLastError();
\r
9295 if (err == WSAEADDRINUSE) continue;
\r
9299 if (connect(s, (struct sockaddr *) &sa,
\r
9300 sizeof(struct sockaddr_in)) == SOCKET_ERROR) {
\r
9301 err = WSAGetLastError();
\r
9302 if (err == WSAEADDRINUSE) {
\r
9313 /* Bind stderr local socket to unused "privileged" port address
\r
9315 s2 = INVALID_SOCKET;
\r
9316 memset((char *) &mysa, 0, sizeof(struct sockaddr_in));
\r
9317 mysa.sin_family = AF_INET;
\r
9318 mysa.sin_addr.s_addr = INADDR_ANY;
\r
9319 for (fromPort = 1023;; fromPort--) {
\r
9320 if (fromPort == prevStderrPort) continue; // don't reuse port
\r
9321 if (fromPort < 0) {
\r
9322 (void) closesocket(s);
\r
9324 return WSAEADDRINUSE;
\r
9326 if (s2 == INVALID_SOCKET) {
\r
9327 if ((s2 = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {
\r
9328 err = WSAGetLastError();
\r
9334 uport = (unsigned short) fromPort;
\r
9335 mysa.sin_port = htons(uport);
\r
9336 if (bind(s2, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))
\r
9337 == SOCKET_ERROR) {
\r
9338 err = WSAGetLastError();
\r
9339 if (err == WSAEADDRINUSE) continue;
\r
9340 (void) closesocket(s);
\r
9344 if (listen(s2, 1) == SOCKET_ERROR) {
\r
9345 err = WSAGetLastError();
\r
9346 if (err == WSAEADDRINUSE) {
\r
9348 s2 = INVALID_SOCKET;
\r
9351 (void) closesocket(s);
\r
9352 (void) closesocket(s2);
\r
9358 prevStderrPort = fromPort; // remember port used
\r
9359 snprintf(stderrPortStr, MSG_SIZ, "%d", fromPort);
\r
9361 if (send(s, stderrPortStr, strlen(stderrPortStr) + 1, 0) == SOCKET_ERROR) {
\r
9362 err = WSAGetLastError();
\r
9363 (void) closesocket(s);
\r
9364 (void) closesocket(s2);
\r
9369 if (send(s, UserName(), strlen(UserName()) + 1, 0) == SOCKET_ERROR) {
\r
9370 err = WSAGetLastError();
\r
9371 (void) closesocket(s);
\r
9372 (void) closesocket(s2);
\r
9376 if (*user == NULLCHAR) user = UserName();
\r
9377 if (send(s, user, strlen(user) + 1, 0) == SOCKET_ERROR) {
\r
9378 err = WSAGetLastError();
\r
9379 (void) closesocket(s);
\r
9380 (void) closesocket(s2);
\r
9384 if (send(s, cmd, strlen(cmd) + 1, 0) == SOCKET_ERROR) {
\r
9385 err = WSAGetLastError();
\r
9386 (void) closesocket(s);
\r
9387 (void) closesocket(s2);
\r
9392 if ((s3 = accept(s2, NULL, NULL)) == INVALID_SOCKET) {
\r
9393 err = WSAGetLastError();
\r
9394 (void) closesocket(s);
\r
9395 (void) closesocket(s2);
\r
9399 (void) closesocket(s2); /* Stop listening */
\r
9401 /* Prepare return value */
\r
9402 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
\r
9403 cp->kind = CPRcmd;
\r
9406 *pr = (ProcRef *) cp;
\r
9413 AddInputSource(ProcRef pr, int lineByLine,
\r
9414 InputCallback func, VOIDSTAR closure)
\r
9416 InputSource *is, *is2 = NULL;
\r
9417 ChildProc *cp = (ChildProc *) pr;
\r
9419 is = (InputSource *) calloc(1, sizeof(InputSource));
\r
9420 is->lineByLine = lineByLine;
\r
9422 is->closure = closure;
\r
9423 is->second = NULL;
\r
9424 is->next = is->buf;
\r
9425 if (pr == NoProc) {
\r
9426 is->kind = CPReal;
\r
9427 consoleInputSource = is;
\r
9429 is->kind = cp->kind;
\r
9431 [AS] Try to avoid a race condition if the thread is given control too early:
\r
9432 we create all threads suspended so that the is->hThread variable can be
\r
9433 safely assigned, then let the threads start with ResumeThread.
\r
9435 switch (cp->kind) {
\r
9437 is->hFile = cp->hFrom;
\r
9438 cp->hFrom = NULL; /* now owned by InputThread */
\r
9440 CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) NonOvlInputThread,
\r
9441 (LPVOID) is, CREATE_SUSPENDED, &is->id);
\r
9445 is->hFile = cp->hFrom;
\r
9446 cp->hFrom = NULL; /* now owned by InputThread */
\r
9448 CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) InputThread,
\r
9449 (LPVOID) is, CREATE_SUSPENDED, &is->id);
\r
9453 is->sock = cp->sock;
\r
9455 CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,
\r
9456 (LPVOID) is, CREATE_SUSPENDED, &is->id);
\r
9460 is2 = (InputSource *) calloc(1, sizeof(InputSource));
\r
9462 is->sock = cp->sock;
\r
9464 is2->sock = cp->sock2;
\r
9465 is2->second = is2;
\r
9467 CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,
\r
9468 (LPVOID) is, CREATE_SUSPENDED, &is->id);
\r
9470 CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,
\r
9471 (LPVOID) is2, CREATE_SUSPENDED, &is2->id);
\r
9475 if( is->hThread != NULL ) {
\r
9476 ResumeThread( is->hThread );
\r
9479 if( is2 != NULL && is2->hThread != NULL ) {
\r
9480 ResumeThread( is2->hThread );
\r
9484 return (InputSourceRef) is;
\r
9488 RemoveInputSource(InputSourceRef isr)
\r
9492 is = (InputSource *) isr;
\r
9493 is->hThread = NULL; /* tell thread to stop */
\r
9494 CloseHandle(is->hThread);
\r
9495 if (is->second != NULL) {
\r
9496 is->second->hThread = NULL;
\r
9497 CloseHandle(is->second->hThread);
\r
9501 int no_wrap(char *message, int count)
\r
9503 ConsoleOutput(message, count, FALSE);
\r
9508 OutputToProcess(ProcRef pr, char *message, int count, int *outError)
\r
9511 int outCount = SOCKET_ERROR;
\r
9512 ChildProc *cp = (ChildProc *) pr;
\r
9513 static OVERLAPPED ovl;
\r
9514 static int line = 0;
\r
9518 if (appData.noJoin || !appData.useInternalWrap)
\r
9519 return no_wrap(message, count);
\r
9522 int width = get_term_width();
\r
9523 int len = wrap(NULL, message, count, width, &line);
\r
9524 char *msg = malloc(len);
\r
9528 return no_wrap(message, count);
\r
9531 dbgchk = wrap(msg, message, count, width, &line);
\r
9532 if (dbgchk != len && appData.debugMode)
\r
9533 fprintf(debugFP, "wrap(): dbgchk(%d) != len(%d)\n", dbgchk, len);
\r
9534 ConsoleOutput(msg, len, FALSE);
\r
9541 if (ovl.hEvent == NULL) {
\r
9542 ovl.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
\r
9544 ovl.Internal = ovl.InternalHigh = ovl.Offset = ovl.OffsetHigh = 0;
\r
9546 switch (cp->kind) {
\r
9549 outCount = send(cp->sock, message, count, 0);
\r
9550 if (outCount == SOCKET_ERROR) {
\r
9551 *outError = WSAGetLastError();
\r
9553 *outError = NO_ERROR;
\r
9558 if (WriteFile(((ChildProc *)pr)->hTo, message, count,
\r
9559 &dOutCount, NULL)) {
\r
9560 *outError = NO_ERROR;
\r
9561 outCount = (int) dOutCount;
\r
9563 *outError = GetLastError();
\r
9568 *outError = DoWriteFile(((ChildProc *)pr)->hTo, message, count,
\r
9569 &dOutCount, &ovl);
\r
9570 if (*outError == NO_ERROR) {
\r
9571 outCount = (int) dOutCount;
\r
9579 OutputToProcessDelayed(ProcRef pr, char *message, int count, int *outError,
\r
9582 /* Ignore delay, not implemented for WinBoard */
\r
9583 return OutputToProcess(pr, message, count, outError);
\r
9588 CmailSigHandlerCallBack(InputSourceRef isr, VOIDSTAR closure,
\r
9589 char *buf, int count, int error)
\r
9591 DisplayFatalError(_("Not implemented"), 0, 1);
\r
9594 /* see wgamelist.c for Game List functions */
\r
9595 /* see wedittags.c for Edit Tags functions */
\r
9602 char buf[MSG_SIZ];
\r
9605 if (SearchPath(installDir, appData.icsLogon, NULL, MSG_SIZ, buf, &dummy)) {
\r
9606 f = fopen(buf, "r");
\r
9608 ProcessICSInitScript(f);
\r
9616 StartAnalysisClock()
\r
9618 if (analysisTimerEvent) return;
\r
9619 analysisTimerEvent = SetTimer(hwndMain, (UINT) ANALYSIS_TIMER_ID,
\r
9620 (UINT) 2000, NULL);
\r
9624 SetHighlights(int fromX, int fromY, int toX, int toY)
\r
9626 highlightInfo.sq[0].x = fromX;
\r
9627 highlightInfo.sq[0].y = fromY;
\r
9628 highlightInfo.sq[1].x = toX;
\r
9629 highlightInfo.sq[1].y = toY;
\r
9635 highlightInfo.sq[0].x = highlightInfo.sq[0].y =
\r
9636 highlightInfo.sq[1].x = highlightInfo.sq[1].y = -1;
\r
9640 SetPremoveHighlights(int fromX, int fromY, int toX, int toY)
\r
9642 premoveHighlightInfo.sq[0].x = fromX;
\r
9643 premoveHighlightInfo.sq[0].y = fromY;
\r
9644 premoveHighlightInfo.sq[1].x = toX;
\r
9645 premoveHighlightInfo.sq[1].y = toY;
\r
9649 ClearPremoveHighlights()
\r
9651 premoveHighlightInfo.sq[0].x = premoveHighlightInfo.sq[0].y =
\r
9652 premoveHighlightInfo.sq[1].x = premoveHighlightInfo.sq[1].y = -1;
\r
9656 ShutDownFrontEnd()
\r
9658 if (saveSettingsOnExit) SaveSettings(settingsFileName);
\r
9659 DeleteClipboardTempFiles();
\r
9665 if (IsIconic(hwndMain))
\r
9666 ShowWindow(hwndMain, SW_RESTORE);
\r
9668 SetActiveWindow(hwndMain);
\r
9672 * Prototypes for animation support routines
\r
9674 static void ScreenSquare(int column, int row, POINT * pt);
\r
9675 static void Tween( POINT * start, POINT * mid, POINT * finish, int factor,
\r
9676 POINT frames[], int * nFrames);
\r
9682 AnimateAtomicCapture(Board board, int fromX, int fromY, int toX, int toY)
\r
9683 { // [HGM] atomic: animate blast wave
\r
9686 explodeInfo.fromX = fromX;
\r
9687 explodeInfo.fromY = fromY;
\r
9688 explodeInfo.toX = toX;
\r
9689 explodeInfo.toY = toY;
\r
9690 for(i=1; i<4*kFactor; i++) {
\r
9691 explodeInfo.radius = (i*180)/(4*kFactor-1);
\r
9692 DrawPosition(FALSE, board);
\r
9693 Sleep(appData.animSpeed);
\r
9695 explodeInfo.radius = 0;
\r
9696 DrawPosition(TRUE, board);
\r
9700 AnimateMove(board, fromX, fromY, toX, toY)
\r
9707 ChessSquare piece;
\r
9708 POINT start, finish, mid;
\r
9709 POINT frames[kFactor * 2 + 1];
\r
9712 if (!appData.animate) return;
\r
9713 if (doingSizing) return;
\r
9714 if (fromY < 0 || fromX < 0) return;
\r
9715 piece = board[fromY][fromX];
\r
9716 if (piece >= EmptySquare) return;
\r
9718 ScreenSquare(fromX, fromY, &start);
\r
9719 ScreenSquare(toX, toY, &finish);
\r
9721 /* All moves except knight jumps move in straight line */
\r
9722 if (!(abs(fromX-toX) == 1 && abs(fromY-toY) == 2 || abs(fromX-toX) == 2 && abs(fromY-toY) == 1)) {
\r
9723 mid.x = start.x + (finish.x - start.x) / 2;
\r
9724 mid.y = start.y + (finish.y - start.y) / 2;
\r
9726 /* Knight: make straight movement then diagonal */
\r
9727 if (abs(toY - fromY) < abs(toX - fromX)) {
\r
9728 mid.x = start.x + (finish.x - start.x) / 2;
\r
9732 mid.y = start.y + (finish.y - start.y) / 2;
\r
9736 /* Don't use as many frames for very short moves */
\r
9737 if (abs(toY - fromY) + abs(toX - fromX) <= 2)
\r
9738 Tween(&start, &mid, &finish, kFactor - 1, frames, &nFrames);
\r
9740 Tween(&start, &mid, &finish, kFactor, frames, &nFrames);
\r
9742 animInfo.from.x = fromX;
\r
9743 animInfo.from.y = fromY;
\r
9744 animInfo.to.x = toX;
\r
9745 animInfo.to.y = toY;
\r
9746 animInfo.lastpos = start;
\r
9747 animInfo.piece = piece;
\r
9748 for (n = 0; n < nFrames; n++) {
\r
9749 animInfo.pos = frames[n];
\r
9750 DrawPosition(FALSE, NULL);
\r
9751 animInfo.lastpos = animInfo.pos;
\r
9752 Sleep(appData.animSpeed);
\r
9754 animInfo.pos = finish;
\r
9755 DrawPosition(FALSE, NULL);
\r
9756 animInfo.piece = EmptySquare;
\r
9757 Explode(board, fromX, fromY, toX, toY);
\r
9760 /* Convert board position to corner of screen rect and color */
\r
9763 ScreenSquare(column, row, pt)
\r
9764 int column; int row; POINT * pt;
\r
9767 pt->x = lineGap + ((BOARD_WIDTH-1)-column) * (squareSize + lineGap);
\r
9768 pt->y = lineGap + row * (squareSize + lineGap);
\r
9770 pt->x = lineGap + column * (squareSize + lineGap);
\r
9771 pt->y = lineGap + ((BOARD_HEIGHT-1)-row) * (squareSize + lineGap);
\r
9775 /* Generate a series of frame coords from start->mid->finish.
\r
9776 The movement rate doubles until the half way point is
\r
9777 reached, then halves back down to the final destination,
\r
9778 which gives a nice slow in/out effect. The algorithmn
\r
9779 may seem to generate too many intermediates for short
\r
9780 moves, but remember that the purpose is to attract the
\r
9781 viewers attention to the piece about to be moved and
\r
9782 then to where it ends up. Too few frames would be less
\r
9786 Tween(start, mid, finish, factor, frames, nFrames)
\r
9787 POINT * start; POINT * mid;
\r
9788 POINT * finish; int factor;
\r
9789 POINT frames[]; int * nFrames;
\r
9791 int n, fraction = 1, count = 0;
\r
9793 /* Slow in, stepping 1/16th, then 1/8th, ... */
\r
9794 for (n = 0; n < factor; n++)
\r
9796 for (n = 0; n < factor; n++) {
\r
9797 frames[count].x = start->x + (mid->x - start->x) / fraction;
\r
9798 frames[count].y = start->y + (mid->y - start->y) / fraction;
\r
9800 fraction = fraction / 2;
\r
9804 frames[count] = *mid;
\r
9807 /* Slow out, stepping 1/2, then 1/4, ... */
\r
9809 for (n = 0; n < factor; n++) {
\r
9810 frames[count].x = finish->x - (finish->x - mid->x) / fraction;
\r
9811 frames[count].y = finish->y - (finish->y - mid->y) / fraction;
\r
9813 fraction = fraction * 2;
\r
9819 HistorySet( char movelist[][2*MOVE_LEN], int first, int last, int current )
\r
9821 MoveHistorySet( movelist, first, last, current, pvInfoList );
\r
9823 EvalGraphSet( first, last, current, pvInfoList );
\r
9827 SettingsPopUp(ChessProgramState *cps)
\r
9828 { // [HGM] wrapper needed because handles must not be passed through back-end
\r
9829 EngineOptionsPopup(savedHwnd, cps);
\r