2 * WinBoard.c -- Windows NT front end to XBoard
\r
4 * Copyright 1991 by Digital Equipment Corporation, Maynard,
\r
7 * Enhancements Copyright 1992-2001, 2002, 2003, 2004, 2005, 2006,
\r
8 * 2007, 2008, 2009, 2010 Free Software Foundation, Inc.
\r
10 * Enhancements Copyright 2005 Alessandro Scotti
\r
12 * XBoard borrows its colors and the bitmaps.xchess bitmap set from XChess,
\r
13 * which was written and is copyrighted by Wayne Christopher.
\r
15 * The following terms apply to Digital Equipment Corporation's copyright
\r
16 * interest in XBoard:
\r
17 * ------------------------------------------------------------------------
\r
18 * All Rights Reserved
\r
20 * Permission to use, copy, modify, and distribute this software and its
\r
21 * documentation for any purpose and without fee is hereby granted,
\r
22 * provided that the above copyright notice appear in all copies and that
\r
23 * both that copyright notice and this permission notice appear in
\r
24 * supporting documentation, and that the name of Digital not be
\r
25 * used in advertising or publicity pertaining to distribution of the
\r
26 * software without specific, written prior permission.
\r
28 * DIGITAL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
\r
29 * ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL
\r
30 * DIGITAL BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
\r
31 * ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
\r
32 * WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
\r
33 * ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
\r
35 * ------------------------------------------------------------------------
\r
37 * The following terms apply to the enhanced version of XBoard
\r
38 * distributed by the Free Software Foundation:
\r
39 * ------------------------------------------------------------------------
\r
41 * GNU XBoard is free software: you can redistribute it and/or modify
\r
42 * it under the terms of the GNU General Public License as published by
\r
43 * the Free Software Foundation, either version 3 of the License, or (at
\r
44 * your option) any later version.
\r
46 * GNU XBoard is distributed in the hope that it will be useful, but
\r
47 * WITHOUT ANY WARRANTY; without even the implied warranty of
\r
48 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
\r
49 * General Public License for more details.
\r
51 * You should have received a copy of the GNU General Public License
\r
52 * along with this program. If not, see http://www.gnu.org/licenses/. *
\r
54 *------------------------------------------------------------------------
\r
55 ** See the file ChangeLog for a revision history. */
\r
59 #include <windows.h>
\r
60 #include <winuser.h>
\r
61 #include <winsock.h>
\r
62 #include <commctrl.h>
\r
68 #include <sys/stat.h>
\r
71 #include <commdlg.h>
\r
73 #include <richedit.h>
\r
74 #include <mmsystem.h>
\r
83 #include "frontend.h"
\r
84 #include "backend.h"
\r
85 #include "winboard.h"
\r
87 #include "wclipbrd.h"
\r
88 #include "woptions.h"
\r
89 #include "wsockerr.h"
\r
90 #include "defaults.h"
\r
94 //void InitEngineUCI( const char * iniDir, ChessProgramState * cps );
\r
97 void mysrandom(unsigned int seed);
\r
99 extern int whiteFlag, blackFlag;
\r
100 Boolean flipClock = FALSE;
\r
101 extern HANDLE chatHandle[];
\r
102 extern int ics_type;
\r
104 void DisplayHoldingsCount(HDC hdc, int x, int y, int align, int copyNumber);
\r
105 VOID NewVariantPopup(HWND hwnd);
\r
106 int FinishMove P((ChessMove moveType, int fromX, int fromY, int toX, int toY,
\r
107 /*char*/int promoChar));
\r
108 void DisplayMove P((int moveNumber));
\r
109 Boolean ParseFEN P((Board board, int *blackPlaysFirst, char *fen));
\r
110 void ChatPopUp P((char *s));
\r
112 ChessSquare piece;
\r
113 POINT pos; /* window coordinates of current pos */
\r
114 POINT lastpos; /* window coordinates of last pos - used for clipping */
\r
115 POINT from; /* board coordinates of the piece's orig pos */
\r
116 POINT to; /* board coordinates of the piece's new pos */
\r
119 static AnimInfo animInfo = { EmptySquare, {-1,-1}, {-1,-1}, {-1,-1} };
\r
122 POINT start; /* window coordinates of start pos */
\r
123 POINT pos; /* window coordinates of current pos */
\r
124 POINT lastpos; /* window coordinates of last pos - used for clipping */
\r
125 POINT from; /* board coordinates of the piece's orig pos */
\r
128 static DragInfo dragInfo = { {-1,-1}, {-1,-1}, {-1,-1}, {-1,-1} };
\r
131 POINT sq[2]; /* board coordinates of from, to squares */
\r
134 static HighlightInfo highlightInfo = { {{-1, -1}, {-1, -1}} };
\r
135 static HighlightInfo premoveHighlightInfo = { {{-1, -1}, {-1, -1}} };
\r
136 static HighlightInfo partnerHighlightInfo = { {{-1, -1}, {-1, -1}} };
\r
137 static HighlightInfo oldPartnerHighlight = { {{-1, -1}, {-1, -1}} };
\r
139 typedef struct { // [HGM] atomic
\r
140 int fromX, fromY, toX, toY, radius;
\r
143 static ExplodeInfo explodeInfo;
\r
145 /* Window class names */
\r
146 char szAppName[] = "WinBoard";
\r
147 char szConsoleName[] = "WBConsole";
\r
149 /* Title bar text */
\r
150 char szTitle[] = "WinBoard";
\r
151 char szConsoleTitle[] = "I C S Interaction";
\r
154 char *settingsFileName;
\r
155 Boolean saveSettingsOnExit;
\r
156 char installDir[MSG_SIZ];
\r
157 int errorExitStatus;
\r
159 BoardSize boardSize;
\r
160 Boolean chessProgram;
\r
161 //static int boardX, boardY;
\r
162 int minX, minY; // [HGM] placement: volatile limits on upper-left corner
\r
163 int squareSize, lineGap, minorSize;
\r
164 static int winW, winH;
\r
165 static RECT messageRect, whiteRect, blackRect, leftLogoRect, rightLogoRect; // [HGM] logo
\r
166 static int logoHeight = 0;
\r
167 static char messageText[MESSAGE_TEXT_MAX];
\r
168 static int clockTimerEvent = 0;
\r
169 static int loadGameTimerEvent = 0;
\r
170 static int analysisTimerEvent = 0;
\r
171 static DelayedEventCallback delayedTimerCallback;
\r
172 static int delayedTimerEvent = 0;
\r
173 static int buttonCount = 2;
\r
174 char *icsTextMenuString;
\r
176 char *firstChessProgramNames;
\r
177 char *secondChessProgramNames;
\r
179 #define PALETTESIZE 256
\r
181 HINSTANCE hInst; /* current instance */
\r
182 Boolean alwaysOnTop = FALSE;
\r
184 COLORREF lightSquareColor, darkSquareColor, whitePieceColor,
\r
185 blackPieceColor, highlightSquareColor, premoveHighlightColor;
\r
187 ColorClass currentColorClass;
\r
189 static HWND savedHwnd;
\r
190 HWND hCommPort = NULL; /* currently open comm port */
\r
191 static HWND hwndPause; /* pause button */
\r
192 static HBITMAP pieceBitmap[3][(int) BlackPawn]; /* [HGM] nr of bitmaps referred to bP in stead of wK */
\r
193 static HBRUSH lightSquareBrush, darkSquareBrush,
\r
194 blackSquareBrush, /* [HGM] for band between board and holdings */
\r
195 explodeBrush, /* [HGM] atomic */
\r
196 markerBrush, /* [HGM] markers */
\r
197 whitePieceBrush, blackPieceBrush, iconBkgndBrush /*, outlineBrush*/;
\r
198 static POINT gridEndpoints[(BOARD_RANKS + BOARD_FILES + 2) * 2];
\r
199 static DWORD gridVertexCounts[BOARD_RANKS + BOARD_FILES + 2];
\r
200 static HPEN gridPen = NULL;
\r
201 static HPEN highlightPen = NULL;
\r
202 static HPEN premovePen = NULL;
\r
203 static NPLOGPALETTE pLogPal;
\r
204 static BOOL paletteChanged = FALSE;
\r
205 static HICON iconWhite, iconBlack, iconCurrent;
\r
206 static int doingSizing = FALSE;
\r
207 static int lastSizing = 0;
\r
208 static int prevStderrPort;
\r
209 static HBITMAP userLogo;
\r
211 static HBITMAP liteBackTexture = NULL;
\r
212 static HBITMAP darkBackTexture = NULL;
\r
213 static int liteBackTextureMode = BACK_TEXTURE_MODE_PLAIN;
\r
214 static int darkBackTextureMode = BACK_TEXTURE_MODE_PLAIN;
\r
215 static int backTextureSquareSize = 0;
\r
216 static struct { int x; int y; int mode; } backTextureSquareInfo[BOARD_RANKS][BOARD_FILES];
\r
218 #if __GNUC__ && !defined(_winmajor)
\r
219 #define oldDialog 0 /* cygwin doesn't define _winmajor; mingw does */
\r
221 #if defined(_winmajor)
\r
222 #define oldDialog (_winmajor < 4)
\r
224 #define oldDialog 0
\r
228 #define INTERNATIONAL
\r
230 #ifdef INTERNATIONAL
\r
231 # define _(s) T_(s)
\r
237 # define Translate(x, y)
\r
238 # define LoadLanguageFile(s)
\r
241 #ifdef INTERNATIONAL
\r
243 Boolean barbaric; // flag indicating if translation is needed
\r
245 // list of item numbers used in each dialog (used to alter language at run time)
\r
247 #define ABOUTBOX -1 /* not sure why these are needed */
\r
248 #define ABOUTBOX2 -1
\r
250 int dialogItems[][40] = {
\r
251 { ABOUTBOX, IDOK, 400 },
\r
252 { DLG_TimeControl, IDC_Babble, OPT_TCUseMoves, OPT_TCUseInc, OPT_TCUseFixed,
\r
253 OPT_TCtext1, OPT_TCtext2, OPT_TCitext1, OPT_TCitext2, OPT_TCftext, GPB_Factors, IDC_Factor1, IDC_Factor2, IDOK, IDCANCEL },
\r
254 { DLG_LoadOptions, OPT_Autostep, OPT_AStext1, IDOK, IDCANCEL },
\r
255 { DLG_SaveOptions, OPT_Autosave, OPT_AVPrompt, OPT_AVToFile, OPT_AVBrowse,
\r
256 801, OPT_PGN, OPT_Old, OPT_OutOfBookInfo, IDOK, IDCANCEL },
\r
257 { 1536, 1090, IDC_Directories, 1089, 1091, IDOK, IDCANCEL, 1038, IDC_IndexNr, 1037 },
\r
258 { DLG_CommPort, IDOK, IDCANCEL, IDC_Port, IDC_Rate, IDC_Bits, IDC_Parity,
\r
259 IDC_Stop, IDC_Flow, OPT_SerialHelp },
\r
260 { DLG_EditComment, IDOK, OPT_CancelComment, OPT_ClearComment, OPT_EditComment },
\r
261 { DLG_PromotionKing, PB_Chancellor, PB_Archbishop, PB_Queen, PB_Rook,
\r
262 PB_Bishop, PB_Knight, PB_King, IDCANCEL, IDC_Yes, IDC_No, IDC_Centaur },
\r
263 { ABOUTBOX2, IDC_ChessBoard },
\r
264 { DLG_GameList, OPT_GameListLoad, OPT_GameListPrev, OPT_GameListNext,
\r
265 OPT_GameListClose, IDC_GameListDoFilter },
\r
266 { DLG_EditTags, IDOK, OPT_TagsCancel, OPT_EditTags },
\r
267 { DLG_Error, IDOK },
\r
268 { DLG_Colorize, IDOK, IDCANCEL, OPT_ChooseColor, OPT_Bold, OPT_Italic,
\r
269 OPT_Underline, OPT_Strikeout, OPT_Sample },
\r
270 { DLG_Question, IDOK, IDCANCEL, OPT_QuestionText },
\r
271 { DLG_Startup, IDC_Welcome, OPT_ChessEngine, OPT_ChessServer, OPT_View,
\r
272 IDC_SPECIFY_ENG_STATIC, IDC_SPECIFY_SERVER_STATIC, OPT_AnyAdditional,
\r
273 IDOK, IDCANCEL, IDM_HELPCONTENTS },
\r
274 { DLG_IndexNumber, IDC_Index },
\r
275 { DLG_TypeInMove, IDOK, IDCANCEL },
\r
276 { DLG_TypeInName, IDOK, IDCANCEL },
\r
277 { DLG_Sound, IDC_Event, OPT_NoSound, OPT_DefaultBeep, OPT_BuiltInSound,
\r
278 OPT_WavFile, OPT_BrowseSound, OPT_DefaultSounds, IDOK, IDCANCEL, OPT_PlaySound },
\r
279 { DLG_GeneralOptions, IDOK, IDCANCEL, OPT_AlwaysOnTop, OPT_HighlightLastMove,
\r
280 OPT_AlwaysQueen, OPT_PeriodicUpdates, OPT_AnimateDragging, OPT_PonderNextMove,
\r
281 OPT_AnimateMoving, OPT_PopupExitMessage, OPT_AutoFlag, OPT_PopupMoveErrors,
\r
282 OPT_AutoFlipView, OPT_ShowButtonBar, OPT_AutoRaiseBoard, OPT_ShowCoordinates,
\r
283 OPT_Blindfold, OPT_ShowThinking, OPT_HighlightDragging, OPT_TestLegality,
\r
284 OPT_SaveExtPGN, OPT_HideThinkFromHuman, OPT_ExtraInfoInMoveHistory,
\r
285 OPT_HighlightMoveArrow, OPT_AutoLogo },
\r
286 { DLG_IcsOptions, IDOK, IDCANCEL, OPT_AutoComment, OPT_AutoKibitz, OPT_AutoObserve,
\r
287 OPT_GetMoveList, OPT_LocalLineEditing, OPT_QuietPlay, OPT_SeekGraph, OPT_AutoRefresh,
\r
288 OPT_BgObserve, OPT_DualBoard, OPT_Premove, OPT_PremoveWhite, OPT_PremoveBlack,
\r
289 OPT_SmartMove, OPT_IcsAlarm, IDC_Sec, OPT_ChooseShoutColor, OPT_ChooseSShoutColor,
\r
290 OPT_ChooseChannel1Color, OPT_ChooseChannelColor, OPT_ChooseKibitzColor,
\r
291 OPT_ChooseTellColor, OPT_ChooseChallengeColor, OPT_ChooseRequestColor,
\r
292 OPT_ChooseSeekColor, OPT_ChooseNormalColor, OPT_ChooseBackgroundColor,
\r
293 OPT_DefaultColors, OPT_DontColorize, IDC_Boxes, GPB_Colors, GPB_Premove,
\r
294 GPB_General, GPB_Alarm },
\r
295 { DLG_BoardOptions, IDOK, IDCANCEL, OPT_SizeTiny, OPT_SizeTeeny, OPT_SizeDinky,
\r
296 OPT_SizePetite, OPT_SizeSlim, OPT_SizeSmall, OPT_SizeMediocre, OPT_SizeMiddling,
\r
297 OPT_SizeAverage, OPT_SizeModerate, OPT_SizeMedium, OPT_SizeBulky, OPT_SizeLarge,
\r
298 OPT_SizeBig, OPT_SizeHuge, OPT_SizeGiant, OPT_SizeColossal, OPT_SizeTitanic,
\r
299 OPT_ChooseLightSquareColor, OPT_ChooseDarkSquareColor, OPT_ChooseWhitePieceColor,
\r
300 OPT_ChooseBlackPieceColor, OPT_ChooseHighlightSquareColor, OPT_ChoosePremoveHighlightColor,
\r
301 OPT_Monochrome, OPT_AllWhite, OPT_UpsideDown, OPT_DefaultBoardColors, GPB_Colors,
\r
302 IDC_Light, IDC_Dark, IDC_White, IDC_Black, IDC_High, IDC_PreHigh, GPB_Size },
\r
303 { DLG_NewVariant, IDOK, IDCANCEL, OPT_VariantNormal, OPT_VariantFRC, OPT_VariantWildcastle,
\r
304 OPT_VariantNocastle, OPT_VariantLosers, OPT_VariantGiveaway, OPT_VariantSuicide,
\r
305 OPT_Variant3Check, OPT_VariantTwoKings, OPT_VariantAtomic, OPT_VariantCrazyhouse,
\r
306 OPT_VariantBughouse, OPT_VariantTwilight, OPT_VariantShogi, OPT_VariantSuper,
\r
307 OPT_VariantKnightmate, OPT_VariantBerolina, OPT_VariantCylinder, OPT_VariantFairy,
\r
308 OPT_VariantMakruk, OPT_VariantGothic, OPT_VariantCapablanca, OPT_VariantJanus,
\r
309 OPT_VariantCRC, OPT_VariantFalcon, OPT_VariantCourier, OPT_VariantGreat, OPT_VariantSChess,
\r
310 OPT_VariantShatranj, OPT_VariantXiangqi, GPB_Variant, GPB_Board, IDC_Height,
\r
311 IDC_Width, IDC_Hand, IDC_Pieces, IDC_Def },
\r
312 { DLG_Fonts, IDOK, IDCANCEL, OPT_ChooseClockFont, OPT_ChooseMessageFont,
\r
313 OPT_ChooseCoordFont, OPT_ChooseTagFont, OPT_ChooseCommentsFont, OPT_ChooseConsoleFont, OPT_ChooseMoveHistoryFont, OPT_DefaultFonts,
\r
314 OPT_ClockFont, OPT_MessageFont, OPT_CoordFont, OPT_EditTagsFont,
\r
315 OPT_CommentsFont, OPT_MessageFont5, GPB_Current, GPB_All, OPT_MessageFont6 },
\r
316 { DLG_NewGameFRC, IDC_NFG_Label, IDC_NFG_Random, IDOK, IDCANCEL },
\r
317 { DLG_GameListOptions, IDC_GLT, IDC_GLT_Up, IDC_GLT_Down, IDC_GLT_Restore,
\r
318 IDC_GLT_Default, IDOK, IDCANCEL, IDC_GLT_RestoreTo },
\r
319 { DLG_MoveHistory },
\r
320 { DLG_EvalGraph },
\r
321 { DLG_EngineOutput, IDC_EngineLabel1, IDC_Engine1_NPS, IDC_EngineLabel2, IDC_Engine2_NPS },
\r
322 { DLG_Chat, IDC_Partner, IDC_Clear, IDC_Send, },
\r
323 { DLG_EnginePlayOptions, IDC_EpPonder, IDC_EpShowThinking, IDC_EpHideThinkingHuman,
\r
324 IDC_EpPeriodicUpdates, GPB_Adjudications, IDC_Draw, IDC_Moves, IDC_Threshold,
\r
325 IDC_Centi, IDC_TestClaims, IDC_DetectMates, IDC_MaterialDraws, IDC_TrivialDraws,
\r
326 GPB_Apply, IDC_Rule, IDC_Repeats, IDC_ScoreAbs1, IDC_ScoreAbs2, IDOK, IDCANCEL },
\r
327 { DLG_OptionsUCI, IDC_PolyDir, IDC_BrowseForPolyglotDir, IDC_Hash, IDC_Path,
\r
328 IDC_BrowseForEGTB, IDC_Cache, IDC_UseBook, IDC_BrowseForBook, IDC_CPU, IDC_OwnBook1,
\r
329 IDC_OwnBook2, IDC_Depth, IDC_Variation, IDC_DefGames, IDOK, IDCANCEL },
\r
333 static char languageBuf[50000], *foreign[1000], *english[1000], *languageFile[MSG_SIZ];
\r
334 static int lastChecked;
\r
335 static char oldLanguage[MSG_SIZ], *menuText[10][30];
\r
336 extern int tinyLayout;
\r
337 extern char * menuBarText[][8];
\r
340 LoadLanguageFile(char *name)
\r
341 { //load the file with translations, and make a list of the strings to be translated, and their translations
\r
343 int i=0, j=0, n=0, k;
\r
346 if(!name || name[0] == NULLCHAR) return;
\r
347 snprintf(buf, MSG_SIZ, "%s%s", name, strchr(name, '.') ? "" : ".lng"); // auto-append lng extension
\r
348 if(!strcmp(buf, oldLanguage)) { barbaric = 1; return; } // this language already loaded; just switch on
\r
349 if((f = fopen(buf, "r")) == NULL) return;
\r
350 while((k = fgetc(f)) != EOF) {
\r
351 if(i >= sizeof(languageBuf)) { DisplayError("Language file too big", 0); return; }
\r
352 languageBuf[i] = k;
\r
354 if(languageBuf[n] == '"' && languageBuf[i-1] == '"') {
\r
356 if(p = strstr(languageBuf + n + 1, "\" === \"")) {
\r
357 if(p > languageBuf+n+2 && p+8 < languageBuf+i) {
\r
358 if(j >= sizeof(english)) { DisplayError("Too many translated strings", 0); return; }
\r
359 english[j] = languageBuf + n + 1; *p = 0;
\r
360 foreign[j++] = p + 7; languageBuf[i-1] = 0;
\r
361 //if(appData.debugMode) fprintf(debugFP, "translation: replace '%s' by '%s'\n", english[j-1], foreign[j-1]);
\r
366 } else if(i > 0 && languageBuf[i-1] == '\\') {
\r
368 case 'n': k = '\n'; break;
\r
369 case 'r': k = '\r'; break;
\r
370 case 't': k = '\t'; break;
\r
372 languageBuf[--i] = k;
\r
377 barbaric = (j != 0);
\r
378 safeStrCpy(oldLanguage, buf, sizeof(oldLanguage)/sizeof(oldLanguage[0]) );
\r
383 { // return the translation of the given string
\r
384 // efficiency can be improved a lot...
\r
386 //if(appData.debugMode) fprintf(debugFP, "T_(%s)\n", s);
\r
387 if(!barbaric) return s;
\r
388 if(!s) return ""; // sanity
\r
389 while(english[i]) {
\r
390 if(!strcmp(s, english[i])) return foreign[i];
\r
397 Translate(HWND hDlg, int dialogID)
\r
398 { // translate all text items in the given dialog
\r
400 char buf[MSG_SIZ], *s;
\r
401 //if(appData.debugMode) fprintf(debugFP, "Translate(%d)\n", dialogID);
\r
402 if(!barbaric) return;
\r
403 while(dialogItems[i][0] && dialogItems[i][0] != dialogID) i++; // find the dialog description
\r
404 if(dialogItems[i][0] != dialogID) return; // unknown dialog, should not happen
\r
405 GetWindowText( hDlg, buf, MSG_SIZ );
\r
407 //if(appData.debugMode) fprintf(debugFP, "WindowText '%s' -> '%s'\n", buf, s);
\r
408 if(strcmp(buf, s)) SetWindowText(hDlg, s); // replace by translated string (if different)
\r
409 for(j=1; k=dialogItems[i][j]; j++) { // translate all listed dialog items
\r
410 GetDlgItemText(hDlg, k, buf, MSG_SIZ);
\r
411 if(strlen(buf) == 0) continue;
\r
413 if(strcmp(buf, s)) SetDlgItemText(hDlg, k, s); // replace by translated string (if different)
\r
418 TranslateMenus(int addLanguage)
\r
421 WIN32_FIND_DATA fileData;
\r
423 #define IDM_English 1895
\r
425 HMENU mainMenu = GetMenu(hwndMain);
\r
426 for (i=GetMenuItemCount(mainMenu)-1; i>=0; i--) {
\r
427 HMENU subMenu = GetSubMenu(mainMenu, i);
\r
428 ModifyMenu(mainMenu, i, MF_STRING|MF_BYPOSITION|MF_POPUP|EnableMenuItem(mainMenu, i, MF_BYPOSITION),
\r
429 (UINT) subMenu, T_(menuBarText[tinyLayout][i]));
\r
430 for(j=GetMenuItemCount(subMenu)-1; j>=0; j--){
\r
432 UINT k = GetMenuItemID(subMenu, j);
\r
434 safeStrCpy(buf, menuText[i][j], sizeof(buf)/sizeof(buf[0]) ); else {
\r
435 GetMenuString(subMenu, j, buf, MSG_SIZ, MF_BYPOSITION);
\r
436 menuText[i][j] = strdup(buf); // remember original on first change
\r
438 if(buf[0] == NULLCHAR) continue;
\r
439 //fprintf(debugFP, "menu(%d,%d) = %s (%08x, %08x) %d\n", i, j, buf, mainMenu, subMenu, k);
\r
440 ModifyMenu(subMenu, j, MF_STRING|MF_BYPOSITION
\r
441 |CheckMenuItem(subMenu, j, MF_BYPOSITION)
\r
442 |EnableMenuItem(subMenu, j, MF_BYPOSITION), k, T_(buf));
\r
445 DrawMenuBar(hwndMain);
\r
448 if(!addLanguage) return;
\r
449 if((hFind = FindFirstFile("*.LNG", &fileData)) != INVALID_HANDLE_VALUE) {
\r
450 HMENU mainMenu = GetMenu(hwndMain);
\r
451 HMENU subMenu = GetSubMenu(mainMenu, GetMenuItemCount(mainMenu)-1);
\r
452 AppendMenu(subMenu, MF_SEPARATOR, (UINT_PTR) 0, NULL);
\r
453 AppendMenu(subMenu, MF_ENABLED|MF_STRING|(barbaric?MF_UNCHECKED:MF_CHECKED), (UINT_PTR) IDM_English, (LPCTSTR) "English");
\r
454 i = 0; lastChecked = IDM_English;
\r
456 char *p, *q = fileData.cFileName;
\r
457 int checkFlag = MF_UNCHECKED;
\r
458 languageFile[i] = strdup(q);
\r
459 if(barbaric && !strcmp(oldLanguage, q)) {
\r
460 checkFlag = MF_CHECKED;
\r
461 lastChecked = IDM_English + i + 1;
\r
462 CheckMenuItem(mainMenu, IDM_English, MF_BYCOMMAND|MF_UNCHECKED);
\r
464 *q = ToUpper(*q); while(*++q) *q = ToLower(*q);
\r
465 p = strstr(fileData.cFileName, ".lng");
\r
467 AppendMenu(subMenu, MF_ENABLED|MF_STRING|checkFlag, (UINT_PTR) IDM_English + ++i, (LPCTSTR) fileData.cFileName);
\r
468 } while(FindNextFile(hFind, &fileData));
\r
481 int cliWidth, cliHeight;
\r
484 SizeInfo sizeInfo[] =
\r
486 { "tiny", 21, 0, 1, 1, 0, 0 },
\r
487 { "teeny", 25, 1, 1, 1, 0, 0 },
\r
488 { "dinky", 29, 1, 1, 1, 0, 0 },
\r
489 { "petite", 33, 1, 1, 1, 0, 0 },
\r
490 { "slim", 37, 2, 1, 0, 0, 0 },
\r
491 { "small", 40, 2, 1, 0, 0, 0 },
\r
492 { "mediocre", 45, 2, 1, 0, 0, 0 },
\r
493 { "middling", 49, 2, 0, 0, 0, 0 },
\r
494 { "average", 54, 2, 0, 0, 0, 0 },
\r
495 { "moderate", 58, 3, 0, 0, 0, 0 },
\r
496 { "medium", 64, 3, 0, 0, 0, 0 },
\r
497 { "bulky", 72, 3, 0, 0, 0, 0 },
\r
498 { "large", 80, 3, 0, 0, 0, 0 },
\r
499 { "big", 87, 3, 0, 0, 0, 0 },
\r
500 { "huge", 95, 3, 0, 0, 0, 0 },
\r
501 { "giant", 108, 3, 0, 0, 0, 0 },
\r
502 { "colossal", 116, 4, 0, 0, 0, 0 },
\r
503 { "titanic", 129, 4, 0, 0, 0, 0 },
\r
504 { NULL, 0, 0, 0, 0, 0, 0 }
\r
507 #define MF(x) {x, {{0,}, 0. }, {0, }, 0}
\r
508 MyFont fontRec[NUM_SIZES][NUM_FONTS] =
\r
510 { MF(CLOCK_FONT_TINY), MF(MESSAGE_FONT_TINY), MF(COORD_FONT_TINY), MF(CONSOLE_FONT_TINY), MF(COMMENT_FONT_TINY), MF(EDITTAGS_FONT_TINY), MF(MOVEHISTORY_FONT_ALL) },
\r
511 { MF(CLOCK_FONT_TEENY), MF(MESSAGE_FONT_TEENY), MF(COORD_FONT_TEENY), MF(CONSOLE_FONT_TEENY), MF(COMMENT_FONT_TEENY), MF(EDITTAGS_FONT_TEENY), MF(MOVEHISTORY_FONT_ALL) },
\r
512 { MF(CLOCK_FONT_DINKY), MF(MESSAGE_FONT_DINKY), MF(COORD_FONT_DINKY), MF(CONSOLE_FONT_DINKY), MF(COMMENT_FONT_DINKY), MF(EDITTAGS_FONT_DINKY), MF(MOVEHISTORY_FONT_ALL) },
\r
513 { MF(CLOCK_FONT_PETITE), MF(MESSAGE_FONT_PETITE), MF(COORD_FONT_PETITE), MF(CONSOLE_FONT_PETITE), MF(COMMENT_FONT_PETITE), MF(EDITTAGS_FONT_PETITE), MF(MOVEHISTORY_FONT_ALL) },
\r
514 { MF(CLOCK_FONT_SLIM), MF(MESSAGE_FONT_SLIM), MF(COORD_FONT_SLIM), MF(CONSOLE_FONT_SLIM), MF(COMMENT_FONT_SLIM), MF(EDITTAGS_FONT_SLIM), MF(MOVEHISTORY_FONT_ALL) },
\r
515 { MF(CLOCK_FONT_SMALL), MF(MESSAGE_FONT_SMALL), MF(COORD_FONT_SMALL), MF(CONSOLE_FONT_SMALL), MF(COMMENT_FONT_SMALL), MF(EDITTAGS_FONT_SMALL), MF(MOVEHISTORY_FONT_ALL) },
\r
516 { MF(CLOCK_FONT_MEDIOCRE), MF(MESSAGE_FONT_MEDIOCRE), MF(COORD_FONT_MEDIOCRE), MF(CONSOLE_FONT_MEDIOCRE), MF(COMMENT_FONT_MEDIOCRE), MF(EDITTAGS_FONT_MEDIOCRE), MF(MOVEHISTORY_FONT_ALL) },
\r
517 { MF(CLOCK_FONT_MIDDLING), MF(MESSAGE_FONT_MIDDLING), MF(COORD_FONT_MIDDLING), MF(CONSOLE_FONT_MIDDLING), MF(COMMENT_FONT_MIDDLING), MF(EDITTAGS_FONT_MIDDLING), MF(MOVEHISTORY_FONT_ALL) },
\r
518 { MF(CLOCK_FONT_AVERAGE), MF(MESSAGE_FONT_AVERAGE), MF(COORD_FONT_AVERAGE), MF(CONSOLE_FONT_AVERAGE), MF(COMMENT_FONT_AVERAGE), MF(EDITTAGS_FONT_AVERAGE), MF(MOVEHISTORY_FONT_ALL) },
\r
519 { MF(CLOCK_FONT_MODERATE), MF(MESSAGE_FONT_MODERATE), MF(COORD_FONT_MODERATE), MF(CONSOLE_FONT_MODERATE), MF(COMMENT_FONT_MODERATE), MF(EDITTAGS_FONT_MODERATE), MF(MOVEHISTORY_FONT_ALL) },
\r
520 { MF(CLOCK_FONT_MEDIUM), MF(MESSAGE_FONT_MEDIUM), MF(COORD_FONT_MEDIUM), MF(CONSOLE_FONT_MEDIUM), MF(COMMENT_FONT_MEDIUM), MF(EDITTAGS_FONT_MEDIUM), MF(MOVEHISTORY_FONT_ALL) },
\r
521 { MF(CLOCK_FONT_BULKY), MF(MESSAGE_FONT_BULKY), MF(COORD_FONT_BULKY), MF(CONSOLE_FONT_BULKY), MF(COMMENT_FONT_BULKY), MF(EDITTAGS_FONT_BULKY), MF(MOVEHISTORY_FONT_ALL) },
\r
522 { MF(CLOCK_FONT_LARGE), MF(MESSAGE_FONT_LARGE), MF(COORD_FONT_LARGE), MF(CONSOLE_FONT_LARGE), MF(COMMENT_FONT_LARGE), MF(EDITTAGS_FONT_LARGE), MF(MOVEHISTORY_FONT_ALL) },
\r
523 { MF(CLOCK_FONT_BIG), MF(MESSAGE_FONT_BIG), MF(COORD_FONT_BIG), MF(CONSOLE_FONT_BIG), MF(COMMENT_FONT_BIG), MF(EDITTAGS_FONT_BIG), MF(MOVEHISTORY_FONT_ALL) },
\r
524 { MF(CLOCK_FONT_HUGE), MF(MESSAGE_FONT_HUGE), MF(COORD_FONT_HUGE), MF(CONSOLE_FONT_HUGE), MF(COMMENT_FONT_HUGE), MF(EDITTAGS_FONT_HUGE), MF(MOVEHISTORY_FONT_ALL) },
\r
525 { MF(CLOCK_FONT_GIANT), MF(MESSAGE_FONT_GIANT), MF(COORD_FONT_GIANT), MF(CONSOLE_FONT_GIANT), MF(COMMENT_FONT_GIANT), MF(EDITTAGS_FONT_GIANT), MF(MOVEHISTORY_FONT_ALL) },
\r
526 { MF(CLOCK_FONT_COLOSSAL), MF(MESSAGE_FONT_COLOSSAL), MF(COORD_FONT_COLOSSAL), MF(CONSOLE_FONT_COLOSSAL), MF(COMMENT_FONT_COLOSSAL), MF(EDITTAGS_FONT_COLOSSAL), MF(MOVEHISTORY_FONT_ALL) },
\r
527 { MF(CLOCK_FONT_TITANIC), MF(MESSAGE_FONT_TITANIC), MF(COORD_FONT_TITANIC), MF(CONSOLE_FONT_TITANIC), MF(COMMENT_FONT_TITANIC), MF(EDITTAGS_FONT_TITANIC), MF(MOVEHISTORY_FONT_ALL) },
\r
530 MyFont *font[NUM_SIZES][NUM_FONTS];
\r
539 #define BUTTON_WIDTH (tinyLayout ? 16 : 32)
\r
540 #define N_BUTTONS 5
\r
542 MyButtonDesc buttonDesc[N_BUTTONS] =
\r
544 {"<<", IDM_ToStart, NULL, NULL},
\r
545 {"<", IDM_Backward, NULL, NULL},
\r
546 {"P", IDM_Pause, NULL, NULL},
\r
547 {">", IDM_Forward, NULL, NULL},
\r
548 {">>", IDM_ToEnd, NULL, NULL},
\r
551 int tinyLayout = 0, smallLayout = 0;
\r
552 #define MENU_BAR_ITEMS 7
\r
553 char *menuBarText[2][MENU_BAR_ITEMS+1] = {
\r
554 { N_("&File"), N_("&Mode"), N_("&Action"), N_("&Step"), N_("&Options"), N_("&Help"), NULL },
\r
555 { N_("&F"), N_("&M"), N_("&A"), N_("&S"), N_("&O"), N_("&H"), NULL },
\r
559 MySound sounds[(int)NSoundClasses];
\r
560 MyTextAttribs textAttribs[(int)NColorClasses];
\r
562 MyColorizeAttribs colorizeAttribs[] = {
\r
563 { (COLORREF)0, 0, N_("Shout Text") },
\r
564 { (COLORREF)0, 0, N_("SShout/CShout") },
\r
565 { (COLORREF)0, 0, N_("Channel 1 Text") },
\r
566 { (COLORREF)0, 0, N_("Channel Text") },
\r
567 { (COLORREF)0, 0, N_("Kibitz Text") },
\r
568 { (COLORREF)0, 0, N_("Tell Text") },
\r
569 { (COLORREF)0, 0, N_("Challenge Text") },
\r
570 { (COLORREF)0, 0, N_("Request Text") },
\r
571 { (COLORREF)0, 0, N_("Seek Text") },
\r
572 { (COLORREF)0, 0, N_("Normal Text") },
\r
573 { (COLORREF)0, 0, N_("None") }
\r
578 static char *commentTitle;
\r
579 static char *commentText;
\r
580 static int commentIndex;
\r
581 static Boolean editComment = FALSE;
\r
584 char errorTitle[MSG_SIZ];
\r
585 char errorMessage[2*MSG_SIZ];
\r
586 HWND errorDialog = NULL;
\r
587 BOOLEAN moveErrorMessageUp = FALSE;
\r
588 BOOLEAN consoleEcho = TRUE;
\r
589 CHARFORMAT consoleCF;
\r
590 COLORREF consoleBackgroundColor;
\r
592 char *programVersion;
\r
598 typedef int CPKind;
\r
607 SOCKET sock2; /* stderr socket for OpenRcmd */
\r
610 #define INPUT_SOURCE_BUF_SIZE 4096
\r
612 typedef struct _InputSource {
\r
619 char buf[INPUT_SOURCE_BUF_SIZE];
\r
623 InputCallback func;
\r
624 struct _InputSource *second; /* for stderr thread on CPRcmd */
\r
628 InputSource *consoleInputSource;
\r
633 VOID ConsoleOutput(char* data, int length, int forceVisible);
\r
634 VOID ConsoleCreate();
\r
636 ConsoleWndProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam);
\r
637 VOID ColorizeTextPopup(HWND hwnd, ColorClass cc);
\r
638 VOID PrintCommSettings(FILE *f, char *name, DCB *dcb);
\r
639 VOID ParseCommSettings(char *arg, DCB *dcb);
\r
641 StartupDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam);
\r
642 VOID APIENTRY MenuPopup(HWND hwnd, POINT pt, HMENU hmenu, UINT def);
\r
643 void ParseIcsTextMenu(char *icsTextMenuString);
\r
644 VOID PopUpMoveDialog(char firstchar);
\r
645 VOID PopUpNameDialog(char firstchar);
\r
646 VOID UpdateSampleText(HWND hDlg, int id, MyColorizeAttribs *mca);
\r
650 int GameListOptions();
\r
652 int dummy; // [HGM] for obsolete args
\r
654 HWND hwndMain = NULL; /* root window*/
\r
655 HWND hwndConsole = NULL;
\r
656 HWND commentDialog = NULL;
\r
657 HWND moveHistoryDialog = NULL;
\r
658 HWND evalGraphDialog = NULL;
\r
659 HWND engineOutputDialog = NULL;
\r
660 HWND gameListDialog = NULL;
\r
661 HWND editTagsDialog = NULL;
\r
663 int commentUp = FALSE;
\r
665 WindowPlacement wpMain;
\r
666 WindowPlacement wpConsole;
\r
667 WindowPlacement wpComment;
\r
668 WindowPlacement wpMoveHistory;
\r
669 WindowPlacement wpEvalGraph;
\r
670 WindowPlacement wpEngineOutput;
\r
671 WindowPlacement wpGameList;
\r
672 WindowPlacement wpTags;
\r
674 VOID EngineOptionsPopup(); // [HGM] settings
\r
676 VOID GothicPopUp(char *title, VariantClass variant);
\r
678 * Setting "frozen" should disable all user input other than deleting
\r
679 * the window. We do this while engines are initializing themselves.
\r
681 static int frozen = 0;
\r
682 static int oldMenuItemState[MENU_BAR_ITEMS];
\r
688 if (frozen) return;
\r
690 hmenu = GetMenu(hwndMain);
\r
691 for (i=0; i<MENU_BAR_ITEMS; i++) {
\r
692 oldMenuItemState[i] = EnableMenuItem(hmenu, i, MF_BYPOSITION|MF_GRAYED);
\r
694 DrawMenuBar(hwndMain);
\r
697 /* Undo a FreezeUI */
\r
703 if (!frozen) return;
\r
705 hmenu = GetMenu(hwndMain);
\r
706 for (i=0; i<MENU_BAR_ITEMS; i++) {
\r
707 EnableMenuItem(hmenu, i, MF_BYPOSITION|oldMenuItemState[i]);
\r
709 DrawMenuBar(hwndMain);
\r
712 /*static*/ int fromX = -1, fromY = -1, toX, toY; // [HGM] moved upstream, so JAWS can use them
\r
714 /* JAWS preparation patch (WinBoard for the sight impaired). Define required insertions as empty */
\r
720 #define JAWS_ALT_INTERCEPT
\r
721 #define JAWS_KB_NAVIGATION
\r
722 #define JAWS_MENU_ITEMS
\r
723 #define JAWS_SILENCE
\r
724 #define JAWS_REPLAY
\r
726 #define JAWS_COPYRIGHT
\r
727 #define JAWS_DELETE(X) X
\r
728 #define SAYMACHINEMOVE()
\r
732 /*---------------------------------------------------------------------------*\
\r
736 \*---------------------------------------------------------------------------*/
\r
739 WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
\r
740 LPSTR lpCmdLine, int nCmdShow)
\r
743 HANDLE hAccelMain, hAccelNoAlt, hAccelNoICS;
\r
744 // INITCOMMONCONTROLSEX ex;
\r
748 LoadLibrary("RICHED32.DLL");
\r
749 consoleCF.cbSize = sizeof(CHARFORMAT);
\r
751 if (!InitApplication(hInstance)) {
\r
754 if (!InitInstance(hInstance, nCmdShow, lpCmdLine)) {
\r
760 // InitCommonControlsEx(&ex);
\r
761 InitCommonControls();
\r
763 hAccelMain = LoadAccelerators (hInstance, szAppName);
\r
764 hAccelNoAlt = LoadAccelerators (hInstance, "NO_ALT");
\r
765 hAccelNoICS = LoadAccelerators( hInstance, "NO_ICS"); /* [AS] No Ctrl-V on ICS!!! */
\r
767 /* Acquire and dispatch messages until a WM_QUIT message is received. */
\r
769 while (GetMessage(&msg, /* message structure */
\r
770 NULL, /* handle of window receiving the message */
\r
771 0, /* lowest message to examine */
\r
772 0)) /* highest message to examine */
\r
775 if(msg.message == WM_CHAR && msg.wParam == '\t') {
\r
776 // [HGM] navigate: switch between all windows with tab
\r
777 HWND e1 = NULL, e2 = NULL, mh = NULL, hInput = NULL, hText = NULL;
\r
778 int i, currentElement = 0;
\r
780 // first determine what element of the chain we come from (if any)
\r
781 if(appData.icsActive) {
\r
782 hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
783 hText = GetDlgItem(hwndConsole, OPT_ConsoleText);
\r
785 if(engineOutputDialog && EngineOutputIsUp()) {
\r
786 e1 = GetDlgItem(engineOutputDialog, IDC_EngineMemo1);
\r
787 e2 = GetDlgItem(engineOutputDialog, IDC_EngineMemo2);
\r
789 if(moveHistoryDialog && MoveHistoryIsUp()) {
\r
790 mh = GetDlgItem(moveHistoryDialog, IDC_MoveHistory);
\r
792 if(msg.hwnd == hwndMain) currentElement = 7 ; else
\r
793 if(msg.hwnd == engineOutputDialog) currentElement = 2; else
\r
794 if(msg.hwnd == e1) currentElement = 2; else
\r
795 if(msg.hwnd == e2) currentElement = 3; else
\r
796 if(msg.hwnd == moveHistoryDialog) currentElement = 4; else
\r
797 if(msg.hwnd == mh) currentElement = 4; else
\r
798 if(msg.hwnd == evalGraphDialog) currentElement = 6; else
\r
799 if(msg.hwnd == hText) currentElement = 5; else
\r
800 if(msg.hwnd == hInput) currentElement = 6; else
\r
801 for (i = 0; i < N_BUTTONS; i++) {
\r
802 if (buttonDesc[i].hwnd == msg.hwnd) { currentElement = 1; break; }
\r
805 // determine where to go to
\r
806 if(currentElement) { HWND h = NULL; int direction = GetKeyState(VK_SHIFT) < 0 ? -1 : 1;
\r
808 currentElement = (currentElement + direction) % 7;
\r
809 switch(currentElement) {
\r
811 h = hwndMain; break; // passing this case always makes the loop exit
\r
813 h = buttonDesc[0].hwnd; break; // could be NULL
\r
815 if(!EngineOutputIsUp()) continue; // skip closed auxiliary windows
\r
818 if(!EngineOutputIsUp()) continue;
\r
821 if(!MoveHistoryIsUp()) continue;
\r
823 // case 6: // input to eval graph does not seem to get here!
\r
824 // if(!EvalGraphIsUp()) continue;
\r
825 // h = evalGraphDialog; break;
\r
827 if(!appData.icsActive) continue;
\r
831 if(!appData.icsActive) continue;
\r
837 if(currentElement > 4 && IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);
\r
838 if(currentElement < 5 && IsIconic(hwndMain)) ShowWindow(hwndMain, SW_RESTORE); // all open together
\r
841 continue; // this message now has been processed
\r
845 if (!(commentDialog && IsDialogMessage(commentDialog, &msg)) &&
\r
846 !(moveHistoryDialog && IsDialogMessage(moveHistoryDialog, &msg)) &&
\r
847 !(evalGraphDialog && IsDialogMessage(evalGraphDialog, &msg)) &&
\r
848 !(engineOutputDialog && IsDialogMessage(engineOutputDialog, &msg)) &&
\r
849 !(editTagsDialog && IsDialogMessage(editTagsDialog, &msg)) &&
\r
850 !(gameListDialog && IsDialogMessage(gameListDialog, &msg)) &&
\r
851 !(errorDialog && IsDialogMessage(errorDialog, &msg)) &&
\r
852 !(!frozen && TranslateAccelerator(hwndMain, hAccelMain, &msg)) && JAWS_ACCEL
\r
853 !(!hwndConsole && TranslateAccelerator(hwndMain, hAccelNoICS, &msg)) &&
\r
854 !(!hwndConsole && TranslateAccelerator(hwndMain, hAccelNoAlt, &msg))) {
\r
855 int done = 0, i; // [HGM] chat: dispatch cat-box messages
\r
856 for(i=0; i<MAX_CHAT; i++)
\r
857 if(chatHandle[i] && IsDialogMessage(chatHandle[i], &msg)) {
\r
860 if(done) continue; // [HGM] chat: end patch
\r
861 TranslateMessage(&msg); /* Translates virtual key codes */
\r
862 DispatchMessage(&msg); /* Dispatches message to window */
\r
867 return (msg.wParam); /* Returns the value from PostQuitMessage */
\r
870 /*---------------------------------------------------------------------------*\
\r
872 * Initialization functions
\r
874 \*---------------------------------------------------------------------------*/
\r
878 { // update user logo if necessary
\r
879 static char oldUserName[MSG_SIZ], *curName;
\r
881 if(appData.autoLogo) {
\r
882 curName = UserName();
\r
883 if(strcmp(curName, oldUserName)) {
\r
884 snprintf(oldUserName, MSG_SIZ, "logos\\%s.bmp", curName);
\r
885 userLogo = LoadImage( 0, oldUserName, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );
\r
886 safeStrCpy(oldUserName, curName, sizeof(oldUserName)/sizeof(oldUserName[0]) );
\r
892 InitApplication(HINSTANCE hInstance)
\r
896 /* Fill in window class structure with parameters that describe the */
\r
899 wc.style = CS_HREDRAW | CS_VREDRAW; /* Class style(s). */
\r
900 wc.lpfnWndProc = (WNDPROC)WndProc; /* Window Procedure */
\r
901 wc.cbClsExtra = 0; /* No per-class extra data. */
\r
902 wc.cbWndExtra = 0; /* No per-window extra data. */
\r
903 wc.hInstance = hInstance; /* Owner of this class */
\r
904 wc.hIcon = LoadIcon(hInstance, "icon_white");
\r
905 wc.hCursor = LoadCursor(NULL, IDC_ARROW); /* Cursor */
\r
906 wc.hbrBackground = (HBRUSH)(COLOR_WINDOW+1); /* Default color */
\r
907 wc.lpszMenuName = szAppName; /* Menu name from .RC */
\r
908 wc.lpszClassName = szAppName; /* Name to register as */
\r
910 /* Register the window class and return success/failure code. */
\r
911 if (!RegisterClass(&wc)) return FALSE;
\r
913 wc.style = CS_HREDRAW | CS_VREDRAW;
\r
914 wc.lpfnWndProc = (WNDPROC)ConsoleWndProc;
\r
916 wc.cbWndExtra = DLGWINDOWEXTRA;
\r
917 wc.hInstance = hInstance;
\r
918 wc.hIcon = LoadIcon(hInstance, "icon_white");
\r
919 wc.hCursor = LoadCursor(NULL, IDC_ARROW);
\r
920 wc.hbrBackground = (HBRUSH)(COLOR_MENU+1);
\r
921 wc.lpszMenuName = NULL;
\r
922 wc.lpszClassName = szConsoleName;
\r
924 if (!RegisterClass(&wc)) return FALSE;
\r
929 /* Set by InitInstance, used by EnsureOnScreen */
\r
930 int screenHeight, screenWidth;
\r
933 EnsureOnScreen(int *x, int *y, int minX, int minY)
\r
935 // int gap = GetSystemMetrics(SM_CYFRAME) + GetSystemMetrics(SM_CYCAPTION);
\r
936 /* Be sure window at (x,y) is not off screen (or even mostly off screen) */
\r
937 if (*x > screenWidth - 32) *x = 0;
\r
938 if (*y > screenHeight - 32) *y = 0;
\r
939 if (*x < minX) *x = minX;
\r
940 if (*y < minY) *y = minY;
\r
944 InitInstance(HINSTANCE hInstance, int nCmdShow, LPSTR lpCmdLine)
\r
946 HWND hwnd; /* Main window handle. */
\r
948 WINDOWPLACEMENT wp;
\r
951 hInst = hInstance; /* Store instance handle in our global variable */
\r
952 programName = szAppName;
\r
954 if (SearchPath(NULL, "WinBoard.exe", NULL, MSG_SIZ, installDir, &filepart)) {
\r
955 *filepart = NULLCHAR;
\r
957 GetCurrentDirectory(MSG_SIZ, installDir);
\r
959 gameInfo.boardWidth = gameInfo.boardHeight = 8; // [HGM] won't have open window otherwise
\r
960 screenWidth = screenHeight = 1000; // [HGM] placement: kludge to allow calling EnsureOnScreen from InitAppData
\r
961 InitAppData(lpCmdLine); /* Get run-time parameters */
\r
962 /* xboard, and older WinBoards, controlled the move sound with the
\r
963 appData.ringBellAfterMoves option. In the current WinBoard, we
\r
964 always turn the option on (so that the backend will call us),
\r
965 then let the user turn the sound off by setting it to silence if
\r
966 desired. To accommodate old winboard.ini files saved by old
\r
967 versions of WinBoard, we also turn off the sound if the option
\r
968 was initially set to false. [HGM] taken out of InitAppData */
\r
969 if (!appData.ringBellAfterMoves) {
\r
970 sounds[(int)SoundMove].name = strdup("");
\r
971 appData.ringBellAfterMoves = TRUE;
\r
973 if (appData.debugMode) {
\r
974 debugFP = fopen(appData.nameOfDebugFile, "w");
\r
975 setbuf(debugFP, NULL);
\r
978 LoadLanguageFile(appData.language);
\r
982 // InitEngineUCI( installDir, &first ); // [HGM] incorporated in InitBackEnd1()
\r
983 // InitEngineUCI( installDir, &second );
\r
985 /* Create a main window for this application instance. */
\r
986 hwnd = CreateWindow(szAppName, szTitle,
\r
987 (WS_OVERLAPPEDWINDOW & ~WS_MAXIMIZEBOX),
\r
988 CW_USEDEFAULT, 0, CW_USEDEFAULT, 0,
\r
989 NULL, NULL, hInstance, NULL);
\r
992 /* If window could not be created, return "failure" */
\r
997 /* [HGM] logo: Load logos if specified (must be done before InitDrawingSizes) */
\r
998 if( appData.firstLogo && appData.firstLogo[0] != NULLCHAR) {
\r
999 first.programLogo = LoadImage( 0, appData.firstLogo, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );
\r
1001 if (first.programLogo == NULL && appData.debugMode) {
\r
1002 fprintf( debugFP, "Unable to load logo bitmap '%s'\n", appData.firstLogo );
\r
1004 } else if(appData.autoLogo) {
\r
1005 if(appData.firstDirectory && appData.firstDirectory[0]) {
\r
1006 char buf[MSG_SIZ];
\r
1007 snprintf(buf, MSG_SIZ, "%s/logo.bmp", appData.firstDirectory);
\r
1008 first.programLogo = LoadImage( 0, buf, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );
\r
1012 if( appData.secondLogo && appData.secondLogo[0] != NULLCHAR) {
\r
1013 second.programLogo = LoadImage( 0, appData.secondLogo, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );
\r
1015 if (second.programLogo == NULL && appData.debugMode) {
\r
1016 fprintf( debugFP, "Unable to load logo bitmap '%s'\n", appData.secondLogo );
\r
1018 } else if(appData.autoLogo) {
\r
1019 char buf[MSG_SIZ];
\r
1020 if(appData.icsActive) { // [HGM] logo: in ICS mode second can be used for ICS
\r
1021 snprintf(buf, MSG_SIZ, "logos\\%s.bmp", appData.icsHost);
\r
1022 second.programLogo = LoadImage( 0, buf, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );
\r
1024 if(appData.secondDirectory && appData.secondDirectory[0]) {
\r
1025 snprintf(buf, MSG_SIZ, "%s\\logo.bmp", appData.secondDirectory);
\r
1026 second.programLogo = LoadImage( 0, buf, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );
\r
1032 iconWhite = LoadIcon(hInstance, "icon_white");
\r
1033 iconBlack = LoadIcon(hInstance, "icon_black");
\r
1034 iconCurrent = iconWhite;
\r
1035 InitDrawingColors();
\r
1036 screenHeight = GetSystemMetrics(SM_CYSCREEN);
\r
1037 screenWidth = GetSystemMetrics(SM_CXSCREEN);
\r
1038 for (ibs = (int) NUM_SIZES - 1; ibs >= 0; ibs--) {
\r
1039 /* Compute window size for each board size, and use the largest
\r
1040 size that fits on this screen as the default. */
\r
1041 InitDrawingSizes((BoardSize)(ibs+1000), 0);
\r
1042 if (boardSize == (BoardSize)-1 &&
\r
1043 winH <= screenHeight
\r
1044 - GetSystemMetrics(SM_CYFRAME) - GetSystemMetrics(SM_CYCAPTION) - 10
\r
1045 && winW <= screenWidth) {
\r
1046 boardSize = (BoardSize)ibs;
\r
1050 InitDrawingSizes(boardSize, 0);
\r
1051 TranslateMenus(1);
\r
1053 buttonCount = GetSystemMetrics(SM_CMOUSEBUTTONS);
\r
1055 /* [AS] Load textures if specified */
\r
1056 ZeroMemory( &backTextureSquareInfo, sizeof(backTextureSquareInfo) );
\r
1058 if( appData.liteBackTextureFile && appData.liteBackTextureFile[0] != NULLCHAR && appData.liteBackTextureFile[0] != '*' ) {
\r
1059 liteBackTexture = LoadImage( 0, appData.liteBackTextureFile, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );
\r
1060 liteBackTextureMode = appData.liteBackTextureMode;
\r
1062 if (liteBackTexture == NULL && appData.debugMode) {
\r
1063 fprintf( debugFP, "Unable to load lite texture bitmap '%s'\n", appData.liteBackTextureFile );
\r
1067 if( appData.darkBackTextureFile && appData.darkBackTextureFile[0] != NULLCHAR && appData.darkBackTextureFile[0] != '*' ) {
\r
1068 darkBackTexture = LoadImage( 0, appData.darkBackTextureFile, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );
\r
1069 darkBackTextureMode = appData.darkBackTextureMode;
\r
1071 if (darkBackTexture == NULL && appData.debugMode) {
\r
1072 fprintf( debugFP, "Unable to load dark texture bitmap '%s'\n", appData.darkBackTextureFile );
\r
1076 mysrandom( (unsigned) time(NULL) );
\r
1078 /* [AS] Restore layout */
\r
1079 if( wpMoveHistory.visible ) {
\r
1080 MoveHistoryPopUp();
\r
1083 if( wpEvalGraph.visible ) {
\r
1087 if( wpEngineOutput.visible ) {
\r
1088 EngineOutputPopUp();
\r
1091 /* Make the window visible; update its client area; and return "success" */
\r
1092 EnsureOnScreen(&wpMain.x, &wpMain.y, minX, minY);
\r
1093 wp.length = sizeof(WINDOWPLACEMENT);
\r
1095 wp.showCmd = nCmdShow;
\r
1096 wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;
\r
1097 wp.rcNormalPosition.left = wpMain.x;
\r
1098 wp.rcNormalPosition.right = wpMain.x + wpMain.width;
\r
1099 wp.rcNormalPosition.top = wpMain.y;
\r
1100 wp.rcNormalPosition.bottom = wpMain.y + wpMain.height;
\r
1101 SetWindowPlacement(hwndMain, &wp);
\r
1103 InitBackEnd2(); // [HGM] moved until after all windows placed, to save correct position if fatal error on engine start
\r
1105 if(!appData.noGUI) SetWindowPos(hwndMain, alwaysOnTop ? HWND_TOPMOST : HWND_NOTOPMOST,
\r
1106 0, 0, 0, 0, SWP_NOMOVE|SWP_NOSIZE);
\r
1108 if (hwndConsole) {
\r
1110 SetWindowPos(hwndConsole, alwaysOnTop ? HWND_TOPMOST : HWND_NOTOPMOST,
\r
1111 0, 0, 0, 0, SWP_NOMOVE|SWP_NOSIZE);
\r
1113 ShowWindow(hwndConsole, nCmdShow);
\r
1114 if(appData.chatBoxes) { // [HGM] chat: open chat boxes
\r
1115 char buf[MSG_SIZ], *p = buf, *q;
\r
1116 safeStrCpy(buf, appData.chatBoxes, sizeof(buf)/sizeof(buf[0]) );
\r
1118 q = strchr(p, ';');
\r
1120 if(*p) ChatPopUp(p);
\r
1123 SetActiveWindow(hwndConsole);
\r
1125 if(!appData.noGUI) UpdateWindow(hwnd); else ShowWindow(hwnd, SW_MINIMIZE);
\r
1126 if(gameListDialog) SetFocus(gameListDialog); // [HGM] jaws: for if we clicked multi-game game file
\r
1135 HMENU hmenu = GetMenu(hwndMain);
\r
1137 (void) EnableMenuItem(hmenu, IDM_CommPort,
\r
1138 MF_BYCOMMAND|((appData.icsActive &&
\r
1139 *appData.icsCommPort != NULLCHAR) ?
\r
1140 MF_ENABLED : MF_GRAYED));
\r
1141 (void) CheckMenuItem(hmenu, IDM_SaveSettingsOnExit,
\r
1142 MF_BYCOMMAND|(saveSettingsOnExit ?
\r
1143 MF_CHECKED : MF_UNCHECKED));
\r
1146 //---------------------------------------------------------------------------------------------------------
\r
1148 #define ICS_TEXT_MENU_SIZE (IDM_CommandXLast - IDM_CommandX + 1)
\r
1149 #define XBOARD FALSE
\r
1151 #define OPTCHAR "/"
\r
1152 #define SEPCHAR "="
\r
1156 // front-end part of option handling
\r
1159 LFfromMFP(LOGFONT* lf, MyFontParams *mfp)
\r
1161 HDC hdc = CreateDC("DISPLAY", NULL, NULL, NULL);
\r
1162 lf->lfHeight = -(int)(mfp->pointSize * GetDeviceCaps(hdc, LOGPIXELSY) / 72.0 + 0.5);
\r
1165 lf->lfEscapement = 0;
\r
1166 lf->lfOrientation = 0;
\r
1167 lf->lfWeight = mfp->bold ? FW_BOLD : FW_NORMAL;
\r
1168 lf->lfItalic = mfp->italic;
\r
1169 lf->lfUnderline = mfp->underline;
\r
1170 lf->lfStrikeOut = mfp->strikeout;
\r
1171 lf->lfCharSet = mfp->charset;
\r
1172 lf->lfOutPrecision = OUT_DEFAULT_PRECIS;
\r
1173 lf->lfClipPrecision = CLIP_DEFAULT_PRECIS;
\r
1174 lf->lfQuality = DEFAULT_QUALITY;
\r
1175 lf->lfPitchAndFamily = DEFAULT_PITCH|FF_DONTCARE;
\r
1176 safeStrCpy(lf->lfFaceName, mfp->faceName, sizeof(lf->lfFaceName)/sizeof(lf->lfFaceName[0]) );
\r
1180 CreateFontInMF(MyFont *mf)
\r
1182 LFfromMFP(&mf->lf, &mf->mfp);
\r
1183 if (mf->hf) DeleteObject(mf->hf);
\r
1184 mf->hf = CreateFontIndirect(&mf->lf);
\r
1187 // [HGM] This platform-dependent table provides the location for storing the color info
\r
1189 colorVariable[] = {
\r
1190 &whitePieceColor,
\r
1191 &blackPieceColor,
\r
1192 &lightSquareColor,
\r
1193 &darkSquareColor,
\r
1194 &highlightSquareColor,
\r
1195 &premoveHighlightColor,
\r
1197 &consoleBackgroundColor,
\r
1198 &appData.fontForeColorWhite,
\r
1199 &appData.fontBackColorWhite,
\r
1200 &appData.fontForeColorBlack,
\r
1201 &appData.fontBackColorBlack,
\r
1202 &appData.evalHistColorWhite,
\r
1203 &appData.evalHistColorBlack,
\r
1204 &appData.highlightArrowColor,
\r
1207 /* Command line font name parser. NULL name means do nothing.
\r
1208 Syntax like "Courier New:10.0 bi" or "Arial:10" or "Arial:10b"
\r
1209 For backward compatibility, syntax without the colon is also
\r
1210 accepted, but font names with digits in them won't work in that case.
\r
1213 ParseFontName(char *name, MyFontParams *mfp)
\r
1216 if (name == NULL) return;
\r
1218 q = strchr(p, ':');
\r
1220 if (q - p >= sizeof(mfp->faceName))
\r
1221 ExitArgError(_("Font name too long:"), name);
\r
1222 memcpy(mfp->faceName, p, q - p);
\r
1223 mfp->faceName[q - p] = NULLCHAR;
\r
1226 q = mfp->faceName;
\r
1227 while (*p && !isdigit(*p)) {
\r
1229 if (q - mfp->faceName >= sizeof(mfp->faceName))
\r
1230 ExitArgError(_("Font name too long:"), name);
\r
1232 while (q > mfp->faceName && q[-1] == ' ') q--;
\r
1235 if (!*p) ExitArgError(_("Font point size missing:"), name);
\r
1236 mfp->pointSize = (float) atof(p);
\r
1237 mfp->bold = (strchr(p, 'b') != NULL);
\r
1238 mfp->italic = (strchr(p, 'i') != NULL);
\r
1239 mfp->underline = (strchr(p, 'u') != NULL);
\r
1240 mfp->strikeout = (strchr(p, 's') != NULL);
\r
1241 mfp->charset = DEFAULT_CHARSET;
\r
1242 q = strchr(p, 'c');
\r
1244 mfp->charset = (BYTE) atoi(q+1);
\r
1248 ParseFont(char *name, int number)
\r
1249 { // wrapper to shield back-end from 'font'
\r
1250 ParseFontName(name, &font[boardSize][number]->mfp);
\r
1255 { // in WB we have a 2D array of fonts; this initializes their description
\r
1257 /* Point font array elements to structures and
\r
1258 parse default font names */
\r
1259 for (i=0; i<NUM_FONTS; i++) {
\r
1260 for (j=0; j<NUM_SIZES; j++) {
\r
1261 font[j][i] = &fontRec[j][i];
\r
1262 ParseFontName(font[j][i]->def, &font[j][i]->mfp);
\r
1269 { // here we create the actual fonts from the selected descriptions
\r
1271 for (i=0; i<NUM_FONTS; i++) {
\r
1272 for (j=0; j<NUM_SIZES; j++) {
\r
1273 CreateFontInMF(font[j][i]);
\r
1277 /* Color name parser.
\r
1278 X version accepts X color names, but this one
\r
1279 handles only the #rrggbb form (hex) or rrr,ggg,bbb (decimal) */
\r
1281 ParseColorName(char *name)
\r
1283 int red, green, blue, count;
\r
1284 char buf[MSG_SIZ];
\r
1286 count = sscanf(name, "#%2x%2x%2x", &red, &green, &blue);
\r
1288 count = sscanf(name, "%3d%*[^0-9]%3d%*[^0-9]%3d",
\r
1289 &red, &green, &blue);
\r
1292 snprintf(buf, MSG_SIZ, _("Can't parse color name %s"), name);
\r
1293 DisplayError(buf, 0);
\r
1294 return RGB(0, 0, 0);
\r
1296 return PALETTERGB(red, green, blue);
\r
1300 ParseColor(int n, char *name)
\r
1301 { // for WinBoard the color is an int, which needs to be derived from the string
\r
1302 if(colorVariable[n]) *(int*)colorVariable[n] = ParseColorName(name);
\r
1306 ParseAttribs(COLORREF *color, int *effects, char* argValue)
\r
1308 char *e = argValue;
\r
1312 if (*e == 'b') eff |= CFE_BOLD;
\r
1313 else if (*e == 'i') eff |= CFE_ITALIC;
\r
1314 else if (*e == 'u') eff |= CFE_UNDERLINE;
\r
1315 else if (*e == 's') eff |= CFE_STRIKEOUT;
\r
1316 else if (*e == '#' || isdigit(*e)) break;
\r
1320 *color = ParseColorName(e);
\r
1324 ParseTextAttribs(ColorClass cc, char *s)
\r
1325 { // [HGM] front-end wrapper that does the platform-dependent call
\r
1326 // for XBoard we would set (&appData.colorShout)[cc] = strdup(s);
\r
1327 ParseAttribs(&textAttribs[cc].color, &textAttribs[cc].effects, s);
\r
1331 ParseBoardSize(void *addr, char *name)
\r
1332 { // [HGM] rewritten with return-value ptr to shield back-end from BoardSize
\r
1333 BoardSize bs = SizeTiny;
\r
1334 while (sizeInfo[bs].name != NULL) {
\r
1335 if (StrCaseCmp(name, sizeInfo[bs].name) == 0) {
\r
1336 *(BoardSize *)addr = bs;
\r
1341 ExitArgError(_("Unrecognized board size value"), name);
\r
1346 { // [HGM] import name from appData first
\r
1349 for (cc = (ColorClass)0; cc < ColorNormal; cc++) {
\r
1350 textAttribs[cc].sound.name = strdup((&appData.soundShout)[cc]);
\r
1351 textAttribs[cc].sound.data = NULL;
\r
1352 MyLoadSound(&textAttribs[cc].sound);
\r
1354 for (cc = ColorNormal; cc < NColorClasses; cc++) {
\r
1355 textAttribs[cc].sound.name = strdup("");
\r
1356 textAttribs[cc].sound.data = NULL;
\r
1358 for (sc = (SoundClass)0; sc < NSoundClasses; sc++) {
\r
1359 sounds[sc].name = strdup((&appData.soundMove)[sc]);
\r
1360 sounds[sc].data = NULL;
\r
1361 MyLoadSound(&sounds[sc]);
\r
1366 SetCommPortDefaults()
\r
1368 memset(&dcb, 0, sizeof(DCB)); // required by VS 2002 +
\r
1369 dcb.DCBlength = sizeof(DCB);
\r
1370 dcb.BaudRate = 9600;
\r
1371 dcb.fBinary = TRUE;
\r
1372 dcb.fParity = FALSE;
\r
1373 dcb.fOutxCtsFlow = FALSE;
\r
1374 dcb.fOutxDsrFlow = FALSE;
\r
1375 dcb.fDtrControl = DTR_CONTROL_ENABLE;
\r
1376 dcb.fDsrSensitivity = FALSE;
\r
1377 dcb.fTXContinueOnXoff = TRUE;
\r
1378 dcb.fOutX = FALSE;
\r
1380 dcb.fNull = FALSE;
\r
1381 dcb.fRtsControl = RTS_CONTROL_ENABLE;
\r
1382 dcb.fAbortOnError = FALSE;
\r
1384 dcb.Parity = SPACEPARITY;
\r
1385 dcb.StopBits = ONESTOPBIT;
\r
1388 // [HGM] args: these three cases taken out to stay in front-end
\r
1390 SaveFontArg(FILE *f, ArgDescriptor *ad)
\r
1391 { // in WinBoard every board size has its own font, and the "argLoc" identifies the table,
\r
1392 // while the curent board size determines the element. This system should be ported to XBoard.
\r
1393 // What the table contains pointers to, and how to print the font description, remains platform-dependent
\r
1395 for (bs=0; bs<NUM_SIZES; bs++) {
\r
1396 MyFontParams *mfp = &font[bs][(int) ad->argLoc]->mfp;
\r
1397 fprintf(f, "/size=%s ", sizeInfo[bs].name);
\r
1398 fprintf(f, "/%s=\"%s:%g%s%s%s%s%sc%d\"\n",
\r
1399 ad->argName, mfp->faceName, mfp->pointSize,
\r
1400 mfp->bold || mfp->italic || mfp->underline || mfp->strikeout ? " " : "",
\r
1401 mfp->bold ? "b" : "",
\r
1402 mfp->italic ? "i" : "",
\r
1403 mfp->underline ? "u" : "",
\r
1404 mfp->strikeout ? "s" : "",
\r
1405 (int)mfp->charset);
\r
1411 { // [HGM] copy the names from the internal WB variables to appData
\r
1414 for (cc = (ColorClass)0; cc < ColorNormal; cc++)
\r
1415 (&appData.soundShout)[cc] = textAttribs[cc].sound.name;
\r
1416 for (sc = (SoundClass)0; sc < NSoundClasses; sc++)
\r
1417 (&appData.soundMove)[sc] = sounds[sc].name;
\r
1421 SaveAttribsArg(FILE *f, ArgDescriptor *ad)
\r
1422 { // here the "argLoc" defines a table index. It could have contained the 'ta' pointer itself, though
\r
1423 MyTextAttribs* ta = &textAttribs[(ColorClass)ad->argLoc];
\r
1424 fprintf(f, "/%s=\"%s%s%s%s%s#%02lx%02lx%02lx\"\n", ad->argName,
\r
1425 (ta->effects & CFE_BOLD) ? "b" : "",
\r
1426 (ta->effects & CFE_ITALIC) ? "i" : "",
\r
1427 (ta->effects & CFE_UNDERLINE) ? "u" : "",
\r
1428 (ta->effects & CFE_STRIKEOUT) ? "s" : "",
\r
1429 (ta->effects) ? " " : "",
\r
1430 ta->color&0xff, (ta->color >> 8)&0xff, (ta->color >> 16)&0xff);
\r
1434 SaveColor(FILE *f, ArgDescriptor *ad)
\r
1435 { // in WinBoard the color is an int and has to be converted to text. In X it would be a string already?
\r
1436 COLORREF color = *(COLORREF *)colorVariable[(int)ad->argLoc];
\r
1437 fprintf(f, "/%s=#%02lx%02lx%02lx\n", ad->argName,
\r
1438 color&0xff, (color>>8)&0xff, (color>>16)&0xff);
\r
1442 SaveBoardSize(FILE *f, char *name, void *addr)
\r
1443 { // wrapper to shield back-end from BoardSize & sizeInfo
\r
1444 fprintf(f, "/%s=%s\n", name, sizeInfo[*(BoardSize *)addr].name);
\r
1448 ParseCommPortSettings(char *s)
\r
1449 { // wrapper to keep dcb from back-end
\r
1450 ParseCommSettings(s, &dcb);
\r
1455 { // wrapper to shield use of window handles from back-end (make addressible by number?)
\r
1456 GetActualPlacement(hwndMain, &wpMain);
\r
1457 GetActualPlacement(hwndConsole, &wpConsole);
\r
1458 GetActualPlacement(commentDialog, &wpComment);
\r
1459 GetActualPlacement(editTagsDialog, &wpTags);
\r
1460 GetActualPlacement(gameListDialog, &wpGameList);
\r
1461 GetActualPlacement(moveHistoryDialog, &wpMoveHistory);
\r
1462 GetActualPlacement(evalGraphDialog, &wpEvalGraph);
\r
1463 GetActualPlacement(engineOutputDialog, &wpEngineOutput);
\r
1467 PrintCommPortSettings(FILE *f, char *name)
\r
1468 { // wrapper to shield back-end from DCB
\r
1469 PrintCommSettings(f, name, &dcb);
\r
1473 MySearchPath(char *installDir, char *name, char *fullname)
\r
1475 char *dummy, buf[MSG_SIZ], *p = name, *q;
\r
1476 if(name[0]== '%') {
\r
1477 fullname[0] = 0; // [HGM] first expand any environment variables in the given name
\r
1478 while(*p == '%' && (q = strchr(p+1, '%'))) { // [HGM] recognize %*% as environment variable
\r
1479 safeStrCpy(buf, p+1, sizeof(buf)/sizeof(buf[0]) );
\r
1480 *strchr(buf, '%') = 0;
\r
1481 strcat(fullname, getenv(buf));
\r
1482 p = q+1; while(*p == '\\') { strcat(fullname, "\\"); p++; }
\r
1484 strcat(fullname, p); // after environment variables (if any), take the remainder of the given name
\r
1485 if(appData.debugMode) fprintf(debugFP, "name = '%s', expanded name = '%s'\n", name, fullname);
\r
1486 return (int) strlen(fullname);
\r
1488 return (int) SearchPath(installDir, name, NULL, MSG_SIZ, fullname, &dummy);
\r
1492 MyGetFullPathName(char *name, char *fullname)
\r
1495 return (int) GetFullPathName(name, MSG_SIZ, fullname, &dummy);
\r
1500 { // [HGM] args: allows testing if main window is realized from back-end
\r
1501 return hwndMain != NULL;
\r
1505 PopUpStartupDialog()
\r
1509 LoadLanguageFile(appData.language);
\r
1510 lpProc = MakeProcInstance((FARPROC)StartupDialog, hInst);
\r
1511 DialogBox(hInst, MAKEINTRESOURCE(DLG_Startup), NULL, (DLGPROC)lpProc);
\r
1512 FreeProcInstance(lpProc);
\r
1515 /*---------------------------------------------------------------------------*\
\r
1517 * GDI board drawing routines
\r
1519 \*---------------------------------------------------------------------------*/
\r
1521 /* [AS] Draw square using background texture */
\r
1522 static void DrawTile( int dx, int dy, int dw, int dh, HDC dst, HDC src, int mode, int sx, int sy )
\r
1527 return; /* Should never happen! */
\r
1530 SetGraphicsMode( dst, GM_ADVANCED );
\r
1537 /* X reflection */
\r
1542 x.eDx = (FLOAT) dw + dx - 1;
\r
1545 SetWorldTransform( dst, &x );
\r
1548 /* Y reflection */
\r
1554 x.eDy = (FLOAT) dh + dy - 1;
\r
1556 SetWorldTransform( dst, &x );
\r
1564 x.eDx = (FLOAT) dx;
\r
1565 x.eDy = (FLOAT) dy;
\r
1568 SetWorldTransform( dst, &x );
\r
1572 BitBlt( dst, dx, dy, dw, dh, src, sx, sy, SRCCOPY );
\r
1580 SetWorldTransform( dst, &x );
\r
1582 ModifyWorldTransform( dst, 0, MWT_IDENTITY );
\r
1585 /* [AS] [HGM] Make room for more piece types, so all pieces can be different */
\r
1587 PM_WP = (int) WhitePawn,
\r
1588 PM_WN = (int) WhiteKnight,
\r
1589 PM_WB = (int) WhiteBishop,
\r
1590 PM_WR = (int) WhiteRook,
\r
1591 PM_WQ = (int) WhiteQueen,
\r
1592 PM_WF = (int) WhiteFerz,
\r
1593 PM_WW = (int) WhiteWazir,
\r
1594 PM_WE = (int) WhiteAlfil,
\r
1595 PM_WM = (int) WhiteMan,
\r
1596 PM_WO = (int) WhiteCannon,
\r
1597 PM_WU = (int) WhiteUnicorn,
\r
1598 PM_WH = (int) WhiteNightrider,
\r
1599 PM_WA = (int) WhiteAngel,
\r
1600 PM_WC = (int) WhiteMarshall,
\r
1601 PM_WAB = (int) WhiteCardinal,
\r
1602 PM_WD = (int) WhiteDragon,
\r
1603 PM_WL = (int) WhiteLance,
\r
1604 PM_WS = (int) WhiteCobra,
\r
1605 PM_WV = (int) WhiteFalcon,
\r
1606 PM_WSG = (int) WhiteSilver,
\r
1607 PM_WG = (int) WhiteGrasshopper,
\r
1608 PM_WK = (int) WhiteKing,
\r
1609 PM_BP = (int) BlackPawn,
\r
1610 PM_BN = (int) BlackKnight,
\r
1611 PM_BB = (int) BlackBishop,
\r
1612 PM_BR = (int) BlackRook,
\r
1613 PM_BQ = (int) BlackQueen,
\r
1614 PM_BF = (int) BlackFerz,
\r
1615 PM_BW = (int) BlackWazir,
\r
1616 PM_BE = (int) BlackAlfil,
\r
1617 PM_BM = (int) BlackMan,
\r
1618 PM_BO = (int) BlackCannon,
\r
1619 PM_BU = (int) BlackUnicorn,
\r
1620 PM_BH = (int) BlackNightrider,
\r
1621 PM_BA = (int) BlackAngel,
\r
1622 PM_BC = (int) BlackMarshall,
\r
1623 PM_BG = (int) BlackGrasshopper,
\r
1624 PM_BAB = (int) BlackCardinal,
\r
1625 PM_BD = (int) BlackDragon,
\r
1626 PM_BL = (int) BlackLance,
\r
1627 PM_BS = (int) BlackCobra,
\r
1628 PM_BV = (int) BlackFalcon,
\r
1629 PM_BSG = (int) BlackSilver,
\r
1630 PM_BK = (int) BlackKing
\r
1633 static HFONT hPieceFont = NULL;
\r
1634 static HBITMAP hPieceMask[(int) EmptySquare];
\r
1635 static HBITMAP hPieceFace[(int) EmptySquare];
\r
1636 static int fontBitmapSquareSize = 0;
\r
1637 static char pieceToFontChar[(int) EmptySquare] =
\r
1638 { 'p', 'n', 'b', 'r', 'q',
\r
1639 'n', 'b', 'p', 'n', 'b', 'r', 'b', 'r', 'q', 'k',
\r
1640 'k', 'o', 'm', 'v', 't', 'w',
\r
1641 'v', 't', 'o', 'm', 'v', 't', 'v', 't', 'w', 'l',
\r
1644 extern BOOL SetCharTable( char *table, const char * map );
\r
1645 /* [HGM] moved to backend.c */
\r
1647 static void SetPieceBackground( HDC hdc, COLORREF color, int mode )
\r
1650 BYTE r1 = GetRValue( color );
\r
1651 BYTE g1 = GetGValue( color );
\r
1652 BYTE b1 = GetBValue( color );
\r
1658 /* Create a uniform background first */
\r
1659 hbrush = CreateSolidBrush( color );
\r
1660 SetRect( &rc, 0, 0, squareSize, squareSize );
\r
1661 FillRect( hdc, &rc, hbrush );
\r
1662 DeleteObject( hbrush );
\r
1665 /* Vertical gradient, good for pawn, knight and rook, less for queen and king */
\r
1666 int steps = squareSize / 2;
\r
1669 for( i=0; i<steps; i++ ) {
\r
1670 BYTE r = r1 - (r1-r2) * i / steps;
\r
1671 BYTE g = g1 - (g1-g2) * i / steps;
\r
1672 BYTE b = b1 - (b1-b2) * i / steps;
\r
1674 hbrush = CreateSolidBrush( RGB(r,g,b) );
\r
1675 SetRect( &rc, i + squareSize - steps, 0, i + squareSize - steps + 1, squareSize );
\r
1676 FillRect( hdc, &rc, hbrush );
\r
1677 DeleteObject(hbrush);
\r
1680 else if( mode == 2 ) {
\r
1681 /* Diagonal gradient, good more or less for every piece */
\r
1682 POINT triangle[3];
\r
1683 HPEN hpen = SelectObject( hdc, GetStockObject(NULL_PEN) );
\r
1684 HBRUSH hbrush_old;
\r
1685 int steps = squareSize;
\r
1688 triangle[0].x = squareSize - steps;
\r
1689 triangle[0].y = squareSize;
\r
1690 triangle[1].x = squareSize;
\r
1691 triangle[1].y = squareSize;
\r
1692 triangle[2].x = squareSize;
\r
1693 triangle[2].y = squareSize - steps;
\r
1695 for( i=0; i<steps; i++ ) {
\r
1696 BYTE r = r1 - (r1-r2) * i / steps;
\r
1697 BYTE g = g1 - (g1-g2) * i / steps;
\r
1698 BYTE b = b1 - (b1-b2) * i / steps;
\r
1700 hbrush = CreateSolidBrush( RGB(r,g,b) );
\r
1701 hbrush_old = SelectObject( hdc, hbrush );
\r
1702 Polygon( hdc, triangle, 3 );
\r
1703 SelectObject( hdc, hbrush_old );
\r
1704 DeleteObject(hbrush);
\r
1709 SelectObject( hdc, hpen );
\r
1714 [AS] The method I use to create the bitmaps it a bit tricky, but it
\r
1715 seems to work ok. The main problem here is to find the "inside" of a chess
\r
1716 piece: follow the steps as explained below.
\r
1718 static void CreatePieceMaskFromFont( HDC hdc_window, HDC hdc, int index )
\r
1722 COLORREF chroma = RGB(0xFF,0x00,0xFF);
\r
1726 int backColor = whitePieceColor;
\r
1727 int foreColor = blackPieceColor;
\r
1729 if( index < (int)BlackPawn && appData.fontBackColorWhite != appData.fontForeColorWhite ) {
\r
1730 backColor = appData.fontBackColorWhite;
\r
1731 foreColor = appData.fontForeColorWhite;
\r
1733 else if( index >= (int)BlackPawn && appData.fontBackColorBlack != appData.fontForeColorBlack ) {
\r
1734 backColor = appData.fontBackColorBlack;
\r
1735 foreColor = appData.fontForeColorBlack;
\r
1739 hbm = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );
\r
1741 hbm_old = SelectObject( hdc, hbm );
\r
1745 rc.right = squareSize;
\r
1746 rc.bottom = squareSize;
\r
1748 /* Step 1: background is now black */
\r
1749 FillRect( hdc, &rc, GetStockObject(BLACK_BRUSH) );
\r
1751 GetTextExtentPoint32( hdc, &pieceToFontChar[index], 1, &sz );
\r
1753 pt.x = (squareSize - sz.cx) / 2;
\r
1754 pt.y = (squareSize - sz.cy) / 2;
\r
1756 SetBkMode( hdc, TRANSPARENT );
\r
1757 SetTextColor( hdc, chroma );
\r
1758 /* Step 2: the piece has been drawn in purple, there are now black and purple in this bitmap */
\r
1759 TextOut( hdc, pt.x, pt.y, &pieceToFontChar[appData.allWhite && index >= (int)BlackPawn ? index - (int)BlackPawn : index], 1 );
\r
1761 SelectObject( hdc, GetStockObject(WHITE_BRUSH) );
\r
1762 /* Step 3: the area outside the piece is filled with white */
\r
1763 // FloodFill( hdc, 0, 0, chroma );
\r
1764 ExtFloodFill( hdc, 0, 0, 0, FLOODFILLSURFACE );
\r
1765 ExtFloodFill( hdc, 0, squareSize-1, 0, FLOODFILLSURFACE ); // [HGM] fill from all 4 corners, for if piece too big
\r
1766 ExtFloodFill( hdc, squareSize-1, 0, 0, FLOODFILLSURFACE );
\r
1767 ExtFloodFill( hdc, squareSize-1, squareSize-1, 0, FLOODFILLSURFACE );
\r
1768 SelectObject( hdc, GetStockObject(BLACK_BRUSH) );
\r
1770 Step 4: this is the tricky part, the area inside the piece is filled with black,
\r
1771 but if the start point is not inside the piece we're lost!
\r
1772 There should be a better way to do this... if we could create a region or path
\r
1773 from the fill operation we would be fine for example.
\r
1775 // FloodFill( hdc, squareSize / 2, squareSize / 2, RGB(0xFF,0xFF,0xFF) );
\r
1776 ExtFloodFill( hdc, squareSize / 2, squareSize / 2, RGB(0xFF,0xFF,0xFF), FLOODFILLBORDER );
\r
1778 { /* [HGM] shave off edges of mask, in an attempt to correct for the fact that FloodFill does not work correctly under Win XP */
\r
1779 HDC dc2 = CreateCompatibleDC( hdc_window );
\r
1780 HBITMAP bm2 = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );
\r
1782 SelectObject( dc2, bm2 );
\r
1783 BitBlt( dc2, 0, 0, squareSize, squareSize, hdc, 0, 0, SRCCOPY ); // make copy
\r
1784 BitBlt( hdc, 0, 1, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );
\r
1785 BitBlt( hdc, 2, 1, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );
\r
1786 BitBlt( hdc, 1, 0, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );
\r
1787 BitBlt( hdc, 1, 2, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );
\r
1790 DeleteObject( bm2 );
\r
1793 SetTextColor( hdc, 0 );
\r
1795 Step 5: some fonts have "disconnected" areas that are skipped by the fill:
\r
1796 draw the piece again in black for safety.
\r
1798 TextOut( hdc, pt.x, pt.y, &pieceToFontChar[appData.allWhite && index >= (int)BlackPawn ? index - (int)BlackPawn : index], 1 );
\r
1800 SelectObject( hdc, hbm_old );
\r
1802 if( hPieceMask[index] != NULL ) {
\r
1803 DeleteObject( hPieceMask[index] );
\r
1806 hPieceMask[index] = hbm;
\r
1809 hbm = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );
\r
1811 SelectObject( hdc, hbm );
\r
1814 HDC dc1 = CreateCompatibleDC( hdc_window );
\r
1815 HDC dc2 = CreateCompatibleDC( hdc_window );
\r
1816 HBITMAP bm2 = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );
\r
1818 SelectObject( dc1, hPieceMask[index] );
\r
1819 SelectObject( dc2, bm2 );
\r
1820 FillRect( dc2, &rc, GetStockObject(WHITE_BRUSH) );
\r
1821 BitBlt( dc2, 0, 0, squareSize, squareSize, dc1, 0, 0, SRCINVERT );
\r
1824 Now dc2 contains the inverse of the piece mask, i.e. a mask that preserves
\r
1825 the piece background and deletes (makes transparent) the rest.
\r
1826 Thanks to that mask, we are free to paint the background with the greates
\r
1827 freedom, as we'll be able to mask off the unwanted parts when finished.
\r
1828 We use this, to make gradients and give the pieces a "roundish" look.
\r
1830 SetPieceBackground( hdc, backColor, 2 );
\r
1831 BitBlt( hdc, 0, 0, squareSize, squareSize, dc2, 0, 0, SRCAND );
\r
1835 DeleteObject( bm2 );
\r
1838 SetTextColor( hdc, foreColor );
\r
1839 TextOut( hdc, pt.x, pt.y, &pieceToFontChar[appData.allWhite && index >= (int)BlackPawn ? index - (int)BlackPawn : index], 1 );
\r
1841 SelectObject( hdc, hbm_old );
\r
1843 if( hPieceFace[index] != NULL ) {
\r
1844 DeleteObject( hPieceFace[index] );
\r
1847 hPieceFace[index] = hbm;
\r
1850 static int TranslatePieceToFontPiece( int piece )
\r
1880 case BlackMarshall:
\r
1884 case BlackNightrider:
\r
1890 case BlackUnicorn:
\r
1894 case BlackGrasshopper:
\r
1906 case BlackCardinal:
\r
1913 case WhiteMarshall:
\r
1917 case WhiteNightrider:
\r
1923 case WhiteUnicorn:
\r
1927 case WhiteGrasshopper:
\r
1939 case WhiteCardinal:
\r
1948 void CreatePiecesFromFont()
\r
1951 HDC hdc_window = NULL;
\r
1957 if( fontBitmapSquareSize < 0 ) {
\r
1958 /* Something went seriously wrong in the past: do not try to recreate fonts! */
\r
1962 if( appData.renderPiecesWithFont == NULL || appData.renderPiecesWithFont[0] == NULLCHAR || appData.renderPiecesWithFont[0] == '*' ) {
\r
1963 fontBitmapSquareSize = -1;
\r
1967 if( fontBitmapSquareSize != squareSize ) {
\r
1968 hdc_window = GetDC( hwndMain );
\r
1969 hdc = CreateCompatibleDC( hdc_window );
\r
1971 if( hPieceFont != NULL ) {
\r
1972 DeleteObject( hPieceFont );
\r
1975 for( i=0; i<=(int)BlackKing; i++ ) {
\r
1976 hPieceMask[i] = NULL;
\r
1977 hPieceFace[i] = NULL;
\r
1983 if( appData.fontPieceSize >= 50 && appData.fontPieceSize <= 150 ) {
\r
1984 fontHeight = appData.fontPieceSize;
\r
1987 fontHeight = (fontHeight * squareSize) / 100;
\r
1989 lf.lfHeight = -MulDiv( fontHeight, GetDeviceCaps(hdc, LOGPIXELSY), 72 );
\r
1991 lf.lfEscapement = 0;
\r
1992 lf.lfOrientation = 0;
\r
1993 lf.lfWeight = FW_NORMAL;
\r
1995 lf.lfUnderline = 0;
\r
1996 lf.lfStrikeOut = 0;
\r
1997 lf.lfCharSet = DEFAULT_CHARSET;
\r
1998 lf.lfOutPrecision = OUT_DEFAULT_PRECIS;
\r
1999 lf.lfClipPrecision = CLIP_DEFAULT_PRECIS;
\r
2000 lf.lfQuality = PROOF_QUALITY;
\r
2001 lf.lfPitchAndFamily = DEFAULT_PITCH | FF_DONTCARE;
\r
2002 strncpy( lf.lfFaceName, appData.renderPiecesWithFont, sizeof(lf.lfFaceName) );
\r
2003 lf.lfFaceName[ sizeof(lf.lfFaceName) - 1 ] = '\0';
\r
2005 hPieceFont = CreateFontIndirect( &lf );
\r
2007 if( hPieceFont == NULL ) {
\r
2008 fontBitmapSquareSize = -2;
\r
2011 /* Setup font-to-piece character table */
\r
2012 if( ! SetCharTable(pieceToFontChar, appData.fontToPieceTable) ) {
\r
2013 /* No (or wrong) global settings, try to detect the font */
\r
2014 if( strstr(lf.lfFaceName,"Alpha") != NULL ) {
\r
2016 SetCharTable(pieceToFontChar, "phbrqkojntwl");
\r
2018 else if( strstr(lf.lfFaceName,"DiagramTT") != NULL ) {
\r
2019 /* DiagramTT* family */
\r
2020 SetCharTable(pieceToFontChar, "PNLRQKpnlrqk");
\r
2022 else if( strstr(lf.lfFaceName,"WinboardF") != NULL ) {
\r
2023 /* Fairy symbols */
\r
2024 SetCharTable(pieceToFontChar, "PNBRQFEACWMOHIJGDVSLUKpnbrqfeacwmohijgdvsluk");
\r
2026 else if( strstr(lf.lfFaceName,"GC2004D") != NULL ) {
\r
2027 /* Good Companion (Some characters get warped as literal :-( */
\r
2028 char s[] = "1cmWG0??S??oYI23wgQU";
\r
2029 s[0]=0xB9; s[1]=0xA9; s[6]=0xB1; s[11]=0xBB; s[12]=0xAB; s[17]=0xB3;
\r
2030 SetCharTable(pieceToFontChar, s);
\r
2033 /* Cases, Condal, Leipzig, Lucena, Marroquin, Merida, Usual */
\r
2034 SetCharTable(pieceToFontChar, "pnbrqkomvtwl");
\r
2038 /* Create bitmaps */
\r
2039 hfont_old = SelectObject( hdc, hPieceFont );
\r
2040 for(i=(int)WhitePawn; i<(int)EmptySquare; i++) /* [HGM] made a loop for this */
\r
2041 if(PieceToChar((ChessSquare)i) != '.') /* skip unused pieces */
\r
2042 CreatePieceMaskFromFont( hdc_window, hdc, i );
\r
2044 SelectObject( hdc, hfont_old );
\r
2046 fontBitmapSquareSize = squareSize;
\r
2050 if( hdc != NULL ) {
\r
2054 if( hdc_window != NULL ) {
\r
2055 ReleaseDC( hwndMain, hdc_window );
\r
2060 DoLoadBitmap(HINSTANCE hinst, char *piece, int squareSize, char *suffix)
\r
2064 snprintf(name, sizeof(name)/sizeof(name[0]), "%s%d%s", piece, squareSize, suffix);
\r
2065 if (gameInfo.event &&
\r
2066 strcmp(gameInfo.event, "Easter Egg Hunt") == 0 &&
\r
2067 strcmp(name, "k80s") == 0) {
\r
2068 safeStrCpy(name, "tim", sizeof(name)/sizeof(name[0]) );
\r
2070 return LoadBitmap(hinst, name);
\r
2074 /* Insert a color into the program's logical palette
\r
2075 structure. This code assumes the given color is
\r
2076 the result of the RGB or PALETTERGB macro, and it
\r
2077 knows how those macros work (which is documented).
\r
2080 InsertInPalette(COLORREF color)
\r
2082 LPPALETTEENTRY pe = &(pLogPal->palPalEntry[pLogPal->palNumEntries]);
\r
2084 if (pLogPal->palNumEntries++ >= PALETTESIZE) {
\r
2085 DisplayFatalError(_("Too many colors"), 0, 1);
\r
2086 pLogPal->palNumEntries--;
\r
2090 pe->peFlags = (char) 0;
\r
2091 pe->peRed = (char) (0xFF & color);
\r
2092 pe->peGreen = (char) (0xFF & (color >> 8));
\r
2093 pe->peBlue = (char) (0xFF & (color >> 16));
\r
2099 InitDrawingColors()
\r
2101 if (pLogPal == NULL) {
\r
2102 /* Allocate enough memory for a logical palette with
\r
2103 * PALETTESIZE entries and set the size and version fields
\r
2104 * of the logical palette structure.
\r
2106 pLogPal = (NPLOGPALETTE)
\r
2107 LocalAlloc(LMEM_FIXED, (sizeof(LOGPALETTE) +
\r
2108 (sizeof(PALETTEENTRY) * (PALETTESIZE))));
\r
2109 pLogPal->palVersion = 0x300;
\r
2111 pLogPal->palNumEntries = 0;
\r
2113 InsertInPalette(lightSquareColor);
\r
2114 InsertInPalette(darkSquareColor);
\r
2115 InsertInPalette(whitePieceColor);
\r
2116 InsertInPalette(blackPieceColor);
\r
2117 InsertInPalette(highlightSquareColor);
\r
2118 InsertInPalette(premoveHighlightColor);
\r
2120 /* create a logical color palette according the information
\r
2121 * in the LOGPALETTE structure.
\r
2123 hPal = CreatePalette((LPLOGPALETTE) pLogPal);
\r
2125 lightSquareBrush = CreateSolidBrush(lightSquareColor);
\r
2126 blackSquareBrush = CreateSolidBrush(blackPieceColor);
\r
2127 darkSquareBrush = CreateSolidBrush(darkSquareColor);
\r
2128 whitePieceBrush = CreateSolidBrush(whitePieceColor);
\r
2129 blackPieceBrush = CreateSolidBrush(blackPieceColor);
\r
2130 iconBkgndBrush = CreateSolidBrush(GetSysColor(COLOR_BACKGROUND));
\r
2131 explodeBrush = CreateSolidBrush(highlightSquareColor); // [HGM] atomic
\r
2132 markerBrush = CreateSolidBrush(premoveHighlightColor); // [HGM] markers
\r
2133 /* [AS] Force rendering of the font-based pieces */
\r
2134 if( fontBitmapSquareSize > 0 ) {
\r
2135 fontBitmapSquareSize = 0;
\r
2141 BoardWidth(int boardSize, int n)
\r
2142 { /* [HGM] argument n added to allow different width and height */
\r
2143 int lineGap = sizeInfo[boardSize].lineGap;
\r
2145 if( appData.overrideLineGap >= 0 && appData.overrideLineGap <= 5 ) {
\r
2146 lineGap = appData.overrideLineGap;
\r
2149 return (n + 1) * lineGap +
\r
2150 n * sizeInfo[boardSize].squareSize;
\r
2153 /* Respond to board resize by dragging edge */
\r
2155 ResizeBoard(int newSizeX, int newSizeY, int flags)
\r
2157 BoardSize newSize = NUM_SIZES - 1;
\r
2158 static int recurse = 0;
\r
2159 if (IsIconic(hwndMain)) return;
\r
2160 if (recurse > 0) return;
\r
2162 while (newSize > 0) {
\r
2163 InitDrawingSizes(newSize+1000, 0); // [HGM] kludge to update sizeInfo without visible effects
\r
2164 if(newSizeX >= sizeInfo[newSize].cliWidth &&
\r
2165 newSizeY >= sizeInfo[newSize].cliHeight) break;
\r
2168 boardSize = newSize;
\r
2169 InitDrawingSizes(boardSize, flags);
\r
2174 extern Boolean twoBoards, partnerUp; // [HGM] dual
\r
2177 InitDrawingSizes(BoardSize boardSize, int flags)
\r
2179 int i, boardWidth, boardHeight; /* [HGM] height treated separately */
\r
2180 ChessSquare piece;
\r
2181 static int oldBoardSize = -1, oldTinyLayout = 0;
\r
2183 SIZE clockSize, messageSize;
\r
2185 char buf[MSG_SIZ];
\r
2187 HMENU hmenu = GetMenu(hwndMain);
\r
2188 RECT crect, wrect, oldRect;
\r
2190 LOGBRUSH logbrush;
\r
2192 int suppressVisibleEffects = 0; // [HGM] kludge to request updating sizeInfo only
\r
2193 if((int)boardSize >= 1000 ) { boardSize -= 1000; suppressVisibleEffects = 1; }
\r
2195 /* [HGM] call with -2 uses old size (for if nr of files, ranks changes) */
\r
2196 if(boardSize == (BoardSize)(-2) ) boardSize = oldBoardSize;
\r
2198 oldRect.left = wpMain.x; //[HGM] placement: remember previous window params
\r
2199 oldRect.top = wpMain.y;
\r
2200 oldRect.right = wpMain.x + wpMain.width;
\r
2201 oldRect.bottom = wpMain.y + wpMain.height;
\r
2203 tinyLayout = sizeInfo[boardSize].tinyLayout;
\r
2204 smallLayout = sizeInfo[boardSize].smallLayout;
\r
2205 squareSize = sizeInfo[boardSize].squareSize;
\r
2206 lineGap = sizeInfo[boardSize].lineGap;
\r
2207 minorSize = 0; /* [HGM] Kludge to see if demagnified pieces need to be shifted */
\r
2209 if( appData.overrideLineGap >= 0 && appData.overrideLineGap <= 5 ) {
\r
2210 lineGap = appData.overrideLineGap;
\r
2213 if (tinyLayout != oldTinyLayout) {
\r
2214 long style = GetWindowLong(hwndMain, GWL_STYLE);
\r
2216 style &= ~WS_SYSMENU;
\r
2217 InsertMenu(hmenu, IDM_Exit, MF_BYCOMMAND, IDM_Minimize,
\r
2218 "&Minimize\tCtrl+F4");
\r
2220 style |= WS_SYSMENU;
\r
2221 RemoveMenu(hmenu, IDM_Minimize, MF_BYCOMMAND);
\r
2223 SetWindowLong(hwndMain, GWL_STYLE, style);
\r
2225 for (i=0; menuBarText[tinyLayout][i]; i++) {
\r
2226 ModifyMenu(hmenu, i, MF_STRING|MF_BYPOSITION|MF_POPUP,
\r
2227 (UINT)GetSubMenu(hmenu, i), T_(menuBarText[tinyLayout][i]));
\r
2229 DrawMenuBar(hwndMain);
\r
2232 boardWidth = BoardWidth(boardSize, BOARD_WIDTH);
\r
2233 boardHeight = BoardWidth(boardSize, BOARD_HEIGHT);
\r
2235 /* Get text area sizes */
\r
2236 hdc = GetDC(hwndMain);
\r
2237 if (appData.clockMode) {
\r
2238 snprintf(buf, MSG_SIZ, _("White: %s"), TimeString(23*60*60*1000L));
\r
2240 snprintf(buf, MSG_SIZ, _("White"));
\r
2242 oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);
\r
2243 GetTextExtentPoint(hdc, buf, strlen(buf), &clockSize);
\r
2244 SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);
\r
2245 str = _("We only care about the height here");
\r
2246 GetTextExtentPoint(hdc, str, strlen(str), &messageSize);
\r
2247 SelectObject(hdc, oldFont);
\r
2248 ReleaseDC(hwndMain, hdc);
\r
2250 /* Compute where everything goes */
\r
2251 if((first.programLogo || second.programLogo) && !tinyLayout) {
\r
2252 /* [HGM] logo: if either logo is on, reserve space for it */
\r
2253 logoHeight = 2*clockSize.cy;
\r
2254 leftLogoRect.left = OUTER_MARGIN;
\r
2255 leftLogoRect.right = leftLogoRect.left + 4*clockSize.cy;
\r
2256 leftLogoRect.top = OUTER_MARGIN;
\r
2257 leftLogoRect.bottom = OUTER_MARGIN + logoHeight;
\r
2259 rightLogoRect.right = OUTER_MARGIN + boardWidth;
\r
2260 rightLogoRect.left = rightLogoRect.right - 4*clockSize.cy;
\r
2261 rightLogoRect.top = OUTER_MARGIN;
\r
2262 rightLogoRect.bottom = OUTER_MARGIN + logoHeight;
\r
2265 whiteRect.left = leftLogoRect.right;
\r
2266 whiteRect.right = OUTER_MARGIN + boardWidth/2 - INNER_MARGIN/2;
\r
2267 whiteRect.top = OUTER_MARGIN;
\r
2268 whiteRect.bottom = whiteRect.top + logoHeight;
\r
2270 blackRect.right = rightLogoRect.left;
\r
2271 blackRect.left = whiteRect.right + INNER_MARGIN;
\r
2272 blackRect.top = whiteRect.top;
\r
2273 blackRect.bottom = whiteRect.bottom;
\r
2275 whiteRect.left = OUTER_MARGIN;
\r
2276 whiteRect.right = whiteRect.left + boardWidth/2 - INNER_MARGIN/2;
\r
2277 whiteRect.top = OUTER_MARGIN;
\r
2278 whiteRect.bottom = whiteRect.top + clockSize.cy;
\r
2280 blackRect.left = whiteRect.right + INNER_MARGIN;
\r
2281 blackRect.right = blackRect.left + boardWidth/2 - 1;
\r
2282 blackRect.top = whiteRect.top;
\r
2283 blackRect.bottom = whiteRect.bottom;
\r
2285 logoHeight = 0; // [HGM] logo: suppress logo after change to tiny layout!
\r
2288 messageRect.left = OUTER_MARGIN + MESSAGE_LINE_LEFTMARGIN;
\r
2289 if (appData.showButtonBar) {
\r
2290 messageRect.right = OUTER_MARGIN + boardWidth // [HGM] logo: expressed independent of clock placement
\r
2291 - N_BUTTONS*BUTTON_WIDTH - MESSAGE_LINE_LEFTMARGIN;
\r
2293 messageRect.right = OUTER_MARGIN + boardWidth;
\r
2295 messageRect.top = whiteRect.bottom + INNER_MARGIN;
\r
2296 messageRect.bottom = messageRect.top + messageSize.cy;
\r
2298 boardRect.left = OUTER_MARGIN;
\r
2299 boardRect.right = boardRect.left + boardWidth;
\r
2300 boardRect.top = messageRect.bottom + INNER_MARGIN;
\r
2301 boardRect.bottom = boardRect.top + boardHeight;
\r
2303 sizeInfo[boardSize].cliWidth = boardRect.right + OUTER_MARGIN;
\r
2304 sizeInfo[boardSize].cliHeight = boardRect.bottom + OUTER_MARGIN;
\r
2305 oldBoardSize = boardSize;
\r
2306 oldTinyLayout = tinyLayout;
\r
2307 winW = 2 * GetSystemMetrics(SM_CXFRAME) + boardRect.right + OUTER_MARGIN;
\r
2308 winH = 2 * GetSystemMetrics(SM_CYFRAME) + GetSystemMetrics(SM_CYMENU) +
\r
2309 GetSystemMetrics(SM_CYCAPTION) + boardRect.bottom + OUTER_MARGIN;
\r
2310 winW *= 1 + twoBoards;
\r
2311 if(suppressVisibleEffects) return; // [HGM] when called for filling sizeInfo only
\r
2312 wpMain.width = winW; // [HGM] placement: set through temporary which can used by initial sizing choice
\r
2313 wpMain.height = winH; // without disturbing window attachments
\r
2314 GetWindowRect(hwndMain, &wrect);
\r
2315 SetWindowPos(hwndMain, NULL, 0, 0, wpMain.width, wpMain.height,
\r
2316 SWP_NOCOPYBITS|SWP_NOZORDER|SWP_NOMOVE);
\r
2318 // [HGM] placement: let attached windows follow size change.
\r
2319 ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, moveHistoryDialog, &wpMoveHistory );
\r
2320 ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, evalGraphDialog, &wpEvalGraph );
\r
2321 ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, engineOutputDialog, &wpEngineOutput );
\r
2322 ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, gameListDialog, &wpGameList );
\r
2323 ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, hwndConsole, &wpConsole );
\r
2325 /* compensate if menu bar wrapped */
\r
2326 GetClientRect(hwndMain, &crect);
\r
2327 offby = boardRect.bottom + OUTER_MARGIN - crect.bottom;
\r
2328 wpMain.height += offby;
\r
2330 case WMSZ_TOPLEFT:
\r
2331 SetWindowPos(hwndMain, NULL,
\r
2332 wrect.right - wpMain.width, wrect.bottom - wpMain.height,
\r
2333 wpMain.width, wpMain.height, SWP_NOCOPYBITS|SWP_NOZORDER);
\r
2336 case WMSZ_TOPRIGHT:
\r
2338 SetWindowPos(hwndMain, NULL,
\r
2339 wrect.left, wrect.bottom - wpMain.height,
\r
2340 wpMain.width, wpMain.height, SWP_NOCOPYBITS|SWP_NOZORDER);
\r
2343 case WMSZ_BOTTOMLEFT:
\r
2345 SetWindowPos(hwndMain, NULL,
\r
2346 wrect.right - wpMain.width, wrect.top,
\r
2347 wpMain.width, wpMain.height, SWP_NOCOPYBITS|SWP_NOZORDER);
\r
2350 case WMSZ_BOTTOMRIGHT:
\r
2354 SetWindowPos(hwndMain, NULL, 0, 0, wpMain.width, wpMain.height,
\r
2355 SWP_NOCOPYBITS|SWP_NOZORDER|SWP_NOMOVE);
\r
2360 for (i = 0; i < N_BUTTONS; i++) {
\r
2361 if (buttonDesc[i].hwnd != NULL) {
\r
2362 DestroyWindow(buttonDesc[i].hwnd);
\r
2363 buttonDesc[i].hwnd = NULL;
\r
2365 if (appData.showButtonBar) {
\r
2366 buttonDesc[i].hwnd =
\r
2367 CreateWindow("BUTTON", buttonDesc[i].label,
\r
2368 WS_VISIBLE | WS_CHILD | BS_PUSHBUTTON,
\r
2369 boardRect.right - BUTTON_WIDTH*(N_BUTTONS-i),
\r
2370 messageRect.top, BUTTON_WIDTH, messageSize.cy, hwndMain,
\r
2371 (HMENU) buttonDesc[i].id,
\r
2372 (HINSTANCE) GetWindowLong(hwndMain, GWL_HINSTANCE), NULL);
\r
2374 SendMessage(buttonDesc[i].hwnd, WM_SETFONT,
\r
2375 (WPARAM)font[boardSize][MESSAGE_FONT]->hf,
\r
2376 MAKELPARAM(FALSE, 0));
\r
2378 if (buttonDesc[i].id == IDM_Pause)
\r
2379 hwndPause = buttonDesc[i].hwnd;
\r
2380 buttonDesc[i].wndproc = (WNDPROC)
\r
2381 SetWindowLong(buttonDesc[i].hwnd, GWL_WNDPROC, (LONG) ButtonProc);
\r
2384 if (gridPen != NULL) DeleteObject(gridPen);
\r
2385 if (highlightPen != NULL) DeleteObject(highlightPen);
\r
2386 if (premovePen != NULL) DeleteObject(premovePen);
\r
2387 if (lineGap != 0) {
\r
2388 logbrush.lbStyle = BS_SOLID;
\r
2389 logbrush.lbColor = RGB(0, 0, 0); /* grid pen color = black */
\r
2391 ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,
\r
2392 lineGap, &logbrush, 0, NULL);
\r
2393 logbrush.lbColor = highlightSquareColor;
\r
2395 ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,
\r
2396 lineGap, &logbrush, 0, NULL);
\r
2398 logbrush.lbColor = premoveHighlightColor;
\r
2400 ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,
\r
2401 lineGap, &logbrush, 0, NULL);
\r
2403 /* [HGM] Loop had to be split in part for vert. and hor. lines */
\r
2404 for (i = 0; i < BOARD_HEIGHT + 1; i++) {
\r
2405 gridEndpoints[i*2].x = boardRect.left + lineGap / 2;
\r
2406 gridEndpoints[i*2].y = gridEndpoints[i*2 + 1].y =
\r
2407 boardRect.top + lineGap / 2 + (i * (squareSize + lineGap));
\r
2408 gridEndpoints[i*2 + 1].x = boardRect.left + lineGap / 2 +
\r
2409 BOARD_WIDTH * (squareSize + lineGap);
\r
2410 gridVertexCounts[i*2] = gridVertexCounts[i*2 + 1] = 2;
\r
2412 for (i = 0; i < BOARD_WIDTH + 1; i++) {
\r
2413 gridEndpoints[i*2 + BOARD_HEIGHT*2 + 2].y = boardRect.top + lineGap / 2;
\r
2414 gridEndpoints[i*2 + BOARD_HEIGHT*2 + 2].x =
\r
2415 gridEndpoints[i*2 + 1 + BOARD_HEIGHT*2 + 2].x = boardRect.left +
\r
2416 lineGap / 2 + (i * (squareSize + lineGap));
\r
2417 gridEndpoints[i*2 + 1 + BOARD_HEIGHT*2 + 2].y =
\r
2418 boardRect.top + BOARD_HEIGHT * (squareSize + lineGap);
\r
2419 gridVertexCounts[i*2] = gridVertexCounts[i*2 + 1] = 2;
\r
2423 /* [HGM] Licensing requirement */
\r
2425 if(gameInfo.variant == VariantGothic) GothicPopUp( GOTHIC, VariantGothic); else
\r
2428 if(gameInfo.variant == VariantFalcon) GothicPopUp( FALCON, VariantFalcon); else
\r
2430 GothicPopUp( "", VariantNormal);
\r
2433 /* if (boardSize == oldBoardSize) return; [HGM] variant might have changed */
\r
2435 /* Load piece bitmaps for this board size */
\r
2436 for (i=0; i<=2; i++) {
\r
2437 for (piece = WhitePawn;
\r
2438 (int) piece < (int) BlackPawn;
\r
2439 piece = (ChessSquare) ((int) piece + 1)) {
\r
2440 if (pieceBitmap[i][piece] != NULL)
\r
2441 DeleteObject(pieceBitmap[i][piece]);
\r
2445 fontBitmapSquareSize = 0; /* [HGM] render: make sure pieces will be recreated, as we might need others now */
\r
2446 // Orthodox Chess pieces
\r
2447 pieceBitmap[0][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "s");
\r
2448 pieceBitmap[0][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "s");
\r
2449 pieceBitmap[0][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "s");
\r
2450 pieceBitmap[0][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "s");
\r
2451 pieceBitmap[0][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "s");
\r
2452 pieceBitmap[1][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "o");
\r
2453 pieceBitmap[1][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "o");
\r
2454 pieceBitmap[1][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "o");
\r
2455 pieceBitmap[1][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "o");
\r
2456 pieceBitmap[1][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "o");
\r
2457 pieceBitmap[2][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "w");
\r
2458 pieceBitmap[2][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "w");
\r
2459 pieceBitmap[2][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "w");
\r
2460 pieceBitmap[2][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "w");
\r
2461 pieceBitmap[2][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "w");
\r
2462 if( gameInfo.variant == VariantShogi && squareSize <= 72 && squareSize >= 33) {
\r
2463 // in Shogi, Hijack the unused Queen for Lance
\r
2464 pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "s");
\r
2465 pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "o");
\r
2466 pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "w");
\r
2468 pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "s");
\r
2469 pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "o");
\r
2470 pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "w");
\r
2473 if(squareSize <= 72 && squareSize >= 33) {
\r
2474 /* A & C are available in most sizes now */
\r
2475 if(squareSize != 49 && squareSize != 72 && squareSize != 33) { // Vortex-like
\r
2476 pieceBitmap[0][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "s");
\r
2477 pieceBitmap[1][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "o");
\r
2478 pieceBitmap[2][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "w");
\r
2479 pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "s");
\r
2480 pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "o");
\r
2481 pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "w");
\r
2482 pieceBitmap[0][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "s");
\r
2483 pieceBitmap[1][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "o");
\r
2484 pieceBitmap[2][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "w");
\r
2485 pieceBitmap[0][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "s");
\r
2486 pieceBitmap[1][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "o");
\r
2487 pieceBitmap[2][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "w");
\r
2488 } else { // Smirf-like
\r
2489 pieceBitmap[0][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "s");
\r
2490 pieceBitmap[1][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "o");
\r
2491 pieceBitmap[2][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "w");
\r
2493 if(gameInfo.variant == VariantGothic) { // Vortex-like
\r
2494 pieceBitmap[0][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "s");
\r
2495 pieceBitmap[1][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "o");
\r
2496 pieceBitmap[2][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "w");
\r
2497 } else { // WinBoard standard
\r
2498 pieceBitmap[0][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "s");
\r
2499 pieceBitmap[1][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "o");
\r
2500 pieceBitmap[2][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "w");
\r
2505 if(squareSize==72 || squareSize==49 || squareSize==33) { /* experiment with some home-made bitmaps */
\r
2506 pieceBitmap[0][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "s");
\r
2507 pieceBitmap[1][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "o");
\r
2508 pieceBitmap[2][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "w");
\r
2509 pieceBitmap[0][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "s");
\r
2510 pieceBitmap[1][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "o");
\r
2511 pieceBitmap[2][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "w");
\r
2512 pieceBitmap[0][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "s");
\r
2513 pieceBitmap[1][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "o");
\r
2514 pieceBitmap[2][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "w");
\r
2515 pieceBitmap[0][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "s");
\r
2516 pieceBitmap[1][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "o");
\r
2517 pieceBitmap[2][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "w");
\r
2518 pieceBitmap[0][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "s");
\r
2519 pieceBitmap[1][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "o");
\r
2520 pieceBitmap[2][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "w");
\r
2521 pieceBitmap[0][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "s");
\r
2522 pieceBitmap[1][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "o");
\r
2523 pieceBitmap[2][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "w");
\r
2524 pieceBitmap[0][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "s");
\r
2525 pieceBitmap[1][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "o");
\r
2526 pieceBitmap[2][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "w");
\r
2527 pieceBitmap[0][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "s");
\r
2528 pieceBitmap[1][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "o");
\r
2529 pieceBitmap[2][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "w");
\r
2530 pieceBitmap[0][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "s");
\r
2531 pieceBitmap[1][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "o");
\r
2532 pieceBitmap[2][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "w");
\r
2533 pieceBitmap[0][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "s");
\r
2534 pieceBitmap[1][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "o");
\r
2535 pieceBitmap[2][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "w");
\r
2537 if(gameInfo.variant == VariantShogi) { /* promoted Gold represemtations */
\r
2538 pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "wp", squareSize, "s");
\r
2539 pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "wp", squareSize, "o");
\r
2540 pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "w", squareSize, "w");
\r
2541 pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "wn", squareSize, "s");
\r
2542 pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "wn", squareSize, "o");
\r
2543 pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "w", squareSize, "w");
\r
2544 pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "ws", squareSize, "s");
\r
2545 pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "ws", squareSize, "o");
\r
2546 pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "w", squareSize, "w");
\r
2547 pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "wl", squareSize, "s");
\r
2548 pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "wl", squareSize, "o");
\r
2549 pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "w", squareSize, "w");
\r
2551 pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "s");
\r
2552 pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "o");
\r
2553 pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "w");
\r
2554 pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "s");
\r
2555 pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "o");
\r
2556 pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "w");
\r
2557 pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "s");
\r
2558 pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "o");
\r
2559 pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "w");
\r
2560 pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "s");
\r
2561 pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "o");
\r
2562 pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "w");
\r
2565 } else { /* other size, no special bitmaps available. Use smaller symbols */
\r
2566 if((int)boardSize < 2) minorSize = sizeInfo[0].squareSize;
\r
2567 else minorSize = sizeInfo[(int)boardSize - 2].squareSize;
\r
2568 pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "s");
\r
2569 pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "o");
\r
2570 pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "w");
\r
2571 pieceBitmap[0][WhiteCardinal] = DoLoadBitmap(hInst, "b", minorSize, "s");
\r
2572 pieceBitmap[1][WhiteCardinal] = DoLoadBitmap(hInst, "b", minorSize, "o");
\r
2573 pieceBitmap[2][WhiteCardinal] = DoLoadBitmap(hInst, "b", minorSize, "w");
\r
2574 pieceBitmap[0][WhiteDragon] = DoLoadBitmap(hInst, "r", minorSize, "s");
\r
2575 pieceBitmap[1][WhiteDragon] = DoLoadBitmap(hInst, "r", minorSize, "o");
\r
2576 pieceBitmap[2][WhiteDragon] = DoLoadBitmap(hInst, "r", minorSize, "w");
\r
2577 pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "s");
\r
2578 pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "o");
\r
2579 pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "w");
\r
2583 if(gameInfo.variant == VariantShogi && squareSize == 58)
\r
2584 /* special Shogi support in this size */
\r
2585 { for (i=0; i<=2; i++) { /* replace all bitmaps */
\r
2586 for (piece = WhitePawn;
\r
2587 (int) piece < (int) BlackPawn;
\r
2588 piece = (ChessSquare) ((int) piece + 1)) {
\r
2589 if (pieceBitmap[i][piece] != NULL)
\r
2590 DeleteObject(pieceBitmap[i][piece]);
\r
2593 pieceBitmap[0][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "o");
\r
2594 pieceBitmap[0][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "o");
\r
2595 pieceBitmap[0][WhiteBishop] = DoLoadBitmap(hInst, "sb", squareSize, "o");
\r
2596 pieceBitmap[0][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "o");
\r
2597 pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "o");
\r
2598 pieceBitmap[0][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "o");
\r
2599 pieceBitmap[0][WhiteFerz] = DoLoadBitmap(hInst, "sf", squareSize, "o");
\r
2600 pieceBitmap[0][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "o");
\r
2601 pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "su", squareSize, "o");
\r
2602 pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "sh", squareSize, "o");
\r
2603 pieceBitmap[0][WhiteCardinal] = DoLoadBitmap(hInst, "sa", squareSize, "o");
\r
2604 pieceBitmap[0][WhiteDragon] = DoLoadBitmap(hInst, "sc", squareSize, "o");
\r
2605 pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "sg", squareSize, "o");
\r
2606 pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "ss", squareSize, "o");
\r
2607 pieceBitmap[1][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "o");
\r
2608 pieceBitmap[1][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "o");
\r
2609 pieceBitmap[1][WhiteBishop] = DoLoadBitmap(hInst, "sb", squareSize, "o");
\r
2610 pieceBitmap[1][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "o");
\r
2611 pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "o");
\r
2612 pieceBitmap[1][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "o");
\r
2613 pieceBitmap[1][WhiteFerz] = DoLoadBitmap(hInst, "sf", squareSize, "o");
\r
2614 pieceBitmap[1][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "o");
\r
2615 pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "su", squareSize, "o");
\r
2616 pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "sh", squareSize, "o");
\r
2617 pieceBitmap[1][WhiteCardinal] = DoLoadBitmap(hInst, "sa", squareSize, "o");
\r
2618 pieceBitmap[1][WhiteDragon] = DoLoadBitmap(hInst, "sc", squareSize, "o");
\r
2619 pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "sg", squareSize, "o");
\r
2620 pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "ss", squareSize, "o");
\r
2621 pieceBitmap[2][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "w");
\r
2622 pieceBitmap[2][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "w");
\r
2623 pieceBitmap[2][WhiteBishop] = DoLoadBitmap(hInst, "sr", squareSize, "w");
\r
2624 pieceBitmap[2][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "w");
\r
2625 pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "w");
\r
2626 pieceBitmap[2][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "w");
\r
2627 pieceBitmap[2][WhiteFerz] = DoLoadBitmap(hInst, "sw", squareSize, "w");
\r
2628 pieceBitmap[2][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "w");
\r
2629 pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "sp", squareSize, "w");
\r
2630 pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "sn", squareSize, "w");
\r
2631 pieceBitmap[2][WhiteCardinal] = DoLoadBitmap(hInst, "sr", squareSize, "w");
\r
2632 pieceBitmap[2][WhiteDragon] = DoLoadBitmap(hInst, "sr", squareSize, "w");
\r
2633 pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "sl", squareSize, "w");
\r
2634 pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "sw", squareSize, "w");
\r
2640 PieceBitmap(ChessSquare p, int kind)
\r
2642 if ((int) p >= (int) BlackPawn)
\r
2643 p = (ChessSquare) ((int) p - (int) BlackPawn + (int) WhitePawn);
\r
2645 return pieceBitmap[kind][(int) p];
\r
2648 /***************************************************************/
\r
2650 #define MIN(a,b) (((a) < (b)) ? (a) : (b))
\r
2651 #define MAX(a,b) (((a) > (b)) ? (a) : (b))
\r
2653 #define MIN3(a,b,c) (((a) < (b) && (a) < (c)) ? (a) : (((b) < (a) && (b) < (c)) ? (b) : (c)))
\r
2654 #define MAX3(a,b,c) (((a) > (b) && (a) > (c)) ? (a) : (((b) > (a) && (b) > (c)) ? (b) : (c)))
\r
2658 SquareToPos(int row, int column, int * x, int * y)
\r
2661 *x = boardRect.left + lineGap + ((BOARD_WIDTH-1)-column) * (squareSize + lineGap);
\r
2662 *y = boardRect.top + lineGap + row * (squareSize + lineGap);
\r
2664 *x = boardRect.left + lineGap + column * (squareSize + lineGap);
\r
2665 *y = boardRect.top + lineGap + ((BOARD_HEIGHT-1)-row) * (squareSize + lineGap);
\r
2670 DrawCoordsOnDC(HDC hdc)
\r
2672 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
2673 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
2674 char str[2] = { NULLCHAR, NULLCHAR };
\r
2675 int oldMode, oldAlign, x, y, start, i;
\r
2679 if (!appData.showCoords)
\r
2682 start = flipView ? 1-(ONE!='1') : 23+(ONE!='1')-BOARD_HEIGHT;
\r
2684 oldBrush = SelectObject(hdc, GetStockObject(BLACK_BRUSH));
\r
2685 oldMode = SetBkMode(hdc, (appData.monoMode ? OPAQUE : TRANSPARENT));
\r
2686 oldAlign = GetTextAlign(hdc);
\r
2687 oldFont = SelectObject(hdc, font[boardSize][COORD_FONT]->hf);
\r
2689 y = boardRect.top + lineGap;
\r
2690 x = boardRect.left + lineGap + gameInfo.holdingsWidth*(squareSize + lineGap);
\r
2692 SetTextAlign(hdc, TA_LEFT|TA_TOP);
\r
2693 for (i = 0; i < BOARD_HEIGHT; i++) {
\r
2694 str[0] = files[start + i];
\r
2695 ExtTextOut(hdc, x + 2, y + 1, 0, NULL, str, 1, NULL);
\r
2696 y += squareSize + lineGap;
\r
2699 start = flipView ? 12-(BOARD_RGHT-BOARD_LEFT) : 12;
\r
2701 SetTextAlign(hdc, TA_RIGHT|TA_BOTTOM);
\r
2702 for (i = 0; i < BOARD_RGHT - BOARD_LEFT; i++) {
\r
2703 str[0] = ranks[start + i];
\r
2704 ExtTextOut(hdc, x + squareSize - 2, y - 1, 0, NULL, str, 1, NULL);
\r
2705 x += squareSize + lineGap;
\r
2708 SelectObject(hdc, oldBrush);
\r
2709 SetBkMode(hdc, oldMode);
\r
2710 SetTextAlign(hdc, oldAlign);
\r
2711 SelectObject(hdc, oldFont);
\r
2715 DrawGridOnDC(HDC hdc)
\r
2719 if (lineGap != 0) {
\r
2720 oldPen = SelectObject(hdc, gridPen);
\r
2721 PolyPolyline(hdc, gridEndpoints, gridVertexCounts, BOARD_WIDTH+BOARD_HEIGHT + 2);
\r
2722 SelectObject(hdc, oldPen);
\r
2726 #define HIGHLIGHT_PEN 0
\r
2727 #define PREMOVE_PEN 1
\r
2730 DrawHighlightOnDC(HDC hdc, BOOLEAN on, int x, int y, int pen)
\r
2733 HPEN oldPen, hPen;
\r
2734 if (lineGap == 0) return;
\r
2736 x1 = boardRect.left +
\r
2737 lineGap/2 + ((BOARD_WIDTH-1)-x) * (squareSize + lineGap);
\r
2738 y1 = boardRect.top +
\r
2739 lineGap/2 + y * (squareSize + lineGap);
\r
2741 x1 = boardRect.left +
\r
2742 lineGap/2 + x * (squareSize + lineGap);
\r
2743 y1 = boardRect.top +
\r
2744 lineGap/2 + ((BOARD_HEIGHT-1)-y) * (squareSize + lineGap);
\r
2746 hPen = pen ? premovePen : highlightPen;
\r
2747 oldPen = SelectObject(hdc, on ? hPen : gridPen);
\r
2748 MoveToEx(hdc, x1, y1, NULL);
\r
2749 LineTo(hdc, x1 + squareSize + lineGap, y1);
\r
2750 LineTo(hdc, x1 + squareSize + lineGap, y1 + squareSize + lineGap);
\r
2751 LineTo(hdc, x1, y1 + squareSize + lineGap);
\r
2752 LineTo(hdc, x1, y1);
\r
2753 SelectObject(hdc, oldPen);
\r
2757 DrawHighlightsOnDC(HDC hdc, HighlightInfo *h, int pen)
\r
2760 for (i=0; i<2; i++) {
\r
2761 if (h->sq[i].x >= 0 && h->sq[i].y >= 0)
\r
2762 DrawHighlightOnDC(hdc, TRUE,
\r
2763 h->sq[i].x, h->sq[i].y,
\r
2768 /* Note: sqcolor is used only in monoMode */
\r
2769 /* Note that this code is largely duplicated in woptions.c,
\r
2770 function DrawSampleSquare, so that needs to be updated too */
\r
2772 DrawPieceOnDC(HDC hdc, ChessSquare piece, int color, int sqcolor, int x, int y, HDC tmphdc)
\r
2774 HBITMAP oldBitmap;
\r
2778 if (appData.blindfold) return;
\r
2780 /* [AS] Use font-based pieces if needed */
\r
2781 if( fontBitmapSquareSize >= 0 && (squareSize > 32 || gameInfo.variant >= VariantShogi)) {
\r
2782 /* Create piece bitmaps, or do nothing if piece set is up to date */
\r
2783 CreatePiecesFromFont();
\r
2785 if( fontBitmapSquareSize == squareSize ) {
\r
2786 int index = TranslatePieceToFontPiece(piece);
\r
2788 SelectObject( tmphdc, hPieceMask[ index ] );
\r
2790 if(appData.upsideDown ? color==flipView : (flipView && gameInfo.variant == VariantShogi))
\r
2791 StretchBlt(hdc, x+squareSize, y+squareSize, -squareSize, -squareSize, tmphdc, 0, 0, squareSize, squareSize, SRCAND);
\r
2795 squareSize, squareSize,
\r
2800 SelectObject( tmphdc, hPieceFace[ index ] );
\r
2802 if(appData.upsideDown ? color==flipView : (flipView && gameInfo.variant == VariantShogi))
\r
2803 StretchBlt(hdc, x+squareSize, y+squareSize, -squareSize, -squareSize, tmphdc, 0, 0, squareSize, squareSize, SRCPAINT);
\r
2807 squareSize, squareSize,
\r
2816 if (appData.monoMode) {
\r
2817 SelectObject(tmphdc, PieceBitmap(piece,
\r
2818 color == sqcolor ? OUTLINE_PIECE : SOLID_PIECE));
\r
2819 BitBlt(hdc, x, y, squareSize, squareSize, tmphdc, 0, 0,
\r
2820 sqcolor ? SRCCOPY : NOTSRCCOPY);
\r
2822 tmpSize = squareSize;
\r
2824 ((piece >= (int)WhiteNightrider && piece <= WhiteGrasshopper) ||
\r
2825 (piece >= (int)BlackNightrider && piece <= BlackGrasshopper)) ) {
\r
2826 /* [HGM] no bitmap available for promoted pieces in Crazyhouse */
\r
2827 /* Bitmaps of smaller size are substituted, but we have to align them */
\r
2828 x += (squareSize - minorSize)>>1;
\r
2829 y += squareSize - minorSize - 2;
\r
2830 tmpSize = minorSize;
\r
2832 if (color || appData.allWhite ) {
\r
2833 oldBitmap = SelectObject(tmphdc, PieceBitmap(piece, WHITE_PIECE));
\r
2835 oldBrush = SelectObject(hdc, whitePieceBrush);
\r
2836 else oldBrush = SelectObject(hdc, blackPieceBrush);
\r
2837 if(appData.upsideDown && color==flipView)
\r
2838 StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, 0x00B8074A);
\r
2840 BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, 0x00B8074A);
\r
2841 /* Use black for outline of white pieces */
\r
2842 SelectObject(tmphdc, PieceBitmap(piece, OUTLINE_PIECE));
\r
2843 if(appData.upsideDown && color==flipView)
\r
2844 StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, SRCAND);
\r
2846 BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, SRCAND);
\r
2848 /* Use square color for details of black pieces */
\r
2849 oldBitmap = SelectObject(tmphdc, PieceBitmap(piece, SOLID_PIECE));
\r
2850 oldBrush = SelectObject(hdc, blackPieceBrush);
\r
2851 if(appData.upsideDown && !flipView)
\r
2852 StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, 0x00B8074A);
\r
2854 BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, 0x00B8074A);
\r
2856 SelectObject(hdc, oldBrush);
\r
2857 SelectObject(tmphdc, oldBitmap);
\r
2861 /* [AS] Compute a drawing mode for a square, based on specified settings (see DrawTile) */
\r
2862 int GetBackTextureMode( int algo )
\r
2864 int result = BACK_TEXTURE_MODE_DISABLED;
\r
2868 case BACK_TEXTURE_MODE_PLAIN:
\r
2869 result = 1; /* Always use identity map */
\r
2871 case BACK_TEXTURE_MODE_FULL_RANDOM:
\r
2872 result = 1 + (myrandom() % 3); /* Pick a transformation at random */
\r
2880 [AS] Compute and save texture drawing info, otherwise we may not be able
\r
2881 to handle redraws cleanly (as random numbers would always be different).
\r
2883 VOID RebuildTextureSquareInfo()
\r
2893 ZeroMemory( &backTextureSquareInfo, sizeof(backTextureSquareInfo) );
\r
2895 if( liteBackTexture != NULL ) {
\r
2896 if( GetObject( liteBackTexture, sizeof(bi), &bi ) > 0 ) {
\r
2897 lite_w = bi.bmWidth;
\r
2898 lite_h = bi.bmHeight;
\r
2902 if( darkBackTexture != NULL ) {
\r
2903 if( GetObject( darkBackTexture, sizeof(bi), &bi ) > 0 ) {
\r
2904 dark_w = bi.bmWidth;
\r
2905 dark_h = bi.bmHeight;
\r
2909 for( row=0; row<BOARD_HEIGHT; row++ ) {
\r
2910 for( col=0; col<BOARD_WIDTH; col++ ) {
\r
2911 if( (col + row) & 1 ) {
\r
2913 if( lite_w >= squareSize && lite_h >= squareSize ) {
\r
2914 if( lite_w >= squareSize*BOARD_WIDTH )
\r
2915 backTextureSquareInfo[row][col].x = (2*col+1)*lite_w/(2*BOARD_WIDTH) - squareSize/2; /* [HGM] cut out of center of virtual square */
\r
2917 backTextureSquareInfo[row][col].x = col * (lite_w - squareSize) / (BOARD_WIDTH-1); /* [HGM] divide by size-1 in stead of size! */
\r
2918 if( lite_h >= squareSize*BOARD_HEIGHT )
\r
2919 backTextureSquareInfo[row][col].y = (2*(BOARD_HEIGHT-row)-1)*lite_h/(2*BOARD_HEIGHT) - squareSize/2;
\r
2921 backTextureSquareInfo[row][col].y = (BOARD_HEIGHT-1-row) * (lite_h - squareSize) / (BOARD_HEIGHT-1);
\r
2922 backTextureSquareInfo[row][col].mode = GetBackTextureMode(liteBackTextureMode);
\r
2927 if( dark_w >= squareSize && dark_h >= squareSize ) {
\r
2928 if( dark_w >= squareSize*BOARD_WIDTH )
\r
2929 backTextureSquareInfo[row][col].x = (2*col+1) * dark_w / (2*BOARD_WIDTH) - squareSize/2;
\r
2931 backTextureSquareInfo[row][col].x = col * (dark_w - squareSize) / (BOARD_WIDTH-1);
\r
2932 if( dark_h >= squareSize*BOARD_HEIGHT )
\r
2933 backTextureSquareInfo[row][col].y = (2*(BOARD_HEIGHT-row)-1) * dark_h / (2*BOARD_HEIGHT) - squareSize/2;
\r
2935 backTextureSquareInfo[row][col].y = (BOARD_HEIGHT-1-row) * (dark_h - squareSize) / (BOARD_HEIGHT-1);
\r
2936 backTextureSquareInfo[row][col].mode = GetBackTextureMode(darkBackTextureMode);
\r
2943 /* [AS] Arrow highlighting support */
\r
2945 static int A_WIDTH = 5; /* Width of arrow body */
\r
2947 #define A_HEIGHT_FACTOR 6 /* Length of arrow "point", relative to body width */
\r
2948 #define A_WIDTH_FACTOR 3 /* Width of arrow "point", relative to body width */
\r
2950 static double Sqr( double x )
\r
2955 static int Round( double x )
\r
2957 return (int) (x + 0.5);
\r
2960 /* Draw an arrow between two points using current settings */
\r
2961 VOID DrawArrowBetweenPoints( HDC hdc, int s_x, int s_y, int d_x, int d_y )
\r
2964 double dx, dy, j, k, x, y;
\r
2966 if( d_x == s_x ) {
\r
2967 int h = (d_y > s_y) ? +A_WIDTH*A_HEIGHT_FACTOR : -A_WIDTH*A_HEIGHT_FACTOR;
\r
2969 arrow[0].x = s_x + A_WIDTH;
\r
2972 arrow[1].x = s_x + A_WIDTH;
\r
2973 arrow[1].y = d_y - h;
\r
2975 arrow[2].x = s_x + A_WIDTH*A_WIDTH_FACTOR;
\r
2976 arrow[2].y = d_y - h;
\r
2981 arrow[4].x = s_x - A_WIDTH*A_WIDTH_FACTOR;
\r
2982 arrow[4].y = d_y - h;
\r
2984 arrow[5].x = s_x - A_WIDTH;
\r
2985 arrow[5].y = d_y - h;
\r
2987 arrow[6].x = s_x - A_WIDTH;
\r
2990 else if( d_y == s_y ) {
\r
2991 int w = (d_x > s_x) ? +A_WIDTH*A_HEIGHT_FACTOR : -A_WIDTH*A_HEIGHT_FACTOR;
\r
2994 arrow[0].y = s_y + A_WIDTH;
\r
2996 arrow[1].x = d_x - w;
\r
2997 arrow[1].y = s_y + A_WIDTH;
\r
2999 arrow[2].x = d_x - w;
\r
3000 arrow[2].y = s_y + A_WIDTH*A_WIDTH_FACTOR;
\r
3005 arrow[4].x = d_x - w;
\r
3006 arrow[4].y = s_y - A_WIDTH*A_WIDTH_FACTOR;
\r
3008 arrow[5].x = d_x - w;
\r
3009 arrow[5].y = s_y - A_WIDTH;
\r
3012 arrow[6].y = s_y - A_WIDTH;
\r
3015 /* [AS] Needed a lot of paper for this! :-) */
\r
3016 dy = (double) (d_y - s_y) / (double) (d_x - s_x);
\r
3017 dx = (double) (s_x - d_x) / (double) (s_y - d_y);
\r
3019 j = sqrt( Sqr(A_WIDTH) / (1.0 + Sqr(dx)) );
\r
3021 k = sqrt( Sqr(A_WIDTH*A_HEIGHT_FACTOR) / (1.0 + Sqr(dy)) );
\r
3026 arrow[0].x = Round(x - j);
\r
3027 arrow[0].y = Round(y + j*dx);
\r
3029 arrow[1].x = Round(x + j);
\r
3030 arrow[1].y = Round(y - j*dx);
\r
3033 x = (double) d_x - k;
\r
3034 y = (double) d_y - k*dy;
\r
3037 x = (double) d_x + k;
\r
3038 y = (double) d_y + k*dy;
\r
3041 arrow[2].x = Round(x + j);
\r
3042 arrow[2].y = Round(y - j*dx);
\r
3044 arrow[3].x = Round(x + j*A_WIDTH_FACTOR);
\r
3045 arrow[3].y = Round(y - j*A_WIDTH_FACTOR*dx);
\r
3050 arrow[5].x = Round(x - j*A_WIDTH_FACTOR);
\r
3051 arrow[5].y = Round(y + j*A_WIDTH_FACTOR*dx);
\r
3053 arrow[6].x = Round(x - j);
\r
3054 arrow[6].y = Round(y + j*dx);
\r
3057 Polygon( hdc, arrow, 7 );
\r
3060 /* [AS] Draw an arrow between two squares */
\r
3061 VOID DrawArrowBetweenSquares( HDC hdc, int s_col, int s_row, int d_col, int d_row )
\r
3063 int s_x, s_y, d_x, d_y;
\r
3070 if( s_col == d_col && s_row == d_row ) {
\r
3074 /* Get source and destination points */
\r
3075 SquareToPos( s_row, s_col, &s_x, &s_y);
\r
3076 SquareToPos( d_row, d_col, &d_x, &d_y);
\r
3079 d_y += squareSize / 4;
\r
3081 else if( d_y < s_y ) {
\r
3082 d_y += 3 * squareSize / 4;
\r
3085 d_y += squareSize / 2;
\r
3089 d_x += squareSize / 4;
\r
3091 else if( d_x < s_x ) {
\r
3092 d_x += 3 * squareSize / 4;
\r
3095 d_x += squareSize / 2;
\r
3098 s_x += squareSize / 2;
\r
3099 s_y += squareSize / 2;
\r
3101 /* Adjust width */
\r
3102 A_WIDTH = squareSize / 14;
\r
3105 stLB.lbStyle = BS_SOLID;
\r
3106 stLB.lbColor = appData.highlightArrowColor;
\r
3109 hpen = CreatePen( PS_SOLID, 2, RGB(0x00,0x00,0x00) );
\r
3110 holdpen = SelectObject( hdc, hpen );
\r
3111 hbrush = CreateBrushIndirect( &stLB );
\r
3112 holdbrush = SelectObject( hdc, hbrush );
\r
3114 DrawArrowBetweenPoints( hdc, s_x, s_y, d_x, d_y );
\r
3116 SelectObject( hdc, holdpen );
\r
3117 SelectObject( hdc, holdbrush );
\r
3118 DeleteObject( hpen );
\r
3119 DeleteObject( hbrush );
\r
3122 BOOL HasHighlightInfo()
\r
3124 BOOL result = FALSE;
\r
3126 if( highlightInfo.sq[0].x >= 0 && highlightInfo.sq[0].y >= 0 &&
\r
3127 highlightInfo.sq[1].x >= 0 && highlightInfo.sq[1].y >= 0 )
\r
3135 BOOL IsDrawArrowEnabled()
\r
3137 BOOL result = FALSE;
\r
3139 if( appData.highlightMoveWithArrow && squareSize >= 32 ) {
\r
3146 VOID DrawArrowHighlight( HDC hdc )
\r
3148 if( IsDrawArrowEnabled() && HasHighlightInfo() ) {
\r
3149 DrawArrowBetweenSquares( hdc,
\r
3150 highlightInfo.sq[0].x, highlightInfo.sq[0].y,
\r
3151 highlightInfo.sq[1].x, highlightInfo.sq[1].y );
\r
3155 HRGN GetArrowHighlightClipRegion( HDC hdc )
\r
3157 HRGN result = NULL;
\r
3159 if( HasHighlightInfo() ) {
\r
3160 int x1, y1, x2, y2;
\r
3161 int sx, sy, dx, dy;
\r
3163 SquareToPos(highlightInfo.sq[0].y, highlightInfo.sq[0].x, &x1, &y1 );
\r
3164 SquareToPos(highlightInfo.sq[1].y, highlightInfo.sq[1].x, &x2, &y2 );
\r
3166 sx = MIN( x1, x2 );
\r
3167 sy = MIN( y1, y2 );
\r
3168 dx = MAX( x1, x2 ) + squareSize;
\r
3169 dy = MAX( y1, y2 ) + squareSize;
\r
3171 result = CreateRectRgn( sx, sy, dx, dy );
\r
3178 Warning: this function modifies the behavior of several other functions.
\r
3180 Basically, Winboard is optimized to avoid drawing the whole board if not strictly
\r
3181 needed. Unfortunately, the decision whether or not to perform a full or partial
\r
3182 repaint is scattered all over the place, which is not good for features such as
\r
3183 "arrow highlighting" that require a full repaint of the board.
\r
3185 So, I've tried to patch the code where I thought it made sense (e.g. after or during
\r
3186 user interaction, when speed is not so important) but especially to avoid errors
\r
3187 in the displayed graphics.
\r
3189 In such patched places, I always try refer to this function so there is a single
\r
3190 place to maintain knowledge.
\r
3192 To restore the original behavior, just return FALSE unconditionally.
\r
3194 BOOL IsFullRepaintPreferrable()
\r
3196 BOOL result = FALSE;
\r
3198 if( (appData.highlightLastMove || appData.highlightDragging) && IsDrawArrowEnabled() ) {
\r
3199 /* Arrow may appear on the board */
\r
3207 This function is called by DrawPosition to know whether a full repaint must
\r
3210 Only DrawPosition may directly call this function, which makes use of
\r
3211 some state information. Other function should call DrawPosition specifying
\r
3212 the repaint flag, and can use IsFullRepaintPreferrable if needed.
\r
3214 BOOL DrawPositionNeedsFullRepaint()
\r
3216 BOOL result = FALSE;
\r
3219 Probably a slightly better policy would be to trigger a full repaint
\r
3220 when animInfo.piece changes state (i.e. empty -> non-empty and viceversa),
\r
3221 but animation is fast enough that it's difficult to notice.
\r
3223 if( animInfo.piece == EmptySquare ) {
\r
3224 if( (appData.highlightLastMove || appData.highlightDragging) && IsDrawArrowEnabled() /*&& HasHighlightInfo()*/ ) {
\r
3233 DrawBoardOnDC(HDC hdc, Board board, HDC tmphdc)
\r
3235 int row, column, x, y, square_color, piece_color;
\r
3236 ChessSquare piece;
\r
3238 HDC texture_hdc = NULL;
\r
3240 /* [AS] Initialize background textures if needed */
\r
3241 if( liteBackTexture != NULL || darkBackTexture != NULL ) {
\r
3242 static int backTextureBoardSize; /* [HGM] boardsize: also new texture if board format changed */
\r
3243 if( backTextureSquareSize != squareSize
\r
3244 || backTextureBoardSize != BOARD_WIDTH+BOARD_FILES*BOARD_HEIGHT) {
\r
3245 backTextureBoardSize = BOARD_WIDTH+BOARD_FILES*BOARD_HEIGHT;
\r
3246 backTextureSquareSize = squareSize;
\r
3247 RebuildTextureSquareInfo();
\r
3250 texture_hdc = CreateCompatibleDC( hdc );
\r
3253 for (row = 0; row < BOARD_HEIGHT; row++) {
\r
3254 for (column = 0; column < BOARD_WIDTH; column++) {
\r
3256 SquareToPos(row, column, &x, &y);
\r
3258 piece = board[row][column];
\r
3260 square_color = ((column + row) % 2) == 1;
\r
3261 if( gameInfo.variant == VariantXiangqi ) {
\r
3262 square_color = !InPalace(row, column);
\r
3263 if(BOARD_HEIGHT&1) { if(row==BOARD_HEIGHT/2) square_color ^= 1; }
\r
3264 else if(row < BOARD_HEIGHT/2) square_color ^= 1;
\r
3266 piece_color = (int) piece < (int) BlackPawn;
\r
3269 /* [HGM] holdings file: light square or black */
\r
3270 if(column == BOARD_LEFT-2) {
\r
3271 if( row > BOARD_HEIGHT - gameInfo.holdingsSize - 1 )
\r
3274 DisplayHoldingsCount(hdc, x, y, 0, 0); /* black out */
\r
3278 if(column == BOARD_RGHT + 1 ) {
\r
3279 if( row < gameInfo.holdingsSize )
\r
3282 DisplayHoldingsCount(hdc, x, y, 0, 0);
\r
3286 if(column == BOARD_LEFT-1 ) /* left align */
\r
3287 DisplayHoldingsCount(hdc, x, y, flipView, (int) board[row][column]);
\r
3288 else if( column == BOARD_RGHT) /* right align */
\r
3289 DisplayHoldingsCount(hdc, x, y, !flipView, (int) board[row][column]);
\r
3291 if (appData.monoMode) {
\r
3292 if (piece == EmptySquare) {
\r
3293 BitBlt(hdc, x, y, squareSize, squareSize, 0, 0, 0,
\r
3294 square_color ? WHITENESS : BLACKNESS);
\r
3296 DrawPieceOnDC(hdc, piece, piece_color, square_color, x, y, tmphdc);
\r
3299 else if( backTextureSquareInfo[row][column].mode > 0 ) {
\r
3300 /* [AS] Draw the square using a texture bitmap */
\r
3301 HBITMAP hbm = SelectObject( texture_hdc, square_color ? liteBackTexture : darkBackTexture );
\r
3302 int r = row, c = column; // [HGM] do not flip board in flipView
\r
3303 if(flipView) { r = BOARD_HEIGHT-1 - r; c = BOARD_WIDTH-1 - c; }
\r
3306 squareSize, squareSize,
\r
3309 backTextureSquareInfo[r][c].mode,
\r
3310 backTextureSquareInfo[r][c].x,
\r
3311 backTextureSquareInfo[r][c].y );
\r
3313 SelectObject( texture_hdc, hbm );
\r
3315 if (piece != EmptySquare) {
\r
3316 DrawPieceOnDC(hdc, piece, piece_color, -1, x, y, tmphdc);
\r
3320 HBRUSH brush = square_color ? lightSquareBrush : darkSquareBrush;
\r
3322 oldBrush = SelectObject(hdc, brush );
\r
3323 BitBlt(hdc, x, y, squareSize, squareSize, 0, 0, 0, PATCOPY);
\r
3324 SelectObject(hdc, oldBrush);
\r
3325 if (piece != EmptySquare)
\r
3326 DrawPieceOnDC(hdc, piece, piece_color, -1, x, y, tmphdc);
\r
3331 if( texture_hdc != NULL ) {
\r
3332 DeleteDC( texture_hdc );
\r
3336 int saveDiagFlag = 0; FILE *diagFile; // [HGM] diag
\r
3337 void fputDW(FILE *f, int x)
\r
3339 fputc(x & 255, f);
\r
3340 fputc(x>>8 & 255, f);
\r
3341 fputc(x>>16 & 255, f);
\r
3342 fputc(x>>24 & 255, f);
\r
3345 #define MAX_CLIPS 200 /* more than enough */
\r
3348 DrawLogoOnDC(HDC hdc, RECT logoRect, HBITMAP logo)
\r
3350 // HBITMAP bufferBitmap;
\r
3355 int w = 100, h = 50;
\r
3357 if(logo == NULL) return;
\r
3358 // GetClientRect(hwndMain, &Rect);
\r
3359 // bufferBitmap = CreateCompatibleBitmap(hdc, Rect.right-Rect.left+1,
\r
3360 // Rect.bottom-Rect.top+1);
\r
3361 tmphdc = CreateCompatibleDC(hdc);
\r
3362 hbm = SelectObject(tmphdc, logo);
\r
3363 if( GetObject( logo, sizeof(bi), &bi ) > 0 ) {
\r
3367 StretchBlt(hdc, logoRect.left, logoRect.top, logoRect.right - logoRect.left,
\r
3368 logoRect.bottom - logoRect.top, tmphdc, 0, 0, w, h, SRCCOPY);
\r
3369 SelectObject(tmphdc, hbm);
\r
3373 static HDC hdcSeek;
\r
3375 // [HGM] seekgraph
\r
3376 void DrawSeekAxis( int x, int y, int xTo, int yTo )
\r
3379 HPEN hp = SelectObject( hdcSeek, gridPen );
\r
3380 MoveToEx( hdcSeek, boardRect.left+x, boardRect.top+y, &stPt );
\r
3381 LineTo( hdcSeek, boardRect.left+xTo, boardRect.top+yTo );
\r
3382 SelectObject( hdcSeek, hp );
\r
3385 // front-end wrapper for drawing functions to do rectangles
\r
3386 void DrawSeekBackground( int left, int top, int right, int bottom )
\r
3391 if (hdcSeek == NULL) {
\r
3392 hdcSeek = GetDC(hwndMain);
\r
3393 if (!appData.monoMode) {
\r
3394 SelectPalette(hdcSeek, hPal, FALSE);
\r
3395 RealizePalette(hdcSeek);
\r
3398 hp = SelectObject( hdcSeek, gridPen );
\r
3399 rc.top = boardRect.top+top; rc.left = boardRect.left+left;
\r
3400 rc.bottom = boardRect.top+bottom; rc.right = boardRect.left+right;
\r
3401 FillRect( hdcSeek, &rc, lightSquareBrush );
\r
3402 SelectObject( hdcSeek, hp );
\r
3405 // front-end wrapper for putting text in graph
\r
3406 void DrawSeekText(char *buf, int x, int y)
\r
3409 SetBkMode( hdcSeek, TRANSPARENT );
\r
3410 GetTextExtentPoint32( hdcSeek, buf, strlen(buf), &stSize );
\r
3411 TextOut( hdcSeek, boardRect.left+x-3, boardRect.top+y-stSize.cy/2, buf, strlen(buf) );
\r
3414 void DrawSeekDot(int x, int y, int color)
\r
3416 int square = color & 0x80;
\r
3417 HBRUSH oldBrush = SelectObject(hdcSeek,
\r
3418 color == 0 ? markerBrush : color == 1 ? darkSquareBrush : explodeBrush);
\r
3421 Rectangle(hdcSeek, boardRect.left+x - squareSize/9, boardRect.top+y - squareSize/9,
\r
3422 boardRect.left+x + squareSize/9, boardRect.top+y + squareSize/9);
\r
3424 Ellipse(hdcSeek, boardRect.left+x - squareSize/8, boardRect.top+y - squareSize/8,
\r
3425 boardRect.left+x + squareSize/8, boardRect.top+y + squareSize/8);
\r
3426 SelectObject(hdcSeek, oldBrush);
\r
3430 HDCDrawPosition(HDC hdc, BOOLEAN repaint, Board board)
\r
3432 static Board lastReq[2], lastDrawn[2];
\r
3433 static HighlightInfo lastDrawnHighlight, lastDrawnPremove;
\r
3434 static int lastDrawnFlipView = 0;
\r
3435 static int lastReqValid[2] = {0, 0}, lastDrawnValid[2] = {0, 0};
\r
3436 int releaseDC, x, y, x2, y2, row, column, num_clips = 0, i;
\r
3439 HBITMAP bufferBitmap;
\r
3440 HBITMAP oldBitmap;
\r
3442 HRGN clips[MAX_CLIPS];
\r
3443 ChessSquare dragged_piece = EmptySquare;
\r
3444 int nr = twoBoards*partnerUp;
\r
3446 /* I'm undecided on this - this function figures out whether a full
\r
3447 * repaint is necessary on its own, so there's no real reason to have the
\r
3448 * caller tell it that. I think this can safely be set to FALSE - but
\r
3449 * if we trust the callers not to request full repaints unnessesarily, then
\r
3450 * we could skip some clipping work. In other words, only request a full
\r
3451 * redraw when the majority of pieces have changed positions (ie. flip,
\r
3452 * gamestart and similar) --Hawk
\r
3454 Boolean fullrepaint = repaint;
\r
3456 if(DrawSeekGraph()) return; // [HG} seekgraph: suppress printing board if seek graph up
\r
3458 if( DrawPositionNeedsFullRepaint() ) {
\r
3459 fullrepaint = TRUE;
\r
3462 if (board == NULL) {
\r
3463 if (!lastReqValid[nr]) {
\r
3466 board = lastReq[nr];
\r
3468 CopyBoard(lastReq[nr], board);
\r
3469 lastReqValid[nr] = 1;
\r
3472 if (doingSizing) {
\r
3476 if (IsIconic(hwndMain)) {
\r
3480 if (hdc == NULL) {
\r
3481 hdc = GetDC(hwndMain);
\r
3482 if (!appData.monoMode) {
\r
3483 SelectPalette(hdc, hPal, FALSE);
\r
3484 RealizePalette(hdc);
\r
3488 releaseDC = FALSE;
\r
3491 /* Create some work-DCs */
\r
3492 hdcmem = CreateCompatibleDC(hdc);
\r
3493 tmphdc = CreateCompatibleDC(hdc);
\r
3495 /* If dragging is in progress, we temporarely remove the piece */
\r
3496 /* [HGM] or temporarily decrease count if stacked */
\r
3497 /* !! Moved to before board compare !! */
\r
3498 if (dragInfo.from.x >= 0 && dragInfo.pos.x >= 0) {
\r
3499 dragged_piece = board[dragInfo.from.y][dragInfo.from.x];
\r
3500 if(dragInfo.from.x == BOARD_LEFT-2 ) {
\r
3501 if(--board[dragInfo.from.y][dragInfo.from.x+1] == 0 )
\r
3502 board[dragInfo.from.y][dragInfo.from.x] = EmptySquare;
\r
3504 if(dragInfo.from.x == BOARD_RGHT+1) {
\r
3505 if(--board[dragInfo.from.y][dragInfo.from.x-1] == 0 )
\r
3506 board[dragInfo.from.y][dragInfo.from.x] = EmptySquare;
\r
3508 board[dragInfo.from.y][dragInfo.from.x] = EmptySquare;
\r
3511 /* Figure out which squares need updating by comparing the
\r
3512 * newest board with the last drawn board and checking if
\r
3513 * flipping has changed.
\r
3515 if (!fullrepaint && lastDrawnValid[nr] && (nr == 1 || lastDrawnFlipView == flipView)) {
\r
3516 for (row = 0; row < BOARD_HEIGHT; row++) { /* [HGM] true size, not 8 */
\r
3517 for (column = 0; column < BOARD_WIDTH; column++) {
\r
3518 if (lastDrawn[nr][row][column] != board[row][column]) {
\r
3519 SquareToPos(row, column, &x, &y);
\r
3520 clips[num_clips++] =
\r
3521 CreateRectRgn(x, y, x + squareSize, y + squareSize);
\r
3525 if(nr == 0) { // [HGM] dual: no highlights on second board
\r
3526 for (i=0; i<2; i++) {
\r
3527 if (lastDrawnHighlight.sq[i].x != highlightInfo.sq[i].x ||
\r
3528 lastDrawnHighlight.sq[i].y != highlightInfo.sq[i].y) {
\r
3529 if (lastDrawnHighlight.sq[i].x >= 0 &&
\r
3530 lastDrawnHighlight.sq[i].y >= 0) {
\r
3531 SquareToPos(lastDrawnHighlight.sq[i].y,
\r
3532 lastDrawnHighlight.sq[i].x, &x, &y);
\r
3533 clips[num_clips++] =
\r
3534 CreateRectRgn(x - lineGap, y - lineGap,
\r
3535 x + squareSize + lineGap, y + squareSize + lineGap);
\r
3537 if (highlightInfo.sq[i].x >= 0 && highlightInfo.sq[i].y >= 0) {
\r
3538 SquareToPos(highlightInfo.sq[i].y, highlightInfo.sq[i].x, &x, &y);
\r
3539 clips[num_clips++] =
\r
3540 CreateRectRgn(x - lineGap, y - lineGap,
\r
3541 x + squareSize + lineGap, y + squareSize + lineGap);
\r
3545 for (i=0; i<2; i++) {
\r
3546 if (lastDrawnPremove.sq[i].x != premoveHighlightInfo.sq[i].x ||
\r
3547 lastDrawnPremove.sq[i].y != premoveHighlightInfo.sq[i].y) {
\r
3548 if (lastDrawnPremove.sq[i].x >= 0 &&
\r
3549 lastDrawnPremove.sq[i].y >= 0) {
\r
3550 SquareToPos(lastDrawnPremove.sq[i].y,
\r
3551 lastDrawnPremove.sq[i].x, &x, &y);
\r
3552 clips[num_clips++] =
\r
3553 CreateRectRgn(x - lineGap, y - lineGap,
\r
3554 x + squareSize + lineGap, y + squareSize + lineGap);
\r
3556 if (premoveHighlightInfo.sq[i].x >= 0 &&
\r
3557 premoveHighlightInfo.sq[i].y >= 0) {
\r
3558 SquareToPos(premoveHighlightInfo.sq[i].y,
\r
3559 premoveHighlightInfo.sq[i].x, &x, &y);
\r
3560 clips[num_clips++] =
\r
3561 CreateRectRgn(x - lineGap, y - lineGap,
\r
3562 x + squareSize + lineGap, y + squareSize + lineGap);
\r
3566 } else { // nr == 1
\r
3567 partnerHighlightInfo.sq[0].y = board[EP_STATUS-4];
\r
3568 partnerHighlightInfo.sq[0].x = board[EP_STATUS-3];
\r
3569 partnerHighlightInfo.sq[1].y = board[EP_STATUS-2];
\r
3570 partnerHighlightInfo.sq[1].x = board[EP_STATUS-1];
\r
3571 for (i=0; i<2; i++) {
\r
3572 if (partnerHighlightInfo.sq[i].x >= 0 &&
\r
3573 partnerHighlightInfo.sq[i].y >= 0) {
\r
3574 SquareToPos(partnerHighlightInfo.sq[i].y,
\r
3575 partnerHighlightInfo.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 (oldPartnerHighlight.sq[i].x >= 0 &&
\r
3581 oldPartnerHighlight.sq[i].y >= 0) {
\r
3582 SquareToPos(oldPartnerHighlight.sq[i].y,
\r
3583 oldPartnerHighlight.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
3591 fullrepaint = TRUE;
\r
3594 /* Create a buffer bitmap - this is the actual bitmap
\r
3595 * being written to. When all the work is done, we can
\r
3596 * copy it to the real DC (the screen). This avoids
\r
3597 * the problems with flickering.
\r
3599 GetClientRect(hwndMain, &Rect);
\r
3600 bufferBitmap = CreateCompatibleBitmap(hdc, Rect.right-Rect.left+1,
\r
3601 Rect.bottom-Rect.top+1);
\r
3602 oldBitmap = SelectObject(hdcmem, bufferBitmap);
\r
3603 if (!appData.monoMode) {
\r
3604 SelectPalette(hdcmem, hPal, FALSE);
\r
3607 /* Create clips for dragging */
\r
3608 if (!fullrepaint) {
\r
3609 if (dragInfo.from.x >= 0) {
\r
3610 SquareToPos(dragInfo.from.y, dragInfo.from.x, &x, &y);
\r
3611 clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);
\r
3613 if (dragInfo.start.x >= 0) {
\r
3614 SquareToPos(dragInfo.start.y, dragInfo.start.x, &x, &y);
\r
3615 clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);
\r
3617 if (dragInfo.pos.x >= 0) {
\r
3618 x = dragInfo.pos.x - squareSize / 2;
\r
3619 y = dragInfo.pos.y - squareSize / 2;
\r
3620 clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);
\r
3622 if (dragInfo.lastpos.x >= 0) {
\r
3623 x = dragInfo.lastpos.x - squareSize / 2;
\r
3624 y = dragInfo.lastpos.y - squareSize / 2;
\r
3625 clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);
\r
3629 /* Are we animating a move?
\r
3631 * - remove the piece from the board (temporarely)
\r
3632 * - calculate the clipping region
\r
3634 if (!fullrepaint) {
\r
3635 if (animInfo.piece != EmptySquare) {
\r
3636 board[animInfo.from.y][animInfo.from.x] = EmptySquare;
\r
3637 x = boardRect.left + animInfo.lastpos.x;
\r
3638 y = boardRect.top + animInfo.lastpos.y;
\r
3639 x2 = boardRect.left + animInfo.pos.x;
\r
3640 y2 = boardRect.top + animInfo.pos.y;
\r
3641 clips[num_clips++] = CreateRectRgn(MIN(x,x2), MIN(y,y2), MAX(x,x2)+squareSize, MAX(y,y2)+squareSize);
\r
3642 /* Slight kludge. The real problem is that after AnimateMove is
\r
3643 done, the position on the screen does not match lastDrawn.
\r
3644 This currently causes trouble only on e.p. captures in
\r
3645 atomic, where the piece moves to an empty square and then
\r
3646 explodes. The old and new positions both had an empty square
\r
3647 at the destination, but animation has drawn a piece there and
\r
3648 we have to remember to erase it. [HGM] moved until after setting lastDrawn */
\r
3649 lastDrawn[0][animInfo.to.y][animInfo.to.x] = animInfo.piece;
\r
3653 /* No clips? Make sure we have fullrepaint set to TRUE */
\r
3654 if (num_clips == 0)
\r
3655 fullrepaint = TRUE;
\r
3657 /* Set clipping on the memory DC */
\r
3658 if (!fullrepaint) {
\r
3659 SelectClipRgn(hdcmem, clips[0]);
\r
3660 for (x = 1; x < num_clips; x++) {
\r
3661 if (ExtSelectClipRgn(hdcmem, clips[x], RGN_OR) == ERROR)
\r
3662 abort(); // this should never ever happen!
\r
3666 /* Do all the drawing to the memory DC */
\r
3667 if(explodeInfo.radius) { // [HGM] atomic
\r
3669 int x, y, r=(explodeInfo.radius * squareSize)/100;
\r
3670 ChessSquare piece = board[explodeInfo.fromY][explodeInfo.fromX];
\r
3671 board[explodeInfo.fromY][explodeInfo.fromX] = EmptySquare; // suppress display of capturer
\r
3672 SquareToPos(explodeInfo.toY, explodeInfo.toX, &x, &y);
\r
3673 x += squareSize/2;
\r
3674 y += squareSize/2;
\r
3675 if(!fullrepaint) {
\r
3676 clips[num_clips] = CreateRectRgn(x-r, y-r, x+r, y+r);
\r
3677 ExtSelectClipRgn(hdcmem, clips[num_clips++], RGN_OR);
\r
3679 DrawGridOnDC(hdcmem);
\r
3680 DrawHighlightsOnDC(hdcmem, &highlightInfo, HIGHLIGHT_PEN);
\r
3681 DrawHighlightsOnDC(hdcmem, &premoveHighlightInfo, PREMOVE_PEN);
\r
3682 DrawBoardOnDC(hdcmem, board, tmphdc);
\r
3683 board[explodeInfo.fromY][explodeInfo.fromX] = piece;
\r
3684 oldBrush = SelectObject(hdcmem, explodeBrush);
\r
3685 Ellipse(hdcmem, x-r, y-r, x+r, y+r);
\r
3686 SelectObject(hdcmem, oldBrush);
\r
3688 DrawGridOnDC(hdcmem);
\r
3689 if(nr == 0) { // [HGM] dual: decide which highlights to draw
\r
3690 DrawHighlightsOnDC(hdcmem, &highlightInfo, HIGHLIGHT_PEN);
\r
3691 DrawHighlightsOnDC(hdcmem, &premoveHighlightInfo, PREMOVE_PEN);
\r
3693 DrawHighlightsOnDC(hdcmem, &partnerHighlightInfo, HIGHLIGHT_PEN);
\r
3694 oldPartnerHighlight = partnerHighlightInfo;
\r
3696 DrawBoardOnDC(hdcmem, board, tmphdc);
\r
3698 if(nr == 0) // [HGM] dual: markers only on left board
\r
3699 for (row = 0; row < BOARD_HEIGHT; row++) {
\r
3700 for (column = 0; column < BOARD_WIDTH; column++) {
\r
3701 if (marker[row][column]) { // marker changes only occur with full repaint!
\r
3702 HBRUSH oldBrush = SelectObject(hdcmem,
\r
3703 marker[row][column] == 2 ? markerBrush : explodeBrush);
\r
3704 SquareToPos(row, column, &x, &y);
\r
3705 Ellipse(hdcmem, x + squareSize/4, y + squareSize/4,
\r
3706 x + 3*squareSize/4, y + 3*squareSize/4);
\r
3707 SelectObject(hdcmem, oldBrush);
\r
3712 HBITMAP whiteLogo = (HBITMAP) first.programLogo, blackLogo = (HBITMAP) second.programLogo;
\r
3713 if(appData.autoLogo) {
\r
3715 switch(gameMode) { // pick logos based on game mode
\r
3716 case IcsObserving:
\r
3717 whiteLogo = second.programLogo; // ICS logo
\r
3718 blackLogo = second.programLogo;
\r
3721 case IcsPlayingWhite:
\r
3722 if(!appData.zippyPlay) whiteLogo = userLogo;
\r
3723 blackLogo = second.programLogo; // ICS logo
\r
3725 case IcsPlayingBlack:
\r
3726 whiteLogo = second.programLogo; // ICS logo
\r
3727 blackLogo = appData.zippyPlay ? first.programLogo : userLogo;
\r
3729 case TwoMachinesPlay:
\r
3730 if(first.twoMachinesColor[0] == 'b') {
\r
3731 whiteLogo = second.programLogo;
\r
3732 blackLogo = first.programLogo;
\r
3735 case MachinePlaysWhite:
\r
3736 blackLogo = userLogo;
\r
3738 case MachinePlaysBlack:
\r
3739 whiteLogo = userLogo;
\r
3740 blackLogo = first.programLogo;
\r
3743 DrawLogoOnDC(hdc, leftLogoRect, flipClock ? blackLogo : whiteLogo);
\r
3744 DrawLogoOnDC(hdc, rightLogoRect, flipClock ? whiteLogo : blackLogo);
\r
3747 if( appData.highlightMoveWithArrow ) {
\r
3748 DrawArrowHighlight(hdcmem);
\r
3751 DrawCoordsOnDC(hdcmem);
\r
3753 CopyBoard(lastDrawn[nr], board); /* [HGM] Moved to here from end of routine, */
\r
3754 /* to make sure lastDrawn contains what is actually drawn */
\r
3756 /* Put the dragged piece back into place and draw it (out of place!) */
\r
3757 if (dragged_piece != EmptySquare) {
\r
3758 /* [HGM] or restack */
\r
3759 if(dragInfo.from.x == BOARD_LEFT-2 )
\r
3760 board[dragInfo.from.y][dragInfo.from.x+1]++;
\r
3762 if(dragInfo.from.x == BOARD_RGHT+1 )
\r
3763 board[dragInfo.from.y][dragInfo.from.x-1]++;
\r
3764 board[dragInfo.from.y][dragInfo.from.x] = dragged_piece;
\r
3765 x = dragInfo.pos.x - squareSize / 2;
\r
3766 y = dragInfo.pos.y - squareSize / 2;
\r
3767 DrawPieceOnDC(hdcmem, dragged_piece,
\r
3768 ((int) dragged_piece < (int) BlackPawn),
\r
3769 (dragInfo.from.y + dragInfo.from.x) % 2, x, y, tmphdc);
\r
3772 /* Put the animated piece back into place and draw it */
\r
3773 if (animInfo.piece != EmptySquare) {
\r
3774 board[animInfo.from.y][animInfo.from.x] = animInfo.piece;
\r
3775 x = boardRect.left + animInfo.pos.x;
\r
3776 y = boardRect.top + animInfo.pos.y;
\r
3777 DrawPieceOnDC(hdcmem, animInfo.piece,
\r
3778 ((int) animInfo.piece < (int) BlackPawn),
\r
3779 (animInfo.from.y + animInfo.from.x) % 2, x, y, tmphdc);
\r
3782 /* Release the bufferBitmap by selecting in the old bitmap
\r
3783 * and delete the memory DC
\r
3785 SelectObject(hdcmem, oldBitmap);
\r
3788 /* Set clipping on the target DC */
\r
3789 if (!fullrepaint) {
\r
3790 if(nr == 1) for (x = 0; x < num_clips; x++) { // [HGM] dual: translate clips
\r
3792 GetRgnBox(clips[x], &rect);
\r
3793 DeleteObject(clips[x]);
\r
3794 clips[x] = CreateRectRgn(rect.left + wpMain.width/2, rect.top,
\r
3795 rect.right + wpMain.width/2, rect.bottom);
\r
3797 SelectClipRgn(hdc, clips[0]);
\r
3798 for (x = 1; x < num_clips; x++) {
\r
3799 if (ExtSelectClipRgn(hdc, clips[x], RGN_OR) == ERROR)
\r
3800 abort(); // this should never ever happen!
\r
3804 /* Copy the new bitmap onto the screen in one go.
\r
3805 * This way we avoid any flickering
\r
3807 oldBitmap = SelectObject(tmphdc, bufferBitmap);
\r
3808 BitBlt(hdc, boardRect.left + twoBoards*partnerUp*wpMain.width/2, boardRect.top, // [HGM] dual
\r
3809 boardRect.right - boardRect.left,
\r
3810 boardRect.bottom - boardRect.top,
\r
3811 tmphdc, boardRect.left, boardRect.top, SRCCOPY);
\r
3812 if(saveDiagFlag) {
\r
3813 BITMAP b; int i, j=0, m, w, wb, fac=0; char pData[1000000];
\r
3814 BITMAPINFOHEADER bih; int color[16], nrColors=0;
\r
3816 GetObject(bufferBitmap, sizeof(b), &b);
\r
3817 if(b.bmWidthBytes*b.bmHeight <= 990000) {
\r
3818 bih.biSize = sizeof(BITMAPINFOHEADER);
\r
3819 bih.biWidth = b.bmWidth;
\r
3820 bih.biHeight = b.bmHeight;
\r
3822 bih.biBitCount = b.bmBitsPixel;
\r
3823 bih.biCompression = 0;
\r
3824 bih.biSizeImage = b.bmWidthBytes*b.bmHeight;
\r
3825 bih.biXPelsPerMeter = 0;
\r
3826 bih.biYPelsPerMeter = 0;
\r
3827 bih.biClrUsed = 0;
\r
3828 bih.biClrImportant = 0;
\r
3829 // fprintf(diagFile, "t=%d\nw=%d\nh=%d\nB=%d\nP=%d\nX=%d\n",
\r
3830 // b.bmType, b.bmWidth, b.bmHeight, b.bmWidthBytes, b.bmPlanes, b.bmBitsPixel);
\r
3831 GetDIBits(tmphdc,bufferBitmap,0,b.bmHeight,pData,(BITMAPINFO*)&bih,DIB_RGB_COLORS);
\r
3832 // fprintf(diagFile, "%8x\n", (int) pData);
\r
3834 wb = b.bmWidthBytes;
\r
3836 for(i=0; i<wb*(b.bmHeight - boardRect.top + OUTER_MARGIN)>>2; i++) {
\r
3837 int k = ((int*) pData)[i];
\r
3838 for(j=0; j<nrColors; j++) if(color[j] == k) break;
\r
3839 if(j >= 16) break;
\r
3841 if(j >= nrColors) nrColors = j+1;
\r
3843 if(j<16) { // 16 colors is enough. Compress to 4 bits per pixel
\r
3845 for(i=0; i<b.bmHeight - boardRect.top + OUTER_MARGIN; i++) {
\r
3846 for(w=0; w<(wb>>2); w+=2) {
\r
3847 int k = ((int*) pData)[(wb*i>>2) + w];
\r
3848 for(j=0; j<nrColors; j++) if(color[j] == k) break;
\r
3849 k = ((int*) pData)[(wb*i>>2) + w + 1];
\r
3850 for(m=0; m<nrColors; m++) if(color[m] == k) break;
\r
3851 pData[p++] = m | j<<4;
\r
3853 while(p&3) pData[p++] = 0;
\r
3856 wb = ((wb+31)>>5)<<2;
\r
3858 // write BITMAPFILEHEADER
\r
3859 fprintf(diagFile, "BM");
\r
3860 fputDW(diagFile, wb*(b.bmHeight - boardRect.top + OUTER_MARGIN)+0x36 + (fac?64:0));
\r
3861 fputDW(diagFile, 0);
\r
3862 fputDW(diagFile, 0x36 + (fac?64:0));
\r
3863 // write BITMAPINFOHEADER
\r
3864 fputDW(diagFile, 40);
\r
3865 fputDW(diagFile, b.bmWidth);
\r
3866 fputDW(diagFile, b.bmHeight - boardRect.top + OUTER_MARGIN);
\r
3867 if(fac) fputDW(diagFile, 0x040001); // planes and bits/pixel
\r
3868 else fputDW(diagFile, 0x200001); // planes and bits/pixel
\r
3869 fputDW(diagFile, 0);
\r
3870 fputDW(diagFile, 0);
\r
3871 fputDW(diagFile, 0);
\r
3872 fputDW(diagFile, 0);
\r
3873 fputDW(diagFile, 0);
\r
3874 fputDW(diagFile, 0);
\r
3875 // write color table
\r
3877 for(i=0; i<16; i++) fputDW(diagFile, color[i]);
\r
3878 // write bitmap data
\r
3879 for(i=0; i<wb*(b.bmHeight - boardRect.top + OUTER_MARGIN); i++)
\r
3880 fputc(pData[i], diagFile);
\r
3884 SelectObject(tmphdc, oldBitmap);
\r
3886 /* Massive cleanup */
\r
3887 for (x = 0; x < num_clips; x++)
\r
3888 DeleteObject(clips[x]);
\r
3891 DeleteObject(bufferBitmap);
\r
3894 ReleaseDC(hwndMain, hdc);
\r
3896 if (lastDrawnFlipView != flipView && nr == 0) {
\r
3898 CheckMenuItem(GetMenu(hwndMain),IDM_FlipView, MF_BYCOMMAND|MF_CHECKED);
\r
3900 CheckMenuItem(GetMenu(hwndMain),IDM_FlipView, MF_BYCOMMAND|MF_UNCHECKED);
\r
3903 /* CopyBoard(lastDrawn, board);*/
\r
3904 lastDrawnHighlight = highlightInfo;
\r
3905 lastDrawnPremove = premoveHighlightInfo;
\r
3906 lastDrawnFlipView = flipView;
\r
3907 lastDrawnValid[nr] = 1;
\r
3910 /* [HGM] diag: Save the current board display to the given open file and close the file */
\r
3915 saveDiagFlag = 1; diagFile = f;
\r
3916 HDCDrawPosition(NULL, TRUE, NULL);
\r
3920 // if(f != NULL) fprintf(f, "Sorry, but this feature is still in preparation\n");
\r
3927 /*---------------------------------------------------------------------------*\
\r
3928 | CLIENT PAINT PROCEDURE
\r
3929 | This is the main event-handler for the WM_PAINT message.
\r
3931 \*---------------------------------------------------------------------------*/
\r
3933 PaintProc(HWND hwnd)
\r
3939 if((hdc = BeginPaint(hwnd, &ps))) {
\r
3940 if (IsIconic(hwnd)) {
\r
3941 DrawIcon(hdc, 2, 2, iconCurrent);
\r
3943 if (!appData.monoMode) {
\r
3944 SelectPalette(hdc, hPal, FALSE);
\r
3945 RealizePalette(hdc);
\r
3947 HDCDrawPosition(hdc, 1, NULL);
\r
3948 if(twoBoards) { // [HGM] dual: also redraw other board in other orientation
\r
3949 flipView = !flipView; partnerUp = !partnerUp;
\r
3950 HDCDrawPosition(hdc, 1, NULL);
\r
3951 flipView = !flipView; partnerUp = !partnerUp;
\r
3954 SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);
\r
3955 ExtTextOut(hdc, messageRect.left, messageRect.top,
\r
3956 ETO_CLIPPED|ETO_OPAQUE,
\r
3957 &messageRect, messageText, strlen(messageText), NULL);
\r
3958 SelectObject(hdc, oldFont);
\r
3959 DisplayBothClocks();
\r
3961 EndPaint(hwnd,&ps);
\r
3969 * If the user selects on a border boundary, return -1; if off the board,
\r
3970 * return -2. Otherwise map the event coordinate to the square.
\r
3971 * The offset boardRect.left or boardRect.top must already have been
\r
3972 * subtracted from x.
\r
3974 int EventToSquare(x, limit)
\r
3982 if ((x % (squareSize + lineGap)) >= squareSize)
\r
3984 x /= (squareSize + lineGap);
\r
3996 DropEnable dropEnables[] = {
\r
3997 { 'P', DP_Pawn, N_("Pawn") },
\r
3998 { 'N', DP_Knight, N_("Knight") },
\r
3999 { 'B', DP_Bishop, N_("Bishop") },
\r
4000 { 'R', DP_Rook, N_("Rook") },
\r
4001 { 'Q', DP_Queen, N_("Queen") },
\r
4005 SetupDropMenu(HMENU hmenu)
\r
4007 int i, count, enable;
\r
4009 extern char white_holding[], black_holding[];
\r
4010 char item[MSG_SIZ];
\r
4012 for (i=0; i<sizeof(dropEnables)/sizeof(DropEnable); i++) {
\r
4013 p = strchr(gameMode == IcsPlayingWhite ? white_holding : black_holding,
\r
4014 dropEnables[i].piece);
\r
4016 while (p && *p++ == dropEnables[i].piece) count++;
\r
4017 snprintf(item, MSG_SIZ, "%s %d", T_(dropEnables[i].name), count);
\r
4018 enable = count > 0 || !appData.testLegality
\r
4019 /*!!temp:*/ || (gameInfo.variant == VariantCrazyhouse
\r
4020 && !appData.icsActive);
\r
4021 ModifyMenu(hmenu, dropEnables[i].command,
\r
4022 MF_BYCOMMAND | (enable ? MF_ENABLED : MF_GRAYED) | MF_STRING,
\r
4023 dropEnables[i].command, item);
\r
4027 void DragPieceBegin(int x, int y)
\r
4029 dragInfo.lastpos.x = boardRect.left + x;
\r
4030 dragInfo.lastpos.y = boardRect.top + y;
\r
4031 dragInfo.from.x = fromX;
\r
4032 dragInfo.from.y = fromY;
\r
4033 dragInfo.start = dragInfo.from;
\r
4034 SetCapture(hwndMain);
\r
4037 void DragPieceEnd(int x, int y)
\r
4040 dragInfo.start.x = dragInfo.start.y = -1;
\r
4041 dragInfo.from = dragInfo.start;
\r
4042 dragInfo.pos = dragInfo.lastpos = dragInfo.start;
\r
4045 /* Event handler for mouse messages */
\r
4047 MouseEvent(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
\r
4051 static int recursive = 0;
\r
4053 BOOLEAN forceFullRepaint = IsFullRepaintPreferrable(); /* [AS] */
\r
4056 if (message == WM_MBUTTONUP) {
\r
4057 /* Hideous kludge to fool TrackPopupMenu into paying attention
\r
4058 to the middle button: we simulate pressing the left button too!
\r
4060 PostMessage(hwnd, WM_LBUTTONDOWN, wParam, lParam);
\r
4061 PostMessage(hwnd, WM_LBUTTONUP, wParam, lParam);
\r
4067 pt.x = LOWORD(lParam);
\r
4068 pt.y = HIWORD(lParam);
\r
4069 x = EventToSquare(pt.x - boardRect.left, BOARD_WIDTH);
\r
4070 y = EventToSquare(pt.y - boardRect.top, BOARD_HEIGHT);
\r
4071 if (!flipView && y >= 0) {
\r
4072 y = BOARD_HEIGHT - 1 - y;
\r
4074 if (flipView && x >= 0) {
\r
4075 x = BOARD_WIDTH - 1 - x;
\r
4078 shiftKey = GetKeyState(VK_SHIFT) < 0; // [HGM] remember last shift status
\r
4080 switch (message) {
\r
4081 case WM_LBUTTONDOWN:
\r
4082 if (PtInRect((LPRECT) &whiteRect, pt)) {
\r
4083 if (gameMode == EditPosition) {
\r
4084 SetWhiteToPlayEvent();
\r
4085 } else if (gameMode == EditGame || GetKeyState(VK_SHIFT) < 0) {
\r
4086 AdjustClock(flipClock, -1);
\r
4087 } else if (gameMode == IcsPlayingBlack ||
\r
4088 gameMode == MachinePlaysWhite) {
\r
4091 } else if (PtInRect((LPRECT) &blackRect, pt)) {
\r
4092 if (gameMode == EditPosition) {
\r
4093 SetBlackToPlayEvent();
\r
4094 } else if (gameMode == EditGame || GetKeyState(VK_SHIFT) < 0) {
\r
4095 AdjustClock(!flipClock, -1);
\r
4096 } else if (gameMode == IcsPlayingWhite ||
\r
4097 gameMode == MachinePlaysBlack) {
\r
4101 dragInfo.start.x = dragInfo.start.y = -1;
\r
4102 dragInfo.from = dragInfo.start;
\r
4103 if(fromX == -1 && frozen) { // not sure where this is for
\r
4104 fromX = fromY = -1;
\r
4105 DrawPosition(forceFullRepaint || FALSE, NULL); /* [AS] */
\r
4108 LeftClick(Press, pt.x - boardRect.left, pt.y - boardRect.top);
\r
4109 DrawPosition(TRUE, NULL);
\r
4112 case WM_LBUTTONUP:
\r
4113 LeftClick(Release, pt.x - boardRect.left, pt.y - boardRect.top);
\r
4114 DrawPosition(TRUE, NULL);
\r
4117 case WM_MOUSEMOVE:
\r
4118 if(SeekGraphClick(Press, pt.x - boardRect.left, pt.y - boardRect.top, 1)) break;
\r
4119 MovePV(pt.x - boardRect.left, pt.y - boardRect.top, boardRect.bottom - boardRect.top);
\r
4120 if ((appData.animateDragging || appData.highlightDragging)
\r
4121 && (wParam & MK_LBUTTON)
\r
4122 && dragInfo.from.x >= 0)
\r
4124 BOOL full_repaint = FALSE;
\r
4126 if (appData.animateDragging) {
\r
4127 dragInfo.pos = pt;
\r
4129 if (appData.highlightDragging) {
\r
4130 SetHighlights(fromX, fromY, x, y);
\r
4131 if( IsDrawArrowEnabled() && (x < 0 || x >= BOARD_WIDTH || y < 0 || y >= BOARD_HEIGHT) ) {
\r
4132 full_repaint = TRUE;
\r
4136 DrawPosition( full_repaint, NULL);
\r
4138 dragInfo.lastpos = dragInfo.pos;
\r
4142 case WM_MOUSEWHEEL: // [DM]
\r
4143 { static int lastDir = 0; // [HGM] build in some hysteresis to avoid spurious events
\r
4144 /* Mouse Wheel is being rolled forward
\r
4145 * Play moves forward
\r
4147 if((short)HIWORD(wParam) < 0 && currentMove < forwardMostMove)
\r
4148 { if(lastDir == 1) ForwardEvent(); else lastDir = 1; } // [HGM] suppress first event in direction
\r
4149 /* Mouse Wheel is being rolled backward
\r
4150 * Play moves backward
\r
4152 if((short)HIWORD(wParam) > 0 && currentMove > backwardMostMove)
\r
4153 { if(lastDir == -1) BackwardEvent(); else lastDir = -1; }
\r
4157 case WM_MBUTTONUP:
\r
4158 case WM_RBUTTONUP:
\r
4160 RightClick(Release, pt.x - boardRect.left, pt.y - boardRect.top, &fromX, &fromY);
\r
4163 case WM_MBUTTONDOWN:
\r
4164 case WM_RBUTTONDOWN:
\r
4167 fromX = fromY = -1;
\r
4168 dragInfo.pos.x = dragInfo.pos.y = -1;
\r
4169 dragInfo.start.x = dragInfo.start.y = -1;
\r
4170 dragInfo.from = dragInfo.start;
\r
4171 dragInfo.lastpos = dragInfo.pos;
\r
4172 if (appData.highlightDragging) {
\r
4173 ClearHighlights();
\r
4176 /* [HGM] right mouse button in clock area edit-game mode ups clock */
\r
4177 if (PtInRect((LPRECT) &whiteRect, pt)) {
\r
4178 if (gameMode == EditGame || GetKeyState(VK_SHIFT) < 0) AdjustClock(flipClock, 1);
\r
4179 } else if (PtInRect((LPRECT) &blackRect, pt)) {
\r
4180 if (gameMode == EditGame || GetKeyState(VK_SHIFT) < 0) AdjustClock(!flipClock, 1);
\r
4184 DrawPosition(TRUE, NULL);
\r
4186 menuNr = RightClick(Press, pt.x - boardRect.left, pt.y - boardRect.top, &fromX, &fromY);
\r
4189 if (message == WM_MBUTTONDOWN) {
\r
4190 buttonCount = 3; /* even if system didn't think so */
\r
4191 if (wParam & MK_SHIFT)
\r
4192 MenuPopup(hwnd, pt, LoadMenu(hInst, "BlackPieceMenu"), -1);
\r
4194 MenuPopup(hwnd, pt, LoadMenu(hInst, "WhitePieceMenu"), -1);
\r
4195 } else { /* message == WM_RBUTTONDOWN */
\r
4196 /* Just have one menu, on the right button. Windows users don't
\r
4197 think to try the middle one, and sometimes other software steals
\r
4198 it, or it doesn't really exist. */
\r
4199 if(gameInfo.variant != VariantShogi)
\r
4200 MenuPopup(hwnd, pt, LoadMenu(hInst, "PieceMenu"), -1);
\r
4202 MenuPopup(hwnd, pt, LoadMenu(hInst, "ShogiPieceMenu"), -1);
\r
4206 SetCapture(hwndMain);
4209 hmenu = LoadMenu(hInst, "DropPieceMenu");
\r
4210 SetupDropMenu(hmenu);
\r
4211 MenuPopup(hwnd, pt, hmenu, -1);
\r
4221 /* Preprocess messages for buttons in main window */
\r
4223 ButtonProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
\r
4225 int id = GetWindowLong(hwnd, GWL_ID);
\r
4228 for (i=0; i<N_BUTTONS; i++) {
\r
4229 if (buttonDesc[i].id == id) break;
\r
4231 if (i == N_BUTTONS) return 0;
\r
4232 switch (message) {
\r
4237 dir = (wParam == VK_LEFT) ? -1 : 1;
\r
4238 SetFocus(buttonDesc[(i + dir + N_BUTTONS) % N_BUTTONS].hwnd);
\r
4245 SendMessage(hwndMain, WM_COMMAND, MAKEWPARAM(buttonDesc[i].id, 0), 0);
\r
4248 if (appData.icsActive && (isalpha((char)wParam) || wParam == '0')) {
\r
4249 // [HGM] movenum: only letters or leading zero should go to ICS input
\r
4250 HWND h = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
4251 if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);
\r
4253 SendMessage(h, WM_CHAR, wParam, lParam);
\r
4255 } else if (isalpha((char)wParam) || isdigit((char)wParam)){
\r
4256 PopUpMoveDialog((char)wParam);
\r
4262 return CallWindowProc(buttonDesc[i].wndproc, hwnd, message, wParam, lParam);
\r
4265 /* Process messages for Promotion dialog box */
\r
4267 Promotion(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
4271 switch (message) {
\r
4272 case WM_INITDIALOG: /* message: initialize dialog box */
\r
4273 /* Center the dialog over the application window */
\r
4274 CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));
\r
4275 Translate(hDlg, DLG_PromotionKing);
\r
4276 ShowWindow(GetDlgItem(hDlg, PB_King),
\r
4277 (!appData.testLegality || gameInfo.variant == VariantSuicide ||
\r
4278 gameInfo.variant == VariantGiveaway || gameInfo.variant == VariantSuper ) ?
\r
4279 SW_SHOW : SW_HIDE);
\r
4280 /* [HGM] Only allow C & A promotions if these pieces are defined */
\r
4281 ShowWindow(GetDlgItem(hDlg, PB_Archbishop),
\r
4282 ((PieceToChar(WhiteAngel) >= 'A' &&
\r
4283 PieceToChar(WhiteAngel) != '~') ||
\r
4284 (PieceToChar(BlackAngel) >= 'A' &&
\r
4285 PieceToChar(BlackAngel) != '~') ) ?
\r
4286 SW_SHOW : SW_HIDE);
\r
4287 ShowWindow(GetDlgItem(hDlg, PB_Chancellor),
\r
4288 ((PieceToChar(WhiteMarshall) >= 'A' &&
\r
4289 PieceToChar(WhiteMarshall) != '~') ||
\r
4290 (PieceToChar(BlackMarshall) >= 'A' &&
\r
4291 PieceToChar(BlackMarshall) != '~') ) ?
\r
4292 SW_SHOW : SW_HIDE);
\r
4293 /* [HGM] Hide B & R button in Shogi, use Q as promote, N as defer */
\r
4294 ShowWindow(GetDlgItem(hDlg, PB_Rook),
\r
4295 gameInfo.variant != VariantShogi ?
\r
4296 SW_SHOW : SW_HIDE);
\r
4297 ShowWindow(GetDlgItem(hDlg, PB_Bishop),
\r
4298 gameInfo.variant != VariantShogi ?
\r
4299 SW_SHOW : SW_HIDE);
\r
4300 ShowWindow(GetDlgItem(hDlg, IDC_Yes),
\r
4301 gameInfo.variant == VariantShogi ?
\r
4302 SW_SHOW : SW_HIDE);
\r
4303 ShowWindow(GetDlgItem(hDlg, IDC_No),
\r
4304 gameInfo.variant == VariantShogi ?
\r
4305 SW_SHOW : SW_HIDE);
\r
4306 ShowWindow(GetDlgItem(hDlg, IDC_Centaur),
\r
4307 gameInfo.variant == VariantSuper ?
\r
4308 SW_SHOW : SW_HIDE);
\r
4311 case WM_COMMAND: /* message: received a command */
\r
4312 switch (LOWORD(wParam)) {
\r
4314 EndDialog(hDlg, TRUE); /* Exit the dialog */
\r
4315 ClearHighlights();
\r
4316 DrawPosition(FALSE, NULL);
\r
4319 promoChar = gameInfo.variant == VariantSuper ? PieceToChar(BlackSilver) : PieceToChar(BlackKing);
\r
4322 promoChar = gameInfo.variant == VariantShogi ? '+' : PieceToChar(BlackQueen);
\r
4325 promoChar = PieceToChar(BlackRook);
\r
4328 promoChar = PieceToChar(BlackBishop);
\r
4330 case PB_Chancellor:
\r
4331 promoChar = PieceToChar(BlackMarshall);
\r
4333 case PB_Archbishop:
\r
4334 promoChar = PieceToChar(BlackAngel);
\r
4337 promoChar = gameInfo.variant == VariantShogi ? '=' : PieceToChar(BlackKnight);
\r
4342 EndDialog(hDlg, TRUE); /* Exit the dialog */
\r
4343 /* [HGM] <popupFix> Call FinishMove rather than UserMoveEvent, as we
\r
4344 only show the popup when we are already sure the move is valid or
\r
4345 legal. We pass a faulty move type, but the kludge is that FinishMove
\r
4346 will figure out it is a promotion from the promoChar. */
\r
4347 UserMoveEvent(fromX, fromY, toX, toY, promoChar);
\r
4348 fromX = fromY = -1;
\r
4349 if (!appData.highlightLastMove) {
\r
4350 ClearHighlights();
\r
4351 DrawPosition(FALSE, NULL);
\r
4358 /* Pop up promotion dialog */
\r
4360 PromotionPopup(HWND hwnd)
\r
4364 lpProc = MakeProcInstance((FARPROC)Promotion, hInst);
\r
4365 DialogBox(hInst, MAKEINTRESOURCE(DLG_PromotionKing),
\r
4366 hwnd, (DLGPROC)lpProc);
\r
4367 FreeProcInstance(lpProc);
\r
4373 DrawPosition(TRUE, NULL);
\r
4374 PromotionPopup(hwndMain);
\r
4377 /* Toggle ShowThinking */
\r
4379 ToggleShowThinking()
\r
4381 appData.showThinking = !appData.showThinking;
\r
4382 ShowThinkingEvent();
\r
4386 LoadGameDialog(HWND hwnd, char* title)
\r
4390 char fileTitle[MSG_SIZ];
\r
4391 f = OpenFileDialog(hwnd, "rb", "",
\r
4392 appData.oldSaveStyle ? "gam" : "pgn",
\r
4394 title, &number, fileTitle, NULL);
\r
4396 cmailMsgLoaded = FALSE;
\r
4397 if (number == 0) {
\r
4398 int error = GameListBuild(f);
\r
4400 DisplayError(_("Cannot build game list"), error);
\r
4401 } else if (!ListEmpty(&gameList) &&
\r
4402 ((ListGame *) gameList.tailPred)->number > 1) {
\r
4403 GameListPopUp(f, fileTitle);
\r
4406 GameListDestroy();
\r
4409 LoadGame(f, number, fileTitle, FALSE);
\r
4413 int get_term_width()
\r
4418 HFONT hfont, hold_font;
\r
4423 hText = GetDlgItem(hwndConsole, OPT_ConsoleText);
\r
4427 // get the text metrics
\r
4428 hdc = GetDC(hText);
\r
4429 lf = font[boardSize][CONSOLE_FONT]->lf;
\r
4430 if (consoleCF.dwEffects & CFE_BOLD)
\r
4431 lf.lfWeight = FW_BOLD;
\r
4432 if (consoleCF.dwEffects & CFE_ITALIC)
\r
4433 lf.lfItalic = TRUE;
\r
4434 if (consoleCF.dwEffects & CFE_STRIKEOUT)
\r
4435 lf.lfStrikeOut = TRUE;
\r
4436 if (consoleCF.dwEffects & CFE_UNDERLINE)
\r
4437 lf.lfUnderline = TRUE;
\r
4438 hfont = CreateFontIndirect(&lf);
\r
4439 hold_font = SelectObject(hdc, hfont);
\r
4440 GetTextMetrics(hdc, &tm);
\r
4441 SelectObject(hdc, hold_font);
\r
4442 DeleteObject(hfont);
\r
4443 ReleaseDC(hText, hdc);
\r
4445 // get the rectangle
\r
4446 SendMessage(hText, EM_GETRECT, 0, (LPARAM)&rc);
\r
4448 return (rc.right-rc.left) / tm.tmAveCharWidth;
\r
4451 void UpdateICSWidth(HWND hText)
\r
4453 LONG old_width, new_width;
\r
4455 new_width = get_term_width(hText, FALSE);
\r
4456 old_width = GetWindowLong(hText, GWL_USERDATA);
\r
4457 if (new_width != old_width)
\r
4459 ics_update_width(new_width);
\r
4460 SetWindowLong(hText, GWL_USERDATA, new_width);
\r
4465 ChangedConsoleFont()
\r
4468 CHARRANGE tmpsel, sel;
\r
4469 MyFont *f = font[boardSize][CONSOLE_FONT];
\r
4470 HWND hText = GetDlgItem(hwndConsole, OPT_ConsoleText);
\r
4471 HWND hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
4474 cfmt.cbSize = sizeof(CHARFORMAT);
\r
4475 cfmt.dwMask = CFM_FACE|CFM_SIZE|CFM_CHARSET;
\r
4476 safeStrCpy(cfmt.szFaceName, font[boardSize][CONSOLE_FONT]->mfp.faceName,
\r
4477 sizeof(cfmt.szFaceName)/sizeof(cfmt.szFaceName[0]) );
\r
4478 /* yHeight is expressed in twips. A twip is 1/20 of a font's point
\r
4479 * size. This was undocumented in the version of MSVC++ that I had
\r
4480 * when I wrote the code, but is apparently documented now.
\r
4482 cfmt.yHeight = (int)(f->mfp.pointSize * 20.0 + 0.5);
\r
4483 cfmt.bCharSet = f->lf.lfCharSet;
\r
4484 cfmt.bPitchAndFamily = f->lf.lfPitchAndFamily;
\r
4485 SendMessage(hText, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &cfmt);
\r
4486 SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &cfmt);
\r
4487 /* Why are the following seemingly needed too? */
\r
4488 SendMessage(hText, EM_SETCHARFORMAT, SCF_DEFAULT, (LPARAM) &cfmt);
\r
4489 SendMessage(hInput, EM_SETCHARFORMAT, SCF_DEFAULT, (LPARAM) &cfmt);
\r
4490 SendMessage(hText, EM_EXGETSEL, 0, (LPARAM)&sel);
\r
4492 tmpsel.cpMax = -1; /*999999?*/
\r
4493 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&tmpsel);
\r
4494 SendMessage(hText, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cfmt);
\r
4495 /* Trying putting this here too. It still seems to tickle a RichEdit
\r
4496 * bug: sometimes RichEdit indents the first line of a paragraph too.
\r
4498 paraf.cbSize = sizeof(paraf);
\r
4499 paraf.dwMask = PFM_OFFSET | PFM_STARTINDENT;
\r
4500 paraf.dxStartIndent = 0;
\r
4501 paraf.dxOffset = WRAP_INDENT;
\r
4502 SendMessage(hText, EM_SETPARAFORMAT, 0, (LPARAM) ¶f);
\r
4503 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
4504 UpdateICSWidth(hText);
\r
4507 /*---------------------------------------------------------------------------*\
\r
4509 * Window Proc for main window
\r
4511 \*---------------------------------------------------------------------------*/
\r
4513 /* Process messages for main window, etc. */
\r
4515 WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
\r
4518 int wmId, wmEvent;
\r
4522 char fileTitle[MSG_SIZ];
\r
4523 char buf[MSG_SIZ];
\r
4524 static SnapData sd;
\r
4526 switch (message) {
\r
4528 case WM_PAINT: /* message: repaint portion of window */
\r
4532 case WM_ERASEBKGND:
\r
4533 if (IsIconic(hwnd)) {
\r
4534 /* Cheat; change the message */
\r
4535 return (DefWindowProc(hwnd, WM_ICONERASEBKGND, wParam, lParam));
\r
4537 return (DefWindowProc(hwnd, message, wParam, lParam));
\r
4541 case WM_LBUTTONDOWN:
\r
4542 case WM_MBUTTONDOWN:
\r
4543 case WM_RBUTTONDOWN:
\r
4544 case WM_LBUTTONUP:
\r
4545 case WM_MBUTTONUP:
\r
4546 case WM_RBUTTONUP:
\r
4547 case WM_MOUSEMOVE:
\r
4548 case WM_MOUSEWHEEL:
\r
4549 MouseEvent(hwnd, message, wParam, lParam);
\r
4552 JAWS_KB_NAVIGATION
\r
4556 JAWS_ALT_INTERCEPT
\r
4558 if (appData.icsActive && ((char)wParam == '\r' || (char)wParam > ' ' && !((char)wParam >= '1' && (char)wParam <= '9'))) {
\r
4559 // [HGM] movenum: for non-zero digits we always do type-in dialog
\r
4560 HWND h = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
4561 if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);
\r
4563 SendMessage(h, message, wParam, lParam);
\r
4564 } else if(lParam != KF_REPEAT) {
\r
4565 if (isalpha((char)wParam) || isdigit((char)wParam)) {
\r
4566 PopUpMoveDialog((char)wParam);
\r
4567 } else if((char)wParam == 003) CopyGameToClipboard();
\r
4568 else if((char)wParam == 026) PasteGameOrFENFromClipboard();
\r
4573 case WM_PALETTECHANGED:
\r
4574 if (hwnd != (HWND)wParam && !appData.monoMode) {
\r
4576 HDC hdc = GetDC(hwndMain);
\r
4577 SelectPalette(hdc, hPal, TRUE);
\r
4578 nnew = RealizePalette(hdc);
\r
4580 paletteChanged = TRUE;
\r
4581 InvalidateRect(hwnd, &boardRect, FALSE);
\r
4583 ReleaseDC(hwnd, hdc);
\r
4587 case WM_QUERYNEWPALETTE:
\r
4588 if (!appData.monoMode /*&& paletteChanged*/) {
\r
4590 HDC hdc = GetDC(hwndMain);
\r
4591 paletteChanged = FALSE;
\r
4592 SelectPalette(hdc, hPal, FALSE);
\r
4593 nnew = RealizePalette(hdc);
\r
4595 InvalidateRect(hwnd, &boardRect, FALSE);
\r
4597 ReleaseDC(hwnd, hdc);
\r
4602 case WM_COMMAND: /* message: command from application menu */
\r
4603 wmId = LOWORD(wParam);
\r
4604 wmEvent = HIWORD(wParam);
\r
4609 SAY("new game enter a move to play against the computer with white");
\r
4612 case IDM_NewGameFRC:
\r
4613 if( NewGameFRC() == 0 ) {
\r
4618 case IDM_NewVariant:
\r
4619 NewVariantPopup(hwnd);
\r
4622 case IDM_LoadGame:
\r
4623 LoadGameDialog(hwnd, _("Load Game from File"));
\r
4626 case IDM_LoadNextGame:
\r
4630 case IDM_LoadPrevGame:
\r
4634 case IDM_ReloadGame:
\r
4638 case IDM_LoadPosition:
\r
4639 if (gameMode == AnalyzeMode || gameMode == AnalyzeFile) {
\r
4640 Reset(FALSE, TRUE);
\r
4643 f = OpenFileDialog(hwnd, "rb", "",
\r
4644 appData.oldSaveStyle ? "pos" : "fen",
\r
4646 _("Load Position from File"), &number, fileTitle, NULL);
\r
4648 LoadPosition(f, number, fileTitle);
\r
4652 case IDM_LoadNextPosition:
\r
4653 ReloadPosition(1);
\r
4656 case IDM_LoadPrevPosition:
\r
4657 ReloadPosition(-1);
\r
4660 case IDM_ReloadPosition:
\r
4661 ReloadPosition(0);
\r
4664 case IDM_SaveGame:
\r
4665 defName = DefaultFileName(appData.oldSaveStyle ? "gam" : "pgn");
\r
4666 f = OpenFileDialog(hwnd, "a", defName,
\r
4667 appData.oldSaveStyle ? "gam" : "pgn",
\r
4669 _("Save Game to File"), NULL, fileTitle, NULL);
\r
4671 SaveGame(f, 0, "");
\r
4675 case IDM_SavePosition:
\r
4676 defName = DefaultFileName(appData.oldSaveStyle ? "pos" : "fen");
\r
4677 f = OpenFileDialog(hwnd, "a", defName,
\r
4678 appData.oldSaveStyle ? "pos" : "fen",
\r
4680 _("Save Position to File"), NULL, fileTitle, NULL);
\r
4682 SavePosition(f, 0, "");
\r
4686 case IDM_SaveDiagram:
\r
4687 defName = "diagram";
\r
4688 f = OpenFileDialog(hwnd, "wb", defName,
\r
4691 "Save Diagram to File", NULL, fileTitle, NULL);
\r
4697 case IDM_CopyGame:
\r
4698 CopyGameToClipboard();
\r
4701 case IDM_PasteGame:
\r
4702 PasteGameFromClipboard();
\r
4705 case IDM_CopyGameListToClipboard:
\r
4706 CopyGameListToClipboard();
\r
4709 /* [AS] Autodetect FEN or PGN data */
\r
4710 case IDM_PasteAny:
\r
4711 PasteGameOrFENFromClipboard();
\r
4714 /* [AS] Move history */
\r
4715 case IDM_ShowMoveHistory:
\r
4716 if( MoveHistoryIsUp() ) {
\r
4717 MoveHistoryPopDown();
\r
4720 MoveHistoryPopUp();
\r
4724 /* [AS] Eval graph */
\r
4725 case IDM_ShowEvalGraph:
\r
4726 if( EvalGraphIsUp() ) {
\r
4727 EvalGraphPopDown();
\r
4731 SetFocus(hwndMain);
\r
4735 /* [AS] Engine output */
\r
4736 case IDM_ShowEngineOutput:
\r
4737 if( EngineOutputIsUp() ) {
\r
4738 EngineOutputPopDown();
\r
4741 EngineOutputPopUp();
\r
4745 /* [AS] User adjudication */
\r
4746 case IDM_UserAdjudication_White:
\r
4747 UserAdjudicationEvent( +1 );
\r
4750 case IDM_UserAdjudication_Black:
\r
4751 UserAdjudicationEvent( -1 );
\r
4754 case IDM_UserAdjudication_Draw:
\r
4755 UserAdjudicationEvent( 0 );
\r
4758 /* [AS] Game list options dialog */
\r
4759 case IDM_GameListOptions:
\r
4760 GameListOptions();
\r
4767 case IDM_CopyPosition:
\r
4768 CopyFENToClipboard();
\r
4771 case IDM_PastePosition:
\r
4772 PasteFENFromClipboard();
\r
4775 case IDM_MailMove:
\r
4779 case IDM_ReloadCMailMsg:
\r
4780 Reset(TRUE, TRUE);
\r
4781 ReloadCmailMsgEvent(FALSE);
\r
4784 case IDM_Minimize:
\r
4785 ShowWindow(hwnd, SW_MINIMIZE);
\r
4792 case IDM_MachineWhite:
\r
4793 MachineWhiteEvent();
\r
4795 * refresh the tags dialog only if it's visible
\r
4797 if (gameMode == MachinePlaysWhite && IsWindowVisible(editTagsDialog)) {
\r
4799 tags = PGNTags(&gameInfo);
\r
4800 TagsPopUp(tags, CmailMsg());
\r
4803 SAY("computer starts playing white");
\r
4806 case IDM_MachineBlack:
\r
4807 MachineBlackEvent();
\r
4809 * refresh the tags dialog only if it's visible
\r
4811 if (gameMode == MachinePlaysBlack && IsWindowVisible(editTagsDialog)) {
\r
4813 tags = PGNTags(&gameInfo);
\r
4814 TagsPopUp(tags, CmailMsg());
\r
4817 SAY("computer starts playing black");
\r
4820 case IDM_Match: // [HGM] match: flows into next case, after setting Match Mode and nr of Games
\r
4821 if(gameMode != BeginningOfGame) break; // allow menu item to remain enabled for better mode highligting
\r
4822 matchMode = 2;// distinguish from command-line-triggered case (matchMode=1)
\r
4823 appData.matchGames = appData.defaultMatchGames;
\r
4826 case IDM_TwoMachines:
\r
4827 TwoMachinesEvent();
\r
4829 * refresh the tags dialog only if it's visible
\r
4831 if (gameMode == TwoMachinesPlay && IsWindowVisible(editTagsDialog)) {
\r
4833 tags = PGNTags(&gameInfo);
\r
4834 TagsPopUp(tags, CmailMsg());
\r
4837 SAY("computer starts playing both sides");
\r
4840 case IDM_AnalysisMode:
\r
4841 if (!first.analysisSupport) {
\r
4842 snprintf(buf, MSG_SIZ, _("%s does not support analysis"), first.tidy);
\r
4843 DisplayError(buf, 0);
\r
4845 SAY("analyzing current position");
\r
4846 /* [DM] icsEngineAnlyze [HGM] Why is this front-end??? */
\r
4847 if (appData.icsActive) {
\r
4848 if (gameMode != IcsObserving) {
\r
4849 snprintf(buf, MSG_SIZ, "You are not observing a game");
\r
4850 DisplayError(buf, 0);
\r
4851 /* secure check */
\r
4852 if (appData.icsEngineAnalyze) {
\r
4853 if (appData.debugMode)
\r
4854 fprintf(debugFP, "Found unexpected active ICS engine analyze \n");
\r
4855 ExitAnalyzeMode();
\r
4861 /* if enable, user want disable icsEngineAnalyze */
\r
4862 if (appData.icsEngineAnalyze) {
\r
4863 ExitAnalyzeMode();
\r
4867 appData.icsEngineAnalyze = TRUE;
\r
4868 if (appData.debugMode) fprintf(debugFP, "ICS engine analyze starting...\n");
\r
4871 if (!appData.showThinking) ToggleShowThinking();
\r
4872 AnalyzeModeEvent();
\r
4876 case IDM_AnalyzeFile:
\r
4877 if (!first.analysisSupport) {
\r
4878 char buf[MSG_SIZ];
\r
4879 snprintf(buf, MSG_SIZ, _("%s does not support analysis"), first.tidy);
\r
4880 DisplayError(buf, 0);
\r
4882 if (!appData.showThinking) ToggleShowThinking();
\r
4883 AnalyzeFileEvent();
\r
4884 LoadGameDialog(hwnd, _("Analyze Game from File"));
\r
4885 AnalysisPeriodicEvent(1);
\r
4889 case IDM_IcsClient:
\r
4893 case IDM_EditGame:
\r
4898 case IDM_EditPosition:
\r
4899 EditPositionEvent();
\r
4900 SAY("enter a FEN string or setup a position on the board using the control R pop up menu");
\r
4903 case IDM_Training:
\r
4907 case IDM_ShowGameList:
\r
4908 ShowGameListProc();
\r
4911 case IDM_EditTags:
\r
4915 case IDM_EditComment:
\r
4916 if (commentUp && editComment) {
\r
4919 EditCommentEvent();
\r
4939 case IDM_CallFlag:
\r
4959 case IDM_StopObserving:
\r
4960 StopObservingEvent();
\r
4963 case IDM_StopExamining:
\r
4964 StopExaminingEvent();
\r
4968 UploadGameEvent();
\r
4971 case IDM_TypeInMove:
\r
4972 PopUpMoveDialog('\000');
\r
4975 case IDM_TypeInName:
\r
4976 PopUpNameDialog('\000');
\r
4979 case IDM_Backward:
\r
4981 SetFocus(hwndMain);
\r
4988 SetFocus(hwndMain);
\r
4993 SetFocus(hwndMain);
\r
4998 SetFocus(hwndMain);
\r
5002 RevertEvent(FALSE);
\r
5005 case IDM_Annotate: // [HGM] vari: revert with annotation
\r
5006 RevertEvent(TRUE);
\r
5009 case IDM_TruncateGame:
\r
5010 TruncateGameEvent();
\r
5017 case IDM_RetractMove:
\r
5018 RetractMoveEvent();
\r
5021 case IDM_FlipView:
\r
5022 flipView = !flipView;
\r
5023 DrawPosition(FALSE, NULL);
\r
5026 case IDM_FlipClock:
\r
5027 flipClock = !flipClock;
\r
5028 DisplayBothClocks();
\r
5029 DrawPosition(FALSE, NULL);
\r
5032 case IDM_MuteSounds:
\r
5033 mute = !mute; // [HGM] mute: keep track of global muting variable
\r
5034 CheckMenuItem(GetMenu(hwndMain),IDM_MuteSounds,
\r
5035 MF_BYCOMMAND|(mute?MF_CHECKED:MF_UNCHECKED));
\r
5038 case IDM_GeneralOptions:
\r
5039 GeneralOptionsPopup(hwnd);
\r
5040 DrawPosition(TRUE, NULL);
\r
5043 case IDM_BoardOptions:
\r
5044 BoardOptionsPopup(hwnd);
\r
5047 case IDM_EnginePlayOptions:
\r
5048 EnginePlayOptionsPopup(hwnd);
\r
5051 case IDM_Engine1Options:
\r
5052 EngineOptionsPopup(hwnd, &first);
\r
5055 case IDM_Engine2Options:
\r
5057 if(WaitForSecond(SettingsMenuIfReady)) break;
\r
5058 EngineOptionsPopup(hwnd, &second);
\r
5061 case IDM_OptionsUCI:
\r
5062 UciOptionsPopup(hwnd);
\r
5065 case IDM_IcsOptions:
\r
5066 IcsOptionsPopup(hwnd);
\r
5070 FontsOptionsPopup(hwnd);
\r
5074 SoundOptionsPopup(hwnd);
\r
5077 case IDM_CommPort:
\r
5078 CommPortOptionsPopup(hwnd);
\r
5081 case IDM_LoadOptions:
\r
5082 LoadOptionsPopup(hwnd);
\r
5085 case IDM_SaveOptions:
\r
5086 SaveOptionsPopup(hwnd);
\r
5089 case IDM_TimeControl:
\r
5090 TimeControlOptionsPopup(hwnd);
\r
5093 case IDM_SaveSettings:
\r
5094 SaveSettings(settingsFileName);
\r
5097 case IDM_SaveSettingsOnExit:
\r
5098 saveSettingsOnExit = !saveSettingsOnExit;
\r
5099 (void) CheckMenuItem(GetMenu(hwndMain), IDM_SaveSettingsOnExit,
\r
5100 MF_BYCOMMAND|(saveSettingsOnExit ?
\r
5101 MF_CHECKED : MF_UNCHECKED));
\r
5112 case IDM_AboutGame:
\r
5117 appData.debugMode = !appData.debugMode;
\r
5118 if (appData.debugMode) {
\r
5119 char dir[MSG_SIZ];
\r
5120 GetCurrentDirectory(MSG_SIZ, dir);
\r
5121 SetCurrentDirectory(installDir);
\r
5122 debugFP = fopen(appData.nameOfDebugFile, "w");
\r
5123 SetCurrentDirectory(dir);
\r
5124 setbuf(debugFP, NULL);
\r
5131 case IDM_HELPCONTENTS:
\r
5132 if (!MyHelp (hwnd, "winboard.hlp", HELP_KEY,(DWORD)(LPSTR)"CONTENTS") &&
\r
5133 !HtmlHelp(hwnd, "winboard.chm", 0, 0) ) {
\r
5134 MessageBox (GetFocus(),
\r
5135 _("Unable to activate help"),
\r
5136 szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);
\r
5140 case IDM_HELPSEARCH:
\r
5141 if (!MyHelp (hwnd, "winboard.hlp", HELP_PARTIALKEY, (DWORD)(LPSTR)"") &&
\r
5142 !HtmlHelp(hwnd, "winboard.chm", 0, 0) ) {
\r
5143 MessageBox (GetFocus(),
\r
5144 _("Unable to activate help"),
\r
5145 szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);
\r
5149 case IDM_HELPHELP:
\r
5150 if(!WinHelp(hwnd, (LPSTR)NULL, HELP_HELPONHELP, 0)) {
\r
5151 MessageBox (GetFocus(),
\r
5152 _("Unable to activate help"),
\r
5153 szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);
\r
5158 lpProc = MakeProcInstance((FARPROC)About, hInst);
\r
5160 (gameInfo.event && strcmp(gameInfo.event, "Easter Egg Hunt") == 0) ?
\r
5161 "AboutBox2" : "AboutBox", hwnd, (DLGPROC)lpProc);
\r
5162 FreeProcInstance(lpProc);
\r
5165 case IDM_DirectCommand1:
\r
5166 AskQuestionEvent(_("Direct Command"),
\r
5167 _("Send to chess program:"), "", "1");
\r
5169 case IDM_DirectCommand2:
\r
5170 AskQuestionEvent(_("Direct Command"),
\r
5171 _("Send to second chess program:"), "", "2");
\r
5174 case EP_WhitePawn:
\r
5175 EditPositionMenuEvent(WhitePawn, fromX, fromY);
\r
5176 fromX = fromY = -1;
\r
5179 case EP_WhiteKnight:
\r
5180 EditPositionMenuEvent(WhiteKnight, fromX, fromY);
\r
5181 fromX = fromY = -1;
\r
5184 case EP_WhiteBishop:
\r
5185 EditPositionMenuEvent(WhiteBishop, fromX, fromY);
\r
5186 fromX = fromY = -1;
\r
5189 case EP_WhiteRook:
\r
5190 EditPositionMenuEvent(WhiteRook, fromX, fromY);
\r
5191 fromX = fromY = -1;
\r
5194 case EP_WhiteQueen:
\r
5195 EditPositionMenuEvent(WhiteQueen, fromX, fromY);
\r
5196 fromX = fromY = -1;
\r
5199 case EP_WhiteFerz:
\r
5200 EditPositionMenuEvent(WhiteFerz, fromX, fromY);
\r
5201 fromX = fromY = -1;
\r
5204 case EP_WhiteWazir:
\r
5205 EditPositionMenuEvent(WhiteWazir, fromX, fromY);
\r
5206 fromX = fromY = -1;
\r
5209 case EP_WhiteAlfil:
\r
5210 EditPositionMenuEvent(WhiteAlfil, fromX, fromY);
\r
5211 fromX = fromY = -1;
\r
5214 case EP_WhiteCannon:
\r
5215 EditPositionMenuEvent(WhiteCannon, fromX, fromY);
\r
5216 fromX = fromY = -1;
\r
5219 case EP_WhiteCardinal:
\r
5220 EditPositionMenuEvent(WhiteAngel, fromX, fromY);
\r
5221 fromX = fromY = -1;
\r
5224 case EP_WhiteMarshall:
\r
5225 EditPositionMenuEvent(WhiteMarshall, fromX, fromY);
\r
5226 fromX = fromY = -1;
\r
5229 case EP_WhiteKing:
\r
5230 EditPositionMenuEvent(WhiteKing, fromX, fromY);
\r
5231 fromX = fromY = -1;
\r
5234 case EP_BlackPawn:
\r
5235 EditPositionMenuEvent(BlackPawn, fromX, fromY);
\r
5236 fromX = fromY = -1;
\r
5239 case EP_BlackKnight:
\r
5240 EditPositionMenuEvent(BlackKnight, fromX, fromY);
\r
5241 fromX = fromY = -1;
\r
5244 case EP_BlackBishop:
\r
5245 EditPositionMenuEvent(BlackBishop, fromX, fromY);
\r
5246 fromX = fromY = -1;
\r
5249 case EP_BlackRook:
\r
5250 EditPositionMenuEvent(BlackRook, fromX, fromY);
\r
5251 fromX = fromY = -1;
\r
5254 case EP_BlackQueen:
\r
5255 EditPositionMenuEvent(BlackQueen, fromX, fromY);
\r
5256 fromX = fromY = -1;
\r
5259 case EP_BlackFerz:
\r
5260 EditPositionMenuEvent(BlackFerz, fromX, fromY);
\r
5261 fromX = fromY = -1;
\r
5264 case EP_BlackWazir:
\r
5265 EditPositionMenuEvent(BlackWazir, fromX, fromY);
\r
5266 fromX = fromY = -1;
\r
5269 case EP_BlackAlfil:
\r
5270 EditPositionMenuEvent(BlackAlfil, fromX, fromY);
\r
5271 fromX = fromY = -1;
\r
5274 case EP_BlackCannon:
\r
5275 EditPositionMenuEvent(BlackCannon, fromX, fromY);
\r
5276 fromX = fromY = -1;
\r
5279 case EP_BlackCardinal:
\r
5280 EditPositionMenuEvent(BlackAngel, fromX, fromY);
\r
5281 fromX = fromY = -1;
\r
5284 case EP_BlackMarshall:
\r
5285 EditPositionMenuEvent(BlackMarshall, fromX, fromY);
\r
5286 fromX = fromY = -1;
\r
5289 case EP_BlackKing:
\r
5290 EditPositionMenuEvent(BlackKing, fromX, fromY);
\r
5291 fromX = fromY = -1;
\r
5294 case EP_EmptySquare:
\r
5295 EditPositionMenuEvent(EmptySquare, fromX, fromY);
\r
5296 fromX = fromY = -1;
\r
5299 case EP_ClearBoard:
\r
5300 EditPositionMenuEvent(ClearBoard, fromX, fromY);
\r
5301 fromX = fromY = -1;
\r
5305 EditPositionMenuEvent(WhitePlay, fromX, fromY);
\r
5306 fromX = fromY = -1;
\r
5310 EditPositionMenuEvent(BlackPlay, fromX, fromY);
\r
5311 fromX = fromY = -1;
\r
5315 EditPositionMenuEvent(PromotePiece, fromX, fromY);
\r
5316 fromX = fromY = -1;
\r
5320 EditPositionMenuEvent(DemotePiece, fromX, fromY);
\r
5321 fromX = fromY = -1;
\r
5325 DropMenuEvent(WhitePawn, fromX, fromY);
\r
5326 fromX = fromY = -1;
\r
5330 DropMenuEvent(WhiteKnight, fromX, fromY);
\r
5331 fromX = fromY = -1;
\r
5335 DropMenuEvent(WhiteBishop, fromX, fromY);
\r
5336 fromX = fromY = -1;
\r
5340 DropMenuEvent(WhiteRook, fromX, fromY);
\r
5341 fromX = fromY = -1;
\r
5345 DropMenuEvent(WhiteQueen, fromX, fromY);
\r
5346 fromX = fromY = -1;
\r
5351 TranslateMenus(0);
\r
5352 CheckMenuItem(GetMenu(hwndMain), lastChecked, MF_BYCOMMAND|MF_UNCHECKED);
\r
5353 CheckMenuItem(GetMenu(hwndMain), IDM_English, MF_BYCOMMAND|MF_CHECKED);
\r
5354 lastChecked = wmId;
\r
5358 if(wmId > IDM_English && wmId < IDM_English+5) {
\r
5359 LoadLanguageFile(languageFile[wmId - IDM_English - 1]);
\r
5360 TranslateMenus(0);
\r
5361 CheckMenuItem(GetMenu(hwndMain), lastChecked, MF_BYCOMMAND|MF_UNCHECKED);
\r
5362 CheckMenuItem(GetMenu(hwndMain), wmId, MF_BYCOMMAND|MF_CHECKED);
\r
5363 lastChecked = wmId;
\r
5366 return (DefWindowProc(hwnd, message, wParam, lParam));
\r
5372 case CLOCK_TIMER_ID:
\r
5373 KillTimer(hwnd, clockTimerEvent); /* Simulate one-shot timer as in X */
\r
5374 clockTimerEvent = 0;
\r
5375 DecrementClocks(); /* call into back end */
\r
5377 case LOAD_GAME_TIMER_ID:
\r
5378 KillTimer(hwnd, loadGameTimerEvent); /* Simulate one-shot timer as in X*/
\r
5379 loadGameTimerEvent = 0;
\r
5380 AutoPlayGameLoop(); /* call into back end */
\r
5382 case ANALYSIS_TIMER_ID:
\r
5383 if ((gameMode == AnalyzeMode || gameMode == AnalyzeFile
\r
5384 || appData.icsEngineAnalyze) && appData.periodicUpdates) {
\r
5385 AnalysisPeriodicEvent(0);
\r
5387 KillTimer(hwnd, analysisTimerEvent);
\r
5388 analysisTimerEvent = 0;
\r
5391 case DELAYED_TIMER_ID:
\r
5392 KillTimer(hwnd, delayedTimerEvent);
\r
5393 delayedTimerEvent = 0;
\r
5394 delayedTimerCallback();
\r
5399 case WM_USER_Input:
\r
5400 InputEvent(hwnd, message, wParam, lParam);
\r
5403 /* [AS] Also move "attached" child windows */
\r
5404 case WM_WINDOWPOSCHANGING:
\r
5406 if( hwnd == hwndMain && appData.useStickyWindows ) {
\r
5407 LPWINDOWPOS lpwp = (LPWINDOWPOS) lParam;
\r
5409 if( ((lpwp->flags & SWP_NOMOVE) == 0) && ((lpwp->flags & SWP_NOSIZE) != 0) ) {
\r
5410 /* Window is moving */
\r
5413 // GetWindowRect( hwnd, &rcMain ); //[HGM] sticky: in XP this returned new position, not old
\r
5414 rcMain.left = wpMain.x; // replace by these 4 lines to reconstruct old rect
\r
5415 rcMain.right = wpMain.x + wpMain.width;
\r
5416 rcMain.top = wpMain.y;
\r
5417 rcMain.bottom = wpMain.y + wpMain.height;
\r
5419 ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, moveHistoryDialog, &wpMoveHistory );
\r
5420 ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, evalGraphDialog, &wpEvalGraph );
\r
5421 ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, engineOutputDialog, &wpEngineOutput );
\r
5422 ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, gameListDialog, &wpGameList );
\r
5423 ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, hwndConsole, &wpConsole );
\r
5424 wpMain.x = lpwp->x;
\r
5425 wpMain.y = lpwp->y;
\r
5430 /* [AS] Snapping */
\r
5431 case WM_ENTERSIZEMOVE:
\r
5432 if(appData.debugMode) { fprintf(debugFP, "size-move\n"); }
\r
5433 if (hwnd == hwndMain) {
\r
5434 doingSizing = TRUE;
\r
5437 return OnEnterSizeMove( &sd, hwnd, wParam, lParam );
\r
5441 if(appData.debugMode) { fprintf(debugFP, "sizing\n"); }
\r
5442 if (hwnd == hwndMain) {
\r
5443 lastSizing = wParam;
\r
5448 if(appData.debugMode) { fprintf(debugFP, "moving\n"); }
\r
5449 return OnMoving( &sd, hwnd, wParam, lParam );
\r
5451 case WM_EXITSIZEMOVE:
\r
5452 if(appData.debugMode) { fprintf(debugFP, "exit size-move, size = %d\n", squareSize); }
\r
5453 if (hwnd == hwndMain) {
\r
5455 doingSizing = FALSE;
\r
5456 InvalidateRect(hwnd, &boardRect, FALSE);
\r
5457 GetClientRect(hwnd, &client);
\r
5458 ResizeBoard(client.right, client.bottom, lastSizing);
\r
5460 if(appData.debugMode) { fprintf(debugFP, "square size = %d\n", squareSize); }
\r
5462 return OnExitSizeMove( &sd, hwnd, wParam, lParam );
\r
5465 case WM_DESTROY: /* message: window being destroyed */
\r
5466 PostQuitMessage(0);
\r
5470 if (hwnd == hwndMain) {
\r
5475 default: /* Passes it on if unprocessed */
\r
5476 return (DefWindowProc(hwnd, message, wParam, lParam));
\r
5481 /*---------------------------------------------------------------------------*\
\r
5483 * Misc utility routines
\r
5485 \*---------------------------------------------------------------------------*/
\r
5488 * Decent random number generator, at least not as bad as Windows
\r
5489 * standard rand, which returns a value in the range 0 to 0x7fff.
\r
5491 unsigned int randstate;
\r
5496 randstate = randstate * 1664525 + 1013904223;
\r
5497 return (int) randstate & 0x7fffffff;
\r
5501 mysrandom(unsigned int seed)
\r
5508 * returns TRUE if user selects a different color, FALSE otherwise
\r
5512 ChangeColor(HWND hwnd, COLORREF *which)
\r
5514 static BOOL firstTime = TRUE;
\r
5515 static DWORD customColors[16];
\r
5517 COLORREF newcolor;
\r
5522 /* Make initial colors in use available as custom colors */
\r
5523 /* Should we put the compiled-in defaults here instead? */
\r
5525 customColors[i++] = lightSquareColor & 0xffffff;
\r
5526 customColors[i++] = darkSquareColor & 0xffffff;
\r
5527 customColors[i++] = whitePieceColor & 0xffffff;
\r
5528 customColors[i++] = blackPieceColor & 0xffffff;
\r
5529 customColors[i++] = highlightSquareColor & 0xffffff;
\r
5530 customColors[i++] = premoveHighlightColor & 0xffffff;
\r
5532 for (ccl = (ColorClass) 0; ccl < NColorClasses && i < 16; ccl++) {
\r
5533 customColors[i++] = textAttribs[ccl].color;
\r
5535 while (i < 16) customColors[i++] = RGB(255, 255, 255);
\r
5536 firstTime = FALSE;
\r
5539 cc.lStructSize = sizeof(cc);
\r
5540 cc.hwndOwner = hwnd;
\r
5541 cc.hInstance = NULL;
\r
5542 cc.rgbResult = (DWORD) (*which & 0xffffff);
\r
5543 cc.lpCustColors = (LPDWORD) customColors;
\r
5544 cc.Flags = CC_RGBINIT|CC_FULLOPEN;
\r
5546 if (!ChooseColor(&cc)) return FALSE;
\r
5548 newcolor = (COLORREF) (0x2000000 | cc.rgbResult);
\r
5549 if (newcolor == *which) return FALSE;
\r
5550 *which = newcolor;
\r
5554 InitDrawingColors();
\r
5555 InvalidateRect(hwnd, &boardRect, FALSE);
\r
5560 MyLoadSound(MySound *ms)
\r
5566 if (ms->data) free(ms->data);
\r
5569 switch (ms->name[0]) {
\r
5575 /* System sound from Control Panel. Don't preload here. */
\r
5579 if (ms->name[1] == NULLCHAR) {
\r
5580 /* "!" alone = silence */
\r
5583 /* Builtin wave resource. Error if not found. */
\r
5584 HANDLE h = FindResource(hInst, ms->name + 1, "WAVE");
\r
5585 if (h == NULL) break;
\r
5586 ms->data = (void *)LoadResource(hInst, h);
\r
5587 if (h == NULL) break;
\r
5592 /* .wav file. Error if not found. */
\r
5593 f = fopen(ms->name, "rb");
\r
5594 if (f == NULL) break;
\r
5595 if (fstat(fileno(f), &st) < 0) break;
\r
5596 ms->data = malloc(st.st_size);
\r
5597 if (fread(ms->data, st.st_size, 1, f) < 1) break;
\r
5603 char buf[MSG_SIZ];
\r
5604 snprintf(buf, MSG_SIZ, _("Error loading sound %s"), ms->name);
\r
5605 DisplayError(buf, GetLastError());
\r
5611 MyPlaySound(MySound *ms)
\r
5613 BOOLEAN ok = FALSE;
\r
5615 if(mute) return TRUE; // [HGM] mute: suppress all sound play when muted
\r
5616 switch (ms->name[0]) {
\r
5618 if(appData.debugMode) fprintf(debugFP, "silence\n");
\r
5623 /* System sound from Control Panel (deprecated feature).
\r
5624 "$" alone or an unset sound name gets default beep (still in use). */
\r
5625 if (ms->name[1]) {
\r
5626 ok = PlaySound(ms->name + 1, NULL, SND_ALIAS|SND_ASYNC);
\r
5628 if (!ok) ok = MessageBeep(MB_OK);
\r
5631 /* Builtin wave resource, or "!" alone for silence */
\r
5632 if (ms->name[1]) {
\r
5633 if (ms->data == NULL) return FALSE;
\r
5634 ok = PlaySound(ms->data, NULL, SND_MEMORY|SND_ASYNC);
\r
5640 /* .wav file. Error if not found. */
\r
5641 if (ms->data == NULL) return FALSE;
\r
5642 ok = PlaySound(ms->data, NULL, SND_MEMORY|SND_ASYNC);
\r
5645 /* Don't print an error: this can happen innocently if the sound driver
\r
5646 is busy; for instance, if another instance of WinBoard is playing
\r
5647 a sound at about the same time. */
\r
5653 OldOpenFileHook(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
5656 OPENFILENAME *ofn;
\r
5657 static UINT *number; /* gross that this is static */
\r
5659 switch (message) {
\r
5660 case WM_INITDIALOG: /* message: initialize dialog box */
\r
5661 /* Center the dialog over the application window */
\r
5662 ofn = (OPENFILENAME *) lParam;
\r
5663 if (ofn->Flags & OFN_ENABLETEMPLATE) {
\r
5664 number = (UINT *) ofn->lCustData;
\r
5665 SendMessage(GetDlgItem(hDlg, edt2), WM_SETTEXT, 0, (LPARAM) "");
\r
5669 CenterWindow (hDlg, GetWindow (hDlg, GW_OWNER));
\r
5670 Translate(hDlg, 1536);
\r
5671 return FALSE; /* Allow for further processing */
\r
5674 if ((LOWORD(wParam) == IDOK) && (number != NULL)) {
\r
5675 *number = GetDlgItemInt(hDlg, OPT_IndexNumberOld, &ok, FALSE);
\r
5677 return FALSE; /* Allow for further processing */
\r
5683 OpenFileHook(HWND hdlg, UINT uiMsg, WPARAM wParam, LPARAM lParam)
\r
5685 static UINT *number;
\r
5686 OPENFILENAME *ofname;
\r
5689 case WM_INITDIALOG:
\r
5690 Translate(hdlg, DLG_IndexNumber);
\r
5691 ofname = (OPENFILENAME *)lParam;
\r
5692 number = (UINT *)(ofname->lCustData);
\r
5695 ofnot = (OFNOTIFY *)lParam;
\r
5696 if (ofnot->hdr.code == CDN_FILEOK) {
\r
5697 *number = GetDlgItemInt(hdlg, OPT_IndexNumber, NULL, FALSE);
\r
5706 OpenFileDialog(HWND hwnd, char *write, char *defName, char *defExt, // [HGM] diag: type of 'write' now string
\r
5707 char *nameFilt, char *dlgTitle, UINT *number,
\r
5708 char fileTitle[MSG_SIZ], char fileName[MSG_SIZ])
\r
5710 OPENFILENAME openFileName;
\r
5711 char buf1[MSG_SIZ];
\r
5714 if (fileName == NULL) fileName = buf1;
\r
5715 if (defName == NULL) {
\r
5716 safeStrCpy(fileName, "*.", 3 );
\r
5717 strcat(fileName, defExt);
\r
5719 safeStrCpy(fileName, defName, MSG_SIZ );
\r
5721 if (fileTitle) safeStrCpy(fileTitle, "", MSG_SIZ );
\r
5722 if (number) *number = 0;
\r
5724 openFileName.lStructSize = sizeof(OPENFILENAME);
\r
5725 openFileName.hwndOwner = hwnd;
\r
5726 openFileName.hInstance = (HANDLE) hInst;
\r
5727 openFileName.lpstrFilter = nameFilt;
\r
5728 openFileName.lpstrCustomFilter = (LPSTR) NULL;
\r
5729 openFileName.nMaxCustFilter = 0L;
\r
5730 openFileName.nFilterIndex = 1L;
\r
5731 openFileName.lpstrFile = fileName;
\r
5732 openFileName.nMaxFile = MSG_SIZ;
\r
5733 openFileName.lpstrFileTitle = fileTitle;
\r
5734 openFileName.nMaxFileTitle = fileTitle ? MSG_SIZ : 0;
\r
5735 openFileName.lpstrInitialDir = NULL;
\r
5736 openFileName.lpstrTitle = dlgTitle;
\r
5737 openFileName.Flags = OFN_PATHMUSTEXIST | OFN_HIDEREADONLY
\r
5738 | (write[0] != 'r' ? 0 : OFN_FILEMUSTEXIST)
\r
5739 | (number ? OFN_ENABLETEMPLATE | OFN_ENABLEHOOK: 0)
\r
5740 | (oldDialog ? 0 : OFN_EXPLORER);
\r
5741 openFileName.nFileOffset = 0;
\r
5742 openFileName.nFileExtension = 0;
\r
5743 openFileName.lpstrDefExt = defExt;
\r
5744 openFileName.lCustData = (LONG) number;
\r
5745 openFileName.lpfnHook = oldDialog ?
\r
5746 (LPOFNHOOKPROC) OldOpenFileHook : (LPOFNHOOKPROC) OpenFileHook;
\r
5747 openFileName.lpTemplateName = (LPSTR)(oldDialog ? 1536 : DLG_IndexNumber);
\r
5749 if (write[0] != 'r' ? GetSaveFileName(&openFileName) :
\r
5750 GetOpenFileName(&openFileName)) {
\r
5751 /* open the file */
\r
5752 f = fopen(openFileName.lpstrFile, write);
\r
5754 MessageBox(hwnd, _("File open failed"), NULL,
\r
5755 MB_OK|MB_ICONEXCLAMATION);
\r
5759 int err = CommDlgExtendedError();
\r
5760 if (err != 0) DisplayError(_("Internal error in file dialog box"), err);
\r
5769 MenuPopup(HWND hwnd, POINT pt, HMENU hmenu, UINT def)
\r
5771 HMENU hmenuTrackPopup; /* floating pop-up menu */
\r
5774 * Get the first pop-up menu in the menu template. This is the
\r
5775 * menu that TrackPopupMenu displays.
\r
5777 hmenuTrackPopup = GetSubMenu(hmenu, 0);
\r
5779 SetMenuDefaultItem(hmenuTrackPopup, def, FALSE);
\r
5782 * TrackPopup uses screen coordinates, so convert the
\r
5783 * coordinates of the mouse click to screen coordinates.
\r
5785 ClientToScreen(hwnd, (LPPOINT) &pt);
\r
5787 /* Draw and track the floating pop-up menu. */
\r
5788 TrackPopupMenu(hmenuTrackPopup, TPM_CENTERALIGN | TPM_RIGHTBUTTON,
\r
5789 pt.x, pt.y, 0, hwnd, NULL);
\r
5791 /* Destroy the menu.*/
\r
5792 DestroyMenu(hmenu);
\r
5797 int sizeX, sizeY, newSizeX, newSizeY;
\r
5799 } ResizeEditPlusButtonsClosure;
\r
5802 ResizeEditPlusButtonsCallback(HWND hChild, LPARAM lparam)
\r
5804 ResizeEditPlusButtonsClosure *cl = (ResizeEditPlusButtonsClosure *)lparam;
\r
5808 if (hChild == cl->hText) return TRUE;
\r
5809 GetWindowRect(hChild, &rect); /* gives screen coords */
\r
5810 pt.x = rect.left + (cl->newSizeX - cl->sizeX)/2;
\r
5811 pt.y = rect.top + cl->newSizeY - cl->sizeY;
\r
5812 ScreenToClient(cl->hDlg, &pt);
\r
5813 cl->hdwp = DeferWindowPos(cl->hdwp, hChild, NULL,
\r
5814 pt.x, pt.y, rect.right - rect.left, rect.bottom - rect.top, SWP_NOZORDER);
\r
5818 /* Resize a dialog that has a (rich) edit field filling most of
\r
5819 the top, with a row of buttons below */
\r
5821 ResizeEditPlusButtons(HWND hDlg, HWND hText, int sizeX, int sizeY, int newSizeX, int newSizeY)
\r
5824 int newTextHeight, newTextWidth;
\r
5825 ResizeEditPlusButtonsClosure cl;
\r
5827 /*if (IsIconic(hDlg)) return;*/
\r
5828 if (newSizeX == sizeX && newSizeY == sizeY) return;
\r
5830 cl.hdwp = BeginDeferWindowPos(8);
\r
5832 GetWindowRect(hText, &rectText); /* gives screen coords */
\r
5833 newTextWidth = rectText.right - rectText.left + newSizeX - sizeX;
\r
5834 newTextHeight = rectText.bottom - rectText.top + newSizeY - sizeY;
\r
5835 if (newTextHeight < 0) {
\r
5836 newSizeY += -newTextHeight;
\r
5837 newTextHeight = 0;
\r
5839 cl.hdwp = DeferWindowPos(cl.hdwp, hText, NULL, 0, 0,
\r
5840 newTextWidth, newTextHeight, SWP_NOZORDER|SWP_NOMOVE);
\r
5846 cl.newSizeX = newSizeX;
\r
5847 cl.newSizeY = newSizeY;
\r
5848 EnumChildWindows(hDlg, ResizeEditPlusButtonsCallback, (LPARAM)&cl);
\r
5850 EndDeferWindowPos(cl.hdwp);
\r
5853 BOOL CenterWindowEx(HWND hwndChild, HWND hwndParent, int mode)
\r
5855 RECT rChild, rParent;
\r
5856 int wChild, hChild, wParent, hParent;
\r
5857 int wScreen, hScreen, xNew, yNew;
\r
5860 /* Get the Height and Width of the child window */
\r
5861 GetWindowRect (hwndChild, &rChild);
\r
5862 wChild = rChild.right - rChild.left;
\r
5863 hChild = rChild.bottom - rChild.top;
\r
5865 /* Get the Height and Width of the parent window */
\r
5866 GetWindowRect (hwndParent, &rParent);
\r
5867 wParent = rParent.right - rParent.left;
\r
5868 hParent = rParent.bottom - rParent.top;
\r
5870 /* Get the display limits */
\r
5871 hdc = GetDC (hwndChild);
\r
5872 wScreen = GetDeviceCaps (hdc, HORZRES);
\r
5873 hScreen = GetDeviceCaps (hdc, VERTRES);
\r
5874 ReleaseDC(hwndChild, hdc);
\r
5876 /* Calculate new X position, then adjust for screen */
\r
5877 xNew = rParent.left + ((wParent - wChild) /2);
\r
5880 } else if ((xNew+wChild) > wScreen) {
\r
5881 xNew = wScreen - wChild;
\r
5884 /* Calculate new Y position, then adjust for screen */
\r
5886 yNew = rParent.top + ((hParent - hChild) /2);
\r
5889 yNew = rParent.top + GetSystemMetrics( SM_CYCAPTION ) * 2 / 3;
\r
5894 } else if ((yNew+hChild) > hScreen) {
\r
5895 yNew = hScreen - hChild;
\r
5898 /* Set it, and return */
\r
5899 return SetWindowPos (hwndChild, NULL,
\r
5900 xNew, yNew, 0, 0, SWP_NOSIZE | SWP_NOZORDER);
\r
5903 /* Center one window over another */
\r
5904 BOOL CenterWindow (HWND hwndChild, HWND hwndParent)
\r
5906 return CenterWindowEx( hwndChild, hwndParent, 0 );
\r
5909 /*---------------------------------------------------------------------------*\
\r
5911 * Startup Dialog functions
\r
5913 \*---------------------------------------------------------------------------*/
\r
5915 InitComboStrings(HANDLE hwndCombo, char **cd)
\r
5917 SendMessage(hwndCombo, CB_RESETCONTENT, 0, 0);
\r
5919 while (*cd != NULL) {
\r
5920 SendMessage(hwndCombo, CB_ADDSTRING, 0, (LPARAM) T_(*cd));
\r
5926 InitComboStringsFromOption(HANDLE hwndCombo, char *str)
\r
5928 char buf1[MAX_ARG_LEN];
\r
5931 if (str[0] == '@') {
\r
5932 FILE* f = fopen(str + 1, "r");
\r
5934 DisplayFatalError(str + 1, errno, 2);
\r
5937 len = fread(buf1, 1, sizeof(buf1)-1, f);
\r
5939 buf1[len] = NULLCHAR;
\r
5943 SendMessage(hwndCombo, CB_RESETCONTENT, 0, 0);
\r
5946 char buf[MSG_SIZ];
\r
5947 char *end = strchr(str, '\n');
\r
5948 if (end == NULL) return;
\r
5949 memcpy(buf, str, end - str);
\r
5950 buf[end - str] = NULLCHAR;
\r
5951 SendMessage(hwndCombo, CB_ADDSTRING, 0, (LPARAM) buf);
\r
5957 SetStartupDialogEnables(HWND hDlg)
\r
5959 EnableWindow(GetDlgItem(hDlg, OPT_ChessEngineName),
\r
5960 IsDlgButtonChecked(hDlg, OPT_ChessEngine) ||
\r
5961 (appData.zippyPlay && IsDlgButtonChecked(hDlg, OPT_ChessServer)));
\r
5962 EnableWindow(GetDlgItem(hDlg, OPT_SecondChessEngineName),
\r
5963 IsDlgButtonChecked(hDlg, OPT_ChessEngine));
\r
5964 EnableWindow(GetDlgItem(hDlg, OPT_ChessServerName),
\r
5965 IsDlgButtonChecked(hDlg, OPT_ChessServer));
\r
5966 EnableWindow(GetDlgItem(hDlg, OPT_AdditionalOptions),
\r
5967 IsDlgButtonChecked(hDlg, OPT_AnyAdditional));
\r
5968 EnableWindow(GetDlgItem(hDlg, IDOK),
\r
5969 IsDlgButtonChecked(hDlg, OPT_ChessEngine) ||
\r
5970 IsDlgButtonChecked(hDlg, OPT_ChessServer) ||
\r
5971 IsDlgButtonChecked(hDlg, OPT_View));
\r
5975 QuoteForFilename(char *filename)
\r
5977 int dquote, space;
\r
5978 dquote = strchr(filename, '"') != NULL;
\r
5979 space = strchr(filename, ' ') != NULL;
\r
5980 if (dquote || space) {
\r
5992 InitEngineBox(HWND hDlg, HWND hwndCombo, char* nthcp, char* nthd, char* nthdir, char *nthnames)
\r
5994 char buf[MSG_SIZ];
\r
5997 InitComboStringsFromOption(hwndCombo, nthnames);
\r
5998 q = QuoteForFilename(nthcp);
\r
5999 snprintf(buf, MSG_SIZ, "%s%s%s", q, nthcp, q);
\r
6000 if (*nthdir != NULLCHAR) {
\r
6001 q = QuoteForFilename(nthdir);
\r
6002 snprintf(buf + strlen(buf), MSG_SIZ, " /%s=%s%s%s", nthd, q, nthdir, q);
\r
6004 if (*nthcp == NULLCHAR) {
\r
6005 SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) 0, (LPARAM) 0);
\r
6006 } else if (SendMessage(hwndCombo, CB_SELECTSTRING, (WPARAM) -1, (LPARAM) buf) == CB_ERR) {
\r
6007 SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) -1, (LPARAM) 0);
\r
6008 SendMessage(hwndCombo, WM_SETTEXT, (WPARAM) 0, (LPARAM) buf);
\r
6013 StartupDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
6015 char buf[MSG_SIZ];
\r
6019 switch (message) {
\r
6020 case WM_INITDIALOG:
\r
6021 /* Center the dialog */
\r
6022 CenterWindow (hDlg, GetDesktopWindow());
\r
6023 Translate(hDlg, DLG_Startup);
\r
6024 /* Initialize the dialog items */
\r
6025 InitEngineBox(hDlg, GetDlgItem(hDlg, OPT_ChessEngineName),
\r
6026 appData.firstChessProgram, "fd", appData.firstDirectory,
\r
6027 firstChessProgramNames);
\r
6028 InitEngineBox(hDlg, GetDlgItem(hDlg, OPT_SecondChessEngineName),
\r
6029 appData.secondChessProgram, "sd", appData.secondDirectory,
\r
6030 secondChessProgramNames);
\r
6031 hwndCombo = GetDlgItem(hDlg, OPT_ChessServerName);
\r
6032 InitComboStringsFromOption(hwndCombo, icsNames);
\r
6033 snprintf(buf, MSG_SIZ, "%s /icsport=%s", appData.icsHost, appData.icsPort);
\r
6034 if (*appData.icsHelper != NULLCHAR) {
\r
6035 char *q = QuoteForFilename(appData.icsHelper);
\r
6036 sprintf(buf + strlen(buf), " /icshelper=%s%s%s", q, appData.icsHelper, q);
\r
6038 if (*appData.icsHost == NULLCHAR) {
\r
6039 SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) 0, (LPARAM) 0);
\r
6040 /*SendMessage(hwndCombo, CB_SHOWDROPDOWN, (WPARAM) TRUE, (LPARAM) 0); !!too soon */
\r
6041 } else if (SendMessage(hwndCombo, CB_SELECTSTRING, (WPARAM) -1, (LPARAM) buf) == CB_ERR) {
\r
6042 SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) -1, (LPARAM) 0);
\r
6043 SendMessage(hwndCombo, WM_SETTEXT, (WPARAM) 0, (LPARAM) buf);
\r
6046 if (appData.icsActive) {
\r
6047 CheckDlgButton(hDlg, OPT_ChessServer, BST_CHECKED);
\r
6049 else if (appData.noChessProgram) {
\r
6050 CheckDlgButton(hDlg, OPT_View, BST_CHECKED);
\r
6053 CheckDlgButton(hDlg, OPT_ChessEngine, BST_CHECKED);
\r
6056 SetStartupDialogEnables(hDlg);
\r
6060 switch (LOWORD(wParam)) {
\r
6062 if (IsDlgButtonChecked(hDlg, OPT_ChessEngine)) {
\r
6063 safeStrCpy(buf, "/fcp=", sizeof(buf)/sizeof(buf[0]) );
\r
6064 GetDlgItemText(hDlg, OPT_ChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));
\r
6066 ParseArgs(StringGet, &p);
\r
6067 safeStrCpy(buf, "/scp=", sizeof(buf)/sizeof(buf[0]) );
\r
6068 GetDlgItemText(hDlg, OPT_SecondChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));
\r
6070 ParseArgs(StringGet, &p);
\r
6071 appData.noChessProgram = FALSE;
\r
6072 appData.icsActive = FALSE;
\r
6073 } else if (IsDlgButtonChecked(hDlg, OPT_ChessServer)) {
\r
6074 safeStrCpy(buf, "/ics /icshost=", sizeof(buf)/sizeof(buf[0]) );
\r
6075 GetDlgItemText(hDlg, OPT_ChessServerName, buf + strlen(buf), sizeof(buf) - strlen(buf));
\r
6077 ParseArgs(StringGet, &p);
\r
6078 if (appData.zippyPlay) {
\r
6079 safeStrCpy(buf, "/fcp=", sizeof(buf)/sizeof(buf[0]) );
\r
6080 GetDlgItemText(hDlg, OPT_ChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));
\r
6082 ParseArgs(StringGet, &p);
\r
6084 } else if (IsDlgButtonChecked(hDlg, OPT_View)) {
\r
6085 appData.noChessProgram = TRUE;
\r
6086 appData.icsActive = FALSE;
\r
6088 MessageBox(hDlg, _("Choose an option, or cancel to exit"),
\r
6089 _("Option Error"), MB_OK|MB_ICONEXCLAMATION);
\r
6092 if (IsDlgButtonChecked(hDlg, OPT_AnyAdditional)) {
\r
6093 GetDlgItemText(hDlg, OPT_AdditionalOptions, buf, sizeof(buf));
\r
6095 ParseArgs(StringGet, &p);
\r
6097 EndDialog(hDlg, TRUE);
\r
6104 case IDM_HELPCONTENTS:
\r
6105 if (!WinHelp (hDlg, "winboard.hlp", HELP_KEY,(DWORD)(LPSTR)"CONTENTS")) {
\r
6106 MessageBox (GetFocus(),
\r
6107 _("Unable to activate help"),
\r
6108 szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);
\r
6113 SetStartupDialogEnables(hDlg);
\r
6121 /*---------------------------------------------------------------------------*\
\r
6123 * About box dialog functions
\r
6125 \*---------------------------------------------------------------------------*/
\r
6127 /* Process messages for "About" dialog box */
\r
6129 About(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
6131 switch (message) {
\r
6132 case WM_INITDIALOG: /* message: initialize dialog box */
\r
6133 /* Center the dialog over the application window */
\r
6134 CenterWindow (hDlg, GetWindow (hDlg, GW_OWNER));
\r
6135 SetDlgItemText(hDlg, ABOUTBOX_Version, programVersion);
\r
6136 Translate(hDlg, ABOUTBOX);
\r
6140 case WM_COMMAND: /* message: received a command */
\r
6141 if (LOWORD(wParam) == IDOK /* "OK" box selected? */
\r
6142 || LOWORD(wParam) == IDCANCEL) { /* System menu close command? */
\r
6143 EndDialog(hDlg, TRUE); /* Exit the dialog */
\r
6151 /*---------------------------------------------------------------------------*\
\r
6153 * Comment Dialog functions
\r
6155 \*---------------------------------------------------------------------------*/
\r
6158 CommentDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
6160 static HANDLE hwndText = NULL;
\r
6161 int len, newSizeX, newSizeY, flags;
\r
6162 static int sizeX, sizeY;
\r
6167 switch (message) {
\r
6168 case WM_INITDIALOG: /* message: initialize dialog box */
\r
6169 /* Initialize the dialog items */
\r
6170 Translate(hDlg, DLG_EditComment);
\r
6171 hwndText = GetDlgItem(hDlg, OPT_CommentText);
\r
6172 SetDlgItemText(hDlg, OPT_CommentText, commentText);
\r
6173 EnableWindow(GetDlgItem(hDlg, OPT_CancelComment), editComment);
\r
6174 EnableWindow(GetDlgItem(hDlg, OPT_ClearComment), editComment);
\r
6175 EnableWindow(GetDlgItem(hDlg, OPT_EditComment), !editComment);
\r
6176 SendMessage(hwndText, EM_SETREADONLY, !editComment, 0);
\r
6177 SetWindowText(hDlg, commentTitle);
\r
6178 if (editComment) {
\r
6179 SetFocus(hwndText);
\r
6181 SetFocus(GetDlgItem(hDlg, IDOK));
\r
6183 SendMessage(GetDlgItem(hDlg, OPT_CommentText),
\r
6184 WM_SETFONT, (WPARAM)font[boardSize][COMMENT_FONT]->hf,
\r
6185 MAKELPARAM(FALSE, 0));
\r
6186 /* Size and position the dialog */
\r
6187 if (!commentDialog) {
\r
6188 commentDialog = hDlg;
\r
6189 flags = SWP_NOZORDER;
\r
6190 GetClientRect(hDlg, &rect);
\r
6191 sizeX = rect.right;
\r
6192 sizeY = rect.bottom;
\r
6193 if (wpComment.x != CW_USEDEFAULT && wpComment.y != CW_USEDEFAULT &&
\r
6194 wpComment.width != CW_USEDEFAULT && wpComment.height != CW_USEDEFAULT) {
\r
6195 WINDOWPLACEMENT wp;
\r
6196 EnsureOnScreen(&wpComment.x, &wpComment.y, 0, 0);
\r
6197 wp.length = sizeof(WINDOWPLACEMENT);
\r
6199 wp.showCmd = SW_SHOW;
\r
6200 wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;
\r
6201 wp.rcNormalPosition.left = wpComment.x;
\r
6202 wp.rcNormalPosition.right = wpComment.x + wpComment.width;
\r
6203 wp.rcNormalPosition.top = wpComment.y;
\r
6204 wp.rcNormalPosition.bottom = wpComment.y + wpComment.height;
\r
6205 SetWindowPlacement(hDlg, &wp);
\r
6207 GetClientRect(hDlg, &rect);
\r
6208 newSizeX = rect.right;
\r
6209 newSizeY = rect.bottom;
\r
6210 ResizeEditPlusButtons(hDlg, hwndText, sizeX, sizeY,
\r
6211 newSizeX, newSizeY);
\r
6216 SendDlgItemMessage( hDlg, OPT_CommentText, EM_SETEVENTMASK, 0, ENM_MOUSEEVENTS );
\r
6219 case WM_COMMAND: /* message: received a command */
\r
6220 switch (LOWORD(wParam)) {
\r
6222 if (editComment) {
\r
6224 /* Read changed options from the dialog box */
\r
6225 hwndText = GetDlgItem(hDlg, OPT_CommentText);
\r
6226 len = GetWindowTextLength(hwndText);
\r
6227 str = (char *) malloc(len + 1);
\r
6228 GetWindowText(hwndText, str, len + 1);
\r
6237 ReplaceComment(commentIndex, str);
\r
6244 case OPT_CancelComment:
\r
6248 case OPT_ClearComment:
\r
6249 SetDlgItemText(hDlg, OPT_CommentText, "");
\r
6252 case OPT_EditComment:
\r
6253 EditCommentEvent();
\r
6261 case WM_NOTIFY: // [HGM] vari: cloned from whistory.c
\r
6262 if( wParam == OPT_CommentText ) {
\r
6263 MSGFILTER * lpMF = (MSGFILTER *) lParam;
\r
6265 if( lpMF->msg == WM_RBUTTONDOWN && (lpMF->wParam & (MK_CONTROL | MK_SHIFT)) == 0 ) {
\r
6269 pt.x = LOWORD( lpMF->lParam );
\r
6270 pt.y = HIWORD( lpMF->lParam );
\r
6272 index = SendDlgItemMessage( hDlg, OPT_CommentText, EM_CHARFROMPOS, 0, (LPARAM) &pt );
\r
6274 hwndText = GetDlgItem(hDlg, OPT_CommentText); // cloned from above
\r
6275 len = GetWindowTextLength(hwndText);
\r
6276 str = (char *) malloc(len + 1);
\r
6277 GetWindowText(hwndText, str, len + 1);
\r
6278 ReplaceComment(commentIndex, str);
\r
6279 if(commentIndex != currentMove) ToNrEvent(commentIndex);
\r
6280 LoadVariation( index, str ); // [HGM] also does the actual moving to it, now
\r
6283 /* Zap the message for good: apparently, returning non-zero is not enough */
\r
6284 lpMF->msg = WM_USER;
\r
6292 newSizeX = LOWORD(lParam);
\r
6293 newSizeY = HIWORD(lParam);
\r
6294 ResizeEditPlusButtons(hDlg, hwndText, sizeX, sizeY, newSizeX, newSizeY);
\r
6299 case WM_GETMINMAXINFO:
\r
6300 /* Prevent resizing window too small */
\r
6301 mmi = (MINMAXINFO *) lParam;
\r
6302 mmi->ptMinTrackSize.x = 100;
\r
6303 mmi->ptMinTrackSize.y = 100;
\r
6310 EitherCommentPopUp(int index, char *title, char *str, BOOLEAN edit)
\r
6315 CheckMenuItem(GetMenu(hwndMain), IDM_EditComment, edit ? MF_CHECKED : MF_UNCHECKED);
\r
6317 if (str == NULL) str = "";
\r
6318 p = (char *) malloc(2 * strlen(str) + 2);
\r
6321 if (*str == '\n') *q++ = '\r';
\r
6325 if (commentText != NULL) free(commentText);
\r
6327 commentIndex = index;
\r
6328 commentTitle = title;
\r
6330 editComment = edit;
\r
6332 if (commentDialog) {
\r
6333 SendMessage(commentDialog, WM_INITDIALOG, 0, 0);
\r
6334 if (!commentUp) ShowWindow(commentDialog, SW_SHOW);
\r
6336 lpProc = MakeProcInstance((FARPROC)CommentDialog, hInst);
\r
6337 CreateDialog(hInst, MAKEINTRESOURCE(DLG_EditComment),
\r
6338 hwndMain, (DLGPROC)lpProc);
\r
6339 FreeProcInstance(lpProc);
\r
6345 /*---------------------------------------------------------------------------*\
\r
6347 * Type-in move dialog functions
\r
6349 \*---------------------------------------------------------------------------*/
\r
6352 TypeInMoveDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
6354 char move[MSG_SIZ];
\r
6356 ChessMove moveType;
\r
6357 int fromX, fromY, toX, toY;
\r
6360 switch (message) {
\r
6361 case WM_INITDIALOG:
\r
6362 move[0] = (char) lParam;
\r
6363 move[1] = NULLCHAR;
\r
6364 CenterWindowEx(hDlg, GetWindow(hDlg, GW_OWNER), 1 );
\r
6365 Translate(hDlg, DLG_TypeInMove);
\r
6366 hInput = GetDlgItem(hDlg, OPT_Move);
\r
6367 SetWindowText(hInput, move);
\r
6369 SendMessage(hInput, EM_SETSEL, (WPARAM)9999, (LPARAM)9999);
\r
6373 switch (LOWORD(wParam)) {
\r
6375 shiftKey = GetKeyState(VK_SHIFT) < 0; // [HGM] remember last shift status
\r
6376 GetDlgItemText(hDlg, OPT_Move, move, sizeof(move));
\r
6377 { int n; Board board;
\r
6379 if(gameMode == EditPosition && ParseFEN(board, &n, move) ) {
\r
6380 EditPositionPasteFEN(move);
\r
6381 EndDialog(hDlg, TRUE);
\r
6384 // [HGM] movenum: allow move number to be typed in any mode
\r
6385 if(sscanf(move, "%d", &n) == 1 && n != 0 ) {
\r
6387 EndDialog(hDlg, TRUE);
\r
6391 if (gameMode != EditGame && currentMove != forwardMostMove &&
\r
6392 gameMode != Training) {
\r
6393 DisplayMoveError(_("Displayed move is not current"));
\r
6395 // GetDlgItemText(hDlg, OPT_Move, move, sizeof(move)); // moved upstream
\r
6396 int ok = ParseOneMove(move, gameMode == EditPosition ? blackPlaysFirst : currentMove,
\r
6397 &moveType, &fromX, &fromY, &toX, &toY, &promoChar);
\r
6398 if(!ok && move[0] >= 'a') { move[0] += 'A' - 'a'; ok = 2; } // [HGM] try also capitalized
\r
6399 if (ok==1 || ok && ParseOneMove(move, gameMode == EditPosition ? blackPlaysFirst : currentMove,
\r
6400 &moveType, &fromX, &fromY, &toX, &toY, &promoChar)) {
\r
6401 if (gameMode != Training)
\r
6402 forwardMostMove = currentMove;
\r
6403 UserMoveEvent(fromX, fromY, toX, toY, promoChar);
\r
6405 DisplayMoveError(_("Could not parse move"));
\r
6408 EndDialog(hDlg, TRUE);
\r
6411 EndDialog(hDlg, FALSE);
\r
6422 PopUpMoveDialog(char firstchar)
\r
6426 if ((gameMode == BeginningOfGame && !appData.icsActive) ||
\r
6427 gameMode == MachinePlaysWhite || gameMode == MachinePlaysBlack ||
\r
6428 gameMode == AnalyzeMode || gameMode == EditGame ||
\r
6429 gameMode == EditPosition || gameMode == IcsExamining ||
\r
6430 gameMode == IcsPlayingWhite || gameMode == IcsPlayingBlack ||
\r
6431 isdigit(firstchar) && // [HGM] movenum: allow typing in of move nr in 'passive' modes
\r
6432 ( gameMode == AnalyzeFile || gameMode == PlayFromGameFile ||
\r
6433 gameMode == IcsObserving || gameMode == TwoMachinesPlay ) ||
\r
6434 gameMode == Training) {
\r
6435 lpProc = MakeProcInstance((FARPROC)TypeInMoveDialog, hInst);
\r
6436 DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_TypeInMove),
\r
6437 hwndMain, (DLGPROC)lpProc, (LPARAM)firstchar);
\r
6438 FreeProcInstance(lpProc);
\r
6442 /*---------------------------------------------------------------------------*\
\r
6444 * Type-in name dialog functions
\r
6446 \*---------------------------------------------------------------------------*/
\r
6449 TypeInNameDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
6451 char move[MSG_SIZ];
\r
6454 switch (message) {
\r
6455 case WM_INITDIALOG:
\r
6456 move[0] = (char) lParam;
\r
6457 move[1] = NULLCHAR;
\r
6458 CenterWindowEx(hDlg, GetWindow(hDlg, GW_OWNER), 1 );
\r
6459 Translate(hDlg, DLG_TypeInName);
\r
6460 hInput = GetDlgItem(hDlg, OPT_Name);
\r
6461 SetWindowText(hInput, move);
\r
6463 SendMessage(hInput, EM_SETSEL, (WPARAM)9999, (LPARAM)9999);
\r
6467 switch (LOWORD(wParam)) {
\r
6469 GetDlgItemText(hDlg, OPT_Name, move, sizeof(move));
\r
6470 appData.userName = strdup(move);
\r
6473 if(gameMode == MachinePlaysWhite || gameMode == MachinePlaysBlack) {
\r
6474 snprintf(move, MSG_SIZ, "%s vs. %s", gameInfo.white, gameInfo.black);
\r
6475 DisplayTitle(move);
\r
6479 EndDialog(hDlg, TRUE);
\r
6482 EndDialog(hDlg, FALSE);
\r
6493 PopUpNameDialog(char firstchar)
\r
6497 lpProc = MakeProcInstance((FARPROC)TypeInNameDialog, hInst);
\r
6498 DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_TypeInName),
\r
6499 hwndMain, (DLGPROC)lpProc, (LPARAM)firstchar);
\r
6500 FreeProcInstance(lpProc);
\r
6503 /*---------------------------------------------------------------------------*\
\r
6507 \*---------------------------------------------------------------------------*/
\r
6509 /* Nonmodal error box */
\r
6510 LRESULT CALLBACK ErrorDialog(HWND hDlg, UINT message,
\r
6511 WPARAM wParam, LPARAM lParam);
\r
6514 ErrorPopUp(char *title, char *content)
\r
6518 BOOLEAN modal = hwndMain == NULL;
\r
6536 strncpy(errorTitle, title, sizeof(errorTitle));
\r
6537 errorTitle[sizeof(errorTitle) - 1] = '\0';
\r
6540 MessageBox(NULL, errorMessage, errorTitle, MB_OK|MB_ICONEXCLAMATION);
\r
6542 lpProc = MakeProcInstance((FARPROC)ErrorDialog, hInst);
\r
6543 CreateDialog(hInst, MAKEINTRESOURCE(DLG_Error),
\r
6544 hwndMain, (DLGPROC)lpProc);
\r
6545 FreeProcInstance(lpProc);
\r
6552 if (!appData.popupMoveErrors && moveErrorMessageUp) DisplayMessage("", "");
\r
6553 if (errorDialog == NULL) return;
\r
6554 DestroyWindow(errorDialog);
\r
6555 errorDialog = NULL;
\r
6556 if(errorExitStatus) ExitEvent(errorExitStatus);
\r
6560 ErrorDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
6565 switch (message) {
\r
6566 case WM_INITDIALOG:
\r
6567 GetWindowRect(hDlg, &rChild);
\r
6570 SetWindowPos(hDlg, NULL, rChild.left,
\r
6571 rChild.top + boardRect.top - (rChild.bottom - rChild.top),
\r
6572 0, 0, SWP_NOZORDER|SWP_NOSIZE);
\r
6576 [AS] It seems that the above code wants to move the dialog up in the "caption
\r
6577 area" of the main window, but it uses the dialog height as an hard-coded constant,
\r
6578 and it doesn't work when you resize the dialog.
\r
6579 For now, just give it a default position.
\r
6581 SetWindowPos(hDlg, NULL, boardRect.left+8, boardRect.top+8, 0, 0, SWP_NOZORDER|SWP_NOSIZE);
\r
6582 Translate(hDlg, DLG_Error);
\r
6584 errorDialog = hDlg;
\r
6585 SetWindowText(hDlg, errorTitle);
\r
6586 hwndText = GetDlgItem(hDlg, OPT_ErrorText);
\r
6587 SetDlgItemText(hDlg, OPT_ErrorText, errorMessage);
\r
6591 switch (LOWORD(wParam)) {
\r
6594 if (errorDialog == hDlg) errorDialog = NULL;
\r
6595 DestroyWindow(hDlg);
\r
6607 HWND gothicDialog = NULL;
\r
6610 GothicDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
6614 int height = GetSystemMetrics(SM_CYCAPTION)+GetSystemMetrics(SM_CYFRAME);
\r
6616 switch (message) {
\r
6617 case WM_INITDIALOG:
\r
6618 GetWindowRect(hDlg, &rChild);
\r
6620 SetWindowPos(hDlg, NULL, wpMain.x, wpMain.y-height, wpMain.width, height,
\r
6624 [AS] It seems that the above code wants to move the dialog up in the "caption
\r
6625 area" of the main window, but it uses the dialog height as an hard-coded constant,
\r
6626 and it doesn't work when you resize the dialog.
\r
6627 For now, just give it a default position.
\r
6629 gothicDialog = hDlg;
\r
6630 SetWindowText(hDlg, errorTitle);
\r
6631 hwndText = GetDlgItem(hDlg, OPT_ErrorText);
\r
6632 SetDlgItemText(hDlg, OPT_ErrorText, errorMessage);
\r
6636 switch (LOWORD(wParam)) {
\r
6639 if (errorDialog == hDlg) errorDialog = NULL;
\r
6640 DestroyWindow(hDlg);
\r
6652 GothicPopUp(char *title, VariantClass variant)
\r
6655 static char *lastTitle;
\r
6657 strncpy(errorTitle, title, sizeof(errorTitle));
\r
6658 errorTitle[sizeof(errorTitle) - 1] = '\0';
\r
6660 if(lastTitle != title && gothicDialog != NULL) {
\r
6661 DestroyWindow(gothicDialog);
\r
6662 gothicDialog = NULL;
\r
6664 if(variant != VariantNormal && gothicDialog == NULL) {
\r
6665 title = lastTitle;
\r
6666 lpProc = MakeProcInstance((FARPROC)GothicDialog, hInst);
\r
6667 CreateDialog(hInst, MAKEINTRESOURCE(DLG_Error),
\r
6668 hwndMain, (DLGPROC)lpProc);
\r
6669 FreeProcInstance(lpProc);
\r
6674 /*---------------------------------------------------------------------------*\
\r
6676 * Ics Interaction console functions
\r
6678 \*---------------------------------------------------------------------------*/
\r
6680 #define HISTORY_SIZE 64
\r
6681 static char *history[HISTORY_SIZE];
\r
6682 int histIn = 0, histP = 0;
\r
6685 SaveInHistory(char *cmd)
\r
6687 if (history[histIn] != NULL) {
\r
6688 free(history[histIn]);
\r
6689 history[histIn] = NULL;
\r
6691 if (*cmd == NULLCHAR) return;
\r
6692 history[histIn] = StrSave(cmd);
\r
6693 histIn = (histIn + 1) % HISTORY_SIZE;
\r
6694 if (history[histIn] != NULL) {
\r
6695 free(history[histIn]);
\r
6696 history[histIn] = NULL;
\r
6702 PrevInHistory(char *cmd)
\r
6705 if (histP == histIn) {
\r
6706 if (history[histIn] != NULL) free(history[histIn]);
\r
6707 history[histIn] = StrSave(cmd);
\r
6709 newhp = (histP - 1 + HISTORY_SIZE) % HISTORY_SIZE;
\r
6710 if (newhp == histIn || history[newhp] == NULL) return NULL;
\r
6712 return history[histP];
\r
6718 if (histP == histIn) return NULL;
\r
6719 histP = (histP + 1) % HISTORY_SIZE;
\r
6720 return history[histP];
\r
6724 LoadIcsTextMenu(IcsTextMenuEntry *e)
\r
6728 hmenu = LoadMenu(hInst, "TextMenu");
\r
6729 h = GetSubMenu(hmenu, 0);
\r
6731 if (strcmp(e->item, "-") == 0) {
\r
6732 AppendMenu(h, MF_SEPARATOR, 0, 0);
\r
6733 } else { // [HGM] re-written a bit to use only one AppendMenu call for both cases (| or no |)
\r
6734 int flags = MF_STRING, j = 0;
\r
6735 if (e->item[0] == '|') {
\r
6736 flags |= MF_MENUBARBREAK;
\r
6739 if(!strcmp(e->command, "none")) flags |= MF_GRAYED; // [HGM] chatclick: provide inactive dummy
\r
6740 AppendMenu(h, flags, IDM_CommandX + i, e->item + j);
\r
6748 WNDPROC consoleTextWindowProc;
\r
6751 CommandX(HWND hwnd, char *command, BOOLEAN getname, BOOLEAN immediate)
\r
6753 char buf[MSG_SIZ], name[MSG_SIZ];
\r
6754 HWND hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
6758 SetWindowText(hInput, command);
\r
6760 SendMessage(hInput, WM_CHAR, '\r', 0);
\r
6762 sel.cpMin = 999999;
\r
6763 sel.cpMax = 999999;
\r
6764 SendMessage(hInput, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
6769 SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);
\r
6770 if (sel.cpMin == sel.cpMax) {
\r
6771 /* Expand to surrounding word */
\r
6774 tr.chrg.cpMax = sel.cpMin;
\r
6775 tr.chrg.cpMin = --sel.cpMin;
\r
6776 if (sel.cpMin < 0) break;
\r
6777 tr.lpstrText = name;
\r
6778 SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr);
\r
6779 } while (isalpha(name[0]) || isdigit(name[0]) || name[0] == '-');
\r
6783 tr.chrg.cpMin = sel.cpMax;
\r
6784 tr.chrg.cpMax = ++sel.cpMax;
\r
6785 tr.lpstrText = name;
\r
6786 if (SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr) < 1) break;
\r
6787 } while (isalpha(name[0]) || isdigit(name[0]) || name[0] == '-');
\r
6790 if (sel.cpMax == sel.cpMin || sel.cpMax - sel.cpMin > MSG_SIZ/2) {
\r
6791 MessageBeep(MB_ICONEXCLAMATION);
\r
6795 tr.lpstrText = name;
\r
6796 SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr);
\r
6798 if (sel.cpMax - sel.cpMin > MSG_SIZ/2) {
\r
6799 MessageBeep(MB_ICONEXCLAMATION);
\r
6802 SendMessage(hwnd, EM_GETSELTEXT, 0, (LPARAM) name);
\r
6805 if(strstr(command, "%s")) snprintf(buf, MSG_SIZ, command, name); else
\r
6806 snprintf(buf, MSG_SIZ, "%s %s", command, name);
\r
6807 SetWindowText(hInput, buf);
\r
6808 SendMessage(hInput, WM_CHAR, '\r', 0);
\r
6810 if(!strcmp(command, "chat")) { ChatPopUp(name); return; }
\r
6811 snprintf(buf, MSG_SIZ, "%s %s ", command, name); /* trailing space */
\r
6812 SetWindowText(hInput, buf);
\r
6813 sel.cpMin = 999999;
\r
6814 sel.cpMax = 999999;
\r
6815 SendMessage(hInput, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
6821 ConsoleTextSubclass(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
\r
6826 switch (message) {
\r
6828 if (!(GetKeyState(VK_CONTROL) & ~1)) break;
\r
6831 SendMessage(hwnd, EM_LINESCROLL, 0, -999999);
\r
6834 sel.cpMin = 999999;
\r
6835 sel.cpMax = 999999;
\r
6836 SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
6837 SendMessage(hwnd, EM_SCROLLCARET, 0, 0);
\r
6842 if(wParam != '\022') {
\r
6843 if (wParam == '\t') {
\r
6844 if (GetKeyState(VK_SHIFT) < 0) {
\r
6846 if (IsIconic(hwndMain)) ShowWindow(hwndMain, SW_RESTORE);
\r
6847 if (buttonDesc[0].hwnd) {
\r
6848 SetFocus(buttonDesc[0].hwnd);
\r
6850 SetFocus(hwndMain);
\r
6854 SetFocus(GetDlgItem(hwndConsole, OPT_ConsoleInput));
\r
6857 hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
6858 JAWS_DELETE( SetFocus(hInput); )
\r
6859 SendMessage(hInput, message, wParam, lParam);
\r
6862 } // [HGM] navigate: for Ctrl+R, flow into nex case (moved up here) to summon up menu
\r
6863 case WM_RBUTTONDOWN:
\r
6864 if (!(GetKeyState(VK_SHIFT) & ~1)) {
\r
6865 /* Move selection here if it was empty */
\r
6867 pt.x = LOWORD(lParam);
\r
6868 pt.y = HIWORD(lParam);
\r
6869 SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);
\r
6870 if (sel.cpMin == sel.cpMax) {
\r
6871 sel.cpMin = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&pt); /*doc is wrong*/
\r
6872 sel.cpMax = sel.cpMin;
\r
6873 SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
6875 SendMessage(hwnd, EM_HIDESELECTION, FALSE, FALSE);
\r
6876 { // [HGM] chatclick: code moved here from WM_RBUTTONUP case, to have menu appear on down-click
\r
6878 HMENU hmenu = LoadIcsTextMenu(icsTextMenuEntry);
\r
6879 SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);
\r
6880 if (sel.cpMin == sel.cpMax) {
\r
6881 EnableMenuItem(hmenu, IDM_Copy, MF_BYCOMMAND|MF_GRAYED);
\r
6882 EnableMenuItem(hmenu, IDM_QuickPaste, MF_BYCOMMAND|MF_GRAYED);
\r
6884 if (!IsClipboardFormatAvailable(CF_TEXT)) {
\r
6885 EnableMenuItem(hmenu, IDM_Paste, MF_BYCOMMAND|MF_GRAYED);
\r
6887 pt.x = LOWORD(lParam)-30; // [HGM] chatclick: make menu pop up with pointer above upper-right item
\r
6888 pt.y = HIWORD(lParam)-10; // make it appear as if mouse moved there, so it will be selected on up-click
\r
6889 PostMessage(hwnd, WM_MOUSEMOVE, wParam, lParam+5);
\r
6890 MenuPopup(hwnd, pt, hmenu, -1);
\r
6894 case WM_RBUTTONUP:
\r
6895 if (GetKeyState(VK_SHIFT) & ~1) {
\r
6896 SendDlgItemMessage(hwndConsole, OPT_ConsoleText,
\r
6897 WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);
\r
6901 hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
6903 return SendMessage(hInput, message, wParam, lParam);
\r
6904 case WM_MBUTTONDOWN:
\r
6905 return SendMessage(hwnd, WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);
\r
6907 switch (LOWORD(wParam)) {
\r
6908 case IDM_QuickPaste:
\r
6910 SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);
\r
6911 if (sel.cpMin == sel.cpMax) {
\r
6912 MessageBeep(MB_ICONEXCLAMATION);
\r
6915 SendMessage(hwnd, WM_COPY, 0, 0);
\r
6916 hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
6917 SendMessage(hInput, WM_PASTE, 0, 0);
\r
6922 SendMessage(hwnd, WM_CUT, 0, 0);
\r
6925 SendMessage(hwnd, WM_PASTE, 0, 0);
\r
6928 SendMessage(hwnd, WM_COPY, 0, 0);
\r
6932 int i = LOWORD(wParam) - IDM_CommandX;
\r
6933 if (i >= 0 && i < ICS_TEXT_MENU_SIZE &&
\r
6934 icsTextMenuEntry[i].command != NULL) {
\r
6935 CommandX(hwnd, icsTextMenuEntry[i].command,
\r
6936 icsTextMenuEntry[i].getname,
\r
6937 icsTextMenuEntry[i].immediate);
\r
6945 return (*consoleTextWindowProc)(hwnd, message, wParam, lParam);
\r
6948 WNDPROC consoleInputWindowProc;
\r
6951 ConsoleInputSubclass(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
\r
6953 char buf[MSG_SIZ];
\r
6955 static BOOL sendNextChar = FALSE;
\r
6956 static BOOL quoteNextChar = FALSE;
\r
6957 InputSource *is = consoleInputSource;
\r
6961 switch (message) {
\r
6963 if (!appData.localLineEditing || sendNextChar) {
\r
6964 is->buf[0] = (CHAR) wParam;
\r
6966 SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);
\r
6967 sendNextChar = FALSE;
\r
6970 if (quoteNextChar) {
\r
6971 buf[0] = (char) wParam;
\r
6972 buf[1] = NULLCHAR;
\r
6973 SendMessage(hwnd, EM_REPLACESEL, TRUE, (LPARAM) buf);
\r
6974 quoteNextChar = FALSE;
\r
6978 case '\r': /* Enter key */
\r
6979 is->count = GetWindowText(hwnd, is->buf, INPUT_SOURCE_BUF_SIZE-1);
\r
6980 if (consoleEcho) SaveInHistory(is->buf);
\r
6981 is->buf[is->count++] = '\n';
\r
6982 is->buf[is->count] = NULLCHAR;
\r
6983 SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);
\r
6984 if (consoleEcho) {
\r
6985 ConsoleOutput(is->buf, is->count, TRUE);
\r
6986 } else if (appData.localLineEditing) {
\r
6987 ConsoleOutput("\n", 1, TRUE);
\r
6990 case '\033': /* Escape key */
\r
6991 SetWindowText(hwnd, "");
\r
6992 cf.cbSize = sizeof(CHARFORMAT);
\r
6993 cf.dwMask = CFM_COLOR|CFM_BOLD|CFM_ITALIC|CFM_UNDERLINE|CFM_STRIKEOUT;
\r
6994 if (consoleEcho) {
\r
6995 cf.crTextColor = textAttribs[ColorNormal].color;
\r
6997 cf.crTextColor = COLOR_ECHOOFF;
\r
6999 cf.dwEffects = textAttribs[ColorNormal].effects;
\r
7000 SendMessage(hwnd, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf);
\r
7002 case '\t': /* Tab key */
\r
7003 if (GetKeyState(VK_SHIFT) < 0) {
\r
7005 SetFocus(GetDlgItem(hwndConsole, OPT_ConsoleText));
\r
7008 if (IsIconic(hwndMain)) ShowWindow(hwndMain, SW_RESTORE);
\r
7009 if (buttonDesc[0].hwnd) {
\r
7010 SetFocus(buttonDesc[0].hwnd);
\r
7012 SetFocus(hwndMain);
\r
7016 case '\023': /* Ctrl+S */
\r
7017 sendNextChar = TRUE;
\r
7019 case '\021': /* Ctrl+Q */
\r
7020 quoteNextChar = TRUE;
\r
7030 GetWindowText(hwnd, buf, MSG_SIZ);
\r
7031 p = PrevInHistory(buf);
\r
7033 SetWindowText(hwnd, p);
\r
7034 sel.cpMin = 999999;
\r
7035 sel.cpMax = 999999;
\r
7036 SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
7041 p = NextInHistory();
\r
7043 SetWindowText(hwnd, p);
\r
7044 sel.cpMin = 999999;
\r
7045 sel.cpMax = 999999;
\r
7046 SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
7052 if (!(GetKeyState(VK_CONTROL) & ~1)) break;
\r
7056 SendDlgItemMessage(hwndConsole, OPT_ConsoleText, message, wParam, lParam);
\r
7060 case WM_MBUTTONDOWN:
\r
7061 SendDlgItemMessage(hwndConsole, OPT_ConsoleText,
\r
7062 WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);
\r
7064 case WM_RBUTTONUP:
\r
7065 if (GetKeyState(VK_SHIFT) & ~1) {
\r
7066 SendDlgItemMessage(hwndConsole, OPT_ConsoleText,
\r
7067 WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);
\r
7071 hmenu = LoadMenu(hInst, "InputMenu");
\r
7072 SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);
\r
7073 if (sel.cpMin == sel.cpMax) {
\r
7074 EnableMenuItem(hmenu, IDM_Copy, MF_BYCOMMAND|MF_GRAYED);
\r
7075 EnableMenuItem(hmenu, IDM_Cut, MF_BYCOMMAND|MF_GRAYED);
\r
7077 if (!IsClipboardFormatAvailable(CF_TEXT)) {
\r
7078 EnableMenuItem(hmenu, IDM_Paste, MF_BYCOMMAND|MF_GRAYED);
\r
7080 pt.x = LOWORD(lParam);
\r
7081 pt.y = HIWORD(lParam);
\r
7082 MenuPopup(hwnd, pt, hmenu, -1);
\r
7086 switch (LOWORD(wParam)) {
\r
7088 SendMessage(hwnd, EM_UNDO, 0, 0);
\r
7090 case IDM_SelectAll:
\r
7092 sel.cpMax = -1; /*999999?*/
\r
7093 SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
7096 SendMessage(hwnd, WM_CUT, 0, 0);
\r
7099 SendMessage(hwnd, WM_PASTE, 0, 0);
\r
7102 SendMessage(hwnd, WM_COPY, 0, 0);
\r
7107 return (*consoleInputWindowProc)(hwnd, message, wParam, lParam);
\r
7110 #define CO_MAX 100000
\r
7111 #define CO_TRIM 1000
\r
7114 ConsoleWndProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
7116 static SnapData sd;
\r
7117 HWND hText, hInput;
\r
7119 static int sizeX, sizeY;
\r
7120 int newSizeX, newSizeY;
\r
7124 hText = GetDlgItem(hDlg, OPT_ConsoleText);
\r
7125 hInput = GetDlgItem(hDlg, OPT_ConsoleInput);
\r
7127 switch (message) {
\r
7129 if (((NMHDR*)lParam)->code == EN_LINK)
\r
7131 ENLINK *pLink = (ENLINK*)lParam;
\r
7132 if (pLink->msg == WM_LBUTTONUP)
\r
7136 tr.chrg = pLink->chrg;
\r
7137 tr.lpstrText = malloc(1+tr.chrg.cpMax-tr.chrg.cpMin);
\r
7138 SendMessage(hText, EM_GETTEXTRANGE, 0, (LPARAM)&tr);
\r
7139 ShellExecute(NULL, "open", tr.lpstrText, NULL, NULL, SW_SHOW);
\r
7140 free(tr.lpstrText);
\r
7144 case WM_INITDIALOG: /* message: initialize dialog box */
\r
7145 hwndConsole = hDlg;
\r
7147 consoleTextWindowProc = (WNDPROC)
\r
7148 SetWindowLong(hText, GWL_WNDPROC, (LONG) ConsoleTextSubclass);
\r
7149 SendMessage(hText, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);
\r
7150 consoleInputWindowProc = (WNDPROC)
\r
7151 SetWindowLong(hInput, GWL_WNDPROC, (LONG) ConsoleInputSubclass);
\r
7152 SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);
\r
7153 Colorize(ColorNormal, TRUE);
\r
7154 SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &consoleCF);
\r
7155 ChangedConsoleFont();
\r
7156 GetClientRect(hDlg, &rect);
\r
7157 sizeX = rect.right;
\r
7158 sizeY = rect.bottom;
\r
7159 if (wpConsole.x != CW_USEDEFAULT && wpConsole.y != CW_USEDEFAULT &&
\r
7160 wpConsole.width != CW_USEDEFAULT && wpConsole.height != CW_USEDEFAULT) {
\r
7161 WINDOWPLACEMENT wp;
\r
7162 EnsureOnScreen(&wpConsole.x, &wpConsole.y, 0, 0);
\r
7163 wp.length = sizeof(WINDOWPLACEMENT);
\r
7165 wp.showCmd = SW_SHOW;
\r
7166 wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;
\r
7167 wp.rcNormalPosition.left = wpConsole.x;
\r
7168 wp.rcNormalPosition.right = wpConsole.x + wpConsole.width;
\r
7169 wp.rcNormalPosition.top = wpConsole.y;
\r
7170 wp.rcNormalPosition.bottom = wpConsole.y + wpConsole.height;
\r
7171 SetWindowPlacement(hDlg, &wp);
\r
7174 // [HGM] Chessknight's change 2004-07-13
\r
7175 else { /* Determine Defaults */
\r
7176 WINDOWPLACEMENT wp;
\r
7177 wpConsole.x = wpMain.width + 1;
\r
7178 wpConsole.y = wpMain.y;
\r
7179 wpConsole.width = screenWidth - wpMain.width;
\r
7180 wpConsole.height = wpMain.height;
\r
7181 EnsureOnScreen(&wpConsole.x, &wpConsole.y, 0, 0);
\r
7182 wp.length = sizeof(WINDOWPLACEMENT);
\r
7184 wp.showCmd = SW_SHOW;
\r
7185 wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;
\r
7186 wp.rcNormalPosition.left = wpConsole.x;
\r
7187 wp.rcNormalPosition.right = wpConsole.x + wpConsole.width;
\r
7188 wp.rcNormalPosition.top = wpConsole.y;
\r
7189 wp.rcNormalPosition.bottom = wpConsole.y + wpConsole.height;
\r
7190 SetWindowPlacement(hDlg, &wp);
\r
7193 // Allow hText to highlight URLs and send notifications on them
\r
7194 wMask = (WORD) SendMessage(hText, EM_GETEVENTMASK, 0, 0L);
\r
7195 SendMessage(hText, EM_SETEVENTMASK, 0, wMask | ENM_LINK);
\r
7196 SendMessage(hText, EM_AUTOURLDETECT, TRUE, 0L);
\r
7197 SetWindowLong(hText, GWL_USERDATA, 79); // initialize the text window's width
\r
7211 if (IsIconic(hDlg)) break;
\r
7212 newSizeX = LOWORD(lParam);
\r
7213 newSizeY = HIWORD(lParam);
\r
7214 if (sizeX != newSizeX || sizeY != newSizeY) {
\r
7215 RECT rectText, rectInput;
\r
7217 int newTextHeight, newTextWidth;
\r
7218 GetWindowRect(hText, &rectText);
\r
7219 newTextWidth = rectText.right - rectText.left + newSizeX - sizeX;
\r
7220 newTextHeight = rectText.bottom - rectText.top + newSizeY - sizeY;
\r
7221 if (newTextHeight < 0) {
\r
7222 newSizeY += -newTextHeight;
\r
7223 newTextHeight = 0;
\r
7225 SetWindowPos(hText, NULL, 0, 0,
\r
7226 newTextWidth, newTextHeight, SWP_NOZORDER|SWP_NOMOVE);
\r
7227 GetWindowRect(hInput, &rectInput); /* gives screen coords */
\r
7228 pt.x = rectInput.left;
\r
7229 pt.y = rectInput.top + newSizeY - sizeY;
\r
7230 ScreenToClient(hDlg, &pt);
\r
7231 SetWindowPos(hInput, NULL,
\r
7232 pt.x, pt.y, /* needs client coords */
\r
7233 rectInput.right - rectInput.left + newSizeX - sizeX,
\r
7234 rectInput.bottom - rectInput.top, SWP_NOZORDER);
\r
7240 case WM_GETMINMAXINFO:
\r
7241 /* Prevent resizing window too small */
\r
7242 mmi = (MINMAXINFO *) lParam;
\r
7243 mmi->ptMinTrackSize.x = 100;
\r
7244 mmi->ptMinTrackSize.y = 100;
\r
7247 /* [AS] Snapping */
\r
7248 case WM_ENTERSIZEMOVE:
\r
7249 return OnEnterSizeMove( &sd, hDlg, wParam, lParam );
\r
7252 return OnSizing( &sd, hDlg, wParam, lParam );
\r
7255 return OnMoving( &sd, hDlg, wParam, lParam );
\r
7257 case WM_EXITSIZEMOVE:
\r
7258 UpdateICSWidth(hText);
\r
7259 return OnExitSizeMove( &sd, hDlg, wParam, lParam );
\r
7262 return DefWindowProc(hDlg, message, wParam, lParam);
\r
7270 if (hwndConsole) return;
\r
7271 hCons = CreateDialog(hInst, szConsoleName, 0, NULL);
\r
7272 SendMessage(hCons, WM_INITDIALOG, 0, 0);
\r
7277 ConsoleOutput(char* data, int length, int forceVisible)
\r
7282 char buf[CO_MAX+1];
\r
7285 static int delayLF = 0;
\r
7286 CHARRANGE savesel, sel;
\r
7288 if (hwndConsole == NULL || length > CO_MAX-100 || length == 0) return;
\r
7296 while (length--) {
\r
7304 } else if (*p == '\007') {
\r
7305 MyPlaySound(&sounds[(int)SoundBell]);
\r
7312 hText = GetDlgItem(hwndConsole, OPT_ConsoleText);
\r
7313 SendMessage(hText, EM_HIDESELECTION, TRUE, FALSE);
\r
7314 /* Save current selection */
\r
7315 SendMessage(hText, EM_EXGETSEL, 0, (LPARAM)&savesel);
\r
7316 exlen = GetWindowTextLength(hText);
\r
7317 /* Find out whether current end of text is visible */
\r
7318 SendMessage(hText, EM_GETRECT, 0, (LPARAM) &rect);
\r
7319 SendMessage(hText, EM_POSFROMCHAR, (WPARAM) &pEnd, exlen);
\r
7320 /* Trim existing text if it's too long */
\r
7321 if (exlen + (q - buf) > CO_MAX) {
\r
7322 trim = (CO_TRIM > (q - buf)) ? CO_TRIM : (q - buf);
\r
7325 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
7326 SendMessage(hText, EM_REPLACESEL, 0, (LPARAM)"");
\r
7328 savesel.cpMin -= trim;
\r
7329 savesel.cpMax -= trim;
\r
7330 if (exlen < 0) exlen = 0;
\r
7331 if (savesel.cpMin < 0) savesel.cpMin = 0;
\r
7332 if (savesel.cpMax < savesel.cpMin) savesel.cpMax = savesel.cpMin;
\r
7334 /* Append the new text */
\r
7335 sel.cpMin = exlen;
\r
7336 sel.cpMax = exlen;
\r
7337 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
7338 SendMessage(hText, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&consoleCF);
\r
7339 SendMessage(hText, EM_REPLACESEL, 0, (LPARAM) buf);
\r
7340 if (forceVisible || exlen == 0 ||
\r
7341 (rect.left <= pEnd.x && pEnd.x < rect.right &&
\r
7342 rect.top <= pEnd.y && pEnd.y < rect.bottom)) {
\r
7343 /* Scroll to make new end of text visible if old end of text
\r
7344 was visible or new text is an echo of user typein */
\r
7345 sel.cpMin = 9999999;
\r
7346 sel.cpMax = 9999999;
\r
7347 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
7348 SendMessage(hText, EM_HIDESELECTION, FALSE, FALSE);
\r
7349 SendMessage(hText, EM_SCROLLCARET, 0, 0);
\r
7350 SendMessage(hText, EM_HIDESELECTION, TRUE, FALSE);
\r
7352 if (savesel.cpMax == exlen || forceVisible) {
\r
7353 /* Move insert point to new end of text if it was at the old
\r
7354 end of text or if the new text is an echo of user typein */
\r
7355 sel.cpMin = 9999999;
\r
7356 sel.cpMax = 9999999;
\r
7357 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
7359 /* Restore previous selection */
\r
7360 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&savesel);
\r
7362 SendMessage(hText, EM_HIDESELECTION, FALSE, FALSE);
\r
7369 DisplayHoldingsCount(HDC hdc, int x, int y, int rightAlign, int copyNumber)
\r
7373 COLORREF oldFg, oldBg;
\r
7378 snprintf(buf, sizeof(buf)/sizeof(buf[0]),"%d", copyNumber); else buf[0] = 0;
\r
7380 oldFg = SetTextColor(hdc, RGB(255, 255, 255)); /* white */
\r
7381 oldBg = SetBkColor(hdc, RGB(0, 0, 0)); /* black */
\r
7382 oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);
\r
7385 rect.right = x + squareSize;
\r
7387 rect.bottom = y + squareSize;
\r
7390 ExtTextOut(hdc, x + MESSAGE_LINE_LEFTMARGIN
\r
7391 + (rightAlign ? (squareSize*2)/3 : 0),
\r
7392 y, ETO_CLIPPED|ETO_OPAQUE,
\r
7393 &rect, str, strlen(str), NULL);
\r
7395 (void) SetTextColor(hdc, oldFg);
\r
7396 (void) SetBkColor(hdc, oldBg);
\r
7397 (void) SelectObject(hdc, oldFont);
\r
7401 DisplayAClock(HDC hdc, int timeRemaining, int highlight,
\r
7402 RECT *rect, char *color, char *flagFell)
\r
7406 COLORREF oldFg, oldBg;
\r
7409 if (appData.clockMode) {
\r
7411 snprintf(buf, sizeof(buf)/sizeof(buf[0]), "%c %s %s", color[0], TimeString(timeRemaining), flagFell);
\r
7413 snprintf(buf, sizeof(buf)/sizeof(buf[0]), "%s:%c%s %s", color, (logoHeight>0 ? 0 : ' '), TimeString(timeRemaining), flagFell);
\r
7420 oldFg = SetTextColor(hdc, RGB(255, 255, 255)); /* white */
\r
7421 oldBg = SetBkColor(hdc, RGB(0, 0, 0)); /* black */
\r
7423 oldFg = SetTextColor(hdc, RGB(0, 0, 0)); /* black */
\r
7424 oldBg = SetBkColor(hdc, RGB(255, 255, 255)); /* white */
\r
7426 oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);
\r
7430 ExtTextOut(hdc, rect->left + MESSAGE_LINE_LEFTMARGIN,
\r
7431 rect->top, ETO_CLIPPED|ETO_OPAQUE,
\r
7432 rect, str, strlen(str), NULL);
\r
7433 if(logoHeight > 0 && appData.clockMode) {
\r
7435 snprintf(buf, sizeof(buf)/sizeof(buf[0]), "%s %s", buf+7, flagFell);
\r
7436 r.top = rect->top + logoHeight/2;
\r
7437 r.left = rect->left;
\r
7438 r.right = rect->right;
\r
7439 r.bottom = rect->bottom;
\r
7440 ExtTextOut(hdc, rect->left + MESSAGE_LINE_LEFTMARGIN,
\r
7441 r.top, ETO_CLIPPED|ETO_OPAQUE,
\r
7442 &r, str, strlen(str), NULL);
\r
7444 (void) SetTextColor(hdc, oldFg);
\r
7445 (void) SetBkColor(hdc, oldBg);
\r
7446 (void) SelectObject(hdc, oldFont);
\r
7451 DoReadFile(HANDLE hFile, char *buf, int count, DWORD *outCount,
\r
7457 if( count <= 0 ) {
\r
7458 if (appData.debugMode) {
\r
7459 fprintf( debugFP, "DoReadFile: trying to read past end of buffer, overflow = %d\n", count );
\r
7462 return ERROR_INVALID_USER_BUFFER;
\r
7465 ResetEvent(ovl->hEvent);
\r
7466 ovl->Offset = ovl->OffsetHigh = 0;
\r
7467 ok = ReadFile(hFile, buf, count, outCount, ovl);
\r
7471 err = GetLastError();
\r
7472 if (err == ERROR_IO_PENDING) {
\r
7473 ok = GetOverlappedResult(hFile, ovl, outCount, TRUE);
\r
7477 err = GetLastError();
\r
7484 DoWriteFile(HANDLE hFile, char *buf, int count, DWORD *outCount,
\r
7489 ResetEvent(ovl->hEvent);
\r
7490 ovl->Offset = ovl->OffsetHigh = 0;
\r
7491 ok = WriteFile(hFile, buf, count, outCount, ovl);
\r
7495 err = GetLastError();
\r
7496 if (err == ERROR_IO_PENDING) {
\r
7497 ok = GetOverlappedResult(hFile, ovl, outCount, TRUE);
\r
7501 err = GetLastError();
\r
7507 /* [AS] If input is line by line and a line exceed the buffer size, force an error */
\r
7508 void CheckForInputBufferFull( InputSource * is )
\r
7510 if( is->lineByLine && (is->next - is->buf) >= INPUT_SOURCE_BUF_SIZE ) {
\r
7511 /* Look for end of line */
\r
7512 char * p = is->buf;
\r
7514 while( p < is->next && *p != '\n' ) {
\r
7518 if( p >= is->next ) {
\r
7519 if (appData.debugMode) {
\r
7520 fprintf( debugFP, "Input line exceeded buffer size (source id=%lu)\n", is->id );
\r
7523 is->error = ERROR_BROKEN_PIPE; /* [AS] Just any non-successful code! */
\r
7524 is->count = (DWORD) -1;
\r
7525 is->next = is->buf;
\r
7531 InputThread(LPVOID arg)
\r
7536 is = (InputSource *) arg;
\r
7537 ovl.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
\r
7538 ovl.Internal = ovl.InternalHigh = ovl.Offset = ovl.OffsetHigh = 0;
\r
7539 while (is->hThread != NULL) {
\r
7540 is->error = DoReadFile(is->hFile, is->next,
\r
7541 INPUT_SOURCE_BUF_SIZE - (is->next - is->buf),
\r
7542 &is->count, &ovl);
\r
7543 if (is->error == NO_ERROR) {
\r
7544 is->next += is->count;
\r
7546 if (is->error == ERROR_BROKEN_PIPE) {
\r
7547 /* Correct for MS brain damage. EOF reading a pipe is not an error. */
\r
7550 is->count = (DWORD) -1;
\r
7551 /* [AS] The (is->count <= 0) check below is not useful for unsigned values! */
\r
7556 CheckForInputBufferFull( is );
\r
7558 SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);
\r
7560 if( is->count == ((DWORD) -1) ) break; /* [AS] */
\r
7562 if (is->count <= 0) break; /* Quit on EOF or error */
\r
7565 CloseHandle(ovl.hEvent);
\r
7566 CloseHandle(is->hFile);
\r
7568 if (appData.debugMode) {
\r
7569 fprintf( debugFP, "Input thread terminated (id=%lu, error=%d, count=%ld)\n", is->id, is->error, is->count );
\r
7576 /* Windows 95 beta 2 won't let you do overlapped i/o on a console or pipe */
\r
7578 NonOvlInputThread(LPVOID arg)
\r
7585 is = (InputSource *) arg;
\r
7586 while (is->hThread != NULL) {
\r
7587 is->error = ReadFile(is->hFile, is->next,
\r
7588 INPUT_SOURCE_BUF_SIZE - (is->next - is->buf),
\r
7589 &is->count, NULL) ? NO_ERROR : GetLastError();
\r
7590 if (is->error == NO_ERROR) {
\r
7591 /* Change CRLF to LF */
\r
7592 if (is->next > is->buf) {
\r
7594 i = is->count + 1;
\r
7602 if (prev == '\r' && *p == '\n') {
\r
7614 if (is->error == ERROR_BROKEN_PIPE) {
\r
7615 /* Correct for MS brain damage. EOF reading a pipe is not an error. */
\r
7618 is->count = (DWORD) -1;
\r
7622 CheckForInputBufferFull( is );
\r
7624 SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);
\r
7626 if( is->count == ((DWORD) -1) ) break; /* [AS] */
\r
7628 if (is->count < 0) break; /* Quit on error */
\r
7630 CloseHandle(is->hFile);
\r
7635 SocketInputThread(LPVOID arg)
\r
7639 is = (InputSource *) arg;
\r
7640 while (is->hThread != NULL) {
\r
7641 is->count = recv(is->sock, is->buf, INPUT_SOURCE_BUF_SIZE, 0);
\r
7642 if ((int)is->count == SOCKET_ERROR) {
\r
7643 is->count = (DWORD) -1;
\r
7644 is->error = WSAGetLastError();
\r
7646 is->error = NO_ERROR;
\r
7647 is->next += is->count;
\r
7648 if (is->count == 0 && is->second == is) {
\r
7649 /* End of file on stderr; quit with no message */
\r
7653 SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);
\r
7655 if( is->count == ((DWORD) -1) ) break; /* [AS] */
\r
7657 if (is->count <= 0) break; /* Quit on EOF or error */
\r
7663 InputEvent(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
\r
7667 is = (InputSource *) lParam;
\r
7668 if (is->lineByLine) {
\r
7669 /* Feed in lines one by one */
\r
7670 char *p = is->buf;
\r
7672 while (q < is->next) {
\r
7673 if (*q++ == '\n') {
\r
7674 (is->func)(is, is->closure, p, q - p, NO_ERROR);
\r
7679 /* Move any partial line to the start of the buffer */
\r
7681 while (p < is->next) {
\r
7686 if (is->error != NO_ERROR || is->count == 0) {
\r
7687 /* Notify backend of the error. Note: If there was a partial
\r
7688 line at the end, it is not flushed through. */
\r
7689 (is->func)(is, is->closure, is->buf, is->count, is->error);
\r
7692 /* Feed in the whole chunk of input at once */
\r
7693 (is->func)(is, is->closure, is->buf, is->count, is->error);
\r
7694 is->next = is->buf;
\r
7698 /*---------------------------------------------------------------------------*\
\r
7700 * Menu enables. Used when setting various modes.
\r
7702 \*---------------------------------------------------------------------------*/
\r
7710 GreyRevert(Boolean grey)
\r
7711 { // [HGM] vari: for retracting variations in local mode
\r
7712 HMENU hmenu = GetMenu(hwndMain);
\r
7713 EnableMenuItem(hmenu, IDM_Revert, MF_BYCOMMAND|(grey ? MF_GRAYED : MF_ENABLED));
\r
7714 EnableMenuItem(hmenu, IDM_Annotate, MF_BYCOMMAND|(grey ? MF_GRAYED : MF_ENABLED));
\r
7718 SetMenuEnables(HMENU hmenu, Enables *enab)
\r
7720 while (enab->item > 0) {
\r
7721 (void) EnableMenuItem(hmenu, enab->item, enab->flags);
\r
7726 Enables gnuEnables[] = {
\r
7727 { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },
\r
7728 { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },
\r
7729 { IDM_IcsClient, MF_BYCOMMAND|MF_GRAYED },
\r
7730 { IDM_Accept, MF_BYCOMMAND|MF_GRAYED },
\r
7731 { IDM_Decline, MF_BYCOMMAND|MF_GRAYED },
\r
7732 { IDM_Rematch, MF_BYCOMMAND|MF_GRAYED },
\r
7733 { IDM_Adjourn, MF_BYCOMMAND|MF_GRAYED },
\r
7734 { IDM_StopExamining, MF_BYCOMMAND|MF_GRAYED },
\r
7735 { IDM_StopObserving, MF_BYCOMMAND|MF_GRAYED },
\r
7736 { IDM_Upload, MF_BYCOMMAND|MF_GRAYED },
\r
7737 { IDM_Revert, MF_BYCOMMAND|MF_GRAYED },
\r
7738 { IDM_Annotate, MF_BYCOMMAND|MF_GRAYED },
\r
7739 { IDM_NewChat, MF_BYCOMMAND|MF_GRAYED },
\r
7743 Enables icsEnables[] = {
\r
7744 { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },
\r
7745 { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },
\r
7746 { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },
\r
7747 { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },
\r
7748 { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },
\r
7749 { IDM_Match, MF_BYCOMMAND|MF_GRAYED },
\r
7750 { IDM_MachineBoth, MF_BYCOMMAND|MF_GRAYED },
\r
7751 { IDM_AnalysisMode, MF_BYCOMMAND|MF_ENABLED },
\r
7752 { IDM_AnalyzeFile, MF_BYCOMMAND|MF_GRAYED },
\r
7753 { IDM_TimeControl, MF_BYCOMMAND|MF_GRAYED },
\r
7754 { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },
\r
7755 { IDM_Hint, MF_BYCOMMAND|MF_GRAYED },
\r
7756 { IDM_Book, MF_BYCOMMAND|MF_GRAYED },
\r
7757 { IDM_IcsOptions, MF_BYCOMMAND|MF_ENABLED },
\r
7758 { IDM_Engine1Options, MF_BYCOMMAND|MF_GRAYED },
\r
7759 { IDM_Engine2Options, MF_BYCOMMAND|MF_GRAYED },
\r
7760 { IDM_Annotate, MF_BYCOMMAND|MF_GRAYED },
\r
7765 Enables zippyEnables[] = {
\r
7766 { IDM_MoveNow, MF_BYCOMMAND|MF_ENABLED },
\r
7767 { IDM_Hint, MF_BYCOMMAND|MF_ENABLED },
\r
7768 { IDM_Book, MF_BYCOMMAND|MF_ENABLED },
\r
7769 { IDM_Engine1Options, MF_BYCOMMAND|MF_ENABLED },
\r
7774 Enables ncpEnables[] = {
\r
7775 { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },
\r
7776 { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },
\r
7777 { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },
\r
7778 { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },
\r
7779 { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },
\r
7780 { IDM_Match, MF_BYCOMMAND|MF_GRAYED },
\r
7781 { IDM_AnalysisMode, MF_BYCOMMAND|MF_GRAYED },
\r
7782 { IDM_AnalyzeFile, MF_BYCOMMAND|MF_GRAYED },
\r
7783 { IDM_IcsClient, MF_BYCOMMAND|MF_GRAYED },
\r
7784 { ACTION_POS, MF_BYPOSITION|MF_GRAYED },
\r
7785 { IDM_Revert, MF_BYCOMMAND|MF_GRAYED },
\r
7786 { IDM_Annotate, MF_BYCOMMAND|MF_GRAYED },
\r
7787 { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },
\r
7788 { IDM_RetractMove, MF_BYCOMMAND|MF_GRAYED },
\r
7789 { IDM_TimeControl, MF_BYCOMMAND|MF_GRAYED },
\r
7790 { IDM_Hint, MF_BYCOMMAND|MF_GRAYED },
\r
7791 { IDM_Book, MF_BYCOMMAND|MF_GRAYED },
\r
7792 { IDM_MachineBoth, MF_BYCOMMAND|MF_GRAYED },
\r
7793 { IDM_NewChat, MF_BYCOMMAND|MF_GRAYED },
\r
7794 { IDM_Engine1Options, MF_BYCOMMAND|MF_GRAYED },
\r
7795 { IDM_Engine2Options, MF_BYCOMMAND|MF_GRAYED },
\r
7799 Enables trainingOnEnables[] = {
\r
7800 { IDM_EditComment, MF_BYCOMMAND|MF_GRAYED },
\r
7801 { IDM_Pause, MF_BYCOMMAND|MF_GRAYED },
\r
7802 { IDM_Forward, MF_BYCOMMAND|MF_GRAYED },
\r
7803 { IDM_Backward, MF_BYCOMMAND|MF_GRAYED },
\r
7804 { IDM_ToEnd, MF_BYCOMMAND|MF_GRAYED },
\r
7805 { IDM_ToStart, MF_BYCOMMAND|MF_GRAYED },
\r
7806 { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },
\r
7807 { IDM_TruncateGame, MF_BYCOMMAND|MF_GRAYED },
\r
7811 Enables trainingOffEnables[] = {
\r
7812 { IDM_EditComment, MF_BYCOMMAND|MF_ENABLED },
\r
7813 { IDM_Pause, MF_BYCOMMAND|MF_ENABLED },
\r
7814 { IDM_Forward, MF_BYCOMMAND|MF_ENABLED },
\r
7815 { IDM_Backward, MF_BYCOMMAND|MF_ENABLED },
\r
7816 { IDM_ToEnd, MF_BYCOMMAND|MF_ENABLED },
\r
7817 { IDM_ToStart, MF_BYCOMMAND|MF_ENABLED },
\r
7818 { IDM_MoveNow, MF_BYCOMMAND|MF_ENABLED },
\r
7819 { IDM_TruncateGame, MF_BYCOMMAND|MF_ENABLED },
\r
7823 /* These modify either ncpEnables or gnuEnables */
\r
7824 Enables cmailEnables[] = {
\r
7825 { IDM_MailMove, MF_BYCOMMAND|MF_ENABLED },
\r
7826 { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_ENABLED },
\r
7827 { ACTION_POS, MF_BYPOSITION|MF_ENABLED },
\r
7828 { IDM_CallFlag, MF_BYCOMMAND|MF_GRAYED },
\r
7829 { IDM_Draw, MF_BYCOMMAND|MF_ENABLED },
\r
7830 { IDM_Adjourn, MF_BYCOMMAND|MF_GRAYED },
\r
7831 { IDM_Abort, MF_BYCOMMAND|MF_GRAYED },
\r
7835 Enables machineThinkingEnables[] = {
\r
7836 { IDM_LoadGame, MF_BYCOMMAND|MF_GRAYED },
\r
7837 { IDM_LoadNextGame, MF_BYCOMMAND|MF_GRAYED },
\r
7838 { IDM_LoadPrevGame, MF_BYCOMMAND|MF_GRAYED },
\r
7839 { IDM_ReloadGame, MF_BYCOMMAND|MF_GRAYED },
\r
7840 { IDM_PasteGame, MF_BYCOMMAND|MF_GRAYED },
\r
7841 { IDM_LoadPosition, MF_BYCOMMAND|MF_GRAYED },
\r
7842 { IDM_LoadNextPosition, MF_BYCOMMAND|MF_GRAYED },
\r
7843 { IDM_LoadPrevPosition, MF_BYCOMMAND|MF_GRAYED },
\r
7844 { IDM_ReloadPosition, MF_BYCOMMAND|MF_GRAYED },
\r
7845 { IDM_PastePosition, MF_BYCOMMAND|MF_GRAYED },
\r
7846 { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },
\r
7847 { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },
\r
7848 { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },
\r
7849 { IDM_Match, MF_BYCOMMAND|MF_GRAYED },
\r
7850 { IDM_TypeInMove, MF_BYCOMMAND|MF_GRAYED },
\r
7851 { IDM_RetractMove, MF_BYCOMMAND|MF_GRAYED },
\r
7855 Enables userThinkingEnables[] = {
\r
7856 { IDM_LoadGame, MF_BYCOMMAND|MF_ENABLED },
\r
7857 { IDM_LoadNextGame, MF_BYCOMMAND|MF_ENABLED },
\r
7858 { IDM_LoadPrevGame, MF_BYCOMMAND|MF_ENABLED },
\r
7859 { IDM_ReloadGame, MF_BYCOMMAND|MF_ENABLED },
\r
7860 { IDM_PasteGame, MF_BYCOMMAND|MF_ENABLED },
\r
7861 { IDM_LoadPosition, MF_BYCOMMAND|MF_ENABLED },
\r
7862 { IDM_LoadNextPosition, MF_BYCOMMAND|MF_ENABLED },
\r
7863 { IDM_LoadPrevPosition, MF_BYCOMMAND|MF_ENABLED },
\r
7864 { IDM_ReloadPosition, MF_BYCOMMAND|MF_ENABLED },
\r
7865 { IDM_PastePosition, MF_BYCOMMAND|MF_ENABLED },
\r
7866 { IDM_MachineWhite, MF_BYCOMMAND|MF_ENABLED },
\r
7867 { IDM_MachineBlack, MF_BYCOMMAND|MF_ENABLED },
\r
7868 { IDM_TwoMachines, MF_BYCOMMAND|MF_ENABLED },
\r
7869 { IDM_Match, MF_BYCOMMAND|MF_ENABLED },
\r
7870 { IDM_TypeInMove, MF_BYCOMMAND|MF_ENABLED },
\r
7871 { IDM_RetractMove, MF_BYCOMMAND|MF_ENABLED },
\r
7875 /*---------------------------------------------------------------------------*\
\r
7877 * Front-end interface functions exported by XBoard.
\r
7878 * Functions appear in same order as prototypes in frontend.h.
\r
7880 \*---------------------------------------------------------------------------*/
\r
7884 static UINT prevChecked = 0;
\r
7885 static int prevPausing = 0;
\r
7888 if (pausing != prevPausing) {
\r
7889 prevPausing = pausing;
\r
7890 (void) CheckMenuItem(GetMenu(hwndMain), IDM_Pause,
\r
7891 MF_BYCOMMAND|(pausing ? MF_CHECKED : MF_UNCHECKED));
\r
7892 if (hwndPause) SetWindowText(hwndPause, pausing ? "C" : "P");
\r
7895 switch (gameMode) {
\r
7896 case BeginningOfGame:
\r
7897 if (appData.icsActive)
\r
7898 nowChecked = IDM_IcsClient;
\r
7899 else if (appData.noChessProgram)
\r
7900 nowChecked = IDM_EditGame;
\r
7902 nowChecked = IDM_MachineBlack;
\r
7904 case MachinePlaysBlack:
\r
7905 nowChecked = IDM_MachineBlack;
\r
7907 case MachinePlaysWhite:
\r
7908 nowChecked = IDM_MachineWhite;
\r
7910 case TwoMachinesPlay:
\r
7911 nowChecked = matchMode ? IDM_Match : IDM_TwoMachines; // [HGM] match
\r
7914 nowChecked = IDM_AnalysisMode;
\r
7917 nowChecked = IDM_AnalyzeFile;
\r
7920 nowChecked = IDM_EditGame;
\r
7922 case PlayFromGameFile:
\r
7923 nowChecked = IDM_LoadGame;
\r
7925 case EditPosition:
\r
7926 nowChecked = IDM_EditPosition;
\r
7929 nowChecked = IDM_Training;
\r
7931 case IcsPlayingWhite:
\r
7932 case IcsPlayingBlack:
\r
7933 case IcsObserving:
\r
7935 nowChecked = IDM_IcsClient;
\r
7942 if (prevChecked != 0)
\r
7943 (void) CheckMenuItem(GetMenu(hwndMain),
\r
7944 prevChecked, MF_BYCOMMAND|MF_UNCHECKED);
\r
7945 if (nowChecked != 0)
\r
7946 (void) CheckMenuItem(GetMenu(hwndMain),
\r
7947 nowChecked, MF_BYCOMMAND|MF_CHECKED);
\r
7949 if (nowChecked == IDM_LoadGame || nowChecked == IDM_Training) {
\r
7950 (void) EnableMenuItem(GetMenu(hwndMain), IDM_Training,
\r
7951 MF_BYCOMMAND|MF_ENABLED);
\r
7953 (void) EnableMenuItem(GetMenu(hwndMain),
\r
7954 IDM_Training, MF_BYCOMMAND|MF_GRAYED);
\r
7957 prevChecked = nowChecked;
\r
7959 /* [DM] icsEngineAnalyze - Do a sceure check too */
\r
7960 if (appData.icsActive) {
\r
7961 if (appData.icsEngineAnalyze) {
\r
7962 (void) CheckMenuItem(GetMenu(hwndMain), IDM_AnalysisMode,
\r
7963 MF_BYCOMMAND|MF_CHECKED);
\r
7965 (void) CheckMenuItem(GetMenu(hwndMain), IDM_AnalysisMode,
\r
7966 MF_BYCOMMAND|MF_UNCHECKED);
\r
7974 HMENU hmenu = GetMenu(hwndMain);
\r
7975 SetMenuEnables(hmenu, icsEnables);
\r
7976 EnableMenuItem(GetSubMenu(hmenu, OPTIONS_POS), ICS_POS,
\r
7977 MF_BYPOSITION|MF_ENABLED);
\r
7979 if (appData.zippyPlay) {
\r
7980 SetMenuEnables(hmenu, zippyEnables);
\r
7981 if (!appData.noChessProgram) /* [DM] icsEngineAnalyze */
\r
7982 (void) EnableMenuItem(GetMenu(hwndMain), IDM_AnalysisMode,
\r
7983 MF_BYCOMMAND|MF_ENABLED);
\r
7991 SetMenuEnables(GetMenu(hwndMain), gnuEnables);
\r
7997 HMENU hmenu = GetMenu(hwndMain);
\r
7998 SetMenuEnables(hmenu, ncpEnables);
\r
7999 EnableMenuItem(GetSubMenu(hmenu, OPTIONS_POS), SOUNDS_POS,
\r
8000 MF_BYPOSITION|MF_GRAYED);
\r
8001 DrawMenuBar(hwndMain);
\r
8007 SetMenuEnables(GetMenu(hwndMain), cmailEnables);
\r
8011 SetTrainingModeOn()
\r
8014 SetMenuEnables(GetMenu(hwndMain), trainingOnEnables);
\r
8015 for (i = 0; i < N_BUTTONS; i++) {
\r
8016 if (buttonDesc[i].hwnd != NULL)
\r
8017 EnableWindow(buttonDesc[i].hwnd, FALSE);
\r
8022 VOID SetTrainingModeOff()
\r
8025 SetMenuEnables(GetMenu(hwndMain), trainingOffEnables);
\r
8026 for (i = 0; i < N_BUTTONS; i++) {
\r
8027 if (buttonDesc[i].hwnd != NULL)
\r
8028 EnableWindow(buttonDesc[i].hwnd, TRUE);
\r
8034 SetUserThinkingEnables()
\r
8036 SetMenuEnables(GetMenu(hwndMain), userThinkingEnables);
\r
8040 SetMachineThinkingEnables()
\r
8042 HMENU hMenu = GetMenu(hwndMain);
\r
8043 int flags = MF_BYCOMMAND|MF_ENABLED;
\r
8045 SetMenuEnables(hMenu, machineThinkingEnables);
\r
8047 if (gameMode == MachinePlaysBlack) {
\r
8048 (void)EnableMenuItem(hMenu, IDM_MachineBlack, flags);
\r
8049 } else if (gameMode == MachinePlaysWhite) {
\r
8050 (void)EnableMenuItem(hMenu, IDM_MachineWhite, flags);
\r
8051 } else if (gameMode == TwoMachinesPlay) {
\r
8052 (void)EnableMenuItem(hMenu, matchMode ? IDM_Match : IDM_TwoMachines, flags); // [HGM] match
\r
8058 DisplayTitle(char *str)
\r
8060 char title[MSG_SIZ], *host;
\r
8061 if (str[0] != NULLCHAR) {
\r
8062 safeStrCpy(title, str, sizeof(title)/sizeof(title[0]) );
\r
8063 } else if (appData.icsActive) {
\r
8064 if (appData.icsCommPort[0] != NULLCHAR)
\r
8067 host = appData.icsHost;
\r
8068 snprintf(title, MSG_SIZ, "%s: %s", szTitle, host);
\r
8069 } else if (appData.noChessProgram) {
\r
8070 safeStrCpy(title, szTitle, sizeof(title)/sizeof(title[0]) );
\r
8072 safeStrCpy(title, szTitle, sizeof(title)/sizeof(title[0]) );
\r
8073 strcat(title, ": ");
\r
8074 strcat(title, first.tidy);
\r
8076 SetWindowText(hwndMain, title);
\r
8081 DisplayMessage(char *str1, char *str2)
\r
8085 int remain = MESSAGE_TEXT_MAX - 1;
\r
8088 moveErrorMessageUp = FALSE; /* turned on later by caller if needed */
\r
8089 messageText[0] = NULLCHAR;
\r
8091 len = strlen(str1);
\r
8092 if (len > remain) len = remain;
\r
8093 strncpy(messageText, str1, len);
\r
8094 messageText[len] = NULLCHAR;
\r
8097 if (*str2 && remain >= 2) {
\r
8099 strcat(messageText, " ");
\r
8102 len = strlen(str2);
\r
8103 if (len > remain) len = remain;
\r
8104 strncat(messageText, str2, len);
\r
8106 messageText[MESSAGE_TEXT_MAX - 1] = NULLCHAR;
\r
8108 if (hwndMain == NULL || IsIconic(hwndMain)) return;
\r
8112 hdc = GetDC(hwndMain);
\r
8113 oldFont = SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);
\r
8114 ExtTextOut(hdc, messageRect.left, messageRect.top, ETO_CLIPPED|ETO_OPAQUE,
\r
8115 &messageRect, messageText, strlen(messageText), NULL);
\r
8116 (void) SelectObject(hdc, oldFont);
\r
8117 (void) ReleaseDC(hwndMain, hdc);
\r
8121 DisplayError(char *str, int error)
\r
8123 char buf[MSG_SIZ*2], buf2[MSG_SIZ];
\r
8127 safeStrCpy(buf, str, sizeof(buf)/sizeof(buf[0]) );
\r
8129 len = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,
\r
8130 NULL, error, LANG_NEUTRAL,
\r
8131 (LPSTR) buf2, MSG_SIZ, NULL);
\r
8133 snprintf(buf, 2*MSG_SIZ, "%s:\n%s", str, buf2);
\r
8135 ErrorMap *em = errmap;
\r
8136 while (em->err != 0 && em->err != error) em++;
\r
8137 if (em->err != 0) {
\r
8138 snprintf(buf, 2*MSG_SIZ, "%s:\n%s", str, em->msg);
\r
8140 snprintf(buf, 2*MSG_SIZ, "%s:\nError code %d", str, error);
\r
8145 ErrorPopUp(_("Error"), buf);
\r
8150 DisplayMoveError(char *str)
\r
8152 fromX = fromY = -1;
\r
8153 ClearHighlights();
\r
8154 DrawPosition(FALSE, NULL);
\r
8155 if (appData.popupMoveErrors) {
\r
8156 ErrorPopUp(_("Error"), str);
\r
8158 DisplayMessage(str, "");
\r
8159 moveErrorMessageUp = TRUE;
\r
8164 DisplayFatalError(char *str, int error, int exitStatus)
\r
8166 char buf[2*MSG_SIZ], buf2[MSG_SIZ];
\r
8168 char *label = exitStatus ? _("Fatal Error") : _("Exiting");
\r
8171 len = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,
\r
8172 NULL, error, LANG_NEUTRAL,
\r
8173 (LPSTR) buf2, MSG_SIZ, NULL);
\r
8175 snprintf(buf, 2*MSG_SIZ, "%s:\n%s", str, buf2);
\r
8177 ErrorMap *em = errmap;
\r
8178 while (em->err != 0 && em->err != error) em++;
\r
8179 if (em->err != 0) {
\r
8180 snprintf(buf, 2*MSG_SIZ, "%s:\n%s", str, em->msg);
\r
8182 snprintf(buf, 2*MSG_SIZ, "%s:\nError code %d", str, error);
\r
8187 if (appData.debugMode) {
\r
8188 fprintf(debugFP, "%s: %s\n", label, str);
\r
8190 if (appData.popupExitMessage) {
\r
8191 (void) MessageBox(hwndMain, str, label, MB_OK|
\r
8192 (exitStatus ? MB_ICONSTOP : MB_ICONINFORMATION));
\r
8194 ExitEvent(exitStatus);
\r
8199 DisplayInformation(char *str)
\r
8201 (void) MessageBox(hwndMain, str, _("Information"), MB_OK|MB_ICONINFORMATION);
\r
8206 DisplayNote(char *str)
\r
8208 ErrorPopUp(_("Note"), str);
\r
8213 char *title, *question, *replyPrefix;
\r
8218 QuestionDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
8220 static QuestionParams *qp;
\r
8221 char reply[MSG_SIZ];
\r
8224 switch (message) {
\r
8225 case WM_INITDIALOG:
\r
8226 qp = (QuestionParams *) lParam;
\r
8227 CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));
\r
8228 Translate(hDlg, DLG_Question);
\r
8229 SetWindowText(hDlg, qp->title);
\r
8230 SetDlgItemText(hDlg, OPT_QuestionText, qp->question);
\r
8231 SetFocus(GetDlgItem(hDlg, OPT_QuestionInput));
\r
8235 switch (LOWORD(wParam)) {
\r
8237 safeStrCpy(reply, qp->replyPrefix, sizeof(reply)/sizeof(reply[0]) );
\r
8238 if (*reply) strcat(reply, " ");
\r
8239 len = strlen(reply);
\r
8240 GetDlgItemText(hDlg, OPT_QuestionInput, reply + len, sizeof(reply) - len);
\r
8241 strcat(reply, "\n");
\r
8242 OutputToProcess(qp->pr, reply, strlen(reply), &err);
\r
8243 EndDialog(hDlg, TRUE);
\r
8244 if (err) DisplayFatalError(_("Error writing to chess program"), err, 1);
\r
8247 EndDialog(hDlg, FALSE);
\r
8258 AskQuestion(char* title, char *question, char *replyPrefix, ProcRef pr)
\r
8260 QuestionParams qp;
\r
8264 qp.question = question;
\r
8265 qp.replyPrefix = replyPrefix;
\r
8267 lpProc = MakeProcInstance((FARPROC)QuestionDialog, hInst);
\r
8268 DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_Question),
\r
8269 hwndMain, (DLGPROC)lpProc, (LPARAM)&qp);
\r
8270 FreeProcInstance(lpProc);
\r
8273 /* [AS] Pick FRC position */
\r
8274 LRESULT CALLBACK NewGameFRC_Proc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
8276 static int * lpIndexFRC;
\r
8282 case WM_INITDIALOG:
\r
8283 lpIndexFRC = (int *) lParam;
\r
8285 CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));
\r
8286 Translate(hDlg, DLG_NewGameFRC);
\r
8288 SendDlgItemMessage( hDlg, IDC_NFG_Edit, EM_SETLIMITTEXT, sizeof(buf)-1, 0 );
\r
8289 SetDlgItemInt( hDlg, IDC_NFG_Edit, *lpIndexFRC, TRUE );
\r
8290 SendDlgItemMessage( hDlg, IDC_NFG_Edit, EM_SETSEL, 0, -1 );
\r
8291 SetFocus(GetDlgItem(hDlg, IDC_NFG_Edit));
\r
8296 switch( LOWORD(wParam) ) {
\r
8298 *lpIndexFRC = GetDlgItemInt(hDlg, IDC_NFG_Edit, &index_is_ok, TRUE );
\r
8299 EndDialog( hDlg, 0 );
\r
8300 shuffleOpenings = TRUE; /* [HGM] shuffle: switch shuffling on for as long as we stay in current variant */
\r
8303 EndDialog( hDlg, 1 );
\r
8305 case IDC_NFG_Edit:
\r
8306 if( HIWORD(wParam) == EN_CHANGE ) {
\r
8307 GetDlgItemInt(hDlg, IDC_NFG_Edit, &index_is_ok, TRUE );
\r
8309 EnableWindow( GetDlgItem(hDlg, IDOK), index_is_ok );
\r
8312 case IDC_NFG_Random:
\r
8313 snprintf( buf, sizeof(buf)/sizeof(buf[0]), "%d", myrandom() ); /* [HGM] shuffle: no longer limit to 960 */
\r
8314 SetDlgItemText(hDlg, IDC_NFG_Edit, buf );
\r
8327 int index = appData.defaultFrcPosition;
\r
8328 FARPROC lpProc = MakeProcInstance( (FARPROC) NewGameFRC_Proc, hInst );
\r
8330 result = DialogBoxParam( hInst, MAKEINTRESOURCE(DLG_NewGameFRC), hwndMain, (DLGPROC)lpProc, (LPARAM)&index );
\r
8332 if( result == 0 ) {
\r
8333 appData.defaultFrcPosition = index;
\r
8339 /* [AS] Game list options. Refactored by HGM */
\r
8341 HWND gameListOptionsDialog;
\r
8343 // low-level front-end: clear text edit / list widget
\r
8347 SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_RESETCONTENT, 0, 0 );
\r
8350 // low-level front-end: clear text edit / list widget
\r
8352 GLT_DeSelectList()
\r
8354 SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_SETCURSEL, 0, 0 );
\r
8357 // low-level front-end: append line to text edit / list widget
\r
8359 GLT_AddToList( char *name )
\r
8362 SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_ADDSTRING, 0, (LPARAM) name );
\r
8366 // low-level front-end: get line from text edit / list widget
\r
8368 GLT_GetFromList( int index, char *name )
\r
8371 if( SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_GETTEXT, index, (LPARAM) name ) != LB_ERR )
\r
8377 void GLT_MoveSelection( HWND hDlg, int delta )
\r
8379 int idx1 = (int) SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETCURSEL, 0, 0 );
\r
8380 int idx2 = idx1 + delta;
\r
8381 int count = (int) SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETCOUNT, 0, 0 );
\r
8383 if( idx1 >=0 && idx1 < count && idx2 >= 0 && idx2 < count ) {
\r
8386 SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETTEXT, idx1, (LPARAM) buf );
\r
8387 SendDlgItemMessage( hDlg, IDC_GameListTags, LB_DELETESTRING, idx1, 0 );
\r
8388 SendDlgItemMessage( hDlg, IDC_GameListTags, LB_INSERTSTRING, idx2, (LPARAM) buf );
\r
8389 SendDlgItemMessage( hDlg, IDC_GameListTags, LB_SETCURSEL, idx2, 0 );
\r
8393 LRESULT CALLBACK GameListOptions_Proc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
8397 case WM_INITDIALOG:
\r
8398 gameListOptionsDialog = hDlg; // [HGM] pass through global to keep out off back-end
\r
8400 CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));
\r
8401 Translate(hDlg, DLG_GameListOptions);
\r
8403 /* Initialize list */
\r
8404 GLT_TagsToList( lpUserGLT );
\r
8406 SetFocus( GetDlgItem(hDlg, IDC_GameListTags) );
\r
8411 switch( LOWORD(wParam) ) {
\r
8414 EndDialog( hDlg, 0 );
\r
8417 EndDialog( hDlg, 1 );
\r
8420 case IDC_GLT_Default:
\r
8421 GLT_TagsToList( GLT_DEFAULT_TAGS );
\r
8424 case IDC_GLT_Restore:
\r
8425 GLT_TagsToList( appData.gameListTags );
\r
8429 GLT_MoveSelection( hDlg, -1 );
\r
8432 case IDC_GLT_Down:
\r
8433 GLT_MoveSelection( hDlg, +1 );
\r
8443 int GameListOptions()
\r
8446 FARPROC lpProc = MakeProcInstance( (FARPROC) GameListOptions_Proc, hInst );
\r
8448 safeStrCpy( lpUserGLT, appData.gameListTags ,LPUSERGLT_SIZE );
\r
8450 result = DialogBoxParam( hInst, MAKEINTRESOURCE(DLG_GameListOptions), hwndMain, (DLGPROC)lpProc, (LPARAM)lpUserGLT );
\r
8452 if( result == 0 ) {
\r
8453 /* [AS] Memory leak here! */
\r
8454 appData.gameListTags = strdup( lpUserGLT );
\r
8461 DisplayIcsInteractionTitle(char *str)
\r
8463 char consoleTitle[MSG_SIZ];
\r
8465 snprintf(consoleTitle, MSG_SIZ, "%s: %s", szConsoleTitle, str);
\r
8466 SetWindowText(hwndConsole, consoleTitle);
\r
8470 DrawPosition(int fullRedraw, Board board)
\r
8472 HDCDrawPosition(NULL, (BOOLEAN) fullRedraw, board);
\r
8475 void NotifyFrontendLogin()
\r
8478 UpdateICSWidth(GetDlgItem(hwndConsole, OPT_ConsoleText));
\r
8484 fromX = fromY = -1;
\r
8485 if (dragInfo.pos.x != -1 || dragInfo.pos.y != -1) {
\r
8486 dragInfo.pos.x = dragInfo.pos.y = -1;
\r
8487 dragInfo.pos.x = dragInfo.pos.y = -1;
\r
8488 dragInfo.lastpos = dragInfo.pos;
\r
8489 dragInfo.start.x = dragInfo.start.y = -1;
\r
8490 dragInfo.from = dragInfo.start;
\r
8492 DrawPosition(TRUE, NULL);
\r
8499 CommentPopUp(char *title, char *str)
\r
8501 HWND hwnd = GetActiveWindow();
\r
8502 EitherCommentPopUp(currentMove, title, str, FALSE); // [HGM] vari: fake move index, rather than 0
\r
8504 SetActiveWindow(hwnd);
\r
8508 CommentPopDown(void)
\r
8510 CheckMenuItem(GetMenu(hwndMain), IDM_EditComment, MF_UNCHECKED);
\r
8511 if (commentDialog) {
\r
8512 ShowWindow(commentDialog, SW_HIDE);
\r
8514 commentUp = FALSE;
\r
8518 EditCommentPopUp(int index, char *title, char *str)
\r
8520 EitherCommentPopUp(index, title, str, TRUE);
\r
8527 MyPlaySound(&sounds[(int)SoundMove]);
\r
8530 VOID PlayIcsWinSound()
\r
8532 MyPlaySound(&sounds[(int)SoundIcsWin]);
\r
8535 VOID PlayIcsLossSound()
\r
8537 MyPlaySound(&sounds[(int)SoundIcsLoss]);
\r
8540 VOID PlayIcsDrawSound()
\r
8542 MyPlaySound(&sounds[(int)SoundIcsDraw]);
\r
8545 VOID PlayIcsUnfinishedSound()
\r
8547 MyPlaySound(&sounds[(int)SoundIcsUnfinished]);
\r
8553 MyPlaySound(&sounds[(int)SoundAlarm]);
\r
8561 consoleEcho = TRUE;
\r
8562 hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
8563 SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&consoleCF);
\r
8564 SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);
\r
8573 consoleEcho = FALSE;
\r
8574 hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
8575 /* This works OK: set text and background both to the same color */
\r
8577 cf.crTextColor = COLOR_ECHOOFF;
\r
8578 SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf);
\r
8579 SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, cf.crTextColor);
\r
8582 /* No Raw()...? */
\r
8584 void Colorize(ColorClass cc, int continuation)
\r
8586 currentColorClass = cc;
\r
8587 consoleCF.dwMask = CFM_COLOR|CFM_BOLD|CFM_ITALIC|CFM_UNDERLINE|CFM_STRIKEOUT;
\r
8588 consoleCF.crTextColor = textAttribs[cc].color;
\r
8589 consoleCF.dwEffects = textAttribs[cc].effects;
\r
8590 if (!continuation) MyPlaySound(&textAttribs[cc].sound);
\r
8596 static char buf[MSG_SIZ];
\r
8597 DWORD bufsiz = MSG_SIZ;
\r
8599 if(appData.userName != NULL && appData.userName[0] != 0) {
\r
8600 return appData.userName; /* [HGM] username: prefer name selected by user over his system login */
\r
8602 if (!GetUserName(buf, &bufsiz)) {
\r
8603 /*DisplayError("Error getting user name", GetLastError());*/
\r
8604 safeStrCpy(buf, _("User"), sizeof(buf)/sizeof(buf[0]) );
\r
8612 static char buf[MSG_SIZ];
\r
8613 DWORD bufsiz = MSG_SIZ;
\r
8615 if (!GetComputerName(buf, &bufsiz)) {
\r
8616 /*DisplayError("Error getting host name", GetLastError());*/
\r
8617 safeStrCpy(buf, _("Unknown"), sizeof(buf)/sizeof(buf[0]) );
\r
8624 ClockTimerRunning()
\r
8626 return clockTimerEvent != 0;
\r
8632 if (clockTimerEvent == 0) return FALSE;
\r
8633 KillTimer(hwndMain, clockTimerEvent);
\r
8634 clockTimerEvent = 0;
\r
8639 StartClockTimer(long millisec)
\r
8641 clockTimerEvent = SetTimer(hwndMain, (UINT) CLOCK_TIMER_ID,
\r
8642 (UINT) millisec, NULL);
\r
8646 DisplayWhiteClock(long timeRemaining, int highlight)
\r
8649 char *flag = whiteFlag && gameMode == TwoMachinesPlay ? "(!)" : "";
\r
8651 if(appData.noGUI) return;
\r
8652 hdc = GetDC(hwndMain);
\r
8653 if (!IsIconic(hwndMain)) {
\r
8654 DisplayAClock(hdc, timeRemaining, highlight,
\r
8655 flipClock ? &blackRect : &whiteRect, _("White"), flag);
\r
8657 if (highlight && iconCurrent == iconBlack) {
\r
8658 iconCurrent = iconWhite;
\r
8659 PostMessage(hwndMain, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);
\r
8660 if (IsIconic(hwndMain)) {
\r
8661 DrawIcon(hdc, 2, 2, iconCurrent);
\r
8664 (void) ReleaseDC(hwndMain, hdc);
\r
8666 PostMessage(hwndConsole, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);
\r
8670 DisplayBlackClock(long timeRemaining, int highlight)
\r
8673 char *flag = blackFlag && gameMode == TwoMachinesPlay ? "(!)" : "";
\r
8675 if(appData.noGUI) return;
\r
8676 hdc = GetDC(hwndMain);
\r
8677 if (!IsIconic(hwndMain)) {
\r
8678 DisplayAClock(hdc, timeRemaining, highlight,
\r
8679 flipClock ? &whiteRect : &blackRect, _("Black"), flag);
\r
8681 if (highlight && iconCurrent == iconWhite) {
\r
8682 iconCurrent = iconBlack;
\r
8683 PostMessage(hwndMain, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);
\r
8684 if (IsIconic(hwndMain)) {
\r
8685 DrawIcon(hdc, 2, 2, iconCurrent);
\r
8688 (void) ReleaseDC(hwndMain, hdc);
\r
8690 PostMessage(hwndConsole, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);
\r
8695 LoadGameTimerRunning()
\r
8697 return loadGameTimerEvent != 0;
\r
8701 StopLoadGameTimer()
\r
8703 if (loadGameTimerEvent == 0) return FALSE;
\r
8704 KillTimer(hwndMain, loadGameTimerEvent);
\r
8705 loadGameTimerEvent = 0;
\r
8710 StartLoadGameTimer(long millisec)
\r
8712 loadGameTimerEvent = SetTimer(hwndMain, (UINT) LOAD_GAME_TIMER_ID,
\r
8713 (UINT) millisec, NULL);
\r
8721 char fileTitle[MSG_SIZ];
\r
8723 defName = DefaultFileName(appData.oldSaveStyle ? "gam" : "pgn");
\r
8724 f = OpenFileDialog(hwndMain, "a", defName,
\r
8725 appData.oldSaveStyle ? "gam" : "pgn",
\r
8727 _("Save Game to File"), NULL, fileTitle, NULL);
\r
8729 SaveGame(f, 0, "");
\r
8736 ScheduleDelayedEvent(DelayedEventCallback cb, long millisec)
\r
8738 if (delayedTimerEvent != 0) {
\r
8739 if (appData.debugMode && cb != delayedTimerCallback) { // [HGM] alive: not too much debug
\r
8740 fprintf(debugFP, "ScheduleDelayedEvent: event already scheduled\n");
\r
8742 KillTimer(hwndMain, delayedTimerEvent);
\r
8743 delayedTimerEvent = 0;
\r
8744 if(delayedTimerCallback != cb) // [HGM] alive: do not "flush" same event, just postpone it
\r
8745 delayedTimerCallback();
\r
8747 delayedTimerCallback = cb;
\r
8748 delayedTimerEvent = SetTimer(hwndMain, (UINT) DELAYED_TIMER_ID,
\r
8749 (UINT) millisec, NULL);
\r
8752 DelayedEventCallback
\r
8755 if (delayedTimerEvent) {
\r
8756 return delayedTimerCallback;
\r
8763 CancelDelayedEvent()
\r
8765 if (delayedTimerEvent) {
\r
8766 KillTimer(hwndMain, delayedTimerEvent);
\r
8767 delayedTimerEvent = 0;
\r
8771 DWORD GetWin32Priority(int nice)
\r
8772 { // [HGM] nice: translate Unix nice() value to indows priority class. (Code stolen from Polyglot 1.4w11)
\r
8774 REALTIME_PRIORITY_CLASS 0x00000100
\r
8775 HIGH_PRIORITY_CLASS 0x00000080
\r
8776 ABOVE_NORMAL_PRIORITY_CLASS 0x00008000
\r
8777 NORMAL_PRIORITY_CLASS 0x00000020
\r
8778 BELOW_NORMAL_PRIORITY_CLASS 0x00004000
\r
8779 IDLE_PRIORITY_CLASS 0x00000040
\r
8781 if (nice < -15) return 0x00000080;
\r
8782 if (nice < 0) return 0x00008000;
\r
8783 if (nice == 0) return 0x00000020;
\r
8784 if (nice < 15) return 0x00004000;
\r
8785 return 0x00000040;
\r
8788 /* Start a child process running the given program.
\r
8789 The process's standard output can be read from "from", and its
\r
8790 standard input can be written to "to".
\r
8791 Exit with fatal error if anything goes wrong.
\r
8792 Returns an opaque pointer that can be used to destroy the process
\r
8796 StartChildProcess(char *cmdLine, char *dir, ProcRef *pr)
\r
8798 #define BUFSIZE 4096
\r
8800 HANDLE hChildStdinRd, hChildStdinWr,
\r
8801 hChildStdoutRd, hChildStdoutWr;
\r
8802 HANDLE hChildStdinWrDup, hChildStdoutRdDup;
\r
8803 SECURITY_ATTRIBUTES saAttr;
\r
8805 PROCESS_INFORMATION piProcInfo;
\r
8806 STARTUPINFO siStartInfo;
\r
8808 char buf[MSG_SIZ];
\r
8811 if (appData.debugMode) {
\r
8812 fprintf(debugFP, "StartChildProcess (dir=\"%s\") %s\n", dir, cmdLine);
\r
8817 /* Set the bInheritHandle flag so pipe handles are inherited. */
\r
8818 saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);
\r
8819 saAttr.bInheritHandle = TRUE;
\r
8820 saAttr.lpSecurityDescriptor = NULL;
\r
8823 * The steps for redirecting child's STDOUT:
\r
8824 * 1. Create anonymous pipe to be STDOUT for child.
\r
8825 * 2. Create a noninheritable duplicate of read handle,
\r
8826 * and close the inheritable read handle.
\r
8829 /* Create a pipe for the child's STDOUT. */
\r
8830 if (! CreatePipe(&hChildStdoutRd, &hChildStdoutWr, &saAttr, 0)) {
\r
8831 return GetLastError();
\r
8834 /* Duplicate the read handle to the pipe, so it is not inherited. */
\r
8835 fSuccess = DuplicateHandle(GetCurrentProcess(), hChildStdoutRd,
\r
8836 GetCurrentProcess(), &hChildStdoutRdDup, 0,
\r
8837 FALSE, /* not inherited */
\r
8838 DUPLICATE_SAME_ACCESS);
\r
8840 return GetLastError();
\r
8842 CloseHandle(hChildStdoutRd);
\r
8845 * The steps for redirecting child's STDIN:
\r
8846 * 1. Create anonymous pipe to be STDIN for child.
\r
8847 * 2. Create a noninheritable duplicate of write handle,
\r
8848 * and close the inheritable write handle.
\r
8851 /* Create a pipe for the child's STDIN. */
\r
8852 if (! CreatePipe(&hChildStdinRd, &hChildStdinWr, &saAttr, 0)) {
\r
8853 return GetLastError();
\r
8856 /* Duplicate the write handle to the pipe, so it is not inherited. */
\r
8857 fSuccess = DuplicateHandle(GetCurrentProcess(), hChildStdinWr,
\r
8858 GetCurrentProcess(), &hChildStdinWrDup, 0,
\r
8859 FALSE, /* not inherited */
\r
8860 DUPLICATE_SAME_ACCESS);
\r
8862 return GetLastError();
\r
8864 CloseHandle(hChildStdinWr);
\r
8866 /* Arrange to (1) look in dir for the child .exe file, and
\r
8867 * (2) have dir be the child's working directory. Interpret
\r
8868 * dir relative to the directory WinBoard loaded from. */
\r
8869 GetCurrentDirectory(MSG_SIZ, buf);
\r
8870 SetCurrentDirectory(installDir);
\r
8871 SetCurrentDirectory(dir);
\r
8873 /* Now create the child process. */
\r
8875 siStartInfo.cb = sizeof(STARTUPINFO);
\r
8876 siStartInfo.lpReserved = NULL;
\r
8877 siStartInfo.lpDesktop = NULL;
\r
8878 siStartInfo.lpTitle = NULL;
\r
8879 siStartInfo.dwFlags = STARTF_USESTDHANDLES;
\r
8880 siStartInfo.cbReserved2 = 0;
\r
8881 siStartInfo.lpReserved2 = NULL;
\r
8882 siStartInfo.hStdInput = hChildStdinRd;
\r
8883 siStartInfo.hStdOutput = hChildStdoutWr;
\r
8884 siStartInfo.hStdError = hChildStdoutWr;
\r
8886 fSuccess = CreateProcess(NULL,
\r
8887 cmdLine, /* command line */
\r
8888 NULL, /* process security attributes */
\r
8889 NULL, /* primary thread security attrs */
\r
8890 TRUE, /* handles are inherited */
\r
8891 DETACHED_PROCESS|CREATE_NEW_PROCESS_GROUP,
\r
8892 NULL, /* use parent's environment */
\r
8894 &siStartInfo, /* STARTUPINFO pointer */
\r
8895 &piProcInfo); /* receives PROCESS_INFORMATION */
\r
8897 err = GetLastError();
\r
8898 SetCurrentDirectory(buf); /* return to prev directory */
\r
8903 if (appData.niceEngines){ // [HGM] nice: adjust engine proc priority
\r
8904 if(appData.debugMode) fprintf(debugFP, "nice engine proc to %d\n", appData.niceEngines);
\r
8905 SetPriorityClass(piProcInfo.hProcess, GetWin32Priority(appData.niceEngines));
\r
8908 /* Close the handles we don't need in the parent */
\r
8909 CloseHandle(piProcInfo.hThread);
\r
8910 CloseHandle(hChildStdinRd);
\r
8911 CloseHandle(hChildStdoutWr);
\r
8913 /* Prepare return value */
\r
8914 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
\r
8915 cp->kind = CPReal;
\r
8916 cp->hProcess = piProcInfo.hProcess;
\r
8917 cp->pid = piProcInfo.dwProcessId;
\r
8918 cp->hFrom = hChildStdoutRdDup;
\r
8919 cp->hTo = hChildStdinWrDup;
\r
8921 *pr = (void *) cp;
\r
8923 /* Klaus Friedel says that this Sleep solves a problem under Windows
\r
8924 2000 where engines sometimes don't see the initial command(s)
\r
8925 from WinBoard and hang. I don't understand how that can happen,
\r
8926 but the Sleep is harmless, so I've put it in. Others have also
\r
8927 reported what may be the same problem, so hopefully this will fix
\r
8928 it for them too. */
\r
8936 DestroyChildProcess(ProcRef pr, int/*boolean*/ signal)
\r
8938 ChildProc *cp; int result;
\r
8940 cp = (ChildProc *) pr;
\r
8941 if (cp == NULL) return;
\r
8943 switch (cp->kind) {
\r
8945 /* TerminateProcess is considered harmful, so... */
\r
8946 CloseHandle(cp->hTo); /* Closing this will give the child an EOF and hopefully kill it */
\r
8947 if (cp->hFrom) CloseHandle(cp->hFrom); /* if NULL, InputThread will close it */
\r
8948 /* The following doesn't work because the chess program
\r
8949 doesn't "have the same console" as WinBoard. Maybe
\r
8950 we could arrange for this even though neither WinBoard
\r
8951 nor the chess program uses a console for stdio? */
\r
8952 /*!!if (signal) GenerateConsoleCtrlEvent(CTRL_BREAK_EVENT, cp->pid);*/
\r
8954 /* [AS] Special termination modes for misbehaving programs... */
\r
8955 if( signal == 9 ) {
\r
8956 result = TerminateProcess( cp->hProcess, 0 );
\r
8958 if ( appData.debugMode) {
\r
8959 fprintf( debugFP, "Terminating process %lu, result=%d\n", cp->pid, result );
\r
8962 else if( signal == 10 ) {
\r
8963 DWORD dw = WaitForSingleObject( cp->hProcess, 3*1000 ); // Wait 3 seconds at most
\r
8965 if( dw != WAIT_OBJECT_0 ) {
\r
8966 result = TerminateProcess( cp->hProcess, 0 );
\r
8968 if ( appData.debugMode) {
\r
8969 fprintf( debugFP, "Process %lu still alive after timeout, killing... result=%d\n", cp->pid, result );
\r
8975 CloseHandle(cp->hProcess);
\r
8979 if (cp->hFrom) CloseHandle(cp->hFrom);
\r
8983 closesocket(cp->sock);
\r
8988 if (signal) send(cp->sock2, "\017", 1, 0); /* 017 = 15 = SIGTERM */
\r
8989 closesocket(cp->sock);
\r
8990 closesocket(cp->sock2);
\r
8998 InterruptChildProcess(ProcRef pr)
\r
9002 cp = (ChildProc *) pr;
\r
9003 if (cp == NULL) return;
\r
9004 switch (cp->kind) {
\r
9006 /* The following doesn't work because the chess program
\r
9007 doesn't "have the same console" as WinBoard. Maybe
\r
9008 we could arrange for this even though neither WinBoard
\r
9009 nor the chess program uses a console for stdio */
\r
9010 /*!!GenerateConsoleCtrlEvent(CTRL_C_EVENT, cp->pid);*/
\r
9015 /* Can't interrupt */
\r
9019 send(cp->sock2, "\002", 1, 0); /* 2 = SIGINT */
\r
9026 OpenTelnet(char *host, char *port, ProcRef *pr)
\r
9028 char cmdLine[MSG_SIZ];
\r
9030 if (port[0] == NULLCHAR) {
\r
9031 snprintf(cmdLine, MSG_SIZ, "%s %s", appData.telnetProgram, host);
\r
9033 snprintf(cmdLine, MSG_SIZ, "%s %s %s", appData.telnetProgram, host, port);
\r
9035 return StartChildProcess(cmdLine, "", pr);
\r
9039 /* Code to open TCP sockets */
\r
9042 OpenTCP(char *host, char *port, ProcRef *pr)
\r
9047 struct sockaddr_in sa, mysa;
\r
9048 struct hostent FAR *hp;
\r
9049 unsigned short uport;
\r
9050 WORD wVersionRequested;
\r
9053 /* Initialize socket DLL */
\r
9054 wVersionRequested = MAKEWORD(1, 1);
\r
9055 err = WSAStartup(wVersionRequested, &wsaData);
\r
9056 if (err != 0) return err;
\r
9059 if ((s = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {
\r
9060 err = WSAGetLastError();
\r
9065 /* Bind local address using (mostly) don't-care values.
\r
9067 memset((char *) &mysa, 0, sizeof(struct sockaddr_in));
\r
9068 mysa.sin_family = AF_INET;
\r
9069 mysa.sin_addr.s_addr = INADDR_ANY;
\r
9070 uport = (unsigned short) 0;
\r
9071 mysa.sin_port = htons(uport);
\r
9072 if (bind(s, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))
\r
9073 == SOCKET_ERROR) {
\r
9074 err = WSAGetLastError();
\r
9079 /* Resolve remote host name */
\r
9080 memset((char *) &sa, 0, sizeof(struct sockaddr_in));
\r
9081 if (!(hp = gethostbyname(host))) {
\r
9082 unsigned int b0, b1, b2, b3;
\r
9084 err = WSAGetLastError();
\r
9086 if (sscanf(host, "%u.%u.%u.%u", &b0, &b1, &b2, &b3) == 4) {
\r
9087 hp = (struct hostent *) calloc(1, sizeof(struct hostent));
\r
9088 hp->h_addrtype = AF_INET;
\r
9090 hp->h_addr_list = (char **) calloc(2, sizeof(char *));
\r
9091 hp->h_addr_list[0] = (char *) malloc(4);
\r
9092 hp->h_addr_list[0][0] = (char) b0;
\r
9093 hp->h_addr_list[0][1] = (char) b1;
\r
9094 hp->h_addr_list[0][2] = (char) b2;
\r
9095 hp->h_addr_list[0][3] = (char) b3;
\r
9101 sa.sin_family = hp->h_addrtype;
\r
9102 uport = (unsigned short) atoi(port);
\r
9103 sa.sin_port = htons(uport);
\r
9104 memcpy((char *) &sa.sin_addr, hp->h_addr, hp->h_length);
\r
9106 /* Make connection */
\r
9107 if (connect(s, (struct sockaddr *) &sa,
\r
9108 sizeof(struct sockaddr_in)) == SOCKET_ERROR) {
\r
9109 err = WSAGetLastError();
\r
9114 /* Prepare return value */
\r
9115 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
\r
9116 cp->kind = CPSock;
\r
9118 *pr = (ProcRef *) cp;
\r
9124 OpenCommPort(char *name, ProcRef *pr)
\r
9129 char fullname[MSG_SIZ];
\r
9131 if (*name != '\\')
\r
9132 snprintf(fullname, MSG_SIZ, "\\\\.\\%s", name);
\r
9134 safeStrCpy(fullname, name, sizeof(fullname)/sizeof(fullname[0]) );
\r
9136 h = CreateFile(name, GENERIC_READ | GENERIC_WRITE,
\r
9137 0, NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL);
\r
9138 if (h == (HANDLE) -1) {
\r
9139 return GetLastError();
\r
9143 if (!SetCommState(h, (LPDCB) &dcb)) return GetLastError();
\r
9145 /* Accumulate characters until a 100ms pause, then parse */
\r
9146 ct.ReadIntervalTimeout = 100;
\r
9147 ct.ReadTotalTimeoutMultiplier = 0;
\r
9148 ct.ReadTotalTimeoutConstant = 0;
\r
9149 ct.WriteTotalTimeoutMultiplier = 0;
\r
9150 ct.WriteTotalTimeoutConstant = 0;
\r
9151 if (!SetCommTimeouts(h, (LPCOMMTIMEOUTS) &ct)) return GetLastError();
\r
9153 /* Prepare return value */
\r
9154 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
\r
9155 cp->kind = CPComm;
\r
9158 *pr = (ProcRef *) cp;
\r
9164 OpenLoopback(ProcRef *pr)
\r
9166 DisplayFatalError(_("Not implemented"), 0, 1);
\r
9172 OpenRcmd(char* host, char* user, char* cmd, ProcRef* pr)
\r
9177 struct sockaddr_in sa, mysa;
\r
9178 struct hostent FAR *hp;
\r
9179 unsigned short uport;
\r
9180 WORD wVersionRequested;
\r
9183 char stderrPortStr[MSG_SIZ];
\r
9185 /* Initialize socket DLL */
\r
9186 wVersionRequested = MAKEWORD(1, 1);
\r
9187 err = WSAStartup(wVersionRequested, &wsaData);
\r
9188 if (err != 0) return err;
\r
9190 /* Resolve remote host name */
\r
9191 memset((char *) &sa, 0, sizeof(struct sockaddr_in));
\r
9192 if (!(hp = gethostbyname(host))) {
\r
9193 unsigned int b0, b1, b2, b3;
\r
9195 err = WSAGetLastError();
\r
9197 if (sscanf(host, "%u.%u.%u.%u", &b0, &b1, &b2, &b3) == 4) {
\r
9198 hp = (struct hostent *) calloc(1, sizeof(struct hostent));
\r
9199 hp->h_addrtype = AF_INET;
\r
9201 hp->h_addr_list = (char **) calloc(2, sizeof(char *));
\r
9202 hp->h_addr_list[0] = (char *) malloc(4);
\r
9203 hp->h_addr_list[0][0] = (char) b0;
\r
9204 hp->h_addr_list[0][1] = (char) b1;
\r
9205 hp->h_addr_list[0][2] = (char) b2;
\r
9206 hp->h_addr_list[0][3] = (char) b3;
\r
9212 sa.sin_family = hp->h_addrtype;
\r
9213 uport = (unsigned short) 514;
\r
9214 sa.sin_port = htons(uport);
\r
9215 memcpy((char *) &sa.sin_addr, hp->h_addr, hp->h_length);
\r
9217 /* Bind local socket to unused "privileged" port address
\r
9219 s = INVALID_SOCKET;
\r
9220 memset((char *) &mysa, 0, sizeof(struct sockaddr_in));
\r
9221 mysa.sin_family = AF_INET;
\r
9222 mysa.sin_addr.s_addr = INADDR_ANY;
\r
9223 for (fromPort = 1023;; fromPort--) {
\r
9224 if (fromPort < 0) {
\r
9226 return WSAEADDRINUSE;
\r
9228 if (s == INVALID_SOCKET) {
\r
9229 if ((s = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {
\r
9230 err = WSAGetLastError();
\r
9235 uport = (unsigned short) fromPort;
\r
9236 mysa.sin_port = htons(uport);
\r
9237 if (bind(s, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))
\r
9238 == SOCKET_ERROR) {
\r
9239 err = WSAGetLastError();
\r
9240 if (err == WSAEADDRINUSE) continue;
\r
9244 if (connect(s, (struct sockaddr *) &sa,
\r
9245 sizeof(struct sockaddr_in)) == SOCKET_ERROR) {
\r
9246 err = WSAGetLastError();
\r
9247 if (err == WSAEADDRINUSE) {
\r
9258 /* Bind stderr local socket to unused "privileged" port address
\r
9260 s2 = INVALID_SOCKET;
\r
9261 memset((char *) &mysa, 0, sizeof(struct sockaddr_in));
\r
9262 mysa.sin_family = AF_INET;
\r
9263 mysa.sin_addr.s_addr = INADDR_ANY;
\r
9264 for (fromPort = 1023;; fromPort--) {
\r
9265 if (fromPort == prevStderrPort) continue; // don't reuse port
\r
9266 if (fromPort < 0) {
\r
9267 (void) closesocket(s);
\r
9269 return WSAEADDRINUSE;
\r
9271 if (s2 == INVALID_SOCKET) {
\r
9272 if ((s2 = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {
\r
9273 err = WSAGetLastError();
\r
9279 uport = (unsigned short) fromPort;
\r
9280 mysa.sin_port = htons(uport);
\r
9281 if (bind(s2, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))
\r
9282 == SOCKET_ERROR) {
\r
9283 err = WSAGetLastError();
\r
9284 if (err == WSAEADDRINUSE) continue;
\r
9285 (void) closesocket(s);
\r
9289 if (listen(s2, 1) == SOCKET_ERROR) {
\r
9290 err = WSAGetLastError();
\r
9291 if (err == WSAEADDRINUSE) {
\r
9293 s2 = INVALID_SOCKET;
\r
9296 (void) closesocket(s);
\r
9297 (void) closesocket(s2);
\r
9303 prevStderrPort = fromPort; // remember port used
\r
9304 snprintf(stderrPortStr, MSG_SIZ, "%d", fromPort);
\r
9306 if (send(s, stderrPortStr, strlen(stderrPortStr) + 1, 0) == SOCKET_ERROR) {
\r
9307 err = WSAGetLastError();
\r
9308 (void) closesocket(s);
\r
9309 (void) closesocket(s2);
\r
9314 if (send(s, UserName(), strlen(UserName()) + 1, 0) == SOCKET_ERROR) {
\r
9315 err = WSAGetLastError();
\r
9316 (void) closesocket(s);
\r
9317 (void) closesocket(s2);
\r
9321 if (*user == NULLCHAR) user = UserName();
\r
9322 if (send(s, user, strlen(user) + 1, 0) == SOCKET_ERROR) {
\r
9323 err = WSAGetLastError();
\r
9324 (void) closesocket(s);
\r
9325 (void) closesocket(s2);
\r
9329 if (send(s, cmd, strlen(cmd) + 1, 0) == SOCKET_ERROR) {
\r
9330 err = WSAGetLastError();
\r
9331 (void) closesocket(s);
\r
9332 (void) closesocket(s2);
\r
9337 if ((s3 = accept(s2, NULL, NULL)) == INVALID_SOCKET) {
\r
9338 err = WSAGetLastError();
\r
9339 (void) closesocket(s);
\r
9340 (void) closesocket(s2);
\r
9344 (void) closesocket(s2); /* Stop listening */
\r
9346 /* Prepare return value */
\r
9347 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
\r
9348 cp->kind = CPRcmd;
\r
9351 *pr = (ProcRef *) cp;
\r
9358 AddInputSource(ProcRef pr, int lineByLine,
\r
9359 InputCallback func, VOIDSTAR closure)
\r
9361 InputSource *is, *is2 = NULL;
\r
9362 ChildProc *cp = (ChildProc *) pr;
\r
9364 is = (InputSource *) calloc(1, sizeof(InputSource));
\r
9365 is->lineByLine = lineByLine;
\r
9367 is->closure = closure;
\r
9368 is->second = NULL;
\r
9369 is->next = is->buf;
\r
9370 if (pr == NoProc) {
\r
9371 is->kind = CPReal;
\r
9372 consoleInputSource = is;
\r
9374 is->kind = cp->kind;
\r
9376 [AS] Try to avoid a race condition if the thread is given control too early:
\r
9377 we create all threads suspended so that the is->hThread variable can be
\r
9378 safely assigned, then let the threads start with ResumeThread.
\r
9380 switch (cp->kind) {
\r
9382 is->hFile = cp->hFrom;
\r
9383 cp->hFrom = NULL; /* now owned by InputThread */
\r
9385 CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) NonOvlInputThread,
\r
9386 (LPVOID) is, CREATE_SUSPENDED, &is->id);
\r
9390 is->hFile = cp->hFrom;
\r
9391 cp->hFrom = NULL; /* now owned by InputThread */
\r
9393 CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) InputThread,
\r
9394 (LPVOID) is, CREATE_SUSPENDED, &is->id);
\r
9398 is->sock = cp->sock;
\r
9400 CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,
\r
9401 (LPVOID) is, CREATE_SUSPENDED, &is->id);
\r
9405 is2 = (InputSource *) calloc(1, sizeof(InputSource));
\r
9407 is->sock = cp->sock;
\r
9409 is2->sock = cp->sock2;
\r
9410 is2->second = is2;
\r
9412 CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,
\r
9413 (LPVOID) is, CREATE_SUSPENDED, &is->id);
\r
9415 CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,
\r
9416 (LPVOID) is2, CREATE_SUSPENDED, &is2->id);
\r
9420 if( is->hThread != NULL ) {
\r
9421 ResumeThread( is->hThread );
\r
9424 if( is2 != NULL && is2->hThread != NULL ) {
\r
9425 ResumeThread( is2->hThread );
\r
9429 return (InputSourceRef) is;
\r
9433 RemoveInputSource(InputSourceRef isr)
\r
9437 is = (InputSource *) isr;
\r
9438 is->hThread = NULL; /* tell thread to stop */
\r
9439 CloseHandle(is->hThread);
\r
9440 if (is->second != NULL) {
\r
9441 is->second->hThread = NULL;
\r
9442 CloseHandle(is->second->hThread);
\r
9446 int no_wrap(char *message, int count)
\r
9448 ConsoleOutput(message, count, FALSE);
\r
9453 OutputToProcess(ProcRef pr, char *message, int count, int *outError)
\r
9456 int outCount = SOCKET_ERROR;
\r
9457 ChildProc *cp = (ChildProc *) pr;
\r
9458 static OVERLAPPED ovl;
\r
9459 static int line = 0;
\r
9463 if (appData.noJoin || !appData.useInternalWrap)
\r
9464 return no_wrap(message, count);
\r
9467 int width = get_term_width();
\r
9468 int len = wrap(NULL, message, count, width, &line);
\r
9469 char *msg = malloc(len);
\r
9473 return no_wrap(message, count);
\r
9476 dbgchk = wrap(msg, message, count, width, &line);
\r
9477 if (dbgchk != len && appData.debugMode)
\r
9478 fprintf(debugFP, "wrap(): dbgchk(%d) != len(%d)\n", dbgchk, len);
\r
9479 ConsoleOutput(msg, len, FALSE);
\r
9486 if (ovl.hEvent == NULL) {
\r
9487 ovl.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
\r
9489 ovl.Internal = ovl.InternalHigh = ovl.Offset = ovl.OffsetHigh = 0;
\r
9491 switch (cp->kind) {
\r
9494 outCount = send(cp->sock, message, count, 0);
\r
9495 if (outCount == SOCKET_ERROR) {
\r
9496 *outError = WSAGetLastError();
\r
9498 *outError = NO_ERROR;
\r
9503 if (WriteFile(((ChildProc *)pr)->hTo, message, count,
\r
9504 &dOutCount, NULL)) {
\r
9505 *outError = NO_ERROR;
\r
9506 outCount = (int) dOutCount;
\r
9508 *outError = GetLastError();
\r
9513 *outError = DoWriteFile(((ChildProc *)pr)->hTo, message, count,
\r
9514 &dOutCount, &ovl);
\r
9515 if (*outError == NO_ERROR) {
\r
9516 outCount = (int) dOutCount;
\r
9524 OutputToProcessDelayed(ProcRef pr, char *message, int count, int *outError,
\r
9527 /* Ignore delay, not implemented for WinBoard */
\r
9528 return OutputToProcess(pr, message, count, outError);
\r
9533 CmailSigHandlerCallBack(InputSourceRef isr, VOIDSTAR closure,
\r
9534 char *buf, int count, int error)
\r
9536 DisplayFatalError(_("Not implemented"), 0, 1);
\r
9539 /* see wgamelist.c for Game List functions */
\r
9540 /* see wedittags.c for Edit Tags functions */
\r
9547 char buf[MSG_SIZ];
\r
9550 if (SearchPath(installDir, appData.icsLogon, NULL, MSG_SIZ, buf, &dummy)) {
\r
9551 f = fopen(buf, "r");
\r
9553 ProcessICSInitScript(f);
\r
9561 StartAnalysisClock()
\r
9563 if (analysisTimerEvent) return;
\r
9564 analysisTimerEvent = SetTimer(hwndMain, (UINT) ANALYSIS_TIMER_ID,
\r
9565 (UINT) 2000, NULL);
\r
9569 SetHighlights(int fromX, int fromY, int toX, int toY)
\r
9571 highlightInfo.sq[0].x = fromX;
\r
9572 highlightInfo.sq[0].y = fromY;
\r
9573 highlightInfo.sq[1].x = toX;
\r
9574 highlightInfo.sq[1].y = toY;
\r
9580 highlightInfo.sq[0].x = highlightInfo.sq[0].y =
\r
9581 highlightInfo.sq[1].x = highlightInfo.sq[1].y = -1;
\r
9585 SetPremoveHighlights(int fromX, int fromY, int toX, int toY)
\r
9587 premoveHighlightInfo.sq[0].x = fromX;
\r
9588 premoveHighlightInfo.sq[0].y = fromY;
\r
9589 premoveHighlightInfo.sq[1].x = toX;
\r
9590 premoveHighlightInfo.sq[1].y = toY;
\r
9594 ClearPremoveHighlights()
\r
9596 premoveHighlightInfo.sq[0].x = premoveHighlightInfo.sq[0].y =
\r
9597 premoveHighlightInfo.sq[1].x = premoveHighlightInfo.sq[1].y = -1;
\r
9601 ShutDownFrontEnd()
\r
9603 if (saveSettingsOnExit) SaveSettings(settingsFileName);
\r
9604 DeleteClipboardTempFiles();
\r
9610 if (IsIconic(hwndMain))
\r
9611 ShowWindow(hwndMain, SW_RESTORE);
\r
9613 SetActiveWindow(hwndMain);
\r
9617 * Prototypes for animation support routines
\r
9619 static void ScreenSquare(int column, int row, POINT * pt);
\r
9620 static void Tween( POINT * start, POINT * mid, POINT * finish, int factor,
\r
9621 POINT frames[], int * nFrames);
\r
9627 AnimateAtomicCapture(Board board, int fromX, int fromY, int toX, int toY)
\r
9628 { // [HGM] atomic: animate blast wave
\r
9631 explodeInfo.fromX = fromX;
\r
9632 explodeInfo.fromY = fromY;
\r
9633 explodeInfo.toX = toX;
\r
9634 explodeInfo.toY = toY;
\r
9635 for(i=1; i<4*kFactor; i++) {
\r
9636 explodeInfo.radius = (i*180)/(4*kFactor-1);
\r
9637 DrawPosition(FALSE, board);
\r
9638 Sleep(appData.animSpeed);
\r
9640 explodeInfo.radius = 0;
\r
9641 DrawPosition(TRUE, board);
\r
9645 AnimateMove(board, fromX, fromY, toX, toY)
\r
9652 ChessSquare piece;
\r
9653 POINT start, finish, mid;
\r
9654 POINT frames[kFactor * 2 + 1];
\r
9657 if (!appData.animate) return;
\r
9658 if (doingSizing) return;
\r
9659 if (fromY < 0 || fromX < 0) return;
\r
9660 piece = board[fromY][fromX];
\r
9661 if (piece >= EmptySquare) return;
\r
9663 ScreenSquare(fromX, fromY, &start);
\r
9664 ScreenSquare(toX, toY, &finish);
\r
9666 /* All moves except knight jumps move in straight line */
\r
9667 if (!(abs(fromX-toX) == 1 && abs(fromY-toY) == 2 || abs(fromX-toX) == 2 && abs(fromY-toY) == 1)) {
\r
9668 mid.x = start.x + (finish.x - start.x) / 2;
\r
9669 mid.y = start.y + (finish.y - start.y) / 2;
\r
9671 /* Knight: make straight movement then diagonal */
\r
9672 if (abs(toY - fromY) < abs(toX - fromX)) {
\r
9673 mid.x = start.x + (finish.x - start.x) / 2;
\r
9677 mid.y = start.y + (finish.y - start.y) / 2;
\r
9681 /* Don't use as many frames for very short moves */
\r
9682 if (abs(toY - fromY) + abs(toX - fromX) <= 2)
\r
9683 Tween(&start, &mid, &finish, kFactor - 1, frames, &nFrames);
\r
9685 Tween(&start, &mid, &finish, kFactor, frames, &nFrames);
\r
9687 animInfo.from.x = fromX;
\r
9688 animInfo.from.y = fromY;
\r
9689 animInfo.to.x = toX;
\r
9690 animInfo.to.y = toY;
\r
9691 animInfo.lastpos = start;
\r
9692 animInfo.piece = piece;
\r
9693 for (n = 0; n < nFrames; n++) {
\r
9694 animInfo.pos = frames[n];
\r
9695 DrawPosition(FALSE, NULL);
\r
9696 animInfo.lastpos = animInfo.pos;
\r
9697 Sleep(appData.animSpeed);
\r
9699 animInfo.pos = finish;
\r
9700 DrawPosition(FALSE, NULL);
\r
9701 animInfo.piece = EmptySquare;
\r
9702 Explode(board, fromX, fromY, toX, toY);
\r
9705 /* Convert board position to corner of screen rect and color */
\r
9708 ScreenSquare(column, row, pt)
\r
9709 int column; int row; POINT * pt;
\r
9712 pt->x = lineGap + ((BOARD_WIDTH-1)-column) * (squareSize + lineGap);
\r
9713 pt->y = lineGap + row * (squareSize + lineGap);
\r
9715 pt->x = lineGap + column * (squareSize + lineGap);
\r
9716 pt->y = lineGap + ((BOARD_HEIGHT-1)-row) * (squareSize + lineGap);
\r
9720 /* Generate a series of frame coords from start->mid->finish.
\r
9721 The movement rate doubles until the half way point is
\r
9722 reached, then halves back down to the final destination,
\r
9723 which gives a nice slow in/out effect. The algorithmn
\r
9724 may seem to generate too many intermediates for short
\r
9725 moves, but remember that the purpose is to attract the
\r
9726 viewers attention to the piece about to be moved and
\r
9727 then to where it ends up. Too few frames would be less
\r
9731 Tween(start, mid, finish, factor, frames, nFrames)
\r
9732 POINT * start; POINT * mid;
\r
9733 POINT * finish; int factor;
\r
9734 POINT frames[]; int * nFrames;
\r
9736 int n, fraction = 1, count = 0;
\r
9738 /* Slow in, stepping 1/16th, then 1/8th, ... */
\r
9739 for (n = 0; n < factor; n++)
\r
9741 for (n = 0; n < factor; n++) {
\r
9742 frames[count].x = start->x + (mid->x - start->x) / fraction;
\r
9743 frames[count].y = start->y + (mid->y - start->y) / fraction;
\r
9745 fraction = fraction / 2;
\r
9749 frames[count] = *mid;
\r
9752 /* Slow out, stepping 1/2, then 1/4, ... */
\r
9754 for (n = 0; n < factor; n++) {
\r
9755 frames[count].x = finish->x - (finish->x - mid->x) / fraction;
\r
9756 frames[count].y = finish->y - (finish->y - mid->y) / fraction;
\r
9758 fraction = fraction * 2;
\r
9764 HistorySet( char movelist[][2*MOVE_LEN], int first, int last, int current )
\r
9766 MoveHistorySet( movelist, first, last, current, pvInfoList );
\r
9768 EvalGraphSet( first, last, current, pvInfoList );
\r
9772 SettingsPopUp(ChessProgramState *cps)
\r
9773 { // [HGM] wrapper needed because handles must not be passed through back-end
\r
9774 EngineOptionsPopup(savedHwnd, cps);
\r