2 * WinBoard.c -- Windows NT front end to XBoard
\r
4 * Copyright 1991 by Digital Equipment Corporation, Maynard,
\r
7 * Enhancements Copyright 1992-2001, 2002, 2003, 2004, 2005, 2006,
\r
8 * 2007, 2008, 2009, 2010, 2011 Free Software Foundation, Inc.
\r
10 * Enhancements Copyright 2005 Alessandro Scotti
\r
12 * XBoard borrows its colors and the bitmaps.xchess bitmap set from XChess,
\r
13 * which was written and is copyrighted by Wayne Christopher.
\r
15 * The following terms apply to Digital Equipment Corporation's copyright
\r
16 * interest in XBoard:
\r
17 * ------------------------------------------------------------------------
\r
18 * All Rights Reserved
\r
20 * Permission to use, copy, modify, and distribute this software and its
\r
21 * documentation for any purpose and without fee is hereby granted,
\r
22 * provided that the above copyright notice appear in all copies and that
\r
23 * both that copyright notice and this permission notice appear in
\r
24 * supporting documentation, and that the name of Digital not be
\r
25 * used in advertising or publicity pertaining to distribution of the
\r
26 * software without specific, written prior permission.
\r
28 * DIGITAL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
\r
29 * ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL
\r
30 * DIGITAL BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
\r
31 * ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
\r
32 * WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
\r
33 * ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
\r
35 * ------------------------------------------------------------------------
\r
37 * The following terms apply to the enhanced version of XBoard
\r
38 * distributed by the Free Software Foundation:
\r
39 * ------------------------------------------------------------------------
\r
41 * GNU XBoard is free software: you can redistribute it and/or modify
\r
42 * it under the terms of the GNU General Public License as published by
\r
43 * the Free Software Foundation, either version 3 of the License, or (at
\r
44 * your option) any later version.
\r
46 * GNU XBoard is distributed in the hope that it will be useful, but
\r
47 * WITHOUT ANY WARRANTY; without even the implied warranty of
\r
48 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
\r
49 * General Public License for more details.
\r
51 * You should have received a copy of the GNU General Public License
\r
52 * along with this program. If not, see http://www.gnu.org/licenses/. *
\r
54 *------------------------------------------------------------------------
\r
55 ** See the file ChangeLog for a revision history. */
\r
59 #include <windows.h>
\r
60 #include <winuser.h>
\r
61 #include <winsock.h>
\r
62 #include <commctrl.h>
\r
68 #include <sys/stat.h>
\r
71 #include <commdlg.h>
\r
73 #include <richedit.h>
\r
74 #include <mmsystem.h>
\r
84 #include "frontend.h"
\r
85 #include "backend.h"
\r
86 #include "winboard.h"
\r
88 #include "wclipbrd.h"
\r
89 #include "woptions.h"
\r
90 #include "wsockerr.h"
\r
91 #include "defaults.h"
\r
95 //void InitEngineUCI( const char * iniDir, ChessProgramState * cps );
\r
98 void mysrandom(unsigned int seed);
\r
100 extern int whiteFlag, blackFlag;
\r
101 Boolean flipClock = FALSE;
\r
102 extern HANDLE chatHandle[];
\r
103 extern int ics_type;
\r
105 void DisplayHoldingsCount(HDC hdc, int x, int y, int align, int copyNumber);
\r
106 VOID NewVariantPopup(HWND hwnd);
\r
107 int FinishMove P((ChessMove moveType, int fromX, int fromY, int toX, int toY,
\r
108 /*char*/int promoChar));
\r
109 void DisplayMove P((int moveNumber));
\r
110 Boolean ParseFEN P((Board board, int *blackPlaysFirst, char *fen));
\r
111 void ChatPopUp P((char *s));
\r
113 ChessSquare piece;
\r
114 POINT pos; /* window coordinates of current pos */
\r
115 POINT lastpos; /* window coordinates of last pos - used for clipping */
\r
116 POINT from; /* board coordinates of the piece's orig pos */
\r
117 POINT to; /* board coordinates of the piece's new pos */
\r
120 static AnimInfo animInfo = { EmptySquare, {-1,-1}, {-1,-1}, {-1,-1} };
\r
123 POINT start; /* window coordinates of start pos */
\r
124 POINT pos; /* window coordinates of current pos */
\r
125 POINT lastpos; /* window coordinates of last pos - used for clipping */
\r
126 POINT from; /* board coordinates of the piece's orig pos */
\r
130 static DragInfo dragInfo = { {-1,-1}, {-1,-1}, {-1,-1}, {-1,-1}, EmptySquare };
\r
133 POINT sq[2]; /* board coordinates of from, to squares */
\r
136 static HighlightInfo highlightInfo = { {{-1, -1}, {-1, -1}} };
\r
137 static HighlightInfo premoveHighlightInfo = { {{-1, -1}, {-1, -1}} };
\r
138 static HighlightInfo partnerHighlightInfo = { {{-1, -1}, {-1, -1}} };
\r
139 static HighlightInfo oldPartnerHighlight = { {{-1, -1}, {-1, -1}} };
\r
141 typedef struct { // [HGM] atomic
\r
142 int fromX, fromY, toX, toY, radius;
\r
145 static ExplodeInfo explodeInfo;
\r
147 /* Window class names */
\r
148 char szAppName[] = "WinBoard";
\r
149 char szConsoleName[] = "WBConsole";
\r
151 /* Title bar text */
\r
152 char szTitle[] = "WinBoard";
\r
153 char szConsoleTitle[] = "I C S Interaction";
\r
156 char *settingsFileName;
\r
157 Boolean saveSettingsOnExit;
\r
158 char installDir[MSG_SIZ];
\r
159 int errorExitStatus;
\r
161 BoardSize boardSize;
\r
162 Boolean chessProgram;
\r
163 //static int boardX, boardY;
\r
164 int minX, minY; // [HGM] placement: volatile limits on upper-left corner
\r
165 int squareSize, lineGap, minorSize;
\r
166 static int winW, winH;
\r
167 static RECT messageRect, whiteRect, blackRect, leftLogoRect, rightLogoRect; // [HGM] logo
\r
168 static int logoHeight = 0;
\r
169 static char messageText[MESSAGE_TEXT_MAX];
\r
170 static int clockTimerEvent = 0;
\r
171 static int loadGameTimerEvent = 0;
\r
172 static int analysisTimerEvent = 0;
\r
173 static DelayedEventCallback delayedTimerCallback;
\r
174 static int delayedTimerEvent = 0;
\r
175 static int buttonCount = 2;
\r
176 char *icsTextMenuString;
\r
178 char *firstChessProgramNames;
\r
179 char *secondChessProgramNames;
\r
181 #define PALETTESIZE 256
\r
183 HINSTANCE hInst; /* current instance */
\r
184 Boolean alwaysOnTop = FALSE;
\r
186 COLORREF lightSquareColor, darkSquareColor, whitePieceColor,
\r
187 blackPieceColor, highlightSquareColor, premoveHighlightColor;
\r
189 ColorClass currentColorClass;
\r
191 static HWND savedHwnd;
\r
192 HWND hCommPort = NULL; /* currently open comm port */
\r
193 static HWND hwndPause; /* pause button */
\r
194 static HBITMAP pieceBitmap[3][(int) BlackPawn]; /* [HGM] nr of bitmaps referred to bP in stead of wK */
\r
195 static HBRUSH lightSquareBrush, darkSquareBrush,
\r
196 blackSquareBrush, /* [HGM] for band between board and holdings */
\r
197 explodeBrush, /* [HGM] atomic */
\r
198 markerBrush, /* [HGM] markers */
\r
199 whitePieceBrush, blackPieceBrush, iconBkgndBrush /*, outlineBrush*/;
\r
200 static POINT gridEndpoints[(BOARD_RANKS + BOARD_FILES + 2) * 2];
\r
201 static DWORD gridVertexCounts[BOARD_RANKS + BOARD_FILES + 2];
\r
202 static HPEN gridPen = NULL;
\r
203 static HPEN highlightPen = NULL;
\r
204 static HPEN premovePen = NULL;
\r
205 static NPLOGPALETTE pLogPal;
\r
206 static BOOL paletteChanged = FALSE;
\r
207 static HICON iconWhite, iconBlack, iconCurrent;
\r
208 static int doingSizing = FALSE;
\r
209 static int lastSizing = 0;
\r
210 static int prevStderrPort;
\r
211 static HBITMAP userLogo;
\r
213 static HBITMAP liteBackTexture = NULL;
\r
214 static HBITMAP darkBackTexture = NULL;
\r
215 static int liteBackTextureMode = BACK_TEXTURE_MODE_PLAIN;
\r
216 static int darkBackTextureMode = BACK_TEXTURE_MODE_PLAIN;
\r
217 static int backTextureSquareSize = 0;
\r
218 static struct { int x; int y; int mode; } backTextureSquareInfo[BOARD_RANKS][BOARD_FILES];
\r
220 #if __GNUC__ && !defined(_winmajor)
\r
221 #define oldDialog 0 /* cygwin doesn't define _winmajor; mingw does */
\r
223 #if defined(_winmajor)
\r
224 #define oldDialog (_winmajor < 4)
\r
226 #define oldDialog 0
\r
230 #define INTERNATIONAL
\r
232 #ifdef INTERNATIONAL
\r
233 # define _(s) T_(s)
\r
239 # define Translate(x, y)
\r
240 # define LoadLanguageFile(s)
\r
243 #ifdef INTERNATIONAL
\r
245 Boolean barbaric; // flag indicating if translation is needed
\r
247 // list of item numbers used in each dialog (used to alter language at run time)
\r
249 #define ABOUTBOX -1 /* not sure why these are needed */
\r
250 #define ABOUTBOX2 -1
\r
252 int dialogItems[][40] = {
\r
253 { ABOUTBOX, IDOK, OPT_MESS, 400 },
\r
254 { DLG_TimeControl, IDC_Babble, OPT_TCUseMoves, OPT_TCUseInc, OPT_TCUseFixed,
\r
255 OPT_TCtext1, OPT_TCtext2, OPT_TCitext1, OPT_TCitext2, OPT_TCftext, GPB_Factors, IDC_Factor1, IDC_Factor2, IDOK, IDCANCEL },
\r
256 { DLG_LoadOptions, OPT_Autostep, OPT_AStext1, IDOK, IDCANCEL },
\r
257 { DLG_SaveOptions, OPT_Autosave, OPT_AVPrompt, OPT_AVToFile, OPT_AVBrowse,
\r
258 801, OPT_PGN, OPT_Old, OPT_OutOfBookInfo, IDOK, IDCANCEL },
\r
259 { 1536, 1090, IDC_Directories, 1089, 1091, IDOK, IDCANCEL, 1038, IDC_IndexNr, 1037 },
\r
260 { DLG_CommPort, IDOK, IDCANCEL, IDC_Port, IDC_Rate, IDC_Bits, IDC_Parity,
\r
261 IDC_Stop, IDC_Flow, OPT_SerialHelp },
\r
262 { DLG_EditComment, IDOK, OPT_CancelComment, OPT_ClearComment, OPT_EditComment },
\r
263 { DLG_PromotionKing, PB_Chancellor, PB_Archbishop, PB_Queen, PB_Rook,
\r
264 PB_Bishop, PB_Knight, PB_King, IDCANCEL, IDC_Yes, IDC_No, IDC_Centaur },
\r
265 { ABOUTBOX2, IDC_ChessBoard },
\r
266 { DLG_GameList, OPT_GameListLoad, OPT_GameListPrev, OPT_GameListNext,
\r
267 OPT_GameListClose, IDC_GameListDoFilter },
\r
268 { DLG_EditTags, IDOK, OPT_TagsCancel, OPT_EditTags },
\r
269 { DLG_Error, IDOK },
\r
270 { DLG_Colorize, IDOK, IDCANCEL, OPT_ChooseColor, OPT_Bold, OPT_Italic,
\r
271 OPT_Underline, OPT_Strikeout, OPT_Sample },
\r
272 { DLG_Question, IDOK, IDCANCEL, OPT_QuestionText },
\r
273 { DLG_Startup, IDC_Welcome, OPT_ChessEngine, OPT_ChessServer, OPT_View,
\r
274 IDC_SPECIFY_ENG_STATIC, IDC_SPECIFY_SERVER_STATIC, OPT_AnyAdditional,
\r
275 IDOK, IDCANCEL, IDM_HELPCONTENTS },
\r
276 { DLG_IndexNumber, IDC_Index },
\r
277 { DLG_TypeInMove, IDOK, IDCANCEL },
\r
278 { DLG_TypeInName, IDOK, IDCANCEL },
\r
279 { DLG_Sound, IDC_Event, OPT_NoSound, OPT_DefaultBeep, OPT_BuiltInSound,
\r
280 OPT_WavFile, OPT_BrowseSound, OPT_DefaultSounds, IDOK, IDCANCEL, OPT_PlaySound },
\r
281 { DLG_GeneralOptions, IDOK, IDCANCEL, OPT_AlwaysOnTop, OPT_HighlightLastMove,
\r
282 OPT_AlwaysQueen, OPT_PeriodicUpdates, OPT_AnimateDragging, OPT_PonderNextMove,
\r
283 OPT_AnimateMoving, OPT_PopupExitMessage, OPT_AutoFlag, OPT_PopupMoveErrors,
\r
284 OPT_AutoFlipView, OPT_ShowButtonBar, OPT_AutoRaiseBoard, OPT_ShowCoordinates,
\r
285 OPT_Blindfold, OPT_ShowThinking, OPT_HighlightDragging, OPT_TestLegality,
\r
286 OPT_SaveExtPGN, OPT_HideThinkFromHuman, OPT_ExtraInfoInMoveHistory,
\r
287 OPT_HighlightMoveArrow, OPT_AutoLogo ,OPT_SmartMove },
\r
288 { DLG_IcsOptions, IDOK, IDCANCEL, OPT_AutoComment, OPT_AutoKibitz, OPT_AutoObserve,
\r
289 OPT_GetMoveList, OPT_LocalLineEditing, OPT_QuietPlay, OPT_SeekGraph, OPT_AutoRefresh,
\r
290 OPT_BgObserve, OPT_DualBoard, OPT_Premove, OPT_PremoveWhite, OPT_PremoveBlack,
\r
291 OPT_SmartMove, OPT_IcsAlarm, IDC_Sec, OPT_ChooseShoutColor, OPT_ChooseSShoutColor,
\r
292 OPT_ChooseChannel1Color, OPT_ChooseChannelColor, OPT_ChooseKibitzColor,
\r
293 OPT_ChooseTellColor, OPT_ChooseChallengeColor, OPT_ChooseRequestColor,
\r
294 OPT_ChooseSeekColor, OPT_ChooseNormalColor, OPT_ChooseBackgroundColor,
\r
295 OPT_DefaultColors, OPT_DontColorize, IDC_Boxes, GPB_Colors, GPB_Premove,
\r
296 GPB_General, GPB_Alarm },
\r
297 { DLG_BoardOptions, IDOK, IDCANCEL, OPT_SizeTiny, OPT_SizeTeeny, OPT_SizeDinky,
\r
298 OPT_SizePetite, OPT_SizeSlim, OPT_SizeSmall, OPT_SizeMediocre, OPT_SizeMiddling,
\r
299 OPT_SizeAverage, OPT_SizeModerate, OPT_SizeMedium, OPT_SizeBulky, OPT_SizeLarge,
\r
300 OPT_SizeBig, OPT_SizeHuge, OPT_SizeGiant, OPT_SizeColossal, OPT_SizeTitanic,
\r
301 OPT_ChooseLightSquareColor, OPT_ChooseDarkSquareColor, OPT_ChooseWhitePieceColor,
\r
302 OPT_ChooseBlackPieceColor, OPT_ChooseHighlightSquareColor, OPT_ChoosePremoveHighlightColor,
\r
303 OPT_Monochrome, OPT_AllWhite, OPT_UpsideDown, OPT_DefaultBoardColors, GPB_Colors,
\r
304 IDC_Light, IDC_Dark, IDC_White, IDC_Black, IDC_High, IDC_PreHigh, GPB_Size },
\r
305 { DLG_NewVariant, IDOK, IDCANCEL, OPT_VariantNormal, OPT_VariantFRC, OPT_VariantWildcastle,
\r
306 OPT_VariantNocastle, OPT_VariantLosers, OPT_VariantGiveaway, OPT_VariantSuicide,
\r
307 OPT_Variant3Check, OPT_VariantTwoKings, OPT_VariantAtomic, OPT_VariantCrazyhouse,
\r
308 OPT_VariantBughouse, OPT_VariantTwilight, OPT_VariantShogi, OPT_VariantSuper,
\r
309 OPT_VariantKnightmate, OPT_VariantBerolina, OPT_VariantCylinder, OPT_VariantFairy,
\r
310 OPT_VariantMakruk, OPT_VariantGothic, OPT_VariantCapablanca, OPT_VariantJanus,
\r
311 OPT_VariantCRC, OPT_VariantFalcon, OPT_VariantCourier, OPT_VariantGreat, OPT_VariantSChess,
\r
312 OPT_VariantShatranj, OPT_VariantXiangqi, GPB_Variant, GPB_Board, IDC_Height,
\r
313 IDC_Width, IDC_Hand, IDC_Pieces, IDC_Def },
\r
314 { DLG_Fonts, IDOK, IDCANCEL, OPT_ChooseClockFont, OPT_ChooseMessageFont,
\r
315 OPT_ChooseCoordFont, OPT_ChooseTagFont, OPT_ChooseCommentsFont, OPT_ChooseConsoleFont, OPT_ChooseMoveHistoryFont, OPT_DefaultFonts,
\r
316 OPT_ClockFont, OPT_MessageFont, OPT_CoordFont, OPT_EditTagsFont,
\r
317 OPT_CommentsFont, OPT_MessageFont5, GPB_Current, GPB_All, OPT_MessageFont6 },
\r
318 { DLG_NewGameFRC, IDC_NFG_Label, IDC_NFG_Random, IDOK, IDCANCEL },
\r
319 { DLG_GameListOptions, IDC_GLT, IDC_GLT_Up, IDC_GLT_Down, IDC_GLT_Restore,
\r
320 IDC_GLT_Default, IDOK, IDCANCEL, IDC_GLT_RestoreTo },
\r
321 { DLG_MoveHistory },
\r
322 { DLG_EvalGraph },
\r
323 { DLG_EngineOutput, IDC_EngineLabel1, IDC_Engine1_NPS, IDC_EngineLabel2, IDC_Engine2_NPS },
\r
324 { DLG_Chat, IDC_Partner, IDC_Clear, IDC_Send, },
\r
325 { DLG_EnginePlayOptions, IDC_EpPonder, IDC_EpShowThinking, IDC_EpHideThinkingHuman,
\r
326 IDC_EpPeriodicUpdates, GPB_Adjudications, IDC_Draw, IDC_Moves, IDC_Threshold,
\r
327 IDC_Centi, IDC_TestClaims, IDC_DetectMates, IDC_MaterialDraws, IDC_TrivialDraws,
\r
328 GPB_Apply, IDC_Rule, IDC_Repeats, IDC_ScoreAbs1, IDC_ScoreAbs2, IDOK, IDCANCEL },
\r
329 { DLG_OptionsUCI, IDC_PolyDir, IDC_BrowseForPolyglotDir, IDC_Hash, IDC_Path,
\r
330 IDC_BrowseForEGTB, IDC_Cache, IDC_UseBook, IDC_BrowseForBook, IDC_CPU, IDC_OwnBook1,
\r
331 IDC_OwnBook2, IDC_Depth, IDC_Variation, IDC_DefGames, IDOK, IDCANCEL },
\r
335 static char languageBuf[50000], *foreign[1000], *english[1000], *languageFile[MSG_SIZ];
\r
336 static int lastChecked;
\r
337 static char oldLanguage[MSG_SIZ], *menuText[10][30];
\r
338 extern int tinyLayout;
\r
339 extern char * menuBarText[][10];
\r
342 LoadLanguageFile(char *name)
\r
343 { //load the file with translations, and make a list of the strings to be translated, and their translations
\r
345 int i=0, j=0, n=0, k;
\r
348 if(!name || name[0] == NULLCHAR) return;
\r
349 snprintf(buf, MSG_SIZ, "%s%s", name, strchr(name, '.') ? "" : ".lng"); // auto-append lng extension
\r
350 appData.language = oldLanguage;
\r
351 if(!strcmp(buf, oldLanguage)) { barbaric = 1; return; } // this language already loaded; just switch on
\r
352 if((f = fopen(buf, "r")) == NULL) return;
\r
353 while((k = fgetc(f)) != EOF) {
\r
354 if(i >= sizeof(languageBuf)) { DisplayError("Language file too big", 0); return; }
\r
355 languageBuf[i] = k;
\r
357 if(languageBuf[n] == '"' && languageBuf[i-1] == '"') {
\r
359 if(p = strstr(languageBuf + n + 1, "\" === \"")) {
\r
360 if(p > languageBuf+n+2 && p+8 < languageBuf+i) {
\r
361 if(j >= sizeof(english)) { DisplayError("Too many translated strings", 0); return; }
\r
362 english[j] = languageBuf + n + 1; *p = 0;
\r
363 foreign[j++] = p + 7; languageBuf[i-1] = 0;
\r
364 //if(appData.debugMode) fprintf(debugFP, "translation: replace '%s' by '%s'\n", english[j-1], foreign[j-1]);
\r
369 } else if(i > 0 && languageBuf[i-1] == '\\') {
\r
371 case 'n': k = '\n'; break;
\r
372 case 'r': k = '\r'; break;
\r
373 case 't': k = '\t'; break;
\r
375 languageBuf[--i] = k;
\r
380 barbaric = (j != 0);
\r
381 safeStrCpy(oldLanguage, buf, sizeof(oldLanguage)/sizeof(oldLanguage[0]) );
\r
386 { // return the translation of the given string
\r
387 // efficiency can be improved a lot...
\r
389 //if(appData.debugMode) fprintf(debugFP, "T_(%s)\n", s);
\r
390 if(!barbaric) return s;
\r
391 if(!s) return ""; // sanity
\r
392 while(english[i]) {
\r
393 if(!strcmp(s, english[i])) return foreign[i];
\r
400 Translate(HWND hDlg, int dialogID)
\r
401 { // translate all text items in the given dialog
\r
403 char buf[MSG_SIZ], *s;
\r
404 if(!barbaric) return;
\r
405 while(dialogItems[i][0] && dialogItems[i][0] != dialogID) i++; // find the dialog description
\r
406 if(dialogItems[i][0] != dialogID) return; // unknown dialog, should not happen
\r
407 GetWindowText( hDlg, buf, MSG_SIZ );
\r
409 if(strcmp(buf, s)) SetWindowText(hDlg, s); // replace by translated string (if different)
\r
410 for(j=1; k=dialogItems[i][j]; j++) { // translate all listed dialog items
\r
411 GetDlgItemText(hDlg, k, buf, MSG_SIZ);
\r
412 if(strlen(buf) == 0) continue;
\r
414 if(strcmp(buf, s)) SetDlgItemText(hDlg, k, s); // replace by translated string (if different)
\r
419 TranslateOneMenu(int i, HMENU subMenu)
\r
422 static MENUITEMINFO info;
\r
424 info.cbSize = sizeof(MENUITEMINFO);
\r
425 info.fMask = MIIM_STATE | MIIM_TYPE;
\r
426 for(j=GetMenuItemCount(subMenu)-1; j>=0; j--){
\r
428 info.dwTypeData = buf;
\r
429 info.cch = sizeof(buf);
\r
430 GetMenuItemInfo(subMenu, j, TRUE, &info);
\r
432 if(menuText[i][j]) safeStrCpy(buf, menuText[i][j], sizeof(buf)/sizeof(buf[0]) );
\r
433 else menuText[i][j] = strdup(buf); // remember original on first change
\r
435 if(buf[0] == NULLCHAR) continue;
\r
436 info.dwTypeData = T_(buf);
\r
437 info.cch = strlen(buf)+1;
\r
438 SetMenuItemInfo(subMenu, j, TRUE, &info);
\r
444 TranslateMenus(int addLanguage)
\r
447 WIN32_FIND_DATA fileData;
\r
449 #define IDM_English 1970
\r
451 HMENU mainMenu = GetMenu(hwndMain);
\r
452 for (i=GetMenuItemCount(mainMenu)-1; i>=0; i--) {
\r
453 HMENU subMenu = GetSubMenu(mainMenu, i);
\r
454 ModifyMenu(mainMenu, i, MF_STRING|MF_BYPOSITION|MF_POPUP|EnableMenuItem(mainMenu, i, MF_BYPOSITION),
\r
455 (UINT) subMenu, T_(menuBarText[tinyLayout][i]));
\r
456 TranslateOneMenu(i, subMenu);
\r
458 DrawMenuBar(hwndMain);
\r
461 if(!addLanguage) return;
\r
462 if((hFind = FindFirstFile("*.LNG", &fileData)) != INVALID_HANDLE_VALUE) {
\r
463 HMENU mainMenu = GetMenu(hwndMain);
\r
464 HMENU subMenu = GetSubMenu(mainMenu, GetMenuItemCount(mainMenu)-1);
\r
465 AppendMenu(subMenu, MF_SEPARATOR, (UINT_PTR) 0, NULL);
\r
466 AppendMenu(subMenu, MF_ENABLED|MF_STRING|(barbaric?MF_UNCHECKED:MF_CHECKED), (UINT_PTR) IDM_English, (LPCTSTR) "English");
\r
467 i = 0; lastChecked = IDM_English;
\r
469 char *p, *q = fileData.cFileName;
\r
470 int checkFlag = MF_UNCHECKED;
\r
471 languageFile[i] = strdup(q);
\r
472 if(barbaric && !strcmp(oldLanguage, q)) {
\r
473 checkFlag = MF_CHECKED;
\r
474 lastChecked = IDM_English + i + 1;
\r
475 CheckMenuItem(mainMenu, IDM_English, MF_BYCOMMAND|MF_UNCHECKED);
\r
477 *q = ToUpper(*q); while(*++q) *q = ToLower(*q);
\r
478 p = strstr(fileData.cFileName, ".lng");
\r
480 AppendMenu(subMenu, MF_ENABLED|MF_STRING|checkFlag, (UINT_PTR) IDM_English + ++i, (LPCTSTR) fileData.cFileName);
\r
481 } while(FindNextFile(hFind, &fileData));
\r
494 int cliWidth, cliHeight;
\r
497 SizeInfo sizeInfo[] =
\r
499 { "tiny", 21, 0, 1, 1, 0, 0 },
\r
500 { "teeny", 25, 1, 1, 1, 0, 0 },
\r
501 { "dinky", 29, 1, 1, 1, 0, 0 },
\r
502 { "petite", 33, 1, 1, 1, 0, 0 },
\r
503 { "slim", 37, 2, 1, 0, 0, 0 },
\r
504 { "small", 40, 2, 1, 0, 0, 0 },
\r
505 { "mediocre", 45, 2, 1, 0, 0, 0 },
\r
506 { "middling", 49, 2, 0, 0, 0, 0 },
\r
507 { "average", 54, 2, 0, 0, 0, 0 },
\r
508 { "moderate", 58, 3, 0, 0, 0, 0 },
\r
509 { "medium", 64, 3, 0, 0, 0, 0 },
\r
510 { "bulky", 72, 3, 0, 0, 0, 0 },
\r
511 { "large", 80, 3, 0, 0, 0, 0 },
\r
512 { "big", 87, 3, 0, 0, 0, 0 },
\r
513 { "huge", 95, 3, 0, 0, 0, 0 },
\r
514 { "giant", 108, 3, 0, 0, 0, 0 },
\r
515 { "colossal", 116, 4, 0, 0, 0, 0 },
\r
516 { "titanic", 129, 4, 0, 0, 0, 0 },
\r
517 { NULL, 0, 0, 0, 0, 0, 0 }
\r
520 #define MF(x) {x, {{0,}, 0. }, {0, }, 0}
\r
521 MyFont fontRec[NUM_SIZES][NUM_FONTS] =
\r
523 { MF(CLOCK_FONT_TINY), MF(MESSAGE_FONT_TINY), MF(COORD_FONT_TINY), MF(CONSOLE_FONT_TINY), MF(COMMENT_FONT_TINY), MF(EDITTAGS_FONT_TINY), MF(MOVEHISTORY_FONT_ALL) },
\r
524 { MF(CLOCK_FONT_TEENY), MF(MESSAGE_FONT_TEENY), MF(COORD_FONT_TEENY), MF(CONSOLE_FONT_TEENY), MF(COMMENT_FONT_TEENY), MF(EDITTAGS_FONT_TEENY), MF(MOVEHISTORY_FONT_ALL) },
\r
525 { MF(CLOCK_FONT_DINKY), MF(MESSAGE_FONT_DINKY), MF(COORD_FONT_DINKY), MF(CONSOLE_FONT_DINKY), MF(COMMENT_FONT_DINKY), MF(EDITTAGS_FONT_DINKY), MF(MOVEHISTORY_FONT_ALL) },
\r
526 { MF(CLOCK_FONT_PETITE), MF(MESSAGE_FONT_PETITE), MF(COORD_FONT_PETITE), MF(CONSOLE_FONT_PETITE), MF(COMMENT_FONT_PETITE), MF(EDITTAGS_FONT_PETITE), MF(MOVEHISTORY_FONT_ALL) },
\r
527 { MF(CLOCK_FONT_SLIM), MF(MESSAGE_FONT_SLIM), MF(COORD_FONT_SLIM), MF(CONSOLE_FONT_SLIM), MF(COMMENT_FONT_SLIM), MF(EDITTAGS_FONT_SLIM), MF(MOVEHISTORY_FONT_ALL) },
\r
528 { MF(CLOCK_FONT_SMALL), MF(MESSAGE_FONT_SMALL), MF(COORD_FONT_SMALL), MF(CONSOLE_FONT_SMALL), MF(COMMENT_FONT_SMALL), MF(EDITTAGS_FONT_SMALL), MF(MOVEHISTORY_FONT_ALL) },
\r
529 { MF(CLOCK_FONT_MEDIOCRE), MF(MESSAGE_FONT_MEDIOCRE), MF(COORD_FONT_MEDIOCRE), MF(CONSOLE_FONT_MEDIOCRE), MF(COMMENT_FONT_MEDIOCRE), MF(EDITTAGS_FONT_MEDIOCRE), MF(MOVEHISTORY_FONT_ALL) },
\r
530 { MF(CLOCK_FONT_MIDDLING), MF(MESSAGE_FONT_MIDDLING), MF(COORD_FONT_MIDDLING), MF(CONSOLE_FONT_MIDDLING), MF(COMMENT_FONT_MIDDLING), MF(EDITTAGS_FONT_MIDDLING), MF(MOVEHISTORY_FONT_ALL) },
\r
531 { MF(CLOCK_FONT_AVERAGE), MF(MESSAGE_FONT_AVERAGE), MF(COORD_FONT_AVERAGE), MF(CONSOLE_FONT_AVERAGE), MF(COMMENT_FONT_AVERAGE), MF(EDITTAGS_FONT_AVERAGE), MF(MOVEHISTORY_FONT_ALL) },
\r
532 { MF(CLOCK_FONT_MODERATE), MF(MESSAGE_FONT_MODERATE), MF(COORD_FONT_MODERATE), MF(CONSOLE_FONT_MODERATE), MF(COMMENT_FONT_MODERATE), MF(EDITTAGS_FONT_MODERATE), MF(MOVEHISTORY_FONT_ALL) },
\r
533 { MF(CLOCK_FONT_MEDIUM), MF(MESSAGE_FONT_MEDIUM), MF(COORD_FONT_MEDIUM), MF(CONSOLE_FONT_MEDIUM), MF(COMMENT_FONT_MEDIUM), MF(EDITTAGS_FONT_MEDIUM), MF(MOVEHISTORY_FONT_ALL) },
\r
534 { MF(CLOCK_FONT_BULKY), MF(MESSAGE_FONT_BULKY), MF(COORD_FONT_BULKY), MF(CONSOLE_FONT_BULKY), MF(COMMENT_FONT_BULKY), MF(EDITTAGS_FONT_BULKY), MF(MOVEHISTORY_FONT_ALL) },
\r
535 { MF(CLOCK_FONT_LARGE), MF(MESSAGE_FONT_LARGE), MF(COORD_FONT_LARGE), MF(CONSOLE_FONT_LARGE), MF(COMMENT_FONT_LARGE), MF(EDITTAGS_FONT_LARGE), MF(MOVEHISTORY_FONT_ALL) },
\r
536 { MF(CLOCK_FONT_BIG), MF(MESSAGE_FONT_BIG), MF(COORD_FONT_BIG), MF(CONSOLE_FONT_BIG), MF(COMMENT_FONT_BIG), MF(EDITTAGS_FONT_BIG), MF(MOVEHISTORY_FONT_ALL) },
\r
537 { MF(CLOCK_FONT_HUGE), MF(MESSAGE_FONT_HUGE), MF(COORD_FONT_HUGE), MF(CONSOLE_FONT_HUGE), MF(COMMENT_FONT_HUGE), MF(EDITTAGS_FONT_HUGE), MF(MOVEHISTORY_FONT_ALL) },
\r
538 { MF(CLOCK_FONT_GIANT), MF(MESSAGE_FONT_GIANT), MF(COORD_FONT_GIANT), MF(CONSOLE_FONT_GIANT), MF(COMMENT_FONT_GIANT), MF(EDITTAGS_FONT_GIANT), MF(MOVEHISTORY_FONT_ALL) },
\r
539 { MF(CLOCK_FONT_COLOSSAL), MF(MESSAGE_FONT_COLOSSAL), MF(COORD_FONT_COLOSSAL), MF(CONSOLE_FONT_COLOSSAL), MF(COMMENT_FONT_COLOSSAL), MF(EDITTAGS_FONT_COLOSSAL), MF(MOVEHISTORY_FONT_ALL) },
\r
540 { MF(CLOCK_FONT_TITANIC), MF(MESSAGE_FONT_TITANIC), MF(COORD_FONT_TITANIC), MF(CONSOLE_FONT_TITANIC), MF(COMMENT_FONT_TITANIC), MF(EDITTAGS_FONT_TITANIC), MF(MOVEHISTORY_FONT_ALL) },
\r
543 MyFont *font[NUM_SIZES][NUM_FONTS];
\r
552 #define BUTTON_WIDTH (tinyLayout ? 16 : 32)
\r
553 #define N_BUTTONS 5
\r
555 MyButtonDesc buttonDesc[N_BUTTONS] =
\r
557 {"<<", IDM_ToStart, NULL, NULL},
\r
558 {"<", IDM_Backward, NULL, NULL},
\r
559 {"P", IDM_Pause, NULL, NULL},
\r
560 {">", IDM_Forward, NULL, NULL},
\r
561 {">>", IDM_ToEnd, NULL, NULL},
\r
564 int tinyLayout = 0, smallLayout = 0;
\r
565 #define MENU_BAR_ITEMS 9
\r
566 char *menuBarText[2][MENU_BAR_ITEMS+1] = {
\r
567 { N_("&File"), N_("&Edit"), N_("&View"), N_("&Mode"), N_("&Action"), N_("E&ngine"), N_("&Options"), N_("&Help"), NULL },
\r
568 { N_("&F"), N_("&E"), N_("&V"), N_("&M"), N_("&A"), N_("&N"), N_("&O"), N_("&H"), NULL },
\r
572 MySound sounds[(int)NSoundClasses];
\r
573 MyTextAttribs textAttribs[(int)NColorClasses];
\r
575 MyColorizeAttribs colorizeAttribs[] = {
\r
576 { (COLORREF)0, 0, N_("Shout Text") },
\r
577 { (COLORREF)0, 0, N_("SShout/CShout") },
\r
578 { (COLORREF)0, 0, N_("Channel 1 Text") },
\r
579 { (COLORREF)0, 0, N_("Channel Text") },
\r
580 { (COLORREF)0, 0, N_("Kibitz Text") },
\r
581 { (COLORREF)0, 0, N_("Tell Text") },
\r
582 { (COLORREF)0, 0, N_("Challenge Text") },
\r
583 { (COLORREF)0, 0, N_("Request Text") },
\r
584 { (COLORREF)0, 0, N_("Seek Text") },
\r
585 { (COLORREF)0, 0, N_("Normal Text") },
\r
586 { (COLORREF)0, 0, N_("None") }
\r
591 static char *commentTitle;
\r
592 static char *commentText;
\r
593 static int commentIndex;
\r
594 static Boolean editComment = FALSE;
\r
597 char errorTitle[MSG_SIZ];
\r
598 char errorMessage[2*MSG_SIZ];
\r
599 HWND errorDialog = NULL;
\r
600 BOOLEAN moveErrorMessageUp = FALSE;
\r
601 BOOLEAN consoleEcho = TRUE;
\r
602 CHARFORMAT consoleCF;
\r
603 COLORREF consoleBackgroundColor;
\r
605 char *programVersion;
\r
611 typedef int CPKind;
\r
620 SOCKET sock2; /* stderr socket for OpenRcmd */
\r
623 #define INPUT_SOURCE_BUF_SIZE 4096
\r
625 typedef struct _InputSource {
\r
632 char buf[INPUT_SOURCE_BUF_SIZE];
\r
636 InputCallback func;
\r
637 struct _InputSource *second; /* for stderr thread on CPRcmd */
\r
641 InputSource *consoleInputSource;
\r
646 VOID ConsoleOutput(char* data, int length, int forceVisible);
\r
647 VOID ConsoleCreate();
\r
649 ConsoleWndProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam);
\r
650 VOID ColorizeTextPopup(HWND hwnd, ColorClass cc);
\r
651 VOID PrintCommSettings(FILE *f, char *name, DCB *dcb);
\r
652 VOID ParseCommSettings(char *arg, DCB *dcb);
\r
654 StartupDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam);
\r
655 VOID APIENTRY MenuPopup(HWND hwnd, POINT pt, HMENU hmenu, UINT def);
\r
656 void ParseIcsTextMenu(char *icsTextMenuString);
\r
657 VOID PopUpNameDialog(char firstchar);
\r
658 VOID UpdateSampleText(HWND hDlg, int id, MyColorizeAttribs *mca);
\r
662 int GameListOptions();
\r
664 int dummy; // [HGM] for obsolete args
\r
666 HWND hwndMain = NULL; /* root window*/
\r
667 HWND hwndConsole = NULL;
\r
668 HWND commentDialog = NULL;
\r
669 HWND moveHistoryDialog = NULL;
\r
670 HWND evalGraphDialog = NULL;
\r
671 HWND engineOutputDialog = NULL;
\r
672 HWND gameListDialog = NULL;
\r
673 HWND editTagsDialog = NULL;
\r
675 int commentUp = FALSE;
\r
677 WindowPlacement wpMain;
\r
678 WindowPlacement wpConsole;
\r
679 WindowPlacement wpComment;
\r
680 WindowPlacement wpMoveHistory;
\r
681 WindowPlacement wpEvalGraph;
\r
682 WindowPlacement wpEngineOutput;
\r
683 WindowPlacement wpGameList;
\r
684 WindowPlacement wpTags;
\r
686 VOID EngineOptionsPopup(); // [HGM] settings
\r
688 VOID GothicPopUp(char *title, VariantClass variant);
\r
690 * Setting "frozen" should disable all user input other than deleting
\r
691 * the window. We do this while engines are initializing themselves.
\r
693 static int frozen = 0;
\r
694 static int oldMenuItemState[MENU_BAR_ITEMS];
\r
700 if (frozen) return;
\r
702 hmenu = GetMenu(hwndMain);
\r
703 for (i=0; i<MENU_BAR_ITEMS; i++) {
\r
704 oldMenuItemState[i] = EnableMenuItem(hmenu, i, MF_BYPOSITION|MF_GRAYED);
\r
706 DrawMenuBar(hwndMain);
\r
709 /* Undo a FreezeUI */
\r
715 if (!frozen) return;
\r
717 hmenu = GetMenu(hwndMain);
\r
718 for (i=0; i<MENU_BAR_ITEMS; i++) {
\r
719 EnableMenuItem(hmenu, i, MF_BYPOSITION|oldMenuItemState[i]);
\r
721 DrawMenuBar(hwndMain);
\r
724 /*static*/ int fromX = -1, fromY = -1, toX, toY; // [HGM] moved upstream, so JAWS can use them
\r
726 /* JAWS preparation patch (WinBoard for the sight impaired). Define required insertions as empty */
\r
732 #define JAWS_ALT_INTERCEPT
\r
733 #define JAWS_KB_NAVIGATION
\r
734 #define JAWS_MENU_ITEMS
\r
735 #define JAWS_SILENCE
\r
736 #define JAWS_REPLAY
\r
738 #define JAWS_COPYRIGHT
\r
739 #define JAWS_DELETE(X) X
\r
740 #define SAYMACHINEMOVE()
\r
744 /*---------------------------------------------------------------------------*\
\r
748 \*---------------------------------------------------------------------------*/
\r
751 WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
\r
752 LPSTR lpCmdLine, int nCmdShow)
\r
755 HANDLE hAccelMain, hAccelNoAlt, hAccelNoICS;
\r
756 // INITCOMMONCONTROLSEX ex;
\r
760 LoadLibrary("RICHED32.DLL");
\r
761 consoleCF.cbSize = sizeof(CHARFORMAT);
\r
763 if (!InitApplication(hInstance)) {
\r
766 if (!InitInstance(hInstance, nCmdShow, lpCmdLine)) {
\r
773 // InitCommonControlsEx(&ex);
\r
774 InitCommonControls();
\r
776 hAccelMain = LoadAccelerators (hInstance, szAppName);
\r
777 hAccelNoAlt = LoadAccelerators (hInstance, "NO_ALT");
\r
778 hAccelNoICS = LoadAccelerators( hInstance, "NO_ICS"); /* [AS] No Ctrl-V on ICS!!! */
\r
780 /* Acquire and dispatch messages until a WM_QUIT message is received. */
\r
782 while (GetMessage(&msg, /* message structure */
\r
783 NULL, /* handle of window receiving the message */
\r
784 0, /* lowest message to examine */
\r
785 0)) /* highest message to examine */
\r
788 if(msg.message == WM_CHAR && msg.wParam == '\t') {
\r
789 // [HGM] navigate: switch between all windows with tab
\r
790 HWND e1 = NULL, e2 = NULL, mh = NULL, hInput = NULL, hText = NULL;
\r
791 int i, currentElement = 0;
\r
793 // first determine what element of the chain we come from (if any)
\r
794 if(appData.icsActive) {
\r
795 hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
796 hText = GetDlgItem(hwndConsole, OPT_ConsoleText);
\r
798 if(engineOutputDialog && EngineOutputIsUp()) {
\r
799 e1 = GetDlgItem(engineOutputDialog, IDC_EngineMemo1);
\r
800 e2 = GetDlgItem(engineOutputDialog, IDC_EngineMemo2);
\r
802 if(moveHistoryDialog && MoveHistoryIsUp()) {
\r
803 mh = GetDlgItem(moveHistoryDialog, IDC_MoveHistory);
\r
805 if(msg.hwnd == hwndMain) currentElement = 7 ; else
\r
806 if(msg.hwnd == engineOutputDialog) currentElement = 2; else
\r
807 if(msg.hwnd == e1) currentElement = 2; else
\r
808 if(msg.hwnd == e2) currentElement = 3; else
\r
809 if(msg.hwnd == moveHistoryDialog) currentElement = 4; else
\r
810 if(msg.hwnd == mh) currentElement = 4; else
\r
811 if(msg.hwnd == evalGraphDialog) currentElement = 6; else
\r
812 if(msg.hwnd == hText) currentElement = 5; else
\r
813 if(msg.hwnd == hInput) currentElement = 6; else
\r
814 for (i = 0; i < N_BUTTONS; i++) {
\r
815 if (buttonDesc[i].hwnd == msg.hwnd) { currentElement = 1; break; }
\r
818 // determine where to go to
\r
819 if(currentElement) { HWND h = NULL; int direction = GetKeyState(VK_SHIFT) < 0 ? -1 : 1;
\r
821 currentElement = (currentElement + direction) % 7;
\r
822 switch(currentElement) {
\r
824 h = hwndMain; break; // passing this case always makes the loop exit
\r
826 h = buttonDesc[0].hwnd; break; // could be NULL
\r
828 if(!EngineOutputIsUp()) continue; // skip closed auxiliary windows
\r
831 if(!EngineOutputIsUp()) continue;
\r
834 if(!MoveHistoryIsUp()) continue;
\r
836 // case 6: // input to eval graph does not seem to get here!
\r
837 // if(!EvalGraphIsUp()) continue;
\r
838 // h = evalGraphDialog; break;
\r
840 if(!appData.icsActive) continue;
\r
844 if(!appData.icsActive) continue;
\r
850 if(currentElement > 4 && IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);
\r
851 if(currentElement < 5 && IsIconic(hwndMain)) ShowWindow(hwndMain, SW_RESTORE); // all open together
\r
854 continue; // this message now has been processed
\r
858 if (!(commentDialog && IsDialogMessage(commentDialog, &msg)) &&
\r
859 !(moveHistoryDialog && IsDialogMessage(moveHistoryDialog, &msg)) &&
\r
860 !(evalGraphDialog && IsDialogMessage(evalGraphDialog, &msg)) &&
\r
861 !(engineOutputDialog && IsDialogMessage(engineOutputDialog, &msg)) &&
\r
862 !(editTagsDialog && IsDialogMessage(editTagsDialog, &msg)) &&
\r
863 !(gameListDialog && IsDialogMessage(gameListDialog, &msg)) &&
\r
864 !(errorDialog && IsDialogMessage(errorDialog, &msg)) &&
\r
865 !(!frozen && TranslateAccelerator(hwndMain, hAccelMain, &msg)) && JAWS_ACCEL
\r
866 !(!hwndConsole && TranslateAccelerator(hwndMain, hAccelNoICS, &msg)) &&
\r
867 !(!hwndConsole && TranslateAccelerator(hwndMain, hAccelNoAlt, &msg))) {
\r
868 int done = 0, i; // [HGM] chat: dispatch cat-box messages
\r
869 for(i=0; i<MAX_CHAT; i++)
\r
870 if(chatHandle[i] && IsDialogMessage(chatHandle[i], &msg)) {
\r
873 if(done) continue; // [HGM] chat: end patch
\r
874 TranslateMessage(&msg); /* Translates virtual key codes */
\r
875 DispatchMessage(&msg); /* Dispatches message to window */
\r
880 return (msg.wParam); /* Returns the value from PostQuitMessage */
\r
883 /*---------------------------------------------------------------------------*\
\r
885 * Initialization functions
\r
887 \*---------------------------------------------------------------------------*/
\r
891 { // update user logo if necessary
\r
892 static char oldUserName[MSG_SIZ], *curName;
\r
894 if(appData.autoLogo) {
\r
895 curName = UserName();
\r
896 if(strcmp(curName, oldUserName)) {
\r
897 snprintf(oldUserName, MSG_SIZ, "logos\\%s.bmp", curName);
\r
898 userLogo = LoadImage( 0, oldUserName, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );
\r
899 safeStrCpy(oldUserName, curName, sizeof(oldUserName)/sizeof(oldUserName[0]) );
\r
900 if(userLogo == NULL)
\r
901 userLogo = LoadImage( 0, "logos\\dummy.bmp", IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );
\r
907 InitApplication(HINSTANCE hInstance)
\r
911 /* Fill in window class structure with parameters that describe the */
\r
914 wc.style = CS_HREDRAW | CS_VREDRAW; /* Class style(s). */
\r
915 wc.lpfnWndProc = (WNDPROC)WndProc; /* Window Procedure */
\r
916 wc.cbClsExtra = 0; /* No per-class extra data. */
\r
917 wc.cbWndExtra = 0; /* No per-window extra data. */
\r
918 wc.hInstance = hInstance; /* Owner of this class */
\r
919 wc.hIcon = LoadIcon(hInstance, "icon_white");
\r
920 wc.hCursor = LoadCursor(NULL, IDC_ARROW); /* Cursor */
\r
921 wc.hbrBackground = (HBRUSH)(COLOR_WINDOW+1); /* Default color */
\r
922 wc.lpszMenuName = szAppName; /* Menu name from .RC */
\r
923 wc.lpszClassName = szAppName; /* Name to register as */
\r
925 /* Register the window class and return success/failure code. */
\r
926 if (!RegisterClass(&wc)) return FALSE;
\r
928 wc.style = CS_HREDRAW | CS_VREDRAW;
\r
929 wc.lpfnWndProc = (WNDPROC)ConsoleWndProc;
\r
931 wc.cbWndExtra = DLGWINDOWEXTRA;
\r
932 wc.hInstance = hInstance;
\r
933 wc.hIcon = LoadIcon(hInstance, "icon_white");
\r
934 wc.hCursor = LoadCursor(NULL, IDC_ARROW);
\r
935 wc.hbrBackground = (HBRUSH)(COLOR_MENU+1);
\r
936 wc.lpszMenuName = NULL;
\r
937 wc.lpszClassName = szConsoleName;
\r
939 if (!RegisterClass(&wc)) return FALSE;
\r
944 /* Set by InitInstance, used by EnsureOnScreen */
\r
945 int screenHeight, screenWidth;
\r
948 EnsureOnScreen(int *x, int *y, int minX, int minY)
\r
950 // int gap = GetSystemMetrics(SM_CYFRAME) + GetSystemMetrics(SM_CYCAPTION);
\r
951 /* Be sure window at (x,y) is not off screen (or even mostly off screen) */
\r
952 if (*x > screenWidth - 32) *x = 0;
\r
953 if (*y > screenHeight - 32) *y = 0;
\r
954 if (*x < minX) *x = minX;
\r
955 if (*y < minY) *y = minY;
\r
959 LoadLogo(ChessProgramState *cps, int n)
\r
961 if( appData.logo[n] && appData.logo[n][0] != NULLCHAR) {
\r
962 cps->programLogo = LoadImage( 0, appData.logo[n], IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );
\r
964 if (cps->programLogo == NULL && appData.debugMode) {
\r
965 fprintf( debugFP, "Unable to load logo bitmap '%s'\n", appData.logo[n] );
\r
967 } else if(appData.autoLogo) {
\r
968 if(appData.firstDirectory && appData.directory[n][0]) {
\r
970 snprintf(buf, MSG_SIZ, "%s/logo.bmp", appData.directory[n]);
\r
971 cps->programLogo = LoadImage( 0, buf, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );
\r
977 InitInstance(HINSTANCE hInstance, int nCmdShow, LPSTR lpCmdLine)
\r
979 HWND hwnd; /* Main window handle. */
\r
981 WINDOWPLACEMENT wp;
\r
984 hInst = hInstance; /* Store instance handle in our global variable */
\r
985 programName = szAppName;
\r
987 if (SearchPath(NULL, "WinBoard.exe", NULL, MSG_SIZ, installDir, &filepart)) {
\r
988 *filepart = NULLCHAR;
\r
990 GetCurrentDirectory(MSG_SIZ, installDir);
\r
992 gameInfo.boardWidth = gameInfo.boardHeight = 8; // [HGM] won't have open window otherwise
\r
993 screenWidth = screenHeight = 1000; // [HGM] placement: kludge to allow calling EnsureOnScreen from InitAppData
\r
994 InitAppData(lpCmdLine); /* Get run-time parameters */
\r
995 /* xboard, and older WinBoards, controlled the move sound with the
\r
996 appData.ringBellAfterMoves option. In the current WinBoard, we
\r
997 always turn the option on (so that the backend will call us),
\r
998 then let the user turn the sound off by setting it to silence if
\r
999 desired. To accommodate old winboard.ini files saved by old
\r
1000 versions of WinBoard, we also turn off the sound if the option
\r
1001 was initially set to false. [HGM] taken out of InitAppData */
\r
1002 if (!appData.ringBellAfterMoves) {
\r
1003 sounds[(int)SoundMove].name = strdup("");
\r
1004 appData.ringBellAfterMoves = TRUE;
\r
1006 if (appData.debugMode) {
\r
1007 debugFP = fopen(appData.nameOfDebugFile, "w");
\r
1008 setbuf(debugFP, NULL);
\r
1011 LoadLanguageFile(appData.language);
\r
1015 // InitEngineUCI( installDir, &first ); // [HGM] incorporated in InitBackEnd1()
\r
1016 // InitEngineUCI( installDir, &second );
\r
1018 /* Create a main window for this application instance. */
\r
1019 hwnd = CreateWindow(szAppName, szTitle,
\r
1020 (WS_OVERLAPPEDWINDOW & ~WS_MAXIMIZEBOX),
\r
1021 CW_USEDEFAULT, 0, CW_USEDEFAULT, 0,
\r
1022 NULL, NULL, hInstance, NULL);
\r
1025 /* If window could not be created, return "failure" */
\r
1030 /* [HGM] logo: Load logos if specified (must be done before InitDrawingSizes) */
\r
1031 LoadLogo(&first, 0);
\r
1033 if( appData.secondLogo && appData.secondLogo[0] != NULLCHAR) {
\r
1034 second.programLogo = LoadImage( 0, appData.secondLogo, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );
\r
1036 if (second.programLogo == NULL && appData.debugMode) {
\r
1037 fprintf( debugFP, "Unable to load logo bitmap '%s'\n", appData.secondLogo );
\r
1039 } else if(appData.autoLogo) {
\r
1040 char buf[MSG_SIZ];
\r
1041 if(appData.icsActive) { // [HGM] logo: in ICS mode second can be used for ICS
\r
1042 snprintf(buf, MSG_SIZ, "logos\\%s.bmp", appData.icsHost);
\r
1043 second.programLogo = LoadImage( 0, buf, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );
\r
1045 if(appData.secondDirectory && appData.secondDirectory[0]) {
\r
1046 snprintf(buf, MSG_SIZ, "%s\\logo.bmp", appData.secondDirectory);
\r
1047 second.programLogo = LoadImage( 0, buf, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );
\r
1053 iconWhite = LoadIcon(hInstance, "icon_white");
\r
1054 iconBlack = LoadIcon(hInstance, "icon_black");
\r
1055 iconCurrent = iconWhite;
\r
1056 InitDrawingColors();
\r
1057 screenHeight = GetSystemMetrics(SM_CYSCREEN);
\r
1058 screenWidth = GetSystemMetrics(SM_CXSCREEN);
\r
1059 for (ibs = (int) NUM_SIZES - 1; ibs >= 0; ibs--) {
\r
1060 /* Compute window size for each board size, and use the largest
\r
1061 size that fits on this screen as the default. */
\r
1062 InitDrawingSizes((BoardSize)(ibs+1000), 0);
\r
1063 if (boardSize == (BoardSize)-1 &&
\r
1064 winH <= screenHeight
\r
1065 - GetSystemMetrics(SM_CYFRAME) - GetSystemMetrics(SM_CYCAPTION) - 10
\r
1066 && winW <= screenWidth) {
\r
1067 boardSize = (BoardSize)ibs;
\r
1071 InitDrawingSizes(boardSize, 0);
\r
1073 buttonCount = GetSystemMetrics(SM_CMOUSEBUTTONS);
\r
1075 /* [AS] Load textures if specified */
\r
1076 ZeroMemory( &backTextureSquareInfo, sizeof(backTextureSquareInfo) );
\r
1078 if( appData.liteBackTextureFile && appData.liteBackTextureFile[0] != NULLCHAR && appData.liteBackTextureFile[0] != '*' ) {
\r
1079 liteBackTexture = LoadImage( 0, appData.liteBackTextureFile, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );
\r
1080 liteBackTextureMode = appData.liteBackTextureMode;
\r
1082 if (liteBackTexture == NULL && appData.debugMode) {
\r
1083 fprintf( debugFP, "Unable to load lite texture bitmap '%s'\n", appData.liteBackTextureFile );
\r
1087 if( appData.darkBackTextureFile && appData.darkBackTextureFile[0] != NULLCHAR && appData.darkBackTextureFile[0] != '*' ) {
\r
1088 darkBackTexture = LoadImage( 0, appData.darkBackTextureFile, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );
\r
1089 darkBackTextureMode = appData.darkBackTextureMode;
\r
1091 if (darkBackTexture == NULL && appData.debugMode) {
\r
1092 fprintf( debugFP, "Unable to load dark texture bitmap '%s'\n", appData.darkBackTextureFile );
\r
1096 mysrandom( (unsigned) time(NULL) );
\r
1098 /* [AS] Restore layout */
\r
1099 if( wpMoveHistory.visible ) {
\r
1100 MoveHistoryPopUp();
\r
1103 if( wpEvalGraph.visible ) {
\r
1107 if( wpEngineOutput.visible ) {
\r
1108 EngineOutputPopUp();
\r
1111 /* Make the window visible; update its client area; and return "success" */
\r
1112 EnsureOnScreen(&wpMain.x, &wpMain.y, minX, minY);
\r
1113 wp.length = sizeof(WINDOWPLACEMENT);
\r
1115 wp.showCmd = nCmdShow;
\r
1116 wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;
\r
1117 wp.rcNormalPosition.left = wpMain.x;
\r
1118 wp.rcNormalPosition.right = wpMain.x + wpMain.width;
\r
1119 wp.rcNormalPosition.top = wpMain.y;
\r
1120 wp.rcNormalPosition.bottom = wpMain.y + wpMain.height;
\r
1121 SetWindowPlacement(hwndMain, &wp);
\r
1123 InitBackEnd2(); // [HGM] moved until after all windows placed, to save correct position if fatal error on engine start
\r
1125 if(!appData.noGUI) SetWindowPos(hwndMain, alwaysOnTop ? HWND_TOPMOST : HWND_NOTOPMOST,
\r
1126 0, 0, 0, 0, SWP_NOMOVE|SWP_NOSIZE);
\r
1128 if (hwndConsole) {
\r
1130 SetWindowPos(hwndConsole, alwaysOnTop ? HWND_TOPMOST : HWND_NOTOPMOST,
\r
1131 0, 0, 0, 0, SWP_NOMOVE|SWP_NOSIZE);
\r
1133 ShowWindow(hwndConsole, nCmdShow);
\r
1134 if(appData.chatBoxes) { // [HGM] chat: open chat boxes
\r
1135 char buf[MSG_SIZ], *p = buf, *q;
\r
1136 safeStrCpy(buf, appData.chatBoxes, sizeof(buf)/sizeof(buf[0]) );
\r
1138 q = strchr(p, ';');
\r
1140 if(*p) ChatPopUp(p);
\r
1143 SetActiveWindow(hwndConsole);
\r
1145 if(!appData.noGUI) UpdateWindow(hwnd); else ShowWindow(hwnd, SW_MINIMIZE);
\r
1146 if(gameListDialog) SetFocus(gameListDialog); // [HGM] jaws: for if we clicked multi-game game file
\r
1155 HMENU hmenu = GetMenu(hwndMain);
\r
1157 (void) EnableMenuItem(hmenu, IDM_CommPort,
\r
1158 MF_BYCOMMAND|((appData.icsActive &&
\r
1159 *appData.icsCommPort != NULLCHAR) ?
\r
1160 MF_ENABLED : MF_GRAYED));
\r
1161 (void) CheckMenuItem(hmenu, IDM_SaveSettingsOnExit,
\r
1162 MF_BYCOMMAND|(saveSettingsOnExit ?
\r
1163 MF_CHECKED : MF_UNCHECKED));
\r
1166 //---------------------------------------------------------------------------------------------------------
\r
1168 #define ICS_TEXT_MENU_SIZE (IDM_CommandXLast - IDM_CommandX + 1)
\r
1169 #define XBOARD FALSE
\r
1171 #define OPTCHAR "/"
\r
1172 #define SEPCHAR "="
\r
1176 // front-end part of option handling
\r
1179 LFfromMFP(LOGFONT* lf, MyFontParams *mfp)
\r
1181 HDC hdc = CreateDC("DISPLAY", NULL, NULL, NULL);
\r
1182 lf->lfHeight = -(int)(mfp->pointSize * GetDeviceCaps(hdc, LOGPIXELSY) / 72.0 + 0.5);
\r
1185 lf->lfEscapement = 0;
\r
1186 lf->lfOrientation = 0;
\r
1187 lf->lfWeight = mfp->bold ? FW_BOLD : FW_NORMAL;
\r
1188 lf->lfItalic = mfp->italic;
\r
1189 lf->lfUnderline = mfp->underline;
\r
1190 lf->lfStrikeOut = mfp->strikeout;
\r
1191 lf->lfCharSet = mfp->charset;
\r
1192 lf->lfOutPrecision = OUT_DEFAULT_PRECIS;
\r
1193 lf->lfClipPrecision = CLIP_DEFAULT_PRECIS;
\r
1194 lf->lfQuality = DEFAULT_QUALITY;
\r
1195 lf->lfPitchAndFamily = DEFAULT_PITCH|FF_DONTCARE;
\r
1196 safeStrCpy(lf->lfFaceName, mfp->faceName, sizeof(lf->lfFaceName)/sizeof(lf->lfFaceName[0]) );
\r
1200 CreateFontInMF(MyFont *mf)
\r
1202 LFfromMFP(&mf->lf, &mf->mfp);
\r
1203 if (mf->hf) DeleteObject(mf->hf);
\r
1204 mf->hf = CreateFontIndirect(&mf->lf);
\r
1207 // [HGM] This platform-dependent table provides the location for storing the color info
\r
1209 colorVariable[] = {
\r
1210 &whitePieceColor,
\r
1211 &blackPieceColor,
\r
1212 &lightSquareColor,
\r
1213 &darkSquareColor,
\r
1214 &highlightSquareColor,
\r
1215 &premoveHighlightColor,
\r
1217 &consoleBackgroundColor,
\r
1218 &appData.fontForeColorWhite,
\r
1219 &appData.fontBackColorWhite,
\r
1220 &appData.fontForeColorBlack,
\r
1221 &appData.fontBackColorBlack,
\r
1222 &appData.evalHistColorWhite,
\r
1223 &appData.evalHistColorBlack,
\r
1224 &appData.highlightArrowColor,
\r
1227 /* Command line font name parser. NULL name means do nothing.
\r
1228 Syntax like "Courier New:10.0 bi" or "Arial:10" or "Arial:10b"
\r
1229 For backward compatibility, syntax without the colon is also
\r
1230 accepted, but font names with digits in them won't work in that case.
\r
1233 ParseFontName(char *name, MyFontParams *mfp)
\r
1236 if (name == NULL) return;
\r
1238 q = strchr(p, ':');
\r
1240 if (q - p >= sizeof(mfp->faceName))
\r
1241 ExitArgError(_("Font name too long:"), name);
\r
1242 memcpy(mfp->faceName, p, q - p);
\r
1243 mfp->faceName[q - p] = NULLCHAR;
\r
1246 q = mfp->faceName;
\r
1247 while (*p && !isdigit(*p)) {
\r
1249 if (q - mfp->faceName >= sizeof(mfp->faceName))
\r
1250 ExitArgError(_("Font name too long:"), name);
\r
1252 while (q > mfp->faceName && q[-1] == ' ') q--;
\r
1255 if (!*p) ExitArgError(_("Font point size missing:"), name);
\r
1256 mfp->pointSize = (float) atof(p);
\r
1257 mfp->bold = (strchr(p, 'b') != NULL);
\r
1258 mfp->italic = (strchr(p, 'i') != NULL);
\r
1259 mfp->underline = (strchr(p, 'u') != NULL);
\r
1260 mfp->strikeout = (strchr(p, 's') != NULL);
\r
1261 mfp->charset = DEFAULT_CHARSET;
\r
1262 q = strchr(p, 'c');
\r
1264 mfp->charset = (BYTE) atoi(q+1);
\r
1268 ParseFont(char *name, int number)
\r
1269 { // wrapper to shield back-end from 'font'
\r
1270 ParseFontName(name, &font[boardSize][number]->mfp);
\r
1275 { // in WB we have a 2D array of fonts; this initializes their description
\r
1277 /* Point font array elements to structures and
\r
1278 parse default font names */
\r
1279 for (i=0; i<NUM_FONTS; i++) {
\r
1280 for (j=0; j<NUM_SIZES; j++) {
\r
1281 font[j][i] = &fontRec[j][i];
\r
1282 ParseFontName(font[j][i]->def, &font[j][i]->mfp);
\r
1289 { // here we create the actual fonts from the selected descriptions
\r
1291 for (i=0; i<NUM_FONTS; i++) {
\r
1292 for (j=0; j<NUM_SIZES; j++) {
\r
1293 CreateFontInMF(font[j][i]);
\r
1297 /* Color name parser.
\r
1298 X version accepts X color names, but this one
\r
1299 handles only the #rrggbb form (hex) or rrr,ggg,bbb (decimal) */
\r
1301 ParseColorName(char *name)
\r
1303 int red, green, blue, count;
\r
1304 char buf[MSG_SIZ];
\r
1306 count = sscanf(name, "#%2x%2x%2x", &red, &green, &blue);
\r
1308 count = sscanf(name, "%3d%*[^0-9]%3d%*[^0-9]%3d",
\r
1309 &red, &green, &blue);
\r
1312 snprintf(buf, MSG_SIZ, _("Can't parse color name %s"), name);
\r
1313 DisplayError(buf, 0);
\r
1314 return RGB(0, 0, 0);
\r
1316 return PALETTERGB(red, green, blue);
\r
1320 ParseColor(int n, char *name)
\r
1321 { // for WinBoard the color is an int, which needs to be derived from the string
\r
1322 if(colorVariable[n]) *(int*)colorVariable[n] = ParseColorName(name);
\r
1326 ParseAttribs(COLORREF *color, int *effects, char* argValue)
\r
1328 char *e = argValue;
\r
1332 if (*e == 'b') eff |= CFE_BOLD;
\r
1333 else if (*e == 'i') eff |= CFE_ITALIC;
\r
1334 else if (*e == 'u') eff |= CFE_UNDERLINE;
\r
1335 else if (*e == 's') eff |= CFE_STRIKEOUT;
\r
1336 else if (*e == '#' || isdigit(*e)) break;
\r
1340 *color = ParseColorName(e);
\r
1344 ParseTextAttribs(ColorClass cc, char *s)
\r
1345 { // [HGM] front-end wrapper that does the platform-dependent call
\r
1346 // for XBoard we would set (&appData.colorShout)[cc] = strdup(s);
\r
1347 ParseAttribs(&textAttribs[cc].color, &textAttribs[cc].effects, s);
\r
1351 ParseBoardSize(void *addr, char *name)
\r
1352 { // [HGM] rewritten with return-value ptr to shield back-end from BoardSize
\r
1353 BoardSize bs = SizeTiny;
\r
1354 while (sizeInfo[bs].name != NULL) {
\r
1355 if (StrCaseCmp(name, sizeInfo[bs].name) == 0) {
\r
1356 *(BoardSize *)addr = bs;
\r
1361 ExitArgError(_("Unrecognized board size value"), name);
\r
1366 { // [HGM] import name from appData first
\r
1369 for (cc = (ColorClass)0; cc < ColorNormal; cc++) {
\r
1370 textAttribs[cc].sound.name = strdup((&appData.soundShout)[cc]);
\r
1371 textAttribs[cc].sound.data = NULL;
\r
1372 MyLoadSound(&textAttribs[cc].sound);
\r
1374 for (cc = ColorNormal; cc < NColorClasses; cc++) {
\r
1375 textAttribs[cc].sound.name = strdup("");
\r
1376 textAttribs[cc].sound.data = NULL;
\r
1378 for (sc = (SoundClass)0; sc < NSoundClasses; sc++) {
\r
1379 sounds[sc].name = strdup((&appData.soundMove)[sc]);
\r
1380 sounds[sc].data = NULL;
\r
1381 MyLoadSound(&sounds[sc]);
\r
1386 SetCommPortDefaults()
\r
1388 memset(&dcb, 0, sizeof(DCB)); // required by VS 2002 +
\r
1389 dcb.DCBlength = sizeof(DCB);
\r
1390 dcb.BaudRate = 9600;
\r
1391 dcb.fBinary = TRUE;
\r
1392 dcb.fParity = FALSE;
\r
1393 dcb.fOutxCtsFlow = FALSE;
\r
1394 dcb.fOutxDsrFlow = FALSE;
\r
1395 dcb.fDtrControl = DTR_CONTROL_ENABLE;
\r
1396 dcb.fDsrSensitivity = FALSE;
\r
1397 dcb.fTXContinueOnXoff = TRUE;
\r
1398 dcb.fOutX = FALSE;
\r
1400 dcb.fNull = FALSE;
\r
1401 dcb.fRtsControl = RTS_CONTROL_ENABLE;
\r
1402 dcb.fAbortOnError = FALSE;
\r
1404 dcb.Parity = SPACEPARITY;
\r
1405 dcb.StopBits = ONESTOPBIT;
\r
1408 // [HGM] args: these three cases taken out to stay in front-end
\r
1410 SaveFontArg(FILE *f, ArgDescriptor *ad)
\r
1411 { // in WinBoard every board size has its own font, and the "argLoc" identifies the table,
\r
1412 // while the curent board size determines the element. This system should be ported to XBoard.
\r
1413 // What the table contains pointers to, and how to print the font description, remains platform-dependent
\r
1415 for (bs=0; bs<NUM_SIZES; bs++) {
\r
1416 MyFontParams *mfp = &font[bs][(int) ad->argLoc]->mfp;
\r
1417 fprintf(f, "/size=%s ", sizeInfo[bs].name);
\r
1418 fprintf(f, "/%s=\"%s:%g%s%s%s%s%sc%d\"\n",
\r
1419 ad->argName, mfp->faceName, mfp->pointSize,
\r
1420 mfp->bold || mfp->italic || mfp->underline || mfp->strikeout ? " " : "",
\r
1421 mfp->bold ? "b" : "",
\r
1422 mfp->italic ? "i" : "",
\r
1423 mfp->underline ? "u" : "",
\r
1424 mfp->strikeout ? "s" : "",
\r
1425 (int)mfp->charset);
\r
1431 { // [HGM] copy the names from the internal WB variables to appData
\r
1434 for (cc = (ColorClass)0; cc < ColorNormal; cc++)
\r
1435 (&appData.soundShout)[cc] = textAttribs[cc].sound.name;
\r
1436 for (sc = (SoundClass)0; sc < NSoundClasses; sc++)
\r
1437 (&appData.soundMove)[sc] = sounds[sc].name;
\r
1441 SaveAttribsArg(FILE *f, ArgDescriptor *ad)
\r
1442 { // here the "argLoc" defines a table index. It could have contained the 'ta' pointer itself, though
\r
1443 MyTextAttribs* ta = &textAttribs[(ColorClass)ad->argLoc];
\r
1444 fprintf(f, "/%s=\"%s%s%s%s%s#%02lx%02lx%02lx\"\n", ad->argName,
\r
1445 (ta->effects & CFE_BOLD) ? "b" : "",
\r
1446 (ta->effects & CFE_ITALIC) ? "i" : "",
\r
1447 (ta->effects & CFE_UNDERLINE) ? "u" : "",
\r
1448 (ta->effects & CFE_STRIKEOUT) ? "s" : "",
\r
1449 (ta->effects) ? " " : "",
\r
1450 ta->color&0xff, (ta->color >> 8)&0xff, (ta->color >> 16)&0xff);
\r
1454 SaveColor(FILE *f, ArgDescriptor *ad)
\r
1455 { // in WinBoard the color is an int and has to be converted to text. In X it would be a string already?
\r
1456 COLORREF color = *(COLORREF *)colorVariable[(int)ad->argLoc];
\r
1457 fprintf(f, "/%s=#%02lx%02lx%02lx\n", ad->argName,
\r
1458 color&0xff, (color>>8)&0xff, (color>>16)&0xff);
\r
1462 SaveBoardSize(FILE *f, char *name, void *addr)
\r
1463 { // wrapper to shield back-end from BoardSize & sizeInfo
\r
1464 fprintf(f, "/%s=%s\n", name, sizeInfo[*(BoardSize *)addr].name);
\r
1468 ParseCommPortSettings(char *s)
\r
1469 { // wrapper to keep dcb from back-end
\r
1470 ParseCommSettings(s, &dcb);
\r
1475 { // wrapper to shield use of window handles from back-end (make addressible by number?)
\r
1476 GetActualPlacement(hwndMain, &wpMain);
\r
1477 GetActualPlacement(hwndConsole, &wpConsole);
\r
1478 GetActualPlacement(commentDialog, &wpComment);
\r
1479 GetActualPlacement(editTagsDialog, &wpTags);
\r
1480 GetActualPlacement(gameListDialog, &wpGameList);
\r
1481 GetActualPlacement(moveHistoryDialog, &wpMoveHistory);
\r
1482 GetActualPlacement(evalGraphDialog, &wpEvalGraph);
\r
1483 GetActualPlacement(engineOutputDialog, &wpEngineOutput);
\r
1487 PrintCommPortSettings(FILE *f, char *name)
\r
1488 { // wrapper to shield back-end from DCB
\r
1489 PrintCommSettings(f, name, &dcb);
\r
1493 MySearchPath(char *installDir, char *name, char *fullname)
\r
1495 char *dummy, buf[MSG_SIZ], *p = name, *q;
\r
1496 if(name[0]== '%') {
\r
1497 fullname[0] = 0; // [HGM] first expand any environment variables in the given name
\r
1498 while(*p == '%' && (q = strchr(p+1, '%'))) { // [HGM] recognize %*% as environment variable
\r
1499 safeStrCpy(buf, p+1, sizeof(buf)/sizeof(buf[0]) );
\r
1500 *strchr(buf, '%') = 0;
\r
1501 strcat(fullname, getenv(buf));
\r
1502 p = q+1; while(*p == '\\') { strcat(fullname, "\\"); p++; }
\r
1504 strcat(fullname, p); // after environment variables (if any), take the remainder of the given name
\r
1505 if(appData.debugMode) fprintf(debugFP, "name = '%s', expanded name = '%s'\n", name, fullname);
\r
1506 return (int) strlen(fullname);
\r
1508 return (int) SearchPath(installDir, name, NULL, MSG_SIZ, fullname, &dummy);
\r
1512 MyGetFullPathName(char *name, char *fullname)
\r
1515 return (int) GetFullPathName(name, MSG_SIZ, fullname, &dummy);
\r
1520 { // [HGM] args: allows testing if main window is realized from back-end
\r
1521 return hwndMain != NULL;
\r
1525 PopUpStartupDialog()
\r
1529 LoadLanguageFile(appData.language);
\r
1530 lpProc = MakeProcInstance((FARPROC)StartupDialog, hInst);
\r
1531 DialogBox(hInst, MAKEINTRESOURCE(DLG_Startup), NULL, (DLGPROC)lpProc);
\r
1532 FreeProcInstance(lpProc);
\r
1535 /*---------------------------------------------------------------------------*\
\r
1537 * GDI board drawing routines
\r
1539 \*---------------------------------------------------------------------------*/
\r
1541 /* [AS] Draw square using background texture */
\r
1542 static void DrawTile( int dx, int dy, int dw, int dh, HDC dst, HDC src, int mode, int sx, int sy )
\r
1547 return; /* Should never happen! */
\r
1550 SetGraphicsMode( dst, GM_ADVANCED );
\r
1557 /* X reflection */
\r
1562 x.eDx = (FLOAT) dw + dx - 1;
\r
1565 SetWorldTransform( dst, &x );
\r
1568 /* Y reflection */
\r
1574 x.eDy = (FLOAT) dh + dy - 1;
\r
1576 SetWorldTransform( dst, &x );
\r
1584 x.eDx = (FLOAT) dx;
\r
1585 x.eDy = (FLOAT) dy;
\r
1588 SetWorldTransform( dst, &x );
\r
1592 BitBlt( dst, dx, dy, dw, dh, src, sx, sy, SRCCOPY );
\r
1600 SetWorldTransform( dst, &x );
\r
1602 ModifyWorldTransform( dst, 0, MWT_IDENTITY );
\r
1605 /* [AS] [HGM] Make room for more piece types, so all pieces can be different */
\r
1607 PM_WP = (int) WhitePawn,
\r
1608 PM_WN = (int) WhiteKnight,
\r
1609 PM_WB = (int) WhiteBishop,
\r
1610 PM_WR = (int) WhiteRook,
\r
1611 PM_WQ = (int) WhiteQueen,
\r
1612 PM_WF = (int) WhiteFerz,
\r
1613 PM_WW = (int) WhiteWazir,
\r
1614 PM_WE = (int) WhiteAlfil,
\r
1615 PM_WM = (int) WhiteMan,
\r
1616 PM_WO = (int) WhiteCannon,
\r
1617 PM_WU = (int) WhiteUnicorn,
\r
1618 PM_WH = (int) WhiteNightrider,
\r
1619 PM_WA = (int) WhiteAngel,
\r
1620 PM_WC = (int) WhiteMarshall,
\r
1621 PM_WAB = (int) WhiteCardinal,
\r
1622 PM_WD = (int) WhiteDragon,
\r
1623 PM_WL = (int) WhiteLance,
\r
1624 PM_WS = (int) WhiteCobra,
\r
1625 PM_WV = (int) WhiteFalcon,
\r
1626 PM_WSG = (int) WhiteSilver,
\r
1627 PM_WG = (int) WhiteGrasshopper,
\r
1628 PM_WK = (int) WhiteKing,
\r
1629 PM_BP = (int) BlackPawn,
\r
1630 PM_BN = (int) BlackKnight,
\r
1631 PM_BB = (int) BlackBishop,
\r
1632 PM_BR = (int) BlackRook,
\r
1633 PM_BQ = (int) BlackQueen,
\r
1634 PM_BF = (int) BlackFerz,
\r
1635 PM_BW = (int) BlackWazir,
\r
1636 PM_BE = (int) BlackAlfil,
\r
1637 PM_BM = (int) BlackMan,
\r
1638 PM_BO = (int) BlackCannon,
\r
1639 PM_BU = (int) BlackUnicorn,
\r
1640 PM_BH = (int) BlackNightrider,
\r
1641 PM_BA = (int) BlackAngel,
\r
1642 PM_BC = (int) BlackMarshall,
\r
1643 PM_BG = (int) BlackGrasshopper,
\r
1644 PM_BAB = (int) BlackCardinal,
\r
1645 PM_BD = (int) BlackDragon,
\r
1646 PM_BL = (int) BlackLance,
\r
1647 PM_BS = (int) BlackCobra,
\r
1648 PM_BV = (int) BlackFalcon,
\r
1649 PM_BSG = (int) BlackSilver,
\r
1650 PM_BK = (int) BlackKing
\r
1653 static HFONT hPieceFont = NULL;
\r
1654 static HBITMAP hPieceMask[(int) EmptySquare];
\r
1655 static HBITMAP hPieceFace[(int) EmptySquare];
\r
1656 static int fontBitmapSquareSize = 0;
\r
1657 static char pieceToFontChar[(int) EmptySquare] =
\r
1658 { 'p', 'n', 'b', 'r', 'q',
\r
1659 'n', 'b', 'p', 'n', 'b', 'r', 'b', 'r', 'q', 'k',
\r
1660 'k', 'o', 'm', 'v', 't', 'w',
\r
1661 'v', 't', 'o', 'm', 'v', 't', 'v', 't', 'w', 'l',
\r
1664 extern BOOL SetCharTable( char *table, const char * map );
\r
1665 /* [HGM] moved to backend.c */
\r
1667 static void SetPieceBackground( HDC hdc, COLORREF color, int mode )
\r
1670 BYTE r1 = GetRValue( color );
\r
1671 BYTE g1 = GetGValue( color );
\r
1672 BYTE b1 = GetBValue( color );
\r
1678 /* Create a uniform background first */
\r
1679 hbrush = CreateSolidBrush( color );
\r
1680 SetRect( &rc, 0, 0, squareSize, squareSize );
\r
1681 FillRect( hdc, &rc, hbrush );
\r
1682 DeleteObject( hbrush );
\r
1685 /* Vertical gradient, good for pawn, knight and rook, less for queen and king */
\r
1686 int steps = squareSize / 2;
\r
1689 for( i=0; i<steps; i++ ) {
\r
1690 BYTE r = r1 - (r1-r2) * i / steps;
\r
1691 BYTE g = g1 - (g1-g2) * i / steps;
\r
1692 BYTE b = b1 - (b1-b2) * i / steps;
\r
1694 hbrush = CreateSolidBrush( RGB(r,g,b) );
\r
1695 SetRect( &rc, i + squareSize - steps, 0, i + squareSize - steps + 1, squareSize );
\r
1696 FillRect( hdc, &rc, hbrush );
\r
1697 DeleteObject(hbrush);
\r
1700 else if( mode == 2 ) {
\r
1701 /* Diagonal gradient, good more or less for every piece */
\r
1702 POINT triangle[3];
\r
1703 HPEN hpen = SelectObject( hdc, GetStockObject(NULL_PEN) );
\r
1704 HBRUSH hbrush_old;
\r
1705 int steps = squareSize;
\r
1708 triangle[0].x = squareSize - steps;
\r
1709 triangle[0].y = squareSize;
\r
1710 triangle[1].x = squareSize;
\r
1711 triangle[1].y = squareSize;
\r
1712 triangle[2].x = squareSize;
\r
1713 triangle[2].y = squareSize - steps;
\r
1715 for( i=0; i<steps; i++ ) {
\r
1716 BYTE r = r1 - (r1-r2) * i / steps;
\r
1717 BYTE g = g1 - (g1-g2) * i / steps;
\r
1718 BYTE b = b1 - (b1-b2) * i / steps;
\r
1720 hbrush = CreateSolidBrush( RGB(r,g,b) );
\r
1721 hbrush_old = SelectObject( hdc, hbrush );
\r
1722 Polygon( hdc, triangle, 3 );
\r
1723 SelectObject( hdc, hbrush_old );
\r
1724 DeleteObject(hbrush);
\r
1729 SelectObject( hdc, hpen );
\r
1734 [AS] The method I use to create the bitmaps it a bit tricky, but it
\r
1735 seems to work ok. The main problem here is to find the "inside" of a chess
\r
1736 piece: follow the steps as explained below.
\r
1738 static void CreatePieceMaskFromFont( HDC hdc_window, HDC hdc, int index )
\r
1742 COLORREF chroma = RGB(0xFF,0x00,0xFF);
\r
1746 int backColor = whitePieceColor;
\r
1747 int foreColor = blackPieceColor;
\r
1749 if( index < (int)BlackPawn && appData.fontBackColorWhite != appData.fontForeColorWhite ) {
\r
1750 backColor = appData.fontBackColorWhite;
\r
1751 foreColor = appData.fontForeColorWhite;
\r
1753 else if( index >= (int)BlackPawn && appData.fontBackColorBlack != appData.fontForeColorBlack ) {
\r
1754 backColor = appData.fontBackColorBlack;
\r
1755 foreColor = appData.fontForeColorBlack;
\r
1759 hbm = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );
\r
1761 hbm_old = SelectObject( hdc, hbm );
\r
1765 rc.right = squareSize;
\r
1766 rc.bottom = squareSize;
\r
1768 /* Step 1: background is now black */
\r
1769 FillRect( hdc, &rc, GetStockObject(BLACK_BRUSH) );
\r
1771 GetTextExtentPoint32( hdc, &pieceToFontChar[index], 1, &sz );
\r
1773 pt.x = (squareSize - sz.cx) / 2;
\r
1774 pt.y = (squareSize - sz.cy) / 2;
\r
1776 SetBkMode( hdc, TRANSPARENT );
\r
1777 SetTextColor( hdc, chroma );
\r
1778 /* Step 2: the piece has been drawn in purple, there are now black and purple in this bitmap */
\r
1779 TextOut( hdc, pt.x, pt.y, &pieceToFontChar[appData.allWhite && index >= (int)BlackPawn ? index - (int)BlackPawn : index], 1 );
\r
1781 SelectObject( hdc, GetStockObject(WHITE_BRUSH) );
\r
1782 /* Step 3: the area outside the piece is filled with white */
\r
1783 // FloodFill( hdc, 0, 0, chroma );
\r
1784 ExtFloodFill( hdc, 0, 0, 0, FLOODFILLSURFACE );
\r
1785 ExtFloodFill( hdc, 0, squareSize-1, 0, FLOODFILLSURFACE ); // [HGM] fill from all 4 corners, for if piece too big
\r
1786 ExtFloodFill( hdc, squareSize-1, 0, 0, FLOODFILLSURFACE );
\r
1787 ExtFloodFill( hdc, squareSize-1, squareSize-1, 0, FLOODFILLSURFACE );
\r
1788 SelectObject( hdc, GetStockObject(BLACK_BRUSH) );
\r
1790 Step 4: this is the tricky part, the area inside the piece is filled with black,
\r
1791 but if the start point is not inside the piece we're lost!
\r
1792 There should be a better way to do this... if we could create a region or path
\r
1793 from the fill operation we would be fine for example.
\r
1795 // FloodFill( hdc, squareSize / 2, squareSize / 2, RGB(0xFF,0xFF,0xFF) );
\r
1796 ExtFloodFill( hdc, squareSize / 2, squareSize / 2, RGB(0xFF,0xFF,0xFF), FLOODFILLBORDER );
\r
1798 { /* [HGM] shave off edges of mask, in an attempt to correct for the fact that FloodFill does not work correctly under Win XP */
\r
1799 HDC dc2 = CreateCompatibleDC( hdc_window );
\r
1800 HBITMAP bm2 = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );
\r
1802 SelectObject( dc2, bm2 );
\r
1803 BitBlt( dc2, 0, 0, squareSize, squareSize, hdc, 0, 0, SRCCOPY ); // make copy
\r
1804 BitBlt( hdc, 0, 1, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );
\r
1805 BitBlt( hdc, 2, 1, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );
\r
1806 BitBlt( hdc, 1, 0, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );
\r
1807 BitBlt( hdc, 1, 2, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );
\r
1810 DeleteObject( bm2 );
\r
1813 SetTextColor( hdc, 0 );
\r
1815 Step 5: some fonts have "disconnected" areas that are skipped by the fill:
\r
1816 draw the piece again in black for safety.
\r
1818 TextOut( hdc, pt.x, pt.y, &pieceToFontChar[appData.allWhite && index >= (int)BlackPawn ? index - (int)BlackPawn : index], 1 );
\r
1820 SelectObject( hdc, hbm_old );
\r
1822 if( hPieceMask[index] != NULL ) {
\r
1823 DeleteObject( hPieceMask[index] );
\r
1826 hPieceMask[index] = hbm;
\r
1829 hbm = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );
\r
1831 SelectObject( hdc, hbm );
\r
1834 HDC dc1 = CreateCompatibleDC( hdc_window );
\r
1835 HDC dc2 = CreateCompatibleDC( hdc_window );
\r
1836 HBITMAP bm2 = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );
\r
1838 SelectObject( dc1, hPieceMask[index] );
\r
1839 SelectObject( dc2, bm2 );
\r
1840 FillRect( dc2, &rc, GetStockObject(WHITE_BRUSH) );
\r
1841 BitBlt( dc2, 0, 0, squareSize, squareSize, dc1, 0, 0, SRCINVERT );
\r
1844 Now dc2 contains the inverse of the piece mask, i.e. a mask that preserves
\r
1845 the piece background and deletes (makes transparent) the rest.
\r
1846 Thanks to that mask, we are free to paint the background with the greates
\r
1847 freedom, as we'll be able to mask off the unwanted parts when finished.
\r
1848 We use this, to make gradients and give the pieces a "roundish" look.
\r
1850 SetPieceBackground( hdc, backColor, 2 );
\r
1851 BitBlt( hdc, 0, 0, squareSize, squareSize, dc2, 0, 0, SRCAND );
\r
1855 DeleteObject( bm2 );
\r
1858 SetTextColor( hdc, foreColor );
\r
1859 TextOut( hdc, pt.x, pt.y, &pieceToFontChar[appData.allWhite && index >= (int)BlackPawn ? index - (int)BlackPawn : index], 1 );
\r
1861 SelectObject( hdc, hbm_old );
\r
1863 if( hPieceFace[index] != NULL ) {
\r
1864 DeleteObject( hPieceFace[index] );
\r
1867 hPieceFace[index] = hbm;
\r
1870 static int TranslatePieceToFontPiece( int piece )
\r
1900 case BlackMarshall:
\r
1904 case BlackNightrider:
\r
1910 case BlackUnicorn:
\r
1914 case BlackGrasshopper:
\r
1926 case BlackCardinal:
\r
1933 case WhiteMarshall:
\r
1937 case WhiteNightrider:
\r
1943 case WhiteUnicorn:
\r
1947 case WhiteGrasshopper:
\r
1959 case WhiteCardinal:
\r
1968 void CreatePiecesFromFont()
\r
1971 HDC hdc_window = NULL;
\r
1977 if( fontBitmapSquareSize < 0 ) {
\r
1978 /* Something went seriously wrong in the past: do not try to recreate fonts! */
\r
1982 if( appData.renderPiecesWithFont == NULL || appData.renderPiecesWithFont[0] == NULLCHAR || appData.renderPiecesWithFont[0] == '*' ) {
\r
1983 fontBitmapSquareSize = -1;
\r
1987 if( fontBitmapSquareSize != squareSize ) {
\r
1988 hdc_window = GetDC( hwndMain );
\r
1989 hdc = CreateCompatibleDC( hdc_window );
\r
1991 if( hPieceFont != NULL ) {
\r
1992 DeleteObject( hPieceFont );
\r
1995 for( i=0; i<=(int)BlackKing; i++ ) {
\r
1996 hPieceMask[i] = NULL;
\r
1997 hPieceFace[i] = NULL;
\r
2003 if( appData.fontPieceSize >= 50 && appData.fontPieceSize <= 150 ) {
\r
2004 fontHeight = appData.fontPieceSize;
\r
2007 fontHeight = (fontHeight * squareSize) / 100;
\r
2009 lf.lfHeight = -MulDiv( fontHeight, GetDeviceCaps(hdc, LOGPIXELSY), 72 );
\r
2011 lf.lfEscapement = 0;
\r
2012 lf.lfOrientation = 0;
\r
2013 lf.lfWeight = FW_NORMAL;
\r
2015 lf.lfUnderline = 0;
\r
2016 lf.lfStrikeOut = 0;
\r
2017 lf.lfCharSet = DEFAULT_CHARSET;
\r
2018 lf.lfOutPrecision = OUT_DEFAULT_PRECIS;
\r
2019 lf.lfClipPrecision = CLIP_DEFAULT_PRECIS;
\r
2020 lf.lfQuality = PROOF_QUALITY;
\r
2021 lf.lfPitchAndFamily = DEFAULT_PITCH | FF_DONTCARE;
\r
2022 strncpy( lf.lfFaceName, appData.renderPiecesWithFont, sizeof(lf.lfFaceName) );
\r
2023 lf.lfFaceName[ sizeof(lf.lfFaceName) - 1 ] = '\0';
\r
2025 hPieceFont = CreateFontIndirect( &lf );
\r
2027 if( hPieceFont == NULL ) {
\r
2028 fontBitmapSquareSize = -2;
\r
2031 /* Setup font-to-piece character table */
\r
2032 if( ! SetCharTable(pieceToFontChar, appData.fontToPieceTable) ) {
\r
2033 /* No (or wrong) global settings, try to detect the font */
\r
2034 if( strstr(lf.lfFaceName,"Alpha") != NULL ) {
\r
2036 SetCharTable(pieceToFontChar, "phbrqkojntwl");
\r
2038 else if( strstr(lf.lfFaceName,"DiagramTT") != NULL ) {
\r
2039 /* DiagramTT* family */
\r
2040 SetCharTable(pieceToFontChar, "PNLRQKpnlrqk");
\r
2042 else if( strstr(lf.lfFaceName,"WinboardF") != NULL ) {
\r
2043 /* Fairy symbols */
\r
2044 SetCharTable(pieceToFontChar, "PNBRQFEACWMOHIJGDVSLUKpnbrqfeacwmohijgdvsluk");
\r
2046 else if( strstr(lf.lfFaceName,"GC2004D") != NULL ) {
\r
2047 /* Good Companion (Some characters get warped as literal :-( */
\r
2048 char s[] = "1cmWG0??S??oYI23wgQU";
\r
2049 s[0]=0xB9; s[1]=0xA9; s[6]=0xB1; s[11]=0xBB; s[12]=0xAB; s[17]=0xB3;
\r
2050 SetCharTable(pieceToFontChar, s);
\r
2053 /* Cases, Condal, Leipzig, Lucena, Marroquin, Merida, Usual */
\r
2054 SetCharTable(pieceToFontChar, "pnbrqkomvtwl");
\r
2058 /* Create bitmaps */
\r
2059 hfont_old = SelectObject( hdc, hPieceFont );
\r
2060 for(i=(int)WhitePawn; i<(int)EmptySquare; i++) /* [HGM] made a loop for this */
\r
2061 if(PieceToChar((ChessSquare)i) != '.') /* skip unused pieces */
\r
2062 CreatePieceMaskFromFont( hdc_window, hdc, i );
\r
2064 SelectObject( hdc, hfont_old );
\r
2066 fontBitmapSquareSize = squareSize;
\r
2070 if( hdc != NULL ) {
\r
2074 if( hdc_window != NULL ) {
\r
2075 ReleaseDC( hwndMain, hdc_window );
\r
2080 DoLoadBitmap(HINSTANCE hinst, char *piece, int squareSize, char *suffix)
\r
2084 snprintf(name, sizeof(name)/sizeof(name[0]), "%s%d%s", piece, squareSize, suffix);
\r
2085 if (gameInfo.event &&
\r
2086 strcmp(gameInfo.event, "Easter Egg Hunt") == 0 &&
\r
2087 strcmp(name, "k80s") == 0) {
\r
2088 safeStrCpy(name, "tim", sizeof(name)/sizeof(name[0]) );
\r
2090 return LoadBitmap(hinst, name);
\r
2094 /* Insert a color into the program's logical palette
\r
2095 structure. This code assumes the given color is
\r
2096 the result of the RGB or PALETTERGB macro, and it
\r
2097 knows how those macros work (which is documented).
\r
2100 InsertInPalette(COLORREF color)
\r
2102 LPPALETTEENTRY pe = &(pLogPal->palPalEntry[pLogPal->palNumEntries]);
\r
2104 if (pLogPal->palNumEntries++ >= PALETTESIZE) {
\r
2105 DisplayFatalError(_("Too many colors"), 0, 1);
\r
2106 pLogPal->palNumEntries--;
\r
2110 pe->peFlags = (char) 0;
\r
2111 pe->peRed = (char) (0xFF & color);
\r
2112 pe->peGreen = (char) (0xFF & (color >> 8));
\r
2113 pe->peBlue = (char) (0xFF & (color >> 16));
\r
2119 InitDrawingColors()
\r
2121 if (pLogPal == NULL) {
\r
2122 /* Allocate enough memory for a logical palette with
\r
2123 * PALETTESIZE entries and set the size and version fields
\r
2124 * of the logical palette structure.
\r
2126 pLogPal = (NPLOGPALETTE)
\r
2127 LocalAlloc(LMEM_FIXED, (sizeof(LOGPALETTE) +
\r
2128 (sizeof(PALETTEENTRY) * (PALETTESIZE))));
\r
2129 pLogPal->palVersion = 0x300;
\r
2131 pLogPal->palNumEntries = 0;
\r
2133 InsertInPalette(lightSquareColor);
\r
2134 InsertInPalette(darkSquareColor);
\r
2135 InsertInPalette(whitePieceColor);
\r
2136 InsertInPalette(blackPieceColor);
\r
2137 InsertInPalette(highlightSquareColor);
\r
2138 InsertInPalette(premoveHighlightColor);
\r
2140 /* create a logical color palette according the information
\r
2141 * in the LOGPALETTE structure.
\r
2143 hPal = CreatePalette((LPLOGPALETTE) pLogPal);
\r
2145 lightSquareBrush = CreateSolidBrush(lightSquareColor);
\r
2146 blackSquareBrush = CreateSolidBrush(blackPieceColor);
\r
2147 darkSquareBrush = CreateSolidBrush(darkSquareColor);
\r
2148 whitePieceBrush = CreateSolidBrush(whitePieceColor);
\r
2149 blackPieceBrush = CreateSolidBrush(blackPieceColor);
\r
2150 iconBkgndBrush = CreateSolidBrush(GetSysColor(COLOR_BACKGROUND));
\r
2151 explodeBrush = CreateSolidBrush(highlightSquareColor); // [HGM] atomic
\r
2152 markerBrush = CreateSolidBrush(premoveHighlightColor); // [HGM] markers
\r
2153 /* [AS] Force rendering of the font-based pieces */
\r
2154 if( fontBitmapSquareSize > 0 ) {
\r
2155 fontBitmapSquareSize = 0;
\r
2161 BoardWidth(int boardSize, int n)
\r
2162 { /* [HGM] argument n added to allow different width and height */
\r
2163 int lineGap = sizeInfo[boardSize].lineGap;
\r
2165 if( appData.overrideLineGap >= 0 && appData.overrideLineGap <= 5 ) {
\r
2166 lineGap = appData.overrideLineGap;
\r
2169 return (n + 1) * lineGap +
\r
2170 n * sizeInfo[boardSize].squareSize;
\r
2173 /* Respond to board resize by dragging edge */
\r
2175 ResizeBoard(int newSizeX, int newSizeY, int flags)
\r
2177 BoardSize newSize = NUM_SIZES - 1;
\r
2178 static int recurse = 0;
\r
2179 if (IsIconic(hwndMain)) return;
\r
2180 if (recurse > 0) return;
\r
2182 while (newSize > 0) {
\r
2183 InitDrawingSizes(newSize+1000, 0); // [HGM] kludge to update sizeInfo without visible effects
\r
2184 if(newSizeX >= sizeInfo[newSize].cliWidth &&
\r
2185 newSizeY >= sizeInfo[newSize].cliHeight) break;
\r
2188 boardSize = newSize;
\r
2189 InitDrawingSizes(boardSize, flags);
\r
2194 extern Boolean twoBoards, partnerUp; // [HGM] dual
\r
2197 InitDrawingSizes(BoardSize boardSize, int flags)
\r
2199 int i, boardWidth, boardHeight; /* [HGM] height treated separately */
\r
2200 ChessSquare piece;
\r
2201 static int oldBoardSize = -1, oldTinyLayout = 0;
\r
2203 SIZE clockSize, messageSize;
\r
2205 char buf[MSG_SIZ];
\r
2207 HMENU hmenu = GetMenu(hwndMain);
\r
2208 RECT crect, wrect, oldRect;
\r
2210 LOGBRUSH logbrush;
\r
2212 int suppressVisibleEffects = 0; // [HGM] kludge to request updating sizeInfo only
\r
2213 if((int)boardSize >= 1000 ) { boardSize -= 1000; suppressVisibleEffects = 1; }
\r
2215 /* [HGM] call with -2 uses old size (for if nr of files, ranks changes) */
\r
2216 if(boardSize == (BoardSize)(-2) ) boardSize = oldBoardSize;
\r
2218 oldRect.left = wpMain.x; //[HGM] placement: remember previous window params
\r
2219 oldRect.top = wpMain.y;
\r
2220 oldRect.right = wpMain.x + wpMain.width;
\r
2221 oldRect.bottom = wpMain.y + wpMain.height;
\r
2223 tinyLayout = sizeInfo[boardSize].tinyLayout;
\r
2224 smallLayout = sizeInfo[boardSize].smallLayout;
\r
2225 squareSize = sizeInfo[boardSize].squareSize;
\r
2226 lineGap = sizeInfo[boardSize].lineGap;
\r
2227 minorSize = 0; /* [HGM] Kludge to see if demagnified pieces need to be shifted */
\r
2229 if( appData.overrideLineGap >= 0 && appData.overrideLineGap <= 5 ) {
\r
2230 lineGap = appData.overrideLineGap;
\r
2233 if (tinyLayout != oldTinyLayout) {
\r
2234 long style = GetWindowLongPtr(hwndMain, GWL_STYLE);
\r
2236 style &= ~WS_SYSMENU;
\r
2237 InsertMenu(hmenu, IDM_Exit, MF_BYCOMMAND, IDM_Minimize,
\r
2238 "&Minimize\tCtrl+F4");
\r
2240 style |= WS_SYSMENU;
\r
2241 RemoveMenu(hmenu, IDM_Minimize, MF_BYCOMMAND);
\r
2243 SetWindowLongPtr(hwndMain, GWL_STYLE, style);
\r
2245 for (i=0; menuBarText[tinyLayout][i]; i++) {
\r
2246 ModifyMenu(hmenu, i, MF_STRING|MF_BYPOSITION|MF_POPUP,
\r
2247 (UINT)GetSubMenu(hmenu, i), T_(menuBarText[tinyLayout][i]));
\r
2249 DrawMenuBar(hwndMain);
\r
2252 boardWidth = BoardWidth(boardSize, BOARD_WIDTH);
\r
2253 boardHeight = BoardWidth(boardSize, BOARD_HEIGHT);
\r
2255 /* Get text area sizes */
\r
2256 hdc = GetDC(hwndMain);
\r
2257 if (appData.clockMode) {
\r
2258 snprintf(buf, MSG_SIZ, _("White: %s"), TimeString(23*60*60*1000L));
\r
2260 snprintf(buf, MSG_SIZ, _("White"));
\r
2262 oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);
\r
2263 GetTextExtentPoint(hdc, buf, strlen(buf), &clockSize);
\r
2264 SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);
\r
2265 str = _("We only care about the height here");
\r
2266 GetTextExtentPoint(hdc, str, strlen(str), &messageSize);
\r
2267 SelectObject(hdc, oldFont);
\r
2268 ReleaseDC(hwndMain, hdc);
\r
2270 /* Compute where everything goes */
\r
2271 if((first.programLogo || second.programLogo) && !tinyLayout) {
\r
2272 /* [HGM] logo: if either logo is on, reserve space for it */
\r
2273 logoHeight = 2*clockSize.cy;
\r
2274 leftLogoRect.left = OUTER_MARGIN;
\r
2275 leftLogoRect.right = leftLogoRect.left + 4*clockSize.cy;
\r
2276 leftLogoRect.top = OUTER_MARGIN;
\r
2277 leftLogoRect.bottom = OUTER_MARGIN + logoHeight;
\r
2279 rightLogoRect.right = OUTER_MARGIN + boardWidth;
\r
2280 rightLogoRect.left = rightLogoRect.right - 4*clockSize.cy;
\r
2281 rightLogoRect.top = OUTER_MARGIN;
\r
2282 rightLogoRect.bottom = OUTER_MARGIN + logoHeight;
\r
2285 whiteRect.left = leftLogoRect.right;
\r
2286 whiteRect.right = OUTER_MARGIN + boardWidth/2 - INNER_MARGIN/2;
\r
2287 whiteRect.top = OUTER_MARGIN;
\r
2288 whiteRect.bottom = whiteRect.top + logoHeight;
\r
2290 blackRect.right = rightLogoRect.left;
\r
2291 blackRect.left = whiteRect.right + INNER_MARGIN;
\r
2292 blackRect.top = whiteRect.top;
\r
2293 blackRect.bottom = whiteRect.bottom;
\r
2295 whiteRect.left = OUTER_MARGIN;
\r
2296 whiteRect.right = whiteRect.left + boardWidth/2 - INNER_MARGIN/2;
\r
2297 whiteRect.top = OUTER_MARGIN;
\r
2298 whiteRect.bottom = whiteRect.top + clockSize.cy;
\r
2300 blackRect.left = whiteRect.right + INNER_MARGIN;
\r
2301 blackRect.right = blackRect.left + boardWidth/2 - 1;
\r
2302 blackRect.top = whiteRect.top;
\r
2303 blackRect.bottom = whiteRect.bottom;
\r
2305 logoHeight = 0; // [HGM] logo: suppress logo after change to tiny layout!
\r
2308 messageRect.left = OUTER_MARGIN + MESSAGE_LINE_LEFTMARGIN;
\r
2309 if (appData.showButtonBar) {
\r
2310 messageRect.right = OUTER_MARGIN + boardWidth // [HGM] logo: expressed independent of clock placement
\r
2311 - N_BUTTONS*BUTTON_WIDTH - MESSAGE_LINE_LEFTMARGIN;
\r
2313 messageRect.right = OUTER_MARGIN + boardWidth;
\r
2315 messageRect.top = whiteRect.bottom + INNER_MARGIN;
\r
2316 messageRect.bottom = messageRect.top + messageSize.cy;
\r
2318 boardRect.left = OUTER_MARGIN;
\r
2319 boardRect.right = boardRect.left + boardWidth;
\r
2320 boardRect.top = messageRect.bottom + INNER_MARGIN;
\r
2321 boardRect.bottom = boardRect.top + boardHeight;
\r
2323 sizeInfo[boardSize].cliWidth = boardRect.right + OUTER_MARGIN;
\r
2324 sizeInfo[boardSize].cliHeight = boardRect.bottom + OUTER_MARGIN;
\r
2325 oldBoardSize = boardSize;
\r
2326 oldTinyLayout = tinyLayout;
\r
2327 winW = 2 * GetSystemMetrics(SM_CXFRAME) + boardRect.right + OUTER_MARGIN;
\r
2328 winH = 2 * GetSystemMetrics(SM_CYFRAME) + GetSystemMetrics(SM_CYMENU) +
\r
2329 GetSystemMetrics(SM_CYCAPTION) + boardRect.bottom + OUTER_MARGIN;
\r
2330 winW *= 1 + twoBoards;
\r
2331 if(suppressVisibleEffects) return; // [HGM] when called for filling sizeInfo only
\r
2332 wpMain.width = winW; // [HGM] placement: set through temporary which can used by initial sizing choice
\r
2333 wpMain.height = winH; // without disturbing window attachments
\r
2334 GetWindowRect(hwndMain, &wrect);
\r
2335 SetWindowPos(hwndMain, NULL, 0, 0, wpMain.width, wpMain.height,
\r
2336 SWP_NOCOPYBITS|SWP_NOZORDER|SWP_NOMOVE);
\r
2338 // [HGM] placement: let attached windows follow size change.
\r
2339 ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, moveHistoryDialog, &wpMoveHistory );
\r
2340 ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, evalGraphDialog, &wpEvalGraph );
\r
2341 ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, engineOutputDialog, &wpEngineOutput );
\r
2342 ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, gameListDialog, &wpGameList );
\r
2343 ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, hwndConsole, &wpConsole );
\r
2345 /* compensate if menu bar wrapped */
\r
2346 GetClientRect(hwndMain, &crect);
\r
2347 offby = boardRect.bottom + OUTER_MARGIN - crect.bottom;
\r
2348 wpMain.height += offby;
\r
2350 case WMSZ_TOPLEFT:
\r
2351 SetWindowPos(hwndMain, NULL,
\r
2352 wrect.right - wpMain.width, wrect.bottom - wpMain.height,
\r
2353 wpMain.width, wpMain.height, SWP_NOCOPYBITS|SWP_NOZORDER);
\r
2356 case WMSZ_TOPRIGHT:
\r
2358 SetWindowPos(hwndMain, NULL,
\r
2359 wrect.left, wrect.bottom - wpMain.height,
\r
2360 wpMain.width, wpMain.height, SWP_NOCOPYBITS|SWP_NOZORDER);
\r
2363 case WMSZ_BOTTOMLEFT:
\r
2365 SetWindowPos(hwndMain, NULL,
\r
2366 wrect.right - wpMain.width, wrect.top,
\r
2367 wpMain.width, wpMain.height, SWP_NOCOPYBITS|SWP_NOZORDER);
\r
2370 case WMSZ_BOTTOMRIGHT:
\r
2374 SetWindowPos(hwndMain, NULL, 0, 0, wpMain.width, wpMain.height,
\r
2375 SWP_NOCOPYBITS|SWP_NOZORDER|SWP_NOMOVE);
\r
2380 for (i = 0; i < N_BUTTONS; i++) {
\r
2381 if (buttonDesc[i].hwnd != NULL) {
\r
2382 DestroyWindow(buttonDesc[i].hwnd);
\r
2383 buttonDesc[i].hwnd = NULL;
\r
2385 if (appData.showButtonBar) {
\r
2386 buttonDesc[i].hwnd =
\r
2387 CreateWindow("BUTTON", buttonDesc[i].label,
\r
2388 WS_VISIBLE | WS_CHILD | BS_PUSHBUTTON,
\r
2389 boardRect.right - BUTTON_WIDTH*(N_BUTTONS-i),
\r
2390 messageRect.top, BUTTON_WIDTH, messageSize.cy, hwndMain,
\r
2391 (HMENU) buttonDesc[i].id,
\r
2392 (HINSTANCE) GetWindowLongPtr(hwndMain, GWLP_HINSTANCE), NULL);
\r
2394 SendMessage(buttonDesc[i].hwnd, WM_SETFONT,
\r
2395 (WPARAM)font[boardSize][MESSAGE_FONT]->hf,
\r
2396 MAKELPARAM(FALSE, 0));
\r
2398 if (buttonDesc[i].id == IDM_Pause)
\r
2399 hwndPause = buttonDesc[i].hwnd;
\r
2400 buttonDesc[i].wndproc = (WNDPROC)
\r
2401 SetWindowLongPtr(buttonDesc[i].hwnd, GWLP_WNDPROC, (LONG_PTR) ButtonProc);
\r
2404 if (gridPen != NULL) DeleteObject(gridPen);
\r
2405 if (highlightPen != NULL) DeleteObject(highlightPen);
\r
2406 if (premovePen != NULL) DeleteObject(premovePen);
\r
2407 if (lineGap != 0) {
\r
2408 logbrush.lbStyle = BS_SOLID;
\r
2409 logbrush.lbColor = RGB(0, 0, 0); /* grid pen color = black */
\r
2411 ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,
\r
2412 lineGap, &logbrush, 0, NULL);
\r
2413 logbrush.lbColor = highlightSquareColor;
\r
2415 ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,
\r
2416 lineGap, &logbrush, 0, NULL);
\r
2418 logbrush.lbColor = premoveHighlightColor;
\r
2420 ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,
\r
2421 lineGap, &logbrush, 0, NULL);
\r
2423 /* [HGM] Loop had to be split in part for vert. and hor. lines */
\r
2424 for (i = 0; i < BOARD_HEIGHT + 1; i++) {
\r
2425 gridEndpoints[i*2].x = boardRect.left + lineGap / 2;
\r
2426 gridEndpoints[i*2].y = gridEndpoints[i*2 + 1].y =
\r
2427 boardRect.top + lineGap / 2 + (i * (squareSize + lineGap));
\r
2428 gridEndpoints[i*2 + 1].x = boardRect.left + lineGap / 2 +
\r
2429 BOARD_WIDTH * (squareSize + lineGap);
\r
2430 gridVertexCounts[i*2] = gridVertexCounts[i*2 + 1] = 2;
\r
2432 for (i = 0; i < BOARD_WIDTH + 1; i++) {
\r
2433 gridEndpoints[i*2 + BOARD_HEIGHT*2 + 2].y = boardRect.top + lineGap / 2;
\r
2434 gridEndpoints[i*2 + BOARD_HEIGHT*2 + 2].x =
\r
2435 gridEndpoints[i*2 + 1 + BOARD_HEIGHT*2 + 2].x = boardRect.left +
\r
2436 lineGap / 2 + (i * (squareSize + lineGap));
\r
2437 gridEndpoints[i*2 + 1 + BOARD_HEIGHT*2 + 2].y =
\r
2438 boardRect.top + BOARD_HEIGHT * (squareSize + lineGap);
\r
2439 gridVertexCounts[i*2] = gridVertexCounts[i*2 + 1] = 2;
\r
2443 /* [HGM] Licensing requirement */
\r
2445 if(gameInfo.variant == VariantGothic) GothicPopUp( GOTHIC, VariantGothic); else
\r
2448 if(gameInfo.variant == VariantFalcon) GothicPopUp( FALCON, VariantFalcon); else
\r
2450 GothicPopUp( "", VariantNormal);
\r
2453 /* if (boardSize == oldBoardSize) return; [HGM] variant might have changed */
\r
2455 /* Load piece bitmaps for this board size */
\r
2456 for (i=0; i<=2; i++) {
\r
2457 for (piece = WhitePawn;
\r
2458 (int) piece < (int) BlackPawn;
\r
2459 piece = (ChessSquare) ((int) piece + 1)) {
\r
2460 if (pieceBitmap[i][piece] != NULL)
\r
2461 DeleteObject(pieceBitmap[i][piece]);
\r
2465 fontBitmapSquareSize = 0; /* [HGM] render: make sure pieces will be recreated, as we might need others now */
\r
2466 // Orthodox Chess pieces
\r
2467 pieceBitmap[0][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "s");
\r
2468 pieceBitmap[0][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "s");
\r
2469 pieceBitmap[0][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "s");
\r
2470 pieceBitmap[0][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "s");
\r
2471 pieceBitmap[0][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "s");
\r
2472 pieceBitmap[1][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "o");
\r
2473 pieceBitmap[1][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "o");
\r
2474 pieceBitmap[1][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "o");
\r
2475 pieceBitmap[1][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "o");
\r
2476 pieceBitmap[1][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "o");
\r
2477 pieceBitmap[2][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "w");
\r
2478 pieceBitmap[2][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "w");
\r
2479 pieceBitmap[2][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "w");
\r
2480 pieceBitmap[2][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "w");
\r
2481 pieceBitmap[2][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "w");
\r
2482 if( gameInfo.variant == VariantShogi && squareSize <= 72 && squareSize >= 33) {
\r
2483 // in Shogi, Hijack the unused Queen for Lance
\r
2484 pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "s");
\r
2485 pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "o");
\r
2486 pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "w");
\r
2488 pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "s");
\r
2489 pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "o");
\r
2490 pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "w");
\r
2493 if(squareSize <= 72 && squareSize >= 33) {
\r
2494 /* A & C are available in most sizes now */
\r
2495 if(squareSize != 49 && squareSize != 72 && squareSize != 33) { // Vortex-like
\r
2496 pieceBitmap[0][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "s");
\r
2497 pieceBitmap[1][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "o");
\r
2498 pieceBitmap[2][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "w");
\r
2499 pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "s");
\r
2500 pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "o");
\r
2501 pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "w");
\r
2502 pieceBitmap[0][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "s");
\r
2503 pieceBitmap[1][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "o");
\r
2504 pieceBitmap[2][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "w");
\r
2505 pieceBitmap[0][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "s");
\r
2506 pieceBitmap[1][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "o");
\r
2507 pieceBitmap[2][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "w");
\r
2508 } else { // Smirf-like
\r
2509 if(gameInfo.variant == VariantSChess) {
\r
2510 pieceBitmap[0][WhiteAngel] = DoLoadBitmap(hInst, "v", squareSize, "s");
\r
2511 pieceBitmap[1][WhiteAngel] = DoLoadBitmap(hInst, "v", squareSize, "o");
\r
2512 pieceBitmap[2][WhiteAngel] = DoLoadBitmap(hInst, "v", squareSize, "w");
\r
2514 pieceBitmap[0][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "s");
\r
2515 pieceBitmap[1][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "o");
\r
2516 pieceBitmap[2][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "w");
\r
2519 if(gameInfo.variant == VariantGothic) { // Vortex-like
\r
2520 pieceBitmap[0][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "s");
\r
2521 pieceBitmap[1][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "o");
\r
2522 pieceBitmap[2][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "w");
\r
2523 } else if(gameInfo.variant == VariantSChess && (squareSize == 49 || squareSize == 72)) {
\r
2524 pieceBitmap[0][WhiteMarshall] = DoLoadBitmap(hInst, "e", squareSize, "s");
\r
2525 pieceBitmap[1][WhiteMarshall] = DoLoadBitmap(hInst, "e", squareSize, "o");
\r
2526 pieceBitmap[2][WhiteMarshall] = DoLoadBitmap(hInst, "e", squareSize, "w");
\r
2527 } else { // WinBoard standard
\r
2528 pieceBitmap[0][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "s");
\r
2529 pieceBitmap[1][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "o");
\r
2530 pieceBitmap[2][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "w");
\r
2535 if(squareSize==72 || squareSize==49 || squareSize==33) { /* experiment with some home-made bitmaps */
\r
2536 pieceBitmap[0][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "s");
\r
2537 pieceBitmap[1][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "o");
\r
2538 pieceBitmap[2][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "w");
\r
2539 pieceBitmap[0][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "s");
\r
2540 pieceBitmap[1][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "o");
\r
2541 pieceBitmap[2][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "w");
\r
2542 pieceBitmap[0][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "s");
\r
2543 pieceBitmap[1][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "o");
\r
2544 pieceBitmap[2][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "w");
\r
2545 pieceBitmap[0][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "s");
\r
2546 pieceBitmap[1][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "o");
\r
2547 pieceBitmap[2][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "w");
\r
2548 pieceBitmap[0][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "s");
\r
2549 pieceBitmap[1][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "o");
\r
2550 pieceBitmap[2][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "w");
\r
2551 pieceBitmap[0][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "s");
\r
2552 pieceBitmap[1][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "o");
\r
2553 pieceBitmap[2][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "w");
\r
2554 pieceBitmap[0][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "s");
\r
2555 pieceBitmap[1][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "o");
\r
2556 pieceBitmap[2][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "w");
\r
2557 pieceBitmap[0][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "s");
\r
2558 pieceBitmap[1][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "o");
\r
2559 pieceBitmap[2][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "w");
\r
2560 pieceBitmap[0][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "s");
\r
2561 pieceBitmap[1][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "o");
\r
2562 pieceBitmap[2][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "w");
\r
2563 pieceBitmap[0][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "s");
\r
2564 pieceBitmap[1][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "o");
\r
2565 pieceBitmap[2][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "w");
\r
2567 if(gameInfo.variant == VariantShogi) { /* promoted Gold represemtations */
\r
2568 pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "wp", squareSize, "s");
\r
2569 pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "wp", squareSize, "o");
\r
2570 pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "w", squareSize, "w");
\r
2571 pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "wn", squareSize, "s");
\r
2572 pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "wn", squareSize, "o");
\r
2573 pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "w", squareSize, "w");
\r
2574 pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "ws", squareSize, "s");
\r
2575 pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "ws", squareSize, "o");
\r
2576 pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "w", squareSize, "w");
\r
2577 pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "wl", squareSize, "s");
\r
2578 pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "wl", squareSize, "o");
\r
2579 pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "w", squareSize, "w");
\r
2581 pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "s");
\r
2582 pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "o");
\r
2583 pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "w");
\r
2584 pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "s");
\r
2585 pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "o");
\r
2586 pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "w");
\r
2587 pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "s");
\r
2588 pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "o");
\r
2589 pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "w");
\r
2590 pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "s");
\r
2591 pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "o");
\r
2592 pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "w");
\r
2595 } else { /* other size, no special bitmaps available. Use smaller symbols */
\r
2596 if((int)boardSize < 2) minorSize = sizeInfo[0].squareSize;
\r
2597 else minorSize = sizeInfo[(int)boardSize - 2].squareSize;
\r
2598 pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "s");
\r
2599 pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "o");
\r
2600 pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "w");
\r
2601 pieceBitmap[0][WhiteCardinal] = DoLoadBitmap(hInst, "b", minorSize, "s");
\r
2602 pieceBitmap[1][WhiteCardinal] = DoLoadBitmap(hInst, "b", minorSize, "o");
\r
2603 pieceBitmap[2][WhiteCardinal] = DoLoadBitmap(hInst, "b", minorSize, "w");
\r
2604 pieceBitmap[0][WhiteDragon] = DoLoadBitmap(hInst, "r", minorSize, "s");
\r
2605 pieceBitmap[1][WhiteDragon] = DoLoadBitmap(hInst, "r", minorSize, "o");
\r
2606 pieceBitmap[2][WhiteDragon] = DoLoadBitmap(hInst, "r", minorSize, "w");
\r
2607 pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "s");
\r
2608 pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "o");
\r
2609 pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "w");
\r
2613 if(gameInfo.variant == VariantShogi && squareSize == 58)
\r
2614 /* special Shogi support in this size */
\r
2615 { for (i=0; i<=2; i++) { /* replace all bitmaps */
\r
2616 for (piece = WhitePawn;
\r
2617 (int) piece < (int) BlackPawn;
\r
2618 piece = (ChessSquare) ((int) piece + 1)) {
\r
2619 if (pieceBitmap[i][piece] != NULL)
\r
2620 DeleteObject(pieceBitmap[i][piece]);
\r
2623 pieceBitmap[0][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "o");
\r
2624 pieceBitmap[0][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "o");
\r
2625 pieceBitmap[0][WhiteBishop] = DoLoadBitmap(hInst, "sb", squareSize, "o");
\r
2626 pieceBitmap[0][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "o");
\r
2627 pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "o");
\r
2628 pieceBitmap[0][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "o");
\r
2629 pieceBitmap[0][WhiteFerz] = DoLoadBitmap(hInst, "sf", squareSize, "o");
\r
2630 pieceBitmap[0][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "o");
\r
2631 pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "su", squareSize, "o");
\r
2632 pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "sh", squareSize, "o");
\r
2633 pieceBitmap[0][WhiteCardinal] = DoLoadBitmap(hInst, "sa", squareSize, "o");
\r
2634 pieceBitmap[0][WhiteDragon] = DoLoadBitmap(hInst, "sc", squareSize, "o");
\r
2635 pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "sg", squareSize, "o");
\r
2636 pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "ss", squareSize, "o");
\r
2637 pieceBitmap[1][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "o");
\r
2638 pieceBitmap[1][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "o");
\r
2639 pieceBitmap[1][WhiteBishop] = DoLoadBitmap(hInst, "sb", squareSize, "o");
\r
2640 pieceBitmap[1][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "o");
\r
2641 pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "o");
\r
2642 pieceBitmap[1][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "o");
\r
2643 pieceBitmap[1][WhiteFerz] = DoLoadBitmap(hInst, "sf", squareSize, "o");
\r
2644 pieceBitmap[1][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "o");
\r
2645 pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "su", squareSize, "o");
\r
2646 pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "sh", squareSize, "o");
\r
2647 pieceBitmap[1][WhiteCardinal] = DoLoadBitmap(hInst, "sa", squareSize, "o");
\r
2648 pieceBitmap[1][WhiteDragon] = DoLoadBitmap(hInst, "sc", squareSize, "o");
\r
2649 pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "sg", squareSize, "o");
\r
2650 pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "ss", squareSize, "o");
\r
2651 pieceBitmap[2][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "w");
\r
2652 pieceBitmap[2][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "w");
\r
2653 pieceBitmap[2][WhiteBishop] = DoLoadBitmap(hInst, "sr", squareSize, "w");
\r
2654 pieceBitmap[2][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "w");
\r
2655 pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "w");
\r
2656 pieceBitmap[2][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "w");
\r
2657 pieceBitmap[2][WhiteFerz] = DoLoadBitmap(hInst, "sw", squareSize, "w");
\r
2658 pieceBitmap[2][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "w");
\r
2659 pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "sp", squareSize, "w");
\r
2660 pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "sn", squareSize, "w");
\r
2661 pieceBitmap[2][WhiteCardinal] = DoLoadBitmap(hInst, "sr", squareSize, "w");
\r
2662 pieceBitmap[2][WhiteDragon] = DoLoadBitmap(hInst, "sr", squareSize, "w");
\r
2663 pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "sl", squareSize, "w");
\r
2664 pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "sw", squareSize, "w");
\r
2670 PieceBitmap(ChessSquare p, int kind)
\r
2672 if ((int) p >= (int) BlackPawn)
\r
2673 p = (ChessSquare) ((int) p - (int) BlackPawn + (int) WhitePawn);
\r
2675 return pieceBitmap[kind][(int) p];
\r
2678 /***************************************************************/
\r
2680 #define MIN(a,b) (((a) < (b)) ? (a) : (b))
\r
2681 #define MAX(a,b) (((a) > (b)) ? (a) : (b))
\r
2683 #define MIN3(a,b,c) (((a) < (b) && (a) < (c)) ? (a) : (((b) < (a) && (b) < (c)) ? (b) : (c)))
\r
2684 #define MAX3(a,b,c) (((a) > (b) && (a) > (c)) ? (a) : (((b) > (a) && (b) > (c)) ? (b) : (c)))
\r
2688 SquareToPos(int row, int column, int * x, int * y)
\r
2691 *x = boardRect.left + lineGap + ((BOARD_WIDTH-1)-column) * (squareSize + lineGap);
\r
2692 *y = boardRect.top + lineGap + row * (squareSize + lineGap);
\r
2694 *x = boardRect.left + lineGap + column * (squareSize + lineGap);
\r
2695 *y = boardRect.top + lineGap + ((BOARD_HEIGHT-1)-row) * (squareSize + lineGap);
\r
2700 DrawCoordsOnDC(HDC hdc)
\r
2702 static char files[24] = {'0', '1','2','3','4','5','6','7','8','9','0','1','1','0','9','8','7','6','5','4','3','2','1','0'};
\r
2703 static char ranks[24] = {'l', 'k','j','i','h','g','f','e','d','c','b','a','a','b','c','d','e','f','g','h','i','j','k','l'};
\r
2704 char str[2] = { NULLCHAR, NULLCHAR };
\r
2705 int oldMode, oldAlign, x, y, start, i;
\r
2709 if (!appData.showCoords)
\r
2712 start = flipView ? 1-(ONE!='1') : 23+(ONE!='1')-BOARD_HEIGHT;
\r
2714 oldBrush = SelectObject(hdc, GetStockObject(BLACK_BRUSH));
\r
2715 oldMode = SetBkMode(hdc, (appData.monoMode ? OPAQUE : TRANSPARENT));
\r
2716 oldAlign = GetTextAlign(hdc);
\r
2717 oldFont = SelectObject(hdc, font[boardSize][COORD_FONT]->hf);
\r
2719 y = boardRect.top + lineGap;
\r
2720 x = boardRect.left + lineGap + gameInfo.holdingsWidth*(squareSize + lineGap);
\r
2722 SetTextAlign(hdc, TA_LEFT|TA_TOP);
\r
2723 for (i = 0; i < BOARD_HEIGHT; i++) {
\r
2724 str[0] = files[start + i];
\r
2725 ExtTextOut(hdc, x + 2, y + 1, 0, NULL, str, 1, NULL);
\r
2726 y += squareSize + lineGap;
\r
2729 start = flipView ? 12-(BOARD_RGHT-BOARD_LEFT) : 12;
\r
2731 SetTextAlign(hdc, TA_RIGHT|TA_BOTTOM);
\r
2732 for (i = 0; i < BOARD_RGHT - BOARD_LEFT; i++) {
\r
2733 str[0] = ranks[start + i];
\r
2734 ExtTextOut(hdc, x + squareSize - 2, y - 1, 0, NULL, str, 1, NULL);
\r
2735 x += squareSize + lineGap;
\r
2738 SelectObject(hdc, oldBrush);
\r
2739 SetBkMode(hdc, oldMode);
\r
2740 SetTextAlign(hdc, oldAlign);
\r
2741 SelectObject(hdc, oldFont);
\r
2745 DrawGridOnDC(HDC hdc)
\r
2749 if (lineGap != 0) {
\r
2750 oldPen = SelectObject(hdc, gridPen);
\r
2751 PolyPolyline(hdc, gridEndpoints, gridVertexCounts, BOARD_WIDTH+BOARD_HEIGHT + 2);
\r
2752 SelectObject(hdc, oldPen);
\r
2756 #define HIGHLIGHT_PEN 0
\r
2757 #define PREMOVE_PEN 1
\r
2760 DrawHighlightOnDC(HDC hdc, BOOLEAN on, int x, int y, int pen)
\r
2763 HPEN oldPen, hPen;
\r
2764 if (lineGap == 0) return;
\r
2766 x1 = boardRect.left +
\r
2767 lineGap/2 + ((BOARD_WIDTH-1)-x) * (squareSize + lineGap);
\r
2768 y1 = boardRect.top +
\r
2769 lineGap/2 + y * (squareSize + lineGap);
\r
2771 x1 = boardRect.left +
\r
2772 lineGap/2 + x * (squareSize + lineGap);
\r
2773 y1 = boardRect.top +
\r
2774 lineGap/2 + ((BOARD_HEIGHT-1)-y) * (squareSize + lineGap);
\r
2776 hPen = pen ? premovePen : highlightPen;
\r
2777 oldPen = SelectObject(hdc, on ? hPen : gridPen);
\r
2778 MoveToEx(hdc, x1, y1, NULL);
\r
2779 LineTo(hdc, x1 + squareSize + lineGap, y1);
\r
2780 LineTo(hdc, x1 + squareSize + lineGap, y1 + squareSize + lineGap);
\r
2781 LineTo(hdc, x1, y1 + squareSize + lineGap);
\r
2782 LineTo(hdc, x1, y1);
\r
2783 SelectObject(hdc, oldPen);
\r
2787 DrawHighlightsOnDC(HDC hdc, HighlightInfo *h, int pen)
\r
2790 for (i=0; i<2; i++) {
\r
2791 if (h->sq[i].x >= 0 && h->sq[i].y >= 0)
\r
2792 DrawHighlightOnDC(hdc, TRUE,
\r
2793 h->sq[i].x, h->sq[i].y,
\r
2798 /* Note: sqcolor is used only in monoMode */
\r
2799 /* Note that this code is largely duplicated in woptions.c,
\r
2800 function DrawSampleSquare, so that needs to be updated too */
\r
2802 DrawPieceOnDC(HDC hdc, ChessSquare piece, int color, int sqcolor, int x, int y, HDC tmphdc)
\r
2804 HBITMAP oldBitmap;
\r
2808 if (appData.blindfold) return;
\r
2810 /* [AS] Use font-based pieces if needed */
\r
2811 if( fontBitmapSquareSize >= 0 && (squareSize > 32 || gameInfo.variant >= VariantShogi)) {
\r
2812 /* Create piece bitmaps, or do nothing if piece set is up to date */
\r
2813 CreatePiecesFromFont();
\r
2815 if( fontBitmapSquareSize == squareSize ) {
\r
2816 int index = TranslatePieceToFontPiece(piece);
\r
2818 SelectObject( tmphdc, hPieceMask[ index ] );
\r
2820 if(appData.upsideDown ? color==flipView : (flipView && gameInfo.variant == VariantShogi))
\r
2821 StretchBlt(hdc, x+squareSize, y+squareSize, -squareSize, -squareSize, tmphdc, 0, 0, squareSize, squareSize, SRCAND);
\r
2825 squareSize, squareSize,
\r
2830 SelectObject( tmphdc, hPieceFace[ index ] );
\r
2832 if(appData.upsideDown ? color==flipView : (flipView && gameInfo.variant == VariantShogi))
\r
2833 StretchBlt(hdc, x+squareSize, y+squareSize, -squareSize, -squareSize, tmphdc, 0, 0, squareSize, squareSize, SRCPAINT);
\r
2837 squareSize, squareSize,
\r
2846 if (appData.monoMode) {
\r
2847 SelectObject(tmphdc, PieceBitmap(piece,
\r
2848 color == sqcolor ? OUTLINE_PIECE : SOLID_PIECE));
\r
2849 BitBlt(hdc, x, y, squareSize, squareSize, tmphdc, 0, 0,
\r
2850 sqcolor ? SRCCOPY : NOTSRCCOPY);
\r
2852 tmpSize = squareSize;
\r
2854 ((piece >= (int)WhiteNightrider && piece <= WhiteGrasshopper) ||
\r
2855 (piece >= (int)BlackNightrider && piece <= BlackGrasshopper)) ) {
\r
2856 /* [HGM] no bitmap available for promoted pieces in Crazyhouse */
\r
2857 /* Bitmaps of smaller size are substituted, but we have to align them */
\r
2858 x += (squareSize - minorSize)>>1;
\r
2859 y += squareSize - minorSize - 2;
\r
2860 tmpSize = minorSize;
\r
2862 if (color || appData.allWhite ) {
\r
2863 oldBitmap = SelectObject(tmphdc, PieceBitmap(piece, WHITE_PIECE));
\r
2865 oldBrush = SelectObject(hdc, whitePieceBrush);
\r
2866 else oldBrush = SelectObject(hdc, blackPieceBrush);
\r
2867 if(appData.upsideDown && color==flipView)
\r
2868 StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, 0x00B8074A);
\r
2870 BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, 0x00B8074A);
\r
2871 /* Use black for outline of white pieces */
\r
2872 SelectObject(tmphdc, PieceBitmap(piece, OUTLINE_PIECE));
\r
2873 if(appData.upsideDown && color==flipView)
\r
2874 StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, SRCAND);
\r
2876 BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, SRCAND);
\r
2878 /* Use square color for details of black pieces */
\r
2879 oldBitmap = SelectObject(tmphdc, PieceBitmap(piece, SOLID_PIECE));
\r
2880 oldBrush = SelectObject(hdc, blackPieceBrush);
\r
2881 if(appData.upsideDown && !flipView)
\r
2882 StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, 0x00B8074A);
\r
2884 BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, 0x00B8074A);
\r
2886 SelectObject(hdc, oldBrush);
\r
2887 SelectObject(tmphdc, oldBitmap);
\r
2891 /* [AS] Compute a drawing mode for a square, based on specified settings (see DrawTile) */
\r
2892 int GetBackTextureMode( int algo )
\r
2894 int result = BACK_TEXTURE_MODE_DISABLED;
\r
2898 case BACK_TEXTURE_MODE_PLAIN:
\r
2899 result = 1; /* Always use identity map */
\r
2901 case BACK_TEXTURE_MODE_FULL_RANDOM:
\r
2902 result = 1 + (myrandom() % 3); /* Pick a transformation at random */
\r
2910 [AS] Compute and save texture drawing info, otherwise we may not be able
\r
2911 to handle redraws cleanly (as random numbers would always be different).
\r
2913 VOID RebuildTextureSquareInfo()
\r
2923 ZeroMemory( &backTextureSquareInfo, sizeof(backTextureSquareInfo) );
\r
2925 if( liteBackTexture != NULL ) {
\r
2926 if( GetObject( liteBackTexture, sizeof(bi), &bi ) > 0 ) {
\r
2927 lite_w = bi.bmWidth;
\r
2928 lite_h = bi.bmHeight;
\r
2932 if( darkBackTexture != NULL ) {
\r
2933 if( GetObject( darkBackTexture, sizeof(bi), &bi ) > 0 ) {
\r
2934 dark_w = bi.bmWidth;
\r
2935 dark_h = bi.bmHeight;
\r
2939 for( row=0; row<BOARD_HEIGHT; row++ ) {
\r
2940 for( col=0; col<BOARD_WIDTH; col++ ) {
\r
2941 if( (col + row) & 1 ) {
\r
2943 if( lite_w >= squareSize && lite_h >= squareSize ) {
\r
2944 if( lite_w >= squareSize*BOARD_WIDTH )
\r
2945 backTextureSquareInfo[row][col].x = (2*col+1)*lite_w/(2*BOARD_WIDTH) - squareSize/2; /* [HGM] cut out of center of virtual square */
\r
2947 backTextureSquareInfo[row][col].x = col * (lite_w - squareSize) / (BOARD_WIDTH-1); /* [HGM] divide by size-1 in stead of size! */
\r
2948 if( lite_h >= squareSize*BOARD_HEIGHT )
\r
2949 backTextureSquareInfo[row][col].y = (2*(BOARD_HEIGHT-row)-1)*lite_h/(2*BOARD_HEIGHT) - squareSize/2;
\r
2951 backTextureSquareInfo[row][col].y = (BOARD_HEIGHT-1-row) * (lite_h - squareSize) / (BOARD_HEIGHT-1);
\r
2952 backTextureSquareInfo[row][col].mode = GetBackTextureMode(liteBackTextureMode);
\r
2957 if( dark_w >= squareSize && dark_h >= squareSize ) {
\r
2958 if( dark_w >= squareSize*BOARD_WIDTH )
\r
2959 backTextureSquareInfo[row][col].x = (2*col+1) * dark_w / (2*BOARD_WIDTH) - squareSize/2;
\r
2961 backTextureSquareInfo[row][col].x = col * (dark_w - squareSize) / (BOARD_WIDTH-1);
\r
2962 if( dark_h >= squareSize*BOARD_HEIGHT )
\r
2963 backTextureSquareInfo[row][col].y = (2*(BOARD_HEIGHT-row)-1) * dark_h / (2*BOARD_HEIGHT) - squareSize/2;
\r
2965 backTextureSquareInfo[row][col].y = (BOARD_HEIGHT-1-row) * (dark_h - squareSize) / (BOARD_HEIGHT-1);
\r
2966 backTextureSquareInfo[row][col].mode = GetBackTextureMode(darkBackTextureMode);
\r
2973 /* [AS] Arrow highlighting support */
\r
2975 static double A_WIDTH = 5; /* Width of arrow body */
\r
2977 #define A_HEIGHT_FACTOR 6 /* Length of arrow "point", relative to body width */
\r
2978 #define A_WIDTH_FACTOR 3 /* Width of arrow "point", relative to body width */
\r
2980 static double Sqr( double x )
\r
2985 static int Round( double x )
\r
2987 return (int) (x + 0.5);
\r
2990 /* Draw an arrow between two points using current settings */
\r
2991 VOID DrawArrowBetweenPoints( HDC hdc, int s_x, int s_y, int d_x, int d_y )
\r
2994 double dx, dy, j, k, x, y;
\r
2996 if( d_x == s_x ) {
\r
2997 int h = (d_y > s_y) ? +A_WIDTH*A_HEIGHT_FACTOR : -A_WIDTH*A_HEIGHT_FACTOR;
\r
2999 arrow[0].x = s_x + A_WIDTH + 0.5;
\r
3002 arrow[1].x = s_x + A_WIDTH + 0.5;
\r
3003 arrow[1].y = d_y - h;
\r
3005 arrow[2].x = arrow[1].x + A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;
\r
3006 arrow[2].y = d_y - h;
\r
3011 arrow[5].x = arrow[1].x - 2*A_WIDTH + 0.5;
\r
3012 arrow[5].y = d_y - h;
\r
3014 arrow[4].x = arrow[5].x - A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;
\r
3015 arrow[4].y = d_y - h;
\r
3017 arrow[6].x = arrow[1].x - 2*A_WIDTH + 0.5;
\r
3020 else if( d_y == s_y ) {
\r
3021 int w = (d_x > s_x) ? +A_WIDTH*A_HEIGHT_FACTOR : -A_WIDTH*A_HEIGHT_FACTOR;
\r
3024 arrow[0].y = s_y + A_WIDTH + 0.5;
\r
3026 arrow[1].x = d_x - w;
\r
3027 arrow[1].y = s_y + A_WIDTH + 0.5;
\r
3029 arrow[2].x = d_x - w;
\r
3030 arrow[2].y = arrow[1].y + A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;
\r
3035 arrow[5].x = d_x - w;
\r
3036 arrow[5].y = arrow[1].y - 2*A_WIDTH + 0.5;
\r
3038 arrow[4].x = d_x - w;
\r
3039 arrow[4].y = arrow[5].y - A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;
\r
3042 arrow[6].y = arrow[1].y - 2*A_WIDTH + 0.5;
\r
3045 /* [AS] Needed a lot of paper for this! :-) */
\r
3046 dy = (double) (d_y - s_y) / (double) (d_x - s_x);
\r
3047 dx = (double) (s_x - d_x) / (double) (s_y - d_y);
\r
3049 j = sqrt( Sqr(A_WIDTH) / (1.0 + Sqr(dx)) );
\r
3051 k = sqrt( Sqr(A_WIDTH*A_HEIGHT_FACTOR) / (1.0 + Sqr(dy)) );
\r
3056 arrow[0].x = Round(x - j);
\r
3057 arrow[0].y = Round(y + j*dx);
\r
3059 arrow[1].x = Round(arrow[0].x + 2*j); // [HGM] prevent width to be affected by rounding twice
\r
3060 arrow[1].y = Round(arrow[0].y - 2*j*dx);
\r
3063 x = (double) d_x - k;
\r
3064 y = (double) d_y - k*dy;
\r
3067 x = (double) d_x + k;
\r
3068 y = (double) d_y + k*dy;
\r
3071 x = Round(x); y = Round(y); // [HGM] make sure width of shaft is rounded the same way on both ends
\r
3073 arrow[6].x = Round(x - j);
\r
3074 arrow[6].y = Round(y + j*dx);
\r
3076 arrow[2].x = Round(arrow[6].x + 2*j);
\r
3077 arrow[2].y = Round(arrow[6].y - 2*j*dx);
\r
3079 arrow[3].x = Round(arrow[2].x + j*(A_WIDTH_FACTOR-1));
\r
3080 arrow[3].y = Round(arrow[2].y - j*(A_WIDTH_FACTOR-1)*dx);
\r
3085 arrow[5].x = Round(arrow[6].x - j*(A_WIDTH_FACTOR-1));
\r
3086 arrow[5].y = Round(arrow[6].y + j*(A_WIDTH_FACTOR-1)*dx);
\r
3089 Polygon( hdc, arrow, 7 );
\r
3092 /* [AS] Draw an arrow between two squares */
\r
3093 VOID DrawArrowBetweenSquares( HDC hdc, int s_col, int s_row, int d_col, int d_row )
\r
3095 int s_x, s_y, d_x, d_y;
\r
3102 if( s_col == d_col && s_row == d_row ) {
\r
3106 /* Get source and destination points */
\r
3107 SquareToPos( s_row, s_col, &s_x, &s_y);
\r
3108 SquareToPos( d_row, d_col, &d_x, &d_y);
\r
3111 d_y += squareSize / 2 - squareSize / 4; // [HGM] round towards same centers on all sides!
\r
3113 else if( d_y < s_y ) {
\r
3114 d_y += squareSize / 2 + squareSize / 4;
\r
3117 d_y += squareSize / 2;
\r
3121 d_x += squareSize / 2 - squareSize / 4;
\r
3123 else if( d_x < s_x ) {
\r
3124 d_x += squareSize / 2 + squareSize / 4;
\r
3127 d_x += squareSize / 2;
\r
3130 s_x += squareSize / 2;
\r
3131 s_y += squareSize / 2;
\r
3133 /* Adjust width */
\r
3134 A_WIDTH = squareSize / 14.; //[HGM] make float
\r
3137 stLB.lbStyle = BS_SOLID;
\r
3138 stLB.lbColor = appData.highlightArrowColor;
\r
3141 hpen = CreatePen( PS_SOLID, 2, RGB(0x00,0x00,0x00) );
\r
3142 holdpen = SelectObject( hdc, hpen );
\r
3143 hbrush = CreateBrushIndirect( &stLB );
\r
3144 holdbrush = SelectObject( hdc, hbrush );
\r
3146 DrawArrowBetweenPoints( hdc, s_x, s_y, d_x, d_y );
\r
3148 SelectObject( hdc, holdpen );
\r
3149 SelectObject( hdc, holdbrush );
\r
3150 DeleteObject( hpen );
\r
3151 DeleteObject( hbrush );
\r
3154 BOOL HasHighlightInfo()
\r
3156 BOOL result = FALSE;
\r
3158 if( highlightInfo.sq[0].x >= 0 && highlightInfo.sq[0].y >= 0 &&
\r
3159 highlightInfo.sq[1].x >= 0 && highlightInfo.sq[1].y >= 0 )
\r
3167 BOOL IsDrawArrowEnabled()
\r
3169 BOOL result = FALSE;
\r
3171 if( appData.highlightMoveWithArrow && squareSize >= 32 ) {
\r
3178 VOID DrawArrowHighlight( HDC hdc )
\r
3180 if( IsDrawArrowEnabled() && HasHighlightInfo() ) {
\r
3181 DrawArrowBetweenSquares( hdc,
\r
3182 highlightInfo.sq[0].x, highlightInfo.sq[0].y,
\r
3183 highlightInfo.sq[1].x, highlightInfo.sq[1].y );
\r
3187 HRGN GetArrowHighlightClipRegion( HDC hdc )
\r
3189 HRGN result = NULL;
\r
3191 if( HasHighlightInfo() ) {
\r
3192 int x1, y1, x2, y2;
\r
3193 int sx, sy, dx, dy;
\r
3195 SquareToPos(highlightInfo.sq[0].y, highlightInfo.sq[0].x, &x1, &y1 );
\r
3196 SquareToPos(highlightInfo.sq[1].y, highlightInfo.sq[1].x, &x2, &y2 );
\r
3198 sx = MIN( x1, x2 );
\r
3199 sy = MIN( y1, y2 );
\r
3200 dx = MAX( x1, x2 ) + squareSize;
\r
3201 dy = MAX( y1, y2 ) + squareSize;
\r
3203 result = CreateRectRgn( sx, sy, dx, dy );
\r
3210 Warning: this function modifies the behavior of several other functions.
\r
3212 Basically, Winboard is optimized to avoid drawing the whole board if not strictly
\r
3213 needed. Unfortunately, the decision whether or not to perform a full or partial
\r
3214 repaint is scattered all over the place, which is not good for features such as
\r
3215 "arrow highlighting" that require a full repaint of the board.
\r
3217 So, I've tried to patch the code where I thought it made sense (e.g. after or during
\r
3218 user interaction, when speed is not so important) but especially to avoid errors
\r
3219 in the displayed graphics.
\r
3221 In such patched places, I always try refer to this function so there is a single
\r
3222 place to maintain knowledge.
\r
3224 To restore the original behavior, just return FALSE unconditionally.
\r
3226 BOOL IsFullRepaintPreferrable()
\r
3228 BOOL result = FALSE;
\r
3230 if( (appData.highlightLastMove || appData.highlightDragging) && IsDrawArrowEnabled() ) {
\r
3231 /* Arrow may appear on the board */
\r
3239 This function is called by DrawPosition to know whether a full repaint must
\r
3242 Only DrawPosition may directly call this function, which makes use of
\r
3243 some state information. Other function should call DrawPosition specifying
\r
3244 the repaint flag, and can use IsFullRepaintPreferrable if needed.
\r
3246 BOOL DrawPositionNeedsFullRepaint()
\r
3248 BOOL result = FALSE;
\r
3251 Probably a slightly better policy would be to trigger a full repaint
\r
3252 when animInfo.piece changes state (i.e. empty -> non-empty and viceversa),
\r
3253 but animation is fast enough that it's difficult to notice.
\r
3255 if( animInfo.piece == EmptySquare ) {
\r
3256 if( (appData.highlightLastMove || appData.highlightDragging) && IsDrawArrowEnabled() /*&& HasHighlightInfo()*/ ) {
\r
3265 DrawBoardOnDC(HDC hdc, Board board, HDC tmphdc)
\r
3267 int row, column, x, y, square_color, piece_color;
\r
3268 ChessSquare piece;
\r
3270 HDC texture_hdc = NULL;
\r
3272 /* [AS] Initialize background textures if needed */
\r
3273 if( liteBackTexture != NULL || darkBackTexture != NULL ) {
\r
3274 static int backTextureBoardSize; /* [HGM] boardsize: also new texture if board format changed */
\r
3275 if( backTextureSquareSize != squareSize
\r
3276 || backTextureBoardSize != BOARD_WIDTH+BOARD_FILES*BOARD_HEIGHT) {
\r
3277 backTextureBoardSize = BOARD_WIDTH+BOARD_FILES*BOARD_HEIGHT;
\r
3278 backTextureSquareSize = squareSize;
\r
3279 RebuildTextureSquareInfo();
\r
3282 texture_hdc = CreateCompatibleDC( hdc );
\r
3285 for (row = 0; row < BOARD_HEIGHT; row++) {
\r
3286 for (column = 0; column < BOARD_WIDTH; column++) {
\r
3288 SquareToPos(row, column, &x, &y);
\r
3290 piece = board[row][column];
\r
3292 square_color = ((column + row) % 2) == 1;
\r
3293 if( gameInfo.variant == VariantXiangqi ) {
\r
3294 square_color = !InPalace(row, column);
\r
3295 if(BOARD_HEIGHT&1) { if(row==BOARD_HEIGHT/2) square_color ^= 1; }
\r
3296 else if(row < BOARD_HEIGHT/2) square_color ^= 1;
\r
3298 piece_color = (int) piece < (int) BlackPawn;
\r
3301 /* [HGM] holdings file: light square or black */
\r
3302 if(column == BOARD_LEFT-2) {
\r
3303 if( row > BOARD_HEIGHT - gameInfo.holdingsSize - 1 )
\r
3306 DisplayHoldingsCount(hdc, x, y, 0, 0); /* black out */
\r
3310 if(column == BOARD_RGHT + 1 ) {
\r
3311 if( row < gameInfo.holdingsSize )
\r
3314 DisplayHoldingsCount(hdc, x, y, 0, 0);
\r
3318 if(column == BOARD_LEFT-1 ) /* left align */
\r
3319 DisplayHoldingsCount(hdc, x, y, flipView, (int) board[row][column]);
\r
3320 else if( column == BOARD_RGHT) /* right align */
\r
3321 DisplayHoldingsCount(hdc, x, y, !flipView, (int) board[row][column]);
\r
3323 if (appData.monoMode) {
\r
3324 if (piece == EmptySquare) {
\r
3325 BitBlt(hdc, x, y, squareSize, squareSize, 0, 0, 0,
\r
3326 square_color ? WHITENESS : BLACKNESS);
\r
3328 DrawPieceOnDC(hdc, piece, piece_color, square_color, x, y, tmphdc);
\r
3331 else if( backTextureSquareInfo[row][column].mode > 0 ) {
\r
3332 /* [AS] Draw the square using a texture bitmap */
\r
3333 HBITMAP hbm = SelectObject( texture_hdc, square_color ? liteBackTexture : darkBackTexture );
\r
3334 int r = row, c = column; // [HGM] do not flip board in flipView
\r
3335 if(flipView) { r = BOARD_HEIGHT-1 - r; c = BOARD_WIDTH-1 - c; }
\r
3338 squareSize, squareSize,
\r
3341 backTextureSquareInfo[r][c].mode,
\r
3342 backTextureSquareInfo[r][c].x,
\r
3343 backTextureSquareInfo[r][c].y );
\r
3345 SelectObject( texture_hdc, hbm );
\r
3347 if (piece != EmptySquare) {
\r
3348 DrawPieceOnDC(hdc, piece, piece_color, -1, x, y, tmphdc);
\r
3352 HBRUSH brush = square_color ? lightSquareBrush : darkSquareBrush;
\r
3354 oldBrush = SelectObject(hdc, brush );
\r
3355 BitBlt(hdc, x, y, squareSize, squareSize, 0, 0, 0, PATCOPY);
\r
3356 SelectObject(hdc, oldBrush);
\r
3357 if (piece != EmptySquare)
\r
3358 DrawPieceOnDC(hdc, piece, piece_color, -1, x, y, tmphdc);
\r
3363 if( texture_hdc != NULL ) {
\r
3364 DeleteDC( texture_hdc );
\r
3368 int saveDiagFlag = 0; FILE *diagFile; // [HGM] diag
\r
3369 void fputDW(FILE *f, int x)
\r
3371 fputc(x & 255, f);
\r
3372 fputc(x>>8 & 255, f);
\r
3373 fputc(x>>16 & 255, f);
\r
3374 fputc(x>>24 & 255, f);
\r
3377 #define MAX_CLIPS 200 /* more than enough */
\r
3380 DrawLogoOnDC(HDC hdc, RECT logoRect, HBITMAP logo)
\r
3382 // HBITMAP bufferBitmap;
\r
3387 int w = 100, h = 50;
\r
3389 if(logo == NULL) return;
\r
3390 // GetClientRect(hwndMain, &Rect);
\r
3391 // bufferBitmap = CreateCompatibleBitmap(hdc, Rect.right-Rect.left+1,
\r
3392 // Rect.bottom-Rect.top+1);
\r
3393 tmphdc = CreateCompatibleDC(hdc);
\r
3394 hbm = SelectObject(tmphdc, logo);
\r
3395 if( GetObject( logo, sizeof(bi), &bi ) > 0 ) {
\r
3399 StretchBlt(hdc, logoRect.left, logoRect.top, logoRect.right - logoRect.left,
\r
3400 logoRect.bottom - logoRect.top, tmphdc, 0, 0, w, h, SRCCOPY);
\r
3401 SelectObject(tmphdc, hbm);
\r
3409 HDC hdc = GetDC(hwndMain);
\r
3410 HBITMAP whiteLogo = (HBITMAP) first.programLogo, blackLogo = (HBITMAP) second.programLogo;
\r
3411 if(appData.autoLogo) {
\r
3413 switch(gameMode) { // pick logos based on game mode
\r
3414 case IcsObserving:
\r
3415 whiteLogo = second.programLogo; // ICS logo
\r
3416 blackLogo = second.programLogo;
\r
3419 case IcsPlayingWhite:
\r
3420 if(!appData.zippyPlay) whiteLogo = userLogo;
\r
3421 blackLogo = second.programLogo; // ICS logo
\r
3423 case IcsPlayingBlack:
\r
3424 whiteLogo = second.programLogo; // ICS logo
\r
3425 blackLogo = appData.zippyPlay ? first.programLogo : userLogo;
\r
3427 case TwoMachinesPlay:
\r
3428 if(first.twoMachinesColor[0] == 'b') {
\r
3429 whiteLogo = second.programLogo;
\r
3430 blackLogo = first.programLogo;
\r
3433 case MachinePlaysWhite:
\r
3434 blackLogo = userLogo;
\r
3436 case MachinePlaysBlack:
\r
3437 whiteLogo = userLogo;
\r
3438 blackLogo = first.programLogo;
\r
3441 DrawLogoOnDC(hdc, leftLogoRect, flipClock ? blackLogo : whiteLogo);
\r
3442 DrawLogoOnDC(hdc, rightLogoRect, flipClock ? whiteLogo : blackLogo);
\r
3443 ReleaseDC(hwndMain, hdc);
\r
3447 static HDC hdcSeek;
\r
3449 // [HGM] seekgraph
\r
3450 void DrawSeekAxis( int x, int y, int xTo, int yTo )
\r
3453 HPEN hp = SelectObject( hdcSeek, gridPen );
\r
3454 MoveToEx( hdcSeek, boardRect.left+x, boardRect.top+y, &stPt );
\r
3455 LineTo( hdcSeek, boardRect.left+xTo, boardRect.top+yTo );
\r
3456 SelectObject( hdcSeek, hp );
\r
3459 // front-end wrapper for drawing functions to do rectangles
\r
3460 void DrawSeekBackground( int left, int top, int right, int bottom )
\r
3465 if (hdcSeek == NULL) {
\r
3466 hdcSeek = GetDC(hwndMain);
\r
3467 if (!appData.monoMode) {
\r
3468 SelectPalette(hdcSeek, hPal, FALSE);
\r
3469 RealizePalette(hdcSeek);
\r
3472 hp = SelectObject( hdcSeek, gridPen );
\r
3473 rc.top = boardRect.top+top; rc.left = boardRect.left+left;
\r
3474 rc.bottom = boardRect.top+bottom; rc.right = boardRect.left+right;
\r
3475 FillRect( hdcSeek, &rc, lightSquareBrush );
\r
3476 SelectObject( hdcSeek, hp );
\r
3479 // front-end wrapper for putting text in graph
\r
3480 void DrawSeekText(char *buf, int x, int y)
\r
3483 SetBkMode( hdcSeek, TRANSPARENT );
\r
3484 GetTextExtentPoint32( hdcSeek, buf, strlen(buf), &stSize );
\r
3485 TextOut( hdcSeek, boardRect.left+x-3, boardRect.top+y-stSize.cy/2, buf, strlen(buf) );
\r
3488 void DrawSeekDot(int x, int y, int color)
\r
3490 int square = color & 0x80;
\r
3491 HBRUSH oldBrush = SelectObject(hdcSeek,
\r
3492 color == 0 ? markerBrush : color == 1 ? darkSquareBrush : explodeBrush);
\r
3495 Rectangle(hdcSeek, boardRect.left+x - squareSize/9, boardRect.top+y - squareSize/9,
\r
3496 boardRect.left+x + squareSize/9, boardRect.top+y + squareSize/9);
\r
3498 Ellipse(hdcSeek, boardRect.left+x - squareSize/8, boardRect.top+y - squareSize/8,
\r
3499 boardRect.left+x + squareSize/8, boardRect.top+y + squareSize/8);
\r
3500 SelectObject(hdcSeek, oldBrush);
\r
3504 HDCDrawPosition(HDC hdc, BOOLEAN repaint, Board board)
\r
3506 static Board lastReq[2], lastDrawn[2];
\r
3507 static HighlightInfo lastDrawnHighlight, lastDrawnPremove;
\r
3508 static int lastDrawnFlipView = 0;
\r
3509 static int lastReqValid[2] = {0, 0}, lastDrawnValid[2] = {0, 0};
\r
3510 int releaseDC, x, y, x2, y2, row, column, num_clips = 0, i;
\r
3513 HBITMAP bufferBitmap;
\r
3514 HBITMAP oldBitmap;
\r
3516 HRGN clips[MAX_CLIPS];
\r
3517 ChessSquare dragged_piece = EmptySquare;
\r
3518 int nr = twoBoards*partnerUp;
\r
3520 /* I'm undecided on this - this function figures out whether a full
\r
3521 * repaint is necessary on its own, so there's no real reason to have the
\r
3522 * caller tell it that. I think this can safely be set to FALSE - but
\r
3523 * if we trust the callers not to request full repaints unnessesarily, then
\r
3524 * we could skip some clipping work. In other words, only request a full
\r
3525 * redraw when the majority of pieces have changed positions (ie. flip,
\r
3526 * gamestart and similar) --Hawk
\r
3528 Boolean fullrepaint = repaint;
\r
3530 if(DrawSeekGraph()) return; // [HG} seekgraph: suppress printing board if seek graph up
\r
3532 if( DrawPositionNeedsFullRepaint() ) {
\r
3533 fullrepaint = TRUE;
\r
3536 if (board == NULL) {
\r
3537 if (!lastReqValid[nr]) {
\r
3540 board = lastReq[nr];
\r
3542 CopyBoard(lastReq[nr], board);
\r
3543 lastReqValid[nr] = 1;
\r
3546 if (doingSizing) {
\r
3550 if (IsIconic(hwndMain)) {
\r
3554 if (hdc == NULL) {
\r
3555 hdc = GetDC(hwndMain);
\r
3556 if (!appData.monoMode) {
\r
3557 SelectPalette(hdc, hPal, FALSE);
\r
3558 RealizePalette(hdc);
\r
3562 releaseDC = FALSE;
\r
3565 /* Create some work-DCs */
\r
3566 hdcmem = CreateCompatibleDC(hdc);
\r
3567 tmphdc = CreateCompatibleDC(hdc);
\r
3569 /* If dragging is in progress, we temporarely remove the piece */
\r
3570 /* [HGM] or temporarily decrease count if stacked */
\r
3571 /* !! Moved to before board compare !! */
\r
3572 if (dragInfo.from.x >= 0 && dragInfo.pos.x >= 0) {
\r
3573 dragged_piece = board[dragInfo.from.y][dragInfo.from.x];
\r
3574 if(dragInfo.from.x == BOARD_LEFT-2 ) {
\r
3575 if(--board[dragInfo.from.y][dragInfo.from.x+1] == 0 )
\r
3576 board[dragInfo.from.y][dragInfo.from.x] = EmptySquare;
\r
3578 if(dragInfo.from.x == BOARD_RGHT+1) {
\r
3579 if(--board[dragInfo.from.y][dragInfo.from.x-1] == 0 )
\r
3580 board[dragInfo.from.y][dragInfo.from.x] = EmptySquare;
\r
3582 board[dragInfo.from.y][dragInfo.from.x] = gatingPiece;
\r
3585 /* Figure out which squares need updating by comparing the
\r
3586 * newest board with the last drawn board and checking if
\r
3587 * flipping has changed.
\r
3589 if (!fullrepaint && lastDrawnValid[nr] && (nr == 1 || lastDrawnFlipView == flipView)) {
\r
3590 for (row = 0; row < BOARD_HEIGHT; row++) { /* [HGM] true size, not 8 */
\r
3591 for (column = 0; column < BOARD_WIDTH; column++) {
\r
3592 if (lastDrawn[nr][row][column] != board[row][column]) {
\r
3593 SquareToPos(row, column, &x, &y);
\r
3594 clips[num_clips++] =
\r
3595 CreateRectRgn(x, y, x + squareSize, y + squareSize);
\r
3599 if(nr == 0) { // [HGM] dual: no highlights on second board
\r
3600 for (i=0; i<2; i++) {
\r
3601 if (lastDrawnHighlight.sq[i].x != highlightInfo.sq[i].x ||
\r
3602 lastDrawnHighlight.sq[i].y != highlightInfo.sq[i].y) {
\r
3603 if (lastDrawnHighlight.sq[i].x >= 0 &&
\r
3604 lastDrawnHighlight.sq[i].y >= 0) {
\r
3605 SquareToPos(lastDrawnHighlight.sq[i].y,
\r
3606 lastDrawnHighlight.sq[i].x, &x, &y);
\r
3607 clips[num_clips++] =
\r
3608 CreateRectRgn(x - lineGap, y - lineGap,
\r
3609 x + squareSize + lineGap, y + squareSize + lineGap);
\r
3611 if (highlightInfo.sq[i].x >= 0 && highlightInfo.sq[i].y >= 0) {
\r
3612 SquareToPos(highlightInfo.sq[i].y, highlightInfo.sq[i].x, &x, &y);
\r
3613 clips[num_clips++] =
\r
3614 CreateRectRgn(x - lineGap, y - lineGap,
\r
3615 x + squareSize + lineGap, y + squareSize + lineGap);
\r
3619 for (i=0; i<2; i++) {
\r
3620 if (lastDrawnPremove.sq[i].x != premoveHighlightInfo.sq[i].x ||
\r
3621 lastDrawnPremove.sq[i].y != premoveHighlightInfo.sq[i].y) {
\r
3622 if (lastDrawnPremove.sq[i].x >= 0 &&
\r
3623 lastDrawnPremove.sq[i].y >= 0) {
\r
3624 SquareToPos(lastDrawnPremove.sq[i].y,
\r
3625 lastDrawnPremove.sq[i].x, &x, &y);
\r
3626 clips[num_clips++] =
\r
3627 CreateRectRgn(x - lineGap, y - lineGap,
\r
3628 x + squareSize + lineGap, y + squareSize + lineGap);
\r
3630 if (premoveHighlightInfo.sq[i].x >= 0 &&
\r
3631 premoveHighlightInfo.sq[i].y >= 0) {
\r
3632 SquareToPos(premoveHighlightInfo.sq[i].y,
\r
3633 premoveHighlightInfo.sq[i].x, &x, &y);
\r
3634 clips[num_clips++] =
\r
3635 CreateRectRgn(x - lineGap, y - lineGap,
\r
3636 x + squareSize + lineGap, y + squareSize + lineGap);
\r
3640 } else { // nr == 1
\r
3641 partnerHighlightInfo.sq[0].y = board[EP_STATUS-4];
\r
3642 partnerHighlightInfo.sq[0].x = board[EP_STATUS-3];
\r
3643 partnerHighlightInfo.sq[1].y = board[EP_STATUS-2];
\r
3644 partnerHighlightInfo.sq[1].x = board[EP_STATUS-1];
\r
3645 for (i=0; i<2; i++) {
\r
3646 if (partnerHighlightInfo.sq[i].x >= 0 &&
\r
3647 partnerHighlightInfo.sq[i].y >= 0) {
\r
3648 SquareToPos(partnerHighlightInfo.sq[i].y,
\r
3649 partnerHighlightInfo.sq[i].x, &x, &y);
\r
3650 clips[num_clips++] =
\r
3651 CreateRectRgn(x - lineGap, y - lineGap,
\r
3652 x + squareSize + lineGap, y + squareSize + lineGap);
\r
3654 if (oldPartnerHighlight.sq[i].x >= 0 &&
\r
3655 oldPartnerHighlight.sq[i].y >= 0) {
\r
3656 SquareToPos(oldPartnerHighlight.sq[i].y,
\r
3657 oldPartnerHighlight.sq[i].x, &x, &y);
\r
3658 clips[num_clips++] =
\r
3659 CreateRectRgn(x - lineGap, y - lineGap,
\r
3660 x + squareSize + lineGap, y + squareSize + lineGap);
\r
3665 fullrepaint = TRUE;
\r
3668 /* Create a buffer bitmap - this is the actual bitmap
\r
3669 * being written to. When all the work is done, we can
\r
3670 * copy it to the real DC (the screen). This avoids
\r
3671 * the problems with flickering.
\r
3673 GetClientRect(hwndMain, &Rect);
\r
3674 bufferBitmap = CreateCompatibleBitmap(hdc, Rect.right-Rect.left+1,
\r
3675 Rect.bottom-Rect.top+1);
\r
3676 oldBitmap = SelectObject(hdcmem, bufferBitmap);
\r
3677 if (!appData.monoMode) {
\r
3678 SelectPalette(hdcmem, hPal, FALSE);
\r
3681 /* Create clips for dragging */
\r
3682 if (!fullrepaint) {
\r
3683 if (dragInfo.from.x >= 0) {
\r
3684 SquareToPos(dragInfo.from.y, dragInfo.from.x, &x, &y);
\r
3685 clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);
\r
3687 if (dragInfo.start.x >= 0) {
\r
3688 SquareToPos(dragInfo.start.y, dragInfo.start.x, &x, &y);
\r
3689 clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);
\r
3691 if (dragInfo.pos.x >= 0) {
\r
3692 x = dragInfo.pos.x - squareSize / 2;
\r
3693 y = dragInfo.pos.y - squareSize / 2;
\r
3694 clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);
\r
3696 if (dragInfo.lastpos.x >= 0) {
\r
3697 x = dragInfo.lastpos.x - squareSize / 2;
\r
3698 y = dragInfo.lastpos.y - squareSize / 2;
\r
3699 clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);
\r
3703 /* Are we animating a move?
\r
3705 * - remove the piece from the board (temporarely)
\r
3706 * - calculate the clipping region
\r
3708 if (!fullrepaint) {
\r
3709 if (animInfo.piece != EmptySquare) {
\r
3710 board[animInfo.from.y][animInfo.from.x] = EmptySquare;
\r
3711 x = boardRect.left + animInfo.lastpos.x;
\r
3712 y = boardRect.top + animInfo.lastpos.y;
\r
3713 x2 = boardRect.left + animInfo.pos.x;
\r
3714 y2 = boardRect.top + animInfo.pos.y;
\r
3715 clips[num_clips++] = CreateRectRgn(MIN(x,x2), MIN(y,y2), MAX(x,x2)+squareSize, MAX(y,y2)+squareSize);
\r
3716 /* Slight kludge. The real problem is that after AnimateMove is
\r
3717 done, the position on the screen does not match lastDrawn.
\r
3718 This currently causes trouble only on e.p. captures in
\r
3719 atomic, where the piece moves to an empty square and then
\r
3720 explodes. The old and new positions both had an empty square
\r
3721 at the destination, but animation has drawn a piece there and
\r
3722 we have to remember to erase it. [HGM] moved until after setting lastDrawn */
\r
3723 lastDrawn[0][animInfo.to.y][animInfo.to.x] = animInfo.piece;
\r
3727 /* No clips? Make sure we have fullrepaint set to TRUE */
\r
3728 if (num_clips == 0)
\r
3729 fullrepaint = TRUE;
\r
3731 /* Set clipping on the memory DC */
\r
3732 if (!fullrepaint) {
\r
3733 SelectClipRgn(hdcmem, clips[0]);
\r
3734 for (x = 1; x < num_clips; x++) {
\r
3735 if (ExtSelectClipRgn(hdcmem, clips[x], RGN_OR) == ERROR)
\r
3736 abort(); // this should never ever happen!
\r
3740 /* Do all the drawing to the memory DC */
\r
3741 if(explodeInfo.radius) { // [HGM] atomic
\r
3743 int x, y, r=(explodeInfo.radius * squareSize)/100;
\r
3744 ChessSquare piece = board[explodeInfo.fromY][explodeInfo.fromX];
\r
3745 board[explodeInfo.fromY][explodeInfo.fromX] = EmptySquare; // suppress display of capturer
\r
3746 SquareToPos(explodeInfo.toY, explodeInfo.toX, &x, &y);
\r
3747 x += squareSize/2;
\r
3748 y += squareSize/2;
\r
3749 if(!fullrepaint) {
\r
3750 clips[num_clips] = CreateRectRgn(x-r, y-r, x+r, y+r);
\r
3751 ExtSelectClipRgn(hdcmem, clips[num_clips++], RGN_OR);
\r
3753 DrawGridOnDC(hdcmem);
\r
3754 DrawHighlightsOnDC(hdcmem, &highlightInfo, HIGHLIGHT_PEN);
\r
3755 DrawHighlightsOnDC(hdcmem, &premoveHighlightInfo, PREMOVE_PEN);
\r
3756 DrawBoardOnDC(hdcmem, board, tmphdc);
\r
3757 board[explodeInfo.fromY][explodeInfo.fromX] = piece;
\r
3758 oldBrush = SelectObject(hdcmem, explodeBrush);
\r
3759 Ellipse(hdcmem, x-r, y-r, x+r, y+r);
\r
3760 SelectObject(hdcmem, oldBrush);
\r
3762 DrawGridOnDC(hdcmem);
\r
3763 if(nr == 0) { // [HGM] dual: decide which highlights to draw
\r
3764 DrawHighlightsOnDC(hdcmem, &highlightInfo, HIGHLIGHT_PEN);
\r
3765 DrawHighlightsOnDC(hdcmem, &premoveHighlightInfo, PREMOVE_PEN);
\r
3767 DrawHighlightsOnDC(hdcmem, &partnerHighlightInfo, HIGHLIGHT_PEN);
\r
3768 oldPartnerHighlight = partnerHighlightInfo;
\r
3770 DrawBoardOnDC(hdcmem, board, tmphdc);
\r
3772 if(nr == 0) // [HGM] dual: markers only on left board
\r
3773 for (row = 0; row < BOARD_HEIGHT; row++) {
\r
3774 for (column = 0; column < BOARD_WIDTH; column++) {
\r
3775 if (marker[row][column]) { // marker changes only occur with full repaint!
\r
3776 HBRUSH oldBrush = SelectObject(hdcmem,
\r
3777 marker[row][column] == 2 ? markerBrush : explodeBrush);
\r
3778 SquareToPos(row, column, &x, &y);
\r
3779 Ellipse(hdcmem, x + squareSize/4, y + squareSize/4,
\r
3780 x + 3*squareSize/4, y + 3*squareSize/4);
\r
3781 SelectObject(hdcmem, oldBrush);
\r
3786 if( appData.highlightMoveWithArrow ) {
\r
3787 DrawArrowHighlight(hdcmem);
\r
3790 DrawCoordsOnDC(hdcmem);
\r
3792 CopyBoard(lastDrawn[nr], board); /* [HGM] Moved to here from end of routine, */
\r
3793 /* to make sure lastDrawn contains what is actually drawn */
\r
3795 /* Put the dragged piece back into place and draw it (out of place!) */
\r
3796 if (dragged_piece != EmptySquare) {
\r
3797 /* [HGM] or restack */
\r
3798 if(dragInfo.from.x == BOARD_LEFT-2 )
\r
3799 board[dragInfo.from.y][dragInfo.from.x+1]++;
\r
3801 if(dragInfo.from.x == BOARD_RGHT+1 )
\r
3802 board[dragInfo.from.y][dragInfo.from.x-1]++;
\r
3803 board[dragInfo.from.y][dragInfo.from.x] = dragged_piece;
\r
3804 x = dragInfo.pos.x - squareSize / 2;
\r
3805 y = dragInfo.pos.y - squareSize / 2;
\r
3806 DrawPieceOnDC(hdcmem, dragInfo.piece,
\r
3807 ((int) dragInfo.piece < (int) BlackPawn),
\r
3808 (dragInfo.from.y + dragInfo.from.x) % 2, x, y, tmphdc);
\r
3811 /* Put the animated piece back into place and draw it */
\r
3812 if (animInfo.piece != EmptySquare) {
\r
3813 board[animInfo.from.y][animInfo.from.x] = animInfo.piece;
\r
3814 x = boardRect.left + animInfo.pos.x;
\r
3815 y = boardRect.top + animInfo.pos.y;
\r
3816 DrawPieceOnDC(hdcmem, animInfo.piece,
\r
3817 ((int) animInfo.piece < (int) BlackPawn),
\r
3818 (animInfo.from.y + animInfo.from.x) % 2, x, y, tmphdc);
\r
3821 /* Release the bufferBitmap by selecting in the old bitmap
\r
3822 * and delete the memory DC
\r
3824 SelectObject(hdcmem, oldBitmap);
\r
3827 /* Set clipping on the target DC */
\r
3828 if (!fullrepaint) {
\r
3829 if(nr == 1) for (x = 0; x < num_clips; x++) { // [HGM] dual: translate clips
\r
3831 GetRgnBox(clips[x], &rect);
\r
3832 DeleteObject(clips[x]);
\r
3833 clips[x] = CreateRectRgn(rect.left + wpMain.width/2, rect.top,
\r
3834 rect.right + wpMain.width/2, rect.bottom);
\r
3836 SelectClipRgn(hdc, clips[0]);
\r
3837 for (x = 1; x < num_clips; x++) {
\r
3838 if (ExtSelectClipRgn(hdc, clips[x], RGN_OR) == ERROR)
\r
3839 abort(); // this should never ever happen!
\r
3843 /* Copy the new bitmap onto the screen in one go.
\r
3844 * This way we avoid any flickering
\r
3846 oldBitmap = SelectObject(tmphdc, bufferBitmap);
\r
3847 BitBlt(hdc, boardRect.left + twoBoards*partnerUp*wpMain.width/2, boardRect.top, // [HGM] dual
\r
3848 boardRect.right - boardRect.left,
\r
3849 boardRect.bottom - boardRect.top,
\r
3850 tmphdc, boardRect.left, boardRect.top, SRCCOPY);
\r
3851 if(saveDiagFlag) {
\r
3852 BITMAP b; int i, j=0, m, w, wb, fac=0; char *pData;
\r
3853 BITMAPINFOHEADER bih; int color[16], nrColors=0;
\r
3855 GetObject(bufferBitmap, sizeof(b), &b);
\r
3856 if(pData = malloc(b.bmWidthBytes*b.bmHeight + 10000)) {
\r
3857 bih.biSize = sizeof(BITMAPINFOHEADER);
\r
3858 bih.biWidth = b.bmWidth;
\r
3859 bih.biHeight = b.bmHeight;
\r
3861 bih.biBitCount = b.bmBitsPixel;
\r
3862 bih.biCompression = 0;
\r
3863 bih.biSizeImage = b.bmWidthBytes*b.bmHeight;
\r
3864 bih.biXPelsPerMeter = 0;
\r
3865 bih.biYPelsPerMeter = 0;
\r
3866 bih.biClrUsed = 0;
\r
3867 bih.biClrImportant = 0;
\r
3868 // fprintf(diagFile, "t=%d\nw=%d\nh=%d\nB=%d\nP=%d\nX=%d\n",
\r
3869 // b.bmType, b.bmWidth, b.bmHeight, b.bmWidthBytes, b.bmPlanes, b.bmBitsPixel);
\r
3870 GetDIBits(tmphdc,bufferBitmap,0,b.bmHeight,pData,(BITMAPINFO*)&bih,DIB_RGB_COLORS);
\r
3871 // fprintf(diagFile, "%8x\n", (int) pData);
\r
3873 wb = b.bmWidthBytes;
\r
3875 for(i=0; i<wb*(b.bmHeight - boardRect.top + OUTER_MARGIN)>>2; i++) {
\r
3876 int k = ((int*) pData)[i];
\r
3877 for(j=0; j<nrColors; j++) if(color[j] == k) break;
\r
3878 if(j >= 16) break;
\r
3880 if(j >= nrColors) nrColors = j+1;
\r
3882 if(j<16) { // 16 colors is enough. Compress to 4 bits per pixel
\r
3884 for(i=0; i<b.bmHeight - boardRect.top + OUTER_MARGIN; i++) {
\r
3885 for(w=0; w<(wb>>2); w+=2) {
\r
3886 int k = ((int*) pData)[(wb*i>>2) + w];
\r
3887 for(j=0; j<nrColors; j++) if(color[j] == k) break;
\r
3888 k = ((int*) pData)[(wb*i>>2) + w + 1];
\r
3889 for(m=0; m<nrColors; m++) if(color[m] == k) break;
\r
3890 pData[p++] = m | j<<4;
\r
3892 while(p&3) pData[p++] = 0;
\r
3895 wb = ((wb+31)>>5)<<2;
\r
3897 // write BITMAPFILEHEADER
\r
3898 fprintf(diagFile, "BM");
\r
3899 fputDW(diagFile, wb*(b.bmHeight - boardRect.top + OUTER_MARGIN)+0x36 + (fac?64:0));
\r
3900 fputDW(diagFile, 0);
\r
3901 fputDW(diagFile, 0x36 + (fac?64:0));
\r
3902 // write BITMAPINFOHEADER
\r
3903 fputDW(diagFile, 40);
\r
3904 fputDW(diagFile, b.bmWidth);
\r
3905 fputDW(diagFile, b.bmHeight - boardRect.top + OUTER_MARGIN);
\r
3906 if(fac) fputDW(diagFile, 0x040001); // planes and bits/pixel
\r
3907 else fputDW(diagFile, 0x200001); // planes and bits/pixel
\r
3908 fputDW(diagFile, 0);
\r
3909 fputDW(diagFile, 0);
\r
3910 fputDW(diagFile, 0);
\r
3911 fputDW(diagFile, 0);
\r
3912 fputDW(diagFile, 0);
\r
3913 fputDW(diagFile, 0);
\r
3914 // write color table
\r
3916 for(i=0; i<16; i++) fputDW(diagFile, color[i]);
\r
3917 // write bitmap data
\r
3918 for(i=0; i<wb*(b.bmHeight - boardRect.top + OUTER_MARGIN); i++)
\r
3919 fputc(pData[i], diagFile);
\r
3924 SelectObject(tmphdc, oldBitmap);
\r
3926 /* Massive cleanup */
\r
3927 for (x = 0; x < num_clips; x++)
\r
3928 DeleteObject(clips[x]);
\r
3931 DeleteObject(bufferBitmap);
\r
3934 ReleaseDC(hwndMain, hdc);
\r
3936 if (lastDrawnFlipView != flipView && nr == 0) {
\r
3938 CheckMenuItem(GetMenu(hwndMain),IDM_FlipView, MF_BYCOMMAND|MF_CHECKED);
\r
3940 CheckMenuItem(GetMenu(hwndMain),IDM_FlipView, MF_BYCOMMAND|MF_UNCHECKED);
\r
3943 /* CopyBoard(lastDrawn, board);*/
\r
3944 lastDrawnHighlight = highlightInfo;
\r
3945 lastDrawnPremove = premoveHighlightInfo;
\r
3946 lastDrawnFlipView = flipView;
\r
3947 lastDrawnValid[nr] = 1;
\r
3950 /* [HGM] diag: Save the current board display to the given open file and close the file */
\r
3955 saveDiagFlag = 1; diagFile = f;
\r
3956 HDCDrawPosition(NULL, TRUE, NULL);
\r
3964 /*---------------------------------------------------------------------------*\
\r
3965 | CLIENT PAINT PROCEDURE
\r
3966 | This is the main event-handler for the WM_PAINT message.
\r
3968 \*---------------------------------------------------------------------------*/
\r
3970 PaintProc(HWND hwnd)
\r
3976 if((hdc = BeginPaint(hwnd, &ps))) {
\r
3977 if (IsIconic(hwnd)) {
\r
3978 DrawIcon(hdc, 2, 2, iconCurrent);
\r
3980 if (!appData.monoMode) {
\r
3981 SelectPalette(hdc, hPal, FALSE);
\r
3982 RealizePalette(hdc);
\r
3984 HDCDrawPosition(hdc, 1, NULL);
\r
3985 if(twoBoards) { // [HGM] dual: also redraw other board in other orientation
\r
3986 flipView = !flipView; partnerUp = !partnerUp;
\r
3987 HDCDrawPosition(hdc, 1, NULL);
\r
3988 flipView = !flipView; partnerUp = !partnerUp;
\r
3991 SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);
\r
3992 ExtTextOut(hdc, messageRect.left, messageRect.top,
\r
3993 ETO_CLIPPED|ETO_OPAQUE,
\r
3994 &messageRect, messageText, strlen(messageText), NULL);
\r
3995 SelectObject(hdc, oldFont);
\r
3996 DisplayBothClocks();
\r
3999 EndPaint(hwnd,&ps);
\r
4007 * If the user selects on a border boundary, return -1; if off the board,
\r
4008 * return -2. Otherwise map the event coordinate to the square.
\r
4009 * The offset boardRect.left or boardRect.top must already have been
\r
4010 * subtracted from x.
\r
4012 int EventToSquare(x, limit)
\r
4020 if ((x % (squareSize + lineGap)) >= squareSize)
\r
4022 x /= (squareSize + lineGap);
\r
4034 DropEnable dropEnables[] = {
\r
4035 { 'P', DP_Pawn, N_("Pawn") },
\r
4036 { 'N', DP_Knight, N_("Knight") },
\r
4037 { 'B', DP_Bishop, N_("Bishop") },
\r
4038 { 'R', DP_Rook, N_("Rook") },
\r
4039 { 'Q', DP_Queen, N_("Queen") },
\r
4043 SetupDropMenu(HMENU hmenu)
\r
4045 int i, count, enable;
\r
4047 extern char white_holding[], black_holding[];
\r
4048 char item[MSG_SIZ];
\r
4050 for (i=0; i<sizeof(dropEnables)/sizeof(DropEnable); i++) {
\r
4051 p = strchr(gameMode == IcsPlayingWhite ? white_holding : black_holding,
\r
4052 dropEnables[i].piece);
\r
4054 while (p && *p++ == dropEnables[i].piece) count++;
\r
4055 snprintf(item, MSG_SIZ, "%s %d", T_(dropEnables[i].name), count);
\r
4056 enable = count > 0 || !appData.testLegality
\r
4057 /*!!temp:*/ || (gameInfo.variant == VariantCrazyhouse
\r
4058 && !appData.icsActive);
\r
4059 ModifyMenu(hmenu, dropEnables[i].command,
\r
4060 MF_BYCOMMAND | (enable ? MF_ENABLED : MF_GRAYED) | MF_STRING,
\r
4061 dropEnables[i].command, item);
\r
4065 void DragPieceBegin(int x, int y)
\r
4067 dragInfo.lastpos.x = boardRect.left + x;
\r
4068 dragInfo.lastpos.y = boardRect.top + y;
\r
4069 dragInfo.from.x = fromX;
\r
4070 dragInfo.from.y = fromY;
\r
4071 dragInfo.piece = boards[currentMove][fromY][fromX];
\r
4072 dragInfo.start = dragInfo.from;
\r
4073 SetCapture(hwndMain);
\r
4076 void DragPieceEnd(int x, int y)
\r
4079 dragInfo.start.x = dragInfo.start.y = -1;
\r
4080 dragInfo.from = dragInfo.start;
\r
4081 dragInfo.pos = dragInfo.lastpos = dragInfo.start;
\r
4084 void ChangeDragPiece(ChessSquare piece)
\r
4086 dragInfo.piece = piece;
\r
4089 /* Event handler for mouse messages */
\r
4091 MouseEvent(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
\r
4095 static int recursive = 0;
\r
4097 BOOLEAN forceFullRepaint = IsFullRepaintPreferrable(); /* [AS] */
\r
4100 if (message == WM_MBUTTONUP) {
\r
4101 /* Hideous kludge to fool TrackPopupMenu into paying attention
\r
4102 to the middle button: we simulate pressing the left button too!
\r
4104 PostMessage(hwnd, WM_LBUTTONDOWN, wParam, lParam);
\r
4105 PostMessage(hwnd, WM_LBUTTONUP, wParam, lParam);
\r
4111 pt.x = LOWORD(lParam);
\r
4112 pt.y = HIWORD(lParam);
\r
4113 x = EventToSquare(pt.x - boardRect.left, BOARD_WIDTH);
\r
4114 y = EventToSquare(pt.y - boardRect.top, BOARD_HEIGHT);
\r
4115 if (!flipView && y >= 0) {
\r
4116 y = BOARD_HEIGHT - 1 - y;
\r
4118 if (flipView && x >= 0) {
\r
4119 x = BOARD_WIDTH - 1 - x;
\r
4122 shiftKey = GetKeyState(VK_SHIFT) < 0; // [HGM] remember last shift status
\r
4124 switch (message) {
\r
4125 case WM_LBUTTONDOWN:
\r
4126 if (PtInRect((LPRECT) &whiteRect, pt)) {
\r
4127 ClockClick(flipClock);
\r
4128 } else if (PtInRect((LPRECT) &blackRect, pt)) {
\r
4129 ClockClick(!flipClock);
\r
4131 dragInfo.start.x = dragInfo.start.y = -1;
\r
4132 dragInfo.from = dragInfo.start;
\r
4133 if(fromX == -1 && frozen) { // not sure where this is for
\r
4134 fromX = fromY = -1;
\r
4135 DrawPosition(forceFullRepaint || FALSE, NULL); /* [AS] */
\r
4138 LeftClick(Press, pt.x - boardRect.left, pt.y - boardRect.top);
\r
4139 DrawPosition(TRUE, NULL);
\r
4142 case WM_LBUTTONUP:
\r
4143 LeftClick(Release, pt.x - boardRect.left, pt.y - boardRect.top);
\r
4144 DrawPosition(TRUE, NULL);
\r
4147 case WM_MOUSEMOVE:
\r
4148 if(SeekGraphClick(Press, pt.x - boardRect.left, pt.y - boardRect.top, 1)) break;
\r
4149 if(PromoScroll(pt.x - boardRect.left, pt.y - boardRect.top)) break;
\r
4150 MovePV(pt.x - boardRect.left, pt.y - boardRect.top, boardRect.bottom - boardRect.top);
\r
4151 if ((appData.animateDragging || appData.highlightDragging)
\r
4152 && (wParam & MK_LBUTTON)
\r
4153 && dragInfo.from.x >= 0)
\r
4155 BOOL full_repaint = FALSE;
\r
4157 if (appData.animateDragging) {
\r
4158 dragInfo.pos = pt;
\r
4160 if (appData.highlightDragging) {
\r
4161 SetHighlights(fromX, fromY, x, y);
\r
4162 if( IsDrawArrowEnabled() && (x < 0 || x >= BOARD_WIDTH || y < 0 || y >= BOARD_HEIGHT) ) {
\r
4163 full_repaint = TRUE;
\r
4167 DrawPosition( full_repaint, NULL);
\r
4169 dragInfo.lastpos = dragInfo.pos;
\r
4173 case WM_MOUSEWHEEL: // [DM]
\r
4174 { static int lastDir = 0; // [HGM] build in some hysteresis to avoid spurious events
\r
4175 /* Mouse Wheel is being rolled forward
\r
4176 * Play moves forward
\r
4178 if((short)HIWORD(wParam) < 0 && currentMove < forwardMostMove)
\r
4179 { if(lastDir == 1) ForwardEvent(); else lastDir = 1; } // [HGM] suppress first event in direction
\r
4180 /* Mouse Wheel is being rolled backward
\r
4181 * Play moves backward
\r
4183 if((short)HIWORD(wParam) > 0 && currentMove > backwardMostMove)
\r
4184 { if(lastDir == -1) BackwardEvent(); else lastDir = -1; }
\r
4188 case WM_MBUTTONUP:
\r
4189 case WM_RBUTTONUP:
\r
4191 RightClick(Release, pt.x - boardRect.left, pt.y - boardRect.top, &fromX, &fromY);
\r
4194 case WM_MBUTTONDOWN:
\r
4195 case WM_RBUTTONDOWN:
\r
4198 fromX = fromY = -1;
\r
4199 dragInfo.pos.x = dragInfo.pos.y = -1;
\r
4200 dragInfo.start.x = dragInfo.start.y = -1;
\r
4201 dragInfo.from = dragInfo.start;
\r
4202 dragInfo.lastpos = dragInfo.pos;
\r
4203 if (appData.highlightDragging) {
\r
4204 ClearHighlights();
\r
4207 /* [HGM] right mouse button in clock area edit-game mode ups clock */
\r
4208 if (PtInRect((LPRECT) &whiteRect, pt)) {
\r
4209 if (gameMode == EditGame || GetKeyState(VK_SHIFT) < 0) AdjustClock(flipClock, 1);
\r
4210 } else if (PtInRect((LPRECT) &blackRect, pt)) {
\r
4211 if (gameMode == EditGame || GetKeyState(VK_SHIFT) < 0) AdjustClock(!flipClock, 1);
\r
4215 DrawPosition(TRUE, NULL);
\r
4217 menuNr = RightClick(Press, pt.x - boardRect.left, pt.y - boardRect.top, &fromX, &fromY);
\r
4220 if (message == WM_MBUTTONDOWN) {
\r
4221 buttonCount = 3; /* even if system didn't think so */
\r
4222 if (wParam & MK_SHIFT)
\r
4223 MenuPopup(hwnd, pt, LoadMenu(hInst, "BlackPieceMenu"), -1);
\r
4225 MenuPopup(hwnd, pt, LoadMenu(hInst, "WhitePieceMenu"), -1);
\r
4226 } else { /* message == WM_RBUTTONDOWN */
\r
4227 /* Just have one menu, on the right button. Windows users don't
\r
4228 think to try the middle one, and sometimes other software steals
\r
4229 it, or it doesn't really exist. */
\r
4230 if(gameInfo.variant != VariantShogi)
\r
4231 MenuPopup(hwnd, pt, LoadMenu(hInst, "PieceMenu"), -1);
\r
4233 MenuPopup(hwnd, pt, LoadMenu(hInst, "ShogiPieceMenu"), -1);
\r
4237 SetCapture(hwndMain);
4240 hmenu = LoadMenu(hInst, "DropPieceMenu");
\r
4241 SetupDropMenu(hmenu);
\r
4242 MenuPopup(hwnd, pt, hmenu, -1);
\r
4252 /* Preprocess messages for buttons in main window */
\r
4254 ButtonProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
\r
4256 int id = GetWindowLongPtr(hwnd, GWLP_ID);
\r
4259 for (i=0; i<N_BUTTONS; i++) {
\r
4260 if (buttonDesc[i].id == id) break;
\r
4262 if (i == N_BUTTONS) return 0;
\r
4263 switch (message) {
\r
4268 dir = (wParam == VK_LEFT) ? -1 : 1;
\r
4269 SetFocus(buttonDesc[(i + dir + N_BUTTONS) % N_BUTTONS].hwnd);
\r
4276 SendMessage(hwndMain, WM_COMMAND, MAKEWPARAM(buttonDesc[i].id, 0), 0);
\r
4279 if (appData.icsActive && (isalpha((char)wParam) || wParam == '0')) {
\r
4280 // [HGM] movenum: only letters or leading zero should go to ICS input
\r
4281 HWND h = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
4282 if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);
\r
4284 SendMessage(h, WM_CHAR, wParam, lParam);
\r
4286 } else if (isalpha((char)wParam) || isdigit((char)wParam)){
\r
4287 TypeInEvent((char)wParam);
\r
4293 return CallWindowProc(buttonDesc[i].wndproc, hwnd, message, wParam, lParam);
\r
4296 /* Process messages for Promotion dialog box */
\r
4298 Promotion(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
4302 switch (message) {
\r
4303 case WM_INITDIALOG: /* message: initialize dialog box */
\r
4304 /* Center the dialog over the application window */
\r
4305 CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));
\r
4306 Translate(hDlg, DLG_PromotionKing);
\r
4307 ShowWindow(GetDlgItem(hDlg, PB_King),
\r
4308 (!appData.testLegality || gameInfo.variant == VariantSuicide ||
\r
4309 gameInfo.variant == VariantSpartan && !WhiteOnMove(currentMove) ||
\r
4310 gameInfo.variant == VariantGiveaway || gameInfo.variant == VariantSuper ) ?
\r
4311 SW_SHOW : SW_HIDE);
\r
4312 /* [HGM] Only allow C & A promotions if these pieces are defined */
\r
4313 ShowWindow(GetDlgItem(hDlg, PB_Archbishop),
\r
4314 ((PieceToChar(WhiteAngel) >= 'A' && WhiteOnMove(currentMove) &&
\r
4315 PieceToChar(WhiteAngel) != '~') ||
\r
4316 (PieceToChar(BlackAngel) >= 'A' && !WhiteOnMove(currentMove) &&
\r
4317 PieceToChar(BlackAngel) != '~') ) ?
\r
4318 SW_SHOW : SW_HIDE);
\r
4319 ShowWindow(GetDlgItem(hDlg, PB_Chancellor),
\r
4320 ((PieceToChar(WhiteMarshall) >= 'A' && WhiteOnMove(currentMove) &&
\r
4321 PieceToChar(WhiteMarshall) != '~') ||
\r
4322 (PieceToChar(BlackMarshall) >= 'A' && !WhiteOnMove(currentMove) &&
\r
4323 PieceToChar(BlackMarshall) != '~') ) ?
\r
4324 SW_SHOW : SW_HIDE);
\r
4325 /* [HGM] Hide B & R button in Shogi, use Q as promote, N as defer */
\r
4326 ShowWindow(GetDlgItem(hDlg, PB_Rook),
\r
4327 gameInfo.variant != VariantShogi ?
\r
4328 SW_SHOW : SW_HIDE);
\r
4329 ShowWindow(GetDlgItem(hDlg, PB_Bishop),
\r
4330 gameInfo.variant != VariantShogi ?
\r
4331 SW_SHOW : SW_HIDE);
\r
4332 if(gameInfo.variant == VariantShogi) {
\r
4333 SetDlgItemText(hDlg, PB_Queen, "YES");
\r
4334 SetDlgItemText(hDlg, PB_Knight, "NO");
\r
4335 SetWindowText(hDlg, "Promote?");
\r
4337 ShowWindow(GetDlgItem(hDlg, IDC_Centaur),
\r
4338 gameInfo.variant == VariantSuper ?
\r
4339 SW_SHOW : SW_HIDE);
\r
4342 case WM_COMMAND: /* message: received a command */
\r
4343 switch (LOWORD(wParam)) {
\r
4345 EndDialog(hDlg, TRUE); /* Exit the dialog */
\r
4346 ClearHighlights();
\r
4347 DrawPosition(FALSE, NULL);
\r
4350 promoChar = gameInfo.variant == VariantSuper ? PieceToChar(BlackSilver) : PieceToChar(BlackKing);
\r
4353 promoChar = gameInfo.variant == VariantShogi ? '+' : ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteQueen : BlackQueen));
\r
4356 promoChar = ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteRook : BlackRook));
\r
4357 if(gameInfo.variant == VariantSpartan && !WhiteOnMove(currentMove)) promoChar = PieceToChar(BlackDragon);
\r
4360 promoChar = ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteBishop : BlackBishop));
\r
4361 if(gameInfo.variant == VariantSpartan && !WhiteOnMove(currentMove)) promoChar = PieceToChar(BlackAlfil);
\r
4363 case PB_Chancellor:
\r
4364 promoChar = ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteMarshall : BlackMarshall));
\r
4366 case PB_Archbishop:
\r
4367 promoChar = ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteAngel : BlackAngel));
\r
4370 promoChar = gameInfo.variant == VariantShogi ? '=' : PieceToChar(WhiteOnMove(currentMove) ? WhiteKnight : BlackKnight);
\r
4375 if(promoChar == '.') return FALSE; // invalid piece chosen
\r
4376 EndDialog(hDlg, TRUE); /* Exit the dialog */
\r
4377 UserMoveEvent(fromX, fromY, toX, toY, promoChar);
\r
4378 fromX = fromY = -1;
\r
4379 if (!appData.highlightLastMove) {
\r
4380 ClearHighlights();
\r
4381 DrawPosition(FALSE, NULL);
\r
4388 /* Pop up promotion dialog */
\r
4390 PromotionPopup(HWND hwnd)
\r
4394 lpProc = MakeProcInstance((FARPROC)Promotion, hInst);
\r
4395 DialogBox(hInst, MAKEINTRESOURCE(DLG_PromotionKing),
\r
4396 hwnd, (DLGPROC)lpProc);
\r
4397 FreeProcInstance(lpProc);
\r
4403 DrawPosition(TRUE, NULL);
\r
4404 PromotionPopup(hwndMain);
\r
4407 /* Toggle ShowThinking */
\r
4409 ToggleShowThinking()
\r
4411 appData.showThinking = !appData.showThinking;
\r
4412 ShowThinkingEvent();
\r
4416 LoadGameDialog(HWND hwnd, char* title)
\r
4420 char fileTitle[MSG_SIZ];
\r
4421 f = OpenFileDialog(hwnd, "rb", "",
\r
4422 appData.oldSaveStyle ? "gam" : "pgn",
\r
4424 title, &number, fileTitle, NULL);
\r
4426 cmailMsgLoaded = FALSE;
\r
4427 if (number == 0) {
\r
4428 int error = GameListBuild(f);
\r
4430 DisplayError(_("Cannot build game list"), error);
\r
4431 } else if (!ListEmpty(&gameList) &&
\r
4432 ((ListGame *) gameList.tailPred)->number > 1) {
\r
4433 GameListPopUp(f, fileTitle);
\r
4436 GameListDestroy();
\r
4439 LoadGame(f, number, fileTitle, FALSE);
\r
4443 int get_term_width()
\r
4448 HFONT hfont, hold_font;
\r
4453 hText = GetDlgItem(hwndConsole, OPT_ConsoleText);
\r
4457 // get the text metrics
\r
4458 hdc = GetDC(hText);
\r
4459 lf = font[boardSize][CONSOLE_FONT]->lf;
\r
4460 if (consoleCF.dwEffects & CFE_BOLD)
\r
4461 lf.lfWeight = FW_BOLD;
\r
4462 if (consoleCF.dwEffects & CFE_ITALIC)
\r
4463 lf.lfItalic = TRUE;
\r
4464 if (consoleCF.dwEffects & CFE_STRIKEOUT)
\r
4465 lf.lfStrikeOut = TRUE;
\r
4466 if (consoleCF.dwEffects & CFE_UNDERLINE)
\r
4467 lf.lfUnderline = TRUE;
\r
4468 hfont = CreateFontIndirect(&lf);
\r
4469 hold_font = SelectObject(hdc, hfont);
\r
4470 GetTextMetrics(hdc, &tm);
\r
4471 SelectObject(hdc, hold_font);
\r
4472 DeleteObject(hfont);
\r
4473 ReleaseDC(hText, hdc);
\r
4475 // get the rectangle
\r
4476 SendMessage(hText, EM_GETRECT, 0, (LPARAM)&rc);
\r
4478 return (rc.right-rc.left) / tm.tmAveCharWidth;
\r
4481 void UpdateICSWidth(HWND hText)
\r
4483 LONG old_width, new_width;
\r
4485 new_width = get_term_width(hText, FALSE);
\r
4486 old_width = GetWindowLongPtr(hText, GWLP_USERDATA);
\r
4487 if (new_width != old_width)
\r
4489 ics_update_width(new_width);
\r
4490 SetWindowLongPtr(hText, GWLP_USERDATA, new_width);
\r
4495 ChangedConsoleFont()
\r
4498 CHARRANGE tmpsel, sel;
\r
4499 MyFont *f = font[boardSize][CONSOLE_FONT];
\r
4500 HWND hText = GetDlgItem(hwndConsole, OPT_ConsoleText);
\r
4501 HWND hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
4504 cfmt.cbSize = sizeof(CHARFORMAT);
\r
4505 cfmt.dwMask = CFM_FACE|CFM_SIZE|CFM_CHARSET;
\r
4506 safeStrCpy(cfmt.szFaceName, font[boardSize][CONSOLE_FONT]->mfp.faceName,
\r
4507 sizeof(cfmt.szFaceName)/sizeof(cfmt.szFaceName[0]) );
\r
4508 /* yHeight is expressed in twips. A twip is 1/20 of a font's point
\r
4509 * size. This was undocumented in the version of MSVC++ that I had
\r
4510 * when I wrote the code, but is apparently documented now.
\r
4512 cfmt.yHeight = (int)(f->mfp.pointSize * 20.0 + 0.5);
\r
4513 cfmt.bCharSet = f->lf.lfCharSet;
\r
4514 cfmt.bPitchAndFamily = f->lf.lfPitchAndFamily;
\r
4515 SendMessage(hText, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &cfmt);
\r
4516 SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &cfmt);
\r
4517 /* Why are the following seemingly needed too? */
\r
4518 SendMessage(hText, EM_SETCHARFORMAT, SCF_DEFAULT, (LPARAM) &cfmt);
\r
4519 SendMessage(hInput, EM_SETCHARFORMAT, SCF_DEFAULT, (LPARAM) &cfmt);
\r
4520 SendMessage(hText, EM_EXGETSEL, 0, (LPARAM)&sel);
\r
4522 tmpsel.cpMax = -1; /*999999?*/
\r
4523 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&tmpsel);
\r
4524 SendMessage(hText, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cfmt);
\r
4525 /* Trying putting this here too. It still seems to tickle a RichEdit
\r
4526 * bug: sometimes RichEdit indents the first line of a paragraph too.
\r
4528 paraf.cbSize = sizeof(paraf);
\r
4529 paraf.dwMask = PFM_OFFSET | PFM_STARTINDENT;
\r
4530 paraf.dxStartIndent = 0;
\r
4531 paraf.dxOffset = WRAP_INDENT;
\r
4532 SendMessage(hText, EM_SETPARAFORMAT, 0, (LPARAM) ¶f);
\r
4533 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
4534 UpdateICSWidth(hText);
\r
4537 /*---------------------------------------------------------------------------*\
\r
4539 * Window Proc for main window
\r
4541 \*---------------------------------------------------------------------------*/
\r
4543 /* Process messages for main window, etc. */
\r
4545 WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
\r
4548 int wmId, wmEvent;
\r
4552 char fileTitle[MSG_SIZ];
\r
4553 char buf[MSG_SIZ];
\r
4554 static SnapData sd;
\r
4556 switch (message) {
\r
4558 case WM_PAINT: /* message: repaint portion of window */
\r
4562 case WM_ERASEBKGND:
\r
4563 if (IsIconic(hwnd)) {
\r
4564 /* Cheat; change the message */
\r
4565 return (DefWindowProc(hwnd, WM_ICONERASEBKGND, wParam, lParam));
\r
4567 return (DefWindowProc(hwnd, message, wParam, lParam));
\r
4571 case WM_LBUTTONDOWN:
\r
4572 case WM_MBUTTONDOWN:
\r
4573 case WM_RBUTTONDOWN:
\r
4574 case WM_LBUTTONUP:
\r
4575 case WM_MBUTTONUP:
\r
4576 case WM_RBUTTONUP:
\r
4577 case WM_MOUSEMOVE:
\r
4578 case WM_MOUSEWHEEL:
\r
4579 MouseEvent(hwnd, message, wParam, lParam);
\r
4582 JAWS_KB_NAVIGATION
\r
4586 JAWS_ALT_INTERCEPT
\r
4588 if (appData.icsActive && ((char)wParam == '\r' || (char)wParam > ' ' && !((char)wParam >= '1' && (char)wParam <= '9'))) {
\r
4589 // [HGM] movenum: for non-zero digits we always do type-in dialog
\r
4590 HWND h = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
4591 if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);
\r
4593 SendMessage(h, message, wParam, lParam);
\r
4594 } else if(lParam != KF_REPEAT) {
\r
4595 if (isalpha((char)wParam) || isdigit((char)wParam)) {
\r
4596 TypeInEvent((char)wParam);
\r
4597 } else if((char)wParam == 003) CopyGameToClipboard();
\r
4598 else if((char)wParam == 026) PasteGameOrFENFromClipboard();
\r
4603 case WM_PALETTECHANGED:
\r
4604 if (hwnd != (HWND)wParam && !appData.monoMode) {
\r
4606 HDC hdc = GetDC(hwndMain);
\r
4607 SelectPalette(hdc, hPal, TRUE);
\r
4608 nnew = RealizePalette(hdc);
\r
4610 paletteChanged = TRUE;
\r
4611 InvalidateRect(hwnd, &boardRect, FALSE);
\r
4613 ReleaseDC(hwnd, hdc);
\r
4617 case WM_QUERYNEWPALETTE:
\r
4618 if (!appData.monoMode /*&& paletteChanged*/) {
\r
4620 HDC hdc = GetDC(hwndMain);
\r
4621 paletteChanged = FALSE;
\r
4622 SelectPalette(hdc, hPal, FALSE);
\r
4623 nnew = RealizePalette(hdc);
\r
4625 InvalidateRect(hwnd, &boardRect, FALSE);
\r
4627 ReleaseDC(hwnd, hdc);
\r
4632 case WM_COMMAND: /* message: command from application menu */
\r
4633 wmId = LOWORD(wParam);
\r
4634 wmEvent = HIWORD(wParam);
\r
4639 SAY("new game enter a move to play against the computer with white");
\r
4642 case IDM_NewGameFRC:
\r
4643 if( NewGameFRC() == 0 ) {
\r
4648 case IDM_NewVariant:
\r
4649 NewVariantPopup(hwnd);
\r
4652 case IDM_LoadGame:
\r
4653 LoadGameDialog(hwnd, _("Load Game from File"));
\r
4656 case IDM_LoadNextGame:
\r
4660 case IDM_LoadPrevGame:
\r
4664 case IDM_ReloadGame:
\r
4668 case IDM_LoadPosition:
\r
4669 if (gameMode == AnalyzeMode || gameMode == AnalyzeFile) {
\r
4670 Reset(FALSE, TRUE);
\r
4673 f = OpenFileDialog(hwnd, "rb", "",
\r
4674 appData.oldSaveStyle ? "pos" : "fen",
\r
4676 _("Load Position from File"), &number, fileTitle, NULL);
\r
4678 LoadPosition(f, number, fileTitle);
\r
4682 case IDM_LoadNextPosition:
\r
4683 ReloadPosition(1);
\r
4686 case IDM_LoadPrevPosition:
\r
4687 ReloadPosition(-1);
\r
4690 case IDM_ReloadPosition:
\r
4691 ReloadPosition(0);
\r
4694 case IDM_SaveGame:
\r
4695 defName = DefaultFileName(appData.oldSaveStyle ? "gam" : "pgn");
\r
4696 f = OpenFileDialog(hwnd, "a", defName,
\r
4697 appData.oldSaveStyle ? "gam" : "pgn",
\r
4699 _("Save Game to File"), NULL, fileTitle, NULL);
\r
4701 SaveGame(f, 0, "");
\r
4705 case IDM_SavePosition:
\r
4706 defName = DefaultFileName(appData.oldSaveStyle ? "pos" : "fen");
\r
4707 f = OpenFileDialog(hwnd, "a", defName,
\r
4708 appData.oldSaveStyle ? "pos" : "fen",
\r
4710 _("Save Position to File"), NULL, fileTitle, NULL);
\r
4712 SavePosition(f, 0, "");
\r
4716 case IDM_SaveDiagram:
\r
4717 defName = "diagram";
\r
4718 f = OpenFileDialog(hwnd, "wb", defName,
\r
4721 _("Save Diagram to File"), NULL, fileTitle, NULL);
\r
4727 case IDM_CopyGame:
\r
4728 CopyGameToClipboard();
\r
4731 case IDM_PasteGame:
\r
4732 PasteGameFromClipboard();
\r
4735 case IDM_CopyGameListToClipboard:
\r
4736 CopyGameListToClipboard();
\r
4739 /* [AS] Autodetect FEN or PGN data */
\r
4740 case IDM_PasteAny:
\r
4741 PasteGameOrFENFromClipboard();
\r
4744 /* [AS] Move history */
\r
4745 case IDM_ShowMoveHistory:
\r
4746 if( MoveHistoryIsUp() ) {
\r
4747 MoveHistoryPopDown();
\r
4750 MoveHistoryPopUp();
\r
4754 /* [AS] Eval graph */
\r
4755 case IDM_ShowEvalGraph:
\r
4756 if( EvalGraphIsUp() ) {
\r
4757 EvalGraphPopDown();
\r
4761 SetFocus(hwndMain);
\r
4765 /* [AS] Engine output */
\r
4766 case IDM_ShowEngineOutput:
\r
4767 if( EngineOutputIsUp() ) {
\r
4768 EngineOutputPopDown();
\r
4771 EngineOutputPopUp();
\r
4775 /* [AS] User adjudication */
\r
4776 case IDM_UserAdjudication_White:
\r
4777 UserAdjudicationEvent( +1 );
\r
4780 case IDM_UserAdjudication_Black:
\r
4781 UserAdjudicationEvent( -1 );
\r
4784 case IDM_UserAdjudication_Draw:
\r
4785 UserAdjudicationEvent( 0 );
\r
4788 /* [AS] Game list options dialog */
\r
4789 case IDM_GameListOptions:
\r
4790 GameListOptions();
\r
4797 case IDM_CopyPosition:
\r
4798 CopyFENToClipboard();
\r
4801 case IDM_PastePosition:
\r
4802 PasteFENFromClipboard();
\r
4805 case IDM_MailMove:
\r
4809 case IDM_ReloadCMailMsg:
\r
4810 Reset(TRUE, TRUE);
\r
4811 ReloadCmailMsgEvent(FALSE);
\r
4814 case IDM_Minimize:
\r
4815 ShowWindow(hwnd, SW_MINIMIZE);
\r
4822 case IDM_MachineWhite:
\r
4823 MachineWhiteEvent();
\r
4825 * refresh the tags dialog only if it's visible
\r
4827 if (gameMode == MachinePlaysWhite && IsWindowVisible(editTagsDialog)) {
\r
4829 tags = PGNTags(&gameInfo);
\r
4830 TagsPopUp(tags, CmailMsg());
\r
4833 SAY("computer starts playing white");
\r
4836 case IDM_MachineBlack:
\r
4837 MachineBlackEvent();
\r
4839 * refresh the tags dialog only if it's visible
\r
4841 if (gameMode == MachinePlaysBlack && IsWindowVisible(editTagsDialog)) {
\r
4843 tags = PGNTags(&gameInfo);
\r
4844 TagsPopUp(tags, CmailMsg());
\r
4847 SAY("computer starts playing black");
\r
4850 case IDM_Match: // [HGM] match: flows into next case, after setting Match Mode and nr of Games
\r
4851 MatchEvent(2); // distinguish from command-line-triggered case (matchMode=1)
\r
4854 case IDM_TwoMachines:
\r
4855 TwoMachinesEvent();
\r
4857 * refresh the tags dialog only if it's visible
\r
4859 if (gameMode == TwoMachinesPlay && IsWindowVisible(editTagsDialog)) {
\r
4861 tags = PGNTags(&gameInfo);
\r
4862 TagsPopUp(tags, CmailMsg());
\r
4865 SAY("computer starts playing both sides");
\r
4868 case IDM_AnalysisMode:
\r
4869 if (!first.analysisSupport) {
\r
4870 snprintf(buf, MSG_SIZ, _("%s does not support analysis"), first.tidy);
\r
4871 DisplayError(buf, 0);
\r
4873 SAY("analyzing current position");
\r
4874 /* [DM] icsEngineAnlyze [HGM] Why is this front-end??? */
\r
4875 if (appData.icsActive) {
\r
4876 if (gameMode != IcsObserving) {
\r
4877 snprintf(buf, MSG_SIZ, "You are not observing a game");
\r
4878 DisplayError(buf, 0);
\r
4879 /* secure check */
\r
4880 if (appData.icsEngineAnalyze) {
\r
4881 if (appData.debugMode)
\r
4882 fprintf(debugFP, "Found unexpected active ICS engine analyze \n");
\r
4883 ExitAnalyzeMode();
\r
4889 /* if enable, user want disable icsEngineAnalyze */
\r
4890 if (appData.icsEngineAnalyze) {
\r
4891 ExitAnalyzeMode();
\r
4895 appData.icsEngineAnalyze = TRUE;
\r
4896 if (appData.debugMode) fprintf(debugFP, "ICS engine analyze starting...\n");
\r
4899 if (!appData.showThinking) ToggleShowThinking();
\r
4900 AnalyzeModeEvent();
\r
4904 case IDM_AnalyzeFile:
\r
4905 if (!first.analysisSupport) {
\r
4906 char buf[MSG_SIZ];
\r
4907 snprintf(buf, MSG_SIZ, _("%s does not support analysis"), first.tidy);
\r
4908 DisplayError(buf, 0);
\r
4910 if (!appData.showThinking) ToggleShowThinking();
\r
4911 AnalyzeFileEvent();
\r
4912 LoadGameDialog(hwnd, _("Analyze Game from File"));
\r
4913 AnalysisPeriodicEvent(1);
\r
4917 case IDM_IcsClient:
\r
4921 case IDM_EditGame:
\r
4922 case IDM_EditGame2:
\r
4927 case IDM_EditPosition:
\r
4928 case IDM_EditPosition2:
\r
4929 EditPositionEvent();
\r
4930 SAY("enter a FEN string or setup a position on the board using the control R pop up menu");
\r
4933 case IDM_Training:
\r
4937 case IDM_ShowGameList:
\r
4938 ShowGameListProc();
\r
4941 case IDM_EditProgs1:
\r
4942 EditTagsPopUp(firstChessProgramNames, &firstChessProgramNames);
\r
4945 case IDM_EditProgs2:
\r
4946 LoadEnginePopUp(hwndMain);
\r
4947 // EditTagsPopUp(secondChessProgramNames, &secondChessProgramNames);
\r
4950 case IDM_EditServers:
\r
4951 EditTagsPopUp(icsNames, &icsNames);
\r
4954 case IDM_EditTags:
\r
4959 case IDM_EditBook:
\r
4963 case IDM_EditComment:
\r
4965 if (commentUp && editComment) {
\r
4968 EditCommentEvent();
\r
4988 case IDM_CallFlag:
\r
5008 case IDM_StopObserving:
\r
5009 StopObservingEvent();
\r
5012 case IDM_StopExamining:
\r
5013 StopExaminingEvent();
\r
5017 UploadGameEvent();
\r
5020 case IDM_TypeInMove:
\r
5021 TypeInEvent('\000');
\r
5024 case IDM_TypeInName:
\r
5025 PopUpNameDialog('\000');
\r
5028 case IDM_Backward:
\r
5030 SetFocus(hwndMain);
\r
5037 SetFocus(hwndMain);
\r
5042 SetFocus(hwndMain);
\r
5047 SetFocus(hwndMain);
\r
5051 RevertEvent(FALSE);
\r
5054 case IDM_Annotate: // [HGM] vari: revert with annotation
\r
5055 RevertEvent(TRUE);
\r
5058 case IDM_TruncateGame:
\r
5059 TruncateGameEvent();
\r
5066 case IDM_RetractMove:
\r
5067 RetractMoveEvent();
\r
5070 case IDM_FlipView:
\r
5071 flipView = !flipView;
\r
5072 DrawPosition(FALSE, NULL);
\r
5075 case IDM_FlipClock:
\r
5076 flipClock = !flipClock;
\r
5077 DisplayBothClocks();
\r
5081 case IDM_MuteSounds:
\r
5082 mute = !mute; // [HGM] mute: keep track of global muting variable
\r
5083 CheckMenuItem(GetMenu(hwndMain),IDM_MuteSounds,
\r
5084 MF_BYCOMMAND|(mute?MF_CHECKED:MF_UNCHECKED));
\r
5087 case IDM_GeneralOptions:
\r
5088 GeneralOptionsPopup(hwnd);
\r
5089 DrawPosition(TRUE, NULL);
\r
5092 case IDM_BoardOptions:
\r
5093 BoardOptionsPopup(hwnd);
\r
5096 case IDM_EnginePlayOptions:
\r
5097 EnginePlayOptionsPopup(hwnd);
\r
5100 case IDM_Engine1Options:
\r
5101 EngineOptionsPopup(hwnd, &first);
\r
5104 case IDM_Engine2Options:
\r
5106 if(WaitForEngine(&second, SettingsMenuIfReady)) break;
\r
5107 EngineOptionsPopup(hwnd, &second);
\r
5110 case IDM_OptionsUCI:
\r
5111 UciOptionsPopup(hwnd);
\r
5115 TourneyPopup(hwnd);
\r
5118 case IDM_IcsOptions:
\r
5119 IcsOptionsPopup(hwnd);
\r
5123 FontsOptionsPopup(hwnd);
\r
5127 SoundOptionsPopup(hwnd);
\r
5130 case IDM_CommPort:
\r
5131 CommPortOptionsPopup(hwnd);
\r
5134 case IDM_LoadOptions:
\r
5135 LoadOptionsPopup(hwnd);
\r
5138 case IDM_SaveOptions:
\r
5139 SaveOptionsPopup(hwnd);
\r
5142 case IDM_TimeControl:
\r
5143 TimeControlOptionsPopup(hwnd);
\r
5146 case IDM_SaveSettings:
\r
5147 SaveSettings(settingsFileName);
\r
5150 case IDM_SaveSettingsOnExit:
\r
5151 saveSettingsOnExit = !saveSettingsOnExit;
\r
5152 (void) CheckMenuItem(GetMenu(hwndMain), IDM_SaveSettingsOnExit,
\r
5153 MF_BYCOMMAND|(saveSettingsOnExit ?
\r
5154 MF_CHECKED : MF_UNCHECKED));
\r
5165 case IDM_AboutGame:
\r
5170 appData.debugMode = !appData.debugMode;
\r
5171 if (appData.debugMode) {
\r
5172 char dir[MSG_SIZ];
\r
5173 GetCurrentDirectory(MSG_SIZ, dir);
\r
5174 SetCurrentDirectory(installDir);
\r
5175 debugFP = fopen(appData.nameOfDebugFile, "w");
\r
5176 SetCurrentDirectory(dir);
\r
5177 setbuf(debugFP, NULL);
\r
5184 case IDM_HELPCONTENTS:
\r
5185 if (!MyHelp (hwnd, "winboard.hlp", HELP_KEY,(DWORD)(LPSTR)"CONTENTS") &&
\r
5186 !HtmlHelp(hwnd, "winboard.chm", 0, 0) ) {
\r
5187 MessageBox (GetFocus(),
\r
5188 _("Unable to activate help"),
\r
5189 szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);
\r
5193 case IDM_HELPSEARCH:
\r
5194 if (!MyHelp (hwnd, "winboard.hlp", HELP_PARTIALKEY, (DWORD)(LPSTR)"") &&
\r
5195 !HtmlHelp(hwnd, "winboard.chm", 0, 0) ) {
\r
5196 MessageBox (GetFocus(),
\r
5197 _("Unable to activate help"),
\r
5198 szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);
\r
5202 case IDM_HELPHELP:
\r
5203 if(!WinHelp(hwnd, (LPSTR)NULL, HELP_HELPONHELP, 0)) {
\r
5204 MessageBox (GetFocus(),
\r
5205 _("Unable to activate help"),
\r
5206 szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);
\r
5211 lpProc = MakeProcInstance((FARPROC)About, hInst);
\r
5213 (gameInfo.event && strcmp(gameInfo.event, "Easter Egg Hunt") == 0) ?
\r
5214 "AboutBox2" : "AboutBox", hwnd, (DLGPROC)lpProc);
\r
5215 FreeProcInstance(lpProc);
\r
5218 case IDM_DirectCommand1:
\r
5219 AskQuestionEvent(_("Direct Command"),
\r
5220 _("Send to chess program:"), "", "1");
\r
5222 case IDM_DirectCommand2:
\r
5223 AskQuestionEvent(_("Direct Command"),
\r
5224 _("Send to second chess program:"), "", "2");
\r
5227 case EP_WhitePawn:
\r
5228 EditPositionMenuEvent(WhitePawn, fromX, fromY);
\r
5229 fromX = fromY = -1;
\r
5232 case EP_WhiteKnight:
\r
5233 EditPositionMenuEvent(WhiteKnight, fromX, fromY);
\r
5234 fromX = fromY = -1;
\r
5237 case EP_WhiteBishop:
\r
5238 EditPositionMenuEvent(WhiteBishop, fromX, fromY);
\r
5239 fromX = fromY = -1;
\r
5242 case EP_WhiteRook:
\r
5243 EditPositionMenuEvent(WhiteRook, fromX, fromY);
\r
5244 fromX = fromY = -1;
\r
5247 case EP_WhiteQueen:
\r
5248 EditPositionMenuEvent(WhiteQueen, fromX, fromY);
\r
5249 fromX = fromY = -1;
\r
5252 case EP_WhiteFerz:
\r
5253 EditPositionMenuEvent(WhiteFerz, fromX, fromY);
\r
5254 fromX = fromY = -1;
\r
5257 case EP_WhiteWazir:
\r
5258 EditPositionMenuEvent(WhiteWazir, fromX, fromY);
\r
5259 fromX = fromY = -1;
\r
5262 case EP_WhiteAlfil:
\r
5263 EditPositionMenuEvent(WhiteAlfil, fromX, fromY);
\r
5264 fromX = fromY = -1;
\r
5267 case EP_WhiteCannon:
\r
5268 EditPositionMenuEvent(WhiteCannon, fromX, fromY);
\r
5269 fromX = fromY = -1;
\r
5272 case EP_WhiteCardinal:
\r
5273 EditPositionMenuEvent(WhiteAngel, fromX, fromY);
\r
5274 fromX = fromY = -1;
\r
5277 case EP_WhiteMarshall:
\r
5278 EditPositionMenuEvent(WhiteMarshall, fromX, fromY);
\r
5279 fromX = fromY = -1;
\r
5282 case EP_WhiteKing:
\r
5283 EditPositionMenuEvent(WhiteKing, fromX, fromY);
\r
5284 fromX = fromY = -1;
\r
5287 case EP_BlackPawn:
\r
5288 EditPositionMenuEvent(BlackPawn, fromX, fromY);
\r
5289 fromX = fromY = -1;
\r
5292 case EP_BlackKnight:
\r
5293 EditPositionMenuEvent(BlackKnight, fromX, fromY);
\r
5294 fromX = fromY = -1;
\r
5297 case EP_BlackBishop:
\r
5298 EditPositionMenuEvent(BlackBishop, fromX, fromY);
\r
5299 fromX = fromY = -1;
\r
5302 case EP_BlackRook:
\r
5303 EditPositionMenuEvent(BlackRook, fromX, fromY);
\r
5304 fromX = fromY = -1;
\r
5307 case EP_BlackQueen:
\r
5308 EditPositionMenuEvent(BlackQueen, fromX, fromY);
\r
5309 fromX = fromY = -1;
\r
5312 case EP_BlackFerz:
\r
5313 EditPositionMenuEvent(BlackFerz, fromX, fromY);
\r
5314 fromX = fromY = -1;
\r
5317 case EP_BlackWazir:
\r
5318 EditPositionMenuEvent(BlackWazir, fromX, fromY);
\r
5319 fromX = fromY = -1;
\r
5322 case EP_BlackAlfil:
\r
5323 EditPositionMenuEvent(BlackAlfil, fromX, fromY);
\r
5324 fromX = fromY = -1;
\r
5327 case EP_BlackCannon:
\r
5328 EditPositionMenuEvent(BlackCannon, fromX, fromY);
\r
5329 fromX = fromY = -1;
\r
5332 case EP_BlackCardinal:
\r
5333 EditPositionMenuEvent(BlackAngel, fromX, fromY);
\r
5334 fromX = fromY = -1;
\r
5337 case EP_BlackMarshall:
\r
5338 EditPositionMenuEvent(BlackMarshall, fromX, fromY);
\r
5339 fromX = fromY = -1;
\r
5342 case EP_BlackKing:
\r
5343 EditPositionMenuEvent(BlackKing, fromX, fromY);
\r
5344 fromX = fromY = -1;
\r
5347 case EP_EmptySquare:
\r
5348 EditPositionMenuEvent(EmptySquare, fromX, fromY);
\r
5349 fromX = fromY = -1;
\r
5352 case EP_ClearBoard:
\r
5353 EditPositionMenuEvent(ClearBoard, fromX, fromY);
\r
5354 fromX = fromY = -1;
\r
5358 EditPositionMenuEvent(WhitePlay, fromX, fromY);
\r
5359 fromX = fromY = -1;
\r
5363 EditPositionMenuEvent(BlackPlay, fromX, fromY);
\r
5364 fromX = fromY = -1;
\r
5368 EditPositionMenuEvent(PromotePiece, fromX, fromY);
\r
5369 fromX = fromY = -1;
\r
5373 EditPositionMenuEvent(DemotePiece, fromX, fromY);
\r
5374 fromX = fromY = -1;
\r
5378 DropMenuEvent(WhitePawn, fromX, fromY);
\r
5379 fromX = fromY = -1;
\r
5383 DropMenuEvent(WhiteKnight, fromX, fromY);
\r
5384 fromX = fromY = -1;
\r
5388 DropMenuEvent(WhiteBishop, fromX, fromY);
\r
5389 fromX = fromY = -1;
\r
5393 DropMenuEvent(WhiteRook, fromX, fromY);
\r
5394 fromX = fromY = -1;
\r
5398 DropMenuEvent(WhiteQueen, fromX, fromY);
\r
5399 fromX = fromY = -1;
\r
5403 barbaric = 0; appData.language = "";
\r
5404 TranslateMenus(0);
\r
5405 CheckMenuItem(GetMenu(hwndMain), lastChecked, MF_BYCOMMAND|MF_UNCHECKED);
\r
5406 CheckMenuItem(GetMenu(hwndMain), IDM_English, MF_BYCOMMAND|MF_CHECKED);
\r
5407 lastChecked = wmId;
\r
5411 if(wmId > IDM_English && wmId < IDM_English+20) {
\r
5412 LoadLanguageFile(languageFile[wmId - IDM_English - 1]);
\r
5413 TranslateMenus(0);
\r
5414 CheckMenuItem(GetMenu(hwndMain), lastChecked, MF_BYCOMMAND|MF_UNCHECKED);
\r
5415 CheckMenuItem(GetMenu(hwndMain), wmId, MF_BYCOMMAND|MF_CHECKED);
\r
5416 lastChecked = wmId;
\r
5419 return (DefWindowProc(hwnd, message, wParam, lParam));
\r
5425 case CLOCK_TIMER_ID:
\r
5426 KillTimer(hwnd, clockTimerEvent); /* Simulate one-shot timer as in X */
\r
5427 clockTimerEvent = 0;
\r
5428 DecrementClocks(); /* call into back end */
\r
5430 case LOAD_GAME_TIMER_ID:
\r
5431 KillTimer(hwnd, loadGameTimerEvent); /* Simulate one-shot timer as in X*/
\r
5432 loadGameTimerEvent = 0;
\r
5433 AutoPlayGameLoop(); /* call into back end */
\r
5435 case ANALYSIS_TIMER_ID:
\r
5436 if ((gameMode == AnalyzeMode || gameMode == AnalyzeFile
\r
5437 || appData.icsEngineAnalyze) && appData.periodicUpdates) {
\r
5438 AnalysisPeriodicEvent(0);
\r
5440 KillTimer(hwnd, analysisTimerEvent);
\r
5441 analysisTimerEvent = 0;
\r
5444 case DELAYED_TIMER_ID:
\r
5445 KillTimer(hwnd, delayedTimerEvent);
\r
5446 delayedTimerEvent = 0;
\r
5447 delayedTimerCallback();
\r
5452 case WM_USER_Input:
\r
5453 InputEvent(hwnd, message, wParam, lParam);
\r
5456 /* [AS] Also move "attached" child windows */
\r
5457 case WM_WINDOWPOSCHANGING:
\r
5459 if( hwnd == hwndMain && appData.useStickyWindows ) {
\r
5460 LPWINDOWPOS lpwp = (LPWINDOWPOS) lParam;
\r
5462 if( ((lpwp->flags & SWP_NOMOVE) == 0) && ((lpwp->flags & SWP_NOSIZE) != 0) ) {
\r
5463 /* Window is moving */
\r
5466 // GetWindowRect( hwnd, &rcMain ); //[HGM] sticky: in XP this returned new position, not old
\r
5467 rcMain.left = wpMain.x; // replace by these 4 lines to reconstruct old rect
\r
5468 rcMain.right = wpMain.x + wpMain.width;
\r
5469 rcMain.top = wpMain.y;
\r
5470 rcMain.bottom = wpMain.y + wpMain.height;
\r
5472 ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, moveHistoryDialog, &wpMoveHistory );
\r
5473 ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, evalGraphDialog, &wpEvalGraph );
\r
5474 ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, engineOutputDialog, &wpEngineOutput );
\r
5475 ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, gameListDialog, &wpGameList );
\r
5476 ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, hwndConsole, &wpConsole );
\r
5477 wpMain.x = lpwp->x;
\r
5478 wpMain.y = lpwp->y;
\r
5483 /* [AS] Snapping */
\r
5484 case WM_ENTERSIZEMOVE:
\r
5485 if(appData.debugMode) { fprintf(debugFP, "size-move\n"); }
\r
5486 if (hwnd == hwndMain) {
\r
5487 doingSizing = TRUE;
\r
5490 return OnEnterSizeMove( &sd, hwnd, wParam, lParam );
\r
5494 if(appData.debugMode) { fprintf(debugFP, "sizing\n"); }
\r
5495 if (hwnd == hwndMain) {
\r
5496 lastSizing = wParam;
\r
5501 if(appData.debugMode) { fprintf(debugFP, "moving\n"); }
\r
5502 return OnMoving( &sd, hwnd, wParam, lParam );
\r
5504 case WM_EXITSIZEMOVE:
\r
5505 if(appData.debugMode) { fprintf(debugFP, "exit size-move, size = %d\n", squareSize); }
\r
5506 if (hwnd == hwndMain) {
\r
5508 doingSizing = FALSE;
\r
5509 InvalidateRect(hwnd, &boardRect, FALSE);
\r
5510 GetClientRect(hwnd, &client);
\r
5511 ResizeBoard(client.right, client.bottom, lastSizing);
\r
5513 if(appData.debugMode) { fprintf(debugFP, "square size = %d\n", squareSize); }
\r
5515 return OnExitSizeMove( &sd, hwnd, wParam, lParam );
\r
5518 case WM_DESTROY: /* message: window being destroyed */
\r
5519 PostQuitMessage(0);
\r
5523 if (hwnd == hwndMain) {
\r
5528 default: /* Passes it on if unprocessed */
\r
5529 return (DefWindowProc(hwnd, message, wParam, lParam));
\r
5534 /*---------------------------------------------------------------------------*\
\r
5536 * Misc utility routines
\r
5538 \*---------------------------------------------------------------------------*/
\r
5541 * Decent random number generator, at least not as bad as Windows
\r
5542 * standard rand, which returns a value in the range 0 to 0x7fff.
\r
5544 unsigned int randstate;
\r
5549 randstate = randstate * 1664525 + 1013904223;
\r
5550 return (int) randstate & 0x7fffffff;
\r
5554 mysrandom(unsigned int seed)
\r
5561 * returns TRUE if user selects a different color, FALSE otherwise
\r
5565 ChangeColor(HWND hwnd, COLORREF *which)
\r
5567 static BOOL firstTime = TRUE;
\r
5568 static DWORD customColors[16];
\r
5570 COLORREF newcolor;
\r
5575 /* Make initial colors in use available as custom colors */
\r
5576 /* Should we put the compiled-in defaults here instead? */
\r
5578 customColors[i++] = lightSquareColor & 0xffffff;
\r
5579 customColors[i++] = darkSquareColor & 0xffffff;
\r
5580 customColors[i++] = whitePieceColor & 0xffffff;
\r
5581 customColors[i++] = blackPieceColor & 0xffffff;
\r
5582 customColors[i++] = highlightSquareColor & 0xffffff;
\r
5583 customColors[i++] = premoveHighlightColor & 0xffffff;
\r
5585 for (ccl = (ColorClass) 0; ccl < NColorClasses && i < 16; ccl++) {
\r
5586 customColors[i++] = textAttribs[ccl].color;
\r
5588 while (i < 16) customColors[i++] = RGB(255, 255, 255);
\r
5589 firstTime = FALSE;
\r
5592 cc.lStructSize = sizeof(cc);
\r
5593 cc.hwndOwner = hwnd;
\r
5594 cc.hInstance = NULL;
\r
5595 cc.rgbResult = (DWORD) (*which & 0xffffff);
\r
5596 cc.lpCustColors = (LPDWORD) customColors;
\r
5597 cc.Flags = CC_RGBINIT|CC_FULLOPEN;
\r
5599 if (!ChooseColor(&cc)) return FALSE;
\r
5601 newcolor = (COLORREF) (0x2000000 | cc.rgbResult);
\r
5602 if (newcolor == *which) return FALSE;
\r
5603 *which = newcolor;
\r
5607 InitDrawingColors();
\r
5608 InvalidateRect(hwnd, &boardRect, FALSE);
\r
5613 MyLoadSound(MySound *ms)
\r
5619 if (ms->data) free(ms->data);
\r
5622 switch (ms->name[0]) {
\r
5628 /* System sound from Control Panel. Don't preload here. */
\r
5632 if (ms->name[1] == NULLCHAR) {
\r
5633 /* "!" alone = silence */
\r
5636 /* Builtin wave resource. Error if not found. */
\r
5637 HANDLE h = FindResource(hInst, ms->name + 1, "WAVE");
\r
5638 if (h == NULL) break;
\r
5639 ms->data = (void *)LoadResource(hInst, h);
\r
5640 if (h == NULL) break;
\r
5645 /* .wav file. Error if not found. */
\r
5646 f = fopen(ms->name, "rb");
\r
5647 if (f == NULL) break;
\r
5648 if (fstat(fileno(f), &st) < 0) break;
\r
5649 ms->data = malloc(st.st_size);
\r
5650 if (fread(ms->data, st.st_size, 1, f) < 1) break;
\r
5656 char buf[MSG_SIZ];
\r
5657 snprintf(buf, MSG_SIZ, _("Error loading sound %s"), ms->name);
\r
5658 DisplayError(buf, GetLastError());
\r
5664 MyPlaySound(MySound *ms)
\r
5666 BOOLEAN ok = FALSE;
\r
5668 if(mute) return TRUE; // [HGM] mute: suppress all sound play when muted
\r
5669 switch (ms->name[0]) {
\r
5671 if(appData.debugMode) fprintf(debugFP, "silence\n");
\r
5676 /* System sound from Control Panel (deprecated feature).
\r
5677 "$" alone or an unset sound name gets default beep (still in use). */
\r
5678 if (ms->name[1]) {
\r
5679 ok = PlaySound(ms->name + 1, NULL, SND_ALIAS|SND_ASYNC);
\r
5681 if (!ok) ok = MessageBeep(MB_OK);
\r
5684 /* Builtin wave resource, or "!" alone for silence */
\r
5685 if (ms->name[1]) {
\r
5686 if (ms->data == NULL) return FALSE;
\r
5687 ok = PlaySound(ms->data, NULL, SND_MEMORY|SND_ASYNC);
\r
5693 /* .wav file. Error if not found. */
\r
5694 if (ms->data == NULL) return FALSE;
\r
5695 ok = PlaySound(ms->data, NULL, SND_MEMORY|SND_ASYNC);
\r
5698 /* Don't print an error: this can happen innocently if the sound driver
\r
5699 is busy; for instance, if another instance of WinBoard is playing
\r
5700 a sound at about the same time. */
\r
5706 OldOpenFileHook(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
5709 OPENFILENAME *ofn;
\r
5710 static UINT *number; /* gross that this is static */
\r
5712 switch (message) {
\r
5713 case WM_INITDIALOG: /* message: initialize dialog box */
\r
5714 /* Center the dialog over the application window */
\r
5715 ofn = (OPENFILENAME *) lParam;
\r
5716 if (ofn->Flags & OFN_ENABLETEMPLATE) {
\r
5717 number = (UINT *) ofn->lCustData;
\r
5718 SendMessage(GetDlgItem(hDlg, edt2), WM_SETTEXT, 0, (LPARAM) "");
\r
5722 CenterWindow (hDlg, GetWindow (hDlg, GW_OWNER));
\r
5723 Translate(hDlg, 1536);
\r
5724 return FALSE; /* Allow for further processing */
\r
5727 if ((LOWORD(wParam) == IDOK) && (number != NULL)) {
\r
5728 *number = GetDlgItemInt(hDlg, OPT_IndexNumberOld, &ok, FALSE);
\r
5730 return FALSE; /* Allow for further processing */
\r
5736 OpenFileHook(HWND hdlg, UINT uiMsg, WPARAM wParam, LPARAM lParam)
\r
5738 static UINT *number;
\r
5739 OPENFILENAME *ofname;
\r
5742 case WM_INITDIALOG:
\r
5743 Translate(hdlg, DLG_IndexNumber);
\r
5744 ofname = (OPENFILENAME *)lParam;
\r
5745 number = (UINT *)(ofname->lCustData);
\r
5748 ofnot = (OFNOTIFY *)lParam;
\r
5749 if (ofnot->hdr.code == CDN_FILEOK) {
\r
5750 *number = GetDlgItemInt(hdlg, OPT_IndexNumber, NULL, FALSE);
\r
5759 OpenFileDialog(HWND hwnd, char *write, char *defName, char *defExt, // [HGM] diag: type of 'write' now string
\r
5760 char *nameFilt, char *dlgTitle, UINT *number,
\r
5761 char fileTitle[MSG_SIZ], char fileName[MSG_SIZ])
\r
5763 OPENFILENAME openFileName;
\r
5764 char buf1[MSG_SIZ];
\r
5767 if (fileName == NULL) fileName = buf1;
\r
5768 if (defName == NULL) {
\r
5769 safeStrCpy(fileName, "*.", 3 );
\r
5770 strcat(fileName, defExt);
\r
5772 safeStrCpy(fileName, defName, MSG_SIZ );
\r
5774 if (fileTitle) safeStrCpy(fileTitle, "", MSG_SIZ );
\r
5775 if (number) *number = 0;
\r
5777 openFileName.lStructSize = sizeof(OPENFILENAME);
\r
5778 openFileName.hwndOwner = hwnd;
\r
5779 openFileName.hInstance = (HANDLE) hInst;
\r
5780 openFileName.lpstrFilter = nameFilt;
\r
5781 openFileName.lpstrCustomFilter = (LPSTR) NULL;
\r
5782 openFileName.nMaxCustFilter = 0L;
\r
5783 openFileName.nFilterIndex = 1L;
\r
5784 openFileName.lpstrFile = fileName;
\r
5785 openFileName.nMaxFile = MSG_SIZ;
\r
5786 openFileName.lpstrFileTitle = fileTitle;
\r
5787 openFileName.nMaxFileTitle = fileTitle ? MSG_SIZ : 0;
\r
5788 openFileName.lpstrInitialDir = NULL;
\r
5789 openFileName.lpstrTitle = dlgTitle;
\r
5790 openFileName.Flags = OFN_PATHMUSTEXIST | OFN_HIDEREADONLY
\r
5791 | (write[0] != 'r' ? 0 : OFN_FILEMUSTEXIST)
\r
5792 | (number ? OFN_ENABLETEMPLATE | OFN_ENABLEHOOK: 0)
\r
5793 | (oldDialog ? 0 : OFN_EXPLORER);
\r
5794 openFileName.nFileOffset = 0;
\r
5795 openFileName.nFileExtension = 0;
\r
5796 openFileName.lpstrDefExt = defExt;
\r
5797 openFileName.lCustData = (LONG) number;
\r
5798 openFileName.lpfnHook = oldDialog ?
\r
5799 (LPOFNHOOKPROC) OldOpenFileHook : (LPOFNHOOKPROC) OpenFileHook;
\r
5800 openFileName.lpTemplateName = (LPSTR)(oldDialog ? 1536 : DLG_IndexNumber);
\r
5802 if (write[0] != 'r' ? GetSaveFileName(&openFileName) :
\r
5803 GetOpenFileName(&openFileName)) {
\r
5804 /* open the file */
\r
5805 f = fopen(openFileName.lpstrFile, write);
\r
5807 MessageBox(hwnd, _("File open failed"), NULL,
\r
5808 MB_OK|MB_ICONEXCLAMATION);
\r
5812 int err = CommDlgExtendedError();
\r
5813 if (err != 0) DisplayError(_("Internal error in file dialog box"), err);
\r
5822 MenuPopup(HWND hwnd, POINT pt, HMENU hmenu, UINT def)
\r
5824 HMENU hmenuTrackPopup; /* floating pop-up menu */
\r
5827 * Get the first pop-up menu in the menu template. This is the
\r
5828 * menu that TrackPopupMenu displays.
\r
5830 hmenuTrackPopup = GetSubMenu(hmenu, 0);
\r
5831 TranslateOneMenu(10, hmenuTrackPopup);
\r
5833 SetMenuDefaultItem(hmenuTrackPopup, def, FALSE);
\r
5836 * TrackPopup uses screen coordinates, so convert the
\r
5837 * coordinates of the mouse click to screen coordinates.
\r
5839 ClientToScreen(hwnd, (LPPOINT) &pt);
\r
5841 /* Draw and track the floating pop-up menu. */
\r
5842 TrackPopupMenu(hmenuTrackPopup, TPM_CENTERALIGN | TPM_RIGHTBUTTON,
\r
5843 pt.x, pt.y, 0, hwnd, NULL);
\r
5845 /* Destroy the menu.*/
\r
5846 DestroyMenu(hmenu);
\r
5851 int sizeX, sizeY, newSizeX, newSizeY;
\r
5853 } ResizeEditPlusButtonsClosure;
\r
5856 ResizeEditPlusButtonsCallback(HWND hChild, LPARAM lparam)
\r
5858 ResizeEditPlusButtonsClosure *cl = (ResizeEditPlusButtonsClosure *)lparam;
\r
5862 if (hChild == cl->hText) return TRUE;
\r
5863 GetWindowRect(hChild, &rect); /* gives screen coords */
\r
5864 pt.x = rect.left + (cl->newSizeX - cl->sizeX)/2;
\r
5865 pt.y = rect.top + cl->newSizeY - cl->sizeY;
\r
5866 ScreenToClient(cl->hDlg, &pt);
\r
5867 cl->hdwp = DeferWindowPos(cl->hdwp, hChild, NULL,
\r
5868 pt.x, pt.y, rect.right - rect.left, rect.bottom - rect.top, SWP_NOZORDER);
\r
5872 /* Resize a dialog that has a (rich) edit field filling most of
\r
5873 the top, with a row of buttons below */
\r
5875 ResizeEditPlusButtons(HWND hDlg, HWND hText, int sizeX, int sizeY, int newSizeX, int newSizeY)
\r
5878 int newTextHeight, newTextWidth;
\r
5879 ResizeEditPlusButtonsClosure cl;
\r
5881 /*if (IsIconic(hDlg)) return;*/
\r
5882 if (newSizeX == sizeX && newSizeY == sizeY) return;
\r
5884 cl.hdwp = BeginDeferWindowPos(8);
\r
5886 GetWindowRect(hText, &rectText); /* gives screen coords */
\r
5887 newTextWidth = rectText.right - rectText.left + newSizeX - sizeX;
\r
5888 newTextHeight = rectText.bottom - rectText.top + newSizeY - sizeY;
\r
5889 if (newTextHeight < 0) {
\r
5890 newSizeY += -newTextHeight;
\r
5891 newTextHeight = 0;
\r
5893 cl.hdwp = DeferWindowPos(cl.hdwp, hText, NULL, 0, 0,
\r
5894 newTextWidth, newTextHeight, SWP_NOZORDER|SWP_NOMOVE);
\r
5900 cl.newSizeX = newSizeX;
\r
5901 cl.newSizeY = newSizeY;
\r
5902 EnumChildWindows(hDlg, ResizeEditPlusButtonsCallback, (LPARAM)&cl);
\r
5904 EndDeferWindowPos(cl.hdwp);
\r
5907 BOOL CenterWindowEx(HWND hwndChild, HWND hwndParent, int mode)
\r
5909 RECT rChild, rParent;
\r
5910 int wChild, hChild, wParent, hParent;
\r
5911 int wScreen, hScreen, xNew, yNew;
\r
5914 /* Get the Height and Width of the child window */
\r
5915 GetWindowRect (hwndChild, &rChild);
\r
5916 wChild = rChild.right - rChild.left;
\r
5917 hChild = rChild.bottom - rChild.top;
\r
5919 /* Get the Height and Width of the parent window */
\r
5920 GetWindowRect (hwndParent, &rParent);
\r
5921 wParent = rParent.right - rParent.left;
\r
5922 hParent = rParent.bottom - rParent.top;
\r
5924 /* Get the display limits */
\r
5925 hdc = GetDC (hwndChild);
\r
5926 wScreen = GetDeviceCaps (hdc, HORZRES);
\r
5927 hScreen = GetDeviceCaps (hdc, VERTRES);
\r
5928 ReleaseDC(hwndChild, hdc);
\r
5930 /* Calculate new X position, then adjust for screen */
\r
5931 xNew = rParent.left + ((wParent - wChild) /2);
\r
5934 } else if ((xNew+wChild) > wScreen) {
\r
5935 xNew = wScreen - wChild;
\r
5938 /* Calculate new Y position, then adjust for screen */
\r
5940 yNew = rParent.top + ((hParent - hChild) /2);
\r
5943 yNew = rParent.top + GetSystemMetrics( SM_CYCAPTION ) * 2 / 3;
\r
5948 } else if ((yNew+hChild) > hScreen) {
\r
5949 yNew = hScreen - hChild;
\r
5952 /* Set it, and return */
\r
5953 return SetWindowPos (hwndChild, NULL,
\r
5954 xNew, yNew, 0, 0, SWP_NOSIZE | SWP_NOZORDER);
\r
5957 /* Center one window over another */
\r
5958 BOOL CenterWindow (HWND hwndChild, HWND hwndParent)
\r
5960 return CenterWindowEx( hwndChild, hwndParent, 0 );
\r
5963 /*---------------------------------------------------------------------------*\
\r
5965 * Startup Dialog functions
\r
5967 \*---------------------------------------------------------------------------*/
\r
5969 InitComboStrings(HANDLE hwndCombo, char **cd)
\r
5971 SendMessage(hwndCombo, CB_RESETCONTENT, 0, 0);
\r
5973 while (*cd != NULL) {
\r
5974 SendMessage(hwndCombo, CB_ADDSTRING, 0, (LPARAM) T_(*cd));
\r
5980 InitComboStringsFromOption(HANDLE hwndCombo, char *str)
\r
5982 char buf1[MAX_ARG_LEN];
\r
5985 if (str[0] == '@') {
\r
5986 FILE* f = fopen(str + 1, "r");
\r
5988 DisplayFatalError(str + 1, errno, 2);
\r
5991 len = fread(buf1, 1, sizeof(buf1)-1, f);
\r
5993 buf1[len] = NULLCHAR;
\r
5997 SendMessage(hwndCombo, CB_RESETCONTENT, 0, 0);
\r
6000 char buf[MSG_SIZ];
\r
6001 char *end = strchr(str, '\n');
\r
6002 if (end == NULL) return;
\r
6003 memcpy(buf, str, end - str);
\r
6004 buf[end - str] = NULLCHAR;
\r
6005 SendMessage(hwndCombo, CB_ADDSTRING, 0, (LPARAM) buf);
\r
6011 SetStartupDialogEnables(HWND hDlg)
\r
6013 EnableWindow(GetDlgItem(hDlg, OPT_ChessEngineName),
\r
6014 IsDlgButtonChecked(hDlg, OPT_ChessEngine) ||
\r
6015 (appData.zippyPlay && IsDlgButtonChecked(hDlg, OPT_ChessServer)));
\r
6016 EnableWindow(GetDlgItem(hDlg, OPT_SecondChessEngineName),
\r
6017 IsDlgButtonChecked(hDlg, OPT_ChessEngine));
\r
6018 EnableWindow(GetDlgItem(hDlg, OPT_ChessServerName),
\r
6019 IsDlgButtonChecked(hDlg, OPT_ChessServer));
\r
6020 EnableWindow(GetDlgItem(hDlg, OPT_AdditionalOptions),
\r
6021 IsDlgButtonChecked(hDlg, OPT_AnyAdditional));
\r
6022 EnableWindow(GetDlgItem(hDlg, IDOK),
\r
6023 IsDlgButtonChecked(hDlg, OPT_ChessEngine) ||
\r
6024 IsDlgButtonChecked(hDlg, OPT_ChessServer) ||
\r
6025 IsDlgButtonChecked(hDlg, OPT_View));
\r
6029 QuoteForFilename(char *filename)
\r
6031 int dquote, space;
\r
6032 dquote = strchr(filename, '"') != NULL;
\r
6033 space = strchr(filename, ' ') != NULL;
\r
6034 if (dquote || space) {
\r
6046 InitEngineBox(HWND hDlg, HWND hwndCombo, char* nthcp, char* nthd, char* nthdir, char *nthnames)
\r
6048 char buf[MSG_SIZ];
\r
6051 InitComboStringsFromOption(hwndCombo, nthnames);
\r
6052 q = QuoteForFilename(nthcp);
\r
6053 snprintf(buf, MSG_SIZ, "%s%s%s", q, nthcp, q);
\r
6054 if (*nthdir != NULLCHAR) {
\r
6055 q = QuoteForFilename(nthdir);
\r
6056 snprintf(buf + strlen(buf), MSG_SIZ, " /%s=%s%s%s", nthd, q, nthdir, q);
\r
6058 if (*nthcp == NULLCHAR) {
\r
6059 SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) 0, (LPARAM) 0);
\r
6060 } else if (SendMessage(hwndCombo, CB_SELECTSTRING, (WPARAM) -1, (LPARAM) buf) == CB_ERR) {
\r
6061 SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) -1, (LPARAM) 0);
\r
6062 SendMessage(hwndCombo, WM_SETTEXT, (WPARAM) 0, (LPARAM) buf);
\r
6067 StartupDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
6069 char buf[MSG_SIZ];
\r
6073 switch (message) {
\r
6074 case WM_INITDIALOG:
\r
6075 /* Center the dialog */
\r
6076 CenterWindow (hDlg, GetDesktopWindow());
\r
6077 Translate(hDlg, DLG_Startup);
\r
6078 /* Initialize the dialog items */
\r
6079 InitEngineBox(hDlg, GetDlgItem(hDlg, OPT_ChessEngineName),
\r
6080 appData.firstChessProgram, "fd", appData.firstDirectory,
\r
6081 firstChessProgramNames);
\r
6082 InitEngineBox(hDlg, GetDlgItem(hDlg, OPT_SecondChessEngineName),
\r
6083 appData.secondChessProgram, singleList ? "fd" : "sd", appData.secondDirectory,
\r
6084 singleList ? firstChessProgramNames : secondChessProgramNames); //[HGM] single: use first list in second combo
\r
6085 hwndCombo = GetDlgItem(hDlg, OPT_ChessServerName);
\r
6086 InitComboStringsFromOption(hwndCombo, icsNames);
\r
6087 snprintf(buf, MSG_SIZ, "%s /icsport=%s", appData.icsHost, appData.icsPort);
\r
6088 if (*appData.icsHelper != NULLCHAR) {
\r
6089 char *q = QuoteForFilename(appData.icsHelper);
\r
6090 sprintf(buf + strlen(buf), " /icshelper=%s%s%s", q, appData.icsHelper, q);
\r
6092 if (*appData.icsHost == NULLCHAR) {
\r
6093 SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) 0, (LPARAM) 0);
\r
6094 /*SendMessage(hwndCombo, CB_SHOWDROPDOWN, (WPARAM) TRUE, (LPARAM) 0); !!too soon */
\r
6095 } else if (SendMessage(hwndCombo, CB_SELECTSTRING, (WPARAM) -1, (LPARAM) buf) == CB_ERR) {
\r
6096 SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) -1, (LPARAM) 0);
\r
6097 SendMessage(hwndCombo, WM_SETTEXT, (WPARAM) 0, (LPARAM) buf);
\r
6100 if (appData.icsActive) {
\r
6101 CheckDlgButton(hDlg, OPT_ChessServer, BST_CHECKED);
\r
6103 else if (appData.noChessProgram) {
\r
6104 CheckDlgButton(hDlg, OPT_View, BST_CHECKED);
\r
6107 CheckDlgButton(hDlg, OPT_ChessEngine, BST_CHECKED);
\r
6110 SetStartupDialogEnables(hDlg);
\r
6114 switch (LOWORD(wParam)) {
\r
6116 if (IsDlgButtonChecked(hDlg, OPT_ChessEngine)) {
\r
6117 safeStrCpy(buf, "/fcp=", sizeof(buf)/sizeof(buf[0]) );
\r
6118 GetDlgItemText(hDlg, OPT_ChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));
\r
6120 ParseArgs(StringGet, &p);
\r
6121 safeStrCpy(buf, singleList ? "/fcp=" : "/scp=", sizeof(buf)/sizeof(buf[0]) );
\r
6122 GetDlgItemText(hDlg, OPT_SecondChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));
\r
6124 SwapEngines(singleList); // temporarily swap first and second, to load a second 'first', ...
\r
6125 ParseArgs(StringGet, &p);
\r
6126 SwapEngines(singleList); // ... and then make it 'second'
\r
6127 appData.noChessProgram = FALSE;
\r
6128 appData.icsActive = FALSE;
\r
6129 } else if (IsDlgButtonChecked(hDlg, OPT_ChessServer)) {
\r
6130 safeStrCpy(buf, "/ics /icshost=", sizeof(buf)/sizeof(buf[0]) );
\r
6131 GetDlgItemText(hDlg, OPT_ChessServerName, buf + strlen(buf), sizeof(buf) - strlen(buf));
\r
6133 ParseArgs(StringGet, &p);
\r
6134 if (appData.zippyPlay) {
\r
6135 safeStrCpy(buf, "/fcp=", sizeof(buf)/sizeof(buf[0]) );
\r
6136 GetDlgItemText(hDlg, OPT_ChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));
\r
6138 ParseArgs(StringGet, &p);
\r
6140 } else if (IsDlgButtonChecked(hDlg, OPT_View)) {
\r
6141 appData.noChessProgram = TRUE;
\r
6142 appData.icsActive = FALSE;
\r
6144 MessageBox(hDlg, _("Choose an option, or cancel to exit"),
\r
6145 _("Option Error"), MB_OK|MB_ICONEXCLAMATION);
\r
6148 if (IsDlgButtonChecked(hDlg, OPT_AnyAdditional)) {
\r
6149 GetDlgItemText(hDlg, OPT_AdditionalOptions, buf, sizeof(buf));
\r
6151 ParseArgs(StringGet, &p);
\r
6153 EndDialog(hDlg, TRUE);
\r
6160 case IDM_HELPCONTENTS:
\r
6161 if (!WinHelp (hDlg, "winboard.hlp", HELP_KEY,(DWORD)(LPSTR)"CONTENTS")) {
\r
6162 MessageBox (GetFocus(),
\r
6163 _("Unable to activate help"),
\r
6164 szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);
\r
6169 SetStartupDialogEnables(hDlg);
\r
6177 /*---------------------------------------------------------------------------*\
\r
6179 * About box dialog functions
\r
6181 \*---------------------------------------------------------------------------*/
\r
6183 /* Process messages for "About" dialog box */
\r
6185 About(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
6187 switch (message) {
\r
6188 case WM_INITDIALOG: /* message: initialize dialog box */
\r
6189 /* Center the dialog over the application window */
\r
6190 CenterWindow (hDlg, GetWindow (hDlg, GW_OWNER));
\r
6191 SetDlgItemText(hDlg, ABOUTBOX_Version, programVersion);
\r
6192 Translate(hDlg, ABOUTBOX);
\r
6196 case WM_COMMAND: /* message: received a command */
\r
6197 if (LOWORD(wParam) == IDOK /* "OK" box selected? */
\r
6198 || LOWORD(wParam) == IDCANCEL) { /* System menu close command? */
\r
6199 EndDialog(hDlg, TRUE); /* Exit the dialog */
\r
6207 /*---------------------------------------------------------------------------*\
\r
6209 * Comment Dialog functions
\r
6211 \*---------------------------------------------------------------------------*/
\r
6214 CommentDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
6216 static HANDLE hwndText = NULL;
\r
6217 int len, newSizeX, newSizeY, flags;
\r
6218 static int sizeX, sizeY;
\r
6223 switch (message) {
\r
6224 case WM_INITDIALOG: /* message: initialize dialog box */
\r
6225 /* Initialize the dialog items */
\r
6226 Translate(hDlg, DLG_EditComment);
\r
6227 hwndText = GetDlgItem(hDlg, OPT_CommentText);
\r
6228 SetDlgItemText(hDlg, OPT_CommentText, commentText);
\r
6229 EnableWindow(GetDlgItem(hDlg, OPT_CancelComment), editComment);
\r
6230 EnableWindow(GetDlgItem(hDlg, OPT_ClearComment), editComment);
\r
6231 EnableWindow(GetDlgItem(hDlg, OPT_EditComment), !editComment);
\r
6232 SendMessage(hwndText, EM_SETREADONLY, !editComment, 0);
\r
6233 SetWindowText(hDlg, commentTitle);
\r
6234 if (editComment) {
\r
6235 SetFocus(hwndText);
\r
6237 SetFocus(GetDlgItem(hDlg, IDOK));
\r
6239 SendMessage(GetDlgItem(hDlg, OPT_CommentText),
\r
6240 WM_SETFONT, (WPARAM)font[boardSize][COMMENT_FONT]->hf,
\r
6241 MAKELPARAM(FALSE, 0));
\r
6242 /* Size and position the dialog */
\r
6243 if (!commentDialog) {
\r
6244 commentDialog = hDlg;
\r
6245 flags = SWP_NOZORDER;
\r
6246 GetClientRect(hDlg, &rect);
\r
6247 sizeX = rect.right;
\r
6248 sizeY = rect.bottom;
\r
6249 if (wpComment.x != CW_USEDEFAULT && wpComment.y != CW_USEDEFAULT &&
\r
6250 wpComment.width != CW_USEDEFAULT && wpComment.height != CW_USEDEFAULT) {
\r
6251 WINDOWPLACEMENT wp;
\r
6252 EnsureOnScreen(&wpComment.x, &wpComment.y, 0, 0);
\r
6253 wp.length = sizeof(WINDOWPLACEMENT);
\r
6255 wp.showCmd = SW_SHOW;
\r
6256 wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;
\r
6257 wp.rcNormalPosition.left = wpComment.x;
\r
6258 wp.rcNormalPosition.right = wpComment.x + wpComment.width;
\r
6259 wp.rcNormalPosition.top = wpComment.y;
\r
6260 wp.rcNormalPosition.bottom = wpComment.y + wpComment.height;
\r
6261 SetWindowPlacement(hDlg, &wp);
\r
6263 GetClientRect(hDlg, &rect);
\r
6264 newSizeX = rect.right;
\r
6265 newSizeY = rect.bottom;
\r
6266 ResizeEditPlusButtons(hDlg, hwndText, sizeX, sizeY,
\r
6267 newSizeX, newSizeY);
\r
6272 SendDlgItemMessage( hDlg, OPT_CommentText, EM_SETEVENTMASK, 0, ENM_MOUSEEVENTS | ENM_KEYEVENTS );
\r
6275 case WM_COMMAND: /* message: received a command */
\r
6276 switch (LOWORD(wParam)) {
\r
6278 if (editComment) {
\r
6280 /* Read changed options from the dialog box */
\r
6281 hwndText = GetDlgItem(hDlg, OPT_CommentText);
\r
6282 len = GetWindowTextLength(hwndText);
\r
6283 str = (char *) malloc(len + 1);
\r
6284 GetWindowText(hwndText, str, len + 1);
\r
6293 ReplaceComment(commentIndex, str);
\r
6300 case OPT_CancelComment:
\r
6304 case OPT_ClearComment:
\r
6305 SetDlgItemText(hDlg, OPT_CommentText, "");
\r
6308 case OPT_EditComment:
\r
6309 EditCommentEvent();
\r
6317 case WM_NOTIFY: // [HGM] vari: cloned from whistory.c
\r
6318 if( wParam == OPT_CommentText ) {
\r
6319 MSGFILTER * lpMF = (MSGFILTER *) lParam;
\r
6321 if( lpMF->msg == WM_RBUTTONDOWN && (lpMF->wParam & (MK_CONTROL | MK_SHIFT)) == 0 ||
\r
6322 lpMF->msg == WM_CHAR && lpMF->wParam == '\022' ) {
\r
6326 pt.x = LOWORD( lpMF->lParam );
\r
6327 pt.y = HIWORD( lpMF->lParam );
\r
6329 if(lpMF->msg == WM_CHAR) {
\r
6331 SendDlgItemMessage( hDlg, OPT_CommentText, EM_EXGETSEL, 0, (LPARAM) &sel );
\r
6332 index = sel.cpMin;
\r
6334 index = SendDlgItemMessage( hDlg, OPT_CommentText, EM_CHARFROMPOS, 0, (LPARAM) &pt );
\r
6336 hwndText = GetDlgItem(hDlg, OPT_CommentText); // cloned from above
\r
6337 len = GetWindowTextLength(hwndText);
\r
6338 str = (char *) malloc(len + 1);
\r
6339 GetWindowText(hwndText, str, len + 1);
\r
6340 ReplaceComment(commentIndex, str);
\r
6341 if(commentIndex != currentMove) ToNrEvent(commentIndex);
\r
6342 LoadVariation( index, str ); // [HGM] also does the actual moving to it, now
\r
6345 /* Zap the message for good: apparently, returning non-zero is not enough */
\r
6346 lpMF->msg = WM_USER;
\r
6354 newSizeX = LOWORD(lParam);
\r
6355 newSizeY = HIWORD(lParam);
\r
6356 ResizeEditPlusButtons(hDlg, hwndText, sizeX, sizeY, newSizeX, newSizeY);
\r
6361 case WM_GETMINMAXINFO:
\r
6362 /* Prevent resizing window too small */
\r
6363 mmi = (MINMAXINFO *) lParam;
\r
6364 mmi->ptMinTrackSize.x = 100;
\r
6365 mmi->ptMinTrackSize.y = 100;
\r
6372 EitherCommentPopUp(int index, char *title, char *str, BOOLEAN edit)
\r
6377 CheckMenuItem(GetMenu(hwndMain), IDM_Comment, edit ? MF_CHECKED : MF_UNCHECKED);
\r
6379 if (str == NULL) str = "";
\r
6380 p = (char *) malloc(2 * strlen(str) + 2);
\r
6383 if (*str == '\n') *q++ = '\r';
\r
6387 if (commentText != NULL) free(commentText);
\r
6389 commentIndex = index;
\r
6390 commentTitle = title;
\r
6392 editComment = edit;
\r
6394 if (commentDialog) {
\r
6395 SendMessage(commentDialog, WM_INITDIALOG, 0, 0);
\r
6396 if (!commentUp) ShowWindow(commentDialog, SW_SHOW);
\r
6398 lpProc = MakeProcInstance((FARPROC)CommentDialog, hInst);
\r
6399 CreateDialog(hInst, MAKEINTRESOURCE(DLG_EditComment),
\r
6400 hwndMain, (DLGPROC)lpProc);
\r
6401 FreeProcInstance(lpProc);
\r
6407 /*---------------------------------------------------------------------------*\
\r
6409 * Type-in move dialog functions
\r
6411 \*---------------------------------------------------------------------------*/
\r
6414 TypeInMoveDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
6416 char move[MSG_SIZ];
\r
6419 switch (message) {
\r
6420 case WM_INITDIALOG:
\r
6421 move[0] = (char) lParam;
\r
6422 move[1] = NULLCHAR;
\r
6423 CenterWindowEx(hDlg, GetWindow(hDlg, GW_OWNER), 1 );
\r
6424 Translate(hDlg, DLG_TypeInMove);
\r
6425 hInput = GetDlgItem(hDlg, OPT_Move);
\r
6426 SetWindowText(hInput, move);
\r
6428 SendMessage(hInput, EM_SETSEL, (WPARAM)9999, (LPARAM)9999);
\r
6432 switch (LOWORD(wParam)) {
\r
6435 shiftKey = GetKeyState(VK_SHIFT) < 0; // [HGM] remember last shift status
\r
6436 GetDlgItemText(hDlg, OPT_Move, move, sizeof(move));
6437 TypeInDoneEvent(move);
\r
6438 EndDialog(hDlg, TRUE);
\r
6441 EndDialog(hDlg, FALSE);
\r
6452 PopUpMoveDialog(char firstchar)
\r
6456 lpProc = MakeProcInstance((FARPROC)TypeInMoveDialog, hInst);
\r
6457 DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_TypeInMove),
\r
6458 hwndMain, (DLGPROC)lpProc, (LPARAM)firstchar);
\r
6459 FreeProcInstance(lpProc);
\r
6462 /*---------------------------------------------------------------------------*\
\r
6464 * Type-in name dialog functions
\r
6466 \*---------------------------------------------------------------------------*/
\r
6469 TypeInNameDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
6471 char move[MSG_SIZ];
\r
6474 switch (message) {
\r
6475 case WM_INITDIALOG:
\r
6476 move[0] = (char) lParam;
\r
6477 move[1] = NULLCHAR;
\r
6478 CenterWindowEx(hDlg, GetWindow(hDlg, GW_OWNER), 1 );
\r
6479 Translate(hDlg, DLG_TypeInName);
\r
6480 hInput = GetDlgItem(hDlg, OPT_Name);
\r
6481 SetWindowText(hInput, move);
\r
6483 SendMessage(hInput, EM_SETSEL, (WPARAM)9999, (LPARAM)9999);
\r
6487 switch (LOWORD(wParam)) {
\r
6489 GetDlgItemText(hDlg, OPT_Name, move, sizeof(move));
\r
6490 appData.userName = strdup(move);
\r
6493 if(gameMode == MachinePlaysWhite || gameMode == MachinePlaysBlack) {
\r
6494 snprintf(move, MSG_SIZ, "%s vs. %s", gameInfo.white, gameInfo.black);
\r
6495 DisplayTitle(move);
\r
6499 EndDialog(hDlg, TRUE);
\r
6502 EndDialog(hDlg, FALSE);
\r
6513 PopUpNameDialog(char firstchar)
\r
6517 lpProc = MakeProcInstance((FARPROC)TypeInNameDialog, hInst);
\r
6518 DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_TypeInName),
\r
6519 hwndMain, (DLGPROC)lpProc, (LPARAM)firstchar);
\r
6520 FreeProcInstance(lpProc);
\r
6523 /*---------------------------------------------------------------------------*\
\r
6527 \*---------------------------------------------------------------------------*/
\r
6529 /* Nonmodal error box */
\r
6530 LRESULT CALLBACK ErrorDialog(HWND hDlg, UINT message,
\r
6531 WPARAM wParam, LPARAM lParam);
\r
6534 ErrorPopUp(char *title, char *content)
\r
6538 BOOLEAN modal = hwndMain == NULL;
\r
6556 strncpy(errorTitle, title, sizeof(errorTitle));
\r
6557 errorTitle[sizeof(errorTitle) - 1] = '\0';
\r
6560 MessageBox(NULL, errorMessage, errorTitle, MB_OK|MB_ICONEXCLAMATION);
\r
6562 lpProc = MakeProcInstance((FARPROC)ErrorDialog, hInst);
\r
6563 CreateDialog(hInst, MAKEINTRESOURCE(DLG_Error),
\r
6564 hwndMain, (DLGPROC)lpProc);
\r
6565 FreeProcInstance(lpProc);
\r
6572 if (!appData.popupMoveErrors && moveErrorMessageUp) DisplayMessage("", "");
\r
6573 if (errorDialog == NULL) return;
\r
6574 DestroyWindow(errorDialog);
\r
6575 errorDialog = NULL;
\r
6576 if(errorExitStatus) ExitEvent(errorExitStatus);
\r
6580 ErrorDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
6585 switch (message) {
\r
6586 case WM_INITDIALOG:
\r
6587 GetWindowRect(hDlg, &rChild);
\r
6590 SetWindowPos(hDlg, NULL, rChild.left,
\r
6591 rChild.top + boardRect.top - (rChild.bottom - rChild.top),
\r
6592 0, 0, SWP_NOZORDER|SWP_NOSIZE);
\r
6596 [AS] It seems that the above code wants to move the dialog up in the "caption
\r
6597 area" of the main window, but it uses the dialog height as an hard-coded constant,
\r
6598 and it doesn't work when you resize the dialog.
\r
6599 For now, just give it a default position.
\r
6601 SetWindowPos(hDlg, NULL, boardRect.left+8, boardRect.top+8, 0, 0, SWP_NOZORDER|SWP_NOSIZE);
\r
6602 Translate(hDlg, DLG_Error);
\r
6604 errorDialog = hDlg;
\r
6605 SetWindowText(hDlg, errorTitle);
\r
6606 hwndText = GetDlgItem(hDlg, OPT_ErrorText);
\r
6607 SetDlgItemText(hDlg, OPT_ErrorText, errorMessage);
\r
6611 switch (LOWORD(wParam)) {
\r
6614 if (errorDialog == hDlg) errorDialog = NULL;
\r
6615 DestroyWindow(hDlg);
\r
6627 HWND gothicDialog = NULL;
\r
6630 GothicDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
6634 int height = GetSystemMetrics(SM_CYCAPTION)+GetSystemMetrics(SM_CYFRAME);
\r
6636 switch (message) {
\r
6637 case WM_INITDIALOG:
\r
6638 GetWindowRect(hDlg, &rChild);
\r
6640 SetWindowPos(hDlg, NULL, wpMain.x, wpMain.y-height, wpMain.width, height,
\r
6644 [AS] It seems that the above code wants to move the dialog up in the "caption
\r
6645 area" of the main window, but it uses the dialog height as an hard-coded constant,
\r
6646 and it doesn't work when you resize the dialog.
\r
6647 For now, just give it a default position.
\r
6649 gothicDialog = hDlg;
\r
6650 SetWindowText(hDlg, errorTitle);
\r
6651 hwndText = GetDlgItem(hDlg, OPT_ErrorText);
\r
6652 SetDlgItemText(hDlg, OPT_ErrorText, errorMessage);
\r
6656 switch (LOWORD(wParam)) {
\r
6659 if (errorDialog == hDlg) errorDialog = NULL;
\r
6660 DestroyWindow(hDlg);
\r
6672 GothicPopUp(char *title, VariantClass variant)
\r
6675 static char *lastTitle;
\r
6677 strncpy(errorTitle, title, sizeof(errorTitle));
\r
6678 errorTitle[sizeof(errorTitle) - 1] = '\0';
\r
6680 if(lastTitle != title && gothicDialog != NULL) {
\r
6681 DestroyWindow(gothicDialog);
\r
6682 gothicDialog = NULL;
\r
6684 if(variant != VariantNormal && gothicDialog == NULL) {
\r
6685 title = lastTitle;
\r
6686 lpProc = MakeProcInstance((FARPROC)GothicDialog, hInst);
\r
6687 CreateDialog(hInst, MAKEINTRESOURCE(DLG_Error),
\r
6688 hwndMain, (DLGPROC)lpProc);
\r
6689 FreeProcInstance(lpProc);
\r
6694 /*---------------------------------------------------------------------------*\
\r
6696 * Ics Interaction console functions
\r
6698 \*---------------------------------------------------------------------------*/
\r
6700 #define HISTORY_SIZE 64
\r
6701 static char *history[HISTORY_SIZE];
\r
6702 int histIn = 0, histP = 0;
\r
6705 SaveInHistory(char *cmd)
\r
6707 if (history[histIn] != NULL) {
\r
6708 free(history[histIn]);
\r
6709 history[histIn] = NULL;
\r
6711 if (*cmd == NULLCHAR) return;
\r
6712 history[histIn] = StrSave(cmd);
\r
6713 histIn = (histIn + 1) % HISTORY_SIZE;
\r
6714 if (history[histIn] != NULL) {
\r
6715 free(history[histIn]);
\r
6716 history[histIn] = NULL;
\r
6722 PrevInHistory(char *cmd)
\r
6725 if (histP == histIn) {
\r
6726 if (history[histIn] != NULL) free(history[histIn]);
\r
6727 history[histIn] = StrSave(cmd);
\r
6729 newhp = (histP - 1 + HISTORY_SIZE) % HISTORY_SIZE;
\r
6730 if (newhp == histIn || history[newhp] == NULL) return NULL;
\r
6732 return history[histP];
\r
6738 if (histP == histIn) return NULL;
\r
6739 histP = (histP + 1) % HISTORY_SIZE;
\r
6740 return history[histP];
\r
6744 LoadIcsTextMenu(IcsTextMenuEntry *e)
\r
6748 hmenu = LoadMenu(hInst, "TextMenu");
\r
6749 h = GetSubMenu(hmenu, 0);
\r
6751 if (strcmp(e->item, "-") == 0) {
\r
6752 AppendMenu(h, MF_SEPARATOR, 0, 0);
\r
6753 } else { // [HGM] re-written a bit to use only one AppendMenu call for both cases (| or no |)
\r
6754 int flags = MF_STRING, j = 0;
\r
6755 if (e->item[0] == '|') {
\r
6756 flags |= MF_MENUBARBREAK;
\r
6759 if(!strcmp(e->command, "none")) flags |= MF_GRAYED; // [HGM] chatclick: provide inactive dummy
\r
6760 AppendMenu(h, flags, IDM_CommandX + i, e->item + j);
\r
6768 WNDPROC consoleTextWindowProc;
\r
6771 CommandX(HWND hwnd, char *command, BOOLEAN getname, BOOLEAN immediate)
\r
6773 char buf[MSG_SIZ], name[MSG_SIZ];
\r
6774 HWND hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
6778 SetWindowText(hInput, command);
\r
6780 SendMessage(hInput, WM_CHAR, '\r', 0);
\r
6782 sel.cpMin = 999999;
\r
6783 sel.cpMax = 999999;
\r
6784 SendMessage(hInput, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
6789 SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);
\r
6790 if (sel.cpMin == sel.cpMax) {
\r
6791 /* Expand to surrounding word */
\r
6794 tr.chrg.cpMax = sel.cpMin;
\r
6795 tr.chrg.cpMin = --sel.cpMin;
\r
6796 if (sel.cpMin < 0) break;
\r
6797 tr.lpstrText = name;
\r
6798 SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr);
\r
6799 } while (isalpha(name[0]) || isdigit(name[0]) || name[0] == '-');
\r
6803 tr.chrg.cpMin = sel.cpMax;
\r
6804 tr.chrg.cpMax = ++sel.cpMax;
\r
6805 tr.lpstrText = name;
\r
6806 if (SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr) < 1) break;
\r
6807 } while (isalpha(name[0]) || isdigit(name[0]) || name[0] == '-');
\r
6810 if (sel.cpMax == sel.cpMin || sel.cpMax - sel.cpMin > MSG_SIZ/2) {
\r
6811 MessageBeep(MB_ICONEXCLAMATION);
\r
6815 tr.lpstrText = name;
\r
6816 SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr);
\r
6818 if (sel.cpMax - sel.cpMin > MSG_SIZ/2) {
\r
6819 MessageBeep(MB_ICONEXCLAMATION);
\r
6822 SendMessage(hwnd, EM_GETSELTEXT, 0, (LPARAM) name);
\r
6825 if(strstr(command, "%s")) snprintf(buf, MSG_SIZ, command, name); else
\r
6826 snprintf(buf, MSG_SIZ, "%s %s", command, name);
\r
6827 SetWindowText(hInput, buf);
\r
6828 SendMessage(hInput, WM_CHAR, '\r', 0);
\r
6830 if(!strcmp(command, "chat")) { ChatPopUp(name); return; }
\r
6831 snprintf(buf, MSG_SIZ, "%s %s ", command, name); /* trailing space */
\r
6832 SetWindowText(hInput, buf);
\r
6833 sel.cpMin = 999999;
\r
6834 sel.cpMax = 999999;
\r
6835 SendMessage(hInput, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
6841 ConsoleTextSubclass(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
\r
6846 switch (message) {
\r
6848 if (!(GetKeyState(VK_CONTROL) & ~1)) break;
\r
6849 if(wParam=='R') return 0;
\r
6852 SendMessage(hwnd, EM_LINESCROLL, 0, -999999);
\r
6855 sel.cpMin = 999999;
\r
6856 sel.cpMax = 999999;
\r
6857 SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
6858 SendMessage(hwnd, EM_SCROLLCARET, 0, 0);
\r
6863 if(wParam != '\022') {
\r
6864 if (wParam == '\t') {
\r
6865 if (GetKeyState(VK_SHIFT) < 0) {
\r
6867 if (IsIconic(hwndMain)) ShowWindow(hwndMain, SW_RESTORE);
\r
6868 if (buttonDesc[0].hwnd) {
\r
6869 SetFocus(buttonDesc[0].hwnd);
\r
6871 SetFocus(hwndMain);
\r
6875 SetFocus(GetDlgItem(hwndConsole, OPT_ConsoleInput));
\r
6878 hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
6879 JAWS_DELETE( SetFocus(hInput); )
\r
6880 SendMessage(hInput, message, wParam, lParam);
\r
6883 } // [HGM] navigate: for Ctrl+R, flow into next case (moved up here) to summon up menu
\r
6885 case WM_RBUTTONDOWN:
\r
6886 if (!(GetKeyState(VK_SHIFT) & ~1)) {
\r
6887 /* Move selection here if it was empty */
\r
6889 pt.x = LOWORD(lParam);
\r
6890 pt.y = HIWORD(lParam);
\r
6891 SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);
\r
6892 if (sel.cpMin == sel.cpMax) {
\r
6893 if(lParam != -1) sel.cpMin = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&pt); /*doc is wrong*/
\r
6894 sel.cpMax = sel.cpMin;
\r
6895 SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
6897 SendMessage(hwnd, EM_HIDESELECTION, FALSE, FALSE);
\r
6898 { // [HGM] chatclick: code moved here from WM_RBUTTONUP case, to have menu appear on down-click
\r
6900 HMENU hmenu = LoadIcsTextMenu(icsTextMenuEntry);
\r
6901 SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);
\r
6902 if (sel.cpMin == sel.cpMax) {
\r
6903 EnableMenuItem(hmenu, IDM_Copy, MF_BYCOMMAND|MF_GRAYED);
\r
6904 EnableMenuItem(hmenu, IDM_QuickPaste, MF_BYCOMMAND|MF_GRAYED);
\r
6906 if (!IsClipboardFormatAvailable(CF_TEXT)) {
\r
6907 EnableMenuItem(hmenu, IDM_Paste, MF_BYCOMMAND|MF_GRAYED);
\r
6909 pt.x = LOWORD(lParam)-30; // [HGM] chatclick: make menu pop up with pointer above upper-right item
\r
6910 pt.y = HIWORD(lParam)-10; // make it appear as if mouse moved there, so it will be selected on up-click
\r
6911 PostMessage(hwnd, WM_MOUSEMOVE, wParam, lParam+5);
\r
6912 MenuPopup(hwnd, pt, hmenu, -1);
\r
6916 case WM_RBUTTONUP:
\r
6917 if (GetKeyState(VK_SHIFT) & ~1) {
\r
6918 SendDlgItemMessage(hwndConsole, OPT_ConsoleText,
\r
6919 WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);
\r
6923 hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
6925 return SendMessage(hInput, message, wParam, lParam);
\r
6926 case WM_MBUTTONDOWN:
\r
6927 return SendMessage(hwnd, WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);
\r
6929 switch (LOWORD(wParam)) {
\r
6930 case IDM_QuickPaste:
\r
6932 SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);
\r
6933 if (sel.cpMin == sel.cpMax) {
\r
6934 MessageBeep(MB_ICONEXCLAMATION);
\r
6937 SendMessage(hwnd, WM_COPY, 0, 0);
\r
6938 hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
6939 SendMessage(hInput, WM_PASTE, 0, 0);
\r
6944 SendMessage(hwnd, WM_CUT, 0, 0);
\r
6947 SendMessage(hwnd, WM_PASTE, 0, 0);
\r
6950 SendMessage(hwnd, WM_COPY, 0, 0);
\r
6954 int i = LOWORD(wParam) - IDM_CommandX;
\r
6955 if (i >= 0 && i < ICS_TEXT_MENU_SIZE &&
\r
6956 icsTextMenuEntry[i].command != NULL) {
\r
6957 CommandX(hwnd, icsTextMenuEntry[i].command,
\r
6958 icsTextMenuEntry[i].getname,
\r
6959 icsTextMenuEntry[i].immediate);
\r
6967 return (*consoleTextWindowProc)(hwnd, message, wParam, lParam);
\r
6970 WNDPROC consoleInputWindowProc;
\r
6973 ConsoleInputSubclass(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
\r
6975 char buf[MSG_SIZ];
\r
6977 static BOOL sendNextChar = FALSE;
\r
6978 static BOOL quoteNextChar = FALSE;
\r
6979 InputSource *is = consoleInputSource;
\r
6983 switch (message) {
\r
6985 if (!appData.localLineEditing || sendNextChar) {
\r
6986 is->buf[0] = (CHAR) wParam;
\r
6988 SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);
\r
6989 sendNextChar = FALSE;
\r
6992 if (quoteNextChar) {
\r
6993 buf[0] = (char) wParam;
\r
6994 buf[1] = NULLCHAR;
\r
6995 SendMessage(hwnd, EM_REPLACESEL, TRUE, (LPARAM) buf);
\r
6996 quoteNextChar = FALSE;
\r
7000 case '\r': /* Enter key */
\r
7001 is->count = GetWindowText(hwnd, is->buf, INPUT_SOURCE_BUF_SIZE-1);
\r
7002 if (consoleEcho) SaveInHistory(is->buf);
\r
7003 is->buf[is->count++] = '\n';
\r
7004 is->buf[is->count] = NULLCHAR;
\r
7005 SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);
\r
7006 if (consoleEcho) {
\r
7007 ConsoleOutput(is->buf, is->count, TRUE);
\r
7008 } else if (appData.localLineEditing) {
\r
7009 ConsoleOutput("\n", 1, TRUE);
\r
7012 case '\033': /* Escape key */
\r
7013 SetWindowText(hwnd, "");
\r
7014 cf.cbSize = sizeof(CHARFORMAT);
\r
7015 cf.dwMask = CFM_COLOR|CFM_BOLD|CFM_ITALIC|CFM_UNDERLINE|CFM_STRIKEOUT;
\r
7016 if (consoleEcho) {
\r
7017 cf.crTextColor = textAttribs[ColorNormal].color;
\r
7019 cf.crTextColor = COLOR_ECHOOFF;
\r
7021 cf.dwEffects = textAttribs[ColorNormal].effects;
\r
7022 SendMessage(hwnd, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf);
\r
7024 case '\t': /* Tab key */
\r
7025 if (GetKeyState(VK_SHIFT) < 0) {
\r
7027 SetFocus(GetDlgItem(hwndConsole, OPT_ConsoleText));
\r
7030 if (IsIconic(hwndMain)) ShowWindow(hwndMain, SW_RESTORE);
\r
7031 if (buttonDesc[0].hwnd) {
\r
7032 SetFocus(buttonDesc[0].hwnd);
\r
7034 SetFocus(hwndMain);
\r
7038 case '\023': /* Ctrl+S */
\r
7039 sendNextChar = TRUE;
\r
7041 case '\021': /* Ctrl+Q */
\r
7042 quoteNextChar = TRUE;
\r
7052 GetWindowText(hwnd, buf, MSG_SIZ);
\r
7053 p = PrevInHistory(buf);
\r
7055 SetWindowText(hwnd, p);
\r
7056 sel.cpMin = 999999;
\r
7057 sel.cpMax = 999999;
\r
7058 SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
7063 p = NextInHistory();
\r
7065 SetWindowText(hwnd, p);
\r
7066 sel.cpMin = 999999;
\r
7067 sel.cpMax = 999999;
\r
7068 SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
7074 if (!(GetKeyState(VK_CONTROL) & ~1)) break;
\r
7078 SendDlgItemMessage(hwndConsole, OPT_ConsoleText, message, wParam, lParam);
\r
7082 case WM_MBUTTONDOWN:
\r
7083 SendDlgItemMessage(hwndConsole, OPT_ConsoleText,
\r
7084 WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);
\r
7086 case WM_RBUTTONUP:
\r
7087 if (GetKeyState(VK_SHIFT) & ~1) {
\r
7088 SendDlgItemMessage(hwndConsole, OPT_ConsoleText,
\r
7089 WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);
\r
7093 hmenu = LoadMenu(hInst, "InputMenu");
\r
7094 SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);
\r
7095 if (sel.cpMin == sel.cpMax) {
\r
7096 EnableMenuItem(hmenu, IDM_Copy, MF_BYCOMMAND|MF_GRAYED);
\r
7097 EnableMenuItem(hmenu, IDM_Cut, MF_BYCOMMAND|MF_GRAYED);
\r
7099 if (!IsClipboardFormatAvailable(CF_TEXT)) {
\r
7100 EnableMenuItem(hmenu, IDM_Paste, MF_BYCOMMAND|MF_GRAYED);
\r
7102 pt.x = LOWORD(lParam);
\r
7103 pt.y = HIWORD(lParam);
\r
7104 MenuPopup(hwnd, pt, hmenu, -1);
\r
7108 switch (LOWORD(wParam)) {
\r
7110 SendMessage(hwnd, EM_UNDO, 0, 0);
\r
7112 case IDM_SelectAll:
\r
7114 sel.cpMax = -1; /*999999?*/
\r
7115 SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
7118 SendMessage(hwnd, WM_CUT, 0, 0);
\r
7121 SendMessage(hwnd, WM_PASTE, 0, 0);
\r
7124 SendMessage(hwnd, WM_COPY, 0, 0);
\r
7129 return (*consoleInputWindowProc)(hwnd, message, wParam, lParam);
\r
7132 #define CO_MAX 100000
\r
7133 #define CO_TRIM 1000
\r
7136 ConsoleWndProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
7138 static SnapData sd;
\r
7139 HWND hText, hInput;
\r
7141 static int sizeX, sizeY;
\r
7142 int newSizeX, newSizeY;
\r
7146 hText = GetDlgItem(hDlg, OPT_ConsoleText);
\r
7147 hInput = GetDlgItem(hDlg, OPT_ConsoleInput);
\r
7149 switch (message) {
\r
7151 if (((NMHDR*)lParam)->code == EN_LINK)
\r
7153 ENLINK *pLink = (ENLINK*)lParam;
\r
7154 if (pLink->msg == WM_LBUTTONUP)
\r
7158 tr.chrg = pLink->chrg;
\r
7159 tr.lpstrText = malloc(1+tr.chrg.cpMax-tr.chrg.cpMin);
\r
7160 SendMessage(hText, EM_GETTEXTRANGE, 0, (LPARAM)&tr);
\r
7161 ShellExecute(NULL, "open", tr.lpstrText, NULL, NULL, SW_SHOW);
\r
7162 free(tr.lpstrText);
\r
7166 case WM_INITDIALOG: /* message: initialize dialog box */
\r
7167 hwndConsole = hDlg;
\r
7169 consoleTextWindowProc = (WNDPROC)
\r
7170 SetWindowLongPtr(hText, GWLP_WNDPROC, (LONG_PTR) ConsoleTextSubclass);
\r
7171 SendMessage(hText, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);
\r
7172 consoleInputWindowProc = (WNDPROC)
\r
7173 SetWindowLongPtr(hInput, GWLP_WNDPROC, (LONG_PTR) ConsoleInputSubclass);
\r
7174 SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);
\r
7175 Colorize(ColorNormal, TRUE);
\r
7176 SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &consoleCF);
\r
7177 ChangedConsoleFont();
\r
7178 GetClientRect(hDlg, &rect);
\r
7179 sizeX = rect.right;
\r
7180 sizeY = rect.bottom;
\r
7181 if (wpConsole.x != CW_USEDEFAULT && wpConsole.y != CW_USEDEFAULT &&
\r
7182 wpConsole.width != CW_USEDEFAULT && wpConsole.height != CW_USEDEFAULT) {
\r
7183 WINDOWPLACEMENT wp;
\r
7184 EnsureOnScreen(&wpConsole.x, &wpConsole.y, 0, 0);
\r
7185 wp.length = sizeof(WINDOWPLACEMENT);
\r
7187 wp.showCmd = SW_SHOW;
\r
7188 wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;
\r
7189 wp.rcNormalPosition.left = wpConsole.x;
\r
7190 wp.rcNormalPosition.right = wpConsole.x + wpConsole.width;
\r
7191 wp.rcNormalPosition.top = wpConsole.y;
\r
7192 wp.rcNormalPosition.bottom = wpConsole.y + wpConsole.height;
\r
7193 SetWindowPlacement(hDlg, &wp);
\r
7196 // [HGM] Chessknight's change 2004-07-13
\r
7197 else { /* Determine Defaults */
\r
7198 WINDOWPLACEMENT wp;
\r
7199 wpConsole.x = wpMain.width + 1;
\r
7200 wpConsole.y = wpMain.y;
\r
7201 wpConsole.width = screenWidth - wpMain.width;
\r
7202 wpConsole.height = wpMain.height;
\r
7203 EnsureOnScreen(&wpConsole.x, &wpConsole.y, 0, 0);
\r
7204 wp.length = sizeof(WINDOWPLACEMENT);
\r
7206 wp.showCmd = SW_SHOW;
\r
7207 wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;
\r
7208 wp.rcNormalPosition.left = wpConsole.x;
\r
7209 wp.rcNormalPosition.right = wpConsole.x + wpConsole.width;
\r
7210 wp.rcNormalPosition.top = wpConsole.y;
\r
7211 wp.rcNormalPosition.bottom = wpConsole.y + wpConsole.height;
\r
7212 SetWindowPlacement(hDlg, &wp);
\r
7215 // Allow hText to highlight URLs and send notifications on them
\r
7216 wMask = (WORD) SendMessage(hText, EM_GETEVENTMASK, 0, 0L);
\r
7217 SendMessage(hText, EM_SETEVENTMASK, 0, wMask | ENM_LINK);
\r
7218 SendMessage(hText, EM_AUTOURLDETECT, TRUE, 0L);
\r
7219 SetWindowLongPtr(hText, GWLP_USERDATA, 79); // initialize the text window's width
\r
7233 if (IsIconic(hDlg)) break;
\r
7234 newSizeX = LOWORD(lParam);
\r
7235 newSizeY = HIWORD(lParam);
\r
7236 if (sizeX != newSizeX || sizeY != newSizeY) {
\r
7237 RECT rectText, rectInput;
\r
7239 int newTextHeight, newTextWidth;
\r
7240 GetWindowRect(hText, &rectText);
\r
7241 newTextWidth = rectText.right - rectText.left + newSizeX - sizeX;
\r
7242 newTextHeight = rectText.bottom - rectText.top + newSizeY - sizeY;
\r
7243 if (newTextHeight < 0) {
\r
7244 newSizeY += -newTextHeight;
\r
7245 newTextHeight = 0;
\r
7247 SetWindowPos(hText, NULL, 0, 0,
\r
7248 newTextWidth, newTextHeight, SWP_NOZORDER|SWP_NOMOVE);
\r
7249 GetWindowRect(hInput, &rectInput); /* gives screen coords */
\r
7250 pt.x = rectInput.left;
\r
7251 pt.y = rectInput.top + newSizeY - sizeY;
\r
7252 ScreenToClient(hDlg, &pt);
\r
7253 SetWindowPos(hInput, NULL,
\r
7254 pt.x, pt.y, /* needs client coords */
\r
7255 rectInput.right - rectInput.left + newSizeX - sizeX,
\r
7256 rectInput.bottom - rectInput.top, SWP_NOZORDER);
\r
7262 case WM_GETMINMAXINFO:
\r
7263 /* Prevent resizing window too small */
\r
7264 mmi = (MINMAXINFO *) lParam;
\r
7265 mmi->ptMinTrackSize.x = 100;
\r
7266 mmi->ptMinTrackSize.y = 100;
\r
7269 /* [AS] Snapping */
\r
7270 case WM_ENTERSIZEMOVE:
\r
7271 return OnEnterSizeMove( &sd, hDlg, wParam, lParam );
\r
7274 return OnSizing( &sd, hDlg, wParam, lParam );
\r
7277 return OnMoving( &sd, hDlg, wParam, lParam );
\r
7279 case WM_EXITSIZEMOVE:
\r
7280 UpdateICSWidth(hText);
\r
7281 return OnExitSizeMove( &sd, hDlg, wParam, lParam );
\r
7284 return DefWindowProc(hDlg, message, wParam, lParam);
\r
7292 if (hwndConsole) return;
\r
7293 hCons = CreateDialog(hInst, szConsoleName, 0, NULL);
\r
7294 SendMessage(hCons, WM_INITDIALOG, 0, 0);
\r
7299 ConsoleOutput(char* data, int length, int forceVisible)
\r
7304 char buf[CO_MAX+1];
\r
7307 static int delayLF = 0;
\r
7308 CHARRANGE savesel, sel;
\r
7310 if (hwndConsole == NULL || length > CO_MAX-100 || length == 0) return;
\r
7318 while (length--) {
\r
7326 } else if (*p == '\007') {
\r
7327 MyPlaySound(&sounds[(int)SoundBell]);
\r
7334 hText = GetDlgItem(hwndConsole, OPT_ConsoleText);
\r
7335 SendMessage(hText, EM_HIDESELECTION, TRUE, FALSE);
\r
7336 /* Save current selection */
\r
7337 SendMessage(hText, EM_EXGETSEL, 0, (LPARAM)&savesel);
\r
7338 exlen = GetWindowTextLength(hText);
\r
7339 /* Find out whether current end of text is visible */
\r
7340 SendMessage(hText, EM_GETRECT, 0, (LPARAM) &rect);
\r
7341 SendMessage(hText, EM_POSFROMCHAR, (WPARAM) &pEnd, exlen);
\r
7342 /* Trim existing text if it's too long */
\r
7343 if (exlen + (q - buf) > CO_MAX) {
\r
7344 trim = (CO_TRIM > (q - buf)) ? CO_TRIM : (q - buf);
\r
7347 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
7348 SendMessage(hText, EM_REPLACESEL, 0, (LPARAM)"");
\r
7350 savesel.cpMin -= trim;
\r
7351 savesel.cpMax -= trim;
\r
7352 if (exlen < 0) exlen = 0;
\r
7353 if (savesel.cpMin < 0) savesel.cpMin = 0;
\r
7354 if (savesel.cpMax < savesel.cpMin) savesel.cpMax = savesel.cpMin;
\r
7356 /* Append the new text */
\r
7357 sel.cpMin = exlen;
\r
7358 sel.cpMax = exlen;
\r
7359 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
7360 SendMessage(hText, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&consoleCF);
\r
7361 SendMessage(hText, EM_REPLACESEL, 0, (LPARAM) buf);
\r
7362 if (forceVisible || exlen == 0 ||
\r
7363 (rect.left <= pEnd.x && pEnd.x < rect.right &&
\r
7364 rect.top <= pEnd.y && pEnd.y < rect.bottom)) {
\r
7365 /* Scroll to make new end of text visible if old end of text
\r
7366 was visible or new text is an echo of user typein */
\r
7367 sel.cpMin = 9999999;
\r
7368 sel.cpMax = 9999999;
\r
7369 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
7370 SendMessage(hText, EM_HIDESELECTION, FALSE, FALSE);
\r
7371 SendMessage(hText, EM_SCROLLCARET, 0, 0);
\r
7372 SendMessage(hText, EM_HIDESELECTION, TRUE, FALSE);
\r
7374 if (savesel.cpMax == exlen || forceVisible) {
\r
7375 /* Move insert point to new end of text if it was at the old
\r
7376 end of text or if the new text is an echo of user typein */
\r
7377 sel.cpMin = 9999999;
\r
7378 sel.cpMax = 9999999;
\r
7379 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
7381 /* Restore previous selection */
\r
7382 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&savesel);
\r
7384 SendMessage(hText, EM_HIDESELECTION, FALSE, FALSE);
\r
7391 DisplayHoldingsCount(HDC hdc, int x, int y, int rightAlign, int copyNumber)
\r
7395 COLORREF oldFg, oldBg;
\r
7400 snprintf(buf, sizeof(buf)/sizeof(buf[0]),"%d", copyNumber); else buf[0] = 0;
\r
7402 oldFg = SetTextColor(hdc, RGB(255, 255, 255)); /* white */
\r
7403 oldBg = SetBkColor(hdc, RGB(0, 0, 0)); /* black */
\r
7404 oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);
\r
7407 rect.right = x + squareSize;
\r
7409 rect.bottom = y + squareSize;
\r
7412 ExtTextOut(hdc, x + MESSAGE_LINE_LEFTMARGIN
\r
7413 + (rightAlign ? (squareSize*2)/3 : 0),
\r
7414 y, ETO_CLIPPED|ETO_OPAQUE,
\r
7415 &rect, str, strlen(str), NULL);
\r
7417 (void) SetTextColor(hdc, oldFg);
\r
7418 (void) SetBkColor(hdc, oldBg);
\r
7419 (void) SelectObject(hdc, oldFont);
\r
7423 DisplayAClock(HDC hdc, int timeRemaining, int highlight,
\r
7424 RECT *rect, char *color, char *flagFell)
\r
7428 COLORREF oldFg, oldBg;
\r
7431 if (appData.clockMode) {
\r
7433 snprintf(buf, sizeof(buf)/sizeof(buf[0]), "%c %s %s", color[0], TimeString(timeRemaining), flagFell);
\r
7435 snprintf(buf, sizeof(buf)/sizeof(buf[0]), "%s:%c%s %s", color, (logoHeight>0 ? 0 : ' '), TimeString(timeRemaining), flagFell);
\r
7442 oldFg = SetTextColor(hdc, RGB(255, 255, 255)); /* white */
\r
7443 oldBg = SetBkColor(hdc, RGB(0, 0, 0)); /* black */
\r
7445 oldFg = SetTextColor(hdc, RGB(0, 0, 0)); /* black */
\r
7446 oldBg = SetBkColor(hdc, RGB(255, 255, 255)); /* white */
\r
7448 oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);
\r
7452 ExtTextOut(hdc, rect->left + MESSAGE_LINE_LEFTMARGIN,
\r
7453 rect->top, ETO_CLIPPED|ETO_OPAQUE,
\r
7454 rect, str, strlen(str), NULL);
\r
7455 if(logoHeight > 0 && appData.clockMode) {
\r
7457 str += strlen(color)+2;
\r
7458 r.top = rect->top + logoHeight/2;
\r
7459 r.left = rect->left;
\r
7460 r.right = rect->right;
\r
7461 r.bottom = rect->bottom;
\r
7462 ExtTextOut(hdc, rect->left + MESSAGE_LINE_LEFTMARGIN,
\r
7463 r.top, ETO_CLIPPED|ETO_OPAQUE,
\r
7464 &r, str, strlen(str), NULL);
\r
7466 (void) SetTextColor(hdc, oldFg);
\r
7467 (void) SetBkColor(hdc, oldBg);
\r
7468 (void) SelectObject(hdc, oldFont);
\r
7473 DoReadFile(HANDLE hFile, char *buf, int count, DWORD *outCount,
\r
7479 if( count <= 0 ) {
\r
7480 if (appData.debugMode) {
\r
7481 fprintf( debugFP, "DoReadFile: trying to read past end of buffer, overflow = %d\n", count );
\r
7484 return ERROR_INVALID_USER_BUFFER;
\r
7487 ResetEvent(ovl->hEvent);
\r
7488 ovl->Offset = ovl->OffsetHigh = 0;
\r
7489 ok = ReadFile(hFile, buf, count, outCount, ovl);
\r
7493 err = GetLastError();
\r
7494 if (err == ERROR_IO_PENDING) {
\r
7495 ok = GetOverlappedResult(hFile, ovl, outCount, TRUE);
\r
7499 err = GetLastError();
\r
7506 DoWriteFile(HANDLE hFile, char *buf, int count, DWORD *outCount,
\r
7511 ResetEvent(ovl->hEvent);
\r
7512 ovl->Offset = ovl->OffsetHigh = 0;
\r
7513 ok = WriteFile(hFile, buf, count, outCount, ovl);
\r
7517 err = GetLastError();
\r
7518 if (err == ERROR_IO_PENDING) {
\r
7519 ok = GetOverlappedResult(hFile, ovl, outCount, TRUE);
\r
7523 err = GetLastError();
\r
7529 /* [AS] If input is line by line and a line exceed the buffer size, force an error */
\r
7530 void CheckForInputBufferFull( InputSource * is )
\r
7532 if( is->lineByLine && (is->next - is->buf) >= INPUT_SOURCE_BUF_SIZE ) {
\r
7533 /* Look for end of line */
\r
7534 char * p = is->buf;
\r
7536 while( p < is->next && *p != '\n' ) {
\r
7540 if( p >= is->next ) {
\r
7541 if (appData.debugMode) {
\r
7542 fprintf( debugFP, "Input line exceeded buffer size (source id=%lu)\n", is->id );
\r
7545 is->error = ERROR_BROKEN_PIPE; /* [AS] Just any non-successful code! */
\r
7546 is->count = (DWORD) -1;
\r
7547 is->next = is->buf;
\r
7553 InputThread(LPVOID arg)
\r
7558 is = (InputSource *) arg;
\r
7559 ovl.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
\r
7560 ovl.Internal = ovl.InternalHigh = ovl.Offset = ovl.OffsetHigh = 0;
\r
7561 while (is->hThread != NULL) {
\r
7562 is->error = DoReadFile(is->hFile, is->next,
\r
7563 INPUT_SOURCE_BUF_SIZE - (is->next - is->buf),
\r
7564 &is->count, &ovl);
\r
7565 if (is->error == NO_ERROR) {
\r
7566 is->next += is->count;
\r
7568 if (is->error == ERROR_BROKEN_PIPE) {
\r
7569 /* Correct for MS brain damage. EOF reading a pipe is not an error. */
\r
7572 is->count = (DWORD) -1;
\r
7573 /* [AS] The (is->count <= 0) check below is not useful for unsigned values! */
\r
7578 CheckForInputBufferFull( is );
\r
7580 SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);
\r
7582 if( is->count == ((DWORD) -1) ) break; /* [AS] */
\r
7584 if (is->count <= 0) break; /* Quit on EOF or error */
\r
7587 CloseHandle(ovl.hEvent);
\r
7588 CloseHandle(is->hFile);
\r
7590 if (appData.debugMode) {
\r
7591 fprintf( debugFP, "Input thread terminated (id=%lu, error=%d, count=%ld)\n", is->id, is->error, is->count );
\r
7598 /* Windows 95 beta 2 won't let you do overlapped i/o on a console or pipe */
\r
7600 NonOvlInputThread(LPVOID arg)
\r
7607 is = (InputSource *) arg;
\r
7608 while (is->hThread != NULL) {
\r
7609 is->error = ReadFile(is->hFile, is->next,
\r
7610 INPUT_SOURCE_BUF_SIZE - (is->next - is->buf),
\r
7611 &is->count, NULL) ? NO_ERROR : GetLastError();
\r
7612 if (is->error == NO_ERROR) {
\r
7613 /* Change CRLF to LF */
\r
7614 if (is->next > is->buf) {
\r
7616 i = is->count + 1;
\r
7624 if (prev == '\r' && *p == '\n') {
\r
7636 if (is->error == ERROR_BROKEN_PIPE) {
\r
7637 /* Correct for MS brain damage. EOF reading a pipe is not an error. */
\r
7640 is->count = (DWORD) -1;
\r
7644 CheckForInputBufferFull( is );
\r
7646 SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);
\r
7648 if( is->count == ((DWORD) -1) ) break; /* [AS] */
\r
7650 if (is->count < 0) break; /* Quit on error */
\r
7652 CloseHandle(is->hFile);
\r
7657 SocketInputThread(LPVOID arg)
\r
7661 is = (InputSource *) arg;
\r
7662 while (is->hThread != NULL) {
\r
7663 is->count = recv(is->sock, is->buf, INPUT_SOURCE_BUF_SIZE, 0);
\r
7664 if ((int)is->count == SOCKET_ERROR) {
\r
7665 is->count = (DWORD) -1;
\r
7666 is->error = WSAGetLastError();
\r
7668 is->error = NO_ERROR;
\r
7669 is->next += is->count;
\r
7670 if (is->count == 0 && is->second == is) {
\r
7671 /* End of file on stderr; quit with no message */
\r
7675 SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);
\r
7677 if( is->count == ((DWORD) -1) ) break; /* [AS] */
\r
7679 if (is->count <= 0) break; /* Quit on EOF or error */
\r
7685 InputEvent(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
\r
7689 is = (InputSource *) lParam;
\r
7690 if (is->lineByLine) {
\r
7691 /* Feed in lines one by one */
\r
7692 char *p = is->buf;
\r
7694 while (q < is->next) {
\r
7695 if (*q++ == '\n') {
\r
7696 (is->func)(is, is->closure, p, q - p, NO_ERROR);
\r
7701 /* Move any partial line to the start of the buffer */
\r
7703 while (p < is->next) {
\r
7708 if (is->error != NO_ERROR || is->count == 0) {
\r
7709 /* Notify backend of the error. Note: If there was a partial
\r
7710 line at the end, it is not flushed through. */
\r
7711 (is->func)(is, is->closure, is->buf, is->count, is->error);
\r
7714 /* Feed in the whole chunk of input at once */
\r
7715 (is->func)(is, is->closure, is->buf, is->count, is->error);
\r
7716 is->next = is->buf;
\r
7720 /*---------------------------------------------------------------------------*\
\r
7722 * Menu enables. Used when setting various modes.
\r
7724 \*---------------------------------------------------------------------------*/
\r
7732 GreyRevert(Boolean grey)
\r
7733 { // [HGM] vari: for retracting variations in local mode
\r
7734 HMENU hmenu = GetMenu(hwndMain);
\r
7735 EnableMenuItem(hmenu, IDM_Revert, MF_BYCOMMAND|(grey ? MF_GRAYED : MF_ENABLED));
\r
7736 EnableMenuItem(hmenu, IDM_Annotate, MF_BYCOMMAND|(grey ? MF_GRAYED : MF_ENABLED));
\r
7740 SetMenuEnables(HMENU hmenu, Enables *enab)
\r
7742 while (enab->item > 0) {
\r
7743 (void) EnableMenuItem(hmenu, enab->item, enab->flags);
\r
7748 Enables gnuEnables[] = {
\r
7749 { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },
\r
7750 { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },
\r
7751 { IDM_IcsClient, MF_BYCOMMAND|MF_GRAYED },
\r
7752 { IDM_Accept, MF_BYCOMMAND|MF_GRAYED },
\r
7753 { IDM_Decline, MF_BYCOMMAND|MF_GRAYED },
\r
7754 { IDM_Rematch, MF_BYCOMMAND|MF_GRAYED },
\r
7755 { IDM_Adjourn, MF_BYCOMMAND|MF_GRAYED },
\r
7756 { IDM_StopExamining, MF_BYCOMMAND|MF_GRAYED },
\r
7757 { IDM_StopObserving, MF_BYCOMMAND|MF_GRAYED },
\r
7758 { IDM_Upload, MF_BYCOMMAND|MF_GRAYED },
\r
7759 { IDM_Revert, MF_BYCOMMAND|MF_GRAYED },
\r
7760 { IDM_Annotate, MF_BYCOMMAND|MF_GRAYED },
\r
7761 { IDM_NewChat, MF_BYCOMMAND|MF_GRAYED },
\r
7763 // Needed to switch from ncp to GNU mode on Engine Load
\r
7764 { ACTION_POS, MF_BYPOSITION|MF_ENABLED },
\r
7765 { IDM_MachineWhite, MF_BYCOMMAND|MF_ENABLED },
\r
7766 { IDM_MachineBlack, MF_BYCOMMAND|MF_ENABLED },
\r
7767 { IDM_TwoMachines, MF_BYCOMMAND|MF_ENABLED },
\r
7768 { IDM_Match, MF_BYCOMMAND|MF_ENABLED },
\r
7769 { IDM_AnalysisMode, MF_BYCOMMAND|MF_ENABLED },
\r
7770 { IDM_AnalyzeFile, MF_BYCOMMAND|MF_ENABLED },
\r
7771 { IDM_Engine1Options, MF_BYCOMMAND|MF_ENABLED },
\r
7772 { IDM_Engine2Options, MF_BYCOMMAND|MF_ENABLED },
\r
7773 { IDM_TimeControl, MF_BYCOMMAND|MF_ENABLED },
\r
7774 { IDM_RetractMove, MF_BYCOMMAND|MF_ENABLED },
\r
7775 { IDM_MoveNow, MF_BYCOMMAND|MF_ENABLED },
\r
7776 { IDM_Hint, MF_BYCOMMAND|MF_ENABLED },
\r
7777 { IDM_Book, MF_BYCOMMAND|MF_ENABLED },
\r
7781 Enables icsEnables[] = {
\r
7782 { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },
\r
7783 { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },
\r
7784 { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },
\r
7785 { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },
\r
7786 { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },
\r
7787 { IDM_Match, MF_BYCOMMAND|MF_GRAYED },
\r
7788 { IDM_MachineBoth, MF_BYCOMMAND|MF_GRAYED },
\r
7789 { IDM_AnalysisMode, MF_BYCOMMAND|MF_ENABLED },
\r
7790 { IDM_AnalyzeFile, MF_BYCOMMAND|MF_GRAYED },
\r
7791 { IDM_TimeControl, MF_BYCOMMAND|MF_GRAYED },
\r
7792 { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },
\r
7793 { IDM_Hint, MF_BYCOMMAND|MF_GRAYED },
\r
7794 { IDM_Book, MF_BYCOMMAND|MF_GRAYED },
\r
7795 { IDM_EditProgs2, MF_BYCOMMAND|MF_GRAYED },
\r
7796 { IDM_IcsOptions, MF_BYCOMMAND|MF_ENABLED },
\r
7797 { IDM_Engine1Options, MF_BYCOMMAND|MF_GRAYED },
\r
7798 { IDM_Engine2Options, MF_BYCOMMAND|MF_GRAYED },
\r
7799 { IDM_Annotate, MF_BYCOMMAND|MF_GRAYED },
\r
7800 { IDM_Tourney, MF_BYCOMMAND|MF_GRAYED },
\r
7805 Enables zippyEnables[] = {
\r
7806 { IDM_MoveNow, MF_BYCOMMAND|MF_ENABLED },
\r
7807 { IDM_Hint, MF_BYCOMMAND|MF_ENABLED },
\r
7808 { IDM_Book, MF_BYCOMMAND|MF_ENABLED },
\r
7809 { IDM_Engine1Options, MF_BYCOMMAND|MF_ENABLED },
\r
7814 Enables ncpEnables[] = {
\r
7815 { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },
\r
7816 { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },
\r
7817 { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },
\r
7818 { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },
\r
7819 { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },
\r
7820 { IDM_Match, MF_BYCOMMAND|MF_GRAYED },
\r
7821 { IDM_AnalysisMode, MF_BYCOMMAND|MF_GRAYED },
\r
7822 { IDM_AnalyzeFile, MF_BYCOMMAND|MF_GRAYED },
\r
7823 { IDM_IcsClient, MF_BYCOMMAND|MF_GRAYED },
\r
7824 { ACTION_POS, MF_BYPOSITION|MF_GRAYED },
\r
7825 { IDM_Revert, MF_BYCOMMAND|MF_GRAYED },
\r
7826 { IDM_Annotate, MF_BYCOMMAND|MF_GRAYED },
\r
7827 { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },
\r
7828 { IDM_RetractMove, MF_BYCOMMAND|MF_GRAYED },
\r
7829 { IDM_TimeControl, MF_BYCOMMAND|MF_GRAYED },
\r
7830 { IDM_Hint, MF_BYCOMMAND|MF_GRAYED },
\r
7831 { IDM_Book, MF_BYCOMMAND|MF_GRAYED },
\r
7832 { IDM_MachineBoth, MF_BYCOMMAND|MF_GRAYED },
\r
7833 { IDM_NewChat, MF_BYCOMMAND|MF_GRAYED },
\r
7834 { IDM_Engine1Options, MF_BYCOMMAND|MF_GRAYED },
\r
7835 { IDM_Engine2Options, MF_BYCOMMAND|MF_GRAYED },
\r
7836 { IDM_Sounds, MF_BYCOMMAND|MF_GRAYED },
\r
7840 Enables trainingOnEnables[] = {
\r
7841 { IDM_EditComment, MF_BYCOMMAND|MF_GRAYED },
\r
7842 { IDM_Comment, MF_BYCOMMAND|MF_GRAYED },
\r
7843 { IDM_Pause, MF_BYCOMMAND|MF_GRAYED },
\r
7844 { IDM_Forward, MF_BYCOMMAND|MF_GRAYED },
\r
7845 { IDM_Backward, MF_BYCOMMAND|MF_GRAYED },
\r
7846 { IDM_ToEnd, MF_BYCOMMAND|MF_GRAYED },
\r
7847 { IDM_ToStart, MF_BYCOMMAND|MF_GRAYED },
\r
7848 { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },
\r
7849 { IDM_TruncateGame, MF_BYCOMMAND|MF_GRAYED },
\r
7853 Enables trainingOffEnables[] = {
\r
7854 { IDM_EditComment, MF_BYCOMMAND|MF_ENABLED },
\r
7855 { IDM_Comment, MF_BYCOMMAND|MF_ENABLED },
\r
7856 { IDM_Pause, MF_BYCOMMAND|MF_ENABLED },
\r
7857 { IDM_Forward, MF_BYCOMMAND|MF_ENABLED },
\r
7858 { IDM_Backward, MF_BYCOMMAND|MF_ENABLED },
\r
7859 { IDM_ToEnd, MF_BYCOMMAND|MF_ENABLED },
\r
7860 { IDM_ToStart, MF_BYCOMMAND|MF_ENABLED },
\r
7861 { IDM_MoveNow, MF_BYCOMMAND|MF_ENABLED },
\r
7862 { IDM_TruncateGame, MF_BYCOMMAND|MF_ENABLED },
\r
7866 /* These modify either ncpEnables or gnuEnables */
\r
7867 Enables cmailEnables[] = {
\r
7868 { IDM_MailMove, MF_BYCOMMAND|MF_ENABLED },
\r
7869 { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_ENABLED },
\r
7870 { ACTION_POS, MF_BYPOSITION|MF_ENABLED },
\r
7871 { IDM_CallFlag, MF_BYCOMMAND|MF_GRAYED },
\r
7872 { IDM_Draw, MF_BYCOMMAND|MF_ENABLED },
\r
7873 { IDM_Adjourn, MF_BYCOMMAND|MF_GRAYED },
\r
7874 { IDM_Abort, MF_BYCOMMAND|MF_GRAYED },
\r
7878 Enables machineThinkingEnables[] = {
\r
7879 { IDM_LoadGame, MF_BYCOMMAND|MF_GRAYED },
\r
7880 { IDM_LoadNextGame, MF_BYCOMMAND|MF_GRAYED },
\r
7881 { IDM_LoadPrevGame, MF_BYCOMMAND|MF_GRAYED },
\r
7882 { IDM_ReloadGame, MF_BYCOMMAND|MF_GRAYED },
\r
7883 { IDM_PasteGame, MF_BYCOMMAND|MF_GRAYED },
\r
7884 { IDM_LoadPosition, MF_BYCOMMAND|MF_GRAYED },
\r
7885 { IDM_LoadNextPosition, MF_BYCOMMAND|MF_GRAYED },
\r
7886 { IDM_LoadPrevPosition, MF_BYCOMMAND|MF_GRAYED },
\r
7887 { IDM_ReloadPosition, MF_BYCOMMAND|MF_GRAYED },
\r
7888 { IDM_PastePosition, MF_BYCOMMAND|MF_GRAYED },
\r
7889 { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },
\r
7890 { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },
\r
7891 { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },
\r
7892 // { IDM_Match, MF_BYCOMMAND|MF_GRAYED },
\r
7893 { IDM_TypeInMove, MF_BYCOMMAND|MF_GRAYED },
\r
7894 { IDM_RetractMove, MF_BYCOMMAND|MF_GRAYED },
\r
7898 Enables userThinkingEnables[] = {
\r
7899 { IDM_LoadGame, MF_BYCOMMAND|MF_ENABLED },
\r
7900 { IDM_LoadNextGame, MF_BYCOMMAND|MF_ENABLED },
\r
7901 { IDM_LoadPrevGame, MF_BYCOMMAND|MF_ENABLED },
\r
7902 { IDM_ReloadGame, MF_BYCOMMAND|MF_ENABLED },
\r
7903 { IDM_PasteGame, MF_BYCOMMAND|MF_ENABLED },
\r
7904 { IDM_LoadPosition, MF_BYCOMMAND|MF_ENABLED },
\r
7905 { IDM_LoadNextPosition, MF_BYCOMMAND|MF_ENABLED },
\r
7906 { IDM_LoadPrevPosition, MF_BYCOMMAND|MF_ENABLED },
\r
7907 { IDM_ReloadPosition, MF_BYCOMMAND|MF_ENABLED },
\r
7908 { IDM_PastePosition, MF_BYCOMMAND|MF_ENABLED },
\r
7909 { IDM_MachineWhite, MF_BYCOMMAND|MF_ENABLED },
\r
7910 { IDM_MachineBlack, MF_BYCOMMAND|MF_ENABLED },
\r
7911 { IDM_TwoMachines, MF_BYCOMMAND|MF_ENABLED },
\r
7912 // { IDM_Match, MF_BYCOMMAND|MF_ENABLED },
\r
7913 { IDM_TypeInMove, MF_BYCOMMAND|MF_ENABLED },
\r
7914 { IDM_RetractMove, MF_BYCOMMAND|MF_ENABLED },
\r
7918 /*---------------------------------------------------------------------------*\
\r
7920 * Front-end interface functions exported by XBoard.
\r
7921 * Functions appear in same order as prototypes in frontend.h.
\r
7923 \*---------------------------------------------------------------------------*/
\r
7925 CheckMark(UINT item, int state)
\r
7927 if(item) CheckMenuItem(GetMenu(hwndMain), item, MF_BYCOMMAND|state);
\r
7933 static UINT prevChecked = 0;
\r
7934 static int prevPausing = 0;
\r
7937 if (pausing != prevPausing) {
\r
7938 prevPausing = pausing;
\r
7939 (void) CheckMenuItem(GetMenu(hwndMain), IDM_Pause,
\r
7940 MF_BYCOMMAND|(pausing ? MF_CHECKED : MF_UNCHECKED));
\r
7941 if (hwndPause) SetWindowText(hwndPause, pausing ? "C" : "P");
\r
7944 switch (gameMode) {
\r
7945 case BeginningOfGame:
\r
7946 if (appData.icsActive)
\r
7947 nowChecked = IDM_IcsClient;
\r
7948 else if (appData.noChessProgram)
\r
7949 nowChecked = IDM_EditGame;
\r
7951 nowChecked = IDM_MachineBlack;
\r
7953 case MachinePlaysBlack:
\r
7954 nowChecked = IDM_MachineBlack;
\r
7956 case MachinePlaysWhite:
\r
7957 nowChecked = IDM_MachineWhite;
\r
7959 case TwoMachinesPlay:
\r
7960 nowChecked = IDM_TwoMachines;
\r
7963 nowChecked = IDM_AnalysisMode;
\r
7966 nowChecked = IDM_AnalyzeFile;
\r
7969 nowChecked = IDM_EditGame;
\r
7971 case PlayFromGameFile:
\r
7972 nowChecked = IDM_LoadGame;
\r
7974 case EditPosition:
\r
7975 nowChecked = IDM_EditPosition;
\r
7978 nowChecked = IDM_Training;
\r
7980 case IcsPlayingWhite:
\r
7981 case IcsPlayingBlack:
\r
7982 case IcsObserving:
\r
7984 nowChecked = IDM_IcsClient;
\r
7991 CheckMark(prevChecked, MF_UNCHECKED);
\r
7992 CheckMark(nowChecked, MF_CHECKED);
\r
7993 CheckMark(IDM_Match, matchMode && matchGame < appData.matchGames ? MF_CHECKED : MF_UNCHECKED);
\r
7995 if (nowChecked == IDM_LoadGame || nowChecked == IDM_Training) {
\r
7996 (void) EnableMenuItem(GetMenu(hwndMain), IDM_Training,
\r
7997 MF_BYCOMMAND|MF_ENABLED);
\r
7999 (void) EnableMenuItem(GetMenu(hwndMain),
\r
8000 IDM_Training, MF_BYCOMMAND|MF_GRAYED);
\r
8003 prevChecked = nowChecked;
\r
8005 /* [DM] icsEngineAnalyze - Do a sceure check too */
\r
8006 if (appData.icsActive) {
\r
8007 if (appData.icsEngineAnalyze) {
\r
8008 CheckMark(IDM_AnalysisMode, MF_CHECKED);
\r
8010 CheckMark(IDM_AnalysisMode, MF_UNCHECKED);
\r
8013 DisplayLogos(); // [HGM] logos: mode change could have altered logos
\r
8019 HMENU hmenu = GetMenu(hwndMain);
\r
8020 SetMenuEnables(hmenu, icsEnables);
\r
8021 EnableMenuItem(GetSubMenu(hmenu, OPTIONS_POS), IDM_IcsOptions,
\r
8022 MF_BYCOMMAND|MF_ENABLED);
\r
8024 if (appData.zippyPlay) {
\r
8025 SetMenuEnables(hmenu, zippyEnables);
\r
8026 if (!appData.noChessProgram) /* [DM] icsEngineAnalyze */
\r
8027 (void) EnableMenuItem(GetMenu(hwndMain), IDM_AnalysisMode,
\r
8028 MF_BYCOMMAND|MF_ENABLED);
\r
8036 SetMenuEnables(GetMenu(hwndMain), gnuEnables);
\r
8042 HMENU hmenu = GetMenu(hwndMain);
\r
8043 SetMenuEnables(hmenu, ncpEnables);
\r
8044 DrawMenuBar(hwndMain);
\r
8050 SetMenuEnables(GetMenu(hwndMain), cmailEnables);
\r
8054 SetTrainingModeOn()
\r
8057 SetMenuEnables(GetMenu(hwndMain), trainingOnEnables);
\r
8058 for (i = 0; i < N_BUTTONS; i++) {
\r
8059 if (buttonDesc[i].hwnd != NULL)
\r
8060 EnableWindow(buttonDesc[i].hwnd, FALSE);
\r
8065 VOID SetTrainingModeOff()
\r
8068 SetMenuEnables(GetMenu(hwndMain), trainingOffEnables);
\r
8069 for (i = 0; i < N_BUTTONS; i++) {
\r
8070 if (buttonDesc[i].hwnd != NULL)
\r
8071 EnableWindow(buttonDesc[i].hwnd, TRUE);
\r
8077 SetUserThinkingEnables()
\r
8079 SetMenuEnables(GetMenu(hwndMain), userThinkingEnables);
\r
8083 SetMachineThinkingEnables()
\r
8085 HMENU hMenu = GetMenu(hwndMain);
\r
8086 int flags = MF_BYCOMMAND|MF_ENABLED;
\r
8088 SetMenuEnables(hMenu, machineThinkingEnables);
\r
8090 if (gameMode == MachinePlaysBlack) {
\r
8091 (void)EnableMenuItem(hMenu, IDM_MachineBlack, flags);
\r
8092 } else if (gameMode == MachinePlaysWhite) {
\r
8093 (void)EnableMenuItem(hMenu, IDM_MachineWhite, flags);
\r
8094 } else if (gameMode == TwoMachinesPlay) {
\r
8095 (void)EnableMenuItem(hMenu, matchMode ? IDM_Match : IDM_TwoMachines, flags); // [HGM] match
\r
8101 DisplayTitle(char *str)
\r
8103 char title[MSG_SIZ], *host;
\r
8104 if (str[0] != NULLCHAR) {
\r
8105 safeStrCpy(title, str, sizeof(title)/sizeof(title[0]) );
\r
8106 } else if (appData.icsActive) {
\r
8107 if (appData.icsCommPort[0] != NULLCHAR)
\r
8110 host = appData.icsHost;
\r
8111 snprintf(title, MSG_SIZ, "%s: %s", szTitle, host);
\r
8112 } else if (appData.noChessProgram) {
\r
8113 safeStrCpy(title, szTitle, sizeof(title)/sizeof(title[0]) );
\r
8115 safeStrCpy(title, szTitle, sizeof(title)/sizeof(title[0]) );
\r
8116 strcat(title, ": ");
\r
8117 strcat(title, first.tidy);
\r
8119 SetWindowText(hwndMain, title);
\r
8124 DisplayMessage(char *str1, char *str2)
\r
8128 int remain = MESSAGE_TEXT_MAX - 1;
\r
8131 moveErrorMessageUp = FALSE; /* turned on later by caller if needed */
\r
8132 messageText[0] = NULLCHAR;
\r
8134 len = strlen(str1);
\r
8135 if (len > remain) len = remain;
\r
8136 strncpy(messageText, str1, len);
\r
8137 messageText[len] = NULLCHAR;
\r
8140 if (*str2 && remain >= 2) {
\r
8142 strcat(messageText, " ");
\r
8145 len = strlen(str2);
\r
8146 if (len > remain) len = remain;
\r
8147 strncat(messageText, str2, len);
\r
8149 messageText[MESSAGE_TEXT_MAX - 1] = NULLCHAR;
8150 safeStrCpy(lastMsg, messageText, MSG_SIZ);
8152 if (hwndMain == NULL || IsIconic(hwndMain)) return;
\r
8156 hdc = GetDC(hwndMain);
\r
8157 oldFont = SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);
\r
8158 ExtTextOut(hdc, messageRect.left, messageRect.top, ETO_CLIPPED|ETO_OPAQUE,
\r
8159 &messageRect, messageText, strlen(messageText), NULL);
\r
8160 (void) SelectObject(hdc, oldFont);
\r
8161 (void) ReleaseDC(hwndMain, hdc);
\r
8165 DisplayError(char *str, int error)
\r
8167 char buf[MSG_SIZ*2], buf2[MSG_SIZ];
\r
8171 safeStrCpy(buf, str, sizeof(buf)/sizeof(buf[0]) );
\r
8173 len = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,
\r
8174 NULL, error, LANG_NEUTRAL,
\r
8175 (LPSTR) buf2, MSG_SIZ, NULL);
\r
8177 snprintf(buf, 2*MSG_SIZ, "%s:\n%s", str, buf2);
\r
8179 ErrorMap *em = errmap;
\r
8180 while (em->err != 0 && em->err != error) em++;
\r
8181 if (em->err != 0) {
\r
8182 snprintf(buf, 2*MSG_SIZ, "%s:\n%s", str, em->msg);
\r
8184 snprintf(buf, 2*MSG_SIZ, "%s:\nError code %d", str, error);
\r
8189 ErrorPopUp(_("Error"), buf);
\r
8194 DisplayMoveError(char *str)
\r
8196 fromX = fromY = -1;
\r
8197 ClearHighlights();
\r
8198 DrawPosition(FALSE, NULL);
\r
8199 if (appData.popupMoveErrors) {
\r
8200 ErrorPopUp(_("Error"), str);
\r
8202 DisplayMessage(str, "");
\r
8203 moveErrorMessageUp = TRUE;
\r
8208 DisplayFatalError(char *str, int error, int exitStatus)
\r
8210 char buf[2*MSG_SIZ], buf2[MSG_SIZ];
\r
8212 char *label = exitStatus ? _("Fatal Error") : _("Exiting");
\r
8215 len = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,
\r
8216 NULL, error, LANG_NEUTRAL,
\r
8217 (LPSTR) buf2, MSG_SIZ, NULL);
\r
8219 snprintf(buf, 2*MSG_SIZ, "%s:\n%s", str, buf2);
\r
8221 ErrorMap *em = errmap;
\r
8222 while (em->err != 0 && em->err != error) em++;
\r
8223 if (em->err != 0) {
\r
8224 snprintf(buf, 2*MSG_SIZ, "%s:\n%s", str, em->msg);
\r
8226 snprintf(buf, 2*MSG_SIZ, "%s:\nError code %d", str, error);
\r
8231 if (appData.debugMode) {
\r
8232 fprintf(debugFP, "%s: %s\n", label, str);
\r
8234 if (appData.popupExitMessage) {
\r
8235 (void) MessageBox(hwndMain, str, label, MB_OK|
\r
8236 (exitStatus ? MB_ICONSTOP : MB_ICONINFORMATION));
\r
8238 ExitEvent(exitStatus);
\r
8243 DisplayInformation(char *str)
\r
8245 (void) MessageBox(hwndMain, str, _("Information"), MB_OK|MB_ICONINFORMATION);
\r
8250 DisplayNote(char *str)
\r
8252 ErrorPopUp(_("Note"), str);
\r
8257 char *title, *question, *replyPrefix;
\r
8262 QuestionDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
8264 static QuestionParams *qp;
\r
8265 char reply[MSG_SIZ];
\r
8268 switch (message) {
\r
8269 case WM_INITDIALOG:
\r
8270 qp = (QuestionParams *) lParam;
\r
8271 CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));
\r
8272 Translate(hDlg, DLG_Question);
\r
8273 SetWindowText(hDlg, qp->title);
\r
8274 SetDlgItemText(hDlg, OPT_QuestionText, qp->question);
\r
8275 SetFocus(GetDlgItem(hDlg, OPT_QuestionInput));
\r
8279 switch (LOWORD(wParam)) {
\r
8281 safeStrCpy(reply, qp->replyPrefix, sizeof(reply)/sizeof(reply[0]) );
\r
8282 if (*reply) strcat(reply, " ");
\r
8283 len = strlen(reply);
\r
8284 GetDlgItemText(hDlg, OPT_QuestionInput, reply + len, sizeof(reply) - len);
\r
8285 strcat(reply, "\n");
\r
8286 OutputToProcess(qp->pr, reply, strlen(reply), &err);
\r
8287 EndDialog(hDlg, TRUE);
\r
8288 if (err) DisplayFatalError(_("Error writing to chess program"), err, 1);
\r
8291 EndDialog(hDlg, FALSE);
\r
8302 AskQuestion(char* title, char *question, char *replyPrefix, ProcRef pr)
\r
8304 QuestionParams qp;
\r
8308 qp.question = question;
\r
8309 qp.replyPrefix = replyPrefix;
\r
8311 lpProc = MakeProcInstance((FARPROC)QuestionDialog, hInst);
\r
8312 DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_Question),
\r
8313 hwndMain, (DLGPROC)lpProc, (LPARAM)&qp);
\r
8314 FreeProcInstance(lpProc);
\r
8317 /* [AS] Pick FRC position */
\r
8318 LRESULT CALLBACK NewGameFRC_Proc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
8320 static int * lpIndexFRC;
\r
8326 case WM_INITDIALOG:
\r
8327 lpIndexFRC = (int *) lParam;
\r
8329 CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));
\r
8330 Translate(hDlg, DLG_NewGameFRC);
\r
8332 SendDlgItemMessage( hDlg, IDC_NFG_Edit, EM_SETLIMITTEXT, sizeof(buf)-1, 0 );
\r
8333 SetDlgItemInt( hDlg, IDC_NFG_Edit, *lpIndexFRC, TRUE );
\r
8334 SendDlgItemMessage( hDlg, IDC_NFG_Edit, EM_SETSEL, 0, -1 );
\r
8335 SetFocus(GetDlgItem(hDlg, IDC_NFG_Edit));
\r
8340 switch( LOWORD(wParam) ) {
\r
8342 *lpIndexFRC = GetDlgItemInt(hDlg, IDC_NFG_Edit, &index_is_ok, TRUE );
\r
8343 EndDialog( hDlg, 0 );
\r
8344 shuffleOpenings = TRUE; /* [HGM] shuffle: switch shuffling on for as long as we stay in current variant */
\r
8347 EndDialog( hDlg, 1 );
\r
8349 case IDC_NFG_Edit:
\r
8350 if( HIWORD(wParam) == EN_CHANGE ) {
\r
8351 GetDlgItemInt(hDlg, IDC_NFG_Edit, &index_is_ok, TRUE );
\r
8353 EnableWindow( GetDlgItem(hDlg, IDOK), index_is_ok );
\r
8356 case IDC_NFG_Random:
\r
8357 snprintf( buf, sizeof(buf)/sizeof(buf[0]), "%d", myrandom() ); /* [HGM] shuffle: no longer limit to 960 */
\r
8358 SetDlgItemText(hDlg, IDC_NFG_Edit, buf );
\r
8371 int index = appData.defaultFrcPosition;
\r
8372 FARPROC lpProc = MakeProcInstance( (FARPROC) NewGameFRC_Proc, hInst );
\r
8374 result = DialogBoxParam( hInst, MAKEINTRESOURCE(DLG_NewGameFRC), hwndMain, (DLGPROC)lpProc, (LPARAM)&index );
\r
8376 if( result == 0 ) {
\r
8377 appData.defaultFrcPosition = index;
\r
8383 /* [AS] Game list options. Refactored by HGM */
\r
8385 HWND gameListOptionsDialog;
\r
8387 // low-level front-end: clear text edit / list widget
\r
8391 SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_RESETCONTENT, 0, 0 );
\r
8394 // low-level front-end: clear text edit / list widget
\r
8396 GLT_DeSelectList()
\r
8398 SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_SETCURSEL, 0, 0 );
\r
8401 // low-level front-end: append line to text edit / list widget
\r
8403 GLT_AddToList( char *name )
\r
8406 SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_ADDSTRING, 0, (LPARAM) name );
\r
8410 // low-level front-end: get line from text edit / list widget
\r
8412 GLT_GetFromList( int index, char *name )
\r
8415 if( SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_GETTEXT, index, (LPARAM) name ) != LB_ERR )
\r
8421 void GLT_MoveSelection( HWND hDlg, int delta )
\r
8423 int idx1 = (int) SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETCURSEL, 0, 0 );
\r
8424 int idx2 = idx1 + delta;
\r
8425 int count = (int) SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETCOUNT, 0, 0 );
\r
8427 if( idx1 >=0 && idx1 < count && idx2 >= 0 && idx2 < count ) {
\r
8430 SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETTEXT, idx1, (LPARAM) buf );
\r
8431 SendDlgItemMessage( hDlg, IDC_GameListTags, LB_DELETESTRING, idx1, 0 );
\r
8432 SendDlgItemMessage( hDlg, IDC_GameListTags, LB_INSERTSTRING, idx2, (LPARAM) buf );
\r
8433 SendDlgItemMessage( hDlg, IDC_GameListTags, LB_SETCURSEL, idx2, 0 );
\r
8437 LRESULT CALLBACK GameListOptions_Proc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
8441 case WM_INITDIALOG:
\r
8442 gameListOptionsDialog = hDlg; // [HGM] pass through global to keep out off back-end
\r
8444 CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));
\r
8445 Translate(hDlg, DLG_GameListOptions);
\r
8447 /* Initialize list */
\r
8448 GLT_TagsToList( lpUserGLT );
\r
8450 SetFocus( GetDlgItem(hDlg, IDC_GameListTags) );
\r
8455 switch( LOWORD(wParam) ) {
\r
8458 EndDialog( hDlg, 0 );
\r
8461 EndDialog( hDlg, 1 );
\r
8464 case IDC_GLT_Default:
\r
8465 GLT_TagsToList( GLT_DEFAULT_TAGS );
\r
8468 case IDC_GLT_Restore:
\r
8469 GLT_TagsToList( appData.gameListTags );
\r
8473 GLT_MoveSelection( hDlg, -1 );
\r
8476 case IDC_GLT_Down:
\r
8477 GLT_MoveSelection( hDlg, +1 );
\r
8487 int GameListOptions()
\r
8490 FARPROC lpProc = MakeProcInstance( (FARPROC) GameListOptions_Proc, hInst );
\r
8492 safeStrCpy( lpUserGLT, appData.gameListTags ,LPUSERGLT_SIZE );
\r
8494 result = DialogBoxParam( hInst, MAKEINTRESOURCE(DLG_GameListOptions), hwndMain, (DLGPROC)lpProc, (LPARAM)lpUserGLT );
\r
8496 if( result == 0 ) {
\r
8497 /* [AS] Memory leak here! */
\r
8498 appData.gameListTags = strdup( lpUserGLT );
\r
8505 DisplayIcsInteractionTitle(char *str)
\r
8507 char consoleTitle[MSG_SIZ];
\r
8509 snprintf(consoleTitle, MSG_SIZ, "%s: %s", szConsoleTitle, str);
\r
8510 SetWindowText(hwndConsole, consoleTitle);
\r
8514 DrawPosition(int fullRedraw, Board board)
\r
8516 HDCDrawPosition(NULL, (BOOLEAN) fullRedraw, board);
\r
8519 void NotifyFrontendLogin()
\r
8522 UpdateICSWidth(GetDlgItem(hwndConsole, OPT_ConsoleText));
\r
8528 fromX = fromY = -1;
\r
8529 if (dragInfo.pos.x != -1 || dragInfo.pos.y != -1) {
\r
8530 dragInfo.pos.x = dragInfo.pos.y = -1;
\r
8531 dragInfo.pos.x = dragInfo.pos.y = -1;
\r
8532 dragInfo.lastpos = dragInfo.pos;
\r
8533 dragInfo.start.x = dragInfo.start.y = -1;
\r
8534 dragInfo.from = dragInfo.start;
\r
8536 DrawPosition(TRUE, NULL);
\r
8543 CommentPopUp(char *title, char *str)
\r
8545 HWND hwnd = GetActiveWindow();
\r
8546 EitherCommentPopUp(currentMove, title, str, FALSE); // [HGM] vari: fake move index, rather than 0
\r
8548 SetActiveWindow(hwnd);
\r
8552 CommentPopDown(void)
\r
8554 CheckMenuItem(GetMenu(hwndMain), IDM_Comment, MF_UNCHECKED);
\r
8555 if (commentDialog) {
\r
8556 ShowWindow(commentDialog, SW_HIDE);
\r
8558 commentUp = FALSE;
\r
8562 EditCommentPopUp(int index, char *title, char *str)
\r
8564 EitherCommentPopUp(index, title, str, TRUE);
\r
8571 MyPlaySound(&sounds[(int)SoundMove]);
\r
8574 VOID PlayIcsWinSound()
\r
8576 MyPlaySound(&sounds[(int)SoundIcsWin]);
\r
8579 VOID PlayIcsLossSound()
\r
8581 MyPlaySound(&sounds[(int)SoundIcsLoss]);
\r
8584 VOID PlayIcsDrawSound()
\r
8586 MyPlaySound(&sounds[(int)SoundIcsDraw]);
\r
8589 VOID PlayIcsUnfinishedSound()
\r
8591 MyPlaySound(&sounds[(int)SoundIcsUnfinished]);
\r
8597 MyPlaySound(&sounds[(int)SoundAlarm]);
\r
8605 consoleEcho = TRUE;
\r
8606 hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
8607 SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&consoleCF);
\r
8608 SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);
\r
8617 consoleEcho = FALSE;
\r
8618 hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
8619 /* This works OK: set text and background both to the same color */
\r
8621 cf.crTextColor = COLOR_ECHOOFF;
\r
8622 SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf);
\r
8623 SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, cf.crTextColor);
\r
8626 /* No Raw()...? */
\r
8628 void Colorize(ColorClass cc, int continuation)
\r
8630 currentColorClass = cc;
\r
8631 consoleCF.dwMask = CFM_COLOR|CFM_BOLD|CFM_ITALIC|CFM_UNDERLINE|CFM_STRIKEOUT;
\r
8632 consoleCF.crTextColor = textAttribs[cc].color;
\r
8633 consoleCF.dwEffects = textAttribs[cc].effects;
\r
8634 if (!continuation) MyPlaySound(&textAttribs[cc].sound);
\r
8640 static char buf[MSG_SIZ];
\r
8641 DWORD bufsiz = MSG_SIZ;
\r
8643 if(appData.userName != NULL && appData.userName[0] != 0) {
\r
8644 return appData.userName; /* [HGM] username: prefer name selected by user over his system login */
\r
8646 if (!GetUserName(buf, &bufsiz)) {
\r
8647 /*DisplayError("Error getting user name", GetLastError());*/
\r
8648 safeStrCpy(buf, _("User"), sizeof(buf)/sizeof(buf[0]) );
\r
8656 static char buf[MSG_SIZ];
\r
8657 DWORD bufsiz = MSG_SIZ;
\r
8659 if (!GetComputerName(buf, &bufsiz)) {
\r
8660 /*DisplayError("Error getting host name", GetLastError());*/
\r
8661 safeStrCpy(buf, _("Unknown"), sizeof(buf)/sizeof(buf[0]) );
\r
8668 ClockTimerRunning()
\r
8670 return clockTimerEvent != 0;
\r
8676 if (clockTimerEvent == 0) return FALSE;
\r
8677 KillTimer(hwndMain, clockTimerEvent);
\r
8678 clockTimerEvent = 0;
\r
8683 StartClockTimer(long millisec)
\r
8685 clockTimerEvent = SetTimer(hwndMain, (UINT) CLOCK_TIMER_ID,
\r
8686 (UINT) millisec, NULL);
\r
8690 DisplayWhiteClock(long timeRemaining, int highlight)
\r
8693 char *flag = whiteFlag && gameMode == TwoMachinesPlay ? "(!)" : "";
\r
8695 if(appData.noGUI) return;
\r
8696 hdc = GetDC(hwndMain);
\r
8697 if (!IsIconic(hwndMain)) {
\r
8698 DisplayAClock(hdc, timeRemaining, highlight,
\r
8699 flipClock ? &blackRect : &whiteRect, _("White"), flag);
\r
8701 if (highlight && iconCurrent == iconBlack) {
\r
8702 iconCurrent = iconWhite;
\r
8703 PostMessage(hwndMain, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);
\r
8704 if (IsIconic(hwndMain)) {
\r
8705 DrawIcon(hdc, 2, 2, iconCurrent);
\r
8708 (void) ReleaseDC(hwndMain, hdc);
\r
8710 PostMessage(hwndConsole, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);
\r
8714 DisplayBlackClock(long timeRemaining, int highlight)
\r
8717 char *flag = blackFlag && gameMode == TwoMachinesPlay ? "(!)" : "";
\r
8719 if(appData.noGUI) return;
\r
8720 hdc = GetDC(hwndMain);
\r
8721 if (!IsIconic(hwndMain)) {
\r
8722 DisplayAClock(hdc, timeRemaining, highlight,
\r
8723 flipClock ? &whiteRect : &blackRect, _("Black"), flag);
\r
8725 if (highlight && iconCurrent == iconWhite) {
\r
8726 iconCurrent = iconBlack;
\r
8727 PostMessage(hwndMain, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);
\r
8728 if (IsIconic(hwndMain)) {
\r
8729 DrawIcon(hdc, 2, 2, iconCurrent);
\r
8732 (void) ReleaseDC(hwndMain, hdc);
\r
8734 PostMessage(hwndConsole, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);
\r
8739 LoadGameTimerRunning()
\r
8741 return loadGameTimerEvent != 0;
\r
8745 StopLoadGameTimer()
\r
8747 if (loadGameTimerEvent == 0) return FALSE;
\r
8748 KillTimer(hwndMain, loadGameTimerEvent);
\r
8749 loadGameTimerEvent = 0;
\r
8754 StartLoadGameTimer(long millisec)
\r
8756 loadGameTimerEvent = SetTimer(hwndMain, (UINT) LOAD_GAME_TIMER_ID,
\r
8757 (UINT) millisec, NULL);
\r
8765 char fileTitle[MSG_SIZ];
\r
8767 defName = DefaultFileName(appData.oldSaveStyle ? "gam" : "pgn");
\r
8768 f = OpenFileDialog(hwndMain, "a", defName,
\r
8769 appData.oldSaveStyle ? "gam" : "pgn",
\r
8771 _("Save Game to File"), NULL, fileTitle, NULL);
\r
8773 SaveGame(f, 0, "");
\r
8780 ScheduleDelayedEvent(DelayedEventCallback cb, long millisec)
\r
8782 if (delayedTimerEvent != 0) {
\r
8783 if (appData.debugMode && cb != delayedTimerCallback) { // [HGM] alive: not too much debug
\r
8784 fprintf(debugFP, "ScheduleDelayedEvent: event already scheduled\n");
\r
8786 KillTimer(hwndMain, delayedTimerEvent);
\r
8787 delayedTimerEvent = 0;
\r
8788 if(delayedTimerCallback != cb) // [HGM] alive: do not "flush" same event, just postpone it
\r
8789 delayedTimerCallback();
\r
8791 delayedTimerCallback = cb;
\r
8792 delayedTimerEvent = SetTimer(hwndMain, (UINT) DELAYED_TIMER_ID,
\r
8793 (UINT) millisec, NULL);
\r
8796 DelayedEventCallback
\r
8799 if (delayedTimerEvent) {
\r
8800 return delayedTimerCallback;
\r
8807 CancelDelayedEvent()
\r
8809 if (delayedTimerEvent) {
\r
8810 KillTimer(hwndMain, delayedTimerEvent);
\r
8811 delayedTimerEvent = 0;
\r
8815 DWORD GetWin32Priority(int nice)
\r
8816 { // [HGM] nice: translate Unix nice() value to indows priority class. (Code stolen from Polyglot 1.4w11)
\r
8818 REALTIME_PRIORITY_CLASS 0x00000100
\r
8819 HIGH_PRIORITY_CLASS 0x00000080
\r
8820 ABOVE_NORMAL_PRIORITY_CLASS 0x00008000
\r
8821 NORMAL_PRIORITY_CLASS 0x00000020
\r
8822 BELOW_NORMAL_PRIORITY_CLASS 0x00004000
\r
8823 IDLE_PRIORITY_CLASS 0x00000040
\r
8825 if (nice < -15) return 0x00000080;
\r
8826 if (nice < 0) return 0x00008000;
\r
8827 if (nice == 0) return 0x00000020;
\r
8828 if (nice < 15) return 0x00004000;
\r
8829 return 0x00000040;
\r
8832 /* Start a child process running the given program.
\r
8833 The process's standard output can be read from "from", and its
\r
8834 standard input can be written to "to".
\r
8835 Exit with fatal error if anything goes wrong.
\r
8836 Returns an opaque pointer that can be used to destroy the process
\r
8840 StartChildProcess(char *cmdLine, char *dir, ProcRef *pr)
\r
8842 #define BUFSIZE 4096
\r
8844 HANDLE hChildStdinRd, hChildStdinWr,
\r
8845 hChildStdoutRd, hChildStdoutWr;
\r
8846 HANDLE hChildStdinWrDup, hChildStdoutRdDup;
\r
8847 SECURITY_ATTRIBUTES saAttr;
\r
8849 PROCESS_INFORMATION piProcInfo;
\r
8850 STARTUPINFO siStartInfo;
\r
8852 char buf[MSG_SIZ];
\r
8855 if (appData.debugMode) {
\r
8856 fprintf(debugFP, "StartChildProcess (dir=\"%s\") %s\n", dir, cmdLine);
\r
8861 /* Set the bInheritHandle flag so pipe handles are inherited. */
\r
8862 saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);
\r
8863 saAttr.bInheritHandle = TRUE;
\r
8864 saAttr.lpSecurityDescriptor = NULL;
\r
8867 * The steps for redirecting child's STDOUT:
\r
8868 * 1. Create anonymous pipe to be STDOUT for child.
\r
8869 * 2. Create a noninheritable duplicate of read handle,
\r
8870 * and close the inheritable read handle.
\r
8873 /* Create a pipe for the child's STDOUT. */
\r
8874 if (! CreatePipe(&hChildStdoutRd, &hChildStdoutWr, &saAttr, 0)) {
\r
8875 return GetLastError();
\r
8878 /* Duplicate the read handle to the pipe, so it is not inherited. */
\r
8879 fSuccess = DuplicateHandle(GetCurrentProcess(), hChildStdoutRd,
\r
8880 GetCurrentProcess(), &hChildStdoutRdDup, 0,
\r
8881 FALSE, /* not inherited */
\r
8882 DUPLICATE_SAME_ACCESS);
\r
8884 return GetLastError();
\r
8886 CloseHandle(hChildStdoutRd);
\r
8889 * The steps for redirecting child's STDIN:
\r
8890 * 1. Create anonymous pipe to be STDIN for child.
\r
8891 * 2. Create a noninheritable duplicate of write handle,
\r
8892 * and close the inheritable write handle.
\r
8895 /* Create a pipe for the child's STDIN. */
\r
8896 if (! CreatePipe(&hChildStdinRd, &hChildStdinWr, &saAttr, 0)) {
\r
8897 return GetLastError();
\r
8900 /* Duplicate the write handle to the pipe, so it is not inherited. */
\r
8901 fSuccess = DuplicateHandle(GetCurrentProcess(), hChildStdinWr,
\r
8902 GetCurrentProcess(), &hChildStdinWrDup, 0,
\r
8903 FALSE, /* not inherited */
\r
8904 DUPLICATE_SAME_ACCESS);
\r
8906 return GetLastError();
\r
8908 CloseHandle(hChildStdinWr);
\r
8910 /* Arrange to (1) look in dir for the child .exe file, and
\r
8911 * (2) have dir be the child's working directory. Interpret
\r
8912 * dir relative to the directory WinBoard loaded from. */
\r
8913 GetCurrentDirectory(MSG_SIZ, buf);
\r
8914 SetCurrentDirectory(installDir);
\r
8915 // kludgey way to update logos in tourney, as long as back-end can't do it
\r
8916 if(!strcmp(cmdLine, first.program)) LoadLogo(&first, 0); else
\r
8917 if(!strcmp(cmdLine, second.program)) LoadLogo(&second, 1);
\r
8918 SetCurrentDirectory(dir);
\r
8920 /* Now create the child process. */
\r
8922 siStartInfo.cb = sizeof(STARTUPINFO);
\r
8923 siStartInfo.lpReserved = NULL;
\r
8924 siStartInfo.lpDesktop = NULL;
\r
8925 siStartInfo.lpTitle = NULL;
\r
8926 siStartInfo.dwFlags = STARTF_USESTDHANDLES;
\r
8927 siStartInfo.cbReserved2 = 0;
\r
8928 siStartInfo.lpReserved2 = NULL;
\r
8929 siStartInfo.hStdInput = hChildStdinRd;
\r
8930 siStartInfo.hStdOutput = hChildStdoutWr;
\r
8931 siStartInfo.hStdError = hChildStdoutWr;
\r
8933 fSuccess = CreateProcess(NULL,
\r
8934 cmdLine, /* command line */
\r
8935 NULL, /* process security attributes */
\r
8936 NULL, /* primary thread security attrs */
\r
8937 TRUE, /* handles are inherited */
\r
8938 DETACHED_PROCESS|CREATE_NEW_PROCESS_GROUP,
\r
8939 NULL, /* use parent's environment */
\r
8941 &siStartInfo, /* STARTUPINFO pointer */
\r
8942 &piProcInfo); /* receives PROCESS_INFORMATION */
\r
8944 err = GetLastError();
\r
8945 SetCurrentDirectory(buf); /* return to prev directory */
\r
8950 if (appData.niceEngines){ // [HGM] nice: adjust engine proc priority
\r
8951 if(appData.debugMode) fprintf(debugFP, "nice engine proc to %d\n", appData.niceEngines);
\r
8952 SetPriorityClass(piProcInfo.hProcess, GetWin32Priority(appData.niceEngines));
\r
8955 /* Close the handles we don't need in the parent */
\r
8956 CloseHandle(piProcInfo.hThread);
\r
8957 CloseHandle(hChildStdinRd);
\r
8958 CloseHandle(hChildStdoutWr);
\r
8960 /* Prepare return value */
\r
8961 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
\r
8962 cp->kind = CPReal;
\r
8963 cp->hProcess = piProcInfo.hProcess;
\r
8964 cp->pid = piProcInfo.dwProcessId;
\r
8965 cp->hFrom = hChildStdoutRdDup;
\r
8966 cp->hTo = hChildStdinWrDup;
\r
8968 *pr = (void *) cp;
\r
8970 /* Klaus Friedel says that this Sleep solves a problem under Windows
\r
8971 2000 where engines sometimes don't see the initial command(s)
\r
8972 from WinBoard and hang. I don't understand how that can happen,
\r
8973 but the Sleep is harmless, so I've put it in. Others have also
\r
8974 reported what may be the same problem, so hopefully this will fix
\r
8975 it for them too. */
\r
8983 DestroyChildProcess(ProcRef pr, int/*boolean*/ signal)
\r
8985 ChildProc *cp; int result;
\r
8987 cp = (ChildProc *) pr;
\r
8988 if (cp == NULL) return;
\r
8990 switch (cp->kind) {
\r
8992 /* TerminateProcess is considered harmful, so... */
\r
8993 CloseHandle(cp->hTo); /* Closing this will give the child an EOF and hopefully kill it */
\r
8994 if (cp->hFrom) CloseHandle(cp->hFrom); /* if NULL, InputThread will close it */
\r
8995 /* The following doesn't work because the chess program
\r
8996 doesn't "have the same console" as WinBoard. Maybe
\r
8997 we could arrange for this even though neither WinBoard
\r
8998 nor the chess program uses a console for stdio? */
\r
8999 /*!!if (signal) GenerateConsoleCtrlEvent(CTRL_BREAK_EVENT, cp->pid);*/
\r
9001 /* [AS] Special termination modes for misbehaving programs... */
\r
9002 if( signal == 9 ) {
\r
9003 result = TerminateProcess( cp->hProcess, 0 );
\r
9005 if ( appData.debugMode) {
\r
9006 fprintf( debugFP, "Terminating process %lu, result=%d\n", cp->pid, result );
\r
9009 else if( signal == 10 ) {
\r
9010 DWORD dw = WaitForSingleObject( cp->hProcess, 3*1000 ); // Wait 3 seconds at most
\r
9012 if( dw != WAIT_OBJECT_0 ) {
\r
9013 result = TerminateProcess( cp->hProcess, 0 );
\r
9015 if ( appData.debugMode) {
\r
9016 fprintf( debugFP, "Process %lu still alive after timeout, killing... result=%d\n", cp->pid, result );
\r
9022 CloseHandle(cp->hProcess);
\r
9026 if (cp->hFrom) CloseHandle(cp->hFrom);
\r
9030 closesocket(cp->sock);
\r
9035 if (signal) send(cp->sock2, "\017", 1, 0); /* 017 = 15 = SIGTERM */
\r
9036 closesocket(cp->sock);
\r
9037 closesocket(cp->sock2);
\r
9045 InterruptChildProcess(ProcRef pr)
\r
9049 cp = (ChildProc *) pr;
\r
9050 if (cp == NULL) return;
\r
9051 switch (cp->kind) {
\r
9053 /* The following doesn't work because the chess program
\r
9054 doesn't "have the same console" as WinBoard. Maybe
\r
9055 we could arrange for this even though neither WinBoard
\r
9056 nor the chess program uses a console for stdio */
\r
9057 /*!!GenerateConsoleCtrlEvent(CTRL_C_EVENT, cp->pid);*/
\r
9062 /* Can't interrupt */
\r
9066 send(cp->sock2, "\002", 1, 0); /* 2 = SIGINT */
\r
9073 OpenTelnet(char *host, char *port, ProcRef *pr)
\r
9075 char cmdLine[MSG_SIZ];
\r
9077 if (port[0] == NULLCHAR) {
\r
9078 snprintf(cmdLine, MSG_SIZ, "%s %s", appData.telnetProgram, host);
\r
9080 snprintf(cmdLine, MSG_SIZ, "%s %s %s", appData.telnetProgram, host, port);
\r
9082 return StartChildProcess(cmdLine, "", pr);
\r
9086 /* Code to open TCP sockets */
\r
9089 OpenTCP(char *host, char *port, ProcRef *pr)
\r
9094 struct sockaddr_in sa, mysa;
\r
9095 struct hostent FAR *hp;
\r
9096 unsigned short uport;
\r
9097 WORD wVersionRequested;
\r
9100 /* Initialize socket DLL */
\r
9101 wVersionRequested = MAKEWORD(1, 1);
\r
9102 err = WSAStartup(wVersionRequested, &wsaData);
\r
9103 if (err != 0) return err;
\r
9106 if ((s = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {
\r
9107 err = WSAGetLastError();
\r
9112 /* Bind local address using (mostly) don't-care values.
\r
9114 memset((char *) &mysa, 0, sizeof(struct sockaddr_in));
\r
9115 mysa.sin_family = AF_INET;
\r
9116 mysa.sin_addr.s_addr = INADDR_ANY;
\r
9117 uport = (unsigned short) 0;
\r
9118 mysa.sin_port = htons(uport);
\r
9119 if (bind(s, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))
\r
9120 == SOCKET_ERROR) {
\r
9121 err = WSAGetLastError();
\r
9126 /* Resolve remote host name */
\r
9127 memset((char *) &sa, 0, sizeof(struct sockaddr_in));
\r
9128 if (!(hp = gethostbyname(host))) {
\r
9129 unsigned int b0, b1, b2, b3;
\r
9131 err = WSAGetLastError();
\r
9133 if (sscanf(host, "%u.%u.%u.%u", &b0, &b1, &b2, &b3) == 4) {
\r
9134 hp = (struct hostent *) calloc(1, sizeof(struct hostent));
\r
9135 hp->h_addrtype = AF_INET;
\r
9137 hp->h_addr_list = (char **) calloc(2, sizeof(char *));
\r
9138 hp->h_addr_list[0] = (char *) malloc(4);
\r
9139 hp->h_addr_list[0][0] = (char) b0;
\r
9140 hp->h_addr_list[0][1] = (char) b1;
\r
9141 hp->h_addr_list[0][2] = (char) b2;
\r
9142 hp->h_addr_list[0][3] = (char) b3;
\r
9148 sa.sin_family = hp->h_addrtype;
\r
9149 uport = (unsigned short) atoi(port);
\r
9150 sa.sin_port = htons(uport);
\r
9151 memcpy((char *) &sa.sin_addr, hp->h_addr, hp->h_length);
\r
9153 /* Make connection */
\r
9154 if (connect(s, (struct sockaddr *) &sa,
\r
9155 sizeof(struct sockaddr_in)) == SOCKET_ERROR) {
\r
9156 err = WSAGetLastError();
\r
9161 /* Prepare return value */
\r
9162 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
\r
9163 cp->kind = CPSock;
\r
9165 *pr = (ProcRef *) cp;
\r
9171 OpenCommPort(char *name, ProcRef *pr)
\r
9176 char fullname[MSG_SIZ];
\r
9178 if (*name != '\\')
\r
9179 snprintf(fullname, MSG_SIZ, "\\\\.\\%s", name);
\r
9181 safeStrCpy(fullname, name, sizeof(fullname)/sizeof(fullname[0]) );
\r
9183 h = CreateFile(name, GENERIC_READ | GENERIC_WRITE,
\r
9184 0, NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL);
\r
9185 if (h == (HANDLE) -1) {
\r
9186 return GetLastError();
\r
9190 if (!SetCommState(h, (LPDCB) &dcb)) return GetLastError();
\r
9192 /* Accumulate characters until a 100ms pause, then parse */
\r
9193 ct.ReadIntervalTimeout = 100;
\r
9194 ct.ReadTotalTimeoutMultiplier = 0;
\r
9195 ct.ReadTotalTimeoutConstant = 0;
\r
9196 ct.WriteTotalTimeoutMultiplier = 0;
\r
9197 ct.WriteTotalTimeoutConstant = 0;
\r
9198 if (!SetCommTimeouts(h, (LPCOMMTIMEOUTS) &ct)) return GetLastError();
\r
9200 /* Prepare return value */
\r
9201 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
\r
9202 cp->kind = CPComm;
\r
9205 *pr = (ProcRef *) cp;
\r
9211 OpenLoopback(ProcRef *pr)
\r
9213 DisplayFatalError(_("Not implemented"), 0, 1);
\r
9219 OpenRcmd(char* host, char* user, char* cmd, ProcRef* pr)
\r
9224 struct sockaddr_in sa, mysa;
\r
9225 struct hostent FAR *hp;
\r
9226 unsigned short uport;
\r
9227 WORD wVersionRequested;
\r
9230 char stderrPortStr[MSG_SIZ];
\r
9232 /* Initialize socket DLL */
\r
9233 wVersionRequested = MAKEWORD(1, 1);
\r
9234 err = WSAStartup(wVersionRequested, &wsaData);
\r
9235 if (err != 0) return err;
\r
9237 /* Resolve remote host name */
\r
9238 memset((char *) &sa, 0, sizeof(struct sockaddr_in));
\r
9239 if (!(hp = gethostbyname(host))) {
\r
9240 unsigned int b0, b1, b2, b3;
\r
9242 err = WSAGetLastError();
\r
9244 if (sscanf(host, "%u.%u.%u.%u", &b0, &b1, &b2, &b3) == 4) {
\r
9245 hp = (struct hostent *) calloc(1, sizeof(struct hostent));
\r
9246 hp->h_addrtype = AF_INET;
\r
9248 hp->h_addr_list = (char **) calloc(2, sizeof(char *));
\r
9249 hp->h_addr_list[0] = (char *) malloc(4);
\r
9250 hp->h_addr_list[0][0] = (char) b0;
\r
9251 hp->h_addr_list[0][1] = (char) b1;
\r
9252 hp->h_addr_list[0][2] = (char) b2;
\r
9253 hp->h_addr_list[0][3] = (char) b3;
\r
9259 sa.sin_family = hp->h_addrtype;
\r
9260 uport = (unsigned short) 514;
\r
9261 sa.sin_port = htons(uport);
\r
9262 memcpy((char *) &sa.sin_addr, hp->h_addr, hp->h_length);
\r
9264 /* Bind local socket to unused "privileged" port address
\r
9266 s = INVALID_SOCKET;
\r
9267 memset((char *) &mysa, 0, sizeof(struct sockaddr_in));
\r
9268 mysa.sin_family = AF_INET;
\r
9269 mysa.sin_addr.s_addr = INADDR_ANY;
\r
9270 for (fromPort = 1023;; fromPort--) {
\r
9271 if (fromPort < 0) {
\r
9273 return WSAEADDRINUSE;
\r
9275 if (s == INVALID_SOCKET) {
\r
9276 if ((s = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {
\r
9277 err = WSAGetLastError();
\r
9282 uport = (unsigned short) fromPort;
\r
9283 mysa.sin_port = htons(uport);
\r
9284 if (bind(s, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))
\r
9285 == SOCKET_ERROR) {
\r
9286 err = WSAGetLastError();
\r
9287 if (err == WSAEADDRINUSE) continue;
\r
9291 if (connect(s, (struct sockaddr *) &sa,
\r
9292 sizeof(struct sockaddr_in)) == SOCKET_ERROR) {
\r
9293 err = WSAGetLastError();
\r
9294 if (err == WSAEADDRINUSE) {
\r
9305 /* Bind stderr local socket to unused "privileged" port address
\r
9307 s2 = INVALID_SOCKET;
\r
9308 memset((char *) &mysa, 0, sizeof(struct sockaddr_in));
\r
9309 mysa.sin_family = AF_INET;
\r
9310 mysa.sin_addr.s_addr = INADDR_ANY;
\r
9311 for (fromPort = 1023;; fromPort--) {
\r
9312 if (fromPort == prevStderrPort) continue; // don't reuse port
\r
9313 if (fromPort < 0) {
\r
9314 (void) closesocket(s);
\r
9316 return WSAEADDRINUSE;
\r
9318 if (s2 == INVALID_SOCKET) {
\r
9319 if ((s2 = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {
\r
9320 err = WSAGetLastError();
\r
9326 uport = (unsigned short) fromPort;
\r
9327 mysa.sin_port = htons(uport);
\r
9328 if (bind(s2, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))
\r
9329 == SOCKET_ERROR) {
\r
9330 err = WSAGetLastError();
\r
9331 if (err == WSAEADDRINUSE) continue;
\r
9332 (void) closesocket(s);
\r
9336 if (listen(s2, 1) == SOCKET_ERROR) {
\r
9337 err = WSAGetLastError();
\r
9338 if (err == WSAEADDRINUSE) {
\r
9340 s2 = INVALID_SOCKET;
\r
9343 (void) closesocket(s);
\r
9344 (void) closesocket(s2);
\r
9350 prevStderrPort = fromPort; // remember port used
\r
9351 snprintf(stderrPortStr, MSG_SIZ, "%d", fromPort);
\r
9353 if (send(s, stderrPortStr, strlen(stderrPortStr) + 1, 0) == SOCKET_ERROR) {
\r
9354 err = WSAGetLastError();
\r
9355 (void) closesocket(s);
\r
9356 (void) closesocket(s2);
\r
9361 if (send(s, UserName(), strlen(UserName()) + 1, 0) == SOCKET_ERROR) {
\r
9362 err = WSAGetLastError();
\r
9363 (void) closesocket(s);
\r
9364 (void) closesocket(s2);
\r
9368 if (*user == NULLCHAR) user = UserName();
\r
9369 if (send(s, user, strlen(user) + 1, 0) == SOCKET_ERROR) {
\r
9370 err = WSAGetLastError();
\r
9371 (void) closesocket(s);
\r
9372 (void) closesocket(s2);
\r
9376 if (send(s, cmd, strlen(cmd) + 1, 0) == SOCKET_ERROR) {
\r
9377 err = WSAGetLastError();
\r
9378 (void) closesocket(s);
\r
9379 (void) closesocket(s2);
\r
9384 if ((s3 = accept(s2, NULL, NULL)) == INVALID_SOCKET) {
\r
9385 err = WSAGetLastError();
\r
9386 (void) closesocket(s);
\r
9387 (void) closesocket(s2);
\r
9391 (void) closesocket(s2); /* Stop listening */
\r
9393 /* Prepare return value */
\r
9394 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
\r
9395 cp->kind = CPRcmd;
\r
9398 *pr = (ProcRef *) cp;
\r
9405 AddInputSource(ProcRef pr, int lineByLine,
\r
9406 InputCallback func, VOIDSTAR closure)
\r
9408 InputSource *is, *is2 = NULL;
\r
9409 ChildProc *cp = (ChildProc *) pr;
\r
9411 is = (InputSource *) calloc(1, sizeof(InputSource));
\r
9412 is->lineByLine = lineByLine;
\r
9414 is->closure = closure;
\r
9415 is->second = NULL;
\r
9416 is->next = is->buf;
\r
9417 if (pr == NoProc) {
\r
9418 is->kind = CPReal;
\r
9419 consoleInputSource = is;
\r
9421 is->kind = cp->kind;
\r
9423 [AS] Try to avoid a race condition if the thread is given control too early:
\r
9424 we create all threads suspended so that the is->hThread variable can be
\r
9425 safely assigned, then let the threads start with ResumeThread.
\r
9427 switch (cp->kind) {
\r
9429 is->hFile = cp->hFrom;
\r
9430 cp->hFrom = NULL; /* now owned by InputThread */
\r
9432 CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) NonOvlInputThread,
\r
9433 (LPVOID) is, CREATE_SUSPENDED, &is->id);
\r
9437 is->hFile = cp->hFrom;
\r
9438 cp->hFrom = NULL; /* now owned by InputThread */
\r
9440 CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) InputThread,
\r
9441 (LPVOID) is, CREATE_SUSPENDED, &is->id);
\r
9445 is->sock = cp->sock;
\r
9447 CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,
\r
9448 (LPVOID) is, CREATE_SUSPENDED, &is->id);
\r
9452 is2 = (InputSource *) calloc(1, sizeof(InputSource));
\r
9454 is->sock = cp->sock;
\r
9456 is2->sock = cp->sock2;
\r
9457 is2->second = is2;
\r
9459 CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,
\r
9460 (LPVOID) is, CREATE_SUSPENDED, &is->id);
\r
9462 CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,
\r
9463 (LPVOID) is2, CREATE_SUSPENDED, &is2->id);
\r
9467 if( is->hThread != NULL ) {
\r
9468 ResumeThread( is->hThread );
\r
9471 if( is2 != NULL && is2->hThread != NULL ) {
\r
9472 ResumeThread( is2->hThread );
\r
9476 return (InputSourceRef) is;
\r
9480 RemoveInputSource(InputSourceRef isr)
\r
9484 is = (InputSource *) isr;
\r
9485 is->hThread = NULL; /* tell thread to stop */
\r
9486 CloseHandle(is->hThread);
\r
9487 if (is->second != NULL) {
\r
9488 is->second->hThread = NULL;
\r
9489 CloseHandle(is->second->hThread);
\r
9493 int no_wrap(char *message, int count)
\r
9495 ConsoleOutput(message, count, FALSE);
\r
9500 OutputToProcess(ProcRef pr, char *message, int count, int *outError)
\r
9503 int outCount = SOCKET_ERROR;
\r
9504 ChildProc *cp = (ChildProc *) pr;
\r
9505 static OVERLAPPED ovl;
\r
9506 static int line = 0;
\r
9510 if (appData.noJoin || !appData.useInternalWrap)
\r
9511 return no_wrap(message, count);
\r
9514 int width = get_term_width();
\r
9515 int len = wrap(NULL, message, count, width, &line);
\r
9516 char *msg = malloc(len);
\r
9520 return no_wrap(message, count);
\r
9523 dbgchk = wrap(msg, message, count, width, &line);
\r
9524 if (dbgchk != len && appData.debugMode)
\r
9525 fprintf(debugFP, "wrap(): dbgchk(%d) != len(%d)\n", dbgchk, len);
\r
9526 ConsoleOutput(msg, len, FALSE);
\r
9533 if (ovl.hEvent == NULL) {
\r
9534 ovl.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
\r
9536 ovl.Internal = ovl.InternalHigh = ovl.Offset = ovl.OffsetHigh = 0;
\r
9538 switch (cp->kind) {
\r
9541 outCount = send(cp->sock, message, count, 0);
\r
9542 if (outCount == SOCKET_ERROR) {
\r
9543 *outError = WSAGetLastError();
\r
9545 *outError = NO_ERROR;
\r
9550 if (WriteFile(((ChildProc *)pr)->hTo, message, count,
\r
9551 &dOutCount, NULL)) {
\r
9552 *outError = NO_ERROR;
\r
9553 outCount = (int) dOutCount;
\r
9555 *outError = GetLastError();
\r
9560 *outError = DoWriteFile(((ChildProc *)pr)->hTo, message, count,
\r
9561 &dOutCount, &ovl);
\r
9562 if (*outError == NO_ERROR) {
\r
9563 outCount = (int) dOutCount;
\r
9571 OutputToProcessDelayed(ProcRef pr, char *message, int count, int *outError,
\r
9574 /* Ignore delay, not implemented for WinBoard */
\r
9575 return OutputToProcess(pr, message, count, outError);
\r
9580 CmailSigHandlerCallBack(InputSourceRef isr, VOIDSTAR closure,
\r
9581 char *buf, int count, int error)
\r
9583 DisplayFatalError(_("Not implemented"), 0, 1);
\r
9586 /* see wgamelist.c for Game List functions */
\r
9587 /* see wedittags.c for Edit Tags functions */
\r
9594 char buf[MSG_SIZ];
\r
9597 if (SearchPath(installDir, appData.icsLogon, NULL, MSG_SIZ, buf, &dummy)) {
\r
9598 f = fopen(buf, "r");
\r
9600 ProcessICSInitScript(f);
\r
9608 StartAnalysisClock()
\r
9610 if (analysisTimerEvent) return;
\r
9611 analysisTimerEvent = SetTimer(hwndMain, (UINT) ANALYSIS_TIMER_ID,
\r
9612 (UINT) 2000, NULL);
\r
9616 SetHighlights(int fromX, int fromY, int toX, int toY)
\r
9618 highlightInfo.sq[0].x = fromX;
\r
9619 highlightInfo.sq[0].y = fromY;
\r
9620 highlightInfo.sq[1].x = toX;
\r
9621 highlightInfo.sq[1].y = toY;
\r
9627 highlightInfo.sq[0].x = highlightInfo.sq[0].y =
\r
9628 highlightInfo.sq[1].x = highlightInfo.sq[1].y = -1;
\r
9632 SetPremoveHighlights(int fromX, int fromY, int toX, int toY)
\r
9634 premoveHighlightInfo.sq[0].x = fromX;
\r
9635 premoveHighlightInfo.sq[0].y = fromY;
\r
9636 premoveHighlightInfo.sq[1].x = toX;
\r
9637 premoveHighlightInfo.sq[1].y = toY;
\r
9641 ClearPremoveHighlights()
\r
9643 premoveHighlightInfo.sq[0].x = premoveHighlightInfo.sq[0].y =
\r
9644 premoveHighlightInfo.sq[1].x = premoveHighlightInfo.sq[1].y = -1;
\r
9648 ShutDownFrontEnd()
\r
9650 if (saveSettingsOnExit) SaveSettings(settingsFileName);
\r
9651 DeleteClipboardTempFiles();
\r
9657 if (IsIconic(hwndMain))
\r
9658 ShowWindow(hwndMain, SW_RESTORE);
\r
9660 SetActiveWindow(hwndMain);
\r
9664 * Prototypes for animation support routines
\r
9666 static void ScreenSquare(int column, int row, POINT * pt);
\r
9667 static void Tween( POINT * start, POINT * mid, POINT * finish, int factor,
\r
9668 POINT frames[], int * nFrames);
\r
9674 AnimateAtomicCapture(Board board, int fromX, int fromY, int toX, int toY)
\r
9675 { // [HGM] atomic: animate blast wave
\r
9678 explodeInfo.fromX = fromX;
\r
9679 explodeInfo.fromY = fromY;
\r
9680 explodeInfo.toX = toX;
\r
9681 explodeInfo.toY = toY;
\r
9682 for(i=1; i<4*kFactor; i++) {
\r
9683 explodeInfo.radius = (i*180)/(4*kFactor-1);
\r
9684 DrawPosition(FALSE, board);
\r
9685 Sleep(appData.animSpeed);
\r
9687 explodeInfo.radius = 0;
\r
9688 DrawPosition(TRUE, board);
\r
9692 AnimateMove(board, fromX, fromY, toX, toY)
\r
9699 ChessSquare piece;
\r
9700 POINT start, finish, mid;
\r
9701 POINT frames[kFactor * 2 + 1];
\r
9704 if (!appData.animate) return;
\r
9705 if (doingSizing) return;
\r
9706 if (fromY < 0 || fromX < 0) return;
\r
9707 piece = board[fromY][fromX];
\r
9708 if (piece >= EmptySquare) return;
\r
9710 ScreenSquare(fromX, fromY, &start);
\r
9711 ScreenSquare(toX, toY, &finish);
\r
9713 /* All moves except knight jumps move in straight line */
\r
9714 if (!(abs(fromX-toX) == 1 && abs(fromY-toY) == 2 || abs(fromX-toX) == 2 && abs(fromY-toY) == 1)) {
\r
9715 mid.x = start.x + (finish.x - start.x) / 2;
\r
9716 mid.y = start.y + (finish.y - start.y) / 2;
\r
9718 /* Knight: make straight movement then diagonal */
\r
9719 if (abs(toY - fromY) < abs(toX - fromX)) {
\r
9720 mid.x = start.x + (finish.x - start.x) / 2;
\r
9724 mid.y = start.y + (finish.y - start.y) / 2;
\r
9728 /* Don't use as many frames for very short moves */
\r
9729 if (abs(toY - fromY) + abs(toX - fromX) <= 2)
\r
9730 Tween(&start, &mid, &finish, kFactor - 1, frames, &nFrames);
\r
9732 Tween(&start, &mid, &finish, kFactor, frames, &nFrames);
\r
9734 animInfo.from.x = fromX;
\r
9735 animInfo.from.y = fromY;
\r
9736 animInfo.to.x = toX;
\r
9737 animInfo.to.y = toY;
\r
9738 animInfo.lastpos = start;
\r
9739 animInfo.piece = piece;
\r
9740 for (n = 0; n < nFrames; n++) {
\r
9741 animInfo.pos = frames[n];
\r
9742 DrawPosition(FALSE, NULL);
\r
9743 animInfo.lastpos = animInfo.pos;
\r
9744 Sleep(appData.animSpeed);
\r
9746 animInfo.pos = finish;
\r
9747 DrawPosition(FALSE, NULL);
\r
9748 animInfo.piece = EmptySquare;
\r
9749 Explode(board, fromX, fromY, toX, toY);
\r
9752 /* Convert board position to corner of screen rect and color */
\r
9755 ScreenSquare(column, row, pt)
\r
9756 int column; int row; POINT * pt;
\r
9759 pt->x = lineGap + ((BOARD_WIDTH-1)-column) * (squareSize + lineGap);
\r
9760 pt->y = lineGap + row * (squareSize + lineGap);
\r
9762 pt->x = lineGap + column * (squareSize + lineGap);
\r
9763 pt->y = lineGap + ((BOARD_HEIGHT-1)-row) * (squareSize + lineGap);
\r
9767 /* Generate a series of frame coords from start->mid->finish.
\r
9768 The movement rate doubles until the half way point is
\r
9769 reached, then halves back down to the final destination,
\r
9770 which gives a nice slow in/out effect. The algorithmn
\r
9771 may seem to generate too many intermediates for short
\r
9772 moves, but remember that the purpose is to attract the
\r
9773 viewers attention to the piece about to be moved and
\r
9774 then to where it ends up. Too few frames would be less
\r
9778 Tween(start, mid, finish, factor, frames, nFrames)
\r
9779 POINT * start; POINT * mid;
\r
9780 POINT * finish; int factor;
\r
9781 POINT frames[]; int * nFrames;
\r
9783 int n, fraction = 1, count = 0;
\r
9785 /* Slow in, stepping 1/16th, then 1/8th, ... */
\r
9786 for (n = 0; n < factor; n++)
\r
9788 for (n = 0; n < factor; n++) {
\r
9789 frames[count].x = start->x + (mid->x - start->x) / fraction;
\r
9790 frames[count].y = start->y + (mid->y - start->y) / fraction;
\r
9792 fraction = fraction / 2;
\r
9796 frames[count] = *mid;
\r
9799 /* Slow out, stepping 1/2, then 1/4, ... */
\r
9801 for (n = 0; n < factor; n++) {
\r
9802 frames[count].x = finish->x - (finish->x - mid->x) / fraction;
\r
9803 frames[count].y = finish->y - (finish->y - mid->y) / fraction;
\r
9805 fraction = fraction * 2;
\r
9811 HistorySet( char movelist[][2*MOVE_LEN], int first, int last, int current )
\r
9813 MoveHistorySet( movelist, first, last, current, pvInfoList );
\r
9815 EvalGraphSet( first, last, current, pvInfoList );
\r
9819 SettingsPopUp(ChessProgramState *cps)
\r
9820 { // [HGM] wrapper needed because handles must not be passed through back-end
\r
9821 EngineOptionsPopup(savedHwnd, cps);
\r
9824 int flock(int fid, int code)
\r
9826 HANDLE hFile = (HANDLE) _get_osfhandle(fid);
\r
9830 ov.OffsetHigh = 0;
\r
9832 case 1: LockFileEx(hFile, LOCKFILE_EXCLUSIVE_LOCK, 0, 1024, 0, &ov); break; // LOCK_SH
\r
9833 case 2: LockFileEx(hFile, LOCKFILE_EXCLUSIVE_LOCK, 0, 1024, 0, &ov); break; // LOCK_EX
\r
9834 case 3: UnlockFileEx(hFile, 0, 1024, 0, &ov); break; // LOCK_UN
\r
9835 default: return -1;
\r