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
84 #include "frontend.h"
\r
85 #include "backend.h"
\r
86 #include "winboard.h"
\r
88 #include "wclipbrd.h"
\r
89 #include "woptions.h"
\r
90 #include "wsockerr.h"
\r
91 #include "defaults.h"
\r
95 //void InitEngineUCI( const char * iniDir, ChessProgramState * cps );
\r
98 void mysrandom(unsigned int seed);
\r
100 extern int whiteFlag, blackFlag;
\r
101 Boolean flipClock = FALSE;
\r
102 extern HANDLE chatHandle[];
\r
103 extern int ics_type;
\r
105 void DisplayHoldingsCount(HDC hdc, int x, int y, int align, int copyNumber);
\r
106 VOID NewVariantPopup(HWND hwnd);
\r
107 int FinishMove P((ChessMove moveType, int fromX, int fromY, int toX, int toY,
\r
108 /*char*/int promoChar));
\r
109 void DisplayMove P((int moveNumber));
\r
110 Boolean ParseFEN P((Board board, int *blackPlaysFirst, char *fen));
\r
111 void ChatPopUp P((char *s));
\r
113 ChessSquare piece;
\r
114 POINT pos; /* window coordinates of current pos */
\r
115 POINT lastpos; /* window coordinates of last pos - used for clipping */
\r
116 POINT from; /* board coordinates of the piece's orig pos */
\r
117 POINT to; /* board coordinates of the piece's new pos */
\r
120 static AnimInfo animInfo = { EmptySquare, {-1,-1}, {-1,-1}, {-1,-1} };
\r
123 POINT start; /* window coordinates of start pos */
\r
124 POINT pos; /* window coordinates of current pos */
\r
125 POINT lastpos; /* window coordinates of last pos - used for clipping */
\r
126 POINT from; /* board coordinates of the piece's orig pos */
\r
130 static DragInfo dragInfo = { {-1,-1}, {-1,-1}, {-1,-1}, {-1,-1}, EmptySquare };
\r
133 POINT sq[2]; /* board coordinates of from, to squares */
\r
136 static HighlightInfo highlightInfo = { {{-1, -1}, {-1, -1}} };
\r
137 static HighlightInfo premoveHighlightInfo = { {{-1, -1}, {-1, -1}} };
\r
138 static HighlightInfo partnerHighlightInfo = { {{-1, -1}, {-1, -1}} };
\r
139 static HighlightInfo oldPartnerHighlight = { {{-1, -1}, {-1, -1}} };
\r
141 typedef struct { // [HGM] atomic
\r
142 int fromX, fromY, toX, toY, radius;
\r
145 static ExplodeInfo explodeInfo;
\r
147 /* Window class names */
\r
148 char szAppName[] = "WinBoard";
\r
149 char szConsoleName[] = "WBConsole";
\r
151 /* Title bar text */
\r
152 char szTitle[] = "WinBoard";
\r
153 char szConsoleTitle[] = "I C S Interaction";
\r
156 char *settingsFileName;
\r
157 Boolean saveSettingsOnExit;
\r
158 char installDir[MSG_SIZ];
\r
159 int errorExitStatus;
\r
161 BoardSize boardSize;
\r
162 Boolean chessProgram;
\r
163 //static int boardX, boardY;
\r
164 int minX, minY; // [HGM] placement: volatile limits on upper-left corner
\r
165 int squareSize, lineGap, minorSize;
\r
166 static int winW, winH;
\r
167 static RECT messageRect, whiteRect, blackRect, leftLogoRect, rightLogoRect; // [HGM] logo
\r
168 static int logoHeight = 0;
\r
169 static char messageText[MESSAGE_TEXT_MAX];
\r
170 static int clockTimerEvent = 0;
\r
171 static int loadGameTimerEvent = 0;
\r
172 static int analysisTimerEvent = 0;
\r
173 static DelayedEventCallback delayedTimerCallback;
\r
174 static int delayedTimerEvent = 0;
\r
175 static int buttonCount = 2;
\r
176 char *icsTextMenuString;
\r
178 char *firstChessProgramNames;
\r
179 char *secondChessProgramNames;
\r
181 #define PALETTESIZE 256
\r
183 HINSTANCE hInst; /* current instance */
\r
184 Boolean alwaysOnTop = FALSE;
\r
186 COLORREF lightSquareColor, darkSquareColor, whitePieceColor,
\r
187 blackPieceColor, highlightSquareColor, premoveHighlightColor;
\r
189 ColorClass currentColorClass;
\r
191 static HWND savedHwnd;
\r
192 HWND hCommPort = NULL; /* currently open comm port */
\r
193 static HWND hwndPause; /* pause button */
\r
194 static HBITMAP pieceBitmap[3][(int) BlackPawn]; /* [HGM] nr of bitmaps referred to bP in stead of wK */
\r
195 static HBRUSH lightSquareBrush, darkSquareBrush,
\r
196 blackSquareBrush, /* [HGM] for band between board and holdings */
\r
197 explodeBrush, /* [HGM] atomic */
\r
198 markerBrush, /* [HGM] markers */
\r
199 whitePieceBrush, blackPieceBrush, iconBkgndBrush /*, outlineBrush*/;
\r
200 static POINT gridEndpoints[(BOARD_RANKS + BOARD_FILES + 2) * 2];
\r
201 static DWORD gridVertexCounts[BOARD_RANKS + BOARD_FILES + 2];
\r
202 static HPEN gridPen = NULL;
\r
203 static HPEN highlightPen = NULL;
\r
204 static HPEN premovePen = NULL;
\r
205 static NPLOGPALETTE pLogPal;
\r
206 static BOOL paletteChanged = FALSE;
\r
207 static HICON iconWhite, iconBlack, iconCurrent;
\r
208 static int doingSizing = FALSE;
\r
209 static int lastSizing = 0;
\r
210 static int prevStderrPort;
\r
211 static HBITMAP userLogo;
\r
213 static HBITMAP liteBackTexture = NULL;
\r
214 static HBITMAP darkBackTexture = NULL;
\r
215 static int liteBackTextureMode = BACK_TEXTURE_MODE_PLAIN;
\r
216 static int darkBackTextureMode = BACK_TEXTURE_MODE_PLAIN;
\r
217 static int backTextureSquareSize = 0;
\r
218 static struct { int x; int y; int mode; } backTextureSquareInfo[BOARD_RANKS][BOARD_FILES];
\r
220 #if __GNUC__ && !defined(_winmajor)
\r
221 #define oldDialog 0 /* cygwin doesn't define _winmajor; mingw does */
\r
223 #if defined(_winmajor)
\r
224 #define oldDialog (_winmajor < 4)
\r
226 #define oldDialog 0
\r
230 #define INTERNATIONAL
\r
232 #ifdef INTERNATIONAL
\r
233 # define _(s) T_(s)
\r
239 # define Translate(x, y)
\r
240 # define LoadLanguageFile(s)
\r
243 #ifdef INTERNATIONAL
\r
245 Boolean barbaric; // flag indicating if translation is needed
\r
247 // list of item numbers used in each dialog (used to alter language at run time)
\r
249 #define ABOUTBOX -1 /* not sure why these are needed */
\r
250 #define ABOUTBOX2 -1
\r
252 int dialogItems[][40] = {
\r
253 { ABOUTBOX, IDOK, OPT_MESS, 400 },
\r
254 { DLG_TimeControl, IDC_Babble, OPT_TCUseMoves, OPT_TCUseInc, OPT_TCUseFixed,
\r
255 OPT_TCtext1, OPT_TCtext2, OPT_TCitext1, OPT_TCitext2, OPT_TCftext, GPB_Factors, IDC_Factor1, IDC_Factor2, IDOK, IDCANCEL },
\r
256 { DLG_LoadOptions, OPT_Autostep, OPT_AStext1, IDOK, IDCANCEL },
\r
257 { DLG_SaveOptions, OPT_Autosave, OPT_AVPrompt, OPT_AVToFile, OPT_AVBrowse,
\r
258 801, OPT_PGN, OPT_Old, OPT_OutOfBookInfo, IDOK, IDCANCEL },
\r
259 { 1536, 1090, IDC_Directories, 1089, 1091, IDOK, IDCANCEL, 1038, IDC_IndexNr, 1037 },
\r
260 { DLG_CommPort, IDOK, IDCANCEL, IDC_Port, IDC_Rate, IDC_Bits, IDC_Parity,
\r
261 IDC_Stop, IDC_Flow, OPT_SerialHelp },
\r
262 { DLG_EditComment, IDOK, OPT_CancelComment, OPT_ClearComment, OPT_EditComment },
\r
263 { DLG_PromotionKing, PB_Chancellor, PB_Archbishop, PB_Queen, PB_Rook,
\r
264 PB_Bishop, PB_Knight, PB_King, IDCANCEL, IDC_Yes, IDC_No, IDC_Centaur },
\r
265 { ABOUTBOX2, IDC_ChessBoard },
\r
266 { DLG_GameList, OPT_GameListLoad, OPT_GameListPrev, OPT_GameListNext,
\r
267 OPT_GameListClose, IDC_GameListDoFilter },
\r
268 { DLG_EditTags, IDOK, OPT_TagsCancel, OPT_EditTags },
\r
269 { DLG_Error, IDOK },
\r
270 { DLG_Colorize, IDOK, IDCANCEL, OPT_ChooseColor, OPT_Bold, OPT_Italic,
\r
271 OPT_Underline, OPT_Strikeout, OPT_Sample },
\r
272 { DLG_Question, IDOK, IDCANCEL, OPT_QuestionText },
\r
273 { DLG_Startup, IDC_Welcome, OPT_ChessEngine, OPT_ChessServer, OPT_View,
\r
274 IDC_SPECIFY_ENG_STATIC, IDC_SPECIFY_SERVER_STATIC, OPT_AnyAdditional,
\r
275 IDOK, IDCANCEL, IDM_HELPCONTENTS },
\r
276 { DLG_IndexNumber, IDC_Index },
\r
277 { DLG_TypeInMove, IDOK, IDCANCEL },
\r
278 { DLG_TypeInName, IDOK, IDCANCEL },
\r
279 { DLG_Sound, IDC_Event, OPT_NoSound, OPT_DefaultBeep, OPT_BuiltInSound,
\r
280 OPT_WavFile, OPT_BrowseSound, OPT_DefaultSounds, IDOK, IDCANCEL, OPT_PlaySound },
\r
281 { DLG_GeneralOptions, IDOK, IDCANCEL, OPT_AlwaysOnTop, OPT_HighlightLastMove,
\r
282 OPT_AlwaysQueen, OPT_PeriodicUpdates, OPT_AnimateDragging, OPT_PonderNextMove,
\r
283 OPT_AnimateMoving, OPT_PopupExitMessage, OPT_AutoFlag, OPT_PopupMoveErrors,
\r
284 OPT_AutoFlipView, OPT_ShowButtonBar, OPT_AutoRaiseBoard, OPT_ShowCoordinates,
\r
285 OPT_Blindfold, OPT_ShowThinking, OPT_HighlightDragging, OPT_TestLegality,
\r
286 OPT_SaveExtPGN, OPT_HideThinkFromHuman, OPT_ExtraInfoInMoveHistory,
\r
287 OPT_HighlightMoveArrow, OPT_AutoLogo ,OPT_SmartMove },
\r
288 { DLG_IcsOptions, IDOK, IDCANCEL, OPT_AutoComment, OPT_AutoKibitz, OPT_AutoObserve,
\r
289 OPT_GetMoveList, OPT_LocalLineEditing, OPT_QuietPlay, OPT_SeekGraph, OPT_AutoRefresh,
\r
290 OPT_BgObserve, OPT_DualBoard, OPT_Premove, OPT_PremoveWhite, OPT_PremoveBlack,
\r
291 OPT_SmartMove, OPT_IcsAlarm, IDC_Sec, OPT_ChooseShoutColor, OPT_ChooseSShoutColor,
\r
292 OPT_ChooseChannel1Color, OPT_ChooseChannelColor, OPT_ChooseKibitzColor,
\r
293 OPT_ChooseTellColor, OPT_ChooseChallengeColor, OPT_ChooseRequestColor,
\r
294 OPT_ChooseSeekColor, OPT_ChooseNormalColor, OPT_ChooseBackgroundColor,
\r
295 OPT_DefaultColors, OPT_DontColorize, IDC_Boxes, GPB_Colors, GPB_Premove,
\r
296 GPB_General, GPB_Alarm },
\r
297 { DLG_BoardOptions, IDOK, IDCANCEL, OPT_SizeTiny, OPT_SizeTeeny, OPT_SizeDinky,
\r
298 OPT_SizePetite, OPT_SizeSlim, OPT_SizeSmall, OPT_SizeMediocre, OPT_SizeMiddling,
\r
299 OPT_SizeAverage, OPT_SizeModerate, OPT_SizeMedium, OPT_SizeBulky, OPT_SizeLarge,
\r
300 OPT_SizeBig, OPT_SizeHuge, OPT_SizeGiant, OPT_SizeColossal, OPT_SizeTitanic,
\r
301 OPT_ChooseLightSquareColor, OPT_ChooseDarkSquareColor, OPT_ChooseWhitePieceColor,
\r
302 OPT_ChooseBlackPieceColor, OPT_ChooseHighlightSquareColor, OPT_ChoosePremoveHighlightColor,
\r
303 OPT_Monochrome, OPT_AllWhite, OPT_UpsideDown, OPT_DefaultBoardColors, GPB_Colors,
\r
304 IDC_Light, IDC_Dark, IDC_White, IDC_Black, IDC_High, IDC_PreHigh, GPB_Size },
\r
305 { DLG_NewVariant, IDOK, IDCANCEL, OPT_VariantNormal, OPT_VariantFRC, OPT_VariantWildcastle,
\r
306 OPT_VariantNocastle, OPT_VariantLosers, OPT_VariantGiveaway, OPT_VariantSuicide,
\r
307 OPT_Variant3Check, OPT_VariantTwoKings, OPT_VariantAtomic, OPT_VariantCrazyhouse,
\r
308 OPT_VariantBughouse, OPT_VariantTwilight, OPT_VariantShogi, OPT_VariantSuper,
\r
309 OPT_VariantKnightmate, OPT_VariantBerolina, OPT_VariantCylinder, OPT_VariantFairy,
\r
310 OPT_VariantMakruk, OPT_VariantGothic, OPT_VariantCapablanca, OPT_VariantJanus,
\r
311 OPT_VariantCRC, OPT_VariantFalcon, OPT_VariantCourier, OPT_VariantGreat, OPT_VariantSChess,
\r
312 OPT_VariantShatranj, OPT_VariantXiangqi, GPB_Variant, GPB_Board, IDC_Height,
\r
313 IDC_Width, IDC_Hand, IDC_Pieces, IDC_Def },
\r
314 { DLG_Fonts, IDOK, IDCANCEL, OPT_ChooseClockFont, OPT_ChooseMessageFont,
\r
315 OPT_ChooseCoordFont, OPT_ChooseTagFont, OPT_ChooseCommentsFont, OPT_ChooseConsoleFont, OPT_ChooseMoveHistoryFont, OPT_DefaultFonts,
\r
316 OPT_ClockFont, OPT_MessageFont, OPT_CoordFont, OPT_EditTagsFont,
\r
317 OPT_CommentsFont, OPT_MessageFont5, GPB_Current, GPB_All, OPT_MessageFont6 },
\r
318 { DLG_NewGameFRC, IDC_NFG_Label, IDC_NFG_Random, IDOK, IDCANCEL },
\r
319 { DLG_GameListOptions, IDC_GLT, IDC_GLT_Up, IDC_GLT_Down, IDC_GLT_Restore,
\r
320 IDC_GLT_Default, IDOK, IDCANCEL, IDC_GLT_RestoreTo },
\r
321 { DLG_MoveHistory },
\r
322 { DLG_EvalGraph },
\r
323 { DLG_EngineOutput, IDC_EngineLabel1, IDC_Engine1_NPS, IDC_EngineLabel2, IDC_Engine2_NPS },
\r
324 { DLG_Chat, IDC_Partner, IDC_Clear, IDC_Send, },
\r
325 { DLG_EnginePlayOptions, IDC_EpPonder, IDC_EpShowThinking, IDC_EpHideThinkingHuman,
\r
326 IDC_EpPeriodicUpdates, GPB_Adjudications, IDC_Draw, IDC_Moves, IDC_Threshold,
\r
327 IDC_Centi, IDC_TestClaims, IDC_DetectMates, IDC_MaterialDraws, IDC_TrivialDraws,
\r
328 GPB_Apply, IDC_Rule, IDC_Repeats, IDC_ScoreAbs1, IDC_ScoreAbs2, IDOK, IDCANCEL },
\r
329 { DLG_OptionsUCI, IDC_PolyDir, IDC_BrowseForPolyglotDir, IDC_Hash, IDC_Path,
\r
330 IDC_BrowseForEGTB, IDC_Cache, IDC_UseBook, IDC_BrowseForBook, IDC_CPU, IDC_OwnBook1,
\r
331 IDC_OwnBook2, IDC_Depth, IDC_Variation, IDC_DefGames, IDOK, IDCANCEL },
\r
335 static char languageBuf[50000], *foreign[1000], *english[1000], *languageFile[MSG_SIZ];
\r
336 static int lastChecked;
\r
337 static char oldLanguage[MSG_SIZ], *menuText[10][30];
\r
338 extern int tinyLayout;
\r
339 extern char * menuBarText[][10];
\r
342 LoadLanguageFile(char *name)
\r
343 { //load the file with translations, and make a list of the strings to be translated, and their translations
\r
345 int i=0, j=0, n=0, k;
\r
348 if(!name || name[0] == NULLCHAR) return;
\r
349 snprintf(buf, MSG_SIZ, "%s%s", name, strchr(name, '.') ? "" : ".lng"); // auto-append lng extension
\r
350 appData.language = oldLanguage;
\r
351 if(!strcmp(buf, oldLanguage)) { barbaric = 1; return; } // this language already loaded; just switch on
\r
352 if((f = fopen(buf, "r")) == NULL) return;
\r
353 while((k = fgetc(f)) != EOF) {
\r
354 if(i >= sizeof(languageBuf)) { DisplayError("Language file too big", 0); return; }
\r
355 languageBuf[i] = k;
\r
357 if(languageBuf[n] == '"' && languageBuf[i-1] == '"') {
\r
359 if(p = strstr(languageBuf + n + 1, "\" === \"")) {
\r
360 if(p > languageBuf+n+2 && p+8 < languageBuf+i) {
\r
361 if(j >= sizeof(english)) { DisplayError("Too many translated strings", 0); return; }
\r
362 english[j] = languageBuf + n + 1; *p = 0;
\r
363 foreign[j++] = p + 7; languageBuf[i-1] = 0;
\r
364 //if(appData.debugMode) fprintf(debugFP, "translation: replace '%s' by '%s'\n", english[j-1], foreign[j-1]);
\r
369 } else if(i > 0 && languageBuf[i-1] == '\\') {
\r
371 case 'n': k = '\n'; break;
\r
372 case 'r': k = '\r'; break;
\r
373 case 't': k = '\t'; break;
\r
375 languageBuf[--i] = k;
\r
380 barbaric = (j != 0);
\r
381 safeStrCpy(oldLanguage, buf, sizeof(oldLanguage)/sizeof(oldLanguage[0]) );
\r
386 { // return the translation of the given string
\r
387 // efficiency can be improved a lot...
\r
389 //if(appData.debugMode) fprintf(debugFP, "T_(%s)\n", s);
\r
390 if(!barbaric) return s;
\r
391 if(!s) return ""; // sanity
\r
392 while(english[i]) {
\r
393 if(!strcmp(s, english[i])) return foreign[i];
\r
400 Translate(HWND hDlg, int dialogID)
\r
401 { // translate all text items in the given dialog
\r
403 char buf[MSG_SIZ], *s;
\r
404 if(!barbaric) return;
\r
405 while(dialogItems[i][0] && dialogItems[i][0] != dialogID) i++; // find the dialog description
\r
406 if(dialogItems[i][0] != dialogID) return; // unknown dialog, should not happen
\r
407 GetWindowText( hDlg, buf, MSG_SIZ );
\r
409 if(strcmp(buf, s)) SetWindowText(hDlg, s); // replace by translated string (if different)
\r
410 for(j=1; k=dialogItems[i][j]; j++) { // translate all listed dialog items
\r
411 GetDlgItemText(hDlg, k, buf, MSG_SIZ);
\r
412 if(strlen(buf) == 0) continue;
\r
414 if(strcmp(buf, s)) SetDlgItemText(hDlg, k, s); // replace by translated string (if different)
\r
419 TranslateOneMenu(int i, HMENU subMenu)
\r
422 static MENUITEMINFO info;
\r
424 info.cbSize = sizeof(MENUITEMINFO);
\r
425 info.fMask = MIIM_STATE | MIIM_TYPE;
\r
426 for(j=GetMenuItemCount(subMenu)-1; j>=0; j--){
\r
428 info.dwTypeData = buf;
\r
429 info.cch = sizeof(buf);
\r
430 GetMenuItemInfo(subMenu, j, TRUE, &info);
\r
432 if(menuText[i][j]) safeStrCpy(buf, menuText[i][j], sizeof(buf)/sizeof(buf[0]) );
\r
433 else menuText[i][j] = strdup(buf); // remember original on first change
\r
435 if(buf[0] == NULLCHAR) continue;
\r
436 info.dwTypeData = T_(buf);
\r
437 info.cch = strlen(buf)+1;
\r
438 SetMenuItemInfo(subMenu, j, TRUE, &info);
\r
444 TranslateMenus(int addLanguage)
\r
447 WIN32_FIND_DATA fileData;
\r
449 #define IDM_English 1970
\r
451 HMENU mainMenu = GetMenu(hwndMain);
\r
452 for (i=GetMenuItemCount(mainMenu)-1; i>=0; i--) {
\r
453 HMENU subMenu = GetSubMenu(mainMenu, i);
\r
454 ModifyMenu(mainMenu, i, MF_STRING|MF_BYPOSITION|MF_POPUP|EnableMenuItem(mainMenu, i, MF_BYPOSITION),
\r
455 (UINT) subMenu, T_(menuBarText[tinyLayout][i]));
\r
456 TranslateOneMenu(i, subMenu);
\r
458 DrawMenuBar(hwndMain);
\r
461 if(!addLanguage) return;
\r
462 if((hFind = FindFirstFile("*.LNG", &fileData)) != INVALID_HANDLE_VALUE) {
\r
463 HMENU mainMenu = GetMenu(hwndMain);
\r
464 HMENU subMenu = GetSubMenu(mainMenu, GetMenuItemCount(mainMenu)-1);
\r
465 AppendMenu(subMenu, MF_SEPARATOR, (UINT_PTR) 0, NULL);
\r
466 AppendMenu(subMenu, MF_ENABLED|MF_STRING|(barbaric?MF_UNCHECKED:MF_CHECKED), (UINT_PTR) IDM_English, (LPCTSTR) "English");
\r
467 i = 0; lastChecked = IDM_English;
\r
469 char *p, *q = fileData.cFileName;
\r
470 int checkFlag = MF_UNCHECKED;
\r
471 languageFile[i] = strdup(q);
\r
472 if(barbaric && !strcmp(oldLanguage, q)) {
\r
473 checkFlag = MF_CHECKED;
\r
474 lastChecked = IDM_English + i + 1;
\r
475 CheckMenuItem(mainMenu, IDM_English, MF_BYCOMMAND|MF_UNCHECKED);
\r
477 *q = ToUpper(*q); while(*++q) *q = ToLower(*q);
\r
478 p = strstr(fileData.cFileName, ".lng");
\r
480 AppendMenu(subMenu, MF_ENABLED|MF_STRING|checkFlag, (UINT_PTR) IDM_English + ++i, (LPCTSTR) fileData.cFileName);
\r
481 } while(FindNextFile(hFind, &fileData));
\r
494 int cliWidth, cliHeight;
\r
497 SizeInfo sizeInfo[] =
\r
499 { "tiny", 21, 0, 1, 1, 0, 0 },
\r
500 { "teeny", 25, 1, 1, 1, 0, 0 },
\r
501 { "dinky", 29, 1, 1, 1, 0, 0 },
\r
502 { "petite", 33, 1, 1, 1, 0, 0 },
\r
503 { "slim", 37, 2, 1, 0, 0, 0 },
\r
504 { "small", 40, 2, 1, 0, 0, 0 },
\r
505 { "mediocre", 45, 2, 1, 0, 0, 0 },
\r
506 { "middling", 49, 2, 0, 0, 0, 0 },
\r
507 { "average", 54, 2, 0, 0, 0, 0 },
\r
508 { "moderate", 58, 3, 0, 0, 0, 0 },
\r
509 { "medium", 64, 3, 0, 0, 0, 0 },
\r
510 { "bulky", 72, 3, 0, 0, 0, 0 },
\r
511 { "large", 80, 3, 0, 0, 0, 0 },
\r
512 { "big", 87, 3, 0, 0, 0, 0 },
\r
513 { "huge", 95, 3, 0, 0, 0, 0 },
\r
514 { "giant", 108, 3, 0, 0, 0, 0 },
\r
515 { "colossal", 116, 4, 0, 0, 0, 0 },
\r
516 { "titanic", 129, 4, 0, 0, 0, 0 },
\r
517 { NULL, 0, 0, 0, 0, 0, 0 }
\r
520 #define MF(x) {x, {{0,}, 0. }, {0, }, 0}
\r
521 MyFont fontRec[NUM_SIZES][NUM_FONTS] =
\r
523 { 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
524 { 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
525 { 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
526 { 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
527 { 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
528 { 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
529 { 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
530 { 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
531 { 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
532 { 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
533 { 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
534 { 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
535 { 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
536 { 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
537 { 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
538 { 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
539 { 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
540 { 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
543 MyFont *font[NUM_SIZES][NUM_FONTS];
\r
552 #define BUTTON_WIDTH (tinyLayout ? 16 : 32)
\r
553 #define N_BUTTONS 5
\r
555 MyButtonDesc buttonDesc[N_BUTTONS] =
\r
557 {"<<", IDM_ToStart, NULL, NULL},
\r
558 {"<", IDM_Backward, NULL, NULL},
\r
559 {"P", IDM_Pause, NULL, NULL},
\r
560 {">", IDM_Forward, NULL, NULL},
\r
561 {">>", IDM_ToEnd, NULL, NULL},
\r
564 int tinyLayout = 0, smallLayout = 0;
\r
565 #define MENU_BAR_ITEMS 9
\r
566 char *menuBarText[2][MENU_BAR_ITEMS+1] = {
\r
567 { N_("&File"), N_("&Edit"), N_("&View"), N_("&Mode"), N_("&Action"), N_("E&ngine"), N_("&Options"), N_("&Help"), NULL },
\r
568 { N_("&F"), N_("&E"), N_("&V"), N_("&M"), N_("&A"), N_("&N"), N_("&O"), N_("&H"), NULL },
\r
572 MySound sounds[(int)NSoundClasses];
\r
573 MyTextAttribs textAttribs[(int)NColorClasses];
\r
575 MyColorizeAttribs colorizeAttribs[] = {
\r
576 { (COLORREF)0, 0, N_("Shout Text") },
\r
577 { (COLORREF)0, 0, N_("SShout/CShout") },
\r
578 { (COLORREF)0, 0, N_("Channel 1 Text") },
\r
579 { (COLORREF)0, 0, N_("Channel Text") },
\r
580 { (COLORREF)0, 0, N_("Kibitz Text") },
\r
581 { (COLORREF)0, 0, N_("Tell Text") },
\r
582 { (COLORREF)0, 0, N_("Challenge Text") },
\r
583 { (COLORREF)0, 0, N_("Request Text") },
\r
584 { (COLORREF)0, 0, N_("Seek Text") },
\r
585 { (COLORREF)0, 0, N_("Normal Text") },
\r
586 { (COLORREF)0, 0, N_("None") }
\r
591 static char *commentTitle;
\r
592 static char *commentText;
\r
593 static int commentIndex;
\r
594 static Boolean editComment = FALSE;
\r
597 char errorTitle[MSG_SIZ];
\r
598 char errorMessage[2*MSG_SIZ];
\r
599 HWND errorDialog = NULL;
\r
600 BOOLEAN moveErrorMessageUp = FALSE;
\r
601 BOOLEAN consoleEcho = TRUE;
\r
602 CHARFORMAT consoleCF;
\r
603 COLORREF consoleBackgroundColor;
\r
605 char *programVersion;
\r
611 typedef int CPKind;
\r
620 SOCKET sock2; /* stderr socket for OpenRcmd */
\r
623 #define INPUT_SOURCE_BUF_SIZE 4096
\r
625 typedef struct _InputSource {
\r
632 char buf[INPUT_SOURCE_BUF_SIZE];
\r
636 InputCallback func;
\r
637 struct _InputSource *second; /* for stderr thread on CPRcmd */
\r
641 InputSource *consoleInputSource;
\r
646 VOID ConsoleOutput(char* data, int length, int forceVisible);
\r
647 VOID ConsoleCreate();
\r
649 ConsoleWndProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam);
\r
650 VOID ColorizeTextPopup(HWND hwnd, ColorClass cc);
\r
651 VOID PrintCommSettings(FILE *f, char *name, DCB *dcb);
\r
652 VOID ParseCommSettings(char *arg, DCB *dcb);
\r
654 StartupDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam);
\r
655 VOID APIENTRY MenuPopup(HWND hwnd, POINT pt, HMENU hmenu, UINT def);
\r
656 void ParseIcsTextMenu(char *icsTextMenuString);
\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;
\r
3847 BITMAPINFOHEADER bih; int color[16], nrColors=0;
\r
3849 GetObject(bufferBitmap, sizeof(b), &b);
\r
3850 if(pData = malloc(b.bmWidthBytes*b.bmHeight + 10000)) {
\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
3918 SelectObject(tmphdc, oldBitmap);
\r
3920 /* Massive cleanup */
\r
3921 for (x = 0; x < num_clips; x++)
\r
3922 DeleteObject(clips[x]);
\r
3925 DeleteObject(bufferBitmap);
\r
3928 ReleaseDC(hwndMain, hdc);
\r
3930 if (lastDrawnFlipView != flipView && nr == 0) {
\r
3932 CheckMenuItem(GetMenu(hwndMain),IDM_FlipView, MF_BYCOMMAND|MF_CHECKED);
\r
3934 CheckMenuItem(GetMenu(hwndMain),IDM_FlipView, MF_BYCOMMAND|MF_UNCHECKED);
\r
3937 /* CopyBoard(lastDrawn, board);*/
\r
3938 lastDrawnHighlight = highlightInfo;
\r
3939 lastDrawnPremove = premoveHighlightInfo;
\r
3940 lastDrawnFlipView = flipView;
\r
3941 lastDrawnValid[nr] = 1;
\r
3944 /* [HGM] diag: Save the current board display to the given open file and close the file */
\r
3949 saveDiagFlag = 1; diagFile = f;
\r
3950 HDCDrawPosition(NULL, TRUE, NULL);
\r
3958 /*---------------------------------------------------------------------------*\
\r
3959 | CLIENT PAINT PROCEDURE
\r
3960 | This is the main event-handler for the WM_PAINT message.
\r
3962 \*---------------------------------------------------------------------------*/
\r
3964 PaintProc(HWND hwnd)
\r
3970 if((hdc = BeginPaint(hwnd, &ps))) {
\r
3971 if (IsIconic(hwnd)) {
\r
3972 DrawIcon(hdc, 2, 2, iconCurrent);
\r
3974 if (!appData.monoMode) {
\r
3975 SelectPalette(hdc, hPal, FALSE);
\r
3976 RealizePalette(hdc);
\r
3978 HDCDrawPosition(hdc, 1, NULL);
\r
3979 if(twoBoards) { // [HGM] dual: also redraw other board in other orientation
\r
3980 flipView = !flipView; partnerUp = !partnerUp;
\r
3981 HDCDrawPosition(hdc, 1, NULL);
\r
3982 flipView = !flipView; partnerUp = !partnerUp;
\r
3985 SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);
\r
3986 ExtTextOut(hdc, messageRect.left, messageRect.top,
\r
3987 ETO_CLIPPED|ETO_OPAQUE,
\r
3988 &messageRect, messageText, strlen(messageText), NULL);
\r
3989 SelectObject(hdc, oldFont);
\r
3990 DisplayBothClocks();
\r
3993 EndPaint(hwnd,&ps);
\r
4001 * If the user selects on a border boundary, return -1; if off the board,
\r
4002 * return -2. Otherwise map the event coordinate to the square.
\r
4003 * The offset boardRect.left or boardRect.top must already have been
\r
4004 * subtracted from x.
\r
4006 int EventToSquare(x, limit)
\r
4014 if ((x % (squareSize + lineGap)) >= squareSize)
\r
4016 x /= (squareSize + lineGap);
\r
4028 DropEnable dropEnables[] = {
\r
4029 { 'P', DP_Pawn, N_("Pawn") },
\r
4030 { 'N', DP_Knight, N_("Knight") },
\r
4031 { 'B', DP_Bishop, N_("Bishop") },
\r
4032 { 'R', DP_Rook, N_("Rook") },
\r
4033 { 'Q', DP_Queen, N_("Queen") },
\r
4037 SetupDropMenu(HMENU hmenu)
\r
4039 int i, count, enable;
\r
4041 extern char white_holding[], black_holding[];
\r
4042 char item[MSG_SIZ];
\r
4044 for (i=0; i<sizeof(dropEnables)/sizeof(DropEnable); i++) {
\r
4045 p = strchr(gameMode == IcsPlayingWhite ? white_holding : black_holding,
\r
4046 dropEnables[i].piece);
\r
4048 while (p && *p++ == dropEnables[i].piece) count++;
\r
4049 snprintf(item, MSG_SIZ, "%s %d", T_(dropEnables[i].name), count);
\r
4050 enable = count > 0 || !appData.testLegality
\r
4051 /*!!temp:*/ || (gameInfo.variant == VariantCrazyhouse
\r
4052 && !appData.icsActive);
\r
4053 ModifyMenu(hmenu, dropEnables[i].command,
\r
4054 MF_BYCOMMAND | (enable ? MF_ENABLED : MF_GRAYED) | MF_STRING,
\r
4055 dropEnables[i].command, item);
\r
4059 void DragPieceBegin(int x, int y)
\r
4061 dragInfo.lastpos.x = boardRect.left + x;
\r
4062 dragInfo.lastpos.y = boardRect.top + y;
\r
4063 dragInfo.from.x = fromX;
\r
4064 dragInfo.from.y = fromY;
\r
4065 dragInfo.piece = boards[currentMove][fromY][fromX];
\r
4066 dragInfo.start = dragInfo.from;
\r
4067 SetCapture(hwndMain);
\r
4070 void DragPieceEnd(int x, int y)
\r
4073 dragInfo.start.x = dragInfo.start.y = -1;
\r
4074 dragInfo.from = dragInfo.start;
\r
4075 dragInfo.pos = dragInfo.lastpos = dragInfo.start;
\r
4078 void ChangeDragPiece(ChessSquare piece)
\r
4080 dragInfo.piece = piece;
\r
4083 /* Event handler for mouse messages */
\r
4085 MouseEvent(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
\r
4089 static int recursive = 0;
\r
4091 BOOLEAN forceFullRepaint = IsFullRepaintPreferrable(); /* [AS] */
\r
4094 if (message == WM_MBUTTONUP) {
\r
4095 /* Hideous kludge to fool TrackPopupMenu into paying attention
\r
4096 to the middle button: we simulate pressing the left button too!
\r
4098 PostMessage(hwnd, WM_LBUTTONDOWN, wParam, lParam);
\r
4099 PostMessage(hwnd, WM_LBUTTONUP, wParam, lParam);
\r
4105 pt.x = LOWORD(lParam);
\r
4106 pt.y = HIWORD(lParam);
\r
4107 x = EventToSquare(pt.x - boardRect.left, BOARD_WIDTH);
\r
4108 y = EventToSquare(pt.y - boardRect.top, BOARD_HEIGHT);
\r
4109 if (!flipView && y >= 0) {
\r
4110 y = BOARD_HEIGHT - 1 - y;
\r
4112 if (flipView && x >= 0) {
\r
4113 x = BOARD_WIDTH - 1 - x;
\r
4116 shiftKey = GetKeyState(VK_SHIFT) < 0; // [HGM] remember last shift status
\r
4118 switch (message) {
\r
4119 case WM_LBUTTONDOWN:
\r
4120 if (PtInRect((LPRECT) &whiteRect, pt)) {
\r
4121 ClockClick(flipClock);
\r
4122 } else if (PtInRect((LPRECT) &blackRect, pt)) {
\r
4123 ClockClick(!flipClock);
\r
4125 dragInfo.start.x = dragInfo.start.y = -1;
\r
4126 dragInfo.from = dragInfo.start;
\r
4127 if(fromX == -1 && frozen) { // not sure where this is for
\r
4128 fromX = fromY = -1;
\r
4129 DrawPosition(forceFullRepaint || FALSE, NULL); /* [AS] */
\r
4132 LeftClick(Press, pt.x - boardRect.left, pt.y - boardRect.top);
\r
4133 DrawPosition(TRUE, NULL);
\r
4136 case WM_LBUTTONUP:
\r
4137 LeftClick(Release, pt.x - boardRect.left, pt.y - boardRect.top);
\r
4138 DrawPosition(TRUE, NULL);
\r
4141 case WM_MOUSEMOVE:
\r
4142 if(SeekGraphClick(Press, pt.x - boardRect.left, pt.y - boardRect.top, 1)) break;
\r
4143 if(PromoScroll(pt.x - boardRect.left, pt.y - boardRect.top)) break;
\r
4144 MovePV(pt.x - boardRect.left, pt.y - boardRect.top, boardRect.bottom - boardRect.top);
\r
4145 if ((appData.animateDragging || appData.highlightDragging)
\r
4146 && (wParam & MK_LBUTTON)
\r
4147 && dragInfo.from.x >= 0)
\r
4149 BOOL full_repaint = FALSE;
\r
4151 if (appData.animateDragging) {
\r
4152 dragInfo.pos = pt;
\r
4154 if (appData.highlightDragging) {
\r
4155 SetHighlights(fromX, fromY, x, y);
\r
4156 if( IsDrawArrowEnabled() && (x < 0 || x >= BOARD_WIDTH || y < 0 || y >= BOARD_HEIGHT) ) {
\r
4157 full_repaint = TRUE;
\r
4161 DrawPosition( full_repaint, NULL);
\r
4163 dragInfo.lastpos = dragInfo.pos;
\r
4167 case WM_MOUSEWHEEL: // [DM]
\r
4168 { static int lastDir = 0; // [HGM] build in some hysteresis to avoid spurious events
\r
4169 /* Mouse Wheel is being rolled forward
\r
4170 * Play moves forward
\r
4172 if((short)HIWORD(wParam) < 0 && currentMove < forwardMostMove)
\r
4173 { if(lastDir == 1) ForwardEvent(); else lastDir = 1; } // [HGM] suppress first event in direction
\r
4174 /* Mouse Wheel is being rolled backward
\r
4175 * Play moves backward
\r
4177 if((short)HIWORD(wParam) > 0 && currentMove > backwardMostMove)
\r
4178 { if(lastDir == -1) BackwardEvent(); else lastDir = -1; }
\r
4182 case WM_MBUTTONUP:
\r
4183 case WM_RBUTTONUP:
\r
4185 RightClick(Release, pt.x - boardRect.left, pt.y - boardRect.top, &fromX, &fromY);
\r
4188 case WM_MBUTTONDOWN:
\r
4189 case WM_RBUTTONDOWN:
\r
4192 fromX = fromY = -1;
\r
4193 dragInfo.pos.x = dragInfo.pos.y = -1;
\r
4194 dragInfo.start.x = dragInfo.start.y = -1;
\r
4195 dragInfo.from = dragInfo.start;
\r
4196 dragInfo.lastpos = dragInfo.pos;
\r
4197 if (appData.highlightDragging) {
\r
4198 ClearHighlights();
\r
4201 /* [HGM] right mouse button in clock area edit-game mode ups clock */
\r
4202 if (PtInRect((LPRECT) &whiteRect, pt)) {
\r
4203 if (gameMode == EditGame || GetKeyState(VK_SHIFT) < 0) AdjustClock(flipClock, 1);
\r
4204 } else if (PtInRect((LPRECT) &blackRect, pt)) {
\r
4205 if (gameMode == EditGame || GetKeyState(VK_SHIFT) < 0) AdjustClock(!flipClock, 1);
\r
4209 DrawPosition(TRUE, NULL);
\r
4211 menuNr = RightClick(Press, pt.x - boardRect.left, pt.y - boardRect.top, &fromX, &fromY);
\r
4214 if (message == WM_MBUTTONDOWN) {
\r
4215 buttonCount = 3; /* even if system didn't think so */
\r
4216 if (wParam & MK_SHIFT)
\r
4217 MenuPopup(hwnd, pt, LoadMenu(hInst, "BlackPieceMenu"), -1);
\r
4219 MenuPopup(hwnd, pt, LoadMenu(hInst, "WhitePieceMenu"), -1);
\r
4220 } else { /* message == WM_RBUTTONDOWN */
\r
4221 /* Just have one menu, on the right button. Windows users don't
\r
4222 think to try the middle one, and sometimes other software steals
\r
4223 it, or it doesn't really exist. */
\r
4224 if(gameInfo.variant != VariantShogi)
\r
4225 MenuPopup(hwnd, pt, LoadMenu(hInst, "PieceMenu"), -1);
\r
4227 MenuPopup(hwnd, pt, LoadMenu(hInst, "ShogiPieceMenu"), -1);
\r
4231 SetCapture(hwndMain);
4234 hmenu = LoadMenu(hInst, "DropPieceMenu");
\r
4235 SetupDropMenu(hmenu);
\r
4236 MenuPopup(hwnd, pt, hmenu, -1);
\r
4246 /* Preprocess messages for buttons in main window */
\r
4248 ButtonProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
\r
4250 int id = GetWindowLongPtr(hwnd, GWLP_ID);
\r
4253 for (i=0; i<N_BUTTONS; i++) {
\r
4254 if (buttonDesc[i].id == id) break;
\r
4256 if (i == N_BUTTONS) return 0;
\r
4257 switch (message) {
\r
4262 dir = (wParam == VK_LEFT) ? -1 : 1;
\r
4263 SetFocus(buttonDesc[(i + dir + N_BUTTONS) % N_BUTTONS].hwnd);
\r
4270 SendMessage(hwndMain, WM_COMMAND, MAKEWPARAM(buttonDesc[i].id, 0), 0);
\r
4273 if (appData.icsActive && (isalpha((char)wParam) || wParam == '0')) {
\r
4274 // [HGM] movenum: only letters or leading zero should go to ICS input
\r
4275 HWND h = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
4276 if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);
\r
4278 SendMessage(h, WM_CHAR, wParam, lParam);
\r
4280 } else if (isalpha((char)wParam) || isdigit((char)wParam)){
\r
4281 TypeInEvent((char)wParam);
\r
4287 return CallWindowProc(buttonDesc[i].wndproc, hwnd, message, wParam, lParam);
\r
4290 /* Process messages for Promotion dialog box */
\r
4292 Promotion(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
4296 switch (message) {
\r
4297 case WM_INITDIALOG: /* message: initialize dialog box */
\r
4298 /* Center the dialog over the application window */
\r
4299 CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));
\r
4300 Translate(hDlg, DLG_PromotionKing);
\r
4301 ShowWindow(GetDlgItem(hDlg, PB_King),
\r
4302 (!appData.testLegality || gameInfo.variant == VariantSuicide ||
\r
4303 gameInfo.variant == VariantSpartan && !WhiteOnMove(currentMove) ||
\r
4304 gameInfo.variant == VariantGiveaway || gameInfo.variant == VariantSuper ) ?
\r
4305 SW_SHOW : SW_HIDE);
\r
4306 /* [HGM] Only allow C & A promotions if these pieces are defined */
\r
4307 ShowWindow(GetDlgItem(hDlg, PB_Archbishop),
\r
4308 ((PieceToChar(WhiteAngel) >= 'A' && WhiteOnMove(currentMove) &&
\r
4309 PieceToChar(WhiteAngel) != '~') ||
\r
4310 (PieceToChar(BlackAngel) >= 'A' && !WhiteOnMove(currentMove) &&
\r
4311 PieceToChar(BlackAngel) != '~') ) ?
\r
4312 SW_SHOW : SW_HIDE);
\r
4313 ShowWindow(GetDlgItem(hDlg, PB_Chancellor),
\r
4314 ((PieceToChar(WhiteMarshall) >= 'A' && WhiteOnMove(currentMove) &&
\r
4315 PieceToChar(WhiteMarshall) != '~') ||
\r
4316 (PieceToChar(BlackMarshall) >= 'A' && !WhiteOnMove(currentMove) &&
\r
4317 PieceToChar(BlackMarshall) != '~') ) ?
\r
4318 SW_SHOW : SW_HIDE);
\r
4319 /* [HGM] Hide B & R button in Shogi, use Q as promote, N as defer */
\r
4320 ShowWindow(GetDlgItem(hDlg, PB_Rook),
\r
4321 gameInfo.variant != VariantShogi ?
\r
4322 SW_SHOW : SW_HIDE);
\r
4323 ShowWindow(GetDlgItem(hDlg, PB_Bishop),
\r
4324 gameInfo.variant != VariantShogi ?
\r
4325 SW_SHOW : SW_HIDE);
\r
4326 if(gameInfo.variant == VariantShogi) {
\r
4327 SetDlgItemText(hDlg, PB_Queen, "YES");
\r
4328 SetDlgItemText(hDlg, PB_Knight, "NO");
\r
4329 SetWindowText(hDlg, "Promote?");
\r
4331 ShowWindow(GetDlgItem(hDlg, IDC_Centaur),
\r
4332 gameInfo.variant == VariantSuper ?
\r
4333 SW_SHOW : SW_HIDE);
\r
4336 case WM_COMMAND: /* message: received a command */
\r
4337 switch (LOWORD(wParam)) {
\r
4339 EndDialog(hDlg, TRUE); /* Exit the dialog */
\r
4340 ClearHighlights();
\r
4341 DrawPosition(FALSE, NULL);
\r
4344 promoChar = gameInfo.variant == VariantSuper ? PieceToChar(BlackSilver) : PieceToChar(BlackKing);
\r
4347 promoChar = gameInfo.variant == VariantShogi ? '+' : ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteQueen : BlackQueen));
\r
4350 promoChar = ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteRook : BlackRook));
\r
4351 if(gameInfo.variant == VariantSpartan && !WhiteOnMove(currentMove)) promoChar = PieceToChar(BlackDragon);
\r
4354 promoChar = ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteBishop : BlackBishop));
\r
4355 if(gameInfo.variant == VariantSpartan && !WhiteOnMove(currentMove)) promoChar = PieceToChar(BlackAlfil);
\r
4357 case PB_Chancellor:
\r
4358 promoChar = ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteMarshall : BlackMarshall));
\r
4360 case PB_Archbishop:
\r
4361 promoChar = ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteAngel : BlackAngel));
\r
4364 promoChar = gameInfo.variant == VariantShogi ? '=' : PieceToChar(WhiteOnMove(currentMove) ? WhiteKnight : BlackKnight);
\r
4369 if(promoChar == '.') return FALSE; // invalid piece chosen
\r
4370 EndDialog(hDlg, TRUE); /* Exit the dialog */
\r
4371 UserMoveEvent(fromX, fromY, toX, toY, promoChar);
\r
4372 fromX = fromY = -1;
\r
4373 if (!appData.highlightLastMove) {
\r
4374 ClearHighlights();
\r
4375 DrawPosition(FALSE, NULL);
\r
4382 /* Pop up promotion dialog */
\r
4384 PromotionPopup(HWND hwnd)
\r
4388 lpProc = MakeProcInstance((FARPROC)Promotion, hInst);
\r
4389 DialogBox(hInst, MAKEINTRESOURCE(DLG_PromotionKing),
\r
4390 hwnd, (DLGPROC)lpProc);
\r
4391 FreeProcInstance(lpProc);
\r
4397 DrawPosition(TRUE, NULL);
\r
4398 PromotionPopup(hwndMain);
\r
4401 /* Toggle ShowThinking */
\r
4403 ToggleShowThinking()
\r
4405 appData.showThinking = !appData.showThinking;
\r
4406 ShowThinkingEvent();
\r
4410 LoadGameDialog(HWND hwnd, char* title)
\r
4414 char fileTitle[MSG_SIZ];
\r
4415 f = OpenFileDialog(hwnd, "rb", "",
\r
4416 appData.oldSaveStyle ? "gam" : "pgn",
\r
4418 title, &number, fileTitle, NULL);
\r
4420 cmailMsgLoaded = FALSE;
\r
4421 if (number == 0) {
\r
4422 int error = GameListBuild(f);
\r
4424 DisplayError(_("Cannot build game list"), error);
\r
4425 } else if (!ListEmpty(&gameList) &&
\r
4426 ((ListGame *) gameList.tailPred)->number > 1) {
\r
4427 GameListPopUp(f, fileTitle);
\r
4430 GameListDestroy();
\r
4433 LoadGame(f, number, fileTitle, FALSE);
\r
4437 int get_term_width()
\r
4442 HFONT hfont, hold_font;
\r
4447 hText = GetDlgItem(hwndConsole, OPT_ConsoleText);
\r
4451 // get the text metrics
\r
4452 hdc = GetDC(hText);
\r
4453 lf = font[boardSize][CONSOLE_FONT]->lf;
\r
4454 if (consoleCF.dwEffects & CFE_BOLD)
\r
4455 lf.lfWeight = FW_BOLD;
\r
4456 if (consoleCF.dwEffects & CFE_ITALIC)
\r
4457 lf.lfItalic = TRUE;
\r
4458 if (consoleCF.dwEffects & CFE_STRIKEOUT)
\r
4459 lf.lfStrikeOut = TRUE;
\r
4460 if (consoleCF.dwEffects & CFE_UNDERLINE)
\r
4461 lf.lfUnderline = TRUE;
\r
4462 hfont = CreateFontIndirect(&lf);
\r
4463 hold_font = SelectObject(hdc, hfont);
\r
4464 GetTextMetrics(hdc, &tm);
\r
4465 SelectObject(hdc, hold_font);
\r
4466 DeleteObject(hfont);
\r
4467 ReleaseDC(hText, hdc);
\r
4469 // get the rectangle
\r
4470 SendMessage(hText, EM_GETRECT, 0, (LPARAM)&rc);
\r
4472 return (rc.right-rc.left) / tm.tmAveCharWidth;
\r
4475 void UpdateICSWidth(HWND hText)
\r
4477 LONG old_width, new_width;
\r
4479 new_width = get_term_width(hText, FALSE);
\r
4480 old_width = GetWindowLongPtr(hText, GWLP_USERDATA);
\r
4481 if (new_width != old_width)
\r
4483 ics_update_width(new_width);
\r
4484 SetWindowLongPtr(hText, GWLP_USERDATA, new_width);
\r
4489 ChangedConsoleFont()
\r
4492 CHARRANGE tmpsel, sel;
\r
4493 MyFont *f = font[boardSize][CONSOLE_FONT];
\r
4494 HWND hText = GetDlgItem(hwndConsole, OPT_ConsoleText);
\r
4495 HWND hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
4498 cfmt.cbSize = sizeof(CHARFORMAT);
\r
4499 cfmt.dwMask = CFM_FACE|CFM_SIZE|CFM_CHARSET;
\r
4500 safeStrCpy(cfmt.szFaceName, font[boardSize][CONSOLE_FONT]->mfp.faceName,
\r
4501 sizeof(cfmt.szFaceName)/sizeof(cfmt.szFaceName[0]) );
\r
4502 /* yHeight is expressed in twips. A twip is 1/20 of a font's point
\r
4503 * size. This was undocumented in the version of MSVC++ that I had
\r
4504 * when I wrote the code, but is apparently documented now.
\r
4506 cfmt.yHeight = (int)(f->mfp.pointSize * 20.0 + 0.5);
\r
4507 cfmt.bCharSet = f->lf.lfCharSet;
\r
4508 cfmt.bPitchAndFamily = f->lf.lfPitchAndFamily;
\r
4509 SendMessage(hText, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &cfmt);
\r
4510 SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &cfmt);
\r
4511 /* Why are the following seemingly needed too? */
\r
4512 SendMessage(hText, EM_SETCHARFORMAT, SCF_DEFAULT, (LPARAM) &cfmt);
\r
4513 SendMessage(hInput, EM_SETCHARFORMAT, SCF_DEFAULT, (LPARAM) &cfmt);
\r
4514 SendMessage(hText, EM_EXGETSEL, 0, (LPARAM)&sel);
\r
4516 tmpsel.cpMax = -1; /*999999?*/
\r
4517 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&tmpsel);
\r
4518 SendMessage(hText, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cfmt);
\r
4519 /* Trying putting this here too. It still seems to tickle a RichEdit
\r
4520 * bug: sometimes RichEdit indents the first line of a paragraph too.
\r
4522 paraf.cbSize = sizeof(paraf);
\r
4523 paraf.dwMask = PFM_OFFSET | PFM_STARTINDENT;
\r
4524 paraf.dxStartIndent = 0;
\r
4525 paraf.dxOffset = WRAP_INDENT;
\r
4526 SendMessage(hText, EM_SETPARAFORMAT, 0, (LPARAM) ¶f);
\r
4527 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
4528 UpdateICSWidth(hText);
\r
4531 /*---------------------------------------------------------------------------*\
\r
4533 * Window Proc for main window
\r
4535 \*---------------------------------------------------------------------------*/
\r
4537 /* Process messages for main window, etc. */
\r
4539 WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
\r
4542 int wmId, wmEvent;
\r
4546 char fileTitle[MSG_SIZ];
\r
4547 char buf[MSG_SIZ];
\r
4548 static SnapData sd;
\r
4550 switch (message) {
\r
4552 case WM_PAINT: /* message: repaint portion of window */
\r
4556 case WM_ERASEBKGND:
\r
4557 if (IsIconic(hwnd)) {
\r
4558 /* Cheat; change the message */
\r
4559 return (DefWindowProc(hwnd, WM_ICONERASEBKGND, wParam, lParam));
\r
4561 return (DefWindowProc(hwnd, message, wParam, lParam));
\r
4565 case WM_LBUTTONDOWN:
\r
4566 case WM_MBUTTONDOWN:
\r
4567 case WM_RBUTTONDOWN:
\r
4568 case WM_LBUTTONUP:
\r
4569 case WM_MBUTTONUP:
\r
4570 case WM_RBUTTONUP:
\r
4571 case WM_MOUSEMOVE:
\r
4572 case WM_MOUSEWHEEL:
\r
4573 MouseEvent(hwnd, message, wParam, lParam);
\r
4576 JAWS_KB_NAVIGATION
\r
4580 JAWS_ALT_INTERCEPT
\r
4582 if (appData.icsActive && ((char)wParam == '\r' || (char)wParam > ' ' && !((char)wParam >= '1' && (char)wParam <= '9'))) {
\r
4583 // [HGM] movenum: for non-zero digits we always do type-in dialog
\r
4584 HWND h = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
4585 if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);
\r
4587 SendMessage(h, message, wParam, lParam);
\r
4588 } else if(lParam != KF_REPEAT) {
\r
4589 if (isalpha((char)wParam) || isdigit((char)wParam)) {
\r
4590 TypeInEvent((char)wParam);
\r
4591 } else if((char)wParam == 003) CopyGameToClipboard();
\r
4592 else if((char)wParam == 026) PasteGameOrFENFromClipboard();
\r
4597 case WM_PALETTECHANGED:
\r
4598 if (hwnd != (HWND)wParam && !appData.monoMode) {
\r
4600 HDC hdc = GetDC(hwndMain);
\r
4601 SelectPalette(hdc, hPal, TRUE);
\r
4602 nnew = RealizePalette(hdc);
\r
4604 paletteChanged = TRUE;
\r
4605 InvalidateRect(hwnd, &boardRect, FALSE);
\r
4607 ReleaseDC(hwnd, hdc);
\r
4611 case WM_QUERYNEWPALETTE:
\r
4612 if (!appData.monoMode /*&& paletteChanged*/) {
\r
4614 HDC hdc = GetDC(hwndMain);
\r
4615 paletteChanged = FALSE;
\r
4616 SelectPalette(hdc, hPal, FALSE);
\r
4617 nnew = RealizePalette(hdc);
\r
4619 InvalidateRect(hwnd, &boardRect, FALSE);
\r
4621 ReleaseDC(hwnd, hdc);
\r
4626 case WM_COMMAND: /* message: command from application menu */
\r
4627 wmId = LOWORD(wParam);
\r
4628 wmEvent = HIWORD(wParam);
\r
4633 SAY("new game enter a move to play against the computer with white");
\r
4636 case IDM_NewGameFRC:
\r
4637 if( NewGameFRC() == 0 ) {
\r
4642 case IDM_NewVariant:
\r
4643 NewVariantPopup(hwnd);
\r
4646 case IDM_LoadGame:
\r
4647 LoadGameDialog(hwnd, _("Load Game from File"));
\r
4650 case IDM_LoadNextGame:
\r
4654 case IDM_LoadPrevGame:
\r
4658 case IDM_ReloadGame:
\r
4662 case IDM_LoadPosition:
\r
4663 if (gameMode == AnalyzeMode || gameMode == AnalyzeFile) {
\r
4664 Reset(FALSE, TRUE);
\r
4667 f = OpenFileDialog(hwnd, "rb", "",
\r
4668 appData.oldSaveStyle ? "pos" : "fen",
\r
4670 _("Load Position from File"), &number, fileTitle, NULL);
\r
4672 LoadPosition(f, number, fileTitle);
\r
4676 case IDM_LoadNextPosition:
\r
4677 ReloadPosition(1);
\r
4680 case IDM_LoadPrevPosition:
\r
4681 ReloadPosition(-1);
\r
4684 case IDM_ReloadPosition:
\r
4685 ReloadPosition(0);
\r
4688 case IDM_SaveGame:
\r
4689 defName = DefaultFileName(appData.oldSaveStyle ? "gam" : "pgn");
\r
4690 f = OpenFileDialog(hwnd, "a", defName,
\r
4691 appData.oldSaveStyle ? "gam" : "pgn",
\r
4693 _("Save Game to File"), NULL, fileTitle, NULL);
\r
4695 SaveGame(f, 0, "");
\r
4699 case IDM_SavePosition:
\r
4700 defName = DefaultFileName(appData.oldSaveStyle ? "pos" : "fen");
\r
4701 f = OpenFileDialog(hwnd, "a", defName,
\r
4702 appData.oldSaveStyle ? "pos" : "fen",
\r
4704 _("Save Position to File"), NULL, fileTitle, NULL);
\r
4706 SavePosition(f, 0, "");
\r
4710 case IDM_SaveDiagram:
\r
4711 defName = "diagram";
\r
4712 f = OpenFileDialog(hwnd, "wb", defName,
\r
4715 _("Save Diagram to File"), NULL, fileTitle, NULL);
\r
4721 case IDM_CopyGame:
\r
4722 CopyGameToClipboard();
\r
4725 case IDM_PasteGame:
\r
4726 PasteGameFromClipboard();
\r
4729 case IDM_CopyGameListToClipboard:
\r
4730 CopyGameListToClipboard();
\r
4733 /* [AS] Autodetect FEN or PGN data */
\r
4734 case IDM_PasteAny:
\r
4735 PasteGameOrFENFromClipboard();
\r
4738 /* [AS] Move history */
\r
4739 case IDM_ShowMoveHistory:
\r
4740 if( MoveHistoryIsUp() ) {
\r
4741 MoveHistoryPopDown();
\r
4744 MoveHistoryPopUp();
\r
4748 /* [AS] Eval graph */
\r
4749 case IDM_ShowEvalGraph:
\r
4750 if( EvalGraphIsUp() ) {
\r
4751 EvalGraphPopDown();
\r
4755 SetFocus(hwndMain);
\r
4759 /* [AS] Engine output */
\r
4760 case IDM_ShowEngineOutput:
\r
4761 if( EngineOutputIsUp() ) {
\r
4762 EngineOutputPopDown();
\r
4765 EngineOutputPopUp();
\r
4769 /* [AS] User adjudication */
\r
4770 case IDM_UserAdjudication_White:
\r
4771 UserAdjudicationEvent( +1 );
\r
4774 case IDM_UserAdjudication_Black:
\r
4775 UserAdjudicationEvent( -1 );
\r
4778 case IDM_UserAdjudication_Draw:
\r
4779 UserAdjudicationEvent( 0 );
\r
4782 /* [AS] Game list options dialog */
\r
4783 case IDM_GameListOptions:
\r
4784 GameListOptions();
\r
4791 case IDM_CopyPosition:
\r
4792 CopyFENToClipboard();
\r
4795 case IDM_PastePosition:
\r
4796 PasteFENFromClipboard();
\r
4799 case IDM_MailMove:
\r
4803 case IDM_ReloadCMailMsg:
\r
4804 Reset(TRUE, TRUE);
\r
4805 ReloadCmailMsgEvent(FALSE);
\r
4808 case IDM_Minimize:
\r
4809 ShowWindow(hwnd, SW_MINIMIZE);
\r
4816 case IDM_MachineWhite:
\r
4817 MachineWhiteEvent();
\r
4819 * refresh the tags dialog only if it's visible
\r
4821 if (gameMode == MachinePlaysWhite && IsWindowVisible(editTagsDialog)) {
\r
4823 tags = PGNTags(&gameInfo);
\r
4824 TagsPopUp(tags, CmailMsg());
\r
4827 SAY("computer starts playing white");
\r
4830 case IDM_MachineBlack:
\r
4831 MachineBlackEvent();
\r
4833 * refresh the tags dialog only if it's visible
\r
4835 if (gameMode == MachinePlaysBlack && IsWindowVisible(editTagsDialog)) {
\r
4837 tags = PGNTags(&gameInfo);
\r
4838 TagsPopUp(tags, CmailMsg());
\r
4841 SAY("computer starts playing black");
\r
4844 case IDM_Match: // [HGM] match: flows into next case, after setting Match Mode and nr of Games
\r
4845 if(gameMode != BeginningOfGame) { // allow menu item to remain enabled for better mode highligting
\r
4846 DisplayError(_("You can only start a match from the initial position."), 0); break;
\r
4848 appData.matchGames = appData.defaultMatchGames;
4849 MatchEvent(2); // distinguish from command-line-triggered case (matchMode=1)
\r
4852 case IDM_TwoMachines:
\r
4853 TwoMachinesEvent();
\r
4855 * refresh the tags dialog only if it's visible
\r
4857 if (gameMode == TwoMachinesPlay && IsWindowVisible(editTagsDialog)) {
\r
4859 tags = PGNTags(&gameInfo);
\r
4860 TagsPopUp(tags, CmailMsg());
\r
4863 SAY("computer starts playing both sides");
\r
4866 case IDM_AnalysisMode:
\r
4867 if (!first.analysisSupport) {
\r
4868 snprintf(buf, MSG_SIZ, _("%s does not support analysis"), first.tidy);
\r
4869 DisplayError(buf, 0);
\r
4871 SAY("analyzing current position");
\r
4872 /* [DM] icsEngineAnlyze [HGM] Why is this front-end??? */
\r
4873 if (appData.icsActive) {
\r
4874 if (gameMode != IcsObserving) {
\r
4875 snprintf(buf, MSG_SIZ, "You are not observing a game");
\r
4876 DisplayError(buf, 0);
\r
4877 /* secure check */
\r
4878 if (appData.icsEngineAnalyze) {
\r
4879 if (appData.debugMode)
\r
4880 fprintf(debugFP, "Found unexpected active ICS engine analyze \n");
\r
4881 ExitAnalyzeMode();
\r
4887 /* if enable, user want disable icsEngineAnalyze */
\r
4888 if (appData.icsEngineAnalyze) {
\r
4889 ExitAnalyzeMode();
\r
4893 appData.icsEngineAnalyze = TRUE;
\r
4894 if (appData.debugMode) fprintf(debugFP, "ICS engine analyze starting...\n");
\r
4897 if (!appData.showThinking) ToggleShowThinking();
\r
4898 AnalyzeModeEvent();
\r
4902 case IDM_AnalyzeFile:
\r
4903 if (!first.analysisSupport) {
\r
4904 char buf[MSG_SIZ];
\r
4905 snprintf(buf, MSG_SIZ, _("%s does not support analysis"), first.tidy);
\r
4906 DisplayError(buf, 0);
\r
4908 if (!appData.showThinking) ToggleShowThinking();
\r
4909 AnalyzeFileEvent();
\r
4910 LoadGameDialog(hwnd, _("Analyze Game from File"));
\r
4911 AnalysisPeriodicEvent(1);
\r
4915 case IDM_IcsClient:
\r
4919 case IDM_EditGame:
\r
4920 case IDM_EditGame2:
\r
4925 case IDM_EditPosition:
\r
4926 case IDM_EditPosition2:
\r
4927 EditPositionEvent();
\r
4928 SAY("enter a FEN string or setup a position on the board using the control R pop up menu");
\r
4931 case IDM_Training:
\r
4935 case IDM_ShowGameList:
\r
4936 ShowGameListProc();
\r
4939 case IDM_EditProgs1:
\r
4940 EditTagsPopUp(firstChessProgramNames, &firstChessProgramNames);
\r
4943 case IDM_EditProgs2:
\r
4944 EditTagsPopUp(secondChessProgramNames, &secondChessProgramNames);
\r
4947 case IDM_EditServers:
\r
4948 EditTagsPopUp(icsNames, &icsNames);
\r
4951 case IDM_EditTags:
\r
4956 case IDM_EditComment:
\r
4958 if (commentUp && editComment) {
\r
4961 EditCommentEvent();
\r
4981 case IDM_CallFlag:
\r
5001 case IDM_StopObserving:
\r
5002 StopObservingEvent();
\r
5005 case IDM_StopExamining:
\r
5006 StopExaminingEvent();
\r
5010 UploadGameEvent();
\r
5013 case IDM_TypeInMove:
\r
5014 TypeInEvent('\000');
\r
5017 case IDM_TypeInName:
\r
5018 PopUpNameDialog('\000');
\r
5021 case IDM_Backward:
\r
5023 SetFocus(hwndMain);
\r
5030 SetFocus(hwndMain);
\r
5035 SetFocus(hwndMain);
\r
5040 SetFocus(hwndMain);
\r
5044 RevertEvent(FALSE);
\r
5047 case IDM_Annotate: // [HGM] vari: revert with annotation
\r
5048 RevertEvent(TRUE);
\r
5051 case IDM_TruncateGame:
\r
5052 TruncateGameEvent();
\r
5059 case IDM_RetractMove:
\r
5060 RetractMoveEvent();
\r
5063 case IDM_FlipView:
\r
5064 flipView = !flipView;
\r
5065 DrawPosition(FALSE, NULL);
\r
5068 case IDM_FlipClock:
\r
5069 flipClock = !flipClock;
\r
5070 DisplayBothClocks();
\r
5074 case IDM_MuteSounds:
\r
5075 mute = !mute; // [HGM] mute: keep track of global muting variable
\r
5076 CheckMenuItem(GetMenu(hwndMain),IDM_MuteSounds,
\r
5077 MF_BYCOMMAND|(mute?MF_CHECKED:MF_UNCHECKED));
\r
5080 case IDM_GeneralOptions:
\r
5081 GeneralOptionsPopup(hwnd);
\r
5082 DrawPosition(TRUE, NULL);
\r
5085 case IDM_BoardOptions:
\r
5086 BoardOptionsPopup(hwnd);
\r
5089 case IDM_EnginePlayOptions:
\r
5090 EnginePlayOptionsPopup(hwnd);
\r
5093 case IDM_Engine1Options:
\r
5094 EngineOptionsPopup(hwnd, &first);
\r
5097 case IDM_Engine2Options:
\r
5099 if(WaitForEngine(&second, SettingsMenuIfReady)) break;
\r
5100 EngineOptionsPopup(hwnd, &second);
\r
5103 case IDM_OptionsUCI:
\r
5104 UciOptionsPopup(hwnd);
\r
5107 case IDM_IcsOptions:
\r
5108 IcsOptionsPopup(hwnd);
\r
5112 FontsOptionsPopup(hwnd);
\r
5116 SoundOptionsPopup(hwnd);
\r
5119 case IDM_CommPort:
\r
5120 CommPortOptionsPopup(hwnd);
\r
5123 case IDM_LoadOptions:
\r
5124 LoadOptionsPopup(hwnd);
\r
5127 case IDM_SaveOptions:
\r
5128 SaveOptionsPopup(hwnd);
\r
5131 case IDM_TimeControl:
\r
5132 TimeControlOptionsPopup(hwnd);
\r
5135 case IDM_SaveSettings:
\r
5136 SaveSettings(settingsFileName);
\r
5139 case IDM_SaveSettingsOnExit:
\r
5140 saveSettingsOnExit = !saveSettingsOnExit;
\r
5141 (void) CheckMenuItem(GetMenu(hwndMain), IDM_SaveSettingsOnExit,
\r
5142 MF_BYCOMMAND|(saveSettingsOnExit ?
\r
5143 MF_CHECKED : MF_UNCHECKED));
\r
5154 case IDM_AboutGame:
\r
5159 appData.debugMode = !appData.debugMode;
\r
5160 if (appData.debugMode) {
\r
5161 char dir[MSG_SIZ];
\r
5162 GetCurrentDirectory(MSG_SIZ, dir);
\r
5163 SetCurrentDirectory(installDir);
\r
5164 debugFP = fopen(appData.nameOfDebugFile, "w");
\r
5165 SetCurrentDirectory(dir);
\r
5166 setbuf(debugFP, NULL);
\r
5173 case IDM_HELPCONTENTS:
\r
5174 if (!MyHelp (hwnd, "winboard.hlp", HELP_KEY,(DWORD)(LPSTR)"CONTENTS") &&
\r
5175 !HtmlHelp(hwnd, "winboard.chm", 0, 0) ) {
\r
5176 MessageBox (GetFocus(),
\r
5177 _("Unable to activate help"),
\r
5178 szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);
\r
5182 case IDM_HELPSEARCH:
\r
5183 if (!MyHelp (hwnd, "winboard.hlp", HELP_PARTIALKEY, (DWORD)(LPSTR)"") &&
\r
5184 !HtmlHelp(hwnd, "winboard.chm", 0, 0) ) {
\r
5185 MessageBox (GetFocus(),
\r
5186 _("Unable to activate help"),
\r
5187 szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);
\r
5191 case IDM_HELPHELP:
\r
5192 if(!WinHelp(hwnd, (LPSTR)NULL, HELP_HELPONHELP, 0)) {
\r
5193 MessageBox (GetFocus(),
\r
5194 _("Unable to activate help"),
\r
5195 szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);
\r
5200 lpProc = MakeProcInstance((FARPROC)About, hInst);
\r
5202 (gameInfo.event && strcmp(gameInfo.event, "Easter Egg Hunt") == 0) ?
\r
5203 "AboutBox2" : "AboutBox", hwnd, (DLGPROC)lpProc);
\r
5204 FreeProcInstance(lpProc);
\r
5207 case IDM_DirectCommand1:
\r
5208 AskQuestionEvent(_("Direct Command"),
\r
5209 _("Send to chess program:"), "", "1");
\r
5211 case IDM_DirectCommand2:
\r
5212 AskQuestionEvent(_("Direct Command"),
\r
5213 _("Send to second chess program:"), "", "2");
\r
5216 case EP_WhitePawn:
\r
5217 EditPositionMenuEvent(WhitePawn, fromX, fromY);
\r
5218 fromX = fromY = -1;
\r
5221 case EP_WhiteKnight:
\r
5222 EditPositionMenuEvent(WhiteKnight, fromX, fromY);
\r
5223 fromX = fromY = -1;
\r
5226 case EP_WhiteBishop:
\r
5227 EditPositionMenuEvent(WhiteBishop, fromX, fromY);
\r
5228 fromX = fromY = -1;
\r
5231 case EP_WhiteRook:
\r
5232 EditPositionMenuEvent(WhiteRook, fromX, fromY);
\r
5233 fromX = fromY = -1;
\r
5236 case EP_WhiteQueen:
\r
5237 EditPositionMenuEvent(WhiteQueen, fromX, fromY);
\r
5238 fromX = fromY = -1;
\r
5241 case EP_WhiteFerz:
\r
5242 EditPositionMenuEvent(WhiteFerz, fromX, fromY);
\r
5243 fromX = fromY = -1;
\r
5246 case EP_WhiteWazir:
\r
5247 EditPositionMenuEvent(WhiteWazir, fromX, fromY);
\r
5248 fromX = fromY = -1;
\r
5251 case EP_WhiteAlfil:
\r
5252 EditPositionMenuEvent(WhiteAlfil, fromX, fromY);
\r
5253 fromX = fromY = -1;
\r
5256 case EP_WhiteCannon:
\r
5257 EditPositionMenuEvent(WhiteCannon, fromX, fromY);
\r
5258 fromX = fromY = -1;
\r
5261 case EP_WhiteCardinal:
\r
5262 EditPositionMenuEvent(WhiteAngel, fromX, fromY);
\r
5263 fromX = fromY = -1;
\r
5266 case EP_WhiteMarshall:
\r
5267 EditPositionMenuEvent(WhiteMarshall, fromX, fromY);
\r
5268 fromX = fromY = -1;
\r
5271 case EP_WhiteKing:
\r
5272 EditPositionMenuEvent(WhiteKing, fromX, fromY);
\r
5273 fromX = fromY = -1;
\r
5276 case EP_BlackPawn:
\r
5277 EditPositionMenuEvent(BlackPawn, fromX, fromY);
\r
5278 fromX = fromY = -1;
\r
5281 case EP_BlackKnight:
\r
5282 EditPositionMenuEvent(BlackKnight, fromX, fromY);
\r
5283 fromX = fromY = -1;
\r
5286 case EP_BlackBishop:
\r
5287 EditPositionMenuEvent(BlackBishop, fromX, fromY);
\r
5288 fromX = fromY = -1;
\r
5291 case EP_BlackRook:
\r
5292 EditPositionMenuEvent(BlackRook, fromX, fromY);
\r
5293 fromX = fromY = -1;
\r
5296 case EP_BlackQueen:
\r
5297 EditPositionMenuEvent(BlackQueen, fromX, fromY);
\r
5298 fromX = fromY = -1;
\r
5301 case EP_BlackFerz:
\r
5302 EditPositionMenuEvent(BlackFerz, fromX, fromY);
\r
5303 fromX = fromY = -1;
\r
5306 case EP_BlackWazir:
\r
5307 EditPositionMenuEvent(BlackWazir, fromX, fromY);
\r
5308 fromX = fromY = -1;
\r
5311 case EP_BlackAlfil:
\r
5312 EditPositionMenuEvent(BlackAlfil, fromX, fromY);
\r
5313 fromX = fromY = -1;
\r
5316 case EP_BlackCannon:
\r
5317 EditPositionMenuEvent(BlackCannon, fromX, fromY);
\r
5318 fromX = fromY = -1;
\r
5321 case EP_BlackCardinal:
\r
5322 EditPositionMenuEvent(BlackAngel, fromX, fromY);
\r
5323 fromX = fromY = -1;
\r
5326 case EP_BlackMarshall:
\r
5327 EditPositionMenuEvent(BlackMarshall, fromX, fromY);
\r
5328 fromX = fromY = -1;
\r
5331 case EP_BlackKing:
\r
5332 EditPositionMenuEvent(BlackKing, fromX, fromY);
\r
5333 fromX = fromY = -1;
\r
5336 case EP_EmptySquare:
\r
5337 EditPositionMenuEvent(EmptySquare, fromX, fromY);
\r
5338 fromX = fromY = -1;
\r
5341 case EP_ClearBoard:
\r
5342 EditPositionMenuEvent(ClearBoard, fromX, fromY);
\r
5343 fromX = fromY = -1;
\r
5347 EditPositionMenuEvent(WhitePlay, fromX, fromY);
\r
5348 fromX = fromY = -1;
\r
5352 EditPositionMenuEvent(BlackPlay, fromX, fromY);
\r
5353 fromX = fromY = -1;
\r
5357 EditPositionMenuEvent(PromotePiece, fromX, fromY);
\r
5358 fromX = fromY = -1;
\r
5362 EditPositionMenuEvent(DemotePiece, fromX, fromY);
\r
5363 fromX = fromY = -1;
\r
5367 DropMenuEvent(WhitePawn, fromX, fromY);
\r
5368 fromX = fromY = -1;
\r
5372 DropMenuEvent(WhiteKnight, fromX, fromY);
\r
5373 fromX = fromY = -1;
\r
5377 DropMenuEvent(WhiteBishop, fromX, fromY);
\r
5378 fromX = fromY = -1;
\r
5382 DropMenuEvent(WhiteRook, fromX, fromY);
\r
5383 fromX = fromY = -1;
\r
5387 DropMenuEvent(WhiteQueen, fromX, fromY);
\r
5388 fromX = fromY = -1;
\r
5392 barbaric = 0; appData.language = "";
\r
5393 TranslateMenus(0);
\r
5394 CheckMenuItem(GetMenu(hwndMain), lastChecked, MF_BYCOMMAND|MF_UNCHECKED);
\r
5395 CheckMenuItem(GetMenu(hwndMain), IDM_English, MF_BYCOMMAND|MF_CHECKED);
\r
5396 lastChecked = wmId;
\r
5400 if(wmId > IDM_English && wmId < IDM_English+20) {
\r
5401 LoadLanguageFile(languageFile[wmId - IDM_English - 1]);
\r
5402 TranslateMenus(0);
\r
5403 CheckMenuItem(GetMenu(hwndMain), lastChecked, MF_BYCOMMAND|MF_UNCHECKED);
\r
5404 CheckMenuItem(GetMenu(hwndMain), wmId, MF_BYCOMMAND|MF_CHECKED);
\r
5405 lastChecked = wmId;
\r
5408 return (DefWindowProc(hwnd, message, wParam, lParam));
\r
5414 case CLOCK_TIMER_ID:
\r
5415 KillTimer(hwnd, clockTimerEvent); /* Simulate one-shot timer as in X */
\r
5416 clockTimerEvent = 0;
\r
5417 DecrementClocks(); /* call into back end */
\r
5419 case LOAD_GAME_TIMER_ID:
\r
5420 KillTimer(hwnd, loadGameTimerEvent); /* Simulate one-shot timer as in X*/
\r
5421 loadGameTimerEvent = 0;
\r
5422 AutoPlayGameLoop(); /* call into back end */
\r
5424 case ANALYSIS_TIMER_ID:
\r
5425 if ((gameMode == AnalyzeMode || gameMode == AnalyzeFile
\r
5426 || appData.icsEngineAnalyze) && appData.periodicUpdates) {
\r
5427 AnalysisPeriodicEvent(0);
\r
5429 KillTimer(hwnd, analysisTimerEvent);
\r
5430 analysisTimerEvent = 0;
\r
5433 case DELAYED_TIMER_ID:
\r
5434 KillTimer(hwnd, delayedTimerEvent);
\r
5435 delayedTimerEvent = 0;
\r
5436 delayedTimerCallback();
\r
5441 case WM_USER_Input:
\r
5442 InputEvent(hwnd, message, wParam, lParam);
\r
5445 /* [AS] Also move "attached" child windows */
\r
5446 case WM_WINDOWPOSCHANGING:
\r
5448 if( hwnd == hwndMain && appData.useStickyWindows ) {
\r
5449 LPWINDOWPOS lpwp = (LPWINDOWPOS) lParam;
\r
5451 if( ((lpwp->flags & SWP_NOMOVE) == 0) && ((lpwp->flags & SWP_NOSIZE) != 0) ) {
\r
5452 /* Window is moving */
\r
5455 // GetWindowRect( hwnd, &rcMain ); //[HGM] sticky: in XP this returned new position, not old
\r
5456 rcMain.left = wpMain.x; // replace by these 4 lines to reconstruct old rect
\r
5457 rcMain.right = wpMain.x + wpMain.width;
\r
5458 rcMain.top = wpMain.y;
\r
5459 rcMain.bottom = wpMain.y + wpMain.height;
\r
5461 ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, moveHistoryDialog, &wpMoveHistory );
\r
5462 ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, evalGraphDialog, &wpEvalGraph );
\r
5463 ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, engineOutputDialog, &wpEngineOutput );
\r
5464 ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, gameListDialog, &wpGameList );
\r
5465 ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, hwndConsole, &wpConsole );
\r
5466 wpMain.x = lpwp->x;
\r
5467 wpMain.y = lpwp->y;
\r
5472 /* [AS] Snapping */
\r
5473 case WM_ENTERSIZEMOVE:
\r
5474 if(appData.debugMode) { fprintf(debugFP, "size-move\n"); }
\r
5475 if (hwnd == hwndMain) {
\r
5476 doingSizing = TRUE;
\r
5479 return OnEnterSizeMove( &sd, hwnd, wParam, lParam );
\r
5483 if(appData.debugMode) { fprintf(debugFP, "sizing\n"); }
\r
5484 if (hwnd == hwndMain) {
\r
5485 lastSizing = wParam;
\r
5490 if(appData.debugMode) { fprintf(debugFP, "moving\n"); }
\r
5491 return OnMoving( &sd, hwnd, wParam, lParam );
\r
5493 case WM_EXITSIZEMOVE:
\r
5494 if(appData.debugMode) { fprintf(debugFP, "exit size-move, size = %d\n", squareSize); }
\r
5495 if (hwnd == hwndMain) {
\r
5497 doingSizing = FALSE;
\r
5498 InvalidateRect(hwnd, &boardRect, FALSE);
\r
5499 GetClientRect(hwnd, &client);
\r
5500 ResizeBoard(client.right, client.bottom, lastSizing);
\r
5502 if(appData.debugMode) { fprintf(debugFP, "square size = %d\n", squareSize); }
\r
5504 return OnExitSizeMove( &sd, hwnd, wParam, lParam );
\r
5507 case WM_DESTROY: /* message: window being destroyed */
\r
5508 PostQuitMessage(0);
\r
5512 if (hwnd == hwndMain) {
\r
5517 default: /* Passes it on if unprocessed */
\r
5518 return (DefWindowProc(hwnd, message, wParam, lParam));
\r
5523 /*---------------------------------------------------------------------------*\
\r
5525 * Misc utility routines
\r
5527 \*---------------------------------------------------------------------------*/
\r
5530 * Decent random number generator, at least not as bad as Windows
\r
5531 * standard rand, which returns a value in the range 0 to 0x7fff.
\r
5533 unsigned int randstate;
\r
5538 randstate = randstate * 1664525 + 1013904223;
\r
5539 return (int) randstate & 0x7fffffff;
\r
5543 mysrandom(unsigned int seed)
\r
5550 * returns TRUE if user selects a different color, FALSE otherwise
\r
5554 ChangeColor(HWND hwnd, COLORREF *which)
\r
5556 static BOOL firstTime = TRUE;
\r
5557 static DWORD customColors[16];
\r
5559 COLORREF newcolor;
\r
5564 /* Make initial colors in use available as custom colors */
\r
5565 /* Should we put the compiled-in defaults here instead? */
\r
5567 customColors[i++] = lightSquareColor & 0xffffff;
\r
5568 customColors[i++] = darkSquareColor & 0xffffff;
\r
5569 customColors[i++] = whitePieceColor & 0xffffff;
\r
5570 customColors[i++] = blackPieceColor & 0xffffff;
\r
5571 customColors[i++] = highlightSquareColor & 0xffffff;
\r
5572 customColors[i++] = premoveHighlightColor & 0xffffff;
\r
5574 for (ccl = (ColorClass) 0; ccl < NColorClasses && i < 16; ccl++) {
\r
5575 customColors[i++] = textAttribs[ccl].color;
\r
5577 while (i < 16) customColors[i++] = RGB(255, 255, 255);
\r
5578 firstTime = FALSE;
\r
5581 cc.lStructSize = sizeof(cc);
\r
5582 cc.hwndOwner = hwnd;
\r
5583 cc.hInstance = NULL;
\r
5584 cc.rgbResult = (DWORD) (*which & 0xffffff);
\r
5585 cc.lpCustColors = (LPDWORD) customColors;
\r
5586 cc.Flags = CC_RGBINIT|CC_FULLOPEN;
\r
5588 if (!ChooseColor(&cc)) return FALSE;
\r
5590 newcolor = (COLORREF) (0x2000000 | cc.rgbResult);
\r
5591 if (newcolor == *which) return FALSE;
\r
5592 *which = newcolor;
\r
5596 InitDrawingColors();
\r
5597 InvalidateRect(hwnd, &boardRect, FALSE);
\r
5602 MyLoadSound(MySound *ms)
\r
5608 if (ms->data) free(ms->data);
\r
5611 switch (ms->name[0]) {
\r
5617 /* System sound from Control Panel. Don't preload here. */
\r
5621 if (ms->name[1] == NULLCHAR) {
\r
5622 /* "!" alone = silence */
\r
5625 /* Builtin wave resource. Error if not found. */
\r
5626 HANDLE h = FindResource(hInst, ms->name + 1, "WAVE");
\r
5627 if (h == NULL) break;
\r
5628 ms->data = (void *)LoadResource(hInst, h);
\r
5629 if (h == NULL) break;
\r
5634 /* .wav file. Error if not found. */
\r
5635 f = fopen(ms->name, "rb");
\r
5636 if (f == NULL) break;
\r
5637 if (fstat(fileno(f), &st) < 0) break;
\r
5638 ms->data = malloc(st.st_size);
\r
5639 if (fread(ms->data, st.st_size, 1, f) < 1) break;
\r
5645 char buf[MSG_SIZ];
\r
5646 snprintf(buf, MSG_SIZ, _("Error loading sound %s"), ms->name);
\r
5647 DisplayError(buf, GetLastError());
\r
5653 MyPlaySound(MySound *ms)
\r
5655 BOOLEAN ok = FALSE;
\r
5657 if(mute) return TRUE; // [HGM] mute: suppress all sound play when muted
\r
5658 switch (ms->name[0]) {
\r
5660 if(appData.debugMode) fprintf(debugFP, "silence\n");
\r
5665 /* System sound from Control Panel (deprecated feature).
\r
5666 "$" alone or an unset sound name gets default beep (still in use). */
\r
5667 if (ms->name[1]) {
\r
5668 ok = PlaySound(ms->name + 1, NULL, SND_ALIAS|SND_ASYNC);
\r
5670 if (!ok) ok = MessageBeep(MB_OK);
\r
5673 /* Builtin wave resource, or "!" alone for silence */
\r
5674 if (ms->name[1]) {
\r
5675 if (ms->data == NULL) return FALSE;
\r
5676 ok = PlaySound(ms->data, NULL, SND_MEMORY|SND_ASYNC);
\r
5682 /* .wav file. Error if not found. */
\r
5683 if (ms->data == NULL) return FALSE;
\r
5684 ok = PlaySound(ms->data, NULL, SND_MEMORY|SND_ASYNC);
\r
5687 /* Don't print an error: this can happen innocently if the sound driver
\r
5688 is busy; for instance, if another instance of WinBoard is playing
\r
5689 a sound at about the same time. */
\r
5695 OldOpenFileHook(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
5698 OPENFILENAME *ofn;
\r
5699 static UINT *number; /* gross that this is static */
\r
5701 switch (message) {
\r
5702 case WM_INITDIALOG: /* message: initialize dialog box */
\r
5703 /* Center the dialog over the application window */
\r
5704 ofn = (OPENFILENAME *) lParam;
\r
5705 if (ofn->Flags & OFN_ENABLETEMPLATE) {
\r
5706 number = (UINT *) ofn->lCustData;
\r
5707 SendMessage(GetDlgItem(hDlg, edt2), WM_SETTEXT, 0, (LPARAM) "");
\r
5711 CenterWindow (hDlg, GetWindow (hDlg, GW_OWNER));
\r
5712 Translate(hDlg, 1536);
\r
5713 return FALSE; /* Allow for further processing */
\r
5716 if ((LOWORD(wParam) == IDOK) && (number != NULL)) {
\r
5717 *number = GetDlgItemInt(hDlg, OPT_IndexNumberOld, &ok, FALSE);
\r
5719 return FALSE; /* Allow for further processing */
\r
5725 OpenFileHook(HWND hdlg, UINT uiMsg, WPARAM wParam, LPARAM lParam)
\r
5727 static UINT *number;
\r
5728 OPENFILENAME *ofname;
\r
5731 case WM_INITDIALOG:
\r
5732 Translate(hdlg, DLG_IndexNumber);
\r
5733 ofname = (OPENFILENAME *)lParam;
\r
5734 number = (UINT *)(ofname->lCustData);
\r
5737 ofnot = (OFNOTIFY *)lParam;
\r
5738 if (ofnot->hdr.code == CDN_FILEOK) {
\r
5739 *number = GetDlgItemInt(hdlg, OPT_IndexNumber, NULL, FALSE);
\r
5748 OpenFileDialog(HWND hwnd, char *write, char *defName, char *defExt, // [HGM] diag: type of 'write' now string
\r
5749 char *nameFilt, char *dlgTitle, UINT *number,
\r
5750 char fileTitle[MSG_SIZ], char fileName[MSG_SIZ])
\r
5752 OPENFILENAME openFileName;
\r
5753 char buf1[MSG_SIZ];
\r
5756 if (fileName == NULL) fileName = buf1;
\r
5757 if (defName == NULL) {
\r
5758 safeStrCpy(fileName, "*.", 3 );
\r
5759 strcat(fileName, defExt);
\r
5761 safeStrCpy(fileName, defName, MSG_SIZ );
\r
5763 if (fileTitle) safeStrCpy(fileTitle, "", MSG_SIZ );
\r
5764 if (number) *number = 0;
\r
5766 openFileName.lStructSize = sizeof(OPENFILENAME);
\r
5767 openFileName.hwndOwner = hwnd;
\r
5768 openFileName.hInstance = (HANDLE) hInst;
\r
5769 openFileName.lpstrFilter = nameFilt;
\r
5770 openFileName.lpstrCustomFilter = (LPSTR) NULL;
\r
5771 openFileName.nMaxCustFilter = 0L;
\r
5772 openFileName.nFilterIndex = 1L;
\r
5773 openFileName.lpstrFile = fileName;
\r
5774 openFileName.nMaxFile = MSG_SIZ;
\r
5775 openFileName.lpstrFileTitle = fileTitle;
\r
5776 openFileName.nMaxFileTitle = fileTitle ? MSG_SIZ : 0;
\r
5777 openFileName.lpstrInitialDir = NULL;
\r
5778 openFileName.lpstrTitle = dlgTitle;
\r
5779 openFileName.Flags = OFN_PATHMUSTEXIST | OFN_HIDEREADONLY
\r
5780 | (write[0] != 'r' ? 0 : OFN_FILEMUSTEXIST)
\r
5781 | (number ? OFN_ENABLETEMPLATE | OFN_ENABLEHOOK: 0)
\r
5782 | (oldDialog ? 0 : OFN_EXPLORER);
\r
5783 openFileName.nFileOffset = 0;
\r
5784 openFileName.nFileExtension = 0;
\r
5785 openFileName.lpstrDefExt = defExt;
\r
5786 openFileName.lCustData = (LONG) number;
\r
5787 openFileName.lpfnHook = oldDialog ?
\r
5788 (LPOFNHOOKPROC) OldOpenFileHook : (LPOFNHOOKPROC) OpenFileHook;
\r
5789 openFileName.lpTemplateName = (LPSTR)(oldDialog ? 1536 : DLG_IndexNumber);
\r
5791 if (write[0] != 'r' ? GetSaveFileName(&openFileName) :
\r
5792 GetOpenFileName(&openFileName)) {
\r
5793 /* open the file */
\r
5794 f = fopen(openFileName.lpstrFile, write);
\r
5796 MessageBox(hwnd, _("File open failed"), NULL,
\r
5797 MB_OK|MB_ICONEXCLAMATION);
\r
5801 int err = CommDlgExtendedError();
\r
5802 if (err != 0) DisplayError(_("Internal error in file dialog box"), err);
\r
5811 MenuPopup(HWND hwnd, POINT pt, HMENU hmenu, UINT def)
\r
5813 HMENU hmenuTrackPopup; /* floating pop-up menu */
\r
5816 * Get the first pop-up menu in the menu template. This is the
\r
5817 * menu that TrackPopupMenu displays.
\r
5819 hmenuTrackPopup = GetSubMenu(hmenu, 0);
\r
5820 TranslateOneMenu(10, hmenuTrackPopup);
\r
5822 SetMenuDefaultItem(hmenuTrackPopup, def, FALSE);
\r
5825 * TrackPopup uses screen coordinates, so convert the
\r
5826 * coordinates of the mouse click to screen coordinates.
\r
5828 ClientToScreen(hwnd, (LPPOINT) &pt);
\r
5830 /* Draw and track the floating pop-up menu. */
\r
5831 TrackPopupMenu(hmenuTrackPopup, TPM_CENTERALIGN | TPM_RIGHTBUTTON,
\r
5832 pt.x, pt.y, 0, hwnd, NULL);
\r
5834 /* Destroy the menu.*/
\r
5835 DestroyMenu(hmenu);
\r
5840 int sizeX, sizeY, newSizeX, newSizeY;
\r
5842 } ResizeEditPlusButtonsClosure;
\r
5845 ResizeEditPlusButtonsCallback(HWND hChild, LPARAM lparam)
\r
5847 ResizeEditPlusButtonsClosure *cl = (ResizeEditPlusButtonsClosure *)lparam;
\r
5851 if (hChild == cl->hText) return TRUE;
\r
5852 GetWindowRect(hChild, &rect); /* gives screen coords */
\r
5853 pt.x = rect.left + (cl->newSizeX - cl->sizeX)/2;
\r
5854 pt.y = rect.top + cl->newSizeY - cl->sizeY;
\r
5855 ScreenToClient(cl->hDlg, &pt);
\r
5856 cl->hdwp = DeferWindowPos(cl->hdwp, hChild, NULL,
\r
5857 pt.x, pt.y, rect.right - rect.left, rect.bottom - rect.top, SWP_NOZORDER);
\r
5861 /* Resize a dialog that has a (rich) edit field filling most of
\r
5862 the top, with a row of buttons below */
\r
5864 ResizeEditPlusButtons(HWND hDlg, HWND hText, int sizeX, int sizeY, int newSizeX, int newSizeY)
\r
5867 int newTextHeight, newTextWidth;
\r
5868 ResizeEditPlusButtonsClosure cl;
\r
5870 /*if (IsIconic(hDlg)) return;*/
\r
5871 if (newSizeX == sizeX && newSizeY == sizeY) return;
\r
5873 cl.hdwp = BeginDeferWindowPos(8);
\r
5875 GetWindowRect(hText, &rectText); /* gives screen coords */
\r
5876 newTextWidth = rectText.right - rectText.left + newSizeX - sizeX;
\r
5877 newTextHeight = rectText.bottom - rectText.top + newSizeY - sizeY;
\r
5878 if (newTextHeight < 0) {
\r
5879 newSizeY += -newTextHeight;
\r
5880 newTextHeight = 0;
\r
5882 cl.hdwp = DeferWindowPos(cl.hdwp, hText, NULL, 0, 0,
\r
5883 newTextWidth, newTextHeight, SWP_NOZORDER|SWP_NOMOVE);
\r
5889 cl.newSizeX = newSizeX;
\r
5890 cl.newSizeY = newSizeY;
\r
5891 EnumChildWindows(hDlg, ResizeEditPlusButtonsCallback, (LPARAM)&cl);
\r
5893 EndDeferWindowPos(cl.hdwp);
\r
5896 BOOL CenterWindowEx(HWND hwndChild, HWND hwndParent, int mode)
\r
5898 RECT rChild, rParent;
\r
5899 int wChild, hChild, wParent, hParent;
\r
5900 int wScreen, hScreen, xNew, yNew;
\r
5903 /* Get the Height and Width of the child window */
\r
5904 GetWindowRect (hwndChild, &rChild);
\r
5905 wChild = rChild.right - rChild.left;
\r
5906 hChild = rChild.bottom - rChild.top;
\r
5908 /* Get the Height and Width of the parent window */
\r
5909 GetWindowRect (hwndParent, &rParent);
\r
5910 wParent = rParent.right - rParent.left;
\r
5911 hParent = rParent.bottom - rParent.top;
\r
5913 /* Get the display limits */
\r
5914 hdc = GetDC (hwndChild);
\r
5915 wScreen = GetDeviceCaps (hdc, HORZRES);
\r
5916 hScreen = GetDeviceCaps (hdc, VERTRES);
\r
5917 ReleaseDC(hwndChild, hdc);
\r
5919 /* Calculate new X position, then adjust for screen */
\r
5920 xNew = rParent.left + ((wParent - wChild) /2);
\r
5923 } else if ((xNew+wChild) > wScreen) {
\r
5924 xNew = wScreen - wChild;
\r
5927 /* Calculate new Y position, then adjust for screen */
\r
5929 yNew = rParent.top + ((hParent - hChild) /2);
\r
5932 yNew = rParent.top + GetSystemMetrics( SM_CYCAPTION ) * 2 / 3;
\r
5937 } else if ((yNew+hChild) > hScreen) {
\r
5938 yNew = hScreen - hChild;
\r
5941 /* Set it, and return */
\r
5942 return SetWindowPos (hwndChild, NULL,
\r
5943 xNew, yNew, 0, 0, SWP_NOSIZE | SWP_NOZORDER);
\r
5946 /* Center one window over another */
\r
5947 BOOL CenterWindow (HWND hwndChild, HWND hwndParent)
\r
5949 return CenterWindowEx( hwndChild, hwndParent, 0 );
\r
5952 /*---------------------------------------------------------------------------*\
\r
5954 * Startup Dialog functions
\r
5956 \*---------------------------------------------------------------------------*/
\r
5958 InitComboStrings(HANDLE hwndCombo, char **cd)
\r
5960 SendMessage(hwndCombo, CB_RESETCONTENT, 0, 0);
\r
5962 while (*cd != NULL) {
\r
5963 SendMessage(hwndCombo, CB_ADDSTRING, 0, (LPARAM) T_(*cd));
\r
5969 InitComboStringsFromOption(HANDLE hwndCombo, char *str)
\r
5971 char buf1[MAX_ARG_LEN];
\r
5974 if (str[0] == '@') {
\r
5975 FILE* f = fopen(str + 1, "r");
\r
5977 DisplayFatalError(str + 1, errno, 2);
\r
5980 len = fread(buf1, 1, sizeof(buf1)-1, f);
\r
5982 buf1[len] = NULLCHAR;
\r
5986 SendMessage(hwndCombo, CB_RESETCONTENT, 0, 0);
\r
5989 char buf[MSG_SIZ];
\r
5990 char *end = strchr(str, '\n');
\r
5991 if (end == NULL) return;
\r
5992 memcpy(buf, str, end - str);
\r
5993 buf[end - str] = NULLCHAR;
\r
5994 SendMessage(hwndCombo, CB_ADDSTRING, 0, (LPARAM) buf);
\r
6000 SetStartupDialogEnables(HWND hDlg)
\r
6002 EnableWindow(GetDlgItem(hDlg, OPT_ChessEngineName),
\r
6003 IsDlgButtonChecked(hDlg, OPT_ChessEngine) ||
\r
6004 (appData.zippyPlay && IsDlgButtonChecked(hDlg, OPT_ChessServer)));
\r
6005 EnableWindow(GetDlgItem(hDlg, OPT_SecondChessEngineName),
\r
6006 IsDlgButtonChecked(hDlg, OPT_ChessEngine));
\r
6007 EnableWindow(GetDlgItem(hDlg, OPT_ChessServerName),
\r
6008 IsDlgButtonChecked(hDlg, OPT_ChessServer));
\r
6009 EnableWindow(GetDlgItem(hDlg, OPT_AdditionalOptions),
\r
6010 IsDlgButtonChecked(hDlg, OPT_AnyAdditional));
\r
6011 EnableWindow(GetDlgItem(hDlg, IDOK),
\r
6012 IsDlgButtonChecked(hDlg, OPT_ChessEngine) ||
\r
6013 IsDlgButtonChecked(hDlg, OPT_ChessServer) ||
\r
6014 IsDlgButtonChecked(hDlg, OPT_View));
\r
6018 QuoteForFilename(char *filename)
\r
6020 int dquote, space;
\r
6021 dquote = strchr(filename, '"') != NULL;
\r
6022 space = strchr(filename, ' ') != NULL;
\r
6023 if (dquote || space) {
\r
6035 InitEngineBox(HWND hDlg, HWND hwndCombo, char* nthcp, char* nthd, char* nthdir, char *nthnames)
\r
6037 char buf[MSG_SIZ];
\r
6040 InitComboStringsFromOption(hwndCombo, nthnames);
\r
6041 q = QuoteForFilename(nthcp);
\r
6042 snprintf(buf, MSG_SIZ, "%s%s%s", q, nthcp, q);
\r
6043 if (*nthdir != NULLCHAR) {
\r
6044 q = QuoteForFilename(nthdir);
\r
6045 snprintf(buf + strlen(buf), MSG_SIZ, " /%s=%s%s%s", nthd, q, nthdir, q);
\r
6047 if (*nthcp == NULLCHAR) {
\r
6048 SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) 0, (LPARAM) 0);
\r
6049 } else if (SendMessage(hwndCombo, CB_SELECTSTRING, (WPARAM) -1, (LPARAM) buf) == CB_ERR) {
\r
6050 SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) -1, (LPARAM) 0);
\r
6051 SendMessage(hwndCombo, WM_SETTEXT, (WPARAM) 0, (LPARAM) buf);
\r
6056 StartupDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
6058 char buf[MSG_SIZ];
\r
6062 switch (message) {
\r
6063 case WM_INITDIALOG:
\r
6064 /* Center the dialog */
\r
6065 CenterWindow (hDlg, GetDesktopWindow());
\r
6066 Translate(hDlg, DLG_Startup);
\r
6067 /* Initialize the dialog items */
\r
6068 InitEngineBox(hDlg, GetDlgItem(hDlg, OPT_ChessEngineName),
\r
6069 appData.firstChessProgram, "fd", appData.firstDirectory,
\r
6070 firstChessProgramNames);
\r
6071 InitEngineBox(hDlg, GetDlgItem(hDlg, OPT_SecondChessEngineName),
\r
6072 appData.secondChessProgram, "sd", appData.secondDirectory,
\r
6073 secondChessProgramNames);
\r
6074 hwndCombo = GetDlgItem(hDlg, OPT_ChessServerName);
\r
6075 InitComboStringsFromOption(hwndCombo, icsNames);
\r
6076 snprintf(buf, MSG_SIZ, "%s /icsport=%s", appData.icsHost, appData.icsPort);
\r
6077 if (*appData.icsHelper != NULLCHAR) {
\r
6078 char *q = QuoteForFilename(appData.icsHelper);
\r
6079 sprintf(buf + strlen(buf), " /icshelper=%s%s%s", q, appData.icsHelper, q);
\r
6081 if (*appData.icsHost == NULLCHAR) {
\r
6082 SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) 0, (LPARAM) 0);
\r
6083 /*SendMessage(hwndCombo, CB_SHOWDROPDOWN, (WPARAM) TRUE, (LPARAM) 0); !!too soon */
\r
6084 } else if (SendMessage(hwndCombo, CB_SELECTSTRING, (WPARAM) -1, (LPARAM) buf) == CB_ERR) {
\r
6085 SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) -1, (LPARAM) 0);
\r
6086 SendMessage(hwndCombo, WM_SETTEXT, (WPARAM) 0, (LPARAM) buf);
\r
6089 if (appData.icsActive) {
\r
6090 CheckDlgButton(hDlg, OPT_ChessServer, BST_CHECKED);
\r
6092 else if (appData.noChessProgram) {
\r
6093 CheckDlgButton(hDlg, OPT_View, BST_CHECKED);
\r
6096 CheckDlgButton(hDlg, OPT_ChessEngine, BST_CHECKED);
\r
6099 SetStartupDialogEnables(hDlg);
\r
6103 switch (LOWORD(wParam)) {
\r
6105 if (IsDlgButtonChecked(hDlg, OPT_ChessEngine)) {
\r
6106 safeStrCpy(buf, "/fcp=", sizeof(buf)/sizeof(buf[0]) );
\r
6107 GetDlgItemText(hDlg, OPT_ChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));
\r
6109 ParseArgs(StringGet, &p);
\r
6110 safeStrCpy(buf, "/scp=", sizeof(buf)/sizeof(buf[0]) );
\r
6111 GetDlgItemText(hDlg, OPT_SecondChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));
\r
6113 ParseArgs(StringGet, &p);
\r
6114 appData.noChessProgram = FALSE;
\r
6115 appData.icsActive = FALSE;
\r
6116 } else if (IsDlgButtonChecked(hDlg, OPT_ChessServer)) {
\r
6117 safeStrCpy(buf, "/ics /icshost=", sizeof(buf)/sizeof(buf[0]) );
\r
6118 GetDlgItemText(hDlg, OPT_ChessServerName, buf + strlen(buf), sizeof(buf) - strlen(buf));
\r
6120 ParseArgs(StringGet, &p);
\r
6121 if (appData.zippyPlay) {
\r
6122 safeStrCpy(buf, "/fcp=", sizeof(buf)/sizeof(buf[0]) );
\r
6123 GetDlgItemText(hDlg, OPT_ChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));
\r
6125 ParseArgs(StringGet, &p);
\r
6127 } else if (IsDlgButtonChecked(hDlg, OPT_View)) {
\r
6128 appData.noChessProgram = TRUE;
\r
6129 appData.icsActive = FALSE;
\r
6131 MessageBox(hDlg, _("Choose an option, or cancel to exit"),
\r
6132 _("Option Error"), MB_OK|MB_ICONEXCLAMATION);
\r
6135 if (IsDlgButtonChecked(hDlg, OPT_AnyAdditional)) {
\r
6136 GetDlgItemText(hDlg, OPT_AdditionalOptions, buf, sizeof(buf));
\r
6138 ParseArgs(StringGet, &p);
\r
6140 EndDialog(hDlg, TRUE);
\r
6147 case IDM_HELPCONTENTS:
\r
6148 if (!WinHelp (hDlg, "winboard.hlp", HELP_KEY,(DWORD)(LPSTR)"CONTENTS")) {
\r
6149 MessageBox (GetFocus(),
\r
6150 _("Unable to activate help"),
\r
6151 szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);
\r
6156 SetStartupDialogEnables(hDlg);
\r
6164 /*---------------------------------------------------------------------------*\
\r
6166 * About box dialog functions
\r
6168 \*---------------------------------------------------------------------------*/
\r
6170 /* Process messages for "About" dialog box */
\r
6172 About(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
6174 switch (message) {
\r
6175 case WM_INITDIALOG: /* message: initialize dialog box */
\r
6176 /* Center the dialog over the application window */
\r
6177 CenterWindow (hDlg, GetWindow (hDlg, GW_OWNER));
\r
6178 SetDlgItemText(hDlg, ABOUTBOX_Version, programVersion);
\r
6179 Translate(hDlg, ABOUTBOX);
\r
6183 case WM_COMMAND: /* message: received a command */
\r
6184 if (LOWORD(wParam) == IDOK /* "OK" box selected? */
\r
6185 || LOWORD(wParam) == IDCANCEL) { /* System menu close command? */
\r
6186 EndDialog(hDlg, TRUE); /* Exit the dialog */
\r
6194 /*---------------------------------------------------------------------------*\
\r
6196 * Comment Dialog functions
\r
6198 \*---------------------------------------------------------------------------*/
\r
6201 CommentDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
6203 static HANDLE hwndText = NULL;
\r
6204 int len, newSizeX, newSizeY, flags;
\r
6205 static int sizeX, sizeY;
\r
6210 switch (message) {
\r
6211 case WM_INITDIALOG: /* message: initialize dialog box */
\r
6212 /* Initialize the dialog items */
\r
6213 Translate(hDlg, DLG_EditComment);
\r
6214 hwndText = GetDlgItem(hDlg, OPT_CommentText);
\r
6215 SetDlgItemText(hDlg, OPT_CommentText, commentText);
\r
6216 EnableWindow(GetDlgItem(hDlg, OPT_CancelComment), editComment);
\r
6217 EnableWindow(GetDlgItem(hDlg, OPT_ClearComment), editComment);
\r
6218 EnableWindow(GetDlgItem(hDlg, OPT_EditComment), !editComment);
\r
6219 SendMessage(hwndText, EM_SETREADONLY, !editComment, 0);
\r
6220 SetWindowText(hDlg, commentTitle);
\r
6221 if (editComment) {
\r
6222 SetFocus(hwndText);
\r
6224 SetFocus(GetDlgItem(hDlg, IDOK));
\r
6226 SendMessage(GetDlgItem(hDlg, OPT_CommentText),
\r
6227 WM_SETFONT, (WPARAM)font[boardSize][COMMENT_FONT]->hf,
\r
6228 MAKELPARAM(FALSE, 0));
\r
6229 /* Size and position the dialog */
\r
6230 if (!commentDialog) {
\r
6231 commentDialog = hDlg;
\r
6232 flags = SWP_NOZORDER;
\r
6233 GetClientRect(hDlg, &rect);
\r
6234 sizeX = rect.right;
\r
6235 sizeY = rect.bottom;
\r
6236 if (wpComment.x != CW_USEDEFAULT && wpComment.y != CW_USEDEFAULT &&
\r
6237 wpComment.width != CW_USEDEFAULT && wpComment.height != CW_USEDEFAULT) {
\r
6238 WINDOWPLACEMENT wp;
\r
6239 EnsureOnScreen(&wpComment.x, &wpComment.y, 0, 0);
\r
6240 wp.length = sizeof(WINDOWPLACEMENT);
\r
6242 wp.showCmd = SW_SHOW;
\r
6243 wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;
\r
6244 wp.rcNormalPosition.left = wpComment.x;
\r
6245 wp.rcNormalPosition.right = wpComment.x + wpComment.width;
\r
6246 wp.rcNormalPosition.top = wpComment.y;
\r
6247 wp.rcNormalPosition.bottom = wpComment.y + wpComment.height;
\r
6248 SetWindowPlacement(hDlg, &wp);
\r
6250 GetClientRect(hDlg, &rect);
\r
6251 newSizeX = rect.right;
\r
6252 newSizeY = rect.bottom;
\r
6253 ResizeEditPlusButtons(hDlg, hwndText, sizeX, sizeY,
\r
6254 newSizeX, newSizeY);
\r
6259 SendDlgItemMessage( hDlg, OPT_CommentText, EM_SETEVENTMASK, 0, ENM_MOUSEEVENTS | ENM_KEYEVENTS );
\r
6262 case WM_COMMAND: /* message: received a command */
\r
6263 switch (LOWORD(wParam)) {
\r
6265 if (editComment) {
\r
6267 /* Read changed options from the dialog box */
\r
6268 hwndText = GetDlgItem(hDlg, OPT_CommentText);
\r
6269 len = GetWindowTextLength(hwndText);
\r
6270 str = (char *) malloc(len + 1);
\r
6271 GetWindowText(hwndText, str, len + 1);
\r
6280 ReplaceComment(commentIndex, str);
\r
6287 case OPT_CancelComment:
\r
6291 case OPT_ClearComment:
\r
6292 SetDlgItemText(hDlg, OPT_CommentText, "");
\r
6295 case OPT_EditComment:
\r
6296 EditCommentEvent();
\r
6304 case WM_NOTIFY: // [HGM] vari: cloned from whistory.c
\r
6305 if( wParam == OPT_CommentText ) {
\r
6306 MSGFILTER * lpMF = (MSGFILTER *) lParam;
\r
6308 if( lpMF->msg == WM_RBUTTONDOWN && (lpMF->wParam & (MK_CONTROL | MK_SHIFT)) == 0 ||
\r
6309 lpMF->msg == WM_CHAR && lpMF->wParam == '\022' ) {
\r
6313 pt.x = LOWORD( lpMF->lParam );
\r
6314 pt.y = HIWORD( lpMF->lParam );
\r
6316 if(lpMF->msg == WM_CHAR) {
\r
6318 SendDlgItemMessage( hDlg, OPT_CommentText, EM_EXGETSEL, 0, (LPARAM) &sel );
\r
6319 index = sel.cpMin;
\r
6321 index = SendDlgItemMessage( hDlg, OPT_CommentText, EM_CHARFROMPOS, 0, (LPARAM) &pt );
\r
6323 hwndText = GetDlgItem(hDlg, OPT_CommentText); // cloned from above
\r
6324 len = GetWindowTextLength(hwndText);
\r
6325 str = (char *) malloc(len + 1);
\r
6326 GetWindowText(hwndText, str, len + 1);
\r
6327 ReplaceComment(commentIndex, str);
\r
6328 if(commentIndex != currentMove) ToNrEvent(commentIndex);
\r
6329 LoadVariation( index, str ); // [HGM] also does the actual moving to it, now
\r
6332 /* Zap the message for good: apparently, returning non-zero is not enough */
\r
6333 lpMF->msg = WM_USER;
\r
6341 newSizeX = LOWORD(lParam);
\r
6342 newSizeY = HIWORD(lParam);
\r
6343 ResizeEditPlusButtons(hDlg, hwndText, sizeX, sizeY, newSizeX, newSizeY);
\r
6348 case WM_GETMINMAXINFO:
\r
6349 /* Prevent resizing window too small */
\r
6350 mmi = (MINMAXINFO *) lParam;
\r
6351 mmi->ptMinTrackSize.x = 100;
\r
6352 mmi->ptMinTrackSize.y = 100;
\r
6359 EitherCommentPopUp(int index, char *title, char *str, BOOLEAN edit)
\r
6364 CheckMenuItem(GetMenu(hwndMain), IDM_Comment, edit ? MF_CHECKED : MF_UNCHECKED);
\r
6366 if (str == NULL) str = "";
\r
6367 p = (char *) malloc(2 * strlen(str) + 2);
\r
6370 if (*str == '\n') *q++ = '\r';
\r
6374 if (commentText != NULL) free(commentText);
\r
6376 commentIndex = index;
\r
6377 commentTitle = title;
\r
6379 editComment = edit;
\r
6381 if (commentDialog) {
\r
6382 SendMessage(commentDialog, WM_INITDIALOG, 0, 0);
\r
6383 if (!commentUp) ShowWindow(commentDialog, SW_SHOW);
\r
6385 lpProc = MakeProcInstance((FARPROC)CommentDialog, hInst);
\r
6386 CreateDialog(hInst, MAKEINTRESOURCE(DLG_EditComment),
\r
6387 hwndMain, (DLGPROC)lpProc);
\r
6388 FreeProcInstance(lpProc);
\r
6394 /*---------------------------------------------------------------------------*\
\r
6396 * Type-in move dialog functions
\r
6398 \*---------------------------------------------------------------------------*/
\r
6401 TypeInMoveDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
6403 char move[MSG_SIZ];
\r
6406 switch (message) {
\r
6407 case WM_INITDIALOG:
\r
6408 move[0] = (char) lParam;
\r
6409 move[1] = NULLCHAR;
\r
6410 CenterWindowEx(hDlg, GetWindow(hDlg, GW_OWNER), 1 );
\r
6411 Translate(hDlg, DLG_TypeInMove);
\r
6412 hInput = GetDlgItem(hDlg, OPT_Move);
\r
6413 SetWindowText(hInput, move);
\r
6415 SendMessage(hInput, EM_SETSEL, (WPARAM)9999, (LPARAM)9999);
\r
6419 switch (LOWORD(wParam)) {
\r
6422 shiftKey = GetKeyState(VK_SHIFT) < 0; // [HGM] remember last shift status
\r
6423 GetDlgItemText(hDlg, OPT_Move, move, sizeof(move));
6424 TypeInDoneEvent(move);
\r
6425 EndDialog(hDlg, TRUE);
\r
6428 EndDialog(hDlg, FALSE);
\r
6439 PopUpMoveDialog(char firstchar)
\r
6443 lpProc = MakeProcInstance((FARPROC)TypeInMoveDialog, hInst);
\r
6444 DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_TypeInMove),
\r
6445 hwndMain, (DLGPROC)lpProc, (LPARAM)firstchar);
\r
6446 FreeProcInstance(lpProc);
\r
6449 /*---------------------------------------------------------------------------*\
\r
6451 * Type-in name dialog functions
\r
6453 \*---------------------------------------------------------------------------*/
\r
6456 TypeInNameDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
6458 char move[MSG_SIZ];
\r
6461 switch (message) {
\r
6462 case WM_INITDIALOG:
\r
6463 move[0] = (char) lParam;
\r
6464 move[1] = NULLCHAR;
\r
6465 CenterWindowEx(hDlg, GetWindow(hDlg, GW_OWNER), 1 );
\r
6466 Translate(hDlg, DLG_TypeInName);
\r
6467 hInput = GetDlgItem(hDlg, OPT_Name);
\r
6468 SetWindowText(hInput, move);
\r
6470 SendMessage(hInput, EM_SETSEL, (WPARAM)9999, (LPARAM)9999);
\r
6474 switch (LOWORD(wParam)) {
\r
6476 GetDlgItemText(hDlg, OPT_Name, move, sizeof(move));
\r
6477 appData.userName = strdup(move);
\r
6480 if(gameMode == MachinePlaysWhite || gameMode == MachinePlaysBlack) {
\r
6481 snprintf(move, MSG_SIZ, "%s vs. %s", gameInfo.white, gameInfo.black);
\r
6482 DisplayTitle(move);
\r
6486 EndDialog(hDlg, TRUE);
\r
6489 EndDialog(hDlg, FALSE);
\r
6500 PopUpNameDialog(char firstchar)
\r
6504 lpProc = MakeProcInstance((FARPROC)TypeInNameDialog, hInst);
\r
6505 DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_TypeInName),
\r
6506 hwndMain, (DLGPROC)lpProc, (LPARAM)firstchar);
\r
6507 FreeProcInstance(lpProc);
\r
6510 /*---------------------------------------------------------------------------*\
\r
6514 \*---------------------------------------------------------------------------*/
\r
6516 /* Nonmodal error box */
\r
6517 LRESULT CALLBACK ErrorDialog(HWND hDlg, UINT message,
\r
6518 WPARAM wParam, LPARAM lParam);
\r
6521 ErrorPopUp(char *title, char *content)
\r
6525 BOOLEAN modal = hwndMain == NULL;
\r
6543 strncpy(errorTitle, title, sizeof(errorTitle));
\r
6544 errorTitle[sizeof(errorTitle) - 1] = '\0';
\r
6547 MessageBox(NULL, errorMessage, errorTitle, MB_OK|MB_ICONEXCLAMATION);
\r
6549 lpProc = MakeProcInstance((FARPROC)ErrorDialog, hInst);
\r
6550 CreateDialog(hInst, MAKEINTRESOURCE(DLG_Error),
\r
6551 hwndMain, (DLGPROC)lpProc);
\r
6552 FreeProcInstance(lpProc);
\r
6559 if (!appData.popupMoveErrors && moveErrorMessageUp) DisplayMessage("", "");
\r
6560 if (errorDialog == NULL) return;
\r
6561 DestroyWindow(errorDialog);
\r
6562 errorDialog = NULL;
\r
6563 if(errorExitStatus) ExitEvent(errorExitStatus);
\r
6567 ErrorDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
6572 switch (message) {
\r
6573 case WM_INITDIALOG:
\r
6574 GetWindowRect(hDlg, &rChild);
\r
6577 SetWindowPos(hDlg, NULL, rChild.left,
\r
6578 rChild.top + boardRect.top - (rChild.bottom - rChild.top),
\r
6579 0, 0, SWP_NOZORDER|SWP_NOSIZE);
\r
6583 [AS] It seems that the above code wants to move the dialog up in the "caption
\r
6584 area" of the main window, but it uses the dialog height as an hard-coded constant,
\r
6585 and it doesn't work when you resize the dialog.
\r
6586 For now, just give it a default position.
\r
6588 SetWindowPos(hDlg, NULL, boardRect.left+8, boardRect.top+8, 0, 0, SWP_NOZORDER|SWP_NOSIZE);
\r
6589 Translate(hDlg, DLG_Error);
\r
6591 errorDialog = hDlg;
\r
6592 SetWindowText(hDlg, errorTitle);
\r
6593 hwndText = GetDlgItem(hDlg, OPT_ErrorText);
\r
6594 SetDlgItemText(hDlg, OPT_ErrorText, errorMessage);
\r
6598 switch (LOWORD(wParam)) {
\r
6601 if (errorDialog == hDlg) errorDialog = NULL;
\r
6602 DestroyWindow(hDlg);
\r
6614 HWND gothicDialog = NULL;
\r
6617 GothicDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
6621 int height = GetSystemMetrics(SM_CYCAPTION)+GetSystemMetrics(SM_CYFRAME);
\r
6623 switch (message) {
\r
6624 case WM_INITDIALOG:
\r
6625 GetWindowRect(hDlg, &rChild);
\r
6627 SetWindowPos(hDlg, NULL, wpMain.x, wpMain.y-height, wpMain.width, height,
\r
6631 [AS] It seems that the above code wants to move the dialog up in the "caption
\r
6632 area" of the main window, but it uses the dialog height as an hard-coded constant,
\r
6633 and it doesn't work when you resize the dialog.
\r
6634 For now, just give it a default position.
\r
6636 gothicDialog = hDlg;
\r
6637 SetWindowText(hDlg, errorTitle);
\r
6638 hwndText = GetDlgItem(hDlg, OPT_ErrorText);
\r
6639 SetDlgItemText(hDlg, OPT_ErrorText, errorMessage);
\r
6643 switch (LOWORD(wParam)) {
\r
6646 if (errorDialog == hDlg) errorDialog = NULL;
\r
6647 DestroyWindow(hDlg);
\r
6659 GothicPopUp(char *title, VariantClass variant)
\r
6662 static char *lastTitle;
\r
6664 strncpy(errorTitle, title, sizeof(errorTitle));
\r
6665 errorTitle[sizeof(errorTitle) - 1] = '\0';
\r
6667 if(lastTitle != title && gothicDialog != NULL) {
\r
6668 DestroyWindow(gothicDialog);
\r
6669 gothicDialog = NULL;
\r
6671 if(variant != VariantNormal && gothicDialog == NULL) {
\r
6672 title = lastTitle;
\r
6673 lpProc = MakeProcInstance((FARPROC)GothicDialog, hInst);
\r
6674 CreateDialog(hInst, MAKEINTRESOURCE(DLG_Error),
\r
6675 hwndMain, (DLGPROC)lpProc);
\r
6676 FreeProcInstance(lpProc);
\r
6681 /*---------------------------------------------------------------------------*\
\r
6683 * Ics Interaction console functions
\r
6685 \*---------------------------------------------------------------------------*/
\r
6687 #define HISTORY_SIZE 64
\r
6688 static char *history[HISTORY_SIZE];
\r
6689 int histIn = 0, histP = 0;
\r
6692 SaveInHistory(char *cmd)
\r
6694 if (history[histIn] != NULL) {
\r
6695 free(history[histIn]);
\r
6696 history[histIn] = NULL;
\r
6698 if (*cmd == NULLCHAR) return;
\r
6699 history[histIn] = StrSave(cmd);
\r
6700 histIn = (histIn + 1) % HISTORY_SIZE;
\r
6701 if (history[histIn] != NULL) {
\r
6702 free(history[histIn]);
\r
6703 history[histIn] = NULL;
\r
6709 PrevInHistory(char *cmd)
\r
6712 if (histP == histIn) {
\r
6713 if (history[histIn] != NULL) free(history[histIn]);
\r
6714 history[histIn] = StrSave(cmd);
\r
6716 newhp = (histP - 1 + HISTORY_SIZE) % HISTORY_SIZE;
\r
6717 if (newhp == histIn || history[newhp] == NULL) return NULL;
\r
6719 return history[histP];
\r
6725 if (histP == histIn) return NULL;
\r
6726 histP = (histP + 1) % HISTORY_SIZE;
\r
6727 return history[histP];
\r
6731 LoadIcsTextMenu(IcsTextMenuEntry *e)
\r
6735 hmenu = LoadMenu(hInst, "TextMenu");
\r
6736 h = GetSubMenu(hmenu, 0);
\r
6738 if (strcmp(e->item, "-") == 0) {
\r
6739 AppendMenu(h, MF_SEPARATOR, 0, 0);
\r
6740 } else { // [HGM] re-written a bit to use only one AppendMenu call for both cases (| or no |)
\r
6741 int flags = MF_STRING, j = 0;
\r
6742 if (e->item[0] == '|') {
\r
6743 flags |= MF_MENUBARBREAK;
\r
6746 if(!strcmp(e->command, "none")) flags |= MF_GRAYED; // [HGM] chatclick: provide inactive dummy
\r
6747 AppendMenu(h, flags, IDM_CommandX + i, e->item + j);
\r
6755 WNDPROC consoleTextWindowProc;
\r
6758 CommandX(HWND hwnd, char *command, BOOLEAN getname, BOOLEAN immediate)
\r
6760 char buf[MSG_SIZ], name[MSG_SIZ];
\r
6761 HWND hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
6765 SetWindowText(hInput, command);
\r
6767 SendMessage(hInput, WM_CHAR, '\r', 0);
\r
6769 sel.cpMin = 999999;
\r
6770 sel.cpMax = 999999;
\r
6771 SendMessage(hInput, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
6776 SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);
\r
6777 if (sel.cpMin == sel.cpMax) {
\r
6778 /* Expand to surrounding word */
\r
6781 tr.chrg.cpMax = sel.cpMin;
\r
6782 tr.chrg.cpMin = --sel.cpMin;
\r
6783 if (sel.cpMin < 0) break;
\r
6784 tr.lpstrText = name;
\r
6785 SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr);
\r
6786 } while (isalpha(name[0]) || isdigit(name[0]) || name[0] == '-');
\r
6790 tr.chrg.cpMin = sel.cpMax;
\r
6791 tr.chrg.cpMax = ++sel.cpMax;
\r
6792 tr.lpstrText = name;
\r
6793 if (SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr) < 1) break;
\r
6794 } while (isalpha(name[0]) || isdigit(name[0]) || name[0] == '-');
\r
6797 if (sel.cpMax == sel.cpMin || sel.cpMax - sel.cpMin > MSG_SIZ/2) {
\r
6798 MessageBeep(MB_ICONEXCLAMATION);
\r
6802 tr.lpstrText = name;
\r
6803 SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr);
\r
6805 if (sel.cpMax - sel.cpMin > MSG_SIZ/2) {
\r
6806 MessageBeep(MB_ICONEXCLAMATION);
\r
6809 SendMessage(hwnd, EM_GETSELTEXT, 0, (LPARAM) name);
\r
6812 if(strstr(command, "%s")) snprintf(buf, MSG_SIZ, command, name); else
\r
6813 snprintf(buf, MSG_SIZ, "%s %s", command, name);
\r
6814 SetWindowText(hInput, buf);
\r
6815 SendMessage(hInput, WM_CHAR, '\r', 0);
\r
6817 if(!strcmp(command, "chat")) { ChatPopUp(name); return; }
\r
6818 snprintf(buf, MSG_SIZ, "%s %s ", command, name); /* trailing space */
\r
6819 SetWindowText(hInput, buf);
\r
6820 sel.cpMin = 999999;
\r
6821 sel.cpMax = 999999;
\r
6822 SendMessage(hInput, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
6828 ConsoleTextSubclass(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
\r
6833 switch (message) {
\r
6835 if (!(GetKeyState(VK_CONTROL) & ~1)) break;
\r
6836 if(wParam=='R') return 0;
\r
6839 SendMessage(hwnd, EM_LINESCROLL, 0, -999999);
\r
6842 sel.cpMin = 999999;
\r
6843 sel.cpMax = 999999;
\r
6844 SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
6845 SendMessage(hwnd, EM_SCROLLCARET, 0, 0);
\r
6850 if(wParam != '\022') {
\r
6851 if (wParam == '\t') {
\r
6852 if (GetKeyState(VK_SHIFT) < 0) {
\r
6854 if (IsIconic(hwndMain)) ShowWindow(hwndMain, SW_RESTORE);
\r
6855 if (buttonDesc[0].hwnd) {
\r
6856 SetFocus(buttonDesc[0].hwnd);
\r
6858 SetFocus(hwndMain);
\r
6862 SetFocus(GetDlgItem(hwndConsole, OPT_ConsoleInput));
\r
6865 hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
6866 JAWS_DELETE( SetFocus(hInput); )
\r
6867 SendMessage(hInput, message, wParam, lParam);
\r
6870 } // [HGM] navigate: for Ctrl+R, flow into next case (moved up here) to summon up menu
\r
6872 case WM_RBUTTONDOWN:
\r
6873 if (!(GetKeyState(VK_SHIFT) & ~1)) {
\r
6874 /* Move selection here if it was empty */
\r
6876 pt.x = LOWORD(lParam);
\r
6877 pt.y = HIWORD(lParam);
\r
6878 SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);
\r
6879 if (sel.cpMin == sel.cpMax) {
\r
6880 if(lParam != -1) sel.cpMin = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&pt); /*doc is wrong*/
\r
6881 sel.cpMax = sel.cpMin;
\r
6882 SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
6884 SendMessage(hwnd, EM_HIDESELECTION, FALSE, FALSE);
\r
6885 { // [HGM] chatclick: code moved here from WM_RBUTTONUP case, to have menu appear on down-click
\r
6887 HMENU hmenu = LoadIcsTextMenu(icsTextMenuEntry);
\r
6888 SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);
\r
6889 if (sel.cpMin == sel.cpMax) {
\r
6890 EnableMenuItem(hmenu, IDM_Copy, MF_BYCOMMAND|MF_GRAYED);
\r
6891 EnableMenuItem(hmenu, IDM_QuickPaste, MF_BYCOMMAND|MF_GRAYED);
\r
6893 if (!IsClipboardFormatAvailable(CF_TEXT)) {
\r
6894 EnableMenuItem(hmenu, IDM_Paste, MF_BYCOMMAND|MF_GRAYED);
\r
6896 pt.x = LOWORD(lParam)-30; // [HGM] chatclick: make menu pop up with pointer above upper-right item
\r
6897 pt.y = HIWORD(lParam)-10; // make it appear as if mouse moved there, so it will be selected on up-click
\r
6898 PostMessage(hwnd, WM_MOUSEMOVE, wParam, lParam+5);
\r
6899 MenuPopup(hwnd, pt, hmenu, -1);
\r
6903 case WM_RBUTTONUP:
\r
6904 if (GetKeyState(VK_SHIFT) & ~1) {
\r
6905 SendDlgItemMessage(hwndConsole, OPT_ConsoleText,
\r
6906 WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);
\r
6910 hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
6912 return SendMessage(hInput, message, wParam, lParam);
\r
6913 case WM_MBUTTONDOWN:
\r
6914 return SendMessage(hwnd, WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);
\r
6916 switch (LOWORD(wParam)) {
\r
6917 case IDM_QuickPaste:
\r
6919 SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);
\r
6920 if (sel.cpMin == sel.cpMax) {
\r
6921 MessageBeep(MB_ICONEXCLAMATION);
\r
6924 SendMessage(hwnd, WM_COPY, 0, 0);
\r
6925 hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
6926 SendMessage(hInput, WM_PASTE, 0, 0);
\r
6931 SendMessage(hwnd, WM_CUT, 0, 0);
\r
6934 SendMessage(hwnd, WM_PASTE, 0, 0);
\r
6937 SendMessage(hwnd, WM_COPY, 0, 0);
\r
6941 int i = LOWORD(wParam) - IDM_CommandX;
\r
6942 if (i >= 0 && i < ICS_TEXT_MENU_SIZE &&
\r
6943 icsTextMenuEntry[i].command != NULL) {
\r
6944 CommandX(hwnd, icsTextMenuEntry[i].command,
\r
6945 icsTextMenuEntry[i].getname,
\r
6946 icsTextMenuEntry[i].immediate);
\r
6954 return (*consoleTextWindowProc)(hwnd, message, wParam, lParam);
\r
6957 WNDPROC consoleInputWindowProc;
\r
6960 ConsoleInputSubclass(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
\r
6962 char buf[MSG_SIZ];
\r
6964 static BOOL sendNextChar = FALSE;
\r
6965 static BOOL quoteNextChar = FALSE;
\r
6966 InputSource *is = consoleInputSource;
\r
6970 switch (message) {
\r
6972 if (!appData.localLineEditing || sendNextChar) {
\r
6973 is->buf[0] = (CHAR) wParam;
\r
6975 SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);
\r
6976 sendNextChar = FALSE;
\r
6979 if (quoteNextChar) {
\r
6980 buf[0] = (char) wParam;
\r
6981 buf[1] = NULLCHAR;
\r
6982 SendMessage(hwnd, EM_REPLACESEL, TRUE, (LPARAM) buf);
\r
6983 quoteNextChar = FALSE;
\r
6987 case '\r': /* Enter key */
\r
6988 is->count = GetWindowText(hwnd, is->buf, INPUT_SOURCE_BUF_SIZE-1);
\r
6989 if (consoleEcho) SaveInHistory(is->buf);
\r
6990 is->buf[is->count++] = '\n';
\r
6991 is->buf[is->count] = NULLCHAR;
\r
6992 SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);
\r
6993 if (consoleEcho) {
\r
6994 ConsoleOutput(is->buf, is->count, TRUE);
\r
6995 } else if (appData.localLineEditing) {
\r
6996 ConsoleOutput("\n", 1, TRUE);
\r
6999 case '\033': /* Escape key */
\r
7000 SetWindowText(hwnd, "");
\r
7001 cf.cbSize = sizeof(CHARFORMAT);
\r
7002 cf.dwMask = CFM_COLOR|CFM_BOLD|CFM_ITALIC|CFM_UNDERLINE|CFM_STRIKEOUT;
\r
7003 if (consoleEcho) {
\r
7004 cf.crTextColor = textAttribs[ColorNormal].color;
\r
7006 cf.crTextColor = COLOR_ECHOOFF;
\r
7008 cf.dwEffects = textAttribs[ColorNormal].effects;
\r
7009 SendMessage(hwnd, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf);
\r
7011 case '\t': /* Tab key */
\r
7012 if (GetKeyState(VK_SHIFT) < 0) {
\r
7014 SetFocus(GetDlgItem(hwndConsole, OPT_ConsoleText));
\r
7017 if (IsIconic(hwndMain)) ShowWindow(hwndMain, SW_RESTORE);
\r
7018 if (buttonDesc[0].hwnd) {
\r
7019 SetFocus(buttonDesc[0].hwnd);
\r
7021 SetFocus(hwndMain);
\r
7025 case '\023': /* Ctrl+S */
\r
7026 sendNextChar = TRUE;
\r
7028 case '\021': /* Ctrl+Q */
\r
7029 quoteNextChar = TRUE;
\r
7039 GetWindowText(hwnd, buf, MSG_SIZ);
\r
7040 p = PrevInHistory(buf);
\r
7042 SetWindowText(hwnd, p);
\r
7043 sel.cpMin = 999999;
\r
7044 sel.cpMax = 999999;
\r
7045 SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
7050 p = NextInHistory();
\r
7052 SetWindowText(hwnd, p);
\r
7053 sel.cpMin = 999999;
\r
7054 sel.cpMax = 999999;
\r
7055 SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
7061 if (!(GetKeyState(VK_CONTROL) & ~1)) break;
\r
7065 SendDlgItemMessage(hwndConsole, OPT_ConsoleText, message, wParam, lParam);
\r
7069 case WM_MBUTTONDOWN:
\r
7070 SendDlgItemMessage(hwndConsole, OPT_ConsoleText,
\r
7071 WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);
\r
7073 case WM_RBUTTONUP:
\r
7074 if (GetKeyState(VK_SHIFT) & ~1) {
\r
7075 SendDlgItemMessage(hwndConsole, OPT_ConsoleText,
\r
7076 WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);
\r
7080 hmenu = LoadMenu(hInst, "InputMenu");
\r
7081 SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);
\r
7082 if (sel.cpMin == sel.cpMax) {
\r
7083 EnableMenuItem(hmenu, IDM_Copy, MF_BYCOMMAND|MF_GRAYED);
\r
7084 EnableMenuItem(hmenu, IDM_Cut, MF_BYCOMMAND|MF_GRAYED);
\r
7086 if (!IsClipboardFormatAvailable(CF_TEXT)) {
\r
7087 EnableMenuItem(hmenu, IDM_Paste, MF_BYCOMMAND|MF_GRAYED);
\r
7089 pt.x = LOWORD(lParam);
\r
7090 pt.y = HIWORD(lParam);
\r
7091 MenuPopup(hwnd, pt, hmenu, -1);
\r
7095 switch (LOWORD(wParam)) {
\r
7097 SendMessage(hwnd, EM_UNDO, 0, 0);
\r
7099 case IDM_SelectAll:
\r
7101 sel.cpMax = -1; /*999999?*/
\r
7102 SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
7105 SendMessage(hwnd, WM_CUT, 0, 0);
\r
7108 SendMessage(hwnd, WM_PASTE, 0, 0);
\r
7111 SendMessage(hwnd, WM_COPY, 0, 0);
\r
7116 return (*consoleInputWindowProc)(hwnd, message, wParam, lParam);
\r
7119 #define CO_MAX 100000
\r
7120 #define CO_TRIM 1000
\r
7123 ConsoleWndProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
7125 static SnapData sd;
\r
7126 HWND hText, hInput;
\r
7128 static int sizeX, sizeY;
\r
7129 int newSizeX, newSizeY;
\r
7133 hText = GetDlgItem(hDlg, OPT_ConsoleText);
\r
7134 hInput = GetDlgItem(hDlg, OPT_ConsoleInput);
\r
7136 switch (message) {
\r
7138 if (((NMHDR*)lParam)->code == EN_LINK)
\r
7140 ENLINK *pLink = (ENLINK*)lParam;
\r
7141 if (pLink->msg == WM_LBUTTONUP)
\r
7145 tr.chrg = pLink->chrg;
\r
7146 tr.lpstrText = malloc(1+tr.chrg.cpMax-tr.chrg.cpMin);
\r
7147 SendMessage(hText, EM_GETTEXTRANGE, 0, (LPARAM)&tr);
\r
7148 ShellExecute(NULL, "open", tr.lpstrText, NULL, NULL, SW_SHOW);
\r
7149 free(tr.lpstrText);
\r
7153 case WM_INITDIALOG: /* message: initialize dialog box */
\r
7154 hwndConsole = hDlg;
\r
7156 consoleTextWindowProc = (WNDPROC)
\r
7157 SetWindowLongPtr(hText, GWLP_WNDPROC, (LONG_PTR) ConsoleTextSubclass);
\r
7158 SendMessage(hText, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);
\r
7159 consoleInputWindowProc = (WNDPROC)
\r
7160 SetWindowLongPtr(hInput, GWLP_WNDPROC, (LONG_PTR) ConsoleInputSubclass);
\r
7161 SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);
\r
7162 Colorize(ColorNormal, TRUE);
\r
7163 SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &consoleCF);
\r
7164 ChangedConsoleFont();
\r
7165 GetClientRect(hDlg, &rect);
\r
7166 sizeX = rect.right;
\r
7167 sizeY = rect.bottom;
\r
7168 if (wpConsole.x != CW_USEDEFAULT && wpConsole.y != CW_USEDEFAULT &&
\r
7169 wpConsole.width != CW_USEDEFAULT && wpConsole.height != CW_USEDEFAULT) {
\r
7170 WINDOWPLACEMENT wp;
\r
7171 EnsureOnScreen(&wpConsole.x, &wpConsole.y, 0, 0);
\r
7172 wp.length = sizeof(WINDOWPLACEMENT);
\r
7174 wp.showCmd = SW_SHOW;
\r
7175 wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;
\r
7176 wp.rcNormalPosition.left = wpConsole.x;
\r
7177 wp.rcNormalPosition.right = wpConsole.x + wpConsole.width;
\r
7178 wp.rcNormalPosition.top = wpConsole.y;
\r
7179 wp.rcNormalPosition.bottom = wpConsole.y + wpConsole.height;
\r
7180 SetWindowPlacement(hDlg, &wp);
\r
7183 // [HGM] Chessknight's change 2004-07-13
\r
7184 else { /* Determine Defaults */
\r
7185 WINDOWPLACEMENT wp;
\r
7186 wpConsole.x = wpMain.width + 1;
\r
7187 wpConsole.y = wpMain.y;
\r
7188 wpConsole.width = screenWidth - wpMain.width;
\r
7189 wpConsole.height = wpMain.height;
\r
7190 EnsureOnScreen(&wpConsole.x, &wpConsole.y, 0, 0);
\r
7191 wp.length = sizeof(WINDOWPLACEMENT);
\r
7193 wp.showCmd = SW_SHOW;
\r
7194 wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;
\r
7195 wp.rcNormalPosition.left = wpConsole.x;
\r
7196 wp.rcNormalPosition.right = wpConsole.x + wpConsole.width;
\r
7197 wp.rcNormalPosition.top = wpConsole.y;
\r
7198 wp.rcNormalPosition.bottom = wpConsole.y + wpConsole.height;
\r
7199 SetWindowPlacement(hDlg, &wp);
\r
7202 // Allow hText to highlight URLs and send notifications on them
\r
7203 wMask = (WORD) SendMessage(hText, EM_GETEVENTMASK, 0, 0L);
\r
7204 SendMessage(hText, EM_SETEVENTMASK, 0, wMask | ENM_LINK);
\r
7205 SendMessage(hText, EM_AUTOURLDETECT, TRUE, 0L);
\r
7206 SetWindowLongPtr(hText, GWLP_USERDATA, 79); // initialize the text window's width
\r
7220 if (IsIconic(hDlg)) break;
\r
7221 newSizeX = LOWORD(lParam);
\r
7222 newSizeY = HIWORD(lParam);
\r
7223 if (sizeX != newSizeX || sizeY != newSizeY) {
\r
7224 RECT rectText, rectInput;
\r
7226 int newTextHeight, newTextWidth;
\r
7227 GetWindowRect(hText, &rectText);
\r
7228 newTextWidth = rectText.right - rectText.left + newSizeX - sizeX;
\r
7229 newTextHeight = rectText.bottom - rectText.top + newSizeY - sizeY;
\r
7230 if (newTextHeight < 0) {
\r
7231 newSizeY += -newTextHeight;
\r
7232 newTextHeight = 0;
\r
7234 SetWindowPos(hText, NULL, 0, 0,
\r
7235 newTextWidth, newTextHeight, SWP_NOZORDER|SWP_NOMOVE);
\r
7236 GetWindowRect(hInput, &rectInput); /* gives screen coords */
\r
7237 pt.x = rectInput.left;
\r
7238 pt.y = rectInput.top + newSizeY - sizeY;
\r
7239 ScreenToClient(hDlg, &pt);
\r
7240 SetWindowPos(hInput, NULL,
\r
7241 pt.x, pt.y, /* needs client coords */
\r
7242 rectInput.right - rectInput.left + newSizeX - sizeX,
\r
7243 rectInput.bottom - rectInput.top, SWP_NOZORDER);
\r
7249 case WM_GETMINMAXINFO:
\r
7250 /* Prevent resizing window too small */
\r
7251 mmi = (MINMAXINFO *) lParam;
\r
7252 mmi->ptMinTrackSize.x = 100;
\r
7253 mmi->ptMinTrackSize.y = 100;
\r
7256 /* [AS] Snapping */
\r
7257 case WM_ENTERSIZEMOVE:
\r
7258 return OnEnterSizeMove( &sd, hDlg, wParam, lParam );
\r
7261 return OnSizing( &sd, hDlg, wParam, lParam );
\r
7264 return OnMoving( &sd, hDlg, wParam, lParam );
\r
7266 case WM_EXITSIZEMOVE:
\r
7267 UpdateICSWidth(hText);
\r
7268 return OnExitSizeMove( &sd, hDlg, wParam, lParam );
\r
7271 return DefWindowProc(hDlg, message, wParam, lParam);
\r
7279 if (hwndConsole) return;
\r
7280 hCons = CreateDialog(hInst, szConsoleName, 0, NULL);
\r
7281 SendMessage(hCons, WM_INITDIALOG, 0, 0);
\r
7286 ConsoleOutput(char* data, int length, int forceVisible)
\r
7291 char buf[CO_MAX+1];
\r
7294 static int delayLF = 0;
\r
7295 CHARRANGE savesel, sel;
\r
7297 if (hwndConsole == NULL || length > CO_MAX-100 || length == 0) return;
\r
7305 while (length--) {
\r
7313 } else if (*p == '\007') {
\r
7314 MyPlaySound(&sounds[(int)SoundBell]);
\r
7321 hText = GetDlgItem(hwndConsole, OPT_ConsoleText);
\r
7322 SendMessage(hText, EM_HIDESELECTION, TRUE, FALSE);
\r
7323 /* Save current selection */
\r
7324 SendMessage(hText, EM_EXGETSEL, 0, (LPARAM)&savesel);
\r
7325 exlen = GetWindowTextLength(hText);
\r
7326 /* Find out whether current end of text is visible */
\r
7327 SendMessage(hText, EM_GETRECT, 0, (LPARAM) &rect);
\r
7328 SendMessage(hText, EM_POSFROMCHAR, (WPARAM) &pEnd, exlen);
\r
7329 /* Trim existing text if it's too long */
\r
7330 if (exlen + (q - buf) > CO_MAX) {
\r
7331 trim = (CO_TRIM > (q - buf)) ? CO_TRIM : (q - buf);
\r
7334 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
7335 SendMessage(hText, EM_REPLACESEL, 0, (LPARAM)"");
\r
7337 savesel.cpMin -= trim;
\r
7338 savesel.cpMax -= trim;
\r
7339 if (exlen < 0) exlen = 0;
\r
7340 if (savesel.cpMin < 0) savesel.cpMin = 0;
\r
7341 if (savesel.cpMax < savesel.cpMin) savesel.cpMax = savesel.cpMin;
\r
7343 /* Append the new text */
\r
7344 sel.cpMin = exlen;
\r
7345 sel.cpMax = exlen;
\r
7346 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
7347 SendMessage(hText, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&consoleCF);
\r
7348 SendMessage(hText, EM_REPLACESEL, 0, (LPARAM) buf);
\r
7349 if (forceVisible || exlen == 0 ||
\r
7350 (rect.left <= pEnd.x && pEnd.x < rect.right &&
\r
7351 rect.top <= pEnd.y && pEnd.y < rect.bottom)) {
\r
7352 /* Scroll to make new end of text visible if old end of text
\r
7353 was visible or new text is an echo of user typein */
\r
7354 sel.cpMin = 9999999;
\r
7355 sel.cpMax = 9999999;
\r
7356 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
7357 SendMessage(hText, EM_HIDESELECTION, FALSE, FALSE);
\r
7358 SendMessage(hText, EM_SCROLLCARET, 0, 0);
\r
7359 SendMessage(hText, EM_HIDESELECTION, TRUE, FALSE);
\r
7361 if (savesel.cpMax == exlen || forceVisible) {
\r
7362 /* Move insert point to new end of text if it was at the old
\r
7363 end of text or if the new text is an echo of user typein */
\r
7364 sel.cpMin = 9999999;
\r
7365 sel.cpMax = 9999999;
\r
7366 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
7368 /* Restore previous selection */
\r
7369 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&savesel);
\r
7371 SendMessage(hText, EM_HIDESELECTION, FALSE, FALSE);
\r
7378 DisplayHoldingsCount(HDC hdc, int x, int y, int rightAlign, int copyNumber)
\r
7382 COLORREF oldFg, oldBg;
\r
7387 snprintf(buf, sizeof(buf)/sizeof(buf[0]),"%d", copyNumber); else buf[0] = 0;
\r
7389 oldFg = SetTextColor(hdc, RGB(255, 255, 255)); /* white */
\r
7390 oldBg = SetBkColor(hdc, RGB(0, 0, 0)); /* black */
\r
7391 oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);
\r
7394 rect.right = x + squareSize;
\r
7396 rect.bottom = y + squareSize;
\r
7399 ExtTextOut(hdc, x + MESSAGE_LINE_LEFTMARGIN
\r
7400 + (rightAlign ? (squareSize*2)/3 : 0),
\r
7401 y, ETO_CLIPPED|ETO_OPAQUE,
\r
7402 &rect, str, strlen(str), NULL);
\r
7404 (void) SetTextColor(hdc, oldFg);
\r
7405 (void) SetBkColor(hdc, oldBg);
\r
7406 (void) SelectObject(hdc, oldFont);
\r
7410 DisplayAClock(HDC hdc, int timeRemaining, int highlight,
\r
7411 RECT *rect, char *color, char *flagFell)
\r
7415 COLORREF oldFg, oldBg;
\r
7418 if (appData.clockMode) {
\r
7420 snprintf(buf, sizeof(buf)/sizeof(buf[0]), "%c %s %s", color[0], TimeString(timeRemaining), flagFell);
\r
7422 snprintf(buf, sizeof(buf)/sizeof(buf[0]), "%s:%c%s %s", color, (logoHeight>0 ? 0 : ' '), TimeString(timeRemaining), flagFell);
\r
7429 oldFg = SetTextColor(hdc, RGB(255, 255, 255)); /* white */
\r
7430 oldBg = SetBkColor(hdc, RGB(0, 0, 0)); /* black */
\r
7432 oldFg = SetTextColor(hdc, RGB(0, 0, 0)); /* black */
\r
7433 oldBg = SetBkColor(hdc, RGB(255, 255, 255)); /* white */
\r
7435 oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);
\r
7439 ExtTextOut(hdc, rect->left + MESSAGE_LINE_LEFTMARGIN,
\r
7440 rect->top, ETO_CLIPPED|ETO_OPAQUE,
\r
7441 rect, str, strlen(str), NULL);
\r
7442 if(logoHeight > 0 && appData.clockMode) {
\r
7444 str += strlen(color)+2;
\r
7445 r.top = rect->top + logoHeight/2;
\r
7446 r.left = rect->left;
\r
7447 r.right = rect->right;
\r
7448 r.bottom = rect->bottom;
\r
7449 ExtTextOut(hdc, rect->left + MESSAGE_LINE_LEFTMARGIN,
\r
7450 r.top, ETO_CLIPPED|ETO_OPAQUE,
\r
7451 &r, str, strlen(str), NULL);
\r
7453 (void) SetTextColor(hdc, oldFg);
\r
7454 (void) SetBkColor(hdc, oldBg);
\r
7455 (void) SelectObject(hdc, oldFont);
\r
7460 DoReadFile(HANDLE hFile, char *buf, int count, DWORD *outCount,
\r
7466 if( count <= 0 ) {
\r
7467 if (appData.debugMode) {
\r
7468 fprintf( debugFP, "DoReadFile: trying to read past end of buffer, overflow = %d\n", count );
\r
7471 return ERROR_INVALID_USER_BUFFER;
\r
7474 ResetEvent(ovl->hEvent);
\r
7475 ovl->Offset = ovl->OffsetHigh = 0;
\r
7476 ok = ReadFile(hFile, buf, count, outCount, ovl);
\r
7480 err = GetLastError();
\r
7481 if (err == ERROR_IO_PENDING) {
\r
7482 ok = GetOverlappedResult(hFile, ovl, outCount, TRUE);
\r
7486 err = GetLastError();
\r
7493 DoWriteFile(HANDLE hFile, char *buf, int count, DWORD *outCount,
\r
7498 ResetEvent(ovl->hEvent);
\r
7499 ovl->Offset = ovl->OffsetHigh = 0;
\r
7500 ok = WriteFile(hFile, buf, count, outCount, ovl);
\r
7504 err = GetLastError();
\r
7505 if (err == ERROR_IO_PENDING) {
\r
7506 ok = GetOverlappedResult(hFile, ovl, outCount, TRUE);
\r
7510 err = GetLastError();
\r
7516 /* [AS] If input is line by line and a line exceed the buffer size, force an error */
\r
7517 void CheckForInputBufferFull( InputSource * is )
\r
7519 if( is->lineByLine && (is->next - is->buf) >= INPUT_SOURCE_BUF_SIZE ) {
\r
7520 /* Look for end of line */
\r
7521 char * p = is->buf;
\r
7523 while( p < is->next && *p != '\n' ) {
\r
7527 if( p >= is->next ) {
\r
7528 if (appData.debugMode) {
\r
7529 fprintf( debugFP, "Input line exceeded buffer size (source id=%lu)\n", is->id );
\r
7532 is->error = ERROR_BROKEN_PIPE; /* [AS] Just any non-successful code! */
\r
7533 is->count = (DWORD) -1;
\r
7534 is->next = is->buf;
\r
7540 InputThread(LPVOID arg)
\r
7545 is = (InputSource *) arg;
\r
7546 ovl.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
\r
7547 ovl.Internal = ovl.InternalHigh = ovl.Offset = ovl.OffsetHigh = 0;
\r
7548 while (is->hThread != NULL) {
\r
7549 is->error = DoReadFile(is->hFile, is->next,
\r
7550 INPUT_SOURCE_BUF_SIZE - (is->next - is->buf),
\r
7551 &is->count, &ovl);
\r
7552 if (is->error == NO_ERROR) {
\r
7553 is->next += is->count;
\r
7555 if (is->error == ERROR_BROKEN_PIPE) {
\r
7556 /* Correct for MS brain damage. EOF reading a pipe is not an error. */
\r
7559 is->count = (DWORD) -1;
\r
7560 /* [AS] The (is->count <= 0) check below is not useful for unsigned values! */
\r
7565 CheckForInputBufferFull( is );
\r
7567 SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);
\r
7569 if( is->count == ((DWORD) -1) ) break; /* [AS] */
\r
7571 if (is->count <= 0) break; /* Quit on EOF or error */
\r
7574 CloseHandle(ovl.hEvent);
\r
7575 CloseHandle(is->hFile);
\r
7577 if (appData.debugMode) {
\r
7578 fprintf( debugFP, "Input thread terminated (id=%lu, error=%d, count=%ld)\n", is->id, is->error, is->count );
\r
7585 /* Windows 95 beta 2 won't let you do overlapped i/o on a console or pipe */
\r
7587 NonOvlInputThread(LPVOID arg)
\r
7594 is = (InputSource *) arg;
\r
7595 while (is->hThread != NULL) {
\r
7596 is->error = ReadFile(is->hFile, is->next,
\r
7597 INPUT_SOURCE_BUF_SIZE - (is->next - is->buf),
\r
7598 &is->count, NULL) ? NO_ERROR : GetLastError();
\r
7599 if (is->error == NO_ERROR) {
\r
7600 /* Change CRLF to LF */
\r
7601 if (is->next > is->buf) {
\r
7603 i = is->count + 1;
\r
7611 if (prev == '\r' && *p == '\n') {
\r
7623 if (is->error == ERROR_BROKEN_PIPE) {
\r
7624 /* Correct for MS brain damage. EOF reading a pipe is not an error. */
\r
7627 is->count = (DWORD) -1;
\r
7631 CheckForInputBufferFull( is );
\r
7633 SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);
\r
7635 if( is->count == ((DWORD) -1) ) break; /* [AS] */
\r
7637 if (is->count < 0) break; /* Quit on error */
\r
7639 CloseHandle(is->hFile);
\r
7644 SocketInputThread(LPVOID arg)
\r
7648 is = (InputSource *) arg;
\r
7649 while (is->hThread != NULL) {
\r
7650 is->count = recv(is->sock, is->buf, INPUT_SOURCE_BUF_SIZE, 0);
\r
7651 if ((int)is->count == SOCKET_ERROR) {
\r
7652 is->count = (DWORD) -1;
\r
7653 is->error = WSAGetLastError();
\r
7655 is->error = NO_ERROR;
\r
7656 is->next += is->count;
\r
7657 if (is->count == 0 && is->second == is) {
\r
7658 /* End of file on stderr; quit with no message */
\r
7662 SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);
\r
7664 if( is->count == ((DWORD) -1) ) break; /* [AS] */
\r
7666 if (is->count <= 0) break; /* Quit on EOF or error */
\r
7672 InputEvent(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
\r
7676 is = (InputSource *) lParam;
\r
7677 if (is->lineByLine) {
\r
7678 /* Feed in lines one by one */
\r
7679 char *p = is->buf;
\r
7681 while (q < is->next) {
\r
7682 if (*q++ == '\n') {
\r
7683 (is->func)(is, is->closure, p, q - p, NO_ERROR);
\r
7688 /* Move any partial line to the start of the buffer */
\r
7690 while (p < is->next) {
\r
7695 if (is->error != NO_ERROR || is->count == 0) {
\r
7696 /* Notify backend of the error. Note: If there was a partial
\r
7697 line at the end, it is not flushed through. */
\r
7698 (is->func)(is, is->closure, is->buf, is->count, is->error);
\r
7701 /* Feed in the whole chunk of input at once */
\r
7702 (is->func)(is, is->closure, is->buf, is->count, is->error);
\r
7703 is->next = is->buf;
\r
7707 /*---------------------------------------------------------------------------*\
\r
7709 * Menu enables. Used when setting various modes.
\r
7711 \*---------------------------------------------------------------------------*/
\r
7719 GreyRevert(Boolean grey)
\r
7720 { // [HGM] vari: for retracting variations in local mode
\r
7721 HMENU hmenu = GetMenu(hwndMain);
\r
7722 EnableMenuItem(hmenu, IDM_Revert, MF_BYCOMMAND|(grey ? MF_GRAYED : MF_ENABLED));
\r
7723 EnableMenuItem(hmenu, IDM_Annotate, MF_BYCOMMAND|(grey ? MF_GRAYED : MF_ENABLED));
\r
7727 SetMenuEnables(HMENU hmenu, Enables *enab)
\r
7729 while (enab->item > 0) {
\r
7730 (void) EnableMenuItem(hmenu, enab->item, enab->flags);
\r
7735 Enables gnuEnables[] = {
\r
7736 { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },
\r
7737 { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },
\r
7738 { IDM_IcsClient, MF_BYCOMMAND|MF_GRAYED },
\r
7739 { IDM_Accept, MF_BYCOMMAND|MF_GRAYED },
\r
7740 { IDM_Decline, MF_BYCOMMAND|MF_GRAYED },
\r
7741 { IDM_Rematch, MF_BYCOMMAND|MF_GRAYED },
\r
7742 { IDM_Adjourn, MF_BYCOMMAND|MF_GRAYED },
\r
7743 { IDM_StopExamining, MF_BYCOMMAND|MF_GRAYED },
\r
7744 { IDM_StopObserving, MF_BYCOMMAND|MF_GRAYED },
\r
7745 { IDM_Upload, MF_BYCOMMAND|MF_GRAYED },
\r
7746 { IDM_Revert, MF_BYCOMMAND|MF_GRAYED },
\r
7747 { IDM_Annotate, MF_BYCOMMAND|MF_GRAYED },
\r
7748 { IDM_NewChat, MF_BYCOMMAND|MF_GRAYED },
\r
7752 Enables icsEnables[] = {
\r
7753 { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },
\r
7754 { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },
\r
7755 { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },
\r
7756 { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },
\r
7757 { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },
\r
7758 { IDM_Match, MF_BYCOMMAND|MF_GRAYED },
\r
7759 { IDM_MachineBoth, MF_BYCOMMAND|MF_GRAYED },
\r
7760 { IDM_AnalysisMode, MF_BYCOMMAND|MF_ENABLED },
\r
7761 { IDM_AnalyzeFile, MF_BYCOMMAND|MF_GRAYED },
\r
7762 { IDM_TimeControl, MF_BYCOMMAND|MF_GRAYED },
\r
7763 { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },
\r
7764 { IDM_Hint, MF_BYCOMMAND|MF_GRAYED },
\r
7765 { IDM_Book, MF_BYCOMMAND|MF_GRAYED },
\r
7766 { IDM_IcsOptions, MF_BYCOMMAND|MF_ENABLED },
\r
7767 { IDM_Engine1Options, MF_BYCOMMAND|MF_GRAYED },
\r
7768 { IDM_Engine2Options, MF_BYCOMMAND|MF_GRAYED },
\r
7769 { IDM_Annotate, MF_BYCOMMAND|MF_GRAYED },
\r
7774 Enables zippyEnables[] = {
\r
7775 { IDM_MoveNow, MF_BYCOMMAND|MF_ENABLED },
\r
7776 { IDM_Hint, MF_BYCOMMAND|MF_ENABLED },
\r
7777 { IDM_Book, MF_BYCOMMAND|MF_ENABLED },
\r
7778 { IDM_Engine1Options, MF_BYCOMMAND|MF_ENABLED },
\r
7783 Enables ncpEnables[] = {
\r
7784 { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },
\r
7785 { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },
\r
7786 { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },
\r
7787 { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },
\r
7788 { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },
\r
7789 { IDM_Match, MF_BYCOMMAND|MF_GRAYED },
\r
7790 { IDM_AnalysisMode, MF_BYCOMMAND|MF_GRAYED },
\r
7791 { IDM_AnalyzeFile, MF_BYCOMMAND|MF_GRAYED },
\r
7792 { IDM_IcsClient, MF_BYCOMMAND|MF_GRAYED },
\r
7793 { ACTION_POS, MF_BYPOSITION|MF_GRAYED },
\r
7794 { IDM_Revert, MF_BYCOMMAND|MF_GRAYED },
\r
7795 { IDM_Annotate, MF_BYCOMMAND|MF_GRAYED },
\r
7796 { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },
\r
7797 { IDM_RetractMove, MF_BYCOMMAND|MF_GRAYED },
\r
7798 { IDM_TimeControl, MF_BYCOMMAND|MF_GRAYED },
\r
7799 { IDM_Hint, MF_BYCOMMAND|MF_GRAYED },
\r
7800 { IDM_Book, MF_BYCOMMAND|MF_GRAYED },
\r
7801 { IDM_MachineBoth, MF_BYCOMMAND|MF_GRAYED },
\r
7802 { IDM_NewChat, MF_BYCOMMAND|MF_GRAYED },
\r
7803 { IDM_Engine1Options, MF_BYCOMMAND|MF_GRAYED },
\r
7804 { IDM_Engine2Options, MF_BYCOMMAND|MF_GRAYED },
\r
7805 { IDM_Sounds, MF_BYCOMMAND|MF_GRAYED },
\r
7809 Enables trainingOnEnables[] = {
\r
7810 { IDM_EditComment, MF_BYCOMMAND|MF_GRAYED },
\r
7811 { IDM_Comment, MF_BYCOMMAND|MF_GRAYED },
\r
7812 { IDM_Pause, MF_BYCOMMAND|MF_GRAYED },
\r
7813 { IDM_Forward, MF_BYCOMMAND|MF_GRAYED },
\r
7814 { IDM_Backward, MF_BYCOMMAND|MF_GRAYED },
\r
7815 { IDM_ToEnd, MF_BYCOMMAND|MF_GRAYED },
\r
7816 { IDM_ToStart, MF_BYCOMMAND|MF_GRAYED },
\r
7817 { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },
\r
7818 { IDM_TruncateGame, MF_BYCOMMAND|MF_GRAYED },
\r
7822 Enables trainingOffEnables[] = {
\r
7823 { IDM_EditComment, MF_BYCOMMAND|MF_ENABLED },
\r
7824 { IDM_Comment, MF_BYCOMMAND|MF_ENABLED },
\r
7825 { IDM_Pause, MF_BYCOMMAND|MF_ENABLED },
\r
7826 { IDM_Forward, MF_BYCOMMAND|MF_ENABLED },
\r
7827 { IDM_Backward, MF_BYCOMMAND|MF_ENABLED },
\r
7828 { IDM_ToEnd, MF_BYCOMMAND|MF_ENABLED },
\r
7829 { IDM_ToStart, MF_BYCOMMAND|MF_ENABLED },
\r
7830 { IDM_MoveNow, MF_BYCOMMAND|MF_ENABLED },
\r
7831 { IDM_TruncateGame, MF_BYCOMMAND|MF_ENABLED },
\r
7835 /* These modify either ncpEnables or gnuEnables */
\r
7836 Enables cmailEnables[] = {
\r
7837 { IDM_MailMove, MF_BYCOMMAND|MF_ENABLED },
\r
7838 { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_ENABLED },
\r
7839 { ACTION_POS, MF_BYPOSITION|MF_ENABLED },
\r
7840 { IDM_CallFlag, MF_BYCOMMAND|MF_GRAYED },
\r
7841 { IDM_Draw, MF_BYCOMMAND|MF_ENABLED },
\r
7842 { IDM_Adjourn, MF_BYCOMMAND|MF_GRAYED },
\r
7843 { IDM_Abort, MF_BYCOMMAND|MF_GRAYED },
\r
7847 Enables machineThinkingEnables[] = {
\r
7848 { IDM_LoadGame, MF_BYCOMMAND|MF_GRAYED },
\r
7849 { IDM_LoadNextGame, MF_BYCOMMAND|MF_GRAYED },
\r
7850 { IDM_LoadPrevGame, MF_BYCOMMAND|MF_GRAYED },
\r
7851 { IDM_ReloadGame, MF_BYCOMMAND|MF_GRAYED },
\r
7852 { IDM_PasteGame, MF_BYCOMMAND|MF_GRAYED },
\r
7853 { IDM_LoadPosition, MF_BYCOMMAND|MF_GRAYED },
\r
7854 { IDM_LoadNextPosition, MF_BYCOMMAND|MF_GRAYED },
\r
7855 { IDM_LoadPrevPosition, MF_BYCOMMAND|MF_GRAYED },
\r
7856 { IDM_ReloadPosition, MF_BYCOMMAND|MF_GRAYED },
\r
7857 { IDM_PastePosition, MF_BYCOMMAND|MF_GRAYED },
\r
7858 { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },
\r
7859 { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },
\r
7860 { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },
\r
7861 { IDM_Match, MF_BYCOMMAND|MF_GRAYED },
\r
7862 { IDM_TypeInMove, MF_BYCOMMAND|MF_GRAYED },
\r
7863 { IDM_RetractMove, MF_BYCOMMAND|MF_GRAYED },
\r
7867 Enables userThinkingEnables[] = {
\r
7868 { IDM_LoadGame, MF_BYCOMMAND|MF_ENABLED },
\r
7869 { IDM_LoadNextGame, MF_BYCOMMAND|MF_ENABLED },
\r
7870 { IDM_LoadPrevGame, MF_BYCOMMAND|MF_ENABLED },
\r
7871 { IDM_ReloadGame, MF_BYCOMMAND|MF_ENABLED },
\r
7872 { IDM_PasteGame, MF_BYCOMMAND|MF_ENABLED },
\r
7873 { IDM_LoadPosition, MF_BYCOMMAND|MF_ENABLED },
\r
7874 { IDM_LoadNextPosition, MF_BYCOMMAND|MF_ENABLED },
\r
7875 { IDM_LoadPrevPosition, MF_BYCOMMAND|MF_ENABLED },
\r
7876 { IDM_ReloadPosition, MF_BYCOMMAND|MF_ENABLED },
\r
7877 { IDM_PastePosition, MF_BYCOMMAND|MF_ENABLED },
\r
7878 { IDM_MachineWhite, MF_BYCOMMAND|MF_ENABLED },
\r
7879 { IDM_MachineBlack, MF_BYCOMMAND|MF_ENABLED },
\r
7880 { IDM_TwoMachines, MF_BYCOMMAND|MF_ENABLED },
\r
7881 { IDM_Match, MF_BYCOMMAND|MF_ENABLED },
\r
7882 { IDM_TypeInMove, MF_BYCOMMAND|MF_ENABLED },
\r
7883 { IDM_RetractMove, MF_BYCOMMAND|MF_ENABLED },
\r
7887 /*---------------------------------------------------------------------------*\
\r
7889 * Front-end interface functions exported by XBoard.
\r
7890 * Functions appear in same order as prototypes in frontend.h.
\r
7892 \*---------------------------------------------------------------------------*/
\r
7896 static UINT prevChecked = 0;
\r
7897 static int prevPausing = 0;
\r
7900 if (pausing != prevPausing) {
\r
7901 prevPausing = pausing;
\r
7902 (void) CheckMenuItem(GetMenu(hwndMain), IDM_Pause,
\r
7903 MF_BYCOMMAND|(pausing ? MF_CHECKED : MF_UNCHECKED));
\r
7904 if (hwndPause) SetWindowText(hwndPause, pausing ? "C" : "P");
\r
7907 switch (gameMode) {
\r
7908 case BeginningOfGame:
\r
7909 if (appData.icsActive)
\r
7910 nowChecked = IDM_IcsClient;
\r
7911 else if (appData.noChessProgram)
\r
7912 nowChecked = IDM_EditGame;
\r
7914 nowChecked = IDM_MachineBlack;
\r
7916 case MachinePlaysBlack:
\r
7917 nowChecked = IDM_MachineBlack;
\r
7919 case MachinePlaysWhite:
\r
7920 nowChecked = IDM_MachineWhite;
\r
7922 case TwoMachinesPlay:
\r
7923 nowChecked = matchMode ? IDM_Match : IDM_TwoMachines; // [HGM] match
\r
7926 nowChecked = IDM_AnalysisMode;
\r
7929 nowChecked = IDM_AnalyzeFile;
\r
7932 nowChecked = IDM_EditGame;
\r
7934 case PlayFromGameFile:
\r
7935 nowChecked = IDM_LoadGame;
\r
7937 case EditPosition:
\r
7938 nowChecked = IDM_EditPosition;
\r
7941 nowChecked = IDM_Training;
\r
7943 case IcsPlayingWhite:
\r
7944 case IcsPlayingBlack:
\r
7945 case IcsObserving:
\r
7947 nowChecked = IDM_IcsClient;
\r
7954 if (prevChecked != 0)
\r
7955 (void) CheckMenuItem(GetMenu(hwndMain),
\r
7956 prevChecked, MF_BYCOMMAND|MF_UNCHECKED);
\r
7957 if (nowChecked != 0)
\r
7958 (void) CheckMenuItem(GetMenu(hwndMain),
\r
7959 nowChecked, MF_BYCOMMAND|MF_CHECKED);
\r
7961 if (nowChecked == IDM_LoadGame || nowChecked == IDM_Training) {
\r
7962 (void) EnableMenuItem(GetMenu(hwndMain), IDM_Training,
\r
7963 MF_BYCOMMAND|MF_ENABLED);
\r
7965 (void) EnableMenuItem(GetMenu(hwndMain),
\r
7966 IDM_Training, MF_BYCOMMAND|MF_GRAYED);
\r
7969 prevChecked = nowChecked;
\r
7971 /* [DM] icsEngineAnalyze - Do a sceure check too */
\r
7972 if (appData.icsActive) {
\r
7973 if (appData.icsEngineAnalyze) {
\r
7974 (void) CheckMenuItem(GetMenu(hwndMain), IDM_AnalysisMode,
\r
7975 MF_BYCOMMAND|MF_CHECKED);
\r
7977 (void) CheckMenuItem(GetMenu(hwndMain), IDM_AnalysisMode,
\r
7978 MF_BYCOMMAND|MF_UNCHECKED);
\r
7981 DisplayLogos(); // [HGM] logos: mode change could have altered logos
\r
7987 HMENU hmenu = GetMenu(hwndMain);
\r
7988 SetMenuEnables(hmenu, icsEnables);
\r
7989 EnableMenuItem(GetSubMenu(hmenu, OPTIONS_POS), IDM_IcsOptions,
\r
7990 MF_BYCOMMAND|MF_ENABLED);
\r
7992 if (appData.zippyPlay) {
\r
7993 SetMenuEnables(hmenu, zippyEnables);
\r
7994 if (!appData.noChessProgram) /* [DM] icsEngineAnalyze */
\r
7995 (void) EnableMenuItem(GetMenu(hwndMain), IDM_AnalysisMode,
\r
7996 MF_BYCOMMAND|MF_ENABLED);
\r
8004 SetMenuEnables(GetMenu(hwndMain), gnuEnables);
\r
8010 HMENU hmenu = GetMenu(hwndMain);
\r
8011 SetMenuEnables(hmenu, ncpEnables);
\r
8012 DrawMenuBar(hwndMain);
\r
8018 SetMenuEnables(GetMenu(hwndMain), cmailEnables);
\r
8022 SetTrainingModeOn()
\r
8025 SetMenuEnables(GetMenu(hwndMain), trainingOnEnables);
\r
8026 for (i = 0; i < N_BUTTONS; i++) {
\r
8027 if (buttonDesc[i].hwnd != NULL)
\r
8028 EnableWindow(buttonDesc[i].hwnd, FALSE);
\r
8033 VOID SetTrainingModeOff()
\r
8036 SetMenuEnables(GetMenu(hwndMain), trainingOffEnables);
\r
8037 for (i = 0; i < N_BUTTONS; i++) {
\r
8038 if (buttonDesc[i].hwnd != NULL)
\r
8039 EnableWindow(buttonDesc[i].hwnd, TRUE);
\r
8045 SetUserThinkingEnables()
\r
8047 SetMenuEnables(GetMenu(hwndMain), userThinkingEnables);
\r
8051 SetMachineThinkingEnables()
\r
8053 HMENU hMenu = GetMenu(hwndMain);
\r
8054 int flags = MF_BYCOMMAND|MF_ENABLED;
\r
8056 SetMenuEnables(hMenu, machineThinkingEnables);
\r
8058 if (gameMode == MachinePlaysBlack) {
\r
8059 (void)EnableMenuItem(hMenu, IDM_MachineBlack, flags);
\r
8060 } else if (gameMode == MachinePlaysWhite) {
\r
8061 (void)EnableMenuItem(hMenu, IDM_MachineWhite, flags);
\r
8062 } else if (gameMode == TwoMachinesPlay) {
\r
8063 (void)EnableMenuItem(hMenu, matchMode ? IDM_Match : IDM_TwoMachines, flags); // [HGM] match
\r
8069 DisplayTitle(char *str)
\r
8071 char title[MSG_SIZ], *host;
\r
8072 if (str[0] != NULLCHAR) {
\r
8073 safeStrCpy(title, str, sizeof(title)/sizeof(title[0]) );
\r
8074 } else if (appData.icsActive) {
\r
8075 if (appData.icsCommPort[0] != NULLCHAR)
\r
8078 host = appData.icsHost;
\r
8079 snprintf(title, MSG_SIZ, "%s: %s", szTitle, host);
\r
8080 } else if (appData.noChessProgram) {
\r
8081 safeStrCpy(title, szTitle, sizeof(title)/sizeof(title[0]) );
\r
8083 safeStrCpy(title, szTitle, sizeof(title)/sizeof(title[0]) );
\r
8084 strcat(title, ": ");
\r
8085 strcat(title, first.tidy);
\r
8087 SetWindowText(hwndMain, title);
\r
8092 DisplayMessage(char *str1, char *str2)
\r
8096 int remain = MESSAGE_TEXT_MAX - 1;
\r
8099 moveErrorMessageUp = FALSE; /* turned on later by caller if needed */
\r
8100 messageText[0] = NULLCHAR;
\r
8102 len = strlen(str1);
\r
8103 if (len > remain) len = remain;
\r
8104 strncpy(messageText, str1, len);
\r
8105 messageText[len] = NULLCHAR;
\r
8108 if (*str2 && remain >= 2) {
\r
8110 strcat(messageText, " ");
\r
8113 len = strlen(str2);
\r
8114 if (len > remain) len = remain;
\r
8115 strncat(messageText, str2, len);
\r
8117 messageText[MESSAGE_TEXT_MAX - 1] = NULLCHAR;
\r
8119 if (hwndMain == NULL || IsIconic(hwndMain)) return;
\r
8123 hdc = GetDC(hwndMain);
\r
8124 oldFont = SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);
\r
8125 ExtTextOut(hdc, messageRect.left, messageRect.top, ETO_CLIPPED|ETO_OPAQUE,
\r
8126 &messageRect, messageText, strlen(messageText), NULL);
\r
8127 (void) SelectObject(hdc, oldFont);
\r
8128 (void) ReleaseDC(hwndMain, hdc);
\r
8132 DisplayError(char *str, int error)
\r
8134 char buf[MSG_SIZ*2], buf2[MSG_SIZ];
\r
8138 safeStrCpy(buf, str, sizeof(buf)/sizeof(buf[0]) );
\r
8140 len = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,
\r
8141 NULL, error, LANG_NEUTRAL,
\r
8142 (LPSTR) buf2, MSG_SIZ, NULL);
\r
8144 snprintf(buf, 2*MSG_SIZ, "%s:\n%s", str, buf2);
\r
8146 ErrorMap *em = errmap;
\r
8147 while (em->err != 0 && em->err != error) em++;
\r
8148 if (em->err != 0) {
\r
8149 snprintf(buf, 2*MSG_SIZ, "%s:\n%s", str, em->msg);
\r
8151 snprintf(buf, 2*MSG_SIZ, "%s:\nError code %d", str, error);
\r
8156 ErrorPopUp(_("Error"), buf);
\r
8161 DisplayMoveError(char *str)
\r
8163 fromX = fromY = -1;
\r
8164 ClearHighlights();
\r
8165 DrawPosition(FALSE, NULL);
\r
8166 if (appData.popupMoveErrors) {
\r
8167 ErrorPopUp(_("Error"), str);
\r
8169 DisplayMessage(str, "");
\r
8170 moveErrorMessageUp = TRUE;
\r
8175 DisplayFatalError(char *str, int error, int exitStatus)
\r
8177 char buf[2*MSG_SIZ], buf2[MSG_SIZ];
\r
8179 char *label = exitStatus ? _("Fatal Error") : _("Exiting");
\r
8182 len = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,
\r
8183 NULL, error, LANG_NEUTRAL,
\r
8184 (LPSTR) buf2, MSG_SIZ, NULL);
\r
8186 snprintf(buf, 2*MSG_SIZ, "%s:\n%s", str, buf2);
\r
8188 ErrorMap *em = errmap;
\r
8189 while (em->err != 0 && em->err != error) em++;
\r
8190 if (em->err != 0) {
\r
8191 snprintf(buf, 2*MSG_SIZ, "%s:\n%s", str, em->msg);
\r
8193 snprintf(buf, 2*MSG_SIZ, "%s:\nError code %d", str, error);
\r
8198 if (appData.debugMode) {
\r
8199 fprintf(debugFP, "%s: %s\n", label, str);
\r
8201 if (appData.popupExitMessage) {
\r
8202 (void) MessageBox(hwndMain, str, label, MB_OK|
\r
8203 (exitStatus ? MB_ICONSTOP : MB_ICONINFORMATION));
\r
8205 ExitEvent(exitStatus);
\r
8210 DisplayInformation(char *str)
\r
8212 (void) MessageBox(hwndMain, str, _("Information"), MB_OK|MB_ICONINFORMATION);
\r
8217 DisplayNote(char *str)
\r
8219 ErrorPopUp(_("Note"), str);
\r
8224 char *title, *question, *replyPrefix;
\r
8229 QuestionDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
8231 static QuestionParams *qp;
\r
8232 char reply[MSG_SIZ];
\r
8235 switch (message) {
\r
8236 case WM_INITDIALOG:
\r
8237 qp = (QuestionParams *) lParam;
\r
8238 CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));
\r
8239 Translate(hDlg, DLG_Question);
\r
8240 SetWindowText(hDlg, qp->title);
\r
8241 SetDlgItemText(hDlg, OPT_QuestionText, qp->question);
\r
8242 SetFocus(GetDlgItem(hDlg, OPT_QuestionInput));
\r
8246 switch (LOWORD(wParam)) {
\r
8248 safeStrCpy(reply, qp->replyPrefix, sizeof(reply)/sizeof(reply[0]) );
\r
8249 if (*reply) strcat(reply, " ");
\r
8250 len = strlen(reply);
\r
8251 GetDlgItemText(hDlg, OPT_QuestionInput, reply + len, sizeof(reply) - len);
\r
8252 strcat(reply, "\n");
\r
8253 OutputToProcess(qp->pr, reply, strlen(reply), &err);
\r
8254 EndDialog(hDlg, TRUE);
\r
8255 if (err) DisplayFatalError(_("Error writing to chess program"), err, 1);
\r
8258 EndDialog(hDlg, FALSE);
\r
8269 AskQuestion(char* title, char *question, char *replyPrefix, ProcRef pr)
\r
8271 QuestionParams qp;
\r
8275 qp.question = question;
\r
8276 qp.replyPrefix = replyPrefix;
\r
8278 lpProc = MakeProcInstance((FARPROC)QuestionDialog, hInst);
\r
8279 DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_Question),
\r
8280 hwndMain, (DLGPROC)lpProc, (LPARAM)&qp);
\r
8281 FreeProcInstance(lpProc);
\r
8284 /* [AS] Pick FRC position */
\r
8285 LRESULT CALLBACK NewGameFRC_Proc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
8287 static int * lpIndexFRC;
\r
8293 case WM_INITDIALOG:
\r
8294 lpIndexFRC = (int *) lParam;
\r
8296 CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));
\r
8297 Translate(hDlg, DLG_NewGameFRC);
\r
8299 SendDlgItemMessage( hDlg, IDC_NFG_Edit, EM_SETLIMITTEXT, sizeof(buf)-1, 0 );
\r
8300 SetDlgItemInt( hDlg, IDC_NFG_Edit, *lpIndexFRC, TRUE );
\r
8301 SendDlgItemMessage( hDlg, IDC_NFG_Edit, EM_SETSEL, 0, -1 );
\r
8302 SetFocus(GetDlgItem(hDlg, IDC_NFG_Edit));
\r
8307 switch( LOWORD(wParam) ) {
\r
8309 *lpIndexFRC = GetDlgItemInt(hDlg, IDC_NFG_Edit, &index_is_ok, TRUE );
\r
8310 EndDialog( hDlg, 0 );
\r
8311 shuffleOpenings = TRUE; /* [HGM] shuffle: switch shuffling on for as long as we stay in current variant */
\r
8314 EndDialog( hDlg, 1 );
\r
8316 case IDC_NFG_Edit:
\r
8317 if( HIWORD(wParam) == EN_CHANGE ) {
\r
8318 GetDlgItemInt(hDlg, IDC_NFG_Edit, &index_is_ok, TRUE );
\r
8320 EnableWindow( GetDlgItem(hDlg, IDOK), index_is_ok );
\r
8323 case IDC_NFG_Random:
\r
8324 snprintf( buf, sizeof(buf)/sizeof(buf[0]), "%d", myrandom() ); /* [HGM] shuffle: no longer limit to 960 */
\r
8325 SetDlgItemText(hDlg, IDC_NFG_Edit, buf );
\r
8338 int index = appData.defaultFrcPosition;
\r
8339 FARPROC lpProc = MakeProcInstance( (FARPROC) NewGameFRC_Proc, hInst );
\r
8341 result = DialogBoxParam( hInst, MAKEINTRESOURCE(DLG_NewGameFRC), hwndMain, (DLGPROC)lpProc, (LPARAM)&index );
\r
8343 if( result == 0 ) {
\r
8344 appData.defaultFrcPosition = index;
\r
8350 /* [AS] Game list options. Refactored by HGM */
\r
8352 HWND gameListOptionsDialog;
\r
8354 // low-level front-end: clear text edit / list widget
\r
8358 SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_RESETCONTENT, 0, 0 );
\r
8361 // low-level front-end: clear text edit / list widget
\r
8363 GLT_DeSelectList()
\r
8365 SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_SETCURSEL, 0, 0 );
\r
8368 // low-level front-end: append line to text edit / list widget
\r
8370 GLT_AddToList( char *name )
\r
8373 SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_ADDSTRING, 0, (LPARAM) name );
\r
8377 // low-level front-end: get line from text edit / list widget
\r
8379 GLT_GetFromList( int index, char *name )
\r
8382 if( SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_GETTEXT, index, (LPARAM) name ) != LB_ERR )
\r
8388 void GLT_MoveSelection( HWND hDlg, int delta )
\r
8390 int idx1 = (int) SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETCURSEL, 0, 0 );
\r
8391 int idx2 = idx1 + delta;
\r
8392 int count = (int) SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETCOUNT, 0, 0 );
\r
8394 if( idx1 >=0 && idx1 < count && idx2 >= 0 && idx2 < count ) {
\r
8397 SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETTEXT, idx1, (LPARAM) buf );
\r
8398 SendDlgItemMessage( hDlg, IDC_GameListTags, LB_DELETESTRING, idx1, 0 );
\r
8399 SendDlgItemMessage( hDlg, IDC_GameListTags, LB_INSERTSTRING, idx2, (LPARAM) buf );
\r
8400 SendDlgItemMessage( hDlg, IDC_GameListTags, LB_SETCURSEL, idx2, 0 );
\r
8404 LRESULT CALLBACK GameListOptions_Proc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
8408 case WM_INITDIALOG:
\r
8409 gameListOptionsDialog = hDlg; // [HGM] pass through global to keep out off back-end
\r
8411 CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));
\r
8412 Translate(hDlg, DLG_GameListOptions);
\r
8414 /* Initialize list */
\r
8415 GLT_TagsToList( lpUserGLT );
\r
8417 SetFocus( GetDlgItem(hDlg, IDC_GameListTags) );
\r
8422 switch( LOWORD(wParam) ) {
\r
8425 EndDialog( hDlg, 0 );
\r
8428 EndDialog( hDlg, 1 );
\r
8431 case IDC_GLT_Default:
\r
8432 GLT_TagsToList( GLT_DEFAULT_TAGS );
\r
8435 case IDC_GLT_Restore:
\r
8436 GLT_TagsToList( appData.gameListTags );
\r
8440 GLT_MoveSelection( hDlg, -1 );
\r
8443 case IDC_GLT_Down:
\r
8444 GLT_MoveSelection( hDlg, +1 );
\r
8454 int GameListOptions()
\r
8457 FARPROC lpProc = MakeProcInstance( (FARPROC) GameListOptions_Proc, hInst );
\r
8459 safeStrCpy( lpUserGLT, appData.gameListTags ,LPUSERGLT_SIZE );
\r
8461 result = DialogBoxParam( hInst, MAKEINTRESOURCE(DLG_GameListOptions), hwndMain, (DLGPROC)lpProc, (LPARAM)lpUserGLT );
\r
8463 if( result == 0 ) {
\r
8464 /* [AS] Memory leak here! */
\r
8465 appData.gameListTags = strdup( lpUserGLT );
\r
8472 DisplayIcsInteractionTitle(char *str)
\r
8474 char consoleTitle[MSG_SIZ];
\r
8476 snprintf(consoleTitle, MSG_SIZ, "%s: %s", szConsoleTitle, str);
\r
8477 SetWindowText(hwndConsole, consoleTitle);
\r
8481 DrawPosition(int fullRedraw, Board board)
\r
8483 HDCDrawPosition(NULL, (BOOLEAN) fullRedraw, board);
\r
8486 void NotifyFrontendLogin()
\r
8489 UpdateICSWidth(GetDlgItem(hwndConsole, OPT_ConsoleText));
\r
8495 fromX = fromY = -1;
\r
8496 if (dragInfo.pos.x != -1 || dragInfo.pos.y != -1) {
\r
8497 dragInfo.pos.x = dragInfo.pos.y = -1;
\r
8498 dragInfo.pos.x = dragInfo.pos.y = -1;
\r
8499 dragInfo.lastpos = dragInfo.pos;
\r
8500 dragInfo.start.x = dragInfo.start.y = -1;
\r
8501 dragInfo.from = dragInfo.start;
\r
8503 DrawPosition(TRUE, NULL);
\r
8510 CommentPopUp(char *title, char *str)
\r
8512 HWND hwnd = GetActiveWindow();
\r
8513 EitherCommentPopUp(currentMove, title, str, FALSE); // [HGM] vari: fake move index, rather than 0
\r
8515 SetActiveWindow(hwnd);
\r
8519 CommentPopDown(void)
\r
8521 CheckMenuItem(GetMenu(hwndMain), IDM_Comment, MF_UNCHECKED);
\r
8522 if (commentDialog) {
\r
8523 ShowWindow(commentDialog, SW_HIDE);
\r
8525 commentUp = FALSE;
\r
8529 EditCommentPopUp(int index, char *title, char *str)
\r
8531 EitherCommentPopUp(index, title, str, TRUE);
\r
8538 MyPlaySound(&sounds[(int)SoundMove]);
\r
8541 VOID PlayIcsWinSound()
\r
8543 MyPlaySound(&sounds[(int)SoundIcsWin]);
\r
8546 VOID PlayIcsLossSound()
\r
8548 MyPlaySound(&sounds[(int)SoundIcsLoss]);
\r
8551 VOID PlayIcsDrawSound()
\r
8553 MyPlaySound(&sounds[(int)SoundIcsDraw]);
\r
8556 VOID PlayIcsUnfinishedSound()
\r
8558 MyPlaySound(&sounds[(int)SoundIcsUnfinished]);
\r
8564 MyPlaySound(&sounds[(int)SoundAlarm]);
\r
8572 consoleEcho = TRUE;
\r
8573 hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
8574 SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&consoleCF);
\r
8575 SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);
\r
8584 consoleEcho = FALSE;
\r
8585 hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
8586 /* This works OK: set text and background both to the same color */
\r
8588 cf.crTextColor = COLOR_ECHOOFF;
\r
8589 SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf);
\r
8590 SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, cf.crTextColor);
\r
8593 /* No Raw()...? */
\r
8595 void Colorize(ColorClass cc, int continuation)
\r
8597 currentColorClass = cc;
\r
8598 consoleCF.dwMask = CFM_COLOR|CFM_BOLD|CFM_ITALIC|CFM_UNDERLINE|CFM_STRIKEOUT;
\r
8599 consoleCF.crTextColor = textAttribs[cc].color;
\r
8600 consoleCF.dwEffects = textAttribs[cc].effects;
\r
8601 if (!continuation) MyPlaySound(&textAttribs[cc].sound);
\r
8607 static char buf[MSG_SIZ];
\r
8608 DWORD bufsiz = MSG_SIZ;
\r
8610 if(appData.userName != NULL && appData.userName[0] != 0) {
\r
8611 return appData.userName; /* [HGM] username: prefer name selected by user over his system login */
\r
8613 if (!GetUserName(buf, &bufsiz)) {
\r
8614 /*DisplayError("Error getting user name", GetLastError());*/
\r
8615 safeStrCpy(buf, _("User"), sizeof(buf)/sizeof(buf[0]) );
\r
8623 static char buf[MSG_SIZ];
\r
8624 DWORD bufsiz = MSG_SIZ;
\r
8626 if (!GetComputerName(buf, &bufsiz)) {
\r
8627 /*DisplayError("Error getting host name", GetLastError());*/
\r
8628 safeStrCpy(buf, _("Unknown"), sizeof(buf)/sizeof(buf[0]) );
\r
8635 ClockTimerRunning()
\r
8637 return clockTimerEvent != 0;
\r
8643 if (clockTimerEvent == 0) return FALSE;
\r
8644 KillTimer(hwndMain, clockTimerEvent);
\r
8645 clockTimerEvent = 0;
\r
8650 StartClockTimer(long millisec)
\r
8652 clockTimerEvent = SetTimer(hwndMain, (UINT) CLOCK_TIMER_ID,
\r
8653 (UINT) millisec, NULL);
\r
8657 DisplayWhiteClock(long timeRemaining, int highlight)
\r
8660 char *flag = whiteFlag && gameMode == TwoMachinesPlay ? "(!)" : "";
\r
8662 if(appData.noGUI) return;
\r
8663 hdc = GetDC(hwndMain);
\r
8664 if (!IsIconic(hwndMain)) {
\r
8665 DisplayAClock(hdc, timeRemaining, highlight,
\r
8666 flipClock ? &blackRect : &whiteRect, _("White"), flag);
\r
8668 if (highlight && iconCurrent == iconBlack) {
\r
8669 iconCurrent = iconWhite;
\r
8670 PostMessage(hwndMain, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);
\r
8671 if (IsIconic(hwndMain)) {
\r
8672 DrawIcon(hdc, 2, 2, iconCurrent);
\r
8675 (void) ReleaseDC(hwndMain, hdc);
\r
8677 PostMessage(hwndConsole, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);
\r
8681 DisplayBlackClock(long timeRemaining, int highlight)
\r
8684 char *flag = blackFlag && gameMode == TwoMachinesPlay ? "(!)" : "";
\r
8686 if(appData.noGUI) return;
\r
8687 hdc = GetDC(hwndMain);
\r
8688 if (!IsIconic(hwndMain)) {
\r
8689 DisplayAClock(hdc, timeRemaining, highlight,
\r
8690 flipClock ? &whiteRect : &blackRect, _("Black"), flag);
\r
8692 if (highlight && iconCurrent == iconWhite) {
\r
8693 iconCurrent = iconBlack;
\r
8694 PostMessage(hwndMain, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);
\r
8695 if (IsIconic(hwndMain)) {
\r
8696 DrawIcon(hdc, 2, 2, iconCurrent);
\r
8699 (void) ReleaseDC(hwndMain, hdc);
\r
8701 PostMessage(hwndConsole, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);
\r
8706 LoadGameTimerRunning()
\r
8708 return loadGameTimerEvent != 0;
\r
8712 StopLoadGameTimer()
\r
8714 if (loadGameTimerEvent == 0) return FALSE;
\r
8715 KillTimer(hwndMain, loadGameTimerEvent);
\r
8716 loadGameTimerEvent = 0;
\r
8721 StartLoadGameTimer(long millisec)
\r
8723 loadGameTimerEvent = SetTimer(hwndMain, (UINT) LOAD_GAME_TIMER_ID,
\r
8724 (UINT) millisec, NULL);
\r
8732 char fileTitle[MSG_SIZ];
\r
8734 defName = DefaultFileName(appData.oldSaveStyle ? "gam" : "pgn");
\r
8735 f = OpenFileDialog(hwndMain, "a", defName,
\r
8736 appData.oldSaveStyle ? "gam" : "pgn",
\r
8738 _("Save Game to File"), NULL, fileTitle, NULL);
\r
8740 SaveGame(f, 0, "");
\r
8747 ScheduleDelayedEvent(DelayedEventCallback cb, long millisec)
\r
8749 if (delayedTimerEvent != 0) {
\r
8750 if (appData.debugMode && cb != delayedTimerCallback) { // [HGM] alive: not too much debug
\r
8751 fprintf(debugFP, "ScheduleDelayedEvent: event already scheduled\n");
\r
8753 KillTimer(hwndMain, delayedTimerEvent);
\r
8754 delayedTimerEvent = 0;
\r
8755 if(delayedTimerCallback != cb) // [HGM] alive: do not "flush" same event, just postpone it
\r
8756 delayedTimerCallback();
\r
8758 delayedTimerCallback = cb;
\r
8759 delayedTimerEvent = SetTimer(hwndMain, (UINT) DELAYED_TIMER_ID,
\r
8760 (UINT) millisec, NULL);
\r
8763 DelayedEventCallback
\r
8766 if (delayedTimerEvent) {
\r
8767 return delayedTimerCallback;
\r
8774 CancelDelayedEvent()
\r
8776 if (delayedTimerEvent) {
\r
8777 KillTimer(hwndMain, delayedTimerEvent);
\r
8778 delayedTimerEvent = 0;
\r
8782 DWORD GetWin32Priority(int nice)
\r
8783 { // [HGM] nice: translate Unix nice() value to indows priority class. (Code stolen from Polyglot 1.4w11)
\r
8785 REALTIME_PRIORITY_CLASS 0x00000100
\r
8786 HIGH_PRIORITY_CLASS 0x00000080
\r
8787 ABOVE_NORMAL_PRIORITY_CLASS 0x00008000
\r
8788 NORMAL_PRIORITY_CLASS 0x00000020
\r
8789 BELOW_NORMAL_PRIORITY_CLASS 0x00004000
\r
8790 IDLE_PRIORITY_CLASS 0x00000040
\r
8792 if (nice < -15) return 0x00000080;
\r
8793 if (nice < 0) return 0x00008000;
\r
8794 if (nice == 0) return 0x00000020;
\r
8795 if (nice < 15) return 0x00004000;
\r
8796 return 0x00000040;
\r
8799 /* Start a child process running the given program.
\r
8800 The process's standard output can be read from "from", and its
\r
8801 standard input can be written to "to".
\r
8802 Exit with fatal error if anything goes wrong.
\r
8803 Returns an opaque pointer that can be used to destroy the process
\r
8807 StartChildProcess(char *cmdLine, char *dir, ProcRef *pr)
\r
8809 #define BUFSIZE 4096
\r
8811 HANDLE hChildStdinRd, hChildStdinWr,
\r
8812 hChildStdoutRd, hChildStdoutWr;
\r
8813 HANDLE hChildStdinWrDup, hChildStdoutRdDup;
\r
8814 SECURITY_ATTRIBUTES saAttr;
\r
8816 PROCESS_INFORMATION piProcInfo;
\r
8817 STARTUPINFO siStartInfo;
\r
8819 char buf[MSG_SIZ];
\r
8822 if (appData.debugMode) {
\r
8823 fprintf(debugFP, "StartChildProcess (dir=\"%s\") %s\n", dir, cmdLine);
\r
8828 /* Set the bInheritHandle flag so pipe handles are inherited. */
\r
8829 saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);
\r
8830 saAttr.bInheritHandle = TRUE;
\r
8831 saAttr.lpSecurityDescriptor = NULL;
\r
8834 * The steps for redirecting child's STDOUT:
\r
8835 * 1. Create anonymous pipe to be STDOUT for child.
\r
8836 * 2. Create a noninheritable duplicate of read handle,
\r
8837 * and close the inheritable read handle.
\r
8840 /* Create a pipe for the child's STDOUT. */
\r
8841 if (! CreatePipe(&hChildStdoutRd, &hChildStdoutWr, &saAttr, 0)) {
\r
8842 return GetLastError();
\r
8845 /* Duplicate the read handle to the pipe, so it is not inherited. */
\r
8846 fSuccess = DuplicateHandle(GetCurrentProcess(), hChildStdoutRd,
\r
8847 GetCurrentProcess(), &hChildStdoutRdDup, 0,
\r
8848 FALSE, /* not inherited */
\r
8849 DUPLICATE_SAME_ACCESS);
\r
8851 return GetLastError();
\r
8853 CloseHandle(hChildStdoutRd);
\r
8856 * The steps for redirecting child's STDIN:
\r
8857 * 1. Create anonymous pipe to be STDIN for child.
\r
8858 * 2. Create a noninheritable duplicate of write handle,
\r
8859 * and close the inheritable write handle.
\r
8862 /* Create a pipe for the child's STDIN. */
\r
8863 if (! CreatePipe(&hChildStdinRd, &hChildStdinWr, &saAttr, 0)) {
\r
8864 return GetLastError();
\r
8867 /* Duplicate the write handle to the pipe, so it is not inherited. */
\r
8868 fSuccess = DuplicateHandle(GetCurrentProcess(), hChildStdinWr,
\r
8869 GetCurrentProcess(), &hChildStdinWrDup, 0,
\r
8870 FALSE, /* not inherited */
\r
8871 DUPLICATE_SAME_ACCESS);
\r
8873 return GetLastError();
\r
8875 CloseHandle(hChildStdinWr);
\r
8877 /* Arrange to (1) look in dir for the child .exe file, and
\r
8878 * (2) have dir be the child's working directory. Interpret
\r
8879 * dir relative to the directory WinBoard loaded from. */
\r
8880 GetCurrentDirectory(MSG_SIZ, buf);
\r
8881 SetCurrentDirectory(installDir);
\r
8882 SetCurrentDirectory(dir);
\r
8884 /* Now create the child process. */
\r
8886 siStartInfo.cb = sizeof(STARTUPINFO);
\r
8887 siStartInfo.lpReserved = NULL;
\r
8888 siStartInfo.lpDesktop = NULL;
\r
8889 siStartInfo.lpTitle = NULL;
\r
8890 siStartInfo.dwFlags = STARTF_USESTDHANDLES;
\r
8891 siStartInfo.cbReserved2 = 0;
\r
8892 siStartInfo.lpReserved2 = NULL;
\r
8893 siStartInfo.hStdInput = hChildStdinRd;
\r
8894 siStartInfo.hStdOutput = hChildStdoutWr;
\r
8895 siStartInfo.hStdError = hChildStdoutWr;
\r
8897 fSuccess = CreateProcess(NULL,
\r
8898 cmdLine, /* command line */
\r
8899 NULL, /* process security attributes */
\r
8900 NULL, /* primary thread security attrs */
\r
8901 TRUE, /* handles are inherited */
\r
8902 DETACHED_PROCESS|CREATE_NEW_PROCESS_GROUP,
\r
8903 NULL, /* use parent's environment */
\r
8905 &siStartInfo, /* STARTUPINFO pointer */
\r
8906 &piProcInfo); /* receives PROCESS_INFORMATION */
\r
8908 err = GetLastError();
\r
8909 SetCurrentDirectory(buf); /* return to prev directory */
\r
8914 if (appData.niceEngines){ // [HGM] nice: adjust engine proc priority
\r
8915 if(appData.debugMode) fprintf(debugFP, "nice engine proc to %d\n", appData.niceEngines);
\r
8916 SetPriorityClass(piProcInfo.hProcess, GetWin32Priority(appData.niceEngines));
\r
8919 /* Close the handles we don't need in the parent */
\r
8920 CloseHandle(piProcInfo.hThread);
\r
8921 CloseHandle(hChildStdinRd);
\r
8922 CloseHandle(hChildStdoutWr);
\r
8924 /* Prepare return value */
\r
8925 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
\r
8926 cp->kind = CPReal;
\r
8927 cp->hProcess = piProcInfo.hProcess;
\r
8928 cp->pid = piProcInfo.dwProcessId;
\r
8929 cp->hFrom = hChildStdoutRdDup;
\r
8930 cp->hTo = hChildStdinWrDup;
\r
8932 *pr = (void *) cp;
\r
8934 /* Klaus Friedel says that this Sleep solves a problem under Windows
\r
8935 2000 where engines sometimes don't see the initial command(s)
\r
8936 from WinBoard and hang. I don't understand how that can happen,
\r
8937 but the Sleep is harmless, so I've put it in. Others have also
\r
8938 reported what may be the same problem, so hopefully this will fix
\r
8939 it for them too. */
\r
8947 DestroyChildProcess(ProcRef pr, int/*boolean*/ signal)
\r
8949 ChildProc *cp; int result;
\r
8951 cp = (ChildProc *) pr;
\r
8952 if (cp == NULL) return;
\r
8954 switch (cp->kind) {
\r
8956 /* TerminateProcess is considered harmful, so... */
\r
8957 CloseHandle(cp->hTo); /* Closing this will give the child an EOF and hopefully kill it */
\r
8958 if (cp->hFrom) CloseHandle(cp->hFrom); /* if NULL, InputThread will close it */
\r
8959 /* The following doesn't work because the chess program
\r
8960 doesn't "have the same console" as WinBoard. Maybe
\r
8961 we could arrange for this even though neither WinBoard
\r
8962 nor the chess program uses a console for stdio? */
\r
8963 /*!!if (signal) GenerateConsoleCtrlEvent(CTRL_BREAK_EVENT, cp->pid);*/
\r
8965 /* [AS] Special termination modes for misbehaving programs... */
\r
8966 if( signal == 9 ) {
\r
8967 result = TerminateProcess( cp->hProcess, 0 );
\r
8969 if ( appData.debugMode) {
\r
8970 fprintf( debugFP, "Terminating process %lu, result=%d\n", cp->pid, result );
\r
8973 else if( signal == 10 ) {
\r
8974 DWORD dw = WaitForSingleObject( cp->hProcess, 3*1000 ); // Wait 3 seconds at most
\r
8976 if( dw != WAIT_OBJECT_0 ) {
\r
8977 result = TerminateProcess( cp->hProcess, 0 );
\r
8979 if ( appData.debugMode) {
\r
8980 fprintf( debugFP, "Process %lu still alive after timeout, killing... result=%d\n", cp->pid, result );
\r
8986 CloseHandle(cp->hProcess);
\r
8990 if (cp->hFrom) CloseHandle(cp->hFrom);
\r
8994 closesocket(cp->sock);
\r
8999 if (signal) send(cp->sock2, "\017", 1, 0); /* 017 = 15 = SIGTERM */
\r
9000 closesocket(cp->sock);
\r
9001 closesocket(cp->sock2);
\r
9009 InterruptChildProcess(ProcRef pr)
\r
9013 cp = (ChildProc *) pr;
\r
9014 if (cp == NULL) return;
\r
9015 switch (cp->kind) {
\r
9017 /* The following doesn't work because the chess program
\r
9018 doesn't "have the same console" as WinBoard. Maybe
\r
9019 we could arrange for this even though neither WinBoard
\r
9020 nor the chess program uses a console for stdio */
\r
9021 /*!!GenerateConsoleCtrlEvent(CTRL_C_EVENT, cp->pid);*/
\r
9026 /* Can't interrupt */
\r
9030 send(cp->sock2, "\002", 1, 0); /* 2 = SIGINT */
\r
9037 OpenTelnet(char *host, char *port, ProcRef *pr)
\r
9039 char cmdLine[MSG_SIZ];
\r
9041 if (port[0] == NULLCHAR) {
\r
9042 snprintf(cmdLine, MSG_SIZ, "%s %s", appData.telnetProgram, host);
\r
9044 snprintf(cmdLine, MSG_SIZ, "%s %s %s", appData.telnetProgram, host, port);
\r
9046 return StartChildProcess(cmdLine, "", pr);
\r
9050 /* Code to open TCP sockets */
\r
9053 OpenTCP(char *host, char *port, ProcRef *pr)
\r
9058 struct sockaddr_in sa, mysa;
\r
9059 struct hostent FAR *hp;
\r
9060 unsigned short uport;
\r
9061 WORD wVersionRequested;
\r
9064 /* Initialize socket DLL */
\r
9065 wVersionRequested = MAKEWORD(1, 1);
\r
9066 err = WSAStartup(wVersionRequested, &wsaData);
\r
9067 if (err != 0) return err;
\r
9070 if ((s = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {
\r
9071 err = WSAGetLastError();
\r
9076 /* Bind local address using (mostly) don't-care values.
\r
9078 memset((char *) &mysa, 0, sizeof(struct sockaddr_in));
\r
9079 mysa.sin_family = AF_INET;
\r
9080 mysa.sin_addr.s_addr = INADDR_ANY;
\r
9081 uport = (unsigned short) 0;
\r
9082 mysa.sin_port = htons(uport);
\r
9083 if (bind(s, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))
\r
9084 == SOCKET_ERROR) {
\r
9085 err = WSAGetLastError();
\r
9090 /* Resolve remote host name */
\r
9091 memset((char *) &sa, 0, sizeof(struct sockaddr_in));
\r
9092 if (!(hp = gethostbyname(host))) {
\r
9093 unsigned int b0, b1, b2, b3;
\r
9095 err = WSAGetLastError();
\r
9097 if (sscanf(host, "%u.%u.%u.%u", &b0, &b1, &b2, &b3) == 4) {
\r
9098 hp = (struct hostent *) calloc(1, sizeof(struct hostent));
\r
9099 hp->h_addrtype = AF_INET;
\r
9101 hp->h_addr_list = (char **) calloc(2, sizeof(char *));
\r
9102 hp->h_addr_list[0] = (char *) malloc(4);
\r
9103 hp->h_addr_list[0][0] = (char) b0;
\r
9104 hp->h_addr_list[0][1] = (char) b1;
\r
9105 hp->h_addr_list[0][2] = (char) b2;
\r
9106 hp->h_addr_list[0][3] = (char) b3;
\r
9112 sa.sin_family = hp->h_addrtype;
\r
9113 uport = (unsigned short) atoi(port);
\r
9114 sa.sin_port = htons(uport);
\r
9115 memcpy((char *) &sa.sin_addr, hp->h_addr, hp->h_length);
\r
9117 /* Make connection */
\r
9118 if (connect(s, (struct sockaddr *) &sa,
\r
9119 sizeof(struct sockaddr_in)) == SOCKET_ERROR) {
\r
9120 err = WSAGetLastError();
\r
9125 /* Prepare return value */
\r
9126 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
\r
9127 cp->kind = CPSock;
\r
9129 *pr = (ProcRef *) cp;
\r
9135 OpenCommPort(char *name, ProcRef *pr)
\r
9140 char fullname[MSG_SIZ];
\r
9142 if (*name != '\\')
\r
9143 snprintf(fullname, MSG_SIZ, "\\\\.\\%s", name);
\r
9145 safeStrCpy(fullname, name, sizeof(fullname)/sizeof(fullname[0]) );
\r
9147 h = CreateFile(name, GENERIC_READ | GENERIC_WRITE,
\r
9148 0, NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL);
\r
9149 if (h == (HANDLE) -1) {
\r
9150 return GetLastError();
\r
9154 if (!SetCommState(h, (LPDCB) &dcb)) return GetLastError();
\r
9156 /* Accumulate characters until a 100ms pause, then parse */
\r
9157 ct.ReadIntervalTimeout = 100;
\r
9158 ct.ReadTotalTimeoutMultiplier = 0;
\r
9159 ct.ReadTotalTimeoutConstant = 0;
\r
9160 ct.WriteTotalTimeoutMultiplier = 0;
\r
9161 ct.WriteTotalTimeoutConstant = 0;
\r
9162 if (!SetCommTimeouts(h, (LPCOMMTIMEOUTS) &ct)) return GetLastError();
\r
9164 /* Prepare return value */
\r
9165 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
\r
9166 cp->kind = CPComm;
\r
9169 *pr = (ProcRef *) cp;
\r
9175 OpenLoopback(ProcRef *pr)
\r
9177 DisplayFatalError(_("Not implemented"), 0, 1);
\r
9183 OpenRcmd(char* host, char* user, char* cmd, ProcRef* pr)
\r
9188 struct sockaddr_in sa, mysa;
\r
9189 struct hostent FAR *hp;
\r
9190 unsigned short uport;
\r
9191 WORD wVersionRequested;
\r
9194 char stderrPortStr[MSG_SIZ];
\r
9196 /* Initialize socket DLL */
\r
9197 wVersionRequested = MAKEWORD(1, 1);
\r
9198 err = WSAStartup(wVersionRequested, &wsaData);
\r
9199 if (err != 0) return err;
\r
9201 /* Resolve remote host name */
\r
9202 memset((char *) &sa, 0, sizeof(struct sockaddr_in));
\r
9203 if (!(hp = gethostbyname(host))) {
\r
9204 unsigned int b0, b1, b2, b3;
\r
9206 err = WSAGetLastError();
\r
9208 if (sscanf(host, "%u.%u.%u.%u", &b0, &b1, &b2, &b3) == 4) {
\r
9209 hp = (struct hostent *) calloc(1, sizeof(struct hostent));
\r
9210 hp->h_addrtype = AF_INET;
\r
9212 hp->h_addr_list = (char **) calloc(2, sizeof(char *));
\r
9213 hp->h_addr_list[0] = (char *) malloc(4);
\r
9214 hp->h_addr_list[0][0] = (char) b0;
\r
9215 hp->h_addr_list[0][1] = (char) b1;
\r
9216 hp->h_addr_list[0][2] = (char) b2;
\r
9217 hp->h_addr_list[0][3] = (char) b3;
\r
9223 sa.sin_family = hp->h_addrtype;
\r
9224 uport = (unsigned short) 514;
\r
9225 sa.sin_port = htons(uport);
\r
9226 memcpy((char *) &sa.sin_addr, hp->h_addr, hp->h_length);
\r
9228 /* Bind local socket to unused "privileged" port address
\r
9230 s = INVALID_SOCKET;
\r
9231 memset((char *) &mysa, 0, sizeof(struct sockaddr_in));
\r
9232 mysa.sin_family = AF_INET;
\r
9233 mysa.sin_addr.s_addr = INADDR_ANY;
\r
9234 for (fromPort = 1023;; fromPort--) {
\r
9235 if (fromPort < 0) {
\r
9237 return WSAEADDRINUSE;
\r
9239 if (s == INVALID_SOCKET) {
\r
9240 if ((s = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {
\r
9241 err = WSAGetLastError();
\r
9246 uport = (unsigned short) fromPort;
\r
9247 mysa.sin_port = htons(uport);
\r
9248 if (bind(s, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))
\r
9249 == SOCKET_ERROR) {
\r
9250 err = WSAGetLastError();
\r
9251 if (err == WSAEADDRINUSE) continue;
\r
9255 if (connect(s, (struct sockaddr *) &sa,
\r
9256 sizeof(struct sockaddr_in)) == SOCKET_ERROR) {
\r
9257 err = WSAGetLastError();
\r
9258 if (err == WSAEADDRINUSE) {
\r
9269 /* Bind stderr local socket to unused "privileged" port address
\r
9271 s2 = INVALID_SOCKET;
\r
9272 memset((char *) &mysa, 0, sizeof(struct sockaddr_in));
\r
9273 mysa.sin_family = AF_INET;
\r
9274 mysa.sin_addr.s_addr = INADDR_ANY;
\r
9275 for (fromPort = 1023;; fromPort--) {
\r
9276 if (fromPort == prevStderrPort) continue; // don't reuse port
\r
9277 if (fromPort < 0) {
\r
9278 (void) closesocket(s);
\r
9280 return WSAEADDRINUSE;
\r
9282 if (s2 == INVALID_SOCKET) {
\r
9283 if ((s2 = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {
\r
9284 err = WSAGetLastError();
\r
9290 uport = (unsigned short) fromPort;
\r
9291 mysa.sin_port = htons(uport);
\r
9292 if (bind(s2, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))
\r
9293 == SOCKET_ERROR) {
\r
9294 err = WSAGetLastError();
\r
9295 if (err == WSAEADDRINUSE) continue;
\r
9296 (void) closesocket(s);
\r
9300 if (listen(s2, 1) == SOCKET_ERROR) {
\r
9301 err = WSAGetLastError();
\r
9302 if (err == WSAEADDRINUSE) {
\r
9304 s2 = INVALID_SOCKET;
\r
9307 (void) closesocket(s);
\r
9308 (void) closesocket(s2);
\r
9314 prevStderrPort = fromPort; // remember port used
\r
9315 snprintf(stderrPortStr, MSG_SIZ, "%d", fromPort);
\r
9317 if (send(s, stderrPortStr, strlen(stderrPortStr) + 1, 0) == SOCKET_ERROR) {
\r
9318 err = WSAGetLastError();
\r
9319 (void) closesocket(s);
\r
9320 (void) closesocket(s2);
\r
9325 if (send(s, UserName(), strlen(UserName()) + 1, 0) == SOCKET_ERROR) {
\r
9326 err = WSAGetLastError();
\r
9327 (void) closesocket(s);
\r
9328 (void) closesocket(s2);
\r
9332 if (*user == NULLCHAR) user = UserName();
\r
9333 if (send(s, user, strlen(user) + 1, 0) == SOCKET_ERROR) {
\r
9334 err = WSAGetLastError();
\r
9335 (void) closesocket(s);
\r
9336 (void) closesocket(s2);
\r
9340 if (send(s, cmd, strlen(cmd) + 1, 0) == SOCKET_ERROR) {
\r
9341 err = WSAGetLastError();
\r
9342 (void) closesocket(s);
\r
9343 (void) closesocket(s2);
\r
9348 if ((s3 = accept(s2, NULL, NULL)) == INVALID_SOCKET) {
\r
9349 err = WSAGetLastError();
\r
9350 (void) closesocket(s);
\r
9351 (void) closesocket(s2);
\r
9355 (void) closesocket(s2); /* Stop listening */
\r
9357 /* Prepare return value */
\r
9358 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
\r
9359 cp->kind = CPRcmd;
\r
9362 *pr = (ProcRef *) cp;
\r
9369 AddInputSource(ProcRef pr, int lineByLine,
\r
9370 InputCallback func, VOIDSTAR closure)
\r
9372 InputSource *is, *is2 = NULL;
\r
9373 ChildProc *cp = (ChildProc *) pr;
\r
9375 is = (InputSource *) calloc(1, sizeof(InputSource));
\r
9376 is->lineByLine = lineByLine;
\r
9378 is->closure = closure;
\r
9379 is->second = NULL;
\r
9380 is->next = is->buf;
\r
9381 if (pr == NoProc) {
\r
9382 is->kind = CPReal;
\r
9383 consoleInputSource = is;
\r
9385 is->kind = cp->kind;
\r
9387 [AS] Try to avoid a race condition if the thread is given control too early:
\r
9388 we create all threads suspended so that the is->hThread variable can be
\r
9389 safely assigned, then let the threads start with ResumeThread.
\r
9391 switch (cp->kind) {
\r
9393 is->hFile = cp->hFrom;
\r
9394 cp->hFrom = NULL; /* now owned by InputThread */
\r
9396 CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) NonOvlInputThread,
\r
9397 (LPVOID) is, CREATE_SUSPENDED, &is->id);
\r
9401 is->hFile = cp->hFrom;
\r
9402 cp->hFrom = NULL; /* now owned by InputThread */
\r
9404 CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) InputThread,
\r
9405 (LPVOID) is, CREATE_SUSPENDED, &is->id);
\r
9409 is->sock = cp->sock;
\r
9411 CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,
\r
9412 (LPVOID) is, CREATE_SUSPENDED, &is->id);
\r
9416 is2 = (InputSource *) calloc(1, sizeof(InputSource));
\r
9418 is->sock = cp->sock;
\r
9420 is2->sock = cp->sock2;
\r
9421 is2->second = is2;
\r
9423 CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,
\r
9424 (LPVOID) is, CREATE_SUSPENDED, &is->id);
\r
9426 CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,
\r
9427 (LPVOID) is2, CREATE_SUSPENDED, &is2->id);
\r
9431 if( is->hThread != NULL ) {
\r
9432 ResumeThread( is->hThread );
\r
9435 if( is2 != NULL && is2->hThread != NULL ) {
\r
9436 ResumeThread( is2->hThread );
\r
9440 return (InputSourceRef) is;
\r
9444 RemoveInputSource(InputSourceRef isr)
\r
9448 is = (InputSource *) isr;
\r
9449 is->hThread = NULL; /* tell thread to stop */
\r
9450 CloseHandle(is->hThread);
\r
9451 if (is->second != NULL) {
\r
9452 is->second->hThread = NULL;
\r
9453 CloseHandle(is->second->hThread);
\r
9457 int no_wrap(char *message, int count)
\r
9459 ConsoleOutput(message, count, FALSE);
\r
9464 OutputToProcess(ProcRef pr, char *message, int count, int *outError)
\r
9467 int outCount = SOCKET_ERROR;
\r
9468 ChildProc *cp = (ChildProc *) pr;
\r
9469 static OVERLAPPED ovl;
\r
9470 static int line = 0;
\r
9474 if (appData.noJoin || !appData.useInternalWrap)
\r
9475 return no_wrap(message, count);
\r
9478 int width = get_term_width();
\r
9479 int len = wrap(NULL, message, count, width, &line);
\r
9480 char *msg = malloc(len);
\r
9484 return no_wrap(message, count);
\r
9487 dbgchk = wrap(msg, message, count, width, &line);
\r
9488 if (dbgchk != len && appData.debugMode)
\r
9489 fprintf(debugFP, "wrap(): dbgchk(%d) != len(%d)\n", dbgchk, len);
\r
9490 ConsoleOutput(msg, len, FALSE);
\r
9497 if (ovl.hEvent == NULL) {
\r
9498 ovl.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
\r
9500 ovl.Internal = ovl.InternalHigh = ovl.Offset = ovl.OffsetHigh = 0;
\r
9502 switch (cp->kind) {
\r
9505 outCount = send(cp->sock, message, count, 0);
\r
9506 if (outCount == SOCKET_ERROR) {
\r
9507 *outError = WSAGetLastError();
\r
9509 *outError = NO_ERROR;
\r
9514 if (WriteFile(((ChildProc *)pr)->hTo, message, count,
\r
9515 &dOutCount, NULL)) {
\r
9516 *outError = NO_ERROR;
\r
9517 outCount = (int) dOutCount;
\r
9519 *outError = GetLastError();
\r
9524 *outError = DoWriteFile(((ChildProc *)pr)->hTo, message, count,
\r
9525 &dOutCount, &ovl);
\r
9526 if (*outError == NO_ERROR) {
\r
9527 outCount = (int) dOutCount;
\r
9535 OutputToProcessDelayed(ProcRef pr, char *message, int count, int *outError,
\r
9538 /* Ignore delay, not implemented for WinBoard */
\r
9539 return OutputToProcess(pr, message, count, outError);
\r
9544 CmailSigHandlerCallBack(InputSourceRef isr, VOIDSTAR closure,
\r
9545 char *buf, int count, int error)
\r
9547 DisplayFatalError(_("Not implemented"), 0, 1);
\r
9550 /* see wgamelist.c for Game List functions */
\r
9551 /* see wedittags.c for Edit Tags functions */
\r
9558 char buf[MSG_SIZ];
\r
9561 if (SearchPath(installDir, appData.icsLogon, NULL, MSG_SIZ, buf, &dummy)) {
\r
9562 f = fopen(buf, "r");
\r
9564 ProcessICSInitScript(f);
\r
9572 StartAnalysisClock()
\r
9574 if (analysisTimerEvent) return;
\r
9575 analysisTimerEvent = SetTimer(hwndMain, (UINT) ANALYSIS_TIMER_ID,
\r
9576 (UINT) 2000, NULL);
\r
9580 SetHighlights(int fromX, int fromY, int toX, int toY)
\r
9582 highlightInfo.sq[0].x = fromX;
\r
9583 highlightInfo.sq[0].y = fromY;
\r
9584 highlightInfo.sq[1].x = toX;
\r
9585 highlightInfo.sq[1].y = toY;
\r
9591 highlightInfo.sq[0].x = highlightInfo.sq[0].y =
\r
9592 highlightInfo.sq[1].x = highlightInfo.sq[1].y = -1;
\r
9596 SetPremoveHighlights(int fromX, int fromY, int toX, int toY)
\r
9598 premoveHighlightInfo.sq[0].x = fromX;
\r
9599 premoveHighlightInfo.sq[0].y = fromY;
\r
9600 premoveHighlightInfo.sq[1].x = toX;
\r
9601 premoveHighlightInfo.sq[1].y = toY;
\r
9605 ClearPremoveHighlights()
\r
9607 premoveHighlightInfo.sq[0].x = premoveHighlightInfo.sq[0].y =
\r
9608 premoveHighlightInfo.sq[1].x = premoveHighlightInfo.sq[1].y = -1;
\r
9612 ShutDownFrontEnd()
\r
9614 if (saveSettingsOnExit) SaveSettings(settingsFileName);
\r
9615 DeleteClipboardTempFiles();
\r
9621 if (IsIconic(hwndMain))
\r
9622 ShowWindow(hwndMain, SW_RESTORE);
\r
9624 SetActiveWindow(hwndMain);
\r
9628 * Prototypes for animation support routines
\r
9630 static void ScreenSquare(int column, int row, POINT * pt);
\r
9631 static void Tween( POINT * start, POINT * mid, POINT * finish, int factor,
\r
9632 POINT frames[], int * nFrames);
\r
9638 AnimateAtomicCapture(Board board, int fromX, int fromY, int toX, int toY)
\r
9639 { // [HGM] atomic: animate blast wave
\r
9642 explodeInfo.fromX = fromX;
\r
9643 explodeInfo.fromY = fromY;
\r
9644 explodeInfo.toX = toX;
\r
9645 explodeInfo.toY = toY;
\r
9646 for(i=1; i<4*kFactor; i++) {
\r
9647 explodeInfo.radius = (i*180)/(4*kFactor-1);
\r
9648 DrawPosition(FALSE, board);
\r
9649 Sleep(appData.animSpeed);
\r
9651 explodeInfo.radius = 0;
\r
9652 DrawPosition(TRUE, board);
\r
9656 AnimateMove(board, fromX, fromY, toX, toY)
\r
9663 ChessSquare piece;
\r
9664 POINT start, finish, mid;
\r
9665 POINT frames[kFactor * 2 + 1];
\r
9668 if (!appData.animate) return;
\r
9669 if (doingSizing) return;
\r
9670 if (fromY < 0 || fromX < 0) return;
\r
9671 piece = board[fromY][fromX];
\r
9672 if (piece >= EmptySquare) return;
\r
9674 ScreenSquare(fromX, fromY, &start);
\r
9675 ScreenSquare(toX, toY, &finish);
\r
9677 /* All moves except knight jumps move in straight line */
\r
9678 if (!(abs(fromX-toX) == 1 && abs(fromY-toY) == 2 || abs(fromX-toX) == 2 && abs(fromY-toY) == 1)) {
\r
9679 mid.x = start.x + (finish.x - start.x) / 2;
\r
9680 mid.y = start.y + (finish.y - start.y) / 2;
\r
9682 /* Knight: make straight movement then diagonal */
\r
9683 if (abs(toY - fromY) < abs(toX - fromX)) {
\r
9684 mid.x = start.x + (finish.x - start.x) / 2;
\r
9688 mid.y = start.y + (finish.y - start.y) / 2;
\r
9692 /* Don't use as many frames for very short moves */
\r
9693 if (abs(toY - fromY) + abs(toX - fromX) <= 2)
\r
9694 Tween(&start, &mid, &finish, kFactor - 1, frames, &nFrames);
\r
9696 Tween(&start, &mid, &finish, kFactor, frames, &nFrames);
\r
9698 animInfo.from.x = fromX;
\r
9699 animInfo.from.y = fromY;
\r
9700 animInfo.to.x = toX;
\r
9701 animInfo.to.y = toY;
\r
9702 animInfo.lastpos = start;
\r
9703 animInfo.piece = piece;
\r
9704 for (n = 0; n < nFrames; n++) {
\r
9705 animInfo.pos = frames[n];
\r
9706 DrawPosition(FALSE, NULL);
\r
9707 animInfo.lastpos = animInfo.pos;
\r
9708 Sleep(appData.animSpeed);
\r
9710 animInfo.pos = finish;
\r
9711 DrawPosition(FALSE, NULL);
\r
9712 animInfo.piece = EmptySquare;
\r
9713 Explode(board, fromX, fromY, toX, toY);
\r
9716 /* Convert board position to corner of screen rect and color */
\r
9719 ScreenSquare(column, row, pt)
\r
9720 int column; int row; POINT * pt;
\r
9723 pt->x = lineGap + ((BOARD_WIDTH-1)-column) * (squareSize + lineGap);
\r
9724 pt->y = lineGap + row * (squareSize + lineGap);
\r
9726 pt->x = lineGap + column * (squareSize + lineGap);
\r
9727 pt->y = lineGap + ((BOARD_HEIGHT-1)-row) * (squareSize + lineGap);
\r
9731 /* Generate a series of frame coords from start->mid->finish.
\r
9732 The movement rate doubles until the half way point is
\r
9733 reached, then halves back down to the final destination,
\r
9734 which gives a nice slow in/out effect. The algorithmn
\r
9735 may seem to generate too many intermediates for short
\r
9736 moves, but remember that the purpose is to attract the
\r
9737 viewers attention to the piece about to be moved and
\r
9738 then to where it ends up. Too few frames would be less
\r
9742 Tween(start, mid, finish, factor, frames, nFrames)
\r
9743 POINT * start; POINT * mid;
\r
9744 POINT * finish; int factor;
\r
9745 POINT frames[]; int * nFrames;
\r
9747 int n, fraction = 1, count = 0;
\r
9749 /* Slow in, stepping 1/16th, then 1/8th, ... */
\r
9750 for (n = 0; n < factor; n++)
\r
9752 for (n = 0; n < factor; n++) {
\r
9753 frames[count].x = start->x + (mid->x - start->x) / fraction;
\r
9754 frames[count].y = start->y + (mid->y - start->y) / fraction;
\r
9756 fraction = fraction / 2;
\r
9760 frames[count] = *mid;
\r
9763 /* Slow out, stepping 1/2, then 1/4, ... */
\r
9765 for (n = 0; n < factor; n++) {
\r
9766 frames[count].x = finish->x - (finish->x - mid->x) / fraction;
\r
9767 frames[count].y = finish->y - (finish->y - mid->y) / fraction;
\r
9769 fraction = fraction * 2;
\r
9775 HistorySet( char movelist[][2*MOVE_LEN], int first, int last, int current )
\r
9777 MoveHistorySet( movelist, first, last, current, pvInfoList );
\r
9779 EvalGraphSet( first, last, current, pvInfoList );
\r
9783 SettingsPopUp(ChessProgramState *cps)
\r
9784 { // [HGM] wrapper needed because handles must not be passed through back-end
\r
9785 EngineOptionsPopup(savedHwnd, cps);
\r
9788 int flock(int fid, int code)
\r
9790 HANDLE hFile = (HANDLE) _get_osfhandle(fid);
\r
9794 ov.OffsetHigh = 0;
\r
9796 case 1: LockFileEx(hFile, LOCKFILE_EXCLUSIVE_LOCK, 0, 1024, 0, &ov); break; // LOCK_SH
\r
9797 case 2: LockFileEx(hFile, LOCKFILE_EXCLUSIVE_LOCK, 0, 1024, 0, &ov); break; // LOCK_EX
\r
9798 case 3: UnlockFileEx(hFile, 0, 1024, 0, &ov); break; // LOCK_UN
\r
9799 default: return -1;
\r