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 HWND hCommPort = NULL; /* currently open comm port */
\r
190 static HWND hwndPause; /* pause button */
\r
191 static HBITMAP pieceBitmap[3][(int) BlackPawn]; /* [HGM] nr of bitmaps referred to bP in stead of wK */
\r
192 static HBRUSH lightSquareBrush, darkSquareBrush,
\r
193 blackSquareBrush, /* [HGM] for band between board and holdings */
\r
194 explodeBrush, /* [HGM] atomic */
\r
195 markerBrush, /* [HGM] markers */
\r
196 whitePieceBrush, blackPieceBrush, iconBkgndBrush /*, outlineBrush*/;
\r
197 static POINT gridEndpoints[(BOARD_RANKS + BOARD_FILES + 2) * 2];
\r
198 static DWORD gridVertexCounts[BOARD_RANKS + BOARD_FILES + 2];
\r
199 static HPEN gridPen = NULL;
\r
200 static HPEN highlightPen = NULL;
\r
201 static HPEN premovePen = NULL;
\r
202 static NPLOGPALETTE pLogPal;
\r
203 static BOOL paletteChanged = FALSE;
\r
204 static HICON iconWhite, iconBlack, iconCurrent;
\r
205 static int doingSizing = FALSE;
\r
206 static int lastSizing = 0;
\r
207 static int prevStderrPort;
\r
208 static HBITMAP userLogo;
\r
210 static HBITMAP liteBackTexture = NULL;
\r
211 static HBITMAP darkBackTexture = NULL;
\r
212 static int liteBackTextureMode = BACK_TEXTURE_MODE_PLAIN;
\r
213 static int darkBackTextureMode = BACK_TEXTURE_MODE_PLAIN;
\r
214 static int backTextureSquareSize = 0;
\r
215 static struct { int x; int y; int mode; } backTextureSquareInfo[BOARD_RANKS][BOARD_FILES];
\r
217 #if __GNUC__ && !defined(_winmajor)
\r
218 #define oldDialog 0 /* cygwin doesn't define _winmajor; mingw does */
\r
220 #if defined(_winmajor)
\r
221 #define oldDialog (_winmajor < 4)
\r
223 #define oldDialog 0
\r
227 #define INTERNATIONAL
\r
229 #ifdef INTERNATIONAL
\r
230 # define _(s) T_(s)
\r
236 # define Translate(x, y)
\r
237 # define LoadLanguageFile(s)
\r
240 #ifdef INTERNATIONAL
\r
242 Boolean barbaric; // flag indicating if translation is needed
\r
244 // list of item numbers used in each dialog (used to alter language at run time)
\r
246 #define ABOUTBOX -1 /* not sure why these are needed */
\r
247 #define ABOUTBOX2 -1
\r
249 int dialogItems[][40] = {
\r
250 { ABOUTBOX, IDOK, 400 },
\r
251 { DLG_TimeControl, IDC_Babble, OPT_TCUseMoves, OPT_TCUseInc, OPT_TCUseFixed,
\r
252 OPT_TCtext1, OPT_TCtext2, OPT_TCitext1, OPT_TCitext2, OPT_TCftext, GPB_Factors, IDC_Factor1, IDC_Factor2, IDOK, IDCANCEL },
\r
253 { DLG_LoadOptions, OPT_Autostep, OPT_AStext1, IDOK, IDCANCEL },
\r
254 { DLG_SaveOptions, OPT_Autosave, OPT_AVPrompt, OPT_AVToFile, OPT_AVBrowse,
\r
255 801, OPT_PGN, OPT_Old, OPT_OutOfBookInfo, IDOK, IDCANCEL },
\r
256 { 1536, 1090, IDC_Directories, 1089, 1091, IDOK, IDCANCEL, 1038, IDC_IndexNr, 1037 },
\r
257 { DLG_CommPort, IDOK, IDCANCEL, IDC_Port, IDC_Rate, IDC_Bits, IDC_Parity,
\r
258 IDC_Stop, IDC_Flow, OPT_SerialHelp },
\r
259 { DLG_EditComment, IDOK, OPT_CancelComment, OPT_ClearComment, OPT_EditComment },
\r
260 { DLG_PromotionKing, PB_Chancellor, PB_Archbishop, PB_Queen, PB_Rook,
\r
261 PB_Bishop, PB_Knight, PB_King, IDCANCEL, IDC_Yes, IDC_No, IDC_Centaur },
\r
262 { ABOUTBOX2, IDC_ChessBoard },
\r
263 { DLG_GameList, OPT_GameListLoad, OPT_GameListPrev, OPT_GameListNext,
\r
264 OPT_GameListClose, IDC_GameListDoFilter },
\r
265 { DLG_EditTags, IDOK, OPT_TagsCancel, OPT_EditTags },
\r
266 { DLG_Error, IDOK },
\r
267 { DLG_Colorize, IDOK, IDCANCEL, OPT_ChooseColor, OPT_Bold, OPT_Italic,
\r
268 OPT_Underline, OPT_Strikeout, OPT_Sample },
\r
269 { DLG_Question, IDOK, IDCANCEL, OPT_QuestionText },
\r
270 { DLG_Startup, IDC_Welcome, OPT_ChessEngine, OPT_ChessServer, OPT_View,
\r
271 IDC_SPECIFY_ENG_STATIC, IDC_SPECIFY_SERVER_STATIC, OPT_AnyAdditional,
\r
272 IDOK, IDCANCEL, IDM_HELPCONTENTS },
\r
273 { DLG_IndexNumber, IDC_Index },
\r
274 { DLG_TypeInMove, IDOK, IDCANCEL },
\r
275 { DLG_TypeInName, IDOK, IDCANCEL },
\r
276 { DLG_Sound, IDC_Event, OPT_NoSound, OPT_DefaultBeep, OPT_BuiltInSound,
\r
277 OPT_WavFile, OPT_BrowseSound, OPT_DefaultSounds, IDOK, IDCANCEL, OPT_PlaySound },
\r
278 { DLG_GeneralOptions, IDOK, IDCANCEL, OPT_AlwaysOnTop, OPT_HighlightLastMove,
\r
279 OPT_AlwaysQueen, OPT_PeriodicUpdates, OPT_AnimateDragging, OPT_PonderNextMove,
\r
280 OPT_AnimateMoving, OPT_PopupExitMessage, OPT_AutoFlag, OPT_PopupMoveErrors,
\r
281 OPT_AutoFlipView, OPT_ShowButtonBar, OPT_AutoRaiseBoard, OPT_ShowCoordinates,
\r
282 OPT_Blindfold, OPT_ShowThinking, OPT_HighlightDragging, OPT_TestLegality,
\r
283 OPT_SaveExtPGN, OPT_HideThinkFromHuman, OPT_ExtraInfoInMoveHistory,
\r
284 OPT_HighlightMoveArrow, OPT_AutoLogo },
\r
285 { DLG_IcsOptions, IDOK, IDCANCEL, OPT_AutoComment, OPT_AutoKibitz, OPT_AutoObserve,
\r
286 OPT_GetMoveList, OPT_LocalLineEditing, OPT_QuietPlay, OPT_SeekGraph, OPT_AutoRefresh,
\r
287 OPT_BgObserve, OPT_DualBoard, OPT_Premove, OPT_PremoveWhite, OPT_PremoveBlack,
\r
288 OPT_SmartMove, OPT_IcsAlarm, IDC_Sec, OPT_ChooseShoutColor, OPT_ChooseSShoutColor,
\r
289 OPT_ChooseChannel1Color, OPT_ChooseChannelColor, OPT_ChooseKibitzColor,
\r
290 OPT_ChooseTellColor, OPT_ChooseChallengeColor, OPT_ChooseRequestColor,
\r
291 OPT_ChooseSeekColor, OPT_ChooseNormalColor, OPT_ChooseBackgroundColor,
\r
292 OPT_DefaultColors, OPT_DontColorize, IDC_Boxes, GPB_Colors, GPB_Premove,
\r
293 GPB_General, GPB_Alarm },
\r
294 { DLG_BoardOptions, IDOK, IDCANCEL, OPT_SizeTiny, OPT_SizeTeeny, OPT_SizeDinky,
\r
295 OPT_SizePetite, OPT_SizeSlim, OPT_SizeSmall, OPT_SizeMediocre, OPT_SizeMiddling,
\r
296 OPT_SizeAverage, OPT_SizeModerate, OPT_SizeMedium, OPT_SizeBulky, OPT_SizeLarge,
\r
297 OPT_SizeBig, OPT_SizeHuge, OPT_SizeGiant, OPT_SizeColossal, OPT_SizeTitanic,
\r
298 OPT_ChooseLightSquareColor, OPT_ChooseDarkSquareColor, OPT_ChooseWhitePieceColor,
\r
299 OPT_ChooseBlackPieceColor, OPT_ChooseHighlightSquareColor, OPT_ChoosePremoveHighlightColor,
\r
300 OPT_Monochrome, OPT_AllWhite, OPT_UpsideDown, OPT_DefaultBoardColors, GPB_Colors,
\r
301 IDC_Light, IDC_Dark, IDC_White, IDC_Black, IDC_High, IDC_PreHigh, GPB_Size },
\r
302 { DLG_NewVariant, IDOK, IDCANCEL, OPT_VariantNormal, OPT_VariantFRC, OPT_VariantWildcastle,
\r
303 OPT_VariantNocastle, OPT_VariantLosers, OPT_VariantGiveaway, OPT_VariantSuicide,
\r
304 OPT_Variant3Check, OPT_VariantTwoKings, OPT_VariantAtomic, OPT_VariantCrazyhouse,
\r
305 OPT_VariantBughouse, OPT_VariantTwilight, OPT_VariantShogi, OPT_VariantSuper,
\r
306 OPT_VariantKnightmate, OPT_VariantBerolina, OPT_VariantCylinder, OPT_VariantFairy,
\r
307 OPT_VariantMakruk, OPT_VariantGothic, OPT_VariantCapablanca, OPT_VariantJanus,
\r
308 OPT_VariantCRC, OPT_VariantFalcon, OPT_VariantCourier, OPT_VariantGreat,
\r
309 OPT_VariantShatranj, OPT_VariantXiangqi, GPB_Variant, GPB_Board, IDC_Height,
\r
310 IDC_Width, IDC_Hand, IDC_Pieces, IDC_Def },
\r
311 { DLG_Fonts, IDOK, IDCANCEL, OPT_ChooseClockFont, OPT_ChooseMessageFont,
\r
312 OPT_ChooseCoordFont, OPT_ChooseTagFont, OPT_ChooseCommentsFont, OPT_ChooseConsoleFont, OPT_ChooseMoveHistoryFont, OPT_DefaultFonts,
\r
313 OPT_ClockFont, OPT_MessageFont, OPT_CoordFont, OPT_EditTagsFont,
\r
314 OPT_CommentsFont, OPT_MessageFont5, GPB_Current, GPB_All, OPT_MessageFont6 },
\r
315 { DLG_NewGameFRC, IDC_NFG_Label, IDC_NFG_Random, IDOK, IDCANCEL },
\r
316 { DLG_GameListOptions, IDC_GLT, IDC_GLT_Up, IDC_GLT_Down, IDC_GLT_Restore,
\r
317 IDC_GLT_Default, IDOK, IDCANCEL, IDC_GLT_RestoreTo },
\r
318 { DLG_MoveHistory },
\r
319 { DLG_EvalGraph },
\r
320 { DLG_EngineOutput, IDC_EngineLabel1, IDC_Engine1_NPS, IDC_EngineLabel2, IDC_Engine2_NPS },
\r
321 { DLG_Chat, IDC_Partner, IDC_Clear, IDC_Send, },
\r
322 { DLG_EnginePlayOptions, IDC_EpPonder, IDC_EpShowThinking, IDC_EpHideThinkingHuman,
\r
323 IDC_EpPeriodicUpdates, GPB_Adjudications, IDC_Draw, IDC_Moves, IDC_Threshold,
\r
324 IDC_Centi, IDC_TestClaims, IDC_DetectMates, IDC_MaterialDraws, IDC_TrivialDraws,
\r
325 GPB_Apply, IDC_Rule, IDC_Repeats, IDC_ScoreAbs1, IDC_ScoreAbs2, IDOK, IDCANCEL },
\r
326 { DLG_OptionsUCI, IDC_PolyDir, IDC_BrowseForPolyglotDir, IDC_Hash, IDC_Path,
\r
327 IDC_BrowseForEGTB, IDC_Cache, IDC_UseBook, IDC_BrowseForBook, IDC_CPU, IDC_OwnBook1,
\r
328 IDC_OwnBook2, IDC_Depth, IDC_Variation, IDC_DefGames, IDOK, IDCANCEL },
\r
332 static char languageBuf[50000], *foreign[1000], *english[1000], *languageFile[MSG_SIZ];
\r
333 static int lastChecked;
\r
334 static char oldLanguage[MSG_SIZ], *menuText[10][30];
\r
335 extern int tinyLayout;
\r
336 extern char * menuBarText[][8];
\r
339 LoadLanguageFile(char *name)
\r
340 { //load the file with translations, and make a list of the strings to be translated, and their translations
\r
342 int i=0, j=0, n=0, k;
\r
345 if(!name || name[0] == NULLCHAR) return;
\r
346 snprintf(buf, MSG_SIZ, "%s%s", name, strchr(name, '.') ? "" : ".lng"); // auto-append lng extension
\r
347 if(!strcmp(buf, oldLanguage)) { barbaric = 1; return; } // this language already loaded; just switch on
\r
348 if((f = fopen(buf, "r")) == NULL) return;
\r
349 while((k = fgetc(f)) != EOF) {
\r
350 if(i >= sizeof(languageBuf)) { DisplayError("Language file too big", 0); return; }
\r
351 languageBuf[i] = k;
\r
353 if(languageBuf[n] == '"' && languageBuf[i-1] == '"') {
\r
355 if(p = strstr(languageBuf + n + 1, "\" === \"")) {
\r
356 if(p > languageBuf+n+2 && p+8 < languageBuf+i) {
\r
357 if(j >= sizeof(english)) { DisplayError("Too many translated strings", 0); return; }
\r
358 english[j] = languageBuf + n + 1; *p = 0;
\r
359 foreign[j++] = p + 7; languageBuf[i-1] = 0;
\r
360 //if(appData.debugMode) fprintf(debugFP, "translation: replace '%s' by '%s'\n", english[j-1], foreign[j-1]);
\r
365 } else if(i > 0 && languageBuf[i-1] == '\\') {
\r
367 case 'n': k = '\n'; break;
\r
368 case 'r': k = '\r'; break;
\r
369 case 't': k = '\t'; break;
\r
371 languageBuf[--i] = k;
\r
376 barbaric = (j != 0);
\r
377 safeStrCpy(oldLanguage, buf, sizeof(oldLanguage)/sizeof(oldLanguage[0]) );
\r
382 { // return the translation of the given string
\r
383 // efficiency can be improved a lot...
\r
385 //if(appData.debugMode) fprintf(debugFP, "T_(%s)\n", s);
\r
386 if(!barbaric) return s;
\r
387 if(!s) return ""; // sanity
\r
388 while(english[i]) {
\r
389 if(!strcmp(s, english[i])) return foreign[i];
\r
396 Translate(HWND hDlg, int dialogID)
\r
397 { // translate all text items in the given dialog
\r
399 char buf[MSG_SIZ], *s;
\r
400 //if(appData.debugMode) fprintf(debugFP, "Translate(%d)\n", dialogID);
\r
401 if(!barbaric) return;
\r
402 while(dialogItems[i][0] && dialogItems[i][0] != dialogID) i++; // find the dialog description
\r
403 if(dialogItems[i][0] != dialogID) return; // unknown dialog, should not happen
\r
404 GetWindowText( hDlg, buf, MSG_SIZ );
\r
406 //if(appData.debugMode) fprintf(debugFP, "WindowText '%s' -> '%s'\n", buf, s);
\r
407 if(strcmp(buf, s)) SetWindowText(hDlg, s); // replace by translated string (if different)
\r
408 for(j=1; k=dialogItems[i][j]; j++) { // translate all listed dialog items
\r
409 GetDlgItemText(hDlg, k, buf, MSG_SIZ);
\r
410 if(strlen(buf) == 0) continue;
\r
412 if(strcmp(buf, s)) SetDlgItemText(hDlg, k, s); // replace by translated string (if different)
\r
417 TranslateMenus(int addLanguage)
\r
420 WIN32_FIND_DATA fileData;
\r
422 #define IDM_English 1895
\r
424 HMENU mainMenu = GetMenu(hwndMain);
\r
425 for (i=GetMenuItemCount(mainMenu)-1; i>=0; i--) {
\r
426 HMENU subMenu = GetSubMenu(mainMenu, i);
\r
427 ModifyMenu(mainMenu, i, MF_STRING|MF_BYPOSITION|MF_POPUP|EnableMenuItem(mainMenu, i, MF_BYPOSITION),
\r
428 (UINT) subMenu, T_(menuBarText[tinyLayout][i]));
\r
429 for(j=GetMenuItemCount(subMenu)-1; j>=0; j--){
\r
431 UINT k = GetMenuItemID(subMenu, j);
\r
433 safeStrCpy(buf, menuText[i][j], sizeof(buf)/sizeof(buf[0]) ); else {
\r
434 GetMenuString(subMenu, j, buf, MSG_SIZ, MF_BYPOSITION);
\r
435 menuText[i][j] = strdup(buf); // remember original on first change
\r
437 if(buf[0] == NULLCHAR) continue;
\r
438 //fprintf(debugFP, "menu(%d,%d) = %s (%08x, %08x) %d\n", i, j, buf, mainMenu, subMenu, k);
\r
439 ModifyMenu(subMenu, j, MF_STRING|MF_BYPOSITION
\r
440 |CheckMenuItem(subMenu, j, MF_BYPOSITION)
\r
441 |EnableMenuItem(subMenu, j, MF_BYPOSITION), k, T_(buf));
\r
444 DrawMenuBar(hwndMain);
\r
447 if(!addLanguage) return;
\r
448 if((hFind = FindFirstFile("*.LNG", &fileData)) != INVALID_HANDLE_VALUE) {
\r
449 HMENU mainMenu = GetMenu(hwndMain);
\r
450 HMENU subMenu = GetSubMenu(mainMenu, GetMenuItemCount(mainMenu)-1);
\r
451 AppendMenu(subMenu, MF_SEPARATOR, (UINT_PTR) 0, NULL);
\r
452 AppendMenu(subMenu, MF_ENABLED|MF_STRING|(barbaric?MF_UNCHECKED:MF_CHECKED), (UINT_PTR) IDM_English, (LPCTSTR) "English");
\r
453 i = 0; lastChecked = IDM_English;
\r
455 char *p, *q = fileData.cFileName;
\r
456 int checkFlag = MF_UNCHECKED;
\r
457 languageFile[i] = strdup(q);
\r
458 if(barbaric && !strcmp(oldLanguage, q)) {
\r
459 checkFlag = MF_CHECKED;
\r
460 lastChecked = IDM_English + i + 1;
\r
461 CheckMenuItem(mainMenu, IDM_English, MF_BYCOMMAND|MF_UNCHECKED);
\r
463 *q = ToUpper(*q); while(*++q) *q = ToLower(*q);
\r
464 p = strstr(fileData.cFileName, ".lng");
\r
466 AppendMenu(subMenu, MF_ENABLED|MF_STRING|checkFlag, (UINT_PTR) IDM_English + ++i, (LPCTSTR) fileData.cFileName);
\r
467 } while(FindNextFile(hFind, &fileData));
\r
480 int cliWidth, cliHeight;
\r
483 SizeInfo sizeInfo[] =
\r
485 { "tiny", 21, 0, 1, 1, 0, 0 },
\r
486 { "teeny", 25, 1, 1, 1, 0, 0 },
\r
487 { "dinky", 29, 1, 1, 1, 0, 0 },
\r
488 { "petite", 33, 1, 1, 1, 0, 0 },
\r
489 { "slim", 37, 2, 1, 0, 0, 0 },
\r
490 { "small", 40, 2, 1, 0, 0, 0 },
\r
491 { "mediocre", 45, 2, 1, 0, 0, 0 },
\r
492 { "middling", 49, 2, 0, 0, 0, 0 },
\r
493 { "average", 54, 2, 0, 0, 0, 0 },
\r
494 { "moderate", 58, 3, 0, 0, 0, 0 },
\r
495 { "medium", 64, 3, 0, 0, 0, 0 },
\r
496 { "bulky", 72, 3, 0, 0, 0, 0 },
\r
497 { "large", 80, 3, 0, 0, 0, 0 },
\r
498 { "big", 87, 3, 0, 0, 0, 0 },
\r
499 { "huge", 95, 3, 0, 0, 0, 0 },
\r
500 { "giant", 108, 3, 0, 0, 0, 0 },
\r
501 { "colossal", 116, 4, 0, 0, 0, 0 },
\r
502 { "titanic", 129, 4, 0, 0, 0, 0 },
\r
503 { NULL, 0, 0, 0, 0, 0, 0 }
\r
506 #define MF(x) {x, {{0,}, 0. }, {0, }, 0}
\r
507 MyFont fontRec[NUM_SIZES][NUM_FONTS] =
\r
509 { 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
510 { 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
511 { 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
512 { 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
513 { 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
514 { 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
515 { 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
516 { 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
517 { 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
518 { 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
519 { 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
520 { 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
521 { 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
522 { 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
523 { 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
524 { 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
525 { 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
526 { 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
529 MyFont *font[NUM_SIZES][NUM_FONTS];
\r
538 #define BUTTON_WIDTH (tinyLayout ? 16 : 32)
\r
539 #define N_BUTTONS 5
\r
541 MyButtonDesc buttonDesc[N_BUTTONS] =
\r
543 {"<<", IDM_ToStart, NULL, NULL},
\r
544 {"<", IDM_Backward, NULL, NULL},
\r
545 {"P", IDM_Pause, NULL, NULL},
\r
546 {">", IDM_Forward, NULL, NULL},
\r
547 {">>", IDM_ToEnd, NULL, NULL},
\r
550 int tinyLayout = 0, smallLayout = 0;
\r
551 #define MENU_BAR_ITEMS 7
\r
552 char *menuBarText[2][MENU_BAR_ITEMS+1] = {
\r
553 { N_("&File"), N_("&Mode"), N_("&Action"), N_("&Step"), N_("&Options"), N_("&Help"), NULL },
\r
554 { N_("&F"), N_("&M"), N_("&A"), N_("&S"), N_("&O"), N_("&H"), NULL },
\r
558 MySound sounds[(int)NSoundClasses];
\r
559 MyTextAttribs textAttribs[(int)NColorClasses];
\r
561 MyColorizeAttribs colorizeAttribs[] = {
\r
562 { (COLORREF)0, 0, N_("Shout Text") },
\r
563 { (COLORREF)0, 0, N_("SShout/CShout") },
\r
564 { (COLORREF)0, 0, N_("Channel 1 Text") },
\r
565 { (COLORREF)0, 0, N_("Channel Text") },
\r
566 { (COLORREF)0, 0, N_("Kibitz Text") },
\r
567 { (COLORREF)0, 0, N_("Tell Text") },
\r
568 { (COLORREF)0, 0, N_("Challenge Text") },
\r
569 { (COLORREF)0, 0, N_("Request Text") },
\r
570 { (COLORREF)0, 0, N_("Seek Text") },
\r
571 { (COLORREF)0, 0, N_("Normal Text") },
\r
572 { (COLORREF)0, 0, N_("None") }
\r
577 static char *commentTitle;
\r
578 static char *commentText;
\r
579 static int commentIndex;
\r
580 static Boolean editComment = FALSE;
\r
583 char errorTitle[MSG_SIZ];
\r
584 char errorMessage[2*MSG_SIZ];
\r
585 HWND errorDialog = NULL;
\r
586 BOOLEAN moveErrorMessageUp = FALSE;
\r
587 BOOLEAN consoleEcho = TRUE;
\r
588 CHARFORMAT consoleCF;
\r
589 COLORREF consoleBackgroundColor;
\r
591 char *programVersion;
\r
597 typedef int CPKind;
\r
606 SOCKET sock2; /* stderr socket for OpenRcmd */
\r
609 #define INPUT_SOURCE_BUF_SIZE 4096
\r
611 typedef struct _InputSource {
\r
618 char buf[INPUT_SOURCE_BUF_SIZE];
\r
622 InputCallback func;
\r
623 struct _InputSource *second; /* for stderr thread on CPRcmd */
\r
627 InputSource *consoleInputSource;
\r
632 VOID ConsoleOutput(char* data, int length, int forceVisible);
\r
633 VOID ConsoleCreate();
\r
635 ConsoleWndProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam);
\r
636 VOID ColorizeTextPopup(HWND hwnd, ColorClass cc);
\r
637 VOID PrintCommSettings(FILE *f, char *name, DCB *dcb);
\r
638 VOID ParseCommSettings(char *arg, DCB *dcb);
\r
640 StartupDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam);
\r
641 VOID APIENTRY MenuPopup(HWND hwnd, POINT pt, HMENU hmenu, UINT def);
\r
642 void ParseIcsTextMenu(char *icsTextMenuString);
\r
643 VOID PopUpMoveDialog(char firstchar);
\r
644 VOID PopUpNameDialog(char firstchar);
\r
645 VOID UpdateSampleText(HWND hDlg, int id, MyColorizeAttribs *mca);
\r
649 int GameListOptions();
\r
651 int dummy; // [HGM] for obsolete args
\r
653 HWND hwndMain = NULL; /* root window*/
\r
654 HWND hwndConsole = NULL;
\r
655 HWND commentDialog = NULL;
\r
656 HWND moveHistoryDialog = NULL;
\r
657 HWND evalGraphDialog = NULL;
\r
658 HWND engineOutputDialog = NULL;
\r
659 HWND gameListDialog = NULL;
\r
660 HWND editTagsDialog = NULL;
\r
662 int commentUp = FALSE;
\r
664 WindowPlacement wpMain;
\r
665 WindowPlacement wpConsole;
\r
666 WindowPlacement wpComment;
\r
667 WindowPlacement wpMoveHistory;
\r
668 WindowPlacement wpEvalGraph;
\r
669 WindowPlacement wpEngineOutput;
\r
670 WindowPlacement wpGameList;
\r
671 WindowPlacement wpTags;
\r
673 VOID EngineOptionsPopup(); // [HGM] settings
\r
675 VOID GothicPopUp(char *title, VariantClass variant);
\r
677 * Setting "frozen" should disable all user input other than deleting
\r
678 * the window. We do this while engines are initializing themselves.
\r
680 static int frozen = 0;
\r
681 static int oldMenuItemState[MENU_BAR_ITEMS];
\r
687 if (frozen) return;
\r
689 hmenu = GetMenu(hwndMain);
\r
690 for (i=0; i<MENU_BAR_ITEMS; i++) {
\r
691 oldMenuItemState[i] = EnableMenuItem(hmenu, i, MF_BYPOSITION|MF_GRAYED);
\r
693 DrawMenuBar(hwndMain);
\r
696 /* Undo a FreezeUI */
\r
702 if (!frozen) return;
\r
704 hmenu = GetMenu(hwndMain);
\r
705 for (i=0; i<MENU_BAR_ITEMS; i++) {
\r
706 EnableMenuItem(hmenu, i, MF_BYPOSITION|oldMenuItemState[i]);
\r
708 DrawMenuBar(hwndMain);
\r
711 /*static*/ int fromX = -1, fromY = -1, toX, toY; // [HGM] moved upstream, so JAWS can use them
\r
713 /* JAWS preparation patch (WinBoard for the sight impaired). Define required insertions as empty */
\r
719 #define JAWS_ALT_INTERCEPT
\r
720 #define JAWS_KB_NAVIGATION
\r
721 #define JAWS_MENU_ITEMS
\r
722 #define JAWS_SILENCE
\r
723 #define JAWS_REPLAY
\r
725 #define JAWS_COPYRIGHT
\r
726 #define JAWS_DELETE(X) X
\r
727 #define SAYMACHINEMOVE()
\r
731 /*---------------------------------------------------------------------------*\
\r
735 \*---------------------------------------------------------------------------*/
\r
738 WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
\r
739 LPSTR lpCmdLine, int nCmdShow)
\r
742 HANDLE hAccelMain, hAccelNoAlt, hAccelNoICS;
\r
743 // INITCOMMONCONTROLSEX ex;
\r
747 LoadLibrary("RICHED32.DLL");
\r
748 consoleCF.cbSize = sizeof(CHARFORMAT);
\r
750 if (!InitApplication(hInstance)) {
\r
753 if (!InitInstance(hInstance, nCmdShow, lpCmdLine)) {
\r
759 // InitCommonControlsEx(&ex);
\r
760 InitCommonControls();
\r
762 hAccelMain = LoadAccelerators (hInstance, szAppName);
\r
763 hAccelNoAlt = LoadAccelerators (hInstance, "NO_ALT");
\r
764 hAccelNoICS = LoadAccelerators( hInstance, "NO_ICS"); /* [AS] No Ctrl-V on ICS!!! */
\r
766 /* Acquire and dispatch messages until a WM_QUIT message is received. */
\r
768 while (GetMessage(&msg, /* message structure */
\r
769 NULL, /* handle of window receiving the message */
\r
770 0, /* lowest message to examine */
\r
771 0)) /* highest message to examine */
\r
774 if(msg.message == WM_CHAR && msg.wParam == '\t') {
\r
775 // [HGM] navigate: switch between all windows with tab
\r
776 HWND e1 = NULL, e2 = NULL, mh = NULL, hInput = NULL, hText = NULL;
\r
777 int i, currentElement = 0;
\r
779 // first determine what element of the chain we come from (if any)
\r
780 if(appData.icsActive) {
\r
781 hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
782 hText = GetDlgItem(hwndConsole, OPT_ConsoleText);
\r
784 if(engineOutputDialog && EngineOutputIsUp()) {
\r
785 e1 = GetDlgItem(engineOutputDialog, IDC_EngineMemo1);
\r
786 e2 = GetDlgItem(engineOutputDialog, IDC_EngineMemo2);
\r
788 if(moveHistoryDialog && MoveHistoryIsUp()) {
\r
789 mh = GetDlgItem(moveHistoryDialog, IDC_MoveHistory);
\r
791 if(msg.hwnd == hwndMain) currentElement = 7 ; else
\r
792 if(msg.hwnd == engineOutputDialog) currentElement = 2; else
\r
793 if(msg.hwnd == e1) currentElement = 2; else
\r
794 if(msg.hwnd == e2) currentElement = 3; else
\r
795 if(msg.hwnd == moveHistoryDialog) currentElement = 4; else
\r
796 if(msg.hwnd == mh) currentElement = 4; else
\r
797 if(msg.hwnd == evalGraphDialog) currentElement = 6; else
\r
798 if(msg.hwnd == hText) currentElement = 5; else
\r
799 if(msg.hwnd == hInput) currentElement = 6; else
\r
800 for (i = 0; i < N_BUTTONS; i++) {
\r
801 if (buttonDesc[i].hwnd == msg.hwnd) { currentElement = 1; break; }
\r
804 // determine where to go to
\r
805 if(currentElement) { HWND h = NULL; int direction = GetKeyState(VK_SHIFT) < 0 ? -1 : 1;
\r
807 currentElement = (currentElement + direction) % 7;
\r
808 switch(currentElement) {
\r
810 h = hwndMain; break; // passing this case always makes the loop exit
\r
812 h = buttonDesc[0].hwnd; break; // could be NULL
\r
814 if(!EngineOutputIsUp()) continue; // skip closed auxiliary windows
\r
817 if(!EngineOutputIsUp()) continue;
\r
820 if(!MoveHistoryIsUp()) continue;
\r
822 // case 6: // input to eval graph does not seem to get here!
\r
823 // if(!EvalGraphIsUp()) continue;
\r
824 // h = evalGraphDialog; break;
\r
826 if(!appData.icsActive) continue;
\r
830 if(!appData.icsActive) continue;
\r
836 if(currentElement > 4 && IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);
\r
837 if(currentElement < 5 && IsIconic(hwndMain)) ShowWindow(hwndMain, SW_RESTORE); // all open together
\r
840 continue; // this message now has been processed
\r
844 if (!(commentDialog && IsDialogMessage(commentDialog, &msg)) &&
\r
845 !(moveHistoryDialog && IsDialogMessage(moveHistoryDialog, &msg)) &&
\r
846 !(evalGraphDialog && IsDialogMessage(evalGraphDialog, &msg)) &&
\r
847 !(engineOutputDialog && IsDialogMessage(engineOutputDialog, &msg)) &&
\r
848 !(editTagsDialog && IsDialogMessage(editTagsDialog, &msg)) &&
\r
849 !(gameListDialog && IsDialogMessage(gameListDialog, &msg)) &&
\r
850 !(errorDialog && IsDialogMessage(errorDialog, &msg)) &&
\r
851 !(!frozen && TranslateAccelerator(hwndMain, hAccelMain, &msg)) && JAWS_ACCEL
\r
852 !(!hwndConsole && TranslateAccelerator(hwndMain, hAccelNoICS, &msg)) &&
\r
853 !(!hwndConsole && TranslateAccelerator(hwndMain, hAccelNoAlt, &msg))) {
\r
854 int done = 0, i; // [HGM] chat: dispatch cat-box messages
\r
855 for(i=0; i<MAX_CHAT; i++)
\r
856 if(chatHandle[i] && IsDialogMessage(chatHandle[i], &msg)) {
\r
859 if(done) continue; // [HGM] chat: end patch
\r
860 TranslateMessage(&msg); /* Translates virtual key codes */
\r
861 DispatchMessage(&msg); /* Dispatches message to window */
\r
866 return (msg.wParam); /* Returns the value from PostQuitMessage */
\r
869 /*---------------------------------------------------------------------------*\
\r
871 * Initialization functions
\r
873 \*---------------------------------------------------------------------------*/
\r
877 { // update user logo if necessary
\r
878 static char oldUserName[MSG_SIZ], *curName;
\r
880 if(appData.autoLogo) {
\r
881 curName = UserName();
\r
882 if(strcmp(curName, oldUserName)) {
\r
883 snprintf(oldUserName, MSG_SIZ, "logos\\%s.bmp", curName);
\r
884 userLogo = LoadImage( 0, oldUserName, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );
\r
885 safeStrCpy(oldUserName, curName, sizeof(oldUserName)/sizeof(oldUserName[0]) );
\r
891 InitApplication(HINSTANCE hInstance)
\r
895 /* Fill in window class structure with parameters that describe the */
\r
898 wc.style = CS_HREDRAW | CS_VREDRAW; /* Class style(s). */
\r
899 wc.lpfnWndProc = (WNDPROC)WndProc; /* Window Procedure */
\r
900 wc.cbClsExtra = 0; /* No per-class extra data. */
\r
901 wc.cbWndExtra = 0; /* No per-window extra data. */
\r
902 wc.hInstance = hInstance; /* Owner of this class */
\r
903 wc.hIcon = LoadIcon(hInstance, "icon_white");
\r
904 wc.hCursor = LoadCursor(NULL, IDC_ARROW); /* Cursor */
\r
905 wc.hbrBackground = (HBRUSH)(COLOR_WINDOW+1); /* Default color */
\r
906 wc.lpszMenuName = szAppName; /* Menu name from .RC */
\r
907 wc.lpszClassName = szAppName; /* Name to register as */
\r
909 /* Register the window class and return success/failure code. */
\r
910 if (!RegisterClass(&wc)) return FALSE;
\r
912 wc.style = CS_HREDRAW | CS_VREDRAW;
\r
913 wc.lpfnWndProc = (WNDPROC)ConsoleWndProc;
\r
915 wc.cbWndExtra = DLGWINDOWEXTRA;
\r
916 wc.hInstance = hInstance;
\r
917 wc.hIcon = LoadIcon(hInstance, "icon_white");
\r
918 wc.hCursor = LoadCursor(NULL, IDC_ARROW);
\r
919 wc.hbrBackground = (HBRUSH)(COLOR_MENU+1);
\r
920 wc.lpszMenuName = NULL;
\r
921 wc.lpszClassName = szConsoleName;
\r
923 if (!RegisterClass(&wc)) return FALSE;
\r
928 /* Set by InitInstance, used by EnsureOnScreen */
\r
929 int screenHeight, screenWidth;
\r
932 EnsureOnScreen(int *x, int *y, int minX, int minY)
\r
934 // int gap = GetSystemMetrics(SM_CYFRAME) + GetSystemMetrics(SM_CYCAPTION);
\r
935 /* Be sure window at (x,y) is not off screen (or even mostly off screen) */
\r
936 if (*x > screenWidth - 32) *x = 0;
\r
937 if (*y > screenHeight - 32) *y = 0;
\r
938 if (*x < minX) *x = minX;
\r
939 if (*y < minY) *y = minY;
\r
943 InitInstance(HINSTANCE hInstance, int nCmdShow, LPSTR lpCmdLine)
\r
945 HWND hwnd; /* Main window handle. */
\r
947 WINDOWPLACEMENT wp;
\r
950 hInst = hInstance; /* Store instance handle in our global variable */
\r
951 programName = szAppName;
\r
953 if (SearchPath(NULL, "WinBoard.exe", NULL, MSG_SIZ, installDir, &filepart)) {
\r
954 *filepart = NULLCHAR;
\r
956 GetCurrentDirectory(MSG_SIZ, installDir);
\r
958 gameInfo.boardWidth = gameInfo.boardHeight = 8; // [HGM] won't have open window otherwise
\r
959 screenWidth = screenHeight = 1000; // [HGM] placement: kludge to allow calling EnsureOnScreen from InitAppData
\r
960 InitAppData(lpCmdLine); /* Get run-time parameters */
\r
961 /* xboard, and older WinBoards, controlled the move sound with the
\r
962 appData.ringBellAfterMoves option. In the current WinBoard, we
\r
963 always turn the option on (so that the backend will call us),
\r
964 then let the user turn the sound off by setting it to silence if
\r
965 desired. To accommodate old winboard.ini files saved by old
\r
966 versions of WinBoard, we also turn off the sound if the option
\r
967 was initially set to false. [HGM] taken out of InitAppData */
\r
968 if (!appData.ringBellAfterMoves) {
\r
969 sounds[(int)SoundMove].name = strdup("");
\r
970 appData.ringBellAfterMoves = TRUE;
\r
972 if (appData.debugMode) {
\r
973 debugFP = fopen(appData.nameOfDebugFile, "w");
\r
974 setbuf(debugFP, NULL);
\r
977 LoadLanguageFile(appData.language);
\r
981 // InitEngineUCI( installDir, &first ); // [HGM] incorporated in InitBackEnd1()
\r
982 // InitEngineUCI( installDir, &second );
\r
984 /* Create a main window for this application instance. */
\r
985 hwnd = CreateWindow(szAppName, szTitle,
\r
986 (WS_OVERLAPPEDWINDOW & ~WS_MAXIMIZEBOX),
\r
987 CW_USEDEFAULT, 0, CW_USEDEFAULT, 0,
\r
988 NULL, NULL, hInstance, NULL);
\r
991 /* If window could not be created, return "failure" */
\r
996 /* [HGM] logo: Load logos if specified (must be done before InitDrawingSizes) */
\r
997 if( appData.firstLogo && appData.firstLogo[0] != NULLCHAR) {
\r
998 first.programLogo = LoadImage( 0, appData.firstLogo, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );
\r
1000 if (first.programLogo == NULL && appData.debugMode) {
\r
1001 fprintf( debugFP, "Unable to load logo bitmap '%s'\n", appData.firstLogo );
\r
1003 } else if(appData.autoLogo) {
\r
1004 if(appData.firstDirectory && appData.firstDirectory[0]) {
\r
1005 char buf[MSG_SIZ];
\r
1006 snprintf(buf, MSG_SIZ, "%s/logo.bmp", appData.firstDirectory);
\r
1007 first.programLogo = LoadImage( 0, buf, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );
\r
1011 if( appData.secondLogo && appData.secondLogo[0] != NULLCHAR) {
\r
1012 second.programLogo = LoadImage( 0, appData.secondLogo, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );
\r
1014 if (second.programLogo == NULL && appData.debugMode) {
\r
1015 fprintf( debugFP, "Unable to load logo bitmap '%s'\n", appData.secondLogo );
\r
1017 } else if(appData.autoLogo) {
\r
1018 char buf[MSG_SIZ];
\r
1019 if(appData.icsActive) { // [HGM] logo: in ICS mode second can be used for ICS
\r
1020 snprintf(buf, MSG_SIZ, "logos\\%s.bmp", appData.icsHost);
\r
1021 second.programLogo = LoadImage( 0, buf, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );
\r
1023 if(appData.secondDirectory && appData.secondDirectory[0]) {
\r
1024 snprintf(buf, MSG_SIZ, "%s\\logo.bmp", appData.secondDirectory);
\r
1025 second.programLogo = LoadImage( 0, buf, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );
\r
1031 iconWhite = LoadIcon(hInstance, "icon_white");
\r
1032 iconBlack = LoadIcon(hInstance, "icon_black");
\r
1033 iconCurrent = iconWhite;
\r
1034 InitDrawingColors();
\r
1035 screenHeight = GetSystemMetrics(SM_CYSCREEN);
\r
1036 screenWidth = GetSystemMetrics(SM_CXSCREEN);
\r
1037 for (ibs = (int) NUM_SIZES - 1; ibs >= 0; ibs--) {
\r
1038 /* Compute window size for each board size, and use the largest
\r
1039 size that fits on this screen as the default. */
\r
1040 InitDrawingSizes((BoardSize)(ibs+1000), 0);
\r
1041 if (boardSize == (BoardSize)-1 &&
\r
1042 winH <= screenHeight
\r
1043 - GetSystemMetrics(SM_CYFRAME) - GetSystemMetrics(SM_CYCAPTION) - 10
\r
1044 && winW <= screenWidth) {
\r
1045 boardSize = (BoardSize)ibs;
\r
1049 InitDrawingSizes(boardSize, 0);
\r
1050 TranslateMenus(1);
\r
1052 buttonCount = GetSystemMetrics(SM_CMOUSEBUTTONS);
\r
1054 /* [AS] Load textures if specified */
\r
1055 ZeroMemory( &backTextureSquareInfo, sizeof(backTextureSquareInfo) );
\r
1057 if( appData.liteBackTextureFile && appData.liteBackTextureFile[0] != NULLCHAR && appData.liteBackTextureFile[0] != '*' ) {
\r
1058 liteBackTexture = LoadImage( 0, appData.liteBackTextureFile, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );
\r
1059 liteBackTextureMode = appData.liteBackTextureMode;
\r
1061 if (liteBackTexture == NULL && appData.debugMode) {
\r
1062 fprintf( debugFP, "Unable to load lite texture bitmap '%s'\n", appData.liteBackTextureFile );
\r
1066 if( appData.darkBackTextureFile && appData.darkBackTextureFile[0] != NULLCHAR && appData.darkBackTextureFile[0] != '*' ) {
\r
1067 darkBackTexture = LoadImage( 0, appData.darkBackTextureFile, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );
\r
1068 darkBackTextureMode = appData.darkBackTextureMode;
\r
1070 if (darkBackTexture == NULL && appData.debugMode) {
\r
1071 fprintf( debugFP, "Unable to load dark texture bitmap '%s'\n", appData.darkBackTextureFile );
\r
1075 mysrandom( (unsigned) time(NULL) );
\r
1077 /* [AS] Restore layout */
\r
1078 if( wpMoveHistory.visible ) {
\r
1079 MoveHistoryPopUp();
\r
1082 if( wpEvalGraph.visible ) {
\r
1086 if( wpEngineOutput.visible ) {
\r
1087 EngineOutputPopUp();
\r
1090 /* Make the window visible; update its client area; and return "success" */
\r
1091 EnsureOnScreen(&wpMain.x, &wpMain.y, minX, minY);
\r
1092 wp.length = sizeof(WINDOWPLACEMENT);
\r
1094 wp.showCmd = nCmdShow;
\r
1095 wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;
\r
1096 wp.rcNormalPosition.left = wpMain.x;
\r
1097 wp.rcNormalPosition.right = wpMain.x + wpMain.width;
\r
1098 wp.rcNormalPosition.top = wpMain.y;
\r
1099 wp.rcNormalPosition.bottom = wpMain.y + wpMain.height;
\r
1100 SetWindowPlacement(hwndMain, &wp);
\r
1102 InitBackEnd2(); // [HGM] moved until after all windows placed, to save correct position if fatal error on engine start
\r
1104 if(!appData.noGUI) SetWindowPos(hwndMain, alwaysOnTop ? HWND_TOPMOST : HWND_NOTOPMOST,
\r
1105 0, 0, 0, 0, SWP_NOMOVE|SWP_NOSIZE);
\r
1107 if (hwndConsole) {
\r
1109 SetWindowPos(hwndConsole, alwaysOnTop ? HWND_TOPMOST : HWND_NOTOPMOST,
\r
1110 0, 0, 0, 0, SWP_NOMOVE|SWP_NOSIZE);
\r
1112 ShowWindow(hwndConsole, nCmdShow);
\r
1113 if(appData.chatBoxes) { // [HGM] chat: open chat boxes
\r
1114 char buf[MSG_SIZ], *p = buf, *q;
\r
1115 safeStrCpy(buf, appData.chatBoxes, sizeof(buf)/sizeof(buf[0]) );
\r
1117 q = strchr(p, ';');
\r
1119 if(*p) ChatPopUp(p);
\r
1122 SetActiveWindow(hwndConsole);
\r
1124 if(!appData.noGUI) UpdateWindow(hwnd); else ShowWindow(hwnd, SW_MINIMIZE);
\r
1125 if(gameListDialog) SetFocus(gameListDialog); // [HGM] jaws: for if we clicked multi-game game file
\r
1134 HMENU hmenu = GetMenu(hwndMain);
\r
1136 (void) EnableMenuItem(hmenu, IDM_CommPort,
\r
1137 MF_BYCOMMAND|((appData.icsActive &&
\r
1138 *appData.icsCommPort != NULLCHAR) ?
\r
1139 MF_ENABLED : MF_GRAYED));
\r
1140 (void) CheckMenuItem(hmenu, IDM_SaveSettingsOnExit,
\r
1141 MF_BYCOMMAND|(saveSettingsOnExit ?
\r
1142 MF_CHECKED : MF_UNCHECKED));
\r
1145 //---------------------------------------------------------------------------------------------------------
\r
1147 #define ICS_TEXT_MENU_SIZE (IDM_CommandXLast - IDM_CommandX + 1)
\r
1148 #define XBOARD FALSE
\r
1150 #define OPTCHAR "/"
\r
1151 #define SEPCHAR "="
\r
1155 // front-end part of option handling
\r
1158 LFfromMFP(LOGFONT* lf, MyFontParams *mfp)
\r
1160 HDC hdc = CreateDC("DISPLAY", NULL, NULL, NULL);
\r
1161 lf->lfHeight = -(int)(mfp->pointSize * GetDeviceCaps(hdc, LOGPIXELSY) / 72.0 + 0.5);
\r
1164 lf->lfEscapement = 0;
\r
1165 lf->lfOrientation = 0;
\r
1166 lf->lfWeight = mfp->bold ? FW_BOLD : FW_NORMAL;
\r
1167 lf->lfItalic = mfp->italic;
\r
1168 lf->lfUnderline = mfp->underline;
\r
1169 lf->lfStrikeOut = mfp->strikeout;
\r
1170 lf->lfCharSet = mfp->charset;
\r
1171 lf->lfOutPrecision = OUT_DEFAULT_PRECIS;
\r
1172 lf->lfClipPrecision = CLIP_DEFAULT_PRECIS;
\r
1173 lf->lfQuality = DEFAULT_QUALITY;
\r
1174 lf->lfPitchAndFamily = DEFAULT_PITCH|FF_DONTCARE;
\r
1175 safeStrCpy(lf->lfFaceName, mfp->faceName, sizeof(lf->lfFaceName)/sizeof(lf->lfFaceName[0]) );
\r
1179 CreateFontInMF(MyFont *mf)
\r
1181 LFfromMFP(&mf->lf, &mf->mfp);
\r
1182 if (mf->hf) DeleteObject(mf->hf);
\r
1183 mf->hf = CreateFontIndirect(&mf->lf);
\r
1186 // [HGM] This platform-dependent table provides the location for storing the color info
\r
1188 colorVariable[] = {
\r
1189 &whitePieceColor,
\r
1190 &blackPieceColor,
\r
1191 &lightSquareColor,
\r
1192 &darkSquareColor,
\r
1193 &highlightSquareColor,
\r
1194 &premoveHighlightColor,
\r
1196 &consoleBackgroundColor,
\r
1197 &appData.fontForeColorWhite,
\r
1198 &appData.fontBackColorWhite,
\r
1199 &appData.fontForeColorBlack,
\r
1200 &appData.fontBackColorBlack,
\r
1201 &appData.evalHistColorWhite,
\r
1202 &appData.evalHistColorBlack,
\r
1203 &appData.highlightArrowColor,
\r
1206 /* Command line font name parser. NULL name means do nothing.
\r
1207 Syntax like "Courier New:10.0 bi" or "Arial:10" or "Arial:10b"
\r
1208 For backward compatibility, syntax without the colon is also
\r
1209 accepted, but font names with digits in them won't work in that case.
\r
1212 ParseFontName(char *name, MyFontParams *mfp)
\r
1215 if (name == NULL) return;
\r
1217 q = strchr(p, ':');
\r
1219 if (q - p >= sizeof(mfp->faceName))
\r
1220 ExitArgError(_("Font name too long:"), name);
\r
1221 memcpy(mfp->faceName, p, q - p);
\r
1222 mfp->faceName[q - p] = NULLCHAR;
\r
1225 q = mfp->faceName;
\r
1226 while (*p && !isdigit(*p)) {
\r
1228 if (q - mfp->faceName >= sizeof(mfp->faceName))
\r
1229 ExitArgError(_("Font name too long:"), name);
\r
1231 while (q > mfp->faceName && q[-1] == ' ') q--;
\r
1234 if (!*p) ExitArgError(_("Font point size missing:"), name);
\r
1235 mfp->pointSize = (float) atof(p);
\r
1236 mfp->bold = (strchr(p, 'b') != NULL);
\r
1237 mfp->italic = (strchr(p, 'i') != NULL);
\r
1238 mfp->underline = (strchr(p, 'u') != NULL);
\r
1239 mfp->strikeout = (strchr(p, 's') != NULL);
\r
1240 mfp->charset = DEFAULT_CHARSET;
\r
1241 q = strchr(p, 'c');
\r
1243 mfp->charset = (BYTE) atoi(q+1);
\r
1247 ParseFont(char *name, int number)
\r
1248 { // wrapper to shield back-end from 'font'
\r
1249 ParseFontName(name, &font[boardSize][number]->mfp);
\r
1254 { // in WB we have a 2D array of fonts; this initializes their description
\r
1256 /* Point font array elements to structures and
\r
1257 parse default font names */
\r
1258 for (i=0; i<NUM_FONTS; i++) {
\r
1259 for (j=0; j<NUM_SIZES; j++) {
\r
1260 font[j][i] = &fontRec[j][i];
\r
1261 ParseFontName(font[j][i]->def, &font[j][i]->mfp);
\r
1268 { // here we create the actual fonts from the selected descriptions
\r
1270 for (i=0; i<NUM_FONTS; i++) {
\r
1271 for (j=0; j<NUM_SIZES; j++) {
\r
1272 CreateFontInMF(font[j][i]);
\r
1276 /* Color name parser.
\r
1277 X version accepts X color names, but this one
\r
1278 handles only the #rrggbb form (hex) or rrr,ggg,bbb (decimal) */
\r
1280 ParseColorName(char *name)
\r
1282 int red, green, blue, count;
\r
1283 char buf[MSG_SIZ];
\r
1285 count = sscanf(name, "#%2x%2x%2x", &red, &green, &blue);
\r
1287 count = sscanf(name, "%3d%*[^0-9]%3d%*[^0-9]%3d",
\r
1288 &red, &green, &blue);
\r
1291 snprintf(buf, MSG_SIZ, _("Can't parse color name %s"), name);
\r
1292 DisplayError(buf, 0);
\r
1293 return RGB(0, 0, 0);
\r
1295 return PALETTERGB(red, green, blue);
\r
1299 ParseColor(int n, char *name)
\r
1300 { // for WinBoard the color is an int, which needs to be derived from the string
\r
1301 if(colorVariable[n]) *(int*)colorVariable[n] = ParseColorName(name);
\r
1305 ParseAttribs(COLORREF *color, int *effects, char* argValue)
\r
1307 char *e = argValue;
\r
1311 if (*e == 'b') eff |= CFE_BOLD;
\r
1312 else if (*e == 'i') eff |= CFE_ITALIC;
\r
1313 else if (*e == 'u') eff |= CFE_UNDERLINE;
\r
1314 else if (*e == 's') eff |= CFE_STRIKEOUT;
\r
1315 else if (*e == '#' || isdigit(*e)) break;
\r
1319 *color = ParseColorName(e);
\r
1323 ParseTextAttribs(ColorClass cc, char *s)
\r
1324 { // [HGM] front-end wrapper that does the platform-dependent call
\r
1325 // for XBoard we would set (&appData.colorShout)[cc] = strdup(s);
\r
1326 ParseAttribs(&textAttribs[cc].color, &textAttribs[cc].effects, s);
\r
1330 ParseBoardSize(void *addr, char *name)
\r
1331 { // [HGM] rewritten with return-value ptr to shield back-end from BoardSize
\r
1332 BoardSize bs = SizeTiny;
\r
1333 while (sizeInfo[bs].name != NULL) {
\r
1334 if (StrCaseCmp(name, sizeInfo[bs].name) == 0) {
\r
1335 *(BoardSize *)addr = bs;
\r
1340 ExitArgError(_("Unrecognized board size value"), name);
\r
1345 { // [HGM] import name from appData first
\r
1348 for (cc = (ColorClass)0; cc < ColorNormal; cc++) {
\r
1349 textAttribs[cc].sound.name = strdup((&appData.soundShout)[cc]);
\r
1350 textAttribs[cc].sound.data = NULL;
\r
1351 MyLoadSound(&textAttribs[cc].sound);
\r
1353 for (cc = ColorNormal; cc < NColorClasses; cc++) {
\r
1354 textAttribs[cc].sound.name = strdup("");
\r
1355 textAttribs[cc].sound.data = NULL;
\r
1357 for (sc = (SoundClass)0; sc < NSoundClasses; sc++) {
\r
1358 sounds[sc].name = strdup((&appData.soundMove)[sc]);
\r
1359 sounds[sc].data = NULL;
\r
1360 MyLoadSound(&sounds[sc]);
\r
1365 SetCommPortDefaults()
\r
1367 memset(&dcb, 0, sizeof(DCB)); // required by VS 2002 +
\r
1368 dcb.DCBlength = sizeof(DCB);
\r
1369 dcb.BaudRate = 9600;
\r
1370 dcb.fBinary = TRUE;
\r
1371 dcb.fParity = FALSE;
\r
1372 dcb.fOutxCtsFlow = FALSE;
\r
1373 dcb.fOutxDsrFlow = FALSE;
\r
1374 dcb.fDtrControl = DTR_CONTROL_ENABLE;
\r
1375 dcb.fDsrSensitivity = FALSE;
\r
1376 dcb.fTXContinueOnXoff = TRUE;
\r
1377 dcb.fOutX = FALSE;
\r
1379 dcb.fNull = FALSE;
\r
1380 dcb.fRtsControl = RTS_CONTROL_ENABLE;
\r
1381 dcb.fAbortOnError = FALSE;
\r
1383 dcb.Parity = SPACEPARITY;
\r
1384 dcb.StopBits = ONESTOPBIT;
\r
1387 // [HGM] args: these three cases taken out to stay in front-end
\r
1389 SaveFontArg(FILE *f, ArgDescriptor *ad)
\r
1390 { // in WinBoard every board size has its own font, and the "argLoc" identifies the table,
\r
1391 // while the curent board size determines the element. This system should be ported to XBoard.
\r
1392 // What the table contains pointers to, and how to print the font description, remains platform-dependent
\r
1394 for (bs=0; bs<NUM_SIZES; bs++) {
\r
1395 MyFontParams *mfp = &font[bs][(int) ad->argLoc]->mfp;
\r
1396 fprintf(f, "/size=%s ", sizeInfo[bs].name);
\r
1397 fprintf(f, "/%s=\"%s:%g%s%s%s%s%sc%d\"\n",
\r
1398 ad->argName, mfp->faceName, mfp->pointSize,
\r
1399 mfp->bold || mfp->italic || mfp->underline || mfp->strikeout ? " " : "",
\r
1400 mfp->bold ? "b" : "",
\r
1401 mfp->italic ? "i" : "",
\r
1402 mfp->underline ? "u" : "",
\r
1403 mfp->strikeout ? "s" : "",
\r
1404 (int)mfp->charset);
\r
1410 { // [HGM] copy the names from the internal WB variables to appData
\r
1413 for (cc = (ColorClass)0; cc < ColorNormal; cc++)
\r
1414 (&appData.soundShout)[cc] = textAttribs[cc].sound.name;
\r
1415 for (sc = (SoundClass)0; sc < NSoundClasses; sc++)
\r
1416 (&appData.soundMove)[sc] = sounds[sc].name;
\r
1420 SaveAttribsArg(FILE *f, ArgDescriptor *ad)
\r
1421 { // here the "argLoc" defines a table index. It could have contained the 'ta' pointer itself, though
\r
1422 MyTextAttribs* ta = &textAttribs[(ColorClass)ad->argLoc];
\r
1423 fprintf(f, "/%s=\"%s%s%s%s%s#%02lx%02lx%02lx\"\n", ad->argName,
\r
1424 (ta->effects & CFE_BOLD) ? "b" : "",
\r
1425 (ta->effects & CFE_ITALIC) ? "i" : "",
\r
1426 (ta->effects & CFE_UNDERLINE) ? "u" : "",
\r
1427 (ta->effects & CFE_STRIKEOUT) ? "s" : "",
\r
1428 (ta->effects) ? " " : "",
\r
1429 ta->color&0xff, (ta->color >> 8)&0xff, (ta->color >> 16)&0xff);
\r
1433 SaveColor(FILE *f, ArgDescriptor *ad)
\r
1434 { // in WinBoard the color is an int and has to be converted to text. In X it would be a string already?
\r
1435 COLORREF color = *(COLORREF *)colorVariable[(int)ad->argLoc];
\r
1436 fprintf(f, "/%s=#%02lx%02lx%02lx\n", ad->argName,
\r
1437 color&0xff, (color>>8)&0xff, (color>>16)&0xff);
\r
1441 SaveBoardSize(FILE *f, char *name, void *addr)
\r
1442 { // wrapper to shield back-end from BoardSize & sizeInfo
\r
1443 fprintf(f, "/%s=%s\n", name, sizeInfo[*(BoardSize *)addr].name);
\r
1447 ParseCommPortSettings(char *s)
\r
1448 { // wrapper to keep dcb from back-end
\r
1449 ParseCommSettings(s, &dcb);
\r
1454 { // wrapper to shield use of window handles from back-end (make addressible by number?)
\r
1455 GetActualPlacement(hwndMain, &wpMain);
\r
1456 GetActualPlacement(hwndConsole, &wpConsole);
\r
1457 GetActualPlacement(commentDialog, &wpComment);
\r
1458 GetActualPlacement(editTagsDialog, &wpTags);
\r
1459 GetActualPlacement(gameListDialog, &wpGameList);
\r
1460 GetActualPlacement(moveHistoryDialog, &wpMoveHistory);
\r
1461 GetActualPlacement(evalGraphDialog, &wpEvalGraph);
\r
1462 GetActualPlacement(engineOutputDialog, &wpEngineOutput);
\r
1466 PrintCommPortSettings(FILE *f, char *name)
\r
1467 { // wrapper to shield back-end from DCB
\r
1468 PrintCommSettings(f, name, &dcb);
\r
1472 MySearchPath(char *installDir, char *name, char *fullname)
\r
1474 char *dummy, buf[MSG_SIZ], *p = name, *q;
\r
1475 if(name[0]== '%') {
\r
1476 fullname[0] = 0; // [HGM] first expand any environment variables in the given name
\r
1477 while(*p == '%' && (q = strchr(p+1, '%'))) { // [HGM] recognize %*% as environment variable
\r
1478 safeStrCpy(buf, p+1, sizeof(buf)/sizeof(buf[0]) );
\r
1479 *strchr(buf, '%') = 0;
\r
1480 strcat(fullname, getenv(buf));
\r
1481 p = q+1; while(*p == '\\') { strcat(fullname, "\\"); p++; }
\r
1483 strcat(fullname, p); // after environment variables (if any), take the remainder of the given name
\r
1484 if(appData.debugMode) fprintf(debugFP, "name = '%s', expanded name = '%s'\n", name, fullname);
\r
1485 return (int) strlen(fullname);
\r
1487 return (int) SearchPath(installDir, name, NULL, MSG_SIZ, fullname, &dummy);
\r
1491 MyGetFullPathName(char *name, char *fullname)
\r
1494 return (int) GetFullPathName(name, MSG_SIZ, fullname, &dummy);
\r
1499 { // [HGM] args: allows testing if main window is realized from back-end
\r
1500 return hwndMain != NULL;
\r
1504 PopUpStartupDialog()
\r
1508 LoadLanguageFile(appData.language);
\r
1509 lpProc = MakeProcInstance((FARPROC)StartupDialog, hInst);
\r
1510 DialogBox(hInst, MAKEINTRESOURCE(DLG_Startup), NULL, (DLGPROC)lpProc);
\r
1511 FreeProcInstance(lpProc);
\r
1514 /*---------------------------------------------------------------------------*\
\r
1516 * GDI board drawing routines
\r
1518 \*---------------------------------------------------------------------------*/
\r
1520 /* [AS] Draw square using background texture */
\r
1521 static void DrawTile( int dx, int dy, int dw, int dh, HDC dst, HDC src, int mode, int sx, int sy )
\r
1526 return; /* Should never happen! */
\r
1529 SetGraphicsMode( dst, GM_ADVANCED );
\r
1536 /* X reflection */
\r
1541 x.eDx = (FLOAT) dw + dx - 1;
\r
1544 SetWorldTransform( dst, &x );
\r
1547 /* Y reflection */
\r
1553 x.eDy = (FLOAT) dh + dy - 1;
\r
1555 SetWorldTransform( dst, &x );
\r
1563 x.eDx = (FLOAT) dx;
\r
1564 x.eDy = (FLOAT) dy;
\r
1567 SetWorldTransform( dst, &x );
\r
1571 BitBlt( dst, dx, dy, dw, dh, src, sx, sy, SRCCOPY );
\r
1579 SetWorldTransform( dst, &x );
\r
1581 ModifyWorldTransform( dst, 0, MWT_IDENTITY );
\r
1584 /* [AS] [HGM] Make room for more piece types, so all pieces can be different */
\r
1586 PM_WP = (int) WhitePawn,
\r
1587 PM_WN = (int) WhiteKnight,
\r
1588 PM_WB = (int) WhiteBishop,
\r
1589 PM_WR = (int) WhiteRook,
\r
1590 PM_WQ = (int) WhiteQueen,
\r
1591 PM_WF = (int) WhiteFerz,
\r
1592 PM_WW = (int) WhiteWazir,
\r
1593 PM_WE = (int) WhiteAlfil,
\r
1594 PM_WM = (int) WhiteMan,
\r
1595 PM_WO = (int) WhiteCannon,
\r
1596 PM_WU = (int) WhiteUnicorn,
\r
1597 PM_WH = (int) WhiteNightrider,
\r
1598 PM_WA = (int) WhiteAngel,
\r
1599 PM_WC = (int) WhiteMarshall,
\r
1600 PM_WAB = (int) WhiteCardinal,
\r
1601 PM_WD = (int) WhiteDragon,
\r
1602 PM_WL = (int) WhiteLance,
\r
1603 PM_WS = (int) WhiteCobra,
\r
1604 PM_WV = (int) WhiteFalcon,
\r
1605 PM_WSG = (int) WhiteSilver,
\r
1606 PM_WG = (int) WhiteGrasshopper,
\r
1607 PM_WK = (int) WhiteKing,
\r
1608 PM_BP = (int) BlackPawn,
\r
1609 PM_BN = (int) BlackKnight,
\r
1610 PM_BB = (int) BlackBishop,
\r
1611 PM_BR = (int) BlackRook,
\r
1612 PM_BQ = (int) BlackQueen,
\r
1613 PM_BF = (int) BlackFerz,
\r
1614 PM_BW = (int) BlackWazir,
\r
1615 PM_BE = (int) BlackAlfil,
\r
1616 PM_BM = (int) BlackMan,
\r
1617 PM_BO = (int) BlackCannon,
\r
1618 PM_BU = (int) BlackUnicorn,
\r
1619 PM_BH = (int) BlackNightrider,
\r
1620 PM_BA = (int) BlackAngel,
\r
1621 PM_BC = (int) BlackMarshall,
\r
1622 PM_BG = (int) BlackGrasshopper,
\r
1623 PM_BAB = (int) BlackCardinal,
\r
1624 PM_BD = (int) BlackDragon,
\r
1625 PM_BL = (int) BlackLance,
\r
1626 PM_BS = (int) BlackCobra,
\r
1627 PM_BV = (int) BlackFalcon,
\r
1628 PM_BSG = (int) BlackSilver,
\r
1629 PM_BK = (int) BlackKing
\r
1632 static HFONT hPieceFont = NULL;
\r
1633 static HBITMAP hPieceMask[(int) EmptySquare];
\r
1634 static HBITMAP hPieceFace[(int) EmptySquare];
\r
1635 static int fontBitmapSquareSize = 0;
\r
1636 static char pieceToFontChar[(int) EmptySquare] =
\r
1637 { 'p', 'n', 'b', 'r', 'q',
\r
1638 'n', 'b', 'p', 'n', 'b', 'r', 'b', 'r', 'q', 'k',
\r
1639 'k', 'o', 'm', 'v', 't', 'w',
\r
1640 'v', 't', 'o', 'm', 'v', 't', 'v', 't', 'w', 'l',
\r
1643 extern BOOL SetCharTable( char *table, const char * map );
\r
1644 /* [HGM] moved to backend.c */
\r
1646 static void SetPieceBackground( HDC hdc, COLORREF color, int mode )
\r
1649 BYTE r1 = GetRValue( color );
\r
1650 BYTE g1 = GetGValue( color );
\r
1651 BYTE b1 = GetBValue( color );
\r
1657 /* Create a uniform background first */
\r
1658 hbrush = CreateSolidBrush( color );
\r
1659 SetRect( &rc, 0, 0, squareSize, squareSize );
\r
1660 FillRect( hdc, &rc, hbrush );
\r
1661 DeleteObject( hbrush );
\r
1664 /* Vertical gradient, good for pawn, knight and rook, less for queen and king */
\r
1665 int steps = squareSize / 2;
\r
1668 for( i=0; i<steps; i++ ) {
\r
1669 BYTE r = r1 - (r1-r2) * i / steps;
\r
1670 BYTE g = g1 - (g1-g2) * i / steps;
\r
1671 BYTE b = b1 - (b1-b2) * i / steps;
\r
1673 hbrush = CreateSolidBrush( RGB(r,g,b) );
\r
1674 SetRect( &rc, i + squareSize - steps, 0, i + squareSize - steps + 1, squareSize );
\r
1675 FillRect( hdc, &rc, hbrush );
\r
1676 DeleteObject(hbrush);
\r
1679 else if( mode == 2 ) {
\r
1680 /* Diagonal gradient, good more or less for every piece */
\r
1681 POINT triangle[3];
\r
1682 HPEN hpen = SelectObject( hdc, GetStockObject(NULL_PEN) );
\r
1683 HBRUSH hbrush_old;
\r
1684 int steps = squareSize;
\r
1687 triangle[0].x = squareSize - steps;
\r
1688 triangle[0].y = squareSize;
\r
1689 triangle[1].x = squareSize;
\r
1690 triangle[1].y = squareSize;
\r
1691 triangle[2].x = squareSize;
\r
1692 triangle[2].y = squareSize - steps;
\r
1694 for( i=0; i<steps; i++ ) {
\r
1695 BYTE r = r1 - (r1-r2) * i / steps;
\r
1696 BYTE g = g1 - (g1-g2) * i / steps;
\r
1697 BYTE b = b1 - (b1-b2) * i / steps;
\r
1699 hbrush = CreateSolidBrush( RGB(r,g,b) );
\r
1700 hbrush_old = SelectObject( hdc, hbrush );
\r
1701 Polygon( hdc, triangle, 3 );
\r
1702 SelectObject( hdc, hbrush_old );
\r
1703 DeleteObject(hbrush);
\r
1708 SelectObject( hdc, hpen );
\r
1713 [AS] The method I use to create the bitmaps it a bit tricky, but it
\r
1714 seems to work ok. The main problem here is to find the "inside" of a chess
\r
1715 piece: follow the steps as explained below.
\r
1717 static void CreatePieceMaskFromFont( HDC hdc_window, HDC hdc, int index )
\r
1721 COLORREF chroma = RGB(0xFF,0x00,0xFF);
\r
1725 int backColor = whitePieceColor;
\r
1726 int foreColor = blackPieceColor;
\r
1728 if( index < (int)BlackPawn && appData.fontBackColorWhite != appData.fontForeColorWhite ) {
\r
1729 backColor = appData.fontBackColorWhite;
\r
1730 foreColor = appData.fontForeColorWhite;
\r
1732 else if( index >= (int)BlackPawn && appData.fontBackColorBlack != appData.fontForeColorBlack ) {
\r
1733 backColor = appData.fontBackColorBlack;
\r
1734 foreColor = appData.fontForeColorBlack;
\r
1738 hbm = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );
\r
1740 hbm_old = SelectObject( hdc, hbm );
\r
1744 rc.right = squareSize;
\r
1745 rc.bottom = squareSize;
\r
1747 /* Step 1: background is now black */
\r
1748 FillRect( hdc, &rc, GetStockObject(BLACK_BRUSH) );
\r
1750 GetTextExtentPoint32( hdc, &pieceToFontChar[index], 1, &sz );
\r
1752 pt.x = (squareSize - sz.cx) / 2;
\r
1753 pt.y = (squareSize - sz.cy) / 2;
\r
1755 SetBkMode( hdc, TRANSPARENT );
\r
1756 SetTextColor( hdc, chroma );
\r
1757 /* Step 2: the piece has been drawn in purple, there are now black and purple in this bitmap */
\r
1758 TextOut( hdc, pt.x, pt.y, &pieceToFontChar[appData.allWhite && index >= (int)BlackPawn ? index - (int)BlackPawn : index], 1 );
\r
1760 SelectObject( hdc, GetStockObject(WHITE_BRUSH) );
\r
1761 /* Step 3: the area outside the piece is filled with white */
\r
1762 // FloodFill( hdc, 0, 0, chroma );
\r
1763 ExtFloodFill( hdc, 0, 0, 0, FLOODFILLSURFACE );
\r
1764 ExtFloodFill( hdc, 0, squareSize-1, 0, FLOODFILLSURFACE ); // [HGM] fill from all 4 corners, for if piece too big
\r
1765 ExtFloodFill( hdc, squareSize-1, 0, 0, FLOODFILLSURFACE );
\r
1766 ExtFloodFill( hdc, squareSize-1, squareSize-1, 0, FLOODFILLSURFACE );
\r
1767 SelectObject( hdc, GetStockObject(BLACK_BRUSH) );
\r
1769 Step 4: this is the tricky part, the area inside the piece is filled with black,
\r
1770 but if the start point is not inside the piece we're lost!
\r
1771 There should be a better way to do this... if we could create a region or path
\r
1772 from the fill operation we would be fine for example.
\r
1774 // FloodFill( hdc, squareSize / 2, squareSize / 2, RGB(0xFF,0xFF,0xFF) );
\r
1775 ExtFloodFill( hdc, squareSize / 2, squareSize / 2, RGB(0xFF,0xFF,0xFF), FLOODFILLBORDER );
\r
1777 { /* [HGM] shave off edges of mask, in an attempt to correct for the fact that FloodFill does not work correctly under Win XP */
\r
1778 HDC dc2 = CreateCompatibleDC( hdc_window );
\r
1779 HBITMAP bm2 = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );
\r
1781 SelectObject( dc2, bm2 );
\r
1782 BitBlt( dc2, 0, 0, squareSize, squareSize, hdc, 0, 0, SRCCOPY ); // make copy
\r
1783 BitBlt( hdc, 0, 1, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );
\r
1784 BitBlt( hdc, 2, 1, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );
\r
1785 BitBlt( hdc, 1, 0, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );
\r
1786 BitBlt( hdc, 1, 2, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );
\r
1789 DeleteObject( bm2 );
\r
1792 SetTextColor( hdc, 0 );
\r
1794 Step 5: some fonts have "disconnected" areas that are skipped by the fill:
\r
1795 draw the piece again in black for safety.
\r
1797 TextOut( hdc, pt.x, pt.y, &pieceToFontChar[appData.allWhite && index >= (int)BlackPawn ? index - (int)BlackPawn : index], 1 );
\r
1799 SelectObject( hdc, hbm_old );
\r
1801 if( hPieceMask[index] != NULL ) {
\r
1802 DeleteObject( hPieceMask[index] );
\r
1805 hPieceMask[index] = hbm;
\r
1808 hbm = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );
\r
1810 SelectObject( hdc, hbm );
\r
1813 HDC dc1 = CreateCompatibleDC( hdc_window );
\r
1814 HDC dc2 = CreateCompatibleDC( hdc_window );
\r
1815 HBITMAP bm2 = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );
\r
1817 SelectObject( dc1, hPieceMask[index] );
\r
1818 SelectObject( dc2, bm2 );
\r
1819 FillRect( dc2, &rc, GetStockObject(WHITE_BRUSH) );
\r
1820 BitBlt( dc2, 0, 0, squareSize, squareSize, dc1, 0, 0, SRCINVERT );
\r
1823 Now dc2 contains the inverse of the piece mask, i.e. a mask that preserves
\r
1824 the piece background and deletes (makes transparent) the rest.
\r
1825 Thanks to that mask, we are free to paint the background with the greates
\r
1826 freedom, as we'll be able to mask off the unwanted parts when finished.
\r
1827 We use this, to make gradients and give the pieces a "roundish" look.
\r
1829 SetPieceBackground( hdc, backColor, 2 );
\r
1830 BitBlt( hdc, 0, 0, squareSize, squareSize, dc2, 0, 0, SRCAND );
\r
1834 DeleteObject( bm2 );
\r
1837 SetTextColor( hdc, foreColor );
\r
1838 TextOut( hdc, pt.x, pt.y, &pieceToFontChar[appData.allWhite && index >= (int)BlackPawn ? index - (int)BlackPawn : index], 1 );
\r
1840 SelectObject( hdc, hbm_old );
\r
1842 if( hPieceFace[index] != NULL ) {
\r
1843 DeleteObject( hPieceFace[index] );
\r
1846 hPieceFace[index] = hbm;
\r
1849 static int TranslatePieceToFontPiece( int piece )
\r
1879 case BlackMarshall:
\r
1883 case BlackNightrider:
\r
1889 case BlackUnicorn:
\r
1893 case BlackGrasshopper:
\r
1905 case BlackCardinal:
\r
1912 case WhiteMarshall:
\r
1916 case WhiteNightrider:
\r
1922 case WhiteUnicorn:
\r
1926 case WhiteGrasshopper:
\r
1938 case WhiteCardinal:
\r
1947 void CreatePiecesFromFont()
\r
1950 HDC hdc_window = NULL;
\r
1956 if( fontBitmapSquareSize < 0 ) {
\r
1957 /* Something went seriously wrong in the past: do not try to recreate fonts! */
\r
1961 if( appData.renderPiecesWithFont == NULL || appData.renderPiecesWithFont[0] == NULLCHAR || appData.renderPiecesWithFont[0] == '*' ) {
\r
1962 fontBitmapSquareSize = -1;
\r
1966 if( fontBitmapSquareSize != squareSize ) {
\r
1967 hdc_window = GetDC( hwndMain );
\r
1968 hdc = CreateCompatibleDC( hdc_window );
\r
1970 if( hPieceFont != NULL ) {
\r
1971 DeleteObject( hPieceFont );
\r
1974 for( i=0; i<=(int)BlackKing; i++ ) {
\r
1975 hPieceMask[i] = NULL;
\r
1976 hPieceFace[i] = NULL;
\r
1982 if( appData.fontPieceSize >= 50 && appData.fontPieceSize <= 150 ) {
\r
1983 fontHeight = appData.fontPieceSize;
\r
1986 fontHeight = (fontHeight * squareSize) / 100;
\r
1988 lf.lfHeight = -MulDiv( fontHeight, GetDeviceCaps(hdc, LOGPIXELSY), 72 );
\r
1990 lf.lfEscapement = 0;
\r
1991 lf.lfOrientation = 0;
\r
1992 lf.lfWeight = FW_NORMAL;
\r
1994 lf.lfUnderline = 0;
\r
1995 lf.lfStrikeOut = 0;
\r
1996 lf.lfCharSet = DEFAULT_CHARSET;
\r
1997 lf.lfOutPrecision = OUT_DEFAULT_PRECIS;
\r
1998 lf.lfClipPrecision = CLIP_DEFAULT_PRECIS;
\r
1999 lf.lfQuality = PROOF_QUALITY;
\r
2000 lf.lfPitchAndFamily = DEFAULT_PITCH | FF_DONTCARE;
\r
2001 strncpy( lf.lfFaceName, appData.renderPiecesWithFont, sizeof(lf.lfFaceName) );
\r
2002 lf.lfFaceName[ sizeof(lf.lfFaceName) - 1 ] = '\0';
\r
2004 hPieceFont = CreateFontIndirect( &lf );
\r
2006 if( hPieceFont == NULL ) {
\r
2007 fontBitmapSquareSize = -2;
\r
2010 /* Setup font-to-piece character table */
\r
2011 if( ! SetCharTable(pieceToFontChar, appData.fontToPieceTable) ) {
\r
2012 /* No (or wrong) global settings, try to detect the font */
\r
2013 if( strstr(lf.lfFaceName,"Alpha") != NULL ) {
\r
2015 SetCharTable(pieceToFontChar, "phbrqkojntwl");
\r
2017 else if( strstr(lf.lfFaceName,"DiagramTT") != NULL ) {
\r
2018 /* DiagramTT* family */
\r
2019 SetCharTable(pieceToFontChar, "PNLRQKpnlrqk");
\r
2021 else if( strstr(lf.lfFaceName,"WinboardF") != NULL ) {
\r
2022 /* Fairy symbols */
\r
2023 SetCharTable(pieceToFontChar, "PNBRQFEACWMOHIJGDVSLUKpnbrqfeacwmohijgdvsluk");
\r
2025 else if( strstr(lf.lfFaceName,"GC2004D") != NULL ) {
\r
2026 /* Good Companion (Some characters get warped as literal :-( */
\r
2027 char s[] = "1cmWG0??S??oYI23wgQU";
\r
2028 s[0]=0xB9; s[1]=0xA9; s[6]=0xB1; s[11]=0xBB; s[12]=0xAB; s[17]=0xB3;
\r
2029 SetCharTable(pieceToFontChar, s);
\r
2032 /* Cases, Condal, Leipzig, Lucena, Marroquin, Merida, Usual */
\r
2033 SetCharTable(pieceToFontChar, "pnbrqkomvtwl");
\r
2037 /* Create bitmaps */
\r
2038 hfont_old = SelectObject( hdc, hPieceFont );
\r
2039 for(i=(int)WhitePawn; i<(int)EmptySquare; i++) /* [HGM] made a loop for this */
\r
2040 if(PieceToChar((ChessSquare)i) != '.') /* skip unused pieces */
\r
2041 CreatePieceMaskFromFont( hdc_window, hdc, i );
\r
2043 SelectObject( hdc, hfont_old );
\r
2045 fontBitmapSquareSize = squareSize;
\r
2049 if( hdc != NULL ) {
\r
2053 if( hdc_window != NULL ) {
\r
2054 ReleaseDC( hwndMain, hdc_window );
\r
2059 DoLoadBitmap(HINSTANCE hinst, char *piece, int squareSize, char *suffix)
\r
2063 snprintf(name, sizeof(name)/sizeof(name[0]), "%s%d%s", piece, squareSize, suffix);
\r
2064 if (gameInfo.event &&
\r
2065 strcmp(gameInfo.event, "Easter Egg Hunt") == 0 &&
\r
2066 strcmp(name, "k80s") == 0) {
\r
2067 safeStrCpy(name, "tim", sizeof(name)/sizeof(name[0]) );
\r
2069 return LoadBitmap(hinst, name);
\r
2073 /* Insert a color into the program's logical palette
\r
2074 structure. This code assumes the given color is
\r
2075 the result of the RGB or PALETTERGB macro, and it
\r
2076 knows how those macros work (which is documented).
\r
2079 InsertInPalette(COLORREF color)
\r
2081 LPPALETTEENTRY pe = &(pLogPal->palPalEntry[pLogPal->palNumEntries]);
\r
2083 if (pLogPal->palNumEntries++ >= PALETTESIZE) {
\r
2084 DisplayFatalError(_("Too many colors"), 0, 1);
\r
2085 pLogPal->palNumEntries--;
\r
2089 pe->peFlags = (char) 0;
\r
2090 pe->peRed = (char) (0xFF & color);
\r
2091 pe->peGreen = (char) (0xFF & (color >> 8));
\r
2092 pe->peBlue = (char) (0xFF & (color >> 16));
\r
2098 InitDrawingColors()
\r
2100 if (pLogPal == NULL) {
\r
2101 /* Allocate enough memory for a logical palette with
\r
2102 * PALETTESIZE entries and set the size and version fields
\r
2103 * of the logical palette structure.
\r
2105 pLogPal = (NPLOGPALETTE)
\r
2106 LocalAlloc(LMEM_FIXED, (sizeof(LOGPALETTE) +
\r
2107 (sizeof(PALETTEENTRY) * (PALETTESIZE))));
\r
2108 pLogPal->palVersion = 0x300;
\r
2110 pLogPal->palNumEntries = 0;
\r
2112 InsertInPalette(lightSquareColor);
\r
2113 InsertInPalette(darkSquareColor);
\r
2114 InsertInPalette(whitePieceColor);
\r
2115 InsertInPalette(blackPieceColor);
\r
2116 InsertInPalette(highlightSquareColor);
\r
2117 InsertInPalette(premoveHighlightColor);
\r
2119 /* create a logical color palette according the information
\r
2120 * in the LOGPALETTE structure.
\r
2122 hPal = CreatePalette((LPLOGPALETTE) pLogPal);
\r
2124 lightSquareBrush = CreateSolidBrush(lightSquareColor);
\r
2125 blackSquareBrush = CreateSolidBrush(blackPieceColor);
\r
2126 darkSquareBrush = CreateSolidBrush(darkSquareColor);
\r
2127 whitePieceBrush = CreateSolidBrush(whitePieceColor);
\r
2128 blackPieceBrush = CreateSolidBrush(blackPieceColor);
\r
2129 iconBkgndBrush = CreateSolidBrush(GetSysColor(COLOR_BACKGROUND));
\r
2130 explodeBrush = CreateSolidBrush(highlightSquareColor); // [HGM] atomic
\r
2131 markerBrush = CreateSolidBrush(premoveHighlightColor); // [HGM] markers
\r
2132 /* [AS] Force rendering of the font-based pieces */
\r
2133 if( fontBitmapSquareSize > 0 ) {
\r
2134 fontBitmapSquareSize = 0;
\r
2140 BoardWidth(int boardSize, int n)
\r
2141 { /* [HGM] argument n added to allow different width and height */
\r
2142 int lineGap = sizeInfo[boardSize].lineGap;
\r
2144 if( appData.overrideLineGap >= 0 && appData.overrideLineGap <= 5 ) {
\r
2145 lineGap = appData.overrideLineGap;
\r
2148 return (n + 1) * lineGap +
\r
2149 n * sizeInfo[boardSize].squareSize;
\r
2152 /* Respond to board resize by dragging edge */
\r
2154 ResizeBoard(int newSizeX, int newSizeY, int flags)
\r
2156 BoardSize newSize = NUM_SIZES - 1;
\r
2157 static int recurse = 0;
\r
2158 if (IsIconic(hwndMain)) return;
\r
2159 if (recurse > 0) return;
\r
2161 while (newSize > 0) {
\r
2162 InitDrawingSizes(newSize+1000, 0); // [HGM] kludge to update sizeInfo without visible effects
\r
2163 if(newSizeX >= sizeInfo[newSize].cliWidth &&
\r
2164 newSizeY >= sizeInfo[newSize].cliHeight) break;
\r
2167 boardSize = newSize;
\r
2168 InitDrawingSizes(boardSize, flags);
\r
2173 extern Boolean twoBoards, partnerUp; // [HGM] dual
\r
2176 InitDrawingSizes(BoardSize boardSize, int flags)
\r
2178 int i, boardWidth, boardHeight; /* [HGM] height treated separately */
\r
2179 ChessSquare piece;
\r
2180 static int oldBoardSize = -1, oldTinyLayout = 0;
\r
2182 SIZE clockSize, messageSize;
\r
2184 char buf[MSG_SIZ];
\r
2186 HMENU hmenu = GetMenu(hwndMain);
\r
2187 RECT crect, wrect, oldRect;
\r
2189 LOGBRUSH logbrush;
\r
2191 int suppressVisibleEffects = 0; // [HGM] kludge to request updating sizeInfo only
\r
2192 if((int)boardSize >= 1000 ) { boardSize -= 1000; suppressVisibleEffects = 1; }
\r
2194 /* [HGM] call with -2 uses old size (for if nr of files, ranks changes) */
\r
2195 if(boardSize == (BoardSize)(-2) ) boardSize = oldBoardSize;
\r
2197 oldRect.left = wpMain.x; //[HGM] placement: remember previous window params
\r
2198 oldRect.top = wpMain.y;
\r
2199 oldRect.right = wpMain.x + wpMain.width;
\r
2200 oldRect.bottom = wpMain.y + wpMain.height;
\r
2202 tinyLayout = sizeInfo[boardSize].tinyLayout;
\r
2203 smallLayout = sizeInfo[boardSize].smallLayout;
\r
2204 squareSize = sizeInfo[boardSize].squareSize;
\r
2205 lineGap = sizeInfo[boardSize].lineGap;
\r
2206 minorSize = 0; /* [HGM] Kludge to see if demagnified pieces need to be shifted */
\r
2208 if( appData.overrideLineGap >= 0 && appData.overrideLineGap <= 5 ) {
\r
2209 lineGap = appData.overrideLineGap;
\r
2212 if (tinyLayout != oldTinyLayout) {
\r
2213 long style = GetWindowLong(hwndMain, GWL_STYLE);
\r
2215 style &= ~WS_SYSMENU;
\r
2216 InsertMenu(hmenu, IDM_Exit, MF_BYCOMMAND, IDM_Minimize,
\r
2217 "&Minimize\tCtrl+F4");
\r
2219 style |= WS_SYSMENU;
\r
2220 RemoveMenu(hmenu, IDM_Minimize, MF_BYCOMMAND);
\r
2222 SetWindowLong(hwndMain, GWL_STYLE, style);
\r
2224 for (i=0; menuBarText[tinyLayout][i]; i++) {
\r
2225 ModifyMenu(hmenu, i, MF_STRING|MF_BYPOSITION|MF_POPUP,
\r
2226 (UINT)GetSubMenu(hmenu, i), T_(menuBarText[tinyLayout][i]));
\r
2228 DrawMenuBar(hwndMain);
\r
2231 boardWidth = BoardWidth(boardSize, BOARD_WIDTH);
\r
2232 boardHeight = BoardWidth(boardSize, BOARD_HEIGHT);
\r
2234 /* Get text area sizes */
\r
2235 hdc = GetDC(hwndMain);
\r
2236 if (appData.clockMode) {
\r
2237 snprintf(buf, MSG_SIZ, _("White: %s"), TimeString(23*60*60*1000L));
\r
2239 snprintf(buf, MSG_SIZ, _("White"));
\r
2241 oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);
\r
2242 GetTextExtentPoint(hdc, buf, strlen(buf), &clockSize);
\r
2243 SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);
\r
2244 str = _("We only care about the height here");
\r
2245 GetTextExtentPoint(hdc, str, strlen(str), &messageSize);
\r
2246 SelectObject(hdc, oldFont);
\r
2247 ReleaseDC(hwndMain, hdc);
\r
2249 /* Compute where everything goes */
\r
2250 if((first.programLogo || second.programLogo) && !tinyLayout) {
\r
2251 /* [HGM] logo: if either logo is on, reserve space for it */
\r
2252 logoHeight = 2*clockSize.cy;
\r
2253 leftLogoRect.left = OUTER_MARGIN;
\r
2254 leftLogoRect.right = leftLogoRect.left + 4*clockSize.cy;
\r
2255 leftLogoRect.top = OUTER_MARGIN;
\r
2256 leftLogoRect.bottom = OUTER_MARGIN + logoHeight;
\r
2258 rightLogoRect.right = OUTER_MARGIN + boardWidth;
\r
2259 rightLogoRect.left = rightLogoRect.right - 4*clockSize.cy;
\r
2260 rightLogoRect.top = OUTER_MARGIN;
\r
2261 rightLogoRect.bottom = OUTER_MARGIN + logoHeight;
\r
2264 whiteRect.left = leftLogoRect.right;
\r
2265 whiteRect.right = OUTER_MARGIN + boardWidth/2 - INNER_MARGIN/2;
\r
2266 whiteRect.top = OUTER_MARGIN;
\r
2267 whiteRect.bottom = whiteRect.top + logoHeight;
\r
2269 blackRect.right = rightLogoRect.left;
\r
2270 blackRect.left = whiteRect.right + INNER_MARGIN;
\r
2271 blackRect.top = whiteRect.top;
\r
2272 blackRect.bottom = whiteRect.bottom;
\r
2274 whiteRect.left = OUTER_MARGIN;
\r
2275 whiteRect.right = whiteRect.left + boardWidth/2 - INNER_MARGIN/2;
\r
2276 whiteRect.top = OUTER_MARGIN;
\r
2277 whiteRect.bottom = whiteRect.top + clockSize.cy;
\r
2279 blackRect.left = whiteRect.right + INNER_MARGIN;
\r
2280 blackRect.right = blackRect.left + boardWidth/2 - 1;
\r
2281 blackRect.top = whiteRect.top;
\r
2282 blackRect.bottom = whiteRect.bottom;
\r
2284 logoHeight = 0; // [HGM] logo: suppress logo after change to tiny layout!
\r
2287 messageRect.left = OUTER_MARGIN + MESSAGE_LINE_LEFTMARGIN;
\r
2288 if (appData.showButtonBar) {
\r
2289 messageRect.right = OUTER_MARGIN + boardWidth // [HGM] logo: expressed independent of clock placement
\r
2290 - N_BUTTONS*BUTTON_WIDTH - MESSAGE_LINE_LEFTMARGIN;
\r
2292 messageRect.right = OUTER_MARGIN + boardWidth;
\r
2294 messageRect.top = whiteRect.bottom + INNER_MARGIN;
\r
2295 messageRect.bottom = messageRect.top + messageSize.cy;
\r
2297 boardRect.left = OUTER_MARGIN;
\r
2298 boardRect.right = boardRect.left + boardWidth;
\r
2299 boardRect.top = messageRect.bottom + INNER_MARGIN;
\r
2300 boardRect.bottom = boardRect.top + boardHeight;
\r
2302 sizeInfo[boardSize].cliWidth = boardRect.right + OUTER_MARGIN;
\r
2303 sizeInfo[boardSize].cliHeight = boardRect.bottom + OUTER_MARGIN;
\r
2304 oldBoardSize = boardSize;
\r
2305 oldTinyLayout = tinyLayout;
\r
2306 winW = 2 * GetSystemMetrics(SM_CXFRAME) + boardRect.right + OUTER_MARGIN;
\r
2307 winH = 2 * GetSystemMetrics(SM_CYFRAME) + GetSystemMetrics(SM_CYMENU) +
\r
2308 GetSystemMetrics(SM_CYCAPTION) + boardRect.bottom + OUTER_MARGIN;
\r
2309 winW *= 1 + twoBoards;
\r
2310 if(suppressVisibleEffects) return; // [HGM] when called for filling sizeInfo only
\r
2311 wpMain.width = winW; // [HGM] placement: set through temporary which can used by initial sizing choice
\r
2312 wpMain.height = winH; // without disturbing window attachments
\r
2313 GetWindowRect(hwndMain, &wrect);
\r
2314 SetWindowPos(hwndMain, NULL, 0, 0, wpMain.width, wpMain.height,
\r
2315 SWP_NOCOPYBITS|SWP_NOZORDER|SWP_NOMOVE);
\r
2317 // [HGM] placement: let attached windows follow size change.
\r
2318 ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, moveHistoryDialog, &wpMoveHistory );
\r
2319 ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, evalGraphDialog, &wpEvalGraph );
\r
2320 ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, engineOutputDialog, &wpEngineOutput );
\r
2321 ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, gameListDialog, &wpGameList );
\r
2322 ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, hwndConsole, &wpConsole );
\r
2324 /* compensate if menu bar wrapped */
\r
2325 GetClientRect(hwndMain, &crect);
\r
2326 offby = boardRect.bottom + OUTER_MARGIN - crect.bottom;
\r
2327 wpMain.height += offby;
\r
2329 case WMSZ_TOPLEFT:
\r
2330 SetWindowPos(hwndMain, NULL,
\r
2331 wrect.right - wpMain.width, wrect.bottom - wpMain.height,
\r
2332 wpMain.width, wpMain.height, SWP_NOCOPYBITS|SWP_NOZORDER);
\r
2335 case WMSZ_TOPRIGHT:
\r
2337 SetWindowPos(hwndMain, NULL,
\r
2338 wrect.left, wrect.bottom - wpMain.height,
\r
2339 wpMain.width, wpMain.height, SWP_NOCOPYBITS|SWP_NOZORDER);
\r
2342 case WMSZ_BOTTOMLEFT:
\r
2344 SetWindowPos(hwndMain, NULL,
\r
2345 wrect.right - wpMain.width, wrect.top,
\r
2346 wpMain.width, wpMain.height, SWP_NOCOPYBITS|SWP_NOZORDER);
\r
2349 case WMSZ_BOTTOMRIGHT:
\r
2353 SetWindowPos(hwndMain, NULL, 0, 0, wpMain.width, wpMain.height,
\r
2354 SWP_NOCOPYBITS|SWP_NOZORDER|SWP_NOMOVE);
\r
2359 for (i = 0; i < N_BUTTONS; i++) {
\r
2360 if (buttonDesc[i].hwnd != NULL) {
\r
2361 DestroyWindow(buttonDesc[i].hwnd);
\r
2362 buttonDesc[i].hwnd = NULL;
\r
2364 if (appData.showButtonBar) {
\r
2365 buttonDesc[i].hwnd =
\r
2366 CreateWindow("BUTTON", buttonDesc[i].label,
\r
2367 WS_VISIBLE | WS_CHILD | BS_PUSHBUTTON,
\r
2368 boardRect.right - BUTTON_WIDTH*(N_BUTTONS-i),
\r
2369 messageRect.top, BUTTON_WIDTH, messageSize.cy, hwndMain,
\r
2370 (HMENU) buttonDesc[i].id,
\r
2371 (HINSTANCE) GetWindowLong(hwndMain, GWL_HINSTANCE), NULL);
\r
2373 SendMessage(buttonDesc[i].hwnd, WM_SETFONT,
\r
2374 (WPARAM)font[boardSize][MESSAGE_FONT]->hf,
\r
2375 MAKELPARAM(FALSE, 0));
\r
2377 if (buttonDesc[i].id == IDM_Pause)
\r
2378 hwndPause = buttonDesc[i].hwnd;
\r
2379 buttonDesc[i].wndproc = (WNDPROC)
\r
2380 SetWindowLong(buttonDesc[i].hwnd, GWL_WNDPROC, (LONG) ButtonProc);
\r
2383 if (gridPen != NULL) DeleteObject(gridPen);
\r
2384 if (highlightPen != NULL) DeleteObject(highlightPen);
\r
2385 if (premovePen != NULL) DeleteObject(premovePen);
\r
2386 if (lineGap != 0) {
\r
2387 logbrush.lbStyle = BS_SOLID;
\r
2388 logbrush.lbColor = RGB(0, 0, 0); /* grid pen color = black */
\r
2390 ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,
\r
2391 lineGap, &logbrush, 0, NULL);
\r
2392 logbrush.lbColor = highlightSquareColor;
\r
2394 ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,
\r
2395 lineGap, &logbrush, 0, NULL);
\r
2397 logbrush.lbColor = premoveHighlightColor;
\r
2399 ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,
\r
2400 lineGap, &logbrush, 0, NULL);
\r
2402 /* [HGM] Loop had to be split in part for vert. and hor. lines */
\r
2403 for (i = 0; i < BOARD_HEIGHT + 1; i++) {
\r
2404 gridEndpoints[i*2].x = boardRect.left + lineGap / 2;
\r
2405 gridEndpoints[i*2].y = gridEndpoints[i*2 + 1].y =
\r
2406 boardRect.top + lineGap / 2 + (i * (squareSize + lineGap));
\r
2407 gridEndpoints[i*2 + 1].x = boardRect.left + lineGap / 2 +
\r
2408 BOARD_WIDTH * (squareSize + lineGap);
\r
2409 gridVertexCounts[i*2] = gridVertexCounts[i*2 + 1] = 2;
\r
2411 for (i = 0; i < BOARD_WIDTH + 1; i++) {
\r
2412 gridEndpoints[i*2 + BOARD_HEIGHT*2 + 2].y = boardRect.top + lineGap / 2;
\r
2413 gridEndpoints[i*2 + BOARD_HEIGHT*2 + 2].x =
\r
2414 gridEndpoints[i*2 + 1 + BOARD_HEIGHT*2 + 2].x = boardRect.left +
\r
2415 lineGap / 2 + (i * (squareSize + lineGap));
\r
2416 gridEndpoints[i*2 + 1 + BOARD_HEIGHT*2 + 2].y =
\r
2417 boardRect.top + BOARD_HEIGHT * (squareSize + lineGap);
\r
2418 gridVertexCounts[i*2] = gridVertexCounts[i*2 + 1] = 2;
\r
2422 /* [HGM] Licensing requirement */
\r
2424 if(gameInfo.variant == VariantGothic) GothicPopUp( GOTHIC, VariantGothic); else
\r
2427 if(gameInfo.variant == VariantFalcon) GothicPopUp( FALCON, VariantFalcon); else
\r
2429 GothicPopUp( "", VariantNormal);
\r
2432 /* if (boardSize == oldBoardSize) return; [HGM] variant might have changed */
\r
2434 /* Load piece bitmaps for this board size */
\r
2435 for (i=0; i<=2; i++) {
\r
2436 for (piece = WhitePawn;
\r
2437 (int) piece < (int) BlackPawn;
\r
2438 piece = (ChessSquare) ((int) piece + 1)) {
\r
2439 if (pieceBitmap[i][piece] != NULL)
\r
2440 DeleteObject(pieceBitmap[i][piece]);
\r
2444 fontBitmapSquareSize = 0; /* [HGM] render: make sure pieces will be recreated, as we might need others now */
\r
2445 // Orthodox Chess pieces
\r
2446 pieceBitmap[0][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "s");
\r
2447 pieceBitmap[0][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "s");
\r
2448 pieceBitmap[0][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "s");
\r
2449 pieceBitmap[0][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "s");
\r
2450 pieceBitmap[0][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "s");
\r
2451 pieceBitmap[1][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "o");
\r
2452 pieceBitmap[1][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "o");
\r
2453 pieceBitmap[1][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "o");
\r
2454 pieceBitmap[1][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "o");
\r
2455 pieceBitmap[1][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "o");
\r
2456 pieceBitmap[2][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "w");
\r
2457 pieceBitmap[2][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "w");
\r
2458 pieceBitmap[2][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "w");
\r
2459 pieceBitmap[2][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "w");
\r
2460 pieceBitmap[2][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "w");
\r
2461 if( gameInfo.variant == VariantShogi && squareSize <= 72 && squareSize >= 33) {
\r
2462 // in Shogi, Hijack the unused Queen for Lance
\r
2463 pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "s");
\r
2464 pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "o");
\r
2465 pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "w");
\r
2467 pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "s");
\r
2468 pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "o");
\r
2469 pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "w");
\r
2472 if(squareSize <= 72 && squareSize >= 33) {
\r
2473 /* A & C are available in most sizes now */
\r
2474 if(squareSize != 49 && squareSize != 72 && squareSize != 33) { // Vortex-like
\r
2475 pieceBitmap[0][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "s");
\r
2476 pieceBitmap[1][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "o");
\r
2477 pieceBitmap[2][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "w");
\r
2478 pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "s");
\r
2479 pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "o");
\r
2480 pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "w");
\r
2481 pieceBitmap[0][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "s");
\r
2482 pieceBitmap[1][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "o");
\r
2483 pieceBitmap[2][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "w");
\r
2484 pieceBitmap[0][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "s");
\r
2485 pieceBitmap[1][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "o");
\r
2486 pieceBitmap[2][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "w");
\r
2487 } else { // Smirf-like
\r
2488 pieceBitmap[0][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "s");
\r
2489 pieceBitmap[1][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "o");
\r
2490 pieceBitmap[2][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "w");
\r
2492 if(gameInfo.variant == VariantGothic) { // Vortex-like
\r
2493 pieceBitmap[0][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "s");
\r
2494 pieceBitmap[1][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "o");
\r
2495 pieceBitmap[2][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "w");
\r
2496 } else { // WinBoard standard
\r
2497 pieceBitmap[0][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "s");
\r
2498 pieceBitmap[1][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "o");
\r
2499 pieceBitmap[2][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "w");
\r
2504 if(squareSize==72 || squareSize==49 || squareSize==33) { /* experiment with some home-made bitmaps */
\r
2505 pieceBitmap[0][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "s");
\r
2506 pieceBitmap[1][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "o");
\r
2507 pieceBitmap[2][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "w");
\r
2508 pieceBitmap[0][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "s");
\r
2509 pieceBitmap[1][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "o");
\r
2510 pieceBitmap[2][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "w");
\r
2511 pieceBitmap[0][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "s");
\r
2512 pieceBitmap[1][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "o");
\r
2513 pieceBitmap[2][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "w");
\r
2514 pieceBitmap[0][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "s");
\r
2515 pieceBitmap[1][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "o");
\r
2516 pieceBitmap[2][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "w");
\r
2517 pieceBitmap[0][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "s");
\r
2518 pieceBitmap[1][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "o");
\r
2519 pieceBitmap[2][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "w");
\r
2520 pieceBitmap[0][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "s");
\r
2521 pieceBitmap[1][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "o");
\r
2522 pieceBitmap[2][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "w");
\r
2523 pieceBitmap[0][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "s");
\r
2524 pieceBitmap[1][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "o");
\r
2525 pieceBitmap[2][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "w");
\r
2526 pieceBitmap[0][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "s");
\r
2527 pieceBitmap[1][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "o");
\r
2528 pieceBitmap[2][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "w");
\r
2529 pieceBitmap[0][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "s");
\r
2530 pieceBitmap[1][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "o");
\r
2531 pieceBitmap[2][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "w");
\r
2532 pieceBitmap[0][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "s");
\r
2533 pieceBitmap[1][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "o");
\r
2534 pieceBitmap[2][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "w");
\r
2536 if(gameInfo.variant == VariantShogi) { /* promoted Gold represemtations */
\r
2537 pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "wp", squareSize, "s");
\r
2538 pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "wp", squareSize, "o");
\r
2539 pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "w", squareSize, "w");
\r
2540 pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "wn", squareSize, "s");
\r
2541 pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "wn", squareSize, "o");
\r
2542 pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "w", squareSize, "w");
\r
2543 pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "ws", squareSize, "s");
\r
2544 pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "ws", squareSize, "o");
\r
2545 pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "w", squareSize, "w");
\r
2546 pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "wl", squareSize, "s");
\r
2547 pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "wl", squareSize, "o");
\r
2548 pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "w", squareSize, "w");
\r
2550 pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "s");
\r
2551 pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "o");
\r
2552 pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "w");
\r
2553 pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "s");
\r
2554 pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "o");
\r
2555 pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "w");
\r
2556 pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "s");
\r
2557 pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "o");
\r
2558 pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "w");
\r
2559 pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "s");
\r
2560 pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "o");
\r
2561 pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "w");
\r
2564 } else { /* other size, no special bitmaps available. Use smaller symbols */
\r
2565 if((int)boardSize < 2) minorSize = sizeInfo[0].squareSize;
\r
2566 else minorSize = sizeInfo[(int)boardSize - 2].squareSize;
\r
2567 pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "s");
\r
2568 pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "o");
\r
2569 pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "w");
\r
2570 pieceBitmap[0][WhiteCardinal] = DoLoadBitmap(hInst, "b", minorSize, "s");
\r
2571 pieceBitmap[1][WhiteCardinal] = DoLoadBitmap(hInst, "b", minorSize, "o");
\r
2572 pieceBitmap[2][WhiteCardinal] = DoLoadBitmap(hInst, "b", minorSize, "w");
\r
2573 pieceBitmap[0][WhiteDragon] = DoLoadBitmap(hInst, "r", minorSize, "s");
\r
2574 pieceBitmap[1][WhiteDragon] = DoLoadBitmap(hInst, "r", minorSize, "o");
\r
2575 pieceBitmap[2][WhiteDragon] = DoLoadBitmap(hInst, "r", minorSize, "w");
\r
2576 pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "s");
\r
2577 pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "o");
\r
2578 pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "w");
\r
2582 if(gameInfo.variant == VariantShogi && squareSize == 58)
\r
2583 /* special Shogi support in this size */
\r
2584 { for (i=0; i<=2; i++) { /* replace all bitmaps */
\r
2585 for (piece = WhitePawn;
\r
2586 (int) piece < (int) BlackPawn;
\r
2587 piece = (ChessSquare) ((int) piece + 1)) {
\r
2588 if (pieceBitmap[i][piece] != NULL)
\r
2589 DeleteObject(pieceBitmap[i][piece]);
\r
2592 pieceBitmap[0][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "o");
\r
2593 pieceBitmap[0][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "o");
\r
2594 pieceBitmap[0][WhiteBishop] = DoLoadBitmap(hInst, "sb", squareSize, "o");
\r
2595 pieceBitmap[0][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "o");
\r
2596 pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "o");
\r
2597 pieceBitmap[0][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "o");
\r
2598 pieceBitmap[0][WhiteFerz] = DoLoadBitmap(hInst, "sf", squareSize, "o");
\r
2599 pieceBitmap[0][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "o");
\r
2600 pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "su", squareSize, "o");
\r
2601 pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "sh", squareSize, "o");
\r
2602 pieceBitmap[0][WhiteCardinal] = DoLoadBitmap(hInst, "sa", squareSize, "o");
\r
2603 pieceBitmap[0][WhiteDragon] = DoLoadBitmap(hInst, "sc", squareSize, "o");
\r
2604 pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "sg", squareSize, "o");
\r
2605 pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "ss", squareSize, "o");
\r
2606 pieceBitmap[1][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "o");
\r
2607 pieceBitmap[1][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "o");
\r
2608 pieceBitmap[1][WhiteBishop] = DoLoadBitmap(hInst, "sb", squareSize, "o");
\r
2609 pieceBitmap[1][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "o");
\r
2610 pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "o");
\r
2611 pieceBitmap[1][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "o");
\r
2612 pieceBitmap[1][WhiteFerz] = DoLoadBitmap(hInst, "sf", squareSize, "o");
\r
2613 pieceBitmap[1][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "o");
\r
2614 pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "su", squareSize, "o");
\r
2615 pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "sh", squareSize, "o");
\r
2616 pieceBitmap[1][WhiteCardinal] = DoLoadBitmap(hInst, "sa", squareSize, "o");
\r
2617 pieceBitmap[1][WhiteDragon] = DoLoadBitmap(hInst, "sc", squareSize, "o");
\r
2618 pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "sg", squareSize, "o");
\r
2619 pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "ss", squareSize, "o");
\r
2620 pieceBitmap[2][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "w");
\r
2621 pieceBitmap[2][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "w");
\r
2622 pieceBitmap[2][WhiteBishop] = DoLoadBitmap(hInst, "sr", squareSize, "w");
\r
2623 pieceBitmap[2][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "w");
\r
2624 pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "w");
\r
2625 pieceBitmap[2][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "w");
\r
2626 pieceBitmap[2][WhiteFerz] = DoLoadBitmap(hInst, "sw", squareSize, "w");
\r
2627 pieceBitmap[2][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "w");
\r
2628 pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "sp", squareSize, "w");
\r
2629 pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "sn", squareSize, "w");
\r
2630 pieceBitmap[2][WhiteCardinal] = DoLoadBitmap(hInst, "sr", squareSize, "w");
\r
2631 pieceBitmap[2][WhiteDragon] = DoLoadBitmap(hInst, "sr", squareSize, "w");
\r
2632 pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "sl", squareSize, "w");
\r
2633 pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "sw", squareSize, "w");
\r
2639 PieceBitmap(ChessSquare p, int kind)
\r
2641 if ((int) p >= (int) BlackPawn)
\r
2642 p = (ChessSquare) ((int) p - (int) BlackPawn + (int) WhitePawn);
\r
2644 return pieceBitmap[kind][(int) p];
\r
2647 /***************************************************************/
\r
2649 #define MIN(a,b) (((a) < (b)) ? (a) : (b))
\r
2650 #define MAX(a,b) (((a) > (b)) ? (a) : (b))
\r
2652 #define MIN3(a,b,c) (((a) < (b) && (a) < (c)) ? (a) : (((b) < (a) && (b) < (c)) ? (b) : (c)))
\r
2653 #define MAX3(a,b,c) (((a) > (b) && (a) > (c)) ? (a) : (((b) > (a) && (b) > (c)) ? (b) : (c)))
\r
2657 SquareToPos(int row, int column, int * x, int * y)
\r
2660 *x = boardRect.left + lineGap + ((BOARD_WIDTH-1)-column) * (squareSize + lineGap);
\r
2661 *y = boardRect.top + lineGap + row * (squareSize + lineGap);
\r
2663 *x = boardRect.left + lineGap + column * (squareSize + lineGap);
\r
2664 *y = boardRect.top + lineGap + ((BOARD_HEIGHT-1)-row) * (squareSize + lineGap);
\r
2669 DrawCoordsOnDC(HDC hdc)
\r
2671 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
2672 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
2673 char str[2] = { NULLCHAR, NULLCHAR };
\r
2674 int oldMode, oldAlign, x, y, start, i;
\r
2678 if (!appData.showCoords)
\r
2681 start = flipView ? 1-(ONE!='1') : 23+(ONE!='1')-BOARD_HEIGHT;
\r
2683 oldBrush = SelectObject(hdc, GetStockObject(BLACK_BRUSH));
\r
2684 oldMode = SetBkMode(hdc, (appData.monoMode ? OPAQUE : TRANSPARENT));
\r
2685 oldAlign = GetTextAlign(hdc);
\r
2686 oldFont = SelectObject(hdc, font[boardSize][COORD_FONT]->hf);
\r
2688 y = boardRect.top + lineGap;
\r
2689 x = boardRect.left + lineGap + gameInfo.holdingsWidth*(squareSize + lineGap);
\r
2691 SetTextAlign(hdc, TA_LEFT|TA_TOP);
\r
2692 for (i = 0; i < BOARD_HEIGHT; i++) {
\r
2693 str[0] = files[start + i];
\r
2694 ExtTextOut(hdc, x + 2, y + 1, 0, NULL, str, 1, NULL);
\r
2695 y += squareSize + lineGap;
\r
2698 start = flipView ? 12-(BOARD_RGHT-BOARD_LEFT) : 12;
\r
2700 SetTextAlign(hdc, TA_RIGHT|TA_BOTTOM);
\r
2701 for (i = 0; i < BOARD_RGHT - BOARD_LEFT; i++) {
\r
2702 str[0] = ranks[start + i];
\r
2703 ExtTextOut(hdc, x + squareSize - 2, y - 1, 0, NULL, str, 1, NULL);
\r
2704 x += squareSize + lineGap;
\r
2707 SelectObject(hdc, oldBrush);
\r
2708 SetBkMode(hdc, oldMode);
\r
2709 SetTextAlign(hdc, oldAlign);
\r
2710 SelectObject(hdc, oldFont);
\r
2714 DrawGridOnDC(HDC hdc)
\r
2718 if (lineGap != 0) {
\r
2719 oldPen = SelectObject(hdc, gridPen);
\r
2720 PolyPolyline(hdc, gridEndpoints, gridVertexCounts, BOARD_WIDTH+BOARD_HEIGHT + 2);
\r
2721 SelectObject(hdc, oldPen);
\r
2725 #define HIGHLIGHT_PEN 0
\r
2726 #define PREMOVE_PEN 1
\r
2729 DrawHighlightOnDC(HDC hdc, BOOLEAN on, int x, int y, int pen)
\r
2732 HPEN oldPen, hPen;
\r
2733 if (lineGap == 0) return;
\r
2735 x1 = boardRect.left +
\r
2736 lineGap/2 + ((BOARD_WIDTH-1)-x) * (squareSize + lineGap);
\r
2737 y1 = boardRect.top +
\r
2738 lineGap/2 + y * (squareSize + lineGap);
\r
2740 x1 = boardRect.left +
\r
2741 lineGap/2 + x * (squareSize + lineGap);
\r
2742 y1 = boardRect.top +
\r
2743 lineGap/2 + ((BOARD_HEIGHT-1)-y) * (squareSize + lineGap);
\r
2745 hPen = pen ? premovePen : highlightPen;
\r
2746 oldPen = SelectObject(hdc, on ? hPen : gridPen);
\r
2747 MoveToEx(hdc, x1, y1, NULL);
\r
2748 LineTo(hdc, x1 + squareSize + lineGap, y1);
\r
2749 LineTo(hdc, x1 + squareSize + lineGap, y1 + squareSize + lineGap);
\r
2750 LineTo(hdc, x1, y1 + squareSize + lineGap);
\r
2751 LineTo(hdc, x1, y1);
\r
2752 SelectObject(hdc, oldPen);
\r
2756 DrawHighlightsOnDC(HDC hdc, HighlightInfo *h, int pen)
\r
2759 for (i=0; i<2; i++) {
\r
2760 if (h->sq[i].x >= 0 && h->sq[i].y >= 0)
\r
2761 DrawHighlightOnDC(hdc, TRUE,
\r
2762 h->sq[i].x, h->sq[i].y,
\r
2767 /* Note: sqcolor is used only in monoMode */
\r
2768 /* Note that this code is largely duplicated in woptions.c,
\r
2769 function DrawSampleSquare, so that needs to be updated too */
\r
2771 DrawPieceOnDC(HDC hdc, ChessSquare piece, int color, int sqcolor, int x, int y, HDC tmphdc)
\r
2773 HBITMAP oldBitmap;
\r
2777 if (appData.blindfold) return;
\r
2779 /* [AS] Use font-based pieces if needed */
\r
2780 if( fontBitmapSquareSize >= 0 && (squareSize > 32 || gameInfo.variant >= VariantShogi)) {
\r
2781 /* Create piece bitmaps, or do nothing if piece set is up to date */
\r
2782 CreatePiecesFromFont();
\r
2784 if( fontBitmapSquareSize == squareSize ) {
\r
2785 int index = TranslatePieceToFontPiece(piece);
\r
2787 SelectObject( tmphdc, hPieceMask[ index ] );
\r
2789 if(appData.upsideDown ? color==flipView : (flipView && gameInfo.variant == VariantShogi))
\r
2790 StretchBlt(hdc, x+squareSize, y+squareSize, -squareSize, -squareSize, tmphdc, 0, 0, squareSize, squareSize, SRCAND);
\r
2794 squareSize, squareSize,
\r
2799 SelectObject( tmphdc, hPieceFace[ index ] );
\r
2801 if(appData.upsideDown ? color==flipView : (flipView && gameInfo.variant == VariantShogi))
\r
2802 StretchBlt(hdc, x+squareSize, y+squareSize, -squareSize, -squareSize, tmphdc, 0, 0, squareSize, squareSize, SRCPAINT);
\r
2806 squareSize, squareSize,
\r
2815 if (appData.monoMode) {
\r
2816 SelectObject(tmphdc, PieceBitmap(piece,
\r
2817 color == sqcolor ? OUTLINE_PIECE : SOLID_PIECE));
\r
2818 BitBlt(hdc, x, y, squareSize, squareSize, tmphdc, 0, 0,
\r
2819 sqcolor ? SRCCOPY : NOTSRCCOPY);
\r
2821 tmpSize = squareSize;
\r
2823 ((piece >= (int)WhiteNightrider && piece <= WhiteGrasshopper) ||
\r
2824 (piece >= (int)BlackNightrider && piece <= BlackGrasshopper)) ) {
\r
2825 /* [HGM] no bitmap available for promoted pieces in Crazyhouse */
\r
2826 /* Bitmaps of smaller size are substituted, but we have to align them */
\r
2827 x += (squareSize - minorSize)>>1;
\r
2828 y += squareSize - minorSize - 2;
\r
2829 tmpSize = minorSize;
\r
2831 if (color || appData.allWhite ) {
\r
2832 oldBitmap = SelectObject(tmphdc, PieceBitmap(piece, WHITE_PIECE));
\r
2834 oldBrush = SelectObject(hdc, whitePieceBrush);
\r
2835 else oldBrush = SelectObject(hdc, blackPieceBrush);
\r
2836 if(appData.upsideDown && color==flipView)
\r
2837 StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, 0x00B8074A);
\r
2839 BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, 0x00B8074A);
\r
2840 /* Use black for outline of white pieces */
\r
2841 SelectObject(tmphdc, PieceBitmap(piece, OUTLINE_PIECE));
\r
2842 if(appData.upsideDown && color==flipView)
\r
2843 StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, SRCAND);
\r
2845 BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, SRCAND);
\r
2847 /* Use square color for details of black pieces */
\r
2848 oldBitmap = SelectObject(tmphdc, PieceBitmap(piece, SOLID_PIECE));
\r
2849 oldBrush = SelectObject(hdc, blackPieceBrush);
\r
2850 if(appData.upsideDown && !flipView)
\r
2851 StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, 0x00B8074A);
\r
2853 BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, 0x00B8074A);
\r
2855 SelectObject(hdc, oldBrush);
\r
2856 SelectObject(tmphdc, oldBitmap);
\r
2860 /* [AS] Compute a drawing mode for a square, based on specified settings (see DrawTile) */
\r
2861 int GetBackTextureMode( int algo )
\r
2863 int result = BACK_TEXTURE_MODE_DISABLED;
\r
2867 case BACK_TEXTURE_MODE_PLAIN:
\r
2868 result = 1; /* Always use identity map */
\r
2870 case BACK_TEXTURE_MODE_FULL_RANDOM:
\r
2871 result = 1 + (myrandom() % 3); /* Pick a transformation at random */
\r
2879 [AS] Compute and save texture drawing info, otherwise we may not be able
\r
2880 to handle redraws cleanly (as random numbers would always be different).
\r
2882 VOID RebuildTextureSquareInfo()
\r
2892 ZeroMemory( &backTextureSquareInfo, sizeof(backTextureSquareInfo) );
\r
2894 if( liteBackTexture != NULL ) {
\r
2895 if( GetObject( liteBackTexture, sizeof(bi), &bi ) > 0 ) {
\r
2896 lite_w = bi.bmWidth;
\r
2897 lite_h = bi.bmHeight;
\r
2901 if( darkBackTexture != NULL ) {
\r
2902 if( GetObject( darkBackTexture, sizeof(bi), &bi ) > 0 ) {
\r
2903 dark_w = bi.bmWidth;
\r
2904 dark_h = bi.bmHeight;
\r
2908 for( row=0; row<BOARD_HEIGHT; row++ ) {
\r
2909 for( col=0; col<BOARD_WIDTH; col++ ) {
\r
2910 if( (col + row) & 1 ) {
\r
2912 if( lite_w >= squareSize && lite_h >= squareSize ) {
\r
2913 if( lite_w >= squareSize*BOARD_WIDTH )
\r
2914 backTextureSquareInfo[row][col].x = (2*col+1)*lite_w/(2*BOARD_WIDTH) - squareSize/2; /* [HGM] cut out of center of virtual square */
\r
2916 backTextureSquareInfo[row][col].x = col * (lite_w - squareSize) / (BOARD_WIDTH-1); /* [HGM] divide by size-1 in stead of size! */
\r
2917 if( lite_h >= squareSize*BOARD_HEIGHT )
\r
2918 backTextureSquareInfo[row][col].y = (2*(BOARD_HEIGHT-row)-1)*lite_h/(2*BOARD_HEIGHT) - squareSize/2;
\r
2920 backTextureSquareInfo[row][col].y = (BOARD_HEIGHT-1-row) * (lite_h - squareSize) / (BOARD_HEIGHT-1);
\r
2921 backTextureSquareInfo[row][col].mode = GetBackTextureMode(liteBackTextureMode);
\r
2926 if( dark_w >= squareSize && dark_h >= squareSize ) {
\r
2927 if( dark_w >= squareSize*BOARD_WIDTH )
\r
2928 backTextureSquareInfo[row][col].x = (2*col+1) * dark_w / (2*BOARD_WIDTH) - squareSize/2;
\r
2930 backTextureSquareInfo[row][col].x = col * (dark_w - squareSize) / (BOARD_WIDTH-1);
\r
2931 if( dark_h >= squareSize*BOARD_HEIGHT )
\r
2932 backTextureSquareInfo[row][col].y = (2*(BOARD_HEIGHT-row)-1) * dark_h / (2*BOARD_HEIGHT) - squareSize/2;
\r
2934 backTextureSquareInfo[row][col].y = (BOARD_HEIGHT-1-row) * (dark_h - squareSize) / (BOARD_HEIGHT-1);
\r
2935 backTextureSquareInfo[row][col].mode = GetBackTextureMode(darkBackTextureMode);
\r
2942 /* [AS] Arrow highlighting support */
\r
2944 static int A_WIDTH = 5; /* Width of arrow body */
\r
2946 #define A_HEIGHT_FACTOR 6 /* Length of arrow "point", relative to body width */
\r
2947 #define A_WIDTH_FACTOR 3 /* Width of arrow "point", relative to body width */
\r
2949 static double Sqr( double x )
\r
2954 static int Round( double x )
\r
2956 return (int) (x + 0.5);
\r
2959 /* Draw an arrow between two points using current settings */
\r
2960 VOID DrawArrowBetweenPoints( HDC hdc, int s_x, int s_y, int d_x, int d_y )
\r
2963 double dx, dy, j, k, x, y;
\r
2965 if( d_x == s_x ) {
\r
2966 int h = (d_y > s_y) ? +A_WIDTH*A_HEIGHT_FACTOR : -A_WIDTH*A_HEIGHT_FACTOR;
\r
2968 arrow[0].x = s_x + A_WIDTH;
\r
2971 arrow[1].x = s_x + A_WIDTH;
\r
2972 arrow[1].y = d_y - h;
\r
2974 arrow[2].x = s_x + A_WIDTH*A_WIDTH_FACTOR;
\r
2975 arrow[2].y = d_y - h;
\r
2980 arrow[4].x = s_x - A_WIDTH*A_WIDTH_FACTOR;
\r
2981 arrow[4].y = d_y - h;
\r
2983 arrow[5].x = s_x - A_WIDTH;
\r
2984 arrow[5].y = d_y - h;
\r
2986 arrow[6].x = s_x - A_WIDTH;
\r
2989 else if( d_y == s_y ) {
\r
2990 int w = (d_x > s_x) ? +A_WIDTH*A_HEIGHT_FACTOR : -A_WIDTH*A_HEIGHT_FACTOR;
\r
2993 arrow[0].y = s_y + A_WIDTH;
\r
2995 arrow[1].x = d_x - w;
\r
2996 arrow[1].y = s_y + A_WIDTH;
\r
2998 arrow[2].x = d_x - w;
\r
2999 arrow[2].y = s_y + A_WIDTH*A_WIDTH_FACTOR;
\r
3004 arrow[4].x = d_x - w;
\r
3005 arrow[4].y = s_y - A_WIDTH*A_WIDTH_FACTOR;
\r
3007 arrow[5].x = d_x - w;
\r
3008 arrow[5].y = s_y - A_WIDTH;
\r
3011 arrow[6].y = s_y - A_WIDTH;
\r
3014 /* [AS] Needed a lot of paper for this! :-) */
\r
3015 dy = (double) (d_y - s_y) / (double) (d_x - s_x);
\r
3016 dx = (double) (s_x - d_x) / (double) (s_y - d_y);
\r
3018 j = sqrt( Sqr(A_WIDTH) / (1.0 + Sqr(dx)) );
\r
3020 k = sqrt( Sqr(A_WIDTH*A_HEIGHT_FACTOR) / (1.0 + Sqr(dy)) );
\r
3025 arrow[0].x = Round(x - j);
\r
3026 arrow[0].y = Round(y + j*dx);
\r
3028 arrow[1].x = Round(x + j);
\r
3029 arrow[1].y = Round(y - j*dx);
\r
3032 x = (double) d_x - k;
\r
3033 y = (double) d_y - k*dy;
\r
3036 x = (double) d_x + k;
\r
3037 y = (double) d_y + k*dy;
\r
3040 arrow[2].x = Round(x + j);
\r
3041 arrow[2].y = Round(y - j*dx);
\r
3043 arrow[3].x = Round(x + j*A_WIDTH_FACTOR);
\r
3044 arrow[3].y = Round(y - j*A_WIDTH_FACTOR*dx);
\r
3049 arrow[5].x = Round(x - j*A_WIDTH_FACTOR);
\r
3050 arrow[5].y = Round(y + j*A_WIDTH_FACTOR*dx);
\r
3052 arrow[6].x = Round(x - j);
\r
3053 arrow[6].y = Round(y + j*dx);
\r
3056 Polygon( hdc, arrow, 7 );
\r
3059 /* [AS] Draw an arrow between two squares */
\r
3060 VOID DrawArrowBetweenSquares( HDC hdc, int s_col, int s_row, int d_col, int d_row )
\r
3062 int s_x, s_y, d_x, d_y;
\r
3069 if( s_col == d_col && s_row == d_row ) {
\r
3073 /* Get source and destination points */
\r
3074 SquareToPos( s_row, s_col, &s_x, &s_y);
\r
3075 SquareToPos( d_row, d_col, &d_x, &d_y);
\r
3078 d_y += squareSize / 4;
\r
3080 else if( d_y < s_y ) {
\r
3081 d_y += 3 * squareSize / 4;
\r
3084 d_y += squareSize / 2;
\r
3088 d_x += squareSize / 4;
\r
3090 else if( d_x < s_x ) {
\r
3091 d_x += 3 * squareSize / 4;
\r
3094 d_x += squareSize / 2;
\r
3097 s_x += squareSize / 2;
\r
3098 s_y += squareSize / 2;
\r
3100 /* Adjust width */
\r
3101 A_WIDTH = squareSize / 14;
\r
3104 stLB.lbStyle = BS_SOLID;
\r
3105 stLB.lbColor = appData.highlightArrowColor;
\r
3108 hpen = CreatePen( PS_SOLID, 2, RGB(0x00,0x00,0x00) );
\r
3109 holdpen = SelectObject( hdc, hpen );
\r
3110 hbrush = CreateBrushIndirect( &stLB );
\r
3111 holdbrush = SelectObject( hdc, hbrush );
\r
3113 DrawArrowBetweenPoints( hdc, s_x, s_y, d_x, d_y );
\r
3115 SelectObject( hdc, holdpen );
\r
3116 SelectObject( hdc, holdbrush );
\r
3117 DeleteObject( hpen );
\r
3118 DeleteObject( hbrush );
\r
3121 BOOL HasHighlightInfo()
\r
3123 BOOL result = FALSE;
\r
3125 if( highlightInfo.sq[0].x >= 0 && highlightInfo.sq[0].y >= 0 &&
\r
3126 highlightInfo.sq[1].x >= 0 && highlightInfo.sq[1].y >= 0 )
\r
3134 BOOL IsDrawArrowEnabled()
\r
3136 BOOL result = FALSE;
\r
3138 if( appData.highlightMoveWithArrow && squareSize >= 32 ) {
\r
3145 VOID DrawArrowHighlight( HDC hdc )
\r
3147 if( IsDrawArrowEnabled() && HasHighlightInfo() ) {
\r
3148 DrawArrowBetweenSquares( hdc,
\r
3149 highlightInfo.sq[0].x, highlightInfo.sq[0].y,
\r
3150 highlightInfo.sq[1].x, highlightInfo.sq[1].y );
\r
3154 HRGN GetArrowHighlightClipRegion( HDC hdc )
\r
3156 HRGN result = NULL;
\r
3158 if( HasHighlightInfo() ) {
\r
3159 int x1, y1, x2, y2;
\r
3160 int sx, sy, dx, dy;
\r
3162 SquareToPos(highlightInfo.sq[0].y, highlightInfo.sq[0].x, &x1, &y1 );
\r
3163 SquareToPos(highlightInfo.sq[1].y, highlightInfo.sq[1].x, &x2, &y2 );
\r
3165 sx = MIN( x1, x2 );
\r
3166 sy = MIN( y1, y2 );
\r
3167 dx = MAX( x1, x2 ) + squareSize;
\r
3168 dy = MAX( y1, y2 ) + squareSize;
\r
3170 result = CreateRectRgn( sx, sy, dx, dy );
\r
3177 Warning: this function modifies the behavior of several other functions.
\r
3179 Basically, Winboard is optimized to avoid drawing the whole board if not strictly
\r
3180 needed. Unfortunately, the decision whether or not to perform a full or partial
\r
3181 repaint is scattered all over the place, which is not good for features such as
\r
3182 "arrow highlighting" that require a full repaint of the board.
\r
3184 So, I've tried to patch the code where I thought it made sense (e.g. after or during
\r
3185 user interaction, when speed is not so important) but especially to avoid errors
\r
3186 in the displayed graphics.
\r
3188 In such patched places, I always try refer to this function so there is a single
\r
3189 place to maintain knowledge.
\r
3191 To restore the original behavior, just return FALSE unconditionally.
\r
3193 BOOL IsFullRepaintPreferrable()
\r
3195 BOOL result = FALSE;
\r
3197 if( (appData.highlightLastMove || appData.highlightDragging) && IsDrawArrowEnabled() ) {
\r
3198 /* Arrow may appear on the board */
\r
3206 This function is called by DrawPosition to know whether a full repaint must
\r
3209 Only DrawPosition may directly call this function, which makes use of
\r
3210 some state information. Other function should call DrawPosition specifying
\r
3211 the repaint flag, and can use IsFullRepaintPreferrable if needed.
\r
3213 BOOL DrawPositionNeedsFullRepaint()
\r
3215 BOOL result = FALSE;
\r
3218 Probably a slightly better policy would be to trigger a full repaint
\r
3219 when animInfo.piece changes state (i.e. empty -> non-empty and viceversa),
\r
3220 but animation is fast enough that it's difficult to notice.
\r
3222 if( animInfo.piece == EmptySquare ) {
\r
3223 if( (appData.highlightLastMove || appData.highlightDragging) && IsDrawArrowEnabled() /*&& HasHighlightInfo()*/ ) {
\r
3232 DrawBoardOnDC(HDC hdc, Board board, HDC tmphdc)
\r
3234 int row, column, x, y, square_color, piece_color;
\r
3235 ChessSquare piece;
\r
3237 HDC texture_hdc = NULL;
\r
3239 /* [AS] Initialize background textures if needed */
\r
3240 if( liteBackTexture != NULL || darkBackTexture != NULL ) {
\r
3241 static int backTextureBoardSize; /* [HGM] boardsize: also new texture if board format changed */
\r
3242 if( backTextureSquareSize != squareSize
\r
3243 || backTextureBoardSize != BOARD_WIDTH+BOARD_FILES*BOARD_HEIGHT) {
\r
3244 backTextureBoardSize = BOARD_WIDTH+BOARD_FILES*BOARD_HEIGHT;
\r
3245 backTextureSquareSize = squareSize;
\r
3246 RebuildTextureSquareInfo();
\r
3249 texture_hdc = CreateCompatibleDC( hdc );
\r
3252 for (row = 0; row < BOARD_HEIGHT; row++) {
\r
3253 for (column = 0; column < BOARD_WIDTH; column++) {
\r
3255 SquareToPos(row, column, &x, &y);
\r
3257 piece = board[row][column];
\r
3259 square_color = ((column + row) % 2) == 1;
\r
3260 if( gameInfo.variant == VariantXiangqi ) {
\r
3261 square_color = !InPalace(row, column);
\r
3262 if(BOARD_HEIGHT&1) { if(row==BOARD_HEIGHT/2) square_color ^= 1; }
\r
3263 else if(row < BOARD_HEIGHT/2) square_color ^= 1;
\r
3265 piece_color = (int) piece < (int) BlackPawn;
\r
3268 /* [HGM] holdings file: light square or black */
\r
3269 if(column == BOARD_LEFT-2) {
\r
3270 if( row > BOARD_HEIGHT - gameInfo.holdingsSize - 1 )
\r
3273 DisplayHoldingsCount(hdc, x, y, 0, 0); /* black out */
\r
3277 if(column == BOARD_RGHT + 1 ) {
\r
3278 if( row < gameInfo.holdingsSize )
\r
3281 DisplayHoldingsCount(hdc, x, y, 0, 0);
\r
3285 if(column == BOARD_LEFT-1 ) /* left align */
\r
3286 DisplayHoldingsCount(hdc, x, y, flipView, (int) board[row][column]);
\r
3287 else if( column == BOARD_RGHT) /* right align */
\r
3288 DisplayHoldingsCount(hdc, x, y, !flipView, (int) board[row][column]);
\r
3290 if (appData.monoMode) {
\r
3291 if (piece == EmptySquare) {
\r
3292 BitBlt(hdc, x, y, squareSize, squareSize, 0, 0, 0,
\r
3293 square_color ? WHITENESS : BLACKNESS);
\r
3295 DrawPieceOnDC(hdc, piece, piece_color, square_color, x, y, tmphdc);
\r
3298 else if( backTextureSquareInfo[row][column].mode > 0 ) {
\r
3299 /* [AS] Draw the square using a texture bitmap */
\r
3300 HBITMAP hbm = SelectObject( texture_hdc, square_color ? liteBackTexture : darkBackTexture );
\r
3301 int r = row, c = column; // [HGM] do not flip board in flipView
\r
3302 if(flipView) { r = BOARD_HEIGHT-1 - r; c = BOARD_WIDTH-1 - c; }
\r
3305 squareSize, squareSize,
\r
3308 backTextureSquareInfo[r][c].mode,
\r
3309 backTextureSquareInfo[r][c].x,
\r
3310 backTextureSquareInfo[r][c].y );
\r
3312 SelectObject( texture_hdc, hbm );
\r
3314 if (piece != EmptySquare) {
\r
3315 DrawPieceOnDC(hdc, piece, piece_color, -1, x, y, tmphdc);
\r
3319 HBRUSH brush = square_color ? lightSquareBrush : darkSquareBrush;
\r
3321 oldBrush = SelectObject(hdc, brush );
\r
3322 BitBlt(hdc, x, y, squareSize, squareSize, 0, 0, 0, PATCOPY);
\r
3323 SelectObject(hdc, oldBrush);
\r
3324 if (piece != EmptySquare)
\r
3325 DrawPieceOnDC(hdc, piece, piece_color, -1, x, y, tmphdc);
\r
3330 if( texture_hdc != NULL ) {
\r
3331 DeleteDC( texture_hdc );
\r
3335 int saveDiagFlag = 0; FILE *diagFile; // [HGM] diag
\r
3336 void fputDW(FILE *f, int x)
\r
3338 fputc(x & 255, f);
\r
3339 fputc(x>>8 & 255, f);
\r
3340 fputc(x>>16 & 255, f);
\r
3341 fputc(x>>24 & 255, f);
\r
3344 #define MAX_CLIPS 200 /* more than enough */
\r
3347 DrawLogoOnDC(HDC hdc, RECT logoRect, HBITMAP logo)
\r
3349 // HBITMAP bufferBitmap;
\r
3354 int w = 100, h = 50;
\r
3356 if(logo == NULL) return;
\r
3357 // GetClientRect(hwndMain, &Rect);
\r
3358 // bufferBitmap = CreateCompatibleBitmap(hdc, Rect.right-Rect.left+1,
\r
3359 // Rect.bottom-Rect.top+1);
\r
3360 tmphdc = CreateCompatibleDC(hdc);
\r
3361 hbm = SelectObject(tmphdc, logo);
\r
3362 if( GetObject( logo, sizeof(bi), &bi ) > 0 ) {
\r
3366 StretchBlt(hdc, logoRect.left, logoRect.top, logoRect.right - logoRect.left,
\r
3367 logoRect.bottom - logoRect.top, tmphdc, 0, 0, w, h, SRCCOPY);
\r
3368 SelectObject(tmphdc, hbm);
\r
3372 static HDC hdcSeek;
\r
3374 // [HGM] seekgraph
\r
3375 void DrawSeekAxis( int x, int y, int xTo, int yTo )
\r
3378 HPEN hp = SelectObject( hdcSeek, gridPen );
\r
3379 MoveToEx( hdcSeek, boardRect.left+x, boardRect.top+y, &stPt );
\r
3380 LineTo( hdcSeek, boardRect.left+xTo, boardRect.top+yTo );
\r
3381 SelectObject( hdcSeek, hp );
\r
3384 // front-end wrapper for drawing functions to do rectangles
\r
3385 void DrawSeekBackground( int left, int top, int right, int bottom )
\r
3390 if (hdcSeek == NULL) {
\r
3391 hdcSeek = GetDC(hwndMain);
\r
3392 if (!appData.monoMode) {
\r
3393 SelectPalette(hdcSeek, hPal, FALSE);
\r
3394 RealizePalette(hdcSeek);
\r
3397 hp = SelectObject( hdcSeek, gridPen );
\r
3398 rc.top = boardRect.top+top; rc.left = boardRect.left+left;
\r
3399 rc.bottom = boardRect.top+bottom; rc.right = boardRect.left+right;
\r
3400 FillRect( hdcSeek, &rc, lightSquareBrush );
\r
3401 SelectObject( hdcSeek, hp );
\r
3404 // front-end wrapper for putting text in graph
\r
3405 void DrawSeekText(char *buf, int x, int y)
\r
3408 SetBkMode( hdcSeek, TRANSPARENT );
\r
3409 GetTextExtentPoint32( hdcSeek, buf, strlen(buf), &stSize );
\r
3410 TextOut( hdcSeek, boardRect.left+x-3, boardRect.top+y-stSize.cy/2, buf, strlen(buf) );
\r
3413 void DrawSeekDot(int x, int y, int color)
\r
3415 int square = color & 0x80;
\r
3416 HBRUSH oldBrush = SelectObject(hdcSeek,
\r
3417 color == 0 ? markerBrush : color == 1 ? darkSquareBrush : explodeBrush);
\r
3420 Rectangle(hdcSeek, boardRect.left+x - squareSize/9, boardRect.top+y - squareSize/9,
\r
3421 boardRect.left+x + squareSize/9, boardRect.top+y + squareSize/9);
\r
3423 Ellipse(hdcSeek, boardRect.left+x - squareSize/8, boardRect.top+y - squareSize/8,
\r
3424 boardRect.left+x + squareSize/8, boardRect.top+y + squareSize/8);
\r
3425 SelectObject(hdcSeek, oldBrush);
\r
3429 HDCDrawPosition(HDC hdc, BOOLEAN repaint, Board board)
\r
3431 static Board lastReq[2], lastDrawn[2];
\r
3432 static HighlightInfo lastDrawnHighlight, lastDrawnPremove;
\r
3433 static int lastDrawnFlipView = 0;
\r
3434 static int lastReqValid[2] = {0, 0}, lastDrawnValid[2] = {0, 0};
\r
3435 int releaseDC, x, y, x2, y2, row, column, num_clips = 0, i;
\r
3438 HBITMAP bufferBitmap;
\r
3439 HBITMAP oldBitmap;
\r
3441 HRGN clips[MAX_CLIPS];
\r
3442 ChessSquare dragged_piece = EmptySquare;
\r
3443 int nr = twoBoards*partnerUp;
\r
3445 /* I'm undecided on this - this function figures out whether a full
\r
3446 * repaint is necessary on its own, so there's no real reason to have the
\r
3447 * caller tell it that. I think this can safely be set to FALSE - but
\r
3448 * if we trust the callers not to request full repaints unnessesarily, then
\r
3449 * we could skip some clipping work. In other words, only request a full
\r
3450 * redraw when the majority of pieces have changed positions (ie. flip,
\r
3451 * gamestart and similar) --Hawk
\r
3453 Boolean fullrepaint = repaint;
\r
3455 if(DrawSeekGraph()) return; // [HG} seekgraph: suppress printing board if seek graph up
\r
3457 if( DrawPositionNeedsFullRepaint() ) {
\r
3458 fullrepaint = TRUE;
\r
3461 if (board == NULL) {
\r
3462 if (!lastReqValid[nr]) {
\r
3465 board = lastReq[nr];
\r
3467 CopyBoard(lastReq[nr], board);
\r
3468 lastReqValid[nr] = 1;
\r
3471 if (doingSizing) {
\r
3475 if (IsIconic(hwndMain)) {
\r
3479 if (hdc == NULL) {
\r
3480 hdc = GetDC(hwndMain);
\r
3481 if (!appData.monoMode) {
\r
3482 SelectPalette(hdc, hPal, FALSE);
\r
3483 RealizePalette(hdc);
\r
3487 releaseDC = FALSE;
\r
3490 /* Create some work-DCs */
\r
3491 hdcmem = CreateCompatibleDC(hdc);
\r
3492 tmphdc = CreateCompatibleDC(hdc);
\r
3494 /* If dragging is in progress, we temporarely remove the piece */
\r
3495 /* [HGM] or temporarily decrease count if stacked */
\r
3496 /* !! Moved to before board compare !! */
\r
3497 if (dragInfo.from.x >= 0 && dragInfo.pos.x >= 0) {
\r
3498 dragged_piece = board[dragInfo.from.y][dragInfo.from.x];
\r
3499 if(dragInfo.from.x == BOARD_LEFT-2 ) {
\r
3500 if(--board[dragInfo.from.y][dragInfo.from.x+1] == 0 )
\r
3501 board[dragInfo.from.y][dragInfo.from.x] = EmptySquare;
\r
3503 if(dragInfo.from.x == BOARD_RGHT+1) {
\r
3504 if(--board[dragInfo.from.y][dragInfo.from.x-1] == 0 )
\r
3505 board[dragInfo.from.y][dragInfo.from.x] = EmptySquare;
\r
3507 board[dragInfo.from.y][dragInfo.from.x] = EmptySquare;
\r
3510 /* Figure out which squares need updating by comparing the
\r
3511 * newest board with the last drawn board and checking if
\r
3512 * flipping has changed.
\r
3514 if (!fullrepaint && lastDrawnValid[nr] && (nr == 1 || lastDrawnFlipView == flipView)) {
\r
3515 for (row = 0; row < BOARD_HEIGHT; row++) { /* [HGM] true size, not 8 */
\r
3516 for (column = 0; column < BOARD_WIDTH; column++) {
\r
3517 if (lastDrawn[nr][row][column] != board[row][column]) {
\r
3518 SquareToPos(row, column, &x, &y);
\r
3519 clips[num_clips++] =
\r
3520 CreateRectRgn(x, y, x + squareSize, y + squareSize);
\r
3524 if(nr == 0) { // [HGM] dual: no highlights on second board
\r
3525 for (i=0; i<2; i++) {
\r
3526 if (lastDrawnHighlight.sq[i].x != highlightInfo.sq[i].x ||
\r
3527 lastDrawnHighlight.sq[i].y != highlightInfo.sq[i].y) {
\r
3528 if (lastDrawnHighlight.sq[i].x >= 0 &&
\r
3529 lastDrawnHighlight.sq[i].y >= 0) {
\r
3530 SquareToPos(lastDrawnHighlight.sq[i].y,
\r
3531 lastDrawnHighlight.sq[i].x, &x, &y);
\r
3532 clips[num_clips++] =
\r
3533 CreateRectRgn(x - lineGap, y - lineGap,
\r
3534 x + squareSize + lineGap, y + squareSize + lineGap);
\r
3536 if (highlightInfo.sq[i].x >= 0 && highlightInfo.sq[i].y >= 0) {
\r
3537 SquareToPos(highlightInfo.sq[i].y, highlightInfo.sq[i].x, &x, &y);
\r
3538 clips[num_clips++] =
\r
3539 CreateRectRgn(x - lineGap, y - lineGap,
\r
3540 x + squareSize + lineGap, y + squareSize + lineGap);
\r
3544 for (i=0; i<2; i++) {
\r
3545 if (lastDrawnPremove.sq[i].x != premoveHighlightInfo.sq[i].x ||
\r
3546 lastDrawnPremove.sq[i].y != premoveHighlightInfo.sq[i].y) {
\r
3547 if (lastDrawnPremove.sq[i].x >= 0 &&
\r
3548 lastDrawnPremove.sq[i].y >= 0) {
\r
3549 SquareToPos(lastDrawnPremove.sq[i].y,
\r
3550 lastDrawnPremove.sq[i].x, &x, &y);
\r
3551 clips[num_clips++] =
\r
3552 CreateRectRgn(x - lineGap, y - lineGap,
\r
3553 x + squareSize + lineGap, y + squareSize + lineGap);
\r
3555 if (premoveHighlightInfo.sq[i].x >= 0 &&
\r
3556 premoveHighlightInfo.sq[i].y >= 0) {
\r
3557 SquareToPos(premoveHighlightInfo.sq[i].y,
\r
3558 premoveHighlightInfo.sq[i].x, &x, &y);
\r
3559 clips[num_clips++] =
\r
3560 CreateRectRgn(x - lineGap, y - lineGap,
\r
3561 x + squareSize + lineGap, y + squareSize + lineGap);
\r
3565 } else { // nr == 1
\r
3566 partnerHighlightInfo.sq[0].y = board[EP_STATUS-4];
\r
3567 partnerHighlightInfo.sq[0].x = board[EP_STATUS-3];
\r
3568 partnerHighlightInfo.sq[1].y = board[EP_STATUS-2];
\r
3569 partnerHighlightInfo.sq[1].x = board[EP_STATUS-1];
\r
3570 for (i=0; i<2; i++) {
\r
3571 if (partnerHighlightInfo.sq[i].x >= 0 &&
\r
3572 partnerHighlightInfo.sq[i].y >= 0) {
\r
3573 SquareToPos(partnerHighlightInfo.sq[i].y,
\r
3574 partnerHighlightInfo.sq[i].x, &x, &y);
\r
3575 clips[num_clips++] =
\r
3576 CreateRectRgn(x - lineGap, y - lineGap,
\r
3577 x + squareSize + lineGap, y + squareSize + lineGap);
\r
3579 if (oldPartnerHighlight.sq[i].x >= 0 &&
\r
3580 oldPartnerHighlight.sq[i].y >= 0) {
\r
3581 SquareToPos(oldPartnerHighlight.sq[i].y,
\r
3582 oldPartnerHighlight.sq[i].x, &x, &y);
\r
3583 clips[num_clips++] =
\r
3584 CreateRectRgn(x - lineGap, y - lineGap,
\r
3585 x + squareSize + lineGap, y + squareSize + lineGap);
\r
3590 fullrepaint = TRUE;
\r
3593 /* Create a buffer bitmap - this is the actual bitmap
\r
3594 * being written to. When all the work is done, we can
\r
3595 * copy it to the real DC (the screen). This avoids
\r
3596 * the problems with flickering.
\r
3598 GetClientRect(hwndMain, &Rect);
\r
3599 bufferBitmap = CreateCompatibleBitmap(hdc, Rect.right-Rect.left+1,
\r
3600 Rect.bottom-Rect.top+1);
\r
3601 oldBitmap = SelectObject(hdcmem, bufferBitmap);
\r
3602 if (!appData.monoMode) {
\r
3603 SelectPalette(hdcmem, hPal, FALSE);
\r
3606 /* Create clips for dragging */
\r
3607 if (!fullrepaint) {
\r
3608 if (dragInfo.from.x >= 0) {
\r
3609 SquareToPos(dragInfo.from.y, dragInfo.from.x, &x, &y);
\r
3610 clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);
\r
3612 if (dragInfo.start.x >= 0) {
\r
3613 SquareToPos(dragInfo.start.y, dragInfo.start.x, &x, &y);
\r
3614 clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);
\r
3616 if (dragInfo.pos.x >= 0) {
\r
3617 x = dragInfo.pos.x - squareSize / 2;
\r
3618 y = dragInfo.pos.y - squareSize / 2;
\r
3619 clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);
\r
3621 if (dragInfo.lastpos.x >= 0) {
\r
3622 x = dragInfo.lastpos.x - squareSize / 2;
\r
3623 y = dragInfo.lastpos.y - squareSize / 2;
\r
3624 clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);
\r
3628 /* Are we animating a move?
\r
3630 * - remove the piece from the board (temporarely)
\r
3631 * - calculate the clipping region
\r
3633 if (!fullrepaint) {
\r
3634 if (animInfo.piece != EmptySquare) {
\r
3635 board[animInfo.from.y][animInfo.from.x] = EmptySquare;
\r
3636 x = boardRect.left + animInfo.lastpos.x;
\r
3637 y = boardRect.top + animInfo.lastpos.y;
\r
3638 x2 = boardRect.left + animInfo.pos.x;
\r
3639 y2 = boardRect.top + animInfo.pos.y;
\r
3640 clips[num_clips++] = CreateRectRgn(MIN(x,x2), MIN(y,y2), MAX(x,x2)+squareSize, MAX(y,y2)+squareSize);
\r
3641 /* Slight kludge. The real problem is that after AnimateMove is
\r
3642 done, the position on the screen does not match lastDrawn.
\r
3643 This currently causes trouble only on e.p. captures in
\r
3644 atomic, where the piece moves to an empty square and then
\r
3645 explodes. The old and new positions both had an empty square
\r
3646 at the destination, but animation has drawn a piece there and
\r
3647 we have to remember to erase it. [HGM] moved until after setting lastDrawn */
\r
3648 lastDrawn[0][animInfo.to.y][animInfo.to.x] = animInfo.piece;
\r
3652 /* No clips? Make sure we have fullrepaint set to TRUE */
\r
3653 if (num_clips == 0)
\r
3654 fullrepaint = TRUE;
\r
3656 /* Set clipping on the memory DC */
\r
3657 if (!fullrepaint) {
\r
3658 SelectClipRgn(hdcmem, clips[0]);
\r
3659 for (x = 1; x < num_clips; x++) {
\r
3660 if (ExtSelectClipRgn(hdcmem, clips[x], RGN_OR) == ERROR)
\r
3661 abort(); // this should never ever happen!
\r
3665 /* Do all the drawing to the memory DC */
\r
3666 if(explodeInfo.radius) { // [HGM] atomic
\r
3668 int x, y, r=(explodeInfo.radius * squareSize)/100;
\r
3669 ChessSquare piece = board[explodeInfo.fromY][explodeInfo.fromX];
\r
3670 board[explodeInfo.fromY][explodeInfo.fromX] = EmptySquare; // suppress display of capturer
\r
3671 SquareToPos(explodeInfo.toY, explodeInfo.toX, &x, &y);
\r
3672 x += squareSize/2;
\r
3673 y += squareSize/2;
\r
3674 if(!fullrepaint) {
\r
3675 clips[num_clips] = CreateRectRgn(x-r, y-r, x+r, y+r);
\r
3676 ExtSelectClipRgn(hdcmem, clips[num_clips++], RGN_OR);
\r
3678 DrawGridOnDC(hdcmem);
\r
3679 DrawHighlightsOnDC(hdcmem, &highlightInfo, HIGHLIGHT_PEN);
\r
3680 DrawHighlightsOnDC(hdcmem, &premoveHighlightInfo, PREMOVE_PEN);
\r
3681 DrawBoardOnDC(hdcmem, board, tmphdc);
\r
3682 board[explodeInfo.fromY][explodeInfo.fromX] = piece;
\r
3683 oldBrush = SelectObject(hdcmem, explodeBrush);
\r
3684 Ellipse(hdcmem, x-r, y-r, x+r, y+r);
\r
3685 SelectObject(hdcmem, oldBrush);
\r
3687 DrawGridOnDC(hdcmem);
\r
3688 if(nr == 0) { // [HGM] dual: decide which highlights to draw
\r
3689 DrawHighlightsOnDC(hdcmem, &highlightInfo, HIGHLIGHT_PEN);
\r
3690 DrawHighlightsOnDC(hdcmem, &premoveHighlightInfo, PREMOVE_PEN);
\r
3692 DrawHighlightsOnDC(hdcmem, &partnerHighlightInfo, HIGHLIGHT_PEN);
\r
3693 oldPartnerHighlight = partnerHighlightInfo;
\r
3695 DrawBoardOnDC(hdcmem, board, tmphdc);
\r
3697 if(nr == 0) // [HGM] dual: markers only on left board
\r
3698 for (row = 0; row < BOARD_HEIGHT; row++) {
\r
3699 for (column = 0; column < BOARD_WIDTH; column++) {
\r
3700 if (marker[row][column]) { // marker changes only occur with full repaint!
\r
3701 HBRUSH oldBrush = SelectObject(hdcmem,
\r
3702 marker[row][column] == 2 ? markerBrush : explodeBrush);
\r
3703 SquareToPos(row, column, &x, &y);
\r
3704 Ellipse(hdcmem, x + squareSize/4, y + squareSize/4,
\r
3705 x + 3*squareSize/4, y + 3*squareSize/4);
\r
3706 SelectObject(hdcmem, oldBrush);
\r
3711 HBITMAP whiteLogo = (HBITMAP) first.programLogo, blackLogo = (HBITMAP) second.programLogo;
\r
3712 if(appData.autoLogo) {
\r
3714 switch(gameMode) { // pick logos based on game mode
\r
3715 case IcsObserving:
\r
3716 whiteLogo = second.programLogo; // ICS logo
\r
3717 blackLogo = second.programLogo;
\r
3720 case IcsPlayingWhite:
\r
3721 if(!appData.zippyPlay) whiteLogo = userLogo;
\r
3722 blackLogo = second.programLogo; // ICS logo
\r
3724 case IcsPlayingBlack:
\r
3725 whiteLogo = second.programLogo; // ICS logo
\r
3726 blackLogo = appData.zippyPlay ? first.programLogo : userLogo;
\r
3728 case TwoMachinesPlay:
\r
3729 if(first.twoMachinesColor[0] == 'b') {
\r
3730 whiteLogo = second.programLogo;
\r
3731 blackLogo = first.programLogo;
\r
3734 case MachinePlaysWhite:
\r
3735 blackLogo = userLogo;
\r
3737 case MachinePlaysBlack:
\r
3738 whiteLogo = userLogo;
\r
3739 blackLogo = first.programLogo;
\r
3742 DrawLogoOnDC(hdc, leftLogoRect, flipClock ? blackLogo : whiteLogo);
\r
3743 DrawLogoOnDC(hdc, rightLogoRect, flipClock ? whiteLogo : blackLogo);
\r
3746 if( appData.highlightMoveWithArrow ) {
\r
3747 DrawArrowHighlight(hdcmem);
\r
3750 DrawCoordsOnDC(hdcmem);
\r
3752 CopyBoard(lastDrawn[nr], board); /* [HGM] Moved to here from end of routine, */
\r
3753 /* to make sure lastDrawn contains what is actually drawn */
\r
3755 /* Put the dragged piece back into place and draw it (out of place!) */
\r
3756 if (dragged_piece != EmptySquare) {
\r
3757 /* [HGM] or restack */
\r
3758 if(dragInfo.from.x == BOARD_LEFT-2 )
\r
3759 board[dragInfo.from.y][dragInfo.from.x+1]++;
\r
3761 if(dragInfo.from.x == BOARD_RGHT+1 )
\r
3762 board[dragInfo.from.y][dragInfo.from.x-1]++;
\r
3763 board[dragInfo.from.y][dragInfo.from.x] = dragged_piece;
\r
3764 x = dragInfo.pos.x - squareSize / 2;
\r
3765 y = dragInfo.pos.y - squareSize / 2;
\r
3766 DrawPieceOnDC(hdcmem, dragged_piece,
\r
3767 ((int) dragged_piece < (int) BlackPawn),
\r
3768 (dragInfo.from.y + dragInfo.from.x) % 2, x, y, tmphdc);
\r
3771 /* Put the animated piece back into place and draw it */
\r
3772 if (animInfo.piece != EmptySquare) {
\r
3773 board[animInfo.from.y][animInfo.from.x] = animInfo.piece;
\r
3774 x = boardRect.left + animInfo.pos.x;
\r
3775 y = boardRect.top + animInfo.pos.y;
\r
3776 DrawPieceOnDC(hdcmem, animInfo.piece,
\r
3777 ((int) animInfo.piece < (int) BlackPawn),
\r
3778 (animInfo.from.y + animInfo.from.x) % 2, x, y, tmphdc);
\r
3781 /* Release the bufferBitmap by selecting in the old bitmap
\r
3782 * and delete the memory DC
\r
3784 SelectObject(hdcmem, oldBitmap);
\r
3787 /* Set clipping on the target DC */
\r
3788 if (!fullrepaint) {
\r
3789 if(nr == 1) for (x = 0; x < num_clips; x++) { // [HGM] dual: translate clips
\r
3791 GetRgnBox(clips[x], &rect);
\r
3792 DeleteObject(clips[x]);
\r
3793 clips[x] = CreateRectRgn(rect.left + wpMain.width/2, rect.top,
\r
3794 rect.right + wpMain.width/2, rect.bottom);
\r
3796 SelectClipRgn(hdc, clips[0]);
\r
3797 for (x = 1; x < num_clips; x++) {
\r
3798 if (ExtSelectClipRgn(hdc, clips[x], RGN_OR) == ERROR)
\r
3799 abort(); // this should never ever happen!
\r
3803 /* Copy the new bitmap onto the screen in one go.
\r
3804 * This way we avoid any flickering
\r
3806 oldBitmap = SelectObject(tmphdc, bufferBitmap);
\r
3807 BitBlt(hdc, boardRect.left + twoBoards*partnerUp*wpMain.width/2, boardRect.top, // [HGM] dual
\r
3808 boardRect.right - boardRect.left,
\r
3809 boardRect.bottom - boardRect.top,
\r
3810 tmphdc, boardRect.left, boardRect.top, SRCCOPY);
\r
3811 if(saveDiagFlag) {
\r
3812 BITMAP b; int i, j=0, m, w, wb, fac=0; char pData[1000000];
\r
3813 BITMAPINFOHEADER bih; int color[16], nrColors=0;
\r
3815 GetObject(bufferBitmap, sizeof(b), &b);
\r
3816 if(b.bmWidthBytes*b.bmHeight <= 990000) {
\r
3817 bih.biSize = sizeof(BITMAPINFOHEADER);
\r
3818 bih.biWidth = b.bmWidth;
\r
3819 bih.biHeight = b.bmHeight;
\r
3821 bih.biBitCount = b.bmBitsPixel;
\r
3822 bih.biCompression = 0;
\r
3823 bih.biSizeImage = b.bmWidthBytes*b.bmHeight;
\r
3824 bih.biXPelsPerMeter = 0;
\r
3825 bih.biYPelsPerMeter = 0;
\r
3826 bih.biClrUsed = 0;
\r
3827 bih.biClrImportant = 0;
\r
3828 // fprintf(diagFile, "t=%d\nw=%d\nh=%d\nB=%d\nP=%d\nX=%d\n",
\r
3829 // b.bmType, b.bmWidth, b.bmHeight, b.bmWidthBytes, b.bmPlanes, b.bmBitsPixel);
\r
3830 GetDIBits(tmphdc,bufferBitmap,0,b.bmHeight,pData,(BITMAPINFO*)&bih,DIB_RGB_COLORS);
\r
3831 // fprintf(diagFile, "%8x\n", (int) pData);
\r
3833 wb = b.bmWidthBytes;
\r
3835 for(i=0; i<wb*(b.bmHeight - boardRect.top + OUTER_MARGIN)>>2; i++) {
\r
3836 int k = ((int*) pData)[i];
\r
3837 for(j=0; j<nrColors; j++) if(color[j] == k) break;
\r
3838 if(j >= 16) break;
\r
3840 if(j >= nrColors) nrColors = j+1;
\r
3842 if(j<16) { // 16 colors is enough. Compress to 4 bits per pixel
\r
3844 for(i=0; i<b.bmHeight - boardRect.top + OUTER_MARGIN; i++) {
\r
3845 for(w=0; w<(wb>>2); w+=2) {
\r
3846 int k = ((int*) pData)[(wb*i>>2) + w];
\r
3847 for(j=0; j<nrColors; j++) if(color[j] == k) break;
\r
3848 k = ((int*) pData)[(wb*i>>2) + w + 1];
\r
3849 for(m=0; m<nrColors; m++) if(color[m] == k) break;
\r
3850 pData[p++] = m | j<<4;
\r
3852 while(p&3) pData[p++] = 0;
\r
3855 wb = ((wb+31)>>5)<<2;
\r
3857 // write BITMAPFILEHEADER
\r
3858 fprintf(diagFile, "BM");
\r
3859 fputDW(diagFile, wb*(b.bmHeight - boardRect.top + OUTER_MARGIN)+0x36 + (fac?64:0));
\r
3860 fputDW(diagFile, 0);
\r
3861 fputDW(diagFile, 0x36 + (fac?64:0));
\r
3862 // write BITMAPINFOHEADER
\r
3863 fputDW(diagFile, 40);
\r
3864 fputDW(diagFile, b.bmWidth);
\r
3865 fputDW(diagFile, b.bmHeight - boardRect.top + OUTER_MARGIN);
\r
3866 if(fac) fputDW(diagFile, 0x040001); // planes and bits/pixel
\r
3867 else fputDW(diagFile, 0x200001); // planes and bits/pixel
\r
3868 fputDW(diagFile, 0);
\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 // write color table
\r
3876 for(i=0; i<16; i++) fputDW(diagFile, color[i]);
\r
3877 // write bitmap data
\r
3878 for(i=0; i<wb*(b.bmHeight - boardRect.top + OUTER_MARGIN); i++)
\r
3879 fputc(pData[i], diagFile);
\r
3883 SelectObject(tmphdc, oldBitmap);
\r
3885 /* Massive cleanup */
\r
3886 for (x = 0; x < num_clips; x++)
\r
3887 DeleteObject(clips[x]);
\r
3890 DeleteObject(bufferBitmap);
\r
3893 ReleaseDC(hwndMain, hdc);
\r
3895 if (lastDrawnFlipView != flipView && nr == 0) {
\r
3897 CheckMenuItem(GetMenu(hwndMain),IDM_FlipView, MF_BYCOMMAND|MF_CHECKED);
\r
3899 CheckMenuItem(GetMenu(hwndMain),IDM_FlipView, MF_BYCOMMAND|MF_UNCHECKED);
\r
3902 /* CopyBoard(lastDrawn, board);*/
\r
3903 lastDrawnHighlight = highlightInfo;
\r
3904 lastDrawnPremove = premoveHighlightInfo;
\r
3905 lastDrawnFlipView = flipView;
\r
3906 lastDrawnValid[nr] = 1;
\r
3909 /* [HGM] diag: Save the current board display to the given open file and close the file */
\r
3914 saveDiagFlag = 1; diagFile = f;
\r
3915 HDCDrawPosition(NULL, TRUE, NULL);
\r
3919 // if(f != NULL) fprintf(f, "Sorry, but this feature is still in preparation\n");
\r
3926 /*---------------------------------------------------------------------------*\
\r
3927 | CLIENT PAINT PROCEDURE
\r
3928 | This is the main event-handler for the WM_PAINT message.
\r
3930 \*---------------------------------------------------------------------------*/
\r
3932 PaintProc(HWND hwnd)
\r
3938 if((hdc = BeginPaint(hwnd, &ps))) {
\r
3939 if (IsIconic(hwnd)) {
\r
3940 DrawIcon(hdc, 2, 2, iconCurrent);
\r
3942 if (!appData.monoMode) {
\r
3943 SelectPalette(hdc, hPal, FALSE);
\r
3944 RealizePalette(hdc);
\r
3946 HDCDrawPosition(hdc, 1, NULL);
\r
3947 if(twoBoards) { // [HGM] dual: also redraw other board in other orientation
\r
3948 flipView = !flipView; partnerUp = !partnerUp;
\r
3949 HDCDrawPosition(hdc, 1, NULL);
\r
3950 flipView = !flipView; partnerUp = !partnerUp;
\r
3953 SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);
\r
3954 ExtTextOut(hdc, messageRect.left, messageRect.top,
\r
3955 ETO_CLIPPED|ETO_OPAQUE,
\r
3956 &messageRect, messageText, strlen(messageText), NULL);
\r
3957 SelectObject(hdc, oldFont);
\r
3958 DisplayBothClocks();
\r
3960 EndPaint(hwnd,&ps);
\r
3968 * If the user selects on a border boundary, return -1; if off the board,
\r
3969 * return -2. Otherwise map the event coordinate to the square.
\r
3970 * The offset boardRect.left or boardRect.top must already have been
\r
3971 * subtracted from x.
\r
3973 int EventToSquare(x, limit)
\r
3981 if ((x % (squareSize + lineGap)) >= squareSize)
\r
3983 x /= (squareSize + lineGap);
\r
3995 DropEnable dropEnables[] = {
\r
3996 { 'P', DP_Pawn, N_("Pawn") },
\r
3997 { 'N', DP_Knight, N_("Knight") },
\r
3998 { 'B', DP_Bishop, N_("Bishop") },
\r
3999 { 'R', DP_Rook, N_("Rook") },
\r
4000 { 'Q', DP_Queen, N_("Queen") },
\r
4004 SetupDropMenu(HMENU hmenu)
\r
4006 int i, count, enable;
\r
4008 extern char white_holding[], black_holding[];
\r
4009 char item[MSG_SIZ];
\r
4011 for (i=0; i<sizeof(dropEnables)/sizeof(DropEnable); i++) {
\r
4012 p = strchr(gameMode == IcsPlayingWhite ? white_holding : black_holding,
\r
4013 dropEnables[i].piece);
\r
4015 while (p && *p++ == dropEnables[i].piece) count++;
\r
4016 snprintf(item, MSG_SIZ, "%s %d", T_(dropEnables[i].name), count);
\r
4017 enable = count > 0 || !appData.testLegality
\r
4018 /*!!temp:*/ || (gameInfo.variant == VariantCrazyhouse
\r
4019 && !appData.icsActive);
\r
4020 ModifyMenu(hmenu, dropEnables[i].command,
\r
4021 MF_BYCOMMAND | (enable ? MF_ENABLED : MF_GRAYED) | MF_STRING,
\r
4022 dropEnables[i].command, item);
\r
4026 void DragPieceBegin(int x, int y)
\r
4028 dragInfo.lastpos.x = boardRect.left + x;
\r
4029 dragInfo.lastpos.y = boardRect.top + y;
\r
4030 dragInfo.from.x = fromX;
\r
4031 dragInfo.from.y = fromY;
\r
4032 dragInfo.start = dragInfo.from;
\r
4033 SetCapture(hwndMain);
\r
4036 void DragPieceEnd(int x, int y)
\r
4039 dragInfo.start.x = dragInfo.start.y = -1;
\r
4040 dragInfo.from = dragInfo.start;
\r
4041 dragInfo.pos = dragInfo.lastpos = dragInfo.start;
\r
4044 /* Event handler for mouse messages */
\r
4046 MouseEvent(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
\r
4050 static int recursive = 0;
\r
4052 BOOLEAN forceFullRepaint = IsFullRepaintPreferrable(); /* [AS] */
\r
4055 if (message == WM_MBUTTONUP) {
\r
4056 /* Hideous kludge to fool TrackPopupMenu into paying attention
\r
4057 to the middle button: we simulate pressing the left button too!
\r
4059 PostMessage(hwnd, WM_LBUTTONDOWN, wParam, lParam);
\r
4060 PostMessage(hwnd, WM_LBUTTONUP, wParam, lParam);
\r
4066 pt.x = LOWORD(lParam);
\r
4067 pt.y = HIWORD(lParam);
\r
4068 x = EventToSquare(pt.x - boardRect.left, BOARD_WIDTH);
\r
4069 y = EventToSquare(pt.y - boardRect.top, BOARD_HEIGHT);
\r
4070 if (!flipView && y >= 0) {
\r
4071 y = BOARD_HEIGHT - 1 - y;
\r
4073 if (flipView && x >= 0) {
\r
4074 x = BOARD_WIDTH - 1 - x;
\r
4077 shiftKey = GetKeyState(VK_SHIFT) < 0; // [HGM] remember last shift status
\r
4079 switch (message) {
\r
4080 case WM_LBUTTONDOWN:
\r
4081 if (PtInRect((LPRECT) &whiteRect, pt)) {
\r
4082 if (gameMode == EditPosition) {
\r
4083 SetWhiteToPlayEvent();
\r
4084 } else if (gameMode == EditGame || GetKeyState(VK_SHIFT) < 0) {
\r
4085 AdjustClock(flipClock, -1);
\r
4086 } else if (gameMode == IcsPlayingBlack ||
\r
4087 gameMode == MachinePlaysWhite) {
\r
4090 } else if (PtInRect((LPRECT) &blackRect, pt)) {
\r
4091 if (gameMode == EditPosition) {
\r
4092 SetBlackToPlayEvent();
\r
4093 } else if (gameMode == EditGame || GetKeyState(VK_SHIFT) < 0) {
\r
4094 AdjustClock(!flipClock, -1);
\r
4095 } else if (gameMode == IcsPlayingWhite ||
\r
4096 gameMode == MachinePlaysBlack) {
\r
4100 dragInfo.start.x = dragInfo.start.y = -1;
\r
4101 dragInfo.from = dragInfo.start;
\r
4102 if(fromX == -1 && frozen) { // not sure where this is for
\r
4103 fromX = fromY = -1;
\r
4104 DrawPosition(forceFullRepaint || FALSE, NULL); /* [AS] */
\r
4107 LeftClick(Press, pt.x - boardRect.left, pt.y - boardRect.top);
\r
4108 DrawPosition(TRUE, NULL);
\r
4111 case WM_LBUTTONUP:
\r
4112 LeftClick(Release, pt.x - boardRect.left, pt.y - boardRect.top);
\r
4113 DrawPosition(TRUE, NULL);
\r
4116 case WM_MOUSEMOVE:
\r
4117 if(SeekGraphClick(Press, pt.x - boardRect.left, pt.y - boardRect.top, 1)) break;
\r
4118 MovePV(pt.x - boardRect.left, pt.y - boardRect.top, boardRect.bottom - boardRect.top);
\r
4119 if ((appData.animateDragging || appData.highlightDragging)
\r
4120 && (wParam & MK_LBUTTON)
\r
4121 && dragInfo.from.x >= 0)
\r
4123 BOOL full_repaint = FALSE;
\r
4125 if (appData.animateDragging) {
\r
4126 dragInfo.pos = pt;
\r
4128 if (appData.highlightDragging) {
\r
4129 SetHighlights(fromX, fromY, x, y);
\r
4130 if( IsDrawArrowEnabled() && (x < 0 || x >= BOARD_WIDTH || y < 0 || y >= BOARD_HEIGHT) ) {
\r
4131 full_repaint = TRUE;
\r
4135 DrawPosition( full_repaint, NULL);
\r
4137 dragInfo.lastpos = dragInfo.pos;
\r
4141 case WM_MOUSEWHEEL: // [DM]
\r
4142 { static int lastDir = 0; // [HGM] build in some hysteresis to avoid spurious events
\r
4143 /* Mouse Wheel is being rolled forward
\r
4144 * Play moves forward
\r
4146 if((short)HIWORD(wParam) < 0 && currentMove < forwardMostMove)
\r
4147 { if(lastDir == 1) ForwardEvent(); else lastDir = 1; } // [HGM] suppress first event in direction
\r
4148 /* Mouse Wheel is being rolled backward
\r
4149 * Play moves backward
\r
4151 if((short)HIWORD(wParam) > 0 && currentMove > backwardMostMove)
\r
4152 { if(lastDir == -1) BackwardEvent(); else lastDir = -1; }
\r
4156 case WM_MBUTTONUP:
\r
4157 case WM_RBUTTONUP:
\r
4159 RightClick(Release, pt.x - boardRect.left, pt.y - boardRect.top, &fromX, &fromY);
\r
4162 case WM_MBUTTONDOWN:
\r
4163 case WM_RBUTTONDOWN:
\r
4166 fromX = fromY = -1;
\r
4167 dragInfo.pos.x = dragInfo.pos.y = -1;
\r
4168 dragInfo.start.x = dragInfo.start.y = -1;
\r
4169 dragInfo.from = dragInfo.start;
\r
4170 dragInfo.lastpos = dragInfo.pos;
\r
4171 if (appData.highlightDragging) {
\r
4172 ClearHighlights();
\r
4175 /* [HGM] right mouse button in clock area edit-game mode ups clock */
\r
4176 if (PtInRect((LPRECT) &whiteRect, pt)) {
\r
4177 if (gameMode == EditGame || GetKeyState(VK_SHIFT) < 0) AdjustClock(flipClock, 1);
\r
4178 } else if (PtInRect((LPRECT) &blackRect, pt)) {
\r
4179 if (gameMode == EditGame || GetKeyState(VK_SHIFT) < 0) AdjustClock(!flipClock, 1);
\r
4183 DrawPosition(TRUE, NULL);
\r
4185 menuNr = RightClick(Press, pt.x - boardRect.left, pt.y - boardRect.top, &fromX, &fromY);
\r
4188 if (message == WM_MBUTTONDOWN) {
\r
4189 buttonCount = 3; /* even if system didn't think so */
\r
4190 if (wParam & MK_SHIFT)
\r
4191 MenuPopup(hwnd, pt, LoadMenu(hInst, "BlackPieceMenu"), -1);
\r
4193 MenuPopup(hwnd, pt, LoadMenu(hInst, "WhitePieceMenu"), -1);
\r
4194 } else { /* message == WM_RBUTTONDOWN */
\r
4195 /* Just have one menu, on the right button. Windows users don't
\r
4196 think to try the middle one, and sometimes other software steals
\r
4197 it, or it doesn't really exist. */
\r
4198 if(gameInfo.variant != VariantShogi)
\r
4199 MenuPopup(hwnd, pt, LoadMenu(hInst, "PieceMenu"), -1);
\r
4201 MenuPopup(hwnd, pt, LoadMenu(hInst, "ShogiPieceMenu"), -1);
\r
4205 SetCapture(hwndMain);
4208 hmenu = LoadMenu(hInst, "DropPieceMenu");
\r
4209 SetupDropMenu(hmenu);
\r
4210 MenuPopup(hwnd, pt, hmenu, -1);
\r
4220 /* Preprocess messages for buttons in main window */
\r
4222 ButtonProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
\r
4224 int id = GetWindowLong(hwnd, GWL_ID);
\r
4227 for (i=0; i<N_BUTTONS; i++) {
\r
4228 if (buttonDesc[i].id == id) break;
\r
4230 if (i == N_BUTTONS) return 0;
\r
4231 switch (message) {
\r
4236 dir = (wParam == VK_LEFT) ? -1 : 1;
\r
4237 SetFocus(buttonDesc[(i + dir + N_BUTTONS) % N_BUTTONS].hwnd);
\r
4244 SendMessage(hwndMain, WM_COMMAND, MAKEWPARAM(buttonDesc[i].id, 0), 0);
\r
4247 if (appData.icsActive && (isalpha((char)wParam) || wParam == '0')) {
\r
4248 // [HGM] movenum: only letters or leading zero should go to ICS input
\r
4249 HWND h = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
4250 if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);
\r
4252 SendMessage(h, WM_CHAR, wParam, lParam);
\r
4254 } else if (isalpha((char)wParam) || isdigit((char)wParam)){
\r
4255 PopUpMoveDialog((char)wParam);
\r
4261 return CallWindowProc(buttonDesc[i].wndproc, hwnd, message, wParam, lParam);
\r
4264 /* Process messages for Promotion dialog box */
\r
4266 Promotion(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
4270 switch (message) {
\r
4271 case WM_INITDIALOG: /* message: initialize dialog box */
\r
4272 /* Center the dialog over the application window */
\r
4273 CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));
\r
4274 Translate(hDlg, DLG_PromotionKing);
\r
4275 ShowWindow(GetDlgItem(hDlg, PB_King),
\r
4276 (!appData.testLegality || gameInfo.variant == VariantSuicide ||
\r
4277 gameInfo.variant == VariantGiveaway || gameInfo.variant == VariantSuper ) ?
\r
4278 SW_SHOW : SW_HIDE);
\r
4279 /* [HGM] Only allow C & A promotions if these pieces are defined */
\r
4280 ShowWindow(GetDlgItem(hDlg, PB_Archbishop),
\r
4281 ((PieceToChar(WhiteAngel) >= 'A' &&
\r
4282 PieceToChar(WhiteAngel) != '~') ||
\r
4283 (PieceToChar(BlackAngel) >= 'A' &&
\r
4284 PieceToChar(BlackAngel) != '~') ) ?
\r
4285 SW_SHOW : SW_HIDE);
\r
4286 ShowWindow(GetDlgItem(hDlg, PB_Chancellor),
\r
4287 ((PieceToChar(WhiteMarshall) >= 'A' &&
\r
4288 PieceToChar(WhiteMarshall) != '~') ||
\r
4289 (PieceToChar(BlackMarshall) >= 'A' &&
\r
4290 PieceToChar(BlackMarshall) != '~') ) ?
\r
4291 SW_SHOW : SW_HIDE);
\r
4292 /* [HGM] Hide B & R button in Shogi, use Q as promote, N as defer */
\r
4293 ShowWindow(GetDlgItem(hDlg, PB_Rook),
\r
4294 gameInfo.variant != VariantShogi ?
\r
4295 SW_SHOW : SW_HIDE);
\r
4296 ShowWindow(GetDlgItem(hDlg, PB_Bishop),
\r
4297 gameInfo.variant != VariantShogi ?
\r
4298 SW_SHOW : SW_HIDE);
\r
4299 ShowWindow(GetDlgItem(hDlg, IDC_Yes),
\r
4300 gameInfo.variant == VariantShogi ?
\r
4301 SW_SHOW : SW_HIDE);
\r
4302 ShowWindow(GetDlgItem(hDlg, IDC_No),
\r
4303 gameInfo.variant == VariantShogi ?
\r
4304 SW_SHOW : SW_HIDE);
\r
4305 ShowWindow(GetDlgItem(hDlg, IDC_Centaur),
\r
4306 gameInfo.variant == VariantSuper ?
\r
4307 SW_SHOW : SW_HIDE);
\r
4310 case WM_COMMAND: /* message: received a command */
\r
4311 switch (LOWORD(wParam)) {
\r
4313 EndDialog(hDlg, TRUE); /* Exit the dialog */
\r
4314 ClearHighlights();
\r
4315 DrawPosition(FALSE, NULL);
\r
4318 promoChar = gameInfo.variant == VariantSuper ? PieceToChar(BlackSilver) : PieceToChar(BlackKing);
\r
4321 promoChar = gameInfo.variant == VariantShogi ? '+' : PieceToChar(BlackQueen);
\r
4324 promoChar = PieceToChar(BlackRook);
\r
4327 promoChar = PieceToChar(BlackBishop);
\r
4329 case PB_Chancellor:
\r
4330 promoChar = PieceToChar(BlackMarshall);
\r
4332 case PB_Archbishop:
\r
4333 promoChar = PieceToChar(BlackAngel);
\r
4336 promoChar = gameInfo.variant == VariantShogi ? '=' : PieceToChar(BlackKnight);
\r
4341 EndDialog(hDlg, TRUE); /* Exit the dialog */
\r
4342 /* [HGM] <popupFix> Call FinishMove rather than UserMoveEvent, as we
\r
4343 only show the popup when we are already sure the move is valid or
\r
4344 legal. We pass a faulty move type, but the kludge is that FinishMove
\r
4345 will figure out it is a promotion from the promoChar. */
\r
4346 UserMoveEvent(fromX, fromY, toX, toY, promoChar);
\r
4347 fromX = fromY = -1;
\r
4348 if (!appData.highlightLastMove) {
\r
4349 ClearHighlights();
\r
4350 DrawPosition(FALSE, NULL);
\r
4357 /* Pop up promotion dialog */
\r
4359 PromotionPopup(HWND hwnd)
\r
4363 lpProc = MakeProcInstance((FARPROC)Promotion, hInst);
\r
4364 DialogBox(hInst, MAKEINTRESOURCE(DLG_PromotionKing),
\r
4365 hwnd, (DLGPROC)lpProc);
\r
4366 FreeProcInstance(lpProc);
\r
4372 DrawPosition(TRUE, NULL);
\r
4373 PromotionPopup(hwndMain);
\r
4376 /* Toggle ShowThinking */
\r
4378 ToggleShowThinking()
\r
4380 appData.showThinking = !appData.showThinking;
\r
4381 ShowThinkingEvent();
\r
4385 LoadGameDialog(HWND hwnd, char* title)
\r
4389 char fileTitle[MSG_SIZ];
\r
4390 f = OpenFileDialog(hwnd, "rb", "",
\r
4391 appData.oldSaveStyle ? "gam" : "pgn",
\r
4393 title, &number, fileTitle, NULL);
\r
4395 cmailMsgLoaded = FALSE;
\r
4396 if (number == 0) {
\r
4397 int error = GameListBuild(f);
\r
4399 DisplayError(_("Cannot build game list"), error);
\r
4400 } else if (!ListEmpty(&gameList) &&
\r
4401 ((ListGame *) gameList.tailPred)->number > 1) {
\r
4402 GameListPopUp(f, fileTitle);
\r
4405 GameListDestroy();
\r
4408 LoadGame(f, number, fileTitle, FALSE);
\r
4412 int get_term_width()
\r
4417 HFONT hfont, hold_font;
\r
4422 hText = GetDlgItem(hwndConsole, OPT_ConsoleText);
\r
4426 // get the text metrics
\r
4427 hdc = GetDC(hText);
\r
4428 lf = font[boardSize][CONSOLE_FONT]->lf;
\r
4429 if (consoleCF.dwEffects & CFE_BOLD)
\r
4430 lf.lfWeight = FW_BOLD;
\r
4431 if (consoleCF.dwEffects & CFE_ITALIC)
\r
4432 lf.lfItalic = TRUE;
\r
4433 if (consoleCF.dwEffects & CFE_STRIKEOUT)
\r
4434 lf.lfStrikeOut = TRUE;
\r
4435 if (consoleCF.dwEffects & CFE_UNDERLINE)
\r
4436 lf.lfUnderline = TRUE;
\r
4437 hfont = CreateFontIndirect(&lf);
\r
4438 hold_font = SelectObject(hdc, hfont);
\r
4439 GetTextMetrics(hdc, &tm);
\r
4440 SelectObject(hdc, hold_font);
\r
4441 DeleteObject(hfont);
\r
4442 ReleaseDC(hText, hdc);
\r
4444 // get the rectangle
\r
4445 SendMessage(hText, EM_GETRECT, 0, (LPARAM)&rc);
\r
4447 return (rc.right-rc.left) / tm.tmAveCharWidth;
\r
4450 void UpdateICSWidth(HWND hText)
\r
4452 LONG old_width, new_width;
\r
4454 new_width = get_term_width(hText, FALSE);
\r
4455 old_width = GetWindowLong(hText, GWL_USERDATA);
\r
4456 if (new_width != old_width)
\r
4458 ics_update_width(new_width);
\r
4459 SetWindowLong(hText, GWL_USERDATA, new_width);
\r
4464 ChangedConsoleFont()
\r
4467 CHARRANGE tmpsel, sel;
\r
4468 MyFont *f = font[boardSize][CONSOLE_FONT];
\r
4469 HWND hText = GetDlgItem(hwndConsole, OPT_ConsoleText);
\r
4470 HWND hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
4473 cfmt.cbSize = sizeof(CHARFORMAT);
\r
4474 cfmt.dwMask = CFM_FACE|CFM_SIZE|CFM_CHARSET;
\r
4475 safeStrCpy(cfmt.szFaceName, font[boardSize][CONSOLE_FONT]->mfp.faceName,
\r
4476 sizeof(cfmt.szFaceName)/sizeof(cfmt.szFaceName[0]) );
\r
4477 /* yHeight is expressed in twips. A twip is 1/20 of a font's point
\r
4478 * size. This was undocumented in the version of MSVC++ that I had
\r
4479 * when I wrote the code, but is apparently documented now.
\r
4481 cfmt.yHeight = (int)(f->mfp.pointSize * 20.0 + 0.5);
\r
4482 cfmt.bCharSet = f->lf.lfCharSet;
\r
4483 cfmt.bPitchAndFamily = f->lf.lfPitchAndFamily;
\r
4484 SendMessage(hText, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &cfmt);
\r
4485 SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &cfmt);
\r
4486 /* Why are the following seemingly needed too? */
\r
4487 SendMessage(hText, EM_SETCHARFORMAT, SCF_DEFAULT, (LPARAM) &cfmt);
\r
4488 SendMessage(hInput, EM_SETCHARFORMAT, SCF_DEFAULT, (LPARAM) &cfmt);
\r
4489 SendMessage(hText, EM_EXGETSEL, 0, (LPARAM)&sel);
\r
4491 tmpsel.cpMax = -1; /*999999?*/
\r
4492 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&tmpsel);
\r
4493 SendMessage(hText, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cfmt);
\r
4494 /* Trying putting this here too. It still seems to tickle a RichEdit
\r
4495 * bug: sometimes RichEdit indents the first line of a paragraph too.
\r
4497 paraf.cbSize = sizeof(paraf);
\r
4498 paraf.dwMask = PFM_OFFSET | PFM_STARTINDENT;
\r
4499 paraf.dxStartIndent = 0;
\r
4500 paraf.dxOffset = WRAP_INDENT;
\r
4501 SendMessage(hText, EM_SETPARAFORMAT, 0, (LPARAM) ¶f);
\r
4502 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
4503 UpdateICSWidth(hText);
\r
4506 /*---------------------------------------------------------------------------*\
\r
4508 * Window Proc for main window
\r
4510 \*---------------------------------------------------------------------------*/
\r
4512 /* Process messages for main window, etc. */
\r
4514 WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
\r
4517 int wmId, wmEvent;
\r
4521 char fileTitle[MSG_SIZ];
\r
4522 char buf[MSG_SIZ];
\r
4523 static SnapData sd;
\r
4525 switch (message) {
\r
4527 case WM_PAINT: /* message: repaint portion of window */
\r
4531 case WM_ERASEBKGND:
\r
4532 if (IsIconic(hwnd)) {
\r
4533 /* Cheat; change the message */
\r
4534 return (DefWindowProc(hwnd, WM_ICONERASEBKGND, wParam, lParam));
\r
4536 return (DefWindowProc(hwnd, message, wParam, lParam));
\r
4540 case WM_LBUTTONDOWN:
\r
4541 case WM_MBUTTONDOWN:
\r
4542 case WM_RBUTTONDOWN:
\r
4543 case WM_LBUTTONUP:
\r
4544 case WM_MBUTTONUP:
\r
4545 case WM_RBUTTONUP:
\r
4546 case WM_MOUSEMOVE:
\r
4547 case WM_MOUSEWHEEL:
\r
4548 MouseEvent(hwnd, message, wParam, lParam);
\r
4551 JAWS_KB_NAVIGATION
\r
4555 JAWS_ALT_INTERCEPT
\r
4557 if (appData.icsActive && ((char)wParam == '\r' || (char)wParam > ' ' && !((char)wParam >= '1' && (char)wParam <= '9'))) {
\r
4558 // [HGM] movenum: for non-zero digits we always do type-in dialog
\r
4559 HWND h = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
4560 if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);
\r
4562 SendMessage(h, message, wParam, lParam);
\r
4563 } else if(lParam != KF_REPEAT) {
\r
4564 if (isalpha((char)wParam) || isdigit((char)wParam)) {
\r
4565 PopUpMoveDialog((char)wParam);
\r
4566 } else if((char)wParam == 003) CopyGameToClipboard();
\r
4567 else if((char)wParam == 026) PasteGameOrFENFromClipboard();
\r
4572 case WM_PALETTECHANGED:
\r
4573 if (hwnd != (HWND)wParam && !appData.monoMode) {
\r
4575 HDC hdc = GetDC(hwndMain);
\r
4576 SelectPalette(hdc, hPal, TRUE);
\r
4577 nnew = RealizePalette(hdc);
\r
4579 paletteChanged = TRUE;
\r
4580 InvalidateRect(hwnd, &boardRect, FALSE);
\r
4582 ReleaseDC(hwnd, hdc);
\r
4586 case WM_QUERYNEWPALETTE:
\r
4587 if (!appData.monoMode /*&& paletteChanged*/) {
\r
4589 HDC hdc = GetDC(hwndMain);
\r
4590 paletteChanged = FALSE;
\r
4591 SelectPalette(hdc, hPal, FALSE);
\r
4592 nnew = RealizePalette(hdc);
\r
4594 InvalidateRect(hwnd, &boardRect, FALSE);
\r
4596 ReleaseDC(hwnd, hdc);
\r
4601 case WM_COMMAND: /* message: command from application menu */
\r
4602 wmId = LOWORD(wParam);
\r
4603 wmEvent = HIWORD(wParam);
\r
4608 SAY("new game enter a move to play against the computer with white");
\r
4611 case IDM_NewGameFRC:
\r
4612 if( NewGameFRC() == 0 ) {
\r
4617 case IDM_NewVariant:
\r
4618 NewVariantPopup(hwnd);
\r
4621 case IDM_LoadGame:
\r
4622 LoadGameDialog(hwnd, _("Load Game from File"));
\r
4625 case IDM_LoadNextGame:
\r
4629 case IDM_LoadPrevGame:
\r
4633 case IDM_ReloadGame:
\r
4637 case IDM_LoadPosition:
\r
4638 if (gameMode == AnalyzeMode || gameMode == AnalyzeFile) {
\r
4639 Reset(FALSE, TRUE);
\r
4642 f = OpenFileDialog(hwnd, "rb", "",
\r
4643 appData.oldSaveStyle ? "pos" : "fen",
\r
4645 _("Load Position from File"), &number, fileTitle, NULL);
\r
4647 LoadPosition(f, number, fileTitle);
\r
4651 case IDM_LoadNextPosition:
\r
4652 ReloadPosition(1);
\r
4655 case IDM_LoadPrevPosition:
\r
4656 ReloadPosition(-1);
\r
4659 case IDM_ReloadPosition:
\r
4660 ReloadPosition(0);
\r
4663 case IDM_SaveGame:
\r
4664 defName = DefaultFileName(appData.oldSaveStyle ? "gam" : "pgn");
\r
4665 f = OpenFileDialog(hwnd, "a", defName,
\r
4666 appData.oldSaveStyle ? "gam" : "pgn",
\r
4668 _("Save Game to File"), NULL, fileTitle, NULL);
\r
4670 SaveGame(f, 0, "");
\r
4674 case IDM_SavePosition:
\r
4675 defName = DefaultFileName(appData.oldSaveStyle ? "pos" : "fen");
\r
4676 f = OpenFileDialog(hwnd, "a", defName,
\r
4677 appData.oldSaveStyle ? "pos" : "fen",
\r
4679 _("Save Position to File"), NULL, fileTitle, NULL);
\r
4681 SavePosition(f, 0, "");
\r
4685 case IDM_SaveDiagram:
\r
4686 defName = "diagram";
\r
4687 f = OpenFileDialog(hwnd, "wb", defName,
\r
4690 "Save Diagram to File", NULL, fileTitle, NULL);
\r
4696 case IDM_CopyGame:
\r
4697 CopyGameToClipboard();
\r
4700 case IDM_PasteGame:
\r
4701 PasteGameFromClipboard();
\r
4704 case IDM_CopyGameListToClipboard:
\r
4705 CopyGameListToClipboard();
\r
4708 /* [AS] Autodetect FEN or PGN data */
\r
4709 case IDM_PasteAny:
\r
4710 PasteGameOrFENFromClipboard();
\r
4713 /* [AS] Move history */
\r
4714 case IDM_ShowMoveHistory:
\r
4715 if( MoveHistoryIsUp() ) {
\r
4716 MoveHistoryPopDown();
\r
4719 MoveHistoryPopUp();
\r
4723 /* [AS] Eval graph */
\r
4724 case IDM_ShowEvalGraph:
\r
4725 if( EvalGraphIsUp() ) {
\r
4726 EvalGraphPopDown();
\r
4730 SetFocus(hwndMain);
\r
4734 /* [AS] Engine output */
\r
4735 case IDM_ShowEngineOutput:
\r
4736 if( EngineOutputIsUp() ) {
\r
4737 EngineOutputPopDown();
\r
4740 EngineOutputPopUp();
\r
4744 /* [AS] User adjudication */
\r
4745 case IDM_UserAdjudication_White:
\r
4746 UserAdjudicationEvent( +1 );
\r
4749 case IDM_UserAdjudication_Black:
\r
4750 UserAdjudicationEvent( -1 );
\r
4753 case IDM_UserAdjudication_Draw:
\r
4754 UserAdjudicationEvent( 0 );
\r
4757 /* [AS] Game list options dialog */
\r
4758 case IDM_GameListOptions:
\r
4759 GameListOptions();
\r
4766 case IDM_CopyPosition:
\r
4767 CopyFENToClipboard();
\r
4770 case IDM_PastePosition:
\r
4771 PasteFENFromClipboard();
\r
4774 case IDM_MailMove:
\r
4778 case IDM_ReloadCMailMsg:
\r
4779 Reset(TRUE, TRUE);
\r
4780 ReloadCmailMsgEvent(FALSE);
\r
4783 case IDM_Minimize:
\r
4784 ShowWindow(hwnd, SW_MINIMIZE);
\r
4791 case IDM_MachineWhite:
\r
4792 MachineWhiteEvent();
\r
4794 * refresh the tags dialog only if it's visible
\r
4796 if (gameMode == MachinePlaysWhite && IsWindowVisible(editTagsDialog)) {
\r
4798 tags = PGNTags(&gameInfo);
\r
4799 TagsPopUp(tags, CmailMsg());
\r
4802 SAY("computer starts playing white");
\r
4805 case IDM_MachineBlack:
\r
4806 MachineBlackEvent();
\r
4808 * refresh the tags dialog only if it's visible
\r
4810 if (gameMode == MachinePlaysBlack && IsWindowVisible(editTagsDialog)) {
\r
4812 tags = PGNTags(&gameInfo);
\r
4813 TagsPopUp(tags, CmailMsg());
\r
4816 SAY("computer starts playing black");
\r
4819 case IDM_Match: // [HGM] match: flows into next case, after setting Match Mode and nr of Games
\r
4820 if(gameMode != BeginningOfGame) break; // allow menu item to remain enabled for better mode highligting
\r
4821 matchMode = 2;// distinguish from command-line-triggered case (matchMode=1)
\r
4822 appData.matchGames = appData.defaultMatchGames;
\r
4825 case IDM_TwoMachines:
\r
4826 TwoMachinesEvent();
\r
4828 * refresh the tags dialog only if it's visible
\r
4830 if (gameMode == TwoMachinesPlay && IsWindowVisible(editTagsDialog)) {
\r
4832 tags = PGNTags(&gameInfo);
\r
4833 TagsPopUp(tags, CmailMsg());
\r
4836 SAY("computer starts playing both sides");
\r
4839 case IDM_AnalysisMode:
\r
4840 if (!first.analysisSupport) {
\r
4841 snprintf(buf, MSG_SIZ, _("%s does not support analysis"), first.tidy);
\r
4842 DisplayError(buf, 0);
\r
4844 SAY("analyzing current position");
\r
4845 /* [DM] icsEngineAnlyze [HGM] Why is this front-end??? */
\r
4846 if (appData.icsActive) {
\r
4847 if (gameMode != IcsObserving) {
\r
4848 snprintf(buf, MSG_SIZ, "You are not observing a game");
\r
4849 DisplayError(buf, 0);
\r
4850 /* secure check */
\r
4851 if (appData.icsEngineAnalyze) {
\r
4852 if (appData.debugMode)
\r
4853 fprintf(debugFP, "Found unexpected active ICS engine analyze \n");
\r
4854 ExitAnalyzeMode();
\r
4860 /* if enable, user want disable icsEngineAnalyze */
\r
4861 if (appData.icsEngineAnalyze) {
\r
4862 ExitAnalyzeMode();
\r
4866 appData.icsEngineAnalyze = TRUE;
\r
4867 if (appData.debugMode) fprintf(debugFP, "ICS engine analyze starting...\n");
\r
4870 if (!appData.showThinking) ToggleShowThinking();
\r
4871 AnalyzeModeEvent();
\r
4875 case IDM_AnalyzeFile:
\r
4876 if (!first.analysisSupport) {
\r
4877 char buf[MSG_SIZ];
\r
4878 snprintf(buf, MSG_SIZ, _("%s does not support analysis"), first.tidy);
\r
4879 DisplayError(buf, 0);
\r
4881 if (!appData.showThinking) ToggleShowThinking();
\r
4882 AnalyzeFileEvent();
\r
4883 LoadGameDialog(hwnd, _("Analyze Game from File"));
\r
4884 AnalysisPeriodicEvent(1);
\r
4888 case IDM_IcsClient:
\r
4892 case IDM_EditGame:
\r
4897 case IDM_EditPosition:
\r
4898 EditPositionEvent();
\r
4899 SAY("enter a FEN string or setup a position on the board using the control R pop up menu");
\r
4902 case IDM_Training:
\r
4906 case IDM_ShowGameList:
\r
4907 ShowGameListProc();
\r
4910 case IDM_EditTags:
\r
4914 case IDM_EditComment:
\r
4915 if (commentUp && editComment) {
\r
4918 EditCommentEvent();
\r
4938 case IDM_CallFlag:
\r
4958 case IDM_StopObserving:
\r
4959 StopObservingEvent();
\r
4962 case IDM_StopExamining:
\r
4963 StopExaminingEvent();
\r
4967 UploadGameEvent();
\r
4970 case IDM_TypeInMove:
\r
4971 PopUpMoveDialog('\000');
\r
4974 case IDM_TypeInName:
\r
4975 PopUpNameDialog('\000');
\r
4978 case IDM_Backward:
\r
4980 SetFocus(hwndMain);
\r
4987 SetFocus(hwndMain);
\r
4992 SetFocus(hwndMain);
\r
4997 SetFocus(hwndMain);
\r
5001 RevertEvent(FALSE);
\r
5004 case IDM_Annotate: // [HGM] vari: revert with annotation
\r
5005 RevertEvent(TRUE);
\r
5008 case IDM_TruncateGame:
\r
5009 TruncateGameEvent();
\r
5016 case IDM_RetractMove:
\r
5017 RetractMoveEvent();
\r
5020 case IDM_FlipView:
\r
5021 flipView = !flipView;
\r
5022 DrawPosition(FALSE, NULL);
\r
5025 case IDM_FlipClock:
\r
5026 flipClock = !flipClock;
\r
5027 DisplayBothClocks();
\r
5028 DrawPosition(FALSE, NULL);
\r
5031 case IDM_MuteSounds:
\r
5032 mute = !mute; // [HGM] mute: keep track of global muting variable
\r
5033 CheckMenuItem(GetMenu(hwndMain),IDM_MuteSounds,
\r
5034 MF_BYCOMMAND|(mute?MF_CHECKED:MF_UNCHECKED));
\r
5037 case IDM_GeneralOptions:
\r
5038 GeneralOptionsPopup(hwnd);
\r
5039 DrawPosition(TRUE, NULL);
\r
5042 case IDM_BoardOptions:
\r
5043 BoardOptionsPopup(hwnd);
\r
5046 case IDM_EnginePlayOptions:
\r
5047 EnginePlayOptionsPopup(hwnd);
\r
5050 case IDM_Engine1Options:
\r
5051 EngineOptionsPopup(hwnd, &first);
\r
5054 case IDM_Engine2Options:
\r
5055 EngineOptionsPopup(hwnd, &second);
\r
5058 case IDM_OptionsUCI:
\r
5059 UciOptionsPopup(hwnd);
\r
5062 case IDM_IcsOptions:
\r
5063 IcsOptionsPopup(hwnd);
\r
5067 FontsOptionsPopup(hwnd);
\r
5071 SoundOptionsPopup(hwnd);
\r
5074 case IDM_CommPort:
\r
5075 CommPortOptionsPopup(hwnd);
\r
5078 case IDM_LoadOptions:
\r
5079 LoadOptionsPopup(hwnd);
\r
5082 case IDM_SaveOptions:
\r
5083 SaveOptionsPopup(hwnd);
\r
5086 case IDM_TimeControl:
\r
5087 TimeControlOptionsPopup(hwnd);
\r
5090 case IDM_SaveSettings:
\r
5091 SaveSettings(settingsFileName);
\r
5094 case IDM_SaveSettingsOnExit:
\r
5095 saveSettingsOnExit = !saveSettingsOnExit;
\r
5096 (void) CheckMenuItem(GetMenu(hwndMain), IDM_SaveSettingsOnExit,
\r
5097 MF_BYCOMMAND|(saveSettingsOnExit ?
\r
5098 MF_CHECKED : MF_UNCHECKED));
\r
5109 case IDM_AboutGame:
\r
5114 appData.debugMode = !appData.debugMode;
\r
5115 if (appData.debugMode) {
\r
5116 char dir[MSG_SIZ];
\r
5117 GetCurrentDirectory(MSG_SIZ, dir);
\r
5118 SetCurrentDirectory(installDir);
\r
5119 debugFP = fopen(appData.nameOfDebugFile, "w");
\r
5120 SetCurrentDirectory(dir);
\r
5121 setbuf(debugFP, NULL);
\r
5128 case IDM_HELPCONTENTS:
\r
5129 if (!MyHelp (hwnd, "winboard.hlp", HELP_KEY,(DWORD)(LPSTR)"CONTENTS") &&
\r
5130 !HtmlHelp(hwnd, "winboard.chm", 0, 0) ) {
\r
5131 MessageBox (GetFocus(),
\r
5132 _("Unable to activate help"),
\r
5133 szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);
\r
5137 case IDM_HELPSEARCH:
\r
5138 if (!MyHelp (hwnd, "winboard.hlp", HELP_PARTIALKEY, (DWORD)(LPSTR)"") &&
\r
5139 !HtmlHelp(hwnd, "winboard.chm", 0, 0) ) {
\r
5140 MessageBox (GetFocus(),
\r
5141 _("Unable to activate help"),
\r
5142 szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);
\r
5146 case IDM_HELPHELP:
\r
5147 if(!WinHelp(hwnd, (LPSTR)NULL, HELP_HELPONHELP, 0)) {
\r
5148 MessageBox (GetFocus(),
\r
5149 _("Unable to activate help"),
\r
5150 szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);
\r
5155 lpProc = MakeProcInstance((FARPROC)About, hInst);
\r
5157 (gameInfo.event && strcmp(gameInfo.event, "Easter Egg Hunt") == 0) ?
\r
5158 "AboutBox2" : "AboutBox", hwnd, (DLGPROC)lpProc);
\r
5159 FreeProcInstance(lpProc);
\r
5162 case IDM_DirectCommand1:
\r
5163 AskQuestionEvent(_("Direct Command"),
\r
5164 _("Send to chess program:"), "", "1");
\r
5166 case IDM_DirectCommand2:
\r
5167 AskQuestionEvent(_("Direct Command"),
\r
5168 _("Send to second chess program:"), "", "2");
\r
5171 case EP_WhitePawn:
\r
5172 EditPositionMenuEvent(WhitePawn, fromX, fromY);
\r
5173 fromX = fromY = -1;
\r
5176 case EP_WhiteKnight:
\r
5177 EditPositionMenuEvent(WhiteKnight, fromX, fromY);
\r
5178 fromX = fromY = -1;
\r
5181 case EP_WhiteBishop:
\r
5182 EditPositionMenuEvent(WhiteBishop, fromX, fromY);
\r
5183 fromX = fromY = -1;
\r
5186 case EP_WhiteRook:
\r
5187 EditPositionMenuEvent(WhiteRook, fromX, fromY);
\r
5188 fromX = fromY = -1;
\r
5191 case EP_WhiteQueen:
\r
5192 EditPositionMenuEvent(WhiteQueen, fromX, fromY);
\r
5193 fromX = fromY = -1;
\r
5196 case EP_WhiteFerz:
\r
5197 EditPositionMenuEvent(WhiteFerz, fromX, fromY);
\r
5198 fromX = fromY = -1;
\r
5201 case EP_WhiteWazir:
\r
5202 EditPositionMenuEvent(WhiteWazir, fromX, fromY);
\r
5203 fromX = fromY = -1;
\r
5206 case EP_WhiteAlfil:
\r
5207 EditPositionMenuEvent(WhiteAlfil, fromX, fromY);
\r
5208 fromX = fromY = -1;
\r
5211 case EP_WhiteCannon:
\r
5212 EditPositionMenuEvent(WhiteCannon, fromX, fromY);
\r
5213 fromX = fromY = -1;
\r
5216 case EP_WhiteCardinal:
\r
5217 EditPositionMenuEvent(WhiteAngel, fromX, fromY);
\r
5218 fromX = fromY = -1;
\r
5221 case EP_WhiteMarshall:
\r
5222 EditPositionMenuEvent(WhiteMarshall, fromX, fromY);
\r
5223 fromX = fromY = -1;
\r
5226 case EP_WhiteKing:
\r
5227 EditPositionMenuEvent(WhiteKing, fromX, fromY);
\r
5228 fromX = fromY = -1;
\r
5231 case EP_BlackPawn:
\r
5232 EditPositionMenuEvent(BlackPawn, fromX, fromY);
\r
5233 fromX = fromY = -1;
\r
5236 case EP_BlackKnight:
\r
5237 EditPositionMenuEvent(BlackKnight, fromX, fromY);
\r
5238 fromX = fromY = -1;
\r
5241 case EP_BlackBishop:
\r
5242 EditPositionMenuEvent(BlackBishop, fromX, fromY);
\r
5243 fromX = fromY = -1;
\r
5246 case EP_BlackRook:
\r
5247 EditPositionMenuEvent(BlackRook, fromX, fromY);
\r
5248 fromX = fromY = -1;
\r
5251 case EP_BlackQueen:
\r
5252 EditPositionMenuEvent(BlackQueen, fromX, fromY);
\r
5253 fromX = fromY = -1;
\r
5256 case EP_BlackFerz:
\r
5257 EditPositionMenuEvent(BlackFerz, fromX, fromY);
\r
5258 fromX = fromY = -1;
\r
5261 case EP_BlackWazir:
\r
5262 EditPositionMenuEvent(BlackWazir, fromX, fromY);
\r
5263 fromX = fromY = -1;
\r
5266 case EP_BlackAlfil:
\r
5267 EditPositionMenuEvent(BlackAlfil, fromX, fromY);
\r
5268 fromX = fromY = -1;
\r
5271 case EP_BlackCannon:
\r
5272 EditPositionMenuEvent(BlackCannon, fromX, fromY);
\r
5273 fromX = fromY = -1;
\r
5276 case EP_BlackCardinal:
\r
5277 EditPositionMenuEvent(BlackAngel, fromX, fromY);
\r
5278 fromX = fromY = -1;
\r
5281 case EP_BlackMarshall:
\r
5282 EditPositionMenuEvent(BlackMarshall, fromX, fromY);
\r
5283 fromX = fromY = -1;
\r
5286 case EP_BlackKing:
\r
5287 EditPositionMenuEvent(BlackKing, fromX, fromY);
\r
5288 fromX = fromY = -1;
\r
5291 case EP_EmptySquare:
\r
5292 EditPositionMenuEvent(EmptySquare, fromX, fromY);
\r
5293 fromX = fromY = -1;
\r
5296 case EP_ClearBoard:
\r
5297 EditPositionMenuEvent(ClearBoard, fromX, fromY);
\r
5298 fromX = fromY = -1;
\r
5302 EditPositionMenuEvent(WhitePlay, fromX, fromY);
\r
5303 fromX = fromY = -1;
\r
5307 EditPositionMenuEvent(BlackPlay, fromX, fromY);
\r
5308 fromX = fromY = -1;
\r
5312 EditPositionMenuEvent(PromotePiece, fromX, fromY);
\r
5313 fromX = fromY = -1;
\r
5317 EditPositionMenuEvent(DemotePiece, fromX, fromY);
\r
5318 fromX = fromY = -1;
\r
5322 DropMenuEvent(WhitePawn, fromX, fromY);
\r
5323 fromX = fromY = -1;
\r
5327 DropMenuEvent(WhiteKnight, fromX, fromY);
\r
5328 fromX = fromY = -1;
\r
5332 DropMenuEvent(WhiteBishop, fromX, fromY);
\r
5333 fromX = fromY = -1;
\r
5337 DropMenuEvent(WhiteRook, fromX, fromY);
\r
5338 fromX = fromY = -1;
\r
5342 DropMenuEvent(WhiteQueen, fromX, fromY);
\r
5343 fromX = fromY = -1;
\r
5348 TranslateMenus(0);
\r
5349 CheckMenuItem(GetMenu(hwndMain), lastChecked, MF_BYCOMMAND|MF_UNCHECKED);
\r
5350 CheckMenuItem(GetMenu(hwndMain), IDM_English, MF_BYCOMMAND|MF_CHECKED);
\r
5351 lastChecked = wmId;
\r
5355 if(wmId > IDM_English && wmId < IDM_English+5) {
\r
5356 LoadLanguageFile(languageFile[wmId - IDM_English - 1]);
\r
5357 TranslateMenus(0);
\r
5358 CheckMenuItem(GetMenu(hwndMain), lastChecked, MF_BYCOMMAND|MF_UNCHECKED);
\r
5359 CheckMenuItem(GetMenu(hwndMain), wmId, MF_BYCOMMAND|MF_CHECKED);
\r
5360 lastChecked = wmId;
\r
5363 return (DefWindowProc(hwnd, message, wParam, lParam));
\r
5369 case CLOCK_TIMER_ID:
\r
5370 KillTimer(hwnd, clockTimerEvent); /* Simulate one-shot timer as in X */
\r
5371 clockTimerEvent = 0;
\r
5372 DecrementClocks(); /* call into back end */
\r
5374 case LOAD_GAME_TIMER_ID:
\r
5375 KillTimer(hwnd, loadGameTimerEvent); /* Simulate one-shot timer as in X*/
\r
5376 loadGameTimerEvent = 0;
\r
5377 AutoPlayGameLoop(); /* call into back end */
\r
5379 case ANALYSIS_TIMER_ID:
\r
5380 if ((gameMode == AnalyzeMode || gameMode == AnalyzeFile
\r
5381 || appData.icsEngineAnalyze) && appData.periodicUpdates) {
\r
5382 AnalysisPeriodicEvent(0);
\r
5384 KillTimer(hwnd, analysisTimerEvent);
\r
5385 analysisTimerEvent = 0;
\r
5388 case DELAYED_TIMER_ID:
\r
5389 KillTimer(hwnd, delayedTimerEvent);
\r
5390 delayedTimerEvent = 0;
\r
5391 delayedTimerCallback();
\r
5396 case WM_USER_Input:
\r
5397 InputEvent(hwnd, message, wParam, lParam);
\r
5400 /* [AS] Also move "attached" child windows */
\r
5401 case WM_WINDOWPOSCHANGING:
\r
5403 if( hwnd == hwndMain && appData.useStickyWindows ) {
\r
5404 LPWINDOWPOS lpwp = (LPWINDOWPOS) lParam;
\r
5406 if( ((lpwp->flags & SWP_NOMOVE) == 0) && ((lpwp->flags & SWP_NOSIZE) != 0) ) {
\r
5407 /* Window is moving */
\r
5410 // GetWindowRect( hwnd, &rcMain ); //[HGM] sticky: in XP this returned new position, not old
\r
5411 rcMain.left = wpMain.x; // replace by these 4 lines to reconstruct old rect
\r
5412 rcMain.right = wpMain.x + wpMain.width;
\r
5413 rcMain.top = wpMain.y;
\r
5414 rcMain.bottom = wpMain.y + wpMain.height;
\r
5416 ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, moveHistoryDialog, &wpMoveHistory );
\r
5417 ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, evalGraphDialog, &wpEvalGraph );
\r
5418 ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, engineOutputDialog, &wpEngineOutput );
\r
5419 ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, gameListDialog, &wpGameList );
\r
5420 ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, hwndConsole, &wpConsole );
\r
5421 wpMain.x = lpwp->x;
\r
5422 wpMain.y = lpwp->y;
\r
5427 /* [AS] Snapping */
\r
5428 case WM_ENTERSIZEMOVE:
\r
5429 if(appData.debugMode) { fprintf(debugFP, "size-move\n"); }
\r
5430 if (hwnd == hwndMain) {
\r
5431 doingSizing = TRUE;
\r
5434 return OnEnterSizeMove( &sd, hwnd, wParam, lParam );
\r
5438 if(appData.debugMode) { fprintf(debugFP, "sizing\n"); }
\r
5439 if (hwnd == hwndMain) {
\r
5440 lastSizing = wParam;
\r
5445 if(appData.debugMode) { fprintf(debugFP, "moving\n"); }
\r
5446 return OnMoving( &sd, hwnd, wParam, lParam );
\r
5448 case WM_EXITSIZEMOVE:
\r
5449 if(appData.debugMode) { fprintf(debugFP, "exit size-move, size = %d\n", squareSize); }
\r
5450 if (hwnd == hwndMain) {
\r
5452 doingSizing = FALSE;
\r
5453 InvalidateRect(hwnd, &boardRect, FALSE);
\r
5454 GetClientRect(hwnd, &client);
\r
5455 ResizeBoard(client.right, client.bottom, lastSizing);
\r
5457 if(appData.debugMode) { fprintf(debugFP, "square size = %d\n", squareSize); }
\r
5459 return OnExitSizeMove( &sd, hwnd, wParam, lParam );
\r
5462 case WM_DESTROY: /* message: window being destroyed */
\r
5463 PostQuitMessage(0);
\r
5467 if (hwnd == hwndMain) {
\r
5472 default: /* Passes it on if unprocessed */
\r
5473 return (DefWindowProc(hwnd, message, wParam, lParam));
\r
5478 /*---------------------------------------------------------------------------*\
\r
5480 * Misc utility routines
\r
5482 \*---------------------------------------------------------------------------*/
\r
5485 * Decent random number generator, at least not as bad as Windows
\r
5486 * standard rand, which returns a value in the range 0 to 0x7fff.
\r
5488 unsigned int randstate;
\r
5493 randstate = randstate * 1664525 + 1013904223;
\r
5494 return (int) randstate & 0x7fffffff;
\r
5498 mysrandom(unsigned int seed)
\r
5505 * returns TRUE if user selects a different color, FALSE otherwise
\r
5509 ChangeColor(HWND hwnd, COLORREF *which)
\r
5511 static BOOL firstTime = TRUE;
\r
5512 static DWORD customColors[16];
\r
5514 COLORREF newcolor;
\r
5519 /* Make initial colors in use available as custom colors */
\r
5520 /* Should we put the compiled-in defaults here instead? */
\r
5522 customColors[i++] = lightSquareColor & 0xffffff;
\r
5523 customColors[i++] = darkSquareColor & 0xffffff;
\r
5524 customColors[i++] = whitePieceColor & 0xffffff;
\r
5525 customColors[i++] = blackPieceColor & 0xffffff;
\r
5526 customColors[i++] = highlightSquareColor & 0xffffff;
\r
5527 customColors[i++] = premoveHighlightColor & 0xffffff;
\r
5529 for (ccl = (ColorClass) 0; ccl < NColorClasses && i < 16; ccl++) {
\r
5530 customColors[i++] = textAttribs[ccl].color;
\r
5532 while (i < 16) customColors[i++] = RGB(255, 255, 255);
\r
5533 firstTime = FALSE;
\r
5536 cc.lStructSize = sizeof(cc);
\r
5537 cc.hwndOwner = hwnd;
\r
5538 cc.hInstance = NULL;
\r
5539 cc.rgbResult = (DWORD) (*which & 0xffffff);
\r
5540 cc.lpCustColors = (LPDWORD) customColors;
\r
5541 cc.Flags = CC_RGBINIT|CC_FULLOPEN;
\r
5543 if (!ChooseColor(&cc)) return FALSE;
\r
5545 newcolor = (COLORREF) (0x2000000 | cc.rgbResult);
\r
5546 if (newcolor == *which) return FALSE;
\r
5547 *which = newcolor;
\r
5551 InitDrawingColors();
\r
5552 InvalidateRect(hwnd, &boardRect, FALSE);
\r
5557 MyLoadSound(MySound *ms)
\r
5563 if (ms->data) free(ms->data);
\r
5566 switch (ms->name[0]) {
\r
5572 /* System sound from Control Panel. Don't preload here. */
\r
5576 if (ms->name[1] == NULLCHAR) {
\r
5577 /* "!" alone = silence */
\r
5580 /* Builtin wave resource. Error if not found. */
\r
5581 HANDLE h = FindResource(hInst, ms->name + 1, "WAVE");
\r
5582 if (h == NULL) break;
\r
5583 ms->data = (void *)LoadResource(hInst, h);
\r
5584 if (h == NULL) break;
\r
5589 /* .wav file. Error if not found. */
\r
5590 f = fopen(ms->name, "rb");
\r
5591 if (f == NULL) break;
\r
5592 if (fstat(fileno(f), &st) < 0) break;
\r
5593 ms->data = malloc(st.st_size);
\r
5594 if (fread(ms->data, st.st_size, 1, f) < 1) break;
\r
5600 char buf[MSG_SIZ];
\r
5601 snprintf(buf, MSG_SIZ, _("Error loading sound %s"), ms->name);
\r
5602 DisplayError(buf, GetLastError());
\r
5608 MyPlaySound(MySound *ms)
\r
5610 BOOLEAN ok = FALSE;
\r
5612 if(mute) return TRUE; // [HGM] mute: suppress all sound play when muted
\r
5613 switch (ms->name[0]) {
\r
5615 if(appData.debugMode) fprintf(debugFP, "silence\n");
\r
5620 /* System sound from Control Panel (deprecated feature).
\r
5621 "$" alone or an unset sound name gets default beep (still in use). */
\r
5622 if (ms->name[1]) {
\r
5623 ok = PlaySound(ms->name + 1, NULL, SND_ALIAS|SND_ASYNC);
\r
5625 if (!ok) ok = MessageBeep(MB_OK);
\r
5628 /* Builtin wave resource, or "!" alone for silence */
\r
5629 if (ms->name[1]) {
\r
5630 if (ms->data == NULL) return FALSE;
\r
5631 ok = PlaySound(ms->data, NULL, SND_MEMORY|SND_ASYNC);
\r
5637 /* .wav file. Error if not found. */
\r
5638 if (ms->data == NULL) return FALSE;
\r
5639 ok = PlaySound(ms->data, NULL, SND_MEMORY|SND_ASYNC);
\r
5642 /* Don't print an error: this can happen innocently if the sound driver
\r
5643 is busy; for instance, if another instance of WinBoard is playing
\r
5644 a sound at about the same time. */
\r
5650 OldOpenFileHook(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
5653 OPENFILENAME *ofn;
\r
5654 static UINT *number; /* gross that this is static */
\r
5656 switch (message) {
\r
5657 case WM_INITDIALOG: /* message: initialize dialog box */
\r
5658 /* Center the dialog over the application window */
\r
5659 ofn = (OPENFILENAME *) lParam;
\r
5660 if (ofn->Flags & OFN_ENABLETEMPLATE) {
\r
5661 number = (UINT *) ofn->lCustData;
\r
5662 SendMessage(GetDlgItem(hDlg, edt2), WM_SETTEXT, 0, (LPARAM) "");
\r
5666 CenterWindow (hDlg, GetWindow (hDlg, GW_OWNER));
\r
5667 Translate(hDlg, 1536);
\r
5668 return FALSE; /* Allow for further processing */
\r
5671 if ((LOWORD(wParam) == IDOK) && (number != NULL)) {
\r
5672 *number = GetDlgItemInt(hDlg, OPT_IndexNumberOld, &ok, FALSE);
\r
5674 return FALSE; /* Allow for further processing */
\r
5680 OpenFileHook(HWND hdlg, UINT uiMsg, WPARAM wParam, LPARAM lParam)
\r
5682 static UINT *number;
\r
5683 OPENFILENAME *ofname;
\r
5686 case WM_INITDIALOG:
\r
5687 Translate(hdlg, DLG_IndexNumber);
\r
5688 ofname = (OPENFILENAME *)lParam;
\r
5689 number = (UINT *)(ofname->lCustData);
\r
5692 ofnot = (OFNOTIFY *)lParam;
\r
5693 if (ofnot->hdr.code == CDN_FILEOK) {
\r
5694 *number = GetDlgItemInt(hdlg, OPT_IndexNumber, NULL, FALSE);
\r
5703 OpenFileDialog(HWND hwnd, char *write, char *defName, char *defExt, // [HGM] diag: type of 'write' now string
\r
5704 char *nameFilt, char *dlgTitle, UINT *number,
\r
5705 char fileTitle[MSG_SIZ], char fileName[MSG_SIZ])
\r
5707 OPENFILENAME openFileName;
\r
5708 char buf1[MSG_SIZ];
\r
5711 if (fileName == NULL) fileName = buf1;
\r
5712 if (defName == NULL) {
\r
5713 safeStrCpy(fileName, "*.", 3 );
\r
5714 strcat(fileName, defExt);
\r
5716 safeStrCpy(fileName, defName, MSG_SIZ );
\r
5718 if (fileTitle) safeStrCpy(fileTitle, "", MSG_SIZ );
\r
5719 if (number) *number = 0;
\r
5721 openFileName.lStructSize = sizeof(OPENFILENAME);
\r
5722 openFileName.hwndOwner = hwnd;
\r
5723 openFileName.hInstance = (HANDLE) hInst;
\r
5724 openFileName.lpstrFilter = nameFilt;
\r
5725 openFileName.lpstrCustomFilter = (LPSTR) NULL;
\r
5726 openFileName.nMaxCustFilter = 0L;
\r
5727 openFileName.nFilterIndex = 1L;
\r
5728 openFileName.lpstrFile = fileName;
\r
5729 openFileName.nMaxFile = MSG_SIZ;
\r
5730 openFileName.lpstrFileTitle = fileTitle;
\r
5731 openFileName.nMaxFileTitle = fileTitle ? MSG_SIZ : 0;
\r
5732 openFileName.lpstrInitialDir = NULL;
\r
5733 openFileName.lpstrTitle = dlgTitle;
\r
5734 openFileName.Flags = OFN_PATHMUSTEXIST | OFN_HIDEREADONLY
\r
5735 | (write[0] != 'r' ? 0 : OFN_FILEMUSTEXIST)
\r
5736 | (number ? OFN_ENABLETEMPLATE | OFN_ENABLEHOOK: 0)
\r
5737 | (oldDialog ? 0 : OFN_EXPLORER);
\r
5738 openFileName.nFileOffset = 0;
\r
5739 openFileName.nFileExtension = 0;
\r
5740 openFileName.lpstrDefExt = defExt;
\r
5741 openFileName.lCustData = (LONG) number;
\r
5742 openFileName.lpfnHook = oldDialog ?
\r
5743 (LPOFNHOOKPROC) OldOpenFileHook : (LPOFNHOOKPROC) OpenFileHook;
\r
5744 openFileName.lpTemplateName = (LPSTR)(oldDialog ? 1536 : DLG_IndexNumber);
\r
5746 if (write[0] != 'r' ? GetSaveFileName(&openFileName) :
\r
5747 GetOpenFileName(&openFileName)) {
\r
5748 /* open the file */
\r
5749 f = fopen(openFileName.lpstrFile, write);
\r
5751 MessageBox(hwnd, _("File open failed"), NULL,
\r
5752 MB_OK|MB_ICONEXCLAMATION);
\r
5756 int err = CommDlgExtendedError();
\r
5757 if (err != 0) DisplayError(_("Internal error in file dialog box"), err);
\r
5766 MenuPopup(HWND hwnd, POINT pt, HMENU hmenu, UINT def)
\r
5768 HMENU hmenuTrackPopup; /* floating pop-up menu */
\r
5771 * Get the first pop-up menu in the menu template. This is the
\r
5772 * menu that TrackPopupMenu displays.
\r
5774 hmenuTrackPopup = GetSubMenu(hmenu, 0);
\r
5776 SetMenuDefaultItem(hmenuTrackPopup, def, FALSE);
\r
5779 * TrackPopup uses screen coordinates, so convert the
\r
5780 * coordinates of the mouse click to screen coordinates.
\r
5782 ClientToScreen(hwnd, (LPPOINT) &pt);
\r
5784 /* Draw and track the floating pop-up menu. */
\r
5785 TrackPopupMenu(hmenuTrackPopup, TPM_CENTERALIGN | TPM_RIGHTBUTTON,
\r
5786 pt.x, pt.y, 0, hwnd, NULL);
\r
5788 /* Destroy the menu.*/
\r
5789 DestroyMenu(hmenu);
\r
5794 int sizeX, sizeY, newSizeX, newSizeY;
\r
5796 } ResizeEditPlusButtonsClosure;
\r
5799 ResizeEditPlusButtonsCallback(HWND hChild, LPARAM lparam)
\r
5801 ResizeEditPlusButtonsClosure *cl = (ResizeEditPlusButtonsClosure *)lparam;
\r
5805 if (hChild == cl->hText) return TRUE;
\r
5806 GetWindowRect(hChild, &rect); /* gives screen coords */
\r
5807 pt.x = rect.left + (cl->newSizeX - cl->sizeX)/2;
\r
5808 pt.y = rect.top + cl->newSizeY - cl->sizeY;
\r
5809 ScreenToClient(cl->hDlg, &pt);
\r
5810 cl->hdwp = DeferWindowPos(cl->hdwp, hChild, NULL,
\r
5811 pt.x, pt.y, rect.right - rect.left, rect.bottom - rect.top, SWP_NOZORDER);
\r
5815 /* Resize a dialog that has a (rich) edit field filling most of
\r
5816 the top, with a row of buttons below */
\r
5818 ResizeEditPlusButtons(HWND hDlg, HWND hText, int sizeX, int sizeY, int newSizeX, int newSizeY)
\r
5821 int newTextHeight, newTextWidth;
\r
5822 ResizeEditPlusButtonsClosure cl;
\r
5824 /*if (IsIconic(hDlg)) return;*/
\r
5825 if (newSizeX == sizeX && newSizeY == sizeY) return;
\r
5827 cl.hdwp = BeginDeferWindowPos(8);
\r
5829 GetWindowRect(hText, &rectText); /* gives screen coords */
\r
5830 newTextWidth = rectText.right - rectText.left + newSizeX - sizeX;
\r
5831 newTextHeight = rectText.bottom - rectText.top + newSizeY - sizeY;
\r
5832 if (newTextHeight < 0) {
\r
5833 newSizeY += -newTextHeight;
\r
5834 newTextHeight = 0;
\r
5836 cl.hdwp = DeferWindowPos(cl.hdwp, hText, NULL, 0, 0,
\r
5837 newTextWidth, newTextHeight, SWP_NOZORDER|SWP_NOMOVE);
\r
5843 cl.newSizeX = newSizeX;
\r
5844 cl.newSizeY = newSizeY;
\r
5845 EnumChildWindows(hDlg, ResizeEditPlusButtonsCallback, (LPARAM)&cl);
\r
5847 EndDeferWindowPos(cl.hdwp);
\r
5850 BOOL CenterWindowEx(HWND hwndChild, HWND hwndParent, int mode)
\r
5852 RECT rChild, rParent;
\r
5853 int wChild, hChild, wParent, hParent;
\r
5854 int wScreen, hScreen, xNew, yNew;
\r
5857 /* Get the Height and Width of the child window */
\r
5858 GetWindowRect (hwndChild, &rChild);
\r
5859 wChild = rChild.right - rChild.left;
\r
5860 hChild = rChild.bottom - rChild.top;
\r
5862 /* Get the Height and Width of the parent window */
\r
5863 GetWindowRect (hwndParent, &rParent);
\r
5864 wParent = rParent.right - rParent.left;
\r
5865 hParent = rParent.bottom - rParent.top;
\r
5867 /* Get the display limits */
\r
5868 hdc = GetDC (hwndChild);
\r
5869 wScreen = GetDeviceCaps (hdc, HORZRES);
\r
5870 hScreen = GetDeviceCaps (hdc, VERTRES);
\r
5871 ReleaseDC(hwndChild, hdc);
\r
5873 /* Calculate new X position, then adjust for screen */
\r
5874 xNew = rParent.left + ((wParent - wChild) /2);
\r
5877 } else if ((xNew+wChild) > wScreen) {
\r
5878 xNew = wScreen - wChild;
\r
5881 /* Calculate new Y position, then adjust for screen */
\r
5883 yNew = rParent.top + ((hParent - hChild) /2);
\r
5886 yNew = rParent.top + GetSystemMetrics( SM_CYCAPTION ) * 2 / 3;
\r
5891 } else if ((yNew+hChild) > hScreen) {
\r
5892 yNew = hScreen - hChild;
\r
5895 /* Set it, and return */
\r
5896 return SetWindowPos (hwndChild, NULL,
\r
5897 xNew, yNew, 0, 0, SWP_NOSIZE | SWP_NOZORDER);
\r
5900 /* Center one window over another */
\r
5901 BOOL CenterWindow (HWND hwndChild, HWND hwndParent)
\r
5903 return CenterWindowEx( hwndChild, hwndParent, 0 );
\r
5906 /*---------------------------------------------------------------------------*\
\r
5908 * Startup Dialog functions
\r
5910 \*---------------------------------------------------------------------------*/
\r
5912 InitComboStrings(HANDLE hwndCombo, char **cd)
\r
5914 SendMessage(hwndCombo, CB_RESETCONTENT, 0, 0);
\r
5916 while (*cd != NULL) {
\r
5917 SendMessage(hwndCombo, CB_ADDSTRING, 0, (LPARAM) T_(*cd));
\r
5923 InitComboStringsFromOption(HANDLE hwndCombo, char *str)
\r
5925 char buf1[MAX_ARG_LEN];
\r
5928 if (str[0] == '@') {
\r
5929 FILE* f = fopen(str + 1, "r");
\r
5931 DisplayFatalError(str + 1, errno, 2);
\r
5934 len = fread(buf1, 1, sizeof(buf1)-1, f);
\r
5936 buf1[len] = NULLCHAR;
\r
5940 SendMessage(hwndCombo, CB_RESETCONTENT, 0, 0);
\r
5943 char buf[MSG_SIZ];
\r
5944 char *end = strchr(str, '\n');
\r
5945 if (end == NULL) return;
\r
5946 memcpy(buf, str, end - str);
\r
5947 buf[end - str] = NULLCHAR;
\r
5948 SendMessage(hwndCombo, CB_ADDSTRING, 0, (LPARAM) buf);
\r
5954 SetStartupDialogEnables(HWND hDlg)
\r
5956 EnableWindow(GetDlgItem(hDlg, OPT_ChessEngineName),
\r
5957 IsDlgButtonChecked(hDlg, OPT_ChessEngine) ||
\r
5958 (appData.zippyPlay && IsDlgButtonChecked(hDlg, OPT_ChessServer)));
\r
5959 EnableWindow(GetDlgItem(hDlg, OPT_SecondChessEngineName),
\r
5960 IsDlgButtonChecked(hDlg, OPT_ChessEngine));
\r
5961 EnableWindow(GetDlgItem(hDlg, OPT_ChessServerName),
\r
5962 IsDlgButtonChecked(hDlg, OPT_ChessServer));
\r
5963 EnableWindow(GetDlgItem(hDlg, OPT_AdditionalOptions),
\r
5964 IsDlgButtonChecked(hDlg, OPT_AnyAdditional));
\r
5965 EnableWindow(GetDlgItem(hDlg, IDOK),
\r
5966 IsDlgButtonChecked(hDlg, OPT_ChessEngine) ||
\r
5967 IsDlgButtonChecked(hDlg, OPT_ChessServer) ||
\r
5968 IsDlgButtonChecked(hDlg, OPT_View));
\r
5972 QuoteForFilename(char *filename)
\r
5974 int dquote, space;
\r
5975 dquote = strchr(filename, '"') != NULL;
\r
5976 space = strchr(filename, ' ') != NULL;
\r
5977 if (dquote || space) {
\r
5989 InitEngineBox(HWND hDlg, HWND hwndCombo, char* nthcp, char* nthd, char* nthdir, char *nthnames)
\r
5991 char buf[MSG_SIZ];
\r
5994 InitComboStringsFromOption(hwndCombo, nthnames);
\r
5995 q = QuoteForFilename(nthcp);
\r
5996 snprintf(buf, MSG_SIZ, "%s%s%s", q, nthcp, q);
\r
5997 if (*nthdir != NULLCHAR) {
\r
5998 q = QuoteForFilename(nthdir);
\r
5999 snprintf(buf + strlen(buf), MSG_SIZ, " /%s=%s%s%s", nthd, q, nthdir, q);
\r
6001 if (*nthcp == NULLCHAR) {
\r
6002 SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) 0, (LPARAM) 0);
\r
6003 } else if (SendMessage(hwndCombo, CB_SELECTSTRING, (WPARAM) -1, (LPARAM) buf) == CB_ERR) {
\r
6004 SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) -1, (LPARAM) 0);
\r
6005 SendMessage(hwndCombo, WM_SETTEXT, (WPARAM) 0, (LPARAM) buf);
\r
6010 StartupDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
6012 char buf[MSG_SIZ];
\r
6016 switch (message) {
\r
6017 case WM_INITDIALOG:
\r
6018 /* Center the dialog */
\r
6019 CenterWindow (hDlg, GetDesktopWindow());
\r
6020 Translate(hDlg, DLG_Startup);
\r
6021 /* Initialize the dialog items */
\r
6022 InitEngineBox(hDlg, GetDlgItem(hDlg, OPT_ChessEngineName),
\r
6023 appData.firstChessProgram, "fd", appData.firstDirectory,
\r
6024 firstChessProgramNames);
\r
6025 InitEngineBox(hDlg, GetDlgItem(hDlg, OPT_SecondChessEngineName),
\r
6026 appData.secondChessProgram, "sd", appData.secondDirectory,
\r
6027 secondChessProgramNames);
\r
6028 hwndCombo = GetDlgItem(hDlg, OPT_ChessServerName);
\r
6029 InitComboStringsFromOption(hwndCombo, icsNames);
\r
6030 snprintf(buf, MSG_SIZ, "%s /icsport=%s", appData.icsHost, appData.icsPort);
\r
6031 if (*appData.icsHelper != NULLCHAR) {
\r
6032 char *q = QuoteForFilename(appData.icsHelper);
\r
6033 sprintf(buf + strlen(buf), " /icshelper=%s%s%s", q, appData.icsHelper, q);
\r
6035 if (*appData.icsHost == NULLCHAR) {
\r
6036 SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) 0, (LPARAM) 0);
\r
6037 /*SendMessage(hwndCombo, CB_SHOWDROPDOWN, (WPARAM) TRUE, (LPARAM) 0); !!too soon */
\r
6038 } else if (SendMessage(hwndCombo, CB_SELECTSTRING, (WPARAM) -1, (LPARAM) buf) == CB_ERR) {
\r
6039 SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) -1, (LPARAM) 0);
\r
6040 SendMessage(hwndCombo, WM_SETTEXT, (WPARAM) 0, (LPARAM) buf);
\r
6043 if (appData.icsActive) {
\r
6044 CheckDlgButton(hDlg, OPT_ChessServer, BST_CHECKED);
\r
6046 else if (appData.noChessProgram) {
\r
6047 CheckDlgButton(hDlg, OPT_View, BST_CHECKED);
\r
6050 CheckDlgButton(hDlg, OPT_ChessEngine, BST_CHECKED);
\r
6053 SetStartupDialogEnables(hDlg);
\r
6057 switch (LOWORD(wParam)) {
\r
6059 if (IsDlgButtonChecked(hDlg, OPT_ChessEngine)) {
\r
6060 safeStrCpy(buf, "/fcp=", sizeof(buf)/sizeof(buf[0]) );
\r
6061 GetDlgItemText(hDlg, OPT_ChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));
\r
6063 ParseArgs(StringGet, &p);
\r
6064 safeStrCpy(buf, "/scp=", sizeof(buf)/sizeof(buf[0]) );
\r
6065 GetDlgItemText(hDlg, OPT_SecondChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));
\r
6067 ParseArgs(StringGet, &p);
\r
6068 appData.noChessProgram = FALSE;
\r
6069 appData.icsActive = FALSE;
\r
6070 } else if (IsDlgButtonChecked(hDlg, OPT_ChessServer)) {
\r
6071 safeStrCpy(buf, "/ics /icshost=", sizeof(buf)/sizeof(buf[0]) );
\r
6072 GetDlgItemText(hDlg, OPT_ChessServerName, buf + strlen(buf), sizeof(buf) - strlen(buf));
\r
6074 ParseArgs(StringGet, &p);
\r
6075 if (appData.zippyPlay) {
\r
6076 safeStrCpy(buf, "/fcp=", sizeof(buf)/sizeof(buf[0]) );
\r
6077 GetDlgItemText(hDlg, OPT_ChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));
\r
6079 ParseArgs(StringGet, &p);
\r
6081 } else if (IsDlgButtonChecked(hDlg, OPT_View)) {
\r
6082 appData.noChessProgram = TRUE;
\r
6083 appData.icsActive = FALSE;
\r
6085 MessageBox(hDlg, _("Choose an option, or cancel to exit"),
\r
6086 _("Option Error"), MB_OK|MB_ICONEXCLAMATION);
\r
6089 if (IsDlgButtonChecked(hDlg, OPT_AnyAdditional)) {
\r
6090 GetDlgItemText(hDlg, OPT_AdditionalOptions, buf, sizeof(buf));
\r
6092 ParseArgs(StringGet, &p);
\r
6094 EndDialog(hDlg, TRUE);
\r
6101 case IDM_HELPCONTENTS:
\r
6102 if (!WinHelp (hDlg, "winboard.hlp", HELP_KEY,(DWORD)(LPSTR)"CONTENTS")) {
\r
6103 MessageBox (GetFocus(),
\r
6104 _("Unable to activate help"),
\r
6105 szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);
\r
6110 SetStartupDialogEnables(hDlg);
\r
6118 /*---------------------------------------------------------------------------*\
\r
6120 * About box dialog functions
\r
6122 \*---------------------------------------------------------------------------*/
\r
6124 /* Process messages for "About" dialog box */
\r
6126 About(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
6128 switch (message) {
\r
6129 case WM_INITDIALOG: /* message: initialize dialog box */
\r
6130 /* Center the dialog over the application window */
\r
6131 CenterWindow (hDlg, GetWindow (hDlg, GW_OWNER));
\r
6132 SetDlgItemText(hDlg, ABOUTBOX_Version, programVersion);
\r
6133 Translate(hDlg, ABOUTBOX);
\r
6137 case WM_COMMAND: /* message: received a command */
\r
6138 if (LOWORD(wParam) == IDOK /* "OK" box selected? */
\r
6139 || LOWORD(wParam) == IDCANCEL) { /* System menu close command? */
\r
6140 EndDialog(hDlg, TRUE); /* Exit the dialog */
\r
6148 /*---------------------------------------------------------------------------*\
\r
6150 * Comment Dialog functions
\r
6152 \*---------------------------------------------------------------------------*/
\r
6155 CommentDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
6157 static HANDLE hwndText = NULL;
\r
6158 int len, newSizeX, newSizeY, flags;
\r
6159 static int sizeX, sizeY;
\r
6164 switch (message) {
\r
6165 case WM_INITDIALOG: /* message: initialize dialog box */
\r
6166 /* Initialize the dialog items */
\r
6167 Translate(hDlg, DLG_EditComment);
\r
6168 hwndText = GetDlgItem(hDlg, OPT_CommentText);
\r
6169 SetDlgItemText(hDlg, OPT_CommentText, commentText);
\r
6170 EnableWindow(GetDlgItem(hDlg, OPT_CancelComment), editComment);
\r
6171 EnableWindow(GetDlgItem(hDlg, OPT_ClearComment), editComment);
\r
6172 EnableWindow(GetDlgItem(hDlg, OPT_EditComment), !editComment);
\r
6173 SendMessage(hwndText, EM_SETREADONLY, !editComment, 0);
\r
6174 SetWindowText(hDlg, commentTitle);
\r
6175 if (editComment) {
\r
6176 SetFocus(hwndText);
\r
6178 SetFocus(GetDlgItem(hDlg, IDOK));
\r
6180 SendMessage(GetDlgItem(hDlg, OPT_CommentText),
\r
6181 WM_SETFONT, (WPARAM)font[boardSize][COMMENT_FONT]->hf,
\r
6182 MAKELPARAM(FALSE, 0));
\r
6183 /* Size and position the dialog */
\r
6184 if (!commentDialog) {
\r
6185 commentDialog = hDlg;
\r
6186 flags = SWP_NOZORDER;
\r
6187 GetClientRect(hDlg, &rect);
\r
6188 sizeX = rect.right;
\r
6189 sizeY = rect.bottom;
\r
6190 if (wpComment.x != CW_USEDEFAULT && wpComment.y != CW_USEDEFAULT &&
\r
6191 wpComment.width != CW_USEDEFAULT && wpComment.height != CW_USEDEFAULT) {
\r
6192 WINDOWPLACEMENT wp;
\r
6193 EnsureOnScreen(&wpComment.x, &wpComment.y, 0, 0);
\r
6194 wp.length = sizeof(WINDOWPLACEMENT);
\r
6196 wp.showCmd = SW_SHOW;
\r
6197 wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;
\r
6198 wp.rcNormalPosition.left = wpComment.x;
\r
6199 wp.rcNormalPosition.right = wpComment.x + wpComment.width;
\r
6200 wp.rcNormalPosition.top = wpComment.y;
\r
6201 wp.rcNormalPosition.bottom = wpComment.y + wpComment.height;
\r
6202 SetWindowPlacement(hDlg, &wp);
\r
6204 GetClientRect(hDlg, &rect);
\r
6205 newSizeX = rect.right;
\r
6206 newSizeY = rect.bottom;
\r
6207 ResizeEditPlusButtons(hDlg, hwndText, sizeX, sizeY,
\r
6208 newSizeX, newSizeY);
\r
6213 SendDlgItemMessage( hDlg, OPT_CommentText, EM_SETEVENTMASK, 0, ENM_MOUSEEVENTS );
\r
6216 case WM_COMMAND: /* message: received a command */
\r
6217 switch (LOWORD(wParam)) {
\r
6219 if (editComment) {
\r
6221 /* Read changed options from the dialog box */
\r
6222 hwndText = GetDlgItem(hDlg, OPT_CommentText);
\r
6223 len = GetWindowTextLength(hwndText);
\r
6224 str = (char *) malloc(len + 1);
\r
6225 GetWindowText(hwndText, str, len + 1);
\r
6234 ReplaceComment(commentIndex, str);
\r
6241 case OPT_CancelComment:
\r
6245 case OPT_ClearComment:
\r
6246 SetDlgItemText(hDlg, OPT_CommentText, "");
\r
6249 case OPT_EditComment:
\r
6250 EditCommentEvent();
\r
6258 case WM_NOTIFY: // [HGM] vari: cloned from whistory.c
\r
6259 if( wParam == OPT_CommentText ) {
\r
6260 MSGFILTER * lpMF = (MSGFILTER *) lParam;
\r
6262 if( lpMF->msg == WM_RBUTTONDOWN && (lpMF->wParam & (MK_CONTROL | MK_SHIFT)) == 0 ) {
\r
6266 pt.x = LOWORD( lpMF->lParam );
\r
6267 pt.y = HIWORD( lpMF->lParam );
\r
6269 index = SendDlgItemMessage( hDlg, OPT_CommentText, EM_CHARFROMPOS, 0, (LPARAM) &pt );
\r
6271 hwndText = GetDlgItem(hDlg, OPT_CommentText); // cloned from above
\r
6272 len = GetWindowTextLength(hwndText);
\r
6273 str = (char *) malloc(len + 1);
\r
6274 GetWindowText(hwndText, str, len + 1);
\r
6275 ReplaceComment(commentIndex, str);
\r
6276 if(commentIndex != currentMove) ToNrEvent(commentIndex);
\r
6277 LoadVariation( index, str ); // [HGM] also does the actual moving to it, now
\r
6280 /* Zap the message for good: apparently, returning non-zero is not enough */
\r
6281 lpMF->msg = WM_USER;
\r
6289 newSizeX = LOWORD(lParam);
\r
6290 newSizeY = HIWORD(lParam);
\r
6291 ResizeEditPlusButtons(hDlg, hwndText, sizeX, sizeY, newSizeX, newSizeY);
\r
6296 case WM_GETMINMAXINFO:
\r
6297 /* Prevent resizing window too small */
\r
6298 mmi = (MINMAXINFO *) lParam;
\r
6299 mmi->ptMinTrackSize.x = 100;
\r
6300 mmi->ptMinTrackSize.y = 100;
\r
6307 EitherCommentPopUp(int index, char *title, char *str, BOOLEAN edit)
\r
6312 CheckMenuItem(GetMenu(hwndMain), IDM_EditComment, edit ? MF_CHECKED : MF_UNCHECKED);
\r
6314 if (str == NULL) str = "";
\r
6315 p = (char *) malloc(2 * strlen(str) + 2);
\r
6318 if (*str == '\n') *q++ = '\r';
\r
6322 if (commentText != NULL) free(commentText);
\r
6324 commentIndex = index;
\r
6325 commentTitle = title;
\r
6327 editComment = edit;
\r
6329 if (commentDialog) {
\r
6330 SendMessage(commentDialog, WM_INITDIALOG, 0, 0);
\r
6331 if (!commentUp) ShowWindow(commentDialog, SW_SHOW);
\r
6333 lpProc = MakeProcInstance((FARPROC)CommentDialog, hInst);
\r
6334 CreateDialog(hInst, MAKEINTRESOURCE(DLG_EditComment),
\r
6335 hwndMain, (DLGPROC)lpProc);
\r
6336 FreeProcInstance(lpProc);
\r
6342 /*---------------------------------------------------------------------------*\
\r
6344 * Type-in move dialog functions
\r
6346 \*---------------------------------------------------------------------------*/
\r
6349 TypeInMoveDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
6351 char move[MSG_SIZ];
\r
6353 ChessMove moveType;
\r
6354 int fromX, fromY, toX, toY;
\r
6357 switch (message) {
\r
6358 case WM_INITDIALOG:
\r
6359 move[0] = (char) lParam;
\r
6360 move[1] = NULLCHAR;
\r
6361 CenterWindowEx(hDlg, GetWindow(hDlg, GW_OWNER), 1 );
\r
6362 Translate(hDlg, DLG_TypeInMove);
\r
6363 hInput = GetDlgItem(hDlg, OPT_Move);
\r
6364 SetWindowText(hInput, move);
\r
6366 SendMessage(hInput, EM_SETSEL, (WPARAM)9999, (LPARAM)9999);
\r
6370 switch (LOWORD(wParam)) {
\r
6372 shiftKey = GetKeyState(VK_SHIFT) < 0; // [HGM] remember last shift status
\r
6373 GetDlgItemText(hDlg, OPT_Move, move, sizeof(move));
\r
6374 { int n; Board board;
\r
6376 if(gameMode == EditPosition && ParseFEN(board, &n, move) ) {
\r
6377 EditPositionPasteFEN(move);
\r
6378 EndDialog(hDlg, TRUE);
\r
6381 // [HGM] movenum: allow move number to be typed in any mode
\r
6382 if(sscanf(move, "%d", &n) == 1 && n != 0 ) {
\r
6384 EndDialog(hDlg, TRUE);
\r
6388 if (gameMode != EditGame && currentMove != forwardMostMove &&
\r
6389 gameMode != Training) {
\r
6390 DisplayMoveError(_("Displayed move is not current"));
\r
6392 // GetDlgItemText(hDlg, OPT_Move, move, sizeof(move)); // moved upstream
\r
6393 int ok = ParseOneMove(move, gameMode == EditPosition ? blackPlaysFirst : currentMove,
\r
6394 &moveType, &fromX, &fromY, &toX, &toY, &promoChar);
\r
6395 if(!ok && move[0] >= 'a') { move[0] += 'A' - 'a'; ok = 2; } // [HGM] try also capitalized
\r
6396 if (ok==1 || ok && ParseOneMove(move, gameMode == EditPosition ? blackPlaysFirst : currentMove,
\r
6397 &moveType, &fromX, &fromY, &toX, &toY, &promoChar)) {
\r
6398 if (gameMode != Training)
\r
6399 forwardMostMove = currentMove;
\r
6400 UserMoveEvent(fromX, fromY, toX, toY, promoChar);
\r
6402 DisplayMoveError(_("Could not parse move"));
\r
6405 EndDialog(hDlg, TRUE);
\r
6408 EndDialog(hDlg, FALSE);
\r
6419 PopUpMoveDialog(char firstchar)
\r
6423 if ((gameMode == BeginningOfGame && !appData.icsActive) ||
\r
6424 gameMode == MachinePlaysWhite || gameMode == MachinePlaysBlack ||
\r
6425 gameMode == AnalyzeMode || gameMode == EditGame ||
\r
6426 gameMode == EditPosition || gameMode == IcsExamining ||
\r
6427 gameMode == IcsPlayingWhite || gameMode == IcsPlayingBlack ||
\r
6428 isdigit(firstchar) && // [HGM] movenum: allow typing in of move nr in 'passive' modes
\r
6429 ( gameMode == AnalyzeFile || gameMode == PlayFromGameFile ||
\r
6430 gameMode == IcsObserving || gameMode == TwoMachinesPlay ) ||
\r
6431 gameMode == Training) {
\r
6432 lpProc = MakeProcInstance((FARPROC)TypeInMoveDialog, hInst);
\r
6433 DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_TypeInMove),
\r
6434 hwndMain, (DLGPROC)lpProc, (LPARAM)firstchar);
\r
6435 FreeProcInstance(lpProc);
\r
6439 /*---------------------------------------------------------------------------*\
\r
6441 * Type-in name dialog functions
\r
6443 \*---------------------------------------------------------------------------*/
\r
6446 TypeInNameDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
6448 char move[MSG_SIZ];
\r
6451 switch (message) {
\r
6452 case WM_INITDIALOG:
\r
6453 move[0] = (char) lParam;
\r
6454 move[1] = NULLCHAR;
\r
6455 CenterWindowEx(hDlg, GetWindow(hDlg, GW_OWNER), 1 );
\r
6456 Translate(hDlg, DLG_TypeInName);
\r
6457 hInput = GetDlgItem(hDlg, OPT_Name);
\r
6458 SetWindowText(hInput, move);
\r
6460 SendMessage(hInput, EM_SETSEL, (WPARAM)9999, (LPARAM)9999);
\r
6464 switch (LOWORD(wParam)) {
\r
6466 GetDlgItemText(hDlg, OPT_Name, move, sizeof(move));
\r
6467 appData.userName = strdup(move);
\r
6470 if(gameMode == MachinePlaysWhite || gameMode == MachinePlaysBlack) {
\r
6471 snprintf(move, MSG_SIZ, "%s vs. %s", gameInfo.white, gameInfo.black);
\r
6472 DisplayTitle(move);
\r
6476 EndDialog(hDlg, TRUE);
\r
6479 EndDialog(hDlg, FALSE);
\r
6490 PopUpNameDialog(char firstchar)
\r
6494 lpProc = MakeProcInstance((FARPROC)TypeInNameDialog, hInst);
\r
6495 DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_TypeInName),
\r
6496 hwndMain, (DLGPROC)lpProc, (LPARAM)firstchar);
\r
6497 FreeProcInstance(lpProc);
\r
6500 /*---------------------------------------------------------------------------*\
\r
6504 \*---------------------------------------------------------------------------*/
\r
6506 /* Nonmodal error box */
\r
6507 LRESULT CALLBACK ErrorDialog(HWND hDlg, UINT message,
\r
6508 WPARAM wParam, LPARAM lParam);
\r
6511 ErrorPopUp(char *title, char *content)
\r
6515 BOOLEAN modal = hwndMain == NULL;
\r
6533 strncpy(errorTitle, title, sizeof(errorTitle));
\r
6534 errorTitle[sizeof(errorTitle) - 1] = '\0';
\r
6537 MessageBox(NULL, errorMessage, errorTitle, MB_OK|MB_ICONEXCLAMATION);
\r
6539 lpProc = MakeProcInstance((FARPROC)ErrorDialog, hInst);
\r
6540 CreateDialog(hInst, MAKEINTRESOURCE(DLG_Error),
\r
6541 hwndMain, (DLGPROC)lpProc);
\r
6542 FreeProcInstance(lpProc);
\r
6549 if (!appData.popupMoveErrors && moveErrorMessageUp) DisplayMessage("", "");
\r
6550 if (errorDialog == NULL) return;
\r
6551 DestroyWindow(errorDialog);
\r
6552 errorDialog = NULL;
\r
6553 if(errorExitStatus) ExitEvent(errorExitStatus);
\r
6557 ErrorDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
6562 switch (message) {
\r
6563 case WM_INITDIALOG:
\r
6564 GetWindowRect(hDlg, &rChild);
\r
6567 SetWindowPos(hDlg, NULL, rChild.left,
\r
6568 rChild.top + boardRect.top - (rChild.bottom - rChild.top),
\r
6569 0, 0, SWP_NOZORDER|SWP_NOSIZE);
\r
6573 [AS] It seems that the above code wants to move the dialog up in the "caption
\r
6574 area" of the main window, but it uses the dialog height as an hard-coded constant,
\r
6575 and it doesn't work when you resize the dialog.
\r
6576 For now, just give it a default position.
\r
6578 SetWindowPos(hDlg, NULL, boardRect.left+8, boardRect.top+8, 0, 0, SWP_NOZORDER|SWP_NOSIZE);
\r
6579 Translate(hDlg, DLG_Error);
\r
6581 errorDialog = hDlg;
\r
6582 SetWindowText(hDlg, errorTitle);
\r
6583 hwndText = GetDlgItem(hDlg, OPT_ErrorText);
\r
6584 SetDlgItemText(hDlg, OPT_ErrorText, errorMessage);
\r
6588 switch (LOWORD(wParam)) {
\r
6591 if (errorDialog == hDlg) errorDialog = NULL;
\r
6592 DestroyWindow(hDlg);
\r
6604 HWND gothicDialog = NULL;
\r
6607 GothicDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
6611 int height = GetSystemMetrics(SM_CYCAPTION)+GetSystemMetrics(SM_CYFRAME);
\r
6613 switch (message) {
\r
6614 case WM_INITDIALOG:
\r
6615 GetWindowRect(hDlg, &rChild);
\r
6617 SetWindowPos(hDlg, NULL, wpMain.x, wpMain.y-height, wpMain.width, height,
\r
6621 [AS] It seems that the above code wants to move the dialog up in the "caption
\r
6622 area" of the main window, but it uses the dialog height as an hard-coded constant,
\r
6623 and it doesn't work when you resize the dialog.
\r
6624 For now, just give it a default position.
\r
6626 gothicDialog = hDlg;
\r
6627 SetWindowText(hDlg, errorTitle);
\r
6628 hwndText = GetDlgItem(hDlg, OPT_ErrorText);
\r
6629 SetDlgItemText(hDlg, OPT_ErrorText, errorMessage);
\r
6633 switch (LOWORD(wParam)) {
\r
6636 if (errorDialog == hDlg) errorDialog = NULL;
\r
6637 DestroyWindow(hDlg);
\r
6649 GothicPopUp(char *title, VariantClass variant)
\r
6652 static char *lastTitle;
\r
6654 strncpy(errorTitle, title, sizeof(errorTitle));
\r
6655 errorTitle[sizeof(errorTitle) - 1] = '\0';
\r
6657 if(lastTitle != title && gothicDialog != NULL) {
\r
6658 DestroyWindow(gothicDialog);
\r
6659 gothicDialog = NULL;
\r
6661 if(variant != VariantNormal && gothicDialog == NULL) {
\r
6662 title = lastTitle;
\r
6663 lpProc = MakeProcInstance((FARPROC)GothicDialog, hInst);
\r
6664 CreateDialog(hInst, MAKEINTRESOURCE(DLG_Error),
\r
6665 hwndMain, (DLGPROC)lpProc);
\r
6666 FreeProcInstance(lpProc);
\r
6671 /*---------------------------------------------------------------------------*\
\r
6673 * Ics Interaction console functions
\r
6675 \*---------------------------------------------------------------------------*/
\r
6677 #define HISTORY_SIZE 64
\r
6678 static char *history[HISTORY_SIZE];
\r
6679 int histIn = 0, histP = 0;
\r
6682 SaveInHistory(char *cmd)
\r
6684 if (history[histIn] != NULL) {
\r
6685 free(history[histIn]);
\r
6686 history[histIn] = NULL;
\r
6688 if (*cmd == NULLCHAR) return;
\r
6689 history[histIn] = StrSave(cmd);
\r
6690 histIn = (histIn + 1) % HISTORY_SIZE;
\r
6691 if (history[histIn] != NULL) {
\r
6692 free(history[histIn]);
\r
6693 history[histIn] = NULL;
\r
6699 PrevInHistory(char *cmd)
\r
6702 if (histP == histIn) {
\r
6703 if (history[histIn] != NULL) free(history[histIn]);
\r
6704 history[histIn] = StrSave(cmd);
\r
6706 newhp = (histP - 1 + HISTORY_SIZE) % HISTORY_SIZE;
\r
6707 if (newhp == histIn || history[newhp] == NULL) return NULL;
\r
6709 return history[histP];
\r
6715 if (histP == histIn) return NULL;
\r
6716 histP = (histP + 1) % HISTORY_SIZE;
\r
6717 return history[histP];
\r
6721 LoadIcsTextMenu(IcsTextMenuEntry *e)
\r
6725 hmenu = LoadMenu(hInst, "TextMenu");
\r
6726 h = GetSubMenu(hmenu, 0);
\r
6728 if (strcmp(e->item, "-") == 0) {
\r
6729 AppendMenu(h, MF_SEPARATOR, 0, 0);
\r
6730 } else { // [HGM] re-written a bit to use only one AppendMenu call for both cases (| or no |)
\r
6731 int flags = MF_STRING, j = 0;
\r
6732 if (e->item[0] == '|') {
\r
6733 flags |= MF_MENUBARBREAK;
\r
6736 if(!strcmp(e->command, "none")) flags |= MF_GRAYED; // [HGM] chatclick: provide inactive dummy
\r
6737 AppendMenu(h, flags, IDM_CommandX + i, e->item + j);
\r
6745 WNDPROC consoleTextWindowProc;
\r
6748 CommandX(HWND hwnd, char *command, BOOLEAN getname, BOOLEAN immediate)
\r
6750 char buf[MSG_SIZ], name[MSG_SIZ];
\r
6751 HWND hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
6755 SetWindowText(hInput, command);
\r
6757 SendMessage(hInput, WM_CHAR, '\r', 0);
\r
6759 sel.cpMin = 999999;
\r
6760 sel.cpMax = 999999;
\r
6761 SendMessage(hInput, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
6766 SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);
\r
6767 if (sel.cpMin == sel.cpMax) {
\r
6768 /* Expand to surrounding word */
\r
6771 tr.chrg.cpMax = sel.cpMin;
\r
6772 tr.chrg.cpMin = --sel.cpMin;
\r
6773 if (sel.cpMin < 0) break;
\r
6774 tr.lpstrText = name;
\r
6775 SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr);
\r
6776 } while (isalpha(name[0]) || isdigit(name[0]) || name[0] == '-');
\r
6780 tr.chrg.cpMin = sel.cpMax;
\r
6781 tr.chrg.cpMax = ++sel.cpMax;
\r
6782 tr.lpstrText = name;
\r
6783 if (SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr) < 1) break;
\r
6784 } while (isalpha(name[0]) || isdigit(name[0]) || name[0] == '-');
\r
6787 if (sel.cpMax == sel.cpMin || sel.cpMax - sel.cpMin > MSG_SIZ/2) {
\r
6788 MessageBeep(MB_ICONEXCLAMATION);
\r
6792 tr.lpstrText = name;
\r
6793 SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr);
\r
6795 if (sel.cpMax - sel.cpMin > MSG_SIZ/2) {
\r
6796 MessageBeep(MB_ICONEXCLAMATION);
\r
6799 SendMessage(hwnd, EM_GETSELTEXT, 0, (LPARAM) name);
\r
6802 snprintf(buf, MSG_SIZ, "%s %s", command, name);
\r
6803 SetWindowText(hInput, buf);
\r
6804 SendMessage(hInput, WM_CHAR, '\r', 0);
\r
6806 if(!strcmp(command, "chat")) { ChatPopUp(name); return; }
\r
6807 snprintf(buf, MSG_SIZ, "%s %s ", command, name); /* trailing space */
\r
6808 SetWindowText(hInput, buf);
\r
6809 sel.cpMin = 999999;
\r
6810 sel.cpMax = 999999;
\r
6811 SendMessage(hInput, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
6817 ConsoleTextSubclass(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
\r
6822 switch (message) {
\r
6824 if (!(GetKeyState(VK_CONTROL) & ~1)) break;
\r
6827 SendMessage(hwnd, EM_LINESCROLL, 0, -999999);
\r
6830 sel.cpMin = 999999;
\r
6831 sel.cpMax = 999999;
\r
6832 SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
6833 SendMessage(hwnd, EM_SCROLLCARET, 0, 0);
\r
6838 if(wParam != '\022') {
\r
6839 if (wParam == '\t') {
\r
6840 if (GetKeyState(VK_SHIFT) < 0) {
\r
6842 if (IsIconic(hwndMain)) ShowWindow(hwndMain, SW_RESTORE);
\r
6843 if (buttonDesc[0].hwnd) {
\r
6844 SetFocus(buttonDesc[0].hwnd);
\r
6846 SetFocus(hwndMain);
\r
6850 SetFocus(GetDlgItem(hwndConsole, OPT_ConsoleInput));
\r
6853 hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
6854 JAWS_DELETE( SetFocus(hInput); )
\r
6855 SendMessage(hInput, message, wParam, lParam);
\r
6858 } // [HGM] navigate: for Ctrl+R, flow into nex case (moved up here) to summon up menu
\r
6859 case WM_RBUTTONDOWN:
\r
6860 if (!(GetKeyState(VK_SHIFT) & ~1)) {
\r
6861 /* Move selection here if it was empty */
\r
6863 pt.x = LOWORD(lParam);
\r
6864 pt.y = HIWORD(lParam);
\r
6865 SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);
\r
6866 if (sel.cpMin == sel.cpMax) {
\r
6867 sel.cpMin = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&pt); /*doc is wrong*/
\r
6868 sel.cpMax = sel.cpMin;
\r
6869 SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
6871 SendMessage(hwnd, EM_HIDESELECTION, FALSE, FALSE);
\r
6872 { // [HGM] chatclick: code moved here from WM_RBUTTONUP case, to have menu appear on down-click
\r
6874 HMENU hmenu = LoadIcsTextMenu(icsTextMenuEntry);
\r
6875 SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);
\r
6876 if (sel.cpMin == sel.cpMax) {
\r
6877 EnableMenuItem(hmenu, IDM_Copy, MF_BYCOMMAND|MF_GRAYED);
\r
6878 EnableMenuItem(hmenu, IDM_QuickPaste, MF_BYCOMMAND|MF_GRAYED);
\r
6880 if (!IsClipboardFormatAvailable(CF_TEXT)) {
\r
6881 EnableMenuItem(hmenu, IDM_Paste, MF_BYCOMMAND|MF_GRAYED);
\r
6883 pt.x = LOWORD(lParam)-30; // [HGM] chatclick: make menu pop up with pointer above upper-right item
\r
6884 pt.y = HIWORD(lParam)-10; // make it appear as if mouse moved there, so it will be selected on up-click
\r
6885 PostMessage(hwnd, WM_MOUSEMOVE, wParam, lParam+5);
\r
6886 MenuPopup(hwnd, pt, hmenu, -1);
\r
6890 case WM_RBUTTONUP:
\r
6891 if (GetKeyState(VK_SHIFT) & ~1) {
\r
6892 SendDlgItemMessage(hwndConsole, OPT_ConsoleText,
\r
6893 WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);
\r
6897 hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
6899 return SendMessage(hInput, message, wParam, lParam);
\r
6900 case WM_MBUTTONDOWN:
\r
6901 return SendMessage(hwnd, WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);
\r
6903 switch (LOWORD(wParam)) {
\r
6904 case IDM_QuickPaste:
\r
6906 SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);
\r
6907 if (sel.cpMin == sel.cpMax) {
\r
6908 MessageBeep(MB_ICONEXCLAMATION);
\r
6911 SendMessage(hwnd, WM_COPY, 0, 0);
\r
6912 hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
6913 SendMessage(hInput, WM_PASTE, 0, 0);
\r
6918 SendMessage(hwnd, WM_CUT, 0, 0);
\r
6921 SendMessage(hwnd, WM_PASTE, 0, 0);
\r
6924 SendMessage(hwnd, WM_COPY, 0, 0);
\r
6928 int i = LOWORD(wParam) - IDM_CommandX;
\r
6929 if (i >= 0 && i < ICS_TEXT_MENU_SIZE &&
\r
6930 icsTextMenuEntry[i].command != NULL) {
\r
6931 CommandX(hwnd, icsTextMenuEntry[i].command,
\r
6932 icsTextMenuEntry[i].getname,
\r
6933 icsTextMenuEntry[i].immediate);
\r
6941 return (*consoleTextWindowProc)(hwnd, message, wParam, lParam);
\r
6944 WNDPROC consoleInputWindowProc;
\r
6947 ConsoleInputSubclass(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
\r
6949 char buf[MSG_SIZ];
\r
6951 static BOOL sendNextChar = FALSE;
\r
6952 static BOOL quoteNextChar = FALSE;
\r
6953 InputSource *is = consoleInputSource;
\r
6957 switch (message) {
\r
6959 if (!appData.localLineEditing || sendNextChar) {
\r
6960 is->buf[0] = (CHAR) wParam;
\r
6962 SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);
\r
6963 sendNextChar = FALSE;
\r
6966 if (quoteNextChar) {
\r
6967 buf[0] = (char) wParam;
\r
6968 buf[1] = NULLCHAR;
\r
6969 SendMessage(hwnd, EM_REPLACESEL, TRUE, (LPARAM) buf);
\r
6970 quoteNextChar = FALSE;
\r
6974 case '\r': /* Enter key */
\r
6975 is->count = GetWindowText(hwnd, is->buf, INPUT_SOURCE_BUF_SIZE-1);
\r
6976 if (consoleEcho) SaveInHistory(is->buf);
\r
6977 is->buf[is->count++] = '\n';
\r
6978 is->buf[is->count] = NULLCHAR;
\r
6979 SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);
\r
6980 if (consoleEcho) {
\r
6981 ConsoleOutput(is->buf, is->count, TRUE);
\r
6982 } else if (appData.localLineEditing) {
\r
6983 ConsoleOutput("\n", 1, TRUE);
\r
6986 case '\033': /* Escape key */
\r
6987 SetWindowText(hwnd, "");
\r
6988 cf.cbSize = sizeof(CHARFORMAT);
\r
6989 cf.dwMask = CFM_COLOR|CFM_BOLD|CFM_ITALIC|CFM_UNDERLINE|CFM_STRIKEOUT;
\r
6990 if (consoleEcho) {
\r
6991 cf.crTextColor = textAttribs[ColorNormal].color;
\r
6993 cf.crTextColor = COLOR_ECHOOFF;
\r
6995 cf.dwEffects = textAttribs[ColorNormal].effects;
\r
6996 SendMessage(hwnd, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf);
\r
6998 case '\t': /* Tab key */
\r
6999 if (GetKeyState(VK_SHIFT) < 0) {
\r
7001 SetFocus(GetDlgItem(hwndConsole, OPT_ConsoleText));
\r
7004 if (IsIconic(hwndMain)) ShowWindow(hwndMain, SW_RESTORE);
\r
7005 if (buttonDesc[0].hwnd) {
\r
7006 SetFocus(buttonDesc[0].hwnd);
\r
7008 SetFocus(hwndMain);
\r
7012 case '\023': /* Ctrl+S */
\r
7013 sendNextChar = TRUE;
\r
7015 case '\021': /* Ctrl+Q */
\r
7016 quoteNextChar = TRUE;
\r
7026 GetWindowText(hwnd, buf, MSG_SIZ);
\r
7027 p = PrevInHistory(buf);
\r
7029 SetWindowText(hwnd, p);
\r
7030 sel.cpMin = 999999;
\r
7031 sel.cpMax = 999999;
\r
7032 SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
7037 p = NextInHistory();
\r
7039 SetWindowText(hwnd, p);
\r
7040 sel.cpMin = 999999;
\r
7041 sel.cpMax = 999999;
\r
7042 SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
7048 if (!(GetKeyState(VK_CONTROL) & ~1)) break;
\r
7052 SendDlgItemMessage(hwndConsole, OPT_ConsoleText, message, wParam, lParam);
\r
7056 case WM_MBUTTONDOWN:
\r
7057 SendDlgItemMessage(hwndConsole, OPT_ConsoleText,
\r
7058 WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);
\r
7060 case WM_RBUTTONUP:
\r
7061 if (GetKeyState(VK_SHIFT) & ~1) {
\r
7062 SendDlgItemMessage(hwndConsole, OPT_ConsoleText,
\r
7063 WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);
\r
7067 hmenu = LoadMenu(hInst, "InputMenu");
\r
7068 SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);
\r
7069 if (sel.cpMin == sel.cpMax) {
\r
7070 EnableMenuItem(hmenu, IDM_Copy, MF_BYCOMMAND|MF_GRAYED);
\r
7071 EnableMenuItem(hmenu, IDM_Cut, MF_BYCOMMAND|MF_GRAYED);
\r
7073 if (!IsClipboardFormatAvailable(CF_TEXT)) {
\r
7074 EnableMenuItem(hmenu, IDM_Paste, MF_BYCOMMAND|MF_GRAYED);
\r
7076 pt.x = LOWORD(lParam);
\r
7077 pt.y = HIWORD(lParam);
\r
7078 MenuPopup(hwnd, pt, hmenu, -1);
\r
7082 switch (LOWORD(wParam)) {
\r
7084 SendMessage(hwnd, EM_UNDO, 0, 0);
\r
7086 case IDM_SelectAll:
\r
7088 sel.cpMax = -1; /*999999?*/
\r
7089 SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
7092 SendMessage(hwnd, WM_CUT, 0, 0);
\r
7095 SendMessage(hwnd, WM_PASTE, 0, 0);
\r
7098 SendMessage(hwnd, WM_COPY, 0, 0);
\r
7103 return (*consoleInputWindowProc)(hwnd, message, wParam, lParam);
\r
7106 #define CO_MAX 100000
\r
7107 #define CO_TRIM 1000
\r
7110 ConsoleWndProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
7112 static SnapData sd;
\r
7113 HWND hText, hInput;
\r
7115 static int sizeX, sizeY;
\r
7116 int newSizeX, newSizeY;
\r
7120 hText = GetDlgItem(hDlg, OPT_ConsoleText);
\r
7121 hInput = GetDlgItem(hDlg, OPT_ConsoleInput);
\r
7123 switch (message) {
\r
7125 if (((NMHDR*)lParam)->code == EN_LINK)
\r
7127 ENLINK *pLink = (ENLINK*)lParam;
\r
7128 if (pLink->msg == WM_LBUTTONUP)
\r
7132 tr.chrg = pLink->chrg;
\r
7133 tr.lpstrText = malloc(1+tr.chrg.cpMax-tr.chrg.cpMin);
\r
7134 SendMessage(hText, EM_GETTEXTRANGE, 0, (LPARAM)&tr);
\r
7135 ShellExecute(NULL, "open", tr.lpstrText, NULL, NULL, SW_SHOW);
\r
7136 free(tr.lpstrText);
\r
7140 case WM_INITDIALOG: /* message: initialize dialog box */
\r
7141 hwndConsole = hDlg;
\r
7143 consoleTextWindowProc = (WNDPROC)
\r
7144 SetWindowLong(hText, GWL_WNDPROC, (LONG) ConsoleTextSubclass);
\r
7145 SendMessage(hText, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);
\r
7146 consoleInputWindowProc = (WNDPROC)
\r
7147 SetWindowLong(hInput, GWL_WNDPROC, (LONG) ConsoleInputSubclass);
\r
7148 SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);
\r
7149 Colorize(ColorNormal, TRUE);
\r
7150 SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &consoleCF);
\r
7151 ChangedConsoleFont();
\r
7152 GetClientRect(hDlg, &rect);
\r
7153 sizeX = rect.right;
\r
7154 sizeY = rect.bottom;
\r
7155 if (wpConsole.x != CW_USEDEFAULT && wpConsole.y != CW_USEDEFAULT &&
\r
7156 wpConsole.width != CW_USEDEFAULT && wpConsole.height != CW_USEDEFAULT) {
\r
7157 WINDOWPLACEMENT wp;
\r
7158 EnsureOnScreen(&wpConsole.x, &wpConsole.y, 0, 0);
\r
7159 wp.length = sizeof(WINDOWPLACEMENT);
\r
7161 wp.showCmd = SW_SHOW;
\r
7162 wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;
\r
7163 wp.rcNormalPosition.left = wpConsole.x;
\r
7164 wp.rcNormalPosition.right = wpConsole.x + wpConsole.width;
\r
7165 wp.rcNormalPosition.top = wpConsole.y;
\r
7166 wp.rcNormalPosition.bottom = wpConsole.y + wpConsole.height;
\r
7167 SetWindowPlacement(hDlg, &wp);
\r
7170 // [HGM] Chessknight's change 2004-07-13
\r
7171 else { /* Determine Defaults */
\r
7172 WINDOWPLACEMENT wp;
\r
7173 wpConsole.x = wpMain.width + 1;
\r
7174 wpConsole.y = wpMain.y;
\r
7175 wpConsole.width = screenWidth - wpMain.width;
\r
7176 wpConsole.height = wpMain.height;
\r
7177 EnsureOnScreen(&wpConsole.x, &wpConsole.y, 0, 0);
\r
7178 wp.length = sizeof(WINDOWPLACEMENT);
\r
7180 wp.showCmd = SW_SHOW;
\r
7181 wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;
\r
7182 wp.rcNormalPosition.left = wpConsole.x;
\r
7183 wp.rcNormalPosition.right = wpConsole.x + wpConsole.width;
\r
7184 wp.rcNormalPosition.top = wpConsole.y;
\r
7185 wp.rcNormalPosition.bottom = wpConsole.y + wpConsole.height;
\r
7186 SetWindowPlacement(hDlg, &wp);
\r
7189 // Allow hText to highlight URLs and send notifications on them
\r
7190 wMask = (WORD) SendMessage(hText, EM_GETEVENTMASK, 0, 0L);
\r
7191 SendMessage(hText, EM_SETEVENTMASK, 0, wMask | ENM_LINK);
\r
7192 SendMessage(hText, EM_AUTOURLDETECT, TRUE, 0L);
\r
7193 SetWindowLong(hText, GWL_USERDATA, 79); // initialize the text window's width
\r
7207 if (IsIconic(hDlg)) break;
\r
7208 newSizeX = LOWORD(lParam);
\r
7209 newSizeY = HIWORD(lParam);
\r
7210 if (sizeX != newSizeX || sizeY != newSizeY) {
\r
7211 RECT rectText, rectInput;
\r
7213 int newTextHeight, newTextWidth;
\r
7214 GetWindowRect(hText, &rectText);
\r
7215 newTextWidth = rectText.right - rectText.left + newSizeX - sizeX;
\r
7216 newTextHeight = rectText.bottom - rectText.top + newSizeY - sizeY;
\r
7217 if (newTextHeight < 0) {
\r
7218 newSizeY += -newTextHeight;
\r
7219 newTextHeight = 0;
\r
7221 SetWindowPos(hText, NULL, 0, 0,
\r
7222 newTextWidth, newTextHeight, SWP_NOZORDER|SWP_NOMOVE);
\r
7223 GetWindowRect(hInput, &rectInput); /* gives screen coords */
\r
7224 pt.x = rectInput.left;
\r
7225 pt.y = rectInput.top + newSizeY - sizeY;
\r
7226 ScreenToClient(hDlg, &pt);
\r
7227 SetWindowPos(hInput, NULL,
\r
7228 pt.x, pt.y, /* needs client coords */
\r
7229 rectInput.right - rectInput.left + newSizeX - sizeX,
\r
7230 rectInput.bottom - rectInput.top, SWP_NOZORDER);
\r
7236 case WM_GETMINMAXINFO:
\r
7237 /* Prevent resizing window too small */
\r
7238 mmi = (MINMAXINFO *) lParam;
\r
7239 mmi->ptMinTrackSize.x = 100;
\r
7240 mmi->ptMinTrackSize.y = 100;
\r
7243 /* [AS] Snapping */
\r
7244 case WM_ENTERSIZEMOVE:
\r
7245 return OnEnterSizeMove( &sd, hDlg, wParam, lParam );
\r
7248 return OnSizing( &sd, hDlg, wParam, lParam );
\r
7251 return OnMoving( &sd, hDlg, wParam, lParam );
\r
7253 case WM_EXITSIZEMOVE:
\r
7254 UpdateICSWidth(hText);
\r
7255 return OnExitSizeMove( &sd, hDlg, wParam, lParam );
\r
7258 return DefWindowProc(hDlg, message, wParam, lParam);
\r
7266 if (hwndConsole) return;
\r
7267 hCons = CreateDialog(hInst, szConsoleName, 0, NULL);
\r
7268 SendMessage(hCons, WM_INITDIALOG, 0, 0);
\r
7273 ConsoleOutput(char* data, int length, int forceVisible)
\r
7278 char buf[CO_MAX+1];
\r
7281 static int delayLF = 0;
\r
7282 CHARRANGE savesel, sel;
\r
7284 if (hwndConsole == NULL || length > CO_MAX-100 || length == 0) return;
\r
7292 while (length--) {
\r
7300 } else if (*p == '\007') {
\r
7301 MyPlaySound(&sounds[(int)SoundBell]);
\r
7308 hText = GetDlgItem(hwndConsole, OPT_ConsoleText);
\r
7309 SendMessage(hText, EM_HIDESELECTION, TRUE, FALSE);
\r
7310 /* Save current selection */
\r
7311 SendMessage(hText, EM_EXGETSEL, 0, (LPARAM)&savesel);
\r
7312 exlen = GetWindowTextLength(hText);
\r
7313 /* Find out whether current end of text is visible */
\r
7314 SendMessage(hText, EM_GETRECT, 0, (LPARAM) &rect);
\r
7315 SendMessage(hText, EM_POSFROMCHAR, (WPARAM) &pEnd, exlen);
\r
7316 /* Trim existing text if it's too long */
\r
7317 if (exlen + (q - buf) > CO_MAX) {
\r
7318 trim = (CO_TRIM > (q - buf)) ? CO_TRIM : (q - buf);
\r
7321 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
7322 SendMessage(hText, EM_REPLACESEL, 0, (LPARAM)"");
\r
7324 savesel.cpMin -= trim;
\r
7325 savesel.cpMax -= trim;
\r
7326 if (exlen < 0) exlen = 0;
\r
7327 if (savesel.cpMin < 0) savesel.cpMin = 0;
\r
7328 if (savesel.cpMax < savesel.cpMin) savesel.cpMax = savesel.cpMin;
\r
7330 /* Append the new text */
\r
7331 sel.cpMin = exlen;
\r
7332 sel.cpMax = exlen;
\r
7333 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
7334 SendMessage(hText, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&consoleCF);
\r
7335 SendMessage(hText, EM_REPLACESEL, 0, (LPARAM) buf);
\r
7336 if (forceVisible || exlen == 0 ||
\r
7337 (rect.left <= pEnd.x && pEnd.x < rect.right &&
\r
7338 rect.top <= pEnd.y && pEnd.y < rect.bottom)) {
\r
7339 /* Scroll to make new end of text visible if old end of text
\r
7340 was visible or new text is an echo of user typein */
\r
7341 sel.cpMin = 9999999;
\r
7342 sel.cpMax = 9999999;
\r
7343 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
7344 SendMessage(hText, EM_HIDESELECTION, FALSE, FALSE);
\r
7345 SendMessage(hText, EM_SCROLLCARET, 0, 0);
\r
7346 SendMessage(hText, EM_HIDESELECTION, TRUE, FALSE);
\r
7348 if (savesel.cpMax == exlen || forceVisible) {
\r
7349 /* Move insert point to new end of text if it was at the old
\r
7350 end of text or if the new text is an echo of user typein */
\r
7351 sel.cpMin = 9999999;
\r
7352 sel.cpMax = 9999999;
\r
7353 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
7355 /* Restore previous selection */
\r
7356 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&savesel);
\r
7358 SendMessage(hText, EM_HIDESELECTION, FALSE, FALSE);
\r
7365 DisplayHoldingsCount(HDC hdc, int x, int y, int rightAlign, int copyNumber)
\r
7369 COLORREF oldFg, oldBg;
\r
7374 snprintf(buf, sizeof(buf)/sizeof(buf[0]),"%d", copyNumber); else buf[0] = 0;
\r
7376 oldFg = SetTextColor(hdc, RGB(255, 255, 255)); /* white */
\r
7377 oldBg = SetBkColor(hdc, RGB(0, 0, 0)); /* black */
\r
7378 oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);
\r
7381 rect.right = x + squareSize;
\r
7383 rect.bottom = y + squareSize;
\r
7386 ExtTextOut(hdc, x + MESSAGE_LINE_LEFTMARGIN
\r
7387 + (rightAlign ? (squareSize*2)/3 : 0),
\r
7388 y, ETO_CLIPPED|ETO_OPAQUE,
\r
7389 &rect, str, strlen(str), NULL);
\r
7391 (void) SetTextColor(hdc, oldFg);
\r
7392 (void) SetBkColor(hdc, oldBg);
\r
7393 (void) SelectObject(hdc, oldFont);
\r
7397 DisplayAClock(HDC hdc, int timeRemaining, int highlight,
\r
7398 RECT *rect, char *color, char *flagFell)
\r
7402 COLORREF oldFg, oldBg;
\r
7405 if (appData.clockMode) {
\r
7407 snprintf(buf, sizeof(buf)/sizeof(buf[0]), "%c %s %s", color[0], TimeString(timeRemaining), flagFell);
\r
7409 snprintf(buf, sizeof(buf)/sizeof(buf[0]), "%s:%c%s %s", color, (logoHeight>0 ? 0 : ' '), TimeString(timeRemaining), flagFell);
\r
7416 oldFg = SetTextColor(hdc, RGB(255, 255, 255)); /* white */
\r
7417 oldBg = SetBkColor(hdc, RGB(0, 0, 0)); /* black */
\r
7419 oldFg = SetTextColor(hdc, RGB(0, 0, 0)); /* black */
\r
7420 oldBg = SetBkColor(hdc, RGB(255, 255, 255)); /* white */
\r
7422 oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);
\r
7426 ExtTextOut(hdc, rect->left + MESSAGE_LINE_LEFTMARGIN,
\r
7427 rect->top, ETO_CLIPPED|ETO_OPAQUE,
\r
7428 rect, str, strlen(str), NULL);
\r
7429 if(logoHeight > 0 && appData.clockMode) {
\r
7431 snprintf(buf, sizeof(buf)/sizeof(buf[0]), "%s %s", buf+7, flagFell);
\r
7432 r.top = rect->top + logoHeight/2;
\r
7433 r.left = rect->left;
\r
7434 r.right = rect->right;
\r
7435 r.bottom = rect->bottom;
\r
7436 ExtTextOut(hdc, rect->left + MESSAGE_LINE_LEFTMARGIN,
\r
7437 r.top, ETO_CLIPPED|ETO_OPAQUE,
\r
7438 &r, str, strlen(str), NULL);
\r
7440 (void) SetTextColor(hdc, oldFg);
\r
7441 (void) SetBkColor(hdc, oldBg);
\r
7442 (void) SelectObject(hdc, oldFont);
\r
7447 DoReadFile(HANDLE hFile, char *buf, int count, DWORD *outCount,
\r
7453 if( count <= 0 ) {
\r
7454 if (appData.debugMode) {
\r
7455 fprintf( debugFP, "DoReadFile: trying to read past end of buffer, overflow = %d\n", count );
\r
7458 return ERROR_INVALID_USER_BUFFER;
\r
7461 ResetEvent(ovl->hEvent);
\r
7462 ovl->Offset = ovl->OffsetHigh = 0;
\r
7463 ok = ReadFile(hFile, buf, count, outCount, ovl);
\r
7467 err = GetLastError();
\r
7468 if (err == ERROR_IO_PENDING) {
\r
7469 ok = GetOverlappedResult(hFile, ovl, outCount, TRUE);
\r
7473 err = GetLastError();
\r
7480 DoWriteFile(HANDLE hFile, char *buf, int count, DWORD *outCount,
\r
7485 ResetEvent(ovl->hEvent);
\r
7486 ovl->Offset = ovl->OffsetHigh = 0;
\r
7487 ok = WriteFile(hFile, buf, count, outCount, ovl);
\r
7491 err = GetLastError();
\r
7492 if (err == ERROR_IO_PENDING) {
\r
7493 ok = GetOverlappedResult(hFile, ovl, outCount, TRUE);
\r
7497 err = GetLastError();
\r
7503 /* [AS] If input is line by line and a line exceed the buffer size, force an error */
\r
7504 void CheckForInputBufferFull( InputSource * is )
\r
7506 if( is->lineByLine && (is->next - is->buf) >= INPUT_SOURCE_BUF_SIZE ) {
\r
7507 /* Look for end of line */
\r
7508 char * p = is->buf;
\r
7510 while( p < is->next && *p != '\n' ) {
\r
7514 if( p >= is->next ) {
\r
7515 if (appData.debugMode) {
\r
7516 fprintf( debugFP, "Input line exceeded buffer size (source id=%lu)\n", is->id );
\r
7519 is->error = ERROR_BROKEN_PIPE; /* [AS] Just any non-successful code! */
\r
7520 is->count = (DWORD) -1;
\r
7521 is->next = is->buf;
\r
7527 InputThread(LPVOID arg)
\r
7532 is = (InputSource *) arg;
\r
7533 ovl.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
\r
7534 ovl.Internal = ovl.InternalHigh = ovl.Offset = ovl.OffsetHigh = 0;
\r
7535 while (is->hThread != NULL) {
\r
7536 is->error = DoReadFile(is->hFile, is->next,
\r
7537 INPUT_SOURCE_BUF_SIZE - (is->next - is->buf),
\r
7538 &is->count, &ovl);
\r
7539 if (is->error == NO_ERROR) {
\r
7540 is->next += is->count;
\r
7542 if (is->error == ERROR_BROKEN_PIPE) {
\r
7543 /* Correct for MS brain damage. EOF reading a pipe is not an error. */
\r
7546 is->count = (DWORD) -1;
\r
7547 /* [AS] The (is->count <= 0) check below is not useful for unsigned values! */
\r
7552 CheckForInputBufferFull( is );
\r
7554 SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);
\r
7556 if( is->count == ((DWORD) -1) ) break; /* [AS] */
\r
7558 if (is->count <= 0) break; /* Quit on EOF or error */
\r
7561 CloseHandle(ovl.hEvent);
\r
7562 CloseHandle(is->hFile);
\r
7564 if (appData.debugMode) {
\r
7565 fprintf( debugFP, "Input thread terminated (id=%lu, error=%d, count=%ld)\n", is->id, is->error, is->count );
\r
7572 /* Windows 95 beta 2 won't let you do overlapped i/o on a console or pipe */
\r
7574 NonOvlInputThread(LPVOID arg)
\r
7581 is = (InputSource *) arg;
\r
7582 while (is->hThread != NULL) {
\r
7583 is->error = ReadFile(is->hFile, is->next,
\r
7584 INPUT_SOURCE_BUF_SIZE - (is->next - is->buf),
\r
7585 &is->count, NULL) ? NO_ERROR : GetLastError();
\r
7586 if (is->error == NO_ERROR) {
\r
7587 /* Change CRLF to LF */
\r
7588 if (is->next > is->buf) {
\r
7590 i = is->count + 1;
\r
7598 if (prev == '\r' && *p == '\n') {
\r
7610 if (is->error == ERROR_BROKEN_PIPE) {
\r
7611 /* Correct for MS brain damage. EOF reading a pipe is not an error. */
\r
7614 is->count = (DWORD) -1;
\r
7618 CheckForInputBufferFull( is );
\r
7620 SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);
\r
7622 if( is->count == ((DWORD) -1) ) break; /* [AS] */
\r
7624 if (is->count < 0) break; /* Quit on error */
\r
7626 CloseHandle(is->hFile);
\r
7631 SocketInputThread(LPVOID arg)
\r
7635 is = (InputSource *) arg;
\r
7636 while (is->hThread != NULL) {
\r
7637 is->count = recv(is->sock, is->buf, INPUT_SOURCE_BUF_SIZE, 0);
\r
7638 if ((int)is->count == SOCKET_ERROR) {
\r
7639 is->count = (DWORD) -1;
\r
7640 is->error = WSAGetLastError();
\r
7642 is->error = NO_ERROR;
\r
7643 is->next += is->count;
\r
7644 if (is->count == 0 && is->second == is) {
\r
7645 /* End of file on stderr; quit with no message */
\r
7649 SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);
\r
7651 if( is->count == ((DWORD) -1) ) break; /* [AS] */
\r
7653 if (is->count <= 0) break; /* Quit on EOF or error */
\r
7659 InputEvent(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
\r
7663 is = (InputSource *) lParam;
\r
7664 if (is->lineByLine) {
\r
7665 /* Feed in lines one by one */
\r
7666 char *p = is->buf;
\r
7668 while (q < is->next) {
\r
7669 if (*q++ == '\n') {
\r
7670 (is->func)(is, is->closure, p, q - p, NO_ERROR);
\r
7675 /* Move any partial line to the start of the buffer */
\r
7677 while (p < is->next) {
\r
7682 if (is->error != NO_ERROR || is->count == 0) {
\r
7683 /* Notify backend of the error. Note: If there was a partial
\r
7684 line at the end, it is not flushed through. */
\r
7685 (is->func)(is, is->closure, is->buf, is->count, is->error);
\r
7688 /* Feed in the whole chunk of input at once */
\r
7689 (is->func)(is, is->closure, is->buf, is->count, is->error);
\r
7690 is->next = is->buf;
\r
7694 /*---------------------------------------------------------------------------*\
\r
7696 * Menu enables. Used when setting various modes.
\r
7698 \*---------------------------------------------------------------------------*/
\r
7706 GreyRevert(Boolean grey)
\r
7707 { // [HGM] vari: for retracting variations in local mode
\r
7708 HMENU hmenu = GetMenu(hwndMain);
\r
7709 EnableMenuItem(hmenu, IDM_Revert, MF_BYCOMMAND|(grey ? MF_GRAYED : MF_ENABLED));
\r
7710 EnableMenuItem(hmenu, IDM_Annotate, MF_BYCOMMAND|(grey ? MF_GRAYED : MF_ENABLED));
\r
7714 SetMenuEnables(HMENU hmenu, Enables *enab)
\r
7716 while (enab->item > 0) {
\r
7717 (void) EnableMenuItem(hmenu, enab->item, enab->flags);
\r
7722 Enables gnuEnables[] = {
\r
7723 { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },
\r
7724 { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },
\r
7725 { IDM_IcsClient, MF_BYCOMMAND|MF_GRAYED },
\r
7726 { IDM_Accept, MF_BYCOMMAND|MF_GRAYED },
\r
7727 { IDM_Decline, MF_BYCOMMAND|MF_GRAYED },
\r
7728 { IDM_Rematch, MF_BYCOMMAND|MF_GRAYED },
\r
7729 { IDM_Adjourn, MF_BYCOMMAND|MF_GRAYED },
\r
7730 { IDM_StopExamining, MF_BYCOMMAND|MF_GRAYED },
\r
7731 { IDM_StopObserving, MF_BYCOMMAND|MF_GRAYED },
\r
7732 { IDM_Upload, MF_BYCOMMAND|MF_GRAYED },
\r
7733 { IDM_Revert, MF_BYCOMMAND|MF_GRAYED },
\r
7734 { IDM_Annotate, MF_BYCOMMAND|MF_GRAYED },
\r
7735 { IDM_NewChat, MF_BYCOMMAND|MF_GRAYED },
\r
7739 Enables icsEnables[] = {
\r
7740 { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },
\r
7741 { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },
\r
7742 { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },
\r
7743 { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },
\r
7744 { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },
\r
7745 { IDM_Match, MF_BYCOMMAND|MF_GRAYED },
\r
7746 { IDM_MachineBoth, MF_BYCOMMAND|MF_GRAYED },
\r
7747 { IDM_AnalysisMode, MF_BYCOMMAND|MF_ENABLED },
\r
7748 { IDM_AnalyzeFile, MF_BYCOMMAND|MF_GRAYED },
\r
7749 { IDM_TimeControl, MF_BYCOMMAND|MF_GRAYED },
\r
7750 { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },
\r
7751 { IDM_Hint, MF_BYCOMMAND|MF_GRAYED },
\r
7752 { IDM_Book, MF_BYCOMMAND|MF_GRAYED },
\r
7753 { IDM_IcsOptions, MF_BYCOMMAND|MF_ENABLED },
\r
7754 { IDM_Engine1Options, MF_BYCOMMAND|MF_GRAYED },
\r
7755 { IDM_Engine2Options, MF_BYCOMMAND|MF_GRAYED },
\r
7756 { IDM_Annotate, MF_BYCOMMAND|MF_GRAYED },
\r
7761 Enables zippyEnables[] = {
\r
7762 { IDM_MoveNow, MF_BYCOMMAND|MF_ENABLED },
\r
7763 { IDM_Hint, MF_BYCOMMAND|MF_ENABLED },
\r
7764 { IDM_Book, MF_BYCOMMAND|MF_ENABLED },
\r
7765 { IDM_Engine1Options, MF_BYCOMMAND|MF_ENABLED },
\r
7770 Enables ncpEnables[] = {
\r
7771 { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },
\r
7772 { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },
\r
7773 { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },
\r
7774 { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },
\r
7775 { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },
\r
7776 { IDM_Match, MF_BYCOMMAND|MF_GRAYED },
\r
7777 { IDM_AnalysisMode, MF_BYCOMMAND|MF_GRAYED },
\r
7778 { IDM_AnalyzeFile, MF_BYCOMMAND|MF_GRAYED },
\r
7779 { IDM_IcsClient, MF_BYCOMMAND|MF_GRAYED },
\r
7780 { ACTION_POS, MF_BYPOSITION|MF_GRAYED },
\r
7781 { IDM_Revert, MF_BYCOMMAND|MF_GRAYED },
\r
7782 { IDM_Annotate, MF_BYCOMMAND|MF_GRAYED },
\r
7783 { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },
\r
7784 { IDM_RetractMove, MF_BYCOMMAND|MF_GRAYED },
\r
7785 { IDM_TimeControl, MF_BYCOMMAND|MF_GRAYED },
\r
7786 { IDM_Hint, MF_BYCOMMAND|MF_GRAYED },
\r
7787 { IDM_Book, MF_BYCOMMAND|MF_GRAYED },
\r
7788 { IDM_MachineBoth, MF_BYCOMMAND|MF_GRAYED },
\r
7789 { IDM_NewChat, MF_BYCOMMAND|MF_GRAYED },
\r
7790 { IDM_Engine1Options, MF_BYCOMMAND|MF_GRAYED },
\r
7791 { IDM_Engine2Options, MF_BYCOMMAND|MF_GRAYED },
\r
7795 Enables trainingOnEnables[] = {
\r
7796 { IDM_EditComment, MF_BYCOMMAND|MF_GRAYED },
\r
7797 { IDM_Pause, MF_BYCOMMAND|MF_GRAYED },
\r
7798 { IDM_Forward, MF_BYCOMMAND|MF_GRAYED },
\r
7799 { IDM_Backward, MF_BYCOMMAND|MF_GRAYED },
\r
7800 { IDM_ToEnd, MF_BYCOMMAND|MF_GRAYED },
\r
7801 { IDM_ToStart, MF_BYCOMMAND|MF_GRAYED },
\r
7802 { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },
\r
7803 { IDM_TruncateGame, MF_BYCOMMAND|MF_GRAYED },
\r
7807 Enables trainingOffEnables[] = {
\r
7808 { IDM_EditComment, MF_BYCOMMAND|MF_ENABLED },
\r
7809 { IDM_Pause, MF_BYCOMMAND|MF_ENABLED },
\r
7810 { IDM_Forward, MF_BYCOMMAND|MF_ENABLED },
\r
7811 { IDM_Backward, MF_BYCOMMAND|MF_ENABLED },
\r
7812 { IDM_ToEnd, MF_BYCOMMAND|MF_ENABLED },
\r
7813 { IDM_ToStart, MF_BYCOMMAND|MF_ENABLED },
\r
7814 { IDM_MoveNow, MF_BYCOMMAND|MF_ENABLED },
\r
7815 { IDM_TruncateGame, MF_BYCOMMAND|MF_ENABLED },
\r
7819 /* These modify either ncpEnables or gnuEnables */
\r
7820 Enables cmailEnables[] = {
\r
7821 { IDM_MailMove, MF_BYCOMMAND|MF_ENABLED },
\r
7822 { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_ENABLED },
\r
7823 { ACTION_POS, MF_BYPOSITION|MF_ENABLED },
\r
7824 { IDM_CallFlag, MF_BYCOMMAND|MF_GRAYED },
\r
7825 { IDM_Draw, MF_BYCOMMAND|MF_ENABLED },
\r
7826 { IDM_Adjourn, MF_BYCOMMAND|MF_GRAYED },
\r
7827 { IDM_Abort, MF_BYCOMMAND|MF_GRAYED },
\r
7831 Enables machineThinkingEnables[] = {
\r
7832 { IDM_LoadGame, MF_BYCOMMAND|MF_GRAYED },
\r
7833 { IDM_LoadNextGame, MF_BYCOMMAND|MF_GRAYED },
\r
7834 { IDM_LoadPrevGame, MF_BYCOMMAND|MF_GRAYED },
\r
7835 { IDM_ReloadGame, MF_BYCOMMAND|MF_GRAYED },
\r
7836 { IDM_PasteGame, MF_BYCOMMAND|MF_GRAYED },
\r
7837 { IDM_LoadPosition, MF_BYCOMMAND|MF_GRAYED },
\r
7838 { IDM_LoadNextPosition, MF_BYCOMMAND|MF_GRAYED },
\r
7839 { IDM_LoadPrevPosition, MF_BYCOMMAND|MF_GRAYED },
\r
7840 { IDM_ReloadPosition, MF_BYCOMMAND|MF_GRAYED },
\r
7841 { IDM_PastePosition, MF_BYCOMMAND|MF_GRAYED },
\r
7842 { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },
\r
7843 { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },
\r
7844 { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },
\r
7845 { IDM_Match, MF_BYCOMMAND|MF_GRAYED },
\r
7846 { IDM_TypeInMove, MF_BYCOMMAND|MF_GRAYED },
\r
7847 { IDM_RetractMove, MF_BYCOMMAND|MF_GRAYED },
\r
7851 Enables userThinkingEnables[] = {
\r
7852 { IDM_LoadGame, MF_BYCOMMAND|MF_ENABLED },
\r
7853 { IDM_LoadNextGame, MF_BYCOMMAND|MF_ENABLED },
\r
7854 { IDM_LoadPrevGame, MF_BYCOMMAND|MF_ENABLED },
\r
7855 { IDM_ReloadGame, MF_BYCOMMAND|MF_ENABLED },
\r
7856 { IDM_PasteGame, MF_BYCOMMAND|MF_ENABLED },
\r
7857 { IDM_LoadPosition, MF_BYCOMMAND|MF_ENABLED },
\r
7858 { IDM_LoadNextPosition, MF_BYCOMMAND|MF_ENABLED },
\r
7859 { IDM_LoadPrevPosition, MF_BYCOMMAND|MF_ENABLED },
\r
7860 { IDM_ReloadPosition, MF_BYCOMMAND|MF_ENABLED },
\r
7861 { IDM_PastePosition, MF_BYCOMMAND|MF_ENABLED },
\r
7862 { IDM_MachineWhite, MF_BYCOMMAND|MF_ENABLED },
\r
7863 { IDM_MachineBlack, MF_BYCOMMAND|MF_ENABLED },
\r
7864 { IDM_TwoMachines, MF_BYCOMMAND|MF_ENABLED },
\r
7865 { IDM_Match, MF_BYCOMMAND|MF_ENABLED },
\r
7866 { IDM_TypeInMove, MF_BYCOMMAND|MF_ENABLED },
\r
7867 { IDM_RetractMove, MF_BYCOMMAND|MF_ENABLED },
\r
7871 /*---------------------------------------------------------------------------*\
\r
7873 * Front-end interface functions exported by XBoard.
\r
7874 * Functions appear in same order as prototypes in frontend.h.
\r
7876 \*---------------------------------------------------------------------------*/
\r
7880 static UINT prevChecked = 0;
\r
7881 static int prevPausing = 0;
\r
7884 if (pausing != prevPausing) {
\r
7885 prevPausing = pausing;
\r
7886 (void) CheckMenuItem(GetMenu(hwndMain), IDM_Pause,
\r
7887 MF_BYCOMMAND|(pausing ? MF_CHECKED : MF_UNCHECKED));
\r
7888 if (hwndPause) SetWindowText(hwndPause, pausing ? "C" : "P");
\r
7891 switch (gameMode) {
\r
7892 case BeginningOfGame:
\r
7893 if (appData.icsActive)
\r
7894 nowChecked = IDM_IcsClient;
\r
7895 else if (appData.noChessProgram)
\r
7896 nowChecked = IDM_EditGame;
\r
7898 nowChecked = IDM_MachineBlack;
\r
7900 case MachinePlaysBlack:
\r
7901 nowChecked = IDM_MachineBlack;
\r
7903 case MachinePlaysWhite:
\r
7904 nowChecked = IDM_MachineWhite;
\r
7906 case TwoMachinesPlay:
\r
7907 nowChecked = matchMode ? IDM_Match : IDM_TwoMachines; // [HGM] match
\r
7910 nowChecked = IDM_AnalysisMode;
\r
7913 nowChecked = IDM_AnalyzeFile;
\r
7916 nowChecked = IDM_EditGame;
\r
7918 case PlayFromGameFile:
\r
7919 nowChecked = IDM_LoadGame;
\r
7921 case EditPosition:
\r
7922 nowChecked = IDM_EditPosition;
\r
7925 nowChecked = IDM_Training;
\r
7927 case IcsPlayingWhite:
\r
7928 case IcsPlayingBlack:
\r
7929 case IcsObserving:
\r
7931 nowChecked = IDM_IcsClient;
\r
7938 if (prevChecked != 0)
\r
7939 (void) CheckMenuItem(GetMenu(hwndMain),
\r
7940 prevChecked, MF_BYCOMMAND|MF_UNCHECKED);
\r
7941 if (nowChecked != 0)
\r
7942 (void) CheckMenuItem(GetMenu(hwndMain),
\r
7943 nowChecked, MF_BYCOMMAND|MF_CHECKED);
\r
7945 if (nowChecked == IDM_LoadGame || nowChecked == IDM_Training) {
\r
7946 (void) EnableMenuItem(GetMenu(hwndMain), IDM_Training,
\r
7947 MF_BYCOMMAND|MF_ENABLED);
\r
7949 (void) EnableMenuItem(GetMenu(hwndMain),
\r
7950 IDM_Training, MF_BYCOMMAND|MF_GRAYED);
\r
7953 prevChecked = nowChecked;
\r
7955 /* [DM] icsEngineAnalyze - Do a sceure check too */
\r
7956 if (appData.icsActive) {
\r
7957 if (appData.icsEngineAnalyze) {
\r
7958 (void) CheckMenuItem(GetMenu(hwndMain), IDM_AnalysisMode,
\r
7959 MF_BYCOMMAND|MF_CHECKED);
\r
7961 (void) CheckMenuItem(GetMenu(hwndMain), IDM_AnalysisMode,
\r
7962 MF_BYCOMMAND|MF_UNCHECKED);
\r
7970 HMENU hmenu = GetMenu(hwndMain);
\r
7971 SetMenuEnables(hmenu, icsEnables);
\r
7972 EnableMenuItem(GetSubMenu(hmenu, OPTIONS_POS), ICS_POS,
\r
7973 MF_BYPOSITION|MF_ENABLED);
\r
7975 if (appData.zippyPlay) {
\r
7976 SetMenuEnables(hmenu, zippyEnables);
\r
7977 if (!appData.noChessProgram) /* [DM] icsEngineAnalyze */
\r
7978 (void) EnableMenuItem(GetMenu(hwndMain), IDM_AnalysisMode,
\r
7979 MF_BYCOMMAND|MF_ENABLED);
\r
7987 SetMenuEnables(GetMenu(hwndMain), gnuEnables);
\r
7993 HMENU hmenu = GetMenu(hwndMain);
\r
7994 SetMenuEnables(hmenu, ncpEnables);
\r
7995 EnableMenuItem(GetSubMenu(hmenu, OPTIONS_POS), SOUNDS_POS,
\r
7996 MF_BYPOSITION|MF_GRAYED);
\r
7997 DrawMenuBar(hwndMain);
\r
8003 SetMenuEnables(GetMenu(hwndMain), cmailEnables);
\r
8007 SetTrainingModeOn()
\r
8010 SetMenuEnables(GetMenu(hwndMain), trainingOnEnables);
\r
8011 for (i = 0; i < N_BUTTONS; i++) {
\r
8012 if (buttonDesc[i].hwnd != NULL)
\r
8013 EnableWindow(buttonDesc[i].hwnd, FALSE);
\r
8018 VOID SetTrainingModeOff()
\r
8021 SetMenuEnables(GetMenu(hwndMain), trainingOffEnables);
\r
8022 for (i = 0; i < N_BUTTONS; i++) {
\r
8023 if (buttonDesc[i].hwnd != NULL)
\r
8024 EnableWindow(buttonDesc[i].hwnd, TRUE);
\r
8030 SetUserThinkingEnables()
\r
8032 SetMenuEnables(GetMenu(hwndMain), userThinkingEnables);
\r
8036 SetMachineThinkingEnables()
\r
8038 HMENU hMenu = GetMenu(hwndMain);
\r
8039 int flags = MF_BYCOMMAND|MF_ENABLED;
\r
8041 SetMenuEnables(hMenu, machineThinkingEnables);
\r
8043 if (gameMode == MachinePlaysBlack) {
\r
8044 (void)EnableMenuItem(hMenu, IDM_MachineBlack, flags);
\r
8045 } else if (gameMode == MachinePlaysWhite) {
\r
8046 (void)EnableMenuItem(hMenu, IDM_MachineWhite, flags);
\r
8047 } else if (gameMode == TwoMachinesPlay) {
\r
8048 (void)EnableMenuItem(hMenu, matchMode ? IDM_Match : IDM_TwoMachines, flags); // [HGM] match
\r
8054 DisplayTitle(char *str)
\r
8056 char title[MSG_SIZ], *host;
\r
8057 if (str[0] != NULLCHAR) {
\r
8058 safeStrCpy(title, str, sizeof(title)/sizeof(title[0]) );
\r
8059 } else if (appData.icsActive) {
\r
8060 if (appData.icsCommPort[0] != NULLCHAR)
\r
8063 host = appData.icsHost;
\r
8064 snprintf(title, MSG_SIZ, "%s: %s", szTitle, host);
\r
8065 } else if (appData.noChessProgram) {
\r
8066 safeStrCpy(title, szTitle, sizeof(title)/sizeof(title[0]) );
\r
8068 safeStrCpy(title, szTitle, sizeof(title)/sizeof(title[0]) );
\r
8069 strcat(title, ": ");
\r
8070 strcat(title, first.tidy);
\r
8072 SetWindowText(hwndMain, title);
\r
8077 DisplayMessage(char *str1, char *str2)
\r
8081 int remain = MESSAGE_TEXT_MAX - 1;
\r
8084 moveErrorMessageUp = FALSE; /* turned on later by caller if needed */
\r
8085 messageText[0] = NULLCHAR;
\r
8087 len = strlen(str1);
\r
8088 if (len > remain) len = remain;
\r
8089 strncpy(messageText, str1, len);
\r
8090 messageText[len] = NULLCHAR;
\r
8093 if (*str2 && remain >= 2) {
\r
8095 strcat(messageText, " ");
\r
8098 len = strlen(str2);
\r
8099 if (len > remain) len = remain;
\r
8100 strncat(messageText, str2, len);
\r
8102 messageText[MESSAGE_TEXT_MAX - 1] = NULLCHAR;
\r
8104 if (hwndMain == NULL || IsIconic(hwndMain)) return;
\r
8108 hdc = GetDC(hwndMain);
\r
8109 oldFont = SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);
\r
8110 ExtTextOut(hdc, messageRect.left, messageRect.top, ETO_CLIPPED|ETO_OPAQUE,
\r
8111 &messageRect, messageText, strlen(messageText), NULL);
\r
8112 (void) SelectObject(hdc, oldFont);
\r
8113 (void) ReleaseDC(hwndMain, hdc);
\r
8117 DisplayError(char *str, int error)
\r
8119 char buf[MSG_SIZ*2], buf2[MSG_SIZ];
\r
8123 safeStrCpy(buf, str, sizeof(buf)/sizeof(buf[0]) );
\r
8125 len = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,
\r
8126 NULL, error, LANG_NEUTRAL,
\r
8127 (LPSTR) buf2, MSG_SIZ, NULL);
\r
8129 snprintf(buf, 2*MSG_SIZ, "%s:\n%s", str, buf2);
\r
8131 ErrorMap *em = errmap;
\r
8132 while (em->err != 0 && em->err != error) em++;
\r
8133 if (em->err != 0) {
\r
8134 snprintf(buf, 2*MSG_SIZ, "%s:\n%s", str, em->msg);
\r
8136 snprintf(buf, 2*MSG_SIZ, "%s:\nError code %d", str, error);
\r
8141 ErrorPopUp(_("Error"), buf);
\r
8146 DisplayMoveError(char *str)
\r
8148 fromX = fromY = -1;
\r
8149 ClearHighlights();
\r
8150 DrawPosition(FALSE, NULL);
\r
8151 if (appData.popupMoveErrors) {
\r
8152 ErrorPopUp(_("Error"), str);
\r
8154 DisplayMessage(str, "");
\r
8155 moveErrorMessageUp = TRUE;
\r
8160 DisplayFatalError(char *str, int error, int exitStatus)
\r
8162 char buf[2*MSG_SIZ], buf2[MSG_SIZ];
\r
8164 char *label = exitStatus ? _("Fatal Error") : _("Exiting");
\r
8167 len = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,
\r
8168 NULL, error, LANG_NEUTRAL,
\r
8169 (LPSTR) buf2, MSG_SIZ, NULL);
\r
8171 snprintf(buf, 2*MSG_SIZ, "%s:\n%s", str, buf2);
\r
8173 ErrorMap *em = errmap;
\r
8174 while (em->err != 0 && em->err != error) em++;
\r
8175 if (em->err != 0) {
\r
8176 snprintf(buf, 2*MSG_SIZ, "%s:\n%s", str, em->msg);
\r
8178 snprintf(buf, 2*MSG_SIZ, "%s:\nError code %d", str, error);
\r
8183 if (appData.debugMode) {
\r
8184 fprintf(debugFP, "%s: %s\n", label, str);
\r
8186 if (appData.popupExitMessage) {
\r
8187 (void) MessageBox(hwndMain, str, label, MB_OK|
\r
8188 (exitStatus ? MB_ICONSTOP : MB_ICONINFORMATION));
\r
8190 ExitEvent(exitStatus);
\r
8195 DisplayInformation(char *str)
\r
8197 (void) MessageBox(hwndMain, str, _("Information"), MB_OK|MB_ICONINFORMATION);
\r
8202 DisplayNote(char *str)
\r
8204 ErrorPopUp(_("Note"), str);
\r
8209 char *title, *question, *replyPrefix;
\r
8214 QuestionDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
8216 static QuestionParams *qp;
\r
8217 char reply[MSG_SIZ];
\r
8220 switch (message) {
\r
8221 case WM_INITDIALOG:
\r
8222 qp = (QuestionParams *) lParam;
\r
8223 CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));
\r
8224 Translate(hDlg, DLG_Question);
\r
8225 SetWindowText(hDlg, qp->title);
\r
8226 SetDlgItemText(hDlg, OPT_QuestionText, qp->question);
\r
8227 SetFocus(GetDlgItem(hDlg, OPT_QuestionInput));
\r
8231 switch (LOWORD(wParam)) {
\r
8233 safeStrCpy(reply, qp->replyPrefix, sizeof(reply)/sizeof(reply[0]) );
\r
8234 if (*reply) strcat(reply, " ");
\r
8235 len = strlen(reply);
\r
8236 GetDlgItemText(hDlg, OPT_QuestionInput, reply + len, sizeof(reply) - len);
\r
8237 strcat(reply, "\n");
\r
8238 OutputToProcess(qp->pr, reply, strlen(reply), &err);
\r
8239 EndDialog(hDlg, TRUE);
\r
8240 if (err) DisplayFatalError(_("Error writing to chess program"), err, 1);
\r
8243 EndDialog(hDlg, FALSE);
\r
8254 AskQuestion(char* title, char *question, char *replyPrefix, ProcRef pr)
\r
8256 QuestionParams qp;
\r
8260 qp.question = question;
\r
8261 qp.replyPrefix = replyPrefix;
\r
8263 lpProc = MakeProcInstance((FARPROC)QuestionDialog, hInst);
\r
8264 DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_Question),
\r
8265 hwndMain, (DLGPROC)lpProc, (LPARAM)&qp);
\r
8266 FreeProcInstance(lpProc);
\r
8269 /* [AS] Pick FRC position */
\r
8270 LRESULT CALLBACK NewGameFRC_Proc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
8272 static int * lpIndexFRC;
\r
8278 case WM_INITDIALOG:
\r
8279 lpIndexFRC = (int *) lParam;
\r
8281 CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));
\r
8282 Translate(hDlg, DLG_NewGameFRC);
\r
8284 SendDlgItemMessage( hDlg, IDC_NFG_Edit, EM_SETLIMITTEXT, sizeof(buf)-1, 0 );
\r
8285 SetDlgItemInt( hDlg, IDC_NFG_Edit, *lpIndexFRC, TRUE );
\r
8286 SendDlgItemMessage( hDlg, IDC_NFG_Edit, EM_SETSEL, 0, -1 );
\r
8287 SetFocus(GetDlgItem(hDlg, IDC_NFG_Edit));
\r
8292 switch( LOWORD(wParam) ) {
\r
8294 *lpIndexFRC = GetDlgItemInt(hDlg, IDC_NFG_Edit, &index_is_ok, TRUE );
\r
8295 EndDialog( hDlg, 0 );
\r
8296 shuffleOpenings = TRUE; /* [HGM] shuffle: switch shuffling on for as long as we stay in current variant */
\r
8299 EndDialog( hDlg, 1 );
\r
8301 case IDC_NFG_Edit:
\r
8302 if( HIWORD(wParam) == EN_CHANGE ) {
\r
8303 GetDlgItemInt(hDlg, IDC_NFG_Edit, &index_is_ok, TRUE );
\r
8305 EnableWindow( GetDlgItem(hDlg, IDOK), index_is_ok );
\r
8308 case IDC_NFG_Random:
\r
8309 snprintf( buf, sizeof(buf)/sizeof(buf[0]), "%d", myrandom() ); /* [HGM] shuffle: no longer limit to 960 */
\r
8310 SetDlgItemText(hDlg, IDC_NFG_Edit, buf );
\r
8323 int index = appData.defaultFrcPosition;
\r
8324 FARPROC lpProc = MakeProcInstance( (FARPROC) NewGameFRC_Proc, hInst );
\r
8326 result = DialogBoxParam( hInst, MAKEINTRESOURCE(DLG_NewGameFRC), hwndMain, (DLGPROC)lpProc, (LPARAM)&index );
\r
8328 if( result == 0 ) {
\r
8329 appData.defaultFrcPosition = index;
\r
8335 /* [AS] Game list options. Refactored by HGM */
\r
8337 HWND gameListOptionsDialog;
\r
8339 // low-level front-end: clear text edit / list widget
\r
8343 SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_RESETCONTENT, 0, 0 );
\r
8346 // low-level front-end: clear text edit / list widget
\r
8348 GLT_DeSelectList()
\r
8350 SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_SETCURSEL, 0, 0 );
\r
8353 // low-level front-end: append line to text edit / list widget
\r
8355 GLT_AddToList( char *name )
\r
8358 SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_ADDSTRING, 0, (LPARAM) name );
\r
8362 // low-level front-end: get line from text edit / list widget
\r
8364 GLT_GetFromList( int index, char *name )
\r
8367 if( SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_GETTEXT, index, (LPARAM) name ) != LB_ERR )
\r
8373 void GLT_MoveSelection( HWND hDlg, int delta )
\r
8375 int idx1 = (int) SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETCURSEL, 0, 0 );
\r
8376 int idx2 = idx1 + delta;
\r
8377 int count = (int) SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETCOUNT, 0, 0 );
\r
8379 if( idx1 >=0 && idx1 < count && idx2 >= 0 && idx2 < count ) {
\r
8382 SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETTEXT, idx1, (LPARAM) buf );
\r
8383 SendDlgItemMessage( hDlg, IDC_GameListTags, LB_DELETESTRING, idx1, 0 );
\r
8384 SendDlgItemMessage( hDlg, IDC_GameListTags, LB_INSERTSTRING, idx2, (LPARAM) buf );
\r
8385 SendDlgItemMessage( hDlg, IDC_GameListTags, LB_SETCURSEL, idx2, 0 );
\r
8389 LRESULT CALLBACK GameListOptions_Proc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
8393 case WM_INITDIALOG:
\r
8394 gameListOptionsDialog = hDlg; // [HGM] pass through global to keep out off back-end
\r
8396 CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));
\r
8397 Translate(hDlg, DLG_GameListOptions);
\r
8399 /* Initialize list */
\r
8400 GLT_TagsToList( lpUserGLT );
\r
8402 SetFocus( GetDlgItem(hDlg, IDC_GameListTags) );
\r
8407 switch( LOWORD(wParam) ) {
\r
8410 EndDialog( hDlg, 0 );
\r
8413 EndDialog( hDlg, 1 );
\r
8416 case IDC_GLT_Default:
\r
8417 GLT_TagsToList( GLT_DEFAULT_TAGS );
\r
8420 case IDC_GLT_Restore:
\r
8421 GLT_TagsToList( appData.gameListTags );
\r
8425 GLT_MoveSelection( hDlg, -1 );
\r
8428 case IDC_GLT_Down:
\r
8429 GLT_MoveSelection( hDlg, +1 );
\r
8439 int GameListOptions()
\r
8442 FARPROC lpProc = MakeProcInstance( (FARPROC) GameListOptions_Proc, hInst );
\r
8444 safeStrCpy( lpUserGLT, appData.gameListTags ,LPUSERGLT_SIZE );
\r
8446 result = DialogBoxParam( hInst, MAKEINTRESOURCE(DLG_GameListOptions), hwndMain, (DLGPROC)lpProc, (LPARAM)lpUserGLT );
\r
8448 if( result == 0 ) {
\r
8449 /* [AS] Memory leak here! */
\r
8450 appData.gameListTags = strdup( lpUserGLT );
\r
8457 DisplayIcsInteractionTitle(char *str)
\r
8459 char consoleTitle[MSG_SIZ];
\r
8461 snprintf(consoleTitle, MSG_SIZ, "%s: %s", szConsoleTitle, str);
\r
8462 SetWindowText(hwndConsole, consoleTitle);
\r
8466 DrawPosition(int fullRedraw, Board board)
\r
8468 HDCDrawPosition(NULL, (BOOLEAN) fullRedraw, board);
\r
8471 void NotifyFrontendLogin()
\r
8474 UpdateICSWidth(GetDlgItem(hwndConsole, OPT_ConsoleText));
\r
8480 fromX = fromY = -1;
\r
8481 if (dragInfo.pos.x != -1 || dragInfo.pos.y != -1) {
\r
8482 dragInfo.pos.x = dragInfo.pos.y = -1;
\r
8483 dragInfo.pos.x = dragInfo.pos.y = -1;
\r
8484 dragInfo.lastpos = dragInfo.pos;
\r
8485 dragInfo.start.x = dragInfo.start.y = -1;
\r
8486 dragInfo.from = dragInfo.start;
\r
8488 DrawPosition(TRUE, NULL);
\r
8495 CommentPopUp(char *title, char *str)
\r
8497 HWND hwnd = GetActiveWindow();
\r
8498 EitherCommentPopUp(currentMove, title, str, FALSE); // [HGM] vari: fake move index, rather than 0
\r
8500 SetActiveWindow(hwnd);
\r
8504 CommentPopDown(void)
\r
8506 CheckMenuItem(GetMenu(hwndMain), IDM_EditComment, MF_UNCHECKED);
\r
8507 if (commentDialog) {
\r
8508 ShowWindow(commentDialog, SW_HIDE);
\r
8510 commentUp = FALSE;
\r
8514 EditCommentPopUp(int index, char *title, char *str)
\r
8516 EitherCommentPopUp(index, title, str, TRUE);
\r
8523 MyPlaySound(&sounds[(int)SoundMove]);
\r
8526 VOID PlayIcsWinSound()
\r
8528 MyPlaySound(&sounds[(int)SoundIcsWin]);
\r
8531 VOID PlayIcsLossSound()
\r
8533 MyPlaySound(&sounds[(int)SoundIcsLoss]);
\r
8536 VOID PlayIcsDrawSound()
\r
8538 MyPlaySound(&sounds[(int)SoundIcsDraw]);
\r
8541 VOID PlayIcsUnfinishedSound()
\r
8543 MyPlaySound(&sounds[(int)SoundIcsUnfinished]);
\r
8549 MyPlaySound(&sounds[(int)SoundAlarm]);
\r
8557 consoleEcho = TRUE;
\r
8558 hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
8559 SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&consoleCF);
\r
8560 SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);
\r
8569 consoleEcho = FALSE;
\r
8570 hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
8571 /* This works OK: set text and background both to the same color */
\r
8573 cf.crTextColor = COLOR_ECHOOFF;
\r
8574 SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf);
\r
8575 SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, cf.crTextColor);
\r
8578 /* No Raw()...? */
\r
8580 void Colorize(ColorClass cc, int continuation)
\r
8582 currentColorClass = cc;
\r
8583 consoleCF.dwMask = CFM_COLOR|CFM_BOLD|CFM_ITALIC|CFM_UNDERLINE|CFM_STRIKEOUT;
\r
8584 consoleCF.crTextColor = textAttribs[cc].color;
\r
8585 consoleCF.dwEffects = textAttribs[cc].effects;
\r
8586 if (!continuation) MyPlaySound(&textAttribs[cc].sound);
\r
8592 static char buf[MSG_SIZ];
\r
8593 DWORD bufsiz = MSG_SIZ;
\r
8595 if(appData.userName != NULL && appData.userName[0] != 0) {
\r
8596 return appData.userName; /* [HGM] username: prefer name selected by user over his system login */
\r
8598 if (!GetUserName(buf, &bufsiz)) {
\r
8599 /*DisplayError("Error getting user name", GetLastError());*/
\r
8600 safeStrCpy(buf, _("User"), sizeof(buf)/sizeof(buf[0]) );
\r
8608 static char buf[MSG_SIZ];
\r
8609 DWORD bufsiz = MSG_SIZ;
\r
8611 if (!GetComputerName(buf, &bufsiz)) {
\r
8612 /*DisplayError("Error getting host name", GetLastError());*/
\r
8613 safeStrCpy(buf, _("Unknown"), sizeof(buf)/sizeof(buf[0]) );
\r
8620 ClockTimerRunning()
\r
8622 return clockTimerEvent != 0;
\r
8628 if (clockTimerEvent == 0) return FALSE;
\r
8629 KillTimer(hwndMain, clockTimerEvent);
\r
8630 clockTimerEvent = 0;
\r
8635 StartClockTimer(long millisec)
\r
8637 clockTimerEvent = SetTimer(hwndMain, (UINT) CLOCK_TIMER_ID,
\r
8638 (UINT) millisec, NULL);
\r
8642 DisplayWhiteClock(long timeRemaining, int highlight)
\r
8645 char *flag = whiteFlag && gameMode == TwoMachinesPlay ? "(!)" : "";
\r
8647 if(appData.noGUI) return;
\r
8648 hdc = GetDC(hwndMain);
\r
8649 if (!IsIconic(hwndMain)) {
\r
8650 DisplayAClock(hdc, timeRemaining, highlight,
\r
8651 flipClock ? &blackRect : &whiteRect, _("White"), flag);
\r
8653 if (highlight && iconCurrent == iconBlack) {
\r
8654 iconCurrent = iconWhite;
\r
8655 PostMessage(hwndMain, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);
\r
8656 if (IsIconic(hwndMain)) {
\r
8657 DrawIcon(hdc, 2, 2, iconCurrent);
\r
8660 (void) ReleaseDC(hwndMain, hdc);
\r
8662 PostMessage(hwndConsole, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);
\r
8666 DisplayBlackClock(long timeRemaining, int highlight)
\r
8669 char *flag = blackFlag && gameMode == TwoMachinesPlay ? "(!)" : "";
\r
8671 if(appData.noGUI) return;
\r
8672 hdc = GetDC(hwndMain);
\r
8673 if (!IsIconic(hwndMain)) {
\r
8674 DisplayAClock(hdc, timeRemaining, highlight,
\r
8675 flipClock ? &whiteRect : &blackRect, _("Black"), flag);
\r
8677 if (highlight && iconCurrent == iconWhite) {
\r
8678 iconCurrent = iconBlack;
\r
8679 PostMessage(hwndMain, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);
\r
8680 if (IsIconic(hwndMain)) {
\r
8681 DrawIcon(hdc, 2, 2, iconCurrent);
\r
8684 (void) ReleaseDC(hwndMain, hdc);
\r
8686 PostMessage(hwndConsole, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);
\r
8691 LoadGameTimerRunning()
\r
8693 return loadGameTimerEvent != 0;
\r
8697 StopLoadGameTimer()
\r
8699 if (loadGameTimerEvent == 0) return FALSE;
\r
8700 KillTimer(hwndMain, loadGameTimerEvent);
\r
8701 loadGameTimerEvent = 0;
\r
8706 StartLoadGameTimer(long millisec)
\r
8708 loadGameTimerEvent = SetTimer(hwndMain, (UINT) LOAD_GAME_TIMER_ID,
\r
8709 (UINT) millisec, NULL);
\r
8717 char fileTitle[MSG_SIZ];
\r
8719 defName = DefaultFileName(appData.oldSaveStyle ? "gam" : "pgn");
\r
8720 f = OpenFileDialog(hwndMain, "a", defName,
\r
8721 appData.oldSaveStyle ? "gam" : "pgn",
\r
8723 _("Save Game to File"), NULL, fileTitle, NULL);
\r
8725 SaveGame(f, 0, "");
\r
8732 ScheduleDelayedEvent(DelayedEventCallback cb, long millisec)
\r
8734 if (delayedTimerEvent != 0) {
\r
8735 if (appData.debugMode && cb != delayedTimerCallback) { // [HGM] alive: not too much debug
\r
8736 fprintf(debugFP, "ScheduleDelayedEvent: event already scheduled\n");
\r
8738 KillTimer(hwndMain, delayedTimerEvent);
\r
8739 delayedTimerEvent = 0;
\r
8740 if(delayedTimerCallback != cb) // [HGM] alive: do not "flush" same event, just postpone it
\r
8741 delayedTimerCallback();
\r
8743 delayedTimerCallback = cb;
\r
8744 delayedTimerEvent = SetTimer(hwndMain, (UINT) DELAYED_TIMER_ID,
\r
8745 (UINT) millisec, NULL);
\r
8748 DelayedEventCallback
\r
8751 if (delayedTimerEvent) {
\r
8752 return delayedTimerCallback;
\r
8759 CancelDelayedEvent()
\r
8761 if (delayedTimerEvent) {
\r
8762 KillTimer(hwndMain, delayedTimerEvent);
\r
8763 delayedTimerEvent = 0;
\r
8767 DWORD GetWin32Priority(int nice)
\r
8768 { // [HGM] nice: translate Unix nice() value to indows priority class. (Code stolen from Polyglot 1.4w11)
\r
8770 REALTIME_PRIORITY_CLASS 0x00000100
\r
8771 HIGH_PRIORITY_CLASS 0x00000080
\r
8772 ABOVE_NORMAL_PRIORITY_CLASS 0x00008000
\r
8773 NORMAL_PRIORITY_CLASS 0x00000020
\r
8774 BELOW_NORMAL_PRIORITY_CLASS 0x00004000
\r
8775 IDLE_PRIORITY_CLASS 0x00000040
\r
8777 if (nice < -15) return 0x00000080;
\r
8778 if (nice < 0) return 0x00008000;
\r
8779 if (nice == 0) return 0x00000020;
\r
8780 if (nice < 15) return 0x00004000;
\r
8781 return 0x00000040;
\r
8784 /* Start a child process running the given program.
\r
8785 The process's standard output can be read from "from", and its
\r
8786 standard input can be written to "to".
\r
8787 Exit with fatal error if anything goes wrong.
\r
8788 Returns an opaque pointer that can be used to destroy the process
\r
8792 StartChildProcess(char *cmdLine, char *dir, ProcRef *pr)
\r
8794 #define BUFSIZE 4096
\r
8796 HANDLE hChildStdinRd, hChildStdinWr,
\r
8797 hChildStdoutRd, hChildStdoutWr;
\r
8798 HANDLE hChildStdinWrDup, hChildStdoutRdDup;
\r
8799 SECURITY_ATTRIBUTES saAttr;
\r
8801 PROCESS_INFORMATION piProcInfo;
\r
8802 STARTUPINFO siStartInfo;
\r
8804 char buf[MSG_SIZ];
\r
8807 if (appData.debugMode) {
\r
8808 fprintf(debugFP, "StartChildProcess (dir=\"%s\") %s\n", dir, cmdLine);
\r
8813 /* Set the bInheritHandle flag so pipe handles are inherited. */
\r
8814 saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);
\r
8815 saAttr.bInheritHandle = TRUE;
\r
8816 saAttr.lpSecurityDescriptor = NULL;
\r
8819 * The steps for redirecting child's STDOUT:
\r
8820 * 1. Create anonymous pipe to be STDOUT for child.
\r
8821 * 2. Create a noninheritable duplicate of read handle,
\r
8822 * and close the inheritable read handle.
\r
8825 /* Create a pipe for the child's STDOUT. */
\r
8826 if (! CreatePipe(&hChildStdoutRd, &hChildStdoutWr, &saAttr, 0)) {
\r
8827 return GetLastError();
\r
8830 /* Duplicate the read handle to the pipe, so it is not inherited. */
\r
8831 fSuccess = DuplicateHandle(GetCurrentProcess(), hChildStdoutRd,
\r
8832 GetCurrentProcess(), &hChildStdoutRdDup, 0,
\r
8833 FALSE, /* not inherited */
\r
8834 DUPLICATE_SAME_ACCESS);
\r
8836 return GetLastError();
\r
8838 CloseHandle(hChildStdoutRd);
\r
8841 * The steps for redirecting child's STDIN:
\r
8842 * 1. Create anonymous pipe to be STDIN for child.
\r
8843 * 2. Create a noninheritable duplicate of write handle,
\r
8844 * and close the inheritable write handle.
\r
8847 /* Create a pipe for the child's STDIN. */
\r
8848 if (! CreatePipe(&hChildStdinRd, &hChildStdinWr, &saAttr, 0)) {
\r
8849 return GetLastError();
\r
8852 /* Duplicate the write handle to the pipe, so it is not inherited. */
\r
8853 fSuccess = DuplicateHandle(GetCurrentProcess(), hChildStdinWr,
\r
8854 GetCurrentProcess(), &hChildStdinWrDup, 0,
\r
8855 FALSE, /* not inherited */
\r
8856 DUPLICATE_SAME_ACCESS);
\r
8858 return GetLastError();
\r
8860 CloseHandle(hChildStdinWr);
\r
8862 /* Arrange to (1) look in dir for the child .exe file, and
\r
8863 * (2) have dir be the child's working directory. Interpret
\r
8864 * dir relative to the directory WinBoard loaded from. */
\r
8865 GetCurrentDirectory(MSG_SIZ, buf);
\r
8866 SetCurrentDirectory(installDir);
\r
8867 SetCurrentDirectory(dir);
\r
8869 /* Now create the child process. */
\r
8871 siStartInfo.cb = sizeof(STARTUPINFO);
\r
8872 siStartInfo.lpReserved = NULL;
\r
8873 siStartInfo.lpDesktop = NULL;
\r
8874 siStartInfo.lpTitle = NULL;
\r
8875 siStartInfo.dwFlags = STARTF_USESTDHANDLES;
\r
8876 siStartInfo.cbReserved2 = 0;
\r
8877 siStartInfo.lpReserved2 = NULL;
\r
8878 siStartInfo.hStdInput = hChildStdinRd;
\r
8879 siStartInfo.hStdOutput = hChildStdoutWr;
\r
8880 siStartInfo.hStdError = hChildStdoutWr;
\r
8882 fSuccess = CreateProcess(NULL,
\r
8883 cmdLine, /* command line */
\r
8884 NULL, /* process security attributes */
\r
8885 NULL, /* primary thread security attrs */
\r
8886 TRUE, /* handles are inherited */
\r
8887 DETACHED_PROCESS|CREATE_NEW_PROCESS_GROUP,
\r
8888 NULL, /* use parent's environment */
\r
8890 &siStartInfo, /* STARTUPINFO pointer */
\r
8891 &piProcInfo); /* receives PROCESS_INFORMATION */
\r
8893 err = GetLastError();
\r
8894 SetCurrentDirectory(buf); /* return to prev directory */
\r
8899 if (appData.niceEngines){ // [HGM] nice: adjust engine proc priority
\r
8900 if(appData.debugMode) fprintf(debugFP, "nice engine proc to %d\n", appData.niceEngines);
\r
8901 SetPriorityClass(piProcInfo.hProcess, GetWin32Priority(appData.niceEngines));
\r
8904 /* Close the handles we don't need in the parent */
\r
8905 CloseHandle(piProcInfo.hThread);
\r
8906 CloseHandle(hChildStdinRd);
\r
8907 CloseHandle(hChildStdoutWr);
\r
8909 /* Prepare return value */
\r
8910 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
\r
8911 cp->kind = CPReal;
\r
8912 cp->hProcess = piProcInfo.hProcess;
\r
8913 cp->pid = piProcInfo.dwProcessId;
\r
8914 cp->hFrom = hChildStdoutRdDup;
\r
8915 cp->hTo = hChildStdinWrDup;
\r
8917 *pr = (void *) cp;
\r
8919 /* Klaus Friedel says that this Sleep solves a problem under Windows
\r
8920 2000 where engines sometimes don't see the initial command(s)
\r
8921 from WinBoard and hang. I don't understand how that can happen,
\r
8922 but the Sleep is harmless, so I've put it in. Others have also
\r
8923 reported what may be the same problem, so hopefully this will fix
\r
8924 it for them too. */
\r
8932 DestroyChildProcess(ProcRef pr, int/*boolean*/ signal)
\r
8934 ChildProc *cp; int result;
\r
8936 cp = (ChildProc *) pr;
\r
8937 if (cp == NULL) return;
\r
8939 switch (cp->kind) {
\r
8941 /* TerminateProcess is considered harmful, so... */
\r
8942 CloseHandle(cp->hTo); /* Closing this will give the child an EOF and hopefully kill it */
\r
8943 if (cp->hFrom) CloseHandle(cp->hFrom); /* if NULL, InputThread will close it */
\r
8944 /* The following doesn't work because the chess program
\r
8945 doesn't "have the same console" as WinBoard. Maybe
\r
8946 we could arrange for this even though neither WinBoard
\r
8947 nor the chess program uses a console for stdio? */
\r
8948 /*!!if (signal) GenerateConsoleCtrlEvent(CTRL_BREAK_EVENT, cp->pid);*/
\r
8950 /* [AS] Special termination modes for misbehaving programs... */
\r
8951 if( signal == 9 ) {
\r
8952 result = TerminateProcess( cp->hProcess, 0 );
\r
8954 if ( appData.debugMode) {
\r
8955 fprintf( debugFP, "Terminating process %lu, result=%d\n", cp->pid, result );
\r
8958 else if( signal == 10 ) {
\r
8959 DWORD dw = WaitForSingleObject( cp->hProcess, 3*1000 ); // Wait 3 seconds at most
\r
8961 if( dw != WAIT_OBJECT_0 ) {
\r
8962 result = TerminateProcess( cp->hProcess, 0 );
\r
8964 if ( appData.debugMode) {
\r
8965 fprintf( debugFP, "Process %lu still alive after timeout, killing... result=%d\n", cp->pid, result );
\r
8971 CloseHandle(cp->hProcess);
\r
8975 if (cp->hFrom) CloseHandle(cp->hFrom);
\r
8979 closesocket(cp->sock);
\r
8984 if (signal) send(cp->sock2, "\017", 1, 0); /* 017 = 15 = SIGTERM */
\r
8985 closesocket(cp->sock);
\r
8986 closesocket(cp->sock2);
\r
8994 InterruptChildProcess(ProcRef pr)
\r
8998 cp = (ChildProc *) pr;
\r
8999 if (cp == NULL) return;
\r
9000 switch (cp->kind) {
\r
9002 /* The following doesn't work because the chess program
\r
9003 doesn't "have the same console" as WinBoard. Maybe
\r
9004 we could arrange for this even though neither WinBoard
\r
9005 nor the chess program uses a console for stdio */
\r
9006 /*!!GenerateConsoleCtrlEvent(CTRL_C_EVENT, cp->pid);*/
\r
9011 /* Can't interrupt */
\r
9015 send(cp->sock2, "\002", 1, 0); /* 2 = SIGINT */
\r
9022 OpenTelnet(char *host, char *port, ProcRef *pr)
\r
9024 char cmdLine[MSG_SIZ];
\r
9026 if (port[0] == NULLCHAR) {
\r
9027 snprintf(cmdLine, MSG_SIZ, "%s %s", appData.telnetProgram, host);
\r
9029 snprintf(cmdLine, MSG_SIZ, "%s %s %s", appData.telnetProgram, host, port);
\r
9031 return StartChildProcess(cmdLine, "", pr);
\r
9035 /* Code to open TCP sockets */
\r
9038 OpenTCP(char *host, char *port, ProcRef *pr)
\r
9043 struct sockaddr_in sa, mysa;
\r
9044 struct hostent FAR *hp;
\r
9045 unsigned short uport;
\r
9046 WORD wVersionRequested;
\r
9049 /* Initialize socket DLL */
\r
9050 wVersionRequested = MAKEWORD(1, 1);
\r
9051 err = WSAStartup(wVersionRequested, &wsaData);
\r
9052 if (err != 0) return err;
\r
9055 if ((s = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {
\r
9056 err = WSAGetLastError();
\r
9061 /* Bind local address using (mostly) don't-care values.
\r
9063 memset((char *) &mysa, 0, sizeof(struct sockaddr_in));
\r
9064 mysa.sin_family = AF_INET;
\r
9065 mysa.sin_addr.s_addr = INADDR_ANY;
\r
9066 uport = (unsigned short) 0;
\r
9067 mysa.sin_port = htons(uport);
\r
9068 if (bind(s, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))
\r
9069 == SOCKET_ERROR) {
\r
9070 err = WSAGetLastError();
\r
9075 /* Resolve remote host name */
\r
9076 memset((char *) &sa, 0, sizeof(struct sockaddr_in));
\r
9077 if (!(hp = gethostbyname(host))) {
\r
9078 unsigned int b0, b1, b2, b3;
\r
9080 err = WSAGetLastError();
\r
9082 if (sscanf(host, "%u.%u.%u.%u", &b0, &b1, &b2, &b3) == 4) {
\r
9083 hp = (struct hostent *) calloc(1, sizeof(struct hostent));
\r
9084 hp->h_addrtype = AF_INET;
\r
9086 hp->h_addr_list = (char **) calloc(2, sizeof(char *));
\r
9087 hp->h_addr_list[0] = (char *) malloc(4);
\r
9088 hp->h_addr_list[0][0] = (char) b0;
\r
9089 hp->h_addr_list[0][1] = (char) b1;
\r
9090 hp->h_addr_list[0][2] = (char) b2;
\r
9091 hp->h_addr_list[0][3] = (char) b3;
\r
9097 sa.sin_family = hp->h_addrtype;
\r
9098 uport = (unsigned short) atoi(port);
\r
9099 sa.sin_port = htons(uport);
\r
9100 memcpy((char *) &sa.sin_addr, hp->h_addr, hp->h_length);
\r
9102 /* Make connection */
\r
9103 if (connect(s, (struct sockaddr *) &sa,
\r
9104 sizeof(struct sockaddr_in)) == SOCKET_ERROR) {
\r
9105 err = WSAGetLastError();
\r
9110 /* Prepare return value */
\r
9111 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
\r
9112 cp->kind = CPSock;
\r
9114 *pr = (ProcRef *) cp;
\r
9120 OpenCommPort(char *name, ProcRef *pr)
\r
9125 char fullname[MSG_SIZ];
\r
9127 if (*name != '\\')
\r
9128 snprintf(fullname, MSG_SIZ, "\\\\.\\%s", name);
\r
9130 safeStrCpy(fullname, name, sizeof(fullname)/sizeof(fullname[0]) );
\r
9132 h = CreateFile(name, GENERIC_READ | GENERIC_WRITE,
\r
9133 0, NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL);
\r
9134 if (h == (HANDLE) -1) {
\r
9135 return GetLastError();
\r
9139 if (!SetCommState(h, (LPDCB) &dcb)) return GetLastError();
\r
9141 /* Accumulate characters until a 100ms pause, then parse */
\r
9142 ct.ReadIntervalTimeout = 100;
\r
9143 ct.ReadTotalTimeoutMultiplier = 0;
\r
9144 ct.ReadTotalTimeoutConstant = 0;
\r
9145 ct.WriteTotalTimeoutMultiplier = 0;
\r
9146 ct.WriteTotalTimeoutConstant = 0;
\r
9147 if (!SetCommTimeouts(h, (LPCOMMTIMEOUTS) &ct)) return GetLastError();
\r
9149 /* Prepare return value */
\r
9150 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
\r
9151 cp->kind = CPComm;
\r
9154 *pr = (ProcRef *) cp;
\r
9160 OpenLoopback(ProcRef *pr)
\r
9162 DisplayFatalError(_("Not implemented"), 0, 1);
\r
9168 OpenRcmd(char* host, char* user, char* cmd, ProcRef* pr)
\r
9173 struct sockaddr_in sa, mysa;
\r
9174 struct hostent FAR *hp;
\r
9175 unsigned short uport;
\r
9176 WORD wVersionRequested;
\r
9179 char stderrPortStr[MSG_SIZ];
\r
9181 /* Initialize socket DLL */
\r
9182 wVersionRequested = MAKEWORD(1, 1);
\r
9183 err = WSAStartup(wVersionRequested, &wsaData);
\r
9184 if (err != 0) return err;
\r
9186 /* Resolve remote host name */
\r
9187 memset((char *) &sa, 0, sizeof(struct sockaddr_in));
\r
9188 if (!(hp = gethostbyname(host))) {
\r
9189 unsigned int b0, b1, b2, b3;
\r
9191 err = WSAGetLastError();
\r
9193 if (sscanf(host, "%u.%u.%u.%u", &b0, &b1, &b2, &b3) == 4) {
\r
9194 hp = (struct hostent *) calloc(1, sizeof(struct hostent));
\r
9195 hp->h_addrtype = AF_INET;
\r
9197 hp->h_addr_list = (char **) calloc(2, sizeof(char *));
\r
9198 hp->h_addr_list[0] = (char *) malloc(4);
\r
9199 hp->h_addr_list[0][0] = (char) b0;
\r
9200 hp->h_addr_list[0][1] = (char) b1;
\r
9201 hp->h_addr_list[0][2] = (char) b2;
\r
9202 hp->h_addr_list[0][3] = (char) b3;
\r
9208 sa.sin_family = hp->h_addrtype;
\r
9209 uport = (unsigned short) 514;
\r
9210 sa.sin_port = htons(uport);
\r
9211 memcpy((char *) &sa.sin_addr, hp->h_addr, hp->h_length);
\r
9213 /* Bind local socket to unused "privileged" port address
\r
9215 s = INVALID_SOCKET;
\r
9216 memset((char *) &mysa, 0, sizeof(struct sockaddr_in));
\r
9217 mysa.sin_family = AF_INET;
\r
9218 mysa.sin_addr.s_addr = INADDR_ANY;
\r
9219 for (fromPort = 1023;; fromPort--) {
\r
9220 if (fromPort < 0) {
\r
9222 return WSAEADDRINUSE;
\r
9224 if (s == INVALID_SOCKET) {
\r
9225 if ((s = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {
\r
9226 err = WSAGetLastError();
\r
9231 uport = (unsigned short) fromPort;
\r
9232 mysa.sin_port = htons(uport);
\r
9233 if (bind(s, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))
\r
9234 == SOCKET_ERROR) {
\r
9235 err = WSAGetLastError();
\r
9236 if (err == WSAEADDRINUSE) continue;
\r
9240 if (connect(s, (struct sockaddr *) &sa,
\r
9241 sizeof(struct sockaddr_in)) == SOCKET_ERROR) {
\r
9242 err = WSAGetLastError();
\r
9243 if (err == WSAEADDRINUSE) {
\r
9254 /* Bind stderr local socket to unused "privileged" port address
\r
9256 s2 = INVALID_SOCKET;
\r
9257 memset((char *) &mysa, 0, sizeof(struct sockaddr_in));
\r
9258 mysa.sin_family = AF_INET;
\r
9259 mysa.sin_addr.s_addr = INADDR_ANY;
\r
9260 for (fromPort = 1023;; fromPort--) {
\r
9261 if (fromPort == prevStderrPort) continue; // don't reuse port
\r
9262 if (fromPort < 0) {
\r
9263 (void) closesocket(s);
\r
9265 return WSAEADDRINUSE;
\r
9267 if (s2 == INVALID_SOCKET) {
\r
9268 if ((s2 = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {
\r
9269 err = WSAGetLastError();
\r
9275 uport = (unsigned short) fromPort;
\r
9276 mysa.sin_port = htons(uport);
\r
9277 if (bind(s2, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))
\r
9278 == SOCKET_ERROR) {
\r
9279 err = WSAGetLastError();
\r
9280 if (err == WSAEADDRINUSE) continue;
\r
9281 (void) closesocket(s);
\r
9285 if (listen(s2, 1) == SOCKET_ERROR) {
\r
9286 err = WSAGetLastError();
\r
9287 if (err == WSAEADDRINUSE) {
\r
9289 s2 = INVALID_SOCKET;
\r
9292 (void) closesocket(s);
\r
9293 (void) closesocket(s2);
\r
9299 prevStderrPort = fromPort; // remember port used
\r
9300 snprintf(stderrPortStr, MSG_SIZ, "%d", fromPort);
\r
9302 if (send(s, stderrPortStr, strlen(stderrPortStr) + 1, 0) == SOCKET_ERROR) {
\r
9303 err = WSAGetLastError();
\r
9304 (void) closesocket(s);
\r
9305 (void) closesocket(s2);
\r
9310 if (send(s, UserName(), strlen(UserName()) + 1, 0) == SOCKET_ERROR) {
\r
9311 err = WSAGetLastError();
\r
9312 (void) closesocket(s);
\r
9313 (void) closesocket(s2);
\r
9317 if (*user == NULLCHAR) user = UserName();
\r
9318 if (send(s, user, strlen(user) + 1, 0) == SOCKET_ERROR) {
\r
9319 err = WSAGetLastError();
\r
9320 (void) closesocket(s);
\r
9321 (void) closesocket(s2);
\r
9325 if (send(s, cmd, strlen(cmd) + 1, 0) == SOCKET_ERROR) {
\r
9326 err = WSAGetLastError();
\r
9327 (void) closesocket(s);
\r
9328 (void) closesocket(s2);
\r
9333 if ((s3 = accept(s2, NULL, NULL)) == INVALID_SOCKET) {
\r
9334 err = WSAGetLastError();
\r
9335 (void) closesocket(s);
\r
9336 (void) closesocket(s2);
\r
9340 (void) closesocket(s2); /* Stop listening */
\r
9342 /* Prepare return value */
\r
9343 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
\r
9344 cp->kind = CPRcmd;
\r
9347 *pr = (ProcRef *) cp;
\r
9354 AddInputSource(ProcRef pr, int lineByLine,
\r
9355 InputCallback func, VOIDSTAR closure)
\r
9357 InputSource *is, *is2 = NULL;
\r
9358 ChildProc *cp = (ChildProc *) pr;
\r
9360 is = (InputSource *) calloc(1, sizeof(InputSource));
\r
9361 is->lineByLine = lineByLine;
\r
9363 is->closure = closure;
\r
9364 is->second = NULL;
\r
9365 is->next = is->buf;
\r
9366 if (pr == NoProc) {
\r
9367 is->kind = CPReal;
\r
9368 consoleInputSource = is;
\r
9370 is->kind = cp->kind;
\r
9372 [AS] Try to avoid a race condition if the thread is given control too early:
\r
9373 we create all threads suspended so that the is->hThread variable can be
\r
9374 safely assigned, then let the threads start with ResumeThread.
\r
9376 switch (cp->kind) {
\r
9378 is->hFile = cp->hFrom;
\r
9379 cp->hFrom = NULL; /* now owned by InputThread */
\r
9381 CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) NonOvlInputThread,
\r
9382 (LPVOID) is, CREATE_SUSPENDED, &is->id);
\r
9386 is->hFile = cp->hFrom;
\r
9387 cp->hFrom = NULL; /* now owned by InputThread */
\r
9389 CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) InputThread,
\r
9390 (LPVOID) is, CREATE_SUSPENDED, &is->id);
\r
9394 is->sock = cp->sock;
\r
9396 CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,
\r
9397 (LPVOID) is, CREATE_SUSPENDED, &is->id);
\r
9401 is2 = (InputSource *) calloc(1, sizeof(InputSource));
\r
9403 is->sock = cp->sock;
\r
9405 is2->sock = cp->sock2;
\r
9406 is2->second = is2;
\r
9408 CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,
\r
9409 (LPVOID) is, CREATE_SUSPENDED, &is->id);
\r
9411 CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,
\r
9412 (LPVOID) is2, CREATE_SUSPENDED, &is2->id);
\r
9416 if( is->hThread != NULL ) {
\r
9417 ResumeThread( is->hThread );
\r
9420 if( is2 != NULL && is2->hThread != NULL ) {
\r
9421 ResumeThread( is2->hThread );
\r
9425 return (InputSourceRef) is;
\r
9429 RemoveInputSource(InputSourceRef isr)
\r
9433 is = (InputSource *) isr;
\r
9434 is->hThread = NULL; /* tell thread to stop */
\r
9435 CloseHandle(is->hThread);
\r
9436 if (is->second != NULL) {
\r
9437 is->second->hThread = NULL;
\r
9438 CloseHandle(is->second->hThread);
\r
9442 int no_wrap(char *message, int count)
\r
9444 ConsoleOutput(message, count, FALSE);
\r
9449 OutputToProcess(ProcRef pr, char *message, int count, int *outError)
\r
9452 int outCount = SOCKET_ERROR;
\r
9453 ChildProc *cp = (ChildProc *) pr;
\r
9454 static OVERLAPPED ovl;
\r
9455 static int line = 0;
\r
9459 if (appData.noJoin || !appData.useInternalWrap)
\r
9460 return no_wrap(message, count);
\r
9463 int width = get_term_width();
\r
9464 int len = wrap(NULL, message, count, width, &line);
\r
9465 char *msg = malloc(len);
\r
9469 return no_wrap(message, count);
\r
9472 dbgchk = wrap(msg, message, count, width, &line);
\r
9473 if (dbgchk != len && appData.debugMode)
\r
9474 fprintf(debugFP, "wrap(): dbgchk(%d) != len(%d)\n", dbgchk, len);
\r
9475 ConsoleOutput(msg, len, FALSE);
\r
9482 if (ovl.hEvent == NULL) {
\r
9483 ovl.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
\r
9485 ovl.Internal = ovl.InternalHigh = ovl.Offset = ovl.OffsetHigh = 0;
\r
9487 switch (cp->kind) {
\r
9490 outCount = send(cp->sock, message, count, 0);
\r
9491 if (outCount == SOCKET_ERROR) {
\r
9492 *outError = WSAGetLastError();
\r
9494 *outError = NO_ERROR;
\r
9499 if (WriteFile(((ChildProc *)pr)->hTo, message, count,
\r
9500 &dOutCount, NULL)) {
\r
9501 *outError = NO_ERROR;
\r
9502 outCount = (int) dOutCount;
\r
9504 *outError = GetLastError();
\r
9509 *outError = DoWriteFile(((ChildProc *)pr)->hTo, message, count,
\r
9510 &dOutCount, &ovl);
\r
9511 if (*outError == NO_ERROR) {
\r
9512 outCount = (int) dOutCount;
\r
9520 OutputToProcessDelayed(ProcRef pr, char *message, int count, int *outError,
\r
9523 /* Ignore delay, not implemented for WinBoard */
\r
9524 return OutputToProcess(pr, message, count, outError);
\r
9529 CmailSigHandlerCallBack(InputSourceRef isr, VOIDSTAR closure,
\r
9530 char *buf, int count, int error)
\r
9532 DisplayFatalError(_("Not implemented"), 0, 1);
\r
9535 /* see wgamelist.c for Game List functions */
\r
9536 /* see wedittags.c for Edit Tags functions */
\r
9543 char buf[MSG_SIZ];
\r
9546 if (SearchPath(installDir, appData.icsLogon, NULL, MSG_SIZ, buf, &dummy)) {
\r
9547 f = fopen(buf, "r");
\r
9549 ProcessICSInitScript(f);
\r
9557 StartAnalysisClock()
\r
9559 if (analysisTimerEvent) return;
\r
9560 analysisTimerEvent = SetTimer(hwndMain, (UINT) ANALYSIS_TIMER_ID,
\r
9561 (UINT) 2000, NULL);
\r
9565 SetHighlights(int fromX, int fromY, int toX, int toY)
\r
9567 highlightInfo.sq[0].x = fromX;
\r
9568 highlightInfo.sq[0].y = fromY;
\r
9569 highlightInfo.sq[1].x = toX;
\r
9570 highlightInfo.sq[1].y = toY;
\r
9576 highlightInfo.sq[0].x = highlightInfo.sq[0].y =
\r
9577 highlightInfo.sq[1].x = highlightInfo.sq[1].y = -1;
\r
9581 SetPremoveHighlights(int fromX, int fromY, int toX, int toY)
\r
9583 premoveHighlightInfo.sq[0].x = fromX;
\r
9584 premoveHighlightInfo.sq[0].y = fromY;
\r
9585 premoveHighlightInfo.sq[1].x = toX;
\r
9586 premoveHighlightInfo.sq[1].y = toY;
\r
9590 ClearPremoveHighlights()
\r
9592 premoveHighlightInfo.sq[0].x = premoveHighlightInfo.sq[0].y =
\r
9593 premoveHighlightInfo.sq[1].x = premoveHighlightInfo.sq[1].y = -1;
\r
9597 ShutDownFrontEnd()
\r
9599 if (saveSettingsOnExit) SaveSettings(settingsFileName);
\r
9600 DeleteClipboardTempFiles();
\r
9606 if (IsIconic(hwndMain))
\r
9607 ShowWindow(hwndMain, SW_RESTORE);
\r
9609 SetActiveWindow(hwndMain);
\r
9613 * Prototypes for animation support routines
\r
9615 static void ScreenSquare(int column, int row, POINT * pt);
\r
9616 static void Tween( POINT * start, POINT * mid, POINT * finish, int factor,
\r
9617 POINT frames[], int * nFrames);
\r
9623 AnimateAtomicCapture(Board board, int fromX, int fromY, int toX, int toY)
\r
9624 { // [HGM] atomic: animate blast wave
\r
9627 explodeInfo.fromX = fromX;
\r
9628 explodeInfo.fromY = fromY;
\r
9629 explodeInfo.toX = toX;
\r
9630 explodeInfo.toY = toY;
\r
9631 for(i=1; i<4*kFactor; i++) {
\r
9632 explodeInfo.radius = (i*180)/(4*kFactor-1);
\r
9633 DrawPosition(FALSE, board);
\r
9634 Sleep(appData.animSpeed);
\r
9636 explodeInfo.radius = 0;
\r
9637 DrawPosition(TRUE, board);
\r
9641 AnimateMove(board, fromX, fromY, toX, toY)
\r
9648 ChessSquare piece;
\r
9649 POINT start, finish, mid;
\r
9650 POINT frames[kFactor * 2 + 1];
\r
9653 if (!appData.animate) return;
\r
9654 if (doingSizing) return;
\r
9655 if (fromY < 0 || fromX < 0) return;
\r
9656 piece = board[fromY][fromX];
\r
9657 if (piece >= EmptySquare) return;
\r
9659 ScreenSquare(fromX, fromY, &start);
\r
9660 ScreenSquare(toX, toY, &finish);
\r
9662 /* All moves except knight jumps move in straight line */
\r
9663 if (!(abs(fromX-toX) == 1 && abs(fromY-toY) == 2 || abs(fromX-toX) == 2 && abs(fromY-toY) == 1)) {
\r
9664 mid.x = start.x + (finish.x - start.x) / 2;
\r
9665 mid.y = start.y + (finish.y - start.y) / 2;
\r
9667 /* Knight: make straight movement then diagonal */
\r
9668 if (abs(toY - fromY) < abs(toX - fromX)) {
\r
9669 mid.x = start.x + (finish.x - start.x) / 2;
\r
9673 mid.y = start.y + (finish.y - start.y) / 2;
\r
9677 /* Don't use as many frames for very short moves */
\r
9678 if (abs(toY - fromY) + abs(toX - fromX) <= 2)
\r
9679 Tween(&start, &mid, &finish, kFactor - 1, frames, &nFrames);
\r
9681 Tween(&start, &mid, &finish, kFactor, frames, &nFrames);
\r
9683 animInfo.from.x = fromX;
\r
9684 animInfo.from.y = fromY;
\r
9685 animInfo.to.x = toX;
\r
9686 animInfo.to.y = toY;
\r
9687 animInfo.lastpos = start;
\r
9688 animInfo.piece = piece;
\r
9689 for (n = 0; n < nFrames; n++) {
\r
9690 animInfo.pos = frames[n];
\r
9691 DrawPosition(FALSE, NULL);
\r
9692 animInfo.lastpos = animInfo.pos;
\r
9693 Sleep(appData.animSpeed);
\r
9695 animInfo.pos = finish;
\r
9696 DrawPosition(FALSE, NULL);
\r
9697 animInfo.piece = EmptySquare;
\r
9698 Explode(board, fromX, fromY, toX, toY);
\r
9701 /* Convert board position to corner of screen rect and color */
\r
9704 ScreenSquare(column, row, pt)
\r
9705 int column; int row; POINT * pt;
\r
9708 pt->x = lineGap + ((BOARD_WIDTH-1)-column) * (squareSize + lineGap);
\r
9709 pt->y = lineGap + row * (squareSize + lineGap);
\r
9711 pt->x = lineGap + column * (squareSize + lineGap);
\r
9712 pt->y = lineGap + ((BOARD_HEIGHT-1)-row) * (squareSize + lineGap);
\r
9716 /* Generate a series of frame coords from start->mid->finish.
\r
9717 The movement rate doubles until the half way point is
\r
9718 reached, then halves back down to the final destination,
\r
9719 which gives a nice slow in/out effect. The algorithmn
\r
9720 may seem to generate too many intermediates for short
\r
9721 moves, but remember that the purpose is to attract the
\r
9722 viewers attention to the piece about to be moved and
\r
9723 then to where it ends up. Too few frames would be less
\r
9727 Tween(start, mid, finish, factor, frames, nFrames)
\r
9728 POINT * start; POINT * mid;
\r
9729 POINT * finish; int factor;
\r
9730 POINT frames[]; int * nFrames;
\r
9732 int n, fraction = 1, count = 0;
\r
9734 /* Slow in, stepping 1/16th, then 1/8th, ... */
\r
9735 for (n = 0; n < factor; n++)
\r
9737 for (n = 0; n < factor; n++) {
\r
9738 frames[count].x = start->x + (mid->x - start->x) / fraction;
\r
9739 frames[count].y = start->y + (mid->y - start->y) / fraction;
\r
9741 fraction = fraction / 2;
\r
9745 frames[count] = *mid;
\r
9748 /* Slow out, stepping 1/2, then 1/4, ... */
\r
9750 for (n = 0; n < factor; n++) {
\r
9751 frames[count].x = finish->x - (finish->x - mid->x) / fraction;
\r
9752 frames[count].y = finish->y - (finish->y - mid->y) / fraction;
\r
9754 fraction = fraction * 2;
\r
9760 HistorySet( char movelist[][2*MOVE_LEN], int first, int last, int current )
\r
9762 MoveHistorySet( movelist, first, last, current, pvInfoList );
\r
9764 EvalGraphSet( first, last, current, pvInfoList );
\r