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, OPT_MESS, 400 },
\r
252 { DLG_TimeControl, IDC_Babble, OPT_TCUseMoves, OPT_TCUseInc, OPT_TCUseFixed,
\r
253 OPT_TCtext1, OPT_TCtext2, OPT_TCitext1, OPT_TCitext2, OPT_TCftext, GPB_Factors, IDC_Factor1, IDC_Factor2, IDOK, IDCANCEL },
\r
254 { DLG_LoadOptions, OPT_Autostep, OPT_AStext1, IDOK, IDCANCEL },
\r
255 { DLG_SaveOptions, OPT_Autosave, OPT_AVPrompt, OPT_AVToFile, OPT_AVBrowse,
\r
256 801, OPT_PGN, OPT_Old, OPT_OutOfBookInfo, IDOK, IDCANCEL },
\r
257 { 1536, 1090, IDC_Directories, 1089, 1091, IDOK, IDCANCEL, 1038, IDC_IndexNr, 1037 },
\r
258 { DLG_CommPort, IDOK, IDCANCEL, IDC_Port, IDC_Rate, IDC_Bits, IDC_Parity,
\r
259 IDC_Stop, IDC_Flow, OPT_SerialHelp },
\r
260 { DLG_EditComment, IDOK, OPT_CancelComment, OPT_ClearComment, OPT_EditComment },
\r
261 { DLG_PromotionKing, PB_Chancellor, PB_Archbishop, PB_Queen, PB_Rook,
\r
262 PB_Bishop, PB_Knight, PB_King, IDCANCEL, IDC_Yes, IDC_No, IDC_Centaur },
\r
263 { ABOUTBOX2, IDC_ChessBoard },
\r
264 { DLG_GameList, OPT_GameListLoad, OPT_GameListPrev, OPT_GameListNext,
\r
265 OPT_GameListClose, IDC_GameListDoFilter },
\r
266 { DLG_EditTags, IDOK, OPT_TagsCancel, OPT_EditTags },
\r
267 { DLG_Error, IDOK },
\r
268 { DLG_Colorize, IDOK, IDCANCEL, OPT_ChooseColor, OPT_Bold, OPT_Italic,
\r
269 OPT_Underline, OPT_Strikeout, OPT_Sample },
\r
270 { DLG_Question, IDOK, IDCANCEL, OPT_QuestionText },
\r
271 { DLG_Startup, IDC_Welcome, OPT_ChessEngine, OPT_ChessServer, OPT_View,
\r
272 IDC_SPECIFY_ENG_STATIC, IDC_SPECIFY_SERVER_STATIC, OPT_AnyAdditional,
\r
273 IDOK, IDCANCEL, IDM_HELPCONTENTS },
\r
274 { DLG_IndexNumber, IDC_Index },
\r
275 { DLG_TypeInMove, IDOK, IDCANCEL },
\r
276 { DLG_TypeInName, IDOK, IDCANCEL },
\r
277 { DLG_Sound, IDC_Event, OPT_NoSound, OPT_DefaultBeep, OPT_BuiltInSound,
\r
278 OPT_WavFile, OPT_BrowseSound, OPT_DefaultSounds, IDOK, IDCANCEL, OPT_PlaySound },
\r
279 { DLG_GeneralOptions, IDOK, IDCANCEL, OPT_AlwaysOnTop, OPT_HighlightLastMove,
\r
280 OPT_AlwaysQueen, OPT_PeriodicUpdates, OPT_AnimateDragging, OPT_PonderNextMove,
\r
281 OPT_AnimateMoving, OPT_PopupExitMessage, OPT_AutoFlag, OPT_PopupMoveErrors,
\r
282 OPT_AutoFlipView, OPT_ShowButtonBar, OPT_AutoRaiseBoard, OPT_ShowCoordinates,
\r
283 OPT_Blindfold, OPT_ShowThinking, OPT_HighlightDragging, OPT_TestLegality,
\r
284 OPT_SaveExtPGN, OPT_HideThinkFromHuman, OPT_ExtraInfoInMoveHistory,
\r
285 OPT_HighlightMoveArrow, OPT_AutoLogo ,OPT_SmartMove },
\r
286 { DLG_IcsOptions, IDOK, IDCANCEL, OPT_AutoComment, OPT_AutoKibitz, OPT_AutoObserve,
\r
287 OPT_GetMoveList, OPT_LocalLineEditing, OPT_QuietPlay, OPT_SeekGraph, OPT_AutoRefresh,
\r
288 OPT_BgObserve, OPT_DualBoard, OPT_Premove, OPT_PremoveWhite, OPT_PremoveBlack,
\r
289 OPT_SmartMove, OPT_IcsAlarm, IDC_Sec, OPT_ChooseShoutColor, OPT_ChooseSShoutColor,
\r
290 OPT_ChooseChannel1Color, OPT_ChooseChannelColor, OPT_ChooseKibitzColor,
\r
291 OPT_ChooseTellColor, OPT_ChooseChallengeColor, OPT_ChooseRequestColor,
\r
292 OPT_ChooseSeekColor, OPT_ChooseNormalColor, OPT_ChooseBackgroundColor,
\r
293 OPT_DefaultColors, OPT_DontColorize, IDC_Boxes, GPB_Colors, GPB_Premove,
\r
294 GPB_General, GPB_Alarm },
\r
295 { DLG_BoardOptions, IDOK, IDCANCEL, OPT_SizeTiny, OPT_SizeTeeny, OPT_SizeDinky,
\r
296 OPT_SizePetite, OPT_SizeSlim, OPT_SizeSmall, OPT_SizeMediocre, OPT_SizeMiddling,
\r
297 OPT_SizeAverage, OPT_SizeModerate, OPT_SizeMedium, OPT_SizeBulky, OPT_SizeLarge,
\r
298 OPT_SizeBig, OPT_SizeHuge, OPT_SizeGiant, OPT_SizeColossal, OPT_SizeTitanic,
\r
299 OPT_ChooseLightSquareColor, OPT_ChooseDarkSquareColor, OPT_ChooseWhitePieceColor,
\r
300 OPT_ChooseBlackPieceColor, OPT_ChooseHighlightSquareColor, OPT_ChoosePremoveHighlightColor,
\r
301 OPT_Monochrome, OPT_AllWhite, OPT_UpsideDown, OPT_DefaultBoardColors, GPB_Colors,
\r
302 IDC_Light, IDC_Dark, IDC_White, IDC_Black, IDC_High, IDC_PreHigh, GPB_Size },
\r
303 { DLG_NewVariant, IDOK, IDCANCEL, OPT_VariantNormal, OPT_VariantFRC, OPT_VariantWildcastle,
\r
304 OPT_VariantNocastle, OPT_VariantLosers, OPT_VariantGiveaway, OPT_VariantSuicide,
\r
305 OPT_Variant3Check, OPT_VariantTwoKings, OPT_VariantAtomic, OPT_VariantCrazyhouse,
\r
306 OPT_VariantBughouse, OPT_VariantTwilight, OPT_VariantShogi, OPT_VariantSuper,
\r
307 OPT_VariantKnightmate, OPT_VariantBerolina, OPT_VariantCylinder, OPT_VariantFairy,
\r
308 OPT_VariantMakruk, OPT_VariantGothic, OPT_VariantCapablanca, OPT_VariantJanus,
\r
309 OPT_VariantCRC, OPT_VariantFalcon, OPT_VariantCourier, OPT_VariantGreat, OPT_VariantSChess,
\r
310 OPT_VariantShatranj, OPT_VariantXiangqi, GPB_Variant, GPB_Board, IDC_Height,
\r
311 IDC_Width, IDC_Hand, IDC_Pieces, IDC_Def },
\r
312 { DLG_Fonts, IDOK, IDCANCEL, OPT_ChooseClockFont, OPT_ChooseMessageFont,
\r
313 OPT_ChooseCoordFont, OPT_ChooseTagFont, OPT_ChooseCommentsFont, OPT_ChooseConsoleFont, OPT_ChooseMoveHistoryFont, OPT_DefaultFonts,
\r
314 OPT_ClockFont, OPT_MessageFont, OPT_CoordFont, OPT_EditTagsFont,
\r
315 OPT_CommentsFont, OPT_MessageFont5, GPB_Current, GPB_All, OPT_MessageFont6 },
\r
316 { DLG_NewGameFRC, IDC_NFG_Label, IDC_NFG_Random, IDOK, IDCANCEL },
\r
317 { DLG_GameListOptions, IDC_GLT, IDC_GLT_Up, IDC_GLT_Down, IDC_GLT_Restore,
\r
318 IDC_GLT_Default, IDOK, IDCANCEL, IDC_GLT_RestoreTo },
\r
319 { DLG_MoveHistory },
\r
320 { DLG_EvalGraph },
\r
321 { DLG_EngineOutput, IDC_EngineLabel1, IDC_Engine1_NPS, IDC_EngineLabel2, IDC_Engine2_NPS },
\r
322 { DLG_Chat, IDC_Partner, IDC_Clear, IDC_Send, },
\r
323 { DLG_EnginePlayOptions, IDC_EpPonder, IDC_EpShowThinking, IDC_EpHideThinkingHuman,
\r
324 IDC_EpPeriodicUpdates, GPB_Adjudications, IDC_Draw, IDC_Moves, IDC_Threshold,
\r
325 IDC_Centi, IDC_TestClaims, IDC_DetectMates, IDC_MaterialDraws, IDC_TrivialDraws,
\r
326 GPB_Apply, IDC_Rule, IDC_Repeats, IDC_ScoreAbs1, IDC_ScoreAbs2, IDOK, IDCANCEL },
\r
327 { DLG_OptionsUCI, IDC_PolyDir, IDC_BrowseForPolyglotDir, IDC_Hash, IDC_Path,
\r
328 IDC_BrowseForEGTB, IDC_Cache, IDC_UseBook, IDC_BrowseForBook, IDC_CPU, IDC_OwnBook1,
\r
329 IDC_OwnBook2, IDC_Depth, IDC_Variation, IDC_DefGames, IDOK, IDCANCEL },
\r
333 static char languageBuf[50000], *foreign[1000], *english[1000], *languageFile[MSG_SIZ];
\r
334 static int lastChecked;
\r
335 static char oldLanguage[MSG_SIZ], *menuText[10][30];
\r
336 extern int tinyLayout;
\r
337 extern char * menuBarText[][10];
\r
340 LoadLanguageFile(char *name)
\r
341 { //load the file with translations, and make a list of the strings to be translated, and their translations
\r
343 int i=0, j=0, n=0, k;
\r
346 if(!name || name[0] == NULLCHAR) return;
\r
347 snprintf(buf, MSG_SIZ, "%s%s", name, strchr(name, '.') ? "" : ".lng"); // auto-append lng extension
\r
348 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(!barbaric) return;
\r
402 while(dialogItems[i][0] && dialogItems[i][0] != dialogID) i++; // find the dialog description
\r
403 if(dialogItems[i][0] != dialogID) return; // unknown dialog, should not happen
\r
404 GetWindowText( hDlg, buf, MSG_SIZ );
\r
406 if(strcmp(buf, s)) SetWindowText(hDlg, s); // replace by translated string (if different)
\r
407 for(j=1; k=dialogItems[i][j]; j++) { // translate all listed dialog items
\r
408 GetDlgItemText(hDlg, k, buf, MSG_SIZ);
\r
409 if(strlen(buf) == 0) continue;
\r
411 if(strcmp(buf, s)) SetDlgItemText(hDlg, k, s); // replace by translated string (if different)
\r
416 TranslateOneMenu(int i, HMENU subMenu)
\r
419 static MENUITEMINFO info;
\r
421 info.cbSize = sizeof(MENUITEMINFO);
\r
422 info.fMask = MIIM_STATE | MIIM_TYPE;
\r
423 for(j=GetMenuItemCount(subMenu)-1; j>=0; j--){
\r
425 info.dwTypeData = buf;
\r
426 info.cch = sizeof(buf);
\r
427 GetMenuItemInfo(subMenu, j, TRUE, &info);
\r
429 if(menuText[i][j]) safeStrCpy(buf, menuText[i][j], sizeof(buf)/sizeof(buf[0]) );
\r
430 else menuText[i][j] = strdup(buf); // remember original on first change
\r
432 if(buf[0] == NULLCHAR) continue;
\r
433 info.dwTypeData = T_(buf);
\r
434 info.cch = strlen(buf)+1;
\r
435 SetMenuItemInfo(subMenu, j, TRUE, &info);
\r
441 TranslateMenus(int addLanguage)
\r
444 WIN32_FIND_DATA fileData;
\r
446 #define IDM_English 1895
\r
448 HMENU mainMenu = GetMenu(hwndMain);
\r
449 for (i=GetMenuItemCount(mainMenu)-1; i>=0; i--) {
\r
450 HMENU subMenu = GetSubMenu(mainMenu, i);
\r
451 ModifyMenu(mainMenu, i, MF_STRING|MF_BYPOSITION|MF_POPUP|EnableMenuItem(mainMenu, i, MF_BYPOSITION),
\r
452 (UINT) subMenu, T_(menuBarText[tinyLayout][i]));
\r
453 TranslateOneMenu(i, subMenu);
\r
455 DrawMenuBar(hwndMain);
\r
458 if(!addLanguage) return;
\r
459 if((hFind = FindFirstFile("*.LNG", &fileData)) != INVALID_HANDLE_VALUE) {
\r
460 HMENU mainMenu = GetMenu(hwndMain);
\r
461 HMENU subMenu = GetSubMenu(mainMenu, GetMenuItemCount(mainMenu)-1);
\r
462 AppendMenu(subMenu, MF_SEPARATOR, (UINT_PTR) 0, NULL);
\r
463 AppendMenu(subMenu, MF_ENABLED|MF_STRING|(barbaric?MF_UNCHECKED:MF_CHECKED), (UINT_PTR) IDM_English, (LPCTSTR) "English");
\r
464 i = 0; lastChecked = IDM_English;
\r
466 char *p, *q = fileData.cFileName;
\r
467 int checkFlag = MF_UNCHECKED;
\r
468 languageFile[i] = strdup(q);
\r
469 if(barbaric && !strcmp(oldLanguage, q)) {
\r
470 checkFlag = MF_CHECKED;
\r
471 lastChecked = IDM_English + i + 1;
\r
472 CheckMenuItem(mainMenu, IDM_English, MF_BYCOMMAND|MF_UNCHECKED);
\r
474 *q = ToUpper(*q); while(*++q) *q = ToLower(*q);
\r
475 p = strstr(fileData.cFileName, ".lng");
\r
477 AppendMenu(subMenu, MF_ENABLED|MF_STRING|checkFlag, (UINT_PTR) IDM_English + ++i, (LPCTSTR) fileData.cFileName);
\r
478 } while(FindNextFile(hFind, &fileData));
\r
491 int cliWidth, cliHeight;
\r
494 SizeInfo sizeInfo[] =
\r
496 { "tiny", 21, 0, 1, 1, 0, 0 },
\r
497 { "teeny", 25, 1, 1, 1, 0, 0 },
\r
498 { "dinky", 29, 1, 1, 1, 0, 0 },
\r
499 { "petite", 33, 1, 1, 1, 0, 0 },
\r
500 { "slim", 37, 2, 1, 0, 0, 0 },
\r
501 { "small", 40, 2, 1, 0, 0, 0 },
\r
502 { "mediocre", 45, 2, 1, 0, 0, 0 },
\r
503 { "middling", 49, 2, 0, 0, 0, 0 },
\r
504 { "average", 54, 2, 0, 0, 0, 0 },
\r
505 { "moderate", 58, 3, 0, 0, 0, 0 },
\r
506 { "medium", 64, 3, 0, 0, 0, 0 },
\r
507 { "bulky", 72, 3, 0, 0, 0, 0 },
\r
508 { "large", 80, 3, 0, 0, 0, 0 },
\r
509 { "big", 87, 3, 0, 0, 0, 0 },
\r
510 { "huge", 95, 3, 0, 0, 0, 0 },
\r
511 { "giant", 108, 3, 0, 0, 0, 0 },
\r
512 { "colossal", 116, 4, 0, 0, 0, 0 },
\r
513 { "titanic", 129, 4, 0, 0, 0, 0 },
\r
514 { NULL, 0, 0, 0, 0, 0, 0 }
\r
517 #define MF(x) {x, {{0,}, 0. }, {0, }, 0}
\r
518 MyFont fontRec[NUM_SIZES][NUM_FONTS] =
\r
520 { 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
521 { 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
522 { 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
523 { 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
524 { 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
525 { 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
526 { 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
527 { 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
528 { 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
529 { 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
530 { 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
531 { 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
532 { 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
533 { 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
534 { 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
535 { 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
536 { 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
537 { 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
540 MyFont *font[NUM_SIZES][NUM_FONTS];
\r
549 #define BUTTON_WIDTH (tinyLayout ? 16 : 32)
\r
550 #define N_BUTTONS 5
\r
552 MyButtonDesc buttonDesc[N_BUTTONS] =
\r
554 {"<<", IDM_ToStart, NULL, NULL},
\r
555 {"<", IDM_Backward, NULL, NULL},
\r
556 {"P", IDM_Pause, NULL, NULL},
\r
557 {">", IDM_Forward, NULL, NULL},
\r
558 {">>", IDM_ToEnd, NULL, NULL},
\r
561 int tinyLayout = 0, smallLayout = 0;
\r
562 #define MENU_BAR_ITEMS 9
\r
563 char *menuBarText[2][MENU_BAR_ITEMS+1] = {
\r
564 { N_("&File"), N_("&Edit"), N_("&View"), N_("&Mode"), N_("&Action"), N_("E&ngine"), N_("&Options"), N_("&Help"), NULL },
\r
565 { N_("&F"), N_("&E"), N_("&V"), N_("&M"), N_("&A"), N_("&N"), N_("&O"), N_("&H"), NULL },
\r
569 MySound sounds[(int)NSoundClasses];
\r
570 MyTextAttribs textAttribs[(int)NColorClasses];
\r
572 MyColorizeAttribs colorizeAttribs[] = {
\r
573 { (COLORREF)0, 0, N_("Shout Text") },
\r
574 { (COLORREF)0, 0, N_("SShout/CShout") },
\r
575 { (COLORREF)0, 0, N_("Channel 1 Text") },
\r
576 { (COLORREF)0, 0, N_("Channel Text") },
\r
577 { (COLORREF)0, 0, N_("Kibitz Text") },
\r
578 { (COLORREF)0, 0, N_("Tell Text") },
\r
579 { (COLORREF)0, 0, N_("Challenge Text") },
\r
580 { (COLORREF)0, 0, N_("Request Text") },
\r
581 { (COLORREF)0, 0, N_("Seek Text") },
\r
582 { (COLORREF)0, 0, N_("Normal Text") },
\r
583 { (COLORREF)0, 0, N_("None") }
\r
588 static char *commentTitle;
\r
589 static char *commentText;
\r
590 static int commentIndex;
\r
591 static Boolean editComment = FALSE;
\r
594 char errorTitle[MSG_SIZ];
\r
595 char errorMessage[2*MSG_SIZ];
\r
596 HWND errorDialog = NULL;
\r
597 BOOLEAN moveErrorMessageUp = FALSE;
\r
598 BOOLEAN consoleEcho = TRUE;
\r
599 CHARFORMAT consoleCF;
\r
600 COLORREF consoleBackgroundColor;
\r
602 char *programVersion;
\r
608 typedef int CPKind;
\r
617 SOCKET sock2; /* stderr socket for OpenRcmd */
\r
620 #define INPUT_SOURCE_BUF_SIZE 4096
\r
622 typedef struct _InputSource {
\r
629 char buf[INPUT_SOURCE_BUF_SIZE];
\r
633 InputCallback func;
\r
634 struct _InputSource *second; /* for stderr thread on CPRcmd */
\r
638 InputSource *consoleInputSource;
\r
643 VOID ConsoleOutput(char* data, int length, int forceVisible);
\r
644 VOID ConsoleCreate();
\r
646 ConsoleWndProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam);
\r
647 VOID ColorizeTextPopup(HWND hwnd, ColorClass cc);
\r
648 VOID PrintCommSettings(FILE *f, char *name, DCB *dcb);
\r
649 VOID ParseCommSettings(char *arg, DCB *dcb);
\r
651 StartupDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam);
\r
652 VOID APIENTRY MenuPopup(HWND hwnd, POINT pt, HMENU hmenu, UINT def);
\r
653 void ParseIcsTextMenu(char *icsTextMenuString);
\r
654 VOID PopUpMoveDialog(char firstchar);
\r
655 VOID PopUpNameDialog(char firstchar);
\r
656 VOID UpdateSampleText(HWND hDlg, int id, MyColorizeAttribs *mca);
\r
660 int GameListOptions();
\r
662 int dummy; // [HGM] for obsolete args
\r
664 HWND hwndMain = NULL; /* root window*/
\r
665 HWND hwndConsole = NULL;
\r
666 HWND commentDialog = NULL;
\r
667 HWND moveHistoryDialog = NULL;
\r
668 HWND evalGraphDialog = NULL;
\r
669 HWND engineOutputDialog = NULL;
\r
670 HWND gameListDialog = NULL;
\r
671 HWND editTagsDialog = NULL;
\r
673 int commentUp = FALSE;
\r
675 WindowPlacement wpMain;
\r
676 WindowPlacement wpConsole;
\r
677 WindowPlacement wpComment;
\r
678 WindowPlacement wpMoveHistory;
\r
679 WindowPlacement wpEvalGraph;
\r
680 WindowPlacement wpEngineOutput;
\r
681 WindowPlacement wpGameList;
\r
682 WindowPlacement wpTags;
\r
684 VOID EngineOptionsPopup(); // [HGM] settings
\r
686 VOID GothicPopUp(char *title, VariantClass variant);
\r
688 * Setting "frozen" should disable all user input other than deleting
\r
689 * the window. We do this while engines are initializing themselves.
\r
691 static int frozen = 0;
\r
692 static int oldMenuItemState[MENU_BAR_ITEMS];
\r
698 if (frozen) return;
\r
700 hmenu = GetMenu(hwndMain);
\r
701 for (i=0; i<MENU_BAR_ITEMS; i++) {
\r
702 oldMenuItemState[i] = EnableMenuItem(hmenu, i, MF_BYPOSITION|MF_GRAYED);
\r
704 DrawMenuBar(hwndMain);
\r
707 /* Undo a FreezeUI */
\r
713 if (!frozen) return;
\r
715 hmenu = GetMenu(hwndMain);
\r
716 for (i=0; i<MENU_BAR_ITEMS; i++) {
\r
717 EnableMenuItem(hmenu, i, MF_BYPOSITION|oldMenuItemState[i]);
\r
719 DrawMenuBar(hwndMain);
\r
722 /*static*/ int fromX = -1, fromY = -1, toX, toY; // [HGM] moved upstream, so JAWS can use them
\r
724 /* JAWS preparation patch (WinBoard for the sight impaired). Define required insertions as empty */
\r
730 #define JAWS_ALT_INTERCEPT
\r
731 #define JAWS_KB_NAVIGATION
\r
732 #define JAWS_MENU_ITEMS
\r
733 #define JAWS_SILENCE
\r
734 #define JAWS_REPLAY
\r
736 #define JAWS_COPYRIGHT
\r
737 #define JAWS_DELETE(X) X
\r
738 #define SAYMACHINEMOVE()
\r
742 /*---------------------------------------------------------------------------*\
\r
746 \*---------------------------------------------------------------------------*/
\r
749 WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
\r
750 LPSTR lpCmdLine, int nCmdShow)
\r
753 HANDLE hAccelMain, hAccelNoAlt, hAccelNoICS;
\r
754 // INITCOMMONCONTROLSEX ex;
\r
758 LoadLibrary("RICHED32.DLL");
\r
759 consoleCF.cbSize = sizeof(CHARFORMAT);
\r
761 if (!InitApplication(hInstance)) {
\r
764 if (!InitInstance(hInstance, nCmdShow, lpCmdLine)) {
\r
771 // InitCommonControlsEx(&ex);
\r
772 InitCommonControls();
\r
774 hAccelMain = LoadAccelerators (hInstance, szAppName);
\r
775 hAccelNoAlt = LoadAccelerators (hInstance, "NO_ALT");
\r
776 hAccelNoICS = LoadAccelerators( hInstance, "NO_ICS"); /* [AS] No Ctrl-V on ICS!!! */
\r
778 /* Acquire and dispatch messages until a WM_QUIT message is received. */
\r
780 while (GetMessage(&msg, /* message structure */
\r
781 NULL, /* handle of window receiving the message */
\r
782 0, /* lowest message to examine */
\r
783 0)) /* highest message to examine */
\r
786 if(msg.message == WM_CHAR && msg.wParam == '\t') {
\r
787 // [HGM] navigate: switch between all windows with tab
\r
788 HWND e1 = NULL, e2 = NULL, mh = NULL, hInput = NULL, hText = NULL;
\r
789 int i, currentElement = 0;
\r
791 // first determine what element of the chain we come from (if any)
\r
792 if(appData.icsActive) {
\r
793 hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
794 hText = GetDlgItem(hwndConsole, OPT_ConsoleText);
\r
796 if(engineOutputDialog && EngineOutputIsUp()) {
\r
797 e1 = GetDlgItem(engineOutputDialog, IDC_EngineMemo1);
\r
798 e2 = GetDlgItem(engineOutputDialog, IDC_EngineMemo2);
\r
800 if(moveHistoryDialog && MoveHistoryIsUp()) {
\r
801 mh = GetDlgItem(moveHistoryDialog, IDC_MoveHistory);
\r
803 if(msg.hwnd == hwndMain) currentElement = 7 ; else
\r
804 if(msg.hwnd == engineOutputDialog) currentElement = 2; else
\r
805 if(msg.hwnd == e1) currentElement = 2; else
\r
806 if(msg.hwnd == e2) currentElement = 3; else
\r
807 if(msg.hwnd == moveHistoryDialog) currentElement = 4; else
\r
808 if(msg.hwnd == mh) currentElement = 4; else
\r
809 if(msg.hwnd == evalGraphDialog) currentElement = 6; else
\r
810 if(msg.hwnd == hText) currentElement = 5; else
\r
811 if(msg.hwnd == hInput) currentElement = 6; else
\r
812 for (i = 0; i < N_BUTTONS; i++) {
\r
813 if (buttonDesc[i].hwnd == msg.hwnd) { currentElement = 1; break; }
\r
816 // determine where to go to
\r
817 if(currentElement) { HWND h = NULL; int direction = GetKeyState(VK_SHIFT) < 0 ? -1 : 1;
\r
819 currentElement = (currentElement + direction) % 7;
\r
820 switch(currentElement) {
\r
822 h = hwndMain; break; // passing this case always makes the loop exit
\r
824 h = buttonDesc[0].hwnd; break; // could be NULL
\r
826 if(!EngineOutputIsUp()) continue; // skip closed auxiliary windows
\r
829 if(!EngineOutputIsUp()) continue;
\r
832 if(!MoveHistoryIsUp()) continue;
\r
834 // case 6: // input to eval graph does not seem to get here!
\r
835 // if(!EvalGraphIsUp()) continue;
\r
836 // h = evalGraphDialog; break;
\r
838 if(!appData.icsActive) continue;
\r
842 if(!appData.icsActive) continue;
\r
848 if(currentElement > 4 && IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);
\r
849 if(currentElement < 5 && IsIconic(hwndMain)) ShowWindow(hwndMain, SW_RESTORE); // all open together
\r
852 continue; // this message now has been processed
\r
856 if (!(commentDialog && IsDialogMessage(commentDialog, &msg)) &&
\r
857 !(moveHistoryDialog && IsDialogMessage(moveHistoryDialog, &msg)) &&
\r
858 !(evalGraphDialog && IsDialogMessage(evalGraphDialog, &msg)) &&
\r
859 !(engineOutputDialog && IsDialogMessage(engineOutputDialog, &msg)) &&
\r
860 !(editTagsDialog && IsDialogMessage(editTagsDialog, &msg)) &&
\r
861 !(gameListDialog && IsDialogMessage(gameListDialog, &msg)) &&
\r
862 !(errorDialog && IsDialogMessage(errorDialog, &msg)) &&
\r
863 !(!frozen && TranslateAccelerator(hwndMain, hAccelMain, &msg)) && JAWS_ACCEL
\r
864 !(!hwndConsole && TranslateAccelerator(hwndMain, hAccelNoICS, &msg)) &&
\r
865 !(!hwndConsole && TranslateAccelerator(hwndMain, hAccelNoAlt, &msg))) {
\r
866 int done = 0, i; // [HGM] chat: dispatch cat-box messages
\r
867 for(i=0; i<MAX_CHAT; i++)
\r
868 if(chatHandle[i] && IsDialogMessage(chatHandle[i], &msg)) {
\r
871 if(done) continue; // [HGM] chat: end patch
\r
872 TranslateMessage(&msg); /* Translates virtual key codes */
\r
873 DispatchMessage(&msg); /* Dispatches message to window */
\r
878 return (msg.wParam); /* Returns the value from PostQuitMessage */
\r
881 /*---------------------------------------------------------------------------*\
\r
883 * Initialization functions
\r
885 \*---------------------------------------------------------------------------*/
\r
889 { // update user logo if necessary
\r
890 static char oldUserName[MSG_SIZ], *curName;
\r
892 if(appData.autoLogo) {
\r
893 curName = UserName();
\r
894 if(strcmp(curName, oldUserName)) {
\r
895 snprintf(oldUserName, MSG_SIZ, "logos\\%s.bmp", curName);
\r
896 userLogo = LoadImage( 0, oldUserName, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );
\r
897 safeStrCpy(oldUserName, curName, sizeof(oldUserName)/sizeof(oldUserName[0]) );
\r
898 if(userLogo == NULL)
\r
899 userLogo = LoadImage( 0, "logos\\dummy.bmp", IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );
\r
905 InitApplication(HINSTANCE hInstance)
\r
909 /* Fill in window class structure with parameters that describe the */
\r
912 wc.style = CS_HREDRAW | CS_VREDRAW; /* Class style(s). */
\r
913 wc.lpfnWndProc = (WNDPROC)WndProc; /* Window Procedure */
\r
914 wc.cbClsExtra = 0; /* No per-class extra data. */
\r
915 wc.cbWndExtra = 0; /* No per-window extra data. */
\r
916 wc.hInstance = hInstance; /* Owner of this class */
\r
917 wc.hIcon = LoadIcon(hInstance, "icon_white");
\r
918 wc.hCursor = LoadCursor(NULL, IDC_ARROW); /* Cursor */
\r
919 wc.hbrBackground = (HBRUSH)(COLOR_WINDOW+1); /* Default color */
\r
920 wc.lpszMenuName = szAppName; /* Menu name from .RC */
\r
921 wc.lpszClassName = szAppName; /* Name to register as */
\r
923 /* Register the window class and return success/failure code. */
\r
924 if (!RegisterClass(&wc)) return FALSE;
\r
926 wc.style = CS_HREDRAW | CS_VREDRAW;
\r
927 wc.lpfnWndProc = (WNDPROC)ConsoleWndProc;
\r
929 wc.cbWndExtra = DLGWINDOWEXTRA;
\r
930 wc.hInstance = hInstance;
\r
931 wc.hIcon = LoadIcon(hInstance, "icon_white");
\r
932 wc.hCursor = LoadCursor(NULL, IDC_ARROW);
\r
933 wc.hbrBackground = (HBRUSH)(COLOR_MENU+1);
\r
934 wc.lpszMenuName = NULL;
\r
935 wc.lpszClassName = szConsoleName;
\r
937 if (!RegisterClass(&wc)) return FALSE;
\r
942 /* Set by InitInstance, used by EnsureOnScreen */
\r
943 int screenHeight, screenWidth;
\r
946 EnsureOnScreen(int *x, int *y, int minX, int minY)
\r
948 // int gap = GetSystemMetrics(SM_CYFRAME) + GetSystemMetrics(SM_CYCAPTION);
\r
949 /* Be sure window at (x,y) is not off screen (or even mostly off screen) */
\r
950 if (*x > screenWidth - 32) *x = 0;
\r
951 if (*y > screenHeight - 32) *y = 0;
\r
952 if (*x < minX) *x = minX;
\r
953 if (*y < minY) *y = minY;
\r
957 InitInstance(HINSTANCE hInstance, int nCmdShow, LPSTR lpCmdLine)
\r
959 HWND hwnd; /* Main window handle. */
\r
961 WINDOWPLACEMENT wp;
\r
964 hInst = hInstance; /* Store instance handle in our global variable */
\r
965 programName = szAppName;
\r
967 if (SearchPath(NULL, "WinBoard.exe", NULL, MSG_SIZ, installDir, &filepart)) {
\r
968 *filepart = NULLCHAR;
\r
970 GetCurrentDirectory(MSG_SIZ, installDir);
\r
972 gameInfo.boardWidth = gameInfo.boardHeight = 8; // [HGM] won't have open window otherwise
\r
973 screenWidth = screenHeight = 1000; // [HGM] placement: kludge to allow calling EnsureOnScreen from InitAppData
\r
974 InitAppData(lpCmdLine); /* Get run-time parameters */
\r
975 /* xboard, and older WinBoards, controlled the move sound with the
\r
976 appData.ringBellAfterMoves option. In the current WinBoard, we
\r
977 always turn the option on (so that the backend will call us),
\r
978 then let the user turn the sound off by setting it to silence if
\r
979 desired. To accommodate old winboard.ini files saved by old
\r
980 versions of WinBoard, we also turn off the sound if the option
\r
981 was initially set to false. [HGM] taken out of InitAppData */
\r
982 if (!appData.ringBellAfterMoves) {
\r
983 sounds[(int)SoundMove].name = strdup("");
\r
984 appData.ringBellAfterMoves = TRUE;
\r
986 if (appData.debugMode) {
\r
987 debugFP = fopen(appData.nameOfDebugFile, "w");
\r
988 setbuf(debugFP, NULL);
\r
991 LoadLanguageFile(appData.language);
\r
995 // InitEngineUCI( installDir, &first ); // [HGM] incorporated in InitBackEnd1()
\r
996 // InitEngineUCI( installDir, &second );
\r
998 /* Create a main window for this application instance. */
\r
999 hwnd = CreateWindow(szAppName, szTitle,
\r
1000 (WS_OVERLAPPEDWINDOW & ~WS_MAXIMIZEBOX),
\r
1001 CW_USEDEFAULT, 0, CW_USEDEFAULT, 0,
\r
1002 NULL, NULL, hInstance, NULL);
\r
1005 /* If window could not be created, return "failure" */
\r
1010 /* [HGM] logo: Load logos if specified (must be done before InitDrawingSizes) */
\r
1011 if( appData.firstLogo && appData.firstLogo[0] != NULLCHAR) {
\r
1012 first.programLogo = LoadImage( 0, appData.firstLogo, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );
\r
1014 if (first.programLogo == NULL && appData.debugMode) {
\r
1015 fprintf( debugFP, "Unable to load logo bitmap '%s'\n", appData.firstLogo );
\r
1017 } else if(appData.autoLogo) {
\r
1018 if(appData.firstDirectory && appData.firstDirectory[0]) {
\r
1019 char buf[MSG_SIZ];
\r
1020 snprintf(buf, MSG_SIZ, "%s/logo.bmp", appData.firstDirectory);
\r
1021 first.programLogo = LoadImage( 0, buf, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );
\r
1025 if( appData.secondLogo && appData.secondLogo[0] != NULLCHAR) {
\r
1026 second.programLogo = LoadImage( 0, appData.secondLogo, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );
\r
1028 if (second.programLogo == NULL && appData.debugMode) {
\r
1029 fprintf( debugFP, "Unable to load logo bitmap '%s'\n", appData.secondLogo );
\r
1031 } else if(appData.autoLogo) {
\r
1032 char buf[MSG_SIZ];
\r
1033 if(appData.icsActive) { // [HGM] logo: in ICS mode second can be used for ICS
\r
1034 snprintf(buf, MSG_SIZ, "logos\\%s.bmp", appData.icsHost);
\r
1035 second.programLogo = LoadImage( 0, buf, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );
\r
1037 if(appData.secondDirectory && appData.secondDirectory[0]) {
\r
1038 snprintf(buf, MSG_SIZ, "%s\\logo.bmp", appData.secondDirectory);
\r
1039 second.programLogo = LoadImage( 0, buf, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );
\r
1045 iconWhite = LoadIcon(hInstance, "icon_white");
\r
1046 iconBlack = LoadIcon(hInstance, "icon_black");
\r
1047 iconCurrent = iconWhite;
\r
1048 InitDrawingColors();
\r
1049 screenHeight = GetSystemMetrics(SM_CYSCREEN);
\r
1050 screenWidth = GetSystemMetrics(SM_CXSCREEN);
\r
1051 for (ibs = (int) NUM_SIZES - 1; ibs >= 0; ibs--) {
\r
1052 /* Compute window size for each board size, and use the largest
\r
1053 size that fits on this screen as the default. */
\r
1054 InitDrawingSizes((BoardSize)(ibs+1000), 0);
\r
1055 if (boardSize == (BoardSize)-1 &&
\r
1056 winH <= screenHeight
\r
1057 - GetSystemMetrics(SM_CYFRAME) - GetSystemMetrics(SM_CYCAPTION) - 10
\r
1058 && winW <= screenWidth) {
\r
1059 boardSize = (BoardSize)ibs;
\r
1063 InitDrawingSizes(boardSize, 0);
\r
1065 buttonCount = GetSystemMetrics(SM_CMOUSEBUTTONS);
\r
1067 /* [AS] Load textures if specified */
\r
1068 ZeroMemory( &backTextureSquareInfo, sizeof(backTextureSquareInfo) );
\r
1070 if( appData.liteBackTextureFile && appData.liteBackTextureFile[0] != NULLCHAR && appData.liteBackTextureFile[0] != '*' ) {
\r
1071 liteBackTexture = LoadImage( 0, appData.liteBackTextureFile, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );
\r
1072 liteBackTextureMode = appData.liteBackTextureMode;
\r
1074 if (liteBackTexture == NULL && appData.debugMode) {
\r
1075 fprintf( debugFP, "Unable to load lite texture bitmap '%s'\n", appData.liteBackTextureFile );
\r
1079 if( appData.darkBackTextureFile && appData.darkBackTextureFile[0] != NULLCHAR && appData.darkBackTextureFile[0] != '*' ) {
\r
1080 darkBackTexture = LoadImage( 0, appData.darkBackTextureFile, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );
\r
1081 darkBackTextureMode = appData.darkBackTextureMode;
\r
1083 if (darkBackTexture == NULL && appData.debugMode) {
\r
1084 fprintf( debugFP, "Unable to load dark texture bitmap '%s'\n", appData.darkBackTextureFile );
\r
1088 mysrandom( (unsigned) time(NULL) );
\r
1090 /* [AS] Restore layout */
\r
1091 if( wpMoveHistory.visible ) {
\r
1092 MoveHistoryPopUp();
\r
1095 if( wpEvalGraph.visible ) {
\r
1099 if( wpEngineOutput.visible ) {
\r
1100 EngineOutputPopUp();
\r
1103 /* Make the window visible; update its client area; and return "success" */
\r
1104 EnsureOnScreen(&wpMain.x, &wpMain.y, minX, minY);
\r
1105 wp.length = sizeof(WINDOWPLACEMENT);
\r
1107 wp.showCmd = nCmdShow;
\r
1108 wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;
\r
1109 wp.rcNormalPosition.left = wpMain.x;
\r
1110 wp.rcNormalPosition.right = wpMain.x + wpMain.width;
\r
1111 wp.rcNormalPosition.top = wpMain.y;
\r
1112 wp.rcNormalPosition.bottom = wpMain.y + wpMain.height;
\r
1113 SetWindowPlacement(hwndMain, &wp);
\r
1115 InitBackEnd2(); // [HGM] moved until after all windows placed, to save correct position if fatal error on engine start
\r
1117 if(!appData.noGUI) SetWindowPos(hwndMain, alwaysOnTop ? HWND_TOPMOST : HWND_NOTOPMOST,
\r
1118 0, 0, 0, 0, SWP_NOMOVE|SWP_NOSIZE);
\r
1120 if (hwndConsole) {
\r
1122 SetWindowPos(hwndConsole, alwaysOnTop ? HWND_TOPMOST : HWND_NOTOPMOST,
\r
1123 0, 0, 0, 0, SWP_NOMOVE|SWP_NOSIZE);
\r
1125 ShowWindow(hwndConsole, nCmdShow);
\r
1126 if(appData.chatBoxes) { // [HGM] chat: open chat boxes
\r
1127 char buf[MSG_SIZ], *p = buf, *q;
\r
1128 safeStrCpy(buf, appData.chatBoxes, sizeof(buf)/sizeof(buf[0]) );
\r
1130 q = strchr(p, ';');
\r
1132 if(*p) ChatPopUp(p);
\r
1135 SetActiveWindow(hwndConsole);
\r
1137 if(!appData.noGUI) UpdateWindow(hwnd); else ShowWindow(hwnd, SW_MINIMIZE);
\r
1138 if(gameListDialog) SetFocus(gameListDialog); // [HGM] jaws: for if we clicked multi-game game file
\r
1147 HMENU hmenu = GetMenu(hwndMain);
\r
1149 (void) EnableMenuItem(hmenu, IDM_CommPort,
\r
1150 MF_BYCOMMAND|((appData.icsActive &&
\r
1151 *appData.icsCommPort != NULLCHAR) ?
\r
1152 MF_ENABLED : MF_GRAYED));
\r
1153 (void) CheckMenuItem(hmenu, IDM_SaveSettingsOnExit,
\r
1154 MF_BYCOMMAND|(saveSettingsOnExit ?
\r
1155 MF_CHECKED : MF_UNCHECKED));
\r
1158 //---------------------------------------------------------------------------------------------------------
\r
1160 #define ICS_TEXT_MENU_SIZE (IDM_CommandXLast - IDM_CommandX + 1)
\r
1161 #define XBOARD FALSE
\r
1163 #define OPTCHAR "/"
\r
1164 #define SEPCHAR "="
\r
1168 // front-end part of option handling
\r
1171 LFfromMFP(LOGFONT* lf, MyFontParams *mfp)
\r
1173 HDC hdc = CreateDC("DISPLAY", NULL, NULL, NULL);
\r
1174 lf->lfHeight = -(int)(mfp->pointSize * GetDeviceCaps(hdc, LOGPIXELSY) / 72.0 + 0.5);
\r
1177 lf->lfEscapement = 0;
\r
1178 lf->lfOrientation = 0;
\r
1179 lf->lfWeight = mfp->bold ? FW_BOLD : FW_NORMAL;
\r
1180 lf->lfItalic = mfp->italic;
\r
1181 lf->lfUnderline = mfp->underline;
\r
1182 lf->lfStrikeOut = mfp->strikeout;
\r
1183 lf->lfCharSet = mfp->charset;
\r
1184 lf->lfOutPrecision = OUT_DEFAULT_PRECIS;
\r
1185 lf->lfClipPrecision = CLIP_DEFAULT_PRECIS;
\r
1186 lf->lfQuality = DEFAULT_QUALITY;
\r
1187 lf->lfPitchAndFamily = DEFAULT_PITCH|FF_DONTCARE;
\r
1188 safeStrCpy(lf->lfFaceName, mfp->faceName, sizeof(lf->lfFaceName)/sizeof(lf->lfFaceName[0]) );
\r
1192 CreateFontInMF(MyFont *mf)
\r
1194 LFfromMFP(&mf->lf, &mf->mfp);
\r
1195 if (mf->hf) DeleteObject(mf->hf);
\r
1196 mf->hf = CreateFontIndirect(&mf->lf);
\r
1199 // [HGM] This platform-dependent table provides the location for storing the color info
\r
1201 colorVariable[] = {
\r
1202 &whitePieceColor,
\r
1203 &blackPieceColor,
\r
1204 &lightSquareColor,
\r
1205 &darkSquareColor,
\r
1206 &highlightSquareColor,
\r
1207 &premoveHighlightColor,
\r
1209 &consoleBackgroundColor,
\r
1210 &appData.fontForeColorWhite,
\r
1211 &appData.fontBackColorWhite,
\r
1212 &appData.fontForeColorBlack,
\r
1213 &appData.fontBackColorBlack,
\r
1214 &appData.evalHistColorWhite,
\r
1215 &appData.evalHistColorBlack,
\r
1216 &appData.highlightArrowColor,
\r
1219 /* Command line font name parser. NULL name means do nothing.
\r
1220 Syntax like "Courier New:10.0 bi" or "Arial:10" or "Arial:10b"
\r
1221 For backward compatibility, syntax without the colon is also
\r
1222 accepted, but font names with digits in them won't work in that case.
\r
1225 ParseFontName(char *name, MyFontParams *mfp)
\r
1228 if (name == NULL) return;
\r
1230 q = strchr(p, ':');
\r
1232 if (q - p >= sizeof(mfp->faceName))
\r
1233 ExitArgError(_("Font name too long:"), name);
\r
1234 memcpy(mfp->faceName, p, q - p);
\r
1235 mfp->faceName[q - p] = NULLCHAR;
\r
1238 q = mfp->faceName;
\r
1239 while (*p && !isdigit(*p)) {
\r
1241 if (q - mfp->faceName >= sizeof(mfp->faceName))
\r
1242 ExitArgError(_("Font name too long:"), name);
\r
1244 while (q > mfp->faceName && q[-1] == ' ') q--;
\r
1247 if (!*p) ExitArgError(_("Font point size missing:"), name);
\r
1248 mfp->pointSize = (float) atof(p);
\r
1249 mfp->bold = (strchr(p, 'b') != NULL);
\r
1250 mfp->italic = (strchr(p, 'i') != NULL);
\r
1251 mfp->underline = (strchr(p, 'u') != NULL);
\r
1252 mfp->strikeout = (strchr(p, 's') != NULL);
\r
1253 mfp->charset = DEFAULT_CHARSET;
\r
1254 q = strchr(p, 'c');
\r
1256 mfp->charset = (BYTE) atoi(q+1);
\r
1260 ParseFont(char *name, int number)
\r
1261 { // wrapper to shield back-end from 'font'
\r
1262 ParseFontName(name, &font[boardSize][number]->mfp);
\r
1267 { // in WB we have a 2D array of fonts; this initializes their description
\r
1269 /* Point font array elements to structures and
\r
1270 parse default font names */
\r
1271 for (i=0; i<NUM_FONTS; i++) {
\r
1272 for (j=0; j<NUM_SIZES; j++) {
\r
1273 font[j][i] = &fontRec[j][i];
\r
1274 ParseFontName(font[j][i]->def, &font[j][i]->mfp);
\r
1281 { // here we create the actual fonts from the selected descriptions
\r
1283 for (i=0; i<NUM_FONTS; i++) {
\r
1284 for (j=0; j<NUM_SIZES; j++) {
\r
1285 CreateFontInMF(font[j][i]);
\r
1289 /* Color name parser.
\r
1290 X version accepts X color names, but this one
\r
1291 handles only the #rrggbb form (hex) or rrr,ggg,bbb (decimal) */
\r
1293 ParseColorName(char *name)
\r
1295 int red, green, blue, count;
\r
1296 char buf[MSG_SIZ];
\r
1298 count = sscanf(name, "#%2x%2x%2x", &red, &green, &blue);
\r
1300 count = sscanf(name, "%3d%*[^0-9]%3d%*[^0-9]%3d",
\r
1301 &red, &green, &blue);
\r
1304 snprintf(buf, MSG_SIZ, _("Can't parse color name %s"), name);
\r
1305 DisplayError(buf, 0);
\r
1306 return RGB(0, 0, 0);
\r
1308 return PALETTERGB(red, green, blue);
\r
1312 ParseColor(int n, char *name)
\r
1313 { // for WinBoard the color is an int, which needs to be derived from the string
\r
1314 if(colorVariable[n]) *(int*)colorVariable[n] = ParseColorName(name);
\r
1318 ParseAttribs(COLORREF *color, int *effects, char* argValue)
\r
1320 char *e = argValue;
\r
1324 if (*e == 'b') eff |= CFE_BOLD;
\r
1325 else if (*e == 'i') eff |= CFE_ITALIC;
\r
1326 else if (*e == 'u') eff |= CFE_UNDERLINE;
\r
1327 else if (*e == 's') eff |= CFE_STRIKEOUT;
\r
1328 else if (*e == '#' || isdigit(*e)) break;
\r
1332 *color = ParseColorName(e);
\r
1336 ParseTextAttribs(ColorClass cc, char *s)
\r
1337 { // [HGM] front-end wrapper that does the platform-dependent call
\r
1338 // for XBoard we would set (&appData.colorShout)[cc] = strdup(s);
\r
1339 ParseAttribs(&textAttribs[cc].color, &textAttribs[cc].effects, s);
\r
1343 ParseBoardSize(void *addr, char *name)
\r
1344 { // [HGM] rewritten with return-value ptr to shield back-end from BoardSize
\r
1345 BoardSize bs = SizeTiny;
\r
1346 while (sizeInfo[bs].name != NULL) {
\r
1347 if (StrCaseCmp(name, sizeInfo[bs].name) == 0) {
\r
1348 *(BoardSize *)addr = bs;
\r
1353 ExitArgError(_("Unrecognized board size value"), name);
\r
1358 { // [HGM] import name from appData first
\r
1361 for (cc = (ColorClass)0; cc < ColorNormal; cc++) {
\r
1362 textAttribs[cc].sound.name = strdup((&appData.soundShout)[cc]);
\r
1363 textAttribs[cc].sound.data = NULL;
\r
1364 MyLoadSound(&textAttribs[cc].sound);
\r
1366 for (cc = ColorNormal; cc < NColorClasses; cc++) {
\r
1367 textAttribs[cc].sound.name = strdup("");
\r
1368 textAttribs[cc].sound.data = NULL;
\r
1370 for (sc = (SoundClass)0; sc < NSoundClasses; sc++) {
\r
1371 sounds[sc].name = strdup((&appData.soundMove)[sc]);
\r
1372 sounds[sc].data = NULL;
\r
1373 MyLoadSound(&sounds[sc]);
\r
1378 SetCommPortDefaults()
\r
1380 memset(&dcb, 0, sizeof(DCB)); // required by VS 2002 +
\r
1381 dcb.DCBlength = sizeof(DCB);
\r
1382 dcb.BaudRate = 9600;
\r
1383 dcb.fBinary = TRUE;
\r
1384 dcb.fParity = FALSE;
\r
1385 dcb.fOutxCtsFlow = FALSE;
\r
1386 dcb.fOutxDsrFlow = FALSE;
\r
1387 dcb.fDtrControl = DTR_CONTROL_ENABLE;
\r
1388 dcb.fDsrSensitivity = FALSE;
\r
1389 dcb.fTXContinueOnXoff = TRUE;
\r
1390 dcb.fOutX = FALSE;
\r
1392 dcb.fNull = FALSE;
\r
1393 dcb.fRtsControl = RTS_CONTROL_ENABLE;
\r
1394 dcb.fAbortOnError = FALSE;
\r
1396 dcb.Parity = SPACEPARITY;
\r
1397 dcb.StopBits = ONESTOPBIT;
\r
1400 // [HGM] args: these three cases taken out to stay in front-end
\r
1402 SaveFontArg(FILE *f, ArgDescriptor *ad)
\r
1403 { // in WinBoard every board size has its own font, and the "argLoc" identifies the table,
\r
1404 // while the curent board size determines the element. This system should be ported to XBoard.
\r
1405 // What the table contains pointers to, and how to print the font description, remains platform-dependent
\r
1407 for (bs=0; bs<NUM_SIZES; bs++) {
\r
1408 MyFontParams *mfp = &font[bs][(int) ad->argLoc]->mfp;
\r
1409 fprintf(f, "/size=%s ", sizeInfo[bs].name);
\r
1410 fprintf(f, "/%s=\"%s:%g%s%s%s%s%sc%d\"\n",
\r
1411 ad->argName, mfp->faceName, mfp->pointSize,
\r
1412 mfp->bold || mfp->italic || mfp->underline || mfp->strikeout ? " " : "",
\r
1413 mfp->bold ? "b" : "",
\r
1414 mfp->italic ? "i" : "",
\r
1415 mfp->underline ? "u" : "",
\r
1416 mfp->strikeout ? "s" : "",
\r
1417 (int)mfp->charset);
\r
1423 { // [HGM] copy the names from the internal WB variables to appData
\r
1426 for (cc = (ColorClass)0; cc < ColorNormal; cc++)
\r
1427 (&appData.soundShout)[cc] = textAttribs[cc].sound.name;
\r
1428 for (sc = (SoundClass)0; sc < NSoundClasses; sc++)
\r
1429 (&appData.soundMove)[sc] = sounds[sc].name;
\r
1433 SaveAttribsArg(FILE *f, ArgDescriptor *ad)
\r
1434 { // here the "argLoc" defines a table index. It could have contained the 'ta' pointer itself, though
\r
1435 MyTextAttribs* ta = &textAttribs[(ColorClass)ad->argLoc];
\r
1436 fprintf(f, "/%s=\"%s%s%s%s%s#%02lx%02lx%02lx\"\n", ad->argName,
\r
1437 (ta->effects & CFE_BOLD) ? "b" : "",
\r
1438 (ta->effects & CFE_ITALIC) ? "i" : "",
\r
1439 (ta->effects & CFE_UNDERLINE) ? "u" : "",
\r
1440 (ta->effects & CFE_STRIKEOUT) ? "s" : "",
\r
1441 (ta->effects) ? " " : "",
\r
1442 ta->color&0xff, (ta->color >> 8)&0xff, (ta->color >> 16)&0xff);
\r
1446 SaveColor(FILE *f, ArgDescriptor *ad)
\r
1447 { // in WinBoard the color is an int and has to be converted to text. In X it would be a string already?
\r
1448 COLORREF color = *(COLORREF *)colorVariable[(int)ad->argLoc];
\r
1449 fprintf(f, "/%s=#%02lx%02lx%02lx\n", ad->argName,
\r
1450 color&0xff, (color>>8)&0xff, (color>>16)&0xff);
\r
1454 SaveBoardSize(FILE *f, char *name, void *addr)
\r
1455 { // wrapper to shield back-end from BoardSize & sizeInfo
\r
1456 fprintf(f, "/%s=%s\n", name, sizeInfo[*(BoardSize *)addr].name);
\r
1460 ParseCommPortSettings(char *s)
\r
1461 { // wrapper to keep dcb from back-end
\r
1462 ParseCommSettings(s, &dcb);
\r
1467 { // wrapper to shield use of window handles from back-end (make addressible by number?)
\r
1468 GetActualPlacement(hwndMain, &wpMain);
\r
1469 GetActualPlacement(hwndConsole, &wpConsole);
\r
1470 GetActualPlacement(commentDialog, &wpComment);
\r
1471 GetActualPlacement(editTagsDialog, &wpTags);
\r
1472 GetActualPlacement(gameListDialog, &wpGameList);
\r
1473 GetActualPlacement(moveHistoryDialog, &wpMoveHistory);
\r
1474 GetActualPlacement(evalGraphDialog, &wpEvalGraph);
\r
1475 GetActualPlacement(engineOutputDialog, &wpEngineOutput);
\r
1479 PrintCommPortSettings(FILE *f, char *name)
\r
1480 { // wrapper to shield back-end from DCB
\r
1481 PrintCommSettings(f, name, &dcb);
\r
1485 MySearchPath(char *installDir, char *name, char *fullname)
\r
1487 char *dummy, buf[MSG_SIZ], *p = name, *q;
\r
1488 if(name[0]== '%') {
\r
1489 fullname[0] = 0; // [HGM] first expand any environment variables in the given name
\r
1490 while(*p == '%' && (q = strchr(p+1, '%'))) { // [HGM] recognize %*% as environment variable
\r
1491 safeStrCpy(buf, p+1, sizeof(buf)/sizeof(buf[0]) );
\r
1492 *strchr(buf, '%') = 0;
\r
1493 strcat(fullname, getenv(buf));
\r
1494 p = q+1; while(*p == '\\') { strcat(fullname, "\\"); p++; }
\r
1496 strcat(fullname, p); // after environment variables (if any), take the remainder of the given name
\r
1497 if(appData.debugMode) fprintf(debugFP, "name = '%s', expanded name = '%s'\n", name, fullname);
\r
1498 return (int) strlen(fullname);
\r
1500 return (int) SearchPath(installDir, name, NULL, MSG_SIZ, fullname, &dummy);
\r
1504 MyGetFullPathName(char *name, char *fullname)
\r
1507 return (int) GetFullPathName(name, MSG_SIZ, fullname, &dummy);
\r
1512 { // [HGM] args: allows testing if main window is realized from back-end
\r
1513 return hwndMain != NULL;
\r
1517 PopUpStartupDialog()
\r
1521 LoadLanguageFile(appData.language);
\r
1522 lpProc = MakeProcInstance((FARPROC)StartupDialog, hInst);
\r
1523 DialogBox(hInst, MAKEINTRESOURCE(DLG_Startup), NULL, (DLGPROC)lpProc);
\r
1524 FreeProcInstance(lpProc);
\r
1527 /*---------------------------------------------------------------------------*\
\r
1529 * GDI board drawing routines
\r
1531 \*---------------------------------------------------------------------------*/
\r
1533 /* [AS] Draw square using background texture */
\r
1534 static void DrawTile( int dx, int dy, int dw, int dh, HDC dst, HDC src, int mode, int sx, int sy )
\r
1539 return; /* Should never happen! */
\r
1542 SetGraphicsMode( dst, GM_ADVANCED );
\r
1549 /* X reflection */
\r
1554 x.eDx = (FLOAT) dw + dx - 1;
\r
1557 SetWorldTransform( dst, &x );
\r
1560 /* Y reflection */
\r
1566 x.eDy = (FLOAT) dh + dy - 1;
\r
1568 SetWorldTransform( dst, &x );
\r
1576 x.eDx = (FLOAT) dx;
\r
1577 x.eDy = (FLOAT) dy;
\r
1580 SetWorldTransform( dst, &x );
\r
1584 BitBlt( dst, dx, dy, dw, dh, src, sx, sy, SRCCOPY );
\r
1592 SetWorldTransform( dst, &x );
\r
1594 ModifyWorldTransform( dst, 0, MWT_IDENTITY );
\r
1597 /* [AS] [HGM] Make room for more piece types, so all pieces can be different */
\r
1599 PM_WP = (int) WhitePawn,
\r
1600 PM_WN = (int) WhiteKnight,
\r
1601 PM_WB = (int) WhiteBishop,
\r
1602 PM_WR = (int) WhiteRook,
\r
1603 PM_WQ = (int) WhiteQueen,
\r
1604 PM_WF = (int) WhiteFerz,
\r
1605 PM_WW = (int) WhiteWazir,
\r
1606 PM_WE = (int) WhiteAlfil,
\r
1607 PM_WM = (int) WhiteMan,
\r
1608 PM_WO = (int) WhiteCannon,
\r
1609 PM_WU = (int) WhiteUnicorn,
\r
1610 PM_WH = (int) WhiteNightrider,
\r
1611 PM_WA = (int) WhiteAngel,
\r
1612 PM_WC = (int) WhiteMarshall,
\r
1613 PM_WAB = (int) WhiteCardinal,
\r
1614 PM_WD = (int) WhiteDragon,
\r
1615 PM_WL = (int) WhiteLance,
\r
1616 PM_WS = (int) WhiteCobra,
\r
1617 PM_WV = (int) WhiteFalcon,
\r
1618 PM_WSG = (int) WhiteSilver,
\r
1619 PM_WG = (int) WhiteGrasshopper,
\r
1620 PM_WK = (int) WhiteKing,
\r
1621 PM_BP = (int) BlackPawn,
\r
1622 PM_BN = (int) BlackKnight,
\r
1623 PM_BB = (int) BlackBishop,
\r
1624 PM_BR = (int) BlackRook,
\r
1625 PM_BQ = (int) BlackQueen,
\r
1626 PM_BF = (int) BlackFerz,
\r
1627 PM_BW = (int) BlackWazir,
\r
1628 PM_BE = (int) BlackAlfil,
\r
1629 PM_BM = (int) BlackMan,
\r
1630 PM_BO = (int) BlackCannon,
\r
1631 PM_BU = (int) BlackUnicorn,
\r
1632 PM_BH = (int) BlackNightrider,
\r
1633 PM_BA = (int) BlackAngel,
\r
1634 PM_BC = (int) BlackMarshall,
\r
1635 PM_BG = (int) BlackGrasshopper,
\r
1636 PM_BAB = (int) BlackCardinal,
\r
1637 PM_BD = (int) BlackDragon,
\r
1638 PM_BL = (int) BlackLance,
\r
1639 PM_BS = (int) BlackCobra,
\r
1640 PM_BV = (int) BlackFalcon,
\r
1641 PM_BSG = (int) BlackSilver,
\r
1642 PM_BK = (int) BlackKing
\r
1645 static HFONT hPieceFont = NULL;
\r
1646 static HBITMAP hPieceMask[(int) EmptySquare];
\r
1647 static HBITMAP hPieceFace[(int) EmptySquare];
\r
1648 static int fontBitmapSquareSize = 0;
\r
1649 static char pieceToFontChar[(int) EmptySquare] =
\r
1650 { 'p', 'n', 'b', 'r', 'q',
\r
1651 'n', 'b', 'p', 'n', 'b', 'r', 'b', 'r', 'q', 'k',
\r
1652 'k', 'o', 'm', 'v', 't', 'w',
\r
1653 'v', 't', 'o', 'm', 'v', 't', 'v', 't', 'w', 'l',
\r
1656 extern BOOL SetCharTable( char *table, const char * map );
\r
1657 /* [HGM] moved to backend.c */
\r
1659 static void SetPieceBackground( HDC hdc, COLORREF color, int mode )
\r
1662 BYTE r1 = GetRValue( color );
\r
1663 BYTE g1 = GetGValue( color );
\r
1664 BYTE b1 = GetBValue( color );
\r
1670 /* Create a uniform background first */
\r
1671 hbrush = CreateSolidBrush( color );
\r
1672 SetRect( &rc, 0, 0, squareSize, squareSize );
\r
1673 FillRect( hdc, &rc, hbrush );
\r
1674 DeleteObject( hbrush );
\r
1677 /* Vertical gradient, good for pawn, knight and rook, less for queen and king */
\r
1678 int steps = squareSize / 2;
\r
1681 for( i=0; i<steps; i++ ) {
\r
1682 BYTE r = r1 - (r1-r2) * i / steps;
\r
1683 BYTE g = g1 - (g1-g2) * i / steps;
\r
1684 BYTE b = b1 - (b1-b2) * i / steps;
\r
1686 hbrush = CreateSolidBrush( RGB(r,g,b) );
\r
1687 SetRect( &rc, i + squareSize - steps, 0, i + squareSize - steps + 1, squareSize );
\r
1688 FillRect( hdc, &rc, hbrush );
\r
1689 DeleteObject(hbrush);
\r
1692 else if( mode == 2 ) {
\r
1693 /* Diagonal gradient, good more or less for every piece */
\r
1694 POINT triangle[3];
\r
1695 HPEN hpen = SelectObject( hdc, GetStockObject(NULL_PEN) );
\r
1696 HBRUSH hbrush_old;
\r
1697 int steps = squareSize;
\r
1700 triangle[0].x = squareSize - steps;
\r
1701 triangle[0].y = squareSize;
\r
1702 triangle[1].x = squareSize;
\r
1703 triangle[1].y = squareSize;
\r
1704 triangle[2].x = squareSize;
\r
1705 triangle[2].y = squareSize - steps;
\r
1707 for( i=0; i<steps; i++ ) {
\r
1708 BYTE r = r1 - (r1-r2) * i / steps;
\r
1709 BYTE g = g1 - (g1-g2) * i / steps;
\r
1710 BYTE b = b1 - (b1-b2) * i / steps;
\r
1712 hbrush = CreateSolidBrush( RGB(r,g,b) );
\r
1713 hbrush_old = SelectObject( hdc, hbrush );
\r
1714 Polygon( hdc, triangle, 3 );
\r
1715 SelectObject( hdc, hbrush_old );
\r
1716 DeleteObject(hbrush);
\r
1721 SelectObject( hdc, hpen );
\r
1726 [AS] The method I use to create the bitmaps it a bit tricky, but it
\r
1727 seems to work ok. The main problem here is to find the "inside" of a chess
\r
1728 piece: follow the steps as explained below.
\r
1730 static void CreatePieceMaskFromFont( HDC hdc_window, HDC hdc, int index )
\r
1734 COLORREF chroma = RGB(0xFF,0x00,0xFF);
\r
1738 int backColor = whitePieceColor;
\r
1739 int foreColor = blackPieceColor;
\r
1741 if( index < (int)BlackPawn && appData.fontBackColorWhite != appData.fontForeColorWhite ) {
\r
1742 backColor = appData.fontBackColorWhite;
\r
1743 foreColor = appData.fontForeColorWhite;
\r
1745 else if( index >= (int)BlackPawn && appData.fontBackColorBlack != appData.fontForeColorBlack ) {
\r
1746 backColor = appData.fontBackColorBlack;
\r
1747 foreColor = appData.fontForeColorBlack;
\r
1751 hbm = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );
\r
1753 hbm_old = SelectObject( hdc, hbm );
\r
1757 rc.right = squareSize;
\r
1758 rc.bottom = squareSize;
\r
1760 /* Step 1: background is now black */
\r
1761 FillRect( hdc, &rc, GetStockObject(BLACK_BRUSH) );
\r
1763 GetTextExtentPoint32( hdc, &pieceToFontChar[index], 1, &sz );
\r
1765 pt.x = (squareSize - sz.cx) / 2;
\r
1766 pt.y = (squareSize - sz.cy) / 2;
\r
1768 SetBkMode( hdc, TRANSPARENT );
\r
1769 SetTextColor( hdc, chroma );
\r
1770 /* Step 2: the piece has been drawn in purple, there are now black and purple in this bitmap */
\r
1771 TextOut( hdc, pt.x, pt.y, &pieceToFontChar[appData.allWhite && index >= (int)BlackPawn ? index - (int)BlackPawn : index], 1 );
\r
1773 SelectObject( hdc, GetStockObject(WHITE_BRUSH) );
\r
1774 /* Step 3: the area outside the piece is filled with white */
\r
1775 // FloodFill( hdc, 0, 0, chroma );
\r
1776 ExtFloodFill( hdc, 0, 0, 0, FLOODFILLSURFACE );
\r
1777 ExtFloodFill( hdc, 0, squareSize-1, 0, FLOODFILLSURFACE ); // [HGM] fill from all 4 corners, for if piece too big
\r
1778 ExtFloodFill( hdc, squareSize-1, 0, 0, FLOODFILLSURFACE );
\r
1779 ExtFloodFill( hdc, squareSize-1, squareSize-1, 0, FLOODFILLSURFACE );
\r
1780 SelectObject( hdc, GetStockObject(BLACK_BRUSH) );
\r
1782 Step 4: this is the tricky part, the area inside the piece is filled with black,
\r
1783 but if the start point is not inside the piece we're lost!
\r
1784 There should be a better way to do this... if we could create a region or path
\r
1785 from the fill operation we would be fine for example.
\r
1787 // FloodFill( hdc, squareSize / 2, squareSize / 2, RGB(0xFF,0xFF,0xFF) );
\r
1788 ExtFloodFill( hdc, squareSize / 2, squareSize / 2, RGB(0xFF,0xFF,0xFF), FLOODFILLBORDER );
\r
1790 { /* [HGM] shave off edges of mask, in an attempt to correct for the fact that FloodFill does not work correctly under Win XP */
\r
1791 HDC dc2 = CreateCompatibleDC( hdc_window );
\r
1792 HBITMAP bm2 = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );
\r
1794 SelectObject( dc2, bm2 );
\r
1795 BitBlt( dc2, 0, 0, squareSize, squareSize, hdc, 0, 0, SRCCOPY ); // make copy
\r
1796 BitBlt( hdc, 0, 1, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );
\r
1797 BitBlt( hdc, 2, 1, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );
\r
1798 BitBlt( hdc, 1, 0, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );
\r
1799 BitBlt( hdc, 1, 2, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );
\r
1802 DeleteObject( bm2 );
\r
1805 SetTextColor( hdc, 0 );
\r
1807 Step 5: some fonts have "disconnected" areas that are skipped by the fill:
\r
1808 draw the piece again in black for safety.
\r
1810 TextOut( hdc, pt.x, pt.y, &pieceToFontChar[appData.allWhite && index >= (int)BlackPawn ? index - (int)BlackPawn : index], 1 );
\r
1812 SelectObject( hdc, hbm_old );
\r
1814 if( hPieceMask[index] != NULL ) {
\r
1815 DeleteObject( hPieceMask[index] );
\r
1818 hPieceMask[index] = hbm;
\r
1821 hbm = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );
\r
1823 SelectObject( hdc, hbm );
\r
1826 HDC dc1 = CreateCompatibleDC( hdc_window );
\r
1827 HDC dc2 = CreateCompatibleDC( hdc_window );
\r
1828 HBITMAP bm2 = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );
\r
1830 SelectObject( dc1, hPieceMask[index] );
\r
1831 SelectObject( dc2, bm2 );
\r
1832 FillRect( dc2, &rc, GetStockObject(WHITE_BRUSH) );
\r
1833 BitBlt( dc2, 0, 0, squareSize, squareSize, dc1, 0, 0, SRCINVERT );
\r
1836 Now dc2 contains the inverse of the piece mask, i.e. a mask that preserves
\r
1837 the piece background and deletes (makes transparent) the rest.
\r
1838 Thanks to that mask, we are free to paint the background with the greates
\r
1839 freedom, as we'll be able to mask off the unwanted parts when finished.
\r
1840 We use this, to make gradients and give the pieces a "roundish" look.
\r
1842 SetPieceBackground( hdc, backColor, 2 );
\r
1843 BitBlt( hdc, 0, 0, squareSize, squareSize, dc2, 0, 0, SRCAND );
\r
1847 DeleteObject( bm2 );
\r
1850 SetTextColor( hdc, foreColor );
\r
1851 TextOut( hdc, pt.x, pt.y, &pieceToFontChar[appData.allWhite && index >= (int)BlackPawn ? index - (int)BlackPawn : index], 1 );
\r
1853 SelectObject( hdc, hbm_old );
\r
1855 if( hPieceFace[index] != NULL ) {
\r
1856 DeleteObject( hPieceFace[index] );
\r
1859 hPieceFace[index] = hbm;
\r
1862 static int TranslatePieceToFontPiece( int piece )
\r
1892 case BlackMarshall:
\r
1896 case BlackNightrider:
\r
1902 case BlackUnicorn:
\r
1906 case BlackGrasshopper:
\r
1918 case BlackCardinal:
\r
1925 case WhiteMarshall:
\r
1929 case WhiteNightrider:
\r
1935 case WhiteUnicorn:
\r
1939 case WhiteGrasshopper:
\r
1951 case WhiteCardinal:
\r
1960 void CreatePiecesFromFont()
\r
1963 HDC hdc_window = NULL;
\r
1969 if( fontBitmapSquareSize < 0 ) {
\r
1970 /* Something went seriously wrong in the past: do not try to recreate fonts! */
\r
1974 if( appData.renderPiecesWithFont == NULL || appData.renderPiecesWithFont[0] == NULLCHAR || appData.renderPiecesWithFont[0] == '*' ) {
\r
1975 fontBitmapSquareSize = -1;
\r
1979 if( fontBitmapSquareSize != squareSize ) {
\r
1980 hdc_window = GetDC( hwndMain );
\r
1981 hdc = CreateCompatibleDC( hdc_window );
\r
1983 if( hPieceFont != NULL ) {
\r
1984 DeleteObject( hPieceFont );
\r
1987 for( i=0; i<=(int)BlackKing; i++ ) {
\r
1988 hPieceMask[i] = NULL;
\r
1989 hPieceFace[i] = NULL;
\r
1995 if( appData.fontPieceSize >= 50 && appData.fontPieceSize <= 150 ) {
\r
1996 fontHeight = appData.fontPieceSize;
\r
1999 fontHeight = (fontHeight * squareSize) / 100;
\r
2001 lf.lfHeight = -MulDiv( fontHeight, GetDeviceCaps(hdc, LOGPIXELSY), 72 );
\r
2003 lf.lfEscapement = 0;
\r
2004 lf.lfOrientation = 0;
\r
2005 lf.lfWeight = FW_NORMAL;
\r
2007 lf.lfUnderline = 0;
\r
2008 lf.lfStrikeOut = 0;
\r
2009 lf.lfCharSet = DEFAULT_CHARSET;
\r
2010 lf.lfOutPrecision = OUT_DEFAULT_PRECIS;
\r
2011 lf.lfClipPrecision = CLIP_DEFAULT_PRECIS;
\r
2012 lf.lfQuality = PROOF_QUALITY;
\r
2013 lf.lfPitchAndFamily = DEFAULT_PITCH | FF_DONTCARE;
\r
2014 strncpy( lf.lfFaceName, appData.renderPiecesWithFont, sizeof(lf.lfFaceName) );
\r
2015 lf.lfFaceName[ sizeof(lf.lfFaceName) - 1 ] = '\0';
\r
2017 hPieceFont = CreateFontIndirect( &lf );
\r
2019 if( hPieceFont == NULL ) {
\r
2020 fontBitmapSquareSize = -2;
\r
2023 /* Setup font-to-piece character table */
\r
2024 if( ! SetCharTable(pieceToFontChar, appData.fontToPieceTable) ) {
\r
2025 /* No (or wrong) global settings, try to detect the font */
\r
2026 if( strstr(lf.lfFaceName,"Alpha") != NULL ) {
\r
2028 SetCharTable(pieceToFontChar, "phbrqkojntwl");
\r
2030 else if( strstr(lf.lfFaceName,"DiagramTT") != NULL ) {
\r
2031 /* DiagramTT* family */
\r
2032 SetCharTable(pieceToFontChar, "PNLRQKpnlrqk");
\r
2034 else if( strstr(lf.lfFaceName,"WinboardF") != NULL ) {
\r
2035 /* Fairy symbols */
\r
2036 SetCharTable(pieceToFontChar, "PNBRQFEACWMOHIJGDVSLUKpnbrqfeacwmohijgdvsluk");
\r
2038 else if( strstr(lf.lfFaceName,"GC2004D") != NULL ) {
\r
2039 /* Good Companion (Some characters get warped as literal :-( */
\r
2040 char s[] = "1cmWG0??S??oYI23wgQU";
\r
2041 s[0]=0xB9; s[1]=0xA9; s[6]=0xB1; s[11]=0xBB; s[12]=0xAB; s[17]=0xB3;
\r
2042 SetCharTable(pieceToFontChar, s);
\r
2045 /* Cases, Condal, Leipzig, Lucena, Marroquin, Merida, Usual */
\r
2046 SetCharTable(pieceToFontChar, "pnbrqkomvtwl");
\r
2050 /* Create bitmaps */
\r
2051 hfont_old = SelectObject( hdc, hPieceFont );
\r
2052 for(i=(int)WhitePawn; i<(int)EmptySquare; i++) /* [HGM] made a loop for this */
\r
2053 if(PieceToChar((ChessSquare)i) != '.') /* skip unused pieces */
\r
2054 CreatePieceMaskFromFont( hdc_window, hdc, i );
\r
2056 SelectObject( hdc, hfont_old );
\r
2058 fontBitmapSquareSize = squareSize;
\r
2062 if( hdc != NULL ) {
\r
2066 if( hdc_window != NULL ) {
\r
2067 ReleaseDC( hwndMain, hdc_window );
\r
2072 DoLoadBitmap(HINSTANCE hinst, char *piece, int squareSize, char *suffix)
\r
2076 snprintf(name, sizeof(name)/sizeof(name[0]), "%s%d%s", piece, squareSize, suffix);
\r
2077 if (gameInfo.event &&
\r
2078 strcmp(gameInfo.event, "Easter Egg Hunt") == 0 &&
\r
2079 strcmp(name, "k80s") == 0) {
\r
2080 safeStrCpy(name, "tim", sizeof(name)/sizeof(name[0]) );
\r
2082 return LoadBitmap(hinst, name);
\r
2086 /* Insert a color into the program's logical palette
\r
2087 structure. This code assumes the given color is
\r
2088 the result of the RGB or PALETTERGB macro, and it
\r
2089 knows how those macros work (which is documented).
\r
2092 InsertInPalette(COLORREF color)
\r
2094 LPPALETTEENTRY pe = &(pLogPal->palPalEntry[pLogPal->palNumEntries]);
\r
2096 if (pLogPal->palNumEntries++ >= PALETTESIZE) {
\r
2097 DisplayFatalError(_("Too many colors"), 0, 1);
\r
2098 pLogPal->palNumEntries--;
\r
2102 pe->peFlags = (char) 0;
\r
2103 pe->peRed = (char) (0xFF & color);
\r
2104 pe->peGreen = (char) (0xFF & (color >> 8));
\r
2105 pe->peBlue = (char) (0xFF & (color >> 16));
\r
2111 InitDrawingColors()
\r
2113 if (pLogPal == NULL) {
\r
2114 /* Allocate enough memory for a logical palette with
\r
2115 * PALETTESIZE entries and set the size and version fields
\r
2116 * of the logical palette structure.
\r
2118 pLogPal = (NPLOGPALETTE)
\r
2119 LocalAlloc(LMEM_FIXED, (sizeof(LOGPALETTE) +
\r
2120 (sizeof(PALETTEENTRY) * (PALETTESIZE))));
\r
2121 pLogPal->palVersion = 0x300;
\r
2123 pLogPal->palNumEntries = 0;
\r
2125 InsertInPalette(lightSquareColor);
\r
2126 InsertInPalette(darkSquareColor);
\r
2127 InsertInPalette(whitePieceColor);
\r
2128 InsertInPalette(blackPieceColor);
\r
2129 InsertInPalette(highlightSquareColor);
\r
2130 InsertInPalette(premoveHighlightColor);
\r
2132 /* create a logical color palette according the information
\r
2133 * in the LOGPALETTE structure.
\r
2135 hPal = CreatePalette((LPLOGPALETTE) pLogPal);
\r
2137 lightSquareBrush = CreateSolidBrush(lightSquareColor);
\r
2138 blackSquareBrush = CreateSolidBrush(blackPieceColor);
\r
2139 darkSquareBrush = CreateSolidBrush(darkSquareColor);
\r
2140 whitePieceBrush = CreateSolidBrush(whitePieceColor);
\r
2141 blackPieceBrush = CreateSolidBrush(blackPieceColor);
\r
2142 iconBkgndBrush = CreateSolidBrush(GetSysColor(COLOR_BACKGROUND));
\r
2143 explodeBrush = CreateSolidBrush(highlightSquareColor); // [HGM] atomic
\r
2144 markerBrush = CreateSolidBrush(premoveHighlightColor); // [HGM] markers
\r
2145 /* [AS] Force rendering of the font-based pieces */
\r
2146 if( fontBitmapSquareSize > 0 ) {
\r
2147 fontBitmapSquareSize = 0;
\r
2153 BoardWidth(int boardSize, int n)
\r
2154 { /* [HGM] argument n added to allow different width and height */
\r
2155 int lineGap = sizeInfo[boardSize].lineGap;
\r
2157 if( appData.overrideLineGap >= 0 && appData.overrideLineGap <= 5 ) {
\r
2158 lineGap = appData.overrideLineGap;
\r
2161 return (n + 1) * lineGap +
\r
2162 n * sizeInfo[boardSize].squareSize;
\r
2165 /* Respond to board resize by dragging edge */
\r
2167 ResizeBoard(int newSizeX, int newSizeY, int flags)
\r
2169 BoardSize newSize = NUM_SIZES - 1;
\r
2170 static int recurse = 0;
\r
2171 if (IsIconic(hwndMain)) return;
\r
2172 if (recurse > 0) return;
\r
2174 while (newSize > 0) {
\r
2175 InitDrawingSizes(newSize+1000, 0); // [HGM] kludge to update sizeInfo without visible effects
\r
2176 if(newSizeX >= sizeInfo[newSize].cliWidth &&
\r
2177 newSizeY >= sizeInfo[newSize].cliHeight) break;
\r
2180 boardSize = newSize;
\r
2181 InitDrawingSizes(boardSize, flags);
\r
2186 extern Boolean twoBoards, partnerUp; // [HGM] dual
\r
2189 InitDrawingSizes(BoardSize boardSize, int flags)
\r
2191 int i, boardWidth, boardHeight; /* [HGM] height treated separately */
\r
2192 ChessSquare piece;
\r
2193 static int oldBoardSize = -1, oldTinyLayout = 0;
\r
2195 SIZE clockSize, messageSize;
\r
2197 char buf[MSG_SIZ];
\r
2199 HMENU hmenu = GetMenu(hwndMain);
\r
2200 RECT crect, wrect, oldRect;
\r
2202 LOGBRUSH logbrush;
\r
2204 int suppressVisibleEffects = 0; // [HGM] kludge to request updating sizeInfo only
\r
2205 if((int)boardSize >= 1000 ) { boardSize -= 1000; suppressVisibleEffects = 1; }
\r
2207 /* [HGM] call with -2 uses old size (for if nr of files, ranks changes) */
\r
2208 if(boardSize == (BoardSize)(-2) ) boardSize = oldBoardSize;
\r
2210 oldRect.left = wpMain.x; //[HGM] placement: remember previous window params
\r
2211 oldRect.top = wpMain.y;
\r
2212 oldRect.right = wpMain.x + wpMain.width;
\r
2213 oldRect.bottom = wpMain.y + wpMain.height;
\r
2215 tinyLayout = sizeInfo[boardSize].tinyLayout;
\r
2216 smallLayout = sizeInfo[boardSize].smallLayout;
\r
2217 squareSize = sizeInfo[boardSize].squareSize;
\r
2218 lineGap = sizeInfo[boardSize].lineGap;
\r
2219 minorSize = 0; /* [HGM] Kludge to see if demagnified pieces need to be shifted */
\r
2221 if( appData.overrideLineGap >= 0 && appData.overrideLineGap <= 5 ) {
\r
2222 lineGap = appData.overrideLineGap;
\r
2225 if (tinyLayout != oldTinyLayout) {
\r
2226 long style = GetWindowLong(hwndMain, GWL_STYLE);
\r
2228 style &= ~WS_SYSMENU;
\r
2229 InsertMenu(hmenu, IDM_Exit, MF_BYCOMMAND, IDM_Minimize,
\r
2230 "&Minimize\tCtrl+F4");
\r
2232 style |= WS_SYSMENU;
\r
2233 RemoveMenu(hmenu, IDM_Minimize, MF_BYCOMMAND);
\r
2235 SetWindowLong(hwndMain, GWL_STYLE, style);
\r
2237 for (i=0; menuBarText[tinyLayout][i]; i++) {
\r
2238 ModifyMenu(hmenu, i, MF_STRING|MF_BYPOSITION|MF_POPUP,
\r
2239 (UINT)GetSubMenu(hmenu, i), T_(menuBarText[tinyLayout][i]));
\r
2241 DrawMenuBar(hwndMain);
\r
2244 boardWidth = BoardWidth(boardSize, BOARD_WIDTH);
\r
2245 boardHeight = BoardWidth(boardSize, BOARD_HEIGHT);
\r
2247 /* Get text area sizes */
\r
2248 hdc = GetDC(hwndMain);
\r
2249 if (appData.clockMode) {
\r
2250 snprintf(buf, MSG_SIZ, _("White: %s"), TimeString(23*60*60*1000L));
\r
2252 snprintf(buf, MSG_SIZ, _("White"));
\r
2254 oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);
\r
2255 GetTextExtentPoint(hdc, buf, strlen(buf), &clockSize);
\r
2256 SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);
\r
2257 str = _("We only care about the height here");
\r
2258 GetTextExtentPoint(hdc, str, strlen(str), &messageSize);
\r
2259 SelectObject(hdc, oldFont);
\r
2260 ReleaseDC(hwndMain, hdc);
\r
2262 /* Compute where everything goes */
\r
2263 if((first.programLogo || second.programLogo) && !tinyLayout) {
\r
2264 /* [HGM] logo: if either logo is on, reserve space for it */
\r
2265 logoHeight = 2*clockSize.cy;
\r
2266 leftLogoRect.left = OUTER_MARGIN;
\r
2267 leftLogoRect.right = leftLogoRect.left + 4*clockSize.cy;
\r
2268 leftLogoRect.top = OUTER_MARGIN;
\r
2269 leftLogoRect.bottom = OUTER_MARGIN + logoHeight;
\r
2271 rightLogoRect.right = OUTER_MARGIN + boardWidth;
\r
2272 rightLogoRect.left = rightLogoRect.right - 4*clockSize.cy;
\r
2273 rightLogoRect.top = OUTER_MARGIN;
\r
2274 rightLogoRect.bottom = OUTER_MARGIN + logoHeight;
\r
2277 whiteRect.left = leftLogoRect.right;
\r
2278 whiteRect.right = OUTER_MARGIN + boardWidth/2 - INNER_MARGIN/2;
\r
2279 whiteRect.top = OUTER_MARGIN;
\r
2280 whiteRect.bottom = whiteRect.top + logoHeight;
\r
2282 blackRect.right = rightLogoRect.left;
\r
2283 blackRect.left = whiteRect.right + INNER_MARGIN;
\r
2284 blackRect.top = whiteRect.top;
\r
2285 blackRect.bottom = whiteRect.bottom;
\r
2287 whiteRect.left = OUTER_MARGIN;
\r
2288 whiteRect.right = whiteRect.left + boardWidth/2 - INNER_MARGIN/2;
\r
2289 whiteRect.top = OUTER_MARGIN;
\r
2290 whiteRect.bottom = whiteRect.top + clockSize.cy;
\r
2292 blackRect.left = whiteRect.right + INNER_MARGIN;
\r
2293 blackRect.right = blackRect.left + boardWidth/2 - 1;
\r
2294 blackRect.top = whiteRect.top;
\r
2295 blackRect.bottom = whiteRect.bottom;
\r
2297 logoHeight = 0; // [HGM] logo: suppress logo after change to tiny layout!
\r
2300 messageRect.left = OUTER_MARGIN + MESSAGE_LINE_LEFTMARGIN;
\r
2301 if (appData.showButtonBar) {
\r
2302 messageRect.right = OUTER_MARGIN + boardWidth // [HGM] logo: expressed independent of clock placement
\r
2303 - N_BUTTONS*BUTTON_WIDTH - MESSAGE_LINE_LEFTMARGIN;
\r
2305 messageRect.right = OUTER_MARGIN + boardWidth;
\r
2307 messageRect.top = whiteRect.bottom + INNER_MARGIN;
\r
2308 messageRect.bottom = messageRect.top + messageSize.cy;
\r
2310 boardRect.left = OUTER_MARGIN;
\r
2311 boardRect.right = boardRect.left + boardWidth;
\r
2312 boardRect.top = messageRect.bottom + INNER_MARGIN;
\r
2313 boardRect.bottom = boardRect.top + boardHeight;
\r
2315 sizeInfo[boardSize].cliWidth = boardRect.right + OUTER_MARGIN;
\r
2316 sizeInfo[boardSize].cliHeight = boardRect.bottom + OUTER_MARGIN;
\r
2317 oldBoardSize = boardSize;
\r
2318 oldTinyLayout = tinyLayout;
\r
2319 winW = 2 * GetSystemMetrics(SM_CXFRAME) + boardRect.right + OUTER_MARGIN;
\r
2320 winH = 2 * GetSystemMetrics(SM_CYFRAME) + GetSystemMetrics(SM_CYMENU) +
\r
2321 GetSystemMetrics(SM_CYCAPTION) + boardRect.bottom + OUTER_MARGIN;
\r
2322 winW *= 1 + twoBoards;
\r
2323 if(suppressVisibleEffects) return; // [HGM] when called for filling sizeInfo only
\r
2324 wpMain.width = winW; // [HGM] placement: set through temporary which can used by initial sizing choice
\r
2325 wpMain.height = winH; // without disturbing window attachments
\r
2326 GetWindowRect(hwndMain, &wrect);
\r
2327 SetWindowPos(hwndMain, NULL, 0, 0, wpMain.width, wpMain.height,
\r
2328 SWP_NOCOPYBITS|SWP_NOZORDER|SWP_NOMOVE);
\r
2330 // [HGM] placement: let attached windows follow size change.
\r
2331 ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, moveHistoryDialog, &wpMoveHistory );
\r
2332 ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, evalGraphDialog, &wpEvalGraph );
\r
2333 ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, engineOutputDialog, &wpEngineOutput );
\r
2334 ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, gameListDialog, &wpGameList );
\r
2335 ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, hwndConsole, &wpConsole );
\r
2337 /* compensate if menu bar wrapped */
\r
2338 GetClientRect(hwndMain, &crect);
\r
2339 offby = boardRect.bottom + OUTER_MARGIN - crect.bottom;
\r
2340 wpMain.height += offby;
\r
2342 case WMSZ_TOPLEFT:
\r
2343 SetWindowPos(hwndMain, NULL,
\r
2344 wrect.right - wpMain.width, wrect.bottom - wpMain.height,
\r
2345 wpMain.width, wpMain.height, SWP_NOCOPYBITS|SWP_NOZORDER);
\r
2348 case WMSZ_TOPRIGHT:
\r
2350 SetWindowPos(hwndMain, NULL,
\r
2351 wrect.left, wrect.bottom - wpMain.height,
\r
2352 wpMain.width, wpMain.height, SWP_NOCOPYBITS|SWP_NOZORDER);
\r
2355 case WMSZ_BOTTOMLEFT:
\r
2357 SetWindowPos(hwndMain, NULL,
\r
2358 wrect.right - wpMain.width, wrect.top,
\r
2359 wpMain.width, wpMain.height, SWP_NOCOPYBITS|SWP_NOZORDER);
\r
2362 case WMSZ_BOTTOMRIGHT:
\r
2366 SetWindowPos(hwndMain, NULL, 0, 0, wpMain.width, wpMain.height,
\r
2367 SWP_NOCOPYBITS|SWP_NOZORDER|SWP_NOMOVE);
\r
2372 for (i = 0; i < N_BUTTONS; i++) {
\r
2373 if (buttonDesc[i].hwnd != NULL) {
\r
2374 DestroyWindow(buttonDesc[i].hwnd);
\r
2375 buttonDesc[i].hwnd = NULL;
\r
2377 if (appData.showButtonBar) {
\r
2378 buttonDesc[i].hwnd =
\r
2379 CreateWindow("BUTTON", buttonDesc[i].label,
\r
2380 WS_VISIBLE | WS_CHILD | BS_PUSHBUTTON,
\r
2381 boardRect.right - BUTTON_WIDTH*(N_BUTTONS-i),
\r
2382 messageRect.top, BUTTON_WIDTH, messageSize.cy, hwndMain,
\r
2383 (HMENU) buttonDesc[i].id,
\r
2384 (HINSTANCE) GetWindowLong(hwndMain, GWL_HINSTANCE), NULL);
\r
2386 SendMessage(buttonDesc[i].hwnd, WM_SETFONT,
\r
2387 (WPARAM)font[boardSize][MESSAGE_FONT]->hf,
\r
2388 MAKELPARAM(FALSE, 0));
\r
2390 if (buttonDesc[i].id == IDM_Pause)
\r
2391 hwndPause = buttonDesc[i].hwnd;
\r
2392 buttonDesc[i].wndproc = (WNDPROC)
\r
2393 SetWindowLong(buttonDesc[i].hwnd, GWL_WNDPROC, (LONG) ButtonProc);
\r
2396 if (gridPen != NULL) DeleteObject(gridPen);
\r
2397 if (highlightPen != NULL) DeleteObject(highlightPen);
\r
2398 if (premovePen != NULL) DeleteObject(premovePen);
\r
2399 if (lineGap != 0) {
\r
2400 logbrush.lbStyle = BS_SOLID;
\r
2401 logbrush.lbColor = RGB(0, 0, 0); /* grid pen color = black */
\r
2403 ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,
\r
2404 lineGap, &logbrush, 0, NULL);
\r
2405 logbrush.lbColor = highlightSquareColor;
\r
2407 ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,
\r
2408 lineGap, &logbrush, 0, NULL);
\r
2410 logbrush.lbColor = premoveHighlightColor;
\r
2412 ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,
\r
2413 lineGap, &logbrush, 0, NULL);
\r
2415 /* [HGM] Loop had to be split in part for vert. and hor. lines */
\r
2416 for (i = 0; i < BOARD_HEIGHT + 1; i++) {
\r
2417 gridEndpoints[i*2].x = boardRect.left + lineGap / 2;
\r
2418 gridEndpoints[i*2].y = gridEndpoints[i*2 + 1].y =
\r
2419 boardRect.top + lineGap / 2 + (i * (squareSize + lineGap));
\r
2420 gridEndpoints[i*2 + 1].x = boardRect.left + lineGap / 2 +
\r
2421 BOARD_WIDTH * (squareSize + lineGap);
\r
2422 gridVertexCounts[i*2] = gridVertexCounts[i*2 + 1] = 2;
\r
2424 for (i = 0; i < BOARD_WIDTH + 1; i++) {
\r
2425 gridEndpoints[i*2 + BOARD_HEIGHT*2 + 2].y = boardRect.top + lineGap / 2;
\r
2426 gridEndpoints[i*2 + BOARD_HEIGHT*2 + 2].x =
\r
2427 gridEndpoints[i*2 + 1 + BOARD_HEIGHT*2 + 2].x = boardRect.left +
\r
2428 lineGap / 2 + (i * (squareSize + lineGap));
\r
2429 gridEndpoints[i*2 + 1 + BOARD_HEIGHT*2 + 2].y =
\r
2430 boardRect.top + BOARD_HEIGHT * (squareSize + lineGap);
\r
2431 gridVertexCounts[i*2] = gridVertexCounts[i*2 + 1] = 2;
\r
2435 /* [HGM] Licensing requirement */
\r
2437 if(gameInfo.variant == VariantGothic) GothicPopUp( GOTHIC, VariantGothic); else
\r
2440 if(gameInfo.variant == VariantFalcon) GothicPopUp( FALCON, VariantFalcon); else
\r
2442 GothicPopUp( "", VariantNormal);
\r
2445 /* if (boardSize == oldBoardSize) return; [HGM] variant might have changed */
\r
2447 /* Load piece bitmaps for this board size */
\r
2448 for (i=0; i<=2; i++) {
\r
2449 for (piece = WhitePawn;
\r
2450 (int) piece < (int) BlackPawn;
\r
2451 piece = (ChessSquare) ((int) piece + 1)) {
\r
2452 if (pieceBitmap[i][piece] != NULL)
\r
2453 DeleteObject(pieceBitmap[i][piece]);
\r
2457 fontBitmapSquareSize = 0; /* [HGM] render: make sure pieces will be recreated, as we might need others now */
\r
2458 // Orthodox Chess pieces
\r
2459 pieceBitmap[0][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "s");
\r
2460 pieceBitmap[0][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "s");
\r
2461 pieceBitmap[0][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "s");
\r
2462 pieceBitmap[0][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "s");
\r
2463 pieceBitmap[0][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "s");
\r
2464 pieceBitmap[1][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "o");
\r
2465 pieceBitmap[1][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "o");
\r
2466 pieceBitmap[1][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "o");
\r
2467 pieceBitmap[1][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "o");
\r
2468 pieceBitmap[1][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "o");
\r
2469 pieceBitmap[2][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "w");
\r
2470 pieceBitmap[2][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "w");
\r
2471 pieceBitmap[2][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "w");
\r
2472 pieceBitmap[2][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "w");
\r
2473 pieceBitmap[2][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "w");
\r
2474 if( gameInfo.variant == VariantShogi && squareSize <= 72 && squareSize >= 33) {
\r
2475 // in Shogi, Hijack the unused Queen for Lance
\r
2476 pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "s");
\r
2477 pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "o");
\r
2478 pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "w");
\r
2480 pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "s");
\r
2481 pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "o");
\r
2482 pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "w");
\r
2485 if(squareSize <= 72 && squareSize >= 33) {
\r
2486 /* A & C are available in most sizes now */
\r
2487 if(squareSize != 49 && squareSize != 72 && squareSize != 33) { // Vortex-like
\r
2488 pieceBitmap[0][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "s");
\r
2489 pieceBitmap[1][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "o");
\r
2490 pieceBitmap[2][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "w");
\r
2491 pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "s");
\r
2492 pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "o");
\r
2493 pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "w");
\r
2494 pieceBitmap[0][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "s");
\r
2495 pieceBitmap[1][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "o");
\r
2496 pieceBitmap[2][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "w");
\r
2497 pieceBitmap[0][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "s");
\r
2498 pieceBitmap[1][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "o");
\r
2499 pieceBitmap[2][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "w");
\r
2500 } else { // Smirf-like
\r
2501 if(gameInfo.variant == VariantSChess) {
\r
2502 pieceBitmap[0][WhiteAngel] = DoLoadBitmap(hInst, "v", squareSize, "s");
\r
2503 pieceBitmap[1][WhiteAngel] = DoLoadBitmap(hInst, "v", squareSize, "o");
\r
2504 pieceBitmap[2][WhiteAngel] = DoLoadBitmap(hInst, "v", squareSize, "w");
\r
2506 pieceBitmap[0][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "s");
\r
2507 pieceBitmap[1][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "o");
\r
2508 pieceBitmap[2][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "w");
\r
2511 if(gameInfo.variant == VariantGothic) { // Vortex-like
\r
2512 pieceBitmap[0][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "s");
\r
2513 pieceBitmap[1][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "o");
\r
2514 pieceBitmap[2][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "w");
\r
2515 } else if(gameInfo.variant == VariantSChess && (squareSize == 49 || squareSize == 72)) {
\r
2516 pieceBitmap[0][WhiteMarshall] = DoLoadBitmap(hInst, "e", squareSize, "s");
\r
2517 pieceBitmap[1][WhiteMarshall] = DoLoadBitmap(hInst, "e", squareSize, "o");
\r
2518 pieceBitmap[2][WhiteMarshall] = DoLoadBitmap(hInst, "e", squareSize, "w");
\r
2519 } else { // WinBoard standard
\r
2520 pieceBitmap[0][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "s");
\r
2521 pieceBitmap[1][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "o");
\r
2522 pieceBitmap[2][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "w");
\r
2527 if(squareSize==72 || squareSize==49 || squareSize==33) { /* experiment with some home-made bitmaps */
\r
2528 pieceBitmap[0][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "s");
\r
2529 pieceBitmap[1][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "o");
\r
2530 pieceBitmap[2][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "w");
\r
2531 pieceBitmap[0][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "s");
\r
2532 pieceBitmap[1][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "o");
\r
2533 pieceBitmap[2][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "w");
\r
2534 pieceBitmap[0][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "s");
\r
2535 pieceBitmap[1][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "o");
\r
2536 pieceBitmap[2][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "w");
\r
2537 pieceBitmap[0][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "s");
\r
2538 pieceBitmap[1][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "o");
\r
2539 pieceBitmap[2][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "w");
\r
2540 pieceBitmap[0][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "s");
\r
2541 pieceBitmap[1][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "o");
\r
2542 pieceBitmap[2][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "w");
\r
2543 pieceBitmap[0][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "s");
\r
2544 pieceBitmap[1][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "o");
\r
2545 pieceBitmap[2][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "w");
\r
2546 pieceBitmap[0][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "s");
\r
2547 pieceBitmap[1][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "o");
\r
2548 pieceBitmap[2][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "w");
\r
2549 pieceBitmap[0][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "s");
\r
2550 pieceBitmap[1][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "o");
\r
2551 pieceBitmap[2][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "w");
\r
2552 pieceBitmap[0][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "s");
\r
2553 pieceBitmap[1][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "o");
\r
2554 pieceBitmap[2][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "w");
\r
2555 pieceBitmap[0][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "s");
\r
2556 pieceBitmap[1][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "o");
\r
2557 pieceBitmap[2][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "w");
\r
2559 if(gameInfo.variant == VariantShogi) { /* promoted Gold represemtations */
\r
2560 pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "wp", squareSize, "s");
\r
2561 pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "wp", squareSize, "o");
\r
2562 pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "w", squareSize, "w");
\r
2563 pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "wn", squareSize, "s");
\r
2564 pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "wn", squareSize, "o");
\r
2565 pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "w", squareSize, "w");
\r
2566 pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "ws", squareSize, "s");
\r
2567 pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "ws", squareSize, "o");
\r
2568 pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "w", squareSize, "w");
\r
2569 pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "wl", squareSize, "s");
\r
2570 pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "wl", squareSize, "o");
\r
2571 pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "w", squareSize, "w");
\r
2573 pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "s");
\r
2574 pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "o");
\r
2575 pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "w");
\r
2576 pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "s");
\r
2577 pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "o");
\r
2578 pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "w");
\r
2579 pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "s");
\r
2580 pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "o");
\r
2581 pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "w");
\r
2582 pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "s");
\r
2583 pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "o");
\r
2584 pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "w");
\r
2587 } else { /* other size, no special bitmaps available. Use smaller symbols */
\r
2588 if((int)boardSize < 2) minorSize = sizeInfo[0].squareSize;
\r
2589 else minorSize = sizeInfo[(int)boardSize - 2].squareSize;
\r
2590 pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "s");
\r
2591 pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "o");
\r
2592 pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "w");
\r
2593 pieceBitmap[0][WhiteCardinal] = DoLoadBitmap(hInst, "b", minorSize, "s");
\r
2594 pieceBitmap[1][WhiteCardinal] = DoLoadBitmap(hInst, "b", minorSize, "o");
\r
2595 pieceBitmap[2][WhiteCardinal] = DoLoadBitmap(hInst, "b", minorSize, "w");
\r
2596 pieceBitmap[0][WhiteDragon] = DoLoadBitmap(hInst, "r", minorSize, "s");
\r
2597 pieceBitmap[1][WhiteDragon] = DoLoadBitmap(hInst, "r", minorSize, "o");
\r
2598 pieceBitmap[2][WhiteDragon] = DoLoadBitmap(hInst, "r", minorSize, "w");
\r
2599 pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "s");
\r
2600 pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "o");
\r
2601 pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "w");
\r
2605 if(gameInfo.variant == VariantShogi && squareSize == 58)
\r
2606 /* special Shogi support in this size */
\r
2607 { for (i=0; i<=2; i++) { /* replace all bitmaps */
\r
2608 for (piece = WhitePawn;
\r
2609 (int) piece < (int) BlackPawn;
\r
2610 piece = (ChessSquare) ((int) piece + 1)) {
\r
2611 if (pieceBitmap[i][piece] != NULL)
\r
2612 DeleteObject(pieceBitmap[i][piece]);
\r
2615 pieceBitmap[0][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "o");
\r
2616 pieceBitmap[0][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "o");
\r
2617 pieceBitmap[0][WhiteBishop] = DoLoadBitmap(hInst, "sb", squareSize, "o");
\r
2618 pieceBitmap[0][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "o");
\r
2619 pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "o");
\r
2620 pieceBitmap[0][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "o");
\r
2621 pieceBitmap[0][WhiteFerz] = DoLoadBitmap(hInst, "sf", squareSize, "o");
\r
2622 pieceBitmap[0][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "o");
\r
2623 pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "su", squareSize, "o");
\r
2624 pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "sh", squareSize, "o");
\r
2625 pieceBitmap[0][WhiteCardinal] = DoLoadBitmap(hInst, "sa", squareSize, "o");
\r
2626 pieceBitmap[0][WhiteDragon] = DoLoadBitmap(hInst, "sc", squareSize, "o");
\r
2627 pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "sg", squareSize, "o");
\r
2628 pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "ss", squareSize, "o");
\r
2629 pieceBitmap[1][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "o");
\r
2630 pieceBitmap[1][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "o");
\r
2631 pieceBitmap[1][WhiteBishop] = DoLoadBitmap(hInst, "sb", squareSize, "o");
\r
2632 pieceBitmap[1][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "o");
\r
2633 pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "o");
\r
2634 pieceBitmap[1][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "o");
\r
2635 pieceBitmap[1][WhiteFerz] = DoLoadBitmap(hInst, "sf", squareSize, "o");
\r
2636 pieceBitmap[1][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "o");
\r
2637 pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "su", squareSize, "o");
\r
2638 pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "sh", squareSize, "o");
\r
2639 pieceBitmap[1][WhiteCardinal] = DoLoadBitmap(hInst, "sa", squareSize, "o");
\r
2640 pieceBitmap[1][WhiteDragon] = DoLoadBitmap(hInst, "sc", squareSize, "o");
\r
2641 pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "sg", squareSize, "o");
\r
2642 pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "ss", squareSize, "o");
\r
2643 pieceBitmap[2][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "w");
\r
2644 pieceBitmap[2][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "w");
\r
2645 pieceBitmap[2][WhiteBishop] = DoLoadBitmap(hInst, "sr", squareSize, "w");
\r
2646 pieceBitmap[2][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "w");
\r
2647 pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "w");
\r
2648 pieceBitmap[2][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "w");
\r
2649 pieceBitmap[2][WhiteFerz] = DoLoadBitmap(hInst, "sw", squareSize, "w");
\r
2650 pieceBitmap[2][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "w");
\r
2651 pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "sp", squareSize, "w");
\r
2652 pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "sn", squareSize, "w");
\r
2653 pieceBitmap[2][WhiteCardinal] = DoLoadBitmap(hInst, "sr", squareSize, "w");
\r
2654 pieceBitmap[2][WhiteDragon] = DoLoadBitmap(hInst, "sr", squareSize, "w");
\r
2655 pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "sl", squareSize, "w");
\r
2656 pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "sw", squareSize, "w");
\r
2662 PieceBitmap(ChessSquare p, int kind)
\r
2664 if ((int) p >= (int) BlackPawn)
\r
2665 p = (ChessSquare) ((int) p - (int) BlackPawn + (int) WhitePawn);
\r
2667 return pieceBitmap[kind][(int) p];
\r
2670 /***************************************************************/
\r
2672 #define MIN(a,b) (((a) < (b)) ? (a) : (b))
\r
2673 #define MAX(a,b) (((a) > (b)) ? (a) : (b))
\r
2675 #define MIN3(a,b,c) (((a) < (b) && (a) < (c)) ? (a) : (((b) < (a) && (b) < (c)) ? (b) : (c)))
\r
2676 #define MAX3(a,b,c) (((a) > (b) && (a) > (c)) ? (a) : (((b) > (a) && (b) > (c)) ? (b) : (c)))
\r
2680 SquareToPos(int row, int column, int * x, int * y)
\r
2683 *x = boardRect.left + lineGap + ((BOARD_WIDTH-1)-column) * (squareSize + lineGap);
\r
2684 *y = boardRect.top + lineGap + row * (squareSize + lineGap);
\r
2686 *x = boardRect.left + lineGap + column * (squareSize + lineGap);
\r
2687 *y = boardRect.top + lineGap + ((BOARD_HEIGHT-1)-row) * (squareSize + lineGap);
\r
2692 DrawCoordsOnDC(HDC hdc)
\r
2694 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
2695 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
2696 char str[2] = { NULLCHAR, NULLCHAR };
\r
2697 int oldMode, oldAlign, x, y, start, i;
\r
2701 if (!appData.showCoords)
\r
2704 start = flipView ? 1-(ONE!='1') : 23+(ONE!='1')-BOARD_HEIGHT;
\r
2706 oldBrush = SelectObject(hdc, GetStockObject(BLACK_BRUSH));
\r
2707 oldMode = SetBkMode(hdc, (appData.monoMode ? OPAQUE : TRANSPARENT));
\r
2708 oldAlign = GetTextAlign(hdc);
\r
2709 oldFont = SelectObject(hdc, font[boardSize][COORD_FONT]->hf);
\r
2711 y = boardRect.top + lineGap;
\r
2712 x = boardRect.left + lineGap + gameInfo.holdingsWidth*(squareSize + lineGap);
\r
2714 SetTextAlign(hdc, TA_LEFT|TA_TOP);
\r
2715 for (i = 0; i < BOARD_HEIGHT; i++) {
\r
2716 str[0] = files[start + i];
\r
2717 ExtTextOut(hdc, x + 2, y + 1, 0, NULL, str, 1, NULL);
\r
2718 y += squareSize + lineGap;
\r
2721 start = flipView ? 12-(BOARD_RGHT-BOARD_LEFT) : 12;
\r
2723 SetTextAlign(hdc, TA_RIGHT|TA_BOTTOM);
\r
2724 for (i = 0; i < BOARD_RGHT - BOARD_LEFT; i++) {
\r
2725 str[0] = ranks[start + i];
\r
2726 ExtTextOut(hdc, x + squareSize - 2, y - 1, 0, NULL, str, 1, NULL);
\r
2727 x += squareSize + lineGap;
\r
2730 SelectObject(hdc, oldBrush);
\r
2731 SetBkMode(hdc, oldMode);
\r
2732 SetTextAlign(hdc, oldAlign);
\r
2733 SelectObject(hdc, oldFont);
\r
2737 DrawGridOnDC(HDC hdc)
\r
2741 if (lineGap != 0) {
\r
2742 oldPen = SelectObject(hdc, gridPen);
\r
2743 PolyPolyline(hdc, gridEndpoints, gridVertexCounts, BOARD_WIDTH+BOARD_HEIGHT + 2);
\r
2744 SelectObject(hdc, oldPen);
\r
2748 #define HIGHLIGHT_PEN 0
\r
2749 #define PREMOVE_PEN 1
\r
2752 DrawHighlightOnDC(HDC hdc, BOOLEAN on, int x, int y, int pen)
\r
2755 HPEN oldPen, hPen;
\r
2756 if (lineGap == 0) return;
\r
2758 x1 = boardRect.left +
\r
2759 lineGap/2 + ((BOARD_WIDTH-1)-x) * (squareSize + lineGap);
\r
2760 y1 = boardRect.top +
\r
2761 lineGap/2 + y * (squareSize + lineGap);
\r
2763 x1 = boardRect.left +
\r
2764 lineGap/2 + x * (squareSize + lineGap);
\r
2765 y1 = boardRect.top +
\r
2766 lineGap/2 + ((BOARD_HEIGHT-1)-y) * (squareSize + lineGap);
\r
2768 hPen = pen ? premovePen : highlightPen;
\r
2769 oldPen = SelectObject(hdc, on ? hPen : gridPen);
\r
2770 MoveToEx(hdc, x1, y1, NULL);
\r
2771 LineTo(hdc, x1 + squareSize + lineGap, y1);
\r
2772 LineTo(hdc, x1 + squareSize + lineGap, y1 + squareSize + lineGap);
\r
2773 LineTo(hdc, x1, y1 + squareSize + lineGap);
\r
2774 LineTo(hdc, x1, y1);
\r
2775 SelectObject(hdc, oldPen);
\r
2779 DrawHighlightsOnDC(HDC hdc, HighlightInfo *h, int pen)
\r
2782 for (i=0; i<2; i++) {
\r
2783 if (h->sq[i].x >= 0 && h->sq[i].y >= 0)
\r
2784 DrawHighlightOnDC(hdc, TRUE,
\r
2785 h->sq[i].x, h->sq[i].y,
\r
2790 /* Note: sqcolor is used only in monoMode */
\r
2791 /* Note that this code is largely duplicated in woptions.c,
\r
2792 function DrawSampleSquare, so that needs to be updated too */
\r
2794 DrawPieceOnDC(HDC hdc, ChessSquare piece, int color, int sqcolor, int x, int y, HDC tmphdc)
\r
2796 HBITMAP oldBitmap;
\r
2800 if (appData.blindfold) return;
\r
2802 /* [AS] Use font-based pieces if needed */
\r
2803 if( fontBitmapSquareSize >= 0 && (squareSize > 32 || gameInfo.variant >= VariantShogi)) {
\r
2804 /* Create piece bitmaps, or do nothing if piece set is up to date */
\r
2805 CreatePiecesFromFont();
\r
2807 if( fontBitmapSquareSize == squareSize ) {
\r
2808 int index = TranslatePieceToFontPiece(piece);
\r
2810 SelectObject( tmphdc, hPieceMask[ index ] );
\r
2812 if(appData.upsideDown ? color==flipView : (flipView && gameInfo.variant == VariantShogi))
\r
2813 StretchBlt(hdc, x+squareSize, y+squareSize, -squareSize, -squareSize, tmphdc, 0, 0, squareSize, squareSize, SRCAND);
\r
2817 squareSize, squareSize,
\r
2822 SelectObject( tmphdc, hPieceFace[ index ] );
\r
2824 if(appData.upsideDown ? color==flipView : (flipView && gameInfo.variant == VariantShogi))
\r
2825 StretchBlt(hdc, x+squareSize, y+squareSize, -squareSize, -squareSize, tmphdc, 0, 0, squareSize, squareSize, SRCPAINT);
\r
2829 squareSize, squareSize,
\r
2838 if (appData.monoMode) {
\r
2839 SelectObject(tmphdc, PieceBitmap(piece,
\r
2840 color == sqcolor ? OUTLINE_PIECE : SOLID_PIECE));
\r
2841 BitBlt(hdc, x, y, squareSize, squareSize, tmphdc, 0, 0,
\r
2842 sqcolor ? SRCCOPY : NOTSRCCOPY);
\r
2844 tmpSize = squareSize;
\r
2846 ((piece >= (int)WhiteNightrider && piece <= WhiteGrasshopper) ||
\r
2847 (piece >= (int)BlackNightrider && piece <= BlackGrasshopper)) ) {
\r
2848 /* [HGM] no bitmap available for promoted pieces in Crazyhouse */
\r
2849 /* Bitmaps of smaller size are substituted, but we have to align them */
\r
2850 x += (squareSize - minorSize)>>1;
\r
2851 y += squareSize - minorSize - 2;
\r
2852 tmpSize = minorSize;
\r
2854 if (color || appData.allWhite ) {
\r
2855 oldBitmap = SelectObject(tmphdc, PieceBitmap(piece, WHITE_PIECE));
\r
2857 oldBrush = SelectObject(hdc, whitePieceBrush);
\r
2858 else oldBrush = SelectObject(hdc, blackPieceBrush);
\r
2859 if(appData.upsideDown && color==flipView)
\r
2860 StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, 0x00B8074A);
\r
2862 BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, 0x00B8074A);
\r
2863 /* Use black for outline of white pieces */
\r
2864 SelectObject(tmphdc, PieceBitmap(piece, OUTLINE_PIECE));
\r
2865 if(appData.upsideDown && color==flipView)
\r
2866 StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, SRCAND);
\r
2868 BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, SRCAND);
\r
2870 /* Use square color for details of black pieces */
\r
2871 oldBitmap = SelectObject(tmphdc, PieceBitmap(piece, SOLID_PIECE));
\r
2872 oldBrush = SelectObject(hdc, blackPieceBrush);
\r
2873 if(appData.upsideDown && !flipView)
\r
2874 StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, 0x00B8074A);
\r
2876 BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, 0x00B8074A);
\r
2878 SelectObject(hdc, oldBrush);
\r
2879 SelectObject(tmphdc, oldBitmap);
\r
2883 /* [AS] Compute a drawing mode for a square, based on specified settings (see DrawTile) */
\r
2884 int GetBackTextureMode( int algo )
\r
2886 int result = BACK_TEXTURE_MODE_DISABLED;
\r
2890 case BACK_TEXTURE_MODE_PLAIN:
\r
2891 result = 1; /* Always use identity map */
\r
2893 case BACK_TEXTURE_MODE_FULL_RANDOM:
\r
2894 result = 1 + (myrandom() % 3); /* Pick a transformation at random */
\r
2902 [AS] Compute and save texture drawing info, otherwise we may not be able
\r
2903 to handle redraws cleanly (as random numbers would always be different).
\r
2905 VOID RebuildTextureSquareInfo()
\r
2915 ZeroMemory( &backTextureSquareInfo, sizeof(backTextureSquareInfo) );
\r
2917 if( liteBackTexture != NULL ) {
\r
2918 if( GetObject( liteBackTexture, sizeof(bi), &bi ) > 0 ) {
\r
2919 lite_w = bi.bmWidth;
\r
2920 lite_h = bi.bmHeight;
\r
2924 if( darkBackTexture != NULL ) {
\r
2925 if( GetObject( darkBackTexture, sizeof(bi), &bi ) > 0 ) {
\r
2926 dark_w = bi.bmWidth;
\r
2927 dark_h = bi.bmHeight;
\r
2931 for( row=0; row<BOARD_HEIGHT; row++ ) {
\r
2932 for( col=0; col<BOARD_WIDTH; col++ ) {
\r
2933 if( (col + row) & 1 ) {
\r
2935 if( lite_w >= squareSize && lite_h >= squareSize ) {
\r
2936 if( lite_w >= squareSize*BOARD_WIDTH )
\r
2937 backTextureSquareInfo[row][col].x = (2*col+1)*lite_w/(2*BOARD_WIDTH) - squareSize/2; /* [HGM] cut out of center of virtual square */
\r
2939 backTextureSquareInfo[row][col].x = col * (lite_w - squareSize) / (BOARD_WIDTH-1); /* [HGM] divide by size-1 in stead of size! */
\r
2940 if( lite_h >= squareSize*BOARD_HEIGHT )
\r
2941 backTextureSquareInfo[row][col].y = (2*(BOARD_HEIGHT-row)-1)*lite_h/(2*BOARD_HEIGHT) - squareSize/2;
\r
2943 backTextureSquareInfo[row][col].y = (BOARD_HEIGHT-1-row) * (lite_h - squareSize) / (BOARD_HEIGHT-1);
\r
2944 backTextureSquareInfo[row][col].mode = GetBackTextureMode(liteBackTextureMode);
\r
2949 if( dark_w >= squareSize && dark_h >= squareSize ) {
\r
2950 if( dark_w >= squareSize*BOARD_WIDTH )
\r
2951 backTextureSquareInfo[row][col].x = (2*col+1) * dark_w / (2*BOARD_WIDTH) - squareSize/2;
\r
2953 backTextureSquareInfo[row][col].x = col * (dark_w - squareSize) / (BOARD_WIDTH-1);
\r
2954 if( dark_h >= squareSize*BOARD_HEIGHT )
\r
2955 backTextureSquareInfo[row][col].y = (2*(BOARD_HEIGHT-row)-1) * dark_h / (2*BOARD_HEIGHT) - squareSize/2;
\r
2957 backTextureSquareInfo[row][col].y = (BOARD_HEIGHT-1-row) * (dark_h - squareSize) / (BOARD_HEIGHT-1);
\r
2958 backTextureSquareInfo[row][col].mode = GetBackTextureMode(darkBackTextureMode);
\r
2965 /* [AS] Arrow highlighting support */
\r
2967 static double A_WIDTH = 5; /* Width of arrow body */
\r
2969 #define A_HEIGHT_FACTOR 6 /* Length of arrow "point", relative to body width */
\r
2970 #define A_WIDTH_FACTOR 3 /* Width of arrow "point", relative to body width */
\r
2972 static double Sqr( double x )
\r
2977 static int Round( double x )
\r
2979 return (int) (x + 0.5);
\r
2982 /* Draw an arrow between two points using current settings */
\r
2983 VOID DrawArrowBetweenPoints( HDC hdc, int s_x, int s_y, int d_x, int d_y )
\r
2986 double dx, dy, j, k, x, y;
\r
2988 if( d_x == s_x ) {
\r
2989 int h = (d_y > s_y) ? +A_WIDTH*A_HEIGHT_FACTOR : -A_WIDTH*A_HEIGHT_FACTOR;
\r
2991 arrow[0].x = s_x + A_WIDTH + 0.5;
\r
2994 arrow[1].x = s_x + A_WIDTH + 0.5;
\r
2995 arrow[1].y = d_y - h;
\r
2997 arrow[2].x = arrow[1].x + A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;
\r
2998 arrow[2].y = d_y - h;
\r
3003 arrow[5].x = arrow[1].x - 2*A_WIDTH + 0.5;
\r
3004 arrow[5].y = d_y - h;
\r
3006 arrow[4].x = arrow[5].x - A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;
\r
3007 arrow[4].y = d_y - h;
\r
3009 arrow[6].x = arrow[1].x - 2*A_WIDTH + 0.5;
\r
3012 else if( d_y == s_y ) {
\r
3013 int w = (d_x > s_x) ? +A_WIDTH*A_HEIGHT_FACTOR : -A_WIDTH*A_HEIGHT_FACTOR;
\r
3016 arrow[0].y = s_y + A_WIDTH + 0.5;
\r
3018 arrow[1].x = d_x - w;
\r
3019 arrow[1].y = s_y + A_WIDTH + 0.5;
\r
3021 arrow[2].x = d_x - w;
\r
3022 arrow[2].y = arrow[1].y + A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;
\r
3027 arrow[5].x = d_x - w;
\r
3028 arrow[5].y = arrow[1].y - 2*A_WIDTH + 0.5;
\r
3030 arrow[4].x = d_x - w;
\r
3031 arrow[4].y = arrow[5].y - A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;
\r
3034 arrow[6].y = arrow[1].y - 2*A_WIDTH + 0.5;
\r
3037 /* [AS] Needed a lot of paper for this! :-) */
\r
3038 dy = (double) (d_y - s_y) / (double) (d_x - s_x);
\r
3039 dx = (double) (s_x - d_x) / (double) (s_y - d_y);
\r
3041 j = sqrt( Sqr(A_WIDTH) / (1.0 + Sqr(dx)) );
\r
3043 k = sqrt( Sqr(A_WIDTH*A_HEIGHT_FACTOR) / (1.0 + Sqr(dy)) );
\r
3048 arrow[0].x = Round(x - j);
\r
3049 arrow[0].y = Round(y + j*dx);
\r
3051 arrow[1].x = Round(arrow[0].x + 2*j); // [HGM] prevent width to be affected by rounding twice
\r
3052 arrow[1].y = Round(arrow[0].y - 2*j*dx);
\r
3055 x = (double) d_x - k;
\r
3056 y = (double) d_y - k*dy;
\r
3059 x = (double) d_x + k;
\r
3060 y = (double) d_y + k*dy;
\r
3063 x = Round(x); y = Round(y); // [HGM] make sure width of shaft is rounded the same way on both ends
\r
3065 arrow[6].x = Round(x - j);
\r
3066 arrow[6].y = Round(y + j*dx);
\r
3068 arrow[2].x = Round(arrow[6].x + 2*j);
\r
3069 arrow[2].y = Round(arrow[6].y - 2*j*dx);
\r
3071 arrow[3].x = Round(arrow[2].x + j*(A_WIDTH_FACTOR-1));
\r
3072 arrow[3].y = Round(arrow[2].y - j*(A_WIDTH_FACTOR-1)*dx);
\r
3077 arrow[5].x = Round(arrow[6].x - j*(A_WIDTH_FACTOR-1));
\r
3078 arrow[5].y = Round(arrow[6].y + j*(A_WIDTH_FACTOR-1)*dx);
\r
3081 Polygon( hdc, arrow, 7 );
\r
3084 /* [AS] Draw an arrow between two squares */
\r
3085 VOID DrawArrowBetweenSquares( HDC hdc, int s_col, int s_row, int d_col, int d_row )
\r
3087 int s_x, s_y, d_x, d_y;
\r
3094 if( s_col == d_col && s_row == d_row ) {
\r
3098 /* Get source and destination points */
\r
3099 SquareToPos( s_row, s_col, &s_x, &s_y);
\r
3100 SquareToPos( d_row, d_col, &d_x, &d_y);
\r
3103 d_y += squareSize / 2 - squareSize / 4; // [HGM] round towards same centers on all sides!
\r
3105 else if( d_y < s_y ) {
\r
3106 d_y += squareSize / 2 + squareSize / 4;
\r
3109 d_y += squareSize / 2;
\r
3113 d_x += squareSize / 2 - squareSize / 4;
\r
3115 else if( d_x < s_x ) {
\r
3116 d_x += squareSize / 2 + squareSize / 4;
\r
3119 d_x += squareSize / 2;
\r
3122 s_x += squareSize / 2;
\r
3123 s_y += squareSize / 2;
\r
3125 /* Adjust width */
\r
3126 A_WIDTH = squareSize / 14.; //[HGM] make float
\r
3129 stLB.lbStyle = BS_SOLID;
\r
3130 stLB.lbColor = appData.highlightArrowColor;
\r
3133 hpen = CreatePen( PS_SOLID, 2, RGB(0x00,0x00,0x00) );
\r
3134 holdpen = SelectObject( hdc, hpen );
\r
3135 hbrush = CreateBrushIndirect( &stLB );
\r
3136 holdbrush = SelectObject( hdc, hbrush );
\r
3138 DrawArrowBetweenPoints( hdc, s_x, s_y, d_x, d_y );
\r
3140 SelectObject( hdc, holdpen );
\r
3141 SelectObject( hdc, holdbrush );
\r
3142 DeleteObject( hpen );
\r
3143 DeleteObject( hbrush );
\r
3146 BOOL HasHighlightInfo()
\r
3148 BOOL result = FALSE;
\r
3150 if( highlightInfo.sq[0].x >= 0 && highlightInfo.sq[0].y >= 0 &&
\r
3151 highlightInfo.sq[1].x >= 0 && highlightInfo.sq[1].y >= 0 )
\r
3159 BOOL IsDrawArrowEnabled()
\r
3161 BOOL result = FALSE;
\r
3163 if( appData.highlightMoveWithArrow && squareSize >= 32 ) {
\r
3170 VOID DrawArrowHighlight( HDC hdc )
\r
3172 if( IsDrawArrowEnabled() && HasHighlightInfo() ) {
\r
3173 DrawArrowBetweenSquares( hdc,
\r
3174 highlightInfo.sq[0].x, highlightInfo.sq[0].y,
\r
3175 highlightInfo.sq[1].x, highlightInfo.sq[1].y );
\r
3179 HRGN GetArrowHighlightClipRegion( HDC hdc )
\r
3181 HRGN result = NULL;
\r
3183 if( HasHighlightInfo() ) {
\r
3184 int x1, y1, x2, y2;
\r
3185 int sx, sy, dx, dy;
\r
3187 SquareToPos(highlightInfo.sq[0].y, highlightInfo.sq[0].x, &x1, &y1 );
\r
3188 SquareToPos(highlightInfo.sq[1].y, highlightInfo.sq[1].x, &x2, &y2 );
\r
3190 sx = MIN( x1, x2 );
\r
3191 sy = MIN( y1, y2 );
\r
3192 dx = MAX( x1, x2 ) + squareSize;
\r
3193 dy = MAX( y1, y2 ) + squareSize;
\r
3195 result = CreateRectRgn( sx, sy, dx, dy );
\r
3202 Warning: this function modifies the behavior of several other functions.
\r
3204 Basically, Winboard is optimized to avoid drawing the whole board if not strictly
\r
3205 needed. Unfortunately, the decision whether or not to perform a full or partial
\r
3206 repaint is scattered all over the place, which is not good for features such as
\r
3207 "arrow highlighting" that require a full repaint of the board.
\r
3209 So, I've tried to patch the code where I thought it made sense (e.g. after or during
\r
3210 user interaction, when speed is not so important) but especially to avoid errors
\r
3211 in the displayed graphics.
\r
3213 In such patched places, I always try refer to this function so there is a single
\r
3214 place to maintain knowledge.
\r
3216 To restore the original behavior, just return FALSE unconditionally.
\r
3218 BOOL IsFullRepaintPreferrable()
\r
3220 BOOL result = FALSE;
\r
3222 if( (appData.highlightLastMove || appData.highlightDragging) && IsDrawArrowEnabled() ) {
\r
3223 /* Arrow may appear on the board */
\r
3231 This function is called by DrawPosition to know whether a full repaint must
\r
3234 Only DrawPosition may directly call this function, which makes use of
\r
3235 some state information. Other function should call DrawPosition specifying
\r
3236 the repaint flag, and can use IsFullRepaintPreferrable if needed.
\r
3238 BOOL DrawPositionNeedsFullRepaint()
\r
3240 BOOL result = FALSE;
\r
3243 Probably a slightly better policy would be to trigger a full repaint
\r
3244 when animInfo.piece changes state (i.e. empty -> non-empty and viceversa),
\r
3245 but animation is fast enough that it's difficult to notice.
\r
3247 if( animInfo.piece == EmptySquare ) {
\r
3248 if( (appData.highlightLastMove || appData.highlightDragging) && IsDrawArrowEnabled() /*&& HasHighlightInfo()*/ ) {
\r
3257 DrawBoardOnDC(HDC hdc, Board board, HDC tmphdc)
\r
3259 int row, column, x, y, square_color, piece_color;
\r
3260 ChessSquare piece;
\r
3262 HDC texture_hdc = NULL;
\r
3264 /* [AS] Initialize background textures if needed */
\r
3265 if( liteBackTexture != NULL || darkBackTexture != NULL ) {
\r
3266 static int backTextureBoardSize; /* [HGM] boardsize: also new texture if board format changed */
\r
3267 if( backTextureSquareSize != squareSize
\r
3268 || backTextureBoardSize != BOARD_WIDTH+BOARD_FILES*BOARD_HEIGHT) {
\r
3269 backTextureBoardSize = BOARD_WIDTH+BOARD_FILES*BOARD_HEIGHT;
\r
3270 backTextureSquareSize = squareSize;
\r
3271 RebuildTextureSquareInfo();
\r
3274 texture_hdc = CreateCompatibleDC( hdc );
\r
3277 for (row = 0; row < BOARD_HEIGHT; row++) {
\r
3278 for (column = 0; column < BOARD_WIDTH; column++) {
\r
3280 SquareToPos(row, column, &x, &y);
\r
3282 piece = board[row][column];
\r
3284 square_color = ((column + row) % 2) == 1;
\r
3285 if( gameInfo.variant == VariantXiangqi ) {
\r
3286 square_color = !InPalace(row, column);
\r
3287 if(BOARD_HEIGHT&1) { if(row==BOARD_HEIGHT/2) square_color ^= 1; }
\r
3288 else if(row < BOARD_HEIGHT/2) square_color ^= 1;
\r
3290 piece_color = (int) piece < (int) BlackPawn;
\r
3293 /* [HGM] holdings file: light square or black */
\r
3294 if(column == BOARD_LEFT-2) {
\r
3295 if( row > BOARD_HEIGHT - gameInfo.holdingsSize - 1 )
\r
3298 DisplayHoldingsCount(hdc, x, y, 0, 0); /* black out */
\r
3302 if(column == BOARD_RGHT + 1 ) {
\r
3303 if( row < gameInfo.holdingsSize )
\r
3306 DisplayHoldingsCount(hdc, x, y, 0, 0);
\r
3310 if(column == BOARD_LEFT-1 ) /* left align */
\r
3311 DisplayHoldingsCount(hdc, x, y, flipView, (int) board[row][column]);
\r
3312 else if( column == BOARD_RGHT) /* right align */
\r
3313 DisplayHoldingsCount(hdc, x, y, !flipView, (int) board[row][column]);
\r
3315 if (appData.monoMode) {
\r
3316 if (piece == EmptySquare) {
\r
3317 BitBlt(hdc, x, y, squareSize, squareSize, 0, 0, 0,
\r
3318 square_color ? WHITENESS : BLACKNESS);
\r
3320 DrawPieceOnDC(hdc, piece, piece_color, square_color, x, y, tmphdc);
\r
3323 else if( backTextureSquareInfo[row][column].mode > 0 ) {
\r
3324 /* [AS] Draw the square using a texture bitmap */
\r
3325 HBITMAP hbm = SelectObject( texture_hdc, square_color ? liteBackTexture : darkBackTexture );
\r
3326 int r = row, c = column; // [HGM] do not flip board in flipView
\r
3327 if(flipView) { r = BOARD_HEIGHT-1 - r; c = BOARD_WIDTH-1 - c; }
\r
3330 squareSize, squareSize,
\r
3333 backTextureSquareInfo[r][c].mode,
\r
3334 backTextureSquareInfo[r][c].x,
\r
3335 backTextureSquareInfo[r][c].y );
\r
3337 SelectObject( texture_hdc, hbm );
\r
3339 if (piece != EmptySquare) {
\r
3340 DrawPieceOnDC(hdc, piece, piece_color, -1, x, y, tmphdc);
\r
3344 HBRUSH brush = square_color ? lightSquareBrush : darkSquareBrush;
\r
3346 oldBrush = SelectObject(hdc, brush );
\r
3347 BitBlt(hdc, x, y, squareSize, squareSize, 0, 0, 0, PATCOPY);
\r
3348 SelectObject(hdc, oldBrush);
\r
3349 if (piece != EmptySquare)
\r
3350 DrawPieceOnDC(hdc, piece, piece_color, -1, x, y, tmphdc);
\r
3355 if( texture_hdc != NULL ) {
\r
3356 DeleteDC( texture_hdc );
\r
3360 int saveDiagFlag = 0; FILE *diagFile; // [HGM] diag
\r
3361 void fputDW(FILE *f, int x)
\r
3363 fputc(x & 255, f);
\r
3364 fputc(x>>8 & 255, f);
\r
3365 fputc(x>>16 & 255, f);
\r
3366 fputc(x>>24 & 255, f);
\r
3369 #define MAX_CLIPS 200 /* more than enough */
\r
3372 DrawLogoOnDC(HDC hdc, RECT logoRect, HBITMAP logo)
\r
3374 // HBITMAP bufferBitmap;
\r
3379 int w = 100, h = 50;
\r
3381 if(logo == NULL) return;
\r
3382 // GetClientRect(hwndMain, &Rect);
\r
3383 // bufferBitmap = CreateCompatibleBitmap(hdc, Rect.right-Rect.left+1,
\r
3384 // Rect.bottom-Rect.top+1);
\r
3385 tmphdc = CreateCompatibleDC(hdc);
\r
3386 hbm = SelectObject(tmphdc, logo);
\r
3387 if( GetObject( logo, sizeof(bi), &bi ) > 0 ) {
\r
3391 StretchBlt(hdc, logoRect.left, logoRect.top, logoRect.right - logoRect.left,
\r
3392 logoRect.bottom - logoRect.top, tmphdc, 0, 0, w, h, SRCCOPY);
\r
3393 SelectObject(tmphdc, hbm);
\r
3401 HDC hdc = GetDC(hwndMain);
\r
3402 HBITMAP whiteLogo = (HBITMAP) first.programLogo, blackLogo = (HBITMAP) second.programLogo;
\r
3403 if(appData.autoLogo) {
\r
3405 switch(gameMode) { // pick logos based on game mode
\r
3406 case IcsObserving:
\r
3407 whiteLogo = second.programLogo; // ICS logo
\r
3408 blackLogo = second.programLogo;
\r
3411 case IcsPlayingWhite:
\r
3412 if(!appData.zippyPlay) whiteLogo = userLogo;
\r
3413 blackLogo = second.programLogo; // ICS logo
\r
3415 case IcsPlayingBlack:
\r
3416 whiteLogo = second.programLogo; // ICS logo
\r
3417 blackLogo = appData.zippyPlay ? first.programLogo : userLogo;
\r
3419 case TwoMachinesPlay:
\r
3420 if(first.twoMachinesColor[0] == 'b') {
\r
3421 whiteLogo = second.programLogo;
\r
3422 blackLogo = first.programLogo;
\r
3425 case MachinePlaysWhite:
\r
3426 blackLogo = userLogo;
\r
3428 case MachinePlaysBlack:
\r
3429 whiteLogo = userLogo;
\r
3430 blackLogo = first.programLogo;
\r
3433 DrawLogoOnDC(hdc, leftLogoRect, flipClock ? blackLogo : whiteLogo);
\r
3434 DrawLogoOnDC(hdc, rightLogoRect, flipClock ? whiteLogo : blackLogo);
\r
3435 ReleaseDC(hwndMain, hdc);
\r
3439 static HDC hdcSeek;
\r
3441 // [HGM] seekgraph
\r
3442 void DrawSeekAxis( int x, int y, int xTo, int yTo )
\r
3445 HPEN hp = SelectObject( hdcSeek, gridPen );
\r
3446 MoveToEx( hdcSeek, boardRect.left+x, boardRect.top+y, &stPt );
\r
3447 LineTo( hdcSeek, boardRect.left+xTo, boardRect.top+yTo );
\r
3448 SelectObject( hdcSeek, hp );
\r
3451 // front-end wrapper for drawing functions to do rectangles
\r
3452 void DrawSeekBackground( int left, int top, int right, int bottom )
\r
3457 if (hdcSeek == NULL) {
\r
3458 hdcSeek = GetDC(hwndMain);
\r
3459 if (!appData.monoMode) {
\r
3460 SelectPalette(hdcSeek, hPal, FALSE);
\r
3461 RealizePalette(hdcSeek);
\r
3464 hp = SelectObject( hdcSeek, gridPen );
\r
3465 rc.top = boardRect.top+top; rc.left = boardRect.left+left;
\r
3466 rc.bottom = boardRect.top+bottom; rc.right = boardRect.left+right;
\r
3467 FillRect( hdcSeek, &rc, lightSquareBrush );
\r
3468 SelectObject( hdcSeek, hp );
\r
3471 // front-end wrapper for putting text in graph
\r
3472 void DrawSeekText(char *buf, int x, int y)
\r
3475 SetBkMode( hdcSeek, TRANSPARENT );
\r
3476 GetTextExtentPoint32( hdcSeek, buf, strlen(buf), &stSize );
\r
3477 TextOut( hdcSeek, boardRect.left+x-3, boardRect.top+y-stSize.cy/2, buf, strlen(buf) );
\r
3480 void DrawSeekDot(int x, int y, int color)
\r
3482 int square = color & 0x80;
\r
3483 HBRUSH oldBrush = SelectObject(hdcSeek,
\r
3484 color == 0 ? markerBrush : color == 1 ? darkSquareBrush : explodeBrush);
\r
3487 Rectangle(hdcSeek, boardRect.left+x - squareSize/9, boardRect.top+y - squareSize/9,
\r
3488 boardRect.left+x + squareSize/9, boardRect.top+y + squareSize/9);
\r
3490 Ellipse(hdcSeek, boardRect.left+x - squareSize/8, boardRect.top+y - squareSize/8,
\r
3491 boardRect.left+x + squareSize/8, boardRect.top+y + squareSize/8);
\r
3492 SelectObject(hdcSeek, oldBrush);
\r
3496 HDCDrawPosition(HDC hdc, BOOLEAN repaint, Board board)
\r
3498 static Board lastReq[2], lastDrawn[2];
\r
3499 static HighlightInfo lastDrawnHighlight, lastDrawnPremove;
\r
3500 static int lastDrawnFlipView = 0;
\r
3501 static int lastReqValid[2] = {0, 0}, lastDrawnValid[2] = {0, 0};
\r
3502 int releaseDC, x, y, x2, y2, row, column, num_clips = 0, i;
\r
3505 HBITMAP bufferBitmap;
\r
3506 HBITMAP oldBitmap;
\r
3508 HRGN clips[MAX_CLIPS];
\r
3509 ChessSquare dragged_piece = EmptySquare;
\r
3510 int nr = twoBoards*partnerUp;
\r
3512 /* I'm undecided on this - this function figures out whether a full
\r
3513 * repaint is necessary on its own, so there's no real reason to have the
\r
3514 * caller tell it that. I think this can safely be set to FALSE - but
\r
3515 * if we trust the callers not to request full repaints unnessesarily, then
\r
3516 * we could skip some clipping work. In other words, only request a full
\r
3517 * redraw when the majority of pieces have changed positions (ie. flip,
\r
3518 * gamestart and similar) --Hawk
\r
3520 Boolean fullrepaint = repaint;
\r
3522 if(DrawSeekGraph()) return; // [HG} seekgraph: suppress printing board if seek graph up
\r
3524 if( DrawPositionNeedsFullRepaint() ) {
\r
3525 fullrepaint = TRUE;
\r
3528 if (board == NULL) {
\r
3529 if (!lastReqValid[nr]) {
\r
3532 board = lastReq[nr];
\r
3534 CopyBoard(lastReq[nr], board);
\r
3535 lastReqValid[nr] = 1;
\r
3538 if (doingSizing) {
\r
3542 if (IsIconic(hwndMain)) {
\r
3546 if (hdc == NULL) {
\r
3547 hdc = GetDC(hwndMain);
\r
3548 if (!appData.monoMode) {
\r
3549 SelectPalette(hdc, hPal, FALSE);
\r
3550 RealizePalette(hdc);
\r
3554 releaseDC = FALSE;
\r
3557 /* Create some work-DCs */
\r
3558 hdcmem = CreateCompatibleDC(hdc);
\r
3559 tmphdc = CreateCompatibleDC(hdc);
\r
3561 /* If dragging is in progress, we temporarely remove the piece */
\r
3562 /* [HGM] or temporarily decrease count if stacked */
\r
3563 /* !! Moved to before board compare !! */
\r
3564 if (dragInfo.from.x >= 0 && dragInfo.pos.x >= 0) {
\r
3565 dragged_piece = board[dragInfo.from.y][dragInfo.from.x];
\r
3566 if(dragInfo.from.x == BOARD_LEFT-2 ) {
\r
3567 if(--board[dragInfo.from.y][dragInfo.from.x+1] == 0 )
\r
3568 board[dragInfo.from.y][dragInfo.from.x] = EmptySquare;
\r
3570 if(dragInfo.from.x == BOARD_RGHT+1) {
\r
3571 if(--board[dragInfo.from.y][dragInfo.from.x-1] == 0 )
\r
3572 board[dragInfo.from.y][dragInfo.from.x] = EmptySquare;
\r
3574 board[dragInfo.from.y][dragInfo.from.x] = gatingPiece;
\r
3577 /* Figure out which squares need updating by comparing the
\r
3578 * newest board with the last drawn board and checking if
\r
3579 * flipping has changed.
\r
3581 if (!fullrepaint && lastDrawnValid[nr] && (nr == 1 || lastDrawnFlipView == flipView)) {
\r
3582 for (row = 0; row < BOARD_HEIGHT; row++) { /* [HGM] true size, not 8 */
\r
3583 for (column = 0; column < BOARD_WIDTH; column++) {
\r
3584 if (lastDrawn[nr][row][column] != board[row][column]) {
\r
3585 SquareToPos(row, column, &x, &y);
\r
3586 clips[num_clips++] =
\r
3587 CreateRectRgn(x, y, x + squareSize, y + squareSize);
\r
3591 if(nr == 0) { // [HGM] dual: no highlights on second board
\r
3592 for (i=0; i<2; i++) {
\r
3593 if (lastDrawnHighlight.sq[i].x != highlightInfo.sq[i].x ||
\r
3594 lastDrawnHighlight.sq[i].y != highlightInfo.sq[i].y) {
\r
3595 if (lastDrawnHighlight.sq[i].x >= 0 &&
\r
3596 lastDrawnHighlight.sq[i].y >= 0) {
\r
3597 SquareToPos(lastDrawnHighlight.sq[i].y,
\r
3598 lastDrawnHighlight.sq[i].x, &x, &y);
\r
3599 clips[num_clips++] =
\r
3600 CreateRectRgn(x - lineGap, y - lineGap,
\r
3601 x + squareSize + lineGap, y + squareSize + lineGap);
\r
3603 if (highlightInfo.sq[i].x >= 0 && highlightInfo.sq[i].y >= 0) {
\r
3604 SquareToPos(highlightInfo.sq[i].y, highlightInfo.sq[i].x, &x, &y);
\r
3605 clips[num_clips++] =
\r
3606 CreateRectRgn(x - lineGap, y - lineGap,
\r
3607 x + squareSize + lineGap, y + squareSize + lineGap);
\r
3611 for (i=0; i<2; i++) {
\r
3612 if (lastDrawnPremove.sq[i].x != premoveHighlightInfo.sq[i].x ||
\r
3613 lastDrawnPremove.sq[i].y != premoveHighlightInfo.sq[i].y) {
\r
3614 if (lastDrawnPremove.sq[i].x >= 0 &&
\r
3615 lastDrawnPremove.sq[i].y >= 0) {
\r
3616 SquareToPos(lastDrawnPremove.sq[i].y,
\r
3617 lastDrawnPremove.sq[i].x, &x, &y);
\r
3618 clips[num_clips++] =
\r
3619 CreateRectRgn(x - lineGap, y - lineGap,
\r
3620 x + squareSize + lineGap, y + squareSize + lineGap);
\r
3622 if (premoveHighlightInfo.sq[i].x >= 0 &&
\r
3623 premoveHighlightInfo.sq[i].y >= 0) {
\r
3624 SquareToPos(premoveHighlightInfo.sq[i].y,
\r
3625 premoveHighlightInfo.sq[i].x, &x, &y);
\r
3626 clips[num_clips++] =
\r
3627 CreateRectRgn(x - lineGap, y - lineGap,
\r
3628 x + squareSize + lineGap, y + squareSize + lineGap);
\r
3632 } else { // nr == 1
\r
3633 partnerHighlightInfo.sq[0].y = board[EP_STATUS-4];
\r
3634 partnerHighlightInfo.sq[0].x = board[EP_STATUS-3];
\r
3635 partnerHighlightInfo.sq[1].y = board[EP_STATUS-2];
\r
3636 partnerHighlightInfo.sq[1].x = board[EP_STATUS-1];
\r
3637 for (i=0; i<2; i++) {
\r
3638 if (partnerHighlightInfo.sq[i].x >= 0 &&
\r
3639 partnerHighlightInfo.sq[i].y >= 0) {
\r
3640 SquareToPos(partnerHighlightInfo.sq[i].y,
\r
3641 partnerHighlightInfo.sq[i].x, &x, &y);
\r
3642 clips[num_clips++] =
\r
3643 CreateRectRgn(x - lineGap, y - lineGap,
\r
3644 x + squareSize + lineGap, y + squareSize + lineGap);
\r
3646 if (oldPartnerHighlight.sq[i].x >= 0 &&
\r
3647 oldPartnerHighlight.sq[i].y >= 0) {
\r
3648 SquareToPos(oldPartnerHighlight.sq[i].y,
\r
3649 oldPartnerHighlight.sq[i].x, &x, &y);
\r
3650 clips[num_clips++] =
\r
3651 CreateRectRgn(x - lineGap, y - lineGap,
\r
3652 x + squareSize + lineGap, y + squareSize + lineGap);
\r
3657 fullrepaint = TRUE;
\r
3660 /* Create a buffer bitmap - this is the actual bitmap
\r
3661 * being written to. When all the work is done, we can
\r
3662 * copy it to the real DC (the screen). This avoids
\r
3663 * the problems with flickering.
\r
3665 GetClientRect(hwndMain, &Rect);
\r
3666 bufferBitmap = CreateCompatibleBitmap(hdc, Rect.right-Rect.left+1,
\r
3667 Rect.bottom-Rect.top+1);
\r
3668 oldBitmap = SelectObject(hdcmem, bufferBitmap);
\r
3669 if (!appData.monoMode) {
\r
3670 SelectPalette(hdcmem, hPal, FALSE);
\r
3673 /* Create clips for dragging */
\r
3674 if (!fullrepaint) {
\r
3675 if (dragInfo.from.x >= 0) {
\r
3676 SquareToPos(dragInfo.from.y, dragInfo.from.x, &x, &y);
\r
3677 clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);
\r
3679 if (dragInfo.start.x >= 0) {
\r
3680 SquareToPos(dragInfo.start.y, dragInfo.start.x, &x, &y);
\r
3681 clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);
\r
3683 if (dragInfo.pos.x >= 0) {
\r
3684 x = dragInfo.pos.x - squareSize / 2;
\r
3685 y = dragInfo.pos.y - squareSize / 2;
\r
3686 clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);
\r
3688 if (dragInfo.lastpos.x >= 0) {
\r
3689 x = dragInfo.lastpos.x - squareSize / 2;
\r
3690 y = dragInfo.lastpos.y - squareSize / 2;
\r
3691 clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);
\r
3695 /* Are we animating a move?
\r
3697 * - remove the piece from the board (temporarely)
\r
3698 * - calculate the clipping region
\r
3700 if (!fullrepaint) {
\r
3701 if (animInfo.piece != EmptySquare) {
\r
3702 board[animInfo.from.y][animInfo.from.x] = EmptySquare;
\r
3703 x = boardRect.left + animInfo.lastpos.x;
\r
3704 y = boardRect.top + animInfo.lastpos.y;
\r
3705 x2 = boardRect.left + animInfo.pos.x;
\r
3706 y2 = boardRect.top + animInfo.pos.y;
\r
3707 clips[num_clips++] = CreateRectRgn(MIN(x,x2), MIN(y,y2), MAX(x,x2)+squareSize, MAX(y,y2)+squareSize);
\r
3708 /* Slight kludge. The real problem is that after AnimateMove is
\r
3709 done, the position on the screen does not match lastDrawn.
\r
3710 This currently causes trouble only on e.p. captures in
\r
3711 atomic, where the piece moves to an empty square and then
\r
3712 explodes. The old and new positions both had an empty square
\r
3713 at the destination, but animation has drawn a piece there and
\r
3714 we have to remember to erase it. [HGM] moved until after setting lastDrawn */
\r
3715 lastDrawn[0][animInfo.to.y][animInfo.to.x] = animInfo.piece;
\r
3719 /* No clips? Make sure we have fullrepaint set to TRUE */
\r
3720 if (num_clips == 0)
\r
3721 fullrepaint = TRUE;
\r
3723 /* Set clipping on the memory DC */
\r
3724 if (!fullrepaint) {
\r
3725 SelectClipRgn(hdcmem, clips[0]);
\r
3726 for (x = 1; x < num_clips; x++) {
\r
3727 if (ExtSelectClipRgn(hdcmem, clips[x], RGN_OR) == ERROR)
\r
3728 abort(); // this should never ever happen!
\r
3732 /* Do all the drawing to the memory DC */
\r
3733 if(explodeInfo.radius) { // [HGM] atomic
\r
3735 int x, y, r=(explodeInfo.radius * squareSize)/100;
\r
3736 ChessSquare piece = board[explodeInfo.fromY][explodeInfo.fromX];
\r
3737 board[explodeInfo.fromY][explodeInfo.fromX] = EmptySquare; // suppress display of capturer
\r
3738 SquareToPos(explodeInfo.toY, explodeInfo.toX, &x, &y);
\r
3739 x += squareSize/2;
\r
3740 y += squareSize/2;
\r
3741 if(!fullrepaint) {
\r
3742 clips[num_clips] = CreateRectRgn(x-r, y-r, x+r, y+r);
\r
3743 ExtSelectClipRgn(hdcmem, clips[num_clips++], RGN_OR);
\r
3745 DrawGridOnDC(hdcmem);
\r
3746 DrawHighlightsOnDC(hdcmem, &highlightInfo, HIGHLIGHT_PEN);
\r
3747 DrawHighlightsOnDC(hdcmem, &premoveHighlightInfo, PREMOVE_PEN);
\r
3748 DrawBoardOnDC(hdcmem, board, tmphdc);
\r
3749 board[explodeInfo.fromY][explodeInfo.fromX] = piece;
\r
3750 oldBrush = SelectObject(hdcmem, explodeBrush);
\r
3751 Ellipse(hdcmem, x-r, y-r, x+r, y+r);
\r
3752 SelectObject(hdcmem, oldBrush);
\r
3754 DrawGridOnDC(hdcmem);
\r
3755 if(nr == 0) { // [HGM] dual: decide which highlights to draw
\r
3756 DrawHighlightsOnDC(hdcmem, &highlightInfo, HIGHLIGHT_PEN);
\r
3757 DrawHighlightsOnDC(hdcmem, &premoveHighlightInfo, PREMOVE_PEN);
\r
3759 DrawHighlightsOnDC(hdcmem, &partnerHighlightInfo, HIGHLIGHT_PEN);
\r
3760 oldPartnerHighlight = partnerHighlightInfo;
\r
3762 DrawBoardOnDC(hdcmem, board, tmphdc);
\r
3764 if(nr == 0) // [HGM] dual: markers only on left board
\r
3765 for (row = 0; row < BOARD_HEIGHT; row++) {
\r
3766 for (column = 0; column < BOARD_WIDTH; column++) {
\r
3767 if (marker[row][column]) { // marker changes only occur with full repaint!
\r
3768 HBRUSH oldBrush = SelectObject(hdcmem,
\r
3769 marker[row][column] == 2 ? markerBrush : explodeBrush);
\r
3770 SquareToPos(row, column, &x, &y);
\r
3771 Ellipse(hdcmem, x + squareSize/4, y + squareSize/4,
\r
3772 x + 3*squareSize/4, y + 3*squareSize/4);
\r
3773 SelectObject(hdcmem, oldBrush);
\r
3778 if( appData.highlightMoveWithArrow ) {
\r
3779 DrawArrowHighlight(hdcmem);
\r
3782 DrawCoordsOnDC(hdcmem);
\r
3784 CopyBoard(lastDrawn[nr], board); /* [HGM] Moved to here from end of routine, */
\r
3785 /* to make sure lastDrawn contains what is actually drawn */
\r
3787 /* Put the dragged piece back into place and draw it (out of place!) */
\r
3788 if (dragged_piece != EmptySquare) {
\r
3789 /* [HGM] or restack */
\r
3790 if(dragInfo.from.x == BOARD_LEFT-2 )
\r
3791 board[dragInfo.from.y][dragInfo.from.x+1]++;
\r
3793 if(dragInfo.from.x == BOARD_RGHT+1 )
\r
3794 board[dragInfo.from.y][dragInfo.from.x-1]++;
\r
3795 board[dragInfo.from.y][dragInfo.from.x] = dragged_piece;
\r
3796 x = dragInfo.pos.x - squareSize / 2;
\r
3797 y = dragInfo.pos.y - squareSize / 2;
\r
3798 DrawPieceOnDC(hdcmem, dragged_piece,
\r
3799 ((int) dragged_piece < (int) BlackPawn),
\r
3800 (dragInfo.from.y + dragInfo.from.x) % 2, x, y, tmphdc);
\r
3803 /* Put the animated piece back into place and draw it */
\r
3804 if (animInfo.piece != EmptySquare) {
\r
3805 board[animInfo.from.y][animInfo.from.x] = animInfo.piece;
\r
3806 x = boardRect.left + animInfo.pos.x;
\r
3807 y = boardRect.top + animInfo.pos.y;
\r
3808 DrawPieceOnDC(hdcmem, animInfo.piece,
\r
3809 ((int) animInfo.piece < (int) BlackPawn),
\r
3810 (animInfo.from.y + animInfo.from.x) % 2, x, y, tmphdc);
\r
3813 /* Release the bufferBitmap by selecting in the old bitmap
\r
3814 * and delete the memory DC
\r
3816 SelectObject(hdcmem, oldBitmap);
\r
3819 /* Set clipping on the target DC */
\r
3820 if (!fullrepaint) {
\r
3821 if(nr == 1) for (x = 0; x < num_clips; x++) { // [HGM] dual: translate clips
\r
3823 GetRgnBox(clips[x], &rect);
\r
3824 DeleteObject(clips[x]);
\r
3825 clips[x] = CreateRectRgn(rect.left + wpMain.width/2, rect.top,
\r
3826 rect.right + wpMain.width/2, rect.bottom);
\r
3828 SelectClipRgn(hdc, clips[0]);
\r
3829 for (x = 1; x < num_clips; x++) {
\r
3830 if (ExtSelectClipRgn(hdc, clips[x], RGN_OR) == ERROR)
\r
3831 abort(); // this should never ever happen!
\r
3835 /* Copy the new bitmap onto the screen in one go.
\r
3836 * This way we avoid any flickering
\r
3838 oldBitmap = SelectObject(tmphdc, bufferBitmap);
\r
3839 BitBlt(hdc, boardRect.left + twoBoards*partnerUp*wpMain.width/2, boardRect.top, // [HGM] dual
\r
3840 boardRect.right - boardRect.left,
\r
3841 boardRect.bottom - boardRect.top,
\r
3842 tmphdc, boardRect.left, boardRect.top, SRCCOPY);
\r
3843 if(saveDiagFlag) {
\r
3844 BITMAP b; int i, j=0, m, w, wb, fac=0; char pData[1000000];
\r
3845 BITMAPINFOHEADER bih; int color[16], nrColors=0;
\r
3847 GetObject(bufferBitmap, sizeof(b), &b);
\r
3848 if(b.bmWidthBytes*b.bmHeight <= 990000) {
\r
3849 bih.biSize = sizeof(BITMAPINFOHEADER);
\r
3850 bih.biWidth = b.bmWidth;
\r
3851 bih.biHeight = b.bmHeight;
\r
3853 bih.biBitCount = b.bmBitsPixel;
\r
3854 bih.biCompression = 0;
\r
3855 bih.biSizeImage = b.bmWidthBytes*b.bmHeight;
\r
3856 bih.biXPelsPerMeter = 0;
\r
3857 bih.biYPelsPerMeter = 0;
\r
3858 bih.biClrUsed = 0;
\r
3859 bih.biClrImportant = 0;
\r
3860 // fprintf(diagFile, "t=%d\nw=%d\nh=%d\nB=%d\nP=%d\nX=%d\n",
\r
3861 // b.bmType, b.bmWidth, b.bmHeight, b.bmWidthBytes, b.bmPlanes, b.bmBitsPixel);
\r
3862 GetDIBits(tmphdc,bufferBitmap,0,b.bmHeight,pData,(BITMAPINFO*)&bih,DIB_RGB_COLORS);
\r
3863 // fprintf(diagFile, "%8x\n", (int) pData);
\r
3865 wb = b.bmWidthBytes;
\r
3867 for(i=0; i<wb*(b.bmHeight - boardRect.top + OUTER_MARGIN)>>2; i++) {
\r
3868 int k = ((int*) pData)[i];
\r
3869 for(j=0; j<nrColors; j++) if(color[j] == k) break;
\r
3870 if(j >= 16) break;
\r
3872 if(j >= nrColors) nrColors = j+1;
\r
3874 if(j<16) { // 16 colors is enough. Compress to 4 bits per pixel
\r
3876 for(i=0; i<b.bmHeight - boardRect.top + OUTER_MARGIN; i++) {
\r
3877 for(w=0; w<(wb>>2); w+=2) {
\r
3878 int k = ((int*) pData)[(wb*i>>2) + w];
\r
3879 for(j=0; j<nrColors; j++) if(color[j] == k) break;
\r
3880 k = ((int*) pData)[(wb*i>>2) + w + 1];
\r
3881 for(m=0; m<nrColors; m++) if(color[m] == k) break;
\r
3882 pData[p++] = m | j<<4;
\r
3884 while(p&3) pData[p++] = 0;
\r
3887 wb = ((wb+31)>>5)<<2;
\r
3889 // write BITMAPFILEHEADER
\r
3890 fprintf(diagFile, "BM");
\r
3891 fputDW(diagFile, wb*(b.bmHeight - boardRect.top + OUTER_MARGIN)+0x36 + (fac?64:0));
\r
3892 fputDW(diagFile, 0);
\r
3893 fputDW(diagFile, 0x36 + (fac?64:0));
\r
3894 // write BITMAPINFOHEADER
\r
3895 fputDW(diagFile, 40);
\r
3896 fputDW(diagFile, b.bmWidth);
\r
3897 fputDW(diagFile, b.bmHeight - boardRect.top + OUTER_MARGIN);
\r
3898 if(fac) fputDW(diagFile, 0x040001); // planes and bits/pixel
\r
3899 else fputDW(diagFile, 0x200001); // planes and bits/pixel
\r
3900 fputDW(diagFile, 0);
\r
3901 fputDW(diagFile, 0);
\r
3902 fputDW(diagFile, 0);
\r
3903 fputDW(diagFile, 0);
\r
3904 fputDW(diagFile, 0);
\r
3905 fputDW(diagFile, 0);
\r
3906 // write color table
\r
3908 for(i=0; i<16; i++) fputDW(diagFile, color[i]);
\r
3909 // write bitmap data
\r
3910 for(i=0; i<wb*(b.bmHeight - boardRect.top + OUTER_MARGIN); i++)
\r
3911 fputc(pData[i], diagFile);
\r
3915 SelectObject(tmphdc, oldBitmap);
\r
3917 /* Massive cleanup */
\r
3918 for (x = 0; x < num_clips; x++)
\r
3919 DeleteObject(clips[x]);
\r
3922 DeleteObject(bufferBitmap);
\r
3925 ReleaseDC(hwndMain, hdc);
\r
3927 if (lastDrawnFlipView != flipView && nr == 0) {
\r
3929 CheckMenuItem(GetMenu(hwndMain),IDM_FlipView, MF_BYCOMMAND|MF_CHECKED);
\r
3931 CheckMenuItem(GetMenu(hwndMain),IDM_FlipView, MF_BYCOMMAND|MF_UNCHECKED);
\r
3934 /* CopyBoard(lastDrawn, board);*/
\r
3935 lastDrawnHighlight = highlightInfo;
\r
3936 lastDrawnPremove = premoveHighlightInfo;
\r
3937 lastDrawnFlipView = flipView;
\r
3938 lastDrawnValid[nr] = 1;
\r
3941 /* [HGM] diag: Save the current board display to the given open file and close the file */
\r
3946 saveDiagFlag = 1; diagFile = f;
\r
3947 HDCDrawPosition(NULL, TRUE, NULL);
\r
3951 // if(f != NULL) fprintf(f, "Sorry, but this feature is still in preparation\n");
\r
3958 /*---------------------------------------------------------------------------*\
\r
3959 | CLIENT PAINT PROCEDURE
\r
3960 | This is the main event-handler for the WM_PAINT message.
\r
3962 \*---------------------------------------------------------------------------*/
\r
3964 PaintProc(HWND hwnd)
\r
3970 if((hdc = BeginPaint(hwnd, &ps))) {
\r
3971 if (IsIconic(hwnd)) {
\r
3972 DrawIcon(hdc, 2, 2, iconCurrent);
\r
3974 if (!appData.monoMode) {
\r
3975 SelectPalette(hdc, hPal, FALSE);
\r
3976 RealizePalette(hdc);
\r
3978 HDCDrawPosition(hdc, 1, NULL);
\r
3979 if(twoBoards) { // [HGM] dual: also redraw other board in other orientation
\r
3980 flipView = !flipView; partnerUp = !partnerUp;
\r
3981 HDCDrawPosition(hdc, 1, NULL);
\r
3982 flipView = !flipView; partnerUp = !partnerUp;
\r
3985 SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);
\r
3986 ExtTextOut(hdc, messageRect.left, messageRect.top,
\r
3987 ETO_CLIPPED|ETO_OPAQUE,
\r
3988 &messageRect, messageText, strlen(messageText), NULL);
\r
3989 SelectObject(hdc, oldFont);
\r
3990 DisplayBothClocks();
\r
3993 EndPaint(hwnd,&ps);
\r
4001 * If the user selects on a border boundary, return -1; if off the board,
\r
4002 * return -2. Otherwise map the event coordinate to the square.
\r
4003 * The offset boardRect.left or boardRect.top must already have been
\r
4004 * subtracted from x.
\r
4006 int EventToSquare(x, limit)
\r
4014 if ((x % (squareSize + lineGap)) >= squareSize)
\r
4016 x /= (squareSize + lineGap);
\r
4028 DropEnable dropEnables[] = {
\r
4029 { 'P', DP_Pawn, N_("Pawn") },
\r
4030 { 'N', DP_Knight, N_("Knight") },
\r
4031 { 'B', DP_Bishop, N_("Bishop") },
\r
4032 { 'R', DP_Rook, N_("Rook") },
\r
4033 { 'Q', DP_Queen, N_("Queen") },
\r
4037 SetupDropMenu(HMENU hmenu)
\r
4039 int i, count, enable;
\r
4041 extern char white_holding[], black_holding[];
\r
4042 char item[MSG_SIZ];
\r
4044 for (i=0; i<sizeof(dropEnables)/sizeof(DropEnable); i++) {
\r
4045 p = strchr(gameMode == IcsPlayingWhite ? white_holding : black_holding,
\r
4046 dropEnables[i].piece);
\r
4048 while (p && *p++ == dropEnables[i].piece) count++;
\r
4049 snprintf(item, MSG_SIZ, "%s %d", T_(dropEnables[i].name), count);
\r
4050 enable = count > 0 || !appData.testLegality
\r
4051 /*!!temp:*/ || (gameInfo.variant == VariantCrazyhouse
\r
4052 && !appData.icsActive);
\r
4053 ModifyMenu(hmenu, dropEnables[i].command,
\r
4054 MF_BYCOMMAND | (enable ? MF_ENABLED : MF_GRAYED) | MF_STRING,
\r
4055 dropEnables[i].command, item);
\r
4059 void DragPieceBegin(int x, int y)
\r
4061 dragInfo.lastpos.x = boardRect.left + x;
\r
4062 dragInfo.lastpos.y = boardRect.top + y;
\r
4063 dragInfo.from.x = fromX;
\r
4064 dragInfo.from.y = fromY;
\r
4065 dragInfo.start = dragInfo.from;
\r
4066 SetCapture(hwndMain);
\r
4069 void DragPieceEnd(int x, int y)
\r
4072 dragInfo.start.x = dragInfo.start.y = -1;
\r
4073 dragInfo.from = dragInfo.start;
\r
4074 dragInfo.pos = dragInfo.lastpos = dragInfo.start;
\r
4077 /* Event handler for mouse messages */
\r
4079 MouseEvent(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
\r
4083 static int recursive = 0;
\r
4085 BOOLEAN forceFullRepaint = IsFullRepaintPreferrable(); /* [AS] */
\r
4088 if (message == WM_MBUTTONUP) {
\r
4089 /* Hideous kludge to fool TrackPopupMenu into paying attention
\r
4090 to the middle button: we simulate pressing the left button too!
\r
4092 PostMessage(hwnd, WM_LBUTTONDOWN, wParam, lParam);
\r
4093 PostMessage(hwnd, WM_LBUTTONUP, wParam, lParam);
\r
4099 pt.x = LOWORD(lParam);
\r
4100 pt.y = HIWORD(lParam);
\r
4101 x = EventToSquare(pt.x - boardRect.left, BOARD_WIDTH);
\r
4102 y = EventToSquare(pt.y - boardRect.top, BOARD_HEIGHT);
\r
4103 if (!flipView && y >= 0) {
\r
4104 y = BOARD_HEIGHT - 1 - y;
\r
4106 if (flipView && x >= 0) {
\r
4107 x = BOARD_WIDTH - 1 - x;
\r
4110 shiftKey = GetKeyState(VK_SHIFT) < 0; // [HGM] remember last shift status
\r
4112 switch (message) {
\r
4113 case WM_LBUTTONDOWN:
\r
4114 if (PtInRect((LPRECT) &whiteRect, pt)) {
\r
4115 if (gameMode == EditPosition) {
\r
4116 SetWhiteToPlayEvent();
\r
4117 } else if (gameMode == EditGame || GetKeyState(VK_SHIFT) < 0) {
\r
4118 AdjustClock(flipClock, -1);
\r
4119 } else if (gameMode == IcsPlayingBlack ||
\r
4120 gameMode == MachinePlaysWhite) {
\r
4123 } else if (PtInRect((LPRECT) &blackRect, pt)) {
\r
4124 if (gameMode == EditPosition) {
\r
4125 SetBlackToPlayEvent();
\r
4126 } else if (gameMode == EditGame || GetKeyState(VK_SHIFT) < 0) {
\r
4127 AdjustClock(!flipClock, -1);
\r
4128 } else if (gameMode == IcsPlayingWhite ||
\r
4129 gameMode == MachinePlaysBlack) {
\r
4133 dragInfo.start.x = dragInfo.start.y = -1;
\r
4134 dragInfo.from = dragInfo.start;
\r
4135 if(fromX == -1 && frozen) { // not sure where this is for
\r
4136 fromX = fromY = -1;
\r
4137 DrawPosition(forceFullRepaint || FALSE, NULL); /* [AS] */
\r
4140 LeftClick(Press, pt.x - boardRect.left, pt.y - boardRect.top);
\r
4141 DrawPosition(TRUE, NULL);
\r
4144 case WM_LBUTTONUP:
\r
4145 LeftClick(Release, pt.x - boardRect.left, pt.y - boardRect.top);
\r
4146 DrawPosition(TRUE, NULL);
\r
4149 case WM_MOUSEMOVE:
\r
4150 if(SeekGraphClick(Press, pt.x - boardRect.left, pt.y - boardRect.top, 1)) break;
\r
4151 MovePV(pt.x - boardRect.left, pt.y - boardRect.top, boardRect.bottom - boardRect.top);
\r
4152 if ((appData.animateDragging || appData.highlightDragging)
\r
4153 && (wParam & MK_LBUTTON)
\r
4154 && dragInfo.from.x >= 0)
\r
4156 BOOL full_repaint = FALSE;
\r
4158 if (appData.animateDragging) {
\r
4159 dragInfo.pos = pt;
\r
4161 if (appData.highlightDragging) {
\r
4162 SetHighlights(fromX, fromY, x, y);
\r
4163 if( IsDrawArrowEnabled() && (x < 0 || x >= BOARD_WIDTH || y < 0 || y >= BOARD_HEIGHT) ) {
\r
4164 full_repaint = TRUE;
\r
4168 DrawPosition( full_repaint, NULL);
\r
4170 dragInfo.lastpos = dragInfo.pos;
\r
4174 case WM_MOUSEWHEEL: // [DM]
\r
4175 { static int lastDir = 0; // [HGM] build in some hysteresis to avoid spurious events
\r
4176 /* Mouse Wheel is being rolled forward
\r
4177 * Play moves forward
\r
4179 if((short)HIWORD(wParam) < 0 && currentMove < forwardMostMove)
\r
4180 { if(lastDir == 1) ForwardEvent(); else lastDir = 1; } // [HGM] suppress first event in direction
\r
4181 /* Mouse Wheel is being rolled backward
\r
4182 * Play moves backward
\r
4184 if((short)HIWORD(wParam) > 0 && currentMove > backwardMostMove)
\r
4185 { if(lastDir == -1) BackwardEvent(); else lastDir = -1; }
\r
4189 case WM_MBUTTONUP:
\r
4190 case WM_RBUTTONUP:
\r
4192 RightClick(Release, pt.x - boardRect.left, pt.y - boardRect.top, &fromX, &fromY);
\r
4195 case WM_MBUTTONDOWN:
\r
4196 case WM_RBUTTONDOWN:
\r
4199 fromX = fromY = -1;
\r
4200 dragInfo.pos.x = dragInfo.pos.y = -1;
\r
4201 dragInfo.start.x = dragInfo.start.y = -1;
\r
4202 dragInfo.from = dragInfo.start;
\r
4203 dragInfo.lastpos = dragInfo.pos;
\r
4204 if (appData.highlightDragging) {
\r
4205 ClearHighlights();
\r
4208 /* [HGM] right mouse button in clock area edit-game mode ups clock */
\r
4209 if (PtInRect((LPRECT) &whiteRect, pt)) {
\r
4210 if (gameMode == EditGame || GetKeyState(VK_SHIFT) < 0) AdjustClock(flipClock, 1);
\r
4211 } else if (PtInRect((LPRECT) &blackRect, pt)) {
\r
4212 if (gameMode == EditGame || GetKeyState(VK_SHIFT) < 0) AdjustClock(!flipClock, 1);
\r
4216 DrawPosition(TRUE, NULL);
\r
4218 menuNr = RightClick(Press, pt.x - boardRect.left, pt.y - boardRect.top, &fromX, &fromY);
\r
4221 if (message == WM_MBUTTONDOWN) {
\r
4222 buttonCount = 3; /* even if system didn't think so */
\r
4223 if (wParam & MK_SHIFT)
\r
4224 MenuPopup(hwnd, pt, LoadMenu(hInst, "BlackPieceMenu"), -1);
\r
4226 MenuPopup(hwnd, pt, LoadMenu(hInst, "WhitePieceMenu"), -1);
\r
4227 } else { /* message == WM_RBUTTONDOWN */
\r
4228 /* Just have one menu, on the right button. Windows users don't
\r
4229 think to try the middle one, and sometimes other software steals
\r
4230 it, or it doesn't really exist. */
\r
4231 if(gameInfo.variant != VariantShogi)
\r
4232 MenuPopup(hwnd, pt, LoadMenu(hInst, "PieceMenu"), -1);
\r
4234 MenuPopup(hwnd, pt, LoadMenu(hInst, "ShogiPieceMenu"), -1);
\r
4238 SetCapture(hwndMain);
4241 hmenu = LoadMenu(hInst, "DropPieceMenu");
\r
4242 SetupDropMenu(hmenu);
\r
4243 MenuPopup(hwnd, pt, hmenu, -1);
\r
4253 /* Preprocess messages for buttons in main window */
\r
4255 ButtonProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
\r
4257 int id = GetWindowLong(hwnd, GWL_ID);
\r
4260 for (i=0; i<N_BUTTONS; i++) {
\r
4261 if (buttonDesc[i].id == id) break;
\r
4263 if (i == N_BUTTONS) return 0;
\r
4264 switch (message) {
\r
4269 dir = (wParam == VK_LEFT) ? -1 : 1;
\r
4270 SetFocus(buttonDesc[(i + dir + N_BUTTONS) % N_BUTTONS].hwnd);
\r
4277 SendMessage(hwndMain, WM_COMMAND, MAKEWPARAM(buttonDesc[i].id, 0), 0);
\r
4280 if (appData.icsActive && (isalpha((char)wParam) || wParam == '0')) {
\r
4281 // [HGM] movenum: only letters or leading zero should go to ICS input
\r
4282 HWND h = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
4283 if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);
\r
4285 SendMessage(h, WM_CHAR, wParam, lParam);
\r
4287 } else if (isalpha((char)wParam) || isdigit((char)wParam)){
\r
4288 PopUpMoveDialog((char)wParam);
\r
4294 return CallWindowProc(buttonDesc[i].wndproc, hwnd, message, wParam, lParam);
\r
4297 /* Process messages for Promotion dialog box */
\r
4299 Promotion(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
4303 switch (message) {
\r
4304 case WM_INITDIALOG: /* message: initialize dialog box */
\r
4305 /* Center the dialog over the application window */
\r
4306 CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));
\r
4307 Translate(hDlg, DLG_PromotionKing);
\r
4308 ShowWindow(GetDlgItem(hDlg, PB_King),
\r
4309 (!appData.testLegality || gameInfo.variant == VariantSuicide ||
\r
4310 gameInfo.variant == VariantSpartan && !WhiteOnMove(currentMove) ||
\r
4311 gameInfo.variant == VariantGiveaway || gameInfo.variant == VariantSuper ) ?
\r
4312 SW_SHOW : SW_HIDE);
\r
4313 /* [HGM] Only allow C & A promotions if these pieces are defined */
\r
4314 ShowWindow(GetDlgItem(hDlg, PB_Archbishop),
\r
4315 ((PieceToChar(WhiteAngel) >= 'A' && WhiteOnMove(currentMove) &&
\r
4316 PieceToChar(WhiteAngel) != '~') ||
\r
4317 (PieceToChar(BlackAngel) >= 'A' && !WhiteOnMove(currentMove) &&
\r
4318 PieceToChar(BlackAngel) != '~') ) ?
\r
4319 SW_SHOW : SW_HIDE);
\r
4320 ShowWindow(GetDlgItem(hDlg, PB_Chancellor),
\r
4321 ((PieceToChar(WhiteMarshall) >= 'A' && WhiteOnMove(currentMove) &&
\r
4322 PieceToChar(WhiteMarshall) != '~') ||
\r
4323 (PieceToChar(BlackMarshall) >= 'A' && !WhiteOnMove(currentMove) &&
\r
4324 PieceToChar(BlackMarshall) != '~') ) ?
\r
4325 SW_SHOW : SW_HIDE);
\r
4326 /* [HGM] Hide B & R button in Shogi, use Q as promote, N as defer */
\r
4327 ShowWindow(GetDlgItem(hDlg, PB_Rook),
\r
4328 gameInfo.variant != VariantShogi ?
\r
4329 SW_SHOW : SW_HIDE);
\r
4330 ShowWindow(GetDlgItem(hDlg, PB_Bishop),
\r
4331 gameInfo.variant != VariantShogi ?
\r
4332 SW_SHOW : SW_HIDE);
\r
4333 if(gameInfo.variant == VariantShogi) {
\r
4334 SetDlgItemText(hDlg, PB_Queen, "YES");
\r
4335 SetDlgItemText(hDlg, PB_Knight, "NO");
\r
4336 SetWindowText(hDlg, "Promote?");
\r
4338 ShowWindow(GetDlgItem(hDlg, IDC_Centaur),
\r
4339 gameInfo.variant == VariantSuper ?
\r
4340 SW_SHOW : SW_HIDE);
\r
4343 case WM_COMMAND: /* message: received a command */
\r
4344 switch (LOWORD(wParam)) {
\r
4346 EndDialog(hDlg, TRUE); /* Exit the dialog */
\r
4347 ClearHighlights();
\r
4348 DrawPosition(FALSE, NULL);
\r
4351 promoChar = gameInfo.variant == VariantSuper ? PieceToChar(BlackSilver) : PieceToChar(BlackKing);
\r
4354 promoChar = gameInfo.variant == VariantShogi ? '+' : ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteQueen : BlackQueen));
\r
4357 promoChar = ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteRook : BlackRook));
\r
4358 if(gameInfo.variant == VariantSpartan && !WhiteOnMove(currentMove)) promoChar = PieceToChar(BlackDragon);
\r
4361 promoChar = ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteBishop : BlackBishop));
\r
4362 if(gameInfo.variant == VariantSpartan && !WhiteOnMove(currentMove)) promoChar = PieceToChar(BlackAlfil);
\r
4364 case PB_Chancellor:
\r
4365 promoChar = ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteMarshall : BlackMarshall));
\r
4367 case PB_Archbishop:
\r
4368 promoChar = ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteAngel : BlackAngel));
\r
4371 promoChar = gameInfo.variant == VariantShogi ? '=' : PieceToChar(WhiteOnMove(currentMove) ? WhiteKnight : BlackKnight);
\r
4376 if(promoChar == '.') return FALSE; // invalid piece chosen
\r
4377 EndDialog(hDlg, TRUE); /* Exit the dialog */
\r
4378 UserMoveEvent(fromX, fromY, toX, toY, promoChar);
\r
4379 fromX = fromY = -1;
\r
4380 if (!appData.highlightLastMove) {
\r
4381 ClearHighlights();
\r
4382 DrawPosition(FALSE, NULL);
\r
4389 /* Pop up promotion dialog */
\r
4391 PromotionPopup(HWND hwnd)
\r
4395 lpProc = MakeProcInstance((FARPROC)Promotion, hInst);
\r
4396 DialogBox(hInst, MAKEINTRESOURCE(DLG_PromotionKing),
\r
4397 hwnd, (DLGPROC)lpProc);
\r
4398 FreeProcInstance(lpProc);
\r
4404 DrawPosition(TRUE, NULL);
\r
4405 PromotionPopup(hwndMain);
\r
4408 /* Toggle ShowThinking */
\r
4410 ToggleShowThinking()
\r
4412 appData.showThinking = !appData.showThinking;
\r
4413 ShowThinkingEvent();
\r
4417 LoadGameDialog(HWND hwnd, char* title)
\r
4421 char fileTitle[MSG_SIZ];
\r
4422 f = OpenFileDialog(hwnd, "rb", "",
\r
4423 appData.oldSaveStyle ? "gam" : "pgn",
\r
4425 title, &number, fileTitle, NULL);
\r
4427 cmailMsgLoaded = FALSE;
\r
4428 if (number == 0) {
\r
4429 int error = GameListBuild(f);
\r
4431 DisplayError(_("Cannot build game list"), error);
\r
4432 } else if (!ListEmpty(&gameList) &&
\r
4433 ((ListGame *) gameList.tailPred)->number > 1) {
\r
4434 GameListPopUp(f, fileTitle);
\r
4437 GameListDestroy();
\r
4440 LoadGame(f, number, fileTitle, FALSE);
\r
4444 int get_term_width()
\r
4449 HFONT hfont, hold_font;
\r
4454 hText = GetDlgItem(hwndConsole, OPT_ConsoleText);
\r
4458 // get the text metrics
\r
4459 hdc = GetDC(hText);
\r
4460 lf = font[boardSize][CONSOLE_FONT]->lf;
\r
4461 if (consoleCF.dwEffects & CFE_BOLD)
\r
4462 lf.lfWeight = FW_BOLD;
\r
4463 if (consoleCF.dwEffects & CFE_ITALIC)
\r
4464 lf.lfItalic = TRUE;
\r
4465 if (consoleCF.dwEffects & CFE_STRIKEOUT)
\r
4466 lf.lfStrikeOut = TRUE;
\r
4467 if (consoleCF.dwEffects & CFE_UNDERLINE)
\r
4468 lf.lfUnderline = TRUE;
\r
4469 hfont = CreateFontIndirect(&lf);
\r
4470 hold_font = SelectObject(hdc, hfont);
\r
4471 GetTextMetrics(hdc, &tm);
\r
4472 SelectObject(hdc, hold_font);
\r
4473 DeleteObject(hfont);
\r
4474 ReleaseDC(hText, hdc);
\r
4476 // get the rectangle
\r
4477 SendMessage(hText, EM_GETRECT, 0, (LPARAM)&rc);
\r
4479 return (rc.right-rc.left) / tm.tmAveCharWidth;
\r
4482 void UpdateICSWidth(HWND hText)
\r
4484 LONG old_width, new_width;
\r
4486 new_width = get_term_width(hText, FALSE);
\r
4487 old_width = GetWindowLong(hText, GWL_USERDATA);
\r
4488 if (new_width != old_width)
\r
4490 ics_update_width(new_width);
\r
4491 SetWindowLong(hText, GWL_USERDATA, new_width);
\r
4496 ChangedConsoleFont()
\r
4499 CHARRANGE tmpsel, sel;
\r
4500 MyFont *f = font[boardSize][CONSOLE_FONT];
\r
4501 HWND hText = GetDlgItem(hwndConsole, OPT_ConsoleText);
\r
4502 HWND hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
4505 cfmt.cbSize = sizeof(CHARFORMAT);
\r
4506 cfmt.dwMask = CFM_FACE|CFM_SIZE|CFM_CHARSET;
\r
4507 safeStrCpy(cfmt.szFaceName, font[boardSize][CONSOLE_FONT]->mfp.faceName,
\r
4508 sizeof(cfmt.szFaceName)/sizeof(cfmt.szFaceName[0]) );
\r
4509 /* yHeight is expressed in twips. A twip is 1/20 of a font's point
\r
4510 * size. This was undocumented in the version of MSVC++ that I had
\r
4511 * when I wrote the code, but is apparently documented now.
\r
4513 cfmt.yHeight = (int)(f->mfp.pointSize * 20.0 + 0.5);
\r
4514 cfmt.bCharSet = f->lf.lfCharSet;
\r
4515 cfmt.bPitchAndFamily = f->lf.lfPitchAndFamily;
\r
4516 SendMessage(hText, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &cfmt);
\r
4517 SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &cfmt);
\r
4518 /* Why are the following seemingly needed too? */
\r
4519 SendMessage(hText, EM_SETCHARFORMAT, SCF_DEFAULT, (LPARAM) &cfmt);
\r
4520 SendMessage(hInput, EM_SETCHARFORMAT, SCF_DEFAULT, (LPARAM) &cfmt);
\r
4521 SendMessage(hText, EM_EXGETSEL, 0, (LPARAM)&sel);
\r
4523 tmpsel.cpMax = -1; /*999999?*/
\r
4524 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&tmpsel);
\r
4525 SendMessage(hText, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cfmt);
\r
4526 /* Trying putting this here too. It still seems to tickle a RichEdit
\r
4527 * bug: sometimes RichEdit indents the first line of a paragraph too.
\r
4529 paraf.cbSize = sizeof(paraf);
\r
4530 paraf.dwMask = PFM_OFFSET | PFM_STARTINDENT;
\r
4531 paraf.dxStartIndent = 0;
\r
4532 paraf.dxOffset = WRAP_INDENT;
\r
4533 SendMessage(hText, EM_SETPARAFORMAT, 0, (LPARAM) ¶f);
\r
4534 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
4535 UpdateICSWidth(hText);
\r
4538 /*---------------------------------------------------------------------------*\
\r
4540 * Window Proc for main window
\r
4542 \*---------------------------------------------------------------------------*/
\r
4544 /* Process messages for main window, etc. */
\r
4546 WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
\r
4549 int wmId, wmEvent;
\r
4553 char fileTitle[MSG_SIZ];
\r
4554 char buf[MSG_SIZ];
\r
4555 static SnapData sd;
\r
4557 switch (message) {
\r
4559 case WM_PAINT: /* message: repaint portion of window */
\r
4563 case WM_ERASEBKGND:
\r
4564 if (IsIconic(hwnd)) {
\r
4565 /* Cheat; change the message */
\r
4566 return (DefWindowProc(hwnd, WM_ICONERASEBKGND, wParam, lParam));
\r
4568 return (DefWindowProc(hwnd, message, wParam, lParam));
\r
4572 case WM_LBUTTONDOWN:
\r
4573 case WM_MBUTTONDOWN:
\r
4574 case WM_RBUTTONDOWN:
\r
4575 case WM_LBUTTONUP:
\r
4576 case WM_MBUTTONUP:
\r
4577 case WM_RBUTTONUP:
\r
4578 case WM_MOUSEMOVE:
\r
4579 case WM_MOUSEWHEEL:
\r
4580 MouseEvent(hwnd, message, wParam, lParam);
\r
4583 JAWS_KB_NAVIGATION
\r
4587 JAWS_ALT_INTERCEPT
\r
4589 if (appData.icsActive && ((char)wParam == '\r' || (char)wParam > ' ' && !((char)wParam >= '1' && (char)wParam <= '9'))) {
\r
4590 // [HGM] movenum: for non-zero digits we always do type-in dialog
\r
4591 HWND h = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
4592 if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);
\r
4594 SendMessage(h, message, wParam, lParam);
\r
4595 } else if(lParam != KF_REPEAT) {
\r
4596 if (isalpha((char)wParam) || isdigit((char)wParam)) {
\r
4597 PopUpMoveDialog((char)wParam);
\r
4598 } else if((char)wParam == 003) CopyGameToClipboard();
\r
4599 else if((char)wParam == 026) PasteGameOrFENFromClipboard();
\r
4604 case WM_PALETTECHANGED:
\r
4605 if (hwnd != (HWND)wParam && !appData.monoMode) {
\r
4607 HDC hdc = GetDC(hwndMain);
\r
4608 SelectPalette(hdc, hPal, TRUE);
\r
4609 nnew = RealizePalette(hdc);
\r
4611 paletteChanged = TRUE;
\r
4612 InvalidateRect(hwnd, &boardRect, FALSE);
\r
4614 ReleaseDC(hwnd, hdc);
\r
4618 case WM_QUERYNEWPALETTE:
\r
4619 if (!appData.monoMode /*&& paletteChanged*/) {
\r
4621 HDC hdc = GetDC(hwndMain);
\r
4622 paletteChanged = FALSE;
\r
4623 SelectPalette(hdc, hPal, FALSE);
\r
4624 nnew = RealizePalette(hdc);
\r
4626 InvalidateRect(hwnd, &boardRect, FALSE);
\r
4628 ReleaseDC(hwnd, hdc);
\r
4633 case WM_COMMAND: /* message: command from application menu */
\r
4634 wmId = LOWORD(wParam);
\r
4635 wmEvent = HIWORD(wParam);
\r
4640 SAY("new game enter a move to play against the computer with white");
\r
4643 case IDM_NewGameFRC:
\r
4644 if( NewGameFRC() == 0 ) {
\r
4649 case IDM_NewVariant:
\r
4650 NewVariantPopup(hwnd);
\r
4653 case IDM_LoadGame:
\r
4654 LoadGameDialog(hwnd, _("Load Game from File"));
\r
4657 case IDM_LoadNextGame:
\r
4661 case IDM_LoadPrevGame:
\r
4665 case IDM_ReloadGame:
\r
4669 case IDM_LoadPosition:
\r
4670 if (gameMode == AnalyzeMode || gameMode == AnalyzeFile) {
\r
4671 Reset(FALSE, TRUE);
\r
4674 f = OpenFileDialog(hwnd, "rb", "",
\r
4675 appData.oldSaveStyle ? "pos" : "fen",
\r
4677 _("Load Position from File"), &number, fileTitle, NULL);
\r
4679 LoadPosition(f, number, fileTitle);
\r
4683 case IDM_LoadNextPosition:
\r
4684 ReloadPosition(1);
\r
4687 case IDM_LoadPrevPosition:
\r
4688 ReloadPosition(-1);
\r
4691 case IDM_ReloadPosition:
\r
4692 ReloadPosition(0);
\r
4695 case IDM_SaveGame:
\r
4696 defName = DefaultFileName(appData.oldSaveStyle ? "gam" : "pgn");
\r
4697 f = OpenFileDialog(hwnd, "a", defName,
\r
4698 appData.oldSaveStyle ? "gam" : "pgn",
\r
4700 _("Save Game to File"), NULL, fileTitle, NULL);
\r
4702 SaveGame(f, 0, "");
\r
4706 case IDM_SavePosition:
\r
4707 defName = DefaultFileName(appData.oldSaveStyle ? "pos" : "fen");
\r
4708 f = OpenFileDialog(hwnd, "a", defName,
\r
4709 appData.oldSaveStyle ? "pos" : "fen",
\r
4711 _("Save Position to File"), NULL, fileTitle, NULL);
\r
4713 SavePosition(f, 0, "");
\r
4717 case IDM_SaveDiagram:
\r
4718 defName = "diagram";
\r
4719 f = OpenFileDialog(hwnd, "wb", defName,
\r
4722 "Save Diagram to File", NULL, fileTitle, NULL);
\r
4728 case IDM_CopyGame:
\r
4729 CopyGameToClipboard();
\r
4732 case IDM_PasteGame:
\r
4733 PasteGameFromClipboard();
\r
4736 case IDM_CopyGameListToClipboard:
\r
4737 CopyGameListToClipboard();
\r
4740 /* [AS] Autodetect FEN or PGN data */
\r
4741 case IDM_PasteAny:
\r
4742 PasteGameOrFENFromClipboard();
\r
4745 /* [AS] Move history */
\r
4746 case IDM_ShowMoveHistory:
\r
4747 if( MoveHistoryIsUp() ) {
\r
4748 MoveHistoryPopDown();
\r
4751 MoveHistoryPopUp();
\r
4755 /* [AS] Eval graph */
\r
4756 case IDM_ShowEvalGraph:
\r
4757 if( EvalGraphIsUp() ) {
\r
4758 EvalGraphPopDown();
\r
4762 SetFocus(hwndMain);
\r
4766 /* [AS] Engine output */
\r
4767 case IDM_ShowEngineOutput:
\r
4768 if( EngineOutputIsUp() ) {
\r
4769 EngineOutputPopDown();
\r
4772 EngineOutputPopUp();
\r
4776 /* [AS] User adjudication */
\r
4777 case IDM_UserAdjudication_White:
\r
4778 UserAdjudicationEvent( +1 );
\r
4781 case IDM_UserAdjudication_Black:
\r
4782 UserAdjudicationEvent( -1 );
\r
4785 case IDM_UserAdjudication_Draw:
\r
4786 UserAdjudicationEvent( 0 );
\r
4789 /* [AS] Game list options dialog */
\r
4790 case IDM_GameListOptions:
\r
4791 GameListOptions();
\r
4798 case IDM_CopyPosition:
\r
4799 CopyFENToClipboard();
\r
4802 case IDM_PastePosition:
\r
4803 PasteFENFromClipboard();
\r
4806 case IDM_MailMove:
\r
4810 case IDM_ReloadCMailMsg:
\r
4811 Reset(TRUE, TRUE);
\r
4812 ReloadCmailMsgEvent(FALSE);
\r
4815 case IDM_Minimize:
\r
4816 ShowWindow(hwnd, SW_MINIMIZE);
\r
4823 case IDM_MachineWhite:
\r
4824 MachineWhiteEvent();
\r
4826 * refresh the tags dialog only if it's visible
\r
4828 if (gameMode == MachinePlaysWhite && IsWindowVisible(editTagsDialog)) {
\r
4830 tags = PGNTags(&gameInfo);
\r
4831 TagsPopUp(tags, CmailMsg());
\r
4834 SAY("computer starts playing white");
\r
4837 case IDM_MachineBlack:
\r
4838 MachineBlackEvent();
\r
4840 * refresh the tags dialog only if it's visible
\r
4842 if (gameMode == MachinePlaysBlack && IsWindowVisible(editTagsDialog)) {
\r
4844 tags = PGNTags(&gameInfo);
\r
4845 TagsPopUp(tags, CmailMsg());
\r
4848 SAY("computer starts playing black");
\r
4851 case IDM_Match: // [HGM] match: flows into next case, after setting Match Mode and nr of Games
\r
4852 if(gameMode != BeginningOfGame) { // allow menu item to remain enabled for better mode highligting
\r
4853 DisplayError(_("You can only start a match from the initial position."), 0); break;
\r
4855 matchMode = 2;// distinguish from command-line-triggered case (matchMode=1)
\r
4856 appData.matchGames = appData.defaultMatchGames;
\r
4858 first.matchWins = second.matchWins = 0;
\r
4860 case IDM_TwoMachines:
\r
4861 TwoMachinesEvent();
\r
4863 * refresh the tags dialog only if it's visible
\r
4865 if (gameMode == TwoMachinesPlay && IsWindowVisible(editTagsDialog)) {
\r
4867 tags = PGNTags(&gameInfo);
\r
4868 TagsPopUp(tags, CmailMsg());
\r
4871 SAY("computer starts playing both sides");
\r
4874 case IDM_AnalysisMode:
\r
4875 if (!first.analysisSupport) {
\r
4876 snprintf(buf, MSG_SIZ, _("%s does not support analysis"), first.tidy);
\r
4877 DisplayError(buf, 0);
\r
4879 SAY("analyzing current position");
\r
4880 /* [DM] icsEngineAnlyze [HGM] Why is this front-end??? */
\r
4881 if (appData.icsActive) {
\r
4882 if (gameMode != IcsObserving) {
\r
4883 snprintf(buf, MSG_SIZ, "You are not observing a game");
\r
4884 DisplayError(buf, 0);
\r
4885 /* secure check */
\r
4886 if (appData.icsEngineAnalyze) {
\r
4887 if (appData.debugMode)
\r
4888 fprintf(debugFP, "Found unexpected active ICS engine analyze \n");
\r
4889 ExitAnalyzeMode();
\r
4895 /* if enable, user want disable icsEngineAnalyze */
\r
4896 if (appData.icsEngineAnalyze) {
\r
4897 ExitAnalyzeMode();
\r
4901 appData.icsEngineAnalyze = TRUE;
\r
4902 if (appData.debugMode) fprintf(debugFP, "ICS engine analyze starting...\n");
\r
4905 if (!appData.showThinking) ToggleShowThinking();
\r
4906 AnalyzeModeEvent();
\r
4910 case IDM_AnalyzeFile:
\r
4911 if (!first.analysisSupport) {
\r
4912 char buf[MSG_SIZ];
\r
4913 snprintf(buf, MSG_SIZ, _("%s does not support analysis"), first.tidy);
\r
4914 DisplayError(buf, 0);
\r
4916 if (!appData.showThinking) ToggleShowThinking();
\r
4917 AnalyzeFileEvent();
\r
4918 LoadGameDialog(hwnd, _("Analyze Game from File"));
\r
4919 AnalysisPeriodicEvent(1);
\r
4923 case IDM_IcsClient:
\r
4927 case IDM_EditGame:
\r
4928 case IDM_EditGame2:
\r
4933 case IDM_EditPosition:
\r
4934 case IDM_EditPosition2:
\r
4935 EditPositionEvent();
\r
4936 SAY("enter a FEN string or setup a position on the board using the control R pop up menu");
\r
4939 case IDM_Training:
\r
4943 case IDM_ShowGameList:
\r
4944 ShowGameListProc();
\r
4947 case IDM_EditProgs1:
\r
4948 EditTagsPopUp(firstChessProgramNames, &firstChessProgramNames);
\r
4951 case IDM_EditProgs2:
\r
4952 EditTagsPopUp(secondChessProgramNames, &secondChessProgramNames);
\r
4955 case IDM_EditServers:
\r
4956 EditTagsPopUp(icsNames, &icsNames);
\r
4959 case IDM_EditTags:
\r
4964 case IDM_EditComment:
\r
4966 if (commentUp && editComment) {
\r
4969 EditCommentEvent();
\r
4989 case IDM_CallFlag:
\r
5009 case IDM_StopObserving:
\r
5010 StopObservingEvent();
\r
5013 case IDM_StopExamining:
\r
5014 StopExaminingEvent();
\r
5018 UploadGameEvent();
\r
5021 case IDM_TypeInMove:
\r
5022 PopUpMoveDialog('\000');
\r
5025 case IDM_TypeInName:
\r
5026 PopUpNameDialog('\000');
\r
5029 case IDM_Backward:
\r
5031 SetFocus(hwndMain);
\r
5038 SetFocus(hwndMain);
\r
5043 SetFocus(hwndMain);
\r
5048 SetFocus(hwndMain);
\r
5052 RevertEvent(FALSE);
\r
5055 case IDM_Annotate: // [HGM] vari: revert with annotation
\r
5056 RevertEvent(TRUE);
\r
5059 case IDM_TruncateGame:
\r
5060 TruncateGameEvent();
\r
5067 case IDM_RetractMove:
\r
5068 RetractMoveEvent();
\r
5071 case IDM_FlipView:
\r
5072 flipView = !flipView;
\r
5073 DrawPosition(FALSE, NULL);
\r
5076 case IDM_FlipClock:
\r
5077 flipClock = !flipClock;
\r
5078 DisplayBothClocks();
\r
5082 case IDM_MuteSounds:
\r
5083 mute = !mute; // [HGM] mute: keep track of global muting variable
\r
5084 CheckMenuItem(GetMenu(hwndMain),IDM_MuteSounds,
\r
5085 MF_BYCOMMAND|(mute?MF_CHECKED:MF_UNCHECKED));
\r
5088 case IDM_GeneralOptions:
\r
5089 GeneralOptionsPopup(hwnd);
\r
5090 DrawPosition(TRUE, NULL);
\r
5093 case IDM_BoardOptions:
\r
5094 BoardOptionsPopup(hwnd);
\r
5097 case IDM_EnginePlayOptions:
\r
5098 EnginePlayOptionsPopup(hwnd);
\r
5101 case IDM_Engine1Options:
\r
5102 EngineOptionsPopup(hwnd, &first);
\r
5105 case IDM_Engine2Options:
\r
5107 if(WaitForSecond(SettingsMenuIfReady)) break;
\r
5108 EngineOptionsPopup(hwnd, &second);
\r
5111 case IDM_OptionsUCI:
\r
5112 UciOptionsPopup(hwnd);
\r
5115 case IDM_IcsOptions:
\r
5116 IcsOptionsPopup(hwnd);
\r
5120 FontsOptionsPopup(hwnd);
\r
5124 SoundOptionsPopup(hwnd);
\r
5127 case IDM_CommPort:
\r
5128 CommPortOptionsPopup(hwnd);
\r
5131 case IDM_LoadOptions:
\r
5132 LoadOptionsPopup(hwnd);
\r
5135 case IDM_SaveOptions:
\r
5136 SaveOptionsPopup(hwnd);
\r
5139 case IDM_TimeControl:
\r
5140 TimeControlOptionsPopup(hwnd);
\r
5143 case IDM_SaveSettings:
\r
5144 SaveSettings(settingsFileName);
\r
5147 case IDM_SaveSettingsOnExit:
\r
5148 saveSettingsOnExit = !saveSettingsOnExit;
\r
5149 (void) CheckMenuItem(GetMenu(hwndMain), IDM_SaveSettingsOnExit,
\r
5150 MF_BYCOMMAND|(saveSettingsOnExit ?
\r
5151 MF_CHECKED : MF_UNCHECKED));
\r
5162 case IDM_AboutGame:
\r
5167 appData.debugMode = !appData.debugMode;
\r
5168 if (appData.debugMode) {
\r
5169 char dir[MSG_SIZ];
\r
5170 GetCurrentDirectory(MSG_SIZ, dir);
\r
5171 SetCurrentDirectory(installDir);
\r
5172 debugFP = fopen(appData.nameOfDebugFile, "w");
\r
5173 SetCurrentDirectory(dir);
\r
5174 setbuf(debugFP, NULL);
\r
5181 case IDM_HELPCONTENTS:
\r
5182 if (!MyHelp (hwnd, "winboard.hlp", HELP_KEY,(DWORD)(LPSTR)"CONTENTS") &&
\r
5183 !HtmlHelp(hwnd, "winboard.chm", 0, 0) ) {
\r
5184 MessageBox (GetFocus(),
\r
5185 _("Unable to activate help"),
\r
5186 szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);
\r
5190 case IDM_HELPSEARCH:
\r
5191 if (!MyHelp (hwnd, "winboard.hlp", HELP_PARTIALKEY, (DWORD)(LPSTR)"") &&
\r
5192 !HtmlHelp(hwnd, "winboard.chm", 0, 0) ) {
\r
5193 MessageBox (GetFocus(),
\r
5194 _("Unable to activate help"),
\r
5195 szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);
\r
5199 case IDM_HELPHELP:
\r
5200 if(!WinHelp(hwnd, (LPSTR)NULL, HELP_HELPONHELP, 0)) {
\r
5201 MessageBox (GetFocus(),
\r
5202 _("Unable to activate help"),
\r
5203 szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);
\r
5208 lpProc = MakeProcInstance((FARPROC)About, hInst);
\r
5210 (gameInfo.event && strcmp(gameInfo.event, "Easter Egg Hunt") == 0) ?
\r
5211 "AboutBox2" : "AboutBox", hwnd, (DLGPROC)lpProc);
\r
5212 FreeProcInstance(lpProc);
\r
5215 case IDM_DirectCommand1:
\r
5216 AskQuestionEvent(_("Direct Command"),
\r
5217 _("Send to chess program:"), "", "1");
\r
5219 case IDM_DirectCommand2:
\r
5220 AskQuestionEvent(_("Direct Command"),
\r
5221 _("Send to second chess program:"), "", "2");
\r
5224 case EP_WhitePawn:
\r
5225 EditPositionMenuEvent(WhitePawn, fromX, fromY);
\r
5226 fromX = fromY = -1;
\r
5229 case EP_WhiteKnight:
\r
5230 EditPositionMenuEvent(WhiteKnight, fromX, fromY);
\r
5231 fromX = fromY = -1;
\r
5234 case EP_WhiteBishop:
\r
5235 EditPositionMenuEvent(WhiteBishop, fromX, fromY);
\r
5236 fromX = fromY = -1;
\r
5239 case EP_WhiteRook:
\r
5240 EditPositionMenuEvent(WhiteRook, fromX, fromY);
\r
5241 fromX = fromY = -1;
\r
5244 case EP_WhiteQueen:
\r
5245 EditPositionMenuEvent(WhiteQueen, fromX, fromY);
\r
5246 fromX = fromY = -1;
\r
5249 case EP_WhiteFerz:
\r
5250 EditPositionMenuEvent(WhiteFerz, fromX, fromY);
\r
5251 fromX = fromY = -1;
\r
5254 case EP_WhiteWazir:
\r
5255 EditPositionMenuEvent(WhiteWazir, fromX, fromY);
\r
5256 fromX = fromY = -1;
\r
5259 case EP_WhiteAlfil:
\r
5260 EditPositionMenuEvent(WhiteAlfil, fromX, fromY);
\r
5261 fromX = fromY = -1;
\r
5264 case EP_WhiteCannon:
\r
5265 EditPositionMenuEvent(WhiteCannon, fromX, fromY);
\r
5266 fromX = fromY = -1;
\r
5269 case EP_WhiteCardinal:
\r
5270 EditPositionMenuEvent(WhiteAngel, fromX, fromY);
\r
5271 fromX = fromY = -1;
\r
5274 case EP_WhiteMarshall:
\r
5275 EditPositionMenuEvent(WhiteMarshall, fromX, fromY);
\r
5276 fromX = fromY = -1;
\r
5279 case EP_WhiteKing:
\r
5280 EditPositionMenuEvent(WhiteKing, fromX, fromY);
\r
5281 fromX = fromY = -1;
\r
5284 case EP_BlackPawn:
\r
5285 EditPositionMenuEvent(BlackPawn, fromX, fromY);
\r
5286 fromX = fromY = -1;
\r
5289 case EP_BlackKnight:
\r
5290 EditPositionMenuEvent(BlackKnight, fromX, fromY);
\r
5291 fromX = fromY = -1;
\r
5294 case EP_BlackBishop:
\r
5295 EditPositionMenuEvent(BlackBishop, fromX, fromY);
\r
5296 fromX = fromY = -1;
\r
5299 case EP_BlackRook:
\r
5300 EditPositionMenuEvent(BlackRook, fromX, fromY);
\r
5301 fromX = fromY = -1;
\r
5304 case EP_BlackQueen:
\r
5305 EditPositionMenuEvent(BlackQueen, fromX, fromY);
\r
5306 fromX = fromY = -1;
\r
5309 case EP_BlackFerz:
\r
5310 EditPositionMenuEvent(BlackFerz, fromX, fromY);
\r
5311 fromX = fromY = -1;
\r
5314 case EP_BlackWazir:
\r
5315 EditPositionMenuEvent(BlackWazir, fromX, fromY);
\r
5316 fromX = fromY = -1;
\r
5319 case EP_BlackAlfil:
\r
5320 EditPositionMenuEvent(BlackAlfil, fromX, fromY);
\r
5321 fromX = fromY = -1;
\r
5324 case EP_BlackCannon:
\r
5325 EditPositionMenuEvent(BlackCannon, fromX, fromY);
\r
5326 fromX = fromY = -1;
\r
5329 case EP_BlackCardinal:
\r
5330 EditPositionMenuEvent(BlackAngel, fromX, fromY);
\r
5331 fromX = fromY = -1;
\r
5334 case EP_BlackMarshall:
\r
5335 EditPositionMenuEvent(BlackMarshall, fromX, fromY);
\r
5336 fromX = fromY = -1;
\r
5339 case EP_BlackKing:
\r
5340 EditPositionMenuEvent(BlackKing, fromX, fromY);
\r
5341 fromX = fromY = -1;
\r
5344 case EP_EmptySquare:
\r
5345 EditPositionMenuEvent(EmptySquare, fromX, fromY);
\r
5346 fromX = fromY = -1;
\r
5349 case EP_ClearBoard:
\r
5350 EditPositionMenuEvent(ClearBoard, fromX, fromY);
\r
5351 fromX = fromY = -1;
\r
5355 EditPositionMenuEvent(WhitePlay, fromX, fromY);
\r
5356 fromX = fromY = -1;
\r
5360 EditPositionMenuEvent(BlackPlay, fromX, fromY);
\r
5361 fromX = fromY = -1;
\r
5365 EditPositionMenuEvent(PromotePiece, fromX, fromY);
\r
5366 fromX = fromY = -1;
\r
5370 EditPositionMenuEvent(DemotePiece, fromX, fromY);
\r
5371 fromX = fromY = -1;
\r
5375 DropMenuEvent(WhitePawn, fromX, fromY);
\r
5376 fromX = fromY = -1;
\r
5380 DropMenuEvent(WhiteKnight, fromX, fromY);
\r
5381 fromX = fromY = -1;
\r
5385 DropMenuEvent(WhiteBishop, fromX, fromY);
\r
5386 fromX = fromY = -1;
\r
5390 DropMenuEvent(WhiteRook, fromX, fromY);
\r
5391 fromX = fromY = -1;
\r
5395 DropMenuEvent(WhiteQueen, fromX, fromY);
\r
5396 fromX = fromY = -1;
\r
5401 TranslateMenus(0);
\r
5402 CheckMenuItem(GetMenu(hwndMain), lastChecked, MF_BYCOMMAND|MF_UNCHECKED);
\r
5403 CheckMenuItem(GetMenu(hwndMain), IDM_English, MF_BYCOMMAND|MF_CHECKED);
\r
5404 lastChecked = wmId;
\r
5408 if(wmId > IDM_English && wmId < IDM_English+5) {
\r
5409 LoadLanguageFile(languageFile[wmId - IDM_English - 1]);
\r
5410 TranslateMenus(0);
\r
5411 CheckMenuItem(GetMenu(hwndMain), lastChecked, MF_BYCOMMAND|MF_UNCHECKED);
\r
5412 CheckMenuItem(GetMenu(hwndMain), wmId, MF_BYCOMMAND|MF_CHECKED);
\r
5413 lastChecked = wmId;
\r
5416 return (DefWindowProc(hwnd, message, wParam, lParam));
\r
5422 case CLOCK_TIMER_ID:
\r
5423 KillTimer(hwnd, clockTimerEvent); /* Simulate one-shot timer as in X */
\r
5424 clockTimerEvent = 0;
\r
5425 DecrementClocks(); /* call into back end */
\r
5427 case LOAD_GAME_TIMER_ID:
\r
5428 KillTimer(hwnd, loadGameTimerEvent); /* Simulate one-shot timer as in X*/
\r
5429 loadGameTimerEvent = 0;
\r
5430 AutoPlayGameLoop(); /* call into back end */
\r
5432 case ANALYSIS_TIMER_ID:
\r
5433 if ((gameMode == AnalyzeMode || gameMode == AnalyzeFile
\r
5434 || appData.icsEngineAnalyze) && appData.periodicUpdates) {
\r
5435 AnalysisPeriodicEvent(0);
\r
5437 KillTimer(hwnd, analysisTimerEvent);
\r
5438 analysisTimerEvent = 0;
\r
5441 case DELAYED_TIMER_ID:
\r
5442 KillTimer(hwnd, delayedTimerEvent);
\r
5443 delayedTimerEvent = 0;
\r
5444 delayedTimerCallback();
\r
5449 case WM_USER_Input:
\r
5450 InputEvent(hwnd, message, wParam, lParam);
\r
5453 /* [AS] Also move "attached" child windows */
\r
5454 case WM_WINDOWPOSCHANGING:
\r
5456 if( hwnd == hwndMain && appData.useStickyWindows ) {
\r
5457 LPWINDOWPOS lpwp = (LPWINDOWPOS) lParam;
\r
5459 if( ((lpwp->flags & SWP_NOMOVE) == 0) && ((lpwp->flags & SWP_NOSIZE) != 0) ) {
\r
5460 /* Window is moving */
\r
5463 // GetWindowRect( hwnd, &rcMain ); //[HGM] sticky: in XP this returned new position, not old
\r
5464 rcMain.left = wpMain.x; // replace by these 4 lines to reconstruct old rect
\r
5465 rcMain.right = wpMain.x + wpMain.width;
\r
5466 rcMain.top = wpMain.y;
\r
5467 rcMain.bottom = wpMain.y + wpMain.height;
\r
5469 ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, moveHistoryDialog, &wpMoveHistory );
\r
5470 ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, evalGraphDialog, &wpEvalGraph );
\r
5471 ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, engineOutputDialog, &wpEngineOutput );
\r
5472 ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, gameListDialog, &wpGameList );
\r
5473 ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, hwndConsole, &wpConsole );
\r
5474 wpMain.x = lpwp->x;
\r
5475 wpMain.y = lpwp->y;
\r
5480 /* [AS] Snapping */
\r
5481 case WM_ENTERSIZEMOVE:
\r
5482 if(appData.debugMode) { fprintf(debugFP, "size-move\n"); }
\r
5483 if (hwnd == hwndMain) {
\r
5484 doingSizing = TRUE;
\r
5487 return OnEnterSizeMove( &sd, hwnd, wParam, lParam );
\r
5491 if(appData.debugMode) { fprintf(debugFP, "sizing\n"); }
\r
5492 if (hwnd == hwndMain) {
\r
5493 lastSizing = wParam;
\r
5498 if(appData.debugMode) { fprintf(debugFP, "moving\n"); }
\r
5499 return OnMoving( &sd, hwnd, wParam, lParam );
\r
5501 case WM_EXITSIZEMOVE:
\r
5502 if(appData.debugMode) { fprintf(debugFP, "exit size-move, size = %d\n", squareSize); }
\r
5503 if (hwnd == hwndMain) {
\r
5505 doingSizing = FALSE;
\r
5506 InvalidateRect(hwnd, &boardRect, FALSE);
\r
5507 GetClientRect(hwnd, &client);
\r
5508 ResizeBoard(client.right, client.bottom, lastSizing);
\r
5510 if(appData.debugMode) { fprintf(debugFP, "square size = %d\n", squareSize); }
\r
5512 return OnExitSizeMove( &sd, hwnd, wParam, lParam );
\r
5515 case WM_DESTROY: /* message: window being destroyed */
\r
5516 PostQuitMessage(0);
\r
5520 if (hwnd == hwndMain) {
\r
5525 default: /* Passes it on if unprocessed */
\r
5526 return (DefWindowProc(hwnd, message, wParam, lParam));
\r
5531 /*---------------------------------------------------------------------------*\
\r
5533 * Misc utility routines
\r
5535 \*---------------------------------------------------------------------------*/
\r
5538 * Decent random number generator, at least not as bad as Windows
\r
5539 * standard rand, which returns a value in the range 0 to 0x7fff.
\r
5541 unsigned int randstate;
\r
5546 randstate = randstate * 1664525 + 1013904223;
\r
5547 return (int) randstate & 0x7fffffff;
\r
5551 mysrandom(unsigned int seed)
\r
5558 * returns TRUE if user selects a different color, FALSE otherwise
\r
5562 ChangeColor(HWND hwnd, COLORREF *which)
\r
5564 static BOOL firstTime = TRUE;
\r
5565 static DWORD customColors[16];
\r
5567 COLORREF newcolor;
\r
5572 /* Make initial colors in use available as custom colors */
\r
5573 /* Should we put the compiled-in defaults here instead? */
\r
5575 customColors[i++] = lightSquareColor & 0xffffff;
\r
5576 customColors[i++] = darkSquareColor & 0xffffff;
\r
5577 customColors[i++] = whitePieceColor & 0xffffff;
\r
5578 customColors[i++] = blackPieceColor & 0xffffff;
\r
5579 customColors[i++] = highlightSquareColor & 0xffffff;
\r
5580 customColors[i++] = premoveHighlightColor & 0xffffff;
\r
5582 for (ccl = (ColorClass) 0; ccl < NColorClasses && i < 16; ccl++) {
\r
5583 customColors[i++] = textAttribs[ccl].color;
\r
5585 while (i < 16) customColors[i++] = RGB(255, 255, 255);
\r
5586 firstTime = FALSE;
\r
5589 cc.lStructSize = sizeof(cc);
\r
5590 cc.hwndOwner = hwnd;
\r
5591 cc.hInstance = NULL;
\r
5592 cc.rgbResult = (DWORD) (*which & 0xffffff);
\r
5593 cc.lpCustColors = (LPDWORD) customColors;
\r
5594 cc.Flags = CC_RGBINIT|CC_FULLOPEN;
\r
5596 if (!ChooseColor(&cc)) return FALSE;
\r
5598 newcolor = (COLORREF) (0x2000000 | cc.rgbResult);
\r
5599 if (newcolor == *which) return FALSE;
\r
5600 *which = newcolor;
\r
5604 InitDrawingColors();
\r
5605 InvalidateRect(hwnd, &boardRect, FALSE);
\r
5610 MyLoadSound(MySound *ms)
\r
5616 if (ms->data) free(ms->data);
\r
5619 switch (ms->name[0]) {
\r
5625 /* System sound from Control Panel. Don't preload here. */
\r
5629 if (ms->name[1] == NULLCHAR) {
\r
5630 /* "!" alone = silence */
\r
5633 /* Builtin wave resource. Error if not found. */
\r
5634 HANDLE h = FindResource(hInst, ms->name + 1, "WAVE");
\r
5635 if (h == NULL) break;
\r
5636 ms->data = (void *)LoadResource(hInst, h);
\r
5637 if (h == NULL) break;
\r
5642 /* .wav file. Error if not found. */
\r
5643 f = fopen(ms->name, "rb");
\r
5644 if (f == NULL) break;
\r
5645 if (fstat(fileno(f), &st) < 0) break;
\r
5646 ms->data = malloc(st.st_size);
\r
5647 if (fread(ms->data, st.st_size, 1, f) < 1) break;
\r
5653 char buf[MSG_SIZ];
\r
5654 snprintf(buf, MSG_SIZ, _("Error loading sound %s"), ms->name);
\r
5655 DisplayError(buf, GetLastError());
\r
5661 MyPlaySound(MySound *ms)
\r
5663 BOOLEAN ok = FALSE;
\r
5665 if(mute) return TRUE; // [HGM] mute: suppress all sound play when muted
\r
5666 switch (ms->name[0]) {
\r
5668 if(appData.debugMode) fprintf(debugFP, "silence\n");
\r
5673 /* System sound from Control Panel (deprecated feature).
\r
5674 "$" alone or an unset sound name gets default beep (still in use). */
\r
5675 if (ms->name[1]) {
\r
5676 ok = PlaySound(ms->name + 1, NULL, SND_ALIAS|SND_ASYNC);
\r
5678 if (!ok) ok = MessageBeep(MB_OK);
\r
5681 /* Builtin wave resource, or "!" alone for silence */
\r
5682 if (ms->name[1]) {
\r
5683 if (ms->data == NULL) return FALSE;
\r
5684 ok = PlaySound(ms->data, NULL, SND_MEMORY|SND_ASYNC);
\r
5690 /* .wav file. Error if not found. */
\r
5691 if (ms->data == NULL) return FALSE;
\r
5692 ok = PlaySound(ms->data, NULL, SND_MEMORY|SND_ASYNC);
\r
5695 /* Don't print an error: this can happen innocently if the sound driver
\r
5696 is busy; for instance, if another instance of WinBoard is playing
\r
5697 a sound at about the same time. */
\r
5703 OldOpenFileHook(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
5706 OPENFILENAME *ofn;
\r
5707 static UINT *number; /* gross that this is static */
\r
5709 switch (message) {
\r
5710 case WM_INITDIALOG: /* message: initialize dialog box */
\r
5711 /* Center the dialog over the application window */
\r
5712 ofn = (OPENFILENAME *) lParam;
\r
5713 if (ofn->Flags & OFN_ENABLETEMPLATE) {
\r
5714 number = (UINT *) ofn->lCustData;
\r
5715 SendMessage(GetDlgItem(hDlg, edt2), WM_SETTEXT, 0, (LPARAM) "");
\r
5719 CenterWindow (hDlg, GetWindow (hDlg, GW_OWNER));
\r
5720 Translate(hDlg, 1536);
\r
5721 return FALSE; /* Allow for further processing */
\r
5724 if ((LOWORD(wParam) == IDOK) && (number != NULL)) {
\r
5725 *number = GetDlgItemInt(hDlg, OPT_IndexNumberOld, &ok, FALSE);
\r
5727 return FALSE; /* Allow for further processing */
\r
5733 OpenFileHook(HWND hdlg, UINT uiMsg, WPARAM wParam, LPARAM lParam)
\r
5735 static UINT *number;
\r
5736 OPENFILENAME *ofname;
\r
5739 case WM_INITDIALOG:
\r
5740 Translate(hdlg, DLG_IndexNumber);
\r
5741 ofname = (OPENFILENAME *)lParam;
\r
5742 number = (UINT *)(ofname->lCustData);
\r
5745 ofnot = (OFNOTIFY *)lParam;
\r
5746 if (ofnot->hdr.code == CDN_FILEOK) {
\r
5747 *number = GetDlgItemInt(hdlg, OPT_IndexNumber, NULL, FALSE);
\r
5756 OpenFileDialog(HWND hwnd, char *write, char *defName, char *defExt, // [HGM] diag: type of 'write' now string
\r
5757 char *nameFilt, char *dlgTitle, UINT *number,
\r
5758 char fileTitle[MSG_SIZ], char fileName[MSG_SIZ])
\r
5760 OPENFILENAME openFileName;
\r
5761 char buf1[MSG_SIZ];
\r
5764 if (fileName == NULL) fileName = buf1;
\r
5765 if (defName == NULL) {
\r
5766 safeStrCpy(fileName, "*.", 3 );
\r
5767 strcat(fileName, defExt);
\r
5769 safeStrCpy(fileName, defName, MSG_SIZ );
\r
5771 if (fileTitle) safeStrCpy(fileTitle, "", MSG_SIZ );
\r
5772 if (number) *number = 0;
\r
5774 openFileName.lStructSize = sizeof(OPENFILENAME);
\r
5775 openFileName.hwndOwner = hwnd;
\r
5776 openFileName.hInstance = (HANDLE) hInst;
\r
5777 openFileName.lpstrFilter = nameFilt;
\r
5778 openFileName.lpstrCustomFilter = (LPSTR) NULL;
\r
5779 openFileName.nMaxCustFilter = 0L;
\r
5780 openFileName.nFilterIndex = 1L;
\r
5781 openFileName.lpstrFile = fileName;
\r
5782 openFileName.nMaxFile = MSG_SIZ;
\r
5783 openFileName.lpstrFileTitle = fileTitle;
\r
5784 openFileName.nMaxFileTitle = fileTitle ? MSG_SIZ : 0;
\r
5785 openFileName.lpstrInitialDir = NULL;
\r
5786 openFileName.lpstrTitle = dlgTitle;
\r
5787 openFileName.Flags = OFN_PATHMUSTEXIST | OFN_HIDEREADONLY
\r
5788 | (write[0] != 'r' ? 0 : OFN_FILEMUSTEXIST)
\r
5789 | (number ? OFN_ENABLETEMPLATE | OFN_ENABLEHOOK: 0)
\r
5790 | (oldDialog ? 0 : OFN_EXPLORER);
\r
5791 openFileName.nFileOffset = 0;
\r
5792 openFileName.nFileExtension = 0;
\r
5793 openFileName.lpstrDefExt = defExt;
\r
5794 openFileName.lCustData = (LONG) number;
\r
5795 openFileName.lpfnHook = oldDialog ?
\r
5796 (LPOFNHOOKPROC) OldOpenFileHook : (LPOFNHOOKPROC) OpenFileHook;
\r
5797 openFileName.lpTemplateName = (LPSTR)(oldDialog ? 1536 : DLG_IndexNumber);
\r
5799 if (write[0] != 'r' ? GetSaveFileName(&openFileName) :
\r
5800 GetOpenFileName(&openFileName)) {
\r
5801 /* open the file */
\r
5802 f = fopen(openFileName.lpstrFile, write);
\r
5804 MessageBox(hwnd, _("File open failed"), NULL,
\r
5805 MB_OK|MB_ICONEXCLAMATION);
\r
5809 int err = CommDlgExtendedError();
\r
5810 if (err != 0) DisplayError(_("Internal error in file dialog box"), err);
\r
5819 MenuPopup(HWND hwnd, POINT pt, HMENU hmenu, UINT def)
\r
5821 HMENU hmenuTrackPopup; /* floating pop-up menu */
\r
5824 * Get the first pop-up menu in the menu template. This is the
\r
5825 * menu that TrackPopupMenu displays.
\r
5827 hmenuTrackPopup = GetSubMenu(hmenu, 0);
\r
5828 TranslateOneMenu(10, hmenuTrackPopup);
\r
5830 SetMenuDefaultItem(hmenuTrackPopup, def, FALSE);
\r
5833 * TrackPopup uses screen coordinates, so convert the
\r
5834 * coordinates of the mouse click to screen coordinates.
\r
5836 ClientToScreen(hwnd, (LPPOINT) &pt);
\r
5838 /* Draw and track the floating pop-up menu. */
\r
5839 TrackPopupMenu(hmenuTrackPopup, TPM_CENTERALIGN | TPM_RIGHTBUTTON,
\r
5840 pt.x, pt.y, 0, hwnd, NULL);
\r
5842 /* Destroy the menu.*/
\r
5843 DestroyMenu(hmenu);
\r
5848 int sizeX, sizeY, newSizeX, newSizeY;
\r
5850 } ResizeEditPlusButtonsClosure;
\r
5853 ResizeEditPlusButtonsCallback(HWND hChild, LPARAM lparam)
\r
5855 ResizeEditPlusButtonsClosure *cl = (ResizeEditPlusButtonsClosure *)lparam;
\r
5859 if (hChild == cl->hText) return TRUE;
\r
5860 GetWindowRect(hChild, &rect); /* gives screen coords */
\r
5861 pt.x = rect.left + (cl->newSizeX - cl->sizeX)/2;
\r
5862 pt.y = rect.top + cl->newSizeY - cl->sizeY;
\r
5863 ScreenToClient(cl->hDlg, &pt);
\r
5864 cl->hdwp = DeferWindowPos(cl->hdwp, hChild, NULL,
\r
5865 pt.x, pt.y, rect.right - rect.left, rect.bottom - rect.top, SWP_NOZORDER);
\r
5869 /* Resize a dialog that has a (rich) edit field filling most of
\r
5870 the top, with a row of buttons below */
\r
5872 ResizeEditPlusButtons(HWND hDlg, HWND hText, int sizeX, int sizeY, int newSizeX, int newSizeY)
\r
5875 int newTextHeight, newTextWidth;
\r
5876 ResizeEditPlusButtonsClosure cl;
\r
5878 /*if (IsIconic(hDlg)) return;*/
\r
5879 if (newSizeX == sizeX && newSizeY == sizeY) return;
\r
5881 cl.hdwp = BeginDeferWindowPos(8);
\r
5883 GetWindowRect(hText, &rectText); /* gives screen coords */
\r
5884 newTextWidth = rectText.right - rectText.left + newSizeX - sizeX;
\r
5885 newTextHeight = rectText.bottom - rectText.top + newSizeY - sizeY;
\r
5886 if (newTextHeight < 0) {
\r
5887 newSizeY += -newTextHeight;
\r
5888 newTextHeight = 0;
\r
5890 cl.hdwp = DeferWindowPos(cl.hdwp, hText, NULL, 0, 0,
\r
5891 newTextWidth, newTextHeight, SWP_NOZORDER|SWP_NOMOVE);
\r
5897 cl.newSizeX = newSizeX;
\r
5898 cl.newSizeY = newSizeY;
\r
5899 EnumChildWindows(hDlg, ResizeEditPlusButtonsCallback, (LPARAM)&cl);
\r
5901 EndDeferWindowPos(cl.hdwp);
\r
5904 BOOL CenterWindowEx(HWND hwndChild, HWND hwndParent, int mode)
\r
5906 RECT rChild, rParent;
\r
5907 int wChild, hChild, wParent, hParent;
\r
5908 int wScreen, hScreen, xNew, yNew;
\r
5911 /* Get the Height and Width of the child window */
\r
5912 GetWindowRect (hwndChild, &rChild);
\r
5913 wChild = rChild.right - rChild.left;
\r
5914 hChild = rChild.bottom - rChild.top;
\r
5916 /* Get the Height and Width of the parent window */
\r
5917 GetWindowRect (hwndParent, &rParent);
\r
5918 wParent = rParent.right - rParent.left;
\r
5919 hParent = rParent.bottom - rParent.top;
\r
5921 /* Get the display limits */
\r
5922 hdc = GetDC (hwndChild);
\r
5923 wScreen = GetDeviceCaps (hdc, HORZRES);
\r
5924 hScreen = GetDeviceCaps (hdc, VERTRES);
\r
5925 ReleaseDC(hwndChild, hdc);
\r
5927 /* Calculate new X position, then adjust for screen */
\r
5928 xNew = rParent.left + ((wParent - wChild) /2);
\r
5931 } else if ((xNew+wChild) > wScreen) {
\r
5932 xNew = wScreen - wChild;
\r
5935 /* Calculate new Y position, then adjust for screen */
\r
5937 yNew = rParent.top + ((hParent - hChild) /2);
\r
5940 yNew = rParent.top + GetSystemMetrics( SM_CYCAPTION ) * 2 / 3;
\r
5945 } else if ((yNew+hChild) > hScreen) {
\r
5946 yNew = hScreen - hChild;
\r
5949 /* Set it, and return */
\r
5950 return SetWindowPos (hwndChild, NULL,
\r
5951 xNew, yNew, 0, 0, SWP_NOSIZE | SWP_NOZORDER);
\r
5954 /* Center one window over another */
\r
5955 BOOL CenterWindow (HWND hwndChild, HWND hwndParent)
\r
5957 return CenterWindowEx( hwndChild, hwndParent, 0 );
\r
5960 /*---------------------------------------------------------------------------*\
\r
5962 * Startup Dialog functions
\r
5964 \*---------------------------------------------------------------------------*/
\r
5966 InitComboStrings(HANDLE hwndCombo, char **cd)
\r
5968 SendMessage(hwndCombo, CB_RESETCONTENT, 0, 0);
\r
5970 while (*cd != NULL) {
\r
5971 SendMessage(hwndCombo, CB_ADDSTRING, 0, (LPARAM) T_(*cd));
\r
5977 InitComboStringsFromOption(HANDLE hwndCombo, char *str)
\r
5979 char buf1[MAX_ARG_LEN];
\r
5982 if (str[0] == '@') {
\r
5983 FILE* f = fopen(str + 1, "r");
\r
5985 DisplayFatalError(str + 1, errno, 2);
\r
5988 len = fread(buf1, 1, sizeof(buf1)-1, f);
\r
5990 buf1[len] = NULLCHAR;
\r
5994 SendMessage(hwndCombo, CB_RESETCONTENT, 0, 0);
\r
5997 char buf[MSG_SIZ];
\r
5998 char *end = strchr(str, '\n');
\r
5999 if (end == NULL) return;
\r
6000 memcpy(buf, str, end - str);
\r
6001 buf[end - str] = NULLCHAR;
\r
6002 SendMessage(hwndCombo, CB_ADDSTRING, 0, (LPARAM) buf);
\r
6008 SetStartupDialogEnables(HWND hDlg)
\r
6010 EnableWindow(GetDlgItem(hDlg, OPT_ChessEngineName),
\r
6011 IsDlgButtonChecked(hDlg, OPT_ChessEngine) ||
\r
6012 (appData.zippyPlay && IsDlgButtonChecked(hDlg, OPT_ChessServer)));
\r
6013 EnableWindow(GetDlgItem(hDlg, OPT_SecondChessEngineName),
\r
6014 IsDlgButtonChecked(hDlg, OPT_ChessEngine));
\r
6015 EnableWindow(GetDlgItem(hDlg, OPT_ChessServerName),
\r
6016 IsDlgButtonChecked(hDlg, OPT_ChessServer));
\r
6017 EnableWindow(GetDlgItem(hDlg, OPT_AdditionalOptions),
\r
6018 IsDlgButtonChecked(hDlg, OPT_AnyAdditional));
\r
6019 EnableWindow(GetDlgItem(hDlg, IDOK),
\r
6020 IsDlgButtonChecked(hDlg, OPT_ChessEngine) ||
\r
6021 IsDlgButtonChecked(hDlg, OPT_ChessServer) ||
\r
6022 IsDlgButtonChecked(hDlg, OPT_View));
\r
6026 QuoteForFilename(char *filename)
\r
6028 int dquote, space;
\r
6029 dquote = strchr(filename, '"') != NULL;
\r
6030 space = strchr(filename, ' ') != NULL;
\r
6031 if (dquote || space) {
\r
6043 InitEngineBox(HWND hDlg, HWND hwndCombo, char* nthcp, char* nthd, char* nthdir, char *nthnames)
\r
6045 char buf[MSG_SIZ];
\r
6048 InitComboStringsFromOption(hwndCombo, nthnames);
\r
6049 q = QuoteForFilename(nthcp);
\r
6050 snprintf(buf, MSG_SIZ, "%s%s%s", q, nthcp, q);
\r
6051 if (*nthdir != NULLCHAR) {
\r
6052 q = QuoteForFilename(nthdir);
\r
6053 snprintf(buf + strlen(buf), MSG_SIZ, " /%s=%s%s%s", nthd, q, nthdir, q);
\r
6055 if (*nthcp == NULLCHAR) {
\r
6056 SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) 0, (LPARAM) 0);
\r
6057 } else if (SendMessage(hwndCombo, CB_SELECTSTRING, (WPARAM) -1, (LPARAM) buf) == CB_ERR) {
\r
6058 SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) -1, (LPARAM) 0);
\r
6059 SendMessage(hwndCombo, WM_SETTEXT, (WPARAM) 0, (LPARAM) buf);
\r
6064 StartupDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
6066 char buf[MSG_SIZ];
\r
6070 switch (message) {
\r
6071 case WM_INITDIALOG:
\r
6072 /* Center the dialog */
\r
6073 CenterWindow (hDlg, GetDesktopWindow());
\r
6074 Translate(hDlg, DLG_Startup);
\r
6075 /* Initialize the dialog items */
\r
6076 InitEngineBox(hDlg, GetDlgItem(hDlg, OPT_ChessEngineName),
\r
6077 appData.firstChessProgram, "fd", appData.firstDirectory,
\r
6078 firstChessProgramNames);
\r
6079 InitEngineBox(hDlg, GetDlgItem(hDlg, OPT_SecondChessEngineName),
\r
6080 appData.secondChessProgram, "sd", appData.secondDirectory,
\r
6081 secondChessProgramNames);
\r
6082 hwndCombo = GetDlgItem(hDlg, OPT_ChessServerName);
\r
6083 InitComboStringsFromOption(hwndCombo, icsNames);
\r
6084 snprintf(buf, MSG_SIZ, "%s /icsport=%s", appData.icsHost, appData.icsPort);
\r
6085 if (*appData.icsHelper != NULLCHAR) {
\r
6086 char *q = QuoteForFilename(appData.icsHelper);
\r
6087 sprintf(buf + strlen(buf), " /icshelper=%s%s%s", q, appData.icsHelper, q);
\r
6089 if (*appData.icsHost == NULLCHAR) {
\r
6090 SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) 0, (LPARAM) 0);
\r
6091 /*SendMessage(hwndCombo, CB_SHOWDROPDOWN, (WPARAM) TRUE, (LPARAM) 0); !!too soon */
\r
6092 } else if (SendMessage(hwndCombo, CB_SELECTSTRING, (WPARAM) -1, (LPARAM) buf) == CB_ERR) {
\r
6093 SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) -1, (LPARAM) 0);
\r
6094 SendMessage(hwndCombo, WM_SETTEXT, (WPARAM) 0, (LPARAM) buf);
\r
6097 if (appData.icsActive) {
\r
6098 CheckDlgButton(hDlg, OPT_ChessServer, BST_CHECKED);
\r
6100 else if (appData.noChessProgram) {
\r
6101 CheckDlgButton(hDlg, OPT_View, BST_CHECKED);
\r
6104 CheckDlgButton(hDlg, OPT_ChessEngine, BST_CHECKED);
\r
6107 SetStartupDialogEnables(hDlg);
\r
6111 switch (LOWORD(wParam)) {
\r
6113 if (IsDlgButtonChecked(hDlg, OPT_ChessEngine)) {
\r
6114 safeStrCpy(buf, "/fcp=", sizeof(buf)/sizeof(buf[0]) );
\r
6115 GetDlgItemText(hDlg, OPT_ChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));
\r
6117 ParseArgs(StringGet, &p);
\r
6118 safeStrCpy(buf, "/scp=", sizeof(buf)/sizeof(buf[0]) );
\r
6119 GetDlgItemText(hDlg, OPT_SecondChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));
\r
6121 ParseArgs(StringGet, &p);
\r
6122 appData.noChessProgram = FALSE;
\r
6123 appData.icsActive = FALSE;
\r
6124 } else if (IsDlgButtonChecked(hDlg, OPT_ChessServer)) {
\r
6125 safeStrCpy(buf, "/ics /icshost=", sizeof(buf)/sizeof(buf[0]) );
\r
6126 GetDlgItemText(hDlg, OPT_ChessServerName, buf + strlen(buf), sizeof(buf) - strlen(buf));
\r
6128 ParseArgs(StringGet, &p);
\r
6129 if (appData.zippyPlay) {
\r
6130 safeStrCpy(buf, "/fcp=", sizeof(buf)/sizeof(buf[0]) );
\r
6131 GetDlgItemText(hDlg, OPT_ChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));
\r
6133 ParseArgs(StringGet, &p);
\r
6135 } else if (IsDlgButtonChecked(hDlg, OPT_View)) {
\r
6136 appData.noChessProgram = TRUE;
\r
6137 appData.icsActive = FALSE;
\r
6139 MessageBox(hDlg, _("Choose an option, or cancel to exit"),
\r
6140 _("Option Error"), MB_OK|MB_ICONEXCLAMATION);
\r
6143 if (IsDlgButtonChecked(hDlg, OPT_AnyAdditional)) {
\r
6144 GetDlgItemText(hDlg, OPT_AdditionalOptions, buf, sizeof(buf));
\r
6146 ParseArgs(StringGet, &p);
\r
6148 EndDialog(hDlg, TRUE);
\r
6155 case IDM_HELPCONTENTS:
\r
6156 if (!WinHelp (hDlg, "winboard.hlp", HELP_KEY,(DWORD)(LPSTR)"CONTENTS")) {
\r
6157 MessageBox (GetFocus(),
\r
6158 _("Unable to activate help"),
\r
6159 szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);
\r
6164 SetStartupDialogEnables(hDlg);
\r
6172 /*---------------------------------------------------------------------------*\
\r
6174 * About box dialog functions
\r
6176 \*---------------------------------------------------------------------------*/
\r
6178 /* Process messages for "About" dialog box */
\r
6180 About(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
6182 switch (message) {
\r
6183 case WM_INITDIALOG: /* message: initialize dialog box */
\r
6184 /* Center the dialog over the application window */
\r
6185 CenterWindow (hDlg, GetWindow (hDlg, GW_OWNER));
\r
6186 SetDlgItemText(hDlg, ABOUTBOX_Version, programVersion);
\r
6187 Translate(hDlg, ABOUTBOX);
\r
6191 case WM_COMMAND: /* message: received a command */
\r
6192 if (LOWORD(wParam) == IDOK /* "OK" box selected? */
\r
6193 || LOWORD(wParam) == IDCANCEL) { /* System menu close command? */
\r
6194 EndDialog(hDlg, TRUE); /* Exit the dialog */
\r
6202 /*---------------------------------------------------------------------------*\
\r
6204 * Comment Dialog functions
\r
6206 \*---------------------------------------------------------------------------*/
\r
6209 CommentDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
6211 static HANDLE hwndText = NULL;
\r
6212 int len, newSizeX, newSizeY, flags;
\r
6213 static int sizeX, sizeY;
\r
6218 switch (message) {
\r
6219 case WM_INITDIALOG: /* message: initialize dialog box */
\r
6220 /* Initialize the dialog items */
\r
6221 Translate(hDlg, DLG_EditComment);
\r
6222 hwndText = GetDlgItem(hDlg, OPT_CommentText);
\r
6223 SetDlgItemText(hDlg, OPT_CommentText, commentText);
\r
6224 EnableWindow(GetDlgItem(hDlg, OPT_CancelComment), editComment);
\r
6225 EnableWindow(GetDlgItem(hDlg, OPT_ClearComment), editComment);
\r
6226 EnableWindow(GetDlgItem(hDlg, OPT_EditComment), !editComment);
\r
6227 SendMessage(hwndText, EM_SETREADONLY, !editComment, 0);
\r
6228 SetWindowText(hDlg, commentTitle);
\r
6229 if (editComment) {
\r
6230 SetFocus(hwndText);
\r
6232 SetFocus(GetDlgItem(hDlg, IDOK));
\r
6234 SendMessage(GetDlgItem(hDlg, OPT_CommentText),
\r
6235 WM_SETFONT, (WPARAM)font[boardSize][COMMENT_FONT]->hf,
\r
6236 MAKELPARAM(FALSE, 0));
\r
6237 /* Size and position the dialog */
\r
6238 if (!commentDialog) {
\r
6239 commentDialog = hDlg;
\r
6240 flags = SWP_NOZORDER;
\r
6241 GetClientRect(hDlg, &rect);
\r
6242 sizeX = rect.right;
\r
6243 sizeY = rect.bottom;
\r
6244 if (wpComment.x != CW_USEDEFAULT && wpComment.y != CW_USEDEFAULT &&
\r
6245 wpComment.width != CW_USEDEFAULT && wpComment.height != CW_USEDEFAULT) {
\r
6246 WINDOWPLACEMENT wp;
\r
6247 EnsureOnScreen(&wpComment.x, &wpComment.y, 0, 0);
\r
6248 wp.length = sizeof(WINDOWPLACEMENT);
\r
6250 wp.showCmd = SW_SHOW;
\r
6251 wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;
\r
6252 wp.rcNormalPosition.left = wpComment.x;
\r
6253 wp.rcNormalPosition.right = wpComment.x + wpComment.width;
\r
6254 wp.rcNormalPosition.top = wpComment.y;
\r
6255 wp.rcNormalPosition.bottom = wpComment.y + wpComment.height;
\r
6256 SetWindowPlacement(hDlg, &wp);
\r
6258 GetClientRect(hDlg, &rect);
\r
6259 newSizeX = rect.right;
\r
6260 newSizeY = rect.bottom;
\r
6261 ResizeEditPlusButtons(hDlg, hwndText, sizeX, sizeY,
\r
6262 newSizeX, newSizeY);
\r
6267 SendDlgItemMessage( hDlg, OPT_CommentText, EM_SETEVENTMASK, 0, ENM_MOUSEEVENTS | ENM_KEYEVENTS );
\r
6270 case WM_COMMAND: /* message: received a command */
\r
6271 switch (LOWORD(wParam)) {
\r
6273 if (editComment) {
\r
6275 /* Read changed options from the dialog box */
\r
6276 hwndText = GetDlgItem(hDlg, OPT_CommentText);
\r
6277 len = GetWindowTextLength(hwndText);
\r
6278 str = (char *) malloc(len + 1);
\r
6279 GetWindowText(hwndText, str, len + 1);
\r
6288 ReplaceComment(commentIndex, str);
\r
6295 case OPT_CancelComment:
\r
6299 case OPT_ClearComment:
\r
6300 SetDlgItemText(hDlg, OPT_CommentText, "");
\r
6303 case OPT_EditComment:
\r
6304 EditCommentEvent();
\r
6312 case WM_NOTIFY: // [HGM] vari: cloned from whistory.c
\r
6313 if( wParam == OPT_CommentText ) {
\r
6314 MSGFILTER * lpMF = (MSGFILTER *) lParam;
\r
6316 if( lpMF->msg == WM_RBUTTONDOWN && (lpMF->wParam & (MK_CONTROL | MK_SHIFT)) == 0 ||
\r
6317 lpMF->msg == WM_CHAR && lpMF->wParam == '\022' ) {
\r
6321 pt.x = LOWORD( lpMF->lParam );
\r
6322 pt.y = HIWORD( lpMF->lParam );
\r
6324 if(lpMF->msg == WM_CHAR) {
\r
6326 SendDlgItemMessage( hDlg, OPT_CommentText, EM_EXGETSEL, 0, (LPARAM) &sel );
\r
6327 index = sel.cpMin;
\r
6329 index = SendDlgItemMessage( hDlg, OPT_CommentText, EM_CHARFROMPOS, 0, (LPARAM) &pt );
\r
6331 hwndText = GetDlgItem(hDlg, OPT_CommentText); // cloned from above
\r
6332 len = GetWindowTextLength(hwndText);
\r
6333 str = (char *) malloc(len + 1);
\r
6334 GetWindowText(hwndText, str, len + 1);
\r
6335 ReplaceComment(commentIndex, str);
\r
6336 if(commentIndex != currentMove) ToNrEvent(commentIndex);
\r
6337 LoadVariation( index, str ); // [HGM] also does the actual moving to it, now
\r
6340 /* Zap the message for good: apparently, returning non-zero is not enough */
\r
6341 lpMF->msg = WM_USER;
\r
6349 newSizeX = LOWORD(lParam);
\r
6350 newSizeY = HIWORD(lParam);
\r
6351 ResizeEditPlusButtons(hDlg, hwndText, sizeX, sizeY, newSizeX, newSizeY);
\r
6356 case WM_GETMINMAXINFO:
\r
6357 /* Prevent resizing window too small */
\r
6358 mmi = (MINMAXINFO *) lParam;
\r
6359 mmi->ptMinTrackSize.x = 100;
\r
6360 mmi->ptMinTrackSize.y = 100;
\r
6367 EitherCommentPopUp(int index, char *title, char *str, BOOLEAN edit)
\r
6372 CheckMenuItem(GetMenu(hwndMain), IDM_Comment, edit ? MF_CHECKED : MF_UNCHECKED);
\r
6374 if (str == NULL) str = "";
\r
6375 p = (char *) malloc(2 * strlen(str) + 2);
\r
6378 if (*str == '\n') *q++ = '\r';
\r
6382 if (commentText != NULL) free(commentText);
\r
6384 commentIndex = index;
\r
6385 commentTitle = title;
\r
6387 editComment = edit;
\r
6389 if (commentDialog) {
\r
6390 SendMessage(commentDialog, WM_INITDIALOG, 0, 0);
\r
6391 if (!commentUp) ShowWindow(commentDialog, SW_SHOW);
\r
6393 lpProc = MakeProcInstance((FARPROC)CommentDialog, hInst);
\r
6394 CreateDialog(hInst, MAKEINTRESOURCE(DLG_EditComment),
\r
6395 hwndMain, (DLGPROC)lpProc);
\r
6396 FreeProcInstance(lpProc);
\r
6402 /*---------------------------------------------------------------------------*\
\r
6404 * Type-in move dialog functions
\r
6406 \*---------------------------------------------------------------------------*/
\r
6409 TypeInMoveDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
6411 char move[MSG_SIZ];
\r
6413 ChessMove moveType;
\r
6414 int fromX, fromY, toX, toY;
\r
6417 switch (message) {
\r
6418 case WM_INITDIALOG:
\r
6419 move[0] = (char) lParam;
\r
6420 move[1] = NULLCHAR;
\r
6421 CenterWindowEx(hDlg, GetWindow(hDlg, GW_OWNER), 1 );
\r
6422 Translate(hDlg, DLG_TypeInMove);
\r
6423 hInput = GetDlgItem(hDlg, OPT_Move);
\r
6424 SetWindowText(hInput, move);
\r
6426 SendMessage(hInput, EM_SETSEL, (WPARAM)9999, (LPARAM)9999);
\r
6430 switch (LOWORD(wParam)) {
\r
6433 shiftKey = GetKeyState(VK_SHIFT) < 0; // [HGM] remember last shift status
\r
6434 GetDlgItemText(hDlg, OPT_Move, move, sizeof(move));
\r
6435 { int n; Board board;
\r
6437 if(gameMode == EditPosition && ParseFEN(board, &n, move) ) {
\r
6438 EditPositionPasteFEN(move);
\r
6439 EndDialog(hDlg, TRUE);
\r
6442 // [HGM] movenum: allow move number to be typed in any mode
\r
6443 if(sscanf(move, "%d", &n) == 1 && n != 0 ) {
\r
6445 EndDialog(hDlg, TRUE);
\r
6449 if (gameMode != EditGame && currentMove != forwardMostMove &&
\r
6450 gameMode != Training) {
\r
6451 DisplayMoveError(_("Displayed move is not current"));
\r
6453 // GetDlgItemText(hDlg, OPT_Move, move, sizeof(move)); // moved upstream
\r
6454 int ok = ParseOneMove(move, gameMode == EditPosition ? blackPlaysFirst : currentMove,
\r
6455 &moveType, &fromX, &fromY, &toX, &toY, &promoChar);
\r
6456 if(!ok && move[0] >= 'a') { move[0] += 'A' - 'a'; ok = 2; } // [HGM] try also capitalized
\r
6457 if (ok==1 || ok && ParseOneMove(move, gameMode == EditPosition ? blackPlaysFirst : currentMove,
\r
6458 &moveType, &fromX, &fromY, &toX, &toY, &promoChar)) {
\r
6459 if (gameMode != Training)
\r
6460 forwardMostMove = currentMove;
\r
6461 UserMoveEvent(fromX, fromY, toX, toY, promoChar);
\r
6463 DisplayMoveError(_("Could not parse move"));
\r
6466 EndDialog(hDlg, TRUE);
\r
6469 EndDialog(hDlg, FALSE);
\r
6480 PopUpMoveDialog(char firstchar)
\r
6484 if ((gameMode == BeginningOfGame && !appData.icsActive) ||
\r
6485 gameMode == MachinePlaysWhite || gameMode == MachinePlaysBlack ||
\r
6486 gameMode == AnalyzeMode || gameMode == EditGame ||
\r
6487 gameMode == EditPosition || gameMode == IcsExamining ||
\r
6488 gameMode == IcsPlayingWhite || gameMode == IcsPlayingBlack ||
\r
6489 isdigit(firstchar) && // [HGM] movenum: allow typing in of move nr in 'passive' modes
\r
6490 ( gameMode == AnalyzeFile || gameMode == PlayFromGameFile ||
\r
6491 gameMode == IcsObserving || gameMode == TwoMachinesPlay ) ||
\r
6492 gameMode == Training) {
\r
6493 lpProc = MakeProcInstance((FARPROC)TypeInMoveDialog, hInst);
\r
6494 DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_TypeInMove),
\r
6495 hwndMain, (DLGPROC)lpProc, (LPARAM)firstchar);
\r
6496 FreeProcInstance(lpProc);
\r
6500 /*---------------------------------------------------------------------------*\
\r
6502 * Type-in name dialog functions
\r
6504 \*---------------------------------------------------------------------------*/
\r
6507 TypeInNameDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
6509 char move[MSG_SIZ];
\r
6512 switch (message) {
\r
6513 case WM_INITDIALOG:
\r
6514 move[0] = (char) lParam;
\r
6515 move[1] = NULLCHAR;
\r
6516 CenterWindowEx(hDlg, GetWindow(hDlg, GW_OWNER), 1 );
\r
6517 Translate(hDlg, DLG_TypeInName);
\r
6518 hInput = GetDlgItem(hDlg, OPT_Name);
\r
6519 SetWindowText(hInput, move);
\r
6521 SendMessage(hInput, EM_SETSEL, (WPARAM)9999, (LPARAM)9999);
\r
6525 switch (LOWORD(wParam)) {
\r
6527 GetDlgItemText(hDlg, OPT_Name, move, sizeof(move));
\r
6528 appData.userName = strdup(move);
\r
6531 if(gameMode == MachinePlaysWhite || gameMode == MachinePlaysBlack) {
\r
6532 snprintf(move, MSG_SIZ, "%s vs. %s", gameInfo.white, gameInfo.black);
\r
6533 DisplayTitle(move);
\r
6537 EndDialog(hDlg, TRUE);
\r
6540 EndDialog(hDlg, FALSE);
\r
6551 PopUpNameDialog(char firstchar)
\r
6555 lpProc = MakeProcInstance((FARPROC)TypeInNameDialog, hInst);
\r
6556 DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_TypeInName),
\r
6557 hwndMain, (DLGPROC)lpProc, (LPARAM)firstchar);
\r
6558 FreeProcInstance(lpProc);
\r
6561 /*---------------------------------------------------------------------------*\
\r
6565 \*---------------------------------------------------------------------------*/
\r
6567 /* Nonmodal error box */
\r
6568 LRESULT CALLBACK ErrorDialog(HWND hDlg, UINT message,
\r
6569 WPARAM wParam, LPARAM lParam);
\r
6572 ErrorPopUp(char *title, char *content)
\r
6576 BOOLEAN modal = hwndMain == NULL;
\r
6594 strncpy(errorTitle, title, sizeof(errorTitle));
\r
6595 errorTitle[sizeof(errorTitle) - 1] = '\0';
\r
6598 MessageBox(NULL, errorMessage, errorTitle, MB_OK|MB_ICONEXCLAMATION);
\r
6600 lpProc = MakeProcInstance((FARPROC)ErrorDialog, hInst);
\r
6601 CreateDialog(hInst, MAKEINTRESOURCE(DLG_Error),
\r
6602 hwndMain, (DLGPROC)lpProc);
\r
6603 FreeProcInstance(lpProc);
\r
6610 if (!appData.popupMoveErrors && moveErrorMessageUp) DisplayMessage("", "");
\r
6611 if (errorDialog == NULL) return;
\r
6612 DestroyWindow(errorDialog);
\r
6613 errorDialog = NULL;
\r
6614 if(errorExitStatus) ExitEvent(errorExitStatus);
\r
6618 ErrorDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
6623 switch (message) {
\r
6624 case WM_INITDIALOG:
\r
6625 GetWindowRect(hDlg, &rChild);
\r
6628 SetWindowPos(hDlg, NULL, rChild.left,
\r
6629 rChild.top + boardRect.top - (rChild.bottom - rChild.top),
\r
6630 0, 0, SWP_NOZORDER|SWP_NOSIZE);
\r
6634 [AS] It seems that the above code wants to move the dialog up in the "caption
\r
6635 area" of the main window, but it uses the dialog height as an hard-coded constant,
\r
6636 and it doesn't work when you resize the dialog.
\r
6637 For now, just give it a default position.
\r
6639 SetWindowPos(hDlg, NULL, boardRect.left+8, boardRect.top+8, 0, 0, SWP_NOZORDER|SWP_NOSIZE);
\r
6640 Translate(hDlg, DLG_Error);
\r
6642 errorDialog = hDlg;
\r
6643 SetWindowText(hDlg, errorTitle);
\r
6644 hwndText = GetDlgItem(hDlg, OPT_ErrorText);
\r
6645 SetDlgItemText(hDlg, OPT_ErrorText, errorMessage);
\r
6649 switch (LOWORD(wParam)) {
\r
6652 if (errorDialog == hDlg) errorDialog = NULL;
\r
6653 DestroyWindow(hDlg);
\r
6665 HWND gothicDialog = NULL;
\r
6668 GothicDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
6672 int height = GetSystemMetrics(SM_CYCAPTION)+GetSystemMetrics(SM_CYFRAME);
\r
6674 switch (message) {
\r
6675 case WM_INITDIALOG:
\r
6676 GetWindowRect(hDlg, &rChild);
\r
6678 SetWindowPos(hDlg, NULL, wpMain.x, wpMain.y-height, wpMain.width, height,
\r
6682 [AS] It seems that the above code wants to move the dialog up in the "caption
\r
6683 area" of the main window, but it uses the dialog height as an hard-coded constant,
\r
6684 and it doesn't work when you resize the dialog.
\r
6685 For now, just give it a default position.
\r
6687 gothicDialog = hDlg;
\r
6688 SetWindowText(hDlg, errorTitle);
\r
6689 hwndText = GetDlgItem(hDlg, OPT_ErrorText);
\r
6690 SetDlgItemText(hDlg, OPT_ErrorText, errorMessage);
\r
6694 switch (LOWORD(wParam)) {
\r
6697 if (errorDialog == hDlg) errorDialog = NULL;
\r
6698 DestroyWindow(hDlg);
\r
6710 GothicPopUp(char *title, VariantClass variant)
\r
6713 static char *lastTitle;
\r
6715 strncpy(errorTitle, title, sizeof(errorTitle));
\r
6716 errorTitle[sizeof(errorTitle) - 1] = '\0';
\r
6718 if(lastTitle != title && gothicDialog != NULL) {
\r
6719 DestroyWindow(gothicDialog);
\r
6720 gothicDialog = NULL;
\r
6722 if(variant != VariantNormal && gothicDialog == NULL) {
\r
6723 title = lastTitle;
\r
6724 lpProc = MakeProcInstance((FARPROC)GothicDialog, hInst);
\r
6725 CreateDialog(hInst, MAKEINTRESOURCE(DLG_Error),
\r
6726 hwndMain, (DLGPROC)lpProc);
\r
6727 FreeProcInstance(lpProc);
\r
6732 /*---------------------------------------------------------------------------*\
\r
6734 * Ics Interaction console functions
\r
6736 \*---------------------------------------------------------------------------*/
\r
6738 #define HISTORY_SIZE 64
\r
6739 static char *history[HISTORY_SIZE];
\r
6740 int histIn = 0, histP = 0;
\r
6743 SaveInHistory(char *cmd)
\r
6745 if (history[histIn] != NULL) {
\r
6746 free(history[histIn]);
\r
6747 history[histIn] = NULL;
\r
6749 if (*cmd == NULLCHAR) return;
\r
6750 history[histIn] = StrSave(cmd);
\r
6751 histIn = (histIn + 1) % HISTORY_SIZE;
\r
6752 if (history[histIn] != NULL) {
\r
6753 free(history[histIn]);
\r
6754 history[histIn] = NULL;
\r
6760 PrevInHistory(char *cmd)
\r
6763 if (histP == histIn) {
\r
6764 if (history[histIn] != NULL) free(history[histIn]);
\r
6765 history[histIn] = StrSave(cmd);
\r
6767 newhp = (histP - 1 + HISTORY_SIZE) % HISTORY_SIZE;
\r
6768 if (newhp == histIn || history[newhp] == NULL) return NULL;
\r
6770 return history[histP];
\r
6776 if (histP == histIn) return NULL;
\r
6777 histP = (histP + 1) % HISTORY_SIZE;
\r
6778 return history[histP];
\r
6782 LoadIcsTextMenu(IcsTextMenuEntry *e)
\r
6786 hmenu = LoadMenu(hInst, "TextMenu");
\r
6787 h = GetSubMenu(hmenu, 0);
\r
6789 if (strcmp(e->item, "-") == 0) {
\r
6790 AppendMenu(h, MF_SEPARATOR, 0, 0);
\r
6791 } else { // [HGM] re-written a bit to use only one AppendMenu call for both cases (| or no |)
\r
6792 int flags = MF_STRING, j = 0;
\r
6793 if (e->item[0] == '|') {
\r
6794 flags |= MF_MENUBARBREAK;
\r
6797 if(!strcmp(e->command, "none")) flags |= MF_GRAYED; // [HGM] chatclick: provide inactive dummy
\r
6798 AppendMenu(h, flags, IDM_CommandX + i, e->item + j);
\r
6806 WNDPROC consoleTextWindowProc;
\r
6809 CommandX(HWND hwnd, char *command, BOOLEAN getname, BOOLEAN immediate)
\r
6811 char buf[MSG_SIZ], name[MSG_SIZ];
\r
6812 HWND hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
6816 SetWindowText(hInput, command);
\r
6818 SendMessage(hInput, WM_CHAR, '\r', 0);
\r
6820 sel.cpMin = 999999;
\r
6821 sel.cpMax = 999999;
\r
6822 SendMessage(hInput, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
6827 SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);
\r
6828 if (sel.cpMin == sel.cpMax) {
\r
6829 /* Expand to surrounding word */
\r
6832 tr.chrg.cpMax = sel.cpMin;
\r
6833 tr.chrg.cpMin = --sel.cpMin;
\r
6834 if (sel.cpMin < 0) break;
\r
6835 tr.lpstrText = name;
\r
6836 SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr);
\r
6837 } while (isalpha(name[0]) || isdigit(name[0]) || name[0] == '-');
\r
6841 tr.chrg.cpMin = sel.cpMax;
\r
6842 tr.chrg.cpMax = ++sel.cpMax;
\r
6843 tr.lpstrText = name;
\r
6844 if (SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr) < 1) break;
\r
6845 } while (isalpha(name[0]) || isdigit(name[0]) || name[0] == '-');
\r
6848 if (sel.cpMax == sel.cpMin || sel.cpMax - sel.cpMin > MSG_SIZ/2) {
\r
6849 MessageBeep(MB_ICONEXCLAMATION);
\r
6853 tr.lpstrText = name;
\r
6854 SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr);
\r
6856 if (sel.cpMax - sel.cpMin > MSG_SIZ/2) {
\r
6857 MessageBeep(MB_ICONEXCLAMATION);
\r
6860 SendMessage(hwnd, EM_GETSELTEXT, 0, (LPARAM) name);
\r
6863 if(strstr(command, "%s")) snprintf(buf, MSG_SIZ, command, name); else
\r
6864 snprintf(buf, MSG_SIZ, "%s %s", command, name);
\r
6865 SetWindowText(hInput, buf);
\r
6866 SendMessage(hInput, WM_CHAR, '\r', 0);
\r
6868 if(!strcmp(command, "chat")) { ChatPopUp(name); return; }
\r
6869 snprintf(buf, MSG_SIZ, "%s %s ", command, name); /* trailing space */
\r
6870 SetWindowText(hInput, buf);
\r
6871 sel.cpMin = 999999;
\r
6872 sel.cpMax = 999999;
\r
6873 SendMessage(hInput, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
6879 ConsoleTextSubclass(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
\r
6884 switch (message) {
\r
6886 if (!(GetKeyState(VK_CONTROL) & ~1)) break;
\r
6887 if(wParam=='R') return 0;
\r
6890 SendMessage(hwnd, EM_LINESCROLL, 0, -999999);
\r
6893 sel.cpMin = 999999;
\r
6894 sel.cpMax = 999999;
\r
6895 SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
6896 SendMessage(hwnd, EM_SCROLLCARET, 0, 0);
\r
6901 if(wParam != '\022') {
\r
6902 if (wParam == '\t') {
\r
6903 if (GetKeyState(VK_SHIFT) < 0) {
\r
6905 if (IsIconic(hwndMain)) ShowWindow(hwndMain, SW_RESTORE);
\r
6906 if (buttonDesc[0].hwnd) {
\r
6907 SetFocus(buttonDesc[0].hwnd);
\r
6909 SetFocus(hwndMain);
\r
6913 SetFocus(GetDlgItem(hwndConsole, OPT_ConsoleInput));
\r
6916 hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
6917 JAWS_DELETE( SetFocus(hInput); )
\r
6918 SendMessage(hInput, message, wParam, lParam);
\r
6921 } // [HGM] navigate: for Ctrl+R, flow into next case (moved up here) to summon up menu
\r
6923 case WM_RBUTTONDOWN:
\r
6924 if (!(GetKeyState(VK_SHIFT) & ~1)) {
\r
6925 /* Move selection here if it was empty */
\r
6927 pt.x = LOWORD(lParam);
\r
6928 pt.y = HIWORD(lParam);
\r
6929 SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);
\r
6930 if (sel.cpMin == sel.cpMax) {
\r
6931 if(lParam != -1) sel.cpMin = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&pt); /*doc is wrong*/
\r
6932 sel.cpMax = sel.cpMin;
\r
6933 SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
6935 SendMessage(hwnd, EM_HIDESELECTION, FALSE, FALSE);
\r
6936 { // [HGM] chatclick: code moved here from WM_RBUTTONUP case, to have menu appear on down-click
\r
6938 HMENU hmenu = LoadIcsTextMenu(icsTextMenuEntry);
\r
6939 SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);
\r
6940 if (sel.cpMin == sel.cpMax) {
\r
6941 EnableMenuItem(hmenu, IDM_Copy, MF_BYCOMMAND|MF_GRAYED);
\r
6942 EnableMenuItem(hmenu, IDM_QuickPaste, MF_BYCOMMAND|MF_GRAYED);
\r
6944 if (!IsClipboardFormatAvailable(CF_TEXT)) {
\r
6945 EnableMenuItem(hmenu, IDM_Paste, MF_BYCOMMAND|MF_GRAYED);
\r
6947 pt.x = LOWORD(lParam)-30; // [HGM] chatclick: make menu pop up with pointer above upper-right item
\r
6948 pt.y = HIWORD(lParam)-10; // make it appear as if mouse moved there, so it will be selected on up-click
\r
6949 PostMessage(hwnd, WM_MOUSEMOVE, wParam, lParam+5);
\r
6950 MenuPopup(hwnd, pt, hmenu, -1);
\r
6954 case WM_RBUTTONUP:
\r
6955 if (GetKeyState(VK_SHIFT) & ~1) {
\r
6956 SendDlgItemMessage(hwndConsole, OPT_ConsoleText,
\r
6957 WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);
\r
6961 hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
6963 return SendMessage(hInput, message, wParam, lParam);
\r
6964 case WM_MBUTTONDOWN:
\r
6965 return SendMessage(hwnd, WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);
\r
6967 switch (LOWORD(wParam)) {
\r
6968 case IDM_QuickPaste:
\r
6970 SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);
\r
6971 if (sel.cpMin == sel.cpMax) {
\r
6972 MessageBeep(MB_ICONEXCLAMATION);
\r
6975 SendMessage(hwnd, WM_COPY, 0, 0);
\r
6976 hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
6977 SendMessage(hInput, WM_PASTE, 0, 0);
\r
6982 SendMessage(hwnd, WM_CUT, 0, 0);
\r
6985 SendMessage(hwnd, WM_PASTE, 0, 0);
\r
6988 SendMessage(hwnd, WM_COPY, 0, 0);
\r
6992 int i = LOWORD(wParam) - IDM_CommandX;
\r
6993 if (i >= 0 && i < ICS_TEXT_MENU_SIZE &&
\r
6994 icsTextMenuEntry[i].command != NULL) {
\r
6995 CommandX(hwnd, icsTextMenuEntry[i].command,
\r
6996 icsTextMenuEntry[i].getname,
\r
6997 icsTextMenuEntry[i].immediate);
\r
7005 return (*consoleTextWindowProc)(hwnd, message, wParam, lParam);
\r
7008 WNDPROC consoleInputWindowProc;
\r
7011 ConsoleInputSubclass(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
\r
7013 char buf[MSG_SIZ];
\r
7015 static BOOL sendNextChar = FALSE;
\r
7016 static BOOL quoteNextChar = FALSE;
\r
7017 InputSource *is = consoleInputSource;
\r
7021 switch (message) {
\r
7023 if (!appData.localLineEditing || sendNextChar) {
\r
7024 is->buf[0] = (CHAR) wParam;
\r
7026 SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);
\r
7027 sendNextChar = FALSE;
\r
7030 if (quoteNextChar) {
\r
7031 buf[0] = (char) wParam;
\r
7032 buf[1] = NULLCHAR;
\r
7033 SendMessage(hwnd, EM_REPLACESEL, TRUE, (LPARAM) buf);
\r
7034 quoteNextChar = FALSE;
\r
7038 case '\r': /* Enter key */
\r
7039 is->count = GetWindowText(hwnd, is->buf, INPUT_SOURCE_BUF_SIZE-1);
\r
7040 if (consoleEcho) SaveInHistory(is->buf);
\r
7041 is->buf[is->count++] = '\n';
\r
7042 is->buf[is->count] = NULLCHAR;
\r
7043 SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);
\r
7044 if (consoleEcho) {
\r
7045 ConsoleOutput(is->buf, is->count, TRUE);
\r
7046 } else if (appData.localLineEditing) {
\r
7047 ConsoleOutput("\n", 1, TRUE);
\r
7050 case '\033': /* Escape key */
\r
7051 SetWindowText(hwnd, "");
\r
7052 cf.cbSize = sizeof(CHARFORMAT);
\r
7053 cf.dwMask = CFM_COLOR|CFM_BOLD|CFM_ITALIC|CFM_UNDERLINE|CFM_STRIKEOUT;
\r
7054 if (consoleEcho) {
\r
7055 cf.crTextColor = textAttribs[ColorNormal].color;
\r
7057 cf.crTextColor = COLOR_ECHOOFF;
\r
7059 cf.dwEffects = textAttribs[ColorNormal].effects;
\r
7060 SendMessage(hwnd, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf);
\r
7062 case '\t': /* Tab key */
\r
7063 if (GetKeyState(VK_SHIFT) < 0) {
\r
7065 SetFocus(GetDlgItem(hwndConsole, OPT_ConsoleText));
\r
7068 if (IsIconic(hwndMain)) ShowWindow(hwndMain, SW_RESTORE);
\r
7069 if (buttonDesc[0].hwnd) {
\r
7070 SetFocus(buttonDesc[0].hwnd);
\r
7072 SetFocus(hwndMain);
\r
7076 case '\023': /* Ctrl+S */
\r
7077 sendNextChar = TRUE;
\r
7079 case '\021': /* Ctrl+Q */
\r
7080 quoteNextChar = TRUE;
\r
7090 GetWindowText(hwnd, buf, MSG_SIZ);
\r
7091 p = PrevInHistory(buf);
\r
7093 SetWindowText(hwnd, p);
\r
7094 sel.cpMin = 999999;
\r
7095 sel.cpMax = 999999;
\r
7096 SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
7101 p = NextInHistory();
\r
7103 SetWindowText(hwnd, p);
\r
7104 sel.cpMin = 999999;
\r
7105 sel.cpMax = 999999;
\r
7106 SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
7112 if (!(GetKeyState(VK_CONTROL) & ~1)) break;
\r
7116 SendDlgItemMessage(hwndConsole, OPT_ConsoleText, message, wParam, lParam);
\r
7120 case WM_MBUTTONDOWN:
\r
7121 SendDlgItemMessage(hwndConsole, OPT_ConsoleText,
\r
7122 WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);
\r
7124 case WM_RBUTTONUP:
\r
7125 if (GetKeyState(VK_SHIFT) & ~1) {
\r
7126 SendDlgItemMessage(hwndConsole, OPT_ConsoleText,
\r
7127 WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);
\r
7131 hmenu = LoadMenu(hInst, "InputMenu");
\r
7132 SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);
\r
7133 if (sel.cpMin == sel.cpMax) {
\r
7134 EnableMenuItem(hmenu, IDM_Copy, MF_BYCOMMAND|MF_GRAYED);
\r
7135 EnableMenuItem(hmenu, IDM_Cut, MF_BYCOMMAND|MF_GRAYED);
\r
7137 if (!IsClipboardFormatAvailable(CF_TEXT)) {
\r
7138 EnableMenuItem(hmenu, IDM_Paste, MF_BYCOMMAND|MF_GRAYED);
\r
7140 pt.x = LOWORD(lParam);
\r
7141 pt.y = HIWORD(lParam);
\r
7142 MenuPopup(hwnd, pt, hmenu, -1);
\r
7146 switch (LOWORD(wParam)) {
\r
7148 SendMessage(hwnd, EM_UNDO, 0, 0);
\r
7150 case IDM_SelectAll:
\r
7152 sel.cpMax = -1; /*999999?*/
\r
7153 SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
7156 SendMessage(hwnd, WM_CUT, 0, 0);
\r
7159 SendMessage(hwnd, WM_PASTE, 0, 0);
\r
7162 SendMessage(hwnd, WM_COPY, 0, 0);
\r
7167 return (*consoleInputWindowProc)(hwnd, message, wParam, lParam);
\r
7170 #define CO_MAX 100000
\r
7171 #define CO_TRIM 1000
\r
7174 ConsoleWndProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
7176 static SnapData sd;
\r
7177 HWND hText, hInput;
\r
7179 static int sizeX, sizeY;
\r
7180 int newSizeX, newSizeY;
\r
7184 hText = GetDlgItem(hDlg, OPT_ConsoleText);
\r
7185 hInput = GetDlgItem(hDlg, OPT_ConsoleInput);
\r
7187 switch (message) {
\r
7189 if (((NMHDR*)lParam)->code == EN_LINK)
\r
7191 ENLINK *pLink = (ENLINK*)lParam;
\r
7192 if (pLink->msg == WM_LBUTTONUP)
\r
7196 tr.chrg = pLink->chrg;
\r
7197 tr.lpstrText = malloc(1+tr.chrg.cpMax-tr.chrg.cpMin);
\r
7198 SendMessage(hText, EM_GETTEXTRANGE, 0, (LPARAM)&tr);
\r
7199 ShellExecute(NULL, "open", tr.lpstrText, NULL, NULL, SW_SHOW);
\r
7200 free(tr.lpstrText);
\r
7204 case WM_INITDIALOG: /* message: initialize dialog box */
\r
7205 hwndConsole = hDlg;
\r
7207 consoleTextWindowProc = (WNDPROC)
\r
7208 SetWindowLong(hText, GWL_WNDPROC, (LONG) ConsoleTextSubclass);
\r
7209 SendMessage(hText, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);
\r
7210 consoleInputWindowProc = (WNDPROC)
\r
7211 SetWindowLong(hInput, GWL_WNDPROC, (LONG) ConsoleInputSubclass);
\r
7212 SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);
\r
7213 Colorize(ColorNormal, TRUE);
\r
7214 SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &consoleCF);
\r
7215 ChangedConsoleFont();
\r
7216 GetClientRect(hDlg, &rect);
\r
7217 sizeX = rect.right;
\r
7218 sizeY = rect.bottom;
\r
7219 if (wpConsole.x != CW_USEDEFAULT && wpConsole.y != CW_USEDEFAULT &&
\r
7220 wpConsole.width != CW_USEDEFAULT && wpConsole.height != CW_USEDEFAULT) {
\r
7221 WINDOWPLACEMENT wp;
\r
7222 EnsureOnScreen(&wpConsole.x, &wpConsole.y, 0, 0);
\r
7223 wp.length = sizeof(WINDOWPLACEMENT);
\r
7225 wp.showCmd = SW_SHOW;
\r
7226 wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;
\r
7227 wp.rcNormalPosition.left = wpConsole.x;
\r
7228 wp.rcNormalPosition.right = wpConsole.x + wpConsole.width;
\r
7229 wp.rcNormalPosition.top = wpConsole.y;
\r
7230 wp.rcNormalPosition.bottom = wpConsole.y + wpConsole.height;
\r
7231 SetWindowPlacement(hDlg, &wp);
\r
7234 // [HGM] Chessknight's change 2004-07-13
\r
7235 else { /* Determine Defaults */
\r
7236 WINDOWPLACEMENT wp;
\r
7237 wpConsole.x = wpMain.width + 1;
\r
7238 wpConsole.y = wpMain.y;
\r
7239 wpConsole.width = screenWidth - wpMain.width;
\r
7240 wpConsole.height = wpMain.height;
\r
7241 EnsureOnScreen(&wpConsole.x, &wpConsole.y, 0, 0);
\r
7242 wp.length = sizeof(WINDOWPLACEMENT);
\r
7244 wp.showCmd = SW_SHOW;
\r
7245 wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;
\r
7246 wp.rcNormalPosition.left = wpConsole.x;
\r
7247 wp.rcNormalPosition.right = wpConsole.x + wpConsole.width;
\r
7248 wp.rcNormalPosition.top = wpConsole.y;
\r
7249 wp.rcNormalPosition.bottom = wpConsole.y + wpConsole.height;
\r
7250 SetWindowPlacement(hDlg, &wp);
\r
7253 // Allow hText to highlight URLs and send notifications on them
\r
7254 wMask = (WORD) SendMessage(hText, EM_GETEVENTMASK, 0, 0L);
\r
7255 SendMessage(hText, EM_SETEVENTMASK, 0, wMask | ENM_LINK);
\r
7256 SendMessage(hText, EM_AUTOURLDETECT, TRUE, 0L);
\r
7257 SetWindowLong(hText, GWL_USERDATA, 79); // initialize the text window's width
\r
7271 if (IsIconic(hDlg)) break;
\r
7272 newSizeX = LOWORD(lParam);
\r
7273 newSizeY = HIWORD(lParam);
\r
7274 if (sizeX != newSizeX || sizeY != newSizeY) {
\r
7275 RECT rectText, rectInput;
\r
7277 int newTextHeight, newTextWidth;
\r
7278 GetWindowRect(hText, &rectText);
\r
7279 newTextWidth = rectText.right - rectText.left + newSizeX - sizeX;
\r
7280 newTextHeight = rectText.bottom - rectText.top + newSizeY - sizeY;
\r
7281 if (newTextHeight < 0) {
\r
7282 newSizeY += -newTextHeight;
\r
7283 newTextHeight = 0;
\r
7285 SetWindowPos(hText, NULL, 0, 0,
\r
7286 newTextWidth, newTextHeight, SWP_NOZORDER|SWP_NOMOVE);
\r
7287 GetWindowRect(hInput, &rectInput); /* gives screen coords */
\r
7288 pt.x = rectInput.left;
\r
7289 pt.y = rectInput.top + newSizeY - sizeY;
\r
7290 ScreenToClient(hDlg, &pt);
\r
7291 SetWindowPos(hInput, NULL,
\r
7292 pt.x, pt.y, /* needs client coords */
\r
7293 rectInput.right - rectInput.left + newSizeX - sizeX,
\r
7294 rectInput.bottom - rectInput.top, SWP_NOZORDER);
\r
7300 case WM_GETMINMAXINFO:
\r
7301 /* Prevent resizing window too small */
\r
7302 mmi = (MINMAXINFO *) lParam;
\r
7303 mmi->ptMinTrackSize.x = 100;
\r
7304 mmi->ptMinTrackSize.y = 100;
\r
7307 /* [AS] Snapping */
\r
7308 case WM_ENTERSIZEMOVE:
\r
7309 return OnEnterSizeMove( &sd, hDlg, wParam, lParam );
\r
7312 return OnSizing( &sd, hDlg, wParam, lParam );
\r
7315 return OnMoving( &sd, hDlg, wParam, lParam );
\r
7317 case WM_EXITSIZEMOVE:
\r
7318 UpdateICSWidth(hText);
\r
7319 return OnExitSizeMove( &sd, hDlg, wParam, lParam );
\r
7322 return DefWindowProc(hDlg, message, wParam, lParam);
\r
7330 if (hwndConsole) return;
\r
7331 hCons = CreateDialog(hInst, szConsoleName, 0, NULL);
\r
7332 SendMessage(hCons, WM_INITDIALOG, 0, 0);
\r
7337 ConsoleOutput(char* data, int length, int forceVisible)
\r
7342 char buf[CO_MAX+1];
\r
7345 static int delayLF = 0;
\r
7346 CHARRANGE savesel, sel;
\r
7348 if (hwndConsole == NULL || length > CO_MAX-100 || length == 0) return;
\r
7356 while (length--) {
\r
7364 } else if (*p == '\007') {
\r
7365 MyPlaySound(&sounds[(int)SoundBell]);
\r
7372 hText = GetDlgItem(hwndConsole, OPT_ConsoleText);
\r
7373 SendMessage(hText, EM_HIDESELECTION, TRUE, FALSE);
\r
7374 /* Save current selection */
\r
7375 SendMessage(hText, EM_EXGETSEL, 0, (LPARAM)&savesel);
\r
7376 exlen = GetWindowTextLength(hText);
\r
7377 /* Find out whether current end of text is visible */
\r
7378 SendMessage(hText, EM_GETRECT, 0, (LPARAM) &rect);
\r
7379 SendMessage(hText, EM_POSFROMCHAR, (WPARAM) &pEnd, exlen);
\r
7380 /* Trim existing text if it's too long */
\r
7381 if (exlen + (q - buf) > CO_MAX) {
\r
7382 trim = (CO_TRIM > (q - buf)) ? CO_TRIM : (q - buf);
\r
7385 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
7386 SendMessage(hText, EM_REPLACESEL, 0, (LPARAM)"");
\r
7388 savesel.cpMin -= trim;
\r
7389 savesel.cpMax -= trim;
\r
7390 if (exlen < 0) exlen = 0;
\r
7391 if (savesel.cpMin < 0) savesel.cpMin = 0;
\r
7392 if (savesel.cpMax < savesel.cpMin) savesel.cpMax = savesel.cpMin;
\r
7394 /* Append the new text */
\r
7395 sel.cpMin = exlen;
\r
7396 sel.cpMax = exlen;
\r
7397 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
7398 SendMessage(hText, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&consoleCF);
\r
7399 SendMessage(hText, EM_REPLACESEL, 0, (LPARAM) buf);
\r
7400 if (forceVisible || exlen == 0 ||
\r
7401 (rect.left <= pEnd.x && pEnd.x < rect.right &&
\r
7402 rect.top <= pEnd.y && pEnd.y < rect.bottom)) {
\r
7403 /* Scroll to make new end of text visible if old end of text
\r
7404 was visible or new text is an echo of user typein */
\r
7405 sel.cpMin = 9999999;
\r
7406 sel.cpMax = 9999999;
\r
7407 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
7408 SendMessage(hText, EM_HIDESELECTION, FALSE, FALSE);
\r
7409 SendMessage(hText, EM_SCROLLCARET, 0, 0);
\r
7410 SendMessage(hText, EM_HIDESELECTION, TRUE, FALSE);
\r
7412 if (savesel.cpMax == exlen || forceVisible) {
\r
7413 /* Move insert point to new end of text if it was at the old
\r
7414 end of text or if the new text is an echo of user typein */
\r
7415 sel.cpMin = 9999999;
\r
7416 sel.cpMax = 9999999;
\r
7417 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
7419 /* Restore previous selection */
\r
7420 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&savesel);
\r
7422 SendMessage(hText, EM_HIDESELECTION, FALSE, FALSE);
\r
7429 DisplayHoldingsCount(HDC hdc, int x, int y, int rightAlign, int copyNumber)
\r
7433 COLORREF oldFg, oldBg;
\r
7438 snprintf(buf, sizeof(buf)/sizeof(buf[0]),"%d", copyNumber); else buf[0] = 0;
\r
7440 oldFg = SetTextColor(hdc, RGB(255, 255, 255)); /* white */
\r
7441 oldBg = SetBkColor(hdc, RGB(0, 0, 0)); /* black */
\r
7442 oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);
\r
7445 rect.right = x + squareSize;
\r
7447 rect.bottom = y + squareSize;
\r
7450 ExtTextOut(hdc, x + MESSAGE_LINE_LEFTMARGIN
\r
7451 + (rightAlign ? (squareSize*2)/3 : 0),
\r
7452 y, ETO_CLIPPED|ETO_OPAQUE,
\r
7453 &rect, str, strlen(str), NULL);
\r
7455 (void) SetTextColor(hdc, oldFg);
\r
7456 (void) SetBkColor(hdc, oldBg);
\r
7457 (void) SelectObject(hdc, oldFont);
\r
7461 DisplayAClock(HDC hdc, int timeRemaining, int highlight,
\r
7462 RECT *rect, char *color, char *flagFell)
\r
7466 COLORREF oldFg, oldBg;
\r
7469 if (appData.clockMode) {
\r
7471 snprintf(buf, sizeof(buf)/sizeof(buf[0]), "%c %s %s", color[0], TimeString(timeRemaining), flagFell);
\r
7473 snprintf(buf, sizeof(buf)/sizeof(buf[0]), "%s:%c%s %s", color, (logoHeight>0 ? 0 : ' '), TimeString(timeRemaining), flagFell);
\r
7480 oldFg = SetTextColor(hdc, RGB(255, 255, 255)); /* white */
\r
7481 oldBg = SetBkColor(hdc, RGB(0, 0, 0)); /* black */
\r
7483 oldFg = SetTextColor(hdc, RGB(0, 0, 0)); /* black */
\r
7484 oldBg = SetBkColor(hdc, RGB(255, 255, 255)); /* white */
\r
7486 oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);
\r
7490 ExtTextOut(hdc, rect->left + MESSAGE_LINE_LEFTMARGIN,
\r
7491 rect->top, ETO_CLIPPED|ETO_OPAQUE,
\r
7492 rect, str, strlen(str), NULL);
\r
7493 if(logoHeight > 0 && appData.clockMode) {
\r
7495 str += strlen(color)+2;
\r
7496 r.top = rect->top + logoHeight/2;
\r
7497 r.left = rect->left;
\r
7498 r.right = rect->right;
\r
7499 r.bottom = rect->bottom;
\r
7500 ExtTextOut(hdc, rect->left + MESSAGE_LINE_LEFTMARGIN,
\r
7501 r.top, ETO_CLIPPED|ETO_OPAQUE,
\r
7502 &r, str, strlen(str), NULL);
\r
7504 (void) SetTextColor(hdc, oldFg);
\r
7505 (void) SetBkColor(hdc, oldBg);
\r
7506 (void) SelectObject(hdc, oldFont);
\r
7511 DoReadFile(HANDLE hFile, char *buf, int count, DWORD *outCount,
\r
7517 if( count <= 0 ) {
\r
7518 if (appData.debugMode) {
\r
7519 fprintf( debugFP, "DoReadFile: trying to read past end of buffer, overflow = %d\n", count );
\r
7522 return ERROR_INVALID_USER_BUFFER;
\r
7525 ResetEvent(ovl->hEvent);
\r
7526 ovl->Offset = ovl->OffsetHigh = 0;
\r
7527 ok = ReadFile(hFile, buf, count, outCount, ovl);
\r
7531 err = GetLastError();
\r
7532 if (err == ERROR_IO_PENDING) {
\r
7533 ok = GetOverlappedResult(hFile, ovl, outCount, TRUE);
\r
7537 err = GetLastError();
\r
7544 DoWriteFile(HANDLE hFile, char *buf, int count, DWORD *outCount,
\r
7549 ResetEvent(ovl->hEvent);
\r
7550 ovl->Offset = ovl->OffsetHigh = 0;
\r
7551 ok = WriteFile(hFile, buf, count, outCount, ovl);
\r
7555 err = GetLastError();
\r
7556 if (err == ERROR_IO_PENDING) {
\r
7557 ok = GetOverlappedResult(hFile, ovl, outCount, TRUE);
\r
7561 err = GetLastError();
\r
7567 /* [AS] If input is line by line and a line exceed the buffer size, force an error */
\r
7568 void CheckForInputBufferFull( InputSource * is )
\r
7570 if( is->lineByLine && (is->next - is->buf) >= INPUT_SOURCE_BUF_SIZE ) {
\r
7571 /* Look for end of line */
\r
7572 char * p = is->buf;
\r
7574 while( p < is->next && *p != '\n' ) {
\r
7578 if( p >= is->next ) {
\r
7579 if (appData.debugMode) {
\r
7580 fprintf( debugFP, "Input line exceeded buffer size (source id=%lu)\n", is->id );
\r
7583 is->error = ERROR_BROKEN_PIPE; /* [AS] Just any non-successful code! */
\r
7584 is->count = (DWORD) -1;
\r
7585 is->next = is->buf;
\r
7591 InputThread(LPVOID arg)
\r
7596 is = (InputSource *) arg;
\r
7597 ovl.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
\r
7598 ovl.Internal = ovl.InternalHigh = ovl.Offset = ovl.OffsetHigh = 0;
\r
7599 while (is->hThread != NULL) {
\r
7600 is->error = DoReadFile(is->hFile, is->next,
\r
7601 INPUT_SOURCE_BUF_SIZE - (is->next - is->buf),
\r
7602 &is->count, &ovl);
\r
7603 if (is->error == NO_ERROR) {
\r
7604 is->next += is->count;
\r
7606 if (is->error == ERROR_BROKEN_PIPE) {
\r
7607 /* Correct for MS brain damage. EOF reading a pipe is not an error. */
\r
7610 is->count = (DWORD) -1;
\r
7611 /* [AS] The (is->count <= 0) check below is not useful for unsigned values! */
\r
7616 CheckForInputBufferFull( is );
\r
7618 SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);
\r
7620 if( is->count == ((DWORD) -1) ) break; /* [AS] */
\r
7622 if (is->count <= 0) break; /* Quit on EOF or error */
\r
7625 CloseHandle(ovl.hEvent);
\r
7626 CloseHandle(is->hFile);
\r
7628 if (appData.debugMode) {
\r
7629 fprintf( debugFP, "Input thread terminated (id=%lu, error=%d, count=%ld)\n", is->id, is->error, is->count );
\r
7636 /* Windows 95 beta 2 won't let you do overlapped i/o on a console or pipe */
\r
7638 NonOvlInputThread(LPVOID arg)
\r
7645 is = (InputSource *) arg;
\r
7646 while (is->hThread != NULL) {
\r
7647 is->error = ReadFile(is->hFile, is->next,
\r
7648 INPUT_SOURCE_BUF_SIZE - (is->next - is->buf),
\r
7649 &is->count, NULL) ? NO_ERROR : GetLastError();
\r
7650 if (is->error == NO_ERROR) {
\r
7651 /* Change CRLF to LF */
\r
7652 if (is->next > is->buf) {
\r
7654 i = is->count + 1;
\r
7662 if (prev == '\r' && *p == '\n') {
\r
7674 if (is->error == ERROR_BROKEN_PIPE) {
\r
7675 /* Correct for MS brain damage. EOF reading a pipe is not an error. */
\r
7678 is->count = (DWORD) -1;
\r
7682 CheckForInputBufferFull( is );
\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 error */
\r
7690 CloseHandle(is->hFile);
\r
7695 SocketInputThread(LPVOID arg)
\r
7699 is = (InputSource *) arg;
\r
7700 while (is->hThread != NULL) {
\r
7701 is->count = recv(is->sock, is->buf, INPUT_SOURCE_BUF_SIZE, 0);
\r
7702 if ((int)is->count == SOCKET_ERROR) {
\r
7703 is->count = (DWORD) -1;
\r
7704 is->error = WSAGetLastError();
\r
7706 is->error = NO_ERROR;
\r
7707 is->next += is->count;
\r
7708 if (is->count == 0 && is->second == is) {
\r
7709 /* End of file on stderr; quit with no message */
\r
7713 SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);
\r
7715 if( is->count == ((DWORD) -1) ) break; /* [AS] */
\r
7717 if (is->count <= 0) break; /* Quit on EOF or error */
\r
7723 InputEvent(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
\r
7727 is = (InputSource *) lParam;
\r
7728 if (is->lineByLine) {
\r
7729 /* Feed in lines one by one */
\r
7730 char *p = is->buf;
\r
7732 while (q < is->next) {
\r
7733 if (*q++ == '\n') {
\r
7734 (is->func)(is, is->closure, p, q - p, NO_ERROR);
\r
7739 /* Move any partial line to the start of the buffer */
\r
7741 while (p < is->next) {
\r
7746 if (is->error != NO_ERROR || is->count == 0) {
\r
7747 /* Notify backend of the error. Note: If there was a partial
\r
7748 line at the end, it is not flushed through. */
\r
7749 (is->func)(is, is->closure, is->buf, is->count, is->error);
\r
7752 /* Feed in the whole chunk of input at once */
\r
7753 (is->func)(is, is->closure, is->buf, is->count, is->error);
\r
7754 is->next = is->buf;
\r
7758 /*---------------------------------------------------------------------------*\
\r
7760 * Menu enables. Used when setting various modes.
\r
7762 \*---------------------------------------------------------------------------*/
\r
7770 GreyRevert(Boolean grey)
\r
7771 { // [HGM] vari: for retracting variations in local mode
\r
7772 HMENU hmenu = GetMenu(hwndMain);
\r
7773 EnableMenuItem(hmenu, IDM_Revert, MF_BYCOMMAND|(grey ? MF_GRAYED : MF_ENABLED));
\r
7774 EnableMenuItem(hmenu, IDM_Annotate, MF_BYCOMMAND|(grey ? MF_GRAYED : MF_ENABLED));
\r
7778 SetMenuEnables(HMENU hmenu, Enables *enab)
\r
7780 while (enab->item > 0) {
\r
7781 (void) EnableMenuItem(hmenu, enab->item, enab->flags);
\r
7786 Enables gnuEnables[] = {
\r
7787 { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },
\r
7788 { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },
\r
7789 { IDM_IcsClient, MF_BYCOMMAND|MF_GRAYED },
\r
7790 { IDM_Accept, MF_BYCOMMAND|MF_GRAYED },
\r
7791 { IDM_Decline, MF_BYCOMMAND|MF_GRAYED },
\r
7792 { IDM_Rematch, MF_BYCOMMAND|MF_GRAYED },
\r
7793 { IDM_Adjourn, MF_BYCOMMAND|MF_GRAYED },
\r
7794 { IDM_StopExamining, MF_BYCOMMAND|MF_GRAYED },
\r
7795 { IDM_StopObserving, MF_BYCOMMAND|MF_GRAYED },
\r
7796 { IDM_Upload, MF_BYCOMMAND|MF_GRAYED },
\r
7797 { IDM_Revert, MF_BYCOMMAND|MF_GRAYED },
\r
7798 { IDM_Annotate, MF_BYCOMMAND|MF_GRAYED },
\r
7799 { IDM_NewChat, MF_BYCOMMAND|MF_GRAYED },
\r
7803 Enables icsEnables[] = {
\r
7804 { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },
\r
7805 { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },
\r
7806 { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },
\r
7807 { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },
\r
7808 { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },
\r
7809 { IDM_Match, MF_BYCOMMAND|MF_GRAYED },
\r
7810 { IDM_MachineBoth, MF_BYCOMMAND|MF_GRAYED },
\r
7811 { IDM_AnalysisMode, MF_BYCOMMAND|MF_ENABLED },
\r
7812 { IDM_AnalyzeFile, MF_BYCOMMAND|MF_GRAYED },
\r
7813 { IDM_TimeControl, MF_BYCOMMAND|MF_GRAYED },
\r
7814 { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },
\r
7815 { IDM_Hint, MF_BYCOMMAND|MF_GRAYED },
\r
7816 { IDM_Book, MF_BYCOMMAND|MF_GRAYED },
\r
7817 { IDM_IcsOptions, MF_BYCOMMAND|MF_ENABLED },
\r
7818 { IDM_Engine1Options, MF_BYCOMMAND|MF_GRAYED },
\r
7819 { IDM_Engine2Options, MF_BYCOMMAND|MF_GRAYED },
\r
7820 { IDM_Annotate, MF_BYCOMMAND|MF_GRAYED },
\r
7825 Enables zippyEnables[] = {
\r
7826 { IDM_MoveNow, MF_BYCOMMAND|MF_ENABLED },
\r
7827 { IDM_Hint, MF_BYCOMMAND|MF_ENABLED },
\r
7828 { IDM_Book, MF_BYCOMMAND|MF_ENABLED },
\r
7829 { IDM_Engine1Options, MF_BYCOMMAND|MF_ENABLED },
\r
7834 Enables ncpEnables[] = {
\r
7835 { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },
\r
7836 { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },
\r
7837 { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },
\r
7838 { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },
\r
7839 { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },
\r
7840 { IDM_Match, MF_BYCOMMAND|MF_GRAYED },
\r
7841 { IDM_AnalysisMode, MF_BYCOMMAND|MF_GRAYED },
\r
7842 { IDM_AnalyzeFile, MF_BYCOMMAND|MF_GRAYED },
\r
7843 { IDM_IcsClient, MF_BYCOMMAND|MF_GRAYED },
\r
7844 { ACTION_POS, MF_BYPOSITION|MF_GRAYED },
\r
7845 { IDM_Revert, MF_BYCOMMAND|MF_GRAYED },
\r
7846 { IDM_Annotate, MF_BYCOMMAND|MF_GRAYED },
\r
7847 { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },
\r
7848 { IDM_RetractMove, MF_BYCOMMAND|MF_GRAYED },
\r
7849 { IDM_TimeControl, MF_BYCOMMAND|MF_GRAYED },
\r
7850 { IDM_Hint, MF_BYCOMMAND|MF_GRAYED },
\r
7851 { IDM_Book, MF_BYCOMMAND|MF_GRAYED },
\r
7852 { IDM_MachineBoth, MF_BYCOMMAND|MF_GRAYED },
\r
7853 { IDM_NewChat, MF_BYCOMMAND|MF_GRAYED },
\r
7854 { IDM_Engine1Options, MF_BYCOMMAND|MF_GRAYED },
\r
7855 { IDM_Engine2Options, MF_BYCOMMAND|MF_GRAYED },
\r
7859 Enables trainingOnEnables[] = {
\r
7860 { IDM_EditComment, MF_BYCOMMAND|MF_GRAYED },
\r
7861 { IDM_Comment, MF_BYCOMMAND|MF_GRAYED },
\r
7862 { IDM_Pause, MF_BYCOMMAND|MF_GRAYED },
\r
7863 { IDM_Forward, MF_BYCOMMAND|MF_GRAYED },
\r
7864 { IDM_Backward, MF_BYCOMMAND|MF_GRAYED },
\r
7865 { IDM_ToEnd, MF_BYCOMMAND|MF_GRAYED },
\r
7866 { IDM_ToStart, MF_BYCOMMAND|MF_GRAYED },
\r
7867 { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },
\r
7868 { IDM_TruncateGame, MF_BYCOMMAND|MF_GRAYED },
\r
7872 Enables trainingOffEnables[] = {
\r
7873 { IDM_EditComment, MF_BYCOMMAND|MF_ENABLED },
\r
7874 { IDM_Comment, MF_BYCOMMAND|MF_ENABLED },
\r
7875 { IDM_Pause, MF_BYCOMMAND|MF_ENABLED },
\r
7876 { IDM_Forward, MF_BYCOMMAND|MF_ENABLED },
\r
7877 { IDM_Backward, MF_BYCOMMAND|MF_ENABLED },
\r
7878 { IDM_ToEnd, MF_BYCOMMAND|MF_ENABLED },
\r
7879 { IDM_ToStart, MF_BYCOMMAND|MF_ENABLED },
\r
7880 { IDM_MoveNow, MF_BYCOMMAND|MF_ENABLED },
\r
7881 { IDM_TruncateGame, MF_BYCOMMAND|MF_ENABLED },
\r
7885 /* These modify either ncpEnables or gnuEnables */
\r
7886 Enables cmailEnables[] = {
\r
7887 { IDM_MailMove, MF_BYCOMMAND|MF_ENABLED },
\r
7888 { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_ENABLED },
\r
7889 { ACTION_POS, MF_BYPOSITION|MF_ENABLED },
\r
7890 { IDM_CallFlag, MF_BYCOMMAND|MF_GRAYED },
\r
7891 { IDM_Draw, MF_BYCOMMAND|MF_ENABLED },
\r
7892 { IDM_Adjourn, MF_BYCOMMAND|MF_GRAYED },
\r
7893 { IDM_Abort, MF_BYCOMMAND|MF_GRAYED },
\r
7897 Enables machineThinkingEnables[] = {
\r
7898 { IDM_LoadGame, MF_BYCOMMAND|MF_GRAYED },
\r
7899 { IDM_LoadNextGame, MF_BYCOMMAND|MF_GRAYED },
\r
7900 { IDM_LoadPrevGame, MF_BYCOMMAND|MF_GRAYED },
\r
7901 { IDM_ReloadGame, MF_BYCOMMAND|MF_GRAYED },
\r
7902 { IDM_PasteGame, MF_BYCOMMAND|MF_GRAYED },
\r
7903 { IDM_LoadPosition, MF_BYCOMMAND|MF_GRAYED },
\r
7904 { IDM_LoadNextPosition, MF_BYCOMMAND|MF_GRAYED },
\r
7905 { IDM_LoadPrevPosition, MF_BYCOMMAND|MF_GRAYED },
\r
7906 { IDM_ReloadPosition, MF_BYCOMMAND|MF_GRAYED },
\r
7907 { IDM_PastePosition, MF_BYCOMMAND|MF_GRAYED },
\r
7908 { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },
\r
7909 { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },
\r
7910 { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },
\r
7911 { IDM_Match, MF_BYCOMMAND|MF_GRAYED },
\r
7912 { IDM_TypeInMove, MF_BYCOMMAND|MF_GRAYED },
\r
7913 { IDM_RetractMove, MF_BYCOMMAND|MF_GRAYED },
\r
7917 Enables userThinkingEnables[] = {
\r
7918 { IDM_LoadGame, MF_BYCOMMAND|MF_ENABLED },
\r
7919 { IDM_LoadNextGame, MF_BYCOMMAND|MF_ENABLED },
\r
7920 { IDM_LoadPrevGame, MF_BYCOMMAND|MF_ENABLED },
\r
7921 { IDM_ReloadGame, MF_BYCOMMAND|MF_ENABLED },
\r
7922 { IDM_PasteGame, MF_BYCOMMAND|MF_ENABLED },
\r
7923 { IDM_LoadPosition, MF_BYCOMMAND|MF_ENABLED },
\r
7924 { IDM_LoadNextPosition, MF_BYCOMMAND|MF_ENABLED },
\r
7925 { IDM_LoadPrevPosition, MF_BYCOMMAND|MF_ENABLED },
\r
7926 { IDM_ReloadPosition, MF_BYCOMMAND|MF_ENABLED },
\r
7927 { IDM_PastePosition, MF_BYCOMMAND|MF_ENABLED },
\r
7928 { IDM_MachineWhite, MF_BYCOMMAND|MF_ENABLED },
\r
7929 { IDM_MachineBlack, MF_BYCOMMAND|MF_ENABLED },
\r
7930 { IDM_TwoMachines, MF_BYCOMMAND|MF_ENABLED },
\r
7931 { IDM_Match, MF_BYCOMMAND|MF_ENABLED },
\r
7932 { IDM_TypeInMove, MF_BYCOMMAND|MF_ENABLED },
\r
7933 { IDM_RetractMove, MF_BYCOMMAND|MF_ENABLED },
\r
7937 /*---------------------------------------------------------------------------*\
\r
7939 * Front-end interface functions exported by XBoard.
\r
7940 * Functions appear in same order as prototypes in frontend.h.
\r
7942 \*---------------------------------------------------------------------------*/
\r
7946 static UINT prevChecked = 0;
\r
7947 static int prevPausing = 0;
\r
7950 if (pausing != prevPausing) {
\r
7951 prevPausing = pausing;
\r
7952 (void) CheckMenuItem(GetMenu(hwndMain), IDM_Pause,
\r
7953 MF_BYCOMMAND|(pausing ? MF_CHECKED : MF_UNCHECKED));
\r
7954 if (hwndPause) SetWindowText(hwndPause, pausing ? "C" : "P");
\r
7957 switch (gameMode) {
\r
7958 case BeginningOfGame:
\r
7959 if (appData.icsActive)
\r
7960 nowChecked = IDM_IcsClient;
\r
7961 else if (appData.noChessProgram)
\r
7962 nowChecked = IDM_EditGame;
\r
7964 nowChecked = IDM_MachineBlack;
\r
7966 case MachinePlaysBlack:
\r
7967 nowChecked = IDM_MachineBlack;
\r
7969 case MachinePlaysWhite:
\r
7970 nowChecked = IDM_MachineWhite;
\r
7972 case TwoMachinesPlay:
\r
7973 nowChecked = matchMode ? IDM_Match : IDM_TwoMachines; // [HGM] match
\r
7976 nowChecked = IDM_AnalysisMode;
\r
7979 nowChecked = IDM_AnalyzeFile;
\r
7982 nowChecked = IDM_EditGame;
\r
7984 case PlayFromGameFile:
\r
7985 nowChecked = IDM_LoadGame;
\r
7987 case EditPosition:
\r
7988 nowChecked = IDM_EditPosition;
\r
7991 nowChecked = IDM_Training;
\r
7993 case IcsPlayingWhite:
\r
7994 case IcsPlayingBlack:
\r
7995 case IcsObserving:
\r
7997 nowChecked = IDM_IcsClient;
\r
8004 if (prevChecked != 0)
\r
8005 (void) CheckMenuItem(GetMenu(hwndMain),
\r
8006 prevChecked, MF_BYCOMMAND|MF_UNCHECKED);
\r
8007 if (nowChecked != 0)
\r
8008 (void) CheckMenuItem(GetMenu(hwndMain),
\r
8009 nowChecked, MF_BYCOMMAND|MF_CHECKED);
\r
8011 if (nowChecked == IDM_LoadGame || nowChecked == IDM_Training) {
\r
8012 (void) EnableMenuItem(GetMenu(hwndMain), IDM_Training,
\r
8013 MF_BYCOMMAND|MF_ENABLED);
\r
8015 (void) EnableMenuItem(GetMenu(hwndMain),
\r
8016 IDM_Training, MF_BYCOMMAND|MF_GRAYED);
\r
8019 prevChecked = nowChecked;
\r
8021 /* [DM] icsEngineAnalyze - Do a sceure check too */
\r
8022 if (appData.icsActive) {
\r
8023 if (appData.icsEngineAnalyze) {
\r
8024 (void) CheckMenuItem(GetMenu(hwndMain), IDM_AnalysisMode,
\r
8025 MF_BYCOMMAND|MF_CHECKED);
\r
8027 (void) CheckMenuItem(GetMenu(hwndMain), IDM_AnalysisMode,
\r
8028 MF_BYCOMMAND|MF_UNCHECKED);
\r
8031 DisplayLogos(); // [HGM] logos: mode change could have altered logos
\r
8037 HMENU hmenu = GetMenu(hwndMain);
\r
8038 SetMenuEnables(hmenu, icsEnables);
\r
8039 EnableMenuItem(GetSubMenu(hmenu, OPTIONS_POS), ICS_POS,
\r
8040 MF_BYPOSITION|MF_ENABLED);
\r
8042 if (appData.zippyPlay) {
\r
8043 SetMenuEnables(hmenu, zippyEnables);
\r
8044 if (!appData.noChessProgram) /* [DM] icsEngineAnalyze */
\r
8045 (void) EnableMenuItem(GetMenu(hwndMain), IDM_AnalysisMode,
\r
8046 MF_BYCOMMAND|MF_ENABLED);
\r
8054 SetMenuEnables(GetMenu(hwndMain), gnuEnables);
\r
8060 HMENU hmenu = GetMenu(hwndMain);
\r
8061 SetMenuEnables(hmenu, ncpEnables);
\r
8062 EnableMenuItem(GetSubMenu(hmenu, OPTIONS_POS), SOUNDS_POS,
\r
8063 MF_BYPOSITION|MF_GRAYED);
\r
8064 DrawMenuBar(hwndMain);
\r
8070 SetMenuEnables(GetMenu(hwndMain), cmailEnables);
\r
8074 SetTrainingModeOn()
\r
8077 SetMenuEnables(GetMenu(hwndMain), trainingOnEnables);
\r
8078 for (i = 0; i < N_BUTTONS; i++) {
\r
8079 if (buttonDesc[i].hwnd != NULL)
\r
8080 EnableWindow(buttonDesc[i].hwnd, FALSE);
\r
8085 VOID SetTrainingModeOff()
\r
8088 SetMenuEnables(GetMenu(hwndMain), trainingOffEnables);
\r
8089 for (i = 0; i < N_BUTTONS; i++) {
\r
8090 if (buttonDesc[i].hwnd != NULL)
\r
8091 EnableWindow(buttonDesc[i].hwnd, TRUE);
\r
8097 SetUserThinkingEnables()
\r
8099 SetMenuEnables(GetMenu(hwndMain), userThinkingEnables);
\r
8103 SetMachineThinkingEnables()
\r
8105 HMENU hMenu = GetMenu(hwndMain);
\r
8106 int flags = MF_BYCOMMAND|MF_ENABLED;
\r
8108 SetMenuEnables(hMenu, machineThinkingEnables);
\r
8110 if (gameMode == MachinePlaysBlack) {
\r
8111 (void)EnableMenuItem(hMenu, IDM_MachineBlack, flags);
\r
8112 } else if (gameMode == MachinePlaysWhite) {
\r
8113 (void)EnableMenuItem(hMenu, IDM_MachineWhite, flags);
\r
8114 } else if (gameMode == TwoMachinesPlay) {
\r
8115 (void)EnableMenuItem(hMenu, matchMode ? IDM_Match : IDM_TwoMachines, flags); // [HGM] match
\r
8121 DisplayTitle(char *str)
\r
8123 char title[MSG_SIZ], *host;
\r
8124 if (str[0] != NULLCHAR) {
\r
8125 safeStrCpy(title, str, sizeof(title)/sizeof(title[0]) );
\r
8126 } else if (appData.icsActive) {
\r
8127 if (appData.icsCommPort[0] != NULLCHAR)
\r
8130 host = appData.icsHost;
\r
8131 snprintf(title, MSG_SIZ, "%s: %s", szTitle, host);
\r
8132 } else if (appData.noChessProgram) {
\r
8133 safeStrCpy(title, szTitle, sizeof(title)/sizeof(title[0]) );
\r
8135 safeStrCpy(title, szTitle, sizeof(title)/sizeof(title[0]) );
\r
8136 strcat(title, ": ");
\r
8137 strcat(title, first.tidy);
\r
8139 SetWindowText(hwndMain, title);
\r
8144 DisplayMessage(char *str1, char *str2)
\r
8148 int remain = MESSAGE_TEXT_MAX - 1;
\r
8151 moveErrorMessageUp = FALSE; /* turned on later by caller if needed */
\r
8152 messageText[0] = NULLCHAR;
\r
8154 len = strlen(str1);
\r
8155 if (len > remain) len = remain;
\r
8156 strncpy(messageText, str1, len);
\r
8157 messageText[len] = NULLCHAR;
\r
8160 if (*str2 && remain >= 2) {
\r
8162 strcat(messageText, " ");
\r
8165 len = strlen(str2);
\r
8166 if (len > remain) len = remain;
\r
8167 strncat(messageText, str2, len);
\r
8169 messageText[MESSAGE_TEXT_MAX - 1] = NULLCHAR;
\r
8171 if (hwndMain == NULL || IsIconic(hwndMain)) return;
\r
8175 hdc = GetDC(hwndMain);
\r
8176 oldFont = SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);
\r
8177 ExtTextOut(hdc, messageRect.left, messageRect.top, ETO_CLIPPED|ETO_OPAQUE,
\r
8178 &messageRect, messageText, strlen(messageText), NULL);
\r
8179 (void) SelectObject(hdc, oldFont);
\r
8180 (void) ReleaseDC(hwndMain, hdc);
\r
8184 DisplayError(char *str, int error)
\r
8186 char buf[MSG_SIZ*2], buf2[MSG_SIZ];
\r
8190 safeStrCpy(buf, str, sizeof(buf)/sizeof(buf[0]) );
\r
8192 len = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,
\r
8193 NULL, error, LANG_NEUTRAL,
\r
8194 (LPSTR) buf2, MSG_SIZ, NULL);
\r
8196 snprintf(buf, 2*MSG_SIZ, "%s:\n%s", str, buf2);
\r
8198 ErrorMap *em = errmap;
\r
8199 while (em->err != 0 && em->err != error) em++;
\r
8200 if (em->err != 0) {
\r
8201 snprintf(buf, 2*MSG_SIZ, "%s:\n%s", str, em->msg);
\r
8203 snprintf(buf, 2*MSG_SIZ, "%s:\nError code %d", str, error);
\r
8208 ErrorPopUp(_("Error"), buf);
\r
8213 DisplayMoveError(char *str)
\r
8215 fromX = fromY = -1;
\r
8216 ClearHighlights();
\r
8217 DrawPosition(FALSE, NULL);
\r
8218 if (appData.popupMoveErrors) {
\r
8219 ErrorPopUp(_("Error"), str);
\r
8221 DisplayMessage(str, "");
\r
8222 moveErrorMessageUp = TRUE;
\r
8227 DisplayFatalError(char *str, int error, int exitStatus)
\r
8229 char buf[2*MSG_SIZ], buf2[MSG_SIZ];
\r
8231 char *label = exitStatus ? _("Fatal Error") : _("Exiting");
\r
8234 len = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,
\r
8235 NULL, error, LANG_NEUTRAL,
\r
8236 (LPSTR) buf2, MSG_SIZ, NULL);
\r
8238 snprintf(buf, 2*MSG_SIZ, "%s:\n%s", str, buf2);
\r
8240 ErrorMap *em = errmap;
\r
8241 while (em->err != 0 && em->err != error) em++;
\r
8242 if (em->err != 0) {
\r
8243 snprintf(buf, 2*MSG_SIZ, "%s:\n%s", str, em->msg);
\r
8245 snprintf(buf, 2*MSG_SIZ, "%s:\nError code %d", str, error);
\r
8250 if (appData.debugMode) {
\r
8251 fprintf(debugFP, "%s: %s\n", label, str);
\r
8253 if (appData.popupExitMessage) {
\r
8254 (void) MessageBox(hwndMain, str, label, MB_OK|
\r
8255 (exitStatus ? MB_ICONSTOP : MB_ICONINFORMATION));
\r
8257 ExitEvent(exitStatus);
\r
8262 DisplayInformation(char *str)
\r
8264 (void) MessageBox(hwndMain, str, _("Information"), MB_OK|MB_ICONINFORMATION);
\r
8269 DisplayNote(char *str)
\r
8271 ErrorPopUp(_("Note"), str);
\r
8276 char *title, *question, *replyPrefix;
\r
8281 QuestionDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
8283 static QuestionParams *qp;
\r
8284 char reply[MSG_SIZ];
\r
8287 switch (message) {
\r
8288 case WM_INITDIALOG:
\r
8289 qp = (QuestionParams *) lParam;
\r
8290 CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));
\r
8291 Translate(hDlg, DLG_Question);
\r
8292 SetWindowText(hDlg, qp->title);
\r
8293 SetDlgItemText(hDlg, OPT_QuestionText, qp->question);
\r
8294 SetFocus(GetDlgItem(hDlg, OPT_QuestionInput));
\r
8298 switch (LOWORD(wParam)) {
\r
8300 safeStrCpy(reply, qp->replyPrefix, sizeof(reply)/sizeof(reply[0]) );
\r
8301 if (*reply) strcat(reply, " ");
\r
8302 len = strlen(reply);
\r
8303 GetDlgItemText(hDlg, OPT_QuestionInput, reply + len, sizeof(reply) - len);
\r
8304 strcat(reply, "\n");
\r
8305 OutputToProcess(qp->pr, reply, strlen(reply), &err);
\r
8306 EndDialog(hDlg, TRUE);
\r
8307 if (err) DisplayFatalError(_("Error writing to chess program"), err, 1);
\r
8310 EndDialog(hDlg, FALSE);
\r
8321 AskQuestion(char* title, char *question, char *replyPrefix, ProcRef pr)
\r
8323 QuestionParams qp;
\r
8327 qp.question = question;
\r
8328 qp.replyPrefix = replyPrefix;
\r
8330 lpProc = MakeProcInstance((FARPROC)QuestionDialog, hInst);
\r
8331 DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_Question),
\r
8332 hwndMain, (DLGPROC)lpProc, (LPARAM)&qp);
\r
8333 FreeProcInstance(lpProc);
\r
8336 /* [AS] Pick FRC position */
\r
8337 LRESULT CALLBACK NewGameFRC_Proc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
8339 static int * lpIndexFRC;
\r
8345 case WM_INITDIALOG:
\r
8346 lpIndexFRC = (int *) lParam;
\r
8348 CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));
\r
8349 Translate(hDlg, DLG_NewGameFRC);
\r
8351 SendDlgItemMessage( hDlg, IDC_NFG_Edit, EM_SETLIMITTEXT, sizeof(buf)-1, 0 );
\r
8352 SetDlgItemInt( hDlg, IDC_NFG_Edit, *lpIndexFRC, TRUE );
\r
8353 SendDlgItemMessage( hDlg, IDC_NFG_Edit, EM_SETSEL, 0, -1 );
\r
8354 SetFocus(GetDlgItem(hDlg, IDC_NFG_Edit));
\r
8359 switch( LOWORD(wParam) ) {
\r
8361 *lpIndexFRC = GetDlgItemInt(hDlg, IDC_NFG_Edit, &index_is_ok, TRUE );
\r
8362 EndDialog( hDlg, 0 );
\r
8363 shuffleOpenings = TRUE; /* [HGM] shuffle: switch shuffling on for as long as we stay in current variant */
\r
8366 EndDialog( hDlg, 1 );
\r
8368 case IDC_NFG_Edit:
\r
8369 if( HIWORD(wParam) == EN_CHANGE ) {
\r
8370 GetDlgItemInt(hDlg, IDC_NFG_Edit, &index_is_ok, TRUE );
\r
8372 EnableWindow( GetDlgItem(hDlg, IDOK), index_is_ok );
\r
8375 case IDC_NFG_Random:
\r
8376 snprintf( buf, sizeof(buf)/sizeof(buf[0]), "%d", myrandom() ); /* [HGM] shuffle: no longer limit to 960 */
\r
8377 SetDlgItemText(hDlg, IDC_NFG_Edit, buf );
\r
8390 int index = appData.defaultFrcPosition;
\r
8391 FARPROC lpProc = MakeProcInstance( (FARPROC) NewGameFRC_Proc, hInst );
\r
8393 result = DialogBoxParam( hInst, MAKEINTRESOURCE(DLG_NewGameFRC), hwndMain, (DLGPROC)lpProc, (LPARAM)&index );
\r
8395 if( result == 0 ) {
\r
8396 appData.defaultFrcPosition = index;
\r
8402 /* [AS] Game list options. Refactored by HGM */
\r
8404 HWND gameListOptionsDialog;
\r
8406 // low-level front-end: clear text edit / list widget
\r
8410 SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_RESETCONTENT, 0, 0 );
\r
8413 // low-level front-end: clear text edit / list widget
\r
8415 GLT_DeSelectList()
\r
8417 SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_SETCURSEL, 0, 0 );
\r
8420 // low-level front-end: append line to text edit / list widget
\r
8422 GLT_AddToList( char *name )
\r
8425 SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_ADDSTRING, 0, (LPARAM) name );
\r
8429 // low-level front-end: get line from text edit / list widget
\r
8431 GLT_GetFromList( int index, char *name )
\r
8434 if( SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_GETTEXT, index, (LPARAM) name ) != LB_ERR )
\r
8440 void GLT_MoveSelection( HWND hDlg, int delta )
\r
8442 int idx1 = (int) SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETCURSEL, 0, 0 );
\r
8443 int idx2 = idx1 + delta;
\r
8444 int count = (int) SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETCOUNT, 0, 0 );
\r
8446 if( idx1 >=0 && idx1 < count && idx2 >= 0 && idx2 < count ) {
\r
8449 SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETTEXT, idx1, (LPARAM) buf );
\r
8450 SendDlgItemMessage( hDlg, IDC_GameListTags, LB_DELETESTRING, idx1, 0 );
\r
8451 SendDlgItemMessage( hDlg, IDC_GameListTags, LB_INSERTSTRING, idx2, (LPARAM) buf );
\r
8452 SendDlgItemMessage( hDlg, IDC_GameListTags, LB_SETCURSEL, idx2, 0 );
\r
8456 LRESULT CALLBACK GameListOptions_Proc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
8460 case WM_INITDIALOG:
\r
8461 gameListOptionsDialog = hDlg; // [HGM] pass through global to keep out off back-end
\r
8463 CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));
\r
8464 Translate(hDlg, DLG_GameListOptions);
\r
8466 /* Initialize list */
\r
8467 GLT_TagsToList( lpUserGLT );
\r
8469 SetFocus( GetDlgItem(hDlg, IDC_GameListTags) );
\r
8474 switch( LOWORD(wParam) ) {
\r
8477 EndDialog( hDlg, 0 );
\r
8480 EndDialog( hDlg, 1 );
\r
8483 case IDC_GLT_Default:
\r
8484 GLT_TagsToList( GLT_DEFAULT_TAGS );
\r
8487 case IDC_GLT_Restore:
\r
8488 GLT_TagsToList( appData.gameListTags );
\r
8492 GLT_MoveSelection( hDlg, -1 );
\r
8495 case IDC_GLT_Down:
\r
8496 GLT_MoveSelection( hDlg, +1 );
\r
8506 int GameListOptions()
\r
8509 FARPROC lpProc = MakeProcInstance( (FARPROC) GameListOptions_Proc, hInst );
\r
8511 safeStrCpy( lpUserGLT, appData.gameListTags ,LPUSERGLT_SIZE );
\r
8513 result = DialogBoxParam( hInst, MAKEINTRESOURCE(DLG_GameListOptions), hwndMain, (DLGPROC)lpProc, (LPARAM)lpUserGLT );
\r
8515 if( result == 0 ) {
\r
8516 /* [AS] Memory leak here! */
\r
8517 appData.gameListTags = strdup( lpUserGLT );
\r
8524 DisplayIcsInteractionTitle(char *str)
\r
8526 char consoleTitle[MSG_SIZ];
\r
8528 snprintf(consoleTitle, MSG_SIZ, "%s: %s", szConsoleTitle, str);
\r
8529 SetWindowText(hwndConsole, consoleTitle);
\r
8533 DrawPosition(int fullRedraw, Board board)
\r
8535 HDCDrawPosition(NULL, (BOOLEAN) fullRedraw, board);
\r
8538 void NotifyFrontendLogin()
\r
8541 UpdateICSWidth(GetDlgItem(hwndConsole, OPT_ConsoleText));
\r
8547 fromX = fromY = -1;
\r
8548 if (dragInfo.pos.x != -1 || dragInfo.pos.y != -1) {
\r
8549 dragInfo.pos.x = dragInfo.pos.y = -1;
\r
8550 dragInfo.pos.x = dragInfo.pos.y = -1;
\r
8551 dragInfo.lastpos = dragInfo.pos;
\r
8552 dragInfo.start.x = dragInfo.start.y = -1;
\r
8553 dragInfo.from = dragInfo.start;
\r
8555 DrawPosition(TRUE, NULL);
\r
8562 CommentPopUp(char *title, char *str)
\r
8564 HWND hwnd = GetActiveWindow();
\r
8565 EitherCommentPopUp(currentMove, title, str, FALSE); // [HGM] vari: fake move index, rather than 0
\r
8567 SetActiveWindow(hwnd);
\r
8571 CommentPopDown(void)
\r
8573 CheckMenuItem(GetMenu(hwndMain), IDM_Comment, MF_UNCHECKED);
\r
8574 if (commentDialog) {
\r
8575 ShowWindow(commentDialog, SW_HIDE);
\r
8577 commentUp = FALSE;
\r
8581 EditCommentPopUp(int index, char *title, char *str)
\r
8583 EitherCommentPopUp(index, title, str, TRUE);
\r
8590 MyPlaySound(&sounds[(int)SoundMove]);
\r
8593 VOID PlayIcsWinSound()
\r
8595 MyPlaySound(&sounds[(int)SoundIcsWin]);
\r
8598 VOID PlayIcsLossSound()
\r
8600 MyPlaySound(&sounds[(int)SoundIcsLoss]);
\r
8603 VOID PlayIcsDrawSound()
\r
8605 MyPlaySound(&sounds[(int)SoundIcsDraw]);
\r
8608 VOID PlayIcsUnfinishedSound()
\r
8610 MyPlaySound(&sounds[(int)SoundIcsUnfinished]);
\r
8616 MyPlaySound(&sounds[(int)SoundAlarm]);
\r
8624 consoleEcho = TRUE;
\r
8625 hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
8626 SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&consoleCF);
\r
8627 SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);
\r
8636 consoleEcho = FALSE;
\r
8637 hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
8638 /* This works OK: set text and background both to the same color */
\r
8640 cf.crTextColor = COLOR_ECHOOFF;
\r
8641 SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf);
\r
8642 SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, cf.crTextColor);
\r
8645 /* No Raw()...? */
\r
8647 void Colorize(ColorClass cc, int continuation)
\r
8649 currentColorClass = cc;
\r
8650 consoleCF.dwMask = CFM_COLOR|CFM_BOLD|CFM_ITALIC|CFM_UNDERLINE|CFM_STRIKEOUT;
\r
8651 consoleCF.crTextColor = textAttribs[cc].color;
\r
8652 consoleCF.dwEffects = textAttribs[cc].effects;
\r
8653 if (!continuation) MyPlaySound(&textAttribs[cc].sound);
\r
8659 static char buf[MSG_SIZ];
\r
8660 DWORD bufsiz = MSG_SIZ;
\r
8662 if(appData.userName != NULL && appData.userName[0] != 0) {
\r
8663 return appData.userName; /* [HGM] username: prefer name selected by user over his system login */
\r
8665 if (!GetUserName(buf, &bufsiz)) {
\r
8666 /*DisplayError("Error getting user name", GetLastError());*/
\r
8667 safeStrCpy(buf, _("User"), sizeof(buf)/sizeof(buf[0]) );
\r
8675 static char buf[MSG_SIZ];
\r
8676 DWORD bufsiz = MSG_SIZ;
\r
8678 if (!GetComputerName(buf, &bufsiz)) {
\r
8679 /*DisplayError("Error getting host name", GetLastError());*/
\r
8680 safeStrCpy(buf, _("Unknown"), sizeof(buf)/sizeof(buf[0]) );
\r
8687 ClockTimerRunning()
\r
8689 return clockTimerEvent != 0;
\r
8695 if (clockTimerEvent == 0) return FALSE;
\r
8696 KillTimer(hwndMain, clockTimerEvent);
\r
8697 clockTimerEvent = 0;
\r
8702 StartClockTimer(long millisec)
\r
8704 clockTimerEvent = SetTimer(hwndMain, (UINT) CLOCK_TIMER_ID,
\r
8705 (UINT) millisec, NULL);
\r
8709 DisplayWhiteClock(long timeRemaining, int highlight)
\r
8712 char *flag = whiteFlag && gameMode == TwoMachinesPlay ? "(!)" : "";
\r
8714 if(appData.noGUI) return;
\r
8715 hdc = GetDC(hwndMain);
\r
8716 if (!IsIconic(hwndMain)) {
\r
8717 DisplayAClock(hdc, timeRemaining, highlight,
\r
8718 flipClock ? &blackRect : &whiteRect, _("White"), flag);
\r
8720 if (highlight && iconCurrent == iconBlack) {
\r
8721 iconCurrent = iconWhite;
\r
8722 PostMessage(hwndMain, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);
\r
8723 if (IsIconic(hwndMain)) {
\r
8724 DrawIcon(hdc, 2, 2, iconCurrent);
\r
8727 (void) ReleaseDC(hwndMain, hdc);
\r
8729 PostMessage(hwndConsole, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);
\r
8733 DisplayBlackClock(long timeRemaining, int highlight)
\r
8736 char *flag = blackFlag && gameMode == TwoMachinesPlay ? "(!)" : "";
\r
8738 if(appData.noGUI) return;
\r
8739 hdc = GetDC(hwndMain);
\r
8740 if (!IsIconic(hwndMain)) {
\r
8741 DisplayAClock(hdc, timeRemaining, highlight,
\r
8742 flipClock ? &whiteRect : &blackRect, _("Black"), flag);
\r
8744 if (highlight && iconCurrent == iconWhite) {
\r
8745 iconCurrent = iconBlack;
\r
8746 PostMessage(hwndMain, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);
\r
8747 if (IsIconic(hwndMain)) {
\r
8748 DrawIcon(hdc, 2, 2, iconCurrent);
\r
8751 (void) ReleaseDC(hwndMain, hdc);
\r
8753 PostMessage(hwndConsole, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);
\r
8758 LoadGameTimerRunning()
\r
8760 return loadGameTimerEvent != 0;
\r
8764 StopLoadGameTimer()
\r
8766 if (loadGameTimerEvent == 0) return FALSE;
\r
8767 KillTimer(hwndMain, loadGameTimerEvent);
\r
8768 loadGameTimerEvent = 0;
\r
8773 StartLoadGameTimer(long millisec)
\r
8775 loadGameTimerEvent = SetTimer(hwndMain, (UINT) LOAD_GAME_TIMER_ID,
\r
8776 (UINT) millisec, NULL);
\r
8784 char fileTitle[MSG_SIZ];
\r
8786 defName = DefaultFileName(appData.oldSaveStyle ? "gam" : "pgn");
\r
8787 f = OpenFileDialog(hwndMain, "a", defName,
\r
8788 appData.oldSaveStyle ? "gam" : "pgn",
\r
8790 _("Save Game to File"), NULL, fileTitle, NULL);
\r
8792 SaveGame(f, 0, "");
\r
8799 ScheduleDelayedEvent(DelayedEventCallback cb, long millisec)
\r
8801 if (delayedTimerEvent != 0) {
\r
8802 if (appData.debugMode && cb != delayedTimerCallback) { // [HGM] alive: not too much debug
\r
8803 fprintf(debugFP, "ScheduleDelayedEvent: event already scheduled\n");
\r
8805 KillTimer(hwndMain, delayedTimerEvent);
\r
8806 delayedTimerEvent = 0;
\r
8807 if(delayedTimerCallback != cb) // [HGM] alive: do not "flush" same event, just postpone it
\r
8808 delayedTimerCallback();
\r
8810 delayedTimerCallback = cb;
\r
8811 delayedTimerEvent = SetTimer(hwndMain, (UINT) DELAYED_TIMER_ID,
\r
8812 (UINT) millisec, NULL);
\r
8815 DelayedEventCallback
\r
8818 if (delayedTimerEvent) {
\r
8819 return delayedTimerCallback;
\r
8826 CancelDelayedEvent()
\r
8828 if (delayedTimerEvent) {
\r
8829 KillTimer(hwndMain, delayedTimerEvent);
\r
8830 delayedTimerEvent = 0;
\r
8834 DWORD GetWin32Priority(int nice)
\r
8835 { // [HGM] nice: translate Unix nice() value to indows priority class. (Code stolen from Polyglot 1.4w11)
\r
8837 REALTIME_PRIORITY_CLASS 0x00000100
\r
8838 HIGH_PRIORITY_CLASS 0x00000080
\r
8839 ABOVE_NORMAL_PRIORITY_CLASS 0x00008000
\r
8840 NORMAL_PRIORITY_CLASS 0x00000020
\r
8841 BELOW_NORMAL_PRIORITY_CLASS 0x00004000
\r
8842 IDLE_PRIORITY_CLASS 0x00000040
\r
8844 if (nice < -15) return 0x00000080;
\r
8845 if (nice < 0) return 0x00008000;
\r
8846 if (nice == 0) return 0x00000020;
\r
8847 if (nice < 15) return 0x00004000;
\r
8848 return 0x00000040;
\r
8851 /* Start a child process running the given program.
\r
8852 The process's standard output can be read from "from", and its
\r
8853 standard input can be written to "to".
\r
8854 Exit with fatal error if anything goes wrong.
\r
8855 Returns an opaque pointer that can be used to destroy the process
\r
8859 StartChildProcess(char *cmdLine, char *dir, ProcRef *pr)
\r
8861 #define BUFSIZE 4096
\r
8863 HANDLE hChildStdinRd, hChildStdinWr,
\r
8864 hChildStdoutRd, hChildStdoutWr;
\r
8865 HANDLE hChildStdinWrDup, hChildStdoutRdDup;
\r
8866 SECURITY_ATTRIBUTES saAttr;
\r
8868 PROCESS_INFORMATION piProcInfo;
\r
8869 STARTUPINFO siStartInfo;
\r
8871 char buf[MSG_SIZ];
\r
8874 if (appData.debugMode) {
\r
8875 fprintf(debugFP, "StartChildProcess (dir=\"%s\") %s\n", dir, cmdLine);
\r
8880 /* Set the bInheritHandle flag so pipe handles are inherited. */
\r
8881 saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);
\r
8882 saAttr.bInheritHandle = TRUE;
\r
8883 saAttr.lpSecurityDescriptor = NULL;
\r
8886 * The steps for redirecting child's STDOUT:
\r
8887 * 1. Create anonymous pipe to be STDOUT for child.
\r
8888 * 2. Create a noninheritable duplicate of read handle,
\r
8889 * and close the inheritable read handle.
\r
8892 /* Create a pipe for the child's STDOUT. */
\r
8893 if (! CreatePipe(&hChildStdoutRd, &hChildStdoutWr, &saAttr, 0)) {
\r
8894 return GetLastError();
\r
8897 /* Duplicate the read handle to the pipe, so it is not inherited. */
\r
8898 fSuccess = DuplicateHandle(GetCurrentProcess(), hChildStdoutRd,
\r
8899 GetCurrentProcess(), &hChildStdoutRdDup, 0,
\r
8900 FALSE, /* not inherited */
\r
8901 DUPLICATE_SAME_ACCESS);
\r
8903 return GetLastError();
\r
8905 CloseHandle(hChildStdoutRd);
\r
8908 * The steps for redirecting child's STDIN:
\r
8909 * 1. Create anonymous pipe to be STDIN for child.
\r
8910 * 2. Create a noninheritable duplicate of write handle,
\r
8911 * and close the inheritable write handle.
\r
8914 /* Create a pipe for the child's STDIN. */
\r
8915 if (! CreatePipe(&hChildStdinRd, &hChildStdinWr, &saAttr, 0)) {
\r
8916 return GetLastError();
\r
8919 /* Duplicate the write handle to the pipe, so it is not inherited. */
\r
8920 fSuccess = DuplicateHandle(GetCurrentProcess(), hChildStdinWr,
\r
8921 GetCurrentProcess(), &hChildStdinWrDup, 0,
\r
8922 FALSE, /* not inherited */
\r
8923 DUPLICATE_SAME_ACCESS);
\r
8925 return GetLastError();
\r
8927 CloseHandle(hChildStdinWr);
\r
8929 /* Arrange to (1) look in dir for the child .exe file, and
\r
8930 * (2) have dir be the child's working directory. Interpret
\r
8931 * dir relative to the directory WinBoard loaded from. */
\r
8932 GetCurrentDirectory(MSG_SIZ, buf);
\r
8933 SetCurrentDirectory(installDir);
\r
8934 SetCurrentDirectory(dir);
\r
8936 /* Now create the child process. */
\r
8938 siStartInfo.cb = sizeof(STARTUPINFO);
\r
8939 siStartInfo.lpReserved = NULL;
\r
8940 siStartInfo.lpDesktop = NULL;
\r
8941 siStartInfo.lpTitle = NULL;
\r
8942 siStartInfo.dwFlags = STARTF_USESTDHANDLES;
\r
8943 siStartInfo.cbReserved2 = 0;
\r
8944 siStartInfo.lpReserved2 = NULL;
\r
8945 siStartInfo.hStdInput = hChildStdinRd;
\r
8946 siStartInfo.hStdOutput = hChildStdoutWr;
\r
8947 siStartInfo.hStdError = hChildStdoutWr;
\r
8949 fSuccess = CreateProcess(NULL,
\r
8950 cmdLine, /* command line */
\r
8951 NULL, /* process security attributes */
\r
8952 NULL, /* primary thread security attrs */
\r
8953 TRUE, /* handles are inherited */
\r
8954 DETACHED_PROCESS|CREATE_NEW_PROCESS_GROUP,
\r
8955 NULL, /* use parent's environment */
\r
8957 &siStartInfo, /* STARTUPINFO pointer */
\r
8958 &piProcInfo); /* receives PROCESS_INFORMATION */
\r
8960 err = GetLastError();
\r
8961 SetCurrentDirectory(buf); /* return to prev directory */
\r
8966 if (appData.niceEngines){ // [HGM] nice: adjust engine proc priority
\r
8967 if(appData.debugMode) fprintf(debugFP, "nice engine proc to %d\n", appData.niceEngines);
\r
8968 SetPriorityClass(piProcInfo.hProcess, GetWin32Priority(appData.niceEngines));
\r
8971 /* Close the handles we don't need in the parent */
\r
8972 CloseHandle(piProcInfo.hThread);
\r
8973 CloseHandle(hChildStdinRd);
\r
8974 CloseHandle(hChildStdoutWr);
\r
8976 /* Prepare return value */
\r
8977 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
\r
8978 cp->kind = CPReal;
\r
8979 cp->hProcess = piProcInfo.hProcess;
\r
8980 cp->pid = piProcInfo.dwProcessId;
\r
8981 cp->hFrom = hChildStdoutRdDup;
\r
8982 cp->hTo = hChildStdinWrDup;
\r
8984 *pr = (void *) cp;
\r
8986 /* Klaus Friedel says that this Sleep solves a problem under Windows
\r
8987 2000 where engines sometimes don't see the initial command(s)
\r
8988 from WinBoard and hang. I don't understand how that can happen,
\r
8989 but the Sleep is harmless, so I've put it in. Others have also
\r
8990 reported what may be the same problem, so hopefully this will fix
\r
8991 it for them too. */
\r
8999 DestroyChildProcess(ProcRef pr, int/*boolean*/ signal)
\r
9001 ChildProc *cp; int result;
\r
9003 cp = (ChildProc *) pr;
\r
9004 if (cp == NULL) return;
\r
9006 switch (cp->kind) {
\r
9008 /* TerminateProcess is considered harmful, so... */
\r
9009 CloseHandle(cp->hTo); /* Closing this will give the child an EOF and hopefully kill it */
\r
9010 if (cp->hFrom) CloseHandle(cp->hFrom); /* if NULL, InputThread will close it */
\r
9011 /* The following doesn't work because the chess program
\r
9012 doesn't "have the same console" as WinBoard. Maybe
\r
9013 we could arrange for this even though neither WinBoard
\r
9014 nor the chess program uses a console for stdio? */
\r
9015 /*!!if (signal) GenerateConsoleCtrlEvent(CTRL_BREAK_EVENT, cp->pid);*/
\r
9017 /* [AS] Special termination modes for misbehaving programs... */
\r
9018 if( signal == 9 ) {
\r
9019 result = TerminateProcess( cp->hProcess, 0 );
\r
9021 if ( appData.debugMode) {
\r
9022 fprintf( debugFP, "Terminating process %lu, result=%d\n", cp->pid, result );
\r
9025 else if( signal == 10 ) {
\r
9026 DWORD dw = WaitForSingleObject( cp->hProcess, 3*1000 ); // Wait 3 seconds at most
\r
9028 if( dw != WAIT_OBJECT_0 ) {
\r
9029 result = TerminateProcess( cp->hProcess, 0 );
\r
9031 if ( appData.debugMode) {
\r
9032 fprintf( debugFP, "Process %lu still alive after timeout, killing... result=%d\n", cp->pid, result );
\r
9038 CloseHandle(cp->hProcess);
\r
9042 if (cp->hFrom) CloseHandle(cp->hFrom);
\r
9046 closesocket(cp->sock);
\r
9051 if (signal) send(cp->sock2, "\017", 1, 0); /* 017 = 15 = SIGTERM */
\r
9052 closesocket(cp->sock);
\r
9053 closesocket(cp->sock2);
\r
9061 InterruptChildProcess(ProcRef pr)
\r
9065 cp = (ChildProc *) pr;
\r
9066 if (cp == NULL) return;
\r
9067 switch (cp->kind) {
\r
9069 /* The following doesn't work because the chess program
\r
9070 doesn't "have the same console" as WinBoard. Maybe
\r
9071 we could arrange for this even though neither WinBoard
\r
9072 nor the chess program uses a console for stdio */
\r
9073 /*!!GenerateConsoleCtrlEvent(CTRL_C_EVENT, cp->pid);*/
\r
9078 /* Can't interrupt */
\r
9082 send(cp->sock2, "\002", 1, 0); /* 2 = SIGINT */
\r
9089 OpenTelnet(char *host, char *port, ProcRef *pr)
\r
9091 char cmdLine[MSG_SIZ];
\r
9093 if (port[0] == NULLCHAR) {
\r
9094 snprintf(cmdLine, MSG_SIZ, "%s %s", appData.telnetProgram, host);
\r
9096 snprintf(cmdLine, MSG_SIZ, "%s %s %s", appData.telnetProgram, host, port);
\r
9098 return StartChildProcess(cmdLine, "", pr);
\r
9102 /* Code to open TCP sockets */
\r
9105 OpenTCP(char *host, char *port, ProcRef *pr)
\r
9110 struct sockaddr_in sa, mysa;
\r
9111 struct hostent FAR *hp;
\r
9112 unsigned short uport;
\r
9113 WORD wVersionRequested;
\r
9116 /* Initialize socket DLL */
\r
9117 wVersionRequested = MAKEWORD(1, 1);
\r
9118 err = WSAStartup(wVersionRequested, &wsaData);
\r
9119 if (err != 0) return err;
\r
9122 if ((s = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {
\r
9123 err = WSAGetLastError();
\r
9128 /* Bind local address using (mostly) don't-care values.
\r
9130 memset((char *) &mysa, 0, sizeof(struct sockaddr_in));
\r
9131 mysa.sin_family = AF_INET;
\r
9132 mysa.sin_addr.s_addr = INADDR_ANY;
\r
9133 uport = (unsigned short) 0;
\r
9134 mysa.sin_port = htons(uport);
\r
9135 if (bind(s, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))
\r
9136 == SOCKET_ERROR) {
\r
9137 err = WSAGetLastError();
\r
9142 /* Resolve remote host name */
\r
9143 memset((char *) &sa, 0, sizeof(struct sockaddr_in));
\r
9144 if (!(hp = gethostbyname(host))) {
\r
9145 unsigned int b0, b1, b2, b3;
\r
9147 err = WSAGetLastError();
\r
9149 if (sscanf(host, "%u.%u.%u.%u", &b0, &b1, &b2, &b3) == 4) {
\r
9150 hp = (struct hostent *) calloc(1, sizeof(struct hostent));
\r
9151 hp->h_addrtype = AF_INET;
\r
9153 hp->h_addr_list = (char **) calloc(2, sizeof(char *));
\r
9154 hp->h_addr_list[0] = (char *) malloc(4);
\r
9155 hp->h_addr_list[0][0] = (char) b0;
\r
9156 hp->h_addr_list[0][1] = (char) b1;
\r
9157 hp->h_addr_list[0][2] = (char) b2;
\r
9158 hp->h_addr_list[0][3] = (char) b3;
\r
9164 sa.sin_family = hp->h_addrtype;
\r
9165 uport = (unsigned short) atoi(port);
\r
9166 sa.sin_port = htons(uport);
\r
9167 memcpy((char *) &sa.sin_addr, hp->h_addr, hp->h_length);
\r
9169 /* Make connection */
\r
9170 if (connect(s, (struct sockaddr *) &sa,
\r
9171 sizeof(struct sockaddr_in)) == SOCKET_ERROR) {
\r
9172 err = WSAGetLastError();
\r
9177 /* Prepare return value */
\r
9178 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
\r
9179 cp->kind = CPSock;
\r
9181 *pr = (ProcRef *) cp;
\r
9187 OpenCommPort(char *name, ProcRef *pr)
\r
9192 char fullname[MSG_SIZ];
\r
9194 if (*name != '\\')
\r
9195 snprintf(fullname, MSG_SIZ, "\\\\.\\%s", name);
\r
9197 safeStrCpy(fullname, name, sizeof(fullname)/sizeof(fullname[0]) );
\r
9199 h = CreateFile(name, GENERIC_READ | GENERIC_WRITE,
\r
9200 0, NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL);
\r
9201 if (h == (HANDLE) -1) {
\r
9202 return GetLastError();
\r
9206 if (!SetCommState(h, (LPDCB) &dcb)) return GetLastError();
\r
9208 /* Accumulate characters until a 100ms pause, then parse */
\r
9209 ct.ReadIntervalTimeout = 100;
\r
9210 ct.ReadTotalTimeoutMultiplier = 0;
\r
9211 ct.ReadTotalTimeoutConstant = 0;
\r
9212 ct.WriteTotalTimeoutMultiplier = 0;
\r
9213 ct.WriteTotalTimeoutConstant = 0;
\r
9214 if (!SetCommTimeouts(h, (LPCOMMTIMEOUTS) &ct)) return GetLastError();
\r
9216 /* Prepare return value */
\r
9217 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
\r
9218 cp->kind = CPComm;
\r
9221 *pr = (ProcRef *) cp;
\r
9227 OpenLoopback(ProcRef *pr)
\r
9229 DisplayFatalError(_("Not implemented"), 0, 1);
\r
9235 OpenRcmd(char* host, char* user, char* cmd, ProcRef* pr)
\r
9240 struct sockaddr_in sa, mysa;
\r
9241 struct hostent FAR *hp;
\r
9242 unsigned short uport;
\r
9243 WORD wVersionRequested;
\r
9246 char stderrPortStr[MSG_SIZ];
\r
9248 /* Initialize socket DLL */
\r
9249 wVersionRequested = MAKEWORD(1, 1);
\r
9250 err = WSAStartup(wVersionRequested, &wsaData);
\r
9251 if (err != 0) return err;
\r
9253 /* Resolve remote host name */
\r
9254 memset((char *) &sa, 0, sizeof(struct sockaddr_in));
\r
9255 if (!(hp = gethostbyname(host))) {
\r
9256 unsigned int b0, b1, b2, b3;
\r
9258 err = WSAGetLastError();
\r
9260 if (sscanf(host, "%u.%u.%u.%u", &b0, &b1, &b2, &b3) == 4) {
\r
9261 hp = (struct hostent *) calloc(1, sizeof(struct hostent));
\r
9262 hp->h_addrtype = AF_INET;
\r
9264 hp->h_addr_list = (char **) calloc(2, sizeof(char *));
\r
9265 hp->h_addr_list[0] = (char *) malloc(4);
\r
9266 hp->h_addr_list[0][0] = (char) b0;
\r
9267 hp->h_addr_list[0][1] = (char) b1;
\r
9268 hp->h_addr_list[0][2] = (char) b2;
\r
9269 hp->h_addr_list[0][3] = (char) b3;
\r
9275 sa.sin_family = hp->h_addrtype;
\r
9276 uport = (unsigned short) 514;
\r
9277 sa.sin_port = htons(uport);
\r
9278 memcpy((char *) &sa.sin_addr, hp->h_addr, hp->h_length);
\r
9280 /* Bind local socket to unused "privileged" port address
\r
9282 s = INVALID_SOCKET;
\r
9283 memset((char *) &mysa, 0, sizeof(struct sockaddr_in));
\r
9284 mysa.sin_family = AF_INET;
\r
9285 mysa.sin_addr.s_addr = INADDR_ANY;
\r
9286 for (fromPort = 1023;; fromPort--) {
\r
9287 if (fromPort < 0) {
\r
9289 return WSAEADDRINUSE;
\r
9291 if (s == INVALID_SOCKET) {
\r
9292 if ((s = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {
\r
9293 err = WSAGetLastError();
\r
9298 uport = (unsigned short) fromPort;
\r
9299 mysa.sin_port = htons(uport);
\r
9300 if (bind(s, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))
\r
9301 == SOCKET_ERROR) {
\r
9302 err = WSAGetLastError();
\r
9303 if (err == WSAEADDRINUSE) continue;
\r
9307 if (connect(s, (struct sockaddr *) &sa,
\r
9308 sizeof(struct sockaddr_in)) == SOCKET_ERROR) {
\r
9309 err = WSAGetLastError();
\r
9310 if (err == WSAEADDRINUSE) {
\r
9321 /* Bind stderr local socket to unused "privileged" port address
\r
9323 s2 = INVALID_SOCKET;
\r
9324 memset((char *) &mysa, 0, sizeof(struct sockaddr_in));
\r
9325 mysa.sin_family = AF_INET;
\r
9326 mysa.sin_addr.s_addr = INADDR_ANY;
\r
9327 for (fromPort = 1023;; fromPort--) {
\r
9328 if (fromPort == prevStderrPort) continue; // don't reuse port
\r
9329 if (fromPort < 0) {
\r
9330 (void) closesocket(s);
\r
9332 return WSAEADDRINUSE;
\r
9334 if (s2 == INVALID_SOCKET) {
\r
9335 if ((s2 = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {
\r
9336 err = WSAGetLastError();
\r
9342 uport = (unsigned short) fromPort;
\r
9343 mysa.sin_port = htons(uport);
\r
9344 if (bind(s2, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))
\r
9345 == SOCKET_ERROR) {
\r
9346 err = WSAGetLastError();
\r
9347 if (err == WSAEADDRINUSE) continue;
\r
9348 (void) closesocket(s);
\r
9352 if (listen(s2, 1) == SOCKET_ERROR) {
\r
9353 err = WSAGetLastError();
\r
9354 if (err == WSAEADDRINUSE) {
\r
9356 s2 = INVALID_SOCKET;
\r
9359 (void) closesocket(s);
\r
9360 (void) closesocket(s2);
\r
9366 prevStderrPort = fromPort; // remember port used
\r
9367 snprintf(stderrPortStr, MSG_SIZ, "%d", fromPort);
\r
9369 if (send(s, stderrPortStr, strlen(stderrPortStr) + 1, 0) == SOCKET_ERROR) {
\r
9370 err = WSAGetLastError();
\r
9371 (void) closesocket(s);
\r
9372 (void) closesocket(s2);
\r
9377 if (send(s, UserName(), strlen(UserName()) + 1, 0) == SOCKET_ERROR) {
\r
9378 err = WSAGetLastError();
\r
9379 (void) closesocket(s);
\r
9380 (void) closesocket(s2);
\r
9384 if (*user == NULLCHAR) user = UserName();
\r
9385 if (send(s, user, strlen(user) + 1, 0) == SOCKET_ERROR) {
\r
9386 err = WSAGetLastError();
\r
9387 (void) closesocket(s);
\r
9388 (void) closesocket(s2);
\r
9392 if (send(s, cmd, strlen(cmd) + 1, 0) == SOCKET_ERROR) {
\r
9393 err = WSAGetLastError();
\r
9394 (void) closesocket(s);
\r
9395 (void) closesocket(s2);
\r
9400 if ((s3 = accept(s2, NULL, NULL)) == INVALID_SOCKET) {
\r
9401 err = WSAGetLastError();
\r
9402 (void) closesocket(s);
\r
9403 (void) closesocket(s2);
\r
9407 (void) closesocket(s2); /* Stop listening */
\r
9409 /* Prepare return value */
\r
9410 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
\r
9411 cp->kind = CPRcmd;
\r
9414 *pr = (ProcRef *) cp;
\r
9421 AddInputSource(ProcRef pr, int lineByLine,
\r
9422 InputCallback func, VOIDSTAR closure)
\r
9424 InputSource *is, *is2 = NULL;
\r
9425 ChildProc *cp = (ChildProc *) pr;
\r
9427 is = (InputSource *) calloc(1, sizeof(InputSource));
\r
9428 is->lineByLine = lineByLine;
\r
9430 is->closure = closure;
\r
9431 is->second = NULL;
\r
9432 is->next = is->buf;
\r
9433 if (pr == NoProc) {
\r
9434 is->kind = CPReal;
\r
9435 consoleInputSource = is;
\r
9437 is->kind = cp->kind;
\r
9439 [AS] Try to avoid a race condition if the thread is given control too early:
\r
9440 we create all threads suspended so that the is->hThread variable can be
\r
9441 safely assigned, then let the threads start with ResumeThread.
\r
9443 switch (cp->kind) {
\r
9445 is->hFile = cp->hFrom;
\r
9446 cp->hFrom = NULL; /* now owned by InputThread */
\r
9448 CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) NonOvlInputThread,
\r
9449 (LPVOID) is, CREATE_SUSPENDED, &is->id);
\r
9453 is->hFile = cp->hFrom;
\r
9454 cp->hFrom = NULL; /* now owned by InputThread */
\r
9456 CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) InputThread,
\r
9457 (LPVOID) is, CREATE_SUSPENDED, &is->id);
\r
9461 is->sock = cp->sock;
\r
9463 CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,
\r
9464 (LPVOID) is, CREATE_SUSPENDED, &is->id);
\r
9468 is2 = (InputSource *) calloc(1, sizeof(InputSource));
\r
9470 is->sock = cp->sock;
\r
9472 is2->sock = cp->sock2;
\r
9473 is2->second = is2;
\r
9475 CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,
\r
9476 (LPVOID) is, CREATE_SUSPENDED, &is->id);
\r
9478 CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,
\r
9479 (LPVOID) is2, CREATE_SUSPENDED, &is2->id);
\r
9483 if( is->hThread != NULL ) {
\r
9484 ResumeThread( is->hThread );
\r
9487 if( is2 != NULL && is2->hThread != NULL ) {
\r
9488 ResumeThread( is2->hThread );
\r
9492 return (InputSourceRef) is;
\r
9496 RemoveInputSource(InputSourceRef isr)
\r
9500 is = (InputSource *) isr;
\r
9501 is->hThread = NULL; /* tell thread to stop */
\r
9502 CloseHandle(is->hThread);
\r
9503 if (is->second != NULL) {
\r
9504 is->second->hThread = NULL;
\r
9505 CloseHandle(is->second->hThread);
\r
9509 int no_wrap(char *message, int count)
\r
9511 ConsoleOutput(message, count, FALSE);
\r
9516 OutputToProcess(ProcRef pr, char *message, int count, int *outError)
\r
9519 int outCount = SOCKET_ERROR;
\r
9520 ChildProc *cp = (ChildProc *) pr;
\r
9521 static OVERLAPPED ovl;
\r
9522 static int line = 0;
\r
9526 if (appData.noJoin || !appData.useInternalWrap)
\r
9527 return no_wrap(message, count);
\r
9530 int width = get_term_width();
\r
9531 int len = wrap(NULL, message, count, width, &line);
\r
9532 char *msg = malloc(len);
\r
9536 return no_wrap(message, count);
\r
9539 dbgchk = wrap(msg, message, count, width, &line);
\r
9540 if (dbgchk != len && appData.debugMode)
\r
9541 fprintf(debugFP, "wrap(): dbgchk(%d) != len(%d)\n", dbgchk, len);
\r
9542 ConsoleOutput(msg, len, FALSE);
\r
9549 if (ovl.hEvent == NULL) {
\r
9550 ovl.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
\r
9552 ovl.Internal = ovl.InternalHigh = ovl.Offset = ovl.OffsetHigh = 0;
\r
9554 switch (cp->kind) {
\r
9557 outCount = send(cp->sock, message, count, 0);
\r
9558 if (outCount == SOCKET_ERROR) {
\r
9559 *outError = WSAGetLastError();
\r
9561 *outError = NO_ERROR;
\r
9566 if (WriteFile(((ChildProc *)pr)->hTo, message, count,
\r
9567 &dOutCount, NULL)) {
\r
9568 *outError = NO_ERROR;
\r
9569 outCount = (int) dOutCount;
\r
9571 *outError = GetLastError();
\r
9576 *outError = DoWriteFile(((ChildProc *)pr)->hTo, message, count,
\r
9577 &dOutCount, &ovl);
\r
9578 if (*outError == NO_ERROR) {
\r
9579 outCount = (int) dOutCount;
\r
9587 OutputToProcessDelayed(ProcRef pr, char *message, int count, int *outError,
\r
9590 /* Ignore delay, not implemented for WinBoard */
\r
9591 return OutputToProcess(pr, message, count, outError);
\r
9596 CmailSigHandlerCallBack(InputSourceRef isr, VOIDSTAR closure,
\r
9597 char *buf, int count, int error)
\r
9599 DisplayFatalError(_("Not implemented"), 0, 1);
\r
9602 /* see wgamelist.c for Game List functions */
\r
9603 /* see wedittags.c for Edit Tags functions */
\r
9610 char buf[MSG_SIZ];
\r
9613 if (SearchPath(installDir, appData.icsLogon, NULL, MSG_SIZ, buf, &dummy)) {
\r
9614 f = fopen(buf, "r");
\r
9616 ProcessICSInitScript(f);
\r
9624 StartAnalysisClock()
\r
9626 if (analysisTimerEvent) return;
\r
9627 analysisTimerEvent = SetTimer(hwndMain, (UINT) ANALYSIS_TIMER_ID,
\r
9628 (UINT) 2000, NULL);
\r
9632 SetHighlights(int fromX, int fromY, int toX, int toY)
\r
9634 highlightInfo.sq[0].x = fromX;
\r
9635 highlightInfo.sq[0].y = fromY;
\r
9636 highlightInfo.sq[1].x = toX;
\r
9637 highlightInfo.sq[1].y = toY;
\r
9643 highlightInfo.sq[0].x = highlightInfo.sq[0].y =
\r
9644 highlightInfo.sq[1].x = highlightInfo.sq[1].y = -1;
\r
9648 SetPremoveHighlights(int fromX, int fromY, int toX, int toY)
\r
9650 premoveHighlightInfo.sq[0].x = fromX;
\r
9651 premoveHighlightInfo.sq[0].y = fromY;
\r
9652 premoveHighlightInfo.sq[1].x = toX;
\r
9653 premoveHighlightInfo.sq[1].y = toY;
\r
9657 ClearPremoveHighlights()
\r
9659 premoveHighlightInfo.sq[0].x = premoveHighlightInfo.sq[0].y =
\r
9660 premoveHighlightInfo.sq[1].x = premoveHighlightInfo.sq[1].y = -1;
\r
9664 ShutDownFrontEnd()
\r
9666 if (saveSettingsOnExit) SaveSettings(settingsFileName);
\r
9667 DeleteClipboardTempFiles();
\r
9673 if (IsIconic(hwndMain))
\r
9674 ShowWindow(hwndMain, SW_RESTORE);
\r
9676 SetActiveWindow(hwndMain);
\r
9680 * Prototypes for animation support routines
\r
9682 static void ScreenSquare(int column, int row, POINT * pt);
\r
9683 static void Tween( POINT * start, POINT * mid, POINT * finish, int factor,
\r
9684 POINT frames[], int * nFrames);
\r
9690 AnimateAtomicCapture(Board board, int fromX, int fromY, int toX, int toY)
\r
9691 { // [HGM] atomic: animate blast wave
\r
9694 explodeInfo.fromX = fromX;
\r
9695 explodeInfo.fromY = fromY;
\r
9696 explodeInfo.toX = toX;
\r
9697 explodeInfo.toY = toY;
\r
9698 for(i=1; i<4*kFactor; i++) {
\r
9699 explodeInfo.radius = (i*180)/(4*kFactor-1);
\r
9700 DrawPosition(FALSE, board);
\r
9701 Sleep(appData.animSpeed);
\r
9703 explodeInfo.radius = 0;
\r
9704 DrawPosition(TRUE, board);
\r
9708 AnimateMove(board, fromX, fromY, toX, toY)
\r
9715 ChessSquare piece;
\r
9716 POINT start, finish, mid;
\r
9717 POINT frames[kFactor * 2 + 1];
\r
9720 if (!appData.animate) return;
\r
9721 if (doingSizing) return;
\r
9722 if (fromY < 0 || fromX < 0) return;
\r
9723 piece = board[fromY][fromX];
\r
9724 if (piece >= EmptySquare) return;
\r
9726 ScreenSquare(fromX, fromY, &start);
\r
9727 ScreenSquare(toX, toY, &finish);
\r
9729 /* All moves except knight jumps move in straight line */
\r
9730 if (!(abs(fromX-toX) == 1 && abs(fromY-toY) == 2 || abs(fromX-toX) == 2 && abs(fromY-toY) == 1)) {
\r
9731 mid.x = start.x + (finish.x - start.x) / 2;
\r
9732 mid.y = start.y + (finish.y - start.y) / 2;
\r
9734 /* Knight: make straight movement then diagonal */
\r
9735 if (abs(toY - fromY) < abs(toX - fromX)) {
\r
9736 mid.x = start.x + (finish.x - start.x) / 2;
\r
9740 mid.y = start.y + (finish.y - start.y) / 2;
\r
9744 /* Don't use as many frames for very short moves */
\r
9745 if (abs(toY - fromY) + abs(toX - fromX) <= 2)
\r
9746 Tween(&start, &mid, &finish, kFactor - 1, frames, &nFrames);
\r
9748 Tween(&start, &mid, &finish, kFactor, frames, &nFrames);
\r
9750 animInfo.from.x = fromX;
\r
9751 animInfo.from.y = fromY;
\r
9752 animInfo.to.x = toX;
\r
9753 animInfo.to.y = toY;
\r
9754 animInfo.lastpos = start;
\r
9755 animInfo.piece = piece;
\r
9756 for (n = 0; n < nFrames; n++) {
\r
9757 animInfo.pos = frames[n];
\r
9758 DrawPosition(FALSE, NULL);
\r
9759 animInfo.lastpos = animInfo.pos;
\r
9760 Sleep(appData.animSpeed);
\r
9762 animInfo.pos = finish;
\r
9763 DrawPosition(FALSE, NULL);
\r
9764 animInfo.piece = EmptySquare;
\r
9765 Explode(board, fromX, fromY, toX, toY);
\r
9768 /* Convert board position to corner of screen rect and color */
\r
9771 ScreenSquare(column, row, pt)
\r
9772 int column; int row; POINT * pt;
\r
9775 pt->x = lineGap + ((BOARD_WIDTH-1)-column) * (squareSize + lineGap);
\r
9776 pt->y = lineGap + row * (squareSize + lineGap);
\r
9778 pt->x = lineGap + column * (squareSize + lineGap);
\r
9779 pt->y = lineGap + ((BOARD_HEIGHT-1)-row) * (squareSize + lineGap);
\r
9783 /* Generate a series of frame coords from start->mid->finish.
\r
9784 The movement rate doubles until the half way point is
\r
9785 reached, then halves back down to the final destination,
\r
9786 which gives a nice slow in/out effect. The algorithmn
\r
9787 may seem to generate too many intermediates for short
\r
9788 moves, but remember that the purpose is to attract the
\r
9789 viewers attention to the piece about to be moved and
\r
9790 then to where it ends up. Too few frames would be less
\r
9794 Tween(start, mid, finish, factor, frames, nFrames)
\r
9795 POINT * start; POINT * mid;
\r
9796 POINT * finish; int factor;
\r
9797 POINT frames[]; int * nFrames;
\r
9799 int n, fraction = 1, count = 0;
\r
9801 /* Slow in, stepping 1/16th, then 1/8th, ... */
\r
9802 for (n = 0; n < factor; n++)
\r
9804 for (n = 0; n < factor; n++) {
\r
9805 frames[count].x = start->x + (mid->x - start->x) / fraction;
\r
9806 frames[count].y = start->y + (mid->y - start->y) / fraction;
\r
9808 fraction = fraction / 2;
\r
9812 frames[count] = *mid;
\r
9815 /* Slow out, stepping 1/2, then 1/4, ... */
\r
9817 for (n = 0; n < factor; n++) {
\r
9818 frames[count].x = finish->x - (finish->x - mid->x) / fraction;
\r
9819 frames[count].y = finish->y - (finish->y - mid->y) / fraction;
\r
9821 fraction = fraction * 2;
\r
9827 HistorySet( char movelist[][2*MOVE_LEN], int first, int last, int current )
\r
9829 MoveHistorySet( movelist, first, last, current, pvInfoList );
\r
9831 EvalGraphSet( first, last, current, pvInfoList );
\r
9835 SettingsPopUp(ChessProgramState *cps)
\r
9836 { // [HGM] wrapper needed because handles must not be passed through back-end
\r
9837 EngineOptionsPopup(savedHwnd, cps);
\r