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
3397 static HDC hdcSeek;
\r
3399 // [HGM] seekgraph
\r
3400 void DrawSeekAxis( int x, int y, int xTo, int yTo )
\r
3403 HPEN hp = SelectObject( hdcSeek, gridPen );
\r
3404 MoveToEx( hdcSeek, boardRect.left+x, boardRect.top+y, &stPt );
\r
3405 LineTo( hdcSeek, boardRect.left+xTo, boardRect.top+yTo );
\r
3406 SelectObject( hdcSeek, hp );
\r
3409 // front-end wrapper for drawing functions to do rectangles
\r
3410 void DrawSeekBackground( int left, int top, int right, int bottom )
\r
3415 if (hdcSeek == NULL) {
\r
3416 hdcSeek = GetDC(hwndMain);
\r
3417 if (!appData.monoMode) {
\r
3418 SelectPalette(hdcSeek, hPal, FALSE);
\r
3419 RealizePalette(hdcSeek);
\r
3422 hp = SelectObject( hdcSeek, gridPen );
\r
3423 rc.top = boardRect.top+top; rc.left = boardRect.left+left;
\r
3424 rc.bottom = boardRect.top+bottom; rc.right = boardRect.left+right;
\r
3425 FillRect( hdcSeek, &rc, lightSquareBrush );
\r
3426 SelectObject( hdcSeek, hp );
\r
3429 // front-end wrapper for putting text in graph
\r
3430 void DrawSeekText(char *buf, int x, int y)
\r
3433 SetBkMode( hdcSeek, TRANSPARENT );
\r
3434 GetTextExtentPoint32( hdcSeek, buf, strlen(buf), &stSize );
\r
3435 TextOut( hdcSeek, boardRect.left+x-3, boardRect.top+y-stSize.cy/2, buf, strlen(buf) );
\r
3438 void DrawSeekDot(int x, int y, int color)
\r
3440 int square = color & 0x80;
\r
3441 HBRUSH oldBrush = SelectObject(hdcSeek,
\r
3442 color == 0 ? markerBrush : color == 1 ? darkSquareBrush : explodeBrush);
\r
3445 Rectangle(hdcSeek, boardRect.left+x - squareSize/9, boardRect.top+y - squareSize/9,
\r
3446 boardRect.left+x + squareSize/9, boardRect.top+y + squareSize/9);
\r
3448 Ellipse(hdcSeek, boardRect.left+x - squareSize/8, boardRect.top+y - squareSize/8,
\r
3449 boardRect.left+x + squareSize/8, boardRect.top+y + squareSize/8);
\r
3450 SelectObject(hdcSeek, oldBrush);
\r
3454 HDCDrawPosition(HDC hdc, BOOLEAN repaint, Board board)
\r
3456 static Board lastReq[2], lastDrawn[2];
\r
3457 static HighlightInfo lastDrawnHighlight, lastDrawnPremove;
\r
3458 static int lastDrawnFlipView = 0;
\r
3459 static int lastReqValid[2] = {0, 0}, lastDrawnValid[2] = {0, 0};
\r
3460 int releaseDC, x, y, x2, y2, row, column, num_clips = 0, i;
\r
3463 HBITMAP bufferBitmap;
\r
3464 HBITMAP oldBitmap;
\r
3466 HRGN clips[MAX_CLIPS];
\r
3467 ChessSquare dragged_piece = EmptySquare;
\r
3468 int nr = twoBoards*partnerUp;
\r
3470 /* I'm undecided on this - this function figures out whether a full
\r
3471 * repaint is necessary on its own, so there's no real reason to have the
\r
3472 * caller tell it that. I think this can safely be set to FALSE - but
\r
3473 * if we trust the callers not to request full repaints unnessesarily, then
\r
3474 * we could skip some clipping work. In other words, only request a full
\r
3475 * redraw when the majority of pieces have changed positions (ie. flip,
\r
3476 * gamestart and similar) --Hawk
\r
3478 Boolean fullrepaint = repaint;
\r
3480 if(DrawSeekGraph()) return; // [HG} seekgraph: suppress printing board if seek graph up
\r
3482 if( DrawPositionNeedsFullRepaint() ) {
\r
3483 fullrepaint = TRUE;
\r
3486 if (board == NULL) {
\r
3487 if (!lastReqValid[nr]) {
\r
3490 board = lastReq[nr];
\r
3492 CopyBoard(lastReq[nr], board);
\r
3493 lastReqValid[nr] = 1;
\r
3496 if (doingSizing) {
\r
3500 if (IsIconic(hwndMain)) {
\r
3504 if (hdc == NULL) {
\r
3505 hdc = GetDC(hwndMain);
\r
3506 if (!appData.monoMode) {
\r
3507 SelectPalette(hdc, hPal, FALSE);
\r
3508 RealizePalette(hdc);
\r
3512 releaseDC = FALSE;
\r
3515 /* Create some work-DCs */
\r
3516 hdcmem = CreateCompatibleDC(hdc);
\r
3517 tmphdc = CreateCompatibleDC(hdc);
\r
3519 /* If dragging is in progress, we temporarely remove the piece */
\r
3520 /* [HGM] or temporarily decrease count if stacked */
\r
3521 /* !! Moved to before board compare !! */
\r
3522 if (dragInfo.from.x >= 0 && dragInfo.pos.x >= 0) {
\r
3523 dragged_piece = board[dragInfo.from.y][dragInfo.from.x];
\r
3524 if(dragInfo.from.x == BOARD_LEFT-2 ) {
\r
3525 if(--board[dragInfo.from.y][dragInfo.from.x+1] == 0 )
\r
3526 board[dragInfo.from.y][dragInfo.from.x] = EmptySquare;
\r
3528 if(dragInfo.from.x == BOARD_RGHT+1) {
\r
3529 if(--board[dragInfo.from.y][dragInfo.from.x-1] == 0 )
\r
3530 board[dragInfo.from.y][dragInfo.from.x] = EmptySquare;
\r
3532 board[dragInfo.from.y][dragInfo.from.x] = gatingPiece;
\r
3535 /* Figure out which squares need updating by comparing the
\r
3536 * newest board with the last drawn board and checking if
\r
3537 * flipping has changed.
\r
3539 if (!fullrepaint && lastDrawnValid[nr] && (nr == 1 || lastDrawnFlipView == flipView)) {
\r
3540 for (row = 0; row < BOARD_HEIGHT; row++) { /* [HGM] true size, not 8 */
\r
3541 for (column = 0; column < BOARD_WIDTH; column++) {
\r
3542 if (lastDrawn[nr][row][column] != board[row][column]) {
\r
3543 SquareToPos(row, column, &x, &y);
\r
3544 clips[num_clips++] =
\r
3545 CreateRectRgn(x, y, x + squareSize, y + squareSize);
\r
3549 if(nr == 0) { // [HGM] dual: no highlights on second board
\r
3550 for (i=0; i<2; i++) {
\r
3551 if (lastDrawnHighlight.sq[i].x != highlightInfo.sq[i].x ||
\r
3552 lastDrawnHighlight.sq[i].y != highlightInfo.sq[i].y) {
\r
3553 if (lastDrawnHighlight.sq[i].x >= 0 &&
\r
3554 lastDrawnHighlight.sq[i].y >= 0) {
\r
3555 SquareToPos(lastDrawnHighlight.sq[i].y,
\r
3556 lastDrawnHighlight.sq[i].x, &x, &y);
\r
3557 clips[num_clips++] =
\r
3558 CreateRectRgn(x - lineGap, y - lineGap,
\r
3559 x + squareSize + lineGap, y + squareSize + lineGap);
\r
3561 if (highlightInfo.sq[i].x >= 0 && highlightInfo.sq[i].y >= 0) {
\r
3562 SquareToPos(highlightInfo.sq[i].y, highlightInfo.sq[i].x, &x, &y);
\r
3563 clips[num_clips++] =
\r
3564 CreateRectRgn(x - lineGap, y - lineGap,
\r
3565 x + squareSize + lineGap, y + squareSize + lineGap);
\r
3569 for (i=0; i<2; i++) {
\r
3570 if (lastDrawnPremove.sq[i].x != premoveHighlightInfo.sq[i].x ||
\r
3571 lastDrawnPremove.sq[i].y != premoveHighlightInfo.sq[i].y) {
\r
3572 if (lastDrawnPremove.sq[i].x >= 0 &&
\r
3573 lastDrawnPremove.sq[i].y >= 0) {
\r
3574 SquareToPos(lastDrawnPremove.sq[i].y,
\r
3575 lastDrawnPremove.sq[i].x, &x, &y);
\r
3576 clips[num_clips++] =
\r
3577 CreateRectRgn(x - lineGap, y - lineGap,
\r
3578 x + squareSize + lineGap, y + squareSize + lineGap);
\r
3580 if (premoveHighlightInfo.sq[i].x >= 0 &&
\r
3581 premoveHighlightInfo.sq[i].y >= 0) {
\r
3582 SquareToPos(premoveHighlightInfo.sq[i].y,
\r
3583 premoveHighlightInfo.sq[i].x, &x, &y);
\r
3584 clips[num_clips++] =
\r
3585 CreateRectRgn(x - lineGap, y - lineGap,
\r
3586 x + squareSize + lineGap, y + squareSize + lineGap);
\r
3590 } else { // nr == 1
\r
3591 partnerHighlightInfo.sq[0].y = board[EP_STATUS-4];
\r
3592 partnerHighlightInfo.sq[0].x = board[EP_STATUS-3];
\r
3593 partnerHighlightInfo.sq[1].y = board[EP_STATUS-2];
\r
3594 partnerHighlightInfo.sq[1].x = board[EP_STATUS-1];
\r
3595 for (i=0; i<2; i++) {
\r
3596 if (partnerHighlightInfo.sq[i].x >= 0 &&
\r
3597 partnerHighlightInfo.sq[i].y >= 0) {
\r
3598 SquareToPos(partnerHighlightInfo.sq[i].y,
\r
3599 partnerHighlightInfo.sq[i].x, &x, &y);
\r
3600 clips[num_clips++] =
\r
3601 CreateRectRgn(x - lineGap, y - lineGap,
\r
3602 x + squareSize + lineGap, y + squareSize + lineGap);
\r
3604 if (oldPartnerHighlight.sq[i].x >= 0 &&
\r
3605 oldPartnerHighlight.sq[i].y >= 0) {
\r
3606 SquareToPos(oldPartnerHighlight.sq[i].y,
\r
3607 oldPartnerHighlight.sq[i].x, &x, &y);
\r
3608 clips[num_clips++] =
\r
3609 CreateRectRgn(x - lineGap, y - lineGap,
\r
3610 x + squareSize + lineGap, y + squareSize + lineGap);
\r
3615 fullrepaint = TRUE;
\r
3618 /* Create a buffer bitmap - this is the actual bitmap
\r
3619 * being written to. When all the work is done, we can
\r
3620 * copy it to the real DC (the screen). This avoids
\r
3621 * the problems with flickering.
\r
3623 GetClientRect(hwndMain, &Rect);
\r
3624 bufferBitmap = CreateCompatibleBitmap(hdc, Rect.right-Rect.left+1,
\r
3625 Rect.bottom-Rect.top+1);
\r
3626 oldBitmap = SelectObject(hdcmem, bufferBitmap);
\r
3627 if (!appData.monoMode) {
\r
3628 SelectPalette(hdcmem, hPal, FALSE);
\r
3631 /* Create clips for dragging */
\r
3632 if (!fullrepaint) {
\r
3633 if (dragInfo.from.x >= 0) {
\r
3634 SquareToPos(dragInfo.from.y, dragInfo.from.x, &x, &y);
\r
3635 clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);
\r
3637 if (dragInfo.start.x >= 0) {
\r
3638 SquareToPos(dragInfo.start.y, dragInfo.start.x, &x, &y);
\r
3639 clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);
\r
3641 if (dragInfo.pos.x >= 0) {
\r
3642 x = dragInfo.pos.x - squareSize / 2;
\r
3643 y = dragInfo.pos.y - squareSize / 2;
\r
3644 clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);
\r
3646 if (dragInfo.lastpos.x >= 0) {
\r
3647 x = dragInfo.lastpos.x - squareSize / 2;
\r
3648 y = dragInfo.lastpos.y - squareSize / 2;
\r
3649 clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);
\r
3653 /* Are we animating a move?
\r
3655 * - remove the piece from the board (temporarely)
\r
3656 * - calculate the clipping region
\r
3658 if (!fullrepaint) {
\r
3659 if (animInfo.piece != EmptySquare) {
\r
3660 board[animInfo.from.y][animInfo.from.x] = EmptySquare;
\r
3661 x = boardRect.left + animInfo.lastpos.x;
\r
3662 y = boardRect.top + animInfo.lastpos.y;
\r
3663 x2 = boardRect.left + animInfo.pos.x;
\r
3664 y2 = boardRect.top + animInfo.pos.y;
\r
3665 clips[num_clips++] = CreateRectRgn(MIN(x,x2), MIN(y,y2), MAX(x,x2)+squareSize, MAX(y,y2)+squareSize);
\r
3666 /* Slight kludge. The real problem is that after AnimateMove is
\r
3667 done, the position on the screen does not match lastDrawn.
\r
3668 This currently causes trouble only on e.p. captures in
\r
3669 atomic, where the piece moves to an empty square and then
\r
3670 explodes. The old and new positions both had an empty square
\r
3671 at the destination, but animation has drawn a piece there and
\r
3672 we have to remember to erase it. [HGM] moved until after setting lastDrawn */
\r
3673 lastDrawn[0][animInfo.to.y][animInfo.to.x] = animInfo.piece;
\r
3677 /* No clips? Make sure we have fullrepaint set to TRUE */
\r
3678 if (num_clips == 0)
\r
3679 fullrepaint = TRUE;
\r
3681 /* Set clipping on the memory DC */
\r
3682 if (!fullrepaint) {
\r
3683 SelectClipRgn(hdcmem, clips[0]);
\r
3684 for (x = 1; x < num_clips; x++) {
\r
3685 if (ExtSelectClipRgn(hdcmem, clips[x], RGN_OR) == ERROR)
\r
3686 abort(); // this should never ever happen!
\r
3690 /* Do all the drawing to the memory DC */
\r
3691 if(explodeInfo.radius) { // [HGM] atomic
\r
3693 int x, y, r=(explodeInfo.radius * squareSize)/100;
\r
3694 ChessSquare piece = board[explodeInfo.fromY][explodeInfo.fromX];
\r
3695 board[explodeInfo.fromY][explodeInfo.fromX] = EmptySquare; // suppress display of capturer
\r
3696 SquareToPos(explodeInfo.toY, explodeInfo.toX, &x, &y);
\r
3697 x += squareSize/2;
\r
3698 y += squareSize/2;
\r
3699 if(!fullrepaint) {
\r
3700 clips[num_clips] = CreateRectRgn(x-r, y-r, x+r, y+r);
\r
3701 ExtSelectClipRgn(hdcmem, clips[num_clips++], RGN_OR);
\r
3703 DrawGridOnDC(hdcmem);
\r
3704 DrawHighlightsOnDC(hdcmem, &highlightInfo, HIGHLIGHT_PEN);
\r
3705 DrawHighlightsOnDC(hdcmem, &premoveHighlightInfo, PREMOVE_PEN);
\r
3706 DrawBoardOnDC(hdcmem, board, tmphdc);
\r
3707 board[explodeInfo.fromY][explodeInfo.fromX] = piece;
\r
3708 oldBrush = SelectObject(hdcmem, explodeBrush);
\r
3709 Ellipse(hdcmem, x-r, y-r, x+r, y+r);
\r
3710 SelectObject(hdcmem, oldBrush);
\r
3712 DrawGridOnDC(hdcmem);
\r
3713 if(nr == 0) { // [HGM] dual: decide which highlights to draw
\r
3714 DrawHighlightsOnDC(hdcmem, &highlightInfo, HIGHLIGHT_PEN);
\r
3715 DrawHighlightsOnDC(hdcmem, &premoveHighlightInfo, PREMOVE_PEN);
\r
3717 DrawHighlightsOnDC(hdcmem, &partnerHighlightInfo, HIGHLIGHT_PEN);
\r
3718 oldPartnerHighlight = partnerHighlightInfo;
\r
3720 DrawBoardOnDC(hdcmem, board, tmphdc);
\r
3722 if(nr == 0) // [HGM] dual: markers only on left board
\r
3723 for (row = 0; row < BOARD_HEIGHT; row++) {
\r
3724 for (column = 0; column < BOARD_WIDTH; column++) {
\r
3725 if (marker[row][column]) { // marker changes only occur with full repaint!
\r
3726 HBRUSH oldBrush = SelectObject(hdcmem,
\r
3727 marker[row][column] == 2 ? markerBrush : explodeBrush);
\r
3728 SquareToPos(row, column, &x, &y);
\r
3729 Ellipse(hdcmem, x + squareSize/4, y + squareSize/4,
\r
3730 x + 3*squareSize/4, y + 3*squareSize/4);
\r
3731 SelectObject(hdcmem, oldBrush);
\r
3736 HBITMAP whiteLogo = (HBITMAP) first.programLogo, blackLogo = (HBITMAP) second.programLogo;
\r
3737 if(appData.autoLogo) {
\r
3739 switch(gameMode) { // pick logos based on game mode
\r
3740 case IcsObserving:
\r
3741 whiteLogo = second.programLogo; // ICS logo
\r
3742 blackLogo = second.programLogo;
\r
3745 case IcsPlayingWhite:
\r
3746 if(!appData.zippyPlay) whiteLogo = userLogo;
\r
3747 blackLogo = second.programLogo; // ICS logo
\r
3749 case IcsPlayingBlack:
\r
3750 whiteLogo = second.programLogo; // ICS logo
\r
3751 blackLogo = appData.zippyPlay ? first.programLogo : userLogo;
\r
3753 case TwoMachinesPlay:
\r
3754 if(first.twoMachinesColor[0] == 'b') {
\r
3755 whiteLogo = second.programLogo;
\r
3756 blackLogo = first.programLogo;
\r
3759 case MachinePlaysWhite:
\r
3760 blackLogo = userLogo;
\r
3762 case MachinePlaysBlack:
\r
3763 whiteLogo = userLogo;
\r
3764 blackLogo = first.programLogo;
\r
3767 DrawLogoOnDC(hdc, leftLogoRect, flipClock ? blackLogo : whiteLogo);
\r
3768 DrawLogoOnDC(hdc, rightLogoRect, flipClock ? whiteLogo : blackLogo);
\r
3771 if( appData.highlightMoveWithArrow ) {
\r
3772 DrawArrowHighlight(hdcmem);
\r
3775 DrawCoordsOnDC(hdcmem);
\r
3777 CopyBoard(lastDrawn[nr], board); /* [HGM] Moved to here from end of routine, */
\r
3778 /* to make sure lastDrawn contains what is actually drawn */
\r
3780 /* Put the dragged piece back into place and draw it (out of place!) */
\r
3781 if (dragged_piece != EmptySquare) {
\r
3782 /* [HGM] or restack */
\r
3783 if(dragInfo.from.x == BOARD_LEFT-2 )
\r
3784 board[dragInfo.from.y][dragInfo.from.x+1]++;
\r
3786 if(dragInfo.from.x == BOARD_RGHT+1 )
\r
3787 board[dragInfo.from.y][dragInfo.from.x-1]++;
\r
3788 board[dragInfo.from.y][dragInfo.from.x] = dragged_piece;
\r
3789 x = dragInfo.pos.x - squareSize / 2;
\r
3790 y = dragInfo.pos.y - squareSize / 2;
\r
3791 DrawPieceOnDC(hdcmem, dragged_piece,
\r
3792 ((int) dragged_piece < (int) BlackPawn),
\r
3793 (dragInfo.from.y + dragInfo.from.x) % 2, x, y, tmphdc);
\r
3796 /* Put the animated piece back into place and draw it */
\r
3797 if (animInfo.piece != EmptySquare) {
\r
3798 board[animInfo.from.y][animInfo.from.x] = animInfo.piece;
\r
3799 x = boardRect.left + animInfo.pos.x;
\r
3800 y = boardRect.top + animInfo.pos.y;
\r
3801 DrawPieceOnDC(hdcmem, animInfo.piece,
\r
3802 ((int) animInfo.piece < (int) BlackPawn),
\r
3803 (animInfo.from.y + animInfo.from.x) % 2, x, y, tmphdc);
\r
3806 /* Release the bufferBitmap by selecting in the old bitmap
\r
3807 * and delete the memory DC
\r
3809 SelectObject(hdcmem, oldBitmap);
\r
3812 /* Set clipping on the target DC */
\r
3813 if (!fullrepaint) {
\r
3814 if(nr == 1) for (x = 0; x < num_clips; x++) { // [HGM] dual: translate clips
\r
3816 GetRgnBox(clips[x], &rect);
\r
3817 DeleteObject(clips[x]);
\r
3818 clips[x] = CreateRectRgn(rect.left + wpMain.width/2, rect.top,
\r
3819 rect.right + wpMain.width/2, rect.bottom);
\r
3821 SelectClipRgn(hdc, clips[0]);
\r
3822 for (x = 1; x < num_clips; x++) {
\r
3823 if (ExtSelectClipRgn(hdc, clips[x], RGN_OR) == ERROR)
\r
3824 abort(); // this should never ever happen!
\r
3828 /* Copy the new bitmap onto the screen in one go.
\r
3829 * This way we avoid any flickering
\r
3831 oldBitmap = SelectObject(tmphdc, bufferBitmap);
\r
3832 BitBlt(hdc, boardRect.left + twoBoards*partnerUp*wpMain.width/2, boardRect.top, // [HGM] dual
\r
3833 boardRect.right - boardRect.left,
\r
3834 boardRect.bottom - boardRect.top,
\r
3835 tmphdc, boardRect.left, boardRect.top, SRCCOPY);
\r
3836 if(saveDiagFlag) {
\r
3837 BITMAP b; int i, j=0, m, w, wb, fac=0; char pData[1000000];
\r
3838 BITMAPINFOHEADER bih; int color[16], nrColors=0;
\r
3840 GetObject(bufferBitmap, sizeof(b), &b);
\r
3841 if(b.bmWidthBytes*b.bmHeight <= 990000) {
\r
3842 bih.biSize = sizeof(BITMAPINFOHEADER);
\r
3843 bih.biWidth = b.bmWidth;
\r
3844 bih.biHeight = b.bmHeight;
\r
3846 bih.biBitCount = b.bmBitsPixel;
\r
3847 bih.biCompression = 0;
\r
3848 bih.biSizeImage = b.bmWidthBytes*b.bmHeight;
\r
3849 bih.biXPelsPerMeter = 0;
\r
3850 bih.biYPelsPerMeter = 0;
\r
3851 bih.biClrUsed = 0;
\r
3852 bih.biClrImportant = 0;
\r
3853 // fprintf(diagFile, "t=%d\nw=%d\nh=%d\nB=%d\nP=%d\nX=%d\n",
\r
3854 // b.bmType, b.bmWidth, b.bmHeight, b.bmWidthBytes, b.bmPlanes, b.bmBitsPixel);
\r
3855 GetDIBits(tmphdc,bufferBitmap,0,b.bmHeight,pData,(BITMAPINFO*)&bih,DIB_RGB_COLORS);
\r
3856 // fprintf(diagFile, "%8x\n", (int) pData);
\r
3858 wb = b.bmWidthBytes;
\r
3860 for(i=0; i<wb*(b.bmHeight - boardRect.top + OUTER_MARGIN)>>2; i++) {
\r
3861 int k = ((int*) pData)[i];
\r
3862 for(j=0; j<nrColors; j++) if(color[j] == k) break;
\r
3863 if(j >= 16) break;
\r
3865 if(j >= nrColors) nrColors = j+1;
\r
3867 if(j<16) { // 16 colors is enough. Compress to 4 bits per pixel
\r
3869 for(i=0; i<b.bmHeight - boardRect.top + OUTER_MARGIN; i++) {
\r
3870 for(w=0; w<(wb>>2); w+=2) {
\r
3871 int k = ((int*) pData)[(wb*i>>2) + w];
\r
3872 for(j=0; j<nrColors; j++) if(color[j] == k) break;
\r
3873 k = ((int*) pData)[(wb*i>>2) + w + 1];
\r
3874 for(m=0; m<nrColors; m++) if(color[m] == k) break;
\r
3875 pData[p++] = m | j<<4;
\r
3877 while(p&3) pData[p++] = 0;
\r
3880 wb = ((wb+31)>>5)<<2;
\r
3882 // write BITMAPFILEHEADER
\r
3883 fprintf(diagFile, "BM");
\r
3884 fputDW(diagFile, wb*(b.bmHeight - boardRect.top + OUTER_MARGIN)+0x36 + (fac?64:0));
\r
3885 fputDW(diagFile, 0);
\r
3886 fputDW(diagFile, 0x36 + (fac?64:0));
\r
3887 // write BITMAPINFOHEADER
\r
3888 fputDW(diagFile, 40);
\r
3889 fputDW(diagFile, b.bmWidth);
\r
3890 fputDW(diagFile, b.bmHeight - boardRect.top + OUTER_MARGIN);
\r
3891 if(fac) fputDW(diagFile, 0x040001); // planes and bits/pixel
\r
3892 else fputDW(diagFile, 0x200001); // planes and bits/pixel
\r
3893 fputDW(diagFile, 0);
\r
3894 fputDW(diagFile, 0);
\r
3895 fputDW(diagFile, 0);
\r
3896 fputDW(diagFile, 0);
\r
3897 fputDW(diagFile, 0);
\r
3898 fputDW(diagFile, 0);
\r
3899 // write color table
\r
3901 for(i=0; i<16; i++) fputDW(diagFile, color[i]);
\r
3902 // write bitmap data
\r
3903 for(i=0; i<wb*(b.bmHeight - boardRect.top + OUTER_MARGIN); i++)
\r
3904 fputc(pData[i], diagFile);
\r
3908 SelectObject(tmphdc, oldBitmap);
\r
3910 /* Massive cleanup */
\r
3911 for (x = 0; x < num_clips; x++)
\r
3912 DeleteObject(clips[x]);
\r
3915 DeleteObject(bufferBitmap);
\r
3918 ReleaseDC(hwndMain, hdc);
\r
3920 if (lastDrawnFlipView != flipView && nr == 0) {
\r
3922 CheckMenuItem(GetMenu(hwndMain),IDM_FlipView, MF_BYCOMMAND|MF_CHECKED);
\r
3924 CheckMenuItem(GetMenu(hwndMain),IDM_FlipView, MF_BYCOMMAND|MF_UNCHECKED);
\r
3927 /* CopyBoard(lastDrawn, board);*/
\r
3928 lastDrawnHighlight = highlightInfo;
\r
3929 lastDrawnPremove = premoveHighlightInfo;
\r
3930 lastDrawnFlipView = flipView;
\r
3931 lastDrawnValid[nr] = 1;
\r
3934 /* [HGM] diag: Save the current board display to the given open file and close the file */
\r
3939 saveDiagFlag = 1; diagFile = f;
\r
3940 HDCDrawPosition(NULL, TRUE, NULL);
\r
3944 // if(f != NULL) fprintf(f, "Sorry, but this feature is still in preparation\n");
\r
3951 /*---------------------------------------------------------------------------*\
\r
3952 | CLIENT PAINT PROCEDURE
\r
3953 | This is the main event-handler for the WM_PAINT message.
\r
3955 \*---------------------------------------------------------------------------*/
\r
3957 PaintProc(HWND hwnd)
\r
3963 if((hdc = BeginPaint(hwnd, &ps))) {
\r
3964 if (IsIconic(hwnd)) {
\r
3965 DrawIcon(hdc, 2, 2, iconCurrent);
\r
3967 if (!appData.monoMode) {
\r
3968 SelectPalette(hdc, hPal, FALSE);
\r
3969 RealizePalette(hdc);
\r
3971 HDCDrawPosition(hdc, 1, NULL);
\r
3972 if(twoBoards) { // [HGM] dual: also redraw other board in other orientation
\r
3973 flipView = !flipView; partnerUp = !partnerUp;
\r
3974 HDCDrawPosition(hdc, 1, NULL);
\r
3975 flipView = !flipView; partnerUp = !partnerUp;
\r
3978 SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);
\r
3979 ExtTextOut(hdc, messageRect.left, messageRect.top,
\r
3980 ETO_CLIPPED|ETO_OPAQUE,
\r
3981 &messageRect, messageText, strlen(messageText), NULL);
\r
3982 SelectObject(hdc, oldFont);
\r
3983 DisplayBothClocks();
\r
3985 EndPaint(hwnd,&ps);
\r
3993 * If the user selects on a border boundary, return -1; if off the board,
\r
3994 * return -2. Otherwise map the event coordinate to the square.
\r
3995 * The offset boardRect.left or boardRect.top must already have been
\r
3996 * subtracted from x.
\r
3998 int EventToSquare(x, limit)
\r
4006 if ((x % (squareSize + lineGap)) >= squareSize)
\r
4008 x /= (squareSize + lineGap);
\r
4020 DropEnable dropEnables[] = {
\r
4021 { 'P', DP_Pawn, N_("Pawn") },
\r
4022 { 'N', DP_Knight, N_("Knight") },
\r
4023 { 'B', DP_Bishop, N_("Bishop") },
\r
4024 { 'R', DP_Rook, N_("Rook") },
\r
4025 { 'Q', DP_Queen, N_("Queen") },
\r
4029 SetupDropMenu(HMENU hmenu)
\r
4031 int i, count, enable;
\r
4033 extern char white_holding[], black_holding[];
\r
4034 char item[MSG_SIZ];
\r
4036 for (i=0; i<sizeof(dropEnables)/sizeof(DropEnable); i++) {
\r
4037 p = strchr(gameMode == IcsPlayingWhite ? white_holding : black_holding,
\r
4038 dropEnables[i].piece);
\r
4040 while (p && *p++ == dropEnables[i].piece) count++;
\r
4041 snprintf(item, MSG_SIZ, "%s %d", T_(dropEnables[i].name), count);
\r
4042 enable = count > 0 || !appData.testLegality
\r
4043 /*!!temp:*/ || (gameInfo.variant == VariantCrazyhouse
\r
4044 && !appData.icsActive);
\r
4045 ModifyMenu(hmenu, dropEnables[i].command,
\r
4046 MF_BYCOMMAND | (enable ? MF_ENABLED : MF_GRAYED) | MF_STRING,
\r
4047 dropEnables[i].command, item);
\r
4051 void DragPieceBegin(int x, int y)
\r
4053 dragInfo.lastpos.x = boardRect.left + x;
\r
4054 dragInfo.lastpos.y = boardRect.top + y;
\r
4055 dragInfo.from.x = fromX;
\r
4056 dragInfo.from.y = fromY;
\r
4057 dragInfo.start = dragInfo.from;
\r
4058 SetCapture(hwndMain);
\r
4061 void DragPieceEnd(int x, int y)
\r
4064 dragInfo.start.x = dragInfo.start.y = -1;
\r
4065 dragInfo.from = dragInfo.start;
\r
4066 dragInfo.pos = dragInfo.lastpos = dragInfo.start;
\r
4069 /* Event handler for mouse messages */
\r
4071 MouseEvent(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
\r
4075 static int recursive = 0;
\r
4077 BOOLEAN forceFullRepaint = IsFullRepaintPreferrable(); /* [AS] */
\r
4080 if (message == WM_MBUTTONUP) {
\r
4081 /* Hideous kludge to fool TrackPopupMenu into paying attention
\r
4082 to the middle button: we simulate pressing the left button too!
\r
4084 PostMessage(hwnd, WM_LBUTTONDOWN, wParam, lParam);
\r
4085 PostMessage(hwnd, WM_LBUTTONUP, wParam, lParam);
\r
4091 pt.x = LOWORD(lParam);
\r
4092 pt.y = HIWORD(lParam);
\r
4093 x = EventToSquare(pt.x - boardRect.left, BOARD_WIDTH);
\r
4094 y = EventToSquare(pt.y - boardRect.top, BOARD_HEIGHT);
\r
4095 if (!flipView && y >= 0) {
\r
4096 y = BOARD_HEIGHT - 1 - y;
\r
4098 if (flipView && x >= 0) {
\r
4099 x = BOARD_WIDTH - 1 - x;
\r
4102 shiftKey = GetKeyState(VK_SHIFT) < 0; // [HGM] remember last shift status
\r
4104 switch (message) {
\r
4105 case WM_LBUTTONDOWN:
\r
4106 if (PtInRect((LPRECT) &whiteRect, pt)) {
\r
4107 if (gameMode == EditPosition) {
\r
4108 SetWhiteToPlayEvent();
\r
4109 } else if (gameMode == EditGame || GetKeyState(VK_SHIFT) < 0) {
\r
4110 AdjustClock(flipClock, -1);
\r
4111 } else if (gameMode == IcsPlayingBlack ||
\r
4112 gameMode == MachinePlaysWhite) {
\r
4115 } else if (PtInRect((LPRECT) &blackRect, pt)) {
\r
4116 if (gameMode == EditPosition) {
\r
4117 SetBlackToPlayEvent();
\r
4118 } else if (gameMode == EditGame || GetKeyState(VK_SHIFT) < 0) {
\r
4119 AdjustClock(!flipClock, -1);
\r
4120 } else if (gameMode == IcsPlayingWhite ||
\r
4121 gameMode == MachinePlaysBlack) {
\r
4125 dragInfo.start.x = dragInfo.start.y = -1;
\r
4126 dragInfo.from = dragInfo.start;
\r
4127 if(fromX == -1 && frozen) { // not sure where this is for
\r
4128 fromX = fromY = -1;
\r
4129 DrawPosition(forceFullRepaint || FALSE, NULL); /* [AS] */
\r
4132 LeftClick(Press, pt.x - boardRect.left, pt.y - boardRect.top);
\r
4133 DrawPosition(TRUE, NULL);
\r
4136 case WM_LBUTTONUP:
\r
4137 LeftClick(Release, pt.x - boardRect.left, pt.y - boardRect.top);
\r
4138 DrawPosition(TRUE, NULL);
\r
4141 case WM_MOUSEMOVE:
\r
4142 if(SeekGraphClick(Press, pt.x - boardRect.left, pt.y - boardRect.top, 1)) break;
\r
4143 MovePV(pt.x - boardRect.left, pt.y - boardRect.top, boardRect.bottom - boardRect.top);
\r
4144 if ((appData.animateDragging || appData.highlightDragging)
\r
4145 && (wParam & MK_LBUTTON)
\r
4146 && dragInfo.from.x >= 0)
\r
4148 BOOL full_repaint = FALSE;
\r
4150 if (appData.animateDragging) {
\r
4151 dragInfo.pos = pt;
\r
4153 if (appData.highlightDragging) {
\r
4154 SetHighlights(fromX, fromY, x, y);
\r
4155 if( IsDrawArrowEnabled() && (x < 0 || x >= BOARD_WIDTH || y < 0 || y >= BOARD_HEIGHT) ) {
\r
4156 full_repaint = TRUE;
\r
4160 DrawPosition( full_repaint, NULL);
\r
4162 dragInfo.lastpos = dragInfo.pos;
\r
4166 case WM_MOUSEWHEEL: // [DM]
\r
4167 { static int lastDir = 0; // [HGM] build in some hysteresis to avoid spurious events
\r
4168 /* Mouse Wheel is being rolled forward
\r
4169 * Play moves forward
\r
4171 if((short)HIWORD(wParam) < 0 && currentMove < forwardMostMove)
\r
4172 { if(lastDir == 1) ForwardEvent(); else lastDir = 1; } // [HGM] suppress first event in direction
\r
4173 /* Mouse Wheel is being rolled backward
\r
4174 * Play moves backward
\r
4176 if((short)HIWORD(wParam) > 0 && currentMove > backwardMostMove)
\r
4177 { if(lastDir == -1) BackwardEvent(); else lastDir = -1; }
\r
4181 case WM_MBUTTONUP:
\r
4182 case WM_RBUTTONUP:
\r
4184 RightClick(Release, pt.x - boardRect.left, pt.y - boardRect.top, &fromX, &fromY);
\r
4187 case WM_MBUTTONDOWN:
\r
4188 case WM_RBUTTONDOWN:
\r
4191 fromX = fromY = -1;
\r
4192 dragInfo.pos.x = dragInfo.pos.y = -1;
\r
4193 dragInfo.start.x = dragInfo.start.y = -1;
\r
4194 dragInfo.from = dragInfo.start;
\r
4195 dragInfo.lastpos = dragInfo.pos;
\r
4196 if (appData.highlightDragging) {
\r
4197 ClearHighlights();
\r
4200 /* [HGM] right mouse button in clock area edit-game mode ups clock */
\r
4201 if (PtInRect((LPRECT) &whiteRect, pt)) {
\r
4202 if (gameMode == EditGame || GetKeyState(VK_SHIFT) < 0) AdjustClock(flipClock, 1);
\r
4203 } else if (PtInRect((LPRECT) &blackRect, pt)) {
\r
4204 if (gameMode == EditGame || GetKeyState(VK_SHIFT) < 0) AdjustClock(!flipClock, 1);
\r
4208 DrawPosition(TRUE, NULL);
\r
4210 menuNr = RightClick(Press, pt.x - boardRect.left, pt.y - boardRect.top, &fromX, &fromY);
\r
4213 if (message == WM_MBUTTONDOWN) {
\r
4214 buttonCount = 3; /* even if system didn't think so */
\r
4215 if (wParam & MK_SHIFT)
\r
4216 MenuPopup(hwnd, pt, LoadMenu(hInst, "BlackPieceMenu"), -1);
\r
4218 MenuPopup(hwnd, pt, LoadMenu(hInst, "WhitePieceMenu"), -1);
\r
4219 } else { /* message == WM_RBUTTONDOWN */
\r
4220 /* Just have one menu, on the right button. Windows users don't
\r
4221 think to try the middle one, and sometimes other software steals
\r
4222 it, or it doesn't really exist. */
\r
4223 if(gameInfo.variant != VariantShogi)
\r
4224 MenuPopup(hwnd, pt, LoadMenu(hInst, "PieceMenu"), -1);
\r
4226 MenuPopup(hwnd, pt, LoadMenu(hInst, "ShogiPieceMenu"), -1);
\r
4230 SetCapture(hwndMain);
4233 hmenu = LoadMenu(hInst, "DropPieceMenu");
\r
4234 SetupDropMenu(hmenu);
\r
4235 MenuPopup(hwnd, pt, hmenu, -1);
\r
4245 /* Preprocess messages for buttons in main window */
\r
4247 ButtonProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
\r
4249 int id = GetWindowLong(hwnd, GWL_ID);
\r
4252 for (i=0; i<N_BUTTONS; i++) {
\r
4253 if (buttonDesc[i].id == id) break;
\r
4255 if (i == N_BUTTONS) return 0;
\r
4256 switch (message) {
\r
4261 dir = (wParam == VK_LEFT) ? -1 : 1;
\r
4262 SetFocus(buttonDesc[(i + dir + N_BUTTONS) % N_BUTTONS].hwnd);
\r
4269 SendMessage(hwndMain, WM_COMMAND, MAKEWPARAM(buttonDesc[i].id, 0), 0);
\r
4272 if (appData.icsActive && (isalpha((char)wParam) || wParam == '0')) {
\r
4273 // [HGM] movenum: only letters or leading zero should go to ICS input
\r
4274 HWND h = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
4275 if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);
\r
4277 SendMessage(h, WM_CHAR, wParam, lParam);
\r
4279 } else if (isalpha((char)wParam) || isdigit((char)wParam)){
\r
4280 PopUpMoveDialog((char)wParam);
\r
4286 return CallWindowProc(buttonDesc[i].wndproc, hwnd, message, wParam, lParam);
\r
4289 /* Process messages for Promotion dialog box */
\r
4291 Promotion(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
4295 switch (message) {
\r
4296 case WM_INITDIALOG: /* message: initialize dialog box */
\r
4297 /* Center the dialog over the application window */
\r
4298 CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));
\r
4299 Translate(hDlg, DLG_PromotionKing);
\r
4300 ShowWindow(GetDlgItem(hDlg, PB_King),
\r
4301 (!appData.testLegality || gameInfo.variant == VariantSuicide ||
\r
4302 gameInfo.variant == VariantSpartan && !WhiteOnMove(currentMove) ||
\r
4303 gameInfo.variant == VariantGiveaway || gameInfo.variant == VariantSuper ) ?
\r
4304 SW_SHOW : SW_HIDE);
\r
4305 /* [HGM] Only allow C & A promotions if these pieces are defined */
\r
4306 ShowWindow(GetDlgItem(hDlg, PB_Archbishop),
\r
4307 ((PieceToChar(WhiteAngel) >= 'A' && WhiteOnMove(currentMove) &&
\r
4308 PieceToChar(WhiteAngel) != '~') ||
\r
4309 (PieceToChar(BlackAngel) >= 'A' && !WhiteOnMove(currentMove) &&
\r
4310 PieceToChar(BlackAngel) != '~') ) ?
\r
4311 SW_SHOW : SW_HIDE);
\r
4312 ShowWindow(GetDlgItem(hDlg, PB_Chancellor),
\r
4313 ((PieceToChar(WhiteMarshall) >= 'A' && WhiteOnMove(currentMove) &&
\r
4314 PieceToChar(WhiteMarshall) != '~') ||
\r
4315 (PieceToChar(BlackMarshall) >= 'A' && !WhiteOnMove(currentMove) &&
\r
4316 PieceToChar(BlackMarshall) != '~') ) ?
\r
4317 SW_SHOW : SW_HIDE);
\r
4318 /* [HGM] Hide B & R button in Shogi, use Q as promote, N as defer */
\r
4319 ShowWindow(GetDlgItem(hDlg, PB_Rook),
\r
4320 gameInfo.variant != VariantShogi ?
\r
4321 SW_SHOW : SW_HIDE);
\r
4322 ShowWindow(GetDlgItem(hDlg, PB_Bishop),
\r
4323 gameInfo.variant != VariantShogi ?
\r
4324 SW_SHOW : SW_HIDE);
\r
4325 if(gameInfo.variant == VariantShogi) {
\r
4326 SetDlgItemText(hDlg, PB_Queen, "YES");
\r
4327 SetDlgItemText(hDlg, PB_Knight, "NO");
\r
4328 SetWindowText(hDlg, "Promote?");
\r
4330 ShowWindow(GetDlgItem(hDlg, IDC_Centaur),
\r
4331 gameInfo.variant == VariantSuper ?
\r
4332 SW_SHOW : SW_HIDE);
\r
4335 case WM_COMMAND: /* message: received a command */
\r
4336 switch (LOWORD(wParam)) {
\r
4338 EndDialog(hDlg, TRUE); /* Exit the dialog */
\r
4339 ClearHighlights();
\r
4340 DrawPosition(FALSE, NULL);
\r
4343 promoChar = gameInfo.variant == VariantSuper ? PieceToChar(BlackSilver) : PieceToChar(BlackKing);
\r
4346 promoChar = gameInfo.variant == VariantShogi ? '+' : ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteQueen : BlackQueen));
\r
4349 promoChar = ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteRook : BlackRook));
\r
4350 if(gameInfo.variant == VariantSpartan && !WhiteOnMove(currentMove)) promoChar = PieceToChar(BlackDragon);
\r
4353 promoChar = ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteBishop : BlackBishop));
\r
4354 if(gameInfo.variant == VariantSpartan && !WhiteOnMove(currentMove)) promoChar = PieceToChar(BlackAlfil);
\r
4356 case PB_Chancellor:
\r
4357 promoChar = ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteMarshall : BlackMarshall));
\r
4359 case PB_Archbishop:
\r
4360 promoChar = ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteAngel : BlackAngel));
\r
4363 promoChar = gameInfo.variant == VariantShogi ? '=' : PieceToChar(WhiteOnMove(currentMove) ? WhiteKnight : BlackKnight);
\r
4368 if(promoChar == '.') return FALSE; // invalid piece chosen
\r
4369 EndDialog(hDlg, TRUE); /* Exit the dialog */
\r
4370 UserMoveEvent(fromX, fromY, toX, toY, promoChar);
\r
4371 fromX = fromY = -1;
\r
4372 if (!appData.highlightLastMove) {
\r
4373 ClearHighlights();
\r
4374 DrawPosition(FALSE, NULL);
\r
4381 /* Pop up promotion dialog */
\r
4383 PromotionPopup(HWND hwnd)
\r
4387 lpProc = MakeProcInstance((FARPROC)Promotion, hInst);
\r
4388 DialogBox(hInst, MAKEINTRESOURCE(DLG_PromotionKing),
\r
4389 hwnd, (DLGPROC)lpProc);
\r
4390 FreeProcInstance(lpProc);
\r
4396 DrawPosition(TRUE, NULL);
\r
4397 PromotionPopup(hwndMain);
\r
4400 /* Toggle ShowThinking */
\r
4402 ToggleShowThinking()
\r
4404 appData.showThinking = !appData.showThinking;
\r
4405 ShowThinkingEvent();
\r
4409 LoadGameDialog(HWND hwnd, char* title)
\r
4413 char fileTitle[MSG_SIZ];
\r
4414 f = OpenFileDialog(hwnd, "rb", "",
\r
4415 appData.oldSaveStyle ? "gam" : "pgn",
\r
4417 title, &number, fileTitle, NULL);
\r
4419 cmailMsgLoaded = FALSE;
\r
4420 if (number == 0) {
\r
4421 int error = GameListBuild(f);
\r
4423 DisplayError(_("Cannot build game list"), error);
\r
4424 } else if (!ListEmpty(&gameList) &&
\r
4425 ((ListGame *) gameList.tailPred)->number > 1) {
\r
4426 GameListPopUp(f, fileTitle);
\r
4429 GameListDestroy();
\r
4432 LoadGame(f, number, fileTitle, FALSE);
\r
4436 int get_term_width()
\r
4441 HFONT hfont, hold_font;
\r
4446 hText = GetDlgItem(hwndConsole, OPT_ConsoleText);
\r
4450 // get the text metrics
\r
4451 hdc = GetDC(hText);
\r
4452 lf = font[boardSize][CONSOLE_FONT]->lf;
\r
4453 if (consoleCF.dwEffects & CFE_BOLD)
\r
4454 lf.lfWeight = FW_BOLD;
\r
4455 if (consoleCF.dwEffects & CFE_ITALIC)
\r
4456 lf.lfItalic = TRUE;
\r
4457 if (consoleCF.dwEffects & CFE_STRIKEOUT)
\r
4458 lf.lfStrikeOut = TRUE;
\r
4459 if (consoleCF.dwEffects & CFE_UNDERLINE)
\r
4460 lf.lfUnderline = TRUE;
\r
4461 hfont = CreateFontIndirect(&lf);
\r
4462 hold_font = SelectObject(hdc, hfont);
\r
4463 GetTextMetrics(hdc, &tm);
\r
4464 SelectObject(hdc, hold_font);
\r
4465 DeleteObject(hfont);
\r
4466 ReleaseDC(hText, hdc);
\r
4468 // get the rectangle
\r
4469 SendMessage(hText, EM_GETRECT, 0, (LPARAM)&rc);
\r
4471 return (rc.right-rc.left) / tm.tmAveCharWidth;
\r
4474 void UpdateICSWidth(HWND hText)
\r
4476 LONG old_width, new_width;
\r
4478 new_width = get_term_width(hText, FALSE);
\r
4479 old_width = GetWindowLong(hText, GWL_USERDATA);
\r
4480 if (new_width != old_width)
\r
4482 ics_update_width(new_width);
\r
4483 SetWindowLong(hText, GWL_USERDATA, new_width);
\r
4488 ChangedConsoleFont()
\r
4491 CHARRANGE tmpsel, sel;
\r
4492 MyFont *f = font[boardSize][CONSOLE_FONT];
\r
4493 HWND hText = GetDlgItem(hwndConsole, OPT_ConsoleText);
\r
4494 HWND hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
4497 cfmt.cbSize = sizeof(CHARFORMAT);
\r
4498 cfmt.dwMask = CFM_FACE|CFM_SIZE|CFM_CHARSET;
\r
4499 safeStrCpy(cfmt.szFaceName, font[boardSize][CONSOLE_FONT]->mfp.faceName,
\r
4500 sizeof(cfmt.szFaceName)/sizeof(cfmt.szFaceName[0]) );
\r
4501 /* yHeight is expressed in twips. A twip is 1/20 of a font's point
\r
4502 * size. This was undocumented in the version of MSVC++ that I had
\r
4503 * when I wrote the code, but is apparently documented now.
\r
4505 cfmt.yHeight = (int)(f->mfp.pointSize * 20.0 + 0.5);
\r
4506 cfmt.bCharSet = f->lf.lfCharSet;
\r
4507 cfmt.bPitchAndFamily = f->lf.lfPitchAndFamily;
\r
4508 SendMessage(hText, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &cfmt);
\r
4509 SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &cfmt);
\r
4510 /* Why are the following seemingly needed too? */
\r
4511 SendMessage(hText, EM_SETCHARFORMAT, SCF_DEFAULT, (LPARAM) &cfmt);
\r
4512 SendMessage(hInput, EM_SETCHARFORMAT, SCF_DEFAULT, (LPARAM) &cfmt);
\r
4513 SendMessage(hText, EM_EXGETSEL, 0, (LPARAM)&sel);
\r
4515 tmpsel.cpMax = -1; /*999999?*/
\r
4516 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&tmpsel);
\r
4517 SendMessage(hText, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cfmt);
\r
4518 /* Trying putting this here too. It still seems to tickle a RichEdit
\r
4519 * bug: sometimes RichEdit indents the first line of a paragraph too.
\r
4521 paraf.cbSize = sizeof(paraf);
\r
4522 paraf.dwMask = PFM_OFFSET | PFM_STARTINDENT;
\r
4523 paraf.dxStartIndent = 0;
\r
4524 paraf.dxOffset = WRAP_INDENT;
\r
4525 SendMessage(hText, EM_SETPARAFORMAT, 0, (LPARAM) ¶f);
\r
4526 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
4527 UpdateICSWidth(hText);
\r
4530 /*---------------------------------------------------------------------------*\
\r
4532 * Window Proc for main window
\r
4534 \*---------------------------------------------------------------------------*/
\r
4536 /* Process messages for main window, etc. */
\r
4538 WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
\r
4541 int wmId, wmEvent;
\r
4545 char fileTitle[MSG_SIZ];
\r
4546 char buf[MSG_SIZ];
\r
4547 static SnapData sd;
\r
4549 switch (message) {
\r
4551 case WM_PAINT: /* message: repaint portion of window */
\r
4555 case WM_ERASEBKGND:
\r
4556 if (IsIconic(hwnd)) {
\r
4557 /* Cheat; change the message */
\r
4558 return (DefWindowProc(hwnd, WM_ICONERASEBKGND, wParam, lParam));
\r
4560 return (DefWindowProc(hwnd, message, wParam, lParam));
\r
4564 case WM_LBUTTONDOWN:
\r
4565 case WM_MBUTTONDOWN:
\r
4566 case WM_RBUTTONDOWN:
\r
4567 case WM_LBUTTONUP:
\r
4568 case WM_MBUTTONUP:
\r
4569 case WM_RBUTTONUP:
\r
4570 case WM_MOUSEMOVE:
\r
4571 case WM_MOUSEWHEEL:
\r
4572 MouseEvent(hwnd, message, wParam, lParam);
\r
4575 JAWS_KB_NAVIGATION
\r
4579 JAWS_ALT_INTERCEPT
\r
4581 if (appData.icsActive && ((char)wParam == '\r' || (char)wParam > ' ' && !((char)wParam >= '1' && (char)wParam <= '9'))) {
\r
4582 // [HGM] movenum: for non-zero digits we always do type-in dialog
\r
4583 HWND h = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
4584 if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);
\r
4586 SendMessage(h, message, wParam, lParam);
\r
4587 } else if(lParam != KF_REPEAT) {
\r
4588 if (isalpha((char)wParam) || isdigit((char)wParam)) {
\r
4589 PopUpMoveDialog((char)wParam);
\r
4590 } else if((char)wParam == 003) CopyGameToClipboard();
\r
4591 else if((char)wParam == 026) PasteGameOrFENFromClipboard();
\r
4596 case WM_PALETTECHANGED:
\r
4597 if (hwnd != (HWND)wParam && !appData.monoMode) {
\r
4599 HDC hdc = GetDC(hwndMain);
\r
4600 SelectPalette(hdc, hPal, TRUE);
\r
4601 nnew = RealizePalette(hdc);
\r
4603 paletteChanged = TRUE;
\r
4604 InvalidateRect(hwnd, &boardRect, FALSE);
\r
4606 ReleaseDC(hwnd, hdc);
\r
4610 case WM_QUERYNEWPALETTE:
\r
4611 if (!appData.monoMode /*&& paletteChanged*/) {
\r
4613 HDC hdc = GetDC(hwndMain);
\r
4614 paletteChanged = FALSE;
\r
4615 SelectPalette(hdc, hPal, FALSE);
\r
4616 nnew = RealizePalette(hdc);
\r
4618 InvalidateRect(hwnd, &boardRect, FALSE);
\r
4620 ReleaseDC(hwnd, hdc);
\r
4625 case WM_COMMAND: /* message: command from application menu */
\r
4626 wmId = LOWORD(wParam);
\r
4627 wmEvent = HIWORD(wParam);
\r
4632 SAY("new game enter a move to play against the computer with white");
\r
4635 case IDM_NewGameFRC:
\r
4636 if( NewGameFRC() == 0 ) {
\r
4641 case IDM_NewVariant:
\r
4642 NewVariantPopup(hwnd);
\r
4645 case IDM_LoadGame:
\r
4646 LoadGameDialog(hwnd, _("Load Game from File"));
\r
4649 case IDM_LoadNextGame:
\r
4653 case IDM_LoadPrevGame:
\r
4657 case IDM_ReloadGame:
\r
4661 case IDM_LoadPosition:
\r
4662 if (gameMode == AnalyzeMode || gameMode == AnalyzeFile) {
\r
4663 Reset(FALSE, TRUE);
\r
4666 f = OpenFileDialog(hwnd, "rb", "",
\r
4667 appData.oldSaveStyle ? "pos" : "fen",
\r
4669 _("Load Position from File"), &number, fileTitle, NULL);
\r
4671 LoadPosition(f, number, fileTitle);
\r
4675 case IDM_LoadNextPosition:
\r
4676 ReloadPosition(1);
\r
4679 case IDM_LoadPrevPosition:
\r
4680 ReloadPosition(-1);
\r
4683 case IDM_ReloadPosition:
\r
4684 ReloadPosition(0);
\r
4687 case IDM_SaveGame:
\r
4688 defName = DefaultFileName(appData.oldSaveStyle ? "gam" : "pgn");
\r
4689 f = OpenFileDialog(hwnd, "a", defName,
\r
4690 appData.oldSaveStyle ? "gam" : "pgn",
\r
4692 _("Save Game to File"), NULL, fileTitle, NULL);
\r
4694 SaveGame(f, 0, "");
\r
4698 case IDM_SavePosition:
\r
4699 defName = DefaultFileName(appData.oldSaveStyle ? "pos" : "fen");
\r
4700 f = OpenFileDialog(hwnd, "a", defName,
\r
4701 appData.oldSaveStyle ? "pos" : "fen",
\r
4703 _("Save Position to File"), NULL, fileTitle, NULL);
\r
4705 SavePosition(f, 0, "");
\r
4709 case IDM_SaveDiagram:
\r
4710 defName = "diagram";
\r
4711 f = OpenFileDialog(hwnd, "wb", defName,
\r
4714 "Save Diagram to File", NULL, fileTitle, NULL);
\r
4720 case IDM_CopyGame:
\r
4721 CopyGameToClipboard();
\r
4724 case IDM_PasteGame:
\r
4725 PasteGameFromClipboard();
\r
4728 case IDM_CopyGameListToClipboard:
\r
4729 CopyGameListToClipboard();
\r
4732 /* [AS] Autodetect FEN or PGN data */
\r
4733 case IDM_PasteAny:
\r
4734 PasteGameOrFENFromClipboard();
\r
4737 /* [AS] Move history */
\r
4738 case IDM_ShowMoveHistory:
\r
4739 if( MoveHistoryIsUp() ) {
\r
4740 MoveHistoryPopDown();
\r
4743 MoveHistoryPopUp();
\r
4747 /* [AS] Eval graph */
\r
4748 case IDM_ShowEvalGraph:
\r
4749 if( EvalGraphIsUp() ) {
\r
4750 EvalGraphPopDown();
\r
4754 SetFocus(hwndMain);
\r
4758 /* [AS] Engine output */
\r
4759 case IDM_ShowEngineOutput:
\r
4760 if( EngineOutputIsUp() ) {
\r
4761 EngineOutputPopDown();
\r
4764 EngineOutputPopUp();
\r
4768 /* [AS] User adjudication */
\r
4769 case IDM_UserAdjudication_White:
\r
4770 UserAdjudicationEvent( +1 );
\r
4773 case IDM_UserAdjudication_Black:
\r
4774 UserAdjudicationEvent( -1 );
\r
4777 case IDM_UserAdjudication_Draw:
\r
4778 UserAdjudicationEvent( 0 );
\r
4781 /* [AS] Game list options dialog */
\r
4782 case IDM_GameListOptions:
\r
4783 GameListOptions();
\r
4790 case IDM_CopyPosition:
\r
4791 CopyFENToClipboard();
\r
4794 case IDM_PastePosition:
\r
4795 PasteFENFromClipboard();
\r
4798 case IDM_MailMove:
\r
4802 case IDM_ReloadCMailMsg:
\r
4803 Reset(TRUE, TRUE);
\r
4804 ReloadCmailMsgEvent(FALSE);
\r
4807 case IDM_Minimize:
\r
4808 ShowWindow(hwnd, SW_MINIMIZE);
\r
4815 case IDM_MachineWhite:
\r
4816 MachineWhiteEvent();
\r
4818 * refresh the tags dialog only if it's visible
\r
4820 if (gameMode == MachinePlaysWhite && IsWindowVisible(editTagsDialog)) {
\r
4822 tags = PGNTags(&gameInfo);
\r
4823 TagsPopUp(tags, CmailMsg());
\r
4826 SAY("computer starts playing white");
\r
4829 case IDM_MachineBlack:
\r
4830 MachineBlackEvent();
\r
4832 * refresh the tags dialog only if it's visible
\r
4834 if (gameMode == MachinePlaysBlack && IsWindowVisible(editTagsDialog)) {
\r
4836 tags = PGNTags(&gameInfo);
\r
4837 TagsPopUp(tags, CmailMsg());
\r
4840 SAY("computer starts playing black");
\r
4843 case IDM_Match: // [HGM] match: flows into next case, after setting Match Mode and nr of Games
\r
4844 if(gameMode != BeginningOfGame) { // allow menu item to remain enabled for better mode highligting
\r
4845 DisplayError(_("You can only start a match from the initial position."), 0); break;
\r
4847 matchMode = 2;// distinguish from command-line-triggered case (matchMode=1)
\r
4848 appData.matchGames = appData.defaultMatchGames;
\r
4850 first.matchWins = second.matchWins = 0;
\r
4852 case IDM_TwoMachines:
\r
4853 TwoMachinesEvent();
\r
4855 * refresh the tags dialog only if it's visible
\r
4857 if (gameMode == TwoMachinesPlay && IsWindowVisible(editTagsDialog)) {
\r
4859 tags = PGNTags(&gameInfo);
\r
4860 TagsPopUp(tags, CmailMsg());
\r
4863 SAY("computer starts playing both sides");
\r
4866 case IDM_AnalysisMode:
\r
4867 if (!first.analysisSupport) {
\r
4868 snprintf(buf, MSG_SIZ, _("%s does not support analysis"), first.tidy);
\r
4869 DisplayError(buf, 0);
\r
4871 SAY("analyzing current position");
\r
4872 /* [DM] icsEngineAnlyze [HGM] Why is this front-end??? */
\r
4873 if (appData.icsActive) {
\r
4874 if (gameMode != IcsObserving) {
\r
4875 snprintf(buf, MSG_SIZ, "You are not observing a game");
\r
4876 DisplayError(buf, 0);
\r
4877 /* secure check */
\r
4878 if (appData.icsEngineAnalyze) {
\r
4879 if (appData.debugMode)
\r
4880 fprintf(debugFP, "Found unexpected active ICS engine analyze \n");
\r
4881 ExitAnalyzeMode();
\r
4887 /* if enable, user want disable icsEngineAnalyze */
\r
4888 if (appData.icsEngineAnalyze) {
\r
4889 ExitAnalyzeMode();
\r
4893 appData.icsEngineAnalyze = TRUE;
\r
4894 if (appData.debugMode) fprintf(debugFP, "ICS engine analyze starting...\n");
\r
4897 if (!appData.showThinking) ToggleShowThinking();
\r
4898 AnalyzeModeEvent();
\r
4902 case IDM_AnalyzeFile:
\r
4903 if (!first.analysisSupport) {
\r
4904 char buf[MSG_SIZ];
\r
4905 snprintf(buf, MSG_SIZ, _("%s does not support analysis"), first.tidy);
\r
4906 DisplayError(buf, 0);
\r
4908 if (!appData.showThinking) ToggleShowThinking();
\r
4909 AnalyzeFileEvent();
\r
4910 LoadGameDialog(hwnd, _("Analyze Game from File"));
\r
4911 AnalysisPeriodicEvent(1);
\r
4915 case IDM_IcsClient:
\r
4919 case IDM_EditGame:
\r
4920 case IDM_EditGame2:
\r
4925 case IDM_EditPosition:
\r
4926 case IDM_EditPosition2:
\r
4927 EditPositionEvent();
\r
4928 SAY("enter a FEN string or setup a position on the board using the control R pop up menu");
\r
4931 case IDM_Training:
\r
4935 case IDM_ShowGameList:
\r
4936 ShowGameListProc();
\r
4939 case IDM_EditProgs1:
\r
4940 EditTagsPopUp(firstChessProgramNames, &firstChessProgramNames);
\r
4943 case IDM_EditProgs2:
\r
4944 EditTagsPopUp(secondChessProgramNames, &secondChessProgramNames);
\r
4947 case IDM_EditServers:
\r
4948 EditTagsPopUp(icsNames, &icsNames);
\r
4951 case IDM_EditTags:
\r
4956 case IDM_EditComment:
\r
4958 if (commentUp && editComment) {
\r
4961 EditCommentEvent();
\r
4981 case IDM_CallFlag:
\r
5001 case IDM_StopObserving:
\r
5002 StopObservingEvent();
\r
5005 case IDM_StopExamining:
\r
5006 StopExaminingEvent();
\r
5010 UploadGameEvent();
\r
5013 case IDM_TypeInMove:
\r
5014 PopUpMoveDialog('\000');
\r
5017 case IDM_TypeInName:
\r
5018 PopUpNameDialog('\000');
\r
5021 case IDM_Backward:
\r
5023 SetFocus(hwndMain);
\r
5030 SetFocus(hwndMain);
\r
5035 SetFocus(hwndMain);
\r
5040 SetFocus(hwndMain);
\r
5044 RevertEvent(FALSE);
\r
5047 case IDM_Annotate: // [HGM] vari: revert with annotation
\r
5048 RevertEvent(TRUE);
\r
5051 case IDM_TruncateGame:
\r
5052 TruncateGameEvent();
\r
5059 case IDM_RetractMove:
\r
5060 RetractMoveEvent();
\r
5063 case IDM_FlipView:
\r
5064 flipView = !flipView;
\r
5065 DrawPosition(FALSE, NULL);
\r
5068 case IDM_FlipClock:
\r
5069 flipClock = !flipClock;
\r
5070 DisplayBothClocks();
\r
5071 DrawPosition(FALSE, NULL);
\r
5074 case IDM_MuteSounds:
\r
5075 mute = !mute; // [HGM] mute: keep track of global muting variable
\r
5076 CheckMenuItem(GetMenu(hwndMain),IDM_MuteSounds,
\r
5077 MF_BYCOMMAND|(mute?MF_CHECKED:MF_UNCHECKED));
\r
5080 case IDM_GeneralOptions:
\r
5081 GeneralOptionsPopup(hwnd);
\r
5082 DrawPosition(TRUE, NULL);
\r
5085 case IDM_BoardOptions:
\r
5086 BoardOptionsPopup(hwnd);
\r
5089 case IDM_EnginePlayOptions:
\r
5090 EnginePlayOptionsPopup(hwnd);
\r
5093 case IDM_Engine1Options:
\r
5094 EngineOptionsPopup(hwnd, &first);
\r
5097 case IDM_Engine2Options:
\r
5099 if(WaitForSecond(SettingsMenuIfReady)) break;
\r
5100 EngineOptionsPopup(hwnd, &second);
\r
5103 case IDM_OptionsUCI:
\r
5104 UciOptionsPopup(hwnd);
\r
5107 case IDM_IcsOptions:
\r
5108 IcsOptionsPopup(hwnd);
\r
5112 FontsOptionsPopup(hwnd);
\r
5116 SoundOptionsPopup(hwnd);
\r
5119 case IDM_CommPort:
\r
5120 CommPortOptionsPopup(hwnd);
\r
5123 case IDM_LoadOptions:
\r
5124 LoadOptionsPopup(hwnd);
\r
5127 case IDM_SaveOptions:
\r
5128 SaveOptionsPopup(hwnd);
\r
5131 case IDM_TimeControl:
\r
5132 TimeControlOptionsPopup(hwnd);
\r
5135 case IDM_SaveSettings:
\r
5136 SaveSettings(settingsFileName);
\r
5139 case IDM_SaveSettingsOnExit:
\r
5140 saveSettingsOnExit = !saveSettingsOnExit;
\r
5141 (void) CheckMenuItem(GetMenu(hwndMain), IDM_SaveSettingsOnExit,
\r
5142 MF_BYCOMMAND|(saveSettingsOnExit ?
\r
5143 MF_CHECKED : MF_UNCHECKED));
\r
5154 case IDM_AboutGame:
\r
5159 appData.debugMode = !appData.debugMode;
\r
5160 if (appData.debugMode) {
\r
5161 char dir[MSG_SIZ];
\r
5162 GetCurrentDirectory(MSG_SIZ, dir);
\r
5163 SetCurrentDirectory(installDir);
\r
5164 debugFP = fopen(appData.nameOfDebugFile, "w");
\r
5165 SetCurrentDirectory(dir);
\r
5166 setbuf(debugFP, NULL);
\r
5173 case IDM_HELPCONTENTS:
\r
5174 if (!MyHelp (hwnd, "winboard.hlp", HELP_KEY,(DWORD)(LPSTR)"CONTENTS") &&
\r
5175 !HtmlHelp(hwnd, "winboard.chm", 0, 0) ) {
\r
5176 MessageBox (GetFocus(),
\r
5177 _("Unable to activate help"),
\r
5178 szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);
\r
5182 case IDM_HELPSEARCH:
\r
5183 if (!MyHelp (hwnd, "winboard.hlp", HELP_PARTIALKEY, (DWORD)(LPSTR)"") &&
\r
5184 !HtmlHelp(hwnd, "winboard.chm", 0, 0) ) {
\r
5185 MessageBox (GetFocus(),
\r
5186 _("Unable to activate help"),
\r
5187 szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);
\r
5191 case IDM_HELPHELP:
\r
5192 if(!WinHelp(hwnd, (LPSTR)NULL, HELP_HELPONHELP, 0)) {
\r
5193 MessageBox (GetFocus(),
\r
5194 _("Unable to activate help"),
\r
5195 szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);
\r
5200 lpProc = MakeProcInstance((FARPROC)About, hInst);
\r
5202 (gameInfo.event && strcmp(gameInfo.event, "Easter Egg Hunt") == 0) ?
\r
5203 "AboutBox2" : "AboutBox", hwnd, (DLGPROC)lpProc);
\r
5204 FreeProcInstance(lpProc);
\r
5207 case IDM_DirectCommand1:
\r
5208 AskQuestionEvent(_("Direct Command"),
\r
5209 _("Send to chess program:"), "", "1");
\r
5211 case IDM_DirectCommand2:
\r
5212 AskQuestionEvent(_("Direct Command"),
\r
5213 _("Send to second chess program:"), "", "2");
\r
5216 case EP_WhitePawn:
\r
5217 EditPositionMenuEvent(WhitePawn, fromX, fromY);
\r
5218 fromX = fromY = -1;
\r
5221 case EP_WhiteKnight:
\r
5222 EditPositionMenuEvent(WhiteKnight, fromX, fromY);
\r
5223 fromX = fromY = -1;
\r
5226 case EP_WhiteBishop:
\r
5227 EditPositionMenuEvent(WhiteBishop, fromX, fromY);
\r
5228 fromX = fromY = -1;
\r
5231 case EP_WhiteRook:
\r
5232 EditPositionMenuEvent(WhiteRook, fromX, fromY);
\r
5233 fromX = fromY = -1;
\r
5236 case EP_WhiteQueen:
\r
5237 EditPositionMenuEvent(WhiteQueen, fromX, fromY);
\r
5238 fromX = fromY = -1;
\r
5241 case EP_WhiteFerz:
\r
5242 EditPositionMenuEvent(WhiteFerz, fromX, fromY);
\r
5243 fromX = fromY = -1;
\r
5246 case EP_WhiteWazir:
\r
5247 EditPositionMenuEvent(WhiteWazir, fromX, fromY);
\r
5248 fromX = fromY = -1;
\r
5251 case EP_WhiteAlfil:
\r
5252 EditPositionMenuEvent(WhiteAlfil, fromX, fromY);
\r
5253 fromX = fromY = -1;
\r
5256 case EP_WhiteCannon:
\r
5257 EditPositionMenuEvent(WhiteCannon, fromX, fromY);
\r
5258 fromX = fromY = -1;
\r
5261 case EP_WhiteCardinal:
\r
5262 EditPositionMenuEvent(WhiteAngel, fromX, fromY);
\r
5263 fromX = fromY = -1;
\r
5266 case EP_WhiteMarshall:
\r
5267 EditPositionMenuEvent(WhiteMarshall, fromX, fromY);
\r
5268 fromX = fromY = -1;
\r
5271 case EP_WhiteKing:
\r
5272 EditPositionMenuEvent(WhiteKing, fromX, fromY);
\r
5273 fromX = fromY = -1;
\r
5276 case EP_BlackPawn:
\r
5277 EditPositionMenuEvent(BlackPawn, fromX, fromY);
\r
5278 fromX = fromY = -1;
\r
5281 case EP_BlackKnight:
\r
5282 EditPositionMenuEvent(BlackKnight, fromX, fromY);
\r
5283 fromX = fromY = -1;
\r
5286 case EP_BlackBishop:
\r
5287 EditPositionMenuEvent(BlackBishop, fromX, fromY);
\r
5288 fromX = fromY = -1;
\r
5291 case EP_BlackRook:
\r
5292 EditPositionMenuEvent(BlackRook, fromX, fromY);
\r
5293 fromX = fromY = -1;
\r
5296 case EP_BlackQueen:
\r
5297 EditPositionMenuEvent(BlackQueen, fromX, fromY);
\r
5298 fromX = fromY = -1;
\r
5301 case EP_BlackFerz:
\r
5302 EditPositionMenuEvent(BlackFerz, fromX, fromY);
\r
5303 fromX = fromY = -1;
\r
5306 case EP_BlackWazir:
\r
5307 EditPositionMenuEvent(BlackWazir, fromX, fromY);
\r
5308 fromX = fromY = -1;
\r
5311 case EP_BlackAlfil:
\r
5312 EditPositionMenuEvent(BlackAlfil, fromX, fromY);
\r
5313 fromX = fromY = -1;
\r
5316 case EP_BlackCannon:
\r
5317 EditPositionMenuEvent(BlackCannon, fromX, fromY);
\r
5318 fromX = fromY = -1;
\r
5321 case EP_BlackCardinal:
\r
5322 EditPositionMenuEvent(BlackAngel, fromX, fromY);
\r
5323 fromX = fromY = -1;
\r
5326 case EP_BlackMarshall:
\r
5327 EditPositionMenuEvent(BlackMarshall, fromX, fromY);
\r
5328 fromX = fromY = -1;
\r
5331 case EP_BlackKing:
\r
5332 EditPositionMenuEvent(BlackKing, fromX, fromY);
\r
5333 fromX = fromY = -1;
\r
5336 case EP_EmptySquare:
\r
5337 EditPositionMenuEvent(EmptySquare, fromX, fromY);
\r
5338 fromX = fromY = -1;
\r
5341 case EP_ClearBoard:
\r
5342 EditPositionMenuEvent(ClearBoard, fromX, fromY);
\r
5343 fromX = fromY = -1;
\r
5347 EditPositionMenuEvent(WhitePlay, fromX, fromY);
\r
5348 fromX = fromY = -1;
\r
5352 EditPositionMenuEvent(BlackPlay, fromX, fromY);
\r
5353 fromX = fromY = -1;
\r
5357 EditPositionMenuEvent(PromotePiece, fromX, fromY);
\r
5358 fromX = fromY = -1;
\r
5362 EditPositionMenuEvent(DemotePiece, fromX, fromY);
\r
5363 fromX = fromY = -1;
\r
5367 DropMenuEvent(WhitePawn, fromX, fromY);
\r
5368 fromX = fromY = -1;
\r
5372 DropMenuEvent(WhiteKnight, fromX, fromY);
\r
5373 fromX = fromY = -1;
\r
5377 DropMenuEvent(WhiteBishop, fromX, fromY);
\r
5378 fromX = fromY = -1;
\r
5382 DropMenuEvent(WhiteRook, fromX, fromY);
\r
5383 fromX = fromY = -1;
\r
5387 DropMenuEvent(WhiteQueen, fromX, fromY);
\r
5388 fromX = fromY = -1;
\r
5393 TranslateMenus(0);
\r
5394 CheckMenuItem(GetMenu(hwndMain), lastChecked, MF_BYCOMMAND|MF_UNCHECKED);
\r
5395 CheckMenuItem(GetMenu(hwndMain), IDM_English, MF_BYCOMMAND|MF_CHECKED);
\r
5396 lastChecked = wmId;
\r
5400 if(wmId > IDM_English && wmId < IDM_English+5) {
\r
5401 LoadLanguageFile(languageFile[wmId - IDM_English - 1]);
\r
5402 TranslateMenus(0);
\r
5403 CheckMenuItem(GetMenu(hwndMain), lastChecked, MF_BYCOMMAND|MF_UNCHECKED);
\r
5404 CheckMenuItem(GetMenu(hwndMain), wmId, MF_BYCOMMAND|MF_CHECKED);
\r
5405 lastChecked = wmId;
\r
5408 return (DefWindowProc(hwnd, message, wParam, lParam));
\r
5414 case CLOCK_TIMER_ID:
\r
5415 KillTimer(hwnd, clockTimerEvent); /* Simulate one-shot timer as in X */
\r
5416 clockTimerEvent = 0;
\r
5417 DecrementClocks(); /* call into back end */
\r
5419 case LOAD_GAME_TIMER_ID:
\r
5420 KillTimer(hwnd, loadGameTimerEvent); /* Simulate one-shot timer as in X*/
\r
5421 loadGameTimerEvent = 0;
\r
5422 AutoPlayGameLoop(); /* call into back end */
\r
5424 case ANALYSIS_TIMER_ID:
\r
5425 if ((gameMode == AnalyzeMode || gameMode == AnalyzeFile
\r
5426 || appData.icsEngineAnalyze) && appData.periodicUpdates) {
\r
5427 AnalysisPeriodicEvent(0);
\r
5429 KillTimer(hwnd, analysisTimerEvent);
\r
5430 analysisTimerEvent = 0;
\r
5433 case DELAYED_TIMER_ID:
\r
5434 KillTimer(hwnd, delayedTimerEvent);
\r
5435 delayedTimerEvent = 0;
\r
5436 delayedTimerCallback();
\r
5441 case WM_USER_Input:
\r
5442 InputEvent(hwnd, message, wParam, lParam);
\r
5445 /* [AS] Also move "attached" child windows */
\r
5446 case WM_WINDOWPOSCHANGING:
\r
5448 if( hwnd == hwndMain && appData.useStickyWindows ) {
\r
5449 LPWINDOWPOS lpwp = (LPWINDOWPOS) lParam;
\r
5451 if( ((lpwp->flags & SWP_NOMOVE) == 0) && ((lpwp->flags & SWP_NOSIZE) != 0) ) {
\r
5452 /* Window is moving */
\r
5455 // GetWindowRect( hwnd, &rcMain ); //[HGM] sticky: in XP this returned new position, not old
\r
5456 rcMain.left = wpMain.x; // replace by these 4 lines to reconstruct old rect
\r
5457 rcMain.right = wpMain.x + wpMain.width;
\r
5458 rcMain.top = wpMain.y;
\r
5459 rcMain.bottom = wpMain.y + wpMain.height;
\r
5461 ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, moveHistoryDialog, &wpMoveHistory );
\r
5462 ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, evalGraphDialog, &wpEvalGraph );
\r
5463 ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, engineOutputDialog, &wpEngineOutput );
\r
5464 ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, gameListDialog, &wpGameList );
\r
5465 ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, hwndConsole, &wpConsole );
\r
5466 wpMain.x = lpwp->x;
\r
5467 wpMain.y = lpwp->y;
\r
5472 /* [AS] Snapping */
\r
5473 case WM_ENTERSIZEMOVE:
\r
5474 if(appData.debugMode) { fprintf(debugFP, "size-move\n"); }
\r
5475 if (hwnd == hwndMain) {
\r
5476 doingSizing = TRUE;
\r
5479 return OnEnterSizeMove( &sd, hwnd, wParam, lParam );
\r
5483 if(appData.debugMode) { fprintf(debugFP, "sizing\n"); }
\r
5484 if (hwnd == hwndMain) {
\r
5485 lastSizing = wParam;
\r
5490 if(appData.debugMode) { fprintf(debugFP, "moving\n"); }
\r
5491 return OnMoving( &sd, hwnd, wParam, lParam );
\r
5493 case WM_EXITSIZEMOVE:
\r
5494 if(appData.debugMode) { fprintf(debugFP, "exit size-move, size = %d\n", squareSize); }
\r
5495 if (hwnd == hwndMain) {
\r
5497 doingSizing = FALSE;
\r
5498 InvalidateRect(hwnd, &boardRect, FALSE);
\r
5499 GetClientRect(hwnd, &client);
\r
5500 ResizeBoard(client.right, client.bottom, lastSizing);
\r
5502 if(appData.debugMode) { fprintf(debugFP, "square size = %d\n", squareSize); }
\r
5504 return OnExitSizeMove( &sd, hwnd, wParam, lParam );
\r
5507 case WM_DESTROY: /* message: window being destroyed */
\r
5508 PostQuitMessage(0);
\r
5512 if (hwnd == hwndMain) {
\r
5517 default: /* Passes it on if unprocessed */
\r
5518 return (DefWindowProc(hwnd, message, wParam, lParam));
\r
5523 /*---------------------------------------------------------------------------*\
\r
5525 * Misc utility routines
\r
5527 \*---------------------------------------------------------------------------*/
\r
5530 * Decent random number generator, at least not as bad as Windows
\r
5531 * standard rand, which returns a value in the range 0 to 0x7fff.
\r
5533 unsigned int randstate;
\r
5538 randstate = randstate * 1664525 + 1013904223;
\r
5539 return (int) randstate & 0x7fffffff;
\r
5543 mysrandom(unsigned int seed)
\r
5550 * returns TRUE if user selects a different color, FALSE otherwise
\r
5554 ChangeColor(HWND hwnd, COLORREF *which)
\r
5556 static BOOL firstTime = TRUE;
\r
5557 static DWORD customColors[16];
\r
5559 COLORREF newcolor;
\r
5564 /* Make initial colors in use available as custom colors */
\r
5565 /* Should we put the compiled-in defaults here instead? */
\r
5567 customColors[i++] = lightSquareColor & 0xffffff;
\r
5568 customColors[i++] = darkSquareColor & 0xffffff;
\r
5569 customColors[i++] = whitePieceColor & 0xffffff;
\r
5570 customColors[i++] = blackPieceColor & 0xffffff;
\r
5571 customColors[i++] = highlightSquareColor & 0xffffff;
\r
5572 customColors[i++] = premoveHighlightColor & 0xffffff;
\r
5574 for (ccl = (ColorClass) 0; ccl < NColorClasses && i < 16; ccl++) {
\r
5575 customColors[i++] = textAttribs[ccl].color;
\r
5577 while (i < 16) customColors[i++] = RGB(255, 255, 255);
\r
5578 firstTime = FALSE;
\r
5581 cc.lStructSize = sizeof(cc);
\r
5582 cc.hwndOwner = hwnd;
\r
5583 cc.hInstance = NULL;
\r
5584 cc.rgbResult = (DWORD) (*which & 0xffffff);
\r
5585 cc.lpCustColors = (LPDWORD) customColors;
\r
5586 cc.Flags = CC_RGBINIT|CC_FULLOPEN;
\r
5588 if (!ChooseColor(&cc)) return FALSE;
\r
5590 newcolor = (COLORREF) (0x2000000 | cc.rgbResult);
\r
5591 if (newcolor == *which) return FALSE;
\r
5592 *which = newcolor;
\r
5596 InitDrawingColors();
\r
5597 InvalidateRect(hwnd, &boardRect, FALSE);
\r
5602 MyLoadSound(MySound *ms)
\r
5608 if (ms->data) free(ms->data);
\r
5611 switch (ms->name[0]) {
\r
5617 /* System sound from Control Panel. Don't preload here. */
\r
5621 if (ms->name[1] == NULLCHAR) {
\r
5622 /* "!" alone = silence */
\r
5625 /* Builtin wave resource. Error if not found. */
\r
5626 HANDLE h = FindResource(hInst, ms->name + 1, "WAVE");
\r
5627 if (h == NULL) break;
\r
5628 ms->data = (void *)LoadResource(hInst, h);
\r
5629 if (h == NULL) break;
\r
5634 /* .wav file. Error if not found. */
\r
5635 f = fopen(ms->name, "rb");
\r
5636 if (f == NULL) break;
\r
5637 if (fstat(fileno(f), &st) < 0) break;
\r
5638 ms->data = malloc(st.st_size);
\r
5639 if (fread(ms->data, st.st_size, 1, f) < 1) break;
\r
5645 char buf[MSG_SIZ];
\r
5646 snprintf(buf, MSG_SIZ, _("Error loading sound %s"), ms->name);
\r
5647 DisplayError(buf, GetLastError());
\r
5653 MyPlaySound(MySound *ms)
\r
5655 BOOLEAN ok = FALSE;
\r
5657 if(mute) return TRUE; // [HGM] mute: suppress all sound play when muted
\r
5658 switch (ms->name[0]) {
\r
5660 if(appData.debugMode) fprintf(debugFP, "silence\n");
\r
5665 /* System sound from Control Panel (deprecated feature).
\r
5666 "$" alone or an unset sound name gets default beep (still in use). */
\r
5667 if (ms->name[1]) {
\r
5668 ok = PlaySound(ms->name + 1, NULL, SND_ALIAS|SND_ASYNC);
\r
5670 if (!ok) ok = MessageBeep(MB_OK);
\r
5673 /* Builtin wave resource, or "!" alone for silence */
\r
5674 if (ms->name[1]) {
\r
5675 if (ms->data == NULL) return FALSE;
\r
5676 ok = PlaySound(ms->data, NULL, SND_MEMORY|SND_ASYNC);
\r
5682 /* .wav file. Error if not found. */
\r
5683 if (ms->data == NULL) return FALSE;
\r
5684 ok = PlaySound(ms->data, NULL, SND_MEMORY|SND_ASYNC);
\r
5687 /* Don't print an error: this can happen innocently if the sound driver
\r
5688 is busy; for instance, if another instance of WinBoard is playing
\r
5689 a sound at about the same time. */
\r
5695 OldOpenFileHook(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
5698 OPENFILENAME *ofn;
\r
5699 static UINT *number; /* gross that this is static */
\r
5701 switch (message) {
\r
5702 case WM_INITDIALOG: /* message: initialize dialog box */
\r
5703 /* Center the dialog over the application window */
\r
5704 ofn = (OPENFILENAME *) lParam;
\r
5705 if (ofn->Flags & OFN_ENABLETEMPLATE) {
\r
5706 number = (UINT *) ofn->lCustData;
\r
5707 SendMessage(GetDlgItem(hDlg, edt2), WM_SETTEXT, 0, (LPARAM) "");
\r
5711 CenterWindow (hDlg, GetWindow (hDlg, GW_OWNER));
\r
5712 Translate(hDlg, 1536);
\r
5713 return FALSE; /* Allow for further processing */
\r
5716 if ((LOWORD(wParam) == IDOK) && (number != NULL)) {
\r
5717 *number = GetDlgItemInt(hDlg, OPT_IndexNumberOld, &ok, FALSE);
\r
5719 return FALSE; /* Allow for further processing */
\r
5725 OpenFileHook(HWND hdlg, UINT uiMsg, WPARAM wParam, LPARAM lParam)
\r
5727 static UINT *number;
\r
5728 OPENFILENAME *ofname;
\r
5731 case WM_INITDIALOG:
\r
5732 Translate(hdlg, DLG_IndexNumber);
\r
5733 ofname = (OPENFILENAME *)lParam;
\r
5734 number = (UINT *)(ofname->lCustData);
\r
5737 ofnot = (OFNOTIFY *)lParam;
\r
5738 if (ofnot->hdr.code == CDN_FILEOK) {
\r
5739 *number = GetDlgItemInt(hdlg, OPT_IndexNumber, NULL, FALSE);
\r
5748 OpenFileDialog(HWND hwnd, char *write, char *defName, char *defExt, // [HGM] diag: type of 'write' now string
\r
5749 char *nameFilt, char *dlgTitle, UINT *number,
\r
5750 char fileTitle[MSG_SIZ], char fileName[MSG_SIZ])
\r
5752 OPENFILENAME openFileName;
\r
5753 char buf1[MSG_SIZ];
\r
5756 if (fileName == NULL) fileName = buf1;
\r
5757 if (defName == NULL) {
\r
5758 safeStrCpy(fileName, "*.", 3 );
\r
5759 strcat(fileName, defExt);
\r
5761 safeStrCpy(fileName, defName, MSG_SIZ );
\r
5763 if (fileTitle) safeStrCpy(fileTitle, "", MSG_SIZ );
\r
5764 if (number) *number = 0;
\r
5766 openFileName.lStructSize = sizeof(OPENFILENAME);
\r
5767 openFileName.hwndOwner = hwnd;
\r
5768 openFileName.hInstance = (HANDLE) hInst;
\r
5769 openFileName.lpstrFilter = nameFilt;
\r
5770 openFileName.lpstrCustomFilter = (LPSTR) NULL;
\r
5771 openFileName.nMaxCustFilter = 0L;
\r
5772 openFileName.nFilterIndex = 1L;
\r
5773 openFileName.lpstrFile = fileName;
\r
5774 openFileName.nMaxFile = MSG_SIZ;
\r
5775 openFileName.lpstrFileTitle = fileTitle;
\r
5776 openFileName.nMaxFileTitle = fileTitle ? MSG_SIZ : 0;
\r
5777 openFileName.lpstrInitialDir = NULL;
\r
5778 openFileName.lpstrTitle = dlgTitle;
\r
5779 openFileName.Flags = OFN_PATHMUSTEXIST | OFN_HIDEREADONLY
\r
5780 | (write[0] != 'r' ? 0 : OFN_FILEMUSTEXIST)
\r
5781 | (number ? OFN_ENABLETEMPLATE | OFN_ENABLEHOOK: 0)
\r
5782 | (oldDialog ? 0 : OFN_EXPLORER);
\r
5783 openFileName.nFileOffset = 0;
\r
5784 openFileName.nFileExtension = 0;
\r
5785 openFileName.lpstrDefExt = defExt;
\r
5786 openFileName.lCustData = (LONG) number;
\r
5787 openFileName.lpfnHook = oldDialog ?
\r
5788 (LPOFNHOOKPROC) OldOpenFileHook : (LPOFNHOOKPROC) OpenFileHook;
\r
5789 openFileName.lpTemplateName = (LPSTR)(oldDialog ? 1536 : DLG_IndexNumber);
\r
5791 if (write[0] != 'r' ? GetSaveFileName(&openFileName) :
\r
5792 GetOpenFileName(&openFileName)) {
\r
5793 /* open the file */
\r
5794 f = fopen(openFileName.lpstrFile, write);
\r
5796 MessageBox(hwnd, _("File open failed"), NULL,
\r
5797 MB_OK|MB_ICONEXCLAMATION);
\r
5801 int err = CommDlgExtendedError();
\r
5802 if (err != 0) DisplayError(_("Internal error in file dialog box"), err);
\r
5811 MenuPopup(HWND hwnd, POINT pt, HMENU hmenu, UINT def)
\r
5813 HMENU hmenuTrackPopup; /* floating pop-up menu */
\r
5816 * Get the first pop-up menu in the menu template. This is the
\r
5817 * menu that TrackPopupMenu displays.
\r
5819 hmenuTrackPopup = GetSubMenu(hmenu, 0);
\r
5820 TranslateOneMenu(10, hmenuTrackPopup);
\r
5822 SetMenuDefaultItem(hmenuTrackPopup, def, FALSE);
\r
5825 * TrackPopup uses screen coordinates, so convert the
\r
5826 * coordinates of the mouse click to screen coordinates.
\r
5828 ClientToScreen(hwnd, (LPPOINT) &pt);
\r
5830 /* Draw and track the floating pop-up menu. */
\r
5831 TrackPopupMenu(hmenuTrackPopup, TPM_CENTERALIGN | TPM_RIGHTBUTTON,
\r
5832 pt.x, pt.y, 0, hwnd, NULL);
\r
5834 /* Destroy the menu.*/
\r
5835 DestroyMenu(hmenu);
\r
5840 int sizeX, sizeY, newSizeX, newSizeY;
\r
5842 } ResizeEditPlusButtonsClosure;
\r
5845 ResizeEditPlusButtonsCallback(HWND hChild, LPARAM lparam)
\r
5847 ResizeEditPlusButtonsClosure *cl = (ResizeEditPlusButtonsClosure *)lparam;
\r
5851 if (hChild == cl->hText) return TRUE;
\r
5852 GetWindowRect(hChild, &rect); /* gives screen coords */
\r
5853 pt.x = rect.left + (cl->newSizeX - cl->sizeX)/2;
\r
5854 pt.y = rect.top + cl->newSizeY - cl->sizeY;
\r
5855 ScreenToClient(cl->hDlg, &pt);
\r
5856 cl->hdwp = DeferWindowPos(cl->hdwp, hChild, NULL,
\r
5857 pt.x, pt.y, rect.right - rect.left, rect.bottom - rect.top, SWP_NOZORDER);
\r
5861 /* Resize a dialog that has a (rich) edit field filling most of
\r
5862 the top, with a row of buttons below */
\r
5864 ResizeEditPlusButtons(HWND hDlg, HWND hText, int sizeX, int sizeY, int newSizeX, int newSizeY)
\r
5867 int newTextHeight, newTextWidth;
\r
5868 ResizeEditPlusButtonsClosure cl;
\r
5870 /*if (IsIconic(hDlg)) return;*/
\r
5871 if (newSizeX == sizeX && newSizeY == sizeY) return;
\r
5873 cl.hdwp = BeginDeferWindowPos(8);
\r
5875 GetWindowRect(hText, &rectText); /* gives screen coords */
\r
5876 newTextWidth = rectText.right - rectText.left + newSizeX - sizeX;
\r
5877 newTextHeight = rectText.bottom - rectText.top + newSizeY - sizeY;
\r
5878 if (newTextHeight < 0) {
\r
5879 newSizeY += -newTextHeight;
\r
5880 newTextHeight = 0;
\r
5882 cl.hdwp = DeferWindowPos(cl.hdwp, hText, NULL, 0, 0,
\r
5883 newTextWidth, newTextHeight, SWP_NOZORDER|SWP_NOMOVE);
\r
5889 cl.newSizeX = newSizeX;
\r
5890 cl.newSizeY = newSizeY;
\r
5891 EnumChildWindows(hDlg, ResizeEditPlusButtonsCallback, (LPARAM)&cl);
\r
5893 EndDeferWindowPos(cl.hdwp);
\r
5896 BOOL CenterWindowEx(HWND hwndChild, HWND hwndParent, int mode)
\r
5898 RECT rChild, rParent;
\r
5899 int wChild, hChild, wParent, hParent;
\r
5900 int wScreen, hScreen, xNew, yNew;
\r
5903 /* Get the Height and Width of the child window */
\r
5904 GetWindowRect (hwndChild, &rChild);
\r
5905 wChild = rChild.right - rChild.left;
\r
5906 hChild = rChild.bottom - rChild.top;
\r
5908 /* Get the Height and Width of the parent window */
\r
5909 GetWindowRect (hwndParent, &rParent);
\r
5910 wParent = rParent.right - rParent.left;
\r
5911 hParent = rParent.bottom - rParent.top;
\r
5913 /* Get the display limits */
\r
5914 hdc = GetDC (hwndChild);
\r
5915 wScreen = GetDeviceCaps (hdc, HORZRES);
\r
5916 hScreen = GetDeviceCaps (hdc, VERTRES);
\r
5917 ReleaseDC(hwndChild, hdc);
\r
5919 /* Calculate new X position, then adjust for screen */
\r
5920 xNew = rParent.left + ((wParent - wChild) /2);
\r
5923 } else if ((xNew+wChild) > wScreen) {
\r
5924 xNew = wScreen - wChild;
\r
5927 /* Calculate new Y position, then adjust for screen */
\r
5929 yNew = rParent.top + ((hParent - hChild) /2);
\r
5932 yNew = rParent.top + GetSystemMetrics( SM_CYCAPTION ) * 2 / 3;
\r
5937 } else if ((yNew+hChild) > hScreen) {
\r
5938 yNew = hScreen - hChild;
\r
5941 /* Set it, and return */
\r
5942 return SetWindowPos (hwndChild, NULL,
\r
5943 xNew, yNew, 0, 0, SWP_NOSIZE | SWP_NOZORDER);
\r
5946 /* Center one window over another */
\r
5947 BOOL CenterWindow (HWND hwndChild, HWND hwndParent)
\r
5949 return CenterWindowEx( hwndChild, hwndParent, 0 );
\r
5952 /*---------------------------------------------------------------------------*\
\r
5954 * Startup Dialog functions
\r
5956 \*---------------------------------------------------------------------------*/
\r
5958 InitComboStrings(HANDLE hwndCombo, char **cd)
\r
5960 SendMessage(hwndCombo, CB_RESETCONTENT, 0, 0);
\r
5962 while (*cd != NULL) {
\r
5963 SendMessage(hwndCombo, CB_ADDSTRING, 0, (LPARAM) T_(*cd));
\r
5969 InitComboStringsFromOption(HANDLE hwndCombo, char *str)
\r
5971 char buf1[MAX_ARG_LEN];
\r
5974 if (str[0] == '@') {
\r
5975 FILE* f = fopen(str + 1, "r");
\r
5977 DisplayFatalError(str + 1, errno, 2);
\r
5980 len = fread(buf1, 1, sizeof(buf1)-1, f);
\r
5982 buf1[len] = NULLCHAR;
\r
5986 SendMessage(hwndCombo, CB_RESETCONTENT, 0, 0);
\r
5989 char buf[MSG_SIZ];
\r
5990 char *end = strchr(str, '\n');
\r
5991 if (end == NULL) return;
\r
5992 memcpy(buf, str, end - str);
\r
5993 buf[end - str] = NULLCHAR;
\r
5994 SendMessage(hwndCombo, CB_ADDSTRING, 0, (LPARAM) buf);
\r
6000 SetStartupDialogEnables(HWND hDlg)
\r
6002 EnableWindow(GetDlgItem(hDlg, OPT_ChessEngineName),
\r
6003 IsDlgButtonChecked(hDlg, OPT_ChessEngine) ||
\r
6004 (appData.zippyPlay && IsDlgButtonChecked(hDlg, OPT_ChessServer)));
\r
6005 EnableWindow(GetDlgItem(hDlg, OPT_SecondChessEngineName),
\r
6006 IsDlgButtonChecked(hDlg, OPT_ChessEngine));
\r
6007 EnableWindow(GetDlgItem(hDlg, OPT_ChessServerName),
\r
6008 IsDlgButtonChecked(hDlg, OPT_ChessServer));
\r
6009 EnableWindow(GetDlgItem(hDlg, OPT_AdditionalOptions),
\r
6010 IsDlgButtonChecked(hDlg, OPT_AnyAdditional));
\r
6011 EnableWindow(GetDlgItem(hDlg, IDOK),
\r
6012 IsDlgButtonChecked(hDlg, OPT_ChessEngine) ||
\r
6013 IsDlgButtonChecked(hDlg, OPT_ChessServer) ||
\r
6014 IsDlgButtonChecked(hDlg, OPT_View));
\r
6018 QuoteForFilename(char *filename)
\r
6020 int dquote, space;
\r
6021 dquote = strchr(filename, '"') != NULL;
\r
6022 space = strchr(filename, ' ') != NULL;
\r
6023 if (dquote || space) {
\r
6035 InitEngineBox(HWND hDlg, HWND hwndCombo, char* nthcp, char* nthd, char* nthdir, char *nthnames)
\r
6037 char buf[MSG_SIZ];
\r
6040 InitComboStringsFromOption(hwndCombo, nthnames);
\r
6041 q = QuoteForFilename(nthcp);
\r
6042 snprintf(buf, MSG_SIZ, "%s%s%s", q, nthcp, q);
\r
6043 if (*nthdir != NULLCHAR) {
\r
6044 q = QuoteForFilename(nthdir);
\r
6045 snprintf(buf + strlen(buf), MSG_SIZ, " /%s=%s%s%s", nthd, q, nthdir, q);
\r
6047 if (*nthcp == NULLCHAR) {
\r
6048 SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) 0, (LPARAM) 0);
\r
6049 } else if (SendMessage(hwndCombo, CB_SELECTSTRING, (WPARAM) -1, (LPARAM) buf) == CB_ERR) {
\r
6050 SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) -1, (LPARAM) 0);
\r
6051 SendMessage(hwndCombo, WM_SETTEXT, (WPARAM) 0, (LPARAM) buf);
\r
6056 StartupDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
6058 char buf[MSG_SIZ];
\r
6062 switch (message) {
\r
6063 case WM_INITDIALOG:
\r
6064 /* Center the dialog */
\r
6065 CenterWindow (hDlg, GetDesktopWindow());
\r
6066 Translate(hDlg, DLG_Startup);
\r
6067 /* Initialize the dialog items */
\r
6068 InitEngineBox(hDlg, GetDlgItem(hDlg, OPT_ChessEngineName),
\r
6069 appData.firstChessProgram, "fd", appData.firstDirectory,
\r
6070 firstChessProgramNames);
\r
6071 InitEngineBox(hDlg, GetDlgItem(hDlg, OPT_SecondChessEngineName),
\r
6072 appData.secondChessProgram, "sd", appData.secondDirectory,
\r
6073 secondChessProgramNames);
\r
6074 hwndCombo = GetDlgItem(hDlg, OPT_ChessServerName);
\r
6075 InitComboStringsFromOption(hwndCombo, icsNames);
\r
6076 snprintf(buf, MSG_SIZ, "%s /icsport=%s", appData.icsHost, appData.icsPort);
\r
6077 if (*appData.icsHelper != NULLCHAR) {
\r
6078 char *q = QuoteForFilename(appData.icsHelper);
\r
6079 sprintf(buf + strlen(buf), " /icshelper=%s%s%s", q, appData.icsHelper, q);
\r
6081 if (*appData.icsHost == NULLCHAR) {
\r
6082 SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) 0, (LPARAM) 0);
\r
6083 /*SendMessage(hwndCombo, CB_SHOWDROPDOWN, (WPARAM) TRUE, (LPARAM) 0); !!too soon */
\r
6084 } else if (SendMessage(hwndCombo, CB_SELECTSTRING, (WPARAM) -1, (LPARAM) buf) == CB_ERR) {
\r
6085 SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) -1, (LPARAM) 0);
\r
6086 SendMessage(hwndCombo, WM_SETTEXT, (WPARAM) 0, (LPARAM) buf);
\r
6089 if (appData.icsActive) {
\r
6090 CheckDlgButton(hDlg, OPT_ChessServer, BST_CHECKED);
\r
6092 else if (appData.noChessProgram) {
\r
6093 CheckDlgButton(hDlg, OPT_View, BST_CHECKED);
\r
6096 CheckDlgButton(hDlg, OPT_ChessEngine, BST_CHECKED);
\r
6099 SetStartupDialogEnables(hDlg);
\r
6103 switch (LOWORD(wParam)) {
\r
6105 if (IsDlgButtonChecked(hDlg, OPT_ChessEngine)) {
\r
6106 safeStrCpy(buf, "/fcp=", sizeof(buf)/sizeof(buf[0]) );
\r
6107 GetDlgItemText(hDlg, OPT_ChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));
\r
6109 ParseArgs(StringGet, &p);
\r
6110 safeStrCpy(buf, "/scp=", sizeof(buf)/sizeof(buf[0]) );
\r
6111 GetDlgItemText(hDlg, OPT_SecondChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));
\r
6113 ParseArgs(StringGet, &p);
\r
6114 appData.noChessProgram = FALSE;
\r
6115 appData.icsActive = FALSE;
\r
6116 } else if (IsDlgButtonChecked(hDlg, OPT_ChessServer)) {
\r
6117 safeStrCpy(buf, "/ics /icshost=", sizeof(buf)/sizeof(buf[0]) );
\r
6118 GetDlgItemText(hDlg, OPT_ChessServerName, buf + strlen(buf), sizeof(buf) - strlen(buf));
\r
6120 ParseArgs(StringGet, &p);
\r
6121 if (appData.zippyPlay) {
\r
6122 safeStrCpy(buf, "/fcp=", sizeof(buf)/sizeof(buf[0]) );
\r
6123 GetDlgItemText(hDlg, OPT_ChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));
\r
6125 ParseArgs(StringGet, &p);
\r
6127 } else if (IsDlgButtonChecked(hDlg, OPT_View)) {
\r
6128 appData.noChessProgram = TRUE;
\r
6129 appData.icsActive = FALSE;
\r
6131 MessageBox(hDlg, _("Choose an option, or cancel to exit"),
\r
6132 _("Option Error"), MB_OK|MB_ICONEXCLAMATION);
\r
6135 if (IsDlgButtonChecked(hDlg, OPT_AnyAdditional)) {
\r
6136 GetDlgItemText(hDlg, OPT_AdditionalOptions, buf, sizeof(buf));
\r
6138 ParseArgs(StringGet, &p);
\r
6140 EndDialog(hDlg, TRUE);
\r
6147 case IDM_HELPCONTENTS:
\r
6148 if (!WinHelp (hDlg, "winboard.hlp", HELP_KEY,(DWORD)(LPSTR)"CONTENTS")) {
\r
6149 MessageBox (GetFocus(),
\r
6150 _("Unable to activate help"),
\r
6151 szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);
\r
6156 SetStartupDialogEnables(hDlg);
\r
6164 /*---------------------------------------------------------------------------*\
\r
6166 * About box dialog functions
\r
6168 \*---------------------------------------------------------------------------*/
\r
6170 /* Process messages for "About" dialog box */
\r
6172 About(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
6174 switch (message) {
\r
6175 case WM_INITDIALOG: /* message: initialize dialog box */
\r
6176 /* Center the dialog over the application window */
\r
6177 CenterWindow (hDlg, GetWindow (hDlg, GW_OWNER));
\r
6178 SetDlgItemText(hDlg, ABOUTBOX_Version, programVersion);
\r
6179 Translate(hDlg, ABOUTBOX);
\r
6183 case WM_COMMAND: /* message: received a command */
\r
6184 if (LOWORD(wParam) == IDOK /* "OK" box selected? */
\r
6185 || LOWORD(wParam) == IDCANCEL) { /* System menu close command? */
\r
6186 EndDialog(hDlg, TRUE); /* Exit the dialog */
\r
6194 /*---------------------------------------------------------------------------*\
\r
6196 * Comment Dialog functions
\r
6198 \*---------------------------------------------------------------------------*/
\r
6201 CommentDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
6203 static HANDLE hwndText = NULL;
\r
6204 int len, newSizeX, newSizeY, flags;
\r
6205 static int sizeX, sizeY;
\r
6210 switch (message) {
\r
6211 case WM_INITDIALOG: /* message: initialize dialog box */
\r
6212 /* Initialize the dialog items */
\r
6213 Translate(hDlg, DLG_EditComment);
\r
6214 hwndText = GetDlgItem(hDlg, OPT_CommentText);
\r
6215 SetDlgItemText(hDlg, OPT_CommentText, commentText);
\r
6216 EnableWindow(GetDlgItem(hDlg, OPT_CancelComment), editComment);
\r
6217 EnableWindow(GetDlgItem(hDlg, OPT_ClearComment), editComment);
\r
6218 EnableWindow(GetDlgItem(hDlg, OPT_EditComment), !editComment);
\r
6219 SendMessage(hwndText, EM_SETREADONLY, !editComment, 0);
\r
6220 SetWindowText(hDlg, commentTitle);
\r
6221 if (editComment) {
\r
6222 SetFocus(hwndText);
\r
6224 SetFocus(GetDlgItem(hDlg, IDOK));
\r
6226 SendMessage(GetDlgItem(hDlg, OPT_CommentText),
\r
6227 WM_SETFONT, (WPARAM)font[boardSize][COMMENT_FONT]->hf,
\r
6228 MAKELPARAM(FALSE, 0));
\r
6229 /* Size and position the dialog */
\r
6230 if (!commentDialog) {
\r
6231 commentDialog = hDlg;
\r
6232 flags = SWP_NOZORDER;
\r
6233 GetClientRect(hDlg, &rect);
\r
6234 sizeX = rect.right;
\r
6235 sizeY = rect.bottom;
\r
6236 if (wpComment.x != CW_USEDEFAULT && wpComment.y != CW_USEDEFAULT &&
\r
6237 wpComment.width != CW_USEDEFAULT && wpComment.height != CW_USEDEFAULT) {
\r
6238 WINDOWPLACEMENT wp;
\r
6239 EnsureOnScreen(&wpComment.x, &wpComment.y, 0, 0);
\r
6240 wp.length = sizeof(WINDOWPLACEMENT);
\r
6242 wp.showCmd = SW_SHOW;
\r
6243 wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;
\r
6244 wp.rcNormalPosition.left = wpComment.x;
\r
6245 wp.rcNormalPosition.right = wpComment.x + wpComment.width;
\r
6246 wp.rcNormalPosition.top = wpComment.y;
\r
6247 wp.rcNormalPosition.bottom = wpComment.y + wpComment.height;
\r
6248 SetWindowPlacement(hDlg, &wp);
\r
6250 GetClientRect(hDlg, &rect);
\r
6251 newSizeX = rect.right;
\r
6252 newSizeY = rect.bottom;
\r
6253 ResizeEditPlusButtons(hDlg, hwndText, sizeX, sizeY,
\r
6254 newSizeX, newSizeY);
\r
6259 SendDlgItemMessage( hDlg, OPT_CommentText, EM_SETEVENTMASK, 0, ENM_MOUSEEVENTS | ENM_KEYEVENTS );
\r
6262 case WM_COMMAND: /* message: received a command */
\r
6263 switch (LOWORD(wParam)) {
\r
6265 if (editComment) {
\r
6267 /* Read changed options from the dialog box */
\r
6268 hwndText = GetDlgItem(hDlg, OPT_CommentText);
\r
6269 len = GetWindowTextLength(hwndText);
\r
6270 str = (char *) malloc(len + 1);
\r
6271 GetWindowText(hwndText, str, len + 1);
\r
6280 ReplaceComment(commentIndex, str);
\r
6287 case OPT_CancelComment:
\r
6291 case OPT_ClearComment:
\r
6292 SetDlgItemText(hDlg, OPT_CommentText, "");
\r
6295 case OPT_EditComment:
\r
6296 EditCommentEvent();
\r
6304 case WM_NOTIFY: // [HGM] vari: cloned from whistory.c
\r
6305 if( wParam == OPT_CommentText ) {
\r
6306 MSGFILTER * lpMF = (MSGFILTER *) lParam;
\r
6308 if( lpMF->msg == WM_RBUTTONDOWN && (lpMF->wParam & (MK_CONTROL | MK_SHIFT)) == 0 ||
\r
6309 lpMF->msg == WM_CHAR && lpMF->wParam == '\022' ) {
\r
6313 pt.x = LOWORD( lpMF->lParam );
\r
6314 pt.y = HIWORD( lpMF->lParam );
\r
6316 if(lpMF->msg == WM_CHAR) {
\r
6318 SendDlgItemMessage( hDlg, OPT_CommentText, EM_EXGETSEL, 0, (LPARAM) &sel );
\r
6319 index = sel.cpMin;
\r
6321 index = SendDlgItemMessage( hDlg, OPT_CommentText, EM_CHARFROMPOS, 0, (LPARAM) &pt );
\r
6323 hwndText = GetDlgItem(hDlg, OPT_CommentText); // cloned from above
\r
6324 len = GetWindowTextLength(hwndText);
\r
6325 str = (char *) malloc(len + 1);
\r
6326 GetWindowText(hwndText, str, len + 1);
\r
6327 ReplaceComment(commentIndex, str);
\r
6328 if(commentIndex != currentMove) ToNrEvent(commentIndex);
\r
6329 LoadVariation( index, str ); // [HGM] also does the actual moving to it, now
\r
6332 /* Zap the message for good: apparently, returning non-zero is not enough */
\r
6333 lpMF->msg = WM_USER;
\r
6341 newSizeX = LOWORD(lParam);
\r
6342 newSizeY = HIWORD(lParam);
\r
6343 ResizeEditPlusButtons(hDlg, hwndText, sizeX, sizeY, newSizeX, newSizeY);
\r
6348 case WM_GETMINMAXINFO:
\r
6349 /* Prevent resizing window too small */
\r
6350 mmi = (MINMAXINFO *) lParam;
\r
6351 mmi->ptMinTrackSize.x = 100;
\r
6352 mmi->ptMinTrackSize.y = 100;
\r
6359 EitherCommentPopUp(int index, char *title, char *str, BOOLEAN edit)
\r
6364 CheckMenuItem(GetMenu(hwndMain), IDM_Comment, edit ? MF_CHECKED : MF_UNCHECKED);
\r
6366 if (str == NULL) str = "";
\r
6367 p = (char *) malloc(2 * strlen(str) + 2);
\r
6370 if (*str == '\n') *q++ = '\r';
\r
6374 if (commentText != NULL) free(commentText);
\r
6376 commentIndex = index;
\r
6377 commentTitle = title;
\r
6379 editComment = edit;
\r
6381 if (commentDialog) {
\r
6382 SendMessage(commentDialog, WM_INITDIALOG, 0, 0);
\r
6383 if (!commentUp) ShowWindow(commentDialog, SW_SHOW);
\r
6385 lpProc = MakeProcInstance((FARPROC)CommentDialog, hInst);
\r
6386 CreateDialog(hInst, MAKEINTRESOURCE(DLG_EditComment),
\r
6387 hwndMain, (DLGPROC)lpProc);
\r
6388 FreeProcInstance(lpProc);
\r
6394 /*---------------------------------------------------------------------------*\
\r
6396 * Type-in move dialog functions
\r
6398 \*---------------------------------------------------------------------------*/
\r
6401 TypeInMoveDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
6403 char move[MSG_SIZ];
\r
6405 ChessMove moveType;
\r
6406 int fromX, fromY, toX, toY;
\r
6409 switch (message) {
\r
6410 case WM_INITDIALOG:
\r
6411 move[0] = (char) lParam;
\r
6412 move[1] = NULLCHAR;
\r
6413 CenterWindowEx(hDlg, GetWindow(hDlg, GW_OWNER), 1 );
\r
6414 Translate(hDlg, DLG_TypeInMove);
\r
6415 hInput = GetDlgItem(hDlg, OPT_Move);
\r
6416 SetWindowText(hInput, move);
\r
6418 SendMessage(hInput, EM_SETSEL, (WPARAM)9999, (LPARAM)9999);
\r
6422 switch (LOWORD(wParam)) {
\r
6425 shiftKey = GetKeyState(VK_SHIFT) < 0; // [HGM] remember last shift status
\r
6426 GetDlgItemText(hDlg, OPT_Move, move, sizeof(move));
\r
6427 { int n; Board board;
\r
6429 if(gameMode == EditPosition && ParseFEN(board, &n, move) ) {
\r
6430 EditPositionPasteFEN(move);
\r
6431 EndDialog(hDlg, TRUE);
\r
6434 // [HGM] movenum: allow move number to be typed in any mode
\r
6435 if(sscanf(move, "%d", &n) == 1 && n != 0 ) {
\r
6437 EndDialog(hDlg, TRUE);
\r
6441 if (gameMode != EditGame && currentMove != forwardMostMove &&
\r
6442 gameMode != Training) {
\r
6443 DisplayMoveError(_("Displayed move is not current"));
\r
6445 // GetDlgItemText(hDlg, OPT_Move, move, sizeof(move)); // moved upstream
\r
6446 int ok = ParseOneMove(move, gameMode == EditPosition ? blackPlaysFirst : currentMove,
\r
6447 &moveType, &fromX, &fromY, &toX, &toY, &promoChar);
\r
6448 if(!ok && move[0] >= 'a') { move[0] += 'A' - 'a'; ok = 2; } // [HGM] try also capitalized
\r
6449 if (ok==1 || ok && ParseOneMove(move, gameMode == EditPosition ? blackPlaysFirst : currentMove,
\r
6450 &moveType, &fromX, &fromY, &toX, &toY, &promoChar)) {
\r
6451 if (gameMode != Training)
\r
6452 forwardMostMove = currentMove;
\r
6453 UserMoveEvent(fromX, fromY, toX, toY, promoChar);
\r
6455 DisplayMoveError(_("Could not parse move"));
\r
6458 EndDialog(hDlg, TRUE);
\r
6461 EndDialog(hDlg, FALSE);
\r
6472 PopUpMoveDialog(char firstchar)
\r
6476 if ((gameMode == BeginningOfGame && !appData.icsActive) ||
\r
6477 gameMode == MachinePlaysWhite || gameMode == MachinePlaysBlack ||
\r
6478 gameMode == AnalyzeMode || gameMode == EditGame ||
\r
6479 gameMode == EditPosition || gameMode == IcsExamining ||
\r
6480 gameMode == IcsPlayingWhite || gameMode == IcsPlayingBlack ||
\r
6481 isdigit(firstchar) && // [HGM] movenum: allow typing in of move nr in 'passive' modes
\r
6482 ( gameMode == AnalyzeFile || gameMode == PlayFromGameFile ||
\r
6483 gameMode == IcsObserving || gameMode == TwoMachinesPlay ) ||
\r
6484 gameMode == Training) {
\r
6485 lpProc = MakeProcInstance((FARPROC)TypeInMoveDialog, hInst);
\r
6486 DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_TypeInMove),
\r
6487 hwndMain, (DLGPROC)lpProc, (LPARAM)firstchar);
\r
6488 FreeProcInstance(lpProc);
\r
6492 /*---------------------------------------------------------------------------*\
\r
6494 * Type-in name dialog functions
\r
6496 \*---------------------------------------------------------------------------*/
\r
6499 TypeInNameDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
6501 char move[MSG_SIZ];
\r
6504 switch (message) {
\r
6505 case WM_INITDIALOG:
\r
6506 move[0] = (char) lParam;
\r
6507 move[1] = NULLCHAR;
\r
6508 CenterWindowEx(hDlg, GetWindow(hDlg, GW_OWNER), 1 );
\r
6509 Translate(hDlg, DLG_TypeInName);
\r
6510 hInput = GetDlgItem(hDlg, OPT_Name);
\r
6511 SetWindowText(hInput, move);
\r
6513 SendMessage(hInput, EM_SETSEL, (WPARAM)9999, (LPARAM)9999);
\r
6517 switch (LOWORD(wParam)) {
\r
6519 GetDlgItemText(hDlg, OPT_Name, move, sizeof(move));
\r
6520 appData.userName = strdup(move);
\r
6523 if(gameMode == MachinePlaysWhite || gameMode == MachinePlaysBlack) {
\r
6524 snprintf(move, MSG_SIZ, "%s vs. %s", gameInfo.white, gameInfo.black);
\r
6525 DisplayTitle(move);
\r
6529 EndDialog(hDlg, TRUE);
\r
6532 EndDialog(hDlg, FALSE);
\r
6543 PopUpNameDialog(char firstchar)
\r
6547 lpProc = MakeProcInstance((FARPROC)TypeInNameDialog, hInst);
\r
6548 DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_TypeInName),
\r
6549 hwndMain, (DLGPROC)lpProc, (LPARAM)firstchar);
\r
6550 FreeProcInstance(lpProc);
\r
6553 /*---------------------------------------------------------------------------*\
\r
6557 \*---------------------------------------------------------------------------*/
\r
6559 /* Nonmodal error box */
\r
6560 LRESULT CALLBACK ErrorDialog(HWND hDlg, UINT message,
\r
6561 WPARAM wParam, LPARAM lParam);
\r
6564 ErrorPopUp(char *title, char *content)
\r
6568 BOOLEAN modal = hwndMain == NULL;
\r
6586 strncpy(errorTitle, title, sizeof(errorTitle));
\r
6587 errorTitle[sizeof(errorTitle) - 1] = '\0';
\r
6590 MessageBox(NULL, errorMessage, errorTitle, MB_OK|MB_ICONEXCLAMATION);
\r
6592 lpProc = MakeProcInstance((FARPROC)ErrorDialog, hInst);
\r
6593 CreateDialog(hInst, MAKEINTRESOURCE(DLG_Error),
\r
6594 hwndMain, (DLGPROC)lpProc);
\r
6595 FreeProcInstance(lpProc);
\r
6602 if (!appData.popupMoveErrors && moveErrorMessageUp) DisplayMessage("", "");
\r
6603 if (errorDialog == NULL) return;
\r
6604 DestroyWindow(errorDialog);
\r
6605 errorDialog = NULL;
\r
6606 if(errorExitStatus) ExitEvent(errorExitStatus);
\r
6610 ErrorDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
6615 switch (message) {
\r
6616 case WM_INITDIALOG:
\r
6617 GetWindowRect(hDlg, &rChild);
\r
6620 SetWindowPos(hDlg, NULL, rChild.left,
\r
6621 rChild.top + boardRect.top - (rChild.bottom - rChild.top),
\r
6622 0, 0, SWP_NOZORDER|SWP_NOSIZE);
\r
6626 [AS] It seems that the above code wants to move the dialog up in the "caption
\r
6627 area" of the main window, but it uses the dialog height as an hard-coded constant,
\r
6628 and it doesn't work when you resize the dialog.
\r
6629 For now, just give it a default position.
\r
6631 SetWindowPos(hDlg, NULL, boardRect.left+8, boardRect.top+8, 0, 0, SWP_NOZORDER|SWP_NOSIZE);
\r
6632 Translate(hDlg, DLG_Error);
\r
6634 errorDialog = hDlg;
\r
6635 SetWindowText(hDlg, errorTitle);
\r
6636 hwndText = GetDlgItem(hDlg, OPT_ErrorText);
\r
6637 SetDlgItemText(hDlg, OPT_ErrorText, errorMessage);
\r
6641 switch (LOWORD(wParam)) {
\r
6644 if (errorDialog == hDlg) errorDialog = NULL;
\r
6645 DestroyWindow(hDlg);
\r
6657 HWND gothicDialog = NULL;
\r
6660 GothicDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
6664 int height = GetSystemMetrics(SM_CYCAPTION)+GetSystemMetrics(SM_CYFRAME);
\r
6666 switch (message) {
\r
6667 case WM_INITDIALOG:
\r
6668 GetWindowRect(hDlg, &rChild);
\r
6670 SetWindowPos(hDlg, NULL, wpMain.x, wpMain.y-height, wpMain.width, height,
\r
6674 [AS] It seems that the above code wants to move the dialog up in the "caption
\r
6675 area" of the main window, but it uses the dialog height as an hard-coded constant,
\r
6676 and it doesn't work when you resize the dialog.
\r
6677 For now, just give it a default position.
\r
6679 gothicDialog = hDlg;
\r
6680 SetWindowText(hDlg, errorTitle);
\r
6681 hwndText = GetDlgItem(hDlg, OPT_ErrorText);
\r
6682 SetDlgItemText(hDlg, OPT_ErrorText, errorMessage);
\r
6686 switch (LOWORD(wParam)) {
\r
6689 if (errorDialog == hDlg) errorDialog = NULL;
\r
6690 DestroyWindow(hDlg);
\r
6702 GothicPopUp(char *title, VariantClass variant)
\r
6705 static char *lastTitle;
\r
6707 strncpy(errorTitle, title, sizeof(errorTitle));
\r
6708 errorTitle[sizeof(errorTitle) - 1] = '\0';
\r
6710 if(lastTitle != title && gothicDialog != NULL) {
\r
6711 DestroyWindow(gothicDialog);
\r
6712 gothicDialog = NULL;
\r
6714 if(variant != VariantNormal && gothicDialog == NULL) {
\r
6715 title = lastTitle;
\r
6716 lpProc = MakeProcInstance((FARPROC)GothicDialog, hInst);
\r
6717 CreateDialog(hInst, MAKEINTRESOURCE(DLG_Error),
\r
6718 hwndMain, (DLGPROC)lpProc);
\r
6719 FreeProcInstance(lpProc);
\r
6724 /*---------------------------------------------------------------------------*\
\r
6726 * Ics Interaction console functions
\r
6728 \*---------------------------------------------------------------------------*/
\r
6730 #define HISTORY_SIZE 64
\r
6731 static char *history[HISTORY_SIZE];
\r
6732 int histIn = 0, histP = 0;
\r
6735 SaveInHistory(char *cmd)
\r
6737 if (history[histIn] != NULL) {
\r
6738 free(history[histIn]);
\r
6739 history[histIn] = NULL;
\r
6741 if (*cmd == NULLCHAR) return;
\r
6742 history[histIn] = StrSave(cmd);
\r
6743 histIn = (histIn + 1) % HISTORY_SIZE;
\r
6744 if (history[histIn] != NULL) {
\r
6745 free(history[histIn]);
\r
6746 history[histIn] = NULL;
\r
6752 PrevInHistory(char *cmd)
\r
6755 if (histP == histIn) {
\r
6756 if (history[histIn] != NULL) free(history[histIn]);
\r
6757 history[histIn] = StrSave(cmd);
\r
6759 newhp = (histP - 1 + HISTORY_SIZE) % HISTORY_SIZE;
\r
6760 if (newhp == histIn || history[newhp] == NULL) return NULL;
\r
6762 return history[histP];
\r
6768 if (histP == histIn) return NULL;
\r
6769 histP = (histP + 1) % HISTORY_SIZE;
\r
6770 return history[histP];
\r
6774 LoadIcsTextMenu(IcsTextMenuEntry *e)
\r
6778 hmenu = LoadMenu(hInst, "TextMenu");
\r
6779 h = GetSubMenu(hmenu, 0);
\r
6781 if (strcmp(e->item, "-") == 0) {
\r
6782 AppendMenu(h, MF_SEPARATOR, 0, 0);
\r
6783 } else { // [HGM] re-written a bit to use only one AppendMenu call for both cases (| or no |)
\r
6784 int flags = MF_STRING, j = 0;
\r
6785 if (e->item[0] == '|') {
\r
6786 flags |= MF_MENUBARBREAK;
\r
6789 if(!strcmp(e->command, "none")) flags |= MF_GRAYED; // [HGM] chatclick: provide inactive dummy
\r
6790 AppendMenu(h, flags, IDM_CommandX + i, e->item + j);
\r
6798 WNDPROC consoleTextWindowProc;
\r
6801 CommandX(HWND hwnd, char *command, BOOLEAN getname, BOOLEAN immediate)
\r
6803 char buf[MSG_SIZ], name[MSG_SIZ];
\r
6804 HWND hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
6808 SetWindowText(hInput, command);
\r
6810 SendMessage(hInput, WM_CHAR, '\r', 0);
\r
6812 sel.cpMin = 999999;
\r
6813 sel.cpMax = 999999;
\r
6814 SendMessage(hInput, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
6819 SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);
\r
6820 if (sel.cpMin == sel.cpMax) {
\r
6821 /* Expand to surrounding word */
\r
6824 tr.chrg.cpMax = sel.cpMin;
\r
6825 tr.chrg.cpMin = --sel.cpMin;
\r
6826 if (sel.cpMin < 0) break;
\r
6827 tr.lpstrText = name;
\r
6828 SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr);
\r
6829 } while (isalpha(name[0]) || isdigit(name[0]) || name[0] == '-');
\r
6833 tr.chrg.cpMin = sel.cpMax;
\r
6834 tr.chrg.cpMax = ++sel.cpMax;
\r
6835 tr.lpstrText = name;
\r
6836 if (SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr) < 1) break;
\r
6837 } while (isalpha(name[0]) || isdigit(name[0]) || name[0] == '-');
\r
6840 if (sel.cpMax == sel.cpMin || sel.cpMax - sel.cpMin > MSG_SIZ/2) {
\r
6841 MessageBeep(MB_ICONEXCLAMATION);
\r
6845 tr.lpstrText = name;
\r
6846 SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr);
\r
6848 if (sel.cpMax - sel.cpMin > MSG_SIZ/2) {
\r
6849 MessageBeep(MB_ICONEXCLAMATION);
\r
6852 SendMessage(hwnd, EM_GETSELTEXT, 0, (LPARAM) name);
\r
6855 if(strstr(command, "%s")) snprintf(buf, MSG_SIZ, command, name); else
\r
6856 snprintf(buf, MSG_SIZ, "%s %s", command, name);
\r
6857 SetWindowText(hInput, buf);
\r
6858 SendMessage(hInput, WM_CHAR, '\r', 0);
\r
6860 if(!strcmp(command, "chat")) { ChatPopUp(name); return; }
\r
6861 snprintf(buf, MSG_SIZ, "%s %s ", command, name); /* trailing space */
\r
6862 SetWindowText(hInput, buf);
\r
6863 sel.cpMin = 999999;
\r
6864 sel.cpMax = 999999;
\r
6865 SendMessage(hInput, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
6871 ConsoleTextSubclass(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
\r
6876 switch (message) {
\r
6878 if (!(GetKeyState(VK_CONTROL) & ~1)) break;
\r
6879 if(wParam=='R') return 0;
\r
6882 SendMessage(hwnd, EM_LINESCROLL, 0, -999999);
\r
6885 sel.cpMin = 999999;
\r
6886 sel.cpMax = 999999;
\r
6887 SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
6888 SendMessage(hwnd, EM_SCROLLCARET, 0, 0);
\r
6893 if(wParam != '\022') {
\r
6894 if (wParam == '\t') {
\r
6895 if (GetKeyState(VK_SHIFT) < 0) {
\r
6897 if (IsIconic(hwndMain)) ShowWindow(hwndMain, SW_RESTORE);
\r
6898 if (buttonDesc[0].hwnd) {
\r
6899 SetFocus(buttonDesc[0].hwnd);
\r
6901 SetFocus(hwndMain);
\r
6905 SetFocus(GetDlgItem(hwndConsole, OPT_ConsoleInput));
\r
6908 hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
6909 JAWS_DELETE( SetFocus(hInput); )
\r
6910 SendMessage(hInput, message, wParam, lParam);
\r
6913 } // [HGM] navigate: for Ctrl+R, flow into next case (moved up here) to summon up menu
\r
6915 case WM_RBUTTONDOWN:
\r
6916 if (!(GetKeyState(VK_SHIFT) & ~1)) {
\r
6917 /* Move selection here if it was empty */
\r
6919 pt.x = LOWORD(lParam);
\r
6920 pt.y = HIWORD(lParam);
\r
6921 SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);
\r
6922 if (sel.cpMin == sel.cpMax) {
\r
6923 if(lParam != -1) sel.cpMin = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&pt); /*doc is wrong*/
\r
6924 sel.cpMax = sel.cpMin;
\r
6925 SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
6927 SendMessage(hwnd, EM_HIDESELECTION, FALSE, FALSE);
\r
6928 { // [HGM] chatclick: code moved here from WM_RBUTTONUP case, to have menu appear on down-click
\r
6930 HMENU hmenu = LoadIcsTextMenu(icsTextMenuEntry);
\r
6931 SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);
\r
6932 if (sel.cpMin == sel.cpMax) {
\r
6933 EnableMenuItem(hmenu, IDM_Copy, MF_BYCOMMAND|MF_GRAYED);
\r
6934 EnableMenuItem(hmenu, IDM_QuickPaste, MF_BYCOMMAND|MF_GRAYED);
\r
6936 if (!IsClipboardFormatAvailable(CF_TEXT)) {
\r
6937 EnableMenuItem(hmenu, IDM_Paste, MF_BYCOMMAND|MF_GRAYED);
\r
6939 pt.x = LOWORD(lParam)-30; // [HGM] chatclick: make menu pop up with pointer above upper-right item
\r
6940 pt.y = HIWORD(lParam)-10; // make it appear as if mouse moved there, so it will be selected on up-click
\r
6941 PostMessage(hwnd, WM_MOUSEMOVE, wParam, lParam+5);
\r
6942 MenuPopup(hwnd, pt, hmenu, -1);
\r
6946 case WM_RBUTTONUP:
\r
6947 if (GetKeyState(VK_SHIFT) & ~1) {
\r
6948 SendDlgItemMessage(hwndConsole, OPT_ConsoleText,
\r
6949 WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);
\r
6953 hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
6955 return SendMessage(hInput, message, wParam, lParam);
\r
6956 case WM_MBUTTONDOWN:
\r
6957 return SendMessage(hwnd, WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);
\r
6959 switch (LOWORD(wParam)) {
\r
6960 case IDM_QuickPaste:
\r
6962 SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);
\r
6963 if (sel.cpMin == sel.cpMax) {
\r
6964 MessageBeep(MB_ICONEXCLAMATION);
\r
6967 SendMessage(hwnd, WM_COPY, 0, 0);
\r
6968 hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
6969 SendMessage(hInput, WM_PASTE, 0, 0);
\r
6974 SendMessage(hwnd, WM_CUT, 0, 0);
\r
6977 SendMessage(hwnd, WM_PASTE, 0, 0);
\r
6980 SendMessage(hwnd, WM_COPY, 0, 0);
\r
6984 int i = LOWORD(wParam) - IDM_CommandX;
\r
6985 if (i >= 0 && i < ICS_TEXT_MENU_SIZE &&
\r
6986 icsTextMenuEntry[i].command != NULL) {
\r
6987 CommandX(hwnd, icsTextMenuEntry[i].command,
\r
6988 icsTextMenuEntry[i].getname,
\r
6989 icsTextMenuEntry[i].immediate);
\r
6997 return (*consoleTextWindowProc)(hwnd, message, wParam, lParam);
\r
7000 WNDPROC consoleInputWindowProc;
\r
7003 ConsoleInputSubclass(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
\r
7005 char buf[MSG_SIZ];
\r
7007 static BOOL sendNextChar = FALSE;
\r
7008 static BOOL quoteNextChar = FALSE;
\r
7009 InputSource *is = consoleInputSource;
\r
7013 switch (message) {
\r
7015 if (!appData.localLineEditing || sendNextChar) {
\r
7016 is->buf[0] = (CHAR) wParam;
\r
7018 SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);
\r
7019 sendNextChar = FALSE;
\r
7022 if (quoteNextChar) {
\r
7023 buf[0] = (char) wParam;
\r
7024 buf[1] = NULLCHAR;
\r
7025 SendMessage(hwnd, EM_REPLACESEL, TRUE, (LPARAM) buf);
\r
7026 quoteNextChar = FALSE;
\r
7030 case '\r': /* Enter key */
\r
7031 is->count = GetWindowText(hwnd, is->buf, INPUT_SOURCE_BUF_SIZE-1);
\r
7032 if (consoleEcho) SaveInHistory(is->buf);
\r
7033 is->buf[is->count++] = '\n';
\r
7034 is->buf[is->count] = NULLCHAR;
\r
7035 SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);
\r
7036 if (consoleEcho) {
\r
7037 ConsoleOutput(is->buf, is->count, TRUE);
\r
7038 } else if (appData.localLineEditing) {
\r
7039 ConsoleOutput("\n", 1, TRUE);
\r
7042 case '\033': /* Escape key */
\r
7043 SetWindowText(hwnd, "");
\r
7044 cf.cbSize = sizeof(CHARFORMAT);
\r
7045 cf.dwMask = CFM_COLOR|CFM_BOLD|CFM_ITALIC|CFM_UNDERLINE|CFM_STRIKEOUT;
\r
7046 if (consoleEcho) {
\r
7047 cf.crTextColor = textAttribs[ColorNormal].color;
\r
7049 cf.crTextColor = COLOR_ECHOOFF;
\r
7051 cf.dwEffects = textAttribs[ColorNormal].effects;
\r
7052 SendMessage(hwnd, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf);
\r
7054 case '\t': /* Tab key */
\r
7055 if (GetKeyState(VK_SHIFT) < 0) {
\r
7057 SetFocus(GetDlgItem(hwndConsole, OPT_ConsoleText));
\r
7060 if (IsIconic(hwndMain)) ShowWindow(hwndMain, SW_RESTORE);
\r
7061 if (buttonDesc[0].hwnd) {
\r
7062 SetFocus(buttonDesc[0].hwnd);
\r
7064 SetFocus(hwndMain);
\r
7068 case '\023': /* Ctrl+S */
\r
7069 sendNextChar = TRUE;
\r
7071 case '\021': /* Ctrl+Q */
\r
7072 quoteNextChar = TRUE;
\r
7082 GetWindowText(hwnd, buf, MSG_SIZ);
\r
7083 p = PrevInHistory(buf);
\r
7085 SetWindowText(hwnd, p);
\r
7086 sel.cpMin = 999999;
\r
7087 sel.cpMax = 999999;
\r
7088 SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
7093 p = NextInHistory();
\r
7095 SetWindowText(hwnd, p);
\r
7096 sel.cpMin = 999999;
\r
7097 sel.cpMax = 999999;
\r
7098 SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
7104 if (!(GetKeyState(VK_CONTROL) & ~1)) break;
\r
7108 SendDlgItemMessage(hwndConsole, OPT_ConsoleText, message, wParam, lParam);
\r
7112 case WM_MBUTTONDOWN:
\r
7113 SendDlgItemMessage(hwndConsole, OPT_ConsoleText,
\r
7114 WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);
\r
7116 case WM_RBUTTONUP:
\r
7117 if (GetKeyState(VK_SHIFT) & ~1) {
\r
7118 SendDlgItemMessage(hwndConsole, OPT_ConsoleText,
\r
7119 WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);
\r
7123 hmenu = LoadMenu(hInst, "InputMenu");
\r
7124 SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);
\r
7125 if (sel.cpMin == sel.cpMax) {
\r
7126 EnableMenuItem(hmenu, IDM_Copy, MF_BYCOMMAND|MF_GRAYED);
\r
7127 EnableMenuItem(hmenu, IDM_Cut, MF_BYCOMMAND|MF_GRAYED);
\r
7129 if (!IsClipboardFormatAvailable(CF_TEXT)) {
\r
7130 EnableMenuItem(hmenu, IDM_Paste, MF_BYCOMMAND|MF_GRAYED);
\r
7132 pt.x = LOWORD(lParam);
\r
7133 pt.y = HIWORD(lParam);
\r
7134 MenuPopup(hwnd, pt, hmenu, -1);
\r
7138 switch (LOWORD(wParam)) {
\r
7140 SendMessage(hwnd, EM_UNDO, 0, 0);
\r
7142 case IDM_SelectAll:
\r
7144 sel.cpMax = -1; /*999999?*/
\r
7145 SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
7148 SendMessage(hwnd, WM_CUT, 0, 0);
\r
7151 SendMessage(hwnd, WM_PASTE, 0, 0);
\r
7154 SendMessage(hwnd, WM_COPY, 0, 0);
\r
7159 return (*consoleInputWindowProc)(hwnd, message, wParam, lParam);
\r
7162 #define CO_MAX 100000
\r
7163 #define CO_TRIM 1000
\r
7166 ConsoleWndProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
7168 static SnapData sd;
\r
7169 HWND hText, hInput;
\r
7171 static int sizeX, sizeY;
\r
7172 int newSizeX, newSizeY;
\r
7176 hText = GetDlgItem(hDlg, OPT_ConsoleText);
\r
7177 hInput = GetDlgItem(hDlg, OPT_ConsoleInput);
\r
7179 switch (message) {
\r
7181 if (((NMHDR*)lParam)->code == EN_LINK)
\r
7183 ENLINK *pLink = (ENLINK*)lParam;
\r
7184 if (pLink->msg == WM_LBUTTONUP)
\r
7188 tr.chrg = pLink->chrg;
\r
7189 tr.lpstrText = malloc(1+tr.chrg.cpMax-tr.chrg.cpMin);
\r
7190 SendMessage(hText, EM_GETTEXTRANGE, 0, (LPARAM)&tr);
\r
7191 ShellExecute(NULL, "open", tr.lpstrText, NULL, NULL, SW_SHOW);
\r
7192 free(tr.lpstrText);
\r
7196 case WM_INITDIALOG: /* message: initialize dialog box */
\r
7197 hwndConsole = hDlg;
\r
7199 consoleTextWindowProc = (WNDPROC)
\r
7200 SetWindowLong(hText, GWL_WNDPROC, (LONG) ConsoleTextSubclass);
\r
7201 SendMessage(hText, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);
\r
7202 consoleInputWindowProc = (WNDPROC)
\r
7203 SetWindowLong(hInput, GWL_WNDPROC, (LONG) ConsoleInputSubclass);
\r
7204 SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);
\r
7205 Colorize(ColorNormal, TRUE);
\r
7206 SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &consoleCF);
\r
7207 ChangedConsoleFont();
\r
7208 GetClientRect(hDlg, &rect);
\r
7209 sizeX = rect.right;
\r
7210 sizeY = rect.bottom;
\r
7211 if (wpConsole.x != CW_USEDEFAULT && wpConsole.y != CW_USEDEFAULT &&
\r
7212 wpConsole.width != CW_USEDEFAULT && wpConsole.height != CW_USEDEFAULT) {
\r
7213 WINDOWPLACEMENT wp;
\r
7214 EnsureOnScreen(&wpConsole.x, &wpConsole.y, 0, 0);
\r
7215 wp.length = sizeof(WINDOWPLACEMENT);
\r
7217 wp.showCmd = SW_SHOW;
\r
7218 wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;
\r
7219 wp.rcNormalPosition.left = wpConsole.x;
\r
7220 wp.rcNormalPosition.right = wpConsole.x + wpConsole.width;
\r
7221 wp.rcNormalPosition.top = wpConsole.y;
\r
7222 wp.rcNormalPosition.bottom = wpConsole.y + wpConsole.height;
\r
7223 SetWindowPlacement(hDlg, &wp);
\r
7226 // [HGM] Chessknight's change 2004-07-13
\r
7227 else { /* Determine Defaults */
\r
7228 WINDOWPLACEMENT wp;
\r
7229 wpConsole.x = wpMain.width + 1;
\r
7230 wpConsole.y = wpMain.y;
\r
7231 wpConsole.width = screenWidth - wpMain.width;
\r
7232 wpConsole.height = wpMain.height;
\r
7233 EnsureOnScreen(&wpConsole.x, &wpConsole.y, 0, 0);
\r
7234 wp.length = sizeof(WINDOWPLACEMENT);
\r
7236 wp.showCmd = SW_SHOW;
\r
7237 wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;
\r
7238 wp.rcNormalPosition.left = wpConsole.x;
\r
7239 wp.rcNormalPosition.right = wpConsole.x + wpConsole.width;
\r
7240 wp.rcNormalPosition.top = wpConsole.y;
\r
7241 wp.rcNormalPosition.bottom = wpConsole.y + wpConsole.height;
\r
7242 SetWindowPlacement(hDlg, &wp);
\r
7245 // Allow hText to highlight URLs and send notifications on them
\r
7246 wMask = (WORD) SendMessage(hText, EM_GETEVENTMASK, 0, 0L);
\r
7247 SendMessage(hText, EM_SETEVENTMASK, 0, wMask | ENM_LINK);
\r
7248 SendMessage(hText, EM_AUTOURLDETECT, TRUE, 0L);
\r
7249 SetWindowLong(hText, GWL_USERDATA, 79); // initialize the text window's width
\r
7263 if (IsIconic(hDlg)) break;
\r
7264 newSizeX = LOWORD(lParam);
\r
7265 newSizeY = HIWORD(lParam);
\r
7266 if (sizeX != newSizeX || sizeY != newSizeY) {
\r
7267 RECT rectText, rectInput;
\r
7269 int newTextHeight, newTextWidth;
\r
7270 GetWindowRect(hText, &rectText);
\r
7271 newTextWidth = rectText.right - rectText.left + newSizeX - sizeX;
\r
7272 newTextHeight = rectText.bottom - rectText.top + newSizeY - sizeY;
\r
7273 if (newTextHeight < 0) {
\r
7274 newSizeY += -newTextHeight;
\r
7275 newTextHeight = 0;
\r
7277 SetWindowPos(hText, NULL, 0, 0,
\r
7278 newTextWidth, newTextHeight, SWP_NOZORDER|SWP_NOMOVE);
\r
7279 GetWindowRect(hInput, &rectInput); /* gives screen coords */
\r
7280 pt.x = rectInput.left;
\r
7281 pt.y = rectInput.top + newSizeY - sizeY;
\r
7282 ScreenToClient(hDlg, &pt);
\r
7283 SetWindowPos(hInput, NULL,
\r
7284 pt.x, pt.y, /* needs client coords */
\r
7285 rectInput.right - rectInput.left + newSizeX - sizeX,
\r
7286 rectInput.bottom - rectInput.top, SWP_NOZORDER);
\r
7292 case WM_GETMINMAXINFO:
\r
7293 /* Prevent resizing window too small */
\r
7294 mmi = (MINMAXINFO *) lParam;
\r
7295 mmi->ptMinTrackSize.x = 100;
\r
7296 mmi->ptMinTrackSize.y = 100;
\r
7299 /* [AS] Snapping */
\r
7300 case WM_ENTERSIZEMOVE:
\r
7301 return OnEnterSizeMove( &sd, hDlg, wParam, lParam );
\r
7304 return OnSizing( &sd, hDlg, wParam, lParam );
\r
7307 return OnMoving( &sd, hDlg, wParam, lParam );
\r
7309 case WM_EXITSIZEMOVE:
\r
7310 UpdateICSWidth(hText);
\r
7311 return OnExitSizeMove( &sd, hDlg, wParam, lParam );
\r
7314 return DefWindowProc(hDlg, message, wParam, lParam);
\r
7322 if (hwndConsole) return;
\r
7323 hCons = CreateDialog(hInst, szConsoleName, 0, NULL);
\r
7324 SendMessage(hCons, WM_INITDIALOG, 0, 0);
\r
7329 ConsoleOutput(char* data, int length, int forceVisible)
\r
7334 char buf[CO_MAX+1];
\r
7337 static int delayLF = 0;
\r
7338 CHARRANGE savesel, sel;
\r
7340 if (hwndConsole == NULL || length > CO_MAX-100 || length == 0) return;
\r
7348 while (length--) {
\r
7356 } else if (*p == '\007') {
\r
7357 MyPlaySound(&sounds[(int)SoundBell]);
\r
7364 hText = GetDlgItem(hwndConsole, OPT_ConsoleText);
\r
7365 SendMessage(hText, EM_HIDESELECTION, TRUE, FALSE);
\r
7366 /* Save current selection */
\r
7367 SendMessage(hText, EM_EXGETSEL, 0, (LPARAM)&savesel);
\r
7368 exlen = GetWindowTextLength(hText);
\r
7369 /* Find out whether current end of text is visible */
\r
7370 SendMessage(hText, EM_GETRECT, 0, (LPARAM) &rect);
\r
7371 SendMessage(hText, EM_POSFROMCHAR, (WPARAM) &pEnd, exlen);
\r
7372 /* Trim existing text if it's too long */
\r
7373 if (exlen + (q - buf) > CO_MAX) {
\r
7374 trim = (CO_TRIM > (q - buf)) ? CO_TRIM : (q - buf);
\r
7377 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
7378 SendMessage(hText, EM_REPLACESEL, 0, (LPARAM)"");
\r
7380 savesel.cpMin -= trim;
\r
7381 savesel.cpMax -= trim;
\r
7382 if (exlen < 0) exlen = 0;
\r
7383 if (savesel.cpMin < 0) savesel.cpMin = 0;
\r
7384 if (savesel.cpMax < savesel.cpMin) savesel.cpMax = savesel.cpMin;
\r
7386 /* Append the new text */
\r
7387 sel.cpMin = exlen;
\r
7388 sel.cpMax = exlen;
\r
7389 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
7390 SendMessage(hText, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&consoleCF);
\r
7391 SendMessage(hText, EM_REPLACESEL, 0, (LPARAM) buf);
\r
7392 if (forceVisible || exlen == 0 ||
\r
7393 (rect.left <= pEnd.x && pEnd.x < rect.right &&
\r
7394 rect.top <= pEnd.y && pEnd.y < rect.bottom)) {
\r
7395 /* Scroll to make new end of text visible if old end of text
\r
7396 was visible or new text is an echo of user typein */
\r
7397 sel.cpMin = 9999999;
\r
7398 sel.cpMax = 9999999;
\r
7399 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
7400 SendMessage(hText, EM_HIDESELECTION, FALSE, FALSE);
\r
7401 SendMessage(hText, EM_SCROLLCARET, 0, 0);
\r
7402 SendMessage(hText, EM_HIDESELECTION, TRUE, FALSE);
\r
7404 if (savesel.cpMax == exlen || forceVisible) {
\r
7405 /* Move insert point to new end of text if it was at the old
\r
7406 end of text or if the new text is an echo of user typein */
\r
7407 sel.cpMin = 9999999;
\r
7408 sel.cpMax = 9999999;
\r
7409 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
7411 /* Restore previous selection */
\r
7412 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&savesel);
\r
7414 SendMessage(hText, EM_HIDESELECTION, FALSE, FALSE);
\r
7421 DisplayHoldingsCount(HDC hdc, int x, int y, int rightAlign, int copyNumber)
\r
7425 COLORREF oldFg, oldBg;
\r
7430 snprintf(buf, sizeof(buf)/sizeof(buf[0]),"%d", copyNumber); else buf[0] = 0;
\r
7432 oldFg = SetTextColor(hdc, RGB(255, 255, 255)); /* white */
\r
7433 oldBg = SetBkColor(hdc, RGB(0, 0, 0)); /* black */
\r
7434 oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);
\r
7437 rect.right = x + squareSize;
\r
7439 rect.bottom = y + squareSize;
\r
7442 ExtTextOut(hdc, x + MESSAGE_LINE_LEFTMARGIN
\r
7443 + (rightAlign ? (squareSize*2)/3 : 0),
\r
7444 y, ETO_CLIPPED|ETO_OPAQUE,
\r
7445 &rect, str, strlen(str), NULL);
\r
7447 (void) SetTextColor(hdc, oldFg);
\r
7448 (void) SetBkColor(hdc, oldBg);
\r
7449 (void) SelectObject(hdc, oldFont);
\r
7453 DisplayAClock(HDC hdc, int timeRemaining, int highlight,
\r
7454 RECT *rect, char *color, char *flagFell)
\r
7458 COLORREF oldFg, oldBg;
\r
7461 if (appData.clockMode) {
\r
7463 snprintf(buf, sizeof(buf)/sizeof(buf[0]), "%c %s %s", color[0], TimeString(timeRemaining), flagFell);
\r
7465 snprintf(buf, sizeof(buf)/sizeof(buf[0]), "%s:%c%s %s", color, (logoHeight>0 ? 0 : ' '), TimeString(timeRemaining), flagFell);
\r
7472 oldFg = SetTextColor(hdc, RGB(255, 255, 255)); /* white */
\r
7473 oldBg = SetBkColor(hdc, RGB(0, 0, 0)); /* black */
\r
7475 oldFg = SetTextColor(hdc, RGB(0, 0, 0)); /* black */
\r
7476 oldBg = SetBkColor(hdc, RGB(255, 255, 255)); /* white */
\r
7478 oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);
\r
7482 ExtTextOut(hdc, rect->left + MESSAGE_LINE_LEFTMARGIN,
\r
7483 rect->top, ETO_CLIPPED|ETO_OPAQUE,
\r
7484 rect, str, strlen(str), NULL);
\r
7485 if(logoHeight > 0 && appData.clockMode) {
\r
7487 str += strlen(color)+2;
\r
7488 r.top = rect->top + logoHeight/2;
\r
7489 r.left = rect->left;
\r
7490 r.right = rect->right;
\r
7491 r.bottom = rect->bottom;
\r
7492 ExtTextOut(hdc, rect->left + MESSAGE_LINE_LEFTMARGIN,
\r
7493 r.top, ETO_CLIPPED|ETO_OPAQUE,
\r
7494 &r, str, strlen(str), NULL);
\r
7496 (void) SetTextColor(hdc, oldFg);
\r
7497 (void) SetBkColor(hdc, oldBg);
\r
7498 (void) SelectObject(hdc, oldFont);
\r
7503 DoReadFile(HANDLE hFile, char *buf, int count, DWORD *outCount,
\r
7509 if( count <= 0 ) {
\r
7510 if (appData.debugMode) {
\r
7511 fprintf( debugFP, "DoReadFile: trying to read past end of buffer, overflow = %d\n", count );
\r
7514 return ERROR_INVALID_USER_BUFFER;
\r
7517 ResetEvent(ovl->hEvent);
\r
7518 ovl->Offset = ovl->OffsetHigh = 0;
\r
7519 ok = ReadFile(hFile, buf, count, outCount, ovl);
\r
7523 err = GetLastError();
\r
7524 if (err == ERROR_IO_PENDING) {
\r
7525 ok = GetOverlappedResult(hFile, ovl, outCount, TRUE);
\r
7529 err = GetLastError();
\r
7536 DoWriteFile(HANDLE hFile, char *buf, int count, DWORD *outCount,
\r
7541 ResetEvent(ovl->hEvent);
\r
7542 ovl->Offset = ovl->OffsetHigh = 0;
\r
7543 ok = WriteFile(hFile, buf, count, outCount, ovl);
\r
7547 err = GetLastError();
\r
7548 if (err == ERROR_IO_PENDING) {
\r
7549 ok = GetOverlappedResult(hFile, ovl, outCount, TRUE);
\r
7553 err = GetLastError();
\r
7559 /* [AS] If input is line by line and a line exceed the buffer size, force an error */
\r
7560 void CheckForInputBufferFull( InputSource * is )
\r
7562 if( is->lineByLine && (is->next - is->buf) >= INPUT_SOURCE_BUF_SIZE ) {
\r
7563 /* Look for end of line */
\r
7564 char * p = is->buf;
\r
7566 while( p < is->next && *p != '\n' ) {
\r
7570 if( p >= is->next ) {
\r
7571 if (appData.debugMode) {
\r
7572 fprintf( debugFP, "Input line exceeded buffer size (source id=%lu)\n", is->id );
\r
7575 is->error = ERROR_BROKEN_PIPE; /* [AS] Just any non-successful code! */
\r
7576 is->count = (DWORD) -1;
\r
7577 is->next = is->buf;
\r
7583 InputThread(LPVOID arg)
\r
7588 is = (InputSource *) arg;
\r
7589 ovl.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
\r
7590 ovl.Internal = ovl.InternalHigh = ovl.Offset = ovl.OffsetHigh = 0;
\r
7591 while (is->hThread != NULL) {
\r
7592 is->error = DoReadFile(is->hFile, is->next,
\r
7593 INPUT_SOURCE_BUF_SIZE - (is->next - is->buf),
\r
7594 &is->count, &ovl);
\r
7595 if (is->error == NO_ERROR) {
\r
7596 is->next += is->count;
\r
7598 if (is->error == ERROR_BROKEN_PIPE) {
\r
7599 /* Correct for MS brain damage. EOF reading a pipe is not an error. */
\r
7602 is->count = (DWORD) -1;
\r
7603 /* [AS] The (is->count <= 0) check below is not useful for unsigned values! */
\r
7608 CheckForInputBufferFull( is );
\r
7610 SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);
\r
7612 if( is->count == ((DWORD) -1) ) break; /* [AS] */
\r
7614 if (is->count <= 0) break; /* Quit on EOF or error */
\r
7617 CloseHandle(ovl.hEvent);
\r
7618 CloseHandle(is->hFile);
\r
7620 if (appData.debugMode) {
\r
7621 fprintf( debugFP, "Input thread terminated (id=%lu, error=%d, count=%ld)\n", is->id, is->error, is->count );
\r
7628 /* Windows 95 beta 2 won't let you do overlapped i/o on a console or pipe */
\r
7630 NonOvlInputThread(LPVOID arg)
\r
7637 is = (InputSource *) arg;
\r
7638 while (is->hThread != NULL) {
\r
7639 is->error = ReadFile(is->hFile, is->next,
\r
7640 INPUT_SOURCE_BUF_SIZE - (is->next - is->buf),
\r
7641 &is->count, NULL) ? NO_ERROR : GetLastError();
\r
7642 if (is->error == NO_ERROR) {
\r
7643 /* Change CRLF to LF */
\r
7644 if (is->next > is->buf) {
\r
7646 i = is->count + 1;
\r
7654 if (prev == '\r' && *p == '\n') {
\r
7666 if (is->error == ERROR_BROKEN_PIPE) {
\r
7667 /* Correct for MS brain damage. EOF reading a pipe is not an error. */
\r
7670 is->count = (DWORD) -1;
\r
7674 CheckForInputBufferFull( is );
\r
7676 SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);
\r
7678 if( is->count == ((DWORD) -1) ) break; /* [AS] */
\r
7680 if (is->count < 0) break; /* Quit on error */
\r
7682 CloseHandle(is->hFile);
\r
7687 SocketInputThread(LPVOID arg)
\r
7691 is = (InputSource *) arg;
\r
7692 while (is->hThread != NULL) {
\r
7693 is->count = recv(is->sock, is->buf, INPUT_SOURCE_BUF_SIZE, 0);
\r
7694 if ((int)is->count == SOCKET_ERROR) {
\r
7695 is->count = (DWORD) -1;
\r
7696 is->error = WSAGetLastError();
\r
7698 is->error = NO_ERROR;
\r
7699 is->next += is->count;
\r
7700 if (is->count == 0 && is->second == is) {
\r
7701 /* End of file on stderr; quit with no message */
\r
7705 SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);
\r
7707 if( is->count == ((DWORD) -1) ) break; /* [AS] */
\r
7709 if (is->count <= 0) break; /* Quit on EOF or error */
\r
7715 InputEvent(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
\r
7719 is = (InputSource *) lParam;
\r
7720 if (is->lineByLine) {
\r
7721 /* Feed in lines one by one */
\r
7722 char *p = is->buf;
\r
7724 while (q < is->next) {
\r
7725 if (*q++ == '\n') {
\r
7726 (is->func)(is, is->closure, p, q - p, NO_ERROR);
\r
7731 /* Move any partial line to the start of the buffer */
\r
7733 while (p < is->next) {
\r
7738 if (is->error != NO_ERROR || is->count == 0) {
\r
7739 /* Notify backend of the error. Note: If there was a partial
\r
7740 line at the end, it is not flushed through. */
\r
7741 (is->func)(is, is->closure, is->buf, is->count, is->error);
\r
7744 /* Feed in the whole chunk of input at once */
\r
7745 (is->func)(is, is->closure, is->buf, is->count, is->error);
\r
7746 is->next = is->buf;
\r
7750 /*---------------------------------------------------------------------------*\
\r
7752 * Menu enables. Used when setting various modes.
\r
7754 \*---------------------------------------------------------------------------*/
\r
7762 GreyRevert(Boolean grey)
\r
7763 { // [HGM] vari: for retracting variations in local mode
\r
7764 HMENU hmenu = GetMenu(hwndMain);
\r
7765 EnableMenuItem(hmenu, IDM_Revert, MF_BYCOMMAND|(grey ? MF_GRAYED : MF_ENABLED));
\r
7766 EnableMenuItem(hmenu, IDM_Annotate, MF_BYCOMMAND|(grey ? MF_GRAYED : MF_ENABLED));
\r
7770 SetMenuEnables(HMENU hmenu, Enables *enab)
\r
7772 while (enab->item > 0) {
\r
7773 (void) EnableMenuItem(hmenu, enab->item, enab->flags);
\r
7778 Enables gnuEnables[] = {
\r
7779 { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },
\r
7780 { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },
\r
7781 { IDM_IcsClient, MF_BYCOMMAND|MF_GRAYED },
\r
7782 { IDM_Accept, MF_BYCOMMAND|MF_GRAYED },
\r
7783 { IDM_Decline, MF_BYCOMMAND|MF_GRAYED },
\r
7784 { IDM_Rematch, MF_BYCOMMAND|MF_GRAYED },
\r
7785 { IDM_Adjourn, MF_BYCOMMAND|MF_GRAYED },
\r
7786 { IDM_StopExamining, MF_BYCOMMAND|MF_GRAYED },
\r
7787 { IDM_StopObserving, MF_BYCOMMAND|MF_GRAYED },
\r
7788 { IDM_Upload, MF_BYCOMMAND|MF_GRAYED },
\r
7789 { IDM_Revert, MF_BYCOMMAND|MF_GRAYED },
\r
7790 { IDM_Annotate, MF_BYCOMMAND|MF_GRAYED },
\r
7791 { IDM_NewChat, MF_BYCOMMAND|MF_GRAYED },
\r
7795 Enables icsEnables[] = {
\r
7796 { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },
\r
7797 { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },
\r
7798 { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },
\r
7799 { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },
\r
7800 { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },
\r
7801 { IDM_Match, MF_BYCOMMAND|MF_GRAYED },
\r
7802 { IDM_MachineBoth, MF_BYCOMMAND|MF_GRAYED },
\r
7803 { IDM_AnalysisMode, MF_BYCOMMAND|MF_ENABLED },
\r
7804 { IDM_AnalyzeFile, MF_BYCOMMAND|MF_GRAYED },
\r
7805 { IDM_TimeControl, MF_BYCOMMAND|MF_GRAYED },
\r
7806 { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },
\r
7807 { IDM_Hint, MF_BYCOMMAND|MF_GRAYED },
\r
7808 { IDM_Book, MF_BYCOMMAND|MF_GRAYED },
\r
7809 { IDM_IcsOptions, MF_BYCOMMAND|MF_ENABLED },
\r
7810 { IDM_Engine1Options, MF_BYCOMMAND|MF_GRAYED },
\r
7811 { IDM_Engine2Options, MF_BYCOMMAND|MF_GRAYED },
\r
7812 { IDM_Annotate, MF_BYCOMMAND|MF_GRAYED },
\r
7817 Enables zippyEnables[] = {
\r
7818 { IDM_MoveNow, MF_BYCOMMAND|MF_ENABLED },
\r
7819 { IDM_Hint, MF_BYCOMMAND|MF_ENABLED },
\r
7820 { IDM_Book, MF_BYCOMMAND|MF_ENABLED },
\r
7821 { IDM_Engine1Options, MF_BYCOMMAND|MF_ENABLED },
\r
7826 Enables ncpEnables[] = {
\r
7827 { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },
\r
7828 { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },
\r
7829 { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },
\r
7830 { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },
\r
7831 { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },
\r
7832 { IDM_Match, MF_BYCOMMAND|MF_GRAYED },
\r
7833 { IDM_AnalysisMode, MF_BYCOMMAND|MF_GRAYED },
\r
7834 { IDM_AnalyzeFile, MF_BYCOMMAND|MF_GRAYED },
\r
7835 { IDM_IcsClient, MF_BYCOMMAND|MF_GRAYED },
\r
7836 { ACTION_POS, MF_BYPOSITION|MF_GRAYED },
\r
7837 { IDM_Revert, MF_BYCOMMAND|MF_GRAYED },
\r
7838 { IDM_Annotate, MF_BYCOMMAND|MF_GRAYED },
\r
7839 { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },
\r
7840 { IDM_RetractMove, MF_BYCOMMAND|MF_GRAYED },
\r
7841 { IDM_TimeControl, MF_BYCOMMAND|MF_GRAYED },
\r
7842 { IDM_Hint, MF_BYCOMMAND|MF_GRAYED },
\r
7843 { IDM_Book, MF_BYCOMMAND|MF_GRAYED },
\r
7844 { IDM_MachineBoth, MF_BYCOMMAND|MF_GRAYED },
\r
7845 { IDM_NewChat, MF_BYCOMMAND|MF_GRAYED },
\r
7846 { IDM_Engine1Options, MF_BYCOMMAND|MF_GRAYED },
\r
7847 { IDM_Engine2Options, MF_BYCOMMAND|MF_GRAYED },
\r
7851 Enables trainingOnEnables[] = {
\r
7852 { IDM_EditComment, MF_BYCOMMAND|MF_GRAYED },
\r
7853 { IDM_Comment, MF_BYCOMMAND|MF_GRAYED },
\r
7854 { IDM_Pause, MF_BYCOMMAND|MF_GRAYED },
\r
7855 { IDM_Forward, MF_BYCOMMAND|MF_GRAYED },
\r
7856 { IDM_Backward, MF_BYCOMMAND|MF_GRAYED },
\r
7857 { IDM_ToEnd, MF_BYCOMMAND|MF_GRAYED },
\r
7858 { IDM_ToStart, MF_BYCOMMAND|MF_GRAYED },
\r
7859 { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },
\r
7860 { IDM_TruncateGame, MF_BYCOMMAND|MF_GRAYED },
\r
7864 Enables trainingOffEnables[] = {
\r
7865 { IDM_EditComment, MF_BYCOMMAND|MF_ENABLED },
\r
7866 { IDM_Comment, MF_BYCOMMAND|MF_ENABLED },
\r
7867 { IDM_Pause, MF_BYCOMMAND|MF_ENABLED },
\r
7868 { IDM_Forward, MF_BYCOMMAND|MF_ENABLED },
\r
7869 { IDM_Backward, MF_BYCOMMAND|MF_ENABLED },
\r
7870 { IDM_ToEnd, MF_BYCOMMAND|MF_ENABLED },
\r
7871 { IDM_ToStart, MF_BYCOMMAND|MF_ENABLED },
\r
7872 { IDM_MoveNow, MF_BYCOMMAND|MF_ENABLED },
\r
7873 { IDM_TruncateGame, MF_BYCOMMAND|MF_ENABLED },
\r
7877 /* These modify either ncpEnables or gnuEnables */
\r
7878 Enables cmailEnables[] = {
\r
7879 { IDM_MailMove, MF_BYCOMMAND|MF_ENABLED },
\r
7880 { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_ENABLED },
\r
7881 { ACTION_POS, MF_BYPOSITION|MF_ENABLED },
\r
7882 { IDM_CallFlag, MF_BYCOMMAND|MF_GRAYED },
\r
7883 { IDM_Draw, MF_BYCOMMAND|MF_ENABLED },
\r
7884 { IDM_Adjourn, MF_BYCOMMAND|MF_GRAYED },
\r
7885 { IDM_Abort, MF_BYCOMMAND|MF_GRAYED },
\r
7889 Enables machineThinkingEnables[] = {
\r
7890 { IDM_LoadGame, MF_BYCOMMAND|MF_GRAYED },
\r
7891 { IDM_LoadNextGame, MF_BYCOMMAND|MF_GRAYED },
\r
7892 { IDM_LoadPrevGame, MF_BYCOMMAND|MF_GRAYED },
\r
7893 { IDM_ReloadGame, MF_BYCOMMAND|MF_GRAYED },
\r
7894 { IDM_PasteGame, MF_BYCOMMAND|MF_GRAYED },
\r
7895 { IDM_LoadPosition, MF_BYCOMMAND|MF_GRAYED },
\r
7896 { IDM_LoadNextPosition, MF_BYCOMMAND|MF_GRAYED },
\r
7897 { IDM_LoadPrevPosition, MF_BYCOMMAND|MF_GRAYED },
\r
7898 { IDM_ReloadPosition, MF_BYCOMMAND|MF_GRAYED },
\r
7899 { IDM_PastePosition, MF_BYCOMMAND|MF_GRAYED },
\r
7900 { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },
\r
7901 { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },
\r
7902 { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },
\r
7903 { IDM_Match, MF_BYCOMMAND|MF_GRAYED },
\r
7904 { IDM_TypeInMove, MF_BYCOMMAND|MF_GRAYED },
\r
7905 { IDM_RetractMove, MF_BYCOMMAND|MF_GRAYED },
\r
7909 Enables userThinkingEnables[] = {
\r
7910 { IDM_LoadGame, MF_BYCOMMAND|MF_ENABLED },
\r
7911 { IDM_LoadNextGame, MF_BYCOMMAND|MF_ENABLED },
\r
7912 { IDM_LoadPrevGame, MF_BYCOMMAND|MF_ENABLED },
\r
7913 { IDM_ReloadGame, MF_BYCOMMAND|MF_ENABLED },
\r
7914 { IDM_PasteGame, MF_BYCOMMAND|MF_ENABLED },
\r
7915 { IDM_LoadPosition, MF_BYCOMMAND|MF_ENABLED },
\r
7916 { IDM_LoadNextPosition, MF_BYCOMMAND|MF_ENABLED },
\r
7917 { IDM_LoadPrevPosition, MF_BYCOMMAND|MF_ENABLED },
\r
7918 { IDM_ReloadPosition, MF_BYCOMMAND|MF_ENABLED },
\r
7919 { IDM_PastePosition, MF_BYCOMMAND|MF_ENABLED },
\r
7920 { IDM_MachineWhite, MF_BYCOMMAND|MF_ENABLED },
\r
7921 { IDM_MachineBlack, MF_BYCOMMAND|MF_ENABLED },
\r
7922 { IDM_TwoMachines, MF_BYCOMMAND|MF_ENABLED },
\r
7923 { IDM_Match, MF_BYCOMMAND|MF_ENABLED },
\r
7924 { IDM_TypeInMove, MF_BYCOMMAND|MF_ENABLED },
\r
7925 { IDM_RetractMove, MF_BYCOMMAND|MF_ENABLED },
\r
7929 /*---------------------------------------------------------------------------*\
\r
7931 * Front-end interface functions exported by XBoard.
\r
7932 * Functions appear in same order as prototypes in frontend.h.
\r
7934 \*---------------------------------------------------------------------------*/
\r
7938 static UINT prevChecked = 0;
\r
7939 static int prevPausing = 0;
\r
7942 if (pausing != prevPausing) {
\r
7943 prevPausing = pausing;
\r
7944 (void) CheckMenuItem(GetMenu(hwndMain), IDM_Pause,
\r
7945 MF_BYCOMMAND|(pausing ? MF_CHECKED : MF_UNCHECKED));
\r
7946 if (hwndPause) SetWindowText(hwndPause, pausing ? "C" : "P");
\r
7949 switch (gameMode) {
\r
7950 case BeginningOfGame:
\r
7951 if (appData.icsActive)
\r
7952 nowChecked = IDM_IcsClient;
\r
7953 else if (appData.noChessProgram)
\r
7954 nowChecked = IDM_EditGame;
\r
7956 nowChecked = IDM_MachineBlack;
\r
7958 case MachinePlaysBlack:
\r
7959 nowChecked = IDM_MachineBlack;
\r
7961 case MachinePlaysWhite:
\r
7962 nowChecked = IDM_MachineWhite;
\r
7964 case TwoMachinesPlay:
\r
7965 nowChecked = matchMode ? IDM_Match : IDM_TwoMachines; // [HGM] match
\r
7968 nowChecked = IDM_AnalysisMode;
\r
7971 nowChecked = IDM_AnalyzeFile;
\r
7974 nowChecked = IDM_EditGame;
\r
7976 case PlayFromGameFile:
\r
7977 nowChecked = IDM_LoadGame;
\r
7979 case EditPosition:
\r
7980 nowChecked = IDM_EditPosition;
\r
7983 nowChecked = IDM_Training;
\r
7985 case IcsPlayingWhite:
\r
7986 case IcsPlayingBlack:
\r
7987 case IcsObserving:
\r
7989 nowChecked = IDM_IcsClient;
\r
7996 if (prevChecked != 0)
\r
7997 (void) CheckMenuItem(GetMenu(hwndMain),
\r
7998 prevChecked, MF_BYCOMMAND|MF_UNCHECKED);
\r
7999 if (nowChecked != 0)
\r
8000 (void) CheckMenuItem(GetMenu(hwndMain),
\r
8001 nowChecked, MF_BYCOMMAND|MF_CHECKED);
\r
8003 if (nowChecked == IDM_LoadGame || nowChecked == IDM_Training) {
\r
8004 (void) EnableMenuItem(GetMenu(hwndMain), IDM_Training,
\r
8005 MF_BYCOMMAND|MF_ENABLED);
\r
8007 (void) EnableMenuItem(GetMenu(hwndMain),
\r
8008 IDM_Training, MF_BYCOMMAND|MF_GRAYED);
\r
8011 prevChecked = nowChecked;
\r
8013 /* [DM] icsEngineAnalyze - Do a sceure check too */
\r
8014 if (appData.icsActive) {
\r
8015 if (appData.icsEngineAnalyze) {
\r
8016 (void) CheckMenuItem(GetMenu(hwndMain), IDM_AnalysisMode,
\r
8017 MF_BYCOMMAND|MF_CHECKED);
\r
8019 (void) CheckMenuItem(GetMenu(hwndMain), IDM_AnalysisMode,
\r
8020 MF_BYCOMMAND|MF_UNCHECKED);
\r
8028 HMENU hmenu = GetMenu(hwndMain);
\r
8029 SetMenuEnables(hmenu, icsEnables);
\r
8030 EnableMenuItem(GetSubMenu(hmenu, OPTIONS_POS), ICS_POS,
\r
8031 MF_BYPOSITION|MF_ENABLED);
\r
8033 if (appData.zippyPlay) {
\r
8034 SetMenuEnables(hmenu, zippyEnables);
\r
8035 if (!appData.noChessProgram) /* [DM] icsEngineAnalyze */
\r
8036 (void) EnableMenuItem(GetMenu(hwndMain), IDM_AnalysisMode,
\r
8037 MF_BYCOMMAND|MF_ENABLED);
\r
8045 SetMenuEnables(GetMenu(hwndMain), gnuEnables);
\r
8051 HMENU hmenu = GetMenu(hwndMain);
\r
8052 SetMenuEnables(hmenu, ncpEnables);
\r
8053 EnableMenuItem(GetSubMenu(hmenu, OPTIONS_POS), SOUNDS_POS,
\r
8054 MF_BYPOSITION|MF_GRAYED);
\r
8055 DrawMenuBar(hwndMain);
\r
8061 SetMenuEnables(GetMenu(hwndMain), cmailEnables);
\r
8065 SetTrainingModeOn()
\r
8068 SetMenuEnables(GetMenu(hwndMain), trainingOnEnables);
\r
8069 for (i = 0; i < N_BUTTONS; i++) {
\r
8070 if (buttonDesc[i].hwnd != NULL)
\r
8071 EnableWindow(buttonDesc[i].hwnd, FALSE);
\r
8076 VOID SetTrainingModeOff()
\r
8079 SetMenuEnables(GetMenu(hwndMain), trainingOffEnables);
\r
8080 for (i = 0; i < N_BUTTONS; i++) {
\r
8081 if (buttonDesc[i].hwnd != NULL)
\r
8082 EnableWindow(buttonDesc[i].hwnd, TRUE);
\r
8088 SetUserThinkingEnables()
\r
8090 SetMenuEnables(GetMenu(hwndMain), userThinkingEnables);
\r
8094 SetMachineThinkingEnables()
\r
8096 HMENU hMenu = GetMenu(hwndMain);
\r
8097 int flags = MF_BYCOMMAND|MF_ENABLED;
\r
8099 SetMenuEnables(hMenu, machineThinkingEnables);
\r
8101 if (gameMode == MachinePlaysBlack) {
\r
8102 (void)EnableMenuItem(hMenu, IDM_MachineBlack, flags);
\r
8103 } else if (gameMode == MachinePlaysWhite) {
\r
8104 (void)EnableMenuItem(hMenu, IDM_MachineWhite, flags);
\r
8105 } else if (gameMode == TwoMachinesPlay) {
\r
8106 (void)EnableMenuItem(hMenu, matchMode ? IDM_Match : IDM_TwoMachines, flags); // [HGM] match
\r
8112 DisplayTitle(char *str)
\r
8114 char title[MSG_SIZ], *host;
\r
8115 if (str[0] != NULLCHAR) {
\r
8116 safeStrCpy(title, str, sizeof(title)/sizeof(title[0]) );
\r
8117 } else if (appData.icsActive) {
\r
8118 if (appData.icsCommPort[0] != NULLCHAR)
\r
8121 host = appData.icsHost;
\r
8122 snprintf(title, MSG_SIZ, "%s: %s", szTitle, host);
\r
8123 } else if (appData.noChessProgram) {
\r
8124 safeStrCpy(title, szTitle, sizeof(title)/sizeof(title[0]) );
\r
8126 safeStrCpy(title, szTitle, sizeof(title)/sizeof(title[0]) );
\r
8127 strcat(title, ": ");
\r
8128 strcat(title, first.tidy);
\r
8130 SetWindowText(hwndMain, title);
\r
8135 DisplayMessage(char *str1, char *str2)
\r
8139 int remain = MESSAGE_TEXT_MAX - 1;
\r
8142 moveErrorMessageUp = FALSE; /* turned on later by caller if needed */
\r
8143 messageText[0] = NULLCHAR;
\r
8145 len = strlen(str1);
\r
8146 if (len > remain) len = remain;
\r
8147 strncpy(messageText, str1, len);
\r
8148 messageText[len] = NULLCHAR;
\r
8151 if (*str2 && remain >= 2) {
\r
8153 strcat(messageText, " ");
\r
8156 len = strlen(str2);
\r
8157 if (len > remain) len = remain;
\r
8158 strncat(messageText, str2, len);
\r
8160 messageText[MESSAGE_TEXT_MAX - 1] = NULLCHAR;
\r
8162 if (hwndMain == NULL || IsIconic(hwndMain)) return;
\r
8166 hdc = GetDC(hwndMain);
\r
8167 oldFont = SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);
\r
8168 ExtTextOut(hdc, messageRect.left, messageRect.top, ETO_CLIPPED|ETO_OPAQUE,
\r
8169 &messageRect, messageText, strlen(messageText), NULL);
\r
8170 (void) SelectObject(hdc, oldFont);
\r
8171 (void) ReleaseDC(hwndMain, hdc);
\r
8175 DisplayError(char *str, int error)
\r
8177 char buf[MSG_SIZ*2], buf2[MSG_SIZ];
\r
8181 safeStrCpy(buf, str, sizeof(buf)/sizeof(buf[0]) );
\r
8183 len = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,
\r
8184 NULL, error, LANG_NEUTRAL,
\r
8185 (LPSTR) buf2, MSG_SIZ, NULL);
\r
8187 snprintf(buf, 2*MSG_SIZ, "%s:\n%s", str, buf2);
\r
8189 ErrorMap *em = errmap;
\r
8190 while (em->err != 0 && em->err != error) em++;
\r
8191 if (em->err != 0) {
\r
8192 snprintf(buf, 2*MSG_SIZ, "%s:\n%s", str, em->msg);
\r
8194 snprintf(buf, 2*MSG_SIZ, "%s:\nError code %d", str, error);
\r
8199 ErrorPopUp(_("Error"), buf);
\r
8204 DisplayMoveError(char *str)
\r
8206 fromX = fromY = -1;
\r
8207 ClearHighlights();
\r
8208 DrawPosition(FALSE, NULL);
\r
8209 if (appData.popupMoveErrors) {
\r
8210 ErrorPopUp(_("Error"), str);
\r
8212 DisplayMessage(str, "");
\r
8213 moveErrorMessageUp = TRUE;
\r
8218 DisplayFatalError(char *str, int error, int exitStatus)
\r
8220 char buf[2*MSG_SIZ], buf2[MSG_SIZ];
\r
8222 char *label = exitStatus ? _("Fatal Error") : _("Exiting");
\r
8225 len = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,
\r
8226 NULL, error, LANG_NEUTRAL,
\r
8227 (LPSTR) buf2, MSG_SIZ, NULL);
\r
8229 snprintf(buf, 2*MSG_SIZ, "%s:\n%s", str, buf2);
\r
8231 ErrorMap *em = errmap;
\r
8232 while (em->err != 0 && em->err != error) em++;
\r
8233 if (em->err != 0) {
\r
8234 snprintf(buf, 2*MSG_SIZ, "%s:\n%s", str, em->msg);
\r
8236 snprintf(buf, 2*MSG_SIZ, "%s:\nError code %d", str, error);
\r
8241 if (appData.debugMode) {
\r
8242 fprintf(debugFP, "%s: %s\n", label, str);
\r
8244 if (appData.popupExitMessage) {
\r
8245 (void) MessageBox(hwndMain, str, label, MB_OK|
\r
8246 (exitStatus ? MB_ICONSTOP : MB_ICONINFORMATION));
\r
8248 ExitEvent(exitStatus);
\r
8253 DisplayInformation(char *str)
\r
8255 (void) MessageBox(hwndMain, str, _("Information"), MB_OK|MB_ICONINFORMATION);
\r
8260 DisplayNote(char *str)
\r
8262 ErrorPopUp(_("Note"), str);
\r
8267 char *title, *question, *replyPrefix;
\r
8272 QuestionDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
8274 static QuestionParams *qp;
\r
8275 char reply[MSG_SIZ];
\r
8278 switch (message) {
\r
8279 case WM_INITDIALOG:
\r
8280 qp = (QuestionParams *) lParam;
\r
8281 CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));
\r
8282 Translate(hDlg, DLG_Question);
\r
8283 SetWindowText(hDlg, qp->title);
\r
8284 SetDlgItemText(hDlg, OPT_QuestionText, qp->question);
\r
8285 SetFocus(GetDlgItem(hDlg, OPT_QuestionInput));
\r
8289 switch (LOWORD(wParam)) {
\r
8291 safeStrCpy(reply, qp->replyPrefix, sizeof(reply)/sizeof(reply[0]) );
\r
8292 if (*reply) strcat(reply, " ");
\r
8293 len = strlen(reply);
\r
8294 GetDlgItemText(hDlg, OPT_QuestionInput, reply + len, sizeof(reply) - len);
\r
8295 strcat(reply, "\n");
\r
8296 OutputToProcess(qp->pr, reply, strlen(reply), &err);
\r
8297 EndDialog(hDlg, TRUE);
\r
8298 if (err) DisplayFatalError(_("Error writing to chess program"), err, 1);
\r
8301 EndDialog(hDlg, FALSE);
\r
8312 AskQuestion(char* title, char *question, char *replyPrefix, ProcRef pr)
\r
8314 QuestionParams qp;
\r
8318 qp.question = question;
\r
8319 qp.replyPrefix = replyPrefix;
\r
8321 lpProc = MakeProcInstance((FARPROC)QuestionDialog, hInst);
\r
8322 DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_Question),
\r
8323 hwndMain, (DLGPROC)lpProc, (LPARAM)&qp);
\r
8324 FreeProcInstance(lpProc);
\r
8327 /* [AS] Pick FRC position */
\r
8328 LRESULT CALLBACK NewGameFRC_Proc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
8330 static int * lpIndexFRC;
\r
8336 case WM_INITDIALOG:
\r
8337 lpIndexFRC = (int *) lParam;
\r
8339 CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));
\r
8340 Translate(hDlg, DLG_NewGameFRC);
\r
8342 SendDlgItemMessage( hDlg, IDC_NFG_Edit, EM_SETLIMITTEXT, sizeof(buf)-1, 0 );
\r
8343 SetDlgItemInt( hDlg, IDC_NFG_Edit, *lpIndexFRC, TRUE );
\r
8344 SendDlgItemMessage( hDlg, IDC_NFG_Edit, EM_SETSEL, 0, -1 );
\r
8345 SetFocus(GetDlgItem(hDlg, IDC_NFG_Edit));
\r
8350 switch( LOWORD(wParam) ) {
\r
8352 *lpIndexFRC = GetDlgItemInt(hDlg, IDC_NFG_Edit, &index_is_ok, TRUE );
\r
8353 EndDialog( hDlg, 0 );
\r
8354 shuffleOpenings = TRUE; /* [HGM] shuffle: switch shuffling on for as long as we stay in current variant */
\r
8357 EndDialog( hDlg, 1 );
\r
8359 case IDC_NFG_Edit:
\r
8360 if( HIWORD(wParam) == EN_CHANGE ) {
\r
8361 GetDlgItemInt(hDlg, IDC_NFG_Edit, &index_is_ok, TRUE );
\r
8363 EnableWindow( GetDlgItem(hDlg, IDOK), index_is_ok );
\r
8366 case IDC_NFG_Random:
\r
8367 snprintf( buf, sizeof(buf)/sizeof(buf[0]), "%d", myrandom() ); /* [HGM] shuffle: no longer limit to 960 */
\r
8368 SetDlgItemText(hDlg, IDC_NFG_Edit, buf );
\r
8381 int index = appData.defaultFrcPosition;
\r
8382 FARPROC lpProc = MakeProcInstance( (FARPROC) NewGameFRC_Proc, hInst );
\r
8384 result = DialogBoxParam( hInst, MAKEINTRESOURCE(DLG_NewGameFRC), hwndMain, (DLGPROC)lpProc, (LPARAM)&index );
\r
8386 if( result == 0 ) {
\r
8387 appData.defaultFrcPosition = index;
\r
8393 /* [AS] Game list options. Refactored by HGM */
\r
8395 HWND gameListOptionsDialog;
\r
8397 // low-level front-end: clear text edit / list widget
\r
8401 SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_RESETCONTENT, 0, 0 );
\r
8404 // low-level front-end: clear text edit / list widget
\r
8406 GLT_DeSelectList()
\r
8408 SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_SETCURSEL, 0, 0 );
\r
8411 // low-level front-end: append line to text edit / list widget
\r
8413 GLT_AddToList( char *name )
\r
8416 SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_ADDSTRING, 0, (LPARAM) name );
\r
8420 // low-level front-end: get line from text edit / list widget
\r
8422 GLT_GetFromList( int index, char *name )
\r
8425 if( SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_GETTEXT, index, (LPARAM) name ) != LB_ERR )
\r
8431 void GLT_MoveSelection( HWND hDlg, int delta )
\r
8433 int idx1 = (int) SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETCURSEL, 0, 0 );
\r
8434 int idx2 = idx1 + delta;
\r
8435 int count = (int) SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETCOUNT, 0, 0 );
\r
8437 if( idx1 >=0 && idx1 < count && idx2 >= 0 && idx2 < count ) {
\r
8440 SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETTEXT, idx1, (LPARAM) buf );
\r
8441 SendDlgItemMessage( hDlg, IDC_GameListTags, LB_DELETESTRING, idx1, 0 );
\r
8442 SendDlgItemMessage( hDlg, IDC_GameListTags, LB_INSERTSTRING, idx2, (LPARAM) buf );
\r
8443 SendDlgItemMessage( hDlg, IDC_GameListTags, LB_SETCURSEL, idx2, 0 );
\r
8447 LRESULT CALLBACK GameListOptions_Proc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
8451 case WM_INITDIALOG:
\r
8452 gameListOptionsDialog = hDlg; // [HGM] pass through global to keep out off back-end
\r
8454 CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));
\r
8455 Translate(hDlg, DLG_GameListOptions);
\r
8457 /* Initialize list */
\r
8458 GLT_TagsToList( lpUserGLT );
\r
8460 SetFocus( GetDlgItem(hDlg, IDC_GameListTags) );
\r
8465 switch( LOWORD(wParam) ) {
\r
8468 EndDialog( hDlg, 0 );
\r
8471 EndDialog( hDlg, 1 );
\r
8474 case IDC_GLT_Default:
\r
8475 GLT_TagsToList( GLT_DEFAULT_TAGS );
\r
8478 case IDC_GLT_Restore:
\r
8479 GLT_TagsToList( appData.gameListTags );
\r
8483 GLT_MoveSelection( hDlg, -1 );
\r
8486 case IDC_GLT_Down:
\r
8487 GLT_MoveSelection( hDlg, +1 );
\r
8497 int GameListOptions()
\r
8500 FARPROC lpProc = MakeProcInstance( (FARPROC) GameListOptions_Proc, hInst );
\r
8502 safeStrCpy( lpUserGLT, appData.gameListTags ,LPUSERGLT_SIZE );
\r
8504 result = DialogBoxParam( hInst, MAKEINTRESOURCE(DLG_GameListOptions), hwndMain, (DLGPROC)lpProc, (LPARAM)lpUserGLT );
\r
8506 if( result == 0 ) {
\r
8507 /* [AS] Memory leak here! */
\r
8508 appData.gameListTags = strdup( lpUserGLT );
\r
8515 DisplayIcsInteractionTitle(char *str)
\r
8517 char consoleTitle[MSG_SIZ];
\r
8519 snprintf(consoleTitle, MSG_SIZ, "%s: %s", szConsoleTitle, str);
\r
8520 SetWindowText(hwndConsole, consoleTitle);
\r
8524 DrawPosition(int fullRedraw, Board board)
\r
8526 HDCDrawPosition(NULL, (BOOLEAN) fullRedraw, board);
\r
8529 void NotifyFrontendLogin()
\r
8532 UpdateICSWidth(GetDlgItem(hwndConsole, OPT_ConsoleText));
\r
8538 fromX = fromY = -1;
\r
8539 if (dragInfo.pos.x != -1 || dragInfo.pos.y != -1) {
\r
8540 dragInfo.pos.x = dragInfo.pos.y = -1;
\r
8541 dragInfo.pos.x = dragInfo.pos.y = -1;
\r
8542 dragInfo.lastpos = dragInfo.pos;
\r
8543 dragInfo.start.x = dragInfo.start.y = -1;
\r
8544 dragInfo.from = dragInfo.start;
\r
8546 DrawPosition(TRUE, NULL);
\r
8553 CommentPopUp(char *title, char *str)
\r
8555 HWND hwnd = GetActiveWindow();
\r
8556 EitherCommentPopUp(currentMove, title, str, FALSE); // [HGM] vari: fake move index, rather than 0
\r
8558 SetActiveWindow(hwnd);
\r
8562 CommentPopDown(void)
\r
8564 CheckMenuItem(GetMenu(hwndMain), IDM_Comment, MF_UNCHECKED);
\r
8565 if (commentDialog) {
\r
8566 ShowWindow(commentDialog, SW_HIDE);
\r
8568 commentUp = FALSE;
\r
8572 EditCommentPopUp(int index, char *title, char *str)
\r
8574 EitherCommentPopUp(index, title, str, TRUE);
\r
8581 MyPlaySound(&sounds[(int)SoundMove]);
\r
8584 VOID PlayIcsWinSound()
\r
8586 MyPlaySound(&sounds[(int)SoundIcsWin]);
\r
8589 VOID PlayIcsLossSound()
\r
8591 MyPlaySound(&sounds[(int)SoundIcsLoss]);
\r
8594 VOID PlayIcsDrawSound()
\r
8596 MyPlaySound(&sounds[(int)SoundIcsDraw]);
\r
8599 VOID PlayIcsUnfinishedSound()
\r
8601 MyPlaySound(&sounds[(int)SoundIcsUnfinished]);
\r
8607 MyPlaySound(&sounds[(int)SoundAlarm]);
\r
8615 consoleEcho = TRUE;
\r
8616 hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
8617 SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&consoleCF);
\r
8618 SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);
\r
8627 consoleEcho = FALSE;
\r
8628 hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
8629 /* This works OK: set text and background both to the same color */
\r
8631 cf.crTextColor = COLOR_ECHOOFF;
\r
8632 SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf);
\r
8633 SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, cf.crTextColor);
\r
8636 /* No Raw()...? */
\r
8638 void Colorize(ColorClass cc, int continuation)
\r
8640 currentColorClass = cc;
\r
8641 consoleCF.dwMask = CFM_COLOR|CFM_BOLD|CFM_ITALIC|CFM_UNDERLINE|CFM_STRIKEOUT;
\r
8642 consoleCF.crTextColor = textAttribs[cc].color;
\r
8643 consoleCF.dwEffects = textAttribs[cc].effects;
\r
8644 if (!continuation) MyPlaySound(&textAttribs[cc].sound);
\r
8650 static char buf[MSG_SIZ];
\r
8651 DWORD bufsiz = MSG_SIZ;
\r
8653 if(appData.userName != NULL && appData.userName[0] != 0) {
\r
8654 return appData.userName; /* [HGM] username: prefer name selected by user over his system login */
\r
8656 if (!GetUserName(buf, &bufsiz)) {
\r
8657 /*DisplayError("Error getting user name", GetLastError());*/
\r
8658 safeStrCpy(buf, _("User"), sizeof(buf)/sizeof(buf[0]) );
\r
8666 static char buf[MSG_SIZ];
\r
8667 DWORD bufsiz = MSG_SIZ;
\r
8669 if (!GetComputerName(buf, &bufsiz)) {
\r
8670 /*DisplayError("Error getting host name", GetLastError());*/
\r
8671 safeStrCpy(buf, _("Unknown"), sizeof(buf)/sizeof(buf[0]) );
\r
8678 ClockTimerRunning()
\r
8680 return clockTimerEvent != 0;
\r
8686 if (clockTimerEvent == 0) return FALSE;
\r
8687 KillTimer(hwndMain, clockTimerEvent);
\r
8688 clockTimerEvent = 0;
\r
8693 StartClockTimer(long millisec)
\r
8695 clockTimerEvent = SetTimer(hwndMain, (UINT) CLOCK_TIMER_ID,
\r
8696 (UINT) millisec, NULL);
\r
8700 DisplayWhiteClock(long timeRemaining, int highlight)
\r
8703 char *flag = whiteFlag && gameMode == TwoMachinesPlay ? "(!)" : "";
\r
8705 if(appData.noGUI) return;
\r
8706 hdc = GetDC(hwndMain);
\r
8707 if (!IsIconic(hwndMain)) {
\r
8708 DisplayAClock(hdc, timeRemaining, highlight,
\r
8709 flipClock ? &blackRect : &whiteRect, _("White"), flag);
\r
8711 if (highlight && iconCurrent == iconBlack) {
\r
8712 iconCurrent = iconWhite;
\r
8713 PostMessage(hwndMain, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);
\r
8714 if (IsIconic(hwndMain)) {
\r
8715 DrawIcon(hdc, 2, 2, iconCurrent);
\r
8718 (void) ReleaseDC(hwndMain, hdc);
\r
8720 PostMessage(hwndConsole, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);
\r
8724 DisplayBlackClock(long timeRemaining, int highlight)
\r
8727 char *flag = blackFlag && gameMode == TwoMachinesPlay ? "(!)" : "";
\r
8729 if(appData.noGUI) return;
\r
8730 hdc = GetDC(hwndMain);
\r
8731 if (!IsIconic(hwndMain)) {
\r
8732 DisplayAClock(hdc, timeRemaining, highlight,
\r
8733 flipClock ? &whiteRect : &blackRect, _("Black"), flag);
\r
8735 if (highlight && iconCurrent == iconWhite) {
\r
8736 iconCurrent = iconBlack;
\r
8737 PostMessage(hwndMain, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);
\r
8738 if (IsIconic(hwndMain)) {
\r
8739 DrawIcon(hdc, 2, 2, iconCurrent);
\r
8742 (void) ReleaseDC(hwndMain, hdc);
\r
8744 PostMessage(hwndConsole, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);
\r
8749 LoadGameTimerRunning()
\r
8751 return loadGameTimerEvent != 0;
\r
8755 StopLoadGameTimer()
\r
8757 if (loadGameTimerEvent == 0) return FALSE;
\r
8758 KillTimer(hwndMain, loadGameTimerEvent);
\r
8759 loadGameTimerEvent = 0;
\r
8764 StartLoadGameTimer(long millisec)
\r
8766 loadGameTimerEvent = SetTimer(hwndMain, (UINT) LOAD_GAME_TIMER_ID,
\r
8767 (UINT) millisec, NULL);
\r
8775 char fileTitle[MSG_SIZ];
\r
8777 defName = DefaultFileName(appData.oldSaveStyle ? "gam" : "pgn");
\r
8778 f = OpenFileDialog(hwndMain, "a", defName,
\r
8779 appData.oldSaveStyle ? "gam" : "pgn",
\r
8781 _("Save Game to File"), NULL, fileTitle, NULL);
\r
8783 SaveGame(f, 0, "");
\r
8790 ScheduleDelayedEvent(DelayedEventCallback cb, long millisec)
\r
8792 if (delayedTimerEvent != 0) {
\r
8793 if (appData.debugMode && cb != delayedTimerCallback) { // [HGM] alive: not too much debug
\r
8794 fprintf(debugFP, "ScheduleDelayedEvent: event already scheduled\n");
\r
8796 KillTimer(hwndMain, delayedTimerEvent);
\r
8797 delayedTimerEvent = 0;
\r
8798 if(delayedTimerCallback != cb) // [HGM] alive: do not "flush" same event, just postpone it
\r
8799 delayedTimerCallback();
\r
8801 delayedTimerCallback = cb;
\r
8802 delayedTimerEvent = SetTimer(hwndMain, (UINT) DELAYED_TIMER_ID,
\r
8803 (UINT) millisec, NULL);
\r
8806 DelayedEventCallback
\r
8809 if (delayedTimerEvent) {
\r
8810 return delayedTimerCallback;
\r
8817 CancelDelayedEvent()
\r
8819 if (delayedTimerEvent) {
\r
8820 KillTimer(hwndMain, delayedTimerEvent);
\r
8821 delayedTimerEvent = 0;
\r
8825 DWORD GetWin32Priority(int nice)
\r
8826 { // [HGM] nice: translate Unix nice() value to indows priority class. (Code stolen from Polyglot 1.4w11)
\r
8828 REALTIME_PRIORITY_CLASS 0x00000100
\r
8829 HIGH_PRIORITY_CLASS 0x00000080
\r
8830 ABOVE_NORMAL_PRIORITY_CLASS 0x00008000
\r
8831 NORMAL_PRIORITY_CLASS 0x00000020
\r
8832 BELOW_NORMAL_PRIORITY_CLASS 0x00004000
\r
8833 IDLE_PRIORITY_CLASS 0x00000040
\r
8835 if (nice < -15) return 0x00000080;
\r
8836 if (nice < 0) return 0x00008000;
\r
8837 if (nice == 0) return 0x00000020;
\r
8838 if (nice < 15) return 0x00004000;
\r
8839 return 0x00000040;
\r
8842 /* Start a child process running the given program.
\r
8843 The process's standard output can be read from "from", and its
\r
8844 standard input can be written to "to".
\r
8845 Exit with fatal error if anything goes wrong.
\r
8846 Returns an opaque pointer that can be used to destroy the process
\r
8850 StartChildProcess(char *cmdLine, char *dir, ProcRef *pr)
\r
8852 #define BUFSIZE 4096
\r
8854 HANDLE hChildStdinRd, hChildStdinWr,
\r
8855 hChildStdoutRd, hChildStdoutWr;
\r
8856 HANDLE hChildStdinWrDup, hChildStdoutRdDup;
\r
8857 SECURITY_ATTRIBUTES saAttr;
\r
8859 PROCESS_INFORMATION piProcInfo;
\r
8860 STARTUPINFO siStartInfo;
\r
8862 char buf[MSG_SIZ];
\r
8865 if (appData.debugMode) {
\r
8866 fprintf(debugFP, "StartChildProcess (dir=\"%s\") %s\n", dir, cmdLine);
\r
8871 /* Set the bInheritHandle flag so pipe handles are inherited. */
\r
8872 saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);
\r
8873 saAttr.bInheritHandle = TRUE;
\r
8874 saAttr.lpSecurityDescriptor = NULL;
\r
8877 * The steps for redirecting child's STDOUT:
\r
8878 * 1. Create anonymous pipe to be STDOUT for child.
\r
8879 * 2. Create a noninheritable duplicate of read handle,
\r
8880 * and close the inheritable read handle.
\r
8883 /* Create a pipe for the child's STDOUT. */
\r
8884 if (! CreatePipe(&hChildStdoutRd, &hChildStdoutWr, &saAttr, 0)) {
\r
8885 return GetLastError();
\r
8888 /* Duplicate the read handle to the pipe, so it is not inherited. */
\r
8889 fSuccess = DuplicateHandle(GetCurrentProcess(), hChildStdoutRd,
\r
8890 GetCurrentProcess(), &hChildStdoutRdDup, 0,
\r
8891 FALSE, /* not inherited */
\r
8892 DUPLICATE_SAME_ACCESS);
\r
8894 return GetLastError();
\r
8896 CloseHandle(hChildStdoutRd);
\r
8899 * The steps for redirecting child's STDIN:
\r
8900 * 1. Create anonymous pipe to be STDIN for child.
\r
8901 * 2. Create a noninheritable duplicate of write handle,
\r
8902 * and close the inheritable write handle.
\r
8905 /* Create a pipe for the child's STDIN. */
\r
8906 if (! CreatePipe(&hChildStdinRd, &hChildStdinWr, &saAttr, 0)) {
\r
8907 return GetLastError();
\r
8910 /* Duplicate the write handle to the pipe, so it is not inherited. */
\r
8911 fSuccess = DuplicateHandle(GetCurrentProcess(), hChildStdinWr,
\r
8912 GetCurrentProcess(), &hChildStdinWrDup, 0,
\r
8913 FALSE, /* not inherited */
\r
8914 DUPLICATE_SAME_ACCESS);
\r
8916 return GetLastError();
\r
8918 CloseHandle(hChildStdinWr);
\r
8920 /* Arrange to (1) look in dir for the child .exe file, and
\r
8921 * (2) have dir be the child's working directory. Interpret
\r
8922 * dir relative to the directory WinBoard loaded from. */
\r
8923 GetCurrentDirectory(MSG_SIZ, buf);
\r
8924 SetCurrentDirectory(installDir);
\r
8925 SetCurrentDirectory(dir);
\r
8927 /* Now create the child process. */
\r
8929 siStartInfo.cb = sizeof(STARTUPINFO);
\r
8930 siStartInfo.lpReserved = NULL;
\r
8931 siStartInfo.lpDesktop = NULL;
\r
8932 siStartInfo.lpTitle = NULL;
\r
8933 siStartInfo.dwFlags = STARTF_USESTDHANDLES;
\r
8934 siStartInfo.cbReserved2 = 0;
\r
8935 siStartInfo.lpReserved2 = NULL;
\r
8936 siStartInfo.hStdInput = hChildStdinRd;
\r
8937 siStartInfo.hStdOutput = hChildStdoutWr;
\r
8938 siStartInfo.hStdError = hChildStdoutWr;
\r
8940 fSuccess = CreateProcess(NULL,
\r
8941 cmdLine, /* command line */
\r
8942 NULL, /* process security attributes */
\r
8943 NULL, /* primary thread security attrs */
\r
8944 TRUE, /* handles are inherited */
\r
8945 DETACHED_PROCESS|CREATE_NEW_PROCESS_GROUP,
\r
8946 NULL, /* use parent's environment */
\r
8948 &siStartInfo, /* STARTUPINFO pointer */
\r
8949 &piProcInfo); /* receives PROCESS_INFORMATION */
\r
8951 err = GetLastError();
\r
8952 SetCurrentDirectory(buf); /* return to prev directory */
\r
8957 if (appData.niceEngines){ // [HGM] nice: adjust engine proc priority
\r
8958 if(appData.debugMode) fprintf(debugFP, "nice engine proc to %d\n", appData.niceEngines);
\r
8959 SetPriorityClass(piProcInfo.hProcess, GetWin32Priority(appData.niceEngines));
\r
8962 /* Close the handles we don't need in the parent */
\r
8963 CloseHandle(piProcInfo.hThread);
\r
8964 CloseHandle(hChildStdinRd);
\r
8965 CloseHandle(hChildStdoutWr);
\r
8967 /* Prepare return value */
\r
8968 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
\r
8969 cp->kind = CPReal;
\r
8970 cp->hProcess = piProcInfo.hProcess;
\r
8971 cp->pid = piProcInfo.dwProcessId;
\r
8972 cp->hFrom = hChildStdoutRdDup;
\r
8973 cp->hTo = hChildStdinWrDup;
\r
8975 *pr = (void *) cp;
\r
8977 /* Klaus Friedel says that this Sleep solves a problem under Windows
\r
8978 2000 where engines sometimes don't see the initial command(s)
\r
8979 from WinBoard and hang. I don't understand how that can happen,
\r
8980 but the Sleep is harmless, so I've put it in. Others have also
\r
8981 reported what may be the same problem, so hopefully this will fix
\r
8982 it for them too. */
\r
8990 DestroyChildProcess(ProcRef pr, int/*boolean*/ signal)
\r
8992 ChildProc *cp; int result;
\r
8994 cp = (ChildProc *) pr;
\r
8995 if (cp == NULL) return;
\r
8997 switch (cp->kind) {
\r
8999 /* TerminateProcess is considered harmful, so... */
\r
9000 CloseHandle(cp->hTo); /* Closing this will give the child an EOF and hopefully kill it */
\r
9001 if (cp->hFrom) CloseHandle(cp->hFrom); /* if NULL, InputThread will close it */
\r
9002 /* The following doesn't work because the chess program
\r
9003 doesn't "have the same console" as WinBoard. Maybe
\r
9004 we could arrange for this even though neither WinBoard
\r
9005 nor the chess program uses a console for stdio? */
\r
9006 /*!!if (signal) GenerateConsoleCtrlEvent(CTRL_BREAK_EVENT, cp->pid);*/
\r
9008 /* [AS] Special termination modes for misbehaving programs... */
\r
9009 if( signal == 9 ) {
\r
9010 result = TerminateProcess( cp->hProcess, 0 );
\r
9012 if ( appData.debugMode) {
\r
9013 fprintf( debugFP, "Terminating process %lu, result=%d\n", cp->pid, result );
\r
9016 else if( signal == 10 ) {
\r
9017 DWORD dw = WaitForSingleObject( cp->hProcess, 3*1000 ); // Wait 3 seconds at most
\r
9019 if( dw != WAIT_OBJECT_0 ) {
\r
9020 result = TerminateProcess( cp->hProcess, 0 );
\r
9022 if ( appData.debugMode) {
\r
9023 fprintf( debugFP, "Process %lu still alive after timeout, killing... result=%d\n", cp->pid, result );
\r
9029 CloseHandle(cp->hProcess);
\r
9033 if (cp->hFrom) CloseHandle(cp->hFrom);
\r
9037 closesocket(cp->sock);
\r
9042 if (signal) send(cp->sock2, "\017", 1, 0); /* 017 = 15 = SIGTERM */
\r
9043 closesocket(cp->sock);
\r
9044 closesocket(cp->sock2);
\r
9052 InterruptChildProcess(ProcRef pr)
\r
9056 cp = (ChildProc *) pr;
\r
9057 if (cp == NULL) return;
\r
9058 switch (cp->kind) {
\r
9060 /* The following doesn't work because the chess program
\r
9061 doesn't "have the same console" as WinBoard. Maybe
\r
9062 we could arrange for this even though neither WinBoard
\r
9063 nor the chess program uses a console for stdio */
\r
9064 /*!!GenerateConsoleCtrlEvent(CTRL_C_EVENT, cp->pid);*/
\r
9069 /* Can't interrupt */
\r
9073 send(cp->sock2, "\002", 1, 0); /* 2 = SIGINT */
\r
9080 OpenTelnet(char *host, char *port, ProcRef *pr)
\r
9082 char cmdLine[MSG_SIZ];
\r
9084 if (port[0] == NULLCHAR) {
\r
9085 snprintf(cmdLine, MSG_SIZ, "%s %s", appData.telnetProgram, host);
\r
9087 snprintf(cmdLine, MSG_SIZ, "%s %s %s", appData.telnetProgram, host, port);
\r
9089 return StartChildProcess(cmdLine, "", pr);
\r
9093 /* Code to open TCP sockets */
\r
9096 OpenTCP(char *host, char *port, ProcRef *pr)
\r
9101 struct sockaddr_in sa, mysa;
\r
9102 struct hostent FAR *hp;
\r
9103 unsigned short uport;
\r
9104 WORD wVersionRequested;
\r
9107 /* Initialize socket DLL */
\r
9108 wVersionRequested = MAKEWORD(1, 1);
\r
9109 err = WSAStartup(wVersionRequested, &wsaData);
\r
9110 if (err != 0) return err;
\r
9113 if ((s = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {
\r
9114 err = WSAGetLastError();
\r
9119 /* Bind local address using (mostly) don't-care values.
\r
9121 memset((char *) &mysa, 0, sizeof(struct sockaddr_in));
\r
9122 mysa.sin_family = AF_INET;
\r
9123 mysa.sin_addr.s_addr = INADDR_ANY;
\r
9124 uport = (unsigned short) 0;
\r
9125 mysa.sin_port = htons(uport);
\r
9126 if (bind(s, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))
\r
9127 == SOCKET_ERROR) {
\r
9128 err = WSAGetLastError();
\r
9133 /* Resolve remote host name */
\r
9134 memset((char *) &sa, 0, sizeof(struct sockaddr_in));
\r
9135 if (!(hp = gethostbyname(host))) {
\r
9136 unsigned int b0, b1, b2, b3;
\r
9138 err = WSAGetLastError();
\r
9140 if (sscanf(host, "%u.%u.%u.%u", &b0, &b1, &b2, &b3) == 4) {
\r
9141 hp = (struct hostent *) calloc(1, sizeof(struct hostent));
\r
9142 hp->h_addrtype = AF_INET;
\r
9144 hp->h_addr_list = (char **) calloc(2, sizeof(char *));
\r
9145 hp->h_addr_list[0] = (char *) malloc(4);
\r
9146 hp->h_addr_list[0][0] = (char) b0;
\r
9147 hp->h_addr_list[0][1] = (char) b1;
\r
9148 hp->h_addr_list[0][2] = (char) b2;
\r
9149 hp->h_addr_list[0][3] = (char) b3;
\r
9155 sa.sin_family = hp->h_addrtype;
\r
9156 uport = (unsigned short) atoi(port);
\r
9157 sa.sin_port = htons(uport);
\r
9158 memcpy((char *) &sa.sin_addr, hp->h_addr, hp->h_length);
\r
9160 /* Make connection */
\r
9161 if (connect(s, (struct sockaddr *) &sa,
\r
9162 sizeof(struct sockaddr_in)) == SOCKET_ERROR) {
\r
9163 err = WSAGetLastError();
\r
9168 /* Prepare return value */
\r
9169 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
\r
9170 cp->kind = CPSock;
\r
9172 *pr = (ProcRef *) cp;
\r
9178 OpenCommPort(char *name, ProcRef *pr)
\r
9183 char fullname[MSG_SIZ];
\r
9185 if (*name != '\\')
\r
9186 snprintf(fullname, MSG_SIZ, "\\\\.\\%s", name);
\r
9188 safeStrCpy(fullname, name, sizeof(fullname)/sizeof(fullname[0]) );
\r
9190 h = CreateFile(name, GENERIC_READ | GENERIC_WRITE,
\r
9191 0, NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL);
\r
9192 if (h == (HANDLE) -1) {
\r
9193 return GetLastError();
\r
9197 if (!SetCommState(h, (LPDCB) &dcb)) return GetLastError();
\r
9199 /* Accumulate characters until a 100ms pause, then parse */
\r
9200 ct.ReadIntervalTimeout = 100;
\r
9201 ct.ReadTotalTimeoutMultiplier = 0;
\r
9202 ct.ReadTotalTimeoutConstant = 0;
\r
9203 ct.WriteTotalTimeoutMultiplier = 0;
\r
9204 ct.WriteTotalTimeoutConstant = 0;
\r
9205 if (!SetCommTimeouts(h, (LPCOMMTIMEOUTS) &ct)) return GetLastError();
\r
9207 /* Prepare return value */
\r
9208 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
\r
9209 cp->kind = CPComm;
\r
9212 *pr = (ProcRef *) cp;
\r
9218 OpenLoopback(ProcRef *pr)
\r
9220 DisplayFatalError(_("Not implemented"), 0, 1);
\r
9226 OpenRcmd(char* host, char* user, char* cmd, ProcRef* pr)
\r
9231 struct sockaddr_in sa, mysa;
\r
9232 struct hostent FAR *hp;
\r
9233 unsigned short uport;
\r
9234 WORD wVersionRequested;
\r
9237 char stderrPortStr[MSG_SIZ];
\r
9239 /* Initialize socket DLL */
\r
9240 wVersionRequested = MAKEWORD(1, 1);
\r
9241 err = WSAStartup(wVersionRequested, &wsaData);
\r
9242 if (err != 0) return err;
\r
9244 /* Resolve remote host name */
\r
9245 memset((char *) &sa, 0, sizeof(struct sockaddr_in));
\r
9246 if (!(hp = gethostbyname(host))) {
\r
9247 unsigned int b0, b1, b2, b3;
\r
9249 err = WSAGetLastError();
\r
9251 if (sscanf(host, "%u.%u.%u.%u", &b0, &b1, &b2, &b3) == 4) {
\r
9252 hp = (struct hostent *) calloc(1, sizeof(struct hostent));
\r
9253 hp->h_addrtype = AF_INET;
\r
9255 hp->h_addr_list = (char **) calloc(2, sizeof(char *));
\r
9256 hp->h_addr_list[0] = (char *) malloc(4);
\r
9257 hp->h_addr_list[0][0] = (char) b0;
\r
9258 hp->h_addr_list[0][1] = (char) b1;
\r
9259 hp->h_addr_list[0][2] = (char) b2;
\r
9260 hp->h_addr_list[0][3] = (char) b3;
\r
9266 sa.sin_family = hp->h_addrtype;
\r
9267 uport = (unsigned short) 514;
\r
9268 sa.sin_port = htons(uport);
\r
9269 memcpy((char *) &sa.sin_addr, hp->h_addr, hp->h_length);
\r
9271 /* Bind local socket to unused "privileged" port address
\r
9273 s = INVALID_SOCKET;
\r
9274 memset((char *) &mysa, 0, sizeof(struct sockaddr_in));
\r
9275 mysa.sin_family = AF_INET;
\r
9276 mysa.sin_addr.s_addr = INADDR_ANY;
\r
9277 for (fromPort = 1023;; fromPort--) {
\r
9278 if (fromPort < 0) {
\r
9280 return WSAEADDRINUSE;
\r
9282 if (s == INVALID_SOCKET) {
\r
9283 if ((s = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {
\r
9284 err = WSAGetLastError();
\r
9289 uport = (unsigned short) fromPort;
\r
9290 mysa.sin_port = htons(uport);
\r
9291 if (bind(s, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))
\r
9292 == SOCKET_ERROR) {
\r
9293 err = WSAGetLastError();
\r
9294 if (err == WSAEADDRINUSE) continue;
\r
9298 if (connect(s, (struct sockaddr *) &sa,
\r
9299 sizeof(struct sockaddr_in)) == SOCKET_ERROR) {
\r
9300 err = WSAGetLastError();
\r
9301 if (err == WSAEADDRINUSE) {
\r
9312 /* Bind stderr local socket to unused "privileged" port address
\r
9314 s2 = INVALID_SOCKET;
\r
9315 memset((char *) &mysa, 0, sizeof(struct sockaddr_in));
\r
9316 mysa.sin_family = AF_INET;
\r
9317 mysa.sin_addr.s_addr = INADDR_ANY;
\r
9318 for (fromPort = 1023;; fromPort--) {
\r
9319 if (fromPort == prevStderrPort) continue; // don't reuse port
\r
9320 if (fromPort < 0) {
\r
9321 (void) closesocket(s);
\r
9323 return WSAEADDRINUSE;
\r
9325 if (s2 == INVALID_SOCKET) {
\r
9326 if ((s2 = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {
\r
9327 err = WSAGetLastError();
\r
9333 uport = (unsigned short) fromPort;
\r
9334 mysa.sin_port = htons(uport);
\r
9335 if (bind(s2, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))
\r
9336 == SOCKET_ERROR) {
\r
9337 err = WSAGetLastError();
\r
9338 if (err == WSAEADDRINUSE) continue;
\r
9339 (void) closesocket(s);
\r
9343 if (listen(s2, 1) == SOCKET_ERROR) {
\r
9344 err = WSAGetLastError();
\r
9345 if (err == WSAEADDRINUSE) {
\r
9347 s2 = INVALID_SOCKET;
\r
9350 (void) closesocket(s);
\r
9351 (void) closesocket(s2);
\r
9357 prevStderrPort = fromPort; // remember port used
\r
9358 snprintf(stderrPortStr, MSG_SIZ, "%d", fromPort);
\r
9360 if (send(s, stderrPortStr, strlen(stderrPortStr) + 1, 0) == SOCKET_ERROR) {
\r
9361 err = WSAGetLastError();
\r
9362 (void) closesocket(s);
\r
9363 (void) closesocket(s2);
\r
9368 if (send(s, UserName(), strlen(UserName()) + 1, 0) == SOCKET_ERROR) {
\r
9369 err = WSAGetLastError();
\r
9370 (void) closesocket(s);
\r
9371 (void) closesocket(s2);
\r
9375 if (*user == NULLCHAR) user = UserName();
\r
9376 if (send(s, user, strlen(user) + 1, 0) == SOCKET_ERROR) {
\r
9377 err = WSAGetLastError();
\r
9378 (void) closesocket(s);
\r
9379 (void) closesocket(s2);
\r
9383 if (send(s, cmd, strlen(cmd) + 1, 0) == SOCKET_ERROR) {
\r
9384 err = WSAGetLastError();
\r
9385 (void) closesocket(s);
\r
9386 (void) closesocket(s2);
\r
9391 if ((s3 = accept(s2, NULL, NULL)) == INVALID_SOCKET) {
\r
9392 err = WSAGetLastError();
\r
9393 (void) closesocket(s);
\r
9394 (void) closesocket(s2);
\r
9398 (void) closesocket(s2); /* Stop listening */
\r
9400 /* Prepare return value */
\r
9401 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
\r
9402 cp->kind = CPRcmd;
\r
9405 *pr = (ProcRef *) cp;
\r
9412 AddInputSource(ProcRef pr, int lineByLine,
\r
9413 InputCallback func, VOIDSTAR closure)
\r
9415 InputSource *is, *is2 = NULL;
\r
9416 ChildProc *cp = (ChildProc *) pr;
\r
9418 is = (InputSource *) calloc(1, sizeof(InputSource));
\r
9419 is->lineByLine = lineByLine;
\r
9421 is->closure = closure;
\r
9422 is->second = NULL;
\r
9423 is->next = is->buf;
\r
9424 if (pr == NoProc) {
\r
9425 is->kind = CPReal;
\r
9426 consoleInputSource = is;
\r
9428 is->kind = cp->kind;
\r
9430 [AS] Try to avoid a race condition if the thread is given control too early:
\r
9431 we create all threads suspended so that the is->hThread variable can be
\r
9432 safely assigned, then let the threads start with ResumeThread.
\r
9434 switch (cp->kind) {
\r
9436 is->hFile = cp->hFrom;
\r
9437 cp->hFrom = NULL; /* now owned by InputThread */
\r
9439 CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) NonOvlInputThread,
\r
9440 (LPVOID) is, CREATE_SUSPENDED, &is->id);
\r
9444 is->hFile = cp->hFrom;
\r
9445 cp->hFrom = NULL; /* now owned by InputThread */
\r
9447 CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) InputThread,
\r
9448 (LPVOID) is, CREATE_SUSPENDED, &is->id);
\r
9452 is->sock = cp->sock;
\r
9454 CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,
\r
9455 (LPVOID) is, CREATE_SUSPENDED, &is->id);
\r
9459 is2 = (InputSource *) calloc(1, sizeof(InputSource));
\r
9461 is->sock = cp->sock;
\r
9463 is2->sock = cp->sock2;
\r
9464 is2->second = is2;
\r
9466 CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,
\r
9467 (LPVOID) is, CREATE_SUSPENDED, &is->id);
\r
9469 CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,
\r
9470 (LPVOID) is2, CREATE_SUSPENDED, &is2->id);
\r
9474 if( is->hThread != NULL ) {
\r
9475 ResumeThread( is->hThread );
\r
9478 if( is2 != NULL && is2->hThread != NULL ) {
\r
9479 ResumeThread( is2->hThread );
\r
9483 return (InputSourceRef) is;
\r
9487 RemoveInputSource(InputSourceRef isr)
\r
9491 is = (InputSource *) isr;
\r
9492 is->hThread = NULL; /* tell thread to stop */
\r
9493 CloseHandle(is->hThread);
\r
9494 if (is->second != NULL) {
\r
9495 is->second->hThread = NULL;
\r
9496 CloseHandle(is->second->hThread);
\r
9500 int no_wrap(char *message, int count)
\r
9502 ConsoleOutput(message, count, FALSE);
\r
9507 OutputToProcess(ProcRef pr, char *message, int count, int *outError)
\r
9510 int outCount = SOCKET_ERROR;
\r
9511 ChildProc *cp = (ChildProc *) pr;
\r
9512 static OVERLAPPED ovl;
\r
9513 static int line = 0;
\r
9517 if (appData.noJoin || !appData.useInternalWrap)
\r
9518 return no_wrap(message, count);
\r
9521 int width = get_term_width();
\r
9522 int len = wrap(NULL, message, count, width, &line);
\r
9523 char *msg = malloc(len);
\r
9527 return no_wrap(message, count);
\r
9530 dbgchk = wrap(msg, message, count, width, &line);
\r
9531 if (dbgchk != len && appData.debugMode)
\r
9532 fprintf(debugFP, "wrap(): dbgchk(%d) != len(%d)\n", dbgchk, len);
\r
9533 ConsoleOutput(msg, len, FALSE);
\r
9540 if (ovl.hEvent == NULL) {
\r
9541 ovl.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
\r
9543 ovl.Internal = ovl.InternalHigh = ovl.Offset = ovl.OffsetHigh = 0;
\r
9545 switch (cp->kind) {
\r
9548 outCount = send(cp->sock, message, count, 0);
\r
9549 if (outCount == SOCKET_ERROR) {
\r
9550 *outError = WSAGetLastError();
\r
9552 *outError = NO_ERROR;
\r
9557 if (WriteFile(((ChildProc *)pr)->hTo, message, count,
\r
9558 &dOutCount, NULL)) {
\r
9559 *outError = NO_ERROR;
\r
9560 outCount = (int) dOutCount;
\r
9562 *outError = GetLastError();
\r
9567 *outError = DoWriteFile(((ChildProc *)pr)->hTo, message, count,
\r
9568 &dOutCount, &ovl);
\r
9569 if (*outError == NO_ERROR) {
\r
9570 outCount = (int) dOutCount;
\r
9578 OutputToProcessDelayed(ProcRef pr, char *message, int count, int *outError,
\r
9581 /* Ignore delay, not implemented for WinBoard */
\r
9582 return OutputToProcess(pr, message, count, outError);
\r
9587 CmailSigHandlerCallBack(InputSourceRef isr, VOIDSTAR closure,
\r
9588 char *buf, int count, int error)
\r
9590 DisplayFatalError(_("Not implemented"), 0, 1);
\r
9593 /* see wgamelist.c for Game List functions */
\r
9594 /* see wedittags.c for Edit Tags functions */
\r
9601 char buf[MSG_SIZ];
\r
9604 if (SearchPath(installDir, appData.icsLogon, NULL, MSG_SIZ, buf, &dummy)) {
\r
9605 f = fopen(buf, "r");
\r
9607 ProcessICSInitScript(f);
\r
9615 StartAnalysisClock()
\r
9617 if (analysisTimerEvent) return;
\r
9618 analysisTimerEvent = SetTimer(hwndMain, (UINT) ANALYSIS_TIMER_ID,
\r
9619 (UINT) 2000, NULL);
\r
9623 SetHighlights(int fromX, int fromY, int toX, int toY)
\r
9625 highlightInfo.sq[0].x = fromX;
\r
9626 highlightInfo.sq[0].y = fromY;
\r
9627 highlightInfo.sq[1].x = toX;
\r
9628 highlightInfo.sq[1].y = toY;
\r
9634 highlightInfo.sq[0].x = highlightInfo.sq[0].y =
\r
9635 highlightInfo.sq[1].x = highlightInfo.sq[1].y = -1;
\r
9639 SetPremoveHighlights(int fromX, int fromY, int toX, int toY)
\r
9641 premoveHighlightInfo.sq[0].x = fromX;
\r
9642 premoveHighlightInfo.sq[0].y = fromY;
\r
9643 premoveHighlightInfo.sq[1].x = toX;
\r
9644 premoveHighlightInfo.sq[1].y = toY;
\r
9648 ClearPremoveHighlights()
\r
9650 premoveHighlightInfo.sq[0].x = premoveHighlightInfo.sq[0].y =
\r
9651 premoveHighlightInfo.sq[1].x = premoveHighlightInfo.sq[1].y = -1;
\r
9655 ShutDownFrontEnd()
\r
9657 if (saveSettingsOnExit) SaveSettings(settingsFileName);
\r
9658 DeleteClipboardTempFiles();
\r
9664 if (IsIconic(hwndMain))
\r
9665 ShowWindow(hwndMain, SW_RESTORE);
\r
9667 SetActiveWindow(hwndMain);
\r
9671 * Prototypes for animation support routines
\r
9673 static void ScreenSquare(int column, int row, POINT * pt);
\r
9674 static void Tween( POINT * start, POINT * mid, POINT * finish, int factor,
\r
9675 POINT frames[], int * nFrames);
\r
9681 AnimateAtomicCapture(Board board, int fromX, int fromY, int toX, int toY)
\r
9682 { // [HGM] atomic: animate blast wave
\r
9685 explodeInfo.fromX = fromX;
\r
9686 explodeInfo.fromY = fromY;
\r
9687 explodeInfo.toX = toX;
\r
9688 explodeInfo.toY = toY;
\r
9689 for(i=1; i<4*kFactor; i++) {
\r
9690 explodeInfo.radius = (i*180)/(4*kFactor-1);
\r
9691 DrawPosition(FALSE, board);
\r
9692 Sleep(appData.animSpeed);
\r
9694 explodeInfo.radius = 0;
\r
9695 DrawPosition(TRUE, board);
\r
9699 AnimateMove(board, fromX, fromY, toX, toY)
\r
9706 ChessSquare piece;
\r
9707 POINT start, finish, mid;
\r
9708 POINT frames[kFactor * 2 + 1];
\r
9711 if (!appData.animate) return;
\r
9712 if (doingSizing) return;
\r
9713 if (fromY < 0 || fromX < 0) return;
\r
9714 piece = board[fromY][fromX];
\r
9715 if (piece >= EmptySquare) return;
\r
9717 ScreenSquare(fromX, fromY, &start);
\r
9718 ScreenSquare(toX, toY, &finish);
\r
9720 /* All moves except knight jumps move in straight line */
\r
9721 if (!(abs(fromX-toX) == 1 && abs(fromY-toY) == 2 || abs(fromX-toX) == 2 && abs(fromY-toY) == 1)) {
\r
9722 mid.x = start.x + (finish.x - start.x) / 2;
\r
9723 mid.y = start.y + (finish.y - start.y) / 2;
\r
9725 /* Knight: make straight movement then diagonal */
\r
9726 if (abs(toY - fromY) < abs(toX - fromX)) {
\r
9727 mid.x = start.x + (finish.x - start.x) / 2;
\r
9731 mid.y = start.y + (finish.y - start.y) / 2;
\r
9735 /* Don't use as many frames for very short moves */
\r
9736 if (abs(toY - fromY) + abs(toX - fromX) <= 2)
\r
9737 Tween(&start, &mid, &finish, kFactor - 1, frames, &nFrames);
\r
9739 Tween(&start, &mid, &finish, kFactor, frames, &nFrames);
\r
9741 animInfo.from.x = fromX;
\r
9742 animInfo.from.y = fromY;
\r
9743 animInfo.to.x = toX;
\r
9744 animInfo.to.y = toY;
\r
9745 animInfo.lastpos = start;
\r
9746 animInfo.piece = piece;
\r
9747 for (n = 0; n < nFrames; n++) {
\r
9748 animInfo.pos = frames[n];
\r
9749 DrawPosition(FALSE, NULL);
\r
9750 animInfo.lastpos = animInfo.pos;
\r
9751 Sleep(appData.animSpeed);
\r
9753 animInfo.pos = finish;
\r
9754 DrawPosition(FALSE, NULL);
\r
9755 animInfo.piece = EmptySquare;
\r
9756 Explode(board, fromX, fromY, toX, toY);
\r
9759 /* Convert board position to corner of screen rect and color */
\r
9762 ScreenSquare(column, row, pt)
\r
9763 int column; int row; POINT * pt;
\r
9766 pt->x = lineGap + ((BOARD_WIDTH-1)-column) * (squareSize + lineGap);
\r
9767 pt->y = lineGap + row * (squareSize + lineGap);
\r
9769 pt->x = lineGap + column * (squareSize + lineGap);
\r
9770 pt->y = lineGap + ((BOARD_HEIGHT-1)-row) * (squareSize + lineGap);
\r
9774 /* Generate a series of frame coords from start->mid->finish.
\r
9775 The movement rate doubles until the half way point is
\r
9776 reached, then halves back down to the final destination,
\r
9777 which gives a nice slow in/out effect. The algorithmn
\r
9778 may seem to generate too many intermediates for short
\r
9779 moves, but remember that the purpose is to attract the
\r
9780 viewers attention to the piece about to be moved and
\r
9781 then to where it ends up. Too few frames would be less
\r
9785 Tween(start, mid, finish, factor, frames, nFrames)
\r
9786 POINT * start; POINT * mid;
\r
9787 POINT * finish; int factor;
\r
9788 POINT frames[]; int * nFrames;
\r
9790 int n, fraction = 1, count = 0;
\r
9792 /* Slow in, stepping 1/16th, then 1/8th, ... */
\r
9793 for (n = 0; n < factor; n++)
\r
9795 for (n = 0; n < factor; n++) {
\r
9796 frames[count].x = start->x + (mid->x - start->x) / fraction;
\r
9797 frames[count].y = start->y + (mid->y - start->y) / fraction;
\r
9799 fraction = fraction / 2;
\r
9803 frames[count] = *mid;
\r
9806 /* Slow out, stepping 1/2, then 1/4, ... */
\r
9808 for (n = 0; n < factor; n++) {
\r
9809 frames[count].x = finish->x - (finish->x - mid->x) / fraction;
\r
9810 frames[count].y = finish->y - (finish->y - mid->y) / fraction;
\r
9812 fraction = fraction * 2;
\r
9818 HistorySet( char movelist[][2*MOVE_LEN], int first, int last, int current )
\r
9820 MoveHistorySet( movelist, first, last, current, pvInfoList );
\r
9822 EvalGraphSet( first, last, current, pvInfoList );
\r
9826 SettingsPopUp(ChessProgramState *cps)
\r
9827 { // [HGM] wrapper needed because handles must not be passed through back-end
\r
9828 EngineOptionsPopup(savedHwnd, cps);
\r