2 * WinBoard.c -- Windows NT front end to XBoard
\r
4 * Copyright 1991 by Digital Equipment Corporation, Maynard,
\r
7 * Enhancements Copyright 1992-2001, 2002, 2003, 2004, 2005, 2006,
\r
8 * 2007, 2008, 2009, 2010, 2011 Free Software Foundation, Inc.
\r
10 * Enhancements Copyright 2005 Alessandro Scotti
\r
12 * XBoard borrows its colors and the bitmaps.xchess bitmap set from XChess,
\r
13 * which was written and is copyrighted by Wayne Christopher.
\r
15 * The following terms apply to Digital Equipment Corporation's copyright
\r
16 * interest in XBoard:
\r
17 * ------------------------------------------------------------------------
\r
18 * All Rights Reserved
\r
20 * Permission to use, copy, modify, and distribute this software and its
\r
21 * documentation for any purpose and without fee is hereby granted,
\r
22 * provided that the above copyright notice appear in all copies and that
\r
23 * both that copyright notice and this permission notice appear in
\r
24 * supporting documentation, and that the name of Digital not be
\r
25 * used in advertising or publicity pertaining to distribution of the
\r
26 * software without specific, written prior permission.
\r
28 * DIGITAL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
\r
29 * ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL
\r
30 * DIGITAL BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
\r
31 * ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
\r
32 * WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
\r
33 * ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
\r
35 * ------------------------------------------------------------------------
\r
37 * The following terms apply to the enhanced version of XBoard
\r
38 * distributed by the Free Software Foundation:
\r
39 * ------------------------------------------------------------------------
\r
41 * GNU XBoard is free software: you can redistribute it and/or modify
\r
42 * it under the terms of the GNU General Public License as published by
\r
43 * the Free Software Foundation, either version 3 of the License, or (at
\r
44 * your option) any later version.
\r
46 * GNU XBoard is distributed in the hope that it will be useful, but
\r
47 * WITHOUT ANY WARRANTY; without even the implied warranty of
\r
48 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
\r
49 * General Public License for more details.
\r
51 * You should have received a copy of the GNU General Public License
\r
52 * along with this program. If not, see http://www.gnu.org/licenses/. *
\r
54 *------------------------------------------------------------------------
\r
55 ** See the file ChangeLog for a revision history. */
\r
59 #include <windows.h>
\r
60 #include <winuser.h>
\r
61 #include <winsock.h>
\r
62 #include <commctrl.h>
\r
68 #include <sys/stat.h>
\r
71 #include <commdlg.h>
\r
73 #include <richedit.h>
\r
74 #include <mmsystem.h>
\r
84 #include "frontend.h"
\r
85 #include "backend.h"
\r
86 #include "winboard.h"
\r
88 #include "wclipbrd.h"
\r
89 #include "woptions.h"
\r
90 #include "wsockerr.h"
\r
91 #include "defaults.h"
\r
95 //void InitEngineUCI( const char * iniDir, ChessProgramState * cps );
\r
98 void mysrandom(unsigned int seed);
\r
100 extern int whiteFlag, blackFlag;
\r
101 Boolean flipClock = FALSE;
\r
102 extern HANDLE chatHandle[];
\r
103 extern int ics_type;
\r
105 void DisplayHoldingsCount(HDC hdc, int x, int y, int align, int copyNumber);
\r
106 VOID NewVariantPopup(HWND hwnd);
\r
107 int FinishMove P((ChessMove moveType, int fromX, int fromY, int toX, int toY,
\r
108 /*char*/int promoChar));
\r
109 void DisplayMove P((int moveNumber));
\r
110 Boolean ParseFEN P((Board board, int *blackPlaysFirst, char *fen));
\r
111 void ChatPopUp P((char *s));
\r
113 ChessSquare piece;
\r
114 POINT pos; /* window coordinates of current pos */
\r
115 POINT lastpos; /* window coordinates of last pos - used for clipping */
\r
116 POINT from; /* board coordinates of the piece's orig pos */
\r
117 POINT to; /* board coordinates of the piece's new pos */
\r
120 static AnimInfo animInfo = { EmptySquare, {-1,-1}, {-1,-1}, {-1,-1} };
\r
123 POINT start; /* window coordinates of start pos */
\r
124 POINT pos; /* window coordinates of current pos */
\r
125 POINT lastpos; /* window coordinates of last pos - used for clipping */
\r
126 POINT from; /* board coordinates of the piece's orig pos */
\r
130 static DragInfo dragInfo = { {-1,-1}, {-1,-1}, {-1,-1}, {-1,-1}, EmptySquare };
\r
133 POINT sq[2]; /* board coordinates of from, to squares */
\r
136 static HighlightInfo highlightInfo = { {{-1, -1}, {-1, -1}} };
\r
137 static HighlightInfo premoveHighlightInfo = { {{-1, -1}, {-1, -1}} };
\r
138 static HighlightInfo partnerHighlightInfo = { {{-1, -1}, {-1, -1}} };
\r
139 static HighlightInfo oldPartnerHighlight = { {{-1, -1}, {-1, -1}} };
\r
141 typedef struct { // [HGM] atomic
\r
142 int fromX, fromY, toX, toY, radius;
\r
145 static ExplodeInfo explodeInfo;
\r
147 /* Window class names */
\r
148 char szAppName[] = "WinBoard";
\r
149 char szConsoleName[] = "WBConsole";
\r
151 /* Title bar text */
\r
152 char szTitle[] = "WinBoard";
\r
153 char szConsoleTitle[] = "I C S Interaction";
\r
156 char *settingsFileName;
\r
157 Boolean saveSettingsOnExit;
\r
158 char installDir[MSG_SIZ];
\r
159 int errorExitStatus;
\r
161 BoardSize boardSize;
\r
162 Boolean chessProgram;
\r
163 //static int boardX, boardY;
\r
164 int minX, minY; // [HGM] placement: volatile limits on upper-left corner
\r
165 int squareSize, lineGap, minorSize;
\r
166 static int winW, winH;
\r
167 static RECT messageRect, whiteRect, blackRect, leftLogoRect, rightLogoRect; // [HGM] logo
\r
168 static int logoHeight = 0;
\r
169 static char messageText[MESSAGE_TEXT_MAX];
\r
170 static int clockTimerEvent = 0;
\r
171 static int loadGameTimerEvent = 0;
\r
172 static int analysisTimerEvent = 0;
\r
173 static DelayedEventCallback delayedTimerCallback;
\r
174 static int delayedTimerEvent = 0;
\r
175 static int buttonCount = 2;
\r
176 char *icsTextMenuString;
\r
178 char *firstChessProgramNames;
\r
179 char *secondChessProgramNames;
\r
181 #define PALETTESIZE 256
\r
183 HINSTANCE hInst; /* current instance */
\r
184 Boolean alwaysOnTop = FALSE;
\r
186 COLORREF lightSquareColor, darkSquareColor, whitePieceColor,
\r
187 blackPieceColor, highlightSquareColor, premoveHighlightColor;
\r
189 ColorClass currentColorClass;
\r
191 static HWND savedHwnd;
\r
192 HWND hCommPort = NULL; /* currently open comm port */
\r
193 static HWND hwndPause; /* pause button */
\r
194 static HBITMAP pieceBitmap[3][(int) BlackPawn]; /* [HGM] nr of bitmaps referred to bP in stead of wK */
\r
195 static HBRUSH lightSquareBrush, darkSquareBrush,
\r
196 blackSquareBrush, /* [HGM] for band between board and holdings */
\r
197 explodeBrush, /* [HGM] atomic */
\r
198 markerBrush, /* [HGM] markers */
\r
199 whitePieceBrush, blackPieceBrush, iconBkgndBrush /*, outlineBrush*/;
\r
200 static POINT gridEndpoints[(BOARD_RANKS + BOARD_FILES + 2) * 2];
\r
201 static DWORD gridVertexCounts[BOARD_RANKS + BOARD_FILES + 2];
\r
202 static HPEN gridPen = NULL;
\r
203 static HPEN highlightPen = NULL;
\r
204 static HPEN premovePen = NULL;
\r
205 static NPLOGPALETTE pLogPal;
\r
206 static BOOL paletteChanged = FALSE;
\r
207 static HICON iconWhite, iconBlack, iconCurrent;
\r
208 static int doingSizing = FALSE;
\r
209 static int lastSizing = 0;
\r
210 static int prevStderrPort;
\r
211 static HBITMAP userLogo;
\r
213 static HBITMAP liteBackTexture = NULL;
\r
214 static HBITMAP darkBackTexture = NULL;
\r
215 static int liteBackTextureMode = BACK_TEXTURE_MODE_PLAIN;
\r
216 static int darkBackTextureMode = BACK_TEXTURE_MODE_PLAIN;
\r
217 static int backTextureSquareSize = 0;
\r
218 static struct { int x; int y; int mode; } backTextureSquareInfo[BOARD_RANKS][BOARD_FILES];
\r
220 #if __GNUC__ && !defined(_winmajor)
\r
221 #define oldDialog 0 /* cygwin doesn't define _winmajor; mingw does */
\r
223 #if defined(_winmajor)
\r
224 #define oldDialog (_winmajor < 4)
\r
226 #define oldDialog 0
\r
230 #define INTERNATIONAL
\r
232 #ifdef INTERNATIONAL
\r
233 # define _(s) T_(s)
\r
239 # define Translate(x, y)
\r
240 # define LoadLanguageFile(s)
\r
243 #ifdef INTERNATIONAL
\r
245 Boolean barbaric; // flag indicating if translation is needed
\r
247 // list of item numbers used in each dialog (used to alter language at run time)
\r
249 #define ABOUTBOX -1 /* not sure why these are needed */
\r
250 #define ABOUTBOX2 -1
\r
252 int dialogItems[][40] = {
\r
253 { ABOUTBOX, IDOK, OPT_MESS, 400 },
\r
254 { DLG_TimeControl, IDC_Babble, OPT_TCUseMoves, OPT_TCUseInc, OPT_TCUseFixed,
\r
255 OPT_TCtext1, OPT_TCtext2, OPT_TCitext1, OPT_TCitext2, OPT_TCftext, GPB_Factors, IDC_Factor1, IDC_Factor2, IDOK, IDCANCEL },
\r
256 { DLG_LoadOptions, OPT_Autostep, OPT_AStext1, IDOK, IDCANCEL },
\r
257 { DLG_SaveOptions, OPT_Autosave, OPT_AVPrompt, OPT_AVToFile, OPT_AVBrowse,
\r
258 801, OPT_PGN, OPT_Old, OPT_OutOfBookInfo, IDOK, IDCANCEL },
\r
259 { 1536, 1090, IDC_Directories, 1089, 1091, IDOK, IDCANCEL, 1038, IDC_IndexNr, 1037 },
\r
260 { DLG_CommPort, IDOK, IDCANCEL, IDC_Port, IDC_Rate, IDC_Bits, IDC_Parity,
\r
261 IDC_Stop, IDC_Flow, OPT_SerialHelp },
\r
262 { DLG_EditComment, IDOK, OPT_CancelComment, OPT_ClearComment, OPT_EditComment },
\r
263 { DLG_PromotionKing, PB_Chancellor, PB_Archbishop, PB_Queen, PB_Rook,
\r
264 PB_Bishop, PB_Knight, PB_King, IDCANCEL, IDC_Yes, IDC_No, IDC_Centaur },
\r
265 { ABOUTBOX2, IDC_ChessBoard },
\r
266 { DLG_GameList, OPT_GameListLoad, OPT_GameListPrev, OPT_GameListNext,
\r
267 OPT_GameListClose, IDC_GameListDoFilter },
\r
268 { DLG_EditTags, IDOK, OPT_TagsCancel, OPT_EditTags },
\r
269 { DLG_Error, IDOK },
\r
270 { DLG_Colorize, IDOK, IDCANCEL, OPT_ChooseColor, OPT_Bold, OPT_Italic,
\r
271 OPT_Underline, OPT_Strikeout, OPT_Sample },
\r
272 { DLG_Question, IDOK, IDCANCEL, OPT_QuestionText },
\r
273 { DLG_Startup, IDC_Welcome, OPT_ChessEngine, OPT_ChessServer, OPT_View,
\r
274 IDC_SPECIFY_ENG_STATIC, IDC_SPECIFY_SERVER_STATIC, OPT_AnyAdditional,
\r
275 IDOK, IDCANCEL, IDM_HELPCONTENTS },
\r
276 { DLG_IndexNumber, IDC_Index },
\r
277 { DLG_TypeInMove, IDOK, IDCANCEL },
\r
278 { DLG_TypeInName, IDOK, IDCANCEL },
\r
279 { DLG_Sound, IDC_Event, OPT_NoSound, OPT_DefaultBeep, OPT_BuiltInSound,
\r
280 OPT_WavFile, OPT_BrowseSound, OPT_DefaultSounds, IDOK, IDCANCEL, OPT_PlaySound },
\r
281 { DLG_GeneralOptions, IDOK, IDCANCEL, OPT_AlwaysOnTop, OPT_HighlightLastMove,
\r
282 OPT_AlwaysQueen, OPT_PeriodicUpdates, OPT_AnimateDragging, OPT_PonderNextMove,
\r
283 OPT_AnimateMoving, OPT_PopupExitMessage, OPT_AutoFlag, OPT_PopupMoveErrors,
\r
284 OPT_AutoFlipView, OPT_ShowButtonBar, OPT_AutoRaiseBoard, OPT_ShowCoordinates,
\r
285 OPT_Blindfold, OPT_ShowThinking, OPT_HighlightDragging, OPT_TestLegality,
\r
286 OPT_SaveExtPGN, OPT_HideThinkFromHuman, OPT_ExtraInfoInMoveHistory,
\r
287 OPT_HighlightMoveArrow, OPT_AutoLogo ,OPT_SmartMove },
\r
288 { DLG_IcsOptions, IDOK, IDCANCEL, OPT_AutoComment, OPT_AutoKibitz, OPT_AutoObserve,
\r
289 OPT_GetMoveList, OPT_LocalLineEditing, OPT_QuietPlay, OPT_SeekGraph, OPT_AutoRefresh,
\r
290 OPT_BgObserve, OPT_DualBoard, OPT_Premove, OPT_PremoveWhite, OPT_PremoveBlack,
\r
291 OPT_SmartMove, OPT_IcsAlarm, IDC_Sec, OPT_ChooseShoutColor, OPT_ChooseSShoutColor,
\r
292 OPT_ChooseChannel1Color, OPT_ChooseChannelColor, OPT_ChooseKibitzColor,
\r
293 OPT_ChooseTellColor, OPT_ChooseChallengeColor, OPT_ChooseRequestColor,
\r
294 OPT_ChooseSeekColor, OPT_ChooseNormalColor, OPT_ChooseBackgroundColor,
\r
295 OPT_DefaultColors, OPT_DontColorize, IDC_Boxes, GPB_Colors, GPB_Premove,
\r
296 GPB_General, GPB_Alarm },
\r
297 { DLG_BoardOptions, IDOK, IDCANCEL, OPT_SizeTiny, OPT_SizeTeeny, OPT_SizeDinky,
\r
298 OPT_SizePetite, OPT_SizeSlim, OPT_SizeSmall, OPT_SizeMediocre, OPT_SizeMiddling,
\r
299 OPT_SizeAverage, OPT_SizeModerate, OPT_SizeMedium, OPT_SizeBulky, OPT_SizeLarge,
\r
300 OPT_SizeBig, OPT_SizeHuge, OPT_SizeGiant, OPT_SizeColossal, OPT_SizeTitanic,
\r
301 OPT_ChooseLightSquareColor, OPT_ChooseDarkSquareColor, OPT_ChooseWhitePieceColor,
\r
302 OPT_ChooseBlackPieceColor, OPT_ChooseHighlightSquareColor, OPT_ChoosePremoveHighlightColor,
\r
303 OPT_Monochrome, OPT_AllWhite, OPT_UpsideDown, OPT_DefaultBoardColors, GPB_Colors,
\r
304 IDC_Light, IDC_Dark, IDC_White, IDC_Black, IDC_High, IDC_PreHigh, GPB_Size },
\r
305 { DLG_NewVariant, IDOK, IDCANCEL, OPT_VariantNormal, OPT_VariantFRC, OPT_VariantWildcastle,
\r
306 OPT_VariantNocastle, OPT_VariantLosers, OPT_VariantGiveaway, OPT_VariantSuicide,
\r
307 OPT_Variant3Check, OPT_VariantTwoKings, OPT_VariantAtomic, OPT_VariantCrazyhouse,
\r
308 OPT_VariantBughouse, OPT_VariantTwilight, OPT_VariantShogi, OPT_VariantSuper,
\r
309 OPT_VariantKnightmate, OPT_VariantBerolina, OPT_VariantCylinder, OPT_VariantFairy,
\r
310 OPT_VariantMakruk, OPT_VariantGothic, OPT_VariantCapablanca, OPT_VariantJanus,
\r
311 OPT_VariantCRC, OPT_VariantFalcon, OPT_VariantCourier, OPT_VariantGreat, OPT_VariantSChess,
\r
312 OPT_VariantShatranj, OPT_VariantXiangqi, GPB_Variant, GPB_Board, IDC_Height,
\r
313 IDC_Width, IDC_Hand, IDC_Pieces, IDC_Def },
\r
314 { DLG_Fonts, IDOK, IDCANCEL, OPT_ChooseClockFont, OPT_ChooseMessageFont,
\r
315 OPT_ChooseCoordFont, OPT_ChooseTagFont, OPT_ChooseCommentsFont, OPT_ChooseConsoleFont, OPT_ChooseMoveHistoryFont, OPT_DefaultFonts,
\r
316 OPT_ClockFont, OPT_MessageFont, OPT_CoordFont, OPT_EditTagsFont,
\r
317 OPT_SampleGameListFont, OPT_ChooseGameListFont, OPT_MessageFont7,
\r
318 OPT_CommentsFont, OPT_MessageFont5, GPB_Current, GPB_All, OPT_MessageFont6 },
\r
319 { DLG_NewGameFRC, IDC_NFG_Label, IDC_NFG_Random, IDOK, IDCANCEL },
\r
320 { DLG_GameListOptions, IDC_GLT, IDC_GLT_Up, IDC_GLT_Down, IDC_GLT_Restore,
\r
321 IDC_GLT_Default, IDOK, IDCANCEL, IDC_GLT_RestoreTo },
\r
322 { DLG_MoveHistory },
\r
323 { DLG_EvalGraph },
\r
324 { DLG_EngineOutput, IDC_EngineLabel1, IDC_Engine1_NPS, IDC_EngineLabel2, IDC_Engine2_NPS },
\r
325 { DLG_Chat, IDC_Partner, IDC_Clear, IDC_Send, },
\r
326 { DLG_EnginePlayOptions, IDC_EpPonder, IDC_EpShowThinking, IDC_EpHideThinkingHuman,
\r
327 IDC_EpPeriodicUpdates, GPB_Adjudications, IDC_Draw, IDC_Moves, IDC_Threshold,
\r
328 IDC_Centi, IDC_TestClaims, IDC_DetectMates, IDC_MaterialDraws, IDC_TrivialDraws,
\r
329 GPB_Apply, IDC_Rule, IDC_Repeats, IDC_ScoreAbs1, IDC_ScoreAbs2, IDOK, IDCANCEL },
\r
330 { DLG_OptionsUCI, IDC_PolyDir, IDC_BrowseForPolyglotDir, IDC_Hash, IDC_Path,
\r
331 IDC_BrowseForEGTB, IDC_Cache, IDC_UseBook, IDC_BrowseForBook, IDC_CPU, IDC_OwnBook1,
\r
332 IDC_OwnBook2, IDC_Depth, IDC_Variation, IDC_DefGames, IDOK, IDCANCEL },
\r
336 static char languageBuf[50000], *foreign[1000], *english[1000], *languageFile[MSG_SIZ];
\r
337 static int lastChecked;
\r
338 static char oldLanguage[MSG_SIZ], *menuText[10][30];
\r
339 extern int tinyLayout;
\r
340 extern char * menuBarText[][10];
\r
343 LoadLanguageFile(char *name)
\r
344 { //load the file with translations, and make a list of the strings to be translated, and their translations
\r
346 int i=0, j=0, n=0, k;
\r
349 if(!name || name[0] == NULLCHAR) return;
\r
350 snprintf(buf, MSG_SIZ, "%s%s", name, strchr(name, '.') ? "" : ".lng"); // auto-append lng extension
\r
351 appData.language = oldLanguage;
\r
352 if(!strcmp(buf, oldLanguage)) { barbaric = 1; return; } // this language already loaded; just switch on
\r
353 if((f = fopen(buf, "r")) == NULL) return;
\r
354 while((k = fgetc(f)) != EOF) {
\r
355 if(i >= sizeof(languageBuf)) { DisplayError("Language file too big", 0); return; }
\r
356 languageBuf[i] = k;
\r
358 if(languageBuf[n] == '"' && languageBuf[i-1] == '"') {
\r
360 if(p = strstr(languageBuf + n + 1, "\" === \"")) {
\r
361 if(p > languageBuf+n+2 && p+8 < languageBuf+i) {
\r
362 if(j >= sizeof(english)) { DisplayError("Too many translated strings", 0); return; }
\r
363 english[j] = languageBuf + n + 1; *p = 0;
\r
364 foreign[j++] = p + 7; languageBuf[i-1] = 0;
\r
365 //if(appData.debugMode) fprintf(debugFP, "translation: replace '%s' by '%s'\n", english[j-1], foreign[j-1]);
\r
370 } else if(i > 0 && languageBuf[i-1] == '\\') {
\r
372 case 'n': k = '\n'; break;
\r
373 case 'r': k = '\r'; break;
\r
374 case 't': k = '\t'; break;
\r
376 languageBuf[--i] = k;
\r
381 barbaric = (j != 0);
\r
382 safeStrCpy(oldLanguage, buf, sizeof(oldLanguage)/sizeof(oldLanguage[0]) );
\r
387 { // return the translation of the given string
\r
388 // efficiency can be improved a lot...
\r
390 //if(appData.debugMode) fprintf(debugFP, "T_(%s)\n", s);
\r
391 if(!barbaric) return s;
\r
392 if(!s) return ""; // sanity
\r
393 while(english[i]) {
\r
394 if(!strcmp(s, english[i])) return foreign[i];
\r
401 Translate(HWND hDlg, int dialogID)
\r
402 { // translate all text items in the given dialog
\r
404 char buf[MSG_SIZ], *s;
\r
405 if(!barbaric) return;
\r
406 while(dialogItems[i][0] && dialogItems[i][0] != dialogID) i++; // find the dialog description
\r
407 if(dialogItems[i][0] != dialogID) return; // unknown dialog, should not happen
\r
408 GetWindowText( hDlg, buf, MSG_SIZ );
\r
410 if(strcmp(buf, s)) SetWindowText(hDlg, s); // replace by translated string (if different)
\r
411 for(j=1; k=dialogItems[i][j]; j++) { // translate all listed dialog items
\r
412 GetDlgItemText(hDlg, k, buf, MSG_SIZ);
\r
413 if(strlen(buf) == 0) continue;
\r
415 if(strcmp(buf, s)) SetDlgItemText(hDlg, k, s); // replace by translated string (if different)
\r
420 TranslateOneMenu(int i, HMENU subMenu)
\r
423 static MENUITEMINFO info;
\r
425 info.cbSize = sizeof(MENUITEMINFO);
\r
426 info.fMask = MIIM_STATE | MIIM_TYPE;
\r
427 for(j=GetMenuItemCount(subMenu)-1; j>=0; j--){
\r
429 info.dwTypeData = buf;
\r
430 info.cch = sizeof(buf);
\r
431 GetMenuItemInfo(subMenu, j, TRUE, &info);
\r
433 if(menuText[i][j]) safeStrCpy(buf, menuText[i][j], sizeof(buf)/sizeof(buf[0]) );
\r
434 else menuText[i][j] = strdup(buf); // remember original on first change
\r
436 if(buf[0] == NULLCHAR) continue;
\r
437 info.dwTypeData = T_(buf);
\r
438 info.cch = strlen(buf)+1;
\r
439 SetMenuItemInfo(subMenu, j, TRUE, &info);
\r
445 TranslateMenus(int addLanguage)
\r
448 WIN32_FIND_DATA fileData;
\r
450 #define IDM_English 1970
\r
452 HMENU mainMenu = GetMenu(hwndMain);
\r
453 for (i=GetMenuItemCount(mainMenu)-1; i>=0; i--) {
\r
454 HMENU subMenu = GetSubMenu(mainMenu, i);
\r
455 ModifyMenu(mainMenu, i, MF_STRING|MF_BYPOSITION|MF_POPUP|EnableMenuItem(mainMenu, i, MF_BYPOSITION),
\r
456 (UINT) subMenu, T_(menuBarText[tinyLayout][i]));
\r
457 TranslateOneMenu(i, subMenu);
\r
459 DrawMenuBar(hwndMain);
\r
462 if(!addLanguage) return;
\r
463 if((hFind = FindFirstFile("*.LNG", &fileData)) != INVALID_HANDLE_VALUE) {
\r
464 HMENU mainMenu = GetMenu(hwndMain);
\r
465 HMENU subMenu = GetSubMenu(mainMenu, GetMenuItemCount(mainMenu)-1);
\r
466 AppendMenu(subMenu, MF_SEPARATOR, (UINT_PTR) 0, NULL);
\r
467 AppendMenu(subMenu, MF_ENABLED|MF_STRING|(barbaric?MF_UNCHECKED:MF_CHECKED), (UINT_PTR) IDM_English, (LPCTSTR) "English");
\r
468 i = 0; lastChecked = IDM_English;
\r
470 char *p, *q = fileData.cFileName;
\r
471 int checkFlag = MF_UNCHECKED;
\r
472 languageFile[i] = strdup(q);
\r
473 if(barbaric && !strcmp(oldLanguage, q)) {
\r
474 checkFlag = MF_CHECKED;
\r
475 lastChecked = IDM_English + i + 1;
\r
476 CheckMenuItem(mainMenu, IDM_English, MF_BYCOMMAND|MF_UNCHECKED);
\r
478 *q = ToUpper(*q); while(*++q) *q = ToLower(*q);
\r
479 p = strstr(fileData.cFileName, ".lng");
\r
481 AppendMenu(subMenu, MF_ENABLED|MF_STRING|checkFlag, (UINT_PTR) IDM_English + ++i, (LPCTSTR) fileData.cFileName);
\r
482 } while(FindNextFile(hFind, &fileData));
\r
495 int cliWidth, cliHeight;
\r
498 SizeInfo sizeInfo[] =
\r
500 { "tiny", 21, 0, 1, 1, 0, 0 },
\r
501 { "teeny", 25, 1, 1, 1, 0, 0 },
\r
502 { "dinky", 29, 1, 1, 1, 0, 0 },
\r
503 { "petite", 33, 1, 1, 1, 0, 0 },
\r
504 { "slim", 37, 2, 1, 0, 0, 0 },
\r
505 { "small", 40, 2, 1, 0, 0, 0 },
\r
506 { "mediocre", 45, 2, 1, 0, 0, 0 },
\r
507 { "middling", 49, 2, 0, 0, 0, 0 },
\r
508 { "average", 54, 2, 0, 0, 0, 0 },
\r
509 { "moderate", 58, 3, 0, 0, 0, 0 },
\r
510 { "medium", 64, 3, 0, 0, 0, 0 },
\r
511 { "bulky", 72, 3, 0, 0, 0, 0 },
\r
512 { "large", 80, 3, 0, 0, 0, 0 },
\r
513 { "big", 87, 3, 0, 0, 0, 0 },
\r
514 { "huge", 95, 3, 0, 0, 0, 0 },
\r
515 { "giant", 108, 3, 0, 0, 0, 0 },
\r
516 { "colossal", 116, 4, 0, 0, 0, 0 },
\r
517 { "titanic", 129, 4, 0, 0, 0, 0 },
\r
518 { NULL, 0, 0, 0, 0, 0, 0 }
\r
521 #define MF(x) {x, {{0,}, 0. }, {0, }, 0}
\r
522 MyFont fontRec[NUM_SIZES][NUM_FONTS] =
\r
524 { 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), MF(GAMELIST_FONT_ALL) },
\r
525 { 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), MF(GAMELIST_FONT_ALL) },
\r
526 { 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), MF(GAMELIST_FONT_ALL) },
\r
527 { 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), MF(GAMELIST_FONT_ALL) },
\r
528 { 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), MF(GAMELIST_FONT_ALL) },
\r
529 { 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), MF(GAMELIST_FONT_ALL) },
\r
530 { 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), MF(GAMELIST_FONT_ALL) },
\r
531 { 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), MF(GAMELIST_FONT_ALL) },
\r
532 { 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), MF(GAMELIST_FONT_ALL) },
\r
533 { 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), MF(GAMELIST_FONT_ALL) },
\r
534 { 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), MF(GAMELIST_FONT_ALL) },
\r
535 { 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), MF(GAMELIST_FONT_ALL) },
\r
536 { 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), MF(GAMELIST_FONT_ALL) },
\r
537 { 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), MF(GAMELIST_FONT_ALL) },
\r
538 { 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), MF(GAMELIST_FONT_ALL) },
\r
539 { 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), MF(GAMELIST_FONT_ALL) },
\r
540 { 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), MF (GAMELIST_FONT_ALL) },
\r
541 { 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), MF(GAMELIST_FONT_ALL) },
\r
544 MyFont *font[NUM_SIZES][NUM_FONTS];
\r
553 #define BUTTON_WIDTH (tinyLayout ? 16 : 32)
\r
554 #define N_BUTTONS 5
\r
556 MyButtonDesc buttonDesc[N_BUTTONS] =
\r
558 {"<<", IDM_ToStart, NULL, NULL},
\r
559 {"<", IDM_Backward, NULL, NULL},
\r
560 {"P", IDM_Pause, NULL, NULL},
\r
561 {">", IDM_Forward, NULL, NULL},
\r
562 {">>", IDM_ToEnd, NULL, NULL},
\r
565 int tinyLayout = 0, smallLayout = 0;
\r
566 #define MENU_BAR_ITEMS 9
\r
567 char *menuBarText[2][MENU_BAR_ITEMS+1] = {
\r
568 { N_("&File"), N_("&Edit"), N_("&View"), N_("&Mode"), N_("&Action"), N_("E&ngine"), N_("&Options"), N_("&Help"), NULL },
\r
569 { N_("&F"), N_("&E"), N_("&V"), N_("&M"), N_("&A"), N_("&N"), N_("&O"), N_("&H"), NULL },
\r
573 MySound sounds[(int)NSoundClasses];
\r
574 MyTextAttribs textAttribs[(int)NColorClasses];
\r
576 MyColorizeAttribs colorizeAttribs[] = {
\r
577 { (COLORREF)0, 0, N_("Shout Text") },
\r
578 { (COLORREF)0, 0, N_("SShout/CShout") },
\r
579 { (COLORREF)0, 0, N_("Channel 1 Text") },
\r
580 { (COLORREF)0, 0, N_("Channel Text") },
\r
581 { (COLORREF)0, 0, N_("Kibitz Text") },
\r
582 { (COLORREF)0, 0, N_("Tell Text") },
\r
583 { (COLORREF)0, 0, N_("Challenge Text") },
\r
584 { (COLORREF)0, 0, N_("Request Text") },
\r
585 { (COLORREF)0, 0, N_("Seek Text") },
\r
586 { (COLORREF)0, 0, N_("Normal Text") },
\r
587 { (COLORREF)0, 0, N_("None") }
\r
592 static char *commentTitle;
\r
593 static char *commentText;
\r
594 static int commentIndex;
\r
595 static Boolean editComment = FALSE;
\r
598 char errorTitle[MSG_SIZ];
\r
599 char errorMessage[2*MSG_SIZ];
\r
600 HWND errorDialog = NULL;
\r
601 BOOLEAN moveErrorMessageUp = FALSE;
\r
602 BOOLEAN consoleEcho = TRUE;
\r
603 CHARFORMAT consoleCF;
\r
604 COLORREF consoleBackgroundColor;
\r
606 char *programVersion;
\r
612 typedef int CPKind;
\r
621 SOCKET sock2; /* stderr socket for OpenRcmd */
\r
624 #define INPUT_SOURCE_BUF_SIZE 4096
\r
626 typedef struct _InputSource {
\r
633 char buf[INPUT_SOURCE_BUF_SIZE];
\r
637 InputCallback func;
\r
638 struct _InputSource *second; /* for stderr thread on CPRcmd */
\r
642 InputSource *consoleInputSource;
\r
647 VOID ConsoleOutput(char* data, int length, int forceVisible);
\r
648 VOID ConsoleCreate();
\r
650 ConsoleWndProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam);
\r
651 VOID ColorizeTextPopup(HWND hwnd, ColorClass cc);
\r
652 VOID PrintCommSettings(FILE *f, char *name, DCB *dcb);
\r
653 VOID ParseCommSettings(char *arg, DCB *dcb);
\r
655 StartupDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam);
\r
656 VOID APIENTRY MenuPopup(HWND hwnd, POINT pt, HMENU hmenu, UINT def);
\r
657 void ParseIcsTextMenu(char *icsTextMenuString);
\r
658 VOID PopUpNameDialog(char firstchar);
\r
659 VOID UpdateSampleText(HWND hDlg, int id, MyColorizeAttribs *mca);
\r
663 int GameListOptions();
\r
665 int dummy; // [HGM] for obsolete args
\r
667 HWND hwndMain = NULL; /* root window*/
\r
668 HWND hwndConsole = NULL;
\r
669 HWND commentDialog = NULL;
\r
670 HWND moveHistoryDialog = NULL;
\r
671 HWND evalGraphDialog = NULL;
\r
672 HWND engineOutputDialog = NULL;
\r
673 HWND gameListDialog = NULL;
\r
674 HWND editTagsDialog = NULL;
\r
676 int commentUp = FALSE;
\r
678 WindowPlacement wpMain;
\r
679 WindowPlacement wpConsole;
\r
680 WindowPlacement wpComment;
\r
681 WindowPlacement wpMoveHistory;
\r
682 WindowPlacement wpEvalGraph;
\r
683 WindowPlacement wpEngineOutput;
\r
684 WindowPlacement wpGameList;
\r
685 WindowPlacement wpTags;
\r
687 VOID EngineOptionsPopup(); // [HGM] settings
\r
689 VOID GothicPopUp(char *title, VariantClass variant);
\r
691 * Setting "frozen" should disable all user input other than deleting
\r
692 * the window. We do this while engines are initializing themselves.
\r
694 static int frozen = 0;
\r
695 static int oldMenuItemState[MENU_BAR_ITEMS];
\r
701 if (frozen) return;
\r
703 hmenu = GetMenu(hwndMain);
\r
704 for (i=0; i<MENU_BAR_ITEMS; i++) {
\r
705 oldMenuItemState[i] = EnableMenuItem(hmenu, i, MF_BYPOSITION|MF_GRAYED);
\r
707 DrawMenuBar(hwndMain);
\r
710 /* Undo a FreezeUI */
\r
716 if (!frozen) return;
\r
718 hmenu = GetMenu(hwndMain);
\r
719 for (i=0; i<MENU_BAR_ITEMS; i++) {
\r
720 EnableMenuItem(hmenu, i, MF_BYPOSITION|oldMenuItemState[i]);
\r
722 DrawMenuBar(hwndMain);
\r
725 /*static*/ int fromX = -1, fromY = -1, toX, toY; // [HGM] moved upstream, so JAWS can use them
\r
727 /* JAWS preparation patch (WinBoard for the sight impaired). Define required insertions as empty */
\r
733 #define JAWS_ALT_INTERCEPT
\r
734 #define JAWS_KB_NAVIGATION
\r
735 #define JAWS_MENU_ITEMS
\r
736 #define JAWS_SILENCE
\r
737 #define JAWS_REPLAY
\r
739 #define JAWS_COPYRIGHT
\r
740 #define JAWS_DELETE(X) X
\r
741 #define SAYMACHINEMOVE()
\r
745 /*---------------------------------------------------------------------------*\
\r
749 \*---------------------------------------------------------------------------*/
\r
752 WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
\r
753 LPSTR lpCmdLine, int nCmdShow)
\r
756 HANDLE hAccelMain, hAccelNoAlt, hAccelNoICS;
\r
757 // INITCOMMONCONTROLSEX ex;
\r
761 LoadLibrary("RICHED32.DLL");
\r
762 consoleCF.cbSize = sizeof(CHARFORMAT);
\r
764 if (!InitApplication(hInstance)) {
\r
767 if (!InitInstance(hInstance, nCmdShow, lpCmdLine)) {
\r
774 // InitCommonControlsEx(&ex);
\r
775 InitCommonControls();
\r
777 hAccelMain = LoadAccelerators (hInstance, szAppName);
\r
778 hAccelNoAlt = LoadAccelerators (hInstance, "NO_ALT");
\r
779 hAccelNoICS = LoadAccelerators( hInstance, "NO_ICS"); /* [AS] No Ctrl-V on ICS!!! */
\r
781 /* Acquire and dispatch messages until a WM_QUIT message is received. */
\r
783 while (GetMessage(&msg, /* message structure */
\r
784 NULL, /* handle of window receiving the message */
\r
785 0, /* lowest message to examine */
\r
786 0)) /* highest message to examine */
\r
789 if(msg.message == WM_CHAR && msg.wParam == '\t') {
\r
790 // [HGM] navigate: switch between all windows with tab
\r
791 HWND e1 = NULL, e2 = NULL, mh = NULL, hInput = NULL, hText = NULL;
\r
792 int i, currentElement = 0;
\r
794 // first determine what element of the chain we come from (if any)
\r
795 if(appData.icsActive) {
\r
796 hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
797 hText = GetDlgItem(hwndConsole, OPT_ConsoleText);
\r
799 if(engineOutputDialog && EngineOutputIsUp()) {
\r
800 e1 = GetDlgItem(engineOutputDialog, IDC_EngineMemo1);
\r
801 e2 = GetDlgItem(engineOutputDialog, IDC_EngineMemo2);
\r
803 if(moveHistoryDialog && MoveHistoryIsUp()) {
\r
804 mh = GetDlgItem(moveHistoryDialog, IDC_MoveHistory);
\r
806 if(msg.hwnd == hwndMain) currentElement = 7 ; else
\r
807 if(msg.hwnd == engineOutputDialog) currentElement = 2; else
\r
808 if(msg.hwnd == e1) currentElement = 2; else
\r
809 if(msg.hwnd == e2) currentElement = 3; else
\r
810 if(msg.hwnd == moveHistoryDialog) currentElement = 4; else
\r
811 if(msg.hwnd == mh) currentElement = 4; else
\r
812 if(msg.hwnd == evalGraphDialog) currentElement = 6; else
\r
813 if(msg.hwnd == hText) currentElement = 5; else
\r
814 if(msg.hwnd == hInput) currentElement = 6; else
\r
815 for (i = 0; i < N_BUTTONS; i++) {
\r
816 if (buttonDesc[i].hwnd == msg.hwnd) { currentElement = 1; break; }
\r
819 // determine where to go to
\r
820 if(currentElement) { HWND h = NULL; int direction = GetKeyState(VK_SHIFT) < 0 ? -1 : 1;
\r
822 currentElement = (currentElement + direction) % 7;
\r
823 switch(currentElement) {
\r
825 h = hwndMain; break; // passing this case always makes the loop exit
\r
827 h = buttonDesc[0].hwnd; break; // could be NULL
\r
829 if(!EngineOutputIsUp()) continue; // skip closed auxiliary windows
\r
832 if(!EngineOutputIsUp()) continue;
\r
835 if(!MoveHistoryIsUp()) continue;
\r
837 // case 6: // input to eval graph does not seem to get here!
\r
838 // if(!EvalGraphIsUp()) continue;
\r
839 // h = evalGraphDialog; break;
\r
841 if(!appData.icsActive) continue;
\r
845 if(!appData.icsActive) continue;
\r
851 if(currentElement > 4 && IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);
\r
852 if(currentElement < 5 && IsIconic(hwndMain)) ShowWindow(hwndMain, SW_RESTORE); // all open together
\r
855 continue; // this message now has been processed
\r
859 if (!(commentDialog && IsDialogMessage(commentDialog, &msg)) &&
\r
860 !(moveHistoryDialog && IsDialogMessage(moveHistoryDialog, &msg)) &&
\r
861 !(evalGraphDialog && IsDialogMessage(evalGraphDialog, &msg)) &&
\r
862 !(engineOutputDialog && IsDialogMessage(engineOutputDialog, &msg)) &&
\r
863 !(editTagsDialog && IsDialogMessage(editTagsDialog, &msg)) &&
\r
864 !(gameListDialog && IsDialogMessage(gameListDialog, &msg)) &&
\r
865 !(errorDialog && IsDialogMessage(errorDialog, &msg)) &&
\r
866 !(!frozen && TranslateAccelerator(hwndMain, hAccelMain, &msg)) && JAWS_ACCEL
\r
867 !(!hwndConsole && TranslateAccelerator(hwndMain, hAccelNoICS, &msg)) &&
\r
868 !(!hwndConsole && TranslateAccelerator(hwndMain, hAccelNoAlt, &msg))) {
\r
869 int done = 0, i; // [HGM] chat: dispatch cat-box messages
\r
870 for(i=0; i<MAX_CHAT; i++)
\r
871 if(chatHandle[i] && IsDialogMessage(chatHandle[i], &msg)) {
\r
874 if(done) continue; // [HGM] chat: end patch
\r
875 TranslateMessage(&msg); /* Translates virtual key codes */
\r
876 DispatchMessage(&msg); /* Dispatches message to window */
\r
881 return (msg.wParam); /* Returns the value from PostQuitMessage */
\r
884 /*---------------------------------------------------------------------------*\
\r
886 * Initialization functions
\r
888 \*---------------------------------------------------------------------------*/
\r
892 { // update user logo if necessary
\r
893 static char oldUserName[MSG_SIZ], dir[MSG_SIZ], *curName;
\r
895 if(appData.autoLogo) {
\r
896 curName = UserName();
\r
897 if(strcmp(curName, oldUserName)) {
\r
898 GetCurrentDirectory(MSG_SIZ, dir);
\r
899 SetCurrentDirectory(installDir);
\r
900 snprintf(oldUserName, MSG_SIZ, "logos\\%s.bmp", curName);
\r
901 userLogo = LoadImage( 0, oldUserName, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );
\r
902 safeStrCpy(oldUserName, curName, sizeof(oldUserName)/sizeof(oldUserName[0]) );
\r
903 if(userLogo == NULL)
\r
904 userLogo = LoadImage( 0, "logos\\dummy.bmp", IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );
\r
905 SetCurrentDirectory(dir); /* return to prev directory */
\r
911 InitApplication(HINSTANCE hInstance)
\r
915 /* Fill in window class structure with parameters that describe the */
\r
918 wc.style = CS_HREDRAW | CS_VREDRAW; /* Class style(s). */
\r
919 wc.lpfnWndProc = (WNDPROC)WndProc; /* Window Procedure */
\r
920 wc.cbClsExtra = 0; /* No per-class extra data. */
\r
921 wc.cbWndExtra = 0; /* No per-window extra data. */
\r
922 wc.hInstance = hInstance; /* Owner of this class */
\r
923 wc.hIcon = LoadIcon(hInstance, "icon_white");
\r
924 wc.hCursor = LoadCursor(NULL, IDC_ARROW); /* Cursor */
\r
925 wc.hbrBackground = (HBRUSH)(COLOR_WINDOW+1); /* Default color */
\r
926 wc.lpszMenuName = szAppName; /* Menu name from .RC */
\r
927 wc.lpszClassName = szAppName; /* Name to register as */
\r
929 /* Register the window class and return success/failure code. */
\r
930 if (!RegisterClass(&wc)) return FALSE;
\r
932 wc.style = CS_HREDRAW | CS_VREDRAW;
\r
933 wc.lpfnWndProc = (WNDPROC)ConsoleWndProc;
\r
935 wc.cbWndExtra = DLGWINDOWEXTRA;
\r
936 wc.hInstance = hInstance;
\r
937 wc.hIcon = LoadIcon(hInstance, "icon_white");
\r
938 wc.hCursor = LoadCursor(NULL, IDC_ARROW);
\r
939 wc.hbrBackground = (HBRUSH)(COLOR_MENU+1);
\r
940 wc.lpszMenuName = NULL;
\r
941 wc.lpszClassName = szConsoleName;
\r
943 if (!RegisterClass(&wc)) return FALSE;
\r
948 /* Set by InitInstance, used by EnsureOnScreen */
\r
949 int screenHeight, screenWidth;
\r
952 EnsureOnScreen(int *x, int *y, int minX, int minY)
\r
954 // int gap = GetSystemMetrics(SM_CYFRAME) + GetSystemMetrics(SM_CYCAPTION);
\r
955 /* Be sure window at (x,y) is not off screen (or even mostly off screen) */
\r
956 if (*x > screenWidth - 32) *x = 0;
\r
957 if (*y > screenHeight - 32) *y = 0;
\r
958 if (*x < minX) *x = minX;
\r
959 if (*y < minY) *y = minY;
\r
963 LoadLogo(ChessProgramState *cps, int n, Boolean ics)
\r
965 char buf[MSG_SIZ], dir[MSG_SIZ];
\r
966 GetCurrentDirectory(MSG_SIZ, dir);
\r
967 SetCurrentDirectory(installDir);
\r
968 if( appData.logo[n] && appData.logo[n][0] != NULLCHAR) {
\r
969 cps->programLogo = LoadImage( 0, appData.logo[n], IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );
\r
971 if (cps->programLogo == NULL && appData.debugMode) {
\r
972 fprintf( debugFP, "Unable to load logo bitmap '%s'\n", appData.logo[n] );
\r
974 } else if(appData.autoLogo) {
\r
975 if(ics) { // [HGM] logo: in ICS mode second can be used for ICS
\r
976 sprintf(buf, "logos\\%s.bmp", appData.icsHost);
\r
977 cps->programLogo = LoadImage( 0, buf, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );
\r
979 if(appData.directory[n] && appData.directory[n][0]) {
\r
980 SetCurrentDirectory(appData.directory[n]);
\r
981 cps->programLogo = LoadImage( 0, "logo.bmp", IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );
\r
984 SetCurrentDirectory(dir); /* return to prev directory */
\r
988 InitInstance(HINSTANCE hInstance, int nCmdShow, LPSTR lpCmdLine)
\r
990 HWND hwnd; /* Main window handle. */
\r
992 WINDOWPLACEMENT wp;
\r
995 hInst = hInstance; /* Store instance handle in our global variable */
\r
996 programName = szAppName;
\r
998 if (SearchPath(NULL, "WinBoard.exe", NULL, MSG_SIZ, installDir, &filepart)) {
\r
999 *filepart = NULLCHAR;
\r
1001 GetCurrentDirectory(MSG_SIZ, installDir);
\r
1003 gameInfo.boardWidth = gameInfo.boardHeight = 8; // [HGM] won't have open window otherwise
\r
1004 screenWidth = screenHeight = 1000; // [HGM] placement: kludge to allow calling EnsureOnScreen from InitAppData
\r
1005 InitAppData(lpCmdLine); /* Get run-time parameters */
\r
1006 /* xboard, and older WinBoards, controlled the move sound with the
\r
1007 appData.ringBellAfterMoves option. In the current WinBoard, we
\r
1008 always turn the option on (so that the backend will call us),
\r
1009 then let the user turn the sound off by setting it to silence if
\r
1010 desired. To accommodate old winboard.ini files saved by old
\r
1011 versions of WinBoard, we also turn off the sound if the option
\r
1012 was initially set to false. [HGM] taken out of InitAppData */
\r
1013 if (!appData.ringBellAfterMoves) {
\r
1014 sounds[(int)SoundMove].name = strdup("");
\r
1015 appData.ringBellAfterMoves = TRUE;
\r
1017 if (appData.debugMode) {
\r
1018 debugFP = fopen(appData.nameOfDebugFile, "w");
\r
1019 setbuf(debugFP, NULL);
\r
1022 LoadLanguageFile(appData.language);
\r
1026 // InitEngineUCI( installDir, &first ); // [HGM] incorporated in InitBackEnd1()
\r
1027 // InitEngineUCI( installDir, &second );
\r
1029 /* Create a main window for this application instance. */
\r
1030 hwnd = CreateWindow(szAppName, szTitle,
\r
1031 (WS_OVERLAPPEDWINDOW & ~WS_MAXIMIZEBOX),
\r
1032 CW_USEDEFAULT, 0, CW_USEDEFAULT, 0,
\r
1033 NULL, NULL, hInstance, NULL);
\r
1036 /* If window could not be created, return "failure" */
\r
1041 /* [HGM] logo: Load logos if specified (must be done before InitDrawingSizes) */
\r
1042 LoadLogo(&first, 0, FALSE);
\r
1043 LoadLogo(&second, 1, appData.icsActive);
\r
1047 iconWhite = LoadIcon(hInstance, "icon_white");
\r
1048 iconBlack = LoadIcon(hInstance, "icon_black");
\r
1049 iconCurrent = iconWhite;
\r
1050 InitDrawingColors();
\r
1051 screenHeight = GetSystemMetrics(SM_CYSCREEN);
\r
1052 screenWidth = GetSystemMetrics(SM_CXSCREEN);
\r
1053 for (ibs = (int) NUM_SIZES - 1; ibs >= 0; ibs--) {
\r
1054 /* Compute window size for each board size, and use the largest
\r
1055 size that fits on this screen as the default. */
\r
1056 InitDrawingSizes((BoardSize)(ibs+1000), 0);
\r
1057 if (boardSize == (BoardSize)-1 &&
\r
1058 winH <= screenHeight
\r
1059 - GetSystemMetrics(SM_CYFRAME) - GetSystemMetrics(SM_CYCAPTION) - 10
\r
1060 && winW <= screenWidth) {
\r
1061 boardSize = (BoardSize)ibs;
\r
1065 InitDrawingSizes(boardSize, 0);
\r
1067 buttonCount = GetSystemMetrics(SM_CMOUSEBUTTONS);
\r
1069 /* [AS] Load textures if specified */
\r
1070 ZeroMemory( &backTextureSquareInfo, sizeof(backTextureSquareInfo) );
\r
1072 if( appData.liteBackTextureFile && appData.liteBackTextureFile[0] != NULLCHAR && appData.liteBackTextureFile[0] != '*' ) {
\r
1073 liteBackTexture = LoadImage( 0, appData.liteBackTextureFile, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );
\r
1074 liteBackTextureMode = appData.liteBackTextureMode;
\r
1076 if (liteBackTexture == NULL && appData.debugMode) {
\r
1077 fprintf( debugFP, "Unable to load lite texture bitmap '%s'\n", appData.liteBackTextureFile );
\r
1081 if( appData.darkBackTextureFile && appData.darkBackTextureFile[0] != NULLCHAR && appData.darkBackTextureFile[0] != '*' ) {
\r
1082 darkBackTexture = LoadImage( 0, appData.darkBackTextureFile, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );
\r
1083 darkBackTextureMode = appData.darkBackTextureMode;
\r
1085 if (darkBackTexture == NULL && appData.debugMode) {
\r
1086 fprintf( debugFP, "Unable to load dark texture bitmap '%s'\n", appData.darkBackTextureFile );
\r
1090 mysrandom( (unsigned) time(NULL) );
\r
1092 /* [AS] Restore layout */
\r
1093 if( wpMoveHistory.visible ) {
\r
1094 MoveHistoryPopUp();
\r
1097 if( wpEvalGraph.visible ) {
\r
1101 if( wpEngineOutput.visible ) {
\r
1102 EngineOutputPopUp();
\r
1105 /* Make the window visible; update its client area; and return "success" */
\r
1106 EnsureOnScreen(&wpMain.x, &wpMain.y, minX, minY);
\r
1107 wp.length = sizeof(WINDOWPLACEMENT);
\r
1109 wp.showCmd = nCmdShow;
\r
1110 wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;
\r
1111 wp.rcNormalPosition.left = wpMain.x;
\r
1112 wp.rcNormalPosition.right = wpMain.x + wpMain.width;
\r
1113 wp.rcNormalPosition.top = wpMain.y;
\r
1114 wp.rcNormalPosition.bottom = wpMain.y + wpMain.height;
\r
1115 SetWindowPlacement(hwndMain, &wp);
\r
1117 InitBackEnd2(); // [HGM] moved until after all windows placed, to save correct position if fatal error on engine start
\r
1119 if(!appData.noGUI) SetWindowPos(hwndMain, alwaysOnTop ? HWND_TOPMOST : HWND_NOTOPMOST,
\r
1120 0, 0, 0, 0, SWP_NOMOVE|SWP_NOSIZE);
\r
1122 if (hwndConsole) {
\r
1124 SetWindowPos(hwndConsole, alwaysOnTop ? HWND_TOPMOST : HWND_NOTOPMOST,
\r
1125 0, 0, 0, 0, SWP_NOMOVE|SWP_NOSIZE);
\r
1127 ShowWindow(hwndConsole, nCmdShow);
\r
1128 if(appData.chatBoxes) { // [HGM] chat: open chat boxes
\r
1129 char buf[MSG_SIZ], *p = buf, *q;
\r
1130 safeStrCpy(buf, appData.chatBoxes, sizeof(buf)/sizeof(buf[0]) );
\r
1132 q = strchr(p, ';');
\r
1134 if(*p) ChatPopUp(p);
\r
1137 SetActiveWindow(hwndConsole);
\r
1139 if(!appData.noGUI) UpdateWindow(hwnd); else ShowWindow(hwnd, SW_MINIMIZE);
\r
1140 if(gameListDialog) SetFocus(gameListDialog); // [HGM] jaws: for if we clicked multi-game game file
\r
1149 HMENU hmenu = GetMenu(hwndMain);
\r
1151 (void) EnableMenuItem(hmenu, IDM_CommPort,
\r
1152 MF_BYCOMMAND|((appData.icsActive &&
\r
1153 *appData.icsCommPort != NULLCHAR) ?
\r
1154 MF_ENABLED : MF_GRAYED));
\r
1155 (void) CheckMenuItem(hmenu, IDM_SaveSettingsOnExit,
\r
1156 MF_BYCOMMAND|(saveSettingsOnExit ?
\r
1157 MF_CHECKED : MF_UNCHECKED));
\r
1160 //---------------------------------------------------------------------------------------------------------
\r
1162 #define ICS_TEXT_MENU_SIZE (IDM_CommandXLast - IDM_CommandX + 1)
\r
1163 #define XBOARD FALSE
\r
1165 #define OPTCHAR "/"
\r
1166 #define SEPCHAR "="
\r
1170 // front-end part of option handling
\r
1173 LFfromMFP(LOGFONT* lf, MyFontParams *mfp)
\r
1175 HDC hdc = CreateDC("DISPLAY", NULL, NULL, NULL);
\r
1176 lf->lfHeight = -(int)(mfp->pointSize * GetDeviceCaps(hdc, LOGPIXELSY) / 72.0 + 0.5);
\r
1179 lf->lfEscapement = 0;
\r
1180 lf->lfOrientation = 0;
\r
1181 lf->lfWeight = mfp->bold ? FW_BOLD : FW_NORMAL;
\r
1182 lf->lfItalic = mfp->italic;
\r
1183 lf->lfUnderline = mfp->underline;
\r
1184 lf->lfStrikeOut = mfp->strikeout;
\r
1185 lf->lfCharSet = mfp->charset;
\r
1186 lf->lfOutPrecision = OUT_DEFAULT_PRECIS;
\r
1187 lf->lfClipPrecision = CLIP_DEFAULT_PRECIS;
\r
1188 lf->lfQuality = DEFAULT_QUALITY;
\r
1189 lf->lfPitchAndFamily = DEFAULT_PITCH|FF_DONTCARE;
\r
1190 safeStrCpy(lf->lfFaceName, mfp->faceName, sizeof(lf->lfFaceName)/sizeof(lf->lfFaceName[0]) );
\r
1194 CreateFontInMF(MyFont *mf)
\r
1196 LFfromMFP(&mf->lf, &mf->mfp);
\r
1197 if (mf->hf) DeleteObject(mf->hf);
\r
1198 mf->hf = CreateFontIndirect(&mf->lf);
\r
1201 // [HGM] This platform-dependent table provides the location for storing the color info
\r
1203 colorVariable[] = {
\r
1204 &whitePieceColor,
\r
1205 &blackPieceColor,
\r
1206 &lightSquareColor,
\r
1207 &darkSquareColor,
\r
1208 &highlightSquareColor,
\r
1209 &premoveHighlightColor,
\r
1211 &consoleBackgroundColor,
\r
1212 &appData.fontForeColorWhite,
\r
1213 &appData.fontBackColorWhite,
\r
1214 &appData.fontForeColorBlack,
\r
1215 &appData.fontBackColorBlack,
\r
1216 &appData.evalHistColorWhite,
\r
1217 &appData.evalHistColorBlack,
\r
1218 &appData.highlightArrowColor,
\r
1221 /* Command line font name parser. NULL name means do nothing.
\r
1222 Syntax like "Courier New:10.0 bi" or "Arial:10" or "Arial:10b"
\r
1223 For backward compatibility, syntax without the colon is also
\r
1224 accepted, but font names with digits in them won't work in that case.
\r
1227 ParseFontName(char *name, MyFontParams *mfp)
\r
1230 if (name == NULL) return;
\r
1232 q = strchr(p, ':');
\r
1234 if (q - p >= sizeof(mfp->faceName))
\r
1235 ExitArgError(_("Font name too long:"), name, TRUE);
\r
1236 memcpy(mfp->faceName, p, q - p);
\r
1237 mfp->faceName[q - p] = NULLCHAR;
\r
1240 q = mfp->faceName;
\r
1241 while (*p && !isdigit(*p)) {
\r
1243 if (q - mfp->faceName >= sizeof(mfp->faceName))
\r
1244 ExitArgError(_("Font name too long:"), name, TRUE);
\r
1246 while (q > mfp->faceName && q[-1] == ' ') q--;
\r
1249 if (!*p) ExitArgError(_("Font point size missing:"), name, TRUE);
\r
1250 mfp->pointSize = (float) atof(p);
\r
1251 mfp->bold = (strchr(p, 'b') != NULL);
\r
1252 mfp->italic = (strchr(p, 'i') != NULL);
\r
1253 mfp->underline = (strchr(p, 'u') != NULL);
\r
1254 mfp->strikeout = (strchr(p, 's') != NULL);
\r
1255 mfp->charset = DEFAULT_CHARSET;
\r
1256 q = strchr(p, 'c');
\r
1258 mfp->charset = (BYTE) atoi(q+1);
\r
1262 ParseFont(char *name, int number)
\r
1263 { // wrapper to shield back-end from 'font'
\r
1264 ParseFontName(name, &font[boardSize][number]->mfp);
\r
1269 { // in WB we have a 2D array of fonts; this initializes their description
\r
1271 /* Point font array elements to structures and
\r
1272 parse default font names */
\r
1273 for (i=0; i<NUM_FONTS; i++) {
\r
1274 for (j=0; j<NUM_SIZES; j++) {
\r
1275 font[j][i] = &fontRec[j][i];
\r
1276 ParseFontName(font[j][i]->def, &font[j][i]->mfp);
\r
1283 { // here we create the actual fonts from the selected descriptions
\r
1285 for (i=0; i<NUM_FONTS; i++) {
\r
1286 for (j=0; j<NUM_SIZES; j++) {
\r
1287 CreateFontInMF(font[j][i]);
\r
1291 /* Color name parser.
\r
1292 X version accepts X color names, but this one
\r
1293 handles only the #rrggbb form (hex) or rrr,ggg,bbb (decimal) */
\r
1295 ParseColorName(char *name)
\r
1297 int red, green, blue, count;
\r
1298 char buf[MSG_SIZ];
\r
1300 count = sscanf(name, "#%2x%2x%2x", &red, &green, &blue);
\r
1302 count = sscanf(name, "%3d%*[^0-9]%3d%*[^0-9]%3d",
\r
1303 &red, &green, &blue);
\r
1306 snprintf(buf, MSG_SIZ, _("Can't parse color name %s"), name);
\r
1307 DisplayError(buf, 0);
\r
1308 return RGB(0, 0, 0);
\r
1310 return PALETTERGB(red, green, blue);
\r
1314 ParseColor(int n, char *name)
\r
1315 { // for WinBoard the color is an int, which needs to be derived from the string
\r
1316 if(colorVariable[n]) *(int*)colorVariable[n] = ParseColorName(name);
\r
1320 ParseAttribs(COLORREF *color, int *effects, char* argValue)
\r
1322 char *e = argValue;
\r
1326 if (*e == 'b') eff |= CFE_BOLD;
\r
1327 else if (*e == 'i') eff |= CFE_ITALIC;
\r
1328 else if (*e == 'u') eff |= CFE_UNDERLINE;
\r
1329 else if (*e == 's') eff |= CFE_STRIKEOUT;
\r
1330 else if (*e == '#' || isdigit(*e)) break;
\r
1334 *color = ParseColorName(e);
\r
1338 ParseTextAttribs(ColorClass cc, char *s)
\r
1339 { // [HGM] front-end wrapper that does the platform-dependent call
\r
1340 // for XBoard we would set (&appData.colorShout)[cc] = strdup(s);
\r
1341 ParseAttribs(&textAttribs[cc].color, &textAttribs[cc].effects, s);
\r
1345 ParseBoardSize(void *addr, char *name)
\r
1346 { // [HGM] rewritten with return-value ptr to shield back-end from BoardSize
\r
1347 BoardSize bs = SizeTiny;
\r
1348 while (sizeInfo[bs].name != NULL) {
\r
1349 if (StrCaseCmp(name, sizeInfo[bs].name) == 0) {
\r
1350 *(BoardSize *)addr = bs;
\r
1355 ExitArgError(_("Unrecognized board size value"), name, TRUE);
\r
1360 { // [HGM] import name from appData first
\r
1363 for (cc = (ColorClass)0; cc < ColorNormal; cc++) {
\r
1364 textAttribs[cc].sound.name = strdup((&appData.soundShout)[cc]);
\r
1365 textAttribs[cc].sound.data = NULL;
\r
1366 MyLoadSound(&textAttribs[cc].sound);
\r
1368 for (cc = ColorNormal; cc < NColorClasses; cc++) {
\r
1369 textAttribs[cc].sound.name = strdup("");
\r
1370 textAttribs[cc].sound.data = NULL;
\r
1372 for (sc = (SoundClass)0; sc < NSoundClasses; sc++) {
\r
1373 sounds[sc].name = strdup((&appData.soundMove)[sc]);
\r
1374 sounds[sc].data = NULL;
\r
1375 MyLoadSound(&sounds[sc]);
\r
1380 SetCommPortDefaults()
\r
1382 memset(&dcb, 0, sizeof(DCB)); // required by VS 2002 +
\r
1383 dcb.DCBlength = sizeof(DCB);
\r
1384 dcb.BaudRate = 9600;
\r
1385 dcb.fBinary = TRUE;
\r
1386 dcb.fParity = FALSE;
\r
1387 dcb.fOutxCtsFlow = FALSE;
\r
1388 dcb.fOutxDsrFlow = FALSE;
\r
1389 dcb.fDtrControl = DTR_CONTROL_ENABLE;
\r
1390 dcb.fDsrSensitivity = FALSE;
\r
1391 dcb.fTXContinueOnXoff = TRUE;
\r
1392 dcb.fOutX = FALSE;
\r
1394 dcb.fNull = FALSE;
\r
1395 dcb.fRtsControl = RTS_CONTROL_ENABLE;
\r
1396 dcb.fAbortOnError = FALSE;
\r
1398 dcb.Parity = SPACEPARITY;
\r
1399 dcb.StopBits = ONESTOPBIT;
\r
1402 // [HGM] args: these three cases taken out to stay in front-end
\r
1404 SaveFontArg(FILE *f, ArgDescriptor *ad)
\r
1405 { // in WinBoard every board size has its own font, and the "argLoc" identifies the table,
\r
1406 // while the curent board size determines the element. This system should be ported to XBoard.
\r
1407 // What the table contains pointers to, and how to print the font description, remains platform-dependent
\r
1409 for (bs=0; bs<NUM_SIZES; bs++) {
\r
1410 MyFontParams *mfp = &font[bs][(int) ad->argLoc]->mfp;
\r
1411 fprintf(f, "/size=%s ", sizeInfo[bs].name);
\r
1412 fprintf(f, "/%s=\"%s:%g%s%s%s%s%sc%d\"\n",
\r
1413 ad->argName, mfp->faceName, mfp->pointSize,
\r
1414 mfp->bold || mfp->italic || mfp->underline || mfp->strikeout ? " " : "",
\r
1415 mfp->bold ? "b" : "",
\r
1416 mfp->italic ? "i" : "",
\r
1417 mfp->underline ? "u" : "",
\r
1418 mfp->strikeout ? "s" : "",
\r
1419 (int)mfp->charset);
\r
1425 { // [HGM] copy the names from the internal WB variables to appData
\r
1428 for (cc = (ColorClass)0; cc < ColorNormal; cc++)
\r
1429 (&appData.soundShout)[cc] = textAttribs[cc].sound.name;
\r
1430 for (sc = (SoundClass)0; sc < NSoundClasses; sc++)
\r
1431 (&appData.soundMove)[sc] = sounds[sc].name;
\r
1435 SaveAttribsArg(FILE *f, ArgDescriptor *ad)
\r
1436 { // here the "argLoc" defines a table index. It could have contained the 'ta' pointer itself, though
\r
1437 MyTextAttribs* ta = &textAttribs[(ColorClass)ad->argLoc];
\r
1438 fprintf(f, "/%s=\"%s%s%s%s%s#%02lx%02lx%02lx\"\n", ad->argName,
\r
1439 (ta->effects & CFE_BOLD) ? "b" : "",
\r
1440 (ta->effects & CFE_ITALIC) ? "i" : "",
\r
1441 (ta->effects & CFE_UNDERLINE) ? "u" : "",
\r
1442 (ta->effects & CFE_STRIKEOUT) ? "s" : "",
\r
1443 (ta->effects) ? " " : "",
\r
1444 ta->color&0xff, (ta->color >> 8)&0xff, (ta->color >> 16)&0xff);
\r
1448 SaveColor(FILE *f, ArgDescriptor *ad)
\r
1449 { // in WinBoard the color is an int and has to be converted to text. In X it would be a string already?
\r
1450 COLORREF color = *(COLORREF *)colorVariable[(int)ad->argLoc];
\r
1451 fprintf(f, "/%s=#%02lx%02lx%02lx\n", ad->argName,
\r
1452 color&0xff, (color>>8)&0xff, (color>>16)&0xff);
\r
1456 SaveBoardSize(FILE *f, char *name, void *addr)
\r
1457 { // wrapper to shield back-end from BoardSize & sizeInfo
\r
1458 fprintf(f, "/%s=%s\n", name, sizeInfo[*(BoardSize *)addr].name);
\r
1462 ParseCommPortSettings(char *s)
\r
1463 { // wrapper to keep dcb from back-end
\r
1464 ParseCommSettings(s, &dcb);
\r
1469 { // wrapper to shield use of window handles from back-end (make addressible by number?)
\r
1470 GetActualPlacement(hwndMain, &wpMain);
\r
1471 GetActualPlacement(hwndConsole, &wpConsole);
\r
1472 GetActualPlacement(commentDialog, &wpComment);
\r
1473 GetActualPlacement(editTagsDialog, &wpTags);
\r
1474 GetActualPlacement(gameListDialog, &wpGameList);
\r
1475 GetActualPlacement(moveHistoryDialog, &wpMoveHistory);
\r
1476 GetActualPlacement(evalGraphDialog, &wpEvalGraph);
\r
1477 GetActualPlacement(engineOutputDialog, &wpEngineOutput);
\r
1481 PrintCommPortSettings(FILE *f, char *name)
\r
1482 { // wrapper to shield back-end from DCB
\r
1483 PrintCommSettings(f, name, &dcb);
\r
1487 MySearchPath(char *installDir, char *name, char *fullname)
\r
1489 char *dummy, buf[MSG_SIZ], *p = name, *q;
\r
1490 if(name[0]== '%') {
\r
1491 fullname[0] = 0; // [HGM] first expand any environment variables in the given name
\r
1492 while(*p == '%' && (q = strchr(p+1, '%'))) { // [HGM] recognize %*% as environment variable
\r
1493 safeStrCpy(buf, p+1, sizeof(buf)/sizeof(buf[0]) );
\r
1494 *strchr(buf, '%') = 0;
\r
1495 strcat(fullname, getenv(buf));
\r
1496 p = q+1; while(*p == '\\') { strcat(fullname, "\\"); p++; }
\r
1498 strcat(fullname, p); // after environment variables (if any), take the remainder of the given name
\r
1499 if(appData.debugMode) fprintf(debugFP, "name = '%s', expanded name = '%s'\n", name, fullname);
\r
1500 return (int) strlen(fullname);
\r
1502 return (int) SearchPath(installDir, name, NULL, MSG_SIZ, fullname, &dummy);
\r
1506 MyGetFullPathName(char *name, char *fullname)
\r
1509 return (int) GetFullPathName(name, MSG_SIZ, fullname, &dummy);
\r
1514 { // [HGM] args: allows testing if main window is realized from back-end
\r
1515 return hwndMain != NULL;
\r
1519 PopUpStartupDialog()
\r
1523 LoadLanguageFile(appData.language);
\r
1524 lpProc = MakeProcInstance((FARPROC)StartupDialog, hInst);
\r
1525 DialogBox(hInst, MAKEINTRESOURCE(DLG_Startup), NULL, (DLGPROC)lpProc);
\r
1526 FreeProcInstance(lpProc);
\r
1529 /*---------------------------------------------------------------------------*\
\r
1531 * GDI board drawing routines
\r
1533 \*---------------------------------------------------------------------------*/
\r
1535 /* [AS] Draw square using background texture */
\r
1536 static void DrawTile( int dx, int dy, int dw, int dh, HDC dst, HDC src, int mode, int sx, int sy )
\r
1541 return; /* Should never happen! */
\r
1544 SetGraphicsMode( dst, GM_ADVANCED );
\r
1551 /* X reflection */
\r
1556 x.eDx = (FLOAT) dw + dx - 1;
\r
1559 SetWorldTransform( dst, &x );
\r
1562 /* Y reflection */
\r
1568 x.eDy = (FLOAT) dh + dy - 1;
\r
1570 SetWorldTransform( dst, &x );
\r
1578 x.eDx = (FLOAT) dx;
\r
1579 x.eDy = (FLOAT) dy;
\r
1582 SetWorldTransform( dst, &x );
\r
1586 BitBlt( dst, dx, dy, dw, dh, src, sx, sy, SRCCOPY );
\r
1594 SetWorldTransform( dst, &x );
\r
1596 ModifyWorldTransform( dst, 0, MWT_IDENTITY );
\r
1599 /* [AS] [HGM] Make room for more piece types, so all pieces can be different */
\r
1601 PM_WP = (int) WhitePawn,
\r
1602 PM_WN = (int) WhiteKnight,
\r
1603 PM_WB = (int) WhiteBishop,
\r
1604 PM_WR = (int) WhiteRook,
\r
1605 PM_WQ = (int) WhiteQueen,
\r
1606 PM_WF = (int) WhiteFerz,
\r
1607 PM_WW = (int) WhiteWazir,
\r
1608 PM_WE = (int) WhiteAlfil,
\r
1609 PM_WM = (int) WhiteMan,
\r
1610 PM_WO = (int) WhiteCannon,
\r
1611 PM_WU = (int) WhiteUnicorn,
\r
1612 PM_WH = (int) WhiteNightrider,
\r
1613 PM_WA = (int) WhiteAngel,
\r
1614 PM_WC = (int) WhiteMarshall,
\r
1615 PM_WAB = (int) WhiteCardinal,
\r
1616 PM_WD = (int) WhiteDragon,
\r
1617 PM_WL = (int) WhiteLance,
\r
1618 PM_WS = (int) WhiteCobra,
\r
1619 PM_WV = (int) WhiteFalcon,
\r
1620 PM_WSG = (int) WhiteSilver,
\r
1621 PM_WG = (int) WhiteGrasshopper,
\r
1622 PM_WK = (int) WhiteKing,
\r
1623 PM_BP = (int) BlackPawn,
\r
1624 PM_BN = (int) BlackKnight,
\r
1625 PM_BB = (int) BlackBishop,
\r
1626 PM_BR = (int) BlackRook,
\r
1627 PM_BQ = (int) BlackQueen,
\r
1628 PM_BF = (int) BlackFerz,
\r
1629 PM_BW = (int) BlackWazir,
\r
1630 PM_BE = (int) BlackAlfil,
\r
1631 PM_BM = (int) BlackMan,
\r
1632 PM_BO = (int) BlackCannon,
\r
1633 PM_BU = (int) BlackUnicorn,
\r
1634 PM_BH = (int) BlackNightrider,
\r
1635 PM_BA = (int) BlackAngel,
\r
1636 PM_BC = (int) BlackMarshall,
\r
1637 PM_BG = (int) BlackGrasshopper,
\r
1638 PM_BAB = (int) BlackCardinal,
\r
1639 PM_BD = (int) BlackDragon,
\r
1640 PM_BL = (int) BlackLance,
\r
1641 PM_BS = (int) BlackCobra,
\r
1642 PM_BV = (int) BlackFalcon,
\r
1643 PM_BSG = (int) BlackSilver,
\r
1644 PM_BK = (int) BlackKing
\r
1647 static HFONT hPieceFont = NULL;
\r
1648 static HBITMAP hPieceMask[(int) EmptySquare];
\r
1649 static HBITMAP hPieceFace[(int) EmptySquare];
\r
1650 static int fontBitmapSquareSize = 0;
\r
1651 static char pieceToFontChar[(int) EmptySquare] =
\r
1652 { 'p', 'n', 'b', 'r', 'q',
\r
1653 'n', 'b', 'p', 'n', 'b', 'r', 'b', 'r', 'q', 'k',
\r
1654 'k', 'o', 'm', 'v', 't', 'w',
\r
1655 'v', 't', 'o', 'm', 'v', 't', 'v', 't', 'w', 'l',
\r
1658 extern BOOL SetCharTable( char *table, const char * map );
\r
1659 /* [HGM] moved to backend.c */
\r
1661 static void SetPieceBackground( HDC hdc, COLORREF color, int mode )
\r
1664 BYTE r1 = GetRValue( color );
\r
1665 BYTE g1 = GetGValue( color );
\r
1666 BYTE b1 = GetBValue( color );
\r
1672 /* Create a uniform background first */
\r
1673 hbrush = CreateSolidBrush( color );
\r
1674 SetRect( &rc, 0, 0, squareSize, squareSize );
\r
1675 FillRect( hdc, &rc, hbrush );
\r
1676 DeleteObject( hbrush );
\r
1679 /* Vertical gradient, good for pawn, knight and rook, less for queen and king */
\r
1680 int steps = squareSize / 2;
\r
1683 for( i=0; i<steps; i++ ) {
\r
1684 BYTE r = r1 - (r1-r2) * i / steps;
\r
1685 BYTE g = g1 - (g1-g2) * i / steps;
\r
1686 BYTE b = b1 - (b1-b2) * i / steps;
\r
1688 hbrush = CreateSolidBrush( RGB(r,g,b) );
\r
1689 SetRect( &rc, i + squareSize - steps, 0, i + squareSize - steps + 1, squareSize );
\r
1690 FillRect( hdc, &rc, hbrush );
\r
1691 DeleteObject(hbrush);
\r
1694 else if( mode == 2 ) {
\r
1695 /* Diagonal gradient, good more or less for every piece */
\r
1696 POINT triangle[3];
\r
1697 HPEN hpen = SelectObject( hdc, GetStockObject(NULL_PEN) );
\r
1698 HBRUSH hbrush_old;
\r
1699 int steps = squareSize;
\r
1702 triangle[0].x = squareSize - steps;
\r
1703 triangle[0].y = squareSize;
\r
1704 triangle[1].x = squareSize;
\r
1705 triangle[1].y = squareSize;
\r
1706 triangle[2].x = squareSize;
\r
1707 triangle[2].y = squareSize - steps;
\r
1709 for( i=0; i<steps; i++ ) {
\r
1710 BYTE r = r1 - (r1-r2) * i / steps;
\r
1711 BYTE g = g1 - (g1-g2) * i / steps;
\r
1712 BYTE b = b1 - (b1-b2) * i / steps;
\r
1714 hbrush = CreateSolidBrush( RGB(r,g,b) );
\r
1715 hbrush_old = SelectObject( hdc, hbrush );
\r
1716 Polygon( hdc, triangle, 3 );
\r
1717 SelectObject( hdc, hbrush_old );
\r
1718 DeleteObject(hbrush);
\r
1723 SelectObject( hdc, hpen );
\r
1728 [AS] The method I use to create the bitmaps it a bit tricky, but it
\r
1729 seems to work ok. The main problem here is to find the "inside" of a chess
\r
1730 piece: follow the steps as explained below.
\r
1732 static void CreatePieceMaskFromFont( HDC hdc_window, HDC hdc, int index )
\r
1736 COLORREF chroma = RGB(0xFF,0x00,0xFF);
\r
1740 int backColor = whitePieceColor;
\r
1741 int foreColor = blackPieceColor;
\r
1743 if( index < (int)BlackPawn && appData.fontBackColorWhite != appData.fontForeColorWhite ) {
\r
1744 backColor = appData.fontBackColorWhite;
\r
1745 foreColor = appData.fontForeColorWhite;
\r
1747 else if( index >= (int)BlackPawn && appData.fontBackColorBlack != appData.fontForeColorBlack ) {
\r
1748 backColor = appData.fontBackColorBlack;
\r
1749 foreColor = appData.fontForeColorBlack;
\r
1753 hbm = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );
\r
1755 hbm_old = SelectObject( hdc, hbm );
\r
1759 rc.right = squareSize;
\r
1760 rc.bottom = squareSize;
\r
1762 /* Step 1: background is now black */
\r
1763 FillRect( hdc, &rc, GetStockObject(BLACK_BRUSH) );
\r
1765 GetTextExtentPoint32( hdc, &pieceToFontChar[index], 1, &sz );
\r
1767 pt.x = (squareSize - sz.cx) / 2;
\r
1768 pt.y = (squareSize - sz.cy) / 2;
\r
1770 SetBkMode( hdc, TRANSPARENT );
\r
1771 SetTextColor( hdc, chroma );
\r
1772 /* Step 2: the piece has been drawn in purple, there are now black and purple in this bitmap */
\r
1773 TextOut( hdc, pt.x, pt.y, &pieceToFontChar[appData.allWhite && index >= (int)BlackPawn ? index - (int)BlackPawn : index], 1 );
\r
1775 SelectObject( hdc, GetStockObject(WHITE_BRUSH) );
\r
1776 /* Step 3: the area outside the piece is filled with white */
\r
1777 // FloodFill( hdc, 0, 0, chroma );
\r
1778 ExtFloodFill( hdc, 0, 0, 0, FLOODFILLSURFACE );
\r
1779 ExtFloodFill( hdc, 0, squareSize-1, 0, FLOODFILLSURFACE ); // [HGM] fill from all 4 corners, for if piece too big
\r
1780 ExtFloodFill( hdc, squareSize-1, 0, 0, FLOODFILLSURFACE );
\r
1781 ExtFloodFill( hdc, squareSize-1, squareSize-1, 0, FLOODFILLSURFACE );
\r
1782 SelectObject( hdc, GetStockObject(BLACK_BRUSH) );
\r
1784 Step 4: this is the tricky part, the area inside the piece is filled with black,
\r
1785 but if the start point is not inside the piece we're lost!
\r
1786 There should be a better way to do this... if we could create a region or path
\r
1787 from the fill operation we would be fine for example.
\r
1789 // FloodFill( hdc, squareSize / 2, squareSize / 2, RGB(0xFF,0xFF,0xFF) );
\r
1790 ExtFloodFill( hdc, squareSize / 2, squareSize / 2, RGB(0xFF,0xFF,0xFF), FLOODFILLBORDER );
\r
1792 { /* [HGM] shave off edges of mask, in an attempt to correct for the fact that FloodFill does not work correctly under Win XP */
\r
1793 HDC dc2 = CreateCompatibleDC( hdc_window );
\r
1794 HBITMAP bm2 = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );
\r
1796 SelectObject( dc2, bm2 );
\r
1797 BitBlt( dc2, 0, 0, squareSize, squareSize, hdc, 0, 0, SRCCOPY ); // make copy
\r
1798 BitBlt( hdc, 0, 1, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );
\r
1799 BitBlt( hdc, 2, 1, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );
\r
1800 BitBlt( hdc, 1, 0, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );
\r
1801 BitBlt( hdc, 1, 2, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );
\r
1804 DeleteObject( bm2 );
\r
1807 SetTextColor( hdc, 0 );
\r
1809 Step 5: some fonts have "disconnected" areas that are skipped by the fill:
\r
1810 draw the piece again in black for safety.
\r
1812 TextOut( hdc, pt.x, pt.y, &pieceToFontChar[appData.allWhite && index >= (int)BlackPawn ? index - (int)BlackPawn : index], 1 );
\r
1814 SelectObject( hdc, hbm_old );
\r
1816 if( hPieceMask[index] != NULL ) {
\r
1817 DeleteObject( hPieceMask[index] );
\r
1820 hPieceMask[index] = hbm;
\r
1823 hbm = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );
\r
1825 SelectObject( hdc, hbm );
\r
1828 HDC dc1 = CreateCompatibleDC( hdc_window );
\r
1829 HDC dc2 = CreateCompatibleDC( hdc_window );
\r
1830 HBITMAP bm2 = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );
\r
1832 SelectObject( dc1, hPieceMask[index] );
\r
1833 SelectObject( dc2, bm2 );
\r
1834 FillRect( dc2, &rc, GetStockObject(WHITE_BRUSH) );
\r
1835 BitBlt( dc2, 0, 0, squareSize, squareSize, dc1, 0, 0, SRCINVERT );
\r
1838 Now dc2 contains the inverse of the piece mask, i.e. a mask that preserves
\r
1839 the piece background and deletes (makes transparent) the rest.
\r
1840 Thanks to that mask, we are free to paint the background with the greates
\r
1841 freedom, as we'll be able to mask off the unwanted parts when finished.
\r
1842 We use this, to make gradients and give the pieces a "roundish" look.
\r
1844 SetPieceBackground( hdc, backColor, 2 );
\r
1845 BitBlt( hdc, 0, 0, squareSize, squareSize, dc2, 0, 0, SRCAND );
\r
1849 DeleteObject( bm2 );
\r
1852 SetTextColor( hdc, foreColor );
\r
1853 TextOut( hdc, pt.x, pt.y, &pieceToFontChar[appData.allWhite && index >= (int)BlackPawn ? index - (int)BlackPawn : index], 1 );
\r
1855 SelectObject( hdc, hbm_old );
\r
1857 if( hPieceFace[index] != NULL ) {
\r
1858 DeleteObject( hPieceFace[index] );
\r
1861 hPieceFace[index] = hbm;
\r
1864 static int TranslatePieceToFontPiece( int piece )
\r
1894 case BlackMarshall:
\r
1898 case BlackNightrider:
\r
1904 case BlackUnicorn:
\r
1908 case BlackGrasshopper:
\r
1920 case BlackCardinal:
\r
1927 case WhiteMarshall:
\r
1931 case WhiteNightrider:
\r
1937 case WhiteUnicorn:
\r
1941 case WhiteGrasshopper:
\r
1953 case WhiteCardinal:
\r
1962 void CreatePiecesFromFont()
\r
1965 HDC hdc_window = NULL;
\r
1971 if( fontBitmapSquareSize < 0 ) {
\r
1972 /* Something went seriously wrong in the past: do not try to recreate fonts! */
\r
1976 if( appData.renderPiecesWithFont == NULL || appData.renderPiecesWithFont[0] == NULLCHAR || appData.renderPiecesWithFont[0] == '*' ) {
\r
1977 fontBitmapSquareSize = -1;
\r
1981 if( fontBitmapSquareSize != squareSize ) {
\r
1982 hdc_window = GetDC( hwndMain );
\r
1983 hdc = CreateCompatibleDC( hdc_window );
\r
1985 if( hPieceFont != NULL ) {
\r
1986 DeleteObject( hPieceFont );
\r
1989 for( i=0; i<=(int)BlackKing; i++ ) {
\r
1990 hPieceMask[i] = NULL;
\r
1991 hPieceFace[i] = NULL;
\r
1997 if( appData.fontPieceSize >= 50 && appData.fontPieceSize <= 150 ) {
\r
1998 fontHeight = appData.fontPieceSize;
\r
2001 fontHeight = (fontHeight * squareSize) / 100;
\r
2003 lf.lfHeight = -MulDiv( fontHeight, GetDeviceCaps(hdc, LOGPIXELSY), 72 );
\r
2005 lf.lfEscapement = 0;
\r
2006 lf.lfOrientation = 0;
\r
2007 lf.lfWeight = FW_NORMAL;
\r
2009 lf.lfUnderline = 0;
\r
2010 lf.lfStrikeOut = 0;
\r
2011 lf.lfCharSet = DEFAULT_CHARSET;
\r
2012 lf.lfOutPrecision = OUT_DEFAULT_PRECIS;
\r
2013 lf.lfClipPrecision = CLIP_DEFAULT_PRECIS;
\r
2014 lf.lfQuality = PROOF_QUALITY;
\r
2015 lf.lfPitchAndFamily = DEFAULT_PITCH | FF_DONTCARE;
\r
2016 strncpy( lf.lfFaceName, appData.renderPiecesWithFont, sizeof(lf.lfFaceName) );
\r
2017 lf.lfFaceName[ sizeof(lf.lfFaceName) - 1 ] = '\0';
\r
2019 hPieceFont = CreateFontIndirect( &lf );
\r
2021 if( hPieceFont == NULL ) {
\r
2022 fontBitmapSquareSize = -2;
\r
2025 /* Setup font-to-piece character table */
\r
2026 if( ! SetCharTable(pieceToFontChar, appData.fontToPieceTable) ) {
\r
2027 /* No (or wrong) global settings, try to detect the font */
\r
2028 if( strstr(lf.lfFaceName,"Alpha") != NULL ) {
\r
2030 SetCharTable(pieceToFontChar, "phbrqkojntwl");
\r
2032 else if( strstr(lf.lfFaceName,"DiagramTT") != NULL ) {
\r
2033 /* DiagramTT* family */
\r
2034 SetCharTable(pieceToFontChar, "PNLRQKpnlrqk");
\r
2036 else if( strstr(lf.lfFaceName,"WinboardF") != NULL ) {
\r
2037 /* Fairy symbols */
\r
2038 SetCharTable(pieceToFontChar, "PNBRQFEACWMOHIJGDVSLUKpnbrqfeacwmohijgdvsluk");
\r
2040 else if( strstr(lf.lfFaceName,"GC2004D") != NULL ) {
\r
2041 /* Good Companion (Some characters get warped as literal :-( */
\r
2042 char s[] = "1cmWG0??S??oYI23wgQU";
\r
2043 s[0]=0xB9; s[1]=0xA9; s[6]=0xB1; s[11]=0xBB; s[12]=0xAB; s[17]=0xB3;
\r
2044 SetCharTable(pieceToFontChar, s);
\r
2047 /* Cases, Condal, Leipzig, Lucena, Marroquin, Merida, Usual */
\r
2048 SetCharTable(pieceToFontChar, "pnbrqkomvtwl");
\r
2052 /* Create bitmaps */
\r
2053 hfont_old = SelectObject( hdc, hPieceFont );
\r
2054 for(i=(int)WhitePawn; i<(int)EmptySquare; i++) /* [HGM] made a loop for this */
\r
2055 if(PieceToChar((ChessSquare)i) != '.') /* skip unused pieces */
\r
2056 CreatePieceMaskFromFont( hdc_window, hdc, i );
\r
2058 SelectObject( hdc, hfont_old );
\r
2060 fontBitmapSquareSize = squareSize;
\r
2064 if( hdc != NULL ) {
\r
2068 if( hdc_window != NULL ) {
\r
2069 ReleaseDC( hwndMain, hdc_window );
\r
2074 DoLoadBitmap(HINSTANCE hinst, char *piece, int squareSize, char *suffix)
\r
2078 snprintf(name, sizeof(name)/sizeof(name[0]), "%s%d%s", piece, squareSize, suffix);
\r
2079 if (gameInfo.event &&
\r
2080 strcmp(gameInfo.event, "Easter Egg Hunt") == 0 &&
\r
2081 strcmp(name, "k80s") == 0) {
\r
2082 safeStrCpy(name, "tim", sizeof(name)/sizeof(name[0]) );
\r
2084 return LoadBitmap(hinst, name);
\r
2088 /* Insert a color into the program's logical palette
\r
2089 structure. This code assumes the given color is
\r
2090 the result of the RGB or PALETTERGB macro, and it
\r
2091 knows how those macros work (which is documented).
\r
2094 InsertInPalette(COLORREF color)
\r
2096 LPPALETTEENTRY pe = &(pLogPal->palPalEntry[pLogPal->palNumEntries]);
\r
2098 if (pLogPal->palNumEntries++ >= PALETTESIZE) {
\r
2099 DisplayFatalError(_("Too many colors"), 0, 1);
\r
2100 pLogPal->palNumEntries--;
\r
2104 pe->peFlags = (char) 0;
\r
2105 pe->peRed = (char) (0xFF & color);
\r
2106 pe->peGreen = (char) (0xFF & (color >> 8));
\r
2107 pe->peBlue = (char) (0xFF & (color >> 16));
\r
2113 InitDrawingColors()
\r
2115 if (pLogPal == NULL) {
\r
2116 /* Allocate enough memory for a logical palette with
\r
2117 * PALETTESIZE entries and set the size and version fields
\r
2118 * of the logical palette structure.
\r
2120 pLogPal = (NPLOGPALETTE)
\r
2121 LocalAlloc(LMEM_FIXED, (sizeof(LOGPALETTE) +
\r
2122 (sizeof(PALETTEENTRY) * (PALETTESIZE))));
\r
2123 pLogPal->palVersion = 0x300;
\r
2125 pLogPal->palNumEntries = 0;
\r
2127 InsertInPalette(lightSquareColor);
\r
2128 InsertInPalette(darkSquareColor);
\r
2129 InsertInPalette(whitePieceColor);
\r
2130 InsertInPalette(blackPieceColor);
\r
2131 InsertInPalette(highlightSquareColor);
\r
2132 InsertInPalette(premoveHighlightColor);
\r
2134 /* create a logical color palette according the information
\r
2135 * in the LOGPALETTE structure.
\r
2137 hPal = CreatePalette((LPLOGPALETTE) pLogPal);
\r
2139 lightSquareBrush = CreateSolidBrush(lightSquareColor);
\r
2140 blackSquareBrush = CreateSolidBrush(blackPieceColor);
\r
2141 darkSquareBrush = CreateSolidBrush(darkSquareColor);
\r
2142 whitePieceBrush = CreateSolidBrush(whitePieceColor);
\r
2143 blackPieceBrush = CreateSolidBrush(blackPieceColor);
\r
2144 iconBkgndBrush = CreateSolidBrush(GetSysColor(COLOR_BACKGROUND));
\r
2145 explodeBrush = CreateSolidBrush(highlightSquareColor); // [HGM] atomic
\r
2146 markerBrush = CreateSolidBrush(premoveHighlightColor); // [HGM] markers
\r
2147 /* [AS] Force rendering of the font-based pieces */
\r
2148 if( fontBitmapSquareSize > 0 ) {
\r
2149 fontBitmapSquareSize = 0;
\r
2155 BoardWidth(int boardSize, int n)
\r
2156 { /* [HGM] argument n added to allow different width and height */
\r
2157 int lineGap = sizeInfo[boardSize].lineGap;
\r
2159 if( appData.overrideLineGap >= 0 && appData.overrideLineGap <= 5 ) {
\r
2160 lineGap = appData.overrideLineGap;
\r
2163 return (n + 1) * lineGap +
\r
2164 n * sizeInfo[boardSize].squareSize;
\r
2167 /* Respond to board resize by dragging edge */
\r
2169 ResizeBoard(int newSizeX, int newSizeY, int flags)
\r
2171 BoardSize newSize = NUM_SIZES - 1;
\r
2172 static int recurse = 0;
\r
2173 if (IsIconic(hwndMain)) return;
\r
2174 if (recurse > 0) return;
\r
2176 while (newSize > 0) {
\r
2177 InitDrawingSizes(newSize+1000, 0); // [HGM] kludge to update sizeInfo without visible effects
\r
2178 if(newSizeX >= sizeInfo[newSize].cliWidth &&
\r
2179 newSizeY >= sizeInfo[newSize].cliHeight) break;
\r
2182 boardSize = newSize;
\r
2183 InitDrawingSizes(boardSize, flags);
\r
2188 extern Boolean twoBoards, partnerUp; // [HGM] dual
\r
2191 InitDrawingSizes(BoardSize boardSize, int flags)
\r
2193 int i, boardWidth, boardHeight; /* [HGM] height treated separately */
\r
2194 ChessSquare piece;
\r
2195 static int oldBoardSize = -1, oldTinyLayout = 0;
\r
2197 SIZE clockSize, messageSize;
\r
2199 char buf[MSG_SIZ];
\r
2201 HMENU hmenu = GetMenu(hwndMain);
\r
2202 RECT crect, wrect, oldRect;
\r
2204 LOGBRUSH logbrush;
\r
2206 int suppressVisibleEffects = 0; // [HGM] kludge to request updating sizeInfo only
\r
2207 if((int)boardSize >= 1000 ) { boardSize -= 1000; suppressVisibleEffects = 1; }
\r
2209 /* [HGM] call with -2 uses old size (for if nr of files, ranks changes) */
\r
2210 if(boardSize == (BoardSize)(-2) ) boardSize = oldBoardSize;
\r
2212 oldRect.left = wpMain.x; //[HGM] placement: remember previous window params
\r
2213 oldRect.top = wpMain.y;
\r
2214 oldRect.right = wpMain.x + wpMain.width;
\r
2215 oldRect.bottom = wpMain.y + wpMain.height;
\r
2217 tinyLayout = sizeInfo[boardSize].tinyLayout;
\r
2218 smallLayout = sizeInfo[boardSize].smallLayout;
\r
2219 squareSize = sizeInfo[boardSize].squareSize;
\r
2220 lineGap = sizeInfo[boardSize].lineGap;
\r
2221 minorSize = 0; /* [HGM] Kludge to see if demagnified pieces need to be shifted */
\r
2223 if( appData.overrideLineGap >= 0 && appData.overrideLineGap <= 5 ) {
\r
2224 lineGap = appData.overrideLineGap;
\r
2227 if (tinyLayout != oldTinyLayout) {
\r
2228 long style = GetWindowLongPtr(hwndMain, GWL_STYLE);
\r
2230 style &= ~WS_SYSMENU;
\r
2231 InsertMenu(hmenu, IDM_Exit, MF_BYCOMMAND, IDM_Minimize,
\r
2232 "&Minimize\tCtrl+F4");
\r
2234 style |= WS_SYSMENU;
\r
2235 RemoveMenu(hmenu, IDM_Minimize, MF_BYCOMMAND);
\r
2237 SetWindowLongPtr(hwndMain, GWL_STYLE, style);
\r
2239 for (i=0; menuBarText[tinyLayout][i]; i++) {
\r
2240 ModifyMenu(hmenu, i, MF_STRING|MF_BYPOSITION|MF_POPUP,
\r
2241 (UINT)GetSubMenu(hmenu, i), T_(menuBarText[tinyLayout][i]));
\r
2243 DrawMenuBar(hwndMain);
\r
2246 boardWidth = BoardWidth(boardSize, BOARD_WIDTH);
\r
2247 boardHeight = BoardWidth(boardSize, BOARD_HEIGHT);
\r
2249 /* Get text area sizes */
\r
2250 hdc = GetDC(hwndMain);
\r
2251 if (appData.clockMode) {
\r
2252 snprintf(buf, MSG_SIZ, _("White: %s"), TimeString(23*60*60*1000L));
\r
2254 snprintf(buf, MSG_SIZ, _("White"));
\r
2256 oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);
\r
2257 GetTextExtentPoint(hdc, buf, strlen(buf), &clockSize);
\r
2258 SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);
\r
2259 str = _("We only care about the height here");
\r
2260 GetTextExtentPoint(hdc, str, strlen(str), &messageSize);
\r
2261 SelectObject(hdc, oldFont);
\r
2262 ReleaseDC(hwndMain, hdc);
\r
2264 /* Compute where everything goes */
\r
2265 if((first.programLogo || second.programLogo) && !tinyLayout) {
\r
2266 /* [HGM] logo: if either logo is on, reserve space for it */
\r
2267 logoHeight = 2*clockSize.cy;
\r
2268 leftLogoRect.left = OUTER_MARGIN;
\r
2269 leftLogoRect.right = leftLogoRect.left + 4*clockSize.cy;
\r
2270 leftLogoRect.top = OUTER_MARGIN;
\r
2271 leftLogoRect.bottom = OUTER_MARGIN + logoHeight;
\r
2273 rightLogoRect.right = OUTER_MARGIN + boardWidth;
\r
2274 rightLogoRect.left = rightLogoRect.right - 4*clockSize.cy;
\r
2275 rightLogoRect.top = OUTER_MARGIN;
\r
2276 rightLogoRect.bottom = OUTER_MARGIN + logoHeight;
\r
2279 whiteRect.left = leftLogoRect.right;
\r
2280 whiteRect.right = OUTER_MARGIN + boardWidth/2 - INNER_MARGIN/2;
\r
2281 whiteRect.top = OUTER_MARGIN;
\r
2282 whiteRect.bottom = whiteRect.top + logoHeight;
\r
2284 blackRect.right = rightLogoRect.left;
\r
2285 blackRect.left = whiteRect.right + INNER_MARGIN;
\r
2286 blackRect.top = whiteRect.top;
\r
2287 blackRect.bottom = whiteRect.bottom;
\r
2289 whiteRect.left = OUTER_MARGIN;
\r
2290 whiteRect.right = whiteRect.left + boardWidth/2 - INNER_MARGIN/2;
\r
2291 whiteRect.top = OUTER_MARGIN;
\r
2292 whiteRect.bottom = whiteRect.top + clockSize.cy;
\r
2294 blackRect.left = whiteRect.right + INNER_MARGIN;
\r
2295 blackRect.right = blackRect.left + boardWidth/2 - 1;
\r
2296 blackRect.top = whiteRect.top;
\r
2297 blackRect.bottom = whiteRect.bottom;
\r
2299 logoHeight = 0; // [HGM] logo: suppress logo after change to tiny layout!
\r
2302 messageRect.left = OUTER_MARGIN + MESSAGE_LINE_LEFTMARGIN;
\r
2303 if (appData.showButtonBar) {
\r
2304 messageRect.right = OUTER_MARGIN + boardWidth // [HGM] logo: expressed independent of clock placement
\r
2305 - N_BUTTONS*BUTTON_WIDTH - MESSAGE_LINE_LEFTMARGIN;
\r
2307 messageRect.right = OUTER_MARGIN + boardWidth;
\r
2309 messageRect.top = whiteRect.bottom + INNER_MARGIN;
\r
2310 messageRect.bottom = messageRect.top + messageSize.cy;
\r
2312 boardRect.left = OUTER_MARGIN;
\r
2313 boardRect.right = boardRect.left + boardWidth;
\r
2314 boardRect.top = messageRect.bottom + INNER_MARGIN;
\r
2315 boardRect.bottom = boardRect.top + boardHeight;
\r
2317 sizeInfo[boardSize].cliWidth = boardRect.right + OUTER_MARGIN;
\r
2318 sizeInfo[boardSize].cliHeight = boardRect.bottom + OUTER_MARGIN;
\r
2319 oldBoardSize = boardSize;
\r
2320 oldTinyLayout = tinyLayout;
\r
2321 winW = 2 * GetSystemMetrics(SM_CXFRAME) + boardRect.right + OUTER_MARGIN;
\r
2322 winH = 2 * GetSystemMetrics(SM_CYFRAME) + GetSystemMetrics(SM_CYMENU) +
\r
2323 GetSystemMetrics(SM_CYCAPTION) + boardRect.bottom + OUTER_MARGIN;
\r
2324 winW *= 1 + twoBoards;
\r
2325 if(suppressVisibleEffects) return; // [HGM] when called for filling sizeInfo only
\r
2326 wpMain.width = winW; // [HGM] placement: set through temporary which can used by initial sizing choice
\r
2327 wpMain.height = winH; // without disturbing window attachments
\r
2328 GetWindowRect(hwndMain, &wrect);
\r
2329 SetWindowPos(hwndMain, NULL, 0, 0, wpMain.width, wpMain.height,
\r
2330 SWP_NOCOPYBITS|SWP_NOZORDER|SWP_NOMOVE);
\r
2332 // [HGM] placement: let attached windows follow size change.
\r
2333 ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, moveHistoryDialog, &wpMoveHistory );
\r
2334 ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, evalGraphDialog, &wpEvalGraph );
\r
2335 ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, engineOutputDialog, &wpEngineOutput );
\r
2336 ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, gameListDialog, &wpGameList );
\r
2337 ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, hwndConsole, &wpConsole );
\r
2339 /* compensate if menu bar wrapped */
\r
2340 GetClientRect(hwndMain, &crect);
\r
2341 offby = boardRect.bottom + OUTER_MARGIN - crect.bottom;
\r
2342 wpMain.height += offby;
\r
2344 case WMSZ_TOPLEFT:
\r
2345 SetWindowPos(hwndMain, NULL,
\r
2346 wrect.right - wpMain.width, wrect.bottom - wpMain.height,
\r
2347 wpMain.width, wpMain.height, SWP_NOCOPYBITS|SWP_NOZORDER);
\r
2350 case WMSZ_TOPRIGHT:
\r
2352 SetWindowPos(hwndMain, NULL,
\r
2353 wrect.left, wrect.bottom - wpMain.height,
\r
2354 wpMain.width, wpMain.height, SWP_NOCOPYBITS|SWP_NOZORDER);
\r
2357 case WMSZ_BOTTOMLEFT:
\r
2359 SetWindowPos(hwndMain, NULL,
\r
2360 wrect.right - wpMain.width, wrect.top,
\r
2361 wpMain.width, wpMain.height, SWP_NOCOPYBITS|SWP_NOZORDER);
\r
2364 case WMSZ_BOTTOMRIGHT:
\r
2368 SetWindowPos(hwndMain, NULL, 0, 0, wpMain.width, wpMain.height,
\r
2369 SWP_NOCOPYBITS|SWP_NOZORDER|SWP_NOMOVE);
\r
2374 for (i = 0; i < N_BUTTONS; i++) {
\r
2375 if (buttonDesc[i].hwnd != NULL) {
\r
2376 DestroyWindow(buttonDesc[i].hwnd);
\r
2377 buttonDesc[i].hwnd = NULL;
\r
2379 if (appData.showButtonBar) {
\r
2380 buttonDesc[i].hwnd =
\r
2381 CreateWindow("BUTTON", buttonDesc[i].label,
\r
2382 WS_VISIBLE | WS_CHILD | BS_PUSHBUTTON,
\r
2383 boardRect.right - BUTTON_WIDTH*(N_BUTTONS-i),
\r
2384 messageRect.top, BUTTON_WIDTH, messageSize.cy, hwndMain,
\r
2385 (HMENU) buttonDesc[i].id,
\r
2386 (HINSTANCE) GetWindowLongPtr(hwndMain, GWLP_HINSTANCE), NULL);
\r
2388 SendMessage(buttonDesc[i].hwnd, WM_SETFONT,
\r
2389 (WPARAM)font[boardSize][MESSAGE_FONT]->hf,
\r
2390 MAKELPARAM(FALSE, 0));
\r
2392 if (buttonDesc[i].id == IDM_Pause)
\r
2393 hwndPause = buttonDesc[i].hwnd;
\r
2394 buttonDesc[i].wndproc = (WNDPROC)
\r
2395 SetWindowLongPtr(buttonDesc[i].hwnd, GWLP_WNDPROC, (LONG_PTR) ButtonProc);
\r
2398 if (gridPen != NULL) DeleteObject(gridPen);
\r
2399 if (highlightPen != NULL) DeleteObject(highlightPen);
\r
2400 if (premovePen != NULL) DeleteObject(premovePen);
\r
2401 if (lineGap != 0) {
\r
2402 logbrush.lbStyle = BS_SOLID;
\r
2403 logbrush.lbColor = RGB(0, 0, 0); /* grid pen color = black */
\r
2405 ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,
\r
2406 lineGap, &logbrush, 0, NULL);
\r
2407 logbrush.lbColor = highlightSquareColor;
\r
2409 ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,
\r
2410 lineGap, &logbrush, 0, NULL);
\r
2412 logbrush.lbColor = premoveHighlightColor;
\r
2414 ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,
\r
2415 lineGap, &logbrush, 0, NULL);
\r
2417 /* [HGM] Loop had to be split in part for vert. and hor. lines */
\r
2418 for (i = 0; i < BOARD_HEIGHT + 1; i++) {
\r
2419 gridEndpoints[i*2].x = boardRect.left + lineGap / 2;
\r
2420 gridEndpoints[i*2].y = gridEndpoints[i*2 + 1].y =
\r
2421 boardRect.top + lineGap / 2 + (i * (squareSize + lineGap));
\r
2422 gridEndpoints[i*2 + 1].x = boardRect.left + lineGap / 2 +
\r
2423 BOARD_WIDTH * (squareSize + lineGap);
\r
2424 gridVertexCounts[i*2] = gridVertexCounts[i*2 + 1] = 2;
\r
2426 for (i = 0; i < BOARD_WIDTH + 1; i++) {
\r
2427 gridEndpoints[i*2 + BOARD_HEIGHT*2 + 2].y = boardRect.top + lineGap / 2;
\r
2428 gridEndpoints[i*2 + BOARD_HEIGHT*2 + 2].x =
\r
2429 gridEndpoints[i*2 + 1 + BOARD_HEIGHT*2 + 2].x = boardRect.left +
\r
2430 lineGap / 2 + (i * (squareSize + lineGap));
\r
2431 gridEndpoints[i*2 + 1 + BOARD_HEIGHT*2 + 2].y =
\r
2432 boardRect.top + BOARD_HEIGHT * (squareSize + lineGap);
\r
2433 gridVertexCounts[i*2] = gridVertexCounts[i*2 + 1] = 2;
\r
2437 /* [HGM] Licensing requirement */
\r
2439 if(gameInfo.variant == VariantGothic) GothicPopUp( GOTHIC, VariantGothic); else
\r
2442 if(gameInfo.variant == VariantFalcon) GothicPopUp( FALCON, VariantFalcon); else
\r
2444 GothicPopUp( "", VariantNormal);
\r
2447 /* if (boardSize == oldBoardSize) return; [HGM] variant might have changed */
\r
2449 /* Load piece bitmaps for this board size */
\r
2450 for (i=0; i<=2; i++) {
\r
2451 for (piece = WhitePawn;
\r
2452 (int) piece < (int) BlackPawn;
\r
2453 piece = (ChessSquare) ((int) piece + 1)) {
\r
2454 if (pieceBitmap[i][piece] != NULL)
\r
2455 DeleteObject(pieceBitmap[i][piece]);
\r
2459 fontBitmapSquareSize = 0; /* [HGM] render: make sure pieces will be recreated, as we might need others now */
\r
2460 // Orthodox Chess pieces
\r
2461 pieceBitmap[0][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "s");
\r
2462 pieceBitmap[0][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "s");
\r
2463 pieceBitmap[0][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "s");
\r
2464 pieceBitmap[0][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "s");
\r
2465 pieceBitmap[0][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "s");
\r
2466 pieceBitmap[1][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "o");
\r
2467 pieceBitmap[1][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "o");
\r
2468 pieceBitmap[1][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "o");
\r
2469 pieceBitmap[1][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "o");
\r
2470 pieceBitmap[1][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "o");
\r
2471 pieceBitmap[2][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "w");
\r
2472 pieceBitmap[2][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "w");
\r
2473 pieceBitmap[2][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "w");
\r
2474 pieceBitmap[2][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "w");
\r
2475 pieceBitmap[2][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "w");
\r
2476 if( gameInfo.variant == VariantShogi && squareSize <= 72 && squareSize >= 33) {
\r
2477 // in Shogi, Hijack the unused Queen for Lance
\r
2478 pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "s");
\r
2479 pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "o");
\r
2480 pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "w");
\r
2482 pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "s");
\r
2483 pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "o");
\r
2484 pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "w");
\r
2487 if(squareSize <= 72 && squareSize >= 33) {
\r
2488 /* A & C are available in most sizes now */
\r
2489 if(squareSize != 49 && squareSize != 72 && squareSize != 33) { // Vortex-like
\r
2490 pieceBitmap[0][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "s");
\r
2491 pieceBitmap[1][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "o");
\r
2492 pieceBitmap[2][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "w");
\r
2493 pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "s");
\r
2494 pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "o");
\r
2495 pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "w");
\r
2496 pieceBitmap[0][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "s");
\r
2497 pieceBitmap[1][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "o");
\r
2498 pieceBitmap[2][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "w");
\r
2499 pieceBitmap[0][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "s");
\r
2500 pieceBitmap[1][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "o");
\r
2501 pieceBitmap[2][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "w");
\r
2502 } else { // Smirf-like
\r
2503 if(gameInfo.variant == VariantSChess) {
\r
2504 pieceBitmap[0][WhiteAngel] = DoLoadBitmap(hInst, "v", squareSize, "s");
\r
2505 pieceBitmap[1][WhiteAngel] = DoLoadBitmap(hInst, "v", squareSize, "o");
\r
2506 pieceBitmap[2][WhiteAngel] = DoLoadBitmap(hInst, "v", squareSize, "w");
\r
2508 pieceBitmap[0][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "s");
\r
2509 pieceBitmap[1][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "o");
\r
2510 pieceBitmap[2][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "w");
\r
2513 if(gameInfo.variant == VariantGothic) { // Vortex-like
\r
2514 pieceBitmap[0][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "s");
\r
2515 pieceBitmap[1][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "o");
\r
2516 pieceBitmap[2][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "w");
\r
2517 } else if(gameInfo.variant == VariantSChess && (squareSize == 49 || squareSize == 72)) {
\r
2518 pieceBitmap[0][WhiteMarshall] = DoLoadBitmap(hInst, "e", squareSize, "s");
\r
2519 pieceBitmap[1][WhiteMarshall] = DoLoadBitmap(hInst, "e", squareSize, "o");
\r
2520 pieceBitmap[2][WhiteMarshall] = DoLoadBitmap(hInst, "e", squareSize, "w");
\r
2521 } else { // WinBoard standard
\r
2522 pieceBitmap[0][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "s");
\r
2523 pieceBitmap[1][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "o");
\r
2524 pieceBitmap[2][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "w");
\r
2529 if(squareSize==72 || squareSize==49 || squareSize==33) { /* experiment with some home-made bitmaps */
\r
2530 pieceBitmap[0][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "s");
\r
2531 pieceBitmap[1][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "o");
\r
2532 pieceBitmap[2][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "w");
\r
2533 pieceBitmap[0][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "s");
\r
2534 pieceBitmap[1][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "o");
\r
2535 pieceBitmap[2][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "w");
\r
2536 pieceBitmap[0][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "s");
\r
2537 pieceBitmap[1][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "o");
\r
2538 pieceBitmap[2][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "w");
\r
2539 pieceBitmap[0][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "s");
\r
2540 pieceBitmap[1][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "o");
\r
2541 pieceBitmap[2][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "w");
\r
2542 pieceBitmap[0][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "s");
\r
2543 pieceBitmap[1][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "o");
\r
2544 pieceBitmap[2][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "w");
\r
2545 pieceBitmap[0][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "s");
\r
2546 pieceBitmap[1][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "o");
\r
2547 pieceBitmap[2][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "w");
\r
2548 pieceBitmap[0][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "s");
\r
2549 pieceBitmap[1][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "o");
\r
2550 pieceBitmap[2][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "w");
\r
2551 pieceBitmap[0][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "s");
\r
2552 pieceBitmap[1][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "o");
\r
2553 pieceBitmap[2][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "w");
\r
2554 pieceBitmap[0][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "s");
\r
2555 pieceBitmap[1][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "o");
\r
2556 pieceBitmap[2][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "w");
\r
2557 pieceBitmap[0][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "s");
\r
2558 pieceBitmap[1][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "o");
\r
2559 pieceBitmap[2][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "w");
\r
2561 if(gameInfo.variant == VariantShogi) { /* promoted Gold represemtations */
\r
2562 pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "wp", squareSize, "s");
\r
2563 pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "wp", squareSize, "o");
\r
2564 pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "w", squareSize, "w");
\r
2565 pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "wn", squareSize, "s");
\r
2566 pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "wn", squareSize, "o");
\r
2567 pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "w", squareSize, "w");
\r
2568 pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "ws", squareSize, "s");
\r
2569 pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "ws", squareSize, "o");
\r
2570 pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "w", squareSize, "w");
\r
2571 pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "wl", squareSize, "s");
\r
2572 pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "wl", squareSize, "o");
\r
2573 pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "w", squareSize, "w");
\r
2575 pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "s");
\r
2576 pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "o");
\r
2577 pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "w");
\r
2578 pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "s");
\r
2579 pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "o");
\r
2580 pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "w");
\r
2581 pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "s");
\r
2582 pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "o");
\r
2583 pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "w");
\r
2584 pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "s");
\r
2585 pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "o");
\r
2586 pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "w");
\r
2589 } else { /* other size, no special bitmaps available. Use smaller symbols */
\r
2590 if((int)boardSize < 2) minorSize = sizeInfo[0].squareSize;
\r
2591 else minorSize = sizeInfo[(int)boardSize - 2].squareSize;
\r
2592 pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "s");
\r
2593 pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "o");
\r
2594 pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "w");
\r
2595 pieceBitmap[0][WhiteCardinal] = DoLoadBitmap(hInst, "b", minorSize, "s");
\r
2596 pieceBitmap[1][WhiteCardinal] = DoLoadBitmap(hInst, "b", minorSize, "o");
\r
2597 pieceBitmap[2][WhiteCardinal] = DoLoadBitmap(hInst, "b", minorSize, "w");
\r
2598 pieceBitmap[0][WhiteDragon] = DoLoadBitmap(hInst, "r", minorSize, "s");
\r
2599 pieceBitmap[1][WhiteDragon] = DoLoadBitmap(hInst, "r", minorSize, "o");
\r
2600 pieceBitmap[2][WhiteDragon] = DoLoadBitmap(hInst, "r", minorSize, "w");
\r
2601 pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "s");
\r
2602 pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "o");
\r
2603 pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "w");
\r
2607 if(gameInfo.variant == VariantShogi && squareSize == 58)
\r
2608 /* special Shogi support in this size */
\r
2609 { for (i=0; i<=2; i++) { /* replace all bitmaps */
\r
2610 for (piece = WhitePawn;
\r
2611 (int) piece < (int) BlackPawn;
\r
2612 piece = (ChessSquare) ((int) piece + 1)) {
\r
2613 if (pieceBitmap[i][piece] != NULL)
\r
2614 DeleteObject(pieceBitmap[i][piece]);
\r
2617 pieceBitmap[0][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "o");
\r
2618 pieceBitmap[0][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "o");
\r
2619 pieceBitmap[0][WhiteBishop] = DoLoadBitmap(hInst, "sb", squareSize, "o");
\r
2620 pieceBitmap[0][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "o");
\r
2621 pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "o");
\r
2622 pieceBitmap[0][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "o");
\r
2623 pieceBitmap[0][WhiteFerz] = DoLoadBitmap(hInst, "sf", squareSize, "o");
\r
2624 pieceBitmap[0][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "o");
\r
2625 pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "su", squareSize, "o");
\r
2626 pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "sh", squareSize, "o");
\r
2627 pieceBitmap[0][WhiteCardinal] = DoLoadBitmap(hInst, "sa", squareSize, "o");
\r
2628 pieceBitmap[0][WhiteDragon] = DoLoadBitmap(hInst, "sc", squareSize, "o");
\r
2629 pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "sg", squareSize, "o");
\r
2630 pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "ss", squareSize, "o");
\r
2631 pieceBitmap[1][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "o");
\r
2632 pieceBitmap[1][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "o");
\r
2633 pieceBitmap[1][WhiteBishop] = DoLoadBitmap(hInst, "sb", squareSize, "o");
\r
2634 pieceBitmap[1][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "o");
\r
2635 pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "o");
\r
2636 pieceBitmap[1][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "o");
\r
2637 pieceBitmap[1][WhiteFerz] = DoLoadBitmap(hInst, "sf", squareSize, "o");
\r
2638 pieceBitmap[1][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "o");
\r
2639 pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "su", squareSize, "o");
\r
2640 pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "sh", squareSize, "o");
\r
2641 pieceBitmap[1][WhiteCardinal] = DoLoadBitmap(hInst, "sa", squareSize, "o");
\r
2642 pieceBitmap[1][WhiteDragon] = DoLoadBitmap(hInst, "sc", squareSize, "o");
\r
2643 pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "sg", squareSize, "o");
\r
2644 pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "ss", squareSize, "o");
\r
2645 pieceBitmap[2][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "w");
\r
2646 pieceBitmap[2][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "w");
\r
2647 pieceBitmap[2][WhiteBishop] = DoLoadBitmap(hInst, "sr", squareSize, "w");
\r
2648 pieceBitmap[2][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "w");
\r
2649 pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "w");
\r
2650 pieceBitmap[2][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "w");
\r
2651 pieceBitmap[2][WhiteFerz] = DoLoadBitmap(hInst, "sw", squareSize, "w");
\r
2652 pieceBitmap[2][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "w");
\r
2653 pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "sp", squareSize, "w");
\r
2654 pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "sn", squareSize, "w");
\r
2655 pieceBitmap[2][WhiteCardinal] = DoLoadBitmap(hInst, "sr", squareSize, "w");
\r
2656 pieceBitmap[2][WhiteDragon] = DoLoadBitmap(hInst, "sr", squareSize, "w");
\r
2657 pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "sl", squareSize, "w");
\r
2658 pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "sw", squareSize, "w");
\r
2664 PieceBitmap(ChessSquare p, int kind)
\r
2666 if ((int) p >= (int) BlackPawn)
\r
2667 p = (ChessSquare) ((int) p - (int) BlackPawn + (int) WhitePawn);
\r
2669 return pieceBitmap[kind][(int) p];
\r
2672 /***************************************************************/
\r
2674 #define MIN(a,b) (((a) < (b)) ? (a) : (b))
\r
2675 #define MAX(a,b) (((a) > (b)) ? (a) : (b))
\r
2677 #define MIN3(a,b,c) (((a) < (b) && (a) < (c)) ? (a) : (((b) < (a) && (b) < (c)) ? (b) : (c)))
\r
2678 #define MAX3(a,b,c) (((a) > (b) && (a) > (c)) ? (a) : (((b) > (a) && (b) > (c)) ? (b) : (c)))
\r
2682 SquareToPos(int row, int column, int * x, int * y)
\r
2685 *x = boardRect.left + lineGap + ((BOARD_WIDTH-1)-column) * (squareSize + lineGap);
\r
2686 *y = boardRect.top + lineGap + row * (squareSize + lineGap);
\r
2688 *x = boardRect.left + lineGap + column * (squareSize + lineGap);
\r
2689 *y = boardRect.top + lineGap + ((BOARD_HEIGHT-1)-row) * (squareSize + lineGap);
\r
2694 DrawCoordsOnDC(HDC hdc)
\r
2696 static char files[] = "0123456789012345678901221098765432109876543210";
\r
2697 static char ranks[] = "wvutsrqponmlkjihgfedcbaabcdefghijklmnopqrstuvw";
\r
2698 char str[2] = { NULLCHAR, NULLCHAR };
\r
2699 int oldMode, oldAlign, x, y, start, i;
\r
2703 if (!appData.showCoords)
\r
2706 start = flipView ? 1-(ONE!='1') : 45+(ONE!='1')-BOARD_HEIGHT;
\r
2708 oldBrush = SelectObject(hdc, GetStockObject(BLACK_BRUSH));
\r
2709 oldMode = SetBkMode(hdc, (appData.monoMode ? OPAQUE : TRANSPARENT));
\r
2710 oldAlign = GetTextAlign(hdc);
\r
2711 oldFont = SelectObject(hdc, font[boardSize][COORD_FONT]->hf);
\r
2713 y = boardRect.top + lineGap;
\r
2714 x = boardRect.left + lineGap + gameInfo.holdingsWidth*(squareSize + lineGap);
\r
2716 SetTextAlign(hdc, TA_LEFT|TA_TOP);
\r
2717 for (i = 0; i < BOARD_HEIGHT; i++) {
\r
2718 str[0] = files[start + i];
\r
2719 ExtTextOut(hdc, x + 2, y + 1, 0, NULL, str, 1, NULL);
\r
2720 y += squareSize + lineGap;
\r
2723 start = flipView ? 23-(BOARD_RGHT-BOARD_LEFT) : 23;
\r
2725 SetTextAlign(hdc, TA_RIGHT|TA_BOTTOM);
\r
2726 for (i = 0; i < BOARD_RGHT - BOARD_LEFT; i++) {
\r
2727 str[0] = ranks[start + i];
\r
2728 ExtTextOut(hdc, x + squareSize - 2, y - 1, 0, NULL, str, 1, NULL);
\r
2729 x += squareSize + lineGap;
\r
2732 SelectObject(hdc, oldBrush);
\r
2733 SetBkMode(hdc, oldMode);
\r
2734 SetTextAlign(hdc, oldAlign);
\r
2735 SelectObject(hdc, oldFont);
\r
2739 DrawGridOnDC(HDC hdc)
\r
2743 if (lineGap != 0) {
\r
2744 oldPen = SelectObject(hdc, gridPen);
\r
2745 PolyPolyline(hdc, gridEndpoints, gridVertexCounts, BOARD_WIDTH+BOARD_HEIGHT + 2);
\r
2746 SelectObject(hdc, oldPen);
\r
2750 #define HIGHLIGHT_PEN 0
\r
2751 #define PREMOVE_PEN 1
\r
2754 DrawHighlightOnDC(HDC hdc, BOOLEAN on, int x, int y, int pen)
\r
2757 HPEN oldPen, hPen;
\r
2758 if (lineGap == 0) return;
\r
2760 x1 = boardRect.left +
\r
2761 lineGap/2 + ((BOARD_WIDTH-1)-x) * (squareSize + lineGap);
\r
2762 y1 = boardRect.top +
\r
2763 lineGap/2 + y * (squareSize + lineGap);
\r
2765 x1 = boardRect.left +
\r
2766 lineGap/2 + x * (squareSize + lineGap);
\r
2767 y1 = boardRect.top +
\r
2768 lineGap/2 + ((BOARD_HEIGHT-1)-y) * (squareSize + lineGap);
\r
2770 hPen = pen ? premovePen : highlightPen;
\r
2771 oldPen = SelectObject(hdc, on ? hPen : gridPen);
\r
2772 MoveToEx(hdc, x1, y1, NULL);
\r
2773 LineTo(hdc, x1 + squareSize + lineGap, y1);
\r
2774 LineTo(hdc, x1 + squareSize + lineGap, y1 + squareSize + lineGap);
\r
2775 LineTo(hdc, x1, y1 + squareSize + lineGap);
\r
2776 LineTo(hdc, x1, y1);
\r
2777 SelectObject(hdc, oldPen);
\r
2781 DrawHighlightsOnDC(HDC hdc, HighlightInfo *h, int pen)
\r
2784 for (i=0; i<2; i++) {
\r
2785 if (h->sq[i].x >= 0 && h->sq[i].y >= 0)
\r
2786 DrawHighlightOnDC(hdc, TRUE,
\r
2787 h->sq[i].x, h->sq[i].y,
\r
2792 /* Note: sqcolor is used only in monoMode */
\r
2793 /* Note that this code is largely duplicated in woptions.c,
\r
2794 function DrawSampleSquare, so that needs to be updated too */
\r
2796 DrawPieceOnDC(HDC hdc, ChessSquare piece, int color, int sqcolor, int x, int y, HDC tmphdc)
\r
2798 HBITMAP oldBitmap;
\r
2802 if (appData.blindfold) return;
\r
2804 /* [AS] Use font-based pieces if needed */
\r
2805 if( fontBitmapSquareSize >= 0 && (squareSize > 32 || gameInfo.variant >= VariantShogi)) {
\r
2806 /* Create piece bitmaps, or do nothing if piece set is up to date */
\r
2807 CreatePiecesFromFont();
\r
2809 if( fontBitmapSquareSize == squareSize ) {
\r
2810 int index = TranslatePieceToFontPiece(piece);
\r
2812 SelectObject( tmphdc, hPieceMask[ index ] );
\r
2814 if(appData.upsideDown ? color==flipView : (flipView && gameInfo.variant == VariantShogi))
\r
2815 StretchBlt(hdc, x+squareSize, y+squareSize, -squareSize, -squareSize, tmphdc, 0, 0, squareSize, squareSize, SRCAND);
\r
2819 squareSize, squareSize,
\r
2824 SelectObject( tmphdc, hPieceFace[ index ] );
\r
2826 if(appData.upsideDown ? color==flipView : (flipView && gameInfo.variant == VariantShogi))
\r
2827 StretchBlt(hdc, x+squareSize, y+squareSize, -squareSize, -squareSize, tmphdc, 0, 0, squareSize, squareSize, SRCPAINT);
\r
2831 squareSize, squareSize,
\r
2840 if (appData.monoMode) {
\r
2841 SelectObject(tmphdc, PieceBitmap(piece,
\r
2842 color == sqcolor ? OUTLINE_PIECE : SOLID_PIECE));
\r
2843 BitBlt(hdc, x, y, squareSize, squareSize, tmphdc, 0, 0,
\r
2844 sqcolor ? SRCCOPY : NOTSRCCOPY);
\r
2846 tmpSize = squareSize;
\r
2848 ((piece >= (int)WhiteNightrider && piece <= WhiteGrasshopper) ||
\r
2849 (piece >= (int)BlackNightrider && piece <= BlackGrasshopper)) ) {
\r
2850 /* [HGM] no bitmap available for promoted pieces in Crazyhouse */
\r
2851 /* Bitmaps of smaller size are substituted, but we have to align them */
\r
2852 x += (squareSize - minorSize)>>1;
\r
2853 y += squareSize - minorSize - 2;
\r
2854 tmpSize = minorSize;
\r
2856 if (color || appData.allWhite ) {
\r
2857 oldBitmap = SelectObject(tmphdc, PieceBitmap(piece, WHITE_PIECE));
\r
2859 oldBrush = SelectObject(hdc, whitePieceBrush);
\r
2860 else oldBrush = SelectObject(hdc, blackPieceBrush);
\r
2861 if(appData.upsideDown && color==flipView)
\r
2862 StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, 0x00B8074A);
\r
2864 BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, 0x00B8074A);
\r
2865 /* Use black for outline of white pieces */
\r
2866 SelectObject(tmphdc, PieceBitmap(piece, OUTLINE_PIECE));
\r
2867 if(appData.upsideDown && color==flipView)
\r
2868 StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, SRCAND);
\r
2870 BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, SRCAND);
\r
2872 /* Use square color for details of black pieces */
\r
2873 oldBitmap = SelectObject(tmphdc, PieceBitmap(piece, SOLID_PIECE));
\r
2874 oldBrush = SelectObject(hdc, blackPieceBrush);
\r
2875 if(appData.upsideDown && !flipView)
\r
2876 StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, 0x00B8074A);
\r
2878 BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, 0x00B8074A);
\r
2880 SelectObject(hdc, oldBrush);
\r
2881 SelectObject(tmphdc, oldBitmap);
\r
2885 /* [AS] Compute a drawing mode for a square, based on specified settings (see DrawTile) */
\r
2886 int GetBackTextureMode( int algo )
\r
2888 int result = BACK_TEXTURE_MODE_DISABLED;
\r
2892 case BACK_TEXTURE_MODE_PLAIN:
\r
2893 result = 1; /* Always use identity map */
\r
2895 case BACK_TEXTURE_MODE_FULL_RANDOM:
\r
2896 result = 1 + (myrandom() % 3); /* Pick a transformation at random */
\r
2904 [AS] Compute and save texture drawing info, otherwise we may not be able
\r
2905 to handle redraws cleanly (as random numbers would always be different).
\r
2907 VOID RebuildTextureSquareInfo()
\r
2917 ZeroMemory( &backTextureSquareInfo, sizeof(backTextureSquareInfo) );
\r
2919 if( liteBackTexture != NULL ) {
\r
2920 if( GetObject( liteBackTexture, sizeof(bi), &bi ) > 0 ) {
\r
2921 lite_w = bi.bmWidth;
\r
2922 lite_h = bi.bmHeight;
\r
2926 if( darkBackTexture != NULL ) {
\r
2927 if( GetObject( darkBackTexture, sizeof(bi), &bi ) > 0 ) {
\r
2928 dark_w = bi.bmWidth;
\r
2929 dark_h = bi.bmHeight;
\r
2933 for( row=0; row<BOARD_HEIGHT; row++ ) {
\r
2934 for( col=0; col<BOARD_WIDTH; col++ ) {
\r
2935 if( (col + row) & 1 ) {
\r
2937 if( lite_w >= squareSize && lite_h >= squareSize ) {
\r
2938 if( lite_w >= squareSize*BOARD_WIDTH )
\r
2939 backTextureSquareInfo[row][col].x = (2*col+1)*lite_w/(2*BOARD_WIDTH) - squareSize/2; /* [HGM] cut out of center of virtual square */
\r
2941 backTextureSquareInfo[row][col].x = col * (lite_w - squareSize) / (BOARD_WIDTH-1); /* [HGM] divide by size-1 in stead of size! */
\r
2942 if( lite_h >= squareSize*BOARD_HEIGHT )
\r
2943 backTextureSquareInfo[row][col].y = (2*(BOARD_HEIGHT-row)-1)*lite_h/(2*BOARD_HEIGHT) - squareSize/2;
\r
2945 backTextureSquareInfo[row][col].y = (BOARD_HEIGHT-1-row) * (lite_h - squareSize) / (BOARD_HEIGHT-1);
\r
2946 backTextureSquareInfo[row][col].mode = GetBackTextureMode(liteBackTextureMode);
\r
2951 if( dark_w >= squareSize && dark_h >= squareSize ) {
\r
2952 if( dark_w >= squareSize*BOARD_WIDTH )
\r
2953 backTextureSquareInfo[row][col].x = (2*col+1) * dark_w / (2*BOARD_WIDTH) - squareSize/2;
\r
2955 backTextureSquareInfo[row][col].x = col * (dark_w - squareSize) / (BOARD_WIDTH-1);
\r
2956 if( dark_h >= squareSize*BOARD_HEIGHT )
\r
2957 backTextureSquareInfo[row][col].y = (2*(BOARD_HEIGHT-row)-1) * dark_h / (2*BOARD_HEIGHT) - squareSize/2;
\r
2959 backTextureSquareInfo[row][col].y = (BOARD_HEIGHT-1-row) * (dark_h - squareSize) / (BOARD_HEIGHT-1);
\r
2960 backTextureSquareInfo[row][col].mode = GetBackTextureMode(darkBackTextureMode);
\r
2967 /* [AS] Arrow highlighting support */
\r
2969 static double A_WIDTH = 5; /* Width of arrow body */
\r
2971 #define A_HEIGHT_FACTOR 6 /* Length of arrow "point", relative to body width */
\r
2972 #define A_WIDTH_FACTOR 3 /* Width of arrow "point", relative to body width */
\r
2974 static double Sqr( double x )
\r
2979 static int Round( double x )
\r
2981 return (int) (x + 0.5);
\r
2984 /* Draw an arrow between two points using current settings */
\r
2985 VOID DrawArrowBetweenPoints( HDC hdc, int s_x, int s_y, int d_x, int d_y )
\r
2988 double dx, dy, j, k, x, y;
\r
2990 if( d_x == s_x ) {
\r
2991 int h = (d_y > s_y) ? +A_WIDTH*A_HEIGHT_FACTOR : -A_WIDTH*A_HEIGHT_FACTOR;
\r
2993 arrow[0].x = s_x + A_WIDTH + 0.5;
\r
2996 arrow[1].x = s_x + A_WIDTH + 0.5;
\r
2997 arrow[1].y = d_y - h;
\r
2999 arrow[2].x = arrow[1].x + A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;
\r
3000 arrow[2].y = d_y - h;
\r
3005 arrow[5].x = arrow[1].x - 2*A_WIDTH + 0.5;
\r
3006 arrow[5].y = d_y - h;
\r
3008 arrow[4].x = arrow[5].x - A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;
\r
3009 arrow[4].y = d_y - h;
\r
3011 arrow[6].x = arrow[1].x - 2*A_WIDTH + 0.5;
\r
3014 else if( d_y == s_y ) {
\r
3015 int w = (d_x > s_x) ? +A_WIDTH*A_HEIGHT_FACTOR : -A_WIDTH*A_HEIGHT_FACTOR;
\r
3018 arrow[0].y = s_y + A_WIDTH + 0.5;
\r
3020 arrow[1].x = d_x - w;
\r
3021 arrow[1].y = s_y + A_WIDTH + 0.5;
\r
3023 arrow[2].x = d_x - w;
\r
3024 arrow[2].y = arrow[1].y + A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;
\r
3029 arrow[5].x = d_x - w;
\r
3030 arrow[5].y = arrow[1].y - 2*A_WIDTH + 0.5;
\r
3032 arrow[4].x = d_x - w;
\r
3033 arrow[4].y = arrow[5].y - A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;
\r
3036 arrow[6].y = arrow[1].y - 2*A_WIDTH + 0.5;
\r
3039 /* [AS] Needed a lot of paper for this! :-) */
\r
3040 dy = (double) (d_y - s_y) / (double) (d_x - s_x);
\r
3041 dx = (double) (s_x - d_x) / (double) (s_y - d_y);
\r
3043 j = sqrt( Sqr(A_WIDTH) / (1.0 + Sqr(dx)) );
\r
3045 k = sqrt( Sqr(A_WIDTH*A_HEIGHT_FACTOR) / (1.0 + Sqr(dy)) );
\r
3050 arrow[0].x = Round(x - j);
\r
3051 arrow[0].y = Round(y + j*dx);
\r
3053 arrow[1].x = Round(arrow[0].x + 2*j); // [HGM] prevent width to be affected by rounding twice
\r
3054 arrow[1].y = Round(arrow[0].y - 2*j*dx);
\r
3057 x = (double) d_x - k;
\r
3058 y = (double) d_y - k*dy;
\r
3061 x = (double) d_x + k;
\r
3062 y = (double) d_y + k*dy;
\r
3065 x = Round(x); y = Round(y); // [HGM] make sure width of shaft is rounded the same way on both ends
\r
3067 arrow[6].x = Round(x - j);
\r
3068 arrow[6].y = Round(y + j*dx);
\r
3070 arrow[2].x = Round(arrow[6].x + 2*j);
\r
3071 arrow[2].y = Round(arrow[6].y - 2*j*dx);
\r
3073 arrow[3].x = Round(arrow[2].x + j*(A_WIDTH_FACTOR-1));
\r
3074 arrow[3].y = Round(arrow[2].y - j*(A_WIDTH_FACTOR-1)*dx);
\r
3079 arrow[5].x = Round(arrow[6].x - j*(A_WIDTH_FACTOR-1));
\r
3080 arrow[5].y = Round(arrow[6].y + j*(A_WIDTH_FACTOR-1)*dx);
\r
3083 Polygon( hdc, arrow, 7 );
\r
3086 /* [AS] Draw an arrow between two squares */
\r
3087 VOID DrawArrowBetweenSquares( HDC hdc, int s_col, int s_row, int d_col, int d_row )
\r
3089 int s_x, s_y, d_x, d_y;
\r
3096 if( s_col == d_col && s_row == d_row ) {
\r
3100 /* Get source and destination points */
\r
3101 SquareToPos( s_row, s_col, &s_x, &s_y);
\r
3102 SquareToPos( d_row, d_col, &d_x, &d_y);
\r
3105 d_y += squareSize / 2 - squareSize / 4; // [HGM] round towards same centers on all sides!
\r
3107 else if( d_y < s_y ) {
\r
3108 d_y += squareSize / 2 + squareSize / 4;
\r
3111 d_y += squareSize / 2;
\r
3115 d_x += squareSize / 2 - squareSize / 4;
\r
3117 else if( d_x < s_x ) {
\r
3118 d_x += squareSize / 2 + squareSize / 4;
\r
3121 d_x += squareSize / 2;
\r
3124 s_x += squareSize / 2;
\r
3125 s_y += squareSize / 2;
\r
3127 /* Adjust width */
\r
3128 A_WIDTH = squareSize / 14.; //[HGM] make float
\r
3131 stLB.lbStyle = BS_SOLID;
\r
3132 stLB.lbColor = appData.highlightArrowColor;
\r
3135 hpen = CreatePen( PS_SOLID, 2, RGB(0x00,0x00,0x00) );
\r
3136 holdpen = SelectObject( hdc, hpen );
\r
3137 hbrush = CreateBrushIndirect( &stLB );
\r
3138 holdbrush = SelectObject( hdc, hbrush );
\r
3140 DrawArrowBetweenPoints( hdc, s_x, s_y, d_x, d_y );
\r
3142 SelectObject( hdc, holdpen );
\r
3143 SelectObject( hdc, holdbrush );
\r
3144 DeleteObject( hpen );
\r
3145 DeleteObject( hbrush );
\r
3148 BOOL HasHighlightInfo()
\r
3150 BOOL result = FALSE;
\r
3152 if( highlightInfo.sq[0].x >= 0 && highlightInfo.sq[0].y >= 0 &&
\r
3153 highlightInfo.sq[1].x >= 0 && highlightInfo.sq[1].y >= 0 )
\r
3161 BOOL IsDrawArrowEnabled()
\r
3163 BOOL result = FALSE;
\r
3165 if( appData.highlightMoveWithArrow && squareSize >= 32 ) {
\r
3172 VOID DrawArrowHighlight( HDC hdc )
\r
3174 if( IsDrawArrowEnabled() && HasHighlightInfo() ) {
\r
3175 DrawArrowBetweenSquares( hdc,
\r
3176 highlightInfo.sq[0].x, highlightInfo.sq[0].y,
\r
3177 highlightInfo.sq[1].x, highlightInfo.sq[1].y );
\r
3181 HRGN GetArrowHighlightClipRegion( HDC hdc )
\r
3183 HRGN result = NULL;
\r
3185 if( HasHighlightInfo() ) {
\r
3186 int x1, y1, x2, y2;
\r
3187 int sx, sy, dx, dy;
\r
3189 SquareToPos(highlightInfo.sq[0].y, highlightInfo.sq[0].x, &x1, &y1 );
\r
3190 SquareToPos(highlightInfo.sq[1].y, highlightInfo.sq[1].x, &x2, &y2 );
\r
3192 sx = MIN( x1, x2 );
\r
3193 sy = MIN( y1, y2 );
\r
3194 dx = MAX( x1, x2 ) + squareSize;
\r
3195 dy = MAX( y1, y2 ) + squareSize;
\r
3197 result = CreateRectRgn( sx, sy, dx, dy );
\r
3204 Warning: this function modifies the behavior of several other functions.
\r
3206 Basically, Winboard is optimized to avoid drawing the whole board if not strictly
\r
3207 needed. Unfortunately, the decision whether or not to perform a full or partial
\r
3208 repaint is scattered all over the place, which is not good for features such as
\r
3209 "arrow highlighting" that require a full repaint of the board.
\r
3211 So, I've tried to patch the code where I thought it made sense (e.g. after or during
\r
3212 user interaction, when speed is not so important) but especially to avoid errors
\r
3213 in the displayed graphics.
\r
3215 In such patched places, I always try refer to this function so there is a single
\r
3216 place to maintain knowledge.
\r
3218 To restore the original behavior, just return FALSE unconditionally.
\r
3220 BOOL IsFullRepaintPreferrable()
\r
3222 BOOL result = FALSE;
\r
3224 if( (appData.highlightLastMove || appData.highlightDragging) && IsDrawArrowEnabled() ) {
\r
3225 /* Arrow may appear on the board */
\r
3233 This function is called by DrawPosition to know whether a full repaint must
\r
3236 Only DrawPosition may directly call this function, which makes use of
\r
3237 some state information. Other function should call DrawPosition specifying
\r
3238 the repaint flag, and can use IsFullRepaintPreferrable if needed.
\r
3240 BOOL DrawPositionNeedsFullRepaint()
\r
3242 BOOL result = FALSE;
\r
3245 Probably a slightly better policy would be to trigger a full repaint
\r
3246 when animInfo.piece changes state (i.e. empty -> non-empty and viceversa),
\r
3247 but animation is fast enough that it's difficult to notice.
\r
3249 if( animInfo.piece == EmptySquare ) {
\r
3250 if( (appData.highlightLastMove || appData.highlightDragging) && IsDrawArrowEnabled() /*&& HasHighlightInfo()*/ ) {
\r
3259 DrawBoardOnDC(HDC hdc, Board board, HDC tmphdc)
\r
3261 int row, column, x, y, square_color, piece_color;
\r
3262 ChessSquare piece;
\r
3264 HDC texture_hdc = NULL;
\r
3266 /* [AS] Initialize background textures if needed */
\r
3267 if( liteBackTexture != NULL || darkBackTexture != NULL ) {
\r
3268 static int backTextureBoardSize; /* [HGM] boardsize: also new texture if board format changed */
\r
3269 if( backTextureSquareSize != squareSize
\r
3270 || backTextureBoardSize != BOARD_WIDTH+BOARD_FILES*BOARD_HEIGHT) {
\r
3271 backTextureBoardSize = BOARD_WIDTH+BOARD_FILES*BOARD_HEIGHT;
\r
3272 backTextureSquareSize = squareSize;
\r
3273 RebuildTextureSquareInfo();
\r
3276 texture_hdc = CreateCompatibleDC( hdc );
\r
3279 for (row = 0; row < BOARD_HEIGHT; row++) {
\r
3280 for (column = 0; column < BOARD_WIDTH; column++) {
\r
3282 SquareToPos(row, column, &x, &y);
\r
3284 piece = board[row][column];
\r
3286 square_color = ((column + row) % 2) == 1;
\r
3287 if( gameInfo.variant == VariantXiangqi ) {
\r
3288 square_color = !InPalace(row, column);
\r
3289 if(BOARD_HEIGHT&1) { if(row==BOARD_HEIGHT/2) square_color ^= 1; }
\r
3290 else if(row < BOARD_HEIGHT/2) square_color ^= 1;
\r
3292 piece_color = (int) piece < (int) BlackPawn;
\r
3295 /* [HGM] holdings file: light square or black */
\r
3296 if(column == BOARD_LEFT-2) {
\r
3297 if( row > BOARD_HEIGHT - gameInfo.holdingsSize - 1 )
\r
3300 DisplayHoldingsCount(hdc, x, y, 0, 0); /* black out */
\r
3304 if(column == BOARD_RGHT + 1 ) {
\r
3305 if( row < gameInfo.holdingsSize )
\r
3308 DisplayHoldingsCount(hdc, x, y, 0, 0);
\r
3312 if(column == BOARD_LEFT-1 ) /* left align */
\r
3313 DisplayHoldingsCount(hdc, x, y, flipView, (int) board[row][column]);
\r
3314 else if( column == BOARD_RGHT) /* right align */
\r
3315 DisplayHoldingsCount(hdc, x, y, !flipView, (int) board[row][column]);
\r
3317 if (appData.monoMode) {
\r
3318 if (piece == EmptySquare) {
\r
3319 BitBlt(hdc, x, y, squareSize, squareSize, 0, 0, 0,
\r
3320 square_color ? WHITENESS : BLACKNESS);
\r
3322 DrawPieceOnDC(hdc, piece, piece_color, square_color, x, y, tmphdc);
\r
3325 else if( backTextureSquareInfo[row][column].mode > 0 ) {
\r
3326 /* [AS] Draw the square using a texture bitmap */
\r
3327 HBITMAP hbm = SelectObject( texture_hdc, square_color ? liteBackTexture : darkBackTexture );
\r
3328 int r = row, c = column; // [HGM] do not flip board in flipView
\r
3329 if(flipView) { r = BOARD_HEIGHT-1 - r; c = BOARD_WIDTH-1 - c; }
\r
3332 squareSize, squareSize,
\r
3335 backTextureSquareInfo[r][c].mode,
\r
3336 backTextureSquareInfo[r][c].x,
\r
3337 backTextureSquareInfo[r][c].y );
\r
3339 SelectObject( texture_hdc, hbm );
\r
3341 if (piece != EmptySquare) {
\r
3342 DrawPieceOnDC(hdc, piece, piece_color, -1, x, y, tmphdc);
\r
3346 HBRUSH brush = square_color ? lightSquareBrush : darkSquareBrush;
\r
3348 oldBrush = SelectObject(hdc, brush );
\r
3349 BitBlt(hdc, x, y, squareSize, squareSize, 0, 0, 0, PATCOPY);
\r
3350 SelectObject(hdc, oldBrush);
\r
3351 if (piece != EmptySquare)
\r
3352 DrawPieceOnDC(hdc, piece, piece_color, -1, x, y, tmphdc);
\r
3357 if( texture_hdc != NULL ) {
\r
3358 DeleteDC( texture_hdc );
\r
3362 int saveDiagFlag = 0; FILE *diagFile; // [HGM] diag
\r
3363 void fputDW(FILE *f, int x)
\r
3365 fputc(x & 255, f);
\r
3366 fputc(x>>8 & 255, f);
\r
3367 fputc(x>>16 & 255, f);
\r
3368 fputc(x>>24 & 255, f);
\r
3371 #define MAX_CLIPS 200 /* more than enough */
\r
3374 DrawLogoOnDC(HDC hdc, RECT logoRect, HBITMAP logo)
\r
3376 // HBITMAP bufferBitmap;
\r
3381 int w = 100, h = 50;
\r
3383 if(logo == NULL) {
\r
3384 if(!logoHeight) return;
\r
3385 FillRect( hdc, &logoRect, whitePieceBrush );
\r
3387 // GetClientRect(hwndMain, &Rect);
\r
3388 // bufferBitmap = CreateCompatibleBitmap(hdc, Rect.right-Rect.left+1,
\r
3389 // Rect.bottom-Rect.top+1);
\r
3390 tmphdc = CreateCompatibleDC(hdc);
\r
3391 hbm = SelectObject(tmphdc, logo);
\r
3392 if( GetObject( logo, sizeof(bi), &bi ) > 0 ) {
\r
3396 StretchBlt(hdc, logoRect.left, logoRect.top, logoRect.right - logoRect.left,
\r
3397 logoRect.bottom - logoRect.top, tmphdc, 0, 0, w, h, SRCCOPY);
\r
3398 SelectObject(tmphdc, hbm);
\r
3406 HDC hdc = GetDC(hwndMain);
\r
3407 HBITMAP whiteLogo = (HBITMAP) first.programLogo, blackLogo = (HBITMAP) second.programLogo;
\r
3408 if(appData.autoLogo) {
\r
3410 switch(gameMode) { // pick logos based on game mode
\r
3411 case IcsObserving:
\r
3412 whiteLogo = second.programLogo; // ICS logo
\r
3413 blackLogo = second.programLogo;
\r
3416 case IcsPlayingWhite:
\r
3417 if(!appData.zippyPlay) whiteLogo = userLogo;
\r
3418 blackLogo = second.programLogo; // ICS logo
\r
3420 case IcsPlayingBlack:
\r
3421 whiteLogo = second.programLogo; // ICS logo
\r
3422 blackLogo = appData.zippyPlay ? first.programLogo : userLogo;
\r
3424 case TwoMachinesPlay:
\r
3425 if(first.twoMachinesColor[0] == 'b') {
\r
3426 whiteLogo = second.programLogo;
\r
3427 blackLogo = first.programLogo;
\r
3430 case MachinePlaysWhite:
\r
3431 blackLogo = userLogo;
\r
3433 case MachinePlaysBlack:
\r
3434 whiteLogo = userLogo;
\r
3435 blackLogo = first.programLogo;
\r
3438 DrawLogoOnDC(hdc, leftLogoRect, flipClock ? blackLogo : whiteLogo);
\r
3439 DrawLogoOnDC(hdc, rightLogoRect, flipClock ? whiteLogo : blackLogo);
\r
3440 ReleaseDC(hwndMain, hdc);
\r
3445 UpdateLogos(int display)
\r
3446 { // called after loading new engine(s), in tourney or from menu
\r
3447 LoadLogo(&first, 0, FALSE);
\r
3448 LoadLogo(&second, 1, appData.icsActive);
\r
3449 InitDrawingSizes(-2, 0); // adapt layout of board window to presence/absence of logos
\r
3450 if(display) DisplayLogos();
\r
3453 static HDC hdcSeek;
\r
3455 // [HGM] seekgraph
\r
3456 void DrawSeekAxis( int x, int y, int xTo, int yTo )
\r
3459 HPEN hp = SelectObject( hdcSeek, gridPen );
\r
3460 MoveToEx( hdcSeek, boardRect.left+x, boardRect.top+y, &stPt );
\r
3461 LineTo( hdcSeek, boardRect.left+xTo, boardRect.top+yTo );
\r
3462 SelectObject( hdcSeek, hp );
\r
3465 // front-end wrapper for drawing functions to do rectangles
\r
3466 void DrawSeekBackground( int left, int top, int right, int bottom )
\r
3471 if (hdcSeek == NULL) {
\r
3472 hdcSeek = GetDC(hwndMain);
\r
3473 if (!appData.monoMode) {
\r
3474 SelectPalette(hdcSeek, hPal, FALSE);
\r
3475 RealizePalette(hdcSeek);
\r
3478 hp = SelectObject( hdcSeek, gridPen );
\r
3479 rc.top = boardRect.top+top; rc.left = boardRect.left+left;
\r
3480 rc.bottom = boardRect.top+bottom; rc.right = boardRect.left+right;
\r
3481 FillRect( hdcSeek, &rc, lightSquareBrush );
\r
3482 SelectObject( hdcSeek, hp );
\r
3485 // front-end wrapper for putting text in graph
\r
3486 void DrawSeekText(char *buf, int x, int y)
\r
3489 SetBkMode( hdcSeek, TRANSPARENT );
\r
3490 GetTextExtentPoint32( hdcSeek, buf, strlen(buf), &stSize );
\r
3491 TextOut( hdcSeek, boardRect.left+x-3, boardRect.top+y-stSize.cy/2, buf, strlen(buf) );
\r
3494 void DrawSeekDot(int x, int y, int color)
\r
3496 int square = color & 0x80;
\r
3497 HBRUSH oldBrush = SelectObject(hdcSeek,
\r
3498 color == 0 ? markerBrush : color == 1 ? darkSquareBrush : explodeBrush);
\r
3501 Rectangle(hdcSeek, boardRect.left+x - squareSize/9, boardRect.top+y - squareSize/9,
\r
3502 boardRect.left+x + squareSize/9, boardRect.top+y + squareSize/9);
\r
3504 Ellipse(hdcSeek, boardRect.left+x - squareSize/8, boardRect.top+y - squareSize/8,
\r
3505 boardRect.left+x + squareSize/8, boardRect.top+y + squareSize/8);
\r
3506 SelectObject(hdcSeek, oldBrush);
\r
3510 HDCDrawPosition(HDC hdc, BOOLEAN repaint, Board board)
\r
3512 static Board lastReq[2], lastDrawn[2];
\r
3513 static HighlightInfo lastDrawnHighlight, lastDrawnPremove;
\r
3514 static int lastDrawnFlipView = 0;
\r
3515 static int lastReqValid[2] = {0, 0}, lastDrawnValid[2] = {0, 0};
\r
3516 int releaseDC, x, y, x2, y2, row, column, num_clips = 0, i;
\r
3519 HBITMAP bufferBitmap;
\r
3520 HBITMAP oldBitmap;
\r
3522 HRGN clips[MAX_CLIPS];
\r
3523 ChessSquare dragged_piece = EmptySquare;
\r
3524 int nr = twoBoards*partnerUp;
\r
3526 /* I'm undecided on this - this function figures out whether a full
\r
3527 * repaint is necessary on its own, so there's no real reason to have the
\r
3528 * caller tell it that. I think this can safely be set to FALSE - but
\r
3529 * if we trust the callers not to request full repaints unnessesarily, then
\r
3530 * we could skip some clipping work. In other words, only request a full
\r
3531 * redraw when the majority of pieces have changed positions (ie. flip,
\r
3532 * gamestart and similar) --Hawk
\r
3534 Boolean fullrepaint = repaint;
\r
3536 if(DrawSeekGraph()) return; // [HG} seekgraph: suppress printing board if seek graph up
\r
3538 if( DrawPositionNeedsFullRepaint() ) {
\r
3539 fullrepaint = TRUE;
\r
3542 if (board == NULL) {
\r
3543 if (!lastReqValid[nr]) {
\r
3546 board = lastReq[nr];
\r
3548 CopyBoard(lastReq[nr], board);
\r
3549 lastReqValid[nr] = 1;
\r
3552 if (doingSizing) {
\r
3556 if (IsIconic(hwndMain)) {
\r
3560 if (hdc == NULL) {
\r
3561 hdc = GetDC(hwndMain);
\r
3562 if (!appData.monoMode) {
\r
3563 SelectPalette(hdc, hPal, FALSE);
\r
3564 RealizePalette(hdc);
\r
3568 releaseDC = FALSE;
\r
3571 /* Create some work-DCs */
\r
3572 hdcmem = CreateCompatibleDC(hdc);
\r
3573 tmphdc = CreateCompatibleDC(hdc);
\r
3575 /* If dragging is in progress, we temporarely remove the piece */
\r
3576 /* [HGM] or temporarily decrease count if stacked */
\r
3577 /* !! Moved to before board compare !! */
\r
3578 if (dragInfo.from.x >= 0 && dragInfo.pos.x >= 0) {
\r
3579 dragged_piece = board[dragInfo.from.y][dragInfo.from.x];
\r
3580 if(dragInfo.from.x == BOARD_LEFT-2 ) {
\r
3581 if(--board[dragInfo.from.y][dragInfo.from.x+1] == 0 )
\r
3582 board[dragInfo.from.y][dragInfo.from.x] = EmptySquare;
\r
3584 if(dragInfo.from.x == BOARD_RGHT+1) {
\r
3585 if(--board[dragInfo.from.y][dragInfo.from.x-1] == 0 )
\r
3586 board[dragInfo.from.y][dragInfo.from.x] = EmptySquare;
\r
3588 board[dragInfo.from.y][dragInfo.from.x] = gatingPiece;
\r
3591 /* Figure out which squares need updating by comparing the
\r
3592 * newest board with the last drawn board and checking if
\r
3593 * flipping has changed.
\r
3595 if (!fullrepaint && lastDrawnValid[nr] && (nr == 1 || lastDrawnFlipView == flipView)) {
\r
3596 for (row = 0; row < BOARD_HEIGHT; row++) { /* [HGM] true size, not 8 */
\r
3597 for (column = 0; column < BOARD_WIDTH; column++) {
\r
3598 if (lastDrawn[nr][row][column] != board[row][column]) {
\r
3599 SquareToPos(row, column, &x, &y);
\r
3600 clips[num_clips++] =
\r
3601 CreateRectRgn(x, y, x + squareSize, y + squareSize);
\r
3605 if(nr == 0) { // [HGM] dual: no highlights on second board
\r
3606 for (i=0; i<2; i++) {
\r
3607 if (lastDrawnHighlight.sq[i].x != highlightInfo.sq[i].x ||
\r
3608 lastDrawnHighlight.sq[i].y != highlightInfo.sq[i].y) {
\r
3609 if (lastDrawnHighlight.sq[i].x >= 0 &&
\r
3610 lastDrawnHighlight.sq[i].y >= 0) {
\r
3611 SquareToPos(lastDrawnHighlight.sq[i].y,
\r
3612 lastDrawnHighlight.sq[i].x, &x, &y);
\r
3613 clips[num_clips++] =
\r
3614 CreateRectRgn(x - lineGap, y - lineGap,
\r
3615 x + squareSize + lineGap, y + squareSize + lineGap);
\r
3617 if (highlightInfo.sq[i].x >= 0 && highlightInfo.sq[i].y >= 0) {
\r
3618 SquareToPos(highlightInfo.sq[i].y, highlightInfo.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
3625 for (i=0; i<2; i++) {
\r
3626 if (lastDrawnPremove.sq[i].x != premoveHighlightInfo.sq[i].x ||
\r
3627 lastDrawnPremove.sq[i].y != premoveHighlightInfo.sq[i].y) {
\r
3628 if (lastDrawnPremove.sq[i].x >= 0 &&
\r
3629 lastDrawnPremove.sq[i].y >= 0) {
\r
3630 SquareToPos(lastDrawnPremove.sq[i].y,
\r
3631 lastDrawnPremove.sq[i].x, &x, &y);
\r
3632 clips[num_clips++] =
\r
3633 CreateRectRgn(x - lineGap, y - lineGap,
\r
3634 x + squareSize + lineGap, y + squareSize + lineGap);
\r
3636 if (premoveHighlightInfo.sq[i].x >= 0 &&
\r
3637 premoveHighlightInfo.sq[i].y >= 0) {
\r
3638 SquareToPos(premoveHighlightInfo.sq[i].y,
\r
3639 premoveHighlightInfo.sq[i].x, &x, &y);
\r
3640 clips[num_clips++] =
\r
3641 CreateRectRgn(x - lineGap, y - lineGap,
\r
3642 x + squareSize + lineGap, y + squareSize + lineGap);
\r
3646 } else { // nr == 1
\r
3647 partnerHighlightInfo.sq[0].y = board[EP_STATUS-4];
\r
3648 partnerHighlightInfo.sq[0].x = board[EP_STATUS-3];
\r
3649 partnerHighlightInfo.sq[1].y = board[EP_STATUS-2];
\r
3650 partnerHighlightInfo.sq[1].x = board[EP_STATUS-1];
\r
3651 for (i=0; i<2; i++) {
\r
3652 if (partnerHighlightInfo.sq[i].x >= 0 &&
\r
3653 partnerHighlightInfo.sq[i].y >= 0) {
\r
3654 SquareToPos(partnerHighlightInfo.sq[i].y,
\r
3655 partnerHighlightInfo.sq[i].x, &x, &y);
\r
3656 clips[num_clips++] =
\r
3657 CreateRectRgn(x - lineGap, y - lineGap,
\r
3658 x + squareSize + lineGap, y + squareSize + lineGap);
\r
3660 if (oldPartnerHighlight.sq[i].x >= 0 &&
\r
3661 oldPartnerHighlight.sq[i].y >= 0) {
\r
3662 SquareToPos(oldPartnerHighlight.sq[i].y,
\r
3663 oldPartnerHighlight.sq[i].x, &x, &y);
\r
3664 clips[num_clips++] =
\r
3665 CreateRectRgn(x - lineGap, y - lineGap,
\r
3666 x + squareSize + lineGap, y + squareSize + lineGap);
\r
3671 fullrepaint = TRUE;
\r
3674 /* Create a buffer bitmap - this is the actual bitmap
\r
3675 * being written to. When all the work is done, we can
\r
3676 * copy it to the real DC (the screen). This avoids
\r
3677 * the problems with flickering.
\r
3679 GetClientRect(hwndMain, &Rect);
\r
3680 bufferBitmap = CreateCompatibleBitmap(hdc, Rect.right-Rect.left+1,
\r
3681 Rect.bottom-Rect.top+1);
\r
3682 oldBitmap = SelectObject(hdcmem, bufferBitmap);
\r
3683 if (!appData.monoMode) {
\r
3684 SelectPalette(hdcmem, hPal, FALSE);
\r
3687 /* Create clips for dragging */
\r
3688 if (!fullrepaint) {
\r
3689 if (dragInfo.from.x >= 0) {
\r
3690 SquareToPos(dragInfo.from.y, dragInfo.from.x, &x, &y);
\r
3691 clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);
\r
3693 if (dragInfo.start.x >= 0) {
\r
3694 SquareToPos(dragInfo.start.y, dragInfo.start.x, &x, &y);
\r
3695 clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);
\r
3697 if (dragInfo.pos.x >= 0) {
\r
3698 x = dragInfo.pos.x - squareSize / 2;
\r
3699 y = dragInfo.pos.y - squareSize / 2;
\r
3700 clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);
\r
3702 if (dragInfo.lastpos.x >= 0) {
\r
3703 x = dragInfo.lastpos.x - squareSize / 2;
\r
3704 y = dragInfo.lastpos.y - squareSize / 2;
\r
3705 clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);
\r
3709 /* Are we animating a move?
\r
3711 * - remove the piece from the board (temporarely)
\r
3712 * - calculate the clipping region
\r
3714 if (!fullrepaint) {
\r
3715 if (animInfo.piece != EmptySquare) {
\r
3716 board[animInfo.from.y][animInfo.from.x] = EmptySquare;
\r
3717 x = boardRect.left + animInfo.lastpos.x;
\r
3718 y = boardRect.top + animInfo.lastpos.y;
\r
3719 x2 = boardRect.left + animInfo.pos.x;
\r
3720 y2 = boardRect.top + animInfo.pos.y;
\r
3721 clips[num_clips++] = CreateRectRgn(MIN(x,x2), MIN(y,y2), MAX(x,x2)+squareSize, MAX(y,y2)+squareSize);
\r
3722 /* Slight kludge. The real problem is that after AnimateMove is
\r
3723 done, the position on the screen does not match lastDrawn.
\r
3724 This currently causes trouble only on e.p. captures in
\r
3725 atomic, where the piece moves to an empty square and then
\r
3726 explodes. The old and new positions both had an empty square
\r
3727 at the destination, but animation has drawn a piece there and
\r
3728 we have to remember to erase it. [HGM] moved until after setting lastDrawn */
\r
3729 lastDrawn[0][animInfo.to.y][animInfo.to.x] = animInfo.piece;
\r
3733 /* No clips? Make sure we have fullrepaint set to TRUE */
\r
3734 if (num_clips == 0)
\r
3735 fullrepaint = TRUE;
\r
3737 /* Set clipping on the memory DC */
\r
3738 if (!fullrepaint) {
\r
3739 SelectClipRgn(hdcmem, clips[0]);
\r
3740 for (x = 1; x < num_clips; x++) {
\r
3741 if (ExtSelectClipRgn(hdcmem, clips[x], RGN_OR) == ERROR)
\r
3742 abort(); // this should never ever happen!
\r
3746 /* Do all the drawing to the memory DC */
\r
3747 if(explodeInfo.radius) { // [HGM] atomic
\r
3749 int x, y, r=(explodeInfo.radius * squareSize)/100;
\r
3750 ChessSquare piece = board[explodeInfo.fromY][explodeInfo.fromX];
\r
3751 board[explodeInfo.fromY][explodeInfo.fromX] = EmptySquare; // suppress display of capturer
\r
3752 SquareToPos(explodeInfo.toY, explodeInfo.toX, &x, &y);
\r
3753 x += squareSize/2;
\r
3754 y += squareSize/2;
\r
3755 if(!fullrepaint) {
\r
3756 clips[num_clips] = CreateRectRgn(x-r, y-r, x+r, y+r);
\r
3757 ExtSelectClipRgn(hdcmem, clips[num_clips++], RGN_OR);
\r
3759 DrawGridOnDC(hdcmem);
\r
3760 DrawHighlightsOnDC(hdcmem, &highlightInfo, HIGHLIGHT_PEN);
\r
3761 DrawHighlightsOnDC(hdcmem, &premoveHighlightInfo, PREMOVE_PEN);
\r
3762 DrawBoardOnDC(hdcmem, board, tmphdc);
\r
3763 board[explodeInfo.fromY][explodeInfo.fromX] = piece;
\r
3764 oldBrush = SelectObject(hdcmem, explodeBrush);
\r
3765 Ellipse(hdcmem, x-r, y-r, x+r, y+r);
\r
3766 SelectObject(hdcmem, oldBrush);
\r
3768 DrawGridOnDC(hdcmem);
\r
3769 if(nr == 0) { // [HGM] dual: decide which highlights to draw
\r
3770 DrawHighlightsOnDC(hdcmem, &highlightInfo, HIGHLIGHT_PEN);
\r
3771 DrawHighlightsOnDC(hdcmem, &premoveHighlightInfo, PREMOVE_PEN);
\r
3773 DrawHighlightsOnDC(hdcmem, &partnerHighlightInfo, HIGHLIGHT_PEN);
\r
3774 oldPartnerHighlight = partnerHighlightInfo;
\r
3776 DrawBoardOnDC(hdcmem, board, tmphdc);
\r
3778 if(nr == 0) // [HGM] dual: markers only on left board
\r
3779 for (row = 0; row < BOARD_HEIGHT; row++) {
\r
3780 for (column = 0; column < BOARD_WIDTH; column++) {
\r
3781 if (marker[row][column]) { // marker changes only occur with full repaint!
\r
3782 HBRUSH oldBrush = SelectObject(hdcmem,
\r
3783 marker[row][column] == 2 ? markerBrush : explodeBrush);
\r
3784 SquareToPos(row, column, &x, &y);
\r
3785 Ellipse(hdcmem, x + squareSize/4, y + squareSize/4,
\r
3786 x + 3*squareSize/4, y + 3*squareSize/4);
\r
3787 SelectObject(hdcmem, oldBrush);
\r
3792 if( appData.highlightMoveWithArrow ) {
\r
3793 DrawArrowHighlight(hdcmem);
\r
3796 DrawCoordsOnDC(hdcmem);
\r
3798 CopyBoard(lastDrawn[nr], board); /* [HGM] Moved to here from end of routine, */
\r
3799 /* to make sure lastDrawn contains what is actually drawn */
\r
3801 /* Put the dragged piece back into place and draw it (out of place!) */
\r
3802 if (dragged_piece != EmptySquare) {
\r
3803 /* [HGM] or restack */
\r
3804 if(dragInfo.from.x == BOARD_LEFT-2 )
\r
3805 board[dragInfo.from.y][dragInfo.from.x+1]++;
\r
3807 if(dragInfo.from.x == BOARD_RGHT+1 )
\r
3808 board[dragInfo.from.y][dragInfo.from.x-1]++;
\r
3809 board[dragInfo.from.y][dragInfo.from.x] = dragged_piece;
\r
3810 x = dragInfo.pos.x - squareSize / 2;
\r
3811 y = dragInfo.pos.y - squareSize / 2;
\r
3812 DrawPieceOnDC(hdcmem, dragInfo.piece,
\r
3813 ((int) dragInfo.piece < (int) BlackPawn),
\r
3814 (dragInfo.from.y + dragInfo.from.x) % 2, x, y, tmphdc);
\r
3817 /* Put the animated piece back into place and draw it */
\r
3818 if (animInfo.piece != EmptySquare) {
\r
3819 board[animInfo.from.y][animInfo.from.x] = animInfo.piece;
\r
3820 x = boardRect.left + animInfo.pos.x;
\r
3821 y = boardRect.top + animInfo.pos.y;
\r
3822 DrawPieceOnDC(hdcmem, animInfo.piece,
\r
3823 ((int) animInfo.piece < (int) BlackPawn),
\r
3824 (animInfo.from.y + animInfo.from.x) % 2, x, y, tmphdc);
\r
3827 /* Release the bufferBitmap by selecting in the old bitmap
\r
3828 * and delete the memory DC
\r
3830 SelectObject(hdcmem, oldBitmap);
\r
3833 /* Set clipping on the target DC */
\r
3834 if (!fullrepaint) {
\r
3835 if(nr == 1) for (x = 0; x < num_clips; x++) { // [HGM] dual: translate clips
\r
3837 GetRgnBox(clips[x], &rect);
\r
3838 DeleteObject(clips[x]);
\r
3839 clips[x] = CreateRectRgn(rect.left + wpMain.width/2, rect.top,
\r
3840 rect.right + wpMain.width/2, rect.bottom);
\r
3842 SelectClipRgn(hdc, clips[0]);
\r
3843 for (x = 1; x < num_clips; x++) {
\r
3844 if (ExtSelectClipRgn(hdc, clips[x], RGN_OR) == ERROR)
\r
3845 abort(); // this should never ever happen!
\r
3849 /* Copy the new bitmap onto the screen in one go.
\r
3850 * This way we avoid any flickering
\r
3852 oldBitmap = SelectObject(tmphdc, bufferBitmap);
\r
3853 BitBlt(hdc, boardRect.left + twoBoards*partnerUp*wpMain.width/2, boardRect.top, // [HGM] dual
\r
3854 boardRect.right - boardRect.left,
\r
3855 boardRect.bottom - boardRect.top,
\r
3856 tmphdc, boardRect.left, boardRect.top, SRCCOPY);
\r
3857 if(saveDiagFlag) {
\r
3858 BITMAP b; int i, j=0, m, w, wb, fac=0; char *pData;
\r
3859 BITMAPINFOHEADER bih; int color[16], nrColors=0;
\r
3861 GetObject(bufferBitmap, sizeof(b), &b);
\r
3862 if(pData = malloc(b.bmWidthBytes*b.bmHeight + 10000)) {
\r
3863 bih.biSize = sizeof(BITMAPINFOHEADER);
\r
3864 bih.biWidth = b.bmWidth;
\r
3865 bih.biHeight = b.bmHeight;
\r
3867 bih.biBitCount = b.bmBitsPixel;
\r
3868 bih.biCompression = 0;
\r
3869 bih.biSizeImage = b.bmWidthBytes*b.bmHeight;
\r
3870 bih.biXPelsPerMeter = 0;
\r
3871 bih.biYPelsPerMeter = 0;
\r
3872 bih.biClrUsed = 0;
\r
3873 bih.biClrImportant = 0;
\r
3874 // fprintf(diagFile, "t=%d\nw=%d\nh=%d\nB=%d\nP=%d\nX=%d\n",
\r
3875 // b.bmType, b.bmWidth, b.bmHeight, b.bmWidthBytes, b.bmPlanes, b.bmBitsPixel);
\r
3876 GetDIBits(tmphdc,bufferBitmap,0,b.bmHeight,pData,(BITMAPINFO*)&bih,DIB_RGB_COLORS);
\r
3877 // fprintf(diagFile, "%8x\n", (int) pData);
\r
3879 wb = b.bmWidthBytes;
\r
3881 for(i=0; i<wb*(b.bmHeight - boardRect.top + OUTER_MARGIN)>>2; i++) {
\r
3882 int k = ((int*) pData)[i];
\r
3883 for(j=0; j<nrColors; j++) if(color[j] == k) break;
\r
3884 if(j >= 16) break;
\r
3886 if(j >= nrColors) nrColors = j+1;
\r
3888 if(j<16) { // 16 colors is enough. Compress to 4 bits per pixel
\r
3890 for(i=0; i<b.bmHeight - boardRect.top + OUTER_MARGIN; i++) {
\r
3891 for(w=0; w<(wb>>2); w+=2) {
\r
3892 int k = ((int*) pData)[(wb*i>>2) + w];
\r
3893 for(j=0; j<nrColors; j++) if(color[j] == k) break;
\r
3894 k = ((int*) pData)[(wb*i>>2) + w + 1];
\r
3895 for(m=0; m<nrColors; m++) if(color[m] == k) break;
\r
3896 pData[p++] = m | j<<4;
\r
3898 while(p&3) pData[p++] = 0;
\r
3901 wb = ((wb+31)>>5)<<2;
\r
3903 // write BITMAPFILEHEADER
\r
3904 fprintf(diagFile, "BM");
\r
3905 fputDW(diagFile, wb*(b.bmHeight - boardRect.top + OUTER_MARGIN)+0x36 + (fac?64:0));
\r
3906 fputDW(diagFile, 0);
\r
3907 fputDW(diagFile, 0x36 + (fac?64:0));
\r
3908 // write BITMAPINFOHEADER
\r
3909 fputDW(diagFile, 40);
\r
3910 fputDW(diagFile, b.bmWidth);
\r
3911 fputDW(diagFile, b.bmHeight - boardRect.top + OUTER_MARGIN);
\r
3912 if(fac) fputDW(diagFile, 0x040001); // planes and bits/pixel
\r
3913 else fputDW(diagFile, 0x200001); // planes and bits/pixel
\r
3914 fputDW(diagFile, 0);
\r
3915 fputDW(diagFile, 0);
\r
3916 fputDW(diagFile, 0);
\r
3917 fputDW(diagFile, 0);
\r
3918 fputDW(diagFile, 0);
\r
3919 fputDW(diagFile, 0);
\r
3920 // write color table
\r
3922 for(i=0; i<16; i++) fputDW(diagFile, color[i]);
\r
3923 // write bitmap data
\r
3924 for(i=0; i<wb*(b.bmHeight - boardRect.top + OUTER_MARGIN); i++)
\r
3925 fputc(pData[i], diagFile);
\r
3930 SelectObject(tmphdc, oldBitmap);
\r
3932 /* Massive cleanup */
\r
3933 for (x = 0; x < num_clips; x++)
\r
3934 DeleteObject(clips[x]);
\r
3937 DeleteObject(bufferBitmap);
\r
3940 ReleaseDC(hwndMain, hdc);
\r
3942 if (lastDrawnFlipView != flipView && nr == 0) {
\r
3944 CheckMenuItem(GetMenu(hwndMain),IDM_FlipView, MF_BYCOMMAND|MF_CHECKED);
\r
3946 CheckMenuItem(GetMenu(hwndMain),IDM_FlipView, MF_BYCOMMAND|MF_UNCHECKED);
\r
3949 /* CopyBoard(lastDrawn, board);*/
\r
3950 lastDrawnHighlight = highlightInfo;
\r
3951 lastDrawnPremove = premoveHighlightInfo;
\r
3952 lastDrawnFlipView = flipView;
\r
3953 lastDrawnValid[nr] = 1;
\r
3956 /* [HGM] diag: Save the current board display to the given open file and close the file */
\r
3961 saveDiagFlag = 1; diagFile = f;
\r
3962 HDCDrawPosition(NULL, TRUE, NULL);
\r
3970 /*---------------------------------------------------------------------------*\
\r
3971 | CLIENT PAINT PROCEDURE
\r
3972 | This is the main event-handler for the WM_PAINT message.
\r
3974 \*---------------------------------------------------------------------------*/
\r
3976 PaintProc(HWND hwnd)
\r
3982 if((hdc = BeginPaint(hwnd, &ps))) {
\r
3983 if (IsIconic(hwnd)) {
\r
3984 DrawIcon(hdc, 2, 2, iconCurrent);
\r
3986 if (!appData.monoMode) {
\r
3987 SelectPalette(hdc, hPal, FALSE);
\r
3988 RealizePalette(hdc);
\r
3990 HDCDrawPosition(hdc, 1, NULL);
\r
3991 if(twoBoards) { // [HGM] dual: also redraw other board in other orientation
\r
3992 flipView = !flipView; partnerUp = !partnerUp;
\r
3993 HDCDrawPosition(hdc, 1, NULL);
\r
3994 flipView = !flipView; partnerUp = !partnerUp;
\r
3997 SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);
\r
3998 ExtTextOut(hdc, messageRect.left, messageRect.top,
\r
3999 ETO_CLIPPED|ETO_OPAQUE,
\r
4000 &messageRect, messageText, strlen(messageText), NULL);
\r
4001 SelectObject(hdc, oldFont);
\r
4002 DisplayBothClocks();
\r
4005 EndPaint(hwnd,&ps);
\r
4013 * If the user selects on a border boundary, return -1; if off the board,
\r
4014 * return -2. Otherwise map the event coordinate to the square.
\r
4015 * The offset boardRect.left or boardRect.top must already have been
\r
4016 * subtracted from x.
\r
4018 int EventToSquare(x, limit)
\r
4026 if ((x % (squareSize + lineGap)) >= squareSize)
\r
4028 x /= (squareSize + lineGap);
\r
4040 DropEnable dropEnables[] = {
\r
4041 { 'P', DP_Pawn, N_("Pawn") },
\r
4042 { 'N', DP_Knight, N_("Knight") },
\r
4043 { 'B', DP_Bishop, N_("Bishop") },
\r
4044 { 'R', DP_Rook, N_("Rook") },
\r
4045 { 'Q', DP_Queen, N_("Queen") },
\r
4049 SetupDropMenu(HMENU hmenu)
\r
4051 int i, count, enable;
\r
4053 extern char white_holding[], black_holding[];
\r
4054 char item[MSG_SIZ];
\r
4056 for (i=0; i<sizeof(dropEnables)/sizeof(DropEnable); i++) {
\r
4057 p = strchr(gameMode == IcsPlayingWhite ? white_holding : black_holding,
\r
4058 dropEnables[i].piece);
\r
4060 while (p && *p++ == dropEnables[i].piece) count++;
\r
4061 snprintf(item, MSG_SIZ, "%s %d", T_(dropEnables[i].name), count);
\r
4062 enable = count > 0 || !appData.testLegality
\r
4063 /*!!temp:*/ || (gameInfo.variant == VariantCrazyhouse
\r
4064 && !appData.icsActive);
\r
4065 ModifyMenu(hmenu, dropEnables[i].command,
\r
4066 MF_BYCOMMAND | (enable ? MF_ENABLED : MF_GRAYED) | MF_STRING,
\r
4067 dropEnables[i].command, item);
\r
4071 void DragPieceBegin(int x, int y, Boolean instantly)
\r
4073 dragInfo.lastpos.x = boardRect.left + x;
\r
4074 dragInfo.lastpos.y = boardRect.top + y;
\r
4075 if(instantly) dragInfo.pos = dragInfo.lastpos;
\r
4076 dragInfo.from.x = fromX;
\r
4077 dragInfo.from.y = fromY;
\r
4078 dragInfo.piece = boards[currentMove][fromY][fromX];
\r
4079 dragInfo.start = dragInfo.from;
\r
4080 SetCapture(hwndMain);
\r
4083 void DragPieceEnd(int x, int y)
\r
4086 dragInfo.start.x = dragInfo.start.y = -1;
\r
4087 dragInfo.from = dragInfo.start;
\r
4088 dragInfo.pos = dragInfo.lastpos = dragInfo.start;
\r
4091 void ChangeDragPiece(ChessSquare piece)
\r
4093 dragInfo.piece = piece;
\r
4096 /* Event handler for mouse messages */
\r
4098 MouseEvent(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
\r
4102 static int recursive = 0;
\r
4104 BOOLEAN forceFullRepaint = IsFullRepaintPreferrable(); /* [AS] */
\r
4107 if (message == WM_MBUTTONUP) {
\r
4108 /* Hideous kludge to fool TrackPopupMenu into paying attention
\r
4109 to the middle button: we simulate pressing the left button too!
\r
4111 PostMessage(hwnd, WM_LBUTTONDOWN, wParam, lParam);
\r
4112 PostMessage(hwnd, WM_LBUTTONUP, wParam, lParam);
\r
4118 pt.x = LOWORD(lParam);
\r
4119 pt.y = HIWORD(lParam);
\r
4120 x = EventToSquare(pt.x - boardRect.left, BOARD_WIDTH);
\r
4121 y = EventToSquare(pt.y - boardRect.top, BOARD_HEIGHT);
\r
4122 if (!flipView && y >= 0) {
\r
4123 y = BOARD_HEIGHT - 1 - y;
\r
4125 if (flipView && x >= 0) {
\r
4126 x = BOARD_WIDTH - 1 - x;
\r
4129 shiftKey = GetKeyState(VK_SHIFT) < 0; // [HGM] remember last shift status
\r
4131 switch (message) {
\r
4132 case WM_LBUTTONDOWN:
\r
4133 if (PtInRect((LPRECT) &whiteRect, pt)) {
\r
4134 ClockClick(flipClock);
\r
4135 } else if (PtInRect((LPRECT) &blackRect, pt)) {
\r
4136 ClockClick(!flipClock);
\r
4138 dragInfo.start.x = dragInfo.start.y = -1;
\r
4139 dragInfo.from = dragInfo.start;
\r
4140 if(fromX == -1 && frozen) { // not sure where this is for
\r
4141 fromX = fromY = -1;
\r
4142 DrawPosition(forceFullRepaint || FALSE, NULL); /* [AS] */
\r
4145 LeftClick(Press, pt.x - boardRect.left, pt.y - boardRect.top);
\r
4146 DrawPosition(TRUE, NULL);
\r
4149 case WM_LBUTTONUP:
\r
4150 LeftClick(Release, pt.x - boardRect.left, pt.y - boardRect.top);
\r
4151 DrawPosition(TRUE, NULL);
\r
4154 case WM_MOUSEMOVE:
\r
4155 if(SeekGraphClick(Press, pt.x - boardRect.left, pt.y - boardRect.top, 1)) break;
\r
4156 if(PromoScroll(pt.x - boardRect.left, pt.y - boardRect.top)) break;
\r
4157 MovePV(pt.x - boardRect.left, pt.y - boardRect.top, boardRect.bottom - boardRect.top);
\r
4158 if ((appData.animateDragging || appData.highlightDragging)
\r
4159 && (wParam & MK_LBUTTON)
\r
4160 && dragInfo.from.x >= 0)
\r
4162 BOOL full_repaint = FALSE;
\r
4164 if (appData.animateDragging) {
\r
4165 dragInfo.pos = pt;
\r
4167 if (appData.highlightDragging) {
\r
4168 SetHighlights(fromX, fromY, x, y);
\r
4169 if( IsDrawArrowEnabled() && (x < 0 || x >= BOARD_WIDTH || y < 0 || y >= BOARD_HEIGHT) ) {
\r
4170 full_repaint = TRUE;
\r
4174 DrawPosition( full_repaint, NULL);
\r
4176 dragInfo.lastpos = dragInfo.pos;
\r
4180 case WM_MOUSEWHEEL: // [DM]
\r
4181 { static int lastDir = 0; // [HGM] build in some hysteresis to avoid spurious events
\r
4182 /* Mouse Wheel is being rolled forward
\r
4183 * Play moves forward
\r
4185 if((short)HIWORD(wParam) < 0 && currentMove < forwardMostMove)
\r
4186 { if(lastDir == 1) ForwardEvent(); else lastDir = 1; } // [HGM] suppress first event in direction
\r
4187 /* Mouse Wheel is being rolled backward
\r
4188 * Play moves backward
\r
4190 if((short)HIWORD(wParam) > 0 && currentMove > backwardMostMove)
\r
4191 { if(lastDir == -1) BackwardEvent(); else lastDir = -1; }
\r
4195 case WM_MBUTTONUP:
\r
4196 case WM_RBUTTONUP:
\r
4198 RightClick(Release, pt.x - boardRect.left, pt.y - boardRect.top, &fromX, &fromY);
\r
4201 case WM_MBUTTONDOWN:
\r
4202 case WM_RBUTTONDOWN:
\r
4205 fromX = fromY = -1;
\r
4206 dragInfo.pos.x = dragInfo.pos.y = -1;
\r
4207 dragInfo.start.x = dragInfo.start.y = -1;
\r
4208 dragInfo.from = dragInfo.start;
\r
4209 dragInfo.lastpos = dragInfo.pos;
\r
4210 if (appData.highlightDragging) {
\r
4211 ClearHighlights();
\r
4214 /* [HGM] right mouse button in clock area edit-game mode ups clock */
\r
4215 if (PtInRect((LPRECT) &whiteRect, pt)) {
\r
4216 if (gameMode == EditGame || GetKeyState(VK_SHIFT) < 0) AdjustClock(flipClock, 1);
\r
4217 } else if (PtInRect((LPRECT) &blackRect, pt)) {
\r
4218 if (gameMode == EditGame || GetKeyState(VK_SHIFT) < 0) AdjustClock(!flipClock, 1);
\r
4222 DrawPosition(TRUE, NULL);
\r
4224 menuNr = RightClick(Press, pt.x - boardRect.left, pt.y - boardRect.top, &fromX, &fromY);
\r
4227 if (message == WM_MBUTTONDOWN) {
\r
4228 buttonCount = 3; /* even if system didn't think so */
\r
4229 if (wParam & MK_SHIFT)
\r
4230 MenuPopup(hwnd, pt, LoadMenu(hInst, "BlackPieceMenu"), -1);
\r
4232 MenuPopup(hwnd, pt, LoadMenu(hInst, "WhitePieceMenu"), -1);
\r
4233 } else { /* message == WM_RBUTTONDOWN */
\r
4234 /* Just have one menu, on the right button. Windows users don't
\r
4235 think to try the middle one, and sometimes other software steals
\r
4236 it, or it doesn't really exist. */
\r
4237 if(gameInfo.variant != VariantShogi)
\r
4238 MenuPopup(hwnd, pt, LoadMenu(hInst, "PieceMenu"), -1);
\r
4240 MenuPopup(hwnd, pt, LoadMenu(hInst, "ShogiPieceMenu"), -1);
\r
4244 SetCapture(hwndMain);
4247 hmenu = LoadMenu(hInst, "DropPieceMenu");
\r
4248 SetupDropMenu(hmenu);
\r
4249 MenuPopup(hwnd, pt, hmenu, -1);
\r
4259 /* Preprocess messages for buttons in main window */
\r
4261 ButtonProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
\r
4263 int id = GetWindowLongPtr(hwnd, GWLP_ID);
\r
4266 for (i=0; i<N_BUTTONS; i++) {
\r
4267 if (buttonDesc[i].id == id) break;
\r
4269 if (i == N_BUTTONS) return 0;
\r
4270 switch (message) {
\r
4275 dir = (wParam == VK_LEFT) ? -1 : 1;
\r
4276 SetFocus(buttonDesc[(i + dir + N_BUTTONS) % N_BUTTONS].hwnd);
\r
4283 SendMessage(hwndMain, WM_COMMAND, MAKEWPARAM(buttonDesc[i].id, 0), 0);
\r
4286 if (appData.icsActive && (isalpha((char)wParam) || wParam == '0')) {
\r
4287 // [HGM] movenum: only letters or leading zero should go to ICS input
\r
4288 HWND h = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
4289 if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);
\r
4291 SendMessage(h, WM_CHAR, wParam, lParam);
\r
4293 } else if (isalpha((char)wParam) || isdigit((char)wParam)){
\r
4294 TypeInEvent((char)wParam);
\r
4300 return CallWindowProc(buttonDesc[i].wndproc, hwnd, message, wParam, lParam);
\r
4303 /* Process messages for Promotion dialog box */
\r
4305 Promotion(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
4309 switch (message) {
\r
4310 case WM_INITDIALOG: /* message: initialize dialog box */
\r
4311 /* Center the dialog over the application window */
\r
4312 CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));
\r
4313 Translate(hDlg, DLG_PromotionKing);
\r
4314 ShowWindow(GetDlgItem(hDlg, PB_King),
\r
4315 (!appData.testLegality || gameInfo.variant == VariantSuicide ||
\r
4316 gameInfo.variant == VariantSpartan && !WhiteOnMove(currentMove) ||
\r
4317 gameInfo.variant == VariantGiveaway || gameInfo.variant == VariantSuper ) ?
\r
4318 SW_SHOW : SW_HIDE);
\r
4319 /* [HGM] Only allow C & A promotions if these pieces are defined */
\r
4320 ShowWindow(GetDlgItem(hDlg, PB_Archbishop),
\r
4321 ((PieceToChar(WhiteAngel) >= 'A' && WhiteOnMove(currentMove) &&
\r
4322 PieceToChar(WhiteAngel) != '~') ||
\r
4323 (PieceToChar(BlackAngel) >= 'A' && !WhiteOnMove(currentMove) &&
\r
4324 PieceToChar(BlackAngel) != '~') ) ?
\r
4325 SW_SHOW : SW_HIDE);
\r
4326 ShowWindow(GetDlgItem(hDlg, PB_Chancellor),
\r
4327 ((PieceToChar(WhiteMarshall) >= 'A' && WhiteOnMove(currentMove) &&
\r
4328 PieceToChar(WhiteMarshall) != '~') ||
\r
4329 (PieceToChar(BlackMarshall) >= 'A' && !WhiteOnMove(currentMove) &&
\r
4330 PieceToChar(BlackMarshall) != '~') ) ?
\r
4331 SW_SHOW : SW_HIDE);
\r
4332 /* [HGM] Hide B & R button in Shogi, use Q as promote, N as defer */
\r
4333 ShowWindow(GetDlgItem(hDlg, PB_Rook),
\r
4334 gameInfo.variant != VariantShogi ?
\r
4335 SW_SHOW : SW_HIDE);
\r
4336 ShowWindow(GetDlgItem(hDlg, PB_Bishop),
\r
4337 gameInfo.variant != VariantShogi ?
\r
4338 SW_SHOW : SW_HIDE);
\r
4339 if(gameInfo.variant == VariantShogi) {
\r
4340 SetDlgItemText(hDlg, PB_Queen, "YES");
\r
4341 SetDlgItemText(hDlg, PB_Knight, "NO");
\r
4342 SetWindowText(hDlg, "Promote?");
\r
4344 ShowWindow(GetDlgItem(hDlg, IDC_Centaur),
\r
4345 gameInfo.variant == VariantSuper ?
\r
4346 SW_SHOW : SW_HIDE);
\r
4349 case WM_COMMAND: /* message: received a command */
\r
4350 switch (LOWORD(wParam)) {
\r
4352 EndDialog(hDlg, TRUE); /* Exit the dialog */
\r
4353 ClearHighlights();
\r
4354 DrawPosition(FALSE, NULL);
\r
4357 promoChar = gameInfo.variant == VariantSuper ? PieceToChar(BlackSilver) : PieceToChar(BlackKing);
\r
4360 promoChar = gameInfo.variant == VariantShogi ? '+' : ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteQueen : BlackQueen));
\r
4363 promoChar = ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteRook : BlackRook));
\r
4364 if(gameInfo.variant == VariantSpartan && !WhiteOnMove(currentMove)) promoChar = PieceToChar(BlackDragon);
\r
4367 promoChar = ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteBishop : BlackBishop));
\r
4368 if(gameInfo.variant == VariantSpartan && !WhiteOnMove(currentMove)) promoChar = PieceToChar(BlackAlfil);
\r
4370 case PB_Chancellor:
\r
4371 promoChar = ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteMarshall : BlackMarshall));
\r
4373 case PB_Archbishop:
\r
4374 promoChar = ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteAngel : BlackAngel));
\r
4377 promoChar = gameInfo.variant == VariantShogi ? '=' : PieceToChar(WhiteOnMove(currentMove) ? WhiteKnight : BlackKnight);
\r
4382 if(promoChar == '.') return FALSE; // invalid piece chosen
\r
4383 EndDialog(hDlg, TRUE); /* Exit the dialog */
\r
4384 UserMoveEvent(fromX, fromY, toX, toY, promoChar);
\r
4385 fromX = fromY = -1;
\r
4386 if (!appData.highlightLastMove) {
\r
4387 ClearHighlights();
\r
4388 DrawPosition(FALSE, NULL);
\r
4395 /* Pop up promotion dialog */
\r
4397 PromotionPopup(HWND hwnd)
\r
4401 lpProc = MakeProcInstance((FARPROC)Promotion, hInst);
\r
4402 DialogBox(hInst, MAKEINTRESOURCE(DLG_PromotionKing),
\r
4403 hwnd, (DLGPROC)lpProc);
\r
4404 FreeProcInstance(lpProc);
\r
4410 DrawPosition(TRUE, NULL);
\r
4411 PromotionPopup(hwndMain);
\r
4414 /* Toggle ShowThinking */
\r
4416 ToggleShowThinking()
\r
4418 appData.showThinking = !appData.showThinking;
\r
4419 ShowThinkingEvent();
\r
4423 LoadGameDialog(HWND hwnd, char* title)
\r
4427 char fileTitle[MSG_SIZ];
\r
4428 f = OpenFileDialog(hwnd, "rb", "",
\r
4429 appData.oldSaveStyle ? "gam" : "pgn",
\r
4431 title, &number, fileTitle, NULL);
\r
4433 cmailMsgLoaded = FALSE;
\r
4434 if (number == 0) {
\r
4435 int error = GameListBuild(f);
\r
4437 DisplayError(_("Cannot build game list"), error);
\r
4438 } else if (!ListEmpty(&gameList) &&
\r
4439 ((ListGame *) gameList.tailPred)->number > 1) {
\r
4440 GameListPopUp(f, fileTitle);
\r
4443 GameListDestroy();
\r
4446 LoadGame(f, number, fileTitle, FALSE);
\r
4450 int get_term_width()
\r
4455 HFONT hfont, hold_font;
\r
4460 hText = GetDlgItem(hwndConsole, OPT_ConsoleText);
\r
4464 // get the text metrics
\r
4465 hdc = GetDC(hText);
\r
4466 lf = font[boardSize][CONSOLE_FONT]->lf;
\r
4467 if (consoleCF.dwEffects & CFE_BOLD)
\r
4468 lf.lfWeight = FW_BOLD;
\r
4469 if (consoleCF.dwEffects & CFE_ITALIC)
\r
4470 lf.lfItalic = TRUE;
\r
4471 if (consoleCF.dwEffects & CFE_STRIKEOUT)
\r
4472 lf.lfStrikeOut = TRUE;
\r
4473 if (consoleCF.dwEffects & CFE_UNDERLINE)
\r
4474 lf.lfUnderline = TRUE;
\r
4475 hfont = CreateFontIndirect(&lf);
\r
4476 hold_font = SelectObject(hdc, hfont);
\r
4477 GetTextMetrics(hdc, &tm);
\r
4478 SelectObject(hdc, hold_font);
\r
4479 DeleteObject(hfont);
\r
4480 ReleaseDC(hText, hdc);
\r
4482 // get the rectangle
\r
4483 SendMessage(hText, EM_GETRECT, 0, (LPARAM)&rc);
\r
4485 return (rc.right-rc.left) / tm.tmAveCharWidth;
\r
4488 void UpdateICSWidth(HWND hText)
\r
4490 LONG old_width, new_width;
\r
4492 new_width = get_term_width(hText, FALSE);
\r
4493 old_width = GetWindowLongPtr(hText, GWLP_USERDATA);
\r
4494 if (new_width != old_width)
\r
4496 ics_update_width(new_width);
\r
4497 SetWindowLongPtr(hText, GWLP_USERDATA, new_width);
\r
4502 ChangedConsoleFont()
\r
4505 CHARRANGE tmpsel, sel;
\r
4506 MyFont *f = font[boardSize][CONSOLE_FONT];
\r
4507 HWND hText = GetDlgItem(hwndConsole, OPT_ConsoleText);
\r
4508 HWND hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
4511 cfmt.cbSize = sizeof(CHARFORMAT);
\r
4512 cfmt.dwMask = CFM_FACE|CFM_SIZE|CFM_CHARSET;
\r
4513 safeStrCpy(cfmt.szFaceName, font[boardSize][CONSOLE_FONT]->mfp.faceName,
\r
4514 sizeof(cfmt.szFaceName)/sizeof(cfmt.szFaceName[0]) );
\r
4515 /* yHeight is expressed in twips. A twip is 1/20 of a font's point
\r
4516 * size. This was undocumented in the version of MSVC++ that I had
\r
4517 * when I wrote the code, but is apparently documented now.
\r
4519 cfmt.yHeight = (int)(f->mfp.pointSize * 20.0 + 0.5);
\r
4520 cfmt.bCharSet = f->lf.lfCharSet;
\r
4521 cfmt.bPitchAndFamily = f->lf.lfPitchAndFamily;
\r
4522 SendMessage(hText, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &cfmt);
\r
4523 SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &cfmt);
\r
4524 /* Why are the following seemingly needed too? */
\r
4525 SendMessage(hText, EM_SETCHARFORMAT, SCF_DEFAULT, (LPARAM) &cfmt);
\r
4526 SendMessage(hInput, EM_SETCHARFORMAT, SCF_DEFAULT, (LPARAM) &cfmt);
\r
4527 SendMessage(hText, EM_EXGETSEL, 0, (LPARAM)&sel);
\r
4529 tmpsel.cpMax = -1; /*999999?*/
\r
4530 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&tmpsel);
\r
4531 SendMessage(hText, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cfmt);
\r
4532 /* Trying putting this here too. It still seems to tickle a RichEdit
\r
4533 * bug: sometimes RichEdit indents the first line of a paragraph too.
\r
4535 paraf.cbSize = sizeof(paraf);
\r
4536 paraf.dwMask = PFM_OFFSET | PFM_STARTINDENT;
\r
4537 paraf.dxStartIndent = 0;
\r
4538 paraf.dxOffset = WRAP_INDENT;
\r
4539 SendMessage(hText, EM_SETPARAFORMAT, 0, (LPARAM) ¶f);
\r
4540 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
4541 UpdateICSWidth(hText);
\r
4544 /*---------------------------------------------------------------------------*\
\r
4546 * Window Proc for main window
\r
4548 \*---------------------------------------------------------------------------*/
\r
4550 /* Process messages for main window, etc. */
\r
4552 WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
\r
4555 int wmId, wmEvent;
\r
4559 char fileTitle[MSG_SIZ];
\r
4560 char buf[MSG_SIZ];
\r
4561 static SnapData sd;
\r
4563 switch (message) {
\r
4565 case WM_PAINT: /* message: repaint portion of window */
\r
4569 case WM_ERASEBKGND:
\r
4570 if (IsIconic(hwnd)) {
\r
4571 /* Cheat; change the message */
\r
4572 return (DefWindowProc(hwnd, WM_ICONERASEBKGND, wParam, lParam));
\r
4574 return (DefWindowProc(hwnd, message, wParam, lParam));
\r
4578 case WM_LBUTTONDOWN:
\r
4579 case WM_MBUTTONDOWN:
\r
4580 case WM_RBUTTONDOWN:
\r
4581 case WM_LBUTTONUP:
\r
4582 case WM_MBUTTONUP:
\r
4583 case WM_RBUTTONUP:
\r
4584 case WM_MOUSEMOVE:
\r
4585 case WM_MOUSEWHEEL:
\r
4586 MouseEvent(hwnd, message, wParam, lParam);
\r
4589 JAWS_KB_NAVIGATION
\r
4593 JAWS_ALT_INTERCEPT
\r
4595 if (appData.icsActive && ((char)wParam == '\r' || (char)wParam > ' ' && !((char)wParam >= '1' && (char)wParam <= '9'))) {
\r
4596 // [HGM] movenum: for non-zero digits we always do type-in dialog
\r
4597 HWND h = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
4598 if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);
\r
4600 SendMessage(h, message, wParam, lParam);
\r
4601 } else if(lParam != KF_REPEAT) {
\r
4602 if (isalpha((char)wParam) || isdigit((char)wParam)) {
\r
4603 TypeInEvent((char)wParam);
\r
4604 } else if((char)wParam == 003) CopyGameToClipboard();
\r
4605 else if((char)wParam == 026) PasteGameOrFENFromClipboard();
\r
4610 case WM_PALETTECHANGED:
\r
4611 if (hwnd != (HWND)wParam && !appData.monoMode) {
\r
4613 HDC hdc = GetDC(hwndMain);
\r
4614 SelectPalette(hdc, hPal, TRUE);
\r
4615 nnew = RealizePalette(hdc);
\r
4617 paletteChanged = TRUE;
\r
4618 InvalidateRect(hwnd, &boardRect, FALSE);
\r
4620 ReleaseDC(hwnd, hdc);
\r
4624 case WM_QUERYNEWPALETTE:
\r
4625 if (!appData.monoMode /*&& paletteChanged*/) {
\r
4627 HDC hdc = GetDC(hwndMain);
\r
4628 paletteChanged = FALSE;
\r
4629 SelectPalette(hdc, hPal, FALSE);
\r
4630 nnew = RealizePalette(hdc);
\r
4632 InvalidateRect(hwnd, &boardRect, FALSE);
\r
4634 ReleaseDC(hwnd, hdc);
\r
4639 case WM_COMMAND: /* message: command from application menu */
\r
4640 wmId = LOWORD(wParam);
\r
4641 wmEvent = HIWORD(wParam);
\r
4646 SAY("new game enter a move to play against the computer with white");
\r
4649 case IDM_NewGameFRC:
\r
4650 if( NewGameFRC() == 0 ) {
\r
4655 case IDM_NewVariant:
\r
4656 NewVariantPopup(hwnd);
\r
4659 case IDM_LoadGame:
\r
4660 LoadGameDialog(hwnd, _("Load Game from File"));
\r
4663 case IDM_LoadNextGame:
\r
4667 case IDM_LoadPrevGame:
\r
4671 case IDM_ReloadGame:
\r
4675 case IDM_LoadPosition:
\r
4676 if (gameMode == AnalyzeMode || gameMode == AnalyzeFile) {
\r
4677 Reset(FALSE, TRUE);
\r
4680 f = OpenFileDialog(hwnd, "rb", "",
\r
4681 appData.oldSaveStyle ? "pos" : "fen",
\r
4683 _("Load Position from File"), &number, fileTitle, NULL);
\r
4685 LoadPosition(f, number, fileTitle);
\r
4689 case IDM_LoadNextPosition:
\r
4690 ReloadPosition(1);
\r
4693 case IDM_LoadPrevPosition:
\r
4694 ReloadPosition(-1);
\r
4697 case IDM_ReloadPosition:
\r
4698 ReloadPosition(0);
\r
4701 case IDM_SaveGame:
\r
4702 defName = DefaultFileName(appData.oldSaveStyle ? "gam" : "pgn");
\r
4703 f = OpenFileDialog(hwnd, "a", defName,
\r
4704 appData.oldSaveStyle ? "gam" : "pgn",
\r
4706 _("Save Game to File"), NULL, fileTitle, NULL);
\r
4708 SaveGame(f, 0, "");
\r
4712 case IDM_SavePosition:
\r
4713 defName = DefaultFileName(appData.oldSaveStyle ? "pos" : "fen");
\r
4714 f = OpenFileDialog(hwnd, "a", defName,
\r
4715 appData.oldSaveStyle ? "pos" : "fen",
\r
4717 _("Save Position to File"), NULL, fileTitle, NULL);
\r
4719 SavePosition(f, 0, "");
\r
4723 case IDM_SaveDiagram:
\r
4724 defName = "diagram";
\r
4725 f = OpenFileDialog(hwnd, "wb", defName,
\r
4728 _("Save Diagram to File"), NULL, fileTitle, NULL);
\r
4734 case IDM_CopyGame:
\r
4735 CopyGameToClipboard();
\r
4738 case IDM_PasteGame:
\r
4739 PasteGameFromClipboard();
\r
4742 case IDM_CopyGameListToClipboard:
\r
4743 CopyGameListToClipboard();
\r
4746 /* [AS] Autodetect FEN or PGN data */
\r
4747 case IDM_PasteAny:
\r
4748 PasteGameOrFENFromClipboard();
\r
4751 /* [AS] Move history */
\r
4752 case IDM_ShowMoveHistory:
\r
4753 if( MoveHistoryIsUp() ) {
\r
4754 MoveHistoryPopDown();
\r
4757 MoveHistoryPopUp();
\r
4761 /* [AS] Eval graph */
\r
4762 case IDM_ShowEvalGraph:
\r
4763 if( EvalGraphIsUp() ) {
\r
4764 EvalGraphPopDown();
\r
4768 SetFocus(hwndMain);
\r
4772 /* [AS] Engine output */
\r
4773 case IDM_ShowEngineOutput:
\r
4774 if( EngineOutputIsUp() ) {
\r
4775 EngineOutputPopDown();
\r
4778 EngineOutputPopUp();
\r
4782 /* [AS] User adjudication */
\r
4783 case IDM_UserAdjudication_White:
\r
4784 UserAdjudicationEvent( +1 );
\r
4787 case IDM_UserAdjudication_Black:
\r
4788 UserAdjudicationEvent( -1 );
\r
4791 case IDM_UserAdjudication_Draw:
\r
4792 UserAdjudicationEvent( 0 );
\r
4795 /* [AS] Game list options dialog */
\r
4796 case IDM_GameListOptions:
\r
4797 GameListOptions();
\r
4804 case IDM_CopyPosition:
\r
4805 CopyFENToClipboard();
\r
4808 case IDM_PastePosition:
\r
4809 PasteFENFromClipboard();
\r
4812 case IDM_MailMove:
\r
4816 case IDM_ReloadCMailMsg:
\r
4817 Reset(TRUE, TRUE);
\r
4818 ReloadCmailMsgEvent(FALSE);
\r
4821 case IDM_Minimize:
\r
4822 ShowWindow(hwnd, SW_MINIMIZE);
\r
4829 case IDM_MachineWhite:
\r
4830 MachineWhiteEvent();
\r
4832 * refresh the tags dialog only if it's visible
\r
4834 if (gameMode == MachinePlaysWhite && IsWindowVisible(editTagsDialog)) {
\r
4836 tags = PGNTags(&gameInfo);
\r
4837 TagsPopUp(tags, CmailMsg());
\r
4840 SAY("computer starts playing white");
\r
4843 case IDM_MachineBlack:
\r
4844 MachineBlackEvent();
\r
4846 * refresh the tags dialog only if it's visible
\r
4848 if (gameMode == MachinePlaysBlack && IsWindowVisible(editTagsDialog)) {
\r
4850 tags = PGNTags(&gameInfo);
\r
4851 TagsPopUp(tags, CmailMsg());
\r
4854 SAY("computer starts playing black");
\r
4857 case IDM_Match: // [HGM] match: flows into next case, after setting Match Mode and nr of Games
\r
4858 MatchEvent(2); // distinguish from command-line-triggered case (matchMode=1)
\r
4861 case IDM_TwoMachines:
\r
4862 TwoMachinesEvent();
\r
4864 * refresh the tags dialog only if it's visible
\r
4866 if (gameMode == TwoMachinesPlay && IsWindowVisible(editTagsDialog)) {
\r
4868 tags = PGNTags(&gameInfo);
\r
4869 TagsPopUp(tags, CmailMsg());
\r
4872 SAY("computer starts playing both sides");
\r
4875 case IDM_AnalysisMode:
\r
4876 if (!first.analysisSupport) {
\r
4877 snprintf(buf, MSG_SIZ, _("%s does not support analysis"), first.tidy);
\r
4878 DisplayError(buf, 0);
\r
4880 SAY("analyzing current position");
\r
4881 /* [DM] icsEngineAnlyze [HGM] Why is this front-end??? */
\r
4882 if (appData.icsActive) {
\r
4883 if (gameMode != IcsObserving) {
\r
4884 snprintf(buf, MSG_SIZ, "You are not observing a game");
\r
4885 DisplayError(buf, 0);
\r
4886 /* secure check */
\r
4887 if (appData.icsEngineAnalyze) {
\r
4888 if (appData.debugMode)
\r
4889 fprintf(debugFP, "Found unexpected active ICS engine analyze \n");
\r
4890 ExitAnalyzeMode();
\r
4896 /* if enable, user want disable icsEngineAnalyze */
\r
4897 if (appData.icsEngineAnalyze) {
\r
4898 ExitAnalyzeMode();
\r
4902 appData.icsEngineAnalyze = TRUE;
\r
4903 if (appData.debugMode) fprintf(debugFP, "ICS engine analyze starting...\n");
\r
4906 if (!appData.showThinking) ToggleShowThinking();
\r
4907 AnalyzeModeEvent();
\r
4911 case IDM_AnalyzeFile:
\r
4912 if (!first.analysisSupport) {
\r
4913 char buf[MSG_SIZ];
\r
4914 snprintf(buf, MSG_SIZ, _("%s does not support analysis"), first.tidy);
\r
4915 DisplayError(buf, 0);
\r
4917 if (!appData.showThinking) ToggleShowThinking();
\r
4918 AnalyzeFileEvent();
\r
4919 LoadGameDialog(hwnd, _("Analyze Game from File"));
\r
4920 AnalysisPeriodicEvent(1);
\r
4924 case IDM_IcsClient:
\r
4928 case IDM_EditGame:
\r
4929 case IDM_EditGame2:
\r
4934 case IDM_EditPosition:
\r
4935 case IDM_EditPosition2:
\r
4936 EditPositionEvent();
\r
4937 SAY("enter a FEN string or setup a position on the board using the control R pop up menu");
\r
4940 case IDM_Training:
\r
4944 case IDM_ShowGameList:
\r
4945 ShowGameListProc();
\r
4948 case IDM_EditProgs1:
\r
4949 EditTagsPopUp(firstChessProgramNames, &firstChessProgramNames);
\r
4952 case IDM_EditProgs2:
\r
4953 LoadEnginePopUp(hwndMain);
\r
4954 // EditTagsPopUp(secondChessProgramNames, &secondChessProgramNames);
\r
4957 case IDM_EditServers:
\r
4958 EditTagsPopUp(icsNames, &icsNames);
\r
4961 case IDM_EditTags:
\r
4966 case IDM_EditBook:
\r
4970 case IDM_EditComment:
\r
4972 if (commentUp && editComment) {
\r
4975 EditCommentEvent();
\r
4995 case IDM_CallFlag:
\r
5015 case IDM_StopObserving:
\r
5016 StopObservingEvent();
\r
5019 case IDM_StopExamining:
\r
5020 StopExaminingEvent();
\r
5024 UploadGameEvent();
\r
5027 case IDM_TypeInMove:
\r
5028 TypeInEvent('\000');
\r
5031 case IDM_TypeInName:
\r
5032 PopUpNameDialog('\000');
\r
5035 case IDM_Backward:
\r
5037 SetFocus(hwndMain);
\r
5044 SetFocus(hwndMain);
\r
5049 SetFocus(hwndMain);
\r
5054 SetFocus(hwndMain);
\r
5058 RevertEvent(FALSE);
\r
5061 case IDM_Annotate: // [HGM] vari: revert with annotation
\r
5062 RevertEvent(TRUE);
\r
5065 case IDM_TruncateGame:
\r
5066 TruncateGameEvent();
\r
5073 case IDM_RetractMove:
\r
5074 RetractMoveEvent();
\r
5077 case IDM_FlipView:
\r
5078 flipView = !flipView;
\r
5079 DrawPosition(FALSE, NULL);
\r
5082 case IDM_FlipClock:
\r
5083 flipClock = !flipClock;
\r
5084 DisplayBothClocks();
\r
5088 case IDM_MuteSounds:
\r
5089 mute = !mute; // [HGM] mute: keep track of global muting variable
\r
5090 CheckMenuItem(GetMenu(hwndMain),IDM_MuteSounds,
\r
5091 MF_BYCOMMAND|(mute?MF_CHECKED:MF_UNCHECKED));
\r
5094 case IDM_GeneralOptions:
\r
5095 GeneralOptionsPopup(hwnd);
\r
5096 DrawPosition(TRUE, NULL);
\r
5099 case IDM_BoardOptions:
\r
5100 BoardOptionsPopup(hwnd);
\r
5103 case IDM_EnginePlayOptions:
\r
5104 EnginePlayOptionsPopup(hwnd);
\r
5107 case IDM_Engine1Options:
\r
5108 EngineOptionsPopup(hwnd, &first);
\r
5111 case IDM_Engine2Options:
\r
5113 if(WaitForEngine(&second, SettingsMenuIfReady)) break;
\r
5114 EngineOptionsPopup(hwnd, &second);
\r
5117 case IDM_OptionsUCI:
\r
5118 UciOptionsPopup(hwnd);
\r
5122 TourneyPopup(hwnd);
\r
5125 case IDM_IcsOptions:
\r
5126 IcsOptionsPopup(hwnd);
\r
5130 FontsOptionsPopup(hwnd);
\r
5134 SoundOptionsPopup(hwnd);
\r
5137 case IDM_CommPort:
\r
5138 CommPortOptionsPopup(hwnd);
\r
5141 case IDM_LoadOptions:
\r
5142 LoadOptionsPopup(hwnd);
\r
5145 case IDM_SaveOptions:
\r
5146 SaveOptionsPopup(hwnd);
\r
5149 case IDM_TimeControl:
\r
5150 TimeControlOptionsPopup(hwnd);
\r
5153 case IDM_SaveSettings:
\r
5154 SaveSettings(settingsFileName);
\r
5157 case IDM_SaveSettingsOnExit:
\r
5158 saveSettingsOnExit = !saveSettingsOnExit;
\r
5159 (void) CheckMenuItem(GetMenu(hwndMain), IDM_SaveSettingsOnExit,
\r
5160 MF_BYCOMMAND|(saveSettingsOnExit ?
\r
5161 MF_CHECKED : MF_UNCHECKED));
\r
5172 case IDM_AboutGame:
\r
5177 appData.debugMode = !appData.debugMode;
\r
5178 if (appData.debugMode) {
\r
5179 char dir[MSG_SIZ];
\r
5180 GetCurrentDirectory(MSG_SIZ, dir);
\r
5181 SetCurrentDirectory(installDir);
\r
5182 debugFP = fopen(appData.nameOfDebugFile, "w");
\r
5183 SetCurrentDirectory(dir);
\r
5184 setbuf(debugFP, NULL);
\r
5191 case IDM_HELPCONTENTS:
\r
5192 if (!MyHelp (hwnd, "winboard.hlp", HELP_KEY,(DWORD)(LPSTR)"CONTENTS") &&
\r
5193 !HtmlHelp(hwnd, "winboard.chm", 0, 0) ) {
\r
5194 MessageBox (GetFocus(),
\r
5195 _("Unable to activate help"),
\r
5196 szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);
\r
5200 case IDM_HELPSEARCH:
\r
5201 if (!MyHelp (hwnd, "winboard.hlp", HELP_PARTIALKEY, (DWORD)(LPSTR)"") &&
\r
5202 !HtmlHelp(hwnd, "winboard.chm", 0, 0) ) {
\r
5203 MessageBox (GetFocus(),
\r
5204 _("Unable to activate help"),
\r
5205 szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);
\r
5209 case IDM_HELPHELP:
\r
5210 if(!WinHelp(hwnd, (LPSTR)NULL, HELP_HELPONHELP, 0)) {
\r
5211 MessageBox (GetFocus(),
\r
5212 _("Unable to activate help"),
\r
5213 szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);
\r
5218 lpProc = MakeProcInstance((FARPROC)About, hInst);
\r
5220 (gameInfo.event && strcmp(gameInfo.event, "Easter Egg Hunt") == 0) ?
\r
5221 "AboutBox2" : "AboutBox", hwnd, (DLGPROC)lpProc);
\r
5222 FreeProcInstance(lpProc);
\r
5225 case IDM_DirectCommand1:
\r
5226 AskQuestionEvent(_("Direct Command"),
\r
5227 _("Send to chess program:"), "", "1");
\r
5229 case IDM_DirectCommand2:
\r
5230 AskQuestionEvent(_("Direct Command"),
\r
5231 _("Send to second chess program:"), "", "2");
\r
5234 case EP_WhitePawn:
\r
5235 EditPositionMenuEvent(WhitePawn, fromX, fromY);
\r
5236 fromX = fromY = -1;
\r
5239 case EP_WhiteKnight:
\r
5240 EditPositionMenuEvent(WhiteKnight, fromX, fromY);
\r
5241 fromX = fromY = -1;
\r
5244 case EP_WhiteBishop:
\r
5245 EditPositionMenuEvent(WhiteBishop, fromX, fromY);
\r
5246 fromX = fromY = -1;
\r
5249 case EP_WhiteRook:
\r
5250 EditPositionMenuEvent(WhiteRook, fromX, fromY);
\r
5251 fromX = fromY = -1;
\r
5254 case EP_WhiteQueen:
\r
5255 EditPositionMenuEvent(WhiteQueen, fromX, fromY);
\r
5256 fromX = fromY = -1;
\r
5259 case EP_WhiteFerz:
\r
5260 EditPositionMenuEvent(WhiteFerz, fromX, fromY);
\r
5261 fromX = fromY = -1;
\r
5264 case EP_WhiteWazir:
\r
5265 EditPositionMenuEvent(WhiteWazir, fromX, fromY);
\r
5266 fromX = fromY = -1;
\r
5269 case EP_WhiteAlfil:
\r
5270 EditPositionMenuEvent(WhiteAlfil, fromX, fromY);
\r
5271 fromX = fromY = -1;
\r
5274 case EP_WhiteCannon:
\r
5275 EditPositionMenuEvent(WhiteCannon, fromX, fromY);
\r
5276 fromX = fromY = -1;
\r
5279 case EP_WhiteCardinal:
\r
5280 EditPositionMenuEvent(WhiteAngel, fromX, fromY);
\r
5281 fromX = fromY = -1;
\r
5284 case EP_WhiteMarshall:
\r
5285 EditPositionMenuEvent(WhiteMarshall, fromX, fromY);
\r
5286 fromX = fromY = -1;
\r
5289 case EP_WhiteKing:
\r
5290 EditPositionMenuEvent(WhiteKing, fromX, fromY);
\r
5291 fromX = fromY = -1;
\r
5294 case EP_BlackPawn:
\r
5295 EditPositionMenuEvent(BlackPawn, fromX, fromY);
\r
5296 fromX = fromY = -1;
\r
5299 case EP_BlackKnight:
\r
5300 EditPositionMenuEvent(BlackKnight, fromX, fromY);
\r
5301 fromX = fromY = -1;
\r
5304 case EP_BlackBishop:
\r
5305 EditPositionMenuEvent(BlackBishop, fromX, fromY);
\r
5306 fromX = fromY = -1;
\r
5309 case EP_BlackRook:
\r
5310 EditPositionMenuEvent(BlackRook, fromX, fromY);
\r
5311 fromX = fromY = -1;
\r
5314 case EP_BlackQueen:
\r
5315 EditPositionMenuEvent(BlackQueen, fromX, fromY);
\r
5316 fromX = fromY = -1;
\r
5319 case EP_BlackFerz:
\r
5320 EditPositionMenuEvent(BlackFerz, fromX, fromY);
\r
5321 fromX = fromY = -1;
\r
5324 case EP_BlackWazir:
\r
5325 EditPositionMenuEvent(BlackWazir, fromX, fromY);
\r
5326 fromX = fromY = -1;
\r
5329 case EP_BlackAlfil:
\r
5330 EditPositionMenuEvent(BlackAlfil, fromX, fromY);
\r
5331 fromX = fromY = -1;
\r
5334 case EP_BlackCannon:
\r
5335 EditPositionMenuEvent(BlackCannon, fromX, fromY);
\r
5336 fromX = fromY = -1;
\r
5339 case EP_BlackCardinal:
\r
5340 EditPositionMenuEvent(BlackAngel, fromX, fromY);
\r
5341 fromX = fromY = -1;
\r
5344 case EP_BlackMarshall:
\r
5345 EditPositionMenuEvent(BlackMarshall, fromX, fromY);
\r
5346 fromX = fromY = -1;
\r
5349 case EP_BlackKing:
\r
5350 EditPositionMenuEvent(BlackKing, fromX, fromY);
\r
5351 fromX = fromY = -1;
\r
5354 case EP_EmptySquare:
\r
5355 EditPositionMenuEvent(EmptySquare, fromX, fromY);
\r
5356 fromX = fromY = -1;
\r
5359 case EP_ClearBoard:
\r
5360 EditPositionMenuEvent(ClearBoard, fromX, fromY);
\r
5361 fromX = fromY = -1;
\r
5365 EditPositionMenuEvent(WhitePlay, fromX, fromY);
\r
5366 fromX = fromY = -1;
\r
5370 EditPositionMenuEvent(BlackPlay, fromX, fromY);
\r
5371 fromX = fromY = -1;
\r
5375 EditPositionMenuEvent(PromotePiece, fromX, fromY);
\r
5376 fromX = fromY = -1;
\r
5380 EditPositionMenuEvent(DemotePiece, fromX, fromY);
\r
5381 fromX = fromY = -1;
\r
5385 DropMenuEvent(WhitePawn, fromX, fromY);
\r
5386 fromX = fromY = -1;
\r
5390 DropMenuEvent(WhiteKnight, fromX, fromY);
\r
5391 fromX = fromY = -1;
\r
5395 DropMenuEvent(WhiteBishop, fromX, fromY);
\r
5396 fromX = fromY = -1;
\r
5400 DropMenuEvent(WhiteRook, fromX, fromY);
\r
5401 fromX = fromY = -1;
\r
5405 DropMenuEvent(WhiteQueen, fromX, fromY);
\r
5406 fromX = fromY = -1;
\r
5410 barbaric = 0; appData.language = "";
\r
5411 TranslateMenus(0);
\r
5412 CheckMenuItem(GetMenu(hwndMain), lastChecked, MF_BYCOMMAND|MF_UNCHECKED);
\r
5413 CheckMenuItem(GetMenu(hwndMain), IDM_English, MF_BYCOMMAND|MF_CHECKED);
\r
5414 lastChecked = wmId;
\r
5418 if(wmId > IDM_English && wmId < IDM_English+20) {
\r
5419 LoadLanguageFile(languageFile[wmId - IDM_English - 1]);
\r
5420 TranslateMenus(0);
\r
5421 CheckMenuItem(GetMenu(hwndMain), lastChecked, MF_BYCOMMAND|MF_UNCHECKED);
\r
5422 CheckMenuItem(GetMenu(hwndMain), wmId, MF_BYCOMMAND|MF_CHECKED);
\r
5423 lastChecked = wmId;
\r
5426 return (DefWindowProc(hwnd, message, wParam, lParam));
\r
5432 case CLOCK_TIMER_ID:
\r
5433 KillTimer(hwnd, clockTimerEvent); /* Simulate one-shot timer as in X */
\r
5434 clockTimerEvent = 0;
\r
5435 DecrementClocks(); /* call into back end */
\r
5437 case LOAD_GAME_TIMER_ID:
\r
5438 KillTimer(hwnd, loadGameTimerEvent); /* Simulate one-shot timer as in X*/
\r
5439 loadGameTimerEvent = 0;
\r
5440 AutoPlayGameLoop(); /* call into back end */
\r
5442 case ANALYSIS_TIMER_ID:
\r
5443 if ((gameMode == AnalyzeMode || gameMode == AnalyzeFile
\r
5444 || appData.icsEngineAnalyze) && appData.periodicUpdates) {
\r
5445 AnalysisPeriodicEvent(0);
\r
5447 KillTimer(hwnd, analysisTimerEvent);
\r
5448 analysisTimerEvent = 0;
\r
5451 case DELAYED_TIMER_ID:
\r
5452 KillTimer(hwnd, delayedTimerEvent);
\r
5453 delayedTimerEvent = 0;
\r
5454 delayedTimerCallback();
\r
5459 case WM_USER_Input:
\r
5460 InputEvent(hwnd, message, wParam, lParam);
\r
5463 /* [AS] Also move "attached" child windows */
\r
5464 case WM_WINDOWPOSCHANGING:
\r
5466 if( hwnd == hwndMain && appData.useStickyWindows ) {
\r
5467 LPWINDOWPOS lpwp = (LPWINDOWPOS) lParam;
\r
5469 if( ((lpwp->flags & SWP_NOMOVE) == 0) && ((lpwp->flags & SWP_NOSIZE) != 0) ) {
\r
5470 /* Window is moving */
\r
5473 // GetWindowRect( hwnd, &rcMain ); //[HGM] sticky: in XP this returned new position, not old
\r
5474 rcMain.left = wpMain.x; // replace by these 4 lines to reconstruct old rect
\r
5475 rcMain.right = wpMain.x + wpMain.width;
\r
5476 rcMain.top = wpMain.y;
\r
5477 rcMain.bottom = wpMain.y + wpMain.height;
\r
5479 ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, moveHistoryDialog, &wpMoveHistory );
\r
5480 ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, evalGraphDialog, &wpEvalGraph );
\r
5481 ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, engineOutputDialog, &wpEngineOutput );
\r
5482 ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, gameListDialog, &wpGameList );
\r
5483 ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, hwndConsole, &wpConsole );
\r
5484 wpMain.x = lpwp->x;
\r
5485 wpMain.y = lpwp->y;
\r
5490 /* [AS] Snapping */
\r
5491 case WM_ENTERSIZEMOVE:
\r
5492 if(appData.debugMode) { fprintf(debugFP, "size-move\n"); }
\r
5493 if (hwnd == hwndMain) {
\r
5494 doingSizing = TRUE;
\r
5497 return OnEnterSizeMove( &sd, hwnd, wParam, lParam );
\r
5501 if(appData.debugMode) { fprintf(debugFP, "sizing\n"); }
\r
5502 if (hwnd == hwndMain) {
\r
5503 lastSizing = wParam;
\r
5508 if(appData.debugMode) { fprintf(debugFP, "moving\n"); }
\r
5509 return OnMoving( &sd, hwnd, wParam, lParam );
\r
5511 case WM_EXITSIZEMOVE:
\r
5512 if(appData.debugMode) { fprintf(debugFP, "exit size-move, size = %d\n", squareSize); }
\r
5513 if (hwnd == hwndMain) {
\r
5515 doingSizing = FALSE;
\r
5516 InvalidateRect(hwnd, &boardRect, FALSE);
\r
5517 GetClientRect(hwnd, &client);
\r
5518 ResizeBoard(client.right, client.bottom, lastSizing);
\r
5520 if(appData.debugMode) { fprintf(debugFP, "square size = %d\n", squareSize); }
\r
5522 return OnExitSizeMove( &sd, hwnd, wParam, lParam );
\r
5525 case WM_DESTROY: /* message: window being destroyed */
\r
5526 PostQuitMessage(0);
\r
5530 if (hwnd == hwndMain) {
\r
5535 default: /* Passes it on if unprocessed */
\r
5536 return (DefWindowProc(hwnd, message, wParam, lParam));
\r
5541 /*---------------------------------------------------------------------------*\
\r
5543 * Misc utility routines
\r
5545 \*---------------------------------------------------------------------------*/
\r
5548 * Decent random number generator, at least not as bad as Windows
\r
5549 * standard rand, which returns a value in the range 0 to 0x7fff.
\r
5551 unsigned int randstate;
\r
5556 randstate = randstate * 1664525 + 1013904223;
\r
5557 return (int) randstate & 0x7fffffff;
\r
5561 mysrandom(unsigned int seed)
\r
5568 * returns TRUE if user selects a different color, FALSE otherwise
\r
5572 ChangeColor(HWND hwnd, COLORREF *which)
\r
5574 static BOOL firstTime = TRUE;
\r
5575 static DWORD customColors[16];
\r
5577 COLORREF newcolor;
\r
5582 /* Make initial colors in use available as custom colors */
\r
5583 /* Should we put the compiled-in defaults here instead? */
\r
5585 customColors[i++] = lightSquareColor & 0xffffff;
\r
5586 customColors[i++] = darkSquareColor & 0xffffff;
\r
5587 customColors[i++] = whitePieceColor & 0xffffff;
\r
5588 customColors[i++] = blackPieceColor & 0xffffff;
\r
5589 customColors[i++] = highlightSquareColor & 0xffffff;
\r
5590 customColors[i++] = premoveHighlightColor & 0xffffff;
\r
5592 for (ccl = (ColorClass) 0; ccl < NColorClasses && i < 16; ccl++) {
\r
5593 customColors[i++] = textAttribs[ccl].color;
\r
5595 while (i < 16) customColors[i++] = RGB(255, 255, 255);
\r
5596 firstTime = FALSE;
\r
5599 cc.lStructSize = sizeof(cc);
\r
5600 cc.hwndOwner = hwnd;
\r
5601 cc.hInstance = NULL;
\r
5602 cc.rgbResult = (DWORD) (*which & 0xffffff);
\r
5603 cc.lpCustColors = (LPDWORD) customColors;
\r
5604 cc.Flags = CC_RGBINIT|CC_FULLOPEN;
\r
5606 if (!ChooseColor(&cc)) return FALSE;
\r
5608 newcolor = (COLORREF) (0x2000000 | cc.rgbResult);
\r
5609 if (newcolor == *which) return FALSE;
\r
5610 *which = newcolor;
\r
5614 InitDrawingColors();
\r
5615 InvalidateRect(hwnd, &boardRect, FALSE);
\r
5620 MyLoadSound(MySound *ms)
\r
5626 if (ms->data && ms->flag) free(ms->data);
\r
5629 switch (ms->name[0]) {
\r
5635 /* System sound from Control Panel. Don't preload here. */
\r
5639 if (ms->name[1] == NULLCHAR) {
\r
5640 /* "!" alone = silence */
\r
5643 /* Builtin wave resource. Error if not found. */
\r
5644 HANDLE h = FindResource(hInst, ms->name + 1, "WAVE");
\r
5645 if (h == NULL) break;
\r
5646 ms->data = (void *)LoadResource(hInst, h);
\r
5647 ms->flag = 0; // not maloced, so cannot be freed!
\r
5648 if (h == NULL) break;
\r
5653 /* .wav file. Error if not found. */
\r
5654 f = fopen(ms->name, "rb");
\r
5655 if (f == NULL) break;
\r
5656 if (fstat(fileno(f), &st) < 0) break;
\r
5657 ms->data = malloc(st.st_size);
\r
5659 if (fread(ms->data, st.st_size, 1, f) < 1) break;
\r
5665 char buf[MSG_SIZ];
\r
5666 snprintf(buf, MSG_SIZ, _("Error loading sound %s"), ms->name);
\r
5667 DisplayError(buf, GetLastError());
\r
5673 MyPlaySound(MySound *ms)
\r
5675 BOOLEAN ok = FALSE;
\r
5677 if(mute) return TRUE; // [HGM] mute: suppress all sound play when muted
\r
5678 switch (ms->name[0]) {
\r
5680 if(appData.debugMode) fprintf(debugFP, "silence\n");
\r
5685 /* System sound from Control Panel (deprecated feature).
\r
5686 "$" alone or an unset sound name gets default beep (still in use). */
\r
5687 if (ms->name[1]) {
\r
5688 ok = PlaySound(ms->name + 1, NULL, SND_ALIAS|SND_ASYNC);
\r
5690 if (!ok) ok = MessageBeep(MB_OK);
\r
5693 /* Builtin wave resource, or "!" alone for silence */
\r
5694 if (ms->name[1]) {
\r
5695 if (ms->data == NULL) return FALSE;
\r
5696 ok = PlaySound(ms->data, NULL, SND_MEMORY|SND_ASYNC);
\r
5702 /* .wav file. Error if not found. */
\r
5703 if (ms->data == NULL) return FALSE;
\r
5704 ok = PlaySound(ms->data, NULL, SND_MEMORY|SND_ASYNC);
\r
5707 /* Don't print an error: this can happen innocently if the sound driver
\r
5708 is busy; for instance, if another instance of WinBoard is playing
\r
5709 a sound at about the same time. */
\r
5715 OldOpenFileHook(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
5718 OPENFILENAME *ofn;
\r
5719 static UINT *number; /* gross that this is static */
\r
5721 switch (message) {
\r
5722 case WM_INITDIALOG: /* message: initialize dialog box */
\r
5723 /* Center the dialog over the application window */
\r
5724 ofn = (OPENFILENAME *) lParam;
\r
5725 if (ofn->Flags & OFN_ENABLETEMPLATE) {
\r
5726 number = (UINT *) ofn->lCustData;
\r
5727 SendMessage(GetDlgItem(hDlg, edt2), WM_SETTEXT, 0, (LPARAM) "");
\r
5731 CenterWindow (hDlg, GetWindow (hDlg, GW_OWNER));
\r
5732 Translate(hDlg, 1536);
\r
5733 return FALSE; /* Allow for further processing */
\r
5736 if ((LOWORD(wParam) == IDOK) && (number != NULL)) {
\r
5737 *number = GetDlgItemInt(hDlg, OPT_IndexNumberOld, &ok, FALSE);
\r
5739 return FALSE; /* Allow for further processing */
\r
5745 OpenFileHook(HWND hdlg, UINT uiMsg, WPARAM wParam, LPARAM lParam)
\r
5747 static UINT *number;
\r
5748 OPENFILENAME *ofname;
\r
5751 case WM_INITDIALOG:
\r
5752 Translate(hdlg, DLG_IndexNumber);
\r
5753 ofname = (OPENFILENAME *)lParam;
\r
5754 number = (UINT *)(ofname->lCustData);
\r
5757 ofnot = (OFNOTIFY *)lParam;
\r
5758 if (ofnot->hdr.code == CDN_FILEOK) {
\r
5759 *number = GetDlgItemInt(hdlg, OPT_IndexNumber, NULL, FALSE);
\r
5768 OpenFileDialog(HWND hwnd, char *write, char *defName, char *defExt, // [HGM] diag: type of 'write' now string
\r
5769 char *nameFilt, char *dlgTitle, UINT *number,
\r
5770 char fileTitle[MSG_SIZ], char fileName[MSG_SIZ])
\r
5772 OPENFILENAME openFileName;
\r
5773 char buf1[MSG_SIZ];
\r
5776 if (fileName == NULL) fileName = buf1;
\r
5777 if (defName == NULL) {
\r
5778 safeStrCpy(fileName, "*.", 3 );
\r
5779 strcat(fileName, defExt);
\r
5781 safeStrCpy(fileName, defName, MSG_SIZ );
\r
5783 if (fileTitle) safeStrCpy(fileTitle, "", MSG_SIZ );
\r
5784 if (number) *number = 0;
\r
5786 openFileName.lStructSize = sizeof(OPENFILENAME);
\r
5787 openFileName.hwndOwner = hwnd;
\r
5788 openFileName.hInstance = (HANDLE) hInst;
\r
5789 openFileName.lpstrFilter = nameFilt;
\r
5790 openFileName.lpstrCustomFilter = (LPSTR) NULL;
\r
5791 openFileName.nMaxCustFilter = 0L;
\r
5792 openFileName.nFilterIndex = 1L;
\r
5793 openFileName.lpstrFile = fileName;
\r
5794 openFileName.nMaxFile = MSG_SIZ;
\r
5795 openFileName.lpstrFileTitle = fileTitle;
\r
5796 openFileName.nMaxFileTitle = fileTitle ? MSG_SIZ : 0;
\r
5797 openFileName.lpstrInitialDir = NULL;
\r
5798 openFileName.lpstrTitle = dlgTitle;
\r
5799 openFileName.Flags = OFN_PATHMUSTEXIST | OFN_HIDEREADONLY
\r
5800 | (write[0] != 'r' ? 0 : OFN_FILEMUSTEXIST)
\r
5801 | (number ? OFN_ENABLETEMPLATE | OFN_ENABLEHOOK: 0)
\r
5802 | (oldDialog ? 0 : OFN_EXPLORER);
\r
5803 openFileName.nFileOffset = 0;
\r
5804 openFileName.nFileExtension = 0;
\r
5805 openFileName.lpstrDefExt = defExt;
\r
5806 openFileName.lCustData = (LONG) number;
\r
5807 openFileName.lpfnHook = oldDialog ?
\r
5808 (LPOFNHOOKPROC) OldOpenFileHook : (LPOFNHOOKPROC) OpenFileHook;
\r
5809 openFileName.lpTemplateName = (LPSTR)(oldDialog ? 1536 : DLG_IndexNumber);
\r
5811 if (write[0] != 'r' ? GetSaveFileName(&openFileName) :
\r
5812 GetOpenFileName(&openFileName)) {
\r
5813 /* open the file */
\r
5814 f = fopen(openFileName.lpstrFile, write);
\r
5816 MessageBox(hwnd, _("File open failed"), NULL,
\r
5817 MB_OK|MB_ICONEXCLAMATION);
\r
5821 int err = CommDlgExtendedError();
\r
5822 if (err != 0) DisplayError(_("Internal error in file dialog box"), err);
\r
5831 MenuPopup(HWND hwnd, POINT pt, HMENU hmenu, UINT def)
\r
5833 HMENU hmenuTrackPopup; /* floating pop-up menu */
\r
5836 * Get the first pop-up menu in the menu template. This is the
\r
5837 * menu that TrackPopupMenu displays.
\r
5839 hmenuTrackPopup = GetSubMenu(hmenu, 0);
\r
5840 TranslateOneMenu(10, hmenuTrackPopup);
\r
5842 SetMenuDefaultItem(hmenuTrackPopup, def, FALSE);
\r
5845 * TrackPopup uses screen coordinates, so convert the
\r
5846 * coordinates of the mouse click to screen coordinates.
\r
5848 ClientToScreen(hwnd, (LPPOINT) &pt);
\r
5850 /* Draw and track the floating pop-up menu. */
\r
5851 TrackPopupMenu(hmenuTrackPopup, TPM_CENTERALIGN | TPM_RIGHTBUTTON,
\r
5852 pt.x, pt.y, 0, hwnd, NULL);
\r
5854 /* Destroy the menu.*/
\r
5855 DestroyMenu(hmenu);
\r
5860 int sizeX, sizeY, newSizeX, newSizeY;
\r
5862 } ResizeEditPlusButtonsClosure;
\r
5865 ResizeEditPlusButtonsCallback(HWND hChild, LPARAM lparam)
\r
5867 ResizeEditPlusButtonsClosure *cl = (ResizeEditPlusButtonsClosure *)lparam;
\r
5871 if (hChild == cl->hText) return TRUE;
\r
5872 GetWindowRect(hChild, &rect); /* gives screen coords */
\r
5873 pt.x = rect.left + (cl->newSizeX - cl->sizeX)/2;
\r
5874 pt.y = rect.top + cl->newSizeY - cl->sizeY;
\r
5875 ScreenToClient(cl->hDlg, &pt);
\r
5876 cl->hdwp = DeferWindowPos(cl->hdwp, hChild, NULL,
\r
5877 pt.x, pt.y, rect.right - rect.left, rect.bottom - rect.top, SWP_NOZORDER);
\r
5881 /* Resize a dialog that has a (rich) edit field filling most of
\r
5882 the top, with a row of buttons below */
\r
5884 ResizeEditPlusButtons(HWND hDlg, HWND hText, int sizeX, int sizeY, int newSizeX, int newSizeY)
\r
5887 int newTextHeight, newTextWidth;
\r
5888 ResizeEditPlusButtonsClosure cl;
\r
5890 /*if (IsIconic(hDlg)) return;*/
\r
5891 if (newSizeX == sizeX && newSizeY == sizeY) return;
\r
5893 cl.hdwp = BeginDeferWindowPos(8);
\r
5895 GetWindowRect(hText, &rectText); /* gives screen coords */
\r
5896 newTextWidth = rectText.right - rectText.left + newSizeX - sizeX;
\r
5897 newTextHeight = rectText.bottom - rectText.top + newSizeY - sizeY;
\r
5898 if (newTextHeight < 0) {
\r
5899 newSizeY += -newTextHeight;
\r
5900 newTextHeight = 0;
\r
5902 cl.hdwp = DeferWindowPos(cl.hdwp, hText, NULL, 0, 0,
\r
5903 newTextWidth, newTextHeight, SWP_NOZORDER|SWP_NOMOVE);
\r
5909 cl.newSizeX = newSizeX;
\r
5910 cl.newSizeY = newSizeY;
\r
5911 EnumChildWindows(hDlg, ResizeEditPlusButtonsCallback, (LPARAM)&cl);
\r
5913 EndDeferWindowPos(cl.hdwp);
\r
5916 BOOL CenterWindowEx(HWND hwndChild, HWND hwndParent, int mode)
\r
5918 RECT rChild, rParent;
\r
5919 int wChild, hChild, wParent, hParent;
\r
5920 int wScreen, hScreen, xNew, yNew;
\r
5923 /* Get the Height and Width of the child window */
\r
5924 GetWindowRect (hwndChild, &rChild);
\r
5925 wChild = rChild.right - rChild.left;
\r
5926 hChild = rChild.bottom - rChild.top;
\r
5928 /* Get the Height and Width of the parent window */
\r
5929 GetWindowRect (hwndParent, &rParent);
\r
5930 wParent = rParent.right - rParent.left;
\r
5931 hParent = rParent.bottom - rParent.top;
\r
5933 /* Get the display limits */
\r
5934 hdc = GetDC (hwndChild);
\r
5935 wScreen = GetDeviceCaps (hdc, HORZRES);
\r
5936 hScreen = GetDeviceCaps (hdc, VERTRES);
\r
5937 ReleaseDC(hwndChild, hdc);
\r
5939 /* Calculate new X position, then adjust for screen */
\r
5940 xNew = rParent.left + ((wParent - wChild) /2);
\r
5943 } else if ((xNew+wChild) > wScreen) {
\r
5944 xNew = wScreen - wChild;
\r
5947 /* Calculate new Y position, then adjust for screen */
\r
5949 yNew = rParent.top + ((hParent - hChild) /2);
\r
5952 yNew = rParent.top + GetSystemMetrics( SM_CYCAPTION ) * 2 / 3;
\r
5957 } else if ((yNew+hChild) > hScreen) {
\r
5958 yNew = hScreen - hChild;
\r
5961 /* Set it, and return */
\r
5962 return SetWindowPos (hwndChild, NULL,
\r
5963 xNew, yNew, 0, 0, SWP_NOSIZE | SWP_NOZORDER);
\r
5966 /* Center one window over another */
\r
5967 BOOL CenterWindow (HWND hwndChild, HWND hwndParent)
\r
5969 return CenterWindowEx( hwndChild, hwndParent, 0 );
\r
5972 /*---------------------------------------------------------------------------*\
\r
5974 * Startup Dialog functions
\r
5976 \*---------------------------------------------------------------------------*/
\r
5978 InitComboStrings(HANDLE hwndCombo, char **cd)
\r
5980 SendMessage(hwndCombo, CB_RESETCONTENT, 0, 0);
\r
5982 while (*cd != NULL) {
\r
5983 SendMessage(hwndCombo, CB_ADDSTRING, 0, (LPARAM) T_(*cd));
\r
5989 InitComboStringsFromOption(HANDLE hwndCombo, char *str)
\r
5991 char buf1[MAX_ARG_LEN];
\r
5994 if (str[0] == '@') {
\r
5995 FILE* f = fopen(str + 1, "r");
\r
5997 DisplayFatalError(str + 1, errno, 2);
\r
6000 len = fread(buf1, 1, sizeof(buf1)-1, f);
\r
6002 buf1[len] = NULLCHAR;
\r
6006 SendMessage(hwndCombo, CB_RESETCONTENT, 0, 0);
\r
6009 char buf[MSG_SIZ];
\r
6010 char *end = strchr(str, '\n');
\r
6011 if (end == NULL) return;
\r
6012 memcpy(buf, str, end - str);
\r
6013 buf[end - str] = NULLCHAR;
\r
6014 SendMessage(hwndCombo, CB_ADDSTRING, 0, (LPARAM) buf);
\r
6020 SetStartupDialogEnables(HWND hDlg)
\r
6022 EnableWindow(GetDlgItem(hDlg, OPT_ChessEngineName),
\r
6023 IsDlgButtonChecked(hDlg, OPT_ChessEngine) ||
\r
6024 (appData.zippyPlay && IsDlgButtonChecked(hDlg, OPT_ChessServer)));
\r
6025 EnableWindow(GetDlgItem(hDlg, OPT_SecondChessEngineName),
\r
6026 IsDlgButtonChecked(hDlg, OPT_ChessEngine));
\r
6027 EnableWindow(GetDlgItem(hDlg, OPT_ChessServerName),
\r
6028 IsDlgButtonChecked(hDlg, OPT_ChessServer));
\r
6029 EnableWindow(GetDlgItem(hDlg, OPT_AdditionalOptions),
\r
6030 IsDlgButtonChecked(hDlg, OPT_AnyAdditional));
\r
6031 EnableWindow(GetDlgItem(hDlg, IDOK),
\r
6032 IsDlgButtonChecked(hDlg, OPT_ChessEngine) ||
\r
6033 IsDlgButtonChecked(hDlg, OPT_ChessServer) ||
\r
6034 IsDlgButtonChecked(hDlg, OPT_View));
\r
6038 QuoteForFilename(char *filename)
\r
6040 int dquote, space;
\r
6041 dquote = strchr(filename, '"') != NULL;
\r
6042 space = strchr(filename, ' ') != NULL;
\r
6043 if (dquote || space) {
\r
6055 InitEngineBox(HWND hDlg, HWND hwndCombo, char* nthcp, char* nthd, char* nthdir, char *nthnames)
\r
6057 char buf[MSG_SIZ];
\r
6060 InitComboStringsFromOption(hwndCombo, nthnames);
\r
6061 q = QuoteForFilename(nthcp);
\r
6062 snprintf(buf, MSG_SIZ, "%s%s%s", q, nthcp, q);
\r
6063 if (*nthdir != NULLCHAR) {
\r
6064 q = QuoteForFilename(nthdir);
\r
6065 snprintf(buf + strlen(buf), MSG_SIZ, " /%s=%s%s%s", nthd, q, nthdir, q);
\r
6067 if (*nthcp == NULLCHAR) {
\r
6068 SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) 0, (LPARAM) 0);
\r
6069 } else if (SendMessage(hwndCombo, CB_SELECTSTRING, (WPARAM) -1, (LPARAM) buf) == CB_ERR) {
\r
6070 SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) -1, (LPARAM) 0);
\r
6071 SendMessage(hwndCombo, WM_SETTEXT, (WPARAM) 0, (LPARAM) buf);
\r
6076 StartupDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
6078 char buf[MSG_SIZ];
\r
6082 switch (message) {
\r
6083 case WM_INITDIALOG:
\r
6084 /* Center the dialog */
\r
6085 CenterWindow (hDlg, GetDesktopWindow());
\r
6086 Translate(hDlg, DLG_Startup);
\r
6087 /* Initialize the dialog items */
\r
6088 InitEngineBox(hDlg, GetDlgItem(hDlg, OPT_ChessEngineName),
\r
6089 appData.firstChessProgram, "fd", appData.firstDirectory,
\r
6090 firstChessProgramNames);
\r
6091 InitEngineBox(hDlg, GetDlgItem(hDlg, OPT_SecondChessEngineName),
\r
6092 appData.secondChessProgram, singleList ? "fd" : "sd", appData.secondDirectory,
\r
6093 singleList ? firstChessProgramNames : secondChessProgramNames); //[HGM] single: use first list in second combo
\r
6094 hwndCombo = GetDlgItem(hDlg, OPT_ChessServerName);
\r
6095 InitComboStringsFromOption(hwndCombo, icsNames);
\r
6096 snprintf(buf, MSG_SIZ, "%s /icsport=%s", appData.icsHost, appData.icsPort);
\r
6097 if (*appData.icsHelper != NULLCHAR) {
\r
6098 char *q = QuoteForFilename(appData.icsHelper);
\r
6099 sprintf(buf + strlen(buf), " /icshelper=%s%s%s", q, appData.icsHelper, q);
\r
6101 if (*appData.icsHost == NULLCHAR) {
\r
6102 SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) 0, (LPARAM) 0);
\r
6103 /*SendMessage(hwndCombo, CB_SHOWDROPDOWN, (WPARAM) TRUE, (LPARAM) 0); !!too soon */
\r
6104 } else if (SendMessage(hwndCombo, CB_SELECTSTRING, (WPARAM) -1, (LPARAM) buf) == CB_ERR) {
\r
6105 SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) -1, (LPARAM) 0);
\r
6106 SendMessage(hwndCombo, WM_SETTEXT, (WPARAM) 0, (LPARAM) buf);
\r
6109 if (appData.icsActive) {
\r
6110 CheckDlgButton(hDlg, OPT_ChessServer, BST_CHECKED);
\r
6112 else if (appData.noChessProgram) {
\r
6113 CheckDlgButton(hDlg, OPT_View, BST_CHECKED);
\r
6116 CheckDlgButton(hDlg, OPT_ChessEngine, BST_CHECKED);
\r
6119 SetStartupDialogEnables(hDlg);
\r
6123 switch (LOWORD(wParam)) {
\r
6125 if (IsDlgButtonChecked(hDlg, OPT_ChessEngine)) {
\r
6126 safeStrCpy(buf, "/fcp=", sizeof(buf)/sizeof(buf[0]) );
\r
6127 GetDlgItemText(hDlg, OPT_ChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));
\r
6129 ParseArgs(StringGet, &p);
\r
6130 safeStrCpy(buf, singleList ? "/fcp=" : "/scp=", sizeof(buf)/sizeof(buf[0]) );
\r
6131 GetDlgItemText(hDlg, OPT_SecondChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));
\r
6133 SwapEngines(singleList); // temporarily swap first and second, to load a second 'first', ...
\r
6134 ParseArgs(StringGet, &p);
\r
6135 SwapEngines(singleList); // ... and then make it 'second'
\r
6136 appData.noChessProgram = FALSE;
\r
6137 appData.icsActive = FALSE;
\r
6138 } else if (IsDlgButtonChecked(hDlg, OPT_ChessServer)) {
\r
6139 safeStrCpy(buf, "/ics /icshost=", sizeof(buf)/sizeof(buf[0]) );
\r
6140 GetDlgItemText(hDlg, OPT_ChessServerName, buf + strlen(buf), sizeof(buf) - strlen(buf));
\r
6142 ParseArgs(StringGet, &p);
\r
6143 if (appData.zippyPlay) {
\r
6144 safeStrCpy(buf, "/fcp=", sizeof(buf)/sizeof(buf[0]) );
\r
6145 GetDlgItemText(hDlg, OPT_ChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));
\r
6147 ParseArgs(StringGet, &p);
\r
6149 } else if (IsDlgButtonChecked(hDlg, OPT_View)) {
\r
6150 appData.noChessProgram = TRUE;
\r
6151 appData.icsActive = FALSE;
\r
6153 MessageBox(hDlg, _("Choose an option, or cancel to exit"),
\r
6154 _("Option Error"), MB_OK|MB_ICONEXCLAMATION);
\r
6157 if (IsDlgButtonChecked(hDlg, OPT_AnyAdditional)) {
\r
6158 GetDlgItemText(hDlg, OPT_AdditionalOptions, buf, sizeof(buf));
\r
6160 ParseArgs(StringGet, &p);
\r
6162 EndDialog(hDlg, TRUE);
\r
6169 case IDM_HELPCONTENTS:
\r
6170 if (!WinHelp (hDlg, "winboard.hlp", HELP_KEY,(DWORD)(LPSTR)"CONTENTS")) {
\r
6171 MessageBox (GetFocus(),
\r
6172 _("Unable to activate help"),
\r
6173 szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);
\r
6178 SetStartupDialogEnables(hDlg);
\r
6186 /*---------------------------------------------------------------------------*\
\r
6188 * About box dialog functions
\r
6190 \*---------------------------------------------------------------------------*/
\r
6192 /* Process messages for "About" dialog box */
\r
6194 About(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
6196 switch (message) {
\r
6197 case WM_INITDIALOG: /* message: initialize dialog box */
\r
6198 /* Center the dialog over the application window */
\r
6199 CenterWindow (hDlg, GetWindow (hDlg, GW_OWNER));
\r
6200 SetDlgItemText(hDlg, ABOUTBOX_Version, programVersion);
\r
6201 Translate(hDlg, ABOUTBOX);
\r
6205 case WM_COMMAND: /* message: received a command */
\r
6206 if (LOWORD(wParam) == IDOK /* "OK" box selected? */
\r
6207 || LOWORD(wParam) == IDCANCEL) { /* System menu close command? */
\r
6208 EndDialog(hDlg, TRUE); /* Exit the dialog */
\r
6216 /*---------------------------------------------------------------------------*\
\r
6218 * Comment Dialog functions
\r
6220 \*---------------------------------------------------------------------------*/
\r
6223 CommentDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
6225 static HANDLE hwndText = NULL;
\r
6226 int len, newSizeX, newSizeY, flags;
\r
6227 static int sizeX, sizeY;
\r
6232 switch (message) {
\r
6233 case WM_INITDIALOG: /* message: initialize dialog box */
\r
6234 /* Initialize the dialog items */
\r
6235 Translate(hDlg, DLG_EditComment);
\r
6236 hwndText = GetDlgItem(hDlg, OPT_CommentText);
\r
6237 SetDlgItemText(hDlg, OPT_CommentText, commentText);
\r
6238 EnableWindow(GetDlgItem(hDlg, OPT_CancelComment), editComment);
\r
6239 EnableWindow(GetDlgItem(hDlg, OPT_ClearComment), editComment);
\r
6240 EnableWindow(GetDlgItem(hDlg, OPT_EditComment), !editComment);
\r
6241 SendMessage(hwndText, EM_SETREADONLY, !editComment, 0);
\r
6242 SetWindowText(hDlg, commentTitle);
\r
6243 if (editComment) {
\r
6244 SetFocus(hwndText);
\r
6246 SetFocus(GetDlgItem(hDlg, IDOK));
\r
6248 SendMessage(GetDlgItem(hDlg, OPT_CommentText),
\r
6249 WM_SETFONT, (WPARAM)font[boardSize][COMMENT_FONT]->hf,
\r
6250 MAKELPARAM(FALSE, 0));
\r
6251 /* Size and position the dialog */
\r
6252 if (!commentDialog) {
\r
6253 commentDialog = hDlg;
\r
6254 flags = SWP_NOZORDER;
\r
6255 GetClientRect(hDlg, &rect);
\r
6256 sizeX = rect.right;
\r
6257 sizeY = rect.bottom;
\r
6258 if (wpComment.x != CW_USEDEFAULT && wpComment.y != CW_USEDEFAULT &&
\r
6259 wpComment.width != CW_USEDEFAULT && wpComment.height != CW_USEDEFAULT) {
\r
6260 WINDOWPLACEMENT wp;
\r
6261 EnsureOnScreen(&wpComment.x, &wpComment.y, 0, 0);
\r
6262 wp.length = sizeof(WINDOWPLACEMENT);
\r
6264 wp.showCmd = SW_SHOW;
\r
6265 wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;
\r
6266 wp.rcNormalPosition.left = wpComment.x;
\r
6267 wp.rcNormalPosition.right = wpComment.x + wpComment.width;
\r
6268 wp.rcNormalPosition.top = wpComment.y;
\r
6269 wp.rcNormalPosition.bottom = wpComment.y + wpComment.height;
\r
6270 SetWindowPlacement(hDlg, &wp);
\r
6272 GetClientRect(hDlg, &rect);
\r
6273 newSizeX = rect.right;
\r
6274 newSizeY = rect.bottom;
\r
6275 ResizeEditPlusButtons(hDlg, hwndText, sizeX, sizeY,
\r
6276 newSizeX, newSizeY);
\r
6281 SendDlgItemMessage( hDlg, OPT_CommentText, EM_SETEVENTMASK, 0, ENM_MOUSEEVENTS | ENM_KEYEVENTS );
\r
6284 case WM_COMMAND: /* message: received a command */
\r
6285 switch (LOWORD(wParam)) {
\r
6287 if (editComment) {
\r
6289 /* Read changed options from the dialog box */
\r
6290 hwndText = GetDlgItem(hDlg, OPT_CommentText);
\r
6291 len = GetWindowTextLength(hwndText);
\r
6292 str = (char *) malloc(len + 1);
\r
6293 GetWindowText(hwndText, str, len + 1);
\r
6302 ReplaceComment(commentIndex, str);
\r
6309 case OPT_CancelComment:
\r
6313 case OPT_ClearComment:
\r
6314 SetDlgItemText(hDlg, OPT_CommentText, "");
\r
6317 case OPT_EditComment:
\r
6318 EditCommentEvent();
\r
6326 case WM_NOTIFY: // [HGM] vari: cloned from whistory.c
\r
6327 if( wParam == OPT_CommentText ) {
\r
6328 MSGFILTER * lpMF = (MSGFILTER *) lParam;
\r
6330 if( lpMF->msg == WM_RBUTTONDOWN && (lpMF->wParam & (MK_CONTROL | MK_SHIFT)) == 0 ||
\r
6331 lpMF->msg == WM_CHAR && lpMF->wParam == '\022' ) {
\r
6335 pt.x = LOWORD( lpMF->lParam );
\r
6336 pt.y = HIWORD( lpMF->lParam );
\r
6338 if(lpMF->msg == WM_CHAR) {
\r
6340 SendDlgItemMessage( hDlg, OPT_CommentText, EM_EXGETSEL, 0, (LPARAM) &sel );
\r
6341 index = sel.cpMin;
\r
6343 index = SendDlgItemMessage( hDlg, OPT_CommentText, EM_CHARFROMPOS, 0, (LPARAM) &pt );
\r
6345 hwndText = GetDlgItem(hDlg, OPT_CommentText); // cloned from above
\r
6346 len = GetWindowTextLength(hwndText);
\r
6347 str = (char *) malloc(len + 1);
\r
6348 GetWindowText(hwndText, str, len + 1);
\r
6349 ReplaceComment(commentIndex, str);
\r
6350 if(commentIndex != currentMove) ToNrEvent(commentIndex);
\r
6351 LoadVariation( index, str ); // [HGM] also does the actual moving to it, now
\r
6354 /* Zap the message for good: apparently, returning non-zero is not enough */
\r
6355 lpMF->msg = WM_USER;
\r
6363 newSizeX = LOWORD(lParam);
\r
6364 newSizeY = HIWORD(lParam);
\r
6365 ResizeEditPlusButtons(hDlg, hwndText, sizeX, sizeY, newSizeX, newSizeY);
\r
6370 case WM_GETMINMAXINFO:
\r
6371 /* Prevent resizing window too small */
\r
6372 mmi = (MINMAXINFO *) lParam;
\r
6373 mmi->ptMinTrackSize.x = 100;
\r
6374 mmi->ptMinTrackSize.y = 100;
\r
6381 EitherCommentPopUp(int index, char *title, char *str, BOOLEAN edit)
\r
6386 CheckMenuItem(GetMenu(hwndMain), IDM_Comment, edit ? MF_CHECKED : MF_UNCHECKED);
\r
6388 if (str == NULL) str = "";
\r
6389 p = (char *) malloc(2 * strlen(str) + 2);
\r
6392 if (*str == '\n') *q++ = '\r';
\r
6396 if (commentText != NULL) free(commentText);
\r
6398 commentIndex = index;
\r
6399 commentTitle = title;
\r
6401 editComment = edit;
\r
6403 if (commentDialog) {
\r
6404 SendMessage(commentDialog, WM_INITDIALOG, 0, 0);
\r
6405 if (!commentUp) ShowWindow(commentDialog, SW_SHOW);
\r
6407 lpProc = MakeProcInstance((FARPROC)CommentDialog, hInst);
\r
6408 CreateDialog(hInst, MAKEINTRESOURCE(DLG_EditComment),
\r
6409 hwndMain, (DLGPROC)lpProc);
\r
6410 FreeProcInstance(lpProc);
\r
6416 /*---------------------------------------------------------------------------*\
\r
6418 * Type-in move dialog functions
\r
6420 \*---------------------------------------------------------------------------*/
\r
6423 TypeInMoveDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
6425 char move[MSG_SIZ];
\r
6428 switch (message) {
\r
6429 case WM_INITDIALOG:
\r
6430 move[0] = (char) lParam;
\r
6431 move[1] = NULLCHAR;
\r
6432 CenterWindowEx(hDlg, GetWindow(hDlg, GW_OWNER), 1 );
\r
6433 Translate(hDlg, DLG_TypeInMove);
\r
6434 hInput = GetDlgItem(hDlg, OPT_Move);
\r
6435 SetWindowText(hInput, move);
\r
6437 SendMessage(hInput, EM_SETSEL, (WPARAM)9999, (LPARAM)9999);
\r
6441 switch (LOWORD(wParam)) {
\r
6444 shiftKey = GetKeyState(VK_SHIFT) < 0; // [HGM] remember last shift status
\r
6445 GetDlgItemText(hDlg, OPT_Move, move, sizeof(move));
6446 TypeInDoneEvent(move);
\r
6447 EndDialog(hDlg, TRUE);
\r
6450 EndDialog(hDlg, FALSE);
\r
6461 PopUpMoveDialog(char firstchar)
\r
6465 lpProc = MakeProcInstance((FARPROC)TypeInMoveDialog, hInst);
\r
6466 DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_TypeInMove),
\r
6467 hwndMain, (DLGPROC)lpProc, (LPARAM)firstchar);
\r
6468 FreeProcInstance(lpProc);
\r
6471 /*---------------------------------------------------------------------------*\
\r
6473 * Type-in name dialog functions
\r
6475 \*---------------------------------------------------------------------------*/
\r
6478 TypeInNameDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
6480 char move[MSG_SIZ];
\r
6483 switch (message) {
\r
6484 case WM_INITDIALOG:
\r
6485 move[0] = (char) lParam;
\r
6486 move[1] = NULLCHAR;
\r
6487 CenterWindowEx(hDlg, GetWindow(hDlg, GW_OWNER), 1 );
\r
6488 Translate(hDlg, DLG_TypeInName);
\r
6489 hInput = GetDlgItem(hDlg, OPT_Name);
\r
6490 SetWindowText(hInput, move);
\r
6492 SendMessage(hInput, EM_SETSEL, (WPARAM)9999, (LPARAM)9999);
\r
6496 switch (LOWORD(wParam)) {
\r
6498 GetDlgItemText(hDlg, OPT_Name, move, sizeof(move));
\r
6499 appData.userName = strdup(move);
\r
6502 if(gameMode == MachinePlaysWhite || gameMode == MachinePlaysBlack) {
\r
6503 snprintf(move, MSG_SIZ, "%s vs. %s", gameInfo.white, gameInfo.black);
\r
6504 DisplayTitle(move);
\r
6508 EndDialog(hDlg, TRUE);
\r
6511 EndDialog(hDlg, FALSE);
\r
6522 PopUpNameDialog(char firstchar)
\r
6526 lpProc = MakeProcInstance((FARPROC)TypeInNameDialog, hInst);
\r
6527 DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_TypeInName),
\r
6528 hwndMain, (DLGPROC)lpProc, (LPARAM)firstchar);
\r
6529 FreeProcInstance(lpProc);
\r
6532 /*---------------------------------------------------------------------------*\
\r
6536 \*---------------------------------------------------------------------------*/
\r
6538 /* Nonmodal error box */
\r
6539 LRESULT CALLBACK ErrorDialog(HWND hDlg, UINT message,
\r
6540 WPARAM wParam, LPARAM lParam);
\r
6543 ErrorPopUp(char *title, char *content)
\r
6547 BOOLEAN modal = hwndMain == NULL;
\r
6565 strncpy(errorTitle, title, sizeof(errorTitle));
\r
6566 errorTitle[sizeof(errorTitle) - 1] = '\0';
\r
6569 MessageBox(NULL, errorMessage, errorTitle, MB_OK|MB_ICONEXCLAMATION);
\r
6571 lpProc = MakeProcInstance((FARPROC)ErrorDialog, hInst);
\r
6572 CreateDialog(hInst, MAKEINTRESOURCE(DLG_Error),
\r
6573 hwndMain, (DLGPROC)lpProc);
\r
6574 FreeProcInstance(lpProc);
\r
6581 if (!appData.popupMoveErrors && moveErrorMessageUp) DisplayMessage("", "");
\r
6582 if (errorDialog == NULL) return;
\r
6583 DestroyWindow(errorDialog);
\r
6584 errorDialog = NULL;
\r
6585 if(errorExitStatus) ExitEvent(errorExitStatus);
\r
6589 ErrorDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
6594 switch (message) {
\r
6595 case WM_INITDIALOG:
\r
6596 GetWindowRect(hDlg, &rChild);
\r
6599 SetWindowPos(hDlg, NULL, rChild.left,
\r
6600 rChild.top + boardRect.top - (rChild.bottom - rChild.top),
\r
6601 0, 0, SWP_NOZORDER|SWP_NOSIZE);
\r
6605 [AS] It seems that the above code wants to move the dialog up in the "caption
\r
6606 area" of the main window, but it uses the dialog height as an hard-coded constant,
\r
6607 and it doesn't work when you resize the dialog.
\r
6608 For now, just give it a default position.
\r
6610 SetWindowPos(hDlg, NULL, boardRect.left+8, boardRect.top+8, 0, 0, SWP_NOZORDER|SWP_NOSIZE);
\r
6611 Translate(hDlg, DLG_Error);
\r
6613 errorDialog = hDlg;
\r
6614 SetWindowText(hDlg, errorTitle);
\r
6615 hwndText = GetDlgItem(hDlg, OPT_ErrorText);
\r
6616 SetDlgItemText(hDlg, OPT_ErrorText, errorMessage);
\r
6620 switch (LOWORD(wParam)) {
\r
6623 if (errorDialog == hDlg) errorDialog = NULL;
\r
6624 DestroyWindow(hDlg);
\r
6636 HWND gothicDialog = NULL;
\r
6639 GothicDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
6643 int height = GetSystemMetrics(SM_CYCAPTION)+GetSystemMetrics(SM_CYFRAME);
\r
6645 switch (message) {
\r
6646 case WM_INITDIALOG:
\r
6647 GetWindowRect(hDlg, &rChild);
\r
6649 SetWindowPos(hDlg, NULL, wpMain.x, wpMain.y-height, wpMain.width, height,
\r
6653 [AS] It seems that the above code wants to move the dialog up in the "caption
\r
6654 area" of the main window, but it uses the dialog height as an hard-coded constant,
\r
6655 and it doesn't work when you resize the dialog.
\r
6656 For now, just give it a default position.
\r
6658 gothicDialog = hDlg;
\r
6659 SetWindowText(hDlg, errorTitle);
\r
6660 hwndText = GetDlgItem(hDlg, OPT_ErrorText);
\r
6661 SetDlgItemText(hDlg, OPT_ErrorText, errorMessage);
\r
6665 switch (LOWORD(wParam)) {
\r
6668 if (errorDialog == hDlg) errorDialog = NULL;
\r
6669 DestroyWindow(hDlg);
\r
6681 GothicPopUp(char *title, VariantClass variant)
\r
6684 static char *lastTitle;
\r
6686 strncpy(errorTitle, title, sizeof(errorTitle));
\r
6687 errorTitle[sizeof(errorTitle) - 1] = '\0';
\r
6689 if(lastTitle != title && gothicDialog != NULL) {
\r
6690 DestroyWindow(gothicDialog);
\r
6691 gothicDialog = NULL;
\r
6693 if(variant != VariantNormal && gothicDialog == NULL) {
\r
6694 title = lastTitle;
\r
6695 lpProc = MakeProcInstance((FARPROC)GothicDialog, hInst);
\r
6696 CreateDialog(hInst, MAKEINTRESOURCE(DLG_Error),
\r
6697 hwndMain, (DLGPROC)lpProc);
\r
6698 FreeProcInstance(lpProc);
\r
6703 /*---------------------------------------------------------------------------*\
\r
6705 * Ics Interaction console functions
\r
6707 \*---------------------------------------------------------------------------*/
\r
6709 #define HISTORY_SIZE 64
\r
6710 static char *history[HISTORY_SIZE];
\r
6711 int histIn = 0, histP = 0;
\r
6714 SaveInHistory(char *cmd)
\r
6716 if (history[histIn] != NULL) {
\r
6717 free(history[histIn]);
\r
6718 history[histIn] = NULL;
\r
6720 if (*cmd == NULLCHAR) return;
\r
6721 history[histIn] = StrSave(cmd);
\r
6722 histIn = (histIn + 1) % HISTORY_SIZE;
\r
6723 if (history[histIn] != NULL) {
\r
6724 free(history[histIn]);
\r
6725 history[histIn] = NULL;
\r
6731 PrevInHistory(char *cmd)
\r
6734 if (histP == histIn) {
\r
6735 if (history[histIn] != NULL) free(history[histIn]);
\r
6736 history[histIn] = StrSave(cmd);
\r
6738 newhp = (histP - 1 + HISTORY_SIZE) % HISTORY_SIZE;
\r
6739 if (newhp == histIn || history[newhp] == NULL) return NULL;
\r
6741 return history[histP];
\r
6747 if (histP == histIn) return NULL;
\r
6748 histP = (histP + 1) % HISTORY_SIZE;
\r
6749 return history[histP];
\r
6753 LoadIcsTextMenu(IcsTextMenuEntry *e)
\r
6757 hmenu = LoadMenu(hInst, "TextMenu");
\r
6758 h = GetSubMenu(hmenu, 0);
\r
6760 if (strcmp(e->item, "-") == 0) {
\r
6761 AppendMenu(h, MF_SEPARATOR, 0, 0);
\r
6762 } else { // [HGM] re-written a bit to use only one AppendMenu call for both cases (| or no |)
\r
6763 int flags = MF_STRING, j = 0;
\r
6764 if (e->item[0] == '|') {
\r
6765 flags |= MF_MENUBARBREAK;
\r
6768 if(!strcmp(e->command, "none")) flags |= MF_GRAYED; // [HGM] chatclick: provide inactive dummy
\r
6769 AppendMenu(h, flags, IDM_CommandX + i, e->item + j);
\r
6777 WNDPROC consoleTextWindowProc;
\r
6780 CommandX(HWND hwnd, char *command, BOOLEAN getname, BOOLEAN immediate)
\r
6782 char buf[MSG_SIZ], name[MSG_SIZ];
\r
6783 HWND hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
6787 SetWindowText(hInput, command);
\r
6789 SendMessage(hInput, WM_CHAR, '\r', 0);
\r
6791 sel.cpMin = 999999;
\r
6792 sel.cpMax = 999999;
\r
6793 SendMessage(hInput, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
6798 SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);
\r
6799 if (sel.cpMin == sel.cpMax) {
\r
6800 /* Expand to surrounding word */
\r
6803 tr.chrg.cpMax = sel.cpMin;
\r
6804 tr.chrg.cpMin = --sel.cpMin;
\r
6805 if (sel.cpMin < 0) break;
\r
6806 tr.lpstrText = name;
\r
6807 SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr);
\r
6808 } while (isalpha(name[0]) || isdigit(name[0]) || name[0] == '-');
\r
6812 tr.chrg.cpMin = sel.cpMax;
\r
6813 tr.chrg.cpMax = ++sel.cpMax;
\r
6814 tr.lpstrText = name;
\r
6815 if (SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr) < 1) break;
\r
6816 } while (isalpha(name[0]) || isdigit(name[0]) || name[0] == '-');
\r
6819 if (sel.cpMax == sel.cpMin || sel.cpMax - sel.cpMin > MSG_SIZ/2) {
\r
6820 MessageBeep(MB_ICONEXCLAMATION);
\r
6824 tr.lpstrText = name;
\r
6825 SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr);
\r
6827 if (sel.cpMax - sel.cpMin > MSG_SIZ/2) {
\r
6828 MessageBeep(MB_ICONEXCLAMATION);
\r
6831 SendMessage(hwnd, EM_GETSELTEXT, 0, (LPARAM) name);
\r
6834 if(strstr(command, "%s")) snprintf(buf, MSG_SIZ, command, name); else
\r
6835 snprintf(buf, MSG_SIZ, "%s %s", command, name);
\r
6836 SetWindowText(hInput, buf);
\r
6837 SendMessage(hInput, WM_CHAR, '\r', 0);
\r
6839 if(!strcmp(command, "chat")) { ChatPopUp(name); return; }
\r
6840 snprintf(buf, MSG_SIZ, "%s %s ", command, name); /* trailing space */
\r
6841 SetWindowText(hInput, buf);
\r
6842 sel.cpMin = 999999;
\r
6843 sel.cpMax = 999999;
\r
6844 SendMessage(hInput, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
6850 ConsoleTextSubclass(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
\r
6855 switch (message) {
\r
6857 if (!(GetKeyState(VK_CONTROL) & ~1)) break;
\r
6858 if(wParam=='R') return 0;
\r
6861 SendMessage(hwnd, EM_LINESCROLL, 0, -999999);
\r
6864 sel.cpMin = 999999;
\r
6865 sel.cpMax = 999999;
\r
6866 SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
6867 SendMessage(hwnd, EM_SCROLLCARET, 0, 0);
\r
6872 if(wParam != '\022') {
\r
6873 if (wParam == '\t') {
\r
6874 if (GetKeyState(VK_SHIFT) < 0) {
\r
6876 if (IsIconic(hwndMain)) ShowWindow(hwndMain, SW_RESTORE);
\r
6877 if (buttonDesc[0].hwnd) {
\r
6878 SetFocus(buttonDesc[0].hwnd);
\r
6880 SetFocus(hwndMain);
\r
6884 SetFocus(GetDlgItem(hwndConsole, OPT_ConsoleInput));
\r
6887 hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
6888 JAWS_DELETE( SetFocus(hInput); )
\r
6889 SendMessage(hInput, message, wParam, lParam);
\r
6892 } // [HGM] navigate: for Ctrl+R, flow into next case (moved up here) to summon up menu
\r
6894 case WM_RBUTTONDOWN:
\r
6895 if (!(GetKeyState(VK_SHIFT) & ~1)) {
\r
6896 /* Move selection here if it was empty */
\r
6898 pt.x = LOWORD(lParam);
\r
6899 pt.y = HIWORD(lParam);
\r
6900 SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);
\r
6901 if (sel.cpMin == sel.cpMax) {
\r
6902 if(lParam != -1) sel.cpMin = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&pt); /*doc is wrong*/
\r
6903 sel.cpMax = sel.cpMin;
\r
6904 SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
6906 SendMessage(hwnd, EM_HIDESELECTION, FALSE, FALSE);
\r
6907 { // [HGM] chatclick: code moved here from WM_RBUTTONUP case, to have menu appear on down-click
\r
6909 HMENU hmenu = LoadIcsTextMenu(icsTextMenuEntry);
\r
6910 SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);
\r
6911 if (sel.cpMin == sel.cpMax) {
\r
6912 EnableMenuItem(hmenu, IDM_Copy, MF_BYCOMMAND|MF_GRAYED);
\r
6913 EnableMenuItem(hmenu, IDM_QuickPaste, MF_BYCOMMAND|MF_GRAYED);
\r
6915 if (!IsClipboardFormatAvailable(CF_TEXT)) {
\r
6916 EnableMenuItem(hmenu, IDM_Paste, MF_BYCOMMAND|MF_GRAYED);
\r
6918 pt.x = LOWORD(lParam)-30; // [HGM] chatclick: make menu pop up with pointer above upper-right item
\r
6919 pt.y = HIWORD(lParam)-10; // make it appear as if mouse moved there, so it will be selected on up-click
\r
6920 PostMessage(hwnd, WM_MOUSEMOVE, wParam, lParam+5);
\r
6921 MenuPopup(hwnd, pt, hmenu, -1);
\r
6925 case WM_RBUTTONUP:
\r
6926 if (GetKeyState(VK_SHIFT) & ~1) {
\r
6927 SendDlgItemMessage(hwndConsole, OPT_ConsoleText,
\r
6928 WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);
\r
6932 hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
6934 return SendMessage(hInput, message, wParam, lParam);
\r
6935 case WM_MBUTTONDOWN:
\r
6936 return SendMessage(hwnd, WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);
\r
6938 switch (LOWORD(wParam)) {
\r
6939 case IDM_QuickPaste:
\r
6941 SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);
\r
6942 if (sel.cpMin == sel.cpMax) {
\r
6943 MessageBeep(MB_ICONEXCLAMATION);
\r
6946 SendMessage(hwnd, WM_COPY, 0, 0);
\r
6947 hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
6948 SendMessage(hInput, WM_PASTE, 0, 0);
\r
6953 SendMessage(hwnd, WM_CUT, 0, 0);
\r
6956 SendMessage(hwnd, WM_PASTE, 0, 0);
\r
6959 SendMessage(hwnd, WM_COPY, 0, 0);
\r
6963 int i = LOWORD(wParam) - IDM_CommandX;
\r
6964 if (i >= 0 && i < ICS_TEXT_MENU_SIZE &&
\r
6965 icsTextMenuEntry[i].command != NULL) {
\r
6966 CommandX(hwnd, icsTextMenuEntry[i].command,
\r
6967 icsTextMenuEntry[i].getname,
\r
6968 icsTextMenuEntry[i].immediate);
\r
6976 return (*consoleTextWindowProc)(hwnd, message, wParam, lParam);
\r
6979 WNDPROC consoleInputWindowProc;
\r
6982 ConsoleInputSubclass(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
\r
6984 char buf[MSG_SIZ];
\r
6986 static BOOL sendNextChar = FALSE;
\r
6987 static BOOL quoteNextChar = FALSE;
\r
6988 InputSource *is = consoleInputSource;
\r
6992 switch (message) {
\r
6994 if (!appData.localLineEditing || sendNextChar) {
\r
6995 is->buf[0] = (CHAR) wParam;
\r
6997 SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);
\r
6998 sendNextChar = FALSE;
\r
7001 if (quoteNextChar) {
\r
7002 buf[0] = (char) wParam;
\r
7003 buf[1] = NULLCHAR;
\r
7004 SendMessage(hwnd, EM_REPLACESEL, TRUE, (LPARAM) buf);
\r
7005 quoteNextChar = FALSE;
\r
7009 case '\r': /* Enter key */
\r
7010 is->count = GetWindowText(hwnd, is->buf, INPUT_SOURCE_BUF_SIZE-1);
\r
7011 if (consoleEcho) SaveInHistory(is->buf);
\r
7012 is->buf[is->count++] = '\n';
\r
7013 is->buf[is->count] = NULLCHAR;
\r
7014 SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);
\r
7015 if (consoleEcho) {
\r
7016 ConsoleOutput(is->buf, is->count, TRUE);
\r
7017 } else if (appData.localLineEditing) {
\r
7018 ConsoleOutput("\n", 1, TRUE);
\r
7021 case '\033': /* Escape key */
\r
7022 SetWindowText(hwnd, "");
\r
7023 cf.cbSize = sizeof(CHARFORMAT);
\r
7024 cf.dwMask = CFM_COLOR|CFM_BOLD|CFM_ITALIC|CFM_UNDERLINE|CFM_STRIKEOUT;
\r
7025 if (consoleEcho) {
\r
7026 cf.crTextColor = textAttribs[ColorNormal].color;
\r
7028 cf.crTextColor = COLOR_ECHOOFF;
\r
7030 cf.dwEffects = textAttribs[ColorNormal].effects;
\r
7031 SendMessage(hwnd, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf);
\r
7033 case '\t': /* Tab key */
\r
7034 if (GetKeyState(VK_SHIFT) < 0) {
\r
7036 SetFocus(GetDlgItem(hwndConsole, OPT_ConsoleText));
\r
7039 if (IsIconic(hwndMain)) ShowWindow(hwndMain, SW_RESTORE);
\r
7040 if (buttonDesc[0].hwnd) {
\r
7041 SetFocus(buttonDesc[0].hwnd);
\r
7043 SetFocus(hwndMain);
\r
7047 case '\023': /* Ctrl+S */
\r
7048 sendNextChar = TRUE;
\r
7050 case '\021': /* Ctrl+Q */
\r
7051 quoteNextChar = TRUE;
\r
7061 GetWindowText(hwnd, buf, MSG_SIZ);
\r
7062 p = PrevInHistory(buf);
\r
7064 SetWindowText(hwnd, p);
\r
7065 sel.cpMin = 999999;
\r
7066 sel.cpMax = 999999;
\r
7067 SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
7072 p = NextInHistory();
\r
7074 SetWindowText(hwnd, p);
\r
7075 sel.cpMin = 999999;
\r
7076 sel.cpMax = 999999;
\r
7077 SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
7083 if (!(GetKeyState(VK_CONTROL) & ~1)) break;
\r
7087 SendDlgItemMessage(hwndConsole, OPT_ConsoleText, message, wParam, lParam);
\r
7091 case WM_MBUTTONDOWN:
\r
7092 SendDlgItemMessage(hwndConsole, OPT_ConsoleText,
\r
7093 WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);
\r
7095 case WM_RBUTTONUP:
\r
7096 if (GetKeyState(VK_SHIFT) & ~1) {
\r
7097 SendDlgItemMessage(hwndConsole, OPT_ConsoleText,
\r
7098 WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);
\r
7102 hmenu = LoadMenu(hInst, "InputMenu");
\r
7103 SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);
\r
7104 if (sel.cpMin == sel.cpMax) {
\r
7105 EnableMenuItem(hmenu, IDM_Copy, MF_BYCOMMAND|MF_GRAYED);
\r
7106 EnableMenuItem(hmenu, IDM_Cut, MF_BYCOMMAND|MF_GRAYED);
\r
7108 if (!IsClipboardFormatAvailable(CF_TEXT)) {
\r
7109 EnableMenuItem(hmenu, IDM_Paste, MF_BYCOMMAND|MF_GRAYED);
\r
7111 pt.x = LOWORD(lParam);
\r
7112 pt.y = HIWORD(lParam);
\r
7113 MenuPopup(hwnd, pt, hmenu, -1);
\r
7117 switch (LOWORD(wParam)) {
\r
7119 SendMessage(hwnd, EM_UNDO, 0, 0);
\r
7121 case IDM_SelectAll:
\r
7123 sel.cpMax = -1; /*999999?*/
\r
7124 SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
7127 SendMessage(hwnd, WM_CUT, 0, 0);
\r
7130 SendMessage(hwnd, WM_PASTE, 0, 0);
\r
7133 SendMessage(hwnd, WM_COPY, 0, 0);
\r
7138 return (*consoleInputWindowProc)(hwnd, message, wParam, lParam);
\r
7141 #define CO_MAX 100000
\r
7142 #define CO_TRIM 1000
\r
7145 ConsoleWndProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
7147 static SnapData sd;
\r
7148 HWND hText, hInput;
\r
7150 static int sizeX, sizeY;
\r
7151 int newSizeX, newSizeY;
\r
7155 hText = GetDlgItem(hDlg, OPT_ConsoleText);
\r
7156 hInput = GetDlgItem(hDlg, OPT_ConsoleInput);
\r
7158 switch (message) {
\r
7160 if (((NMHDR*)lParam)->code == EN_LINK)
\r
7162 ENLINK *pLink = (ENLINK*)lParam;
\r
7163 if (pLink->msg == WM_LBUTTONUP)
\r
7167 tr.chrg = pLink->chrg;
\r
7168 tr.lpstrText = malloc(1+tr.chrg.cpMax-tr.chrg.cpMin);
\r
7169 SendMessage(hText, EM_GETTEXTRANGE, 0, (LPARAM)&tr);
\r
7170 ShellExecute(NULL, "open", tr.lpstrText, NULL, NULL, SW_SHOW);
\r
7171 free(tr.lpstrText);
\r
7175 case WM_INITDIALOG: /* message: initialize dialog box */
\r
7176 hwndConsole = hDlg;
\r
7178 consoleTextWindowProc = (WNDPROC)
\r
7179 SetWindowLongPtr(hText, GWLP_WNDPROC, (LONG_PTR) ConsoleTextSubclass);
\r
7180 SendMessage(hText, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);
\r
7181 consoleInputWindowProc = (WNDPROC)
\r
7182 SetWindowLongPtr(hInput, GWLP_WNDPROC, (LONG_PTR) ConsoleInputSubclass);
\r
7183 SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);
\r
7184 Colorize(ColorNormal, TRUE);
\r
7185 SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &consoleCF);
\r
7186 ChangedConsoleFont();
\r
7187 GetClientRect(hDlg, &rect);
\r
7188 sizeX = rect.right;
\r
7189 sizeY = rect.bottom;
\r
7190 if (wpConsole.x != CW_USEDEFAULT && wpConsole.y != CW_USEDEFAULT &&
\r
7191 wpConsole.width != CW_USEDEFAULT && wpConsole.height != CW_USEDEFAULT) {
\r
7192 WINDOWPLACEMENT wp;
\r
7193 EnsureOnScreen(&wpConsole.x, &wpConsole.y, 0, 0);
\r
7194 wp.length = sizeof(WINDOWPLACEMENT);
\r
7196 wp.showCmd = SW_SHOW;
\r
7197 wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;
\r
7198 wp.rcNormalPosition.left = wpConsole.x;
\r
7199 wp.rcNormalPosition.right = wpConsole.x + wpConsole.width;
\r
7200 wp.rcNormalPosition.top = wpConsole.y;
\r
7201 wp.rcNormalPosition.bottom = wpConsole.y + wpConsole.height;
\r
7202 SetWindowPlacement(hDlg, &wp);
\r
7205 // [HGM] Chessknight's change 2004-07-13
\r
7206 else { /* Determine Defaults */
\r
7207 WINDOWPLACEMENT wp;
\r
7208 wpConsole.x = wpMain.width + 1;
\r
7209 wpConsole.y = wpMain.y;
\r
7210 wpConsole.width = screenWidth - wpMain.width;
\r
7211 wpConsole.height = wpMain.height;
\r
7212 EnsureOnScreen(&wpConsole.x, &wpConsole.y, 0, 0);
\r
7213 wp.length = sizeof(WINDOWPLACEMENT);
\r
7215 wp.showCmd = SW_SHOW;
\r
7216 wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;
\r
7217 wp.rcNormalPosition.left = wpConsole.x;
\r
7218 wp.rcNormalPosition.right = wpConsole.x + wpConsole.width;
\r
7219 wp.rcNormalPosition.top = wpConsole.y;
\r
7220 wp.rcNormalPosition.bottom = wpConsole.y + wpConsole.height;
\r
7221 SetWindowPlacement(hDlg, &wp);
\r
7224 // Allow hText to highlight URLs and send notifications on them
\r
7225 wMask = (WORD) SendMessage(hText, EM_GETEVENTMASK, 0, 0L);
\r
7226 SendMessage(hText, EM_SETEVENTMASK, 0, wMask | ENM_LINK);
\r
7227 SendMessage(hText, EM_AUTOURLDETECT, TRUE, 0L);
\r
7228 SetWindowLongPtr(hText, GWLP_USERDATA, 79); // initialize the text window's width
\r
7242 if (IsIconic(hDlg)) break;
\r
7243 newSizeX = LOWORD(lParam);
\r
7244 newSizeY = HIWORD(lParam);
\r
7245 if (sizeX != newSizeX || sizeY != newSizeY) {
\r
7246 RECT rectText, rectInput;
\r
7248 int newTextHeight, newTextWidth;
\r
7249 GetWindowRect(hText, &rectText);
\r
7250 newTextWidth = rectText.right - rectText.left + newSizeX - sizeX;
\r
7251 newTextHeight = rectText.bottom - rectText.top + newSizeY - sizeY;
\r
7252 if (newTextHeight < 0) {
\r
7253 newSizeY += -newTextHeight;
\r
7254 newTextHeight = 0;
\r
7256 SetWindowPos(hText, NULL, 0, 0,
\r
7257 newTextWidth, newTextHeight, SWP_NOZORDER|SWP_NOMOVE);
\r
7258 GetWindowRect(hInput, &rectInput); /* gives screen coords */
\r
7259 pt.x = rectInput.left;
\r
7260 pt.y = rectInput.top + newSizeY - sizeY;
\r
7261 ScreenToClient(hDlg, &pt);
\r
7262 SetWindowPos(hInput, NULL,
\r
7263 pt.x, pt.y, /* needs client coords */
\r
7264 rectInput.right - rectInput.left + newSizeX - sizeX,
\r
7265 rectInput.bottom - rectInput.top, SWP_NOZORDER);
\r
7271 case WM_GETMINMAXINFO:
\r
7272 /* Prevent resizing window too small */
\r
7273 mmi = (MINMAXINFO *) lParam;
\r
7274 mmi->ptMinTrackSize.x = 100;
\r
7275 mmi->ptMinTrackSize.y = 100;
\r
7278 /* [AS] Snapping */
\r
7279 case WM_ENTERSIZEMOVE:
\r
7280 return OnEnterSizeMove( &sd, hDlg, wParam, lParam );
\r
7283 return OnSizing( &sd, hDlg, wParam, lParam );
\r
7286 return OnMoving( &sd, hDlg, wParam, lParam );
\r
7288 case WM_EXITSIZEMOVE:
\r
7289 UpdateICSWidth(hText);
\r
7290 return OnExitSizeMove( &sd, hDlg, wParam, lParam );
\r
7293 return DefWindowProc(hDlg, message, wParam, lParam);
\r
7301 if (hwndConsole) return;
\r
7302 hCons = CreateDialog(hInst, szConsoleName, 0, NULL);
\r
7303 SendMessage(hCons, WM_INITDIALOG, 0, 0);
\r
7308 ConsoleOutput(char* data, int length, int forceVisible)
\r
7313 char buf[CO_MAX+1];
\r
7316 static int delayLF = 0;
\r
7317 CHARRANGE savesel, sel;
\r
7319 if (hwndConsole == NULL || length > CO_MAX-100 || length == 0) return;
\r
7327 while (length--) {
\r
7335 } else if (*p == '\007') {
\r
7336 MyPlaySound(&sounds[(int)SoundBell]);
\r
7343 hText = GetDlgItem(hwndConsole, OPT_ConsoleText);
\r
7344 SendMessage(hText, EM_HIDESELECTION, TRUE, FALSE);
\r
7345 /* Save current selection */
\r
7346 SendMessage(hText, EM_EXGETSEL, 0, (LPARAM)&savesel);
\r
7347 exlen = GetWindowTextLength(hText);
\r
7348 /* Find out whether current end of text is visible */
\r
7349 SendMessage(hText, EM_GETRECT, 0, (LPARAM) &rect);
\r
7350 SendMessage(hText, EM_POSFROMCHAR, (WPARAM) &pEnd, exlen);
\r
7351 /* Trim existing text if it's too long */
\r
7352 if (exlen + (q - buf) > CO_MAX) {
\r
7353 trim = (CO_TRIM > (q - buf)) ? CO_TRIM : (q - buf);
\r
7356 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
7357 SendMessage(hText, EM_REPLACESEL, 0, (LPARAM)"");
\r
7359 savesel.cpMin -= trim;
\r
7360 savesel.cpMax -= trim;
\r
7361 if (exlen < 0) exlen = 0;
\r
7362 if (savesel.cpMin < 0) savesel.cpMin = 0;
\r
7363 if (savesel.cpMax < savesel.cpMin) savesel.cpMax = savesel.cpMin;
\r
7365 /* Append the new text */
\r
7366 sel.cpMin = exlen;
\r
7367 sel.cpMax = exlen;
\r
7368 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
7369 SendMessage(hText, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&consoleCF);
\r
7370 SendMessage(hText, EM_REPLACESEL, 0, (LPARAM) buf);
\r
7371 if (forceVisible || exlen == 0 ||
\r
7372 (rect.left <= pEnd.x && pEnd.x < rect.right &&
\r
7373 rect.top <= pEnd.y && pEnd.y < rect.bottom)) {
\r
7374 /* Scroll to make new end of text visible if old end of text
\r
7375 was visible or new text is an echo of user typein */
\r
7376 sel.cpMin = 9999999;
\r
7377 sel.cpMax = 9999999;
\r
7378 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
7379 SendMessage(hText, EM_HIDESELECTION, FALSE, FALSE);
\r
7380 SendMessage(hText, EM_SCROLLCARET, 0, 0);
\r
7381 SendMessage(hText, EM_HIDESELECTION, TRUE, FALSE);
\r
7383 if (savesel.cpMax == exlen || forceVisible) {
\r
7384 /* Move insert point to new end of text if it was at the old
\r
7385 end of text or if the new text is an echo of user typein */
\r
7386 sel.cpMin = 9999999;
\r
7387 sel.cpMax = 9999999;
\r
7388 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
7390 /* Restore previous selection */
\r
7391 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&savesel);
\r
7393 SendMessage(hText, EM_HIDESELECTION, FALSE, FALSE);
\r
7400 DisplayHoldingsCount(HDC hdc, int x, int y, int rightAlign, int copyNumber)
\r
7404 COLORREF oldFg, oldBg;
\r
7409 snprintf(buf, sizeof(buf)/sizeof(buf[0]),"%d", copyNumber); else buf[0] = 0;
\r
7411 oldFg = SetTextColor(hdc, RGB(255, 255, 255)); /* white */
\r
7412 oldBg = SetBkColor(hdc, RGB(0, 0, 0)); /* black */
\r
7413 oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);
\r
7416 rect.right = x + squareSize;
\r
7418 rect.bottom = y + squareSize;
\r
7421 ExtTextOut(hdc, x + MESSAGE_LINE_LEFTMARGIN
\r
7422 + (rightAlign ? (squareSize*2)/3 : 0),
\r
7423 y, ETO_CLIPPED|ETO_OPAQUE,
\r
7424 &rect, str, strlen(str), NULL);
\r
7426 (void) SetTextColor(hdc, oldFg);
\r
7427 (void) SetBkColor(hdc, oldBg);
\r
7428 (void) SelectObject(hdc, oldFont);
\r
7432 DisplayAClock(HDC hdc, int timeRemaining, int highlight,
\r
7433 RECT *rect, char *color, char *flagFell)
\r
7437 COLORREF oldFg, oldBg;
\r
7440 if (appData.clockMode) {
\r
7442 snprintf(buf, sizeof(buf)/sizeof(buf[0]), "%c %s %s", color[0], TimeString(timeRemaining), flagFell);
\r
7444 snprintf(buf, sizeof(buf)/sizeof(buf[0]), "%s:%c%s %s", color, (logoHeight>0 ? 0 : ' '), TimeString(timeRemaining), flagFell);
\r
7451 oldFg = SetTextColor(hdc, RGB(255, 255, 255)); /* white */
\r
7452 oldBg = SetBkColor(hdc, RGB(0, 0, 0)); /* black */
\r
7454 oldFg = SetTextColor(hdc, RGB(0, 0, 0)); /* black */
\r
7455 oldBg = SetBkColor(hdc, RGB(255, 255, 255)); /* white */
\r
7457 oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);
\r
7461 ExtTextOut(hdc, rect->left + MESSAGE_LINE_LEFTMARGIN,
\r
7462 rect->top, ETO_CLIPPED|ETO_OPAQUE,
\r
7463 rect, str, strlen(str), NULL);
\r
7464 if(logoHeight > 0 && appData.clockMode) {
\r
7466 str += strlen(color)+2;
\r
7467 r.top = rect->top + logoHeight/2;
\r
7468 r.left = rect->left;
\r
7469 r.right = rect->right;
\r
7470 r.bottom = rect->bottom;
\r
7471 ExtTextOut(hdc, rect->left + MESSAGE_LINE_LEFTMARGIN,
\r
7472 r.top, ETO_CLIPPED|ETO_OPAQUE,
\r
7473 &r, str, strlen(str), NULL);
\r
7475 (void) SetTextColor(hdc, oldFg);
\r
7476 (void) SetBkColor(hdc, oldBg);
\r
7477 (void) SelectObject(hdc, oldFont);
\r
7482 DoReadFile(HANDLE hFile, char *buf, int count, DWORD *outCount,
\r
7488 if( count <= 0 ) {
\r
7489 if (appData.debugMode) {
\r
7490 fprintf( debugFP, "DoReadFile: trying to read past end of buffer, overflow = %d\n", count );
\r
7493 return ERROR_INVALID_USER_BUFFER;
\r
7496 ResetEvent(ovl->hEvent);
\r
7497 ovl->Offset = ovl->OffsetHigh = 0;
\r
7498 ok = ReadFile(hFile, buf, count, outCount, ovl);
\r
7502 err = GetLastError();
\r
7503 if (err == ERROR_IO_PENDING) {
\r
7504 ok = GetOverlappedResult(hFile, ovl, outCount, TRUE);
\r
7508 err = GetLastError();
\r
7515 DoWriteFile(HANDLE hFile, char *buf, int count, DWORD *outCount,
\r
7520 ResetEvent(ovl->hEvent);
\r
7521 ovl->Offset = ovl->OffsetHigh = 0;
\r
7522 ok = WriteFile(hFile, buf, count, outCount, ovl);
\r
7526 err = GetLastError();
\r
7527 if (err == ERROR_IO_PENDING) {
\r
7528 ok = GetOverlappedResult(hFile, ovl, outCount, TRUE);
\r
7532 err = GetLastError();
\r
7538 /* [AS] If input is line by line and a line exceed the buffer size, force an error */
\r
7539 void CheckForInputBufferFull( InputSource * is )
\r
7541 if( is->lineByLine && (is->next - is->buf) >= INPUT_SOURCE_BUF_SIZE ) {
\r
7542 /* Look for end of line */
\r
7543 char * p = is->buf;
\r
7545 while( p < is->next && *p != '\n' ) {
\r
7549 if( p >= is->next ) {
\r
7550 if (appData.debugMode) {
\r
7551 fprintf( debugFP, "Input line exceeded buffer size (source id=%lu)\n", is->id );
\r
7554 is->error = ERROR_BROKEN_PIPE; /* [AS] Just any non-successful code! */
\r
7555 is->count = (DWORD) -1;
\r
7556 is->next = is->buf;
\r
7562 InputThread(LPVOID arg)
\r
7567 is = (InputSource *) arg;
\r
7568 ovl.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
\r
7569 ovl.Internal = ovl.InternalHigh = ovl.Offset = ovl.OffsetHigh = 0;
\r
7570 while (is->hThread != NULL) {
\r
7571 is->error = DoReadFile(is->hFile, is->next,
\r
7572 INPUT_SOURCE_BUF_SIZE - (is->next - is->buf),
\r
7573 &is->count, &ovl);
\r
7574 if (is->error == NO_ERROR) {
\r
7575 is->next += is->count;
\r
7577 if (is->error == ERROR_BROKEN_PIPE) {
\r
7578 /* Correct for MS brain damage. EOF reading a pipe is not an error. */
\r
7581 is->count = (DWORD) -1;
\r
7582 /* [AS] The (is->count <= 0) check below is not useful for unsigned values! */
\r
7587 CheckForInputBufferFull( is );
\r
7589 SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);
\r
7591 if( is->count == ((DWORD) -1) ) break; /* [AS] */
\r
7593 if (is->count <= 0) break; /* Quit on EOF or error */
\r
7596 CloseHandle(ovl.hEvent);
\r
7597 CloseHandle(is->hFile);
\r
7599 if (appData.debugMode) {
\r
7600 fprintf( debugFP, "Input thread terminated (id=%lu, error=%d, count=%ld)\n", is->id, is->error, is->count );
\r
7607 /* Windows 95 beta 2 won't let you do overlapped i/o on a console or pipe */
\r
7609 NonOvlInputThread(LPVOID arg)
\r
7616 is = (InputSource *) arg;
\r
7617 while (is->hThread != NULL) {
\r
7618 is->error = ReadFile(is->hFile, is->next,
\r
7619 INPUT_SOURCE_BUF_SIZE - (is->next - is->buf),
\r
7620 &is->count, NULL) ? NO_ERROR : GetLastError();
\r
7621 if (is->error == NO_ERROR) {
\r
7622 /* Change CRLF to LF */
\r
7623 if (is->next > is->buf) {
\r
7625 i = is->count + 1;
\r
7633 if (prev == '\r' && *p == '\n') {
\r
7645 if (is->error == ERROR_BROKEN_PIPE) {
\r
7646 /* Correct for MS brain damage. EOF reading a pipe is not an error. */
\r
7649 is->count = (DWORD) -1;
\r
7653 CheckForInputBufferFull( is );
\r
7655 SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);
\r
7657 if( is->count == ((DWORD) -1) ) break; /* [AS] */
\r
7659 if (is->count < 0) break; /* Quit on error */
\r
7661 CloseHandle(is->hFile);
\r
7666 SocketInputThread(LPVOID arg)
\r
7670 is = (InputSource *) arg;
\r
7671 while (is->hThread != NULL) {
\r
7672 is->count = recv(is->sock, is->buf, INPUT_SOURCE_BUF_SIZE, 0);
\r
7673 if ((int)is->count == SOCKET_ERROR) {
\r
7674 is->count = (DWORD) -1;
\r
7675 is->error = WSAGetLastError();
\r
7677 is->error = NO_ERROR;
\r
7678 is->next += is->count;
\r
7679 if (is->count == 0 && is->second == is) {
\r
7680 /* End of file on stderr; quit with no message */
\r
7684 SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);
\r
7686 if( is->count == ((DWORD) -1) ) break; /* [AS] */
\r
7688 if (is->count <= 0) break; /* Quit on EOF or error */
\r
7694 InputEvent(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
\r
7698 is = (InputSource *) lParam;
\r
7699 if (is->lineByLine) {
\r
7700 /* Feed in lines one by one */
\r
7701 char *p = is->buf;
\r
7703 while (q < is->next) {
\r
7704 if (*q++ == '\n') {
\r
7705 (is->func)(is, is->closure, p, q - p, NO_ERROR);
\r
7710 /* Move any partial line to the start of the buffer */
\r
7712 while (p < is->next) {
\r
7717 if (is->error != NO_ERROR || is->count == 0) {
\r
7718 /* Notify backend of the error. Note: If there was a partial
\r
7719 line at the end, it is not flushed through. */
\r
7720 (is->func)(is, is->closure, is->buf, is->count, is->error);
\r
7723 /* Feed in the whole chunk of input at once */
\r
7724 (is->func)(is, is->closure, is->buf, is->count, is->error);
\r
7725 is->next = is->buf;
\r
7729 /*---------------------------------------------------------------------------*\
\r
7731 * Menu enables. Used when setting various modes.
\r
7733 \*---------------------------------------------------------------------------*/
\r
7741 GreyRevert(Boolean grey)
\r
7742 { // [HGM] vari: for retracting variations in local mode
\r
7743 HMENU hmenu = GetMenu(hwndMain);
\r
7744 EnableMenuItem(hmenu, IDM_Revert, MF_BYCOMMAND|(grey ? MF_GRAYED : MF_ENABLED));
\r
7745 EnableMenuItem(hmenu, IDM_Annotate, MF_BYCOMMAND|(grey ? MF_GRAYED : MF_ENABLED));
\r
7749 SetMenuEnables(HMENU hmenu, Enables *enab)
\r
7751 while (enab->item > 0) {
\r
7752 (void) EnableMenuItem(hmenu, enab->item, enab->flags);
\r
7757 Enables gnuEnables[] = {
\r
7758 { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },
\r
7759 { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },
\r
7760 { IDM_IcsClient, MF_BYCOMMAND|MF_GRAYED },
\r
7761 { IDM_Accept, MF_BYCOMMAND|MF_GRAYED },
\r
7762 { IDM_Decline, MF_BYCOMMAND|MF_GRAYED },
\r
7763 { IDM_Rematch, MF_BYCOMMAND|MF_GRAYED },
\r
7764 { IDM_Adjourn, MF_BYCOMMAND|MF_GRAYED },
\r
7765 { IDM_StopExamining, MF_BYCOMMAND|MF_GRAYED },
\r
7766 { IDM_StopObserving, MF_BYCOMMAND|MF_GRAYED },
\r
7767 { IDM_Upload, MF_BYCOMMAND|MF_GRAYED },
\r
7768 { IDM_Revert, MF_BYCOMMAND|MF_GRAYED },
\r
7769 { IDM_Annotate, MF_BYCOMMAND|MF_GRAYED },
\r
7770 { IDM_NewChat, MF_BYCOMMAND|MF_GRAYED },
\r
7772 // Needed to switch from ncp to GNU mode on Engine Load
\r
7773 { ACTION_POS, MF_BYPOSITION|MF_ENABLED },
\r
7774 { IDM_MachineWhite, MF_BYCOMMAND|MF_ENABLED },
\r
7775 { IDM_MachineBlack, MF_BYCOMMAND|MF_ENABLED },
\r
7776 { IDM_TwoMachines, MF_BYCOMMAND|MF_ENABLED },
\r
7777 { IDM_Match, MF_BYCOMMAND|MF_ENABLED },
\r
7778 { IDM_AnalysisMode, MF_BYCOMMAND|MF_ENABLED },
\r
7779 { IDM_AnalyzeFile, MF_BYCOMMAND|MF_ENABLED },
\r
7780 { IDM_Engine1Options, MF_BYCOMMAND|MF_ENABLED },
\r
7781 { IDM_Engine2Options, MF_BYCOMMAND|MF_ENABLED },
\r
7782 { IDM_TimeControl, MF_BYCOMMAND|MF_ENABLED },
\r
7783 { IDM_RetractMove, MF_BYCOMMAND|MF_ENABLED },
\r
7784 { IDM_MoveNow, MF_BYCOMMAND|MF_ENABLED },
\r
7785 { IDM_Hint, MF_BYCOMMAND|MF_ENABLED },
\r
7786 { IDM_Book, MF_BYCOMMAND|MF_ENABLED },
\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_EditProgs2, MF_BYCOMMAND|MF_GRAYED },
\r
7805 { IDM_IcsOptions, MF_BYCOMMAND|MF_ENABLED },
\r
7806 { IDM_Engine1Options, MF_BYCOMMAND|MF_GRAYED },
\r
7807 { IDM_Engine2Options, MF_BYCOMMAND|MF_GRAYED },
\r
7808 { IDM_Annotate, MF_BYCOMMAND|MF_GRAYED },
\r
7809 { IDM_Tourney, MF_BYCOMMAND|MF_GRAYED },
\r
7814 Enables zippyEnables[] = {
\r
7815 { IDM_MoveNow, MF_BYCOMMAND|MF_ENABLED },
\r
7816 { IDM_Hint, MF_BYCOMMAND|MF_ENABLED },
\r
7817 { IDM_Book, MF_BYCOMMAND|MF_ENABLED },
\r
7818 { IDM_Engine1Options, MF_BYCOMMAND|MF_ENABLED },
\r
7823 Enables ncpEnables[] = {
\r
7824 { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },
\r
7825 { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },
\r
7826 { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },
\r
7827 { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },
\r
7828 { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },
\r
7829 { IDM_Match, MF_BYCOMMAND|MF_GRAYED },
\r
7830 { IDM_AnalysisMode, MF_BYCOMMAND|MF_GRAYED },
\r
7831 { IDM_AnalyzeFile, MF_BYCOMMAND|MF_GRAYED },
\r
7832 { IDM_IcsClient, MF_BYCOMMAND|MF_GRAYED },
\r
7833 { ACTION_POS, MF_BYPOSITION|MF_GRAYED },
\r
7834 { IDM_Revert, MF_BYCOMMAND|MF_GRAYED },
\r
7835 { IDM_Annotate, MF_BYCOMMAND|MF_GRAYED },
\r
7836 { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },
\r
7837 { IDM_RetractMove, MF_BYCOMMAND|MF_GRAYED },
\r
7838 { IDM_TimeControl, MF_BYCOMMAND|MF_GRAYED },
\r
7839 { IDM_Hint, MF_BYCOMMAND|MF_GRAYED },
\r
7840 { IDM_Book, MF_BYCOMMAND|MF_GRAYED },
\r
7841 { IDM_MachineBoth, MF_BYCOMMAND|MF_GRAYED },
\r
7842 { IDM_NewChat, MF_BYCOMMAND|MF_GRAYED },
\r
7843 { IDM_Engine1Options, MF_BYCOMMAND|MF_GRAYED },
\r
7844 { IDM_Engine2Options, MF_BYCOMMAND|MF_GRAYED },
\r
7845 { IDM_Sounds, MF_BYCOMMAND|MF_GRAYED },
\r
7849 Enables trainingOnEnables[] = {
\r
7850 { IDM_EditComment, MF_BYCOMMAND|MF_GRAYED },
\r
7851 { IDM_Comment, MF_BYCOMMAND|MF_GRAYED },
\r
7852 { IDM_Pause, MF_BYCOMMAND|MF_GRAYED },
\r
7853 { IDM_Forward, MF_BYCOMMAND|MF_GRAYED },
\r
7854 { IDM_Backward, MF_BYCOMMAND|MF_GRAYED },
\r
7855 { IDM_ToEnd, MF_BYCOMMAND|MF_GRAYED },
\r
7856 { IDM_ToStart, MF_BYCOMMAND|MF_GRAYED },
\r
7857 { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },
\r
7858 { IDM_TruncateGame, MF_BYCOMMAND|MF_GRAYED },
\r
7862 Enables trainingOffEnables[] = {
\r
7863 { IDM_EditComment, MF_BYCOMMAND|MF_ENABLED },
\r
7864 { IDM_Comment, MF_BYCOMMAND|MF_ENABLED },
\r
7865 { IDM_Pause, MF_BYCOMMAND|MF_ENABLED },
\r
7866 { IDM_Forward, MF_BYCOMMAND|MF_ENABLED },
\r
7867 { IDM_Backward, MF_BYCOMMAND|MF_ENABLED },
\r
7868 { IDM_ToEnd, MF_BYCOMMAND|MF_ENABLED },
\r
7869 { IDM_ToStart, MF_BYCOMMAND|MF_ENABLED },
\r
7870 { IDM_MoveNow, MF_BYCOMMAND|MF_ENABLED },
\r
7871 { IDM_TruncateGame, MF_BYCOMMAND|MF_ENABLED },
\r
7875 /* These modify either ncpEnables or gnuEnables */
\r
7876 Enables cmailEnables[] = {
\r
7877 { IDM_MailMove, MF_BYCOMMAND|MF_ENABLED },
\r
7878 { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_ENABLED },
\r
7879 { ACTION_POS, MF_BYPOSITION|MF_ENABLED },
\r
7880 { IDM_CallFlag, MF_BYCOMMAND|MF_GRAYED },
\r
7881 { IDM_Draw, MF_BYCOMMAND|MF_ENABLED },
\r
7882 { IDM_Adjourn, MF_BYCOMMAND|MF_GRAYED },
\r
7883 { IDM_Abort, MF_BYCOMMAND|MF_GRAYED },
\r
7887 Enables machineThinkingEnables[] = {
\r
7888 { IDM_LoadGame, MF_BYCOMMAND|MF_GRAYED },
\r
7889 { IDM_LoadNextGame, MF_BYCOMMAND|MF_GRAYED },
\r
7890 { IDM_LoadPrevGame, MF_BYCOMMAND|MF_GRAYED },
\r
7891 { IDM_ReloadGame, MF_BYCOMMAND|MF_GRAYED },
\r
7892 { IDM_PasteGame, MF_BYCOMMAND|MF_GRAYED },
\r
7893 { IDM_LoadPosition, MF_BYCOMMAND|MF_GRAYED },
\r
7894 { IDM_LoadNextPosition, MF_BYCOMMAND|MF_GRAYED },
\r
7895 { IDM_LoadPrevPosition, MF_BYCOMMAND|MF_GRAYED },
\r
7896 { IDM_ReloadPosition, MF_BYCOMMAND|MF_GRAYED },
\r
7897 { IDM_PastePosition, MF_BYCOMMAND|MF_GRAYED },
\r
7898 { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },
\r
7899 { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },
\r
7900 { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },
\r
7901 // { IDM_Match, MF_BYCOMMAND|MF_GRAYED },
\r
7902 { IDM_TypeInMove, MF_BYCOMMAND|MF_GRAYED },
\r
7903 { IDM_RetractMove, MF_BYCOMMAND|MF_GRAYED },
\r
7907 Enables userThinkingEnables[] = {
\r
7908 { IDM_LoadGame, MF_BYCOMMAND|MF_ENABLED },
\r
7909 { IDM_LoadNextGame, MF_BYCOMMAND|MF_ENABLED },
\r
7910 { IDM_LoadPrevGame, MF_BYCOMMAND|MF_ENABLED },
\r
7911 { IDM_ReloadGame, MF_BYCOMMAND|MF_ENABLED },
\r
7912 { IDM_PasteGame, MF_BYCOMMAND|MF_ENABLED },
\r
7913 { IDM_LoadPosition, MF_BYCOMMAND|MF_ENABLED },
\r
7914 { IDM_LoadNextPosition, MF_BYCOMMAND|MF_ENABLED },
\r
7915 { IDM_LoadPrevPosition, MF_BYCOMMAND|MF_ENABLED },
\r
7916 { IDM_ReloadPosition, MF_BYCOMMAND|MF_ENABLED },
\r
7917 { IDM_PastePosition, MF_BYCOMMAND|MF_ENABLED },
\r
7918 { IDM_MachineWhite, MF_BYCOMMAND|MF_ENABLED },
\r
7919 { IDM_MachineBlack, MF_BYCOMMAND|MF_ENABLED },
\r
7920 { IDM_TwoMachines, MF_BYCOMMAND|MF_ENABLED },
\r
7921 // { IDM_Match, MF_BYCOMMAND|MF_ENABLED },
\r
7922 { IDM_TypeInMove, MF_BYCOMMAND|MF_ENABLED },
\r
7923 { IDM_RetractMove, MF_BYCOMMAND|MF_ENABLED },
\r
7927 /*---------------------------------------------------------------------------*\
\r
7929 * Front-end interface functions exported by XBoard.
\r
7930 * Functions appear in same order as prototypes in frontend.h.
\r
7932 \*---------------------------------------------------------------------------*/
\r
7934 CheckMark(UINT item, int state)
\r
7936 if(item) CheckMenuItem(GetMenu(hwndMain), item, MF_BYCOMMAND|state);
\r
7942 static UINT prevChecked = 0;
\r
7943 static int prevPausing = 0;
\r
7946 if (pausing != prevPausing) {
\r
7947 prevPausing = pausing;
\r
7948 (void) CheckMenuItem(GetMenu(hwndMain), IDM_Pause,
\r
7949 MF_BYCOMMAND|(pausing ? MF_CHECKED : MF_UNCHECKED));
\r
7950 if (hwndPause) SetWindowText(hwndPause, pausing ? "C" : "P");
\r
7953 switch (gameMode) {
\r
7954 case BeginningOfGame:
\r
7955 if (appData.icsActive)
\r
7956 nowChecked = IDM_IcsClient;
\r
7957 else if (appData.noChessProgram)
\r
7958 nowChecked = IDM_EditGame;
\r
7960 nowChecked = IDM_MachineBlack;
\r
7962 case MachinePlaysBlack:
\r
7963 nowChecked = IDM_MachineBlack;
\r
7965 case MachinePlaysWhite:
\r
7966 nowChecked = IDM_MachineWhite;
\r
7968 case TwoMachinesPlay:
\r
7969 nowChecked = IDM_TwoMachines;
\r
7972 nowChecked = IDM_AnalysisMode;
\r
7975 nowChecked = IDM_AnalyzeFile;
\r
7978 nowChecked = IDM_EditGame;
\r
7980 case PlayFromGameFile:
\r
7981 nowChecked = IDM_LoadGame;
\r
7983 case EditPosition:
\r
7984 nowChecked = IDM_EditPosition;
\r
7987 nowChecked = IDM_Training;
\r
7989 case IcsPlayingWhite:
\r
7990 case IcsPlayingBlack:
\r
7991 case IcsObserving:
\r
7993 nowChecked = IDM_IcsClient;
\r
8000 CheckMark(prevChecked, MF_UNCHECKED);
\r
8001 CheckMark(nowChecked, MF_CHECKED);
\r
8002 CheckMark(IDM_Match, matchMode && matchGame < appData.matchGames ? MF_CHECKED : MF_UNCHECKED);
\r
8004 if (nowChecked == IDM_LoadGame || nowChecked == IDM_Training) {
\r
8005 (void) EnableMenuItem(GetMenu(hwndMain), IDM_Training,
\r
8006 MF_BYCOMMAND|MF_ENABLED);
\r
8008 (void) EnableMenuItem(GetMenu(hwndMain),
\r
8009 IDM_Training, MF_BYCOMMAND|MF_GRAYED);
\r
8012 prevChecked = nowChecked;
\r
8014 /* [DM] icsEngineAnalyze - Do a sceure check too */
\r
8015 if (appData.icsActive) {
\r
8016 if (appData.icsEngineAnalyze) {
\r
8017 CheckMark(IDM_AnalysisMode, MF_CHECKED);
\r
8019 CheckMark(IDM_AnalysisMode, MF_UNCHECKED);
\r
8022 DisplayLogos(); // [HGM] logos: mode change could have altered logos
\r
8028 HMENU hmenu = GetMenu(hwndMain);
\r
8029 SetMenuEnables(hmenu, icsEnables);
\r
8030 EnableMenuItem(GetSubMenu(hmenu, OPTIONS_POS), IDM_IcsOptions,
\r
8031 MF_BYCOMMAND|MF_ENABLED);
\r
8033 if (appData.zippyPlay) {
\r
8034 SetMenuEnables(hmenu, zippyEnables);
\r
8035 if (!appData.noChessProgram) /* [DM] icsEngineAnalyze */
\r
8036 (void) EnableMenuItem(GetMenu(hwndMain), IDM_AnalysisMode,
\r
8037 MF_BYCOMMAND|MF_ENABLED);
\r
8045 SetMenuEnables(GetMenu(hwndMain), gnuEnables);
\r
8051 HMENU hmenu = GetMenu(hwndMain);
\r
8052 SetMenuEnables(hmenu, ncpEnables);
\r
8053 DrawMenuBar(hwndMain);
\r
8059 SetMenuEnables(GetMenu(hwndMain), cmailEnables);
\r
8063 SetTrainingModeOn()
\r
8066 SetMenuEnables(GetMenu(hwndMain), trainingOnEnables);
\r
8067 for (i = 0; i < N_BUTTONS; i++) {
\r
8068 if (buttonDesc[i].hwnd != NULL)
\r
8069 EnableWindow(buttonDesc[i].hwnd, FALSE);
\r
8074 VOID SetTrainingModeOff()
\r
8077 SetMenuEnables(GetMenu(hwndMain), trainingOffEnables);
\r
8078 for (i = 0; i < N_BUTTONS; i++) {
\r
8079 if (buttonDesc[i].hwnd != NULL)
\r
8080 EnableWindow(buttonDesc[i].hwnd, TRUE);
\r
8086 SetUserThinkingEnables()
\r
8088 SetMenuEnables(GetMenu(hwndMain), userThinkingEnables);
\r
8092 SetMachineThinkingEnables()
\r
8094 HMENU hMenu = GetMenu(hwndMain);
\r
8095 int flags = MF_BYCOMMAND|MF_ENABLED;
\r
8097 SetMenuEnables(hMenu, machineThinkingEnables);
\r
8099 if (gameMode == MachinePlaysBlack) {
\r
8100 (void)EnableMenuItem(hMenu, IDM_MachineBlack, flags);
\r
8101 } else if (gameMode == MachinePlaysWhite) {
\r
8102 (void)EnableMenuItem(hMenu, IDM_MachineWhite, flags);
\r
8103 } else if (gameMode == TwoMachinesPlay) {
\r
8104 (void)EnableMenuItem(hMenu, matchMode ? IDM_Match : IDM_TwoMachines, flags); // [HGM] match
\r
8110 DisplayTitle(char *str)
\r
8112 char title[MSG_SIZ], *host;
\r
8113 if (str[0] != NULLCHAR) {
\r
8114 safeStrCpy(title, str, sizeof(title)/sizeof(title[0]) );
\r
8115 } else if (appData.icsActive) {
\r
8116 if (appData.icsCommPort[0] != NULLCHAR)
\r
8119 host = appData.icsHost;
\r
8120 snprintf(title, MSG_SIZ, "%s: %s", szTitle, host);
\r
8121 } else if (appData.noChessProgram) {
\r
8122 safeStrCpy(title, szTitle, sizeof(title)/sizeof(title[0]) );
\r
8124 safeStrCpy(title, szTitle, sizeof(title)/sizeof(title[0]) );
\r
8125 strcat(title, ": ");
\r
8126 strcat(title, first.tidy);
\r
8128 SetWindowText(hwndMain, title);
\r
8133 DisplayMessage(char *str1, char *str2)
\r
8137 int remain = MESSAGE_TEXT_MAX - 1;
\r
8140 moveErrorMessageUp = FALSE; /* turned on later by caller if needed */
\r
8141 messageText[0] = NULLCHAR;
\r
8143 len = strlen(str1);
\r
8144 if (len > remain) len = remain;
\r
8145 strncpy(messageText, str1, len);
\r
8146 messageText[len] = NULLCHAR;
\r
8149 if (*str2 && remain >= 2) {
\r
8151 strcat(messageText, " ");
\r
8154 len = strlen(str2);
\r
8155 if (len > remain) len = remain;
\r
8156 strncat(messageText, str2, len);
\r
8158 messageText[MESSAGE_TEXT_MAX - 1] = NULLCHAR;
8159 safeStrCpy(lastMsg, messageText, MSG_SIZ);
8161 if (hwndMain == NULL || IsIconic(hwndMain)) return;
\r
8165 hdc = GetDC(hwndMain);
\r
8166 oldFont = SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);
\r
8167 ExtTextOut(hdc, messageRect.left, messageRect.top, ETO_CLIPPED|ETO_OPAQUE,
\r
8168 &messageRect, messageText, strlen(messageText), NULL);
\r
8169 (void) SelectObject(hdc, oldFont);
\r
8170 (void) ReleaseDC(hwndMain, hdc);
\r
8174 DisplayError(char *str, int error)
\r
8176 char buf[MSG_SIZ*2], buf2[MSG_SIZ];
\r
8180 safeStrCpy(buf, str, sizeof(buf)/sizeof(buf[0]) );
\r
8182 len = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,
\r
8183 NULL, error, LANG_NEUTRAL,
\r
8184 (LPSTR) buf2, MSG_SIZ, NULL);
\r
8186 snprintf(buf, 2*MSG_SIZ, "%s:\n%s", str, buf2);
\r
8188 ErrorMap *em = errmap;
\r
8189 while (em->err != 0 && em->err != error) em++;
\r
8190 if (em->err != 0) {
\r
8191 snprintf(buf, 2*MSG_SIZ, "%s:\n%s", str, em->msg);
\r
8193 snprintf(buf, 2*MSG_SIZ, "%s:\nError code %d", str, error);
\r
8198 ErrorPopUp(_("Error"), buf);
\r
8203 DisplayMoveError(char *str)
\r
8205 fromX = fromY = -1;
\r
8206 ClearHighlights();
\r
8207 DrawPosition(FALSE, NULL);
\r
8208 if (appData.popupMoveErrors) {
\r
8209 ErrorPopUp(_("Error"), str);
\r
8211 DisplayMessage(str, "");
\r
8212 moveErrorMessageUp = TRUE;
\r
8217 DisplayFatalError(char *str, int error, int exitStatus)
\r
8219 char buf[2*MSG_SIZ], buf2[MSG_SIZ];
\r
8221 char *label = exitStatus ? _("Fatal Error") : _("Exiting");
\r
8224 len = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,
\r
8225 NULL, error, LANG_NEUTRAL,
\r
8226 (LPSTR) buf2, MSG_SIZ, NULL);
\r
8228 snprintf(buf, 2*MSG_SIZ, "%s:\n%s", str, buf2);
\r
8230 ErrorMap *em = errmap;
\r
8231 while (em->err != 0 && em->err != error) em++;
\r
8232 if (em->err != 0) {
\r
8233 snprintf(buf, 2*MSG_SIZ, "%s:\n%s", str, em->msg);
\r
8235 snprintf(buf, 2*MSG_SIZ, "%s:\nError code %d", str, error);
\r
8240 if (appData.debugMode) {
\r
8241 fprintf(debugFP, "%s: %s\n", label, str);
\r
8243 if (appData.popupExitMessage) {
\r
8244 (void) MessageBox(hwndMain, str, label, MB_OK|
\r
8245 (exitStatus ? MB_ICONSTOP : MB_ICONINFORMATION));
\r
8247 ExitEvent(exitStatus);
\r
8252 DisplayInformation(char *str)
\r
8254 (void) MessageBox(hwndMain, str, _("Information"), MB_OK|MB_ICONINFORMATION);
\r
8259 DisplayNote(char *str)
\r
8261 ErrorPopUp(_("Note"), str);
\r
8266 char *title, *question, *replyPrefix;
\r
8271 QuestionDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
8273 static QuestionParams *qp;
\r
8274 char reply[MSG_SIZ];
\r
8277 switch (message) {
\r
8278 case WM_INITDIALOG:
\r
8279 qp = (QuestionParams *) lParam;
\r
8280 CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));
\r
8281 Translate(hDlg, DLG_Question);
\r
8282 SetWindowText(hDlg, qp->title);
\r
8283 SetDlgItemText(hDlg, OPT_QuestionText, qp->question);
\r
8284 SetFocus(GetDlgItem(hDlg, OPT_QuestionInput));
\r
8288 switch (LOWORD(wParam)) {
\r
8290 safeStrCpy(reply, qp->replyPrefix, sizeof(reply)/sizeof(reply[0]) );
\r
8291 if (*reply) strcat(reply, " ");
\r
8292 len = strlen(reply);
\r
8293 GetDlgItemText(hDlg, OPT_QuestionInput, reply + len, sizeof(reply) - len);
\r
8294 strcat(reply, "\n");
\r
8295 OutputToProcess(qp->pr, reply, strlen(reply), &err);
\r
8296 EndDialog(hDlg, TRUE);
\r
8297 if (err) DisplayFatalError(_("Error writing to chess program"), err, 1);
\r
8300 EndDialog(hDlg, FALSE);
\r
8311 AskQuestion(char* title, char *question, char *replyPrefix, ProcRef pr)
\r
8313 QuestionParams qp;
\r
8317 qp.question = question;
\r
8318 qp.replyPrefix = replyPrefix;
\r
8320 lpProc = MakeProcInstance((FARPROC)QuestionDialog, hInst);
\r
8321 DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_Question),
\r
8322 hwndMain, (DLGPROC)lpProc, (LPARAM)&qp);
\r
8323 FreeProcInstance(lpProc);
\r
8326 /* [AS] Pick FRC position */
\r
8327 LRESULT CALLBACK NewGameFRC_Proc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
8329 static int * lpIndexFRC;
\r
8335 case WM_INITDIALOG:
\r
8336 lpIndexFRC = (int *) lParam;
\r
8338 CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));
\r
8339 Translate(hDlg, DLG_NewGameFRC);
\r
8341 SendDlgItemMessage( hDlg, IDC_NFG_Edit, EM_SETLIMITTEXT, sizeof(buf)-1, 0 );
\r
8342 SetDlgItemInt( hDlg, IDC_NFG_Edit, *lpIndexFRC, TRUE );
\r
8343 SendDlgItemMessage( hDlg, IDC_NFG_Edit, EM_SETSEL, 0, -1 );
\r
8344 SetFocus(GetDlgItem(hDlg, IDC_NFG_Edit));
\r
8349 switch( LOWORD(wParam) ) {
\r
8351 *lpIndexFRC = GetDlgItemInt(hDlg, IDC_NFG_Edit, &index_is_ok, TRUE );
\r
8352 EndDialog( hDlg, 0 );
\r
8353 shuffleOpenings = TRUE; /* [HGM] shuffle: switch shuffling on for as long as we stay in current variant */
\r
8356 EndDialog( hDlg, 1 );
\r
8358 case IDC_NFG_Edit:
\r
8359 if( HIWORD(wParam) == EN_CHANGE ) {
\r
8360 GetDlgItemInt(hDlg, IDC_NFG_Edit, &index_is_ok, TRUE );
\r
8362 EnableWindow( GetDlgItem(hDlg, IDOK), index_is_ok );
\r
8365 case IDC_NFG_Random:
\r
8366 snprintf( buf, sizeof(buf)/sizeof(buf[0]), "%d", myrandom() ); /* [HGM] shuffle: no longer limit to 960 */
\r
8367 SetDlgItemText(hDlg, IDC_NFG_Edit, buf );
\r
8380 int index = appData.defaultFrcPosition;
\r
8381 FARPROC lpProc = MakeProcInstance( (FARPROC) NewGameFRC_Proc, hInst );
\r
8383 result = DialogBoxParam( hInst, MAKEINTRESOURCE(DLG_NewGameFRC), hwndMain, (DLGPROC)lpProc, (LPARAM)&index );
\r
8385 if( result == 0 ) {
\r
8386 appData.defaultFrcPosition = index;
\r
8392 /* [AS] Game list options. Refactored by HGM */
\r
8394 HWND gameListOptionsDialog;
\r
8396 // low-level front-end: clear text edit / list widget
\r
8400 SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_RESETCONTENT, 0, 0 );
\r
8403 // low-level front-end: clear text edit / list widget
\r
8405 GLT_DeSelectList()
\r
8407 SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_SETCURSEL, 0, 0 );
\r
8410 // low-level front-end: append line to text edit / list widget
\r
8412 GLT_AddToList( char *name )
\r
8415 SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_ADDSTRING, 0, (LPARAM) name );
\r
8419 // low-level front-end: get line from text edit / list widget
\r
8421 GLT_GetFromList( int index, char *name )
\r
8424 if( SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_GETTEXT, index, (LPARAM) name ) != LB_ERR )
\r
8430 void GLT_MoveSelection( HWND hDlg, int delta )
\r
8432 int idx1 = (int) SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETCURSEL, 0, 0 );
\r
8433 int idx2 = idx1 + delta;
\r
8434 int count = (int) SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETCOUNT, 0, 0 );
\r
8436 if( idx1 >=0 && idx1 < count && idx2 >= 0 && idx2 < count ) {
\r
8439 SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETTEXT, idx1, (LPARAM) buf );
\r
8440 SendDlgItemMessage( hDlg, IDC_GameListTags, LB_DELETESTRING, idx1, 0 );
\r
8441 SendDlgItemMessage( hDlg, IDC_GameListTags, LB_INSERTSTRING, idx2, (LPARAM) buf );
\r
8442 SendDlgItemMessage( hDlg, IDC_GameListTags, LB_SETCURSEL, idx2, 0 );
\r
8446 LRESULT CALLBACK GameListOptions_Proc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
8450 case WM_INITDIALOG:
\r
8451 gameListOptionsDialog = hDlg; // [HGM] pass through global to keep out off back-end
\r
8453 CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));
\r
8454 Translate(hDlg, DLG_GameListOptions);
\r
8456 /* Initialize list */
\r
8457 GLT_TagsToList( lpUserGLT );
\r
8459 SetFocus( GetDlgItem(hDlg, IDC_GameListTags) );
\r
8464 switch( LOWORD(wParam) ) {
\r
8467 EndDialog( hDlg, 0 );
\r
8470 EndDialog( hDlg, 1 );
\r
8473 case IDC_GLT_Default:
\r
8474 GLT_TagsToList( GLT_DEFAULT_TAGS );
\r
8477 case IDC_GLT_Restore:
\r
8478 GLT_TagsToList( appData.gameListTags );
\r
8482 GLT_MoveSelection( hDlg, -1 );
\r
8485 case IDC_GLT_Down:
\r
8486 GLT_MoveSelection( hDlg, +1 );
\r
8496 int GameListOptions()
\r
8499 FARPROC lpProc = MakeProcInstance( (FARPROC) GameListOptions_Proc, hInst );
\r
8501 safeStrCpy( lpUserGLT, appData.gameListTags ,LPUSERGLT_SIZE );
\r
8503 result = DialogBoxParam( hInst, MAKEINTRESOURCE(DLG_GameListOptions), hwndMain, (DLGPROC)lpProc, (LPARAM)lpUserGLT );
\r
8505 if( result == 0 ) {
\r
8506 /* [AS] Memory leak here! */
\r
8507 appData.gameListTags = strdup( lpUserGLT );
\r
8514 DisplayIcsInteractionTitle(char *str)
\r
8516 char consoleTitle[MSG_SIZ];
\r
8518 snprintf(consoleTitle, MSG_SIZ, "%s: %s", szConsoleTitle, str);
\r
8519 SetWindowText(hwndConsole, consoleTitle);
\r
8523 DrawPosition(int fullRedraw, Board board)
\r
8525 HDCDrawPosition(NULL, (BOOLEAN) fullRedraw, board);
\r
8528 void NotifyFrontendLogin()
\r
8531 UpdateICSWidth(GetDlgItem(hwndConsole, OPT_ConsoleText));
\r
8537 fromX = fromY = -1;
\r
8538 if (dragInfo.pos.x != -1 || dragInfo.pos.y != -1) {
\r
8539 dragInfo.pos.x = dragInfo.pos.y = -1;
\r
8540 dragInfo.pos.x = dragInfo.pos.y = -1;
\r
8541 dragInfo.lastpos = dragInfo.pos;
\r
8542 dragInfo.start.x = dragInfo.start.y = -1;
\r
8543 dragInfo.from = dragInfo.start;
\r
8545 DrawPosition(TRUE, NULL);
\r
8552 CommentPopUp(char *title, char *str)
\r
8554 HWND hwnd = GetActiveWindow();
\r
8555 EitherCommentPopUp(currentMove, title, str, FALSE); // [HGM] vari: fake move index, rather than 0
\r
8557 SetActiveWindow(hwnd);
\r
8561 CommentPopDown(void)
\r
8563 CheckMenuItem(GetMenu(hwndMain), IDM_Comment, MF_UNCHECKED);
\r
8564 if (commentDialog) {
\r
8565 ShowWindow(commentDialog, SW_HIDE);
\r
8567 commentUp = FALSE;
\r
8571 EditCommentPopUp(int index, char *title, char *str)
\r
8573 EitherCommentPopUp(index, title, str, TRUE);
\r
8580 MyPlaySound(&sounds[(int)SoundMove]);
\r
8583 VOID PlayIcsWinSound()
\r
8585 MyPlaySound(&sounds[(int)SoundIcsWin]);
\r
8588 VOID PlayIcsLossSound()
\r
8590 MyPlaySound(&sounds[(int)SoundIcsLoss]);
\r
8593 VOID PlayIcsDrawSound()
\r
8595 MyPlaySound(&sounds[(int)SoundIcsDraw]);
\r
8598 VOID PlayIcsUnfinishedSound()
\r
8600 MyPlaySound(&sounds[(int)SoundIcsUnfinished]);
\r
8606 MyPlaySound(&sounds[(int)SoundAlarm]);
\r
8612 MyPlaySound(&textAttribs[ColorTell].sound);
\r
8620 consoleEcho = TRUE;
\r
8621 hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
8622 SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&consoleCF);
\r
8623 SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);
\r
8632 consoleEcho = FALSE;
\r
8633 hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
8634 /* This works OK: set text and background both to the same color */
\r
8636 cf.crTextColor = COLOR_ECHOOFF;
\r
8637 SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf);
\r
8638 SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, cf.crTextColor);
\r
8641 /* No Raw()...? */
\r
8643 void Colorize(ColorClass cc, int continuation)
\r
8645 currentColorClass = cc;
\r
8646 consoleCF.dwMask = CFM_COLOR|CFM_BOLD|CFM_ITALIC|CFM_UNDERLINE|CFM_STRIKEOUT;
\r
8647 consoleCF.crTextColor = textAttribs[cc].color;
\r
8648 consoleCF.dwEffects = textAttribs[cc].effects;
\r
8649 if (!continuation) MyPlaySound(&textAttribs[cc].sound);
\r
8655 static char buf[MSG_SIZ];
\r
8656 DWORD bufsiz = MSG_SIZ;
\r
8658 if(appData.userName != NULL && appData.userName[0] != 0) {
\r
8659 return appData.userName; /* [HGM] username: prefer name selected by user over his system login */
\r
8661 if (!GetUserName(buf, &bufsiz)) {
\r
8662 /*DisplayError("Error getting user name", GetLastError());*/
\r
8663 safeStrCpy(buf, _("User"), sizeof(buf)/sizeof(buf[0]) );
\r
8671 static char buf[MSG_SIZ];
\r
8672 DWORD bufsiz = MSG_SIZ;
\r
8674 if (!GetComputerName(buf, &bufsiz)) {
\r
8675 /*DisplayError("Error getting host name", GetLastError());*/
\r
8676 safeStrCpy(buf, _("Unknown"), sizeof(buf)/sizeof(buf[0]) );
\r
8683 ClockTimerRunning()
\r
8685 return clockTimerEvent != 0;
\r
8691 if (clockTimerEvent == 0) return FALSE;
\r
8692 KillTimer(hwndMain, clockTimerEvent);
\r
8693 clockTimerEvent = 0;
\r
8698 StartClockTimer(long millisec)
\r
8700 clockTimerEvent = SetTimer(hwndMain, (UINT) CLOCK_TIMER_ID,
\r
8701 (UINT) millisec, NULL);
\r
8705 DisplayWhiteClock(long timeRemaining, int highlight)
\r
8708 char *flag = whiteFlag && gameMode == TwoMachinesPlay ? "(!)" : "";
\r
8710 if(appData.noGUI) return;
\r
8711 hdc = GetDC(hwndMain);
\r
8712 if (!IsIconic(hwndMain)) {
\r
8713 DisplayAClock(hdc, timeRemaining, highlight,
\r
8714 flipClock ? &blackRect : &whiteRect, _("White"), flag);
\r
8716 if (highlight && iconCurrent == iconBlack) {
\r
8717 iconCurrent = iconWhite;
\r
8718 PostMessage(hwndMain, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);
\r
8719 if (IsIconic(hwndMain)) {
\r
8720 DrawIcon(hdc, 2, 2, iconCurrent);
\r
8723 (void) ReleaseDC(hwndMain, hdc);
\r
8725 PostMessage(hwndConsole, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);
\r
8729 DisplayBlackClock(long timeRemaining, int highlight)
\r
8732 char *flag = blackFlag && gameMode == TwoMachinesPlay ? "(!)" : "";
\r
8734 if(appData.noGUI) return;
\r
8735 hdc = GetDC(hwndMain);
\r
8736 if (!IsIconic(hwndMain)) {
\r
8737 DisplayAClock(hdc, timeRemaining, highlight,
\r
8738 flipClock ? &whiteRect : &blackRect, _("Black"), flag);
\r
8740 if (highlight && iconCurrent == iconWhite) {
\r
8741 iconCurrent = iconBlack;
\r
8742 PostMessage(hwndMain, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);
\r
8743 if (IsIconic(hwndMain)) {
\r
8744 DrawIcon(hdc, 2, 2, iconCurrent);
\r
8747 (void) ReleaseDC(hwndMain, hdc);
\r
8749 PostMessage(hwndConsole, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);
\r
8754 LoadGameTimerRunning()
\r
8756 return loadGameTimerEvent != 0;
\r
8760 StopLoadGameTimer()
\r
8762 if (loadGameTimerEvent == 0) return FALSE;
\r
8763 KillTimer(hwndMain, loadGameTimerEvent);
\r
8764 loadGameTimerEvent = 0;
\r
8769 StartLoadGameTimer(long millisec)
\r
8771 loadGameTimerEvent = SetTimer(hwndMain, (UINT) LOAD_GAME_TIMER_ID,
\r
8772 (UINT) millisec, NULL);
\r
8780 char fileTitle[MSG_SIZ];
\r
8782 defName = DefaultFileName(appData.oldSaveStyle ? "gam" : "pgn");
\r
8783 f = OpenFileDialog(hwndMain, "a", defName,
\r
8784 appData.oldSaveStyle ? "gam" : "pgn",
\r
8786 _("Save Game to File"), NULL, fileTitle, NULL);
\r
8788 SaveGame(f, 0, "");
\r
8795 ScheduleDelayedEvent(DelayedEventCallback cb, long millisec)
\r
8797 if (delayedTimerEvent != 0) {
\r
8798 if (appData.debugMode && cb != delayedTimerCallback) { // [HGM] alive: not too much debug
\r
8799 fprintf(debugFP, "ScheduleDelayedEvent: event already scheduled\n");
\r
8801 KillTimer(hwndMain, delayedTimerEvent);
\r
8802 delayedTimerEvent = 0;
\r
8803 if(delayedTimerCallback != cb) // [HGM] alive: do not "flush" same event, just postpone it
\r
8804 delayedTimerCallback();
\r
8806 delayedTimerCallback = cb;
\r
8807 delayedTimerEvent = SetTimer(hwndMain, (UINT) DELAYED_TIMER_ID,
\r
8808 (UINT) millisec, NULL);
\r
8811 DelayedEventCallback
\r
8814 if (delayedTimerEvent) {
\r
8815 return delayedTimerCallback;
\r
8822 CancelDelayedEvent()
\r
8824 if (delayedTimerEvent) {
\r
8825 KillTimer(hwndMain, delayedTimerEvent);
\r
8826 delayedTimerEvent = 0;
\r
8830 DWORD GetWin32Priority(int nice)
\r
8831 { // [HGM] nice: translate Unix nice() value to indows priority class. (Code stolen from Polyglot 1.4w11)
\r
8833 REALTIME_PRIORITY_CLASS 0x00000100
\r
8834 HIGH_PRIORITY_CLASS 0x00000080
\r
8835 ABOVE_NORMAL_PRIORITY_CLASS 0x00008000
\r
8836 NORMAL_PRIORITY_CLASS 0x00000020
\r
8837 BELOW_NORMAL_PRIORITY_CLASS 0x00004000
\r
8838 IDLE_PRIORITY_CLASS 0x00000040
\r
8840 if (nice < -15) return 0x00000080;
\r
8841 if (nice < 0) return 0x00008000;
\r
8842 if (nice == 0) return 0x00000020;
\r
8843 if (nice < 15) return 0x00004000;
\r
8844 return 0x00000040;
\r
8847 /* Start a child process running the given program.
\r
8848 The process's standard output can be read from "from", and its
\r
8849 standard input can be written to "to".
\r
8850 Exit with fatal error if anything goes wrong.
\r
8851 Returns an opaque pointer that can be used to destroy the process
\r
8855 StartChildProcess(char *cmdLine, char *dir, ProcRef *pr)
\r
8857 #define BUFSIZE 4096
\r
8859 HANDLE hChildStdinRd, hChildStdinWr,
\r
8860 hChildStdoutRd, hChildStdoutWr;
\r
8861 HANDLE hChildStdinWrDup, hChildStdoutRdDup;
\r
8862 SECURITY_ATTRIBUTES saAttr;
\r
8864 PROCESS_INFORMATION piProcInfo;
\r
8865 STARTUPINFO siStartInfo;
\r
8867 char buf[MSG_SIZ];
\r
8870 if (appData.debugMode) {
\r
8871 fprintf(debugFP, "StartChildProcess (dir=\"%s\") %s\n", dir, cmdLine);
\r
8876 /* Set the bInheritHandle flag so pipe handles are inherited. */
\r
8877 saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);
\r
8878 saAttr.bInheritHandle = TRUE;
\r
8879 saAttr.lpSecurityDescriptor = NULL;
\r
8882 * The steps for redirecting child's STDOUT:
\r
8883 * 1. Create anonymous pipe to be STDOUT for child.
\r
8884 * 2. Create a noninheritable duplicate of read handle,
\r
8885 * and close the inheritable read handle.
\r
8888 /* Create a pipe for the child's STDOUT. */
\r
8889 if (! CreatePipe(&hChildStdoutRd, &hChildStdoutWr, &saAttr, 0)) {
\r
8890 return GetLastError();
\r
8893 /* Duplicate the read handle to the pipe, so it is not inherited. */
\r
8894 fSuccess = DuplicateHandle(GetCurrentProcess(), hChildStdoutRd,
\r
8895 GetCurrentProcess(), &hChildStdoutRdDup, 0,
\r
8896 FALSE, /* not inherited */
\r
8897 DUPLICATE_SAME_ACCESS);
\r
8899 return GetLastError();
\r
8901 CloseHandle(hChildStdoutRd);
\r
8904 * The steps for redirecting child's STDIN:
\r
8905 * 1. Create anonymous pipe to be STDIN for child.
\r
8906 * 2. Create a noninheritable duplicate of write handle,
\r
8907 * and close the inheritable write handle.
\r
8910 /* Create a pipe for the child's STDIN. */
\r
8911 if (! CreatePipe(&hChildStdinRd, &hChildStdinWr, &saAttr, 0)) {
\r
8912 return GetLastError();
\r
8915 /* Duplicate the write handle to the pipe, so it is not inherited. */
\r
8916 fSuccess = DuplicateHandle(GetCurrentProcess(), hChildStdinWr,
\r
8917 GetCurrentProcess(), &hChildStdinWrDup, 0,
\r
8918 FALSE, /* not inherited */
\r
8919 DUPLICATE_SAME_ACCESS);
\r
8921 return GetLastError();
\r
8923 CloseHandle(hChildStdinWr);
\r
8925 /* Arrange to (1) look in dir for the child .exe file, and
\r
8926 * (2) have dir be the child's working directory. Interpret
\r
8927 * dir relative to the directory WinBoard loaded from. */
\r
8928 GetCurrentDirectory(MSG_SIZ, buf);
\r
8929 SetCurrentDirectory(installDir);
\r
8930 SetCurrentDirectory(dir);
\r
8932 /* Now create the child process. */
\r
8934 siStartInfo.cb = sizeof(STARTUPINFO);
\r
8935 siStartInfo.lpReserved = NULL;
\r
8936 siStartInfo.lpDesktop = NULL;
\r
8937 siStartInfo.lpTitle = NULL;
\r
8938 siStartInfo.dwFlags = STARTF_USESTDHANDLES;
\r
8939 siStartInfo.cbReserved2 = 0;
\r
8940 siStartInfo.lpReserved2 = NULL;
\r
8941 siStartInfo.hStdInput = hChildStdinRd;
\r
8942 siStartInfo.hStdOutput = hChildStdoutWr;
\r
8943 siStartInfo.hStdError = hChildStdoutWr;
\r
8945 fSuccess = CreateProcess(NULL,
\r
8946 cmdLine, /* command line */
\r
8947 NULL, /* process security attributes */
\r
8948 NULL, /* primary thread security attrs */
\r
8949 TRUE, /* handles are inherited */
\r
8950 DETACHED_PROCESS|CREATE_NEW_PROCESS_GROUP,
\r
8951 NULL, /* use parent's environment */
\r
8953 &siStartInfo, /* STARTUPINFO pointer */
\r
8954 &piProcInfo); /* receives PROCESS_INFORMATION */
\r
8956 err = GetLastError();
\r
8957 SetCurrentDirectory(buf); /* return to prev directory */
\r
8962 if (appData.niceEngines){ // [HGM] nice: adjust engine proc priority
\r
8963 if(appData.debugMode) fprintf(debugFP, "nice engine proc to %d\n", appData.niceEngines);
\r
8964 SetPriorityClass(piProcInfo.hProcess, GetWin32Priority(appData.niceEngines));
\r
8967 /* Close the handles we don't need in the parent */
\r
8968 CloseHandle(piProcInfo.hThread);
\r
8969 CloseHandle(hChildStdinRd);
\r
8970 CloseHandle(hChildStdoutWr);
\r
8972 /* Prepare return value */
\r
8973 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
\r
8974 cp->kind = CPReal;
\r
8975 cp->hProcess = piProcInfo.hProcess;
\r
8976 cp->pid = piProcInfo.dwProcessId;
\r
8977 cp->hFrom = hChildStdoutRdDup;
\r
8978 cp->hTo = hChildStdinWrDup;
\r
8980 *pr = (void *) cp;
\r
8982 /* Klaus Friedel says that this Sleep solves a problem under Windows
\r
8983 2000 where engines sometimes don't see the initial command(s)
\r
8984 from WinBoard and hang. I don't understand how that can happen,
\r
8985 but the Sleep is harmless, so I've put it in. Others have also
\r
8986 reported what may be the same problem, so hopefully this will fix
\r
8987 it for them too. */
\r
8995 DestroyChildProcess(ProcRef pr, int/*boolean*/ signal)
\r
8997 ChildProc *cp; int result;
\r
8999 cp = (ChildProc *) pr;
\r
9000 if (cp == NULL) return;
\r
9002 switch (cp->kind) {
\r
9004 /* TerminateProcess is considered harmful, so... */
\r
9005 CloseHandle(cp->hTo); /* Closing this will give the child an EOF and hopefully kill it */
\r
9006 if (cp->hFrom) CloseHandle(cp->hFrom); /* if NULL, InputThread will close it */
\r
9007 /* The following doesn't work because the chess program
\r
9008 doesn't "have the same console" as WinBoard. Maybe
\r
9009 we could arrange for this even though neither WinBoard
\r
9010 nor the chess program uses a console for stdio? */
\r
9011 /*!!if (signal) GenerateConsoleCtrlEvent(CTRL_BREAK_EVENT, cp->pid);*/
\r
9013 /* [AS] Special termination modes for misbehaving programs... */
\r
9014 if( signal == 9 ) {
\r
9015 result = TerminateProcess( cp->hProcess, 0 );
\r
9017 if ( appData.debugMode) {
\r
9018 fprintf( debugFP, "Terminating process %lu, result=%d\n", cp->pid, result );
\r
9021 else if( signal == 10 ) {
\r
9022 DWORD dw = WaitForSingleObject( cp->hProcess, 3*1000 ); // Wait 3 seconds at most
\r
9024 if( dw != WAIT_OBJECT_0 ) {
\r
9025 result = TerminateProcess( cp->hProcess, 0 );
\r
9027 if ( appData.debugMode) {
\r
9028 fprintf( debugFP, "Process %lu still alive after timeout, killing... result=%d\n", cp->pid, result );
\r
9034 CloseHandle(cp->hProcess);
\r
9038 if (cp->hFrom) CloseHandle(cp->hFrom);
\r
9042 closesocket(cp->sock);
\r
9047 if (signal) send(cp->sock2, "\017", 1, 0); /* 017 = 15 = SIGTERM */
\r
9048 closesocket(cp->sock);
\r
9049 closesocket(cp->sock2);
\r
9057 InterruptChildProcess(ProcRef pr)
\r
9061 cp = (ChildProc *) pr;
\r
9062 if (cp == NULL) return;
\r
9063 switch (cp->kind) {
\r
9065 /* The following doesn't work because the chess program
\r
9066 doesn't "have the same console" as WinBoard. Maybe
\r
9067 we could arrange for this even though neither WinBoard
\r
9068 nor the chess program uses a console for stdio */
\r
9069 /*!!GenerateConsoleCtrlEvent(CTRL_C_EVENT, cp->pid);*/
\r
9074 /* Can't interrupt */
\r
9078 send(cp->sock2, "\002", 1, 0); /* 2 = SIGINT */
\r
9085 OpenTelnet(char *host, char *port, ProcRef *pr)
\r
9087 char cmdLine[MSG_SIZ];
\r
9089 if (port[0] == NULLCHAR) {
\r
9090 snprintf(cmdLine, MSG_SIZ, "%s %s", appData.telnetProgram, host);
\r
9092 snprintf(cmdLine, MSG_SIZ, "%s %s %s", appData.telnetProgram, host, port);
\r
9094 return StartChildProcess(cmdLine, "", pr);
\r
9098 /* Code to open TCP sockets */
\r
9101 OpenTCP(char *host, char *port, ProcRef *pr)
\r
9106 struct sockaddr_in sa, mysa;
\r
9107 struct hostent FAR *hp;
\r
9108 unsigned short uport;
\r
9109 WORD wVersionRequested;
\r
9112 /* Initialize socket DLL */
\r
9113 wVersionRequested = MAKEWORD(1, 1);
\r
9114 err = WSAStartup(wVersionRequested, &wsaData);
\r
9115 if (err != 0) return err;
\r
9118 if ((s = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {
\r
9119 err = WSAGetLastError();
\r
9124 /* Bind local address using (mostly) don't-care values.
\r
9126 memset((char *) &mysa, 0, sizeof(struct sockaddr_in));
\r
9127 mysa.sin_family = AF_INET;
\r
9128 mysa.sin_addr.s_addr = INADDR_ANY;
\r
9129 uport = (unsigned short) 0;
\r
9130 mysa.sin_port = htons(uport);
\r
9131 if (bind(s, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))
\r
9132 == SOCKET_ERROR) {
\r
9133 err = WSAGetLastError();
\r
9138 /* Resolve remote host name */
\r
9139 memset((char *) &sa, 0, sizeof(struct sockaddr_in));
\r
9140 if (!(hp = gethostbyname(host))) {
\r
9141 unsigned int b0, b1, b2, b3;
\r
9143 err = WSAGetLastError();
\r
9145 if (sscanf(host, "%u.%u.%u.%u", &b0, &b1, &b2, &b3) == 4) {
\r
9146 hp = (struct hostent *) calloc(1, sizeof(struct hostent));
\r
9147 hp->h_addrtype = AF_INET;
\r
9149 hp->h_addr_list = (char **) calloc(2, sizeof(char *));
\r
9150 hp->h_addr_list[0] = (char *) malloc(4);
\r
9151 hp->h_addr_list[0][0] = (char) b0;
\r
9152 hp->h_addr_list[0][1] = (char) b1;
\r
9153 hp->h_addr_list[0][2] = (char) b2;
\r
9154 hp->h_addr_list[0][3] = (char) b3;
\r
9160 sa.sin_family = hp->h_addrtype;
\r
9161 uport = (unsigned short) atoi(port);
\r
9162 sa.sin_port = htons(uport);
\r
9163 memcpy((char *) &sa.sin_addr, hp->h_addr, hp->h_length);
\r
9165 /* Make connection */
\r
9166 if (connect(s, (struct sockaddr *) &sa,
\r
9167 sizeof(struct sockaddr_in)) == SOCKET_ERROR) {
\r
9168 err = WSAGetLastError();
\r
9173 /* Prepare return value */
\r
9174 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
\r
9175 cp->kind = CPSock;
\r
9177 *pr = (ProcRef *) cp;
\r
9183 OpenCommPort(char *name, ProcRef *pr)
\r
9188 char fullname[MSG_SIZ];
\r
9190 if (*name != '\\')
\r
9191 snprintf(fullname, MSG_SIZ, "\\\\.\\%s", name);
\r
9193 safeStrCpy(fullname, name, sizeof(fullname)/sizeof(fullname[0]) );
\r
9195 h = CreateFile(name, GENERIC_READ | GENERIC_WRITE,
\r
9196 0, NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL);
\r
9197 if (h == (HANDLE) -1) {
\r
9198 return GetLastError();
\r
9202 if (!SetCommState(h, (LPDCB) &dcb)) return GetLastError();
\r
9204 /* Accumulate characters until a 100ms pause, then parse */
\r
9205 ct.ReadIntervalTimeout = 100;
\r
9206 ct.ReadTotalTimeoutMultiplier = 0;
\r
9207 ct.ReadTotalTimeoutConstant = 0;
\r
9208 ct.WriteTotalTimeoutMultiplier = 0;
\r
9209 ct.WriteTotalTimeoutConstant = 0;
\r
9210 if (!SetCommTimeouts(h, (LPCOMMTIMEOUTS) &ct)) return GetLastError();
\r
9212 /* Prepare return value */
\r
9213 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
\r
9214 cp->kind = CPComm;
\r
9217 *pr = (ProcRef *) cp;
\r
9223 OpenLoopback(ProcRef *pr)
\r
9225 DisplayFatalError(_("Not implemented"), 0, 1);
\r
9231 OpenRcmd(char* host, char* user, char* cmd, ProcRef* pr)
\r
9236 struct sockaddr_in sa, mysa;
\r
9237 struct hostent FAR *hp;
\r
9238 unsigned short uport;
\r
9239 WORD wVersionRequested;
\r
9242 char stderrPortStr[MSG_SIZ];
\r
9244 /* Initialize socket DLL */
\r
9245 wVersionRequested = MAKEWORD(1, 1);
\r
9246 err = WSAStartup(wVersionRequested, &wsaData);
\r
9247 if (err != 0) return err;
\r
9249 /* Resolve remote host name */
\r
9250 memset((char *) &sa, 0, sizeof(struct sockaddr_in));
\r
9251 if (!(hp = gethostbyname(host))) {
\r
9252 unsigned int b0, b1, b2, b3;
\r
9254 err = WSAGetLastError();
\r
9256 if (sscanf(host, "%u.%u.%u.%u", &b0, &b1, &b2, &b3) == 4) {
\r
9257 hp = (struct hostent *) calloc(1, sizeof(struct hostent));
\r
9258 hp->h_addrtype = AF_INET;
\r
9260 hp->h_addr_list = (char **) calloc(2, sizeof(char *));
\r
9261 hp->h_addr_list[0] = (char *) malloc(4);
\r
9262 hp->h_addr_list[0][0] = (char) b0;
\r
9263 hp->h_addr_list[0][1] = (char) b1;
\r
9264 hp->h_addr_list[0][2] = (char) b2;
\r
9265 hp->h_addr_list[0][3] = (char) b3;
\r
9271 sa.sin_family = hp->h_addrtype;
\r
9272 uport = (unsigned short) 514;
\r
9273 sa.sin_port = htons(uport);
\r
9274 memcpy((char *) &sa.sin_addr, hp->h_addr, hp->h_length);
\r
9276 /* Bind local socket to unused "privileged" port address
\r
9278 s = INVALID_SOCKET;
\r
9279 memset((char *) &mysa, 0, sizeof(struct sockaddr_in));
\r
9280 mysa.sin_family = AF_INET;
\r
9281 mysa.sin_addr.s_addr = INADDR_ANY;
\r
9282 for (fromPort = 1023;; fromPort--) {
\r
9283 if (fromPort < 0) {
\r
9285 return WSAEADDRINUSE;
\r
9287 if (s == INVALID_SOCKET) {
\r
9288 if ((s = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {
\r
9289 err = WSAGetLastError();
\r
9294 uport = (unsigned short) fromPort;
\r
9295 mysa.sin_port = htons(uport);
\r
9296 if (bind(s, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))
\r
9297 == SOCKET_ERROR) {
\r
9298 err = WSAGetLastError();
\r
9299 if (err == WSAEADDRINUSE) continue;
\r
9303 if (connect(s, (struct sockaddr *) &sa,
\r
9304 sizeof(struct sockaddr_in)) == SOCKET_ERROR) {
\r
9305 err = WSAGetLastError();
\r
9306 if (err == WSAEADDRINUSE) {
\r
9317 /* Bind stderr local socket to unused "privileged" port address
\r
9319 s2 = INVALID_SOCKET;
\r
9320 memset((char *) &mysa, 0, sizeof(struct sockaddr_in));
\r
9321 mysa.sin_family = AF_INET;
\r
9322 mysa.sin_addr.s_addr = INADDR_ANY;
\r
9323 for (fromPort = 1023;; fromPort--) {
\r
9324 if (fromPort == prevStderrPort) continue; // don't reuse port
\r
9325 if (fromPort < 0) {
\r
9326 (void) closesocket(s);
\r
9328 return WSAEADDRINUSE;
\r
9330 if (s2 == INVALID_SOCKET) {
\r
9331 if ((s2 = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {
\r
9332 err = WSAGetLastError();
\r
9338 uport = (unsigned short) fromPort;
\r
9339 mysa.sin_port = htons(uport);
\r
9340 if (bind(s2, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))
\r
9341 == SOCKET_ERROR) {
\r
9342 err = WSAGetLastError();
\r
9343 if (err == WSAEADDRINUSE) continue;
\r
9344 (void) closesocket(s);
\r
9348 if (listen(s2, 1) == SOCKET_ERROR) {
\r
9349 err = WSAGetLastError();
\r
9350 if (err == WSAEADDRINUSE) {
\r
9352 s2 = INVALID_SOCKET;
\r
9355 (void) closesocket(s);
\r
9356 (void) closesocket(s2);
\r
9362 prevStderrPort = fromPort; // remember port used
\r
9363 snprintf(stderrPortStr, MSG_SIZ, "%d", fromPort);
\r
9365 if (send(s, stderrPortStr, strlen(stderrPortStr) + 1, 0) == SOCKET_ERROR) {
\r
9366 err = WSAGetLastError();
\r
9367 (void) closesocket(s);
\r
9368 (void) closesocket(s2);
\r
9373 if (send(s, UserName(), strlen(UserName()) + 1, 0) == SOCKET_ERROR) {
\r
9374 err = WSAGetLastError();
\r
9375 (void) closesocket(s);
\r
9376 (void) closesocket(s2);
\r
9380 if (*user == NULLCHAR) user = UserName();
\r
9381 if (send(s, user, strlen(user) + 1, 0) == SOCKET_ERROR) {
\r
9382 err = WSAGetLastError();
\r
9383 (void) closesocket(s);
\r
9384 (void) closesocket(s2);
\r
9388 if (send(s, cmd, strlen(cmd) + 1, 0) == SOCKET_ERROR) {
\r
9389 err = WSAGetLastError();
\r
9390 (void) closesocket(s);
\r
9391 (void) closesocket(s2);
\r
9396 if ((s3 = accept(s2, NULL, NULL)) == INVALID_SOCKET) {
\r
9397 err = WSAGetLastError();
\r
9398 (void) closesocket(s);
\r
9399 (void) closesocket(s2);
\r
9403 (void) closesocket(s2); /* Stop listening */
\r
9405 /* Prepare return value */
\r
9406 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
\r
9407 cp->kind = CPRcmd;
\r
9410 *pr = (ProcRef *) cp;
\r
9417 AddInputSource(ProcRef pr, int lineByLine,
\r
9418 InputCallback func, VOIDSTAR closure)
\r
9420 InputSource *is, *is2 = NULL;
\r
9421 ChildProc *cp = (ChildProc *) pr;
\r
9423 is = (InputSource *) calloc(1, sizeof(InputSource));
\r
9424 is->lineByLine = lineByLine;
\r
9426 is->closure = closure;
\r
9427 is->second = NULL;
\r
9428 is->next = is->buf;
\r
9429 if (pr == NoProc) {
\r
9430 is->kind = CPReal;
\r
9431 consoleInputSource = is;
\r
9433 is->kind = cp->kind;
\r
9435 [AS] Try to avoid a race condition if the thread is given control too early:
\r
9436 we create all threads suspended so that the is->hThread variable can be
\r
9437 safely assigned, then let the threads start with ResumeThread.
\r
9439 switch (cp->kind) {
\r
9441 is->hFile = cp->hFrom;
\r
9442 cp->hFrom = NULL; /* now owned by InputThread */
\r
9444 CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) NonOvlInputThread,
\r
9445 (LPVOID) is, CREATE_SUSPENDED, &is->id);
\r
9449 is->hFile = cp->hFrom;
\r
9450 cp->hFrom = NULL; /* now owned by InputThread */
\r
9452 CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) InputThread,
\r
9453 (LPVOID) is, CREATE_SUSPENDED, &is->id);
\r
9457 is->sock = cp->sock;
\r
9459 CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,
\r
9460 (LPVOID) is, CREATE_SUSPENDED, &is->id);
\r
9464 is2 = (InputSource *) calloc(1, sizeof(InputSource));
\r
9466 is->sock = cp->sock;
\r
9468 is2->sock = cp->sock2;
\r
9469 is2->second = is2;
\r
9471 CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,
\r
9472 (LPVOID) is, CREATE_SUSPENDED, &is->id);
\r
9474 CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,
\r
9475 (LPVOID) is2, CREATE_SUSPENDED, &is2->id);
\r
9479 if( is->hThread != NULL ) {
\r
9480 ResumeThread( is->hThread );
\r
9483 if( is2 != NULL && is2->hThread != NULL ) {
\r
9484 ResumeThread( is2->hThread );
\r
9488 return (InputSourceRef) is;
\r
9492 RemoveInputSource(InputSourceRef isr)
\r
9496 is = (InputSource *) isr;
\r
9497 is->hThread = NULL; /* tell thread to stop */
\r
9498 CloseHandle(is->hThread);
\r
9499 if (is->second != NULL) {
\r
9500 is->second->hThread = NULL;
\r
9501 CloseHandle(is->second->hThread);
\r
9505 int no_wrap(char *message, int count)
\r
9507 ConsoleOutput(message, count, FALSE);
\r
9512 OutputToProcess(ProcRef pr, char *message, int count, int *outError)
\r
9515 int outCount = SOCKET_ERROR;
\r
9516 ChildProc *cp = (ChildProc *) pr;
\r
9517 static OVERLAPPED ovl;
\r
9518 static int line = 0;
\r
9522 if (appData.noJoin || !appData.useInternalWrap)
\r
9523 return no_wrap(message, count);
\r
9526 int width = get_term_width();
\r
9527 int len = wrap(NULL, message, count, width, &line);
\r
9528 char *msg = malloc(len);
\r
9532 return no_wrap(message, count);
\r
9535 dbgchk = wrap(msg, message, count, width, &line);
\r
9536 if (dbgchk != len && appData.debugMode)
\r
9537 fprintf(debugFP, "wrap(): dbgchk(%d) != len(%d)\n", dbgchk, len);
\r
9538 ConsoleOutput(msg, len, FALSE);
\r
9545 if (ovl.hEvent == NULL) {
\r
9546 ovl.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
\r
9548 ovl.Internal = ovl.InternalHigh = ovl.Offset = ovl.OffsetHigh = 0;
\r
9550 switch (cp->kind) {
\r
9553 outCount = send(cp->sock, message, count, 0);
\r
9554 if (outCount == SOCKET_ERROR) {
\r
9555 *outError = WSAGetLastError();
\r
9557 *outError = NO_ERROR;
\r
9562 if (WriteFile(((ChildProc *)pr)->hTo, message, count,
\r
9563 &dOutCount, NULL)) {
\r
9564 *outError = NO_ERROR;
\r
9565 outCount = (int) dOutCount;
\r
9567 *outError = GetLastError();
\r
9572 *outError = DoWriteFile(((ChildProc *)pr)->hTo, message, count,
\r
9573 &dOutCount, &ovl);
\r
9574 if (*outError == NO_ERROR) {
\r
9575 outCount = (int) dOutCount;
\r
9583 OutputToProcessDelayed(ProcRef pr, char *message, int count, int *outError,
\r
9586 /* Ignore delay, not implemented for WinBoard */
\r
9587 return OutputToProcess(pr, message, count, outError);
\r
9592 CmailSigHandlerCallBack(InputSourceRef isr, VOIDSTAR closure,
\r
9593 char *buf, int count, int error)
\r
9595 DisplayFatalError(_("Not implemented"), 0, 1);
\r
9598 /* see wgamelist.c for Game List functions */
\r
9599 /* see wedittags.c for Edit Tags functions */
\r
9606 char buf[MSG_SIZ];
\r
9609 if (SearchPath(installDir, appData.icsLogon, NULL, MSG_SIZ, buf, &dummy)) {
\r
9610 f = fopen(buf, "r");
\r
9612 ProcessICSInitScript(f);
\r
9620 StartAnalysisClock()
\r
9622 if (analysisTimerEvent) return;
\r
9623 analysisTimerEvent = SetTimer(hwndMain, (UINT) ANALYSIS_TIMER_ID,
\r
9624 (UINT) 2000, NULL);
\r
9628 SetHighlights(int fromX, int fromY, int toX, int toY)
\r
9630 highlightInfo.sq[0].x = fromX;
\r
9631 highlightInfo.sq[0].y = fromY;
\r
9632 highlightInfo.sq[1].x = toX;
\r
9633 highlightInfo.sq[1].y = toY;
\r
9639 highlightInfo.sq[0].x = highlightInfo.sq[0].y =
\r
9640 highlightInfo.sq[1].x = highlightInfo.sq[1].y = -1;
\r
9644 SetPremoveHighlights(int fromX, int fromY, int toX, int toY)
\r
9646 premoveHighlightInfo.sq[0].x = fromX;
\r
9647 premoveHighlightInfo.sq[0].y = fromY;
\r
9648 premoveHighlightInfo.sq[1].x = toX;
\r
9649 premoveHighlightInfo.sq[1].y = toY;
\r
9653 ClearPremoveHighlights()
\r
9655 premoveHighlightInfo.sq[0].x = premoveHighlightInfo.sq[0].y =
\r
9656 premoveHighlightInfo.sq[1].x = premoveHighlightInfo.sq[1].y = -1;
\r
9660 ShutDownFrontEnd()
\r
9662 if (saveSettingsOnExit) SaveSettings(settingsFileName);
\r
9663 DeleteClipboardTempFiles();
\r
9669 if (IsIconic(hwndMain))
\r
9670 ShowWindow(hwndMain, SW_RESTORE);
\r
9672 SetActiveWindow(hwndMain);
\r
9676 * Prototypes for animation support routines
\r
9678 static void ScreenSquare(int column, int row, POINT * pt);
\r
9679 static void Tween( POINT * start, POINT * mid, POINT * finish, int factor,
\r
9680 POINT frames[], int * nFrames);
\r
9686 AnimateAtomicCapture(Board board, int fromX, int fromY, int toX, int toY)
\r
9687 { // [HGM] atomic: animate blast wave
\r
9690 explodeInfo.fromX = fromX;
\r
9691 explodeInfo.fromY = fromY;
\r
9692 explodeInfo.toX = toX;
\r
9693 explodeInfo.toY = toY;
\r
9694 for(i=1; i<4*kFactor; i++) {
\r
9695 explodeInfo.radius = (i*180)/(4*kFactor-1);
\r
9696 DrawPosition(FALSE, board);
\r
9697 Sleep(appData.animSpeed);
\r
9699 explodeInfo.radius = 0;
\r
9700 DrawPosition(TRUE, board);
\r
9704 AnimateMove(board, fromX, fromY, toX, toY)
\r
9711 ChessSquare piece;
\r
9712 POINT start, finish, mid;
\r
9713 POINT frames[kFactor * 2 + 1];
\r
9716 if (!appData.animate) return;
\r
9717 if (doingSizing) return;
\r
9718 if (fromY < 0 || fromX < 0) return;
\r
9719 piece = board[fromY][fromX];
\r
9720 if (piece >= EmptySquare) return;
\r
9722 ScreenSquare(fromX, fromY, &start);
\r
9723 ScreenSquare(toX, toY, &finish);
\r
9725 /* All moves except knight jumps move in straight line */
\r
9726 if (!(abs(fromX-toX) == 1 && abs(fromY-toY) == 2 || abs(fromX-toX) == 2 && abs(fromY-toY) == 1)) {
\r
9727 mid.x = start.x + (finish.x - start.x) / 2;
\r
9728 mid.y = start.y + (finish.y - start.y) / 2;
\r
9730 /* Knight: make straight movement then diagonal */
\r
9731 if (abs(toY - fromY) < abs(toX - fromX)) {
\r
9732 mid.x = start.x + (finish.x - start.x) / 2;
\r
9736 mid.y = start.y + (finish.y - start.y) / 2;
\r
9740 /* Don't use as many frames for very short moves */
\r
9741 if (abs(toY - fromY) + abs(toX - fromX) <= 2)
\r
9742 Tween(&start, &mid, &finish, kFactor - 1, frames, &nFrames);
\r
9744 Tween(&start, &mid, &finish, kFactor, frames, &nFrames);
\r
9746 animInfo.from.x = fromX;
\r
9747 animInfo.from.y = fromY;
\r
9748 animInfo.to.x = toX;
\r
9749 animInfo.to.y = toY;
\r
9750 animInfo.lastpos = start;
\r
9751 animInfo.piece = piece;
\r
9752 for (n = 0; n < nFrames; n++) {
\r
9753 animInfo.pos = frames[n];
\r
9754 DrawPosition(FALSE, NULL);
\r
9755 animInfo.lastpos = animInfo.pos;
\r
9756 Sleep(appData.animSpeed);
\r
9758 animInfo.pos = finish;
\r
9759 DrawPosition(FALSE, NULL);
\r
9760 animInfo.piece = EmptySquare;
\r
9761 Explode(board, fromX, fromY, toX, toY);
\r
9764 /* Convert board position to corner of screen rect and color */
\r
9767 ScreenSquare(column, row, pt)
\r
9768 int column; int row; POINT * pt;
\r
9771 pt->x = lineGap + ((BOARD_WIDTH-1)-column) * (squareSize + lineGap);
\r
9772 pt->y = lineGap + row * (squareSize + lineGap);
\r
9774 pt->x = lineGap + column * (squareSize + lineGap);
\r
9775 pt->y = lineGap + ((BOARD_HEIGHT-1)-row) * (squareSize + lineGap);
\r
9779 /* Generate a series of frame coords from start->mid->finish.
\r
9780 The movement rate doubles until the half way point is
\r
9781 reached, then halves back down to the final destination,
\r
9782 which gives a nice slow in/out effect. The algorithmn
\r
9783 may seem to generate too many intermediates for short
\r
9784 moves, but remember that the purpose is to attract the
\r
9785 viewers attention to the piece about to be moved and
\r
9786 then to where it ends up. Too few frames would be less
\r
9790 Tween(start, mid, finish, factor, frames, nFrames)
\r
9791 POINT * start; POINT * mid;
\r
9792 POINT * finish; int factor;
\r
9793 POINT frames[]; int * nFrames;
\r
9795 int n, fraction = 1, count = 0;
\r
9797 /* Slow in, stepping 1/16th, then 1/8th, ... */
\r
9798 for (n = 0; n < factor; n++)
\r
9800 for (n = 0; n < factor; n++) {
\r
9801 frames[count].x = start->x + (mid->x - start->x) / fraction;
\r
9802 frames[count].y = start->y + (mid->y - start->y) / fraction;
\r
9804 fraction = fraction / 2;
\r
9808 frames[count] = *mid;
\r
9811 /* Slow out, stepping 1/2, then 1/4, ... */
\r
9813 for (n = 0; n < factor; n++) {
\r
9814 frames[count].x = finish->x - (finish->x - mid->x) / fraction;
\r
9815 frames[count].y = finish->y - (finish->y - mid->y) / fraction;
\r
9817 fraction = fraction * 2;
\r
9823 HistorySet( char movelist[][2*MOVE_LEN], int first, int last, int current )
\r
9825 MoveHistorySet( movelist, first, last, current, pvInfoList );
\r
9827 EvalGraphSet( first, last, current, pvInfoList );
\r
9829 MakeEngineOutputTitle();
\r
9833 SettingsPopUp(ChessProgramState *cps)
\r
9834 { // [HGM] wrapper needed because handles must not be passed through back-end
\r
9835 EngineOptionsPopup(savedHwnd, cps);
\r
9838 int flock(int fid, int code)
\r
9840 HANDLE hFile = (HANDLE) _get_osfhandle(fid);
\r
9844 ov.OffsetHigh = 0;
\r
9846 case 1: LockFileEx(hFile, LOCKFILE_EXCLUSIVE_LOCK, 0, 1024, 0, &ov); break; // LOCK_SH
\r
9847 case 2: LockFileEx(hFile, LOCKFILE_EXCLUSIVE_LOCK, 0, 1024, 0, &ov); break; // LOCK_EX
\r
9848 case 3: UnlockFileEx(hFile, 0, 1024, 0, &ov); break; // LOCK_UN
\r
9849 default: return -1;
\r