2 * WinBoard.c -- Windows NT front end to XBoard
\r
4 * Copyright 1991 by Digital Equipment Corporation, Maynard,
\r
7 * Enhancements Copyright 1992-2001, 2002, 2003, 2004, 2005, 2006,
\r
8 * 2007, 2008, 2009, 2010, 2011 Free Software Foundation, Inc.
\r
10 * Enhancements Copyright 2005 Alessandro Scotti
\r
12 * XBoard borrows its colors and the bitmaps.xchess bitmap set from XChess,
\r
13 * which was written and is copyrighted by Wayne Christopher.
\r
15 * The following terms apply to Digital Equipment Corporation's copyright
\r
16 * interest in XBoard:
\r
17 * ------------------------------------------------------------------------
\r
18 * All Rights Reserved
\r
20 * Permission to use, copy, modify, and distribute this software and its
\r
21 * documentation for any purpose and without fee is hereby granted,
\r
22 * provided that the above copyright notice appear in all copies and that
\r
23 * both that copyright notice and this permission notice appear in
\r
24 * supporting documentation, and that the name of Digital not be
\r
25 * used in advertising or publicity pertaining to distribution of the
\r
26 * software without specific, written prior permission.
\r
28 * DIGITAL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
\r
29 * ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL
\r
30 * DIGITAL BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
\r
31 * ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
\r
32 * WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
\r
33 * ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
\r
35 * ------------------------------------------------------------------------
\r
37 * The following terms apply to the enhanced version of XBoard
\r
38 * distributed by the Free Software Foundation:
\r
39 * ------------------------------------------------------------------------
\r
41 * GNU XBoard is free software: you can redistribute it and/or modify
\r
42 * it under the terms of the GNU General Public License as published by
\r
43 * the Free Software Foundation, either version 3 of the License, or (at
\r
44 * your option) any later version.
\r
46 * GNU XBoard is distributed in the hope that it will be useful, but
\r
47 * WITHOUT ANY WARRANTY; without even the implied warranty of
\r
48 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
\r
49 * General Public License for more details.
\r
51 * You should have received a copy of the GNU General Public License
\r
52 * along with this program. If not, see http://www.gnu.org/licenses/. *
\r
54 *------------------------------------------------------------------------
\r
55 ** See the file ChangeLog for a revision history. */
\r
59 #include <windows.h>
\r
60 #include <winuser.h>
\r
61 #include <winsock.h>
\r
62 #include <commctrl.h>
\r
68 #include <sys/stat.h>
\r
71 #include <commdlg.h>
\r
73 #include <richedit.h>
\r
74 #include <mmsystem.h>
\r
84 #include "frontend.h"
\r
85 #include "backend.h"
\r
86 #include "winboard.h"
\r
88 #include "wclipbrd.h"
\r
89 #include "woptions.h"
\r
90 #include "wsockerr.h"
\r
91 #include "defaults.h"
\r
95 //void InitEngineUCI( const char * iniDir, ChessProgramState * cps );
\r
98 void mysrandom(unsigned int seed);
\r
100 extern int whiteFlag, blackFlag;
\r
101 Boolean flipClock = FALSE;
\r
102 extern HANDLE chatHandle[];
\r
103 extern int ics_type;
\r
105 void DisplayHoldingsCount(HDC hdc, int x, int y, int align, int copyNumber);
\r
106 VOID NewVariantPopup(HWND hwnd);
\r
107 int FinishMove P((ChessMove moveType, int fromX, int fromY, int toX, int toY,
\r
108 /*char*/int promoChar));
\r
109 void DisplayMove P((int moveNumber));
\r
110 Boolean ParseFEN P((Board board, int *blackPlaysFirst, char *fen));
\r
111 void ChatPopUp P((char *s));
\r
113 ChessSquare piece;
\r
114 POINT pos; /* window coordinates of current pos */
\r
115 POINT lastpos; /* window coordinates of last pos - used for clipping */
\r
116 POINT from; /* board coordinates of the piece's orig pos */
\r
117 POINT to; /* board coordinates of the piece's new pos */
\r
120 static AnimInfo animInfo = { EmptySquare, {-1,-1}, {-1,-1}, {-1,-1} };
\r
123 POINT start; /* window coordinates of start pos */
\r
124 POINT pos; /* window coordinates of current pos */
\r
125 POINT lastpos; /* window coordinates of last pos - used for clipping */
\r
126 POINT from; /* board coordinates of the piece's orig pos */
\r
130 static DragInfo dragInfo = { {-1,-1}, {-1,-1}, {-1,-1}, {-1,-1}, EmptySquare };
\r
133 POINT sq[2]; /* board coordinates of from, to squares */
\r
136 static HighlightInfo highlightInfo = { {{-1, -1}, {-1, -1}} };
\r
137 static HighlightInfo premoveHighlightInfo = { {{-1, -1}, {-1, -1}} };
\r
138 static HighlightInfo partnerHighlightInfo = { {{-1, -1}, {-1, -1}} };
\r
139 static HighlightInfo oldPartnerHighlight = { {{-1, -1}, {-1, -1}} };
\r
141 typedef struct { // [HGM] atomic
\r
142 int fromX, fromY, toX, toY, radius;
\r
145 static ExplodeInfo explodeInfo;
\r
147 /* Window class names */
\r
148 char szAppName[] = "WinBoard";
\r
149 char szConsoleName[] = "WBConsole";
\r
151 /* Title bar text */
\r
152 char szTitle[] = "WinBoard";
\r
153 char szConsoleTitle[] = "I C S Interaction";
\r
156 char *settingsFileName;
\r
157 Boolean saveSettingsOnExit;
\r
158 char installDir[MSG_SIZ];
\r
159 int errorExitStatus;
\r
161 BoardSize boardSize;
\r
162 Boolean chessProgram;
\r
163 //static int boardX, boardY;
\r
164 int minX, minY; // [HGM] placement: volatile limits on upper-left corner
\r
165 int squareSize, lineGap, minorSize;
\r
166 static int winW, winH;
\r
167 static RECT messageRect, whiteRect, blackRect, leftLogoRect, rightLogoRect; // [HGM] logo
\r
168 static int logoHeight = 0;
\r
169 static char messageText[MESSAGE_TEXT_MAX];
\r
170 static int clockTimerEvent = 0;
\r
171 static int loadGameTimerEvent = 0;
\r
172 static int analysisTimerEvent = 0;
\r
173 static DelayedEventCallback delayedTimerCallback;
\r
174 static int delayedTimerEvent = 0;
\r
175 static int buttonCount = 2;
\r
176 char *icsTextMenuString;
\r
178 char *firstChessProgramNames;
\r
179 char *secondChessProgramNames;
\r
181 #define PALETTESIZE 256
\r
183 HINSTANCE hInst; /* current instance */
\r
184 Boolean alwaysOnTop = FALSE;
\r
186 COLORREF lightSquareColor, darkSquareColor, whitePieceColor,
\r
187 blackPieceColor, highlightSquareColor, premoveHighlightColor;
\r
189 ColorClass currentColorClass;
\r
191 static HWND savedHwnd;
\r
192 HWND hCommPort = NULL; /* currently open comm port */
\r
193 static HWND hwndPause; /* pause button */
\r
194 static HBITMAP pieceBitmap[3][(int) BlackPawn]; /* [HGM] nr of bitmaps referred to bP in stead of wK */
\r
195 static HBRUSH lightSquareBrush, darkSquareBrush,
\r
196 blackSquareBrush, /* [HGM] for band between board and holdings */
\r
197 explodeBrush, /* [HGM] atomic */
\r
198 markerBrush, /* [HGM] markers */
\r
199 whitePieceBrush, blackPieceBrush, iconBkgndBrush /*, outlineBrush*/;
\r
200 static POINT gridEndpoints[(BOARD_RANKS + BOARD_FILES + 2) * 2];
\r
201 static DWORD gridVertexCounts[BOARD_RANKS + BOARD_FILES + 2];
\r
202 static HPEN gridPen = NULL;
\r
203 static HPEN highlightPen = NULL;
\r
204 static HPEN premovePen = NULL;
\r
205 static NPLOGPALETTE pLogPal;
\r
206 static BOOL paletteChanged = FALSE;
\r
207 static HICON iconWhite, iconBlack, iconCurrent;
\r
208 static int doingSizing = FALSE;
\r
209 static int lastSizing = 0;
\r
210 static int prevStderrPort;
\r
211 static HBITMAP userLogo;
\r
213 static HBITMAP liteBackTexture = NULL;
\r
214 static HBITMAP darkBackTexture = NULL;
\r
215 static int liteBackTextureMode = BACK_TEXTURE_MODE_PLAIN;
\r
216 static int darkBackTextureMode = BACK_TEXTURE_MODE_PLAIN;
\r
217 static int backTextureSquareSize = 0;
\r
218 static struct { int x; int y; int mode; } backTextureSquareInfo[BOARD_RANKS][BOARD_FILES];
\r
220 #if __GNUC__ && !defined(_winmajor)
\r
221 #define oldDialog 0 /* cygwin doesn't define _winmajor; mingw does */
\r
223 #if defined(_winmajor)
\r
224 #define oldDialog (_winmajor < 4)
\r
226 #define oldDialog 0
\r
230 #define INTERNATIONAL
\r
232 #ifdef INTERNATIONAL
\r
233 # define _(s) T_(s)
\r
239 # define Translate(x, y)
\r
240 # define LoadLanguageFile(s)
\r
243 #ifdef INTERNATIONAL
\r
245 Boolean barbaric; // flag indicating if translation is needed
\r
247 // list of item numbers used in each dialog (used to alter language at run time)
\r
249 #define ABOUTBOX -1 /* not sure why these are needed */
\r
250 #define ABOUTBOX2 -1
\r
252 int dialogItems[][40] = {
\r
253 { ABOUTBOX, IDOK, OPT_MESS, 400 },
\r
254 { DLG_TimeControl, IDC_Babble, OPT_TCUseMoves, OPT_TCUseInc, OPT_TCUseFixed,
\r
255 OPT_TCtext1, OPT_TCtext2, OPT_TCitext1, OPT_TCitext2, OPT_TCftext, GPB_Factors, IDC_Factor1, IDC_Factor2, IDOK, IDCANCEL },
\r
256 { DLG_LoadOptions, OPT_Autostep, OPT_AStext1, IDOK, IDCANCEL },
\r
257 { DLG_SaveOptions, OPT_Autosave, OPT_AVPrompt, OPT_AVToFile, OPT_AVBrowse,
\r
258 801, OPT_PGN, OPT_Old, OPT_OutOfBookInfo, IDOK, IDCANCEL },
\r
259 { 1536, 1090, IDC_Directories, 1089, 1091, IDOK, IDCANCEL, 1038, IDC_IndexNr, 1037 },
\r
260 { DLG_CommPort, IDOK, IDCANCEL, IDC_Port, IDC_Rate, IDC_Bits, IDC_Parity,
\r
261 IDC_Stop, IDC_Flow, OPT_SerialHelp },
\r
262 { DLG_EditComment, IDOK, OPT_CancelComment, OPT_ClearComment, OPT_EditComment },
\r
263 { DLG_PromotionKing, PB_Chancellor, PB_Archbishop, PB_Queen, PB_Rook,
\r
264 PB_Bishop, PB_Knight, PB_King, IDCANCEL, IDC_Yes, IDC_No, IDC_Centaur },
\r
265 { ABOUTBOX2, IDC_ChessBoard },
\r
266 { DLG_GameList, OPT_GameListLoad, OPT_GameListPrev, OPT_GameListNext,
\r
267 OPT_GameListClose, IDC_GameListDoFilter },
\r
268 { DLG_EditTags, IDOK, OPT_TagsCancel, OPT_EditTags },
\r
269 { DLG_Error, IDOK },
\r
270 { DLG_Colorize, IDOK, IDCANCEL, OPT_ChooseColor, OPT_Bold, OPT_Italic,
\r
271 OPT_Underline, OPT_Strikeout, OPT_Sample },
\r
272 { DLG_Question, IDOK, IDCANCEL, OPT_QuestionText },
\r
273 { DLG_Startup, IDC_Welcome, OPT_ChessEngine, OPT_ChessServer, OPT_View,
\r
274 IDC_SPECIFY_ENG_STATIC, IDC_SPECIFY_SERVER_STATIC, OPT_AnyAdditional,
\r
275 IDOK, IDCANCEL, IDM_HELPCONTENTS },
\r
276 { DLG_IndexNumber, IDC_Index },
\r
277 { DLG_TypeInMove, IDOK, IDCANCEL },
\r
278 { DLG_TypeInName, IDOK, IDCANCEL },
\r
279 { DLG_Sound, IDC_Event, OPT_NoSound, OPT_DefaultBeep, OPT_BuiltInSound,
\r
280 OPT_WavFile, OPT_BrowseSound, OPT_DefaultSounds, IDOK, IDCANCEL, OPT_PlaySound },
\r
281 { DLG_GeneralOptions, IDOK, IDCANCEL, OPT_AlwaysOnTop, OPT_HighlightLastMove,
\r
282 OPT_AlwaysQueen, OPT_PeriodicUpdates, OPT_AnimateDragging, OPT_PonderNextMove,
\r
283 OPT_AnimateMoving, OPT_PopupExitMessage, OPT_AutoFlag, OPT_PopupMoveErrors,
\r
284 OPT_AutoFlipView, OPT_ShowButtonBar, OPT_AutoRaiseBoard, OPT_ShowCoordinates,
\r
285 OPT_Blindfold, OPT_ShowThinking, OPT_HighlightDragging, OPT_TestLegality,
\r
286 OPT_SaveExtPGN, OPT_HideThinkFromHuman, OPT_ExtraInfoInMoveHistory,
\r
287 OPT_HighlightMoveArrow, OPT_AutoLogo ,OPT_SmartMove },
\r
288 { DLG_IcsOptions, IDOK, IDCANCEL, OPT_AutoComment, OPT_AutoKibitz, OPT_AutoObserve,
\r
289 OPT_GetMoveList, OPT_LocalLineEditing, OPT_QuietPlay, OPT_SeekGraph, OPT_AutoRefresh,
\r
290 OPT_BgObserve, OPT_DualBoard, OPT_Premove, OPT_PremoveWhite, OPT_PremoveBlack,
\r
291 OPT_SmartMove, OPT_IcsAlarm, IDC_Sec, OPT_ChooseShoutColor, OPT_ChooseSShoutColor,
\r
292 OPT_ChooseChannel1Color, OPT_ChooseChannelColor, OPT_ChooseKibitzColor,
\r
293 OPT_ChooseTellColor, OPT_ChooseChallengeColor, OPT_ChooseRequestColor,
\r
294 OPT_ChooseSeekColor, OPT_ChooseNormalColor, OPT_ChooseBackgroundColor,
\r
295 OPT_DefaultColors, OPT_DontColorize, IDC_Boxes, GPB_Colors, GPB_Premove,
\r
296 GPB_General, GPB_Alarm },
\r
297 { DLG_BoardOptions, IDOK, IDCANCEL, OPT_SizeTiny, OPT_SizeTeeny, OPT_SizeDinky,
\r
298 OPT_SizePetite, OPT_SizeSlim, OPT_SizeSmall, OPT_SizeMediocre, OPT_SizeMiddling,
\r
299 OPT_SizeAverage, OPT_SizeModerate, OPT_SizeMedium, OPT_SizeBulky, OPT_SizeLarge,
\r
300 OPT_SizeBig, OPT_SizeHuge, OPT_SizeGiant, OPT_SizeColossal, OPT_SizeTitanic,
\r
301 OPT_ChooseLightSquareColor, OPT_ChooseDarkSquareColor, OPT_ChooseWhitePieceColor,
\r
302 OPT_ChooseBlackPieceColor, OPT_ChooseHighlightSquareColor, OPT_ChoosePremoveHighlightColor,
\r
303 OPT_Monochrome, OPT_AllWhite, OPT_UpsideDown, OPT_DefaultBoardColors, GPB_Colors,
\r
304 IDC_Light, IDC_Dark, IDC_White, IDC_Black, IDC_High, IDC_PreHigh, GPB_Size },
\r
305 { DLG_NewVariant, IDOK, IDCANCEL, OPT_VariantNormal, OPT_VariantFRC, OPT_VariantWildcastle,
\r
306 OPT_VariantNocastle, OPT_VariantLosers, OPT_VariantGiveaway, OPT_VariantSuicide,
\r
307 OPT_Variant3Check, OPT_VariantTwoKings, OPT_VariantAtomic, OPT_VariantCrazyhouse,
\r
308 OPT_VariantBughouse, OPT_VariantTwilight, OPT_VariantShogi, OPT_VariantSuper,
\r
309 OPT_VariantKnightmate, OPT_VariantBerolina, OPT_VariantCylinder, OPT_VariantFairy,
\r
310 OPT_VariantMakruk, OPT_VariantGothic, OPT_VariantCapablanca, OPT_VariantJanus,
\r
311 OPT_VariantCRC, OPT_VariantFalcon, OPT_VariantCourier, OPT_VariantGreat, OPT_VariantSChess,
\r
312 OPT_VariantShatranj, OPT_VariantXiangqi, GPB_Variant, GPB_Board, IDC_Height,
\r
313 IDC_Width, IDC_Hand, IDC_Pieces, IDC_Def },
\r
314 { DLG_Fonts, IDOK, IDCANCEL, OPT_ChooseClockFont, OPT_ChooseMessageFont,
\r
315 OPT_ChooseCoordFont, OPT_ChooseTagFont, OPT_ChooseCommentsFont, OPT_ChooseConsoleFont, OPT_ChooseMoveHistoryFont, OPT_DefaultFonts,
\r
316 OPT_ClockFont, OPT_MessageFont, OPT_CoordFont, OPT_EditTagsFont,
\r
317 OPT_CommentsFont, OPT_MessageFont5, GPB_Current, GPB_All, OPT_MessageFont6 },
\r
318 { DLG_NewGameFRC, IDC_NFG_Label, IDC_NFG_Random, IDOK, IDCANCEL },
\r
319 { DLG_GameListOptions, IDC_GLT, IDC_GLT_Up, IDC_GLT_Down, IDC_GLT_Restore,
\r
320 IDC_GLT_Default, IDOK, IDCANCEL, IDC_GLT_RestoreTo },
\r
321 { DLG_MoveHistory },
\r
322 { DLG_EvalGraph },
\r
323 { DLG_EngineOutput, IDC_EngineLabel1, IDC_Engine1_NPS, IDC_EngineLabel2, IDC_Engine2_NPS },
\r
324 { DLG_Chat, IDC_Partner, IDC_Clear, IDC_Send, },
\r
325 { DLG_EnginePlayOptions, IDC_EpPonder, IDC_EpShowThinking, IDC_EpHideThinkingHuman,
\r
326 IDC_EpPeriodicUpdates, GPB_Adjudications, IDC_Draw, IDC_Moves, IDC_Threshold,
\r
327 IDC_Centi, IDC_TestClaims, IDC_DetectMates, IDC_MaterialDraws, IDC_TrivialDraws,
\r
328 GPB_Apply, IDC_Rule, IDC_Repeats, IDC_ScoreAbs1, IDC_ScoreAbs2, IDOK, IDCANCEL },
\r
329 { DLG_OptionsUCI, IDC_PolyDir, IDC_BrowseForPolyglotDir, IDC_Hash, IDC_Path,
\r
330 IDC_BrowseForEGTB, IDC_Cache, IDC_UseBook, IDC_BrowseForBook, IDC_CPU, IDC_OwnBook1,
\r
331 IDC_OwnBook2, IDC_Depth, IDC_Variation, IDC_DefGames, IDOK, IDCANCEL },
\r
335 static char languageBuf[50000], *foreign[1000], *english[1000], *languageFile[MSG_SIZ];
\r
336 static int lastChecked;
\r
337 static char oldLanguage[MSG_SIZ], *menuText[10][30];
\r
338 extern int tinyLayout;
\r
339 extern char * menuBarText[][10];
\r
342 LoadLanguageFile(char *name)
\r
343 { //load the file with translations, and make a list of the strings to be translated, and their translations
\r
345 int i=0, j=0, n=0, k;
\r
348 if(!name || name[0] == NULLCHAR) return;
\r
349 snprintf(buf, MSG_SIZ, "%s%s", name, strchr(name, '.') ? "" : ".lng"); // auto-append lng extension
\r
350 appData.language = oldLanguage;
\r
351 if(!strcmp(buf, oldLanguage)) { barbaric = 1; return; } // this language already loaded; just switch on
\r
352 if((f = fopen(buf, "r")) == NULL) return;
\r
353 while((k = fgetc(f)) != EOF) {
\r
354 if(i >= sizeof(languageBuf)) { DisplayError("Language file too big", 0); return; }
\r
355 languageBuf[i] = k;
\r
357 if(languageBuf[n] == '"' && languageBuf[i-1] == '"') {
\r
359 if(p = strstr(languageBuf + n + 1, "\" === \"")) {
\r
360 if(p > languageBuf+n+2 && p+8 < languageBuf+i) {
\r
361 if(j >= sizeof(english)) { DisplayError("Too many translated strings", 0); return; }
\r
362 english[j] = languageBuf + n + 1; *p = 0;
\r
363 foreign[j++] = p + 7; languageBuf[i-1] = 0;
\r
364 //if(appData.debugMode) fprintf(debugFP, "translation: replace '%s' by '%s'\n", english[j-1], foreign[j-1]);
\r
369 } else if(i > 0 && languageBuf[i-1] == '\\') {
\r
371 case 'n': k = '\n'; break;
\r
372 case 'r': k = '\r'; break;
\r
373 case 't': k = '\t'; break;
\r
375 languageBuf[--i] = k;
\r
380 barbaric = (j != 0);
\r
381 safeStrCpy(oldLanguage, buf, sizeof(oldLanguage)/sizeof(oldLanguage[0]) );
\r
386 { // return the translation of the given string
\r
387 // efficiency can be improved a lot...
\r
389 //if(appData.debugMode) fprintf(debugFP, "T_(%s)\n", s);
\r
390 if(!barbaric) return s;
\r
391 if(!s) return ""; // sanity
\r
392 while(english[i]) {
\r
393 if(!strcmp(s, english[i])) return foreign[i];
\r
400 Translate(HWND hDlg, int dialogID)
\r
401 { // translate all text items in the given dialog
\r
403 char buf[MSG_SIZ], *s;
\r
404 if(!barbaric) return;
\r
405 while(dialogItems[i][0] && dialogItems[i][0] != dialogID) i++; // find the dialog description
\r
406 if(dialogItems[i][0] != dialogID) return; // unknown dialog, should not happen
\r
407 GetWindowText( hDlg, buf, MSG_SIZ );
\r
409 if(strcmp(buf, s)) SetWindowText(hDlg, s); // replace by translated string (if different)
\r
410 for(j=1; k=dialogItems[i][j]; j++) { // translate all listed dialog items
\r
411 GetDlgItemText(hDlg, k, buf, MSG_SIZ);
\r
412 if(strlen(buf) == 0) continue;
\r
414 if(strcmp(buf, s)) SetDlgItemText(hDlg, k, s); // replace by translated string (if different)
\r
419 TranslateOneMenu(int i, HMENU subMenu)
\r
422 static MENUITEMINFO info;
\r
424 info.cbSize = sizeof(MENUITEMINFO);
\r
425 info.fMask = MIIM_STATE | MIIM_TYPE;
\r
426 for(j=GetMenuItemCount(subMenu)-1; j>=0; j--){
\r
428 info.dwTypeData = buf;
\r
429 info.cch = sizeof(buf);
\r
430 GetMenuItemInfo(subMenu, j, TRUE, &info);
\r
432 if(menuText[i][j]) safeStrCpy(buf, menuText[i][j], sizeof(buf)/sizeof(buf[0]) );
\r
433 else menuText[i][j] = strdup(buf); // remember original on first change
\r
435 if(buf[0] == NULLCHAR) continue;
\r
436 info.dwTypeData = T_(buf);
\r
437 info.cch = strlen(buf)+1;
\r
438 SetMenuItemInfo(subMenu, j, TRUE, &info);
\r
444 TranslateMenus(int addLanguage)
\r
447 WIN32_FIND_DATA fileData;
\r
449 #define IDM_English 1970
\r
451 HMENU mainMenu = GetMenu(hwndMain);
\r
452 for (i=GetMenuItemCount(mainMenu)-1; i>=0; i--) {
\r
453 HMENU subMenu = GetSubMenu(mainMenu, i);
\r
454 ModifyMenu(mainMenu, i, MF_STRING|MF_BYPOSITION|MF_POPUP|EnableMenuItem(mainMenu, i, MF_BYPOSITION),
\r
455 (UINT) subMenu, T_(menuBarText[tinyLayout][i]));
\r
456 TranslateOneMenu(i, subMenu);
\r
458 DrawMenuBar(hwndMain);
\r
461 if(!addLanguage) return;
\r
462 if((hFind = FindFirstFile("*.LNG", &fileData)) != INVALID_HANDLE_VALUE) {
\r
463 HMENU mainMenu = GetMenu(hwndMain);
\r
464 HMENU subMenu = GetSubMenu(mainMenu, GetMenuItemCount(mainMenu)-1);
\r
465 AppendMenu(subMenu, MF_SEPARATOR, (UINT_PTR) 0, NULL);
\r
466 AppendMenu(subMenu, MF_ENABLED|MF_STRING|(barbaric?MF_UNCHECKED:MF_CHECKED), (UINT_PTR) IDM_English, (LPCTSTR) "English");
\r
467 i = 0; lastChecked = IDM_English;
\r
469 char *p, *q = fileData.cFileName;
\r
470 int checkFlag = MF_UNCHECKED;
\r
471 languageFile[i] = strdup(q);
\r
472 if(barbaric && !strcmp(oldLanguage, q)) {
\r
473 checkFlag = MF_CHECKED;
\r
474 lastChecked = IDM_English + i + 1;
\r
475 CheckMenuItem(mainMenu, IDM_English, MF_BYCOMMAND|MF_UNCHECKED);
\r
477 *q = ToUpper(*q); while(*++q) *q = ToLower(*q);
\r
478 p = strstr(fileData.cFileName, ".lng");
\r
480 AppendMenu(subMenu, MF_ENABLED|MF_STRING|checkFlag, (UINT_PTR) IDM_English + ++i, (LPCTSTR) fileData.cFileName);
\r
481 } while(FindNextFile(hFind, &fileData));
\r
494 int cliWidth, cliHeight;
\r
497 SizeInfo sizeInfo[] =
\r
499 { "tiny", 21, 0, 1, 1, 0, 0 },
\r
500 { "teeny", 25, 1, 1, 1, 0, 0 },
\r
501 { "dinky", 29, 1, 1, 1, 0, 0 },
\r
502 { "petite", 33, 1, 1, 1, 0, 0 },
\r
503 { "slim", 37, 2, 1, 0, 0, 0 },
\r
504 { "small", 40, 2, 1, 0, 0, 0 },
\r
505 { "mediocre", 45, 2, 1, 0, 0, 0 },
\r
506 { "middling", 49, 2, 0, 0, 0, 0 },
\r
507 { "average", 54, 2, 0, 0, 0, 0 },
\r
508 { "moderate", 58, 3, 0, 0, 0, 0 },
\r
509 { "medium", 64, 3, 0, 0, 0, 0 },
\r
510 { "bulky", 72, 3, 0, 0, 0, 0 },
\r
511 { "large", 80, 3, 0, 0, 0, 0 },
\r
512 { "big", 87, 3, 0, 0, 0, 0 },
\r
513 { "huge", 95, 3, 0, 0, 0, 0 },
\r
514 { "giant", 108, 3, 0, 0, 0, 0 },
\r
515 { "colossal", 116, 4, 0, 0, 0, 0 },
\r
516 { "titanic", 129, 4, 0, 0, 0, 0 },
\r
517 { NULL, 0, 0, 0, 0, 0, 0 }
\r
520 #define MF(x) {x, {{0,}, 0. }, {0, }, 0}
\r
521 MyFont fontRec[NUM_SIZES][NUM_FONTS] =
\r
523 { MF(CLOCK_FONT_TINY), MF(MESSAGE_FONT_TINY), MF(COORD_FONT_TINY), MF(CONSOLE_FONT_TINY), MF(COMMENT_FONT_TINY), MF(EDITTAGS_FONT_TINY), MF(MOVEHISTORY_FONT_ALL) },
\r
524 { MF(CLOCK_FONT_TEENY), MF(MESSAGE_FONT_TEENY), MF(COORD_FONT_TEENY), MF(CONSOLE_FONT_TEENY), MF(COMMENT_FONT_TEENY), MF(EDITTAGS_FONT_TEENY), MF(MOVEHISTORY_FONT_ALL) },
\r
525 { MF(CLOCK_FONT_DINKY), MF(MESSAGE_FONT_DINKY), MF(COORD_FONT_DINKY), MF(CONSOLE_FONT_DINKY), MF(COMMENT_FONT_DINKY), MF(EDITTAGS_FONT_DINKY), MF(MOVEHISTORY_FONT_ALL) },
\r
526 { MF(CLOCK_FONT_PETITE), MF(MESSAGE_FONT_PETITE), MF(COORD_FONT_PETITE), MF(CONSOLE_FONT_PETITE), MF(COMMENT_FONT_PETITE), MF(EDITTAGS_FONT_PETITE), MF(MOVEHISTORY_FONT_ALL) },
\r
527 { MF(CLOCK_FONT_SLIM), MF(MESSAGE_FONT_SLIM), MF(COORD_FONT_SLIM), MF(CONSOLE_FONT_SLIM), MF(COMMENT_FONT_SLIM), MF(EDITTAGS_FONT_SLIM), MF(MOVEHISTORY_FONT_ALL) },
\r
528 { MF(CLOCK_FONT_SMALL), MF(MESSAGE_FONT_SMALL), MF(COORD_FONT_SMALL), MF(CONSOLE_FONT_SMALL), MF(COMMENT_FONT_SMALL), MF(EDITTAGS_FONT_SMALL), MF(MOVEHISTORY_FONT_ALL) },
\r
529 { MF(CLOCK_FONT_MEDIOCRE), MF(MESSAGE_FONT_MEDIOCRE), MF(COORD_FONT_MEDIOCRE), MF(CONSOLE_FONT_MEDIOCRE), MF(COMMENT_FONT_MEDIOCRE), MF(EDITTAGS_FONT_MEDIOCRE), MF(MOVEHISTORY_FONT_ALL) },
\r
530 { MF(CLOCK_FONT_MIDDLING), MF(MESSAGE_FONT_MIDDLING), MF(COORD_FONT_MIDDLING), MF(CONSOLE_FONT_MIDDLING), MF(COMMENT_FONT_MIDDLING), MF(EDITTAGS_FONT_MIDDLING), MF(MOVEHISTORY_FONT_ALL) },
\r
531 { MF(CLOCK_FONT_AVERAGE), MF(MESSAGE_FONT_AVERAGE), MF(COORD_FONT_AVERAGE), MF(CONSOLE_FONT_AVERAGE), MF(COMMENT_FONT_AVERAGE), MF(EDITTAGS_FONT_AVERAGE), MF(MOVEHISTORY_FONT_ALL) },
\r
532 { MF(CLOCK_FONT_MODERATE), MF(MESSAGE_FONT_MODERATE), MF(COORD_FONT_MODERATE), MF(CONSOLE_FONT_MODERATE), MF(COMMENT_FONT_MODERATE), MF(EDITTAGS_FONT_MODERATE), MF(MOVEHISTORY_FONT_ALL) },
\r
533 { MF(CLOCK_FONT_MEDIUM), MF(MESSAGE_FONT_MEDIUM), MF(COORD_FONT_MEDIUM), MF(CONSOLE_FONT_MEDIUM), MF(COMMENT_FONT_MEDIUM), MF(EDITTAGS_FONT_MEDIUM), MF(MOVEHISTORY_FONT_ALL) },
\r
534 { MF(CLOCK_FONT_BULKY), MF(MESSAGE_FONT_BULKY), MF(COORD_FONT_BULKY), MF(CONSOLE_FONT_BULKY), MF(COMMENT_FONT_BULKY), MF(EDITTAGS_FONT_BULKY), MF(MOVEHISTORY_FONT_ALL) },
\r
535 { MF(CLOCK_FONT_LARGE), MF(MESSAGE_FONT_LARGE), MF(COORD_FONT_LARGE), MF(CONSOLE_FONT_LARGE), MF(COMMENT_FONT_LARGE), MF(EDITTAGS_FONT_LARGE), MF(MOVEHISTORY_FONT_ALL) },
\r
536 { MF(CLOCK_FONT_BIG), MF(MESSAGE_FONT_BIG), MF(COORD_FONT_BIG), MF(CONSOLE_FONT_BIG), MF(COMMENT_FONT_BIG), MF(EDITTAGS_FONT_BIG), MF(MOVEHISTORY_FONT_ALL) },
\r
537 { MF(CLOCK_FONT_HUGE), MF(MESSAGE_FONT_HUGE), MF(COORD_FONT_HUGE), MF(CONSOLE_FONT_HUGE), MF(COMMENT_FONT_HUGE), MF(EDITTAGS_FONT_HUGE), MF(MOVEHISTORY_FONT_ALL) },
\r
538 { MF(CLOCK_FONT_GIANT), MF(MESSAGE_FONT_GIANT), MF(COORD_FONT_GIANT), MF(CONSOLE_FONT_GIANT), MF(COMMENT_FONT_GIANT), MF(EDITTAGS_FONT_GIANT), MF(MOVEHISTORY_FONT_ALL) },
\r
539 { MF(CLOCK_FONT_COLOSSAL), MF(MESSAGE_FONT_COLOSSAL), MF(COORD_FONT_COLOSSAL), MF(CONSOLE_FONT_COLOSSAL), MF(COMMENT_FONT_COLOSSAL), MF(EDITTAGS_FONT_COLOSSAL), MF(MOVEHISTORY_FONT_ALL) },
\r
540 { MF(CLOCK_FONT_TITANIC), MF(MESSAGE_FONT_TITANIC), MF(COORD_FONT_TITANIC), MF(CONSOLE_FONT_TITANIC), MF(COMMENT_FONT_TITANIC), MF(EDITTAGS_FONT_TITANIC), MF(MOVEHISTORY_FONT_ALL) },
\r
543 MyFont *font[NUM_SIZES][NUM_FONTS];
\r
552 #define BUTTON_WIDTH (tinyLayout ? 16 : 32)
\r
553 #define N_BUTTONS 5
\r
555 MyButtonDesc buttonDesc[N_BUTTONS] =
\r
557 {"<<", IDM_ToStart, NULL, NULL},
\r
558 {"<", IDM_Backward, NULL, NULL},
\r
559 {"P", IDM_Pause, NULL, NULL},
\r
560 {">", IDM_Forward, NULL, NULL},
\r
561 {">>", IDM_ToEnd, NULL, NULL},
\r
564 int tinyLayout = 0, smallLayout = 0;
\r
565 #define MENU_BAR_ITEMS 9
\r
566 char *menuBarText[2][MENU_BAR_ITEMS+1] = {
\r
567 { N_("&File"), N_("&Edit"), N_("&View"), N_("&Mode"), N_("&Action"), N_("E&ngine"), N_("&Options"), N_("&Help"), NULL },
\r
568 { N_("&F"), N_("&E"), N_("&V"), N_("&M"), N_("&A"), N_("&N"), N_("&O"), N_("&H"), NULL },
\r
572 MySound sounds[(int)NSoundClasses];
\r
573 MyTextAttribs textAttribs[(int)NColorClasses];
\r
575 MyColorizeAttribs colorizeAttribs[] = {
\r
576 { (COLORREF)0, 0, N_("Shout Text") },
\r
577 { (COLORREF)0, 0, N_("SShout/CShout") },
\r
578 { (COLORREF)0, 0, N_("Channel 1 Text") },
\r
579 { (COLORREF)0, 0, N_("Channel Text") },
\r
580 { (COLORREF)0, 0, N_("Kibitz Text") },
\r
581 { (COLORREF)0, 0, N_("Tell Text") },
\r
582 { (COLORREF)0, 0, N_("Challenge Text") },
\r
583 { (COLORREF)0, 0, N_("Request Text") },
\r
584 { (COLORREF)0, 0, N_("Seek Text") },
\r
585 { (COLORREF)0, 0, N_("Normal Text") },
\r
586 { (COLORREF)0, 0, N_("None") }
\r
591 static char *commentTitle;
\r
592 static char *commentText;
\r
593 static int commentIndex;
\r
594 static Boolean editComment = FALSE;
\r
597 char errorTitle[MSG_SIZ];
\r
598 char errorMessage[2*MSG_SIZ];
\r
599 HWND errorDialog = NULL;
\r
600 BOOLEAN moveErrorMessageUp = FALSE;
\r
601 BOOLEAN consoleEcho = TRUE;
\r
602 CHARFORMAT consoleCF;
\r
603 COLORREF consoleBackgroundColor;
\r
605 char *programVersion;
\r
611 typedef int CPKind;
\r
620 SOCKET sock2; /* stderr socket for OpenRcmd */
\r
623 #define INPUT_SOURCE_BUF_SIZE 4096
\r
625 typedef struct _InputSource {
\r
632 char buf[INPUT_SOURCE_BUF_SIZE];
\r
636 InputCallback func;
\r
637 struct _InputSource *second; /* for stderr thread on CPRcmd */
\r
641 InputSource *consoleInputSource;
\r
646 VOID ConsoleOutput(char* data, int length, int forceVisible);
\r
647 VOID ConsoleCreate();
\r
649 ConsoleWndProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam);
\r
650 VOID ColorizeTextPopup(HWND hwnd, ColorClass cc);
\r
651 VOID PrintCommSettings(FILE *f, char *name, DCB *dcb);
\r
652 VOID ParseCommSettings(char *arg, DCB *dcb);
\r
654 StartupDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam);
\r
655 VOID APIENTRY MenuPopup(HWND hwnd, POINT pt, HMENU hmenu, UINT def);
\r
656 void ParseIcsTextMenu(char *icsTextMenuString);
\r
657 VOID PopUpNameDialog(char firstchar);
\r
658 VOID UpdateSampleText(HWND hDlg, int id, MyColorizeAttribs *mca);
\r
662 int GameListOptions();
\r
664 int dummy; // [HGM] for obsolete args
\r
666 HWND hwndMain = NULL; /* root window*/
\r
667 HWND hwndConsole = NULL;
\r
668 HWND commentDialog = NULL;
\r
669 HWND moveHistoryDialog = NULL;
\r
670 HWND evalGraphDialog = NULL;
\r
671 HWND engineOutputDialog = NULL;
\r
672 HWND gameListDialog = NULL;
\r
673 HWND editTagsDialog = NULL;
\r
675 int commentUp = FALSE;
\r
677 WindowPlacement wpMain;
\r
678 WindowPlacement wpConsole;
\r
679 WindowPlacement wpComment;
\r
680 WindowPlacement wpMoveHistory;
\r
681 WindowPlacement wpEvalGraph;
\r
682 WindowPlacement wpEngineOutput;
\r
683 WindowPlacement wpGameList;
\r
684 WindowPlacement wpTags;
\r
686 VOID EngineOptionsPopup(); // [HGM] settings
\r
688 VOID GothicPopUp(char *title, VariantClass variant);
\r
690 * Setting "frozen" should disable all user input other than deleting
\r
691 * the window. We do this while engines are initializing themselves.
\r
693 static int frozen = 0;
\r
694 static int oldMenuItemState[MENU_BAR_ITEMS];
\r
700 if (frozen) return;
\r
702 hmenu = GetMenu(hwndMain);
\r
703 for (i=0; i<MENU_BAR_ITEMS; i++) {
\r
704 oldMenuItemState[i] = EnableMenuItem(hmenu, i, MF_BYPOSITION|MF_GRAYED);
\r
706 DrawMenuBar(hwndMain);
\r
709 /* Undo a FreezeUI */
\r
715 if (!frozen) return;
\r
717 hmenu = GetMenu(hwndMain);
\r
718 for (i=0; i<MENU_BAR_ITEMS; i++) {
\r
719 EnableMenuItem(hmenu, i, MF_BYPOSITION|oldMenuItemState[i]);
\r
721 DrawMenuBar(hwndMain);
\r
724 /*static*/ int fromX = -1, fromY = -1, toX, toY; // [HGM] moved upstream, so JAWS can use them
\r
726 /* JAWS preparation patch (WinBoard for the sight impaired). Define required insertions as empty */
\r
732 #define JAWS_ALT_INTERCEPT
\r
733 #define JAWS_KB_NAVIGATION
\r
734 #define JAWS_MENU_ITEMS
\r
735 #define JAWS_SILENCE
\r
736 #define JAWS_REPLAY
\r
738 #define JAWS_COPYRIGHT
\r
739 #define JAWS_DELETE(X) X
\r
740 #define SAYMACHINEMOVE()
\r
744 /*---------------------------------------------------------------------------*\
\r
748 \*---------------------------------------------------------------------------*/
\r
751 WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
\r
752 LPSTR lpCmdLine, int nCmdShow)
\r
755 HANDLE hAccelMain, hAccelNoAlt, hAccelNoICS;
\r
756 // INITCOMMONCONTROLSEX ex;
\r
760 LoadLibrary("RICHED32.DLL");
\r
761 consoleCF.cbSize = sizeof(CHARFORMAT);
\r
763 if (!InitApplication(hInstance)) {
\r
766 if (!InitInstance(hInstance, nCmdShow, lpCmdLine)) {
\r
773 // InitCommonControlsEx(&ex);
\r
774 InitCommonControls();
\r
776 hAccelMain = LoadAccelerators (hInstance, szAppName);
\r
777 hAccelNoAlt = LoadAccelerators (hInstance, "NO_ALT");
\r
778 hAccelNoICS = LoadAccelerators( hInstance, "NO_ICS"); /* [AS] No Ctrl-V on ICS!!! */
\r
780 /* Acquire and dispatch messages until a WM_QUIT message is received. */
\r
782 while (GetMessage(&msg, /* message structure */
\r
783 NULL, /* handle of window receiving the message */
\r
784 0, /* lowest message to examine */
\r
785 0)) /* highest message to examine */
\r
788 if(msg.message == WM_CHAR && msg.wParam == '\t') {
\r
789 // [HGM] navigate: switch between all windows with tab
\r
790 HWND e1 = NULL, e2 = NULL, mh = NULL, hInput = NULL, hText = NULL;
\r
791 int i, currentElement = 0;
\r
793 // first determine what element of the chain we come from (if any)
\r
794 if(appData.icsActive) {
\r
795 hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
796 hText = GetDlgItem(hwndConsole, OPT_ConsoleText);
\r
798 if(engineOutputDialog && EngineOutputIsUp()) {
\r
799 e1 = GetDlgItem(engineOutputDialog, IDC_EngineMemo1);
\r
800 e2 = GetDlgItem(engineOutputDialog, IDC_EngineMemo2);
\r
802 if(moveHistoryDialog && MoveHistoryIsUp()) {
\r
803 mh = GetDlgItem(moveHistoryDialog, IDC_MoveHistory);
\r
805 if(msg.hwnd == hwndMain) currentElement = 7 ; else
\r
806 if(msg.hwnd == engineOutputDialog) currentElement = 2; else
\r
807 if(msg.hwnd == e1) currentElement = 2; else
\r
808 if(msg.hwnd == e2) currentElement = 3; else
\r
809 if(msg.hwnd == moveHistoryDialog) currentElement = 4; else
\r
810 if(msg.hwnd == mh) currentElement = 4; else
\r
811 if(msg.hwnd == evalGraphDialog) currentElement = 6; else
\r
812 if(msg.hwnd == hText) currentElement = 5; else
\r
813 if(msg.hwnd == hInput) currentElement = 6; else
\r
814 for (i = 0; i < N_BUTTONS; i++) {
\r
815 if (buttonDesc[i].hwnd == msg.hwnd) { currentElement = 1; break; }
\r
818 // determine where to go to
\r
819 if(currentElement) { HWND h = NULL; int direction = GetKeyState(VK_SHIFT) < 0 ? -1 : 1;
\r
821 currentElement = (currentElement + direction) % 7;
\r
822 switch(currentElement) {
\r
824 h = hwndMain; break; // passing this case always makes the loop exit
\r
826 h = buttonDesc[0].hwnd; break; // could be NULL
\r
828 if(!EngineOutputIsUp()) continue; // skip closed auxiliary windows
\r
831 if(!EngineOutputIsUp()) continue;
\r
834 if(!MoveHistoryIsUp()) continue;
\r
836 // case 6: // input to eval graph does not seem to get here!
\r
837 // if(!EvalGraphIsUp()) continue;
\r
838 // h = evalGraphDialog; break;
\r
840 if(!appData.icsActive) continue;
\r
844 if(!appData.icsActive) continue;
\r
850 if(currentElement > 4 && IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);
\r
851 if(currentElement < 5 && IsIconic(hwndMain)) ShowWindow(hwndMain, SW_RESTORE); // all open together
\r
854 continue; // this message now has been processed
\r
858 if (!(commentDialog && IsDialogMessage(commentDialog, &msg)) &&
\r
859 !(moveHistoryDialog && IsDialogMessage(moveHistoryDialog, &msg)) &&
\r
860 !(evalGraphDialog && IsDialogMessage(evalGraphDialog, &msg)) &&
\r
861 !(engineOutputDialog && IsDialogMessage(engineOutputDialog, &msg)) &&
\r
862 !(editTagsDialog && IsDialogMessage(editTagsDialog, &msg)) &&
\r
863 !(gameListDialog && IsDialogMessage(gameListDialog, &msg)) &&
\r
864 !(errorDialog && IsDialogMessage(errorDialog, &msg)) &&
\r
865 !(!frozen && TranslateAccelerator(hwndMain, hAccelMain, &msg)) && JAWS_ACCEL
\r
866 !(!hwndConsole && TranslateAccelerator(hwndMain, hAccelNoICS, &msg)) &&
\r
867 !(!hwndConsole && TranslateAccelerator(hwndMain, hAccelNoAlt, &msg))) {
\r
868 int done = 0, i; // [HGM] chat: dispatch cat-box messages
\r
869 for(i=0; i<MAX_CHAT; i++)
\r
870 if(chatHandle[i] && IsDialogMessage(chatHandle[i], &msg)) {
\r
873 if(done) continue; // [HGM] chat: end patch
\r
874 TranslateMessage(&msg); /* Translates virtual key codes */
\r
875 DispatchMessage(&msg); /* Dispatches message to window */
\r
880 return (msg.wParam); /* Returns the value from PostQuitMessage */
\r
883 /*---------------------------------------------------------------------------*\
\r
885 * Initialization functions
\r
887 \*---------------------------------------------------------------------------*/
\r
891 { // update user logo if necessary
\r
892 static char oldUserName[MSG_SIZ], dir[MSG_SIZ], *curName;
\r
894 if(appData.autoLogo) {
\r
895 curName = UserName();
\r
896 if(strcmp(curName, oldUserName)) {
\r
897 GetCurrentDirectory(MSG_SIZ, dir);
\r
898 SetCurrentDirectory(installDir);
\r
899 snprintf(oldUserName, MSG_SIZ, "logos\\%s.bmp", curName);
\r
900 userLogo = LoadImage( 0, oldUserName, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );
\r
901 safeStrCpy(oldUserName, curName, sizeof(oldUserName)/sizeof(oldUserName[0]) );
\r
902 if(userLogo == NULL)
\r
903 userLogo = LoadImage( 0, "logos\\dummy.bmp", IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );
\r
904 SetCurrentDirectory(dir); /* return to prev directory */
\r
910 InitApplication(HINSTANCE hInstance)
\r
914 /* Fill in window class structure with parameters that describe the */
\r
917 wc.style = CS_HREDRAW | CS_VREDRAW; /* Class style(s). */
\r
918 wc.lpfnWndProc = (WNDPROC)WndProc; /* Window Procedure */
\r
919 wc.cbClsExtra = 0; /* No per-class extra data. */
\r
920 wc.cbWndExtra = 0; /* No per-window extra data. */
\r
921 wc.hInstance = hInstance; /* Owner of this class */
\r
922 wc.hIcon = LoadIcon(hInstance, "icon_white");
\r
923 wc.hCursor = LoadCursor(NULL, IDC_ARROW); /* Cursor */
\r
924 wc.hbrBackground = (HBRUSH)(COLOR_WINDOW+1); /* Default color */
\r
925 wc.lpszMenuName = szAppName; /* Menu name from .RC */
\r
926 wc.lpszClassName = szAppName; /* Name to register as */
\r
928 /* Register the window class and return success/failure code. */
\r
929 if (!RegisterClass(&wc)) return FALSE;
\r
931 wc.style = CS_HREDRAW | CS_VREDRAW;
\r
932 wc.lpfnWndProc = (WNDPROC)ConsoleWndProc;
\r
934 wc.cbWndExtra = DLGWINDOWEXTRA;
\r
935 wc.hInstance = hInstance;
\r
936 wc.hIcon = LoadIcon(hInstance, "icon_white");
\r
937 wc.hCursor = LoadCursor(NULL, IDC_ARROW);
\r
938 wc.hbrBackground = (HBRUSH)(COLOR_MENU+1);
\r
939 wc.lpszMenuName = NULL;
\r
940 wc.lpszClassName = szConsoleName;
\r
942 if (!RegisterClass(&wc)) return FALSE;
\r
947 /* Set by InitInstance, used by EnsureOnScreen */
\r
948 int screenHeight, screenWidth;
\r
951 EnsureOnScreen(int *x, int *y, int minX, int minY)
\r
953 // int gap = GetSystemMetrics(SM_CYFRAME) + GetSystemMetrics(SM_CYCAPTION);
\r
954 /* Be sure window at (x,y) is not off screen (or even mostly off screen) */
\r
955 if (*x > screenWidth - 32) *x = 0;
\r
956 if (*y > screenHeight - 32) *y = 0;
\r
957 if (*x < minX) *x = minX;
\r
958 if (*y < minY) *y = minY;
\r
962 LoadLogo(ChessProgramState *cps, int n, Boolean ics)
\r
964 char buf[MSG_SIZ], dir[MSG_SIZ];
\r
965 GetCurrentDirectory(MSG_SIZ, dir);
\r
966 SetCurrentDirectory(installDir);
\r
967 if( appData.logo[n] && appData.logo[n][0] != NULLCHAR) {
\r
968 cps->programLogo = LoadImage( 0, appData.logo[n], IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );
\r
970 if (cps->programLogo == NULL && appData.debugMode) {
\r
971 fprintf( debugFP, "Unable to load logo bitmap '%s'\n", appData.logo[n] );
\r
973 } else if(appData.autoLogo) {
\r
974 if(ics) { // [HGM] logo: in ICS mode second can be used for ICS
\r
975 sprintf(buf, "logos\\%s.bmp", appData.icsHost);
\r
976 cps->programLogo = LoadImage( 0, buf, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );
\r
978 if(appData.directory[n] && appData.directory[n][0]) {
\r
979 SetCurrentDirectory(appData.directory[n]);
\r
980 cps->programLogo = LoadImage( 0, "logo.bmp", IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );
\r
983 SetCurrentDirectory(dir); /* return to prev directory */
\r
987 InitInstance(HINSTANCE hInstance, int nCmdShow, LPSTR lpCmdLine)
\r
989 HWND hwnd; /* Main window handle. */
\r
991 WINDOWPLACEMENT wp;
\r
994 hInst = hInstance; /* Store instance handle in our global variable */
\r
995 programName = szAppName;
\r
997 if (SearchPath(NULL, "WinBoard.exe", NULL, MSG_SIZ, installDir, &filepart)) {
\r
998 *filepart = NULLCHAR;
\r
1000 GetCurrentDirectory(MSG_SIZ, installDir);
\r
1002 gameInfo.boardWidth = gameInfo.boardHeight = 8; // [HGM] won't have open window otherwise
\r
1003 screenWidth = screenHeight = 1000; // [HGM] placement: kludge to allow calling EnsureOnScreen from InitAppData
\r
1004 InitAppData(lpCmdLine); /* Get run-time parameters */
\r
1005 /* xboard, and older WinBoards, controlled the move sound with the
\r
1006 appData.ringBellAfterMoves option. In the current WinBoard, we
\r
1007 always turn the option on (so that the backend will call us),
\r
1008 then let the user turn the sound off by setting it to silence if
\r
1009 desired. To accommodate old winboard.ini files saved by old
\r
1010 versions of WinBoard, we also turn off the sound if the option
\r
1011 was initially set to false. [HGM] taken out of InitAppData */
\r
1012 if (!appData.ringBellAfterMoves) {
\r
1013 sounds[(int)SoundMove].name = strdup("");
\r
1014 appData.ringBellAfterMoves = TRUE;
\r
1016 if (appData.debugMode) {
\r
1017 debugFP = fopen(appData.nameOfDebugFile, "w");
\r
1018 setbuf(debugFP, NULL);
\r
1021 LoadLanguageFile(appData.language);
\r
1025 // InitEngineUCI( installDir, &first ); // [HGM] incorporated in InitBackEnd1()
\r
1026 // InitEngineUCI( installDir, &second );
\r
1028 /* Create a main window for this application instance. */
\r
1029 hwnd = CreateWindow(szAppName, szTitle,
\r
1030 (WS_OVERLAPPEDWINDOW & ~WS_MAXIMIZEBOX),
\r
1031 CW_USEDEFAULT, 0, CW_USEDEFAULT, 0,
\r
1032 NULL, NULL, hInstance, NULL);
\r
1035 /* If window could not be created, return "failure" */
\r
1040 /* [HGM] logo: Load logos if specified (must be done before InitDrawingSizes) */
\r
1041 LoadLogo(&first, 0, FALSE);
\r
1042 LoadLogo(&second, 1, appData.icsActive);
\r
1046 iconWhite = LoadIcon(hInstance, "icon_white");
\r
1047 iconBlack = LoadIcon(hInstance, "icon_black");
\r
1048 iconCurrent = iconWhite;
\r
1049 InitDrawingColors();
\r
1050 screenHeight = GetSystemMetrics(SM_CYSCREEN);
\r
1051 screenWidth = GetSystemMetrics(SM_CXSCREEN);
\r
1052 for (ibs = (int) NUM_SIZES - 1; ibs >= 0; ibs--) {
\r
1053 /* Compute window size for each board size, and use the largest
\r
1054 size that fits on this screen as the default. */
\r
1055 InitDrawingSizes((BoardSize)(ibs+1000), 0);
\r
1056 if (boardSize == (BoardSize)-1 &&
\r
1057 winH <= screenHeight
\r
1058 - GetSystemMetrics(SM_CYFRAME) - GetSystemMetrics(SM_CYCAPTION) - 10
\r
1059 && winW <= screenWidth) {
\r
1060 boardSize = (BoardSize)ibs;
\r
1064 InitDrawingSizes(boardSize, 0);
\r
1066 buttonCount = GetSystemMetrics(SM_CMOUSEBUTTONS);
\r
1068 /* [AS] Load textures if specified */
\r
1069 ZeroMemory( &backTextureSquareInfo, sizeof(backTextureSquareInfo) );
\r
1071 if( appData.liteBackTextureFile && appData.liteBackTextureFile[0] != NULLCHAR && appData.liteBackTextureFile[0] != '*' ) {
\r
1072 liteBackTexture = LoadImage( 0, appData.liteBackTextureFile, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );
\r
1073 liteBackTextureMode = appData.liteBackTextureMode;
\r
1075 if (liteBackTexture == NULL && appData.debugMode) {
\r
1076 fprintf( debugFP, "Unable to load lite texture bitmap '%s'\n", appData.liteBackTextureFile );
\r
1080 if( appData.darkBackTextureFile && appData.darkBackTextureFile[0] != NULLCHAR && appData.darkBackTextureFile[0] != '*' ) {
\r
1081 darkBackTexture = LoadImage( 0, appData.darkBackTextureFile, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );
\r
1082 darkBackTextureMode = appData.darkBackTextureMode;
\r
1084 if (darkBackTexture == NULL && appData.debugMode) {
\r
1085 fprintf( debugFP, "Unable to load dark texture bitmap '%s'\n", appData.darkBackTextureFile );
\r
1089 mysrandom( (unsigned) time(NULL) );
\r
1091 /* [AS] Restore layout */
\r
1092 if( wpMoveHistory.visible ) {
\r
1093 MoveHistoryPopUp();
\r
1096 if( wpEvalGraph.visible ) {
\r
1100 if( wpEngineOutput.visible ) {
\r
1101 EngineOutputPopUp();
\r
1104 /* Make the window visible; update its client area; and return "success" */
\r
1105 EnsureOnScreen(&wpMain.x, &wpMain.y, minX, minY);
\r
1106 wp.length = sizeof(WINDOWPLACEMENT);
\r
1108 wp.showCmd = nCmdShow;
\r
1109 wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;
\r
1110 wp.rcNormalPosition.left = wpMain.x;
\r
1111 wp.rcNormalPosition.right = wpMain.x + wpMain.width;
\r
1112 wp.rcNormalPosition.top = wpMain.y;
\r
1113 wp.rcNormalPosition.bottom = wpMain.y + wpMain.height;
\r
1114 SetWindowPlacement(hwndMain, &wp);
\r
1116 InitBackEnd2(); // [HGM] moved until after all windows placed, to save correct position if fatal error on engine start
\r
1118 if(!appData.noGUI) SetWindowPos(hwndMain, alwaysOnTop ? HWND_TOPMOST : HWND_NOTOPMOST,
\r
1119 0, 0, 0, 0, SWP_NOMOVE|SWP_NOSIZE);
\r
1121 if (hwndConsole) {
\r
1123 SetWindowPos(hwndConsole, alwaysOnTop ? HWND_TOPMOST : HWND_NOTOPMOST,
\r
1124 0, 0, 0, 0, SWP_NOMOVE|SWP_NOSIZE);
\r
1126 ShowWindow(hwndConsole, nCmdShow);
\r
1127 if(appData.chatBoxes) { // [HGM] chat: open chat boxes
\r
1128 char buf[MSG_SIZ], *p = buf, *q;
\r
1129 safeStrCpy(buf, appData.chatBoxes, sizeof(buf)/sizeof(buf[0]) );
\r
1131 q = strchr(p, ';');
\r
1133 if(*p) ChatPopUp(p);
\r
1136 SetActiveWindow(hwndConsole);
\r
1138 if(!appData.noGUI) UpdateWindow(hwnd); else ShowWindow(hwnd, SW_MINIMIZE);
\r
1139 if(gameListDialog) SetFocus(gameListDialog); // [HGM] jaws: for if we clicked multi-game game file
\r
1148 HMENU hmenu = GetMenu(hwndMain);
\r
1150 (void) EnableMenuItem(hmenu, IDM_CommPort,
\r
1151 MF_BYCOMMAND|((appData.icsActive &&
\r
1152 *appData.icsCommPort != NULLCHAR) ?
\r
1153 MF_ENABLED : MF_GRAYED));
\r
1154 (void) CheckMenuItem(hmenu, IDM_SaveSettingsOnExit,
\r
1155 MF_BYCOMMAND|(saveSettingsOnExit ?
\r
1156 MF_CHECKED : MF_UNCHECKED));
\r
1159 //---------------------------------------------------------------------------------------------------------
\r
1161 #define ICS_TEXT_MENU_SIZE (IDM_CommandXLast - IDM_CommandX + 1)
\r
1162 #define XBOARD FALSE
\r
1164 #define OPTCHAR "/"
\r
1165 #define SEPCHAR "="
\r
1169 // front-end part of option handling
\r
1172 LFfromMFP(LOGFONT* lf, MyFontParams *mfp)
\r
1174 HDC hdc = CreateDC("DISPLAY", NULL, NULL, NULL);
\r
1175 lf->lfHeight = -(int)(mfp->pointSize * GetDeviceCaps(hdc, LOGPIXELSY) / 72.0 + 0.5);
\r
1178 lf->lfEscapement = 0;
\r
1179 lf->lfOrientation = 0;
\r
1180 lf->lfWeight = mfp->bold ? FW_BOLD : FW_NORMAL;
\r
1181 lf->lfItalic = mfp->italic;
\r
1182 lf->lfUnderline = mfp->underline;
\r
1183 lf->lfStrikeOut = mfp->strikeout;
\r
1184 lf->lfCharSet = mfp->charset;
\r
1185 lf->lfOutPrecision = OUT_DEFAULT_PRECIS;
\r
1186 lf->lfClipPrecision = CLIP_DEFAULT_PRECIS;
\r
1187 lf->lfQuality = DEFAULT_QUALITY;
\r
1188 lf->lfPitchAndFamily = DEFAULT_PITCH|FF_DONTCARE;
\r
1189 safeStrCpy(lf->lfFaceName, mfp->faceName, sizeof(lf->lfFaceName)/sizeof(lf->lfFaceName[0]) );
\r
1193 CreateFontInMF(MyFont *mf)
\r
1195 LFfromMFP(&mf->lf, &mf->mfp);
\r
1196 if (mf->hf) DeleteObject(mf->hf);
\r
1197 mf->hf = CreateFontIndirect(&mf->lf);
\r
1200 // [HGM] This platform-dependent table provides the location for storing the color info
\r
1202 colorVariable[] = {
\r
1203 &whitePieceColor,
\r
1204 &blackPieceColor,
\r
1205 &lightSquareColor,
\r
1206 &darkSquareColor,
\r
1207 &highlightSquareColor,
\r
1208 &premoveHighlightColor,
\r
1210 &consoleBackgroundColor,
\r
1211 &appData.fontForeColorWhite,
\r
1212 &appData.fontBackColorWhite,
\r
1213 &appData.fontForeColorBlack,
\r
1214 &appData.fontBackColorBlack,
\r
1215 &appData.evalHistColorWhite,
\r
1216 &appData.evalHistColorBlack,
\r
1217 &appData.highlightArrowColor,
\r
1220 /* Command line font name parser. NULL name means do nothing.
\r
1221 Syntax like "Courier New:10.0 bi" or "Arial:10" or "Arial:10b"
\r
1222 For backward compatibility, syntax without the colon is also
\r
1223 accepted, but font names with digits in them won't work in that case.
\r
1226 ParseFontName(char *name, MyFontParams *mfp)
\r
1229 if (name == NULL) return;
\r
1231 q = strchr(p, ':');
\r
1233 if (q - p >= sizeof(mfp->faceName))
\r
1234 ExitArgError(_("Font name too long:"), name, TRUE);
\r
1235 memcpy(mfp->faceName, p, q - p);
\r
1236 mfp->faceName[q - p] = NULLCHAR;
\r
1239 q = mfp->faceName;
\r
1240 while (*p && !isdigit(*p)) {
\r
1242 if (q - mfp->faceName >= sizeof(mfp->faceName))
\r
1243 ExitArgError(_("Font name too long:"), name, TRUE);
\r
1245 while (q > mfp->faceName && q[-1] == ' ') q--;
\r
1248 if (!*p) ExitArgError(_("Font point size missing:"), name, TRUE);
\r
1249 mfp->pointSize = (float) atof(p);
\r
1250 mfp->bold = (strchr(p, 'b') != NULL);
\r
1251 mfp->italic = (strchr(p, 'i') != NULL);
\r
1252 mfp->underline = (strchr(p, 'u') != NULL);
\r
1253 mfp->strikeout = (strchr(p, 's') != NULL);
\r
1254 mfp->charset = DEFAULT_CHARSET;
\r
1255 q = strchr(p, 'c');
\r
1257 mfp->charset = (BYTE) atoi(q+1);
\r
1261 ParseFont(char *name, int number)
\r
1262 { // wrapper to shield back-end from 'font'
\r
1263 ParseFontName(name, &font[boardSize][number]->mfp);
\r
1268 { // in WB we have a 2D array of fonts; this initializes their description
\r
1270 /* Point font array elements to structures and
\r
1271 parse default font names */
\r
1272 for (i=0; i<NUM_FONTS; i++) {
\r
1273 for (j=0; j<NUM_SIZES; j++) {
\r
1274 font[j][i] = &fontRec[j][i];
\r
1275 ParseFontName(font[j][i]->def, &font[j][i]->mfp);
\r
1282 { // here we create the actual fonts from the selected descriptions
\r
1284 for (i=0; i<NUM_FONTS; i++) {
\r
1285 for (j=0; j<NUM_SIZES; j++) {
\r
1286 CreateFontInMF(font[j][i]);
\r
1290 /* Color name parser.
\r
1291 X version accepts X color names, but this one
\r
1292 handles only the #rrggbb form (hex) or rrr,ggg,bbb (decimal) */
\r
1294 ParseColorName(char *name)
\r
1296 int red, green, blue, count;
\r
1297 char buf[MSG_SIZ];
\r
1299 count = sscanf(name, "#%2x%2x%2x", &red, &green, &blue);
\r
1301 count = sscanf(name, "%3d%*[^0-9]%3d%*[^0-9]%3d",
\r
1302 &red, &green, &blue);
\r
1305 snprintf(buf, MSG_SIZ, _("Can't parse color name %s"), name);
\r
1306 DisplayError(buf, 0);
\r
1307 return RGB(0, 0, 0);
\r
1309 return PALETTERGB(red, green, blue);
\r
1313 ParseColor(int n, char *name)
\r
1314 { // for WinBoard the color is an int, which needs to be derived from the string
\r
1315 if(colorVariable[n]) *(int*)colorVariable[n] = ParseColorName(name);
\r
1319 ParseAttribs(COLORREF *color, int *effects, char* argValue)
\r
1321 char *e = argValue;
\r
1325 if (*e == 'b') eff |= CFE_BOLD;
\r
1326 else if (*e == 'i') eff |= CFE_ITALIC;
\r
1327 else if (*e == 'u') eff |= CFE_UNDERLINE;
\r
1328 else if (*e == 's') eff |= CFE_STRIKEOUT;
\r
1329 else if (*e == '#' || isdigit(*e)) break;
\r
1333 *color = ParseColorName(e);
\r
1337 ParseTextAttribs(ColorClass cc, char *s)
\r
1338 { // [HGM] front-end wrapper that does the platform-dependent call
\r
1339 // for XBoard we would set (&appData.colorShout)[cc] = strdup(s);
\r
1340 ParseAttribs(&textAttribs[cc].color, &textAttribs[cc].effects, s);
\r
1344 ParseBoardSize(void *addr, char *name)
\r
1345 { // [HGM] rewritten with return-value ptr to shield back-end from BoardSize
\r
1346 BoardSize bs = SizeTiny;
\r
1347 while (sizeInfo[bs].name != NULL) {
\r
1348 if (StrCaseCmp(name, sizeInfo[bs].name) == 0) {
\r
1349 *(BoardSize *)addr = bs;
\r
1354 ExitArgError(_("Unrecognized board size value"), name, TRUE);
\r
1359 { // [HGM] import name from appData first
\r
1362 for (cc = (ColorClass)0; cc < ColorNormal; cc++) {
\r
1363 textAttribs[cc].sound.name = strdup((&appData.soundShout)[cc]);
\r
1364 textAttribs[cc].sound.data = NULL;
\r
1365 MyLoadSound(&textAttribs[cc].sound);
\r
1367 for (cc = ColorNormal; cc < NColorClasses; cc++) {
\r
1368 textAttribs[cc].sound.name = strdup("");
\r
1369 textAttribs[cc].sound.data = NULL;
\r
1371 for (sc = (SoundClass)0; sc < NSoundClasses; sc++) {
\r
1372 sounds[sc].name = strdup((&appData.soundMove)[sc]);
\r
1373 sounds[sc].data = NULL;
\r
1374 MyLoadSound(&sounds[sc]);
\r
1379 SetCommPortDefaults()
\r
1381 memset(&dcb, 0, sizeof(DCB)); // required by VS 2002 +
\r
1382 dcb.DCBlength = sizeof(DCB);
\r
1383 dcb.BaudRate = 9600;
\r
1384 dcb.fBinary = TRUE;
\r
1385 dcb.fParity = FALSE;
\r
1386 dcb.fOutxCtsFlow = FALSE;
\r
1387 dcb.fOutxDsrFlow = FALSE;
\r
1388 dcb.fDtrControl = DTR_CONTROL_ENABLE;
\r
1389 dcb.fDsrSensitivity = FALSE;
\r
1390 dcb.fTXContinueOnXoff = TRUE;
\r
1391 dcb.fOutX = FALSE;
\r
1393 dcb.fNull = FALSE;
\r
1394 dcb.fRtsControl = RTS_CONTROL_ENABLE;
\r
1395 dcb.fAbortOnError = FALSE;
\r
1397 dcb.Parity = SPACEPARITY;
\r
1398 dcb.StopBits = ONESTOPBIT;
\r
1401 // [HGM] args: these three cases taken out to stay in front-end
\r
1403 SaveFontArg(FILE *f, ArgDescriptor *ad)
\r
1404 { // in WinBoard every board size has its own font, and the "argLoc" identifies the table,
\r
1405 // while the curent board size determines the element. This system should be ported to XBoard.
\r
1406 // What the table contains pointers to, and how to print the font description, remains platform-dependent
\r
1408 for (bs=0; bs<NUM_SIZES; bs++) {
\r
1409 MyFontParams *mfp = &font[bs][(int) ad->argLoc]->mfp;
\r
1410 fprintf(f, "/size=%s ", sizeInfo[bs].name);
\r
1411 fprintf(f, "/%s=\"%s:%g%s%s%s%s%sc%d\"\n",
\r
1412 ad->argName, mfp->faceName, mfp->pointSize,
\r
1413 mfp->bold || mfp->italic || mfp->underline || mfp->strikeout ? " " : "",
\r
1414 mfp->bold ? "b" : "",
\r
1415 mfp->italic ? "i" : "",
\r
1416 mfp->underline ? "u" : "",
\r
1417 mfp->strikeout ? "s" : "",
\r
1418 (int)mfp->charset);
\r
1424 { // [HGM] copy the names from the internal WB variables to appData
\r
1427 for (cc = (ColorClass)0; cc < ColorNormal; cc++)
\r
1428 (&appData.soundShout)[cc] = textAttribs[cc].sound.name;
\r
1429 for (sc = (SoundClass)0; sc < NSoundClasses; sc++)
\r
1430 (&appData.soundMove)[sc] = sounds[sc].name;
\r
1434 SaveAttribsArg(FILE *f, ArgDescriptor *ad)
\r
1435 { // here the "argLoc" defines a table index. It could have contained the 'ta' pointer itself, though
\r
1436 MyTextAttribs* ta = &textAttribs[(ColorClass)ad->argLoc];
\r
1437 fprintf(f, "/%s=\"%s%s%s%s%s#%02lx%02lx%02lx\"\n", ad->argName,
\r
1438 (ta->effects & CFE_BOLD) ? "b" : "",
\r
1439 (ta->effects & CFE_ITALIC) ? "i" : "",
\r
1440 (ta->effects & CFE_UNDERLINE) ? "u" : "",
\r
1441 (ta->effects & CFE_STRIKEOUT) ? "s" : "",
\r
1442 (ta->effects) ? " " : "",
\r
1443 ta->color&0xff, (ta->color >> 8)&0xff, (ta->color >> 16)&0xff);
\r
1447 SaveColor(FILE *f, ArgDescriptor *ad)
\r
1448 { // in WinBoard the color is an int and has to be converted to text. In X it would be a string already?
\r
1449 COLORREF color = *(COLORREF *)colorVariable[(int)ad->argLoc];
\r
1450 fprintf(f, "/%s=#%02lx%02lx%02lx\n", ad->argName,
\r
1451 color&0xff, (color>>8)&0xff, (color>>16)&0xff);
\r
1455 SaveBoardSize(FILE *f, char *name, void *addr)
\r
1456 { // wrapper to shield back-end from BoardSize & sizeInfo
\r
1457 fprintf(f, "/%s=%s\n", name, sizeInfo[*(BoardSize *)addr].name);
\r
1461 ParseCommPortSettings(char *s)
\r
1462 { // wrapper to keep dcb from back-end
\r
1463 ParseCommSettings(s, &dcb);
\r
1468 { // wrapper to shield use of window handles from back-end (make addressible by number?)
\r
1469 GetActualPlacement(hwndMain, &wpMain);
\r
1470 GetActualPlacement(hwndConsole, &wpConsole);
\r
1471 GetActualPlacement(commentDialog, &wpComment);
\r
1472 GetActualPlacement(editTagsDialog, &wpTags);
\r
1473 GetActualPlacement(gameListDialog, &wpGameList);
\r
1474 GetActualPlacement(moveHistoryDialog, &wpMoveHistory);
\r
1475 GetActualPlacement(evalGraphDialog, &wpEvalGraph);
\r
1476 GetActualPlacement(engineOutputDialog, &wpEngineOutput);
\r
1480 PrintCommPortSettings(FILE *f, char *name)
\r
1481 { // wrapper to shield back-end from DCB
\r
1482 PrintCommSettings(f, name, &dcb);
\r
1486 MySearchPath(char *installDir, char *name, char *fullname)
\r
1488 char *dummy, buf[MSG_SIZ], *p = name, *q;
\r
1489 if(name[0]== '%') {
\r
1490 fullname[0] = 0; // [HGM] first expand any environment variables in the given name
\r
1491 while(*p == '%' && (q = strchr(p+1, '%'))) { // [HGM] recognize %*% as environment variable
\r
1492 safeStrCpy(buf, p+1, sizeof(buf)/sizeof(buf[0]) );
\r
1493 *strchr(buf, '%') = 0;
\r
1494 strcat(fullname, getenv(buf));
\r
1495 p = q+1; while(*p == '\\') { strcat(fullname, "\\"); p++; }
\r
1497 strcat(fullname, p); // after environment variables (if any), take the remainder of the given name
\r
1498 if(appData.debugMode) fprintf(debugFP, "name = '%s', expanded name = '%s'\n", name, fullname);
\r
1499 return (int) strlen(fullname);
\r
1501 return (int) SearchPath(installDir, name, NULL, MSG_SIZ, fullname, &dummy);
\r
1505 MyGetFullPathName(char *name, char *fullname)
\r
1508 return (int) GetFullPathName(name, MSG_SIZ, fullname, &dummy);
\r
1513 { // [HGM] args: allows testing if main window is realized from back-end
\r
1514 return hwndMain != NULL;
\r
1518 PopUpStartupDialog()
\r
1522 LoadLanguageFile(appData.language);
\r
1523 lpProc = MakeProcInstance((FARPROC)StartupDialog, hInst);
\r
1524 DialogBox(hInst, MAKEINTRESOURCE(DLG_Startup), NULL, (DLGPROC)lpProc);
\r
1525 FreeProcInstance(lpProc);
\r
1528 /*---------------------------------------------------------------------------*\
\r
1530 * GDI board drawing routines
\r
1532 \*---------------------------------------------------------------------------*/
\r
1534 /* [AS] Draw square using background texture */
\r
1535 static void DrawTile( int dx, int dy, int dw, int dh, HDC dst, HDC src, int mode, int sx, int sy )
\r
1540 return; /* Should never happen! */
\r
1543 SetGraphicsMode( dst, GM_ADVANCED );
\r
1550 /* X reflection */
\r
1555 x.eDx = (FLOAT) dw + dx - 1;
\r
1558 SetWorldTransform( dst, &x );
\r
1561 /* Y reflection */
\r
1567 x.eDy = (FLOAT) dh + dy - 1;
\r
1569 SetWorldTransform( dst, &x );
\r
1577 x.eDx = (FLOAT) dx;
\r
1578 x.eDy = (FLOAT) dy;
\r
1581 SetWorldTransform( dst, &x );
\r
1585 BitBlt( dst, dx, dy, dw, dh, src, sx, sy, SRCCOPY );
\r
1593 SetWorldTransform( dst, &x );
\r
1595 ModifyWorldTransform( dst, 0, MWT_IDENTITY );
\r
1598 /* [AS] [HGM] Make room for more piece types, so all pieces can be different */
\r
1600 PM_WP = (int) WhitePawn,
\r
1601 PM_WN = (int) WhiteKnight,
\r
1602 PM_WB = (int) WhiteBishop,
\r
1603 PM_WR = (int) WhiteRook,
\r
1604 PM_WQ = (int) WhiteQueen,
\r
1605 PM_WF = (int) WhiteFerz,
\r
1606 PM_WW = (int) WhiteWazir,
\r
1607 PM_WE = (int) WhiteAlfil,
\r
1608 PM_WM = (int) WhiteMan,
\r
1609 PM_WO = (int) WhiteCannon,
\r
1610 PM_WU = (int) WhiteUnicorn,
\r
1611 PM_WH = (int) WhiteNightrider,
\r
1612 PM_WA = (int) WhiteAngel,
\r
1613 PM_WC = (int) WhiteMarshall,
\r
1614 PM_WAB = (int) WhiteCardinal,
\r
1615 PM_WD = (int) WhiteDragon,
\r
1616 PM_WL = (int) WhiteLance,
\r
1617 PM_WS = (int) WhiteCobra,
\r
1618 PM_WV = (int) WhiteFalcon,
\r
1619 PM_WSG = (int) WhiteSilver,
\r
1620 PM_WG = (int) WhiteGrasshopper,
\r
1621 PM_WK = (int) WhiteKing,
\r
1622 PM_BP = (int) BlackPawn,
\r
1623 PM_BN = (int) BlackKnight,
\r
1624 PM_BB = (int) BlackBishop,
\r
1625 PM_BR = (int) BlackRook,
\r
1626 PM_BQ = (int) BlackQueen,
\r
1627 PM_BF = (int) BlackFerz,
\r
1628 PM_BW = (int) BlackWazir,
\r
1629 PM_BE = (int) BlackAlfil,
\r
1630 PM_BM = (int) BlackMan,
\r
1631 PM_BO = (int) BlackCannon,
\r
1632 PM_BU = (int) BlackUnicorn,
\r
1633 PM_BH = (int) BlackNightrider,
\r
1634 PM_BA = (int) BlackAngel,
\r
1635 PM_BC = (int) BlackMarshall,
\r
1636 PM_BG = (int) BlackGrasshopper,
\r
1637 PM_BAB = (int) BlackCardinal,
\r
1638 PM_BD = (int) BlackDragon,
\r
1639 PM_BL = (int) BlackLance,
\r
1640 PM_BS = (int) BlackCobra,
\r
1641 PM_BV = (int) BlackFalcon,
\r
1642 PM_BSG = (int) BlackSilver,
\r
1643 PM_BK = (int) BlackKing
\r
1646 static HFONT hPieceFont = NULL;
\r
1647 static HBITMAP hPieceMask[(int) EmptySquare];
\r
1648 static HBITMAP hPieceFace[(int) EmptySquare];
\r
1649 static int fontBitmapSquareSize = 0;
\r
1650 static char pieceToFontChar[(int) EmptySquare] =
\r
1651 { 'p', 'n', 'b', 'r', 'q',
\r
1652 'n', 'b', 'p', 'n', 'b', 'r', 'b', 'r', 'q', 'k',
\r
1653 'k', 'o', 'm', 'v', 't', 'w',
\r
1654 'v', 't', 'o', 'm', 'v', 't', 'v', 't', 'w', 'l',
\r
1657 extern BOOL SetCharTable( char *table, const char * map );
\r
1658 /* [HGM] moved to backend.c */
\r
1660 static void SetPieceBackground( HDC hdc, COLORREF color, int mode )
\r
1663 BYTE r1 = GetRValue( color );
\r
1664 BYTE g1 = GetGValue( color );
\r
1665 BYTE b1 = GetBValue( color );
\r
1671 /* Create a uniform background first */
\r
1672 hbrush = CreateSolidBrush( color );
\r
1673 SetRect( &rc, 0, 0, squareSize, squareSize );
\r
1674 FillRect( hdc, &rc, hbrush );
\r
1675 DeleteObject( hbrush );
\r
1678 /* Vertical gradient, good for pawn, knight and rook, less for queen and king */
\r
1679 int steps = squareSize / 2;
\r
1682 for( i=0; i<steps; i++ ) {
\r
1683 BYTE r = r1 - (r1-r2) * i / steps;
\r
1684 BYTE g = g1 - (g1-g2) * i / steps;
\r
1685 BYTE b = b1 - (b1-b2) * i / steps;
\r
1687 hbrush = CreateSolidBrush( RGB(r,g,b) );
\r
1688 SetRect( &rc, i + squareSize - steps, 0, i + squareSize - steps + 1, squareSize );
\r
1689 FillRect( hdc, &rc, hbrush );
\r
1690 DeleteObject(hbrush);
\r
1693 else if( mode == 2 ) {
\r
1694 /* Diagonal gradient, good more or less for every piece */
\r
1695 POINT triangle[3];
\r
1696 HPEN hpen = SelectObject( hdc, GetStockObject(NULL_PEN) );
\r
1697 HBRUSH hbrush_old;
\r
1698 int steps = squareSize;
\r
1701 triangle[0].x = squareSize - steps;
\r
1702 triangle[0].y = squareSize;
\r
1703 triangle[1].x = squareSize;
\r
1704 triangle[1].y = squareSize;
\r
1705 triangle[2].x = squareSize;
\r
1706 triangle[2].y = squareSize - steps;
\r
1708 for( i=0; i<steps; i++ ) {
\r
1709 BYTE r = r1 - (r1-r2) * i / steps;
\r
1710 BYTE g = g1 - (g1-g2) * i / steps;
\r
1711 BYTE b = b1 - (b1-b2) * i / steps;
\r
1713 hbrush = CreateSolidBrush( RGB(r,g,b) );
\r
1714 hbrush_old = SelectObject( hdc, hbrush );
\r
1715 Polygon( hdc, triangle, 3 );
\r
1716 SelectObject( hdc, hbrush_old );
\r
1717 DeleteObject(hbrush);
\r
1722 SelectObject( hdc, hpen );
\r
1727 [AS] The method I use to create the bitmaps it a bit tricky, but it
\r
1728 seems to work ok. The main problem here is to find the "inside" of a chess
\r
1729 piece: follow the steps as explained below.
\r
1731 static void CreatePieceMaskFromFont( HDC hdc_window, HDC hdc, int index )
\r
1735 COLORREF chroma = RGB(0xFF,0x00,0xFF);
\r
1739 int backColor = whitePieceColor;
\r
1740 int foreColor = blackPieceColor;
\r
1742 if( index < (int)BlackPawn && appData.fontBackColorWhite != appData.fontForeColorWhite ) {
\r
1743 backColor = appData.fontBackColorWhite;
\r
1744 foreColor = appData.fontForeColorWhite;
\r
1746 else if( index >= (int)BlackPawn && appData.fontBackColorBlack != appData.fontForeColorBlack ) {
\r
1747 backColor = appData.fontBackColorBlack;
\r
1748 foreColor = appData.fontForeColorBlack;
\r
1752 hbm = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );
\r
1754 hbm_old = SelectObject( hdc, hbm );
\r
1758 rc.right = squareSize;
\r
1759 rc.bottom = squareSize;
\r
1761 /* Step 1: background is now black */
\r
1762 FillRect( hdc, &rc, GetStockObject(BLACK_BRUSH) );
\r
1764 GetTextExtentPoint32( hdc, &pieceToFontChar[index], 1, &sz );
\r
1766 pt.x = (squareSize - sz.cx) / 2;
\r
1767 pt.y = (squareSize - sz.cy) / 2;
\r
1769 SetBkMode( hdc, TRANSPARENT );
\r
1770 SetTextColor( hdc, chroma );
\r
1771 /* Step 2: the piece has been drawn in purple, there are now black and purple in this bitmap */
\r
1772 TextOut( hdc, pt.x, pt.y, &pieceToFontChar[appData.allWhite && index >= (int)BlackPawn ? index - (int)BlackPawn : index], 1 );
\r
1774 SelectObject( hdc, GetStockObject(WHITE_BRUSH) );
\r
1775 /* Step 3: the area outside the piece is filled with white */
\r
1776 // FloodFill( hdc, 0, 0, chroma );
\r
1777 ExtFloodFill( hdc, 0, 0, 0, FLOODFILLSURFACE );
\r
1778 ExtFloodFill( hdc, 0, squareSize-1, 0, FLOODFILLSURFACE ); // [HGM] fill from all 4 corners, for if piece too big
\r
1779 ExtFloodFill( hdc, squareSize-1, 0, 0, FLOODFILLSURFACE );
\r
1780 ExtFloodFill( hdc, squareSize-1, squareSize-1, 0, FLOODFILLSURFACE );
\r
1781 SelectObject( hdc, GetStockObject(BLACK_BRUSH) );
\r
1783 Step 4: this is the tricky part, the area inside the piece is filled with black,
\r
1784 but if the start point is not inside the piece we're lost!
\r
1785 There should be a better way to do this... if we could create a region or path
\r
1786 from the fill operation we would be fine for example.
\r
1788 // FloodFill( hdc, squareSize / 2, squareSize / 2, RGB(0xFF,0xFF,0xFF) );
\r
1789 ExtFloodFill( hdc, squareSize / 2, squareSize / 2, RGB(0xFF,0xFF,0xFF), FLOODFILLBORDER );
\r
1791 { /* [HGM] shave off edges of mask, in an attempt to correct for the fact that FloodFill does not work correctly under Win XP */
\r
1792 HDC dc2 = CreateCompatibleDC( hdc_window );
\r
1793 HBITMAP bm2 = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );
\r
1795 SelectObject( dc2, bm2 );
\r
1796 BitBlt( dc2, 0, 0, squareSize, squareSize, hdc, 0, 0, SRCCOPY ); // make copy
\r
1797 BitBlt( hdc, 0, 1, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );
\r
1798 BitBlt( hdc, 2, 1, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );
\r
1799 BitBlt( hdc, 1, 0, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );
\r
1800 BitBlt( hdc, 1, 2, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );
\r
1803 DeleteObject( bm2 );
\r
1806 SetTextColor( hdc, 0 );
\r
1808 Step 5: some fonts have "disconnected" areas that are skipped by the fill:
\r
1809 draw the piece again in black for safety.
\r
1811 TextOut( hdc, pt.x, pt.y, &pieceToFontChar[appData.allWhite && index >= (int)BlackPawn ? index - (int)BlackPawn : index], 1 );
\r
1813 SelectObject( hdc, hbm_old );
\r
1815 if( hPieceMask[index] != NULL ) {
\r
1816 DeleteObject( hPieceMask[index] );
\r
1819 hPieceMask[index] = hbm;
\r
1822 hbm = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );
\r
1824 SelectObject( hdc, hbm );
\r
1827 HDC dc1 = CreateCompatibleDC( hdc_window );
\r
1828 HDC dc2 = CreateCompatibleDC( hdc_window );
\r
1829 HBITMAP bm2 = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );
\r
1831 SelectObject( dc1, hPieceMask[index] );
\r
1832 SelectObject( dc2, bm2 );
\r
1833 FillRect( dc2, &rc, GetStockObject(WHITE_BRUSH) );
\r
1834 BitBlt( dc2, 0, 0, squareSize, squareSize, dc1, 0, 0, SRCINVERT );
\r
1837 Now dc2 contains the inverse of the piece mask, i.e. a mask that preserves
\r
1838 the piece background and deletes (makes transparent) the rest.
\r
1839 Thanks to that mask, we are free to paint the background with the greates
\r
1840 freedom, as we'll be able to mask off the unwanted parts when finished.
\r
1841 We use this, to make gradients and give the pieces a "roundish" look.
\r
1843 SetPieceBackground( hdc, backColor, 2 );
\r
1844 BitBlt( hdc, 0, 0, squareSize, squareSize, dc2, 0, 0, SRCAND );
\r
1848 DeleteObject( bm2 );
\r
1851 SetTextColor( hdc, foreColor );
\r
1852 TextOut( hdc, pt.x, pt.y, &pieceToFontChar[appData.allWhite && index >= (int)BlackPawn ? index - (int)BlackPawn : index], 1 );
\r
1854 SelectObject( hdc, hbm_old );
\r
1856 if( hPieceFace[index] != NULL ) {
\r
1857 DeleteObject( hPieceFace[index] );
\r
1860 hPieceFace[index] = hbm;
\r
1863 static int TranslatePieceToFontPiece( int piece )
\r
1893 case BlackMarshall:
\r
1897 case BlackNightrider:
\r
1903 case BlackUnicorn:
\r
1907 case BlackGrasshopper:
\r
1919 case BlackCardinal:
\r
1926 case WhiteMarshall:
\r
1930 case WhiteNightrider:
\r
1936 case WhiteUnicorn:
\r
1940 case WhiteGrasshopper:
\r
1952 case WhiteCardinal:
\r
1961 void CreatePiecesFromFont()
\r
1964 HDC hdc_window = NULL;
\r
1970 if( fontBitmapSquareSize < 0 ) {
\r
1971 /* Something went seriously wrong in the past: do not try to recreate fonts! */
\r
1975 if( appData.renderPiecesWithFont == NULL || appData.renderPiecesWithFont[0] == NULLCHAR || appData.renderPiecesWithFont[0] == '*' ) {
\r
1976 fontBitmapSquareSize = -1;
\r
1980 if( fontBitmapSquareSize != squareSize ) {
\r
1981 hdc_window = GetDC( hwndMain );
\r
1982 hdc = CreateCompatibleDC( hdc_window );
\r
1984 if( hPieceFont != NULL ) {
\r
1985 DeleteObject( hPieceFont );
\r
1988 for( i=0; i<=(int)BlackKing; i++ ) {
\r
1989 hPieceMask[i] = NULL;
\r
1990 hPieceFace[i] = NULL;
\r
1996 if( appData.fontPieceSize >= 50 && appData.fontPieceSize <= 150 ) {
\r
1997 fontHeight = appData.fontPieceSize;
\r
2000 fontHeight = (fontHeight * squareSize) / 100;
\r
2002 lf.lfHeight = -MulDiv( fontHeight, GetDeviceCaps(hdc, LOGPIXELSY), 72 );
\r
2004 lf.lfEscapement = 0;
\r
2005 lf.lfOrientation = 0;
\r
2006 lf.lfWeight = FW_NORMAL;
\r
2008 lf.lfUnderline = 0;
\r
2009 lf.lfStrikeOut = 0;
\r
2010 lf.lfCharSet = DEFAULT_CHARSET;
\r
2011 lf.lfOutPrecision = OUT_DEFAULT_PRECIS;
\r
2012 lf.lfClipPrecision = CLIP_DEFAULT_PRECIS;
\r
2013 lf.lfQuality = PROOF_QUALITY;
\r
2014 lf.lfPitchAndFamily = DEFAULT_PITCH | FF_DONTCARE;
\r
2015 strncpy( lf.lfFaceName, appData.renderPiecesWithFont, sizeof(lf.lfFaceName) );
\r
2016 lf.lfFaceName[ sizeof(lf.lfFaceName) - 1 ] = '\0';
\r
2018 hPieceFont = CreateFontIndirect( &lf );
\r
2020 if( hPieceFont == NULL ) {
\r
2021 fontBitmapSquareSize = -2;
\r
2024 /* Setup font-to-piece character table */
\r
2025 if( ! SetCharTable(pieceToFontChar, appData.fontToPieceTable) ) {
\r
2026 /* No (or wrong) global settings, try to detect the font */
\r
2027 if( strstr(lf.lfFaceName,"Alpha") != NULL ) {
\r
2029 SetCharTable(pieceToFontChar, "phbrqkojntwl");
\r
2031 else if( strstr(lf.lfFaceName,"DiagramTT") != NULL ) {
\r
2032 /* DiagramTT* family */
\r
2033 SetCharTable(pieceToFontChar, "PNLRQKpnlrqk");
\r
2035 else if( strstr(lf.lfFaceName,"WinboardF") != NULL ) {
\r
2036 /* Fairy symbols */
\r
2037 SetCharTable(pieceToFontChar, "PNBRQFEACWMOHIJGDVSLUKpnbrqfeacwmohijgdvsluk");
\r
2039 else if( strstr(lf.lfFaceName,"GC2004D") != NULL ) {
\r
2040 /* Good Companion (Some characters get warped as literal :-( */
\r
2041 char s[] = "1cmWG0??S??oYI23wgQU";
\r
2042 s[0]=0xB9; s[1]=0xA9; s[6]=0xB1; s[11]=0xBB; s[12]=0xAB; s[17]=0xB3;
\r
2043 SetCharTable(pieceToFontChar, s);
\r
2046 /* Cases, Condal, Leipzig, Lucena, Marroquin, Merida, Usual */
\r
2047 SetCharTable(pieceToFontChar, "pnbrqkomvtwl");
\r
2051 /* Create bitmaps */
\r
2052 hfont_old = SelectObject( hdc, hPieceFont );
\r
2053 for(i=(int)WhitePawn; i<(int)EmptySquare; i++) /* [HGM] made a loop for this */
\r
2054 if(PieceToChar((ChessSquare)i) != '.') /* skip unused pieces */
\r
2055 CreatePieceMaskFromFont( hdc_window, hdc, i );
\r
2057 SelectObject( hdc, hfont_old );
\r
2059 fontBitmapSquareSize = squareSize;
\r
2063 if( hdc != NULL ) {
\r
2067 if( hdc_window != NULL ) {
\r
2068 ReleaseDC( hwndMain, hdc_window );
\r
2073 DoLoadBitmap(HINSTANCE hinst, char *piece, int squareSize, char *suffix)
\r
2077 snprintf(name, sizeof(name)/sizeof(name[0]), "%s%d%s", piece, squareSize, suffix);
\r
2078 if (gameInfo.event &&
\r
2079 strcmp(gameInfo.event, "Easter Egg Hunt") == 0 &&
\r
2080 strcmp(name, "k80s") == 0) {
\r
2081 safeStrCpy(name, "tim", sizeof(name)/sizeof(name[0]) );
\r
2083 return LoadBitmap(hinst, name);
\r
2087 /* Insert a color into the program's logical palette
\r
2088 structure. This code assumes the given color is
\r
2089 the result of the RGB or PALETTERGB macro, and it
\r
2090 knows how those macros work (which is documented).
\r
2093 InsertInPalette(COLORREF color)
\r
2095 LPPALETTEENTRY pe = &(pLogPal->palPalEntry[pLogPal->palNumEntries]);
\r
2097 if (pLogPal->palNumEntries++ >= PALETTESIZE) {
\r
2098 DisplayFatalError(_("Too many colors"), 0, 1);
\r
2099 pLogPal->palNumEntries--;
\r
2103 pe->peFlags = (char) 0;
\r
2104 pe->peRed = (char) (0xFF & color);
\r
2105 pe->peGreen = (char) (0xFF & (color >> 8));
\r
2106 pe->peBlue = (char) (0xFF & (color >> 16));
\r
2112 InitDrawingColors()
\r
2114 if (pLogPal == NULL) {
\r
2115 /* Allocate enough memory for a logical palette with
\r
2116 * PALETTESIZE entries and set the size and version fields
\r
2117 * of the logical palette structure.
\r
2119 pLogPal = (NPLOGPALETTE)
\r
2120 LocalAlloc(LMEM_FIXED, (sizeof(LOGPALETTE) +
\r
2121 (sizeof(PALETTEENTRY) * (PALETTESIZE))));
\r
2122 pLogPal->palVersion = 0x300;
\r
2124 pLogPal->palNumEntries = 0;
\r
2126 InsertInPalette(lightSquareColor);
\r
2127 InsertInPalette(darkSquareColor);
\r
2128 InsertInPalette(whitePieceColor);
\r
2129 InsertInPalette(blackPieceColor);
\r
2130 InsertInPalette(highlightSquareColor);
\r
2131 InsertInPalette(premoveHighlightColor);
\r
2133 /* create a logical color palette according the information
\r
2134 * in the LOGPALETTE structure.
\r
2136 hPal = CreatePalette((LPLOGPALETTE) pLogPal);
\r
2138 lightSquareBrush = CreateSolidBrush(lightSquareColor);
\r
2139 blackSquareBrush = CreateSolidBrush(blackPieceColor);
\r
2140 darkSquareBrush = CreateSolidBrush(darkSquareColor);
\r
2141 whitePieceBrush = CreateSolidBrush(whitePieceColor);
\r
2142 blackPieceBrush = CreateSolidBrush(blackPieceColor);
\r
2143 iconBkgndBrush = CreateSolidBrush(GetSysColor(COLOR_BACKGROUND));
\r
2144 explodeBrush = CreateSolidBrush(highlightSquareColor); // [HGM] atomic
\r
2145 markerBrush = CreateSolidBrush(premoveHighlightColor); // [HGM] markers
\r
2146 /* [AS] Force rendering of the font-based pieces */
\r
2147 if( fontBitmapSquareSize > 0 ) {
\r
2148 fontBitmapSquareSize = 0;
\r
2154 BoardWidth(int boardSize, int n)
\r
2155 { /* [HGM] argument n added to allow different width and height */
\r
2156 int lineGap = sizeInfo[boardSize].lineGap;
\r
2158 if( appData.overrideLineGap >= 0 && appData.overrideLineGap <= 5 ) {
\r
2159 lineGap = appData.overrideLineGap;
\r
2162 return (n + 1) * lineGap +
\r
2163 n * sizeInfo[boardSize].squareSize;
\r
2166 /* Respond to board resize by dragging edge */
\r
2168 ResizeBoard(int newSizeX, int newSizeY, int flags)
\r
2170 BoardSize newSize = NUM_SIZES - 1;
\r
2171 static int recurse = 0;
\r
2172 if (IsIconic(hwndMain)) return;
\r
2173 if (recurse > 0) return;
\r
2175 while (newSize > 0) {
\r
2176 InitDrawingSizes(newSize+1000, 0); // [HGM] kludge to update sizeInfo without visible effects
\r
2177 if(newSizeX >= sizeInfo[newSize].cliWidth &&
\r
2178 newSizeY >= sizeInfo[newSize].cliHeight) break;
\r
2181 boardSize = newSize;
\r
2182 InitDrawingSizes(boardSize, flags);
\r
2187 extern Boolean twoBoards, partnerUp; // [HGM] dual
\r
2190 InitDrawingSizes(BoardSize boardSize, int flags)
\r
2192 int i, boardWidth, boardHeight; /* [HGM] height treated separately */
\r
2193 ChessSquare piece;
\r
2194 static int oldBoardSize = -1, oldTinyLayout = 0;
\r
2196 SIZE clockSize, messageSize;
\r
2198 char buf[MSG_SIZ];
\r
2200 HMENU hmenu = GetMenu(hwndMain);
\r
2201 RECT crect, wrect, oldRect;
\r
2203 LOGBRUSH logbrush;
\r
2205 int suppressVisibleEffects = 0; // [HGM] kludge to request updating sizeInfo only
\r
2206 if((int)boardSize >= 1000 ) { boardSize -= 1000; suppressVisibleEffects = 1; }
\r
2208 /* [HGM] call with -2 uses old size (for if nr of files, ranks changes) */
\r
2209 if(boardSize == (BoardSize)(-2) ) boardSize = oldBoardSize;
\r
2211 oldRect.left = wpMain.x; //[HGM] placement: remember previous window params
\r
2212 oldRect.top = wpMain.y;
\r
2213 oldRect.right = wpMain.x + wpMain.width;
\r
2214 oldRect.bottom = wpMain.y + wpMain.height;
\r
2216 tinyLayout = sizeInfo[boardSize].tinyLayout;
\r
2217 smallLayout = sizeInfo[boardSize].smallLayout;
\r
2218 squareSize = sizeInfo[boardSize].squareSize;
\r
2219 lineGap = sizeInfo[boardSize].lineGap;
\r
2220 minorSize = 0; /* [HGM] Kludge to see if demagnified pieces need to be shifted */
\r
2222 if( appData.overrideLineGap >= 0 && appData.overrideLineGap <= 5 ) {
\r
2223 lineGap = appData.overrideLineGap;
\r
2226 if (tinyLayout != oldTinyLayout) {
\r
2227 long style = GetWindowLongPtr(hwndMain, GWL_STYLE);
\r
2229 style &= ~WS_SYSMENU;
\r
2230 InsertMenu(hmenu, IDM_Exit, MF_BYCOMMAND, IDM_Minimize,
\r
2231 "&Minimize\tCtrl+F4");
\r
2233 style |= WS_SYSMENU;
\r
2234 RemoveMenu(hmenu, IDM_Minimize, MF_BYCOMMAND);
\r
2236 SetWindowLongPtr(hwndMain, GWL_STYLE, style);
\r
2238 for (i=0; menuBarText[tinyLayout][i]; i++) {
\r
2239 ModifyMenu(hmenu, i, MF_STRING|MF_BYPOSITION|MF_POPUP,
\r
2240 (UINT)GetSubMenu(hmenu, i), T_(menuBarText[tinyLayout][i]));
\r
2242 DrawMenuBar(hwndMain);
\r
2245 boardWidth = BoardWidth(boardSize, BOARD_WIDTH);
\r
2246 boardHeight = BoardWidth(boardSize, BOARD_HEIGHT);
\r
2248 /* Get text area sizes */
\r
2249 hdc = GetDC(hwndMain);
\r
2250 if (appData.clockMode) {
\r
2251 snprintf(buf, MSG_SIZ, _("White: %s"), TimeString(23*60*60*1000L));
\r
2253 snprintf(buf, MSG_SIZ, _("White"));
\r
2255 oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);
\r
2256 GetTextExtentPoint(hdc, buf, strlen(buf), &clockSize);
\r
2257 SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);
\r
2258 str = _("We only care about the height here");
\r
2259 GetTextExtentPoint(hdc, str, strlen(str), &messageSize);
\r
2260 SelectObject(hdc, oldFont);
\r
2261 ReleaseDC(hwndMain, hdc);
\r
2263 /* Compute where everything goes */
\r
2264 if((first.programLogo || second.programLogo) && !tinyLayout) {
\r
2265 /* [HGM] logo: if either logo is on, reserve space for it */
\r
2266 logoHeight = 2*clockSize.cy;
\r
2267 leftLogoRect.left = OUTER_MARGIN;
\r
2268 leftLogoRect.right = leftLogoRect.left + 4*clockSize.cy;
\r
2269 leftLogoRect.top = OUTER_MARGIN;
\r
2270 leftLogoRect.bottom = OUTER_MARGIN + logoHeight;
\r
2272 rightLogoRect.right = OUTER_MARGIN + boardWidth;
\r
2273 rightLogoRect.left = rightLogoRect.right - 4*clockSize.cy;
\r
2274 rightLogoRect.top = OUTER_MARGIN;
\r
2275 rightLogoRect.bottom = OUTER_MARGIN + logoHeight;
\r
2278 whiteRect.left = leftLogoRect.right;
\r
2279 whiteRect.right = OUTER_MARGIN + boardWidth/2 - INNER_MARGIN/2;
\r
2280 whiteRect.top = OUTER_MARGIN;
\r
2281 whiteRect.bottom = whiteRect.top + logoHeight;
\r
2283 blackRect.right = rightLogoRect.left;
\r
2284 blackRect.left = whiteRect.right + INNER_MARGIN;
\r
2285 blackRect.top = whiteRect.top;
\r
2286 blackRect.bottom = whiteRect.bottom;
\r
2288 whiteRect.left = OUTER_MARGIN;
\r
2289 whiteRect.right = whiteRect.left + boardWidth/2 - INNER_MARGIN/2;
\r
2290 whiteRect.top = OUTER_MARGIN;
\r
2291 whiteRect.bottom = whiteRect.top + clockSize.cy;
\r
2293 blackRect.left = whiteRect.right + INNER_MARGIN;
\r
2294 blackRect.right = blackRect.left + boardWidth/2 - 1;
\r
2295 blackRect.top = whiteRect.top;
\r
2296 blackRect.bottom = whiteRect.bottom;
\r
2298 logoHeight = 0; // [HGM] logo: suppress logo after change to tiny layout!
\r
2301 messageRect.left = OUTER_MARGIN + MESSAGE_LINE_LEFTMARGIN;
\r
2302 if (appData.showButtonBar) {
\r
2303 messageRect.right = OUTER_MARGIN + boardWidth // [HGM] logo: expressed independent of clock placement
\r
2304 - N_BUTTONS*BUTTON_WIDTH - MESSAGE_LINE_LEFTMARGIN;
\r
2306 messageRect.right = OUTER_MARGIN + boardWidth;
\r
2308 messageRect.top = whiteRect.bottom + INNER_MARGIN;
\r
2309 messageRect.bottom = messageRect.top + messageSize.cy;
\r
2311 boardRect.left = OUTER_MARGIN;
\r
2312 boardRect.right = boardRect.left + boardWidth;
\r
2313 boardRect.top = messageRect.bottom + INNER_MARGIN;
\r
2314 boardRect.bottom = boardRect.top + boardHeight;
\r
2316 sizeInfo[boardSize].cliWidth = boardRect.right + OUTER_MARGIN;
\r
2317 sizeInfo[boardSize].cliHeight = boardRect.bottom + OUTER_MARGIN;
\r
2318 oldBoardSize = boardSize;
\r
2319 oldTinyLayout = tinyLayout;
\r
2320 winW = 2 * GetSystemMetrics(SM_CXFRAME) + boardRect.right + OUTER_MARGIN;
\r
2321 winH = 2 * GetSystemMetrics(SM_CYFRAME) + GetSystemMetrics(SM_CYMENU) +
\r
2322 GetSystemMetrics(SM_CYCAPTION) + boardRect.bottom + OUTER_MARGIN;
\r
2323 winW *= 1 + twoBoards;
\r
2324 if(suppressVisibleEffects) return; // [HGM] when called for filling sizeInfo only
\r
2325 wpMain.width = winW; // [HGM] placement: set through temporary which can used by initial sizing choice
\r
2326 wpMain.height = winH; // without disturbing window attachments
\r
2327 GetWindowRect(hwndMain, &wrect);
\r
2328 SetWindowPos(hwndMain, NULL, 0, 0, wpMain.width, wpMain.height,
\r
2329 SWP_NOCOPYBITS|SWP_NOZORDER|SWP_NOMOVE);
\r
2331 // [HGM] placement: let attached windows follow size change.
\r
2332 ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, moveHistoryDialog, &wpMoveHistory );
\r
2333 ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, evalGraphDialog, &wpEvalGraph );
\r
2334 ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, engineOutputDialog, &wpEngineOutput );
\r
2335 ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, gameListDialog, &wpGameList );
\r
2336 ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, hwndConsole, &wpConsole );
\r
2338 /* compensate if menu bar wrapped */
\r
2339 GetClientRect(hwndMain, &crect);
\r
2340 offby = boardRect.bottom + OUTER_MARGIN - crect.bottom;
\r
2341 wpMain.height += offby;
\r
2343 case WMSZ_TOPLEFT:
\r
2344 SetWindowPos(hwndMain, NULL,
\r
2345 wrect.right - wpMain.width, wrect.bottom - wpMain.height,
\r
2346 wpMain.width, wpMain.height, SWP_NOCOPYBITS|SWP_NOZORDER);
\r
2349 case WMSZ_TOPRIGHT:
\r
2351 SetWindowPos(hwndMain, NULL,
\r
2352 wrect.left, wrect.bottom - wpMain.height,
\r
2353 wpMain.width, wpMain.height, SWP_NOCOPYBITS|SWP_NOZORDER);
\r
2356 case WMSZ_BOTTOMLEFT:
\r
2358 SetWindowPos(hwndMain, NULL,
\r
2359 wrect.right - wpMain.width, wrect.top,
\r
2360 wpMain.width, wpMain.height, SWP_NOCOPYBITS|SWP_NOZORDER);
\r
2363 case WMSZ_BOTTOMRIGHT:
\r
2367 SetWindowPos(hwndMain, NULL, 0, 0, wpMain.width, wpMain.height,
\r
2368 SWP_NOCOPYBITS|SWP_NOZORDER|SWP_NOMOVE);
\r
2373 for (i = 0; i < N_BUTTONS; i++) {
\r
2374 if (buttonDesc[i].hwnd != NULL) {
\r
2375 DestroyWindow(buttonDesc[i].hwnd);
\r
2376 buttonDesc[i].hwnd = NULL;
\r
2378 if (appData.showButtonBar) {
\r
2379 buttonDesc[i].hwnd =
\r
2380 CreateWindow("BUTTON", buttonDesc[i].label,
\r
2381 WS_VISIBLE | WS_CHILD | BS_PUSHBUTTON,
\r
2382 boardRect.right - BUTTON_WIDTH*(N_BUTTONS-i),
\r
2383 messageRect.top, BUTTON_WIDTH, messageSize.cy, hwndMain,
\r
2384 (HMENU) buttonDesc[i].id,
\r
2385 (HINSTANCE) GetWindowLongPtr(hwndMain, GWLP_HINSTANCE), NULL);
\r
2387 SendMessage(buttonDesc[i].hwnd, WM_SETFONT,
\r
2388 (WPARAM)font[boardSize][MESSAGE_FONT]->hf,
\r
2389 MAKELPARAM(FALSE, 0));
\r
2391 if (buttonDesc[i].id == IDM_Pause)
\r
2392 hwndPause = buttonDesc[i].hwnd;
\r
2393 buttonDesc[i].wndproc = (WNDPROC)
\r
2394 SetWindowLongPtr(buttonDesc[i].hwnd, GWLP_WNDPROC, (LONG_PTR) ButtonProc);
\r
2397 if (gridPen != NULL) DeleteObject(gridPen);
\r
2398 if (highlightPen != NULL) DeleteObject(highlightPen);
\r
2399 if (premovePen != NULL) DeleteObject(premovePen);
\r
2400 if (lineGap != 0) {
\r
2401 logbrush.lbStyle = BS_SOLID;
\r
2402 logbrush.lbColor = RGB(0, 0, 0); /* grid pen color = black */
\r
2404 ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,
\r
2405 lineGap, &logbrush, 0, NULL);
\r
2406 logbrush.lbColor = highlightSquareColor;
\r
2408 ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,
\r
2409 lineGap, &logbrush, 0, NULL);
\r
2411 logbrush.lbColor = premoveHighlightColor;
\r
2413 ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,
\r
2414 lineGap, &logbrush, 0, NULL);
\r
2416 /* [HGM] Loop had to be split in part for vert. and hor. lines */
\r
2417 for (i = 0; i < BOARD_HEIGHT + 1; i++) {
\r
2418 gridEndpoints[i*2].x = boardRect.left + lineGap / 2;
\r
2419 gridEndpoints[i*2].y = gridEndpoints[i*2 + 1].y =
\r
2420 boardRect.top + lineGap / 2 + (i * (squareSize + lineGap));
\r
2421 gridEndpoints[i*2 + 1].x = boardRect.left + lineGap / 2 +
\r
2422 BOARD_WIDTH * (squareSize + lineGap);
\r
2423 gridVertexCounts[i*2] = gridVertexCounts[i*2 + 1] = 2;
\r
2425 for (i = 0; i < BOARD_WIDTH + 1; i++) {
\r
2426 gridEndpoints[i*2 + BOARD_HEIGHT*2 + 2].y = boardRect.top + lineGap / 2;
\r
2427 gridEndpoints[i*2 + BOARD_HEIGHT*2 + 2].x =
\r
2428 gridEndpoints[i*2 + 1 + BOARD_HEIGHT*2 + 2].x = boardRect.left +
\r
2429 lineGap / 2 + (i * (squareSize + lineGap));
\r
2430 gridEndpoints[i*2 + 1 + BOARD_HEIGHT*2 + 2].y =
\r
2431 boardRect.top + BOARD_HEIGHT * (squareSize + lineGap);
\r
2432 gridVertexCounts[i*2] = gridVertexCounts[i*2 + 1] = 2;
\r
2436 /* [HGM] Licensing requirement */
\r
2438 if(gameInfo.variant == VariantGothic) GothicPopUp( GOTHIC, VariantGothic); else
\r
2441 if(gameInfo.variant == VariantFalcon) GothicPopUp( FALCON, VariantFalcon); else
\r
2443 GothicPopUp( "", VariantNormal);
\r
2446 /* if (boardSize == oldBoardSize) return; [HGM] variant might have changed */
\r
2448 /* Load piece bitmaps for this board size */
\r
2449 for (i=0; i<=2; i++) {
\r
2450 for (piece = WhitePawn;
\r
2451 (int) piece < (int) BlackPawn;
\r
2452 piece = (ChessSquare) ((int) piece + 1)) {
\r
2453 if (pieceBitmap[i][piece] != NULL)
\r
2454 DeleteObject(pieceBitmap[i][piece]);
\r
2458 fontBitmapSquareSize = 0; /* [HGM] render: make sure pieces will be recreated, as we might need others now */
\r
2459 // Orthodox Chess pieces
\r
2460 pieceBitmap[0][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "s");
\r
2461 pieceBitmap[0][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "s");
\r
2462 pieceBitmap[0][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "s");
\r
2463 pieceBitmap[0][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "s");
\r
2464 pieceBitmap[0][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "s");
\r
2465 pieceBitmap[1][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "o");
\r
2466 pieceBitmap[1][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "o");
\r
2467 pieceBitmap[1][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "o");
\r
2468 pieceBitmap[1][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "o");
\r
2469 pieceBitmap[1][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "o");
\r
2470 pieceBitmap[2][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "w");
\r
2471 pieceBitmap[2][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "w");
\r
2472 pieceBitmap[2][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "w");
\r
2473 pieceBitmap[2][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "w");
\r
2474 pieceBitmap[2][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "w");
\r
2475 if( gameInfo.variant == VariantShogi && squareSize <= 72 && squareSize >= 33) {
\r
2476 // in Shogi, Hijack the unused Queen for Lance
\r
2477 pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "s");
\r
2478 pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "o");
\r
2479 pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "w");
\r
2481 pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "s");
\r
2482 pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "o");
\r
2483 pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "w");
\r
2486 if(squareSize <= 72 && squareSize >= 33) {
\r
2487 /* A & C are available in most sizes now */
\r
2488 if(squareSize != 49 && squareSize != 72 && squareSize != 33) { // Vortex-like
\r
2489 pieceBitmap[0][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "s");
\r
2490 pieceBitmap[1][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "o");
\r
2491 pieceBitmap[2][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "w");
\r
2492 pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "s");
\r
2493 pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "o");
\r
2494 pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "w");
\r
2495 pieceBitmap[0][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "s");
\r
2496 pieceBitmap[1][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "o");
\r
2497 pieceBitmap[2][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "w");
\r
2498 pieceBitmap[0][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "s");
\r
2499 pieceBitmap[1][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "o");
\r
2500 pieceBitmap[2][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "w");
\r
2501 } else { // Smirf-like
\r
2502 if(gameInfo.variant == VariantSChess) {
\r
2503 pieceBitmap[0][WhiteAngel] = DoLoadBitmap(hInst, "v", squareSize, "s");
\r
2504 pieceBitmap[1][WhiteAngel] = DoLoadBitmap(hInst, "v", squareSize, "o");
\r
2505 pieceBitmap[2][WhiteAngel] = DoLoadBitmap(hInst, "v", squareSize, "w");
\r
2507 pieceBitmap[0][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "s");
\r
2508 pieceBitmap[1][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "o");
\r
2509 pieceBitmap[2][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "w");
\r
2512 if(gameInfo.variant == VariantGothic) { // Vortex-like
\r
2513 pieceBitmap[0][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "s");
\r
2514 pieceBitmap[1][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "o");
\r
2515 pieceBitmap[2][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "w");
\r
2516 } else if(gameInfo.variant == VariantSChess && (squareSize == 49 || squareSize == 72)) {
\r
2517 pieceBitmap[0][WhiteMarshall] = DoLoadBitmap(hInst, "e", squareSize, "s");
\r
2518 pieceBitmap[1][WhiteMarshall] = DoLoadBitmap(hInst, "e", squareSize, "o");
\r
2519 pieceBitmap[2][WhiteMarshall] = DoLoadBitmap(hInst, "e", squareSize, "w");
\r
2520 } else { // WinBoard standard
\r
2521 pieceBitmap[0][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "s");
\r
2522 pieceBitmap[1][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "o");
\r
2523 pieceBitmap[2][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "w");
\r
2528 if(squareSize==72 || squareSize==49 || squareSize==33) { /* experiment with some home-made bitmaps */
\r
2529 pieceBitmap[0][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "s");
\r
2530 pieceBitmap[1][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "o");
\r
2531 pieceBitmap[2][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "w");
\r
2532 pieceBitmap[0][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "s");
\r
2533 pieceBitmap[1][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "o");
\r
2534 pieceBitmap[2][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "w");
\r
2535 pieceBitmap[0][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "s");
\r
2536 pieceBitmap[1][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "o");
\r
2537 pieceBitmap[2][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "w");
\r
2538 pieceBitmap[0][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "s");
\r
2539 pieceBitmap[1][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "o");
\r
2540 pieceBitmap[2][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "w");
\r
2541 pieceBitmap[0][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "s");
\r
2542 pieceBitmap[1][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "o");
\r
2543 pieceBitmap[2][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "w");
\r
2544 pieceBitmap[0][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "s");
\r
2545 pieceBitmap[1][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "o");
\r
2546 pieceBitmap[2][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "w");
\r
2547 pieceBitmap[0][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "s");
\r
2548 pieceBitmap[1][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "o");
\r
2549 pieceBitmap[2][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "w");
\r
2550 pieceBitmap[0][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "s");
\r
2551 pieceBitmap[1][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "o");
\r
2552 pieceBitmap[2][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "w");
\r
2553 pieceBitmap[0][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "s");
\r
2554 pieceBitmap[1][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "o");
\r
2555 pieceBitmap[2][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "w");
\r
2556 pieceBitmap[0][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "s");
\r
2557 pieceBitmap[1][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "o");
\r
2558 pieceBitmap[2][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "w");
\r
2560 if(gameInfo.variant == VariantShogi) { /* promoted Gold represemtations */
\r
2561 pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "wp", squareSize, "s");
\r
2562 pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "wp", squareSize, "o");
\r
2563 pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "w", squareSize, "w");
\r
2564 pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "wn", squareSize, "s");
\r
2565 pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "wn", squareSize, "o");
\r
2566 pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "w", squareSize, "w");
\r
2567 pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "ws", squareSize, "s");
\r
2568 pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "ws", squareSize, "o");
\r
2569 pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "w", squareSize, "w");
\r
2570 pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "wl", squareSize, "s");
\r
2571 pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "wl", squareSize, "o");
\r
2572 pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "w", squareSize, "w");
\r
2574 pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "s");
\r
2575 pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "o");
\r
2576 pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "w");
\r
2577 pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "s");
\r
2578 pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "o");
\r
2579 pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "w");
\r
2580 pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "s");
\r
2581 pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "o");
\r
2582 pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "w");
\r
2583 pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "s");
\r
2584 pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "o");
\r
2585 pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "w");
\r
2588 } else { /* other size, no special bitmaps available. Use smaller symbols */
\r
2589 if((int)boardSize < 2) minorSize = sizeInfo[0].squareSize;
\r
2590 else minorSize = sizeInfo[(int)boardSize - 2].squareSize;
\r
2591 pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "s");
\r
2592 pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "o");
\r
2593 pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "w");
\r
2594 pieceBitmap[0][WhiteCardinal] = DoLoadBitmap(hInst, "b", minorSize, "s");
\r
2595 pieceBitmap[1][WhiteCardinal] = DoLoadBitmap(hInst, "b", minorSize, "o");
\r
2596 pieceBitmap[2][WhiteCardinal] = DoLoadBitmap(hInst, "b", minorSize, "w");
\r
2597 pieceBitmap[0][WhiteDragon] = DoLoadBitmap(hInst, "r", minorSize, "s");
\r
2598 pieceBitmap[1][WhiteDragon] = DoLoadBitmap(hInst, "r", minorSize, "o");
\r
2599 pieceBitmap[2][WhiteDragon] = DoLoadBitmap(hInst, "r", minorSize, "w");
\r
2600 pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "s");
\r
2601 pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "o");
\r
2602 pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "w");
\r
2606 if(gameInfo.variant == VariantShogi && squareSize == 58)
\r
2607 /* special Shogi support in this size */
\r
2608 { for (i=0; i<=2; i++) { /* replace all bitmaps */
\r
2609 for (piece = WhitePawn;
\r
2610 (int) piece < (int) BlackPawn;
\r
2611 piece = (ChessSquare) ((int) piece + 1)) {
\r
2612 if (pieceBitmap[i][piece] != NULL)
\r
2613 DeleteObject(pieceBitmap[i][piece]);
\r
2616 pieceBitmap[0][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "o");
\r
2617 pieceBitmap[0][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "o");
\r
2618 pieceBitmap[0][WhiteBishop] = DoLoadBitmap(hInst, "sb", squareSize, "o");
\r
2619 pieceBitmap[0][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "o");
\r
2620 pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "o");
\r
2621 pieceBitmap[0][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "o");
\r
2622 pieceBitmap[0][WhiteFerz] = DoLoadBitmap(hInst, "sf", squareSize, "o");
\r
2623 pieceBitmap[0][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "o");
\r
2624 pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "su", squareSize, "o");
\r
2625 pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "sh", squareSize, "o");
\r
2626 pieceBitmap[0][WhiteCardinal] = DoLoadBitmap(hInst, "sa", squareSize, "o");
\r
2627 pieceBitmap[0][WhiteDragon] = DoLoadBitmap(hInst, "sc", squareSize, "o");
\r
2628 pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "sg", squareSize, "o");
\r
2629 pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "ss", squareSize, "o");
\r
2630 pieceBitmap[1][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "o");
\r
2631 pieceBitmap[1][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "o");
\r
2632 pieceBitmap[1][WhiteBishop] = DoLoadBitmap(hInst, "sb", squareSize, "o");
\r
2633 pieceBitmap[1][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "o");
\r
2634 pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "o");
\r
2635 pieceBitmap[1][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "o");
\r
2636 pieceBitmap[1][WhiteFerz] = DoLoadBitmap(hInst, "sf", squareSize, "o");
\r
2637 pieceBitmap[1][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "o");
\r
2638 pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "su", squareSize, "o");
\r
2639 pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "sh", squareSize, "o");
\r
2640 pieceBitmap[1][WhiteCardinal] = DoLoadBitmap(hInst, "sa", squareSize, "o");
\r
2641 pieceBitmap[1][WhiteDragon] = DoLoadBitmap(hInst, "sc", squareSize, "o");
\r
2642 pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "sg", squareSize, "o");
\r
2643 pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "ss", squareSize, "o");
\r
2644 pieceBitmap[2][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "w");
\r
2645 pieceBitmap[2][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "w");
\r
2646 pieceBitmap[2][WhiteBishop] = DoLoadBitmap(hInst, "sr", squareSize, "w");
\r
2647 pieceBitmap[2][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "w");
\r
2648 pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "w");
\r
2649 pieceBitmap[2][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "w");
\r
2650 pieceBitmap[2][WhiteFerz] = DoLoadBitmap(hInst, "sw", squareSize, "w");
\r
2651 pieceBitmap[2][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "w");
\r
2652 pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "sp", squareSize, "w");
\r
2653 pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "sn", squareSize, "w");
\r
2654 pieceBitmap[2][WhiteCardinal] = DoLoadBitmap(hInst, "sr", squareSize, "w");
\r
2655 pieceBitmap[2][WhiteDragon] = DoLoadBitmap(hInst, "sr", squareSize, "w");
\r
2656 pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "sl", squareSize, "w");
\r
2657 pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "sw", squareSize, "w");
\r
2663 PieceBitmap(ChessSquare p, int kind)
\r
2665 if ((int) p >= (int) BlackPawn)
\r
2666 p = (ChessSquare) ((int) p - (int) BlackPawn + (int) WhitePawn);
\r
2668 return pieceBitmap[kind][(int) p];
\r
2671 /***************************************************************/
\r
2673 #define MIN(a,b) (((a) < (b)) ? (a) : (b))
\r
2674 #define MAX(a,b) (((a) > (b)) ? (a) : (b))
\r
2676 #define MIN3(a,b,c) (((a) < (b) && (a) < (c)) ? (a) : (((b) < (a) && (b) < (c)) ? (b) : (c)))
\r
2677 #define MAX3(a,b,c) (((a) > (b) && (a) > (c)) ? (a) : (((b) > (a) && (b) > (c)) ? (b) : (c)))
\r
2681 SquareToPos(int row, int column, int * x, int * y)
\r
2684 *x = boardRect.left + lineGap + ((BOARD_WIDTH-1)-column) * (squareSize + lineGap);
\r
2685 *y = boardRect.top + lineGap + row * (squareSize + lineGap);
\r
2687 *x = boardRect.left + lineGap + column * (squareSize + lineGap);
\r
2688 *y = boardRect.top + lineGap + ((BOARD_HEIGHT-1)-row) * (squareSize + lineGap);
\r
2693 DrawCoordsOnDC(HDC hdc)
\r
2695 static char files[] = "0123456789012345678901221098765432109876543210";
\r
2696 static char ranks[] = "wvutsrqponmlkjihgfedcbaabcdefghijklmnopqrstuvw";
\r
2697 char str[2] = { NULLCHAR, NULLCHAR };
\r
2698 int oldMode, oldAlign, x, y, start, i;
\r
2702 if (!appData.showCoords)
\r
2705 start = flipView ? 1-(ONE!='1') : 45+(ONE!='1')-BOARD_HEIGHT;
\r
2707 oldBrush = SelectObject(hdc, GetStockObject(BLACK_BRUSH));
\r
2708 oldMode = SetBkMode(hdc, (appData.monoMode ? OPAQUE : TRANSPARENT));
\r
2709 oldAlign = GetTextAlign(hdc);
\r
2710 oldFont = SelectObject(hdc, font[boardSize][COORD_FONT]->hf);
\r
2712 y = boardRect.top + lineGap;
\r
2713 x = boardRect.left + lineGap + gameInfo.holdingsWidth*(squareSize + lineGap);
\r
2715 SetTextAlign(hdc, TA_LEFT|TA_TOP);
\r
2716 for (i = 0; i < BOARD_HEIGHT; i++) {
\r
2717 str[0] = files[start + i];
\r
2718 ExtTextOut(hdc, x + 2, y + 1, 0, NULL, str, 1, NULL);
\r
2719 y += squareSize + lineGap;
\r
2722 start = flipView ? 23-(BOARD_RGHT-BOARD_LEFT) : 23;
\r
2724 SetTextAlign(hdc, TA_RIGHT|TA_BOTTOM);
\r
2725 for (i = 0; i < BOARD_RGHT - BOARD_LEFT; i++) {
\r
2726 str[0] = ranks[start + i];
\r
2727 ExtTextOut(hdc, x + squareSize - 2, y - 1, 0, NULL, str, 1, NULL);
\r
2728 x += squareSize + lineGap;
\r
2731 SelectObject(hdc, oldBrush);
\r
2732 SetBkMode(hdc, oldMode);
\r
2733 SetTextAlign(hdc, oldAlign);
\r
2734 SelectObject(hdc, oldFont);
\r
2738 DrawGridOnDC(HDC hdc)
\r
2742 if (lineGap != 0) {
\r
2743 oldPen = SelectObject(hdc, gridPen);
\r
2744 PolyPolyline(hdc, gridEndpoints, gridVertexCounts, BOARD_WIDTH+BOARD_HEIGHT + 2);
\r
2745 SelectObject(hdc, oldPen);
\r
2749 #define HIGHLIGHT_PEN 0
\r
2750 #define PREMOVE_PEN 1
\r
2753 DrawHighlightOnDC(HDC hdc, BOOLEAN on, int x, int y, int pen)
\r
2756 HPEN oldPen, hPen;
\r
2757 if (lineGap == 0) return;
\r
2759 x1 = boardRect.left +
\r
2760 lineGap/2 + ((BOARD_WIDTH-1)-x) * (squareSize + lineGap);
\r
2761 y1 = boardRect.top +
\r
2762 lineGap/2 + y * (squareSize + lineGap);
\r
2764 x1 = boardRect.left +
\r
2765 lineGap/2 + x * (squareSize + lineGap);
\r
2766 y1 = boardRect.top +
\r
2767 lineGap/2 + ((BOARD_HEIGHT-1)-y) * (squareSize + lineGap);
\r
2769 hPen = pen ? premovePen : highlightPen;
\r
2770 oldPen = SelectObject(hdc, on ? hPen : gridPen);
\r
2771 MoveToEx(hdc, x1, y1, NULL);
\r
2772 LineTo(hdc, x1 + squareSize + lineGap, y1);
\r
2773 LineTo(hdc, x1 + squareSize + lineGap, y1 + squareSize + lineGap);
\r
2774 LineTo(hdc, x1, y1 + squareSize + lineGap);
\r
2775 LineTo(hdc, x1, y1);
\r
2776 SelectObject(hdc, oldPen);
\r
2780 DrawHighlightsOnDC(HDC hdc, HighlightInfo *h, int pen)
\r
2783 for (i=0; i<2; i++) {
\r
2784 if (h->sq[i].x >= 0 && h->sq[i].y >= 0)
\r
2785 DrawHighlightOnDC(hdc, TRUE,
\r
2786 h->sq[i].x, h->sq[i].y,
\r
2791 /* Note: sqcolor is used only in monoMode */
\r
2792 /* Note that this code is largely duplicated in woptions.c,
\r
2793 function DrawSampleSquare, so that needs to be updated too */
\r
2795 DrawPieceOnDC(HDC hdc, ChessSquare piece, int color, int sqcolor, int x, int y, HDC tmphdc)
\r
2797 HBITMAP oldBitmap;
\r
2801 if (appData.blindfold) return;
\r
2803 /* [AS] Use font-based pieces if needed */
\r
2804 if( fontBitmapSquareSize >= 0 && (squareSize > 32 || gameInfo.variant >= VariantShogi)) {
\r
2805 /* Create piece bitmaps, or do nothing if piece set is up to date */
\r
2806 CreatePiecesFromFont();
\r
2808 if( fontBitmapSquareSize == squareSize ) {
\r
2809 int index = TranslatePieceToFontPiece(piece);
\r
2811 SelectObject( tmphdc, hPieceMask[ index ] );
\r
2813 if(appData.upsideDown ? color==flipView : (flipView && gameInfo.variant == VariantShogi))
\r
2814 StretchBlt(hdc, x+squareSize, y+squareSize, -squareSize, -squareSize, tmphdc, 0, 0, squareSize, squareSize, SRCAND);
\r
2818 squareSize, squareSize,
\r
2823 SelectObject( tmphdc, hPieceFace[ index ] );
\r
2825 if(appData.upsideDown ? color==flipView : (flipView && gameInfo.variant == VariantShogi))
\r
2826 StretchBlt(hdc, x+squareSize, y+squareSize, -squareSize, -squareSize, tmphdc, 0, 0, squareSize, squareSize, SRCPAINT);
\r
2830 squareSize, squareSize,
\r
2839 if (appData.monoMode) {
\r
2840 SelectObject(tmphdc, PieceBitmap(piece,
\r
2841 color == sqcolor ? OUTLINE_PIECE : SOLID_PIECE));
\r
2842 BitBlt(hdc, x, y, squareSize, squareSize, tmphdc, 0, 0,
\r
2843 sqcolor ? SRCCOPY : NOTSRCCOPY);
\r
2845 tmpSize = squareSize;
\r
2847 ((piece >= (int)WhiteNightrider && piece <= WhiteGrasshopper) ||
\r
2848 (piece >= (int)BlackNightrider && piece <= BlackGrasshopper)) ) {
\r
2849 /* [HGM] no bitmap available for promoted pieces in Crazyhouse */
\r
2850 /* Bitmaps of smaller size are substituted, but we have to align them */
\r
2851 x += (squareSize - minorSize)>>1;
\r
2852 y += squareSize - minorSize - 2;
\r
2853 tmpSize = minorSize;
\r
2855 if (color || appData.allWhite ) {
\r
2856 oldBitmap = SelectObject(tmphdc, PieceBitmap(piece, WHITE_PIECE));
\r
2858 oldBrush = SelectObject(hdc, whitePieceBrush);
\r
2859 else oldBrush = SelectObject(hdc, blackPieceBrush);
\r
2860 if(appData.upsideDown && color==flipView)
\r
2861 StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, 0x00B8074A);
\r
2863 BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, 0x00B8074A);
\r
2864 /* Use black for outline of white pieces */
\r
2865 SelectObject(tmphdc, PieceBitmap(piece, OUTLINE_PIECE));
\r
2866 if(appData.upsideDown && color==flipView)
\r
2867 StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, SRCAND);
\r
2869 BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, SRCAND);
\r
2871 /* Use square color for details of black pieces */
\r
2872 oldBitmap = SelectObject(tmphdc, PieceBitmap(piece, SOLID_PIECE));
\r
2873 oldBrush = SelectObject(hdc, blackPieceBrush);
\r
2874 if(appData.upsideDown && !flipView)
\r
2875 StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, 0x00B8074A);
\r
2877 BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, 0x00B8074A);
\r
2879 SelectObject(hdc, oldBrush);
\r
2880 SelectObject(tmphdc, oldBitmap);
\r
2884 /* [AS] Compute a drawing mode for a square, based on specified settings (see DrawTile) */
\r
2885 int GetBackTextureMode( int algo )
\r
2887 int result = BACK_TEXTURE_MODE_DISABLED;
\r
2891 case BACK_TEXTURE_MODE_PLAIN:
\r
2892 result = 1; /* Always use identity map */
\r
2894 case BACK_TEXTURE_MODE_FULL_RANDOM:
\r
2895 result = 1 + (myrandom() % 3); /* Pick a transformation at random */
\r
2903 [AS] Compute and save texture drawing info, otherwise we may not be able
\r
2904 to handle redraws cleanly (as random numbers would always be different).
\r
2906 VOID RebuildTextureSquareInfo()
\r
2916 ZeroMemory( &backTextureSquareInfo, sizeof(backTextureSquareInfo) );
\r
2918 if( liteBackTexture != NULL ) {
\r
2919 if( GetObject( liteBackTexture, sizeof(bi), &bi ) > 0 ) {
\r
2920 lite_w = bi.bmWidth;
\r
2921 lite_h = bi.bmHeight;
\r
2925 if( darkBackTexture != NULL ) {
\r
2926 if( GetObject( darkBackTexture, sizeof(bi), &bi ) > 0 ) {
\r
2927 dark_w = bi.bmWidth;
\r
2928 dark_h = bi.bmHeight;
\r
2932 for( row=0; row<BOARD_HEIGHT; row++ ) {
\r
2933 for( col=0; col<BOARD_WIDTH; col++ ) {
\r
2934 if( (col + row) & 1 ) {
\r
2936 if( lite_w >= squareSize && lite_h >= squareSize ) {
\r
2937 if( lite_w >= squareSize*BOARD_WIDTH )
\r
2938 backTextureSquareInfo[row][col].x = (2*col+1)*lite_w/(2*BOARD_WIDTH) - squareSize/2; /* [HGM] cut out of center of virtual square */
\r
2940 backTextureSquareInfo[row][col].x = col * (lite_w - squareSize) / (BOARD_WIDTH-1); /* [HGM] divide by size-1 in stead of size! */
\r
2941 if( lite_h >= squareSize*BOARD_HEIGHT )
\r
2942 backTextureSquareInfo[row][col].y = (2*(BOARD_HEIGHT-row)-1)*lite_h/(2*BOARD_HEIGHT) - squareSize/2;
\r
2944 backTextureSquareInfo[row][col].y = (BOARD_HEIGHT-1-row) * (lite_h - squareSize) / (BOARD_HEIGHT-1);
\r
2945 backTextureSquareInfo[row][col].mode = GetBackTextureMode(liteBackTextureMode);
\r
2950 if( dark_w >= squareSize && dark_h >= squareSize ) {
\r
2951 if( dark_w >= squareSize*BOARD_WIDTH )
\r
2952 backTextureSquareInfo[row][col].x = (2*col+1) * dark_w / (2*BOARD_WIDTH) - squareSize/2;
\r
2954 backTextureSquareInfo[row][col].x = col * (dark_w - squareSize) / (BOARD_WIDTH-1);
\r
2955 if( dark_h >= squareSize*BOARD_HEIGHT )
\r
2956 backTextureSquareInfo[row][col].y = (2*(BOARD_HEIGHT-row)-1) * dark_h / (2*BOARD_HEIGHT) - squareSize/2;
\r
2958 backTextureSquareInfo[row][col].y = (BOARD_HEIGHT-1-row) * (dark_h - squareSize) / (BOARD_HEIGHT-1);
\r
2959 backTextureSquareInfo[row][col].mode = GetBackTextureMode(darkBackTextureMode);
\r
2966 /* [AS] Arrow highlighting support */
\r
2968 static double A_WIDTH = 5; /* Width of arrow body */
\r
2970 #define A_HEIGHT_FACTOR 6 /* Length of arrow "point", relative to body width */
\r
2971 #define A_WIDTH_FACTOR 3 /* Width of arrow "point", relative to body width */
\r
2973 static double Sqr( double x )
\r
2978 static int Round( double x )
\r
2980 return (int) (x + 0.5);
\r
2983 /* Draw an arrow between two points using current settings */
\r
2984 VOID DrawArrowBetweenPoints( HDC hdc, int s_x, int s_y, int d_x, int d_y )
\r
2987 double dx, dy, j, k, x, y;
\r
2989 if( d_x == s_x ) {
\r
2990 int h = (d_y > s_y) ? +A_WIDTH*A_HEIGHT_FACTOR : -A_WIDTH*A_HEIGHT_FACTOR;
\r
2992 arrow[0].x = s_x + A_WIDTH + 0.5;
\r
2995 arrow[1].x = s_x + A_WIDTH + 0.5;
\r
2996 arrow[1].y = d_y - h;
\r
2998 arrow[2].x = arrow[1].x + A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;
\r
2999 arrow[2].y = d_y - h;
\r
3004 arrow[5].x = arrow[1].x - 2*A_WIDTH + 0.5;
\r
3005 arrow[5].y = d_y - h;
\r
3007 arrow[4].x = arrow[5].x - A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;
\r
3008 arrow[4].y = d_y - h;
\r
3010 arrow[6].x = arrow[1].x - 2*A_WIDTH + 0.5;
\r
3013 else if( d_y == s_y ) {
\r
3014 int w = (d_x > s_x) ? +A_WIDTH*A_HEIGHT_FACTOR : -A_WIDTH*A_HEIGHT_FACTOR;
\r
3017 arrow[0].y = s_y + A_WIDTH + 0.5;
\r
3019 arrow[1].x = d_x - w;
\r
3020 arrow[1].y = s_y + A_WIDTH + 0.5;
\r
3022 arrow[2].x = d_x - w;
\r
3023 arrow[2].y = arrow[1].y + A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;
\r
3028 arrow[5].x = d_x - w;
\r
3029 arrow[5].y = arrow[1].y - 2*A_WIDTH + 0.5;
\r
3031 arrow[4].x = d_x - w;
\r
3032 arrow[4].y = arrow[5].y - A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;
\r
3035 arrow[6].y = arrow[1].y - 2*A_WIDTH + 0.5;
\r
3038 /* [AS] Needed a lot of paper for this! :-) */
\r
3039 dy = (double) (d_y - s_y) / (double) (d_x - s_x);
\r
3040 dx = (double) (s_x - d_x) / (double) (s_y - d_y);
\r
3042 j = sqrt( Sqr(A_WIDTH) / (1.0 + Sqr(dx)) );
\r
3044 k = sqrt( Sqr(A_WIDTH*A_HEIGHT_FACTOR) / (1.0 + Sqr(dy)) );
\r
3049 arrow[0].x = Round(x - j);
\r
3050 arrow[0].y = Round(y + j*dx);
\r
3052 arrow[1].x = Round(arrow[0].x + 2*j); // [HGM] prevent width to be affected by rounding twice
\r
3053 arrow[1].y = Round(arrow[0].y - 2*j*dx);
\r
3056 x = (double) d_x - k;
\r
3057 y = (double) d_y - k*dy;
\r
3060 x = (double) d_x + k;
\r
3061 y = (double) d_y + k*dy;
\r
3064 x = Round(x); y = Round(y); // [HGM] make sure width of shaft is rounded the same way on both ends
\r
3066 arrow[6].x = Round(x - j);
\r
3067 arrow[6].y = Round(y + j*dx);
\r
3069 arrow[2].x = Round(arrow[6].x + 2*j);
\r
3070 arrow[2].y = Round(arrow[6].y - 2*j*dx);
\r
3072 arrow[3].x = Round(arrow[2].x + j*(A_WIDTH_FACTOR-1));
\r
3073 arrow[3].y = Round(arrow[2].y - j*(A_WIDTH_FACTOR-1)*dx);
\r
3078 arrow[5].x = Round(arrow[6].x - j*(A_WIDTH_FACTOR-1));
\r
3079 arrow[5].y = Round(arrow[6].y + j*(A_WIDTH_FACTOR-1)*dx);
\r
3082 Polygon( hdc, arrow, 7 );
\r
3085 /* [AS] Draw an arrow between two squares */
\r
3086 VOID DrawArrowBetweenSquares( HDC hdc, int s_col, int s_row, int d_col, int d_row )
\r
3088 int s_x, s_y, d_x, d_y;
\r
3095 if( s_col == d_col && s_row == d_row ) {
\r
3099 /* Get source and destination points */
\r
3100 SquareToPos( s_row, s_col, &s_x, &s_y);
\r
3101 SquareToPos( d_row, d_col, &d_x, &d_y);
\r
3104 d_y += squareSize / 2 - squareSize / 4; // [HGM] round towards same centers on all sides!
\r
3106 else if( d_y < s_y ) {
\r
3107 d_y += squareSize / 2 + squareSize / 4;
\r
3110 d_y += squareSize / 2;
\r
3114 d_x += squareSize / 2 - squareSize / 4;
\r
3116 else if( d_x < s_x ) {
\r
3117 d_x += squareSize / 2 + squareSize / 4;
\r
3120 d_x += squareSize / 2;
\r
3123 s_x += squareSize / 2;
\r
3124 s_y += squareSize / 2;
\r
3126 /* Adjust width */
\r
3127 A_WIDTH = squareSize / 14.; //[HGM] make float
\r
3130 stLB.lbStyle = BS_SOLID;
\r
3131 stLB.lbColor = appData.highlightArrowColor;
\r
3134 hpen = CreatePen( PS_SOLID, 2, RGB(0x00,0x00,0x00) );
\r
3135 holdpen = SelectObject( hdc, hpen );
\r
3136 hbrush = CreateBrushIndirect( &stLB );
\r
3137 holdbrush = SelectObject( hdc, hbrush );
\r
3139 DrawArrowBetweenPoints( hdc, s_x, s_y, d_x, d_y );
\r
3141 SelectObject( hdc, holdpen );
\r
3142 SelectObject( hdc, holdbrush );
\r
3143 DeleteObject( hpen );
\r
3144 DeleteObject( hbrush );
\r
3147 BOOL HasHighlightInfo()
\r
3149 BOOL result = FALSE;
\r
3151 if( highlightInfo.sq[0].x >= 0 && highlightInfo.sq[0].y >= 0 &&
\r
3152 highlightInfo.sq[1].x >= 0 && highlightInfo.sq[1].y >= 0 )
\r
3160 BOOL IsDrawArrowEnabled()
\r
3162 BOOL result = FALSE;
\r
3164 if( appData.highlightMoveWithArrow && squareSize >= 32 ) {
\r
3171 VOID DrawArrowHighlight( HDC hdc )
\r
3173 if( IsDrawArrowEnabled() && HasHighlightInfo() ) {
\r
3174 DrawArrowBetweenSquares( hdc,
\r
3175 highlightInfo.sq[0].x, highlightInfo.sq[0].y,
\r
3176 highlightInfo.sq[1].x, highlightInfo.sq[1].y );
\r
3180 HRGN GetArrowHighlightClipRegion( HDC hdc )
\r
3182 HRGN result = NULL;
\r
3184 if( HasHighlightInfo() ) {
\r
3185 int x1, y1, x2, y2;
\r
3186 int sx, sy, dx, dy;
\r
3188 SquareToPos(highlightInfo.sq[0].y, highlightInfo.sq[0].x, &x1, &y1 );
\r
3189 SquareToPos(highlightInfo.sq[1].y, highlightInfo.sq[1].x, &x2, &y2 );
\r
3191 sx = MIN( x1, x2 );
\r
3192 sy = MIN( y1, y2 );
\r
3193 dx = MAX( x1, x2 ) + squareSize;
\r
3194 dy = MAX( y1, y2 ) + squareSize;
\r
3196 result = CreateRectRgn( sx, sy, dx, dy );
\r
3203 Warning: this function modifies the behavior of several other functions.
\r
3205 Basically, Winboard is optimized to avoid drawing the whole board if not strictly
\r
3206 needed. Unfortunately, the decision whether or not to perform a full or partial
\r
3207 repaint is scattered all over the place, which is not good for features such as
\r
3208 "arrow highlighting" that require a full repaint of the board.
\r
3210 So, I've tried to patch the code where I thought it made sense (e.g. after or during
\r
3211 user interaction, when speed is not so important) but especially to avoid errors
\r
3212 in the displayed graphics.
\r
3214 In such patched places, I always try refer to this function so there is a single
\r
3215 place to maintain knowledge.
\r
3217 To restore the original behavior, just return FALSE unconditionally.
\r
3219 BOOL IsFullRepaintPreferrable()
\r
3221 BOOL result = FALSE;
\r
3223 if( (appData.highlightLastMove || appData.highlightDragging) && IsDrawArrowEnabled() ) {
\r
3224 /* Arrow may appear on the board */
\r
3232 This function is called by DrawPosition to know whether a full repaint must
\r
3235 Only DrawPosition may directly call this function, which makes use of
\r
3236 some state information. Other function should call DrawPosition specifying
\r
3237 the repaint flag, and can use IsFullRepaintPreferrable if needed.
\r
3239 BOOL DrawPositionNeedsFullRepaint()
\r
3241 BOOL result = FALSE;
\r
3244 Probably a slightly better policy would be to trigger a full repaint
\r
3245 when animInfo.piece changes state (i.e. empty -> non-empty and viceversa),
\r
3246 but animation is fast enough that it's difficult to notice.
\r
3248 if( animInfo.piece == EmptySquare ) {
\r
3249 if( (appData.highlightLastMove || appData.highlightDragging) && IsDrawArrowEnabled() /*&& HasHighlightInfo()*/ ) {
\r
3258 DrawBoardOnDC(HDC hdc, Board board, HDC tmphdc)
\r
3260 int row, column, x, y, square_color, piece_color;
\r
3261 ChessSquare piece;
\r
3263 HDC texture_hdc = NULL;
\r
3265 /* [AS] Initialize background textures if needed */
\r
3266 if( liteBackTexture != NULL || darkBackTexture != NULL ) {
\r
3267 static int backTextureBoardSize; /* [HGM] boardsize: also new texture if board format changed */
\r
3268 if( backTextureSquareSize != squareSize
\r
3269 || backTextureBoardSize != BOARD_WIDTH+BOARD_FILES*BOARD_HEIGHT) {
\r
3270 backTextureBoardSize = BOARD_WIDTH+BOARD_FILES*BOARD_HEIGHT;
\r
3271 backTextureSquareSize = squareSize;
\r
3272 RebuildTextureSquareInfo();
\r
3275 texture_hdc = CreateCompatibleDC( hdc );
\r
3278 for (row = 0; row < BOARD_HEIGHT; row++) {
\r
3279 for (column = 0; column < BOARD_WIDTH; column++) {
\r
3281 SquareToPos(row, column, &x, &y);
\r
3283 piece = board[row][column];
\r
3285 square_color = ((column + row) % 2) == 1;
\r
3286 if( gameInfo.variant == VariantXiangqi ) {
\r
3287 square_color = !InPalace(row, column);
\r
3288 if(BOARD_HEIGHT&1) { if(row==BOARD_HEIGHT/2) square_color ^= 1; }
\r
3289 else if(row < BOARD_HEIGHT/2) square_color ^= 1;
\r
3291 piece_color = (int) piece < (int) BlackPawn;
\r
3294 /* [HGM] holdings file: light square or black */
\r
3295 if(column == BOARD_LEFT-2) {
\r
3296 if( row > BOARD_HEIGHT - gameInfo.holdingsSize - 1 )
\r
3299 DisplayHoldingsCount(hdc, x, y, 0, 0); /* black out */
\r
3303 if(column == BOARD_RGHT + 1 ) {
\r
3304 if( row < gameInfo.holdingsSize )
\r
3307 DisplayHoldingsCount(hdc, x, y, 0, 0);
\r
3311 if(column == BOARD_LEFT-1 ) /* left align */
\r
3312 DisplayHoldingsCount(hdc, x, y, flipView, (int) board[row][column]);
\r
3313 else if( column == BOARD_RGHT) /* right align */
\r
3314 DisplayHoldingsCount(hdc, x, y, !flipView, (int) board[row][column]);
\r
3316 if (appData.monoMode) {
\r
3317 if (piece == EmptySquare) {
\r
3318 BitBlt(hdc, x, y, squareSize, squareSize, 0, 0, 0,
\r
3319 square_color ? WHITENESS : BLACKNESS);
\r
3321 DrawPieceOnDC(hdc, piece, piece_color, square_color, x, y, tmphdc);
\r
3324 else if( backTextureSquareInfo[row][column].mode > 0 ) {
\r
3325 /* [AS] Draw the square using a texture bitmap */
\r
3326 HBITMAP hbm = SelectObject( texture_hdc, square_color ? liteBackTexture : darkBackTexture );
\r
3327 int r = row, c = column; // [HGM] do not flip board in flipView
\r
3328 if(flipView) { r = BOARD_HEIGHT-1 - r; c = BOARD_WIDTH-1 - c; }
\r
3331 squareSize, squareSize,
\r
3334 backTextureSquareInfo[r][c].mode,
\r
3335 backTextureSquareInfo[r][c].x,
\r
3336 backTextureSquareInfo[r][c].y );
\r
3338 SelectObject( texture_hdc, hbm );
\r
3340 if (piece != EmptySquare) {
\r
3341 DrawPieceOnDC(hdc, piece, piece_color, -1, x, y, tmphdc);
\r
3345 HBRUSH brush = square_color ? lightSquareBrush : darkSquareBrush;
\r
3347 oldBrush = SelectObject(hdc, brush );
\r
3348 BitBlt(hdc, x, y, squareSize, squareSize, 0, 0, 0, PATCOPY);
\r
3349 SelectObject(hdc, oldBrush);
\r
3350 if (piece != EmptySquare)
\r
3351 DrawPieceOnDC(hdc, piece, piece_color, -1, x, y, tmphdc);
\r
3356 if( texture_hdc != NULL ) {
\r
3357 DeleteDC( texture_hdc );
\r
3361 int saveDiagFlag = 0; FILE *diagFile; // [HGM] diag
\r
3362 void fputDW(FILE *f, int x)
\r
3364 fputc(x & 255, f);
\r
3365 fputc(x>>8 & 255, f);
\r
3366 fputc(x>>16 & 255, f);
\r
3367 fputc(x>>24 & 255, f);
\r
3370 #define MAX_CLIPS 200 /* more than enough */
\r
3373 DrawLogoOnDC(HDC hdc, RECT logoRect, HBITMAP logo)
\r
3375 // HBITMAP bufferBitmap;
\r
3380 int w = 100, h = 50;
\r
3382 if(logo == NULL) {
\r
3383 if(!logoHeight) return;
\r
3384 FillRect( hdc, &logoRect, whitePieceBrush );
\r
3386 // GetClientRect(hwndMain, &Rect);
\r
3387 // bufferBitmap = CreateCompatibleBitmap(hdc, Rect.right-Rect.left+1,
\r
3388 // Rect.bottom-Rect.top+1);
\r
3389 tmphdc = CreateCompatibleDC(hdc);
\r
3390 hbm = SelectObject(tmphdc, logo);
\r
3391 if( GetObject( logo, sizeof(bi), &bi ) > 0 ) {
\r
3395 StretchBlt(hdc, logoRect.left, logoRect.top, logoRect.right - logoRect.left,
\r
3396 logoRect.bottom - logoRect.top, tmphdc, 0, 0, w, h, SRCCOPY);
\r
3397 SelectObject(tmphdc, hbm);
\r
3405 HDC hdc = GetDC(hwndMain);
\r
3406 HBITMAP whiteLogo = (HBITMAP) first.programLogo, blackLogo = (HBITMAP) second.programLogo;
\r
3407 if(appData.autoLogo) {
\r
3409 switch(gameMode) { // pick logos based on game mode
\r
3410 case IcsObserving:
\r
3411 whiteLogo = second.programLogo; // ICS logo
\r
3412 blackLogo = second.programLogo;
\r
3415 case IcsPlayingWhite:
\r
3416 if(!appData.zippyPlay) whiteLogo = userLogo;
\r
3417 blackLogo = second.programLogo; // ICS logo
\r
3419 case IcsPlayingBlack:
\r
3420 whiteLogo = second.programLogo; // ICS logo
\r
3421 blackLogo = appData.zippyPlay ? first.programLogo : userLogo;
\r
3423 case TwoMachinesPlay:
\r
3424 if(first.twoMachinesColor[0] == 'b') {
\r
3425 whiteLogo = second.programLogo;
\r
3426 blackLogo = first.programLogo;
\r
3429 case MachinePlaysWhite:
\r
3430 blackLogo = userLogo;
\r
3432 case MachinePlaysBlack:
\r
3433 whiteLogo = userLogo;
\r
3434 blackLogo = first.programLogo;
\r
3437 DrawLogoOnDC(hdc, leftLogoRect, flipClock ? blackLogo : whiteLogo);
\r
3438 DrawLogoOnDC(hdc, rightLogoRect, flipClock ? whiteLogo : blackLogo);
\r
3439 ReleaseDC(hwndMain, hdc);
\r
3444 UpdateLogos(int display)
\r
3445 { // called after loading new engine(s), in tourney or from menu
\r
3446 LoadLogo(&first, 0, FALSE);
\r
3447 LoadLogo(&second, 1, appData.icsActive);
\r
3448 InitDrawingSizes(-2, 0); // adapt layout of board window to presence/absence of logos
\r
3449 if(display) DisplayLogos();
\r
3452 static HDC hdcSeek;
\r
3454 // [HGM] seekgraph
\r
3455 void DrawSeekAxis( int x, int y, int xTo, int yTo )
\r
3458 HPEN hp = SelectObject( hdcSeek, gridPen );
\r
3459 MoveToEx( hdcSeek, boardRect.left+x, boardRect.top+y, &stPt );
\r
3460 LineTo( hdcSeek, boardRect.left+xTo, boardRect.top+yTo );
\r
3461 SelectObject( hdcSeek, hp );
\r
3464 // front-end wrapper for drawing functions to do rectangles
\r
3465 void DrawSeekBackground( int left, int top, int right, int bottom )
\r
3470 if (hdcSeek == NULL) {
\r
3471 hdcSeek = GetDC(hwndMain);
\r
3472 if (!appData.monoMode) {
\r
3473 SelectPalette(hdcSeek, hPal, FALSE);
\r
3474 RealizePalette(hdcSeek);
\r
3477 hp = SelectObject( hdcSeek, gridPen );
\r
3478 rc.top = boardRect.top+top; rc.left = boardRect.left+left;
\r
3479 rc.bottom = boardRect.top+bottom; rc.right = boardRect.left+right;
\r
3480 FillRect( hdcSeek, &rc, lightSquareBrush );
\r
3481 SelectObject( hdcSeek, hp );
\r
3484 // front-end wrapper for putting text in graph
\r
3485 void DrawSeekText(char *buf, int x, int y)
\r
3488 SetBkMode( hdcSeek, TRANSPARENT );
\r
3489 GetTextExtentPoint32( hdcSeek, buf, strlen(buf), &stSize );
\r
3490 TextOut( hdcSeek, boardRect.left+x-3, boardRect.top+y-stSize.cy/2, buf, strlen(buf) );
\r
3493 void DrawSeekDot(int x, int y, int color)
\r
3495 int square = color & 0x80;
\r
3496 HBRUSH oldBrush = SelectObject(hdcSeek,
\r
3497 color == 0 ? markerBrush : color == 1 ? darkSquareBrush : explodeBrush);
\r
3500 Rectangle(hdcSeek, boardRect.left+x - squareSize/9, boardRect.top+y - squareSize/9,
\r
3501 boardRect.left+x + squareSize/9, boardRect.top+y + squareSize/9);
\r
3503 Ellipse(hdcSeek, boardRect.left+x - squareSize/8, boardRect.top+y - squareSize/8,
\r
3504 boardRect.left+x + squareSize/8, boardRect.top+y + squareSize/8);
\r
3505 SelectObject(hdcSeek, oldBrush);
\r
3509 HDCDrawPosition(HDC hdc, BOOLEAN repaint, Board board)
\r
3511 static Board lastReq[2], lastDrawn[2];
\r
3512 static HighlightInfo lastDrawnHighlight, lastDrawnPremove;
\r
3513 static int lastDrawnFlipView = 0;
\r
3514 static int lastReqValid[2] = {0, 0}, lastDrawnValid[2] = {0, 0};
\r
3515 int releaseDC, x, y, x2, y2, row, column, num_clips = 0, i;
\r
3518 HBITMAP bufferBitmap;
\r
3519 HBITMAP oldBitmap;
\r
3521 HRGN clips[MAX_CLIPS];
\r
3522 ChessSquare dragged_piece = EmptySquare;
\r
3523 int nr = twoBoards*partnerUp;
\r
3525 /* I'm undecided on this - this function figures out whether a full
\r
3526 * repaint is necessary on its own, so there's no real reason to have the
\r
3527 * caller tell it that. I think this can safely be set to FALSE - but
\r
3528 * if we trust the callers not to request full repaints unnessesarily, then
\r
3529 * we could skip some clipping work. In other words, only request a full
\r
3530 * redraw when the majority of pieces have changed positions (ie. flip,
\r
3531 * gamestart and similar) --Hawk
\r
3533 Boolean fullrepaint = repaint;
\r
3535 if(DrawSeekGraph()) return; // [HG} seekgraph: suppress printing board if seek graph up
\r
3537 if( DrawPositionNeedsFullRepaint() ) {
\r
3538 fullrepaint = TRUE;
\r
3541 if (board == NULL) {
\r
3542 if (!lastReqValid[nr]) {
\r
3545 board = lastReq[nr];
\r
3547 CopyBoard(lastReq[nr], board);
\r
3548 lastReqValid[nr] = 1;
\r
3551 if (doingSizing) {
\r
3555 if (IsIconic(hwndMain)) {
\r
3559 if (hdc == NULL) {
\r
3560 hdc = GetDC(hwndMain);
\r
3561 if (!appData.monoMode) {
\r
3562 SelectPalette(hdc, hPal, FALSE);
\r
3563 RealizePalette(hdc);
\r
3567 releaseDC = FALSE;
\r
3570 /* Create some work-DCs */
\r
3571 hdcmem = CreateCompatibleDC(hdc);
\r
3572 tmphdc = CreateCompatibleDC(hdc);
\r
3574 /* If dragging is in progress, we temporarely remove the piece */
\r
3575 /* [HGM] or temporarily decrease count if stacked */
\r
3576 /* !! Moved to before board compare !! */
\r
3577 if (dragInfo.from.x >= 0 && dragInfo.pos.x >= 0) {
\r
3578 dragged_piece = board[dragInfo.from.y][dragInfo.from.x];
\r
3579 if(dragInfo.from.x == BOARD_LEFT-2 ) {
\r
3580 if(--board[dragInfo.from.y][dragInfo.from.x+1] == 0 )
\r
3581 board[dragInfo.from.y][dragInfo.from.x] = EmptySquare;
\r
3583 if(dragInfo.from.x == BOARD_RGHT+1) {
\r
3584 if(--board[dragInfo.from.y][dragInfo.from.x-1] == 0 )
\r
3585 board[dragInfo.from.y][dragInfo.from.x] = EmptySquare;
\r
3587 board[dragInfo.from.y][dragInfo.from.x] = gatingPiece;
\r
3590 /* Figure out which squares need updating by comparing the
\r
3591 * newest board with the last drawn board and checking if
\r
3592 * flipping has changed.
\r
3594 if (!fullrepaint && lastDrawnValid[nr] && (nr == 1 || lastDrawnFlipView == flipView)) {
\r
3595 for (row = 0; row < BOARD_HEIGHT; row++) { /* [HGM] true size, not 8 */
\r
3596 for (column = 0; column < BOARD_WIDTH; column++) {
\r
3597 if (lastDrawn[nr][row][column] != board[row][column]) {
\r
3598 SquareToPos(row, column, &x, &y);
\r
3599 clips[num_clips++] =
\r
3600 CreateRectRgn(x, y, x + squareSize, y + squareSize);
\r
3604 if(nr == 0) { // [HGM] dual: no highlights on second board
\r
3605 for (i=0; i<2; i++) {
\r
3606 if (lastDrawnHighlight.sq[i].x != highlightInfo.sq[i].x ||
\r
3607 lastDrawnHighlight.sq[i].y != highlightInfo.sq[i].y) {
\r
3608 if (lastDrawnHighlight.sq[i].x >= 0 &&
\r
3609 lastDrawnHighlight.sq[i].y >= 0) {
\r
3610 SquareToPos(lastDrawnHighlight.sq[i].y,
\r
3611 lastDrawnHighlight.sq[i].x, &x, &y);
\r
3612 clips[num_clips++] =
\r
3613 CreateRectRgn(x - lineGap, y - lineGap,
\r
3614 x + squareSize + lineGap, y + squareSize + lineGap);
\r
3616 if (highlightInfo.sq[i].x >= 0 && highlightInfo.sq[i].y >= 0) {
\r
3617 SquareToPos(highlightInfo.sq[i].y, highlightInfo.sq[i].x, &x, &y);
\r
3618 clips[num_clips++] =
\r
3619 CreateRectRgn(x - lineGap, y - lineGap,
\r
3620 x + squareSize + lineGap, y + squareSize + lineGap);
\r
3624 for (i=0; i<2; i++) {
\r
3625 if (lastDrawnPremove.sq[i].x != premoveHighlightInfo.sq[i].x ||
\r
3626 lastDrawnPremove.sq[i].y != premoveHighlightInfo.sq[i].y) {
\r
3627 if (lastDrawnPremove.sq[i].x >= 0 &&
\r
3628 lastDrawnPremove.sq[i].y >= 0) {
\r
3629 SquareToPos(lastDrawnPremove.sq[i].y,
\r
3630 lastDrawnPremove.sq[i].x, &x, &y);
\r
3631 clips[num_clips++] =
\r
3632 CreateRectRgn(x - lineGap, y - lineGap,
\r
3633 x + squareSize + lineGap, y + squareSize + lineGap);
\r
3635 if (premoveHighlightInfo.sq[i].x >= 0 &&
\r
3636 premoveHighlightInfo.sq[i].y >= 0) {
\r
3637 SquareToPos(premoveHighlightInfo.sq[i].y,
\r
3638 premoveHighlightInfo.sq[i].x, &x, &y);
\r
3639 clips[num_clips++] =
\r
3640 CreateRectRgn(x - lineGap, y - lineGap,
\r
3641 x + squareSize + lineGap, y + squareSize + lineGap);
\r
3645 } else { // nr == 1
\r
3646 partnerHighlightInfo.sq[0].y = board[EP_STATUS-4];
\r
3647 partnerHighlightInfo.sq[0].x = board[EP_STATUS-3];
\r
3648 partnerHighlightInfo.sq[1].y = board[EP_STATUS-2];
\r
3649 partnerHighlightInfo.sq[1].x = board[EP_STATUS-1];
\r
3650 for (i=0; i<2; i++) {
\r
3651 if (partnerHighlightInfo.sq[i].x >= 0 &&
\r
3652 partnerHighlightInfo.sq[i].y >= 0) {
\r
3653 SquareToPos(partnerHighlightInfo.sq[i].y,
\r
3654 partnerHighlightInfo.sq[i].x, &x, &y);
\r
3655 clips[num_clips++] =
\r
3656 CreateRectRgn(x - lineGap, y - lineGap,
\r
3657 x + squareSize + lineGap, y + squareSize + lineGap);
\r
3659 if (oldPartnerHighlight.sq[i].x >= 0 &&
\r
3660 oldPartnerHighlight.sq[i].y >= 0) {
\r
3661 SquareToPos(oldPartnerHighlight.sq[i].y,
\r
3662 oldPartnerHighlight.sq[i].x, &x, &y);
\r
3663 clips[num_clips++] =
\r
3664 CreateRectRgn(x - lineGap, y - lineGap,
\r
3665 x + squareSize + lineGap, y + squareSize + lineGap);
\r
3670 fullrepaint = TRUE;
\r
3673 /* Create a buffer bitmap - this is the actual bitmap
\r
3674 * being written to. When all the work is done, we can
\r
3675 * copy it to the real DC (the screen). This avoids
\r
3676 * the problems with flickering.
\r
3678 GetClientRect(hwndMain, &Rect);
\r
3679 bufferBitmap = CreateCompatibleBitmap(hdc, Rect.right-Rect.left+1,
\r
3680 Rect.bottom-Rect.top+1);
\r
3681 oldBitmap = SelectObject(hdcmem, bufferBitmap);
\r
3682 if (!appData.monoMode) {
\r
3683 SelectPalette(hdcmem, hPal, FALSE);
\r
3686 /* Create clips for dragging */
\r
3687 if (!fullrepaint) {
\r
3688 if (dragInfo.from.x >= 0) {
\r
3689 SquareToPos(dragInfo.from.y, dragInfo.from.x, &x, &y);
\r
3690 clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);
\r
3692 if (dragInfo.start.x >= 0) {
\r
3693 SquareToPos(dragInfo.start.y, dragInfo.start.x, &x, &y);
\r
3694 clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);
\r
3696 if (dragInfo.pos.x >= 0) {
\r
3697 x = dragInfo.pos.x - squareSize / 2;
\r
3698 y = dragInfo.pos.y - squareSize / 2;
\r
3699 clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);
\r
3701 if (dragInfo.lastpos.x >= 0) {
\r
3702 x = dragInfo.lastpos.x - squareSize / 2;
\r
3703 y = dragInfo.lastpos.y - squareSize / 2;
\r
3704 clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);
\r
3708 /* Are we animating a move?
\r
3710 * - remove the piece from the board (temporarely)
\r
3711 * - calculate the clipping region
\r
3713 if (!fullrepaint) {
\r
3714 if (animInfo.piece != EmptySquare) {
\r
3715 board[animInfo.from.y][animInfo.from.x] = EmptySquare;
\r
3716 x = boardRect.left + animInfo.lastpos.x;
\r
3717 y = boardRect.top + animInfo.lastpos.y;
\r
3718 x2 = boardRect.left + animInfo.pos.x;
\r
3719 y2 = boardRect.top + animInfo.pos.y;
\r
3720 clips[num_clips++] = CreateRectRgn(MIN(x,x2), MIN(y,y2), MAX(x,x2)+squareSize, MAX(y,y2)+squareSize);
\r
3721 /* Slight kludge. The real problem is that after AnimateMove is
\r
3722 done, the position on the screen does not match lastDrawn.
\r
3723 This currently causes trouble only on e.p. captures in
\r
3724 atomic, where the piece moves to an empty square and then
\r
3725 explodes. The old and new positions both had an empty square
\r
3726 at the destination, but animation has drawn a piece there and
\r
3727 we have to remember to erase it. [HGM] moved until after setting lastDrawn */
\r
3728 lastDrawn[0][animInfo.to.y][animInfo.to.x] = animInfo.piece;
\r
3732 /* No clips? Make sure we have fullrepaint set to TRUE */
\r
3733 if (num_clips == 0)
\r
3734 fullrepaint = TRUE;
\r
3736 /* Set clipping on the memory DC */
\r
3737 if (!fullrepaint) {
\r
3738 SelectClipRgn(hdcmem, clips[0]);
\r
3739 for (x = 1; x < num_clips; x++) {
\r
3740 if (ExtSelectClipRgn(hdcmem, clips[x], RGN_OR) == ERROR)
\r
3741 abort(); // this should never ever happen!
\r
3745 /* Do all the drawing to the memory DC */
\r
3746 if(explodeInfo.radius) { // [HGM] atomic
\r
3748 int x, y, r=(explodeInfo.radius * squareSize)/100;
\r
3749 ChessSquare piece = board[explodeInfo.fromY][explodeInfo.fromX];
\r
3750 board[explodeInfo.fromY][explodeInfo.fromX] = EmptySquare; // suppress display of capturer
\r
3751 SquareToPos(explodeInfo.toY, explodeInfo.toX, &x, &y);
\r
3752 x += squareSize/2;
\r
3753 y += squareSize/2;
\r
3754 if(!fullrepaint) {
\r
3755 clips[num_clips] = CreateRectRgn(x-r, y-r, x+r, y+r);
\r
3756 ExtSelectClipRgn(hdcmem, clips[num_clips++], RGN_OR);
\r
3758 DrawGridOnDC(hdcmem);
\r
3759 DrawHighlightsOnDC(hdcmem, &highlightInfo, HIGHLIGHT_PEN);
\r
3760 DrawHighlightsOnDC(hdcmem, &premoveHighlightInfo, PREMOVE_PEN);
\r
3761 DrawBoardOnDC(hdcmem, board, tmphdc);
\r
3762 board[explodeInfo.fromY][explodeInfo.fromX] = piece;
\r
3763 oldBrush = SelectObject(hdcmem, explodeBrush);
\r
3764 Ellipse(hdcmem, x-r, y-r, x+r, y+r);
\r
3765 SelectObject(hdcmem, oldBrush);
\r
3767 DrawGridOnDC(hdcmem);
\r
3768 if(nr == 0) { // [HGM] dual: decide which highlights to draw
\r
3769 DrawHighlightsOnDC(hdcmem, &highlightInfo, HIGHLIGHT_PEN);
\r
3770 DrawHighlightsOnDC(hdcmem, &premoveHighlightInfo, PREMOVE_PEN);
\r
3772 DrawHighlightsOnDC(hdcmem, &partnerHighlightInfo, HIGHLIGHT_PEN);
\r
3773 oldPartnerHighlight = partnerHighlightInfo;
\r
3775 DrawBoardOnDC(hdcmem, board, tmphdc);
\r
3777 if(nr == 0) // [HGM] dual: markers only on left board
\r
3778 for (row = 0; row < BOARD_HEIGHT; row++) {
\r
3779 for (column = 0; column < BOARD_WIDTH; column++) {
\r
3780 if (marker[row][column]) { // marker changes only occur with full repaint!
\r
3781 HBRUSH oldBrush = SelectObject(hdcmem,
\r
3782 marker[row][column] == 2 ? markerBrush : explodeBrush);
\r
3783 SquareToPos(row, column, &x, &y);
\r
3784 Ellipse(hdcmem, x + squareSize/4, y + squareSize/4,
\r
3785 x + 3*squareSize/4, y + 3*squareSize/4);
\r
3786 SelectObject(hdcmem, oldBrush);
\r
3791 if( appData.highlightMoveWithArrow ) {
\r
3792 DrawArrowHighlight(hdcmem);
\r
3795 DrawCoordsOnDC(hdcmem);
\r
3797 CopyBoard(lastDrawn[nr], board); /* [HGM] Moved to here from end of routine, */
\r
3798 /* to make sure lastDrawn contains what is actually drawn */
\r
3800 /* Put the dragged piece back into place and draw it (out of place!) */
\r
3801 if (dragged_piece != EmptySquare) {
\r
3802 /* [HGM] or restack */
\r
3803 if(dragInfo.from.x == BOARD_LEFT-2 )
\r
3804 board[dragInfo.from.y][dragInfo.from.x+1]++;
\r
3806 if(dragInfo.from.x == BOARD_RGHT+1 )
\r
3807 board[dragInfo.from.y][dragInfo.from.x-1]++;
\r
3808 board[dragInfo.from.y][dragInfo.from.x] = dragged_piece;
\r
3809 x = dragInfo.pos.x - squareSize / 2;
\r
3810 y = dragInfo.pos.y - squareSize / 2;
\r
3811 DrawPieceOnDC(hdcmem, dragInfo.piece,
\r
3812 ((int) dragInfo.piece < (int) BlackPawn),
\r
3813 (dragInfo.from.y + dragInfo.from.x) % 2, x, y, tmphdc);
\r
3816 /* Put the animated piece back into place and draw it */
\r
3817 if (animInfo.piece != EmptySquare) {
\r
3818 board[animInfo.from.y][animInfo.from.x] = animInfo.piece;
\r
3819 x = boardRect.left + animInfo.pos.x;
\r
3820 y = boardRect.top + animInfo.pos.y;
\r
3821 DrawPieceOnDC(hdcmem, animInfo.piece,
\r
3822 ((int) animInfo.piece < (int) BlackPawn),
\r
3823 (animInfo.from.y + animInfo.from.x) % 2, x, y, tmphdc);
\r
3826 /* Release the bufferBitmap by selecting in the old bitmap
\r
3827 * and delete the memory DC
\r
3829 SelectObject(hdcmem, oldBitmap);
\r
3832 /* Set clipping on the target DC */
\r
3833 if (!fullrepaint) {
\r
3834 if(nr == 1) for (x = 0; x < num_clips; x++) { // [HGM] dual: translate clips
\r
3836 GetRgnBox(clips[x], &rect);
\r
3837 DeleteObject(clips[x]);
\r
3838 clips[x] = CreateRectRgn(rect.left + wpMain.width/2, rect.top,
\r
3839 rect.right + wpMain.width/2, rect.bottom);
\r
3841 SelectClipRgn(hdc, clips[0]);
\r
3842 for (x = 1; x < num_clips; x++) {
\r
3843 if (ExtSelectClipRgn(hdc, clips[x], RGN_OR) == ERROR)
\r
3844 abort(); // this should never ever happen!
\r
3848 /* Copy the new bitmap onto the screen in one go.
\r
3849 * This way we avoid any flickering
\r
3851 oldBitmap = SelectObject(tmphdc, bufferBitmap);
\r
3852 BitBlt(hdc, boardRect.left + twoBoards*partnerUp*wpMain.width/2, boardRect.top, // [HGM] dual
\r
3853 boardRect.right - boardRect.left,
\r
3854 boardRect.bottom - boardRect.top,
\r
3855 tmphdc, boardRect.left, boardRect.top, SRCCOPY);
\r
3856 if(saveDiagFlag) {
\r
3857 BITMAP b; int i, j=0, m, w, wb, fac=0; char *pData;
\r
3858 BITMAPINFOHEADER bih; int color[16], nrColors=0;
\r
3860 GetObject(bufferBitmap, sizeof(b), &b);
\r
3861 if(pData = malloc(b.bmWidthBytes*b.bmHeight + 10000)) {
\r
3862 bih.biSize = sizeof(BITMAPINFOHEADER);
\r
3863 bih.biWidth = b.bmWidth;
\r
3864 bih.biHeight = b.bmHeight;
\r
3866 bih.biBitCount = b.bmBitsPixel;
\r
3867 bih.biCompression = 0;
\r
3868 bih.biSizeImage = b.bmWidthBytes*b.bmHeight;
\r
3869 bih.biXPelsPerMeter = 0;
\r
3870 bih.biYPelsPerMeter = 0;
\r
3871 bih.biClrUsed = 0;
\r
3872 bih.biClrImportant = 0;
\r
3873 // fprintf(diagFile, "t=%d\nw=%d\nh=%d\nB=%d\nP=%d\nX=%d\n",
\r
3874 // b.bmType, b.bmWidth, b.bmHeight, b.bmWidthBytes, b.bmPlanes, b.bmBitsPixel);
\r
3875 GetDIBits(tmphdc,bufferBitmap,0,b.bmHeight,pData,(BITMAPINFO*)&bih,DIB_RGB_COLORS);
\r
3876 // fprintf(diagFile, "%8x\n", (int) pData);
\r
3878 wb = b.bmWidthBytes;
\r
3880 for(i=0; i<wb*(b.bmHeight - boardRect.top + OUTER_MARGIN)>>2; i++) {
\r
3881 int k = ((int*) pData)[i];
\r
3882 for(j=0; j<nrColors; j++) if(color[j] == k) break;
\r
3883 if(j >= 16) break;
\r
3885 if(j >= nrColors) nrColors = j+1;
\r
3887 if(j<16) { // 16 colors is enough. Compress to 4 bits per pixel
\r
3889 for(i=0; i<b.bmHeight - boardRect.top + OUTER_MARGIN; i++) {
\r
3890 for(w=0; w<(wb>>2); w+=2) {
\r
3891 int k = ((int*) pData)[(wb*i>>2) + w];
\r
3892 for(j=0; j<nrColors; j++) if(color[j] == k) break;
\r
3893 k = ((int*) pData)[(wb*i>>2) + w + 1];
\r
3894 for(m=0; m<nrColors; m++) if(color[m] == k) break;
\r
3895 pData[p++] = m | j<<4;
\r
3897 while(p&3) pData[p++] = 0;
\r
3900 wb = ((wb+31)>>5)<<2;
\r
3902 // write BITMAPFILEHEADER
\r
3903 fprintf(diagFile, "BM");
\r
3904 fputDW(diagFile, wb*(b.bmHeight - boardRect.top + OUTER_MARGIN)+0x36 + (fac?64:0));
\r
3905 fputDW(diagFile, 0);
\r
3906 fputDW(diagFile, 0x36 + (fac?64:0));
\r
3907 // write BITMAPINFOHEADER
\r
3908 fputDW(diagFile, 40);
\r
3909 fputDW(diagFile, b.bmWidth);
\r
3910 fputDW(diagFile, b.bmHeight - boardRect.top + OUTER_MARGIN);
\r
3911 if(fac) fputDW(diagFile, 0x040001); // planes and bits/pixel
\r
3912 else fputDW(diagFile, 0x200001); // planes and bits/pixel
\r
3913 fputDW(diagFile, 0);
\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 // write color table
\r
3921 for(i=0; i<16; i++) fputDW(diagFile, color[i]);
\r
3922 // write bitmap data
\r
3923 for(i=0; i<wb*(b.bmHeight - boardRect.top + OUTER_MARGIN); i++)
\r
3924 fputc(pData[i], diagFile);
\r
3929 SelectObject(tmphdc, oldBitmap);
\r
3931 /* Massive cleanup */
\r
3932 for (x = 0; x < num_clips; x++)
\r
3933 DeleteObject(clips[x]);
\r
3936 DeleteObject(bufferBitmap);
\r
3939 ReleaseDC(hwndMain, hdc);
\r
3941 if (lastDrawnFlipView != flipView && nr == 0) {
\r
3943 CheckMenuItem(GetMenu(hwndMain),IDM_FlipView, MF_BYCOMMAND|MF_CHECKED);
\r
3945 CheckMenuItem(GetMenu(hwndMain),IDM_FlipView, MF_BYCOMMAND|MF_UNCHECKED);
\r
3948 /* CopyBoard(lastDrawn, board);*/
\r
3949 lastDrawnHighlight = highlightInfo;
\r
3950 lastDrawnPremove = premoveHighlightInfo;
\r
3951 lastDrawnFlipView = flipView;
\r
3952 lastDrawnValid[nr] = 1;
\r
3955 /* [HGM] diag: Save the current board display to the given open file and close the file */
\r
3960 saveDiagFlag = 1; diagFile = f;
\r
3961 HDCDrawPosition(NULL, TRUE, NULL);
\r
3969 /*---------------------------------------------------------------------------*\
\r
3970 | CLIENT PAINT PROCEDURE
\r
3971 | This is the main event-handler for the WM_PAINT message.
\r
3973 \*---------------------------------------------------------------------------*/
\r
3975 PaintProc(HWND hwnd)
\r
3981 if((hdc = BeginPaint(hwnd, &ps))) {
\r
3982 if (IsIconic(hwnd)) {
\r
3983 DrawIcon(hdc, 2, 2, iconCurrent);
\r
3985 if (!appData.monoMode) {
\r
3986 SelectPalette(hdc, hPal, FALSE);
\r
3987 RealizePalette(hdc);
\r
3989 HDCDrawPosition(hdc, 1, NULL);
\r
3990 if(twoBoards) { // [HGM] dual: also redraw other board in other orientation
\r
3991 flipView = !flipView; partnerUp = !partnerUp;
\r
3992 HDCDrawPosition(hdc, 1, NULL);
\r
3993 flipView = !flipView; partnerUp = !partnerUp;
\r
3996 SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);
\r
3997 ExtTextOut(hdc, messageRect.left, messageRect.top,
\r
3998 ETO_CLIPPED|ETO_OPAQUE,
\r
3999 &messageRect, messageText, strlen(messageText), NULL);
\r
4000 SelectObject(hdc, oldFont);
\r
4001 DisplayBothClocks();
\r
4004 EndPaint(hwnd,&ps);
\r
4012 * If the user selects on a border boundary, return -1; if off the board,
\r
4013 * return -2. Otherwise map the event coordinate to the square.
\r
4014 * The offset boardRect.left or boardRect.top must already have been
\r
4015 * subtracted from x.
\r
4017 int EventToSquare(x, limit)
\r
4025 if ((x % (squareSize + lineGap)) >= squareSize)
\r
4027 x /= (squareSize + lineGap);
\r
4039 DropEnable dropEnables[] = {
\r
4040 { 'P', DP_Pawn, N_("Pawn") },
\r
4041 { 'N', DP_Knight, N_("Knight") },
\r
4042 { 'B', DP_Bishop, N_("Bishop") },
\r
4043 { 'R', DP_Rook, N_("Rook") },
\r
4044 { 'Q', DP_Queen, N_("Queen") },
\r
4048 SetupDropMenu(HMENU hmenu)
\r
4050 int i, count, enable;
\r
4052 extern char white_holding[], black_holding[];
\r
4053 char item[MSG_SIZ];
\r
4055 for (i=0; i<sizeof(dropEnables)/sizeof(DropEnable); i++) {
\r
4056 p = strchr(gameMode == IcsPlayingWhite ? white_holding : black_holding,
\r
4057 dropEnables[i].piece);
\r
4059 while (p && *p++ == dropEnables[i].piece) count++;
\r
4060 snprintf(item, MSG_SIZ, "%s %d", T_(dropEnables[i].name), count);
\r
4061 enable = count > 0 || !appData.testLegality
\r
4062 /*!!temp:*/ || (gameInfo.variant == VariantCrazyhouse
\r
4063 && !appData.icsActive);
\r
4064 ModifyMenu(hmenu, dropEnables[i].command,
\r
4065 MF_BYCOMMAND | (enable ? MF_ENABLED : MF_GRAYED) | MF_STRING,
\r
4066 dropEnables[i].command, item);
\r
4070 void DragPieceBegin(int x, int y, Boolean instantly)
\r
4072 dragInfo.lastpos.x = boardRect.left + x;
\r
4073 dragInfo.lastpos.y = boardRect.top + y;
\r
4074 if(instantly) dragInfo.pos = dragInfo.lastpos;
\r
4075 dragInfo.from.x = fromX;
\r
4076 dragInfo.from.y = fromY;
\r
4077 dragInfo.piece = boards[currentMove][fromY][fromX];
\r
4078 dragInfo.start = dragInfo.from;
\r
4079 SetCapture(hwndMain);
\r
4082 void DragPieceEnd(int x, int y)
\r
4085 dragInfo.start.x = dragInfo.start.y = -1;
\r
4086 dragInfo.from = dragInfo.start;
\r
4087 dragInfo.pos = dragInfo.lastpos = dragInfo.start;
\r
4090 void ChangeDragPiece(ChessSquare piece)
\r
4092 dragInfo.piece = piece;
\r
4095 /* Event handler for mouse messages */
\r
4097 MouseEvent(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
\r
4101 static int recursive = 0;
\r
4103 BOOLEAN forceFullRepaint = IsFullRepaintPreferrable(); /* [AS] */
\r
4106 if (message == WM_MBUTTONUP) {
\r
4107 /* Hideous kludge to fool TrackPopupMenu into paying attention
\r
4108 to the middle button: we simulate pressing the left button too!
\r
4110 PostMessage(hwnd, WM_LBUTTONDOWN, wParam, lParam);
\r
4111 PostMessage(hwnd, WM_LBUTTONUP, wParam, lParam);
\r
4117 pt.x = LOWORD(lParam);
\r
4118 pt.y = HIWORD(lParam);
\r
4119 x = EventToSquare(pt.x - boardRect.left, BOARD_WIDTH);
\r
4120 y = EventToSquare(pt.y - boardRect.top, BOARD_HEIGHT);
\r
4121 if (!flipView && y >= 0) {
\r
4122 y = BOARD_HEIGHT - 1 - y;
\r
4124 if (flipView && x >= 0) {
\r
4125 x = BOARD_WIDTH - 1 - x;
\r
4128 shiftKey = GetKeyState(VK_SHIFT) < 0; // [HGM] remember last shift status
\r
4130 switch (message) {
\r
4131 case WM_LBUTTONDOWN:
\r
4132 if (PtInRect((LPRECT) &whiteRect, pt)) {
\r
4133 ClockClick(flipClock);
\r
4134 } else if (PtInRect((LPRECT) &blackRect, pt)) {
\r
4135 ClockClick(!flipClock);
\r
4137 dragInfo.start.x = dragInfo.start.y = -1;
\r
4138 dragInfo.from = dragInfo.start;
\r
4139 if(fromX == -1 && frozen) { // not sure where this is for
\r
4140 fromX = fromY = -1;
\r
4141 DrawPosition(forceFullRepaint || FALSE, NULL); /* [AS] */
\r
4144 LeftClick(Press, pt.x - boardRect.left, pt.y - boardRect.top);
\r
4145 DrawPosition(TRUE, NULL);
\r
4148 case WM_LBUTTONUP:
\r
4149 LeftClick(Release, pt.x - boardRect.left, pt.y - boardRect.top);
\r
4150 DrawPosition(TRUE, NULL);
\r
4153 case WM_MOUSEMOVE:
\r
4154 if(SeekGraphClick(Press, pt.x - boardRect.left, pt.y - boardRect.top, 1)) break;
\r
4155 if(PromoScroll(pt.x - boardRect.left, pt.y - boardRect.top)) break;
\r
4156 MovePV(pt.x - boardRect.left, pt.y - boardRect.top, boardRect.bottom - boardRect.top);
\r
4157 if ((appData.animateDragging || appData.highlightDragging)
\r
4158 && (wParam & MK_LBUTTON)
\r
4159 && dragInfo.from.x >= 0)
\r
4161 BOOL full_repaint = FALSE;
\r
4163 if (appData.animateDragging) {
\r
4164 dragInfo.pos = pt;
\r
4166 if (appData.highlightDragging) {
\r
4167 SetHighlights(fromX, fromY, x, y);
\r
4168 if( IsDrawArrowEnabled() && (x < 0 || x >= BOARD_WIDTH || y < 0 || y >= BOARD_HEIGHT) ) {
\r
4169 full_repaint = TRUE;
\r
4173 DrawPosition( full_repaint, NULL);
\r
4175 dragInfo.lastpos = dragInfo.pos;
\r
4179 case WM_MOUSEWHEEL: // [DM]
\r
4180 { static int lastDir = 0; // [HGM] build in some hysteresis to avoid spurious events
\r
4181 /* Mouse Wheel is being rolled forward
\r
4182 * Play moves forward
\r
4184 if((short)HIWORD(wParam) < 0 && currentMove < forwardMostMove)
\r
4185 { if(lastDir == 1) ForwardEvent(); else lastDir = 1; } // [HGM] suppress first event in direction
\r
4186 /* Mouse Wheel is being rolled backward
\r
4187 * Play moves backward
\r
4189 if((short)HIWORD(wParam) > 0 && currentMove > backwardMostMove)
\r
4190 { if(lastDir == -1) BackwardEvent(); else lastDir = -1; }
\r
4194 case WM_MBUTTONUP:
\r
4195 case WM_RBUTTONUP:
\r
4197 RightClick(Release, pt.x - boardRect.left, pt.y - boardRect.top, &fromX, &fromY);
\r
4200 case WM_MBUTTONDOWN:
\r
4201 case WM_RBUTTONDOWN:
\r
4204 fromX = fromY = -1;
\r
4205 dragInfo.pos.x = dragInfo.pos.y = -1;
\r
4206 dragInfo.start.x = dragInfo.start.y = -1;
\r
4207 dragInfo.from = dragInfo.start;
\r
4208 dragInfo.lastpos = dragInfo.pos;
\r
4209 if (appData.highlightDragging) {
\r
4210 ClearHighlights();
\r
4213 /* [HGM] right mouse button in clock area edit-game mode ups clock */
\r
4214 if (PtInRect((LPRECT) &whiteRect, pt)) {
\r
4215 if (gameMode == EditGame || GetKeyState(VK_SHIFT) < 0) AdjustClock(flipClock, 1);
\r
4216 } else if (PtInRect((LPRECT) &blackRect, pt)) {
\r
4217 if (gameMode == EditGame || GetKeyState(VK_SHIFT) < 0) AdjustClock(!flipClock, 1);
\r
4221 DrawPosition(TRUE, NULL);
\r
4223 menuNr = RightClick(Press, pt.x - boardRect.left, pt.y - boardRect.top, &fromX, &fromY);
\r
4226 if (message == WM_MBUTTONDOWN) {
\r
4227 buttonCount = 3; /* even if system didn't think so */
\r
4228 if (wParam & MK_SHIFT)
\r
4229 MenuPopup(hwnd, pt, LoadMenu(hInst, "BlackPieceMenu"), -1);
\r
4231 MenuPopup(hwnd, pt, LoadMenu(hInst, "WhitePieceMenu"), -1);
\r
4232 } else { /* message == WM_RBUTTONDOWN */
\r
4233 /* Just have one menu, on the right button. Windows users don't
\r
4234 think to try the middle one, and sometimes other software steals
\r
4235 it, or it doesn't really exist. */
\r
4236 if(gameInfo.variant != VariantShogi)
\r
4237 MenuPopup(hwnd, pt, LoadMenu(hInst, "PieceMenu"), -1);
\r
4239 MenuPopup(hwnd, pt, LoadMenu(hInst, "ShogiPieceMenu"), -1);
\r
4243 SetCapture(hwndMain);
4246 hmenu = LoadMenu(hInst, "DropPieceMenu");
\r
4247 SetupDropMenu(hmenu);
\r
4248 MenuPopup(hwnd, pt, hmenu, -1);
\r
4258 /* Preprocess messages for buttons in main window */
\r
4260 ButtonProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
\r
4262 int id = GetWindowLongPtr(hwnd, GWLP_ID);
\r
4265 for (i=0; i<N_BUTTONS; i++) {
\r
4266 if (buttonDesc[i].id == id) break;
\r
4268 if (i == N_BUTTONS) return 0;
\r
4269 switch (message) {
\r
4274 dir = (wParam == VK_LEFT) ? -1 : 1;
\r
4275 SetFocus(buttonDesc[(i + dir + N_BUTTONS) % N_BUTTONS].hwnd);
\r
4282 SendMessage(hwndMain, WM_COMMAND, MAKEWPARAM(buttonDesc[i].id, 0), 0);
\r
4285 if (appData.icsActive && (isalpha((char)wParam) || wParam == '0')) {
\r
4286 // [HGM] movenum: only letters or leading zero should go to ICS input
\r
4287 HWND h = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
4288 if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);
\r
4290 SendMessage(h, WM_CHAR, wParam, lParam);
\r
4292 } else if (isalpha((char)wParam) || isdigit((char)wParam)){
\r
4293 TypeInEvent((char)wParam);
\r
4299 return CallWindowProc(buttonDesc[i].wndproc, hwnd, message, wParam, lParam);
\r
4302 /* Process messages for Promotion dialog box */
\r
4304 Promotion(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
4308 switch (message) {
\r
4309 case WM_INITDIALOG: /* message: initialize dialog box */
\r
4310 /* Center the dialog over the application window */
\r
4311 CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));
\r
4312 Translate(hDlg, DLG_PromotionKing);
\r
4313 ShowWindow(GetDlgItem(hDlg, PB_King),
\r
4314 (!appData.testLegality || gameInfo.variant == VariantSuicide ||
\r
4315 gameInfo.variant == VariantSpartan && !WhiteOnMove(currentMove) ||
\r
4316 gameInfo.variant == VariantGiveaway || gameInfo.variant == VariantSuper ) ?
\r
4317 SW_SHOW : SW_HIDE);
\r
4318 /* [HGM] Only allow C & A promotions if these pieces are defined */
\r
4319 ShowWindow(GetDlgItem(hDlg, PB_Archbishop),
\r
4320 ((PieceToChar(WhiteAngel) >= 'A' && WhiteOnMove(currentMove) &&
\r
4321 PieceToChar(WhiteAngel) != '~') ||
\r
4322 (PieceToChar(BlackAngel) >= 'A' && !WhiteOnMove(currentMove) &&
\r
4323 PieceToChar(BlackAngel) != '~') ) ?
\r
4324 SW_SHOW : SW_HIDE);
\r
4325 ShowWindow(GetDlgItem(hDlg, PB_Chancellor),
\r
4326 ((PieceToChar(WhiteMarshall) >= 'A' && WhiteOnMove(currentMove) &&
\r
4327 PieceToChar(WhiteMarshall) != '~') ||
\r
4328 (PieceToChar(BlackMarshall) >= 'A' && !WhiteOnMove(currentMove) &&
\r
4329 PieceToChar(BlackMarshall) != '~') ) ?
\r
4330 SW_SHOW : SW_HIDE);
\r
4331 /* [HGM] Hide B & R button in Shogi, use Q as promote, N as defer */
\r
4332 ShowWindow(GetDlgItem(hDlg, PB_Rook),
\r
4333 gameInfo.variant != VariantShogi ?
\r
4334 SW_SHOW : SW_HIDE);
\r
4335 ShowWindow(GetDlgItem(hDlg, PB_Bishop),
\r
4336 gameInfo.variant != VariantShogi ?
\r
4337 SW_SHOW : SW_HIDE);
\r
4338 if(gameInfo.variant == VariantShogi) {
\r
4339 SetDlgItemText(hDlg, PB_Queen, "YES");
\r
4340 SetDlgItemText(hDlg, PB_Knight, "NO");
\r
4341 SetWindowText(hDlg, "Promote?");
\r
4343 ShowWindow(GetDlgItem(hDlg, IDC_Centaur),
\r
4344 gameInfo.variant == VariantSuper ?
\r
4345 SW_SHOW : SW_HIDE);
\r
4348 case WM_COMMAND: /* message: received a command */
\r
4349 switch (LOWORD(wParam)) {
\r
4351 EndDialog(hDlg, TRUE); /* Exit the dialog */
\r
4352 ClearHighlights();
\r
4353 DrawPosition(FALSE, NULL);
\r
4356 promoChar = gameInfo.variant == VariantSuper ? PieceToChar(BlackSilver) : PieceToChar(BlackKing);
\r
4359 promoChar = gameInfo.variant == VariantShogi ? '+' : ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteQueen : BlackQueen));
\r
4362 promoChar = ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteRook : BlackRook));
\r
4363 if(gameInfo.variant == VariantSpartan && !WhiteOnMove(currentMove)) promoChar = PieceToChar(BlackDragon);
\r
4366 promoChar = ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteBishop : BlackBishop));
\r
4367 if(gameInfo.variant == VariantSpartan && !WhiteOnMove(currentMove)) promoChar = PieceToChar(BlackAlfil);
\r
4369 case PB_Chancellor:
\r
4370 promoChar = ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteMarshall : BlackMarshall));
\r
4372 case PB_Archbishop:
\r
4373 promoChar = ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteAngel : BlackAngel));
\r
4376 promoChar = gameInfo.variant == VariantShogi ? '=' : PieceToChar(WhiteOnMove(currentMove) ? WhiteKnight : BlackKnight);
\r
4381 if(promoChar == '.') return FALSE; // invalid piece chosen
\r
4382 EndDialog(hDlg, TRUE); /* Exit the dialog */
\r
4383 UserMoveEvent(fromX, fromY, toX, toY, promoChar);
\r
4384 fromX = fromY = -1;
\r
4385 if (!appData.highlightLastMove) {
\r
4386 ClearHighlights();
\r
4387 DrawPosition(FALSE, NULL);
\r
4394 /* Pop up promotion dialog */
\r
4396 PromotionPopup(HWND hwnd)
\r
4400 lpProc = MakeProcInstance((FARPROC)Promotion, hInst);
\r
4401 DialogBox(hInst, MAKEINTRESOURCE(DLG_PromotionKing),
\r
4402 hwnd, (DLGPROC)lpProc);
\r
4403 FreeProcInstance(lpProc);
\r
4409 DrawPosition(TRUE, NULL);
\r
4410 PromotionPopup(hwndMain);
\r
4413 /* Toggle ShowThinking */
\r
4415 ToggleShowThinking()
\r
4417 appData.showThinking = !appData.showThinking;
\r
4418 ShowThinkingEvent();
\r
4422 LoadGameDialog(HWND hwnd, char* title)
\r
4426 char fileTitle[MSG_SIZ];
\r
4427 f = OpenFileDialog(hwnd, "rb", "",
\r
4428 appData.oldSaveStyle ? "gam" : "pgn",
\r
4430 title, &number, fileTitle, NULL);
\r
4432 cmailMsgLoaded = FALSE;
\r
4433 if (number == 0) {
\r
4434 int error = GameListBuild(f);
\r
4436 DisplayError(_("Cannot build game list"), error);
\r
4437 } else if (!ListEmpty(&gameList) &&
\r
4438 ((ListGame *) gameList.tailPred)->number > 1) {
\r
4439 GameListPopUp(f, fileTitle);
\r
4442 GameListDestroy();
\r
4445 LoadGame(f, number, fileTitle, FALSE);
\r
4449 int get_term_width()
\r
4454 HFONT hfont, hold_font;
\r
4459 hText = GetDlgItem(hwndConsole, OPT_ConsoleText);
\r
4463 // get the text metrics
\r
4464 hdc = GetDC(hText);
\r
4465 lf = font[boardSize][CONSOLE_FONT]->lf;
\r
4466 if (consoleCF.dwEffects & CFE_BOLD)
\r
4467 lf.lfWeight = FW_BOLD;
\r
4468 if (consoleCF.dwEffects & CFE_ITALIC)
\r
4469 lf.lfItalic = TRUE;
\r
4470 if (consoleCF.dwEffects & CFE_STRIKEOUT)
\r
4471 lf.lfStrikeOut = TRUE;
\r
4472 if (consoleCF.dwEffects & CFE_UNDERLINE)
\r
4473 lf.lfUnderline = TRUE;
\r
4474 hfont = CreateFontIndirect(&lf);
\r
4475 hold_font = SelectObject(hdc, hfont);
\r
4476 GetTextMetrics(hdc, &tm);
\r
4477 SelectObject(hdc, hold_font);
\r
4478 DeleteObject(hfont);
\r
4479 ReleaseDC(hText, hdc);
\r
4481 // get the rectangle
\r
4482 SendMessage(hText, EM_GETRECT, 0, (LPARAM)&rc);
\r
4484 return (rc.right-rc.left) / tm.tmAveCharWidth;
\r
4487 void UpdateICSWidth(HWND hText)
\r
4489 LONG old_width, new_width;
\r
4491 new_width = get_term_width(hText, FALSE);
\r
4492 old_width = GetWindowLongPtr(hText, GWLP_USERDATA);
\r
4493 if (new_width != old_width)
\r
4495 ics_update_width(new_width);
\r
4496 SetWindowLongPtr(hText, GWLP_USERDATA, new_width);
\r
4501 ChangedConsoleFont()
\r
4504 CHARRANGE tmpsel, sel;
\r
4505 MyFont *f = font[boardSize][CONSOLE_FONT];
\r
4506 HWND hText = GetDlgItem(hwndConsole, OPT_ConsoleText);
\r
4507 HWND hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
4510 cfmt.cbSize = sizeof(CHARFORMAT);
\r
4511 cfmt.dwMask = CFM_FACE|CFM_SIZE|CFM_CHARSET;
\r
4512 safeStrCpy(cfmt.szFaceName, font[boardSize][CONSOLE_FONT]->mfp.faceName,
\r
4513 sizeof(cfmt.szFaceName)/sizeof(cfmt.szFaceName[0]) );
\r
4514 /* yHeight is expressed in twips. A twip is 1/20 of a font's point
\r
4515 * size. This was undocumented in the version of MSVC++ that I had
\r
4516 * when I wrote the code, but is apparently documented now.
\r
4518 cfmt.yHeight = (int)(f->mfp.pointSize * 20.0 + 0.5);
\r
4519 cfmt.bCharSet = f->lf.lfCharSet;
\r
4520 cfmt.bPitchAndFamily = f->lf.lfPitchAndFamily;
\r
4521 SendMessage(hText, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &cfmt);
\r
4522 SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &cfmt);
\r
4523 /* Why are the following seemingly needed too? */
\r
4524 SendMessage(hText, EM_SETCHARFORMAT, SCF_DEFAULT, (LPARAM) &cfmt);
\r
4525 SendMessage(hInput, EM_SETCHARFORMAT, SCF_DEFAULT, (LPARAM) &cfmt);
\r
4526 SendMessage(hText, EM_EXGETSEL, 0, (LPARAM)&sel);
\r
4528 tmpsel.cpMax = -1; /*999999?*/
\r
4529 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&tmpsel);
\r
4530 SendMessage(hText, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cfmt);
\r
4531 /* Trying putting this here too. It still seems to tickle a RichEdit
\r
4532 * bug: sometimes RichEdit indents the first line of a paragraph too.
\r
4534 paraf.cbSize = sizeof(paraf);
\r
4535 paraf.dwMask = PFM_OFFSET | PFM_STARTINDENT;
\r
4536 paraf.dxStartIndent = 0;
\r
4537 paraf.dxOffset = WRAP_INDENT;
\r
4538 SendMessage(hText, EM_SETPARAFORMAT, 0, (LPARAM) ¶f);
\r
4539 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
4540 UpdateICSWidth(hText);
\r
4543 /*---------------------------------------------------------------------------*\
\r
4545 * Window Proc for main window
\r
4547 \*---------------------------------------------------------------------------*/
\r
4549 /* Process messages for main window, etc. */
\r
4551 WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
\r
4554 int wmId, wmEvent;
\r
4558 char fileTitle[MSG_SIZ];
\r
4559 char buf[MSG_SIZ];
\r
4560 static SnapData sd;
\r
4562 switch (message) {
\r
4564 case WM_PAINT: /* message: repaint portion of window */
\r
4568 case WM_ERASEBKGND:
\r
4569 if (IsIconic(hwnd)) {
\r
4570 /* Cheat; change the message */
\r
4571 return (DefWindowProc(hwnd, WM_ICONERASEBKGND, wParam, lParam));
\r
4573 return (DefWindowProc(hwnd, message, wParam, lParam));
\r
4577 case WM_LBUTTONDOWN:
\r
4578 case WM_MBUTTONDOWN:
\r
4579 case WM_RBUTTONDOWN:
\r
4580 case WM_LBUTTONUP:
\r
4581 case WM_MBUTTONUP:
\r
4582 case WM_RBUTTONUP:
\r
4583 case WM_MOUSEMOVE:
\r
4584 case WM_MOUSEWHEEL:
\r
4585 MouseEvent(hwnd, message, wParam, lParam);
\r
4588 JAWS_KB_NAVIGATION
\r
4592 JAWS_ALT_INTERCEPT
\r
4594 if (appData.icsActive && ((char)wParam == '\r' || (char)wParam > ' ' && !((char)wParam >= '1' && (char)wParam <= '9'))) {
\r
4595 // [HGM] movenum: for non-zero digits we always do type-in dialog
\r
4596 HWND h = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
4597 if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);
\r
4599 SendMessage(h, message, wParam, lParam);
\r
4600 } else if(lParam != KF_REPEAT) {
\r
4601 if (isalpha((char)wParam) || isdigit((char)wParam)) {
\r
4602 TypeInEvent((char)wParam);
\r
4603 } else if((char)wParam == 003) CopyGameToClipboard();
\r
4604 else if((char)wParam == 026) PasteGameOrFENFromClipboard();
\r
4609 case WM_PALETTECHANGED:
\r
4610 if (hwnd != (HWND)wParam && !appData.monoMode) {
\r
4612 HDC hdc = GetDC(hwndMain);
\r
4613 SelectPalette(hdc, hPal, TRUE);
\r
4614 nnew = RealizePalette(hdc);
\r
4616 paletteChanged = TRUE;
\r
4617 InvalidateRect(hwnd, &boardRect, FALSE);
\r
4619 ReleaseDC(hwnd, hdc);
\r
4623 case WM_QUERYNEWPALETTE:
\r
4624 if (!appData.monoMode /*&& paletteChanged*/) {
\r
4626 HDC hdc = GetDC(hwndMain);
\r
4627 paletteChanged = FALSE;
\r
4628 SelectPalette(hdc, hPal, FALSE);
\r
4629 nnew = RealizePalette(hdc);
\r
4631 InvalidateRect(hwnd, &boardRect, FALSE);
\r
4633 ReleaseDC(hwnd, hdc);
\r
4638 case WM_COMMAND: /* message: command from application menu */
\r
4639 wmId = LOWORD(wParam);
\r
4640 wmEvent = HIWORD(wParam);
\r
4645 SAY("new game enter a move to play against the computer with white");
\r
4648 case IDM_NewGameFRC:
\r
4649 if( NewGameFRC() == 0 ) {
\r
4654 case IDM_NewVariant:
\r
4655 NewVariantPopup(hwnd);
\r
4658 case IDM_LoadGame:
\r
4659 LoadGameDialog(hwnd, _("Load Game from File"));
\r
4662 case IDM_LoadNextGame:
\r
4666 case IDM_LoadPrevGame:
\r
4670 case IDM_ReloadGame:
\r
4674 case IDM_LoadPosition:
\r
4675 if (gameMode == AnalyzeMode || gameMode == AnalyzeFile) {
\r
4676 Reset(FALSE, TRUE);
\r
4679 f = OpenFileDialog(hwnd, "rb", "",
\r
4680 appData.oldSaveStyle ? "pos" : "fen",
\r
4682 _("Load Position from File"), &number, fileTitle, NULL);
\r
4684 LoadPosition(f, number, fileTitle);
\r
4688 case IDM_LoadNextPosition:
\r
4689 ReloadPosition(1);
\r
4692 case IDM_LoadPrevPosition:
\r
4693 ReloadPosition(-1);
\r
4696 case IDM_ReloadPosition:
\r
4697 ReloadPosition(0);
\r
4700 case IDM_SaveGame:
\r
4701 defName = DefaultFileName(appData.oldSaveStyle ? "gam" : "pgn");
\r
4702 f = OpenFileDialog(hwnd, "a", defName,
\r
4703 appData.oldSaveStyle ? "gam" : "pgn",
\r
4705 _("Save Game to File"), NULL, fileTitle, NULL);
\r
4707 SaveGame(f, 0, "");
\r
4711 case IDM_SavePosition:
\r
4712 defName = DefaultFileName(appData.oldSaveStyle ? "pos" : "fen");
\r
4713 f = OpenFileDialog(hwnd, "a", defName,
\r
4714 appData.oldSaveStyle ? "pos" : "fen",
\r
4716 _("Save Position to File"), NULL, fileTitle, NULL);
\r
4718 SavePosition(f, 0, "");
\r
4722 case IDM_SaveDiagram:
\r
4723 defName = "diagram";
\r
4724 f = OpenFileDialog(hwnd, "wb", defName,
\r
4727 _("Save Diagram to File"), NULL, fileTitle, NULL);
\r
4733 case IDM_CopyGame:
\r
4734 CopyGameToClipboard();
\r
4737 case IDM_PasteGame:
\r
4738 PasteGameFromClipboard();
\r
4741 case IDM_CopyGameListToClipboard:
\r
4742 CopyGameListToClipboard();
\r
4745 /* [AS] Autodetect FEN or PGN data */
\r
4746 case IDM_PasteAny:
\r
4747 PasteGameOrFENFromClipboard();
\r
4750 /* [AS] Move history */
\r
4751 case IDM_ShowMoveHistory:
\r
4752 if( MoveHistoryIsUp() ) {
\r
4753 MoveHistoryPopDown();
\r
4756 MoveHistoryPopUp();
\r
4760 /* [AS] Eval graph */
\r
4761 case IDM_ShowEvalGraph:
\r
4762 if( EvalGraphIsUp() ) {
\r
4763 EvalGraphPopDown();
\r
4767 SetFocus(hwndMain);
\r
4771 /* [AS] Engine output */
\r
4772 case IDM_ShowEngineOutput:
\r
4773 if( EngineOutputIsUp() ) {
\r
4774 EngineOutputPopDown();
\r
4777 EngineOutputPopUp();
\r
4781 /* [AS] User adjudication */
\r
4782 case IDM_UserAdjudication_White:
\r
4783 UserAdjudicationEvent( +1 );
\r
4786 case IDM_UserAdjudication_Black:
\r
4787 UserAdjudicationEvent( -1 );
\r
4790 case IDM_UserAdjudication_Draw:
\r
4791 UserAdjudicationEvent( 0 );
\r
4794 /* [AS] Game list options dialog */
\r
4795 case IDM_GameListOptions:
\r
4796 GameListOptions();
\r
4803 case IDM_CopyPosition:
\r
4804 CopyFENToClipboard();
\r
4807 case IDM_PastePosition:
\r
4808 PasteFENFromClipboard();
\r
4811 case IDM_MailMove:
\r
4815 case IDM_ReloadCMailMsg:
\r
4816 Reset(TRUE, TRUE);
\r
4817 ReloadCmailMsgEvent(FALSE);
\r
4820 case IDM_Minimize:
\r
4821 ShowWindow(hwnd, SW_MINIMIZE);
\r
4828 case IDM_MachineWhite:
\r
4829 MachineWhiteEvent();
\r
4831 * refresh the tags dialog only if it's visible
\r
4833 if (gameMode == MachinePlaysWhite && IsWindowVisible(editTagsDialog)) {
\r
4835 tags = PGNTags(&gameInfo);
\r
4836 TagsPopUp(tags, CmailMsg());
\r
4839 SAY("computer starts playing white");
\r
4842 case IDM_MachineBlack:
\r
4843 MachineBlackEvent();
\r
4845 * refresh the tags dialog only if it's visible
\r
4847 if (gameMode == MachinePlaysBlack && IsWindowVisible(editTagsDialog)) {
\r
4849 tags = PGNTags(&gameInfo);
\r
4850 TagsPopUp(tags, CmailMsg());
\r
4853 SAY("computer starts playing black");
\r
4856 case IDM_Match: // [HGM] match: flows into next case, after setting Match Mode and nr of Games
\r
4857 MatchEvent(2); // distinguish from command-line-triggered case (matchMode=1)
\r
4860 case IDM_TwoMachines:
\r
4861 TwoMachinesEvent();
\r
4863 * refresh the tags dialog only if it's visible
\r
4865 if (gameMode == TwoMachinesPlay && IsWindowVisible(editTagsDialog)) {
\r
4867 tags = PGNTags(&gameInfo);
\r
4868 TagsPopUp(tags, CmailMsg());
\r
4871 SAY("computer starts playing both sides");
\r
4874 case IDM_AnalysisMode:
\r
4875 if (!first.analysisSupport) {
\r
4876 snprintf(buf, MSG_SIZ, _("%s does not support analysis"), first.tidy);
\r
4877 DisplayError(buf, 0);
\r
4879 SAY("analyzing current position");
\r
4880 /* [DM] icsEngineAnlyze [HGM] Why is this front-end??? */
\r
4881 if (appData.icsActive) {
\r
4882 if (gameMode != IcsObserving) {
\r
4883 snprintf(buf, MSG_SIZ, "You are not observing a game");
\r
4884 DisplayError(buf, 0);
\r
4885 /* secure check */
\r
4886 if (appData.icsEngineAnalyze) {
\r
4887 if (appData.debugMode)
\r
4888 fprintf(debugFP, "Found unexpected active ICS engine analyze \n");
\r
4889 ExitAnalyzeMode();
\r
4895 /* if enable, user want disable icsEngineAnalyze */
\r
4896 if (appData.icsEngineAnalyze) {
\r
4897 ExitAnalyzeMode();
\r
4901 appData.icsEngineAnalyze = TRUE;
\r
4902 if (appData.debugMode) fprintf(debugFP, "ICS engine analyze starting...\n");
\r
4905 if (!appData.showThinking) ToggleShowThinking();
\r
4906 AnalyzeModeEvent();
\r
4910 case IDM_AnalyzeFile:
\r
4911 if (!first.analysisSupport) {
\r
4912 char buf[MSG_SIZ];
\r
4913 snprintf(buf, MSG_SIZ, _("%s does not support analysis"), first.tidy);
\r
4914 DisplayError(buf, 0);
\r
4916 if (!appData.showThinking) ToggleShowThinking();
\r
4917 AnalyzeFileEvent();
\r
4918 LoadGameDialog(hwnd, _("Analyze Game from File"));
\r
4919 AnalysisPeriodicEvent(1);
\r
4923 case IDM_IcsClient:
\r
4927 case IDM_EditGame:
\r
4928 case IDM_EditGame2:
\r
4933 case IDM_EditPosition:
\r
4934 case IDM_EditPosition2:
\r
4935 EditPositionEvent();
\r
4936 SAY("enter a FEN string or setup a position on the board using the control R pop up menu");
\r
4939 case IDM_Training:
\r
4943 case IDM_ShowGameList:
\r
4944 ShowGameListProc();
\r
4947 case IDM_EditProgs1:
\r
4948 EditTagsPopUp(firstChessProgramNames, &firstChessProgramNames);
\r
4951 case IDM_EditProgs2:
\r
4952 LoadEnginePopUp(hwndMain);
\r
4953 // EditTagsPopUp(secondChessProgramNames, &secondChessProgramNames);
\r
4956 case IDM_EditServers:
\r
4957 EditTagsPopUp(icsNames, &icsNames);
\r
4960 case IDM_EditTags:
\r
4965 case IDM_EditBook:
\r
4969 case IDM_EditComment:
\r
4971 if (commentUp && editComment) {
\r
4974 EditCommentEvent();
\r
4994 case IDM_CallFlag:
\r
5014 case IDM_StopObserving:
\r
5015 StopObservingEvent();
\r
5018 case IDM_StopExamining:
\r
5019 StopExaminingEvent();
\r
5023 UploadGameEvent();
\r
5026 case IDM_TypeInMove:
\r
5027 TypeInEvent('\000');
\r
5030 case IDM_TypeInName:
\r
5031 PopUpNameDialog('\000');
\r
5034 case IDM_Backward:
\r
5036 SetFocus(hwndMain);
\r
5043 SetFocus(hwndMain);
\r
5048 SetFocus(hwndMain);
\r
5053 SetFocus(hwndMain);
\r
5057 RevertEvent(FALSE);
\r
5060 case IDM_Annotate: // [HGM] vari: revert with annotation
\r
5061 RevertEvent(TRUE);
\r
5064 case IDM_TruncateGame:
\r
5065 TruncateGameEvent();
\r
5072 case IDM_RetractMove:
\r
5073 RetractMoveEvent();
\r
5076 case IDM_FlipView:
\r
5077 flipView = !flipView;
\r
5078 DrawPosition(FALSE, NULL);
\r
5081 case IDM_FlipClock:
\r
5082 flipClock = !flipClock;
\r
5083 DisplayBothClocks();
\r
5087 case IDM_MuteSounds:
\r
5088 mute = !mute; // [HGM] mute: keep track of global muting variable
\r
5089 CheckMenuItem(GetMenu(hwndMain),IDM_MuteSounds,
\r
5090 MF_BYCOMMAND|(mute?MF_CHECKED:MF_UNCHECKED));
\r
5093 case IDM_GeneralOptions:
\r
5094 GeneralOptionsPopup(hwnd);
\r
5095 DrawPosition(TRUE, NULL);
\r
5098 case IDM_BoardOptions:
\r
5099 BoardOptionsPopup(hwnd);
\r
5102 case IDM_EnginePlayOptions:
\r
5103 EnginePlayOptionsPopup(hwnd);
\r
5106 case IDM_Engine1Options:
\r
5107 EngineOptionsPopup(hwnd, &first);
\r
5110 case IDM_Engine2Options:
\r
5112 if(WaitForEngine(&second, SettingsMenuIfReady)) break;
\r
5113 EngineOptionsPopup(hwnd, &second);
\r
5116 case IDM_OptionsUCI:
\r
5117 UciOptionsPopup(hwnd);
\r
5121 TourneyPopup(hwnd);
\r
5124 case IDM_IcsOptions:
\r
5125 IcsOptionsPopup(hwnd);
\r
5129 FontsOptionsPopup(hwnd);
\r
5133 SoundOptionsPopup(hwnd);
\r
5136 case IDM_CommPort:
\r
5137 CommPortOptionsPopup(hwnd);
\r
5140 case IDM_LoadOptions:
\r
5141 LoadOptionsPopup(hwnd);
\r
5144 case IDM_SaveOptions:
\r
5145 SaveOptionsPopup(hwnd);
\r
5148 case IDM_TimeControl:
\r
5149 TimeControlOptionsPopup(hwnd);
\r
5152 case IDM_SaveSettings:
\r
5153 SaveSettings(settingsFileName);
\r
5156 case IDM_SaveSettingsOnExit:
\r
5157 saveSettingsOnExit = !saveSettingsOnExit;
\r
5158 (void) CheckMenuItem(GetMenu(hwndMain), IDM_SaveSettingsOnExit,
\r
5159 MF_BYCOMMAND|(saveSettingsOnExit ?
\r
5160 MF_CHECKED : MF_UNCHECKED));
\r
5171 case IDM_AboutGame:
\r
5176 appData.debugMode = !appData.debugMode;
\r
5177 if (appData.debugMode) {
\r
5178 char dir[MSG_SIZ];
\r
5179 GetCurrentDirectory(MSG_SIZ, dir);
\r
5180 SetCurrentDirectory(installDir);
\r
5181 debugFP = fopen(appData.nameOfDebugFile, "w");
\r
5182 SetCurrentDirectory(dir);
\r
5183 setbuf(debugFP, NULL);
\r
5190 case IDM_HELPCONTENTS:
\r
5191 if (!MyHelp (hwnd, "winboard.hlp", HELP_KEY,(DWORD)(LPSTR)"CONTENTS") &&
\r
5192 !HtmlHelp(hwnd, "winboard.chm", 0, 0) ) {
\r
5193 MessageBox (GetFocus(),
\r
5194 _("Unable to activate help"),
\r
5195 szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);
\r
5199 case IDM_HELPSEARCH:
\r
5200 if (!MyHelp (hwnd, "winboard.hlp", HELP_PARTIALKEY, (DWORD)(LPSTR)"") &&
\r
5201 !HtmlHelp(hwnd, "winboard.chm", 0, 0) ) {
\r
5202 MessageBox (GetFocus(),
\r
5203 _("Unable to activate help"),
\r
5204 szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);
\r
5208 case IDM_HELPHELP:
\r
5209 if(!WinHelp(hwnd, (LPSTR)NULL, HELP_HELPONHELP, 0)) {
\r
5210 MessageBox (GetFocus(),
\r
5211 _("Unable to activate help"),
\r
5212 szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);
\r
5217 lpProc = MakeProcInstance((FARPROC)About, hInst);
\r
5219 (gameInfo.event && strcmp(gameInfo.event, "Easter Egg Hunt") == 0) ?
\r
5220 "AboutBox2" : "AboutBox", hwnd, (DLGPROC)lpProc);
\r
5221 FreeProcInstance(lpProc);
\r
5224 case IDM_DirectCommand1:
\r
5225 AskQuestionEvent(_("Direct Command"),
\r
5226 _("Send to chess program:"), "", "1");
\r
5228 case IDM_DirectCommand2:
\r
5229 AskQuestionEvent(_("Direct Command"),
\r
5230 _("Send to second chess program:"), "", "2");
\r
5233 case EP_WhitePawn:
\r
5234 EditPositionMenuEvent(WhitePawn, fromX, fromY);
\r
5235 fromX = fromY = -1;
\r
5238 case EP_WhiteKnight:
\r
5239 EditPositionMenuEvent(WhiteKnight, fromX, fromY);
\r
5240 fromX = fromY = -1;
\r
5243 case EP_WhiteBishop:
\r
5244 EditPositionMenuEvent(WhiteBishop, fromX, fromY);
\r
5245 fromX = fromY = -1;
\r
5248 case EP_WhiteRook:
\r
5249 EditPositionMenuEvent(WhiteRook, fromX, fromY);
\r
5250 fromX = fromY = -1;
\r
5253 case EP_WhiteQueen:
\r
5254 EditPositionMenuEvent(WhiteQueen, fromX, fromY);
\r
5255 fromX = fromY = -1;
\r
5258 case EP_WhiteFerz:
\r
5259 EditPositionMenuEvent(WhiteFerz, fromX, fromY);
\r
5260 fromX = fromY = -1;
\r
5263 case EP_WhiteWazir:
\r
5264 EditPositionMenuEvent(WhiteWazir, fromX, fromY);
\r
5265 fromX = fromY = -1;
\r
5268 case EP_WhiteAlfil:
\r
5269 EditPositionMenuEvent(WhiteAlfil, fromX, fromY);
\r
5270 fromX = fromY = -1;
\r
5273 case EP_WhiteCannon:
\r
5274 EditPositionMenuEvent(WhiteCannon, fromX, fromY);
\r
5275 fromX = fromY = -1;
\r
5278 case EP_WhiteCardinal:
\r
5279 EditPositionMenuEvent(WhiteAngel, fromX, fromY);
\r
5280 fromX = fromY = -1;
\r
5283 case EP_WhiteMarshall:
\r
5284 EditPositionMenuEvent(WhiteMarshall, fromX, fromY);
\r
5285 fromX = fromY = -1;
\r
5288 case EP_WhiteKing:
\r
5289 EditPositionMenuEvent(WhiteKing, fromX, fromY);
\r
5290 fromX = fromY = -1;
\r
5293 case EP_BlackPawn:
\r
5294 EditPositionMenuEvent(BlackPawn, fromX, fromY);
\r
5295 fromX = fromY = -1;
\r
5298 case EP_BlackKnight:
\r
5299 EditPositionMenuEvent(BlackKnight, fromX, fromY);
\r
5300 fromX = fromY = -1;
\r
5303 case EP_BlackBishop:
\r
5304 EditPositionMenuEvent(BlackBishop, fromX, fromY);
\r
5305 fromX = fromY = -1;
\r
5308 case EP_BlackRook:
\r
5309 EditPositionMenuEvent(BlackRook, fromX, fromY);
\r
5310 fromX = fromY = -1;
\r
5313 case EP_BlackQueen:
\r
5314 EditPositionMenuEvent(BlackQueen, fromX, fromY);
\r
5315 fromX = fromY = -1;
\r
5318 case EP_BlackFerz:
\r
5319 EditPositionMenuEvent(BlackFerz, fromX, fromY);
\r
5320 fromX = fromY = -1;
\r
5323 case EP_BlackWazir:
\r
5324 EditPositionMenuEvent(BlackWazir, fromX, fromY);
\r
5325 fromX = fromY = -1;
\r
5328 case EP_BlackAlfil:
\r
5329 EditPositionMenuEvent(BlackAlfil, fromX, fromY);
\r
5330 fromX = fromY = -1;
\r
5333 case EP_BlackCannon:
\r
5334 EditPositionMenuEvent(BlackCannon, fromX, fromY);
\r
5335 fromX = fromY = -1;
\r
5338 case EP_BlackCardinal:
\r
5339 EditPositionMenuEvent(BlackAngel, fromX, fromY);
\r
5340 fromX = fromY = -1;
\r
5343 case EP_BlackMarshall:
\r
5344 EditPositionMenuEvent(BlackMarshall, fromX, fromY);
\r
5345 fromX = fromY = -1;
\r
5348 case EP_BlackKing:
\r
5349 EditPositionMenuEvent(BlackKing, fromX, fromY);
\r
5350 fromX = fromY = -1;
\r
5353 case EP_EmptySquare:
\r
5354 EditPositionMenuEvent(EmptySquare, fromX, fromY);
\r
5355 fromX = fromY = -1;
\r
5358 case EP_ClearBoard:
\r
5359 EditPositionMenuEvent(ClearBoard, fromX, fromY);
\r
5360 fromX = fromY = -1;
\r
5364 EditPositionMenuEvent(WhitePlay, fromX, fromY);
\r
5365 fromX = fromY = -1;
\r
5369 EditPositionMenuEvent(BlackPlay, fromX, fromY);
\r
5370 fromX = fromY = -1;
\r
5374 EditPositionMenuEvent(PromotePiece, fromX, fromY);
\r
5375 fromX = fromY = -1;
\r
5379 EditPositionMenuEvent(DemotePiece, fromX, fromY);
\r
5380 fromX = fromY = -1;
\r
5384 DropMenuEvent(WhitePawn, fromX, fromY);
\r
5385 fromX = fromY = -1;
\r
5389 DropMenuEvent(WhiteKnight, fromX, fromY);
\r
5390 fromX = fromY = -1;
\r
5394 DropMenuEvent(WhiteBishop, fromX, fromY);
\r
5395 fromX = fromY = -1;
\r
5399 DropMenuEvent(WhiteRook, fromX, fromY);
\r
5400 fromX = fromY = -1;
\r
5404 DropMenuEvent(WhiteQueen, fromX, fromY);
\r
5405 fromX = fromY = -1;
\r
5409 barbaric = 0; appData.language = "";
\r
5410 TranslateMenus(0);
\r
5411 CheckMenuItem(GetMenu(hwndMain), lastChecked, MF_BYCOMMAND|MF_UNCHECKED);
\r
5412 CheckMenuItem(GetMenu(hwndMain), IDM_English, MF_BYCOMMAND|MF_CHECKED);
\r
5413 lastChecked = wmId;
\r
5417 if(wmId > IDM_English && wmId < IDM_English+20) {
\r
5418 LoadLanguageFile(languageFile[wmId - IDM_English - 1]);
\r
5419 TranslateMenus(0);
\r
5420 CheckMenuItem(GetMenu(hwndMain), lastChecked, MF_BYCOMMAND|MF_UNCHECKED);
\r
5421 CheckMenuItem(GetMenu(hwndMain), wmId, MF_BYCOMMAND|MF_CHECKED);
\r
5422 lastChecked = wmId;
\r
5425 return (DefWindowProc(hwnd, message, wParam, lParam));
\r
5431 case CLOCK_TIMER_ID:
\r
5432 KillTimer(hwnd, clockTimerEvent); /* Simulate one-shot timer as in X */
\r
5433 clockTimerEvent = 0;
\r
5434 DecrementClocks(); /* call into back end */
\r
5436 case LOAD_GAME_TIMER_ID:
\r
5437 KillTimer(hwnd, loadGameTimerEvent); /* Simulate one-shot timer as in X*/
\r
5438 loadGameTimerEvent = 0;
\r
5439 AutoPlayGameLoop(); /* call into back end */
\r
5441 case ANALYSIS_TIMER_ID:
\r
5442 if ((gameMode == AnalyzeMode || gameMode == AnalyzeFile
\r
5443 || appData.icsEngineAnalyze) && appData.periodicUpdates) {
\r
5444 AnalysisPeriodicEvent(0);
\r
5446 KillTimer(hwnd, analysisTimerEvent);
\r
5447 analysisTimerEvent = 0;
\r
5450 case DELAYED_TIMER_ID:
\r
5451 KillTimer(hwnd, delayedTimerEvent);
\r
5452 delayedTimerEvent = 0;
\r
5453 delayedTimerCallback();
\r
5458 case WM_USER_Input:
\r
5459 InputEvent(hwnd, message, wParam, lParam);
\r
5462 /* [AS] Also move "attached" child windows */
\r
5463 case WM_WINDOWPOSCHANGING:
\r
5465 if( hwnd == hwndMain && appData.useStickyWindows ) {
\r
5466 LPWINDOWPOS lpwp = (LPWINDOWPOS) lParam;
\r
5468 if( ((lpwp->flags & SWP_NOMOVE) == 0) && ((lpwp->flags & SWP_NOSIZE) != 0) ) {
\r
5469 /* Window is moving */
\r
5472 // GetWindowRect( hwnd, &rcMain ); //[HGM] sticky: in XP this returned new position, not old
\r
5473 rcMain.left = wpMain.x; // replace by these 4 lines to reconstruct old rect
\r
5474 rcMain.right = wpMain.x + wpMain.width;
\r
5475 rcMain.top = wpMain.y;
\r
5476 rcMain.bottom = wpMain.y + wpMain.height;
\r
5478 ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, moveHistoryDialog, &wpMoveHistory );
\r
5479 ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, evalGraphDialog, &wpEvalGraph );
\r
5480 ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, engineOutputDialog, &wpEngineOutput );
\r
5481 ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, gameListDialog, &wpGameList );
\r
5482 ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, hwndConsole, &wpConsole );
\r
5483 wpMain.x = lpwp->x;
\r
5484 wpMain.y = lpwp->y;
\r
5489 /* [AS] Snapping */
\r
5490 case WM_ENTERSIZEMOVE:
\r
5491 if(appData.debugMode) { fprintf(debugFP, "size-move\n"); }
\r
5492 if (hwnd == hwndMain) {
\r
5493 doingSizing = TRUE;
\r
5496 return OnEnterSizeMove( &sd, hwnd, wParam, lParam );
\r
5500 if(appData.debugMode) { fprintf(debugFP, "sizing\n"); }
\r
5501 if (hwnd == hwndMain) {
\r
5502 lastSizing = wParam;
\r
5507 if(appData.debugMode) { fprintf(debugFP, "moving\n"); }
\r
5508 return OnMoving( &sd, hwnd, wParam, lParam );
\r
5510 case WM_EXITSIZEMOVE:
\r
5511 if(appData.debugMode) { fprintf(debugFP, "exit size-move, size = %d\n", squareSize); }
\r
5512 if (hwnd == hwndMain) {
\r
5514 doingSizing = FALSE;
\r
5515 InvalidateRect(hwnd, &boardRect, FALSE);
\r
5516 GetClientRect(hwnd, &client);
\r
5517 ResizeBoard(client.right, client.bottom, lastSizing);
\r
5519 if(appData.debugMode) { fprintf(debugFP, "square size = %d\n", squareSize); }
\r
5521 return OnExitSizeMove( &sd, hwnd, wParam, lParam );
\r
5524 case WM_DESTROY: /* message: window being destroyed */
\r
5525 PostQuitMessage(0);
\r
5529 if (hwnd == hwndMain) {
\r
5534 default: /* Passes it on if unprocessed */
\r
5535 return (DefWindowProc(hwnd, message, wParam, lParam));
\r
5540 /*---------------------------------------------------------------------------*\
\r
5542 * Misc utility routines
\r
5544 \*---------------------------------------------------------------------------*/
\r
5547 * Decent random number generator, at least not as bad as Windows
\r
5548 * standard rand, which returns a value in the range 0 to 0x7fff.
\r
5550 unsigned int randstate;
\r
5555 randstate = randstate * 1664525 + 1013904223;
\r
5556 return (int) randstate & 0x7fffffff;
\r
5560 mysrandom(unsigned int seed)
\r
5567 * returns TRUE if user selects a different color, FALSE otherwise
\r
5571 ChangeColor(HWND hwnd, COLORREF *which)
\r
5573 static BOOL firstTime = TRUE;
\r
5574 static DWORD customColors[16];
\r
5576 COLORREF newcolor;
\r
5581 /* Make initial colors in use available as custom colors */
\r
5582 /* Should we put the compiled-in defaults here instead? */
\r
5584 customColors[i++] = lightSquareColor & 0xffffff;
\r
5585 customColors[i++] = darkSquareColor & 0xffffff;
\r
5586 customColors[i++] = whitePieceColor & 0xffffff;
\r
5587 customColors[i++] = blackPieceColor & 0xffffff;
\r
5588 customColors[i++] = highlightSquareColor & 0xffffff;
\r
5589 customColors[i++] = premoveHighlightColor & 0xffffff;
\r
5591 for (ccl = (ColorClass) 0; ccl < NColorClasses && i < 16; ccl++) {
\r
5592 customColors[i++] = textAttribs[ccl].color;
\r
5594 while (i < 16) customColors[i++] = RGB(255, 255, 255);
\r
5595 firstTime = FALSE;
\r
5598 cc.lStructSize = sizeof(cc);
\r
5599 cc.hwndOwner = hwnd;
\r
5600 cc.hInstance = NULL;
\r
5601 cc.rgbResult = (DWORD) (*which & 0xffffff);
\r
5602 cc.lpCustColors = (LPDWORD) customColors;
\r
5603 cc.Flags = CC_RGBINIT|CC_FULLOPEN;
\r
5605 if (!ChooseColor(&cc)) return FALSE;
\r
5607 newcolor = (COLORREF) (0x2000000 | cc.rgbResult);
\r
5608 if (newcolor == *which) return FALSE;
\r
5609 *which = newcolor;
\r
5613 InitDrawingColors();
\r
5614 InvalidateRect(hwnd, &boardRect, FALSE);
\r
5619 MyLoadSound(MySound *ms)
\r
5625 if (ms->data && ms->flag) free(ms->data);
\r
5628 switch (ms->name[0]) {
\r
5634 /* System sound from Control Panel. Don't preload here. */
\r
5638 if (ms->name[1] == NULLCHAR) {
\r
5639 /* "!" alone = silence */
\r
5642 /* Builtin wave resource. Error if not found. */
\r
5643 HANDLE h = FindResource(hInst, ms->name + 1, "WAVE");
\r
5644 if (h == NULL) break;
\r
5645 ms->data = (void *)LoadResource(hInst, h);
\r
5646 ms->flag = 0; // not maloced, so cannot be freed!
\r
5647 if (h == NULL) break;
\r
5652 /* .wav file. Error if not found. */
\r
5653 f = fopen(ms->name, "rb");
\r
5654 if (f == NULL) break;
\r
5655 if (fstat(fileno(f), &st) < 0) break;
\r
5656 ms->data = malloc(st.st_size);
\r
5658 if (fread(ms->data, st.st_size, 1, f) < 1) break;
\r
5664 char buf[MSG_SIZ];
\r
5665 snprintf(buf, MSG_SIZ, _("Error loading sound %s"), ms->name);
\r
5666 DisplayError(buf, GetLastError());
\r
5672 MyPlaySound(MySound *ms)
\r
5674 BOOLEAN ok = FALSE;
\r
5676 if(mute) return TRUE; // [HGM] mute: suppress all sound play when muted
\r
5677 switch (ms->name[0]) {
\r
5679 if(appData.debugMode) fprintf(debugFP, "silence\n");
\r
5684 /* System sound from Control Panel (deprecated feature).
\r
5685 "$" alone or an unset sound name gets default beep (still in use). */
\r
5686 if (ms->name[1]) {
\r
5687 ok = PlaySound(ms->name + 1, NULL, SND_ALIAS|SND_ASYNC);
\r
5689 if (!ok) ok = MessageBeep(MB_OK);
\r
5692 /* Builtin wave resource, or "!" alone for silence */
\r
5693 if (ms->name[1]) {
\r
5694 if (ms->data == NULL) return FALSE;
\r
5695 ok = PlaySound(ms->data, NULL, SND_MEMORY|SND_ASYNC);
\r
5701 /* .wav file. Error if not found. */
\r
5702 if (ms->data == NULL) return FALSE;
\r
5703 ok = PlaySound(ms->data, NULL, SND_MEMORY|SND_ASYNC);
\r
5706 /* Don't print an error: this can happen innocently if the sound driver
\r
5707 is busy; for instance, if another instance of WinBoard is playing
\r
5708 a sound at about the same time. */
\r
5714 OldOpenFileHook(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
5717 OPENFILENAME *ofn;
\r
5718 static UINT *number; /* gross that this is static */
\r
5720 switch (message) {
\r
5721 case WM_INITDIALOG: /* message: initialize dialog box */
\r
5722 /* Center the dialog over the application window */
\r
5723 ofn = (OPENFILENAME *) lParam;
\r
5724 if (ofn->Flags & OFN_ENABLETEMPLATE) {
\r
5725 number = (UINT *) ofn->lCustData;
\r
5726 SendMessage(GetDlgItem(hDlg, edt2), WM_SETTEXT, 0, (LPARAM) "");
\r
5730 CenterWindow (hDlg, GetWindow (hDlg, GW_OWNER));
\r
5731 Translate(hDlg, 1536);
\r
5732 return FALSE; /* Allow for further processing */
\r
5735 if ((LOWORD(wParam) == IDOK) && (number != NULL)) {
\r
5736 *number = GetDlgItemInt(hDlg, OPT_IndexNumberOld, &ok, FALSE);
\r
5738 return FALSE; /* Allow for further processing */
\r
5744 OpenFileHook(HWND hdlg, UINT uiMsg, WPARAM wParam, LPARAM lParam)
\r
5746 static UINT *number;
\r
5747 OPENFILENAME *ofname;
\r
5750 case WM_INITDIALOG:
\r
5751 Translate(hdlg, DLG_IndexNumber);
\r
5752 ofname = (OPENFILENAME *)lParam;
\r
5753 number = (UINT *)(ofname->lCustData);
\r
5756 ofnot = (OFNOTIFY *)lParam;
\r
5757 if (ofnot->hdr.code == CDN_FILEOK) {
\r
5758 *number = GetDlgItemInt(hdlg, OPT_IndexNumber, NULL, FALSE);
\r
5767 OpenFileDialog(HWND hwnd, char *write, char *defName, char *defExt, // [HGM] diag: type of 'write' now string
\r
5768 char *nameFilt, char *dlgTitle, UINT *number,
\r
5769 char fileTitle[MSG_SIZ], char fileName[MSG_SIZ])
\r
5771 OPENFILENAME openFileName;
\r
5772 char buf1[MSG_SIZ];
\r
5775 if (fileName == NULL) fileName = buf1;
\r
5776 if (defName == NULL) {
\r
5777 safeStrCpy(fileName, "*.", 3 );
\r
5778 strcat(fileName, defExt);
\r
5780 safeStrCpy(fileName, defName, MSG_SIZ );
\r
5782 if (fileTitle) safeStrCpy(fileTitle, "", MSG_SIZ );
\r
5783 if (number) *number = 0;
\r
5785 openFileName.lStructSize = sizeof(OPENFILENAME);
\r
5786 openFileName.hwndOwner = hwnd;
\r
5787 openFileName.hInstance = (HANDLE) hInst;
\r
5788 openFileName.lpstrFilter = nameFilt;
\r
5789 openFileName.lpstrCustomFilter = (LPSTR) NULL;
\r
5790 openFileName.nMaxCustFilter = 0L;
\r
5791 openFileName.nFilterIndex = 1L;
\r
5792 openFileName.lpstrFile = fileName;
\r
5793 openFileName.nMaxFile = MSG_SIZ;
\r
5794 openFileName.lpstrFileTitle = fileTitle;
\r
5795 openFileName.nMaxFileTitle = fileTitle ? MSG_SIZ : 0;
\r
5796 openFileName.lpstrInitialDir = NULL;
\r
5797 openFileName.lpstrTitle = dlgTitle;
\r
5798 openFileName.Flags = OFN_PATHMUSTEXIST | OFN_HIDEREADONLY
\r
5799 | (write[0] != 'r' ? 0 : OFN_FILEMUSTEXIST)
\r
5800 | (number ? OFN_ENABLETEMPLATE | OFN_ENABLEHOOK: 0)
\r
5801 | (oldDialog ? 0 : OFN_EXPLORER);
\r
5802 openFileName.nFileOffset = 0;
\r
5803 openFileName.nFileExtension = 0;
\r
5804 openFileName.lpstrDefExt = defExt;
\r
5805 openFileName.lCustData = (LONG) number;
\r
5806 openFileName.lpfnHook = oldDialog ?
\r
5807 (LPOFNHOOKPROC) OldOpenFileHook : (LPOFNHOOKPROC) OpenFileHook;
\r
5808 openFileName.lpTemplateName = (LPSTR)(oldDialog ? 1536 : DLG_IndexNumber);
\r
5810 if (write[0] != 'r' ? GetSaveFileName(&openFileName) :
\r
5811 GetOpenFileName(&openFileName)) {
\r
5812 /* open the file */
\r
5813 f = fopen(openFileName.lpstrFile, write);
\r
5815 MessageBox(hwnd, _("File open failed"), NULL,
\r
5816 MB_OK|MB_ICONEXCLAMATION);
\r
5820 int err = CommDlgExtendedError();
\r
5821 if (err != 0) DisplayError(_("Internal error in file dialog box"), err);
\r
5830 MenuPopup(HWND hwnd, POINT pt, HMENU hmenu, UINT def)
\r
5832 HMENU hmenuTrackPopup; /* floating pop-up menu */
\r
5835 * Get the first pop-up menu in the menu template. This is the
\r
5836 * menu that TrackPopupMenu displays.
\r
5838 hmenuTrackPopup = GetSubMenu(hmenu, 0);
\r
5839 TranslateOneMenu(10, hmenuTrackPopup);
\r
5841 SetMenuDefaultItem(hmenuTrackPopup, def, FALSE);
\r
5844 * TrackPopup uses screen coordinates, so convert the
\r
5845 * coordinates of the mouse click to screen coordinates.
\r
5847 ClientToScreen(hwnd, (LPPOINT) &pt);
\r
5849 /* Draw and track the floating pop-up menu. */
\r
5850 TrackPopupMenu(hmenuTrackPopup, TPM_CENTERALIGN | TPM_RIGHTBUTTON,
\r
5851 pt.x, pt.y, 0, hwnd, NULL);
\r
5853 /* Destroy the menu.*/
\r
5854 DestroyMenu(hmenu);
\r
5859 int sizeX, sizeY, newSizeX, newSizeY;
\r
5861 } ResizeEditPlusButtonsClosure;
\r
5864 ResizeEditPlusButtonsCallback(HWND hChild, LPARAM lparam)
\r
5866 ResizeEditPlusButtonsClosure *cl = (ResizeEditPlusButtonsClosure *)lparam;
\r
5870 if (hChild == cl->hText) return TRUE;
\r
5871 GetWindowRect(hChild, &rect); /* gives screen coords */
\r
5872 pt.x = rect.left + (cl->newSizeX - cl->sizeX)/2;
\r
5873 pt.y = rect.top + cl->newSizeY - cl->sizeY;
\r
5874 ScreenToClient(cl->hDlg, &pt);
\r
5875 cl->hdwp = DeferWindowPos(cl->hdwp, hChild, NULL,
\r
5876 pt.x, pt.y, rect.right - rect.left, rect.bottom - rect.top, SWP_NOZORDER);
\r
5880 /* Resize a dialog that has a (rich) edit field filling most of
\r
5881 the top, with a row of buttons below */
\r
5883 ResizeEditPlusButtons(HWND hDlg, HWND hText, int sizeX, int sizeY, int newSizeX, int newSizeY)
\r
5886 int newTextHeight, newTextWidth;
\r
5887 ResizeEditPlusButtonsClosure cl;
\r
5889 /*if (IsIconic(hDlg)) return;*/
\r
5890 if (newSizeX == sizeX && newSizeY == sizeY) return;
\r
5892 cl.hdwp = BeginDeferWindowPos(8);
\r
5894 GetWindowRect(hText, &rectText); /* gives screen coords */
\r
5895 newTextWidth = rectText.right - rectText.left + newSizeX - sizeX;
\r
5896 newTextHeight = rectText.bottom - rectText.top + newSizeY - sizeY;
\r
5897 if (newTextHeight < 0) {
\r
5898 newSizeY += -newTextHeight;
\r
5899 newTextHeight = 0;
\r
5901 cl.hdwp = DeferWindowPos(cl.hdwp, hText, NULL, 0, 0,
\r
5902 newTextWidth, newTextHeight, SWP_NOZORDER|SWP_NOMOVE);
\r
5908 cl.newSizeX = newSizeX;
\r
5909 cl.newSizeY = newSizeY;
\r
5910 EnumChildWindows(hDlg, ResizeEditPlusButtonsCallback, (LPARAM)&cl);
\r
5912 EndDeferWindowPos(cl.hdwp);
\r
5915 BOOL CenterWindowEx(HWND hwndChild, HWND hwndParent, int mode)
\r
5917 RECT rChild, rParent;
\r
5918 int wChild, hChild, wParent, hParent;
\r
5919 int wScreen, hScreen, xNew, yNew;
\r
5922 /* Get the Height and Width of the child window */
\r
5923 GetWindowRect (hwndChild, &rChild);
\r
5924 wChild = rChild.right - rChild.left;
\r
5925 hChild = rChild.bottom - rChild.top;
\r
5927 /* Get the Height and Width of the parent window */
\r
5928 GetWindowRect (hwndParent, &rParent);
\r
5929 wParent = rParent.right - rParent.left;
\r
5930 hParent = rParent.bottom - rParent.top;
\r
5932 /* Get the display limits */
\r
5933 hdc = GetDC (hwndChild);
\r
5934 wScreen = GetDeviceCaps (hdc, HORZRES);
\r
5935 hScreen = GetDeviceCaps (hdc, VERTRES);
\r
5936 ReleaseDC(hwndChild, hdc);
\r
5938 /* Calculate new X position, then adjust for screen */
\r
5939 xNew = rParent.left + ((wParent - wChild) /2);
\r
5942 } else if ((xNew+wChild) > wScreen) {
\r
5943 xNew = wScreen - wChild;
\r
5946 /* Calculate new Y position, then adjust for screen */
\r
5948 yNew = rParent.top + ((hParent - hChild) /2);
\r
5951 yNew = rParent.top + GetSystemMetrics( SM_CYCAPTION ) * 2 / 3;
\r
5956 } else if ((yNew+hChild) > hScreen) {
\r
5957 yNew = hScreen - hChild;
\r
5960 /* Set it, and return */
\r
5961 return SetWindowPos (hwndChild, NULL,
\r
5962 xNew, yNew, 0, 0, SWP_NOSIZE | SWP_NOZORDER);
\r
5965 /* Center one window over another */
\r
5966 BOOL CenterWindow (HWND hwndChild, HWND hwndParent)
\r
5968 return CenterWindowEx( hwndChild, hwndParent, 0 );
\r
5971 /*---------------------------------------------------------------------------*\
\r
5973 * Startup Dialog functions
\r
5975 \*---------------------------------------------------------------------------*/
\r
5977 InitComboStrings(HANDLE hwndCombo, char **cd)
\r
5979 SendMessage(hwndCombo, CB_RESETCONTENT, 0, 0);
\r
5981 while (*cd != NULL) {
\r
5982 SendMessage(hwndCombo, CB_ADDSTRING, 0, (LPARAM) T_(*cd));
\r
5988 InitComboStringsFromOption(HANDLE hwndCombo, char *str)
\r
5990 char buf1[MAX_ARG_LEN];
\r
5993 if (str[0] == '@') {
\r
5994 FILE* f = fopen(str + 1, "r");
\r
5996 DisplayFatalError(str + 1, errno, 2);
\r
5999 len = fread(buf1, 1, sizeof(buf1)-1, f);
\r
6001 buf1[len] = NULLCHAR;
\r
6005 SendMessage(hwndCombo, CB_RESETCONTENT, 0, 0);
\r
6008 char buf[MSG_SIZ];
\r
6009 char *end = strchr(str, '\n');
\r
6010 if (end == NULL) return;
\r
6011 memcpy(buf, str, end - str);
\r
6012 buf[end - str] = NULLCHAR;
\r
6013 SendMessage(hwndCombo, CB_ADDSTRING, 0, (LPARAM) buf);
\r
6019 SetStartupDialogEnables(HWND hDlg)
\r
6021 EnableWindow(GetDlgItem(hDlg, OPT_ChessEngineName),
\r
6022 IsDlgButtonChecked(hDlg, OPT_ChessEngine) ||
\r
6023 (appData.zippyPlay && IsDlgButtonChecked(hDlg, OPT_ChessServer)));
\r
6024 EnableWindow(GetDlgItem(hDlg, OPT_SecondChessEngineName),
\r
6025 IsDlgButtonChecked(hDlg, OPT_ChessEngine));
\r
6026 EnableWindow(GetDlgItem(hDlg, OPT_ChessServerName),
\r
6027 IsDlgButtonChecked(hDlg, OPT_ChessServer));
\r
6028 EnableWindow(GetDlgItem(hDlg, OPT_AdditionalOptions),
\r
6029 IsDlgButtonChecked(hDlg, OPT_AnyAdditional));
\r
6030 EnableWindow(GetDlgItem(hDlg, IDOK),
\r
6031 IsDlgButtonChecked(hDlg, OPT_ChessEngine) ||
\r
6032 IsDlgButtonChecked(hDlg, OPT_ChessServer) ||
\r
6033 IsDlgButtonChecked(hDlg, OPT_View));
\r
6037 QuoteForFilename(char *filename)
\r
6039 int dquote, space;
\r
6040 dquote = strchr(filename, '"') != NULL;
\r
6041 space = strchr(filename, ' ') != NULL;
\r
6042 if (dquote || space) {
\r
6054 InitEngineBox(HWND hDlg, HWND hwndCombo, char* nthcp, char* nthd, char* nthdir, char *nthnames)
\r
6056 char buf[MSG_SIZ];
\r
6059 InitComboStringsFromOption(hwndCombo, nthnames);
\r
6060 q = QuoteForFilename(nthcp);
\r
6061 snprintf(buf, MSG_SIZ, "%s%s%s", q, nthcp, q);
\r
6062 if (*nthdir != NULLCHAR) {
\r
6063 q = QuoteForFilename(nthdir);
\r
6064 snprintf(buf + strlen(buf), MSG_SIZ, " /%s=%s%s%s", nthd, q, nthdir, q);
\r
6066 if (*nthcp == NULLCHAR) {
\r
6067 SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) 0, (LPARAM) 0);
\r
6068 } else if (SendMessage(hwndCombo, CB_SELECTSTRING, (WPARAM) -1, (LPARAM) buf) == CB_ERR) {
\r
6069 SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) -1, (LPARAM) 0);
\r
6070 SendMessage(hwndCombo, WM_SETTEXT, (WPARAM) 0, (LPARAM) buf);
\r
6075 StartupDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
6077 char buf[MSG_SIZ];
\r
6081 switch (message) {
\r
6082 case WM_INITDIALOG:
\r
6083 /* Center the dialog */
\r
6084 CenterWindow (hDlg, GetDesktopWindow());
\r
6085 Translate(hDlg, DLG_Startup);
\r
6086 /* Initialize the dialog items */
\r
6087 InitEngineBox(hDlg, GetDlgItem(hDlg, OPT_ChessEngineName),
\r
6088 appData.firstChessProgram, "fd", appData.firstDirectory,
\r
6089 firstChessProgramNames);
\r
6090 InitEngineBox(hDlg, GetDlgItem(hDlg, OPT_SecondChessEngineName),
\r
6091 appData.secondChessProgram, singleList ? "fd" : "sd", appData.secondDirectory,
\r
6092 singleList ? firstChessProgramNames : secondChessProgramNames); //[HGM] single: use first list in second combo
\r
6093 hwndCombo = GetDlgItem(hDlg, OPT_ChessServerName);
\r
6094 InitComboStringsFromOption(hwndCombo, icsNames);
\r
6095 snprintf(buf, MSG_SIZ, "%s /icsport=%s", appData.icsHost, appData.icsPort);
\r
6096 if (*appData.icsHelper != NULLCHAR) {
\r
6097 char *q = QuoteForFilename(appData.icsHelper);
\r
6098 sprintf(buf + strlen(buf), " /icshelper=%s%s%s", q, appData.icsHelper, q);
\r
6100 if (*appData.icsHost == NULLCHAR) {
\r
6101 SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) 0, (LPARAM) 0);
\r
6102 /*SendMessage(hwndCombo, CB_SHOWDROPDOWN, (WPARAM) TRUE, (LPARAM) 0); !!too soon */
\r
6103 } else if (SendMessage(hwndCombo, CB_SELECTSTRING, (WPARAM) -1, (LPARAM) buf) == CB_ERR) {
\r
6104 SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) -1, (LPARAM) 0);
\r
6105 SendMessage(hwndCombo, WM_SETTEXT, (WPARAM) 0, (LPARAM) buf);
\r
6108 if (appData.icsActive) {
\r
6109 CheckDlgButton(hDlg, OPT_ChessServer, BST_CHECKED);
\r
6111 else if (appData.noChessProgram) {
\r
6112 CheckDlgButton(hDlg, OPT_View, BST_CHECKED);
\r
6115 CheckDlgButton(hDlg, OPT_ChessEngine, BST_CHECKED);
\r
6118 SetStartupDialogEnables(hDlg);
\r
6122 switch (LOWORD(wParam)) {
\r
6124 if (IsDlgButtonChecked(hDlg, OPT_ChessEngine)) {
\r
6125 safeStrCpy(buf, "/fcp=", sizeof(buf)/sizeof(buf[0]) );
\r
6126 GetDlgItemText(hDlg, OPT_ChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));
\r
6128 ParseArgs(StringGet, &p);
\r
6129 safeStrCpy(buf, singleList ? "/fcp=" : "/scp=", sizeof(buf)/sizeof(buf[0]) );
\r
6130 GetDlgItemText(hDlg, OPT_SecondChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));
\r
6132 SwapEngines(singleList); // temporarily swap first and second, to load a second 'first', ...
\r
6133 ParseArgs(StringGet, &p);
\r
6134 SwapEngines(singleList); // ... and then make it 'second'
\r
6135 appData.noChessProgram = FALSE;
\r
6136 appData.icsActive = FALSE;
\r
6137 } else if (IsDlgButtonChecked(hDlg, OPT_ChessServer)) {
\r
6138 safeStrCpy(buf, "/ics /icshost=", sizeof(buf)/sizeof(buf[0]) );
\r
6139 GetDlgItemText(hDlg, OPT_ChessServerName, buf + strlen(buf), sizeof(buf) - strlen(buf));
\r
6141 ParseArgs(StringGet, &p);
\r
6142 if (appData.zippyPlay) {
\r
6143 safeStrCpy(buf, "/fcp=", sizeof(buf)/sizeof(buf[0]) );
\r
6144 GetDlgItemText(hDlg, OPT_ChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));
\r
6146 ParseArgs(StringGet, &p);
\r
6148 } else if (IsDlgButtonChecked(hDlg, OPT_View)) {
\r
6149 appData.noChessProgram = TRUE;
\r
6150 appData.icsActive = FALSE;
\r
6152 MessageBox(hDlg, _("Choose an option, or cancel to exit"),
\r
6153 _("Option Error"), MB_OK|MB_ICONEXCLAMATION);
\r
6156 if (IsDlgButtonChecked(hDlg, OPT_AnyAdditional)) {
\r
6157 GetDlgItemText(hDlg, OPT_AdditionalOptions, buf, sizeof(buf));
\r
6159 ParseArgs(StringGet, &p);
\r
6161 EndDialog(hDlg, TRUE);
\r
6168 case IDM_HELPCONTENTS:
\r
6169 if (!WinHelp (hDlg, "winboard.hlp", HELP_KEY,(DWORD)(LPSTR)"CONTENTS")) {
\r
6170 MessageBox (GetFocus(),
\r
6171 _("Unable to activate help"),
\r
6172 szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);
\r
6177 SetStartupDialogEnables(hDlg);
\r
6185 /*---------------------------------------------------------------------------*\
\r
6187 * About box dialog functions
\r
6189 \*---------------------------------------------------------------------------*/
\r
6191 /* Process messages for "About" dialog box */
\r
6193 About(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
6195 switch (message) {
\r
6196 case WM_INITDIALOG: /* message: initialize dialog box */
\r
6197 /* Center the dialog over the application window */
\r
6198 CenterWindow (hDlg, GetWindow (hDlg, GW_OWNER));
\r
6199 SetDlgItemText(hDlg, ABOUTBOX_Version, programVersion);
\r
6200 Translate(hDlg, ABOUTBOX);
\r
6204 case WM_COMMAND: /* message: received a command */
\r
6205 if (LOWORD(wParam) == IDOK /* "OK" box selected? */
\r
6206 || LOWORD(wParam) == IDCANCEL) { /* System menu close command? */
\r
6207 EndDialog(hDlg, TRUE); /* Exit the dialog */
\r
6215 /*---------------------------------------------------------------------------*\
\r
6217 * Comment Dialog functions
\r
6219 \*---------------------------------------------------------------------------*/
\r
6222 CommentDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
6224 static HANDLE hwndText = NULL;
\r
6225 int len, newSizeX, newSizeY, flags;
\r
6226 static int sizeX, sizeY;
\r
6231 switch (message) {
\r
6232 case WM_INITDIALOG: /* message: initialize dialog box */
\r
6233 /* Initialize the dialog items */
\r
6234 Translate(hDlg, DLG_EditComment);
\r
6235 hwndText = GetDlgItem(hDlg, OPT_CommentText);
\r
6236 SetDlgItemText(hDlg, OPT_CommentText, commentText);
\r
6237 EnableWindow(GetDlgItem(hDlg, OPT_CancelComment), editComment);
\r
6238 EnableWindow(GetDlgItem(hDlg, OPT_ClearComment), editComment);
\r
6239 EnableWindow(GetDlgItem(hDlg, OPT_EditComment), !editComment);
\r
6240 SendMessage(hwndText, EM_SETREADONLY, !editComment, 0);
\r
6241 SetWindowText(hDlg, commentTitle);
\r
6242 if (editComment) {
\r
6243 SetFocus(hwndText);
\r
6245 SetFocus(GetDlgItem(hDlg, IDOK));
\r
6247 SendMessage(GetDlgItem(hDlg, OPT_CommentText),
\r
6248 WM_SETFONT, (WPARAM)font[boardSize][COMMENT_FONT]->hf,
\r
6249 MAKELPARAM(FALSE, 0));
\r
6250 /* Size and position the dialog */
\r
6251 if (!commentDialog) {
\r
6252 commentDialog = hDlg;
\r
6253 flags = SWP_NOZORDER;
\r
6254 GetClientRect(hDlg, &rect);
\r
6255 sizeX = rect.right;
\r
6256 sizeY = rect.bottom;
\r
6257 if (wpComment.x != CW_USEDEFAULT && wpComment.y != CW_USEDEFAULT &&
\r
6258 wpComment.width != CW_USEDEFAULT && wpComment.height != CW_USEDEFAULT) {
\r
6259 WINDOWPLACEMENT wp;
\r
6260 EnsureOnScreen(&wpComment.x, &wpComment.y, 0, 0);
\r
6261 wp.length = sizeof(WINDOWPLACEMENT);
\r
6263 wp.showCmd = SW_SHOW;
\r
6264 wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;
\r
6265 wp.rcNormalPosition.left = wpComment.x;
\r
6266 wp.rcNormalPosition.right = wpComment.x + wpComment.width;
\r
6267 wp.rcNormalPosition.top = wpComment.y;
\r
6268 wp.rcNormalPosition.bottom = wpComment.y + wpComment.height;
\r
6269 SetWindowPlacement(hDlg, &wp);
\r
6271 GetClientRect(hDlg, &rect);
\r
6272 newSizeX = rect.right;
\r
6273 newSizeY = rect.bottom;
\r
6274 ResizeEditPlusButtons(hDlg, hwndText, sizeX, sizeY,
\r
6275 newSizeX, newSizeY);
\r
6280 SendDlgItemMessage( hDlg, OPT_CommentText, EM_SETEVENTMASK, 0, ENM_MOUSEEVENTS | ENM_KEYEVENTS );
\r
6283 case WM_COMMAND: /* message: received a command */
\r
6284 switch (LOWORD(wParam)) {
\r
6286 if (editComment) {
\r
6288 /* Read changed options from the dialog box */
\r
6289 hwndText = GetDlgItem(hDlg, OPT_CommentText);
\r
6290 len = GetWindowTextLength(hwndText);
\r
6291 str = (char *) malloc(len + 1);
\r
6292 GetWindowText(hwndText, str, len + 1);
\r
6301 ReplaceComment(commentIndex, str);
\r
6308 case OPT_CancelComment:
\r
6312 case OPT_ClearComment:
\r
6313 SetDlgItemText(hDlg, OPT_CommentText, "");
\r
6316 case OPT_EditComment:
\r
6317 EditCommentEvent();
\r
6325 case WM_NOTIFY: // [HGM] vari: cloned from whistory.c
\r
6326 if( wParam == OPT_CommentText ) {
\r
6327 MSGFILTER * lpMF = (MSGFILTER *) lParam;
\r
6329 if( lpMF->msg == WM_RBUTTONDOWN && (lpMF->wParam & (MK_CONTROL | MK_SHIFT)) == 0 ||
\r
6330 lpMF->msg == WM_CHAR && lpMF->wParam == '\022' ) {
\r
6334 pt.x = LOWORD( lpMF->lParam );
\r
6335 pt.y = HIWORD( lpMF->lParam );
\r
6337 if(lpMF->msg == WM_CHAR) {
\r
6339 SendDlgItemMessage( hDlg, OPT_CommentText, EM_EXGETSEL, 0, (LPARAM) &sel );
\r
6340 index = sel.cpMin;
\r
6342 index = SendDlgItemMessage( hDlg, OPT_CommentText, EM_CHARFROMPOS, 0, (LPARAM) &pt );
\r
6344 hwndText = GetDlgItem(hDlg, OPT_CommentText); // cloned from above
\r
6345 len = GetWindowTextLength(hwndText);
\r
6346 str = (char *) malloc(len + 1);
\r
6347 GetWindowText(hwndText, str, len + 1);
\r
6348 ReplaceComment(commentIndex, str);
\r
6349 if(commentIndex != currentMove) ToNrEvent(commentIndex);
\r
6350 LoadVariation( index, str ); // [HGM] also does the actual moving to it, now
\r
6353 /* Zap the message for good: apparently, returning non-zero is not enough */
\r
6354 lpMF->msg = WM_USER;
\r
6362 newSizeX = LOWORD(lParam);
\r
6363 newSizeY = HIWORD(lParam);
\r
6364 ResizeEditPlusButtons(hDlg, hwndText, sizeX, sizeY, newSizeX, newSizeY);
\r
6369 case WM_GETMINMAXINFO:
\r
6370 /* Prevent resizing window too small */
\r
6371 mmi = (MINMAXINFO *) lParam;
\r
6372 mmi->ptMinTrackSize.x = 100;
\r
6373 mmi->ptMinTrackSize.y = 100;
\r
6380 EitherCommentPopUp(int index, char *title, char *str, BOOLEAN edit)
\r
6385 CheckMenuItem(GetMenu(hwndMain), IDM_Comment, edit ? MF_CHECKED : MF_UNCHECKED);
\r
6387 if (str == NULL) str = "";
\r
6388 p = (char *) malloc(2 * strlen(str) + 2);
\r
6391 if (*str == '\n') *q++ = '\r';
\r
6395 if (commentText != NULL) free(commentText);
\r
6397 commentIndex = index;
\r
6398 commentTitle = title;
\r
6400 editComment = edit;
\r
6402 if (commentDialog) {
\r
6403 SendMessage(commentDialog, WM_INITDIALOG, 0, 0);
\r
6404 if (!commentUp) ShowWindow(commentDialog, SW_SHOW);
\r
6406 lpProc = MakeProcInstance((FARPROC)CommentDialog, hInst);
\r
6407 CreateDialog(hInst, MAKEINTRESOURCE(DLG_EditComment),
\r
6408 hwndMain, (DLGPROC)lpProc);
\r
6409 FreeProcInstance(lpProc);
\r
6415 /*---------------------------------------------------------------------------*\
\r
6417 * Type-in move dialog functions
\r
6419 \*---------------------------------------------------------------------------*/
\r
6422 TypeInMoveDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
6424 char move[MSG_SIZ];
\r
6427 switch (message) {
\r
6428 case WM_INITDIALOG:
\r
6429 move[0] = (char) lParam;
\r
6430 move[1] = NULLCHAR;
\r
6431 CenterWindowEx(hDlg, GetWindow(hDlg, GW_OWNER), 1 );
\r
6432 Translate(hDlg, DLG_TypeInMove);
\r
6433 hInput = GetDlgItem(hDlg, OPT_Move);
\r
6434 SetWindowText(hInput, move);
\r
6436 SendMessage(hInput, EM_SETSEL, (WPARAM)9999, (LPARAM)9999);
\r
6440 switch (LOWORD(wParam)) {
\r
6443 shiftKey = GetKeyState(VK_SHIFT) < 0; // [HGM] remember last shift status
\r
6444 GetDlgItemText(hDlg, OPT_Move, move, sizeof(move));
6445 TypeInDoneEvent(move);
\r
6446 EndDialog(hDlg, TRUE);
\r
6449 EndDialog(hDlg, FALSE);
\r
6460 PopUpMoveDialog(char firstchar)
\r
6464 lpProc = MakeProcInstance((FARPROC)TypeInMoveDialog, hInst);
\r
6465 DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_TypeInMove),
\r
6466 hwndMain, (DLGPROC)lpProc, (LPARAM)firstchar);
\r
6467 FreeProcInstance(lpProc);
\r
6470 /*---------------------------------------------------------------------------*\
\r
6472 * Type-in name dialog functions
\r
6474 \*---------------------------------------------------------------------------*/
\r
6477 TypeInNameDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
6479 char move[MSG_SIZ];
\r
6482 switch (message) {
\r
6483 case WM_INITDIALOG:
\r
6484 move[0] = (char) lParam;
\r
6485 move[1] = NULLCHAR;
\r
6486 CenterWindowEx(hDlg, GetWindow(hDlg, GW_OWNER), 1 );
\r
6487 Translate(hDlg, DLG_TypeInName);
\r
6488 hInput = GetDlgItem(hDlg, OPT_Name);
\r
6489 SetWindowText(hInput, move);
\r
6491 SendMessage(hInput, EM_SETSEL, (WPARAM)9999, (LPARAM)9999);
\r
6495 switch (LOWORD(wParam)) {
\r
6497 GetDlgItemText(hDlg, OPT_Name, move, sizeof(move));
\r
6498 appData.userName = strdup(move);
\r
6501 if(gameMode == MachinePlaysWhite || gameMode == MachinePlaysBlack) {
\r
6502 snprintf(move, MSG_SIZ, "%s vs. %s", gameInfo.white, gameInfo.black);
\r
6503 DisplayTitle(move);
\r
6507 EndDialog(hDlg, TRUE);
\r
6510 EndDialog(hDlg, FALSE);
\r
6521 PopUpNameDialog(char firstchar)
\r
6525 lpProc = MakeProcInstance((FARPROC)TypeInNameDialog, hInst);
\r
6526 DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_TypeInName),
\r
6527 hwndMain, (DLGPROC)lpProc, (LPARAM)firstchar);
\r
6528 FreeProcInstance(lpProc);
\r
6531 /*---------------------------------------------------------------------------*\
\r
6535 \*---------------------------------------------------------------------------*/
\r
6537 /* Nonmodal error box */
\r
6538 LRESULT CALLBACK ErrorDialog(HWND hDlg, UINT message,
\r
6539 WPARAM wParam, LPARAM lParam);
\r
6542 ErrorPopUp(char *title, char *content)
\r
6546 BOOLEAN modal = hwndMain == NULL;
\r
6564 strncpy(errorTitle, title, sizeof(errorTitle));
\r
6565 errorTitle[sizeof(errorTitle) - 1] = '\0';
\r
6568 MessageBox(NULL, errorMessage, errorTitle, MB_OK|MB_ICONEXCLAMATION);
\r
6570 lpProc = MakeProcInstance((FARPROC)ErrorDialog, hInst);
\r
6571 CreateDialog(hInst, MAKEINTRESOURCE(DLG_Error),
\r
6572 hwndMain, (DLGPROC)lpProc);
\r
6573 FreeProcInstance(lpProc);
\r
6580 if (!appData.popupMoveErrors && moveErrorMessageUp) DisplayMessage("", "");
\r
6581 if (errorDialog == NULL) return;
\r
6582 DestroyWindow(errorDialog);
\r
6583 errorDialog = NULL;
\r
6584 if(errorExitStatus) ExitEvent(errorExitStatus);
\r
6588 ErrorDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
6593 switch (message) {
\r
6594 case WM_INITDIALOG:
\r
6595 GetWindowRect(hDlg, &rChild);
\r
6598 SetWindowPos(hDlg, NULL, rChild.left,
\r
6599 rChild.top + boardRect.top - (rChild.bottom - rChild.top),
\r
6600 0, 0, SWP_NOZORDER|SWP_NOSIZE);
\r
6604 [AS] It seems that the above code wants to move the dialog up in the "caption
\r
6605 area" of the main window, but it uses the dialog height as an hard-coded constant,
\r
6606 and it doesn't work when you resize the dialog.
\r
6607 For now, just give it a default position.
\r
6609 SetWindowPos(hDlg, NULL, boardRect.left+8, boardRect.top+8, 0, 0, SWP_NOZORDER|SWP_NOSIZE);
\r
6610 Translate(hDlg, DLG_Error);
\r
6612 errorDialog = hDlg;
\r
6613 SetWindowText(hDlg, errorTitle);
\r
6614 hwndText = GetDlgItem(hDlg, OPT_ErrorText);
\r
6615 SetDlgItemText(hDlg, OPT_ErrorText, errorMessage);
\r
6619 switch (LOWORD(wParam)) {
\r
6622 if (errorDialog == hDlg) errorDialog = NULL;
\r
6623 DestroyWindow(hDlg);
\r
6635 HWND gothicDialog = NULL;
\r
6638 GothicDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
6642 int height = GetSystemMetrics(SM_CYCAPTION)+GetSystemMetrics(SM_CYFRAME);
\r
6644 switch (message) {
\r
6645 case WM_INITDIALOG:
\r
6646 GetWindowRect(hDlg, &rChild);
\r
6648 SetWindowPos(hDlg, NULL, wpMain.x, wpMain.y-height, wpMain.width, height,
\r
6652 [AS] It seems that the above code wants to move the dialog up in the "caption
\r
6653 area" of the main window, but it uses the dialog height as an hard-coded constant,
\r
6654 and it doesn't work when you resize the dialog.
\r
6655 For now, just give it a default position.
\r
6657 gothicDialog = hDlg;
\r
6658 SetWindowText(hDlg, errorTitle);
\r
6659 hwndText = GetDlgItem(hDlg, OPT_ErrorText);
\r
6660 SetDlgItemText(hDlg, OPT_ErrorText, errorMessage);
\r
6664 switch (LOWORD(wParam)) {
\r
6667 if (errorDialog == hDlg) errorDialog = NULL;
\r
6668 DestroyWindow(hDlg);
\r
6680 GothicPopUp(char *title, VariantClass variant)
\r
6683 static char *lastTitle;
\r
6685 strncpy(errorTitle, title, sizeof(errorTitle));
\r
6686 errorTitle[sizeof(errorTitle) - 1] = '\0';
\r
6688 if(lastTitle != title && gothicDialog != NULL) {
\r
6689 DestroyWindow(gothicDialog);
\r
6690 gothicDialog = NULL;
\r
6692 if(variant != VariantNormal && gothicDialog == NULL) {
\r
6693 title = lastTitle;
\r
6694 lpProc = MakeProcInstance((FARPROC)GothicDialog, hInst);
\r
6695 CreateDialog(hInst, MAKEINTRESOURCE(DLG_Error),
\r
6696 hwndMain, (DLGPROC)lpProc);
\r
6697 FreeProcInstance(lpProc);
\r
6702 /*---------------------------------------------------------------------------*\
\r
6704 * Ics Interaction console functions
\r
6706 \*---------------------------------------------------------------------------*/
\r
6708 #define HISTORY_SIZE 64
\r
6709 static char *history[HISTORY_SIZE];
\r
6710 int histIn = 0, histP = 0;
\r
6713 SaveInHistory(char *cmd)
\r
6715 if (history[histIn] != NULL) {
\r
6716 free(history[histIn]);
\r
6717 history[histIn] = NULL;
\r
6719 if (*cmd == NULLCHAR) return;
\r
6720 history[histIn] = StrSave(cmd);
\r
6721 histIn = (histIn + 1) % HISTORY_SIZE;
\r
6722 if (history[histIn] != NULL) {
\r
6723 free(history[histIn]);
\r
6724 history[histIn] = NULL;
\r
6730 PrevInHistory(char *cmd)
\r
6733 if (histP == histIn) {
\r
6734 if (history[histIn] != NULL) free(history[histIn]);
\r
6735 history[histIn] = StrSave(cmd);
\r
6737 newhp = (histP - 1 + HISTORY_SIZE) % HISTORY_SIZE;
\r
6738 if (newhp == histIn || history[newhp] == NULL) return NULL;
\r
6740 return history[histP];
\r
6746 if (histP == histIn) return NULL;
\r
6747 histP = (histP + 1) % HISTORY_SIZE;
\r
6748 return history[histP];
\r
6752 LoadIcsTextMenu(IcsTextMenuEntry *e)
\r
6756 hmenu = LoadMenu(hInst, "TextMenu");
\r
6757 h = GetSubMenu(hmenu, 0);
\r
6759 if (strcmp(e->item, "-") == 0) {
\r
6760 AppendMenu(h, MF_SEPARATOR, 0, 0);
\r
6761 } else { // [HGM] re-written a bit to use only one AppendMenu call for both cases (| or no |)
\r
6762 int flags = MF_STRING, j = 0;
\r
6763 if (e->item[0] == '|') {
\r
6764 flags |= MF_MENUBARBREAK;
\r
6767 if(!strcmp(e->command, "none")) flags |= MF_GRAYED; // [HGM] chatclick: provide inactive dummy
\r
6768 AppendMenu(h, flags, IDM_CommandX + i, e->item + j);
\r
6776 WNDPROC consoleTextWindowProc;
\r
6779 CommandX(HWND hwnd, char *command, BOOLEAN getname, BOOLEAN immediate)
\r
6781 char buf[MSG_SIZ], name[MSG_SIZ];
\r
6782 HWND hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
6786 SetWindowText(hInput, command);
\r
6788 SendMessage(hInput, WM_CHAR, '\r', 0);
\r
6790 sel.cpMin = 999999;
\r
6791 sel.cpMax = 999999;
\r
6792 SendMessage(hInput, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
6797 SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);
\r
6798 if (sel.cpMin == sel.cpMax) {
\r
6799 /* Expand to surrounding word */
\r
6802 tr.chrg.cpMax = sel.cpMin;
\r
6803 tr.chrg.cpMin = --sel.cpMin;
\r
6804 if (sel.cpMin < 0) break;
\r
6805 tr.lpstrText = name;
\r
6806 SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr);
\r
6807 } while (isalpha(name[0]) || isdigit(name[0]) || name[0] == '-');
\r
6811 tr.chrg.cpMin = sel.cpMax;
\r
6812 tr.chrg.cpMax = ++sel.cpMax;
\r
6813 tr.lpstrText = name;
\r
6814 if (SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr) < 1) break;
\r
6815 } while (isalpha(name[0]) || isdigit(name[0]) || name[0] == '-');
\r
6818 if (sel.cpMax == sel.cpMin || sel.cpMax - sel.cpMin > MSG_SIZ/2) {
\r
6819 MessageBeep(MB_ICONEXCLAMATION);
\r
6823 tr.lpstrText = name;
\r
6824 SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr);
\r
6826 if (sel.cpMax - sel.cpMin > MSG_SIZ/2) {
\r
6827 MessageBeep(MB_ICONEXCLAMATION);
\r
6830 SendMessage(hwnd, EM_GETSELTEXT, 0, (LPARAM) name);
\r
6833 if(strstr(command, "%s")) snprintf(buf, MSG_SIZ, command, name); else
\r
6834 snprintf(buf, MSG_SIZ, "%s %s", command, name);
\r
6835 SetWindowText(hInput, buf);
\r
6836 SendMessage(hInput, WM_CHAR, '\r', 0);
\r
6838 if(!strcmp(command, "chat")) { ChatPopUp(name); return; }
\r
6839 snprintf(buf, MSG_SIZ, "%s %s ", command, name); /* trailing space */
\r
6840 SetWindowText(hInput, buf);
\r
6841 sel.cpMin = 999999;
\r
6842 sel.cpMax = 999999;
\r
6843 SendMessage(hInput, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
6849 ConsoleTextSubclass(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
\r
6854 switch (message) {
\r
6856 if (!(GetKeyState(VK_CONTROL) & ~1)) break;
\r
6857 if(wParam=='R') return 0;
\r
6860 SendMessage(hwnd, EM_LINESCROLL, 0, -999999);
\r
6863 sel.cpMin = 999999;
\r
6864 sel.cpMax = 999999;
\r
6865 SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
6866 SendMessage(hwnd, EM_SCROLLCARET, 0, 0);
\r
6871 if(wParam != '\022') {
\r
6872 if (wParam == '\t') {
\r
6873 if (GetKeyState(VK_SHIFT) < 0) {
\r
6875 if (IsIconic(hwndMain)) ShowWindow(hwndMain, SW_RESTORE);
\r
6876 if (buttonDesc[0].hwnd) {
\r
6877 SetFocus(buttonDesc[0].hwnd);
\r
6879 SetFocus(hwndMain);
\r
6883 SetFocus(GetDlgItem(hwndConsole, OPT_ConsoleInput));
\r
6886 hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
6887 JAWS_DELETE( SetFocus(hInput); )
\r
6888 SendMessage(hInput, message, wParam, lParam);
\r
6891 } // [HGM] navigate: for Ctrl+R, flow into next case (moved up here) to summon up menu
\r
6893 case WM_RBUTTONDOWN:
\r
6894 if (!(GetKeyState(VK_SHIFT) & ~1)) {
\r
6895 /* Move selection here if it was empty */
\r
6897 pt.x = LOWORD(lParam);
\r
6898 pt.y = HIWORD(lParam);
\r
6899 SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);
\r
6900 if (sel.cpMin == sel.cpMax) {
\r
6901 if(lParam != -1) sel.cpMin = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&pt); /*doc is wrong*/
\r
6902 sel.cpMax = sel.cpMin;
\r
6903 SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
6905 SendMessage(hwnd, EM_HIDESELECTION, FALSE, FALSE);
\r
6906 { // [HGM] chatclick: code moved here from WM_RBUTTONUP case, to have menu appear on down-click
\r
6908 HMENU hmenu = LoadIcsTextMenu(icsTextMenuEntry);
\r
6909 SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);
\r
6910 if (sel.cpMin == sel.cpMax) {
\r
6911 EnableMenuItem(hmenu, IDM_Copy, MF_BYCOMMAND|MF_GRAYED);
\r
6912 EnableMenuItem(hmenu, IDM_QuickPaste, MF_BYCOMMAND|MF_GRAYED);
\r
6914 if (!IsClipboardFormatAvailable(CF_TEXT)) {
\r
6915 EnableMenuItem(hmenu, IDM_Paste, MF_BYCOMMAND|MF_GRAYED);
\r
6917 pt.x = LOWORD(lParam)-30; // [HGM] chatclick: make menu pop up with pointer above upper-right item
\r
6918 pt.y = HIWORD(lParam)-10; // make it appear as if mouse moved there, so it will be selected on up-click
\r
6919 PostMessage(hwnd, WM_MOUSEMOVE, wParam, lParam+5);
\r
6920 MenuPopup(hwnd, pt, hmenu, -1);
\r
6924 case WM_RBUTTONUP:
\r
6925 if (GetKeyState(VK_SHIFT) & ~1) {
\r
6926 SendDlgItemMessage(hwndConsole, OPT_ConsoleText,
\r
6927 WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);
\r
6931 hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
6933 return SendMessage(hInput, message, wParam, lParam);
\r
6934 case WM_MBUTTONDOWN:
\r
6935 return SendMessage(hwnd, WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);
\r
6937 switch (LOWORD(wParam)) {
\r
6938 case IDM_QuickPaste:
\r
6940 SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);
\r
6941 if (sel.cpMin == sel.cpMax) {
\r
6942 MessageBeep(MB_ICONEXCLAMATION);
\r
6945 SendMessage(hwnd, WM_COPY, 0, 0);
\r
6946 hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
6947 SendMessage(hInput, WM_PASTE, 0, 0);
\r
6952 SendMessage(hwnd, WM_CUT, 0, 0);
\r
6955 SendMessage(hwnd, WM_PASTE, 0, 0);
\r
6958 SendMessage(hwnd, WM_COPY, 0, 0);
\r
6962 int i = LOWORD(wParam) - IDM_CommandX;
\r
6963 if (i >= 0 && i < ICS_TEXT_MENU_SIZE &&
\r
6964 icsTextMenuEntry[i].command != NULL) {
\r
6965 CommandX(hwnd, icsTextMenuEntry[i].command,
\r
6966 icsTextMenuEntry[i].getname,
\r
6967 icsTextMenuEntry[i].immediate);
\r
6975 return (*consoleTextWindowProc)(hwnd, message, wParam, lParam);
\r
6978 WNDPROC consoleInputWindowProc;
\r
6981 ConsoleInputSubclass(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
\r
6983 char buf[MSG_SIZ];
\r
6985 static BOOL sendNextChar = FALSE;
\r
6986 static BOOL quoteNextChar = FALSE;
\r
6987 InputSource *is = consoleInputSource;
\r
6991 switch (message) {
\r
6993 if (!appData.localLineEditing || sendNextChar) {
\r
6994 is->buf[0] = (CHAR) wParam;
\r
6996 SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);
\r
6997 sendNextChar = FALSE;
\r
7000 if (quoteNextChar) {
\r
7001 buf[0] = (char) wParam;
\r
7002 buf[1] = NULLCHAR;
\r
7003 SendMessage(hwnd, EM_REPLACESEL, TRUE, (LPARAM) buf);
\r
7004 quoteNextChar = FALSE;
\r
7008 case '\r': /* Enter key */
\r
7009 is->count = GetWindowText(hwnd, is->buf, INPUT_SOURCE_BUF_SIZE-1);
\r
7010 if (consoleEcho) SaveInHistory(is->buf);
\r
7011 is->buf[is->count++] = '\n';
\r
7012 is->buf[is->count] = NULLCHAR;
\r
7013 SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);
\r
7014 if (consoleEcho) {
\r
7015 ConsoleOutput(is->buf, is->count, TRUE);
\r
7016 } else if (appData.localLineEditing) {
\r
7017 ConsoleOutput("\n", 1, TRUE);
\r
7020 case '\033': /* Escape key */
\r
7021 SetWindowText(hwnd, "");
\r
7022 cf.cbSize = sizeof(CHARFORMAT);
\r
7023 cf.dwMask = CFM_COLOR|CFM_BOLD|CFM_ITALIC|CFM_UNDERLINE|CFM_STRIKEOUT;
\r
7024 if (consoleEcho) {
\r
7025 cf.crTextColor = textAttribs[ColorNormal].color;
\r
7027 cf.crTextColor = COLOR_ECHOOFF;
\r
7029 cf.dwEffects = textAttribs[ColorNormal].effects;
\r
7030 SendMessage(hwnd, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf);
\r
7032 case '\t': /* Tab key */
\r
7033 if (GetKeyState(VK_SHIFT) < 0) {
\r
7035 SetFocus(GetDlgItem(hwndConsole, OPT_ConsoleText));
\r
7038 if (IsIconic(hwndMain)) ShowWindow(hwndMain, SW_RESTORE);
\r
7039 if (buttonDesc[0].hwnd) {
\r
7040 SetFocus(buttonDesc[0].hwnd);
\r
7042 SetFocus(hwndMain);
\r
7046 case '\023': /* Ctrl+S */
\r
7047 sendNextChar = TRUE;
\r
7049 case '\021': /* Ctrl+Q */
\r
7050 quoteNextChar = TRUE;
\r
7060 GetWindowText(hwnd, buf, MSG_SIZ);
\r
7061 p = PrevInHistory(buf);
\r
7063 SetWindowText(hwnd, p);
\r
7064 sel.cpMin = 999999;
\r
7065 sel.cpMax = 999999;
\r
7066 SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
7071 p = NextInHistory();
\r
7073 SetWindowText(hwnd, p);
\r
7074 sel.cpMin = 999999;
\r
7075 sel.cpMax = 999999;
\r
7076 SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
7082 if (!(GetKeyState(VK_CONTROL) & ~1)) break;
\r
7086 SendDlgItemMessage(hwndConsole, OPT_ConsoleText, message, wParam, lParam);
\r
7090 case WM_MBUTTONDOWN:
\r
7091 SendDlgItemMessage(hwndConsole, OPT_ConsoleText,
\r
7092 WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);
\r
7094 case WM_RBUTTONUP:
\r
7095 if (GetKeyState(VK_SHIFT) & ~1) {
\r
7096 SendDlgItemMessage(hwndConsole, OPT_ConsoleText,
\r
7097 WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);
\r
7101 hmenu = LoadMenu(hInst, "InputMenu");
\r
7102 SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);
\r
7103 if (sel.cpMin == sel.cpMax) {
\r
7104 EnableMenuItem(hmenu, IDM_Copy, MF_BYCOMMAND|MF_GRAYED);
\r
7105 EnableMenuItem(hmenu, IDM_Cut, MF_BYCOMMAND|MF_GRAYED);
\r
7107 if (!IsClipboardFormatAvailable(CF_TEXT)) {
\r
7108 EnableMenuItem(hmenu, IDM_Paste, MF_BYCOMMAND|MF_GRAYED);
\r
7110 pt.x = LOWORD(lParam);
\r
7111 pt.y = HIWORD(lParam);
\r
7112 MenuPopup(hwnd, pt, hmenu, -1);
\r
7116 switch (LOWORD(wParam)) {
\r
7118 SendMessage(hwnd, EM_UNDO, 0, 0);
\r
7120 case IDM_SelectAll:
\r
7122 sel.cpMax = -1; /*999999?*/
\r
7123 SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
7126 SendMessage(hwnd, WM_CUT, 0, 0);
\r
7129 SendMessage(hwnd, WM_PASTE, 0, 0);
\r
7132 SendMessage(hwnd, WM_COPY, 0, 0);
\r
7137 return (*consoleInputWindowProc)(hwnd, message, wParam, lParam);
\r
7140 #define CO_MAX 100000
\r
7141 #define CO_TRIM 1000
\r
7144 ConsoleWndProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
7146 static SnapData sd;
\r
7147 HWND hText, hInput;
\r
7149 static int sizeX, sizeY;
\r
7150 int newSizeX, newSizeY;
\r
7154 hText = GetDlgItem(hDlg, OPT_ConsoleText);
\r
7155 hInput = GetDlgItem(hDlg, OPT_ConsoleInput);
\r
7157 switch (message) {
\r
7159 if (((NMHDR*)lParam)->code == EN_LINK)
\r
7161 ENLINK *pLink = (ENLINK*)lParam;
\r
7162 if (pLink->msg == WM_LBUTTONUP)
\r
7166 tr.chrg = pLink->chrg;
\r
7167 tr.lpstrText = malloc(1+tr.chrg.cpMax-tr.chrg.cpMin);
\r
7168 SendMessage(hText, EM_GETTEXTRANGE, 0, (LPARAM)&tr);
\r
7169 ShellExecute(NULL, "open", tr.lpstrText, NULL, NULL, SW_SHOW);
\r
7170 free(tr.lpstrText);
\r
7174 case WM_INITDIALOG: /* message: initialize dialog box */
\r
7175 hwndConsole = hDlg;
\r
7177 consoleTextWindowProc = (WNDPROC)
\r
7178 SetWindowLongPtr(hText, GWLP_WNDPROC, (LONG_PTR) ConsoleTextSubclass);
\r
7179 SendMessage(hText, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);
\r
7180 consoleInputWindowProc = (WNDPROC)
\r
7181 SetWindowLongPtr(hInput, GWLP_WNDPROC, (LONG_PTR) ConsoleInputSubclass);
\r
7182 SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);
\r
7183 Colorize(ColorNormal, TRUE);
\r
7184 SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &consoleCF);
\r
7185 ChangedConsoleFont();
\r
7186 GetClientRect(hDlg, &rect);
\r
7187 sizeX = rect.right;
\r
7188 sizeY = rect.bottom;
\r
7189 if (wpConsole.x != CW_USEDEFAULT && wpConsole.y != CW_USEDEFAULT &&
\r
7190 wpConsole.width != CW_USEDEFAULT && wpConsole.height != CW_USEDEFAULT) {
\r
7191 WINDOWPLACEMENT wp;
\r
7192 EnsureOnScreen(&wpConsole.x, &wpConsole.y, 0, 0);
\r
7193 wp.length = sizeof(WINDOWPLACEMENT);
\r
7195 wp.showCmd = SW_SHOW;
\r
7196 wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;
\r
7197 wp.rcNormalPosition.left = wpConsole.x;
\r
7198 wp.rcNormalPosition.right = wpConsole.x + wpConsole.width;
\r
7199 wp.rcNormalPosition.top = wpConsole.y;
\r
7200 wp.rcNormalPosition.bottom = wpConsole.y + wpConsole.height;
\r
7201 SetWindowPlacement(hDlg, &wp);
\r
7204 // [HGM] Chessknight's change 2004-07-13
\r
7205 else { /* Determine Defaults */
\r
7206 WINDOWPLACEMENT wp;
\r
7207 wpConsole.x = wpMain.width + 1;
\r
7208 wpConsole.y = wpMain.y;
\r
7209 wpConsole.width = screenWidth - wpMain.width;
\r
7210 wpConsole.height = wpMain.height;
\r
7211 EnsureOnScreen(&wpConsole.x, &wpConsole.y, 0, 0);
\r
7212 wp.length = sizeof(WINDOWPLACEMENT);
\r
7214 wp.showCmd = SW_SHOW;
\r
7215 wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;
\r
7216 wp.rcNormalPosition.left = wpConsole.x;
\r
7217 wp.rcNormalPosition.right = wpConsole.x + wpConsole.width;
\r
7218 wp.rcNormalPosition.top = wpConsole.y;
\r
7219 wp.rcNormalPosition.bottom = wpConsole.y + wpConsole.height;
\r
7220 SetWindowPlacement(hDlg, &wp);
\r
7223 // Allow hText to highlight URLs and send notifications on them
\r
7224 wMask = (WORD) SendMessage(hText, EM_GETEVENTMASK, 0, 0L);
\r
7225 SendMessage(hText, EM_SETEVENTMASK, 0, wMask | ENM_LINK);
\r
7226 SendMessage(hText, EM_AUTOURLDETECT, TRUE, 0L);
\r
7227 SetWindowLongPtr(hText, GWLP_USERDATA, 79); // initialize the text window's width
\r
7241 if (IsIconic(hDlg)) break;
\r
7242 newSizeX = LOWORD(lParam);
\r
7243 newSizeY = HIWORD(lParam);
\r
7244 if (sizeX != newSizeX || sizeY != newSizeY) {
\r
7245 RECT rectText, rectInput;
\r
7247 int newTextHeight, newTextWidth;
\r
7248 GetWindowRect(hText, &rectText);
\r
7249 newTextWidth = rectText.right - rectText.left + newSizeX - sizeX;
\r
7250 newTextHeight = rectText.bottom - rectText.top + newSizeY - sizeY;
\r
7251 if (newTextHeight < 0) {
\r
7252 newSizeY += -newTextHeight;
\r
7253 newTextHeight = 0;
\r
7255 SetWindowPos(hText, NULL, 0, 0,
\r
7256 newTextWidth, newTextHeight, SWP_NOZORDER|SWP_NOMOVE);
\r
7257 GetWindowRect(hInput, &rectInput); /* gives screen coords */
\r
7258 pt.x = rectInput.left;
\r
7259 pt.y = rectInput.top + newSizeY - sizeY;
\r
7260 ScreenToClient(hDlg, &pt);
\r
7261 SetWindowPos(hInput, NULL,
\r
7262 pt.x, pt.y, /* needs client coords */
\r
7263 rectInput.right - rectInput.left + newSizeX - sizeX,
\r
7264 rectInput.bottom - rectInput.top, SWP_NOZORDER);
\r
7270 case WM_GETMINMAXINFO:
\r
7271 /* Prevent resizing window too small */
\r
7272 mmi = (MINMAXINFO *) lParam;
\r
7273 mmi->ptMinTrackSize.x = 100;
\r
7274 mmi->ptMinTrackSize.y = 100;
\r
7277 /* [AS] Snapping */
\r
7278 case WM_ENTERSIZEMOVE:
\r
7279 return OnEnterSizeMove( &sd, hDlg, wParam, lParam );
\r
7282 return OnSizing( &sd, hDlg, wParam, lParam );
\r
7285 return OnMoving( &sd, hDlg, wParam, lParam );
\r
7287 case WM_EXITSIZEMOVE:
\r
7288 UpdateICSWidth(hText);
\r
7289 return OnExitSizeMove( &sd, hDlg, wParam, lParam );
\r
7292 return DefWindowProc(hDlg, message, wParam, lParam);
\r
7300 if (hwndConsole) return;
\r
7301 hCons = CreateDialog(hInst, szConsoleName, 0, NULL);
\r
7302 SendMessage(hCons, WM_INITDIALOG, 0, 0);
\r
7307 ConsoleOutput(char* data, int length, int forceVisible)
\r
7312 char buf[CO_MAX+1];
\r
7315 static int delayLF = 0;
\r
7316 CHARRANGE savesel, sel;
\r
7318 if (hwndConsole == NULL || length > CO_MAX-100 || length == 0) return;
\r
7326 while (length--) {
\r
7334 } else if (*p == '\007') {
\r
7335 MyPlaySound(&sounds[(int)SoundBell]);
\r
7342 hText = GetDlgItem(hwndConsole, OPT_ConsoleText);
\r
7343 SendMessage(hText, EM_HIDESELECTION, TRUE, FALSE);
\r
7344 /* Save current selection */
\r
7345 SendMessage(hText, EM_EXGETSEL, 0, (LPARAM)&savesel);
\r
7346 exlen = GetWindowTextLength(hText);
\r
7347 /* Find out whether current end of text is visible */
\r
7348 SendMessage(hText, EM_GETRECT, 0, (LPARAM) &rect);
\r
7349 SendMessage(hText, EM_POSFROMCHAR, (WPARAM) &pEnd, exlen);
\r
7350 /* Trim existing text if it's too long */
\r
7351 if (exlen + (q - buf) > CO_MAX) {
\r
7352 trim = (CO_TRIM > (q - buf)) ? CO_TRIM : (q - buf);
\r
7355 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
7356 SendMessage(hText, EM_REPLACESEL, 0, (LPARAM)"");
\r
7358 savesel.cpMin -= trim;
\r
7359 savesel.cpMax -= trim;
\r
7360 if (exlen < 0) exlen = 0;
\r
7361 if (savesel.cpMin < 0) savesel.cpMin = 0;
\r
7362 if (savesel.cpMax < savesel.cpMin) savesel.cpMax = savesel.cpMin;
\r
7364 /* Append the new text */
\r
7365 sel.cpMin = exlen;
\r
7366 sel.cpMax = exlen;
\r
7367 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
7368 SendMessage(hText, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&consoleCF);
\r
7369 SendMessage(hText, EM_REPLACESEL, 0, (LPARAM) buf);
\r
7370 if (forceVisible || exlen == 0 ||
\r
7371 (rect.left <= pEnd.x && pEnd.x < rect.right &&
\r
7372 rect.top <= pEnd.y && pEnd.y < rect.bottom)) {
\r
7373 /* Scroll to make new end of text visible if old end of text
\r
7374 was visible or new text is an echo of user typein */
\r
7375 sel.cpMin = 9999999;
\r
7376 sel.cpMax = 9999999;
\r
7377 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
7378 SendMessage(hText, EM_HIDESELECTION, FALSE, FALSE);
\r
7379 SendMessage(hText, EM_SCROLLCARET, 0, 0);
\r
7380 SendMessage(hText, EM_HIDESELECTION, TRUE, FALSE);
\r
7382 if (savesel.cpMax == exlen || forceVisible) {
\r
7383 /* Move insert point to new end of text if it was at the old
\r
7384 end of text or if the new text is an echo of user typein */
\r
7385 sel.cpMin = 9999999;
\r
7386 sel.cpMax = 9999999;
\r
7387 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
7389 /* Restore previous selection */
\r
7390 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&savesel);
\r
7392 SendMessage(hText, EM_HIDESELECTION, FALSE, FALSE);
\r
7399 DisplayHoldingsCount(HDC hdc, int x, int y, int rightAlign, int copyNumber)
\r
7403 COLORREF oldFg, oldBg;
\r
7408 snprintf(buf, sizeof(buf)/sizeof(buf[0]),"%d", copyNumber); else buf[0] = 0;
\r
7410 oldFg = SetTextColor(hdc, RGB(255, 255, 255)); /* white */
\r
7411 oldBg = SetBkColor(hdc, RGB(0, 0, 0)); /* black */
\r
7412 oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);
\r
7415 rect.right = x + squareSize;
\r
7417 rect.bottom = y + squareSize;
\r
7420 ExtTextOut(hdc, x + MESSAGE_LINE_LEFTMARGIN
\r
7421 + (rightAlign ? (squareSize*2)/3 : 0),
\r
7422 y, ETO_CLIPPED|ETO_OPAQUE,
\r
7423 &rect, str, strlen(str), NULL);
\r
7425 (void) SetTextColor(hdc, oldFg);
\r
7426 (void) SetBkColor(hdc, oldBg);
\r
7427 (void) SelectObject(hdc, oldFont);
\r
7431 DisplayAClock(HDC hdc, int timeRemaining, int highlight,
\r
7432 RECT *rect, char *color, char *flagFell)
\r
7436 COLORREF oldFg, oldBg;
\r
7439 if (appData.clockMode) {
\r
7441 snprintf(buf, sizeof(buf)/sizeof(buf[0]), "%c %s %s", color[0], TimeString(timeRemaining), flagFell);
\r
7443 snprintf(buf, sizeof(buf)/sizeof(buf[0]), "%s:%c%s %s", color, (logoHeight>0 ? 0 : ' '), TimeString(timeRemaining), flagFell);
\r
7450 oldFg = SetTextColor(hdc, RGB(255, 255, 255)); /* white */
\r
7451 oldBg = SetBkColor(hdc, RGB(0, 0, 0)); /* black */
\r
7453 oldFg = SetTextColor(hdc, RGB(0, 0, 0)); /* black */
\r
7454 oldBg = SetBkColor(hdc, RGB(255, 255, 255)); /* white */
\r
7456 oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);
\r
7460 ExtTextOut(hdc, rect->left + MESSAGE_LINE_LEFTMARGIN,
\r
7461 rect->top, ETO_CLIPPED|ETO_OPAQUE,
\r
7462 rect, str, strlen(str), NULL);
\r
7463 if(logoHeight > 0 && appData.clockMode) {
\r
7465 str += strlen(color)+2;
\r
7466 r.top = rect->top + logoHeight/2;
\r
7467 r.left = rect->left;
\r
7468 r.right = rect->right;
\r
7469 r.bottom = rect->bottom;
\r
7470 ExtTextOut(hdc, rect->left + MESSAGE_LINE_LEFTMARGIN,
\r
7471 r.top, ETO_CLIPPED|ETO_OPAQUE,
\r
7472 &r, str, strlen(str), NULL);
\r
7474 (void) SetTextColor(hdc, oldFg);
\r
7475 (void) SetBkColor(hdc, oldBg);
\r
7476 (void) SelectObject(hdc, oldFont);
\r
7481 DoReadFile(HANDLE hFile, char *buf, int count, DWORD *outCount,
\r
7487 if( count <= 0 ) {
\r
7488 if (appData.debugMode) {
\r
7489 fprintf( debugFP, "DoReadFile: trying to read past end of buffer, overflow = %d\n", count );
\r
7492 return ERROR_INVALID_USER_BUFFER;
\r
7495 ResetEvent(ovl->hEvent);
\r
7496 ovl->Offset = ovl->OffsetHigh = 0;
\r
7497 ok = ReadFile(hFile, buf, count, outCount, ovl);
\r
7501 err = GetLastError();
\r
7502 if (err == ERROR_IO_PENDING) {
\r
7503 ok = GetOverlappedResult(hFile, ovl, outCount, TRUE);
\r
7507 err = GetLastError();
\r
7514 DoWriteFile(HANDLE hFile, char *buf, int count, DWORD *outCount,
\r
7519 ResetEvent(ovl->hEvent);
\r
7520 ovl->Offset = ovl->OffsetHigh = 0;
\r
7521 ok = WriteFile(hFile, buf, count, outCount, ovl);
\r
7525 err = GetLastError();
\r
7526 if (err == ERROR_IO_PENDING) {
\r
7527 ok = GetOverlappedResult(hFile, ovl, outCount, TRUE);
\r
7531 err = GetLastError();
\r
7537 /* [AS] If input is line by line and a line exceed the buffer size, force an error */
\r
7538 void CheckForInputBufferFull( InputSource * is )
\r
7540 if( is->lineByLine && (is->next - is->buf) >= INPUT_SOURCE_BUF_SIZE ) {
\r
7541 /* Look for end of line */
\r
7542 char * p = is->buf;
\r
7544 while( p < is->next && *p != '\n' ) {
\r
7548 if( p >= is->next ) {
\r
7549 if (appData.debugMode) {
\r
7550 fprintf( debugFP, "Input line exceeded buffer size (source id=%lu)\n", is->id );
\r
7553 is->error = ERROR_BROKEN_PIPE; /* [AS] Just any non-successful code! */
\r
7554 is->count = (DWORD) -1;
\r
7555 is->next = is->buf;
\r
7561 InputThread(LPVOID arg)
\r
7566 is = (InputSource *) arg;
\r
7567 ovl.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
\r
7568 ovl.Internal = ovl.InternalHigh = ovl.Offset = ovl.OffsetHigh = 0;
\r
7569 while (is->hThread != NULL) {
\r
7570 is->error = DoReadFile(is->hFile, is->next,
\r
7571 INPUT_SOURCE_BUF_SIZE - (is->next - is->buf),
\r
7572 &is->count, &ovl);
\r
7573 if (is->error == NO_ERROR) {
\r
7574 is->next += is->count;
\r
7576 if (is->error == ERROR_BROKEN_PIPE) {
\r
7577 /* Correct for MS brain damage. EOF reading a pipe is not an error. */
\r
7580 is->count = (DWORD) -1;
\r
7581 /* [AS] The (is->count <= 0) check below is not useful for unsigned values! */
\r
7586 CheckForInputBufferFull( is );
\r
7588 SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);
\r
7590 if( is->count == ((DWORD) -1) ) break; /* [AS] */
\r
7592 if (is->count <= 0) break; /* Quit on EOF or error */
\r
7595 CloseHandle(ovl.hEvent);
\r
7596 CloseHandle(is->hFile);
\r
7598 if (appData.debugMode) {
\r
7599 fprintf( debugFP, "Input thread terminated (id=%lu, error=%d, count=%ld)\n", is->id, is->error, is->count );
\r
7606 /* Windows 95 beta 2 won't let you do overlapped i/o on a console or pipe */
\r
7608 NonOvlInputThread(LPVOID arg)
\r
7615 is = (InputSource *) arg;
\r
7616 while (is->hThread != NULL) {
\r
7617 is->error = ReadFile(is->hFile, is->next,
\r
7618 INPUT_SOURCE_BUF_SIZE - (is->next - is->buf),
\r
7619 &is->count, NULL) ? NO_ERROR : GetLastError();
\r
7620 if (is->error == NO_ERROR) {
\r
7621 /* Change CRLF to LF */
\r
7622 if (is->next > is->buf) {
\r
7624 i = is->count + 1;
\r
7632 if (prev == '\r' && *p == '\n') {
\r
7644 if (is->error == ERROR_BROKEN_PIPE) {
\r
7645 /* Correct for MS brain damage. EOF reading a pipe is not an error. */
\r
7648 is->count = (DWORD) -1;
\r
7652 CheckForInputBufferFull( is );
\r
7654 SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);
\r
7656 if( is->count == ((DWORD) -1) ) break; /* [AS] */
\r
7658 if (is->count < 0) break; /* Quit on error */
\r
7660 CloseHandle(is->hFile);
\r
7665 SocketInputThread(LPVOID arg)
\r
7669 is = (InputSource *) arg;
\r
7670 while (is->hThread != NULL) {
\r
7671 is->count = recv(is->sock, is->buf, INPUT_SOURCE_BUF_SIZE, 0);
\r
7672 if ((int)is->count == SOCKET_ERROR) {
\r
7673 is->count = (DWORD) -1;
\r
7674 is->error = WSAGetLastError();
\r
7676 is->error = NO_ERROR;
\r
7677 is->next += is->count;
\r
7678 if (is->count == 0 && is->second == is) {
\r
7679 /* End of file on stderr; quit with no message */
\r
7683 SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);
\r
7685 if( is->count == ((DWORD) -1) ) break; /* [AS] */
\r
7687 if (is->count <= 0) break; /* Quit on EOF or error */
\r
7693 InputEvent(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
\r
7697 is = (InputSource *) lParam;
\r
7698 if (is->lineByLine) {
\r
7699 /* Feed in lines one by one */
\r
7700 char *p = is->buf;
\r
7702 while (q < is->next) {
\r
7703 if (*q++ == '\n') {
\r
7704 (is->func)(is, is->closure, p, q - p, NO_ERROR);
\r
7709 /* Move any partial line to the start of the buffer */
\r
7711 while (p < is->next) {
\r
7716 if (is->error != NO_ERROR || is->count == 0) {
\r
7717 /* Notify backend of the error. Note: If there was a partial
\r
7718 line at the end, it is not flushed through. */
\r
7719 (is->func)(is, is->closure, is->buf, is->count, is->error);
\r
7722 /* Feed in the whole chunk of input at once */
\r
7723 (is->func)(is, is->closure, is->buf, is->count, is->error);
\r
7724 is->next = is->buf;
\r
7728 /*---------------------------------------------------------------------------*\
\r
7730 * Menu enables. Used when setting various modes.
\r
7732 \*---------------------------------------------------------------------------*/
\r
7740 GreyRevert(Boolean grey)
\r
7741 { // [HGM] vari: for retracting variations in local mode
\r
7742 HMENU hmenu = GetMenu(hwndMain);
\r
7743 EnableMenuItem(hmenu, IDM_Revert, MF_BYCOMMAND|(grey ? MF_GRAYED : MF_ENABLED));
\r
7744 EnableMenuItem(hmenu, IDM_Annotate, MF_BYCOMMAND|(grey ? MF_GRAYED : MF_ENABLED));
\r
7748 SetMenuEnables(HMENU hmenu, Enables *enab)
\r
7750 while (enab->item > 0) {
\r
7751 (void) EnableMenuItem(hmenu, enab->item, enab->flags);
\r
7756 Enables gnuEnables[] = {
\r
7757 { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },
\r
7758 { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },
\r
7759 { IDM_IcsClient, MF_BYCOMMAND|MF_GRAYED },
\r
7760 { IDM_Accept, MF_BYCOMMAND|MF_GRAYED },
\r
7761 { IDM_Decline, MF_BYCOMMAND|MF_GRAYED },
\r
7762 { IDM_Rematch, MF_BYCOMMAND|MF_GRAYED },
\r
7763 { IDM_Adjourn, MF_BYCOMMAND|MF_GRAYED },
\r
7764 { IDM_StopExamining, MF_BYCOMMAND|MF_GRAYED },
\r
7765 { IDM_StopObserving, MF_BYCOMMAND|MF_GRAYED },
\r
7766 { IDM_Upload, MF_BYCOMMAND|MF_GRAYED },
\r
7767 { IDM_Revert, MF_BYCOMMAND|MF_GRAYED },
\r
7768 { IDM_Annotate, MF_BYCOMMAND|MF_GRAYED },
\r
7769 { IDM_NewChat, MF_BYCOMMAND|MF_GRAYED },
\r
7771 // Needed to switch from ncp to GNU mode on Engine Load
\r
7772 { ACTION_POS, MF_BYPOSITION|MF_ENABLED },
\r
7773 { IDM_MachineWhite, MF_BYCOMMAND|MF_ENABLED },
\r
7774 { IDM_MachineBlack, MF_BYCOMMAND|MF_ENABLED },
\r
7775 { IDM_TwoMachines, MF_BYCOMMAND|MF_ENABLED },
\r
7776 { IDM_Match, MF_BYCOMMAND|MF_ENABLED },
\r
7777 { IDM_AnalysisMode, MF_BYCOMMAND|MF_ENABLED },
\r
7778 { IDM_AnalyzeFile, MF_BYCOMMAND|MF_ENABLED },
\r
7779 { IDM_Engine1Options, MF_BYCOMMAND|MF_ENABLED },
\r
7780 { IDM_Engine2Options, MF_BYCOMMAND|MF_ENABLED },
\r
7781 { IDM_TimeControl, MF_BYCOMMAND|MF_ENABLED },
\r
7782 { IDM_RetractMove, MF_BYCOMMAND|MF_ENABLED },
\r
7783 { IDM_MoveNow, MF_BYCOMMAND|MF_ENABLED },
\r
7784 { IDM_Hint, MF_BYCOMMAND|MF_ENABLED },
\r
7785 { IDM_Book, MF_BYCOMMAND|MF_ENABLED },
\r
7789 Enables icsEnables[] = {
\r
7790 { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },
\r
7791 { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },
\r
7792 { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },
\r
7793 { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },
\r
7794 { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },
\r
7795 { IDM_Match, MF_BYCOMMAND|MF_GRAYED },
\r
7796 { IDM_MachineBoth, MF_BYCOMMAND|MF_GRAYED },
\r
7797 { IDM_AnalysisMode, MF_BYCOMMAND|MF_ENABLED },
\r
7798 { IDM_AnalyzeFile, MF_BYCOMMAND|MF_GRAYED },
\r
7799 { IDM_TimeControl, MF_BYCOMMAND|MF_GRAYED },
\r
7800 { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },
\r
7801 { IDM_Hint, MF_BYCOMMAND|MF_GRAYED },
\r
7802 { IDM_Book, MF_BYCOMMAND|MF_GRAYED },
\r
7803 { IDM_EditProgs2, MF_BYCOMMAND|MF_GRAYED },
\r
7804 { IDM_IcsOptions, MF_BYCOMMAND|MF_ENABLED },
\r
7805 { IDM_Engine1Options, MF_BYCOMMAND|MF_GRAYED },
\r
7806 { IDM_Engine2Options, MF_BYCOMMAND|MF_GRAYED },
\r
7807 { IDM_Annotate, MF_BYCOMMAND|MF_GRAYED },
\r
7808 { IDM_Tourney, MF_BYCOMMAND|MF_GRAYED },
\r
7813 Enables zippyEnables[] = {
\r
7814 { IDM_MoveNow, MF_BYCOMMAND|MF_ENABLED },
\r
7815 { IDM_Hint, MF_BYCOMMAND|MF_ENABLED },
\r
7816 { IDM_Book, MF_BYCOMMAND|MF_ENABLED },
\r
7817 { IDM_Engine1Options, MF_BYCOMMAND|MF_ENABLED },
\r
7822 Enables ncpEnables[] = {
\r
7823 { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },
\r
7824 { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },
\r
7825 { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },
\r
7826 { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },
\r
7827 { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },
\r
7828 { IDM_Match, MF_BYCOMMAND|MF_GRAYED },
\r
7829 { IDM_AnalysisMode, MF_BYCOMMAND|MF_GRAYED },
\r
7830 { IDM_AnalyzeFile, MF_BYCOMMAND|MF_GRAYED },
\r
7831 { IDM_IcsClient, MF_BYCOMMAND|MF_GRAYED },
\r
7832 { ACTION_POS, MF_BYPOSITION|MF_GRAYED },
\r
7833 { IDM_Revert, MF_BYCOMMAND|MF_GRAYED },
\r
7834 { IDM_Annotate, MF_BYCOMMAND|MF_GRAYED },
\r
7835 { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },
\r
7836 { IDM_RetractMove, MF_BYCOMMAND|MF_GRAYED },
\r
7837 { IDM_TimeControl, MF_BYCOMMAND|MF_GRAYED },
\r
7838 { IDM_Hint, MF_BYCOMMAND|MF_GRAYED },
\r
7839 { IDM_Book, MF_BYCOMMAND|MF_GRAYED },
\r
7840 { IDM_MachineBoth, MF_BYCOMMAND|MF_GRAYED },
\r
7841 { IDM_NewChat, MF_BYCOMMAND|MF_GRAYED },
\r
7842 { IDM_Engine1Options, MF_BYCOMMAND|MF_GRAYED },
\r
7843 { IDM_Engine2Options, MF_BYCOMMAND|MF_GRAYED },
\r
7844 { IDM_Sounds, MF_BYCOMMAND|MF_GRAYED },
\r
7848 Enables trainingOnEnables[] = {
\r
7849 { IDM_EditComment, MF_BYCOMMAND|MF_GRAYED },
\r
7850 { IDM_Comment, MF_BYCOMMAND|MF_GRAYED },
\r
7851 { IDM_Pause, MF_BYCOMMAND|MF_GRAYED },
\r
7852 { IDM_Forward, MF_BYCOMMAND|MF_GRAYED },
\r
7853 { IDM_Backward, MF_BYCOMMAND|MF_GRAYED },
\r
7854 { IDM_ToEnd, MF_BYCOMMAND|MF_GRAYED },
\r
7855 { IDM_ToStart, MF_BYCOMMAND|MF_GRAYED },
\r
7856 { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },
\r
7857 { IDM_TruncateGame, MF_BYCOMMAND|MF_GRAYED },
\r
7861 Enables trainingOffEnables[] = {
\r
7862 { IDM_EditComment, MF_BYCOMMAND|MF_ENABLED },
\r
7863 { IDM_Comment, MF_BYCOMMAND|MF_ENABLED },
\r
7864 { IDM_Pause, MF_BYCOMMAND|MF_ENABLED },
\r
7865 { IDM_Forward, MF_BYCOMMAND|MF_ENABLED },
\r
7866 { IDM_Backward, MF_BYCOMMAND|MF_ENABLED },
\r
7867 { IDM_ToEnd, MF_BYCOMMAND|MF_ENABLED },
\r
7868 { IDM_ToStart, MF_BYCOMMAND|MF_ENABLED },
\r
7869 { IDM_MoveNow, MF_BYCOMMAND|MF_ENABLED },
\r
7870 { IDM_TruncateGame, MF_BYCOMMAND|MF_ENABLED },
\r
7874 /* These modify either ncpEnables or gnuEnables */
\r
7875 Enables cmailEnables[] = {
\r
7876 { IDM_MailMove, MF_BYCOMMAND|MF_ENABLED },
\r
7877 { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_ENABLED },
\r
7878 { ACTION_POS, MF_BYPOSITION|MF_ENABLED },
\r
7879 { IDM_CallFlag, MF_BYCOMMAND|MF_GRAYED },
\r
7880 { IDM_Draw, MF_BYCOMMAND|MF_ENABLED },
\r
7881 { IDM_Adjourn, MF_BYCOMMAND|MF_GRAYED },
\r
7882 { IDM_Abort, MF_BYCOMMAND|MF_GRAYED },
\r
7886 Enables machineThinkingEnables[] = {
\r
7887 { IDM_LoadGame, MF_BYCOMMAND|MF_GRAYED },
\r
7888 { IDM_LoadNextGame, MF_BYCOMMAND|MF_GRAYED },
\r
7889 { IDM_LoadPrevGame, MF_BYCOMMAND|MF_GRAYED },
\r
7890 { IDM_ReloadGame, MF_BYCOMMAND|MF_GRAYED },
\r
7891 { IDM_PasteGame, MF_BYCOMMAND|MF_GRAYED },
\r
7892 { IDM_LoadPosition, MF_BYCOMMAND|MF_GRAYED },
\r
7893 { IDM_LoadNextPosition, MF_BYCOMMAND|MF_GRAYED },
\r
7894 { IDM_LoadPrevPosition, MF_BYCOMMAND|MF_GRAYED },
\r
7895 { IDM_ReloadPosition, MF_BYCOMMAND|MF_GRAYED },
\r
7896 { IDM_PastePosition, MF_BYCOMMAND|MF_GRAYED },
\r
7897 { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },
\r
7898 { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },
\r
7899 { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },
\r
7900 // { IDM_Match, MF_BYCOMMAND|MF_GRAYED },
\r
7901 { IDM_TypeInMove, MF_BYCOMMAND|MF_GRAYED },
\r
7902 { IDM_RetractMove, MF_BYCOMMAND|MF_GRAYED },
\r
7906 Enables userThinkingEnables[] = {
\r
7907 { IDM_LoadGame, MF_BYCOMMAND|MF_ENABLED },
\r
7908 { IDM_LoadNextGame, MF_BYCOMMAND|MF_ENABLED },
\r
7909 { IDM_LoadPrevGame, MF_BYCOMMAND|MF_ENABLED },
\r
7910 { IDM_ReloadGame, MF_BYCOMMAND|MF_ENABLED },
\r
7911 { IDM_PasteGame, MF_BYCOMMAND|MF_ENABLED },
\r
7912 { IDM_LoadPosition, MF_BYCOMMAND|MF_ENABLED },
\r
7913 { IDM_LoadNextPosition, MF_BYCOMMAND|MF_ENABLED },
\r
7914 { IDM_LoadPrevPosition, MF_BYCOMMAND|MF_ENABLED },
\r
7915 { IDM_ReloadPosition, MF_BYCOMMAND|MF_ENABLED },
\r
7916 { IDM_PastePosition, MF_BYCOMMAND|MF_ENABLED },
\r
7917 { IDM_MachineWhite, MF_BYCOMMAND|MF_ENABLED },
\r
7918 { IDM_MachineBlack, MF_BYCOMMAND|MF_ENABLED },
\r
7919 { IDM_TwoMachines, MF_BYCOMMAND|MF_ENABLED },
\r
7920 // { IDM_Match, MF_BYCOMMAND|MF_ENABLED },
\r
7921 { IDM_TypeInMove, MF_BYCOMMAND|MF_ENABLED },
\r
7922 { IDM_RetractMove, MF_BYCOMMAND|MF_ENABLED },
\r
7926 /*---------------------------------------------------------------------------*\
\r
7928 * Front-end interface functions exported by XBoard.
\r
7929 * Functions appear in same order as prototypes in frontend.h.
\r
7931 \*---------------------------------------------------------------------------*/
\r
7933 CheckMark(UINT item, int state)
\r
7935 if(item) CheckMenuItem(GetMenu(hwndMain), item, MF_BYCOMMAND|state);
\r
7941 static UINT prevChecked = 0;
\r
7942 static int prevPausing = 0;
\r
7945 if (pausing != prevPausing) {
\r
7946 prevPausing = pausing;
\r
7947 (void) CheckMenuItem(GetMenu(hwndMain), IDM_Pause,
\r
7948 MF_BYCOMMAND|(pausing ? MF_CHECKED : MF_UNCHECKED));
\r
7949 if (hwndPause) SetWindowText(hwndPause, pausing ? "C" : "P");
\r
7952 switch (gameMode) {
\r
7953 case BeginningOfGame:
\r
7954 if (appData.icsActive)
\r
7955 nowChecked = IDM_IcsClient;
\r
7956 else if (appData.noChessProgram)
\r
7957 nowChecked = IDM_EditGame;
\r
7959 nowChecked = IDM_MachineBlack;
\r
7961 case MachinePlaysBlack:
\r
7962 nowChecked = IDM_MachineBlack;
\r
7964 case MachinePlaysWhite:
\r
7965 nowChecked = IDM_MachineWhite;
\r
7967 case TwoMachinesPlay:
\r
7968 nowChecked = IDM_TwoMachines;
\r
7971 nowChecked = IDM_AnalysisMode;
\r
7974 nowChecked = IDM_AnalyzeFile;
\r
7977 nowChecked = IDM_EditGame;
\r
7979 case PlayFromGameFile:
\r
7980 nowChecked = IDM_LoadGame;
\r
7982 case EditPosition:
\r
7983 nowChecked = IDM_EditPosition;
\r
7986 nowChecked = IDM_Training;
\r
7988 case IcsPlayingWhite:
\r
7989 case IcsPlayingBlack:
\r
7990 case IcsObserving:
\r
7992 nowChecked = IDM_IcsClient;
\r
7999 CheckMark(prevChecked, MF_UNCHECKED);
\r
8000 CheckMark(nowChecked, MF_CHECKED);
\r
8001 CheckMark(IDM_Match, matchMode && matchGame < appData.matchGames ? MF_CHECKED : MF_UNCHECKED);
\r
8003 if (nowChecked == IDM_LoadGame || nowChecked == IDM_Training) {
\r
8004 (void) EnableMenuItem(GetMenu(hwndMain), IDM_Training,
\r
8005 MF_BYCOMMAND|MF_ENABLED);
\r
8007 (void) EnableMenuItem(GetMenu(hwndMain),
\r
8008 IDM_Training, MF_BYCOMMAND|MF_GRAYED);
\r
8011 prevChecked = nowChecked;
\r
8013 /* [DM] icsEngineAnalyze - Do a sceure check too */
\r
8014 if (appData.icsActive) {
\r
8015 if (appData.icsEngineAnalyze) {
\r
8016 CheckMark(IDM_AnalysisMode, MF_CHECKED);
\r
8018 CheckMark(IDM_AnalysisMode, MF_UNCHECKED);
\r
8021 DisplayLogos(); // [HGM] logos: mode change could have altered logos
\r
8027 HMENU hmenu = GetMenu(hwndMain);
\r
8028 SetMenuEnables(hmenu, icsEnables);
\r
8029 EnableMenuItem(GetSubMenu(hmenu, OPTIONS_POS), IDM_IcsOptions,
\r
8030 MF_BYCOMMAND|MF_ENABLED);
\r
8032 if (appData.zippyPlay) {
\r
8033 SetMenuEnables(hmenu, zippyEnables);
\r
8034 if (!appData.noChessProgram) /* [DM] icsEngineAnalyze */
\r
8035 (void) EnableMenuItem(GetMenu(hwndMain), IDM_AnalysisMode,
\r
8036 MF_BYCOMMAND|MF_ENABLED);
\r
8044 SetMenuEnables(GetMenu(hwndMain), gnuEnables);
\r
8050 HMENU hmenu = GetMenu(hwndMain);
\r
8051 SetMenuEnables(hmenu, ncpEnables);
\r
8052 DrawMenuBar(hwndMain);
\r
8058 SetMenuEnables(GetMenu(hwndMain), cmailEnables);
\r
8062 SetTrainingModeOn()
\r
8065 SetMenuEnables(GetMenu(hwndMain), trainingOnEnables);
\r
8066 for (i = 0; i < N_BUTTONS; i++) {
\r
8067 if (buttonDesc[i].hwnd != NULL)
\r
8068 EnableWindow(buttonDesc[i].hwnd, FALSE);
\r
8073 VOID SetTrainingModeOff()
\r
8076 SetMenuEnables(GetMenu(hwndMain), trainingOffEnables);
\r
8077 for (i = 0; i < N_BUTTONS; i++) {
\r
8078 if (buttonDesc[i].hwnd != NULL)
\r
8079 EnableWindow(buttonDesc[i].hwnd, TRUE);
\r
8085 SetUserThinkingEnables()
\r
8087 SetMenuEnables(GetMenu(hwndMain), userThinkingEnables);
\r
8091 SetMachineThinkingEnables()
\r
8093 HMENU hMenu = GetMenu(hwndMain);
\r
8094 int flags = MF_BYCOMMAND|MF_ENABLED;
\r
8096 SetMenuEnables(hMenu, machineThinkingEnables);
\r
8098 if (gameMode == MachinePlaysBlack) {
\r
8099 (void)EnableMenuItem(hMenu, IDM_MachineBlack, flags);
\r
8100 } else if (gameMode == MachinePlaysWhite) {
\r
8101 (void)EnableMenuItem(hMenu, IDM_MachineWhite, flags);
\r
8102 } else if (gameMode == TwoMachinesPlay) {
\r
8103 (void)EnableMenuItem(hMenu, matchMode ? IDM_Match : IDM_TwoMachines, flags); // [HGM] match
\r
8109 DisplayTitle(char *str)
\r
8111 char title[MSG_SIZ], *host;
\r
8112 if (str[0] != NULLCHAR) {
\r
8113 safeStrCpy(title, str, sizeof(title)/sizeof(title[0]) );
\r
8114 } else if (appData.icsActive) {
\r
8115 if (appData.icsCommPort[0] != NULLCHAR)
\r
8118 host = appData.icsHost;
\r
8119 snprintf(title, MSG_SIZ, "%s: %s", szTitle, host);
\r
8120 } else if (appData.noChessProgram) {
\r
8121 safeStrCpy(title, szTitle, sizeof(title)/sizeof(title[0]) );
\r
8123 safeStrCpy(title, szTitle, sizeof(title)/sizeof(title[0]) );
\r
8124 strcat(title, ": ");
\r
8125 strcat(title, first.tidy);
\r
8127 SetWindowText(hwndMain, title);
\r
8132 DisplayMessage(char *str1, char *str2)
\r
8136 int remain = MESSAGE_TEXT_MAX - 1;
\r
8139 moveErrorMessageUp = FALSE; /* turned on later by caller if needed */
\r
8140 messageText[0] = NULLCHAR;
\r
8142 len = strlen(str1);
\r
8143 if (len > remain) len = remain;
\r
8144 strncpy(messageText, str1, len);
\r
8145 messageText[len] = NULLCHAR;
\r
8148 if (*str2 && remain >= 2) {
\r
8150 strcat(messageText, " ");
\r
8153 len = strlen(str2);
\r
8154 if (len > remain) len = remain;
\r
8155 strncat(messageText, str2, len);
\r
8157 messageText[MESSAGE_TEXT_MAX - 1] = NULLCHAR;
8158 safeStrCpy(lastMsg, messageText, MSG_SIZ);
8160 if (hwndMain == NULL || IsIconic(hwndMain)) return;
\r
8164 hdc = GetDC(hwndMain);
\r
8165 oldFont = SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);
\r
8166 ExtTextOut(hdc, messageRect.left, messageRect.top, ETO_CLIPPED|ETO_OPAQUE,
\r
8167 &messageRect, messageText, strlen(messageText), NULL);
\r
8168 (void) SelectObject(hdc, oldFont);
\r
8169 (void) ReleaseDC(hwndMain, hdc);
\r
8173 DisplayError(char *str, int error)
\r
8175 char buf[MSG_SIZ*2], buf2[MSG_SIZ];
\r
8179 safeStrCpy(buf, str, sizeof(buf)/sizeof(buf[0]) );
\r
8181 len = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,
\r
8182 NULL, error, LANG_NEUTRAL,
\r
8183 (LPSTR) buf2, MSG_SIZ, NULL);
\r
8185 snprintf(buf, 2*MSG_SIZ, "%s:\n%s", str, buf2);
\r
8187 ErrorMap *em = errmap;
\r
8188 while (em->err != 0 && em->err != error) em++;
\r
8189 if (em->err != 0) {
\r
8190 snprintf(buf, 2*MSG_SIZ, "%s:\n%s", str, em->msg);
\r
8192 snprintf(buf, 2*MSG_SIZ, "%s:\nError code %d", str, error);
\r
8197 ErrorPopUp(_("Error"), buf);
\r
8202 DisplayMoveError(char *str)
\r
8204 fromX = fromY = -1;
\r
8205 ClearHighlights();
\r
8206 DrawPosition(FALSE, NULL);
\r
8207 if (appData.popupMoveErrors) {
\r
8208 ErrorPopUp(_("Error"), str);
\r
8210 DisplayMessage(str, "");
\r
8211 moveErrorMessageUp = TRUE;
\r
8216 DisplayFatalError(char *str, int error, int exitStatus)
\r
8218 char buf[2*MSG_SIZ], buf2[MSG_SIZ];
\r
8220 char *label = exitStatus ? _("Fatal Error") : _("Exiting");
\r
8223 len = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,
\r
8224 NULL, error, LANG_NEUTRAL,
\r
8225 (LPSTR) buf2, MSG_SIZ, NULL);
\r
8227 snprintf(buf, 2*MSG_SIZ, "%s:\n%s", str, buf2);
\r
8229 ErrorMap *em = errmap;
\r
8230 while (em->err != 0 && em->err != error) em++;
\r
8231 if (em->err != 0) {
\r
8232 snprintf(buf, 2*MSG_SIZ, "%s:\n%s", str, em->msg);
\r
8234 snprintf(buf, 2*MSG_SIZ, "%s:\nError code %d", str, error);
\r
8239 if (appData.debugMode) {
\r
8240 fprintf(debugFP, "%s: %s\n", label, str);
\r
8242 if (appData.popupExitMessage) {
\r
8243 (void) MessageBox(hwndMain, str, label, MB_OK|
\r
8244 (exitStatus ? MB_ICONSTOP : MB_ICONINFORMATION));
\r
8246 ExitEvent(exitStatus);
\r
8251 DisplayInformation(char *str)
\r
8253 (void) MessageBox(hwndMain, str, _("Information"), MB_OK|MB_ICONINFORMATION);
\r
8258 DisplayNote(char *str)
\r
8260 ErrorPopUp(_("Note"), str);
\r
8265 char *title, *question, *replyPrefix;
\r
8270 QuestionDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
8272 static QuestionParams *qp;
\r
8273 char reply[MSG_SIZ];
\r
8276 switch (message) {
\r
8277 case WM_INITDIALOG:
\r
8278 qp = (QuestionParams *) lParam;
\r
8279 CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));
\r
8280 Translate(hDlg, DLG_Question);
\r
8281 SetWindowText(hDlg, qp->title);
\r
8282 SetDlgItemText(hDlg, OPT_QuestionText, qp->question);
\r
8283 SetFocus(GetDlgItem(hDlg, OPT_QuestionInput));
\r
8287 switch (LOWORD(wParam)) {
\r
8289 safeStrCpy(reply, qp->replyPrefix, sizeof(reply)/sizeof(reply[0]) );
\r
8290 if (*reply) strcat(reply, " ");
\r
8291 len = strlen(reply);
\r
8292 GetDlgItemText(hDlg, OPT_QuestionInput, reply + len, sizeof(reply) - len);
\r
8293 strcat(reply, "\n");
\r
8294 OutputToProcess(qp->pr, reply, strlen(reply), &err);
\r
8295 EndDialog(hDlg, TRUE);
\r
8296 if (err) DisplayFatalError(_("Error writing to chess program"), err, 1);
\r
8299 EndDialog(hDlg, FALSE);
\r
8310 AskQuestion(char* title, char *question, char *replyPrefix, ProcRef pr)
\r
8312 QuestionParams qp;
\r
8316 qp.question = question;
\r
8317 qp.replyPrefix = replyPrefix;
\r
8319 lpProc = MakeProcInstance((FARPROC)QuestionDialog, hInst);
\r
8320 DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_Question),
\r
8321 hwndMain, (DLGPROC)lpProc, (LPARAM)&qp);
\r
8322 FreeProcInstance(lpProc);
\r
8325 /* [AS] Pick FRC position */
\r
8326 LRESULT CALLBACK NewGameFRC_Proc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
8328 static int * lpIndexFRC;
\r
8334 case WM_INITDIALOG:
\r
8335 lpIndexFRC = (int *) lParam;
\r
8337 CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));
\r
8338 Translate(hDlg, DLG_NewGameFRC);
\r
8340 SendDlgItemMessage( hDlg, IDC_NFG_Edit, EM_SETLIMITTEXT, sizeof(buf)-1, 0 );
\r
8341 SetDlgItemInt( hDlg, IDC_NFG_Edit, *lpIndexFRC, TRUE );
\r
8342 SendDlgItemMessage( hDlg, IDC_NFG_Edit, EM_SETSEL, 0, -1 );
\r
8343 SetFocus(GetDlgItem(hDlg, IDC_NFG_Edit));
\r
8348 switch( LOWORD(wParam) ) {
\r
8350 *lpIndexFRC = GetDlgItemInt(hDlg, IDC_NFG_Edit, &index_is_ok, TRUE );
\r
8351 EndDialog( hDlg, 0 );
\r
8352 shuffleOpenings = TRUE; /* [HGM] shuffle: switch shuffling on for as long as we stay in current variant */
\r
8355 EndDialog( hDlg, 1 );
\r
8357 case IDC_NFG_Edit:
\r
8358 if( HIWORD(wParam) == EN_CHANGE ) {
\r
8359 GetDlgItemInt(hDlg, IDC_NFG_Edit, &index_is_ok, TRUE );
\r
8361 EnableWindow( GetDlgItem(hDlg, IDOK), index_is_ok );
\r
8364 case IDC_NFG_Random:
\r
8365 snprintf( buf, sizeof(buf)/sizeof(buf[0]), "%d", myrandom() ); /* [HGM] shuffle: no longer limit to 960 */
\r
8366 SetDlgItemText(hDlg, IDC_NFG_Edit, buf );
\r
8379 int index = appData.defaultFrcPosition;
\r
8380 FARPROC lpProc = MakeProcInstance( (FARPROC) NewGameFRC_Proc, hInst );
\r
8382 result = DialogBoxParam( hInst, MAKEINTRESOURCE(DLG_NewGameFRC), hwndMain, (DLGPROC)lpProc, (LPARAM)&index );
\r
8384 if( result == 0 ) {
\r
8385 appData.defaultFrcPosition = index;
\r
8391 /* [AS] Game list options. Refactored by HGM */
\r
8393 HWND gameListOptionsDialog;
\r
8395 // low-level front-end: clear text edit / list widget
\r
8399 SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_RESETCONTENT, 0, 0 );
\r
8402 // low-level front-end: clear text edit / list widget
\r
8404 GLT_DeSelectList()
\r
8406 SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_SETCURSEL, 0, 0 );
\r
8409 // low-level front-end: append line to text edit / list widget
\r
8411 GLT_AddToList( char *name )
\r
8414 SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_ADDSTRING, 0, (LPARAM) name );
\r
8418 // low-level front-end: get line from text edit / list widget
\r
8420 GLT_GetFromList( int index, char *name )
\r
8423 if( SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_GETTEXT, index, (LPARAM) name ) != LB_ERR )
\r
8429 void GLT_MoveSelection( HWND hDlg, int delta )
\r
8431 int idx1 = (int) SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETCURSEL, 0, 0 );
\r
8432 int idx2 = idx1 + delta;
\r
8433 int count = (int) SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETCOUNT, 0, 0 );
\r
8435 if( idx1 >=0 && idx1 < count && idx2 >= 0 && idx2 < count ) {
\r
8438 SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETTEXT, idx1, (LPARAM) buf );
\r
8439 SendDlgItemMessage( hDlg, IDC_GameListTags, LB_DELETESTRING, idx1, 0 );
\r
8440 SendDlgItemMessage( hDlg, IDC_GameListTags, LB_INSERTSTRING, idx2, (LPARAM) buf );
\r
8441 SendDlgItemMessage( hDlg, IDC_GameListTags, LB_SETCURSEL, idx2, 0 );
\r
8445 LRESULT CALLBACK GameListOptions_Proc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
8449 case WM_INITDIALOG:
\r
8450 gameListOptionsDialog = hDlg; // [HGM] pass through global to keep out off back-end
\r
8452 CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));
\r
8453 Translate(hDlg, DLG_GameListOptions);
\r
8455 /* Initialize list */
\r
8456 GLT_TagsToList( lpUserGLT );
\r
8458 SetFocus( GetDlgItem(hDlg, IDC_GameListTags) );
\r
8463 switch( LOWORD(wParam) ) {
\r
8466 EndDialog( hDlg, 0 );
\r
8469 EndDialog( hDlg, 1 );
\r
8472 case IDC_GLT_Default:
\r
8473 GLT_TagsToList( GLT_DEFAULT_TAGS );
\r
8476 case IDC_GLT_Restore:
\r
8477 GLT_TagsToList( appData.gameListTags );
\r
8481 GLT_MoveSelection( hDlg, -1 );
\r
8484 case IDC_GLT_Down:
\r
8485 GLT_MoveSelection( hDlg, +1 );
\r
8495 int GameListOptions()
\r
8498 FARPROC lpProc = MakeProcInstance( (FARPROC) GameListOptions_Proc, hInst );
\r
8500 safeStrCpy( lpUserGLT, appData.gameListTags ,LPUSERGLT_SIZE );
\r
8502 result = DialogBoxParam( hInst, MAKEINTRESOURCE(DLG_GameListOptions), hwndMain, (DLGPROC)lpProc, (LPARAM)lpUserGLT );
\r
8504 if( result == 0 ) {
\r
8505 /* [AS] Memory leak here! */
\r
8506 appData.gameListTags = strdup( lpUserGLT );
\r
8513 DisplayIcsInteractionTitle(char *str)
\r
8515 char consoleTitle[MSG_SIZ];
\r
8517 snprintf(consoleTitle, MSG_SIZ, "%s: %s", szConsoleTitle, str);
\r
8518 SetWindowText(hwndConsole, consoleTitle);
\r
8522 DrawPosition(int fullRedraw, Board board)
\r
8524 HDCDrawPosition(NULL, (BOOLEAN) fullRedraw, board);
\r
8527 void NotifyFrontendLogin()
\r
8530 UpdateICSWidth(GetDlgItem(hwndConsole, OPT_ConsoleText));
\r
8536 fromX = fromY = -1;
\r
8537 if (dragInfo.pos.x != -1 || dragInfo.pos.y != -1) {
\r
8538 dragInfo.pos.x = dragInfo.pos.y = -1;
\r
8539 dragInfo.pos.x = dragInfo.pos.y = -1;
\r
8540 dragInfo.lastpos = dragInfo.pos;
\r
8541 dragInfo.start.x = dragInfo.start.y = -1;
\r
8542 dragInfo.from = dragInfo.start;
\r
8544 DrawPosition(TRUE, NULL);
\r
8551 CommentPopUp(char *title, char *str)
\r
8553 HWND hwnd = GetActiveWindow();
\r
8554 EitherCommentPopUp(currentMove, title, str, FALSE); // [HGM] vari: fake move index, rather than 0
\r
8556 SetActiveWindow(hwnd);
\r
8560 CommentPopDown(void)
\r
8562 CheckMenuItem(GetMenu(hwndMain), IDM_Comment, MF_UNCHECKED);
\r
8563 if (commentDialog) {
\r
8564 ShowWindow(commentDialog, SW_HIDE);
\r
8566 commentUp = FALSE;
\r
8570 EditCommentPopUp(int index, char *title, char *str)
\r
8572 EitherCommentPopUp(index, title, str, TRUE);
\r
8579 MyPlaySound(&sounds[(int)SoundMove]);
\r
8582 VOID PlayIcsWinSound()
\r
8584 MyPlaySound(&sounds[(int)SoundIcsWin]);
\r
8587 VOID PlayIcsLossSound()
\r
8589 MyPlaySound(&sounds[(int)SoundIcsLoss]);
\r
8592 VOID PlayIcsDrawSound()
\r
8594 MyPlaySound(&sounds[(int)SoundIcsDraw]);
\r
8597 VOID PlayIcsUnfinishedSound()
\r
8599 MyPlaySound(&sounds[(int)SoundIcsUnfinished]);
\r
8605 MyPlaySound(&sounds[(int)SoundAlarm]);
\r
8611 MyPlaySound(&textAttribs[ColorTell].sound);
\r
8619 consoleEcho = TRUE;
\r
8620 hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
8621 SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&consoleCF);
\r
8622 SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);
\r
8631 consoleEcho = FALSE;
\r
8632 hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
8633 /* This works OK: set text and background both to the same color */
\r
8635 cf.crTextColor = COLOR_ECHOOFF;
\r
8636 SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf);
\r
8637 SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, cf.crTextColor);
\r
8640 /* No Raw()...? */
\r
8642 void Colorize(ColorClass cc, int continuation)
\r
8644 currentColorClass = cc;
\r
8645 consoleCF.dwMask = CFM_COLOR|CFM_BOLD|CFM_ITALIC|CFM_UNDERLINE|CFM_STRIKEOUT;
\r
8646 consoleCF.crTextColor = textAttribs[cc].color;
\r
8647 consoleCF.dwEffects = textAttribs[cc].effects;
\r
8648 if (!continuation) MyPlaySound(&textAttribs[cc].sound);
\r
8654 static char buf[MSG_SIZ];
\r
8655 DWORD bufsiz = MSG_SIZ;
\r
8657 if(appData.userName != NULL && appData.userName[0] != 0) {
\r
8658 return appData.userName; /* [HGM] username: prefer name selected by user over his system login */
\r
8660 if (!GetUserName(buf, &bufsiz)) {
\r
8661 /*DisplayError("Error getting user name", GetLastError());*/
\r
8662 safeStrCpy(buf, _("User"), sizeof(buf)/sizeof(buf[0]) );
\r
8670 static char buf[MSG_SIZ];
\r
8671 DWORD bufsiz = MSG_SIZ;
\r
8673 if (!GetComputerName(buf, &bufsiz)) {
\r
8674 /*DisplayError("Error getting host name", GetLastError());*/
\r
8675 safeStrCpy(buf, _("Unknown"), sizeof(buf)/sizeof(buf[0]) );
\r
8682 ClockTimerRunning()
\r
8684 return clockTimerEvent != 0;
\r
8690 if (clockTimerEvent == 0) return FALSE;
\r
8691 KillTimer(hwndMain, clockTimerEvent);
\r
8692 clockTimerEvent = 0;
\r
8697 StartClockTimer(long millisec)
\r
8699 clockTimerEvent = SetTimer(hwndMain, (UINT) CLOCK_TIMER_ID,
\r
8700 (UINT) millisec, NULL);
\r
8704 DisplayWhiteClock(long timeRemaining, int highlight)
\r
8707 char *flag = whiteFlag && gameMode == TwoMachinesPlay ? "(!)" : "";
\r
8709 if(appData.noGUI) return;
\r
8710 hdc = GetDC(hwndMain);
\r
8711 if (!IsIconic(hwndMain)) {
\r
8712 DisplayAClock(hdc, timeRemaining, highlight,
\r
8713 flipClock ? &blackRect : &whiteRect, _("White"), flag);
\r
8715 if (highlight && iconCurrent == iconBlack) {
\r
8716 iconCurrent = iconWhite;
\r
8717 PostMessage(hwndMain, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);
\r
8718 if (IsIconic(hwndMain)) {
\r
8719 DrawIcon(hdc, 2, 2, iconCurrent);
\r
8722 (void) ReleaseDC(hwndMain, hdc);
\r
8724 PostMessage(hwndConsole, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);
\r
8728 DisplayBlackClock(long timeRemaining, int highlight)
\r
8731 char *flag = blackFlag && gameMode == TwoMachinesPlay ? "(!)" : "";
\r
8733 if(appData.noGUI) return;
\r
8734 hdc = GetDC(hwndMain);
\r
8735 if (!IsIconic(hwndMain)) {
\r
8736 DisplayAClock(hdc, timeRemaining, highlight,
\r
8737 flipClock ? &whiteRect : &blackRect, _("Black"), flag);
\r
8739 if (highlight && iconCurrent == iconWhite) {
\r
8740 iconCurrent = iconBlack;
\r
8741 PostMessage(hwndMain, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);
\r
8742 if (IsIconic(hwndMain)) {
\r
8743 DrawIcon(hdc, 2, 2, iconCurrent);
\r
8746 (void) ReleaseDC(hwndMain, hdc);
\r
8748 PostMessage(hwndConsole, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);
\r
8753 LoadGameTimerRunning()
\r
8755 return loadGameTimerEvent != 0;
\r
8759 StopLoadGameTimer()
\r
8761 if (loadGameTimerEvent == 0) return FALSE;
\r
8762 KillTimer(hwndMain, loadGameTimerEvent);
\r
8763 loadGameTimerEvent = 0;
\r
8768 StartLoadGameTimer(long millisec)
\r
8770 loadGameTimerEvent = SetTimer(hwndMain, (UINT) LOAD_GAME_TIMER_ID,
\r
8771 (UINT) millisec, NULL);
\r
8779 char fileTitle[MSG_SIZ];
\r
8781 defName = DefaultFileName(appData.oldSaveStyle ? "gam" : "pgn");
\r
8782 f = OpenFileDialog(hwndMain, "a", defName,
\r
8783 appData.oldSaveStyle ? "gam" : "pgn",
\r
8785 _("Save Game to File"), NULL, fileTitle, NULL);
\r
8787 SaveGame(f, 0, "");
\r
8794 ScheduleDelayedEvent(DelayedEventCallback cb, long millisec)
\r
8796 if (delayedTimerEvent != 0) {
\r
8797 if (appData.debugMode && cb != delayedTimerCallback) { // [HGM] alive: not too much debug
\r
8798 fprintf(debugFP, "ScheduleDelayedEvent: event already scheduled\n");
\r
8800 KillTimer(hwndMain, delayedTimerEvent);
\r
8801 delayedTimerEvent = 0;
\r
8802 if(delayedTimerCallback != cb) // [HGM] alive: do not "flush" same event, just postpone it
\r
8803 delayedTimerCallback();
\r
8805 delayedTimerCallback = cb;
\r
8806 delayedTimerEvent = SetTimer(hwndMain, (UINT) DELAYED_TIMER_ID,
\r
8807 (UINT) millisec, NULL);
\r
8810 DelayedEventCallback
\r
8813 if (delayedTimerEvent) {
\r
8814 return delayedTimerCallback;
\r
8821 CancelDelayedEvent()
\r
8823 if (delayedTimerEvent) {
\r
8824 KillTimer(hwndMain, delayedTimerEvent);
\r
8825 delayedTimerEvent = 0;
\r
8829 DWORD GetWin32Priority(int nice)
\r
8830 { // [HGM] nice: translate Unix nice() value to indows priority class. (Code stolen from Polyglot 1.4w11)
\r
8832 REALTIME_PRIORITY_CLASS 0x00000100
\r
8833 HIGH_PRIORITY_CLASS 0x00000080
\r
8834 ABOVE_NORMAL_PRIORITY_CLASS 0x00008000
\r
8835 NORMAL_PRIORITY_CLASS 0x00000020
\r
8836 BELOW_NORMAL_PRIORITY_CLASS 0x00004000
\r
8837 IDLE_PRIORITY_CLASS 0x00000040
\r
8839 if (nice < -15) return 0x00000080;
\r
8840 if (nice < 0) return 0x00008000;
\r
8841 if (nice == 0) return 0x00000020;
\r
8842 if (nice < 15) return 0x00004000;
\r
8843 return 0x00000040;
\r
8846 /* Start a child process running the given program.
\r
8847 The process's standard output can be read from "from", and its
\r
8848 standard input can be written to "to".
\r
8849 Exit with fatal error if anything goes wrong.
\r
8850 Returns an opaque pointer that can be used to destroy the process
\r
8854 StartChildProcess(char *cmdLine, char *dir, ProcRef *pr)
\r
8856 #define BUFSIZE 4096
\r
8858 HANDLE hChildStdinRd, hChildStdinWr,
\r
8859 hChildStdoutRd, hChildStdoutWr;
\r
8860 HANDLE hChildStdinWrDup, hChildStdoutRdDup;
\r
8861 SECURITY_ATTRIBUTES saAttr;
\r
8863 PROCESS_INFORMATION piProcInfo;
\r
8864 STARTUPINFO siStartInfo;
\r
8866 char buf[MSG_SIZ];
\r
8869 if (appData.debugMode) {
\r
8870 fprintf(debugFP, "StartChildProcess (dir=\"%s\") %s\n", dir, cmdLine);
\r
8875 /* Set the bInheritHandle flag so pipe handles are inherited. */
\r
8876 saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);
\r
8877 saAttr.bInheritHandle = TRUE;
\r
8878 saAttr.lpSecurityDescriptor = NULL;
\r
8881 * The steps for redirecting child's STDOUT:
\r
8882 * 1. Create anonymous pipe to be STDOUT for child.
\r
8883 * 2. Create a noninheritable duplicate of read handle,
\r
8884 * and close the inheritable read handle.
\r
8887 /* Create a pipe for the child's STDOUT. */
\r
8888 if (! CreatePipe(&hChildStdoutRd, &hChildStdoutWr, &saAttr, 0)) {
\r
8889 return GetLastError();
\r
8892 /* Duplicate the read handle to the pipe, so it is not inherited. */
\r
8893 fSuccess = DuplicateHandle(GetCurrentProcess(), hChildStdoutRd,
\r
8894 GetCurrentProcess(), &hChildStdoutRdDup, 0,
\r
8895 FALSE, /* not inherited */
\r
8896 DUPLICATE_SAME_ACCESS);
\r
8898 return GetLastError();
\r
8900 CloseHandle(hChildStdoutRd);
\r
8903 * The steps for redirecting child's STDIN:
\r
8904 * 1. Create anonymous pipe to be STDIN for child.
\r
8905 * 2. Create a noninheritable duplicate of write handle,
\r
8906 * and close the inheritable write handle.
\r
8909 /* Create a pipe for the child's STDIN. */
\r
8910 if (! CreatePipe(&hChildStdinRd, &hChildStdinWr, &saAttr, 0)) {
\r
8911 return GetLastError();
\r
8914 /* Duplicate the write handle to the pipe, so it is not inherited. */
\r
8915 fSuccess = DuplicateHandle(GetCurrentProcess(), hChildStdinWr,
\r
8916 GetCurrentProcess(), &hChildStdinWrDup, 0,
\r
8917 FALSE, /* not inherited */
\r
8918 DUPLICATE_SAME_ACCESS);
\r
8920 return GetLastError();
\r
8922 CloseHandle(hChildStdinWr);
\r
8924 /* Arrange to (1) look in dir for the child .exe file, and
\r
8925 * (2) have dir be the child's working directory. Interpret
\r
8926 * dir relative to the directory WinBoard loaded from. */
\r
8927 GetCurrentDirectory(MSG_SIZ, buf);
\r
8928 SetCurrentDirectory(installDir);
\r
8929 SetCurrentDirectory(dir);
\r
8931 /* Now create the child process. */
\r
8933 siStartInfo.cb = sizeof(STARTUPINFO);
\r
8934 siStartInfo.lpReserved = NULL;
\r
8935 siStartInfo.lpDesktop = NULL;
\r
8936 siStartInfo.lpTitle = NULL;
\r
8937 siStartInfo.dwFlags = STARTF_USESTDHANDLES;
\r
8938 siStartInfo.cbReserved2 = 0;
\r
8939 siStartInfo.lpReserved2 = NULL;
\r
8940 siStartInfo.hStdInput = hChildStdinRd;
\r
8941 siStartInfo.hStdOutput = hChildStdoutWr;
\r
8942 siStartInfo.hStdError = hChildStdoutWr;
\r
8944 fSuccess = CreateProcess(NULL,
\r
8945 cmdLine, /* command line */
\r
8946 NULL, /* process security attributes */
\r
8947 NULL, /* primary thread security attrs */
\r
8948 TRUE, /* handles are inherited */
\r
8949 DETACHED_PROCESS|CREATE_NEW_PROCESS_GROUP,
\r
8950 NULL, /* use parent's environment */
\r
8952 &siStartInfo, /* STARTUPINFO pointer */
\r
8953 &piProcInfo); /* receives PROCESS_INFORMATION */
\r
8955 err = GetLastError();
\r
8956 SetCurrentDirectory(buf); /* return to prev directory */
\r
8961 if (appData.niceEngines){ // [HGM] nice: adjust engine proc priority
\r
8962 if(appData.debugMode) fprintf(debugFP, "nice engine proc to %d\n", appData.niceEngines);
\r
8963 SetPriorityClass(piProcInfo.hProcess, GetWin32Priority(appData.niceEngines));
\r
8966 /* Close the handles we don't need in the parent */
\r
8967 CloseHandle(piProcInfo.hThread);
\r
8968 CloseHandle(hChildStdinRd);
\r
8969 CloseHandle(hChildStdoutWr);
\r
8971 /* Prepare return value */
\r
8972 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
\r
8973 cp->kind = CPReal;
\r
8974 cp->hProcess = piProcInfo.hProcess;
\r
8975 cp->pid = piProcInfo.dwProcessId;
\r
8976 cp->hFrom = hChildStdoutRdDup;
\r
8977 cp->hTo = hChildStdinWrDup;
\r
8979 *pr = (void *) cp;
\r
8981 /* Klaus Friedel says that this Sleep solves a problem under Windows
\r
8982 2000 where engines sometimes don't see the initial command(s)
\r
8983 from WinBoard and hang. I don't understand how that can happen,
\r
8984 but the Sleep is harmless, so I've put it in. Others have also
\r
8985 reported what may be the same problem, so hopefully this will fix
\r
8986 it for them too. */
\r
8994 DestroyChildProcess(ProcRef pr, int/*boolean*/ signal)
\r
8996 ChildProc *cp; int result;
\r
8998 cp = (ChildProc *) pr;
\r
8999 if (cp == NULL) return;
\r
9001 switch (cp->kind) {
\r
9003 /* TerminateProcess is considered harmful, so... */
\r
9004 CloseHandle(cp->hTo); /* Closing this will give the child an EOF and hopefully kill it */
\r
9005 if (cp->hFrom) CloseHandle(cp->hFrom); /* if NULL, InputThread will close it */
\r
9006 /* The following doesn't work because the chess program
\r
9007 doesn't "have the same console" as WinBoard. Maybe
\r
9008 we could arrange for this even though neither WinBoard
\r
9009 nor the chess program uses a console for stdio? */
\r
9010 /*!!if (signal) GenerateConsoleCtrlEvent(CTRL_BREAK_EVENT, cp->pid);*/
\r
9012 /* [AS] Special termination modes for misbehaving programs... */
\r
9013 if( signal == 9 ) {
\r
9014 result = TerminateProcess( cp->hProcess, 0 );
\r
9016 if ( appData.debugMode) {
\r
9017 fprintf( debugFP, "Terminating process %lu, result=%d\n", cp->pid, result );
\r
9020 else if( signal == 10 ) {
\r
9021 DWORD dw = WaitForSingleObject( cp->hProcess, 3*1000 ); // Wait 3 seconds at most
\r
9023 if( dw != WAIT_OBJECT_0 ) {
\r
9024 result = TerminateProcess( cp->hProcess, 0 );
\r
9026 if ( appData.debugMode) {
\r
9027 fprintf( debugFP, "Process %lu still alive after timeout, killing... result=%d\n", cp->pid, result );
\r
9033 CloseHandle(cp->hProcess);
\r
9037 if (cp->hFrom) CloseHandle(cp->hFrom);
\r
9041 closesocket(cp->sock);
\r
9046 if (signal) send(cp->sock2, "\017", 1, 0); /* 017 = 15 = SIGTERM */
\r
9047 closesocket(cp->sock);
\r
9048 closesocket(cp->sock2);
\r
9056 InterruptChildProcess(ProcRef pr)
\r
9060 cp = (ChildProc *) pr;
\r
9061 if (cp == NULL) return;
\r
9062 switch (cp->kind) {
\r
9064 /* The following doesn't work because the chess program
\r
9065 doesn't "have the same console" as WinBoard. Maybe
\r
9066 we could arrange for this even though neither WinBoard
\r
9067 nor the chess program uses a console for stdio */
\r
9068 /*!!GenerateConsoleCtrlEvent(CTRL_C_EVENT, cp->pid);*/
\r
9073 /* Can't interrupt */
\r
9077 send(cp->sock2, "\002", 1, 0); /* 2 = SIGINT */
\r
9084 OpenTelnet(char *host, char *port, ProcRef *pr)
\r
9086 char cmdLine[MSG_SIZ];
\r
9088 if (port[0] == NULLCHAR) {
\r
9089 snprintf(cmdLine, MSG_SIZ, "%s %s", appData.telnetProgram, host);
\r
9091 snprintf(cmdLine, MSG_SIZ, "%s %s %s", appData.telnetProgram, host, port);
\r
9093 return StartChildProcess(cmdLine, "", pr);
\r
9097 /* Code to open TCP sockets */
\r
9100 OpenTCP(char *host, char *port, ProcRef *pr)
\r
9105 struct sockaddr_in sa, mysa;
\r
9106 struct hostent FAR *hp;
\r
9107 unsigned short uport;
\r
9108 WORD wVersionRequested;
\r
9111 /* Initialize socket DLL */
\r
9112 wVersionRequested = MAKEWORD(1, 1);
\r
9113 err = WSAStartup(wVersionRequested, &wsaData);
\r
9114 if (err != 0) return err;
\r
9117 if ((s = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {
\r
9118 err = WSAGetLastError();
\r
9123 /* Bind local address using (mostly) don't-care values.
\r
9125 memset((char *) &mysa, 0, sizeof(struct sockaddr_in));
\r
9126 mysa.sin_family = AF_INET;
\r
9127 mysa.sin_addr.s_addr = INADDR_ANY;
\r
9128 uport = (unsigned short) 0;
\r
9129 mysa.sin_port = htons(uport);
\r
9130 if (bind(s, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))
\r
9131 == SOCKET_ERROR) {
\r
9132 err = WSAGetLastError();
\r
9137 /* Resolve remote host name */
\r
9138 memset((char *) &sa, 0, sizeof(struct sockaddr_in));
\r
9139 if (!(hp = gethostbyname(host))) {
\r
9140 unsigned int b0, b1, b2, b3;
\r
9142 err = WSAGetLastError();
\r
9144 if (sscanf(host, "%u.%u.%u.%u", &b0, &b1, &b2, &b3) == 4) {
\r
9145 hp = (struct hostent *) calloc(1, sizeof(struct hostent));
\r
9146 hp->h_addrtype = AF_INET;
\r
9148 hp->h_addr_list = (char **) calloc(2, sizeof(char *));
\r
9149 hp->h_addr_list[0] = (char *) malloc(4);
\r
9150 hp->h_addr_list[0][0] = (char) b0;
\r
9151 hp->h_addr_list[0][1] = (char) b1;
\r
9152 hp->h_addr_list[0][2] = (char) b2;
\r
9153 hp->h_addr_list[0][3] = (char) b3;
\r
9159 sa.sin_family = hp->h_addrtype;
\r
9160 uport = (unsigned short) atoi(port);
\r
9161 sa.sin_port = htons(uport);
\r
9162 memcpy((char *) &sa.sin_addr, hp->h_addr, hp->h_length);
\r
9164 /* Make connection */
\r
9165 if (connect(s, (struct sockaddr *) &sa,
\r
9166 sizeof(struct sockaddr_in)) == SOCKET_ERROR) {
\r
9167 err = WSAGetLastError();
\r
9172 /* Prepare return value */
\r
9173 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
\r
9174 cp->kind = CPSock;
\r
9176 *pr = (ProcRef *) cp;
\r
9182 OpenCommPort(char *name, ProcRef *pr)
\r
9187 char fullname[MSG_SIZ];
\r
9189 if (*name != '\\')
\r
9190 snprintf(fullname, MSG_SIZ, "\\\\.\\%s", name);
\r
9192 safeStrCpy(fullname, name, sizeof(fullname)/sizeof(fullname[0]) );
\r
9194 h = CreateFile(name, GENERIC_READ | GENERIC_WRITE,
\r
9195 0, NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL);
\r
9196 if (h == (HANDLE) -1) {
\r
9197 return GetLastError();
\r
9201 if (!SetCommState(h, (LPDCB) &dcb)) return GetLastError();
\r
9203 /* Accumulate characters until a 100ms pause, then parse */
\r
9204 ct.ReadIntervalTimeout = 100;
\r
9205 ct.ReadTotalTimeoutMultiplier = 0;
\r
9206 ct.ReadTotalTimeoutConstant = 0;
\r
9207 ct.WriteTotalTimeoutMultiplier = 0;
\r
9208 ct.WriteTotalTimeoutConstant = 0;
\r
9209 if (!SetCommTimeouts(h, (LPCOMMTIMEOUTS) &ct)) return GetLastError();
\r
9211 /* Prepare return value */
\r
9212 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
\r
9213 cp->kind = CPComm;
\r
9216 *pr = (ProcRef *) cp;
\r
9222 OpenLoopback(ProcRef *pr)
\r
9224 DisplayFatalError(_("Not implemented"), 0, 1);
\r
9230 OpenRcmd(char* host, char* user, char* cmd, ProcRef* pr)
\r
9235 struct sockaddr_in sa, mysa;
\r
9236 struct hostent FAR *hp;
\r
9237 unsigned short uport;
\r
9238 WORD wVersionRequested;
\r
9241 char stderrPortStr[MSG_SIZ];
\r
9243 /* Initialize socket DLL */
\r
9244 wVersionRequested = MAKEWORD(1, 1);
\r
9245 err = WSAStartup(wVersionRequested, &wsaData);
\r
9246 if (err != 0) return err;
\r
9248 /* Resolve remote host name */
\r
9249 memset((char *) &sa, 0, sizeof(struct sockaddr_in));
\r
9250 if (!(hp = gethostbyname(host))) {
\r
9251 unsigned int b0, b1, b2, b3;
\r
9253 err = WSAGetLastError();
\r
9255 if (sscanf(host, "%u.%u.%u.%u", &b0, &b1, &b2, &b3) == 4) {
\r
9256 hp = (struct hostent *) calloc(1, sizeof(struct hostent));
\r
9257 hp->h_addrtype = AF_INET;
\r
9259 hp->h_addr_list = (char **) calloc(2, sizeof(char *));
\r
9260 hp->h_addr_list[0] = (char *) malloc(4);
\r
9261 hp->h_addr_list[0][0] = (char) b0;
\r
9262 hp->h_addr_list[0][1] = (char) b1;
\r
9263 hp->h_addr_list[0][2] = (char) b2;
\r
9264 hp->h_addr_list[0][3] = (char) b3;
\r
9270 sa.sin_family = hp->h_addrtype;
\r
9271 uport = (unsigned short) 514;
\r
9272 sa.sin_port = htons(uport);
\r
9273 memcpy((char *) &sa.sin_addr, hp->h_addr, hp->h_length);
\r
9275 /* Bind local socket to unused "privileged" port address
\r
9277 s = INVALID_SOCKET;
\r
9278 memset((char *) &mysa, 0, sizeof(struct sockaddr_in));
\r
9279 mysa.sin_family = AF_INET;
\r
9280 mysa.sin_addr.s_addr = INADDR_ANY;
\r
9281 for (fromPort = 1023;; fromPort--) {
\r
9282 if (fromPort < 0) {
\r
9284 return WSAEADDRINUSE;
\r
9286 if (s == INVALID_SOCKET) {
\r
9287 if ((s = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {
\r
9288 err = WSAGetLastError();
\r
9293 uport = (unsigned short) fromPort;
\r
9294 mysa.sin_port = htons(uport);
\r
9295 if (bind(s, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))
\r
9296 == SOCKET_ERROR) {
\r
9297 err = WSAGetLastError();
\r
9298 if (err == WSAEADDRINUSE) continue;
\r
9302 if (connect(s, (struct sockaddr *) &sa,
\r
9303 sizeof(struct sockaddr_in)) == SOCKET_ERROR) {
\r
9304 err = WSAGetLastError();
\r
9305 if (err == WSAEADDRINUSE) {
\r
9316 /* Bind stderr local socket to unused "privileged" port address
\r
9318 s2 = INVALID_SOCKET;
\r
9319 memset((char *) &mysa, 0, sizeof(struct sockaddr_in));
\r
9320 mysa.sin_family = AF_INET;
\r
9321 mysa.sin_addr.s_addr = INADDR_ANY;
\r
9322 for (fromPort = 1023;; fromPort--) {
\r
9323 if (fromPort == prevStderrPort) continue; // don't reuse port
\r
9324 if (fromPort < 0) {
\r
9325 (void) closesocket(s);
\r
9327 return WSAEADDRINUSE;
\r
9329 if (s2 == INVALID_SOCKET) {
\r
9330 if ((s2 = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {
\r
9331 err = WSAGetLastError();
\r
9337 uport = (unsigned short) fromPort;
\r
9338 mysa.sin_port = htons(uport);
\r
9339 if (bind(s2, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))
\r
9340 == SOCKET_ERROR) {
\r
9341 err = WSAGetLastError();
\r
9342 if (err == WSAEADDRINUSE) continue;
\r
9343 (void) closesocket(s);
\r
9347 if (listen(s2, 1) == SOCKET_ERROR) {
\r
9348 err = WSAGetLastError();
\r
9349 if (err == WSAEADDRINUSE) {
\r
9351 s2 = INVALID_SOCKET;
\r
9354 (void) closesocket(s);
\r
9355 (void) closesocket(s2);
\r
9361 prevStderrPort = fromPort; // remember port used
\r
9362 snprintf(stderrPortStr, MSG_SIZ, "%d", fromPort);
\r
9364 if (send(s, stderrPortStr, strlen(stderrPortStr) + 1, 0) == SOCKET_ERROR) {
\r
9365 err = WSAGetLastError();
\r
9366 (void) closesocket(s);
\r
9367 (void) closesocket(s2);
\r
9372 if (send(s, UserName(), strlen(UserName()) + 1, 0) == SOCKET_ERROR) {
\r
9373 err = WSAGetLastError();
\r
9374 (void) closesocket(s);
\r
9375 (void) closesocket(s2);
\r
9379 if (*user == NULLCHAR) user = UserName();
\r
9380 if (send(s, user, strlen(user) + 1, 0) == SOCKET_ERROR) {
\r
9381 err = WSAGetLastError();
\r
9382 (void) closesocket(s);
\r
9383 (void) closesocket(s2);
\r
9387 if (send(s, cmd, strlen(cmd) + 1, 0) == SOCKET_ERROR) {
\r
9388 err = WSAGetLastError();
\r
9389 (void) closesocket(s);
\r
9390 (void) closesocket(s2);
\r
9395 if ((s3 = accept(s2, NULL, NULL)) == INVALID_SOCKET) {
\r
9396 err = WSAGetLastError();
\r
9397 (void) closesocket(s);
\r
9398 (void) closesocket(s2);
\r
9402 (void) closesocket(s2); /* Stop listening */
\r
9404 /* Prepare return value */
\r
9405 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
\r
9406 cp->kind = CPRcmd;
\r
9409 *pr = (ProcRef *) cp;
\r
9416 AddInputSource(ProcRef pr, int lineByLine,
\r
9417 InputCallback func, VOIDSTAR closure)
\r
9419 InputSource *is, *is2 = NULL;
\r
9420 ChildProc *cp = (ChildProc *) pr;
\r
9422 is = (InputSource *) calloc(1, sizeof(InputSource));
\r
9423 is->lineByLine = lineByLine;
\r
9425 is->closure = closure;
\r
9426 is->second = NULL;
\r
9427 is->next = is->buf;
\r
9428 if (pr == NoProc) {
\r
9429 is->kind = CPReal;
\r
9430 consoleInputSource = is;
\r
9432 is->kind = cp->kind;
\r
9434 [AS] Try to avoid a race condition if the thread is given control too early:
\r
9435 we create all threads suspended so that the is->hThread variable can be
\r
9436 safely assigned, then let the threads start with ResumeThread.
\r
9438 switch (cp->kind) {
\r
9440 is->hFile = cp->hFrom;
\r
9441 cp->hFrom = NULL; /* now owned by InputThread */
\r
9443 CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) NonOvlInputThread,
\r
9444 (LPVOID) is, CREATE_SUSPENDED, &is->id);
\r
9448 is->hFile = cp->hFrom;
\r
9449 cp->hFrom = NULL; /* now owned by InputThread */
\r
9451 CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) InputThread,
\r
9452 (LPVOID) is, CREATE_SUSPENDED, &is->id);
\r
9456 is->sock = cp->sock;
\r
9458 CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,
\r
9459 (LPVOID) is, CREATE_SUSPENDED, &is->id);
\r
9463 is2 = (InputSource *) calloc(1, sizeof(InputSource));
\r
9465 is->sock = cp->sock;
\r
9467 is2->sock = cp->sock2;
\r
9468 is2->second = is2;
\r
9470 CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,
\r
9471 (LPVOID) is, CREATE_SUSPENDED, &is->id);
\r
9473 CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,
\r
9474 (LPVOID) is2, CREATE_SUSPENDED, &is2->id);
\r
9478 if( is->hThread != NULL ) {
\r
9479 ResumeThread( is->hThread );
\r
9482 if( is2 != NULL && is2->hThread != NULL ) {
\r
9483 ResumeThread( is2->hThread );
\r
9487 return (InputSourceRef) is;
\r
9491 RemoveInputSource(InputSourceRef isr)
\r
9495 is = (InputSource *) isr;
\r
9496 is->hThread = NULL; /* tell thread to stop */
\r
9497 CloseHandle(is->hThread);
\r
9498 if (is->second != NULL) {
\r
9499 is->second->hThread = NULL;
\r
9500 CloseHandle(is->second->hThread);
\r
9504 int no_wrap(char *message, int count)
\r
9506 ConsoleOutput(message, count, FALSE);
\r
9511 OutputToProcess(ProcRef pr, char *message, int count, int *outError)
\r
9514 int outCount = SOCKET_ERROR;
\r
9515 ChildProc *cp = (ChildProc *) pr;
\r
9516 static OVERLAPPED ovl;
\r
9517 static int line = 0;
\r
9521 if (appData.noJoin || !appData.useInternalWrap)
\r
9522 return no_wrap(message, count);
\r
9525 int width = get_term_width();
\r
9526 int len = wrap(NULL, message, count, width, &line);
\r
9527 char *msg = malloc(len);
\r
9531 return no_wrap(message, count);
\r
9534 dbgchk = wrap(msg, message, count, width, &line);
\r
9535 if (dbgchk != len && appData.debugMode)
\r
9536 fprintf(debugFP, "wrap(): dbgchk(%d) != len(%d)\n", dbgchk, len);
\r
9537 ConsoleOutput(msg, len, FALSE);
\r
9544 if (ovl.hEvent == NULL) {
\r
9545 ovl.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
\r
9547 ovl.Internal = ovl.InternalHigh = ovl.Offset = ovl.OffsetHigh = 0;
\r
9549 switch (cp->kind) {
\r
9552 outCount = send(cp->sock, message, count, 0);
\r
9553 if (outCount == SOCKET_ERROR) {
\r
9554 *outError = WSAGetLastError();
\r
9556 *outError = NO_ERROR;
\r
9561 if (WriteFile(((ChildProc *)pr)->hTo, message, count,
\r
9562 &dOutCount, NULL)) {
\r
9563 *outError = NO_ERROR;
\r
9564 outCount = (int) dOutCount;
\r
9566 *outError = GetLastError();
\r
9571 *outError = DoWriteFile(((ChildProc *)pr)->hTo, message, count,
\r
9572 &dOutCount, &ovl);
\r
9573 if (*outError == NO_ERROR) {
\r
9574 outCount = (int) dOutCount;
\r
9582 OutputToProcessDelayed(ProcRef pr, char *message, int count, int *outError,
\r
9585 /* Ignore delay, not implemented for WinBoard */
\r
9586 return OutputToProcess(pr, message, count, outError);
\r
9591 CmailSigHandlerCallBack(InputSourceRef isr, VOIDSTAR closure,
\r
9592 char *buf, int count, int error)
\r
9594 DisplayFatalError(_("Not implemented"), 0, 1);
\r
9597 /* see wgamelist.c for Game List functions */
\r
9598 /* see wedittags.c for Edit Tags functions */
\r
9605 char buf[MSG_SIZ];
\r
9608 if (SearchPath(installDir, appData.icsLogon, NULL, MSG_SIZ, buf, &dummy)) {
\r
9609 f = fopen(buf, "r");
\r
9611 ProcessICSInitScript(f);
\r
9619 StartAnalysisClock()
\r
9621 if (analysisTimerEvent) return;
\r
9622 analysisTimerEvent = SetTimer(hwndMain, (UINT) ANALYSIS_TIMER_ID,
\r
9623 (UINT) 2000, NULL);
\r
9627 SetHighlights(int fromX, int fromY, int toX, int toY)
\r
9629 highlightInfo.sq[0].x = fromX;
\r
9630 highlightInfo.sq[0].y = fromY;
\r
9631 highlightInfo.sq[1].x = toX;
\r
9632 highlightInfo.sq[1].y = toY;
\r
9638 highlightInfo.sq[0].x = highlightInfo.sq[0].y =
\r
9639 highlightInfo.sq[1].x = highlightInfo.sq[1].y = -1;
\r
9643 SetPremoveHighlights(int fromX, int fromY, int toX, int toY)
\r
9645 premoveHighlightInfo.sq[0].x = fromX;
\r
9646 premoveHighlightInfo.sq[0].y = fromY;
\r
9647 premoveHighlightInfo.sq[1].x = toX;
\r
9648 premoveHighlightInfo.sq[1].y = toY;
\r
9652 ClearPremoveHighlights()
\r
9654 premoveHighlightInfo.sq[0].x = premoveHighlightInfo.sq[0].y =
\r
9655 premoveHighlightInfo.sq[1].x = premoveHighlightInfo.sq[1].y = -1;
\r
9659 ShutDownFrontEnd()
\r
9661 if (saveSettingsOnExit) SaveSettings(settingsFileName);
\r
9662 DeleteClipboardTempFiles();
\r
9668 if (IsIconic(hwndMain))
\r
9669 ShowWindow(hwndMain, SW_RESTORE);
\r
9671 SetActiveWindow(hwndMain);
\r
9675 * Prototypes for animation support routines
\r
9677 static void ScreenSquare(int column, int row, POINT * pt);
\r
9678 static void Tween( POINT * start, POINT * mid, POINT * finish, int factor,
\r
9679 POINT frames[], int * nFrames);
\r
9685 AnimateAtomicCapture(Board board, int fromX, int fromY, int toX, int toY)
\r
9686 { // [HGM] atomic: animate blast wave
\r
9689 explodeInfo.fromX = fromX;
\r
9690 explodeInfo.fromY = fromY;
\r
9691 explodeInfo.toX = toX;
\r
9692 explodeInfo.toY = toY;
\r
9693 for(i=1; i<4*kFactor; i++) {
\r
9694 explodeInfo.radius = (i*180)/(4*kFactor-1);
\r
9695 DrawPosition(FALSE, board);
\r
9696 Sleep(appData.animSpeed);
\r
9698 explodeInfo.radius = 0;
\r
9699 DrawPosition(TRUE, board);
\r
9703 AnimateMove(board, fromX, fromY, toX, toY)
\r
9710 ChessSquare piece;
\r
9711 POINT start, finish, mid;
\r
9712 POINT frames[kFactor * 2 + 1];
\r
9715 if (!appData.animate) return;
\r
9716 if (doingSizing) return;
\r
9717 if (fromY < 0 || fromX < 0) return;
\r
9718 piece = board[fromY][fromX];
\r
9719 if (piece >= EmptySquare) return;
\r
9721 ScreenSquare(fromX, fromY, &start);
\r
9722 ScreenSquare(toX, toY, &finish);
\r
9724 /* All moves except knight jumps move in straight line */
\r
9725 if (!(abs(fromX-toX) == 1 && abs(fromY-toY) == 2 || abs(fromX-toX) == 2 && abs(fromY-toY) == 1)) {
\r
9726 mid.x = start.x + (finish.x - start.x) / 2;
\r
9727 mid.y = start.y + (finish.y - start.y) / 2;
\r
9729 /* Knight: make straight movement then diagonal */
\r
9730 if (abs(toY - fromY) < abs(toX - fromX)) {
\r
9731 mid.x = start.x + (finish.x - start.x) / 2;
\r
9735 mid.y = start.y + (finish.y - start.y) / 2;
\r
9739 /* Don't use as many frames for very short moves */
\r
9740 if (abs(toY - fromY) + abs(toX - fromX) <= 2)
\r
9741 Tween(&start, &mid, &finish, kFactor - 1, frames, &nFrames);
\r
9743 Tween(&start, &mid, &finish, kFactor, frames, &nFrames);
\r
9745 animInfo.from.x = fromX;
\r
9746 animInfo.from.y = fromY;
\r
9747 animInfo.to.x = toX;
\r
9748 animInfo.to.y = toY;
\r
9749 animInfo.lastpos = start;
\r
9750 animInfo.piece = piece;
\r
9751 for (n = 0; n < nFrames; n++) {
\r
9752 animInfo.pos = frames[n];
\r
9753 DrawPosition(FALSE, NULL);
\r
9754 animInfo.lastpos = animInfo.pos;
\r
9755 Sleep(appData.animSpeed);
\r
9757 animInfo.pos = finish;
\r
9758 DrawPosition(FALSE, NULL);
\r
9759 animInfo.piece = EmptySquare;
\r
9760 Explode(board, fromX, fromY, toX, toY);
\r
9763 /* Convert board position to corner of screen rect and color */
\r
9766 ScreenSquare(column, row, pt)
\r
9767 int column; int row; POINT * pt;
\r
9770 pt->x = lineGap + ((BOARD_WIDTH-1)-column) * (squareSize + lineGap);
\r
9771 pt->y = lineGap + row * (squareSize + lineGap);
\r
9773 pt->x = lineGap + column * (squareSize + lineGap);
\r
9774 pt->y = lineGap + ((BOARD_HEIGHT-1)-row) * (squareSize + lineGap);
\r
9778 /* Generate a series of frame coords from start->mid->finish.
\r
9779 The movement rate doubles until the half way point is
\r
9780 reached, then halves back down to the final destination,
\r
9781 which gives a nice slow in/out effect. The algorithmn
\r
9782 may seem to generate too many intermediates for short
\r
9783 moves, but remember that the purpose is to attract the
\r
9784 viewers attention to the piece about to be moved and
\r
9785 then to where it ends up. Too few frames would be less
\r
9789 Tween(start, mid, finish, factor, frames, nFrames)
\r
9790 POINT * start; POINT * mid;
\r
9791 POINT * finish; int factor;
\r
9792 POINT frames[]; int * nFrames;
\r
9794 int n, fraction = 1, count = 0;
\r
9796 /* Slow in, stepping 1/16th, then 1/8th, ... */
\r
9797 for (n = 0; n < factor; n++)
\r
9799 for (n = 0; n < factor; n++) {
\r
9800 frames[count].x = start->x + (mid->x - start->x) / fraction;
\r
9801 frames[count].y = start->y + (mid->y - start->y) / fraction;
\r
9803 fraction = fraction / 2;
\r
9807 frames[count] = *mid;
\r
9810 /* Slow out, stepping 1/2, then 1/4, ... */
\r
9812 for (n = 0; n < factor; n++) {
\r
9813 frames[count].x = finish->x - (finish->x - mid->x) / fraction;
\r
9814 frames[count].y = finish->y - (finish->y - mid->y) / fraction;
\r
9816 fraction = fraction * 2;
\r
9822 HistorySet( char movelist[][2*MOVE_LEN], int first, int last, int current )
\r
9824 MoveHistorySet( movelist, first, last, current, pvInfoList );
\r
9826 EvalGraphSet( first, last, current, pvInfoList );
\r
9828 MakeEngineOutputTitle();
\r
9832 SettingsPopUp(ChessProgramState *cps)
\r
9833 { // [HGM] wrapper needed because handles must not be passed through back-end
\r
9834 EngineOptionsPopup(savedHwnd, cps);
\r
9837 int flock(int fid, int code)
\r
9839 HANDLE hFile = (HANDLE) _get_osfhandle(fid);
\r
9843 ov.OffsetHigh = 0;
\r
9845 case 1: LockFileEx(hFile, LOCKFILE_EXCLUSIVE_LOCK, 0, 1024, 0, &ov); break; // LOCK_SH
\r
9846 case 2: LockFileEx(hFile, LOCKFILE_EXCLUSIVE_LOCK, 0, 1024, 0, &ov); break; // LOCK_EX
\r
9847 case 3: UnlockFileEx(hFile, 0, 1024, 0, &ov); break; // LOCK_UN
\r
9848 default: return -1;
\r