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 Free Software Foundation, Inc.
\r
10 * Enhancements Copyright 2005 Alessandro Scotti
\r
12 * XBoard borrows its colors and the bitmaps.xchess bitmap set from XChess,
\r
13 * which was written and is copyrighted by Wayne Christopher.
\r
15 * The following terms apply to Digital Equipment Corporation's copyright
\r
16 * interest in XBoard:
\r
17 * ------------------------------------------------------------------------
\r
18 * All Rights Reserved
\r
20 * Permission to use, copy, modify, and distribute this software and its
\r
21 * documentation for any purpose and without fee is hereby granted,
\r
22 * provided that the above copyright notice appear in all copies and that
\r
23 * both that copyright notice and this permission notice appear in
\r
24 * supporting documentation, and that the name of Digital not be
\r
25 * used in advertising or publicity pertaining to distribution of the
\r
26 * software without specific, written prior permission.
\r
28 * DIGITAL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
\r
29 * ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL
\r
30 * DIGITAL BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
\r
31 * ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
\r
32 * WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
\r
33 * ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
\r
35 * ------------------------------------------------------------------------
\r
37 * The following terms apply to the enhanced version of XBoard
\r
38 * distributed by the Free Software Foundation:
\r
39 * ------------------------------------------------------------------------
\r
41 * GNU XBoard is free software: you can redistribute it and/or modify
\r
42 * it under the terms of the GNU General Public License as published by
\r
43 * the Free Software Foundation, either version 3 of the License, or (at
\r
44 * your option) any later version.
\r
46 * GNU XBoard is distributed in the hope that it will be useful, but
\r
47 * WITHOUT ANY WARRANTY; without even the implied warranty of
\r
48 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
\r
49 * General Public License for more details.
\r
51 * You should have received a copy of the GNU General Public License
\r
52 * along with this program. If not, see http://www.gnu.org/licenses/. *
\r
54 *------------------------------------------------------------------------
\r
55 ** See the file ChangeLog for a revision history. */
\r
59 #include <windows.h>
\r
60 #include <winuser.h>
\r
61 #include <winsock.h>
\r
62 #include <commctrl.h>
\r
68 #include <sys/stat.h>
\r
71 #include <commdlg.h>
\r
73 #include <richedit.h>
\r
74 #include <mmsystem.h>
\r
83 #include "frontend.h"
\r
84 #include "backend.h"
\r
85 #include "winboard.h"
\r
87 #include "wclipbrd.h"
\r
88 #include "woptions.h"
\r
89 #include "wsockerr.h"
\r
90 #include "defaults.h"
\r
94 //void InitEngineUCI( const char * iniDir, ChessProgramState * cps );
\r
97 void mysrandom(unsigned int seed);
\r
99 extern int whiteFlag, blackFlag;
\r
100 Boolean flipClock = FALSE;
\r
101 extern HANDLE chatHandle[];
\r
102 extern int ics_type;
\r
104 void DisplayHoldingsCount(HDC hdc, int x, int y, int align, int copyNumber);
\r
105 VOID NewVariantPopup(HWND hwnd);
\r
106 int FinishMove P((ChessMove moveType, int fromX, int fromY, int toX, int toY,
\r
107 /*char*/int promoChar));
\r
108 void DisplayMove P((int moveNumber));
\r
109 Boolean ParseFEN P((Board board, int *blackPlaysFirst, char *fen));
\r
110 void ChatPopUp P((char *s));
\r
112 ChessSquare piece;
\r
113 POINT pos; /* window coordinates of current pos */
\r
114 POINT lastpos; /* window coordinates of last pos - used for clipping */
\r
115 POINT from; /* board coordinates of the piece's orig pos */
\r
116 POINT to; /* board coordinates of the piece's new pos */
\r
119 static AnimInfo animInfo = { EmptySquare, {-1,-1}, {-1,-1}, {-1,-1} };
\r
122 POINT start; /* window coordinates of start pos */
\r
123 POINT pos; /* window coordinates of current pos */
\r
124 POINT lastpos; /* window coordinates of last pos - used for clipping */
\r
125 POINT from; /* board coordinates of the piece's orig pos */
\r
128 static DragInfo dragInfo = { {-1,-1}, {-1,-1}, {-1,-1}, {-1,-1} };
\r
131 POINT sq[2]; /* board coordinates of from, to squares */
\r
134 static HighlightInfo highlightInfo = { {{-1, -1}, {-1, -1}} };
\r
135 static HighlightInfo premoveHighlightInfo = { {{-1, -1}, {-1, -1}} };
\r
136 static HighlightInfo partnerHighlightInfo = { {{-1, -1}, {-1, -1}} };
\r
137 static HighlightInfo oldPartnerHighlight = { {{-1, -1}, {-1, -1}} };
\r
139 typedef struct { // [HGM] atomic
\r
140 int fromX, fromY, toX, toY, radius;
\r
143 static ExplodeInfo explodeInfo;
\r
145 /* Window class names */
\r
146 char szAppName[] = "WinBoard";
\r
147 char szConsoleName[] = "WBConsole";
\r
149 /* Title bar text */
\r
150 char szTitle[] = "WinBoard";
\r
151 char szConsoleTitle[] = "I C S Interaction";
\r
154 char *settingsFileName;
\r
155 Boolean saveSettingsOnExit;
\r
156 char installDir[MSG_SIZ];
\r
157 int errorExitStatus;
\r
159 BoardSize boardSize;
\r
160 Boolean chessProgram;
\r
161 //static int boardX, boardY;
\r
162 int minX, minY; // [HGM] placement: volatile limits on upper-left corner
\r
163 int squareSize, lineGap, minorSize;
\r
164 static int winW, winH;
\r
165 static RECT messageRect, whiteRect, blackRect, leftLogoRect, rightLogoRect; // [HGM] logo
\r
166 static int logoHeight = 0;
\r
167 static char messageText[MESSAGE_TEXT_MAX];
\r
168 static int clockTimerEvent = 0;
\r
169 static int loadGameTimerEvent = 0;
\r
170 static int analysisTimerEvent = 0;
\r
171 static DelayedEventCallback delayedTimerCallback;
\r
172 static int delayedTimerEvent = 0;
\r
173 static int buttonCount = 2;
\r
174 char *icsTextMenuString;
\r
176 char *firstChessProgramNames;
\r
177 char *secondChessProgramNames;
\r
179 #define PALETTESIZE 256
\r
181 HINSTANCE hInst; /* current instance */
\r
182 Boolean alwaysOnTop = FALSE;
\r
184 COLORREF lightSquareColor, darkSquareColor, whitePieceColor,
\r
185 blackPieceColor, highlightSquareColor, premoveHighlightColor;
\r
187 ColorClass currentColorClass;
\r
189 static HWND savedHwnd;
\r
190 HWND hCommPort = NULL; /* currently open comm port */
\r
191 static HWND hwndPause; /* pause button */
\r
192 static HBITMAP pieceBitmap[3][(int) BlackPawn]; /* [HGM] nr of bitmaps referred to bP in stead of wK */
\r
193 static HBRUSH lightSquareBrush, darkSquareBrush,
\r
194 blackSquareBrush, /* [HGM] for band between board and holdings */
\r
195 explodeBrush, /* [HGM] atomic */
\r
196 markerBrush, /* [HGM] markers */
\r
197 whitePieceBrush, blackPieceBrush, iconBkgndBrush /*, outlineBrush*/;
\r
198 static POINT gridEndpoints[(BOARD_RANKS + BOARD_FILES + 2) * 2];
\r
199 static DWORD gridVertexCounts[BOARD_RANKS + BOARD_FILES + 2];
\r
200 static HPEN gridPen = NULL;
\r
201 static HPEN highlightPen = NULL;
\r
202 static HPEN premovePen = NULL;
\r
203 static NPLOGPALETTE pLogPal;
\r
204 static BOOL paletteChanged = FALSE;
\r
205 static HICON iconWhite, iconBlack, iconCurrent;
\r
206 static int doingSizing = FALSE;
\r
207 static int lastSizing = 0;
\r
208 static int prevStderrPort;
\r
209 static HBITMAP userLogo;
\r
211 static HBITMAP liteBackTexture = NULL;
\r
212 static HBITMAP darkBackTexture = NULL;
\r
213 static int liteBackTextureMode = BACK_TEXTURE_MODE_PLAIN;
\r
214 static int darkBackTextureMode = BACK_TEXTURE_MODE_PLAIN;
\r
215 static int backTextureSquareSize = 0;
\r
216 static struct { int x; int y; int mode; } backTextureSquareInfo[BOARD_RANKS][BOARD_FILES];
\r
218 #if __GNUC__ && !defined(_winmajor)
\r
219 #define oldDialog 0 /* cygwin doesn't define _winmajor; mingw does */
\r
221 #if defined(_winmajor)
\r
222 #define oldDialog (_winmajor < 4)
\r
224 #define oldDialog 0
\r
228 #define INTERNATIONAL
\r
230 #ifdef INTERNATIONAL
\r
231 # define _(s) T_(s)
\r
237 # define Translate(x, y)
\r
238 # define LoadLanguageFile(s)
\r
241 #ifdef INTERNATIONAL
\r
243 Boolean barbaric; // flag indicating if translation is needed
\r
245 // list of item numbers used in each dialog (used to alter language at run time)
\r
247 #define ABOUTBOX -1 /* not sure why these are needed */
\r
248 #define ABOUTBOX2 -1
\r
250 int dialogItems[][40] = {
\r
251 { ABOUTBOX, IDOK, 400 },
\r
252 { DLG_TimeControl, IDC_Babble, OPT_TCUseMoves, OPT_TCUseInc, OPT_TCUseFixed,
\r
253 OPT_TCtext1, OPT_TCtext2, OPT_TCitext1, OPT_TCitext2, OPT_TCftext, GPB_Factors, IDC_Factor1, IDC_Factor2, IDOK, IDCANCEL },
\r
254 { DLG_LoadOptions, OPT_Autostep, OPT_AStext1, IDOK, IDCANCEL },
\r
255 { DLG_SaveOptions, OPT_Autosave, OPT_AVPrompt, OPT_AVToFile, OPT_AVBrowse,
\r
256 801, OPT_PGN, OPT_Old, OPT_OutOfBookInfo, IDOK, IDCANCEL },
\r
257 { 1536, 1090, IDC_Directories, 1089, 1091, IDOK, IDCANCEL, 1038, IDC_IndexNr, 1037 },
\r
258 { DLG_CommPort, IDOK, IDCANCEL, IDC_Port, IDC_Rate, IDC_Bits, IDC_Parity,
\r
259 IDC_Stop, IDC_Flow, OPT_SerialHelp },
\r
260 { DLG_EditComment, IDOK, OPT_CancelComment, OPT_ClearComment, OPT_EditComment },
\r
261 { DLG_PromotionKing, PB_Chancellor, PB_Archbishop, PB_Queen, PB_Rook,
\r
262 PB_Bishop, PB_Knight, PB_King, IDCANCEL, IDC_Yes, IDC_No, IDC_Centaur },
\r
263 { ABOUTBOX2, IDC_ChessBoard },
\r
264 { DLG_GameList, OPT_GameListLoad, OPT_GameListPrev, OPT_GameListNext,
\r
265 OPT_GameListClose, IDC_GameListDoFilter },
\r
266 { DLG_EditTags, IDOK, OPT_TagsCancel, OPT_EditTags },
\r
267 { DLG_Error, IDOK },
\r
268 { DLG_Colorize, IDOK, IDCANCEL, OPT_ChooseColor, OPT_Bold, OPT_Italic,
\r
269 OPT_Underline, OPT_Strikeout, OPT_Sample },
\r
270 { DLG_Question, IDOK, IDCANCEL, OPT_QuestionText },
\r
271 { DLG_Startup, IDC_Welcome, OPT_ChessEngine, OPT_ChessServer, OPT_View,
\r
272 IDC_SPECIFY_ENG_STATIC, IDC_SPECIFY_SERVER_STATIC, OPT_AnyAdditional,
\r
273 IDOK, IDCANCEL, IDM_HELPCONTENTS },
\r
274 { DLG_IndexNumber, IDC_Index },
\r
275 { DLG_TypeInMove, IDOK, IDCANCEL },
\r
276 { DLG_TypeInName, IDOK, IDCANCEL },
\r
277 { DLG_Sound, IDC_Event, OPT_NoSound, OPT_DefaultBeep, OPT_BuiltInSound,
\r
278 OPT_WavFile, OPT_BrowseSound, OPT_DefaultSounds, IDOK, IDCANCEL, OPT_PlaySound },
\r
279 { DLG_GeneralOptions, IDOK, IDCANCEL, OPT_AlwaysOnTop, OPT_HighlightLastMove,
\r
280 OPT_AlwaysQueen, OPT_PeriodicUpdates, OPT_AnimateDragging, OPT_PonderNextMove,
\r
281 OPT_AnimateMoving, OPT_PopupExitMessage, OPT_AutoFlag, OPT_PopupMoveErrors,
\r
282 OPT_AutoFlipView, OPT_ShowButtonBar, OPT_AutoRaiseBoard, OPT_ShowCoordinates,
\r
283 OPT_Blindfold, OPT_ShowThinking, OPT_HighlightDragging, OPT_TestLegality,
\r
284 OPT_SaveExtPGN, OPT_HideThinkFromHuman, OPT_ExtraInfoInMoveHistory,
\r
285 OPT_HighlightMoveArrow, OPT_AutoLogo },
\r
286 { DLG_IcsOptions, IDOK, IDCANCEL, OPT_AutoComment, OPT_AutoKibitz, OPT_AutoObserve,
\r
287 OPT_GetMoveList, OPT_LocalLineEditing, OPT_QuietPlay, OPT_SeekGraph, OPT_AutoRefresh,
\r
288 OPT_BgObserve, OPT_DualBoard, OPT_Premove, OPT_PremoveWhite, OPT_PremoveBlack,
\r
289 OPT_SmartMove, OPT_IcsAlarm, IDC_Sec, OPT_ChooseShoutColor, OPT_ChooseSShoutColor,
\r
290 OPT_ChooseChannel1Color, OPT_ChooseChannelColor, OPT_ChooseKibitzColor,
\r
291 OPT_ChooseTellColor, OPT_ChooseChallengeColor, OPT_ChooseRequestColor,
\r
292 OPT_ChooseSeekColor, OPT_ChooseNormalColor, OPT_ChooseBackgroundColor,
\r
293 OPT_DefaultColors, OPT_DontColorize, IDC_Boxes, GPB_Colors, GPB_Premove,
\r
294 GPB_General, GPB_Alarm },
\r
295 { DLG_BoardOptions, IDOK, IDCANCEL, OPT_SizeTiny, OPT_SizeTeeny, OPT_SizeDinky,
\r
296 OPT_SizePetite, OPT_SizeSlim, OPT_SizeSmall, OPT_SizeMediocre, OPT_SizeMiddling,
\r
297 OPT_SizeAverage, OPT_SizeModerate, OPT_SizeMedium, OPT_SizeBulky, OPT_SizeLarge,
\r
298 OPT_SizeBig, OPT_SizeHuge, OPT_SizeGiant, OPT_SizeColossal, OPT_SizeTitanic,
\r
299 OPT_ChooseLightSquareColor, OPT_ChooseDarkSquareColor, OPT_ChooseWhitePieceColor,
\r
300 OPT_ChooseBlackPieceColor, OPT_ChooseHighlightSquareColor, OPT_ChoosePremoveHighlightColor,
\r
301 OPT_Monochrome, OPT_AllWhite, OPT_UpsideDown, OPT_DefaultBoardColors, GPB_Colors,
\r
302 IDC_Light, IDC_Dark, IDC_White, IDC_Black, IDC_High, IDC_PreHigh, GPB_Size },
\r
303 { DLG_NewVariant, IDOK, IDCANCEL, OPT_VariantNormal, OPT_VariantFRC, OPT_VariantWildcastle,
\r
304 OPT_VariantNocastle, OPT_VariantLosers, OPT_VariantGiveaway, OPT_VariantSuicide,
\r
305 OPT_Variant3Check, OPT_VariantTwoKings, OPT_VariantAtomic, OPT_VariantCrazyhouse,
\r
306 OPT_VariantBughouse, OPT_VariantTwilight, OPT_VariantShogi, OPT_VariantSuper,
\r
307 OPT_VariantKnightmate, OPT_VariantBerolina, OPT_VariantCylinder, OPT_VariantFairy,
\r
308 OPT_VariantMakruk, OPT_VariantGothic, OPT_VariantCapablanca, OPT_VariantJanus,
\r
309 OPT_VariantCRC, OPT_VariantFalcon, OPT_VariantCourier, OPT_VariantGreat, OPT_VariantSChess,
\r
310 OPT_VariantShatranj, OPT_VariantXiangqi, GPB_Variant, GPB_Board, IDC_Height,
\r
311 IDC_Width, IDC_Hand, IDC_Pieces, IDC_Def },
\r
312 { DLG_Fonts, IDOK, IDCANCEL, OPT_ChooseClockFont, OPT_ChooseMessageFont,
\r
313 OPT_ChooseCoordFont, OPT_ChooseTagFont, OPT_ChooseCommentsFont, OPT_ChooseConsoleFont, OPT_ChooseMoveHistoryFont, OPT_DefaultFonts,
\r
314 OPT_ClockFont, OPT_MessageFont, OPT_CoordFont, OPT_EditTagsFont,
\r
315 OPT_CommentsFont, OPT_MessageFont5, GPB_Current, GPB_All, OPT_MessageFont6 },
\r
316 { DLG_NewGameFRC, IDC_NFG_Label, IDC_NFG_Random, IDOK, IDCANCEL },
\r
317 { DLG_GameListOptions, IDC_GLT, IDC_GLT_Up, IDC_GLT_Down, IDC_GLT_Restore,
\r
318 IDC_GLT_Default, IDOK, IDCANCEL, IDC_GLT_RestoreTo },
\r
319 { DLG_MoveHistory },
\r
320 { DLG_EvalGraph },
\r
321 { DLG_EngineOutput, IDC_EngineLabel1, IDC_Engine1_NPS, IDC_EngineLabel2, IDC_Engine2_NPS },
\r
322 { DLG_Chat, IDC_Partner, IDC_Clear, IDC_Send, },
\r
323 { DLG_EnginePlayOptions, IDC_EpPonder, IDC_EpShowThinking, IDC_EpHideThinkingHuman,
\r
324 IDC_EpPeriodicUpdates, GPB_Adjudications, IDC_Draw, IDC_Moves, IDC_Threshold,
\r
325 IDC_Centi, IDC_TestClaims, IDC_DetectMates, IDC_MaterialDraws, IDC_TrivialDraws,
\r
326 GPB_Apply, IDC_Rule, IDC_Repeats, IDC_ScoreAbs1, IDC_ScoreAbs2, IDOK, IDCANCEL },
\r
327 { DLG_OptionsUCI, IDC_PolyDir, IDC_BrowseForPolyglotDir, IDC_Hash, IDC_Path,
\r
328 IDC_BrowseForEGTB, IDC_Cache, IDC_UseBook, IDC_BrowseForBook, IDC_CPU, IDC_OwnBook1,
\r
329 IDC_OwnBook2, IDC_Depth, IDC_Variation, IDC_DefGames, IDOK, IDCANCEL },
\r
333 static char languageBuf[50000], *foreign[1000], *english[1000], *languageFile[MSG_SIZ];
\r
334 static int lastChecked;
\r
335 static char oldLanguage[MSG_SIZ], *menuText[10][30];
\r
336 extern int tinyLayout;
\r
337 extern char * menuBarText[][10];
\r
340 LoadLanguageFile(char *name)
\r
341 { //load the file with translations, and make a list of the strings to be translated, and their translations
\r
343 int i=0, j=0, n=0, k;
\r
346 if(!name || name[0] == NULLCHAR) return;
\r
347 snprintf(buf, MSG_SIZ, "%s%s", name, strchr(name, '.') ? "" : ".lng"); // auto-append lng extension
\r
348 if(!strcmp(buf, oldLanguage)) { barbaric = 1; return; } // this language already loaded; just switch on
\r
349 if((f = fopen(buf, "r")) == NULL) return;
\r
350 while((k = fgetc(f)) != EOF) {
\r
351 if(i >= sizeof(languageBuf)) { DisplayError("Language file too big", 0); return; }
\r
352 languageBuf[i] = k;
\r
354 if(languageBuf[n] == '"' && languageBuf[i-1] == '"') {
\r
356 if(p = strstr(languageBuf + n + 1, "\" === \"")) {
\r
357 if(p > languageBuf+n+2 && p+8 < languageBuf+i) {
\r
358 if(j >= sizeof(english)) { DisplayError("Too many translated strings", 0); return; }
\r
359 english[j] = languageBuf + n + 1; *p = 0;
\r
360 foreign[j++] = p + 7; languageBuf[i-1] = 0;
\r
361 //if(appData.debugMode) fprintf(debugFP, "translation: replace '%s' by '%s'\n", english[j-1], foreign[j-1]);
\r
366 } else if(i > 0 && languageBuf[i-1] == '\\') {
\r
368 case 'n': k = '\n'; break;
\r
369 case 'r': k = '\r'; break;
\r
370 case 't': k = '\t'; break;
\r
372 languageBuf[--i] = k;
\r
377 barbaric = (j != 0);
\r
378 safeStrCpy(oldLanguage, buf, sizeof(oldLanguage)/sizeof(oldLanguage[0]) );
\r
383 { // return the translation of the given string
\r
384 // efficiency can be improved a lot...
\r
386 //if(appData.debugMode) fprintf(debugFP, "T_(%s)\n", s);
\r
387 if(!barbaric) return s;
\r
388 if(!s) return ""; // sanity
\r
389 while(english[i]) {
\r
390 if(!strcmp(s, english[i])) return foreign[i];
\r
397 Translate(HWND hDlg, int dialogID)
\r
398 { // translate all text items in the given dialog
\r
400 char buf[MSG_SIZ], *s;
\r
401 //if(appData.debugMode) fprintf(debugFP, "Translate(%d)\n", dialogID);
\r
402 if(!barbaric) return;
\r
403 while(dialogItems[i][0] && dialogItems[i][0] != dialogID) i++; // find the dialog description
\r
404 if(dialogItems[i][0] != dialogID) return; // unknown dialog, should not happen
\r
405 GetWindowText( hDlg, buf, MSG_SIZ );
\r
407 //if(appData.debugMode) fprintf(debugFP, "WindowText '%s' -> '%s'\n", buf, s);
\r
408 if(strcmp(buf, s)) SetWindowText(hDlg, s); // replace by translated string (if different)
\r
409 for(j=1; k=dialogItems[i][j]; j++) { // translate all listed dialog items
\r
410 GetDlgItemText(hDlg, k, buf, MSG_SIZ);
\r
411 if(strlen(buf) == 0) continue;
\r
413 if(strcmp(buf, s)) SetDlgItemText(hDlg, k, s); // replace by translated string (if different)
\r
418 TranslateMenus(int addLanguage)
\r
421 WIN32_FIND_DATA fileData;
\r
423 #define IDM_English 1895
\r
425 HMENU mainMenu = GetMenu(hwndMain);
\r
426 for (i=GetMenuItemCount(mainMenu)-1; i>=0; i--) {
\r
427 HMENU subMenu = GetSubMenu(mainMenu, i);
\r
428 ModifyMenu(mainMenu, i, MF_STRING|MF_BYPOSITION|MF_POPUP|EnableMenuItem(mainMenu, i, MF_BYPOSITION),
\r
429 (UINT) subMenu, T_(menuBarText[tinyLayout][i]));
\r
430 for(j=GetMenuItemCount(subMenu)-1; j>=0; j--){
\r
432 UINT k = GetMenuItemID(subMenu, j);
\r
434 safeStrCpy(buf, menuText[i][j], sizeof(buf)/sizeof(buf[0]) ); else {
\r
435 GetMenuString(subMenu, j, buf, MSG_SIZ, MF_BYPOSITION);
\r
436 menuText[i][j] = strdup(buf); // remember original on first change
\r
438 if(buf[0] == NULLCHAR) continue;
\r
439 //fprintf(debugFP, "menu(%d,%d) = %s (%08x, %08x) %d\n", i, j, buf, mainMenu, subMenu, k);
\r
440 ModifyMenu(subMenu, j, MF_STRING|MF_BYPOSITION
\r
441 |CheckMenuItem(subMenu, j, MF_BYPOSITION)
\r
442 |EnableMenuItem(subMenu, j, MF_BYPOSITION), k, T_(buf));
\r
445 DrawMenuBar(hwndMain);
\r
448 if(!addLanguage) return;
\r
449 if((hFind = FindFirstFile("*.LNG", &fileData)) != INVALID_HANDLE_VALUE) {
\r
450 HMENU mainMenu = GetMenu(hwndMain);
\r
451 HMENU subMenu = GetSubMenu(mainMenu, GetMenuItemCount(mainMenu)-1);
\r
452 AppendMenu(subMenu, MF_SEPARATOR, (UINT_PTR) 0, NULL);
\r
453 AppendMenu(subMenu, MF_ENABLED|MF_STRING|(barbaric?MF_UNCHECKED:MF_CHECKED), (UINT_PTR) IDM_English, (LPCTSTR) "English");
\r
454 i = 0; lastChecked = IDM_English;
\r
456 char *p, *q = fileData.cFileName;
\r
457 int checkFlag = MF_UNCHECKED;
\r
458 languageFile[i] = strdup(q);
\r
459 if(barbaric && !strcmp(oldLanguage, q)) {
\r
460 checkFlag = MF_CHECKED;
\r
461 lastChecked = IDM_English + i + 1;
\r
462 CheckMenuItem(mainMenu, IDM_English, MF_BYCOMMAND|MF_UNCHECKED);
\r
464 *q = ToUpper(*q); while(*++q) *q = ToLower(*q);
\r
465 p = strstr(fileData.cFileName, ".lng");
\r
467 AppendMenu(subMenu, MF_ENABLED|MF_STRING|checkFlag, (UINT_PTR) IDM_English + ++i, (LPCTSTR) fileData.cFileName);
\r
468 } while(FindNextFile(hFind, &fileData));
\r
481 int cliWidth, cliHeight;
\r
484 SizeInfo sizeInfo[] =
\r
486 { "tiny", 21, 0, 1, 1, 0, 0 },
\r
487 { "teeny", 25, 1, 1, 1, 0, 0 },
\r
488 { "dinky", 29, 1, 1, 1, 0, 0 },
\r
489 { "petite", 33, 1, 1, 1, 0, 0 },
\r
490 { "slim", 37, 2, 1, 0, 0, 0 },
\r
491 { "small", 40, 2, 1, 0, 0, 0 },
\r
492 { "mediocre", 45, 2, 1, 0, 0, 0 },
\r
493 { "middling", 49, 2, 0, 0, 0, 0 },
\r
494 { "average", 54, 2, 0, 0, 0, 0 },
\r
495 { "moderate", 58, 3, 0, 0, 0, 0 },
\r
496 { "medium", 64, 3, 0, 0, 0, 0 },
\r
497 { "bulky", 72, 3, 0, 0, 0, 0 },
\r
498 { "large", 80, 3, 0, 0, 0, 0 },
\r
499 { "big", 87, 3, 0, 0, 0, 0 },
\r
500 { "huge", 95, 3, 0, 0, 0, 0 },
\r
501 { "giant", 108, 3, 0, 0, 0, 0 },
\r
502 { "colossal", 116, 4, 0, 0, 0, 0 },
\r
503 { "titanic", 129, 4, 0, 0, 0, 0 },
\r
504 { NULL, 0, 0, 0, 0, 0, 0 }
\r
507 #define MF(x) {x, {{0,}, 0. }, {0, }, 0}
\r
508 MyFont fontRec[NUM_SIZES][NUM_FONTS] =
\r
510 { 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
511 { 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
512 { 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
513 { 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
514 { 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
515 { 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
516 { 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
517 { 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
518 { 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
519 { 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
520 { 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
521 { 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
522 { 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
523 { 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
524 { 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
525 { 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
526 { 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
527 { 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
530 MyFont *font[NUM_SIZES][NUM_FONTS];
\r
539 #define BUTTON_WIDTH (tinyLayout ? 16 : 32)
\r
540 #define N_BUTTONS 5
\r
542 MyButtonDesc buttonDesc[N_BUTTONS] =
\r
544 {"<<", IDM_ToStart, NULL, NULL},
\r
545 {"<", IDM_Backward, NULL, NULL},
\r
546 {"P", IDM_Pause, NULL, NULL},
\r
547 {">", IDM_Forward, NULL, NULL},
\r
548 {">>", IDM_ToEnd, NULL, NULL},
\r
551 int tinyLayout = 0, smallLayout = 0;
\r
552 #define MENU_BAR_ITEMS 9
\r
553 char *menuBarText[2][MENU_BAR_ITEMS+1] = {
\r
554 { N_("&File"), N_("&Edit"), N_("&View"), N_("&Mode"), N_("&Action"), N_("E&ngine"), N_("&Options"), N_("&Help"), NULL },
\r
555 { N_("&F"), N_("&E"), N_("&V"), N_("&M"), N_("&A"), N_("&N"), N_("&O"), N_("&H"), NULL },
\r
559 MySound sounds[(int)NSoundClasses];
\r
560 MyTextAttribs textAttribs[(int)NColorClasses];
\r
562 MyColorizeAttribs colorizeAttribs[] = {
\r
563 { (COLORREF)0, 0, N_("Shout Text") },
\r
564 { (COLORREF)0, 0, N_("SShout/CShout") },
\r
565 { (COLORREF)0, 0, N_("Channel 1 Text") },
\r
566 { (COLORREF)0, 0, N_("Channel Text") },
\r
567 { (COLORREF)0, 0, N_("Kibitz Text") },
\r
568 { (COLORREF)0, 0, N_("Tell Text") },
\r
569 { (COLORREF)0, 0, N_("Challenge Text") },
\r
570 { (COLORREF)0, 0, N_("Request Text") },
\r
571 { (COLORREF)0, 0, N_("Seek Text") },
\r
572 { (COLORREF)0, 0, N_("Normal Text") },
\r
573 { (COLORREF)0, 0, N_("None") }
\r
578 static char *commentTitle;
\r
579 static char *commentText;
\r
580 static int commentIndex;
\r
581 static Boolean editComment = FALSE;
\r
584 char errorTitle[MSG_SIZ];
\r
585 char errorMessage[2*MSG_SIZ];
\r
586 HWND errorDialog = NULL;
\r
587 BOOLEAN moveErrorMessageUp = FALSE;
\r
588 BOOLEAN consoleEcho = TRUE;
\r
589 CHARFORMAT consoleCF;
\r
590 COLORREF consoleBackgroundColor;
\r
592 char *programVersion;
\r
598 typedef int CPKind;
\r
607 SOCKET sock2; /* stderr socket for OpenRcmd */
\r
610 #define INPUT_SOURCE_BUF_SIZE 4096
\r
612 typedef struct _InputSource {
\r
619 char buf[INPUT_SOURCE_BUF_SIZE];
\r
623 InputCallback func;
\r
624 struct _InputSource *second; /* for stderr thread on CPRcmd */
\r
628 InputSource *consoleInputSource;
\r
633 VOID ConsoleOutput(char* data, int length, int forceVisible);
\r
634 VOID ConsoleCreate();
\r
636 ConsoleWndProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam);
\r
637 VOID ColorizeTextPopup(HWND hwnd, ColorClass cc);
\r
638 VOID PrintCommSettings(FILE *f, char *name, DCB *dcb);
\r
639 VOID ParseCommSettings(char *arg, DCB *dcb);
\r
641 StartupDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam);
\r
642 VOID APIENTRY MenuPopup(HWND hwnd, POINT pt, HMENU hmenu, UINT def);
\r
643 void ParseIcsTextMenu(char *icsTextMenuString);
\r
644 VOID PopUpMoveDialog(char firstchar);
\r
645 VOID PopUpNameDialog(char firstchar);
\r
646 VOID UpdateSampleText(HWND hDlg, int id, MyColorizeAttribs *mca);
\r
650 int GameListOptions();
\r
652 int dummy; // [HGM] for obsolete args
\r
654 HWND hwndMain = NULL; /* root window*/
\r
655 HWND hwndConsole = NULL;
\r
656 HWND commentDialog = NULL;
\r
657 HWND moveHistoryDialog = NULL;
\r
658 HWND evalGraphDialog = NULL;
\r
659 HWND engineOutputDialog = NULL;
\r
660 HWND gameListDialog = NULL;
\r
661 HWND editTagsDialog = NULL;
\r
663 int commentUp = FALSE;
\r
665 WindowPlacement wpMain;
\r
666 WindowPlacement wpConsole;
\r
667 WindowPlacement wpComment;
\r
668 WindowPlacement wpMoveHistory;
\r
669 WindowPlacement wpEvalGraph;
\r
670 WindowPlacement wpEngineOutput;
\r
671 WindowPlacement wpGameList;
\r
672 WindowPlacement wpTags;
\r
674 VOID EngineOptionsPopup(); // [HGM] settings
\r
676 VOID GothicPopUp(char *title, VariantClass variant);
\r
678 * Setting "frozen" should disable all user input other than deleting
\r
679 * the window. We do this while engines are initializing themselves.
\r
681 static int frozen = 0;
\r
682 static int oldMenuItemState[MENU_BAR_ITEMS];
\r
688 if (frozen) return;
\r
690 hmenu = GetMenu(hwndMain);
\r
691 for (i=0; i<MENU_BAR_ITEMS; i++) {
\r
692 oldMenuItemState[i] = EnableMenuItem(hmenu, i, MF_BYPOSITION|MF_GRAYED);
\r
694 DrawMenuBar(hwndMain);
\r
697 /* Undo a FreezeUI */
\r
703 if (!frozen) return;
\r
705 hmenu = GetMenu(hwndMain);
\r
706 for (i=0; i<MENU_BAR_ITEMS; i++) {
\r
707 EnableMenuItem(hmenu, i, MF_BYPOSITION|oldMenuItemState[i]);
\r
709 DrawMenuBar(hwndMain);
\r
712 /*static*/ int fromX = -1, fromY = -1, toX, toY; // [HGM] moved upstream, so JAWS can use them
\r
714 /* JAWS preparation patch (WinBoard for the sight impaired). Define required insertions as empty */
\r
720 #define JAWS_ALT_INTERCEPT
\r
721 #define JAWS_KB_NAVIGATION
\r
722 #define JAWS_MENU_ITEMS
\r
723 #define JAWS_SILENCE
\r
724 #define JAWS_REPLAY
\r
726 #define JAWS_COPYRIGHT
\r
727 #define JAWS_DELETE(X) X
\r
728 #define SAYMACHINEMOVE()
\r
732 /*---------------------------------------------------------------------------*\
\r
736 \*---------------------------------------------------------------------------*/
\r
739 WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
\r
740 LPSTR lpCmdLine, int nCmdShow)
\r
743 HANDLE hAccelMain, hAccelNoAlt, hAccelNoICS;
\r
744 // INITCOMMONCONTROLSEX ex;
\r
748 LoadLibrary("RICHED32.DLL");
\r
749 consoleCF.cbSize = sizeof(CHARFORMAT);
\r
751 if (!InitApplication(hInstance)) {
\r
754 if (!InitInstance(hInstance, nCmdShow, lpCmdLine)) {
\r
760 // InitCommonControlsEx(&ex);
\r
761 InitCommonControls();
\r
763 hAccelMain = LoadAccelerators (hInstance, szAppName);
\r
764 hAccelNoAlt = LoadAccelerators (hInstance, "NO_ALT");
\r
765 hAccelNoICS = LoadAccelerators( hInstance, "NO_ICS"); /* [AS] No Ctrl-V on ICS!!! */
\r
767 /* Acquire and dispatch messages until a WM_QUIT message is received. */
\r
769 while (GetMessage(&msg, /* message structure */
\r
770 NULL, /* handle of window receiving the message */
\r
771 0, /* lowest message to examine */
\r
772 0)) /* highest message to examine */
\r
775 if(msg.message == WM_CHAR && msg.wParam == '\t') {
\r
776 // [HGM] navigate: switch between all windows with tab
\r
777 HWND e1 = NULL, e2 = NULL, mh = NULL, hInput = NULL, hText = NULL;
\r
778 int i, currentElement = 0;
\r
780 // first determine what element of the chain we come from (if any)
\r
781 if(appData.icsActive) {
\r
782 hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
783 hText = GetDlgItem(hwndConsole, OPT_ConsoleText);
\r
785 if(engineOutputDialog && EngineOutputIsUp()) {
\r
786 e1 = GetDlgItem(engineOutputDialog, IDC_EngineMemo1);
\r
787 e2 = GetDlgItem(engineOutputDialog, IDC_EngineMemo2);
\r
789 if(moveHistoryDialog && MoveHistoryIsUp()) {
\r
790 mh = GetDlgItem(moveHistoryDialog, IDC_MoveHistory);
\r
792 if(msg.hwnd == hwndMain) currentElement = 7 ; else
\r
793 if(msg.hwnd == engineOutputDialog) currentElement = 2; else
\r
794 if(msg.hwnd == e1) currentElement = 2; else
\r
795 if(msg.hwnd == e2) currentElement = 3; else
\r
796 if(msg.hwnd == moveHistoryDialog) currentElement = 4; else
\r
797 if(msg.hwnd == mh) currentElement = 4; else
\r
798 if(msg.hwnd == evalGraphDialog) currentElement = 6; else
\r
799 if(msg.hwnd == hText) currentElement = 5; else
\r
800 if(msg.hwnd == hInput) currentElement = 6; else
\r
801 for (i = 0; i < N_BUTTONS; i++) {
\r
802 if (buttonDesc[i].hwnd == msg.hwnd) { currentElement = 1; break; }
\r
805 // determine where to go to
\r
806 if(currentElement) { HWND h = NULL; int direction = GetKeyState(VK_SHIFT) < 0 ? -1 : 1;
\r
808 currentElement = (currentElement + direction) % 7;
\r
809 switch(currentElement) {
\r
811 h = hwndMain; break; // passing this case always makes the loop exit
\r
813 h = buttonDesc[0].hwnd; break; // could be NULL
\r
815 if(!EngineOutputIsUp()) continue; // skip closed auxiliary windows
\r
818 if(!EngineOutputIsUp()) continue;
\r
821 if(!MoveHistoryIsUp()) continue;
\r
823 // case 6: // input to eval graph does not seem to get here!
\r
824 // if(!EvalGraphIsUp()) continue;
\r
825 // h = evalGraphDialog; break;
\r
827 if(!appData.icsActive) continue;
\r
831 if(!appData.icsActive) continue;
\r
837 if(currentElement > 4 && IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);
\r
838 if(currentElement < 5 && IsIconic(hwndMain)) ShowWindow(hwndMain, SW_RESTORE); // all open together
\r
841 continue; // this message now has been processed
\r
845 if (!(commentDialog && IsDialogMessage(commentDialog, &msg)) &&
\r
846 !(moveHistoryDialog && IsDialogMessage(moveHistoryDialog, &msg)) &&
\r
847 !(evalGraphDialog && IsDialogMessage(evalGraphDialog, &msg)) &&
\r
848 !(engineOutputDialog && IsDialogMessage(engineOutputDialog, &msg)) &&
\r
849 !(editTagsDialog && IsDialogMessage(editTagsDialog, &msg)) &&
\r
850 !(gameListDialog && IsDialogMessage(gameListDialog, &msg)) &&
\r
851 !(errorDialog && IsDialogMessage(errorDialog, &msg)) &&
\r
852 !(!frozen && TranslateAccelerator(hwndMain, hAccelMain, &msg)) && JAWS_ACCEL
\r
853 !(!hwndConsole && TranslateAccelerator(hwndMain, hAccelNoICS, &msg)) &&
\r
854 !(!hwndConsole && TranslateAccelerator(hwndMain, hAccelNoAlt, &msg))) {
\r
855 int done = 0, i; // [HGM] chat: dispatch cat-box messages
\r
856 for(i=0; i<MAX_CHAT; i++)
\r
857 if(chatHandle[i] && IsDialogMessage(chatHandle[i], &msg)) {
\r
860 if(done) continue; // [HGM] chat: end patch
\r
861 TranslateMessage(&msg); /* Translates virtual key codes */
\r
862 DispatchMessage(&msg); /* Dispatches message to window */
\r
867 return (msg.wParam); /* Returns the value from PostQuitMessage */
\r
870 /*---------------------------------------------------------------------------*\
\r
872 * Initialization functions
\r
874 \*---------------------------------------------------------------------------*/
\r
878 { // update user logo if necessary
\r
879 static char oldUserName[MSG_SIZ], *curName;
\r
881 if(appData.autoLogo) {
\r
882 curName = UserName();
\r
883 if(strcmp(curName, oldUserName)) {
\r
884 snprintf(oldUserName, MSG_SIZ, "logos\\%s.bmp", curName);
\r
885 userLogo = LoadImage( 0, oldUserName, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );
\r
886 safeStrCpy(oldUserName, curName, sizeof(oldUserName)/sizeof(oldUserName[0]) );
\r
887 if(userLogo == NULL)
\r
888 userLogo = LoadImage( 0, "logos\\dummy.bmp", IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );
\r
894 InitApplication(HINSTANCE hInstance)
\r
898 /* Fill in window class structure with parameters that describe the */
\r
901 wc.style = CS_HREDRAW | CS_VREDRAW; /* Class style(s). */
\r
902 wc.lpfnWndProc = (WNDPROC)WndProc; /* Window Procedure */
\r
903 wc.cbClsExtra = 0; /* No per-class extra data. */
\r
904 wc.cbWndExtra = 0; /* No per-window extra data. */
\r
905 wc.hInstance = hInstance; /* Owner of this class */
\r
906 wc.hIcon = LoadIcon(hInstance, "icon_white");
\r
907 wc.hCursor = LoadCursor(NULL, IDC_ARROW); /* Cursor */
\r
908 wc.hbrBackground = (HBRUSH)(COLOR_WINDOW+1); /* Default color */
\r
909 wc.lpszMenuName = szAppName; /* Menu name from .RC */
\r
910 wc.lpszClassName = szAppName; /* Name to register as */
\r
912 /* Register the window class and return success/failure code. */
\r
913 if (!RegisterClass(&wc)) return FALSE;
\r
915 wc.style = CS_HREDRAW | CS_VREDRAW;
\r
916 wc.lpfnWndProc = (WNDPROC)ConsoleWndProc;
\r
918 wc.cbWndExtra = DLGWINDOWEXTRA;
\r
919 wc.hInstance = hInstance;
\r
920 wc.hIcon = LoadIcon(hInstance, "icon_white");
\r
921 wc.hCursor = LoadCursor(NULL, IDC_ARROW);
\r
922 wc.hbrBackground = (HBRUSH)(COLOR_MENU+1);
\r
923 wc.lpszMenuName = NULL;
\r
924 wc.lpszClassName = szConsoleName;
\r
926 if (!RegisterClass(&wc)) return FALSE;
\r
931 /* Set by InitInstance, used by EnsureOnScreen */
\r
932 int screenHeight, screenWidth;
\r
935 EnsureOnScreen(int *x, int *y, int minX, int minY)
\r
937 // int gap = GetSystemMetrics(SM_CYFRAME) + GetSystemMetrics(SM_CYCAPTION);
\r
938 /* Be sure window at (x,y) is not off screen (or even mostly off screen) */
\r
939 if (*x > screenWidth - 32) *x = 0;
\r
940 if (*y > screenHeight - 32) *y = 0;
\r
941 if (*x < minX) *x = minX;
\r
942 if (*y < minY) *y = minY;
\r
946 InitInstance(HINSTANCE hInstance, int nCmdShow, LPSTR lpCmdLine)
\r
948 HWND hwnd; /* Main window handle. */
\r
950 WINDOWPLACEMENT wp;
\r
953 hInst = hInstance; /* Store instance handle in our global variable */
\r
954 programName = szAppName;
\r
956 if (SearchPath(NULL, "WinBoard.exe", NULL, MSG_SIZ, installDir, &filepart)) {
\r
957 *filepart = NULLCHAR;
\r
959 GetCurrentDirectory(MSG_SIZ, installDir);
\r
961 gameInfo.boardWidth = gameInfo.boardHeight = 8; // [HGM] won't have open window otherwise
\r
962 screenWidth = screenHeight = 1000; // [HGM] placement: kludge to allow calling EnsureOnScreen from InitAppData
\r
963 InitAppData(lpCmdLine); /* Get run-time parameters */
\r
964 /* xboard, and older WinBoards, controlled the move sound with the
\r
965 appData.ringBellAfterMoves option. In the current WinBoard, we
\r
966 always turn the option on (so that the backend will call us),
\r
967 then let the user turn the sound off by setting it to silence if
\r
968 desired. To accommodate old winboard.ini files saved by old
\r
969 versions of WinBoard, we also turn off the sound if the option
\r
970 was initially set to false. [HGM] taken out of InitAppData */
\r
971 if (!appData.ringBellAfterMoves) {
\r
972 sounds[(int)SoundMove].name = strdup("");
\r
973 appData.ringBellAfterMoves = TRUE;
\r
975 if (appData.debugMode) {
\r
976 debugFP = fopen(appData.nameOfDebugFile, "w");
\r
977 setbuf(debugFP, NULL);
\r
980 LoadLanguageFile(appData.language);
\r
984 // InitEngineUCI( installDir, &first ); // [HGM] incorporated in InitBackEnd1()
\r
985 // InitEngineUCI( installDir, &second );
\r
987 /* Create a main window for this application instance. */
\r
988 hwnd = CreateWindow(szAppName, szTitle,
\r
989 (WS_OVERLAPPEDWINDOW & ~WS_MAXIMIZEBOX),
\r
990 CW_USEDEFAULT, 0, CW_USEDEFAULT, 0,
\r
991 NULL, NULL, hInstance, NULL);
\r
994 /* If window could not be created, return "failure" */
\r
999 /* [HGM] logo: Load logos if specified (must be done before InitDrawingSizes) */
\r
1000 if( appData.firstLogo && appData.firstLogo[0] != NULLCHAR) {
\r
1001 first.programLogo = LoadImage( 0, appData.firstLogo, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );
\r
1003 if (first.programLogo == NULL && appData.debugMode) {
\r
1004 fprintf( debugFP, "Unable to load logo bitmap '%s'\n", appData.firstLogo );
\r
1006 } else if(appData.autoLogo) {
\r
1007 if(appData.firstDirectory && appData.firstDirectory[0]) {
\r
1008 char buf[MSG_SIZ];
\r
1009 snprintf(buf, MSG_SIZ, "%s/logo.bmp", appData.firstDirectory);
\r
1010 first.programLogo = LoadImage( 0, buf, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );
\r
1014 if( appData.secondLogo && appData.secondLogo[0] != NULLCHAR) {
\r
1015 second.programLogo = LoadImage( 0, appData.secondLogo, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );
\r
1017 if (second.programLogo == NULL && appData.debugMode) {
\r
1018 fprintf( debugFP, "Unable to load logo bitmap '%s'\n", appData.secondLogo );
\r
1020 } else if(appData.autoLogo) {
\r
1021 char buf[MSG_SIZ];
\r
1022 if(appData.icsActive) { // [HGM] logo: in ICS mode second can be used for ICS
\r
1023 snprintf(buf, MSG_SIZ, "logos\\%s.bmp", appData.icsHost);
\r
1024 second.programLogo = LoadImage( 0, buf, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );
\r
1026 if(appData.secondDirectory && appData.secondDirectory[0]) {
\r
1027 snprintf(buf, MSG_SIZ, "%s\\logo.bmp", appData.secondDirectory);
\r
1028 second.programLogo = LoadImage( 0, buf, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );
\r
1034 iconWhite = LoadIcon(hInstance, "icon_white");
\r
1035 iconBlack = LoadIcon(hInstance, "icon_black");
\r
1036 iconCurrent = iconWhite;
\r
1037 InitDrawingColors();
\r
1038 screenHeight = GetSystemMetrics(SM_CYSCREEN);
\r
1039 screenWidth = GetSystemMetrics(SM_CXSCREEN);
\r
1040 for (ibs = (int) NUM_SIZES - 1; ibs >= 0; ibs--) {
\r
1041 /* Compute window size for each board size, and use the largest
\r
1042 size that fits on this screen as the default. */
\r
1043 InitDrawingSizes((BoardSize)(ibs+1000), 0);
\r
1044 if (boardSize == (BoardSize)-1 &&
\r
1045 winH <= screenHeight
\r
1046 - GetSystemMetrics(SM_CYFRAME) - GetSystemMetrics(SM_CYCAPTION) - 10
\r
1047 && winW <= screenWidth) {
\r
1048 boardSize = (BoardSize)ibs;
\r
1052 InitDrawingSizes(boardSize, 0);
\r
1053 TranslateMenus(1);
\r
1055 buttonCount = GetSystemMetrics(SM_CMOUSEBUTTONS);
\r
1057 /* [AS] Load textures if specified */
\r
1058 ZeroMemory( &backTextureSquareInfo, sizeof(backTextureSquareInfo) );
\r
1060 if( appData.liteBackTextureFile && appData.liteBackTextureFile[0] != NULLCHAR && appData.liteBackTextureFile[0] != '*' ) {
\r
1061 liteBackTexture = LoadImage( 0, appData.liteBackTextureFile, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );
\r
1062 liteBackTextureMode = appData.liteBackTextureMode;
\r
1064 if (liteBackTexture == NULL && appData.debugMode) {
\r
1065 fprintf( debugFP, "Unable to load lite texture bitmap '%s'\n", appData.liteBackTextureFile );
\r
1069 if( appData.darkBackTextureFile && appData.darkBackTextureFile[0] != NULLCHAR && appData.darkBackTextureFile[0] != '*' ) {
\r
1070 darkBackTexture = LoadImage( 0, appData.darkBackTextureFile, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );
\r
1071 darkBackTextureMode = appData.darkBackTextureMode;
\r
1073 if (darkBackTexture == NULL && appData.debugMode) {
\r
1074 fprintf( debugFP, "Unable to load dark texture bitmap '%s'\n", appData.darkBackTextureFile );
\r
1078 mysrandom( (unsigned) time(NULL) );
\r
1080 /* [AS] Restore layout */
\r
1081 if( wpMoveHistory.visible ) {
\r
1082 MoveHistoryPopUp();
\r
1085 if( wpEvalGraph.visible ) {
\r
1089 if( wpEngineOutput.visible ) {
\r
1090 EngineOutputPopUp();
\r
1093 /* Make the window visible; update its client area; and return "success" */
\r
1094 EnsureOnScreen(&wpMain.x, &wpMain.y, minX, minY);
\r
1095 wp.length = sizeof(WINDOWPLACEMENT);
\r
1097 wp.showCmd = nCmdShow;
\r
1098 wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;
\r
1099 wp.rcNormalPosition.left = wpMain.x;
\r
1100 wp.rcNormalPosition.right = wpMain.x + wpMain.width;
\r
1101 wp.rcNormalPosition.top = wpMain.y;
\r
1102 wp.rcNormalPosition.bottom = wpMain.y + wpMain.height;
\r
1103 SetWindowPlacement(hwndMain, &wp);
\r
1105 InitBackEnd2(); // [HGM] moved until after all windows placed, to save correct position if fatal error on engine start
\r
1107 if(!appData.noGUI) SetWindowPos(hwndMain, alwaysOnTop ? HWND_TOPMOST : HWND_NOTOPMOST,
\r
1108 0, 0, 0, 0, SWP_NOMOVE|SWP_NOSIZE);
\r
1110 if (hwndConsole) {
\r
1112 SetWindowPos(hwndConsole, alwaysOnTop ? HWND_TOPMOST : HWND_NOTOPMOST,
\r
1113 0, 0, 0, 0, SWP_NOMOVE|SWP_NOSIZE);
\r
1115 ShowWindow(hwndConsole, nCmdShow);
\r
1116 if(appData.chatBoxes) { // [HGM] chat: open chat boxes
\r
1117 char buf[MSG_SIZ], *p = buf, *q;
\r
1118 safeStrCpy(buf, appData.chatBoxes, sizeof(buf)/sizeof(buf[0]) );
\r
1120 q = strchr(p, ';');
\r
1122 if(*p) ChatPopUp(p);
\r
1125 SetActiveWindow(hwndConsole);
\r
1127 if(!appData.noGUI) UpdateWindow(hwnd); else ShowWindow(hwnd, SW_MINIMIZE);
\r
1128 if(gameListDialog) SetFocus(gameListDialog); // [HGM] jaws: for if we clicked multi-game game file
\r
1137 HMENU hmenu = GetMenu(hwndMain);
\r
1139 (void) EnableMenuItem(hmenu, IDM_CommPort,
\r
1140 MF_BYCOMMAND|((appData.icsActive &&
\r
1141 *appData.icsCommPort != NULLCHAR) ?
\r
1142 MF_ENABLED : MF_GRAYED));
\r
1143 (void) CheckMenuItem(hmenu, IDM_SaveSettingsOnExit,
\r
1144 MF_BYCOMMAND|(saveSettingsOnExit ?
\r
1145 MF_CHECKED : MF_UNCHECKED));
\r
1148 //---------------------------------------------------------------------------------------------------------
\r
1150 #define ICS_TEXT_MENU_SIZE (IDM_CommandXLast - IDM_CommandX + 1)
\r
1151 #define XBOARD FALSE
\r
1153 #define OPTCHAR "/"
\r
1154 #define SEPCHAR "="
\r
1158 // front-end part of option handling
\r
1161 LFfromMFP(LOGFONT* lf, MyFontParams *mfp)
\r
1163 HDC hdc = CreateDC("DISPLAY", NULL, NULL, NULL);
\r
1164 lf->lfHeight = -(int)(mfp->pointSize * GetDeviceCaps(hdc, LOGPIXELSY) / 72.0 + 0.5);
\r
1167 lf->lfEscapement = 0;
\r
1168 lf->lfOrientation = 0;
\r
1169 lf->lfWeight = mfp->bold ? FW_BOLD : FW_NORMAL;
\r
1170 lf->lfItalic = mfp->italic;
\r
1171 lf->lfUnderline = mfp->underline;
\r
1172 lf->lfStrikeOut = mfp->strikeout;
\r
1173 lf->lfCharSet = mfp->charset;
\r
1174 lf->lfOutPrecision = OUT_DEFAULT_PRECIS;
\r
1175 lf->lfClipPrecision = CLIP_DEFAULT_PRECIS;
\r
1176 lf->lfQuality = DEFAULT_QUALITY;
\r
1177 lf->lfPitchAndFamily = DEFAULT_PITCH|FF_DONTCARE;
\r
1178 safeStrCpy(lf->lfFaceName, mfp->faceName, sizeof(lf->lfFaceName)/sizeof(lf->lfFaceName[0]) );
\r
1182 CreateFontInMF(MyFont *mf)
\r
1184 LFfromMFP(&mf->lf, &mf->mfp);
\r
1185 if (mf->hf) DeleteObject(mf->hf);
\r
1186 mf->hf = CreateFontIndirect(&mf->lf);
\r
1189 // [HGM] This platform-dependent table provides the location for storing the color info
\r
1191 colorVariable[] = {
\r
1192 &whitePieceColor,
\r
1193 &blackPieceColor,
\r
1194 &lightSquareColor,
\r
1195 &darkSquareColor,
\r
1196 &highlightSquareColor,
\r
1197 &premoveHighlightColor,
\r
1199 &consoleBackgroundColor,
\r
1200 &appData.fontForeColorWhite,
\r
1201 &appData.fontBackColorWhite,
\r
1202 &appData.fontForeColorBlack,
\r
1203 &appData.fontBackColorBlack,
\r
1204 &appData.evalHistColorWhite,
\r
1205 &appData.evalHistColorBlack,
\r
1206 &appData.highlightArrowColor,
\r
1209 /* Command line font name parser. NULL name means do nothing.
\r
1210 Syntax like "Courier New:10.0 bi" or "Arial:10" or "Arial:10b"
\r
1211 For backward compatibility, syntax without the colon is also
\r
1212 accepted, but font names with digits in them won't work in that case.
\r
1215 ParseFontName(char *name, MyFontParams *mfp)
\r
1218 if (name == NULL) return;
\r
1220 q = strchr(p, ':');
\r
1222 if (q - p >= sizeof(mfp->faceName))
\r
1223 ExitArgError(_("Font name too long:"), name);
\r
1224 memcpy(mfp->faceName, p, q - p);
\r
1225 mfp->faceName[q - p] = NULLCHAR;
\r
1228 q = mfp->faceName;
\r
1229 while (*p && !isdigit(*p)) {
\r
1231 if (q - mfp->faceName >= sizeof(mfp->faceName))
\r
1232 ExitArgError(_("Font name too long:"), name);
\r
1234 while (q > mfp->faceName && q[-1] == ' ') q--;
\r
1237 if (!*p) ExitArgError(_("Font point size missing:"), name);
\r
1238 mfp->pointSize = (float) atof(p);
\r
1239 mfp->bold = (strchr(p, 'b') != NULL);
\r
1240 mfp->italic = (strchr(p, 'i') != NULL);
\r
1241 mfp->underline = (strchr(p, 'u') != NULL);
\r
1242 mfp->strikeout = (strchr(p, 's') != NULL);
\r
1243 mfp->charset = DEFAULT_CHARSET;
\r
1244 q = strchr(p, 'c');
\r
1246 mfp->charset = (BYTE) atoi(q+1);
\r
1250 ParseFont(char *name, int number)
\r
1251 { // wrapper to shield back-end from 'font'
\r
1252 ParseFontName(name, &font[boardSize][number]->mfp);
\r
1257 { // in WB we have a 2D array of fonts; this initializes their description
\r
1259 /* Point font array elements to structures and
\r
1260 parse default font names */
\r
1261 for (i=0; i<NUM_FONTS; i++) {
\r
1262 for (j=0; j<NUM_SIZES; j++) {
\r
1263 font[j][i] = &fontRec[j][i];
\r
1264 ParseFontName(font[j][i]->def, &font[j][i]->mfp);
\r
1271 { // here we create the actual fonts from the selected descriptions
\r
1273 for (i=0; i<NUM_FONTS; i++) {
\r
1274 for (j=0; j<NUM_SIZES; j++) {
\r
1275 CreateFontInMF(font[j][i]);
\r
1279 /* Color name parser.
\r
1280 X version accepts X color names, but this one
\r
1281 handles only the #rrggbb form (hex) or rrr,ggg,bbb (decimal) */
\r
1283 ParseColorName(char *name)
\r
1285 int red, green, blue, count;
\r
1286 char buf[MSG_SIZ];
\r
1288 count = sscanf(name, "#%2x%2x%2x", &red, &green, &blue);
\r
1290 count = sscanf(name, "%3d%*[^0-9]%3d%*[^0-9]%3d",
\r
1291 &red, &green, &blue);
\r
1294 snprintf(buf, MSG_SIZ, _("Can't parse color name %s"), name);
\r
1295 DisplayError(buf, 0);
\r
1296 return RGB(0, 0, 0);
\r
1298 return PALETTERGB(red, green, blue);
\r
1302 ParseColor(int n, char *name)
\r
1303 { // for WinBoard the color is an int, which needs to be derived from the string
\r
1304 if(colorVariable[n]) *(int*)colorVariable[n] = ParseColorName(name);
\r
1308 ParseAttribs(COLORREF *color, int *effects, char* argValue)
\r
1310 char *e = argValue;
\r
1314 if (*e == 'b') eff |= CFE_BOLD;
\r
1315 else if (*e == 'i') eff |= CFE_ITALIC;
\r
1316 else if (*e == 'u') eff |= CFE_UNDERLINE;
\r
1317 else if (*e == 's') eff |= CFE_STRIKEOUT;
\r
1318 else if (*e == '#' || isdigit(*e)) break;
\r
1322 *color = ParseColorName(e);
\r
1326 ParseTextAttribs(ColorClass cc, char *s)
\r
1327 { // [HGM] front-end wrapper that does the platform-dependent call
\r
1328 // for XBoard we would set (&appData.colorShout)[cc] = strdup(s);
\r
1329 ParseAttribs(&textAttribs[cc].color, &textAttribs[cc].effects, s);
\r
1333 ParseBoardSize(void *addr, char *name)
\r
1334 { // [HGM] rewritten with return-value ptr to shield back-end from BoardSize
\r
1335 BoardSize bs = SizeTiny;
\r
1336 while (sizeInfo[bs].name != NULL) {
\r
1337 if (StrCaseCmp(name, sizeInfo[bs].name) == 0) {
\r
1338 *(BoardSize *)addr = bs;
\r
1343 ExitArgError(_("Unrecognized board size value"), name);
\r
1348 { // [HGM] import name from appData first
\r
1351 for (cc = (ColorClass)0; cc < ColorNormal; cc++) {
\r
1352 textAttribs[cc].sound.name = strdup((&appData.soundShout)[cc]);
\r
1353 textAttribs[cc].sound.data = NULL;
\r
1354 MyLoadSound(&textAttribs[cc].sound);
\r
1356 for (cc = ColorNormal; cc < NColorClasses; cc++) {
\r
1357 textAttribs[cc].sound.name = strdup("");
\r
1358 textAttribs[cc].sound.data = NULL;
\r
1360 for (sc = (SoundClass)0; sc < NSoundClasses; sc++) {
\r
1361 sounds[sc].name = strdup((&appData.soundMove)[sc]);
\r
1362 sounds[sc].data = NULL;
\r
1363 MyLoadSound(&sounds[sc]);
\r
1368 SetCommPortDefaults()
\r
1370 memset(&dcb, 0, sizeof(DCB)); // required by VS 2002 +
\r
1371 dcb.DCBlength = sizeof(DCB);
\r
1372 dcb.BaudRate = 9600;
\r
1373 dcb.fBinary = TRUE;
\r
1374 dcb.fParity = FALSE;
\r
1375 dcb.fOutxCtsFlow = FALSE;
\r
1376 dcb.fOutxDsrFlow = FALSE;
\r
1377 dcb.fDtrControl = DTR_CONTROL_ENABLE;
\r
1378 dcb.fDsrSensitivity = FALSE;
\r
1379 dcb.fTXContinueOnXoff = TRUE;
\r
1380 dcb.fOutX = FALSE;
\r
1382 dcb.fNull = FALSE;
\r
1383 dcb.fRtsControl = RTS_CONTROL_ENABLE;
\r
1384 dcb.fAbortOnError = FALSE;
\r
1386 dcb.Parity = SPACEPARITY;
\r
1387 dcb.StopBits = ONESTOPBIT;
\r
1390 // [HGM] args: these three cases taken out to stay in front-end
\r
1392 SaveFontArg(FILE *f, ArgDescriptor *ad)
\r
1393 { // in WinBoard every board size has its own font, and the "argLoc" identifies the table,
\r
1394 // while the curent board size determines the element. This system should be ported to XBoard.
\r
1395 // What the table contains pointers to, and how to print the font description, remains platform-dependent
\r
1397 for (bs=0; bs<NUM_SIZES; bs++) {
\r
1398 MyFontParams *mfp = &font[bs][(int) ad->argLoc]->mfp;
\r
1399 fprintf(f, "/size=%s ", sizeInfo[bs].name);
\r
1400 fprintf(f, "/%s=\"%s:%g%s%s%s%s%sc%d\"\n",
\r
1401 ad->argName, mfp->faceName, mfp->pointSize,
\r
1402 mfp->bold || mfp->italic || mfp->underline || mfp->strikeout ? " " : "",
\r
1403 mfp->bold ? "b" : "",
\r
1404 mfp->italic ? "i" : "",
\r
1405 mfp->underline ? "u" : "",
\r
1406 mfp->strikeout ? "s" : "",
\r
1407 (int)mfp->charset);
\r
1413 { // [HGM] copy the names from the internal WB variables to appData
\r
1416 for (cc = (ColorClass)0; cc < ColorNormal; cc++)
\r
1417 (&appData.soundShout)[cc] = textAttribs[cc].sound.name;
\r
1418 for (sc = (SoundClass)0; sc < NSoundClasses; sc++)
\r
1419 (&appData.soundMove)[sc] = sounds[sc].name;
\r
1423 SaveAttribsArg(FILE *f, ArgDescriptor *ad)
\r
1424 { // here the "argLoc" defines a table index. It could have contained the 'ta' pointer itself, though
\r
1425 MyTextAttribs* ta = &textAttribs[(ColorClass)ad->argLoc];
\r
1426 fprintf(f, "/%s=\"%s%s%s%s%s#%02lx%02lx%02lx\"\n", ad->argName,
\r
1427 (ta->effects & CFE_BOLD) ? "b" : "",
\r
1428 (ta->effects & CFE_ITALIC) ? "i" : "",
\r
1429 (ta->effects & CFE_UNDERLINE) ? "u" : "",
\r
1430 (ta->effects & CFE_STRIKEOUT) ? "s" : "",
\r
1431 (ta->effects) ? " " : "",
\r
1432 ta->color&0xff, (ta->color >> 8)&0xff, (ta->color >> 16)&0xff);
\r
1436 SaveColor(FILE *f, ArgDescriptor *ad)
\r
1437 { // in WinBoard the color is an int and has to be converted to text. In X it would be a string already?
\r
1438 COLORREF color = *(COLORREF *)colorVariable[(int)ad->argLoc];
\r
1439 fprintf(f, "/%s=#%02lx%02lx%02lx\n", ad->argName,
\r
1440 color&0xff, (color>>8)&0xff, (color>>16)&0xff);
\r
1444 SaveBoardSize(FILE *f, char *name, void *addr)
\r
1445 { // wrapper to shield back-end from BoardSize & sizeInfo
\r
1446 fprintf(f, "/%s=%s\n", name, sizeInfo[*(BoardSize *)addr].name);
\r
1450 ParseCommPortSettings(char *s)
\r
1451 { // wrapper to keep dcb from back-end
\r
1452 ParseCommSettings(s, &dcb);
\r
1457 { // wrapper to shield use of window handles from back-end (make addressible by number?)
\r
1458 GetActualPlacement(hwndMain, &wpMain);
\r
1459 GetActualPlacement(hwndConsole, &wpConsole);
\r
1460 GetActualPlacement(commentDialog, &wpComment);
\r
1461 GetActualPlacement(editTagsDialog, &wpTags);
\r
1462 GetActualPlacement(gameListDialog, &wpGameList);
\r
1463 GetActualPlacement(moveHistoryDialog, &wpMoveHistory);
\r
1464 GetActualPlacement(evalGraphDialog, &wpEvalGraph);
\r
1465 GetActualPlacement(engineOutputDialog, &wpEngineOutput);
\r
1469 PrintCommPortSettings(FILE *f, char *name)
\r
1470 { // wrapper to shield back-end from DCB
\r
1471 PrintCommSettings(f, name, &dcb);
\r
1475 MySearchPath(char *installDir, char *name, char *fullname)
\r
1477 char *dummy, buf[MSG_SIZ], *p = name, *q;
\r
1478 if(name[0]== '%') {
\r
1479 fullname[0] = 0; // [HGM] first expand any environment variables in the given name
\r
1480 while(*p == '%' && (q = strchr(p+1, '%'))) { // [HGM] recognize %*% as environment variable
\r
1481 safeStrCpy(buf, p+1, sizeof(buf)/sizeof(buf[0]) );
\r
1482 *strchr(buf, '%') = 0;
\r
1483 strcat(fullname, getenv(buf));
\r
1484 p = q+1; while(*p == '\\') { strcat(fullname, "\\"); p++; }
\r
1486 strcat(fullname, p); // after environment variables (if any), take the remainder of the given name
\r
1487 if(appData.debugMode) fprintf(debugFP, "name = '%s', expanded name = '%s'\n", name, fullname);
\r
1488 return (int) strlen(fullname);
\r
1490 return (int) SearchPath(installDir, name, NULL, MSG_SIZ, fullname, &dummy);
\r
1494 MyGetFullPathName(char *name, char *fullname)
\r
1497 return (int) GetFullPathName(name, MSG_SIZ, fullname, &dummy);
\r
1502 { // [HGM] args: allows testing if main window is realized from back-end
\r
1503 return hwndMain != NULL;
\r
1507 PopUpStartupDialog()
\r
1511 LoadLanguageFile(appData.language);
\r
1512 lpProc = MakeProcInstance((FARPROC)StartupDialog, hInst);
\r
1513 DialogBox(hInst, MAKEINTRESOURCE(DLG_Startup), NULL, (DLGPROC)lpProc);
\r
1514 FreeProcInstance(lpProc);
\r
1517 /*---------------------------------------------------------------------------*\
\r
1519 * GDI board drawing routines
\r
1521 \*---------------------------------------------------------------------------*/
\r
1523 /* [AS] Draw square using background texture */
\r
1524 static void DrawTile( int dx, int dy, int dw, int dh, HDC dst, HDC src, int mode, int sx, int sy )
\r
1529 return; /* Should never happen! */
\r
1532 SetGraphicsMode( dst, GM_ADVANCED );
\r
1539 /* X reflection */
\r
1544 x.eDx = (FLOAT) dw + dx - 1;
\r
1547 SetWorldTransform( dst, &x );
\r
1550 /* Y reflection */
\r
1556 x.eDy = (FLOAT) dh + dy - 1;
\r
1558 SetWorldTransform( dst, &x );
\r
1566 x.eDx = (FLOAT) dx;
\r
1567 x.eDy = (FLOAT) dy;
\r
1570 SetWorldTransform( dst, &x );
\r
1574 BitBlt( dst, dx, dy, dw, dh, src, sx, sy, SRCCOPY );
\r
1582 SetWorldTransform( dst, &x );
\r
1584 ModifyWorldTransform( dst, 0, MWT_IDENTITY );
\r
1587 /* [AS] [HGM] Make room for more piece types, so all pieces can be different */
\r
1589 PM_WP = (int) WhitePawn,
\r
1590 PM_WN = (int) WhiteKnight,
\r
1591 PM_WB = (int) WhiteBishop,
\r
1592 PM_WR = (int) WhiteRook,
\r
1593 PM_WQ = (int) WhiteQueen,
\r
1594 PM_WF = (int) WhiteFerz,
\r
1595 PM_WW = (int) WhiteWazir,
\r
1596 PM_WE = (int) WhiteAlfil,
\r
1597 PM_WM = (int) WhiteMan,
\r
1598 PM_WO = (int) WhiteCannon,
\r
1599 PM_WU = (int) WhiteUnicorn,
\r
1600 PM_WH = (int) WhiteNightrider,
\r
1601 PM_WA = (int) WhiteAngel,
\r
1602 PM_WC = (int) WhiteMarshall,
\r
1603 PM_WAB = (int) WhiteCardinal,
\r
1604 PM_WD = (int) WhiteDragon,
\r
1605 PM_WL = (int) WhiteLance,
\r
1606 PM_WS = (int) WhiteCobra,
\r
1607 PM_WV = (int) WhiteFalcon,
\r
1608 PM_WSG = (int) WhiteSilver,
\r
1609 PM_WG = (int) WhiteGrasshopper,
\r
1610 PM_WK = (int) WhiteKing,
\r
1611 PM_BP = (int) BlackPawn,
\r
1612 PM_BN = (int) BlackKnight,
\r
1613 PM_BB = (int) BlackBishop,
\r
1614 PM_BR = (int) BlackRook,
\r
1615 PM_BQ = (int) BlackQueen,
\r
1616 PM_BF = (int) BlackFerz,
\r
1617 PM_BW = (int) BlackWazir,
\r
1618 PM_BE = (int) BlackAlfil,
\r
1619 PM_BM = (int) BlackMan,
\r
1620 PM_BO = (int) BlackCannon,
\r
1621 PM_BU = (int) BlackUnicorn,
\r
1622 PM_BH = (int) BlackNightrider,
\r
1623 PM_BA = (int) BlackAngel,
\r
1624 PM_BC = (int) BlackMarshall,
\r
1625 PM_BG = (int) BlackGrasshopper,
\r
1626 PM_BAB = (int) BlackCardinal,
\r
1627 PM_BD = (int) BlackDragon,
\r
1628 PM_BL = (int) BlackLance,
\r
1629 PM_BS = (int) BlackCobra,
\r
1630 PM_BV = (int) BlackFalcon,
\r
1631 PM_BSG = (int) BlackSilver,
\r
1632 PM_BK = (int) BlackKing
\r
1635 static HFONT hPieceFont = NULL;
\r
1636 static HBITMAP hPieceMask[(int) EmptySquare];
\r
1637 static HBITMAP hPieceFace[(int) EmptySquare];
\r
1638 static int fontBitmapSquareSize = 0;
\r
1639 static char pieceToFontChar[(int) EmptySquare] =
\r
1640 { 'p', 'n', 'b', 'r', 'q',
\r
1641 'n', 'b', 'p', 'n', 'b', 'r', 'b', 'r', 'q', 'k',
\r
1642 'k', 'o', 'm', 'v', 't', 'w',
\r
1643 'v', 't', 'o', 'm', 'v', 't', 'v', 't', 'w', 'l',
\r
1646 extern BOOL SetCharTable( char *table, const char * map );
\r
1647 /* [HGM] moved to backend.c */
\r
1649 static void SetPieceBackground( HDC hdc, COLORREF color, int mode )
\r
1652 BYTE r1 = GetRValue( color );
\r
1653 BYTE g1 = GetGValue( color );
\r
1654 BYTE b1 = GetBValue( color );
\r
1660 /* Create a uniform background first */
\r
1661 hbrush = CreateSolidBrush( color );
\r
1662 SetRect( &rc, 0, 0, squareSize, squareSize );
\r
1663 FillRect( hdc, &rc, hbrush );
\r
1664 DeleteObject( hbrush );
\r
1667 /* Vertical gradient, good for pawn, knight and rook, less for queen and king */
\r
1668 int steps = squareSize / 2;
\r
1671 for( i=0; i<steps; i++ ) {
\r
1672 BYTE r = r1 - (r1-r2) * i / steps;
\r
1673 BYTE g = g1 - (g1-g2) * i / steps;
\r
1674 BYTE b = b1 - (b1-b2) * i / steps;
\r
1676 hbrush = CreateSolidBrush( RGB(r,g,b) );
\r
1677 SetRect( &rc, i + squareSize - steps, 0, i + squareSize - steps + 1, squareSize );
\r
1678 FillRect( hdc, &rc, hbrush );
\r
1679 DeleteObject(hbrush);
\r
1682 else if( mode == 2 ) {
\r
1683 /* Diagonal gradient, good more or less for every piece */
\r
1684 POINT triangle[3];
\r
1685 HPEN hpen = SelectObject( hdc, GetStockObject(NULL_PEN) );
\r
1686 HBRUSH hbrush_old;
\r
1687 int steps = squareSize;
\r
1690 triangle[0].x = squareSize - steps;
\r
1691 triangle[0].y = squareSize;
\r
1692 triangle[1].x = squareSize;
\r
1693 triangle[1].y = squareSize;
\r
1694 triangle[2].x = squareSize;
\r
1695 triangle[2].y = squareSize - steps;
\r
1697 for( i=0; i<steps; i++ ) {
\r
1698 BYTE r = r1 - (r1-r2) * i / steps;
\r
1699 BYTE g = g1 - (g1-g2) * i / steps;
\r
1700 BYTE b = b1 - (b1-b2) * i / steps;
\r
1702 hbrush = CreateSolidBrush( RGB(r,g,b) );
\r
1703 hbrush_old = SelectObject( hdc, hbrush );
\r
1704 Polygon( hdc, triangle, 3 );
\r
1705 SelectObject( hdc, hbrush_old );
\r
1706 DeleteObject(hbrush);
\r
1711 SelectObject( hdc, hpen );
\r
1716 [AS] The method I use to create the bitmaps it a bit tricky, but it
\r
1717 seems to work ok. The main problem here is to find the "inside" of a chess
\r
1718 piece: follow the steps as explained below.
\r
1720 static void CreatePieceMaskFromFont( HDC hdc_window, HDC hdc, int index )
\r
1724 COLORREF chroma = RGB(0xFF,0x00,0xFF);
\r
1728 int backColor = whitePieceColor;
\r
1729 int foreColor = blackPieceColor;
\r
1731 if( index < (int)BlackPawn && appData.fontBackColorWhite != appData.fontForeColorWhite ) {
\r
1732 backColor = appData.fontBackColorWhite;
\r
1733 foreColor = appData.fontForeColorWhite;
\r
1735 else if( index >= (int)BlackPawn && appData.fontBackColorBlack != appData.fontForeColorBlack ) {
\r
1736 backColor = appData.fontBackColorBlack;
\r
1737 foreColor = appData.fontForeColorBlack;
\r
1741 hbm = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );
\r
1743 hbm_old = SelectObject( hdc, hbm );
\r
1747 rc.right = squareSize;
\r
1748 rc.bottom = squareSize;
\r
1750 /* Step 1: background is now black */
\r
1751 FillRect( hdc, &rc, GetStockObject(BLACK_BRUSH) );
\r
1753 GetTextExtentPoint32( hdc, &pieceToFontChar[index], 1, &sz );
\r
1755 pt.x = (squareSize - sz.cx) / 2;
\r
1756 pt.y = (squareSize - sz.cy) / 2;
\r
1758 SetBkMode( hdc, TRANSPARENT );
\r
1759 SetTextColor( hdc, chroma );
\r
1760 /* Step 2: the piece has been drawn in purple, there are now black and purple in this bitmap */
\r
1761 TextOut( hdc, pt.x, pt.y, &pieceToFontChar[appData.allWhite && index >= (int)BlackPawn ? index - (int)BlackPawn : index], 1 );
\r
1763 SelectObject( hdc, GetStockObject(WHITE_BRUSH) );
\r
1764 /* Step 3: the area outside the piece is filled with white */
\r
1765 // FloodFill( hdc, 0, 0, chroma );
\r
1766 ExtFloodFill( hdc, 0, 0, 0, FLOODFILLSURFACE );
\r
1767 ExtFloodFill( hdc, 0, squareSize-1, 0, FLOODFILLSURFACE ); // [HGM] fill from all 4 corners, for if piece too big
\r
1768 ExtFloodFill( hdc, squareSize-1, 0, 0, FLOODFILLSURFACE );
\r
1769 ExtFloodFill( hdc, squareSize-1, squareSize-1, 0, FLOODFILLSURFACE );
\r
1770 SelectObject( hdc, GetStockObject(BLACK_BRUSH) );
\r
1772 Step 4: this is the tricky part, the area inside the piece is filled with black,
\r
1773 but if the start point is not inside the piece we're lost!
\r
1774 There should be a better way to do this... if we could create a region or path
\r
1775 from the fill operation we would be fine for example.
\r
1777 // FloodFill( hdc, squareSize / 2, squareSize / 2, RGB(0xFF,0xFF,0xFF) );
\r
1778 ExtFloodFill( hdc, squareSize / 2, squareSize / 2, RGB(0xFF,0xFF,0xFF), FLOODFILLBORDER );
\r
1780 { /* [HGM] shave off edges of mask, in an attempt to correct for the fact that FloodFill does not work correctly under Win XP */
\r
1781 HDC dc2 = CreateCompatibleDC( hdc_window );
\r
1782 HBITMAP bm2 = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );
\r
1784 SelectObject( dc2, bm2 );
\r
1785 BitBlt( dc2, 0, 0, squareSize, squareSize, hdc, 0, 0, SRCCOPY ); // make copy
\r
1786 BitBlt( hdc, 0, 1, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );
\r
1787 BitBlt( hdc, 2, 1, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );
\r
1788 BitBlt( hdc, 1, 0, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );
\r
1789 BitBlt( hdc, 1, 2, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );
\r
1792 DeleteObject( bm2 );
\r
1795 SetTextColor( hdc, 0 );
\r
1797 Step 5: some fonts have "disconnected" areas that are skipped by the fill:
\r
1798 draw the piece again in black for safety.
\r
1800 TextOut( hdc, pt.x, pt.y, &pieceToFontChar[appData.allWhite && index >= (int)BlackPawn ? index - (int)BlackPawn : index], 1 );
\r
1802 SelectObject( hdc, hbm_old );
\r
1804 if( hPieceMask[index] != NULL ) {
\r
1805 DeleteObject( hPieceMask[index] );
\r
1808 hPieceMask[index] = hbm;
\r
1811 hbm = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );
\r
1813 SelectObject( hdc, hbm );
\r
1816 HDC dc1 = CreateCompatibleDC( hdc_window );
\r
1817 HDC dc2 = CreateCompatibleDC( hdc_window );
\r
1818 HBITMAP bm2 = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );
\r
1820 SelectObject( dc1, hPieceMask[index] );
\r
1821 SelectObject( dc2, bm2 );
\r
1822 FillRect( dc2, &rc, GetStockObject(WHITE_BRUSH) );
\r
1823 BitBlt( dc2, 0, 0, squareSize, squareSize, dc1, 0, 0, SRCINVERT );
\r
1826 Now dc2 contains the inverse of the piece mask, i.e. a mask that preserves
\r
1827 the piece background and deletes (makes transparent) the rest.
\r
1828 Thanks to that mask, we are free to paint the background with the greates
\r
1829 freedom, as we'll be able to mask off the unwanted parts when finished.
\r
1830 We use this, to make gradients and give the pieces a "roundish" look.
\r
1832 SetPieceBackground( hdc, backColor, 2 );
\r
1833 BitBlt( hdc, 0, 0, squareSize, squareSize, dc2, 0, 0, SRCAND );
\r
1837 DeleteObject( bm2 );
\r
1840 SetTextColor( hdc, foreColor );
\r
1841 TextOut( hdc, pt.x, pt.y, &pieceToFontChar[appData.allWhite && index >= (int)BlackPawn ? index - (int)BlackPawn : index], 1 );
\r
1843 SelectObject( hdc, hbm_old );
\r
1845 if( hPieceFace[index] != NULL ) {
\r
1846 DeleteObject( hPieceFace[index] );
\r
1849 hPieceFace[index] = hbm;
\r
1852 static int TranslatePieceToFontPiece( int piece )
\r
1882 case BlackMarshall:
\r
1886 case BlackNightrider:
\r
1892 case BlackUnicorn:
\r
1896 case BlackGrasshopper:
\r
1908 case BlackCardinal:
\r
1915 case WhiteMarshall:
\r
1919 case WhiteNightrider:
\r
1925 case WhiteUnicorn:
\r
1929 case WhiteGrasshopper:
\r
1941 case WhiteCardinal:
\r
1950 void CreatePiecesFromFont()
\r
1953 HDC hdc_window = NULL;
\r
1959 if( fontBitmapSquareSize < 0 ) {
\r
1960 /* Something went seriously wrong in the past: do not try to recreate fonts! */
\r
1964 if( appData.renderPiecesWithFont == NULL || appData.renderPiecesWithFont[0] == NULLCHAR || appData.renderPiecesWithFont[0] == '*' ) {
\r
1965 fontBitmapSquareSize = -1;
\r
1969 if( fontBitmapSquareSize != squareSize ) {
\r
1970 hdc_window = GetDC( hwndMain );
\r
1971 hdc = CreateCompatibleDC( hdc_window );
\r
1973 if( hPieceFont != NULL ) {
\r
1974 DeleteObject( hPieceFont );
\r
1977 for( i=0; i<=(int)BlackKing; i++ ) {
\r
1978 hPieceMask[i] = NULL;
\r
1979 hPieceFace[i] = NULL;
\r
1985 if( appData.fontPieceSize >= 50 && appData.fontPieceSize <= 150 ) {
\r
1986 fontHeight = appData.fontPieceSize;
\r
1989 fontHeight = (fontHeight * squareSize) / 100;
\r
1991 lf.lfHeight = -MulDiv( fontHeight, GetDeviceCaps(hdc, LOGPIXELSY), 72 );
\r
1993 lf.lfEscapement = 0;
\r
1994 lf.lfOrientation = 0;
\r
1995 lf.lfWeight = FW_NORMAL;
\r
1997 lf.lfUnderline = 0;
\r
1998 lf.lfStrikeOut = 0;
\r
1999 lf.lfCharSet = DEFAULT_CHARSET;
\r
2000 lf.lfOutPrecision = OUT_DEFAULT_PRECIS;
\r
2001 lf.lfClipPrecision = CLIP_DEFAULT_PRECIS;
\r
2002 lf.lfQuality = PROOF_QUALITY;
\r
2003 lf.lfPitchAndFamily = DEFAULT_PITCH | FF_DONTCARE;
\r
2004 strncpy( lf.lfFaceName, appData.renderPiecesWithFont, sizeof(lf.lfFaceName) );
\r
2005 lf.lfFaceName[ sizeof(lf.lfFaceName) - 1 ] = '\0';
\r
2007 hPieceFont = CreateFontIndirect( &lf );
\r
2009 if( hPieceFont == NULL ) {
\r
2010 fontBitmapSquareSize = -2;
\r
2013 /* Setup font-to-piece character table */
\r
2014 if( ! SetCharTable(pieceToFontChar, appData.fontToPieceTable) ) {
\r
2015 /* No (or wrong) global settings, try to detect the font */
\r
2016 if( strstr(lf.lfFaceName,"Alpha") != NULL ) {
\r
2018 SetCharTable(pieceToFontChar, "phbrqkojntwl");
\r
2020 else if( strstr(lf.lfFaceName,"DiagramTT") != NULL ) {
\r
2021 /* DiagramTT* family */
\r
2022 SetCharTable(pieceToFontChar, "PNLRQKpnlrqk");
\r
2024 else if( strstr(lf.lfFaceName,"WinboardF") != NULL ) {
\r
2025 /* Fairy symbols */
\r
2026 SetCharTable(pieceToFontChar, "PNBRQFEACWMOHIJGDVSLUKpnbrqfeacwmohijgdvsluk");
\r
2028 else if( strstr(lf.lfFaceName,"GC2004D") != NULL ) {
\r
2029 /* Good Companion (Some characters get warped as literal :-( */
\r
2030 char s[] = "1cmWG0??S??oYI23wgQU";
\r
2031 s[0]=0xB9; s[1]=0xA9; s[6]=0xB1; s[11]=0xBB; s[12]=0xAB; s[17]=0xB3;
\r
2032 SetCharTable(pieceToFontChar, s);
\r
2035 /* Cases, Condal, Leipzig, Lucena, Marroquin, Merida, Usual */
\r
2036 SetCharTable(pieceToFontChar, "pnbrqkomvtwl");
\r
2040 /* Create bitmaps */
\r
2041 hfont_old = SelectObject( hdc, hPieceFont );
\r
2042 for(i=(int)WhitePawn; i<(int)EmptySquare; i++) /* [HGM] made a loop for this */
\r
2043 if(PieceToChar((ChessSquare)i) != '.') /* skip unused pieces */
\r
2044 CreatePieceMaskFromFont( hdc_window, hdc, i );
\r
2046 SelectObject( hdc, hfont_old );
\r
2048 fontBitmapSquareSize = squareSize;
\r
2052 if( hdc != NULL ) {
\r
2056 if( hdc_window != NULL ) {
\r
2057 ReleaseDC( hwndMain, hdc_window );
\r
2062 DoLoadBitmap(HINSTANCE hinst, char *piece, int squareSize, char *suffix)
\r
2066 snprintf(name, sizeof(name)/sizeof(name[0]), "%s%d%s", piece, squareSize, suffix);
\r
2067 if (gameInfo.event &&
\r
2068 strcmp(gameInfo.event, "Easter Egg Hunt") == 0 &&
\r
2069 strcmp(name, "k80s") == 0) {
\r
2070 safeStrCpy(name, "tim", sizeof(name)/sizeof(name[0]) );
\r
2072 return LoadBitmap(hinst, name);
\r
2076 /* Insert a color into the program's logical palette
\r
2077 structure. This code assumes the given color is
\r
2078 the result of the RGB or PALETTERGB macro, and it
\r
2079 knows how those macros work (which is documented).
\r
2082 InsertInPalette(COLORREF color)
\r
2084 LPPALETTEENTRY pe = &(pLogPal->palPalEntry[pLogPal->palNumEntries]);
\r
2086 if (pLogPal->palNumEntries++ >= PALETTESIZE) {
\r
2087 DisplayFatalError(_("Too many colors"), 0, 1);
\r
2088 pLogPal->palNumEntries--;
\r
2092 pe->peFlags = (char) 0;
\r
2093 pe->peRed = (char) (0xFF & color);
\r
2094 pe->peGreen = (char) (0xFF & (color >> 8));
\r
2095 pe->peBlue = (char) (0xFF & (color >> 16));
\r
2101 InitDrawingColors()
\r
2103 if (pLogPal == NULL) {
\r
2104 /* Allocate enough memory for a logical palette with
\r
2105 * PALETTESIZE entries and set the size and version fields
\r
2106 * of the logical palette structure.
\r
2108 pLogPal = (NPLOGPALETTE)
\r
2109 LocalAlloc(LMEM_FIXED, (sizeof(LOGPALETTE) +
\r
2110 (sizeof(PALETTEENTRY) * (PALETTESIZE))));
\r
2111 pLogPal->palVersion = 0x300;
\r
2113 pLogPal->palNumEntries = 0;
\r
2115 InsertInPalette(lightSquareColor);
\r
2116 InsertInPalette(darkSquareColor);
\r
2117 InsertInPalette(whitePieceColor);
\r
2118 InsertInPalette(blackPieceColor);
\r
2119 InsertInPalette(highlightSquareColor);
\r
2120 InsertInPalette(premoveHighlightColor);
\r
2122 /* create a logical color palette according the information
\r
2123 * in the LOGPALETTE structure.
\r
2125 hPal = CreatePalette((LPLOGPALETTE) pLogPal);
\r
2127 lightSquareBrush = CreateSolidBrush(lightSquareColor);
\r
2128 blackSquareBrush = CreateSolidBrush(blackPieceColor);
\r
2129 darkSquareBrush = CreateSolidBrush(darkSquareColor);
\r
2130 whitePieceBrush = CreateSolidBrush(whitePieceColor);
\r
2131 blackPieceBrush = CreateSolidBrush(blackPieceColor);
\r
2132 iconBkgndBrush = CreateSolidBrush(GetSysColor(COLOR_BACKGROUND));
\r
2133 explodeBrush = CreateSolidBrush(highlightSquareColor); // [HGM] atomic
\r
2134 markerBrush = CreateSolidBrush(premoveHighlightColor); // [HGM] markers
\r
2135 /* [AS] Force rendering of the font-based pieces */
\r
2136 if( fontBitmapSquareSize > 0 ) {
\r
2137 fontBitmapSquareSize = 0;
\r
2143 BoardWidth(int boardSize, int n)
\r
2144 { /* [HGM] argument n added to allow different width and height */
\r
2145 int lineGap = sizeInfo[boardSize].lineGap;
\r
2147 if( appData.overrideLineGap >= 0 && appData.overrideLineGap <= 5 ) {
\r
2148 lineGap = appData.overrideLineGap;
\r
2151 return (n + 1) * lineGap +
\r
2152 n * sizeInfo[boardSize].squareSize;
\r
2155 /* Respond to board resize by dragging edge */
\r
2157 ResizeBoard(int newSizeX, int newSizeY, int flags)
\r
2159 BoardSize newSize = NUM_SIZES - 1;
\r
2160 static int recurse = 0;
\r
2161 if (IsIconic(hwndMain)) return;
\r
2162 if (recurse > 0) return;
\r
2164 while (newSize > 0) {
\r
2165 InitDrawingSizes(newSize+1000, 0); // [HGM] kludge to update sizeInfo without visible effects
\r
2166 if(newSizeX >= sizeInfo[newSize].cliWidth &&
\r
2167 newSizeY >= sizeInfo[newSize].cliHeight) break;
\r
2170 boardSize = newSize;
\r
2171 InitDrawingSizes(boardSize, flags);
\r
2176 extern Boolean twoBoards, partnerUp; // [HGM] dual
\r
2179 InitDrawingSizes(BoardSize boardSize, int flags)
\r
2181 int i, boardWidth, boardHeight; /* [HGM] height treated separately */
\r
2182 ChessSquare piece;
\r
2183 static int oldBoardSize = -1, oldTinyLayout = 0;
\r
2185 SIZE clockSize, messageSize;
\r
2187 char buf[MSG_SIZ];
\r
2189 HMENU hmenu = GetMenu(hwndMain);
\r
2190 RECT crect, wrect, oldRect;
\r
2192 LOGBRUSH logbrush;
\r
2194 int suppressVisibleEffects = 0; // [HGM] kludge to request updating sizeInfo only
\r
2195 if((int)boardSize >= 1000 ) { boardSize -= 1000; suppressVisibleEffects = 1; }
\r
2197 /* [HGM] call with -2 uses old size (for if nr of files, ranks changes) */
\r
2198 if(boardSize == (BoardSize)(-2) ) boardSize = oldBoardSize;
\r
2200 oldRect.left = wpMain.x; //[HGM] placement: remember previous window params
\r
2201 oldRect.top = wpMain.y;
\r
2202 oldRect.right = wpMain.x + wpMain.width;
\r
2203 oldRect.bottom = wpMain.y + wpMain.height;
\r
2205 tinyLayout = sizeInfo[boardSize].tinyLayout;
\r
2206 smallLayout = sizeInfo[boardSize].smallLayout;
\r
2207 squareSize = sizeInfo[boardSize].squareSize;
\r
2208 lineGap = sizeInfo[boardSize].lineGap;
\r
2209 minorSize = 0; /* [HGM] Kludge to see if demagnified pieces need to be shifted */
\r
2211 if( appData.overrideLineGap >= 0 && appData.overrideLineGap <= 5 ) {
\r
2212 lineGap = appData.overrideLineGap;
\r
2215 if (tinyLayout != oldTinyLayout) {
\r
2216 long style = GetWindowLong(hwndMain, GWL_STYLE);
\r
2218 style &= ~WS_SYSMENU;
\r
2219 InsertMenu(hmenu, IDM_Exit, MF_BYCOMMAND, IDM_Minimize,
\r
2220 "&Minimize\tCtrl+F4");
\r
2222 style |= WS_SYSMENU;
\r
2223 RemoveMenu(hmenu, IDM_Minimize, MF_BYCOMMAND);
\r
2225 SetWindowLong(hwndMain, GWL_STYLE, style);
\r
2227 for (i=0; menuBarText[tinyLayout][i]; i++) {
\r
2228 ModifyMenu(hmenu, i, MF_STRING|MF_BYPOSITION|MF_POPUP,
\r
2229 (UINT)GetSubMenu(hmenu, i), T_(menuBarText[tinyLayout][i]));
\r
2231 DrawMenuBar(hwndMain);
\r
2234 boardWidth = BoardWidth(boardSize, BOARD_WIDTH);
\r
2235 boardHeight = BoardWidth(boardSize, BOARD_HEIGHT);
\r
2237 /* Get text area sizes */
\r
2238 hdc = GetDC(hwndMain);
\r
2239 if (appData.clockMode) {
\r
2240 snprintf(buf, MSG_SIZ, _("White: %s"), TimeString(23*60*60*1000L));
\r
2242 snprintf(buf, MSG_SIZ, _("White"));
\r
2244 oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);
\r
2245 GetTextExtentPoint(hdc, buf, strlen(buf), &clockSize);
\r
2246 SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);
\r
2247 str = _("We only care about the height here");
\r
2248 GetTextExtentPoint(hdc, str, strlen(str), &messageSize);
\r
2249 SelectObject(hdc, oldFont);
\r
2250 ReleaseDC(hwndMain, hdc);
\r
2252 /* Compute where everything goes */
\r
2253 if((first.programLogo || second.programLogo) && !tinyLayout) {
\r
2254 /* [HGM] logo: if either logo is on, reserve space for it */
\r
2255 logoHeight = 2*clockSize.cy;
\r
2256 leftLogoRect.left = OUTER_MARGIN;
\r
2257 leftLogoRect.right = leftLogoRect.left + 4*clockSize.cy;
\r
2258 leftLogoRect.top = OUTER_MARGIN;
\r
2259 leftLogoRect.bottom = OUTER_MARGIN + logoHeight;
\r
2261 rightLogoRect.right = OUTER_MARGIN + boardWidth;
\r
2262 rightLogoRect.left = rightLogoRect.right - 4*clockSize.cy;
\r
2263 rightLogoRect.top = OUTER_MARGIN;
\r
2264 rightLogoRect.bottom = OUTER_MARGIN + logoHeight;
\r
2267 whiteRect.left = leftLogoRect.right;
\r
2268 whiteRect.right = OUTER_MARGIN + boardWidth/2 - INNER_MARGIN/2;
\r
2269 whiteRect.top = OUTER_MARGIN;
\r
2270 whiteRect.bottom = whiteRect.top + logoHeight;
\r
2272 blackRect.right = rightLogoRect.left;
\r
2273 blackRect.left = whiteRect.right + INNER_MARGIN;
\r
2274 blackRect.top = whiteRect.top;
\r
2275 blackRect.bottom = whiteRect.bottom;
\r
2277 whiteRect.left = OUTER_MARGIN;
\r
2278 whiteRect.right = whiteRect.left + boardWidth/2 - INNER_MARGIN/2;
\r
2279 whiteRect.top = OUTER_MARGIN;
\r
2280 whiteRect.bottom = whiteRect.top + clockSize.cy;
\r
2282 blackRect.left = whiteRect.right + INNER_MARGIN;
\r
2283 blackRect.right = blackRect.left + boardWidth/2 - 1;
\r
2284 blackRect.top = whiteRect.top;
\r
2285 blackRect.bottom = whiteRect.bottom;
\r
2287 logoHeight = 0; // [HGM] logo: suppress logo after change to tiny layout!
\r
2290 messageRect.left = OUTER_MARGIN + MESSAGE_LINE_LEFTMARGIN;
\r
2291 if (appData.showButtonBar) {
\r
2292 messageRect.right = OUTER_MARGIN + boardWidth // [HGM] logo: expressed independent of clock placement
\r
2293 - N_BUTTONS*BUTTON_WIDTH - MESSAGE_LINE_LEFTMARGIN;
\r
2295 messageRect.right = OUTER_MARGIN + boardWidth;
\r
2297 messageRect.top = whiteRect.bottom + INNER_MARGIN;
\r
2298 messageRect.bottom = messageRect.top + messageSize.cy;
\r
2300 boardRect.left = OUTER_MARGIN;
\r
2301 boardRect.right = boardRect.left + boardWidth;
\r
2302 boardRect.top = messageRect.bottom + INNER_MARGIN;
\r
2303 boardRect.bottom = boardRect.top + boardHeight;
\r
2305 sizeInfo[boardSize].cliWidth = boardRect.right + OUTER_MARGIN;
\r
2306 sizeInfo[boardSize].cliHeight = boardRect.bottom + OUTER_MARGIN;
\r
2307 oldBoardSize = boardSize;
\r
2308 oldTinyLayout = tinyLayout;
\r
2309 winW = 2 * GetSystemMetrics(SM_CXFRAME) + boardRect.right + OUTER_MARGIN;
\r
2310 winH = 2 * GetSystemMetrics(SM_CYFRAME) + GetSystemMetrics(SM_CYMENU) +
\r
2311 GetSystemMetrics(SM_CYCAPTION) + boardRect.bottom + OUTER_MARGIN;
\r
2312 winW *= 1 + twoBoards;
\r
2313 if(suppressVisibleEffects) return; // [HGM] when called for filling sizeInfo only
\r
2314 wpMain.width = winW; // [HGM] placement: set through temporary which can used by initial sizing choice
\r
2315 wpMain.height = winH; // without disturbing window attachments
\r
2316 GetWindowRect(hwndMain, &wrect);
\r
2317 SetWindowPos(hwndMain, NULL, 0, 0, wpMain.width, wpMain.height,
\r
2318 SWP_NOCOPYBITS|SWP_NOZORDER|SWP_NOMOVE);
\r
2320 // [HGM] placement: let attached windows follow size change.
\r
2321 ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, moveHistoryDialog, &wpMoveHistory );
\r
2322 ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, evalGraphDialog, &wpEvalGraph );
\r
2323 ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, engineOutputDialog, &wpEngineOutput );
\r
2324 ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, gameListDialog, &wpGameList );
\r
2325 ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, hwndConsole, &wpConsole );
\r
2327 /* compensate if menu bar wrapped */
\r
2328 GetClientRect(hwndMain, &crect);
\r
2329 offby = boardRect.bottom + OUTER_MARGIN - crect.bottom;
\r
2330 wpMain.height += offby;
\r
2332 case WMSZ_TOPLEFT:
\r
2333 SetWindowPos(hwndMain, NULL,
\r
2334 wrect.right - wpMain.width, wrect.bottom - wpMain.height,
\r
2335 wpMain.width, wpMain.height, SWP_NOCOPYBITS|SWP_NOZORDER);
\r
2338 case WMSZ_TOPRIGHT:
\r
2340 SetWindowPos(hwndMain, NULL,
\r
2341 wrect.left, wrect.bottom - wpMain.height,
\r
2342 wpMain.width, wpMain.height, SWP_NOCOPYBITS|SWP_NOZORDER);
\r
2345 case WMSZ_BOTTOMLEFT:
\r
2347 SetWindowPos(hwndMain, NULL,
\r
2348 wrect.right - wpMain.width, wrect.top,
\r
2349 wpMain.width, wpMain.height, SWP_NOCOPYBITS|SWP_NOZORDER);
\r
2352 case WMSZ_BOTTOMRIGHT:
\r
2356 SetWindowPos(hwndMain, NULL, 0, 0, wpMain.width, wpMain.height,
\r
2357 SWP_NOCOPYBITS|SWP_NOZORDER|SWP_NOMOVE);
\r
2362 for (i = 0; i < N_BUTTONS; i++) {
\r
2363 if (buttonDesc[i].hwnd != NULL) {
\r
2364 DestroyWindow(buttonDesc[i].hwnd);
\r
2365 buttonDesc[i].hwnd = NULL;
\r
2367 if (appData.showButtonBar) {
\r
2368 buttonDesc[i].hwnd =
\r
2369 CreateWindow("BUTTON", buttonDesc[i].label,
\r
2370 WS_VISIBLE | WS_CHILD | BS_PUSHBUTTON,
\r
2371 boardRect.right - BUTTON_WIDTH*(N_BUTTONS-i),
\r
2372 messageRect.top, BUTTON_WIDTH, messageSize.cy, hwndMain,
\r
2373 (HMENU) buttonDesc[i].id,
\r
2374 (HINSTANCE) GetWindowLong(hwndMain, GWL_HINSTANCE), NULL);
\r
2376 SendMessage(buttonDesc[i].hwnd, WM_SETFONT,
\r
2377 (WPARAM)font[boardSize][MESSAGE_FONT]->hf,
\r
2378 MAKELPARAM(FALSE, 0));
\r
2380 if (buttonDesc[i].id == IDM_Pause)
\r
2381 hwndPause = buttonDesc[i].hwnd;
\r
2382 buttonDesc[i].wndproc = (WNDPROC)
\r
2383 SetWindowLong(buttonDesc[i].hwnd, GWL_WNDPROC, (LONG) ButtonProc);
\r
2386 if (gridPen != NULL) DeleteObject(gridPen);
\r
2387 if (highlightPen != NULL) DeleteObject(highlightPen);
\r
2388 if (premovePen != NULL) DeleteObject(premovePen);
\r
2389 if (lineGap != 0) {
\r
2390 logbrush.lbStyle = BS_SOLID;
\r
2391 logbrush.lbColor = RGB(0, 0, 0); /* grid pen color = black */
\r
2393 ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,
\r
2394 lineGap, &logbrush, 0, NULL);
\r
2395 logbrush.lbColor = highlightSquareColor;
\r
2397 ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,
\r
2398 lineGap, &logbrush, 0, NULL);
\r
2400 logbrush.lbColor = premoveHighlightColor;
\r
2402 ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,
\r
2403 lineGap, &logbrush, 0, NULL);
\r
2405 /* [HGM] Loop had to be split in part for vert. and hor. lines */
\r
2406 for (i = 0; i < BOARD_HEIGHT + 1; i++) {
\r
2407 gridEndpoints[i*2].x = boardRect.left + lineGap / 2;
\r
2408 gridEndpoints[i*2].y = gridEndpoints[i*2 + 1].y =
\r
2409 boardRect.top + lineGap / 2 + (i * (squareSize + lineGap));
\r
2410 gridEndpoints[i*2 + 1].x = boardRect.left + lineGap / 2 +
\r
2411 BOARD_WIDTH * (squareSize + lineGap);
\r
2412 gridVertexCounts[i*2] = gridVertexCounts[i*2 + 1] = 2;
\r
2414 for (i = 0; i < BOARD_WIDTH + 1; i++) {
\r
2415 gridEndpoints[i*2 + BOARD_HEIGHT*2 + 2].y = boardRect.top + lineGap / 2;
\r
2416 gridEndpoints[i*2 + BOARD_HEIGHT*2 + 2].x =
\r
2417 gridEndpoints[i*2 + 1 + BOARD_HEIGHT*2 + 2].x = boardRect.left +
\r
2418 lineGap / 2 + (i * (squareSize + lineGap));
\r
2419 gridEndpoints[i*2 + 1 + BOARD_HEIGHT*2 + 2].y =
\r
2420 boardRect.top + BOARD_HEIGHT * (squareSize + lineGap);
\r
2421 gridVertexCounts[i*2] = gridVertexCounts[i*2 + 1] = 2;
\r
2425 /* [HGM] Licensing requirement */
\r
2427 if(gameInfo.variant == VariantGothic) GothicPopUp( GOTHIC, VariantGothic); else
\r
2430 if(gameInfo.variant == VariantFalcon) GothicPopUp( FALCON, VariantFalcon); else
\r
2432 GothicPopUp( "", VariantNormal);
\r
2435 /* if (boardSize == oldBoardSize) return; [HGM] variant might have changed */
\r
2437 /* Load piece bitmaps for this board size */
\r
2438 for (i=0; i<=2; i++) {
\r
2439 for (piece = WhitePawn;
\r
2440 (int) piece < (int) BlackPawn;
\r
2441 piece = (ChessSquare) ((int) piece + 1)) {
\r
2442 if (pieceBitmap[i][piece] != NULL)
\r
2443 DeleteObject(pieceBitmap[i][piece]);
\r
2447 fontBitmapSquareSize = 0; /* [HGM] render: make sure pieces will be recreated, as we might need others now */
\r
2448 // Orthodox Chess pieces
\r
2449 pieceBitmap[0][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "s");
\r
2450 pieceBitmap[0][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "s");
\r
2451 pieceBitmap[0][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "s");
\r
2452 pieceBitmap[0][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "s");
\r
2453 pieceBitmap[0][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "s");
\r
2454 pieceBitmap[1][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "o");
\r
2455 pieceBitmap[1][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "o");
\r
2456 pieceBitmap[1][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "o");
\r
2457 pieceBitmap[1][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "o");
\r
2458 pieceBitmap[1][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "o");
\r
2459 pieceBitmap[2][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "w");
\r
2460 pieceBitmap[2][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "w");
\r
2461 pieceBitmap[2][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "w");
\r
2462 pieceBitmap[2][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "w");
\r
2463 pieceBitmap[2][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "w");
\r
2464 if( gameInfo.variant == VariantShogi && squareSize <= 72 && squareSize >= 33) {
\r
2465 // in Shogi, Hijack the unused Queen for Lance
\r
2466 pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "s");
\r
2467 pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "o");
\r
2468 pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "w");
\r
2470 pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "s");
\r
2471 pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "o");
\r
2472 pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "w");
\r
2475 if(squareSize <= 72 && squareSize >= 33) {
\r
2476 /* A & C are available in most sizes now */
\r
2477 if(squareSize != 49 && squareSize != 72 && squareSize != 33) { // Vortex-like
\r
2478 pieceBitmap[0][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "s");
\r
2479 pieceBitmap[1][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "o");
\r
2480 pieceBitmap[2][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "w");
\r
2481 pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "s");
\r
2482 pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "o");
\r
2483 pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "w");
\r
2484 pieceBitmap[0][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "s");
\r
2485 pieceBitmap[1][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "o");
\r
2486 pieceBitmap[2][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "w");
\r
2487 pieceBitmap[0][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "s");
\r
2488 pieceBitmap[1][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "o");
\r
2489 pieceBitmap[2][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "w");
\r
2490 } else { // Smirf-like
\r
2491 if(gameInfo.variant == VariantSChess) {
\r
2492 pieceBitmap[0][WhiteAngel] = DoLoadBitmap(hInst, "v", squareSize, "s");
\r
2493 pieceBitmap[1][WhiteAngel] = DoLoadBitmap(hInst, "v", squareSize, "o");
\r
2494 pieceBitmap[2][WhiteAngel] = DoLoadBitmap(hInst, "v", squareSize, "w");
\r
2496 pieceBitmap[0][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "s");
\r
2497 pieceBitmap[1][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "o");
\r
2498 pieceBitmap[2][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "w");
\r
2501 if(gameInfo.variant == VariantGothic) { // Vortex-like
\r
2502 pieceBitmap[0][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "s");
\r
2503 pieceBitmap[1][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "o");
\r
2504 pieceBitmap[2][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "w");
\r
2505 } else if(gameInfo.variant == VariantSChess && (squareSize == 49 || squareSize == 72)) {
\r
2506 pieceBitmap[0][WhiteMarshall] = DoLoadBitmap(hInst, "e", squareSize, "s");
\r
2507 pieceBitmap[1][WhiteMarshall] = DoLoadBitmap(hInst, "e", squareSize, "o");
\r
2508 pieceBitmap[2][WhiteMarshall] = DoLoadBitmap(hInst, "e", squareSize, "w");
\r
2509 } else { // WinBoard standard
\r
2510 pieceBitmap[0][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "s");
\r
2511 pieceBitmap[1][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "o");
\r
2512 pieceBitmap[2][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "w");
\r
2517 if(squareSize==72 || squareSize==49 || squareSize==33) { /* experiment with some home-made bitmaps */
\r
2518 pieceBitmap[0][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "s");
\r
2519 pieceBitmap[1][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "o");
\r
2520 pieceBitmap[2][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "w");
\r
2521 pieceBitmap[0][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "s");
\r
2522 pieceBitmap[1][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "o");
\r
2523 pieceBitmap[2][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "w");
\r
2524 pieceBitmap[0][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "s");
\r
2525 pieceBitmap[1][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "o");
\r
2526 pieceBitmap[2][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "w");
\r
2527 pieceBitmap[0][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "s");
\r
2528 pieceBitmap[1][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "o");
\r
2529 pieceBitmap[2][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "w");
\r
2530 pieceBitmap[0][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "s");
\r
2531 pieceBitmap[1][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "o");
\r
2532 pieceBitmap[2][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "w");
\r
2533 pieceBitmap[0][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "s");
\r
2534 pieceBitmap[1][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "o");
\r
2535 pieceBitmap[2][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "w");
\r
2536 pieceBitmap[0][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "s");
\r
2537 pieceBitmap[1][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "o");
\r
2538 pieceBitmap[2][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "w");
\r
2539 pieceBitmap[0][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "s");
\r
2540 pieceBitmap[1][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "o");
\r
2541 pieceBitmap[2][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "w");
\r
2542 pieceBitmap[0][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "s");
\r
2543 pieceBitmap[1][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "o");
\r
2544 pieceBitmap[2][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "w");
\r
2545 pieceBitmap[0][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "s");
\r
2546 pieceBitmap[1][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "o");
\r
2547 pieceBitmap[2][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "w");
\r
2549 if(gameInfo.variant == VariantShogi) { /* promoted Gold represemtations */
\r
2550 pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "wp", squareSize, "s");
\r
2551 pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "wp", squareSize, "o");
\r
2552 pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "w", squareSize, "w");
\r
2553 pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "wn", squareSize, "s");
\r
2554 pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "wn", squareSize, "o");
\r
2555 pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "w", squareSize, "w");
\r
2556 pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "ws", squareSize, "s");
\r
2557 pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "ws", squareSize, "o");
\r
2558 pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "w", squareSize, "w");
\r
2559 pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "wl", squareSize, "s");
\r
2560 pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "wl", squareSize, "o");
\r
2561 pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "w", squareSize, "w");
\r
2563 pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "s");
\r
2564 pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "o");
\r
2565 pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "w");
\r
2566 pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "s");
\r
2567 pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "o");
\r
2568 pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "w");
\r
2569 pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "s");
\r
2570 pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "o");
\r
2571 pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "w");
\r
2572 pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "s");
\r
2573 pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "o");
\r
2574 pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "w");
\r
2577 } else { /* other size, no special bitmaps available. Use smaller symbols */
\r
2578 if((int)boardSize < 2) minorSize = sizeInfo[0].squareSize;
\r
2579 else minorSize = sizeInfo[(int)boardSize - 2].squareSize;
\r
2580 pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "s");
\r
2581 pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "o");
\r
2582 pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "w");
\r
2583 pieceBitmap[0][WhiteCardinal] = DoLoadBitmap(hInst, "b", minorSize, "s");
\r
2584 pieceBitmap[1][WhiteCardinal] = DoLoadBitmap(hInst, "b", minorSize, "o");
\r
2585 pieceBitmap[2][WhiteCardinal] = DoLoadBitmap(hInst, "b", minorSize, "w");
\r
2586 pieceBitmap[0][WhiteDragon] = DoLoadBitmap(hInst, "r", minorSize, "s");
\r
2587 pieceBitmap[1][WhiteDragon] = DoLoadBitmap(hInst, "r", minorSize, "o");
\r
2588 pieceBitmap[2][WhiteDragon] = DoLoadBitmap(hInst, "r", minorSize, "w");
\r
2589 pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "s");
\r
2590 pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "o");
\r
2591 pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "w");
\r
2595 if(gameInfo.variant == VariantShogi && squareSize == 58)
\r
2596 /* special Shogi support in this size */
\r
2597 { for (i=0; i<=2; i++) { /* replace all bitmaps */
\r
2598 for (piece = WhitePawn;
\r
2599 (int) piece < (int) BlackPawn;
\r
2600 piece = (ChessSquare) ((int) piece + 1)) {
\r
2601 if (pieceBitmap[i][piece] != NULL)
\r
2602 DeleteObject(pieceBitmap[i][piece]);
\r
2605 pieceBitmap[0][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "o");
\r
2606 pieceBitmap[0][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "o");
\r
2607 pieceBitmap[0][WhiteBishop] = DoLoadBitmap(hInst, "sb", squareSize, "o");
\r
2608 pieceBitmap[0][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "o");
\r
2609 pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "o");
\r
2610 pieceBitmap[0][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "o");
\r
2611 pieceBitmap[0][WhiteFerz] = DoLoadBitmap(hInst, "sf", squareSize, "o");
\r
2612 pieceBitmap[0][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "o");
\r
2613 pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "su", squareSize, "o");
\r
2614 pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "sh", squareSize, "o");
\r
2615 pieceBitmap[0][WhiteCardinal] = DoLoadBitmap(hInst, "sa", squareSize, "o");
\r
2616 pieceBitmap[0][WhiteDragon] = DoLoadBitmap(hInst, "sc", squareSize, "o");
\r
2617 pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "sg", squareSize, "o");
\r
2618 pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "ss", squareSize, "o");
\r
2619 pieceBitmap[1][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "o");
\r
2620 pieceBitmap[1][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "o");
\r
2621 pieceBitmap[1][WhiteBishop] = DoLoadBitmap(hInst, "sb", squareSize, "o");
\r
2622 pieceBitmap[1][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "o");
\r
2623 pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "o");
\r
2624 pieceBitmap[1][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "o");
\r
2625 pieceBitmap[1][WhiteFerz] = DoLoadBitmap(hInst, "sf", squareSize, "o");
\r
2626 pieceBitmap[1][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "o");
\r
2627 pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "su", squareSize, "o");
\r
2628 pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "sh", squareSize, "o");
\r
2629 pieceBitmap[1][WhiteCardinal] = DoLoadBitmap(hInst, "sa", squareSize, "o");
\r
2630 pieceBitmap[1][WhiteDragon] = DoLoadBitmap(hInst, "sc", squareSize, "o");
\r
2631 pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "sg", squareSize, "o");
\r
2632 pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "ss", squareSize, "o");
\r
2633 pieceBitmap[2][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "w");
\r
2634 pieceBitmap[2][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "w");
\r
2635 pieceBitmap[2][WhiteBishop] = DoLoadBitmap(hInst, "sr", squareSize, "w");
\r
2636 pieceBitmap[2][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "w");
\r
2637 pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "w");
\r
2638 pieceBitmap[2][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "w");
\r
2639 pieceBitmap[2][WhiteFerz] = DoLoadBitmap(hInst, "sw", squareSize, "w");
\r
2640 pieceBitmap[2][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "w");
\r
2641 pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "sp", squareSize, "w");
\r
2642 pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "sn", squareSize, "w");
\r
2643 pieceBitmap[2][WhiteCardinal] = DoLoadBitmap(hInst, "sr", squareSize, "w");
\r
2644 pieceBitmap[2][WhiteDragon] = DoLoadBitmap(hInst, "sr", squareSize, "w");
\r
2645 pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "sl", squareSize, "w");
\r
2646 pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "sw", squareSize, "w");
\r
2652 PieceBitmap(ChessSquare p, int kind)
\r
2654 if ((int) p >= (int) BlackPawn)
\r
2655 p = (ChessSquare) ((int) p - (int) BlackPawn + (int) WhitePawn);
\r
2657 return pieceBitmap[kind][(int) p];
\r
2660 /***************************************************************/
\r
2662 #define MIN(a,b) (((a) < (b)) ? (a) : (b))
\r
2663 #define MAX(a,b) (((a) > (b)) ? (a) : (b))
\r
2665 #define MIN3(a,b,c) (((a) < (b) && (a) < (c)) ? (a) : (((b) < (a) && (b) < (c)) ? (b) : (c)))
\r
2666 #define MAX3(a,b,c) (((a) > (b) && (a) > (c)) ? (a) : (((b) > (a) && (b) > (c)) ? (b) : (c)))
\r
2670 SquareToPos(int row, int column, int * x, int * y)
\r
2673 *x = boardRect.left + lineGap + ((BOARD_WIDTH-1)-column) * (squareSize + lineGap);
\r
2674 *y = boardRect.top + lineGap + row * (squareSize + lineGap);
\r
2676 *x = boardRect.left + lineGap + column * (squareSize + lineGap);
\r
2677 *y = boardRect.top + lineGap + ((BOARD_HEIGHT-1)-row) * (squareSize + lineGap);
\r
2682 DrawCoordsOnDC(HDC hdc)
\r
2684 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
2685 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
2686 char str[2] = { NULLCHAR, NULLCHAR };
\r
2687 int oldMode, oldAlign, x, y, start, i;
\r
2691 if (!appData.showCoords)
\r
2694 start = flipView ? 1-(ONE!='1') : 23+(ONE!='1')-BOARD_HEIGHT;
\r
2696 oldBrush = SelectObject(hdc, GetStockObject(BLACK_BRUSH));
\r
2697 oldMode = SetBkMode(hdc, (appData.monoMode ? OPAQUE : TRANSPARENT));
\r
2698 oldAlign = GetTextAlign(hdc);
\r
2699 oldFont = SelectObject(hdc, font[boardSize][COORD_FONT]->hf);
\r
2701 y = boardRect.top + lineGap;
\r
2702 x = boardRect.left + lineGap + gameInfo.holdingsWidth*(squareSize + lineGap);
\r
2704 SetTextAlign(hdc, TA_LEFT|TA_TOP);
\r
2705 for (i = 0; i < BOARD_HEIGHT; i++) {
\r
2706 str[0] = files[start + i];
\r
2707 ExtTextOut(hdc, x + 2, y + 1, 0, NULL, str, 1, NULL);
\r
2708 y += squareSize + lineGap;
\r
2711 start = flipView ? 12-(BOARD_RGHT-BOARD_LEFT) : 12;
\r
2713 SetTextAlign(hdc, TA_RIGHT|TA_BOTTOM);
\r
2714 for (i = 0; i < BOARD_RGHT - BOARD_LEFT; i++) {
\r
2715 str[0] = ranks[start + i];
\r
2716 ExtTextOut(hdc, x + squareSize - 2, y - 1, 0, NULL, str, 1, NULL);
\r
2717 x += squareSize + lineGap;
\r
2720 SelectObject(hdc, oldBrush);
\r
2721 SetBkMode(hdc, oldMode);
\r
2722 SetTextAlign(hdc, oldAlign);
\r
2723 SelectObject(hdc, oldFont);
\r
2727 DrawGridOnDC(HDC hdc)
\r
2731 if (lineGap != 0) {
\r
2732 oldPen = SelectObject(hdc, gridPen);
\r
2733 PolyPolyline(hdc, gridEndpoints, gridVertexCounts, BOARD_WIDTH+BOARD_HEIGHT + 2);
\r
2734 SelectObject(hdc, oldPen);
\r
2738 #define HIGHLIGHT_PEN 0
\r
2739 #define PREMOVE_PEN 1
\r
2742 DrawHighlightOnDC(HDC hdc, BOOLEAN on, int x, int y, int pen)
\r
2745 HPEN oldPen, hPen;
\r
2746 if (lineGap == 0) return;
\r
2748 x1 = boardRect.left +
\r
2749 lineGap/2 + ((BOARD_WIDTH-1)-x) * (squareSize + lineGap);
\r
2750 y1 = boardRect.top +
\r
2751 lineGap/2 + y * (squareSize + lineGap);
\r
2753 x1 = boardRect.left +
\r
2754 lineGap/2 + x * (squareSize + lineGap);
\r
2755 y1 = boardRect.top +
\r
2756 lineGap/2 + ((BOARD_HEIGHT-1)-y) * (squareSize + lineGap);
\r
2758 hPen = pen ? premovePen : highlightPen;
\r
2759 oldPen = SelectObject(hdc, on ? hPen : gridPen);
\r
2760 MoveToEx(hdc, x1, y1, NULL);
\r
2761 LineTo(hdc, x1 + squareSize + lineGap, y1);
\r
2762 LineTo(hdc, x1 + squareSize + lineGap, y1 + squareSize + lineGap);
\r
2763 LineTo(hdc, x1, y1 + squareSize + lineGap);
\r
2764 LineTo(hdc, x1, y1);
\r
2765 SelectObject(hdc, oldPen);
\r
2769 DrawHighlightsOnDC(HDC hdc, HighlightInfo *h, int pen)
\r
2772 for (i=0; i<2; i++) {
\r
2773 if (h->sq[i].x >= 0 && h->sq[i].y >= 0)
\r
2774 DrawHighlightOnDC(hdc, TRUE,
\r
2775 h->sq[i].x, h->sq[i].y,
\r
2780 /* Note: sqcolor is used only in monoMode */
\r
2781 /* Note that this code is largely duplicated in woptions.c,
\r
2782 function DrawSampleSquare, so that needs to be updated too */
\r
2784 DrawPieceOnDC(HDC hdc, ChessSquare piece, int color, int sqcolor, int x, int y, HDC tmphdc)
\r
2786 HBITMAP oldBitmap;
\r
2790 if (appData.blindfold) return;
\r
2792 /* [AS] Use font-based pieces if needed */
\r
2793 if( fontBitmapSquareSize >= 0 && (squareSize > 32 || gameInfo.variant >= VariantShogi)) {
\r
2794 /* Create piece bitmaps, or do nothing if piece set is up to date */
\r
2795 CreatePiecesFromFont();
\r
2797 if( fontBitmapSquareSize == squareSize ) {
\r
2798 int index = TranslatePieceToFontPiece(piece);
\r
2800 SelectObject( tmphdc, hPieceMask[ index ] );
\r
2802 if(appData.upsideDown ? color==flipView : (flipView && gameInfo.variant == VariantShogi))
\r
2803 StretchBlt(hdc, x+squareSize, y+squareSize, -squareSize, -squareSize, tmphdc, 0, 0, squareSize, squareSize, SRCAND);
\r
2807 squareSize, squareSize,
\r
2812 SelectObject( tmphdc, hPieceFace[ index ] );
\r
2814 if(appData.upsideDown ? color==flipView : (flipView && gameInfo.variant == VariantShogi))
\r
2815 StretchBlt(hdc, x+squareSize, y+squareSize, -squareSize, -squareSize, tmphdc, 0, 0, squareSize, squareSize, SRCPAINT);
\r
2819 squareSize, squareSize,
\r
2828 if (appData.monoMode) {
\r
2829 SelectObject(tmphdc, PieceBitmap(piece,
\r
2830 color == sqcolor ? OUTLINE_PIECE : SOLID_PIECE));
\r
2831 BitBlt(hdc, x, y, squareSize, squareSize, tmphdc, 0, 0,
\r
2832 sqcolor ? SRCCOPY : NOTSRCCOPY);
\r
2834 tmpSize = squareSize;
\r
2836 ((piece >= (int)WhiteNightrider && piece <= WhiteGrasshopper) ||
\r
2837 (piece >= (int)BlackNightrider && piece <= BlackGrasshopper)) ) {
\r
2838 /* [HGM] no bitmap available for promoted pieces in Crazyhouse */
\r
2839 /* Bitmaps of smaller size are substituted, but we have to align them */
\r
2840 x += (squareSize - minorSize)>>1;
\r
2841 y += squareSize - minorSize - 2;
\r
2842 tmpSize = minorSize;
\r
2844 if (color || appData.allWhite ) {
\r
2845 oldBitmap = SelectObject(tmphdc, PieceBitmap(piece, WHITE_PIECE));
\r
2847 oldBrush = SelectObject(hdc, whitePieceBrush);
\r
2848 else oldBrush = SelectObject(hdc, blackPieceBrush);
\r
2849 if(appData.upsideDown && color==flipView)
\r
2850 StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, 0x00B8074A);
\r
2852 BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, 0x00B8074A);
\r
2853 /* Use black for outline of white pieces */
\r
2854 SelectObject(tmphdc, PieceBitmap(piece, OUTLINE_PIECE));
\r
2855 if(appData.upsideDown && color==flipView)
\r
2856 StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, SRCAND);
\r
2858 BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, SRCAND);
\r
2860 /* Use square color for details of black pieces */
\r
2861 oldBitmap = SelectObject(tmphdc, PieceBitmap(piece, SOLID_PIECE));
\r
2862 oldBrush = SelectObject(hdc, blackPieceBrush);
\r
2863 if(appData.upsideDown && !flipView)
\r
2864 StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, 0x00B8074A);
\r
2866 BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, 0x00B8074A);
\r
2868 SelectObject(hdc, oldBrush);
\r
2869 SelectObject(tmphdc, oldBitmap);
\r
2873 /* [AS] Compute a drawing mode for a square, based on specified settings (see DrawTile) */
\r
2874 int GetBackTextureMode( int algo )
\r
2876 int result = BACK_TEXTURE_MODE_DISABLED;
\r
2880 case BACK_TEXTURE_MODE_PLAIN:
\r
2881 result = 1; /* Always use identity map */
\r
2883 case BACK_TEXTURE_MODE_FULL_RANDOM:
\r
2884 result = 1 + (myrandom() % 3); /* Pick a transformation at random */
\r
2892 [AS] Compute and save texture drawing info, otherwise we may not be able
\r
2893 to handle redraws cleanly (as random numbers would always be different).
\r
2895 VOID RebuildTextureSquareInfo()
\r
2905 ZeroMemory( &backTextureSquareInfo, sizeof(backTextureSquareInfo) );
\r
2907 if( liteBackTexture != NULL ) {
\r
2908 if( GetObject( liteBackTexture, sizeof(bi), &bi ) > 0 ) {
\r
2909 lite_w = bi.bmWidth;
\r
2910 lite_h = bi.bmHeight;
\r
2914 if( darkBackTexture != NULL ) {
\r
2915 if( GetObject( darkBackTexture, sizeof(bi), &bi ) > 0 ) {
\r
2916 dark_w = bi.bmWidth;
\r
2917 dark_h = bi.bmHeight;
\r
2921 for( row=0; row<BOARD_HEIGHT; row++ ) {
\r
2922 for( col=0; col<BOARD_WIDTH; col++ ) {
\r
2923 if( (col + row) & 1 ) {
\r
2925 if( lite_w >= squareSize && lite_h >= squareSize ) {
\r
2926 if( lite_w >= squareSize*BOARD_WIDTH )
\r
2927 backTextureSquareInfo[row][col].x = (2*col+1)*lite_w/(2*BOARD_WIDTH) - squareSize/2; /* [HGM] cut out of center of virtual square */
\r
2929 backTextureSquareInfo[row][col].x = col * (lite_w - squareSize) / (BOARD_WIDTH-1); /* [HGM] divide by size-1 in stead of size! */
\r
2930 if( lite_h >= squareSize*BOARD_HEIGHT )
\r
2931 backTextureSquareInfo[row][col].y = (2*(BOARD_HEIGHT-row)-1)*lite_h/(2*BOARD_HEIGHT) - squareSize/2;
\r
2933 backTextureSquareInfo[row][col].y = (BOARD_HEIGHT-1-row) * (lite_h - squareSize) / (BOARD_HEIGHT-1);
\r
2934 backTextureSquareInfo[row][col].mode = GetBackTextureMode(liteBackTextureMode);
\r
2939 if( dark_w >= squareSize && dark_h >= squareSize ) {
\r
2940 if( dark_w >= squareSize*BOARD_WIDTH )
\r
2941 backTextureSquareInfo[row][col].x = (2*col+1) * dark_w / (2*BOARD_WIDTH) - squareSize/2;
\r
2943 backTextureSquareInfo[row][col].x = col * (dark_w - squareSize) / (BOARD_WIDTH-1);
\r
2944 if( dark_h >= squareSize*BOARD_HEIGHT )
\r
2945 backTextureSquareInfo[row][col].y = (2*(BOARD_HEIGHT-row)-1) * dark_h / (2*BOARD_HEIGHT) - squareSize/2;
\r
2947 backTextureSquareInfo[row][col].y = (BOARD_HEIGHT-1-row) * (dark_h - squareSize) / (BOARD_HEIGHT-1);
\r
2948 backTextureSquareInfo[row][col].mode = GetBackTextureMode(darkBackTextureMode);
\r
2955 /* [AS] Arrow highlighting support */
\r
2957 static double A_WIDTH = 5; /* Width of arrow body */
\r
2959 #define A_HEIGHT_FACTOR 6 /* Length of arrow "point", relative to body width */
\r
2960 #define A_WIDTH_FACTOR 3 /* Width of arrow "point", relative to body width */
\r
2962 static double Sqr( double x )
\r
2967 static int Round( double x )
\r
2969 return (int) (x + 0.5);
\r
2972 /* Draw an arrow between two points using current settings */
\r
2973 VOID DrawArrowBetweenPoints( HDC hdc, int s_x, int s_y, int d_x, int d_y )
\r
2976 double dx, dy, j, k, x, y;
\r
2978 if( d_x == s_x ) {
\r
2979 int h = (d_y > s_y) ? +A_WIDTH*A_HEIGHT_FACTOR : -A_WIDTH*A_HEIGHT_FACTOR;
\r
2981 arrow[0].x = s_x + A_WIDTH + 0.5;
\r
2984 arrow[1].x = s_x + A_WIDTH + 0.5;
\r
2985 arrow[1].y = d_y - h;
\r
2987 arrow[2].x = arrow[1].x + A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;
\r
2988 arrow[2].y = d_y - h;
\r
2993 arrow[5].x = arrow[1].x - 2*A_WIDTH + 0.5;
\r
2994 arrow[5].y = d_y - h;
\r
2996 arrow[4].x = arrow[5].x - A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;
\r
2997 arrow[4].y = d_y - h;
\r
2999 arrow[6].x = arrow[1].x - 2*A_WIDTH + 0.5;
\r
3002 else if( d_y == s_y ) {
\r
3003 int w = (d_x > s_x) ? +A_WIDTH*A_HEIGHT_FACTOR : -A_WIDTH*A_HEIGHT_FACTOR;
\r
3006 arrow[0].y = s_y + A_WIDTH + 0.5;
\r
3008 arrow[1].x = d_x - w;
\r
3009 arrow[1].y = s_y + A_WIDTH + 0.5;
\r
3011 arrow[2].x = d_x - w;
\r
3012 arrow[2].y = arrow[1].y + A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;
\r
3017 arrow[5].x = d_x - w;
\r
3018 arrow[5].y = arrow[1].y - 2*A_WIDTH + 0.5;
\r
3020 arrow[4].x = d_x - w;
\r
3021 arrow[4].y = arrow[5].y - A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;
\r
3024 arrow[6].y = arrow[1].y - 2*A_WIDTH + 0.5;
\r
3027 /* [AS] Needed a lot of paper for this! :-) */
\r
3028 dy = (double) (d_y - s_y) / (double) (d_x - s_x);
\r
3029 dx = (double) (s_x - d_x) / (double) (s_y - d_y);
\r
3031 j = sqrt( Sqr(A_WIDTH) / (1.0 + Sqr(dx)) );
\r
3033 k = sqrt( Sqr(A_WIDTH*A_HEIGHT_FACTOR) / (1.0 + Sqr(dy)) );
\r
3038 arrow[0].x = Round(x - j);
\r
3039 arrow[0].y = Round(y + j*dx);
\r
3041 arrow[1].x = Round(arrow[0].x + 2*j); // [HGM] prevent width to be affected by rounding twice
\r
3042 arrow[1].y = Round(arrow[0].y - 2*j*dx);
\r
3045 x = (double) d_x - k;
\r
3046 y = (double) d_y - k*dy;
\r
3049 x = (double) d_x + k;
\r
3050 y = (double) d_y + k*dy;
\r
3053 x = Round(x); y = Round(y); // [HGM] make sure width of shaft is rounded the same way on both ends
\r
3055 arrow[6].x = Round(x - j);
\r
3056 arrow[6].y = Round(y + j*dx);
\r
3058 arrow[2].x = Round(arrow[6].x + 2*j);
\r
3059 arrow[2].y = Round(arrow[6].y - 2*j*dx);
\r
3061 arrow[3].x = Round(arrow[2].x + j*(A_WIDTH_FACTOR-1));
\r
3062 arrow[3].y = Round(arrow[2].y - j*(A_WIDTH_FACTOR-1)*dx);
\r
3067 arrow[5].x = Round(arrow[6].x - j*(A_WIDTH_FACTOR-1));
\r
3068 arrow[5].y = Round(arrow[6].y + j*(A_WIDTH_FACTOR-1)*dx);
\r
3071 Polygon( hdc, arrow, 7 );
\r
3074 /* [AS] Draw an arrow between two squares */
\r
3075 VOID DrawArrowBetweenSquares( HDC hdc, int s_col, int s_row, int d_col, int d_row )
\r
3077 int s_x, s_y, d_x, d_y;
\r
3084 if( s_col == d_col && s_row == d_row ) {
\r
3088 /* Get source and destination points */
\r
3089 SquareToPos( s_row, s_col, &s_x, &s_y);
\r
3090 SquareToPos( d_row, d_col, &d_x, &d_y);
\r
3093 d_y += squareSize / 2 - squareSize / 4; // [HGM] round towards same centers on all sides!
\r
3095 else if( d_y < s_y ) {
\r
3096 d_y += squareSize / 2 + squareSize / 4;
\r
3099 d_y += squareSize / 2;
\r
3103 d_x += squareSize / 2 - squareSize / 4;
\r
3105 else if( d_x < s_x ) {
\r
3106 d_x += squareSize / 2 + squareSize / 4;
\r
3109 d_x += squareSize / 2;
\r
3112 s_x += squareSize / 2;
\r
3113 s_y += squareSize / 2;
\r
3115 /* Adjust width */
\r
3116 A_WIDTH = squareSize / 14.; //[HGM] make float
\r
3119 stLB.lbStyle = BS_SOLID;
\r
3120 stLB.lbColor = appData.highlightArrowColor;
\r
3123 hpen = CreatePen( PS_SOLID, 2, RGB(0x00,0x00,0x00) );
\r
3124 holdpen = SelectObject( hdc, hpen );
\r
3125 hbrush = CreateBrushIndirect( &stLB );
\r
3126 holdbrush = SelectObject( hdc, hbrush );
\r
3128 DrawArrowBetweenPoints( hdc, s_x, s_y, d_x, d_y );
\r
3130 SelectObject( hdc, holdpen );
\r
3131 SelectObject( hdc, holdbrush );
\r
3132 DeleteObject( hpen );
\r
3133 DeleteObject( hbrush );
\r
3136 BOOL HasHighlightInfo()
\r
3138 BOOL result = FALSE;
\r
3140 if( highlightInfo.sq[0].x >= 0 && highlightInfo.sq[0].y >= 0 &&
\r
3141 highlightInfo.sq[1].x >= 0 && highlightInfo.sq[1].y >= 0 )
\r
3149 BOOL IsDrawArrowEnabled()
\r
3151 BOOL result = FALSE;
\r
3153 if( appData.highlightMoveWithArrow && squareSize >= 32 ) {
\r
3160 VOID DrawArrowHighlight( HDC hdc )
\r
3162 if( IsDrawArrowEnabled() && HasHighlightInfo() ) {
\r
3163 DrawArrowBetweenSquares( hdc,
\r
3164 highlightInfo.sq[0].x, highlightInfo.sq[0].y,
\r
3165 highlightInfo.sq[1].x, highlightInfo.sq[1].y );
\r
3169 HRGN GetArrowHighlightClipRegion( HDC hdc )
\r
3171 HRGN result = NULL;
\r
3173 if( HasHighlightInfo() ) {
\r
3174 int x1, y1, x2, y2;
\r
3175 int sx, sy, dx, dy;
\r
3177 SquareToPos(highlightInfo.sq[0].y, highlightInfo.sq[0].x, &x1, &y1 );
\r
3178 SquareToPos(highlightInfo.sq[1].y, highlightInfo.sq[1].x, &x2, &y2 );
\r
3180 sx = MIN( x1, x2 );
\r
3181 sy = MIN( y1, y2 );
\r
3182 dx = MAX( x1, x2 ) + squareSize;
\r
3183 dy = MAX( y1, y2 ) + squareSize;
\r
3185 result = CreateRectRgn( sx, sy, dx, dy );
\r
3192 Warning: this function modifies the behavior of several other functions.
\r
3194 Basically, Winboard is optimized to avoid drawing the whole board if not strictly
\r
3195 needed. Unfortunately, the decision whether or not to perform a full or partial
\r
3196 repaint is scattered all over the place, which is not good for features such as
\r
3197 "arrow highlighting" that require a full repaint of the board.
\r
3199 So, I've tried to patch the code where I thought it made sense (e.g. after or during
\r
3200 user interaction, when speed is not so important) but especially to avoid errors
\r
3201 in the displayed graphics.
\r
3203 In such patched places, I always try refer to this function so there is a single
\r
3204 place to maintain knowledge.
\r
3206 To restore the original behavior, just return FALSE unconditionally.
\r
3208 BOOL IsFullRepaintPreferrable()
\r
3210 BOOL result = FALSE;
\r
3212 if( (appData.highlightLastMove || appData.highlightDragging) && IsDrawArrowEnabled() ) {
\r
3213 /* Arrow may appear on the board */
\r
3221 This function is called by DrawPosition to know whether a full repaint must
\r
3224 Only DrawPosition may directly call this function, which makes use of
\r
3225 some state information. Other function should call DrawPosition specifying
\r
3226 the repaint flag, and can use IsFullRepaintPreferrable if needed.
\r
3228 BOOL DrawPositionNeedsFullRepaint()
\r
3230 BOOL result = FALSE;
\r
3233 Probably a slightly better policy would be to trigger a full repaint
\r
3234 when animInfo.piece changes state (i.e. empty -> non-empty and viceversa),
\r
3235 but animation is fast enough that it's difficult to notice.
\r
3237 if( animInfo.piece == EmptySquare ) {
\r
3238 if( (appData.highlightLastMove || appData.highlightDragging) && IsDrawArrowEnabled() /*&& HasHighlightInfo()*/ ) {
\r
3247 DrawBoardOnDC(HDC hdc, Board board, HDC tmphdc)
\r
3249 int row, column, x, y, square_color, piece_color;
\r
3250 ChessSquare piece;
\r
3252 HDC texture_hdc = NULL;
\r
3254 /* [AS] Initialize background textures if needed */
\r
3255 if( liteBackTexture != NULL || darkBackTexture != NULL ) {
\r
3256 static int backTextureBoardSize; /* [HGM] boardsize: also new texture if board format changed */
\r
3257 if( backTextureSquareSize != squareSize
\r
3258 || backTextureBoardSize != BOARD_WIDTH+BOARD_FILES*BOARD_HEIGHT) {
\r
3259 backTextureBoardSize = BOARD_WIDTH+BOARD_FILES*BOARD_HEIGHT;
\r
3260 backTextureSquareSize = squareSize;
\r
3261 RebuildTextureSquareInfo();
\r
3264 texture_hdc = CreateCompatibleDC( hdc );
\r
3267 for (row = 0; row < BOARD_HEIGHT; row++) {
\r
3268 for (column = 0; column < BOARD_WIDTH; column++) {
\r
3270 SquareToPos(row, column, &x, &y);
\r
3272 piece = board[row][column];
\r
3274 square_color = ((column + row) % 2) == 1;
\r
3275 if( gameInfo.variant == VariantXiangqi ) {
\r
3276 square_color = !InPalace(row, column);
\r
3277 if(BOARD_HEIGHT&1) { if(row==BOARD_HEIGHT/2) square_color ^= 1; }
\r
3278 else if(row < BOARD_HEIGHT/2) square_color ^= 1;
\r
3280 piece_color = (int) piece < (int) BlackPawn;
\r
3283 /* [HGM] holdings file: light square or black */
\r
3284 if(column == BOARD_LEFT-2) {
\r
3285 if( row > BOARD_HEIGHT - gameInfo.holdingsSize - 1 )
\r
3288 DisplayHoldingsCount(hdc, x, y, 0, 0); /* black out */
\r
3292 if(column == BOARD_RGHT + 1 ) {
\r
3293 if( row < gameInfo.holdingsSize )
\r
3296 DisplayHoldingsCount(hdc, x, y, 0, 0);
\r
3300 if(column == BOARD_LEFT-1 ) /* left align */
\r
3301 DisplayHoldingsCount(hdc, x, y, flipView, (int) board[row][column]);
\r
3302 else if( column == BOARD_RGHT) /* right align */
\r
3303 DisplayHoldingsCount(hdc, x, y, !flipView, (int) board[row][column]);
\r
3305 if (appData.monoMode) {
\r
3306 if (piece == EmptySquare) {
\r
3307 BitBlt(hdc, x, y, squareSize, squareSize, 0, 0, 0,
\r
3308 square_color ? WHITENESS : BLACKNESS);
\r
3310 DrawPieceOnDC(hdc, piece, piece_color, square_color, x, y, tmphdc);
\r
3313 else if( backTextureSquareInfo[row][column].mode > 0 ) {
\r
3314 /* [AS] Draw the square using a texture bitmap */
\r
3315 HBITMAP hbm = SelectObject( texture_hdc, square_color ? liteBackTexture : darkBackTexture );
\r
3316 int r = row, c = column; // [HGM] do not flip board in flipView
\r
3317 if(flipView) { r = BOARD_HEIGHT-1 - r; c = BOARD_WIDTH-1 - c; }
\r
3320 squareSize, squareSize,
\r
3323 backTextureSquareInfo[r][c].mode,
\r
3324 backTextureSquareInfo[r][c].x,
\r
3325 backTextureSquareInfo[r][c].y );
\r
3327 SelectObject( texture_hdc, hbm );
\r
3329 if (piece != EmptySquare) {
\r
3330 DrawPieceOnDC(hdc, piece, piece_color, -1, x, y, tmphdc);
\r
3334 HBRUSH brush = square_color ? lightSquareBrush : darkSquareBrush;
\r
3336 oldBrush = SelectObject(hdc, brush );
\r
3337 BitBlt(hdc, x, y, squareSize, squareSize, 0, 0, 0, PATCOPY);
\r
3338 SelectObject(hdc, oldBrush);
\r
3339 if (piece != EmptySquare)
\r
3340 DrawPieceOnDC(hdc, piece, piece_color, -1, x, y, tmphdc);
\r
3345 if( texture_hdc != NULL ) {
\r
3346 DeleteDC( texture_hdc );
\r
3350 int saveDiagFlag = 0; FILE *diagFile; // [HGM] diag
\r
3351 void fputDW(FILE *f, int x)
\r
3353 fputc(x & 255, f);
\r
3354 fputc(x>>8 & 255, f);
\r
3355 fputc(x>>16 & 255, f);
\r
3356 fputc(x>>24 & 255, f);
\r
3359 #define MAX_CLIPS 200 /* more than enough */
\r
3362 DrawLogoOnDC(HDC hdc, RECT logoRect, HBITMAP logo)
\r
3364 // HBITMAP bufferBitmap;
\r
3369 int w = 100, h = 50;
\r
3371 if(logo == NULL) return;
\r
3372 // GetClientRect(hwndMain, &Rect);
\r
3373 // bufferBitmap = CreateCompatibleBitmap(hdc, Rect.right-Rect.left+1,
\r
3374 // Rect.bottom-Rect.top+1);
\r
3375 tmphdc = CreateCompatibleDC(hdc);
\r
3376 hbm = SelectObject(tmphdc, logo);
\r
3377 if( GetObject( logo, sizeof(bi), &bi ) > 0 ) {
\r
3381 StretchBlt(hdc, logoRect.left, logoRect.top, logoRect.right - logoRect.left,
\r
3382 logoRect.bottom - logoRect.top, tmphdc, 0, 0, w, h, SRCCOPY);
\r
3383 SelectObject(tmphdc, hbm);
\r
3387 static HDC hdcSeek;
\r
3389 // [HGM] seekgraph
\r
3390 void DrawSeekAxis( int x, int y, int xTo, int yTo )
\r
3393 HPEN hp = SelectObject( hdcSeek, gridPen );
\r
3394 MoveToEx( hdcSeek, boardRect.left+x, boardRect.top+y, &stPt );
\r
3395 LineTo( hdcSeek, boardRect.left+xTo, boardRect.top+yTo );
\r
3396 SelectObject( hdcSeek, hp );
\r
3399 // front-end wrapper for drawing functions to do rectangles
\r
3400 void DrawSeekBackground( int left, int top, int right, int bottom )
\r
3405 if (hdcSeek == NULL) {
\r
3406 hdcSeek = GetDC(hwndMain);
\r
3407 if (!appData.monoMode) {
\r
3408 SelectPalette(hdcSeek, hPal, FALSE);
\r
3409 RealizePalette(hdcSeek);
\r
3412 hp = SelectObject( hdcSeek, gridPen );
\r
3413 rc.top = boardRect.top+top; rc.left = boardRect.left+left;
\r
3414 rc.bottom = boardRect.top+bottom; rc.right = boardRect.left+right;
\r
3415 FillRect( hdcSeek, &rc, lightSquareBrush );
\r
3416 SelectObject( hdcSeek, hp );
\r
3419 // front-end wrapper for putting text in graph
\r
3420 void DrawSeekText(char *buf, int x, int y)
\r
3423 SetBkMode( hdcSeek, TRANSPARENT );
\r
3424 GetTextExtentPoint32( hdcSeek, buf, strlen(buf), &stSize );
\r
3425 TextOut( hdcSeek, boardRect.left+x-3, boardRect.top+y-stSize.cy/2, buf, strlen(buf) );
\r
3428 void DrawSeekDot(int x, int y, int color)
\r
3430 int square = color & 0x80;
\r
3431 HBRUSH oldBrush = SelectObject(hdcSeek,
\r
3432 color == 0 ? markerBrush : color == 1 ? darkSquareBrush : explodeBrush);
\r
3435 Rectangle(hdcSeek, boardRect.left+x - squareSize/9, boardRect.top+y - squareSize/9,
\r
3436 boardRect.left+x + squareSize/9, boardRect.top+y + squareSize/9);
\r
3438 Ellipse(hdcSeek, boardRect.left+x - squareSize/8, boardRect.top+y - squareSize/8,
\r
3439 boardRect.left+x + squareSize/8, boardRect.top+y + squareSize/8);
\r
3440 SelectObject(hdcSeek, oldBrush);
\r
3444 HDCDrawPosition(HDC hdc, BOOLEAN repaint, Board board)
\r
3446 static Board lastReq[2], lastDrawn[2];
\r
3447 static HighlightInfo lastDrawnHighlight, lastDrawnPremove;
\r
3448 static int lastDrawnFlipView = 0;
\r
3449 static int lastReqValid[2] = {0, 0}, lastDrawnValid[2] = {0, 0};
\r
3450 int releaseDC, x, y, x2, y2, row, column, num_clips = 0, i;
\r
3453 HBITMAP bufferBitmap;
\r
3454 HBITMAP oldBitmap;
\r
3456 HRGN clips[MAX_CLIPS];
\r
3457 ChessSquare dragged_piece = EmptySquare;
\r
3458 int nr = twoBoards*partnerUp;
\r
3460 /* I'm undecided on this - this function figures out whether a full
\r
3461 * repaint is necessary on its own, so there's no real reason to have the
\r
3462 * caller tell it that. I think this can safely be set to FALSE - but
\r
3463 * if we trust the callers not to request full repaints unnessesarily, then
\r
3464 * we could skip some clipping work. In other words, only request a full
\r
3465 * redraw when the majority of pieces have changed positions (ie. flip,
\r
3466 * gamestart and similar) --Hawk
\r
3468 Boolean fullrepaint = repaint;
\r
3470 if(DrawSeekGraph()) return; // [HG} seekgraph: suppress printing board if seek graph up
\r
3472 if( DrawPositionNeedsFullRepaint() ) {
\r
3473 fullrepaint = TRUE;
\r
3476 if (board == NULL) {
\r
3477 if (!lastReqValid[nr]) {
\r
3480 board = lastReq[nr];
\r
3482 CopyBoard(lastReq[nr], board);
\r
3483 lastReqValid[nr] = 1;
\r
3486 if (doingSizing) {
\r
3490 if (IsIconic(hwndMain)) {
\r
3494 if (hdc == NULL) {
\r
3495 hdc = GetDC(hwndMain);
\r
3496 if (!appData.monoMode) {
\r
3497 SelectPalette(hdc, hPal, FALSE);
\r
3498 RealizePalette(hdc);
\r
3502 releaseDC = FALSE;
\r
3505 /* Create some work-DCs */
\r
3506 hdcmem = CreateCompatibleDC(hdc);
\r
3507 tmphdc = CreateCompatibleDC(hdc);
\r
3509 /* If dragging is in progress, we temporarely remove the piece */
\r
3510 /* [HGM] or temporarily decrease count if stacked */
\r
3511 /* !! Moved to before board compare !! */
\r
3512 if (dragInfo.from.x >= 0 && dragInfo.pos.x >= 0) {
\r
3513 dragged_piece = board[dragInfo.from.y][dragInfo.from.x];
\r
3514 if(dragInfo.from.x == BOARD_LEFT-2 ) {
\r
3515 if(--board[dragInfo.from.y][dragInfo.from.x+1] == 0 )
\r
3516 board[dragInfo.from.y][dragInfo.from.x] = EmptySquare;
\r
3518 if(dragInfo.from.x == BOARD_RGHT+1) {
\r
3519 if(--board[dragInfo.from.y][dragInfo.from.x-1] == 0 )
\r
3520 board[dragInfo.from.y][dragInfo.from.x] = EmptySquare;
\r
3522 board[dragInfo.from.y][dragInfo.from.x] = gatingPiece;
\r
3525 /* Figure out which squares need updating by comparing the
\r
3526 * newest board with the last drawn board and checking if
\r
3527 * flipping has changed.
\r
3529 if (!fullrepaint && lastDrawnValid[nr] && (nr == 1 || lastDrawnFlipView == flipView)) {
\r
3530 for (row = 0; row < BOARD_HEIGHT; row++) { /* [HGM] true size, not 8 */
\r
3531 for (column = 0; column < BOARD_WIDTH; column++) {
\r
3532 if (lastDrawn[nr][row][column] != board[row][column]) {
\r
3533 SquareToPos(row, column, &x, &y);
\r
3534 clips[num_clips++] =
\r
3535 CreateRectRgn(x, y, x + squareSize, y + squareSize);
\r
3539 if(nr == 0) { // [HGM] dual: no highlights on second board
\r
3540 for (i=0; i<2; i++) {
\r
3541 if (lastDrawnHighlight.sq[i].x != highlightInfo.sq[i].x ||
\r
3542 lastDrawnHighlight.sq[i].y != highlightInfo.sq[i].y) {
\r
3543 if (lastDrawnHighlight.sq[i].x >= 0 &&
\r
3544 lastDrawnHighlight.sq[i].y >= 0) {
\r
3545 SquareToPos(lastDrawnHighlight.sq[i].y,
\r
3546 lastDrawnHighlight.sq[i].x, &x, &y);
\r
3547 clips[num_clips++] =
\r
3548 CreateRectRgn(x - lineGap, y - lineGap,
\r
3549 x + squareSize + lineGap, y + squareSize + lineGap);
\r
3551 if (highlightInfo.sq[i].x >= 0 && highlightInfo.sq[i].y >= 0) {
\r
3552 SquareToPos(highlightInfo.sq[i].y, highlightInfo.sq[i].x, &x, &y);
\r
3553 clips[num_clips++] =
\r
3554 CreateRectRgn(x - lineGap, y - lineGap,
\r
3555 x + squareSize + lineGap, y + squareSize + lineGap);
\r
3559 for (i=0; i<2; i++) {
\r
3560 if (lastDrawnPremove.sq[i].x != premoveHighlightInfo.sq[i].x ||
\r
3561 lastDrawnPremove.sq[i].y != premoveHighlightInfo.sq[i].y) {
\r
3562 if (lastDrawnPremove.sq[i].x >= 0 &&
\r
3563 lastDrawnPremove.sq[i].y >= 0) {
\r
3564 SquareToPos(lastDrawnPremove.sq[i].y,
\r
3565 lastDrawnPremove.sq[i].x, &x, &y);
\r
3566 clips[num_clips++] =
\r
3567 CreateRectRgn(x - lineGap, y - lineGap,
\r
3568 x + squareSize + lineGap, y + squareSize + lineGap);
\r
3570 if (premoveHighlightInfo.sq[i].x >= 0 &&
\r
3571 premoveHighlightInfo.sq[i].y >= 0) {
\r
3572 SquareToPos(premoveHighlightInfo.sq[i].y,
\r
3573 premoveHighlightInfo.sq[i].x, &x, &y);
\r
3574 clips[num_clips++] =
\r
3575 CreateRectRgn(x - lineGap, y - lineGap,
\r
3576 x + squareSize + lineGap, y + squareSize + lineGap);
\r
3580 } else { // nr == 1
\r
3581 partnerHighlightInfo.sq[0].y = board[EP_STATUS-4];
\r
3582 partnerHighlightInfo.sq[0].x = board[EP_STATUS-3];
\r
3583 partnerHighlightInfo.sq[1].y = board[EP_STATUS-2];
\r
3584 partnerHighlightInfo.sq[1].x = board[EP_STATUS-1];
\r
3585 for (i=0; i<2; i++) {
\r
3586 if (partnerHighlightInfo.sq[i].x >= 0 &&
\r
3587 partnerHighlightInfo.sq[i].y >= 0) {
\r
3588 SquareToPos(partnerHighlightInfo.sq[i].y,
\r
3589 partnerHighlightInfo.sq[i].x, &x, &y);
\r
3590 clips[num_clips++] =
\r
3591 CreateRectRgn(x - lineGap, y - lineGap,
\r
3592 x + squareSize + lineGap, y + squareSize + lineGap);
\r
3594 if (oldPartnerHighlight.sq[i].x >= 0 &&
\r
3595 oldPartnerHighlight.sq[i].y >= 0) {
\r
3596 SquareToPos(oldPartnerHighlight.sq[i].y,
\r
3597 oldPartnerHighlight.sq[i].x, &x, &y);
\r
3598 clips[num_clips++] =
\r
3599 CreateRectRgn(x - lineGap, y - lineGap,
\r
3600 x + squareSize + lineGap, y + squareSize + lineGap);
\r
3605 fullrepaint = TRUE;
\r
3608 /* Create a buffer bitmap - this is the actual bitmap
\r
3609 * being written to. When all the work is done, we can
\r
3610 * copy it to the real DC (the screen). This avoids
\r
3611 * the problems with flickering.
\r
3613 GetClientRect(hwndMain, &Rect);
\r
3614 bufferBitmap = CreateCompatibleBitmap(hdc, Rect.right-Rect.left+1,
\r
3615 Rect.bottom-Rect.top+1);
\r
3616 oldBitmap = SelectObject(hdcmem, bufferBitmap);
\r
3617 if (!appData.monoMode) {
\r
3618 SelectPalette(hdcmem, hPal, FALSE);
\r
3621 /* Create clips for dragging */
\r
3622 if (!fullrepaint) {
\r
3623 if (dragInfo.from.x >= 0) {
\r
3624 SquareToPos(dragInfo.from.y, dragInfo.from.x, &x, &y);
\r
3625 clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);
\r
3627 if (dragInfo.start.x >= 0) {
\r
3628 SquareToPos(dragInfo.start.y, dragInfo.start.x, &x, &y);
\r
3629 clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);
\r
3631 if (dragInfo.pos.x >= 0) {
\r
3632 x = dragInfo.pos.x - squareSize / 2;
\r
3633 y = dragInfo.pos.y - squareSize / 2;
\r
3634 clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);
\r
3636 if (dragInfo.lastpos.x >= 0) {
\r
3637 x = dragInfo.lastpos.x - squareSize / 2;
\r
3638 y = dragInfo.lastpos.y - squareSize / 2;
\r
3639 clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);
\r
3643 /* Are we animating a move?
\r
3645 * - remove the piece from the board (temporarely)
\r
3646 * - calculate the clipping region
\r
3648 if (!fullrepaint) {
\r
3649 if (animInfo.piece != EmptySquare) {
\r
3650 board[animInfo.from.y][animInfo.from.x] = EmptySquare;
\r
3651 x = boardRect.left + animInfo.lastpos.x;
\r
3652 y = boardRect.top + animInfo.lastpos.y;
\r
3653 x2 = boardRect.left + animInfo.pos.x;
\r
3654 y2 = boardRect.top + animInfo.pos.y;
\r
3655 clips[num_clips++] = CreateRectRgn(MIN(x,x2), MIN(y,y2), MAX(x,x2)+squareSize, MAX(y,y2)+squareSize);
\r
3656 /* Slight kludge. The real problem is that after AnimateMove is
\r
3657 done, the position on the screen does not match lastDrawn.
\r
3658 This currently causes trouble only on e.p. captures in
\r
3659 atomic, where the piece moves to an empty square and then
\r
3660 explodes. The old and new positions both had an empty square
\r
3661 at the destination, but animation has drawn a piece there and
\r
3662 we have to remember to erase it. [HGM] moved until after setting lastDrawn */
\r
3663 lastDrawn[0][animInfo.to.y][animInfo.to.x] = animInfo.piece;
\r
3667 /* No clips? Make sure we have fullrepaint set to TRUE */
\r
3668 if (num_clips == 0)
\r
3669 fullrepaint = TRUE;
\r
3671 /* Set clipping on the memory DC */
\r
3672 if (!fullrepaint) {
\r
3673 SelectClipRgn(hdcmem, clips[0]);
\r
3674 for (x = 1; x < num_clips; x++) {
\r
3675 if (ExtSelectClipRgn(hdcmem, clips[x], RGN_OR) == ERROR)
\r
3676 abort(); // this should never ever happen!
\r
3680 /* Do all the drawing to the memory DC */
\r
3681 if(explodeInfo.radius) { // [HGM] atomic
\r
3683 int x, y, r=(explodeInfo.radius * squareSize)/100;
\r
3684 ChessSquare piece = board[explodeInfo.fromY][explodeInfo.fromX];
\r
3685 board[explodeInfo.fromY][explodeInfo.fromX] = EmptySquare; // suppress display of capturer
\r
3686 SquareToPos(explodeInfo.toY, explodeInfo.toX, &x, &y);
\r
3687 x += squareSize/2;
\r
3688 y += squareSize/2;
\r
3689 if(!fullrepaint) {
\r
3690 clips[num_clips] = CreateRectRgn(x-r, y-r, x+r, y+r);
\r
3691 ExtSelectClipRgn(hdcmem, clips[num_clips++], RGN_OR);
\r
3693 DrawGridOnDC(hdcmem);
\r
3694 DrawHighlightsOnDC(hdcmem, &highlightInfo, HIGHLIGHT_PEN);
\r
3695 DrawHighlightsOnDC(hdcmem, &premoveHighlightInfo, PREMOVE_PEN);
\r
3696 DrawBoardOnDC(hdcmem, board, tmphdc);
\r
3697 board[explodeInfo.fromY][explodeInfo.fromX] = piece;
\r
3698 oldBrush = SelectObject(hdcmem, explodeBrush);
\r
3699 Ellipse(hdcmem, x-r, y-r, x+r, y+r);
\r
3700 SelectObject(hdcmem, oldBrush);
\r
3702 DrawGridOnDC(hdcmem);
\r
3703 if(nr == 0) { // [HGM] dual: decide which highlights to draw
\r
3704 DrawHighlightsOnDC(hdcmem, &highlightInfo, HIGHLIGHT_PEN);
\r
3705 DrawHighlightsOnDC(hdcmem, &premoveHighlightInfo, PREMOVE_PEN);
\r
3707 DrawHighlightsOnDC(hdcmem, &partnerHighlightInfo, HIGHLIGHT_PEN);
\r
3708 oldPartnerHighlight = partnerHighlightInfo;
\r
3710 DrawBoardOnDC(hdcmem, board, tmphdc);
\r
3712 if(nr == 0) // [HGM] dual: markers only on left board
\r
3713 for (row = 0; row < BOARD_HEIGHT; row++) {
\r
3714 for (column = 0; column < BOARD_WIDTH; column++) {
\r
3715 if (marker[row][column]) { // marker changes only occur with full repaint!
\r
3716 HBRUSH oldBrush = SelectObject(hdcmem,
\r
3717 marker[row][column] == 2 ? markerBrush : explodeBrush);
\r
3718 SquareToPos(row, column, &x, &y);
\r
3719 Ellipse(hdcmem, x + squareSize/4, y + squareSize/4,
\r
3720 x + 3*squareSize/4, y + 3*squareSize/4);
\r
3721 SelectObject(hdcmem, oldBrush);
\r
3726 HBITMAP whiteLogo = (HBITMAP) first.programLogo, blackLogo = (HBITMAP) second.programLogo;
\r
3727 if(appData.autoLogo) {
\r
3729 switch(gameMode) { // pick logos based on game mode
\r
3730 case IcsObserving:
\r
3731 whiteLogo = second.programLogo; // ICS logo
\r
3732 blackLogo = second.programLogo;
\r
3735 case IcsPlayingWhite:
\r
3736 if(!appData.zippyPlay) whiteLogo = userLogo;
\r
3737 blackLogo = second.programLogo; // ICS logo
\r
3739 case IcsPlayingBlack:
\r
3740 whiteLogo = second.programLogo; // ICS logo
\r
3741 blackLogo = appData.zippyPlay ? first.programLogo : userLogo;
\r
3743 case TwoMachinesPlay:
\r
3744 if(first.twoMachinesColor[0] == 'b') {
\r
3745 whiteLogo = second.programLogo;
\r
3746 blackLogo = first.programLogo;
\r
3749 case MachinePlaysWhite:
\r
3750 blackLogo = userLogo;
\r
3752 case MachinePlaysBlack:
\r
3753 whiteLogo = userLogo;
\r
3754 blackLogo = first.programLogo;
\r
3757 DrawLogoOnDC(hdc, leftLogoRect, flipClock ? blackLogo : whiteLogo);
\r
3758 DrawLogoOnDC(hdc, rightLogoRect, flipClock ? whiteLogo : blackLogo);
\r
3761 if( appData.highlightMoveWithArrow ) {
\r
3762 DrawArrowHighlight(hdcmem);
\r
3765 DrawCoordsOnDC(hdcmem);
\r
3767 CopyBoard(lastDrawn[nr], board); /* [HGM] Moved to here from end of routine, */
\r
3768 /* to make sure lastDrawn contains what is actually drawn */
\r
3770 /* Put the dragged piece back into place and draw it (out of place!) */
\r
3771 if (dragged_piece != EmptySquare) {
\r
3772 /* [HGM] or restack */
\r
3773 if(dragInfo.from.x == BOARD_LEFT-2 )
\r
3774 board[dragInfo.from.y][dragInfo.from.x+1]++;
\r
3776 if(dragInfo.from.x == BOARD_RGHT+1 )
\r
3777 board[dragInfo.from.y][dragInfo.from.x-1]++;
\r
3778 board[dragInfo.from.y][dragInfo.from.x] = dragged_piece;
\r
3779 x = dragInfo.pos.x - squareSize / 2;
\r
3780 y = dragInfo.pos.y - squareSize / 2;
\r
3781 DrawPieceOnDC(hdcmem, dragged_piece,
\r
3782 ((int) dragged_piece < (int) BlackPawn),
\r
3783 (dragInfo.from.y + dragInfo.from.x) % 2, x, y, tmphdc);
\r
3786 /* Put the animated piece back into place and draw it */
\r
3787 if (animInfo.piece != EmptySquare) {
\r
3788 board[animInfo.from.y][animInfo.from.x] = animInfo.piece;
\r
3789 x = boardRect.left + animInfo.pos.x;
\r
3790 y = boardRect.top + animInfo.pos.y;
\r
3791 DrawPieceOnDC(hdcmem, animInfo.piece,
\r
3792 ((int) animInfo.piece < (int) BlackPawn),
\r
3793 (animInfo.from.y + animInfo.from.x) % 2, x, y, tmphdc);
\r
3796 /* Release the bufferBitmap by selecting in the old bitmap
\r
3797 * and delete the memory DC
\r
3799 SelectObject(hdcmem, oldBitmap);
\r
3802 /* Set clipping on the target DC */
\r
3803 if (!fullrepaint) {
\r
3804 if(nr == 1) for (x = 0; x < num_clips; x++) { // [HGM] dual: translate clips
\r
3806 GetRgnBox(clips[x], &rect);
\r
3807 DeleteObject(clips[x]);
\r
3808 clips[x] = CreateRectRgn(rect.left + wpMain.width/2, rect.top,
\r
3809 rect.right + wpMain.width/2, rect.bottom);
\r
3811 SelectClipRgn(hdc, clips[0]);
\r
3812 for (x = 1; x < num_clips; x++) {
\r
3813 if (ExtSelectClipRgn(hdc, clips[x], RGN_OR) == ERROR)
\r
3814 abort(); // this should never ever happen!
\r
3818 /* Copy the new bitmap onto the screen in one go.
\r
3819 * This way we avoid any flickering
\r
3821 oldBitmap = SelectObject(tmphdc, bufferBitmap);
\r
3822 BitBlt(hdc, boardRect.left + twoBoards*partnerUp*wpMain.width/2, boardRect.top, // [HGM] dual
\r
3823 boardRect.right - boardRect.left,
\r
3824 boardRect.bottom - boardRect.top,
\r
3825 tmphdc, boardRect.left, boardRect.top, SRCCOPY);
\r
3826 if(saveDiagFlag) {
\r
3827 BITMAP b; int i, j=0, m, w, wb, fac=0; char pData[1000000];
\r
3828 BITMAPINFOHEADER bih; int color[16], nrColors=0;
\r
3830 GetObject(bufferBitmap, sizeof(b), &b);
\r
3831 if(b.bmWidthBytes*b.bmHeight <= 990000) {
\r
3832 bih.biSize = sizeof(BITMAPINFOHEADER);
\r
3833 bih.biWidth = b.bmWidth;
\r
3834 bih.biHeight = b.bmHeight;
\r
3836 bih.biBitCount = b.bmBitsPixel;
\r
3837 bih.biCompression = 0;
\r
3838 bih.biSizeImage = b.bmWidthBytes*b.bmHeight;
\r
3839 bih.biXPelsPerMeter = 0;
\r
3840 bih.biYPelsPerMeter = 0;
\r
3841 bih.biClrUsed = 0;
\r
3842 bih.biClrImportant = 0;
\r
3843 // fprintf(diagFile, "t=%d\nw=%d\nh=%d\nB=%d\nP=%d\nX=%d\n",
\r
3844 // b.bmType, b.bmWidth, b.bmHeight, b.bmWidthBytes, b.bmPlanes, b.bmBitsPixel);
\r
3845 GetDIBits(tmphdc,bufferBitmap,0,b.bmHeight,pData,(BITMAPINFO*)&bih,DIB_RGB_COLORS);
\r
3846 // fprintf(diagFile, "%8x\n", (int) pData);
\r
3848 wb = b.bmWidthBytes;
\r
3850 for(i=0; i<wb*(b.bmHeight - boardRect.top + OUTER_MARGIN)>>2; i++) {
\r
3851 int k = ((int*) pData)[i];
\r
3852 for(j=0; j<nrColors; j++) if(color[j] == k) break;
\r
3853 if(j >= 16) break;
\r
3855 if(j >= nrColors) nrColors = j+1;
\r
3857 if(j<16) { // 16 colors is enough. Compress to 4 bits per pixel
\r
3859 for(i=0; i<b.bmHeight - boardRect.top + OUTER_MARGIN; i++) {
\r
3860 for(w=0; w<(wb>>2); w+=2) {
\r
3861 int k = ((int*) pData)[(wb*i>>2) + w];
\r
3862 for(j=0; j<nrColors; j++) if(color[j] == k) break;
\r
3863 k = ((int*) pData)[(wb*i>>2) + w + 1];
\r
3864 for(m=0; m<nrColors; m++) if(color[m] == k) break;
\r
3865 pData[p++] = m | j<<4;
\r
3867 while(p&3) pData[p++] = 0;
\r
3870 wb = ((wb+31)>>5)<<2;
\r
3872 // write BITMAPFILEHEADER
\r
3873 fprintf(diagFile, "BM");
\r
3874 fputDW(diagFile, wb*(b.bmHeight - boardRect.top + OUTER_MARGIN)+0x36 + (fac?64:0));
\r
3875 fputDW(diagFile, 0);
\r
3876 fputDW(diagFile, 0x36 + (fac?64:0));
\r
3877 // write BITMAPINFOHEADER
\r
3878 fputDW(diagFile, 40);
\r
3879 fputDW(diagFile, b.bmWidth);
\r
3880 fputDW(diagFile, b.bmHeight - boardRect.top + OUTER_MARGIN);
\r
3881 if(fac) fputDW(diagFile, 0x040001); // planes and bits/pixel
\r
3882 else fputDW(diagFile, 0x200001); // planes and bits/pixel
\r
3883 fputDW(diagFile, 0);
\r
3884 fputDW(diagFile, 0);
\r
3885 fputDW(diagFile, 0);
\r
3886 fputDW(diagFile, 0);
\r
3887 fputDW(diagFile, 0);
\r
3888 fputDW(diagFile, 0);
\r
3889 // write color table
\r
3891 for(i=0; i<16; i++) fputDW(diagFile, color[i]);
\r
3892 // write bitmap data
\r
3893 for(i=0; i<wb*(b.bmHeight - boardRect.top + OUTER_MARGIN); i++)
\r
3894 fputc(pData[i], diagFile);
\r
3898 SelectObject(tmphdc, oldBitmap);
\r
3900 /* Massive cleanup */
\r
3901 for (x = 0; x < num_clips; x++)
\r
3902 DeleteObject(clips[x]);
\r
3905 DeleteObject(bufferBitmap);
\r
3908 ReleaseDC(hwndMain, hdc);
\r
3910 if (lastDrawnFlipView != flipView && nr == 0) {
\r
3912 CheckMenuItem(GetMenu(hwndMain),IDM_FlipView, MF_BYCOMMAND|MF_CHECKED);
\r
3914 CheckMenuItem(GetMenu(hwndMain),IDM_FlipView, MF_BYCOMMAND|MF_UNCHECKED);
\r
3917 /* CopyBoard(lastDrawn, board);*/
\r
3918 lastDrawnHighlight = highlightInfo;
\r
3919 lastDrawnPremove = premoveHighlightInfo;
\r
3920 lastDrawnFlipView = flipView;
\r
3921 lastDrawnValid[nr] = 1;
\r
3924 /* [HGM] diag: Save the current board display to the given open file and close the file */
\r
3929 saveDiagFlag = 1; diagFile = f;
\r
3930 HDCDrawPosition(NULL, TRUE, NULL);
\r
3934 // if(f != NULL) fprintf(f, "Sorry, but this feature is still in preparation\n");
\r
3941 /*---------------------------------------------------------------------------*\
\r
3942 | CLIENT PAINT PROCEDURE
\r
3943 | This is the main event-handler for the WM_PAINT message.
\r
3945 \*---------------------------------------------------------------------------*/
\r
3947 PaintProc(HWND hwnd)
\r
3953 if((hdc = BeginPaint(hwnd, &ps))) {
\r
3954 if (IsIconic(hwnd)) {
\r
3955 DrawIcon(hdc, 2, 2, iconCurrent);
\r
3957 if (!appData.monoMode) {
\r
3958 SelectPalette(hdc, hPal, FALSE);
\r
3959 RealizePalette(hdc);
\r
3961 HDCDrawPosition(hdc, 1, NULL);
\r
3962 if(twoBoards) { // [HGM] dual: also redraw other board in other orientation
\r
3963 flipView = !flipView; partnerUp = !partnerUp;
\r
3964 HDCDrawPosition(hdc, 1, NULL);
\r
3965 flipView = !flipView; partnerUp = !partnerUp;
\r
3968 SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);
\r
3969 ExtTextOut(hdc, messageRect.left, messageRect.top,
\r
3970 ETO_CLIPPED|ETO_OPAQUE,
\r
3971 &messageRect, messageText, strlen(messageText), NULL);
\r
3972 SelectObject(hdc, oldFont);
\r
3973 DisplayBothClocks();
\r
3975 EndPaint(hwnd,&ps);
\r
3983 * If the user selects on a border boundary, return -1; if off the board,
\r
3984 * return -2. Otherwise map the event coordinate to the square.
\r
3985 * The offset boardRect.left or boardRect.top must already have been
\r
3986 * subtracted from x.
\r
3988 int EventToSquare(x, limit)
\r
3996 if ((x % (squareSize + lineGap)) >= squareSize)
\r
3998 x /= (squareSize + lineGap);
\r
4010 DropEnable dropEnables[] = {
\r
4011 { 'P', DP_Pawn, N_("Pawn") },
\r
4012 { 'N', DP_Knight, N_("Knight") },
\r
4013 { 'B', DP_Bishop, N_("Bishop") },
\r
4014 { 'R', DP_Rook, N_("Rook") },
\r
4015 { 'Q', DP_Queen, N_("Queen") },
\r
4019 SetupDropMenu(HMENU hmenu)
\r
4021 int i, count, enable;
\r
4023 extern char white_holding[], black_holding[];
\r
4024 char item[MSG_SIZ];
\r
4026 for (i=0; i<sizeof(dropEnables)/sizeof(DropEnable); i++) {
\r
4027 p = strchr(gameMode == IcsPlayingWhite ? white_holding : black_holding,
\r
4028 dropEnables[i].piece);
\r
4030 while (p && *p++ == dropEnables[i].piece) count++;
\r
4031 snprintf(item, MSG_SIZ, "%s %d", T_(dropEnables[i].name), count);
\r
4032 enable = count > 0 || !appData.testLegality
\r
4033 /*!!temp:*/ || (gameInfo.variant == VariantCrazyhouse
\r
4034 && !appData.icsActive);
\r
4035 ModifyMenu(hmenu, dropEnables[i].command,
\r
4036 MF_BYCOMMAND | (enable ? MF_ENABLED : MF_GRAYED) | MF_STRING,
\r
4037 dropEnables[i].command, item);
\r
4041 void DragPieceBegin(int x, int y)
\r
4043 dragInfo.lastpos.x = boardRect.left + x;
\r
4044 dragInfo.lastpos.y = boardRect.top + y;
\r
4045 dragInfo.from.x = fromX;
\r
4046 dragInfo.from.y = fromY;
\r
4047 dragInfo.start = dragInfo.from;
\r
4048 SetCapture(hwndMain);
\r
4051 void DragPieceEnd(int x, int y)
\r
4054 dragInfo.start.x = dragInfo.start.y = -1;
\r
4055 dragInfo.from = dragInfo.start;
\r
4056 dragInfo.pos = dragInfo.lastpos = dragInfo.start;
\r
4059 /* Event handler for mouse messages */
\r
4061 MouseEvent(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
\r
4065 static int recursive = 0;
\r
4067 BOOLEAN forceFullRepaint = IsFullRepaintPreferrable(); /* [AS] */
\r
4070 if (message == WM_MBUTTONUP) {
\r
4071 /* Hideous kludge to fool TrackPopupMenu into paying attention
\r
4072 to the middle button: we simulate pressing the left button too!
\r
4074 PostMessage(hwnd, WM_LBUTTONDOWN, wParam, lParam);
\r
4075 PostMessage(hwnd, WM_LBUTTONUP, wParam, lParam);
\r
4081 pt.x = LOWORD(lParam);
\r
4082 pt.y = HIWORD(lParam);
\r
4083 x = EventToSquare(pt.x - boardRect.left, BOARD_WIDTH);
\r
4084 y = EventToSquare(pt.y - boardRect.top, BOARD_HEIGHT);
\r
4085 if (!flipView && y >= 0) {
\r
4086 y = BOARD_HEIGHT - 1 - y;
\r
4088 if (flipView && x >= 0) {
\r
4089 x = BOARD_WIDTH - 1 - x;
\r
4092 shiftKey = GetKeyState(VK_SHIFT) < 0; // [HGM] remember last shift status
\r
4094 switch (message) {
\r
4095 case WM_LBUTTONDOWN:
\r
4096 if (PtInRect((LPRECT) &whiteRect, pt)) {
\r
4097 if (gameMode == EditPosition) {
\r
4098 SetWhiteToPlayEvent();
\r
4099 } else if (gameMode == EditGame || GetKeyState(VK_SHIFT) < 0) {
\r
4100 AdjustClock(flipClock, -1);
\r
4101 } else if (gameMode == IcsPlayingBlack ||
\r
4102 gameMode == MachinePlaysWhite) {
\r
4105 } else if (PtInRect((LPRECT) &blackRect, pt)) {
\r
4106 if (gameMode == EditPosition) {
\r
4107 SetBlackToPlayEvent();
\r
4108 } else if (gameMode == EditGame || GetKeyState(VK_SHIFT) < 0) {
\r
4109 AdjustClock(!flipClock, -1);
\r
4110 } else if (gameMode == IcsPlayingWhite ||
\r
4111 gameMode == MachinePlaysBlack) {
\r
4115 dragInfo.start.x = dragInfo.start.y = -1;
\r
4116 dragInfo.from = dragInfo.start;
\r
4117 if(fromX == -1 && frozen) { // not sure where this is for
\r
4118 fromX = fromY = -1;
\r
4119 DrawPosition(forceFullRepaint || FALSE, NULL); /* [AS] */
\r
4122 LeftClick(Press, pt.x - boardRect.left, pt.y - boardRect.top);
\r
4123 DrawPosition(TRUE, NULL);
\r
4126 case WM_LBUTTONUP:
\r
4127 LeftClick(Release, pt.x - boardRect.left, pt.y - boardRect.top);
\r
4128 DrawPosition(TRUE, NULL);
\r
4131 case WM_MOUSEMOVE:
\r
4132 if(SeekGraphClick(Press, pt.x - boardRect.left, pt.y - boardRect.top, 1)) break;
\r
4133 MovePV(pt.x - boardRect.left, pt.y - boardRect.top, boardRect.bottom - boardRect.top);
\r
4134 if ((appData.animateDragging || appData.highlightDragging)
\r
4135 && (wParam & MK_LBUTTON)
\r
4136 && dragInfo.from.x >= 0)
\r
4138 BOOL full_repaint = FALSE;
\r
4140 if (appData.animateDragging) {
\r
4141 dragInfo.pos = pt;
\r
4143 if (appData.highlightDragging) {
\r
4144 SetHighlights(fromX, fromY, x, y);
\r
4145 if( IsDrawArrowEnabled() && (x < 0 || x >= BOARD_WIDTH || y < 0 || y >= BOARD_HEIGHT) ) {
\r
4146 full_repaint = TRUE;
\r
4150 DrawPosition( full_repaint, NULL);
\r
4152 dragInfo.lastpos = dragInfo.pos;
\r
4156 case WM_MOUSEWHEEL: // [DM]
\r
4157 { static int lastDir = 0; // [HGM] build in some hysteresis to avoid spurious events
\r
4158 /* Mouse Wheel is being rolled forward
\r
4159 * Play moves forward
\r
4161 if((short)HIWORD(wParam) < 0 && currentMove < forwardMostMove)
\r
4162 { if(lastDir == 1) ForwardEvent(); else lastDir = 1; } // [HGM] suppress first event in direction
\r
4163 /* Mouse Wheel is being rolled backward
\r
4164 * Play moves backward
\r
4166 if((short)HIWORD(wParam) > 0 && currentMove > backwardMostMove)
\r
4167 { if(lastDir == -1) BackwardEvent(); else lastDir = -1; }
\r
4171 case WM_MBUTTONUP:
\r
4172 case WM_RBUTTONUP:
\r
4174 RightClick(Release, pt.x - boardRect.left, pt.y - boardRect.top, &fromX, &fromY);
\r
4177 case WM_MBUTTONDOWN:
\r
4178 case WM_RBUTTONDOWN:
\r
4181 fromX = fromY = -1;
\r
4182 dragInfo.pos.x = dragInfo.pos.y = -1;
\r
4183 dragInfo.start.x = dragInfo.start.y = -1;
\r
4184 dragInfo.from = dragInfo.start;
\r
4185 dragInfo.lastpos = dragInfo.pos;
\r
4186 if (appData.highlightDragging) {
\r
4187 ClearHighlights();
\r
4190 /* [HGM] right mouse button in clock area edit-game mode ups clock */
\r
4191 if (PtInRect((LPRECT) &whiteRect, pt)) {
\r
4192 if (gameMode == EditGame || GetKeyState(VK_SHIFT) < 0) AdjustClock(flipClock, 1);
\r
4193 } else if (PtInRect((LPRECT) &blackRect, pt)) {
\r
4194 if (gameMode == EditGame || GetKeyState(VK_SHIFT) < 0) AdjustClock(!flipClock, 1);
\r
4198 DrawPosition(TRUE, NULL);
\r
4200 menuNr = RightClick(Press, pt.x - boardRect.left, pt.y - boardRect.top, &fromX, &fromY);
\r
4203 if (message == WM_MBUTTONDOWN) {
\r
4204 buttonCount = 3; /* even if system didn't think so */
\r
4205 if (wParam & MK_SHIFT)
\r
4206 MenuPopup(hwnd, pt, LoadMenu(hInst, "BlackPieceMenu"), -1);
\r
4208 MenuPopup(hwnd, pt, LoadMenu(hInst, "WhitePieceMenu"), -1);
\r
4209 } else { /* message == WM_RBUTTONDOWN */
\r
4210 /* Just have one menu, on the right button. Windows users don't
\r
4211 think to try the middle one, and sometimes other software steals
\r
4212 it, or it doesn't really exist. */
\r
4213 if(gameInfo.variant != VariantShogi)
\r
4214 MenuPopup(hwnd, pt, LoadMenu(hInst, "PieceMenu"), -1);
\r
4216 MenuPopup(hwnd, pt, LoadMenu(hInst, "ShogiPieceMenu"), -1);
\r
4220 SetCapture(hwndMain);
4223 hmenu = LoadMenu(hInst, "DropPieceMenu");
\r
4224 SetupDropMenu(hmenu);
\r
4225 MenuPopup(hwnd, pt, hmenu, -1);
\r
4235 /* Preprocess messages for buttons in main window */
\r
4237 ButtonProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
\r
4239 int id = GetWindowLong(hwnd, GWL_ID);
\r
4242 for (i=0; i<N_BUTTONS; i++) {
\r
4243 if (buttonDesc[i].id == id) break;
\r
4245 if (i == N_BUTTONS) return 0;
\r
4246 switch (message) {
\r
4251 dir = (wParam == VK_LEFT) ? -1 : 1;
\r
4252 SetFocus(buttonDesc[(i + dir + N_BUTTONS) % N_BUTTONS].hwnd);
\r
4259 SendMessage(hwndMain, WM_COMMAND, MAKEWPARAM(buttonDesc[i].id, 0), 0);
\r
4262 if (appData.icsActive && (isalpha((char)wParam) || wParam == '0')) {
\r
4263 // [HGM] movenum: only letters or leading zero should go to ICS input
\r
4264 HWND h = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
4265 if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);
\r
4267 SendMessage(h, WM_CHAR, wParam, lParam);
\r
4269 } else if (isalpha((char)wParam) || isdigit((char)wParam)){
\r
4270 PopUpMoveDialog((char)wParam);
\r
4276 return CallWindowProc(buttonDesc[i].wndproc, hwnd, message, wParam, lParam);
\r
4279 /* Process messages for Promotion dialog box */
\r
4281 Promotion(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
4285 switch (message) {
\r
4286 case WM_INITDIALOG: /* message: initialize dialog box */
\r
4287 /* Center the dialog over the application window */
\r
4288 CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));
\r
4289 Translate(hDlg, DLG_PromotionKing);
\r
4290 ShowWindow(GetDlgItem(hDlg, PB_King),
\r
4291 (!appData.testLegality || gameInfo.variant == VariantSuicide ||
\r
4292 gameInfo.variant == VariantGiveaway || gameInfo.variant == VariantSuper ) ?
\r
4293 SW_SHOW : SW_HIDE);
\r
4294 /* [HGM] Only allow C & A promotions if these pieces are defined */
\r
4295 ShowWindow(GetDlgItem(hDlg, PB_Archbishop),
\r
4296 ((PieceToChar(WhiteAngel) >= 'A' &&
\r
4297 PieceToChar(WhiteAngel) != '~') ||
\r
4298 (PieceToChar(BlackAngel) >= 'A' &&
\r
4299 PieceToChar(BlackAngel) != '~') ) ?
\r
4300 SW_SHOW : SW_HIDE);
\r
4301 ShowWindow(GetDlgItem(hDlg, PB_Chancellor),
\r
4302 ((PieceToChar(WhiteMarshall) >= 'A' &&
\r
4303 PieceToChar(WhiteMarshall) != '~') ||
\r
4304 (PieceToChar(BlackMarshall) >= 'A' &&
\r
4305 PieceToChar(BlackMarshall) != '~') ) ?
\r
4306 SW_SHOW : SW_HIDE);
\r
4307 /* [HGM] Hide B & R button in Shogi, use Q as promote, N as defer */
\r
4308 ShowWindow(GetDlgItem(hDlg, PB_Rook),
\r
4309 gameInfo.variant != VariantShogi ?
\r
4310 SW_SHOW : SW_HIDE);
\r
4311 ShowWindow(GetDlgItem(hDlg, PB_Bishop),
\r
4312 gameInfo.variant != VariantShogi ?
\r
4313 SW_SHOW : SW_HIDE);
\r
4314 if(gameInfo.variant == VariantShogi) {
\r
4315 SetDlgItemText(hDlg, PB_Queen, "YES");
\r
4316 SetDlgItemText(hDlg, PB_Knight, "NO");
\r
4317 SetWindowText(hDlg, "Promote?");
\r
4319 ShowWindow(GetDlgItem(hDlg, IDC_Centaur),
\r
4320 gameInfo.variant == VariantSuper ?
\r
4321 SW_SHOW : SW_HIDE);
\r
4324 case WM_COMMAND: /* message: received a command */
\r
4325 switch (LOWORD(wParam)) {
\r
4327 EndDialog(hDlg, TRUE); /* Exit the dialog */
\r
4328 ClearHighlights();
\r
4329 DrawPosition(FALSE, NULL);
\r
4332 promoChar = gameInfo.variant == VariantSuper ? PieceToChar(BlackSilver) : PieceToChar(BlackKing);
\r
4335 promoChar = gameInfo.variant == VariantShogi ? '+' : PieceToChar(BlackQueen);
\r
4338 promoChar = PieceToChar(BlackRook);
\r
4341 promoChar = PieceToChar(BlackBishop);
\r
4343 case PB_Chancellor:
\r
4344 promoChar = PieceToChar(BlackMarshall);
\r
4346 case PB_Archbishop:
\r
4347 promoChar = PieceToChar(BlackAngel);
\r
4350 promoChar = gameInfo.variant == VariantShogi ? '=' : PieceToChar(BlackKnight);
\r
4355 EndDialog(hDlg, TRUE); /* Exit the dialog */
\r
4356 /* [HGM] <popupFix> Call FinishMove rather than UserMoveEvent, as we
\r
4357 only show the popup when we are already sure the move is valid or
\r
4358 legal. We pass a faulty move type, but the kludge is that FinishMove
\r
4359 will figure out it is a promotion from the promoChar. */
\r
4360 UserMoveEvent(fromX, fromY, toX, toY, promoChar);
\r
4361 fromX = fromY = -1;
\r
4362 if (!appData.highlightLastMove) {
\r
4363 ClearHighlights();
\r
4364 DrawPosition(FALSE, NULL);
\r
4371 /* Pop up promotion dialog */
\r
4373 PromotionPopup(HWND hwnd)
\r
4377 lpProc = MakeProcInstance((FARPROC)Promotion, hInst);
\r
4378 DialogBox(hInst, MAKEINTRESOURCE(DLG_PromotionKing),
\r
4379 hwnd, (DLGPROC)lpProc);
\r
4380 FreeProcInstance(lpProc);
\r
4386 DrawPosition(TRUE, NULL);
\r
4387 PromotionPopup(hwndMain);
\r
4390 /* Toggle ShowThinking */
\r
4392 ToggleShowThinking()
\r
4394 appData.showThinking = !appData.showThinking;
\r
4395 ShowThinkingEvent();
\r
4399 LoadGameDialog(HWND hwnd, char* title)
\r
4403 char fileTitle[MSG_SIZ];
\r
4404 f = OpenFileDialog(hwnd, "rb", "",
\r
4405 appData.oldSaveStyle ? "gam" : "pgn",
\r
4407 title, &number, fileTitle, NULL);
\r
4409 cmailMsgLoaded = FALSE;
\r
4410 if (number == 0) {
\r
4411 int error = GameListBuild(f);
\r
4413 DisplayError(_("Cannot build game list"), error);
\r
4414 } else if (!ListEmpty(&gameList) &&
\r
4415 ((ListGame *) gameList.tailPred)->number > 1) {
\r
4416 GameListPopUp(f, fileTitle);
\r
4419 GameListDestroy();
\r
4422 LoadGame(f, number, fileTitle, FALSE);
\r
4426 int get_term_width()
\r
4431 HFONT hfont, hold_font;
\r
4436 hText = GetDlgItem(hwndConsole, OPT_ConsoleText);
\r
4440 // get the text metrics
\r
4441 hdc = GetDC(hText);
\r
4442 lf = font[boardSize][CONSOLE_FONT]->lf;
\r
4443 if (consoleCF.dwEffects & CFE_BOLD)
\r
4444 lf.lfWeight = FW_BOLD;
\r
4445 if (consoleCF.dwEffects & CFE_ITALIC)
\r
4446 lf.lfItalic = TRUE;
\r
4447 if (consoleCF.dwEffects & CFE_STRIKEOUT)
\r
4448 lf.lfStrikeOut = TRUE;
\r
4449 if (consoleCF.dwEffects & CFE_UNDERLINE)
\r
4450 lf.lfUnderline = TRUE;
\r
4451 hfont = CreateFontIndirect(&lf);
\r
4452 hold_font = SelectObject(hdc, hfont);
\r
4453 GetTextMetrics(hdc, &tm);
\r
4454 SelectObject(hdc, hold_font);
\r
4455 DeleteObject(hfont);
\r
4456 ReleaseDC(hText, hdc);
\r
4458 // get the rectangle
\r
4459 SendMessage(hText, EM_GETRECT, 0, (LPARAM)&rc);
\r
4461 return (rc.right-rc.left) / tm.tmAveCharWidth;
\r
4464 void UpdateICSWidth(HWND hText)
\r
4466 LONG old_width, new_width;
\r
4468 new_width = get_term_width(hText, FALSE);
\r
4469 old_width = GetWindowLong(hText, GWL_USERDATA);
\r
4470 if (new_width != old_width)
\r
4472 ics_update_width(new_width);
\r
4473 SetWindowLong(hText, GWL_USERDATA, new_width);
\r
4478 ChangedConsoleFont()
\r
4481 CHARRANGE tmpsel, sel;
\r
4482 MyFont *f = font[boardSize][CONSOLE_FONT];
\r
4483 HWND hText = GetDlgItem(hwndConsole, OPT_ConsoleText);
\r
4484 HWND hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
4487 cfmt.cbSize = sizeof(CHARFORMAT);
\r
4488 cfmt.dwMask = CFM_FACE|CFM_SIZE|CFM_CHARSET;
\r
4489 safeStrCpy(cfmt.szFaceName, font[boardSize][CONSOLE_FONT]->mfp.faceName,
\r
4490 sizeof(cfmt.szFaceName)/sizeof(cfmt.szFaceName[0]) );
\r
4491 /* yHeight is expressed in twips. A twip is 1/20 of a font's point
\r
4492 * size. This was undocumented in the version of MSVC++ that I had
\r
4493 * when I wrote the code, but is apparently documented now.
\r
4495 cfmt.yHeight = (int)(f->mfp.pointSize * 20.0 + 0.5);
\r
4496 cfmt.bCharSet = f->lf.lfCharSet;
\r
4497 cfmt.bPitchAndFamily = f->lf.lfPitchAndFamily;
\r
4498 SendMessage(hText, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &cfmt);
\r
4499 SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &cfmt);
\r
4500 /* Why are the following seemingly needed too? */
\r
4501 SendMessage(hText, EM_SETCHARFORMAT, SCF_DEFAULT, (LPARAM) &cfmt);
\r
4502 SendMessage(hInput, EM_SETCHARFORMAT, SCF_DEFAULT, (LPARAM) &cfmt);
\r
4503 SendMessage(hText, EM_EXGETSEL, 0, (LPARAM)&sel);
\r
4505 tmpsel.cpMax = -1; /*999999?*/
\r
4506 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&tmpsel);
\r
4507 SendMessage(hText, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cfmt);
\r
4508 /* Trying putting this here too. It still seems to tickle a RichEdit
\r
4509 * bug: sometimes RichEdit indents the first line of a paragraph too.
\r
4511 paraf.cbSize = sizeof(paraf);
\r
4512 paraf.dwMask = PFM_OFFSET | PFM_STARTINDENT;
\r
4513 paraf.dxStartIndent = 0;
\r
4514 paraf.dxOffset = WRAP_INDENT;
\r
4515 SendMessage(hText, EM_SETPARAFORMAT, 0, (LPARAM) ¶f);
\r
4516 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
4517 UpdateICSWidth(hText);
\r
4520 /*---------------------------------------------------------------------------*\
\r
4522 * Window Proc for main window
\r
4524 \*---------------------------------------------------------------------------*/
\r
4526 /* Process messages for main window, etc. */
\r
4528 WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
\r
4531 int wmId, wmEvent;
\r
4535 char fileTitle[MSG_SIZ];
\r
4536 char buf[MSG_SIZ];
\r
4537 static SnapData sd;
\r
4539 switch (message) {
\r
4541 case WM_PAINT: /* message: repaint portion of window */
\r
4545 case WM_ERASEBKGND:
\r
4546 if (IsIconic(hwnd)) {
\r
4547 /* Cheat; change the message */
\r
4548 return (DefWindowProc(hwnd, WM_ICONERASEBKGND, wParam, lParam));
\r
4550 return (DefWindowProc(hwnd, message, wParam, lParam));
\r
4554 case WM_LBUTTONDOWN:
\r
4555 case WM_MBUTTONDOWN:
\r
4556 case WM_RBUTTONDOWN:
\r
4557 case WM_LBUTTONUP:
\r
4558 case WM_MBUTTONUP:
\r
4559 case WM_RBUTTONUP:
\r
4560 case WM_MOUSEMOVE:
\r
4561 case WM_MOUSEWHEEL:
\r
4562 MouseEvent(hwnd, message, wParam, lParam);
\r
4565 JAWS_KB_NAVIGATION
\r
4569 JAWS_ALT_INTERCEPT
\r
4571 if (appData.icsActive && ((char)wParam == '\r' || (char)wParam > ' ' && !((char)wParam >= '1' && (char)wParam <= '9'))) {
\r
4572 // [HGM] movenum: for non-zero digits we always do type-in dialog
\r
4573 HWND h = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
4574 if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);
\r
4576 SendMessage(h, message, wParam, lParam);
\r
4577 } else if(lParam != KF_REPEAT) {
\r
4578 if (isalpha((char)wParam) || isdigit((char)wParam)) {
\r
4579 PopUpMoveDialog((char)wParam);
\r
4580 } else if((char)wParam == 003) CopyGameToClipboard();
\r
4581 else if((char)wParam == 026) PasteGameOrFENFromClipboard();
\r
4586 case WM_PALETTECHANGED:
\r
4587 if (hwnd != (HWND)wParam && !appData.monoMode) {
\r
4589 HDC hdc = GetDC(hwndMain);
\r
4590 SelectPalette(hdc, hPal, TRUE);
\r
4591 nnew = RealizePalette(hdc);
\r
4593 paletteChanged = TRUE;
\r
4594 InvalidateRect(hwnd, &boardRect, FALSE);
\r
4596 ReleaseDC(hwnd, hdc);
\r
4600 case WM_QUERYNEWPALETTE:
\r
4601 if (!appData.monoMode /*&& paletteChanged*/) {
\r
4603 HDC hdc = GetDC(hwndMain);
\r
4604 paletteChanged = FALSE;
\r
4605 SelectPalette(hdc, hPal, FALSE);
\r
4606 nnew = RealizePalette(hdc);
\r
4608 InvalidateRect(hwnd, &boardRect, FALSE);
\r
4610 ReleaseDC(hwnd, hdc);
\r
4615 case WM_COMMAND: /* message: command from application menu */
\r
4616 wmId = LOWORD(wParam);
\r
4617 wmEvent = HIWORD(wParam);
\r
4622 SAY("new game enter a move to play against the computer with white");
\r
4625 case IDM_NewGameFRC:
\r
4626 if( NewGameFRC() == 0 ) {
\r
4631 case IDM_NewVariant:
\r
4632 NewVariantPopup(hwnd);
\r
4635 case IDM_LoadGame:
\r
4636 LoadGameDialog(hwnd, _("Load Game from File"));
\r
4639 case IDM_LoadNextGame:
\r
4643 case IDM_LoadPrevGame:
\r
4647 case IDM_ReloadGame:
\r
4651 case IDM_LoadPosition:
\r
4652 if (gameMode == AnalyzeMode || gameMode == AnalyzeFile) {
\r
4653 Reset(FALSE, TRUE);
\r
4656 f = OpenFileDialog(hwnd, "rb", "",
\r
4657 appData.oldSaveStyle ? "pos" : "fen",
\r
4659 _("Load Position from File"), &number, fileTitle, NULL);
\r
4661 LoadPosition(f, number, fileTitle);
\r
4665 case IDM_LoadNextPosition:
\r
4666 ReloadPosition(1);
\r
4669 case IDM_LoadPrevPosition:
\r
4670 ReloadPosition(-1);
\r
4673 case IDM_ReloadPosition:
\r
4674 ReloadPosition(0);
\r
4677 case IDM_SaveGame:
\r
4678 defName = DefaultFileName(appData.oldSaveStyle ? "gam" : "pgn");
\r
4679 f = OpenFileDialog(hwnd, "a", defName,
\r
4680 appData.oldSaveStyle ? "gam" : "pgn",
\r
4682 _("Save Game to File"), NULL, fileTitle, NULL);
\r
4684 SaveGame(f, 0, "");
\r
4688 case IDM_SavePosition:
\r
4689 defName = DefaultFileName(appData.oldSaveStyle ? "pos" : "fen");
\r
4690 f = OpenFileDialog(hwnd, "a", defName,
\r
4691 appData.oldSaveStyle ? "pos" : "fen",
\r
4693 _("Save Position to File"), NULL, fileTitle, NULL);
\r
4695 SavePosition(f, 0, "");
\r
4699 case IDM_SaveDiagram:
\r
4700 defName = "diagram";
\r
4701 f = OpenFileDialog(hwnd, "wb", defName,
\r
4704 "Save Diagram to File", NULL, fileTitle, NULL);
\r
4710 case IDM_CopyGame:
\r
4711 CopyGameToClipboard();
\r
4714 case IDM_PasteGame:
\r
4715 PasteGameFromClipboard();
\r
4718 case IDM_CopyGameListToClipboard:
\r
4719 CopyGameListToClipboard();
\r
4722 /* [AS] Autodetect FEN or PGN data */
\r
4723 case IDM_PasteAny:
\r
4724 PasteGameOrFENFromClipboard();
\r
4727 /* [AS] Move history */
\r
4728 case IDM_ShowMoveHistory:
\r
4729 if( MoveHistoryIsUp() ) {
\r
4730 MoveHistoryPopDown();
\r
4733 MoveHistoryPopUp();
\r
4737 /* [AS] Eval graph */
\r
4738 case IDM_ShowEvalGraph:
\r
4739 if( EvalGraphIsUp() ) {
\r
4740 EvalGraphPopDown();
\r
4744 SetFocus(hwndMain);
\r
4748 /* [AS] Engine output */
\r
4749 case IDM_ShowEngineOutput:
\r
4750 if( EngineOutputIsUp() ) {
\r
4751 EngineOutputPopDown();
\r
4754 EngineOutputPopUp();
\r
4758 /* [AS] User adjudication */
\r
4759 case IDM_UserAdjudication_White:
\r
4760 UserAdjudicationEvent( +1 );
\r
4763 case IDM_UserAdjudication_Black:
\r
4764 UserAdjudicationEvent( -1 );
\r
4767 case IDM_UserAdjudication_Draw:
\r
4768 UserAdjudicationEvent( 0 );
\r
4771 /* [AS] Game list options dialog */
\r
4772 case IDM_GameListOptions:
\r
4773 GameListOptions();
\r
4780 case IDM_CopyPosition:
\r
4781 CopyFENToClipboard();
\r
4784 case IDM_PastePosition:
\r
4785 PasteFENFromClipboard();
\r
4788 case IDM_MailMove:
\r
4792 case IDM_ReloadCMailMsg:
\r
4793 Reset(TRUE, TRUE);
\r
4794 ReloadCmailMsgEvent(FALSE);
\r
4797 case IDM_Minimize:
\r
4798 ShowWindow(hwnd, SW_MINIMIZE);
\r
4805 case IDM_MachineWhite:
\r
4806 MachineWhiteEvent();
\r
4808 * refresh the tags dialog only if it's visible
\r
4810 if (gameMode == MachinePlaysWhite && IsWindowVisible(editTagsDialog)) {
\r
4812 tags = PGNTags(&gameInfo);
\r
4813 TagsPopUp(tags, CmailMsg());
\r
4816 SAY("computer starts playing white");
\r
4819 case IDM_MachineBlack:
\r
4820 MachineBlackEvent();
\r
4822 * refresh the tags dialog only if it's visible
\r
4824 if (gameMode == MachinePlaysBlack && IsWindowVisible(editTagsDialog)) {
\r
4826 tags = PGNTags(&gameInfo);
\r
4827 TagsPopUp(tags, CmailMsg());
\r
4830 SAY("computer starts playing black");
\r
4833 case IDM_Match: // [HGM] match: flows into next case, after setting Match Mode and nr of Games
\r
4834 if(gameMode != BeginningOfGame) { // allow menu item to remain enabled for better mode highligting
\r
4835 DisplayError(_("You can only start a match from the initial position."), 0); break;
\r
4837 matchMode = 2;// distinguish from command-line-triggered case (matchMode=1)
\r
4838 appData.matchGames = appData.defaultMatchGames;
\r
4841 case IDM_TwoMachines:
\r
4842 TwoMachinesEvent();
\r
4844 * refresh the tags dialog only if it's visible
\r
4846 if (gameMode == TwoMachinesPlay && IsWindowVisible(editTagsDialog)) {
\r
4848 tags = PGNTags(&gameInfo);
\r
4849 TagsPopUp(tags, CmailMsg());
\r
4852 SAY("computer starts playing both sides");
\r
4855 case IDM_AnalysisMode:
\r
4856 if (!first.analysisSupport) {
\r
4857 snprintf(buf, MSG_SIZ, _("%s does not support analysis"), first.tidy);
\r
4858 DisplayError(buf, 0);
\r
4860 SAY("analyzing current position");
\r
4861 /* [DM] icsEngineAnlyze [HGM] Why is this front-end??? */
\r
4862 if (appData.icsActive) {
\r
4863 if (gameMode != IcsObserving) {
\r
4864 snprintf(buf, MSG_SIZ, "You are not observing a game");
\r
4865 DisplayError(buf, 0);
\r
4866 /* secure check */
\r
4867 if (appData.icsEngineAnalyze) {
\r
4868 if (appData.debugMode)
\r
4869 fprintf(debugFP, "Found unexpected active ICS engine analyze \n");
\r
4870 ExitAnalyzeMode();
\r
4876 /* if enable, user want disable icsEngineAnalyze */
\r
4877 if (appData.icsEngineAnalyze) {
\r
4878 ExitAnalyzeMode();
\r
4882 appData.icsEngineAnalyze = TRUE;
\r
4883 if (appData.debugMode) fprintf(debugFP, "ICS engine analyze starting...\n");
\r
4886 if (!appData.showThinking) ToggleShowThinking();
\r
4887 AnalyzeModeEvent();
\r
4891 case IDM_AnalyzeFile:
\r
4892 if (!first.analysisSupport) {
\r
4893 char buf[MSG_SIZ];
\r
4894 snprintf(buf, MSG_SIZ, _("%s does not support analysis"), first.tidy);
\r
4895 DisplayError(buf, 0);
\r
4897 if (!appData.showThinking) ToggleShowThinking();
\r
4898 AnalyzeFileEvent();
\r
4899 LoadGameDialog(hwnd, _("Analyze Game from File"));
\r
4900 AnalysisPeriodicEvent(1);
\r
4904 case IDM_IcsClient:
\r
4908 case IDM_EditGame:
\r
4909 case IDM_EditGame2:
\r
4914 case IDM_EditPosition:
\r
4915 case IDM_EditPosition2:
\r
4916 EditPositionEvent();
\r
4917 SAY("enter a FEN string or setup a position on the board using the control R pop up menu");
\r
4920 case IDM_Training:
\r
4924 case IDM_ShowGameList:
\r
4925 ShowGameListProc();
\r
4928 case IDM_EditProgs1:
\r
4929 EditTagsPopUp(firstChessProgramNames, &firstChessProgramNames);
\r
4932 case IDM_EditProgs2:
\r
4933 EditTagsPopUp(secondChessProgramNames, &secondChessProgramNames);
\r
4936 case IDM_EditServers:
\r
4937 EditTagsPopUp(icsNames, &icsNames);
\r
4940 case IDM_EditTags:
\r
4945 case IDM_EditComment:
\r
4947 if (commentUp && editComment) {
\r
4950 EditCommentEvent();
\r
4970 case IDM_CallFlag:
\r
4990 case IDM_StopObserving:
\r
4991 StopObservingEvent();
\r
4994 case IDM_StopExamining:
\r
4995 StopExaminingEvent();
\r
4999 UploadGameEvent();
\r
5002 case IDM_TypeInMove:
\r
5003 PopUpMoveDialog('\000');
\r
5006 case IDM_TypeInName:
\r
5007 PopUpNameDialog('\000');
\r
5010 case IDM_Backward:
\r
5012 SetFocus(hwndMain);
\r
5019 SetFocus(hwndMain);
\r
5024 SetFocus(hwndMain);
\r
5029 SetFocus(hwndMain);
\r
5033 RevertEvent(FALSE);
\r
5036 case IDM_Annotate: // [HGM] vari: revert with annotation
\r
5037 RevertEvent(TRUE);
\r
5040 case IDM_TruncateGame:
\r
5041 TruncateGameEvent();
\r
5048 case IDM_RetractMove:
\r
5049 RetractMoveEvent();
\r
5052 case IDM_FlipView:
\r
5053 flipView = !flipView;
\r
5054 DrawPosition(FALSE, NULL);
\r
5057 case IDM_FlipClock:
\r
5058 flipClock = !flipClock;
\r
5059 DisplayBothClocks();
\r
5060 DrawPosition(FALSE, NULL);
\r
5063 case IDM_MuteSounds:
\r
5064 mute = !mute; // [HGM] mute: keep track of global muting variable
\r
5065 CheckMenuItem(GetMenu(hwndMain),IDM_MuteSounds,
\r
5066 MF_BYCOMMAND|(mute?MF_CHECKED:MF_UNCHECKED));
\r
5069 case IDM_GeneralOptions:
\r
5070 GeneralOptionsPopup(hwnd);
\r
5071 DrawPosition(TRUE, NULL);
\r
5074 case IDM_BoardOptions:
\r
5075 BoardOptionsPopup(hwnd);
\r
5078 case IDM_EnginePlayOptions:
\r
5079 EnginePlayOptionsPopup(hwnd);
\r
5082 case IDM_Engine1Options:
\r
5083 EngineOptionsPopup(hwnd, &first);
\r
5086 case IDM_Engine2Options:
\r
5088 if(WaitForSecond(SettingsMenuIfReady)) break;
\r
5089 EngineOptionsPopup(hwnd, &second);
\r
5092 case IDM_OptionsUCI:
\r
5093 UciOptionsPopup(hwnd);
\r
5096 case IDM_IcsOptions:
\r
5097 IcsOptionsPopup(hwnd);
\r
5101 FontsOptionsPopup(hwnd);
\r
5105 SoundOptionsPopup(hwnd);
\r
5108 case IDM_CommPort:
\r
5109 CommPortOptionsPopup(hwnd);
\r
5112 case IDM_LoadOptions:
\r
5113 LoadOptionsPopup(hwnd);
\r
5116 case IDM_SaveOptions:
\r
5117 SaveOptionsPopup(hwnd);
\r
5120 case IDM_TimeControl:
\r
5121 TimeControlOptionsPopup(hwnd);
\r
5124 case IDM_SaveSettings:
\r
5125 SaveSettings(settingsFileName);
\r
5128 case IDM_SaveSettingsOnExit:
\r
5129 saveSettingsOnExit = !saveSettingsOnExit;
\r
5130 (void) CheckMenuItem(GetMenu(hwndMain), IDM_SaveSettingsOnExit,
\r
5131 MF_BYCOMMAND|(saveSettingsOnExit ?
\r
5132 MF_CHECKED : MF_UNCHECKED));
\r
5143 case IDM_AboutGame:
\r
5148 appData.debugMode = !appData.debugMode;
\r
5149 if (appData.debugMode) {
\r
5150 char dir[MSG_SIZ];
\r
5151 GetCurrentDirectory(MSG_SIZ, dir);
\r
5152 SetCurrentDirectory(installDir);
\r
5153 debugFP = fopen(appData.nameOfDebugFile, "w");
\r
5154 SetCurrentDirectory(dir);
\r
5155 setbuf(debugFP, NULL);
\r
5162 case IDM_HELPCONTENTS:
\r
5163 if (!MyHelp (hwnd, "winboard.hlp", HELP_KEY,(DWORD)(LPSTR)"CONTENTS") &&
\r
5164 !HtmlHelp(hwnd, "winboard.chm", 0, 0) ) {
\r
5165 MessageBox (GetFocus(),
\r
5166 _("Unable to activate help"),
\r
5167 szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);
\r
5171 case IDM_HELPSEARCH:
\r
5172 if (!MyHelp (hwnd, "winboard.hlp", HELP_PARTIALKEY, (DWORD)(LPSTR)"") &&
\r
5173 !HtmlHelp(hwnd, "winboard.chm", 0, 0) ) {
\r
5174 MessageBox (GetFocus(),
\r
5175 _("Unable to activate help"),
\r
5176 szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);
\r
5180 case IDM_HELPHELP:
\r
5181 if(!WinHelp(hwnd, (LPSTR)NULL, HELP_HELPONHELP, 0)) {
\r
5182 MessageBox (GetFocus(),
\r
5183 _("Unable to activate help"),
\r
5184 szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);
\r
5189 lpProc = MakeProcInstance((FARPROC)About, hInst);
\r
5191 (gameInfo.event && strcmp(gameInfo.event, "Easter Egg Hunt") == 0) ?
\r
5192 "AboutBox2" : "AboutBox", hwnd, (DLGPROC)lpProc);
\r
5193 FreeProcInstance(lpProc);
\r
5196 case IDM_DirectCommand1:
\r
5197 AskQuestionEvent(_("Direct Command"),
\r
5198 _("Send to chess program:"), "", "1");
\r
5200 case IDM_DirectCommand2:
\r
5201 AskQuestionEvent(_("Direct Command"),
\r
5202 _("Send to second chess program:"), "", "2");
\r
5205 case EP_WhitePawn:
\r
5206 EditPositionMenuEvent(WhitePawn, fromX, fromY);
\r
5207 fromX = fromY = -1;
\r
5210 case EP_WhiteKnight:
\r
5211 EditPositionMenuEvent(WhiteKnight, fromX, fromY);
\r
5212 fromX = fromY = -1;
\r
5215 case EP_WhiteBishop:
\r
5216 EditPositionMenuEvent(WhiteBishop, fromX, fromY);
\r
5217 fromX = fromY = -1;
\r
5220 case EP_WhiteRook:
\r
5221 EditPositionMenuEvent(WhiteRook, fromX, fromY);
\r
5222 fromX = fromY = -1;
\r
5225 case EP_WhiteQueen:
\r
5226 EditPositionMenuEvent(WhiteQueen, fromX, fromY);
\r
5227 fromX = fromY = -1;
\r
5230 case EP_WhiteFerz:
\r
5231 EditPositionMenuEvent(WhiteFerz, fromX, fromY);
\r
5232 fromX = fromY = -1;
\r
5235 case EP_WhiteWazir:
\r
5236 EditPositionMenuEvent(WhiteWazir, fromX, fromY);
\r
5237 fromX = fromY = -1;
\r
5240 case EP_WhiteAlfil:
\r
5241 EditPositionMenuEvent(WhiteAlfil, fromX, fromY);
\r
5242 fromX = fromY = -1;
\r
5245 case EP_WhiteCannon:
\r
5246 EditPositionMenuEvent(WhiteCannon, fromX, fromY);
\r
5247 fromX = fromY = -1;
\r
5250 case EP_WhiteCardinal:
\r
5251 EditPositionMenuEvent(WhiteAngel, fromX, fromY);
\r
5252 fromX = fromY = -1;
\r
5255 case EP_WhiteMarshall:
\r
5256 EditPositionMenuEvent(WhiteMarshall, fromX, fromY);
\r
5257 fromX = fromY = -1;
\r
5260 case EP_WhiteKing:
\r
5261 EditPositionMenuEvent(WhiteKing, fromX, fromY);
\r
5262 fromX = fromY = -1;
\r
5265 case EP_BlackPawn:
\r
5266 EditPositionMenuEvent(BlackPawn, fromX, fromY);
\r
5267 fromX = fromY = -1;
\r
5270 case EP_BlackKnight:
\r
5271 EditPositionMenuEvent(BlackKnight, fromX, fromY);
\r
5272 fromX = fromY = -1;
\r
5275 case EP_BlackBishop:
\r
5276 EditPositionMenuEvent(BlackBishop, fromX, fromY);
\r
5277 fromX = fromY = -1;
\r
5280 case EP_BlackRook:
\r
5281 EditPositionMenuEvent(BlackRook, fromX, fromY);
\r
5282 fromX = fromY = -1;
\r
5285 case EP_BlackQueen:
\r
5286 EditPositionMenuEvent(BlackQueen, fromX, fromY);
\r
5287 fromX = fromY = -1;
\r
5290 case EP_BlackFerz:
\r
5291 EditPositionMenuEvent(BlackFerz, fromX, fromY);
\r
5292 fromX = fromY = -1;
\r
5295 case EP_BlackWazir:
\r
5296 EditPositionMenuEvent(BlackWazir, fromX, fromY);
\r
5297 fromX = fromY = -1;
\r
5300 case EP_BlackAlfil:
\r
5301 EditPositionMenuEvent(BlackAlfil, fromX, fromY);
\r
5302 fromX = fromY = -1;
\r
5305 case EP_BlackCannon:
\r
5306 EditPositionMenuEvent(BlackCannon, fromX, fromY);
\r
5307 fromX = fromY = -1;
\r
5310 case EP_BlackCardinal:
\r
5311 EditPositionMenuEvent(BlackAngel, fromX, fromY);
\r
5312 fromX = fromY = -1;
\r
5315 case EP_BlackMarshall:
\r
5316 EditPositionMenuEvent(BlackMarshall, fromX, fromY);
\r
5317 fromX = fromY = -1;
\r
5320 case EP_BlackKing:
\r
5321 EditPositionMenuEvent(BlackKing, fromX, fromY);
\r
5322 fromX = fromY = -1;
\r
5325 case EP_EmptySquare:
\r
5326 EditPositionMenuEvent(EmptySquare, fromX, fromY);
\r
5327 fromX = fromY = -1;
\r
5330 case EP_ClearBoard:
\r
5331 EditPositionMenuEvent(ClearBoard, fromX, fromY);
\r
5332 fromX = fromY = -1;
\r
5336 EditPositionMenuEvent(WhitePlay, fromX, fromY);
\r
5337 fromX = fromY = -1;
\r
5341 EditPositionMenuEvent(BlackPlay, fromX, fromY);
\r
5342 fromX = fromY = -1;
\r
5346 EditPositionMenuEvent(PromotePiece, fromX, fromY);
\r
5347 fromX = fromY = -1;
\r
5351 EditPositionMenuEvent(DemotePiece, fromX, fromY);
\r
5352 fromX = fromY = -1;
\r
5356 DropMenuEvent(WhitePawn, fromX, fromY);
\r
5357 fromX = fromY = -1;
\r
5361 DropMenuEvent(WhiteKnight, fromX, fromY);
\r
5362 fromX = fromY = -1;
\r
5366 DropMenuEvent(WhiteBishop, fromX, fromY);
\r
5367 fromX = fromY = -1;
\r
5371 DropMenuEvent(WhiteRook, fromX, fromY);
\r
5372 fromX = fromY = -1;
\r
5376 DropMenuEvent(WhiteQueen, fromX, fromY);
\r
5377 fromX = fromY = -1;
\r
5382 TranslateMenus(0);
\r
5383 CheckMenuItem(GetMenu(hwndMain), lastChecked, MF_BYCOMMAND|MF_UNCHECKED);
\r
5384 CheckMenuItem(GetMenu(hwndMain), IDM_English, MF_BYCOMMAND|MF_CHECKED);
\r
5385 lastChecked = wmId;
\r
5389 if(wmId > IDM_English && wmId < IDM_English+5) {
\r
5390 LoadLanguageFile(languageFile[wmId - IDM_English - 1]);
\r
5391 TranslateMenus(0);
\r
5392 CheckMenuItem(GetMenu(hwndMain), lastChecked, MF_BYCOMMAND|MF_UNCHECKED);
\r
5393 CheckMenuItem(GetMenu(hwndMain), wmId, MF_BYCOMMAND|MF_CHECKED);
\r
5394 lastChecked = wmId;
\r
5397 return (DefWindowProc(hwnd, message, wParam, lParam));
\r
5403 case CLOCK_TIMER_ID:
\r
5404 KillTimer(hwnd, clockTimerEvent); /* Simulate one-shot timer as in X */
\r
5405 clockTimerEvent = 0;
\r
5406 DecrementClocks(); /* call into back end */
\r
5408 case LOAD_GAME_TIMER_ID:
\r
5409 KillTimer(hwnd, loadGameTimerEvent); /* Simulate one-shot timer as in X*/
\r
5410 loadGameTimerEvent = 0;
\r
5411 AutoPlayGameLoop(); /* call into back end */
\r
5413 case ANALYSIS_TIMER_ID:
\r
5414 if ((gameMode == AnalyzeMode || gameMode == AnalyzeFile
\r
5415 || appData.icsEngineAnalyze) && appData.periodicUpdates) {
\r
5416 AnalysisPeriodicEvent(0);
\r
5418 KillTimer(hwnd, analysisTimerEvent);
\r
5419 analysisTimerEvent = 0;
\r
5422 case DELAYED_TIMER_ID:
\r
5423 KillTimer(hwnd, delayedTimerEvent);
\r
5424 delayedTimerEvent = 0;
\r
5425 delayedTimerCallback();
\r
5430 case WM_USER_Input:
\r
5431 InputEvent(hwnd, message, wParam, lParam);
\r
5434 /* [AS] Also move "attached" child windows */
\r
5435 case WM_WINDOWPOSCHANGING:
\r
5437 if( hwnd == hwndMain && appData.useStickyWindows ) {
\r
5438 LPWINDOWPOS lpwp = (LPWINDOWPOS) lParam;
\r
5440 if( ((lpwp->flags & SWP_NOMOVE) == 0) && ((lpwp->flags & SWP_NOSIZE) != 0) ) {
\r
5441 /* Window is moving */
\r
5444 // GetWindowRect( hwnd, &rcMain ); //[HGM] sticky: in XP this returned new position, not old
\r
5445 rcMain.left = wpMain.x; // replace by these 4 lines to reconstruct old rect
\r
5446 rcMain.right = wpMain.x + wpMain.width;
\r
5447 rcMain.top = wpMain.y;
\r
5448 rcMain.bottom = wpMain.y + wpMain.height;
\r
5450 ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, moveHistoryDialog, &wpMoveHistory );
\r
5451 ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, evalGraphDialog, &wpEvalGraph );
\r
5452 ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, engineOutputDialog, &wpEngineOutput );
\r
5453 ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, gameListDialog, &wpGameList );
\r
5454 ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, hwndConsole, &wpConsole );
\r
5455 wpMain.x = lpwp->x;
\r
5456 wpMain.y = lpwp->y;
\r
5461 /* [AS] Snapping */
\r
5462 case WM_ENTERSIZEMOVE:
\r
5463 if(appData.debugMode) { fprintf(debugFP, "size-move\n"); }
\r
5464 if (hwnd == hwndMain) {
\r
5465 doingSizing = TRUE;
\r
5468 return OnEnterSizeMove( &sd, hwnd, wParam, lParam );
\r
5472 if(appData.debugMode) { fprintf(debugFP, "sizing\n"); }
\r
5473 if (hwnd == hwndMain) {
\r
5474 lastSizing = wParam;
\r
5479 if(appData.debugMode) { fprintf(debugFP, "moving\n"); }
\r
5480 return OnMoving( &sd, hwnd, wParam, lParam );
\r
5482 case WM_EXITSIZEMOVE:
\r
5483 if(appData.debugMode) { fprintf(debugFP, "exit size-move, size = %d\n", squareSize); }
\r
5484 if (hwnd == hwndMain) {
\r
5486 doingSizing = FALSE;
\r
5487 InvalidateRect(hwnd, &boardRect, FALSE);
\r
5488 GetClientRect(hwnd, &client);
\r
5489 ResizeBoard(client.right, client.bottom, lastSizing);
\r
5491 if(appData.debugMode) { fprintf(debugFP, "square size = %d\n", squareSize); }
\r
5493 return OnExitSizeMove( &sd, hwnd, wParam, lParam );
\r
5496 case WM_DESTROY: /* message: window being destroyed */
\r
5497 PostQuitMessage(0);
\r
5501 if (hwnd == hwndMain) {
\r
5506 default: /* Passes it on if unprocessed */
\r
5507 return (DefWindowProc(hwnd, message, wParam, lParam));
\r
5512 /*---------------------------------------------------------------------------*\
\r
5514 * Misc utility routines
\r
5516 \*---------------------------------------------------------------------------*/
\r
5519 * Decent random number generator, at least not as bad as Windows
\r
5520 * standard rand, which returns a value in the range 0 to 0x7fff.
\r
5522 unsigned int randstate;
\r
5527 randstate = randstate * 1664525 + 1013904223;
\r
5528 return (int) randstate & 0x7fffffff;
\r
5532 mysrandom(unsigned int seed)
\r
5539 * returns TRUE if user selects a different color, FALSE otherwise
\r
5543 ChangeColor(HWND hwnd, COLORREF *which)
\r
5545 static BOOL firstTime = TRUE;
\r
5546 static DWORD customColors[16];
\r
5548 COLORREF newcolor;
\r
5553 /* Make initial colors in use available as custom colors */
\r
5554 /* Should we put the compiled-in defaults here instead? */
\r
5556 customColors[i++] = lightSquareColor & 0xffffff;
\r
5557 customColors[i++] = darkSquareColor & 0xffffff;
\r
5558 customColors[i++] = whitePieceColor & 0xffffff;
\r
5559 customColors[i++] = blackPieceColor & 0xffffff;
\r
5560 customColors[i++] = highlightSquareColor & 0xffffff;
\r
5561 customColors[i++] = premoveHighlightColor & 0xffffff;
\r
5563 for (ccl = (ColorClass) 0; ccl < NColorClasses && i < 16; ccl++) {
\r
5564 customColors[i++] = textAttribs[ccl].color;
\r
5566 while (i < 16) customColors[i++] = RGB(255, 255, 255);
\r
5567 firstTime = FALSE;
\r
5570 cc.lStructSize = sizeof(cc);
\r
5571 cc.hwndOwner = hwnd;
\r
5572 cc.hInstance = NULL;
\r
5573 cc.rgbResult = (DWORD) (*which & 0xffffff);
\r
5574 cc.lpCustColors = (LPDWORD) customColors;
\r
5575 cc.Flags = CC_RGBINIT|CC_FULLOPEN;
\r
5577 if (!ChooseColor(&cc)) return FALSE;
\r
5579 newcolor = (COLORREF) (0x2000000 | cc.rgbResult);
\r
5580 if (newcolor == *which) return FALSE;
\r
5581 *which = newcolor;
\r
5585 InitDrawingColors();
\r
5586 InvalidateRect(hwnd, &boardRect, FALSE);
\r
5591 MyLoadSound(MySound *ms)
\r
5597 if (ms->data) free(ms->data);
\r
5600 switch (ms->name[0]) {
\r
5606 /* System sound from Control Panel. Don't preload here. */
\r
5610 if (ms->name[1] == NULLCHAR) {
\r
5611 /* "!" alone = silence */
\r
5614 /* Builtin wave resource. Error if not found. */
\r
5615 HANDLE h = FindResource(hInst, ms->name + 1, "WAVE");
\r
5616 if (h == NULL) break;
\r
5617 ms->data = (void *)LoadResource(hInst, h);
\r
5618 if (h == NULL) break;
\r
5623 /* .wav file. Error if not found. */
\r
5624 f = fopen(ms->name, "rb");
\r
5625 if (f == NULL) break;
\r
5626 if (fstat(fileno(f), &st) < 0) break;
\r
5627 ms->data = malloc(st.st_size);
\r
5628 if (fread(ms->data, st.st_size, 1, f) < 1) break;
\r
5634 char buf[MSG_SIZ];
\r
5635 snprintf(buf, MSG_SIZ, _("Error loading sound %s"), ms->name);
\r
5636 DisplayError(buf, GetLastError());
\r
5642 MyPlaySound(MySound *ms)
\r
5644 BOOLEAN ok = FALSE;
\r
5646 if(mute) return TRUE; // [HGM] mute: suppress all sound play when muted
\r
5647 switch (ms->name[0]) {
\r
5649 if(appData.debugMode) fprintf(debugFP, "silence\n");
\r
5654 /* System sound from Control Panel (deprecated feature).
\r
5655 "$" alone or an unset sound name gets default beep (still in use). */
\r
5656 if (ms->name[1]) {
\r
5657 ok = PlaySound(ms->name + 1, NULL, SND_ALIAS|SND_ASYNC);
\r
5659 if (!ok) ok = MessageBeep(MB_OK);
\r
5662 /* Builtin wave resource, or "!" alone for silence */
\r
5663 if (ms->name[1]) {
\r
5664 if (ms->data == NULL) return FALSE;
\r
5665 ok = PlaySound(ms->data, NULL, SND_MEMORY|SND_ASYNC);
\r
5671 /* .wav file. Error if not found. */
\r
5672 if (ms->data == NULL) return FALSE;
\r
5673 ok = PlaySound(ms->data, NULL, SND_MEMORY|SND_ASYNC);
\r
5676 /* Don't print an error: this can happen innocently if the sound driver
\r
5677 is busy; for instance, if another instance of WinBoard is playing
\r
5678 a sound at about the same time. */
\r
5684 OldOpenFileHook(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
5687 OPENFILENAME *ofn;
\r
5688 static UINT *number; /* gross that this is static */
\r
5690 switch (message) {
\r
5691 case WM_INITDIALOG: /* message: initialize dialog box */
\r
5692 /* Center the dialog over the application window */
\r
5693 ofn = (OPENFILENAME *) lParam;
\r
5694 if (ofn->Flags & OFN_ENABLETEMPLATE) {
\r
5695 number = (UINT *) ofn->lCustData;
\r
5696 SendMessage(GetDlgItem(hDlg, edt2), WM_SETTEXT, 0, (LPARAM) "");
\r
5700 CenterWindow (hDlg, GetWindow (hDlg, GW_OWNER));
\r
5701 Translate(hDlg, 1536);
\r
5702 return FALSE; /* Allow for further processing */
\r
5705 if ((LOWORD(wParam) == IDOK) && (number != NULL)) {
\r
5706 *number = GetDlgItemInt(hDlg, OPT_IndexNumberOld, &ok, FALSE);
\r
5708 return FALSE; /* Allow for further processing */
\r
5714 OpenFileHook(HWND hdlg, UINT uiMsg, WPARAM wParam, LPARAM lParam)
\r
5716 static UINT *number;
\r
5717 OPENFILENAME *ofname;
\r
5720 case WM_INITDIALOG:
\r
5721 Translate(hdlg, DLG_IndexNumber);
\r
5722 ofname = (OPENFILENAME *)lParam;
\r
5723 number = (UINT *)(ofname->lCustData);
\r
5726 ofnot = (OFNOTIFY *)lParam;
\r
5727 if (ofnot->hdr.code == CDN_FILEOK) {
\r
5728 *number = GetDlgItemInt(hdlg, OPT_IndexNumber, NULL, FALSE);
\r
5737 OpenFileDialog(HWND hwnd, char *write, char *defName, char *defExt, // [HGM] diag: type of 'write' now string
\r
5738 char *nameFilt, char *dlgTitle, UINT *number,
\r
5739 char fileTitle[MSG_SIZ], char fileName[MSG_SIZ])
\r
5741 OPENFILENAME openFileName;
\r
5742 char buf1[MSG_SIZ];
\r
5745 if (fileName == NULL) fileName = buf1;
\r
5746 if (defName == NULL) {
\r
5747 safeStrCpy(fileName, "*.", 3 );
\r
5748 strcat(fileName, defExt);
\r
5750 safeStrCpy(fileName, defName, MSG_SIZ );
\r
5752 if (fileTitle) safeStrCpy(fileTitle, "", MSG_SIZ );
\r
5753 if (number) *number = 0;
\r
5755 openFileName.lStructSize = sizeof(OPENFILENAME);
\r
5756 openFileName.hwndOwner = hwnd;
\r
5757 openFileName.hInstance = (HANDLE) hInst;
\r
5758 openFileName.lpstrFilter = nameFilt;
\r
5759 openFileName.lpstrCustomFilter = (LPSTR) NULL;
\r
5760 openFileName.nMaxCustFilter = 0L;
\r
5761 openFileName.nFilterIndex = 1L;
\r
5762 openFileName.lpstrFile = fileName;
\r
5763 openFileName.nMaxFile = MSG_SIZ;
\r
5764 openFileName.lpstrFileTitle = fileTitle;
\r
5765 openFileName.nMaxFileTitle = fileTitle ? MSG_SIZ : 0;
\r
5766 openFileName.lpstrInitialDir = NULL;
\r
5767 openFileName.lpstrTitle = dlgTitle;
\r
5768 openFileName.Flags = OFN_PATHMUSTEXIST | OFN_HIDEREADONLY
\r
5769 | (write[0] != 'r' ? 0 : OFN_FILEMUSTEXIST)
\r
5770 | (number ? OFN_ENABLETEMPLATE | OFN_ENABLEHOOK: 0)
\r
5771 | (oldDialog ? 0 : OFN_EXPLORER);
\r
5772 openFileName.nFileOffset = 0;
\r
5773 openFileName.nFileExtension = 0;
\r
5774 openFileName.lpstrDefExt = defExt;
\r
5775 openFileName.lCustData = (LONG) number;
\r
5776 openFileName.lpfnHook = oldDialog ?
\r
5777 (LPOFNHOOKPROC) OldOpenFileHook : (LPOFNHOOKPROC) OpenFileHook;
\r
5778 openFileName.lpTemplateName = (LPSTR)(oldDialog ? 1536 : DLG_IndexNumber);
\r
5780 if (write[0] != 'r' ? GetSaveFileName(&openFileName) :
\r
5781 GetOpenFileName(&openFileName)) {
\r
5782 /* open the file */
\r
5783 f = fopen(openFileName.lpstrFile, write);
\r
5785 MessageBox(hwnd, _("File open failed"), NULL,
\r
5786 MB_OK|MB_ICONEXCLAMATION);
\r
5790 int err = CommDlgExtendedError();
\r
5791 if (err != 0) DisplayError(_("Internal error in file dialog box"), err);
\r
5800 MenuPopup(HWND hwnd, POINT pt, HMENU hmenu, UINT def)
\r
5802 HMENU hmenuTrackPopup; /* floating pop-up menu */
\r
5805 * Get the first pop-up menu in the menu template. This is the
\r
5806 * menu that TrackPopupMenu displays.
\r
5808 hmenuTrackPopup = GetSubMenu(hmenu, 0);
\r
5810 SetMenuDefaultItem(hmenuTrackPopup, def, FALSE);
\r
5813 * TrackPopup uses screen coordinates, so convert the
\r
5814 * coordinates of the mouse click to screen coordinates.
\r
5816 ClientToScreen(hwnd, (LPPOINT) &pt);
\r
5818 /* Draw and track the floating pop-up menu. */
\r
5819 TrackPopupMenu(hmenuTrackPopup, TPM_CENTERALIGN | TPM_RIGHTBUTTON,
\r
5820 pt.x, pt.y, 0, hwnd, NULL);
\r
5822 /* Destroy the menu.*/
\r
5823 DestroyMenu(hmenu);
\r
5828 int sizeX, sizeY, newSizeX, newSizeY;
\r
5830 } ResizeEditPlusButtonsClosure;
\r
5833 ResizeEditPlusButtonsCallback(HWND hChild, LPARAM lparam)
\r
5835 ResizeEditPlusButtonsClosure *cl = (ResizeEditPlusButtonsClosure *)lparam;
\r
5839 if (hChild == cl->hText) return TRUE;
\r
5840 GetWindowRect(hChild, &rect); /* gives screen coords */
\r
5841 pt.x = rect.left + (cl->newSizeX - cl->sizeX)/2;
\r
5842 pt.y = rect.top + cl->newSizeY - cl->sizeY;
\r
5843 ScreenToClient(cl->hDlg, &pt);
\r
5844 cl->hdwp = DeferWindowPos(cl->hdwp, hChild, NULL,
\r
5845 pt.x, pt.y, rect.right - rect.left, rect.bottom - rect.top, SWP_NOZORDER);
\r
5849 /* Resize a dialog that has a (rich) edit field filling most of
\r
5850 the top, with a row of buttons below */
\r
5852 ResizeEditPlusButtons(HWND hDlg, HWND hText, int sizeX, int sizeY, int newSizeX, int newSizeY)
\r
5855 int newTextHeight, newTextWidth;
\r
5856 ResizeEditPlusButtonsClosure cl;
\r
5858 /*if (IsIconic(hDlg)) return;*/
\r
5859 if (newSizeX == sizeX && newSizeY == sizeY) return;
\r
5861 cl.hdwp = BeginDeferWindowPos(8);
\r
5863 GetWindowRect(hText, &rectText); /* gives screen coords */
\r
5864 newTextWidth = rectText.right - rectText.left + newSizeX - sizeX;
\r
5865 newTextHeight = rectText.bottom - rectText.top + newSizeY - sizeY;
\r
5866 if (newTextHeight < 0) {
\r
5867 newSizeY += -newTextHeight;
\r
5868 newTextHeight = 0;
\r
5870 cl.hdwp = DeferWindowPos(cl.hdwp, hText, NULL, 0, 0,
\r
5871 newTextWidth, newTextHeight, SWP_NOZORDER|SWP_NOMOVE);
\r
5877 cl.newSizeX = newSizeX;
\r
5878 cl.newSizeY = newSizeY;
\r
5879 EnumChildWindows(hDlg, ResizeEditPlusButtonsCallback, (LPARAM)&cl);
\r
5881 EndDeferWindowPos(cl.hdwp);
\r
5884 BOOL CenterWindowEx(HWND hwndChild, HWND hwndParent, int mode)
\r
5886 RECT rChild, rParent;
\r
5887 int wChild, hChild, wParent, hParent;
\r
5888 int wScreen, hScreen, xNew, yNew;
\r
5891 /* Get the Height and Width of the child window */
\r
5892 GetWindowRect (hwndChild, &rChild);
\r
5893 wChild = rChild.right - rChild.left;
\r
5894 hChild = rChild.bottom - rChild.top;
\r
5896 /* Get the Height and Width of the parent window */
\r
5897 GetWindowRect (hwndParent, &rParent);
\r
5898 wParent = rParent.right - rParent.left;
\r
5899 hParent = rParent.bottom - rParent.top;
\r
5901 /* Get the display limits */
\r
5902 hdc = GetDC (hwndChild);
\r
5903 wScreen = GetDeviceCaps (hdc, HORZRES);
\r
5904 hScreen = GetDeviceCaps (hdc, VERTRES);
\r
5905 ReleaseDC(hwndChild, hdc);
\r
5907 /* Calculate new X position, then adjust for screen */
\r
5908 xNew = rParent.left + ((wParent - wChild) /2);
\r
5911 } else if ((xNew+wChild) > wScreen) {
\r
5912 xNew = wScreen - wChild;
\r
5915 /* Calculate new Y position, then adjust for screen */
\r
5917 yNew = rParent.top + ((hParent - hChild) /2);
\r
5920 yNew = rParent.top + GetSystemMetrics( SM_CYCAPTION ) * 2 / 3;
\r
5925 } else if ((yNew+hChild) > hScreen) {
\r
5926 yNew = hScreen - hChild;
\r
5929 /* Set it, and return */
\r
5930 return SetWindowPos (hwndChild, NULL,
\r
5931 xNew, yNew, 0, 0, SWP_NOSIZE | SWP_NOZORDER);
\r
5934 /* Center one window over another */
\r
5935 BOOL CenterWindow (HWND hwndChild, HWND hwndParent)
\r
5937 return CenterWindowEx( hwndChild, hwndParent, 0 );
\r
5940 /*---------------------------------------------------------------------------*\
\r
5942 * Startup Dialog functions
\r
5944 \*---------------------------------------------------------------------------*/
\r
5946 InitComboStrings(HANDLE hwndCombo, char **cd)
\r
5948 SendMessage(hwndCombo, CB_RESETCONTENT, 0, 0);
\r
5950 while (*cd != NULL) {
\r
5951 SendMessage(hwndCombo, CB_ADDSTRING, 0, (LPARAM) T_(*cd));
\r
5957 InitComboStringsFromOption(HANDLE hwndCombo, char *str)
\r
5959 char buf1[MAX_ARG_LEN];
\r
5962 if (str[0] == '@') {
\r
5963 FILE* f = fopen(str + 1, "r");
\r
5965 DisplayFatalError(str + 1, errno, 2);
\r
5968 len = fread(buf1, 1, sizeof(buf1)-1, f);
\r
5970 buf1[len] = NULLCHAR;
\r
5974 SendMessage(hwndCombo, CB_RESETCONTENT, 0, 0);
\r
5977 char buf[MSG_SIZ];
\r
5978 char *end = strchr(str, '\n');
\r
5979 if (end == NULL) return;
\r
5980 memcpy(buf, str, end - str);
\r
5981 buf[end - str] = NULLCHAR;
\r
5982 SendMessage(hwndCombo, CB_ADDSTRING, 0, (LPARAM) buf);
\r
5988 SetStartupDialogEnables(HWND hDlg)
\r
5990 EnableWindow(GetDlgItem(hDlg, OPT_ChessEngineName),
\r
5991 IsDlgButtonChecked(hDlg, OPT_ChessEngine) ||
\r
5992 (appData.zippyPlay && IsDlgButtonChecked(hDlg, OPT_ChessServer)));
\r
5993 EnableWindow(GetDlgItem(hDlg, OPT_SecondChessEngineName),
\r
5994 IsDlgButtonChecked(hDlg, OPT_ChessEngine));
\r
5995 EnableWindow(GetDlgItem(hDlg, OPT_ChessServerName),
\r
5996 IsDlgButtonChecked(hDlg, OPT_ChessServer));
\r
5997 EnableWindow(GetDlgItem(hDlg, OPT_AdditionalOptions),
\r
5998 IsDlgButtonChecked(hDlg, OPT_AnyAdditional));
\r
5999 EnableWindow(GetDlgItem(hDlg, IDOK),
\r
6000 IsDlgButtonChecked(hDlg, OPT_ChessEngine) ||
\r
6001 IsDlgButtonChecked(hDlg, OPT_ChessServer) ||
\r
6002 IsDlgButtonChecked(hDlg, OPT_View));
\r
6006 QuoteForFilename(char *filename)
\r
6008 int dquote, space;
\r
6009 dquote = strchr(filename, '"') != NULL;
\r
6010 space = strchr(filename, ' ') != NULL;
\r
6011 if (dquote || space) {
\r
6023 InitEngineBox(HWND hDlg, HWND hwndCombo, char* nthcp, char* nthd, char* nthdir, char *nthnames)
\r
6025 char buf[MSG_SIZ];
\r
6028 InitComboStringsFromOption(hwndCombo, nthnames);
\r
6029 q = QuoteForFilename(nthcp);
\r
6030 snprintf(buf, MSG_SIZ, "%s%s%s", q, nthcp, q);
\r
6031 if (*nthdir != NULLCHAR) {
\r
6032 q = QuoteForFilename(nthdir);
\r
6033 snprintf(buf + strlen(buf), MSG_SIZ, " /%s=%s%s%s", nthd, q, nthdir, q);
\r
6035 if (*nthcp == NULLCHAR) {
\r
6036 SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) 0, (LPARAM) 0);
\r
6037 } else if (SendMessage(hwndCombo, CB_SELECTSTRING, (WPARAM) -1, (LPARAM) buf) == CB_ERR) {
\r
6038 SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) -1, (LPARAM) 0);
\r
6039 SendMessage(hwndCombo, WM_SETTEXT, (WPARAM) 0, (LPARAM) buf);
\r
6044 StartupDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
6046 char buf[MSG_SIZ];
\r
6050 switch (message) {
\r
6051 case WM_INITDIALOG:
\r
6052 /* Center the dialog */
\r
6053 CenterWindow (hDlg, GetDesktopWindow());
\r
6054 Translate(hDlg, DLG_Startup);
\r
6055 /* Initialize the dialog items */
\r
6056 InitEngineBox(hDlg, GetDlgItem(hDlg, OPT_ChessEngineName),
\r
6057 appData.firstChessProgram, "fd", appData.firstDirectory,
\r
6058 firstChessProgramNames);
\r
6059 InitEngineBox(hDlg, GetDlgItem(hDlg, OPT_SecondChessEngineName),
\r
6060 appData.secondChessProgram, "sd", appData.secondDirectory,
\r
6061 secondChessProgramNames);
\r
6062 hwndCombo = GetDlgItem(hDlg, OPT_ChessServerName);
\r
6063 InitComboStringsFromOption(hwndCombo, icsNames);
\r
6064 snprintf(buf, MSG_SIZ, "%s /icsport=%s", appData.icsHost, appData.icsPort);
\r
6065 if (*appData.icsHelper != NULLCHAR) {
\r
6066 char *q = QuoteForFilename(appData.icsHelper);
\r
6067 sprintf(buf + strlen(buf), " /icshelper=%s%s%s", q, appData.icsHelper, q);
\r
6069 if (*appData.icsHost == NULLCHAR) {
\r
6070 SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) 0, (LPARAM) 0);
\r
6071 /*SendMessage(hwndCombo, CB_SHOWDROPDOWN, (WPARAM) TRUE, (LPARAM) 0); !!too soon */
\r
6072 } else if (SendMessage(hwndCombo, CB_SELECTSTRING, (WPARAM) -1, (LPARAM) buf) == CB_ERR) {
\r
6073 SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) -1, (LPARAM) 0);
\r
6074 SendMessage(hwndCombo, WM_SETTEXT, (WPARAM) 0, (LPARAM) buf);
\r
6077 if (appData.icsActive) {
\r
6078 CheckDlgButton(hDlg, OPT_ChessServer, BST_CHECKED);
\r
6080 else if (appData.noChessProgram) {
\r
6081 CheckDlgButton(hDlg, OPT_View, BST_CHECKED);
\r
6084 CheckDlgButton(hDlg, OPT_ChessEngine, BST_CHECKED);
\r
6087 SetStartupDialogEnables(hDlg);
\r
6091 switch (LOWORD(wParam)) {
\r
6093 if (IsDlgButtonChecked(hDlg, OPT_ChessEngine)) {
\r
6094 safeStrCpy(buf, "/fcp=", sizeof(buf)/sizeof(buf[0]) );
\r
6095 GetDlgItemText(hDlg, OPT_ChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));
\r
6097 ParseArgs(StringGet, &p);
\r
6098 safeStrCpy(buf, "/scp=", sizeof(buf)/sizeof(buf[0]) );
\r
6099 GetDlgItemText(hDlg, OPT_SecondChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));
\r
6101 ParseArgs(StringGet, &p);
\r
6102 appData.noChessProgram = FALSE;
\r
6103 appData.icsActive = FALSE;
\r
6104 } else if (IsDlgButtonChecked(hDlg, OPT_ChessServer)) {
\r
6105 safeStrCpy(buf, "/ics /icshost=", sizeof(buf)/sizeof(buf[0]) );
\r
6106 GetDlgItemText(hDlg, OPT_ChessServerName, buf + strlen(buf), sizeof(buf) - strlen(buf));
\r
6108 ParseArgs(StringGet, &p);
\r
6109 if (appData.zippyPlay) {
\r
6110 safeStrCpy(buf, "/fcp=", sizeof(buf)/sizeof(buf[0]) );
\r
6111 GetDlgItemText(hDlg, OPT_ChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));
\r
6113 ParseArgs(StringGet, &p);
\r
6115 } else if (IsDlgButtonChecked(hDlg, OPT_View)) {
\r
6116 appData.noChessProgram = TRUE;
\r
6117 appData.icsActive = FALSE;
\r
6119 MessageBox(hDlg, _("Choose an option, or cancel to exit"),
\r
6120 _("Option Error"), MB_OK|MB_ICONEXCLAMATION);
\r
6123 if (IsDlgButtonChecked(hDlg, OPT_AnyAdditional)) {
\r
6124 GetDlgItemText(hDlg, OPT_AdditionalOptions, buf, sizeof(buf));
\r
6126 ParseArgs(StringGet, &p);
\r
6128 EndDialog(hDlg, TRUE);
\r
6135 case IDM_HELPCONTENTS:
\r
6136 if (!WinHelp (hDlg, "winboard.hlp", HELP_KEY,(DWORD)(LPSTR)"CONTENTS")) {
\r
6137 MessageBox (GetFocus(),
\r
6138 _("Unable to activate help"),
\r
6139 szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);
\r
6144 SetStartupDialogEnables(hDlg);
\r
6152 /*---------------------------------------------------------------------------*\
\r
6154 * About box dialog functions
\r
6156 \*---------------------------------------------------------------------------*/
\r
6158 /* Process messages for "About" dialog box */
\r
6160 About(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
6162 switch (message) {
\r
6163 case WM_INITDIALOG: /* message: initialize dialog box */
\r
6164 /* Center the dialog over the application window */
\r
6165 CenterWindow (hDlg, GetWindow (hDlg, GW_OWNER));
\r
6166 SetDlgItemText(hDlg, ABOUTBOX_Version, programVersion);
\r
6167 Translate(hDlg, ABOUTBOX);
\r
6171 case WM_COMMAND: /* message: received a command */
\r
6172 if (LOWORD(wParam) == IDOK /* "OK" box selected? */
\r
6173 || LOWORD(wParam) == IDCANCEL) { /* System menu close command? */
\r
6174 EndDialog(hDlg, TRUE); /* Exit the dialog */
\r
6182 /*---------------------------------------------------------------------------*\
\r
6184 * Comment Dialog functions
\r
6186 \*---------------------------------------------------------------------------*/
\r
6189 CommentDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
6191 static HANDLE hwndText = NULL;
\r
6192 int len, newSizeX, newSizeY, flags;
\r
6193 static int sizeX, sizeY;
\r
6198 switch (message) {
\r
6199 case WM_INITDIALOG: /* message: initialize dialog box */
\r
6200 /* Initialize the dialog items */
\r
6201 Translate(hDlg, DLG_EditComment);
\r
6202 hwndText = GetDlgItem(hDlg, OPT_CommentText);
\r
6203 SetDlgItemText(hDlg, OPT_CommentText, commentText);
\r
6204 EnableWindow(GetDlgItem(hDlg, OPT_CancelComment), editComment);
\r
6205 EnableWindow(GetDlgItem(hDlg, OPT_ClearComment), editComment);
\r
6206 EnableWindow(GetDlgItem(hDlg, OPT_EditComment), !editComment);
\r
6207 SendMessage(hwndText, EM_SETREADONLY, !editComment, 0);
\r
6208 SetWindowText(hDlg, commentTitle);
\r
6209 if (editComment) {
\r
6210 SetFocus(hwndText);
\r
6212 SetFocus(GetDlgItem(hDlg, IDOK));
\r
6214 SendMessage(GetDlgItem(hDlg, OPT_CommentText),
\r
6215 WM_SETFONT, (WPARAM)font[boardSize][COMMENT_FONT]->hf,
\r
6216 MAKELPARAM(FALSE, 0));
\r
6217 /* Size and position the dialog */
\r
6218 if (!commentDialog) {
\r
6219 commentDialog = hDlg;
\r
6220 flags = SWP_NOZORDER;
\r
6221 GetClientRect(hDlg, &rect);
\r
6222 sizeX = rect.right;
\r
6223 sizeY = rect.bottom;
\r
6224 if (wpComment.x != CW_USEDEFAULT && wpComment.y != CW_USEDEFAULT &&
\r
6225 wpComment.width != CW_USEDEFAULT && wpComment.height != CW_USEDEFAULT) {
\r
6226 WINDOWPLACEMENT wp;
\r
6227 EnsureOnScreen(&wpComment.x, &wpComment.y, 0, 0);
\r
6228 wp.length = sizeof(WINDOWPLACEMENT);
\r
6230 wp.showCmd = SW_SHOW;
\r
6231 wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;
\r
6232 wp.rcNormalPosition.left = wpComment.x;
\r
6233 wp.rcNormalPosition.right = wpComment.x + wpComment.width;
\r
6234 wp.rcNormalPosition.top = wpComment.y;
\r
6235 wp.rcNormalPosition.bottom = wpComment.y + wpComment.height;
\r
6236 SetWindowPlacement(hDlg, &wp);
\r
6238 GetClientRect(hDlg, &rect);
\r
6239 newSizeX = rect.right;
\r
6240 newSizeY = rect.bottom;
\r
6241 ResizeEditPlusButtons(hDlg, hwndText, sizeX, sizeY,
\r
6242 newSizeX, newSizeY);
\r
6247 SendDlgItemMessage( hDlg, OPT_CommentText, EM_SETEVENTMASK, 0, ENM_MOUSEEVENTS );
\r
6250 case WM_COMMAND: /* message: received a command */
\r
6251 switch (LOWORD(wParam)) {
\r
6253 if (editComment) {
\r
6255 /* Read changed options from the dialog box */
\r
6256 hwndText = GetDlgItem(hDlg, OPT_CommentText);
\r
6257 len = GetWindowTextLength(hwndText);
\r
6258 str = (char *) malloc(len + 1);
\r
6259 GetWindowText(hwndText, str, len + 1);
\r
6268 ReplaceComment(commentIndex, str);
\r
6275 case OPT_CancelComment:
\r
6279 case OPT_ClearComment:
\r
6280 SetDlgItemText(hDlg, OPT_CommentText, "");
\r
6283 case OPT_EditComment:
\r
6284 EditCommentEvent();
\r
6292 case WM_NOTIFY: // [HGM] vari: cloned from whistory.c
\r
6293 if( wParam == OPT_CommentText ) {
\r
6294 MSGFILTER * lpMF = (MSGFILTER *) lParam;
\r
6296 if( lpMF->msg == WM_RBUTTONDOWN && (lpMF->wParam & (MK_CONTROL | MK_SHIFT)) == 0 ) {
\r
6300 pt.x = LOWORD( lpMF->lParam );
\r
6301 pt.y = HIWORD( lpMF->lParam );
\r
6303 index = SendDlgItemMessage( hDlg, OPT_CommentText, EM_CHARFROMPOS, 0, (LPARAM) &pt );
\r
6305 hwndText = GetDlgItem(hDlg, OPT_CommentText); // cloned from above
\r
6306 len = GetWindowTextLength(hwndText);
\r
6307 str = (char *) malloc(len + 1);
\r
6308 GetWindowText(hwndText, str, len + 1);
\r
6309 ReplaceComment(commentIndex, str);
\r
6310 if(commentIndex != currentMove) ToNrEvent(commentIndex);
\r
6311 LoadVariation( index, str ); // [HGM] also does the actual moving to it, now
\r
6314 /* Zap the message for good: apparently, returning non-zero is not enough */
\r
6315 lpMF->msg = WM_USER;
\r
6323 newSizeX = LOWORD(lParam);
\r
6324 newSizeY = HIWORD(lParam);
\r
6325 ResizeEditPlusButtons(hDlg, hwndText, sizeX, sizeY, newSizeX, newSizeY);
\r
6330 case WM_GETMINMAXINFO:
\r
6331 /* Prevent resizing window too small */
\r
6332 mmi = (MINMAXINFO *) lParam;
\r
6333 mmi->ptMinTrackSize.x = 100;
\r
6334 mmi->ptMinTrackSize.y = 100;
\r
6341 EitherCommentPopUp(int index, char *title, char *str, BOOLEAN edit)
\r
6346 CheckMenuItem(GetMenu(hwndMain), IDM_Comment, edit ? MF_CHECKED : MF_UNCHECKED);
\r
6348 if (str == NULL) str = "";
\r
6349 p = (char *) malloc(2 * strlen(str) + 2);
\r
6352 if (*str == '\n') *q++ = '\r';
\r
6356 if (commentText != NULL) free(commentText);
\r
6358 commentIndex = index;
\r
6359 commentTitle = title;
\r
6361 editComment = edit;
\r
6363 if (commentDialog) {
\r
6364 SendMessage(commentDialog, WM_INITDIALOG, 0, 0);
\r
6365 if (!commentUp) ShowWindow(commentDialog, SW_SHOW);
\r
6367 lpProc = MakeProcInstance((FARPROC)CommentDialog, hInst);
\r
6368 CreateDialog(hInst, MAKEINTRESOURCE(DLG_EditComment),
\r
6369 hwndMain, (DLGPROC)lpProc);
\r
6370 FreeProcInstance(lpProc);
\r
6376 /*---------------------------------------------------------------------------*\
\r
6378 * Type-in move dialog functions
\r
6380 \*---------------------------------------------------------------------------*/
\r
6383 TypeInMoveDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
6385 char move[MSG_SIZ];
\r
6387 ChessMove moveType;
\r
6388 int fromX, fromY, toX, toY;
\r
6391 switch (message) {
\r
6392 case WM_INITDIALOG:
\r
6393 move[0] = (char) lParam;
\r
6394 move[1] = NULLCHAR;
\r
6395 CenterWindowEx(hDlg, GetWindow(hDlg, GW_OWNER), 1 );
\r
6396 Translate(hDlg, DLG_TypeInMove);
\r
6397 hInput = GetDlgItem(hDlg, OPT_Move);
\r
6398 SetWindowText(hInput, move);
\r
6400 SendMessage(hInput, EM_SETSEL, (WPARAM)9999, (LPARAM)9999);
\r
6404 switch (LOWORD(wParam)) {
\r
6406 shiftKey = GetKeyState(VK_SHIFT) < 0; // [HGM] remember last shift status
\r
6407 GetDlgItemText(hDlg, OPT_Move, move, sizeof(move));
\r
6408 { int n; Board board;
\r
6410 if(gameMode == EditPosition && ParseFEN(board, &n, move) ) {
\r
6411 EditPositionPasteFEN(move);
\r
6412 EndDialog(hDlg, TRUE);
\r
6415 // [HGM] movenum: allow move number to be typed in any mode
\r
6416 if(sscanf(move, "%d", &n) == 1 && n != 0 ) {
\r
6418 EndDialog(hDlg, TRUE);
\r
6422 if (gameMode != EditGame && currentMove != forwardMostMove &&
\r
6423 gameMode != Training) {
\r
6424 DisplayMoveError(_("Displayed move is not current"));
\r
6426 // GetDlgItemText(hDlg, OPT_Move, move, sizeof(move)); // moved upstream
\r
6427 int ok = ParseOneMove(move, gameMode == EditPosition ? blackPlaysFirst : currentMove,
\r
6428 &moveType, &fromX, &fromY, &toX, &toY, &promoChar);
\r
6429 if(!ok && move[0] >= 'a') { move[0] += 'A' - 'a'; ok = 2; } // [HGM] try also capitalized
\r
6430 if (ok==1 || ok && ParseOneMove(move, gameMode == EditPosition ? blackPlaysFirst : currentMove,
\r
6431 &moveType, &fromX, &fromY, &toX, &toY, &promoChar)) {
\r
6432 if (gameMode != Training)
\r
6433 forwardMostMove = currentMove;
\r
6434 UserMoveEvent(fromX, fromY, toX, toY, promoChar);
\r
6436 DisplayMoveError(_("Could not parse move"));
\r
6439 EndDialog(hDlg, TRUE);
\r
6442 EndDialog(hDlg, FALSE);
\r
6453 PopUpMoveDialog(char firstchar)
\r
6457 if ((gameMode == BeginningOfGame && !appData.icsActive) ||
\r
6458 gameMode == MachinePlaysWhite || gameMode == MachinePlaysBlack ||
\r
6459 gameMode == AnalyzeMode || gameMode == EditGame ||
\r
6460 gameMode == EditPosition || gameMode == IcsExamining ||
\r
6461 gameMode == IcsPlayingWhite || gameMode == IcsPlayingBlack ||
\r
6462 isdigit(firstchar) && // [HGM] movenum: allow typing in of move nr in 'passive' modes
\r
6463 ( gameMode == AnalyzeFile || gameMode == PlayFromGameFile ||
\r
6464 gameMode == IcsObserving || gameMode == TwoMachinesPlay ) ||
\r
6465 gameMode == Training) {
\r
6466 lpProc = MakeProcInstance((FARPROC)TypeInMoveDialog, hInst);
\r
6467 DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_TypeInMove),
\r
6468 hwndMain, (DLGPROC)lpProc, (LPARAM)firstchar);
\r
6469 FreeProcInstance(lpProc);
\r
6473 /*---------------------------------------------------------------------------*\
\r
6475 * Type-in name dialog functions
\r
6477 \*---------------------------------------------------------------------------*/
\r
6480 TypeInNameDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
6482 char move[MSG_SIZ];
\r
6485 switch (message) {
\r
6486 case WM_INITDIALOG:
\r
6487 move[0] = (char) lParam;
\r
6488 move[1] = NULLCHAR;
\r
6489 CenterWindowEx(hDlg, GetWindow(hDlg, GW_OWNER), 1 );
\r
6490 Translate(hDlg, DLG_TypeInName);
\r
6491 hInput = GetDlgItem(hDlg, OPT_Name);
\r
6492 SetWindowText(hInput, move);
\r
6494 SendMessage(hInput, EM_SETSEL, (WPARAM)9999, (LPARAM)9999);
\r
6498 switch (LOWORD(wParam)) {
\r
6500 GetDlgItemText(hDlg, OPT_Name, move, sizeof(move));
\r
6501 appData.userName = strdup(move);
\r
6504 if(gameMode == MachinePlaysWhite || gameMode == MachinePlaysBlack) {
\r
6505 snprintf(move, MSG_SIZ, "%s vs. %s", gameInfo.white, gameInfo.black);
\r
6506 DisplayTitle(move);
\r
6510 EndDialog(hDlg, TRUE);
\r
6513 EndDialog(hDlg, FALSE);
\r
6524 PopUpNameDialog(char firstchar)
\r
6528 lpProc = MakeProcInstance((FARPROC)TypeInNameDialog, hInst);
\r
6529 DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_TypeInName),
\r
6530 hwndMain, (DLGPROC)lpProc, (LPARAM)firstchar);
\r
6531 FreeProcInstance(lpProc);
\r
6534 /*---------------------------------------------------------------------------*\
\r
6538 \*---------------------------------------------------------------------------*/
\r
6540 /* Nonmodal error box */
\r
6541 LRESULT CALLBACK ErrorDialog(HWND hDlg, UINT message,
\r
6542 WPARAM wParam, LPARAM lParam);
\r
6545 ErrorPopUp(char *title, char *content)
\r
6549 BOOLEAN modal = hwndMain == NULL;
\r
6567 strncpy(errorTitle, title, sizeof(errorTitle));
\r
6568 errorTitle[sizeof(errorTitle) - 1] = '\0';
\r
6571 MessageBox(NULL, errorMessage, errorTitle, MB_OK|MB_ICONEXCLAMATION);
\r
6573 lpProc = MakeProcInstance((FARPROC)ErrorDialog, hInst);
\r
6574 CreateDialog(hInst, MAKEINTRESOURCE(DLG_Error),
\r
6575 hwndMain, (DLGPROC)lpProc);
\r
6576 FreeProcInstance(lpProc);
\r
6583 if (!appData.popupMoveErrors && moveErrorMessageUp) DisplayMessage("", "");
\r
6584 if (errorDialog == NULL) return;
\r
6585 DestroyWindow(errorDialog);
\r
6586 errorDialog = NULL;
\r
6587 if(errorExitStatus) ExitEvent(errorExitStatus);
\r
6591 ErrorDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
6596 switch (message) {
\r
6597 case WM_INITDIALOG:
\r
6598 GetWindowRect(hDlg, &rChild);
\r
6601 SetWindowPos(hDlg, NULL, rChild.left,
\r
6602 rChild.top + boardRect.top - (rChild.bottom - rChild.top),
\r
6603 0, 0, SWP_NOZORDER|SWP_NOSIZE);
\r
6607 [AS] It seems that the above code wants to move the dialog up in the "caption
\r
6608 area" of the main window, but it uses the dialog height as an hard-coded constant,
\r
6609 and it doesn't work when you resize the dialog.
\r
6610 For now, just give it a default position.
\r
6612 SetWindowPos(hDlg, NULL, boardRect.left+8, boardRect.top+8, 0, 0, SWP_NOZORDER|SWP_NOSIZE);
\r
6613 Translate(hDlg, DLG_Error);
\r
6615 errorDialog = hDlg;
\r
6616 SetWindowText(hDlg, errorTitle);
\r
6617 hwndText = GetDlgItem(hDlg, OPT_ErrorText);
\r
6618 SetDlgItemText(hDlg, OPT_ErrorText, errorMessage);
\r
6622 switch (LOWORD(wParam)) {
\r
6625 if (errorDialog == hDlg) errorDialog = NULL;
\r
6626 DestroyWindow(hDlg);
\r
6638 HWND gothicDialog = NULL;
\r
6641 GothicDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
6645 int height = GetSystemMetrics(SM_CYCAPTION)+GetSystemMetrics(SM_CYFRAME);
\r
6647 switch (message) {
\r
6648 case WM_INITDIALOG:
\r
6649 GetWindowRect(hDlg, &rChild);
\r
6651 SetWindowPos(hDlg, NULL, wpMain.x, wpMain.y-height, wpMain.width, height,
\r
6655 [AS] It seems that the above code wants to move the dialog up in the "caption
\r
6656 area" of the main window, but it uses the dialog height as an hard-coded constant,
\r
6657 and it doesn't work when you resize the dialog.
\r
6658 For now, just give it a default position.
\r
6660 gothicDialog = hDlg;
\r
6661 SetWindowText(hDlg, errorTitle);
\r
6662 hwndText = GetDlgItem(hDlg, OPT_ErrorText);
\r
6663 SetDlgItemText(hDlg, OPT_ErrorText, errorMessage);
\r
6667 switch (LOWORD(wParam)) {
\r
6670 if (errorDialog == hDlg) errorDialog = NULL;
\r
6671 DestroyWindow(hDlg);
\r
6683 GothicPopUp(char *title, VariantClass variant)
\r
6686 static char *lastTitle;
\r
6688 strncpy(errorTitle, title, sizeof(errorTitle));
\r
6689 errorTitle[sizeof(errorTitle) - 1] = '\0';
\r
6691 if(lastTitle != title && gothicDialog != NULL) {
\r
6692 DestroyWindow(gothicDialog);
\r
6693 gothicDialog = NULL;
\r
6695 if(variant != VariantNormal && gothicDialog == NULL) {
\r
6696 title = lastTitle;
\r
6697 lpProc = MakeProcInstance((FARPROC)GothicDialog, hInst);
\r
6698 CreateDialog(hInst, MAKEINTRESOURCE(DLG_Error),
\r
6699 hwndMain, (DLGPROC)lpProc);
\r
6700 FreeProcInstance(lpProc);
\r
6705 /*---------------------------------------------------------------------------*\
\r
6707 * Ics Interaction console functions
\r
6709 \*---------------------------------------------------------------------------*/
\r
6711 #define HISTORY_SIZE 64
\r
6712 static char *history[HISTORY_SIZE];
\r
6713 int histIn = 0, histP = 0;
\r
6716 SaveInHistory(char *cmd)
\r
6718 if (history[histIn] != NULL) {
\r
6719 free(history[histIn]);
\r
6720 history[histIn] = NULL;
\r
6722 if (*cmd == NULLCHAR) return;
\r
6723 history[histIn] = StrSave(cmd);
\r
6724 histIn = (histIn + 1) % HISTORY_SIZE;
\r
6725 if (history[histIn] != NULL) {
\r
6726 free(history[histIn]);
\r
6727 history[histIn] = NULL;
\r
6733 PrevInHistory(char *cmd)
\r
6736 if (histP == histIn) {
\r
6737 if (history[histIn] != NULL) free(history[histIn]);
\r
6738 history[histIn] = StrSave(cmd);
\r
6740 newhp = (histP - 1 + HISTORY_SIZE) % HISTORY_SIZE;
\r
6741 if (newhp == histIn || history[newhp] == NULL) return NULL;
\r
6743 return history[histP];
\r
6749 if (histP == histIn) return NULL;
\r
6750 histP = (histP + 1) % HISTORY_SIZE;
\r
6751 return history[histP];
\r
6755 LoadIcsTextMenu(IcsTextMenuEntry *e)
\r
6759 hmenu = LoadMenu(hInst, "TextMenu");
\r
6760 h = GetSubMenu(hmenu, 0);
\r
6762 if (strcmp(e->item, "-") == 0) {
\r
6763 AppendMenu(h, MF_SEPARATOR, 0, 0);
\r
6764 } else { // [HGM] re-written a bit to use only one AppendMenu call for both cases (| or no |)
\r
6765 int flags = MF_STRING, j = 0;
\r
6766 if (e->item[0] == '|') {
\r
6767 flags |= MF_MENUBARBREAK;
\r
6770 if(!strcmp(e->command, "none")) flags |= MF_GRAYED; // [HGM] chatclick: provide inactive dummy
\r
6771 AppendMenu(h, flags, IDM_CommandX + i, e->item + j);
\r
6779 WNDPROC consoleTextWindowProc;
\r
6782 CommandX(HWND hwnd, char *command, BOOLEAN getname, BOOLEAN immediate)
\r
6784 char buf[MSG_SIZ], name[MSG_SIZ];
\r
6785 HWND hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
6789 SetWindowText(hInput, command);
\r
6791 SendMessage(hInput, WM_CHAR, '\r', 0);
\r
6793 sel.cpMin = 999999;
\r
6794 sel.cpMax = 999999;
\r
6795 SendMessage(hInput, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
6800 SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);
\r
6801 if (sel.cpMin == sel.cpMax) {
\r
6802 /* Expand to surrounding word */
\r
6805 tr.chrg.cpMax = sel.cpMin;
\r
6806 tr.chrg.cpMin = --sel.cpMin;
\r
6807 if (sel.cpMin < 0) break;
\r
6808 tr.lpstrText = name;
\r
6809 SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr);
\r
6810 } while (isalpha(name[0]) || isdigit(name[0]) || name[0] == '-');
\r
6814 tr.chrg.cpMin = sel.cpMax;
\r
6815 tr.chrg.cpMax = ++sel.cpMax;
\r
6816 tr.lpstrText = name;
\r
6817 if (SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr) < 1) break;
\r
6818 } while (isalpha(name[0]) || isdigit(name[0]) || name[0] == '-');
\r
6821 if (sel.cpMax == sel.cpMin || sel.cpMax - sel.cpMin > MSG_SIZ/2) {
\r
6822 MessageBeep(MB_ICONEXCLAMATION);
\r
6826 tr.lpstrText = name;
\r
6827 SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr);
\r
6829 if (sel.cpMax - sel.cpMin > MSG_SIZ/2) {
\r
6830 MessageBeep(MB_ICONEXCLAMATION);
\r
6833 SendMessage(hwnd, EM_GETSELTEXT, 0, (LPARAM) name);
\r
6836 if(strstr(command, "%s")) snprintf(buf, MSG_SIZ, command, name); else
\r
6837 snprintf(buf, MSG_SIZ, "%s %s", command, name);
\r
6838 SetWindowText(hInput, buf);
\r
6839 SendMessage(hInput, WM_CHAR, '\r', 0);
\r
6841 if(!strcmp(command, "chat")) { ChatPopUp(name); return; }
\r
6842 snprintf(buf, MSG_SIZ, "%s %s ", command, name); /* trailing space */
\r
6843 SetWindowText(hInput, buf);
\r
6844 sel.cpMin = 999999;
\r
6845 sel.cpMax = 999999;
\r
6846 SendMessage(hInput, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
6852 ConsoleTextSubclass(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
\r
6857 switch (message) {
\r
6859 if (!(GetKeyState(VK_CONTROL) & ~1)) break;
\r
6862 SendMessage(hwnd, EM_LINESCROLL, 0, -999999);
\r
6865 sel.cpMin = 999999;
\r
6866 sel.cpMax = 999999;
\r
6867 SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
6868 SendMessage(hwnd, EM_SCROLLCARET, 0, 0);
\r
6873 if(wParam != '\022') {
\r
6874 if (wParam == '\t') {
\r
6875 if (GetKeyState(VK_SHIFT) < 0) {
\r
6877 if (IsIconic(hwndMain)) ShowWindow(hwndMain, SW_RESTORE);
\r
6878 if (buttonDesc[0].hwnd) {
\r
6879 SetFocus(buttonDesc[0].hwnd);
\r
6881 SetFocus(hwndMain);
\r
6885 SetFocus(GetDlgItem(hwndConsole, OPT_ConsoleInput));
\r
6888 hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
6889 JAWS_DELETE( SetFocus(hInput); )
\r
6890 SendMessage(hInput, message, wParam, lParam);
\r
6893 } // [HGM] navigate: for Ctrl+R, flow into nex case (moved up here) to summon up menu
\r
6894 case WM_RBUTTONDOWN:
\r
6895 if (!(GetKeyState(VK_SHIFT) & ~1)) {
\r
6896 /* Move selection here if it was empty */
\r
6898 pt.x = LOWORD(lParam);
\r
6899 pt.y = HIWORD(lParam);
\r
6900 SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);
\r
6901 if (sel.cpMin == sel.cpMax) {
\r
6902 sel.cpMin = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&pt); /*doc is wrong*/
\r
6903 sel.cpMax = sel.cpMin;
\r
6904 SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
6906 SendMessage(hwnd, EM_HIDESELECTION, FALSE, FALSE);
\r
6907 { // [HGM] chatclick: code moved here from WM_RBUTTONUP case, to have menu appear on down-click
\r
6909 HMENU hmenu = LoadIcsTextMenu(icsTextMenuEntry);
\r
6910 SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);
\r
6911 if (sel.cpMin == sel.cpMax) {
\r
6912 EnableMenuItem(hmenu, IDM_Copy, MF_BYCOMMAND|MF_GRAYED);
\r
6913 EnableMenuItem(hmenu, IDM_QuickPaste, MF_BYCOMMAND|MF_GRAYED);
\r
6915 if (!IsClipboardFormatAvailable(CF_TEXT)) {
\r
6916 EnableMenuItem(hmenu, IDM_Paste, MF_BYCOMMAND|MF_GRAYED);
\r
6918 pt.x = LOWORD(lParam)-30; // [HGM] chatclick: make menu pop up with pointer above upper-right item
\r
6919 pt.y = HIWORD(lParam)-10; // make it appear as if mouse moved there, so it will be selected on up-click
\r
6920 PostMessage(hwnd, WM_MOUSEMOVE, wParam, lParam+5);
\r
6921 MenuPopup(hwnd, pt, hmenu, -1);
\r
6925 case WM_RBUTTONUP:
\r
6926 if (GetKeyState(VK_SHIFT) & ~1) {
\r
6927 SendDlgItemMessage(hwndConsole, OPT_ConsoleText,
\r
6928 WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);
\r
6932 hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
6934 return SendMessage(hInput, message, wParam, lParam);
\r
6935 case WM_MBUTTONDOWN:
\r
6936 return SendMessage(hwnd, WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);
\r
6938 switch (LOWORD(wParam)) {
\r
6939 case IDM_QuickPaste:
\r
6941 SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);
\r
6942 if (sel.cpMin == sel.cpMax) {
\r
6943 MessageBeep(MB_ICONEXCLAMATION);
\r
6946 SendMessage(hwnd, WM_COPY, 0, 0);
\r
6947 hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
6948 SendMessage(hInput, WM_PASTE, 0, 0);
\r
6953 SendMessage(hwnd, WM_CUT, 0, 0);
\r
6956 SendMessage(hwnd, WM_PASTE, 0, 0);
\r
6959 SendMessage(hwnd, WM_COPY, 0, 0);
\r
6963 int i = LOWORD(wParam) - IDM_CommandX;
\r
6964 if (i >= 0 && i < ICS_TEXT_MENU_SIZE &&
\r
6965 icsTextMenuEntry[i].command != NULL) {
\r
6966 CommandX(hwnd, icsTextMenuEntry[i].command,
\r
6967 icsTextMenuEntry[i].getname,
\r
6968 icsTextMenuEntry[i].immediate);
\r
6976 return (*consoleTextWindowProc)(hwnd, message, wParam, lParam);
\r
6979 WNDPROC consoleInputWindowProc;
\r
6982 ConsoleInputSubclass(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
\r
6984 char buf[MSG_SIZ];
\r
6986 static BOOL sendNextChar = FALSE;
\r
6987 static BOOL quoteNextChar = FALSE;
\r
6988 InputSource *is = consoleInputSource;
\r
6992 switch (message) {
\r
6994 if (!appData.localLineEditing || sendNextChar) {
\r
6995 is->buf[0] = (CHAR) wParam;
\r
6997 SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);
\r
6998 sendNextChar = FALSE;
\r
7001 if (quoteNextChar) {
\r
7002 buf[0] = (char) wParam;
\r
7003 buf[1] = NULLCHAR;
\r
7004 SendMessage(hwnd, EM_REPLACESEL, TRUE, (LPARAM) buf);
\r
7005 quoteNextChar = FALSE;
\r
7009 case '\r': /* Enter key */
\r
7010 is->count = GetWindowText(hwnd, is->buf, INPUT_SOURCE_BUF_SIZE-1);
\r
7011 if (consoleEcho) SaveInHistory(is->buf);
\r
7012 is->buf[is->count++] = '\n';
\r
7013 is->buf[is->count] = NULLCHAR;
\r
7014 SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);
\r
7015 if (consoleEcho) {
\r
7016 ConsoleOutput(is->buf, is->count, TRUE);
\r
7017 } else if (appData.localLineEditing) {
\r
7018 ConsoleOutput("\n", 1, TRUE);
\r
7021 case '\033': /* Escape key */
\r
7022 SetWindowText(hwnd, "");
\r
7023 cf.cbSize = sizeof(CHARFORMAT);
\r
7024 cf.dwMask = CFM_COLOR|CFM_BOLD|CFM_ITALIC|CFM_UNDERLINE|CFM_STRIKEOUT;
\r
7025 if (consoleEcho) {
\r
7026 cf.crTextColor = textAttribs[ColorNormal].color;
\r
7028 cf.crTextColor = COLOR_ECHOOFF;
\r
7030 cf.dwEffects = textAttribs[ColorNormal].effects;
\r
7031 SendMessage(hwnd, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf);
\r
7033 case '\t': /* Tab key */
\r
7034 if (GetKeyState(VK_SHIFT) < 0) {
\r
7036 SetFocus(GetDlgItem(hwndConsole, OPT_ConsoleText));
\r
7039 if (IsIconic(hwndMain)) ShowWindow(hwndMain, SW_RESTORE);
\r
7040 if (buttonDesc[0].hwnd) {
\r
7041 SetFocus(buttonDesc[0].hwnd);
\r
7043 SetFocus(hwndMain);
\r
7047 case '\023': /* Ctrl+S */
\r
7048 sendNextChar = TRUE;
\r
7050 case '\021': /* Ctrl+Q */
\r
7051 quoteNextChar = TRUE;
\r
7061 GetWindowText(hwnd, buf, MSG_SIZ);
\r
7062 p = PrevInHistory(buf);
\r
7064 SetWindowText(hwnd, p);
\r
7065 sel.cpMin = 999999;
\r
7066 sel.cpMax = 999999;
\r
7067 SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
7072 p = NextInHistory();
\r
7074 SetWindowText(hwnd, p);
\r
7075 sel.cpMin = 999999;
\r
7076 sel.cpMax = 999999;
\r
7077 SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
7083 if (!(GetKeyState(VK_CONTROL) & ~1)) break;
\r
7087 SendDlgItemMessage(hwndConsole, OPT_ConsoleText, message, wParam, lParam);
\r
7091 case WM_MBUTTONDOWN:
\r
7092 SendDlgItemMessage(hwndConsole, OPT_ConsoleText,
\r
7093 WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);
\r
7095 case WM_RBUTTONUP:
\r
7096 if (GetKeyState(VK_SHIFT) & ~1) {
\r
7097 SendDlgItemMessage(hwndConsole, OPT_ConsoleText,
\r
7098 WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);
\r
7102 hmenu = LoadMenu(hInst, "InputMenu");
\r
7103 SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);
\r
7104 if (sel.cpMin == sel.cpMax) {
\r
7105 EnableMenuItem(hmenu, IDM_Copy, MF_BYCOMMAND|MF_GRAYED);
\r
7106 EnableMenuItem(hmenu, IDM_Cut, MF_BYCOMMAND|MF_GRAYED);
\r
7108 if (!IsClipboardFormatAvailable(CF_TEXT)) {
\r
7109 EnableMenuItem(hmenu, IDM_Paste, MF_BYCOMMAND|MF_GRAYED);
\r
7111 pt.x = LOWORD(lParam);
\r
7112 pt.y = HIWORD(lParam);
\r
7113 MenuPopup(hwnd, pt, hmenu, -1);
\r
7117 switch (LOWORD(wParam)) {
\r
7119 SendMessage(hwnd, EM_UNDO, 0, 0);
\r
7121 case IDM_SelectAll:
\r
7123 sel.cpMax = -1; /*999999?*/
\r
7124 SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
7127 SendMessage(hwnd, WM_CUT, 0, 0);
\r
7130 SendMessage(hwnd, WM_PASTE, 0, 0);
\r
7133 SendMessage(hwnd, WM_COPY, 0, 0);
\r
7138 return (*consoleInputWindowProc)(hwnd, message, wParam, lParam);
\r
7141 #define CO_MAX 100000
\r
7142 #define CO_TRIM 1000
\r
7145 ConsoleWndProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
7147 static SnapData sd;
\r
7148 HWND hText, hInput;
\r
7150 static int sizeX, sizeY;
\r
7151 int newSizeX, newSizeY;
\r
7155 hText = GetDlgItem(hDlg, OPT_ConsoleText);
\r
7156 hInput = GetDlgItem(hDlg, OPT_ConsoleInput);
\r
7158 switch (message) {
\r
7160 if (((NMHDR*)lParam)->code == EN_LINK)
\r
7162 ENLINK *pLink = (ENLINK*)lParam;
\r
7163 if (pLink->msg == WM_LBUTTONUP)
\r
7167 tr.chrg = pLink->chrg;
\r
7168 tr.lpstrText = malloc(1+tr.chrg.cpMax-tr.chrg.cpMin);
\r
7169 SendMessage(hText, EM_GETTEXTRANGE, 0, (LPARAM)&tr);
\r
7170 ShellExecute(NULL, "open", tr.lpstrText, NULL, NULL, SW_SHOW);
\r
7171 free(tr.lpstrText);
\r
7175 case WM_INITDIALOG: /* message: initialize dialog box */
\r
7176 hwndConsole = hDlg;
\r
7178 consoleTextWindowProc = (WNDPROC)
\r
7179 SetWindowLong(hText, GWL_WNDPROC, (LONG) ConsoleTextSubclass);
\r
7180 SendMessage(hText, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);
\r
7181 consoleInputWindowProc = (WNDPROC)
\r
7182 SetWindowLong(hInput, GWL_WNDPROC, (LONG) ConsoleInputSubclass);
\r
7183 SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);
\r
7184 Colorize(ColorNormal, TRUE);
\r
7185 SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &consoleCF);
\r
7186 ChangedConsoleFont();
\r
7187 GetClientRect(hDlg, &rect);
\r
7188 sizeX = rect.right;
\r
7189 sizeY = rect.bottom;
\r
7190 if (wpConsole.x != CW_USEDEFAULT && wpConsole.y != CW_USEDEFAULT &&
\r
7191 wpConsole.width != CW_USEDEFAULT && wpConsole.height != CW_USEDEFAULT) {
\r
7192 WINDOWPLACEMENT wp;
\r
7193 EnsureOnScreen(&wpConsole.x, &wpConsole.y, 0, 0);
\r
7194 wp.length = sizeof(WINDOWPLACEMENT);
\r
7196 wp.showCmd = SW_SHOW;
\r
7197 wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;
\r
7198 wp.rcNormalPosition.left = wpConsole.x;
\r
7199 wp.rcNormalPosition.right = wpConsole.x + wpConsole.width;
\r
7200 wp.rcNormalPosition.top = wpConsole.y;
\r
7201 wp.rcNormalPosition.bottom = wpConsole.y + wpConsole.height;
\r
7202 SetWindowPlacement(hDlg, &wp);
\r
7205 // [HGM] Chessknight's change 2004-07-13
\r
7206 else { /* Determine Defaults */
\r
7207 WINDOWPLACEMENT wp;
\r
7208 wpConsole.x = wpMain.width + 1;
\r
7209 wpConsole.y = wpMain.y;
\r
7210 wpConsole.width = screenWidth - wpMain.width;
\r
7211 wpConsole.height = wpMain.height;
\r
7212 EnsureOnScreen(&wpConsole.x, &wpConsole.y, 0, 0);
\r
7213 wp.length = sizeof(WINDOWPLACEMENT);
\r
7215 wp.showCmd = SW_SHOW;
\r
7216 wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;
\r
7217 wp.rcNormalPosition.left = wpConsole.x;
\r
7218 wp.rcNormalPosition.right = wpConsole.x + wpConsole.width;
\r
7219 wp.rcNormalPosition.top = wpConsole.y;
\r
7220 wp.rcNormalPosition.bottom = wpConsole.y + wpConsole.height;
\r
7221 SetWindowPlacement(hDlg, &wp);
\r
7224 // Allow hText to highlight URLs and send notifications on them
\r
7225 wMask = (WORD) SendMessage(hText, EM_GETEVENTMASK, 0, 0L);
\r
7226 SendMessage(hText, EM_SETEVENTMASK, 0, wMask | ENM_LINK);
\r
7227 SendMessage(hText, EM_AUTOURLDETECT, TRUE, 0L);
\r
7228 SetWindowLong(hText, GWL_USERDATA, 79); // initialize the text window's width
\r
7242 if (IsIconic(hDlg)) break;
\r
7243 newSizeX = LOWORD(lParam);
\r
7244 newSizeY = HIWORD(lParam);
\r
7245 if (sizeX != newSizeX || sizeY != newSizeY) {
\r
7246 RECT rectText, rectInput;
\r
7248 int newTextHeight, newTextWidth;
\r
7249 GetWindowRect(hText, &rectText);
\r
7250 newTextWidth = rectText.right - rectText.left + newSizeX - sizeX;
\r
7251 newTextHeight = rectText.bottom - rectText.top + newSizeY - sizeY;
\r
7252 if (newTextHeight < 0) {
\r
7253 newSizeY += -newTextHeight;
\r
7254 newTextHeight = 0;
\r
7256 SetWindowPos(hText, NULL, 0, 0,
\r
7257 newTextWidth, newTextHeight, SWP_NOZORDER|SWP_NOMOVE);
\r
7258 GetWindowRect(hInput, &rectInput); /* gives screen coords */
\r
7259 pt.x = rectInput.left;
\r
7260 pt.y = rectInput.top + newSizeY - sizeY;
\r
7261 ScreenToClient(hDlg, &pt);
\r
7262 SetWindowPos(hInput, NULL,
\r
7263 pt.x, pt.y, /* needs client coords */
\r
7264 rectInput.right - rectInput.left + newSizeX - sizeX,
\r
7265 rectInput.bottom - rectInput.top, SWP_NOZORDER);
\r
7271 case WM_GETMINMAXINFO:
\r
7272 /* Prevent resizing window too small */
\r
7273 mmi = (MINMAXINFO *) lParam;
\r
7274 mmi->ptMinTrackSize.x = 100;
\r
7275 mmi->ptMinTrackSize.y = 100;
\r
7278 /* [AS] Snapping */
\r
7279 case WM_ENTERSIZEMOVE:
\r
7280 return OnEnterSizeMove( &sd, hDlg, wParam, lParam );
\r
7283 return OnSizing( &sd, hDlg, wParam, lParam );
\r
7286 return OnMoving( &sd, hDlg, wParam, lParam );
\r
7288 case WM_EXITSIZEMOVE:
\r
7289 UpdateICSWidth(hText);
\r
7290 return OnExitSizeMove( &sd, hDlg, wParam, lParam );
\r
7293 return DefWindowProc(hDlg, message, wParam, lParam);
\r
7301 if (hwndConsole) return;
\r
7302 hCons = CreateDialog(hInst, szConsoleName, 0, NULL);
\r
7303 SendMessage(hCons, WM_INITDIALOG, 0, 0);
\r
7308 ConsoleOutput(char* data, int length, int forceVisible)
\r
7313 char buf[CO_MAX+1];
\r
7316 static int delayLF = 0;
\r
7317 CHARRANGE savesel, sel;
\r
7319 if (hwndConsole == NULL || length > CO_MAX-100 || length == 0) return;
\r
7327 while (length--) {
\r
7335 } else if (*p == '\007') {
\r
7336 MyPlaySound(&sounds[(int)SoundBell]);
\r
7343 hText = GetDlgItem(hwndConsole, OPT_ConsoleText);
\r
7344 SendMessage(hText, EM_HIDESELECTION, TRUE, FALSE);
\r
7345 /* Save current selection */
\r
7346 SendMessage(hText, EM_EXGETSEL, 0, (LPARAM)&savesel);
\r
7347 exlen = GetWindowTextLength(hText);
\r
7348 /* Find out whether current end of text is visible */
\r
7349 SendMessage(hText, EM_GETRECT, 0, (LPARAM) &rect);
\r
7350 SendMessage(hText, EM_POSFROMCHAR, (WPARAM) &pEnd, exlen);
\r
7351 /* Trim existing text if it's too long */
\r
7352 if (exlen + (q - buf) > CO_MAX) {
\r
7353 trim = (CO_TRIM > (q - buf)) ? CO_TRIM : (q - buf);
\r
7356 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
7357 SendMessage(hText, EM_REPLACESEL, 0, (LPARAM)"");
\r
7359 savesel.cpMin -= trim;
\r
7360 savesel.cpMax -= trim;
\r
7361 if (exlen < 0) exlen = 0;
\r
7362 if (savesel.cpMin < 0) savesel.cpMin = 0;
\r
7363 if (savesel.cpMax < savesel.cpMin) savesel.cpMax = savesel.cpMin;
\r
7365 /* Append the new text */
\r
7366 sel.cpMin = exlen;
\r
7367 sel.cpMax = exlen;
\r
7368 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
7369 SendMessage(hText, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&consoleCF);
\r
7370 SendMessage(hText, EM_REPLACESEL, 0, (LPARAM) buf);
\r
7371 if (forceVisible || exlen == 0 ||
\r
7372 (rect.left <= pEnd.x && pEnd.x < rect.right &&
\r
7373 rect.top <= pEnd.y && pEnd.y < rect.bottom)) {
\r
7374 /* Scroll to make new end of text visible if old end of text
\r
7375 was visible or new text is an echo of user typein */
\r
7376 sel.cpMin = 9999999;
\r
7377 sel.cpMax = 9999999;
\r
7378 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
7379 SendMessage(hText, EM_HIDESELECTION, FALSE, FALSE);
\r
7380 SendMessage(hText, EM_SCROLLCARET, 0, 0);
\r
7381 SendMessage(hText, EM_HIDESELECTION, TRUE, FALSE);
\r
7383 if (savesel.cpMax == exlen || forceVisible) {
\r
7384 /* Move insert point to new end of text if it was at the old
\r
7385 end of text or if the new text is an echo of user typein */
\r
7386 sel.cpMin = 9999999;
\r
7387 sel.cpMax = 9999999;
\r
7388 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
7390 /* Restore previous selection */
\r
7391 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&savesel);
\r
7393 SendMessage(hText, EM_HIDESELECTION, FALSE, FALSE);
\r
7400 DisplayHoldingsCount(HDC hdc, int x, int y, int rightAlign, int copyNumber)
\r
7404 COLORREF oldFg, oldBg;
\r
7409 snprintf(buf, sizeof(buf)/sizeof(buf[0]),"%d", copyNumber); else buf[0] = 0;
\r
7411 oldFg = SetTextColor(hdc, RGB(255, 255, 255)); /* white */
\r
7412 oldBg = SetBkColor(hdc, RGB(0, 0, 0)); /* black */
\r
7413 oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);
\r
7416 rect.right = x + squareSize;
\r
7418 rect.bottom = y + squareSize;
\r
7421 ExtTextOut(hdc, x + MESSAGE_LINE_LEFTMARGIN
\r
7422 + (rightAlign ? (squareSize*2)/3 : 0),
\r
7423 y, ETO_CLIPPED|ETO_OPAQUE,
\r
7424 &rect, str, strlen(str), NULL);
\r
7426 (void) SetTextColor(hdc, oldFg);
\r
7427 (void) SetBkColor(hdc, oldBg);
\r
7428 (void) SelectObject(hdc, oldFont);
\r
7432 DisplayAClock(HDC hdc, int timeRemaining, int highlight,
\r
7433 RECT *rect, char *color, char *flagFell)
\r
7437 COLORREF oldFg, oldBg;
\r
7440 if (appData.clockMode) {
\r
7442 snprintf(buf, sizeof(buf)/sizeof(buf[0]), "%c %s %s", color[0], TimeString(timeRemaining), flagFell);
\r
7444 snprintf(buf, sizeof(buf)/sizeof(buf[0]), "%s:%c%s %s", color, (logoHeight>0 ? 0 : ' '), TimeString(timeRemaining), flagFell);
\r
7451 oldFg = SetTextColor(hdc, RGB(255, 255, 255)); /* white */
\r
7452 oldBg = SetBkColor(hdc, RGB(0, 0, 0)); /* black */
\r
7454 oldFg = SetTextColor(hdc, RGB(0, 0, 0)); /* black */
\r
7455 oldBg = SetBkColor(hdc, RGB(255, 255, 255)); /* white */
\r
7457 oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);
\r
7461 ExtTextOut(hdc, rect->left + MESSAGE_LINE_LEFTMARGIN,
\r
7462 rect->top, ETO_CLIPPED|ETO_OPAQUE,
\r
7463 rect, str, strlen(str), NULL);
\r
7464 if(logoHeight > 0 && appData.clockMode) {
\r
7466 str += strlen(color)+2;
\r
7467 r.top = rect->top + logoHeight/2;
\r
7468 r.left = rect->left;
\r
7469 r.right = rect->right;
\r
7470 r.bottom = rect->bottom;
\r
7471 ExtTextOut(hdc, rect->left + MESSAGE_LINE_LEFTMARGIN,
\r
7472 r.top, ETO_CLIPPED|ETO_OPAQUE,
\r
7473 &r, str, strlen(str), NULL);
\r
7475 (void) SetTextColor(hdc, oldFg);
\r
7476 (void) SetBkColor(hdc, oldBg);
\r
7477 (void) SelectObject(hdc, oldFont);
\r
7482 DoReadFile(HANDLE hFile, char *buf, int count, DWORD *outCount,
\r
7488 if( count <= 0 ) {
\r
7489 if (appData.debugMode) {
\r
7490 fprintf( debugFP, "DoReadFile: trying to read past end of buffer, overflow = %d\n", count );
\r
7493 return ERROR_INVALID_USER_BUFFER;
\r
7496 ResetEvent(ovl->hEvent);
\r
7497 ovl->Offset = ovl->OffsetHigh = 0;
\r
7498 ok = ReadFile(hFile, buf, count, outCount, ovl);
\r
7502 err = GetLastError();
\r
7503 if (err == ERROR_IO_PENDING) {
\r
7504 ok = GetOverlappedResult(hFile, ovl, outCount, TRUE);
\r
7508 err = GetLastError();
\r
7515 DoWriteFile(HANDLE hFile, char *buf, int count, DWORD *outCount,
\r
7520 ResetEvent(ovl->hEvent);
\r
7521 ovl->Offset = ovl->OffsetHigh = 0;
\r
7522 ok = WriteFile(hFile, buf, count, outCount, ovl);
\r
7526 err = GetLastError();
\r
7527 if (err == ERROR_IO_PENDING) {
\r
7528 ok = GetOverlappedResult(hFile, ovl, outCount, TRUE);
\r
7532 err = GetLastError();
\r
7538 /* [AS] If input is line by line and a line exceed the buffer size, force an error */
\r
7539 void CheckForInputBufferFull( InputSource * is )
\r
7541 if( is->lineByLine && (is->next - is->buf) >= INPUT_SOURCE_BUF_SIZE ) {
\r
7542 /* Look for end of line */
\r
7543 char * p = is->buf;
\r
7545 while( p < is->next && *p != '\n' ) {
\r
7549 if( p >= is->next ) {
\r
7550 if (appData.debugMode) {
\r
7551 fprintf( debugFP, "Input line exceeded buffer size (source id=%lu)\n", is->id );
\r
7554 is->error = ERROR_BROKEN_PIPE; /* [AS] Just any non-successful code! */
\r
7555 is->count = (DWORD) -1;
\r
7556 is->next = is->buf;
\r
7562 InputThread(LPVOID arg)
\r
7567 is = (InputSource *) arg;
\r
7568 ovl.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
\r
7569 ovl.Internal = ovl.InternalHigh = ovl.Offset = ovl.OffsetHigh = 0;
\r
7570 while (is->hThread != NULL) {
\r
7571 is->error = DoReadFile(is->hFile, is->next,
\r
7572 INPUT_SOURCE_BUF_SIZE - (is->next - is->buf),
\r
7573 &is->count, &ovl);
\r
7574 if (is->error == NO_ERROR) {
\r
7575 is->next += is->count;
\r
7577 if (is->error == ERROR_BROKEN_PIPE) {
\r
7578 /* Correct for MS brain damage. EOF reading a pipe is not an error. */
\r
7581 is->count = (DWORD) -1;
\r
7582 /* [AS] The (is->count <= 0) check below is not useful for unsigned values! */
\r
7587 CheckForInputBufferFull( is );
\r
7589 SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);
\r
7591 if( is->count == ((DWORD) -1) ) break; /* [AS] */
\r
7593 if (is->count <= 0) break; /* Quit on EOF or error */
\r
7596 CloseHandle(ovl.hEvent);
\r
7597 CloseHandle(is->hFile);
\r
7599 if (appData.debugMode) {
\r
7600 fprintf( debugFP, "Input thread terminated (id=%lu, error=%d, count=%ld)\n", is->id, is->error, is->count );
\r
7607 /* Windows 95 beta 2 won't let you do overlapped i/o on a console or pipe */
\r
7609 NonOvlInputThread(LPVOID arg)
\r
7616 is = (InputSource *) arg;
\r
7617 while (is->hThread != NULL) {
\r
7618 is->error = ReadFile(is->hFile, is->next,
\r
7619 INPUT_SOURCE_BUF_SIZE - (is->next - is->buf),
\r
7620 &is->count, NULL) ? NO_ERROR : GetLastError();
\r
7621 if (is->error == NO_ERROR) {
\r
7622 /* Change CRLF to LF */
\r
7623 if (is->next > is->buf) {
\r
7625 i = is->count + 1;
\r
7633 if (prev == '\r' && *p == '\n') {
\r
7645 if (is->error == ERROR_BROKEN_PIPE) {
\r
7646 /* Correct for MS brain damage. EOF reading a pipe is not an error. */
\r
7649 is->count = (DWORD) -1;
\r
7653 CheckForInputBufferFull( is );
\r
7655 SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);
\r
7657 if( is->count == ((DWORD) -1) ) break; /* [AS] */
\r
7659 if (is->count < 0) break; /* Quit on error */
\r
7661 CloseHandle(is->hFile);
\r
7666 SocketInputThread(LPVOID arg)
\r
7670 is = (InputSource *) arg;
\r
7671 while (is->hThread != NULL) {
\r
7672 is->count = recv(is->sock, is->buf, INPUT_SOURCE_BUF_SIZE, 0);
\r
7673 if ((int)is->count == SOCKET_ERROR) {
\r
7674 is->count = (DWORD) -1;
\r
7675 is->error = WSAGetLastError();
\r
7677 is->error = NO_ERROR;
\r
7678 is->next += is->count;
\r
7679 if (is->count == 0 && is->second == is) {
\r
7680 /* End of file on stderr; quit with no message */
\r
7684 SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);
\r
7686 if( is->count == ((DWORD) -1) ) break; /* [AS] */
\r
7688 if (is->count <= 0) break; /* Quit on EOF or error */
\r
7694 InputEvent(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
\r
7698 is = (InputSource *) lParam;
\r
7699 if (is->lineByLine) {
\r
7700 /* Feed in lines one by one */
\r
7701 char *p = is->buf;
\r
7703 while (q < is->next) {
\r
7704 if (*q++ == '\n') {
\r
7705 (is->func)(is, is->closure, p, q - p, NO_ERROR);
\r
7710 /* Move any partial line to the start of the buffer */
\r
7712 while (p < is->next) {
\r
7717 if (is->error != NO_ERROR || is->count == 0) {
\r
7718 /* Notify backend of the error. Note: If there was a partial
\r
7719 line at the end, it is not flushed through. */
\r
7720 (is->func)(is, is->closure, is->buf, is->count, is->error);
\r
7723 /* Feed in the whole chunk of input at once */
\r
7724 (is->func)(is, is->closure, is->buf, is->count, is->error);
\r
7725 is->next = is->buf;
\r
7729 /*---------------------------------------------------------------------------*\
\r
7731 * Menu enables. Used when setting various modes.
\r
7733 \*---------------------------------------------------------------------------*/
\r
7741 GreyRevert(Boolean grey)
\r
7742 { // [HGM] vari: for retracting variations in local mode
\r
7743 HMENU hmenu = GetMenu(hwndMain);
\r
7744 EnableMenuItem(hmenu, IDM_Revert, MF_BYCOMMAND|(grey ? MF_GRAYED : MF_ENABLED));
\r
7745 EnableMenuItem(hmenu, IDM_Annotate, MF_BYCOMMAND|(grey ? MF_GRAYED : MF_ENABLED));
\r
7749 SetMenuEnables(HMENU hmenu, Enables *enab)
\r
7751 while (enab->item > 0) {
\r
7752 (void) EnableMenuItem(hmenu, enab->item, enab->flags);
\r
7757 Enables gnuEnables[] = {
\r
7758 { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },
\r
7759 { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },
\r
7760 { IDM_IcsClient, MF_BYCOMMAND|MF_GRAYED },
\r
7761 { IDM_Accept, MF_BYCOMMAND|MF_GRAYED },
\r
7762 { IDM_Decline, MF_BYCOMMAND|MF_GRAYED },
\r
7763 { IDM_Rematch, MF_BYCOMMAND|MF_GRAYED },
\r
7764 { IDM_Adjourn, MF_BYCOMMAND|MF_GRAYED },
\r
7765 { IDM_StopExamining, MF_BYCOMMAND|MF_GRAYED },
\r
7766 { IDM_StopObserving, MF_BYCOMMAND|MF_GRAYED },
\r
7767 { IDM_Upload, MF_BYCOMMAND|MF_GRAYED },
\r
7768 { IDM_Revert, MF_BYCOMMAND|MF_GRAYED },
\r
7769 { IDM_Annotate, MF_BYCOMMAND|MF_GRAYED },
\r
7770 { IDM_NewChat, MF_BYCOMMAND|MF_GRAYED },
\r
7774 Enables icsEnables[] = {
\r
7775 { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },
\r
7776 { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },
\r
7777 { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },
\r
7778 { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },
\r
7779 { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },
\r
7780 { IDM_Match, MF_BYCOMMAND|MF_GRAYED },
\r
7781 { IDM_MachineBoth, MF_BYCOMMAND|MF_GRAYED },
\r
7782 { IDM_AnalysisMode, MF_BYCOMMAND|MF_ENABLED },
\r
7783 { IDM_AnalyzeFile, MF_BYCOMMAND|MF_GRAYED },
\r
7784 { IDM_TimeControl, MF_BYCOMMAND|MF_GRAYED },
\r
7785 { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },
\r
7786 { IDM_Hint, MF_BYCOMMAND|MF_GRAYED },
\r
7787 { IDM_Book, MF_BYCOMMAND|MF_GRAYED },
\r
7788 { IDM_IcsOptions, MF_BYCOMMAND|MF_ENABLED },
\r
7789 { IDM_Engine1Options, MF_BYCOMMAND|MF_GRAYED },
\r
7790 { IDM_Engine2Options, MF_BYCOMMAND|MF_GRAYED },
\r
7791 { IDM_Annotate, MF_BYCOMMAND|MF_GRAYED },
\r
7796 Enables zippyEnables[] = {
\r
7797 { IDM_MoveNow, MF_BYCOMMAND|MF_ENABLED },
\r
7798 { IDM_Hint, MF_BYCOMMAND|MF_ENABLED },
\r
7799 { IDM_Book, MF_BYCOMMAND|MF_ENABLED },
\r
7800 { IDM_Engine1Options, MF_BYCOMMAND|MF_ENABLED },
\r
7805 Enables ncpEnables[] = {
\r
7806 { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },
\r
7807 { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },
\r
7808 { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },
\r
7809 { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },
\r
7810 { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },
\r
7811 { IDM_Match, MF_BYCOMMAND|MF_GRAYED },
\r
7812 { IDM_AnalysisMode, MF_BYCOMMAND|MF_GRAYED },
\r
7813 { IDM_AnalyzeFile, MF_BYCOMMAND|MF_GRAYED },
\r
7814 { IDM_IcsClient, MF_BYCOMMAND|MF_GRAYED },
\r
7815 { ACTION_POS, MF_BYPOSITION|MF_GRAYED },
\r
7816 { IDM_Revert, MF_BYCOMMAND|MF_GRAYED },
\r
7817 { IDM_Annotate, MF_BYCOMMAND|MF_GRAYED },
\r
7818 { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },
\r
7819 { IDM_RetractMove, MF_BYCOMMAND|MF_GRAYED },
\r
7820 { IDM_TimeControl, MF_BYCOMMAND|MF_GRAYED },
\r
7821 { IDM_Hint, MF_BYCOMMAND|MF_GRAYED },
\r
7822 { IDM_Book, MF_BYCOMMAND|MF_GRAYED },
\r
7823 { IDM_MachineBoth, MF_BYCOMMAND|MF_GRAYED },
\r
7824 { IDM_NewChat, MF_BYCOMMAND|MF_GRAYED },
\r
7825 { IDM_Engine1Options, MF_BYCOMMAND|MF_GRAYED },
\r
7826 { IDM_Engine2Options, MF_BYCOMMAND|MF_GRAYED },
\r
7830 Enables trainingOnEnables[] = {
\r
7831 { IDM_EditComment, MF_BYCOMMAND|MF_GRAYED },
\r
7832 { IDM_Comment, MF_BYCOMMAND|MF_GRAYED },
\r
7833 { IDM_Pause, MF_BYCOMMAND|MF_GRAYED },
\r
7834 { IDM_Forward, MF_BYCOMMAND|MF_GRAYED },
\r
7835 { IDM_Backward, MF_BYCOMMAND|MF_GRAYED },
\r
7836 { IDM_ToEnd, MF_BYCOMMAND|MF_GRAYED },
\r
7837 { IDM_ToStart, MF_BYCOMMAND|MF_GRAYED },
\r
7838 { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },
\r
7839 { IDM_TruncateGame, MF_BYCOMMAND|MF_GRAYED },
\r
7843 Enables trainingOffEnables[] = {
\r
7844 { IDM_EditComment, MF_BYCOMMAND|MF_ENABLED },
\r
7845 { IDM_Comment, MF_BYCOMMAND|MF_ENABLED },
\r
7846 { IDM_Pause, MF_BYCOMMAND|MF_ENABLED },
\r
7847 { IDM_Forward, MF_BYCOMMAND|MF_ENABLED },
\r
7848 { IDM_Backward, MF_BYCOMMAND|MF_ENABLED },
\r
7849 { IDM_ToEnd, MF_BYCOMMAND|MF_ENABLED },
\r
7850 { IDM_ToStart, MF_BYCOMMAND|MF_ENABLED },
\r
7851 { IDM_MoveNow, MF_BYCOMMAND|MF_ENABLED },
\r
7852 { IDM_TruncateGame, MF_BYCOMMAND|MF_ENABLED },
\r
7856 /* These modify either ncpEnables or gnuEnables */
\r
7857 Enables cmailEnables[] = {
\r
7858 { IDM_MailMove, MF_BYCOMMAND|MF_ENABLED },
\r
7859 { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_ENABLED },
\r
7860 { ACTION_POS, MF_BYPOSITION|MF_ENABLED },
\r
7861 { IDM_CallFlag, MF_BYCOMMAND|MF_GRAYED },
\r
7862 { IDM_Draw, MF_BYCOMMAND|MF_ENABLED },
\r
7863 { IDM_Adjourn, MF_BYCOMMAND|MF_GRAYED },
\r
7864 { IDM_Abort, MF_BYCOMMAND|MF_GRAYED },
\r
7868 Enables machineThinkingEnables[] = {
\r
7869 { IDM_LoadGame, MF_BYCOMMAND|MF_GRAYED },
\r
7870 { IDM_LoadNextGame, MF_BYCOMMAND|MF_GRAYED },
\r
7871 { IDM_LoadPrevGame, MF_BYCOMMAND|MF_GRAYED },
\r
7872 { IDM_ReloadGame, MF_BYCOMMAND|MF_GRAYED },
\r
7873 { IDM_PasteGame, MF_BYCOMMAND|MF_GRAYED },
\r
7874 { IDM_LoadPosition, MF_BYCOMMAND|MF_GRAYED },
\r
7875 { IDM_LoadNextPosition, MF_BYCOMMAND|MF_GRAYED },
\r
7876 { IDM_LoadPrevPosition, MF_BYCOMMAND|MF_GRAYED },
\r
7877 { IDM_ReloadPosition, MF_BYCOMMAND|MF_GRAYED },
\r
7878 { IDM_PastePosition, MF_BYCOMMAND|MF_GRAYED },
\r
7879 { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },
\r
7880 { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },
\r
7881 { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },
\r
7882 { IDM_Match, MF_BYCOMMAND|MF_GRAYED },
\r
7883 { IDM_TypeInMove, MF_BYCOMMAND|MF_GRAYED },
\r
7884 { IDM_RetractMove, MF_BYCOMMAND|MF_GRAYED },
\r
7888 Enables userThinkingEnables[] = {
\r
7889 { IDM_LoadGame, MF_BYCOMMAND|MF_ENABLED },
\r
7890 { IDM_LoadNextGame, MF_BYCOMMAND|MF_ENABLED },
\r
7891 { IDM_LoadPrevGame, MF_BYCOMMAND|MF_ENABLED },
\r
7892 { IDM_ReloadGame, MF_BYCOMMAND|MF_ENABLED },
\r
7893 { IDM_PasteGame, MF_BYCOMMAND|MF_ENABLED },
\r
7894 { IDM_LoadPosition, MF_BYCOMMAND|MF_ENABLED },
\r
7895 { IDM_LoadNextPosition, MF_BYCOMMAND|MF_ENABLED },
\r
7896 { IDM_LoadPrevPosition, MF_BYCOMMAND|MF_ENABLED },
\r
7897 { IDM_ReloadPosition, MF_BYCOMMAND|MF_ENABLED },
\r
7898 { IDM_PastePosition, MF_BYCOMMAND|MF_ENABLED },
\r
7899 { IDM_MachineWhite, MF_BYCOMMAND|MF_ENABLED },
\r
7900 { IDM_MachineBlack, MF_BYCOMMAND|MF_ENABLED },
\r
7901 { IDM_TwoMachines, MF_BYCOMMAND|MF_ENABLED },
\r
7902 { IDM_Match, MF_BYCOMMAND|MF_ENABLED },
\r
7903 { IDM_TypeInMove, MF_BYCOMMAND|MF_ENABLED },
\r
7904 { IDM_RetractMove, MF_BYCOMMAND|MF_ENABLED },
\r
7908 /*---------------------------------------------------------------------------*\
\r
7910 * Front-end interface functions exported by XBoard.
\r
7911 * Functions appear in same order as prototypes in frontend.h.
\r
7913 \*---------------------------------------------------------------------------*/
\r
7917 static UINT prevChecked = 0;
\r
7918 static int prevPausing = 0;
\r
7921 if (pausing != prevPausing) {
\r
7922 prevPausing = pausing;
\r
7923 (void) CheckMenuItem(GetMenu(hwndMain), IDM_Pause,
\r
7924 MF_BYCOMMAND|(pausing ? MF_CHECKED : MF_UNCHECKED));
\r
7925 if (hwndPause) SetWindowText(hwndPause, pausing ? "C" : "P");
\r
7928 switch (gameMode) {
\r
7929 case BeginningOfGame:
\r
7930 if (appData.icsActive)
\r
7931 nowChecked = IDM_IcsClient;
\r
7932 else if (appData.noChessProgram)
\r
7933 nowChecked = IDM_EditGame;
\r
7935 nowChecked = IDM_MachineBlack;
\r
7937 case MachinePlaysBlack:
\r
7938 nowChecked = IDM_MachineBlack;
\r
7940 case MachinePlaysWhite:
\r
7941 nowChecked = IDM_MachineWhite;
\r
7943 case TwoMachinesPlay:
\r
7944 nowChecked = matchMode ? IDM_Match : IDM_TwoMachines; // [HGM] match
\r
7947 nowChecked = IDM_AnalysisMode;
\r
7950 nowChecked = IDM_AnalyzeFile;
\r
7953 nowChecked = IDM_EditGame;
\r
7955 case PlayFromGameFile:
\r
7956 nowChecked = IDM_LoadGame;
\r
7958 case EditPosition:
\r
7959 nowChecked = IDM_EditPosition;
\r
7962 nowChecked = IDM_Training;
\r
7964 case IcsPlayingWhite:
\r
7965 case IcsPlayingBlack:
\r
7966 case IcsObserving:
\r
7968 nowChecked = IDM_IcsClient;
\r
7975 if (prevChecked != 0)
\r
7976 (void) CheckMenuItem(GetMenu(hwndMain),
\r
7977 prevChecked, MF_BYCOMMAND|MF_UNCHECKED);
\r
7978 if (nowChecked != 0)
\r
7979 (void) CheckMenuItem(GetMenu(hwndMain),
\r
7980 nowChecked, MF_BYCOMMAND|MF_CHECKED);
\r
7982 if (nowChecked == IDM_LoadGame || nowChecked == IDM_Training) {
\r
7983 (void) EnableMenuItem(GetMenu(hwndMain), IDM_Training,
\r
7984 MF_BYCOMMAND|MF_ENABLED);
\r
7986 (void) EnableMenuItem(GetMenu(hwndMain),
\r
7987 IDM_Training, MF_BYCOMMAND|MF_GRAYED);
\r
7990 prevChecked = nowChecked;
\r
7992 /* [DM] icsEngineAnalyze - Do a sceure check too */
\r
7993 if (appData.icsActive) {
\r
7994 if (appData.icsEngineAnalyze) {
\r
7995 (void) CheckMenuItem(GetMenu(hwndMain), IDM_AnalysisMode,
\r
7996 MF_BYCOMMAND|MF_CHECKED);
\r
7998 (void) CheckMenuItem(GetMenu(hwndMain), IDM_AnalysisMode,
\r
7999 MF_BYCOMMAND|MF_UNCHECKED);
\r
8007 HMENU hmenu = GetMenu(hwndMain);
\r
8008 SetMenuEnables(hmenu, icsEnables);
\r
8009 EnableMenuItem(GetSubMenu(hmenu, OPTIONS_POS), ICS_POS,
\r
8010 MF_BYPOSITION|MF_ENABLED);
\r
8012 if (appData.zippyPlay) {
\r
8013 SetMenuEnables(hmenu, zippyEnables);
\r
8014 if (!appData.noChessProgram) /* [DM] icsEngineAnalyze */
\r
8015 (void) EnableMenuItem(GetMenu(hwndMain), IDM_AnalysisMode,
\r
8016 MF_BYCOMMAND|MF_ENABLED);
\r
8024 SetMenuEnables(GetMenu(hwndMain), gnuEnables);
\r
8030 HMENU hmenu = GetMenu(hwndMain);
\r
8031 SetMenuEnables(hmenu, ncpEnables);
\r
8032 EnableMenuItem(GetSubMenu(hmenu, OPTIONS_POS), SOUNDS_POS,
\r
8033 MF_BYPOSITION|MF_GRAYED);
\r
8034 DrawMenuBar(hwndMain);
\r
8040 SetMenuEnables(GetMenu(hwndMain), cmailEnables);
\r
8044 SetTrainingModeOn()
\r
8047 SetMenuEnables(GetMenu(hwndMain), trainingOnEnables);
\r
8048 for (i = 0; i < N_BUTTONS; i++) {
\r
8049 if (buttonDesc[i].hwnd != NULL)
\r
8050 EnableWindow(buttonDesc[i].hwnd, FALSE);
\r
8055 VOID SetTrainingModeOff()
\r
8058 SetMenuEnables(GetMenu(hwndMain), trainingOffEnables);
\r
8059 for (i = 0; i < N_BUTTONS; i++) {
\r
8060 if (buttonDesc[i].hwnd != NULL)
\r
8061 EnableWindow(buttonDesc[i].hwnd, TRUE);
\r
8067 SetUserThinkingEnables()
\r
8069 SetMenuEnables(GetMenu(hwndMain), userThinkingEnables);
\r
8073 SetMachineThinkingEnables()
\r
8075 HMENU hMenu = GetMenu(hwndMain);
\r
8076 int flags = MF_BYCOMMAND|MF_ENABLED;
\r
8078 SetMenuEnables(hMenu, machineThinkingEnables);
\r
8080 if (gameMode == MachinePlaysBlack) {
\r
8081 (void)EnableMenuItem(hMenu, IDM_MachineBlack, flags);
\r
8082 } else if (gameMode == MachinePlaysWhite) {
\r
8083 (void)EnableMenuItem(hMenu, IDM_MachineWhite, flags);
\r
8084 } else if (gameMode == TwoMachinesPlay) {
\r
8085 (void)EnableMenuItem(hMenu, matchMode ? IDM_Match : IDM_TwoMachines, flags); // [HGM] match
\r
8091 DisplayTitle(char *str)
\r
8093 char title[MSG_SIZ], *host;
\r
8094 if (str[0] != NULLCHAR) {
\r
8095 safeStrCpy(title, str, sizeof(title)/sizeof(title[0]) );
\r
8096 } else if (appData.icsActive) {
\r
8097 if (appData.icsCommPort[0] != NULLCHAR)
\r
8100 host = appData.icsHost;
\r
8101 snprintf(title, MSG_SIZ, "%s: %s", szTitle, host);
\r
8102 } else if (appData.noChessProgram) {
\r
8103 safeStrCpy(title, szTitle, sizeof(title)/sizeof(title[0]) );
\r
8105 safeStrCpy(title, szTitle, sizeof(title)/sizeof(title[0]) );
\r
8106 strcat(title, ": ");
\r
8107 strcat(title, first.tidy);
\r
8109 SetWindowText(hwndMain, title);
\r
8114 DisplayMessage(char *str1, char *str2)
\r
8118 int remain = MESSAGE_TEXT_MAX - 1;
\r
8121 moveErrorMessageUp = FALSE; /* turned on later by caller if needed */
\r
8122 messageText[0] = NULLCHAR;
\r
8124 len = strlen(str1);
\r
8125 if (len > remain) len = remain;
\r
8126 strncpy(messageText, str1, len);
\r
8127 messageText[len] = NULLCHAR;
\r
8130 if (*str2 && remain >= 2) {
\r
8132 strcat(messageText, " ");
\r
8135 len = strlen(str2);
\r
8136 if (len > remain) len = remain;
\r
8137 strncat(messageText, str2, len);
\r
8139 messageText[MESSAGE_TEXT_MAX - 1] = NULLCHAR;
\r
8141 if (hwndMain == NULL || IsIconic(hwndMain)) return;
\r
8145 hdc = GetDC(hwndMain);
\r
8146 oldFont = SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);
\r
8147 ExtTextOut(hdc, messageRect.left, messageRect.top, ETO_CLIPPED|ETO_OPAQUE,
\r
8148 &messageRect, messageText, strlen(messageText), NULL);
\r
8149 (void) SelectObject(hdc, oldFont);
\r
8150 (void) ReleaseDC(hwndMain, hdc);
\r
8154 DisplayError(char *str, int error)
\r
8156 char buf[MSG_SIZ*2], buf2[MSG_SIZ];
\r
8160 safeStrCpy(buf, str, sizeof(buf)/sizeof(buf[0]) );
\r
8162 len = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,
\r
8163 NULL, error, LANG_NEUTRAL,
\r
8164 (LPSTR) buf2, MSG_SIZ, NULL);
\r
8166 snprintf(buf, 2*MSG_SIZ, "%s:\n%s", str, buf2);
\r
8168 ErrorMap *em = errmap;
\r
8169 while (em->err != 0 && em->err != error) em++;
\r
8170 if (em->err != 0) {
\r
8171 snprintf(buf, 2*MSG_SIZ, "%s:\n%s", str, em->msg);
\r
8173 snprintf(buf, 2*MSG_SIZ, "%s:\nError code %d", str, error);
\r
8178 ErrorPopUp(_("Error"), buf);
\r
8183 DisplayMoveError(char *str)
\r
8185 fromX = fromY = -1;
\r
8186 ClearHighlights();
\r
8187 DrawPosition(FALSE, NULL);
\r
8188 if (appData.popupMoveErrors) {
\r
8189 ErrorPopUp(_("Error"), str);
\r
8191 DisplayMessage(str, "");
\r
8192 moveErrorMessageUp = TRUE;
\r
8197 DisplayFatalError(char *str, int error, int exitStatus)
\r
8199 char buf[2*MSG_SIZ], buf2[MSG_SIZ];
\r
8201 char *label = exitStatus ? _("Fatal Error") : _("Exiting");
\r
8204 len = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,
\r
8205 NULL, error, LANG_NEUTRAL,
\r
8206 (LPSTR) buf2, MSG_SIZ, NULL);
\r
8208 snprintf(buf, 2*MSG_SIZ, "%s:\n%s", str, buf2);
\r
8210 ErrorMap *em = errmap;
\r
8211 while (em->err != 0 && em->err != error) em++;
\r
8212 if (em->err != 0) {
\r
8213 snprintf(buf, 2*MSG_SIZ, "%s:\n%s", str, em->msg);
\r
8215 snprintf(buf, 2*MSG_SIZ, "%s:\nError code %d", str, error);
\r
8220 if (appData.debugMode) {
\r
8221 fprintf(debugFP, "%s: %s\n", label, str);
\r
8223 if (appData.popupExitMessage) {
\r
8224 (void) MessageBox(hwndMain, str, label, MB_OK|
\r
8225 (exitStatus ? MB_ICONSTOP : MB_ICONINFORMATION));
\r
8227 ExitEvent(exitStatus);
\r
8232 DisplayInformation(char *str)
\r
8234 (void) MessageBox(hwndMain, str, _("Information"), MB_OK|MB_ICONINFORMATION);
\r
8239 DisplayNote(char *str)
\r
8241 ErrorPopUp(_("Note"), str);
\r
8246 char *title, *question, *replyPrefix;
\r
8251 QuestionDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
8253 static QuestionParams *qp;
\r
8254 char reply[MSG_SIZ];
\r
8257 switch (message) {
\r
8258 case WM_INITDIALOG:
\r
8259 qp = (QuestionParams *) lParam;
\r
8260 CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));
\r
8261 Translate(hDlg, DLG_Question);
\r
8262 SetWindowText(hDlg, qp->title);
\r
8263 SetDlgItemText(hDlg, OPT_QuestionText, qp->question);
\r
8264 SetFocus(GetDlgItem(hDlg, OPT_QuestionInput));
\r
8268 switch (LOWORD(wParam)) {
\r
8270 safeStrCpy(reply, qp->replyPrefix, sizeof(reply)/sizeof(reply[0]) );
\r
8271 if (*reply) strcat(reply, " ");
\r
8272 len = strlen(reply);
\r
8273 GetDlgItemText(hDlg, OPT_QuestionInput, reply + len, sizeof(reply) - len);
\r
8274 strcat(reply, "\n");
\r
8275 OutputToProcess(qp->pr, reply, strlen(reply), &err);
\r
8276 EndDialog(hDlg, TRUE);
\r
8277 if (err) DisplayFatalError(_("Error writing to chess program"), err, 1);
\r
8280 EndDialog(hDlg, FALSE);
\r
8291 AskQuestion(char* title, char *question, char *replyPrefix, ProcRef pr)
\r
8293 QuestionParams qp;
\r
8297 qp.question = question;
\r
8298 qp.replyPrefix = replyPrefix;
\r
8300 lpProc = MakeProcInstance((FARPROC)QuestionDialog, hInst);
\r
8301 DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_Question),
\r
8302 hwndMain, (DLGPROC)lpProc, (LPARAM)&qp);
\r
8303 FreeProcInstance(lpProc);
\r
8306 /* [AS] Pick FRC position */
\r
8307 LRESULT CALLBACK NewGameFRC_Proc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
8309 static int * lpIndexFRC;
\r
8315 case WM_INITDIALOG:
\r
8316 lpIndexFRC = (int *) lParam;
\r
8318 CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));
\r
8319 Translate(hDlg, DLG_NewGameFRC);
\r
8321 SendDlgItemMessage( hDlg, IDC_NFG_Edit, EM_SETLIMITTEXT, sizeof(buf)-1, 0 );
\r
8322 SetDlgItemInt( hDlg, IDC_NFG_Edit, *lpIndexFRC, TRUE );
\r
8323 SendDlgItemMessage( hDlg, IDC_NFG_Edit, EM_SETSEL, 0, -1 );
\r
8324 SetFocus(GetDlgItem(hDlg, IDC_NFG_Edit));
\r
8329 switch( LOWORD(wParam) ) {
\r
8331 *lpIndexFRC = GetDlgItemInt(hDlg, IDC_NFG_Edit, &index_is_ok, TRUE );
\r
8332 EndDialog( hDlg, 0 );
\r
8333 shuffleOpenings = TRUE; /* [HGM] shuffle: switch shuffling on for as long as we stay in current variant */
\r
8336 EndDialog( hDlg, 1 );
\r
8338 case IDC_NFG_Edit:
\r
8339 if( HIWORD(wParam) == EN_CHANGE ) {
\r
8340 GetDlgItemInt(hDlg, IDC_NFG_Edit, &index_is_ok, TRUE );
\r
8342 EnableWindow( GetDlgItem(hDlg, IDOK), index_is_ok );
\r
8345 case IDC_NFG_Random:
\r
8346 snprintf( buf, sizeof(buf)/sizeof(buf[0]), "%d", myrandom() ); /* [HGM] shuffle: no longer limit to 960 */
\r
8347 SetDlgItemText(hDlg, IDC_NFG_Edit, buf );
\r
8360 int index = appData.defaultFrcPosition;
\r
8361 FARPROC lpProc = MakeProcInstance( (FARPROC) NewGameFRC_Proc, hInst );
\r
8363 result = DialogBoxParam( hInst, MAKEINTRESOURCE(DLG_NewGameFRC), hwndMain, (DLGPROC)lpProc, (LPARAM)&index );
\r
8365 if( result == 0 ) {
\r
8366 appData.defaultFrcPosition = index;
\r
8372 /* [AS] Game list options. Refactored by HGM */
\r
8374 HWND gameListOptionsDialog;
\r
8376 // low-level front-end: clear text edit / list widget
\r
8380 SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_RESETCONTENT, 0, 0 );
\r
8383 // low-level front-end: clear text edit / list widget
\r
8385 GLT_DeSelectList()
\r
8387 SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_SETCURSEL, 0, 0 );
\r
8390 // low-level front-end: append line to text edit / list widget
\r
8392 GLT_AddToList( char *name )
\r
8395 SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_ADDSTRING, 0, (LPARAM) name );
\r
8399 // low-level front-end: get line from text edit / list widget
\r
8401 GLT_GetFromList( int index, char *name )
\r
8404 if( SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_GETTEXT, index, (LPARAM) name ) != LB_ERR )
\r
8410 void GLT_MoveSelection( HWND hDlg, int delta )
\r
8412 int idx1 = (int) SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETCURSEL, 0, 0 );
\r
8413 int idx2 = idx1 + delta;
\r
8414 int count = (int) SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETCOUNT, 0, 0 );
\r
8416 if( idx1 >=0 && idx1 < count && idx2 >= 0 && idx2 < count ) {
\r
8419 SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETTEXT, idx1, (LPARAM) buf );
\r
8420 SendDlgItemMessage( hDlg, IDC_GameListTags, LB_DELETESTRING, idx1, 0 );
\r
8421 SendDlgItemMessage( hDlg, IDC_GameListTags, LB_INSERTSTRING, idx2, (LPARAM) buf );
\r
8422 SendDlgItemMessage( hDlg, IDC_GameListTags, LB_SETCURSEL, idx2, 0 );
\r
8426 LRESULT CALLBACK GameListOptions_Proc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
8430 case WM_INITDIALOG:
\r
8431 gameListOptionsDialog = hDlg; // [HGM] pass through global to keep out off back-end
\r
8433 CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));
\r
8434 Translate(hDlg, DLG_GameListOptions);
\r
8436 /* Initialize list */
\r
8437 GLT_TagsToList( lpUserGLT );
\r
8439 SetFocus( GetDlgItem(hDlg, IDC_GameListTags) );
\r
8444 switch( LOWORD(wParam) ) {
\r
8447 EndDialog( hDlg, 0 );
\r
8450 EndDialog( hDlg, 1 );
\r
8453 case IDC_GLT_Default:
\r
8454 GLT_TagsToList( GLT_DEFAULT_TAGS );
\r
8457 case IDC_GLT_Restore:
\r
8458 GLT_TagsToList( appData.gameListTags );
\r
8462 GLT_MoveSelection( hDlg, -1 );
\r
8465 case IDC_GLT_Down:
\r
8466 GLT_MoveSelection( hDlg, +1 );
\r
8476 int GameListOptions()
\r
8479 FARPROC lpProc = MakeProcInstance( (FARPROC) GameListOptions_Proc, hInst );
\r
8481 safeStrCpy( lpUserGLT, appData.gameListTags ,LPUSERGLT_SIZE );
\r
8483 result = DialogBoxParam( hInst, MAKEINTRESOURCE(DLG_GameListOptions), hwndMain, (DLGPROC)lpProc, (LPARAM)lpUserGLT );
\r
8485 if( result == 0 ) {
\r
8486 /* [AS] Memory leak here! */
\r
8487 appData.gameListTags = strdup( lpUserGLT );
\r
8494 DisplayIcsInteractionTitle(char *str)
\r
8496 char consoleTitle[MSG_SIZ];
\r
8498 snprintf(consoleTitle, MSG_SIZ, "%s: %s", szConsoleTitle, str);
\r
8499 SetWindowText(hwndConsole, consoleTitle);
\r
8503 DrawPosition(int fullRedraw, Board board)
\r
8505 HDCDrawPosition(NULL, (BOOLEAN) fullRedraw, board);
\r
8508 void NotifyFrontendLogin()
\r
8511 UpdateICSWidth(GetDlgItem(hwndConsole, OPT_ConsoleText));
\r
8517 fromX = fromY = -1;
\r
8518 if (dragInfo.pos.x != -1 || dragInfo.pos.y != -1) {
\r
8519 dragInfo.pos.x = dragInfo.pos.y = -1;
\r
8520 dragInfo.pos.x = dragInfo.pos.y = -1;
\r
8521 dragInfo.lastpos = dragInfo.pos;
\r
8522 dragInfo.start.x = dragInfo.start.y = -1;
\r
8523 dragInfo.from = dragInfo.start;
\r
8525 DrawPosition(TRUE, NULL);
\r
8532 CommentPopUp(char *title, char *str)
\r
8534 HWND hwnd = GetActiveWindow();
\r
8535 EitherCommentPopUp(currentMove, title, str, FALSE); // [HGM] vari: fake move index, rather than 0
\r
8537 SetActiveWindow(hwnd);
\r
8541 CommentPopDown(void)
\r
8543 CheckMenuItem(GetMenu(hwndMain), IDM_Comment, MF_UNCHECKED);
\r
8544 if (commentDialog) {
\r
8545 ShowWindow(commentDialog, SW_HIDE);
\r
8547 commentUp = FALSE;
\r
8551 EditCommentPopUp(int index, char *title, char *str)
\r
8553 EitherCommentPopUp(index, title, str, TRUE);
\r
8560 MyPlaySound(&sounds[(int)SoundMove]);
\r
8563 VOID PlayIcsWinSound()
\r
8565 MyPlaySound(&sounds[(int)SoundIcsWin]);
\r
8568 VOID PlayIcsLossSound()
\r
8570 MyPlaySound(&sounds[(int)SoundIcsLoss]);
\r
8573 VOID PlayIcsDrawSound()
\r
8575 MyPlaySound(&sounds[(int)SoundIcsDraw]);
\r
8578 VOID PlayIcsUnfinishedSound()
\r
8580 MyPlaySound(&sounds[(int)SoundIcsUnfinished]);
\r
8586 MyPlaySound(&sounds[(int)SoundAlarm]);
\r
8594 consoleEcho = TRUE;
\r
8595 hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
8596 SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&consoleCF);
\r
8597 SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);
\r
8606 consoleEcho = FALSE;
\r
8607 hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
8608 /* This works OK: set text and background both to the same color */
\r
8610 cf.crTextColor = COLOR_ECHOOFF;
\r
8611 SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf);
\r
8612 SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, cf.crTextColor);
\r
8615 /* No Raw()...? */
\r
8617 void Colorize(ColorClass cc, int continuation)
\r
8619 currentColorClass = cc;
\r
8620 consoleCF.dwMask = CFM_COLOR|CFM_BOLD|CFM_ITALIC|CFM_UNDERLINE|CFM_STRIKEOUT;
\r
8621 consoleCF.crTextColor = textAttribs[cc].color;
\r
8622 consoleCF.dwEffects = textAttribs[cc].effects;
\r
8623 if (!continuation) MyPlaySound(&textAttribs[cc].sound);
\r
8629 static char buf[MSG_SIZ];
\r
8630 DWORD bufsiz = MSG_SIZ;
\r
8632 if(appData.userName != NULL && appData.userName[0] != 0) {
\r
8633 return appData.userName; /* [HGM] username: prefer name selected by user over his system login */
\r
8635 if (!GetUserName(buf, &bufsiz)) {
\r
8636 /*DisplayError("Error getting user name", GetLastError());*/
\r
8637 safeStrCpy(buf, _("User"), sizeof(buf)/sizeof(buf[0]) );
\r
8645 static char buf[MSG_SIZ];
\r
8646 DWORD bufsiz = MSG_SIZ;
\r
8648 if (!GetComputerName(buf, &bufsiz)) {
\r
8649 /*DisplayError("Error getting host name", GetLastError());*/
\r
8650 safeStrCpy(buf, _("Unknown"), sizeof(buf)/sizeof(buf[0]) );
\r
8657 ClockTimerRunning()
\r
8659 return clockTimerEvent != 0;
\r
8665 if (clockTimerEvent == 0) return FALSE;
\r
8666 KillTimer(hwndMain, clockTimerEvent);
\r
8667 clockTimerEvent = 0;
\r
8672 StartClockTimer(long millisec)
\r
8674 clockTimerEvent = SetTimer(hwndMain, (UINT) CLOCK_TIMER_ID,
\r
8675 (UINT) millisec, NULL);
\r
8679 DisplayWhiteClock(long timeRemaining, int highlight)
\r
8682 char *flag = whiteFlag && gameMode == TwoMachinesPlay ? "(!)" : "";
\r
8684 if(appData.noGUI) return;
\r
8685 hdc = GetDC(hwndMain);
\r
8686 if (!IsIconic(hwndMain)) {
\r
8687 DisplayAClock(hdc, timeRemaining, highlight,
\r
8688 flipClock ? &blackRect : &whiteRect, _("White"), flag);
\r
8690 if (highlight && iconCurrent == iconBlack) {
\r
8691 iconCurrent = iconWhite;
\r
8692 PostMessage(hwndMain, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);
\r
8693 if (IsIconic(hwndMain)) {
\r
8694 DrawIcon(hdc, 2, 2, iconCurrent);
\r
8697 (void) ReleaseDC(hwndMain, hdc);
\r
8699 PostMessage(hwndConsole, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);
\r
8703 DisplayBlackClock(long timeRemaining, int highlight)
\r
8706 char *flag = blackFlag && gameMode == TwoMachinesPlay ? "(!)" : "";
\r
8708 if(appData.noGUI) return;
\r
8709 hdc = GetDC(hwndMain);
\r
8710 if (!IsIconic(hwndMain)) {
\r
8711 DisplayAClock(hdc, timeRemaining, highlight,
\r
8712 flipClock ? &whiteRect : &blackRect, _("Black"), flag);
\r
8714 if (highlight && iconCurrent == iconWhite) {
\r
8715 iconCurrent = iconBlack;
\r
8716 PostMessage(hwndMain, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);
\r
8717 if (IsIconic(hwndMain)) {
\r
8718 DrawIcon(hdc, 2, 2, iconCurrent);
\r
8721 (void) ReleaseDC(hwndMain, hdc);
\r
8723 PostMessage(hwndConsole, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);
\r
8728 LoadGameTimerRunning()
\r
8730 return loadGameTimerEvent != 0;
\r
8734 StopLoadGameTimer()
\r
8736 if (loadGameTimerEvent == 0) return FALSE;
\r
8737 KillTimer(hwndMain, loadGameTimerEvent);
\r
8738 loadGameTimerEvent = 0;
\r
8743 StartLoadGameTimer(long millisec)
\r
8745 loadGameTimerEvent = SetTimer(hwndMain, (UINT) LOAD_GAME_TIMER_ID,
\r
8746 (UINT) millisec, NULL);
\r
8754 char fileTitle[MSG_SIZ];
\r
8756 defName = DefaultFileName(appData.oldSaveStyle ? "gam" : "pgn");
\r
8757 f = OpenFileDialog(hwndMain, "a", defName,
\r
8758 appData.oldSaveStyle ? "gam" : "pgn",
\r
8760 _("Save Game to File"), NULL, fileTitle, NULL);
\r
8762 SaveGame(f, 0, "");
\r
8769 ScheduleDelayedEvent(DelayedEventCallback cb, long millisec)
\r
8771 if (delayedTimerEvent != 0) {
\r
8772 if (appData.debugMode && cb != delayedTimerCallback) { // [HGM] alive: not too much debug
\r
8773 fprintf(debugFP, "ScheduleDelayedEvent: event already scheduled\n");
\r
8775 KillTimer(hwndMain, delayedTimerEvent);
\r
8776 delayedTimerEvent = 0;
\r
8777 if(delayedTimerCallback != cb) // [HGM] alive: do not "flush" same event, just postpone it
\r
8778 delayedTimerCallback();
\r
8780 delayedTimerCallback = cb;
\r
8781 delayedTimerEvent = SetTimer(hwndMain, (UINT) DELAYED_TIMER_ID,
\r
8782 (UINT) millisec, NULL);
\r
8785 DelayedEventCallback
\r
8788 if (delayedTimerEvent) {
\r
8789 return delayedTimerCallback;
\r
8796 CancelDelayedEvent()
\r
8798 if (delayedTimerEvent) {
\r
8799 KillTimer(hwndMain, delayedTimerEvent);
\r
8800 delayedTimerEvent = 0;
\r
8804 DWORD GetWin32Priority(int nice)
\r
8805 { // [HGM] nice: translate Unix nice() value to indows priority class. (Code stolen from Polyglot 1.4w11)
\r
8807 REALTIME_PRIORITY_CLASS 0x00000100
\r
8808 HIGH_PRIORITY_CLASS 0x00000080
\r
8809 ABOVE_NORMAL_PRIORITY_CLASS 0x00008000
\r
8810 NORMAL_PRIORITY_CLASS 0x00000020
\r
8811 BELOW_NORMAL_PRIORITY_CLASS 0x00004000
\r
8812 IDLE_PRIORITY_CLASS 0x00000040
\r
8814 if (nice < -15) return 0x00000080;
\r
8815 if (nice < 0) return 0x00008000;
\r
8816 if (nice == 0) return 0x00000020;
\r
8817 if (nice < 15) return 0x00004000;
\r
8818 return 0x00000040;
\r
8821 /* Start a child process running the given program.
\r
8822 The process's standard output can be read from "from", and its
\r
8823 standard input can be written to "to".
\r
8824 Exit with fatal error if anything goes wrong.
\r
8825 Returns an opaque pointer that can be used to destroy the process
\r
8829 StartChildProcess(char *cmdLine, char *dir, ProcRef *pr)
\r
8831 #define BUFSIZE 4096
\r
8833 HANDLE hChildStdinRd, hChildStdinWr,
\r
8834 hChildStdoutRd, hChildStdoutWr;
\r
8835 HANDLE hChildStdinWrDup, hChildStdoutRdDup;
\r
8836 SECURITY_ATTRIBUTES saAttr;
\r
8838 PROCESS_INFORMATION piProcInfo;
\r
8839 STARTUPINFO siStartInfo;
\r
8841 char buf[MSG_SIZ];
\r
8844 if (appData.debugMode) {
\r
8845 fprintf(debugFP, "StartChildProcess (dir=\"%s\") %s\n", dir, cmdLine);
\r
8850 /* Set the bInheritHandle flag so pipe handles are inherited. */
\r
8851 saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);
\r
8852 saAttr.bInheritHandle = TRUE;
\r
8853 saAttr.lpSecurityDescriptor = NULL;
\r
8856 * The steps for redirecting child's STDOUT:
\r
8857 * 1. Create anonymous pipe to be STDOUT for child.
\r
8858 * 2. Create a noninheritable duplicate of read handle,
\r
8859 * and close the inheritable read handle.
\r
8862 /* Create a pipe for the child's STDOUT. */
\r
8863 if (! CreatePipe(&hChildStdoutRd, &hChildStdoutWr, &saAttr, 0)) {
\r
8864 return GetLastError();
\r
8867 /* Duplicate the read handle to the pipe, so it is not inherited. */
\r
8868 fSuccess = DuplicateHandle(GetCurrentProcess(), hChildStdoutRd,
\r
8869 GetCurrentProcess(), &hChildStdoutRdDup, 0,
\r
8870 FALSE, /* not inherited */
\r
8871 DUPLICATE_SAME_ACCESS);
\r
8873 return GetLastError();
\r
8875 CloseHandle(hChildStdoutRd);
\r
8878 * The steps for redirecting child's STDIN:
\r
8879 * 1. Create anonymous pipe to be STDIN for child.
\r
8880 * 2. Create a noninheritable duplicate of write handle,
\r
8881 * and close the inheritable write handle.
\r
8884 /* Create a pipe for the child's STDIN. */
\r
8885 if (! CreatePipe(&hChildStdinRd, &hChildStdinWr, &saAttr, 0)) {
\r
8886 return GetLastError();
\r
8889 /* Duplicate the write handle to the pipe, so it is not inherited. */
\r
8890 fSuccess = DuplicateHandle(GetCurrentProcess(), hChildStdinWr,
\r
8891 GetCurrentProcess(), &hChildStdinWrDup, 0,
\r
8892 FALSE, /* not inherited */
\r
8893 DUPLICATE_SAME_ACCESS);
\r
8895 return GetLastError();
\r
8897 CloseHandle(hChildStdinWr);
\r
8899 /* Arrange to (1) look in dir for the child .exe file, and
\r
8900 * (2) have dir be the child's working directory. Interpret
\r
8901 * dir relative to the directory WinBoard loaded from. */
\r
8902 GetCurrentDirectory(MSG_SIZ, buf);
\r
8903 SetCurrentDirectory(installDir);
\r
8904 SetCurrentDirectory(dir);
\r
8906 /* Now create the child process. */
\r
8908 siStartInfo.cb = sizeof(STARTUPINFO);
\r
8909 siStartInfo.lpReserved = NULL;
\r
8910 siStartInfo.lpDesktop = NULL;
\r
8911 siStartInfo.lpTitle = NULL;
\r
8912 siStartInfo.dwFlags = STARTF_USESTDHANDLES;
\r
8913 siStartInfo.cbReserved2 = 0;
\r
8914 siStartInfo.lpReserved2 = NULL;
\r
8915 siStartInfo.hStdInput = hChildStdinRd;
\r
8916 siStartInfo.hStdOutput = hChildStdoutWr;
\r
8917 siStartInfo.hStdError = hChildStdoutWr;
\r
8919 fSuccess = CreateProcess(NULL,
\r
8920 cmdLine, /* command line */
\r
8921 NULL, /* process security attributes */
\r
8922 NULL, /* primary thread security attrs */
\r
8923 TRUE, /* handles are inherited */
\r
8924 DETACHED_PROCESS|CREATE_NEW_PROCESS_GROUP,
\r
8925 NULL, /* use parent's environment */
\r
8927 &siStartInfo, /* STARTUPINFO pointer */
\r
8928 &piProcInfo); /* receives PROCESS_INFORMATION */
\r
8930 err = GetLastError();
\r
8931 SetCurrentDirectory(buf); /* return to prev directory */
\r
8936 if (appData.niceEngines){ // [HGM] nice: adjust engine proc priority
\r
8937 if(appData.debugMode) fprintf(debugFP, "nice engine proc to %d\n", appData.niceEngines);
\r
8938 SetPriorityClass(piProcInfo.hProcess, GetWin32Priority(appData.niceEngines));
\r
8941 /* Close the handles we don't need in the parent */
\r
8942 CloseHandle(piProcInfo.hThread);
\r
8943 CloseHandle(hChildStdinRd);
\r
8944 CloseHandle(hChildStdoutWr);
\r
8946 /* Prepare return value */
\r
8947 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
\r
8948 cp->kind = CPReal;
\r
8949 cp->hProcess = piProcInfo.hProcess;
\r
8950 cp->pid = piProcInfo.dwProcessId;
\r
8951 cp->hFrom = hChildStdoutRdDup;
\r
8952 cp->hTo = hChildStdinWrDup;
\r
8954 *pr = (void *) cp;
\r
8956 /* Klaus Friedel says that this Sleep solves a problem under Windows
\r
8957 2000 where engines sometimes don't see the initial command(s)
\r
8958 from WinBoard and hang. I don't understand how that can happen,
\r
8959 but the Sleep is harmless, so I've put it in. Others have also
\r
8960 reported what may be the same problem, so hopefully this will fix
\r
8961 it for them too. */
\r
8969 DestroyChildProcess(ProcRef pr, int/*boolean*/ signal)
\r
8971 ChildProc *cp; int result;
\r
8973 cp = (ChildProc *) pr;
\r
8974 if (cp == NULL) return;
\r
8976 switch (cp->kind) {
\r
8978 /* TerminateProcess is considered harmful, so... */
\r
8979 CloseHandle(cp->hTo); /* Closing this will give the child an EOF and hopefully kill it */
\r
8980 if (cp->hFrom) CloseHandle(cp->hFrom); /* if NULL, InputThread will close it */
\r
8981 /* The following doesn't work because the chess program
\r
8982 doesn't "have the same console" as WinBoard. Maybe
\r
8983 we could arrange for this even though neither WinBoard
\r
8984 nor the chess program uses a console for stdio? */
\r
8985 /*!!if (signal) GenerateConsoleCtrlEvent(CTRL_BREAK_EVENT, cp->pid);*/
\r
8987 /* [AS] Special termination modes for misbehaving programs... */
\r
8988 if( signal == 9 ) {
\r
8989 result = TerminateProcess( cp->hProcess, 0 );
\r
8991 if ( appData.debugMode) {
\r
8992 fprintf( debugFP, "Terminating process %lu, result=%d\n", cp->pid, result );
\r
8995 else if( signal == 10 ) {
\r
8996 DWORD dw = WaitForSingleObject( cp->hProcess, 3*1000 ); // Wait 3 seconds at most
\r
8998 if( dw != WAIT_OBJECT_0 ) {
\r
8999 result = TerminateProcess( cp->hProcess, 0 );
\r
9001 if ( appData.debugMode) {
\r
9002 fprintf( debugFP, "Process %lu still alive after timeout, killing... result=%d\n", cp->pid, result );
\r
9008 CloseHandle(cp->hProcess);
\r
9012 if (cp->hFrom) CloseHandle(cp->hFrom);
\r
9016 closesocket(cp->sock);
\r
9021 if (signal) send(cp->sock2, "\017", 1, 0); /* 017 = 15 = SIGTERM */
\r
9022 closesocket(cp->sock);
\r
9023 closesocket(cp->sock2);
\r
9031 InterruptChildProcess(ProcRef pr)
\r
9035 cp = (ChildProc *) pr;
\r
9036 if (cp == NULL) return;
\r
9037 switch (cp->kind) {
\r
9039 /* The following doesn't work because the chess program
\r
9040 doesn't "have the same console" as WinBoard. Maybe
\r
9041 we could arrange for this even though neither WinBoard
\r
9042 nor the chess program uses a console for stdio */
\r
9043 /*!!GenerateConsoleCtrlEvent(CTRL_C_EVENT, cp->pid);*/
\r
9048 /* Can't interrupt */
\r
9052 send(cp->sock2, "\002", 1, 0); /* 2 = SIGINT */
\r
9059 OpenTelnet(char *host, char *port, ProcRef *pr)
\r
9061 char cmdLine[MSG_SIZ];
\r
9063 if (port[0] == NULLCHAR) {
\r
9064 snprintf(cmdLine, MSG_SIZ, "%s %s", appData.telnetProgram, host);
\r
9066 snprintf(cmdLine, MSG_SIZ, "%s %s %s", appData.telnetProgram, host, port);
\r
9068 return StartChildProcess(cmdLine, "", pr);
\r
9072 /* Code to open TCP sockets */
\r
9075 OpenTCP(char *host, char *port, ProcRef *pr)
\r
9080 struct sockaddr_in sa, mysa;
\r
9081 struct hostent FAR *hp;
\r
9082 unsigned short uport;
\r
9083 WORD wVersionRequested;
\r
9086 /* Initialize socket DLL */
\r
9087 wVersionRequested = MAKEWORD(1, 1);
\r
9088 err = WSAStartup(wVersionRequested, &wsaData);
\r
9089 if (err != 0) return err;
\r
9092 if ((s = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {
\r
9093 err = WSAGetLastError();
\r
9098 /* Bind local address using (mostly) don't-care values.
\r
9100 memset((char *) &mysa, 0, sizeof(struct sockaddr_in));
\r
9101 mysa.sin_family = AF_INET;
\r
9102 mysa.sin_addr.s_addr = INADDR_ANY;
\r
9103 uport = (unsigned short) 0;
\r
9104 mysa.sin_port = htons(uport);
\r
9105 if (bind(s, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))
\r
9106 == SOCKET_ERROR) {
\r
9107 err = WSAGetLastError();
\r
9112 /* Resolve remote host name */
\r
9113 memset((char *) &sa, 0, sizeof(struct sockaddr_in));
\r
9114 if (!(hp = gethostbyname(host))) {
\r
9115 unsigned int b0, b1, b2, b3;
\r
9117 err = WSAGetLastError();
\r
9119 if (sscanf(host, "%u.%u.%u.%u", &b0, &b1, &b2, &b3) == 4) {
\r
9120 hp = (struct hostent *) calloc(1, sizeof(struct hostent));
\r
9121 hp->h_addrtype = AF_INET;
\r
9123 hp->h_addr_list = (char **) calloc(2, sizeof(char *));
\r
9124 hp->h_addr_list[0] = (char *) malloc(4);
\r
9125 hp->h_addr_list[0][0] = (char) b0;
\r
9126 hp->h_addr_list[0][1] = (char) b1;
\r
9127 hp->h_addr_list[0][2] = (char) b2;
\r
9128 hp->h_addr_list[0][3] = (char) b3;
\r
9134 sa.sin_family = hp->h_addrtype;
\r
9135 uport = (unsigned short) atoi(port);
\r
9136 sa.sin_port = htons(uport);
\r
9137 memcpy((char *) &sa.sin_addr, hp->h_addr, hp->h_length);
\r
9139 /* Make connection */
\r
9140 if (connect(s, (struct sockaddr *) &sa,
\r
9141 sizeof(struct sockaddr_in)) == SOCKET_ERROR) {
\r
9142 err = WSAGetLastError();
\r
9147 /* Prepare return value */
\r
9148 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
\r
9149 cp->kind = CPSock;
\r
9151 *pr = (ProcRef *) cp;
\r
9157 OpenCommPort(char *name, ProcRef *pr)
\r
9162 char fullname[MSG_SIZ];
\r
9164 if (*name != '\\')
\r
9165 snprintf(fullname, MSG_SIZ, "\\\\.\\%s", name);
\r
9167 safeStrCpy(fullname, name, sizeof(fullname)/sizeof(fullname[0]) );
\r
9169 h = CreateFile(name, GENERIC_READ | GENERIC_WRITE,
\r
9170 0, NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL);
\r
9171 if (h == (HANDLE) -1) {
\r
9172 return GetLastError();
\r
9176 if (!SetCommState(h, (LPDCB) &dcb)) return GetLastError();
\r
9178 /* Accumulate characters until a 100ms pause, then parse */
\r
9179 ct.ReadIntervalTimeout = 100;
\r
9180 ct.ReadTotalTimeoutMultiplier = 0;
\r
9181 ct.ReadTotalTimeoutConstant = 0;
\r
9182 ct.WriteTotalTimeoutMultiplier = 0;
\r
9183 ct.WriteTotalTimeoutConstant = 0;
\r
9184 if (!SetCommTimeouts(h, (LPCOMMTIMEOUTS) &ct)) return GetLastError();
\r
9186 /* Prepare return value */
\r
9187 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
\r
9188 cp->kind = CPComm;
\r
9191 *pr = (ProcRef *) cp;
\r
9197 OpenLoopback(ProcRef *pr)
\r
9199 DisplayFatalError(_("Not implemented"), 0, 1);
\r
9205 OpenRcmd(char* host, char* user, char* cmd, ProcRef* pr)
\r
9210 struct sockaddr_in sa, mysa;
\r
9211 struct hostent FAR *hp;
\r
9212 unsigned short uport;
\r
9213 WORD wVersionRequested;
\r
9216 char stderrPortStr[MSG_SIZ];
\r
9218 /* Initialize socket DLL */
\r
9219 wVersionRequested = MAKEWORD(1, 1);
\r
9220 err = WSAStartup(wVersionRequested, &wsaData);
\r
9221 if (err != 0) return err;
\r
9223 /* Resolve remote host name */
\r
9224 memset((char *) &sa, 0, sizeof(struct sockaddr_in));
\r
9225 if (!(hp = gethostbyname(host))) {
\r
9226 unsigned int b0, b1, b2, b3;
\r
9228 err = WSAGetLastError();
\r
9230 if (sscanf(host, "%u.%u.%u.%u", &b0, &b1, &b2, &b3) == 4) {
\r
9231 hp = (struct hostent *) calloc(1, sizeof(struct hostent));
\r
9232 hp->h_addrtype = AF_INET;
\r
9234 hp->h_addr_list = (char **) calloc(2, sizeof(char *));
\r
9235 hp->h_addr_list[0] = (char *) malloc(4);
\r
9236 hp->h_addr_list[0][0] = (char) b0;
\r
9237 hp->h_addr_list[0][1] = (char) b1;
\r
9238 hp->h_addr_list[0][2] = (char) b2;
\r
9239 hp->h_addr_list[0][3] = (char) b3;
\r
9245 sa.sin_family = hp->h_addrtype;
\r
9246 uport = (unsigned short) 514;
\r
9247 sa.sin_port = htons(uport);
\r
9248 memcpy((char *) &sa.sin_addr, hp->h_addr, hp->h_length);
\r
9250 /* Bind local socket to unused "privileged" port address
\r
9252 s = INVALID_SOCKET;
\r
9253 memset((char *) &mysa, 0, sizeof(struct sockaddr_in));
\r
9254 mysa.sin_family = AF_INET;
\r
9255 mysa.sin_addr.s_addr = INADDR_ANY;
\r
9256 for (fromPort = 1023;; fromPort--) {
\r
9257 if (fromPort < 0) {
\r
9259 return WSAEADDRINUSE;
\r
9261 if (s == INVALID_SOCKET) {
\r
9262 if ((s = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {
\r
9263 err = WSAGetLastError();
\r
9268 uport = (unsigned short) fromPort;
\r
9269 mysa.sin_port = htons(uport);
\r
9270 if (bind(s, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))
\r
9271 == SOCKET_ERROR) {
\r
9272 err = WSAGetLastError();
\r
9273 if (err == WSAEADDRINUSE) continue;
\r
9277 if (connect(s, (struct sockaddr *) &sa,
\r
9278 sizeof(struct sockaddr_in)) == SOCKET_ERROR) {
\r
9279 err = WSAGetLastError();
\r
9280 if (err == WSAEADDRINUSE) {
\r
9291 /* Bind stderr local socket to unused "privileged" port address
\r
9293 s2 = INVALID_SOCKET;
\r
9294 memset((char *) &mysa, 0, sizeof(struct sockaddr_in));
\r
9295 mysa.sin_family = AF_INET;
\r
9296 mysa.sin_addr.s_addr = INADDR_ANY;
\r
9297 for (fromPort = 1023;; fromPort--) {
\r
9298 if (fromPort == prevStderrPort) continue; // don't reuse port
\r
9299 if (fromPort < 0) {
\r
9300 (void) closesocket(s);
\r
9302 return WSAEADDRINUSE;
\r
9304 if (s2 == INVALID_SOCKET) {
\r
9305 if ((s2 = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {
\r
9306 err = WSAGetLastError();
\r
9312 uport = (unsigned short) fromPort;
\r
9313 mysa.sin_port = htons(uport);
\r
9314 if (bind(s2, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))
\r
9315 == SOCKET_ERROR) {
\r
9316 err = WSAGetLastError();
\r
9317 if (err == WSAEADDRINUSE) continue;
\r
9318 (void) closesocket(s);
\r
9322 if (listen(s2, 1) == SOCKET_ERROR) {
\r
9323 err = WSAGetLastError();
\r
9324 if (err == WSAEADDRINUSE) {
\r
9326 s2 = INVALID_SOCKET;
\r
9329 (void) closesocket(s);
\r
9330 (void) closesocket(s2);
\r
9336 prevStderrPort = fromPort; // remember port used
\r
9337 snprintf(stderrPortStr, MSG_SIZ, "%d", fromPort);
\r
9339 if (send(s, stderrPortStr, strlen(stderrPortStr) + 1, 0) == SOCKET_ERROR) {
\r
9340 err = WSAGetLastError();
\r
9341 (void) closesocket(s);
\r
9342 (void) closesocket(s2);
\r
9347 if (send(s, UserName(), strlen(UserName()) + 1, 0) == SOCKET_ERROR) {
\r
9348 err = WSAGetLastError();
\r
9349 (void) closesocket(s);
\r
9350 (void) closesocket(s2);
\r
9354 if (*user == NULLCHAR) user = UserName();
\r
9355 if (send(s, user, strlen(user) + 1, 0) == SOCKET_ERROR) {
\r
9356 err = WSAGetLastError();
\r
9357 (void) closesocket(s);
\r
9358 (void) closesocket(s2);
\r
9362 if (send(s, cmd, strlen(cmd) + 1, 0) == SOCKET_ERROR) {
\r
9363 err = WSAGetLastError();
\r
9364 (void) closesocket(s);
\r
9365 (void) closesocket(s2);
\r
9370 if ((s3 = accept(s2, NULL, NULL)) == INVALID_SOCKET) {
\r
9371 err = WSAGetLastError();
\r
9372 (void) closesocket(s);
\r
9373 (void) closesocket(s2);
\r
9377 (void) closesocket(s2); /* Stop listening */
\r
9379 /* Prepare return value */
\r
9380 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
\r
9381 cp->kind = CPRcmd;
\r
9384 *pr = (ProcRef *) cp;
\r
9391 AddInputSource(ProcRef pr, int lineByLine,
\r
9392 InputCallback func, VOIDSTAR closure)
\r
9394 InputSource *is, *is2 = NULL;
\r
9395 ChildProc *cp = (ChildProc *) pr;
\r
9397 is = (InputSource *) calloc(1, sizeof(InputSource));
\r
9398 is->lineByLine = lineByLine;
\r
9400 is->closure = closure;
\r
9401 is->second = NULL;
\r
9402 is->next = is->buf;
\r
9403 if (pr == NoProc) {
\r
9404 is->kind = CPReal;
\r
9405 consoleInputSource = is;
\r
9407 is->kind = cp->kind;
\r
9409 [AS] Try to avoid a race condition if the thread is given control too early:
\r
9410 we create all threads suspended so that the is->hThread variable can be
\r
9411 safely assigned, then let the threads start with ResumeThread.
\r
9413 switch (cp->kind) {
\r
9415 is->hFile = cp->hFrom;
\r
9416 cp->hFrom = NULL; /* now owned by InputThread */
\r
9418 CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) NonOvlInputThread,
\r
9419 (LPVOID) is, CREATE_SUSPENDED, &is->id);
\r
9423 is->hFile = cp->hFrom;
\r
9424 cp->hFrom = NULL; /* now owned by InputThread */
\r
9426 CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) InputThread,
\r
9427 (LPVOID) is, CREATE_SUSPENDED, &is->id);
\r
9431 is->sock = cp->sock;
\r
9433 CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,
\r
9434 (LPVOID) is, CREATE_SUSPENDED, &is->id);
\r
9438 is2 = (InputSource *) calloc(1, sizeof(InputSource));
\r
9440 is->sock = cp->sock;
\r
9442 is2->sock = cp->sock2;
\r
9443 is2->second = is2;
\r
9445 CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,
\r
9446 (LPVOID) is, CREATE_SUSPENDED, &is->id);
\r
9448 CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,
\r
9449 (LPVOID) is2, CREATE_SUSPENDED, &is2->id);
\r
9453 if( is->hThread != NULL ) {
\r
9454 ResumeThread( is->hThread );
\r
9457 if( is2 != NULL && is2->hThread != NULL ) {
\r
9458 ResumeThread( is2->hThread );
\r
9462 return (InputSourceRef) is;
\r
9466 RemoveInputSource(InputSourceRef isr)
\r
9470 is = (InputSource *) isr;
\r
9471 is->hThread = NULL; /* tell thread to stop */
\r
9472 CloseHandle(is->hThread);
\r
9473 if (is->second != NULL) {
\r
9474 is->second->hThread = NULL;
\r
9475 CloseHandle(is->second->hThread);
\r
9479 int no_wrap(char *message, int count)
\r
9481 ConsoleOutput(message, count, FALSE);
\r
9486 OutputToProcess(ProcRef pr, char *message, int count, int *outError)
\r
9489 int outCount = SOCKET_ERROR;
\r
9490 ChildProc *cp = (ChildProc *) pr;
\r
9491 static OVERLAPPED ovl;
\r
9492 static int line = 0;
\r
9496 if (appData.noJoin || !appData.useInternalWrap)
\r
9497 return no_wrap(message, count);
\r
9500 int width = get_term_width();
\r
9501 int len = wrap(NULL, message, count, width, &line);
\r
9502 char *msg = malloc(len);
\r
9506 return no_wrap(message, count);
\r
9509 dbgchk = wrap(msg, message, count, width, &line);
\r
9510 if (dbgchk != len && appData.debugMode)
\r
9511 fprintf(debugFP, "wrap(): dbgchk(%d) != len(%d)\n", dbgchk, len);
\r
9512 ConsoleOutput(msg, len, FALSE);
\r
9519 if (ovl.hEvent == NULL) {
\r
9520 ovl.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
\r
9522 ovl.Internal = ovl.InternalHigh = ovl.Offset = ovl.OffsetHigh = 0;
\r
9524 switch (cp->kind) {
\r
9527 outCount = send(cp->sock, message, count, 0);
\r
9528 if (outCount == SOCKET_ERROR) {
\r
9529 *outError = WSAGetLastError();
\r
9531 *outError = NO_ERROR;
\r
9536 if (WriteFile(((ChildProc *)pr)->hTo, message, count,
\r
9537 &dOutCount, NULL)) {
\r
9538 *outError = NO_ERROR;
\r
9539 outCount = (int) dOutCount;
\r
9541 *outError = GetLastError();
\r
9546 *outError = DoWriteFile(((ChildProc *)pr)->hTo, message, count,
\r
9547 &dOutCount, &ovl);
\r
9548 if (*outError == NO_ERROR) {
\r
9549 outCount = (int) dOutCount;
\r
9557 OutputToProcessDelayed(ProcRef pr, char *message, int count, int *outError,
\r
9560 /* Ignore delay, not implemented for WinBoard */
\r
9561 return OutputToProcess(pr, message, count, outError);
\r
9566 CmailSigHandlerCallBack(InputSourceRef isr, VOIDSTAR closure,
\r
9567 char *buf, int count, int error)
\r
9569 DisplayFatalError(_("Not implemented"), 0, 1);
\r
9572 /* see wgamelist.c for Game List functions */
\r
9573 /* see wedittags.c for Edit Tags functions */
\r
9580 char buf[MSG_SIZ];
\r
9583 if (SearchPath(installDir, appData.icsLogon, NULL, MSG_SIZ, buf, &dummy)) {
\r
9584 f = fopen(buf, "r");
\r
9586 ProcessICSInitScript(f);
\r
9594 StartAnalysisClock()
\r
9596 if (analysisTimerEvent) return;
\r
9597 analysisTimerEvent = SetTimer(hwndMain, (UINT) ANALYSIS_TIMER_ID,
\r
9598 (UINT) 2000, NULL);
\r
9602 SetHighlights(int fromX, int fromY, int toX, int toY)
\r
9604 highlightInfo.sq[0].x = fromX;
\r
9605 highlightInfo.sq[0].y = fromY;
\r
9606 highlightInfo.sq[1].x = toX;
\r
9607 highlightInfo.sq[1].y = toY;
\r
9613 highlightInfo.sq[0].x = highlightInfo.sq[0].y =
\r
9614 highlightInfo.sq[1].x = highlightInfo.sq[1].y = -1;
\r
9618 SetPremoveHighlights(int fromX, int fromY, int toX, int toY)
\r
9620 premoveHighlightInfo.sq[0].x = fromX;
\r
9621 premoveHighlightInfo.sq[0].y = fromY;
\r
9622 premoveHighlightInfo.sq[1].x = toX;
\r
9623 premoveHighlightInfo.sq[1].y = toY;
\r
9627 ClearPremoveHighlights()
\r
9629 premoveHighlightInfo.sq[0].x = premoveHighlightInfo.sq[0].y =
\r
9630 premoveHighlightInfo.sq[1].x = premoveHighlightInfo.sq[1].y = -1;
\r
9634 ShutDownFrontEnd()
\r
9636 if (saveSettingsOnExit) SaveSettings(settingsFileName);
\r
9637 DeleteClipboardTempFiles();
\r
9643 if (IsIconic(hwndMain))
\r
9644 ShowWindow(hwndMain, SW_RESTORE);
\r
9646 SetActiveWindow(hwndMain);
\r
9650 * Prototypes for animation support routines
\r
9652 static void ScreenSquare(int column, int row, POINT * pt);
\r
9653 static void Tween( POINT * start, POINT * mid, POINT * finish, int factor,
\r
9654 POINT frames[], int * nFrames);
\r
9660 AnimateAtomicCapture(Board board, int fromX, int fromY, int toX, int toY)
\r
9661 { // [HGM] atomic: animate blast wave
\r
9664 explodeInfo.fromX = fromX;
\r
9665 explodeInfo.fromY = fromY;
\r
9666 explodeInfo.toX = toX;
\r
9667 explodeInfo.toY = toY;
\r
9668 for(i=1; i<4*kFactor; i++) {
\r
9669 explodeInfo.radius = (i*180)/(4*kFactor-1);
\r
9670 DrawPosition(FALSE, board);
\r
9671 Sleep(appData.animSpeed);
\r
9673 explodeInfo.radius = 0;
\r
9674 DrawPosition(TRUE, board);
\r
9678 AnimateMove(board, fromX, fromY, toX, toY)
\r
9685 ChessSquare piece;
\r
9686 POINT start, finish, mid;
\r
9687 POINT frames[kFactor * 2 + 1];
\r
9690 if (!appData.animate) return;
\r
9691 if (doingSizing) return;
\r
9692 if (fromY < 0 || fromX < 0) return;
\r
9693 piece = board[fromY][fromX];
\r
9694 if (piece >= EmptySquare) return;
\r
9696 ScreenSquare(fromX, fromY, &start);
\r
9697 ScreenSquare(toX, toY, &finish);
\r
9699 /* All moves except knight jumps move in straight line */
\r
9700 if (!(abs(fromX-toX) == 1 && abs(fromY-toY) == 2 || abs(fromX-toX) == 2 && abs(fromY-toY) == 1)) {
\r
9701 mid.x = start.x + (finish.x - start.x) / 2;
\r
9702 mid.y = start.y + (finish.y - start.y) / 2;
\r
9704 /* Knight: make straight movement then diagonal */
\r
9705 if (abs(toY - fromY) < abs(toX - fromX)) {
\r
9706 mid.x = start.x + (finish.x - start.x) / 2;
\r
9710 mid.y = start.y + (finish.y - start.y) / 2;
\r
9714 /* Don't use as many frames for very short moves */
\r
9715 if (abs(toY - fromY) + abs(toX - fromX) <= 2)
\r
9716 Tween(&start, &mid, &finish, kFactor - 1, frames, &nFrames);
\r
9718 Tween(&start, &mid, &finish, kFactor, frames, &nFrames);
\r
9720 animInfo.from.x = fromX;
\r
9721 animInfo.from.y = fromY;
\r
9722 animInfo.to.x = toX;
\r
9723 animInfo.to.y = toY;
\r
9724 animInfo.lastpos = start;
\r
9725 animInfo.piece = piece;
\r
9726 for (n = 0; n < nFrames; n++) {
\r
9727 animInfo.pos = frames[n];
\r
9728 DrawPosition(FALSE, NULL);
\r
9729 animInfo.lastpos = animInfo.pos;
\r
9730 Sleep(appData.animSpeed);
\r
9732 animInfo.pos = finish;
\r
9733 DrawPosition(FALSE, NULL);
\r
9734 animInfo.piece = EmptySquare;
\r
9735 Explode(board, fromX, fromY, toX, toY);
\r
9738 /* Convert board position to corner of screen rect and color */
\r
9741 ScreenSquare(column, row, pt)
\r
9742 int column; int row; POINT * pt;
\r
9745 pt->x = lineGap + ((BOARD_WIDTH-1)-column) * (squareSize + lineGap);
\r
9746 pt->y = lineGap + row * (squareSize + lineGap);
\r
9748 pt->x = lineGap + column * (squareSize + lineGap);
\r
9749 pt->y = lineGap + ((BOARD_HEIGHT-1)-row) * (squareSize + lineGap);
\r
9753 /* Generate a series of frame coords from start->mid->finish.
\r
9754 The movement rate doubles until the half way point is
\r
9755 reached, then halves back down to the final destination,
\r
9756 which gives a nice slow in/out effect. The algorithmn
\r
9757 may seem to generate too many intermediates for short
\r
9758 moves, but remember that the purpose is to attract the
\r
9759 viewers attention to the piece about to be moved and
\r
9760 then to where it ends up. Too few frames would be less
\r
9764 Tween(start, mid, finish, factor, frames, nFrames)
\r
9765 POINT * start; POINT * mid;
\r
9766 POINT * finish; int factor;
\r
9767 POINT frames[]; int * nFrames;
\r
9769 int n, fraction = 1, count = 0;
\r
9771 /* Slow in, stepping 1/16th, then 1/8th, ... */
\r
9772 for (n = 0; n < factor; n++)
\r
9774 for (n = 0; n < factor; n++) {
\r
9775 frames[count].x = start->x + (mid->x - start->x) / fraction;
\r
9776 frames[count].y = start->y + (mid->y - start->y) / fraction;
\r
9778 fraction = fraction / 2;
\r
9782 frames[count] = *mid;
\r
9785 /* Slow out, stepping 1/2, then 1/4, ... */
\r
9787 for (n = 0; n < factor; n++) {
\r
9788 frames[count].x = finish->x - (finish->x - mid->x) / fraction;
\r
9789 frames[count].y = finish->y - (finish->y - mid->y) / fraction;
\r
9791 fraction = fraction * 2;
\r
9797 HistorySet( char movelist[][2*MOVE_LEN], int first, int last, int current )
\r
9799 MoveHistorySet( movelist, first, last, current, pvInfoList );
\r
9801 EvalGraphSet( first, last, current, pvInfoList );
\r
9805 SettingsPopUp(ChessProgramState *cps)
\r
9806 { // [HGM] wrapper needed because handles must not be passed through back-end
\r
9807 EngineOptionsPopup(savedHwnd, cps);
\r