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
128 static DragInfo dragInfo = { {-1,-1}, {-1,-1}, {-1,-1}, {-1,-1} };
\r
131 POINT sq[2]; /* board coordinates of from, to squares */
\r
134 static HighlightInfo highlightInfo = { {{-1, -1}, {-1, -1}} };
\r
135 static HighlightInfo premoveHighlightInfo = { {{-1, -1}, {-1, -1}} };
\r
136 static HighlightInfo partnerHighlightInfo = { {{-1, -1}, {-1, -1}} };
\r
137 static HighlightInfo oldPartnerHighlight = { {{-1, -1}, {-1, -1}} };
\r
139 typedef struct { // [HGM] atomic
\r
140 int fromX, fromY, toX, toY, radius;
\r
143 static ExplodeInfo explodeInfo;
\r
145 /* Window class names */
\r
146 char szAppName[] = "WinBoard";
\r
147 char szConsoleName[] = "WBConsole";
\r
149 /* Title bar text */
\r
150 char szTitle[] = "WinBoard";
\r
151 char szConsoleTitle[] = "I C S Interaction";
\r
154 char *settingsFileName;
\r
155 Boolean saveSettingsOnExit;
\r
156 char installDir[MSG_SIZ];
\r
157 int errorExitStatus;
\r
159 BoardSize boardSize;
\r
160 Boolean chessProgram;
\r
161 //static int boardX, boardY;
\r
162 int minX, minY; // [HGM] placement: volatile limits on upper-left corner
\r
163 int squareSize, lineGap, minorSize;
\r
164 static int winW, winH;
\r
165 static RECT messageRect, whiteRect, blackRect, leftLogoRect, rightLogoRect; // [HGM] logo
\r
166 static int logoHeight = 0;
\r
167 static char messageText[MESSAGE_TEXT_MAX];
\r
168 static int clockTimerEvent = 0;
\r
169 static int loadGameTimerEvent = 0;
\r
170 static int analysisTimerEvent = 0;
\r
171 static DelayedEventCallback delayedTimerCallback;
\r
172 static int delayedTimerEvent = 0;
\r
173 static int buttonCount = 2;
\r
174 char *icsTextMenuString;
\r
176 char *firstChessProgramNames;
\r
177 char *secondChessProgramNames;
\r
179 #define PALETTESIZE 256
\r
181 HINSTANCE hInst; /* current instance */
\r
182 Boolean alwaysOnTop = FALSE;
\r
184 COLORREF lightSquareColor, darkSquareColor, whitePieceColor,
\r
185 blackPieceColor, highlightSquareColor, premoveHighlightColor;
\r
187 ColorClass currentColorClass;
\r
189 static HWND savedHwnd;
\r
190 HWND hCommPort = NULL; /* currently open comm port */
\r
191 static HWND hwndPause; /* pause button */
\r
192 static HBITMAP pieceBitmap[3][(int) BlackPawn]; /* [HGM] nr of bitmaps referred to bP in stead of wK */
\r
193 static HBRUSH lightSquareBrush, darkSquareBrush,
\r
194 blackSquareBrush, /* [HGM] for band between board and holdings */
\r
195 explodeBrush, /* [HGM] atomic */
\r
196 markerBrush, /* [HGM] markers */
\r
197 whitePieceBrush, blackPieceBrush, iconBkgndBrush /*, outlineBrush*/;
\r
198 static POINT gridEndpoints[(BOARD_RANKS + BOARD_FILES + 2) * 2];
\r
199 static DWORD gridVertexCounts[BOARD_RANKS + BOARD_FILES + 2];
\r
200 static HPEN gridPen = NULL;
\r
201 static HPEN highlightPen = NULL;
\r
202 static HPEN premovePen = NULL;
\r
203 static NPLOGPALETTE pLogPal;
\r
204 static BOOL paletteChanged = FALSE;
\r
205 static HICON iconWhite, iconBlack, iconCurrent;
\r
206 static int doingSizing = FALSE;
\r
207 static int lastSizing = 0;
\r
208 static int prevStderrPort;
\r
209 static HBITMAP userLogo;
\r
211 static HBITMAP liteBackTexture = NULL;
\r
212 static HBITMAP darkBackTexture = NULL;
\r
213 static int liteBackTextureMode = BACK_TEXTURE_MODE_PLAIN;
\r
214 static int darkBackTextureMode = BACK_TEXTURE_MODE_PLAIN;
\r
215 static int backTextureSquareSize = 0;
\r
216 static struct { int x; int y; int mode; } backTextureSquareInfo[BOARD_RANKS][BOARD_FILES];
\r
218 #if __GNUC__ && !defined(_winmajor)
\r
219 #define oldDialog 0 /* cygwin doesn't define _winmajor; mingw does */
\r
221 #if defined(_winmajor)
\r
222 #define oldDialog (_winmajor < 4)
\r
224 #define oldDialog 0
\r
228 #define INTERNATIONAL
\r
230 #ifdef INTERNATIONAL
\r
231 # define _(s) T_(s)
\r
237 # define Translate(x, y)
\r
238 # define LoadLanguageFile(s)
\r
241 #ifdef INTERNATIONAL
\r
243 Boolean barbaric; // flag indicating if translation is needed
\r
245 // list of item numbers used in each dialog (used to alter language at run time)
\r
247 #define ABOUTBOX -1 /* not sure why these are needed */
\r
248 #define ABOUTBOX2 -1
\r
250 int dialogItems[][40] = {
\r
251 { ABOUTBOX, IDOK, OPT_MESS, 400 },
\r
252 { DLG_TimeControl, IDC_Babble, OPT_TCUseMoves, OPT_TCUseInc, OPT_TCUseFixed,
\r
253 OPT_TCtext1, OPT_TCtext2, OPT_TCitext1, OPT_TCitext2, OPT_TCftext, GPB_Factors, IDC_Factor1, IDC_Factor2, IDOK, IDCANCEL },
\r
254 { DLG_LoadOptions, OPT_Autostep, OPT_AStext1, IDOK, IDCANCEL },
\r
255 { DLG_SaveOptions, OPT_Autosave, OPT_AVPrompt, OPT_AVToFile, OPT_AVBrowse,
\r
256 801, OPT_PGN, OPT_Old, OPT_OutOfBookInfo, IDOK, IDCANCEL },
\r
257 { 1536, 1090, IDC_Directories, 1089, 1091, IDOK, IDCANCEL, 1038, IDC_IndexNr, 1037 },
\r
258 { DLG_CommPort, IDOK, IDCANCEL, IDC_Port, IDC_Rate, IDC_Bits, IDC_Parity,
\r
259 IDC_Stop, IDC_Flow, OPT_SerialHelp },
\r
260 { DLG_EditComment, IDOK, OPT_CancelComment, OPT_ClearComment, OPT_EditComment },
\r
261 { DLG_PromotionKing, PB_Chancellor, PB_Archbishop, PB_Queen, PB_Rook,
\r
262 PB_Bishop, PB_Knight, PB_King, IDCANCEL, IDC_Yes, IDC_No, IDC_Centaur },
\r
263 { ABOUTBOX2, IDC_ChessBoard },
\r
264 { DLG_GameList, OPT_GameListLoad, OPT_GameListPrev, OPT_GameListNext,
\r
265 OPT_GameListClose, IDC_GameListDoFilter },
\r
266 { DLG_EditTags, IDOK, OPT_TagsCancel, OPT_EditTags },
\r
267 { DLG_Error, IDOK },
\r
268 { DLG_Colorize, IDOK, IDCANCEL, OPT_ChooseColor, OPT_Bold, OPT_Italic,
\r
269 OPT_Underline, OPT_Strikeout, OPT_Sample },
\r
270 { DLG_Question, IDOK, IDCANCEL, OPT_QuestionText },
\r
271 { DLG_Startup, IDC_Welcome, OPT_ChessEngine, OPT_ChessServer, OPT_View,
\r
272 IDC_SPECIFY_ENG_STATIC, IDC_SPECIFY_SERVER_STATIC, OPT_AnyAdditional,
\r
273 IDOK, IDCANCEL, IDM_HELPCONTENTS },
\r
274 { DLG_IndexNumber, IDC_Index },
\r
275 { DLG_TypeInMove, IDOK, IDCANCEL },
\r
276 { DLG_TypeInName, IDOK, IDCANCEL },
\r
277 { DLG_Sound, IDC_Event, OPT_NoSound, OPT_DefaultBeep, OPT_BuiltInSound,
\r
278 OPT_WavFile, OPT_BrowseSound, OPT_DefaultSounds, IDOK, IDCANCEL, OPT_PlaySound },
\r
279 { DLG_GeneralOptions, IDOK, IDCANCEL, OPT_AlwaysOnTop, OPT_HighlightLastMove,
\r
280 OPT_AlwaysQueen, OPT_PeriodicUpdates, OPT_AnimateDragging, OPT_PonderNextMove,
\r
281 OPT_AnimateMoving, OPT_PopupExitMessage, OPT_AutoFlag, OPT_PopupMoveErrors,
\r
282 OPT_AutoFlipView, OPT_ShowButtonBar, OPT_AutoRaiseBoard, OPT_ShowCoordinates,
\r
283 OPT_Blindfold, OPT_ShowThinking, OPT_HighlightDragging, OPT_TestLegality,
\r
284 OPT_SaveExtPGN, OPT_HideThinkFromHuman, OPT_ExtraInfoInMoveHistory,
\r
285 OPT_HighlightMoveArrow, OPT_AutoLogo ,OPT_SmartMove },
\r
286 { DLG_IcsOptions, IDOK, IDCANCEL, OPT_AutoComment, OPT_AutoKibitz, OPT_AutoObserve,
\r
287 OPT_GetMoveList, OPT_LocalLineEditing, OPT_QuietPlay, OPT_SeekGraph, OPT_AutoRefresh,
\r
288 OPT_BgObserve, OPT_DualBoard, OPT_Premove, OPT_PremoveWhite, OPT_PremoveBlack,
\r
289 OPT_SmartMove, OPT_IcsAlarm, IDC_Sec, OPT_ChooseShoutColor, OPT_ChooseSShoutColor,
\r
290 OPT_ChooseChannel1Color, OPT_ChooseChannelColor, OPT_ChooseKibitzColor,
\r
291 OPT_ChooseTellColor, OPT_ChooseChallengeColor, OPT_ChooseRequestColor,
\r
292 OPT_ChooseSeekColor, OPT_ChooseNormalColor, OPT_ChooseBackgroundColor,
\r
293 OPT_DefaultColors, OPT_DontColorize, IDC_Boxes, GPB_Colors, GPB_Premove,
\r
294 GPB_General, GPB_Alarm },
\r
295 { DLG_BoardOptions, IDOK, IDCANCEL, OPT_SizeTiny, OPT_SizeTeeny, OPT_SizeDinky,
\r
296 OPT_SizePetite, OPT_SizeSlim, OPT_SizeSmall, OPT_SizeMediocre, OPT_SizeMiddling,
\r
297 OPT_SizeAverage, OPT_SizeModerate, OPT_SizeMedium, OPT_SizeBulky, OPT_SizeLarge,
\r
298 OPT_SizeBig, OPT_SizeHuge, OPT_SizeGiant, OPT_SizeColossal, OPT_SizeTitanic,
\r
299 OPT_ChooseLightSquareColor, OPT_ChooseDarkSquareColor, OPT_ChooseWhitePieceColor,
\r
300 OPT_ChooseBlackPieceColor, OPT_ChooseHighlightSquareColor, OPT_ChoosePremoveHighlightColor,
\r
301 OPT_Monochrome, OPT_AllWhite, OPT_UpsideDown, OPT_DefaultBoardColors, GPB_Colors,
\r
302 IDC_Light, IDC_Dark, IDC_White, IDC_Black, IDC_High, IDC_PreHigh, GPB_Size },
\r
303 { DLG_NewVariant, IDOK, IDCANCEL, OPT_VariantNormal, OPT_VariantFRC, OPT_VariantWildcastle,
\r
304 OPT_VariantNocastle, OPT_VariantLosers, OPT_VariantGiveaway, OPT_VariantSuicide,
\r
305 OPT_Variant3Check, OPT_VariantTwoKings, OPT_VariantAtomic, OPT_VariantCrazyhouse,
\r
306 OPT_VariantBughouse, OPT_VariantTwilight, OPT_VariantShogi, OPT_VariantSuper,
\r
307 OPT_VariantKnightmate, OPT_VariantBerolina, OPT_VariantCylinder, OPT_VariantFairy,
\r
308 OPT_VariantMakruk, OPT_VariantGothic, OPT_VariantCapablanca, OPT_VariantJanus,
\r
309 OPT_VariantCRC, OPT_VariantFalcon, OPT_VariantCourier, OPT_VariantGreat, OPT_VariantSChess,
\r
310 OPT_VariantShatranj, OPT_VariantXiangqi, GPB_Variant, GPB_Board, IDC_Height,
\r
311 IDC_Width, IDC_Hand, IDC_Pieces, IDC_Def },
\r
312 { DLG_Fonts, IDOK, IDCANCEL, OPT_ChooseClockFont, OPT_ChooseMessageFont,
\r
313 OPT_ChooseCoordFont, OPT_ChooseTagFont, OPT_ChooseCommentsFont, OPT_ChooseConsoleFont, OPT_ChooseMoveHistoryFont, OPT_DefaultFonts,
\r
314 OPT_ClockFont, OPT_MessageFont, OPT_CoordFont, OPT_EditTagsFont,
\r
315 OPT_CommentsFont, OPT_MessageFont5, GPB_Current, GPB_All, OPT_MessageFont6 },
\r
316 { DLG_NewGameFRC, IDC_NFG_Label, IDC_NFG_Random, IDOK, IDCANCEL },
\r
317 { DLG_GameListOptions, IDC_GLT, IDC_GLT_Up, IDC_GLT_Down, IDC_GLT_Restore,
\r
318 IDC_GLT_Default, IDOK, IDCANCEL, IDC_GLT_RestoreTo },
\r
319 { DLG_MoveHistory },
\r
320 { DLG_EvalGraph },
\r
321 { DLG_EngineOutput, IDC_EngineLabel1, IDC_Engine1_NPS, IDC_EngineLabel2, IDC_Engine2_NPS },
\r
322 { DLG_Chat, IDC_Partner, IDC_Clear, IDC_Send, },
\r
323 { DLG_EnginePlayOptions, IDC_EpPonder, IDC_EpShowThinking, IDC_EpHideThinkingHuman,
\r
324 IDC_EpPeriodicUpdates, GPB_Adjudications, IDC_Draw, IDC_Moves, IDC_Threshold,
\r
325 IDC_Centi, IDC_TestClaims, IDC_DetectMates, IDC_MaterialDraws, IDC_TrivialDraws,
\r
326 GPB_Apply, IDC_Rule, IDC_Repeats, IDC_ScoreAbs1, IDC_ScoreAbs2, IDOK, IDCANCEL },
\r
327 { DLG_OptionsUCI, IDC_PolyDir, IDC_BrowseForPolyglotDir, IDC_Hash, IDC_Path,
\r
328 IDC_BrowseForEGTB, IDC_Cache, IDC_UseBook, IDC_BrowseForBook, IDC_CPU, IDC_OwnBook1,
\r
329 IDC_OwnBook2, IDC_Depth, IDC_Variation, IDC_DefGames, IDOK, IDCANCEL },
\r
333 static char languageBuf[50000], *foreign[1000], *english[1000], *languageFile[MSG_SIZ];
\r
334 static int lastChecked;
\r
335 static char oldLanguage[MSG_SIZ], *menuText[10][30];
\r
336 extern int tinyLayout;
\r
337 extern char * menuBarText[][10];
\r
340 LoadLanguageFile(char *name)
\r
341 { //load the file with translations, and make a list of the strings to be translated, and their translations
\r
343 int i=0, j=0, n=0, k;
\r
346 if(!name || name[0] == NULLCHAR) return;
\r
347 snprintf(buf, MSG_SIZ, "%s%s", name, strchr(name, '.') ? "" : ".lng"); // auto-append lng extension
\r
348 appData.language = oldLanguage;
\r
349 if(!strcmp(buf, oldLanguage)) { barbaric = 1; return; } // this language already loaded; just switch on
\r
350 if((f = fopen(buf, "r")) == NULL) return;
\r
351 while((k = fgetc(f)) != EOF) {
\r
352 if(i >= sizeof(languageBuf)) { DisplayError("Language file too big", 0); return; }
\r
353 languageBuf[i] = k;
\r
355 if(languageBuf[n] == '"' && languageBuf[i-1] == '"') {
\r
357 if(p = strstr(languageBuf + n + 1, "\" === \"")) {
\r
358 if(p > languageBuf+n+2 && p+8 < languageBuf+i) {
\r
359 if(j >= sizeof(english)) { DisplayError("Too many translated strings", 0); return; }
\r
360 english[j] = languageBuf + n + 1; *p = 0;
\r
361 foreign[j++] = p + 7; languageBuf[i-1] = 0;
\r
362 //if(appData.debugMode) fprintf(debugFP, "translation: replace '%s' by '%s'\n", english[j-1], foreign[j-1]);
\r
367 } else if(i > 0 && languageBuf[i-1] == '\\') {
\r
369 case 'n': k = '\n'; break;
\r
370 case 'r': k = '\r'; break;
\r
371 case 't': k = '\t'; break;
\r
373 languageBuf[--i] = k;
\r
378 barbaric = (j != 0);
\r
379 safeStrCpy(oldLanguage, buf, sizeof(oldLanguage)/sizeof(oldLanguage[0]) );
\r
384 { // return the translation of the given string
\r
385 // efficiency can be improved a lot...
\r
387 //if(appData.debugMode) fprintf(debugFP, "T_(%s)\n", s);
\r
388 if(!barbaric) return s;
\r
389 if(!s) return ""; // sanity
\r
390 while(english[i]) {
\r
391 if(!strcmp(s, english[i])) return foreign[i];
\r
398 Translate(HWND hDlg, int dialogID)
\r
399 { // translate all text items in the given dialog
\r
401 char buf[MSG_SIZ], *s;
\r
402 if(!barbaric) return;
\r
403 while(dialogItems[i][0] && dialogItems[i][0] != dialogID) i++; // find the dialog description
\r
404 if(dialogItems[i][0] != dialogID) return; // unknown dialog, should not happen
\r
405 GetWindowText( hDlg, buf, MSG_SIZ );
\r
407 if(strcmp(buf, s)) SetWindowText(hDlg, s); // replace by translated string (if different)
\r
408 for(j=1; k=dialogItems[i][j]; j++) { // translate all listed dialog items
\r
409 GetDlgItemText(hDlg, k, buf, MSG_SIZ);
\r
410 if(strlen(buf) == 0) continue;
\r
412 if(strcmp(buf, s)) SetDlgItemText(hDlg, k, s); // replace by translated string (if different)
\r
417 TranslateOneMenu(int i, HMENU subMenu)
\r
420 static MENUITEMINFO info;
\r
422 info.cbSize = sizeof(MENUITEMINFO);
\r
423 info.fMask = MIIM_STATE | MIIM_TYPE;
\r
424 for(j=GetMenuItemCount(subMenu)-1; j>=0; j--){
\r
426 info.dwTypeData = buf;
\r
427 info.cch = sizeof(buf);
\r
428 GetMenuItemInfo(subMenu, j, TRUE, &info);
\r
430 if(menuText[i][j]) safeStrCpy(buf, menuText[i][j], sizeof(buf)/sizeof(buf[0]) );
\r
431 else menuText[i][j] = strdup(buf); // remember original on first change
\r
433 if(buf[0] == NULLCHAR) continue;
\r
434 info.dwTypeData = T_(buf);
\r
435 info.cch = strlen(buf)+1;
\r
436 SetMenuItemInfo(subMenu, j, TRUE, &info);
\r
442 TranslateMenus(int addLanguage)
\r
445 WIN32_FIND_DATA fileData;
\r
447 #define IDM_English 1970
\r
449 HMENU mainMenu = GetMenu(hwndMain);
\r
450 for (i=GetMenuItemCount(mainMenu)-1; i>=0; i--) {
\r
451 HMENU subMenu = GetSubMenu(mainMenu, i);
\r
452 ModifyMenu(mainMenu, i, MF_STRING|MF_BYPOSITION|MF_POPUP|EnableMenuItem(mainMenu, i, MF_BYPOSITION),
\r
453 (UINT) subMenu, T_(menuBarText[tinyLayout][i]));
\r
454 TranslateOneMenu(i, subMenu);
\r
456 DrawMenuBar(hwndMain);
\r
459 if(!addLanguage) return;
\r
460 if((hFind = FindFirstFile("*.LNG", &fileData)) != INVALID_HANDLE_VALUE) {
\r
461 HMENU mainMenu = GetMenu(hwndMain);
\r
462 HMENU subMenu = GetSubMenu(mainMenu, GetMenuItemCount(mainMenu)-1);
\r
463 AppendMenu(subMenu, MF_SEPARATOR, (UINT_PTR) 0, NULL);
\r
464 AppendMenu(subMenu, MF_ENABLED|MF_STRING|(barbaric?MF_UNCHECKED:MF_CHECKED), (UINT_PTR) IDM_English, (LPCTSTR) "English");
\r
465 i = 0; lastChecked = IDM_English;
\r
467 char *p, *q = fileData.cFileName;
\r
468 int checkFlag = MF_UNCHECKED;
\r
469 languageFile[i] = strdup(q);
\r
470 if(barbaric && !strcmp(oldLanguage, q)) {
\r
471 checkFlag = MF_CHECKED;
\r
472 lastChecked = IDM_English + i + 1;
\r
473 CheckMenuItem(mainMenu, IDM_English, MF_BYCOMMAND|MF_UNCHECKED);
\r
475 *q = ToUpper(*q); while(*++q) *q = ToLower(*q);
\r
476 p = strstr(fileData.cFileName, ".lng");
\r
478 AppendMenu(subMenu, MF_ENABLED|MF_STRING|checkFlag, (UINT_PTR) IDM_English + ++i, (LPCTSTR) fileData.cFileName);
\r
479 } while(FindNextFile(hFind, &fileData));
\r
492 int cliWidth, cliHeight;
\r
495 SizeInfo sizeInfo[] =
\r
497 { "tiny", 21, 0, 1, 1, 0, 0 },
\r
498 { "teeny", 25, 1, 1, 1, 0, 0 },
\r
499 { "dinky", 29, 1, 1, 1, 0, 0 },
\r
500 { "petite", 33, 1, 1, 1, 0, 0 },
\r
501 { "slim", 37, 2, 1, 0, 0, 0 },
\r
502 { "small", 40, 2, 1, 0, 0, 0 },
\r
503 { "mediocre", 45, 2, 1, 0, 0, 0 },
\r
504 { "middling", 49, 2, 0, 0, 0, 0 },
\r
505 { "average", 54, 2, 0, 0, 0, 0 },
\r
506 { "moderate", 58, 3, 0, 0, 0, 0 },
\r
507 { "medium", 64, 3, 0, 0, 0, 0 },
\r
508 { "bulky", 72, 3, 0, 0, 0, 0 },
\r
509 { "large", 80, 3, 0, 0, 0, 0 },
\r
510 { "big", 87, 3, 0, 0, 0, 0 },
\r
511 { "huge", 95, 3, 0, 0, 0, 0 },
\r
512 { "giant", 108, 3, 0, 0, 0, 0 },
\r
513 { "colossal", 116, 4, 0, 0, 0, 0 },
\r
514 { "titanic", 129, 4, 0, 0, 0, 0 },
\r
515 { NULL, 0, 0, 0, 0, 0, 0 }
\r
518 #define MF(x) {x, {{0,}, 0. }, {0, }, 0}
\r
519 MyFont fontRec[NUM_SIZES][NUM_FONTS] =
\r
521 { 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
522 { 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
523 { 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
524 { 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
525 { 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
526 { 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
527 { 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
528 { 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
529 { 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
530 { 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
531 { 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
532 { 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
533 { 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
534 { 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
535 { 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
536 { 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
537 { 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
538 { 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
541 MyFont *font[NUM_SIZES][NUM_FONTS];
\r
550 #define BUTTON_WIDTH (tinyLayout ? 16 : 32)
\r
551 #define N_BUTTONS 5
\r
553 MyButtonDesc buttonDesc[N_BUTTONS] =
\r
555 {"<<", IDM_ToStart, NULL, NULL},
\r
556 {"<", IDM_Backward, NULL, NULL},
\r
557 {"P", IDM_Pause, NULL, NULL},
\r
558 {">", IDM_Forward, NULL, NULL},
\r
559 {">>", IDM_ToEnd, NULL, NULL},
\r
562 int tinyLayout = 0, smallLayout = 0;
\r
563 #define MENU_BAR_ITEMS 9
\r
564 char *menuBarText[2][MENU_BAR_ITEMS+1] = {
\r
565 { N_("&File"), N_("&Edit"), N_("&View"), N_("&Mode"), N_("&Action"), N_("E&ngine"), N_("&Options"), N_("&Help"), NULL },
\r
566 { N_("&F"), N_("&E"), N_("&V"), N_("&M"), N_("&A"), N_("&N"), N_("&O"), N_("&H"), NULL },
\r
570 MySound sounds[(int)NSoundClasses];
\r
571 MyTextAttribs textAttribs[(int)NColorClasses];
\r
573 MyColorizeAttribs colorizeAttribs[] = {
\r
574 { (COLORREF)0, 0, N_("Shout Text") },
\r
575 { (COLORREF)0, 0, N_("SShout/CShout") },
\r
576 { (COLORREF)0, 0, N_("Channel 1 Text") },
\r
577 { (COLORREF)0, 0, N_("Channel Text") },
\r
578 { (COLORREF)0, 0, N_("Kibitz Text") },
\r
579 { (COLORREF)0, 0, N_("Tell Text") },
\r
580 { (COLORREF)0, 0, N_("Challenge Text") },
\r
581 { (COLORREF)0, 0, N_("Request Text") },
\r
582 { (COLORREF)0, 0, N_("Seek Text") },
\r
583 { (COLORREF)0, 0, N_("Normal Text") },
\r
584 { (COLORREF)0, 0, N_("None") }
\r
589 static char *commentTitle;
\r
590 static char *commentText;
\r
591 static int commentIndex;
\r
592 static Boolean editComment = FALSE;
\r
595 char errorTitle[MSG_SIZ];
\r
596 char errorMessage[2*MSG_SIZ];
\r
597 HWND errorDialog = NULL;
\r
598 BOOLEAN moveErrorMessageUp = FALSE;
\r
599 BOOLEAN consoleEcho = TRUE;
\r
600 CHARFORMAT consoleCF;
\r
601 COLORREF consoleBackgroundColor;
\r
603 char *programVersion;
\r
609 typedef int CPKind;
\r
618 SOCKET sock2; /* stderr socket for OpenRcmd */
\r
621 #define INPUT_SOURCE_BUF_SIZE 4096
\r
623 typedef struct _InputSource {
\r
630 char buf[INPUT_SOURCE_BUF_SIZE];
\r
634 InputCallback func;
\r
635 struct _InputSource *second; /* for stderr thread on CPRcmd */
\r
639 InputSource *consoleInputSource;
\r
644 VOID ConsoleOutput(char* data, int length, int forceVisible);
\r
645 VOID ConsoleCreate();
\r
647 ConsoleWndProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam);
\r
648 VOID ColorizeTextPopup(HWND hwnd, ColorClass cc);
\r
649 VOID PrintCommSettings(FILE *f, char *name, DCB *dcb);
\r
650 VOID ParseCommSettings(char *arg, DCB *dcb);
\r
652 StartupDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam);
\r
653 VOID APIENTRY MenuPopup(HWND hwnd, POINT pt, HMENU hmenu, UINT def);
\r
654 void ParseIcsTextMenu(char *icsTextMenuString);
\r
655 VOID PopUpMoveDialog(char firstchar);
\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, dragged_piece,
\r
3800 ((int) dragged_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.start = dragInfo.from;
\r
4067 SetCapture(hwndMain);
\r
4070 void DragPieceEnd(int x, int y)
\r
4073 dragInfo.start.x = dragInfo.start.y = -1;
\r
4074 dragInfo.from = dragInfo.start;
\r
4075 dragInfo.pos = dragInfo.lastpos = dragInfo.start;
\r
4078 /* Event handler for mouse messages */
\r
4080 MouseEvent(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
\r
4084 static int recursive = 0;
\r
4086 BOOLEAN forceFullRepaint = IsFullRepaintPreferrable(); /* [AS] */
\r
4087 extern ChessSquare promoSweep;
\r
4090 if (message == WM_MBUTTONUP) {
\r
4091 /* Hideous kludge to fool TrackPopupMenu into paying attention
\r
4092 to the middle button: we simulate pressing the left button too!
\r
4094 PostMessage(hwnd, WM_LBUTTONDOWN, wParam, lParam);
\r
4095 PostMessage(hwnd, WM_LBUTTONUP, wParam, lParam);
\r
4101 pt.x = LOWORD(lParam);
\r
4102 pt.y = HIWORD(lParam);
\r
4103 x = EventToSquare(pt.x - boardRect.left, BOARD_WIDTH);
\r
4104 y = EventToSquare(pt.y - boardRect.top, BOARD_HEIGHT);
\r
4105 if (!flipView && y >= 0) {
\r
4106 y = BOARD_HEIGHT - 1 - y;
\r
4108 if (flipView && x >= 0) {
\r
4109 x = BOARD_WIDTH - 1 - x;
\r
4112 shiftKey = GetKeyState(VK_SHIFT) < 0; // [HGM] remember last shift status
\r
4114 switch (message) {
\r
4115 case WM_LBUTTONDOWN:
\r
4116 if (PtInRect((LPRECT) &whiteRect, pt)) {
\r
4117 ClockClick(flipClock);
\r
4118 } else if (PtInRect((LPRECT) &blackRect, pt)) {
\r
4119 ClockClick(!flipClock);
\r
4121 dragInfo.start.x = dragInfo.start.y = -1;
\r
4122 dragInfo.from = dragInfo.start;
\r
4123 if(fromX == -1 && frozen) { // not sure where this is for
\r
4124 fromX = fromY = -1;
\r
4125 DrawPosition(forceFullRepaint || FALSE, NULL); /* [AS] */
\r
4128 LeftClick(Press, pt.x - boardRect.left, pt.y - boardRect.top);
\r
4129 DrawPosition(TRUE, NULL);
\r
4132 case WM_LBUTTONUP:
\r
4133 LeftClick(Release, pt.x - boardRect.left, pt.y - boardRect.top);
\r
4134 DrawPosition(TRUE, NULL);
\r
4137 case WM_MOUSEMOVE:
\r
4138 if(SeekGraphClick(Press, pt.x - boardRect.left, pt.y - boardRect.top, 1)) break;
\r
4139 if(promoSweep != EmptySquare && appData.sweepSelect) { PromoScroll(pt.x - boardRect.left, pt.y - boardRect.top); break; }
\r
4140 MovePV(pt.x - boardRect.left, pt.y - boardRect.top, boardRect.bottom - boardRect.top);
\r
4141 if ((appData.animateDragging || appData.highlightDragging)
\r
4142 && (wParam & MK_LBUTTON)
\r
4143 && dragInfo.from.x >= 0)
\r
4145 BOOL full_repaint = FALSE;
\r
4147 if (appData.animateDragging) {
\r
4148 dragInfo.pos = pt;
\r
4150 if (appData.highlightDragging) {
\r
4151 SetHighlights(fromX, fromY, x, y);
\r
4152 if( IsDrawArrowEnabled() && (x < 0 || x >= BOARD_WIDTH || y < 0 || y >= BOARD_HEIGHT) ) {
\r
4153 full_repaint = TRUE;
\r
4157 DrawPosition( full_repaint, NULL);
\r
4159 dragInfo.lastpos = dragInfo.pos;
\r
4163 case WM_MOUSEWHEEL: // [DM]
\r
4164 { static int lastDir = 0; // [HGM] build in some hysteresis to avoid spurious events
\r
4165 /* Mouse Wheel is being rolled forward
\r
4166 * Play moves forward
\r
4168 if((short)HIWORD(wParam) < 0 && currentMove < forwardMostMove)
\r
4169 { if(lastDir == 1) ForwardEvent(); else lastDir = 1; } // [HGM] suppress first event in direction
\r
4170 /* Mouse Wheel is being rolled backward
\r
4171 * Play moves backward
\r
4173 if((short)HIWORD(wParam) > 0 && currentMove > backwardMostMove)
\r
4174 { if(lastDir == -1) BackwardEvent(); else lastDir = -1; }
\r
4178 case WM_MBUTTONUP:
\r
4179 case WM_RBUTTONUP:
\r
4181 RightClick(Release, pt.x - boardRect.left, pt.y - boardRect.top, &fromX, &fromY);
\r
4184 case WM_MBUTTONDOWN:
\r
4185 case WM_RBUTTONDOWN:
\r
4188 fromX = fromY = -1;
\r
4189 dragInfo.pos.x = dragInfo.pos.y = -1;
\r
4190 dragInfo.start.x = dragInfo.start.y = -1;
\r
4191 dragInfo.from = dragInfo.start;
\r
4192 dragInfo.lastpos = dragInfo.pos;
\r
4193 if (appData.highlightDragging) {
\r
4194 ClearHighlights();
\r
4197 /* [HGM] right mouse button in clock area edit-game mode ups clock */
\r
4198 if (PtInRect((LPRECT) &whiteRect, pt)) {
\r
4199 if (gameMode == EditGame || GetKeyState(VK_SHIFT) < 0) AdjustClock(flipClock, 1);
\r
4200 } else if (PtInRect((LPRECT) &blackRect, pt)) {
\r
4201 if (gameMode == EditGame || GetKeyState(VK_SHIFT) < 0) AdjustClock(!flipClock, 1);
\r
4205 DrawPosition(TRUE, NULL);
\r
4207 menuNr = RightClick(Press, pt.x - boardRect.left, pt.y - boardRect.top, &fromX, &fromY);
\r
4210 if (message == WM_MBUTTONDOWN) {
\r
4211 buttonCount = 3; /* even if system didn't think so */
\r
4212 if (wParam & MK_SHIFT)
\r
4213 MenuPopup(hwnd, pt, LoadMenu(hInst, "BlackPieceMenu"), -1);
\r
4215 MenuPopup(hwnd, pt, LoadMenu(hInst, "WhitePieceMenu"), -1);
\r
4216 } else { /* message == WM_RBUTTONDOWN */
\r
4217 /* Just have one menu, on the right button. Windows users don't
\r
4218 think to try the middle one, and sometimes other software steals
\r
4219 it, or it doesn't really exist. */
\r
4220 if(gameInfo.variant != VariantShogi)
\r
4221 MenuPopup(hwnd, pt, LoadMenu(hInst, "PieceMenu"), -1);
\r
4223 MenuPopup(hwnd, pt, LoadMenu(hInst, "ShogiPieceMenu"), -1);
\r
4227 SetCapture(hwndMain);
4230 hmenu = LoadMenu(hInst, "DropPieceMenu");
\r
4231 SetupDropMenu(hmenu);
\r
4232 MenuPopup(hwnd, pt, hmenu, -1);
\r
4242 /* Preprocess messages for buttons in main window */
\r
4244 ButtonProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
\r
4246 int id = GetWindowLongPtr(hwnd, GWLP_ID);
\r
4249 for (i=0; i<N_BUTTONS; i++) {
\r
4250 if (buttonDesc[i].id == id) break;
\r
4252 if (i == N_BUTTONS) return 0;
\r
4253 switch (message) {
\r
4258 dir = (wParam == VK_LEFT) ? -1 : 1;
\r
4259 SetFocus(buttonDesc[(i + dir + N_BUTTONS) % N_BUTTONS].hwnd);
\r
4266 SendMessage(hwndMain, WM_COMMAND, MAKEWPARAM(buttonDesc[i].id, 0), 0);
\r
4269 if (appData.icsActive && (isalpha((char)wParam) || wParam == '0')) {
\r
4270 // [HGM] movenum: only letters or leading zero should go to ICS input
\r
4271 HWND h = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
4272 if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);
\r
4274 SendMessage(h, WM_CHAR, wParam, lParam);
\r
4276 } else if (isalpha((char)wParam) || isdigit((char)wParam)){
\r
4277 PopUpMoveDialog((char)wParam);
\r
4283 return CallWindowProc(buttonDesc[i].wndproc, hwnd, message, wParam, lParam);
\r
4286 /* Process messages for Promotion dialog box */
\r
4288 Promotion(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
4292 switch (message) {
\r
4293 case WM_INITDIALOG: /* message: initialize dialog box */
\r
4294 /* Center the dialog over the application window */
\r
4295 CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));
\r
4296 Translate(hDlg, DLG_PromotionKing);
\r
4297 ShowWindow(GetDlgItem(hDlg, PB_King),
\r
4298 (!appData.testLegality || gameInfo.variant == VariantSuicide ||
\r
4299 gameInfo.variant == VariantSpartan && !WhiteOnMove(currentMove) ||
\r
4300 gameInfo.variant == VariantGiveaway || gameInfo.variant == VariantSuper ) ?
\r
4301 SW_SHOW : SW_HIDE);
\r
4302 /* [HGM] Only allow C & A promotions if these pieces are defined */
\r
4303 ShowWindow(GetDlgItem(hDlg, PB_Archbishop),
\r
4304 ((PieceToChar(WhiteAngel) >= 'A' && WhiteOnMove(currentMove) &&
\r
4305 PieceToChar(WhiteAngel) != '~') ||
\r
4306 (PieceToChar(BlackAngel) >= 'A' && !WhiteOnMove(currentMove) &&
\r
4307 PieceToChar(BlackAngel) != '~') ) ?
\r
4308 SW_SHOW : SW_HIDE);
\r
4309 ShowWindow(GetDlgItem(hDlg, PB_Chancellor),
\r
4310 ((PieceToChar(WhiteMarshall) >= 'A' && WhiteOnMove(currentMove) &&
\r
4311 PieceToChar(WhiteMarshall) != '~') ||
\r
4312 (PieceToChar(BlackMarshall) >= 'A' && !WhiteOnMove(currentMove) &&
\r
4313 PieceToChar(BlackMarshall) != '~') ) ?
\r
4314 SW_SHOW : SW_HIDE);
\r
4315 /* [HGM] Hide B & R button in Shogi, use Q as promote, N as defer */
\r
4316 ShowWindow(GetDlgItem(hDlg, PB_Rook),
\r
4317 gameInfo.variant != VariantShogi ?
\r
4318 SW_SHOW : SW_HIDE);
\r
4319 ShowWindow(GetDlgItem(hDlg, PB_Bishop),
\r
4320 gameInfo.variant != VariantShogi ?
\r
4321 SW_SHOW : SW_HIDE);
\r
4322 if(gameInfo.variant == VariantShogi) {
\r
4323 SetDlgItemText(hDlg, PB_Queen, "YES");
\r
4324 SetDlgItemText(hDlg, PB_Knight, "NO");
\r
4325 SetWindowText(hDlg, "Promote?");
\r
4327 ShowWindow(GetDlgItem(hDlg, IDC_Centaur),
\r
4328 gameInfo.variant == VariantSuper ?
\r
4329 SW_SHOW : SW_HIDE);
\r
4332 case WM_COMMAND: /* message: received a command */
\r
4333 switch (LOWORD(wParam)) {
\r
4335 EndDialog(hDlg, TRUE); /* Exit the dialog */
\r
4336 ClearHighlights();
\r
4337 DrawPosition(FALSE, NULL);
\r
4340 promoChar = gameInfo.variant == VariantSuper ? PieceToChar(BlackSilver) : PieceToChar(BlackKing);
\r
4343 promoChar = gameInfo.variant == VariantShogi ? '+' : ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteQueen : BlackQueen));
\r
4346 promoChar = ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteRook : BlackRook));
\r
4347 if(gameInfo.variant == VariantSpartan && !WhiteOnMove(currentMove)) promoChar = PieceToChar(BlackDragon);
\r
4350 promoChar = ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteBishop : BlackBishop));
\r
4351 if(gameInfo.variant == VariantSpartan && !WhiteOnMove(currentMove)) promoChar = PieceToChar(BlackAlfil);
\r
4353 case PB_Chancellor:
\r
4354 promoChar = ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteMarshall : BlackMarshall));
\r
4356 case PB_Archbishop:
\r
4357 promoChar = ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteAngel : BlackAngel));
\r
4360 promoChar = gameInfo.variant == VariantShogi ? '=' : PieceToChar(WhiteOnMove(currentMove) ? WhiteKnight : BlackKnight);
\r
4365 if(promoChar == '.') return FALSE; // invalid piece chosen
\r
4366 EndDialog(hDlg, TRUE); /* Exit the dialog */
\r
4367 UserMoveEvent(fromX, fromY, toX, toY, promoChar);
\r
4368 fromX = fromY = -1;
\r
4369 if (!appData.highlightLastMove) {
\r
4370 ClearHighlights();
\r
4371 DrawPosition(FALSE, NULL);
\r
4378 /* Pop up promotion dialog */
\r
4380 PromotionPopup(HWND hwnd)
\r
4384 lpProc = MakeProcInstance((FARPROC)Promotion, hInst);
\r
4385 DialogBox(hInst, MAKEINTRESOURCE(DLG_PromotionKing),
\r
4386 hwnd, (DLGPROC)lpProc);
\r
4387 FreeProcInstance(lpProc);
\r
4393 DrawPosition(TRUE, NULL);
\r
4394 PromotionPopup(hwndMain);
\r
4397 /* Toggle ShowThinking */
\r
4399 ToggleShowThinking()
\r
4401 appData.showThinking = !appData.showThinking;
\r
4402 ShowThinkingEvent();
\r
4406 LoadGameDialog(HWND hwnd, char* title)
\r
4410 char fileTitle[MSG_SIZ];
\r
4411 f = OpenFileDialog(hwnd, "rb", "",
\r
4412 appData.oldSaveStyle ? "gam" : "pgn",
\r
4414 title, &number, fileTitle, NULL);
\r
4416 cmailMsgLoaded = FALSE;
\r
4417 if (number == 0) {
\r
4418 int error = GameListBuild(f);
\r
4420 DisplayError(_("Cannot build game list"), error);
\r
4421 } else if (!ListEmpty(&gameList) &&
\r
4422 ((ListGame *) gameList.tailPred)->number > 1) {
\r
4423 GameListPopUp(f, fileTitle);
\r
4426 GameListDestroy();
\r
4429 LoadGame(f, number, fileTitle, FALSE);
\r
4433 int get_term_width()
\r
4438 HFONT hfont, hold_font;
\r
4443 hText = GetDlgItem(hwndConsole, OPT_ConsoleText);
\r
4447 // get the text metrics
\r
4448 hdc = GetDC(hText);
\r
4449 lf = font[boardSize][CONSOLE_FONT]->lf;
\r
4450 if (consoleCF.dwEffects & CFE_BOLD)
\r
4451 lf.lfWeight = FW_BOLD;
\r
4452 if (consoleCF.dwEffects & CFE_ITALIC)
\r
4453 lf.lfItalic = TRUE;
\r
4454 if (consoleCF.dwEffects & CFE_STRIKEOUT)
\r
4455 lf.lfStrikeOut = TRUE;
\r
4456 if (consoleCF.dwEffects & CFE_UNDERLINE)
\r
4457 lf.lfUnderline = TRUE;
\r
4458 hfont = CreateFontIndirect(&lf);
\r
4459 hold_font = SelectObject(hdc, hfont);
\r
4460 GetTextMetrics(hdc, &tm);
\r
4461 SelectObject(hdc, hold_font);
\r
4462 DeleteObject(hfont);
\r
4463 ReleaseDC(hText, hdc);
\r
4465 // get the rectangle
\r
4466 SendMessage(hText, EM_GETRECT, 0, (LPARAM)&rc);
\r
4468 return (rc.right-rc.left) / tm.tmAveCharWidth;
\r
4471 void UpdateICSWidth(HWND hText)
\r
4473 LONG old_width, new_width;
\r
4475 new_width = get_term_width(hText, FALSE);
\r
4476 old_width = GetWindowLongPtr(hText, GWLP_USERDATA);
\r
4477 if (new_width != old_width)
\r
4479 ics_update_width(new_width);
\r
4480 SetWindowLongPtr(hText, GWLP_USERDATA, new_width);
\r
4485 ChangedConsoleFont()
\r
4488 CHARRANGE tmpsel, sel;
\r
4489 MyFont *f = font[boardSize][CONSOLE_FONT];
\r
4490 HWND hText = GetDlgItem(hwndConsole, OPT_ConsoleText);
\r
4491 HWND hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
4494 cfmt.cbSize = sizeof(CHARFORMAT);
\r
4495 cfmt.dwMask = CFM_FACE|CFM_SIZE|CFM_CHARSET;
\r
4496 safeStrCpy(cfmt.szFaceName, font[boardSize][CONSOLE_FONT]->mfp.faceName,
\r
4497 sizeof(cfmt.szFaceName)/sizeof(cfmt.szFaceName[0]) );
\r
4498 /* yHeight is expressed in twips. A twip is 1/20 of a font's point
\r
4499 * size. This was undocumented in the version of MSVC++ that I had
\r
4500 * when I wrote the code, but is apparently documented now.
\r
4502 cfmt.yHeight = (int)(f->mfp.pointSize * 20.0 + 0.5);
\r
4503 cfmt.bCharSet = f->lf.lfCharSet;
\r
4504 cfmt.bPitchAndFamily = f->lf.lfPitchAndFamily;
\r
4505 SendMessage(hText, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &cfmt);
\r
4506 SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &cfmt);
\r
4507 /* Why are the following seemingly needed too? */
\r
4508 SendMessage(hText, EM_SETCHARFORMAT, SCF_DEFAULT, (LPARAM) &cfmt);
\r
4509 SendMessage(hInput, EM_SETCHARFORMAT, SCF_DEFAULT, (LPARAM) &cfmt);
\r
4510 SendMessage(hText, EM_EXGETSEL, 0, (LPARAM)&sel);
\r
4512 tmpsel.cpMax = -1; /*999999?*/
\r
4513 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&tmpsel);
\r
4514 SendMessage(hText, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cfmt);
\r
4515 /* Trying putting this here too. It still seems to tickle a RichEdit
\r
4516 * bug: sometimes RichEdit indents the first line of a paragraph too.
\r
4518 paraf.cbSize = sizeof(paraf);
\r
4519 paraf.dwMask = PFM_OFFSET | PFM_STARTINDENT;
\r
4520 paraf.dxStartIndent = 0;
\r
4521 paraf.dxOffset = WRAP_INDENT;
\r
4522 SendMessage(hText, EM_SETPARAFORMAT, 0, (LPARAM) ¶f);
\r
4523 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
4524 UpdateICSWidth(hText);
\r
4527 /*---------------------------------------------------------------------------*\
\r
4529 * Window Proc for main window
\r
4531 \*---------------------------------------------------------------------------*/
\r
4533 /* Process messages for main window, etc. */
\r
4535 WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
\r
4538 int wmId, wmEvent;
\r
4542 char fileTitle[MSG_SIZ];
\r
4543 char buf[MSG_SIZ];
\r
4544 static SnapData sd;
\r
4546 switch (message) {
\r
4548 case WM_PAINT: /* message: repaint portion of window */
\r
4552 case WM_ERASEBKGND:
\r
4553 if (IsIconic(hwnd)) {
\r
4554 /* Cheat; change the message */
\r
4555 return (DefWindowProc(hwnd, WM_ICONERASEBKGND, wParam, lParam));
\r
4557 return (DefWindowProc(hwnd, message, wParam, lParam));
\r
4561 case WM_LBUTTONDOWN:
\r
4562 case WM_MBUTTONDOWN:
\r
4563 case WM_RBUTTONDOWN:
\r
4564 case WM_LBUTTONUP:
\r
4565 case WM_MBUTTONUP:
\r
4566 case WM_RBUTTONUP:
\r
4567 case WM_MOUSEMOVE:
\r
4568 case WM_MOUSEWHEEL:
\r
4569 MouseEvent(hwnd, message, wParam, lParam);
\r
4572 JAWS_KB_NAVIGATION
\r
4576 JAWS_ALT_INTERCEPT
\r
4578 if (appData.icsActive && ((char)wParam == '\r' || (char)wParam > ' ' && !((char)wParam >= '1' && (char)wParam <= '9'))) {
\r
4579 // [HGM] movenum: for non-zero digits we always do type-in dialog
\r
4580 HWND h = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
4581 if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);
\r
4583 SendMessage(h, message, wParam, lParam);
\r
4584 } else if(lParam != KF_REPEAT) {
\r
4585 if (isalpha((char)wParam) || isdigit((char)wParam)) {
\r
4586 PopUpMoveDialog((char)wParam);
\r
4587 } else if((char)wParam == 003) CopyGameToClipboard();
\r
4588 else if((char)wParam == 026) PasteGameOrFENFromClipboard();
\r
4593 case WM_PALETTECHANGED:
\r
4594 if (hwnd != (HWND)wParam && !appData.monoMode) {
\r
4596 HDC hdc = GetDC(hwndMain);
\r
4597 SelectPalette(hdc, hPal, TRUE);
\r
4598 nnew = RealizePalette(hdc);
\r
4600 paletteChanged = TRUE;
\r
4601 InvalidateRect(hwnd, &boardRect, FALSE);
\r
4603 ReleaseDC(hwnd, hdc);
\r
4607 case WM_QUERYNEWPALETTE:
\r
4608 if (!appData.monoMode /*&& paletteChanged*/) {
\r
4610 HDC hdc = GetDC(hwndMain);
\r
4611 paletteChanged = FALSE;
\r
4612 SelectPalette(hdc, hPal, FALSE);
\r
4613 nnew = RealizePalette(hdc);
\r
4615 InvalidateRect(hwnd, &boardRect, FALSE);
\r
4617 ReleaseDC(hwnd, hdc);
\r
4622 case WM_COMMAND: /* message: command from application menu */
\r
4623 wmId = LOWORD(wParam);
\r
4624 wmEvent = HIWORD(wParam);
\r
4629 SAY("new game enter a move to play against the computer with white");
\r
4632 case IDM_NewGameFRC:
\r
4633 if( NewGameFRC() == 0 ) {
\r
4638 case IDM_NewVariant:
\r
4639 NewVariantPopup(hwnd);
\r
4642 case IDM_LoadGame:
\r
4643 LoadGameDialog(hwnd, _("Load Game from File"));
\r
4646 case IDM_LoadNextGame:
\r
4650 case IDM_LoadPrevGame:
\r
4654 case IDM_ReloadGame:
\r
4658 case IDM_LoadPosition:
\r
4659 if (gameMode == AnalyzeMode || gameMode == AnalyzeFile) {
\r
4660 Reset(FALSE, TRUE);
\r
4663 f = OpenFileDialog(hwnd, "rb", "",
\r
4664 appData.oldSaveStyle ? "pos" : "fen",
\r
4666 _("Load Position from File"), &number, fileTitle, NULL);
\r
4668 LoadPosition(f, number, fileTitle);
\r
4672 case IDM_LoadNextPosition:
\r
4673 ReloadPosition(1);
\r
4676 case IDM_LoadPrevPosition:
\r
4677 ReloadPosition(-1);
\r
4680 case IDM_ReloadPosition:
\r
4681 ReloadPosition(0);
\r
4684 case IDM_SaveGame:
\r
4685 defName = DefaultFileName(appData.oldSaveStyle ? "gam" : "pgn");
\r
4686 f = OpenFileDialog(hwnd, "a", defName,
\r
4687 appData.oldSaveStyle ? "gam" : "pgn",
\r
4689 _("Save Game to File"), NULL, fileTitle, NULL);
\r
4691 SaveGame(f, 0, "");
\r
4695 case IDM_SavePosition:
\r
4696 defName = DefaultFileName(appData.oldSaveStyle ? "pos" : "fen");
\r
4697 f = OpenFileDialog(hwnd, "a", defName,
\r
4698 appData.oldSaveStyle ? "pos" : "fen",
\r
4700 _("Save Position to File"), NULL, fileTitle, NULL);
\r
4702 SavePosition(f, 0, "");
\r
4706 case IDM_SaveDiagram:
\r
4707 defName = "diagram";
\r
4708 f = OpenFileDialog(hwnd, "wb", defName,
\r
4711 _("Save Diagram to File"), NULL, fileTitle, NULL);
\r
4717 case IDM_CopyGame:
\r
4718 CopyGameToClipboard();
\r
4721 case IDM_PasteGame:
\r
4722 PasteGameFromClipboard();
\r
4725 case IDM_CopyGameListToClipboard:
\r
4726 CopyGameListToClipboard();
\r
4729 /* [AS] Autodetect FEN or PGN data */
\r
4730 case IDM_PasteAny:
\r
4731 PasteGameOrFENFromClipboard();
\r
4734 /* [AS] Move history */
\r
4735 case IDM_ShowMoveHistory:
\r
4736 if( MoveHistoryIsUp() ) {
\r
4737 MoveHistoryPopDown();
\r
4740 MoveHistoryPopUp();
\r
4744 /* [AS] Eval graph */
\r
4745 case IDM_ShowEvalGraph:
\r
4746 if( EvalGraphIsUp() ) {
\r
4747 EvalGraphPopDown();
\r
4751 SetFocus(hwndMain);
\r
4755 /* [AS] Engine output */
\r
4756 case IDM_ShowEngineOutput:
\r
4757 if( EngineOutputIsUp() ) {
\r
4758 EngineOutputPopDown();
\r
4761 EngineOutputPopUp();
\r
4765 /* [AS] User adjudication */
\r
4766 case IDM_UserAdjudication_White:
\r
4767 UserAdjudicationEvent( +1 );
\r
4770 case IDM_UserAdjudication_Black:
\r
4771 UserAdjudicationEvent( -1 );
\r
4774 case IDM_UserAdjudication_Draw:
\r
4775 UserAdjudicationEvent( 0 );
\r
4778 /* [AS] Game list options dialog */
\r
4779 case IDM_GameListOptions:
\r
4780 GameListOptions();
\r
4787 case IDM_CopyPosition:
\r
4788 CopyFENToClipboard();
\r
4791 case IDM_PastePosition:
\r
4792 PasteFENFromClipboard();
\r
4795 case IDM_MailMove:
\r
4799 case IDM_ReloadCMailMsg:
\r
4800 Reset(TRUE, TRUE);
\r
4801 ReloadCmailMsgEvent(FALSE);
\r
4804 case IDM_Minimize:
\r
4805 ShowWindow(hwnd, SW_MINIMIZE);
\r
4812 case IDM_MachineWhite:
\r
4813 MachineWhiteEvent();
\r
4815 * refresh the tags dialog only if it's visible
\r
4817 if (gameMode == MachinePlaysWhite && IsWindowVisible(editTagsDialog)) {
\r
4819 tags = PGNTags(&gameInfo);
\r
4820 TagsPopUp(tags, CmailMsg());
\r
4823 SAY("computer starts playing white");
\r
4826 case IDM_MachineBlack:
\r
4827 MachineBlackEvent();
\r
4829 * refresh the tags dialog only if it's visible
\r
4831 if (gameMode == MachinePlaysBlack && IsWindowVisible(editTagsDialog)) {
\r
4833 tags = PGNTags(&gameInfo);
\r
4834 TagsPopUp(tags, CmailMsg());
\r
4837 SAY("computer starts playing black");
\r
4840 case IDM_Match: // [HGM] match: flows into next case, after setting Match Mode and nr of Games
\r
4841 if(gameMode != BeginningOfGame) { // allow menu item to remain enabled for better mode highligting
\r
4842 DisplayError(_("You can only start a match from the initial position."), 0); break;
\r
4844 matchMode = 2;// distinguish from command-line-triggered case (matchMode=1)
\r
4845 appData.matchGames = appData.defaultMatchGames;
\r
4847 first.matchWins = second.matchWins = 0;
\r
4849 case IDM_TwoMachines:
\r
4850 TwoMachinesEvent();
\r
4852 * refresh the tags dialog only if it's visible
\r
4854 if (gameMode == TwoMachinesPlay && IsWindowVisible(editTagsDialog)) {
\r
4856 tags = PGNTags(&gameInfo);
\r
4857 TagsPopUp(tags, CmailMsg());
\r
4860 SAY("computer starts playing both sides");
\r
4863 case IDM_AnalysisMode:
\r
4864 if (!first.analysisSupport) {
\r
4865 snprintf(buf, MSG_SIZ, _("%s does not support analysis"), first.tidy);
\r
4866 DisplayError(buf, 0);
\r
4868 SAY("analyzing current position");
\r
4869 /* [DM] icsEngineAnlyze [HGM] Why is this front-end??? */
\r
4870 if (appData.icsActive) {
\r
4871 if (gameMode != IcsObserving) {
\r
4872 snprintf(buf, MSG_SIZ, "You are not observing a game");
\r
4873 DisplayError(buf, 0);
\r
4874 /* secure check */
\r
4875 if (appData.icsEngineAnalyze) {
\r
4876 if (appData.debugMode)
\r
4877 fprintf(debugFP, "Found unexpected active ICS engine analyze \n");
\r
4878 ExitAnalyzeMode();
\r
4884 /* if enable, user want disable icsEngineAnalyze */
\r
4885 if (appData.icsEngineAnalyze) {
\r
4886 ExitAnalyzeMode();
\r
4890 appData.icsEngineAnalyze = TRUE;
\r
4891 if (appData.debugMode) fprintf(debugFP, "ICS engine analyze starting...\n");
\r
4894 if (!appData.showThinking) ToggleShowThinking();
\r
4895 AnalyzeModeEvent();
\r
4899 case IDM_AnalyzeFile:
\r
4900 if (!first.analysisSupport) {
\r
4901 char buf[MSG_SIZ];
\r
4902 snprintf(buf, MSG_SIZ, _("%s does not support analysis"), first.tidy);
\r
4903 DisplayError(buf, 0);
\r
4905 if (!appData.showThinking) ToggleShowThinking();
\r
4906 AnalyzeFileEvent();
\r
4907 LoadGameDialog(hwnd, _("Analyze Game from File"));
\r
4908 AnalysisPeriodicEvent(1);
\r
4912 case IDM_IcsClient:
\r
4916 case IDM_EditGame:
\r
4917 case IDM_EditGame2:
\r
4922 case IDM_EditPosition:
\r
4923 case IDM_EditPosition2:
\r
4924 EditPositionEvent();
\r
4925 SAY("enter a FEN string or setup a position on the board using the control R pop up menu");
\r
4928 case IDM_Training:
\r
4932 case IDM_ShowGameList:
\r
4933 ShowGameListProc();
\r
4936 case IDM_EditProgs1:
\r
4937 EditTagsPopUp(firstChessProgramNames, &firstChessProgramNames);
\r
4940 case IDM_EditProgs2:
\r
4941 EditTagsPopUp(secondChessProgramNames, &secondChessProgramNames);
\r
4944 case IDM_EditServers:
\r
4945 EditTagsPopUp(icsNames, &icsNames);
\r
4948 case IDM_EditTags:
\r
4953 case IDM_EditComment:
\r
4955 if (commentUp && editComment) {
\r
4958 EditCommentEvent();
\r
4978 case IDM_CallFlag:
\r
4998 case IDM_StopObserving:
\r
4999 StopObservingEvent();
\r
5002 case IDM_StopExamining:
\r
5003 StopExaminingEvent();
\r
5007 UploadGameEvent();
\r
5010 case IDM_TypeInMove:
\r
5011 PopUpMoveDialog('\000');
\r
5014 case IDM_TypeInName:
\r
5015 PopUpNameDialog('\000');
\r
5018 case IDM_Backward:
\r
5020 SetFocus(hwndMain);
\r
5027 SetFocus(hwndMain);
\r
5032 SetFocus(hwndMain);
\r
5037 SetFocus(hwndMain);
\r
5041 RevertEvent(FALSE);
\r
5044 case IDM_Annotate: // [HGM] vari: revert with annotation
\r
5045 RevertEvent(TRUE);
\r
5048 case IDM_TruncateGame:
\r
5049 TruncateGameEvent();
\r
5056 case IDM_RetractMove:
\r
5057 RetractMoveEvent();
\r
5060 case IDM_FlipView:
\r
5061 flipView = !flipView;
\r
5062 DrawPosition(FALSE, NULL);
\r
5065 case IDM_FlipClock:
\r
5066 flipClock = !flipClock;
\r
5067 DisplayBothClocks();
\r
5071 case IDM_MuteSounds:
\r
5072 mute = !mute; // [HGM] mute: keep track of global muting variable
\r
5073 CheckMenuItem(GetMenu(hwndMain),IDM_MuteSounds,
\r
5074 MF_BYCOMMAND|(mute?MF_CHECKED:MF_UNCHECKED));
\r
5077 case IDM_GeneralOptions:
\r
5078 GeneralOptionsPopup(hwnd);
\r
5079 DrawPosition(TRUE, NULL);
\r
5082 case IDM_BoardOptions:
\r
5083 BoardOptionsPopup(hwnd);
\r
5086 case IDM_EnginePlayOptions:
\r
5087 EnginePlayOptionsPopup(hwnd);
\r
5090 case IDM_Engine1Options:
\r
5091 EngineOptionsPopup(hwnd, &first);
\r
5094 case IDM_Engine2Options:
\r
5096 if(WaitForSecond(SettingsMenuIfReady)) break;
\r
5097 EngineOptionsPopup(hwnd, &second);
\r
5100 case IDM_OptionsUCI:
\r
5101 UciOptionsPopup(hwnd);
\r
5104 case IDM_IcsOptions:
\r
5105 IcsOptionsPopup(hwnd);
\r
5109 FontsOptionsPopup(hwnd);
\r
5113 SoundOptionsPopup(hwnd);
\r
5116 case IDM_CommPort:
\r
5117 CommPortOptionsPopup(hwnd);
\r
5120 case IDM_LoadOptions:
\r
5121 LoadOptionsPopup(hwnd);
\r
5124 case IDM_SaveOptions:
\r
5125 SaveOptionsPopup(hwnd);
\r
5128 case IDM_TimeControl:
\r
5129 TimeControlOptionsPopup(hwnd);
\r
5132 case IDM_SaveSettings:
\r
5133 SaveSettings(settingsFileName);
\r
5136 case IDM_SaveSettingsOnExit:
\r
5137 saveSettingsOnExit = !saveSettingsOnExit;
\r
5138 (void) CheckMenuItem(GetMenu(hwndMain), IDM_SaveSettingsOnExit,
\r
5139 MF_BYCOMMAND|(saveSettingsOnExit ?
\r
5140 MF_CHECKED : MF_UNCHECKED));
\r
5151 case IDM_AboutGame:
\r
5156 appData.debugMode = !appData.debugMode;
\r
5157 if (appData.debugMode) {
\r
5158 char dir[MSG_SIZ];
\r
5159 GetCurrentDirectory(MSG_SIZ, dir);
\r
5160 SetCurrentDirectory(installDir);
\r
5161 debugFP = fopen(appData.nameOfDebugFile, "w");
\r
5162 SetCurrentDirectory(dir);
\r
5163 setbuf(debugFP, NULL);
\r
5170 case IDM_HELPCONTENTS:
\r
5171 if (!MyHelp (hwnd, "winboard.hlp", HELP_KEY,(DWORD)(LPSTR)"CONTENTS") &&
\r
5172 !HtmlHelp(hwnd, "winboard.chm", 0, 0) ) {
\r
5173 MessageBox (GetFocus(),
\r
5174 _("Unable to activate help"),
\r
5175 szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);
\r
5179 case IDM_HELPSEARCH:
\r
5180 if (!MyHelp (hwnd, "winboard.hlp", HELP_PARTIALKEY, (DWORD)(LPSTR)"") &&
\r
5181 !HtmlHelp(hwnd, "winboard.chm", 0, 0) ) {
\r
5182 MessageBox (GetFocus(),
\r
5183 _("Unable to activate help"),
\r
5184 szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);
\r
5188 case IDM_HELPHELP:
\r
5189 if(!WinHelp(hwnd, (LPSTR)NULL, HELP_HELPONHELP, 0)) {
\r
5190 MessageBox (GetFocus(),
\r
5191 _("Unable to activate help"),
\r
5192 szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);
\r
5197 lpProc = MakeProcInstance((FARPROC)About, hInst);
\r
5199 (gameInfo.event && strcmp(gameInfo.event, "Easter Egg Hunt") == 0) ?
\r
5200 "AboutBox2" : "AboutBox", hwnd, (DLGPROC)lpProc);
\r
5201 FreeProcInstance(lpProc);
\r
5204 case IDM_DirectCommand1:
\r
5205 AskQuestionEvent(_("Direct Command"),
\r
5206 _("Send to chess program:"), "", "1");
\r
5208 case IDM_DirectCommand2:
\r
5209 AskQuestionEvent(_("Direct Command"),
\r
5210 _("Send to second chess program:"), "", "2");
\r
5213 case EP_WhitePawn:
\r
5214 EditPositionMenuEvent(WhitePawn, fromX, fromY);
\r
5215 fromX = fromY = -1;
\r
5218 case EP_WhiteKnight:
\r
5219 EditPositionMenuEvent(WhiteKnight, fromX, fromY);
\r
5220 fromX = fromY = -1;
\r
5223 case EP_WhiteBishop:
\r
5224 EditPositionMenuEvent(WhiteBishop, fromX, fromY);
\r
5225 fromX = fromY = -1;
\r
5228 case EP_WhiteRook:
\r
5229 EditPositionMenuEvent(WhiteRook, fromX, fromY);
\r
5230 fromX = fromY = -1;
\r
5233 case EP_WhiteQueen:
\r
5234 EditPositionMenuEvent(WhiteQueen, fromX, fromY);
\r
5235 fromX = fromY = -1;
\r
5238 case EP_WhiteFerz:
\r
5239 EditPositionMenuEvent(WhiteFerz, fromX, fromY);
\r
5240 fromX = fromY = -1;
\r
5243 case EP_WhiteWazir:
\r
5244 EditPositionMenuEvent(WhiteWazir, fromX, fromY);
\r
5245 fromX = fromY = -1;
\r
5248 case EP_WhiteAlfil:
\r
5249 EditPositionMenuEvent(WhiteAlfil, fromX, fromY);
\r
5250 fromX = fromY = -1;
\r
5253 case EP_WhiteCannon:
\r
5254 EditPositionMenuEvent(WhiteCannon, fromX, fromY);
\r
5255 fromX = fromY = -1;
\r
5258 case EP_WhiteCardinal:
\r
5259 EditPositionMenuEvent(WhiteAngel, fromX, fromY);
\r
5260 fromX = fromY = -1;
\r
5263 case EP_WhiteMarshall:
\r
5264 EditPositionMenuEvent(WhiteMarshall, fromX, fromY);
\r
5265 fromX = fromY = -1;
\r
5268 case EP_WhiteKing:
\r
5269 EditPositionMenuEvent(WhiteKing, fromX, fromY);
\r
5270 fromX = fromY = -1;
\r
5273 case EP_BlackPawn:
\r
5274 EditPositionMenuEvent(BlackPawn, fromX, fromY);
\r
5275 fromX = fromY = -1;
\r
5278 case EP_BlackKnight:
\r
5279 EditPositionMenuEvent(BlackKnight, fromX, fromY);
\r
5280 fromX = fromY = -1;
\r
5283 case EP_BlackBishop:
\r
5284 EditPositionMenuEvent(BlackBishop, fromX, fromY);
\r
5285 fromX = fromY = -1;
\r
5288 case EP_BlackRook:
\r
5289 EditPositionMenuEvent(BlackRook, fromX, fromY);
\r
5290 fromX = fromY = -1;
\r
5293 case EP_BlackQueen:
\r
5294 EditPositionMenuEvent(BlackQueen, fromX, fromY);
\r
5295 fromX = fromY = -1;
\r
5298 case EP_BlackFerz:
\r
5299 EditPositionMenuEvent(BlackFerz, fromX, fromY);
\r
5300 fromX = fromY = -1;
\r
5303 case EP_BlackWazir:
\r
5304 EditPositionMenuEvent(BlackWazir, fromX, fromY);
\r
5305 fromX = fromY = -1;
\r
5308 case EP_BlackAlfil:
\r
5309 EditPositionMenuEvent(BlackAlfil, fromX, fromY);
\r
5310 fromX = fromY = -1;
\r
5313 case EP_BlackCannon:
\r
5314 EditPositionMenuEvent(BlackCannon, fromX, fromY);
\r
5315 fromX = fromY = -1;
\r
5318 case EP_BlackCardinal:
\r
5319 EditPositionMenuEvent(BlackAngel, fromX, fromY);
\r
5320 fromX = fromY = -1;
\r
5323 case EP_BlackMarshall:
\r
5324 EditPositionMenuEvent(BlackMarshall, fromX, fromY);
\r
5325 fromX = fromY = -1;
\r
5328 case EP_BlackKing:
\r
5329 EditPositionMenuEvent(BlackKing, fromX, fromY);
\r
5330 fromX = fromY = -1;
\r
5333 case EP_EmptySquare:
\r
5334 EditPositionMenuEvent(EmptySquare, fromX, fromY);
\r
5335 fromX = fromY = -1;
\r
5338 case EP_ClearBoard:
\r
5339 EditPositionMenuEvent(ClearBoard, fromX, fromY);
\r
5340 fromX = fromY = -1;
\r
5344 EditPositionMenuEvent(WhitePlay, fromX, fromY);
\r
5345 fromX = fromY = -1;
\r
5349 EditPositionMenuEvent(BlackPlay, fromX, fromY);
\r
5350 fromX = fromY = -1;
\r
5354 EditPositionMenuEvent(PromotePiece, fromX, fromY);
\r
5355 fromX = fromY = -1;
\r
5359 EditPositionMenuEvent(DemotePiece, fromX, fromY);
\r
5360 fromX = fromY = -1;
\r
5364 DropMenuEvent(WhitePawn, fromX, fromY);
\r
5365 fromX = fromY = -1;
\r
5369 DropMenuEvent(WhiteKnight, fromX, fromY);
\r
5370 fromX = fromY = -1;
\r
5374 DropMenuEvent(WhiteBishop, fromX, fromY);
\r
5375 fromX = fromY = -1;
\r
5379 DropMenuEvent(WhiteRook, fromX, fromY);
\r
5380 fromX = fromY = -1;
\r
5384 DropMenuEvent(WhiteQueen, fromX, fromY);
\r
5385 fromX = fromY = -1;
\r
5389 barbaric = 0; appData.language = "";
\r
5390 TranslateMenus(0);
\r
5391 CheckMenuItem(GetMenu(hwndMain), lastChecked, MF_BYCOMMAND|MF_UNCHECKED);
\r
5392 CheckMenuItem(GetMenu(hwndMain), IDM_English, MF_BYCOMMAND|MF_CHECKED);
\r
5393 lastChecked = wmId;
\r
5397 if(wmId > IDM_English && wmId < IDM_English+20) {
\r
5398 LoadLanguageFile(languageFile[wmId - IDM_English - 1]);
\r
5399 TranslateMenus(0);
\r
5400 CheckMenuItem(GetMenu(hwndMain), lastChecked, MF_BYCOMMAND|MF_UNCHECKED);
\r
5401 CheckMenuItem(GetMenu(hwndMain), wmId, MF_BYCOMMAND|MF_CHECKED);
\r
5402 lastChecked = wmId;
\r
5405 return (DefWindowProc(hwnd, message, wParam, lParam));
\r
5411 case CLOCK_TIMER_ID:
\r
5412 KillTimer(hwnd, clockTimerEvent); /* Simulate one-shot timer as in X */
\r
5413 clockTimerEvent = 0;
\r
5414 DecrementClocks(); /* call into back end */
\r
5416 case LOAD_GAME_TIMER_ID:
\r
5417 KillTimer(hwnd, loadGameTimerEvent); /* Simulate one-shot timer as in X*/
\r
5418 loadGameTimerEvent = 0;
\r
5419 AutoPlayGameLoop(); /* call into back end */
\r
5421 case ANALYSIS_TIMER_ID:
\r
5422 if ((gameMode == AnalyzeMode || gameMode == AnalyzeFile
\r
5423 || appData.icsEngineAnalyze) && appData.periodicUpdates) {
\r
5424 AnalysisPeriodicEvent(0);
\r
5426 KillTimer(hwnd, analysisTimerEvent);
\r
5427 analysisTimerEvent = 0;
\r
5430 case DELAYED_TIMER_ID:
\r
5431 KillTimer(hwnd, delayedTimerEvent);
\r
5432 delayedTimerEvent = 0;
\r
5433 delayedTimerCallback();
\r
5438 case WM_USER_Input:
\r
5439 InputEvent(hwnd, message, wParam, lParam);
\r
5442 /* [AS] Also move "attached" child windows */
\r
5443 case WM_WINDOWPOSCHANGING:
\r
5445 if( hwnd == hwndMain && appData.useStickyWindows ) {
\r
5446 LPWINDOWPOS lpwp = (LPWINDOWPOS) lParam;
\r
5448 if( ((lpwp->flags & SWP_NOMOVE) == 0) && ((lpwp->flags & SWP_NOSIZE) != 0) ) {
\r
5449 /* Window is moving */
\r
5452 // GetWindowRect( hwnd, &rcMain ); //[HGM] sticky: in XP this returned new position, not old
\r
5453 rcMain.left = wpMain.x; // replace by these 4 lines to reconstruct old rect
\r
5454 rcMain.right = wpMain.x + wpMain.width;
\r
5455 rcMain.top = wpMain.y;
\r
5456 rcMain.bottom = wpMain.y + wpMain.height;
\r
5458 ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, moveHistoryDialog, &wpMoveHistory );
\r
5459 ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, evalGraphDialog, &wpEvalGraph );
\r
5460 ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, engineOutputDialog, &wpEngineOutput );
\r
5461 ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, gameListDialog, &wpGameList );
\r
5462 ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, hwndConsole, &wpConsole );
\r
5463 wpMain.x = lpwp->x;
\r
5464 wpMain.y = lpwp->y;
\r
5469 /* [AS] Snapping */
\r
5470 case WM_ENTERSIZEMOVE:
\r
5471 if(appData.debugMode) { fprintf(debugFP, "size-move\n"); }
\r
5472 if (hwnd == hwndMain) {
\r
5473 doingSizing = TRUE;
\r
5476 return OnEnterSizeMove( &sd, hwnd, wParam, lParam );
\r
5480 if(appData.debugMode) { fprintf(debugFP, "sizing\n"); }
\r
5481 if (hwnd == hwndMain) {
\r
5482 lastSizing = wParam;
\r
5487 if(appData.debugMode) { fprintf(debugFP, "moving\n"); }
\r
5488 return OnMoving( &sd, hwnd, wParam, lParam );
\r
5490 case WM_EXITSIZEMOVE:
\r
5491 if(appData.debugMode) { fprintf(debugFP, "exit size-move, size = %d\n", squareSize); }
\r
5492 if (hwnd == hwndMain) {
\r
5494 doingSizing = FALSE;
\r
5495 InvalidateRect(hwnd, &boardRect, FALSE);
\r
5496 GetClientRect(hwnd, &client);
\r
5497 ResizeBoard(client.right, client.bottom, lastSizing);
\r
5499 if(appData.debugMode) { fprintf(debugFP, "square size = %d\n", squareSize); }
\r
5501 return OnExitSizeMove( &sd, hwnd, wParam, lParam );
\r
5504 case WM_DESTROY: /* message: window being destroyed */
\r
5505 PostQuitMessage(0);
\r
5509 if (hwnd == hwndMain) {
\r
5514 default: /* Passes it on if unprocessed */
\r
5515 return (DefWindowProc(hwnd, message, wParam, lParam));
\r
5520 /*---------------------------------------------------------------------------*\
\r
5522 * Misc utility routines
\r
5524 \*---------------------------------------------------------------------------*/
\r
5527 * Decent random number generator, at least not as bad as Windows
\r
5528 * standard rand, which returns a value in the range 0 to 0x7fff.
\r
5530 unsigned int randstate;
\r
5535 randstate = randstate * 1664525 + 1013904223;
\r
5536 return (int) randstate & 0x7fffffff;
\r
5540 mysrandom(unsigned int seed)
\r
5547 * returns TRUE if user selects a different color, FALSE otherwise
\r
5551 ChangeColor(HWND hwnd, COLORREF *which)
\r
5553 static BOOL firstTime = TRUE;
\r
5554 static DWORD customColors[16];
\r
5556 COLORREF newcolor;
\r
5561 /* Make initial colors in use available as custom colors */
\r
5562 /* Should we put the compiled-in defaults here instead? */
\r
5564 customColors[i++] = lightSquareColor & 0xffffff;
\r
5565 customColors[i++] = darkSquareColor & 0xffffff;
\r
5566 customColors[i++] = whitePieceColor & 0xffffff;
\r
5567 customColors[i++] = blackPieceColor & 0xffffff;
\r
5568 customColors[i++] = highlightSquareColor & 0xffffff;
\r
5569 customColors[i++] = premoveHighlightColor & 0xffffff;
\r
5571 for (ccl = (ColorClass) 0; ccl < NColorClasses && i < 16; ccl++) {
\r
5572 customColors[i++] = textAttribs[ccl].color;
\r
5574 while (i < 16) customColors[i++] = RGB(255, 255, 255);
\r
5575 firstTime = FALSE;
\r
5578 cc.lStructSize = sizeof(cc);
\r
5579 cc.hwndOwner = hwnd;
\r
5580 cc.hInstance = NULL;
\r
5581 cc.rgbResult = (DWORD) (*which & 0xffffff);
\r
5582 cc.lpCustColors = (LPDWORD) customColors;
\r
5583 cc.Flags = CC_RGBINIT|CC_FULLOPEN;
\r
5585 if (!ChooseColor(&cc)) return FALSE;
\r
5587 newcolor = (COLORREF) (0x2000000 | cc.rgbResult);
\r
5588 if (newcolor == *which) return FALSE;
\r
5589 *which = newcolor;
\r
5593 InitDrawingColors();
\r
5594 InvalidateRect(hwnd, &boardRect, FALSE);
\r
5599 MyLoadSound(MySound *ms)
\r
5605 if (ms->data) free(ms->data);
\r
5608 switch (ms->name[0]) {
\r
5614 /* System sound from Control Panel. Don't preload here. */
\r
5618 if (ms->name[1] == NULLCHAR) {
\r
5619 /* "!" alone = silence */
\r
5622 /* Builtin wave resource. Error if not found. */
\r
5623 HANDLE h = FindResource(hInst, ms->name + 1, "WAVE");
\r
5624 if (h == NULL) break;
\r
5625 ms->data = (void *)LoadResource(hInst, h);
\r
5626 if (h == NULL) break;
\r
5631 /* .wav file. Error if not found. */
\r
5632 f = fopen(ms->name, "rb");
\r
5633 if (f == NULL) break;
\r
5634 if (fstat(fileno(f), &st) < 0) break;
\r
5635 ms->data = malloc(st.st_size);
\r
5636 if (fread(ms->data, st.st_size, 1, f) < 1) break;
\r
5642 char buf[MSG_SIZ];
\r
5643 snprintf(buf, MSG_SIZ, _("Error loading sound %s"), ms->name);
\r
5644 DisplayError(buf, GetLastError());
\r
5650 MyPlaySound(MySound *ms)
\r
5652 BOOLEAN ok = FALSE;
\r
5654 if(mute) return TRUE; // [HGM] mute: suppress all sound play when muted
\r
5655 switch (ms->name[0]) {
\r
5657 if(appData.debugMode) fprintf(debugFP, "silence\n");
\r
5662 /* System sound from Control Panel (deprecated feature).
\r
5663 "$" alone or an unset sound name gets default beep (still in use). */
\r
5664 if (ms->name[1]) {
\r
5665 ok = PlaySound(ms->name + 1, NULL, SND_ALIAS|SND_ASYNC);
\r
5667 if (!ok) ok = MessageBeep(MB_OK);
\r
5670 /* Builtin wave resource, or "!" alone for silence */
\r
5671 if (ms->name[1]) {
\r
5672 if (ms->data == NULL) return FALSE;
\r
5673 ok = PlaySound(ms->data, NULL, SND_MEMORY|SND_ASYNC);
\r
5679 /* .wav file. Error if not found. */
\r
5680 if (ms->data == NULL) return FALSE;
\r
5681 ok = PlaySound(ms->data, NULL, SND_MEMORY|SND_ASYNC);
\r
5684 /* Don't print an error: this can happen innocently if the sound driver
\r
5685 is busy; for instance, if another instance of WinBoard is playing
\r
5686 a sound at about the same time. */
\r
5692 OldOpenFileHook(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
5695 OPENFILENAME *ofn;
\r
5696 static UINT *number; /* gross that this is static */
\r
5698 switch (message) {
\r
5699 case WM_INITDIALOG: /* message: initialize dialog box */
\r
5700 /* Center the dialog over the application window */
\r
5701 ofn = (OPENFILENAME *) lParam;
\r
5702 if (ofn->Flags & OFN_ENABLETEMPLATE) {
\r
5703 number = (UINT *) ofn->lCustData;
\r
5704 SendMessage(GetDlgItem(hDlg, edt2), WM_SETTEXT, 0, (LPARAM) "");
\r
5708 CenterWindow (hDlg, GetWindow (hDlg, GW_OWNER));
\r
5709 Translate(hDlg, 1536);
\r
5710 return FALSE; /* Allow for further processing */
\r
5713 if ((LOWORD(wParam) == IDOK) && (number != NULL)) {
\r
5714 *number = GetDlgItemInt(hDlg, OPT_IndexNumberOld, &ok, FALSE);
\r
5716 return FALSE; /* Allow for further processing */
\r
5722 OpenFileHook(HWND hdlg, UINT uiMsg, WPARAM wParam, LPARAM lParam)
\r
5724 static UINT *number;
\r
5725 OPENFILENAME *ofname;
\r
5728 case WM_INITDIALOG:
\r
5729 Translate(hdlg, DLG_IndexNumber);
\r
5730 ofname = (OPENFILENAME *)lParam;
\r
5731 number = (UINT *)(ofname->lCustData);
\r
5734 ofnot = (OFNOTIFY *)lParam;
\r
5735 if (ofnot->hdr.code == CDN_FILEOK) {
\r
5736 *number = GetDlgItemInt(hdlg, OPT_IndexNumber, NULL, FALSE);
\r
5745 OpenFileDialog(HWND hwnd, char *write, char *defName, char *defExt, // [HGM] diag: type of 'write' now string
\r
5746 char *nameFilt, char *dlgTitle, UINT *number,
\r
5747 char fileTitle[MSG_SIZ], char fileName[MSG_SIZ])
\r
5749 OPENFILENAME openFileName;
\r
5750 char buf1[MSG_SIZ];
\r
5753 if (fileName == NULL) fileName = buf1;
\r
5754 if (defName == NULL) {
\r
5755 safeStrCpy(fileName, "*.", 3 );
\r
5756 strcat(fileName, defExt);
\r
5758 safeStrCpy(fileName, defName, MSG_SIZ );
\r
5760 if (fileTitle) safeStrCpy(fileTitle, "", MSG_SIZ );
\r
5761 if (number) *number = 0;
\r
5763 openFileName.lStructSize = sizeof(OPENFILENAME);
\r
5764 openFileName.hwndOwner = hwnd;
\r
5765 openFileName.hInstance = (HANDLE) hInst;
\r
5766 openFileName.lpstrFilter = nameFilt;
\r
5767 openFileName.lpstrCustomFilter = (LPSTR) NULL;
\r
5768 openFileName.nMaxCustFilter = 0L;
\r
5769 openFileName.nFilterIndex = 1L;
\r
5770 openFileName.lpstrFile = fileName;
\r
5771 openFileName.nMaxFile = MSG_SIZ;
\r
5772 openFileName.lpstrFileTitle = fileTitle;
\r
5773 openFileName.nMaxFileTitle = fileTitle ? MSG_SIZ : 0;
\r
5774 openFileName.lpstrInitialDir = NULL;
\r
5775 openFileName.lpstrTitle = dlgTitle;
\r
5776 openFileName.Flags = OFN_PATHMUSTEXIST | OFN_HIDEREADONLY
\r
5777 | (write[0] != 'r' ? 0 : OFN_FILEMUSTEXIST)
\r
5778 | (number ? OFN_ENABLETEMPLATE | OFN_ENABLEHOOK: 0)
\r
5779 | (oldDialog ? 0 : OFN_EXPLORER);
\r
5780 openFileName.nFileOffset = 0;
\r
5781 openFileName.nFileExtension = 0;
\r
5782 openFileName.lpstrDefExt = defExt;
\r
5783 openFileName.lCustData = (LONG) number;
\r
5784 openFileName.lpfnHook = oldDialog ?
\r
5785 (LPOFNHOOKPROC) OldOpenFileHook : (LPOFNHOOKPROC) OpenFileHook;
\r
5786 openFileName.lpTemplateName = (LPSTR)(oldDialog ? 1536 : DLG_IndexNumber);
\r
5788 if (write[0] != 'r' ? GetSaveFileName(&openFileName) :
\r
5789 GetOpenFileName(&openFileName)) {
\r
5790 /* open the file */
\r
5791 f = fopen(openFileName.lpstrFile, write);
\r
5793 MessageBox(hwnd, _("File open failed"), NULL,
\r
5794 MB_OK|MB_ICONEXCLAMATION);
\r
5798 int err = CommDlgExtendedError();
\r
5799 if (err != 0) DisplayError(_("Internal error in file dialog box"), err);
\r
5808 MenuPopup(HWND hwnd, POINT pt, HMENU hmenu, UINT def)
\r
5810 HMENU hmenuTrackPopup; /* floating pop-up menu */
\r
5813 * Get the first pop-up menu in the menu template. This is the
\r
5814 * menu that TrackPopupMenu displays.
\r
5816 hmenuTrackPopup = GetSubMenu(hmenu, 0);
\r
5817 TranslateOneMenu(10, hmenuTrackPopup);
\r
5819 SetMenuDefaultItem(hmenuTrackPopup, def, FALSE);
\r
5822 * TrackPopup uses screen coordinates, so convert the
\r
5823 * coordinates of the mouse click to screen coordinates.
\r
5825 ClientToScreen(hwnd, (LPPOINT) &pt);
\r
5827 /* Draw and track the floating pop-up menu. */
\r
5828 TrackPopupMenu(hmenuTrackPopup, TPM_CENTERALIGN | TPM_RIGHTBUTTON,
\r
5829 pt.x, pt.y, 0, hwnd, NULL);
\r
5831 /* Destroy the menu.*/
\r
5832 DestroyMenu(hmenu);
\r
5837 int sizeX, sizeY, newSizeX, newSizeY;
\r
5839 } ResizeEditPlusButtonsClosure;
\r
5842 ResizeEditPlusButtonsCallback(HWND hChild, LPARAM lparam)
\r
5844 ResizeEditPlusButtonsClosure *cl = (ResizeEditPlusButtonsClosure *)lparam;
\r
5848 if (hChild == cl->hText) return TRUE;
\r
5849 GetWindowRect(hChild, &rect); /* gives screen coords */
\r
5850 pt.x = rect.left + (cl->newSizeX - cl->sizeX)/2;
\r
5851 pt.y = rect.top + cl->newSizeY - cl->sizeY;
\r
5852 ScreenToClient(cl->hDlg, &pt);
\r
5853 cl->hdwp = DeferWindowPos(cl->hdwp, hChild, NULL,
\r
5854 pt.x, pt.y, rect.right - rect.left, rect.bottom - rect.top, SWP_NOZORDER);
\r
5858 /* Resize a dialog that has a (rich) edit field filling most of
\r
5859 the top, with a row of buttons below */
\r
5861 ResizeEditPlusButtons(HWND hDlg, HWND hText, int sizeX, int sizeY, int newSizeX, int newSizeY)
\r
5864 int newTextHeight, newTextWidth;
\r
5865 ResizeEditPlusButtonsClosure cl;
\r
5867 /*if (IsIconic(hDlg)) return;*/
\r
5868 if (newSizeX == sizeX && newSizeY == sizeY) return;
\r
5870 cl.hdwp = BeginDeferWindowPos(8);
\r
5872 GetWindowRect(hText, &rectText); /* gives screen coords */
\r
5873 newTextWidth = rectText.right - rectText.left + newSizeX - sizeX;
\r
5874 newTextHeight = rectText.bottom - rectText.top + newSizeY - sizeY;
\r
5875 if (newTextHeight < 0) {
\r
5876 newSizeY += -newTextHeight;
\r
5877 newTextHeight = 0;
\r
5879 cl.hdwp = DeferWindowPos(cl.hdwp, hText, NULL, 0, 0,
\r
5880 newTextWidth, newTextHeight, SWP_NOZORDER|SWP_NOMOVE);
\r
5886 cl.newSizeX = newSizeX;
\r
5887 cl.newSizeY = newSizeY;
\r
5888 EnumChildWindows(hDlg, ResizeEditPlusButtonsCallback, (LPARAM)&cl);
\r
5890 EndDeferWindowPos(cl.hdwp);
\r
5893 BOOL CenterWindowEx(HWND hwndChild, HWND hwndParent, int mode)
\r
5895 RECT rChild, rParent;
\r
5896 int wChild, hChild, wParent, hParent;
\r
5897 int wScreen, hScreen, xNew, yNew;
\r
5900 /* Get the Height and Width of the child window */
\r
5901 GetWindowRect (hwndChild, &rChild);
\r
5902 wChild = rChild.right - rChild.left;
\r
5903 hChild = rChild.bottom - rChild.top;
\r
5905 /* Get the Height and Width of the parent window */
\r
5906 GetWindowRect (hwndParent, &rParent);
\r
5907 wParent = rParent.right - rParent.left;
\r
5908 hParent = rParent.bottom - rParent.top;
\r
5910 /* Get the display limits */
\r
5911 hdc = GetDC (hwndChild);
\r
5912 wScreen = GetDeviceCaps (hdc, HORZRES);
\r
5913 hScreen = GetDeviceCaps (hdc, VERTRES);
\r
5914 ReleaseDC(hwndChild, hdc);
\r
5916 /* Calculate new X position, then adjust for screen */
\r
5917 xNew = rParent.left + ((wParent - wChild) /2);
\r
5920 } else if ((xNew+wChild) > wScreen) {
\r
5921 xNew = wScreen - wChild;
\r
5924 /* Calculate new Y position, then adjust for screen */
\r
5926 yNew = rParent.top + ((hParent - hChild) /2);
\r
5929 yNew = rParent.top + GetSystemMetrics( SM_CYCAPTION ) * 2 / 3;
\r
5934 } else if ((yNew+hChild) > hScreen) {
\r
5935 yNew = hScreen - hChild;
\r
5938 /* Set it, and return */
\r
5939 return SetWindowPos (hwndChild, NULL,
\r
5940 xNew, yNew, 0, 0, SWP_NOSIZE | SWP_NOZORDER);
\r
5943 /* Center one window over another */
\r
5944 BOOL CenterWindow (HWND hwndChild, HWND hwndParent)
\r
5946 return CenterWindowEx( hwndChild, hwndParent, 0 );
\r
5949 /*---------------------------------------------------------------------------*\
\r
5951 * Startup Dialog functions
\r
5953 \*---------------------------------------------------------------------------*/
\r
5955 InitComboStrings(HANDLE hwndCombo, char **cd)
\r
5957 SendMessage(hwndCombo, CB_RESETCONTENT, 0, 0);
\r
5959 while (*cd != NULL) {
\r
5960 SendMessage(hwndCombo, CB_ADDSTRING, 0, (LPARAM) T_(*cd));
\r
5966 InitComboStringsFromOption(HANDLE hwndCombo, char *str)
\r
5968 char buf1[MAX_ARG_LEN];
\r
5971 if (str[0] == '@') {
\r
5972 FILE* f = fopen(str + 1, "r");
\r
5974 DisplayFatalError(str + 1, errno, 2);
\r
5977 len = fread(buf1, 1, sizeof(buf1)-1, f);
\r
5979 buf1[len] = NULLCHAR;
\r
5983 SendMessage(hwndCombo, CB_RESETCONTENT, 0, 0);
\r
5986 char buf[MSG_SIZ];
\r
5987 char *end = strchr(str, '\n');
\r
5988 if (end == NULL) return;
\r
5989 memcpy(buf, str, end - str);
\r
5990 buf[end - str] = NULLCHAR;
\r
5991 SendMessage(hwndCombo, CB_ADDSTRING, 0, (LPARAM) buf);
\r
5997 SetStartupDialogEnables(HWND hDlg)
\r
5999 EnableWindow(GetDlgItem(hDlg, OPT_ChessEngineName),
\r
6000 IsDlgButtonChecked(hDlg, OPT_ChessEngine) ||
\r
6001 (appData.zippyPlay && IsDlgButtonChecked(hDlg, OPT_ChessServer)));
\r
6002 EnableWindow(GetDlgItem(hDlg, OPT_SecondChessEngineName),
\r
6003 IsDlgButtonChecked(hDlg, OPT_ChessEngine));
\r
6004 EnableWindow(GetDlgItem(hDlg, OPT_ChessServerName),
\r
6005 IsDlgButtonChecked(hDlg, OPT_ChessServer));
\r
6006 EnableWindow(GetDlgItem(hDlg, OPT_AdditionalOptions),
\r
6007 IsDlgButtonChecked(hDlg, OPT_AnyAdditional));
\r
6008 EnableWindow(GetDlgItem(hDlg, IDOK),
\r
6009 IsDlgButtonChecked(hDlg, OPT_ChessEngine) ||
\r
6010 IsDlgButtonChecked(hDlg, OPT_ChessServer) ||
\r
6011 IsDlgButtonChecked(hDlg, OPT_View));
\r
6015 QuoteForFilename(char *filename)
\r
6017 int dquote, space;
\r
6018 dquote = strchr(filename, '"') != NULL;
\r
6019 space = strchr(filename, ' ') != NULL;
\r
6020 if (dquote || space) {
\r
6032 InitEngineBox(HWND hDlg, HWND hwndCombo, char* nthcp, char* nthd, char* nthdir, char *nthnames)
\r
6034 char buf[MSG_SIZ];
\r
6037 InitComboStringsFromOption(hwndCombo, nthnames);
\r
6038 q = QuoteForFilename(nthcp);
\r
6039 snprintf(buf, MSG_SIZ, "%s%s%s", q, nthcp, q);
\r
6040 if (*nthdir != NULLCHAR) {
\r
6041 q = QuoteForFilename(nthdir);
\r
6042 snprintf(buf + strlen(buf), MSG_SIZ, " /%s=%s%s%s", nthd, q, nthdir, q);
\r
6044 if (*nthcp == NULLCHAR) {
\r
6045 SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) 0, (LPARAM) 0);
\r
6046 } else if (SendMessage(hwndCombo, CB_SELECTSTRING, (WPARAM) -1, (LPARAM) buf) == CB_ERR) {
\r
6047 SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) -1, (LPARAM) 0);
\r
6048 SendMessage(hwndCombo, WM_SETTEXT, (WPARAM) 0, (LPARAM) buf);
\r
6053 StartupDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
6055 char buf[MSG_SIZ];
\r
6059 switch (message) {
\r
6060 case WM_INITDIALOG:
\r
6061 /* Center the dialog */
\r
6062 CenterWindow (hDlg, GetDesktopWindow());
\r
6063 Translate(hDlg, DLG_Startup);
\r
6064 /* Initialize the dialog items */
\r
6065 InitEngineBox(hDlg, GetDlgItem(hDlg, OPT_ChessEngineName),
\r
6066 appData.firstChessProgram, "fd", appData.firstDirectory,
\r
6067 firstChessProgramNames);
\r
6068 InitEngineBox(hDlg, GetDlgItem(hDlg, OPT_SecondChessEngineName),
\r
6069 appData.secondChessProgram, "sd", appData.secondDirectory,
\r
6070 secondChessProgramNames);
\r
6071 hwndCombo = GetDlgItem(hDlg, OPT_ChessServerName);
\r
6072 InitComboStringsFromOption(hwndCombo, icsNames);
\r
6073 snprintf(buf, MSG_SIZ, "%s /icsport=%s", appData.icsHost, appData.icsPort);
\r
6074 if (*appData.icsHelper != NULLCHAR) {
\r
6075 char *q = QuoteForFilename(appData.icsHelper);
\r
6076 sprintf(buf + strlen(buf), " /icshelper=%s%s%s", q, appData.icsHelper, q);
\r
6078 if (*appData.icsHost == NULLCHAR) {
\r
6079 SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) 0, (LPARAM) 0);
\r
6080 /*SendMessage(hwndCombo, CB_SHOWDROPDOWN, (WPARAM) TRUE, (LPARAM) 0); !!too soon */
\r
6081 } else if (SendMessage(hwndCombo, CB_SELECTSTRING, (WPARAM) -1, (LPARAM) buf) == CB_ERR) {
\r
6082 SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) -1, (LPARAM) 0);
\r
6083 SendMessage(hwndCombo, WM_SETTEXT, (WPARAM) 0, (LPARAM) buf);
\r
6086 if (appData.icsActive) {
\r
6087 CheckDlgButton(hDlg, OPT_ChessServer, BST_CHECKED);
\r
6089 else if (appData.noChessProgram) {
\r
6090 CheckDlgButton(hDlg, OPT_View, BST_CHECKED);
\r
6093 CheckDlgButton(hDlg, OPT_ChessEngine, BST_CHECKED);
\r
6096 SetStartupDialogEnables(hDlg);
\r
6100 switch (LOWORD(wParam)) {
\r
6102 if (IsDlgButtonChecked(hDlg, OPT_ChessEngine)) {
\r
6103 safeStrCpy(buf, "/fcp=", sizeof(buf)/sizeof(buf[0]) );
\r
6104 GetDlgItemText(hDlg, OPT_ChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));
\r
6106 ParseArgs(StringGet, &p);
\r
6107 safeStrCpy(buf, "/scp=", sizeof(buf)/sizeof(buf[0]) );
\r
6108 GetDlgItemText(hDlg, OPT_SecondChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));
\r
6110 ParseArgs(StringGet, &p);
\r
6111 appData.noChessProgram = FALSE;
\r
6112 appData.icsActive = FALSE;
\r
6113 } else if (IsDlgButtonChecked(hDlg, OPT_ChessServer)) {
\r
6114 safeStrCpy(buf, "/ics /icshost=", sizeof(buf)/sizeof(buf[0]) );
\r
6115 GetDlgItemText(hDlg, OPT_ChessServerName, buf + strlen(buf), sizeof(buf) - strlen(buf));
\r
6117 ParseArgs(StringGet, &p);
\r
6118 if (appData.zippyPlay) {
\r
6119 safeStrCpy(buf, "/fcp=", sizeof(buf)/sizeof(buf[0]) );
\r
6120 GetDlgItemText(hDlg, OPT_ChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));
\r
6122 ParseArgs(StringGet, &p);
\r
6124 } else if (IsDlgButtonChecked(hDlg, OPT_View)) {
\r
6125 appData.noChessProgram = TRUE;
\r
6126 appData.icsActive = FALSE;
\r
6128 MessageBox(hDlg, _("Choose an option, or cancel to exit"),
\r
6129 _("Option Error"), MB_OK|MB_ICONEXCLAMATION);
\r
6132 if (IsDlgButtonChecked(hDlg, OPT_AnyAdditional)) {
\r
6133 GetDlgItemText(hDlg, OPT_AdditionalOptions, buf, sizeof(buf));
\r
6135 ParseArgs(StringGet, &p);
\r
6137 EndDialog(hDlg, TRUE);
\r
6144 case IDM_HELPCONTENTS:
\r
6145 if (!WinHelp (hDlg, "winboard.hlp", HELP_KEY,(DWORD)(LPSTR)"CONTENTS")) {
\r
6146 MessageBox (GetFocus(),
\r
6147 _("Unable to activate help"),
\r
6148 szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);
\r
6153 SetStartupDialogEnables(hDlg);
\r
6161 /*---------------------------------------------------------------------------*\
\r
6163 * About box dialog functions
\r
6165 \*---------------------------------------------------------------------------*/
\r
6167 /* Process messages for "About" dialog box */
\r
6169 About(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
6171 switch (message) {
\r
6172 case WM_INITDIALOG: /* message: initialize dialog box */
\r
6173 /* Center the dialog over the application window */
\r
6174 CenterWindow (hDlg, GetWindow (hDlg, GW_OWNER));
\r
6175 SetDlgItemText(hDlg, ABOUTBOX_Version, programVersion);
\r
6176 Translate(hDlg, ABOUTBOX);
\r
6180 case WM_COMMAND: /* message: received a command */
\r
6181 if (LOWORD(wParam) == IDOK /* "OK" box selected? */
\r
6182 || LOWORD(wParam) == IDCANCEL) { /* System menu close command? */
\r
6183 EndDialog(hDlg, TRUE); /* Exit the dialog */
\r
6191 /*---------------------------------------------------------------------------*\
\r
6193 * Comment Dialog functions
\r
6195 \*---------------------------------------------------------------------------*/
\r
6198 CommentDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
6200 static HANDLE hwndText = NULL;
\r
6201 int len, newSizeX, newSizeY, flags;
\r
6202 static int sizeX, sizeY;
\r
6207 switch (message) {
\r
6208 case WM_INITDIALOG: /* message: initialize dialog box */
\r
6209 /* Initialize the dialog items */
\r
6210 Translate(hDlg, DLG_EditComment);
\r
6211 hwndText = GetDlgItem(hDlg, OPT_CommentText);
\r
6212 SetDlgItemText(hDlg, OPT_CommentText, commentText);
\r
6213 EnableWindow(GetDlgItem(hDlg, OPT_CancelComment), editComment);
\r
6214 EnableWindow(GetDlgItem(hDlg, OPT_ClearComment), editComment);
\r
6215 EnableWindow(GetDlgItem(hDlg, OPT_EditComment), !editComment);
\r
6216 SendMessage(hwndText, EM_SETREADONLY, !editComment, 0);
\r
6217 SetWindowText(hDlg, commentTitle);
\r
6218 if (editComment) {
\r
6219 SetFocus(hwndText);
\r
6221 SetFocus(GetDlgItem(hDlg, IDOK));
\r
6223 SendMessage(GetDlgItem(hDlg, OPT_CommentText),
\r
6224 WM_SETFONT, (WPARAM)font[boardSize][COMMENT_FONT]->hf,
\r
6225 MAKELPARAM(FALSE, 0));
\r
6226 /* Size and position the dialog */
\r
6227 if (!commentDialog) {
\r
6228 commentDialog = hDlg;
\r
6229 flags = SWP_NOZORDER;
\r
6230 GetClientRect(hDlg, &rect);
\r
6231 sizeX = rect.right;
\r
6232 sizeY = rect.bottom;
\r
6233 if (wpComment.x != CW_USEDEFAULT && wpComment.y != CW_USEDEFAULT &&
\r
6234 wpComment.width != CW_USEDEFAULT && wpComment.height != CW_USEDEFAULT) {
\r
6235 WINDOWPLACEMENT wp;
\r
6236 EnsureOnScreen(&wpComment.x, &wpComment.y, 0, 0);
\r
6237 wp.length = sizeof(WINDOWPLACEMENT);
\r
6239 wp.showCmd = SW_SHOW;
\r
6240 wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;
\r
6241 wp.rcNormalPosition.left = wpComment.x;
\r
6242 wp.rcNormalPosition.right = wpComment.x + wpComment.width;
\r
6243 wp.rcNormalPosition.top = wpComment.y;
\r
6244 wp.rcNormalPosition.bottom = wpComment.y + wpComment.height;
\r
6245 SetWindowPlacement(hDlg, &wp);
\r
6247 GetClientRect(hDlg, &rect);
\r
6248 newSizeX = rect.right;
\r
6249 newSizeY = rect.bottom;
\r
6250 ResizeEditPlusButtons(hDlg, hwndText, sizeX, sizeY,
\r
6251 newSizeX, newSizeY);
\r
6256 SendDlgItemMessage( hDlg, OPT_CommentText, EM_SETEVENTMASK, 0, ENM_MOUSEEVENTS | ENM_KEYEVENTS );
\r
6259 case WM_COMMAND: /* message: received a command */
\r
6260 switch (LOWORD(wParam)) {
\r
6262 if (editComment) {
\r
6264 /* Read changed options from the dialog box */
\r
6265 hwndText = GetDlgItem(hDlg, OPT_CommentText);
\r
6266 len = GetWindowTextLength(hwndText);
\r
6267 str = (char *) malloc(len + 1);
\r
6268 GetWindowText(hwndText, str, len + 1);
\r
6277 ReplaceComment(commentIndex, str);
\r
6284 case OPT_CancelComment:
\r
6288 case OPT_ClearComment:
\r
6289 SetDlgItemText(hDlg, OPT_CommentText, "");
\r
6292 case OPT_EditComment:
\r
6293 EditCommentEvent();
\r
6301 case WM_NOTIFY: // [HGM] vari: cloned from whistory.c
\r
6302 if( wParam == OPT_CommentText ) {
\r
6303 MSGFILTER * lpMF = (MSGFILTER *) lParam;
\r
6305 if( lpMF->msg == WM_RBUTTONDOWN && (lpMF->wParam & (MK_CONTROL | MK_SHIFT)) == 0 ||
\r
6306 lpMF->msg == WM_CHAR && lpMF->wParam == '\022' ) {
\r
6310 pt.x = LOWORD( lpMF->lParam );
\r
6311 pt.y = HIWORD( lpMF->lParam );
\r
6313 if(lpMF->msg == WM_CHAR) {
\r
6315 SendDlgItemMessage( hDlg, OPT_CommentText, EM_EXGETSEL, 0, (LPARAM) &sel );
\r
6316 index = sel.cpMin;
\r
6318 index = SendDlgItemMessage( hDlg, OPT_CommentText, EM_CHARFROMPOS, 0, (LPARAM) &pt );
\r
6320 hwndText = GetDlgItem(hDlg, OPT_CommentText); // cloned from above
\r
6321 len = GetWindowTextLength(hwndText);
\r
6322 str = (char *) malloc(len + 1);
\r
6323 GetWindowText(hwndText, str, len + 1);
\r
6324 ReplaceComment(commentIndex, str);
\r
6325 if(commentIndex != currentMove) ToNrEvent(commentIndex);
\r
6326 LoadVariation( index, str ); // [HGM] also does the actual moving to it, now
\r
6329 /* Zap the message for good: apparently, returning non-zero is not enough */
\r
6330 lpMF->msg = WM_USER;
\r
6338 newSizeX = LOWORD(lParam);
\r
6339 newSizeY = HIWORD(lParam);
\r
6340 ResizeEditPlusButtons(hDlg, hwndText, sizeX, sizeY, newSizeX, newSizeY);
\r
6345 case WM_GETMINMAXINFO:
\r
6346 /* Prevent resizing window too small */
\r
6347 mmi = (MINMAXINFO *) lParam;
\r
6348 mmi->ptMinTrackSize.x = 100;
\r
6349 mmi->ptMinTrackSize.y = 100;
\r
6356 EitherCommentPopUp(int index, char *title, char *str, BOOLEAN edit)
\r
6361 CheckMenuItem(GetMenu(hwndMain), IDM_Comment, edit ? MF_CHECKED : MF_UNCHECKED);
\r
6363 if (str == NULL) str = "";
\r
6364 p = (char *) malloc(2 * strlen(str) + 2);
\r
6367 if (*str == '\n') *q++ = '\r';
\r
6371 if (commentText != NULL) free(commentText);
\r
6373 commentIndex = index;
\r
6374 commentTitle = title;
\r
6376 editComment = edit;
\r
6378 if (commentDialog) {
\r
6379 SendMessage(commentDialog, WM_INITDIALOG, 0, 0);
\r
6380 if (!commentUp) ShowWindow(commentDialog, SW_SHOW);
\r
6382 lpProc = MakeProcInstance((FARPROC)CommentDialog, hInst);
\r
6383 CreateDialog(hInst, MAKEINTRESOURCE(DLG_EditComment),
\r
6384 hwndMain, (DLGPROC)lpProc);
\r
6385 FreeProcInstance(lpProc);
\r
6391 /*---------------------------------------------------------------------------*\
\r
6393 * Type-in move dialog functions
\r
6395 \*---------------------------------------------------------------------------*/
\r
6398 TypeInMoveDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
6400 char move[MSG_SIZ];
\r
6402 ChessMove moveType;
\r
6403 int fromX, fromY, toX, toY;
\r
6406 switch (message) {
\r
6407 case WM_INITDIALOG:
\r
6408 move[0] = (char) lParam;
\r
6409 move[1] = NULLCHAR;
\r
6410 CenterWindowEx(hDlg, GetWindow(hDlg, GW_OWNER), 1 );
\r
6411 Translate(hDlg, DLG_TypeInMove);
\r
6412 hInput = GetDlgItem(hDlg, OPT_Move);
\r
6413 SetWindowText(hInput, move);
\r
6415 SendMessage(hInput, EM_SETSEL, (WPARAM)9999, (LPARAM)9999);
\r
6419 switch (LOWORD(wParam)) {
\r
6422 shiftKey = GetKeyState(VK_SHIFT) < 0; // [HGM] remember last shift status
\r
6423 GetDlgItemText(hDlg, OPT_Move, move, sizeof(move));
\r
6424 { int n; Board board;
\r
6426 if(gameMode == EditPosition && ParseFEN(board, &n, move) ) {
\r
6427 EditPositionPasteFEN(move);
\r
6428 EndDialog(hDlg, TRUE);
\r
6431 // [HGM] movenum: allow move number to be typed in any mode
\r
6432 if(sscanf(move, "%d", &n) == 1 && n != 0 ) {
\r
6434 EndDialog(hDlg, TRUE);
\r
6438 if (gameMode != EditGame && currentMove != forwardMostMove &&
\r
6439 gameMode != Training) {
\r
6440 DisplayMoveError(_("Displayed move is not current"));
\r
6442 // GetDlgItemText(hDlg, OPT_Move, move, sizeof(move)); // moved upstream
\r
6443 int ok = ParseOneMove(move, gameMode == EditPosition ? blackPlaysFirst : currentMove,
\r
6444 &moveType, &fromX, &fromY, &toX, &toY, &promoChar);
\r
6445 if(!ok && move[0] >= 'a') { move[0] += 'A' - 'a'; ok = 2; } // [HGM] try also capitalized
\r
6446 if (ok==1 || ok && ParseOneMove(move, gameMode == EditPosition ? blackPlaysFirst : currentMove,
\r
6447 &moveType, &fromX, &fromY, &toX, &toY, &promoChar)) {
\r
6448 UserMoveEvent(fromX, fromY, toX, toY, promoChar);
\r
6450 DisplayMoveError(_("Could not parse move"));
\r
6453 EndDialog(hDlg, TRUE);
\r
6456 EndDialog(hDlg, FALSE);
\r
6467 PopUpMoveDialog(char firstchar)
\r
6471 if ((gameMode == BeginningOfGame && !appData.icsActive) ||
\r
6472 gameMode == MachinePlaysWhite || gameMode == MachinePlaysBlack ||
\r
6473 gameMode == AnalyzeMode || gameMode == EditGame ||
\r
6474 gameMode == EditPosition || gameMode == IcsExamining ||
\r
6475 gameMode == IcsPlayingWhite || gameMode == IcsPlayingBlack ||
\r
6476 isdigit(firstchar) && // [HGM] movenum: allow typing in of move nr in 'passive' modes
\r
6477 ( gameMode == AnalyzeFile || gameMode == PlayFromGameFile ||
\r
6478 gameMode == IcsObserving || gameMode == TwoMachinesPlay ) ||
\r
6479 gameMode == Training) {
\r
6480 lpProc = MakeProcInstance((FARPROC)TypeInMoveDialog, hInst);
\r
6481 DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_TypeInMove),
\r
6482 hwndMain, (DLGPROC)lpProc, (LPARAM)firstchar);
\r
6483 FreeProcInstance(lpProc);
\r
6487 /*---------------------------------------------------------------------------*\
\r
6489 * Type-in name dialog functions
\r
6491 \*---------------------------------------------------------------------------*/
\r
6494 TypeInNameDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
6496 char move[MSG_SIZ];
\r
6499 switch (message) {
\r
6500 case WM_INITDIALOG:
\r
6501 move[0] = (char) lParam;
\r
6502 move[1] = NULLCHAR;
\r
6503 CenterWindowEx(hDlg, GetWindow(hDlg, GW_OWNER), 1 );
\r
6504 Translate(hDlg, DLG_TypeInName);
\r
6505 hInput = GetDlgItem(hDlg, OPT_Name);
\r
6506 SetWindowText(hInput, move);
\r
6508 SendMessage(hInput, EM_SETSEL, (WPARAM)9999, (LPARAM)9999);
\r
6512 switch (LOWORD(wParam)) {
\r
6514 GetDlgItemText(hDlg, OPT_Name, move, sizeof(move));
\r
6515 appData.userName = strdup(move);
\r
6518 if(gameMode == MachinePlaysWhite || gameMode == MachinePlaysBlack) {
\r
6519 snprintf(move, MSG_SIZ, "%s vs. %s", gameInfo.white, gameInfo.black);
\r
6520 DisplayTitle(move);
\r
6524 EndDialog(hDlg, TRUE);
\r
6527 EndDialog(hDlg, FALSE);
\r
6538 PopUpNameDialog(char firstchar)
\r
6542 lpProc = MakeProcInstance((FARPROC)TypeInNameDialog, hInst);
\r
6543 DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_TypeInName),
\r
6544 hwndMain, (DLGPROC)lpProc, (LPARAM)firstchar);
\r
6545 FreeProcInstance(lpProc);
\r
6548 /*---------------------------------------------------------------------------*\
\r
6552 \*---------------------------------------------------------------------------*/
\r
6554 /* Nonmodal error box */
\r
6555 LRESULT CALLBACK ErrorDialog(HWND hDlg, UINT message,
\r
6556 WPARAM wParam, LPARAM lParam);
\r
6559 ErrorPopUp(char *title, char *content)
\r
6563 BOOLEAN modal = hwndMain == NULL;
\r
6581 strncpy(errorTitle, title, sizeof(errorTitle));
\r
6582 errorTitle[sizeof(errorTitle) - 1] = '\0';
\r
6585 MessageBox(NULL, errorMessage, errorTitle, MB_OK|MB_ICONEXCLAMATION);
\r
6587 lpProc = MakeProcInstance((FARPROC)ErrorDialog, hInst);
\r
6588 CreateDialog(hInst, MAKEINTRESOURCE(DLG_Error),
\r
6589 hwndMain, (DLGPROC)lpProc);
\r
6590 FreeProcInstance(lpProc);
\r
6597 if (!appData.popupMoveErrors && moveErrorMessageUp) DisplayMessage("", "");
\r
6598 if (errorDialog == NULL) return;
\r
6599 DestroyWindow(errorDialog);
\r
6600 errorDialog = NULL;
\r
6601 if(errorExitStatus) ExitEvent(errorExitStatus);
\r
6605 ErrorDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
6610 switch (message) {
\r
6611 case WM_INITDIALOG:
\r
6612 GetWindowRect(hDlg, &rChild);
\r
6615 SetWindowPos(hDlg, NULL, rChild.left,
\r
6616 rChild.top + boardRect.top - (rChild.bottom - rChild.top),
\r
6617 0, 0, SWP_NOZORDER|SWP_NOSIZE);
\r
6621 [AS] It seems that the above code wants to move the dialog up in the "caption
\r
6622 area" of the main window, but it uses the dialog height as an hard-coded constant,
\r
6623 and it doesn't work when you resize the dialog.
\r
6624 For now, just give it a default position.
\r
6626 SetWindowPos(hDlg, NULL, boardRect.left+8, boardRect.top+8, 0, 0, SWP_NOZORDER|SWP_NOSIZE);
\r
6627 Translate(hDlg, DLG_Error);
\r
6629 errorDialog = hDlg;
\r
6630 SetWindowText(hDlg, errorTitle);
\r
6631 hwndText = GetDlgItem(hDlg, OPT_ErrorText);
\r
6632 SetDlgItemText(hDlg, OPT_ErrorText, errorMessage);
\r
6636 switch (LOWORD(wParam)) {
\r
6639 if (errorDialog == hDlg) errorDialog = NULL;
\r
6640 DestroyWindow(hDlg);
\r
6652 HWND gothicDialog = NULL;
\r
6655 GothicDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
6659 int height = GetSystemMetrics(SM_CYCAPTION)+GetSystemMetrics(SM_CYFRAME);
\r
6661 switch (message) {
\r
6662 case WM_INITDIALOG:
\r
6663 GetWindowRect(hDlg, &rChild);
\r
6665 SetWindowPos(hDlg, NULL, wpMain.x, wpMain.y-height, wpMain.width, height,
\r
6669 [AS] It seems that the above code wants to move the dialog up in the "caption
\r
6670 area" of the main window, but it uses the dialog height as an hard-coded constant,
\r
6671 and it doesn't work when you resize the dialog.
\r
6672 For now, just give it a default position.
\r
6674 gothicDialog = hDlg;
\r
6675 SetWindowText(hDlg, errorTitle);
\r
6676 hwndText = GetDlgItem(hDlg, OPT_ErrorText);
\r
6677 SetDlgItemText(hDlg, OPT_ErrorText, errorMessage);
\r
6681 switch (LOWORD(wParam)) {
\r
6684 if (errorDialog == hDlg) errorDialog = NULL;
\r
6685 DestroyWindow(hDlg);
\r
6697 GothicPopUp(char *title, VariantClass variant)
\r
6700 static char *lastTitle;
\r
6702 strncpy(errorTitle, title, sizeof(errorTitle));
\r
6703 errorTitle[sizeof(errorTitle) - 1] = '\0';
\r
6705 if(lastTitle != title && gothicDialog != NULL) {
\r
6706 DestroyWindow(gothicDialog);
\r
6707 gothicDialog = NULL;
\r
6709 if(variant != VariantNormal && gothicDialog == NULL) {
\r
6710 title = lastTitle;
\r
6711 lpProc = MakeProcInstance((FARPROC)GothicDialog, hInst);
\r
6712 CreateDialog(hInst, MAKEINTRESOURCE(DLG_Error),
\r
6713 hwndMain, (DLGPROC)lpProc);
\r
6714 FreeProcInstance(lpProc);
\r
6719 /*---------------------------------------------------------------------------*\
\r
6721 * Ics Interaction console functions
\r
6723 \*---------------------------------------------------------------------------*/
\r
6725 #define HISTORY_SIZE 64
\r
6726 static char *history[HISTORY_SIZE];
\r
6727 int histIn = 0, histP = 0;
\r
6730 SaveInHistory(char *cmd)
\r
6732 if (history[histIn] != NULL) {
\r
6733 free(history[histIn]);
\r
6734 history[histIn] = NULL;
\r
6736 if (*cmd == NULLCHAR) return;
\r
6737 history[histIn] = StrSave(cmd);
\r
6738 histIn = (histIn + 1) % HISTORY_SIZE;
\r
6739 if (history[histIn] != NULL) {
\r
6740 free(history[histIn]);
\r
6741 history[histIn] = NULL;
\r
6747 PrevInHistory(char *cmd)
\r
6750 if (histP == histIn) {
\r
6751 if (history[histIn] != NULL) free(history[histIn]);
\r
6752 history[histIn] = StrSave(cmd);
\r
6754 newhp = (histP - 1 + HISTORY_SIZE) % HISTORY_SIZE;
\r
6755 if (newhp == histIn || history[newhp] == NULL) return NULL;
\r
6757 return history[histP];
\r
6763 if (histP == histIn) return NULL;
\r
6764 histP = (histP + 1) % HISTORY_SIZE;
\r
6765 return history[histP];
\r
6769 LoadIcsTextMenu(IcsTextMenuEntry *e)
\r
6773 hmenu = LoadMenu(hInst, "TextMenu");
\r
6774 h = GetSubMenu(hmenu, 0);
\r
6776 if (strcmp(e->item, "-") == 0) {
\r
6777 AppendMenu(h, MF_SEPARATOR, 0, 0);
\r
6778 } else { // [HGM] re-written a bit to use only one AppendMenu call for both cases (| or no |)
\r
6779 int flags = MF_STRING, j = 0;
\r
6780 if (e->item[0] == '|') {
\r
6781 flags |= MF_MENUBARBREAK;
\r
6784 if(!strcmp(e->command, "none")) flags |= MF_GRAYED; // [HGM] chatclick: provide inactive dummy
\r
6785 AppendMenu(h, flags, IDM_CommandX + i, e->item + j);
\r
6793 WNDPROC consoleTextWindowProc;
\r
6796 CommandX(HWND hwnd, char *command, BOOLEAN getname, BOOLEAN immediate)
\r
6798 char buf[MSG_SIZ], name[MSG_SIZ];
\r
6799 HWND hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
6803 SetWindowText(hInput, command);
\r
6805 SendMessage(hInput, WM_CHAR, '\r', 0);
\r
6807 sel.cpMin = 999999;
\r
6808 sel.cpMax = 999999;
\r
6809 SendMessage(hInput, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
6814 SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);
\r
6815 if (sel.cpMin == sel.cpMax) {
\r
6816 /* Expand to surrounding word */
\r
6819 tr.chrg.cpMax = sel.cpMin;
\r
6820 tr.chrg.cpMin = --sel.cpMin;
\r
6821 if (sel.cpMin < 0) break;
\r
6822 tr.lpstrText = name;
\r
6823 SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr);
\r
6824 } while (isalpha(name[0]) || isdigit(name[0]) || name[0] == '-');
\r
6828 tr.chrg.cpMin = sel.cpMax;
\r
6829 tr.chrg.cpMax = ++sel.cpMax;
\r
6830 tr.lpstrText = name;
\r
6831 if (SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr) < 1) break;
\r
6832 } while (isalpha(name[0]) || isdigit(name[0]) || name[0] == '-');
\r
6835 if (sel.cpMax == sel.cpMin || sel.cpMax - sel.cpMin > MSG_SIZ/2) {
\r
6836 MessageBeep(MB_ICONEXCLAMATION);
\r
6840 tr.lpstrText = name;
\r
6841 SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr);
\r
6843 if (sel.cpMax - sel.cpMin > MSG_SIZ/2) {
\r
6844 MessageBeep(MB_ICONEXCLAMATION);
\r
6847 SendMessage(hwnd, EM_GETSELTEXT, 0, (LPARAM) name);
\r
6850 if(strstr(command, "%s")) snprintf(buf, MSG_SIZ, command, name); else
\r
6851 snprintf(buf, MSG_SIZ, "%s %s", command, name);
\r
6852 SetWindowText(hInput, buf);
\r
6853 SendMessage(hInput, WM_CHAR, '\r', 0);
\r
6855 if(!strcmp(command, "chat")) { ChatPopUp(name); return; }
\r
6856 snprintf(buf, MSG_SIZ, "%s %s ", command, name); /* trailing space */
\r
6857 SetWindowText(hInput, buf);
\r
6858 sel.cpMin = 999999;
\r
6859 sel.cpMax = 999999;
\r
6860 SendMessage(hInput, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
6866 ConsoleTextSubclass(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
\r
6871 switch (message) {
\r
6873 if (!(GetKeyState(VK_CONTROL) & ~1)) break;
\r
6874 if(wParam=='R') return 0;
\r
6877 SendMessage(hwnd, EM_LINESCROLL, 0, -999999);
\r
6880 sel.cpMin = 999999;
\r
6881 sel.cpMax = 999999;
\r
6882 SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
6883 SendMessage(hwnd, EM_SCROLLCARET, 0, 0);
\r
6888 if(wParam != '\022') {
\r
6889 if (wParam == '\t') {
\r
6890 if (GetKeyState(VK_SHIFT) < 0) {
\r
6892 if (IsIconic(hwndMain)) ShowWindow(hwndMain, SW_RESTORE);
\r
6893 if (buttonDesc[0].hwnd) {
\r
6894 SetFocus(buttonDesc[0].hwnd);
\r
6896 SetFocus(hwndMain);
\r
6900 SetFocus(GetDlgItem(hwndConsole, OPT_ConsoleInput));
\r
6903 hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
6904 JAWS_DELETE( SetFocus(hInput); )
\r
6905 SendMessage(hInput, message, wParam, lParam);
\r
6908 } // [HGM] navigate: for Ctrl+R, flow into next case (moved up here) to summon up menu
\r
6910 case WM_RBUTTONDOWN:
\r
6911 if (!(GetKeyState(VK_SHIFT) & ~1)) {
\r
6912 /* Move selection here if it was empty */
\r
6914 pt.x = LOWORD(lParam);
\r
6915 pt.y = HIWORD(lParam);
\r
6916 SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);
\r
6917 if (sel.cpMin == sel.cpMax) {
\r
6918 if(lParam != -1) sel.cpMin = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&pt); /*doc is wrong*/
\r
6919 sel.cpMax = sel.cpMin;
\r
6920 SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
6922 SendMessage(hwnd, EM_HIDESELECTION, FALSE, FALSE);
\r
6923 { // [HGM] chatclick: code moved here from WM_RBUTTONUP case, to have menu appear on down-click
\r
6925 HMENU hmenu = LoadIcsTextMenu(icsTextMenuEntry);
\r
6926 SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);
\r
6927 if (sel.cpMin == sel.cpMax) {
\r
6928 EnableMenuItem(hmenu, IDM_Copy, MF_BYCOMMAND|MF_GRAYED);
\r
6929 EnableMenuItem(hmenu, IDM_QuickPaste, MF_BYCOMMAND|MF_GRAYED);
\r
6931 if (!IsClipboardFormatAvailable(CF_TEXT)) {
\r
6932 EnableMenuItem(hmenu, IDM_Paste, MF_BYCOMMAND|MF_GRAYED);
\r
6934 pt.x = LOWORD(lParam)-30; // [HGM] chatclick: make menu pop up with pointer above upper-right item
\r
6935 pt.y = HIWORD(lParam)-10; // make it appear as if mouse moved there, so it will be selected on up-click
\r
6936 PostMessage(hwnd, WM_MOUSEMOVE, wParam, lParam+5);
\r
6937 MenuPopup(hwnd, pt, hmenu, -1);
\r
6941 case WM_RBUTTONUP:
\r
6942 if (GetKeyState(VK_SHIFT) & ~1) {
\r
6943 SendDlgItemMessage(hwndConsole, OPT_ConsoleText,
\r
6944 WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);
\r
6948 hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
6950 return SendMessage(hInput, message, wParam, lParam);
\r
6951 case WM_MBUTTONDOWN:
\r
6952 return SendMessage(hwnd, WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);
\r
6954 switch (LOWORD(wParam)) {
\r
6955 case IDM_QuickPaste:
\r
6957 SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);
\r
6958 if (sel.cpMin == sel.cpMax) {
\r
6959 MessageBeep(MB_ICONEXCLAMATION);
\r
6962 SendMessage(hwnd, WM_COPY, 0, 0);
\r
6963 hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
6964 SendMessage(hInput, WM_PASTE, 0, 0);
\r
6969 SendMessage(hwnd, WM_CUT, 0, 0);
\r
6972 SendMessage(hwnd, WM_PASTE, 0, 0);
\r
6975 SendMessage(hwnd, WM_COPY, 0, 0);
\r
6979 int i = LOWORD(wParam) - IDM_CommandX;
\r
6980 if (i >= 0 && i < ICS_TEXT_MENU_SIZE &&
\r
6981 icsTextMenuEntry[i].command != NULL) {
\r
6982 CommandX(hwnd, icsTextMenuEntry[i].command,
\r
6983 icsTextMenuEntry[i].getname,
\r
6984 icsTextMenuEntry[i].immediate);
\r
6992 return (*consoleTextWindowProc)(hwnd, message, wParam, lParam);
\r
6995 WNDPROC consoleInputWindowProc;
\r
6998 ConsoleInputSubclass(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
\r
7000 char buf[MSG_SIZ];
\r
7002 static BOOL sendNextChar = FALSE;
\r
7003 static BOOL quoteNextChar = FALSE;
\r
7004 InputSource *is = consoleInputSource;
\r
7008 switch (message) {
\r
7010 if (!appData.localLineEditing || sendNextChar) {
\r
7011 is->buf[0] = (CHAR) wParam;
\r
7013 SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);
\r
7014 sendNextChar = FALSE;
\r
7017 if (quoteNextChar) {
\r
7018 buf[0] = (char) wParam;
\r
7019 buf[1] = NULLCHAR;
\r
7020 SendMessage(hwnd, EM_REPLACESEL, TRUE, (LPARAM) buf);
\r
7021 quoteNextChar = FALSE;
\r
7025 case '\r': /* Enter key */
\r
7026 is->count = GetWindowText(hwnd, is->buf, INPUT_SOURCE_BUF_SIZE-1);
\r
7027 if (consoleEcho) SaveInHistory(is->buf);
\r
7028 is->buf[is->count++] = '\n';
\r
7029 is->buf[is->count] = NULLCHAR;
\r
7030 SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);
\r
7031 if (consoleEcho) {
\r
7032 ConsoleOutput(is->buf, is->count, TRUE);
\r
7033 } else if (appData.localLineEditing) {
\r
7034 ConsoleOutput("\n", 1, TRUE);
\r
7037 case '\033': /* Escape key */
\r
7038 SetWindowText(hwnd, "");
\r
7039 cf.cbSize = sizeof(CHARFORMAT);
\r
7040 cf.dwMask = CFM_COLOR|CFM_BOLD|CFM_ITALIC|CFM_UNDERLINE|CFM_STRIKEOUT;
\r
7041 if (consoleEcho) {
\r
7042 cf.crTextColor = textAttribs[ColorNormal].color;
\r
7044 cf.crTextColor = COLOR_ECHOOFF;
\r
7046 cf.dwEffects = textAttribs[ColorNormal].effects;
\r
7047 SendMessage(hwnd, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf);
\r
7049 case '\t': /* Tab key */
\r
7050 if (GetKeyState(VK_SHIFT) < 0) {
\r
7052 SetFocus(GetDlgItem(hwndConsole, OPT_ConsoleText));
\r
7055 if (IsIconic(hwndMain)) ShowWindow(hwndMain, SW_RESTORE);
\r
7056 if (buttonDesc[0].hwnd) {
\r
7057 SetFocus(buttonDesc[0].hwnd);
\r
7059 SetFocus(hwndMain);
\r
7063 case '\023': /* Ctrl+S */
\r
7064 sendNextChar = TRUE;
\r
7066 case '\021': /* Ctrl+Q */
\r
7067 quoteNextChar = TRUE;
\r
7077 GetWindowText(hwnd, buf, MSG_SIZ);
\r
7078 p = PrevInHistory(buf);
\r
7080 SetWindowText(hwnd, p);
\r
7081 sel.cpMin = 999999;
\r
7082 sel.cpMax = 999999;
\r
7083 SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
7088 p = NextInHistory();
\r
7090 SetWindowText(hwnd, p);
\r
7091 sel.cpMin = 999999;
\r
7092 sel.cpMax = 999999;
\r
7093 SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
7099 if (!(GetKeyState(VK_CONTROL) & ~1)) break;
\r
7103 SendDlgItemMessage(hwndConsole, OPT_ConsoleText, message, wParam, lParam);
\r
7107 case WM_MBUTTONDOWN:
\r
7108 SendDlgItemMessage(hwndConsole, OPT_ConsoleText,
\r
7109 WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);
\r
7111 case WM_RBUTTONUP:
\r
7112 if (GetKeyState(VK_SHIFT) & ~1) {
\r
7113 SendDlgItemMessage(hwndConsole, OPT_ConsoleText,
\r
7114 WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);
\r
7118 hmenu = LoadMenu(hInst, "InputMenu");
\r
7119 SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);
\r
7120 if (sel.cpMin == sel.cpMax) {
\r
7121 EnableMenuItem(hmenu, IDM_Copy, MF_BYCOMMAND|MF_GRAYED);
\r
7122 EnableMenuItem(hmenu, IDM_Cut, MF_BYCOMMAND|MF_GRAYED);
\r
7124 if (!IsClipboardFormatAvailable(CF_TEXT)) {
\r
7125 EnableMenuItem(hmenu, IDM_Paste, MF_BYCOMMAND|MF_GRAYED);
\r
7127 pt.x = LOWORD(lParam);
\r
7128 pt.y = HIWORD(lParam);
\r
7129 MenuPopup(hwnd, pt, hmenu, -1);
\r
7133 switch (LOWORD(wParam)) {
\r
7135 SendMessage(hwnd, EM_UNDO, 0, 0);
\r
7137 case IDM_SelectAll:
\r
7139 sel.cpMax = -1; /*999999?*/
\r
7140 SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
7143 SendMessage(hwnd, WM_CUT, 0, 0);
\r
7146 SendMessage(hwnd, WM_PASTE, 0, 0);
\r
7149 SendMessage(hwnd, WM_COPY, 0, 0);
\r
7154 return (*consoleInputWindowProc)(hwnd, message, wParam, lParam);
\r
7157 #define CO_MAX 100000
\r
7158 #define CO_TRIM 1000
\r
7161 ConsoleWndProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
7163 static SnapData sd;
\r
7164 HWND hText, hInput;
\r
7166 static int sizeX, sizeY;
\r
7167 int newSizeX, newSizeY;
\r
7171 hText = GetDlgItem(hDlg, OPT_ConsoleText);
\r
7172 hInput = GetDlgItem(hDlg, OPT_ConsoleInput);
\r
7174 switch (message) {
\r
7176 if (((NMHDR*)lParam)->code == EN_LINK)
\r
7178 ENLINK *pLink = (ENLINK*)lParam;
\r
7179 if (pLink->msg == WM_LBUTTONUP)
\r
7183 tr.chrg = pLink->chrg;
\r
7184 tr.lpstrText = malloc(1+tr.chrg.cpMax-tr.chrg.cpMin);
\r
7185 SendMessage(hText, EM_GETTEXTRANGE, 0, (LPARAM)&tr);
\r
7186 ShellExecute(NULL, "open", tr.lpstrText, NULL, NULL, SW_SHOW);
\r
7187 free(tr.lpstrText);
\r
7191 case WM_INITDIALOG: /* message: initialize dialog box */
\r
7192 hwndConsole = hDlg;
\r
7194 consoleTextWindowProc = (WNDPROC)
\r
7195 SetWindowLongPtr(hText, GWLP_WNDPROC, (LONG_PTR) ConsoleTextSubclass);
\r
7196 SendMessage(hText, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);
\r
7197 consoleInputWindowProc = (WNDPROC)
\r
7198 SetWindowLongPtr(hInput, GWLP_WNDPROC, (LONG_PTR) ConsoleInputSubclass);
\r
7199 SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);
\r
7200 Colorize(ColorNormal, TRUE);
\r
7201 SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &consoleCF);
\r
7202 ChangedConsoleFont();
\r
7203 GetClientRect(hDlg, &rect);
\r
7204 sizeX = rect.right;
\r
7205 sizeY = rect.bottom;
\r
7206 if (wpConsole.x != CW_USEDEFAULT && wpConsole.y != CW_USEDEFAULT &&
\r
7207 wpConsole.width != CW_USEDEFAULT && wpConsole.height != CW_USEDEFAULT) {
\r
7208 WINDOWPLACEMENT wp;
\r
7209 EnsureOnScreen(&wpConsole.x, &wpConsole.y, 0, 0);
\r
7210 wp.length = sizeof(WINDOWPLACEMENT);
\r
7212 wp.showCmd = SW_SHOW;
\r
7213 wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;
\r
7214 wp.rcNormalPosition.left = wpConsole.x;
\r
7215 wp.rcNormalPosition.right = wpConsole.x + wpConsole.width;
\r
7216 wp.rcNormalPosition.top = wpConsole.y;
\r
7217 wp.rcNormalPosition.bottom = wpConsole.y + wpConsole.height;
\r
7218 SetWindowPlacement(hDlg, &wp);
\r
7221 // [HGM] Chessknight's change 2004-07-13
\r
7222 else { /* Determine Defaults */
\r
7223 WINDOWPLACEMENT wp;
\r
7224 wpConsole.x = wpMain.width + 1;
\r
7225 wpConsole.y = wpMain.y;
\r
7226 wpConsole.width = screenWidth - wpMain.width;
\r
7227 wpConsole.height = wpMain.height;
\r
7228 EnsureOnScreen(&wpConsole.x, &wpConsole.y, 0, 0);
\r
7229 wp.length = sizeof(WINDOWPLACEMENT);
\r
7231 wp.showCmd = SW_SHOW;
\r
7232 wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;
\r
7233 wp.rcNormalPosition.left = wpConsole.x;
\r
7234 wp.rcNormalPosition.right = wpConsole.x + wpConsole.width;
\r
7235 wp.rcNormalPosition.top = wpConsole.y;
\r
7236 wp.rcNormalPosition.bottom = wpConsole.y + wpConsole.height;
\r
7237 SetWindowPlacement(hDlg, &wp);
\r
7240 // Allow hText to highlight URLs and send notifications on them
\r
7241 wMask = (WORD) SendMessage(hText, EM_GETEVENTMASK, 0, 0L);
\r
7242 SendMessage(hText, EM_SETEVENTMASK, 0, wMask | ENM_LINK);
\r
7243 SendMessage(hText, EM_AUTOURLDETECT, TRUE, 0L);
\r
7244 SetWindowLongPtr(hText, GWLP_USERDATA, 79); // initialize the text window's width
\r
7258 if (IsIconic(hDlg)) break;
\r
7259 newSizeX = LOWORD(lParam);
\r
7260 newSizeY = HIWORD(lParam);
\r
7261 if (sizeX != newSizeX || sizeY != newSizeY) {
\r
7262 RECT rectText, rectInput;
\r
7264 int newTextHeight, newTextWidth;
\r
7265 GetWindowRect(hText, &rectText);
\r
7266 newTextWidth = rectText.right - rectText.left + newSizeX - sizeX;
\r
7267 newTextHeight = rectText.bottom - rectText.top + newSizeY - sizeY;
\r
7268 if (newTextHeight < 0) {
\r
7269 newSizeY += -newTextHeight;
\r
7270 newTextHeight = 0;
\r
7272 SetWindowPos(hText, NULL, 0, 0,
\r
7273 newTextWidth, newTextHeight, SWP_NOZORDER|SWP_NOMOVE);
\r
7274 GetWindowRect(hInput, &rectInput); /* gives screen coords */
\r
7275 pt.x = rectInput.left;
\r
7276 pt.y = rectInput.top + newSizeY - sizeY;
\r
7277 ScreenToClient(hDlg, &pt);
\r
7278 SetWindowPos(hInput, NULL,
\r
7279 pt.x, pt.y, /* needs client coords */
\r
7280 rectInput.right - rectInput.left + newSizeX - sizeX,
\r
7281 rectInput.bottom - rectInput.top, SWP_NOZORDER);
\r
7287 case WM_GETMINMAXINFO:
\r
7288 /* Prevent resizing window too small */
\r
7289 mmi = (MINMAXINFO *) lParam;
\r
7290 mmi->ptMinTrackSize.x = 100;
\r
7291 mmi->ptMinTrackSize.y = 100;
\r
7294 /* [AS] Snapping */
\r
7295 case WM_ENTERSIZEMOVE:
\r
7296 return OnEnterSizeMove( &sd, hDlg, wParam, lParam );
\r
7299 return OnSizing( &sd, hDlg, wParam, lParam );
\r
7302 return OnMoving( &sd, hDlg, wParam, lParam );
\r
7304 case WM_EXITSIZEMOVE:
\r
7305 UpdateICSWidth(hText);
\r
7306 return OnExitSizeMove( &sd, hDlg, wParam, lParam );
\r
7309 return DefWindowProc(hDlg, message, wParam, lParam);
\r
7317 if (hwndConsole) return;
\r
7318 hCons = CreateDialog(hInst, szConsoleName, 0, NULL);
\r
7319 SendMessage(hCons, WM_INITDIALOG, 0, 0);
\r
7324 ConsoleOutput(char* data, int length, int forceVisible)
\r
7329 char buf[CO_MAX+1];
\r
7332 static int delayLF = 0;
\r
7333 CHARRANGE savesel, sel;
\r
7335 if (hwndConsole == NULL || length > CO_MAX-100 || length == 0) return;
\r
7343 while (length--) {
\r
7351 } else if (*p == '\007') {
\r
7352 MyPlaySound(&sounds[(int)SoundBell]);
\r
7359 hText = GetDlgItem(hwndConsole, OPT_ConsoleText);
\r
7360 SendMessage(hText, EM_HIDESELECTION, TRUE, FALSE);
\r
7361 /* Save current selection */
\r
7362 SendMessage(hText, EM_EXGETSEL, 0, (LPARAM)&savesel);
\r
7363 exlen = GetWindowTextLength(hText);
\r
7364 /* Find out whether current end of text is visible */
\r
7365 SendMessage(hText, EM_GETRECT, 0, (LPARAM) &rect);
\r
7366 SendMessage(hText, EM_POSFROMCHAR, (WPARAM) &pEnd, exlen);
\r
7367 /* Trim existing text if it's too long */
\r
7368 if (exlen + (q - buf) > CO_MAX) {
\r
7369 trim = (CO_TRIM > (q - buf)) ? CO_TRIM : (q - buf);
\r
7372 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
7373 SendMessage(hText, EM_REPLACESEL, 0, (LPARAM)"");
\r
7375 savesel.cpMin -= trim;
\r
7376 savesel.cpMax -= trim;
\r
7377 if (exlen < 0) exlen = 0;
\r
7378 if (savesel.cpMin < 0) savesel.cpMin = 0;
\r
7379 if (savesel.cpMax < savesel.cpMin) savesel.cpMax = savesel.cpMin;
\r
7381 /* Append the new text */
\r
7382 sel.cpMin = exlen;
\r
7383 sel.cpMax = exlen;
\r
7384 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
7385 SendMessage(hText, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&consoleCF);
\r
7386 SendMessage(hText, EM_REPLACESEL, 0, (LPARAM) buf);
\r
7387 if (forceVisible || exlen == 0 ||
\r
7388 (rect.left <= pEnd.x && pEnd.x < rect.right &&
\r
7389 rect.top <= pEnd.y && pEnd.y < rect.bottom)) {
\r
7390 /* Scroll to make new end of text visible if old end of text
\r
7391 was visible or new text is an echo of user typein */
\r
7392 sel.cpMin = 9999999;
\r
7393 sel.cpMax = 9999999;
\r
7394 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
7395 SendMessage(hText, EM_HIDESELECTION, FALSE, FALSE);
\r
7396 SendMessage(hText, EM_SCROLLCARET, 0, 0);
\r
7397 SendMessage(hText, EM_HIDESELECTION, TRUE, FALSE);
\r
7399 if (savesel.cpMax == exlen || forceVisible) {
\r
7400 /* Move insert point to new end of text if it was at the old
\r
7401 end of text or if the new text is an echo of user typein */
\r
7402 sel.cpMin = 9999999;
\r
7403 sel.cpMax = 9999999;
\r
7404 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
7406 /* Restore previous selection */
\r
7407 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&savesel);
\r
7409 SendMessage(hText, EM_HIDESELECTION, FALSE, FALSE);
\r
7416 DisplayHoldingsCount(HDC hdc, int x, int y, int rightAlign, int copyNumber)
\r
7420 COLORREF oldFg, oldBg;
\r
7425 snprintf(buf, sizeof(buf)/sizeof(buf[0]),"%d", copyNumber); else buf[0] = 0;
\r
7427 oldFg = SetTextColor(hdc, RGB(255, 255, 255)); /* white */
\r
7428 oldBg = SetBkColor(hdc, RGB(0, 0, 0)); /* black */
\r
7429 oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);
\r
7432 rect.right = x + squareSize;
\r
7434 rect.bottom = y + squareSize;
\r
7437 ExtTextOut(hdc, x + MESSAGE_LINE_LEFTMARGIN
\r
7438 + (rightAlign ? (squareSize*2)/3 : 0),
\r
7439 y, ETO_CLIPPED|ETO_OPAQUE,
\r
7440 &rect, str, strlen(str), NULL);
\r
7442 (void) SetTextColor(hdc, oldFg);
\r
7443 (void) SetBkColor(hdc, oldBg);
\r
7444 (void) SelectObject(hdc, oldFont);
\r
7448 DisplayAClock(HDC hdc, int timeRemaining, int highlight,
\r
7449 RECT *rect, char *color, char *flagFell)
\r
7453 COLORREF oldFg, oldBg;
\r
7456 if (appData.clockMode) {
\r
7458 snprintf(buf, sizeof(buf)/sizeof(buf[0]), "%c %s %s", color[0], TimeString(timeRemaining), flagFell);
\r
7460 snprintf(buf, sizeof(buf)/sizeof(buf[0]), "%s:%c%s %s", color, (logoHeight>0 ? 0 : ' '), TimeString(timeRemaining), flagFell);
\r
7467 oldFg = SetTextColor(hdc, RGB(255, 255, 255)); /* white */
\r
7468 oldBg = SetBkColor(hdc, RGB(0, 0, 0)); /* black */
\r
7470 oldFg = SetTextColor(hdc, RGB(0, 0, 0)); /* black */
\r
7471 oldBg = SetBkColor(hdc, RGB(255, 255, 255)); /* white */
\r
7473 oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);
\r
7477 ExtTextOut(hdc, rect->left + MESSAGE_LINE_LEFTMARGIN,
\r
7478 rect->top, ETO_CLIPPED|ETO_OPAQUE,
\r
7479 rect, str, strlen(str), NULL);
\r
7480 if(logoHeight > 0 && appData.clockMode) {
\r
7482 str += strlen(color)+2;
\r
7483 r.top = rect->top + logoHeight/2;
\r
7484 r.left = rect->left;
\r
7485 r.right = rect->right;
\r
7486 r.bottom = rect->bottom;
\r
7487 ExtTextOut(hdc, rect->left + MESSAGE_LINE_LEFTMARGIN,
\r
7488 r.top, ETO_CLIPPED|ETO_OPAQUE,
\r
7489 &r, str, strlen(str), NULL);
\r
7491 (void) SetTextColor(hdc, oldFg);
\r
7492 (void) SetBkColor(hdc, oldBg);
\r
7493 (void) SelectObject(hdc, oldFont);
\r
7498 DoReadFile(HANDLE hFile, char *buf, int count, DWORD *outCount,
\r
7504 if( count <= 0 ) {
\r
7505 if (appData.debugMode) {
\r
7506 fprintf( debugFP, "DoReadFile: trying to read past end of buffer, overflow = %d\n", count );
\r
7509 return ERROR_INVALID_USER_BUFFER;
\r
7512 ResetEvent(ovl->hEvent);
\r
7513 ovl->Offset = ovl->OffsetHigh = 0;
\r
7514 ok = ReadFile(hFile, buf, count, outCount, ovl);
\r
7518 err = GetLastError();
\r
7519 if (err == ERROR_IO_PENDING) {
\r
7520 ok = GetOverlappedResult(hFile, ovl, outCount, TRUE);
\r
7524 err = GetLastError();
\r
7531 DoWriteFile(HANDLE hFile, char *buf, int count, DWORD *outCount,
\r
7536 ResetEvent(ovl->hEvent);
\r
7537 ovl->Offset = ovl->OffsetHigh = 0;
\r
7538 ok = WriteFile(hFile, buf, count, outCount, ovl);
\r
7542 err = GetLastError();
\r
7543 if (err == ERROR_IO_PENDING) {
\r
7544 ok = GetOverlappedResult(hFile, ovl, outCount, TRUE);
\r
7548 err = GetLastError();
\r
7554 /* [AS] If input is line by line and a line exceed the buffer size, force an error */
\r
7555 void CheckForInputBufferFull( InputSource * is )
\r
7557 if( is->lineByLine && (is->next - is->buf) >= INPUT_SOURCE_BUF_SIZE ) {
\r
7558 /* Look for end of line */
\r
7559 char * p = is->buf;
\r
7561 while( p < is->next && *p != '\n' ) {
\r
7565 if( p >= is->next ) {
\r
7566 if (appData.debugMode) {
\r
7567 fprintf( debugFP, "Input line exceeded buffer size (source id=%lu)\n", is->id );
\r
7570 is->error = ERROR_BROKEN_PIPE; /* [AS] Just any non-successful code! */
\r
7571 is->count = (DWORD) -1;
\r
7572 is->next = is->buf;
\r
7578 InputThread(LPVOID arg)
\r
7583 is = (InputSource *) arg;
\r
7584 ovl.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
\r
7585 ovl.Internal = ovl.InternalHigh = ovl.Offset = ovl.OffsetHigh = 0;
\r
7586 while (is->hThread != NULL) {
\r
7587 is->error = DoReadFile(is->hFile, is->next,
\r
7588 INPUT_SOURCE_BUF_SIZE - (is->next - is->buf),
\r
7589 &is->count, &ovl);
\r
7590 if (is->error == NO_ERROR) {
\r
7591 is->next += is->count;
\r
7593 if (is->error == ERROR_BROKEN_PIPE) {
\r
7594 /* Correct for MS brain damage. EOF reading a pipe is not an error. */
\r
7597 is->count = (DWORD) -1;
\r
7598 /* [AS] The (is->count <= 0) check below is not useful for unsigned values! */
\r
7603 CheckForInputBufferFull( is );
\r
7605 SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);
\r
7607 if( is->count == ((DWORD) -1) ) break; /* [AS] */
\r
7609 if (is->count <= 0) break; /* Quit on EOF or error */
\r
7612 CloseHandle(ovl.hEvent);
\r
7613 CloseHandle(is->hFile);
\r
7615 if (appData.debugMode) {
\r
7616 fprintf( debugFP, "Input thread terminated (id=%lu, error=%d, count=%ld)\n", is->id, is->error, is->count );
\r
7623 /* Windows 95 beta 2 won't let you do overlapped i/o on a console or pipe */
\r
7625 NonOvlInputThread(LPVOID arg)
\r
7632 is = (InputSource *) arg;
\r
7633 while (is->hThread != NULL) {
\r
7634 is->error = ReadFile(is->hFile, is->next,
\r
7635 INPUT_SOURCE_BUF_SIZE - (is->next - is->buf),
\r
7636 &is->count, NULL) ? NO_ERROR : GetLastError();
\r
7637 if (is->error == NO_ERROR) {
\r
7638 /* Change CRLF to LF */
\r
7639 if (is->next > is->buf) {
\r
7641 i = is->count + 1;
\r
7649 if (prev == '\r' && *p == '\n') {
\r
7661 if (is->error == ERROR_BROKEN_PIPE) {
\r
7662 /* Correct for MS brain damage. EOF reading a pipe is not an error. */
\r
7665 is->count = (DWORD) -1;
\r
7669 CheckForInputBufferFull( is );
\r
7671 SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);
\r
7673 if( is->count == ((DWORD) -1) ) break; /* [AS] */
\r
7675 if (is->count < 0) break; /* Quit on error */
\r
7677 CloseHandle(is->hFile);
\r
7682 SocketInputThread(LPVOID arg)
\r
7686 is = (InputSource *) arg;
\r
7687 while (is->hThread != NULL) {
\r
7688 is->count = recv(is->sock, is->buf, INPUT_SOURCE_BUF_SIZE, 0);
\r
7689 if ((int)is->count == SOCKET_ERROR) {
\r
7690 is->count = (DWORD) -1;
\r
7691 is->error = WSAGetLastError();
\r
7693 is->error = NO_ERROR;
\r
7694 is->next += is->count;
\r
7695 if (is->count == 0 && is->second == is) {
\r
7696 /* End of file on stderr; quit with no message */
\r
7700 SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);
\r
7702 if( is->count == ((DWORD) -1) ) break; /* [AS] */
\r
7704 if (is->count <= 0) break; /* Quit on EOF or error */
\r
7710 InputEvent(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
\r
7714 is = (InputSource *) lParam;
\r
7715 if (is->lineByLine) {
\r
7716 /* Feed in lines one by one */
\r
7717 char *p = is->buf;
\r
7719 while (q < is->next) {
\r
7720 if (*q++ == '\n') {
\r
7721 (is->func)(is, is->closure, p, q - p, NO_ERROR);
\r
7726 /* Move any partial line to the start of the buffer */
\r
7728 while (p < is->next) {
\r
7733 if (is->error != NO_ERROR || is->count == 0) {
\r
7734 /* Notify backend of the error. Note: If there was a partial
\r
7735 line at the end, it is not flushed through. */
\r
7736 (is->func)(is, is->closure, is->buf, is->count, is->error);
\r
7739 /* Feed in the whole chunk of input at once */
\r
7740 (is->func)(is, is->closure, is->buf, is->count, is->error);
\r
7741 is->next = is->buf;
\r
7745 /*---------------------------------------------------------------------------*\
\r
7747 * Menu enables. Used when setting various modes.
\r
7749 \*---------------------------------------------------------------------------*/
\r
7757 GreyRevert(Boolean grey)
\r
7758 { // [HGM] vari: for retracting variations in local mode
\r
7759 HMENU hmenu = GetMenu(hwndMain);
\r
7760 EnableMenuItem(hmenu, IDM_Revert, MF_BYCOMMAND|(grey ? MF_GRAYED : MF_ENABLED));
\r
7761 EnableMenuItem(hmenu, IDM_Annotate, MF_BYCOMMAND|(grey ? MF_GRAYED : MF_ENABLED));
\r
7765 SetMenuEnables(HMENU hmenu, Enables *enab)
\r
7767 while (enab->item > 0) {
\r
7768 (void) EnableMenuItem(hmenu, enab->item, enab->flags);
\r
7773 Enables gnuEnables[] = {
\r
7774 { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },
\r
7775 { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },
\r
7776 { IDM_IcsClient, MF_BYCOMMAND|MF_GRAYED },
\r
7777 { IDM_Accept, MF_BYCOMMAND|MF_GRAYED },
\r
7778 { IDM_Decline, MF_BYCOMMAND|MF_GRAYED },
\r
7779 { IDM_Rematch, MF_BYCOMMAND|MF_GRAYED },
\r
7780 { IDM_Adjourn, MF_BYCOMMAND|MF_GRAYED },
\r
7781 { IDM_StopExamining, MF_BYCOMMAND|MF_GRAYED },
\r
7782 { IDM_StopObserving, MF_BYCOMMAND|MF_GRAYED },
\r
7783 { IDM_Upload, MF_BYCOMMAND|MF_GRAYED },
\r
7784 { IDM_Revert, MF_BYCOMMAND|MF_GRAYED },
\r
7785 { IDM_Annotate, MF_BYCOMMAND|MF_GRAYED },
\r
7786 { IDM_NewChat, MF_BYCOMMAND|MF_GRAYED },
\r
7790 Enables icsEnables[] = {
\r
7791 { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },
\r
7792 { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },
\r
7793 { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },
\r
7794 { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },
\r
7795 { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },
\r
7796 { IDM_Match, MF_BYCOMMAND|MF_GRAYED },
\r
7797 { IDM_MachineBoth, MF_BYCOMMAND|MF_GRAYED },
\r
7798 { IDM_AnalysisMode, MF_BYCOMMAND|MF_ENABLED },
\r
7799 { IDM_AnalyzeFile, MF_BYCOMMAND|MF_GRAYED },
\r
7800 { IDM_TimeControl, MF_BYCOMMAND|MF_GRAYED },
\r
7801 { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },
\r
7802 { IDM_Hint, MF_BYCOMMAND|MF_GRAYED },
\r
7803 { IDM_Book, MF_BYCOMMAND|MF_GRAYED },
\r
7804 { IDM_IcsOptions, MF_BYCOMMAND|MF_ENABLED },
\r
7805 { IDM_Engine1Options, MF_BYCOMMAND|MF_GRAYED },
\r
7806 { IDM_Engine2Options, MF_BYCOMMAND|MF_GRAYED },
\r
7807 { IDM_Annotate, MF_BYCOMMAND|MF_GRAYED },
\r
7812 Enables zippyEnables[] = {
\r
7813 { IDM_MoveNow, MF_BYCOMMAND|MF_ENABLED },
\r
7814 { IDM_Hint, MF_BYCOMMAND|MF_ENABLED },
\r
7815 { IDM_Book, MF_BYCOMMAND|MF_ENABLED },
\r
7816 { IDM_Engine1Options, MF_BYCOMMAND|MF_ENABLED },
\r
7821 Enables ncpEnables[] = {
\r
7822 { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },
\r
7823 { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },
\r
7824 { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },
\r
7825 { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },
\r
7826 { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },
\r
7827 { IDM_Match, MF_BYCOMMAND|MF_GRAYED },
\r
7828 { IDM_AnalysisMode, MF_BYCOMMAND|MF_GRAYED },
\r
7829 { IDM_AnalyzeFile, MF_BYCOMMAND|MF_GRAYED },
\r
7830 { IDM_IcsClient, MF_BYCOMMAND|MF_GRAYED },
\r
7831 { ACTION_POS, MF_BYPOSITION|MF_GRAYED },
\r
7832 { IDM_Revert, MF_BYCOMMAND|MF_GRAYED },
\r
7833 { IDM_Annotate, MF_BYCOMMAND|MF_GRAYED },
\r
7834 { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },
\r
7835 { IDM_RetractMove, MF_BYCOMMAND|MF_GRAYED },
\r
7836 { IDM_TimeControl, MF_BYCOMMAND|MF_GRAYED },
\r
7837 { IDM_Hint, MF_BYCOMMAND|MF_GRAYED },
\r
7838 { IDM_Book, MF_BYCOMMAND|MF_GRAYED },
\r
7839 { IDM_MachineBoth, MF_BYCOMMAND|MF_GRAYED },
\r
7840 { IDM_NewChat, MF_BYCOMMAND|MF_GRAYED },
\r
7841 { IDM_Engine1Options, MF_BYCOMMAND|MF_GRAYED },
\r
7842 { IDM_Engine2Options, MF_BYCOMMAND|MF_GRAYED },
\r
7843 { IDM_Sounds, MF_BYCOMMAND|MF_GRAYED },
\r
7847 Enables trainingOnEnables[] = {
\r
7848 { IDM_EditComment, MF_BYCOMMAND|MF_GRAYED },
\r
7849 { IDM_Comment, MF_BYCOMMAND|MF_GRAYED },
\r
7850 { IDM_Pause, MF_BYCOMMAND|MF_GRAYED },
\r
7851 { IDM_Forward, MF_BYCOMMAND|MF_GRAYED },
\r
7852 { IDM_Backward, MF_BYCOMMAND|MF_GRAYED },
\r
7853 { IDM_ToEnd, MF_BYCOMMAND|MF_GRAYED },
\r
7854 { IDM_ToStart, MF_BYCOMMAND|MF_GRAYED },
\r
7855 { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },
\r
7856 { IDM_TruncateGame, MF_BYCOMMAND|MF_GRAYED },
\r
7860 Enables trainingOffEnables[] = {
\r
7861 { IDM_EditComment, MF_BYCOMMAND|MF_ENABLED },
\r
7862 { IDM_Comment, MF_BYCOMMAND|MF_ENABLED },
\r
7863 { IDM_Pause, MF_BYCOMMAND|MF_ENABLED },
\r
7864 { IDM_Forward, MF_BYCOMMAND|MF_ENABLED },
\r
7865 { IDM_Backward, MF_BYCOMMAND|MF_ENABLED },
\r
7866 { IDM_ToEnd, MF_BYCOMMAND|MF_ENABLED },
\r
7867 { IDM_ToStart, MF_BYCOMMAND|MF_ENABLED },
\r
7868 { IDM_MoveNow, MF_BYCOMMAND|MF_ENABLED },
\r
7869 { IDM_TruncateGame, MF_BYCOMMAND|MF_ENABLED },
\r
7873 /* These modify either ncpEnables or gnuEnables */
\r
7874 Enables cmailEnables[] = {
\r
7875 { IDM_MailMove, MF_BYCOMMAND|MF_ENABLED },
\r
7876 { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_ENABLED },
\r
7877 { ACTION_POS, MF_BYPOSITION|MF_ENABLED },
\r
7878 { IDM_CallFlag, MF_BYCOMMAND|MF_GRAYED },
\r
7879 { IDM_Draw, MF_BYCOMMAND|MF_ENABLED },
\r
7880 { IDM_Adjourn, MF_BYCOMMAND|MF_GRAYED },
\r
7881 { IDM_Abort, MF_BYCOMMAND|MF_GRAYED },
\r
7885 Enables machineThinkingEnables[] = {
\r
7886 { IDM_LoadGame, MF_BYCOMMAND|MF_GRAYED },
\r
7887 { IDM_LoadNextGame, MF_BYCOMMAND|MF_GRAYED },
\r
7888 { IDM_LoadPrevGame, MF_BYCOMMAND|MF_GRAYED },
\r
7889 { IDM_ReloadGame, MF_BYCOMMAND|MF_GRAYED },
\r
7890 { IDM_PasteGame, MF_BYCOMMAND|MF_GRAYED },
\r
7891 { IDM_LoadPosition, MF_BYCOMMAND|MF_GRAYED },
\r
7892 { IDM_LoadNextPosition, MF_BYCOMMAND|MF_GRAYED },
\r
7893 { IDM_LoadPrevPosition, MF_BYCOMMAND|MF_GRAYED },
\r
7894 { IDM_ReloadPosition, MF_BYCOMMAND|MF_GRAYED },
\r
7895 { IDM_PastePosition, MF_BYCOMMAND|MF_GRAYED },
\r
7896 { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },
\r
7897 { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },
\r
7898 { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },
\r
7899 { IDM_Match, MF_BYCOMMAND|MF_GRAYED },
\r
7900 { IDM_TypeInMove, MF_BYCOMMAND|MF_GRAYED },
\r
7901 { IDM_RetractMove, MF_BYCOMMAND|MF_GRAYED },
\r
7905 Enables userThinkingEnables[] = {
\r
7906 { IDM_LoadGame, MF_BYCOMMAND|MF_ENABLED },
\r
7907 { IDM_LoadNextGame, MF_BYCOMMAND|MF_ENABLED },
\r
7908 { IDM_LoadPrevGame, MF_BYCOMMAND|MF_ENABLED },
\r
7909 { IDM_ReloadGame, MF_BYCOMMAND|MF_ENABLED },
\r
7910 { IDM_PasteGame, MF_BYCOMMAND|MF_ENABLED },
\r
7911 { IDM_LoadPosition, MF_BYCOMMAND|MF_ENABLED },
\r
7912 { IDM_LoadNextPosition, MF_BYCOMMAND|MF_ENABLED },
\r
7913 { IDM_LoadPrevPosition, MF_BYCOMMAND|MF_ENABLED },
\r
7914 { IDM_ReloadPosition, MF_BYCOMMAND|MF_ENABLED },
\r
7915 { IDM_PastePosition, MF_BYCOMMAND|MF_ENABLED },
\r
7916 { IDM_MachineWhite, MF_BYCOMMAND|MF_ENABLED },
\r
7917 { IDM_MachineBlack, MF_BYCOMMAND|MF_ENABLED },
\r
7918 { IDM_TwoMachines, MF_BYCOMMAND|MF_ENABLED },
\r
7919 { IDM_Match, MF_BYCOMMAND|MF_ENABLED },
\r
7920 { IDM_TypeInMove, MF_BYCOMMAND|MF_ENABLED },
\r
7921 { IDM_RetractMove, MF_BYCOMMAND|MF_ENABLED },
\r
7925 /*---------------------------------------------------------------------------*\
\r
7927 * Front-end interface functions exported by XBoard.
\r
7928 * Functions appear in same order as prototypes in frontend.h.
\r
7930 \*---------------------------------------------------------------------------*/
\r
7934 static UINT prevChecked = 0;
\r
7935 static int prevPausing = 0;
\r
7938 if (pausing != prevPausing) {
\r
7939 prevPausing = pausing;
\r
7940 (void) CheckMenuItem(GetMenu(hwndMain), IDM_Pause,
\r
7941 MF_BYCOMMAND|(pausing ? MF_CHECKED : MF_UNCHECKED));
\r
7942 if (hwndPause) SetWindowText(hwndPause, pausing ? "C" : "P");
\r
7945 switch (gameMode) {
\r
7946 case BeginningOfGame:
\r
7947 if (appData.icsActive)
\r
7948 nowChecked = IDM_IcsClient;
\r
7949 else if (appData.noChessProgram)
\r
7950 nowChecked = IDM_EditGame;
\r
7952 nowChecked = IDM_MachineBlack;
\r
7954 case MachinePlaysBlack:
\r
7955 nowChecked = IDM_MachineBlack;
\r
7957 case MachinePlaysWhite:
\r
7958 nowChecked = IDM_MachineWhite;
\r
7960 case TwoMachinesPlay:
\r
7961 nowChecked = matchMode ? IDM_Match : IDM_TwoMachines; // [HGM] match
\r
7964 nowChecked = IDM_AnalysisMode;
\r
7967 nowChecked = IDM_AnalyzeFile;
\r
7970 nowChecked = IDM_EditGame;
\r
7972 case PlayFromGameFile:
\r
7973 nowChecked = IDM_LoadGame;
\r
7975 case EditPosition:
\r
7976 nowChecked = IDM_EditPosition;
\r
7979 nowChecked = IDM_Training;
\r
7981 case IcsPlayingWhite:
\r
7982 case IcsPlayingBlack:
\r
7983 case IcsObserving:
\r
7985 nowChecked = IDM_IcsClient;
\r
7992 if (prevChecked != 0)
\r
7993 (void) CheckMenuItem(GetMenu(hwndMain),
\r
7994 prevChecked, MF_BYCOMMAND|MF_UNCHECKED);
\r
7995 if (nowChecked != 0)
\r
7996 (void) CheckMenuItem(GetMenu(hwndMain),
\r
7997 nowChecked, MF_BYCOMMAND|MF_CHECKED);
\r
7999 if (nowChecked == IDM_LoadGame || nowChecked == IDM_Training) {
\r
8000 (void) EnableMenuItem(GetMenu(hwndMain), IDM_Training,
\r
8001 MF_BYCOMMAND|MF_ENABLED);
\r
8003 (void) EnableMenuItem(GetMenu(hwndMain),
\r
8004 IDM_Training, MF_BYCOMMAND|MF_GRAYED);
\r
8007 prevChecked = nowChecked;
\r
8009 /* [DM] icsEngineAnalyze - Do a sceure check too */
\r
8010 if (appData.icsActive) {
\r
8011 if (appData.icsEngineAnalyze) {
\r
8012 (void) CheckMenuItem(GetMenu(hwndMain), IDM_AnalysisMode,
\r
8013 MF_BYCOMMAND|MF_CHECKED);
\r
8015 (void) CheckMenuItem(GetMenu(hwndMain), IDM_AnalysisMode,
\r
8016 MF_BYCOMMAND|MF_UNCHECKED);
\r
8019 DisplayLogos(); // [HGM] logos: mode change could have altered logos
\r
8025 HMENU hmenu = GetMenu(hwndMain);
\r
8026 SetMenuEnables(hmenu, icsEnables);
\r
8027 EnableMenuItem(GetSubMenu(hmenu, OPTIONS_POS), IDM_IcsOptions,
\r
8028 MF_BYCOMMAND|MF_ENABLED);
\r
8030 if (appData.zippyPlay) {
\r
8031 SetMenuEnables(hmenu, zippyEnables);
\r
8032 if (!appData.noChessProgram) /* [DM] icsEngineAnalyze */
\r
8033 (void) EnableMenuItem(GetMenu(hwndMain), IDM_AnalysisMode,
\r
8034 MF_BYCOMMAND|MF_ENABLED);
\r
8042 SetMenuEnables(GetMenu(hwndMain), gnuEnables);
\r
8048 HMENU hmenu = GetMenu(hwndMain);
\r
8049 SetMenuEnables(hmenu, ncpEnables);
\r
8050 DrawMenuBar(hwndMain);
\r
8056 SetMenuEnables(GetMenu(hwndMain), cmailEnables);
\r
8060 SetTrainingModeOn()
\r
8063 SetMenuEnables(GetMenu(hwndMain), trainingOnEnables);
\r
8064 for (i = 0; i < N_BUTTONS; i++) {
\r
8065 if (buttonDesc[i].hwnd != NULL)
\r
8066 EnableWindow(buttonDesc[i].hwnd, FALSE);
\r
8071 VOID SetTrainingModeOff()
\r
8074 SetMenuEnables(GetMenu(hwndMain), trainingOffEnables);
\r
8075 for (i = 0; i < N_BUTTONS; i++) {
\r
8076 if (buttonDesc[i].hwnd != NULL)
\r
8077 EnableWindow(buttonDesc[i].hwnd, TRUE);
\r
8083 SetUserThinkingEnables()
\r
8085 SetMenuEnables(GetMenu(hwndMain), userThinkingEnables);
\r
8089 SetMachineThinkingEnables()
\r
8091 HMENU hMenu = GetMenu(hwndMain);
\r
8092 int flags = MF_BYCOMMAND|MF_ENABLED;
\r
8094 SetMenuEnables(hMenu, machineThinkingEnables);
\r
8096 if (gameMode == MachinePlaysBlack) {
\r
8097 (void)EnableMenuItem(hMenu, IDM_MachineBlack, flags);
\r
8098 } else if (gameMode == MachinePlaysWhite) {
\r
8099 (void)EnableMenuItem(hMenu, IDM_MachineWhite, flags);
\r
8100 } else if (gameMode == TwoMachinesPlay) {
\r
8101 (void)EnableMenuItem(hMenu, matchMode ? IDM_Match : IDM_TwoMachines, flags); // [HGM] match
\r
8107 DisplayTitle(char *str)
\r
8109 char title[MSG_SIZ], *host;
\r
8110 if (str[0] != NULLCHAR) {
\r
8111 safeStrCpy(title, str, sizeof(title)/sizeof(title[0]) );
\r
8112 } else if (appData.icsActive) {
\r
8113 if (appData.icsCommPort[0] != NULLCHAR)
\r
8116 host = appData.icsHost;
\r
8117 snprintf(title, MSG_SIZ, "%s: %s", szTitle, host);
\r
8118 } else if (appData.noChessProgram) {
\r
8119 safeStrCpy(title, szTitle, sizeof(title)/sizeof(title[0]) );
\r
8121 safeStrCpy(title, szTitle, sizeof(title)/sizeof(title[0]) );
\r
8122 strcat(title, ": ");
\r
8123 strcat(title, first.tidy);
\r
8125 SetWindowText(hwndMain, title);
\r
8130 DisplayMessage(char *str1, char *str2)
\r
8134 int remain = MESSAGE_TEXT_MAX - 1;
\r
8137 moveErrorMessageUp = FALSE; /* turned on later by caller if needed */
\r
8138 messageText[0] = NULLCHAR;
\r
8140 len = strlen(str1);
\r
8141 if (len > remain) len = remain;
\r
8142 strncpy(messageText, str1, len);
\r
8143 messageText[len] = NULLCHAR;
\r
8146 if (*str2 && remain >= 2) {
\r
8148 strcat(messageText, " ");
\r
8151 len = strlen(str2);
\r
8152 if (len > remain) len = remain;
\r
8153 strncat(messageText, str2, len);
\r
8155 messageText[MESSAGE_TEXT_MAX - 1] = NULLCHAR;
\r
8157 if (hwndMain == NULL || IsIconic(hwndMain)) return;
\r
8161 hdc = GetDC(hwndMain);
\r
8162 oldFont = SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);
\r
8163 ExtTextOut(hdc, messageRect.left, messageRect.top, ETO_CLIPPED|ETO_OPAQUE,
\r
8164 &messageRect, messageText, strlen(messageText), NULL);
\r
8165 (void) SelectObject(hdc, oldFont);
\r
8166 (void) ReleaseDC(hwndMain, hdc);
\r
8170 DisplayError(char *str, int error)
\r
8172 char buf[MSG_SIZ*2], buf2[MSG_SIZ];
\r
8176 safeStrCpy(buf, str, sizeof(buf)/sizeof(buf[0]) );
\r
8178 len = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,
\r
8179 NULL, error, LANG_NEUTRAL,
\r
8180 (LPSTR) buf2, MSG_SIZ, NULL);
\r
8182 snprintf(buf, 2*MSG_SIZ, "%s:\n%s", str, buf2);
\r
8184 ErrorMap *em = errmap;
\r
8185 while (em->err != 0 && em->err != error) em++;
\r
8186 if (em->err != 0) {
\r
8187 snprintf(buf, 2*MSG_SIZ, "%s:\n%s", str, em->msg);
\r
8189 snprintf(buf, 2*MSG_SIZ, "%s:\nError code %d", str, error);
\r
8194 ErrorPopUp(_("Error"), buf);
\r
8199 DisplayMoveError(char *str)
\r
8201 fromX = fromY = -1;
\r
8202 ClearHighlights();
\r
8203 DrawPosition(FALSE, NULL);
\r
8204 if (appData.popupMoveErrors) {
\r
8205 ErrorPopUp(_("Error"), str);
\r
8207 DisplayMessage(str, "");
\r
8208 moveErrorMessageUp = TRUE;
\r
8213 DisplayFatalError(char *str, int error, int exitStatus)
\r
8215 char buf[2*MSG_SIZ], buf2[MSG_SIZ];
\r
8217 char *label = exitStatus ? _("Fatal Error") : _("Exiting");
\r
8220 len = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,
\r
8221 NULL, error, LANG_NEUTRAL,
\r
8222 (LPSTR) buf2, MSG_SIZ, NULL);
\r
8224 snprintf(buf, 2*MSG_SIZ, "%s:\n%s", str, buf2);
\r
8226 ErrorMap *em = errmap;
\r
8227 while (em->err != 0 && em->err != error) em++;
\r
8228 if (em->err != 0) {
\r
8229 snprintf(buf, 2*MSG_SIZ, "%s:\n%s", str, em->msg);
\r
8231 snprintf(buf, 2*MSG_SIZ, "%s:\nError code %d", str, error);
\r
8236 if (appData.debugMode) {
\r
8237 fprintf(debugFP, "%s: %s\n", label, str);
\r
8239 if (appData.popupExitMessage) {
\r
8240 (void) MessageBox(hwndMain, str, label, MB_OK|
\r
8241 (exitStatus ? MB_ICONSTOP : MB_ICONINFORMATION));
\r
8243 ExitEvent(exitStatus);
\r
8248 DisplayInformation(char *str)
\r
8250 (void) MessageBox(hwndMain, str, _("Information"), MB_OK|MB_ICONINFORMATION);
\r
8255 DisplayNote(char *str)
\r
8257 ErrorPopUp(_("Note"), str);
\r
8262 char *title, *question, *replyPrefix;
\r
8267 QuestionDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
8269 static QuestionParams *qp;
\r
8270 char reply[MSG_SIZ];
\r
8273 switch (message) {
\r
8274 case WM_INITDIALOG:
\r
8275 qp = (QuestionParams *) lParam;
\r
8276 CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));
\r
8277 Translate(hDlg, DLG_Question);
\r
8278 SetWindowText(hDlg, qp->title);
\r
8279 SetDlgItemText(hDlg, OPT_QuestionText, qp->question);
\r
8280 SetFocus(GetDlgItem(hDlg, OPT_QuestionInput));
\r
8284 switch (LOWORD(wParam)) {
\r
8286 safeStrCpy(reply, qp->replyPrefix, sizeof(reply)/sizeof(reply[0]) );
\r
8287 if (*reply) strcat(reply, " ");
\r
8288 len = strlen(reply);
\r
8289 GetDlgItemText(hDlg, OPT_QuestionInput, reply + len, sizeof(reply) - len);
\r
8290 strcat(reply, "\n");
\r
8291 OutputToProcess(qp->pr, reply, strlen(reply), &err);
\r
8292 EndDialog(hDlg, TRUE);
\r
8293 if (err) DisplayFatalError(_("Error writing to chess program"), err, 1);
\r
8296 EndDialog(hDlg, FALSE);
\r
8307 AskQuestion(char* title, char *question, char *replyPrefix, ProcRef pr)
\r
8309 QuestionParams qp;
\r
8313 qp.question = question;
\r
8314 qp.replyPrefix = replyPrefix;
\r
8316 lpProc = MakeProcInstance((FARPROC)QuestionDialog, hInst);
\r
8317 DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_Question),
\r
8318 hwndMain, (DLGPROC)lpProc, (LPARAM)&qp);
\r
8319 FreeProcInstance(lpProc);
\r
8322 /* [AS] Pick FRC position */
\r
8323 LRESULT CALLBACK NewGameFRC_Proc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
8325 static int * lpIndexFRC;
\r
8331 case WM_INITDIALOG:
\r
8332 lpIndexFRC = (int *) lParam;
\r
8334 CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));
\r
8335 Translate(hDlg, DLG_NewGameFRC);
\r
8337 SendDlgItemMessage( hDlg, IDC_NFG_Edit, EM_SETLIMITTEXT, sizeof(buf)-1, 0 );
\r
8338 SetDlgItemInt( hDlg, IDC_NFG_Edit, *lpIndexFRC, TRUE );
\r
8339 SendDlgItemMessage( hDlg, IDC_NFG_Edit, EM_SETSEL, 0, -1 );
\r
8340 SetFocus(GetDlgItem(hDlg, IDC_NFG_Edit));
\r
8345 switch( LOWORD(wParam) ) {
\r
8347 *lpIndexFRC = GetDlgItemInt(hDlg, IDC_NFG_Edit, &index_is_ok, TRUE );
\r
8348 EndDialog( hDlg, 0 );
\r
8349 shuffleOpenings = TRUE; /* [HGM] shuffle: switch shuffling on for as long as we stay in current variant */
\r
8352 EndDialog( hDlg, 1 );
\r
8354 case IDC_NFG_Edit:
\r
8355 if( HIWORD(wParam) == EN_CHANGE ) {
\r
8356 GetDlgItemInt(hDlg, IDC_NFG_Edit, &index_is_ok, TRUE );
\r
8358 EnableWindow( GetDlgItem(hDlg, IDOK), index_is_ok );
\r
8361 case IDC_NFG_Random:
\r
8362 snprintf( buf, sizeof(buf)/sizeof(buf[0]), "%d", myrandom() ); /* [HGM] shuffle: no longer limit to 960 */
\r
8363 SetDlgItemText(hDlg, IDC_NFG_Edit, buf );
\r
8376 int index = appData.defaultFrcPosition;
\r
8377 FARPROC lpProc = MakeProcInstance( (FARPROC) NewGameFRC_Proc, hInst );
\r
8379 result = DialogBoxParam( hInst, MAKEINTRESOURCE(DLG_NewGameFRC), hwndMain, (DLGPROC)lpProc, (LPARAM)&index );
\r
8381 if( result == 0 ) {
\r
8382 appData.defaultFrcPosition = index;
\r
8388 /* [AS] Game list options. Refactored by HGM */
\r
8390 HWND gameListOptionsDialog;
\r
8392 // low-level front-end: clear text edit / list widget
\r
8396 SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_RESETCONTENT, 0, 0 );
\r
8399 // low-level front-end: clear text edit / list widget
\r
8401 GLT_DeSelectList()
\r
8403 SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_SETCURSEL, 0, 0 );
\r
8406 // low-level front-end: append line to text edit / list widget
\r
8408 GLT_AddToList( char *name )
\r
8411 SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_ADDSTRING, 0, (LPARAM) name );
\r
8415 // low-level front-end: get line from text edit / list widget
\r
8417 GLT_GetFromList( int index, char *name )
\r
8420 if( SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_GETTEXT, index, (LPARAM) name ) != LB_ERR )
\r
8426 void GLT_MoveSelection( HWND hDlg, int delta )
\r
8428 int idx1 = (int) SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETCURSEL, 0, 0 );
\r
8429 int idx2 = idx1 + delta;
\r
8430 int count = (int) SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETCOUNT, 0, 0 );
\r
8432 if( idx1 >=0 && idx1 < count && idx2 >= 0 && idx2 < count ) {
\r
8435 SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETTEXT, idx1, (LPARAM) buf );
\r
8436 SendDlgItemMessage( hDlg, IDC_GameListTags, LB_DELETESTRING, idx1, 0 );
\r
8437 SendDlgItemMessage( hDlg, IDC_GameListTags, LB_INSERTSTRING, idx2, (LPARAM) buf );
\r
8438 SendDlgItemMessage( hDlg, IDC_GameListTags, LB_SETCURSEL, idx2, 0 );
\r
8442 LRESULT CALLBACK GameListOptions_Proc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
8446 case WM_INITDIALOG:
\r
8447 gameListOptionsDialog = hDlg; // [HGM] pass through global to keep out off back-end
\r
8449 CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));
\r
8450 Translate(hDlg, DLG_GameListOptions);
\r
8452 /* Initialize list */
\r
8453 GLT_TagsToList( lpUserGLT );
\r
8455 SetFocus( GetDlgItem(hDlg, IDC_GameListTags) );
\r
8460 switch( LOWORD(wParam) ) {
\r
8463 EndDialog( hDlg, 0 );
\r
8466 EndDialog( hDlg, 1 );
\r
8469 case IDC_GLT_Default:
\r
8470 GLT_TagsToList( GLT_DEFAULT_TAGS );
\r
8473 case IDC_GLT_Restore:
\r
8474 GLT_TagsToList( appData.gameListTags );
\r
8478 GLT_MoveSelection( hDlg, -1 );
\r
8481 case IDC_GLT_Down:
\r
8482 GLT_MoveSelection( hDlg, +1 );
\r
8492 int GameListOptions()
\r
8495 FARPROC lpProc = MakeProcInstance( (FARPROC) GameListOptions_Proc, hInst );
\r
8497 safeStrCpy( lpUserGLT, appData.gameListTags ,LPUSERGLT_SIZE );
\r
8499 result = DialogBoxParam( hInst, MAKEINTRESOURCE(DLG_GameListOptions), hwndMain, (DLGPROC)lpProc, (LPARAM)lpUserGLT );
\r
8501 if( result == 0 ) {
\r
8502 /* [AS] Memory leak here! */
\r
8503 appData.gameListTags = strdup( lpUserGLT );
\r
8510 DisplayIcsInteractionTitle(char *str)
\r
8512 char consoleTitle[MSG_SIZ];
\r
8514 snprintf(consoleTitle, MSG_SIZ, "%s: %s", szConsoleTitle, str);
\r
8515 SetWindowText(hwndConsole, consoleTitle);
\r
8519 DrawPosition(int fullRedraw, Board board)
\r
8521 HDCDrawPosition(NULL, (BOOLEAN) fullRedraw, board);
\r
8524 void NotifyFrontendLogin()
\r
8527 UpdateICSWidth(GetDlgItem(hwndConsole, OPT_ConsoleText));
\r
8533 fromX = fromY = -1;
\r
8534 if (dragInfo.pos.x != -1 || dragInfo.pos.y != -1) {
\r
8535 dragInfo.pos.x = dragInfo.pos.y = -1;
\r
8536 dragInfo.pos.x = dragInfo.pos.y = -1;
\r
8537 dragInfo.lastpos = dragInfo.pos;
\r
8538 dragInfo.start.x = dragInfo.start.y = -1;
\r
8539 dragInfo.from = dragInfo.start;
\r
8541 DrawPosition(TRUE, NULL);
\r
8548 CommentPopUp(char *title, char *str)
\r
8550 HWND hwnd = GetActiveWindow();
\r
8551 EitherCommentPopUp(currentMove, title, str, FALSE); // [HGM] vari: fake move index, rather than 0
\r
8553 SetActiveWindow(hwnd);
\r
8557 CommentPopDown(void)
\r
8559 CheckMenuItem(GetMenu(hwndMain), IDM_Comment, MF_UNCHECKED);
\r
8560 if (commentDialog) {
\r
8561 ShowWindow(commentDialog, SW_HIDE);
\r
8563 commentUp = FALSE;
\r
8567 EditCommentPopUp(int index, char *title, char *str)
\r
8569 EitherCommentPopUp(index, title, str, TRUE);
\r
8576 MyPlaySound(&sounds[(int)SoundMove]);
\r
8579 VOID PlayIcsWinSound()
\r
8581 MyPlaySound(&sounds[(int)SoundIcsWin]);
\r
8584 VOID PlayIcsLossSound()
\r
8586 MyPlaySound(&sounds[(int)SoundIcsLoss]);
\r
8589 VOID PlayIcsDrawSound()
\r
8591 MyPlaySound(&sounds[(int)SoundIcsDraw]);
\r
8594 VOID PlayIcsUnfinishedSound()
\r
8596 MyPlaySound(&sounds[(int)SoundIcsUnfinished]);
\r
8602 MyPlaySound(&sounds[(int)SoundAlarm]);
\r
8610 consoleEcho = TRUE;
\r
8611 hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
8612 SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&consoleCF);
\r
8613 SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);
\r
8622 consoleEcho = FALSE;
\r
8623 hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
8624 /* This works OK: set text and background both to the same color */
\r
8626 cf.crTextColor = COLOR_ECHOOFF;
\r
8627 SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf);
\r
8628 SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, cf.crTextColor);
\r
8631 /* No Raw()...? */
\r
8633 void Colorize(ColorClass cc, int continuation)
\r
8635 currentColorClass = cc;
\r
8636 consoleCF.dwMask = CFM_COLOR|CFM_BOLD|CFM_ITALIC|CFM_UNDERLINE|CFM_STRIKEOUT;
\r
8637 consoleCF.crTextColor = textAttribs[cc].color;
\r
8638 consoleCF.dwEffects = textAttribs[cc].effects;
\r
8639 if (!continuation) MyPlaySound(&textAttribs[cc].sound);
\r
8645 static char buf[MSG_SIZ];
\r
8646 DWORD bufsiz = MSG_SIZ;
\r
8648 if(appData.userName != NULL && appData.userName[0] != 0) {
\r
8649 return appData.userName; /* [HGM] username: prefer name selected by user over his system login */
\r
8651 if (!GetUserName(buf, &bufsiz)) {
\r
8652 /*DisplayError("Error getting user name", GetLastError());*/
\r
8653 safeStrCpy(buf, _("User"), sizeof(buf)/sizeof(buf[0]) );
\r
8661 static char buf[MSG_SIZ];
\r
8662 DWORD bufsiz = MSG_SIZ;
\r
8664 if (!GetComputerName(buf, &bufsiz)) {
\r
8665 /*DisplayError("Error getting host name", GetLastError());*/
\r
8666 safeStrCpy(buf, _("Unknown"), sizeof(buf)/sizeof(buf[0]) );
\r
8673 ClockTimerRunning()
\r
8675 return clockTimerEvent != 0;
\r
8681 if (clockTimerEvent == 0) return FALSE;
\r
8682 KillTimer(hwndMain, clockTimerEvent);
\r
8683 clockTimerEvent = 0;
\r
8688 StartClockTimer(long millisec)
\r
8690 clockTimerEvent = SetTimer(hwndMain, (UINT) CLOCK_TIMER_ID,
\r
8691 (UINT) millisec, NULL);
\r
8695 DisplayWhiteClock(long timeRemaining, int highlight)
\r
8698 char *flag = whiteFlag && gameMode == TwoMachinesPlay ? "(!)" : "";
\r
8700 if(appData.noGUI) return;
\r
8701 hdc = GetDC(hwndMain);
\r
8702 if (!IsIconic(hwndMain)) {
\r
8703 DisplayAClock(hdc, timeRemaining, highlight,
\r
8704 flipClock ? &blackRect : &whiteRect, _("White"), flag);
\r
8706 if (highlight && iconCurrent == iconBlack) {
\r
8707 iconCurrent = iconWhite;
\r
8708 PostMessage(hwndMain, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);
\r
8709 if (IsIconic(hwndMain)) {
\r
8710 DrawIcon(hdc, 2, 2, iconCurrent);
\r
8713 (void) ReleaseDC(hwndMain, hdc);
\r
8715 PostMessage(hwndConsole, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);
\r
8719 DisplayBlackClock(long timeRemaining, int highlight)
\r
8722 char *flag = blackFlag && gameMode == TwoMachinesPlay ? "(!)" : "";
\r
8724 if(appData.noGUI) return;
\r
8725 hdc = GetDC(hwndMain);
\r
8726 if (!IsIconic(hwndMain)) {
\r
8727 DisplayAClock(hdc, timeRemaining, highlight,
\r
8728 flipClock ? &whiteRect : &blackRect, _("Black"), flag);
\r
8730 if (highlight && iconCurrent == iconWhite) {
\r
8731 iconCurrent = iconBlack;
\r
8732 PostMessage(hwndMain, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);
\r
8733 if (IsIconic(hwndMain)) {
\r
8734 DrawIcon(hdc, 2, 2, iconCurrent);
\r
8737 (void) ReleaseDC(hwndMain, hdc);
\r
8739 PostMessage(hwndConsole, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);
\r
8744 LoadGameTimerRunning()
\r
8746 return loadGameTimerEvent != 0;
\r
8750 StopLoadGameTimer()
\r
8752 if (loadGameTimerEvent == 0) return FALSE;
\r
8753 KillTimer(hwndMain, loadGameTimerEvent);
\r
8754 loadGameTimerEvent = 0;
\r
8759 StartLoadGameTimer(long millisec)
\r
8761 loadGameTimerEvent = SetTimer(hwndMain, (UINT) LOAD_GAME_TIMER_ID,
\r
8762 (UINT) millisec, NULL);
\r
8770 char fileTitle[MSG_SIZ];
\r
8772 defName = DefaultFileName(appData.oldSaveStyle ? "gam" : "pgn");
\r
8773 f = OpenFileDialog(hwndMain, "a", defName,
\r
8774 appData.oldSaveStyle ? "gam" : "pgn",
\r
8776 _("Save Game to File"), NULL, fileTitle, NULL);
\r
8778 SaveGame(f, 0, "");
\r
8785 ScheduleDelayedEvent(DelayedEventCallback cb, long millisec)
\r
8787 if (delayedTimerEvent != 0) {
\r
8788 if (appData.debugMode && cb != delayedTimerCallback) { // [HGM] alive: not too much debug
\r
8789 fprintf(debugFP, "ScheduleDelayedEvent: event already scheduled\n");
\r
8791 KillTimer(hwndMain, delayedTimerEvent);
\r
8792 delayedTimerEvent = 0;
\r
8793 if(delayedTimerCallback != cb) // [HGM] alive: do not "flush" same event, just postpone it
\r
8794 delayedTimerCallback();
\r
8796 delayedTimerCallback = cb;
\r
8797 delayedTimerEvent = SetTimer(hwndMain, (UINT) DELAYED_TIMER_ID,
\r
8798 (UINT) millisec, NULL);
\r
8801 DelayedEventCallback
\r
8804 if (delayedTimerEvent) {
\r
8805 return delayedTimerCallback;
\r
8812 CancelDelayedEvent()
\r
8814 if (delayedTimerEvent) {
\r
8815 KillTimer(hwndMain, delayedTimerEvent);
\r
8816 delayedTimerEvent = 0;
\r
8820 DWORD GetWin32Priority(int nice)
\r
8821 { // [HGM] nice: translate Unix nice() value to indows priority class. (Code stolen from Polyglot 1.4w11)
\r
8823 REALTIME_PRIORITY_CLASS 0x00000100
\r
8824 HIGH_PRIORITY_CLASS 0x00000080
\r
8825 ABOVE_NORMAL_PRIORITY_CLASS 0x00008000
\r
8826 NORMAL_PRIORITY_CLASS 0x00000020
\r
8827 BELOW_NORMAL_PRIORITY_CLASS 0x00004000
\r
8828 IDLE_PRIORITY_CLASS 0x00000040
\r
8830 if (nice < -15) return 0x00000080;
\r
8831 if (nice < 0) return 0x00008000;
\r
8832 if (nice == 0) return 0x00000020;
\r
8833 if (nice < 15) return 0x00004000;
\r
8834 return 0x00000040;
\r
8837 /* Start a child process running the given program.
\r
8838 The process's standard output can be read from "from", and its
\r
8839 standard input can be written to "to".
\r
8840 Exit with fatal error if anything goes wrong.
\r
8841 Returns an opaque pointer that can be used to destroy the process
\r
8845 StartChildProcess(char *cmdLine, char *dir, ProcRef *pr)
\r
8847 #define BUFSIZE 4096
\r
8849 HANDLE hChildStdinRd, hChildStdinWr,
\r
8850 hChildStdoutRd, hChildStdoutWr;
\r
8851 HANDLE hChildStdinWrDup, hChildStdoutRdDup;
\r
8852 SECURITY_ATTRIBUTES saAttr;
\r
8854 PROCESS_INFORMATION piProcInfo;
\r
8855 STARTUPINFO siStartInfo;
\r
8857 char buf[MSG_SIZ];
\r
8860 if (appData.debugMode) {
\r
8861 fprintf(debugFP, "StartChildProcess (dir=\"%s\") %s\n", dir, cmdLine);
\r
8866 /* Set the bInheritHandle flag so pipe handles are inherited. */
\r
8867 saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);
\r
8868 saAttr.bInheritHandle = TRUE;
\r
8869 saAttr.lpSecurityDescriptor = NULL;
\r
8872 * The steps for redirecting child's STDOUT:
\r
8873 * 1. Create anonymous pipe to be STDOUT for child.
\r
8874 * 2. Create a noninheritable duplicate of read handle,
\r
8875 * and close the inheritable read handle.
\r
8878 /* Create a pipe for the child's STDOUT. */
\r
8879 if (! CreatePipe(&hChildStdoutRd, &hChildStdoutWr, &saAttr, 0)) {
\r
8880 return GetLastError();
\r
8883 /* Duplicate the read handle to the pipe, so it is not inherited. */
\r
8884 fSuccess = DuplicateHandle(GetCurrentProcess(), hChildStdoutRd,
\r
8885 GetCurrentProcess(), &hChildStdoutRdDup, 0,
\r
8886 FALSE, /* not inherited */
\r
8887 DUPLICATE_SAME_ACCESS);
\r
8889 return GetLastError();
\r
8891 CloseHandle(hChildStdoutRd);
\r
8894 * The steps for redirecting child's STDIN:
\r
8895 * 1. Create anonymous pipe to be STDIN for child.
\r
8896 * 2. Create a noninheritable duplicate of write handle,
\r
8897 * and close the inheritable write handle.
\r
8900 /* Create a pipe for the child's STDIN. */
\r
8901 if (! CreatePipe(&hChildStdinRd, &hChildStdinWr, &saAttr, 0)) {
\r
8902 return GetLastError();
\r
8905 /* Duplicate the write handle to the pipe, so it is not inherited. */
\r
8906 fSuccess = DuplicateHandle(GetCurrentProcess(), hChildStdinWr,
\r
8907 GetCurrentProcess(), &hChildStdinWrDup, 0,
\r
8908 FALSE, /* not inherited */
\r
8909 DUPLICATE_SAME_ACCESS);
\r
8911 return GetLastError();
\r
8913 CloseHandle(hChildStdinWr);
\r
8915 /* Arrange to (1) look in dir for the child .exe file, and
\r
8916 * (2) have dir be the child's working directory. Interpret
\r
8917 * dir relative to the directory WinBoard loaded from. */
\r
8918 GetCurrentDirectory(MSG_SIZ, buf);
\r
8919 SetCurrentDirectory(installDir);
\r
8920 SetCurrentDirectory(dir);
\r
8922 /* Now create the child process. */
\r
8924 siStartInfo.cb = sizeof(STARTUPINFO);
\r
8925 siStartInfo.lpReserved = NULL;
\r
8926 siStartInfo.lpDesktop = NULL;
\r
8927 siStartInfo.lpTitle = NULL;
\r
8928 siStartInfo.dwFlags = STARTF_USESTDHANDLES;
\r
8929 siStartInfo.cbReserved2 = 0;
\r
8930 siStartInfo.lpReserved2 = NULL;
\r
8931 siStartInfo.hStdInput = hChildStdinRd;
\r
8932 siStartInfo.hStdOutput = hChildStdoutWr;
\r
8933 siStartInfo.hStdError = hChildStdoutWr;
\r
8935 fSuccess = CreateProcess(NULL,
\r
8936 cmdLine, /* command line */
\r
8937 NULL, /* process security attributes */
\r
8938 NULL, /* primary thread security attrs */
\r
8939 TRUE, /* handles are inherited */
\r
8940 DETACHED_PROCESS|CREATE_NEW_PROCESS_GROUP,
\r
8941 NULL, /* use parent's environment */
\r
8943 &siStartInfo, /* STARTUPINFO pointer */
\r
8944 &piProcInfo); /* receives PROCESS_INFORMATION */
\r
8946 err = GetLastError();
\r
8947 SetCurrentDirectory(buf); /* return to prev directory */
\r
8952 if (appData.niceEngines){ // [HGM] nice: adjust engine proc priority
\r
8953 if(appData.debugMode) fprintf(debugFP, "nice engine proc to %d\n", appData.niceEngines);
\r
8954 SetPriorityClass(piProcInfo.hProcess, GetWin32Priority(appData.niceEngines));
\r
8957 /* Close the handles we don't need in the parent */
\r
8958 CloseHandle(piProcInfo.hThread);
\r
8959 CloseHandle(hChildStdinRd);
\r
8960 CloseHandle(hChildStdoutWr);
\r
8962 /* Prepare return value */
\r
8963 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
\r
8964 cp->kind = CPReal;
\r
8965 cp->hProcess = piProcInfo.hProcess;
\r
8966 cp->pid = piProcInfo.dwProcessId;
\r
8967 cp->hFrom = hChildStdoutRdDup;
\r
8968 cp->hTo = hChildStdinWrDup;
\r
8970 *pr = (void *) cp;
\r
8972 /* Klaus Friedel says that this Sleep solves a problem under Windows
\r
8973 2000 where engines sometimes don't see the initial command(s)
\r
8974 from WinBoard and hang. I don't understand how that can happen,
\r
8975 but the Sleep is harmless, so I've put it in. Others have also
\r
8976 reported what may be the same problem, so hopefully this will fix
\r
8977 it for them too. */
\r
8985 DestroyChildProcess(ProcRef pr, int/*boolean*/ signal)
\r
8987 ChildProc *cp; int result;
\r
8989 cp = (ChildProc *) pr;
\r
8990 if (cp == NULL) return;
\r
8992 switch (cp->kind) {
\r
8994 /* TerminateProcess is considered harmful, so... */
\r
8995 CloseHandle(cp->hTo); /* Closing this will give the child an EOF and hopefully kill it */
\r
8996 if (cp->hFrom) CloseHandle(cp->hFrom); /* if NULL, InputThread will close it */
\r
8997 /* The following doesn't work because the chess program
\r
8998 doesn't "have the same console" as WinBoard. Maybe
\r
8999 we could arrange for this even though neither WinBoard
\r
9000 nor the chess program uses a console for stdio? */
\r
9001 /*!!if (signal) GenerateConsoleCtrlEvent(CTRL_BREAK_EVENT, cp->pid);*/
\r
9003 /* [AS] Special termination modes for misbehaving programs... */
\r
9004 if( signal == 9 ) {
\r
9005 result = TerminateProcess( cp->hProcess, 0 );
\r
9007 if ( appData.debugMode) {
\r
9008 fprintf( debugFP, "Terminating process %lu, result=%d\n", cp->pid, result );
\r
9011 else if( signal == 10 ) {
\r
9012 DWORD dw = WaitForSingleObject( cp->hProcess, 3*1000 ); // Wait 3 seconds at most
\r
9014 if( dw != WAIT_OBJECT_0 ) {
\r
9015 result = TerminateProcess( cp->hProcess, 0 );
\r
9017 if ( appData.debugMode) {
\r
9018 fprintf( debugFP, "Process %lu still alive after timeout, killing... result=%d\n", cp->pid, result );
\r
9024 CloseHandle(cp->hProcess);
\r
9028 if (cp->hFrom) CloseHandle(cp->hFrom);
\r
9032 closesocket(cp->sock);
\r
9037 if (signal) send(cp->sock2, "\017", 1, 0); /* 017 = 15 = SIGTERM */
\r
9038 closesocket(cp->sock);
\r
9039 closesocket(cp->sock2);
\r
9047 InterruptChildProcess(ProcRef pr)
\r
9051 cp = (ChildProc *) pr;
\r
9052 if (cp == NULL) return;
\r
9053 switch (cp->kind) {
\r
9055 /* The following doesn't work because the chess program
\r
9056 doesn't "have the same console" as WinBoard. Maybe
\r
9057 we could arrange for this even though neither WinBoard
\r
9058 nor the chess program uses a console for stdio */
\r
9059 /*!!GenerateConsoleCtrlEvent(CTRL_C_EVENT, cp->pid);*/
\r
9064 /* Can't interrupt */
\r
9068 send(cp->sock2, "\002", 1, 0); /* 2 = SIGINT */
\r
9075 OpenTelnet(char *host, char *port, ProcRef *pr)
\r
9077 char cmdLine[MSG_SIZ];
\r
9079 if (port[0] == NULLCHAR) {
\r
9080 snprintf(cmdLine, MSG_SIZ, "%s %s", appData.telnetProgram, host);
\r
9082 snprintf(cmdLine, MSG_SIZ, "%s %s %s", appData.telnetProgram, host, port);
\r
9084 return StartChildProcess(cmdLine, "", pr);
\r
9088 /* Code to open TCP sockets */
\r
9091 OpenTCP(char *host, char *port, ProcRef *pr)
\r
9096 struct sockaddr_in sa, mysa;
\r
9097 struct hostent FAR *hp;
\r
9098 unsigned short uport;
\r
9099 WORD wVersionRequested;
\r
9102 /* Initialize socket DLL */
\r
9103 wVersionRequested = MAKEWORD(1, 1);
\r
9104 err = WSAStartup(wVersionRequested, &wsaData);
\r
9105 if (err != 0) return err;
\r
9108 if ((s = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {
\r
9109 err = WSAGetLastError();
\r
9114 /* Bind local address using (mostly) don't-care values.
\r
9116 memset((char *) &mysa, 0, sizeof(struct sockaddr_in));
\r
9117 mysa.sin_family = AF_INET;
\r
9118 mysa.sin_addr.s_addr = INADDR_ANY;
\r
9119 uport = (unsigned short) 0;
\r
9120 mysa.sin_port = htons(uport);
\r
9121 if (bind(s, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))
\r
9122 == SOCKET_ERROR) {
\r
9123 err = WSAGetLastError();
\r
9128 /* Resolve remote host name */
\r
9129 memset((char *) &sa, 0, sizeof(struct sockaddr_in));
\r
9130 if (!(hp = gethostbyname(host))) {
\r
9131 unsigned int b0, b1, b2, b3;
\r
9133 err = WSAGetLastError();
\r
9135 if (sscanf(host, "%u.%u.%u.%u", &b0, &b1, &b2, &b3) == 4) {
\r
9136 hp = (struct hostent *) calloc(1, sizeof(struct hostent));
\r
9137 hp->h_addrtype = AF_INET;
\r
9139 hp->h_addr_list = (char **) calloc(2, sizeof(char *));
\r
9140 hp->h_addr_list[0] = (char *) malloc(4);
\r
9141 hp->h_addr_list[0][0] = (char) b0;
\r
9142 hp->h_addr_list[0][1] = (char) b1;
\r
9143 hp->h_addr_list[0][2] = (char) b2;
\r
9144 hp->h_addr_list[0][3] = (char) b3;
\r
9150 sa.sin_family = hp->h_addrtype;
\r
9151 uport = (unsigned short) atoi(port);
\r
9152 sa.sin_port = htons(uport);
\r
9153 memcpy((char *) &sa.sin_addr, hp->h_addr, hp->h_length);
\r
9155 /* Make connection */
\r
9156 if (connect(s, (struct sockaddr *) &sa,
\r
9157 sizeof(struct sockaddr_in)) == SOCKET_ERROR) {
\r
9158 err = WSAGetLastError();
\r
9163 /* Prepare return value */
\r
9164 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
\r
9165 cp->kind = CPSock;
\r
9167 *pr = (ProcRef *) cp;
\r
9173 OpenCommPort(char *name, ProcRef *pr)
\r
9178 char fullname[MSG_SIZ];
\r
9180 if (*name != '\\')
\r
9181 snprintf(fullname, MSG_SIZ, "\\\\.\\%s", name);
\r
9183 safeStrCpy(fullname, name, sizeof(fullname)/sizeof(fullname[0]) );
\r
9185 h = CreateFile(name, GENERIC_READ | GENERIC_WRITE,
\r
9186 0, NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL);
\r
9187 if (h == (HANDLE) -1) {
\r
9188 return GetLastError();
\r
9192 if (!SetCommState(h, (LPDCB) &dcb)) return GetLastError();
\r
9194 /* Accumulate characters until a 100ms pause, then parse */
\r
9195 ct.ReadIntervalTimeout = 100;
\r
9196 ct.ReadTotalTimeoutMultiplier = 0;
\r
9197 ct.ReadTotalTimeoutConstant = 0;
\r
9198 ct.WriteTotalTimeoutMultiplier = 0;
\r
9199 ct.WriteTotalTimeoutConstant = 0;
\r
9200 if (!SetCommTimeouts(h, (LPCOMMTIMEOUTS) &ct)) return GetLastError();
\r
9202 /* Prepare return value */
\r
9203 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
\r
9204 cp->kind = CPComm;
\r
9207 *pr = (ProcRef *) cp;
\r
9213 OpenLoopback(ProcRef *pr)
\r
9215 DisplayFatalError(_("Not implemented"), 0, 1);
\r
9221 OpenRcmd(char* host, char* user, char* cmd, ProcRef* pr)
\r
9226 struct sockaddr_in sa, mysa;
\r
9227 struct hostent FAR *hp;
\r
9228 unsigned short uport;
\r
9229 WORD wVersionRequested;
\r
9232 char stderrPortStr[MSG_SIZ];
\r
9234 /* Initialize socket DLL */
\r
9235 wVersionRequested = MAKEWORD(1, 1);
\r
9236 err = WSAStartup(wVersionRequested, &wsaData);
\r
9237 if (err != 0) return err;
\r
9239 /* Resolve remote host name */
\r
9240 memset((char *) &sa, 0, sizeof(struct sockaddr_in));
\r
9241 if (!(hp = gethostbyname(host))) {
\r
9242 unsigned int b0, b1, b2, b3;
\r
9244 err = WSAGetLastError();
\r
9246 if (sscanf(host, "%u.%u.%u.%u", &b0, &b1, &b2, &b3) == 4) {
\r
9247 hp = (struct hostent *) calloc(1, sizeof(struct hostent));
\r
9248 hp->h_addrtype = AF_INET;
\r
9250 hp->h_addr_list = (char **) calloc(2, sizeof(char *));
\r
9251 hp->h_addr_list[0] = (char *) malloc(4);
\r
9252 hp->h_addr_list[0][0] = (char) b0;
\r
9253 hp->h_addr_list[0][1] = (char) b1;
\r
9254 hp->h_addr_list[0][2] = (char) b2;
\r
9255 hp->h_addr_list[0][3] = (char) b3;
\r
9261 sa.sin_family = hp->h_addrtype;
\r
9262 uport = (unsigned short) 514;
\r
9263 sa.sin_port = htons(uport);
\r
9264 memcpy((char *) &sa.sin_addr, hp->h_addr, hp->h_length);
\r
9266 /* Bind local socket to unused "privileged" port address
\r
9268 s = INVALID_SOCKET;
\r
9269 memset((char *) &mysa, 0, sizeof(struct sockaddr_in));
\r
9270 mysa.sin_family = AF_INET;
\r
9271 mysa.sin_addr.s_addr = INADDR_ANY;
\r
9272 for (fromPort = 1023;; fromPort--) {
\r
9273 if (fromPort < 0) {
\r
9275 return WSAEADDRINUSE;
\r
9277 if (s == INVALID_SOCKET) {
\r
9278 if ((s = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {
\r
9279 err = WSAGetLastError();
\r
9284 uport = (unsigned short) fromPort;
\r
9285 mysa.sin_port = htons(uport);
\r
9286 if (bind(s, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))
\r
9287 == SOCKET_ERROR) {
\r
9288 err = WSAGetLastError();
\r
9289 if (err == WSAEADDRINUSE) continue;
\r
9293 if (connect(s, (struct sockaddr *) &sa,
\r
9294 sizeof(struct sockaddr_in)) == SOCKET_ERROR) {
\r
9295 err = WSAGetLastError();
\r
9296 if (err == WSAEADDRINUSE) {
\r
9307 /* Bind stderr local socket to unused "privileged" port address
\r
9309 s2 = INVALID_SOCKET;
\r
9310 memset((char *) &mysa, 0, sizeof(struct sockaddr_in));
\r
9311 mysa.sin_family = AF_INET;
\r
9312 mysa.sin_addr.s_addr = INADDR_ANY;
\r
9313 for (fromPort = 1023;; fromPort--) {
\r
9314 if (fromPort == prevStderrPort) continue; // don't reuse port
\r
9315 if (fromPort < 0) {
\r
9316 (void) closesocket(s);
\r
9318 return WSAEADDRINUSE;
\r
9320 if (s2 == INVALID_SOCKET) {
\r
9321 if ((s2 = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {
\r
9322 err = WSAGetLastError();
\r
9328 uport = (unsigned short) fromPort;
\r
9329 mysa.sin_port = htons(uport);
\r
9330 if (bind(s2, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))
\r
9331 == SOCKET_ERROR) {
\r
9332 err = WSAGetLastError();
\r
9333 if (err == WSAEADDRINUSE) continue;
\r
9334 (void) closesocket(s);
\r
9338 if (listen(s2, 1) == SOCKET_ERROR) {
\r
9339 err = WSAGetLastError();
\r
9340 if (err == WSAEADDRINUSE) {
\r
9342 s2 = INVALID_SOCKET;
\r
9345 (void) closesocket(s);
\r
9346 (void) closesocket(s2);
\r
9352 prevStderrPort = fromPort; // remember port used
\r
9353 snprintf(stderrPortStr, MSG_SIZ, "%d", fromPort);
\r
9355 if (send(s, stderrPortStr, strlen(stderrPortStr) + 1, 0) == SOCKET_ERROR) {
\r
9356 err = WSAGetLastError();
\r
9357 (void) closesocket(s);
\r
9358 (void) closesocket(s2);
\r
9363 if (send(s, UserName(), strlen(UserName()) + 1, 0) == SOCKET_ERROR) {
\r
9364 err = WSAGetLastError();
\r
9365 (void) closesocket(s);
\r
9366 (void) closesocket(s2);
\r
9370 if (*user == NULLCHAR) user = UserName();
\r
9371 if (send(s, user, strlen(user) + 1, 0) == SOCKET_ERROR) {
\r
9372 err = WSAGetLastError();
\r
9373 (void) closesocket(s);
\r
9374 (void) closesocket(s2);
\r
9378 if (send(s, cmd, strlen(cmd) + 1, 0) == SOCKET_ERROR) {
\r
9379 err = WSAGetLastError();
\r
9380 (void) closesocket(s);
\r
9381 (void) closesocket(s2);
\r
9386 if ((s3 = accept(s2, NULL, NULL)) == INVALID_SOCKET) {
\r
9387 err = WSAGetLastError();
\r
9388 (void) closesocket(s);
\r
9389 (void) closesocket(s2);
\r
9393 (void) closesocket(s2); /* Stop listening */
\r
9395 /* Prepare return value */
\r
9396 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
\r
9397 cp->kind = CPRcmd;
\r
9400 *pr = (ProcRef *) cp;
\r
9407 AddInputSource(ProcRef pr, int lineByLine,
\r
9408 InputCallback func, VOIDSTAR closure)
\r
9410 InputSource *is, *is2 = NULL;
\r
9411 ChildProc *cp = (ChildProc *) pr;
\r
9413 is = (InputSource *) calloc(1, sizeof(InputSource));
\r
9414 is->lineByLine = lineByLine;
\r
9416 is->closure = closure;
\r
9417 is->second = NULL;
\r
9418 is->next = is->buf;
\r
9419 if (pr == NoProc) {
\r
9420 is->kind = CPReal;
\r
9421 consoleInputSource = is;
\r
9423 is->kind = cp->kind;
\r
9425 [AS] Try to avoid a race condition if the thread is given control too early:
\r
9426 we create all threads suspended so that the is->hThread variable can be
\r
9427 safely assigned, then let the threads start with ResumeThread.
\r
9429 switch (cp->kind) {
\r
9431 is->hFile = cp->hFrom;
\r
9432 cp->hFrom = NULL; /* now owned by InputThread */
\r
9434 CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) NonOvlInputThread,
\r
9435 (LPVOID) is, CREATE_SUSPENDED, &is->id);
\r
9439 is->hFile = cp->hFrom;
\r
9440 cp->hFrom = NULL; /* now owned by InputThread */
\r
9442 CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) InputThread,
\r
9443 (LPVOID) is, CREATE_SUSPENDED, &is->id);
\r
9447 is->sock = cp->sock;
\r
9449 CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,
\r
9450 (LPVOID) is, CREATE_SUSPENDED, &is->id);
\r
9454 is2 = (InputSource *) calloc(1, sizeof(InputSource));
\r
9456 is->sock = cp->sock;
\r
9458 is2->sock = cp->sock2;
\r
9459 is2->second = is2;
\r
9461 CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,
\r
9462 (LPVOID) is, CREATE_SUSPENDED, &is->id);
\r
9464 CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,
\r
9465 (LPVOID) is2, CREATE_SUSPENDED, &is2->id);
\r
9469 if( is->hThread != NULL ) {
\r
9470 ResumeThread( is->hThread );
\r
9473 if( is2 != NULL && is2->hThread != NULL ) {
\r
9474 ResumeThread( is2->hThread );
\r
9478 return (InputSourceRef) is;
\r
9482 RemoveInputSource(InputSourceRef isr)
\r
9486 is = (InputSource *) isr;
\r
9487 is->hThread = NULL; /* tell thread to stop */
\r
9488 CloseHandle(is->hThread);
\r
9489 if (is->second != NULL) {
\r
9490 is->second->hThread = NULL;
\r
9491 CloseHandle(is->second->hThread);
\r
9495 int no_wrap(char *message, int count)
\r
9497 ConsoleOutput(message, count, FALSE);
\r
9502 OutputToProcess(ProcRef pr, char *message, int count, int *outError)
\r
9505 int outCount = SOCKET_ERROR;
\r
9506 ChildProc *cp = (ChildProc *) pr;
\r
9507 static OVERLAPPED ovl;
\r
9508 static int line = 0;
\r
9512 if (appData.noJoin || !appData.useInternalWrap)
\r
9513 return no_wrap(message, count);
\r
9516 int width = get_term_width();
\r
9517 int len = wrap(NULL, message, count, width, &line);
\r
9518 char *msg = malloc(len);
\r
9522 return no_wrap(message, count);
\r
9525 dbgchk = wrap(msg, message, count, width, &line);
\r
9526 if (dbgchk != len && appData.debugMode)
\r
9527 fprintf(debugFP, "wrap(): dbgchk(%d) != len(%d)\n", dbgchk, len);
\r
9528 ConsoleOutput(msg, len, FALSE);
\r
9535 if (ovl.hEvent == NULL) {
\r
9536 ovl.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
\r
9538 ovl.Internal = ovl.InternalHigh = ovl.Offset = ovl.OffsetHigh = 0;
\r
9540 switch (cp->kind) {
\r
9543 outCount = send(cp->sock, message, count, 0);
\r
9544 if (outCount == SOCKET_ERROR) {
\r
9545 *outError = WSAGetLastError();
\r
9547 *outError = NO_ERROR;
\r
9552 if (WriteFile(((ChildProc *)pr)->hTo, message, count,
\r
9553 &dOutCount, NULL)) {
\r
9554 *outError = NO_ERROR;
\r
9555 outCount = (int) dOutCount;
\r
9557 *outError = GetLastError();
\r
9562 *outError = DoWriteFile(((ChildProc *)pr)->hTo, message, count,
\r
9563 &dOutCount, &ovl);
\r
9564 if (*outError == NO_ERROR) {
\r
9565 outCount = (int) dOutCount;
\r
9573 OutputToProcessDelayed(ProcRef pr, char *message, int count, int *outError,
\r
9576 /* Ignore delay, not implemented for WinBoard */
\r
9577 return OutputToProcess(pr, message, count, outError);
\r
9582 CmailSigHandlerCallBack(InputSourceRef isr, VOIDSTAR closure,
\r
9583 char *buf, int count, int error)
\r
9585 DisplayFatalError(_("Not implemented"), 0, 1);
\r
9588 /* see wgamelist.c for Game List functions */
\r
9589 /* see wedittags.c for Edit Tags functions */
\r
9596 char buf[MSG_SIZ];
\r
9599 if (SearchPath(installDir, appData.icsLogon, NULL, MSG_SIZ, buf, &dummy)) {
\r
9600 f = fopen(buf, "r");
\r
9602 ProcessICSInitScript(f);
\r
9610 StartAnalysisClock()
\r
9612 if (analysisTimerEvent) return;
\r
9613 analysisTimerEvent = SetTimer(hwndMain, (UINT) ANALYSIS_TIMER_ID,
\r
9614 (UINT) 2000, NULL);
\r
9618 SetHighlights(int fromX, int fromY, int toX, int toY)
\r
9620 highlightInfo.sq[0].x = fromX;
\r
9621 highlightInfo.sq[0].y = fromY;
\r
9622 highlightInfo.sq[1].x = toX;
\r
9623 highlightInfo.sq[1].y = toY;
\r
9629 highlightInfo.sq[0].x = highlightInfo.sq[0].y =
\r
9630 highlightInfo.sq[1].x = highlightInfo.sq[1].y = -1;
\r
9634 SetPremoveHighlights(int fromX, int fromY, int toX, int toY)
\r
9636 premoveHighlightInfo.sq[0].x = fromX;
\r
9637 premoveHighlightInfo.sq[0].y = fromY;
\r
9638 premoveHighlightInfo.sq[1].x = toX;
\r
9639 premoveHighlightInfo.sq[1].y = toY;
\r
9643 ClearPremoveHighlights()
\r
9645 premoveHighlightInfo.sq[0].x = premoveHighlightInfo.sq[0].y =
\r
9646 premoveHighlightInfo.sq[1].x = premoveHighlightInfo.sq[1].y = -1;
\r
9650 ShutDownFrontEnd()
\r
9652 if (saveSettingsOnExit) SaveSettings(settingsFileName);
\r
9653 DeleteClipboardTempFiles();
\r
9659 if (IsIconic(hwndMain))
\r
9660 ShowWindow(hwndMain, SW_RESTORE);
\r
9662 SetActiveWindow(hwndMain);
\r
9666 * Prototypes for animation support routines
\r
9668 static void ScreenSquare(int column, int row, POINT * pt);
\r
9669 static void Tween( POINT * start, POINT * mid, POINT * finish, int factor,
\r
9670 POINT frames[], int * nFrames);
\r
9676 AnimateAtomicCapture(Board board, int fromX, int fromY, int toX, int toY)
\r
9677 { // [HGM] atomic: animate blast wave
\r
9680 explodeInfo.fromX = fromX;
\r
9681 explodeInfo.fromY = fromY;
\r
9682 explodeInfo.toX = toX;
\r
9683 explodeInfo.toY = toY;
\r
9684 for(i=1; i<4*kFactor; i++) {
\r
9685 explodeInfo.radius = (i*180)/(4*kFactor-1);
\r
9686 DrawPosition(FALSE, board);
\r
9687 Sleep(appData.animSpeed);
\r
9689 explodeInfo.radius = 0;
\r
9690 DrawPosition(TRUE, board);
\r
9694 AnimateMove(board, fromX, fromY, toX, toY)
\r
9701 ChessSquare piece;
\r
9702 POINT start, finish, mid;
\r
9703 POINT frames[kFactor * 2 + 1];
\r
9706 if (!appData.animate) return;
\r
9707 if (doingSizing) return;
\r
9708 if (fromY < 0 || fromX < 0) return;
\r
9709 piece = board[fromY][fromX];
\r
9710 if (piece >= EmptySquare) return;
\r
9712 ScreenSquare(fromX, fromY, &start);
\r
9713 ScreenSquare(toX, toY, &finish);
\r
9715 /* All moves except knight jumps move in straight line */
\r
9716 if (!(abs(fromX-toX) == 1 && abs(fromY-toY) == 2 || abs(fromX-toX) == 2 && abs(fromY-toY) == 1)) {
\r
9717 mid.x = start.x + (finish.x - start.x) / 2;
\r
9718 mid.y = start.y + (finish.y - start.y) / 2;
\r
9720 /* Knight: make straight movement then diagonal */
\r
9721 if (abs(toY - fromY) < abs(toX - fromX)) {
\r
9722 mid.x = start.x + (finish.x - start.x) / 2;
\r
9726 mid.y = start.y + (finish.y - start.y) / 2;
\r
9730 /* Don't use as many frames for very short moves */
\r
9731 if (abs(toY - fromY) + abs(toX - fromX) <= 2)
\r
9732 Tween(&start, &mid, &finish, kFactor - 1, frames, &nFrames);
\r
9734 Tween(&start, &mid, &finish, kFactor, frames, &nFrames);
\r
9736 animInfo.from.x = fromX;
\r
9737 animInfo.from.y = fromY;
\r
9738 animInfo.to.x = toX;
\r
9739 animInfo.to.y = toY;
\r
9740 animInfo.lastpos = start;
\r
9741 animInfo.piece = piece;
\r
9742 for (n = 0; n < nFrames; n++) {
\r
9743 animInfo.pos = frames[n];
\r
9744 DrawPosition(FALSE, NULL);
\r
9745 animInfo.lastpos = animInfo.pos;
\r
9746 Sleep(appData.animSpeed);
\r
9748 animInfo.pos = finish;
\r
9749 DrawPosition(FALSE, NULL);
\r
9750 animInfo.piece = EmptySquare;
\r
9751 Explode(board, fromX, fromY, toX, toY);
\r
9754 /* Convert board position to corner of screen rect and color */
\r
9757 ScreenSquare(column, row, pt)
\r
9758 int column; int row; POINT * pt;
\r
9761 pt->x = lineGap + ((BOARD_WIDTH-1)-column) * (squareSize + lineGap);
\r
9762 pt->y = lineGap + row * (squareSize + lineGap);
\r
9764 pt->x = lineGap + column * (squareSize + lineGap);
\r
9765 pt->y = lineGap + ((BOARD_HEIGHT-1)-row) * (squareSize + lineGap);
\r
9769 /* Generate a series of frame coords from start->mid->finish.
\r
9770 The movement rate doubles until the half way point is
\r
9771 reached, then halves back down to the final destination,
\r
9772 which gives a nice slow in/out effect. The algorithmn
\r
9773 may seem to generate too many intermediates for short
\r
9774 moves, but remember that the purpose is to attract the
\r
9775 viewers attention to the piece about to be moved and
\r
9776 then to where it ends up. Too few frames would be less
\r
9780 Tween(start, mid, finish, factor, frames, nFrames)
\r
9781 POINT * start; POINT * mid;
\r
9782 POINT * finish; int factor;
\r
9783 POINT frames[]; int * nFrames;
\r
9785 int n, fraction = 1, count = 0;
\r
9787 /* Slow in, stepping 1/16th, then 1/8th, ... */
\r
9788 for (n = 0; n < factor; n++)
\r
9790 for (n = 0; n < factor; n++) {
\r
9791 frames[count].x = start->x + (mid->x - start->x) / fraction;
\r
9792 frames[count].y = start->y + (mid->y - start->y) / fraction;
\r
9794 fraction = fraction / 2;
\r
9798 frames[count] = *mid;
\r
9801 /* Slow out, stepping 1/2, then 1/4, ... */
\r
9803 for (n = 0; n < factor; n++) {
\r
9804 frames[count].x = finish->x - (finish->x - mid->x) / fraction;
\r
9805 frames[count].y = finish->y - (finish->y - mid->y) / fraction;
\r
9807 fraction = fraction * 2;
\r
9813 HistorySet( char movelist[][2*MOVE_LEN], int first, int last, int current )
\r
9815 MoveHistorySet( movelist, first, last, current, pvInfoList );
\r
9817 EvalGraphSet( first, last, current, pvInfoList );
\r
9821 SettingsPopUp(ChessProgramState *cps)
\r
9822 { // [HGM] wrapper needed because handles must not be passed through back-end
\r
9823 EngineOptionsPopup(savedHwnd, cps);
\r