2 * WinBoard.c -- Windows NT front end to XBoard
\r
4 * Copyright 1991 by Digital Equipment Corporation, Maynard,
\r
7 * Enhancements Copyright 1992-2001, 2002, 2003, 2004, 2005, 2006,
\r
8 * 2007, 2008, 2009, 2010, 2011 Free Software Foundation, Inc.
\r
10 * Enhancements Copyright 2005 Alessandro Scotti
\r
12 * XBoard borrows its colors and the bitmaps.xchess bitmap set from XChess,
\r
13 * which was written and is copyrighted by Wayne Christopher.
\r
15 * The following terms apply to Digital Equipment Corporation's copyright
\r
16 * interest in XBoard:
\r
17 * ------------------------------------------------------------------------
\r
18 * All Rights Reserved
\r
20 * Permission to use, copy, modify, and distribute this software and its
\r
21 * documentation for any purpose and without fee is hereby granted,
\r
22 * provided that the above copyright notice appear in all copies and that
\r
23 * both that copyright notice and this permission notice appear in
\r
24 * supporting documentation, and that the name of Digital not be
\r
25 * used in advertising or publicity pertaining to distribution of the
\r
26 * software without specific, written prior permission.
\r
28 * DIGITAL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
\r
29 * ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL
\r
30 * DIGITAL BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
\r
31 * ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
\r
32 * WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
\r
33 * ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
\r
35 * ------------------------------------------------------------------------
\r
37 * The following terms apply to the enhanced version of XBoard
\r
38 * distributed by the Free Software Foundation:
\r
39 * ------------------------------------------------------------------------
\r
41 * GNU XBoard is free software: you can redistribute it and/or modify
\r
42 * it under the terms of the GNU General Public License as published by
\r
43 * the Free Software Foundation, either version 3 of the License, or (at
\r
44 * your option) any later version.
\r
46 * GNU XBoard is distributed in the hope that it will be useful, but
\r
47 * WITHOUT ANY WARRANTY; without even the implied warranty of
\r
48 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
\r
49 * General Public License for more details.
\r
51 * You should have received a copy of the GNU General Public License
\r
52 * along with this program. If not, see http://www.gnu.org/licenses/. *
\r
54 *------------------------------------------------------------------------
\r
55 ** See the file ChangeLog for a revision history. */
\r
59 #include <windows.h>
\r
60 #include <winuser.h>
\r
61 #include <winsock.h>
\r
62 #include <commctrl.h>
\r
68 #include <sys/stat.h>
\r
71 #include <commdlg.h>
\r
73 #include <richedit.h>
\r
74 #include <mmsystem.h>
\r
83 #include "frontend.h"
\r
84 #include "backend.h"
\r
85 #include "winboard.h"
\r
87 #include "wclipbrd.h"
\r
88 #include "woptions.h"
\r
89 #include "wsockerr.h"
\r
90 #include "defaults.h"
\r
94 //void InitEngineUCI( const char * iniDir, ChessProgramState * cps );
\r
97 void mysrandom(unsigned int seed);
\r
99 extern int whiteFlag, blackFlag;
\r
100 Boolean flipClock = FALSE;
\r
101 extern HANDLE chatHandle[];
\r
102 extern int ics_type;
\r
104 void DisplayHoldingsCount(HDC hdc, int x, int y, int align, int copyNumber);
\r
105 VOID NewVariantPopup(HWND hwnd);
\r
106 int FinishMove P((ChessMove moveType, int fromX, int fromY, int toX, int toY,
\r
107 /*char*/int promoChar));
\r
108 void DisplayMove P((int moveNumber));
\r
109 Boolean ParseFEN P((Board board, int *blackPlaysFirst, char *fen));
\r
110 void ChatPopUp P((char *s));
\r
112 ChessSquare piece;
\r
113 POINT pos; /* window coordinates of current pos */
\r
114 POINT lastpos; /* window coordinates of last pos - used for clipping */
\r
115 POINT from; /* board coordinates of the piece's orig pos */
\r
116 POINT to; /* board coordinates of the piece's new pos */
\r
119 static AnimInfo animInfo = { EmptySquare, {-1,-1}, {-1,-1}, {-1,-1} };
\r
122 POINT start; /* window coordinates of start pos */
\r
123 POINT pos; /* window coordinates of current pos */
\r
124 POINT lastpos; /* window coordinates of last pos - used for clipping */
\r
125 POINT from; /* board coordinates of the piece's orig pos */
\r
129 static DragInfo dragInfo = { {-1,-1}, {-1,-1}, {-1,-1}, {-1,-1}, EmptySquare };
\r
132 POINT sq[2]; /* board coordinates of from, to squares */
\r
135 static HighlightInfo highlightInfo = { {{-1, -1}, {-1, -1}} };
\r
136 static HighlightInfo premoveHighlightInfo = { {{-1, -1}, {-1, -1}} };
\r
137 static HighlightInfo partnerHighlightInfo = { {{-1, -1}, {-1, -1}} };
\r
138 static HighlightInfo oldPartnerHighlight = { {{-1, -1}, {-1, -1}} };
\r
140 typedef struct { // [HGM] atomic
\r
141 int fromX, fromY, toX, toY, radius;
\r
144 static ExplodeInfo explodeInfo;
\r
146 /* Window class names */
\r
147 char szAppName[] = "WinBoard";
\r
148 char szConsoleName[] = "WBConsole";
\r
150 /* Title bar text */
\r
151 char szTitle[] = "WinBoard";
\r
152 char szConsoleTitle[] = "I C S Interaction";
\r
155 char *settingsFileName;
\r
156 Boolean saveSettingsOnExit;
\r
157 char installDir[MSG_SIZ];
\r
158 int errorExitStatus;
\r
160 BoardSize boardSize;
\r
161 Boolean chessProgram;
\r
162 //static int boardX, boardY;
\r
163 int minX, minY; // [HGM] placement: volatile limits on upper-left corner
\r
164 int squareSize, lineGap, minorSize;
\r
165 static int winW, winH;
\r
166 static RECT messageRect, whiteRect, blackRect, leftLogoRect, rightLogoRect; // [HGM] logo
\r
167 static int logoHeight = 0;
\r
168 static char messageText[MESSAGE_TEXT_MAX];
\r
169 static int clockTimerEvent = 0;
\r
170 static int loadGameTimerEvent = 0;
\r
171 static int analysisTimerEvent = 0;
\r
172 static DelayedEventCallback delayedTimerCallback;
\r
173 static int delayedTimerEvent = 0;
\r
174 static int buttonCount = 2;
\r
175 char *icsTextMenuString;
\r
177 char *firstChessProgramNames;
\r
178 char *secondChessProgramNames;
\r
180 #define PALETTESIZE 256
\r
182 HINSTANCE hInst; /* current instance */
\r
183 Boolean alwaysOnTop = FALSE;
\r
185 COLORREF lightSquareColor, darkSquareColor, whitePieceColor,
\r
186 blackPieceColor, highlightSquareColor, premoveHighlightColor;
\r
188 ColorClass currentColorClass;
\r
190 static HWND savedHwnd;
\r
191 HWND hCommPort = NULL; /* currently open comm port */
\r
192 static HWND hwndPause; /* pause button */
\r
193 static HBITMAP pieceBitmap[3][(int) BlackPawn]; /* [HGM] nr of bitmaps referred to bP in stead of wK */
\r
194 static HBRUSH lightSquareBrush, darkSquareBrush,
\r
195 blackSquareBrush, /* [HGM] for band between board and holdings */
\r
196 explodeBrush, /* [HGM] atomic */
\r
197 markerBrush, /* [HGM] markers */
\r
198 whitePieceBrush, blackPieceBrush, iconBkgndBrush /*, outlineBrush*/;
\r
199 static POINT gridEndpoints[(BOARD_RANKS + BOARD_FILES + 2) * 2];
\r
200 static DWORD gridVertexCounts[BOARD_RANKS + BOARD_FILES + 2];
\r
201 static HPEN gridPen = NULL;
\r
202 static HPEN highlightPen = NULL;
\r
203 static HPEN premovePen = NULL;
\r
204 static NPLOGPALETTE pLogPal;
\r
205 static BOOL paletteChanged = FALSE;
\r
206 static HICON iconWhite, iconBlack, iconCurrent;
\r
207 static int doingSizing = FALSE;
\r
208 static int lastSizing = 0;
\r
209 static int prevStderrPort;
\r
210 static HBITMAP userLogo;
\r
212 static HBITMAP liteBackTexture = NULL;
\r
213 static HBITMAP darkBackTexture = NULL;
\r
214 static int liteBackTextureMode = BACK_TEXTURE_MODE_PLAIN;
\r
215 static int darkBackTextureMode = BACK_TEXTURE_MODE_PLAIN;
\r
216 static int backTextureSquareSize = 0;
\r
217 static struct { int x; int y; int mode; } backTextureSquareInfo[BOARD_RANKS][BOARD_FILES];
\r
219 #if __GNUC__ && !defined(_winmajor)
\r
220 #define oldDialog 0 /* cygwin doesn't define _winmajor; mingw does */
\r
222 #if defined(_winmajor)
\r
223 #define oldDialog (_winmajor < 4)
\r
225 #define oldDialog 0
\r
229 #define INTERNATIONAL
\r
231 #ifdef INTERNATIONAL
\r
232 # define _(s) T_(s)
\r
238 # define Translate(x, y)
\r
239 # define LoadLanguageFile(s)
\r
242 #ifdef INTERNATIONAL
\r
244 Boolean barbaric; // flag indicating if translation is needed
\r
246 // list of item numbers used in each dialog (used to alter language at run time)
\r
248 #define ABOUTBOX -1 /* not sure why these are needed */
\r
249 #define ABOUTBOX2 -1
\r
251 int dialogItems[][40] = {
\r
252 { ABOUTBOX, IDOK, OPT_MESS, 400 },
\r
253 { DLG_TimeControl, IDC_Babble, OPT_TCUseMoves, OPT_TCUseInc, OPT_TCUseFixed,
\r
254 OPT_TCtext1, OPT_TCtext2, OPT_TCitext1, OPT_TCitext2, OPT_TCftext, GPB_Factors, IDC_Factor1, IDC_Factor2, IDOK, IDCANCEL },
\r
255 { DLG_LoadOptions, OPT_Autostep, OPT_AStext1, IDOK, IDCANCEL },
\r
256 { DLG_SaveOptions, OPT_Autosave, OPT_AVPrompt, OPT_AVToFile, OPT_AVBrowse,
\r
257 801, OPT_PGN, OPT_Old, OPT_OutOfBookInfo, IDOK, IDCANCEL },
\r
258 { 1536, 1090, IDC_Directories, 1089, 1091, IDOK, IDCANCEL, 1038, IDC_IndexNr, 1037 },
\r
259 { DLG_CommPort, IDOK, IDCANCEL, IDC_Port, IDC_Rate, IDC_Bits, IDC_Parity,
\r
260 IDC_Stop, IDC_Flow, OPT_SerialHelp },
\r
261 { DLG_EditComment, IDOK, OPT_CancelComment, OPT_ClearComment, OPT_EditComment },
\r
262 { DLG_PromotionKing, PB_Chancellor, PB_Archbishop, PB_Queen, PB_Rook,
\r
263 PB_Bishop, PB_Knight, PB_King, IDCANCEL, IDC_Yes, IDC_No, IDC_Centaur },
\r
264 { ABOUTBOX2, IDC_ChessBoard },
\r
265 { DLG_GameList, OPT_GameListLoad, OPT_GameListPrev, OPT_GameListNext,
\r
266 OPT_GameListClose, IDC_GameListDoFilter },
\r
267 { DLG_EditTags, IDOK, OPT_TagsCancel, OPT_EditTags },
\r
268 { DLG_Error, IDOK },
\r
269 { DLG_Colorize, IDOK, IDCANCEL, OPT_ChooseColor, OPT_Bold, OPT_Italic,
\r
270 OPT_Underline, OPT_Strikeout, OPT_Sample },
\r
271 { DLG_Question, IDOK, IDCANCEL, OPT_QuestionText },
\r
272 { DLG_Startup, IDC_Welcome, OPT_ChessEngine, OPT_ChessServer, OPT_View,
\r
273 IDC_SPECIFY_ENG_STATIC, IDC_SPECIFY_SERVER_STATIC, OPT_AnyAdditional,
\r
274 IDOK, IDCANCEL, IDM_HELPCONTENTS },
\r
275 { DLG_IndexNumber, IDC_Index },
\r
276 { DLG_TypeInMove, IDOK, IDCANCEL },
\r
277 { DLG_TypeInName, IDOK, IDCANCEL },
\r
278 { DLG_Sound, IDC_Event, OPT_NoSound, OPT_DefaultBeep, OPT_BuiltInSound,
\r
279 OPT_WavFile, OPT_BrowseSound, OPT_DefaultSounds, IDOK, IDCANCEL, OPT_PlaySound },
\r
280 { DLG_GeneralOptions, IDOK, IDCANCEL, OPT_AlwaysOnTop, OPT_HighlightLastMove,
\r
281 OPT_AlwaysQueen, OPT_PeriodicUpdates, OPT_AnimateDragging, OPT_PonderNextMove,
\r
282 OPT_AnimateMoving, OPT_PopupExitMessage, OPT_AutoFlag, OPT_PopupMoveErrors,
\r
283 OPT_AutoFlipView, OPT_ShowButtonBar, OPT_AutoRaiseBoard, OPT_ShowCoordinates,
\r
284 OPT_Blindfold, OPT_ShowThinking, OPT_HighlightDragging, OPT_TestLegality,
\r
285 OPT_SaveExtPGN, OPT_HideThinkFromHuman, OPT_ExtraInfoInMoveHistory,
\r
286 OPT_HighlightMoveArrow, OPT_AutoLogo ,OPT_SmartMove },
\r
287 { DLG_IcsOptions, IDOK, IDCANCEL, OPT_AutoComment, OPT_AutoKibitz, OPT_AutoObserve,
\r
288 OPT_GetMoveList, OPT_LocalLineEditing, OPT_QuietPlay, OPT_SeekGraph, OPT_AutoRefresh,
\r
289 OPT_BgObserve, OPT_DualBoard, OPT_Premove, OPT_PremoveWhite, OPT_PremoveBlack,
\r
290 OPT_SmartMove, OPT_IcsAlarm, IDC_Sec, OPT_ChooseShoutColor, OPT_ChooseSShoutColor,
\r
291 OPT_ChooseChannel1Color, OPT_ChooseChannelColor, OPT_ChooseKibitzColor,
\r
292 OPT_ChooseTellColor, OPT_ChooseChallengeColor, OPT_ChooseRequestColor,
\r
293 OPT_ChooseSeekColor, OPT_ChooseNormalColor, OPT_ChooseBackgroundColor,
\r
294 OPT_DefaultColors, OPT_DontColorize, IDC_Boxes, GPB_Colors, GPB_Premove,
\r
295 GPB_General, GPB_Alarm },
\r
296 { DLG_BoardOptions, IDOK, IDCANCEL, OPT_SizeTiny, OPT_SizeTeeny, OPT_SizeDinky,
\r
297 OPT_SizePetite, OPT_SizeSlim, OPT_SizeSmall, OPT_SizeMediocre, OPT_SizeMiddling,
\r
298 OPT_SizeAverage, OPT_SizeModerate, OPT_SizeMedium, OPT_SizeBulky, OPT_SizeLarge,
\r
299 OPT_SizeBig, OPT_SizeHuge, OPT_SizeGiant, OPT_SizeColossal, OPT_SizeTitanic,
\r
300 OPT_ChooseLightSquareColor, OPT_ChooseDarkSquareColor, OPT_ChooseWhitePieceColor,
\r
301 OPT_ChooseBlackPieceColor, OPT_ChooseHighlightSquareColor, OPT_ChoosePremoveHighlightColor,
\r
302 OPT_Monochrome, OPT_AllWhite, OPT_UpsideDown, OPT_DefaultBoardColors, GPB_Colors,
\r
303 IDC_Light, IDC_Dark, IDC_White, IDC_Black, IDC_High, IDC_PreHigh, GPB_Size },
\r
304 { DLG_NewVariant, IDOK, IDCANCEL, OPT_VariantNormal, OPT_VariantFRC, OPT_VariantWildcastle,
\r
305 OPT_VariantNocastle, OPT_VariantLosers, OPT_VariantGiveaway, OPT_VariantSuicide,
\r
306 OPT_Variant3Check, OPT_VariantTwoKings, OPT_VariantAtomic, OPT_VariantCrazyhouse,
\r
307 OPT_VariantBughouse, OPT_VariantTwilight, OPT_VariantShogi, OPT_VariantSuper,
\r
308 OPT_VariantKnightmate, OPT_VariantBerolina, OPT_VariantCylinder, OPT_VariantFairy,
\r
309 OPT_VariantMakruk, OPT_VariantGothic, OPT_VariantCapablanca, OPT_VariantJanus,
\r
310 OPT_VariantCRC, OPT_VariantFalcon, OPT_VariantCourier, OPT_VariantGreat, OPT_VariantSChess,
\r
311 OPT_VariantShatranj, OPT_VariantXiangqi, GPB_Variant, GPB_Board, IDC_Height,
\r
312 IDC_Width, IDC_Hand, IDC_Pieces, IDC_Def },
\r
313 { DLG_Fonts, IDOK, IDCANCEL, OPT_ChooseClockFont, OPT_ChooseMessageFont,
\r
314 OPT_ChooseCoordFont, OPT_ChooseTagFont, OPT_ChooseCommentsFont, OPT_ChooseConsoleFont, OPT_ChooseMoveHistoryFont, OPT_DefaultFonts,
\r
315 OPT_ClockFont, OPT_MessageFont, OPT_CoordFont, OPT_EditTagsFont,
\r
316 OPT_CommentsFont, OPT_MessageFont5, GPB_Current, GPB_All, OPT_MessageFont6 },
\r
317 { DLG_NewGameFRC, IDC_NFG_Label, IDC_NFG_Random, IDOK, IDCANCEL },
\r
318 { DLG_GameListOptions, IDC_GLT, IDC_GLT_Up, IDC_GLT_Down, IDC_GLT_Restore,
\r
319 IDC_GLT_Default, IDOK, IDCANCEL, IDC_GLT_RestoreTo },
\r
320 { DLG_MoveHistory },
\r
321 { DLG_EvalGraph },
\r
322 { DLG_EngineOutput, IDC_EngineLabel1, IDC_Engine1_NPS, IDC_EngineLabel2, IDC_Engine2_NPS },
\r
323 { DLG_Chat, IDC_Partner, IDC_Clear, IDC_Send, },
\r
324 { DLG_EnginePlayOptions, IDC_EpPonder, IDC_EpShowThinking, IDC_EpHideThinkingHuman,
\r
325 IDC_EpPeriodicUpdates, GPB_Adjudications, IDC_Draw, IDC_Moves, IDC_Threshold,
\r
326 IDC_Centi, IDC_TestClaims, IDC_DetectMates, IDC_MaterialDraws, IDC_TrivialDraws,
\r
327 GPB_Apply, IDC_Rule, IDC_Repeats, IDC_ScoreAbs1, IDC_ScoreAbs2, IDOK, IDCANCEL },
\r
328 { DLG_OptionsUCI, IDC_PolyDir, IDC_BrowseForPolyglotDir, IDC_Hash, IDC_Path,
\r
329 IDC_BrowseForEGTB, IDC_Cache, IDC_UseBook, IDC_BrowseForBook, IDC_CPU, IDC_OwnBook1,
\r
330 IDC_OwnBook2, IDC_Depth, IDC_Variation, IDC_DefGames, IDOK, IDCANCEL },
\r
334 static char languageBuf[50000], *foreign[1000], *english[1000], *languageFile[MSG_SIZ];
\r
335 static int lastChecked;
\r
336 static char oldLanguage[MSG_SIZ], *menuText[10][30];
\r
337 extern int tinyLayout;
\r
338 extern char * menuBarText[][10];
\r
341 LoadLanguageFile(char *name)
\r
342 { //load the file with translations, and make a list of the strings to be translated, and their translations
\r
344 int i=0, j=0, n=0, k;
\r
347 if(!name || name[0] == NULLCHAR) return;
\r
348 snprintf(buf, MSG_SIZ, "%s%s", name, strchr(name, '.') ? "" : ".lng"); // auto-append lng extension
\r
349 appData.language = oldLanguage;
\r
350 if(!strcmp(buf, oldLanguage)) { barbaric = 1; return; } // this language already loaded; just switch on
\r
351 if((f = fopen(buf, "r")) == NULL) return;
\r
352 while((k = fgetc(f)) != EOF) {
\r
353 if(i >= sizeof(languageBuf)) { DisplayError("Language file too big", 0); return; }
\r
354 languageBuf[i] = k;
\r
356 if(languageBuf[n] == '"' && languageBuf[i-1] == '"') {
\r
358 if(p = strstr(languageBuf + n + 1, "\" === \"")) {
\r
359 if(p > languageBuf+n+2 && p+8 < languageBuf+i) {
\r
360 if(j >= sizeof(english)) { DisplayError("Too many translated strings", 0); return; }
\r
361 english[j] = languageBuf + n + 1; *p = 0;
\r
362 foreign[j++] = p + 7; languageBuf[i-1] = 0;
\r
363 //if(appData.debugMode) fprintf(debugFP, "translation: replace '%s' by '%s'\n", english[j-1], foreign[j-1]);
\r
368 } else if(i > 0 && languageBuf[i-1] == '\\') {
\r
370 case 'n': k = '\n'; break;
\r
371 case 'r': k = '\r'; break;
\r
372 case 't': k = '\t'; break;
\r
374 languageBuf[--i] = k;
\r
379 barbaric = (j != 0);
\r
380 safeStrCpy(oldLanguage, buf, sizeof(oldLanguage)/sizeof(oldLanguage[0]) );
\r
385 { // return the translation of the given string
\r
386 // efficiency can be improved a lot...
\r
388 //if(appData.debugMode) fprintf(debugFP, "T_(%s)\n", s);
\r
389 if(!barbaric) return s;
\r
390 if(!s) return ""; // sanity
\r
391 while(english[i]) {
\r
392 if(!strcmp(s, english[i])) return foreign[i];
\r
399 Translate(HWND hDlg, int dialogID)
\r
400 { // translate all text items in the given dialog
\r
402 char buf[MSG_SIZ], *s;
\r
403 if(!barbaric) return;
\r
404 while(dialogItems[i][0] && dialogItems[i][0] != dialogID) i++; // find the dialog description
\r
405 if(dialogItems[i][0] != dialogID) return; // unknown dialog, should not happen
\r
406 GetWindowText( hDlg, buf, MSG_SIZ );
\r
408 if(strcmp(buf, s)) SetWindowText(hDlg, s); // replace by translated string (if different)
\r
409 for(j=1; k=dialogItems[i][j]; j++) { // translate all listed dialog items
\r
410 GetDlgItemText(hDlg, k, buf, MSG_SIZ);
\r
411 if(strlen(buf) == 0) continue;
\r
413 if(strcmp(buf, s)) SetDlgItemText(hDlg, k, s); // replace by translated string (if different)
\r
418 TranslateOneMenu(int i, HMENU subMenu)
\r
421 static MENUITEMINFO info;
\r
423 info.cbSize = sizeof(MENUITEMINFO);
\r
424 info.fMask = MIIM_STATE | MIIM_TYPE;
\r
425 for(j=GetMenuItemCount(subMenu)-1; j>=0; j--){
\r
427 info.dwTypeData = buf;
\r
428 info.cch = sizeof(buf);
\r
429 GetMenuItemInfo(subMenu, j, TRUE, &info);
\r
431 if(menuText[i][j]) safeStrCpy(buf, menuText[i][j], sizeof(buf)/sizeof(buf[0]) );
\r
432 else menuText[i][j] = strdup(buf); // remember original on first change
\r
434 if(buf[0] == NULLCHAR) continue;
\r
435 info.dwTypeData = T_(buf);
\r
436 info.cch = strlen(buf)+1;
\r
437 SetMenuItemInfo(subMenu, j, TRUE, &info);
\r
443 TranslateMenus(int addLanguage)
\r
446 WIN32_FIND_DATA fileData;
\r
448 #define IDM_English 1970
\r
450 HMENU mainMenu = GetMenu(hwndMain);
\r
451 for (i=GetMenuItemCount(mainMenu)-1; i>=0; i--) {
\r
452 HMENU subMenu = GetSubMenu(mainMenu, i);
\r
453 ModifyMenu(mainMenu, i, MF_STRING|MF_BYPOSITION|MF_POPUP|EnableMenuItem(mainMenu, i, MF_BYPOSITION),
\r
454 (UINT) subMenu, T_(menuBarText[tinyLayout][i]));
\r
455 TranslateOneMenu(i, subMenu);
\r
457 DrawMenuBar(hwndMain);
\r
460 if(!addLanguage) return;
\r
461 if((hFind = FindFirstFile("*.LNG", &fileData)) != INVALID_HANDLE_VALUE) {
\r
462 HMENU mainMenu = GetMenu(hwndMain);
\r
463 HMENU subMenu = GetSubMenu(mainMenu, GetMenuItemCount(mainMenu)-1);
\r
464 AppendMenu(subMenu, MF_SEPARATOR, (UINT_PTR) 0, NULL);
\r
465 AppendMenu(subMenu, MF_ENABLED|MF_STRING|(barbaric?MF_UNCHECKED:MF_CHECKED), (UINT_PTR) IDM_English, (LPCTSTR) "English");
\r
466 i = 0; lastChecked = IDM_English;
\r
468 char *p, *q = fileData.cFileName;
\r
469 int checkFlag = MF_UNCHECKED;
\r
470 languageFile[i] = strdup(q);
\r
471 if(barbaric && !strcmp(oldLanguage, q)) {
\r
472 checkFlag = MF_CHECKED;
\r
473 lastChecked = IDM_English + i + 1;
\r
474 CheckMenuItem(mainMenu, IDM_English, MF_BYCOMMAND|MF_UNCHECKED);
\r
476 *q = ToUpper(*q); while(*++q) *q = ToLower(*q);
\r
477 p = strstr(fileData.cFileName, ".lng");
\r
479 AppendMenu(subMenu, MF_ENABLED|MF_STRING|checkFlag, (UINT_PTR) IDM_English + ++i, (LPCTSTR) fileData.cFileName);
\r
480 } while(FindNextFile(hFind, &fileData));
\r
493 int cliWidth, cliHeight;
\r
496 SizeInfo sizeInfo[] =
\r
498 { "tiny", 21, 0, 1, 1, 0, 0 },
\r
499 { "teeny", 25, 1, 1, 1, 0, 0 },
\r
500 { "dinky", 29, 1, 1, 1, 0, 0 },
\r
501 { "petite", 33, 1, 1, 1, 0, 0 },
\r
502 { "slim", 37, 2, 1, 0, 0, 0 },
\r
503 { "small", 40, 2, 1, 0, 0, 0 },
\r
504 { "mediocre", 45, 2, 1, 0, 0, 0 },
\r
505 { "middling", 49, 2, 0, 0, 0, 0 },
\r
506 { "average", 54, 2, 0, 0, 0, 0 },
\r
507 { "moderate", 58, 3, 0, 0, 0, 0 },
\r
508 { "medium", 64, 3, 0, 0, 0, 0 },
\r
509 { "bulky", 72, 3, 0, 0, 0, 0 },
\r
510 { "large", 80, 3, 0, 0, 0, 0 },
\r
511 { "big", 87, 3, 0, 0, 0, 0 },
\r
512 { "huge", 95, 3, 0, 0, 0, 0 },
\r
513 { "giant", 108, 3, 0, 0, 0, 0 },
\r
514 { "colossal", 116, 4, 0, 0, 0, 0 },
\r
515 { "titanic", 129, 4, 0, 0, 0, 0 },
\r
516 { NULL, 0, 0, 0, 0, 0, 0 }
\r
519 #define MF(x) {x, {{0,}, 0. }, {0, }, 0}
\r
520 MyFont fontRec[NUM_SIZES][NUM_FONTS] =
\r
522 { MF(CLOCK_FONT_TINY), MF(MESSAGE_FONT_TINY), MF(COORD_FONT_TINY), MF(CONSOLE_FONT_TINY), MF(COMMENT_FONT_TINY), MF(EDITTAGS_FONT_TINY), MF(MOVEHISTORY_FONT_ALL) },
\r
523 { MF(CLOCK_FONT_TEENY), MF(MESSAGE_FONT_TEENY), MF(COORD_FONT_TEENY), MF(CONSOLE_FONT_TEENY), MF(COMMENT_FONT_TEENY), MF(EDITTAGS_FONT_TEENY), MF(MOVEHISTORY_FONT_ALL) },
\r
524 { MF(CLOCK_FONT_DINKY), MF(MESSAGE_FONT_DINKY), MF(COORD_FONT_DINKY), MF(CONSOLE_FONT_DINKY), MF(COMMENT_FONT_DINKY), MF(EDITTAGS_FONT_DINKY), MF(MOVEHISTORY_FONT_ALL) },
\r
525 { MF(CLOCK_FONT_PETITE), MF(MESSAGE_FONT_PETITE), MF(COORD_FONT_PETITE), MF(CONSOLE_FONT_PETITE), MF(COMMENT_FONT_PETITE), MF(EDITTAGS_FONT_PETITE), MF(MOVEHISTORY_FONT_ALL) },
\r
526 { MF(CLOCK_FONT_SLIM), MF(MESSAGE_FONT_SLIM), MF(COORD_FONT_SLIM), MF(CONSOLE_FONT_SLIM), MF(COMMENT_FONT_SLIM), MF(EDITTAGS_FONT_SLIM), MF(MOVEHISTORY_FONT_ALL) },
\r
527 { MF(CLOCK_FONT_SMALL), MF(MESSAGE_FONT_SMALL), MF(COORD_FONT_SMALL), MF(CONSOLE_FONT_SMALL), MF(COMMENT_FONT_SMALL), MF(EDITTAGS_FONT_SMALL), MF(MOVEHISTORY_FONT_ALL) },
\r
528 { MF(CLOCK_FONT_MEDIOCRE), MF(MESSAGE_FONT_MEDIOCRE), MF(COORD_FONT_MEDIOCRE), MF(CONSOLE_FONT_MEDIOCRE), MF(COMMENT_FONT_MEDIOCRE), MF(EDITTAGS_FONT_MEDIOCRE), MF(MOVEHISTORY_FONT_ALL) },
\r
529 { MF(CLOCK_FONT_MIDDLING), MF(MESSAGE_FONT_MIDDLING), MF(COORD_FONT_MIDDLING), MF(CONSOLE_FONT_MIDDLING), MF(COMMENT_FONT_MIDDLING), MF(EDITTAGS_FONT_MIDDLING), MF(MOVEHISTORY_FONT_ALL) },
\r
530 { MF(CLOCK_FONT_AVERAGE), MF(MESSAGE_FONT_AVERAGE), MF(COORD_FONT_AVERAGE), MF(CONSOLE_FONT_AVERAGE), MF(COMMENT_FONT_AVERAGE), MF(EDITTAGS_FONT_AVERAGE), MF(MOVEHISTORY_FONT_ALL) },
\r
531 { MF(CLOCK_FONT_MODERATE), MF(MESSAGE_FONT_MODERATE), MF(COORD_FONT_MODERATE), MF(CONSOLE_FONT_MODERATE), MF(COMMENT_FONT_MODERATE), MF(EDITTAGS_FONT_MODERATE), MF(MOVEHISTORY_FONT_ALL) },
\r
532 { MF(CLOCK_FONT_MEDIUM), MF(MESSAGE_FONT_MEDIUM), MF(COORD_FONT_MEDIUM), MF(CONSOLE_FONT_MEDIUM), MF(COMMENT_FONT_MEDIUM), MF(EDITTAGS_FONT_MEDIUM), MF(MOVEHISTORY_FONT_ALL) },
\r
533 { MF(CLOCK_FONT_BULKY), MF(MESSAGE_FONT_BULKY), MF(COORD_FONT_BULKY), MF(CONSOLE_FONT_BULKY), MF(COMMENT_FONT_BULKY), MF(EDITTAGS_FONT_BULKY), MF(MOVEHISTORY_FONT_ALL) },
\r
534 { MF(CLOCK_FONT_LARGE), MF(MESSAGE_FONT_LARGE), MF(COORD_FONT_LARGE), MF(CONSOLE_FONT_LARGE), MF(COMMENT_FONT_LARGE), MF(EDITTAGS_FONT_LARGE), MF(MOVEHISTORY_FONT_ALL) },
\r
535 { MF(CLOCK_FONT_BIG), MF(MESSAGE_FONT_BIG), MF(COORD_FONT_BIG), MF(CONSOLE_FONT_BIG), MF(COMMENT_FONT_BIG), MF(EDITTAGS_FONT_BIG), MF(MOVEHISTORY_FONT_ALL) },
\r
536 { MF(CLOCK_FONT_HUGE), MF(MESSAGE_FONT_HUGE), MF(COORD_FONT_HUGE), MF(CONSOLE_FONT_HUGE), MF(COMMENT_FONT_HUGE), MF(EDITTAGS_FONT_HUGE), MF(MOVEHISTORY_FONT_ALL) },
\r
537 { MF(CLOCK_FONT_GIANT), MF(MESSAGE_FONT_GIANT), MF(COORD_FONT_GIANT), MF(CONSOLE_FONT_GIANT), MF(COMMENT_FONT_GIANT), MF(EDITTAGS_FONT_GIANT), MF(MOVEHISTORY_FONT_ALL) },
\r
538 { MF(CLOCK_FONT_COLOSSAL), MF(MESSAGE_FONT_COLOSSAL), MF(COORD_FONT_COLOSSAL), MF(CONSOLE_FONT_COLOSSAL), MF(COMMENT_FONT_COLOSSAL), MF(EDITTAGS_FONT_COLOSSAL), MF(MOVEHISTORY_FONT_ALL) },
\r
539 { MF(CLOCK_FONT_TITANIC), MF(MESSAGE_FONT_TITANIC), MF(COORD_FONT_TITANIC), MF(CONSOLE_FONT_TITANIC), MF(COMMENT_FONT_TITANIC), MF(EDITTAGS_FONT_TITANIC), MF(MOVEHISTORY_FONT_ALL) },
\r
542 MyFont *font[NUM_SIZES][NUM_FONTS];
\r
551 #define BUTTON_WIDTH (tinyLayout ? 16 : 32)
\r
552 #define N_BUTTONS 5
\r
554 MyButtonDesc buttonDesc[N_BUTTONS] =
\r
556 {"<<", IDM_ToStart, NULL, NULL},
\r
557 {"<", IDM_Backward, NULL, NULL},
\r
558 {"P", IDM_Pause, NULL, NULL},
\r
559 {">", IDM_Forward, NULL, NULL},
\r
560 {">>", IDM_ToEnd, NULL, NULL},
\r
563 int tinyLayout = 0, smallLayout = 0;
\r
564 #define MENU_BAR_ITEMS 9
\r
565 char *menuBarText[2][MENU_BAR_ITEMS+1] = {
\r
566 { N_("&File"), N_("&Edit"), N_("&View"), N_("&Mode"), N_("&Action"), N_("E&ngine"), N_("&Options"), N_("&Help"), NULL },
\r
567 { N_("&F"), N_("&E"), N_("&V"), N_("&M"), N_("&A"), N_("&N"), N_("&O"), N_("&H"), NULL },
\r
571 MySound sounds[(int)NSoundClasses];
\r
572 MyTextAttribs textAttribs[(int)NColorClasses];
\r
574 MyColorizeAttribs colorizeAttribs[] = {
\r
575 { (COLORREF)0, 0, N_("Shout Text") },
\r
576 { (COLORREF)0, 0, N_("SShout/CShout") },
\r
577 { (COLORREF)0, 0, N_("Channel 1 Text") },
\r
578 { (COLORREF)0, 0, N_("Channel Text") },
\r
579 { (COLORREF)0, 0, N_("Kibitz Text") },
\r
580 { (COLORREF)0, 0, N_("Tell Text") },
\r
581 { (COLORREF)0, 0, N_("Challenge Text") },
\r
582 { (COLORREF)0, 0, N_("Request Text") },
\r
583 { (COLORREF)0, 0, N_("Seek Text") },
\r
584 { (COLORREF)0, 0, N_("Normal Text") },
\r
585 { (COLORREF)0, 0, N_("None") }
\r
590 static char *commentTitle;
\r
591 static char *commentText;
\r
592 static int commentIndex;
\r
593 static Boolean editComment = FALSE;
\r
596 char errorTitle[MSG_SIZ];
\r
597 char errorMessage[2*MSG_SIZ];
\r
598 HWND errorDialog = NULL;
\r
599 BOOLEAN moveErrorMessageUp = FALSE;
\r
600 BOOLEAN consoleEcho = TRUE;
\r
601 CHARFORMAT consoleCF;
\r
602 COLORREF consoleBackgroundColor;
\r
604 char *programVersion;
\r
610 typedef int CPKind;
\r
619 SOCKET sock2; /* stderr socket for OpenRcmd */
\r
622 #define INPUT_SOURCE_BUF_SIZE 4096
\r
624 typedef struct _InputSource {
\r
631 char buf[INPUT_SOURCE_BUF_SIZE];
\r
635 InputCallback func;
\r
636 struct _InputSource *second; /* for stderr thread on CPRcmd */
\r
640 InputSource *consoleInputSource;
\r
645 VOID ConsoleOutput(char* data, int length, int forceVisible);
\r
646 VOID ConsoleCreate();
\r
648 ConsoleWndProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam);
\r
649 VOID ColorizeTextPopup(HWND hwnd, ColorClass cc);
\r
650 VOID PrintCommSettings(FILE *f, char *name, DCB *dcb);
\r
651 VOID ParseCommSettings(char *arg, DCB *dcb);
\r
653 StartupDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam);
\r
654 VOID APIENTRY MenuPopup(HWND hwnd, POINT pt, HMENU hmenu, UINT def);
\r
655 void ParseIcsTextMenu(char *icsTextMenuString);
\r
656 VOID PopUpNameDialog(char firstchar);
\r
657 VOID UpdateSampleText(HWND hDlg, int id, MyColorizeAttribs *mca);
\r
661 int GameListOptions();
\r
663 int dummy; // [HGM] for obsolete args
\r
665 HWND hwndMain = NULL; /* root window*/
\r
666 HWND hwndConsole = NULL;
\r
667 HWND commentDialog = NULL;
\r
668 HWND moveHistoryDialog = NULL;
\r
669 HWND evalGraphDialog = NULL;
\r
670 HWND engineOutputDialog = NULL;
\r
671 HWND gameListDialog = NULL;
\r
672 HWND editTagsDialog = NULL;
\r
674 int commentUp = FALSE;
\r
676 WindowPlacement wpMain;
\r
677 WindowPlacement wpConsole;
\r
678 WindowPlacement wpComment;
\r
679 WindowPlacement wpMoveHistory;
\r
680 WindowPlacement wpEvalGraph;
\r
681 WindowPlacement wpEngineOutput;
\r
682 WindowPlacement wpGameList;
\r
683 WindowPlacement wpTags;
\r
685 VOID EngineOptionsPopup(); // [HGM] settings
\r
687 VOID GothicPopUp(char *title, VariantClass variant);
\r
689 * Setting "frozen" should disable all user input other than deleting
\r
690 * the window. We do this while engines are initializing themselves.
\r
692 static int frozen = 0;
\r
693 static int oldMenuItemState[MENU_BAR_ITEMS];
\r
699 if (frozen) return;
\r
701 hmenu = GetMenu(hwndMain);
\r
702 for (i=0; i<MENU_BAR_ITEMS; i++) {
\r
703 oldMenuItemState[i] = EnableMenuItem(hmenu, i, MF_BYPOSITION|MF_GRAYED);
\r
705 DrawMenuBar(hwndMain);
\r
708 /* Undo a FreezeUI */
\r
714 if (!frozen) return;
\r
716 hmenu = GetMenu(hwndMain);
\r
717 for (i=0; i<MENU_BAR_ITEMS; i++) {
\r
718 EnableMenuItem(hmenu, i, MF_BYPOSITION|oldMenuItemState[i]);
\r
720 DrawMenuBar(hwndMain);
\r
723 /*static*/ int fromX = -1, fromY = -1, toX, toY; // [HGM] moved upstream, so JAWS can use them
\r
725 /* JAWS preparation patch (WinBoard for the sight impaired). Define required insertions as empty */
\r
731 #define JAWS_ALT_INTERCEPT
\r
732 #define JAWS_KB_NAVIGATION
\r
733 #define JAWS_MENU_ITEMS
\r
734 #define JAWS_SILENCE
\r
735 #define JAWS_REPLAY
\r
737 #define JAWS_COPYRIGHT
\r
738 #define JAWS_DELETE(X) X
\r
739 #define SAYMACHINEMOVE()
\r
743 /*---------------------------------------------------------------------------*\
\r
747 \*---------------------------------------------------------------------------*/
\r
750 WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
\r
751 LPSTR lpCmdLine, int nCmdShow)
\r
754 HANDLE hAccelMain, hAccelNoAlt, hAccelNoICS;
\r
755 // INITCOMMONCONTROLSEX ex;
\r
759 LoadLibrary("RICHED32.DLL");
\r
760 consoleCF.cbSize = sizeof(CHARFORMAT);
\r
762 if (!InitApplication(hInstance)) {
\r
765 if (!InitInstance(hInstance, nCmdShow, lpCmdLine)) {
\r
772 // InitCommonControlsEx(&ex);
\r
773 InitCommonControls();
\r
775 hAccelMain = LoadAccelerators (hInstance, szAppName);
\r
776 hAccelNoAlt = LoadAccelerators (hInstance, "NO_ALT");
\r
777 hAccelNoICS = LoadAccelerators( hInstance, "NO_ICS"); /* [AS] No Ctrl-V on ICS!!! */
\r
779 /* Acquire and dispatch messages until a WM_QUIT message is received. */
\r
781 while (GetMessage(&msg, /* message structure */
\r
782 NULL, /* handle of window receiving the message */
\r
783 0, /* lowest message to examine */
\r
784 0)) /* highest message to examine */
\r
787 if(msg.message == WM_CHAR && msg.wParam == '\t') {
\r
788 // [HGM] navigate: switch between all windows with tab
\r
789 HWND e1 = NULL, e2 = NULL, mh = NULL, hInput = NULL, hText = NULL;
\r
790 int i, currentElement = 0;
\r
792 // first determine what element of the chain we come from (if any)
\r
793 if(appData.icsActive) {
\r
794 hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
795 hText = GetDlgItem(hwndConsole, OPT_ConsoleText);
\r
797 if(engineOutputDialog && EngineOutputIsUp()) {
\r
798 e1 = GetDlgItem(engineOutputDialog, IDC_EngineMemo1);
\r
799 e2 = GetDlgItem(engineOutputDialog, IDC_EngineMemo2);
\r
801 if(moveHistoryDialog && MoveHistoryIsUp()) {
\r
802 mh = GetDlgItem(moveHistoryDialog, IDC_MoveHistory);
\r
804 if(msg.hwnd == hwndMain) currentElement = 7 ; else
\r
805 if(msg.hwnd == engineOutputDialog) currentElement = 2; else
\r
806 if(msg.hwnd == e1) currentElement = 2; else
\r
807 if(msg.hwnd == e2) currentElement = 3; else
\r
808 if(msg.hwnd == moveHistoryDialog) currentElement = 4; else
\r
809 if(msg.hwnd == mh) currentElement = 4; else
\r
810 if(msg.hwnd == evalGraphDialog) currentElement = 6; else
\r
811 if(msg.hwnd == hText) currentElement = 5; else
\r
812 if(msg.hwnd == hInput) currentElement = 6; else
\r
813 for (i = 0; i < N_BUTTONS; i++) {
\r
814 if (buttonDesc[i].hwnd == msg.hwnd) { currentElement = 1; break; }
\r
817 // determine where to go to
\r
818 if(currentElement) { HWND h = NULL; int direction = GetKeyState(VK_SHIFT) < 0 ? -1 : 1;
\r
820 currentElement = (currentElement + direction) % 7;
\r
821 switch(currentElement) {
\r
823 h = hwndMain; break; // passing this case always makes the loop exit
\r
825 h = buttonDesc[0].hwnd; break; // could be NULL
\r
827 if(!EngineOutputIsUp()) continue; // skip closed auxiliary windows
\r
830 if(!EngineOutputIsUp()) continue;
\r
833 if(!MoveHistoryIsUp()) continue;
\r
835 // case 6: // input to eval graph does not seem to get here!
\r
836 // if(!EvalGraphIsUp()) continue;
\r
837 // h = evalGraphDialog; break;
\r
839 if(!appData.icsActive) continue;
\r
843 if(!appData.icsActive) continue;
\r
849 if(currentElement > 4 && IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);
\r
850 if(currentElement < 5 && IsIconic(hwndMain)) ShowWindow(hwndMain, SW_RESTORE); // all open together
\r
853 continue; // this message now has been processed
\r
857 if (!(commentDialog && IsDialogMessage(commentDialog, &msg)) &&
\r
858 !(moveHistoryDialog && IsDialogMessage(moveHistoryDialog, &msg)) &&
\r
859 !(evalGraphDialog && IsDialogMessage(evalGraphDialog, &msg)) &&
\r
860 !(engineOutputDialog && IsDialogMessage(engineOutputDialog, &msg)) &&
\r
861 !(editTagsDialog && IsDialogMessage(editTagsDialog, &msg)) &&
\r
862 !(gameListDialog && IsDialogMessage(gameListDialog, &msg)) &&
\r
863 !(errorDialog && IsDialogMessage(errorDialog, &msg)) &&
\r
864 !(!frozen && TranslateAccelerator(hwndMain, hAccelMain, &msg)) && JAWS_ACCEL
\r
865 !(!hwndConsole && TranslateAccelerator(hwndMain, hAccelNoICS, &msg)) &&
\r
866 !(!hwndConsole && TranslateAccelerator(hwndMain, hAccelNoAlt, &msg))) {
\r
867 int done = 0, i; // [HGM] chat: dispatch cat-box messages
\r
868 for(i=0; i<MAX_CHAT; i++)
\r
869 if(chatHandle[i] && IsDialogMessage(chatHandle[i], &msg)) {
\r
872 if(done) continue; // [HGM] chat: end patch
\r
873 TranslateMessage(&msg); /* Translates virtual key codes */
\r
874 DispatchMessage(&msg); /* Dispatches message to window */
\r
879 return (msg.wParam); /* Returns the value from PostQuitMessage */
\r
882 /*---------------------------------------------------------------------------*\
\r
884 * Initialization functions
\r
886 \*---------------------------------------------------------------------------*/
\r
890 { // update user logo if necessary
\r
891 static char oldUserName[MSG_SIZ], *curName;
\r
893 if(appData.autoLogo) {
\r
894 curName = UserName();
\r
895 if(strcmp(curName, oldUserName)) {
\r
896 snprintf(oldUserName, MSG_SIZ, "logos\\%s.bmp", curName);
\r
897 userLogo = LoadImage( 0, oldUserName, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );
\r
898 safeStrCpy(oldUserName, curName, sizeof(oldUserName)/sizeof(oldUserName[0]) );
\r
899 if(userLogo == NULL)
\r
900 userLogo = LoadImage( 0, "logos\\dummy.bmp", IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );
\r
906 InitApplication(HINSTANCE hInstance)
\r
910 /* Fill in window class structure with parameters that describe the */
\r
913 wc.style = CS_HREDRAW | CS_VREDRAW; /* Class style(s). */
\r
914 wc.lpfnWndProc = (WNDPROC)WndProc; /* Window Procedure */
\r
915 wc.cbClsExtra = 0; /* No per-class extra data. */
\r
916 wc.cbWndExtra = 0; /* No per-window extra data. */
\r
917 wc.hInstance = hInstance; /* Owner of this class */
\r
918 wc.hIcon = LoadIcon(hInstance, "icon_white");
\r
919 wc.hCursor = LoadCursor(NULL, IDC_ARROW); /* Cursor */
\r
920 wc.hbrBackground = (HBRUSH)(COLOR_WINDOW+1); /* Default color */
\r
921 wc.lpszMenuName = szAppName; /* Menu name from .RC */
\r
922 wc.lpszClassName = szAppName; /* Name to register as */
\r
924 /* Register the window class and return success/failure code. */
\r
925 if (!RegisterClass(&wc)) return FALSE;
\r
927 wc.style = CS_HREDRAW | CS_VREDRAW;
\r
928 wc.lpfnWndProc = (WNDPROC)ConsoleWndProc;
\r
930 wc.cbWndExtra = DLGWINDOWEXTRA;
\r
931 wc.hInstance = hInstance;
\r
932 wc.hIcon = LoadIcon(hInstance, "icon_white");
\r
933 wc.hCursor = LoadCursor(NULL, IDC_ARROW);
\r
934 wc.hbrBackground = (HBRUSH)(COLOR_MENU+1);
\r
935 wc.lpszMenuName = NULL;
\r
936 wc.lpszClassName = szConsoleName;
\r
938 if (!RegisterClass(&wc)) return FALSE;
\r
943 /* Set by InitInstance, used by EnsureOnScreen */
\r
944 int screenHeight, screenWidth;
\r
947 EnsureOnScreen(int *x, int *y, int minX, int minY)
\r
949 // int gap = GetSystemMetrics(SM_CYFRAME) + GetSystemMetrics(SM_CYCAPTION);
\r
950 /* Be sure window at (x,y) is not off screen (or even mostly off screen) */
\r
951 if (*x > screenWidth - 32) *x = 0;
\r
952 if (*y > screenHeight - 32) *y = 0;
\r
953 if (*x < minX) *x = minX;
\r
954 if (*y < minY) *y = minY;
\r
958 InitInstance(HINSTANCE hInstance, int nCmdShow, LPSTR lpCmdLine)
\r
960 HWND hwnd; /* Main window handle. */
\r
962 WINDOWPLACEMENT wp;
\r
965 hInst = hInstance; /* Store instance handle in our global variable */
\r
966 programName = szAppName;
\r
968 if (SearchPath(NULL, "WinBoard.exe", NULL, MSG_SIZ, installDir, &filepart)) {
\r
969 *filepart = NULLCHAR;
\r
971 GetCurrentDirectory(MSG_SIZ, installDir);
\r
973 gameInfo.boardWidth = gameInfo.boardHeight = 8; // [HGM] won't have open window otherwise
\r
974 screenWidth = screenHeight = 1000; // [HGM] placement: kludge to allow calling EnsureOnScreen from InitAppData
\r
975 InitAppData(lpCmdLine); /* Get run-time parameters */
\r
976 /* xboard, and older WinBoards, controlled the move sound with the
\r
977 appData.ringBellAfterMoves option. In the current WinBoard, we
\r
978 always turn the option on (so that the backend will call us),
\r
979 then let the user turn the sound off by setting it to silence if
\r
980 desired. To accommodate old winboard.ini files saved by old
\r
981 versions of WinBoard, we also turn off the sound if the option
\r
982 was initially set to false. [HGM] taken out of InitAppData */
\r
983 if (!appData.ringBellAfterMoves) {
\r
984 sounds[(int)SoundMove].name = strdup("");
\r
985 appData.ringBellAfterMoves = TRUE;
\r
987 if (appData.debugMode) {
\r
988 debugFP = fopen(appData.nameOfDebugFile, "w");
\r
989 setbuf(debugFP, NULL);
\r
992 LoadLanguageFile(appData.language);
\r
996 // InitEngineUCI( installDir, &first ); // [HGM] incorporated in InitBackEnd1()
\r
997 // InitEngineUCI( installDir, &second );
\r
999 /* Create a main window for this application instance. */
\r
1000 hwnd = CreateWindow(szAppName, szTitle,
\r
1001 (WS_OVERLAPPEDWINDOW & ~WS_MAXIMIZEBOX),
\r
1002 CW_USEDEFAULT, 0, CW_USEDEFAULT, 0,
\r
1003 NULL, NULL, hInstance, NULL);
\r
1006 /* If window could not be created, return "failure" */
\r
1011 /* [HGM] logo: Load logos if specified (must be done before InitDrawingSizes) */
\r
1012 if( appData.firstLogo && appData.firstLogo[0] != NULLCHAR) {
\r
1013 first.programLogo = LoadImage( 0, appData.firstLogo, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );
\r
1015 if (first.programLogo == NULL && appData.debugMode) {
\r
1016 fprintf( debugFP, "Unable to load logo bitmap '%s'\n", appData.firstLogo );
\r
1018 } else if(appData.autoLogo) {
\r
1019 if(appData.firstDirectory && appData.firstDirectory[0]) {
\r
1020 char buf[MSG_SIZ];
\r
1021 snprintf(buf, MSG_SIZ, "%s/logo.bmp", appData.firstDirectory);
\r
1022 first.programLogo = LoadImage( 0, buf, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );
\r
1026 if( appData.secondLogo && appData.secondLogo[0] != NULLCHAR) {
\r
1027 second.programLogo = LoadImage( 0, appData.secondLogo, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );
\r
1029 if (second.programLogo == NULL && appData.debugMode) {
\r
1030 fprintf( debugFP, "Unable to load logo bitmap '%s'\n", appData.secondLogo );
\r
1032 } else if(appData.autoLogo) {
\r
1033 char buf[MSG_SIZ];
\r
1034 if(appData.icsActive) { // [HGM] logo: in ICS mode second can be used for ICS
\r
1035 snprintf(buf, MSG_SIZ, "logos\\%s.bmp", appData.icsHost);
\r
1036 second.programLogo = LoadImage( 0, buf, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );
\r
1038 if(appData.secondDirectory && appData.secondDirectory[0]) {
\r
1039 snprintf(buf, MSG_SIZ, "%s\\logo.bmp", appData.secondDirectory);
\r
1040 second.programLogo = LoadImage( 0, buf, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );
\r
1046 iconWhite = LoadIcon(hInstance, "icon_white");
\r
1047 iconBlack = LoadIcon(hInstance, "icon_black");
\r
1048 iconCurrent = iconWhite;
\r
1049 InitDrawingColors();
\r
1050 screenHeight = GetSystemMetrics(SM_CYSCREEN);
\r
1051 screenWidth = GetSystemMetrics(SM_CXSCREEN);
\r
1052 for (ibs = (int) NUM_SIZES - 1; ibs >= 0; ibs--) {
\r
1053 /* Compute window size for each board size, and use the largest
\r
1054 size that fits on this screen as the default. */
\r
1055 InitDrawingSizes((BoardSize)(ibs+1000), 0);
\r
1056 if (boardSize == (BoardSize)-1 &&
\r
1057 winH <= screenHeight
\r
1058 - GetSystemMetrics(SM_CYFRAME) - GetSystemMetrics(SM_CYCAPTION) - 10
\r
1059 && winW <= screenWidth) {
\r
1060 boardSize = (BoardSize)ibs;
\r
1064 InitDrawingSizes(boardSize, 0);
\r
1066 buttonCount = GetSystemMetrics(SM_CMOUSEBUTTONS);
\r
1068 /* [AS] Load textures if specified */
\r
1069 ZeroMemory( &backTextureSquareInfo, sizeof(backTextureSquareInfo) );
\r
1071 if( appData.liteBackTextureFile && appData.liteBackTextureFile[0] != NULLCHAR && appData.liteBackTextureFile[0] != '*' ) {
\r
1072 liteBackTexture = LoadImage( 0, appData.liteBackTextureFile, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );
\r
1073 liteBackTextureMode = appData.liteBackTextureMode;
\r
1075 if (liteBackTexture == NULL && appData.debugMode) {
\r
1076 fprintf( debugFP, "Unable to load lite texture bitmap '%s'\n", appData.liteBackTextureFile );
\r
1080 if( appData.darkBackTextureFile && appData.darkBackTextureFile[0] != NULLCHAR && appData.darkBackTextureFile[0] != '*' ) {
\r
1081 darkBackTexture = LoadImage( 0, appData.darkBackTextureFile, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );
\r
1082 darkBackTextureMode = appData.darkBackTextureMode;
\r
1084 if (darkBackTexture == NULL && appData.debugMode) {
\r
1085 fprintf( debugFP, "Unable to load dark texture bitmap '%s'\n", appData.darkBackTextureFile );
\r
1089 mysrandom( (unsigned) time(NULL) );
\r
1091 /* [AS] Restore layout */
\r
1092 if( wpMoveHistory.visible ) {
\r
1093 MoveHistoryPopUp();
\r
1096 if( wpEvalGraph.visible ) {
\r
1100 if( wpEngineOutput.visible ) {
\r
1101 EngineOutputPopUp();
\r
1104 /* Make the window visible; update its client area; and return "success" */
\r
1105 EnsureOnScreen(&wpMain.x, &wpMain.y, minX, minY);
\r
1106 wp.length = sizeof(WINDOWPLACEMENT);
\r
1108 wp.showCmd = nCmdShow;
\r
1109 wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;
\r
1110 wp.rcNormalPosition.left = wpMain.x;
\r
1111 wp.rcNormalPosition.right = wpMain.x + wpMain.width;
\r
1112 wp.rcNormalPosition.top = wpMain.y;
\r
1113 wp.rcNormalPosition.bottom = wpMain.y + wpMain.height;
\r
1114 SetWindowPlacement(hwndMain, &wp);
\r
1116 InitBackEnd2(); // [HGM] moved until after all windows placed, to save correct position if fatal error on engine start
\r
1118 if(!appData.noGUI) SetWindowPos(hwndMain, alwaysOnTop ? HWND_TOPMOST : HWND_NOTOPMOST,
\r
1119 0, 0, 0, 0, SWP_NOMOVE|SWP_NOSIZE);
\r
1121 if (hwndConsole) {
\r
1123 SetWindowPos(hwndConsole, alwaysOnTop ? HWND_TOPMOST : HWND_NOTOPMOST,
\r
1124 0, 0, 0, 0, SWP_NOMOVE|SWP_NOSIZE);
\r
1126 ShowWindow(hwndConsole, nCmdShow);
\r
1127 if(appData.chatBoxes) { // [HGM] chat: open chat boxes
\r
1128 char buf[MSG_SIZ], *p = buf, *q;
\r
1129 safeStrCpy(buf, appData.chatBoxes, sizeof(buf)/sizeof(buf[0]) );
\r
1131 q = strchr(p, ';');
\r
1133 if(*p) ChatPopUp(p);
\r
1136 SetActiveWindow(hwndConsole);
\r
1138 if(!appData.noGUI) UpdateWindow(hwnd); else ShowWindow(hwnd, SW_MINIMIZE);
\r
1139 if(gameListDialog) SetFocus(gameListDialog); // [HGM] jaws: for if we clicked multi-game game file
\r
1148 HMENU hmenu = GetMenu(hwndMain);
\r
1150 (void) EnableMenuItem(hmenu, IDM_CommPort,
\r
1151 MF_BYCOMMAND|((appData.icsActive &&
\r
1152 *appData.icsCommPort != NULLCHAR) ?
\r
1153 MF_ENABLED : MF_GRAYED));
\r
1154 (void) CheckMenuItem(hmenu, IDM_SaveSettingsOnExit,
\r
1155 MF_BYCOMMAND|(saveSettingsOnExit ?
\r
1156 MF_CHECKED : MF_UNCHECKED));
\r
1159 //---------------------------------------------------------------------------------------------------------
\r
1161 #define ICS_TEXT_MENU_SIZE (IDM_CommandXLast - IDM_CommandX + 1)
\r
1162 #define XBOARD FALSE
\r
1164 #define OPTCHAR "/"
\r
1165 #define SEPCHAR "="
\r
1169 // front-end part of option handling
\r
1172 LFfromMFP(LOGFONT* lf, MyFontParams *mfp)
\r
1174 HDC hdc = CreateDC("DISPLAY", NULL, NULL, NULL);
\r
1175 lf->lfHeight = -(int)(mfp->pointSize * GetDeviceCaps(hdc, LOGPIXELSY) / 72.0 + 0.5);
\r
1178 lf->lfEscapement = 0;
\r
1179 lf->lfOrientation = 0;
\r
1180 lf->lfWeight = mfp->bold ? FW_BOLD : FW_NORMAL;
\r
1181 lf->lfItalic = mfp->italic;
\r
1182 lf->lfUnderline = mfp->underline;
\r
1183 lf->lfStrikeOut = mfp->strikeout;
\r
1184 lf->lfCharSet = mfp->charset;
\r
1185 lf->lfOutPrecision = OUT_DEFAULT_PRECIS;
\r
1186 lf->lfClipPrecision = CLIP_DEFAULT_PRECIS;
\r
1187 lf->lfQuality = DEFAULT_QUALITY;
\r
1188 lf->lfPitchAndFamily = DEFAULT_PITCH|FF_DONTCARE;
\r
1189 safeStrCpy(lf->lfFaceName, mfp->faceName, sizeof(lf->lfFaceName)/sizeof(lf->lfFaceName[0]) );
\r
1193 CreateFontInMF(MyFont *mf)
\r
1195 LFfromMFP(&mf->lf, &mf->mfp);
\r
1196 if (mf->hf) DeleteObject(mf->hf);
\r
1197 mf->hf = CreateFontIndirect(&mf->lf);
\r
1200 // [HGM] This platform-dependent table provides the location for storing the color info
\r
1202 colorVariable[] = {
\r
1203 &whitePieceColor,
\r
1204 &blackPieceColor,
\r
1205 &lightSquareColor,
\r
1206 &darkSquareColor,
\r
1207 &highlightSquareColor,
\r
1208 &premoveHighlightColor,
\r
1210 &consoleBackgroundColor,
\r
1211 &appData.fontForeColorWhite,
\r
1212 &appData.fontBackColorWhite,
\r
1213 &appData.fontForeColorBlack,
\r
1214 &appData.fontBackColorBlack,
\r
1215 &appData.evalHistColorWhite,
\r
1216 &appData.evalHistColorBlack,
\r
1217 &appData.highlightArrowColor,
\r
1220 /* Command line font name parser. NULL name means do nothing.
\r
1221 Syntax like "Courier New:10.0 bi" or "Arial:10" or "Arial:10b"
\r
1222 For backward compatibility, syntax without the colon is also
\r
1223 accepted, but font names with digits in them won't work in that case.
\r
1226 ParseFontName(char *name, MyFontParams *mfp)
\r
1229 if (name == NULL) return;
\r
1231 q = strchr(p, ':');
\r
1233 if (q - p >= sizeof(mfp->faceName))
\r
1234 ExitArgError(_("Font name too long:"), name);
\r
1235 memcpy(mfp->faceName, p, q - p);
\r
1236 mfp->faceName[q - p] = NULLCHAR;
\r
1239 q = mfp->faceName;
\r
1240 while (*p && !isdigit(*p)) {
\r
1242 if (q - mfp->faceName >= sizeof(mfp->faceName))
\r
1243 ExitArgError(_("Font name too long:"), name);
\r
1245 while (q > mfp->faceName && q[-1] == ' ') q--;
\r
1248 if (!*p) ExitArgError(_("Font point size missing:"), name);
\r
1249 mfp->pointSize = (float) atof(p);
\r
1250 mfp->bold = (strchr(p, 'b') != NULL);
\r
1251 mfp->italic = (strchr(p, 'i') != NULL);
\r
1252 mfp->underline = (strchr(p, 'u') != NULL);
\r
1253 mfp->strikeout = (strchr(p, 's') != NULL);
\r
1254 mfp->charset = DEFAULT_CHARSET;
\r
1255 q = strchr(p, 'c');
\r
1257 mfp->charset = (BYTE) atoi(q+1);
\r
1261 ParseFont(char *name, int number)
\r
1262 { // wrapper to shield back-end from 'font'
\r
1263 ParseFontName(name, &font[boardSize][number]->mfp);
\r
1268 { // in WB we have a 2D array of fonts; this initializes their description
\r
1270 /* Point font array elements to structures and
\r
1271 parse default font names */
\r
1272 for (i=0; i<NUM_FONTS; i++) {
\r
1273 for (j=0; j<NUM_SIZES; j++) {
\r
1274 font[j][i] = &fontRec[j][i];
\r
1275 ParseFontName(font[j][i]->def, &font[j][i]->mfp);
\r
1282 { // here we create the actual fonts from the selected descriptions
\r
1284 for (i=0; i<NUM_FONTS; i++) {
\r
1285 for (j=0; j<NUM_SIZES; j++) {
\r
1286 CreateFontInMF(font[j][i]);
\r
1290 /* Color name parser.
\r
1291 X version accepts X color names, but this one
\r
1292 handles only the #rrggbb form (hex) or rrr,ggg,bbb (decimal) */
\r
1294 ParseColorName(char *name)
\r
1296 int red, green, blue, count;
\r
1297 char buf[MSG_SIZ];
\r
1299 count = sscanf(name, "#%2x%2x%2x", &red, &green, &blue);
\r
1301 count = sscanf(name, "%3d%*[^0-9]%3d%*[^0-9]%3d",
\r
1302 &red, &green, &blue);
\r
1305 snprintf(buf, MSG_SIZ, _("Can't parse color name %s"), name);
\r
1306 DisplayError(buf, 0);
\r
1307 return RGB(0, 0, 0);
\r
1309 return PALETTERGB(red, green, blue);
\r
1313 ParseColor(int n, char *name)
\r
1314 { // for WinBoard the color is an int, which needs to be derived from the string
\r
1315 if(colorVariable[n]) *(int*)colorVariable[n] = ParseColorName(name);
\r
1319 ParseAttribs(COLORREF *color, int *effects, char* argValue)
\r
1321 char *e = argValue;
\r
1325 if (*e == 'b') eff |= CFE_BOLD;
\r
1326 else if (*e == 'i') eff |= CFE_ITALIC;
\r
1327 else if (*e == 'u') eff |= CFE_UNDERLINE;
\r
1328 else if (*e == 's') eff |= CFE_STRIKEOUT;
\r
1329 else if (*e == '#' || isdigit(*e)) break;
\r
1333 *color = ParseColorName(e);
\r
1337 ParseTextAttribs(ColorClass cc, char *s)
\r
1338 { // [HGM] front-end wrapper that does the platform-dependent call
\r
1339 // for XBoard we would set (&appData.colorShout)[cc] = strdup(s);
\r
1340 ParseAttribs(&textAttribs[cc].color, &textAttribs[cc].effects, s);
\r
1344 ParseBoardSize(void *addr, char *name)
\r
1345 { // [HGM] rewritten with return-value ptr to shield back-end from BoardSize
\r
1346 BoardSize bs = SizeTiny;
\r
1347 while (sizeInfo[bs].name != NULL) {
\r
1348 if (StrCaseCmp(name, sizeInfo[bs].name) == 0) {
\r
1349 *(BoardSize *)addr = bs;
\r
1354 ExitArgError(_("Unrecognized board size value"), name);
\r
1359 { // [HGM] import name from appData first
\r
1362 for (cc = (ColorClass)0; cc < ColorNormal; cc++) {
\r
1363 textAttribs[cc].sound.name = strdup((&appData.soundShout)[cc]);
\r
1364 textAttribs[cc].sound.data = NULL;
\r
1365 MyLoadSound(&textAttribs[cc].sound);
\r
1367 for (cc = ColorNormal; cc < NColorClasses; cc++) {
\r
1368 textAttribs[cc].sound.name = strdup("");
\r
1369 textAttribs[cc].sound.data = NULL;
\r
1371 for (sc = (SoundClass)0; sc < NSoundClasses; sc++) {
\r
1372 sounds[sc].name = strdup((&appData.soundMove)[sc]);
\r
1373 sounds[sc].data = NULL;
\r
1374 MyLoadSound(&sounds[sc]);
\r
1379 SetCommPortDefaults()
\r
1381 memset(&dcb, 0, sizeof(DCB)); // required by VS 2002 +
\r
1382 dcb.DCBlength = sizeof(DCB);
\r
1383 dcb.BaudRate = 9600;
\r
1384 dcb.fBinary = TRUE;
\r
1385 dcb.fParity = FALSE;
\r
1386 dcb.fOutxCtsFlow = FALSE;
\r
1387 dcb.fOutxDsrFlow = FALSE;
\r
1388 dcb.fDtrControl = DTR_CONTROL_ENABLE;
\r
1389 dcb.fDsrSensitivity = FALSE;
\r
1390 dcb.fTXContinueOnXoff = TRUE;
\r
1391 dcb.fOutX = FALSE;
\r
1393 dcb.fNull = FALSE;
\r
1394 dcb.fRtsControl = RTS_CONTROL_ENABLE;
\r
1395 dcb.fAbortOnError = FALSE;
\r
1397 dcb.Parity = SPACEPARITY;
\r
1398 dcb.StopBits = ONESTOPBIT;
\r
1401 // [HGM] args: these three cases taken out to stay in front-end
\r
1403 SaveFontArg(FILE *f, ArgDescriptor *ad)
\r
1404 { // in WinBoard every board size has its own font, and the "argLoc" identifies the table,
\r
1405 // while the curent board size determines the element. This system should be ported to XBoard.
\r
1406 // What the table contains pointers to, and how to print the font description, remains platform-dependent
\r
1408 for (bs=0; bs<NUM_SIZES; bs++) {
\r
1409 MyFontParams *mfp = &font[bs][(int) ad->argLoc]->mfp;
\r
1410 fprintf(f, "/size=%s ", sizeInfo[bs].name);
\r
1411 fprintf(f, "/%s=\"%s:%g%s%s%s%s%sc%d\"\n",
\r
1412 ad->argName, mfp->faceName, mfp->pointSize,
\r
1413 mfp->bold || mfp->italic || mfp->underline || mfp->strikeout ? " " : "",
\r
1414 mfp->bold ? "b" : "",
\r
1415 mfp->italic ? "i" : "",
\r
1416 mfp->underline ? "u" : "",
\r
1417 mfp->strikeout ? "s" : "",
\r
1418 (int)mfp->charset);
\r
1424 { // [HGM] copy the names from the internal WB variables to appData
\r
1427 for (cc = (ColorClass)0; cc < ColorNormal; cc++)
\r
1428 (&appData.soundShout)[cc] = textAttribs[cc].sound.name;
\r
1429 for (sc = (SoundClass)0; sc < NSoundClasses; sc++)
\r
1430 (&appData.soundMove)[sc] = sounds[sc].name;
\r
1434 SaveAttribsArg(FILE *f, ArgDescriptor *ad)
\r
1435 { // here the "argLoc" defines a table index. It could have contained the 'ta' pointer itself, though
\r
1436 MyTextAttribs* ta = &textAttribs[(ColorClass)ad->argLoc];
\r
1437 fprintf(f, "/%s=\"%s%s%s%s%s#%02lx%02lx%02lx\"\n", ad->argName,
\r
1438 (ta->effects & CFE_BOLD) ? "b" : "",
\r
1439 (ta->effects & CFE_ITALIC) ? "i" : "",
\r
1440 (ta->effects & CFE_UNDERLINE) ? "u" : "",
\r
1441 (ta->effects & CFE_STRIKEOUT) ? "s" : "",
\r
1442 (ta->effects) ? " " : "",
\r
1443 ta->color&0xff, (ta->color >> 8)&0xff, (ta->color >> 16)&0xff);
\r
1447 SaveColor(FILE *f, ArgDescriptor *ad)
\r
1448 { // in WinBoard the color is an int and has to be converted to text. In X it would be a string already?
\r
1449 COLORREF color = *(COLORREF *)colorVariable[(int)ad->argLoc];
\r
1450 fprintf(f, "/%s=#%02lx%02lx%02lx\n", ad->argName,
\r
1451 color&0xff, (color>>8)&0xff, (color>>16)&0xff);
\r
1455 SaveBoardSize(FILE *f, char *name, void *addr)
\r
1456 { // wrapper to shield back-end from BoardSize & sizeInfo
\r
1457 fprintf(f, "/%s=%s\n", name, sizeInfo[*(BoardSize *)addr].name);
\r
1461 ParseCommPortSettings(char *s)
\r
1462 { // wrapper to keep dcb from back-end
\r
1463 ParseCommSettings(s, &dcb);
\r
1468 { // wrapper to shield use of window handles from back-end (make addressible by number?)
\r
1469 GetActualPlacement(hwndMain, &wpMain);
\r
1470 GetActualPlacement(hwndConsole, &wpConsole);
\r
1471 GetActualPlacement(commentDialog, &wpComment);
\r
1472 GetActualPlacement(editTagsDialog, &wpTags);
\r
1473 GetActualPlacement(gameListDialog, &wpGameList);
\r
1474 GetActualPlacement(moveHistoryDialog, &wpMoveHistory);
\r
1475 GetActualPlacement(evalGraphDialog, &wpEvalGraph);
\r
1476 GetActualPlacement(engineOutputDialog, &wpEngineOutput);
\r
1480 PrintCommPortSettings(FILE *f, char *name)
\r
1481 { // wrapper to shield back-end from DCB
\r
1482 PrintCommSettings(f, name, &dcb);
\r
1486 MySearchPath(char *installDir, char *name, char *fullname)
\r
1488 char *dummy, buf[MSG_SIZ], *p = name, *q;
\r
1489 if(name[0]== '%') {
\r
1490 fullname[0] = 0; // [HGM] first expand any environment variables in the given name
\r
1491 while(*p == '%' && (q = strchr(p+1, '%'))) { // [HGM] recognize %*% as environment variable
\r
1492 safeStrCpy(buf, p+1, sizeof(buf)/sizeof(buf[0]) );
\r
1493 *strchr(buf, '%') = 0;
\r
1494 strcat(fullname, getenv(buf));
\r
1495 p = q+1; while(*p == '\\') { strcat(fullname, "\\"); p++; }
\r
1497 strcat(fullname, p); // after environment variables (if any), take the remainder of the given name
\r
1498 if(appData.debugMode) fprintf(debugFP, "name = '%s', expanded name = '%s'\n", name, fullname);
\r
1499 return (int) strlen(fullname);
\r
1501 return (int) SearchPath(installDir, name, NULL, MSG_SIZ, fullname, &dummy);
\r
1505 MyGetFullPathName(char *name, char *fullname)
\r
1508 return (int) GetFullPathName(name, MSG_SIZ, fullname, &dummy);
\r
1513 { // [HGM] args: allows testing if main window is realized from back-end
\r
1514 return hwndMain != NULL;
\r
1518 PopUpStartupDialog()
\r
1522 LoadLanguageFile(appData.language);
\r
1523 lpProc = MakeProcInstance((FARPROC)StartupDialog, hInst);
\r
1524 DialogBox(hInst, MAKEINTRESOURCE(DLG_Startup), NULL, (DLGPROC)lpProc);
\r
1525 FreeProcInstance(lpProc);
\r
1528 /*---------------------------------------------------------------------------*\
\r
1530 * GDI board drawing routines
\r
1532 \*---------------------------------------------------------------------------*/
\r
1534 /* [AS] Draw square using background texture */
\r
1535 static void DrawTile( int dx, int dy, int dw, int dh, HDC dst, HDC src, int mode, int sx, int sy )
\r
1540 return; /* Should never happen! */
\r
1543 SetGraphicsMode( dst, GM_ADVANCED );
\r
1550 /* X reflection */
\r
1555 x.eDx = (FLOAT) dw + dx - 1;
\r
1558 SetWorldTransform( dst, &x );
\r
1561 /* Y reflection */
\r
1567 x.eDy = (FLOAT) dh + dy - 1;
\r
1569 SetWorldTransform( dst, &x );
\r
1577 x.eDx = (FLOAT) dx;
\r
1578 x.eDy = (FLOAT) dy;
\r
1581 SetWorldTransform( dst, &x );
\r
1585 BitBlt( dst, dx, dy, dw, dh, src, sx, sy, SRCCOPY );
\r
1593 SetWorldTransform( dst, &x );
\r
1595 ModifyWorldTransform( dst, 0, MWT_IDENTITY );
\r
1598 /* [AS] [HGM] Make room for more piece types, so all pieces can be different */
\r
1600 PM_WP = (int) WhitePawn,
\r
1601 PM_WN = (int) WhiteKnight,
\r
1602 PM_WB = (int) WhiteBishop,
\r
1603 PM_WR = (int) WhiteRook,
\r
1604 PM_WQ = (int) WhiteQueen,
\r
1605 PM_WF = (int) WhiteFerz,
\r
1606 PM_WW = (int) WhiteWazir,
\r
1607 PM_WE = (int) WhiteAlfil,
\r
1608 PM_WM = (int) WhiteMan,
\r
1609 PM_WO = (int) WhiteCannon,
\r
1610 PM_WU = (int) WhiteUnicorn,
\r
1611 PM_WH = (int) WhiteNightrider,
\r
1612 PM_WA = (int) WhiteAngel,
\r
1613 PM_WC = (int) WhiteMarshall,
\r
1614 PM_WAB = (int) WhiteCardinal,
\r
1615 PM_WD = (int) WhiteDragon,
\r
1616 PM_WL = (int) WhiteLance,
\r
1617 PM_WS = (int) WhiteCobra,
\r
1618 PM_WV = (int) WhiteFalcon,
\r
1619 PM_WSG = (int) WhiteSilver,
\r
1620 PM_WG = (int) WhiteGrasshopper,
\r
1621 PM_WK = (int) WhiteKing,
\r
1622 PM_BP = (int) BlackPawn,
\r
1623 PM_BN = (int) BlackKnight,
\r
1624 PM_BB = (int) BlackBishop,
\r
1625 PM_BR = (int) BlackRook,
\r
1626 PM_BQ = (int) BlackQueen,
\r
1627 PM_BF = (int) BlackFerz,
\r
1628 PM_BW = (int) BlackWazir,
\r
1629 PM_BE = (int) BlackAlfil,
\r
1630 PM_BM = (int) BlackMan,
\r
1631 PM_BO = (int) BlackCannon,
\r
1632 PM_BU = (int) BlackUnicorn,
\r
1633 PM_BH = (int) BlackNightrider,
\r
1634 PM_BA = (int) BlackAngel,
\r
1635 PM_BC = (int) BlackMarshall,
\r
1636 PM_BG = (int) BlackGrasshopper,
\r
1637 PM_BAB = (int) BlackCardinal,
\r
1638 PM_BD = (int) BlackDragon,
\r
1639 PM_BL = (int) BlackLance,
\r
1640 PM_BS = (int) BlackCobra,
\r
1641 PM_BV = (int) BlackFalcon,
\r
1642 PM_BSG = (int) BlackSilver,
\r
1643 PM_BK = (int) BlackKing
\r
1646 static HFONT hPieceFont = NULL;
\r
1647 static HBITMAP hPieceMask[(int) EmptySquare];
\r
1648 static HBITMAP hPieceFace[(int) EmptySquare];
\r
1649 static int fontBitmapSquareSize = 0;
\r
1650 static char pieceToFontChar[(int) EmptySquare] =
\r
1651 { 'p', 'n', 'b', 'r', 'q',
\r
1652 'n', 'b', 'p', 'n', 'b', 'r', 'b', 'r', 'q', 'k',
\r
1653 'k', 'o', 'm', 'v', 't', 'w',
\r
1654 'v', 't', 'o', 'm', 'v', 't', 'v', 't', 'w', 'l',
\r
1657 extern BOOL SetCharTable( char *table, const char * map );
\r
1658 /* [HGM] moved to backend.c */
\r
1660 static void SetPieceBackground( HDC hdc, COLORREF color, int mode )
\r
1663 BYTE r1 = GetRValue( color );
\r
1664 BYTE g1 = GetGValue( color );
\r
1665 BYTE b1 = GetBValue( color );
\r
1671 /* Create a uniform background first */
\r
1672 hbrush = CreateSolidBrush( color );
\r
1673 SetRect( &rc, 0, 0, squareSize, squareSize );
\r
1674 FillRect( hdc, &rc, hbrush );
\r
1675 DeleteObject( hbrush );
\r
1678 /* Vertical gradient, good for pawn, knight and rook, less for queen and king */
\r
1679 int steps = squareSize / 2;
\r
1682 for( i=0; i<steps; i++ ) {
\r
1683 BYTE r = r1 - (r1-r2) * i / steps;
\r
1684 BYTE g = g1 - (g1-g2) * i / steps;
\r
1685 BYTE b = b1 - (b1-b2) * i / steps;
\r
1687 hbrush = CreateSolidBrush( RGB(r,g,b) );
\r
1688 SetRect( &rc, i + squareSize - steps, 0, i + squareSize - steps + 1, squareSize );
\r
1689 FillRect( hdc, &rc, hbrush );
\r
1690 DeleteObject(hbrush);
\r
1693 else if( mode == 2 ) {
\r
1694 /* Diagonal gradient, good more or less for every piece */
\r
1695 POINT triangle[3];
\r
1696 HPEN hpen = SelectObject( hdc, GetStockObject(NULL_PEN) );
\r
1697 HBRUSH hbrush_old;
\r
1698 int steps = squareSize;
\r
1701 triangle[0].x = squareSize - steps;
\r
1702 triangle[0].y = squareSize;
\r
1703 triangle[1].x = squareSize;
\r
1704 triangle[1].y = squareSize;
\r
1705 triangle[2].x = squareSize;
\r
1706 triangle[2].y = squareSize - steps;
\r
1708 for( i=0; i<steps; i++ ) {
\r
1709 BYTE r = r1 - (r1-r2) * i / steps;
\r
1710 BYTE g = g1 - (g1-g2) * i / steps;
\r
1711 BYTE b = b1 - (b1-b2) * i / steps;
\r
1713 hbrush = CreateSolidBrush( RGB(r,g,b) );
\r
1714 hbrush_old = SelectObject( hdc, hbrush );
\r
1715 Polygon( hdc, triangle, 3 );
\r
1716 SelectObject( hdc, hbrush_old );
\r
1717 DeleteObject(hbrush);
\r
1722 SelectObject( hdc, hpen );
\r
1727 [AS] The method I use to create the bitmaps it a bit tricky, but it
\r
1728 seems to work ok. The main problem here is to find the "inside" of a chess
\r
1729 piece: follow the steps as explained below.
\r
1731 static void CreatePieceMaskFromFont( HDC hdc_window, HDC hdc, int index )
\r
1735 COLORREF chroma = RGB(0xFF,0x00,0xFF);
\r
1739 int backColor = whitePieceColor;
\r
1740 int foreColor = blackPieceColor;
\r
1742 if( index < (int)BlackPawn && appData.fontBackColorWhite != appData.fontForeColorWhite ) {
\r
1743 backColor = appData.fontBackColorWhite;
\r
1744 foreColor = appData.fontForeColorWhite;
\r
1746 else if( index >= (int)BlackPawn && appData.fontBackColorBlack != appData.fontForeColorBlack ) {
\r
1747 backColor = appData.fontBackColorBlack;
\r
1748 foreColor = appData.fontForeColorBlack;
\r
1752 hbm = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );
\r
1754 hbm_old = SelectObject( hdc, hbm );
\r
1758 rc.right = squareSize;
\r
1759 rc.bottom = squareSize;
\r
1761 /* Step 1: background is now black */
\r
1762 FillRect( hdc, &rc, GetStockObject(BLACK_BRUSH) );
\r
1764 GetTextExtentPoint32( hdc, &pieceToFontChar[index], 1, &sz );
\r
1766 pt.x = (squareSize - sz.cx) / 2;
\r
1767 pt.y = (squareSize - sz.cy) / 2;
\r
1769 SetBkMode( hdc, TRANSPARENT );
\r
1770 SetTextColor( hdc, chroma );
\r
1771 /* Step 2: the piece has been drawn in purple, there are now black and purple in this bitmap */
\r
1772 TextOut( hdc, pt.x, pt.y, &pieceToFontChar[appData.allWhite && index >= (int)BlackPawn ? index - (int)BlackPawn : index], 1 );
\r
1774 SelectObject( hdc, GetStockObject(WHITE_BRUSH) );
\r
1775 /* Step 3: the area outside the piece is filled with white */
\r
1776 // FloodFill( hdc, 0, 0, chroma );
\r
1777 ExtFloodFill( hdc, 0, 0, 0, FLOODFILLSURFACE );
\r
1778 ExtFloodFill( hdc, 0, squareSize-1, 0, FLOODFILLSURFACE ); // [HGM] fill from all 4 corners, for if piece too big
\r
1779 ExtFloodFill( hdc, squareSize-1, 0, 0, FLOODFILLSURFACE );
\r
1780 ExtFloodFill( hdc, squareSize-1, squareSize-1, 0, FLOODFILLSURFACE );
\r
1781 SelectObject( hdc, GetStockObject(BLACK_BRUSH) );
\r
1783 Step 4: this is the tricky part, the area inside the piece is filled with black,
\r
1784 but if the start point is not inside the piece we're lost!
\r
1785 There should be a better way to do this... if we could create a region or path
\r
1786 from the fill operation we would be fine for example.
\r
1788 // FloodFill( hdc, squareSize / 2, squareSize / 2, RGB(0xFF,0xFF,0xFF) );
\r
1789 ExtFloodFill( hdc, squareSize / 2, squareSize / 2, RGB(0xFF,0xFF,0xFF), FLOODFILLBORDER );
\r
1791 { /* [HGM] shave off edges of mask, in an attempt to correct for the fact that FloodFill does not work correctly under Win XP */
\r
1792 HDC dc2 = CreateCompatibleDC( hdc_window );
\r
1793 HBITMAP bm2 = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );
\r
1795 SelectObject( dc2, bm2 );
\r
1796 BitBlt( dc2, 0, 0, squareSize, squareSize, hdc, 0, 0, SRCCOPY ); // make copy
\r
1797 BitBlt( hdc, 0, 1, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );
\r
1798 BitBlt( hdc, 2, 1, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );
\r
1799 BitBlt( hdc, 1, 0, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );
\r
1800 BitBlt( hdc, 1, 2, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );
\r
1803 DeleteObject( bm2 );
\r
1806 SetTextColor( hdc, 0 );
\r
1808 Step 5: some fonts have "disconnected" areas that are skipped by the fill:
\r
1809 draw the piece again in black for safety.
\r
1811 TextOut( hdc, pt.x, pt.y, &pieceToFontChar[appData.allWhite && index >= (int)BlackPawn ? index - (int)BlackPawn : index], 1 );
\r
1813 SelectObject( hdc, hbm_old );
\r
1815 if( hPieceMask[index] != NULL ) {
\r
1816 DeleteObject( hPieceMask[index] );
\r
1819 hPieceMask[index] = hbm;
\r
1822 hbm = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );
\r
1824 SelectObject( hdc, hbm );
\r
1827 HDC dc1 = CreateCompatibleDC( hdc_window );
\r
1828 HDC dc2 = CreateCompatibleDC( hdc_window );
\r
1829 HBITMAP bm2 = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );
\r
1831 SelectObject( dc1, hPieceMask[index] );
\r
1832 SelectObject( dc2, bm2 );
\r
1833 FillRect( dc2, &rc, GetStockObject(WHITE_BRUSH) );
\r
1834 BitBlt( dc2, 0, 0, squareSize, squareSize, dc1, 0, 0, SRCINVERT );
\r
1837 Now dc2 contains the inverse of the piece mask, i.e. a mask that preserves
\r
1838 the piece background and deletes (makes transparent) the rest.
\r
1839 Thanks to that mask, we are free to paint the background with the greates
\r
1840 freedom, as we'll be able to mask off the unwanted parts when finished.
\r
1841 We use this, to make gradients and give the pieces a "roundish" look.
\r
1843 SetPieceBackground( hdc, backColor, 2 );
\r
1844 BitBlt( hdc, 0, 0, squareSize, squareSize, dc2, 0, 0, SRCAND );
\r
1848 DeleteObject( bm2 );
\r
1851 SetTextColor( hdc, foreColor );
\r
1852 TextOut( hdc, pt.x, pt.y, &pieceToFontChar[appData.allWhite && index >= (int)BlackPawn ? index - (int)BlackPawn : index], 1 );
\r
1854 SelectObject( hdc, hbm_old );
\r
1856 if( hPieceFace[index] != NULL ) {
\r
1857 DeleteObject( hPieceFace[index] );
\r
1860 hPieceFace[index] = hbm;
\r
1863 static int TranslatePieceToFontPiece( int piece )
\r
1893 case BlackMarshall:
\r
1897 case BlackNightrider:
\r
1903 case BlackUnicorn:
\r
1907 case BlackGrasshopper:
\r
1919 case BlackCardinal:
\r
1926 case WhiteMarshall:
\r
1930 case WhiteNightrider:
\r
1936 case WhiteUnicorn:
\r
1940 case WhiteGrasshopper:
\r
1952 case WhiteCardinal:
\r
1961 void CreatePiecesFromFont()
\r
1964 HDC hdc_window = NULL;
\r
1970 if( fontBitmapSquareSize < 0 ) {
\r
1971 /* Something went seriously wrong in the past: do not try to recreate fonts! */
\r
1975 if( appData.renderPiecesWithFont == NULL || appData.renderPiecesWithFont[0] == NULLCHAR || appData.renderPiecesWithFont[0] == '*' ) {
\r
1976 fontBitmapSquareSize = -1;
\r
1980 if( fontBitmapSquareSize != squareSize ) {
\r
1981 hdc_window = GetDC( hwndMain );
\r
1982 hdc = CreateCompatibleDC( hdc_window );
\r
1984 if( hPieceFont != NULL ) {
\r
1985 DeleteObject( hPieceFont );
\r
1988 for( i=0; i<=(int)BlackKing; i++ ) {
\r
1989 hPieceMask[i] = NULL;
\r
1990 hPieceFace[i] = NULL;
\r
1996 if( appData.fontPieceSize >= 50 && appData.fontPieceSize <= 150 ) {
\r
1997 fontHeight = appData.fontPieceSize;
\r
2000 fontHeight = (fontHeight * squareSize) / 100;
\r
2002 lf.lfHeight = -MulDiv( fontHeight, GetDeviceCaps(hdc, LOGPIXELSY), 72 );
\r
2004 lf.lfEscapement = 0;
\r
2005 lf.lfOrientation = 0;
\r
2006 lf.lfWeight = FW_NORMAL;
\r
2008 lf.lfUnderline = 0;
\r
2009 lf.lfStrikeOut = 0;
\r
2010 lf.lfCharSet = DEFAULT_CHARSET;
\r
2011 lf.lfOutPrecision = OUT_DEFAULT_PRECIS;
\r
2012 lf.lfClipPrecision = CLIP_DEFAULT_PRECIS;
\r
2013 lf.lfQuality = PROOF_QUALITY;
\r
2014 lf.lfPitchAndFamily = DEFAULT_PITCH | FF_DONTCARE;
\r
2015 strncpy( lf.lfFaceName, appData.renderPiecesWithFont, sizeof(lf.lfFaceName) );
\r
2016 lf.lfFaceName[ sizeof(lf.lfFaceName) - 1 ] = '\0';
\r
2018 hPieceFont = CreateFontIndirect( &lf );
\r
2020 if( hPieceFont == NULL ) {
\r
2021 fontBitmapSquareSize = -2;
\r
2024 /* Setup font-to-piece character table */
\r
2025 if( ! SetCharTable(pieceToFontChar, appData.fontToPieceTable) ) {
\r
2026 /* No (or wrong) global settings, try to detect the font */
\r
2027 if( strstr(lf.lfFaceName,"Alpha") != NULL ) {
\r
2029 SetCharTable(pieceToFontChar, "phbrqkojntwl");
\r
2031 else if( strstr(lf.lfFaceName,"DiagramTT") != NULL ) {
\r
2032 /* DiagramTT* family */
\r
2033 SetCharTable(pieceToFontChar, "PNLRQKpnlrqk");
\r
2035 else if( strstr(lf.lfFaceName,"WinboardF") != NULL ) {
\r
2036 /* Fairy symbols */
\r
2037 SetCharTable(pieceToFontChar, "PNBRQFEACWMOHIJGDVSLUKpnbrqfeacwmohijgdvsluk");
\r
2039 else if( strstr(lf.lfFaceName,"GC2004D") != NULL ) {
\r
2040 /* Good Companion (Some characters get warped as literal :-( */
\r
2041 char s[] = "1cmWG0??S??oYI23wgQU";
\r
2042 s[0]=0xB9; s[1]=0xA9; s[6]=0xB1; s[11]=0xBB; s[12]=0xAB; s[17]=0xB3;
\r
2043 SetCharTable(pieceToFontChar, s);
\r
2046 /* Cases, Condal, Leipzig, Lucena, Marroquin, Merida, Usual */
\r
2047 SetCharTable(pieceToFontChar, "pnbrqkomvtwl");
\r
2051 /* Create bitmaps */
\r
2052 hfont_old = SelectObject( hdc, hPieceFont );
\r
2053 for(i=(int)WhitePawn; i<(int)EmptySquare; i++) /* [HGM] made a loop for this */
\r
2054 if(PieceToChar((ChessSquare)i) != '.') /* skip unused pieces */
\r
2055 CreatePieceMaskFromFont( hdc_window, hdc, i );
\r
2057 SelectObject( hdc, hfont_old );
\r
2059 fontBitmapSquareSize = squareSize;
\r
2063 if( hdc != NULL ) {
\r
2067 if( hdc_window != NULL ) {
\r
2068 ReleaseDC( hwndMain, hdc_window );
\r
2073 DoLoadBitmap(HINSTANCE hinst, char *piece, int squareSize, char *suffix)
\r
2077 snprintf(name, sizeof(name)/sizeof(name[0]), "%s%d%s", piece, squareSize, suffix);
\r
2078 if (gameInfo.event &&
\r
2079 strcmp(gameInfo.event, "Easter Egg Hunt") == 0 &&
\r
2080 strcmp(name, "k80s") == 0) {
\r
2081 safeStrCpy(name, "tim", sizeof(name)/sizeof(name[0]) );
\r
2083 return LoadBitmap(hinst, name);
\r
2087 /* Insert a color into the program's logical palette
\r
2088 structure. This code assumes the given color is
\r
2089 the result of the RGB or PALETTERGB macro, and it
\r
2090 knows how those macros work (which is documented).
\r
2093 InsertInPalette(COLORREF color)
\r
2095 LPPALETTEENTRY pe = &(pLogPal->palPalEntry[pLogPal->palNumEntries]);
\r
2097 if (pLogPal->palNumEntries++ >= PALETTESIZE) {
\r
2098 DisplayFatalError(_("Too many colors"), 0, 1);
\r
2099 pLogPal->palNumEntries--;
\r
2103 pe->peFlags = (char) 0;
\r
2104 pe->peRed = (char) (0xFF & color);
\r
2105 pe->peGreen = (char) (0xFF & (color >> 8));
\r
2106 pe->peBlue = (char) (0xFF & (color >> 16));
\r
2112 InitDrawingColors()
\r
2114 if (pLogPal == NULL) {
\r
2115 /* Allocate enough memory for a logical palette with
\r
2116 * PALETTESIZE entries and set the size and version fields
\r
2117 * of the logical palette structure.
\r
2119 pLogPal = (NPLOGPALETTE)
\r
2120 LocalAlloc(LMEM_FIXED, (sizeof(LOGPALETTE) +
\r
2121 (sizeof(PALETTEENTRY) * (PALETTESIZE))));
\r
2122 pLogPal->palVersion = 0x300;
\r
2124 pLogPal->palNumEntries = 0;
\r
2126 InsertInPalette(lightSquareColor);
\r
2127 InsertInPalette(darkSquareColor);
\r
2128 InsertInPalette(whitePieceColor);
\r
2129 InsertInPalette(blackPieceColor);
\r
2130 InsertInPalette(highlightSquareColor);
\r
2131 InsertInPalette(premoveHighlightColor);
\r
2133 /* create a logical color palette according the information
\r
2134 * in the LOGPALETTE structure.
\r
2136 hPal = CreatePalette((LPLOGPALETTE) pLogPal);
\r
2138 lightSquareBrush = CreateSolidBrush(lightSquareColor);
\r
2139 blackSquareBrush = CreateSolidBrush(blackPieceColor);
\r
2140 darkSquareBrush = CreateSolidBrush(darkSquareColor);
\r
2141 whitePieceBrush = CreateSolidBrush(whitePieceColor);
\r
2142 blackPieceBrush = CreateSolidBrush(blackPieceColor);
\r
2143 iconBkgndBrush = CreateSolidBrush(GetSysColor(COLOR_BACKGROUND));
\r
2144 explodeBrush = CreateSolidBrush(highlightSquareColor); // [HGM] atomic
\r
2145 markerBrush = CreateSolidBrush(premoveHighlightColor); // [HGM] markers
\r
2146 /* [AS] Force rendering of the font-based pieces */
\r
2147 if( fontBitmapSquareSize > 0 ) {
\r
2148 fontBitmapSquareSize = 0;
\r
2154 BoardWidth(int boardSize, int n)
\r
2155 { /* [HGM] argument n added to allow different width and height */
\r
2156 int lineGap = sizeInfo[boardSize].lineGap;
\r
2158 if( appData.overrideLineGap >= 0 && appData.overrideLineGap <= 5 ) {
\r
2159 lineGap = appData.overrideLineGap;
\r
2162 return (n + 1) * lineGap +
\r
2163 n * sizeInfo[boardSize].squareSize;
\r
2166 /* Respond to board resize by dragging edge */
\r
2168 ResizeBoard(int newSizeX, int newSizeY, int flags)
\r
2170 BoardSize newSize = NUM_SIZES - 1;
\r
2171 static int recurse = 0;
\r
2172 if (IsIconic(hwndMain)) return;
\r
2173 if (recurse > 0) return;
\r
2175 while (newSize > 0) {
\r
2176 InitDrawingSizes(newSize+1000, 0); // [HGM] kludge to update sizeInfo without visible effects
\r
2177 if(newSizeX >= sizeInfo[newSize].cliWidth &&
\r
2178 newSizeY >= sizeInfo[newSize].cliHeight) break;
\r
2181 boardSize = newSize;
\r
2182 InitDrawingSizes(boardSize, flags);
\r
2187 extern Boolean twoBoards, partnerUp; // [HGM] dual
\r
2190 InitDrawingSizes(BoardSize boardSize, int flags)
\r
2192 int i, boardWidth, boardHeight; /* [HGM] height treated separately */
\r
2193 ChessSquare piece;
\r
2194 static int oldBoardSize = -1, oldTinyLayout = 0;
\r
2196 SIZE clockSize, messageSize;
\r
2198 char buf[MSG_SIZ];
\r
2200 HMENU hmenu = GetMenu(hwndMain);
\r
2201 RECT crect, wrect, oldRect;
\r
2203 LOGBRUSH logbrush;
\r
2205 int suppressVisibleEffects = 0; // [HGM] kludge to request updating sizeInfo only
\r
2206 if((int)boardSize >= 1000 ) { boardSize -= 1000; suppressVisibleEffects = 1; }
\r
2208 /* [HGM] call with -2 uses old size (for if nr of files, ranks changes) */
\r
2209 if(boardSize == (BoardSize)(-2) ) boardSize = oldBoardSize;
\r
2211 oldRect.left = wpMain.x; //[HGM] placement: remember previous window params
\r
2212 oldRect.top = wpMain.y;
\r
2213 oldRect.right = wpMain.x + wpMain.width;
\r
2214 oldRect.bottom = wpMain.y + wpMain.height;
\r
2216 tinyLayout = sizeInfo[boardSize].tinyLayout;
\r
2217 smallLayout = sizeInfo[boardSize].smallLayout;
\r
2218 squareSize = sizeInfo[boardSize].squareSize;
\r
2219 lineGap = sizeInfo[boardSize].lineGap;
\r
2220 minorSize = 0; /* [HGM] Kludge to see if demagnified pieces need to be shifted */
\r
2222 if( appData.overrideLineGap >= 0 && appData.overrideLineGap <= 5 ) {
\r
2223 lineGap = appData.overrideLineGap;
\r
2226 if (tinyLayout != oldTinyLayout) {
\r
2227 long style = GetWindowLongPtr(hwndMain, GWL_STYLE);
\r
2229 style &= ~WS_SYSMENU;
\r
2230 InsertMenu(hmenu, IDM_Exit, MF_BYCOMMAND, IDM_Minimize,
\r
2231 "&Minimize\tCtrl+F4");
\r
2233 style |= WS_SYSMENU;
\r
2234 RemoveMenu(hmenu, IDM_Minimize, MF_BYCOMMAND);
\r
2236 SetWindowLongPtr(hwndMain, GWL_STYLE, style);
\r
2238 for (i=0; menuBarText[tinyLayout][i]; i++) {
\r
2239 ModifyMenu(hmenu, i, MF_STRING|MF_BYPOSITION|MF_POPUP,
\r
2240 (UINT)GetSubMenu(hmenu, i), T_(menuBarText[tinyLayout][i]));
\r
2242 DrawMenuBar(hwndMain);
\r
2245 boardWidth = BoardWidth(boardSize, BOARD_WIDTH);
\r
2246 boardHeight = BoardWidth(boardSize, BOARD_HEIGHT);
\r
2248 /* Get text area sizes */
\r
2249 hdc = GetDC(hwndMain);
\r
2250 if (appData.clockMode) {
\r
2251 snprintf(buf, MSG_SIZ, _("White: %s"), TimeString(23*60*60*1000L));
\r
2253 snprintf(buf, MSG_SIZ, _("White"));
\r
2255 oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);
\r
2256 GetTextExtentPoint(hdc, buf, strlen(buf), &clockSize);
\r
2257 SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);
\r
2258 str = _("We only care about the height here");
\r
2259 GetTextExtentPoint(hdc, str, strlen(str), &messageSize);
\r
2260 SelectObject(hdc, oldFont);
\r
2261 ReleaseDC(hwndMain, hdc);
\r
2263 /* Compute where everything goes */
\r
2264 if((first.programLogo || second.programLogo) && !tinyLayout) {
\r
2265 /* [HGM] logo: if either logo is on, reserve space for it */
\r
2266 logoHeight = 2*clockSize.cy;
\r
2267 leftLogoRect.left = OUTER_MARGIN;
\r
2268 leftLogoRect.right = leftLogoRect.left + 4*clockSize.cy;
\r
2269 leftLogoRect.top = OUTER_MARGIN;
\r
2270 leftLogoRect.bottom = OUTER_MARGIN + logoHeight;
\r
2272 rightLogoRect.right = OUTER_MARGIN + boardWidth;
\r
2273 rightLogoRect.left = rightLogoRect.right - 4*clockSize.cy;
\r
2274 rightLogoRect.top = OUTER_MARGIN;
\r
2275 rightLogoRect.bottom = OUTER_MARGIN + logoHeight;
\r
2278 whiteRect.left = leftLogoRect.right;
\r
2279 whiteRect.right = OUTER_MARGIN + boardWidth/2 - INNER_MARGIN/2;
\r
2280 whiteRect.top = OUTER_MARGIN;
\r
2281 whiteRect.bottom = whiteRect.top + logoHeight;
\r
2283 blackRect.right = rightLogoRect.left;
\r
2284 blackRect.left = whiteRect.right + INNER_MARGIN;
\r
2285 blackRect.top = whiteRect.top;
\r
2286 blackRect.bottom = whiteRect.bottom;
\r
2288 whiteRect.left = OUTER_MARGIN;
\r
2289 whiteRect.right = whiteRect.left + boardWidth/2 - INNER_MARGIN/2;
\r
2290 whiteRect.top = OUTER_MARGIN;
\r
2291 whiteRect.bottom = whiteRect.top + clockSize.cy;
\r
2293 blackRect.left = whiteRect.right + INNER_MARGIN;
\r
2294 blackRect.right = blackRect.left + boardWidth/2 - 1;
\r
2295 blackRect.top = whiteRect.top;
\r
2296 blackRect.bottom = whiteRect.bottom;
\r
2298 logoHeight = 0; // [HGM] logo: suppress logo after change to tiny layout!
\r
2301 messageRect.left = OUTER_MARGIN + MESSAGE_LINE_LEFTMARGIN;
\r
2302 if (appData.showButtonBar) {
\r
2303 messageRect.right = OUTER_MARGIN + boardWidth // [HGM] logo: expressed independent of clock placement
\r
2304 - N_BUTTONS*BUTTON_WIDTH - MESSAGE_LINE_LEFTMARGIN;
\r
2306 messageRect.right = OUTER_MARGIN + boardWidth;
\r
2308 messageRect.top = whiteRect.bottom + INNER_MARGIN;
\r
2309 messageRect.bottom = messageRect.top + messageSize.cy;
\r
2311 boardRect.left = OUTER_MARGIN;
\r
2312 boardRect.right = boardRect.left + boardWidth;
\r
2313 boardRect.top = messageRect.bottom + INNER_MARGIN;
\r
2314 boardRect.bottom = boardRect.top + boardHeight;
\r
2316 sizeInfo[boardSize].cliWidth = boardRect.right + OUTER_MARGIN;
\r
2317 sizeInfo[boardSize].cliHeight = boardRect.bottom + OUTER_MARGIN;
\r
2318 oldBoardSize = boardSize;
\r
2319 oldTinyLayout = tinyLayout;
\r
2320 winW = 2 * GetSystemMetrics(SM_CXFRAME) + boardRect.right + OUTER_MARGIN;
\r
2321 winH = 2 * GetSystemMetrics(SM_CYFRAME) + GetSystemMetrics(SM_CYMENU) +
\r
2322 GetSystemMetrics(SM_CYCAPTION) + boardRect.bottom + OUTER_MARGIN;
\r
2323 winW *= 1 + twoBoards;
\r
2324 if(suppressVisibleEffects) return; // [HGM] when called for filling sizeInfo only
\r
2325 wpMain.width = winW; // [HGM] placement: set through temporary which can used by initial sizing choice
\r
2326 wpMain.height = winH; // without disturbing window attachments
\r
2327 GetWindowRect(hwndMain, &wrect);
\r
2328 SetWindowPos(hwndMain, NULL, 0, 0, wpMain.width, wpMain.height,
\r
2329 SWP_NOCOPYBITS|SWP_NOZORDER|SWP_NOMOVE);
\r
2331 // [HGM] placement: let attached windows follow size change.
\r
2332 ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, moveHistoryDialog, &wpMoveHistory );
\r
2333 ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, evalGraphDialog, &wpEvalGraph );
\r
2334 ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, engineOutputDialog, &wpEngineOutput );
\r
2335 ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, gameListDialog, &wpGameList );
\r
2336 ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, hwndConsole, &wpConsole );
\r
2338 /* compensate if menu bar wrapped */
\r
2339 GetClientRect(hwndMain, &crect);
\r
2340 offby = boardRect.bottom + OUTER_MARGIN - crect.bottom;
\r
2341 wpMain.height += offby;
\r
2343 case WMSZ_TOPLEFT:
\r
2344 SetWindowPos(hwndMain, NULL,
\r
2345 wrect.right - wpMain.width, wrect.bottom - wpMain.height,
\r
2346 wpMain.width, wpMain.height, SWP_NOCOPYBITS|SWP_NOZORDER);
\r
2349 case WMSZ_TOPRIGHT:
\r
2351 SetWindowPos(hwndMain, NULL,
\r
2352 wrect.left, wrect.bottom - wpMain.height,
\r
2353 wpMain.width, wpMain.height, SWP_NOCOPYBITS|SWP_NOZORDER);
\r
2356 case WMSZ_BOTTOMLEFT:
\r
2358 SetWindowPos(hwndMain, NULL,
\r
2359 wrect.right - wpMain.width, wrect.top,
\r
2360 wpMain.width, wpMain.height, SWP_NOCOPYBITS|SWP_NOZORDER);
\r
2363 case WMSZ_BOTTOMRIGHT:
\r
2367 SetWindowPos(hwndMain, NULL, 0, 0, wpMain.width, wpMain.height,
\r
2368 SWP_NOCOPYBITS|SWP_NOZORDER|SWP_NOMOVE);
\r
2373 for (i = 0; i < N_BUTTONS; i++) {
\r
2374 if (buttonDesc[i].hwnd != NULL) {
\r
2375 DestroyWindow(buttonDesc[i].hwnd);
\r
2376 buttonDesc[i].hwnd = NULL;
\r
2378 if (appData.showButtonBar) {
\r
2379 buttonDesc[i].hwnd =
\r
2380 CreateWindow("BUTTON", buttonDesc[i].label,
\r
2381 WS_VISIBLE | WS_CHILD | BS_PUSHBUTTON,
\r
2382 boardRect.right - BUTTON_WIDTH*(N_BUTTONS-i),
\r
2383 messageRect.top, BUTTON_WIDTH, messageSize.cy, hwndMain,
\r
2384 (HMENU) buttonDesc[i].id,
\r
2385 (HINSTANCE) GetWindowLongPtr(hwndMain, GWLP_HINSTANCE), NULL);
\r
2387 SendMessage(buttonDesc[i].hwnd, WM_SETFONT,
\r
2388 (WPARAM)font[boardSize][MESSAGE_FONT]->hf,
\r
2389 MAKELPARAM(FALSE, 0));
\r
2391 if (buttonDesc[i].id == IDM_Pause)
\r
2392 hwndPause = buttonDesc[i].hwnd;
\r
2393 buttonDesc[i].wndproc = (WNDPROC)
\r
2394 SetWindowLongPtr(buttonDesc[i].hwnd, GWLP_WNDPROC, (LONG_PTR) ButtonProc);
\r
2397 if (gridPen != NULL) DeleteObject(gridPen);
\r
2398 if (highlightPen != NULL) DeleteObject(highlightPen);
\r
2399 if (premovePen != NULL) DeleteObject(premovePen);
\r
2400 if (lineGap != 0) {
\r
2401 logbrush.lbStyle = BS_SOLID;
\r
2402 logbrush.lbColor = RGB(0, 0, 0); /* grid pen color = black */
\r
2404 ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,
\r
2405 lineGap, &logbrush, 0, NULL);
\r
2406 logbrush.lbColor = highlightSquareColor;
\r
2408 ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,
\r
2409 lineGap, &logbrush, 0, NULL);
\r
2411 logbrush.lbColor = premoveHighlightColor;
\r
2413 ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,
\r
2414 lineGap, &logbrush, 0, NULL);
\r
2416 /* [HGM] Loop had to be split in part for vert. and hor. lines */
\r
2417 for (i = 0; i < BOARD_HEIGHT + 1; i++) {
\r
2418 gridEndpoints[i*2].x = boardRect.left + lineGap / 2;
\r
2419 gridEndpoints[i*2].y = gridEndpoints[i*2 + 1].y =
\r
2420 boardRect.top + lineGap / 2 + (i * (squareSize + lineGap));
\r
2421 gridEndpoints[i*2 + 1].x = boardRect.left + lineGap / 2 +
\r
2422 BOARD_WIDTH * (squareSize + lineGap);
\r
2423 gridVertexCounts[i*2] = gridVertexCounts[i*2 + 1] = 2;
\r
2425 for (i = 0; i < BOARD_WIDTH + 1; i++) {
\r
2426 gridEndpoints[i*2 + BOARD_HEIGHT*2 + 2].y = boardRect.top + lineGap / 2;
\r
2427 gridEndpoints[i*2 + BOARD_HEIGHT*2 + 2].x =
\r
2428 gridEndpoints[i*2 + 1 + BOARD_HEIGHT*2 + 2].x = boardRect.left +
\r
2429 lineGap / 2 + (i * (squareSize + lineGap));
\r
2430 gridEndpoints[i*2 + 1 + BOARD_HEIGHT*2 + 2].y =
\r
2431 boardRect.top + BOARD_HEIGHT * (squareSize + lineGap);
\r
2432 gridVertexCounts[i*2] = gridVertexCounts[i*2 + 1] = 2;
\r
2436 /* [HGM] Licensing requirement */
\r
2438 if(gameInfo.variant == VariantGothic) GothicPopUp( GOTHIC, VariantGothic); else
\r
2441 if(gameInfo.variant == VariantFalcon) GothicPopUp( FALCON, VariantFalcon); else
\r
2443 GothicPopUp( "", VariantNormal);
\r
2446 /* if (boardSize == oldBoardSize) return; [HGM] variant might have changed */
\r
2448 /* Load piece bitmaps for this board size */
\r
2449 for (i=0; i<=2; i++) {
\r
2450 for (piece = WhitePawn;
\r
2451 (int) piece < (int) BlackPawn;
\r
2452 piece = (ChessSquare) ((int) piece + 1)) {
\r
2453 if (pieceBitmap[i][piece] != NULL)
\r
2454 DeleteObject(pieceBitmap[i][piece]);
\r
2458 fontBitmapSquareSize = 0; /* [HGM] render: make sure pieces will be recreated, as we might need others now */
\r
2459 // Orthodox Chess pieces
\r
2460 pieceBitmap[0][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "s");
\r
2461 pieceBitmap[0][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "s");
\r
2462 pieceBitmap[0][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "s");
\r
2463 pieceBitmap[0][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "s");
\r
2464 pieceBitmap[0][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "s");
\r
2465 pieceBitmap[1][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "o");
\r
2466 pieceBitmap[1][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "o");
\r
2467 pieceBitmap[1][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "o");
\r
2468 pieceBitmap[1][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "o");
\r
2469 pieceBitmap[1][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "o");
\r
2470 pieceBitmap[2][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "w");
\r
2471 pieceBitmap[2][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "w");
\r
2472 pieceBitmap[2][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "w");
\r
2473 pieceBitmap[2][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "w");
\r
2474 pieceBitmap[2][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "w");
\r
2475 if( gameInfo.variant == VariantShogi && squareSize <= 72 && squareSize >= 33) {
\r
2476 // in Shogi, Hijack the unused Queen for Lance
\r
2477 pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "s");
\r
2478 pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "o");
\r
2479 pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "w");
\r
2481 pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "s");
\r
2482 pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "o");
\r
2483 pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "w");
\r
2486 if(squareSize <= 72 && squareSize >= 33) {
\r
2487 /* A & C are available in most sizes now */
\r
2488 if(squareSize != 49 && squareSize != 72 && squareSize != 33) { // Vortex-like
\r
2489 pieceBitmap[0][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "s");
\r
2490 pieceBitmap[1][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "o");
\r
2491 pieceBitmap[2][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "w");
\r
2492 pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "s");
\r
2493 pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "o");
\r
2494 pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "w");
\r
2495 pieceBitmap[0][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "s");
\r
2496 pieceBitmap[1][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "o");
\r
2497 pieceBitmap[2][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "w");
\r
2498 pieceBitmap[0][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "s");
\r
2499 pieceBitmap[1][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "o");
\r
2500 pieceBitmap[2][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "w");
\r
2501 } else { // Smirf-like
\r
2502 if(gameInfo.variant == VariantSChess) {
\r
2503 pieceBitmap[0][WhiteAngel] = DoLoadBitmap(hInst, "v", squareSize, "s");
\r
2504 pieceBitmap[1][WhiteAngel] = DoLoadBitmap(hInst, "v", squareSize, "o");
\r
2505 pieceBitmap[2][WhiteAngel] = DoLoadBitmap(hInst, "v", squareSize, "w");
\r
2507 pieceBitmap[0][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "s");
\r
2508 pieceBitmap[1][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "o");
\r
2509 pieceBitmap[2][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "w");
\r
2512 if(gameInfo.variant == VariantGothic) { // Vortex-like
\r
2513 pieceBitmap[0][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "s");
\r
2514 pieceBitmap[1][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "o");
\r
2515 pieceBitmap[2][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "w");
\r
2516 } else if(gameInfo.variant == VariantSChess && (squareSize == 49 || squareSize == 72)) {
\r
2517 pieceBitmap[0][WhiteMarshall] = DoLoadBitmap(hInst, "e", squareSize, "s");
\r
2518 pieceBitmap[1][WhiteMarshall] = DoLoadBitmap(hInst, "e", squareSize, "o");
\r
2519 pieceBitmap[2][WhiteMarshall] = DoLoadBitmap(hInst, "e", squareSize, "w");
\r
2520 } else { // WinBoard standard
\r
2521 pieceBitmap[0][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "s");
\r
2522 pieceBitmap[1][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "o");
\r
2523 pieceBitmap[2][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "w");
\r
2528 if(squareSize==72 || squareSize==49 || squareSize==33) { /* experiment with some home-made bitmaps */
\r
2529 pieceBitmap[0][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "s");
\r
2530 pieceBitmap[1][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "o");
\r
2531 pieceBitmap[2][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "w");
\r
2532 pieceBitmap[0][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "s");
\r
2533 pieceBitmap[1][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "o");
\r
2534 pieceBitmap[2][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "w");
\r
2535 pieceBitmap[0][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "s");
\r
2536 pieceBitmap[1][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "o");
\r
2537 pieceBitmap[2][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "w");
\r
2538 pieceBitmap[0][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "s");
\r
2539 pieceBitmap[1][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "o");
\r
2540 pieceBitmap[2][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "w");
\r
2541 pieceBitmap[0][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "s");
\r
2542 pieceBitmap[1][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "o");
\r
2543 pieceBitmap[2][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "w");
\r
2544 pieceBitmap[0][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "s");
\r
2545 pieceBitmap[1][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "o");
\r
2546 pieceBitmap[2][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "w");
\r
2547 pieceBitmap[0][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "s");
\r
2548 pieceBitmap[1][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "o");
\r
2549 pieceBitmap[2][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "w");
\r
2550 pieceBitmap[0][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "s");
\r
2551 pieceBitmap[1][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "o");
\r
2552 pieceBitmap[2][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "w");
\r
2553 pieceBitmap[0][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "s");
\r
2554 pieceBitmap[1][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "o");
\r
2555 pieceBitmap[2][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "w");
\r
2556 pieceBitmap[0][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "s");
\r
2557 pieceBitmap[1][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "o");
\r
2558 pieceBitmap[2][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "w");
\r
2560 if(gameInfo.variant == VariantShogi) { /* promoted Gold represemtations */
\r
2561 pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "wp", squareSize, "s");
\r
2562 pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "wp", squareSize, "o");
\r
2563 pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "w", squareSize, "w");
\r
2564 pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "wn", squareSize, "s");
\r
2565 pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "wn", squareSize, "o");
\r
2566 pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "w", squareSize, "w");
\r
2567 pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "ws", squareSize, "s");
\r
2568 pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "ws", squareSize, "o");
\r
2569 pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "w", squareSize, "w");
\r
2570 pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "wl", squareSize, "s");
\r
2571 pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "wl", squareSize, "o");
\r
2572 pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "w", squareSize, "w");
\r
2574 pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "s");
\r
2575 pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "o");
\r
2576 pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "w");
\r
2577 pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "s");
\r
2578 pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "o");
\r
2579 pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "w");
\r
2580 pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "s");
\r
2581 pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "o");
\r
2582 pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "w");
\r
2583 pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "s");
\r
2584 pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "o");
\r
2585 pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "w");
\r
2588 } else { /* other size, no special bitmaps available. Use smaller symbols */
\r
2589 if((int)boardSize < 2) minorSize = sizeInfo[0].squareSize;
\r
2590 else minorSize = sizeInfo[(int)boardSize - 2].squareSize;
\r
2591 pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "s");
\r
2592 pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "o");
\r
2593 pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "w");
\r
2594 pieceBitmap[0][WhiteCardinal] = DoLoadBitmap(hInst, "b", minorSize, "s");
\r
2595 pieceBitmap[1][WhiteCardinal] = DoLoadBitmap(hInst, "b", minorSize, "o");
\r
2596 pieceBitmap[2][WhiteCardinal] = DoLoadBitmap(hInst, "b", minorSize, "w");
\r
2597 pieceBitmap[0][WhiteDragon] = DoLoadBitmap(hInst, "r", minorSize, "s");
\r
2598 pieceBitmap[1][WhiteDragon] = DoLoadBitmap(hInst, "r", minorSize, "o");
\r
2599 pieceBitmap[2][WhiteDragon] = DoLoadBitmap(hInst, "r", minorSize, "w");
\r
2600 pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "s");
\r
2601 pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "o");
\r
2602 pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "w");
\r
2606 if(gameInfo.variant == VariantShogi && squareSize == 58)
\r
2607 /* special Shogi support in this size */
\r
2608 { for (i=0; i<=2; i++) { /* replace all bitmaps */
\r
2609 for (piece = WhitePawn;
\r
2610 (int) piece < (int) BlackPawn;
\r
2611 piece = (ChessSquare) ((int) piece + 1)) {
\r
2612 if (pieceBitmap[i][piece] != NULL)
\r
2613 DeleteObject(pieceBitmap[i][piece]);
\r
2616 pieceBitmap[0][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "o");
\r
2617 pieceBitmap[0][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "o");
\r
2618 pieceBitmap[0][WhiteBishop] = DoLoadBitmap(hInst, "sb", squareSize, "o");
\r
2619 pieceBitmap[0][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "o");
\r
2620 pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "o");
\r
2621 pieceBitmap[0][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "o");
\r
2622 pieceBitmap[0][WhiteFerz] = DoLoadBitmap(hInst, "sf", squareSize, "o");
\r
2623 pieceBitmap[0][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "o");
\r
2624 pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "su", squareSize, "o");
\r
2625 pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "sh", squareSize, "o");
\r
2626 pieceBitmap[0][WhiteCardinal] = DoLoadBitmap(hInst, "sa", squareSize, "o");
\r
2627 pieceBitmap[0][WhiteDragon] = DoLoadBitmap(hInst, "sc", squareSize, "o");
\r
2628 pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "sg", squareSize, "o");
\r
2629 pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "ss", squareSize, "o");
\r
2630 pieceBitmap[1][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "o");
\r
2631 pieceBitmap[1][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "o");
\r
2632 pieceBitmap[1][WhiteBishop] = DoLoadBitmap(hInst, "sb", squareSize, "o");
\r
2633 pieceBitmap[1][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "o");
\r
2634 pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "o");
\r
2635 pieceBitmap[1][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "o");
\r
2636 pieceBitmap[1][WhiteFerz] = DoLoadBitmap(hInst, "sf", squareSize, "o");
\r
2637 pieceBitmap[1][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "o");
\r
2638 pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "su", squareSize, "o");
\r
2639 pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "sh", squareSize, "o");
\r
2640 pieceBitmap[1][WhiteCardinal] = DoLoadBitmap(hInst, "sa", squareSize, "o");
\r
2641 pieceBitmap[1][WhiteDragon] = DoLoadBitmap(hInst, "sc", squareSize, "o");
\r
2642 pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "sg", squareSize, "o");
\r
2643 pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "ss", squareSize, "o");
\r
2644 pieceBitmap[2][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "w");
\r
2645 pieceBitmap[2][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "w");
\r
2646 pieceBitmap[2][WhiteBishop] = DoLoadBitmap(hInst, "sr", squareSize, "w");
\r
2647 pieceBitmap[2][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "w");
\r
2648 pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "w");
\r
2649 pieceBitmap[2][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "w");
\r
2650 pieceBitmap[2][WhiteFerz] = DoLoadBitmap(hInst, "sw", squareSize, "w");
\r
2651 pieceBitmap[2][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "w");
\r
2652 pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "sp", squareSize, "w");
\r
2653 pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "sn", squareSize, "w");
\r
2654 pieceBitmap[2][WhiteCardinal] = DoLoadBitmap(hInst, "sr", squareSize, "w");
\r
2655 pieceBitmap[2][WhiteDragon] = DoLoadBitmap(hInst, "sr", squareSize, "w");
\r
2656 pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "sl", squareSize, "w");
\r
2657 pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "sw", squareSize, "w");
\r
2663 PieceBitmap(ChessSquare p, int kind)
\r
2665 if ((int) p >= (int) BlackPawn)
\r
2666 p = (ChessSquare) ((int) p - (int) BlackPawn + (int) WhitePawn);
\r
2668 return pieceBitmap[kind][(int) p];
\r
2671 /***************************************************************/
\r
2673 #define MIN(a,b) (((a) < (b)) ? (a) : (b))
\r
2674 #define MAX(a,b) (((a) > (b)) ? (a) : (b))
\r
2676 #define MIN3(a,b,c) (((a) < (b) && (a) < (c)) ? (a) : (((b) < (a) && (b) < (c)) ? (b) : (c)))
\r
2677 #define MAX3(a,b,c) (((a) > (b) && (a) > (c)) ? (a) : (((b) > (a) && (b) > (c)) ? (b) : (c)))
\r
2681 SquareToPos(int row, int column, int * x, int * y)
\r
2684 *x = boardRect.left + lineGap + ((BOARD_WIDTH-1)-column) * (squareSize + lineGap);
\r
2685 *y = boardRect.top + lineGap + row * (squareSize + lineGap);
\r
2687 *x = boardRect.left + lineGap + column * (squareSize + lineGap);
\r
2688 *y = boardRect.top + lineGap + ((BOARD_HEIGHT-1)-row) * (squareSize + lineGap);
\r
2693 DrawCoordsOnDC(HDC hdc)
\r
2695 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
2696 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
2697 char str[2] = { NULLCHAR, NULLCHAR };
\r
2698 int oldMode, oldAlign, x, y, start, i;
\r
2702 if (!appData.showCoords)
\r
2705 start = flipView ? 1-(ONE!='1') : 23+(ONE!='1')-BOARD_HEIGHT;
\r
2707 oldBrush = SelectObject(hdc, GetStockObject(BLACK_BRUSH));
\r
2708 oldMode = SetBkMode(hdc, (appData.monoMode ? OPAQUE : TRANSPARENT));
\r
2709 oldAlign = GetTextAlign(hdc);
\r
2710 oldFont = SelectObject(hdc, font[boardSize][COORD_FONT]->hf);
\r
2712 y = boardRect.top + lineGap;
\r
2713 x = boardRect.left + lineGap + gameInfo.holdingsWidth*(squareSize + lineGap);
\r
2715 SetTextAlign(hdc, TA_LEFT|TA_TOP);
\r
2716 for (i = 0; i < BOARD_HEIGHT; i++) {
\r
2717 str[0] = files[start + i];
\r
2718 ExtTextOut(hdc, x + 2, y + 1, 0, NULL, str, 1, NULL);
\r
2719 y += squareSize + lineGap;
\r
2722 start = flipView ? 12-(BOARD_RGHT-BOARD_LEFT) : 12;
\r
2724 SetTextAlign(hdc, TA_RIGHT|TA_BOTTOM);
\r
2725 for (i = 0; i < BOARD_RGHT - BOARD_LEFT; i++) {
\r
2726 str[0] = ranks[start + i];
\r
2727 ExtTextOut(hdc, x + squareSize - 2, y - 1, 0, NULL, str, 1, NULL);
\r
2728 x += squareSize + lineGap;
\r
2731 SelectObject(hdc, oldBrush);
\r
2732 SetBkMode(hdc, oldMode);
\r
2733 SetTextAlign(hdc, oldAlign);
\r
2734 SelectObject(hdc, oldFont);
\r
2738 DrawGridOnDC(HDC hdc)
\r
2742 if (lineGap != 0) {
\r
2743 oldPen = SelectObject(hdc, gridPen);
\r
2744 PolyPolyline(hdc, gridEndpoints, gridVertexCounts, BOARD_WIDTH+BOARD_HEIGHT + 2);
\r
2745 SelectObject(hdc, oldPen);
\r
2749 #define HIGHLIGHT_PEN 0
\r
2750 #define PREMOVE_PEN 1
\r
2753 DrawHighlightOnDC(HDC hdc, BOOLEAN on, int x, int y, int pen)
\r
2756 HPEN oldPen, hPen;
\r
2757 if (lineGap == 0) return;
\r
2759 x1 = boardRect.left +
\r
2760 lineGap/2 + ((BOARD_WIDTH-1)-x) * (squareSize + lineGap);
\r
2761 y1 = boardRect.top +
\r
2762 lineGap/2 + y * (squareSize + lineGap);
\r
2764 x1 = boardRect.left +
\r
2765 lineGap/2 + x * (squareSize + lineGap);
\r
2766 y1 = boardRect.top +
\r
2767 lineGap/2 + ((BOARD_HEIGHT-1)-y) * (squareSize + lineGap);
\r
2769 hPen = pen ? premovePen : highlightPen;
\r
2770 oldPen = SelectObject(hdc, on ? hPen : gridPen);
\r
2771 MoveToEx(hdc, x1, y1, NULL);
\r
2772 LineTo(hdc, x1 + squareSize + lineGap, y1);
\r
2773 LineTo(hdc, x1 + squareSize + lineGap, y1 + squareSize + lineGap);
\r
2774 LineTo(hdc, x1, y1 + squareSize + lineGap);
\r
2775 LineTo(hdc, x1, y1);
\r
2776 SelectObject(hdc, oldPen);
\r
2780 DrawHighlightsOnDC(HDC hdc, HighlightInfo *h, int pen)
\r
2783 for (i=0; i<2; i++) {
\r
2784 if (h->sq[i].x >= 0 && h->sq[i].y >= 0)
\r
2785 DrawHighlightOnDC(hdc, TRUE,
\r
2786 h->sq[i].x, h->sq[i].y,
\r
2791 /* Note: sqcolor is used only in monoMode */
\r
2792 /* Note that this code is largely duplicated in woptions.c,
\r
2793 function DrawSampleSquare, so that needs to be updated too */
\r
2795 DrawPieceOnDC(HDC hdc, ChessSquare piece, int color, int sqcolor, int x, int y, HDC tmphdc)
\r
2797 HBITMAP oldBitmap;
\r
2801 if (appData.blindfold) return;
\r
2803 /* [AS] Use font-based pieces if needed */
\r
2804 if( fontBitmapSquareSize >= 0 && (squareSize > 32 || gameInfo.variant >= VariantShogi)) {
\r
2805 /* Create piece bitmaps, or do nothing if piece set is up to date */
\r
2806 CreatePiecesFromFont();
\r
2808 if( fontBitmapSquareSize == squareSize ) {
\r
2809 int index = TranslatePieceToFontPiece(piece);
\r
2811 SelectObject( tmphdc, hPieceMask[ index ] );
\r
2813 if(appData.upsideDown ? color==flipView : (flipView && gameInfo.variant == VariantShogi))
\r
2814 StretchBlt(hdc, x+squareSize, y+squareSize, -squareSize, -squareSize, tmphdc, 0, 0, squareSize, squareSize, SRCAND);
\r
2818 squareSize, squareSize,
\r
2823 SelectObject( tmphdc, hPieceFace[ index ] );
\r
2825 if(appData.upsideDown ? color==flipView : (flipView && gameInfo.variant == VariantShogi))
\r
2826 StretchBlt(hdc, x+squareSize, y+squareSize, -squareSize, -squareSize, tmphdc, 0, 0, squareSize, squareSize, SRCPAINT);
\r
2830 squareSize, squareSize,
\r
2839 if (appData.monoMode) {
\r
2840 SelectObject(tmphdc, PieceBitmap(piece,
\r
2841 color == sqcolor ? OUTLINE_PIECE : SOLID_PIECE));
\r
2842 BitBlt(hdc, x, y, squareSize, squareSize, tmphdc, 0, 0,
\r
2843 sqcolor ? SRCCOPY : NOTSRCCOPY);
\r
2845 tmpSize = squareSize;
\r
2847 ((piece >= (int)WhiteNightrider && piece <= WhiteGrasshopper) ||
\r
2848 (piece >= (int)BlackNightrider && piece <= BlackGrasshopper)) ) {
\r
2849 /* [HGM] no bitmap available for promoted pieces in Crazyhouse */
\r
2850 /* Bitmaps of smaller size are substituted, but we have to align them */
\r
2851 x += (squareSize - minorSize)>>1;
\r
2852 y += squareSize - minorSize - 2;
\r
2853 tmpSize = minorSize;
\r
2855 if (color || appData.allWhite ) {
\r
2856 oldBitmap = SelectObject(tmphdc, PieceBitmap(piece, WHITE_PIECE));
\r
2858 oldBrush = SelectObject(hdc, whitePieceBrush);
\r
2859 else oldBrush = SelectObject(hdc, blackPieceBrush);
\r
2860 if(appData.upsideDown && color==flipView)
\r
2861 StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, 0x00B8074A);
\r
2863 BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, 0x00B8074A);
\r
2864 /* Use black for outline of white pieces */
\r
2865 SelectObject(tmphdc, PieceBitmap(piece, OUTLINE_PIECE));
\r
2866 if(appData.upsideDown && color==flipView)
\r
2867 StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, SRCAND);
\r
2869 BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, SRCAND);
\r
2871 /* Use square color for details of black pieces */
\r
2872 oldBitmap = SelectObject(tmphdc, PieceBitmap(piece, SOLID_PIECE));
\r
2873 oldBrush = SelectObject(hdc, blackPieceBrush);
\r
2874 if(appData.upsideDown && !flipView)
\r
2875 StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, 0x00B8074A);
\r
2877 BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, 0x00B8074A);
\r
2879 SelectObject(hdc, oldBrush);
\r
2880 SelectObject(tmphdc, oldBitmap);
\r
2884 /* [AS] Compute a drawing mode for a square, based on specified settings (see DrawTile) */
\r
2885 int GetBackTextureMode( int algo )
\r
2887 int result = BACK_TEXTURE_MODE_DISABLED;
\r
2891 case BACK_TEXTURE_MODE_PLAIN:
\r
2892 result = 1; /* Always use identity map */
\r
2894 case BACK_TEXTURE_MODE_FULL_RANDOM:
\r
2895 result = 1 + (myrandom() % 3); /* Pick a transformation at random */
\r
2903 [AS] Compute and save texture drawing info, otherwise we may not be able
\r
2904 to handle redraws cleanly (as random numbers would always be different).
\r
2906 VOID RebuildTextureSquareInfo()
\r
2916 ZeroMemory( &backTextureSquareInfo, sizeof(backTextureSquareInfo) );
\r
2918 if( liteBackTexture != NULL ) {
\r
2919 if( GetObject( liteBackTexture, sizeof(bi), &bi ) > 0 ) {
\r
2920 lite_w = bi.bmWidth;
\r
2921 lite_h = bi.bmHeight;
\r
2925 if( darkBackTexture != NULL ) {
\r
2926 if( GetObject( darkBackTexture, sizeof(bi), &bi ) > 0 ) {
\r
2927 dark_w = bi.bmWidth;
\r
2928 dark_h = bi.bmHeight;
\r
2932 for( row=0; row<BOARD_HEIGHT; row++ ) {
\r
2933 for( col=0; col<BOARD_WIDTH; col++ ) {
\r
2934 if( (col + row) & 1 ) {
\r
2936 if( lite_w >= squareSize && lite_h >= squareSize ) {
\r
2937 if( lite_w >= squareSize*BOARD_WIDTH )
\r
2938 backTextureSquareInfo[row][col].x = (2*col+1)*lite_w/(2*BOARD_WIDTH) - squareSize/2; /* [HGM] cut out of center of virtual square */
\r
2940 backTextureSquareInfo[row][col].x = col * (lite_w - squareSize) / (BOARD_WIDTH-1); /* [HGM] divide by size-1 in stead of size! */
\r
2941 if( lite_h >= squareSize*BOARD_HEIGHT )
\r
2942 backTextureSquareInfo[row][col].y = (2*(BOARD_HEIGHT-row)-1)*lite_h/(2*BOARD_HEIGHT) - squareSize/2;
\r
2944 backTextureSquareInfo[row][col].y = (BOARD_HEIGHT-1-row) * (lite_h - squareSize) / (BOARD_HEIGHT-1);
\r
2945 backTextureSquareInfo[row][col].mode = GetBackTextureMode(liteBackTextureMode);
\r
2950 if( dark_w >= squareSize && dark_h >= squareSize ) {
\r
2951 if( dark_w >= squareSize*BOARD_WIDTH )
\r
2952 backTextureSquareInfo[row][col].x = (2*col+1) * dark_w / (2*BOARD_WIDTH) - squareSize/2;
\r
2954 backTextureSquareInfo[row][col].x = col * (dark_w - squareSize) / (BOARD_WIDTH-1);
\r
2955 if( dark_h >= squareSize*BOARD_HEIGHT )
\r
2956 backTextureSquareInfo[row][col].y = (2*(BOARD_HEIGHT-row)-1) * dark_h / (2*BOARD_HEIGHT) - squareSize/2;
\r
2958 backTextureSquareInfo[row][col].y = (BOARD_HEIGHT-1-row) * (dark_h - squareSize) / (BOARD_HEIGHT-1);
\r
2959 backTextureSquareInfo[row][col].mode = GetBackTextureMode(darkBackTextureMode);
\r
2966 /* [AS] Arrow highlighting support */
\r
2968 static double A_WIDTH = 5; /* Width of arrow body */
\r
2970 #define A_HEIGHT_FACTOR 6 /* Length of arrow "point", relative to body width */
\r
2971 #define A_WIDTH_FACTOR 3 /* Width of arrow "point", relative to body width */
\r
2973 static double Sqr( double x )
\r
2978 static int Round( double x )
\r
2980 return (int) (x + 0.5);
\r
2983 /* Draw an arrow between two points using current settings */
\r
2984 VOID DrawArrowBetweenPoints( HDC hdc, int s_x, int s_y, int d_x, int d_y )
\r
2987 double dx, dy, j, k, x, y;
\r
2989 if( d_x == s_x ) {
\r
2990 int h = (d_y > s_y) ? +A_WIDTH*A_HEIGHT_FACTOR : -A_WIDTH*A_HEIGHT_FACTOR;
\r
2992 arrow[0].x = s_x + A_WIDTH + 0.5;
\r
2995 arrow[1].x = s_x + A_WIDTH + 0.5;
\r
2996 arrow[1].y = d_y - h;
\r
2998 arrow[2].x = arrow[1].x + A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;
\r
2999 arrow[2].y = d_y - h;
\r
3004 arrow[5].x = arrow[1].x - 2*A_WIDTH + 0.5;
\r
3005 arrow[5].y = d_y - h;
\r
3007 arrow[4].x = arrow[5].x - A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;
\r
3008 arrow[4].y = d_y - h;
\r
3010 arrow[6].x = arrow[1].x - 2*A_WIDTH + 0.5;
\r
3013 else if( d_y == s_y ) {
\r
3014 int w = (d_x > s_x) ? +A_WIDTH*A_HEIGHT_FACTOR : -A_WIDTH*A_HEIGHT_FACTOR;
\r
3017 arrow[0].y = s_y + A_WIDTH + 0.5;
\r
3019 arrow[1].x = d_x - w;
\r
3020 arrow[1].y = s_y + A_WIDTH + 0.5;
\r
3022 arrow[2].x = d_x - w;
\r
3023 arrow[2].y = arrow[1].y + A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;
\r
3028 arrow[5].x = d_x - w;
\r
3029 arrow[5].y = arrow[1].y - 2*A_WIDTH + 0.5;
\r
3031 arrow[4].x = d_x - w;
\r
3032 arrow[4].y = arrow[5].y - A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;
\r
3035 arrow[6].y = arrow[1].y - 2*A_WIDTH + 0.5;
\r
3038 /* [AS] Needed a lot of paper for this! :-) */
\r
3039 dy = (double) (d_y - s_y) / (double) (d_x - s_x);
\r
3040 dx = (double) (s_x - d_x) / (double) (s_y - d_y);
\r
3042 j = sqrt( Sqr(A_WIDTH) / (1.0 + Sqr(dx)) );
\r
3044 k = sqrt( Sqr(A_WIDTH*A_HEIGHT_FACTOR) / (1.0 + Sqr(dy)) );
\r
3049 arrow[0].x = Round(x - j);
\r
3050 arrow[0].y = Round(y + j*dx);
\r
3052 arrow[1].x = Round(arrow[0].x + 2*j); // [HGM] prevent width to be affected by rounding twice
\r
3053 arrow[1].y = Round(arrow[0].y - 2*j*dx);
\r
3056 x = (double) d_x - k;
\r
3057 y = (double) d_y - k*dy;
\r
3060 x = (double) d_x + k;
\r
3061 y = (double) d_y + k*dy;
\r
3064 x = Round(x); y = Round(y); // [HGM] make sure width of shaft is rounded the same way on both ends
\r
3066 arrow[6].x = Round(x - j);
\r
3067 arrow[6].y = Round(y + j*dx);
\r
3069 arrow[2].x = Round(arrow[6].x + 2*j);
\r
3070 arrow[2].y = Round(arrow[6].y - 2*j*dx);
\r
3072 arrow[3].x = Round(arrow[2].x + j*(A_WIDTH_FACTOR-1));
\r
3073 arrow[3].y = Round(arrow[2].y - j*(A_WIDTH_FACTOR-1)*dx);
\r
3078 arrow[5].x = Round(arrow[6].x - j*(A_WIDTH_FACTOR-1));
\r
3079 arrow[5].y = Round(arrow[6].y + j*(A_WIDTH_FACTOR-1)*dx);
\r
3082 Polygon( hdc, arrow, 7 );
\r
3085 /* [AS] Draw an arrow between two squares */
\r
3086 VOID DrawArrowBetweenSquares( HDC hdc, int s_col, int s_row, int d_col, int d_row )
\r
3088 int s_x, s_y, d_x, d_y;
\r
3095 if( s_col == d_col && s_row == d_row ) {
\r
3099 /* Get source and destination points */
\r
3100 SquareToPos( s_row, s_col, &s_x, &s_y);
\r
3101 SquareToPos( d_row, d_col, &d_x, &d_y);
\r
3104 d_y += squareSize / 2 - squareSize / 4; // [HGM] round towards same centers on all sides!
\r
3106 else if( d_y < s_y ) {
\r
3107 d_y += squareSize / 2 + squareSize / 4;
\r
3110 d_y += squareSize / 2;
\r
3114 d_x += squareSize / 2 - squareSize / 4;
\r
3116 else if( d_x < s_x ) {
\r
3117 d_x += squareSize / 2 + squareSize / 4;
\r
3120 d_x += squareSize / 2;
\r
3123 s_x += squareSize / 2;
\r
3124 s_y += squareSize / 2;
\r
3126 /* Adjust width */
\r
3127 A_WIDTH = squareSize / 14.; //[HGM] make float
\r
3130 stLB.lbStyle = BS_SOLID;
\r
3131 stLB.lbColor = appData.highlightArrowColor;
\r
3134 hpen = CreatePen( PS_SOLID, 2, RGB(0x00,0x00,0x00) );
\r
3135 holdpen = SelectObject( hdc, hpen );
\r
3136 hbrush = CreateBrushIndirect( &stLB );
\r
3137 holdbrush = SelectObject( hdc, hbrush );
\r
3139 DrawArrowBetweenPoints( hdc, s_x, s_y, d_x, d_y );
\r
3141 SelectObject( hdc, holdpen );
\r
3142 SelectObject( hdc, holdbrush );
\r
3143 DeleteObject( hpen );
\r
3144 DeleteObject( hbrush );
\r
3147 BOOL HasHighlightInfo()
\r
3149 BOOL result = FALSE;
\r
3151 if( highlightInfo.sq[0].x >= 0 && highlightInfo.sq[0].y >= 0 &&
\r
3152 highlightInfo.sq[1].x >= 0 && highlightInfo.sq[1].y >= 0 )
\r
3160 BOOL IsDrawArrowEnabled()
\r
3162 BOOL result = FALSE;
\r
3164 if( appData.highlightMoveWithArrow && squareSize >= 32 ) {
\r
3171 VOID DrawArrowHighlight( HDC hdc )
\r
3173 if( IsDrawArrowEnabled() && HasHighlightInfo() ) {
\r
3174 DrawArrowBetweenSquares( hdc,
\r
3175 highlightInfo.sq[0].x, highlightInfo.sq[0].y,
\r
3176 highlightInfo.sq[1].x, highlightInfo.sq[1].y );
\r
3180 HRGN GetArrowHighlightClipRegion( HDC hdc )
\r
3182 HRGN result = NULL;
\r
3184 if( HasHighlightInfo() ) {
\r
3185 int x1, y1, x2, y2;
\r
3186 int sx, sy, dx, dy;
\r
3188 SquareToPos(highlightInfo.sq[0].y, highlightInfo.sq[0].x, &x1, &y1 );
\r
3189 SquareToPos(highlightInfo.sq[1].y, highlightInfo.sq[1].x, &x2, &y2 );
\r
3191 sx = MIN( x1, x2 );
\r
3192 sy = MIN( y1, y2 );
\r
3193 dx = MAX( x1, x2 ) + squareSize;
\r
3194 dy = MAX( y1, y2 ) + squareSize;
\r
3196 result = CreateRectRgn( sx, sy, dx, dy );
\r
3203 Warning: this function modifies the behavior of several other functions.
\r
3205 Basically, Winboard is optimized to avoid drawing the whole board if not strictly
\r
3206 needed. Unfortunately, the decision whether or not to perform a full or partial
\r
3207 repaint is scattered all over the place, which is not good for features such as
\r
3208 "arrow highlighting" that require a full repaint of the board.
\r
3210 So, I've tried to patch the code where I thought it made sense (e.g. after or during
\r
3211 user interaction, when speed is not so important) but especially to avoid errors
\r
3212 in the displayed graphics.
\r
3214 In such patched places, I always try refer to this function so there is a single
\r
3215 place to maintain knowledge.
\r
3217 To restore the original behavior, just return FALSE unconditionally.
\r
3219 BOOL IsFullRepaintPreferrable()
\r
3221 BOOL result = FALSE;
\r
3223 if( (appData.highlightLastMove || appData.highlightDragging) && IsDrawArrowEnabled() ) {
\r
3224 /* Arrow may appear on the board */
\r
3232 This function is called by DrawPosition to know whether a full repaint must
\r
3235 Only DrawPosition may directly call this function, which makes use of
\r
3236 some state information. Other function should call DrawPosition specifying
\r
3237 the repaint flag, and can use IsFullRepaintPreferrable if needed.
\r
3239 BOOL DrawPositionNeedsFullRepaint()
\r
3241 BOOL result = FALSE;
\r
3244 Probably a slightly better policy would be to trigger a full repaint
\r
3245 when animInfo.piece changes state (i.e. empty -> non-empty and viceversa),
\r
3246 but animation is fast enough that it's difficult to notice.
\r
3248 if( animInfo.piece == EmptySquare ) {
\r
3249 if( (appData.highlightLastMove || appData.highlightDragging) && IsDrawArrowEnabled() /*&& HasHighlightInfo()*/ ) {
\r
3258 DrawBoardOnDC(HDC hdc, Board board, HDC tmphdc)
\r
3260 int row, column, x, y, square_color, piece_color;
\r
3261 ChessSquare piece;
\r
3263 HDC texture_hdc = NULL;
\r
3265 /* [AS] Initialize background textures if needed */
\r
3266 if( liteBackTexture != NULL || darkBackTexture != NULL ) {
\r
3267 static int backTextureBoardSize; /* [HGM] boardsize: also new texture if board format changed */
\r
3268 if( backTextureSquareSize != squareSize
\r
3269 || backTextureBoardSize != BOARD_WIDTH+BOARD_FILES*BOARD_HEIGHT) {
\r
3270 backTextureBoardSize = BOARD_WIDTH+BOARD_FILES*BOARD_HEIGHT;
\r
3271 backTextureSquareSize = squareSize;
\r
3272 RebuildTextureSquareInfo();
\r
3275 texture_hdc = CreateCompatibleDC( hdc );
\r
3278 for (row = 0; row < BOARD_HEIGHT; row++) {
\r
3279 for (column = 0; column < BOARD_WIDTH; column++) {
\r
3281 SquareToPos(row, column, &x, &y);
\r
3283 piece = board[row][column];
\r
3285 square_color = ((column + row) % 2) == 1;
\r
3286 if( gameInfo.variant == VariantXiangqi ) {
\r
3287 square_color = !InPalace(row, column);
\r
3288 if(BOARD_HEIGHT&1) { if(row==BOARD_HEIGHT/2) square_color ^= 1; }
\r
3289 else if(row < BOARD_HEIGHT/2) square_color ^= 1;
\r
3291 piece_color = (int) piece < (int) BlackPawn;
\r
3294 /* [HGM] holdings file: light square or black */
\r
3295 if(column == BOARD_LEFT-2) {
\r
3296 if( row > BOARD_HEIGHT - gameInfo.holdingsSize - 1 )
\r
3299 DisplayHoldingsCount(hdc, x, y, 0, 0); /* black out */
\r
3303 if(column == BOARD_RGHT + 1 ) {
\r
3304 if( row < gameInfo.holdingsSize )
\r
3307 DisplayHoldingsCount(hdc, x, y, 0, 0);
\r
3311 if(column == BOARD_LEFT-1 ) /* left align */
\r
3312 DisplayHoldingsCount(hdc, x, y, flipView, (int) board[row][column]);
\r
3313 else if( column == BOARD_RGHT) /* right align */
\r
3314 DisplayHoldingsCount(hdc, x, y, !flipView, (int) board[row][column]);
\r
3316 if (appData.monoMode) {
\r
3317 if (piece == EmptySquare) {
\r
3318 BitBlt(hdc, x, y, squareSize, squareSize, 0, 0, 0,
\r
3319 square_color ? WHITENESS : BLACKNESS);
\r
3321 DrawPieceOnDC(hdc, piece, piece_color, square_color, x, y, tmphdc);
\r
3324 else if( backTextureSquareInfo[row][column].mode > 0 ) {
\r
3325 /* [AS] Draw the square using a texture bitmap */
\r
3326 HBITMAP hbm = SelectObject( texture_hdc, square_color ? liteBackTexture : darkBackTexture );
\r
3327 int r = row, c = column; // [HGM] do not flip board in flipView
\r
3328 if(flipView) { r = BOARD_HEIGHT-1 - r; c = BOARD_WIDTH-1 - c; }
\r
3331 squareSize, squareSize,
\r
3334 backTextureSquareInfo[r][c].mode,
\r
3335 backTextureSquareInfo[r][c].x,
\r
3336 backTextureSquareInfo[r][c].y );
\r
3338 SelectObject( texture_hdc, hbm );
\r
3340 if (piece != EmptySquare) {
\r
3341 DrawPieceOnDC(hdc, piece, piece_color, -1, x, y, tmphdc);
\r
3345 HBRUSH brush = square_color ? lightSquareBrush : darkSquareBrush;
\r
3347 oldBrush = SelectObject(hdc, brush );
\r
3348 BitBlt(hdc, x, y, squareSize, squareSize, 0, 0, 0, PATCOPY);
\r
3349 SelectObject(hdc, oldBrush);
\r
3350 if (piece != EmptySquare)
\r
3351 DrawPieceOnDC(hdc, piece, piece_color, -1, x, y, tmphdc);
\r
3356 if( texture_hdc != NULL ) {
\r
3357 DeleteDC( texture_hdc );
\r
3361 int saveDiagFlag = 0; FILE *diagFile; // [HGM] diag
\r
3362 void fputDW(FILE *f, int x)
\r
3364 fputc(x & 255, f);
\r
3365 fputc(x>>8 & 255, f);
\r
3366 fputc(x>>16 & 255, f);
\r
3367 fputc(x>>24 & 255, f);
\r
3370 #define MAX_CLIPS 200 /* more than enough */
\r
3373 DrawLogoOnDC(HDC hdc, RECT logoRect, HBITMAP logo)
\r
3375 // HBITMAP bufferBitmap;
\r
3380 int w = 100, h = 50;
\r
3382 if(logo == NULL) return;
\r
3383 // GetClientRect(hwndMain, &Rect);
\r
3384 // bufferBitmap = CreateCompatibleBitmap(hdc, Rect.right-Rect.left+1,
\r
3385 // Rect.bottom-Rect.top+1);
\r
3386 tmphdc = CreateCompatibleDC(hdc);
\r
3387 hbm = SelectObject(tmphdc, logo);
\r
3388 if( GetObject( logo, sizeof(bi), &bi ) > 0 ) {
\r
3392 StretchBlt(hdc, logoRect.left, logoRect.top, logoRect.right - logoRect.left,
\r
3393 logoRect.bottom - logoRect.top, tmphdc, 0, 0, w, h, SRCCOPY);
\r
3394 SelectObject(tmphdc, hbm);
\r
3402 HDC hdc = GetDC(hwndMain);
\r
3403 HBITMAP whiteLogo = (HBITMAP) first.programLogo, blackLogo = (HBITMAP) second.programLogo;
\r
3404 if(appData.autoLogo) {
\r
3406 switch(gameMode) { // pick logos based on game mode
\r
3407 case IcsObserving:
\r
3408 whiteLogo = second.programLogo; // ICS logo
\r
3409 blackLogo = second.programLogo;
\r
3412 case IcsPlayingWhite:
\r
3413 if(!appData.zippyPlay) whiteLogo = userLogo;
\r
3414 blackLogo = second.programLogo; // ICS logo
\r
3416 case IcsPlayingBlack:
\r
3417 whiteLogo = second.programLogo; // ICS logo
\r
3418 blackLogo = appData.zippyPlay ? first.programLogo : userLogo;
\r
3420 case TwoMachinesPlay:
\r
3421 if(first.twoMachinesColor[0] == 'b') {
\r
3422 whiteLogo = second.programLogo;
\r
3423 blackLogo = first.programLogo;
\r
3426 case MachinePlaysWhite:
\r
3427 blackLogo = userLogo;
\r
3429 case MachinePlaysBlack:
\r
3430 whiteLogo = userLogo;
\r
3431 blackLogo = first.programLogo;
\r
3434 DrawLogoOnDC(hdc, leftLogoRect, flipClock ? blackLogo : whiteLogo);
\r
3435 DrawLogoOnDC(hdc, rightLogoRect, flipClock ? whiteLogo : blackLogo);
\r
3436 ReleaseDC(hwndMain, hdc);
\r
3440 static HDC hdcSeek;
\r
3442 // [HGM] seekgraph
\r
3443 void DrawSeekAxis( int x, int y, int xTo, int yTo )
\r
3446 HPEN hp = SelectObject( hdcSeek, gridPen );
\r
3447 MoveToEx( hdcSeek, boardRect.left+x, boardRect.top+y, &stPt );
\r
3448 LineTo( hdcSeek, boardRect.left+xTo, boardRect.top+yTo );
\r
3449 SelectObject( hdcSeek, hp );
\r
3452 // front-end wrapper for drawing functions to do rectangles
\r
3453 void DrawSeekBackground( int left, int top, int right, int bottom )
\r
3458 if (hdcSeek == NULL) {
\r
3459 hdcSeek = GetDC(hwndMain);
\r
3460 if (!appData.monoMode) {
\r
3461 SelectPalette(hdcSeek, hPal, FALSE);
\r
3462 RealizePalette(hdcSeek);
\r
3465 hp = SelectObject( hdcSeek, gridPen );
\r
3466 rc.top = boardRect.top+top; rc.left = boardRect.left+left;
\r
3467 rc.bottom = boardRect.top+bottom; rc.right = boardRect.left+right;
\r
3468 FillRect( hdcSeek, &rc, lightSquareBrush );
\r
3469 SelectObject( hdcSeek, hp );
\r
3472 // front-end wrapper for putting text in graph
\r
3473 void DrawSeekText(char *buf, int x, int y)
\r
3476 SetBkMode( hdcSeek, TRANSPARENT );
\r
3477 GetTextExtentPoint32( hdcSeek, buf, strlen(buf), &stSize );
\r
3478 TextOut( hdcSeek, boardRect.left+x-3, boardRect.top+y-stSize.cy/2, buf, strlen(buf) );
\r
3481 void DrawSeekDot(int x, int y, int color)
\r
3483 int square = color & 0x80;
\r
3484 HBRUSH oldBrush = SelectObject(hdcSeek,
\r
3485 color == 0 ? markerBrush : color == 1 ? darkSquareBrush : explodeBrush);
\r
3488 Rectangle(hdcSeek, boardRect.left+x - squareSize/9, boardRect.top+y - squareSize/9,
\r
3489 boardRect.left+x + squareSize/9, boardRect.top+y + squareSize/9);
\r
3491 Ellipse(hdcSeek, boardRect.left+x - squareSize/8, boardRect.top+y - squareSize/8,
\r
3492 boardRect.left+x + squareSize/8, boardRect.top+y + squareSize/8);
\r
3493 SelectObject(hdcSeek, oldBrush);
\r
3497 HDCDrawPosition(HDC hdc, BOOLEAN repaint, Board board)
\r
3499 static Board lastReq[2], lastDrawn[2];
\r
3500 static HighlightInfo lastDrawnHighlight, lastDrawnPremove;
\r
3501 static int lastDrawnFlipView = 0;
\r
3502 static int lastReqValid[2] = {0, 0}, lastDrawnValid[2] = {0, 0};
\r
3503 int releaseDC, x, y, x2, y2, row, column, num_clips = 0, i;
\r
3506 HBITMAP bufferBitmap;
\r
3507 HBITMAP oldBitmap;
\r
3509 HRGN clips[MAX_CLIPS];
\r
3510 ChessSquare dragged_piece = EmptySquare;
\r
3511 int nr = twoBoards*partnerUp;
\r
3513 /* I'm undecided on this - this function figures out whether a full
\r
3514 * repaint is necessary on its own, so there's no real reason to have the
\r
3515 * caller tell it that. I think this can safely be set to FALSE - but
\r
3516 * if we trust the callers not to request full repaints unnessesarily, then
\r
3517 * we could skip some clipping work. In other words, only request a full
\r
3518 * redraw when the majority of pieces have changed positions (ie. flip,
\r
3519 * gamestart and similar) --Hawk
\r
3521 Boolean fullrepaint = repaint;
\r
3523 if(DrawSeekGraph()) return; // [HG} seekgraph: suppress printing board if seek graph up
\r
3525 if( DrawPositionNeedsFullRepaint() ) {
\r
3526 fullrepaint = TRUE;
\r
3529 if (board == NULL) {
\r
3530 if (!lastReqValid[nr]) {
\r
3533 board = lastReq[nr];
\r
3535 CopyBoard(lastReq[nr], board);
\r
3536 lastReqValid[nr] = 1;
\r
3539 if (doingSizing) {
\r
3543 if (IsIconic(hwndMain)) {
\r
3547 if (hdc == NULL) {
\r
3548 hdc = GetDC(hwndMain);
\r
3549 if (!appData.monoMode) {
\r
3550 SelectPalette(hdc, hPal, FALSE);
\r
3551 RealizePalette(hdc);
\r
3555 releaseDC = FALSE;
\r
3558 /* Create some work-DCs */
\r
3559 hdcmem = CreateCompatibleDC(hdc);
\r
3560 tmphdc = CreateCompatibleDC(hdc);
\r
3562 /* If dragging is in progress, we temporarely remove the piece */
\r
3563 /* [HGM] or temporarily decrease count if stacked */
\r
3564 /* !! Moved to before board compare !! */
\r
3565 if (dragInfo.from.x >= 0 && dragInfo.pos.x >= 0) {
\r
3566 dragged_piece = board[dragInfo.from.y][dragInfo.from.x];
\r
3567 if(dragInfo.from.x == BOARD_LEFT-2 ) {
\r
3568 if(--board[dragInfo.from.y][dragInfo.from.x+1] == 0 )
\r
3569 board[dragInfo.from.y][dragInfo.from.x] = EmptySquare;
\r
3571 if(dragInfo.from.x == BOARD_RGHT+1) {
\r
3572 if(--board[dragInfo.from.y][dragInfo.from.x-1] == 0 )
\r
3573 board[dragInfo.from.y][dragInfo.from.x] = EmptySquare;
\r
3575 board[dragInfo.from.y][dragInfo.from.x] = gatingPiece;
\r
3578 /* Figure out which squares need updating by comparing the
\r
3579 * newest board with the last drawn board and checking if
\r
3580 * flipping has changed.
\r
3582 if (!fullrepaint && lastDrawnValid[nr] && (nr == 1 || lastDrawnFlipView == flipView)) {
\r
3583 for (row = 0; row < BOARD_HEIGHT; row++) { /* [HGM] true size, not 8 */
\r
3584 for (column = 0; column < BOARD_WIDTH; column++) {
\r
3585 if (lastDrawn[nr][row][column] != board[row][column]) {
\r
3586 SquareToPos(row, column, &x, &y);
\r
3587 clips[num_clips++] =
\r
3588 CreateRectRgn(x, y, x + squareSize, y + squareSize);
\r
3592 if(nr == 0) { // [HGM] dual: no highlights on second board
\r
3593 for (i=0; i<2; i++) {
\r
3594 if (lastDrawnHighlight.sq[i].x != highlightInfo.sq[i].x ||
\r
3595 lastDrawnHighlight.sq[i].y != highlightInfo.sq[i].y) {
\r
3596 if (lastDrawnHighlight.sq[i].x >= 0 &&
\r
3597 lastDrawnHighlight.sq[i].y >= 0) {
\r
3598 SquareToPos(lastDrawnHighlight.sq[i].y,
\r
3599 lastDrawnHighlight.sq[i].x, &x, &y);
\r
3600 clips[num_clips++] =
\r
3601 CreateRectRgn(x - lineGap, y - lineGap,
\r
3602 x + squareSize + lineGap, y + squareSize + lineGap);
\r
3604 if (highlightInfo.sq[i].x >= 0 && highlightInfo.sq[i].y >= 0) {
\r
3605 SquareToPos(highlightInfo.sq[i].y, highlightInfo.sq[i].x, &x, &y);
\r
3606 clips[num_clips++] =
\r
3607 CreateRectRgn(x - lineGap, y - lineGap,
\r
3608 x + squareSize + lineGap, y + squareSize + lineGap);
\r
3612 for (i=0; i<2; i++) {
\r
3613 if (lastDrawnPremove.sq[i].x != premoveHighlightInfo.sq[i].x ||
\r
3614 lastDrawnPremove.sq[i].y != premoveHighlightInfo.sq[i].y) {
\r
3615 if (lastDrawnPremove.sq[i].x >= 0 &&
\r
3616 lastDrawnPremove.sq[i].y >= 0) {
\r
3617 SquareToPos(lastDrawnPremove.sq[i].y,
\r
3618 lastDrawnPremove.sq[i].x, &x, &y);
\r
3619 clips[num_clips++] =
\r
3620 CreateRectRgn(x - lineGap, y - lineGap,
\r
3621 x + squareSize + lineGap, y + squareSize + lineGap);
\r
3623 if (premoveHighlightInfo.sq[i].x >= 0 &&
\r
3624 premoveHighlightInfo.sq[i].y >= 0) {
\r
3625 SquareToPos(premoveHighlightInfo.sq[i].y,
\r
3626 premoveHighlightInfo.sq[i].x, &x, &y);
\r
3627 clips[num_clips++] =
\r
3628 CreateRectRgn(x - lineGap, y - lineGap,
\r
3629 x + squareSize + lineGap, y + squareSize + lineGap);
\r
3633 } else { // nr == 1
\r
3634 partnerHighlightInfo.sq[0].y = board[EP_STATUS-4];
\r
3635 partnerHighlightInfo.sq[0].x = board[EP_STATUS-3];
\r
3636 partnerHighlightInfo.sq[1].y = board[EP_STATUS-2];
\r
3637 partnerHighlightInfo.sq[1].x = board[EP_STATUS-1];
\r
3638 for (i=0; i<2; i++) {
\r
3639 if (partnerHighlightInfo.sq[i].x >= 0 &&
\r
3640 partnerHighlightInfo.sq[i].y >= 0) {
\r
3641 SquareToPos(partnerHighlightInfo.sq[i].y,
\r
3642 partnerHighlightInfo.sq[i].x, &x, &y);
\r
3643 clips[num_clips++] =
\r
3644 CreateRectRgn(x - lineGap, y - lineGap,
\r
3645 x + squareSize + lineGap, y + squareSize + lineGap);
\r
3647 if (oldPartnerHighlight.sq[i].x >= 0 &&
\r
3648 oldPartnerHighlight.sq[i].y >= 0) {
\r
3649 SquareToPos(oldPartnerHighlight.sq[i].y,
\r
3650 oldPartnerHighlight.sq[i].x, &x, &y);
\r
3651 clips[num_clips++] =
\r
3652 CreateRectRgn(x - lineGap, y - lineGap,
\r
3653 x + squareSize + lineGap, y + squareSize + lineGap);
\r
3658 fullrepaint = TRUE;
\r
3661 /* Create a buffer bitmap - this is the actual bitmap
\r
3662 * being written to. When all the work is done, we can
\r
3663 * copy it to the real DC (the screen). This avoids
\r
3664 * the problems with flickering.
\r
3666 GetClientRect(hwndMain, &Rect);
\r
3667 bufferBitmap = CreateCompatibleBitmap(hdc, Rect.right-Rect.left+1,
\r
3668 Rect.bottom-Rect.top+1);
\r
3669 oldBitmap = SelectObject(hdcmem, bufferBitmap);
\r
3670 if (!appData.monoMode) {
\r
3671 SelectPalette(hdcmem, hPal, FALSE);
\r
3674 /* Create clips for dragging */
\r
3675 if (!fullrepaint) {
\r
3676 if (dragInfo.from.x >= 0) {
\r
3677 SquareToPos(dragInfo.from.y, dragInfo.from.x, &x, &y);
\r
3678 clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);
\r
3680 if (dragInfo.start.x >= 0) {
\r
3681 SquareToPos(dragInfo.start.y, dragInfo.start.x, &x, &y);
\r
3682 clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);
\r
3684 if (dragInfo.pos.x >= 0) {
\r
3685 x = dragInfo.pos.x - squareSize / 2;
\r
3686 y = dragInfo.pos.y - squareSize / 2;
\r
3687 clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);
\r
3689 if (dragInfo.lastpos.x >= 0) {
\r
3690 x = dragInfo.lastpos.x - squareSize / 2;
\r
3691 y = dragInfo.lastpos.y - squareSize / 2;
\r
3692 clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);
\r
3696 /* Are we animating a move?
\r
3698 * - remove the piece from the board (temporarely)
\r
3699 * - calculate the clipping region
\r
3701 if (!fullrepaint) {
\r
3702 if (animInfo.piece != EmptySquare) {
\r
3703 board[animInfo.from.y][animInfo.from.x] = EmptySquare;
\r
3704 x = boardRect.left + animInfo.lastpos.x;
\r
3705 y = boardRect.top + animInfo.lastpos.y;
\r
3706 x2 = boardRect.left + animInfo.pos.x;
\r
3707 y2 = boardRect.top + animInfo.pos.y;
\r
3708 clips[num_clips++] = CreateRectRgn(MIN(x,x2), MIN(y,y2), MAX(x,x2)+squareSize, MAX(y,y2)+squareSize);
\r
3709 /* Slight kludge. The real problem is that after AnimateMove is
\r
3710 done, the position on the screen does not match lastDrawn.
\r
3711 This currently causes trouble only on e.p. captures in
\r
3712 atomic, where the piece moves to an empty square and then
\r
3713 explodes. The old and new positions both had an empty square
\r
3714 at the destination, but animation has drawn a piece there and
\r
3715 we have to remember to erase it. [HGM] moved until after setting lastDrawn */
\r
3716 lastDrawn[0][animInfo.to.y][animInfo.to.x] = animInfo.piece;
\r
3720 /* No clips? Make sure we have fullrepaint set to TRUE */
\r
3721 if (num_clips == 0)
\r
3722 fullrepaint = TRUE;
\r
3724 /* Set clipping on the memory DC */
\r
3725 if (!fullrepaint) {
\r
3726 SelectClipRgn(hdcmem, clips[0]);
\r
3727 for (x = 1; x < num_clips; x++) {
\r
3728 if (ExtSelectClipRgn(hdcmem, clips[x], RGN_OR) == ERROR)
\r
3729 abort(); // this should never ever happen!
\r
3733 /* Do all the drawing to the memory DC */
\r
3734 if(explodeInfo.radius) { // [HGM] atomic
\r
3736 int x, y, r=(explodeInfo.radius * squareSize)/100;
\r
3737 ChessSquare piece = board[explodeInfo.fromY][explodeInfo.fromX];
\r
3738 board[explodeInfo.fromY][explodeInfo.fromX] = EmptySquare; // suppress display of capturer
\r
3739 SquareToPos(explodeInfo.toY, explodeInfo.toX, &x, &y);
\r
3740 x += squareSize/2;
\r
3741 y += squareSize/2;
\r
3742 if(!fullrepaint) {
\r
3743 clips[num_clips] = CreateRectRgn(x-r, y-r, x+r, y+r);
\r
3744 ExtSelectClipRgn(hdcmem, clips[num_clips++], RGN_OR);
\r
3746 DrawGridOnDC(hdcmem);
\r
3747 DrawHighlightsOnDC(hdcmem, &highlightInfo, HIGHLIGHT_PEN);
\r
3748 DrawHighlightsOnDC(hdcmem, &premoveHighlightInfo, PREMOVE_PEN);
\r
3749 DrawBoardOnDC(hdcmem, board, tmphdc);
\r
3750 board[explodeInfo.fromY][explodeInfo.fromX] = piece;
\r
3751 oldBrush = SelectObject(hdcmem, explodeBrush);
\r
3752 Ellipse(hdcmem, x-r, y-r, x+r, y+r);
\r
3753 SelectObject(hdcmem, oldBrush);
\r
3755 DrawGridOnDC(hdcmem);
\r
3756 if(nr == 0) { // [HGM] dual: decide which highlights to draw
\r
3757 DrawHighlightsOnDC(hdcmem, &highlightInfo, HIGHLIGHT_PEN);
\r
3758 DrawHighlightsOnDC(hdcmem, &premoveHighlightInfo, PREMOVE_PEN);
\r
3760 DrawHighlightsOnDC(hdcmem, &partnerHighlightInfo, HIGHLIGHT_PEN);
\r
3761 oldPartnerHighlight = partnerHighlightInfo;
\r
3763 DrawBoardOnDC(hdcmem, board, tmphdc);
\r
3765 if(nr == 0) // [HGM] dual: markers only on left board
\r
3766 for (row = 0; row < BOARD_HEIGHT; row++) {
\r
3767 for (column = 0; column < BOARD_WIDTH; column++) {
\r
3768 if (marker[row][column]) { // marker changes only occur with full repaint!
\r
3769 HBRUSH oldBrush = SelectObject(hdcmem,
\r
3770 marker[row][column] == 2 ? markerBrush : explodeBrush);
\r
3771 SquareToPos(row, column, &x, &y);
\r
3772 Ellipse(hdcmem, x + squareSize/4, y + squareSize/4,
\r
3773 x + 3*squareSize/4, y + 3*squareSize/4);
\r
3774 SelectObject(hdcmem, oldBrush);
\r
3779 if( appData.highlightMoveWithArrow ) {
\r
3780 DrawArrowHighlight(hdcmem);
\r
3783 DrawCoordsOnDC(hdcmem);
\r
3785 CopyBoard(lastDrawn[nr], board); /* [HGM] Moved to here from end of routine, */
\r
3786 /* to make sure lastDrawn contains what is actually drawn */
\r
3788 /* Put the dragged piece back into place and draw it (out of place!) */
\r
3789 if (dragged_piece != EmptySquare) {
\r
3790 /* [HGM] or restack */
\r
3791 if(dragInfo.from.x == BOARD_LEFT-2 )
\r
3792 board[dragInfo.from.y][dragInfo.from.x+1]++;
\r
3794 if(dragInfo.from.x == BOARD_RGHT+1 )
\r
3795 board[dragInfo.from.y][dragInfo.from.x-1]++;
\r
3796 board[dragInfo.from.y][dragInfo.from.x] = dragged_piece;
\r
3797 x = dragInfo.pos.x - squareSize / 2;
\r
3798 y = dragInfo.pos.y - squareSize / 2;
\r
3799 DrawPieceOnDC(hdcmem, dragInfo.piece,
\r
3800 ((int) dragInfo.piece < (int) BlackPawn),
\r
3801 (dragInfo.from.y + dragInfo.from.x) % 2, x, y, tmphdc);
\r
3804 /* Put the animated piece back into place and draw it */
\r
3805 if (animInfo.piece != EmptySquare) {
\r
3806 board[animInfo.from.y][animInfo.from.x] = animInfo.piece;
\r
3807 x = boardRect.left + animInfo.pos.x;
\r
3808 y = boardRect.top + animInfo.pos.y;
\r
3809 DrawPieceOnDC(hdcmem, animInfo.piece,
\r
3810 ((int) animInfo.piece < (int) BlackPawn),
\r
3811 (animInfo.from.y + animInfo.from.x) % 2, x, y, tmphdc);
\r
3814 /* Release the bufferBitmap by selecting in the old bitmap
\r
3815 * and delete the memory DC
\r
3817 SelectObject(hdcmem, oldBitmap);
\r
3820 /* Set clipping on the target DC */
\r
3821 if (!fullrepaint) {
\r
3822 if(nr == 1) for (x = 0; x < num_clips; x++) { // [HGM] dual: translate clips
\r
3824 GetRgnBox(clips[x], &rect);
\r
3825 DeleteObject(clips[x]);
\r
3826 clips[x] = CreateRectRgn(rect.left + wpMain.width/2, rect.top,
\r
3827 rect.right + wpMain.width/2, rect.bottom);
\r
3829 SelectClipRgn(hdc, clips[0]);
\r
3830 for (x = 1; x < num_clips; x++) {
\r
3831 if (ExtSelectClipRgn(hdc, clips[x], RGN_OR) == ERROR)
\r
3832 abort(); // this should never ever happen!
\r
3836 /* Copy the new bitmap onto the screen in one go.
\r
3837 * This way we avoid any flickering
\r
3839 oldBitmap = SelectObject(tmphdc, bufferBitmap);
\r
3840 BitBlt(hdc, boardRect.left + twoBoards*partnerUp*wpMain.width/2, boardRect.top, // [HGM] dual
\r
3841 boardRect.right - boardRect.left,
\r
3842 boardRect.bottom - boardRect.top,
\r
3843 tmphdc, boardRect.left, boardRect.top, SRCCOPY);
\r
3844 if(saveDiagFlag) {
\r
3845 BITMAP b; int i, j=0, m, w, wb, fac=0; char pData[1000000];
\r
3846 BITMAPINFOHEADER bih; int color[16], nrColors=0;
\r
3848 GetObject(bufferBitmap, sizeof(b), &b);
\r
3849 if(b.bmWidthBytes*b.bmHeight <= 990000) {
\r
3850 bih.biSize = sizeof(BITMAPINFOHEADER);
\r
3851 bih.biWidth = b.bmWidth;
\r
3852 bih.biHeight = b.bmHeight;
\r
3854 bih.biBitCount = b.bmBitsPixel;
\r
3855 bih.biCompression = 0;
\r
3856 bih.biSizeImage = b.bmWidthBytes*b.bmHeight;
\r
3857 bih.biXPelsPerMeter = 0;
\r
3858 bih.biYPelsPerMeter = 0;
\r
3859 bih.biClrUsed = 0;
\r
3860 bih.biClrImportant = 0;
\r
3861 // fprintf(diagFile, "t=%d\nw=%d\nh=%d\nB=%d\nP=%d\nX=%d\n",
\r
3862 // b.bmType, b.bmWidth, b.bmHeight, b.bmWidthBytes, b.bmPlanes, b.bmBitsPixel);
\r
3863 GetDIBits(tmphdc,bufferBitmap,0,b.bmHeight,pData,(BITMAPINFO*)&bih,DIB_RGB_COLORS);
\r
3864 // fprintf(diagFile, "%8x\n", (int) pData);
\r
3866 wb = b.bmWidthBytes;
\r
3868 for(i=0; i<wb*(b.bmHeight - boardRect.top + OUTER_MARGIN)>>2; i++) {
\r
3869 int k = ((int*) pData)[i];
\r
3870 for(j=0; j<nrColors; j++) if(color[j] == k) break;
\r
3871 if(j >= 16) break;
\r
3873 if(j >= nrColors) nrColors = j+1;
\r
3875 if(j<16) { // 16 colors is enough. Compress to 4 bits per pixel
\r
3877 for(i=0; i<b.bmHeight - boardRect.top + OUTER_MARGIN; i++) {
\r
3878 for(w=0; w<(wb>>2); w+=2) {
\r
3879 int k = ((int*) pData)[(wb*i>>2) + w];
\r
3880 for(j=0; j<nrColors; j++) if(color[j] == k) break;
\r
3881 k = ((int*) pData)[(wb*i>>2) + w + 1];
\r
3882 for(m=0; m<nrColors; m++) if(color[m] == k) break;
\r
3883 pData[p++] = m | j<<4;
\r
3885 while(p&3) pData[p++] = 0;
\r
3888 wb = ((wb+31)>>5)<<2;
\r
3890 // write BITMAPFILEHEADER
\r
3891 fprintf(diagFile, "BM");
\r
3892 fputDW(diagFile, wb*(b.bmHeight - boardRect.top + OUTER_MARGIN)+0x36 + (fac?64:0));
\r
3893 fputDW(diagFile, 0);
\r
3894 fputDW(diagFile, 0x36 + (fac?64:0));
\r
3895 // write BITMAPINFOHEADER
\r
3896 fputDW(diagFile, 40);
\r
3897 fputDW(diagFile, b.bmWidth);
\r
3898 fputDW(diagFile, b.bmHeight - boardRect.top + OUTER_MARGIN);
\r
3899 if(fac) fputDW(diagFile, 0x040001); // planes and bits/pixel
\r
3900 else fputDW(diagFile, 0x200001); // planes and bits/pixel
\r
3901 fputDW(diagFile, 0);
\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 // write color table
\r
3909 for(i=0; i<16; i++) fputDW(diagFile, color[i]);
\r
3910 // write bitmap data
\r
3911 for(i=0; i<wb*(b.bmHeight - boardRect.top + OUTER_MARGIN); i++)
\r
3912 fputc(pData[i], diagFile);
\r
3916 SelectObject(tmphdc, oldBitmap);
\r
3918 /* Massive cleanup */
\r
3919 for (x = 0; x < num_clips; x++)
\r
3920 DeleteObject(clips[x]);
\r
3923 DeleteObject(bufferBitmap);
\r
3926 ReleaseDC(hwndMain, hdc);
\r
3928 if (lastDrawnFlipView != flipView && nr == 0) {
\r
3930 CheckMenuItem(GetMenu(hwndMain),IDM_FlipView, MF_BYCOMMAND|MF_CHECKED);
\r
3932 CheckMenuItem(GetMenu(hwndMain),IDM_FlipView, MF_BYCOMMAND|MF_UNCHECKED);
\r
3935 /* CopyBoard(lastDrawn, board);*/
\r
3936 lastDrawnHighlight = highlightInfo;
\r
3937 lastDrawnPremove = premoveHighlightInfo;
\r
3938 lastDrawnFlipView = flipView;
\r
3939 lastDrawnValid[nr] = 1;
\r
3942 /* [HGM] diag: Save the current board display to the given open file and close the file */
\r
3947 saveDiagFlag = 1; diagFile = f;
\r
3948 HDCDrawPosition(NULL, TRUE, NULL);
\r
3952 // if(f != NULL) fprintf(f, "Sorry, but this feature is still in preparation\n");
\r
3959 /*---------------------------------------------------------------------------*\
\r
3960 | CLIENT PAINT PROCEDURE
\r
3961 | This is the main event-handler for the WM_PAINT message.
\r
3963 \*---------------------------------------------------------------------------*/
\r
3965 PaintProc(HWND hwnd)
\r
3971 if((hdc = BeginPaint(hwnd, &ps))) {
\r
3972 if (IsIconic(hwnd)) {
\r
3973 DrawIcon(hdc, 2, 2, iconCurrent);
\r
3975 if (!appData.monoMode) {
\r
3976 SelectPalette(hdc, hPal, FALSE);
\r
3977 RealizePalette(hdc);
\r
3979 HDCDrawPosition(hdc, 1, NULL);
\r
3980 if(twoBoards) { // [HGM] dual: also redraw other board in other orientation
\r
3981 flipView = !flipView; partnerUp = !partnerUp;
\r
3982 HDCDrawPosition(hdc, 1, NULL);
\r
3983 flipView = !flipView; partnerUp = !partnerUp;
\r
3986 SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);
\r
3987 ExtTextOut(hdc, messageRect.left, messageRect.top,
\r
3988 ETO_CLIPPED|ETO_OPAQUE,
\r
3989 &messageRect, messageText, strlen(messageText), NULL);
\r
3990 SelectObject(hdc, oldFont);
\r
3991 DisplayBothClocks();
\r
3994 EndPaint(hwnd,&ps);
\r
4002 * If the user selects on a border boundary, return -1; if off the board,
\r
4003 * return -2. Otherwise map the event coordinate to the square.
\r
4004 * The offset boardRect.left or boardRect.top must already have been
\r
4005 * subtracted from x.
\r
4007 int EventToSquare(x, limit)
\r
4015 if ((x % (squareSize + lineGap)) >= squareSize)
\r
4017 x /= (squareSize + lineGap);
\r
4029 DropEnable dropEnables[] = {
\r
4030 { 'P', DP_Pawn, N_("Pawn") },
\r
4031 { 'N', DP_Knight, N_("Knight") },
\r
4032 { 'B', DP_Bishop, N_("Bishop") },
\r
4033 { 'R', DP_Rook, N_("Rook") },
\r
4034 { 'Q', DP_Queen, N_("Queen") },
\r
4038 SetupDropMenu(HMENU hmenu)
\r
4040 int i, count, enable;
\r
4042 extern char white_holding[], black_holding[];
\r
4043 char item[MSG_SIZ];
\r
4045 for (i=0; i<sizeof(dropEnables)/sizeof(DropEnable); i++) {
\r
4046 p = strchr(gameMode == IcsPlayingWhite ? white_holding : black_holding,
\r
4047 dropEnables[i].piece);
\r
4049 while (p && *p++ == dropEnables[i].piece) count++;
\r
4050 snprintf(item, MSG_SIZ, "%s %d", T_(dropEnables[i].name), count);
\r
4051 enable = count > 0 || !appData.testLegality
\r
4052 /*!!temp:*/ || (gameInfo.variant == VariantCrazyhouse
\r
4053 && !appData.icsActive);
\r
4054 ModifyMenu(hmenu, dropEnables[i].command,
\r
4055 MF_BYCOMMAND | (enable ? MF_ENABLED : MF_GRAYED) | MF_STRING,
\r
4056 dropEnables[i].command, item);
\r
4060 void DragPieceBegin(int x, int y)
\r
4062 dragInfo.lastpos.x = boardRect.left + x;
\r
4063 dragInfo.lastpos.y = boardRect.top + y;
\r
4064 dragInfo.from.x = fromX;
\r
4065 dragInfo.from.y = fromY;
\r
4066 dragInfo.piece = boards[currentMove][fromY][fromX];
\r
4067 dragInfo.start = dragInfo.from;
\r
4068 SetCapture(hwndMain);
\r
4071 void DragPieceEnd(int x, int y)
\r
4074 dragInfo.start.x = dragInfo.start.y = -1;
\r
4075 dragInfo.from = dragInfo.start;
\r
4076 dragInfo.pos = dragInfo.lastpos = dragInfo.start;
\r
4079 void ChangeDragPiece(ChessSquare piece)
\r
4081 dragInfo.piece = piece;
\r
4084 /* Event handler for mouse messages */
\r
4086 MouseEvent(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
\r
4090 static int recursive = 0;
\r
4092 BOOLEAN forceFullRepaint = IsFullRepaintPreferrable(); /* [AS] */
\r
4095 if (message == WM_MBUTTONUP) {
\r
4096 /* Hideous kludge to fool TrackPopupMenu into paying attention
\r
4097 to the middle button: we simulate pressing the left button too!
\r
4099 PostMessage(hwnd, WM_LBUTTONDOWN, wParam, lParam);
\r
4100 PostMessage(hwnd, WM_LBUTTONUP, wParam, lParam);
\r
4106 pt.x = LOWORD(lParam);
\r
4107 pt.y = HIWORD(lParam);
\r
4108 x = EventToSquare(pt.x - boardRect.left, BOARD_WIDTH);
\r
4109 y = EventToSquare(pt.y - boardRect.top, BOARD_HEIGHT);
\r
4110 if (!flipView && y >= 0) {
\r
4111 y = BOARD_HEIGHT - 1 - y;
\r
4113 if (flipView && x >= 0) {
\r
4114 x = BOARD_WIDTH - 1 - x;
\r
4117 shiftKey = GetKeyState(VK_SHIFT) < 0; // [HGM] remember last shift status
\r
4119 switch (message) {
\r
4120 case WM_LBUTTONDOWN:
\r
4121 if (PtInRect((LPRECT) &whiteRect, pt)) {
\r
4122 ClockClick(flipClock);
\r
4123 } else if (PtInRect((LPRECT) &blackRect, pt)) {
\r
4124 ClockClick(!flipClock);
\r
4126 dragInfo.start.x = dragInfo.start.y = -1;
\r
4127 dragInfo.from = dragInfo.start;
\r
4128 if(fromX == -1 && frozen) { // not sure where this is for
\r
4129 fromX = fromY = -1;
\r
4130 DrawPosition(forceFullRepaint || FALSE, NULL); /* [AS] */
\r
4133 LeftClick(Press, pt.x - boardRect.left, pt.y - boardRect.top);
\r
4134 DrawPosition(TRUE, NULL);
\r
4137 case WM_LBUTTONUP:
\r
4138 LeftClick(Release, pt.x - boardRect.left, pt.y - boardRect.top);
\r
4139 DrawPosition(TRUE, NULL);
\r
4142 case WM_MOUSEMOVE:
\r
4143 if(SeekGraphClick(Press, pt.x - boardRect.left, pt.y - boardRect.top, 1)) break;
\r
4144 if(PromoScroll(pt.x - boardRect.left, pt.y - boardRect.top)) break;
\r
4145 MovePV(pt.x - boardRect.left, pt.y - boardRect.top, boardRect.bottom - boardRect.top);
\r
4146 if ((appData.animateDragging || appData.highlightDragging)
\r
4147 && (wParam & MK_LBUTTON)
\r
4148 && dragInfo.from.x >= 0)
\r
4150 BOOL full_repaint = FALSE;
\r
4152 if (appData.animateDragging) {
\r
4153 dragInfo.pos = pt;
\r
4155 if (appData.highlightDragging) {
\r
4156 SetHighlights(fromX, fromY, x, y);
\r
4157 if( IsDrawArrowEnabled() && (x < 0 || x >= BOARD_WIDTH || y < 0 || y >= BOARD_HEIGHT) ) {
\r
4158 full_repaint = TRUE;
\r
4162 DrawPosition( full_repaint, NULL);
\r
4164 dragInfo.lastpos = dragInfo.pos;
\r
4168 case WM_MOUSEWHEEL: // [DM]
\r
4169 { static int lastDir = 0; // [HGM] build in some hysteresis to avoid spurious events
\r
4170 /* Mouse Wheel is being rolled forward
\r
4171 * Play moves forward
\r
4173 if((short)HIWORD(wParam) < 0 && currentMove < forwardMostMove)
\r
4174 { if(lastDir == 1) ForwardEvent(); else lastDir = 1; } // [HGM] suppress first event in direction
\r
4175 /* Mouse Wheel is being rolled backward
\r
4176 * Play moves backward
\r
4178 if((short)HIWORD(wParam) > 0 && currentMove > backwardMostMove)
\r
4179 { if(lastDir == -1) BackwardEvent(); else lastDir = -1; }
\r
4183 case WM_MBUTTONUP:
\r
4184 case WM_RBUTTONUP:
\r
4186 RightClick(Release, pt.x - boardRect.left, pt.y - boardRect.top, &fromX, &fromY);
\r
4189 case WM_MBUTTONDOWN:
\r
4190 case WM_RBUTTONDOWN:
\r
4193 fromX = fromY = -1;
\r
4194 dragInfo.pos.x = dragInfo.pos.y = -1;
\r
4195 dragInfo.start.x = dragInfo.start.y = -1;
\r
4196 dragInfo.from = dragInfo.start;
\r
4197 dragInfo.lastpos = dragInfo.pos;
\r
4198 if (appData.highlightDragging) {
\r
4199 ClearHighlights();
\r
4202 /* [HGM] right mouse button in clock area edit-game mode ups clock */
\r
4203 if (PtInRect((LPRECT) &whiteRect, pt)) {
\r
4204 if (gameMode == EditGame || GetKeyState(VK_SHIFT) < 0) AdjustClock(flipClock, 1);
\r
4205 } else if (PtInRect((LPRECT) &blackRect, pt)) {
\r
4206 if (gameMode == EditGame || GetKeyState(VK_SHIFT) < 0) AdjustClock(!flipClock, 1);
\r
4210 DrawPosition(TRUE, NULL);
\r
4212 menuNr = RightClick(Press, pt.x - boardRect.left, pt.y - boardRect.top, &fromX, &fromY);
\r
4215 if (message == WM_MBUTTONDOWN) {
\r
4216 buttonCount = 3; /* even if system didn't think so */
\r
4217 if (wParam & MK_SHIFT)
\r
4218 MenuPopup(hwnd, pt, LoadMenu(hInst, "BlackPieceMenu"), -1);
\r
4220 MenuPopup(hwnd, pt, LoadMenu(hInst, "WhitePieceMenu"), -1);
\r
4221 } else { /* message == WM_RBUTTONDOWN */
\r
4222 /* Just have one menu, on the right button. Windows users don't
\r
4223 think to try the middle one, and sometimes other software steals
\r
4224 it, or it doesn't really exist. */
\r
4225 if(gameInfo.variant != VariantShogi)
\r
4226 MenuPopup(hwnd, pt, LoadMenu(hInst, "PieceMenu"), -1);
\r
4228 MenuPopup(hwnd, pt, LoadMenu(hInst, "ShogiPieceMenu"), -1);
\r
4232 SetCapture(hwndMain);
4235 hmenu = LoadMenu(hInst, "DropPieceMenu");
\r
4236 SetupDropMenu(hmenu);
\r
4237 MenuPopup(hwnd, pt, hmenu, -1);
\r
4247 /* Preprocess messages for buttons in main window */
\r
4249 ButtonProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
\r
4251 int id = GetWindowLongPtr(hwnd, GWLP_ID);
\r
4254 for (i=0; i<N_BUTTONS; i++) {
\r
4255 if (buttonDesc[i].id == id) break;
\r
4257 if (i == N_BUTTONS) return 0;
\r
4258 switch (message) {
\r
4263 dir = (wParam == VK_LEFT) ? -1 : 1;
\r
4264 SetFocus(buttonDesc[(i + dir + N_BUTTONS) % N_BUTTONS].hwnd);
\r
4271 SendMessage(hwndMain, WM_COMMAND, MAKEWPARAM(buttonDesc[i].id, 0), 0);
\r
4274 if (appData.icsActive && (isalpha((char)wParam) || wParam == '0')) {
\r
4275 // [HGM] movenum: only letters or leading zero should go to ICS input
\r
4276 HWND h = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
4277 if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);
\r
4279 SendMessage(h, WM_CHAR, wParam, lParam);
\r
4281 } else if (isalpha((char)wParam) || isdigit((char)wParam)){
\r
4282 TypeInEvent((char)wParam);
\r
4288 return CallWindowProc(buttonDesc[i].wndproc, hwnd, message, wParam, lParam);
\r
4291 /* Process messages for Promotion dialog box */
\r
4293 Promotion(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
4297 switch (message) {
\r
4298 case WM_INITDIALOG: /* message: initialize dialog box */
\r
4299 /* Center the dialog over the application window */
\r
4300 CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));
\r
4301 Translate(hDlg, DLG_PromotionKing);
\r
4302 ShowWindow(GetDlgItem(hDlg, PB_King),
\r
4303 (!appData.testLegality || gameInfo.variant == VariantSuicide ||
\r
4304 gameInfo.variant == VariantSpartan && !WhiteOnMove(currentMove) ||
\r
4305 gameInfo.variant == VariantGiveaway || gameInfo.variant == VariantSuper ) ?
\r
4306 SW_SHOW : SW_HIDE);
\r
4307 /* [HGM] Only allow C & A promotions if these pieces are defined */
\r
4308 ShowWindow(GetDlgItem(hDlg, PB_Archbishop),
\r
4309 ((PieceToChar(WhiteAngel) >= 'A' && WhiteOnMove(currentMove) &&
\r
4310 PieceToChar(WhiteAngel) != '~') ||
\r
4311 (PieceToChar(BlackAngel) >= 'A' && !WhiteOnMove(currentMove) &&
\r
4312 PieceToChar(BlackAngel) != '~') ) ?
\r
4313 SW_SHOW : SW_HIDE);
\r
4314 ShowWindow(GetDlgItem(hDlg, PB_Chancellor),
\r
4315 ((PieceToChar(WhiteMarshall) >= 'A' && WhiteOnMove(currentMove) &&
\r
4316 PieceToChar(WhiteMarshall) != '~') ||
\r
4317 (PieceToChar(BlackMarshall) >= 'A' && !WhiteOnMove(currentMove) &&
\r
4318 PieceToChar(BlackMarshall) != '~') ) ?
\r
4319 SW_SHOW : SW_HIDE);
\r
4320 /* [HGM] Hide B & R button in Shogi, use Q as promote, N as defer */
\r
4321 ShowWindow(GetDlgItem(hDlg, PB_Rook),
\r
4322 gameInfo.variant != VariantShogi ?
\r
4323 SW_SHOW : SW_HIDE);
\r
4324 ShowWindow(GetDlgItem(hDlg, PB_Bishop),
\r
4325 gameInfo.variant != VariantShogi ?
\r
4326 SW_SHOW : SW_HIDE);
\r
4327 if(gameInfo.variant == VariantShogi) {
\r
4328 SetDlgItemText(hDlg, PB_Queen, "YES");
\r
4329 SetDlgItemText(hDlg, PB_Knight, "NO");
\r
4330 SetWindowText(hDlg, "Promote?");
\r
4332 ShowWindow(GetDlgItem(hDlg, IDC_Centaur),
\r
4333 gameInfo.variant == VariantSuper ?
\r
4334 SW_SHOW : SW_HIDE);
\r
4337 case WM_COMMAND: /* message: received a command */
\r
4338 switch (LOWORD(wParam)) {
\r
4340 EndDialog(hDlg, TRUE); /* Exit the dialog */
\r
4341 ClearHighlights();
\r
4342 DrawPosition(FALSE, NULL);
\r
4345 promoChar = gameInfo.variant == VariantSuper ? PieceToChar(BlackSilver) : PieceToChar(BlackKing);
\r
4348 promoChar = gameInfo.variant == VariantShogi ? '+' : ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteQueen : BlackQueen));
\r
4351 promoChar = ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteRook : BlackRook));
\r
4352 if(gameInfo.variant == VariantSpartan && !WhiteOnMove(currentMove)) promoChar = PieceToChar(BlackDragon);
\r
4355 promoChar = ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteBishop : BlackBishop));
\r
4356 if(gameInfo.variant == VariantSpartan && !WhiteOnMove(currentMove)) promoChar = PieceToChar(BlackAlfil);
\r
4358 case PB_Chancellor:
\r
4359 promoChar = ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteMarshall : BlackMarshall));
\r
4361 case PB_Archbishop:
\r
4362 promoChar = ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteAngel : BlackAngel));
\r
4365 promoChar = gameInfo.variant == VariantShogi ? '=' : PieceToChar(WhiteOnMove(currentMove) ? WhiteKnight : BlackKnight);
\r
4370 if(promoChar == '.') return FALSE; // invalid piece chosen
\r
4371 EndDialog(hDlg, TRUE); /* Exit the dialog */
\r
4372 UserMoveEvent(fromX, fromY, toX, toY, promoChar);
\r
4373 fromX = fromY = -1;
\r
4374 if (!appData.highlightLastMove) {
\r
4375 ClearHighlights();
\r
4376 DrawPosition(FALSE, NULL);
\r
4383 /* Pop up promotion dialog */
\r
4385 PromotionPopup(HWND hwnd)
\r
4389 lpProc = MakeProcInstance((FARPROC)Promotion, hInst);
\r
4390 DialogBox(hInst, MAKEINTRESOURCE(DLG_PromotionKing),
\r
4391 hwnd, (DLGPROC)lpProc);
\r
4392 FreeProcInstance(lpProc);
\r
4398 DrawPosition(TRUE, NULL);
\r
4399 PromotionPopup(hwndMain);
\r
4402 /* Toggle ShowThinking */
\r
4404 ToggleShowThinking()
\r
4406 appData.showThinking = !appData.showThinking;
\r
4407 ShowThinkingEvent();
\r
4411 LoadGameDialog(HWND hwnd, char* title)
\r
4415 char fileTitle[MSG_SIZ];
\r
4416 f = OpenFileDialog(hwnd, "rb", "",
\r
4417 appData.oldSaveStyle ? "gam" : "pgn",
\r
4419 title, &number, fileTitle, NULL);
\r
4421 cmailMsgLoaded = FALSE;
\r
4422 if (number == 0) {
\r
4423 int error = GameListBuild(f);
\r
4425 DisplayError(_("Cannot build game list"), error);
\r
4426 } else if (!ListEmpty(&gameList) &&
\r
4427 ((ListGame *) gameList.tailPred)->number > 1) {
\r
4428 GameListPopUp(f, fileTitle);
\r
4431 GameListDestroy();
\r
4434 LoadGame(f, number, fileTitle, FALSE);
\r
4438 int get_term_width()
\r
4443 HFONT hfont, hold_font;
\r
4448 hText = GetDlgItem(hwndConsole, OPT_ConsoleText);
\r
4452 // get the text metrics
\r
4453 hdc = GetDC(hText);
\r
4454 lf = font[boardSize][CONSOLE_FONT]->lf;
\r
4455 if (consoleCF.dwEffects & CFE_BOLD)
\r
4456 lf.lfWeight = FW_BOLD;
\r
4457 if (consoleCF.dwEffects & CFE_ITALIC)
\r
4458 lf.lfItalic = TRUE;
\r
4459 if (consoleCF.dwEffects & CFE_STRIKEOUT)
\r
4460 lf.lfStrikeOut = TRUE;
\r
4461 if (consoleCF.dwEffects & CFE_UNDERLINE)
\r
4462 lf.lfUnderline = TRUE;
\r
4463 hfont = CreateFontIndirect(&lf);
\r
4464 hold_font = SelectObject(hdc, hfont);
\r
4465 GetTextMetrics(hdc, &tm);
\r
4466 SelectObject(hdc, hold_font);
\r
4467 DeleteObject(hfont);
\r
4468 ReleaseDC(hText, hdc);
\r
4470 // get the rectangle
\r
4471 SendMessage(hText, EM_GETRECT, 0, (LPARAM)&rc);
\r
4473 return (rc.right-rc.left) / tm.tmAveCharWidth;
\r
4476 void UpdateICSWidth(HWND hText)
\r
4478 LONG old_width, new_width;
\r
4480 new_width = get_term_width(hText, FALSE);
\r
4481 old_width = GetWindowLongPtr(hText, GWLP_USERDATA);
\r
4482 if (new_width != old_width)
\r
4484 ics_update_width(new_width);
\r
4485 SetWindowLongPtr(hText, GWLP_USERDATA, new_width);
\r
4490 ChangedConsoleFont()
\r
4493 CHARRANGE tmpsel, sel;
\r
4494 MyFont *f = font[boardSize][CONSOLE_FONT];
\r
4495 HWND hText = GetDlgItem(hwndConsole, OPT_ConsoleText);
\r
4496 HWND hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
4499 cfmt.cbSize = sizeof(CHARFORMAT);
\r
4500 cfmt.dwMask = CFM_FACE|CFM_SIZE|CFM_CHARSET;
\r
4501 safeStrCpy(cfmt.szFaceName, font[boardSize][CONSOLE_FONT]->mfp.faceName,
\r
4502 sizeof(cfmt.szFaceName)/sizeof(cfmt.szFaceName[0]) );
\r
4503 /* yHeight is expressed in twips. A twip is 1/20 of a font's point
\r
4504 * size. This was undocumented in the version of MSVC++ that I had
\r
4505 * when I wrote the code, but is apparently documented now.
\r
4507 cfmt.yHeight = (int)(f->mfp.pointSize * 20.0 + 0.5);
\r
4508 cfmt.bCharSet = f->lf.lfCharSet;
\r
4509 cfmt.bPitchAndFamily = f->lf.lfPitchAndFamily;
\r
4510 SendMessage(hText, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &cfmt);
\r
4511 SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &cfmt);
\r
4512 /* Why are the following seemingly needed too? */
\r
4513 SendMessage(hText, EM_SETCHARFORMAT, SCF_DEFAULT, (LPARAM) &cfmt);
\r
4514 SendMessage(hInput, EM_SETCHARFORMAT, SCF_DEFAULT, (LPARAM) &cfmt);
\r
4515 SendMessage(hText, EM_EXGETSEL, 0, (LPARAM)&sel);
\r
4517 tmpsel.cpMax = -1; /*999999?*/
\r
4518 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&tmpsel);
\r
4519 SendMessage(hText, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cfmt);
\r
4520 /* Trying putting this here too. It still seems to tickle a RichEdit
\r
4521 * bug: sometimes RichEdit indents the first line of a paragraph too.
\r
4523 paraf.cbSize = sizeof(paraf);
\r
4524 paraf.dwMask = PFM_OFFSET | PFM_STARTINDENT;
\r
4525 paraf.dxStartIndent = 0;
\r
4526 paraf.dxOffset = WRAP_INDENT;
\r
4527 SendMessage(hText, EM_SETPARAFORMAT, 0, (LPARAM) ¶f);
\r
4528 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
4529 UpdateICSWidth(hText);
\r
4532 /*---------------------------------------------------------------------------*\
\r
4534 * Window Proc for main window
\r
4536 \*---------------------------------------------------------------------------*/
\r
4538 /* Process messages for main window, etc. */
\r
4540 WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
\r
4543 int wmId, wmEvent;
\r
4547 char fileTitle[MSG_SIZ];
\r
4548 char buf[MSG_SIZ];
\r
4549 static SnapData sd;
\r
4551 switch (message) {
\r
4553 case WM_PAINT: /* message: repaint portion of window */
\r
4557 case WM_ERASEBKGND:
\r
4558 if (IsIconic(hwnd)) {
\r
4559 /* Cheat; change the message */
\r
4560 return (DefWindowProc(hwnd, WM_ICONERASEBKGND, wParam, lParam));
\r
4562 return (DefWindowProc(hwnd, message, wParam, lParam));
\r
4566 case WM_LBUTTONDOWN:
\r
4567 case WM_MBUTTONDOWN:
\r
4568 case WM_RBUTTONDOWN:
\r
4569 case WM_LBUTTONUP:
\r
4570 case WM_MBUTTONUP:
\r
4571 case WM_RBUTTONUP:
\r
4572 case WM_MOUSEMOVE:
\r
4573 case WM_MOUSEWHEEL:
\r
4574 MouseEvent(hwnd, message, wParam, lParam);
\r
4577 JAWS_KB_NAVIGATION
\r
4581 JAWS_ALT_INTERCEPT
\r
4583 if (appData.icsActive && ((char)wParam == '\r' || (char)wParam > ' ' && !((char)wParam >= '1' && (char)wParam <= '9'))) {
\r
4584 // [HGM] movenum: for non-zero digits we always do type-in dialog
\r
4585 HWND h = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
4586 if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);
\r
4588 SendMessage(h, message, wParam, lParam);
\r
4589 } else if(lParam != KF_REPEAT) {
\r
4590 if (isalpha((char)wParam) || isdigit((char)wParam)) {
\r
4591 TypeInEvent((char)wParam);
\r
4592 } else if((char)wParam == 003) CopyGameToClipboard();
\r
4593 else if((char)wParam == 026) PasteGameOrFENFromClipboard();
\r
4598 case WM_PALETTECHANGED:
\r
4599 if (hwnd != (HWND)wParam && !appData.monoMode) {
\r
4601 HDC hdc = GetDC(hwndMain);
\r
4602 SelectPalette(hdc, hPal, TRUE);
\r
4603 nnew = RealizePalette(hdc);
\r
4605 paletteChanged = TRUE;
\r
4606 InvalidateRect(hwnd, &boardRect, FALSE);
\r
4608 ReleaseDC(hwnd, hdc);
\r
4612 case WM_QUERYNEWPALETTE:
\r
4613 if (!appData.monoMode /*&& paletteChanged*/) {
\r
4615 HDC hdc = GetDC(hwndMain);
\r
4616 paletteChanged = FALSE;
\r
4617 SelectPalette(hdc, hPal, FALSE);
\r
4618 nnew = RealizePalette(hdc);
\r
4620 InvalidateRect(hwnd, &boardRect, FALSE);
\r
4622 ReleaseDC(hwnd, hdc);
\r
4627 case WM_COMMAND: /* message: command from application menu */
\r
4628 wmId = LOWORD(wParam);
\r
4629 wmEvent = HIWORD(wParam);
\r
4634 SAY("new game enter a move to play against the computer with white");
\r
4637 case IDM_NewGameFRC:
\r
4638 if( NewGameFRC() == 0 ) {
\r
4643 case IDM_NewVariant:
\r
4644 NewVariantPopup(hwnd);
\r
4647 case IDM_LoadGame:
\r
4648 LoadGameDialog(hwnd, _("Load Game from File"));
\r
4651 case IDM_LoadNextGame:
\r
4655 case IDM_LoadPrevGame:
\r
4659 case IDM_ReloadGame:
\r
4663 case IDM_LoadPosition:
\r
4664 if (gameMode == AnalyzeMode || gameMode == AnalyzeFile) {
\r
4665 Reset(FALSE, TRUE);
\r
4668 f = OpenFileDialog(hwnd, "rb", "",
\r
4669 appData.oldSaveStyle ? "pos" : "fen",
\r
4671 _("Load Position from File"), &number, fileTitle, NULL);
\r
4673 LoadPosition(f, number, fileTitle);
\r
4677 case IDM_LoadNextPosition:
\r
4678 ReloadPosition(1);
\r
4681 case IDM_LoadPrevPosition:
\r
4682 ReloadPosition(-1);
\r
4685 case IDM_ReloadPosition:
\r
4686 ReloadPosition(0);
\r
4689 case IDM_SaveGame:
\r
4690 defName = DefaultFileName(appData.oldSaveStyle ? "gam" : "pgn");
\r
4691 f = OpenFileDialog(hwnd, "a", defName,
\r
4692 appData.oldSaveStyle ? "gam" : "pgn",
\r
4694 _("Save Game to File"), NULL, fileTitle, NULL);
\r
4696 SaveGame(f, 0, "");
\r
4700 case IDM_SavePosition:
\r
4701 defName = DefaultFileName(appData.oldSaveStyle ? "pos" : "fen");
\r
4702 f = OpenFileDialog(hwnd, "a", defName,
\r
4703 appData.oldSaveStyle ? "pos" : "fen",
\r
4705 _("Save Position to File"), NULL, fileTitle, NULL);
\r
4707 SavePosition(f, 0, "");
\r
4711 case IDM_SaveDiagram:
\r
4712 defName = "diagram";
\r
4713 f = OpenFileDialog(hwnd, "wb", defName,
\r
4716 _("Save Diagram to File"), NULL, fileTitle, NULL);
\r
4722 case IDM_CopyGame:
\r
4723 CopyGameToClipboard();
\r
4726 case IDM_PasteGame:
\r
4727 PasteGameFromClipboard();
\r
4730 case IDM_CopyGameListToClipboard:
\r
4731 CopyGameListToClipboard();
\r
4734 /* [AS] Autodetect FEN or PGN data */
\r
4735 case IDM_PasteAny:
\r
4736 PasteGameOrFENFromClipboard();
\r
4739 /* [AS] Move history */
\r
4740 case IDM_ShowMoveHistory:
\r
4741 if( MoveHistoryIsUp() ) {
\r
4742 MoveHistoryPopDown();
\r
4745 MoveHistoryPopUp();
\r
4749 /* [AS] Eval graph */
\r
4750 case IDM_ShowEvalGraph:
\r
4751 if( EvalGraphIsUp() ) {
\r
4752 EvalGraphPopDown();
\r
4756 SetFocus(hwndMain);
\r
4760 /* [AS] Engine output */
\r
4761 case IDM_ShowEngineOutput:
\r
4762 if( EngineOutputIsUp() ) {
\r
4763 EngineOutputPopDown();
\r
4766 EngineOutputPopUp();
\r
4770 /* [AS] User adjudication */
\r
4771 case IDM_UserAdjudication_White:
\r
4772 UserAdjudicationEvent( +1 );
\r
4775 case IDM_UserAdjudication_Black:
\r
4776 UserAdjudicationEvent( -1 );
\r
4779 case IDM_UserAdjudication_Draw:
\r
4780 UserAdjudicationEvent( 0 );
\r
4783 /* [AS] Game list options dialog */
\r
4784 case IDM_GameListOptions:
\r
4785 GameListOptions();
\r
4792 case IDM_CopyPosition:
\r
4793 CopyFENToClipboard();
\r
4796 case IDM_PastePosition:
\r
4797 PasteFENFromClipboard();
\r
4800 case IDM_MailMove:
\r
4804 case IDM_ReloadCMailMsg:
\r
4805 Reset(TRUE, TRUE);
\r
4806 ReloadCmailMsgEvent(FALSE);
\r
4809 case IDM_Minimize:
\r
4810 ShowWindow(hwnd, SW_MINIMIZE);
\r
4817 case IDM_MachineWhite:
\r
4818 MachineWhiteEvent();
\r
4820 * refresh the tags dialog only if it's visible
\r
4822 if (gameMode == MachinePlaysWhite && IsWindowVisible(editTagsDialog)) {
\r
4824 tags = PGNTags(&gameInfo);
\r
4825 TagsPopUp(tags, CmailMsg());
\r
4828 SAY("computer starts playing white");
\r
4831 case IDM_MachineBlack:
\r
4832 MachineBlackEvent();
\r
4834 * refresh the tags dialog only if it's visible
\r
4836 if (gameMode == MachinePlaysBlack && IsWindowVisible(editTagsDialog)) {
\r
4838 tags = PGNTags(&gameInfo);
\r
4839 TagsPopUp(tags, CmailMsg());
\r
4842 SAY("computer starts playing black");
\r
4845 case IDM_Match: // [HGM] match: flows into next case, after setting Match Mode and nr of Games
\r
4846 if(gameMode != BeginningOfGame) { // allow menu item to remain enabled for better mode highligting
\r
4847 DisplayError(_("You can only start a match from the initial position."), 0); break;
\r
4849 appData.matchGames = appData.defaultMatchGames;
4850 MatchEvent(2); // distinguish from command-line-triggered case (matchMode=1)
\r
4853 case IDM_TwoMachines:
\r
4854 TwoMachinesEvent();
\r
4856 * refresh the tags dialog only if it's visible
\r
4858 if (gameMode == TwoMachinesPlay && IsWindowVisible(editTagsDialog)) {
\r
4860 tags = PGNTags(&gameInfo);
\r
4861 TagsPopUp(tags, CmailMsg());
\r
4864 SAY("computer starts playing both sides");
\r
4867 case IDM_AnalysisMode:
\r
4868 if (!first.analysisSupport) {
\r
4869 snprintf(buf, MSG_SIZ, _("%s does not support analysis"), first.tidy);
\r
4870 DisplayError(buf, 0);
\r
4872 SAY("analyzing current position");
\r
4873 /* [DM] icsEngineAnlyze [HGM] Why is this front-end??? */
\r
4874 if (appData.icsActive) {
\r
4875 if (gameMode != IcsObserving) {
\r
4876 snprintf(buf, MSG_SIZ, "You are not observing a game");
\r
4877 DisplayError(buf, 0);
\r
4878 /* secure check */
\r
4879 if (appData.icsEngineAnalyze) {
\r
4880 if (appData.debugMode)
\r
4881 fprintf(debugFP, "Found unexpected active ICS engine analyze \n");
\r
4882 ExitAnalyzeMode();
\r
4888 /* if enable, user want disable icsEngineAnalyze */
\r
4889 if (appData.icsEngineAnalyze) {
\r
4890 ExitAnalyzeMode();
\r
4894 appData.icsEngineAnalyze = TRUE;
\r
4895 if (appData.debugMode) fprintf(debugFP, "ICS engine analyze starting...\n");
\r
4898 if (!appData.showThinking) ToggleShowThinking();
\r
4899 AnalyzeModeEvent();
\r
4903 case IDM_AnalyzeFile:
\r
4904 if (!first.analysisSupport) {
\r
4905 char buf[MSG_SIZ];
\r
4906 snprintf(buf, MSG_SIZ, _("%s does not support analysis"), first.tidy);
\r
4907 DisplayError(buf, 0);
\r
4909 if (!appData.showThinking) ToggleShowThinking();
\r
4910 AnalyzeFileEvent();
\r
4911 LoadGameDialog(hwnd, _("Analyze Game from File"));
\r
4912 AnalysisPeriodicEvent(1);
\r
4916 case IDM_IcsClient:
\r
4920 case IDM_EditGame:
\r
4921 case IDM_EditGame2:
\r
4926 case IDM_EditPosition:
\r
4927 case IDM_EditPosition2:
\r
4928 EditPositionEvent();
\r
4929 SAY("enter a FEN string or setup a position on the board using the control R pop up menu");
\r
4932 case IDM_Training:
\r
4936 case IDM_ShowGameList:
\r
4937 ShowGameListProc();
\r
4940 case IDM_EditProgs1:
\r
4941 EditTagsPopUp(firstChessProgramNames, &firstChessProgramNames);
\r
4944 case IDM_EditProgs2:
\r
4945 EditTagsPopUp(secondChessProgramNames, &secondChessProgramNames);
\r
4948 case IDM_EditServers:
\r
4949 EditTagsPopUp(icsNames, &icsNames);
\r
4952 case IDM_EditTags:
\r
4957 case IDM_EditComment:
\r
4959 if (commentUp && editComment) {
\r
4962 EditCommentEvent();
\r
4982 case IDM_CallFlag:
\r
5002 case IDM_StopObserving:
\r
5003 StopObservingEvent();
\r
5006 case IDM_StopExamining:
\r
5007 StopExaminingEvent();
\r
5011 UploadGameEvent();
\r
5014 case IDM_TypeInMove:
\r
5015 TypeInEvent('\000');
\r
5018 case IDM_TypeInName:
\r
5019 PopUpNameDialog('\000');
\r
5022 case IDM_Backward:
\r
5024 SetFocus(hwndMain);
\r
5031 SetFocus(hwndMain);
\r
5036 SetFocus(hwndMain);
\r
5041 SetFocus(hwndMain);
\r
5045 RevertEvent(FALSE);
\r
5048 case IDM_Annotate: // [HGM] vari: revert with annotation
\r
5049 RevertEvent(TRUE);
\r
5052 case IDM_TruncateGame:
\r
5053 TruncateGameEvent();
\r
5060 case IDM_RetractMove:
\r
5061 RetractMoveEvent();
\r
5064 case IDM_FlipView:
\r
5065 flipView = !flipView;
\r
5066 DrawPosition(FALSE, NULL);
\r
5069 case IDM_FlipClock:
\r
5070 flipClock = !flipClock;
\r
5071 DisplayBothClocks();
\r
5075 case IDM_MuteSounds:
\r
5076 mute = !mute; // [HGM] mute: keep track of global muting variable
\r
5077 CheckMenuItem(GetMenu(hwndMain),IDM_MuteSounds,
\r
5078 MF_BYCOMMAND|(mute?MF_CHECKED:MF_UNCHECKED));
\r
5081 case IDM_GeneralOptions:
\r
5082 GeneralOptionsPopup(hwnd);
\r
5083 DrawPosition(TRUE, NULL);
\r
5086 case IDM_BoardOptions:
\r
5087 BoardOptionsPopup(hwnd);
\r
5090 case IDM_EnginePlayOptions:
\r
5091 EnginePlayOptionsPopup(hwnd);
\r
5094 case IDM_Engine1Options:
\r
5095 EngineOptionsPopup(hwnd, &first);
\r
5098 case IDM_Engine2Options:
\r
5100 if(WaitForSecond(SettingsMenuIfReady)) break;
\r
5101 EngineOptionsPopup(hwnd, &second);
\r
5104 case IDM_OptionsUCI:
\r
5105 UciOptionsPopup(hwnd);
\r
5108 case IDM_IcsOptions:
\r
5109 IcsOptionsPopup(hwnd);
\r
5113 FontsOptionsPopup(hwnd);
\r
5117 SoundOptionsPopup(hwnd);
\r
5120 case IDM_CommPort:
\r
5121 CommPortOptionsPopup(hwnd);
\r
5124 case IDM_LoadOptions:
\r
5125 LoadOptionsPopup(hwnd);
\r
5128 case IDM_SaveOptions:
\r
5129 SaveOptionsPopup(hwnd);
\r
5132 case IDM_TimeControl:
\r
5133 TimeControlOptionsPopup(hwnd);
\r
5136 case IDM_SaveSettings:
\r
5137 SaveSettings(settingsFileName);
\r
5140 case IDM_SaveSettingsOnExit:
\r
5141 saveSettingsOnExit = !saveSettingsOnExit;
\r
5142 (void) CheckMenuItem(GetMenu(hwndMain), IDM_SaveSettingsOnExit,
\r
5143 MF_BYCOMMAND|(saveSettingsOnExit ?
\r
5144 MF_CHECKED : MF_UNCHECKED));
\r
5155 case IDM_AboutGame:
\r
5160 appData.debugMode = !appData.debugMode;
\r
5161 if (appData.debugMode) {
\r
5162 char dir[MSG_SIZ];
\r
5163 GetCurrentDirectory(MSG_SIZ, dir);
\r
5164 SetCurrentDirectory(installDir);
\r
5165 debugFP = fopen(appData.nameOfDebugFile, "w");
\r
5166 SetCurrentDirectory(dir);
\r
5167 setbuf(debugFP, NULL);
\r
5174 case IDM_HELPCONTENTS:
\r
5175 if (!MyHelp (hwnd, "winboard.hlp", HELP_KEY,(DWORD)(LPSTR)"CONTENTS") &&
\r
5176 !HtmlHelp(hwnd, "winboard.chm", 0, 0) ) {
\r
5177 MessageBox (GetFocus(),
\r
5178 _("Unable to activate help"),
\r
5179 szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);
\r
5183 case IDM_HELPSEARCH:
\r
5184 if (!MyHelp (hwnd, "winboard.hlp", HELP_PARTIALKEY, (DWORD)(LPSTR)"") &&
\r
5185 !HtmlHelp(hwnd, "winboard.chm", 0, 0) ) {
\r
5186 MessageBox (GetFocus(),
\r
5187 _("Unable to activate help"),
\r
5188 szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);
\r
5192 case IDM_HELPHELP:
\r
5193 if(!WinHelp(hwnd, (LPSTR)NULL, HELP_HELPONHELP, 0)) {
\r
5194 MessageBox (GetFocus(),
\r
5195 _("Unable to activate help"),
\r
5196 szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);
\r
5201 lpProc = MakeProcInstance((FARPROC)About, hInst);
\r
5203 (gameInfo.event && strcmp(gameInfo.event, "Easter Egg Hunt") == 0) ?
\r
5204 "AboutBox2" : "AboutBox", hwnd, (DLGPROC)lpProc);
\r
5205 FreeProcInstance(lpProc);
\r
5208 case IDM_DirectCommand1:
\r
5209 AskQuestionEvent(_("Direct Command"),
\r
5210 _("Send to chess program:"), "", "1");
\r
5212 case IDM_DirectCommand2:
\r
5213 AskQuestionEvent(_("Direct Command"),
\r
5214 _("Send to second chess program:"), "", "2");
\r
5217 case EP_WhitePawn:
\r
5218 EditPositionMenuEvent(WhitePawn, fromX, fromY);
\r
5219 fromX = fromY = -1;
\r
5222 case EP_WhiteKnight:
\r
5223 EditPositionMenuEvent(WhiteKnight, fromX, fromY);
\r
5224 fromX = fromY = -1;
\r
5227 case EP_WhiteBishop:
\r
5228 EditPositionMenuEvent(WhiteBishop, fromX, fromY);
\r
5229 fromX = fromY = -1;
\r
5232 case EP_WhiteRook:
\r
5233 EditPositionMenuEvent(WhiteRook, fromX, fromY);
\r
5234 fromX = fromY = -1;
\r
5237 case EP_WhiteQueen:
\r
5238 EditPositionMenuEvent(WhiteQueen, fromX, fromY);
\r
5239 fromX = fromY = -1;
\r
5242 case EP_WhiteFerz:
\r
5243 EditPositionMenuEvent(WhiteFerz, fromX, fromY);
\r
5244 fromX = fromY = -1;
\r
5247 case EP_WhiteWazir:
\r
5248 EditPositionMenuEvent(WhiteWazir, fromX, fromY);
\r
5249 fromX = fromY = -1;
\r
5252 case EP_WhiteAlfil:
\r
5253 EditPositionMenuEvent(WhiteAlfil, fromX, fromY);
\r
5254 fromX = fromY = -1;
\r
5257 case EP_WhiteCannon:
\r
5258 EditPositionMenuEvent(WhiteCannon, fromX, fromY);
\r
5259 fromX = fromY = -1;
\r
5262 case EP_WhiteCardinal:
\r
5263 EditPositionMenuEvent(WhiteAngel, fromX, fromY);
\r
5264 fromX = fromY = -1;
\r
5267 case EP_WhiteMarshall:
\r
5268 EditPositionMenuEvent(WhiteMarshall, fromX, fromY);
\r
5269 fromX = fromY = -1;
\r
5272 case EP_WhiteKing:
\r
5273 EditPositionMenuEvent(WhiteKing, fromX, fromY);
\r
5274 fromX = fromY = -1;
\r
5277 case EP_BlackPawn:
\r
5278 EditPositionMenuEvent(BlackPawn, fromX, fromY);
\r
5279 fromX = fromY = -1;
\r
5282 case EP_BlackKnight:
\r
5283 EditPositionMenuEvent(BlackKnight, fromX, fromY);
\r
5284 fromX = fromY = -1;
\r
5287 case EP_BlackBishop:
\r
5288 EditPositionMenuEvent(BlackBishop, fromX, fromY);
\r
5289 fromX = fromY = -1;
\r
5292 case EP_BlackRook:
\r
5293 EditPositionMenuEvent(BlackRook, fromX, fromY);
\r
5294 fromX = fromY = -1;
\r
5297 case EP_BlackQueen:
\r
5298 EditPositionMenuEvent(BlackQueen, fromX, fromY);
\r
5299 fromX = fromY = -1;
\r
5302 case EP_BlackFerz:
\r
5303 EditPositionMenuEvent(BlackFerz, fromX, fromY);
\r
5304 fromX = fromY = -1;
\r
5307 case EP_BlackWazir:
\r
5308 EditPositionMenuEvent(BlackWazir, fromX, fromY);
\r
5309 fromX = fromY = -1;
\r
5312 case EP_BlackAlfil:
\r
5313 EditPositionMenuEvent(BlackAlfil, fromX, fromY);
\r
5314 fromX = fromY = -1;
\r
5317 case EP_BlackCannon:
\r
5318 EditPositionMenuEvent(BlackCannon, fromX, fromY);
\r
5319 fromX = fromY = -1;
\r
5322 case EP_BlackCardinal:
\r
5323 EditPositionMenuEvent(BlackAngel, fromX, fromY);
\r
5324 fromX = fromY = -1;
\r
5327 case EP_BlackMarshall:
\r
5328 EditPositionMenuEvent(BlackMarshall, fromX, fromY);
\r
5329 fromX = fromY = -1;
\r
5332 case EP_BlackKing:
\r
5333 EditPositionMenuEvent(BlackKing, fromX, fromY);
\r
5334 fromX = fromY = -1;
\r
5337 case EP_EmptySquare:
\r
5338 EditPositionMenuEvent(EmptySquare, fromX, fromY);
\r
5339 fromX = fromY = -1;
\r
5342 case EP_ClearBoard:
\r
5343 EditPositionMenuEvent(ClearBoard, fromX, fromY);
\r
5344 fromX = fromY = -1;
\r
5348 EditPositionMenuEvent(WhitePlay, fromX, fromY);
\r
5349 fromX = fromY = -1;
\r
5353 EditPositionMenuEvent(BlackPlay, fromX, fromY);
\r
5354 fromX = fromY = -1;
\r
5358 EditPositionMenuEvent(PromotePiece, fromX, fromY);
\r
5359 fromX = fromY = -1;
\r
5363 EditPositionMenuEvent(DemotePiece, fromX, fromY);
\r
5364 fromX = fromY = -1;
\r
5368 DropMenuEvent(WhitePawn, fromX, fromY);
\r
5369 fromX = fromY = -1;
\r
5373 DropMenuEvent(WhiteKnight, fromX, fromY);
\r
5374 fromX = fromY = -1;
\r
5378 DropMenuEvent(WhiteBishop, fromX, fromY);
\r
5379 fromX = fromY = -1;
\r
5383 DropMenuEvent(WhiteRook, fromX, fromY);
\r
5384 fromX = fromY = -1;
\r
5388 DropMenuEvent(WhiteQueen, fromX, fromY);
\r
5389 fromX = fromY = -1;
\r
5393 barbaric = 0; appData.language = "";
\r
5394 TranslateMenus(0);
\r
5395 CheckMenuItem(GetMenu(hwndMain), lastChecked, MF_BYCOMMAND|MF_UNCHECKED);
\r
5396 CheckMenuItem(GetMenu(hwndMain), IDM_English, MF_BYCOMMAND|MF_CHECKED);
\r
5397 lastChecked = wmId;
\r
5401 if(wmId > IDM_English && wmId < IDM_English+20) {
\r
5402 LoadLanguageFile(languageFile[wmId - IDM_English - 1]);
\r
5403 TranslateMenus(0);
\r
5404 CheckMenuItem(GetMenu(hwndMain), lastChecked, MF_BYCOMMAND|MF_UNCHECKED);
\r
5405 CheckMenuItem(GetMenu(hwndMain), wmId, MF_BYCOMMAND|MF_CHECKED);
\r
5406 lastChecked = wmId;
\r
5409 return (DefWindowProc(hwnd, message, wParam, lParam));
\r
5415 case CLOCK_TIMER_ID:
\r
5416 KillTimer(hwnd, clockTimerEvent); /* Simulate one-shot timer as in X */
\r
5417 clockTimerEvent = 0;
\r
5418 DecrementClocks(); /* call into back end */
\r
5420 case LOAD_GAME_TIMER_ID:
\r
5421 KillTimer(hwnd, loadGameTimerEvent); /* Simulate one-shot timer as in X*/
\r
5422 loadGameTimerEvent = 0;
\r
5423 AutoPlayGameLoop(); /* call into back end */
\r
5425 case ANALYSIS_TIMER_ID:
\r
5426 if ((gameMode == AnalyzeMode || gameMode == AnalyzeFile
\r
5427 || appData.icsEngineAnalyze) && appData.periodicUpdates) {
\r
5428 AnalysisPeriodicEvent(0);
\r
5430 KillTimer(hwnd, analysisTimerEvent);
\r
5431 analysisTimerEvent = 0;
\r
5434 case DELAYED_TIMER_ID:
\r
5435 KillTimer(hwnd, delayedTimerEvent);
\r
5436 delayedTimerEvent = 0;
\r
5437 delayedTimerCallback();
\r
5442 case WM_USER_Input:
\r
5443 InputEvent(hwnd, message, wParam, lParam);
\r
5446 /* [AS] Also move "attached" child windows */
\r
5447 case WM_WINDOWPOSCHANGING:
\r
5449 if( hwnd == hwndMain && appData.useStickyWindows ) {
\r
5450 LPWINDOWPOS lpwp = (LPWINDOWPOS) lParam;
\r
5452 if( ((lpwp->flags & SWP_NOMOVE) == 0) && ((lpwp->flags & SWP_NOSIZE) != 0) ) {
\r
5453 /* Window is moving */
\r
5456 // GetWindowRect( hwnd, &rcMain ); //[HGM] sticky: in XP this returned new position, not old
\r
5457 rcMain.left = wpMain.x; // replace by these 4 lines to reconstruct old rect
\r
5458 rcMain.right = wpMain.x + wpMain.width;
\r
5459 rcMain.top = wpMain.y;
\r
5460 rcMain.bottom = wpMain.y + wpMain.height;
\r
5462 ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, moveHistoryDialog, &wpMoveHistory );
\r
5463 ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, evalGraphDialog, &wpEvalGraph );
\r
5464 ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, engineOutputDialog, &wpEngineOutput );
\r
5465 ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, gameListDialog, &wpGameList );
\r
5466 ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, hwndConsole, &wpConsole );
\r
5467 wpMain.x = lpwp->x;
\r
5468 wpMain.y = lpwp->y;
\r
5473 /* [AS] Snapping */
\r
5474 case WM_ENTERSIZEMOVE:
\r
5475 if(appData.debugMode) { fprintf(debugFP, "size-move\n"); }
\r
5476 if (hwnd == hwndMain) {
\r
5477 doingSizing = TRUE;
\r
5480 return OnEnterSizeMove( &sd, hwnd, wParam, lParam );
\r
5484 if(appData.debugMode) { fprintf(debugFP, "sizing\n"); }
\r
5485 if (hwnd == hwndMain) {
\r
5486 lastSizing = wParam;
\r
5491 if(appData.debugMode) { fprintf(debugFP, "moving\n"); }
\r
5492 return OnMoving( &sd, hwnd, wParam, lParam );
\r
5494 case WM_EXITSIZEMOVE:
\r
5495 if(appData.debugMode) { fprintf(debugFP, "exit size-move, size = %d\n", squareSize); }
\r
5496 if (hwnd == hwndMain) {
\r
5498 doingSizing = FALSE;
\r
5499 InvalidateRect(hwnd, &boardRect, FALSE);
\r
5500 GetClientRect(hwnd, &client);
\r
5501 ResizeBoard(client.right, client.bottom, lastSizing);
\r
5503 if(appData.debugMode) { fprintf(debugFP, "square size = %d\n", squareSize); }
\r
5505 return OnExitSizeMove( &sd, hwnd, wParam, lParam );
\r
5508 case WM_DESTROY: /* message: window being destroyed */
\r
5509 PostQuitMessage(0);
\r
5513 if (hwnd == hwndMain) {
\r
5518 default: /* Passes it on if unprocessed */
\r
5519 return (DefWindowProc(hwnd, message, wParam, lParam));
\r
5524 /*---------------------------------------------------------------------------*\
\r
5526 * Misc utility routines
\r
5528 \*---------------------------------------------------------------------------*/
\r
5531 * Decent random number generator, at least not as bad as Windows
\r
5532 * standard rand, which returns a value in the range 0 to 0x7fff.
\r
5534 unsigned int randstate;
\r
5539 randstate = randstate * 1664525 + 1013904223;
\r
5540 return (int) randstate & 0x7fffffff;
\r
5544 mysrandom(unsigned int seed)
\r
5551 * returns TRUE if user selects a different color, FALSE otherwise
\r
5555 ChangeColor(HWND hwnd, COLORREF *which)
\r
5557 static BOOL firstTime = TRUE;
\r
5558 static DWORD customColors[16];
\r
5560 COLORREF newcolor;
\r
5565 /* Make initial colors in use available as custom colors */
\r
5566 /* Should we put the compiled-in defaults here instead? */
\r
5568 customColors[i++] = lightSquareColor & 0xffffff;
\r
5569 customColors[i++] = darkSquareColor & 0xffffff;
\r
5570 customColors[i++] = whitePieceColor & 0xffffff;
\r
5571 customColors[i++] = blackPieceColor & 0xffffff;
\r
5572 customColors[i++] = highlightSquareColor & 0xffffff;
\r
5573 customColors[i++] = premoveHighlightColor & 0xffffff;
\r
5575 for (ccl = (ColorClass) 0; ccl < NColorClasses && i < 16; ccl++) {
\r
5576 customColors[i++] = textAttribs[ccl].color;
\r
5578 while (i < 16) customColors[i++] = RGB(255, 255, 255);
\r
5579 firstTime = FALSE;
\r
5582 cc.lStructSize = sizeof(cc);
\r
5583 cc.hwndOwner = hwnd;
\r
5584 cc.hInstance = NULL;
\r
5585 cc.rgbResult = (DWORD) (*which & 0xffffff);
\r
5586 cc.lpCustColors = (LPDWORD) customColors;
\r
5587 cc.Flags = CC_RGBINIT|CC_FULLOPEN;
\r
5589 if (!ChooseColor(&cc)) return FALSE;
\r
5591 newcolor = (COLORREF) (0x2000000 | cc.rgbResult);
\r
5592 if (newcolor == *which) return FALSE;
\r
5593 *which = newcolor;
\r
5597 InitDrawingColors();
\r
5598 InvalidateRect(hwnd, &boardRect, FALSE);
\r
5603 MyLoadSound(MySound *ms)
\r
5609 if (ms->data) free(ms->data);
\r
5612 switch (ms->name[0]) {
\r
5618 /* System sound from Control Panel. Don't preload here. */
\r
5622 if (ms->name[1] == NULLCHAR) {
\r
5623 /* "!" alone = silence */
\r
5626 /* Builtin wave resource. Error if not found. */
\r
5627 HANDLE h = FindResource(hInst, ms->name + 1, "WAVE");
\r
5628 if (h == NULL) break;
\r
5629 ms->data = (void *)LoadResource(hInst, h);
\r
5630 if (h == NULL) break;
\r
5635 /* .wav file. Error if not found. */
\r
5636 f = fopen(ms->name, "rb");
\r
5637 if (f == NULL) break;
\r
5638 if (fstat(fileno(f), &st) < 0) break;
\r
5639 ms->data = malloc(st.st_size);
\r
5640 if (fread(ms->data, st.st_size, 1, f) < 1) break;
\r
5646 char buf[MSG_SIZ];
\r
5647 snprintf(buf, MSG_SIZ, _("Error loading sound %s"), ms->name);
\r
5648 DisplayError(buf, GetLastError());
\r
5654 MyPlaySound(MySound *ms)
\r
5656 BOOLEAN ok = FALSE;
\r
5658 if(mute) return TRUE; // [HGM] mute: suppress all sound play when muted
\r
5659 switch (ms->name[0]) {
\r
5661 if(appData.debugMode) fprintf(debugFP, "silence\n");
\r
5666 /* System sound from Control Panel (deprecated feature).
\r
5667 "$" alone or an unset sound name gets default beep (still in use). */
\r
5668 if (ms->name[1]) {
\r
5669 ok = PlaySound(ms->name + 1, NULL, SND_ALIAS|SND_ASYNC);
\r
5671 if (!ok) ok = MessageBeep(MB_OK);
\r
5674 /* Builtin wave resource, or "!" alone for silence */
\r
5675 if (ms->name[1]) {
\r
5676 if (ms->data == NULL) return FALSE;
\r
5677 ok = PlaySound(ms->data, NULL, SND_MEMORY|SND_ASYNC);
\r
5683 /* .wav file. Error if not found. */
\r
5684 if (ms->data == NULL) return FALSE;
\r
5685 ok = PlaySound(ms->data, NULL, SND_MEMORY|SND_ASYNC);
\r
5688 /* Don't print an error: this can happen innocently if the sound driver
\r
5689 is busy; for instance, if another instance of WinBoard is playing
\r
5690 a sound at about the same time. */
\r
5696 OldOpenFileHook(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
5699 OPENFILENAME *ofn;
\r
5700 static UINT *number; /* gross that this is static */
\r
5702 switch (message) {
\r
5703 case WM_INITDIALOG: /* message: initialize dialog box */
\r
5704 /* Center the dialog over the application window */
\r
5705 ofn = (OPENFILENAME *) lParam;
\r
5706 if (ofn->Flags & OFN_ENABLETEMPLATE) {
\r
5707 number = (UINT *) ofn->lCustData;
\r
5708 SendMessage(GetDlgItem(hDlg, edt2), WM_SETTEXT, 0, (LPARAM) "");
\r
5712 CenterWindow (hDlg, GetWindow (hDlg, GW_OWNER));
\r
5713 Translate(hDlg, 1536);
\r
5714 return FALSE; /* Allow for further processing */
\r
5717 if ((LOWORD(wParam) == IDOK) && (number != NULL)) {
\r
5718 *number = GetDlgItemInt(hDlg, OPT_IndexNumberOld, &ok, FALSE);
\r
5720 return FALSE; /* Allow for further processing */
\r
5726 OpenFileHook(HWND hdlg, UINT uiMsg, WPARAM wParam, LPARAM lParam)
\r
5728 static UINT *number;
\r
5729 OPENFILENAME *ofname;
\r
5732 case WM_INITDIALOG:
\r
5733 Translate(hdlg, DLG_IndexNumber);
\r
5734 ofname = (OPENFILENAME *)lParam;
\r
5735 number = (UINT *)(ofname->lCustData);
\r
5738 ofnot = (OFNOTIFY *)lParam;
\r
5739 if (ofnot->hdr.code == CDN_FILEOK) {
\r
5740 *number = GetDlgItemInt(hdlg, OPT_IndexNumber, NULL, FALSE);
\r
5749 OpenFileDialog(HWND hwnd, char *write, char *defName, char *defExt, // [HGM] diag: type of 'write' now string
\r
5750 char *nameFilt, char *dlgTitle, UINT *number,
\r
5751 char fileTitle[MSG_SIZ], char fileName[MSG_SIZ])
\r
5753 OPENFILENAME openFileName;
\r
5754 char buf1[MSG_SIZ];
\r
5757 if (fileName == NULL) fileName = buf1;
\r
5758 if (defName == NULL) {
\r
5759 safeStrCpy(fileName, "*.", 3 );
\r
5760 strcat(fileName, defExt);
\r
5762 safeStrCpy(fileName, defName, MSG_SIZ );
\r
5764 if (fileTitle) safeStrCpy(fileTitle, "", MSG_SIZ );
\r
5765 if (number) *number = 0;
\r
5767 openFileName.lStructSize = sizeof(OPENFILENAME);
\r
5768 openFileName.hwndOwner = hwnd;
\r
5769 openFileName.hInstance = (HANDLE) hInst;
\r
5770 openFileName.lpstrFilter = nameFilt;
\r
5771 openFileName.lpstrCustomFilter = (LPSTR) NULL;
\r
5772 openFileName.nMaxCustFilter = 0L;
\r
5773 openFileName.nFilterIndex = 1L;
\r
5774 openFileName.lpstrFile = fileName;
\r
5775 openFileName.nMaxFile = MSG_SIZ;
\r
5776 openFileName.lpstrFileTitle = fileTitle;
\r
5777 openFileName.nMaxFileTitle = fileTitle ? MSG_SIZ : 0;
\r
5778 openFileName.lpstrInitialDir = NULL;
\r
5779 openFileName.lpstrTitle = dlgTitle;
\r
5780 openFileName.Flags = OFN_PATHMUSTEXIST | OFN_HIDEREADONLY
\r
5781 | (write[0] != 'r' ? 0 : OFN_FILEMUSTEXIST)
\r
5782 | (number ? OFN_ENABLETEMPLATE | OFN_ENABLEHOOK: 0)
\r
5783 | (oldDialog ? 0 : OFN_EXPLORER);
\r
5784 openFileName.nFileOffset = 0;
\r
5785 openFileName.nFileExtension = 0;
\r
5786 openFileName.lpstrDefExt = defExt;
\r
5787 openFileName.lCustData = (LONG) number;
\r
5788 openFileName.lpfnHook = oldDialog ?
\r
5789 (LPOFNHOOKPROC) OldOpenFileHook : (LPOFNHOOKPROC) OpenFileHook;
\r
5790 openFileName.lpTemplateName = (LPSTR)(oldDialog ? 1536 : DLG_IndexNumber);
\r
5792 if (write[0] != 'r' ? GetSaveFileName(&openFileName) :
\r
5793 GetOpenFileName(&openFileName)) {
\r
5794 /* open the file */
\r
5795 f = fopen(openFileName.lpstrFile, write);
\r
5797 MessageBox(hwnd, _("File open failed"), NULL,
\r
5798 MB_OK|MB_ICONEXCLAMATION);
\r
5802 int err = CommDlgExtendedError();
\r
5803 if (err != 0) DisplayError(_("Internal error in file dialog box"), err);
\r
5812 MenuPopup(HWND hwnd, POINT pt, HMENU hmenu, UINT def)
\r
5814 HMENU hmenuTrackPopup; /* floating pop-up menu */
\r
5817 * Get the first pop-up menu in the menu template. This is the
\r
5818 * menu that TrackPopupMenu displays.
\r
5820 hmenuTrackPopup = GetSubMenu(hmenu, 0);
\r
5821 TranslateOneMenu(10, hmenuTrackPopup);
\r
5823 SetMenuDefaultItem(hmenuTrackPopup, def, FALSE);
\r
5826 * TrackPopup uses screen coordinates, so convert the
\r
5827 * coordinates of the mouse click to screen coordinates.
\r
5829 ClientToScreen(hwnd, (LPPOINT) &pt);
\r
5831 /* Draw and track the floating pop-up menu. */
\r
5832 TrackPopupMenu(hmenuTrackPopup, TPM_CENTERALIGN | TPM_RIGHTBUTTON,
\r
5833 pt.x, pt.y, 0, hwnd, NULL);
\r
5835 /* Destroy the menu.*/
\r
5836 DestroyMenu(hmenu);
\r
5841 int sizeX, sizeY, newSizeX, newSizeY;
\r
5843 } ResizeEditPlusButtonsClosure;
\r
5846 ResizeEditPlusButtonsCallback(HWND hChild, LPARAM lparam)
\r
5848 ResizeEditPlusButtonsClosure *cl = (ResizeEditPlusButtonsClosure *)lparam;
\r
5852 if (hChild == cl->hText) return TRUE;
\r
5853 GetWindowRect(hChild, &rect); /* gives screen coords */
\r
5854 pt.x = rect.left + (cl->newSizeX - cl->sizeX)/2;
\r
5855 pt.y = rect.top + cl->newSizeY - cl->sizeY;
\r
5856 ScreenToClient(cl->hDlg, &pt);
\r
5857 cl->hdwp = DeferWindowPos(cl->hdwp, hChild, NULL,
\r
5858 pt.x, pt.y, rect.right - rect.left, rect.bottom - rect.top, SWP_NOZORDER);
\r
5862 /* Resize a dialog that has a (rich) edit field filling most of
\r
5863 the top, with a row of buttons below */
\r
5865 ResizeEditPlusButtons(HWND hDlg, HWND hText, int sizeX, int sizeY, int newSizeX, int newSizeY)
\r
5868 int newTextHeight, newTextWidth;
\r
5869 ResizeEditPlusButtonsClosure cl;
\r
5871 /*if (IsIconic(hDlg)) return;*/
\r
5872 if (newSizeX == sizeX && newSizeY == sizeY) return;
\r
5874 cl.hdwp = BeginDeferWindowPos(8);
\r
5876 GetWindowRect(hText, &rectText); /* gives screen coords */
\r
5877 newTextWidth = rectText.right - rectText.left + newSizeX - sizeX;
\r
5878 newTextHeight = rectText.bottom - rectText.top + newSizeY - sizeY;
\r
5879 if (newTextHeight < 0) {
\r
5880 newSizeY += -newTextHeight;
\r
5881 newTextHeight = 0;
\r
5883 cl.hdwp = DeferWindowPos(cl.hdwp, hText, NULL, 0, 0,
\r
5884 newTextWidth, newTextHeight, SWP_NOZORDER|SWP_NOMOVE);
\r
5890 cl.newSizeX = newSizeX;
\r
5891 cl.newSizeY = newSizeY;
\r
5892 EnumChildWindows(hDlg, ResizeEditPlusButtonsCallback, (LPARAM)&cl);
\r
5894 EndDeferWindowPos(cl.hdwp);
\r
5897 BOOL CenterWindowEx(HWND hwndChild, HWND hwndParent, int mode)
\r
5899 RECT rChild, rParent;
\r
5900 int wChild, hChild, wParent, hParent;
\r
5901 int wScreen, hScreen, xNew, yNew;
\r
5904 /* Get the Height and Width of the child window */
\r
5905 GetWindowRect (hwndChild, &rChild);
\r
5906 wChild = rChild.right - rChild.left;
\r
5907 hChild = rChild.bottom - rChild.top;
\r
5909 /* Get the Height and Width of the parent window */
\r
5910 GetWindowRect (hwndParent, &rParent);
\r
5911 wParent = rParent.right - rParent.left;
\r
5912 hParent = rParent.bottom - rParent.top;
\r
5914 /* Get the display limits */
\r
5915 hdc = GetDC (hwndChild);
\r
5916 wScreen = GetDeviceCaps (hdc, HORZRES);
\r
5917 hScreen = GetDeviceCaps (hdc, VERTRES);
\r
5918 ReleaseDC(hwndChild, hdc);
\r
5920 /* Calculate new X position, then adjust for screen */
\r
5921 xNew = rParent.left + ((wParent - wChild) /2);
\r
5924 } else if ((xNew+wChild) > wScreen) {
\r
5925 xNew = wScreen - wChild;
\r
5928 /* Calculate new Y position, then adjust for screen */
\r
5930 yNew = rParent.top + ((hParent - hChild) /2);
\r
5933 yNew = rParent.top + GetSystemMetrics( SM_CYCAPTION ) * 2 / 3;
\r
5938 } else if ((yNew+hChild) > hScreen) {
\r
5939 yNew = hScreen - hChild;
\r
5942 /* Set it, and return */
\r
5943 return SetWindowPos (hwndChild, NULL,
\r
5944 xNew, yNew, 0, 0, SWP_NOSIZE | SWP_NOZORDER);
\r
5947 /* Center one window over another */
\r
5948 BOOL CenterWindow (HWND hwndChild, HWND hwndParent)
\r
5950 return CenterWindowEx( hwndChild, hwndParent, 0 );
\r
5953 /*---------------------------------------------------------------------------*\
\r
5955 * Startup Dialog functions
\r
5957 \*---------------------------------------------------------------------------*/
\r
5959 InitComboStrings(HANDLE hwndCombo, char **cd)
\r
5961 SendMessage(hwndCombo, CB_RESETCONTENT, 0, 0);
\r
5963 while (*cd != NULL) {
\r
5964 SendMessage(hwndCombo, CB_ADDSTRING, 0, (LPARAM) T_(*cd));
\r
5970 InitComboStringsFromOption(HANDLE hwndCombo, char *str)
\r
5972 char buf1[MAX_ARG_LEN];
\r
5975 if (str[0] == '@') {
\r
5976 FILE* f = fopen(str + 1, "r");
\r
5978 DisplayFatalError(str + 1, errno, 2);
\r
5981 len = fread(buf1, 1, sizeof(buf1)-1, f);
\r
5983 buf1[len] = NULLCHAR;
\r
5987 SendMessage(hwndCombo, CB_RESETCONTENT, 0, 0);
\r
5990 char buf[MSG_SIZ];
\r
5991 char *end = strchr(str, '\n');
\r
5992 if (end == NULL) return;
\r
5993 memcpy(buf, str, end - str);
\r
5994 buf[end - str] = NULLCHAR;
\r
5995 SendMessage(hwndCombo, CB_ADDSTRING, 0, (LPARAM) buf);
\r
6001 SetStartupDialogEnables(HWND hDlg)
\r
6003 EnableWindow(GetDlgItem(hDlg, OPT_ChessEngineName),
\r
6004 IsDlgButtonChecked(hDlg, OPT_ChessEngine) ||
\r
6005 (appData.zippyPlay && IsDlgButtonChecked(hDlg, OPT_ChessServer)));
\r
6006 EnableWindow(GetDlgItem(hDlg, OPT_SecondChessEngineName),
\r
6007 IsDlgButtonChecked(hDlg, OPT_ChessEngine));
\r
6008 EnableWindow(GetDlgItem(hDlg, OPT_ChessServerName),
\r
6009 IsDlgButtonChecked(hDlg, OPT_ChessServer));
\r
6010 EnableWindow(GetDlgItem(hDlg, OPT_AdditionalOptions),
\r
6011 IsDlgButtonChecked(hDlg, OPT_AnyAdditional));
\r
6012 EnableWindow(GetDlgItem(hDlg, IDOK),
\r
6013 IsDlgButtonChecked(hDlg, OPT_ChessEngine) ||
\r
6014 IsDlgButtonChecked(hDlg, OPT_ChessServer) ||
\r
6015 IsDlgButtonChecked(hDlg, OPT_View));
\r
6019 QuoteForFilename(char *filename)
\r
6021 int dquote, space;
\r
6022 dquote = strchr(filename, '"') != NULL;
\r
6023 space = strchr(filename, ' ') != NULL;
\r
6024 if (dquote || space) {
\r
6036 InitEngineBox(HWND hDlg, HWND hwndCombo, char* nthcp, char* nthd, char* nthdir, char *nthnames)
\r
6038 char buf[MSG_SIZ];
\r
6041 InitComboStringsFromOption(hwndCombo, nthnames);
\r
6042 q = QuoteForFilename(nthcp);
\r
6043 snprintf(buf, MSG_SIZ, "%s%s%s", q, nthcp, q);
\r
6044 if (*nthdir != NULLCHAR) {
\r
6045 q = QuoteForFilename(nthdir);
\r
6046 snprintf(buf + strlen(buf), MSG_SIZ, " /%s=%s%s%s", nthd, q, nthdir, q);
\r
6048 if (*nthcp == NULLCHAR) {
\r
6049 SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) 0, (LPARAM) 0);
\r
6050 } else if (SendMessage(hwndCombo, CB_SELECTSTRING, (WPARAM) -1, (LPARAM) buf) == CB_ERR) {
\r
6051 SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) -1, (LPARAM) 0);
\r
6052 SendMessage(hwndCombo, WM_SETTEXT, (WPARAM) 0, (LPARAM) buf);
\r
6057 StartupDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
6059 char buf[MSG_SIZ];
\r
6063 switch (message) {
\r
6064 case WM_INITDIALOG:
\r
6065 /* Center the dialog */
\r
6066 CenterWindow (hDlg, GetDesktopWindow());
\r
6067 Translate(hDlg, DLG_Startup);
\r
6068 /* Initialize the dialog items */
\r
6069 InitEngineBox(hDlg, GetDlgItem(hDlg, OPT_ChessEngineName),
\r
6070 appData.firstChessProgram, "fd", appData.firstDirectory,
\r
6071 firstChessProgramNames);
\r
6072 InitEngineBox(hDlg, GetDlgItem(hDlg, OPT_SecondChessEngineName),
\r
6073 appData.secondChessProgram, "sd", appData.secondDirectory,
\r
6074 secondChessProgramNames);
\r
6075 hwndCombo = GetDlgItem(hDlg, OPT_ChessServerName);
\r
6076 InitComboStringsFromOption(hwndCombo, icsNames);
\r
6077 snprintf(buf, MSG_SIZ, "%s /icsport=%s", appData.icsHost, appData.icsPort);
\r
6078 if (*appData.icsHelper != NULLCHAR) {
\r
6079 char *q = QuoteForFilename(appData.icsHelper);
\r
6080 sprintf(buf + strlen(buf), " /icshelper=%s%s%s", q, appData.icsHelper, q);
\r
6082 if (*appData.icsHost == NULLCHAR) {
\r
6083 SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) 0, (LPARAM) 0);
\r
6084 /*SendMessage(hwndCombo, CB_SHOWDROPDOWN, (WPARAM) TRUE, (LPARAM) 0); !!too soon */
\r
6085 } else if (SendMessage(hwndCombo, CB_SELECTSTRING, (WPARAM) -1, (LPARAM) buf) == CB_ERR) {
\r
6086 SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) -1, (LPARAM) 0);
\r
6087 SendMessage(hwndCombo, WM_SETTEXT, (WPARAM) 0, (LPARAM) buf);
\r
6090 if (appData.icsActive) {
\r
6091 CheckDlgButton(hDlg, OPT_ChessServer, BST_CHECKED);
\r
6093 else if (appData.noChessProgram) {
\r
6094 CheckDlgButton(hDlg, OPT_View, BST_CHECKED);
\r
6097 CheckDlgButton(hDlg, OPT_ChessEngine, BST_CHECKED);
\r
6100 SetStartupDialogEnables(hDlg);
\r
6104 switch (LOWORD(wParam)) {
\r
6106 if (IsDlgButtonChecked(hDlg, OPT_ChessEngine)) {
\r
6107 safeStrCpy(buf, "/fcp=", sizeof(buf)/sizeof(buf[0]) );
\r
6108 GetDlgItemText(hDlg, OPT_ChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));
\r
6110 ParseArgs(StringGet, &p);
\r
6111 safeStrCpy(buf, "/scp=", sizeof(buf)/sizeof(buf[0]) );
\r
6112 GetDlgItemText(hDlg, OPT_SecondChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));
\r
6114 ParseArgs(StringGet, &p);
\r
6115 appData.noChessProgram = FALSE;
\r
6116 appData.icsActive = FALSE;
\r
6117 } else if (IsDlgButtonChecked(hDlg, OPT_ChessServer)) {
\r
6118 safeStrCpy(buf, "/ics /icshost=", sizeof(buf)/sizeof(buf[0]) );
\r
6119 GetDlgItemText(hDlg, OPT_ChessServerName, buf + strlen(buf), sizeof(buf) - strlen(buf));
\r
6121 ParseArgs(StringGet, &p);
\r
6122 if (appData.zippyPlay) {
\r
6123 safeStrCpy(buf, "/fcp=", sizeof(buf)/sizeof(buf[0]) );
\r
6124 GetDlgItemText(hDlg, OPT_ChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));
\r
6126 ParseArgs(StringGet, &p);
\r
6128 } else if (IsDlgButtonChecked(hDlg, OPT_View)) {
\r
6129 appData.noChessProgram = TRUE;
\r
6130 appData.icsActive = FALSE;
\r
6132 MessageBox(hDlg, _("Choose an option, or cancel to exit"),
\r
6133 _("Option Error"), MB_OK|MB_ICONEXCLAMATION);
\r
6136 if (IsDlgButtonChecked(hDlg, OPT_AnyAdditional)) {
\r
6137 GetDlgItemText(hDlg, OPT_AdditionalOptions, buf, sizeof(buf));
\r
6139 ParseArgs(StringGet, &p);
\r
6141 EndDialog(hDlg, TRUE);
\r
6148 case IDM_HELPCONTENTS:
\r
6149 if (!WinHelp (hDlg, "winboard.hlp", HELP_KEY,(DWORD)(LPSTR)"CONTENTS")) {
\r
6150 MessageBox (GetFocus(),
\r
6151 _("Unable to activate help"),
\r
6152 szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);
\r
6157 SetStartupDialogEnables(hDlg);
\r
6165 /*---------------------------------------------------------------------------*\
\r
6167 * About box dialog functions
\r
6169 \*---------------------------------------------------------------------------*/
\r
6171 /* Process messages for "About" dialog box */
\r
6173 About(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
6175 switch (message) {
\r
6176 case WM_INITDIALOG: /* message: initialize dialog box */
\r
6177 /* Center the dialog over the application window */
\r
6178 CenterWindow (hDlg, GetWindow (hDlg, GW_OWNER));
\r
6179 SetDlgItemText(hDlg, ABOUTBOX_Version, programVersion);
\r
6180 Translate(hDlg, ABOUTBOX);
\r
6184 case WM_COMMAND: /* message: received a command */
\r
6185 if (LOWORD(wParam) == IDOK /* "OK" box selected? */
\r
6186 || LOWORD(wParam) == IDCANCEL) { /* System menu close command? */
\r
6187 EndDialog(hDlg, TRUE); /* Exit the dialog */
\r
6195 /*---------------------------------------------------------------------------*\
\r
6197 * Comment Dialog functions
\r
6199 \*---------------------------------------------------------------------------*/
\r
6202 CommentDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
6204 static HANDLE hwndText = NULL;
\r
6205 int len, newSizeX, newSizeY, flags;
\r
6206 static int sizeX, sizeY;
\r
6211 switch (message) {
\r
6212 case WM_INITDIALOG: /* message: initialize dialog box */
\r
6213 /* Initialize the dialog items */
\r
6214 Translate(hDlg, DLG_EditComment);
\r
6215 hwndText = GetDlgItem(hDlg, OPT_CommentText);
\r
6216 SetDlgItemText(hDlg, OPT_CommentText, commentText);
\r
6217 EnableWindow(GetDlgItem(hDlg, OPT_CancelComment), editComment);
\r
6218 EnableWindow(GetDlgItem(hDlg, OPT_ClearComment), editComment);
\r
6219 EnableWindow(GetDlgItem(hDlg, OPT_EditComment), !editComment);
\r
6220 SendMessage(hwndText, EM_SETREADONLY, !editComment, 0);
\r
6221 SetWindowText(hDlg, commentTitle);
\r
6222 if (editComment) {
\r
6223 SetFocus(hwndText);
\r
6225 SetFocus(GetDlgItem(hDlg, IDOK));
\r
6227 SendMessage(GetDlgItem(hDlg, OPT_CommentText),
\r
6228 WM_SETFONT, (WPARAM)font[boardSize][COMMENT_FONT]->hf,
\r
6229 MAKELPARAM(FALSE, 0));
\r
6230 /* Size and position the dialog */
\r
6231 if (!commentDialog) {
\r
6232 commentDialog = hDlg;
\r
6233 flags = SWP_NOZORDER;
\r
6234 GetClientRect(hDlg, &rect);
\r
6235 sizeX = rect.right;
\r
6236 sizeY = rect.bottom;
\r
6237 if (wpComment.x != CW_USEDEFAULT && wpComment.y != CW_USEDEFAULT &&
\r
6238 wpComment.width != CW_USEDEFAULT && wpComment.height != CW_USEDEFAULT) {
\r
6239 WINDOWPLACEMENT wp;
\r
6240 EnsureOnScreen(&wpComment.x, &wpComment.y, 0, 0);
\r
6241 wp.length = sizeof(WINDOWPLACEMENT);
\r
6243 wp.showCmd = SW_SHOW;
\r
6244 wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;
\r
6245 wp.rcNormalPosition.left = wpComment.x;
\r
6246 wp.rcNormalPosition.right = wpComment.x + wpComment.width;
\r
6247 wp.rcNormalPosition.top = wpComment.y;
\r
6248 wp.rcNormalPosition.bottom = wpComment.y + wpComment.height;
\r
6249 SetWindowPlacement(hDlg, &wp);
\r
6251 GetClientRect(hDlg, &rect);
\r
6252 newSizeX = rect.right;
\r
6253 newSizeY = rect.bottom;
\r
6254 ResizeEditPlusButtons(hDlg, hwndText, sizeX, sizeY,
\r
6255 newSizeX, newSizeY);
\r
6260 SendDlgItemMessage( hDlg, OPT_CommentText, EM_SETEVENTMASK, 0, ENM_MOUSEEVENTS | ENM_KEYEVENTS );
\r
6263 case WM_COMMAND: /* message: received a command */
\r
6264 switch (LOWORD(wParam)) {
\r
6266 if (editComment) {
\r
6268 /* Read changed options from the dialog box */
\r
6269 hwndText = GetDlgItem(hDlg, OPT_CommentText);
\r
6270 len = GetWindowTextLength(hwndText);
\r
6271 str = (char *) malloc(len + 1);
\r
6272 GetWindowText(hwndText, str, len + 1);
\r
6281 ReplaceComment(commentIndex, str);
\r
6288 case OPT_CancelComment:
\r
6292 case OPT_ClearComment:
\r
6293 SetDlgItemText(hDlg, OPT_CommentText, "");
\r
6296 case OPT_EditComment:
\r
6297 EditCommentEvent();
\r
6305 case WM_NOTIFY: // [HGM] vari: cloned from whistory.c
\r
6306 if( wParam == OPT_CommentText ) {
\r
6307 MSGFILTER * lpMF = (MSGFILTER *) lParam;
\r
6309 if( lpMF->msg == WM_RBUTTONDOWN && (lpMF->wParam & (MK_CONTROL | MK_SHIFT)) == 0 ||
\r
6310 lpMF->msg == WM_CHAR && lpMF->wParam == '\022' ) {
\r
6314 pt.x = LOWORD( lpMF->lParam );
\r
6315 pt.y = HIWORD( lpMF->lParam );
\r
6317 if(lpMF->msg == WM_CHAR) {
\r
6319 SendDlgItemMessage( hDlg, OPT_CommentText, EM_EXGETSEL, 0, (LPARAM) &sel );
\r
6320 index = sel.cpMin;
\r
6322 index = SendDlgItemMessage( hDlg, OPT_CommentText, EM_CHARFROMPOS, 0, (LPARAM) &pt );
\r
6324 hwndText = GetDlgItem(hDlg, OPT_CommentText); // cloned from above
\r
6325 len = GetWindowTextLength(hwndText);
\r
6326 str = (char *) malloc(len + 1);
\r
6327 GetWindowText(hwndText, str, len + 1);
\r
6328 ReplaceComment(commentIndex, str);
\r
6329 if(commentIndex != currentMove) ToNrEvent(commentIndex);
\r
6330 LoadVariation( index, str ); // [HGM] also does the actual moving to it, now
\r
6333 /* Zap the message for good: apparently, returning non-zero is not enough */
\r
6334 lpMF->msg = WM_USER;
\r
6342 newSizeX = LOWORD(lParam);
\r
6343 newSizeY = HIWORD(lParam);
\r
6344 ResizeEditPlusButtons(hDlg, hwndText, sizeX, sizeY, newSizeX, newSizeY);
\r
6349 case WM_GETMINMAXINFO:
\r
6350 /* Prevent resizing window too small */
\r
6351 mmi = (MINMAXINFO *) lParam;
\r
6352 mmi->ptMinTrackSize.x = 100;
\r
6353 mmi->ptMinTrackSize.y = 100;
\r
6360 EitherCommentPopUp(int index, char *title, char *str, BOOLEAN edit)
\r
6365 CheckMenuItem(GetMenu(hwndMain), IDM_Comment, edit ? MF_CHECKED : MF_UNCHECKED);
\r
6367 if (str == NULL) str = "";
\r
6368 p = (char *) malloc(2 * strlen(str) + 2);
\r
6371 if (*str == '\n') *q++ = '\r';
\r
6375 if (commentText != NULL) free(commentText);
\r
6377 commentIndex = index;
\r
6378 commentTitle = title;
\r
6380 editComment = edit;
\r
6382 if (commentDialog) {
\r
6383 SendMessage(commentDialog, WM_INITDIALOG, 0, 0);
\r
6384 if (!commentUp) ShowWindow(commentDialog, SW_SHOW);
\r
6386 lpProc = MakeProcInstance((FARPROC)CommentDialog, hInst);
\r
6387 CreateDialog(hInst, MAKEINTRESOURCE(DLG_EditComment),
\r
6388 hwndMain, (DLGPROC)lpProc);
\r
6389 FreeProcInstance(lpProc);
\r
6395 /*---------------------------------------------------------------------------*\
\r
6397 * Type-in move dialog functions
\r
6399 \*---------------------------------------------------------------------------*/
\r
6402 TypeInMoveDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
6404 char move[MSG_SIZ];
\r
6407 switch (message) {
\r
6408 case WM_INITDIALOG:
\r
6409 move[0] = (char) lParam;
\r
6410 move[1] = NULLCHAR;
\r
6411 CenterWindowEx(hDlg, GetWindow(hDlg, GW_OWNER), 1 );
\r
6412 Translate(hDlg, DLG_TypeInMove);
\r
6413 hInput = GetDlgItem(hDlg, OPT_Move);
\r
6414 SetWindowText(hInput, move);
\r
6416 SendMessage(hInput, EM_SETSEL, (WPARAM)9999, (LPARAM)9999);
\r
6420 switch (LOWORD(wParam)) {
\r
6423 shiftKey = GetKeyState(VK_SHIFT) < 0; // [HGM] remember last shift status
\r
6424 GetDlgItemText(hDlg, OPT_Move, move, sizeof(move));
6425 TypeInDoneEvent(move);
\r
6426 EndDialog(hDlg, TRUE);
\r
6429 EndDialog(hDlg, FALSE);
\r
6440 PopUpMoveDialog(char firstchar)
\r
6444 lpProc = MakeProcInstance((FARPROC)TypeInMoveDialog, hInst);
\r
6445 DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_TypeInMove),
\r
6446 hwndMain, (DLGPROC)lpProc, (LPARAM)firstchar);
\r
6447 FreeProcInstance(lpProc);
\r
6450 /*---------------------------------------------------------------------------*\
\r
6452 * Type-in name dialog functions
\r
6454 \*---------------------------------------------------------------------------*/
\r
6457 TypeInNameDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
6459 char move[MSG_SIZ];
\r
6462 switch (message) {
\r
6463 case WM_INITDIALOG:
\r
6464 move[0] = (char) lParam;
\r
6465 move[1] = NULLCHAR;
\r
6466 CenterWindowEx(hDlg, GetWindow(hDlg, GW_OWNER), 1 );
\r
6467 Translate(hDlg, DLG_TypeInName);
\r
6468 hInput = GetDlgItem(hDlg, OPT_Name);
\r
6469 SetWindowText(hInput, move);
\r
6471 SendMessage(hInput, EM_SETSEL, (WPARAM)9999, (LPARAM)9999);
\r
6475 switch (LOWORD(wParam)) {
\r
6477 GetDlgItemText(hDlg, OPT_Name, move, sizeof(move));
\r
6478 appData.userName = strdup(move);
\r
6481 if(gameMode == MachinePlaysWhite || gameMode == MachinePlaysBlack) {
\r
6482 snprintf(move, MSG_SIZ, "%s vs. %s", gameInfo.white, gameInfo.black);
\r
6483 DisplayTitle(move);
\r
6487 EndDialog(hDlg, TRUE);
\r
6490 EndDialog(hDlg, FALSE);
\r
6501 PopUpNameDialog(char firstchar)
\r
6505 lpProc = MakeProcInstance((FARPROC)TypeInNameDialog, hInst);
\r
6506 DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_TypeInName),
\r
6507 hwndMain, (DLGPROC)lpProc, (LPARAM)firstchar);
\r
6508 FreeProcInstance(lpProc);
\r
6511 /*---------------------------------------------------------------------------*\
\r
6515 \*---------------------------------------------------------------------------*/
\r
6517 /* Nonmodal error box */
\r
6518 LRESULT CALLBACK ErrorDialog(HWND hDlg, UINT message,
\r
6519 WPARAM wParam, LPARAM lParam);
\r
6522 ErrorPopUp(char *title, char *content)
\r
6526 BOOLEAN modal = hwndMain == NULL;
\r
6544 strncpy(errorTitle, title, sizeof(errorTitle));
\r
6545 errorTitle[sizeof(errorTitle) - 1] = '\0';
\r
6548 MessageBox(NULL, errorMessage, errorTitle, MB_OK|MB_ICONEXCLAMATION);
\r
6550 lpProc = MakeProcInstance((FARPROC)ErrorDialog, hInst);
\r
6551 CreateDialog(hInst, MAKEINTRESOURCE(DLG_Error),
\r
6552 hwndMain, (DLGPROC)lpProc);
\r
6553 FreeProcInstance(lpProc);
\r
6560 if (!appData.popupMoveErrors && moveErrorMessageUp) DisplayMessage("", "");
\r
6561 if (errorDialog == NULL) return;
\r
6562 DestroyWindow(errorDialog);
\r
6563 errorDialog = NULL;
\r
6564 if(errorExitStatus) ExitEvent(errorExitStatus);
\r
6568 ErrorDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
6573 switch (message) {
\r
6574 case WM_INITDIALOG:
\r
6575 GetWindowRect(hDlg, &rChild);
\r
6578 SetWindowPos(hDlg, NULL, rChild.left,
\r
6579 rChild.top + boardRect.top - (rChild.bottom - rChild.top),
\r
6580 0, 0, SWP_NOZORDER|SWP_NOSIZE);
\r
6584 [AS] It seems that the above code wants to move the dialog up in the "caption
\r
6585 area" of the main window, but it uses the dialog height as an hard-coded constant,
\r
6586 and it doesn't work when you resize the dialog.
\r
6587 For now, just give it a default position.
\r
6589 SetWindowPos(hDlg, NULL, boardRect.left+8, boardRect.top+8, 0, 0, SWP_NOZORDER|SWP_NOSIZE);
\r
6590 Translate(hDlg, DLG_Error);
\r
6592 errorDialog = hDlg;
\r
6593 SetWindowText(hDlg, errorTitle);
\r
6594 hwndText = GetDlgItem(hDlg, OPT_ErrorText);
\r
6595 SetDlgItemText(hDlg, OPT_ErrorText, errorMessage);
\r
6599 switch (LOWORD(wParam)) {
\r
6602 if (errorDialog == hDlg) errorDialog = NULL;
\r
6603 DestroyWindow(hDlg);
\r
6615 HWND gothicDialog = NULL;
\r
6618 GothicDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
6622 int height = GetSystemMetrics(SM_CYCAPTION)+GetSystemMetrics(SM_CYFRAME);
\r
6624 switch (message) {
\r
6625 case WM_INITDIALOG:
\r
6626 GetWindowRect(hDlg, &rChild);
\r
6628 SetWindowPos(hDlg, NULL, wpMain.x, wpMain.y-height, wpMain.width, height,
\r
6632 [AS] It seems that the above code wants to move the dialog up in the "caption
\r
6633 area" of the main window, but it uses the dialog height as an hard-coded constant,
\r
6634 and it doesn't work when you resize the dialog.
\r
6635 For now, just give it a default position.
\r
6637 gothicDialog = hDlg;
\r
6638 SetWindowText(hDlg, errorTitle);
\r
6639 hwndText = GetDlgItem(hDlg, OPT_ErrorText);
\r
6640 SetDlgItemText(hDlg, OPT_ErrorText, errorMessage);
\r
6644 switch (LOWORD(wParam)) {
\r
6647 if (errorDialog == hDlg) errorDialog = NULL;
\r
6648 DestroyWindow(hDlg);
\r
6660 GothicPopUp(char *title, VariantClass variant)
\r
6663 static char *lastTitle;
\r
6665 strncpy(errorTitle, title, sizeof(errorTitle));
\r
6666 errorTitle[sizeof(errorTitle) - 1] = '\0';
\r
6668 if(lastTitle != title && gothicDialog != NULL) {
\r
6669 DestroyWindow(gothicDialog);
\r
6670 gothicDialog = NULL;
\r
6672 if(variant != VariantNormal && gothicDialog == NULL) {
\r
6673 title = lastTitle;
\r
6674 lpProc = MakeProcInstance((FARPROC)GothicDialog, hInst);
\r
6675 CreateDialog(hInst, MAKEINTRESOURCE(DLG_Error),
\r
6676 hwndMain, (DLGPROC)lpProc);
\r
6677 FreeProcInstance(lpProc);
\r
6682 /*---------------------------------------------------------------------------*\
\r
6684 * Ics Interaction console functions
\r
6686 \*---------------------------------------------------------------------------*/
\r
6688 #define HISTORY_SIZE 64
\r
6689 static char *history[HISTORY_SIZE];
\r
6690 int histIn = 0, histP = 0;
\r
6693 SaveInHistory(char *cmd)
\r
6695 if (history[histIn] != NULL) {
\r
6696 free(history[histIn]);
\r
6697 history[histIn] = NULL;
\r
6699 if (*cmd == NULLCHAR) return;
\r
6700 history[histIn] = StrSave(cmd);
\r
6701 histIn = (histIn + 1) % HISTORY_SIZE;
\r
6702 if (history[histIn] != NULL) {
\r
6703 free(history[histIn]);
\r
6704 history[histIn] = NULL;
\r
6710 PrevInHistory(char *cmd)
\r
6713 if (histP == histIn) {
\r
6714 if (history[histIn] != NULL) free(history[histIn]);
\r
6715 history[histIn] = StrSave(cmd);
\r
6717 newhp = (histP - 1 + HISTORY_SIZE) % HISTORY_SIZE;
\r
6718 if (newhp == histIn || history[newhp] == NULL) return NULL;
\r
6720 return history[histP];
\r
6726 if (histP == histIn) return NULL;
\r
6727 histP = (histP + 1) % HISTORY_SIZE;
\r
6728 return history[histP];
\r
6732 LoadIcsTextMenu(IcsTextMenuEntry *e)
\r
6736 hmenu = LoadMenu(hInst, "TextMenu");
\r
6737 h = GetSubMenu(hmenu, 0);
\r
6739 if (strcmp(e->item, "-") == 0) {
\r
6740 AppendMenu(h, MF_SEPARATOR, 0, 0);
\r
6741 } else { // [HGM] re-written a bit to use only one AppendMenu call for both cases (| or no |)
\r
6742 int flags = MF_STRING, j = 0;
\r
6743 if (e->item[0] == '|') {
\r
6744 flags |= MF_MENUBARBREAK;
\r
6747 if(!strcmp(e->command, "none")) flags |= MF_GRAYED; // [HGM] chatclick: provide inactive dummy
\r
6748 AppendMenu(h, flags, IDM_CommandX + i, e->item + j);
\r
6756 WNDPROC consoleTextWindowProc;
\r
6759 CommandX(HWND hwnd, char *command, BOOLEAN getname, BOOLEAN immediate)
\r
6761 char buf[MSG_SIZ], name[MSG_SIZ];
\r
6762 HWND hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
6766 SetWindowText(hInput, command);
\r
6768 SendMessage(hInput, WM_CHAR, '\r', 0);
\r
6770 sel.cpMin = 999999;
\r
6771 sel.cpMax = 999999;
\r
6772 SendMessage(hInput, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
6777 SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);
\r
6778 if (sel.cpMin == sel.cpMax) {
\r
6779 /* Expand to surrounding word */
\r
6782 tr.chrg.cpMax = sel.cpMin;
\r
6783 tr.chrg.cpMin = --sel.cpMin;
\r
6784 if (sel.cpMin < 0) break;
\r
6785 tr.lpstrText = name;
\r
6786 SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr);
\r
6787 } while (isalpha(name[0]) || isdigit(name[0]) || name[0] == '-');
\r
6791 tr.chrg.cpMin = sel.cpMax;
\r
6792 tr.chrg.cpMax = ++sel.cpMax;
\r
6793 tr.lpstrText = name;
\r
6794 if (SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr) < 1) break;
\r
6795 } while (isalpha(name[0]) || isdigit(name[0]) || name[0] == '-');
\r
6798 if (sel.cpMax == sel.cpMin || sel.cpMax - sel.cpMin > MSG_SIZ/2) {
\r
6799 MessageBeep(MB_ICONEXCLAMATION);
\r
6803 tr.lpstrText = name;
\r
6804 SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr);
\r
6806 if (sel.cpMax - sel.cpMin > MSG_SIZ/2) {
\r
6807 MessageBeep(MB_ICONEXCLAMATION);
\r
6810 SendMessage(hwnd, EM_GETSELTEXT, 0, (LPARAM) name);
\r
6813 if(strstr(command, "%s")) snprintf(buf, MSG_SIZ, command, name); else
\r
6814 snprintf(buf, MSG_SIZ, "%s %s", command, name);
\r
6815 SetWindowText(hInput, buf);
\r
6816 SendMessage(hInput, WM_CHAR, '\r', 0);
\r
6818 if(!strcmp(command, "chat")) { ChatPopUp(name); return; }
\r
6819 snprintf(buf, MSG_SIZ, "%s %s ", command, name); /* trailing space */
\r
6820 SetWindowText(hInput, buf);
\r
6821 sel.cpMin = 999999;
\r
6822 sel.cpMax = 999999;
\r
6823 SendMessage(hInput, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
6829 ConsoleTextSubclass(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
\r
6834 switch (message) {
\r
6836 if (!(GetKeyState(VK_CONTROL) & ~1)) break;
\r
6837 if(wParam=='R') return 0;
\r
6840 SendMessage(hwnd, EM_LINESCROLL, 0, -999999);
\r
6843 sel.cpMin = 999999;
\r
6844 sel.cpMax = 999999;
\r
6845 SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
6846 SendMessage(hwnd, EM_SCROLLCARET, 0, 0);
\r
6851 if(wParam != '\022') {
\r
6852 if (wParam == '\t') {
\r
6853 if (GetKeyState(VK_SHIFT) < 0) {
\r
6855 if (IsIconic(hwndMain)) ShowWindow(hwndMain, SW_RESTORE);
\r
6856 if (buttonDesc[0].hwnd) {
\r
6857 SetFocus(buttonDesc[0].hwnd);
\r
6859 SetFocus(hwndMain);
\r
6863 SetFocus(GetDlgItem(hwndConsole, OPT_ConsoleInput));
\r
6866 hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
6867 JAWS_DELETE( SetFocus(hInput); )
\r
6868 SendMessage(hInput, message, wParam, lParam);
\r
6871 } // [HGM] navigate: for Ctrl+R, flow into next case (moved up here) to summon up menu
\r
6873 case WM_RBUTTONDOWN:
\r
6874 if (!(GetKeyState(VK_SHIFT) & ~1)) {
\r
6875 /* Move selection here if it was empty */
\r
6877 pt.x = LOWORD(lParam);
\r
6878 pt.y = HIWORD(lParam);
\r
6879 SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);
\r
6880 if (sel.cpMin == sel.cpMax) {
\r
6881 if(lParam != -1) sel.cpMin = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&pt); /*doc is wrong*/
\r
6882 sel.cpMax = sel.cpMin;
\r
6883 SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
6885 SendMessage(hwnd, EM_HIDESELECTION, FALSE, FALSE);
\r
6886 { // [HGM] chatclick: code moved here from WM_RBUTTONUP case, to have menu appear on down-click
\r
6888 HMENU hmenu = LoadIcsTextMenu(icsTextMenuEntry);
\r
6889 SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);
\r
6890 if (sel.cpMin == sel.cpMax) {
\r
6891 EnableMenuItem(hmenu, IDM_Copy, MF_BYCOMMAND|MF_GRAYED);
\r
6892 EnableMenuItem(hmenu, IDM_QuickPaste, MF_BYCOMMAND|MF_GRAYED);
\r
6894 if (!IsClipboardFormatAvailable(CF_TEXT)) {
\r
6895 EnableMenuItem(hmenu, IDM_Paste, MF_BYCOMMAND|MF_GRAYED);
\r
6897 pt.x = LOWORD(lParam)-30; // [HGM] chatclick: make menu pop up with pointer above upper-right item
\r
6898 pt.y = HIWORD(lParam)-10; // make it appear as if mouse moved there, so it will be selected on up-click
\r
6899 PostMessage(hwnd, WM_MOUSEMOVE, wParam, lParam+5);
\r
6900 MenuPopup(hwnd, pt, hmenu, -1);
\r
6904 case WM_RBUTTONUP:
\r
6905 if (GetKeyState(VK_SHIFT) & ~1) {
\r
6906 SendDlgItemMessage(hwndConsole, OPT_ConsoleText,
\r
6907 WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);
\r
6911 hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
6913 return SendMessage(hInput, message, wParam, lParam);
\r
6914 case WM_MBUTTONDOWN:
\r
6915 return SendMessage(hwnd, WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);
\r
6917 switch (LOWORD(wParam)) {
\r
6918 case IDM_QuickPaste:
\r
6920 SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);
\r
6921 if (sel.cpMin == sel.cpMax) {
\r
6922 MessageBeep(MB_ICONEXCLAMATION);
\r
6925 SendMessage(hwnd, WM_COPY, 0, 0);
\r
6926 hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
6927 SendMessage(hInput, WM_PASTE, 0, 0);
\r
6932 SendMessage(hwnd, WM_CUT, 0, 0);
\r
6935 SendMessage(hwnd, WM_PASTE, 0, 0);
\r
6938 SendMessage(hwnd, WM_COPY, 0, 0);
\r
6942 int i = LOWORD(wParam) - IDM_CommandX;
\r
6943 if (i >= 0 && i < ICS_TEXT_MENU_SIZE &&
\r
6944 icsTextMenuEntry[i].command != NULL) {
\r
6945 CommandX(hwnd, icsTextMenuEntry[i].command,
\r
6946 icsTextMenuEntry[i].getname,
\r
6947 icsTextMenuEntry[i].immediate);
\r
6955 return (*consoleTextWindowProc)(hwnd, message, wParam, lParam);
\r
6958 WNDPROC consoleInputWindowProc;
\r
6961 ConsoleInputSubclass(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
\r
6963 char buf[MSG_SIZ];
\r
6965 static BOOL sendNextChar = FALSE;
\r
6966 static BOOL quoteNextChar = FALSE;
\r
6967 InputSource *is = consoleInputSource;
\r
6971 switch (message) {
\r
6973 if (!appData.localLineEditing || sendNextChar) {
\r
6974 is->buf[0] = (CHAR) wParam;
\r
6976 SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);
\r
6977 sendNextChar = FALSE;
\r
6980 if (quoteNextChar) {
\r
6981 buf[0] = (char) wParam;
\r
6982 buf[1] = NULLCHAR;
\r
6983 SendMessage(hwnd, EM_REPLACESEL, TRUE, (LPARAM) buf);
\r
6984 quoteNextChar = FALSE;
\r
6988 case '\r': /* Enter key */
\r
6989 is->count = GetWindowText(hwnd, is->buf, INPUT_SOURCE_BUF_SIZE-1);
\r
6990 if (consoleEcho) SaveInHistory(is->buf);
\r
6991 is->buf[is->count++] = '\n';
\r
6992 is->buf[is->count] = NULLCHAR;
\r
6993 SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);
\r
6994 if (consoleEcho) {
\r
6995 ConsoleOutput(is->buf, is->count, TRUE);
\r
6996 } else if (appData.localLineEditing) {
\r
6997 ConsoleOutput("\n", 1, TRUE);
\r
7000 case '\033': /* Escape key */
\r
7001 SetWindowText(hwnd, "");
\r
7002 cf.cbSize = sizeof(CHARFORMAT);
\r
7003 cf.dwMask = CFM_COLOR|CFM_BOLD|CFM_ITALIC|CFM_UNDERLINE|CFM_STRIKEOUT;
\r
7004 if (consoleEcho) {
\r
7005 cf.crTextColor = textAttribs[ColorNormal].color;
\r
7007 cf.crTextColor = COLOR_ECHOOFF;
\r
7009 cf.dwEffects = textAttribs[ColorNormal].effects;
\r
7010 SendMessage(hwnd, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf);
\r
7012 case '\t': /* Tab key */
\r
7013 if (GetKeyState(VK_SHIFT) < 0) {
\r
7015 SetFocus(GetDlgItem(hwndConsole, OPT_ConsoleText));
\r
7018 if (IsIconic(hwndMain)) ShowWindow(hwndMain, SW_RESTORE);
\r
7019 if (buttonDesc[0].hwnd) {
\r
7020 SetFocus(buttonDesc[0].hwnd);
\r
7022 SetFocus(hwndMain);
\r
7026 case '\023': /* Ctrl+S */
\r
7027 sendNextChar = TRUE;
\r
7029 case '\021': /* Ctrl+Q */
\r
7030 quoteNextChar = TRUE;
\r
7040 GetWindowText(hwnd, buf, MSG_SIZ);
\r
7041 p = PrevInHistory(buf);
\r
7043 SetWindowText(hwnd, p);
\r
7044 sel.cpMin = 999999;
\r
7045 sel.cpMax = 999999;
\r
7046 SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
7051 p = NextInHistory();
\r
7053 SetWindowText(hwnd, p);
\r
7054 sel.cpMin = 999999;
\r
7055 sel.cpMax = 999999;
\r
7056 SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
7062 if (!(GetKeyState(VK_CONTROL) & ~1)) break;
\r
7066 SendDlgItemMessage(hwndConsole, OPT_ConsoleText, message, wParam, lParam);
\r
7070 case WM_MBUTTONDOWN:
\r
7071 SendDlgItemMessage(hwndConsole, OPT_ConsoleText,
\r
7072 WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);
\r
7074 case WM_RBUTTONUP:
\r
7075 if (GetKeyState(VK_SHIFT) & ~1) {
\r
7076 SendDlgItemMessage(hwndConsole, OPT_ConsoleText,
\r
7077 WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);
\r
7081 hmenu = LoadMenu(hInst, "InputMenu");
\r
7082 SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);
\r
7083 if (sel.cpMin == sel.cpMax) {
\r
7084 EnableMenuItem(hmenu, IDM_Copy, MF_BYCOMMAND|MF_GRAYED);
\r
7085 EnableMenuItem(hmenu, IDM_Cut, MF_BYCOMMAND|MF_GRAYED);
\r
7087 if (!IsClipboardFormatAvailable(CF_TEXT)) {
\r
7088 EnableMenuItem(hmenu, IDM_Paste, MF_BYCOMMAND|MF_GRAYED);
\r
7090 pt.x = LOWORD(lParam);
\r
7091 pt.y = HIWORD(lParam);
\r
7092 MenuPopup(hwnd, pt, hmenu, -1);
\r
7096 switch (LOWORD(wParam)) {
\r
7098 SendMessage(hwnd, EM_UNDO, 0, 0);
\r
7100 case IDM_SelectAll:
\r
7102 sel.cpMax = -1; /*999999?*/
\r
7103 SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
7106 SendMessage(hwnd, WM_CUT, 0, 0);
\r
7109 SendMessage(hwnd, WM_PASTE, 0, 0);
\r
7112 SendMessage(hwnd, WM_COPY, 0, 0);
\r
7117 return (*consoleInputWindowProc)(hwnd, message, wParam, lParam);
\r
7120 #define CO_MAX 100000
\r
7121 #define CO_TRIM 1000
\r
7124 ConsoleWndProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
7126 static SnapData sd;
\r
7127 HWND hText, hInput;
\r
7129 static int sizeX, sizeY;
\r
7130 int newSizeX, newSizeY;
\r
7134 hText = GetDlgItem(hDlg, OPT_ConsoleText);
\r
7135 hInput = GetDlgItem(hDlg, OPT_ConsoleInput);
\r
7137 switch (message) {
\r
7139 if (((NMHDR*)lParam)->code == EN_LINK)
\r
7141 ENLINK *pLink = (ENLINK*)lParam;
\r
7142 if (pLink->msg == WM_LBUTTONUP)
\r
7146 tr.chrg = pLink->chrg;
\r
7147 tr.lpstrText = malloc(1+tr.chrg.cpMax-tr.chrg.cpMin);
\r
7148 SendMessage(hText, EM_GETTEXTRANGE, 0, (LPARAM)&tr);
\r
7149 ShellExecute(NULL, "open", tr.lpstrText, NULL, NULL, SW_SHOW);
\r
7150 free(tr.lpstrText);
\r
7154 case WM_INITDIALOG: /* message: initialize dialog box */
\r
7155 hwndConsole = hDlg;
\r
7157 consoleTextWindowProc = (WNDPROC)
\r
7158 SetWindowLongPtr(hText, GWLP_WNDPROC, (LONG_PTR) ConsoleTextSubclass);
\r
7159 SendMessage(hText, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);
\r
7160 consoleInputWindowProc = (WNDPROC)
\r
7161 SetWindowLongPtr(hInput, GWLP_WNDPROC, (LONG_PTR) ConsoleInputSubclass);
\r
7162 SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);
\r
7163 Colorize(ColorNormal, TRUE);
\r
7164 SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &consoleCF);
\r
7165 ChangedConsoleFont();
\r
7166 GetClientRect(hDlg, &rect);
\r
7167 sizeX = rect.right;
\r
7168 sizeY = rect.bottom;
\r
7169 if (wpConsole.x != CW_USEDEFAULT && wpConsole.y != CW_USEDEFAULT &&
\r
7170 wpConsole.width != CW_USEDEFAULT && wpConsole.height != CW_USEDEFAULT) {
\r
7171 WINDOWPLACEMENT wp;
\r
7172 EnsureOnScreen(&wpConsole.x, &wpConsole.y, 0, 0);
\r
7173 wp.length = sizeof(WINDOWPLACEMENT);
\r
7175 wp.showCmd = SW_SHOW;
\r
7176 wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;
\r
7177 wp.rcNormalPosition.left = wpConsole.x;
\r
7178 wp.rcNormalPosition.right = wpConsole.x + wpConsole.width;
\r
7179 wp.rcNormalPosition.top = wpConsole.y;
\r
7180 wp.rcNormalPosition.bottom = wpConsole.y + wpConsole.height;
\r
7181 SetWindowPlacement(hDlg, &wp);
\r
7184 // [HGM] Chessknight's change 2004-07-13
\r
7185 else { /* Determine Defaults */
\r
7186 WINDOWPLACEMENT wp;
\r
7187 wpConsole.x = wpMain.width + 1;
\r
7188 wpConsole.y = wpMain.y;
\r
7189 wpConsole.width = screenWidth - wpMain.width;
\r
7190 wpConsole.height = wpMain.height;
\r
7191 EnsureOnScreen(&wpConsole.x, &wpConsole.y, 0, 0);
\r
7192 wp.length = sizeof(WINDOWPLACEMENT);
\r
7194 wp.showCmd = SW_SHOW;
\r
7195 wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;
\r
7196 wp.rcNormalPosition.left = wpConsole.x;
\r
7197 wp.rcNormalPosition.right = wpConsole.x + wpConsole.width;
\r
7198 wp.rcNormalPosition.top = wpConsole.y;
\r
7199 wp.rcNormalPosition.bottom = wpConsole.y + wpConsole.height;
\r
7200 SetWindowPlacement(hDlg, &wp);
\r
7203 // Allow hText to highlight URLs and send notifications on them
\r
7204 wMask = (WORD) SendMessage(hText, EM_GETEVENTMASK, 0, 0L);
\r
7205 SendMessage(hText, EM_SETEVENTMASK, 0, wMask | ENM_LINK);
\r
7206 SendMessage(hText, EM_AUTOURLDETECT, TRUE, 0L);
\r
7207 SetWindowLongPtr(hText, GWLP_USERDATA, 79); // initialize the text window's width
\r
7221 if (IsIconic(hDlg)) break;
\r
7222 newSizeX = LOWORD(lParam);
\r
7223 newSizeY = HIWORD(lParam);
\r
7224 if (sizeX != newSizeX || sizeY != newSizeY) {
\r
7225 RECT rectText, rectInput;
\r
7227 int newTextHeight, newTextWidth;
\r
7228 GetWindowRect(hText, &rectText);
\r
7229 newTextWidth = rectText.right - rectText.left + newSizeX - sizeX;
\r
7230 newTextHeight = rectText.bottom - rectText.top + newSizeY - sizeY;
\r
7231 if (newTextHeight < 0) {
\r
7232 newSizeY += -newTextHeight;
\r
7233 newTextHeight = 0;
\r
7235 SetWindowPos(hText, NULL, 0, 0,
\r
7236 newTextWidth, newTextHeight, SWP_NOZORDER|SWP_NOMOVE);
\r
7237 GetWindowRect(hInput, &rectInput); /* gives screen coords */
\r
7238 pt.x = rectInput.left;
\r
7239 pt.y = rectInput.top + newSizeY - sizeY;
\r
7240 ScreenToClient(hDlg, &pt);
\r
7241 SetWindowPos(hInput, NULL,
\r
7242 pt.x, pt.y, /* needs client coords */
\r
7243 rectInput.right - rectInput.left + newSizeX - sizeX,
\r
7244 rectInput.bottom - rectInput.top, SWP_NOZORDER);
\r
7250 case WM_GETMINMAXINFO:
\r
7251 /* Prevent resizing window too small */
\r
7252 mmi = (MINMAXINFO *) lParam;
\r
7253 mmi->ptMinTrackSize.x = 100;
\r
7254 mmi->ptMinTrackSize.y = 100;
\r
7257 /* [AS] Snapping */
\r
7258 case WM_ENTERSIZEMOVE:
\r
7259 return OnEnterSizeMove( &sd, hDlg, wParam, lParam );
\r
7262 return OnSizing( &sd, hDlg, wParam, lParam );
\r
7265 return OnMoving( &sd, hDlg, wParam, lParam );
\r
7267 case WM_EXITSIZEMOVE:
\r
7268 UpdateICSWidth(hText);
\r
7269 return OnExitSizeMove( &sd, hDlg, wParam, lParam );
\r
7272 return DefWindowProc(hDlg, message, wParam, lParam);
\r
7280 if (hwndConsole) return;
\r
7281 hCons = CreateDialog(hInst, szConsoleName, 0, NULL);
\r
7282 SendMessage(hCons, WM_INITDIALOG, 0, 0);
\r
7287 ConsoleOutput(char* data, int length, int forceVisible)
\r
7292 char buf[CO_MAX+1];
\r
7295 static int delayLF = 0;
\r
7296 CHARRANGE savesel, sel;
\r
7298 if (hwndConsole == NULL || length > CO_MAX-100 || length == 0) return;
\r
7306 while (length--) {
\r
7314 } else if (*p == '\007') {
\r
7315 MyPlaySound(&sounds[(int)SoundBell]);
\r
7322 hText = GetDlgItem(hwndConsole, OPT_ConsoleText);
\r
7323 SendMessage(hText, EM_HIDESELECTION, TRUE, FALSE);
\r
7324 /* Save current selection */
\r
7325 SendMessage(hText, EM_EXGETSEL, 0, (LPARAM)&savesel);
\r
7326 exlen = GetWindowTextLength(hText);
\r
7327 /* Find out whether current end of text is visible */
\r
7328 SendMessage(hText, EM_GETRECT, 0, (LPARAM) &rect);
\r
7329 SendMessage(hText, EM_POSFROMCHAR, (WPARAM) &pEnd, exlen);
\r
7330 /* Trim existing text if it's too long */
\r
7331 if (exlen + (q - buf) > CO_MAX) {
\r
7332 trim = (CO_TRIM > (q - buf)) ? CO_TRIM : (q - buf);
\r
7335 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
7336 SendMessage(hText, EM_REPLACESEL, 0, (LPARAM)"");
\r
7338 savesel.cpMin -= trim;
\r
7339 savesel.cpMax -= trim;
\r
7340 if (exlen < 0) exlen = 0;
\r
7341 if (savesel.cpMin < 0) savesel.cpMin = 0;
\r
7342 if (savesel.cpMax < savesel.cpMin) savesel.cpMax = savesel.cpMin;
\r
7344 /* Append the new text */
\r
7345 sel.cpMin = exlen;
\r
7346 sel.cpMax = exlen;
\r
7347 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
7348 SendMessage(hText, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&consoleCF);
\r
7349 SendMessage(hText, EM_REPLACESEL, 0, (LPARAM) buf);
\r
7350 if (forceVisible || exlen == 0 ||
\r
7351 (rect.left <= pEnd.x && pEnd.x < rect.right &&
\r
7352 rect.top <= pEnd.y && pEnd.y < rect.bottom)) {
\r
7353 /* Scroll to make new end of text visible if old end of text
\r
7354 was visible or new text is an echo of user typein */
\r
7355 sel.cpMin = 9999999;
\r
7356 sel.cpMax = 9999999;
\r
7357 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
7358 SendMessage(hText, EM_HIDESELECTION, FALSE, FALSE);
\r
7359 SendMessage(hText, EM_SCROLLCARET, 0, 0);
\r
7360 SendMessage(hText, EM_HIDESELECTION, TRUE, FALSE);
\r
7362 if (savesel.cpMax == exlen || forceVisible) {
\r
7363 /* Move insert point to new end of text if it was at the old
\r
7364 end of text or if the new text is an echo of user typein */
\r
7365 sel.cpMin = 9999999;
\r
7366 sel.cpMax = 9999999;
\r
7367 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
7369 /* Restore previous selection */
\r
7370 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&savesel);
\r
7372 SendMessage(hText, EM_HIDESELECTION, FALSE, FALSE);
\r
7379 DisplayHoldingsCount(HDC hdc, int x, int y, int rightAlign, int copyNumber)
\r
7383 COLORREF oldFg, oldBg;
\r
7388 snprintf(buf, sizeof(buf)/sizeof(buf[0]),"%d", copyNumber); else buf[0] = 0;
\r
7390 oldFg = SetTextColor(hdc, RGB(255, 255, 255)); /* white */
\r
7391 oldBg = SetBkColor(hdc, RGB(0, 0, 0)); /* black */
\r
7392 oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);
\r
7395 rect.right = x + squareSize;
\r
7397 rect.bottom = y + squareSize;
\r
7400 ExtTextOut(hdc, x + MESSAGE_LINE_LEFTMARGIN
\r
7401 + (rightAlign ? (squareSize*2)/3 : 0),
\r
7402 y, ETO_CLIPPED|ETO_OPAQUE,
\r
7403 &rect, str, strlen(str), NULL);
\r
7405 (void) SetTextColor(hdc, oldFg);
\r
7406 (void) SetBkColor(hdc, oldBg);
\r
7407 (void) SelectObject(hdc, oldFont);
\r
7411 DisplayAClock(HDC hdc, int timeRemaining, int highlight,
\r
7412 RECT *rect, char *color, char *flagFell)
\r
7416 COLORREF oldFg, oldBg;
\r
7419 if (appData.clockMode) {
\r
7421 snprintf(buf, sizeof(buf)/sizeof(buf[0]), "%c %s %s", color[0], TimeString(timeRemaining), flagFell);
\r
7423 snprintf(buf, sizeof(buf)/sizeof(buf[0]), "%s:%c%s %s", color, (logoHeight>0 ? 0 : ' '), TimeString(timeRemaining), flagFell);
\r
7430 oldFg = SetTextColor(hdc, RGB(255, 255, 255)); /* white */
\r
7431 oldBg = SetBkColor(hdc, RGB(0, 0, 0)); /* black */
\r
7433 oldFg = SetTextColor(hdc, RGB(0, 0, 0)); /* black */
\r
7434 oldBg = SetBkColor(hdc, RGB(255, 255, 255)); /* white */
\r
7436 oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);
\r
7440 ExtTextOut(hdc, rect->left + MESSAGE_LINE_LEFTMARGIN,
\r
7441 rect->top, ETO_CLIPPED|ETO_OPAQUE,
\r
7442 rect, str, strlen(str), NULL);
\r
7443 if(logoHeight > 0 && appData.clockMode) {
\r
7445 str += strlen(color)+2;
\r
7446 r.top = rect->top + logoHeight/2;
\r
7447 r.left = rect->left;
\r
7448 r.right = rect->right;
\r
7449 r.bottom = rect->bottom;
\r
7450 ExtTextOut(hdc, rect->left + MESSAGE_LINE_LEFTMARGIN,
\r
7451 r.top, ETO_CLIPPED|ETO_OPAQUE,
\r
7452 &r, str, strlen(str), NULL);
\r
7454 (void) SetTextColor(hdc, oldFg);
\r
7455 (void) SetBkColor(hdc, oldBg);
\r
7456 (void) SelectObject(hdc, oldFont);
\r
7461 DoReadFile(HANDLE hFile, char *buf, int count, DWORD *outCount,
\r
7467 if( count <= 0 ) {
\r
7468 if (appData.debugMode) {
\r
7469 fprintf( debugFP, "DoReadFile: trying to read past end of buffer, overflow = %d\n", count );
\r
7472 return ERROR_INVALID_USER_BUFFER;
\r
7475 ResetEvent(ovl->hEvent);
\r
7476 ovl->Offset = ovl->OffsetHigh = 0;
\r
7477 ok = ReadFile(hFile, buf, count, outCount, ovl);
\r
7481 err = GetLastError();
\r
7482 if (err == ERROR_IO_PENDING) {
\r
7483 ok = GetOverlappedResult(hFile, ovl, outCount, TRUE);
\r
7487 err = GetLastError();
\r
7494 DoWriteFile(HANDLE hFile, char *buf, int count, DWORD *outCount,
\r
7499 ResetEvent(ovl->hEvent);
\r
7500 ovl->Offset = ovl->OffsetHigh = 0;
\r
7501 ok = WriteFile(hFile, buf, count, outCount, ovl);
\r
7505 err = GetLastError();
\r
7506 if (err == ERROR_IO_PENDING) {
\r
7507 ok = GetOverlappedResult(hFile, ovl, outCount, TRUE);
\r
7511 err = GetLastError();
\r
7517 /* [AS] If input is line by line and a line exceed the buffer size, force an error */
\r
7518 void CheckForInputBufferFull( InputSource * is )
\r
7520 if( is->lineByLine && (is->next - is->buf) >= INPUT_SOURCE_BUF_SIZE ) {
\r
7521 /* Look for end of line */
\r
7522 char * p = is->buf;
\r
7524 while( p < is->next && *p != '\n' ) {
\r
7528 if( p >= is->next ) {
\r
7529 if (appData.debugMode) {
\r
7530 fprintf( debugFP, "Input line exceeded buffer size (source id=%lu)\n", is->id );
\r
7533 is->error = ERROR_BROKEN_PIPE; /* [AS] Just any non-successful code! */
\r
7534 is->count = (DWORD) -1;
\r
7535 is->next = is->buf;
\r
7541 InputThread(LPVOID arg)
\r
7546 is = (InputSource *) arg;
\r
7547 ovl.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
\r
7548 ovl.Internal = ovl.InternalHigh = ovl.Offset = ovl.OffsetHigh = 0;
\r
7549 while (is->hThread != NULL) {
\r
7550 is->error = DoReadFile(is->hFile, is->next,
\r
7551 INPUT_SOURCE_BUF_SIZE - (is->next - is->buf),
\r
7552 &is->count, &ovl);
\r
7553 if (is->error == NO_ERROR) {
\r
7554 is->next += is->count;
\r
7556 if (is->error == ERROR_BROKEN_PIPE) {
\r
7557 /* Correct for MS brain damage. EOF reading a pipe is not an error. */
\r
7560 is->count = (DWORD) -1;
\r
7561 /* [AS] The (is->count <= 0) check below is not useful for unsigned values! */
\r
7566 CheckForInputBufferFull( is );
\r
7568 SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);
\r
7570 if( is->count == ((DWORD) -1) ) break; /* [AS] */
\r
7572 if (is->count <= 0) break; /* Quit on EOF or error */
\r
7575 CloseHandle(ovl.hEvent);
\r
7576 CloseHandle(is->hFile);
\r
7578 if (appData.debugMode) {
\r
7579 fprintf( debugFP, "Input thread terminated (id=%lu, error=%d, count=%ld)\n", is->id, is->error, is->count );
\r
7586 /* Windows 95 beta 2 won't let you do overlapped i/o on a console or pipe */
\r
7588 NonOvlInputThread(LPVOID arg)
\r
7595 is = (InputSource *) arg;
\r
7596 while (is->hThread != NULL) {
\r
7597 is->error = ReadFile(is->hFile, is->next,
\r
7598 INPUT_SOURCE_BUF_SIZE - (is->next - is->buf),
\r
7599 &is->count, NULL) ? NO_ERROR : GetLastError();
\r
7600 if (is->error == NO_ERROR) {
\r
7601 /* Change CRLF to LF */
\r
7602 if (is->next > is->buf) {
\r
7604 i = is->count + 1;
\r
7612 if (prev == '\r' && *p == '\n') {
\r
7624 if (is->error == ERROR_BROKEN_PIPE) {
\r
7625 /* Correct for MS brain damage. EOF reading a pipe is not an error. */
\r
7628 is->count = (DWORD) -1;
\r
7632 CheckForInputBufferFull( is );
\r
7634 SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);
\r
7636 if( is->count == ((DWORD) -1) ) break; /* [AS] */
\r
7638 if (is->count < 0) break; /* Quit on error */
\r
7640 CloseHandle(is->hFile);
\r
7645 SocketInputThread(LPVOID arg)
\r
7649 is = (InputSource *) arg;
\r
7650 while (is->hThread != NULL) {
\r
7651 is->count = recv(is->sock, is->buf, INPUT_SOURCE_BUF_SIZE, 0);
\r
7652 if ((int)is->count == SOCKET_ERROR) {
\r
7653 is->count = (DWORD) -1;
\r
7654 is->error = WSAGetLastError();
\r
7656 is->error = NO_ERROR;
\r
7657 is->next += is->count;
\r
7658 if (is->count == 0 && is->second == is) {
\r
7659 /* End of file on stderr; quit with no message */
\r
7663 SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);
\r
7665 if( is->count == ((DWORD) -1) ) break; /* [AS] */
\r
7667 if (is->count <= 0) break; /* Quit on EOF or error */
\r
7673 InputEvent(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
\r
7677 is = (InputSource *) lParam;
\r
7678 if (is->lineByLine) {
\r
7679 /* Feed in lines one by one */
\r
7680 char *p = is->buf;
\r
7682 while (q < is->next) {
\r
7683 if (*q++ == '\n') {
\r
7684 (is->func)(is, is->closure, p, q - p, NO_ERROR);
\r
7689 /* Move any partial line to the start of the buffer */
\r
7691 while (p < is->next) {
\r
7696 if (is->error != NO_ERROR || is->count == 0) {
\r
7697 /* Notify backend of the error. Note: If there was a partial
\r
7698 line at the end, it is not flushed through. */
\r
7699 (is->func)(is, is->closure, is->buf, is->count, is->error);
\r
7702 /* Feed in the whole chunk of input at once */
\r
7703 (is->func)(is, is->closure, is->buf, is->count, is->error);
\r
7704 is->next = is->buf;
\r
7708 /*---------------------------------------------------------------------------*\
\r
7710 * Menu enables. Used when setting various modes.
\r
7712 \*---------------------------------------------------------------------------*/
\r
7720 GreyRevert(Boolean grey)
\r
7721 { // [HGM] vari: for retracting variations in local mode
\r
7722 HMENU hmenu = GetMenu(hwndMain);
\r
7723 EnableMenuItem(hmenu, IDM_Revert, MF_BYCOMMAND|(grey ? MF_GRAYED : MF_ENABLED));
\r
7724 EnableMenuItem(hmenu, IDM_Annotate, MF_BYCOMMAND|(grey ? MF_GRAYED : MF_ENABLED));
\r
7728 SetMenuEnables(HMENU hmenu, Enables *enab)
\r
7730 while (enab->item > 0) {
\r
7731 (void) EnableMenuItem(hmenu, enab->item, enab->flags);
\r
7736 Enables gnuEnables[] = {
\r
7737 { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },
\r
7738 { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },
\r
7739 { IDM_IcsClient, MF_BYCOMMAND|MF_GRAYED },
\r
7740 { IDM_Accept, MF_BYCOMMAND|MF_GRAYED },
\r
7741 { IDM_Decline, MF_BYCOMMAND|MF_GRAYED },
\r
7742 { IDM_Rematch, MF_BYCOMMAND|MF_GRAYED },
\r
7743 { IDM_Adjourn, MF_BYCOMMAND|MF_GRAYED },
\r
7744 { IDM_StopExamining, MF_BYCOMMAND|MF_GRAYED },
\r
7745 { IDM_StopObserving, MF_BYCOMMAND|MF_GRAYED },
\r
7746 { IDM_Upload, MF_BYCOMMAND|MF_GRAYED },
\r
7747 { IDM_Revert, MF_BYCOMMAND|MF_GRAYED },
\r
7748 { IDM_Annotate, MF_BYCOMMAND|MF_GRAYED },
\r
7749 { IDM_NewChat, MF_BYCOMMAND|MF_GRAYED },
\r
7753 Enables icsEnables[] = {
\r
7754 { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },
\r
7755 { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },
\r
7756 { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },
\r
7757 { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },
\r
7758 { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },
\r
7759 { IDM_Match, MF_BYCOMMAND|MF_GRAYED },
\r
7760 { IDM_MachineBoth, MF_BYCOMMAND|MF_GRAYED },
\r
7761 { IDM_AnalysisMode, MF_BYCOMMAND|MF_ENABLED },
\r
7762 { IDM_AnalyzeFile, MF_BYCOMMAND|MF_GRAYED },
\r
7763 { IDM_TimeControl, MF_BYCOMMAND|MF_GRAYED },
\r
7764 { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },
\r
7765 { IDM_Hint, MF_BYCOMMAND|MF_GRAYED },
\r
7766 { IDM_Book, MF_BYCOMMAND|MF_GRAYED },
\r
7767 { IDM_IcsOptions, MF_BYCOMMAND|MF_ENABLED },
\r
7768 { IDM_Engine1Options, MF_BYCOMMAND|MF_GRAYED },
\r
7769 { IDM_Engine2Options, MF_BYCOMMAND|MF_GRAYED },
\r
7770 { IDM_Annotate, MF_BYCOMMAND|MF_GRAYED },
\r
7775 Enables zippyEnables[] = {
\r
7776 { IDM_MoveNow, MF_BYCOMMAND|MF_ENABLED },
\r
7777 { IDM_Hint, MF_BYCOMMAND|MF_ENABLED },
\r
7778 { IDM_Book, MF_BYCOMMAND|MF_ENABLED },
\r
7779 { IDM_Engine1Options, MF_BYCOMMAND|MF_ENABLED },
\r
7784 Enables ncpEnables[] = {
\r
7785 { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },
\r
7786 { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },
\r
7787 { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },
\r
7788 { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },
\r
7789 { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },
\r
7790 { IDM_Match, MF_BYCOMMAND|MF_GRAYED },
\r
7791 { IDM_AnalysisMode, MF_BYCOMMAND|MF_GRAYED },
\r
7792 { IDM_AnalyzeFile, MF_BYCOMMAND|MF_GRAYED },
\r
7793 { IDM_IcsClient, MF_BYCOMMAND|MF_GRAYED },
\r
7794 { ACTION_POS, MF_BYPOSITION|MF_GRAYED },
\r
7795 { IDM_Revert, MF_BYCOMMAND|MF_GRAYED },
\r
7796 { IDM_Annotate, MF_BYCOMMAND|MF_GRAYED },
\r
7797 { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },
\r
7798 { IDM_RetractMove, MF_BYCOMMAND|MF_GRAYED },
\r
7799 { IDM_TimeControl, MF_BYCOMMAND|MF_GRAYED },
\r
7800 { IDM_Hint, MF_BYCOMMAND|MF_GRAYED },
\r
7801 { IDM_Book, MF_BYCOMMAND|MF_GRAYED },
\r
7802 { IDM_MachineBoth, MF_BYCOMMAND|MF_GRAYED },
\r
7803 { IDM_NewChat, MF_BYCOMMAND|MF_GRAYED },
\r
7804 { IDM_Engine1Options, MF_BYCOMMAND|MF_GRAYED },
\r
7805 { IDM_Engine2Options, MF_BYCOMMAND|MF_GRAYED },
\r
7806 { IDM_Sounds, MF_BYCOMMAND|MF_GRAYED },
\r
7810 Enables trainingOnEnables[] = {
\r
7811 { IDM_EditComment, MF_BYCOMMAND|MF_GRAYED },
\r
7812 { IDM_Comment, MF_BYCOMMAND|MF_GRAYED },
\r
7813 { IDM_Pause, MF_BYCOMMAND|MF_GRAYED },
\r
7814 { IDM_Forward, MF_BYCOMMAND|MF_GRAYED },
\r
7815 { IDM_Backward, MF_BYCOMMAND|MF_GRAYED },
\r
7816 { IDM_ToEnd, MF_BYCOMMAND|MF_GRAYED },
\r
7817 { IDM_ToStart, MF_BYCOMMAND|MF_GRAYED },
\r
7818 { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },
\r
7819 { IDM_TruncateGame, MF_BYCOMMAND|MF_GRAYED },
\r
7823 Enables trainingOffEnables[] = {
\r
7824 { IDM_EditComment, MF_BYCOMMAND|MF_ENABLED },
\r
7825 { IDM_Comment, MF_BYCOMMAND|MF_ENABLED },
\r
7826 { IDM_Pause, MF_BYCOMMAND|MF_ENABLED },
\r
7827 { IDM_Forward, MF_BYCOMMAND|MF_ENABLED },
\r
7828 { IDM_Backward, MF_BYCOMMAND|MF_ENABLED },
\r
7829 { IDM_ToEnd, MF_BYCOMMAND|MF_ENABLED },
\r
7830 { IDM_ToStart, MF_BYCOMMAND|MF_ENABLED },
\r
7831 { IDM_MoveNow, MF_BYCOMMAND|MF_ENABLED },
\r
7832 { IDM_TruncateGame, MF_BYCOMMAND|MF_ENABLED },
\r
7836 /* These modify either ncpEnables or gnuEnables */
\r
7837 Enables cmailEnables[] = {
\r
7838 { IDM_MailMove, MF_BYCOMMAND|MF_ENABLED },
\r
7839 { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_ENABLED },
\r
7840 { ACTION_POS, MF_BYPOSITION|MF_ENABLED },
\r
7841 { IDM_CallFlag, MF_BYCOMMAND|MF_GRAYED },
\r
7842 { IDM_Draw, MF_BYCOMMAND|MF_ENABLED },
\r
7843 { IDM_Adjourn, MF_BYCOMMAND|MF_GRAYED },
\r
7844 { IDM_Abort, MF_BYCOMMAND|MF_GRAYED },
\r
7848 Enables machineThinkingEnables[] = {
\r
7849 { IDM_LoadGame, MF_BYCOMMAND|MF_GRAYED },
\r
7850 { IDM_LoadNextGame, MF_BYCOMMAND|MF_GRAYED },
\r
7851 { IDM_LoadPrevGame, MF_BYCOMMAND|MF_GRAYED },
\r
7852 { IDM_ReloadGame, MF_BYCOMMAND|MF_GRAYED },
\r
7853 { IDM_PasteGame, MF_BYCOMMAND|MF_GRAYED },
\r
7854 { IDM_LoadPosition, MF_BYCOMMAND|MF_GRAYED },
\r
7855 { IDM_LoadNextPosition, MF_BYCOMMAND|MF_GRAYED },
\r
7856 { IDM_LoadPrevPosition, MF_BYCOMMAND|MF_GRAYED },
\r
7857 { IDM_ReloadPosition, MF_BYCOMMAND|MF_GRAYED },
\r
7858 { IDM_PastePosition, MF_BYCOMMAND|MF_GRAYED },
\r
7859 { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },
\r
7860 { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },
\r
7861 { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },
\r
7862 { IDM_Match, MF_BYCOMMAND|MF_GRAYED },
\r
7863 { IDM_TypeInMove, MF_BYCOMMAND|MF_GRAYED },
\r
7864 { IDM_RetractMove, MF_BYCOMMAND|MF_GRAYED },
\r
7868 Enables userThinkingEnables[] = {
\r
7869 { IDM_LoadGame, MF_BYCOMMAND|MF_ENABLED },
\r
7870 { IDM_LoadNextGame, MF_BYCOMMAND|MF_ENABLED },
\r
7871 { IDM_LoadPrevGame, MF_BYCOMMAND|MF_ENABLED },
\r
7872 { IDM_ReloadGame, MF_BYCOMMAND|MF_ENABLED },
\r
7873 { IDM_PasteGame, MF_BYCOMMAND|MF_ENABLED },
\r
7874 { IDM_LoadPosition, MF_BYCOMMAND|MF_ENABLED },
\r
7875 { IDM_LoadNextPosition, MF_BYCOMMAND|MF_ENABLED },
\r
7876 { IDM_LoadPrevPosition, MF_BYCOMMAND|MF_ENABLED },
\r
7877 { IDM_ReloadPosition, MF_BYCOMMAND|MF_ENABLED },
\r
7878 { IDM_PastePosition, MF_BYCOMMAND|MF_ENABLED },
\r
7879 { IDM_MachineWhite, MF_BYCOMMAND|MF_ENABLED },
\r
7880 { IDM_MachineBlack, MF_BYCOMMAND|MF_ENABLED },
\r
7881 { IDM_TwoMachines, MF_BYCOMMAND|MF_ENABLED },
\r
7882 { IDM_Match, MF_BYCOMMAND|MF_ENABLED },
\r
7883 { IDM_TypeInMove, MF_BYCOMMAND|MF_ENABLED },
\r
7884 { IDM_RetractMove, MF_BYCOMMAND|MF_ENABLED },
\r
7888 /*---------------------------------------------------------------------------*\
\r
7890 * Front-end interface functions exported by XBoard.
\r
7891 * Functions appear in same order as prototypes in frontend.h.
\r
7893 \*---------------------------------------------------------------------------*/
\r
7897 static UINT prevChecked = 0;
\r
7898 static int prevPausing = 0;
\r
7901 if (pausing != prevPausing) {
\r
7902 prevPausing = pausing;
\r
7903 (void) CheckMenuItem(GetMenu(hwndMain), IDM_Pause,
\r
7904 MF_BYCOMMAND|(pausing ? MF_CHECKED : MF_UNCHECKED));
\r
7905 if (hwndPause) SetWindowText(hwndPause, pausing ? "C" : "P");
\r
7908 switch (gameMode) {
\r
7909 case BeginningOfGame:
\r
7910 if (appData.icsActive)
\r
7911 nowChecked = IDM_IcsClient;
\r
7912 else if (appData.noChessProgram)
\r
7913 nowChecked = IDM_EditGame;
\r
7915 nowChecked = IDM_MachineBlack;
\r
7917 case MachinePlaysBlack:
\r
7918 nowChecked = IDM_MachineBlack;
\r
7920 case MachinePlaysWhite:
\r
7921 nowChecked = IDM_MachineWhite;
\r
7923 case TwoMachinesPlay:
\r
7924 nowChecked = matchMode ? IDM_Match : IDM_TwoMachines; // [HGM] match
\r
7927 nowChecked = IDM_AnalysisMode;
\r
7930 nowChecked = IDM_AnalyzeFile;
\r
7933 nowChecked = IDM_EditGame;
\r
7935 case PlayFromGameFile:
\r
7936 nowChecked = IDM_LoadGame;
\r
7938 case EditPosition:
\r
7939 nowChecked = IDM_EditPosition;
\r
7942 nowChecked = IDM_Training;
\r
7944 case IcsPlayingWhite:
\r
7945 case IcsPlayingBlack:
\r
7946 case IcsObserving:
\r
7948 nowChecked = IDM_IcsClient;
\r
7955 if (prevChecked != 0)
\r
7956 (void) CheckMenuItem(GetMenu(hwndMain),
\r
7957 prevChecked, MF_BYCOMMAND|MF_UNCHECKED);
\r
7958 if (nowChecked != 0)
\r
7959 (void) CheckMenuItem(GetMenu(hwndMain),
\r
7960 nowChecked, MF_BYCOMMAND|MF_CHECKED);
\r
7962 if (nowChecked == IDM_LoadGame || nowChecked == IDM_Training) {
\r
7963 (void) EnableMenuItem(GetMenu(hwndMain), IDM_Training,
\r
7964 MF_BYCOMMAND|MF_ENABLED);
\r
7966 (void) EnableMenuItem(GetMenu(hwndMain),
\r
7967 IDM_Training, MF_BYCOMMAND|MF_GRAYED);
\r
7970 prevChecked = nowChecked;
\r
7972 /* [DM] icsEngineAnalyze - Do a sceure check too */
\r
7973 if (appData.icsActive) {
\r
7974 if (appData.icsEngineAnalyze) {
\r
7975 (void) CheckMenuItem(GetMenu(hwndMain), IDM_AnalysisMode,
\r
7976 MF_BYCOMMAND|MF_CHECKED);
\r
7978 (void) CheckMenuItem(GetMenu(hwndMain), IDM_AnalysisMode,
\r
7979 MF_BYCOMMAND|MF_UNCHECKED);
\r
7982 DisplayLogos(); // [HGM] logos: mode change could have altered logos
\r
7988 HMENU hmenu = GetMenu(hwndMain);
\r
7989 SetMenuEnables(hmenu, icsEnables);
\r
7990 EnableMenuItem(GetSubMenu(hmenu, OPTIONS_POS), IDM_IcsOptions,
\r
7991 MF_BYCOMMAND|MF_ENABLED);
\r
7993 if (appData.zippyPlay) {
\r
7994 SetMenuEnables(hmenu, zippyEnables);
\r
7995 if (!appData.noChessProgram) /* [DM] icsEngineAnalyze */
\r
7996 (void) EnableMenuItem(GetMenu(hwndMain), IDM_AnalysisMode,
\r
7997 MF_BYCOMMAND|MF_ENABLED);
\r
8005 SetMenuEnables(GetMenu(hwndMain), gnuEnables);
\r
8011 HMENU hmenu = GetMenu(hwndMain);
\r
8012 SetMenuEnables(hmenu, ncpEnables);
\r
8013 DrawMenuBar(hwndMain);
\r
8019 SetMenuEnables(GetMenu(hwndMain), cmailEnables);
\r
8023 SetTrainingModeOn()
\r
8026 SetMenuEnables(GetMenu(hwndMain), trainingOnEnables);
\r
8027 for (i = 0; i < N_BUTTONS; i++) {
\r
8028 if (buttonDesc[i].hwnd != NULL)
\r
8029 EnableWindow(buttonDesc[i].hwnd, FALSE);
\r
8034 VOID SetTrainingModeOff()
\r
8037 SetMenuEnables(GetMenu(hwndMain), trainingOffEnables);
\r
8038 for (i = 0; i < N_BUTTONS; i++) {
\r
8039 if (buttonDesc[i].hwnd != NULL)
\r
8040 EnableWindow(buttonDesc[i].hwnd, TRUE);
\r
8046 SetUserThinkingEnables()
\r
8048 SetMenuEnables(GetMenu(hwndMain), userThinkingEnables);
\r
8052 SetMachineThinkingEnables()
\r
8054 HMENU hMenu = GetMenu(hwndMain);
\r
8055 int flags = MF_BYCOMMAND|MF_ENABLED;
\r
8057 SetMenuEnables(hMenu, machineThinkingEnables);
\r
8059 if (gameMode == MachinePlaysBlack) {
\r
8060 (void)EnableMenuItem(hMenu, IDM_MachineBlack, flags);
\r
8061 } else if (gameMode == MachinePlaysWhite) {
\r
8062 (void)EnableMenuItem(hMenu, IDM_MachineWhite, flags);
\r
8063 } else if (gameMode == TwoMachinesPlay) {
\r
8064 (void)EnableMenuItem(hMenu, matchMode ? IDM_Match : IDM_TwoMachines, flags); // [HGM] match
\r
8070 DisplayTitle(char *str)
\r
8072 char title[MSG_SIZ], *host;
\r
8073 if (str[0] != NULLCHAR) {
\r
8074 safeStrCpy(title, str, sizeof(title)/sizeof(title[0]) );
\r
8075 } else if (appData.icsActive) {
\r
8076 if (appData.icsCommPort[0] != NULLCHAR)
\r
8079 host = appData.icsHost;
\r
8080 snprintf(title, MSG_SIZ, "%s: %s", szTitle, host);
\r
8081 } else if (appData.noChessProgram) {
\r
8082 safeStrCpy(title, szTitle, sizeof(title)/sizeof(title[0]) );
\r
8084 safeStrCpy(title, szTitle, sizeof(title)/sizeof(title[0]) );
\r
8085 strcat(title, ": ");
\r
8086 strcat(title, first.tidy);
\r
8088 SetWindowText(hwndMain, title);
\r
8093 DisplayMessage(char *str1, char *str2)
\r
8097 int remain = MESSAGE_TEXT_MAX - 1;
\r
8100 moveErrorMessageUp = FALSE; /* turned on later by caller if needed */
\r
8101 messageText[0] = NULLCHAR;
\r
8103 len = strlen(str1);
\r
8104 if (len > remain) len = remain;
\r
8105 strncpy(messageText, str1, len);
\r
8106 messageText[len] = NULLCHAR;
\r
8109 if (*str2 && remain >= 2) {
\r
8111 strcat(messageText, " ");
\r
8114 len = strlen(str2);
\r
8115 if (len > remain) len = remain;
\r
8116 strncat(messageText, str2, len);
\r
8118 messageText[MESSAGE_TEXT_MAX - 1] = NULLCHAR;
\r
8120 if (hwndMain == NULL || IsIconic(hwndMain)) return;
\r
8124 hdc = GetDC(hwndMain);
\r
8125 oldFont = SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);
\r
8126 ExtTextOut(hdc, messageRect.left, messageRect.top, ETO_CLIPPED|ETO_OPAQUE,
\r
8127 &messageRect, messageText, strlen(messageText), NULL);
\r
8128 (void) SelectObject(hdc, oldFont);
\r
8129 (void) ReleaseDC(hwndMain, hdc);
\r
8133 DisplayError(char *str, int error)
\r
8135 char buf[MSG_SIZ*2], buf2[MSG_SIZ];
\r
8139 safeStrCpy(buf, str, sizeof(buf)/sizeof(buf[0]) );
\r
8141 len = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,
\r
8142 NULL, error, LANG_NEUTRAL,
\r
8143 (LPSTR) buf2, MSG_SIZ, NULL);
\r
8145 snprintf(buf, 2*MSG_SIZ, "%s:\n%s", str, buf2);
\r
8147 ErrorMap *em = errmap;
\r
8148 while (em->err != 0 && em->err != error) em++;
\r
8149 if (em->err != 0) {
\r
8150 snprintf(buf, 2*MSG_SIZ, "%s:\n%s", str, em->msg);
\r
8152 snprintf(buf, 2*MSG_SIZ, "%s:\nError code %d", str, error);
\r
8157 ErrorPopUp(_("Error"), buf);
\r
8162 DisplayMoveError(char *str)
\r
8164 fromX = fromY = -1;
\r
8165 ClearHighlights();
\r
8166 DrawPosition(FALSE, NULL);
\r
8167 if (appData.popupMoveErrors) {
\r
8168 ErrorPopUp(_("Error"), str);
\r
8170 DisplayMessage(str, "");
\r
8171 moveErrorMessageUp = TRUE;
\r
8176 DisplayFatalError(char *str, int error, int exitStatus)
\r
8178 char buf[2*MSG_SIZ], buf2[MSG_SIZ];
\r
8180 char *label = exitStatus ? _("Fatal Error") : _("Exiting");
\r
8183 len = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,
\r
8184 NULL, error, LANG_NEUTRAL,
\r
8185 (LPSTR) buf2, MSG_SIZ, NULL);
\r
8187 snprintf(buf, 2*MSG_SIZ, "%s:\n%s", str, buf2);
\r
8189 ErrorMap *em = errmap;
\r
8190 while (em->err != 0 && em->err != error) em++;
\r
8191 if (em->err != 0) {
\r
8192 snprintf(buf, 2*MSG_SIZ, "%s:\n%s", str, em->msg);
\r
8194 snprintf(buf, 2*MSG_SIZ, "%s:\nError code %d", str, error);
\r
8199 if (appData.debugMode) {
\r
8200 fprintf(debugFP, "%s: %s\n", label, str);
\r
8202 if (appData.popupExitMessage) {
\r
8203 (void) MessageBox(hwndMain, str, label, MB_OK|
\r
8204 (exitStatus ? MB_ICONSTOP : MB_ICONINFORMATION));
\r
8206 ExitEvent(exitStatus);
\r
8211 DisplayInformation(char *str)
\r
8213 (void) MessageBox(hwndMain, str, _("Information"), MB_OK|MB_ICONINFORMATION);
\r
8218 DisplayNote(char *str)
\r
8220 ErrorPopUp(_("Note"), str);
\r
8225 char *title, *question, *replyPrefix;
\r
8230 QuestionDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
8232 static QuestionParams *qp;
\r
8233 char reply[MSG_SIZ];
\r
8236 switch (message) {
\r
8237 case WM_INITDIALOG:
\r
8238 qp = (QuestionParams *) lParam;
\r
8239 CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));
\r
8240 Translate(hDlg, DLG_Question);
\r
8241 SetWindowText(hDlg, qp->title);
\r
8242 SetDlgItemText(hDlg, OPT_QuestionText, qp->question);
\r
8243 SetFocus(GetDlgItem(hDlg, OPT_QuestionInput));
\r
8247 switch (LOWORD(wParam)) {
\r
8249 safeStrCpy(reply, qp->replyPrefix, sizeof(reply)/sizeof(reply[0]) );
\r
8250 if (*reply) strcat(reply, " ");
\r
8251 len = strlen(reply);
\r
8252 GetDlgItemText(hDlg, OPT_QuestionInput, reply + len, sizeof(reply) - len);
\r
8253 strcat(reply, "\n");
\r
8254 OutputToProcess(qp->pr, reply, strlen(reply), &err);
\r
8255 EndDialog(hDlg, TRUE);
\r
8256 if (err) DisplayFatalError(_("Error writing to chess program"), err, 1);
\r
8259 EndDialog(hDlg, FALSE);
\r
8270 AskQuestion(char* title, char *question, char *replyPrefix, ProcRef pr)
\r
8272 QuestionParams qp;
\r
8276 qp.question = question;
\r
8277 qp.replyPrefix = replyPrefix;
\r
8279 lpProc = MakeProcInstance((FARPROC)QuestionDialog, hInst);
\r
8280 DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_Question),
\r
8281 hwndMain, (DLGPROC)lpProc, (LPARAM)&qp);
\r
8282 FreeProcInstance(lpProc);
\r
8285 /* [AS] Pick FRC position */
\r
8286 LRESULT CALLBACK NewGameFRC_Proc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
8288 static int * lpIndexFRC;
\r
8294 case WM_INITDIALOG:
\r
8295 lpIndexFRC = (int *) lParam;
\r
8297 CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));
\r
8298 Translate(hDlg, DLG_NewGameFRC);
\r
8300 SendDlgItemMessage( hDlg, IDC_NFG_Edit, EM_SETLIMITTEXT, sizeof(buf)-1, 0 );
\r
8301 SetDlgItemInt( hDlg, IDC_NFG_Edit, *lpIndexFRC, TRUE );
\r
8302 SendDlgItemMessage( hDlg, IDC_NFG_Edit, EM_SETSEL, 0, -1 );
\r
8303 SetFocus(GetDlgItem(hDlg, IDC_NFG_Edit));
\r
8308 switch( LOWORD(wParam) ) {
\r
8310 *lpIndexFRC = GetDlgItemInt(hDlg, IDC_NFG_Edit, &index_is_ok, TRUE );
\r
8311 EndDialog( hDlg, 0 );
\r
8312 shuffleOpenings = TRUE; /* [HGM] shuffle: switch shuffling on for as long as we stay in current variant */
\r
8315 EndDialog( hDlg, 1 );
\r
8317 case IDC_NFG_Edit:
\r
8318 if( HIWORD(wParam) == EN_CHANGE ) {
\r
8319 GetDlgItemInt(hDlg, IDC_NFG_Edit, &index_is_ok, TRUE );
\r
8321 EnableWindow( GetDlgItem(hDlg, IDOK), index_is_ok );
\r
8324 case IDC_NFG_Random:
\r
8325 snprintf( buf, sizeof(buf)/sizeof(buf[0]), "%d", myrandom() ); /* [HGM] shuffle: no longer limit to 960 */
\r
8326 SetDlgItemText(hDlg, IDC_NFG_Edit, buf );
\r
8339 int index = appData.defaultFrcPosition;
\r
8340 FARPROC lpProc = MakeProcInstance( (FARPROC) NewGameFRC_Proc, hInst );
\r
8342 result = DialogBoxParam( hInst, MAKEINTRESOURCE(DLG_NewGameFRC), hwndMain, (DLGPROC)lpProc, (LPARAM)&index );
\r
8344 if( result == 0 ) {
\r
8345 appData.defaultFrcPosition = index;
\r
8351 /* [AS] Game list options. Refactored by HGM */
\r
8353 HWND gameListOptionsDialog;
\r
8355 // low-level front-end: clear text edit / list widget
\r
8359 SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_RESETCONTENT, 0, 0 );
\r
8362 // low-level front-end: clear text edit / list widget
\r
8364 GLT_DeSelectList()
\r
8366 SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_SETCURSEL, 0, 0 );
\r
8369 // low-level front-end: append line to text edit / list widget
\r
8371 GLT_AddToList( char *name )
\r
8374 SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_ADDSTRING, 0, (LPARAM) name );
\r
8378 // low-level front-end: get line from text edit / list widget
\r
8380 GLT_GetFromList( int index, char *name )
\r
8383 if( SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_GETTEXT, index, (LPARAM) name ) != LB_ERR )
\r
8389 void GLT_MoveSelection( HWND hDlg, int delta )
\r
8391 int idx1 = (int) SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETCURSEL, 0, 0 );
\r
8392 int idx2 = idx1 + delta;
\r
8393 int count = (int) SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETCOUNT, 0, 0 );
\r
8395 if( idx1 >=0 && idx1 < count && idx2 >= 0 && idx2 < count ) {
\r
8398 SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETTEXT, idx1, (LPARAM) buf );
\r
8399 SendDlgItemMessage( hDlg, IDC_GameListTags, LB_DELETESTRING, idx1, 0 );
\r
8400 SendDlgItemMessage( hDlg, IDC_GameListTags, LB_INSERTSTRING, idx2, (LPARAM) buf );
\r
8401 SendDlgItemMessage( hDlg, IDC_GameListTags, LB_SETCURSEL, idx2, 0 );
\r
8405 LRESULT CALLBACK GameListOptions_Proc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
8409 case WM_INITDIALOG:
\r
8410 gameListOptionsDialog = hDlg; // [HGM] pass through global to keep out off back-end
\r
8412 CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));
\r
8413 Translate(hDlg, DLG_GameListOptions);
\r
8415 /* Initialize list */
\r
8416 GLT_TagsToList( lpUserGLT );
\r
8418 SetFocus( GetDlgItem(hDlg, IDC_GameListTags) );
\r
8423 switch( LOWORD(wParam) ) {
\r
8426 EndDialog( hDlg, 0 );
\r
8429 EndDialog( hDlg, 1 );
\r
8432 case IDC_GLT_Default:
\r
8433 GLT_TagsToList( GLT_DEFAULT_TAGS );
\r
8436 case IDC_GLT_Restore:
\r
8437 GLT_TagsToList( appData.gameListTags );
\r
8441 GLT_MoveSelection( hDlg, -1 );
\r
8444 case IDC_GLT_Down:
\r
8445 GLT_MoveSelection( hDlg, +1 );
\r
8455 int GameListOptions()
\r
8458 FARPROC lpProc = MakeProcInstance( (FARPROC) GameListOptions_Proc, hInst );
\r
8460 safeStrCpy( lpUserGLT, appData.gameListTags ,LPUSERGLT_SIZE );
\r
8462 result = DialogBoxParam( hInst, MAKEINTRESOURCE(DLG_GameListOptions), hwndMain, (DLGPROC)lpProc, (LPARAM)lpUserGLT );
\r
8464 if( result == 0 ) {
\r
8465 /* [AS] Memory leak here! */
\r
8466 appData.gameListTags = strdup( lpUserGLT );
\r
8473 DisplayIcsInteractionTitle(char *str)
\r
8475 char consoleTitle[MSG_SIZ];
\r
8477 snprintf(consoleTitle, MSG_SIZ, "%s: %s", szConsoleTitle, str);
\r
8478 SetWindowText(hwndConsole, consoleTitle);
\r
8482 DrawPosition(int fullRedraw, Board board)
\r
8484 HDCDrawPosition(NULL, (BOOLEAN) fullRedraw, board);
\r
8487 void NotifyFrontendLogin()
\r
8490 UpdateICSWidth(GetDlgItem(hwndConsole, OPT_ConsoleText));
\r
8496 fromX = fromY = -1;
\r
8497 if (dragInfo.pos.x != -1 || dragInfo.pos.y != -1) {
\r
8498 dragInfo.pos.x = dragInfo.pos.y = -1;
\r
8499 dragInfo.pos.x = dragInfo.pos.y = -1;
\r
8500 dragInfo.lastpos = dragInfo.pos;
\r
8501 dragInfo.start.x = dragInfo.start.y = -1;
\r
8502 dragInfo.from = dragInfo.start;
\r
8504 DrawPosition(TRUE, NULL);
\r
8511 CommentPopUp(char *title, char *str)
\r
8513 HWND hwnd = GetActiveWindow();
\r
8514 EitherCommentPopUp(currentMove, title, str, FALSE); // [HGM] vari: fake move index, rather than 0
\r
8516 SetActiveWindow(hwnd);
\r
8520 CommentPopDown(void)
\r
8522 CheckMenuItem(GetMenu(hwndMain), IDM_Comment, MF_UNCHECKED);
\r
8523 if (commentDialog) {
\r
8524 ShowWindow(commentDialog, SW_HIDE);
\r
8526 commentUp = FALSE;
\r
8530 EditCommentPopUp(int index, char *title, char *str)
\r
8532 EitherCommentPopUp(index, title, str, TRUE);
\r
8539 MyPlaySound(&sounds[(int)SoundMove]);
\r
8542 VOID PlayIcsWinSound()
\r
8544 MyPlaySound(&sounds[(int)SoundIcsWin]);
\r
8547 VOID PlayIcsLossSound()
\r
8549 MyPlaySound(&sounds[(int)SoundIcsLoss]);
\r
8552 VOID PlayIcsDrawSound()
\r
8554 MyPlaySound(&sounds[(int)SoundIcsDraw]);
\r
8557 VOID PlayIcsUnfinishedSound()
\r
8559 MyPlaySound(&sounds[(int)SoundIcsUnfinished]);
\r
8565 MyPlaySound(&sounds[(int)SoundAlarm]);
\r
8573 consoleEcho = TRUE;
\r
8574 hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
8575 SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&consoleCF);
\r
8576 SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);
\r
8585 consoleEcho = FALSE;
\r
8586 hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
8587 /* This works OK: set text and background both to the same color */
\r
8589 cf.crTextColor = COLOR_ECHOOFF;
\r
8590 SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf);
\r
8591 SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, cf.crTextColor);
\r
8594 /* No Raw()...? */
\r
8596 void Colorize(ColorClass cc, int continuation)
\r
8598 currentColorClass = cc;
\r
8599 consoleCF.dwMask = CFM_COLOR|CFM_BOLD|CFM_ITALIC|CFM_UNDERLINE|CFM_STRIKEOUT;
\r
8600 consoleCF.crTextColor = textAttribs[cc].color;
\r
8601 consoleCF.dwEffects = textAttribs[cc].effects;
\r
8602 if (!continuation) MyPlaySound(&textAttribs[cc].sound);
\r
8608 static char buf[MSG_SIZ];
\r
8609 DWORD bufsiz = MSG_SIZ;
\r
8611 if(appData.userName != NULL && appData.userName[0] != 0) {
\r
8612 return appData.userName; /* [HGM] username: prefer name selected by user over his system login */
\r
8614 if (!GetUserName(buf, &bufsiz)) {
\r
8615 /*DisplayError("Error getting user name", GetLastError());*/
\r
8616 safeStrCpy(buf, _("User"), sizeof(buf)/sizeof(buf[0]) );
\r
8624 static char buf[MSG_SIZ];
\r
8625 DWORD bufsiz = MSG_SIZ;
\r
8627 if (!GetComputerName(buf, &bufsiz)) {
\r
8628 /*DisplayError("Error getting host name", GetLastError());*/
\r
8629 safeStrCpy(buf, _("Unknown"), sizeof(buf)/sizeof(buf[0]) );
\r
8636 ClockTimerRunning()
\r
8638 return clockTimerEvent != 0;
\r
8644 if (clockTimerEvent == 0) return FALSE;
\r
8645 KillTimer(hwndMain, clockTimerEvent);
\r
8646 clockTimerEvent = 0;
\r
8651 StartClockTimer(long millisec)
\r
8653 clockTimerEvent = SetTimer(hwndMain, (UINT) CLOCK_TIMER_ID,
\r
8654 (UINT) millisec, NULL);
\r
8658 DisplayWhiteClock(long timeRemaining, int highlight)
\r
8661 char *flag = whiteFlag && gameMode == TwoMachinesPlay ? "(!)" : "";
\r
8663 if(appData.noGUI) return;
\r
8664 hdc = GetDC(hwndMain);
\r
8665 if (!IsIconic(hwndMain)) {
\r
8666 DisplayAClock(hdc, timeRemaining, highlight,
\r
8667 flipClock ? &blackRect : &whiteRect, _("White"), flag);
\r
8669 if (highlight && iconCurrent == iconBlack) {
\r
8670 iconCurrent = iconWhite;
\r
8671 PostMessage(hwndMain, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);
\r
8672 if (IsIconic(hwndMain)) {
\r
8673 DrawIcon(hdc, 2, 2, iconCurrent);
\r
8676 (void) ReleaseDC(hwndMain, hdc);
\r
8678 PostMessage(hwndConsole, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);
\r
8682 DisplayBlackClock(long timeRemaining, int highlight)
\r
8685 char *flag = blackFlag && gameMode == TwoMachinesPlay ? "(!)" : "";
\r
8687 if(appData.noGUI) return;
\r
8688 hdc = GetDC(hwndMain);
\r
8689 if (!IsIconic(hwndMain)) {
\r
8690 DisplayAClock(hdc, timeRemaining, highlight,
\r
8691 flipClock ? &whiteRect : &blackRect, _("Black"), flag);
\r
8693 if (highlight && iconCurrent == iconWhite) {
\r
8694 iconCurrent = iconBlack;
\r
8695 PostMessage(hwndMain, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);
\r
8696 if (IsIconic(hwndMain)) {
\r
8697 DrawIcon(hdc, 2, 2, iconCurrent);
\r
8700 (void) ReleaseDC(hwndMain, hdc);
\r
8702 PostMessage(hwndConsole, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);
\r
8707 LoadGameTimerRunning()
\r
8709 return loadGameTimerEvent != 0;
\r
8713 StopLoadGameTimer()
\r
8715 if (loadGameTimerEvent == 0) return FALSE;
\r
8716 KillTimer(hwndMain, loadGameTimerEvent);
\r
8717 loadGameTimerEvent = 0;
\r
8722 StartLoadGameTimer(long millisec)
\r
8724 loadGameTimerEvent = SetTimer(hwndMain, (UINT) LOAD_GAME_TIMER_ID,
\r
8725 (UINT) millisec, NULL);
\r
8733 char fileTitle[MSG_SIZ];
\r
8735 defName = DefaultFileName(appData.oldSaveStyle ? "gam" : "pgn");
\r
8736 f = OpenFileDialog(hwndMain, "a", defName,
\r
8737 appData.oldSaveStyle ? "gam" : "pgn",
\r
8739 _("Save Game to File"), NULL, fileTitle, NULL);
\r
8741 SaveGame(f, 0, "");
\r
8748 ScheduleDelayedEvent(DelayedEventCallback cb, long millisec)
\r
8750 if (delayedTimerEvent != 0) {
\r
8751 if (appData.debugMode && cb != delayedTimerCallback) { // [HGM] alive: not too much debug
\r
8752 fprintf(debugFP, "ScheduleDelayedEvent: event already scheduled\n");
\r
8754 KillTimer(hwndMain, delayedTimerEvent);
\r
8755 delayedTimerEvent = 0;
\r
8756 if(delayedTimerCallback != cb) // [HGM] alive: do not "flush" same event, just postpone it
\r
8757 delayedTimerCallback();
\r
8759 delayedTimerCallback = cb;
\r
8760 delayedTimerEvent = SetTimer(hwndMain, (UINT) DELAYED_TIMER_ID,
\r
8761 (UINT) millisec, NULL);
\r
8764 DelayedEventCallback
\r
8767 if (delayedTimerEvent) {
\r
8768 return delayedTimerCallback;
\r
8775 CancelDelayedEvent()
\r
8777 if (delayedTimerEvent) {
\r
8778 KillTimer(hwndMain, delayedTimerEvent);
\r
8779 delayedTimerEvent = 0;
\r
8783 DWORD GetWin32Priority(int nice)
\r
8784 { // [HGM] nice: translate Unix nice() value to indows priority class. (Code stolen from Polyglot 1.4w11)
\r
8786 REALTIME_PRIORITY_CLASS 0x00000100
\r
8787 HIGH_PRIORITY_CLASS 0x00000080
\r
8788 ABOVE_NORMAL_PRIORITY_CLASS 0x00008000
\r
8789 NORMAL_PRIORITY_CLASS 0x00000020
\r
8790 BELOW_NORMAL_PRIORITY_CLASS 0x00004000
\r
8791 IDLE_PRIORITY_CLASS 0x00000040
\r
8793 if (nice < -15) return 0x00000080;
\r
8794 if (nice < 0) return 0x00008000;
\r
8795 if (nice == 0) return 0x00000020;
\r
8796 if (nice < 15) return 0x00004000;
\r
8797 return 0x00000040;
\r
8800 /* Start a child process running the given program.
\r
8801 The process's standard output can be read from "from", and its
\r
8802 standard input can be written to "to".
\r
8803 Exit with fatal error if anything goes wrong.
\r
8804 Returns an opaque pointer that can be used to destroy the process
\r
8808 StartChildProcess(char *cmdLine, char *dir, ProcRef *pr)
\r
8810 #define BUFSIZE 4096
\r
8812 HANDLE hChildStdinRd, hChildStdinWr,
\r
8813 hChildStdoutRd, hChildStdoutWr;
\r
8814 HANDLE hChildStdinWrDup, hChildStdoutRdDup;
\r
8815 SECURITY_ATTRIBUTES saAttr;
\r
8817 PROCESS_INFORMATION piProcInfo;
\r
8818 STARTUPINFO siStartInfo;
\r
8820 char buf[MSG_SIZ];
\r
8823 if (appData.debugMode) {
\r
8824 fprintf(debugFP, "StartChildProcess (dir=\"%s\") %s\n", dir, cmdLine);
\r
8829 /* Set the bInheritHandle flag so pipe handles are inherited. */
\r
8830 saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);
\r
8831 saAttr.bInheritHandle = TRUE;
\r
8832 saAttr.lpSecurityDescriptor = NULL;
\r
8835 * The steps for redirecting child's STDOUT:
\r
8836 * 1. Create anonymous pipe to be STDOUT for child.
\r
8837 * 2. Create a noninheritable duplicate of read handle,
\r
8838 * and close the inheritable read handle.
\r
8841 /* Create a pipe for the child's STDOUT. */
\r
8842 if (! CreatePipe(&hChildStdoutRd, &hChildStdoutWr, &saAttr, 0)) {
\r
8843 return GetLastError();
\r
8846 /* Duplicate the read handle to the pipe, so it is not inherited. */
\r
8847 fSuccess = DuplicateHandle(GetCurrentProcess(), hChildStdoutRd,
\r
8848 GetCurrentProcess(), &hChildStdoutRdDup, 0,
\r
8849 FALSE, /* not inherited */
\r
8850 DUPLICATE_SAME_ACCESS);
\r
8852 return GetLastError();
\r
8854 CloseHandle(hChildStdoutRd);
\r
8857 * The steps for redirecting child's STDIN:
\r
8858 * 1. Create anonymous pipe to be STDIN for child.
\r
8859 * 2. Create a noninheritable duplicate of write handle,
\r
8860 * and close the inheritable write handle.
\r
8863 /* Create a pipe for the child's STDIN. */
\r
8864 if (! CreatePipe(&hChildStdinRd, &hChildStdinWr, &saAttr, 0)) {
\r
8865 return GetLastError();
\r
8868 /* Duplicate the write handle to the pipe, so it is not inherited. */
\r
8869 fSuccess = DuplicateHandle(GetCurrentProcess(), hChildStdinWr,
\r
8870 GetCurrentProcess(), &hChildStdinWrDup, 0,
\r
8871 FALSE, /* not inherited */
\r
8872 DUPLICATE_SAME_ACCESS);
\r
8874 return GetLastError();
\r
8876 CloseHandle(hChildStdinWr);
\r
8878 /* Arrange to (1) look in dir for the child .exe file, and
\r
8879 * (2) have dir be the child's working directory. Interpret
\r
8880 * dir relative to the directory WinBoard loaded from. */
\r
8881 GetCurrentDirectory(MSG_SIZ, buf);
\r
8882 SetCurrentDirectory(installDir);
\r
8883 SetCurrentDirectory(dir);
\r
8885 /* Now create the child process. */
\r
8887 siStartInfo.cb = sizeof(STARTUPINFO);
\r
8888 siStartInfo.lpReserved = NULL;
\r
8889 siStartInfo.lpDesktop = NULL;
\r
8890 siStartInfo.lpTitle = NULL;
\r
8891 siStartInfo.dwFlags = STARTF_USESTDHANDLES;
\r
8892 siStartInfo.cbReserved2 = 0;
\r
8893 siStartInfo.lpReserved2 = NULL;
\r
8894 siStartInfo.hStdInput = hChildStdinRd;
\r
8895 siStartInfo.hStdOutput = hChildStdoutWr;
\r
8896 siStartInfo.hStdError = hChildStdoutWr;
\r
8898 fSuccess = CreateProcess(NULL,
\r
8899 cmdLine, /* command line */
\r
8900 NULL, /* process security attributes */
\r
8901 NULL, /* primary thread security attrs */
\r
8902 TRUE, /* handles are inherited */
\r
8903 DETACHED_PROCESS|CREATE_NEW_PROCESS_GROUP,
\r
8904 NULL, /* use parent's environment */
\r
8906 &siStartInfo, /* STARTUPINFO pointer */
\r
8907 &piProcInfo); /* receives PROCESS_INFORMATION */
\r
8909 err = GetLastError();
\r
8910 SetCurrentDirectory(buf); /* return to prev directory */
\r
8915 if (appData.niceEngines){ // [HGM] nice: adjust engine proc priority
\r
8916 if(appData.debugMode) fprintf(debugFP, "nice engine proc to %d\n", appData.niceEngines);
\r
8917 SetPriorityClass(piProcInfo.hProcess, GetWin32Priority(appData.niceEngines));
\r
8920 /* Close the handles we don't need in the parent */
\r
8921 CloseHandle(piProcInfo.hThread);
\r
8922 CloseHandle(hChildStdinRd);
\r
8923 CloseHandle(hChildStdoutWr);
\r
8925 /* Prepare return value */
\r
8926 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
\r
8927 cp->kind = CPReal;
\r
8928 cp->hProcess = piProcInfo.hProcess;
\r
8929 cp->pid = piProcInfo.dwProcessId;
\r
8930 cp->hFrom = hChildStdoutRdDup;
\r
8931 cp->hTo = hChildStdinWrDup;
\r
8933 *pr = (void *) cp;
\r
8935 /* Klaus Friedel says that this Sleep solves a problem under Windows
\r
8936 2000 where engines sometimes don't see the initial command(s)
\r
8937 from WinBoard and hang. I don't understand how that can happen,
\r
8938 but the Sleep is harmless, so I've put it in. Others have also
\r
8939 reported what may be the same problem, so hopefully this will fix
\r
8940 it for them too. */
\r
8948 DestroyChildProcess(ProcRef pr, int/*boolean*/ signal)
\r
8950 ChildProc *cp; int result;
\r
8952 cp = (ChildProc *) pr;
\r
8953 if (cp == NULL) return;
\r
8955 switch (cp->kind) {
\r
8957 /* TerminateProcess is considered harmful, so... */
\r
8958 CloseHandle(cp->hTo); /* Closing this will give the child an EOF and hopefully kill it */
\r
8959 if (cp->hFrom) CloseHandle(cp->hFrom); /* if NULL, InputThread will close it */
\r
8960 /* The following doesn't work because the chess program
\r
8961 doesn't "have the same console" as WinBoard. Maybe
\r
8962 we could arrange for this even though neither WinBoard
\r
8963 nor the chess program uses a console for stdio? */
\r
8964 /*!!if (signal) GenerateConsoleCtrlEvent(CTRL_BREAK_EVENT, cp->pid);*/
\r
8966 /* [AS] Special termination modes for misbehaving programs... */
\r
8967 if( signal == 9 ) {
\r
8968 result = TerminateProcess( cp->hProcess, 0 );
\r
8970 if ( appData.debugMode) {
\r
8971 fprintf( debugFP, "Terminating process %lu, result=%d\n", cp->pid, result );
\r
8974 else if( signal == 10 ) {
\r
8975 DWORD dw = WaitForSingleObject( cp->hProcess, 3*1000 ); // Wait 3 seconds at most
\r
8977 if( dw != WAIT_OBJECT_0 ) {
\r
8978 result = TerminateProcess( cp->hProcess, 0 );
\r
8980 if ( appData.debugMode) {
\r
8981 fprintf( debugFP, "Process %lu still alive after timeout, killing... result=%d\n", cp->pid, result );
\r
8987 CloseHandle(cp->hProcess);
\r
8991 if (cp->hFrom) CloseHandle(cp->hFrom);
\r
8995 closesocket(cp->sock);
\r
9000 if (signal) send(cp->sock2, "\017", 1, 0); /* 017 = 15 = SIGTERM */
\r
9001 closesocket(cp->sock);
\r
9002 closesocket(cp->sock2);
\r
9010 InterruptChildProcess(ProcRef pr)
\r
9014 cp = (ChildProc *) pr;
\r
9015 if (cp == NULL) return;
\r
9016 switch (cp->kind) {
\r
9018 /* The following doesn't work because the chess program
\r
9019 doesn't "have the same console" as WinBoard. Maybe
\r
9020 we could arrange for this even though neither WinBoard
\r
9021 nor the chess program uses a console for stdio */
\r
9022 /*!!GenerateConsoleCtrlEvent(CTRL_C_EVENT, cp->pid);*/
\r
9027 /* Can't interrupt */
\r
9031 send(cp->sock2, "\002", 1, 0); /* 2 = SIGINT */
\r
9038 OpenTelnet(char *host, char *port, ProcRef *pr)
\r
9040 char cmdLine[MSG_SIZ];
\r
9042 if (port[0] == NULLCHAR) {
\r
9043 snprintf(cmdLine, MSG_SIZ, "%s %s", appData.telnetProgram, host);
\r
9045 snprintf(cmdLine, MSG_SIZ, "%s %s %s", appData.telnetProgram, host, port);
\r
9047 return StartChildProcess(cmdLine, "", pr);
\r
9051 /* Code to open TCP sockets */
\r
9054 OpenTCP(char *host, char *port, ProcRef *pr)
\r
9059 struct sockaddr_in sa, mysa;
\r
9060 struct hostent FAR *hp;
\r
9061 unsigned short uport;
\r
9062 WORD wVersionRequested;
\r
9065 /* Initialize socket DLL */
\r
9066 wVersionRequested = MAKEWORD(1, 1);
\r
9067 err = WSAStartup(wVersionRequested, &wsaData);
\r
9068 if (err != 0) return err;
\r
9071 if ((s = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {
\r
9072 err = WSAGetLastError();
\r
9077 /* Bind local address using (mostly) don't-care values.
\r
9079 memset((char *) &mysa, 0, sizeof(struct sockaddr_in));
\r
9080 mysa.sin_family = AF_INET;
\r
9081 mysa.sin_addr.s_addr = INADDR_ANY;
\r
9082 uport = (unsigned short) 0;
\r
9083 mysa.sin_port = htons(uport);
\r
9084 if (bind(s, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))
\r
9085 == SOCKET_ERROR) {
\r
9086 err = WSAGetLastError();
\r
9091 /* Resolve remote host name */
\r
9092 memset((char *) &sa, 0, sizeof(struct sockaddr_in));
\r
9093 if (!(hp = gethostbyname(host))) {
\r
9094 unsigned int b0, b1, b2, b3;
\r
9096 err = WSAGetLastError();
\r
9098 if (sscanf(host, "%u.%u.%u.%u", &b0, &b1, &b2, &b3) == 4) {
\r
9099 hp = (struct hostent *) calloc(1, sizeof(struct hostent));
\r
9100 hp->h_addrtype = AF_INET;
\r
9102 hp->h_addr_list = (char **) calloc(2, sizeof(char *));
\r
9103 hp->h_addr_list[0] = (char *) malloc(4);
\r
9104 hp->h_addr_list[0][0] = (char) b0;
\r
9105 hp->h_addr_list[0][1] = (char) b1;
\r
9106 hp->h_addr_list[0][2] = (char) b2;
\r
9107 hp->h_addr_list[0][3] = (char) b3;
\r
9113 sa.sin_family = hp->h_addrtype;
\r
9114 uport = (unsigned short) atoi(port);
\r
9115 sa.sin_port = htons(uport);
\r
9116 memcpy((char *) &sa.sin_addr, hp->h_addr, hp->h_length);
\r
9118 /* Make connection */
\r
9119 if (connect(s, (struct sockaddr *) &sa,
\r
9120 sizeof(struct sockaddr_in)) == SOCKET_ERROR) {
\r
9121 err = WSAGetLastError();
\r
9126 /* Prepare return value */
\r
9127 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
\r
9128 cp->kind = CPSock;
\r
9130 *pr = (ProcRef *) cp;
\r
9136 OpenCommPort(char *name, ProcRef *pr)
\r
9141 char fullname[MSG_SIZ];
\r
9143 if (*name != '\\')
\r
9144 snprintf(fullname, MSG_SIZ, "\\\\.\\%s", name);
\r
9146 safeStrCpy(fullname, name, sizeof(fullname)/sizeof(fullname[0]) );
\r
9148 h = CreateFile(name, GENERIC_READ | GENERIC_WRITE,
\r
9149 0, NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL);
\r
9150 if (h == (HANDLE) -1) {
\r
9151 return GetLastError();
\r
9155 if (!SetCommState(h, (LPDCB) &dcb)) return GetLastError();
\r
9157 /* Accumulate characters until a 100ms pause, then parse */
\r
9158 ct.ReadIntervalTimeout = 100;
\r
9159 ct.ReadTotalTimeoutMultiplier = 0;
\r
9160 ct.ReadTotalTimeoutConstant = 0;
\r
9161 ct.WriteTotalTimeoutMultiplier = 0;
\r
9162 ct.WriteTotalTimeoutConstant = 0;
\r
9163 if (!SetCommTimeouts(h, (LPCOMMTIMEOUTS) &ct)) return GetLastError();
\r
9165 /* Prepare return value */
\r
9166 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
\r
9167 cp->kind = CPComm;
\r
9170 *pr = (ProcRef *) cp;
\r
9176 OpenLoopback(ProcRef *pr)
\r
9178 DisplayFatalError(_("Not implemented"), 0, 1);
\r
9184 OpenRcmd(char* host, char* user, char* cmd, ProcRef* pr)
\r
9189 struct sockaddr_in sa, mysa;
\r
9190 struct hostent FAR *hp;
\r
9191 unsigned short uport;
\r
9192 WORD wVersionRequested;
\r
9195 char stderrPortStr[MSG_SIZ];
\r
9197 /* Initialize socket DLL */
\r
9198 wVersionRequested = MAKEWORD(1, 1);
\r
9199 err = WSAStartup(wVersionRequested, &wsaData);
\r
9200 if (err != 0) return err;
\r
9202 /* Resolve remote host name */
\r
9203 memset((char *) &sa, 0, sizeof(struct sockaddr_in));
\r
9204 if (!(hp = gethostbyname(host))) {
\r
9205 unsigned int b0, b1, b2, b3;
\r
9207 err = WSAGetLastError();
\r
9209 if (sscanf(host, "%u.%u.%u.%u", &b0, &b1, &b2, &b3) == 4) {
\r
9210 hp = (struct hostent *) calloc(1, sizeof(struct hostent));
\r
9211 hp->h_addrtype = AF_INET;
\r
9213 hp->h_addr_list = (char **) calloc(2, sizeof(char *));
\r
9214 hp->h_addr_list[0] = (char *) malloc(4);
\r
9215 hp->h_addr_list[0][0] = (char) b0;
\r
9216 hp->h_addr_list[0][1] = (char) b1;
\r
9217 hp->h_addr_list[0][2] = (char) b2;
\r
9218 hp->h_addr_list[0][3] = (char) b3;
\r
9224 sa.sin_family = hp->h_addrtype;
\r
9225 uport = (unsigned short) 514;
\r
9226 sa.sin_port = htons(uport);
\r
9227 memcpy((char *) &sa.sin_addr, hp->h_addr, hp->h_length);
\r
9229 /* Bind local socket to unused "privileged" port address
\r
9231 s = INVALID_SOCKET;
\r
9232 memset((char *) &mysa, 0, sizeof(struct sockaddr_in));
\r
9233 mysa.sin_family = AF_INET;
\r
9234 mysa.sin_addr.s_addr = INADDR_ANY;
\r
9235 for (fromPort = 1023;; fromPort--) {
\r
9236 if (fromPort < 0) {
\r
9238 return WSAEADDRINUSE;
\r
9240 if (s == INVALID_SOCKET) {
\r
9241 if ((s = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {
\r
9242 err = WSAGetLastError();
\r
9247 uport = (unsigned short) fromPort;
\r
9248 mysa.sin_port = htons(uport);
\r
9249 if (bind(s, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))
\r
9250 == SOCKET_ERROR) {
\r
9251 err = WSAGetLastError();
\r
9252 if (err == WSAEADDRINUSE) continue;
\r
9256 if (connect(s, (struct sockaddr *) &sa,
\r
9257 sizeof(struct sockaddr_in)) == SOCKET_ERROR) {
\r
9258 err = WSAGetLastError();
\r
9259 if (err == WSAEADDRINUSE) {
\r
9270 /* Bind stderr local socket to unused "privileged" port address
\r
9272 s2 = INVALID_SOCKET;
\r
9273 memset((char *) &mysa, 0, sizeof(struct sockaddr_in));
\r
9274 mysa.sin_family = AF_INET;
\r
9275 mysa.sin_addr.s_addr = INADDR_ANY;
\r
9276 for (fromPort = 1023;; fromPort--) {
\r
9277 if (fromPort == prevStderrPort) continue; // don't reuse port
\r
9278 if (fromPort < 0) {
\r
9279 (void) closesocket(s);
\r
9281 return WSAEADDRINUSE;
\r
9283 if (s2 == INVALID_SOCKET) {
\r
9284 if ((s2 = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {
\r
9285 err = WSAGetLastError();
\r
9291 uport = (unsigned short) fromPort;
\r
9292 mysa.sin_port = htons(uport);
\r
9293 if (bind(s2, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))
\r
9294 == SOCKET_ERROR) {
\r
9295 err = WSAGetLastError();
\r
9296 if (err == WSAEADDRINUSE) continue;
\r
9297 (void) closesocket(s);
\r
9301 if (listen(s2, 1) == SOCKET_ERROR) {
\r
9302 err = WSAGetLastError();
\r
9303 if (err == WSAEADDRINUSE) {
\r
9305 s2 = INVALID_SOCKET;
\r
9308 (void) closesocket(s);
\r
9309 (void) closesocket(s2);
\r
9315 prevStderrPort = fromPort; // remember port used
\r
9316 snprintf(stderrPortStr, MSG_SIZ, "%d", fromPort);
\r
9318 if (send(s, stderrPortStr, strlen(stderrPortStr) + 1, 0) == SOCKET_ERROR) {
\r
9319 err = WSAGetLastError();
\r
9320 (void) closesocket(s);
\r
9321 (void) closesocket(s2);
\r
9326 if (send(s, UserName(), strlen(UserName()) + 1, 0) == SOCKET_ERROR) {
\r
9327 err = WSAGetLastError();
\r
9328 (void) closesocket(s);
\r
9329 (void) closesocket(s2);
\r
9333 if (*user == NULLCHAR) user = UserName();
\r
9334 if (send(s, user, strlen(user) + 1, 0) == SOCKET_ERROR) {
\r
9335 err = WSAGetLastError();
\r
9336 (void) closesocket(s);
\r
9337 (void) closesocket(s2);
\r
9341 if (send(s, cmd, strlen(cmd) + 1, 0) == SOCKET_ERROR) {
\r
9342 err = WSAGetLastError();
\r
9343 (void) closesocket(s);
\r
9344 (void) closesocket(s2);
\r
9349 if ((s3 = accept(s2, NULL, NULL)) == INVALID_SOCKET) {
\r
9350 err = WSAGetLastError();
\r
9351 (void) closesocket(s);
\r
9352 (void) closesocket(s2);
\r
9356 (void) closesocket(s2); /* Stop listening */
\r
9358 /* Prepare return value */
\r
9359 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
\r
9360 cp->kind = CPRcmd;
\r
9363 *pr = (ProcRef *) cp;
\r
9370 AddInputSource(ProcRef pr, int lineByLine,
\r
9371 InputCallback func, VOIDSTAR closure)
\r
9373 InputSource *is, *is2 = NULL;
\r
9374 ChildProc *cp = (ChildProc *) pr;
\r
9376 is = (InputSource *) calloc(1, sizeof(InputSource));
\r
9377 is->lineByLine = lineByLine;
\r
9379 is->closure = closure;
\r
9380 is->second = NULL;
\r
9381 is->next = is->buf;
\r
9382 if (pr == NoProc) {
\r
9383 is->kind = CPReal;
\r
9384 consoleInputSource = is;
\r
9386 is->kind = cp->kind;
\r
9388 [AS] Try to avoid a race condition if the thread is given control too early:
\r
9389 we create all threads suspended so that the is->hThread variable can be
\r
9390 safely assigned, then let the threads start with ResumeThread.
\r
9392 switch (cp->kind) {
\r
9394 is->hFile = cp->hFrom;
\r
9395 cp->hFrom = NULL; /* now owned by InputThread */
\r
9397 CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) NonOvlInputThread,
\r
9398 (LPVOID) is, CREATE_SUSPENDED, &is->id);
\r
9402 is->hFile = cp->hFrom;
\r
9403 cp->hFrom = NULL; /* now owned by InputThread */
\r
9405 CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) InputThread,
\r
9406 (LPVOID) is, CREATE_SUSPENDED, &is->id);
\r
9410 is->sock = cp->sock;
\r
9412 CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,
\r
9413 (LPVOID) is, CREATE_SUSPENDED, &is->id);
\r
9417 is2 = (InputSource *) calloc(1, sizeof(InputSource));
\r
9419 is->sock = cp->sock;
\r
9421 is2->sock = cp->sock2;
\r
9422 is2->second = is2;
\r
9424 CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,
\r
9425 (LPVOID) is, CREATE_SUSPENDED, &is->id);
\r
9427 CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,
\r
9428 (LPVOID) is2, CREATE_SUSPENDED, &is2->id);
\r
9432 if( is->hThread != NULL ) {
\r
9433 ResumeThread( is->hThread );
\r
9436 if( is2 != NULL && is2->hThread != NULL ) {
\r
9437 ResumeThread( is2->hThread );
\r
9441 return (InputSourceRef) is;
\r
9445 RemoveInputSource(InputSourceRef isr)
\r
9449 is = (InputSource *) isr;
\r
9450 is->hThread = NULL; /* tell thread to stop */
\r
9451 CloseHandle(is->hThread);
\r
9452 if (is->second != NULL) {
\r
9453 is->second->hThread = NULL;
\r
9454 CloseHandle(is->second->hThread);
\r
9458 int no_wrap(char *message, int count)
\r
9460 ConsoleOutput(message, count, FALSE);
\r
9465 OutputToProcess(ProcRef pr, char *message, int count, int *outError)
\r
9468 int outCount = SOCKET_ERROR;
\r
9469 ChildProc *cp = (ChildProc *) pr;
\r
9470 static OVERLAPPED ovl;
\r
9471 static int line = 0;
\r
9475 if (appData.noJoin || !appData.useInternalWrap)
\r
9476 return no_wrap(message, count);
\r
9479 int width = get_term_width();
\r
9480 int len = wrap(NULL, message, count, width, &line);
\r
9481 char *msg = malloc(len);
\r
9485 return no_wrap(message, count);
\r
9488 dbgchk = wrap(msg, message, count, width, &line);
\r
9489 if (dbgchk != len && appData.debugMode)
\r
9490 fprintf(debugFP, "wrap(): dbgchk(%d) != len(%d)\n", dbgchk, len);
\r
9491 ConsoleOutput(msg, len, FALSE);
\r
9498 if (ovl.hEvent == NULL) {
\r
9499 ovl.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
\r
9501 ovl.Internal = ovl.InternalHigh = ovl.Offset = ovl.OffsetHigh = 0;
\r
9503 switch (cp->kind) {
\r
9506 outCount = send(cp->sock, message, count, 0);
\r
9507 if (outCount == SOCKET_ERROR) {
\r
9508 *outError = WSAGetLastError();
\r
9510 *outError = NO_ERROR;
\r
9515 if (WriteFile(((ChildProc *)pr)->hTo, message, count,
\r
9516 &dOutCount, NULL)) {
\r
9517 *outError = NO_ERROR;
\r
9518 outCount = (int) dOutCount;
\r
9520 *outError = GetLastError();
\r
9525 *outError = DoWriteFile(((ChildProc *)pr)->hTo, message, count,
\r
9526 &dOutCount, &ovl);
\r
9527 if (*outError == NO_ERROR) {
\r
9528 outCount = (int) dOutCount;
\r
9536 OutputToProcessDelayed(ProcRef pr, char *message, int count, int *outError,
\r
9539 /* Ignore delay, not implemented for WinBoard */
\r
9540 return OutputToProcess(pr, message, count, outError);
\r
9545 CmailSigHandlerCallBack(InputSourceRef isr, VOIDSTAR closure,
\r
9546 char *buf, int count, int error)
\r
9548 DisplayFatalError(_("Not implemented"), 0, 1);
\r
9551 /* see wgamelist.c for Game List functions */
\r
9552 /* see wedittags.c for Edit Tags functions */
\r
9559 char buf[MSG_SIZ];
\r
9562 if (SearchPath(installDir, appData.icsLogon, NULL, MSG_SIZ, buf, &dummy)) {
\r
9563 f = fopen(buf, "r");
\r
9565 ProcessICSInitScript(f);
\r
9573 StartAnalysisClock()
\r
9575 if (analysisTimerEvent) return;
\r
9576 analysisTimerEvent = SetTimer(hwndMain, (UINT) ANALYSIS_TIMER_ID,
\r
9577 (UINT) 2000, NULL);
\r
9581 SetHighlights(int fromX, int fromY, int toX, int toY)
\r
9583 highlightInfo.sq[0].x = fromX;
\r
9584 highlightInfo.sq[0].y = fromY;
\r
9585 highlightInfo.sq[1].x = toX;
\r
9586 highlightInfo.sq[1].y = toY;
\r
9592 highlightInfo.sq[0].x = highlightInfo.sq[0].y =
\r
9593 highlightInfo.sq[1].x = highlightInfo.sq[1].y = -1;
\r
9597 SetPremoveHighlights(int fromX, int fromY, int toX, int toY)
\r
9599 premoveHighlightInfo.sq[0].x = fromX;
\r
9600 premoveHighlightInfo.sq[0].y = fromY;
\r
9601 premoveHighlightInfo.sq[1].x = toX;
\r
9602 premoveHighlightInfo.sq[1].y = toY;
\r
9606 ClearPremoveHighlights()
\r
9608 premoveHighlightInfo.sq[0].x = premoveHighlightInfo.sq[0].y =
\r
9609 premoveHighlightInfo.sq[1].x = premoveHighlightInfo.sq[1].y = -1;
\r
9613 ShutDownFrontEnd()
\r
9615 if (saveSettingsOnExit) SaveSettings(settingsFileName);
\r
9616 DeleteClipboardTempFiles();
\r
9622 if (IsIconic(hwndMain))
\r
9623 ShowWindow(hwndMain, SW_RESTORE);
\r
9625 SetActiveWindow(hwndMain);
\r
9629 * Prototypes for animation support routines
\r
9631 static void ScreenSquare(int column, int row, POINT * pt);
\r
9632 static void Tween( POINT * start, POINT * mid, POINT * finish, int factor,
\r
9633 POINT frames[], int * nFrames);
\r
9639 AnimateAtomicCapture(Board board, int fromX, int fromY, int toX, int toY)
\r
9640 { // [HGM] atomic: animate blast wave
\r
9643 explodeInfo.fromX = fromX;
\r
9644 explodeInfo.fromY = fromY;
\r
9645 explodeInfo.toX = toX;
\r
9646 explodeInfo.toY = toY;
\r
9647 for(i=1; i<4*kFactor; i++) {
\r
9648 explodeInfo.radius = (i*180)/(4*kFactor-1);
\r
9649 DrawPosition(FALSE, board);
\r
9650 Sleep(appData.animSpeed);
\r
9652 explodeInfo.radius = 0;
\r
9653 DrawPosition(TRUE, board);
\r
9657 AnimateMove(board, fromX, fromY, toX, toY)
\r
9664 ChessSquare piece;
\r
9665 POINT start, finish, mid;
\r
9666 POINT frames[kFactor * 2 + 1];
\r
9669 if (!appData.animate) return;
\r
9670 if (doingSizing) return;
\r
9671 if (fromY < 0 || fromX < 0) return;
\r
9672 piece = board[fromY][fromX];
\r
9673 if (piece >= EmptySquare) return;
\r
9675 ScreenSquare(fromX, fromY, &start);
\r
9676 ScreenSquare(toX, toY, &finish);
\r
9678 /* All moves except knight jumps move in straight line */
\r
9679 if (!(abs(fromX-toX) == 1 && abs(fromY-toY) == 2 || abs(fromX-toX) == 2 && abs(fromY-toY) == 1)) {
\r
9680 mid.x = start.x + (finish.x - start.x) / 2;
\r
9681 mid.y = start.y + (finish.y - start.y) / 2;
\r
9683 /* Knight: make straight movement then diagonal */
\r
9684 if (abs(toY - fromY) < abs(toX - fromX)) {
\r
9685 mid.x = start.x + (finish.x - start.x) / 2;
\r
9689 mid.y = start.y + (finish.y - start.y) / 2;
\r
9693 /* Don't use as many frames for very short moves */
\r
9694 if (abs(toY - fromY) + abs(toX - fromX) <= 2)
\r
9695 Tween(&start, &mid, &finish, kFactor - 1, frames, &nFrames);
\r
9697 Tween(&start, &mid, &finish, kFactor, frames, &nFrames);
\r
9699 animInfo.from.x = fromX;
\r
9700 animInfo.from.y = fromY;
\r
9701 animInfo.to.x = toX;
\r
9702 animInfo.to.y = toY;
\r
9703 animInfo.lastpos = start;
\r
9704 animInfo.piece = piece;
\r
9705 for (n = 0; n < nFrames; n++) {
\r
9706 animInfo.pos = frames[n];
\r
9707 DrawPosition(FALSE, NULL);
\r
9708 animInfo.lastpos = animInfo.pos;
\r
9709 Sleep(appData.animSpeed);
\r
9711 animInfo.pos = finish;
\r
9712 DrawPosition(FALSE, NULL);
\r
9713 animInfo.piece = EmptySquare;
\r
9714 Explode(board, fromX, fromY, toX, toY);
\r
9717 /* Convert board position to corner of screen rect and color */
\r
9720 ScreenSquare(column, row, pt)
\r
9721 int column; int row; POINT * pt;
\r
9724 pt->x = lineGap + ((BOARD_WIDTH-1)-column) * (squareSize + lineGap);
\r
9725 pt->y = lineGap + row * (squareSize + lineGap);
\r
9727 pt->x = lineGap + column * (squareSize + lineGap);
\r
9728 pt->y = lineGap + ((BOARD_HEIGHT-1)-row) * (squareSize + lineGap);
\r
9732 /* Generate a series of frame coords from start->mid->finish.
\r
9733 The movement rate doubles until the half way point is
\r
9734 reached, then halves back down to the final destination,
\r
9735 which gives a nice slow in/out effect. The algorithmn
\r
9736 may seem to generate too many intermediates for short
\r
9737 moves, but remember that the purpose is to attract the
\r
9738 viewers attention to the piece about to be moved and
\r
9739 then to where it ends up. Too few frames would be less
\r
9743 Tween(start, mid, finish, factor, frames, nFrames)
\r
9744 POINT * start; POINT * mid;
\r
9745 POINT * finish; int factor;
\r
9746 POINT frames[]; int * nFrames;
\r
9748 int n, fraction = 1, count = 0;
\r
9750 /* Slow in, stepping 1/16th, then 1/8th, ... */
\r
9751 for (n = 0; n < factor; n++)
\r
9753 for (n = 0; n < factor; n++) {
\r
9754 frames[count].x = start->x + (mid->x - start->x) / fraction;
\r
9755 frames[count].y = start->y + (mid->y - start->y) / fraction;
\r
9757 fraction = fraction / 2;
\r
9761 frames[count] = *mid;
\r
9764 /* Slow out, stepping 1/2, then 1/4, ... */
\r
9766 for (n = 0; n < factor; n++) {
\r
9767 frames[count].x = finish->x - (finish->x - mid->x) / fraction;
\r
9768 frames[count].y = finish->y - (finish->y - mid->y) / fraction;
\r
9770 fraction = fraction * 2;
\r
9776 HistorySet( char movelist[][2*MOVE_LEN], int first, int last, int current )
\r
9778 MoveHistorySet( movelist, first, last, current, pvInfoList );
\r
9780 EvalGraphSet( first, last, current, pvInfoList );
\r
9784 SettingsPopUp(ChessProgramState *cps)
\r
9785 { // [HGM] wrapper needed because handles must not be passed through back-end
\r
9786 EngineOptionsPopup(savedHwnd, cps);
\r