2 * WinBoard.c -- Windows NT front end to XBoard
\r
4 * Copyright 1991 by Digital Equipment Corporation, Maynard,
\r
7 * Enhancements Copyright 1992-2001, 2002, 2003, 2004, 2005, 2006,
\r
8 * 2007, 2008, 2009, 2010 Free Software Foundation, Inc.
\r
10 * Enhancements Copyright 2005 Alessandro Scotti
\r
12 * XBoard borrows its colors and the bitmaps.xchess bitmap set from XChess,
\r
13 * which was written and is copyrighted by Wayne Christopher.
\r
15 * The following terms apply to Digital Equipment Corporation's copyright
\r
16 * interest in XBoard:
\r
17 * ------------------------------------------------------------------------
\r
18 * All Rights Reserved
\r
20 * Permission to use, copy, modify, and distribute this software and its
\r
21 * documentation for any purpose and without fee is hereby granted,
\r
22 * provided that the above copyright notice appear in all copies and that
\r
23 * both that copyright notice and this permission notice appear in
\r
24 * supporting documentation, and that the name of Digital not be
\r
25 * used in advertising or publicity pertaining to distribution of the
\r
26 * software without specific, written prior permission.
\r
28 * DIGITAL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
\r
29 * ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL
\r
30 * DIGITAL BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
\r
31 * ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
\r
32 * WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
\r
33 * ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
\r
35 * ------------------------------------------------------------------------
\r
37 * The following terms apply to the enhanced version of XBoard
\r
38 * distributed by the Free Software Foundation:
\r
39 * ------------------------------------------------------------------------
\r
41 * GNU XBoard is free software: you can redistribute it and/or modify
\r
42 * it under the terms of the GNU General Public License as published by
\r
43 * the Free Software Foundation, either version 3 of the License, or (at
\r
44 * your option) any later version.
\r
46 * GNU XBoard is distributed in the hope that it will be useful, but
\r
47 * WITHOUT ANY WARRANTY; without even the implied warranty of
\r
48 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
\r
49 * General Public License for more details.
\r
51 * You should have received a copy of the GNU General Public License
\r
52 * along with this program. If not, see http://www.gnu.org/licenses/. *
\r
54 *------------------------------------------------------------------------
\r
55 ** See the file ChangeLog for a revision history. */
\r
59 #include <windows.h>
\r
60 #include <winuser.h>
\r
61 #include <winsock.h>
\r
62 #include <commctrl.h>
\r
68 #include <sys/stat.h>
\r
71 #include <commdlg.h>
\r
73 #include <richedit.h>
\r
74 #include <mmsystem.h>
\r
83 #include "frontend.h"
\r
84 #include "backend.h"
\r
85 #include "winboard.h"
\r
87 #include "wclipbrd.h"
\r
88 #include "woptions.h"
\r
89 #include "wsockerr.h"
\r
90 #include "defaults.h"
\r
94 //void InitEngineUCI( const char * iniDir, ChessProgramState * cps );
\r
97 void mysrandom(unsigned int seed);
\r
99 extern int whiteFlag, blackFlag;
\r
100 Boolean flipClock = FALSE;
\r
101 extern HANDLE chatHandle[];
\r
102 extern int ics_type;
\r
104 void DisplayHoldingsCount(HDC hdc, int x, int y, int align, int copyNumber);
\r
105 VOID NewVariantPopup(HWND hwnd);
\r
106 int FinishMove P((ChessMove moveType, int fromX, int fromY, int toX, int toY,
\r
107 /*char*/int promoChar));
\r
108 void DisplayMove P((int moveNumber));
\r
109 Boolean ParseFEN P((Board board, int *blackPlaysFirst, char *fen));
\r
110 void ChatPopUp P((char *s));
\r
112 ChessSquare piece;
\r
113 POINT pos; /* window coordinates of current pos */
\r
114 POINT lastpos; /* window coordinates of last pos - used for clipping */
\r
115 POINT from; /* board coordinates of the piece's orig pos */
\r
116 POINT to; /* board coordinates of the piece's new pos */
\r
119 static AnimInfo animInfo = { EmptySquare, {-1,-1}, {-1,-1}, {-1,-1} };
\r
122 POINT start; /* window coordinates of start pos */
\r
123 POINT pos; /* window coordinates of current pos */
\r
124 POINT lastpos; /* window coordinates of last pos - used for clipping */
\r
125 POINT from; /* board coordinates of the piece's orig pos */
\r
128 static DragInfo dragInfo = { {-1,-1}, {-1,-1}, {-1,-1}, {-1,-1} };
\r
131 POINT sq[2]; /* board coordinates of from, to squares */
\r
134 static HighlightInfo highlightInfo = { {{-1, -1}, {-1, -1}} };
\r
135 static HighlightInfo premoveHighlightInfo = { {{-1, -1}, {-1, -1}} };
\r
136 static HighlightInfo partnerHighlightInfo = { {{-1, -1}, {-1, -1}} };
\r
137 static HighlightInfo oldPartnerHighlight = { {{-1, -1}, {-1, -1}} };
\r
139 typedef struct { // [HGM] atomic
\r
140 int fromX, fromY, toX, toY, radius;
\r
143 static ExplodeInfo explodeInfo;
\r
145 /* Window class names */
\r
146 char szAppName[] = "WinBoard";
\r
147 char szConsoleName[] = "WBConsole";
\r
149 /* Title bar text */
\r
150 char szTitle[] = "WinBoard";
\r
151 char szConsoleTitle[] = "I C S Interaction";
\r
154 char *settingsFileName;
\r
155 Boolean saveSettingsOnExit;
\r
156 char installDir[MSG_SIZ];
\r
157 int errorExitStatus;
\r
159 BoardSize boardSize;
\r
160 Boolean chessProgram;
\r
161 //static int boardX, boardY;
\r
162 int minX, minY; // [HGM] placement: volatile limits on upper-left corner
\r
163 int squareSize, lineGap, minorSize;
\r
164 static int winW, winH;
\r
165 static RECT messageRect, whiteRect, blackRect, leftLogoRect, rightLogoRect; // [HGM] logo
\r
166 static int logoHeight = 0;
\r
167 static char messageText[MESSAGE_TEXT_MAX];
\r
168 static int clockTimerEvent = 0;
\r
169 static int loadGameTimerEvent = 0;
\r
170 static int analysisTimerEvent = 0;
\r
171 static DelayedEventCallback delayedTimerCallback;
\r
172 static int delayedTimerEvent = 0;
\r
173 static int buttonCount = 2;
\r
174 char *icsTextMenuString;
\r
176 char *firstChessProgramNames;
\r
177 char *secondChessProgramNames;
\r
179 #define PALETTESIZE 256
\r
181 HINSTANCE hInst; /* current instance */
\r
182 Boolean alwaysOnTop = FALSE;
\r
184 COLORREF lightSquareColor, darkSquareColor, whitePieceColor,
\r
185 blackPieceColor, highlightSquareColor, premoveHighlightColor;
\r
187 ColorClass currentColorClass;
\r
189 static HWND savedHwnd;
\r
190 HWND hCommPort = NULL; /* currently open comm port */
\r
191 static HWND hwndPause; /* pause button */
\r
192 static HBITMAP pieceBitmap[3][(int) BlackPawn]; /* [HGM] nr of bitmaps referred to bP in stead of wK */
\r
193 static HBRUSH lightSquareBrush, darkSquareBrush,
\r
194 blackSquareBrush, /* [HGM] for band between board and holdings */
\r
195 explodeBrush, /* [HGM] atomic */
\r
196 markerBrush, /* [HGM] markers */
\r
197 whitePieceBrush, blackPieceBrush, iconBkgndBrush /*, outlineBrush*/;
\r
198 static POINT gridEndpoints[(BOARD_RANKS + BOARD_FILES + 2) * 2];
\r
199 static DWORD gridVertexCounts[BOARD_RANKS + BOARD_FILES + 2];
\r
200 static HPEN gridPen = NULL;
\r
201 static HPEN highlightPen = NULL;
\r
202 static HPEN premovePen = NULL;
\r
203 static NPLOGPALETTE pLogPal;
\r
204 static BOOL paletteChanged = FALSE;
\r
205 static HICON iconWhite, iconBlack, iconCurrent;
\r
206 static int doingSizing = FALSE;
\r
207 static int lastSizing = 0;
\r
208 static int prevStderrPort;
\r
209 static HBITMAP userLogo;
\r
211 static HBITMAP liteBackTexture = NULL;
\r
212 static HBITMAP darkBackTexture = NULL;
\r
213 static int liteBackTextureMode = BACK_TEXTURE_MODE_PLAIN;
\r
214 static int darkBackTextureMode = BACK_TEXTURE_MODE_PLAIN;
\r
215 static int backTextureSquareSize = 0;
\r
216 static struct { int x; int y; int mode; } backTextureSquareInfo[BOARD_RANKS][BOARD_FILES];
\r
218 #if __GNUC__ && !defined(_winmajor)
\r
219 #define oldDialog 0 /* cygwin doesn't define _winmajor; mingw does */
\r
221 #if defined(_winmajor)
\r
222 #define oldDialog (_winmajor < 4)
\r
224 #define oldDialog 0
\r
228 #define INTERNATIONAL
\r
230 #ifdef INTERNATIONAL
\r
231 # define _(s) T_(s)
\r
237 # define Translate(x, y)
\r
238 # define LoadLanguageFile(s)
\r
241 #ifdef INTERNATIONAL
\r
243 Boolean barbaric; // flag indicating if translation is needed
\r
245 // list of item numbers used in each dialog (used to alter language at run time)
\r
247 #define ABOUTBOX -1 /* not sure why these are needed */
\r
248 #define ABOUTBOX2 -1
\r
250 int dialogItems[][40] = {
\r
251 { ABOUTBOX, IDOK, 400 },
\r
252 { DLG_TimeControl, IDC_Babble, OPT_TCUseMoves, OPT_TCUseInc, OPT_TCUseFixed,
\r
253 OPT_TCtext1, OPT_TCtext2, OPT_TCitext1, OPT_TCitext2, OPT_TCftext, GPB_Factors, IDC_Factor1, IDC_Factor2, IDOK, IDCANCEL },
\r
254 { DLG_LoadOptions, OPT_Autostep, OPT_AStext1, IDOK, IDCANCEL },
\r
255 { DLG_SaveOptions, OPT_Autosave, OPT_AVPrompt, OPT_AVToFile, OPT_AVBrowse,
\r
256 801, OPT_PGN, OPT_Old, OPT_OutOfBookInfo, IDOK, IDCANCEL },
\r
257 { 1536, 1090, IDC_Directories, 1089, 1091, IDOK, IDCANCEL, 1038, IDC_IndexNr, 1037 },
\r
258 { DLG_CommPort, IDOK, IDCANCEL, IDC_Port, IDC_Rate, IDC_Bits, IDC_Parity,
\r
259 IDC_Stop, IDC_Flow, OPT_SerialHelp },
\r
260 { DLG_EditComment, IDOK, OPT_CancelComment, OPT_ClearComment, OPT_EditComment },
\r
261 { DLG_PromotionKing, PB_Chancellor, PB_Archbishop, PB_Queen, PB_Rook,
\r
262 PB_Bishop, PB_Knight, PB_King, IDCANCEL, IDC_Yes, IDC_No, IDC_Centaur },
\r
263 { ABOUTBOX2, IDC_ChessBoard },
\r
264 { DLG_GameList, OPT_GameListLoad, OPT_GameListPrev, OPT_GameListNext,
\r
265 OPT_GameListClose, IDC_GameListDoFilter },
\r
266 { DLG_EditTags, IDOK, OPT_TagsCancel, OPT_EditTags },
\r
267 { DLG_Error, IDOK },
\r
268 { DLG_Colorize, IDOK, IDCANCEL, OPT_ChooseColor, OPT_Bold, OPT_Italic,
\r
269 OPT_Underline, OPT_Strikeout, OPT_Sample },
\r
270 { DLG_Question, IDOK, IDCANCEL, OPT_QuestionText },
\r
271 { DLG_Startup, IDC_Welcome, OPT_ChessEngine, OPT_ChessServer, OPT_View,
\r
272 IDC_SPECIFY_ENG_STATIC, IDC_SPECIFY_SERVER_STATIC, OPT_AnyAdditional,
\r
273 IDOK, IDCANCEL, IDM_HELPCONTENTS },
\r
274 { DLG_IndexNumber, IDC_Index },
\r
275 { DLG_TypeInMove, IDOK, IDCANCEL },
\r
276 { DLG_TypeInName, IDOK, IDCANCEL },
\r
277 { DLG_Sound, IDC_Event, OPT_NoSound, OPT_DefaultBeep, OPT_BuiltInSound,
\r
278 OPT_WavFile, OPT_BrowseSound, OPT_DefaultSounds, IDOK, IDCANCEL, OPT_PlaySound },
\r
279 { DLG_GeneralOptions, IDOK, IDCANCEL, OPT_AlwaysOnTop, OPT_HighlightLastMove,
\r
280 OPT_AlwaysQueen, OPT_PeriodicUpdates, OPT_AnimateDragging, OPT_PonderNextMove,
\r
281 OPT_AnimateMoving, OPT_PopupExitMessage, OPT_AutoFlag, OPT_PopupMoveErrors,
\r
282 OPT_AutoFlipView, OPT_ShowButtonBar, OPT_AutoRaiseBoard, OPT_ShowCoordinates,
\r
283 OPT_Blindfold, OPT_ShowThinking, OPT_HighlightDragging, OPT_TestLegality,
\r
284 OPT_SaveExtPGN, OPT_HideThinkFromHuman, OPT_ExtraInfoInMoveHistory,
\r
285 OPT_HighlightMoveArrow, OPT_AutoLogo },
\r
286 { DLG_IcsOptions, IDOK, IDCANCEL, OPT_AutoComment, OPT_AutoKibitz, OPT_AutoObserve,
\r
287 OPT_GetMoveList, OPT_LocalLineEditing, OPT_QuietPlay, OPT_SeekGraph, OPT_AutoRefresh,
\r
288 OPT_BgObserve, OPT_DualBoard, OPT_Premove, OPT_PremoveWhite, OPT_PremoveBlack,
\r
289 OPT_SmartMove, OPT_IcsAlarm, IDC_Sec, OPT_ChooseShoutColor, OPT_ChooseSShoutColor,
\r
290 OPT_ChooseChannel1Color, OPT_ChooseChannelColor, OPT_ChooseKibitzColor,
\r
291 OPT_ChooseTellColor, OPT_ChooseChallengeColor, OPT_ChooseRequestColor,
\r
292 OPT_ChooseSeekColor, OPT_ChooseNormalColor, OPT_ChooseBackgroundColor,
\r
293 OPT_DefaultColors, OPT_DontColorize, IDC_Boxes, GPB_Colors, GPB_Premove,
\r
294 GPB_General, GPB_Alarm },
\r
295 { DLG_BoardOptions, IDOK, IDCANCEL, OPT_SizeTiny, OPT_SizeTeeny, OPT_SizeDinky,
\r
296 OPT_SizePetite, OPT_SizeSlim, OPT_SizeSmall, OPT_SizeMediocre, OPT_SizeMiddling,
\r
297 OPT_SizeAverage, OPT_SizeModerate, OPT_SizeMedium, OPT_SizeBulky, OPT_SizeLarge,
\r
298 OPT_SizeBig, OPT_SizeHuge, OPT_SizeGiant, OPT_SizeColossal, OPT_SizeTitanic,
\r
299 OPT_ChooseLightSquareColor, OPT_ChooseDarkSquareColor, OPT_ChooseWhitePieceColor,
\r
300 OPT_ChooseBlackPieceColor, OPT_ChooseHighlightSquareColor, OPT_ChoosePremoveHighlightColor,
\r
301 OPT_Monochrome, OPT_AllWhite, OPT_UpsideDown, OPT_DefaultBoardColors, GPB_Colors,
\r
302 IDC_Light, IDC_Dark, IDC_White, IDC_Black, IDC_High, IDC_PreHigh, GPB_Size },
\r
303 { DLG_NewVariant, IDOK, IDCANCEL, OPT_VariantNormal, OPT_VariantFRC, OPT_VariantWildcastle,
\r
304 OPT_VariantNocastle, OPT_VariantLosers, OPT_VariantGiveaway, OPT_VariantSuicide,
\r
305 OPT_Variant3Check, OPT_VariantTwoKings, OPT_VariantAtomic, OPT_VariantCrazyhouse,
\r
306 OPT_VariantBughouse, OPT_VariantTwilight, OPT_VariantShogi, OPT_VariantSuper,
\r
307 OPT_VariantKnightmate, OPT_VariantBerolina, OPT_VariantCylinder, OPT_VariantFairy,
\r
308 OPT_VariantMakruk, OPT_VariantGothic, OPT_VariantCapablanca, OPT_VariantJanus,
\r
309 OPT_VariantCRC, OPT_VariantFalcon, OPT_VariantCourier, OPT_VariantGreat, OPT_VariantSChess,
\r
310 OPT_VariantShatranj, OPT_VariantXiangqi, GPB_Variant, GPB_Board, IDC_Height,
\r
311 IDC_Width, IDC_Hand, IDC_Pieces, IDC_Def },
\r
312 { DLG_Fonts, IDOK, IDCANCEL, OPT_ChooseClockFont, OPT_ChooseMessageFont,
\r
313 OPT_ChooseCoordFont, OPT_ChooseTagFont, OPT_ChooseCommentsFont, OPT_ChooseConsoleFont, OPT_ChooseMoveHistoryFont, OPT_DefaultFonts,
\r
314 OPT_ClockFont, OPT_MessageFont, OPT_CoordFont, OPT_EditTagsFont,
\r
315 OPT_CommentsFont, OPT_MessageFont5, GPB_Current, GPB_All, OPT_MessageFont6 },
\r
316 { DLG_NewGameFRC, IDC_NFG_Label, IDC_NFG_Random, IDOK, IDCANCEL },
\r
317 { DLG_GameListOptions, IDC_GLT, IDC_GLT_Up, IDC_GLT_Down, IDC_GLT_Restore,
\r
318 IDC_GLT_Default, IDOK, IDCANCEL, IDC_GLT_RestoreTo },
\r
319 { DLG_MoveHistory },
\r
320 { DLG_EvalGraph },
\r
321 { DLG_EngineOutput, IDC_EngineLabel1, IDC_Engine1_NPS, IDC_EngineLabel2, IDC_Engine2_NPS },
\r
322 { DLG_Chat, IDC_Partner, IDC_Clear, IDC_Send, },
\r
323 { DLG_EnginePlayOptions, IDC_EpPonder, IDC_EpShowThinking, IDC_EpHideThinkingHuman,
\r
324 IDC_EpPeriodicUpdates, GPB_Adjudications, IDC_Draw, IDC_Moves, IDC_Threshold,
\r
325 IDC_Centi, IDC_TestClaims, IDC_DetectMates, IDC_MaterialDraws, IDC_TrivialDraws,
\r
326 GPB_Apply, IDC_Rule, IDC_Repeats, IDC_ScoreAbs1, IDC_ScoreAbs2, IDOK, IDCANCEL },
\r
327 { DLG_OptionsUCI, IDC_PolyDir, IDC_BrowseForPolyglotDir, IDC_Hash, IDC_Path,
\r
328 IDC_BrowseForEGTB, IDC_Cache, IDC_UseBook, IDC_BrowseForBook, IDC_CPU, IDC_OwnBook1,
\r
329 IDC_OwnBook2, IDC_Depth, IDC_Variation, IDC_DefGames, IDOK, IDCANCEL },
\r
333 static char languageBuf[50000], *foreign[1000], *english[1000], *languageFile[MSG_SIZ];
\r
334 static int lastChecked;
\r
335 static char oldLanguage[MSG_SIZ], *menuText[10][30];
\r
336 extern int tinyLayout;
\r
337 extern char * menuBarText[][10];
\r
340 LoadLanguageFile(char *name)
\r
341 { //load the file with translations, and make a list of the strings to be translated, and their translations
\r
343 int i=0, j=0, n=0, k;
\r
346 if(!name || name[0] == NULLCHAR) return;
\r
347 snprintf(buf, MSG_SIZ, "%s%s", name, strchr(name, '.') ? "" : ".lng"); // auto-append lng extension
\r
348 if(!strcmp(buf, oldLanguage)) { barbaric = 1; return; } // this language already loaded; just switch on
\r
349 if((f = fopen(buf, "r")) == NULL) return;
\r
350 while((k = fgetc(f)) != EOF) {
\r
351 if(i >= sizeof(languageBuf)) { DisplayError("Language file too big", 0); return; }
\r
352 languageBuf[i] = k;
\r
354 if(languageBuf[n] == '"' && languageBuf[i-1] == '"') {
\r
356 if(p = strstr(languageBuf + n + 1, "\" === \"")) {
\r
357 if(p > languageBuf+n+2 && p+8 < languageBuf+i) {
\r
358 if(j >= sizeof(english)) { DisplayError("Too many translated strings", 0); return; }
\r
359 english[j] = languageBuf + n + 1; *p = 0;
\r
360 foreign[j++] = p + 7; languageBuf[i-1] = 0;
\r
361 //if(appData.debugMode) fprintf(debugFP, "translation: replace '%s' by '%s'\n", english[j-1], foreign[j-1]);
\r
366 } else if(i > 0 && languageBuf[i-1] == '\\') {
\r
368 case 'n': k = '\n'; break;
\r
369 case 'r': k = '\r'; break;
\r
370 case 't': k = '\t'; break;
\r
372 languageBuf[--i] = k;
\r
377 barbaric = (j != 0);
\r
378 safeStrCpy(oldLanguage, buf, sizeof(oldLanguage)/sizeof(oldLanguage[0]) );
\r
383 { // return the translation of the given string
\r
384 // efficiency can be improved a lot...
\r
386 //if(appData.debugMode) fprintf(debugFP, "T_(%s)\n", s);
\r
387 if(!barbaric) return s;
\r
388 if(!s) return ""; // sanity
\r
389 while(english[i]) {
\r
390 if(!strcmp(s, english[i])) return foreign[i];
\r
397 Translate(HWND hDlg, int dialogID)
\r
398 { // translate all text items in the given dialog
\r
400 char buf[MSG_SIZ], *s;
\r
401 //if(appData.debugMode) fprintf(debugFP, "Translate(%d)\n", dialogID);
\r
402 if(!barbaric) return;
\r
403 while(dialogItems[i][0] && dialogItems[i][0] != dialogID) i++; // find the dialog description
\r
404 if(dialogItems[i][0] != dialogID) return; // unknown dialog, should not happen
\r
405 GetWindowText( hDlg, buf, MSG_SIZ );
\r
407 //if(appData.debugMode) fprintf(debugFP, "WindowText '%s' -> '%s'\n", buf, s);
\r
408 if(strcmp(buf, s)) SetWindowText(hDlg, s); // replace by translated string (if different)
\r
409 for(j=1; k=dialogItems[i][j]; j++) { // translate all listed dialog items
\r
410 GetDlgItemText(hDlg, k, buf, MSG_SIZ);
\r
411 if(strlen(buf) == 0) continue;
\r
413 if(strcmp(buf, s)) SetDlgItemText(hDlg, k, s); // replace by translated string (if different)
\r
418 TranslateMenus(int addLanguage)
\r
421 WIN32_FIND_DATA fileData;
\r
423 #define IDM_English 1895
\r
425 HMENU mainMenu = GetMenu(hwndMain);
\r
426 for (i=GetMenuItemCount(mainMenu)-1; i>=0; i--) {
\r
427 HMENU subMenu = GetSubMenu(mainMenu, i);
\r
428 ModifyMenu(mainMenu, i, MF_STRING|MF_BYPOSITION|MF_POPUP|EnableMenuItem(mainMenu, i, MF_BYPOSITION),
\r
429 (UINT) subMenu, T_(menuBarText[tinyLayout][i]));
\r
430 for(j=GetMenuItemCount(subMenu)-1; j>=0; j--){
\r
432 UINT k = GetMenuItemID(subMenu, j);
\r
434 safeStrCpy(buf, menuText[i][j], sizeof(buf)/sizeof(buf[0]) ); else {
\r
435 GetMenuString(subMenu, j, buf, MSG_SIZ, MF_BYPOSITION);
\r
436 menuText[i][j] = strdup(buf); // remember original on first change
\r
438 if(buf[0] == NULLCHAR) continue;
\r
439 //fprintf(debugFP, "menu(%d,%d) = %s (%08x, %08x) %d\n", i, j, buf, mainMenu, subMenu, k);
\r
440 ModifyMenu(subMenu, j, MF_STRING|MF_BYPOSITION
\r
441 |CheckMenuItem(subMenu, j, MF_BYPOSITION)
\r
442 |EnableMenuItem(subMenu, j, MF_BYPOSITION), k, T_(buf));
\r
445 DrawMenuBar(hwndMain);
\r
448 if(!addLanguage) return;
\r
449 if((hFind = FindFirstFile("*.LNG", &fileData)) != INVALID_HANDLE_VALUE) {
\r
450 HMENU mainMenu = GetMenu(hwndMain);
\r
451 HMENU subMenu = GetSubMenu(mainMenu, GetMenuItemCount(mainMenu)-1);
\r
452 AppendMenu(subMenu, MF_SEPARATOR, (UINT_PTR) 0, NULL);
\r
453 AppendMenu(subMenu, MF_ENABLED|MF_STRING|(barbaric?MF_UNCHECKED:MF_CHECKED), (UINT_PTR) IDM_English, (LPCTSTR) "English");
\r
454 i = 0; lastChecked = IDM_English;
\r
456 char *p, *q = fileData.cFileName;
\r
457 int checkFlag = MF_UNCHECKED;
\r
458 languageFile[i] = strdup(q);
\r
459 if(barbaric && !strcmp(oldLanguage, q)) {
\r
460 checkFlag = MF_CHECKED;
\r
461 lastChecked = IDM_English + i + 1;
\r
462 CheckMenuItem(mainMenu, IDM_English, MF_BYCOMMAND|MF_UNCHECKED);
\r
464 *q = ToUpper(*q); while(*++q) *q = ToLower(*q);
\r
465 p = strstr(fileData.cFileName, ".lng");
\r
467 AppendMenu(subMenu, MF_ENABLED|MF_STRING|checkFlag, (UINT_PTR) IDM_English + ++i, (LPCTSTR) fileData.cFileName);
\r
468 } while(FindNextFile(hFind, &fileData));
\r
481 int cliWidth, cliHeight;
\r
484 SizeInfo sizeInfo[] =
\r
486 { "tiny", 21, 0, 1, 1, 0, 0 },
\r
487 { "teeny", 25, 1, 1, 1, 0, 0 },
\r
488 { "dinky", 29, 1, 1, 1, 0, 0 },
\r
489 { "petite", 33, 1, 1, 1, 0, 0 },
\r
490 { "slim", 37, 2, 1, 0, 0, 0 },
\r
491 { "small", 40, 2, 1, 0, 0, 0 },
\r
492 { "mediocre", 45, 2, 1, 0, 0, 0 },
\r
493 { "middling", 49, 2, 0, 0, 0, 0 },
\r
494 { "average", 54, 2, 0, 0, 0, 0 },
\r
495 { "moderate", 58, 3, 0, 0, 0, 0 },
\r
496 { "medium", 64, 3, 0, 0, 0, 0 },
\r
497 { "bulky", 72, 3, 0, 0, 0, 0 },
\r
498 { "large", 80, 3, 0, 0, 0, 0 },
\r
499 { "big", 87, 3, 0, 0, 0, 0 },
\r
500 { "huge", 95, 3, 0, 0, 0, 0 },
\r
501 { "giant", 108, 3, 0, 0, 0, 0 },
\r
502 { "colossal", 116, 4, 0, 0, 0, 0 },
\r
503 { "titanic", 129, 4, 0, 0, 0, 0 },
\r
504 { NULL, 0, 0, 0, 0, 0, 0 }
\r
507 #define MF(x) {x, {{0,}, 0. }, {0, }, 0}
\r
508 MyFont fontRec[NUM_SIZES][NUM_FONTS] =
\r
510 { MF(CLOCK_FONT_TINY), MF(MESSAGE_FONT_TINY), MF(COORD_FONT_TINY), MF(CONSOLE_FONT_TINY), MF(COMMENT_FONT_TINY), MF(EDITTAGS_FONT_TINY), MF(MOVEHISTORY_FONT_ALL) },
\r
511 { MF(CLOCK_FONT_TEENY), MF(MESSAGE_FONT_TEENY), MF(COORD_FONT_TEENY), MF(CONSOLE_FONT_TEENY), MF(COMMENT_FONT_TEENY), MF(EDITTAGS_FONT_TEENY), MF(MOVEHISTORY_FONT_ALL) },
\r
512 { MF(CLOCK_FONT_DINKY), MF(MESSAGE_FONT_DINKY), MF(COORD_FONT_DINKY), MF(CONSOLE_FONT_DINKY), MF(COMMENT_FONT_DINKY), MF(EDITTAGS_FONT_DINKY), MF(MOVEHISTORY_FONT_ALL) },
\r
513 { MF(CLOCK_FONT_PETITE), MF(MESSAGE_FONT_PETITE), MF(COORD_FONT_PETITE), MF(CONSOLE_FONT_PETITE), MF(COMMENT_FONT_PETITE), MF(EDITTAGS_FONT_PETITE), MF(MOVEHISTORY_FONT_ALL) },
\r
514 { MF(CLOCK_FONT_SLIM), MF(MESSAGE_FONT_SLIM), MF(COORD_FONT_SLIM), MF(CONSOLE_FONT_SLIM), MF(COMMENT_FONT_SLIM), MF(EDITTAGS_FONT_SLIM), MF(MOVEHISTORY_FONT_ALL) },
\r
515 { MF(CLOCK_FONT_SMALL), MF(MESSAGE_FONT_SMALL), MF(COORD_FONT_SMALL), MF(CONSOLE_FONT_SMALL), MF(COMMENT_FONT_SMALL), MF(EDITTAGS_FONT_SMALL), MF(MOVEHISTORY_FONT_ALL) },
\r
516 { MF(CLOCK_FONT_MEDIOCRE), MF(MESSAGE_FONT_MEDIOCRE), MF(COORD_FONT_MEDIOCRE), MF(CONSOLE_FONT_MEDIOCRE), MF(COMMENT_FONT_MEDIOCRE), MF(EDITTAGS_FONT_MEDIOCRE), MF(MOVEHISTORY_FONT_ALL) },
\r
517 { MF(CLOCK_FONT_MIDDLING), MF(MESSAGE_FONT_MIDDLING), MF(COORD_FONT_MIDDLING), MF(CONSOLE_FONT_MIDDLING), MF(COMMENT_FONT_MIDDLING), MF(EDITTAGS_FONT_MIDDLING), MF(MOVEHISTORY_FONT_ALL) },
\r
518 { MF(CLOCK_FONT_AVERAGE), MF(MESSAGE_FONT_AVERAGE), MF(COORD_FONT_AVERAGE), MF(CONSOLE_FONT_AVERAGE), MF(COMMENT_FONT_AVERAGE), MF(EDITTAGS_FONT_AVERAGE), MF(MOVEHISTORY_FONT_ALL) },
\r
519 { MF(CLOCK_FONT_MODERATE), MF(MESSAGE_FONT_MODERATE), MF(COORD_FONT_MODERATE), MF(CONSOLE_FONT_MODERATE), MF(COMMENT_FONT_MODERATE), MF(EDITTAGS_FONT_MODERATE), MF(MOVEHISTORY_FONT_ALL) },
\r
520 { MF(CLOCK_FONT_MEDIUM), MF(MESSAGE_FONT_MEDIUM), MF(COORD_FONT_MEDIUM), MF(CONSOLE_FONT_MEDIUM), MF(COMMENT_FONT_MEDIUM), MF(EDITTAGS_FONT_MEDIUM), MF(MOVEHISTORY_FONT_ALL) },
\r
521 { MF(CLOCK_FONT_BULKY), MF(MESSAGE_FONT_BULKY), MF(COORD_FONT_BULKY), MF(CONSOLE_FONT_BULKY), MF(COMMENT_FONT_BULKY), MF(EDITTAGS_FONT_BULKY), MF(MOVEHISTORY_FONT_ALL) },
\r
522 { MF(CLOCK_FONT_LARGE), MF(MESSAGE_FONT_LARGE), MF(COORD_FONT_LARGE), MF(CONSOLE_FONT_LARGE), MF(COMMENT_FONT_LARGE), MF(EDITTAGS_FONT_LARGE), MF(MOVEHISTORY_FONT_ALL) },
\r
523 { MF(CLOCK_FONT_BIG), MF(MESSAGE_FONT_BIG), MF(COORD_FONT_BIG), MF(CONSOLE_FONT_BIG), MF(COMMENT_FONT_BIG), MF(EDITTAGS_FONT_BIG), MF(MOVEHISTORY_FONT_ALL) },
\r
524 { MF(CLOCK_FONT_HUGE), MF(MESSAGE_FONT_HUGE), MF(COORD_FONT_HUGE), MF(CONSOLE_FONT_HUGE), MF(COMMENT_FONT_HUGE), MF(EDITTAGS_FONT_HUGE), MF(MOVEHISTORY_FONT_ALL) },
\r
525 { MF(CLOCK_FONT_GIANT), MF(MESSAGE_FONT_GIANT), MF(COORD_FONT_GIANT), MF(CONSOLE_FONT_GIANT), MF(COMMENT_FONT_GIANT), MF(EDITTAGS_FONT_GIANT), MF(MOVEHISTORY_FONT_ALL) },
\r
526 { MF(CLOCK_FONT_COLOSSAL), MF(MESSAGE_FONT_COLOSSAL), MF(COORD_FONT_COLOSSAL), MF(CONSOLE_FONT_COLOSSAL), MF(COMMENT_FONT_COLOSSAL), MF(EDITTAGS_FONT_COLOSSAL), MF(MOVEHISTORY_FONT_ALL) },
\r
527 { MF(CLOCK_FONT_TITANIC), MF(MESSAGE_FONT_TITANIC), MF(COORD_FONT_TITANIC), MF(CONSOLE_FONT_TITANIC), MF(COMMENT_FONT_TITANIC), MF(EDITTAGS_FONT_TITANIC), MF(MOVEHISTORY_FONT_ALL) },
\r
530 MyFont *font[NUM_SIZES][NUM_FONTS];
\r
539 #define BUTTON_WIDTH (tinyLayout ? 16 : 32)
\r
540 #define N_BUTTONS 5
\r
542 MyButtonDesc buttonDesc[N_BUTTONS] =
\r
544 {"<<", IDM_ToStart, NULL, NULL},
\r
545 {"<", IDM_Backward, NULL, NULL},
\r
546 {"P", IDM_Pause, NULL, NULL},
\r
547 {">", IDM_Forward, NULL, NULL},
\r
548 {">>", IDM_ToEnd, NULL, NULL},
\r
551 int tinyLayout = 0, smallLayout = 0;
\r
552 #define MENU_BAR_ITEMS 9
\r
553 char *menuBarText[2][MENU_BAR_ITEMS+1] = {
\r
554 { N_("&File"), N_("&Edit"), N_("&View"), N_("&Mode"), N_("&Action"), N_("E&ngine"), N_("&Options"), N_("&Help"), NULL },
\r
555 { N_("&F"), N_("&E"), N_("&V"), N_("&M"), N_("&A"), N_("&N"), N_("&O"), N_("&H"), NULL },
\r
559 MySound sounds[(int)NSoundClasses];
\r
560 MyTextAttribs textAttribs[(int)NColorClasses];
\r
562 MyColorizeAttribs colorizeAttribs[] = {
\r
563 { (COLORREF)0, 0, N_("Shout Text") },
\r
564 { (COLORREF)0, 0, N_("SShout/CShout") },
\r
565 { (COLORREF)0, 0, N_("Channel 1 Text") },
\r
566 { (COLORREF)0, 0, N_("Channel Text") },
\r
567 { (COLORREF)0, 0, N_("Kibitz Text") },
\r
568 { (COLORREF)0, 0, N_("Tell Text") },
\r
569 { (COLORREF)0, 0, N_("Challenge Text") },
\r
570 { (COLORREF)0, 0, N_("Request Text") },
\r
571 { (COLORREF)0, 0, N_("Seek Text") },
\r
572 { (COLORREF)0, 0, N_("Normal Text") },
\r
573 { (COLORREF)0, 0, N_("None") }
\r
578 static char *commentTitle;
\r
579 static char *commentText;
\r
580 static int commentIndex;
\r
581 static Boolean editComment = FALSE;
\r
584 char errorTitle[MSG_SIZ];
\r
585 char errorMessage[2*MSG_SIZ];
\r
586 HWND errorDialog = NULL;
\r
587 BOOLEAN moveErrorMessageUp = FALSE;
\r
588 BOOLEAN consoleEcho = TRUE;
\r
589 CHARFORMAT consoleCF;
\r
590 COLORREF consoleBackgroundColor;
\r
592 char *programVersion;
\r
598 typedef int CPKind;
\r
607 SOCKET sock2; /* stderr socket for OpenRcmd */
\r
610 #define INPUT_SOURCE_BUF_SIZE 4096
\r
612 typedef struct _InputSource {
\r
619 char buf[INPUT_SOURCE_BUF_SIZE];
\r
623 InputCallback func;
\r
624 struct _InputSource *second; /* for stderr thread on CPRcmd */
\r
628 InputSource *consoleInputSource;
\r
633 VOID ConsoleOutput(char* data, int length, int forceVisible);
\r
634 VOID ConsoleCreate();
\r
636 ConsoleWndProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam);
\r
637 VOID ColorizeTextPopup(HWND hwnd, ColorClass cc);
\r
638 VOID PrintCommSettings(FILE *f, char *name, DCB *dcb);
\r
639 VOID ParseCommSettings(char *arg, DCB *dcb);
\r
641 StartupDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam);
\r
642 VOID APIENTRY MenuPopup(HWND hwnd, POINT pt, HMENU hmenu, UINT def);
\r
643 void ParseIcsTextMenu(char *icsTextMenuString);
\r
644 VOID PopUpMoveDialog(char firstchar);
\r
645 VOID PopUpNameDialog(char firstchar);
\r
646 VOID UpdateSampleText(HWND hDlg, int id, MyColorizeAttribs *mca);
\r
650 int GameListOptions();
\r
652 int dummy; // [HGM] for obsolete args
\r
654 HWND hwndMain = NULL; /* root window*/
\r
655 HWND hwndConsole = NULL;
\r
656 HWND commentDialog = NULL;
\r
657 HWND moveHistoryDialog = NULL;
\r
658 HWND evalGraphDialog = NULL;
\r
659 HWND engineOutputDialog = NULL;
\r
660 HWND gameListDialog = NULL;
\r
661 HWND editTagsDialog = NULL;
\r
663 int commentUp = FALSE;
\r
665 WindowPlacement wpMain;
\r
666 WindowPlacement wpConsole;
\r
667 WindowPlacement wpComment;
\r
668 WindowPlacement wpMoveHistory;
\r
669 WindowPlacement wpEvalGraph;
\r
670 WindowPlacement wpEngineOutput;
\r
671 WindowPlacement wpGameList;
\r
672 WindowPlacement wpTags;
\r
674 VOID EngineOptionsPopup(); // [HGM] settings
\r
676 VOID GothicPopUp(char *title, VariantClass variant);
\r
678 * Setting "frozen" should disable all user input other than deleting
\r
679 * the window. We do this while engines are initializing themselves.
\r
681 static int frozen = 0;
\r
682 static int oldMenuItemState[MENU_BAR_ITEMS];
\r
688 if (frozen) return;
\r
690 hmenu = GetMenu(hwndMain);
\r
691 for (i=0; i<MENU_BAR_ITEMS; i++) {
\r
692 oldMenuItemState[i] = EnableMenuItem(hmenu, i, MF_BYPOSITION|MF_GRAYED);
\r
694 DrawMenuBar(hwndMain);
\r
697 /* Undo a FreezeUI */
\r
703 if (!frozen) return;
\r
705 hmenu = GetMenu(hwndMain);
\r
706 for (i=0; i<MENU_BAR_ITEMS; i++) {
\r
707 EnableMenuItem(hmenu, i, MF_BYPOSITION|oldMenuItemState[i]);
\r
709 DrawMenuBar(hwndMain);
\r
712 /*static*/ int fromX = -1, fromY = -1, toX, toY; // [HGM] moved upstream, so JAWS can use them
\r
714 /* JAWS preparation patch (WinBoard for the sight impaired). Define required insertions as empty */
\r
720 #define JAWS_ALT_INTERCEPT
\r
721 #define JAWS_KB_NAVIGATION
\r
722 #define JAWS_MENU_ITEMS
\r
723 #define JAWS_SILENCE
\r
724 #define JAWS_REPLAY
\r
726 #define JAWS_COPYRIGHT
\r
727 #define JAWS_DELETE(X) X
\r
728 #define SAYMACHINEMOVE()
\r
732 /*---------------------------------------------------------------------------*\
\r
736 \*---------------------------------------------------------------------------*/
\r
739 WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
\r
740 LPSTR lpCmdLine, int nCmdShow)
\r
743 HANDLE hAccelMain, hAccelNoAlt, hAccelNoICS;
\r
744 // INITCOMMONCONTROLSEX ex;
\r
748 LoadLibrary("RICHED32.DLL");
\r
749 consoleCF.cbSize = sizeof(CHARFORMAT);
\r
751 if (!InitApplication(hInstance)) {
\r
754 if (!InitInstance(hInstance, nCmdShow, lpCmdLine)) {
\r
760 // InitCommonControlsEx(&ex);
\r
761 InitCommonControls();
\r
763 hAccelMain = LoadAccelerators (hInstance, szAppName);
\r
764 hAccelNoAlt = LoadAccelerators (hInstance, "NO_ALT");
\r
765 hAccelNoICS = LoadAccelerators( hInstance, "NO_ICS"); /* [AS] No Ctrl-V on ICS!!! */
\r
767 /* Acquire and dispatch messages until a WM_QUIT message is received. */
\r
769 while (GetMessage(&msg, /* message structure */
\r
770 NULL, /* handle of window receiving the message */
\r
771 0, /* lowest message to examine */
\r
772 0)) /* highest message to examine */
\r
775 if(msg.message == WM_CHAR && msg.wParam == '\t') {
\r
776 // [HGM] navigate: switch between all windows with tab
\r
777 HWND e1 = NULL, e2 = NULL, mh = NULL, hInput = NULL, hText = NULL;
\r
778 int i, currentElement = 0;
\r
780 // first determine what element of the chain we come from (if any)
\r
781 if(appData.icsActive) {
\r
782 hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
783 hText = GetDlgItem(hwndConsole, OPT_ConsoleText);
\r
785 if(engineOutputDialog && EngineOutputIsUp()) {
\r
786 e1 = GetDlgItem(engineOutputDialog, IDC_EngineMemo1);
\r
787 e2 = GetDlgItem(engineOutputDialog, IDC_EngineMemo2);
\r
789 if(moveHistoryDialog && MoveHistoryIsUp()) {
\r
790 mh = GetDlgItem(moveHistoryDialog, IDC_MoveHistory);
\r
792 if(msg.hwnd == hwndMain) currentElement = 7 ; else
\r
793 if(msg.hwnd == engineOutputDialog) currentElement = 2; else
\r
794 if(msg.hwnd == e1) currentElement = 2; else
\r
795 if(msg.hwnd == e2) currentElement = 3; else
\r
796 if(msg.hwnd == moveHistoryDialog) currentElement = 4; else
\r
797 if(msg.hwnd == mh) currentElement = 4; else
\r
798 if(msg.hwnd == evalGraphDialog) currentElement = 6; else
\r
799 if(msg.hwnd == hText) currentElement = 5; else
\r
800 if(msg.hwnd == hInput) currentElement = 6; else
\r
801 for (i = 0; i < N_BUTTONS; i++) {
\r
802 if (buttonDesc[i].hwnd == msg.hwnd) { currentElement = 1; break; }
\r
805 // determine where to go to
\r
806 if(currentElement) { HWND h = NULL; int direction = GetKeyState(VK_SHIFT) < 0 ? -1 : 1;
\r
808 currentElement = (currentElement + direction) % 7;
\r
809 switch(currentElement) {
\r
811 h = hwndMain; break; // passing this case always makes the loop exit
\r
813 h = buttonDesc[0].hwnd; break; // could be NULL
\r
815 if(!EngineOutputIsUp()) continue; // skip closed auxiliary windows
\r
818 if(!EngineOutputIsUp()) continue;
\r
821 if(!MoveHistoryIsUp()) continue;
\r
823 // case 6: // input to eval graph does not seem to get here!
\r
824 // if(!EvalGraphIsUp()) continue;
\r
825 // h = evalGraphDialog; break;
\r
827 if(!appData.icsActive) continue;
\r
831 if(!appData.icsActive) continue;
\r
837 if(currentElement > 4 && IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);
\r
838 if(currentElement < 5 && IsIconic(hwndMain)) ShowWindow(hwndMain, SW_RESTORE); // all open together
\r
841 continue; // this message now has been processed
\r
845 if (!(commentDialog && IsDialogMessage(commentDialog, &msg)) &&
\r
846 !(moveHistoryDialog && IsDialogMessage(moveHistoryDialog, &msg)) &&
\r
847 !(evalGraphDialog && IsDialogMessage(evalGraphDialog, &msg)) &&
\r
848 !(engineOutputDialog && IsDialogMessage(engineOutputDialog, &msg)) &&
\r
849 !(editTagsDialog && IsDialogMessage(editTagsDialog, &msg)) &&
\r
850 !(gameListDialog && IsDialogMessage(gameListDialog, &msg)) &&
\r
851 !(errorDialog && IsDialogMessage(errorDialog, &msg)) &&
\r
852 !(!frozen && TranslateAccelerator(hwndMain, hAccelMain, &msg)) && JAWS_ACCEL
\r
853 !(!hwndConsole && TranslateAccelerator(hwndMain, hAccelNoICS, &msg)) &&
\r
854 !(!hwndConsole && TranslateAccelerator(hwndMain, hAccelNoAlt, &msg))) {
\r
855 int done = 0, i; // [HGM] chat: dispatch cat-box messages
\r
856 for(i=0; i<MAX_CHAT; i++)
\r
857 if(chatHandle[i] && IsDialogMessage(chatHandle[i], &msg)) {
\r
860 if(done) continue; // [HGM] chat: end patch
\r
861 TranslateMessage(&msg); /* Translates virtual key codes */
\r
862 DispatchMessage(&msg); /* Dispatches message to window */
\r
867 return (msg.wParam); /* Returns the value from PostQuitMessage */
\r
870 /*---------------------------------------------------------------------------*\
\r
872 * Initialization functions
\r
874 \*---------------------------------------------------------------------------*/
\r
878 { // update user logo if necessary
\r
879 static char oldUserName[MSG_SIZ], *curName;
\r
881 if(appData.autoLogo) {
\r
882 curName = UserName();
\r
883 if(strcmp(curName, oldUserName)) {
\r
884 snprintf(oldUserName, MSG_SIZ, "logos\\%s.bmp", curName);
\r
885 userLogo = LoadImage( 0, oldUserName, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );
\r
886 safeStrCpy(oldUserName, curName, sizeof(oldUserName)/sizeof(oldUserName[0]) );
\r
892 InitApplication(HINSTANCE hInstance)
\r
896 /* Fill in window class structure with parameters that describe the */
\r
899 wc.style = CS_HREDRAW | CS_VREDRAW; /* Class style(s). */
\r
900 wc.lpfnWndProc = (WNDPROC)WndProc; /* Window Procedure */
\r
901 wc.cbClsExtra = 0; /* No per-class extra data. */
\r
902 wc.cbWndExtra = 0; /* No per-window extra data. */
\r
903 wc.hInstance = hInstance; /* Owner of this class */
\r
904 wc.hIcon = LoadIcon(hInstance, "icon_white");
\r
905 wc.hCursor = LoadCursor(NULL, IDC_ARROW); /* Cursor */
\r
906 wc.hbrBackground = (HBRUSH)(COLOR_WINDOW+1); /* Default color */
\r
907 wc.lpszMenuName = szAppName; /* Menu name from .RC */
\r
908 wc.lpszClassName = szAppName; /* Name to register as */
\r
910 /* Register the window class and return success/failure code. */
\r
911 if (!RegisterClass(&wc)) return FALSE;
\r
913 wc.style = CS_HREDRAW | CS_VREDRAW;
\r
914 wc.lpfnWndProc = (WNDPROC)ConsoleWndProc;
\r
916 wc.cbWndExtra = DLGWINDOWEXTRA;
\r
917 wc.hInstance = hInstance;
\r
918 wc.hIcon = LoadIcon(hInstance, "icon_white");
\r
919 wc.hCursor = LoadCursor(NULL, IDC_ARROW);
\r
920 wc.hbrBackground = (HBRUSH)(COLOR_MENU+1);
\r
921 wc.lpszMenuName = NULL;
\r
922 wc.lpszClassName = szConsoleName;
\r
924 if (!RegisterClass(&wc)) return FALSE;
\r
929 /* Set by InitInstance, used by EnsureOnScreen */
\r
930 int screenHeight, screenWidth;
\r
933 EnsureOnScreen(int *x, int *y, int minX, int minY)
\r
935 // int gap = GetSystemMetrics(SM_CYFRAME) + GetSystemMetrics(SM_CYCAPTION);
\r
936 /* Be sure window at (x,y) is not off screen (or even mostly off screen) */
\r
937 if (*x > screenWidth - 32) *x = 0;
\r
938 if (*y > screenHeight - 32) *y = 0;
\r
939 if (*x < minX) *x = minX;
\r
940 if (*y < minY) *y = minY;
\r
944 InitInstance(HINSTANCE hInstance, int nCmdShow, LPSTR lpCmdLine)
\r
946 HWND hwnd; /* Main window handle. */
\r
948 WINDOWPLACEMENT wp;
\r
951 hInst = hInstance; /* Store instance handle in our global variable */
\r
952 programName = szAppName;
\r
954 if (SearchPath(NULL, "WinBoard.exe", NULL, MSG_SIZ, installDir, &filepart)) {
\r
955 *filepart = NULLCHAR;
\r
957 GetCurrentDirectory(MSG_SIZ, installDir);
\r
959 gameInfo.boardWidth = gameInfo.boardHeight = 8; // [HGM] won't have open window otherwise
\r
960 screenWidth = screenHeight = 1000; // [HGM] placement: kludge to allow calling EnsureOnScreen from InitAppData
\r
961 InitAppData(lpCmdLine); /* Get run-time parameters */
\r
962 /* xboard, and older WinBoards, controlled the move sound with the
\r
963 appData.ringBellAfterMoves option. In the current WinBoard, we
\r
964 always turn the option on (so that the backend will call us),
\r
965 then let the user turn the sound off by setting it to silence if
\r
966 desired. To accommodate old winboard.ini files saved by old
\r
967 versions of WinBoard, we also turn off the sound if the option
\r
968 was initially set to false. [HGM] taken out of InitAppData */
\r
969 if (!appData.ringBellAfterMoves) {
\r
970 sounds[(int)SoundMove].name = strdup("");
\r
971 appData.ringBellAfterMoves = TRUE;
\r
973 if (appData.debugMode) {
\r
974 debugFP = fopen(appData.nameOfDebugFile, "w");
\r
975 setbuf(debugFP, NULL);
\r
978 LoadLanguageFile(appData.language);
\r
982 // InitEngineUCI( installDir, &first ); // [HGM] incorporated in InitBackEnd1()
\r
983 // InitEngineUCI( installDir, &second );
\r
985 /* Create a main window for this application instance. */
\r
986 hwnd = CreateWindow(szAppName, szTitle,
\r
987 (WS_OVERLAPPEDWINDOW & ~WS_MAXIMIZEBOX),
\r
988 CW_USEDEFAULT, 0, CW_USEDEFAULT, 0,
\r
989 NULL, NULL, hInstance, NULL);
\r
992 /* If window could not be created, return "failure" */
\r
997 /* [HGM] logo: Load logos if specified (must be done before InitDrawingSizes) */
\r
998 if( appData.firstLogo && appData.firstLogo[0] != NULLCHAR) {
\r
999 first.programLogo = LoadImage( 0, appData.firstLogo, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );
\r
1001 if (first.programLogo == NULL && appData.debugMode) {
\r
1002 fprintf( debugFP, "Unable to load logo bitmap '%s'\n", appData.firstLogo );
\r
1004 } else if(appData.autoLogo) {
\r
1005 if(appData.firstDirectory && appData.firstDirectory[0]) {
\r
1006 char buf[MSG_SIZ];
\r
1007 snprintf(buf, MSG_SIZ, "%s/logo.bmp", appData.firstDirectory);
\r
1008 first.programLogo = LoadImage( 0, buf, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );
\r
1012 if( appData.secondLogo && appData.secondLogo[0] != NULLCHAR) {
\r
1013 second.programLogo = LoadImage( 0, appData.secondLogo, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );
\r
1015 if (second.programLogo == NULL && appData.debugMode) {
\r
1016 fprintf( debugFP, "Unable to load logo bitmap '%s'\n", appData.secondLogo );
\r
1018 } else if(appData.autoLogo) {
\r
1019 char buf[MSG_SIZ];
\r
1020 if(appData.icsActive) { // [HGM] logo: in ICS mode second can be used for ICS
\r
1021 snprintf(buf, MSG_SIZ, "logos\\%s.bmp", appData.icsHost);
\r
1022 second.programLogo = LoadImage( 0, buf, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );
\r
1024 if(appData.secondDirectory && appData.secondDirectory[0]) {
\r
1025 snprintf(buf, MSG_SIZ, "%s\\logo.bmp", appData.secondDirectory);
\r
1026 second.programLogo = LoadImage( 0, buf, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );
\r
1032 iconWhite = LoadIcon(hInstance, "icon_white");
\r
1033 iconBlack = LoadIcon(hInstance, "icon_black");
\r
1034 iconCurrent = iconWhite;
\r
1035 InitDrawingColors();
\r
1036 screenHeight = GetSystemMetrics(SM_CYSCREEN);
\r
1037 screenWidth = GetSystemMetrics(SM_CXSCREEN);
\r
1038 for (ibs = (int) NUM_SIZES - 1; ibs >= 0; ibs--) {
\r
1039 /* Compute window size for each board size, and use the largest
\r
1040 size that fits on this screen as the default. */
\r
1041 InitDrawingSizes((BoardSize)(ibs+1000), 0);
\r
1042 if (boardSize == (BoardSize)-1 &&
\r
1043 winH <= screenHeight
\r
1044 - GetSystemMetrics(SM_CYFRAME) - GetSystemMetrics(SM_CYCAPTION) - 10
\r
1045 && winW <= screenWidth) {
\r
1046 boardSize = (BoardSize)ibs;
\r
1050 InitDrawingSizes(boardSize, 0);
\r
1051 TranslateMenus(1);
\r
1053 buttonCount = GetSystemMetrics(SM_CMOUSEBUTTONS);
\r
1055 /* [AS] Load textures if specified */
\r
1056 ZeroMemory( &backTextureSquareInfo, sizeof(backTextureSquareInfo) );
\r
1058 if( appData.liteBackTextureFile && appData.liteBackTextureFile[0] != NULLCHAR && appData.liteBackTextureFile[0] != '*' ) {
\r
1059 liteBackTexture = LoadImage( 0, appData.liteBackTextureFile, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );
\r
1060 liteBackTextureMode = appData.liteBackTextureMode;
\r
1062 if (liteBackTexture == NULL && appData.debugMode) {
\r
1063 fprintf( debugFP, "Unable to load lite texture bitmap '%s'\n", appData.liteBackTextureFile );
\r
1067 if( appData.darkBackTextureFile && appData.darkBackTextureFile[0] != NULLCHAR && appData.darkBackTextureFile[0] != '*' ) {
\r
1068 darkBackTexture = LoadImage( 0, appData.darkBackTextureFile, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );
\r
1069 darkBackTextureMode = appData.darkBackTextureMode;
\r
1071 if (darkBackTexture == NULL && appData.debugMode) {
\r
1072 fprintf( debugFP, "Unable to load dark texture bitmap '%s'\n", appData.darkBackTextureFile );
\r
1076 mysrandom( (unsigned) time(NULL) );
\r
1078 /* [AS] Restore layout */
\r
1079 if( wpMoveHistory.visible ) {
\r
1080 MoveHistoryPopUp();
\r
1083 if( wpEvalGraph.visible ) {
\r
1087 if( wpEngineOutput.visible ) {
\r
1088 EngineOutputPopUp();
\r
1091 /* Make the window visible; update its client area; and return "success" */
\r
1092 EnsureOnScreen(&wpMain.x, &wpMain.y, minX, minY);
\r
1093 wp.length = sizeof(WINDOWPLACEMENT);
\r
1095 wp.showCmd = nCmdShow;
\r
1096 wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;
\r
1097 wp.rcNormalPosition.left = wpMain.x;
\r
1098 wp.rcNormalPosition.right = wpMain.x + wpMain.width;
\r
1099 wp.rcNormalPosition.top = wpMain.y;
\r
1100 wp.rcNormalPosition.bottom = wpMain.y + wpMain.height;
\r
1101 SetWindowPlacement(hwndMain, &wp);
\r
1103 InitBackEnd2(); // [HGM] moved until after all windows placed, to save correct position if fatal error on engine start
\r
1105 if(!appData.noGUI) SetWindowPos(hwndMain, alwaysOnTop ? HWND_TOPMOST : HWND_NOTOPMOST,
\r
1106 0, 0, 0, 0, SWP_NOMOVE|SWP_NOSIZE);
\r
1108 if (hwndConsole) {
\r
1110 SetWindowPos(hwndConsole, alwaysOnTop ? HWND_TOPMOST : HWND_NOTOPMOST,
\r
1111 0, 0, 0, 0, SWP_NOMOVE|SWP_NOSIZE);
\r
1113 ShowWindow(hwndConsole, nCmdShow);
\r
1114 if(appData.chatBoxes) { // [HGM] chat: open chat boxes
\r
1115 char buf[MSG_SIZ], *p = buf, *q;
\r
1116 safeStrCpy(buf, appData.chatBoxes, sizeof(buf)/sizeof(buf[0]) );
\r
1118 q = strchr(p, ';');
\r
1120 if(*p) ChatPopUp(p);
\r
1123 SetActiveWindow(hwndConsole);
\r
1125 if(!appData.noGUI) UpdateWindow(hwnd); else ShowWindow(hwnd, SW_MINIMIZE);
\r
1126 if(gameListDialog) SetFocus(gameListDialog); // [HGM] jaws: for if we clicked multi-game game file
\r
1135 HMENU hmenu = GetMenu(hwndMain);
\r
1137 (void) EnableMenuItem(hmenu, IDM_CommPort,
\r
1138 MF_BYCOMMAND|((appData.icsActive &&
\r
1139 *appData.icsCommPort != NULLCHAR) ?
\r
1140 MF_ENABLED : MF_GRAYED));
\r
1141 (void) CheckMenuItem(hmenu, IDM_SaveSettingsOnExit,
\r
1142 MF_BYCOMMAND|(saveSettingsOnExit ?
\r
1143 MF_CHECKED : MF_UNCHECKED));
\r
1146 //---------------------------------------------------------------------------------------------------------
\r
1148 #define ICS_TEXT_MENU_SIZE (IDM_CommandXLast - IDM_CommandX + 1)
\r
1149 #define XBOARD FALSE
\r
1151 #define OPTCHAR "/"
\r
1152 #define SEPCHAR "="
\r
1156 // front-end part of option handling
\r
1159 LFfromMFP(LOGFONT* lf, MyFontParams *mfp)
\r
1161 HDC hdc = CreateDC("DISPLAY", NULL, NULL, NULL);
\r
1162 lf->lfHeight = -(int)(mfp->pointSize * GetDeviceCaps(hdc, LOGPIXELSY) / 72.0 + 0.5);
\r
1165 lf->lfEscapement = 0;
\r
1166 lf->lfOrientation = 0;
\r
1167 lf->lfWeight = mfp->bold ? FW_BOLD : FW_NORMAL;
\r
1168 lf->lfItalic = mfp->italic;
\r
1169 lf->lfUnderline = mfp->underline;
\r
1170 lf->lfStrikeOut = mfp->strikeout;
\r
1171 lf->lfCharSet = mfp->charset;
\r
1172 lf->lfOutPrecision = OUT_DEFAULT_PRECIS;
\r
1173 lf->lfClipPrecision = CLIP_DEFAULT_PRECIS;
\r
1174 lf->lfQuality = DEFAULT_QUALITY;
\r
1175 lf->lfPitchAndFamily = DEFAULT_PITCH|FF_DONTCARE;
\r
1176 safeStrCpy(lf->lfFaceName, mfp->faceName, sizeof(lf->lfFaceName)/sizeof(lf->lfFaceName[0]) );
\r
1180 CreateFontInMF(MyFont *mf)
\r
1182 LFfromMFP(&mf->lf, &mf->mfp);
\r
1183 if (mf->hf) DeleteObject(mf->hf);
\r
1184 mf->hf = CreateFontIndirect(&mf->lf);
\r
1187 // [HGM] This platform-dependent table provides the location for storing the color info
\r
1189 colorVariable[] = {
\r
1190 &whitePieceColor,
\r
1191 &blackPieceColor,
\r
1192 &lightSquareColor,
\r
1193 &darkSquareColor,
\r
1194 &highlightSquareColor,
\r
1195 &premoveHighlightColor,
\r
1197 &consoleBackgroundColor,
\r
1198 &appData.fontForeColorWhite,
\r
1199 &appData.fontBackColorWhite,
\r
1200 &appData.fontForeColorBlack,
\r
1201 &appData.fontBackColorBlack,
\r
1202 &appData.evalHistColorWhite,
\r
1203 &appData.evalHistColorBlack,
\r
1204 &appData.highlightArrowColor,
\r
1207 /* Command line font name parser. NULL name means do nothing.
\r
1208 Syntax like "Courier New:10.0 bi" or "Arial:10" or "Arial:10b"
\r
1209 For backward compatibility, syntax without the colon is also
\r
1210 accepted, but font names with digits in them won't work in that case.
\r
1213 ParseFontName(char *name, MyFontParams *mfp)
\r
1216 if (name == NULL) return;
\r
1218 q = strchr(p, ':');
\r
1220 if (q - p >= sizeof(mfp->faceName))
\r
1221 ExitArgError(_("Font name too long:"), name);
\r
1222 memcpy(mfp->faceName, p, q - p);
\r
1223 mfp->faceName[q - p] = NULLCHAR;
\r
1226 q = mfp->faceName;
\r
1227 while (*p && !isdigit(*p)) {
\r
1229 if (q - mfp->faceName >= sizeof(mfp->faceName))
\r
1230 ExitArgError(_("Font name too long:"), name);
\r
1232 while (q > mfp->faceName && q[-1] == ' ') q--;
\r
1235 if (!*p) ExitArgError(_("Font point size missing:"), name);
\r
1236 mfp->pointSize = (float) atof(p);
\r
1237 mfp->bold = (strchr(p, 'b') != NULL);
\r
1238 mfp->italic = (strchr(p, 'i') != NULL);
\r
1239 mfp->underline = (strchr(p, 'u') != NULL);
\r
1240 mfp->strikeout = (strchr(p, 's') != NULL);
\r
1241 mfp->charset = DEFAULT_CHARSET;
\r
1242 q = strchr(p, 'c');
\r
1244 mfp->charset = (BYTE) atoi(q+1);
\r
1248 ParseFont(char *name, int number)
\r
1249 { // wrapper to shield back-end from 'font'
\r
1250 ParseFontName(name, &font[boardSize][number]->mfp);
\r
1255 { // in WB we have a 2D array of fonts; this initializes their description
\r
1257 /* Point font array elements to structures and
\r
1258 parse default font names */
\r
1259 for (i=0; i<NUM_FONTS; i++) {
\r
1260 for (j=0; j<NUM_SIZES; j++) {
\r
1261 font[j][i] = &fontRec[j][i];
\r
1262 ParseFontName(font[j][i]->def, &font[j][i]->mfp);
\r
1269 { // here we create the actual fonts from the selected descriptions
\r
1271 for (i=0; i<NUM_FONTS; i++) {
\r
1272 for (j=0; j<NUM_SIZES; j++) {
\r
1273 CreateFontInMF(font[j][i]);
\r
1277 /* Color name parser.
\r
1278 X version accepts X color names, but this one
\r
1279 handles only the #rrggbb form (hex) or rrr,ggg,bbb (decimal) */
\r
1281 ParseColorName(char *name)
\r
1283 int red, green, blue, count;
\r
1284 char buf[MSG_SIZ];
\r
1286 count = sscanf(name, "#%2x%2x%2x", &red, &green, &blue);
\r
1288 count = sscanf(name, "%3d%*[^0-9]%3d%*[^0-9]%3d",
\r
1289 &red, &green, &blue);
\r
1292 snprintf(buf, MSG_SIZ, _("Can't parse color name %s"), name);
\r
1293 DisplayError(buf, 0);
\r
1294 return RGB(0, 0, 0);
\r
1296 return PALETTERGB(red, green, blue);
\r
1300 ParseColor(int n, char *name)
\r
1301 { // for WinBoard the color is an int, which needs to be derived from the string
\r
1302 if(colorVariable[n]) *(int*)colorVariable[n] = ParseColorName(name);
\r
1306 ParseAttribs(COLORREF *color, int *effects, char* argValue)
\r
1308 char *e = argValue;
\r
1312 if (*e == 'b') eff |= CFE_BOLD;
\r
1313 else if (*e == 'i') eff |= CFE_ITALIC;
\r
1314 else if (*e == 'u') eff |= CFE_UNDERLINE;
\r
1315 else if (*e == 's') eff |= CFE_STRIKEOUT;
\r
1316 else if (*e == '#' || isdigit(*e)) break;
\r
1320 *color = ParseColorName(e);
\r
1324 ParseTextAttribs(ColorClass cc, char *s)
\r
1325 { // [HGM] front-end wrapper that does the platform-dependent call
\r
1326 // for XBoard we would set (&appData.colorShout)[cc] = strdup(s);
\r
1327 ParseAttribs(&textAttribs[cc].color, &textAttribs[cc].effects, s);
\r
1331 ParseBoardSize(void *addr, char *name)
\r
1332 { // [HGM] rewritten with return-value ptr to shield back-end from BoardSize
\r
1333 BoardSize bs = SizeTiny;
\r
1334 while (sizeInfo[bs].name != NULL) {
\r
1335 if (StrCaseCmp(name, sizeInfo[bs].name) == 0) {
\r
1336 *(BoardSize *)addr = bs;
\r
1341 ExitArgError(_("Unrecognized board size value"), name);
\r
1346 { // [HGM] import name from appData first
\r
1349 for (cc = (ColorClass)0; cc < ColorNormal; cc++) {
\r
1350 textAttribs[cc].sound.name = strdup((&appData.soundShout)[cc]);
\r
1351 textAttribs[cc].sound.data = NULL;
\r
1352 MyLoadSound(&textAttribs[cc].sound);
\r
1354 for (cc = ColorNormal; cc < NColorClasses; cc++) {
\r
1355 textAttribs[cc].sound.name = strdup("");
\r
1356 textAttribs[cc].sound.data = NULL;
\r
1358 for (sc = (SoundClass)0; sc < NSoundClasses; sc++) {
\r
1359 sounds[sc].name = strdup((&appData.soundMove)[sc]);
\r
1360 sounds[sc].data = NULL;
\r
1361 MyLoadSound(&sounds[sc]);
\r
1366 SetCommPortDefaults()
\r
1368 memset(&dcb, 0, sizeof(DCB)); // required by VS 2002 +
\r
1369 dcb.DCBlength = sizeof(DCB);
\r
1370 dcb.BaudRate = 9600;
\r
1371 dcb.fBinary = TRUE;
\r
1372 dcb.fParity = FALSE;
\r
1373 dcb.fOutxCtsFlow = FALSE;
\r
1374 dcb.fOutxDsrFlow = FALSE;
\r
1375 dcb.fDtrControl = DTR_CONTROL_ENABLE;
\r
1376 dcb.fDsrSensitivity = FALSE;
\r
1377 dcb.fTXContinueOnXoff = TRUE;
\r
1378 dcb.fOutX = FALSE;
\r
1380 dcb.fNull = FALSE;
\r
1381 dcb.fRtsControl = RTS_CONTROL_ENABLE;
\r
1382 dcb.fAbortOnError = FALSE;
\r
1384 dcb.Parity = SPACEPARITY;
\r
1385 dcb.StopBits = ONESTOPBIT;
\r
1388 // [HGM] args: these three cases taken out to stay in front-end
\r
1390 SaveFontArg(FILE *f, ArgDescriptor *ad)
\r
1391 { // in WinBoard every board size has its own font, and the "argLoc" identifies the table,
\r
1392 // while the curent board size determines the element. This system should be ported to XBoard.
\r
1393 // What the table contains pointers to, and how to print the font description, remains platform-dependent
\r
1395 for (bs=0; bs<NUM_SIZES; bs++) {
\r
1396 MyFontParams *mfp = &font[bs][(int) ad->argLoc]->mfp;
\r
1397 fprintf(f, "/size=%s ", sizeInfo[bs].name);
\r
1398 fprintf(f, "/%s=\"%s:%g%s%s%s%s%sc%d\"\n",
\r
1399 ad->argName, mfp->faceName, mfp->pointSize,
\r
1400 mfp->bold || mfp->italic || mfp->underline || mfp->strikeout ? " " : "",
\r
1401 mfp->bold ? "b" : "",
\r
1402 mfp->italic ? "i" : "",
\r
1403 mfp->underline ? "u" : "",
\r
1404 mfp->strikeout ? "s" : "",
\r
1405 (int)mfp->charset);
\r
1411 { // [HGM] copy the names from the internal WB variables to appData
\r
1414 for (cc = (ColorClass)0; cc < ColorNormal; cc++)
\r
1415 (&appData.soundShout)[cc] = textAttribs[cc].sound.name;
\r
1416 for (sc = (SoundClass)0; sc < NSoundClasses; sc++)
\r
1417 (&appData.soundMove)[sc] = sounds[sc].name;
\r
1421 SaveAttribsArg(FILE *f, ArgDescriptor *ad)
\r
1422 { // here the "argLoc" defines a table index. It could have contained the 'ta' pointer itself, though
\r
1423 MyTextAttribs* ta = &textAttribs[(ColorClass)ad->argLoc];
\r
1424 fprintf(f, "/%s=\"%s%s%s%s%s#%02lx%02lx%02lx\"\n", ad->argName,
\r
1425 (ta->effects & CFE_BOLD) ? "b" : "",
\r
1426 (ta->effects & CFE_ITALIC) ? "i" : "",
\r
1427 (ta->effects & CFE_UNDERLINE) ? "u" : "",
\r
1428 (ta->effects & CFE_STRIKEOUT) ? "s" : "",
\r
1429 (ta->effects) ? " " : "",
\r
1430 ta->color&0xff, (ta->color >> 8)&0xff, (ta->color >> 16)&0xff);
\r
1434 SaveColor(FILE *f, ArgDescriptor *ad)
\r
1435 { // in WinBoard the color is an int and has to be converted to text. In X it would be a string already?
\r
1436 COLORREF color = *(COLORREF *)colorVariable[(int)ad->argLoc];
\r
1437 fprintf(f, "/%s=#%02lx%02lx%02lx\n", ad->argName,
\r
1438 color&0xff, (color>>8)&0xff, (color>>16)&0xff);
\r
1442 SaveBoardSize(FILE *f, char *name, void *addr)
\r
1443 { // wrapper to shield back-end from BoardSize & sizeInfo
\r
1444 fprintf(f, "/%s=%s\n", name, sizeInfo[*(BoardSize *)addr].name);
\r
1448 ParseCommPortSettings(char *s)
\r
1449 { // wrapper to keep dcb from back-end
\r
1450 ParseCommSettings(s, &dcb);
\r
1455 { // wrapper to shield use of window handles from back-end (make addressible by number?)
\r
1456 GetActualPlacement(hwndMain, &wpMain);
\r
1457 GetActualPlacement(hwndConsole, &wpConsole);
\r
1458 GetActualPlacement(commentDialog, &wpComment);
\r
1459 GetActualPlacement(editTagsDialog, &wpTags);
\r
1460 GetActualPlacement(gameListDialog, &wpGameList);
\r
1461 GetActualPlacement(moveHistoryDialog, &wpMoveHistory);
\r
1462 GetActualPlacement(evalGraphDialog, &wpEvalGraph);
\r
1463 GetActualPlacement(engineOutputDialog, &wpEngineOutput);
\r
1467 PrintCommPortSettings(FILE *f, char *name)
\r
1468 { // wrapper to shield back-end from DCB
\r
1469 PrintCommSettings(f, name, &dcb);
\r
1473 MySearchPath(char *installDir, char *name, char *fullname)
\r
1475 char *dummy, buf[MSG_SIZ], *p = name, *q;
\r
1476 if(name[0]== '%') {
\r
1477 fullname[0] = 0; // [HGM] first expand any environment variables in the given name
\r
1478 while(*p == '%' && (q = strchr(p+1, '%'))) { // [HGM] recognize %*% as environment variable
\r
1479 safeStrCpy(buf, p+1, sizeof(buf)/sizeof(buf[0]) );
\r
1480 *strchr(buf, '%') = 0;
\r
1481 strcat(fullname, getenv(buf));
\r
1482 p = q+1; while(*p == '\\') { strcat(fullname, "\\"); p++; }
\r
1484 strcat(fullname, p); // after environment variables (if any), take the remainder of the given name
\r
1485 if(appData.debugMode) fprintf(debugFP, "name = '%s', expanded name = '%s'\n", name, fullname);
\r
1486 return (int) strlen(fullname);
\r
1488 return (int) SearchPath(installDir, name, NULL, MSG_SIZ, fullname, &dummy);
\r
1492 MyGetFullPathName(char *name, char *fullname)
\r
1495 return (int) GetFullPathName(name, MSG_SIZ, fullname, &dummy);
\r
1500 { // [HGM] args: allows testing if main window is realized from back-end
\r
1501 return hwndMain != NULL;
\r
1505 PopUpStartupDialog()
\r
1509 LoadLanguageFile(appData.language);
\r
1510 lpProc = MakeProcInstance((FARPROC)StartupDialog, hInst);
\r
1511 DialogBox(hInst, MAKEINTRESOURCE(DLG_Startup), NULL, (DLGPROC)lpProc);
\r
1512 FreeProcInstance(lpProc);
\r
1515 /*---------------------------------------------------------------------------*\
\r
1517 * GDI board drawing routines
\r
1519 \*---------------------------------------------------------------------------*/
\r
1521 /* [AS] Draw square using background texture */
\r
1522 static void DrawTile( int dx, int dy, int dw, int dh, HDC dst, HDC src, int mode, int sx, int sy )
\r
1527 return; /* Should never happen! */
\r
1530 SetGraphicsMode( dst, GM_ADVANCED );
\r
1537 /* X reflection */
\r
1542 x.eDx = (FLOAT) dw + dx - 1;
\r
1545 SetWorldTransform( dst, &x );
\r
1548 /* Y reflection */
\r
1554 x.eDy = (FLOAT) dh + dy - 1;
\r
1556 SetWorldTransform( dst, &x );
\r
1564 x.eDx = (FLOAT) dx;
\r
1565 x.eDy = (FLOAT) dy;
\r
1568 SetWorldTransform( dst, &x );
\r
1572 BitBlt( dst, dx, dy, dw, dh, src, sx, sy, SRCCOPY );
\r
1580 SetWorldTransform( dst, &x );
\r
1582 ModifyWorldTransform( dst, 0, MWT_IDENTITY );
\r
1585 /* [AS] [HGM] Make room for more piece types, so all pieces can be different */
\r
1587 PM_WP = (int) WhitePawn,
\r
1588 PM_WN = (int) WhiteKnight,
\r
1589 PM_WB = (int) WhiteBishop,
\r
1590 PM_WR = (int) WhiteRook,
\r
1591 PM_WQ = (int) WhiteQueen,
\r
1592 PM_WF = (int) WhiteFerz,
\r
1593 PM_WW = (int) WhiteWazir,
\r
1594 PM_WE = (int) WhiteAlfil,
\r
1595 PM_WM = (int) WhiteMan,
\r
1596 PM_WO = (int) WhiteCannon,
\r
1597 PM_WU = (int) WhiteUnicorn,
\r
1598 PM_WH = (int) WhiteNightrider,
\r
1599 PM_WA = (int) WhiteAngel,
\r
1600 PM_WC = (int) WhiteMarshall,
\r
1601 PM_WAB = (int) WhiteCardinal,
\r
1602 PM_WD = (int) WhiteDragon,
\r
1603 PM_WL = (int) WhiteLance,
\r
1604 PM_WS = (int) WhiteCobra,
\r
1605 PM_WV = (int) WhiteFalcon,
\r
1606 PM_WSG = (int) WhiteSilver,
\r
1607 PM_WG = (int) WhiteGrasshopper,
\r
1608 PM_WK = (int) WhiteKing,
\r
1609 PM_BP = (int) BlackPawn,
\r
1610 PM_BN = (int) BlackKnight,
\r
1611 PM_BB = (int) BlackBishop,
\r
1612 PM_BR = (int) BlackRook,
\r
1613 PM_BQ = (int) BlackQueen,
\r
1614 PM_BF = (int) BlackFerz,
\r
1615 PM_BW = (int) BlackWazir,
\r
1616 PM_BE = (int) BlackAlfil,
\r
1617 PM_BM = (int) BlackMan,
\r
1618 PM_BO = (int) BlackCannon,
\r
1619 PM_BU = (int) BlackUnicorn,
\r
1620 PM_BH = (int) BlackNightrider,
\r
1621 PM_BA = (int) BlackAngel,
\r
1622 PM_BC = (int) BlackMarshall,
\r
1623 PM_BG = (int) BlackGrasshopper,
\r
1624 PM_BAB = (int) BlackCardinal,
\r
1625 PM_BD = (int) BlackDragon,
\r
1626 PM_BL = (int) BlackLance,
\r
1627 PM_BS = (int) BlackCobra,
\r
1628 PM_BV = (int) BlackFalcon,
\r
1629 PM_BSG = (int) BlackSilver,
\r
1630 PM_BK = (int) BlackKing
\r
1633 static HFONT hPieceFont = NULL;
\r
1634 static HBITMAP hPieceMask[(int) EmptySquare];
\r
1635 static HBITMAP hPieceFace[(int) EmptySquare];
\r
1636 static int fontBitmapSquareSize = 0;
\r
1637 static char pieceToFontChar[(int) EmptySquare] =
\r
1638 { 'p', 'n', 'b', 'r', 'q',
\r
1639 'n', 'b', 'p', 'n', 'b', 'r', 'b', 'r', 'q', 'k',
\r
1640 'k', 'o', 'm', 'v', 't', 'w',
\r
1641 'v', 't', 'o', 'm', 'v', 't', 'v', 't', 'w', 'l',
\r
1644 extern BOOL SetCharTable( char *table, const char * map );
\r
1645 /* [HGM] moved to backend.c */
\r
1647 static void SetPieceBackground( HDC hdc, COLORREF color, int mode )
\r
1650 BYTE r1 = GetRValue( color );
\r
1651 BYTE g1 = GetGValue( color );
\r
1652 BYTE b1 = GetBValue( color );
\r
1658 /* Create a uniform background first */
\r
1659 hbrush = CreateSolidBrush( color );
\r
1660 SetRect( &rc, 0, 0, squareSize, squareSize );
\r
1661 FillRect( hdc, &rc, hbrush );
\r
1662 DeleteObject( hbrush );
\r
1665 /* Vertical gradient, good for pawn, knight and rook, less for queen and king */
\r
1666 int steps = squareSize / 2;
\r
1669 for( i=0; i<steps; i++ ) {
\r
1670 BYTE r = r1 - (r1-r2) * i / steps;
\r
1671 BYTE g = g1 - (g1-g2) * i / steps;
\r
1672 BYTE b = b1 - (b1-b2) * i / steps;
\r
1674 hbrush = CreateSolidBrush( RGB(r,g,b) );
\r
1675 SetRect( &rc, i + squareSize - steps, 0, i + squareSize - steps + 1, squareSize );
\r
1676 FillRect( hdc, &rc, hbrush );
\r
1677 DeleteObject(hbrush);
\r
1680 else if( mode == 2 ) {
\r
1681 /* Diagonal gradient, good more or less for every piece */
\r
1682 POINT triangle[3];
\r
1683 HPEN hpen = SelectObject( hdc, GetStockObject(NULL_PEN) );
\r
1684 HBRUSH hbrush_old;
\r
1685 int steps = squareSize;
\r
1688 triangle[0].x = squareSize - steps;
\r
1689 triangle[0].y = squareSize;
\r
1690 triangle[1].x = squareSize;
\r
1691 triangle[1].y = squareSize;
\r
1692 triangle[2].x = squareSize;
\r
1693 triangle[2].y = squareSize - steps;
\r
1695 for( i=0; i<steps; i++ ) {
\r
1696 BYTE r = r1 - (r1-r2) * i / steps;
\r
1697 BYTE g = g1 - (g1-g2) * i / steps;
\r
1698 BYTE b = b1 - (b1-b2) * i / steps;
\r
1700 hbrush = CreateSolidBrush( RGB(r,g,b) );
\r
1701 hbrush_old = SelectObject( hdc, hbrush );
\r
1702 Polygon( hdc, triangle, 3 );
\r
1703 SelectObject( hdc, hbrush_old );
\r
1704 DeleteObject(hbrush);
\r
1709 SelectObject( hdc, hpen );
\r
1714 [AS] The method I use to create the bitmaps it a bit tricky, but it
\r
1715 seems to work ok. The main problem here is to find the "inside" of a chess
\r
1716 piece: follow the steps as explained below.
\r
1718 static void CreatePieceMaskFromFont( HDC hdc_window, HDC hdc, int index )
\r
1722 COLORREF chroma = RGB(0xFF,0x00,0xFF);
\r
1726 int backColor = whitePieceColor;
\r
1727 int foreColor = blackPieceColor;
\r
1729 if( index < (int)BlackPawn && appData.fontBackColorWhite != appData.fontForeColorWhite ) {
\r
1730 backColor = appData.fontBackColorWhite;
\r
1731 foreColor = appData.fontForeColorWhite;
\r
1733 else if( index >= (int)BlackPawn && appData.fontBackColorBlack != appData.fontForeColorBlack ) {
\r
1734 backColor = appData.fontBackColorBlack;
\r
1735 foreColor = appData.fontForeColorBlack;
\r
1739 hbm = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );
\r
1741 hbm_old = SelectObject( hdc, hbm );
\r
1745 rc.right = squareSize;
\r
1746 rc.bottom = squareSize;
\r
1748 /* Step 1: background is now black */
\r
1749 FillRect( hdc, &rc, GetStockObject(BLACK_BRUSH) );
\r
1751 GetTextExtentPoint32( hdc, &pieceToFontChar[index], 1, &sz );
\r
1753 pt.x = (squareSize - sz.cx) / 2;
\r
1754 pt.y = (squareSize - sz.cy) / 2;
\r
1756 SetBkMode( hdc, TRANSPARENT );
\r
1757 SetTextColor( hdc, chroma );
\r
1758 /* Step 2: the piece has been drawn in purple, there are now black and purple in this bitmap */
\r
1759 TextOut( hdc, pt.x, pt.y, &pieceToFontChar[appData.allWhite && index >= (int)BlackPawn ? index - (int)BlackPawn : index], 1 );
\r
1761 SelectObject( hdc, GetStockObject(WHITE_BRUSH) );
\r
1762 /* Step 3: the area outside the piece is filled with white */
\r
1763 // FloodFill( hdc, 0, 0, chroma );
\r
1764 ExtFloodFill( hdc, 0, 0, 0, FLOODFILLSURFACE );
\r
1765 ExtFloodFill( hdc, 0, squareSize-1, 0, FLOODFILLSURFACE ); // [HGM] fill from all 4 corners, for if piece too big
\r
1766 ExtFloodFill( hdc, squareSize-1, 0, 0, FLOODFILLSURFACE );
\r
1767 ExtFloodFill( hdc, squareSize-1, squareSize-1, 0, FLOODFILLSURFACE );
\r
1768 SelectObject( hdc, GetStockObject(BLACK_BRUSH) );
\r
1770 Step 4: this is the tricky part, the area inside the piece is filled with black,
\r
1771 but if the start point is not inside the piece we're lost!
\r
1772 There should be a better way to do this... if we could create a region or path
\r
1773 from the fill operation we would be fine for example.
\r
1775 // FloodFill( hdc, squareSize / 2, squareSize / 2, RGB(0xFF,0xFF,0xFF) );
\r
1776 ExtFloodFill( hdc, squareSize / 2, squareSize / 2, RGB(0xFF,0xFF,0xFF), FLOODFILLBORDER );
\r
1778 { /* [HGM] shave off edges of mask, in an attempt to correct for the fact that FloodFill does not work correctly under Win XP */
\r
1779 HDC dc2 = CreateCompatibleDC( hdc_window );
\r
1780 HBITMAP bm2 = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );
\r
1782 SelectObject( dc2, bm2 );
\r
1783 BitBlt( dc2, 0, 0, squareSize, squareSize, hdc, 0, 0, SRCCOPY ); // make copy
\r
1784 BitBlt( hdc, 0, 1, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );
\r
1785 BitBlt( hdc, 2, 1, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );
\r
1786 BitBlt( hdc, 1, 0, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );
\r
1787 BitBlt( hdc, 1, 2, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );
\r
1790 DeleteObject( bm2 );
\r
1793 SetTextColor( hdc, 0 );
\r
1795 Step 5: some fonts have "disconnected" areas that are skipped by the fill:
\r
1796 draw the piece again in black for safety.
\r
1798 TextOut( hdc, pt.x, pt.y, &pieceToFontChar[appData.allWhite && index >= (int)BlackPawn ? index - (int)BlackPawn : index], 1 );
\r
1800 SelectObject( hdc, hbm_old );
\r
1802 if( hPieceMask[index] != NULL ) {
\r
1803 DeleteObject( hPieceMask[index] );
\r
1806 hPieceMask[index] = hbm;
\r
1809 hbm = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );
\r
1811 SelectObject( hdc, hbm );
\r
1814 HDC dc1 = CreateCompatibleDC( hdc_window );
\r
1815 HDC dc2 = CreateCompatibleDC( hdc_window );
\r
1816 HBITMAP bm2 = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );
\r
1818 SelectObject( dc1, hPieceMask[index] );
\r
1819 SelectObject( dc2, bm2 );
\r
1820 FillRect( dc2, &rc, GetStockObject(WHITE_BRUSH) );
\r
1821 BitBlt( dc2, 0, 0, squareSize, squareSize, dc1, 0, 0, SRCINVERT );
\r
1824 Now dc2 contains the inverse of the piece mask, i.e. a mask that preserves
\r
1825 the piece background and deletes (makes transparent) the rest.
\r
1826 Thanks to that mask, we are free to paint the background with the greates
\r
1827 freedom, as we'll be able to mask off the unwanted parts when finished.
\r
1828 We use this, to make gradients and give the pieces a "roundish" look.
\r
1830 SetPieceBackground( hdc, backColor, 2 );
\r
1831 BitBlt( hdc, 0, 0, squareSize, squareSize, dc2, 0, 0, SRCAND );
\r
1835 DeleteObject( bm2 );
\r
1838 SetTextColor( hdc, foreColor );
\r
1839 TextOut( hdc, pt.x, pt.y, &pieceToFontChar[appData.allWhite && index >= (int)BlackPawn ? index - (int)BlackPawn : index], 1 );
\r
1841 SelectObject( hdc, hbm_old );
\r
1843 if( hPieceFace[index] != NULL ) {
\r
1844 DeleteObject( hPieceFace[index] );
\r
1847 hPieceFace[index] = hbm;
\r
1850 static int TranslatePieceToFontPiece( int piece )
\r
1880 case BlackMarshall:
\r
1884 case BlackNightrider:
\r
1890 case BlackUnicorn:
\r
1894 case BlackGrasshopper:
\r
1906 case BlackCardinal:
\r
1913 case WhiteMarshall:
\r
1917 case WhiteNightrider:
\r
1923 case WhiteUnicorn:
\r
1927 case WhiteGrasshopper:
\r
1939 case WhiteCardinal:
\r
1948 void CreatePiecesFromFont()
\r
1951 HDC hdc_window = NULL;
\r
1957 if( fontBitmapSquareSize < 0 ) {
\r
1958 /* Something went seriously wrong in the past: do not try to recreate fonts! */
\r
1962 if( appData.renderPiecesWithFont == NULL || appData.renderPiecesWithFont[0] == NULLCHAR || appData.renderPiecesWithFont[0] == '*' ) {
\r
1963 fontBitmapSquareSize = -1;
\r
1967 if( fontBitmapSquareSize != squareSize ) {
\r
1968 hdc_window = GetDC( hwndMain );
\r
1969 hdc = CreateCompatibleDC( hdc_window );
\r
1971 if( hPieceFont != NULL ) {
\r
1972 DeleteObject( hPieceFont );
\r
1975 for( i=0; i<=(int)BlackKing; i++ ) {
\r
1976 hPieceMask[i] = NULL;
\r
1977 hPieceFace[i] = NULL;
\r
1983 if( appData.fontPieceSize >= 50 && appData.fontPieceSize <= 150 ) {
\r
1984 fontHeight = appData.fontPieceSize;
\r
1987 fontHeight = (fontHeight * squareSize) / 100;
\r
1989 lf.lfHeight = -MulDiv( fontHeight, GetDeviceCaps(hdc, LOGPIXELSY), 72 );
\r
1991 lf.lfEscapement = 0;
\r
1992 lf.lfOrientation = 0;
\r
1993 lf.lfWeight = FW_NORMAL;
\r
1995 lf.lfUnderline = 0;
\r
1996 lf.lfStrikeOut = 0;
\r
1997 lf.lfCharSet = DEFAULT_CHARSET;
\r
1998 lf.lfOutPrecision = OUT_DEFAULT_PRECIS;
\r
1999 lf.lfClipPrecision = CLIP_DEFAULT_PRECIS;
\r
2000 lf.lfQuality = PROOF_QUALITY;
\r
2001 lf.lfPitchAndFamily = DEFAULT_PITCH | FF_DONTCARE;
\r
2002 strncpy( lf.lfFaceName, appData.renderPiecesWithFont, sizeof(lf.lfFaceName) );
\r
2003 lf.lfFaceName[ sizeof(lf.lfFaceName) - 1 ] = '\0';
\r
2005 hPieceFont = CreateFontIndirect( &lf );
\r
2007 if( hPieceFont == NULL ) {
\r
2008 fontBitmapSquareSize = -2;
\r
2011 /* Setup font-to-piece character table */
\r
2012 if( ! SetCharTable(pieceToFontChar, appData.fontToPieceTable) ) {
\r
2013 /* No (or wrong) global settings, try to detect the font */
\r
2014 if( strstr(lf.lfFaceName,"Alpha") != NULL ) {
\r
2016 SetCharTable(pieceToFontChar, "phbrqkojntwl");
\r
2018 else if( strstr(lf.lfFaceName,"DiagramTT") != NULL ) {
\r
2019 /* DiagramTT* family */
\r
2020 SetCharTable(pieceToFontChar, "PNLRQKpnlrqk");
\r
2022 else if( strstr(lf.lfFaceName,"WinboardF") != NULL ) {
\r
2023 /* Fairy symbols */
\r
2024 SetCharTable(pieceToFontChar, "PNBRQFEACWMOHIJGDVSLUKpnbrqfeacwmohijgdvsluk");
\r
2026 else if( strstr(lf.lfFaceName,"GC2004D") != NULL ) {
\r
2027 /* Good Companion (Some characters get warped as literal :-( */
\r
2028 char s[] = "1cmWG0??S??oYI23wgQU";
\r
2029 s[0]=0xB9; s[1]=0xA9; s[6]=0xB1; s[11]=0xBB; s[12]=0xAB; s[17]=0xB3;
\r
2030 SetCharTable(pieceToFontChar, s);
\r
2033 /* Cases, Condal, Leipzig, Lucena, Marroquin, Merida, Usual */
\r
2034 SetCharTable(pieceToFontChar, "pnbrqkomvtwl");
\r
2038 /* Create bitmaps */
\r
2039 hfont_old = SelectObject( hdc, hPieceFont );
\r
2040 for(i=(int)WhitePawn; i<(int)EmptySquare; i++) /* [HGM] made a loop for this */
\r
2041 if(PieceToChar((ChessSquare)i) != '.') /* skip unused pieces */
\r
2042 CreatePieceMaskFromFont( hdc_window, hdc, i );
\r
2044 SelectObject( hdc, hfont_old );
\r
2046 fontBitmapSquareSize = squareSize;
\r
2050 if( hdc != NULL ) {
\r
2054 if( hdc_window != NULL ) {
\r
2055 ReleaseDC( hwndMain, hdc_window );
\r
2060 DoLoadBitmap(HINSTANCE hinst, char *piece, int squareSize, char *suffix)
\r
2064 snprintf(name, sizeof(name)/sizeof(name[0]), "%s%d%s", piece, squareSize, suffix);
\r
2065 if (gameInfo.event &&
\r
2066 strcmp(gameInfo.event, "Easter Egg Hunt") == 0 &&
\r
2067 strcmp(name, "k80s") == 0) {
\r
2068 safeStrCpy(name, "tim", sizeof(name)/sizeof(name[0]) );
\r
2070 return LoadBitmap(hinst, name);
\r
2074 /* Insert a color into the program's logical palette
\r
2075 structure. This code assumes the given color is
\r
2076 the result of the RGB or PALETTERGB macro, and it
\r
2077 knows how those macros work (which is documented).
\r
2080 InsertInPalette(COLORREF color)
\r
2082 LPPALETTEENTRY pe = &(pLogPal->palPalEntry[pLogPal->palNumEntries]);
\r
2084 if (pLogPal->palNumEntries++ >= PALETTESIZE) {
\r
2085 DisplayFatalError(_("Too many colors"), 0, 1);
\r
2086 pLogPal->palNumEntries--;
\r
2090 pe->peFlags = (char) 0;
\r
2091 pe->peRed = (char) (0xFF & color);
\r
2092 pe->peGreen = (char) (0xFF & (color >> 8));
\r
2093 pe->peBlue = (char) (0xFF & (color >> 16));
\r
2099 InitDrawingColors()
\r
2101 if (pLogPal == NULL) {
\r
2102 /* Allocate enough memory for a logical palette with
\r
2103 * PALETTESIZE entries and set the size and version fields
\r
2104 * of the logical palette structure.
\r
2106 pLogPal = (NPLOGPALETTE)
\r
2107 LocalAlloc(LMEM_FIXED, (sizeof(LOGPALETTE) +
\r
2108 (sizeof(PALETTEENTRY) * (PALETTESIZE))));
\r
2109 pLogPal->palVersion = 0x300;
\r
2111 pLogPal->palNumEntries = 0;
\r
2113 InsertInPalette(lightSquareColor);
\r
2114 InsertInPalette(darkSquareColor);
\r
2115 InsertInPalette(whitePieceColor);
\r
2116 InsertInPalette(blackPieceColor);
\r
2117 InsertInPalette(highlightSquareColor);
\r
2118 InsertInPalette(premoveHighlightColor);
\r
2120 /* create a logical color palette according the information
\r
2121 * in the LOGPALETTE structure.
\r
2123 hPal = CreatePalette((LPLOGPALETTE) pLogPal);
\r
2125 lightSquareBrush = CreateSolidBrush(lightSquareColor);
\r
2126 blackSquareBrush = CreateSolidBrush(blackPieceColor);
\r
2127 darkSquareBrush = CreateSolidBrush(darkSquareColor);
\r
2128 whitePieceBrush = CreateSolidBrush(whitePieceColor);
\r
2129 blackPieceBrush = CreateSolidBrush(blackPieceColor);
\r
2130 iconBkgndBrush = CreateSolidBrush(GetSysColor(COLOR_BACKGROUND));
\r
2131 explodeBrush = CreateSolidBrush(highlightSquareColor); // [HGM] atomic
\r
2132 markerBrush = CreateSolidBrush(premoveHighlightColor); // [HGM] markers
\r
2133 /* [AS] Force rendering of the font-based pieces */
\r
2134 if( fontBitmapSquareSize > 0 ) {
\r
2135 fontBitmapSquareSize = 0;
\r
2141 BoardWidth(int boardSize, int n)
\r
2142 { /* [HGM] argument n added to allow different width and height */
\r
2143 int lineGap = sizeInfo[boardSize].lineGap;
\r
2145 if( appData.overrideLineGap >= 0 && appData.overrideLineGap <= 5 ) {
\r
2146 lineGap = appData.overrideLineGap;
\r
2149 return (n + 1) * lineGap +
\r
2150 n * sizeInfo[boardSize].squareSize;
\r
2153 /* Respond to board resize by dragging edge */
\r
2155 ResizeBoard(int newSizeX, int newSizeY, int flags)
\r
2157 BoardSize newSize = NUM_SIZES - 1;
\r
2158 static int recurse = 0;
\r
2159 if (IsIconic(hwndMain)) return;
\r
2160 if (recurse > 0) return;
\r
2162 while (newSize > 0) {
\r
2163 InitDrawingSizes(newSize+1000, 0); // [HGM] kludge to update sizeInfo without visible effects
\r
2164 if(newSizeX >= sizeInfo[newSize].cliWidth &&
\r
2165 newSizeY >= sizeInfo[newSize].cliHeight) break;
\r
2168 boardSize = newSize;
\r
2169 InitDrawingSizes(boardSize, flags);
\r
2174 extern Boolean twoBoards, partnerUp; // [HGM] dual
\r
2177 InitDrawingSizes(BoardSize boardSize, int flags)
\r
2179 int i, boardWidth, boardHeight; /* [HGM] height treated separately */
\r
2180 ChessSquare piece;
\r
2181 static int oldBoardSize = -1, oldTinyLayout = 0;
\r
2183 SIZE clockSize, messageSize;
\r
2185 char buf[MSG_SIZ];
\r
2187 HMENU hmenu = GetMenu(hwndMain);
\r
2188 RECT crect, wrect, oldRect;
\r
2190 LOGBRUSH logbrush;
\r
2192 int suppressVisibleEffects = 0; // [HGM] kludge to request updating sizeInfo only
\r
2193 if((int)boardSize >= 1000 ) { boardSize -= 1000; suppressVisibleEffects = 1; }
\r
2195 /* [HGM] call with -2 uses old size (for if nr of files, ranks changes) */
\r
2196 if(boardSize == (BoardSize)(-2) ) boardSize = oldBoardSize;
\r
2198 oldRect.left = wpMain.x; //[HGM] placement: remember previous window params
\r
2199 oldRect.top = wpMain.y;
\r
2200 oldRect.right = wpMain.x + wpMain.width;
\r
2201 oldRect.bottom = wpMain.y + wpMain.height;
\r
2203 tinyLayout = sizeInfo[boardSize].tinyLayout;
\r
2204 smallLayout = sizeInfo[boardSize].smallLayout;
\r
2205 squareSize = sizeInfo[boardSize].squareSize;
\r
2206 lineGap = sizeInfo[boardSize].lineGap;
\r
2207 minorSize = 0; /* [HGM] Kludge to see if demagnified pieces need to be shifted */
\r
2209 if( appData.overrideLineGap >= 0 && appData.overrideLineGap <= 5 ) {
\r
2210 lineGap = appData.overrideLineGap;
\r
2213 if (tinyLayout != oldTinyLayout) {
\r
2214 long style = GetWindowLong(hwndMain, GWL_STYLE);
\r
2216 style &= ~WS_SYSMENU;
\r
2217 InsertMenu(hmenu, IDM_Exit, MF_BYCOMMAND, IDM_Minimize,
\r
2218 "&Minimize\tCtrl+F4");
\r
2220 style |= WS_SYSMENU;
\r
2221 RemoveMenu(hmenu, IDM_Minimize, MF_BYCOMMAND);
\r
2223 SetWindowLong(hwndMain, GWL_STYLE, style);
\r
2225 for (i=0; menuBarText[tinyLayout][i]; i++) {
\r
2226 ModifyMenu(hmenu, i, MF_STRING|MF_BYPOSITION|MF_POPUP,
\r
2227 (UINT)GetSubMenu(hmenu, i), T_(menuBarText[tinyLayout][i]));
\r
2229 DrawMenuBar(hwndMain);
\r
2232 boardWidth = BoardWidth(boardSize, BOARD_WIDTH);
\r
2233 boardHeight = BoardWidth(boardSize, BOARD_HEIGHT);
\r
2235 /* Get text area sizes */
\r
2236 hdc = GetDC(hwndMain);
\r
2237 if (appData.clockMode) {
\r
2238 snprintf(buf, MSG_SIZ, _("White: %s"), TimeString(23*60*60*1000L));
\r
2240 snprintf(buf, MSG_SIZ, _("White"));
\r
2242 oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);
\r
2243 GetTextExtentPoint(hdc, buf, strlen(buf), &clockSize);
\r
2244 SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);
\r
2245 str = _("We only care about the height here");
\r
2246 GetTextExtentPoint(hdc, str, strlen(str), &messageSize);
\r
2247 SelectObject(hdc, oldFont);
\r
2248 ReleaseDC(hwndMain, hdc);
\r
2250 /* Compute where everything goes */
\r
2251 if((first.programLogo || second.programLogo) && !tinyLayout) {
\r
2252 /* [HGM] logo: if either logo is on, reserve space for it */
\r
2253 logoHeight = 2*clockSize.cy;
\r
2254 leftLogoRect.left = OUTER_MARGIN;
\r
2255 leftLogoRect.right = leftLogoRect.left + 4*clockSize.cy;
\r
2256 leftLogoRect.top = OUTER_MARGIN;
\r
2257 leftLogoRect.bottom = OUTER_MARGIN + logoHeight;
\r
2259 rightLogoRect.right = OUTER_MARGIN + boardWidth;
\r
2260 rightLogoRect.left = rightLogoRect.right - 4*clockSize.cy;
\r
2261 rightLogoRect.top = OUTER_MARGIN;
\r
2262 rightLogoRect.bottom = OUTER_MARGIN + logoHeight;
\r
2265 whiteRect.left = leftLogoRect.right;
\r
2266 whiteRect.right = OUTER_MARGIN + boardWidth/2 - INNER_MARGIN/2;
\r
2267 whiteRect.top = OUTER_MARGIN;
\r
2268 whiteRect.bottom = whiteRect.top + logoHeight;
\r
2270 blackRect.right = rightLogoRect.left;
\r
2271 blackRect.left = whiteRect.right + INNER_MARGIN;
\r
2272 blackRect.top = whiteRect.top;
\r
2273 blackRect.bottom = whiteRect.bottom;
\r
2275 whiteRect.left = OUTER_MARGIN;
\r
2276 whiteRect.right = whiteRect.left + boardWidth/2 - INNER_MARGIN/2;
\r
2277 whiteRect.top = OUTER_MARGIN;
\r
2278 whiteRect.bottom = whiteRect.top + clockSize.cy;
\r
2280 blackRect.left = whiteRect.right + INNER_MARGIN;
\r
2281 blackRect.right = blackRect.left + boardWidth/2 - 1;
\r
2282 blackRect.top = whiteRect.top;
\r
2283 blackRect.bottom = whiteRect.bottom;
\r
2285 logoHeight = 0; // [HGM] logo: suppress logo after change to tiny layout!
\r
2288 messageRect.left = OUTER_MARGIN + MESSAGE_LINE_LEFTMARGIN;
\r
2289 if (appData.showButtonBar) {
\r
2290 messageRect.right = OUTER_MARGIN + boardWidth // [HGM] logo: expressed independent of clock placement
\r
2291 - N_BUTTONS*BUTTON_WIDTH - MESSAGE_LINE_LEFTMARGIN;
\r
2293 messageRect.right = OUTER_MARGIN + boardWidth;
\r
2295 messageRect.top = whiteRect.bottom + INNER_MARGIN;
\r
2296 messageRect.bottom = messageRect.top + messageSize.cy;
\r
2298 boardRect.left = OUTER_MARGIN;
\r
2299 boardRect.right = boardRect.left + boardWidth;
\r
2300 boardRect.top = messageRect.bottom + INNER_MARGIN;
\r
2301 boardRect.bottom = boardRect.top + boardHeight;
\r
2303 sizeInfo[boardSize].cliWidth = boardRect.right + OUTER_MARGIN;
\r
2304 sizeInfo[boardSize].cliHeight = boardRect.bottom + OUTER_MARGIN;
\r
2305 oldBoardSize = boardSize;
\r
2306 oldTinyLayout = tinyLayout;
\r
2307 winW = 2 * GetSystemMetrics(SM_CXFRAME) + boardRect.right + OUTER_MARGIN;
\r
2308 winH = 2 * GetSystemMetrics(SM_CYFRAME) + GetSystemMetrics(SM_CYMENU) +
\r
2309 GetSystemMetrics(SM_CYCAPTION) + boardRect.bottom + OUTER_MARGIN;
\r
2310 winW *= 1 + twoBoards;
\r
2311 if(suppressVisibleEffects) return; // [HGM] when called for filling sizeInfo only
\r
2312 wpMain.width = winW; // [HGM] placement: set through temporary which can used by initial sizing choice
\r
2313 wpMain.height = winH; // without disturbing window attachments
\r
2314 GetWindowRect(hwndMain, &wrect);
\r
2315 SetWindowPos(hwndMain, NULL, 0, 0, wpMain.width, wpMain.height,
\r
2316 SWP_NOCOPYBITS|SWP_NOZORDER|SWP_NOMOVE);
\r
2318 // [HGM] placement: let attached windows follow size change.
\r
2319 ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, moveHistoryDialog, &wpMoveHistory );
\r
2320 ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, evalGraphDialog, &wpEvalGraph );
\r
2321 ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, engineOutputDialog, &wpEngineOutput );
\r
2322 ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, gameListDialog, &wpGameList );
\r
2323 ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, hwndConsole, &wpConsole );
\r
2325 /* compensate if menu bar wrapped */
\r
2326 GetClientRect(hwndMain, &crect);
\r
2327 offby = boardRect.bottom + OUTER_MARGIN - crect.bottom;
\r
2328 wpMain.height += offby;
\r
2330 case WMSZ_TOPLEFT:
\r
2331 SetWindowPos(hwndMain, NULL,
\r
2332 wrect.right - wpMain.width, wrect.bottom - wpMain.height,
\r
2333 wpMain.width, wpMain.height, SWP_NOCOPYBITS|SWP_NOZORDER);
\r
2336 case WMSZ_TOPRIGHT:
\r
2338 SetWindowPos(hwndMain, NULL,
\r
2339 wrect.left, wrect.bottom - wpMain.height,
\r
2340 wpMain.width, wpMain.height, SWP_NOCOPYBITS|SWP_NOZORDER);
\r
2343 case WMSZ_BOTTOMLEFT:
\r
2345 SetWindowPos(hwndMain, NULL,
\r
2346 wrect.right - wpMain.width, wrect.top,
\r
2347 wpMain.width, wpMain.height, SWP_NOCOPYBITS|SWP_NOZORDER);
\r
2350 case WMSZ_BOTTOMRIGHT:
\r
2354 SetWindowPos(hwndMain, NULL, 0, 0, wpMain.width, wpMain.height,
\r
2355 SWP_NOCOPYBITS|SWP_NOZORDER|SWP_NOMOVE);
\r
2360 for (i = 0; i < N_BUTTONS; i++) {
\r
2361 if (buttonDesc[i].hwnd != NULL) {
\r
2362 DestroyWindow(buttonDesc[i].hwnd);
\r
2363 buttonDesc[i].hwnd = NULL;
\r
2365 if (appData.showButtonBar) {
\r
2366 buttonDesc[i].hwnd =
\r
2367 CreateWindow("BUTTON", buttonDesc[i].label,
\r
2368 WS_VISIBLE | WS_CHILD | BS_PUSHBUTTON,
\r
2369 boardRect.right - BUTTON_WIDTH*(N_BUTTONS-i),
\r
2370 messageRect.top, BUTTON_WIDTH, messageSize.cy, hwndMain,
\r
2371 (HMENU) buttonDesc[i].id,
\r
2372 (HINSTANCE) GetWindowLong(hwndMain, GWL_HINSTANCE), NULL);
\r
2374 SendMessage(buttonDesc[i].hwnd, WM_SETFONT,
\r
2375 (WPARAM)font[boardSize][MESSAGE_FONT]->hf,
\r
2376 MAKELPARAM(FALSE, 0));
\r
2378 if (buttonDesc[i].id == IDM_Pause)
\r
2379 hwndPause = buttonDesc[i].hwnd;
\r
2380 buttonDesc[i].wndproc = (WNDPROC)
\r
2381 SetWindowLong(buttonDesc[i].hwnd, GWL_WNDPROC, (LONG) ButtonProc);
\r
2384 if (gridPen != NULL) DeleteObject(gridPen);
\r
2385 if (highlightPen != NULL) DeleteObject(highlightPen);
\r
2386 if (premovePen != NULL) DeleteObject(premovePen);
\r
2387 if (lineGap != 0) {
\r
2388 logbrush.lbStyle = BS_SOLID;
\r
2389 logbrush.lbColor = RGB(0, 0, 0); /* grid pen color = black */
\r
2391 ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,
\r
2392 lineGap, &logbrush, 0, NULL);
\r
2393 logbrush.lbColor = highlightSquareColor;
\r
2395 ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,
\r
2396 lineGap, &logbrush, 0, NULL);
\r
2398 logbrush.lbColor = premoveHighlightColor;
\r
2400 ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,
\r
2401 lineGap, &logbrush, 0, NULL);
\r
2403 /* [HGM] Loop had to be split in part for vert. and hor. lines */
\r
2404 for (i = 0; i < BOARD_HEIGHT + 1; i++) {
\r
2405 gridEndpoints[i*2].x = boardRect.left + lineGap / 2;
\r
2406 gridEndpoints[i*2].y = gridEndpoints[i*2 + 1].y =
\r
2407 boardRect.top + lineGap / 2 + (i * (squareSize + lineGap));
\r
2408 gridEndpoints[i*2 + 1].x = boardRect.left + lineGap / 2 +
\r
2409 BOARD_WIDTH * (squareSize + lineGap);
\r
2410 gridVertexCounts[i*2] = gridVertexCounts[i*2 + 1] = 2;
\r
2412 for (i = 0; i < BOARD_WIDTH + 1; i++) {
\r
2413 gridEndpoints[i*2 + BOARD_HEIGHT*2 + 2].y = boardRect.top + lineGap / 2;
\r
2414 gridEndpoints[i*2 + BOARD_HEIGHT*2 + 2].x =
\r
2415 gridEndpoints[i*2 + 1 + BOARD_HEIGHT*2 + 2].x = boardRect.left +
\r
2416 lineGap / 2 + (i * (squareSize + lineGap));
\r
2417 gridEndpoints[i*2 + 1 + BOARD_HEIGHT*2 + 2].y =
\r
2418 boardRect.top + BOARD_HEIGHT * (squareSize + lineGap);
\r
2419 gridVertexCounts[i*2] = gridVertexCounts[i*2 + 1] = 2;
\r
2423 /* [HGM] Licensing requirement */
\r
2425 if(gameInfo.variant == VariantGothic) GothicPopUp( GOTHIC, VariantGothic); else
\r
2428 if(gameInfo.variant == VariantFalcon) GothicPopUp( FALCON, VariantFalcon); else
\r
2430 GothicPopUp( "", VariantNormal);
\r
2433 /* if (boardSize == oldBoardSize) return; [HGM] variant might have changed */
\r
2435 /* Load piece bitmaps for this board size */
\r
2436 for (i=0; i<=2; i++) {
\r
2437 for (piece = WhitePawn;
\r
2438 (int) piece < (int) BlackPawn;
\r
2439 piece = (ChessSquare) ((int) piece + 1)) {
\r
2440 if (pieceBitmap[i][piece] != NULL)
\r
2441 DeleteObject(pieceBitmap[i][piece]);
\r
2445 fontBitmapSquareSize = 0; /* [HGM] render: make sure pieces will be recreated, as we might need others now */
\r
2446 // Orthodox Chess pieces
\r
2447 pieceBitmap[0][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "s");
\r
2448 pieceBitmap[0][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "s");
\r
2449 pieceBitmap[0][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "s");
\r
2450 pieceBitmap[0][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "s");
\r
2451 pieceBitmap[0][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "s");
\r
2452 pieceBitmap[1][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "o");
\r
2453 pieceBitmap[1][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "o");
\r
2454 pieceBitmap[1][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "o");
\r
2455 pieceBitmap[1][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "o");
\r
2456 pieceBitmap[1][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "o");
\r
2457 pieceBitmap[2][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "w");
\r
2458 pieceBitmap[2][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "w");
\r
2459 pieceBitmap[2][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "w");
\r
2460 pieceBitmap[2][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "w");
\r
2461 pieceBitmap[2][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "w");
\r
2462 if( gameInfo.variant == VariantShogi && squareSize <= 72 && squareSize >= 33) {
\r
2463 // in Shogi, Hijack the unused Queen for Lance
\r
2464 pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "s");
\r
2465 pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "o");
\r
2466 pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "w");
\r
2468 pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "s");
\r
2469 pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "o");
\r
2470 pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "w");
\r
2473 if(squareSize <= 72 && squareSize >= 33) {
\r
2474 /* A & C are available in most sizes now */
\r
2475 if(squareSize != 49 && squareSize != 72 && squareSize != 33) { // Vortex-like
\r
2476 pieceBitmap[0][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "s");
\r
2477 pieceBitmap[1][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "o");
\r
2478 pieceBitmap[2][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "w");
\r
2479 pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "s");
\r
2480 pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "o");
\r
2481 pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "w");
\r
2482 pieceBitmap[0][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "s");
\r
2483 pieceBitmap[1][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "o");
\r
2484 pieceBitmap[2][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "w");
\r
2485 pieceBitmap[0][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "s");
\r
2486 pieceBitmap[1][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "o");
\r
2487 pieceBitmap[2][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "w");
\r
2488 } else { // Smirf-like
\r
2489 if(gameInfo.variant == VariantSChess) {
\r
2490 pieceBitmap[0][WhiteAngel] = DoLoadBitmap(hInst, "v", squareSize, "s");
\r
2491 pieceBitmap[1][WhiteAngel] = DoLoadBitmap(hInst, "v", squareSize, "o");
\r
2492 pieceBitmap[2][WhiteAngel] = DoLoadBitmap(hInst, "v", squareSize, "w");
\r
2494 pieceBitmap[0][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "s");
\r
2495 pieceBitmap[1][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "o");
\r
2496 pieceBitmap[2][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "w");
\r
2499 if(gameInfo.variant == VariantGothic) { // Vortex-like
\r
2500 pieceBitmap[0][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "s");
\r
2501 pieceBitmap[1][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "o");
\r
2502 pieceBitmap[2][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "w");
\r
2503 } else if(gameInfo.variant == VariantSChess && (squareSize == 49 || squareSize == 72)) {
\r
2504 pieceBitmap[0][WhiteMarshall] = DoLoadBitmap(hInst, "e", squareSize, "s");
\r
2505 pieceBitmap[1][WhiteMarshall] = DoLoadBitmap(hInst, "e", squareSize, "o");
\r
2506 pieceBitmap[2][WhiteMarshall] = DoLoadBitmap(hInst, "e", squareSize, "w");
\r
2507 } else { // WinBoard standard
\r
2508 pieceBitmap[0][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "s");
\r
2509 pieceBitmap[1][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "o");
\r
2510 pieceBitmap[2][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "w");
\r
2515 if(squareSize==72 || squareSize==49 || squareSize==33) { /* experiment with some home-made bitmaps */
\r
2516 pieceBitmap[0][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "s");
\r
2517 pieceBitmap[1][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "o");
\r
2518 pieceBitmap[2][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "w");
\r
2519 pieceBitmap[0][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "s");
\r
2520 pieceBitmap[1][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "o");
\r
2521 pieceBitmap[2][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "w");
\r
2522 pieceBitmap[0][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "s");
\r
2523 pieceBitmap[1][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "o");
\r
2524 pieceBitmap[2][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "w");
\r
2525 pieceBitmap[0][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "s");
\r
2526 pieceBitmap[1][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "o");
\r
2527 pieceBitmap[2][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "w");
\r
2528 pieceBitmap[0][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "s");
\r
2529 pieceBitmap[1][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "o");
\r
2530 pieceBitmap[2][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "w");
\r
2531 pieceBitmap[0][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "s");
\r
2532 pieceBitmap[1][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "o");
\r
2533 pieceBitmap[2][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "w");
\r
2534 pieceBitmap[0][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "s");
\r
2535 pieceBitmap[1][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "o");
\r
2536 pieceBitmap[2][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "w");
\r
2537 pieceBitmap[0][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "s");
\r
2538 pieceBitmap[1][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "o");
\r
2539 pieceBitmap[2][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "w");
\r
2540 pieceBitmap[0][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "s");
\r
2541 pieceBitmap[1][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "o");
\r
2542 pieceBitmap[2][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "w");
\r
2543 pieceBitmap[0][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "s");
\r
2544 pieceBitmap[1][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "o");
\r
2545 pieceBitmap[2][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "w");
\r
2547 if(gameInfo.variant == VariantShogi) { /* promoted Gold represemtations */
\r
2548 pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "wp", squareSize, "s");
\r
2549 pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "wp", squareSize, "o");
\r
2550 pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "w", squareSize, "w");
\r
2551 pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "wn", squareSize, "s");
\r
2552 pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "wn", squareSize, "o");
\r
2553 pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "w", squareSize, "w");
\r
2554 pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "ws", squareSize, "s");
\r
2555 pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "ws", squareSize, "o");
\r
2556 pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "w", squareSize, "w");
\r
2557 pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "wl", squareSize, "s");
\r
2558 pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "wl", squareSize, "o");
\r
2559 pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "w", squareSize, "w");
\r
2561 pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "s");
\r
2562 pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "o");
\r
2563 pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "w");
\r
2564 pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "s");
\r
2565 pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "o");
\r
2566 pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "w");
\r
2567 pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "s");
\r
2568 pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "o");
\r
2569 pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "w");
\r
2570 pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "s");
\r
2571 pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "o");
\r
2572 pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "w");
\r
2575 } else { /* other size, no special bitmaps available. Use smaller symbols */
\r
2576 if((int)boardSize < 2) minorSize = sizeInfo[0].squareSize;
\r
2577 else minorSize = sizeInfo[(int)boardSize - 2].squareSize;
\r
2578 pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "s");
\r
2579 pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "o");
\r
2580 pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "w");
\r
2581 pieceBitmap[0][WhiteCardinal] = DoLoadBitmap(hInst, "b", minorSize, "s");
\r
2582 pieceBitmap[1][WhiteCardinal] = DoLoadBitmap(hInst, "b", minorSize, "o");
\r
2583 pieceBitmap[2][WhiteCardinal] = DoLoadBitmap(hInst, "b", minorSize, "w");
\r
2584 pieceBitmap[0][WhiteDragon] = DoLoadBitmap(hInst, "r", minorSize, "s");
\r
2585 pieceBitmap[1][WhiteDragon] = DoLoadBitmap(hInst, "r", minorSize, "o");
\r
2586 pieceBitmap[2][WhiteDragon] = DoLoadBitmap(hInst, "r", minorSize, "w");
\r
2587 pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "s");
\r
2588 pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "o");
\r
2589 pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "w");
\r
2593 if(gameInfo.variant == VariantShogi && squareSize == 58)
\r
2594 /* special Shogi support in this size */
\r
2595 { for (i=0; i<=2; i++) { /* replace all bitmaps */
\r
2596 for (piece = WhitePawn;
\r
2597 (int) piece < (int) BlackPawn;
\r
2598 piece = (ChessSquare) ((int) piece + 1)) {
\r
2599 if (pieceBitmap[i][piece] != NULL)
\r
2600 DeleteObject(pieceBitmap[i][piece]);
\r
2603 pieceBitmap[0][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "o");
\r
2604 pieceBitmap[0][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "o");
\r
2605 pieceBitmap[0][WhiteBishop] = DoLoadBitmap(hInst, "sb", squareSize, "o");
\r
2606 pieceBitmap[0][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "o");
\r
2607 pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "o");
\r
2608 pieceBitmap[0][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "o");
\r
2609 pieceBitmap[0][WhiteFerz] = DoLoadBitmap(hInst, "sf", squareSize, "o");
\r
2610 pieceBitmap[0][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "o");
\r
2611 pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "su", squareSize, "o");
\r
2612 pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "sh", squareSize, "o");
\r
2613 pieceBitmap[0][WhiteCardinal] = DoLoadBitmap(hInst, "sa", squareSize, "o");
\r
2614 pieceBitmap[0][WhiteDragon] = DoLoadBitmap(hInst, "sc", squareSize, "o");
\r
2615 pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "sg", squareSize, "o");
\r
2616 pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "ss", squareSize, "o");
\r
2617 pieceBitmap[1][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "o");
\r
2618 pieceBitmap[1][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "o");
\r
2619 pieceBitmap[1][WhiteBishop] = DoLoadBitmap(hInst, "sb", squareSize, "o");
\r
2620 pieceBitmap[1][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "o");
\r
2621 pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "o");
\r
2622 pieceBitmap[1][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "o");
\r
2623 pieceBitmap[1][WhiteFerz] = DoLoadBitmap(hInst, "sf", squareSize, "o");
\r
2624 pieceBitmap[1][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "o");
\r
2625 pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "su", squareSize, "o");
\r
2626 pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "sh", squareSize, "o");
\r
2627 pieceBitmap[1][WhiteCardinal] = DoLoadBitmap(hInst, "sa", squareSize, "o");
\r
2628 pieceBitmap[1][WhiteDragon] = DoLoadBitmap(hInst, "sc", squareSize, "o");
\r
2629 pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "sg", squareSize, "o");
\r
2630 pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "ss", squareSize, "o");
\r
2631 pieceBitmap[2][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "w");
\r
2632 pieceBitmap[2][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "w");
\r
2633 pieceBitmap[2][WhiteBishop] = DoLoadBitmap(hInst, "sr", squareSize, "w");
\r
2634 pieceBitmap[2][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "w");
\r
2635 pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "w");
\r
2636 pieceBitmap[2][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "w");
\r
2637 pieceBitmap[2][WhiteFerz] = DoLoadBitmap(hInst, "sw", squareSize, "w");
\r
2638 pieceBitmap[2][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "w");
\r
2639 pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "sp", squareSize, "w");
\r
2640 pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "sn", squareSize, "w");
\r
2641 pieceBitmap[2][WhiteCardinal] = DoLoadBitmap(hInst, "sr", squareSize, "w");
\r
2642 pieceBitmap[2][WhiteDragon] = DoLoadBitmap(hInst, "sr", squareSize, "w");
\r
2643 pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "sl", squareSize, "w");
\r
2644 pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "sw", squareSize, "w");
\r
2650 PieceBitmap(ChessSquare p, int kind)
\r
2652 if ((int) p >= (int) BlackPawn)
\r
2653 p = (ChessSquare) ((int) p - (int) BlackPawn + (int) WhitePawn);
\r
2655 return pieceBitmap[kind][(int) p];
\r
2658 /***************************************************************/
\r
2660 #define MIN(a,b) (((a) < (b)) ? (a) : (b))
\r
2661 #define MAX(a,b) (((a) > (b)) ? (a) : (b))
\r
2663 #define MIN3(a,b,c) (((a) < (b) && (a) < (c)) ? (a) : (((b) < (a) && (b) < (c)) ? (b) : (c)))
\r
2664 #define MAX3(a,b,c) (((a) > (b) && (a) > (c)) ? (a) : (((b) > (a) && (b) > (c)) ? (b) : (c)))
\r
2668 SquareToPos(int row, int column, int * x, int * y)
\r
2671 *x = boardRect.left + lineGap + ((BOARD_WIDTH-1)-column) * (squareSize + lineGap);
\r
2672 *y = boardRect.top + lineGap + row * (squareSize + lineGap);
\r
2674 *x = boardRect.left + lineGap + column * (squareSize + lineGap);
\r
2675 *y = boardRect.top + lineGap + ((BOARD_HEIGHT-1)-row) * (squareSize + lineGap);
\r
2680 DrawCoordsOnDC(HDC hdc)
\r
2682 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
2683 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
2684 char str[2] = { NULLCHAR, NULLCHAR };
\r
2685 int oldMode, oldAlign, x, y, start, i;
\r
2689 if (!appData.showCoords)
\r
2692 start = flipView ? 1-(ONE!='1') : 23+(ONE!='1')-BOARD_HEIGHT;
\r
2694 oldBrush = SelectObject(hdc, GetStockObject(BLACK_BRUSH));
\r
2695 oldMode = SetBkMode(hdc, (appData.monoMode ? OPAQUE : TRANSPARENT));
\r
2696 oldAlign = GetTextAlign(hdc);
\r
2697 oldFont = SelectObject(hdc, font[boardSize][COORD_FONT]->hf);
\r
2699 y = boardRect.top + lineGap;
\r
2700 x = boardRect.left + lineGap + gameInfo.holdingsWidth*(squareSize + lineGap);
\r
2702 SetTextAlign(hdc, TA_LEFT|TA_TOP);
\r
2703 for (i = 0; i < BOARD_HEIGHT; i++) {
\r
2704 str[0] = files[start + i];
\r
2705 ExtTextOut(hdc, x + 2, y + 1, 0, NULL, str, 1, NULL);
\r
2706 y += squareSize + lineGap;
\r
2709 start = flipView ? 12-(BOARD_RGHT-BOARD_LEFT) : 12;
\r
2711 SetTextAlign(hdc, TA_RIGHT|TA_BOTTOM);
\r
2712 for (i = 0; i < BOARD_RGHT - BOARD_LEFT; i++) {
\r
2713 str[0] = ranks[start + i];
\r
2714 ExtTextOut(hdc, x + squareSize - 2, y - 1, 0, NULL, str, 1, NULL);
\r
2715 x += squareSize + lineGap;
\r
2718 SelectObject(hdc, oldBrush);
\r
2719 SetBkMode(hdc, oldMode);
\r
2720 SetTextAlign(hdc, oldAlign);
\r
2721 SelectObject(hdc, oldFont);
\r
2725 DrawGridOnDC(HDC hdc)
\r
2729 if (lineGap != 0) {
\r
2730 oldPen = SelectObject(hdc, gridPen);
\r
2731 PolyPolyline(hdc, gridEndpoints, gridVertexCounts, BOARD_WIDTH+BOARD_HEIGHT + 2);
\r
2732 SelectObject(hdc, oldPen);
\r
2736 #define HIGHLIGHT_PEN 0
\r
2737 #define PREMOVE_PEN 1
\r
2740 DrawHighlightOnDC(HDC hdc, BOOLEAN on, int x, int y, int pen)
\r
2743 HPEN oldPen, hPen;
\r
2744 if (lineGap == 0) return;
\r
2746 x1 = boardRect.left +
\r
2747 lineGap/2 + ((BOARD_WIDTH-1)-x) * (squareSize + lineGap);
\r
2748 y1 = boardRect.top +
\r
2749 lineGap/2 + y * (squareSize + lineGap);
\r
2751 x1 = boardRect.left +
\r
2752 lineGap/2 + x * (squareSize + lineGap);
\r
2753 y1 = boardRect.top +
\r
2754 lineGap/2 + ((BOARD_HEIGHT-1)-y) * (squareSize + lineGap);
\r
2756 hPen = pen ? premovePen : highlightPen;
\r
2757 oldPen = SelectObject(hdc, on ? hPen : gridPen);
\r
2758 MoveToEx(hdc, x1, y1, NULL);
\r
2759 LineTo(hdc, x1 + squareSize + lineGap, y1);
\r
2760 LineTo(hdc, x1 + squareSize + lineGap, y1 + squareSize + lineGap);
\r
2761 LineTo(hdc, x1, y1 + squareSize + lineGap);
\r
2762 LineTo(hdc, x1, y1);
\r
2763 SelectObject(hdc, oldPen);
\r
2767 DrawHighlightsOnDC(HDC hdc, HighlightInfo *h, int pen)
\r
2770 for (i=0; i<2; i++) {
\r
2771 if (h->sq[i].x >= 0 && h->sq[i].y >= 0)
\r
2772 DrawHighlightOnDC(hdc, TRUE,
\r
2773 h->sq[i].x, h->sq[i].y,
\r
2778 /* Note: sqcolor is used only in monoMode */
\r
2779 /* Note that this code is largely duplicated in woptions.c,
\r
2780 function DrawSampleSquare, so that needs to be updated too */
\r
2782 DrawPieceOnDC(HDC hdc, ChessSquare piece, int color, int sqcolor, int x, int y, HDC tmphdc)
\r
2784 HBITMAP oldBitmap;
\r
2788 if (appData.blindfold) return;
\r
2790 /* [AS] Use font-based pieces if needed */
\r
2791 if( fontBitmapSquareSize >= 0 && (squareSize > 32 || gameInfo.variant >= VariantShogi)) {
\r
2792 /* Create piece bitmaps, or do nothing if piece set is up to date */
\r
2793 CreatePiecesFromFont();
\r
2795 if( fontBitmapSquareSize == squareSize ) {
\r
2796 int index = TranslatePieceToFontPiece(piece);
\r
2798 SelectObject( tmphdc, hPieceMask[ index ] );
\r
2800 if(appData.upsideDown ? color==flipView : (flipView && gameInfo.variant == VariantShogi))
\r
2801 StretchBlt(hdc, x+squareSize, y+squareSize, -squareSize, -squareSize, tmphdc, 0, 0, squareSize, squareSize, SRCAND);
\r
2805 squareSize, squareSize,
\r
2810 SelectObject( tmphdc, hPieceFace[ index ] );
\r
2812 if(appData.upsideDown ? color==flipView : (flipView && gameInfo.variant == VariantShogi))
\r
2813 StretchBlt(hdc, x+squareSize, y+squareSize, -squareSize, -squareSize, tmphdc, 0, 0, squareSize, squareSize, SRCPAINT);
\r
2817 squareSize, squareSize,
\r
2826 if (appData.monoMode) {
\r
2827 SelectObject(tmphdc, PieceBitmap(piece,
\r
2828 color == sqcolor ? OUTLINE_PIECE : SOLID_PIECE));
\r
2829 BitBlt(hdc, x, y, squareSize, squareSize, tmphdc, 0, 0,
\r
2830 sqcolor ? SRCCOPY : NOTSRCCOPY);
\r
2832 tmpSize = squareSize;
\r
2834 ((piece >= (int)WhiteNightrider && piece <= WhiteGrasshopper) ||
\r
2835 (piece >= (int)BlackNightrider && piece <= BlackGrasshopper)) ) {
\r
2836 /* [HGM] no bitmap available for promoted pieces in Crazyhouse */
\r
2837 /* Bitmaps of smaller size are substituted, but we have to align them */
\r
2838 x += (squareSize - minorSize)>>1;
\r
2839 y += squareSize - minorSize - 2;
\r
2840 tmpSize = minorSize;
\r
2842 if (color || appData.allWhite ) {
\r
2843 oldBitmap = SelectObject(tmphdc, PieceBitmap(piece, WHITE_PIECE));
\r
2845 oldBrush = SelectObject(hdc, whitePieceBrush);
\r
2846 else oldBrush = SelectObject(hdc, blackPieceBrush);
\r
2847 if(appData.upsideDown && color==flipView)
\r
2848 StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, 0x00B8074A);
\r
2850 BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, 0x00B8074A);
\r
2851 /* Use black for outline of white pieces */
\r
2852 SelectObject(tmphdc, PieceBitmap(piece, OUTLINE_PIECE));
\r
2853 if(appData.upsideDown && color==flipView)
\r
2854 StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, SRCAND);
\r
2856 BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, SRCAND);
\r
2858 /* Use square color for details of black pieces */
\r
2859 oldBitmap = SelectObject(tmphdc, PieceBitmap(piece, SOLID_PIECE));
\r
2860 oldBrush = SelectObject(hdc, blackPieceBrush);
\r
2861 if(appData.upsideDown && !flipView)
\r
2862 StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, 0x00B8074A);
\r
2864 BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, 0x00B8074A);
\r
2866 SelectObject(hdc, oldBrush);
\r
2867 SelectObject(tmphdc, oldBitmap);
\r
2871 /* [AS] Compute a drawing mode for a square, based on specified settings (see DrawTile) */
\r
2872 int GetBackTextureMode( int algo )
\r
2874 int result = BACK_TEXTURE_MODE_DISABLED;
\r
2878 case BACK_TEXTURE_MODE_PLAIN:
\r
2879 result = 1; /* Always use identity map */
\r
2881 case BACK_TEXTURE_MODE_FULL_RANDOM:
\r
2882 result = 1 + (myrandom() % 3); /* Pick a transformation at random */
\r
2890 [AS] Compute and save texture drawing info, otherwise we may not be able
\r
2891 to handle redraws cleanly (as random numbers would always be different).
\r
2893 VOID RebuildTextureSquareInfo()
\r
2903 ZeroMemory( &backTextureSquareInfo, sizeof(backTextureSquareInfo) );
\r
2905 if( liteBackTexture != NULL ) {
\r
2906 if( GetObject( liteBackTexture, sizeof(bi), &bi ) > 0 ) {
\r
2907 lite_w = bi.bmWidth;
\r
2908 lite_h = bi.bmHeight;
\r
2912 if( darkBackTexture != NULL ) {
\r
2913 if( GetObject( darkBackTexture, sizeof(bi), &bi ) > 0 ) {
\r
2914 dark_w = bi.bmWidth;
\r
2915 dark_h = bi.bmHeight;
\r
2919 for( row=0; row<BOARD_HEIGHT; row++ ) {
\r
2920 for( col=0; col<BOARD_WIDTH; col++ ) {
\r
2921 if( (col + row) & 1 ) {
\r
2923 if( lite_w >= squareSize && lite_h >= squareSize ) {
\r
2924 if( lite_w >= squareSize*BOARD_WIDTH )
\r
2925 backTextureSquareInfo[row][col].x = (2*col+1)*lite_w/(2*BOARD_WIDTH) - squareSize/2; /* [HGM] cut out of center of virtual square */
\r
2927 backTextureSquareInfo[row][col].x = col * (lite_w - squareSize) / (BOARD_WIDTH-1); /* [HGM] divide by size-1 in stead of size! */
\r
2928 if( lite_h >= squareSize*BOARD_HEIGHT )
\r
2929 backTextureSquareInfo[row][col].y = (2*(BOARD_HEIGHT-row)-1)*lite_h/(2*BOARD_HEIGHT) - squareSize/2;
\r
2931 backTextureSquareInfo[row][col].y = (BOARD_HEIGHT-1-row) * (lite_h - squareSize) / (BOARD_HEIGHT-1);
\r
2932 backTextureSquareInfo[row][col].mode = GetBackTextureMode(liteBackTextureMode);
\r
2937 if( dark_w >= squareSize && dark_h >= squareSize ) {
\r
2938 if( dark_w >= squareSize*BOARD_WIDTH )
\r
2939 backTextureSquareInfo[row][col].x = (2*col+1) * dark_w / (2*BOARD_WIDTH) - squareSize/2;
\r
2941 backTextureSquareInfo[row][col].x = col * (dark_w - squareSize) / (BOARD_WIDTH-1);
\r
2942 if( dark_h >= squareSize*BOARD_HEIGHT )
\r
2943 backTextureSquareInfo[row][col].y = (2*(BOARD_HEIGHT-row)-1) * dark_h / (2*BOARD_HEIGHT) - squareSize/2;
\r
2945 backTextureSquareInfo[row][col].y = (BOARD_HEIGHT-1-row) * (dark_h - squareSize) / (BOARD_HEIGHT-1);
\r
2946 backTextureSquareInfo[row][col].mode = GetBackTextureMode(darkBackTextureMode);
\r
2953 /* [AS] Arrow highlighting support */
\r
2955 static double A_WIDTH = 5; /* Width of arrow body */
\r
2957 #define A_HEIGHT_FACTOR 6 /* Length of arrow "point", relative to body width */
\r
2958 #define A_WIDTH_FACTOR 3 /* Width of arrow "point", relative to body width */
\r
2960 static double Sqr( double x )
\r
2965 static int Round( double x )
\r
2967 return (int) (x + 0.5);
\r
2970 /* Draw an arrow between two points using current settings */
\r
2971 VOID DrawArrowBetweenPoints( HDC hdc, int s_x, int s_y, int d_x, int d_y )
\r
2974 double dx, dy, j, k, x, y;
\r
2976 if( d_x == s_x ) {
\r
2977 int h = (d_y > s_y) ? +A_WIDTH*A_HEIGHT_FACTOR : -A_WIDTH*A_HEIGHT_FACTOR;
\r
2979 arrow[0].x = s_x + A_WIDTH + 0.5;
\r
2982 arrow[1].x = s_x + A_WIDTH + 0.5;
\r
2983 arrow[1].y = d_y - h;
\r
2985 arrow[2].x = arrow[1].x + A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;
\r
2986 arrow[2].y = d_y - h;
\r
2991 arrow[5].x = arrow[1].x - 2*A_WIDTH + 0.5;
\r
2992 arrow[5].y = d_y - h;
\r
2994 arrow[4].x = arrow[5].x - A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;
\r
2995 arrow[4].y = d_y - h;
\r
2997 arrow[6].x = arrow[1].x - 2*A_WIDTH + 0.5;
\r
3000 else if( d_y == s_y ) {
\r
3001 int w = (d_x > s_x) ? +A_WIDTH*A_HEIGHT_FACTOR : -A_WIDTH*A_HEIGHT_FACTOR;
\r
3004 arrow[0].y = s_y + A_WIDTH + 0.5;
\r
3006 arrow[1].x = d_x - w;
\r
3007 arrow[1].y = s_y + A_WIDTH + 0.5;
\r
3009 arrow[2].x = d_x - w;
\r
3010 arrow[2].y = arrow[1].y + A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;
\r
3015 arrow[5].x = d_x - w;
\r
3016 arrow[5].y = arrow[1].y - 2*A_WIDTH + 0.5;
\r
3018 arrow[4].x = d_x - w;
\r
3019 arrow[4].y = arrow[5].y - A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;
\r
3022 arrow[6].y = arrow[1].y - 2*A_WIDTH + 0.5;
\r
3025 /* [AS] Needed a lot of paper for this! :-) */
\r
3026 dy = (double) (d_y - s_y) / (double) (d_x - s_x);
\r
3027 dx = (double) (s_x - d_x) / (double) (s_y - d_y);
\r
3029 j = sqrt( Sqr(A_WIDTH) / (1.0 + Sqr(dx)) );
\r
3031 k = sqrt( Sqr(A_WIDTH*A_HEIGHT_FACTOR) / (1.0 + Sqr(dy)) );
\r
3036 arrow[0].x = Round(x - j);
\r
3037 arrow[0].y = Round(y + j*dx);
\r
3039 arrow[1].x = Round(arrow[0].x + 2*j); // [HGM] prevent width to be affected by rounding twice
\r
3040 arrow[1].y = Round(arrow[0].y - 2*j*dx);
\r
3043 x = (double) d_x - k;
\r
3044 y = (double) d_y - k*dy;
\r
3047 x = (double) d_x + k;
\r
3048 y = (double) d_y + k*dy;
\r
3051 x = Round(x); y = Round(y); // [HGM] make sure width of shaft is rounded the same way on both ends
\r
3053 arrow[6].x = Round(x - j);
\r
3054 arrow[6].y = Round(y + j*dx);
\r
3056 arrow[2].x = Round(arrow[6].x + 2*j);
\r
3057 arrow[2].y = Round(arrow[6].y - 2*j*dx);
\r
3059 arrow[3].x = Round(arrow[2].x + j*(A_WIDTH_FACTOR-1));
\r
3060 arrow[3].y = Round(arrow[2].y - j*(A_WIDTH_FACTOR-1)*dx);
\r
3065 arrow[5].x = Round(arrow[6].x - j*(A_WIDTH_FACTOR-1));
\r
3066 arrow[5].y = Round(arrow[6].y + j*(A_WIDTH_FACTOR-1)*dx);
\r
3069 Polygon( hdc, arrow, 7 );
\r
3072 /* [AS] Draw an arrow between two squares */
\r
3073 VOID DrawArrowBetweenSquares( HDC hdc, int s_col, int s_row, int d_col, int d_row )
\r
3075 int s_x, s_y, d_x, d_y;
\r
3082 if( s_col == d_col && s_row == d_row ) {
\r
3086 /* Get source and destination points */
\r
3087 SquareToPos( s_row, s_col, &s_x, &s_y);
\r
3088 SquareToPos( d_row, d_col, &d_x, &d_y);
\r
3091 d_y += squareSize / 2 - squareSize / 4; // [HGM] round towards same centers on all sides!
\r
3093 else if( d_y < s_y ) {
\r
3094 d_y += squareSize / 2 + squareSize / 4;
\r
3097 d_y += squareSize / 2;
\r
3101 d_x += squareSize / 2 - squareSize / 4;
\r
3103 else if( d_x < s_x ) {
\r
3104 d_x += squareSize / 2 + squareSize / 4;
\r
3107 d_x += squareSize / 2;
\r
3110 s_x += squareSize / 2;
\r
3111 s_y += squareSize / 2;
\r
3113 /* Adjust width */
\r
3114 A_WIDTH = squareSize / 14.; //[HGM] make float
\r
3117 stLB.lbStyle = BS_SOLID;
\r
3118 stLB.lbColor = appData.highlightArrowColor;
\r
3121 hpen = CreatePen( PS_SOLID, 2, RGB(0x00,0x00,0x00) );
\r
3122 holdpen = SelectObject( hdc, hpen );
\r
3123 hbrush = CreateBrushIndirect( &stLB );
\r
3124 holdbrush = SelectObject( hdc, hbrush );
\r
3126 DrawArrowBetweenPoints( hdc, s_x, s_y, d_x, d_y );
\r
3128 SelectObject( hdc, holdpen );
\r
3129 SelectObject( hdc, holdbrush );
\r
3130 DeleteObject( hpen );
\r
3131 DeleteObject( hbrush );
\r
3134 BOOL HasHighlightInfo()
\r
3136 BOOL result = FALSE;
\r
3138 if( highlightInfo.sq[0].x >= 0 && highlightInfo.sq[0].y >= 0 &&
\r
3139 highlightInfo.sq[1].x >= 0 && highlightInfo.sq[1].y >= 0 )
\r
3147 BOOL IsDrawArrowEnabled()
\r
3149 BOOL result = FALSE;
\r
3151 if( appData.highlightMoveWithArrow && squareSize >= 32 ) {
\r
3158 VOID DrawArrowHighlight( HDC hdc )
\r
3160 if( IsDrawArrowEnabled() && HasHighlightInfo() ) {
\r
3161 DrawArrowBetweenSquares( hdc,
\r
3162 highlightInfo.sq[0].x, highlightInfo.sq[0].y,
\r
3163 highlightInfo.sq[1].x, highlightInfo.sq[1].y );
\r
3167 HRGN GetArrowHighlightClipRegion( HDC hdc )
\r
3169 HRGN result = NULL;
\r
3171 if( HasHighlightInfo() ) {
\r
3172 int x1, y1, x2, y2;
\r
3173 int sx, sy, dx, dy;
\r
3175 SquareToPos(highlightInfo.sq[0].y, highlightInfo.sq[0].x, &x1, &y1 );
\r
3176 SquareToPos(highlightInfo.sq[1].y, highlightInfo.sq[1].x, &x2, &y2 );
\r
3178 sx = MIN( x1, x2 );
\r
3179 sy = MIN( y1, y2 );
\r
3180 dx = MAX( x1, x2 ) + squareSize;
\r
3181 dy = MAX( y1, y2 ) + squareSize;
\r
3183 result = CreateRectRgn( sx, sy, dx, dy );
\r
3190 Warning: this function modifies the behavior of several other functions.
\r
3192 Basically, Winboard is optimized to avoid drawing the whole board if not strictly
\r
3193 needed. Unfortunately, the decision whether or not to perform a full or partial
\r
3194 repaint is scattered all over the place, which is not good for features such as
\r
3195 "arrow highlighting" that require a full repaint of the board.
\r
3197 So, I've tried to patch the code where I thought it made sense (e.g. after or during
\r
3198 user interaction, when speed is not so important) but especially to avoid errors
\r
3199 in the displayed graphics.
\r
3201 In such patched places, I always try refer to this function so there is a single
\r
3202 place to maintain knowledge.
\r
3204 To restore the original behavior, just return FALSE unconditionally.
\r
3206 BOOL IsFullRepaintPreferrable()
\r
3208 BOOL result = FALSE;
\r
3210 if( (appData.highlightLastMove || appData.highlightDragging) && IsDrawArrowEnabled() ) {
\r
3211 /* Arrow may appear on the board */
\r
3219 This function is called by DrawPosition to know whether a full repaint must
\r
3222 Only DrawPosition may directly call this function, which makes use of
\r
3223 some state information. Other function should call DrawPosition specifying
\r
3224 the repaint flag, and can use IsFullRepaintPreferrable if needed.
\r
3226 BOOL DrawPositionNeedsFullRepaint()
\r
3228 BOOL result = FALSE;
\r
3231 Probably a slightly better policy would be to trigger a full repaint
\r
3232 when animInfo.piece changes state (i.e. empty -> non-empty and viceversa),
\r
3233 but animation is fast enough that it's difficult to notice.
\r
3235 if( animInfo.piece == EmptySquare ) {
\r
3236 if( (appData.highlightLastMove || appData.highlightDragging) && IsDrawArrowEnabled() /*&& HasHighlightInfo()*/ ) {
\r
3245 DrawBoardOnDC(HDC hdc, Board board, HDC tmphdc)
\r
3247 int row, column, x, y, square_color, piece_color;
\r
3248 ChessSquare piece;
\r
3250 HDC texture_hdc = NULL;
\r
3252 /* [AS] Initialize background textures if needed */
\r
3253 if( liteBackTexture != NULL || darkBackTexture != NULL ) {
\r
3254 static int backTextureBoardSize; /* [HGM] boardsize: also new texture if board format changed */
\r
3255 if( backTextureSquareSize != squareSize
\r
3256 || backTextureBoardSize != BOARD_WIDTH+BOARD_FILES*BOARD_HEIGHT) {
\r
3257 backTextureBoardSize = BOARD_WIDTH+BOARD_FILES*BOARD_HEIGHT;
\r
3258 backTextureSquareSize = squareSize;
\r
3259 RebuildTextureSquareInfo();
\r
3262 texture_hdc = CreateCompatibleDC( hdc );
\r
3265 for (row = 0; row < BOARD_HEIGHT; row++) {
\r
3266 for (column = 0; column < BOARD_WIDTH; column++) {
\r
3268 SquareToPos(row, column, &x, &y);
\r
3270 piece = board[row][column];
\r
3272 square_color = ((column + row) % 2) == 1;
\r
3273 if( gameInfo.variant == VariantXiangqi ) {
\r
3274 square_color = !InPalace(row, column);
\r
3275 if(BOARD_HEIGHT&1) { if(row==BOARD_HEIGHT/2) square_color ^= 1; }
\r
3276 else if(row < BOARD_HEIGHT/2) square_color ^= 1;
\r
3278 piece_color = (int) piece < (int) BlackPawn;
\r
3281 /* [HGM] holdings file: light square or black */
\r
3282 if(column == BOARD_LEFT-2) {
\r
3283 if( row > BOARD_HEIGHT - gameInfo.holdingsSize - 1 )
\r
3286 DisplayHoldingsCount(hdc, x, y, 0, 0); /* black out */
\r
3290 if(column == BOARD_RGHT + 1 ) {
\r
3291 if( row < gameInfo.holdingsSize )
\r
3294 DisplayHoldingsCount(hdc, x, y, 0, 0);
\r
3298 if(column == BOARD_LEFT-1 ) /* left align */
\r
3299 DisplayHoldingsCount(hdc, x, y, flipView, (int) board[row][column]);
\r
3300 else if( column == BOARD_RGHT) /* right align */
\r
3301 DisplayHoldingsCount(hdc, x, y, !flipView, (int) board[row][column]);
\r
3303 if (appData.monoMode) {
\r
3304 if (piece == EmptySquare) {
\r
3305 BitBlt(hdc, x, y, squareSize, squareSize, 0, 0, 0,
\r
3306 square_color ? WHITENESS : BLACKNESS);
\r
3308 DrawPieceOnDC(hdc, piece, piece_color, square_color, x, y, tmphdc);
\r
3311 else if( backTextureSquareInfo[row][column].mode > 0 ) {
\r
3312 /* [AS] Draw the square using a texture bitmap */
\r
3313 HBITMAP hbm = SelectObject( texture_hdc, square_color ? liteBackTexture : darkBackTexture );
\r
3314 int r = row, c = column; // [HGM] do not flip board in flipView
\r
3315 if(flipView) { r = BOARD_HEIGHT-1 - r; c = BOARD_WIDTH-1 - c; }
\r
3318 squareSize, squareSize,
\r
3321 backTextureSquareInfo[r][c].mode,
\r
3322 backTextureSquareInfo[r][c].x,
\r
3323 backTextureSquareInfo[r][c].y );
\r
3325 SelectObject( texture_hdc, hbm );
\r
3327 if (piece != EmptySquare) {
\r
3328 DrawPieceOnDC(hdc, piece, piece_color, -1, x, y, tmphdc);
\r
3332 HBRUSH brush = square_color ? lightSquareBrush : darkSquareBrush;
\r
3334 oldBrush = SelectObject(hdc, brush );
\r
3335 BitBlt(hdc, x, y, squareSize, squareSize, 0, 0, 0, PATCOPY);
\r
3336 SelectObject(hdc, oldBrush);
\r
3337 if (piece != EmptySquare)
\r
3338 DrawPieceOnDC(hdc, piece, piece_color, -1, x, y, tmphdc);
\r
3343 if( texture_hdc != NULL ) {
\r
3344 DeleteDC( texture_hdc );
\r
3348 int saveDiagFlag = 0; FILE *diagFile; // [HGM] diag
\r
3349 void fputDW(FILE *f, int x)
\r
3351 fputc(x & 255, f);
\r
3352 fputc(x>>8 & 255, f);
\r
3353 fputc(x>>16 & 255, f);
\r
3354 fputc(x>>24 & 255, f);
\r
3357 #define MAX_CLIPS 200 /* more than enough */
\r
3360 DrawLogoOnDC(HDC hdc, RECT logoRect, HBITMAP logo)
\r
3362 // HBITMAP bufferBitmap;
\r
3367 int w = 100, h = 50;
\r
3369 if(logo == NULL) return;
\r
3370 // GetClientRect(hwndMain, &Rect);
\r
3371 // bufferBitmap = CreateCompatibleBitmap(hdc, Rect.right-Rect.left+1,
\r
3372 // Rect.bottom-Rect.top+1);
\r
3373 tmphdc = CreateCompatibleDC(hdc);
\r
3374 hbm = SelectObject(tmphdc, logo);
\r
3375 if( GetObject( logo, sizeof(bi), &bi ) > 0 ) {
\r
3379 StretchBlt(hdc, logoRect.left, logoRect.top, logoRect.right - logoRect.left,
\r
3380 logoRect.bottom - logoRect.top, tmphdc, 0, 0, w, h, SRCCOPY);
\r
3381 SelectObject(tmphdc, hbm);
\r
3385 static HDC hdcSeek;
\r
3387 // [HGM] seekgraph
\r
3388 void DrawSeekAxis( int x, int y, int xTo, int yTo )
\r
3391 HPEN hp = SelectObject( hdcSeek, gridPen );
\r
3392 MoveToEx( hdcSeek, boardRect.left+x, boardRect.top+y, &stPt );
\r
3393 LineTo( hdcSeek, boardRect.left+xTo, boardRect.top+yTo );
\r
3394 SelectObject( hdcSeek, hp );
\r
3397 // front-end wrapper for drawing functions to do rectangles
\r
3398 void DrawSeekBackground( int left, int top, int right, int bottom )
\r
3403 if (hdcSeek == NULL) {
\r
3404 hdcSeek = GetDC(hwndMain);
\r
3405 if (!appData.monoMode) {
\r
3406 SelectPalette(hdcSeek, hPal, FALSE);
\r
3407 RealizePalette(hdcSeek);
\r
3410 hp = SelectObject( hdcSeek, gridPen );
\r
3411 rc.top = boardRect.top+top; rc.left = boardRect.left+left;
\r
3412 rc.bottom = boardRect.top+bottom; rc.right = boardRect.left+right;
\r
3413 FillRect( hdcSeek, &rc, lightSquareBrush );
\r
3414 SelectObject( hdcSeek, hp );
\r
3417 // front-end wrapper for putting text in graph
\r
3418 void DrawSeekText(char *buf, int x, int y)
\r
3421 SetBkMode( hdcSeek, TRANSPARENT );
\r
3422 GetTextExtentPoint32( hdcSeek, buf, strlen(buf), &stSize );
\r
3423 TextOut( hdcSeek, boardRect.left+x-3, boardRect.top+y-stSize.cy/2, buf, strlen(buf) );
\r
3426 void DrawSeekDot(int x, int y, int color)
\r
3428 int square = color & 0x80;
\r
3429 HBRUSH oldBrush = SelectObject(hdcSeek,
\r
3430 color == 0 ? markerBrush : color == 1 ? darkSquareBrush : explodeBrush);
\r
3433 Rectangle(hdcSeek, boardRect.left+x - squareSize/9, boardRect.top+y - squareSize/9,
\r
3434 boardRect.left+x + squareSize/9, boardRect.top+y + squareSize/9);
\r
3436 Ellipse(hdcSeek, boardRect.left+x - squareSize/8, boardRect.top+y - squareSize/8,
\r
3437 boardRect.left+x + squareSize/8, boardRect.top+y + squareSize/8);
\r
3438 SelectObject(hdcSeek, oldBrush);
\r
3442 HDCDrawPosition(HDC hdc, BOOLEAN repaint, Board board)
\r
3444 static Board lastReq[2], lastDrawn[2];
\r
3445 static HighlightInfo lastDrawnHighlight, lastDrawnPremove;
\r
3446 static int lastDrawnFlipView = 0;
\r
3447 static int lastReqValid[2] = {0, 0}, lastDrawnValid[2] = {0, 0};
\r
3448 int releaseDC, x, y, x2, y2, row, column, num_clips = 0, i;
\r
3451 HBITMAP bufferBitmap;
\r
3452 HBITMAP oldBitmap;
\r
3454 HRGN clips[MAX_CLIPS];
\r
3455 ChessSquare dragged_piece = EmptySquare;
\r
3456 int nr = twoBoards*partnerUp;
\r
3458 /* I'm undecided on this - this function figures out whether a full
\r
3459 * repaint is necessary on its own, so there's no real reason to have the
\r
3460 * caller tell it that. I think this can safely be set to FALSE - but
\r
3461 * if we trust the callers not to request full repaints unnessesarily, then
\r
3462 * we could skip some clipping work. In other words, only request a full
\r
3463 * redraw when the majority of pieces have changed positions (ie. flip,
\r
3464 * gamestart and similar) --Hawk
\r
3466 Boolean fullrepaint = repaint;
\r
3468 if(DrawSeekGraph()) return; // [HG} seekgraph: suppress printing board if seek graph up
\r
3470 if( DrawPositionNeedsFullRepaint() ) {
\r
3471 fullrepaint = TRUE;
\r
3474 if (board == NULL) {
\r
3475 if (!lastReqValid[nr]) {
\r
3478 board = lastReq[nr];
\r
3480 CopyBoard(lastReq[nr], board);
\r
3481 lastReqValid[nr] = 1;
\r
3484 if (doingSizing) {
\r
3488 if (IsIconic(hwndMain)) {
\r
3492 if (hdc == NULL) {
\r
3493 hdc = GetDC(hwndMain);
\r
3494 if (!appData.monoMode) {
\r
3495 SelectPalette(hdc, hPal, FALSE);
\r
3496 RealizePalette(hdc);
\r
3500 releaseDC = FALSE;
\r
3503 /* Create some work-DCs */
\r
3504 hdcmem = CreateCompatibleDC(hdc);
\r
3505 tmphdc = CreateCompatibleDC(hdc);
\r
3507 /* If dragging is in progress, we temporarely remove the piece */
\r
3508 /* [HGM] or temporarily decrease count if stacked */
\r
3509 /* !! Moved to before board compare !! */
\r
3510 if (dragInfo.from.x >= 0 && dragInfo.pos.x >= 0) {
\r
3511 dragged_piece = board[dragInfo.from.y][dragInfo.from.x];
\r
3512 if(dragInfo.from.x == BOARD_LEFT-2 ) {
\r
3513 if(--board[dragInfo.from.y][dragInfo.from.x+1] == 0 )
\r
3514 board[dragInfo.from.y][dragInfo.from.x] = EmptySquare;
\r
3516 if(dragInfo.from.x == BOARD_RGHT+1) {
\r
3517 if(--board[dragInfo.from.y][dragInfo.from.x-1] == 0 )
\r
3518 board[dragInfo.from.y][dragInfo.from.x] = EmptySquare;
\r
3520 board[dragInfo.from.y][dragInfo.from.x] = gatingPiece;
\r
3523 /* Figure out which squares need updating by comparing the
\r
3524 * newest board with the last drawn board and checking if
\r
3525 * flipping has changed.
\r
3527 if (!fullrepaint && lastDrawnValid[nr] && (nr == 1 || lastDrawnFlipView == flipView)) {
\r
3528 for (row = 0; row < BOARD_HEIGHT; row++) { /* [HGM] true size, not 8 */
\r
3529 for (column = 0; column < BOARD_WIDTH; column++) {
\r
3530 if (lastDrawn[nr][row][column] != board[row][column]) {
\r
3531 SquareToPos(row, column, &x, &y);
\r
3532 clips[num_clips++] =
\r
3533 CreateRectRgn(x, y, x + squareSize, y + squareSize);
\r
3537 if(nr == 0) { // [HGM] dual: no highlights on second board
\r
3538 for (i=0; i<2; i++) {
\r
3539 if (lastDrawnHighlight.sq[i].x != highlightInfo.sq[i].x ||
\r
3540 lastDrawnHighlight.sq[i].y != highlightInfo.sq[i].y) {
\r
3541 if (lastDrawnHighlight.sq[i].x >= 0 &&
\r
3542 lastDrawnHighlight.sq[i].y >= 0) {
\r
3543 SquareToPos(lastDrawnHighlight.sq[i].y,
\r
3544 lastDrawnHighlight.sq[i].x, &x, &y);
\r
3545 clips[num_clips++] =
\r
3546 CreateRectRgn(x - lineGap, y - lineGap,
\r
3547 x + squareSize + lineGap, y + squareSize + lineGap);
\r
3549 if (highlightInfo.sq[i].x >= 0 && highlightInfo.sq[i].y >= 0) {
\r
3550 SquareToPos(highlightInfo.sq[i].y, highlightInfo.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
3557 for (i=0; i<2; i++) {
\r
3558 if (lastDrawnPremove.sq[i].x != premoveHighlightInfo.sq[i].x ||
\r
3559 lastDrawnPremove.sq[i].y != premoveHighlightInfo.sq[i].y) {
\r
3560 if (lastDrawnPremove.sq[i].x >= 0 &&
\r
3561 lastDrawnPremove.sq[i].y >= 0) {
\r
3562 SquareToPos(lastDrawnPremove.sq[i].y,
\r
3563 lastDrawnPremove.sq[i].x, &x, &y);
\r
3564 clips[num_clips++] =
\r
3565 CreateRectRgn(x - lineGap, y - lineGap,
\r
3566 x + squareSize + lineGap, y + squareSize + lineGap);
\r
3568 if (premoveHighlightInfo.sq[i].x >= 0 &&
\r
3569 premoveHighlightInfo.sq[i].y >= 0) {
\r
3570 SquareToPos(premoveHighlightInfo.sq[i].y,
\r
3571 premoveHighlightInfo.sq[i].x, &x, &y);
\r
3572 clips[num_clips++] =
\r
3573 CreateRectRgn(x - lineGap, y - lineGap,
\r
3574 x + squareSize + lineGap, y + squareSize + lineGap);
\r
3578 } else { // nr == 1
\r
3579 partnerHighlightInfo.sq[0].y = board[EP_STATUS-4];
\r
3580 partnerHighlightInfo.sq[0].x = board[EP_STATUS-3];
\r
3581 partnerHighlightInfo.sq[1].y = board[EP_STATUS-2];
\r
3582 partnerHighlightInfo.sq[1].x = board[EP_STATUS-1];
\r
3583 for (i=0; i<2; i++) {
\r
3584 if (partnerHighlightInfo.sq[i].x >= 0 &&
\r
3585 partnerHighlightInfo.sq[i].y >= 0) {
\r
3586 SquareToPos(partnerHighlightInfo.sq[i].y,
\r
3587 partnerHighlightInfo.sq[i].x, &x, &y);
\r
3588 clips[num_clips++] =
\r
3589 CreateRectRgn(x - lineGap, y - lineGap,
\r
3590 x + squareSize + lineGap, y + squareSize + lineGap);
\r
3592 if (oldPartnerHighlight.sq[i].x >= 0 &&
\r
3593 oldPartnerHighlight.sq[i].y >= 0) {
\r
3594 SquareToPos(oldPartnerHighlight.sq[i].y,
\r
3595 oldPartnerHighlight.sq[i].x, &x, &y);
\r
3596 clips[num_clips++] =
\r
3597 CreateRectRgn(x - lineGap, y - lineGap,
\r
3598 x + squareSize + lineGap, y + squareSize + lineGap);
\r
3603 fullrepaint = TRUE;
\r
3606 /* Create a buffer bitmap - this is the actual bitmap
\r
3607 * being written to. When all the work is done, we can
\r
3608 * copy it to the real DC (the screen). This avoids
\r
3609 * the problems with flickering.
\r
3611 GetClientRect(hwndMain, &Rect);
\r
3612 bufferBitmap = CreateCompatibleBitmap(hdc, Rect.right-Rect.left+1,
\r
3613 Rect.bottom-Rect.top+1);
\r
3614 oldBitmap = SelectObject(hdcmem, bufferBitmap);
\r
3615 if (!appData.monoMode) {
\r
3616 SelectPalette(hdcmem, hPal, FALSE);
\r
3619 /* Create clips for dragging */
\r
3620 if (!fullrepaint) {
\r
3621 if (dragInfo.from.x >= 0) {
\r
3622 SquareToPos(dragInfo.from.y, dragInfo.from.x, &x, &y);
\r
3623 clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);
\r
3625 if (dragInfo.start.x >= 0) {
\r
3626 SquareToPos(dragInfo.start.y, dragInfo.start.x, &x, &y);
\r
3627 clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);
\r
3629 if (dragInfo.pos.x >= 0) {
\r
3630 x = dragInfo.pos.x - squareSize / 2;
\r
3631 y = dragInfo.pos.y - squareSize / 2;
\r
3632 clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);
\r
3634 if (dragInfo.lastpos.x >= 0) {
\r
3635 x = dragInfo.lastpos.x - squareSize / 2;
\r
3636 y = dragInfo.lastpos.y - squareSize / 2;
\r
3637 clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);
\r
3641 /* Are we animating a move?
\r
3643 * - remove the piece from the board (temporarely)
\r
3644 * - calculate the clipping region
\r
3646 if (!fullrepaint) {
\r
3647 if (animInfo.piece != EmptySquare) {
\r
3648 board[animInfo.from.y][animInfo.from.x] = EmptySquare;
\r
3649 x = boardRect.left + animInfo.lastpos.x;
\r
3650 y = boardRect.top + animInfo.lastpos.y;
\r
3651 x2 = boardRect.left + animInfo.pos.x;
\r
3652 y2 = boardRect.top + animInfo.pos.y;
\r
3653 clips[num_clips++] = CreateRectRgn(MIN(x,x2), MIN(y,y2), MAX(x,x2)+squareSize, MAX(y,y2)+squareSize);
\r
3654 /* Slight kludge. The real problem is that after AnimateMove is
\r
3655 done, the position on the screen does not match lastDrawn.
\r
3656 This currently causes trouble only on e.p. captures in
\r
3657 atomic, where the piece moves to an empty square and then
\r
3658 explodes. The old and new positions both had an empty square
\r
3659 at the destination, but animation has drawn a piece there and
\r
3660 we have to remember to erase it. [HGM] moved until after setting lastDrawn */
\r
3661 lastDrawn[0][animInfo.to.y][animInfo.to.x] = animInfo.piece;
\r
3665 /* No clips? Make sure we have fullrepaint set to TRUE */
\r
3666 if (num_clips == 0)
\r
3667 fullrepaint = TRUE;
\r
3669 /* Set clipping on the memory DC */
\r
3670 if (!fullrepaint) {
\r
3671 SelectClipRgn(hdcmem, clips[0]);
\r
3672 for (x = 1; x < num_clips; x++) {
\r
3673 if (ExtSelectClipRgn(hdcmem, clips[x], RGN_OR) == ERROR)
\r
3674 abort(); // this should never ever happen!
\r
3678 /* Do all the drawing to the memory DC */
\r
3679 if(explodeInfo.radius) { // [HGM] atomic
\r
3681 int x, y, r=(explodeInfo.radius * squareSize)/100;
\r
3682 ChessSquare piece = board[explodeInfo.fromY][explodeInfo.fromX];
\r
3683 board[explodeInfo.fromY][explodeInfo.fromX] = EmptySquare; // suppress display of capturer
\r
3684 SquareToPos(explodeInfo.toY, explodeInfo.toX, &x, &y);
\r
3685 x += squareSize/2;
\r
3686 y += squareSize/2;
\r
3687 if(!fullrepaint) {
\r
3688 clips[num_clips] = CreateRectRgn(x-r, y-r, x+r, y+r);
\r
3689 ExtSelectClipRgn(hdcmem, clips[num_clips++], RGN_OR);
\r
3691 DrawGridOnDC(hdcmem);
\r
3692 DrawHighlightsOnDC(hdcmem, &highlightInfo, HIGHLIGHT_PEN);
\r
3693 DrawHighlightsOnDC(hdcmem, &premoveHighlightInfo, PREMOVE_PEN);
\r
3694 DrawBoardOnDC(hdcmem, board, tmphdc);
\r
3695 board[explodeInfo.fromY][explodeInfo.fromX] = piece;
\r
3696 oldBrush = SelectObject(hdcmem, explodeBrush);
\r
3697 Ellipse(hdcmem, x-r, y-r, x+r, y+r);
\r
3698 SelectObject(hdcmem, oldBrush);
\r
3700 DrawGridOnDC(hdcmem);
\r
3701 if(nr == 0) { // [HGM] dual: decide which highlights to draw
\r
3702 DrawHighlightsOnDC(hdcmem, &highlightInfo, HIGHLIGHT_PEN);
\r
3703 DrawHighlightsOnDC(hdcmem, &premoveHighlightInfo, PREMOVE_PEN);
\r
3705 DrawHighlightsOnDC(hdcmem, &partnerHighlightInfo, HIGHLIGHT_PEN);
\r
3706 oldPartnerHighlight = partnerHighlightInfo;
\r
3708 DrawBoardOnDC(hdcmem, board, tmphdc);
\r
3710 if(nr == 0) // [HGM] dual: markers only on left board
\r
3711 for (row = 0; row < BOARD_HEIGHT; row++) {
\r
3712 for (column = 0; column < BOARD_WIDTH; column++) {
\r
3713 if (marker[row][column]) { // marker changes only occur with full repaint!
\r
3714 HBRUSH oldBrush = SelectObject(hdcmem,
\r
3715 marker[row][column] == 2 ? markerBrush : explodeBrush);
\r
3716 SquareToPos(row, column, &x, &y);
\r
3717 Ellipse(hdcmem, x + squareSize/4, y + squareSize/4,
\r
3718 x + 3*squareSize/4, y + 3*squareSize/4);
\r
3719 SelectObject(hdcmem, oldBrush);
\r
3724 HBITMAP whiteLogo = (HBITMAP) first.programLogo, blackLogo = (HBITMAP) second.programLogo;
\r
3725 if(appData.autoLogo) {
\r
3727 switch(gameMode) { // pick logos based on game mode
\r
3728 case IcsObserving:
\r
3729 whiteLogo = second.programLogo; // ICS logo
\r
3730 blackLogo = second.programLogo;
\r
3733 case IcsPlayingWhite:
\r
3734 if(!appData.zippyPlay) whiteLogo = userLogo;
\r
3735 blackLogo = second.programLogo; // ICS logo
\r
3737 case IcsPlayingBlack:
\r
3738 whiteLogo = second.programLogo; // ICS logo
\r
3739 blackLogo = appData.zippyPlay ? first.programLogo : userLogo;
\r
3741 case TwoMachinesPlay:
\r
3742 if(first.twoMachinesColor[0] == 'b') {
\r
3743 whiteLogo = second.programLogo;
\r
3744 blackLogo = first.programLogo;
\r
3747 case MachinePlaysWhite:
\r
3748 blackLogo = userLogo;
\r
3750 case MachinePlaysBlack:
\r
3751 whiteLogo = userLogo;
\r
3752 blackLogo = first.programLogo;
\r
3755 DrawLogoOnDC(hdc, leftLogoRect, flipClock ? blackLogo : whiteLogo);
\r
3756 DrawLogoOnDC(hdc, rightLogoRect, flipClock ? whiteLogo : blackLogo);
\r
3759 if( appData.highlightMoveWithArrow ) {
\r
3760 DrawArrowHighlight(hdcmem);
\r
3763 DrawCoordsOnDC(hdcmem);
\r
3765 CopyBoard(lastDrawn[nr], board); /* [HGM] Moved to here from end of routine, */
\r
3766 /* to make sure lastDrawn contains what is actually drawn */
\r
3768 /* Put the dragged piece back into place and draw it (out of place!) */
\r
3769 if (dragged_piece != EmptySquare) {
\r
3770 /* [HGM] or restack */
\r
3771 if(dragInfo.from.x == BOARD_LEFT-2 )
\r
3772 board[dragInfo.from.y][dragInfo.from.x+1]++;
\r
3774 if(dragInfo.from.x == BOARD_RGHT+1 )
\r
3775 board[dragInfo.from.y][dragInfo.from.x-1]++;
\r
3776 board[dragInfo.from.y][dragInfo.from.x] = dragged_piece;
\r
3777 x = dragInfo.pos.x - squareSize / 2;
\r
3778 y = dragInfo.pos.y - squareSize / 2;
\r
3779 DrawPieceOnDC(hdcmem, dragged_piece,
\r
3780 ((int) dragged_piece < (int) BlackPawn),
\r
3781 (dragInfo.from.y + dragInfo.from.x) % 2, x, y, tmphdc);
\r
3784 /* Put the animated piece back into place and draw it */
\r
3785 if (animInfo.piece != EmptySquare) {
\r
3786 board[animInfo.from.y][animInfo.from.x] = animInfo.piece;
\r
3787 x = boardRect.left + animInfo.pos.x;
\r
3788 y = boardRect.top + animInfo.pos.y;
\r
3789 DrawPieceOnDC(hdcmem, animInfo.piece,
\r
3790 ((int) animInfo.piece < (int) BlackPawn),
\r
3791 (animInfo.from.y + animInfo.from.x) % 2, x, y, tmphdc);
\r
3794 /* Release the bufferBitmap by selecting in the old bitmap
\r
3795 * and delete the memory DC
\r
3797 SelectObject(hdcmem, oldBitmap);
\r
3800 /* Set clipping on the target DC */
\r
3801 if (!fullrepaint) {
\r
3802 if(nr == 1) for (x = 0; x < num_clips; x++) { // [HGM] dual: translate clips
\r
3804 GetRgnBox(clips[x], &rect);
\r
3805 DeleteObject(clips[x]);
\r
3806 clips[x] = CreateRectRgn(rect.left + wpMain.width/2, rect.top,
\r
3807 rect.right + wpMain.width/2, rect.bottom);
\r
3809 SelectClipRgn(hdc, clips[0]);
\r
3810 for (x = 1; x < num_clips; x++) {
\r
3811 if (ExtSelectClipRgn(hdc, clips[x], RGN_OR) == ERROR)
\r
3812 abort(); // this should never ever happen!
\r
3816 /* Copy the new bitmap onto the screen in one go.
\r
3817 * This way we avoid any flickering
\r
3819 oldBitmap = SelectObject(tmphdc, bufferBitmap);
\r
3820 BitBlt(hdc, boardRect.left + twoBoards*partnerUp*wpMain.width/2, boardRect.top, // [HGM] dual
\r
3821 boardRect.right - boardRect.left,
\r
3822 boardRect.bottom - boardRect.top,
\r
3823 tmphdc, boardRect.left, boardRect.top, SRCCOPY);
\r
3824 if(saveDiagFlag) {
\r
3825 BITMAP b; int i, j=0, m, w, wb, fac=0; char pData[1000000];
\r
3826 BITMAPINFOHEADER bih; int color[16], nrColors=0;
\r
3828 GetObject(bufferBitmap, sizeof(b), &b);
\r
3829 if(b.bmWidthBytes*b.bmHeight <= 990000) {
\r
3830 bih.biSize = sizeof(BITMAPINFOHEADER);
\r
3831 bih.biWidth = b.bmWidth;
\r
3832 bih.biHeight = b.bmHeight;
\r
3834 bih.biBitCount = b.bmBitsPixel;
\r
3835 bih.biCompression = 0;
\r
3836 bih.biSizeImage = b.bmWidthBytes*b.bmHeight;
\r
3837 bih.biXPelsPerMeter = 0;
\r
3838 bih.biYPelsPerMeter = 0;
\r
3839 bih.biClrUsed = 0;
\r
3840 bih.biClrImportant = 0;
\r
3841 // fprintf(diagFile, "t=%d\nw=%d\nh=%d\nB=%d\nP=%d\nX=%d\n",
\r
3842 // b.bmType, b.bmWidth, b.bmHeight, b.bmWidthBytes, b.bmPlanes, b.bmBitsPixel);
\r
3843 GetDIBits(tmphdc,bufferBitmap,0,b.bmHeight,pData,(BITMAPINFO*)&bih,DIB_RGB_COLORS);
\r
3844 // fprintf(diagFile, "%8x\n", (int) pData);
\r
3846 wb = b.bmWidthBytes;
\r
3848 for(i=0; i<wb*(b.bmHeight - boardRect.top + OUTER_MARGIN)>>2; i++) {
\r
3849 int k = ((int*) pData)[i];
\r
3850 for(j=0; j<nrColors; j++) if(color[j] == k) break;
\r
3851 if(j >= 16) break;
\r
3853 if(j >= nrColors) nrColors = j+1;
\r
3855 if(j<16) { // 16 colors is enough. Compress to 4 bits per pixel
\r
3857 for(i=0; i<b.bmHeight - boardRect.top + OUTER_MARGIN; i++) {
\r
3858 for(w=0; w<(wb>>2); w+=2) {
\r
3859 int k = ((int*) pData)[(wb*i>>2) + w];
\r
3860 for(j=0; j<nrColors; j++) if(color[j] == k) break;
\r
3861 k = ((int*) pData)[(wb*i>>2) + w + 1];
\r
3862 for(m=0; m<nrColors; m++) if(color[m] == k) break;
\r
3863 pData[p++] = m | j<<4;
\r
3865 while(p&3) pData[p++] = 0;
\r
3868 wb = ((wb+31)>>5)<<2;
\r
3870 // write BITMAPFILEHEADER
\r
3871 fprintf(diagFile, "BM");
\r
3872 fputDW(diagFile, wb*(b.bmHeight - boardRect.top + OUTER_MARGIN)+0x36 + (fac?64:0));
\r
3873 fputDW(diagFile, 0);
\r
3874 fputDW(diagFile, 0x36 + (fac?64:0));
\r
3875 // write BITMAPINFOHEADER
\r
3876 fputDW(diagFile, 40);
\r
3877 fputDW(diagFile, b.bmWidth);
\r
3878 fputDW(diagFile, b.bmHeight - boardRect.top + OUTER_MARGIN);
\r
3879 if(fac) fputDW(diagFile, 0x040001); // planes and bits/pixel
\r
3880 else fputDW(diagFile, 0x200001); // planes and bits/pixel
\r
3881 fputDW(diagFile, 0);
\r
3882 fputDW(diagFile, 0);
\r
3883 fputDW(diagFile, 0);
\r
3884 fputDW(diagFile, 0);
\r
3885 fputDW(diagFile, 0);
\r
3886 fputDW(diagFile, 0);
\r
3887 // write color table
\r
3889 for(i=0; i<16; i++) fputDW(diagFile, color[i]);
\r
3890 // write bitmap data
\r
3891 for(i=0; i<wb*(b.bmHeight - boardRect.top + OUTER_MARGIN); i++)
\r
3892 fputc(pData[i], diagFile);
\r
3896 SelectObject(tmphdc, oldBitmap);
\r
3898 /* Massive cleanup */
\r
3899 for (x = 0; x < num_clips; x++)
\r
3900 DeleteObject(clips[x]);
\r
3903 DeleteObject(bufferBitmap);
\r
3906 ReleaseDC(hwndMain, hdc);
\r
3908 if (lastDrawnFlipView != flipView && nr == 0) {
\r
3910 CheckMenuItem(GetMenu(hwndMain),IDM_FlipView, MF_BYCOMMAND|MF_CHECKED);
\r
3912 CheckMenuItem(GetMenu(hwndMain),IDM_FlipView, MF_BYCOMMAND|MF_UNCHECKED);
\r
3915 /* CopyBoard(lastDrawn, board);*/
\r
3916 lastDrawnHighlight = highlightInfo;
\r
3917 lastDrawnPremove = premoveHighlightInfo;
\r
3918 lastDrawnFlipView = flipView;
\r
3919 lastDrawnValid[nr] = 1;
\r
3922 /* [HGM] diag: Save the current board display to the given open file and close the file */
\r
3927 saveDiagFlag = 1; diagFile = f;
\r
3928 HDCDrawPosition(NULL, TRUE, NULL);
\r
3932 // if(f != NULL) fprintf(f, "Sorry, but this feature is still in preparation\n");
\r
3939 /*---------------------------------------------------------------------------*\
\r
3940 | CLIENT PAINT PROCEDURE
\r
3941 | This is the main event-handler for the WM_PAINT message.
\r
3943 \*---------------------------------------------------------------------------*/
\r
3945 PaintProc(HWND hwnd)
\r
3951 if((hdc = BeginPaint(hwnd, &ps))) {
\r
3952 if (IsIconic(hwnd)) {
\r
3953 DrawIcon(hdc, 2, 2, iconCurrent);
\r
3955 if (!appData.monoMode) {
\r
3956 SelectPalette(hdc, hPal, FALSE);
\r
3957 RealizePalette(hdc);
\r
3959 HDCDrawPosition(hdc, 1, NULL);
\r
3960 if(twoBoards) { // [HGM] dual: also redraw other board in other orientation
\r
3961 flipView = !flipView; partnerUp = !partnerUp;
\r
3962 HDCDrawPosition(hdc, 1, NULL);
\r
3963 flipView = !flipView; partnerUp = !partnerUp;
\r
3966 SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);
\r
3967 ExtTextOut(hdc, messageRect.left, messageRect.top,
\r
3968 ETO_CLIPPED|ETO_OPAQUE,
\r
3969 &messageRect, messageText, strlen(messageText), NULL);
\r
3970 SelectObject(hdc, oldFont);
\r
3971 DisplayBothClocks();
\r
3973 EndPaint(hwnd,&ps);
\r
3981 * If the user selects on a border boundary, return -1; if off the board,
\r
3982 * return -2. Otherwise map the event coordinate to the square.
\r
3983 * The offset boardRect.left or boardRect.top must already have been
\r
3984 * subtracted from x.
\r
3986 int EventToSquare(x, limit)
\r
3994 if ((x % (squareSize + lineGap)) >= squareSize)
\r
3996 x /= (squareSize + lineGap);
\r
4008 DropEnable dropEnables[] = {
\r
4009 { 'P', DP_Pawn, N_("Pawn") },
\r
4010 { 'N', DP_Knight, N_("Knight") },
\r
4011 { 'B', DP_Bishop, N_("Bishop") },
\r
4012 { 'R', DP_Rook, N_("Rook") },
\r
4013 { 'Q', DP_Queen, N_("Queen") },
\r
4017 SetupDropMenu(HMENU hmenu)
\r
4019 int i, count, enable;
\r
4021 extern char white_holding[], black_holding[];
\r
4022 char item[MSG_SIZ];
\r
4024 for (i=0; i<sizeof(dropEnables)/sizeof(DropEnable); i++) {
\r
4025 p = strchr(gameMode == IcsPlayingWhite ? white_holding : black_holding,
\r
4026 dropEnables[i].piece);
\r
4028 while (p && *p++ == dropEnables[i].piece) count++;
\r
4029 snprintf(item, MSG_SIZ, "%s %d", T_(dropEnables[i].name), count);
\r
4030 enable = count > 0 || !appData.testLegality
\r
4031 /*!!temp:*/ || (gameInfo.variant == VariantCrazyhouse
\r
4032 && !appData.icsActive);
\r
4033 ModifyMenu(hmenu, dropEnables[i].command,
\r
4034 MF_BYCOMMAND | (enable ? MF_ENABLED : MF_GRAYED) | MF_STRING,
\r
4035 dropEnables[i].command, item);
\r
4039 void DragPieceBegin(int x, int y)
\r
4041 dragInfo.lastpos.x = boardRect.left + x;
\r
4042 dragInfo.lastpos.y = boardRect.top + y;
\r
4043 dragInfo.from.x = fromX;
\r
4044 dragInfo.from.y = fromY;
\r
4045 dragInfo.start = dragInfo.from;
\r
4046 SetCapture(hwndMain);
\r
4049 void DragPieceEnd(int x, int y)
\r
4052 dragInfo.start.x = dragInfo.start.y = -1;
\r
4053 dragInfo.from = dragInfo.start;
\r
4054 dragInfo.pos = dragInfo.lastpos = dragInfo.start;
\r
4057 /* Event handler for mouse messages */
\r
4059 MouseEvent(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
\r
4063 static int recursive = 0;
\r
4065 BOOLEAN forceFullRepaint = IsFullRepaintPreferrable(); /* [AS] */
\r
4068 if (message == WM_MBUTTONUP) {
\r
4069 /* Hideous kludge to fool TrackPopupMenu into paying attention
\r
4070 to the middle button: we simulate pressing the left button too!
\r
4072 PostMessage(hwnd, WM_LBUTTONDOWN, wParam, lParam);
\r
4073 PostMessage(hwnd, WM_LBUTTONUP, wParam, lParam);
\r
4079 pt.x = LOWORD(lParam);
\r
4080 pt.y = HIWORD(lParam);
\r
4081 x = EventToSquare(pt.x - boardRect.left, BOARD_WIDTH);
\r
4082 y = EventToSquare(pt.y - boardRect.top, BOARD_HEIGHT);
\r
4083 if (!flipView && y >= 0) {
\r
4084 y = BOARD_HEIGHT - 1 - y;
\r
4086 if (flipView && x >= 0) {
\r
4087 x = BOARD_WIDTH - 1 - x;
\r
4090 shiftKey = GetKeyState(VK_SHIFT) < 0; // [HGM] remember last shift status
\r
4092 switch (message) {
\r
4093 case WM_LBUTTONDOWN:
\r
4094 if (PtInRect((LPRECT) &whiteRect, pt)) {
\r
4095 if (gameMode == EditPosition) {
\r
4096 SetWhiteToPlayEvent();
\r
4097 } else if (gameMode == EditGame || GetKeyState(VK_SHIFT) < 0) {
\r
4098 AdjustClock(flipClock, -1);
\r
4099 } else if (gameMode == IcsPlayingBlack ||
\r
4100 gameMode == MachinePlaysWhite) {
\r
4103 } else if (PtInRect((LPRECT) &blackRect, pt)) {
\r
4104 if (gameMode == EditPosition) {
\r
4105 SetBlackToPlayEvent();
\r
4106 } else if (gameMode == EditGame || GetKeyState(VK_SHIFT) < 0) {
\r
4107 AdjustClock(!flipClock, -1);
\r
4108 } else if (gameMode == IcsPlayingWhite ||
\r
4109 gameMode == MachinePlaysBlack) {
\r
4113 dragInfo.start.x = dragInfo.start.y = -1;
\r
4114 dragInfo.from = dragInfo.start;
\r
4115 if(fromX == -1 && frozen) { // not sure where this is for
\r
4116 fromX = fromY = -1;
\r
4117 DrawPosition(forceFullRepaint || FALSE, NULL); /* [AS] */
\r
4120 LeftClick(Press, pt.x - boardRect.left, pt.y - boardRect.top);
\r
4121 DrawPosition(TRUE, NULL);
\r
4124 case WM_LBUTTONUP:
\r
4125 LeftClick(Release, pt.x - boardRect.left, pt.y - boardRect.top);
\r
4126 DrawPosition(TRUE, NULL);
\r
4129 case WM_MOUSEMOVE:
\r
4130 if(SeekGraphClick(Press, pt.x - boardRect.left, pt.y - boardRect.top, 1)) break;
\r
4131 MovePV(pt.x - boardRect.left, pt.y - boardRect.top, boardRect.bottom - boardRect.top);
\r
4132 if ((appData.animateDragging || appData.highlightDragging)
\r
4133 && (wParam & MK_LBUTTON)
\r
4134 && dragInfo.from.x >= 0)
\r
4136 BOOL full_repaint = FALSE;
\r
4138 if (appData.animateDragging) {
\r
4139 dragInfo.pos = pt;
\r
4141 if (appData.highlightDragging) {
\r
4142 SetHighlights(fromX, fromY, x, y);
\r
4143 if( IsDrawArrowEnabled() && (x < 0 || x >= BOARD_WIDTH || y < 0 || y >= BOARD_HEIGHT) ) {
\r
4144 full_repaint = TRUE;
\r
4148 DrawPosition( full_repaint, NULL);
\r
4150 dragInfo.lastpos = dragInfo.pos;
\r
4154 case WM_MOUSEWHEEL: // [DM]
\r
4155 { static int lastDir = 0; // [HGM] build in some hysteresis to avoid spurious events
\r
4156 /* Mouse Wheel is being rolled forward
\r
4157 * Play moves forward
\r
4159 if((short)HIWORD(wParam) < 0 && currentMove < forwardMostMove)
\r
4160 { if(lastDir == 1) ForwardEvent(); else lastDir = 1; } // [HGM] suppress first event in direction
\r
4161 /* Mouse Wheel is being rolled backward
\r
4162 * Play moves backward
\r
4164 if((short)HIWORD(wParam) > 0 && currentMove > backwardMostMove)
\r
4165 { if(lastDir == -1) BackwardEvent(); else lastDir = -1; }
\r
4169 case WM_MBUTTONUP:
\r
4170 case WM_RBUTTONUP:
\r
4172 RightClick(Release, pt.x - boardRect.left, pt.y - boardRect.top, &fromX, &fromY);
\r
4175 case WM_MBUTTONDOWN:
\r
4176 case WM_RBUTTONDOWN:
\r
4179 fromX = fromY = -1;
\r
4180 dragInfo.pos.x = dragInfo.pos.y = -1;
\r
4181 dragInfo.start.x = dragInfo.start.y = -1;
\r
4182 dragInfo.from = dragInfo.start;
\r
4183 dragInfo.lastpos = dragInfo.pos;
\r
4184 if (appData.highlightDragging) {
\r
4185 ClearHighlights();
\r
4188 /* [HGM] right mouse button in clock area edit-game mode ups clock */
\r
4189 if (PtInRect((LPRECT) &whiteRect, pt)) {
\r
4190 if (gameMode == EditGame || GetKeyState(VK_SHIFT) < 0) AdjustClock(flipClock, 1);
\r
4191 } else if (PtInRect((LPRECT) &blackRect, pt)) {
\r
4192 if (gameMode == EditGame || GetKeyState(VK_SHIFT) < 0) AdjustClock(!flipClock, 1);
\r
4196 DrawPosition(TRUE, NULL);
\r
4198 menuNr = RightClick(Press, pt.x - boardRect.left, pt.y - boardRect.top, &fromX, &fromY);
\r
4201 if (message == WM_MBUTTONDOWN) {
\r
4202 buttonCount = 3; /* even if system didn't think so */
\r
4203 if (wParam & MK_SHIFT)
\r
4204 MenuPopup(hwnd, pt, LoadMenu(hInst, "BlackPieceMenu"), -1);
\r
4206 MenuPopup(hwnd, pt, LoadMenu(hInst, "WhitePieceMenu"), -1);
\r
4207 } else { /* message == WM_RBUTTONDOWN */
\r
4208 /* Just have one menu, on the right button. Windows users don't
\r
4209 think to try the middle one, and sometimes other software steals
\r
4210 it, or it doesn't really exist. */
\r
4211 if(gameInfo.variant != VariantShogi)
\r
4212 MenuPopup(hwnd, pt, LoadMenu(hInst, "PieceMenu"), -1);
\r
4214 MenuPopup(hwnd, pt, LoadMenu(hInst, "ShogiPieceMenu"), -1);
\r
4218 SetCapture(hwndMain);
4221 hmenu = LoadMenu(hInst, "DropPieceMenu");
\r
4222 SetupDropMenu(hmenu);
\r
4223 MenuPopup(hwnd, pt, hmenu, -1);
\r
4233 /* Preprocess messages for buttons in main window */
\r
4235 ButtonProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
\r
4237 int id = GetWindowLong(hwnd, GWL_ID);
\r
4240 for (i=0; i<N_BUTTONS; i++) {
\r
4241 if (buttonDesc[i].id == id) break;
\r
4243 if (i == N_BUTTONS) return 0;
\r
4244 switch (message) {
\r
4249 dir = (wParam == VK_LEFT) ? -1 : 1;
\r
4250 SetFocus(buttonDesc[(i + dir + N_BUTTONS) % N_BUTTONS].hwnd);
\r
4257 SendMessage(hwndMain, WM_COMMAND, MAKEWPARAM(buttonDesc[i].id, 0), 0);
\r
4260 if (appData.icsActive && (isalpha((char)wParam) || wParam == '0')) {
\r
4261 // [HGM] movenum: only letters or leading zero should go to ICS input
\r
4262 HWND h = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
4263 if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);
\r
4265 SendMessage(h, WM_CHAR, wParam, lParam);
\r
4267 } else if (isalpha((char)wParam) || isdigit((char)wParam)){
\r
4268 PopUpMoveDialog((char)wParam);
\r
4274 return CallWindowProc(buttonDesc[i].wndproc, hwnd, message, wParam, lParam);
\r
4277 /* Process messages for Promotion dialog box */
\r
4279 Promotion(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
4283 switch (message) {
\r
4284 case WM_INITDIALOG: /* message: initialize dialog box */
\r
4285 /* Center the dialog over the application window */
\r
4286 CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));
\r
4287 Translate(hDlg, DLG_PromotionKing);
\r
4288 ShowWindow(GetDlgItem(hDlg, PB_King),
\r
4289 (!appData.testLegality || gameInfo.variant == VariantSuicide ||
\r
4290 gameInfo.variant == VariantGiveaway || gameInfo.variant == VariantSuper ) ?
\r
4291 SW_SHOW : SW_HIDE);
\r
4292 /* [HGM] Only allow C & A promotions if these pieces are defined */
\r
4293 ShowWindow(GetDlgItem(hDlg, PB_Archbishop),
\r
4294 ((PieceToChar(WhiteAngel) >= 'A' &&
\r
4295 PieceToChar(WhiteAngel) != '~') ||
\r
4296 (PieceToChar(BlackAngel) >= 'A' &&
\r
4297 PieceToChar(BlackAngel) != '~') ) ?
\r
4298 SW_SHOW : SW_HIDE);
\r
4299 ShowWindow(GetDlgItem(hDlg, PB_Chancellor),
\r
4300 ((PieceToChar(WhiteMarshall) >= 'A' &&
\r
4301 PieceToChar(WhiteMarshall) != '~') ||
\r
4302 (PieceToChar(BlackMarshall) >= 'A' &&
\r
4303 PieceToChar(BlackMarshall) != '~') ) ?
\r
4304 SW_SHOW : SW_HIDE);
\r
4305 /* [HGM] Hide B & R button in Shogi, use Q as promote, N as defer */
\r
4306 ShowWindow(GetDlgItem(hDlg, PB_Rook),
\r
4307 gameInfo.variant != VariantShogi ?
\r
4308 SW_SHOW : SW_HIDE);
\r
4309 ShowWindow(GetDlgItem(hDlg, PB_Bishop),
\r
4310 gameInfo.variant != VariantShogi ?
\r
4311 SW_SHOW : SW_HIDE);
\r
4312 if(gameInfo.variant == VariantShogi) {
\r
4313 SetDlgItemText(hDlg, PB_Queen, "YES");
\r
4314 SetDlgItemText(hDlg, PB_Knight, "NO");
\r
4315 SetWindowText(hDlg, "Promote?");
\r
4317 ShowWindow(GetDlgItem(hDlg, IDC_Centaur),
\r
4318 gameInfo.variant == VariantSuper ?
\r
4319 SW_SHOW : SW_HIDE);
\r
4322 case WM_COMMAND: /* message: received a command */
\r
4323 switch (LOWORD(wParam)) {
\r
4325 EndDialog(hDlg, TRUE); /* Exit the dialog */
\r
4326 ClearHighlights();
\r
4327 DrawPosition(FALSE, NULL);
\r
4330 promoChar = gameInfo.variant == VariantSuper ? PieceToChar(BlackSilver) : PieceToChar(BlackKing);
\r
4333 promoChar = gameInfo.variant == VariantShogi ? '+' : PieceToChar(BlackQueen);
\r
4336 promoChar = PieceToChar(BlackRook);
\r
4339 promoChar = PieceToChar(BlackBishop);
\r
4341 case PB_Chancellor:
\r
4342 promoChar = PieceToChar(BlackMarshall);
\r
4344 case PB_Archbishop:
\r
4345 promoChar = PieceToChar(BlackAngel);
\r
4348 promoChar = gameInfo.variant == VariantShogi ? '=' : PieceToChar(BlackKnight);
\r
4353 EndDialog(hDlg, TRUE); /* Exit the dialog */
\r
4354 /* [HGM] <popupFix> Call FinishMove rather than UserMoveEvent, as we
\r
4355 only show the popup when we are already sure the move is valid or
\r
4356 legal. We pass a faulty move type, but the kludge is that FinishMove
\r
4357 will figure out it is a promotion from the promoChar. */
\r
4358 UserMoveEvent(fromX, fromY, toX, toY, promoChar);
\r
4359 fromX = fromY = -1;
\r
4360 if (!appData.highlightLastMove) {
\r
4361 ClearHighlights();
\r
4362 DrawPosition(FALSE, NULL);
\r
4369 /* Pop up promotion dialog */
\r
4371 PromotionPopup(HWND hwnd)
\r
4375 lpProc = MakeProcInstance((FARPROC)Promotion, hInst);
\r
4376 DialogBox(hInst, MAKEINTRESOURCE(DLG_PromotionKing),
\r
4377 hwnd, (DLGPROC)lpProc);
\r
4378 FreeProcInstance(lpProc);
\r
4384 DrawPosition(TRUE, NULL);
\r
4385 PromotionPopup(hwndMain);
\r
4388 /* Toggle ShowThinking */
\r
4390 ToggleShowThinking()
\r
4392 appData.showThinking = !appData.showThinking;
\r
4393 ShowThinkingEvent();
\r
4397 LoadGameDialog(HWND hwnd, char* title)
\r
4401 char fileTitle[MSG_SIZ];
\r
4402 f = OpenFileDialog(hwnd, "rb", "",
\r
4403 appData.oldSaveStyle ? "gam" : "pgn",
\r
4405 title, &number, fileTitle, NULL);
\r
4407 cmailMsgLoaded = FALSE;
\r
4408 if (number == 0) {
\r
4409 int error = GameListBuild(f);
\r
4411 DisplayError(_("Cannot build game list"), error);
\r
4412 } else if (!ListEmpty(&gameList) &&
\r
4413 ((ListGame *) gameList.tailPred)->number > 1) {
\r
4414 GameListPopUp(f, fileTitle);
\r
4417 GameListDestroy();
\r
4420 LoadGame(f, number, fileTitle, FALSE);
\r
4424 int get_term_width()
\r
4429 HFONT hfont, hold_font;
\r
4434 hText = GetDlgItem(hwndConsole, OPT_ConsoleText);
\r
4438 // get the text metrics
\r
4439 hdc = GetDC(hText);
\r
4440 lf = font[boardSize][CONSOLE_FONT]->lf;
\r
4441 if (consoleCF.dwEffects & CFE_BOLD)
\r
4442 lf.lfWeight = FW_BOLD;
\r
4443 if (consoleCF.dwEffects & CFE_ITALIC)
\r
4444 lf.lfItalic = TRUE;
\r
4445 if (consoleCF.dwEffects & CFE_STRIKEOUT)
\r
4446 lf.lfStrikeOut = TRUE;
\r
4447 if (consoleCF.dwEffects & CFE_UNDERLINE)
\r
4448 lf.lfUnderline = TRUE;
\r
4449 hfont = CreateFontIndirect(&lf);
\r
4450 hold_font = SelectObject(hdc, hfont);
\r
4451 GetTextMetrics(hdc, &tm);
\r
4452 SelectObject(hdc, hold_font);
\r
4453 DeleteObject(hfont);
\r
4454 ReleaseDC(hText, hdc);
\r
4456 // get the rectangle
\r
4457 SendMessage(hText, EM_GETRECT, 0, (LPARAM)&rc);
\r
4459 return (rc.right-rc.left) / tm.tmAveCharWidth;
\r
4462 void UpdateICSWidth(HWND hText)
\r
4464 LONG old_width, new_width;
\r
4466 new_width = get_term_width(hText, FALSE);
\r
4467 old_width = GetWindowLong(hText, GWL_USERDATA);
\r
4468 if (new_width != old_width)
\r
4470 ics_update_width(new_width);
\r
4471 SetWindowLong(hText, GWL_USERDATA, new_width);
\r
4476 ChangedConsoleFont()
\r
4479 CHARRANGE tmpsel, sel;
\r
4480 MyFont *f = font[boardSize][CONSOLE_FONT];
\r
4481 HWND hText = GetDlgItem(hwndConsole, OPT_ConsoleText);
\r
4482 HWND hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
4485 cfmt.cbSize = sizeof(CHARFORMAT);
\r
4486 cfmt.dwMask = CFM_FACE|CFM_SIZE|CFM_CHARSET;
\r
4487 safeStrCpy(cfmt.szFaceName, font[boardSize][CONSOLE_FONT]->mfp.faceName,
\r
4488 sizeof(cfmt.szFaceName)/sizeof(cfmt.szFaceName[0]) );
\r
4489 /* yHeight is expressed in twips. A twip is 1/20 of a font's point
\r
4490 * size. This was undocumented in the version of MSVC++ that I had
\r
4491 * when I wrote the code, but is apparently documented now.
\r
4493 cfmt.yHeight = (int)(f->mfp.pointSize * 20.0 + 0.5);
\r
4494 cfmt.bCharSet = f->lf.lfCharSet;
\r
4495 cfmt.bPitchAndFamily = f->lf.lfPitchAndFamily;
\r
4496 SendMessage(hText, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &cfmt);
\r
4497 SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &cfmt);
\r
4498 /* Why are the following seemingly needed too? */
\r
4499 SendMessage(hText, EM_SETCHARFORMAT, SCF_DEFAULT, (LPARAM) &cfmt);
\r
4500 SendMessage(hInput, EM_SETCHARFORMAT, SCF_DEFAULT, (LPARAM) &cfmt);
\r
4501 SendMessage(hText, EM_EXGETSEL, 0, (LPARAM)&sel);
\r
4503 tmpsel.cpMax = -1; /*999999?*/
\r
4504 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&tmpsel);
\r
4505 SendMessage(hText, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cfmt);
\r
4506 /* Trying putting this here too. It still seems to tickle a RichEdit
\r
4507 * bug: sometimes RichEdit indents the first line of a paragraph too.
\r
4509 paraf.cbSize = sizeof(paraf);
\r
4510 paraf.dwMask = PFM_OFFSET | PFM_STARTINDENT;
\r
4511 paraf.dxStartIndent = 0;
\r
4512 paraf.dxOffset = WRAP_INDENT;
\r
4513 SendMessage(hText, EM_SETPARAFORMAT, 0, (LPARAM) ¶f);
\r
4514 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
4515 UpdateICSWidth(hText);
\r
4518 /*---------------------------------------------------------------------------*\
\r
4520 * Window Proc for main window
\r
4522 \*---------------------------------------------------------------------------*/
\r
4524 /* Process messages for main window, etc. */
\r
4526 WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
\r
4529 int wmId, wmEvent;
\r
4533 char fileTitle[MSG_SIZ];
\r
4534 char buf[MSG_SIZ];
\r
4535 static SnapData sd;
\r
4537 switch (message) {
\r
4539 case WM_PAINT: /* message: repaint portion of window */
\r
4543 case WM_ERASEBKGND:
\r
4544 if (IsIconic(hwnd)) {
\r
4545 /* Cheat; change the message */
\r
4546 return (DefWindowProc(hwnd, WM_ICONERASEBKGND, wParam, lParam));
\r
4548 return (DefWindowProc(hwnd, message, wParam, lParam));
\r
4552 case WM_LBUTTONDOWN:
\r
4553 case WM_MBUTTONDOWN:
\r
4554 case WM_RBUTTONDOWN:
\r
4555 case WM_LBUTTONUP:
\r
4556 case WM_MBUTTONUP:
\r
4557 case WM_RBUTTONUP:
\r
4558 case WM_MOUSEMOVE:
\r
4559 case WM_MOUSEWHEEL:
\r
4560 MouseEvent(hwnd, message, wParam, lParam);
\r
4563 JAWS_KB_NAVIGATION
\r
4567 JAWS_ALT_INTERCEPT
\r
4569 if (appData.icsActive && ((char)wParam == '\r' || (char)wParam > ' ' && !((char)wParam >= '1' && (char)wParam <= '9'))) {
\r
4570 // [HGM] movenum: for non-zero digits we always do type-in dialog
\r
4571 HWND h = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
4572 if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);
\r
4574 SendMessage(h, message, wParam, lParam);
\r
4575 } else if(lParam != KF_REPEAT) {
\r
4576 if (isalpha((char)wParam) || isdigit((char)wParam)) {
\r
4577 PopUpMoveDialog((char)wParam);
\r
4578 } else if((char)wParam == 003) CopyGameToClipboard();
\r
4579 else if((char)wParam == 026) PasteGameOrFENFromClipboard();
\r
4584 case WM_PALETTECHANGED:
\r
4585 if (hwnd != (HWND)wParam && !appData.monoMode) {
\r
4587 HDC hdc = GetDC(hwndMain);
\r
4588 SelectPalette(hdc, hPal, TRUE);
\r
4589 nnew = RealizePalette(hdc);
\r
4591 paletteChanged = TRUE;
\r
4592 InvalidateRect(hwnd, &boardRect, FALSE);
\r
4594 ReleaseDC(hwnd, hdc);
\r
4598 case WM_QUERYNEWPALETTE:
\r
4599 if (!appData.monoMode /*&& paletteChanged*/) {
\r
4601 HDC hdc = GetDC(hwndMain);
\r
4602 paletteChanged = FALSE;
\r
4603 SelectPalette(hdc, hPal, FALSE);
\r
4604 nnew = RealizePalette(hdc);
\r
4606 InvalidateRect(hwnd, &boardRect, FALSE);
\r
4608 ReleaseDC(hwnd, hdc);
\r
4613 case WM_COMMAND: /* message: command from application menu */
\r
4614 wmId = LOWORD(wParam);
\r
4615 wmEvent = HIWORD(wParam);
\r
4620 SAY("new game enter a move to play against the computer with white");
\r
4623 case IDM_NewGameFRC:
\r
4624 if( NewGameFRC() == 0 ) {
\r
4629 case IDM_NewVariant:
\r
4630 NewVariantPopup(hwnd);
\r
4633 case IDM_LoadGame:
\r
4634 LoadGameDialog(hwnd, _("Load Game from File"));
\r
4637 case IDM_LoadNextGame:
\r
4641 case IDM_LoadPrevGame:
\r
4645 case IDM_ReloadGame:
\r
4649 case IDM_LoadPosition:
\r
4650 if (gameMode == AnalyzeMode || gameMode == AnalyzeFile) {
\r
4651 Reset(FALSE, TRUE);
\r
4654 f = OpenFileDialog(hwnd, "rb", "",
\r
4655 appData.oldSaveStyle ? "pos" : "fen",
\r
4657 _("Load Position from File"), &number, fileTitle, NULL);
\r
4659 LoadPosition(f, number, fileTitle);
\r
4663 case IDM_LoadNextPosition:
\r
4664 ReloadPosition(1);
\r
4667 case IDM_LoadPrevPosition:
\r
4668 ReloadPosition(-1);
\r
4671 case IDM_ReloadPosition:
\r
4672 ReloadPosition(0);
\r
4675 case IDM_SaveGame:
\r
4676 defName = DefaultFileName(appData.oldSaveStyle ? "gam" : "pgn");
\r
4677 f = OpenFileDialog(hwnd, "a", defName,
\r
4678 appData.oldSaveStyle ? "gam" : "pgn",
\r
4680 _("Save Game to File"), NULL, fileTitle, NULL);
\r
4682 SaveGame(f, 0, "");
\r
4686 case IDM_SavePosition:
\r
4687 defName = DefaultFileName(appData.oldSaveStyle ? "pos" : "fen");
\r
4688 f = OpenFileDialog(hwnd, "a", defName,
\r
4689 appData.oldSaveStyle ? "pos" : "fen",
\r
4691 _("Save Position to File"), NULL, fileTitle, NULL);
\r
4693 SavePosition(f, 0, "");
\r
4697 case IDM_SaveDiagram:
\r
4698 defName = "diagram";
\r
4699 f = OpenFileDialog(hwnd, "wb", defName,
\r
4702 "Save Diagram to File", NULL, fileTitle, NULL);
\r
4708 case IDM_CopyGame:
\r
4709 CopyGameToClipboard();
\r
4712 case IDM_PasteGame:
\r
4713 PasteGameFromClipboard();
\r
4716 case IDM_CopyGameListToClipboard:
\r
4717 CopyGameListToClipboard();
\r
4720 /* [AS] Autodetect FEN or PGN data */
\r
4721 case IDM_PasteAny:
\r
4722 PasteGameOrFENFromClipboard();
\r
4725 /* [AS] Move history */
\r
4726 case IDM_ShowMoveHistory:
\r
4727 if( MoveHistoryIsUp() ) {
\r
4728 MoveHistoryPopDown();
\r
4731 MoveHistoryPopUp();
\r
4735 /* [AS] Eval graph */
\r
4736 case IDM_ShowEvalGraph:
\r
4737 if( EvalGraphIsUp() ) {
\r
4738 EvalGraphPopDown();
\r
4742 SetFocus(hwndMain);
\r
4746 /* [AS] Engine output */
\r
4747 case IDM_ShowEngineOutput:
\r
4748 if( EngineOutputIsUp() ) {
\r
4749 EngineOutputPopDown();
\r
4752 EngineOutputPopUp();
\r
4756 /* [AS] User adjudication */
\r
4757 case IDM_UserAdjudication_White:
\r
4758 UserAdjudicationEvent( +1 );
\r
4761 case IDM_UserAdjudication_Black:
\r
4762 UserAdjudicationEvent( -1 );
\r
4765 case IDM_UserAdjudication_Draw:
\r
4766 UserAdjudicationEvent( 0 );
\r
4769 /* [AS] Game list options dialog */
\r
4770 case IDM_GameListOptions:
\r
4771 GameListOptions();
\r
4778 case IDM_CopyPosition:
\r
4779 CopyFENToClipboard();
\r
4782 case IDM_PastePosition:
\r
4783 PasteFENFromClipboard();
\r
4786 case IDM_MailMove:
\r
4790 case IDM_ReloadCMailMsg:
\r
4791 Reset(TRUE, TRUE);
\r
4792 ReloadCmailMsgEvent(FALSE);
\r
4795 case IDM_Minimize:
\r
4796 ShowWindow(hwnd, SW_MINIMIZE);
\r
4803 case IDM_MachineWhite:
\r
4804 MachineWhiteEvent();
\r
4806 * refresh the tags dialog only if it's visible
\r
4808 if (gameMode == MachinePlaysWhite && IsWindowVisible(editTagsDialog)) {
\r
4810 tags = PGNTags(&gameInfo);
\r
4811 TagsPopUp(tags, CmailMsg());
\r
4814 SAY("computer starts playing white");
\r
4817 case IDM_MachineBlack:
\r
4818 MachineBlackEvent();
\r
4820 * refresh the tags dialog only if it's visible
\r
4822 if (gameMode == MachinePlaysBlack && IsWindowVisible(editTagsDialog)) {
\r
4824 tags = PGNTags(&gameInfo);
\r
4825 TagsPopUp(tags, CmailMsg());
\r
4828 SAY("computer starts playing black");
\r
4831 case IDM_Match: // [HGM] match: flows into next case, after setting Match Mode and nr of Games
\r
4832 if(gameMode != BeginningOfGame) { // allow menu item to remain enabled for better mode highligting
\r
4833 DisplayError(_("You can only start a match from the initial position."), 0); break;
\r
4835 matchMode = 2;// distinguish from command-line-triggered case (matchMode=1)
\r
4836 appData.matchGames = appData.defaultMatchGames;
\r
4839 case IDM_TwoMachines:
\r
4840 TwoMachinesEvent();
\r
4842 * refresh the tags dialog only if it's visible
\r
4844 if (gameMode == TwoMachinesPlay && IsWindowVisible(editTagsDialog)) {
\r
4846 tags = PGNTags(&gameInfo);
\r
4847 TagsPopUp(tags, CmailMsg());
\r
4850 SAY("computer starts playing both sides");
\r
4853 case IDM_AnalysisMode:
\r
4854 if (!first.analysisSupport) {
\r
4855 snprintf(buf, MSG_SIZ, _("%s does not support analysis"), first.tidy);
\r
4856 DisplayError(buf, 0);
\r
4858 SAY("analyzing current position");
\r
4859 /* [DM] icsEngineAnlyze [HGM] Why is this front-end??? */
\r
4860 if (appData.icsActive) {
\r
4861 if (gameMode != IcsObserving) {
\r
4862 snprintf(buf, MSG_SIZ, "You are not observing a game");
\r
4863 DisplayError(buf, 0);
\r
4864 /* secure check */
\r
4865 if (appData.icsEngineAnalyze) {
\r
4866 if (appData.debugMode)
\r
4867 fprintf(debugFP, "Found unexpected active ICS engine analyze \n");
\r
4868 ExitAnalyzeMode();
\r
4874 /* if enable, user want disable icsEngineAnalyze */
\r
4875 if (appData.icsEngineAnalyze) {
\r
4876 ExitAnalyzeMode();
\r
4880 appData.icsEngineAnalyze = TRUE;
\r
4881 if (appData.debugMode) fprintf(debugFP, "ICS engine analyze starting...\n");
\r
4884 if (!appData.showThinking) ToggleShowThinking();
\r
4885 AnalyzeModeEvent();
\r
4889 case IDM_AnalyzeFile:
\r
4890 if (!first.analysisSupport) {
\r
4891 char buf[MSG_SIZ];
\r
4892 snprintf(buf, MSG_SIZ, _("%s does not support analysis"), first.tidy);
\r
4893 DisplayError(buf, 0);
\r
4895 if (!appData.showThinking) ToggleShowThinking();
\r
4896 AnalyzeFileEvent();
\r
4897 LoadGameDialog(hwnd, _("Analyze Game from File"));
\r
4898 AnalysisPeriodicEvent(1);
\r
4902 case IDM_IcsClient:
\r
4906 case IDM_EditGame:
\r
4907 case IDM_EditGame2:
\r
4912 case IDM_EditPosition:
\r
4913 case IDM_EditPosition2:
\r
4914 EditPositionEvent();
\r
4915 SAY("enter a FEN string or setup a position on the board using the control R pop up menu");
\r
4918 case IDM_Training:
\r
4922 case IDM_ShowGameList:
\r
4923 ShowGameListProc();
\r
4926 case IDM_EditProgs1:
\r
4927 EditTagsPopUp(firstChessProgramNames, &firstChessProgramNames);
\r
4930 case IDM_EditProgs2:
\r
4931 EditTagsPopUp(secondChessProgramNames, &secondChessProgramNames);
\r
4934 case IDM_EditServers:
\r
4935 EditTagsPopUp(icsNames, &icsNames);
\r
4938 case IDM_EditTags:
\r
4943 case IDM_EditComment:
\r
4945 if (commentUp && editComment) {
\r
4948 EditCommentEvent();
\r
4968 case IDM_CallFlag:
\r
4988 case IDM_StopObserving:
\r
4989 StopObservingEvent();
\r
4992 case IDM_StopExamining:
\r
4993 StopExaminingEvent();
\r
4997 UploadGameEvent();
\r
5000 case IDM_TypeInMove:
\r
5001 PopUpMoveDialog('\000');
\r
5004 case IDM_TypeInName:
\r
5005 PopUpNameDialog('\000');
\r
5008 case IDM_Backward:
\r
5010 SetFocus(hwndMain);
\r
5017 SetFocus(hwndMain);
\r
5022 SetFocus(hwndMain);
\r
5027 SetFocus(hwndMain);
\r
5031 RevertEvent(FALSE);
\r
5034 case IDM_Annotate: // [HGM] vari: revert with annotation
\r
5035 RevertEvent(TRUE);
\r
5038 case IDM_TruncateGame:
\r
5039 TruncateGameEvent();
\r
5046 case IDM_RetractMove:
\r
5047 RetractMoveEvent();
\r
5050 case IDM_FlipView:
\r
5051 flipView = !flipView;
\r
5052 DrawPosition(FALSE, NULL);
\r
5055 case IDM_FlipClock:
\r
5056 flipClock = !flipClock;
\r
5057 DisplayBothClocks();
\r
5058 DrawPosition(FALSE, NULL);
\r
5061 case IDM_MuteSounds:
\r
5062 mute = !mute; // [HGM] mute: keep track of global muting variable
\r
5063 CheckMenuItem(GetMenu(hwndMain),IDM_MuteSounds,
\r
5064 MF_BYCOMMAND|(mute?MF_CHECKED:MF_UNCHECKED));
\r
5067 case IDM_GeneralOptions:
\r
5068 GeneralOptionsPopup(hwnd);
\r
5069 DrawPosition(TRUE, NULL);
\r
5072 case IDM_BoardOptions:
\r
5073 BoardOptionsPopup(hwnd);
\r
5076 case IDM_EnginePlayOptions:
\r
5077 EnginePlayOptionsPopup(hwnd);
\r
5080 case IDM_Engine1Options:
\r
5081 EngineOptionsPopup(hwnd, &first);
\r
5084 case IDM_Engine2Options:
\r
5086 if(WaitForSecond(SettingsMenuIfReady)) break;
\r
5087 EngineOptionsPopup(hwnd, &second);
\r
5090 case IDM_OptionsUCI:
\r
5091 UciOptionsPopup(hwnd);
\r
5094 case IDM_IcsOptions:
\r
5095 IcsOptionsPopup(hwnd);
\r
5099 FontsOptionsPopup(hwnd);
\r
5103 SoundOptionsPopup(hwnd);
\r
5106 case IDM_CommPort:
\r
5107 CommPortOptionsPopup(hwnd);
\r
5110 case IDM_LoadOptions:
\r
5111 LoadOptionsPopup(hwnd);
\r
5114 case IDM_SaveOptions:
\r
5115 SaveOptionsPopup(hwnd);
\r
5118 case IDM_TimeControl:
\r
5119 TimeControlOptionsPopup(hwnd);
\r
5122 case IDM_SaveSettings:
\r
5123 SaveSettings(settingsFileName);
\r
5126 case IDM_SaveSettingsOnExit:
\r
5127 saveSettingsOnExit = !saveSettingsOnExit;
\r
5128 (void) CheckMenuItem(GetMenu(hwndMain), IDM_SaveSettingsOnExit,
\r
5129 MF_BYCOMMAND|(saveSettingsOnExit ?
\r
5130 MF_CHECKED : MF_UNCHECKED));
\r
5141 case IDM_AboutGame:
\r
5146 appData.debugMode = !appData.debugMode;
\r
5147 if (appData.debugMode) {
\r
5148 char dir[MSG_SIZ];
\r
5149 GetCurrentDirectory(MSG_SIZ, dir);
\r
5150 SetCurrentDirectory(installDir);
\r
5151 debugFP = fopen(appData.nameOfDebugFile, "w");
\r
5152 SetCurrentDirectory(dir);
\r
5153 setbuf(debugFP, NULL);
\r
5160 case IDM_HELPCONTENTS:
\r
5161 if (!MyHelp (hwnd, "winboard.hlp", HELP_KEY,(DWORD)(LPSTR)"CONTENTS") &&
\r
5162 !HtmlHelp(hwnd, "winboard.chm", 0, 0) ) {
\r
5163 MessageBox (GetFocus(),
\r
5164 _("Unable to activate help"),
\r
5165 szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);
\r
5169 case IDM_HELPSEARCH:
\r
5170 if (!MyHelp (hwnd, "winboard.hlp", HELP_PARTIALKEY, (DWORD)(LPSTR)"") &&
\r
5171 !HtmlHelp(hwnd, "winboard.chm", 0, 0) ) {
\r
5172 MessageBox (GetFocus(),
\r
5173 _("Unable to activate help"),
\r
5174 szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);
\r
5178 case IDM_HELPHELP:
\r
5179 if(!WinHelp(hwnd, (LPSTR)NULL, HELP_HELPONHELP, 0)) {
\r
5180 MessageBox (GetFocus(),
\r
5181 _("Unable to activate help"),
\r
5182 szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);
\r
5187 lpProc = MakeProcInstance((FARPROC)About, hInst);
\r
5189 (gameInfo.event && strcmp(gameInfo.event, "Easter Egg Hunt") == 0) ?
\r
5190 "AboutBox2" : "AboutBox", hwnd, (DLGPROC)lpProc);
\r
5191 FreeProcInstance(lpProc);
\r
5194 case IDM_DirectCommand1:
\r
5195 AskQuestionEvent(_("Direct Command"),
\r
5196 _("Send to chess program:"), "", "1");
\r
5198 case IDM_DirectCommand2:
\r
5199 AskQuestionEvent(_("Direct Command"),
\r
5200 _("Send to second chess program:"), "", "2");
\r
5203 case EP_WhitePawn:
\r
5204 EditPositionMenuEvent(WhitePawn, fromX, fromY);
\r
5205 fromX = fromY = -1;
\r
5208 case EP_WhiteKnight:
\r
5209 EditPositionMenuEvent(WhiteKnight, fromX, fromY);
\r
5210 fromX = fromY = -1;
\r
5213 case EP_WhiteBishop:
\r
5214 EditPositionMenuEvent(WhiteBishop, fromX, fromY);
\r
5215 fromX = fromY = -1;
\r
5218 case EP_WhiteRook:
\r
5219 EditPositionMenuEvent(WhiteRook, fromX, fromY);
\r
5220 fromX = fromY = -1;
\r
5223 case EP_WhiteQueen:
\r
5224 EditPositionMenuEvent(WhiteQueen, fromX, fromY);
\r
5225 fromX = fromY = -1;
\r
5228 case EP_WhiteFerz:
\r
5229 EditPositionMenuEvent(WhiteFerz, fromX, fromY);
\r
5230 fromX = fromY = -1;
\r
5233 case EP_WhiteWazir:
\r
5234 EditPositionMenuEvent(WhiteWazir, fromX, fromY);
\r
5235 fromX = fromY = -1;
\r
5238 case EP_WhiteAlfil:
\r
5239 EditPositionMenuEvent(WhiteAlfil, fromX, fromY);
\r
5240 fromX = fromY = -1;
\r
5243 case EP_WhiteCannon:
\r
5244 EditPositionMenuEvent(WhiteCannon, fromX, fromY);
\r
5245 fromX = fromY = -1;
\r
5248 case EP_WhiteCardinal:
\r
5249 EditPositionMenuEvent(WhiteAngel, fromX, fromY);
\r
5250 fromX = fromY = -1;
\r
5253 case EP_WhiteMarshall:
\r
5254 EditPositionMenuEvent(WhiteMarshall, fromX, fromY);
\r
5255 fromX = fromY = -1;
\r
5258 case EP_WhiteKing:
\r
5259 EditPositionMenuEvent(WhiteKing, fromX, fromY);
\r
5260 fromX = fromY = -1;
\r
5263 case EP_BlackPawn:
\r
5264 EditPositionMenuEvent(BlackPawn, fromX, fromY);
\r
5265 fromX = fromY = -1;
\r
5268 case EP_BlackKnight:
\r
5269 EditPositionMenuEvent(BlackKnight, fromX, fromY);
\r
5270 fromX = fromY = -1;
\r
5273 case EP_BlackBishop:
\r
5274 EditPositionMenuEvent(BlackBishop, fromX, fromY);
\r
5275 fromX = fromY = -1;
\r
5278 case EP_BlackRook:
\r
5279 EditPositionMenuEvent(BlackRook, fromX, fromY);
\r
5280 fromX = fromY = -1;
\r
5283 case EP_BlackQueen:
\r
5284 EditPositionMenuEvent(BlackQueen, fromX, fromY);
\r
5285 fromX = fromY = -1;
\r
5288 case EP_BlackFerz:
\r
5289 EditPositionMenuEvent(BlackFerz, fromX, fromY);
\r
5290 fromX = fromY = -1;
\r
5293 case EP_BlackWazir:
\r
5294 EditPositionMenuEvent(BlackWazir, fromX, fromY);
\r
5295 fromX = fromY = -1;
\r
5298 case EP_BlackAlfil:
\r
5299 EditPositionMenuEvent(BlackAlfil, fromX, fromY);
\r
5300 fromX = fromY = -1;
\r
5303 case EP_BlackCannon:
\r
5304 EditPositionMenuEvent(BlackCannon, fromX, fromY);
\r
5305 fromX = fromY = -1;
\r
5308 case EP_BlackCardinal:
\r
5309 EditPositionMenuEvent(BlackAngel, fromX, fromY);
\r
5310 fromX = fromY = -1;
\r
5313 case EP_BlackMarshall:
\r
5314 EditPositionMenuEvent(BlackMarshall, fromX, fromY);
\r
5315 fromX = fromY = -1;
\r
5318 case EP_BlackKing:
\r
5319 EditPositionMenuEvent(BlackKing, fromX, fromY);
\r
5320 fromX = fromY = -1;
\r
5323 case EP_EmptySquare:
\r
5324 EditPositionMenuEvent(EmptySquare, fromX, fromY);
\r
5325 fromX = fromY = -1;
\r
5328 case EP_ClearBoard:
\r
5329 EditPositionMenuEvent(ClearBoard, fromX, fromY);
\r
5330 fromX = fromY = -1;
\r
5334 EditPositionMenuEvent(WhitePlay, fromX, fromY);
\r
5335 fromX = fromY = -1;
\r
5339 EditPositionMenuEvent(BlackPlay, fromX, fromY);
\r
5340 fromX = fromY = -1;
\r
5344 EditPositionMenuEvent(PromotePiece, fromX, fromY);
\r
5345 fromX = fromY = -1;
\r
5349 EditPositionMenuEvent(DemotePiece, fromX, fromY);
\r
5350 fromX = fromY = -1;
\r
5354 DropMenuEvent(WhitePawn, fromX, fromY);
\r
5355 fromX = fromY = -1;
\r
5359 DropMenuEvent(WhiteKnight, fromX, fromY);
\r
5360 fromX = fromY = -1;
\r
5364 DropMenuEvent(WhiteBishop, fromX, fromY);
\r
5365 fromX = fromY = -1;
\r
5369 DropMenuEvent(WhiteRook, fromX, fromY);
\r
5370 fromX = fromY = -1;
\r
5374 DropMenuEvent(WhiteQueen, fromX, fromY);
\r
5375 fromX = fromY = -1;
\r
5380 TranslateMenus(0);
\r
5381 CheckMenuItem(GetMenu(hwndMain), lastChecked, MF_BYCOMMAND|MF_UNCHECKED);
\r
5382 CheckMenuItem(GetMenu(hwndMain), IDM_English, MF_BYCOMMAND|MF_CHECKED);
\r
5383 lastChecked = wmId;
\r
5387 if(wmId > IDM_English && wmId < IDM_English+5) {
\r
5388 LoadLanguageFile(languageFile[wmId - IDM_English - 1]);
\r
5389 TranslateMenus(0);
\r
5390 CheckMenuItem(GetMenu(hwndMain), lastChecked, MF_BYCOMMAND|MF_UNCHECKED);
\r
5391 CheckMenuItem(GetMenu(hwndMain), wmId, MF_BYCOMMAND|MF_CHECKED);
\r
5392 lastChecked = wmId;
\r
5395 return (DefWindowProc(hwnd, message, wParam, lParam));
\r
5401 case CLOCK_TIMER_ID:
\r
5402 KillTimer(hwnd, clockTimerEvent); /* Simulate one-shot timer as in X */
\r
5403 clockTimerEvent = 0;
\r
5404 DecrementClocks(); /* call into back end */
\r
5406 case LOAD_GAME_TIMER_ID:
\r
5407 KillTimer(hwnd, loadGameTimerEvent); /* Simulate one-shot timer as in X*/
\r
5408 loadGameTimerEvent = 0;
\r
5409 AutoPlayGameLoop(); /* call into back end */
\r
5411 case ANALYSIS_TIMER_ID:
\r
5412 if ((gameMode == AnalyzeMode || gameMode == AnalyzeFile
\r
5413 || appData.icsEngineAnalyze) && appData.periodicUpdates) {
\r
5414 AnalysisPeriodicEvent(0);
\r
5416 KillTimer(hwnd, analysisTimerEvent);
\r
5417 analysisTimerEvent = 0;
\r
5420 case DELAYED_TIMER_ID:
\r
5421 KillTimer(hwnd, delayedTimerEvent);
\r
5422 delayedTimerEvent = 0;
\r
5423 delayedTimerCallback();
\r
5428 case WM_USER_Input:
\r
5429 InputEvent(hwnd, message, wParam, lParam);
\r
5432 /* [AS] Also move "attached" child windows */
\r
5433 case WM_WINDOWPOSCHANGING:
\r
5435 if( hwnd == hwndMain && appData.useStickyWindows ) {
\r
5436 LPWINDOWPOS lpwp = (LPWINDOWPOS) lParam;
\r
5438 if( ((lpwp->flags & SWP_NOMOVE) == 0) && ((lpwp->flags & SWP_NOSIZE) != 0) ) {
\r
5439 /* Window is moving */
\r
5442 // GetWindowRect( hwnd, &rcMain ); //[HGM] sticky: in XP this returned new position, not old
\r
5443 rcMain.left = wpMain.x; // replace by these 4 lines to reconstruct old rect
\r
5444 rcMain.right = wpMain.x + wpMain.width;
\r
5445 rcMain.top = wpMain.y;
\r
5446 rcMain.bottom = wpMain.y + wpMain.height;
\r
5448 ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, moveHistoryDialog, &wpMoveHistory );
\r
5449 ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, evalGraphDialog, &wpEvalGraph );
\r
5450 ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, engineOutputDialog, &wpEngineOutput );
\r
5451 ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, gameListDialog, &wpGameList );
\r
5452 ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, hwndConsole, &wpConsole );
\r
5453 wpMain.x = lpwp->x;
\r
5454 wpMain.y = lpwp->y;
\r
5459 /* [AS] Snapping */
\r
5460 case WM_ENTERSIZEMOVE:
\r
5461 if(appData.debugMode) { fprintf(debugFP, "size-move\n"); }
\r
5462 if (hwnd == hwndMain) {
\r
5463 doingSizing = TRUE;
\r
5466 return OnEnterSizeMove( &sd, hwnd, wParam, lParam );
\r
5470 if(appData.debugMode) { fprintf(debugFP, "sizing\n"); }
\r
5471 if (hwnd == hwndMain) {
\r
5472 lastSizing = wParam;
\r
5477 if(appData.debugMode) { fprintf(debugFP, "moving\n"); }
\r
5478 return OnMoving( &sd, hwnd, wParam, lParam );
\r
5480 case WM_EXITSIZEMOVE:
\r
5481 if(appData.debugMode) { fprintf(debugFP, "exit size-move, size = %d\n", squareSize); }
\r
5482 if (hwnd == hwndMain) {
\r
5484 doingSizing = FALSE;
\r
5485 InvalidateRect(hwnd, &boardRect, FALSE);
\r
5486 GetClientRect(hwnd, &client);
\r
5487 ResizeBoard(client.right, client.bottom, lastSizing);
\r
5489 if(appData.debugMode) { fprintf(debugFP, "square size = %d\n", squareSize); }
\r
5491 return OnExitSizeMove( &sd, hwnd, wParam, lParam );
\r
5494 case WM_DESTROY: /* message: window being destroyed */
\r
5495 PostQuitMessage(0);
\r
5499 if (hwnd == hwndMain) {
\r
5504 default: /* Passes it on if unprocessed */
\r
5505 return (DefWindowProc(hwnd, message, wParam, lParam));
\r
5510 /*---------------------------------------------------------------------------*\
\r
5512 * Misc utility routines
\r
5514 \*---------------------------------------------------------------------------*/
\r
5517 * Decent random number generator, at least not as bad as Windows
\r
5518 * standard rand, which returns a value in the range 0 to 0x7fff.
\r
5520 unsigned int randstate;
\r
5525 randstate = randstate * 1664525 + 1013904223;
\r
5526 return (int) randstate & 0x7fffffff;
\r
5530 mysrandom(unsigned int seed)
\r
5537 * returns TRUE if user selects a different color, FALSE otherwise
\r
5541 ChangeColor(HWND hwnd, COLORREF *which)
\r
5543 static BOOL firstTime = TRUE;
\r
5544 static DWORD customColors[16];
\r
5546 COLORREF newcolor;
\r
5551 /* Make initial colors in use available as custom colors */
\r
5552 /* Should we put the compiled-in defaults here instead? */
\r
5554 customColors[i++] = lightSquareColor & 0xffffff;
\r
5555 customColors[i++] = darkSquareColor & 0xffffff;
\r
5556 customColors[i++] = whitePieceColor & 0xffffff;
\r
5557 customColors[i++] = blackPieceColor & 0xffffff;
\r
5558 customColors[i++] = highlightSquareColor & 0xffffff;
\r
5559 customColors[i++] = premoveHighlightColor & 0xffffff;
\r
5561 for (ccl = (ColorClass) 0; ccl < NColorClasses && i < 16; ccl++) {
\r
5562 customColors[i++] = textAttribs[ccl].color;
\r
5564 while (i < 16) customColors[i++] = RGB(255, 255, 255);
\r
5565 firstTime = FALSE;
\r
5568 cc.lStructSize = sizeof(cc);
\r
5569 cc.hwndOwner = hwnd;
\r
5570 cc.hInstance = NULL;
\r
5571 cc.rgbResult = (DWORD) (*which & 0xffffff);
\r
5572 cc.lpCustColors = (LPDWORD) customColors;
\r
5573 cc.Flags = CC_RGBINIT|CC_FULLOPEN;
\r
5575 if (!ChooseColor(&cc)) return FALSE;
\r
5577 newcolor = (COLORREF) (0x2000000 | cc.rgbResult);
\r
5578 if (newcolor == *which) return FALSE;
\r
5579 *which = newcolor;
\r
5583 InitDrawingColors();
\r
5584 InvalidateRect(hwnd, &boardRect, FALSE);
\r
5589 MyLoadSound(MySound *ms)
\r
5595 if (ms->data) free(ms->data);
\r
5598 switch (ms->name[0]) {
\r
5604 /* System sound from Control Panel. Don't preload here. */
\r
5608 if (ms->name[1] == NULLCHAR) {
\r
5609 /* "!" alone = silence */
\r
5612 /* Builtin wave resource. Error if not found. */
\r
5613 HANDLE h = FindResource(hInst, ms->name + 1, "WAVE");
\r
5614 if (h == NULL) break;
\r
5615 ms->data = (void *)LoadResource(hInst, h);
\r
5616 if (h == NULL) break;
\r
5621 /* .wav file. Error if not found. */
\r
5622 f = fopen(ms->name, "rb");
\r
5623 if (f == NULL) break;
\r
5624 if (fstat(fileno(f), &st) < 0) break;
\r
5625 ms->data = malloc(st.st_size);
\r
5626 if (fread(ms->data, st.st_size, 1, f) < 1) break;
\r
5632 char buf[MSG_SIZ];
\r
5633 snprintf(buf, MSG_SIZ, _("Error loading sound %s"), ms->name);
\r
5634 DisplayError(buf, GetLastError());
\r
5640 MyPlaySound(MySound *ms)
\r
5642 BOOLEAN ok = FALSE;
\r
5644 if(mute) return TRUE; // [HGM] mute: suppress all sound play when muted
\r
5645 switch (ms->name[0]) {
\r
5647 if(appData.debugMode) fprintf(debugFP, "silence\n");
\r
5652 /* System sound from Control Panel (deprecated feature).
\r
5653 "$" alone or an unset sound name gets default beep (still in use). */
\r
5654 if (ms->name[1]) {
\r
5655 ok = PlaySound(ms->name + 1, NULL, SND_ALIAS|SND_ASYNC);
\r
5657 if (!ok) ok = MessageBeep(MB_OK);
\r
5660 /* Builtin wave resource, or "!" alone for silence */
\r
5661 if (ms->name[1]) {
\r
5662 if (ms->data == NULL) return FALSE;
\r
5663 ok = PlaySound(ms->data, NULL, SND_MEMORY|SND_ASYNC);
\r
5669 /* .wav file. Error if not found. */
\r
5670 if (ms->data == NULL) return FALSE;
\r
5671 ok = PlaySound(ms->data, NULL, SND_MEMORY|SND_ASYNC);
\r
5674 /* Don't print an error: this can happen innocently if the sound driver
\r
5675 is busy; for instance, if another instance of WinBoard is playing
\r
5676 a sound at about the same time. */
\r
5682 OldOpenFileHook(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
5685 OPENFILENAME *ofn;
\r
5686 static UINT *number; /* gross that this is static */
\r
5688 switch (message) {
\r
5689 case WM_INITDIALOG: /* message: initialize dialog box */
\r
5690 /* Center the dialog over the application window */
\r
5691 ofn = (OPENFILENAME *) lParam;
\r
5692 if (ofn->Flags & OFN_ENABLETEMPLATE) {
\r
5693 number = (UINT *) ofn->lCustData;
\r
5694 SendMessage(GetDlgItem(hDlg, edt2), WM_SETTEXT, 0, (LPARAM) "");
\r
5698 CenterWindow (hDlg, GetWindow (hDlg, GW_OWNER));
\r
5699 Translate(hDlg, 1536);
\r
5700 return FALSE; /* Allow for further processing */
\r
5703 if ((LOWORD(wParam) == IDOK) && (number != NULL)) {
\r
5704 *number = GetDlgItemInt(hDlg, OPT_IndexNumberOld, &ok, FALSE);
\r
5706 return FALSE; /* Allow for further processing */
\r
5712 OpenFileHook(HWND hdlg, UINT uiMsg, WPARAM wParam, LPARAM lParam)
\r
5714 static UINT *number;
\r
5715 OPENFILENAME *ofname;
\r
5718 case WM_INITDIALOG:
\r
5719 Translate(hdlg, DLG_IndexNumber);
\r
5720 ofname = (OPENFILENAME *)lParam;
\r
5721 number = (UINT *)(ofname->lCustData);
\r
5724 ofnot = (OFNOTIFY *)lParam;
\r
5725 if (ofnot->hdr.code == CDN_FILEOK) {
\r
5726 *number = GetDlgItemInt(hdlg, OPT_IndexNumber, NULL, FALSE);
\r
5735 OpenFileDialog(HWND hwnd, char *write, char *defName, char *defExt, // [HGM] diag: type of 'write' now string
\r
5736 char *nameFilt, char *dlgTitle, UINT *number,
\r
5737 char fileTitle[MSG_SIZ], char fileName[MSG_SIZ])
\r
5739 OPENFILENAME openFileName;
\r
5740 char buf1[MSG_SIZ];
\r
5743 if (fileName == NULL) fileName = buf1;
\r
5744 if (defName == NULL) {
\r
5745 safeStrCpy(fileName, "*.", 3 );
\r
5746 strcat(fileName, defExt);
\r
5748 safeStrCpy(fileName, defName, MSG_SIZ );
\r
5750 if (fileTitle) safeStrCpy(fileTitle, "", MSG_SIZ );
\r
5751 if (number) *number = 0;
\r
5753 openFileName.lStructSize = sizeof(OPENFILENAME);
\r
5754 openFileName.hwndOwner = hwnd;
\r
5755 openFileName.hInstance = (HANDLE) hInst;
\r
5756 openFileName.lpstrFilter = nameFilt;
\r
5757 openFileName.lpstrCustomFilter = (LPSTR) NULL;
\r
5758 openFileName.nMaxCustFilter = 0L;
\r
5759 openFileName.nFilterIndex = 1L;
\r
5760 openFileName.lpstrFile = fileName;
\r
5761 openFileName.nMaxFile = MSG_SIZ;
\r
5762 openFileName.lpstrFileTitle = fileTitle;
\r
5763 openFileName.nMaxFileTitle = fileTitle ? MSG_SIZ : 0;
\r
5764 openFileName.lpstrInitialDir = NULL;
\r
5765 openFileName.lpstrTitle = dlgTitle;
\r
5766 openFileName.Flags = OFN_PATHMUSTEXIST | OFN_HIDEREADONLY
\r
5767 | (write[0] != 'r' ? 0 : OFN_FILEMUSTEXIST)
\r
5768 | (number ? OFN_ENABLETEMPLATE | OFN_ENABLEHOOK: 0)
\r
5769 | (oldDialog ? 0 : OFN_EXPLORER);
\r
5770 openFileName.nFileOffset = 0;
\r
5771 openFileName.nFileExtension = 0;
\r
5772 openFileName.lpstrDefExt = defExt;
\r
5773 openFileName.lCustData = (LONG) number;
\r
5774 openFileName.lpfnHook = oldDialog ?
\r
5775 (LPOFNHOOKPROC) OldOpenFileHook : (LPOFNHOOKPROC) OpenFileHook;
\r
5776 openFileName.lpTemplateName = (LPSTR)(oldDialog ? 1536 : DLG_IndexNumber);
\r
5778 if (write[0] != 'r' ? GetSaveFileName(&openFileName) :
\r
5779 GetOpenFileName(&openFileName)) {
\r
5780 /* open the file */
\r
5781 f = fopen(openFileName.lpstrFile, write);
\r
5783 MessageBox(hwnd, _("File open failed"), NULL,
\r
5784 MB_OK|MB_ICONEXCLAMATION);
\r
5788 int err = CommDlgExtendedError();
\r
5789 if (err != 0) DisplayError(_("Internal error in file dialog box"), err);
\r
5798 MenuPopup(HWND hwnd, POINT pt, HMENU hmenu, UINT def)
\r
5800 HMENU hmenuTrackPopup; /* floating pop-up menu */
\r
5803 * Get the first pop-up menu in the menu template. This is the
\r
5804 * menu that TrackPopupMenu displays.
\r
5806 hmenuTrackPopup = GetSubMenu(hmenu, 0);
\r
5808 SetMenuDefaultItem(hmenuTrackPopup, def, FALSE);
\r
5811 * TrackPopup uses screen coordinates, so convert the
\r
5812 * coordinates of the mouse click to screen coordinates.
\r
5814 ClientToScreen(hwnd, (LPPOINT) &pt);
\r
5816 /* Draw and track the floating pop-up menu. */
\r
5817 TrackPopupMenu(hmenuTrackPopup, TPM_CENTERALIGN | TPM_RIGHTBUTTON,
\r
5818 pt.x, pt.y, 0, hwnd, NULL);
\r
5820 /* Destroy the menu.*/
\r
5821 DestroyMenu(hmenu);
\r
5826 int sizeX, sizeY, newSizeX, newSizeY;
\r
5828 } ResizeEditPlusButtonsClosure;
\r
5831 ResizeEditPlusButtonsCallback(HWND hChild, LPARAM lparam)
\r
5833 ResizeEditPlusButtonsClosure *cl = (ResizeEditPlusButtonsClosure *)lparam;
\r
5837 if (hChild == cl->hText) return TRUE;
\r
5838 GetWindowRect(hChild, &rect); /* gives screen coords */
\r
5839 pt.x = rect.left + (cl->newSizeX - cl->sizeX)/2;
\r
5840 pt.y = rect.top + cl->newSizeY - cl->sizeY;
\r
5841 ScreenToClient(cl->hDlg, &pt);
\r
5842 cl->hdwp = DeferWindowPos(cl->hdwp, hChild, NULL,
\r
5843 pt.x, pt.y, rect.right - rect.left, rect.bottom - rect.top, SWP_NOZORDER);
\r
5847 /* Resize a dialog that has a (rich) edit field filling most of
\r
5848 the top, with a row of buttons below */
\r
5850 ResizeEditPlusButtons(HWND hDlg, HWND hText, int sizeX, int sizeY, int newSizeX, int newSizeY)
\r
5853 int newTextHeight, newTextWidth;
\r
5854 ResizeEditPlusButtonsClosure cl;
\r
5856 /*if (IsIconic(hDlg)) return;*/
\r
5857 if (newSizeX == sizeX && newSizeY == sizeY) return;
\r
5859 cl.hdwp = BeginDeferWindowPos(8);
\r
5861 GetWindowRect(hText, &rectText); /* gives screen coords */
\r
5862 newTextWidth = rectText.right - rectText.left + newSizeX - sizeX;
\r
5863 newTextHeight = rectText.bottom - rectText.top + newSizeY - sizeY;
\r
5864 if (newTextHeight < 0) {
\r
5865 newSizeY += -newTextHeight;
\r
5866 newTextHeight = 0;
\r
5868 cl.hdwp = DeferWindowPos(cl.hdwp, hText, NULL, 0, 0,
\r
5869 newTextWidth, newTextHeight, SWP_NOZORDER|SWP_NOMOVE);
\r
5875 cl.newSizeX = newSizeX;
\r
5876 cl.newSizeY = newSizeY;
\r
5877 EnumChildWindows(hDlg, ResizeEditPlusButtonsCallback, (LPARAM)&cl);
\r
5879 EndDeferWindowPos(cl.hdwp);
\r
5882 BOOL CenterWindowEx(HWND hwndChild, HWND hwndParent, int mode)
\r
5884 RECT rChild, rParent;
\r
5885 int wChild, hChild, wParent, hParent;
\r
5886 int wScreen, hScreen, xNew, yNew;
\r
5889 /* Get the Height and Width of the child window */
\r
5890 GetWindowRect (hwndChild, &rChild);
\r
5891 wChild = rChild.right - rChild.left;
\r
5892 hChild = rChild.bottom - rChild.top;
\r
5894 /* Get the Height and Width of the parent window */
\r
5895 GetWindowRect (hwndParent, &rParent);
\r
5896 wParent = rParent.right - rParent.left;
\r
5897 hParent = rParent.bottom - rParent.top;
\r
5899 /* Get the display limits */
\r
5900 hdc = GetDC (hwndChild);
\r
5901 wScreen = GetDeviceCaps (hdc, HORZRES);
\r
5902 hScreen = GetDeviceCaps (hdc, VERTRES);
\r
5903 ReleaseDC(hwndChild, hdc);
\r
5905 /* Calculate new X position, then adjust for screen */
\r
5906 xNew = rParent.left + ((wParent - wChild) /2);
\r
5909 } else if ((xNew+wChild) > wScreen) {
\r
5910 xNew = wScreen - wChild;
\r
5913 /* Calculate new Y position, then adjust for screen */
\r
5915 yNew = rParent.top + ((hParent - hChild) /2);
\r
5918 yNew = rParent.top + GetSystemMetrics( SM_CYCAPTION ) * 2 / 3;
\r
5923 } else if ((yNew+hChild) > hScreen) {
\r
5924 yNew = hScreen - hChild;
\r
5927 /* Set it, and return */
\r
5928 return SetWindowPos (hwndChild, NULL,
\r
5929 xNew, yNew, 0, 0, SWP_NOSIZE | SWP_NOZORDER);
\r
5932 /* Center one window over another */
\r
5933 BOOL CenterWindow (HWND hwndChild, HWND hwndParent)
\r
5935 return CenterWindowEx( hwndChild, hwndParent, 0 );
\r
5938 /*---------------------------------------------------------------------------*\
\r
5940 * Startup Dialog functions
\r
5942 \*---------------------------------------------------------------------------*/
\r
5944 InitComboStrings(HANDLE hwndCombo, char **cd)
\r
5946 SendMessage(hwndCombo, CB_RESETCONTENT, 0, 0);
\r
5948 while (*cd != NULL) {
\r
5949 SendMessage(hwndCombo, CB_ADDSTRING, 0, (LPARAM) T_(*cd));
\r
5955 InitComboStringsFromOption(HANDLE hwndCombo, char *str)
\r
5957 char buf1[MAX_ARG_LEN];
\r
5960 if (str[0] == '@') {
\r
5961 FILE* f = fopen(str + 1, "r");
\r
5963 DisplayFatalError(str + 1, errno, 2);
\r
5966 len = fread(buf1, 1, sizeof(buf1)-1, f);
\r
5968 buf1[len] = NULLCHAR;
\r
5972 SendMessage(hwndCombo, CB_RESETCONTENT, 0, 0);
\r
5975 char buf[MSG_SIZ];
\r
5976 char *end = strchr(str, '\n');
\r
5977 if (end == NULL) return;
\r
5978 memcpy(buf, str, end - str);
\r
5979 buf[end - str] = NULLCHAR;
\r
5980 SendMessage(hwndCombo, CB_ADDSTRING, 0, (LPARAM) buf);
\r
5986 SetStartupDialogEnables(HWND hDlg)
\r
5988 EnableWindow(GetDlgItem(hDlg, OPT_ChessEngineName),
\r
5989 IsDlgButtonChecked(hDlg, OPT_ChessEngine) ||
\r
5990 (appData.zippyPlay && IsDlgButtonChecked(hDlg, OPT_ChessServer)));
\r
5991 EnableWindow(GetDlgItem(hDlg, OPT_SecondChessEngineName),
\r
5992 IsDlgButtonChecked(hDlg, OPT_ChessEngine));
\r
5993 EnableWindow(GetDlgItem(hDlg, OPT_ChessServerName),
\r
5994 IsDlgButtonChecked(hDlg, OPT_ChessServer));
\r
5995 EnableWindow(GetDlgItem(hDlg, OPT_AdditionalOptions),
\r
5996 IsDlgButtonChecked(hDlg, OPT_AnyAdditional));
\r
5997 EnableWindow(GetDlgItem(hDlg, IDOK),
\r
5998 IsDlgButtonChecked(hDlg, OPT_ChessEngine) ||
\r
5999 IsDlgButtonChecked(hDlg, OPT_ChessServer) ||
\r
6000 IsDlgButtonChecked(hDlg, OPT_View));
\r
6004 QuoteForFilename(char *filename)
\r
6006 int dquote, space;
\r
6007 dquote = strchr(filename, '"') != NULL;
\r
6008 space = strchr(filename, ' ') != NULL;
\r
6009 if (dquote || space) {
\r
6021 InitEngineBox(HWND hDlg, HWND hwndCombo, char* nthcp, char* nthd, char* nthdir, char *nthnames)
\r
6023 char buf[MSG_SIZ];
\r
6026 InitComboStringsFromOption(hwndCombo, nthnames);
\r
6027 q = QuoteForFilename(nthcp);
\r
6028 snprintf(buf, MSG_SIZ, "%s%s%s", q, nthcp, q);
\r
6029 if (*nthdir != NULLCHAR) {
\r
6030 q = QuoteForFilename(nthdir);
\r
6031 snprintf(buf + strlen(buf), MSG_SIZ, " /%s=%s%s%s", nthd, q, nthdir, q);
\r
6033 if (*nthcp == NULLCHAR) {
\r
6034 SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) 0, (LPARAM) 0);
\r
6035 } else if (SendMessage(hwndCombo, CB_SELECTSTRING, (WPARAM) -1, (LPARAM) buf) == CB_ERR) {
\r
6036 SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) -1, (LPARAM) 0);
\r
6037 SendMessage(hwndCombo, WM_SETTEXT, (WPARAM) 0, (LPARAM) buf);
\r
6042 StartupDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
6044 char buf[MSG_SIZ];
\r
6048 switch (message) {
\r
6049 case WM_INITDIALOG:
\r
6050 /* Center the dialog */
\r
6051 CenterWindow (hDlg, GetDesktopWindow());
\r
6052 Translate(hDlg, DLG_Startup);
\r
6053 /* Initialize the dialog items */
\r
6054 InitEngineBox(hDlg, GetDlgItem(hDlg, OPT_ChessEngineName),
\r
6055 appData.firstChessProgram, "fd", appData.firstDirectory,
\r
6056 firstChessProgramNames);
\r
6057 InitEngineBox(hDlg, GetDlgItem(hDlg, OPT_SecondChessEngineName),
\r
6058 appData.secondChessProgram, "sd", appData.secondDirectory,
\r
6059 secondChessProgramNames);
\r
6060 hwndCombo = GetDlgItem(hDlg, OPT_ChessServerName);
\r
6061 InitComboStringsFromOption(hwndCombo, icsNames);
\r
6062 snprintf(buf, MSG_SIZ, "%s /icsport=%s", appData.icsHost, appData.icsPort);
\r
6063 if (*appData.icsHelper != NULLCHAR) {
\r
6064 char *q = QuoteForFilename(appData.icsHelper);
\r
6065 sprintf(buf + strlen(buf), " /icshelper=%s%s%s", q, appData.icsHelper, q);
\r
6067 if (*appData.icsHost == NULLCHAR) {
\r
6068 SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) 0, (LPARAM) 0);
\r
6069 /*SendMessage(hwndCombo, CB_SHOWDROPDOWN, (WPARAM) TRUE, (LPARAM) 0); !!too soon */
\r
6070 } else if (SendMessage(hwndCombo, CB_SELECTSTRING, (WPARAM) -1, (LPARAM) buf) == CB_ERR) {
\r
6071 SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) -1, (LPARAM) 0);
\r
6072 SendMessage(hwndCombo, WM_SETTEXT, (WPARAM) 0, (LPARAM) buf);
\r
6075 if (appData.icsActive) {
\r
6076 CheckDlgButton(hDlg, OPT_ChessServer, BST_CHECKED);
\r
6078 else if (appData.noChessProgram) {
\r
6079 CheckDlgButton(hDlg, OPT_View, BST_CHECKED);
\r
6082 CheckDlgButton(hDlg, OPT_ChessEngine, BST_CHECKED);
\r
6085 SetStartupDialogEnables(hDlg);
\r
6089 switch (LOWORD(wParam)) {
\r
6091 if (IsDlgButtonChecked(hDlg, OPT_ChessEngine)) {
\r
6092 safeStrCpy(buf, "/fcp=", sizeof(buf)/sizeof(buf[0]) );
\r
6093 GetDlgItemText(hDlg, OPT_ChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));
\r
6095 ParseArgs(StringGet, &p);
\r
6096 safeStrCpy(buf, "/scp=", sizeof(buf)/sizeof(buf[0]) );
\r
6097 GetDlgItemText(hDlg, OPT_SecondChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));
\r
6099 ParseArgs(StringGet, &p);
\r
6100 appData.noChessProgram = FALSE;
\r
6101 appData.icsActive = FALSE;
\r
6102 } else if (IsDlgButtonChecked(hDlg, OPT_ChessServer)) {
\r
6103 safeStrCpy(buf, "/ics /icshost=", sizeof(buf)/sizeof(buf[0]) );
\r
6104 GetDlgItemText(hDlg, OPT_ChessServerName, buf + strlen(buf), sizeof(buf) - strlen(buf));
\r
6106 ParseArgs(StringGet, &p);
\r
6107 if (appData.zippyPlay) {
\r
6108 safeStrCpy(buf, "/fcp=", sizeof(buf)/sizeof(buf[0]) );
\r
6109 GetDlgItemText(hDlg, OPT_ChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));
\r
6111 ParseArgs(StringGet, &p);
\r
6113 } else if (IsDlgButtonChecked(hDlg, OPT_View)) {
\r
6114 appData.noChessProgram = TRUE;
\r
6115 appData.icsActive = FALSE;
\r
6117 MessageBox(hDlg, _("Choose an option, or cancel to exit"),
\r
6118 _("Option Error"), MB_OK|MB_ICONEXCLAMATION);
\r
6121 if (IsDlgButtonChecked(hDlg, OPT_AnyAdditional)) {
\r
6122 GetDlgItemText(hDlg, OPT_AdditionalOptions, buf, sizeof(buf));
\r
6124 ParseArgs(StringGet, &p);
\r
6126 EndDialog(hDlg, TRUE);
\r
6133 case IDM_HELPCONTENTS:
\r
6134 if (!WinHelp (hDlg, "winboard.hlp", HELP_KEY,(DWORD)(LPSTR)"CONTENTS")) {
\r
6135 MessageBox (GetFocus(),
\r
6136 _("Unable to activate help"),
\r
6137 szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);
\r
6142 SetStartupDialogEnables(hDlg);
\r
6150 /*---------------------------------------------------------------------------*\
\r
6152 * About box dialog functions
\r
6154 \*---------------------------------------------------------------------------*/
\r
6156 /* Process messages for "About" dialog box */
\r
6158 About(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
6160 switch (message) {
\r
6161 case WM_INITDIALOG: /* message: initialize dialog box */
\r
6162 /* Center the dialog over the application window */
\r
6163 CenterWindow (hDlg, GetWindow (hDlg, GW_OWNER));
\r
6164 SetDlgItemText(hDlg, ABOUTBOX_Version, programVersion);
\r
6165 Translate(hDlg, ABOUTBOX);
\r
6169 case WM_COMMAND: /* message: received a command */
\r
6170 if (LOWORD(wParam) == IDOK /* "OK" box selected? */
\r
6171 || LOWORD(wParam) == IDCANCEL) { /* System menu close command? */
\r
6172 EndDialog(hDlg, TRUE); /* Exit the dialog */
\r
6180 /*---------------------------------------------------------------------------*\
\r
6182 * Comment Dialog functions
\r
6184 \*---------------------------------------------------------------------------*/
\r
6187 CommentDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
6189 static HANDLE hwndText = NULL;
\r
6190 int len, newSizeX, newSizeY, flags;
\r
6191 static int sizeX, sizeY;
\r
6196 switch (message) {
\r
6197 case WM_INITDIALOG: /* message: initialize dialog box */
\r
6198 /* Initialize the dialog items */
\r
6199 Translate(hDlg, DLG_EditComment);
\r
6200 hwndText = GetDlgItem(hDlg, OPT_CommentText);
\r
6201 SetDlgItemText(hDlg, OPT_CommentText, commentText);
\r
6202 EnableWindow(GetDlgItem(hDlg, OPT_CancelComment), editComment);
\r
6203 EnableWindow(GetDlgItem(hDlg, OPT_ClearComment), editComment);
\r
6204 EnableWindow(GetDlgItem(hDlg, OPT_EditComment), !editComment);
\r
6205 SendMessage(hwndText, EM_SETREADONLY, !editComment, 0);
\r
6206 SetWindowText(hDlg, commentTitle);
\r
6207 if (editComment) {
\r
6208 SetFocus(hwndText);
\r
6210 SetFocus(GetDlgItem(hDlg, IDOK));
\r
6212 SendMessage(GetDlgItem(hDlg, OPT_CommentText),
\r
6213 WM_SETFONT, (WPARAM)font[boardSize][COMMENT_FONT]->hf,
\r
6214 MAKELPARAM(FALSE, 0));
\r
6215 /* Size and position the dialog */
\r
6216 if (!commentDialog) {
\r
6217 commentDialog = hDlg;
\r
6218 flags = SWP_NOZORDER;
\r
6219 GetClientRect(hDlg, &rect);
\r
6220 sizeX = rect.right;
\r
6221 sizeY = rect.bottom;
\r
6222 if (wpComment.x != CW_USEDEFAULT && wpComment.y != CW_USEDEFAULT &&
\r
6223 wpComment.width != CW_USEDEFAULT && wpComment.height != CW_USEDEFAULT) {
\r
6224 WINDOWPLACEMENT wp;
\r
6225 EnsureOnScreen(&wpComment.x, &wpComment.y, 0, 0);
\r
6226 wp.length = sizeof(WINDOWPLACEMENT);
\r
6228 wp.showCmd = SW_SHOW;
\r
6229 wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;
\r
6230 wp.rcNormalPosition.left = wpComment.x;
\r
6231 wp.rcNormalPosition.right = wpComment.x + wpComment.width;
\r
6232 wp.rcNormalPosition.top = wpComment.y;
\r
6233 wp.rcNormalPosition.bottom = wpComment.y + wpComment.height;
\r
6234 SetWindowPlacement(hDlg, &wp);
\r
6236 GetClientRect(hDlg, &rect);
\r
6237 newSizeX = rect.right;
\r
6238 newSizeY = rect.bottom;
\r
6239 ResizeEditPlusButtons(hDlg, hwndText, sizeX, sizeY,
\r
6240 newSizeX, newSizeY);
\r
6245 SendDlgItemMessage( hDlg, OPT_CommentText, EM_SETEVENTMASK, 0, ENM_MOUSEEVENTS );
\r
6248 case WM_COMMAND: /* message: received a command */
\r
6249 switch (LOWORD(wParam)) {
\r
6251 if (editComment) {
\r
6253 /* Read changed options from the dialog box */
\r
6254 hwndText = GetDlgItem(hDlg, OPT_CommentText);
\r
6255 len = GetWindowTextLength(hwndText);
\r
6256 str = (char *) malloc(len + 1);
\r
6257 GetWindowText(hwndText, str, len + 1);
\r
6266 ReplaceComment(commentIndex, str);
\r
6273 case OPT_CancelComment:
\r
6277 case OPT_ClearComment:
\r
6278 SetDlgItemText(hDlg, OPT_CommentText, "");
\r
6281 case OPT_EditComment:
\r
6282 EditCommentEvent();
\r
6290 case WM_NOTIFY: // [HGM] vari: cloned from whistory.c
\r
6291 if( wParam == OPT_CommentText ) {
\r
6292 MSGFILTER * lpMF = (MSGFILTER *) lParam;
\r
6294 if( lpMF->msg == WM_RBUTTONDOWN && (lpMF->wParam & (MK_CONTROL | MK_SHIFT)) == 0 ) {
\r
6298 pt.x = LOWORD( lpMF->lParam );
\r
6299 pt.y = HIWORD( lpMF->lParam );
\r
6301 index = SendDlgItemMessage( hDlg, OPT_CommentText, EM_CHARFROMPOS, 0, (LPARAM) &pt );
\r
6303 hwndText = GetDlgItem(hDlg, OPT_CommentText); // cloned from above
\r
6304 len = GetWindowTextLength(hwndText);
\r
6305 str = (char *) malloc(len + 1);
\r
6306 GetWindowText(hwndText, str, len + 1);
\r
6307 ReplaceComment(commentIndex, str);
\r
6308 if(commentIndex != currentMove) ToNrEvent(commentIndex);
\r
6309 LoadVariation( index, str ); // [HGM] also does the actual moving to it, now
\r
6312 /* Zap the message for good: apparently, returning non-zero is not enough */
\r
6313 lpMF->msg = WM_USER;
\r
6321 newSizeX = LOWORD(lParam);
\r
6322 newSizeY = HIWORD(lParam);
\r
6323 ResizeEditPlusButtons(hDlg, hwndText, sizeX, sizeY, newSizeX, newSizeY);
\r
6328 case WM_GETMINMAXINFO:
\r
6329 /* Prevent resizing window too small */
\r
6330 mmi = (MINMAXINFO *) lParam;
\r
6331 mmi->ptMinTrackSize.x = 100;
\r
6332 mmi->ptMinTrackSize.y = 100;
\r
6339 EitherCommentPopUp(int index, char *title, char *str, BOOLEAN edit)
\r
6344 CheckMenuItem(GetMenu(hwndMain), IDM_Comment, edit ? MF_CHECKED : MF_UNCHECKED);
\r
6346 if (str == NULL) str = "";
\r
6347 p = (char *) malloc(2 * strlen(str) + 2);
\r
6350 if (*str == '\n') *q++ = '\r';
\r
6354 if (commentText != NULL) free(commentText);
\r
6356 commentIndex = index;
\r
6357 commentTitle = title;
\r
6359 editComment = edit;
\r
6361 if (commentDialog) {
\r
6362 SendMessage(commentDialog, WM_INITDIALOG, 0, 0);
\r
6363 if (!commentUp) ShowWindow(commentDialog, SW_SHOW);
\r
6365 lpProc = MakeProcInstance((FARPROC)CommentDialog, hInst);
\r
6366 CreateDialog(hInst, MAKEINTRESOURCE(DLG_EditComment),
\r
6367 hwndMain, (DLGPROC)lpProc);
\r
6368 FreeProcInstance(lpProc);
\r
6374 /*---------------------------------------------------------------------------*\
\r
6376 * Type-in move dialog functions
\r
6378 \*---------------------------------------------------------------------------*/
\r
6381 TypeInMoveDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
6383 char move[MSG_SIZ];
\r
6385 ChessMove moveType;
\r
6386 int fromX, fromY, toX, toY;
\r
6389 switch (message) {
\r
6390 case WM_INITDIALOG:
\r
6391 move[0] = (char) lParam;
\r
6392 move[1] = NULLCHAR;
\r
6393 CenterWindowEx(hDlg, GetWindow(hDlg, GW_OWNER), 1 );
\r
6394 Translate(hDlg, DLG_TypeInMove);
\r
6395 hInput = GetDlgItem(hDlg, OPT_Move);
\r
6396 SetWindowText(hInput, move);
\r
6398 SendMessage(hInput, EM_SETSEL, (WPARAM)9999, (LPARAM)9999);
\r
6402 switch (LOWORD(wParam)) {
\r
6404 shiftKey = GetKeyState(VK_SHIFT) < 0; // [HGM] remember last shift status
\r
6405 GetDlgItemText(hDlg, OPT_Move, move, sizeof(move));
\r
6406 { int n; Board board;
\r
6408 if(gameMode == EditPosition && ParseFEN(board, &n, move) ) {
\r
6409 EditPositionPasteFEN(move);
\r
6410 EndDialog(hDlg, TRUE);
\r
6413 // [HGM] movenum: allow move number to be typed in any mode
\r
6414 if(sscanf(move, "%d", &n) == 1 && n != 0 ) {
\r
6416 EndDialog(hDlg, TRUE);
\r
6420 if (gameMode != EditGame && currentMove != forwardMostMove &&
\r
6421 gameMode != Training) {
\r
6422 DisplayMoveError(_("Displayed move is not current"));
\r
6424 // GetDlgItemText(hDlg, OPT_Move, move, sizeof(move)); // moved upstream
\r
6425 int ok = ParseOneMove(move, gameMode == EditPosition ? blackPlaysFirst : currentMove,
\r
6426 &moveType, &fromX, &fromY, &toX, &toY, &promoChar);
\r
6427 if(!ok && move[0] >= 'a') { move[0] += 'A' - 'a'; ok = 2; } // [HGM] try also capitalized
\r
6428 if (ok==1 || ok && ParseOneMove(move, gameMode == EditPosition ? blackPlaysFirst : currentMove,
\r
6429 &moveType, &fromX, &fromY, &toX, &toY, &promoChar)) {
\r
6430 if (gameMode != Training)
\r
6431 forwardMostMove = currentMove;
\r
6432 UserMoveEvent(fromX, fromY, toX, toY, promoChar);
\r
6434 DisplayMoveError(_("Could not parse move"));
\r
6437 EndDialog(hDlg, TRUE);
\r
6440 EndDialog(hDlg, FALSE);
\r
6451 PopUpMoveDialog(char firstchar)
\r
6455 if ((gameMode == BeginningOfGame && !appData.icsActive) ||
\r
6456 gameMode == MachinePlaysWhite || gameMode == MachinePlaysBlack ||
\r
6457 gameMode == AnalyzeMode || gameMode == EditGame ||
\r
6458 gameMode == EditPosition || gameMode == IcsExamining ||
\r
6459 gameMode == IcsPlayingWhite || gameMode == IcsPlayingBlack ||
\r
6460 isdigit(firstchar) && // [HGM] movenum: allow typing in of move nr in 'passive' modes
\r
6461 ( gameMode == AnalyzeFile || gameMode == PlayFromGameFile ||
\r
6462 gameMode == IcsObserving || gameMode == TwoMachinesPlay ) ||
\r
6463 gameMode == Training) {
\r
6464 lpProc = MakeProcInstance((FARPROC)TypeInMoveDialog, hInst);
\r
6465 DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_TypeInMove),
\r
6466 hwndMain, (DLGPROC)lpProc, (LPARAM)firstchar);
\r
6467 FreeProcInstance(lpProc);
\r
6471 /*---------------------------------------------------------------------------*\
\r
6473 * Type-in name dialog functions
\r
6475 \*---------------------------------------------------------------------------*/
\r
6478 TypeInNameDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
6480 char move[MSG_SIZ];
\r
6483 switch (message) {
\r
6484 case WM_INITDIALOG:
\r
6485 move[0] = (char) lParam;
\r
6486 move[1] = NULLCHAR;
\r
6487 CenterWindowEx(hDlg, GetWindow(hDlg, GW_OWNER), 1 );
\r
6488 Translate(hDlg, DLG_TypeInName);
\r
6489 hInput = GetDlgItem(hDlg, OPT_Name);
\r
6490 SetWindowText(hInput, move);
\r
6492 SendMessage(hInput, EM_SETSEL, (WPARAM)9999, (LPARAM)9999);
\r
6496 switch (LOWORD(wParam)) {
\r
6498 GetDlgItemText(hDlg, OPT_Name, move, sizeof(move));
\r
6499 appData.userName = strdup(move);
\r
6502 if(gameMode == MachinePlaysWhite || gameMode == MachinePlaysBlack) {
\r
6503 snprintf(move, MSG_SIZ, "%s vs. %s", gameInfo.white, gameInfo.black);
\r
6504 DisplayTitle(move);
\r
6508 EndDialog(hDlg, TRUE);
\r
6511 EndDialog(hDlg, FALSE);
\r
6522 PopUpNameDialog(char firstchar)
\r
6526 lpProc = MakeProcInstance((FARPROC)TypeInNameDialog, hInst);
\r
6527 DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_TypeInName),
\r
6528 hwndMain, (DLGPROC)lpProc, (LPARAM)firstchar);
\r
6529 FreeProcInstance(lpProc);
\r
6532 /*---------------------------------------------------------------------------*\
\r
6536 \*---------------------------------------------------------------------------*/
\r
6538 /* Nonmodal error box */
\r
6539 LRESULT CALLBACK ErrorDialog(HWND hDlg, UINT message,
\r
6540 WPARAM wParam, LPARAM lParam);
\r
6543 ErrorPopUp(char *title, char *content)
\r
6547 BOOLEAN modal = hwndMain == NULL;
\r
6565 strncpy(errorTitle, title, sizeof(errorTitle));
\r
6566 errorTitle[sizeof(errorTitle) - 1] = '\0';
\r
6569 MessageBox(NULL, errorMessage, errorTitle, MB_OK|MB_ICONEXCLAMATION);
\r
6571 lpProc = MakeProcInstance((FARPROC)ErrorDialog, hInst);
\r
6572 CreateDialog(hInst, MAKEINTRESOURCE(DLG_Error),
\r
6573 hwndMain, (DLGPROC)lpProc);
\r
6574 FreeProcInstance(lpProc);
\r
6581 if (!appData.popupMoveErrors && moveErrorMessageUp) DisplayMessage("", "");
\r
6582 if (errorDialog == NULL) return;
\r
6583 DestroyWindow(errorDialog);
\r
6584 errorDialog = NULL;
\r
6585 if(errorExitStatus) ExitEvent(errorExitStatus);
\r
6589 ErrorDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
6594 switch (message) {
\r
6595 case WM_INITDIALOG:
\r
6596 GetWindowRect(hDlg, &rChild);
\r
6599 SetWindowPos(hDlg, NULL, rChild.left,
\r
6600 rChild.top + boardRect.top - (rChild.bottom - rChild.top),
\r
6601 0, 0, SWP_NOZORDER|SWP_NOSIZE);
\r
6605 [AS] It seems that the above code wants to move the dialog up in the "caption
\r
6606 area" of the main window, but it uses the dialog height as an hard-coded constant,
\r
6607 and it doesn't work when you resize the dialog.
\r
6608 For now, just give it a default position.
\r
6610 SetWindowPos(hDlg, NULL, boardRect.left+8, boardRect.top+8, 0, 0, SWP_NOZORDER|SWP_NOSIZE);
\r
6611 Translate(hDlg, DLG_Error);
\r
6613 errorDialog = hDlg;
\r
6614 SetWindowText(hDlg, errorTitle);
\r
6615 hwndText = GetDlgItem(hDlg, OPT_ErrorText);
\r
6616 SetDlgItemText(hDlg, OPT_ErrorText, errorMessage);
\r
6620 switch (LOWORD(wParam)) {
\r
6623 if (errorDialog == hDlg) errorDialog = NULL;
\r
6624 DestroyWindow(hDlg);
\r
6636 HWND gothicDialog = NULL;
\r
6639 GothicDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
6643 int height = GetSystemMetrics(SM_CYCAPTION)+GetSystemMetrics(SM_CYFRAME);
\r
6645 switch (message) {
\r
6646 case WM_INITDIALOG:
\r
6647 GetWindowRect(hDlg, &rChild);
\r
6649 SetWindowPos(hDlg, NULL, wpMain.x, wpMain.y-height, wpMain.width, height,
\r
6653 [AS] It seems that the above code wants to move the dialog up in the "caption
\r
6654 area" of the main window, but it uses the dialog height as an hard-coded constant,
\r
6655 and it doesn't work when you resize the dialog.
\r
6656 For now, just give it a default position.
\r
6658 gothicDialog = hDlg;
\r
6659 SetWindowText(hDlg, errorTitle);
\r
6660 hwndText = GetDlgItem(hDlg, OPT_ErrorText);
\r
6661 SetDlgItemText(hDlg, OPT_ErrorText, errorMessage);
\r
6665 switch (LOWORD(wParam)) {
\r
6668 if (errorDialog == hDlg) errorDialog = NULL;
\r
6669 DestroyWindow(hDlg);
\r
6681 GothicPopUp(char *title, VariantClass variant)
\r
6684 static char *lastTitle;
\r
6686 strncpy(errorTitle, title, sizeof(errorTitle));
\r
6687 errorTitle[sizeof(errorTitle) - 1] = '\0';
\r
6689 if(lastTitle != title && gothicDialog != NULL) {
\r
6690 DestroyWindow(gothicDialog);
\r
6691 gothicDialog = NULL;
\r
6693 if(variant != VariantNormal && gothicDialog == NULL) {
\r
6694 title = lastTitle;
\r
6695 lpProc = MakeProcInstance((FARPROC)GothicDialog, hInst);
\r
6696 CreateDialog(hInst, MAKEINTRESOURCE(DLG_Error),
\r
6697 hwndMain, (DLGPROC)lpProc);
\r
6698 FreeProcInstance(lpProc);
\r
6703 /*---------------------------------------------------------------------------*\
\r
6705 * Ics Interaction console functions
\r
6707 \*---------------------------------------------------------------------------*/
\r
6709 #define HISTORY_SIZE 64
\r
6710 static char *history[HISTORY_SIZE];
\r
6711 int histIn = 0, histP = 0;
\r
6714 SaveInHistory(char *cmd)
\r
6716 if (history[histIn] != NULL) {
\r
6717 free(history[histIn]);
\r
6718 history[histIn] = NULL;
\r
6720 if (*cmd == NULLCHAR) return;
\r
6721 history[histIn] = StrSave(cmd);
\r
6722 histIn = (histIn + 1) % HISTORY_SIZE;
\r
6723 if (history[histIn] != NULL) {
\r
6724 free(history[histIn]);
\r
6725 history[histIn] = NULL;
\r
6731 PrevInHistory(char *cmd)
\r
6734 if (histP == histIn) {
\r
6735 if (history[histIn] != NULL) free(history[histIn]);
\r
6736 history[histIn] = StrSave(cmd);
\r
6738 newhp = (histP - 1 + HISTORY_SIZE) % HISTORY_SIZE;
\r
6739 if (newhp == histIn || history[newhp] == NULL) return NULL;
\r
6741 return history[histP];
\r
6747 if (histP == histIn) return NULL;
\r
6748 histP = (histP + 1) % HISTORY_SIZE;
\r
6749 return history[histP];
\r
6753 LoadIcsTextMenu(IcsTextMenuEntry *e)
\r
6757 hmenu = LoadMenu(hInst, "TextMenu");
\r
6758 h = GetSubMenu(hmenu, 0);
\r
6760 if (strcmp(e->item, "-") == 0) {
\r
6761 AppendMenu(h, MF_SEPARATOR, 0, 0);
\r
6762 } else { // [HGM] re-written a bit to use only one AppendMenu call for both cases (| or no |)
\r
6763 int flags = MF_STRING, j = 0;
\r
6764 if (e->item[0] == '|') {
\r
6765 flags |= MF_MENUBARBREAK;
\r
6768 if(!strcmp(e->command, "none")) flags |= MF_GRAYED; // [HGM] chatclick: provide inactive dummy
\r
6769 AppendMenu(h, flags, IDM_CommandX + i, e->item + j);
\r
6777 WNDPROC consoleTextWindowProc;
\r
6780 CommandX(HWND hwnd, char *command, BOOLEAN getname, BOOLEAN immediate)
\r
6782 char buf[MSG_SIZ], name[MSG_SIZ];
\r
6783 HWND hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
6787 SetWindowText(hInput, command);
\r
6789 SendMessage(hInput, WM_CHAR, '\r', 0);
\r
6791 sel.cpMin = 999999;
\r
6792 sel.cpMax = 999999;
\r
6793 SendMessage(hInput, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
6798 SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);
\r
6799 if (sel.cpMin == sel.cpMax) {
\r
6800 /* Expand to surrounding word */
\r
6803 tr.chrg.cpMax = sel.cpMin;
\r
6804 tr.chrg.cpMin = --sel.cpMin;
\r
6805 if (sel.cpMin < 0) break;
\r
6806 tr.lpstrText = name;
\r
6807 SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr);
\r
6808 } while (isalpha(name[0]) || isdigit(name[0]) || name[0] == '-');
\r
6812 tr.chrg.cpMin = sel.cpMax;
\r
6813 tr.chrg.cpMax = ++sel.cpMax;
\r
6814 tr.lpstrText = name;
\r
6815 if (SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr) < 1) break;
\r
6816 } while (isalpha(name[0]) || isdigit(name[0]) || name[0] == '-');
\r
6819 if (sel.cpMax == sel.cpMin || sel.cpMax - sel.cpMin > MSG_SIZ/2) {
\r
6820 MessageBeep(MB_ICONEXCLAMATION);
\r
6824 tr.lpstrText = name;
\r
6825 SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr);
\r
6827 if (sel.cpMax - sel.cpMin > MSG_SIZ/2) {
\r
6828 MessageBeep(MB_ICONEXCLAMATION);
\r
6831 SendMessage(hwnd, EM_GETSELTEXT, 0, (LPARAM) name);
\r
6834 if(strstr(command, "%s")) snprintf(buf, MSG_SIZ, command, name); else
\r
6835 snprintf(buf, MSG_SIZ, "%s %s", command, name);
\r
6836 SetWindowText(hInput, buf);
\r
6837 SendMessage(hInput, WM_CHAR, '\r', 0);
\r
6839 if(!strcmp(command, "chat")) { ChatPopUp(name); return; }
\r
6840 snprintf(buf, MSG_SIZ, "%s %s ", command, name); /* trailing space */
\r
6841 SetWindowText(hInput, buf);
\r
6842 sel.cpMin = 999999;
\r
6843 sel.cpMax = 999999;
\r
6844 SendMessage(hInput, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
6850 ConsoleTextSubclass(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
\r
6855 switch (message) {
\r
6857 if (!(GetKeyState(VK_CONTROL) & ~1)) break;
\r
6860 SendMessage(hwnd, EM_LINESCROLL, 0, -999999);
\r
6863 sel.cpMin = 999999;
\r
6864 sel.cpMax = 999999;
\r
6865 SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
6866 SendMessage(hwnd, EM_SCROLLCARET, 0, 0);
\r
6871 if(wParam != '\022') {
\r
6872 if (wParam == '\t') {
\r
6873 if (GetKeyState(VK_SHIFT) < 0) {
\r
6875 if (IsIconic(hwndMain)) ShowWindow(hwndMain, SW_RESTORE);
\r
6876 if (buttonDesc[0].hwnd) {
\r
6877 SetFocus(buttonDesc[0].hwnd);
\r
6879 SetFocus(hwndMain);
\r
6883 SetFocus(GetDlgItem(hwndConsole, OPT_ConsoleInput));
\r
6886 hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
6887 JAWS_DELETE( SetFocus(hInput); )
\r
6888 SendMessage(hInput, message, wParam, lParam);
\r
6891 } // [HGM] navigate: for Ctrl+R, flow into nex case (moved up here) to summon up menu
\r
6892 case WM_RBUTTONDOWN:
\r
6893 if (!(GetKeyState(VK_SHIFT) & ~1)) {
\r
6894 /* Move selection here if it was empty */
\r
6896 pt.x = LOWORD(lParam);
\r
6897 pt.y = HIWORD(lParam);
\r
6898 SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);
\r
6899 if (sel.cpMin == sel.cpMax) {
\r
6900 sel.cpMin = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&pt); /*doc is wrong*/
\r
6901 sel.cpMax = sel.cpMin;
\r
6902 SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
6904 SendMessage(hwnd, EM_HIDESELECTION, FALSE, FALSE);
\r
6905 { // [HGM] chatclick: code moved here from WM_RBUTTONUP case, to have menu appear on down-click
\r
6907 HMENU hmenu = LoadIcsTextMenu(icsTextMenuEntry);
\r
6908 SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);
\r
6909 if (sel.cpMin == sel.cpMax) {
\r
6910 EnableMenuItem(hmenu, IDM_Copy, MF_BYCOMMAND|MF_GRAYED);
\r
6911 EnableMenuItem(hmenu, IDM_QuickPaste, MF_BYCOMMAND|MF_GRAYED);
\r
6913 if (!IsClipboardFormatAvailable(CF_TEXT)) {
\r
6914 EnableMenuItem(hmenu, IDM_Paste, MF_BYCOMMAND|MF_GRAYED);
\r
6916 pt.x = LOWORD(lParam)-30; // [HGM] chatclick: make menu pop up with pointer above upper-right item
\r
6917 pt.y = HIWORD(lParam)-10; // make it appear as if mouse moved there, so it will be selected on up-click
\r
6918 PostMessage(hwnd, WM_MOUSEMOVE, wParam, lParam+5);
\r
6919 MenuPopup(hwnd, pt, hmenu, -1);
\r
6923 case WM_RBUTTONUP:
\r
6924 if (GetKeyState(VK_SHIFT) & ~1) {
\r
6925 SendDlgItemMessage(hwndConsole, OPT_ConsoleText,
\r
6926 WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);
\r
6930 hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
6932 return SendMessage(hInput, message, wParam, lParam);
\r
6933 case WM_MBUTTONDOWN:
\r
6934 return SendMessage(hwnd, WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);
\r
6936 switch (LOWORD(wParam)) {
\r
6937 case IDM_QuickPaste:
\r
6939 SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);
\r
6940 if (sel.cpMin == sel.cpMax) {
\r
6941 MessageBeep(MB_ICONEXCLAMATION);
\r
6944 SendMessage(hwnd, WM_COPY, 0, 0);
\r
6945 hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
6946 SendMessage(hInput, WM_PASTE, 0, 0);
\r
6951 SendMessage(hwnd, WM_CUT, 0, 0);
\r
6954 SendMessage(hwnd, WM_PASTE, 0, 0);
\r
6957 SendMessage(hwnd, WM_COPY, 0, 0);
\r
6961 int i = LOWORD(wParam) - IDM_CommandX;
\r
6962 if (i >= 0 && i < ICS_TEXT_MENU_SIZE &&
\r
6963 icsTextMenuEntry[i].command != NULL) {
\r
6964 CommandX(hwnd, icsTextMenuEntry[i].command,
\r
6965 icsTextMenuEntry[i].getname,
\r
6966 icsTextMenuEntry[i].immediate);
\r
6974 return (*consoleTextWindowProc)(hwnd, message, wParam, lParam);
\r
6977 WNDPROC consoleInputWindowProc;
\r
6980 ConsoleInputSubclass(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
\r
6982 char buf[MSG_SIZ];
\r
6984 static BOOL sendNextChar = FALSE;
\r
6985 static BOOL quoteNextChar = FALSE;
\r
6986 InputSource *is = consoleInputSource;
\r
6990 switch (message) {
\r
6992 if (!appData.localLineEditing || sendNextChar) {
\r
6993 is->buf[0] = (CHAR) wParam;
\r
6995 SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);
\r
6996 sendNextChar = FALSE;
\r
6999 if (quoteNextChar) {
\r
7000 buf[0] = (char) wParam;
\r
7001 buf[1] = NULLCHAR;
\r
7002 SendMessage(hwnd, EM_REPLACESEL, TRUE, (LPARAM) buf);
\r
7003 quoteNextChar = FALSE;
\r
7007 case '\r': /* Enter key */
\r
7008 is->count = GetWindowText(hwnd, is->buf, INPUT_SOURCE_BUF_SIZE-1);
\r
7009 if (consoleEcho) SaveInHistory(is->buf);
\r
7010 is->buf[is->count++] = '\n';
\r
7011 is->buf[is->count] = NULLCHAR;
\r
7012 SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);
\r
7013 if (consoleEcho) {
\r
7014 ConsoleOutput(is->buf, is->count, TRUE);
\r
7015 } else if (appData.localLineEditing) {
\r
7016 ConsoleOutput("\n", 1, TRUE);
\r
7019 case '\033': /* Escape key */
\r
7020 SetWindowText(hwnd, "");
\r
7021 cf.cbSize = sizeof(CHARFORMAT);
\r
7022 cf.dwMask = CFM_COLOR|CFM_BOLD|CFM_ITALIC|CFM_UNDERLINE|CFM_STRIKEOUT;
\r
7023 if (consoleEcho) {
\r
7024 cf.crTextColor = textAttribs[ColorNormal].color;
\r
7026 cf.crTextColor = COLOR_ECHOOFF;
\r
7028 cf.dwEffects = textAttribs[ColorNormal].effects;
\r
7029 SendMessage(hwnd, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf);
\r
7031 case '\t': /* Tab key */
\r
7032 if (GetKeyState(VK_SHIFT) < 0) {
\r
7034 SetFocus(GetDlgItem(hwndConsole, OPT_ConsoleText));
\r
7037 if (IsIconic(hwndMain)) ShowWindow(hwndMain, SW_RESTORE);
\r
7038 if (buttonDesc[0].hwnd) {
\r
7039 SetFocus(buttonDesc[0].hwnd);
\r
7041 SetFocus(hwndMain);
\r
7045 case '\023': /* Ctrl+S */
\r
7046 sendNextChar = TRUE;
\r
7048 case '\021': /* Ctrl+Q */
\r
7049 quoteNextChar = TRUE;
\r
7059 GetWindowText(hwnd, buf, MSG_SIZ);
\r
7060 p = PrevInHistory(buf);
\r
7062 SetWindowText(hwnd, p);
\r
7063 sel.cpMin = 999999;
\r
7064 sel.cpMax = 999999;
\r
7065 SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
7070 p = NextInHistory();
\r
7072 SetWindowText(hwnd, p);
\r
7073 sel.cpMin = 999999;
\r
7074 sel.cpMax = 999999;
\r
7075 SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
7081 if (!(GetKeyState(VK_CONTROL) & ~1)) break;
\r
7085 SendDlgItemMessage(hwndConsole, OPT_ConsoleText, message, wParam, lParam);
\r
7089 case WM_MBUTTONDOWN:
\r
7090 SendDlgItemMessage(hwndConsole, OPT_ConsoleText,
\r
7091 WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);
\r
7093 case WM_RBUTTONUP:
\r
7094 if (GetKeyState(VK_SHIFT) & ~1) {
\r
7095 SendDlgItemMessage(hwndConsole, OPT_ConsoleText,
\r
7096 WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);
\r
7100 hmenu = LoadMenu(hInst, "InputMenu");
\r
7101 SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);
\r
7102 if (sel.cpMin == sel.cpMax) {
\r
7103 EnableMenuItem(hmenu, IDM_Copy, MF_BYCOMMAND|MF_GRAYED);
\r
7104 EnableMenuItem(hmenu, IDM_Cut, MF_BYCOMMAND|MF_GRAYED);
\r
7106 if (!IsClipboardFormatAvailable(CF_TEXT)) {
\r
7107 EnableMenuItem(hmenu, IDM_Paste, MF_BYCOMMAND|MF_GRAYED);
\r
7109 pt.x = LOWORD(lParam);
\r
7110 pt.y = HIWORD(lParam);
\r
7111 MenuPopup(hwnd, pt, hmenu, -1);
\r
7115 switch (LOWORD(wParam)) {
\r
7117 SendMessage(hwnd, EM_UNDO, 0, 0);
\r
7119 case IDM_SelectAll:
\r
7121 sel.cpMax = -1; /*999999?*/
\r
7122 SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
7125 SendMessage(hwnd, WM_CUT, 0, 0);
\r
7128 SendMessage(hwnd, WM_PASTE, 0, 0);
\r
7131 SendMessage(hwnd, WM_COPY, 0, 0);
\r
7136 return (*consoleInputWindowProc)(hwnd, message, wParam, lParam);
\r
7139 #define CO_MAX 100000
\r
7140 #define CO_TRIM 1000
\r
7143 ConsoleWndProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
7145 static SnapData sd;
\r
7146 HWND hText, hInput;
\r
7148 static int sizeX, sizeY;
\r
7149 int newSizeX, newSizeY;
\r
7153 hText = GetDlgItem(hDlg, OPT_ConsoleText);
\r
7154 hInput = GetDlgItem(hDlg, OPT_ConsoleInput);
\r
7156 switch (message) {
\r
7158 if (((NMHDR*)lParam)->code == EN_LINK)
\r
7160 ENLINK *pLink = (ENLINK*)lParam;
\r
7161 if (pLink->msg == WM_LBUTTONUP)
\r
7165 tr.chrg = pLink->chrg;
\r
7166 tr.lpstrText = malloc(1+tr.chrg.cpMax-tr.chrg.cpMin);
\r
7167 SendMessage(hText, EM_GETTEXTRANGE, 0, (LPARAM)&tr);
\r
7168 ShellExecute(NULL, "open", tr.lpstrText, NULL, NULL, SW_SHOW);
\r
7169 free(tr.lpstrText);
\r
7173 case WM_INITDIALOG: /* message: initialize dialog box */
\r
7174 hwndConsole = hDlg;
\r
7176 consoleTextWindowProc = (WNDPROC)
\r
7177 SetWindowLong(hText, GWL_WNDPROC, (LONG) ConsoleTextSubclass);
\r
7178 SendMessage(hText, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);
\r
7179 consoleInputWindowProc = (WNDPROC)
\r
7180 SetWindowLong(hInput, GWL_WNDPROC, (LONG) ConsoleInputSubclass);
\r
7181 SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);
\r
7182 Colorize(ColorNormal, TRUE);
\r
7183 SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &consoleCF);
\r
7184 ChangedConsoleFont();
\r
7185 GetClientRect(hDlg, &rect);
\r
7186 sizeX = rect.right;
\r
7187 sizeY = rect.bottom;
\r
7188 if (wpConsole.x != CW_USEDEFAULT && wpConsole.y != CW_USEDEFAULT &&
\r
7189 wpConsole.width != CW_USEDEFAULT && wpConsole.height != CW_USEDEFAULT) {
\r
7190 WINDOWPLACEMENT wp;
\r
7191 EnsureOnScreen(&wpConsole.x, &wpConsole.y, 0, 0);
\r
7192 wp.length = sizeof(WINDOWPLACEMENT);
\r
7194 wp.showCmd = SW_SHOW;
\r
7195 wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;
\r
7196 wp.rcNormalPosition.left = wpConsole.x;
\r
7197 wp.rcNormalPosition.right = wpConsole.x + wpConsole.width;
\r
7198 wp.rcNormalPosition.top = wpConsole.y;
\r
7199 wp.rcNormalPosition.bottom = wpConsole.y + wpConsole.height;
\r
7200 SetWindowPlacement(hDlg, &wp);
\r
7203 // [HGM] Chessknight's change 2004-07-13
\r
7204 else { /* Determine Defaults */
\r
7205 WINDOWPLACEMENT wp;
\r
7206 wpConsole.x = wpMain.width + 1;
\r
7207 wpConsole.y = wpMain.y;
\r
7208 wpConsole.width = screenWidth - wpMain.width;
\r
7209 wpConsole.height = wpMain.height;
\r
7210 EnsureOnScreen(&wpConsole.x, &wpConsole.y, 0, 0);
\r
7211 wp.length = sizeof(WINDOWPLACEMENT);
\r
7213 wp.showCmd = SW_SHOW;
\r
7214 wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;
\r
7215 wp.rcNormalPosition.left = wpConsole.x;
\r
7216 wp.rcNormalPosition.right = wpConsole.x + wpConsole.width;
\r
7217 wp.rcNormalPosition.top = wpConsole.y;
\r
7218 wp.rcNormalPosition.bottom = wpConsole.y + wpConsole.height;
\r
7219 SetWindowPlacement(hDlg, &wp);
\r
7222 // Allow hText to highlight URLs and send notifications on them
\r
7223 wMask = (WORD) SendMessage(hText, EM_GETEVENTMASK, 0, 0L);
\r
7224 SendMessage(hText, EM_SETEVENTMASK, 0, wMask | ENM_LINK);
\r
7225 SendMessage(hText, EM_AUTOURLDETECT, TRUE, 0L);
\r
7226 SetWindowLong(hText, GWL_USERDATA, 79); // initialize the text window's width
\r
7240 if (IsIconic(hDlg)) break;
\r
7241 newSizeX = LOWORD(lParam);
\r
7242 newSizeY = HIWORD(lParam);
\r
7243 if (sizeX != newSizeX || sizeY != newSizeY) {
\r
7244 RECT rectText, rectInput;
\r
7246 int newTextHeight, newTextWidth;
\r
7247 GetWindowRect(hText, &rectText);
\r
7248 newTextWidth = rectText.right - rectText.left + newSizeX - sizeX;
\r
7249 newTextHeight = rectText.bottom - rectText.top + newSizeY - sizeY;
\r
7250 if (newTextHeight < 0) {
\r
7251 newSizeY += -newTextHeight;
\r
7252 newTextHeight = 0;
\r
7254 SetWindowPos(hText, NULL, 0, 0,
\r
7255 newTextWidth, newTextHeight, SWP_NOZORDER|SWP_NOMOVE);
\r
7256 GetWindowRect(hInput, &rectInput); /* gives screen coords */
\r
7257 pt.x = rectInput.left;
\r
7258 pt.y = rectInput.top + newSizeY - sizeY;
\r
7259 ScreenToClient(hDlg, &pt);
\r
7260 SetWindowPos(hInput, NULL,
\r
7261 pt.x, pt.y, /* needs client coords */
\r
7262 rectInput.right - rectInput.left + newSizeX - sizeX,
\r
7263 rectInput.bottom - rectInput.top, SWP_NOZORDER);
\r
7269 case WM_GETMINMAXINFO:
\r
7270 /* Prevent resizing window too small */
\r
7271 mmi = (MINMAXINFO *) lParam;
\r
7272 mmi->ptMinTrackSize.x = 100;
\r
7273 mmi->ptMinTrackSize.y = 100;
\r
7276 /* [AS] Snapping */
\r
7277 case WM_ENTERSIZEMOVE:
\r
7278 return OnEnterSizeMove( &sd, hDlg, wParam, lParam );
\r
7281 return OnSizing( &sd, hDlg, wParam, lParam );
\r
7284 return OnMoving( &sd, hDlg, wParam, lParam );
\r
7286 case WM_EXITSIZEMOVE:
\r
7287 UpdateICSWidth(hText);
\r
7288 return OnExitSizeMove( &sd, hDlg, wParam, lParam );
\r
7291 return DefWindowProc(hDlg, message, wParam, lParam);
\r
7299 if (hwndConsole) return;
\r
7300 hCons = CreateDialog(hInst, szConsoleName, 0, NULL);
\r
7301 SendMessage(hCons, WM_INITDIALOG, 0, 0);
\r
7306 ConsoleOutput(char* data, int length, int forceVisible)
\r
7311 char buf[CO_MAX+1];
\r
7314 static int delayLF = 0;
\r
7315 CHARRANGE savesel, sel;
\r
7317 if (hwndConsole == NULL || length > CO_MAX-100 || length == 0) return;
\r
7325 while (length--) {
\r
7333 } else if (*p == '\007') {
\r
7334 MyPlaySound(&sounds[(int)SoundBell]);
\r
7341 hText = GetDlgItem(hwndConsole, OPT_ConsoleText);
\r
7342 SendMessage(hText, EM_HIDESELECTION, TRUE, FALSE);
\r
7343 /* Save current selection */
\r
7344 SendMessage(hText, EM_EXGETSEL, 0, (LPARAM)&savesel);
\r
7345 exlen = GetWindowTextLength(hText);
\r
7346 /* Find out whether current end of text is visible */
\r
7347 SendMessage(hText, EM_GETRECT, 0, (LPARAM) &rect);
\r
7348 SendMessage(hText, EM_POSFROMCHAR, (WPARAM) &pEnd, exlen);
\r
7349 /* Trim existing text if it's too long */
\r
7350 if (exlen + (q - buf) > CO_MAX) {
\r
7351 trim = (CO_TRIM > (q - buf)) ? CO_TRIM : (q - buf);
\r
7354 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
7355 SendMessage(hText, EM_REPLACESEL, 0, (LPARAM)"");
\r
7357 savesel.cpMin -= trim;
\r
7358 savesel.cpMax -= trim;
\r
7359 if (exlen < 0) exlen = 0;
\r
7360 if (savesel.cpMin < 0) savesel.cpMin = 0;
\r
7361 if (savesel.cpMax < savesel.cpMin) savesel.cpMax = savesel.cpMin;
\r
7363 /* Append the new text */
\r
7364 sel.cpMin = exlen;
\r
7365 sel.cpMax = exlen;
\r
7366 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
7367 SendMessage(hText, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&consoleCF);
\r
7368 SendMessage(hText, EM_REPLACESEL, 0, (LPARAM) buf);
\r
7369 if (forceVisible || exlen == 0 ||
\r
7370 (rect.left <= pEnd.x && pEnd.x < rect.right &&
\r
7371 rect.top <= pEnd.y && pEnd.y < rect.bottom)) {
\r
7372 /* Scroll to make new end of text visible if old end of text
\r
7373 was visible or new text is an echo of user typein */
\r
7374 sel.cpMin = 9999999;
\r
7375 sel.cpMax = 9999999;
\r
7376 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
7377 SendMessage(hText, EM_HIDESELECTION, FALSE, FALSE);
\r
7378 SendMessage(hText, EM_SCROLLCARET, 0, 0);
\r
7379 SendMessage(hText, EM_HIDESELECTION, TRUE, FALSE);
\r
7381 if (savesel.cpMax == exlen || forceVisible) {
\r
7382 /* Move insert point to new end of text if it was at the old
\r
7383 end of text or if the new text is an echo of user typein */
\r
7384 sel.cpMin = 9999999;
\r
7385 sel.cpMax = 9999999;
\r
7386 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
7388 /* Restore previous selection */
\r
7389 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&savesel);
\r
7391 SendMessage(hText, EM_HIDESELECTION, FALSE, FALSE);
\r
7398 DisplayHoldingsCount(HDC hdc, int x, int y, int rightAlign, int copyNumber)
\r
7402 COLORREF oldFg, oldBg;
\r
7407 snprintf(buf, sizeof(buf)/sizeof(buf[0]),"%d", copyNumber); else buf[0] = 0;
\r
7409 oldFg = SetTextColor(hdc, RGB(255, 255, 255)); /* white */
\r
7410 oldBg = SetBkColor(hdc, RGB(0, 0, 0)); /* black */
\r
7411 oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);
\r
7414 rect.right = x + squareSize;
\r
7416 rect.bottom = y + squareSize;
\r
7419 ExtTextOut(hdc, x + MESSAGE_LINE_LEFTMARGIN
\r
7420 + (rightAlign ? (squareSize*2)/3 : 0),
\r
7421 y, ETO_CLIPPED|ETO_OPAQUE,
\r
7422 &rect, str, strlen(str), NULL);
\r
7424 (void) SetTextColor(hdc, oldFg);
\r
7425 (void) SetBkColor(hdc, oldBg);
\r
7426 (void) SelectObject(hdc, oldFont);
\r
7430 DisplayAClock(HDC hdc, int timeRemaining, int highlight,
\r
7431 RECT *rect, char *color, char *flagFell)
\r
7435 COLORREF oldFg, oldBg;
\r
7438 if (appData.clockMode) {
\r
7440 snprintf(buf, sizeof(buf)/sizeof(buf[0]), "%c %s %s", color[0], TimeString(timeRemaining), flagFell);
\r
7442 snprintf(buf, sizeof(buf)/sizeof(buf[0]), "%s:%c%s %s", color, (logoHeight>0 ? 0 : ' '), TimeString(timeRemaining), flagFell);
\r
7449 oldFg = SetTextColor(hdc, RGB(255, 255, 255)); /* white */
\r
7450 oldBg = SetBkColor(hdc, RGB(0, 0, 0)); /* black */
\r
7452 oldFg = SetTextColor(hdc, RGB(0, 0, 0)); /* black */
\r
7453 oldBg = SetBkColor(hdc, RGB(255, 255, 255)); /* white */
\r
7455 oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);
\r
7459 ExtTextOut(hdc, rect->left + MESSAGE_LINE_LEFTMARGIN,
\r
7460 rect->top, ETO_CLIPPED|ETO_OPAQUE,
\r
7461 rect, str, strlen(str), NULL);
\r
7462 if(logoHeight > 0 && appData.clockMode) {
\r
7464 str += strlen(color)+2;
\r
7465 r.top = rect->top + logoHeight/2;
\r
7466 r.left = rect->left;
\r
7467 r.right = rect->right;
\r
7468 r.bottom = rect->bottom;
\r
7469 ExtTextOut(hdc, rect->left + MESSAGE_LINE_LEFTMARGIN,
\r
7470 r.top, ETO_CLIPPED|ETO_OPAQUE,
\r
7471 &r, str, strlen(str), NULL);
\r
7473 (void) SetTextColor(hdc, oldFg);
\r
7474 (void) SetBkColor(hdc, oldBg);
\r
7475 (void) SelectObject(hdc, oldFont);
\r
7480 DoReadFile(HANDLE hFile, char *buf, int count, DWORD *outCount,
\r
7486 if( count <= 0 ) {
\r
7487 if (appData.debugMode) {
\r
7488 fprintf( debugFP, "DoReadFile: trying to read past end of buffer, overflow = %d\n", count );
\r
7491 return ERROR_INVALID_USER_BUFFER;
\r
7494 ResetEvent(ovl->hEvent);
\r
7495 ovl->Offset = ovl->OffsetHigh = 0;
\r
7496 ok = ReadFile(hFile, buf, count, outCount, ovl);
\r
7500 err = GetLastError();
\r
7501 if (err == ERROR_IO_PENDING) {
\r
7502 ok = GetOverlappedResult(hFile, ovl, outCount, TRUE);
\r
7506 err = GetLastError();
\r
7513 DoWriteFile(HANDLE hFile, char *buf, int count, DWORD *outCount,
\r
7518 ResetEvent(ovl->hEvent);
\r
7519 ovl->Offset = ovl->OffsetHigh = 0;
\r
7520 ok = WriteFile(hFile, buf, count, outCount, ovl);
\r
7524 err = GetLastError();
\r
7525 if (err == ERROR_IO_PENDING) {
\r
7526 ok = GetOverlappedResult(hFile, ovl, outCount, TRUE);
\r
7530 err = GetLastError();
\r
7536 /* [AS] If input is line by line and a line exceed the buffer size, force an error */
\r
7537 void CheckForInputBufferFull( InputSource * is )
\r
7539 if( is->lineByLine && (is->next - is->buf) >= INPUT_SOURCE_BUF_SIZE ) {
\r
7540 /* Look for end of line */
\r
7541 char * p = is->buf;
\r
7543 while( p < is->next && *p != '\n' ) {
\r
7547 if( p >= is->next ) {
\r
7548 if (appData.debugMode) {
\r
7549 fprintf( debugFP, "Input line exceeded buffer size (source id=%lu)\n", is->id );
\r
7552 is->error = ERROR_BROKEN_PIPE; /* [AS] Just any non-successful code! */
\r
7553 is->count = (DWORD) -1;
\r
7554 is->next = is->buf;
\r
7560 InputThread(LPVOID arg)
\r
7565 is = (InputSource *) arg;
\r
7566 ovl.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
\r
7567 ovl.Internal = ovl.InternalHigh = ovl.Offset = ovl.OffsetHigh = 0;
\r
7568 while (is->hThread != NULL) {
\r
7569 is->error = DoReadFile(is->hFile, is->next,
\r
7570 INPUT_SOURCE_BUF_SIZE - (is->next - is->buf),
\r
7571 &is->count, &ovl);
\r
7572 if (is->error == NO_ERROR) {
\r
7573 is->next += is->count;
\r
7575 if (is->error == ERROR_BROKEN_PIPE) {
\r
7576 /* Correct for MS brain damage. EOF reading a pipe is not an error. */
\r
7579 is->count = (DWORD) -1;
\r
7580 /* [AS] The (is->count <= 0) check below is not useful for unsigned values! */
\r
7585 CheckForInputBufferFull( is );
\r
7587 SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);
\r
7589 if( is->count == ((DWORD) -1) ) break; /* [AS] */
\r
7591 if (is->count <= 0) break; /* Quit on EOF or error */
\r
7594 CloseHandle(ovl.hEvent);
\r
7595 CloseHandle(is->hFile);
\r
7597 if (appData.debugMode) {
\r
7598 fprintf( debugFP, "Input thread terminated (id=%lu, error=%d, count=%ld)\n", is->id, is->error, is->count );
\r
7605 /* Windows 95 beta 2 won't let you do overlapped i/o on a console or pipe */
\r
7607 NonOvlInputThread(LPVOID arg)
\r
7614 is = (InputSource *) arg;
\r
7615 while (is->hThread != NULL) {
\r
7616 is->error = ReadFile(is->hFile, is->next,
\r
7617 INPUT_SOURCE_BUF_SIZE - (is->next - is->buf),
\r
7618 &is->count, NULL) ? NO_ERROR : GetLastError();
\r
7619 if (is->error == NO_ERROR) {
\r
7620 /* Change CRLF to LF */
\r
7621 if (is->next > is->buf) {
\r
7623 i = is->count + 1;
\r
7631 if (prev == '\r' && *p == '\n') {
\r
7643 if (is->error == ERROR_BROKEN_PIPE) {
\r
7644 /* Correct for MS brain damage. EOF reading a pipe is not an error. */
\r
7647 is->count = (DWORD) -1;
\r
7651 CheckForInputBufferFull( is );
\r
7653 SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);
\r
7655 if( is->count == ((DWORD) -1) ) break; /* [AS] */
\r
7657 if (is->count < 0) break; /* Quit on error */
\r
7659 CloseHandle(is->hFile);
\r
7664 SocketInputThread(LPVOID arg)
\r
7668 is = (InputSource *) arg;
\r
7669 while (is->hThread != NULL) {
\r
7670 is->count = recv(is->sock, is->buf, INPUT_SOURCE_BUF_SIZE, 0);
\r
7671 if ((int)is->count == SOCKET_ERROR) {
\r
7672 is->count = (DWORD) -1;
\r
7673 is->error = WSAGetLastError();
\r
7675 is->error = NO_ERROR;
\r
7676 is->next += is->count;
\r
7677 if (is->count == 0 && is->second == is) {
\r
7678 /* End of file on stderr; quit with no message */
\r
7682 SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);
\r
7684 if( is->count == ((DWORD) -1) ) break; /* [AS] */
\r
7686 if (is->count <= 0) break; /* Quit on EOF or error */
\r
7692 InputEvent(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
\r
7696 is = (InputSource *) lParam;
\r
7697 if (is->lineByLine) {
\r
7698 /* Feed in lines one by one */
\r
7699 char *p = is->buf;
\r
7701 while (q < is->next) {
\r
7702 if (*q++ == '\n') {
\r
7703 (is->func)(is, is->closure, p, q - p, NO_ERROR);
\r
7708 /* Move any partial line to the start of the buffer */
\r
7710 while (p < is->next) {
\r
7715 if (is->error != NO_ERROR || is->count == 0) {
\r
7716 /* Notify backend of the error. Note: If there was a partial
\r
7717 line at the end, it is not flushed through. */
\r
7718 (is->func)(is, is->closure, is->buf, is->count, is->error);
\r
7721 /* Feed in the whole chunk of input at once */
\r
7722 (is->func)(is, is->closure, is->buf, is->count, is->error);
\r
7723 is->next = is->buf;
\r
7727 /*---------------------------------------------------------------------------*\
\r
7729 * Menu enables. Used when setting various modes.
\r
7731 \*---------------------------------------------------------------------------*/
\r
7739 GreyRevert(Boolean grey)
\r
7740 { // [HGM] vari: for retracting variations in local mode
\r
7741 HMENU hmenu = GetMenu(hwndMain);
\r
7742 EnableMenuItem(hmenu, IDM_Revert, MF_BYCOMMAND|(grey ? MF_GRAYED : MF_ENABLED));
\r
7743 EnableMenuItem(hmenu, IDM_Annotate, MF_BYCOMMAND|(grey ? MF_GRAYED : MF_ENABLED));
\r
7747 SetMenuEnables(HMENU hmenu, Enables *enab)
\r
7749 while (enab->item > 0) {
\r
7750 (void) EnableMenuItem(hmenu, enab->item, enab->flags);
\r
7755 Enables gnuEnables[] = {
\r
7756 { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },
\r
7757 { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },
\r
7758 { IDM_IcsClient, MF_BYCOMMAND|MF_GRAYED },
\r
7759 { IDM_Accept, MF_BYCOMMAND|MF_GRAYED },
\r
7760 { IDM_Decline, MF_BYCOMMAND|MF_GRAYED },
\r
7761 { IDM_Rematch, MF_BYCOMMAND|MF_GRAYED },
\r
7762 { IDM_Adjourn, MF_BYCOMMAND|MF_GRAYED },
\r
7763 { IDM_StopExamining, MF_BYCOMMAND|MF_GRAYED },
\r
7764 { IDM_StopObserving, MF_BYCOMMAND|MF_GRAYED },
\r
7765 { IDM_Upload, MF_BYCOMMAND|MF_GRAYED },
\r
7766 { IDM_Revert, MF_BYCOMMAND|MF_GRAYED },
\r
7767 { IDM_Annotate, MF_BYCOMMAND|MF_GRAYED },
\r
7768 { IDM_NewChat, MF_BYCOMMAND|MF_GRAYED },
\r
7772 Enables icsEnables[] = {
\r
7773 { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },
\r
7774 { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },
\r
7775 { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },
\r
7776 { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },
\r
7777 { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },
\r
7778 { IDM_Match, MF_BYCOMMAND|MF_GRAYED },
\r
7779 { IDM_MachineBoth, MF_BYCOMMAND|MF_GRAYED },
\r
7780 { IDM_AnalysisMode, MF_BYCOMMAND|MF_ENABLED },
\r
7781 { IDM_AnalyzeFile, MF_BYCOMMAND|MF_GRAYED },
\r
7782 { IDM_TimeControl, MF_BYCOMMAND|MF_GRAYED },
\r
7783 { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },
\r
7784 { IDM_Hint, MF_BYCOMMAND|MF_GRAYED },
\r
7785 { IDM_Book, MF_BYCOMMAND|MF_GRAYED },
\r
7786 { IDM_IcsOptions, MF_BYCOMMAND|MF_ENABLED },
\r
7787 { IDM_Engine1Options, MF_BYCOMMAND|MF_GRAYED },
\r
7788 { IDM_Engine2Options, MF_BYCOMMAND|MF_GRAYED },
\r
7789 { IDM_Annotate, MF_BYCOMMAND|MF_GRAYED },
\r
7794 Enables zippyEnables[] = {
\r
7795 { IDM_MoveNow, MF_BYCOMMAND|MF_ENABLED },
\r
7796 { IDM_Hint, MF_BYCOMMAND|MF_ENABLED },
\r
7797 { IDM_Book, MF_BYCOMMAND|MF_ENABLED },
\r
7798 { IDM_Engine1Options, MF_BYCOMMAND|MF_ENABLED },
\r
7803 Enables ncpEnables[] = {
\r
7804 { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },
\r
7805 { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },
\r
7806 { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },
\r
7807 { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },
\r
7808 { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },
\r
7809 { IDM_Match, MF_BYCOMMAND|MF_GRAYED },
\r
7810 { IDM_AnalysisMode, MF_BYCOMMAND|MF_GRAYED },
\r
7811 { IDM_AnalyzeFile, MF_BYCOMMAND|MF_GRAYED },
\r
7812 { IDM_IcsClient, MF_BYCOMMAND|MF_GRAYED },
\r
7813 { ACTION_POS, MF_BYPOSITION|MF_GRAYED },
\r
7814 { IDM_Revert, MF_BYCOMMAND|MF_GRAYED },
\r
7815 { IDM_Annotate, MF_BYCOMMAND|MF_GRAYED },
\r
7816 { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },
\r
7817 { IDM_RetractMove, MF_BYCOMMAND|MF_GRAYED },
\r
7818 { IDM_TimeControl, MF_BYCOMMAND|MF_GRAYED },
\r
7819 { IDM_Hint, MF_BYCOMMAND|MF_GRAYED },
\r
7820 { IDM_Book, MF_BYCOMMAND|MF_GRAYED },
\r
7821 { IDM_MachineBoth, MF_BYCOMMAND|MF_GRAYED },
\r
7822 { IDM_NewChat, MF_BYCOMMAND|MF_GRAYED },
\r
7823 { IDM_Engine1Options, MF_BYCOMMAND|MF_GRAYED },
\r
7824 { IDM_Engine2Options, MF_BYCOMMAND|MF_GRAYED },
\r
7828 Enables trainingOnEnables[] = {
\r
7829 { IDM_EditComment, MF_BYCOMMAND|MF_GRAYED },
\r
7830 { IDM_Comment, MF_BYCOMMAND|MF_GRAYED },
\r
7831 { IDM_Pause, MF_BYCOMMAND|MF_GRAYED },
\r
7832 { IDM_Forward, MF_BYCOMMAND|MF_GRAYED },
\r
7833 { IDM_Backward, MF_BYCOMMAND|MF_GRAYED },
\r
7834 { IDM_ToEnd, MF_BYCOMMAND|MF_GRAYED },
\r
7835 { IDM_ToStart, MF_BYCOMMAND|MF_GRAYED },
\r
7836 { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },
\r
7837 { IDM_TruncateGame, MF_BYCOMMAND|MF_GRAYED },
\r
7841 Enables trainingOffEnables[] = {
\r
7842 { IDM_EditComment, MF_BYCOMMAND|MF_ENABLED },
\r
7843 { IDM_Comment, MF_BYCOMMAND|MF_ENABLED },
\r
7844 { IDM_Pause, MF_BYCOMMAND|MF_ENABLED },
\r
7845 { IDM_Forward, MF_BYCOMMAND|MF_ENABLED },
\r
7846 { IDM_Backward, MF_BYCOMMAND|MF_ENABLED },
\r
7847 { IDM_ToEnd, MF_BYCOMMAND|MF_ENABLED },
\r
7848 { IDM_ToStart, MF_BYCOMMAND|MF_ENABLED },
\r
7849 { IDM_MoveNow, MF_BYCOMMAND|MF_ENABLED },
\r
7850 { IDM_TruncateGame, MF_BYCOMMAND|MF_ENABLED },
\r
7854 /* These modify either ncpEnables or gnuEnables */
\r
7855 Enables cmailEnables[] = {
\r
7856 { IDM_MailMove, MF_BYCOMMAND|MF_ENABLED },
\r
7857 { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_ENABLED },
\r
7858 { ACTION_POS, MF_BYPOSITION|MF_ENABLED },
\r
7859 { IDM_CallFlag, MF_BYCOMMAND|MF_GRAYED },
\r
7860 { IDM_Draw, MF_BYCOMMAND|MF_ENABLED },
\r
7861 { IDM_Adjourn, MF_BYCOMMAND|MF_GRAYED },
\r
7862 { IDM_Abort, MF_BYCOMMAND|MF_GRAYED },
\r
7866 Enables machineThinkingEnables[] = {
\r
7867 { IDM_LoadGame, MF_BYCOMMAND|MF_GRAYED },
\r
7868 { IDM_LoadNextGame, MF_BYCOMMAND|MF_GRAYED },
\r
7869 { IDM_LoadPrevGame, MF_BYCOMMAND|MF_GRAYED },
\r
7870 { IDM_ReloadGame, MF_BYCOMMAND|MF_GRAYED },
\r
7871 { IDM_PasteGame, MF_BYCOMMAND|MF_GRAYED },
\r
7872 { IDM_LoadPosition, MF_BYCOMMAND|MF_GRAYED },
\r
7873 { IDM_LoadNextPosition, MF_BYCOMMAND|MF_GRAYED },
\r
7874 { IDM_LoadPrevPosition, MF_BYCOMMAND|MF_GRAYED },
\r
7875 { IDM_ReloadPosition, MF_BYCOMMAND|MF_GRAYED },
\r
7876 { IDM_PastePosition, MF_BYCOMMAND|MF_GRAYED },
\r
7877 { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },
\r
7878 { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },
\r
7879 { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },
\r
7880 { IDM_Match, MF_BYCOMMAND|MF_GRAYED },
\r
7881 { IDM_TypeInMove, MF_BYCOMMAND|MF_GRAYED },
\r
7882 { IDM_RetractMove, MF_BYCOMMAND|MF_GRAYED },
\r
7886 Enables userThinkingEnables[] = {
\r
7887 { IDM_LoadGame, MF_BYCOMMAND|MF_ENABLED },
\r
7888 { IDM_LoadNextGame, MF_BYCOMMAND|MF_ENABLED },
\r
7889 { IDM_LoadPrevGame, MF_BYCOMMAND|MF_ENABLED },
\r
7890 { IDM_ReloadGame, MF_BYCOMMAND|MF_ENABLED },
\r
7891 { IDM_PasteGame, MF_BYCOMMAND|MF_ENABLED },
\r
7892 { IDM_LoadPosition, MF_BYCOMMAND|MF_ENABLED },
\r
7893 { IDM_LoadNextPosition, MF_BYCOMMAND|MF_ENABLED },
\r
7894 { IDM_LoadPrevPosition, MF_BYCOMMAND|MF_ENABLED },
\r
7895 { IDM_ReloadPosition, MF_BYCOMMAND|MF_ENABLED },
\r
7896 { IDM_PastePosition, MF_BYCOMMAND|MF_ENABLED },
\r
7897 { IDM_MachineWhite, MF_BYCOMMAND|MF_ENABLED },
\r
7898 { IDM_MachineBlack, MF_BYCOMMAND|MF_ENABLED },
\r
7899 { IDM_TwoMachines, MF_BYCOMMAND|MF_ENABLED },
\r
7900 { IDM_Match, MF_BYCOMMAND|MF_ENABLED },
\r
7901 { IDM_TypeInMove, MF_BYCOMMAND|MF_ENABLED },
\r
7902 { IDM_RetractMove, MF_BYCOMMAND|MF_ENABLED },
\r
7906 /*---------------------------------------------------------------------------*\
\r
7908 * Front-end interface functions exported by XBoard.
\r
7909 * Functions appear in same order as prototypes in frontend.h.
\r
7911 \*---------------------------------------------------------------------------*/
\r
7915 static UINT prevChecked = 0;
\r
7916 static int prevPausing = 0;
\r
7919 if (pausing != prevPausing) {
\r
7920 prevPausing = pausing;
\r
7921 (void) CheckMenuItem(GetMenu(hwndMain), IDM_Pause,
\r
7922 MF_BYCOMMAND|(pausing ? MF_CHECKED : MF_UNCHECKED));
\r
7923 if (hwndPause) SetWindowText(hwndPause, pausing ? "C" : "P");
\r
7926 switch (gameMode) {
\r
7927 case BeginningOfGame:
\r
7928 if (appData.icsActive)
\r
7929 nowChecked = IDM_IcsClient;
\r
7930 else if (appData.noChessProgram)
\r
7931 nowChecked = IDM_EditGame;
\r
7933 nowChecked = IDM_MachineBlack;
\r
7935 case MachinePlaysBlack:
\r
7936 nowChecked = IDM_MachineBlack;
\r
7938 case MachinePlaysWhite:
\r
7939 nowChecked = IDM_MachineWhite;
\r
7941 case TwoMachinesPlay:
\r
7942 nowChecked = matchMode ? IDM_Match : IDM_TwoMachines; // [HGM] match
\r
7945 nowChecked = IDM_AnalysisMode;
\r
7948 nowChecked = IDM_AnalyzeFile;
\r
7951 nowChecked = IDM_EditGame;
\r
7953 case PlayFromGameFile:
\r
7954 nowChecked = IDM_LoadGame;
\r
7956 case EditPosition:
\r
7957 nowChecked = IDM_EditPosition;
\r
7960 nowChecked = IDM_Training;
\r
7962 case IcsPlayingWhite:
\r
7963 case IcsPlayingBlack:
\r
7964 case IcsObserving:
\r
7966 nowChecked = IDM_IcsClient;
\r
7973 if (prevChecked != 0)
\r
7974 (void) CheckMenuItem(GetMenu(hwndMain),
\r
7975 prevChecked, MF_BYCOMMAND|MF_UNCHECKED);
\r
7976 if (nowChecked != 0)
\r
7977 (void) CheckMenuItem(GetMenu(hwndMain),
\r
7978 nowChecked, MF_BYCOMMAND|MF_CHECKED);
\r
7980 if (nowChecked == IDM_LoadGame || nowChecked == IDM_Training) {
\r
7981 (void) EnableMenuItem(GetMenu(hwndMain), IDM_Training,
\r
7982 MF_BYCOMMAND|MF_ENABLED);
\r
7984 (void) EnableMenuItem(GetMenu(hwndMain),
\r
7985 IDM_Training, MF_BYCOMMAND|MF_GRAYED);
\r
7988 prevChecked = nowChecked;
\r
7990 /* [DM] icsEngineAnalyze - Do a sceure check too */
\r
7991 if (appData.icsActive) {
\r
7992 if (appData.icsEngineAnalyze) {
\r
7993 (void) CheckMenuItem(GetMenu(hwndMain), IDM_AnalysisMode,
\r
7994 MF_BYCOMMAND|MF_CHECKED);
\r
7996 (void) CheckMenuItem(GetMenu(hwndMain), IDM_AnalysisMode,
\r
7997 MF_BYCOMMAND|MF_UNCHECKED);
\r
8005 HMENU hmenu = GetMenu(hwndMain);
\r
8006 SetMenuEnables(hmenu, icsEnables);
\r
8007 EnableMenuItem(GetSubMenu(hmenu, OPTIONS_POS), ICS_POS,
\r
8008 MF_BYPOSITION|MF_ENABLED);
\r
8010 if (appData.zippyPlay) {
\r
8011 SetMenuEnables(hmenu, zippyEnables);
\r
8012 if (!appData.noChessProgram) /* [DM] icsEngineAnalyze */
\r
8013 (void) EnableMenuItem(GetMenu(hwndMain), IDM_AnalysisMode,
\r
8014 MF_BYCOMMAND|MF_ENABLED);
\r
8022 SetMenuEnables(GetMenu(hwndMain), gnuEnables);
\r
8028 HMENU hmenu = GetMenu(hwndMain);
\r
8029 SetMenuEnables(hmenu, ncpEnables);
\r
8030 EnableMenuItem(GetSubMenu(hmenu, OPTIONS_POS), SOUNDS_POS,
\r
8031 MF_BYPOSITION|MF_GRAYED);
\r
8032 DrawMenuBar(hwndMain);
\r
8038 SetMenuEnables(GetMenu(hwndMain), cmailEnables);
\r
8042 SetTrainingModeOn()
\r
8045 SetMenuEnables(GetMenu(hwndMain), trainingOnEnables);
\r
8046 for (i = 0; i < N_BUTTONS; i++) {
\r
8047 if (buttonDesc[i].hwnd != NULL)
\r
8048 EnableWindow(buttonDesc[i].hwnd, FALSE);
\r
8053 VOID SetTrainingModeOff()
\r
8056 SetMenuEnables(GetMenu(hwndMain), trainingOffEnables);
\r
8057 for (i = 0; i < N_BUTTONS; i++) {
\r
8058 if (buttonDesc[i].hwnd != NULL)
\r
8059 EnableWindow(buttonDesc[i].hwnd, TRUE);
\r
8065 SetUserThinkingEnables()
\r
8067 SetMenuEnables(GetMenu(hwndMain), userThinkingEnables);
\r
8071 SetMachineThinkingEnables()
\r
8073 HMENU hMenu = GetMenu(hwndMain);
\r
8074 int flags = MF_BYCOMMAND|MF_ENABLED;
\r
8076 SetMenuEnables(hMenu, machineThinkingEnables);
\r
8078 if (gameMode == MachinePlaysBlack) {
\r
8079 (void)EnableMenuItem(hMenu, IDM_MachineBlack, flags);
\r
8080 } else if (gameMode == MachinePlaysWhite) {
\r
8081 (void)EnableMenuItem(hMenu, IDM_MachineWhite, flags);
\r
8082 } else if (gameMode == TwoMachinesPlay) {
\r
8083 (void)EnableMenuItem(hMenu, matchMode ? IDM_Match : IDM_TwoMachines, flags); // [HGM] match
\r
8089 DisplayTitle(char *str)
\r
8091 char title[MSG_SIZ], *host;
\r
8092 if (str[0] != NULLCHAR) {
\r
8093 safeStrCpy(title, str, sizeof(title)/sizeof(title[0]) );
\r
8094 } else if (appData.icsActive) {
\r
8095 if (appData.icsCommPort[0] != NULLCHAR)
\r
8098 host = appData.icsHost;
\r
8099 snprintf(title, MSG_SIZ, "%s: %s", szTitle, host);
\r
8100 } else if (appData.noChessProgram) {
\r
8101 safeStrCpy(title, szTitle, sizeof(title)/sizeof(title[0]) );
\r
8103 safeStrCpy(title, szTitle, sizeof(title)/sizeof(title[0]) );
\r
8104 strcat(title, ": ");
\r
8105 strcat(title, first.tidy);
\r
8107 SetWindowText(hwndMain, title);
\r
8112 DisplayMessage(char *str1, char *str2)
\r
8116 int remain = MESSAGE_TEXT_MAX - 1;
\r
8119 moveErrorMessageUp = FALSE; /* turned on later by caller if needed */
\r
8120 messageText[0] = NULLCHAR;
\r
8122 len = strlen(str1);
\r
8123 if (len > remain) len = remain;
\r
8124 strncpy(messageText, str1, len);
\r
8125 messageText[len] = NULLCHAR;
\r
8128 if (*str2 && remain >= 2) {
\r
8130 strcat(messageText, " ");
\r
8133 len = strlen(str2);
\r
8134 if (len > remain) len = remain;
\r
8135 strncat(messageText, str2, len);
\r
8137 messageText[MESSAGE_TEXT_MAX - 1] = NULLCHAR;
\r
8139 if (hwndMain == NULL || IsIconic(hwndMain)) return;
\r
8143 hdc = GetDC(hwndMain);
\r
8144 oldFont = SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);
\r
8145 ExtTextOut(hdc, messageRect.left, messageRect.top, ETO_CLIPPED|ETO_OPAQUE,
\r
8146 &messageRect, messageText, strlen(messageText), NULL);
\r
8147 (void) SelectObject(hdc, oldFont);
\r
8148 (void) ReleaseDC(hwndMain, hdc);
\r
8152 DisplayError(char *str, int error)
\r
8154 char buf[MSG_SIZ*2], buf2[MSG_SIZ];
\r
8158 safeStrCpy(buf, str, sizeof(buf)/sizeof(buf[0]) );
\r
8160 len = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,
\r
8161 NULL, error, LANG_NEUTRAL,
\r
8162 (LPSTR) buf2, MSG_SIZ, NULL);
\r
8164 snprintf(buf, 2*MSG_SIZ, "%s:\n%s", str, buf2);
\r
8166 ErrorMap *em = errmap;
\r
8167 while (em->err != 0 && em->err != error) em++;
\r
8168 if (em->err != 0) {
\r
8169 snprintf(buf, 2*MSG_SIZ, "%s:\n%s", str, em->msg);
\r
8171 snprintf(buf, 2*MSG_SIZ, "%s:\nError code %d", str, error);
\r
8176 ErrorPopUp(_("Error"), buf);
\r
8181 DisplayMoveError(char *str)
\r
8183 fromX = fromY = -1;
\r
8184 ClearHighlights();
\r
8185 DrawPosition(FALSE, NULL);
\r
8186 if (appData.popupMoveErrors) {
\r
8187 ErrorPopUp(_("Error"), str);
\r
8189 DisplayMessage(str, "");
\r
8190 moveErrorMessageUp = TRUE;
\r
8195 DisplayFatalError(char *str, int error, int exitStatus)
\r
8197 char buf[2*MSG_SIZ], buf2[MSG_SIZ];
\r
8199 char *label = exitStatus ? _("Fatal Error") : _("Exiting");
\r
8202 len = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,
\r
8203 NULL, error, LANG_NEUTRAL,
\r
8204 (LPSTR) buf2, MSG_SIZ, NULL);
\r
8206 snprintf(buf, 2*MSG_SIZ, "%s:\n%s", str, buf2);
\r
8208 ErrorMap *em = errmap;
\r
8209 while (em->err != 0 && em->err != error) em++;
\r
8210 if (em->err != 0) {
\r
8211 snprintf(buf, 2*MSG_SIZ, "%s:\n%s", str, em->msg);
\r
8213 snprintf(buf, 2*MSG_SIZ, "%s:\nError code %d", str, error);
\r
8218 if (appData.debugMode) {
\r
8219 fprintf(debugFP, "%s: %s\n", label, str);
\r
8221 if (appData.popupExitMessage) {
\r
8222 (void) MessageBox(hwndMain, str, label, MB_OK|
\r
8223 (exitStatus ? MB_ICONSTOP : MB_ICONINFORMATION));
\r
8225 ExitEvent(exitStatus);
\r
8230 DisplayInformation(char *str)
\r
8232 (void) MessageBox(hwndMain, str, _("Information"), MB_OK|MB_ICONINFORMATION);
\r
8237 DisplayNote(char *str)
\r
8239 ErrorPopUp(_("Note"), str);
\r
8244 char *title, *question, *replyPrefix;
\r
8249 QuestionDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
8251 static QuestionParams *qp;
\r
8252 char reply[MSG_SIZ];
\r
8255 switch (message) {
\r
8256 case WM_INITDIALOG:
\r
8257 qp = (QuestionParams *) lParam;
\r
8258 CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));
\r
8259 Translate(hDlg, DLG_Question);
\r
8260 SetWindowText(hDlg, qp->title);
\r
8261 SetDlgItemText(hDlg, OPT_QuestionText, qp->question);
\r
8262 SetFocus(GetDlgItem(hDlg, OPT_QuestionInput));
\r
8266 switch (LOWORD(wParam)) {
\r
8268 safeStrCpy(reply, qp->replyPrefix, sizeof(reply)/sizeof(reply[0]) );
\r
8269 if (*reply) strcat(reply, " ");
\r
8270 len = strlen(reply);
\r
8271 GetDlgItemText(hDlg, OPT_QuestionInput, reply + len, sizeof(reply) - len);
\r
8272 strcat(reply, "\n");
\r
8273 OutputToProcess(qp->pr, reply, strlen(reply), &err);
\r
8274 EndDialog(hDlg, TRUE);
\r
8275 if (err) DisplayFatalError(_("Error writing to chess program"), err, 1);
\r
8278 EndDialog(hDlg, FALSE);
\r
8289 AskQuestion(char* title, char *question, char *replyPrefix, ProcRef pr)
\r
8291 QuestionParams qp;
\r
8295 qp.question = question;
\r
8296 qp.replyPrefix = replyPrefix;
\r
8298 lpProc = MakeProcInstance((FARPROC)QuestionDialog, hInst);
\r
8299 DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_Question),
\r
8300 hwndMain, (DLGPROC)lpProc, (LPARAM)&qp);
\r
8301 FreeProcInstance(lpProc);
\r
8304 /* [AS] Pick FRC position */
\r
8305 LRESULT CALLBACK NewGameFRC_Proc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
8307 static int * lpIndexFRC;
\r
8313 case WM_INITDIALOG:
\r
8314 lpIndexFRC = (int *) lParam;
\r
8316 CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));
\r
8317 Translate(hDlg, DLG_NewGameFRC);
\r
8319 SendDlgItemMessage( hDlg, IDC_NFG_Edit, EM_SETLIMITTEXT, sizeof(buf)-1, 0 );
\r
8320 SetDlgItemInt( hDlg, IDC_NFG_Edit, *lpIndexFRC, TRUE );
\r
8321 SendDlgItemMessage( hDlg, IDC_NFG_Edit, EM_SETSEL, 0, -1 );
\r
8322 SetFocus(GetDlgItem(hDlg, IDC_NFG_Edit));
\r
8327 switch( LOWORD(wParam) ) {
\r
8329 *lpIndexFRC = GetDlgItemInt(hDlg, IDC_NFG_Edit, &index_is_ok, TRUE );
\r
8330 EndDialog( hDlg, 0 );
\r
8331 shuffleOpenings = TRUE; /* [HGM] shuffle: switch shuffling on for as long as we stay in current variant */
\r
8334 EndDialog( hDlg, 1 );
\r
8336 case IDC_NFG_Edit:
\r
8337 if( HIWORD(wParam) == EN_CHANGE ) {
\r
8338 GetDlgItemInt(hDlg, IDC_NFG_Edit, &index_is_ok, TRUE );
\r
8340 EnableWindow( GetDlgItem(hDlg, IDOK), index_is_ok );
\r
8343 case IDC_NFG_Random:
\r
8344 snprintf( buf, sizeof(buf)/sizeof(buf[0]), "%d", myrandom() ); /* [HGM] shuffle: no longer limit to 960 */
\r
8345 SetDlgItemText(hDlg, IDC_NFG_Edit, buf );
\r
8358 int index = appData.defaultFrcPosition;
\r
8359 FARPROC lpProc = MakeProcInstance( (FARPROC) NewGameFRC_Proc, hInst );
\r
8361 result = DialogBoxParam( hInst, MAKEINTRESOURCE(DLG_NewGameFRC), hwndMain, (DLGPROC)lpProc, (LPARAM)&index );
\r
8363 if( result == 0 ) {
\r
8364 appData.defaultFrcPosition = index;
\r
8370 /* [AS] Game list options. Refactored by HGM */
\r
8372 HWND gameListOptionsDialog;
\r
8374 // low-level front-end: clear text edit / list widget
\r
8378 SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_RESETCONTENT, 0, 0 );
\r
8381 // low-level front-end: clear text edit / list widget
\r
8383 GLT_DeSelectList()
\r
8385 SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_SETCURSEL, 0, 0 );
\r
8388 // low-level front-end: append line to text edit / list widget
\r
8390 GLT_AddToList( char *name )
\r
8393 SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_ADDSTRING, 0, (LPARAM) name );
\r
8397 // low-level front-end: get line from text edit / list widget
\r
8399 GLT_GetFromList( int index, char *name )
\r
8402 if( SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_GETTEXT, index, (LPARAM) name ) != LB_ERR )
\r
8408 void GLT_MoveSelection( HWND hDlg, int delta )
\r
8410 int idx1 = (int) SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETCURSEL, 0, 0 );
\r
8411 int idx2 = idx1 + delta;
\r
8412 int count = (int) SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETCOUNT, 0, 0 );
\r
8414 if( idx1 >=0 && idx1 < count && idx2 >= 0 && idx2 < count ) {
\r
8417 SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETTEXT, idx1, (LPARAM) buf );
\r
8418 SendDlgItemMessage( hDlg, IDC_GameListTags, LB_DELETESTRING, idx1, 0 );
\r
8419 SendDlgItemMessage( hDlg, IDC_GameListTags, LB_INSERTSTRING, idx2, (LPARAM) buf );
\r
8420 SendDlgItemMessage( hDlg, IDC_GameListTags, LB_SETCURSEL, idx2, 0 );
\r
8424 LRESULT CALLBACK GameListOptions_Proc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
8428 case WM_INITDIALOG:
\r
8429 gameListOptionsDialog = hDlg; // [HGM] pass through global to keep out off back-end
\r
8431 CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));
\r
8432 Translate(hDlg, DLG_GameListOptions);
\r
8434 /* Initialize list */
\r
8435 GLT_TagsToList( lpUserGLT );
\r
8437 SetFocus( GetDlgItem(hDlg, IDC_GameListTags) );
\r
8442 switch( LOWORD(wParam) ) {
\r
8445 EndDialog( hDlg, 0 );
\r
8448 EndDialog( hDlg, 1 );
\r
8451 case IDC_GLT_Default:
\r
8452 GLT_TagsToList( GLT_DEFAULT_TAGS );
\r
8455 case IDC_GLT_Restore:
\r
8456 GLT_TagsToList( appData.gameListTags );
\r
8460 GLT_MoveSelection( hDlg, -1 );
\r
8463 case IDC_GLT_Down:
\r
8464 GLT_MoveSelection( hDlg, +1 );
\r
8474 int GameListOptions()
\r
8477 FARPROC lpProc = MakeProcInstance( (FARPROC) GameListOptions_Proc, hInst );
\r
8479 safeStrCpy( lpUserGLT, appData.gameListTags ,LPUSERGLT_SIZE );
\r
8481 result = DialogBoxParam( hInst, MAKEINTRESOURCE(DLG_GameListOptions), hwndMain, (DLGPROC)lpProc, (LPARAM)lpUserGLT );
\r
8483 if( result == 0 ) {
\r
8484 /* [AS] Memory leak here! */
\r
8485 appData.gameListTags = strdup( lpUserGLT );
\r
8492 DisplayIcsInteractionTitle(char *str)
\r
8494 char consoleTitle[MSG_SIZ];
\r
8496 snprintf(consoleTitle, MSG_SIZ, "%s: %s", szConsoleTitle, str);
\r
8497 SetWindowText(hwndConsole, consoleTitle);
\r
8501 DrawPosition(int fullRedraw, Board board)
\r
8503 HDCDrawPosition(NULL, (BOOLEAN) fullRedraw, board);
\r
8506 void NotifyFrontendLogin()
\r
8509 UpdateICSWidth(GetDlgItem(hwndConsole, OPT_ConsoleText));
\r
8515 fromX = fromY = -1;
\r
8516 if (dragInfo.pos.x != -1 || dragInfo.pos.y != -1) {
\r
8517 dragInfo.pos.x = dragInfo.pos.y = -1;
\r
8518 dragInfo.pos.x = dragInfo.pos.y = -1;
\r
8519 dragInfo.lastpos = dragInfo.pos;
\r
8520 dragInfo.start.x = dragInfo.start.y = -1;
\r
8521 dragInfo.from = dragInfo.start;
\r
8523 DrawPosition(TRUE, NULL);
\r
8530 CommentPopUp(char *title, char *str)
\r
8532 HWND hwnd = GetActiveWindow();
\r
8533 EitherCommentPopUp(currentMove, title, str, FALSE); // [HGM] vari: fake move index, rather than 0
\r
8535 SetActiveWindow(hwnd);
\r
8539 CommentPopDown(void)
\r
8541 CheckMenuItem(GetMenu(hwndMain), IDM_Comment, MF_UNCHECKED);
\r
8542 if (commentDialog) {
\r
8543 ShowWindow(commentDialog, SW_HIDE);
\r
8545 commentUp = FALSE;
\r
8549 EditCommentPopUp(int index, char *title, char *str)
\r
8551 EitherCommentPopUp(index, title, str, TRUE);
\r
8558 MyPlaySound(&sounds[(int)SoundMove]);
\r
8561 VOID PlayIcsWinSound()
\r
8563 MyPlaySound(&sounds[(int)SoundIcsWin]);
\r
8566 VOID PlayIcsLossSound()
\r
8568 MyPlaySound(&sounds[(int)SoundIcsLoss]);
\r
8571 VOID PlayIcsDrawSound()
\r
8573 MyPlaySound(&sounds[(int)SoundIcsDraw]);
\r
8576 VOID PlayIcsUnfinishedSound()
\r
8578 MyPlaySound(&sounds[(int)SoundIcsUnfinished]);
\r
8584 MyPlaySound(&sounds[(int)SoundAlarm]);
\r
8592 consoleEcho = TRUE;
\r
8593 hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
8594 SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&consoleCF);
\r
8595 SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);
\r
8604 consoleEcho = FALSE;
\r
8605 hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
8606 /* This works OK: set text and background both to the same color */
\r
8608 cf.crTextColor = COLOR_ECHOOFF;
\r
8609 SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf);
\r
8610 SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, cf.crTextColor);
\r
8613 /* No Raw()...? */
\r
8615 void Colorize(ColorClass cc, int continuation)
\r
8617 currentColorClass = cc;
\r
8618 consoleCF.dwMask = CFM_COLOR|CFM_BOLD|CFM_ITALIC|CFM_UNDERLINE|CFM_STRIKEOUT;
\r
8619 consoleCF.crTextColor = textAttribs[cc].color;
\r
8620 consoleCF.dwEffects = textAttribs[cc].effects;
\r
8621 if (!continuation) MyPlaySound(&textAttribs[cc].sound);
\r
8627 static char buf[MSG_SIZ];
\r
8628 DWORD bufsiz = MSG_SIZ;
\r
8630 if(appData.userName != NULL && appData.userName[0] != 0) {
\r
8631 return appData.userName; /* [HGM] username: prefer name selected by user over his system login */
\r
8633 if (!GetUserName(buf, &bufsiz)) {
\r
8634 /*DisplayError("Error getting user name", GetLastError());*/
\r
8635 safeStrCpy(buf, _("User"), sizeof(buf)/sizeof(buf[0]) );
\r
8643 static char buf[MSG_SIZ];
\r
8644 DWORD bufsiz = MSG_SIZ;
\r
8646 if (!GetComputerName(buf, &bufsiz)) {
\r
8647 /*DisplayError("Error getting host name", GetLastError());*/
\r
8648 safeStrCpy(buf, _("Unknown"), sizeof(buf)/sizeof(buf[0]) );
\r
8655 ClockTimerRunning()
\r
8657 return clockTimerEvent != 0;
\r
8663 if (clockTimerEvent == 0) return FALSE;
\r
8664 KillTimer(hwndMain, clockTimerEvent);
\r
8665 clockTimerEvent = 0;
\r
8670 StartClockTimer(long millisec)
\r
8672 clockTimerEvent = SetTimer(hwndMain, (UINT) CLOCK_TIMER_ID,
\r
8673 (UINT) millisec, NULL);
\r
8677 DisplayWhiteClock(long timeRemaining, int highlight)
\r
8680 char *flag = whiteFlag && gameMode == TwoMachinesPlay ? "(!)" : "";
\r
8682 if(appData.noGUI) return;
\r
8683 hdc = GetDC(hwndMain);
\r
8684 if (!IsIconic(hwndMain)) {
\r
8685 DisplayAClock(hdc, timeRemaining, highlight,
\r
8686 flipClock ? &blackRect : &whiteRect, _("White"), flag);
\r
8688 if (highlight && iconCurrent == iconBlack) {
\r
8689 iconCurrent = iconWhite;
\r
8690 PostMessage(hwndMain, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);
\r
8691 if (IsIconic(hwndMain)) {
\r
8692 DrawIcon(hdc, 2, 2, iconCurrent);
\r
8695 (void) ReleaseDC(hwndMain, hdc);
\r
8697 PostMessage(hwndConsole, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);
\r
8701 DisplayBlackClock(long timeRemaining, int highlight)
\r
8704 char *flag = blackFlag && gameMode == TwoMachinesPlay ? "(!)" : "";
\r
8706 if(appData.noGUI) return;
\r
8707 hdc = GetDC(hwndMain);
\r
8708 if (!IsIconic(hwndMain)) {
\r
8709 DisplayAClock(hdc, timeRemaining, highlight,
\r
8710 flipClock ? &whiteRect : &blackRect, _("Black"), flag);
\r
8712 if (highlight && iconCurrent == iconWhite) {
\r
8713 iconCurrent = iconBlack;
\r
8714 PostMessage(hwndMain, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);
\r
8715 if (IsIconic(hwndMain)) {
\r
8716 DrawIcon(hdc, 2, 2, iconCurrent);
\r
8719 (void) ReleaseDC(hwndMain, hdc);
\r
8721 PostMessage(hwndConsole, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);
\r
8726 LoadGameTimerRunning()
\r
8728 return loadGameTimerEvent != 0;
\r
8732 StopLoadGameTimer()
\r
8734 if (loadGameTimerEvent == 0) return FALSE;
\r
8735 KillTimer(hwndMain, loadGameTimerEvent);
\r
8736 loadGameTimerEvent = 0;
\r
8741 StartLoadGameTimer(long millisec)
\r
8743 loadGameTimerEvent = SetTimer(hwndMain, (UINT) LOAD_GAME_TIMER_ID,
\r
8744 (UINT) millisec, NULL);
\r
8752 char fileTitle[MSG_SIZ];
\r
8754 defName = DefaultFileName(appData.oldSaveStyle ? "gam" : "pgn");
\r
8755 f = OpenFileDialog(hwndMain, "a", defName,
\r
8756 appData.oldSaveStyle ? "gam" : "pgn",
\r
8758 _("Save Game to File"), NULL, fileTitle, NULL);
\r
8760 SaveGame(f, 0, "");
\r
8767 ScheduleDelayedEvent(DelayedEventCallback cb, long millisec)
\r
8769 if (delayedTimerEvent != 0) {
\r
8770 if (appData.debugMode && cb != delayedTimerCallback) { // [HGM] alive: not too much debug
\r
8771 fprintf(debugFP, "ScheduleDelayedEvent: event already scheduled\n");
\r
8773 KillTimer(hwndMain, delayedTimerEvent);
\r
8774 delayedTimerEvent = 0;
\r
8775 if(delayedTimerCallback != cb) // [HGM] alive: do not "flush" same event, just postpone it
\r
8776 delayedTimerCallback();
\r
8778 delayedTimerCallback = cb;
\r
8779 delayedTimerEvent = SetTimer(hwndMain, (UINT) DELAYED_TIMER_ID,
\r
8780 (UINT) millisec, NULL);
\r
8783 DelayedEventCallback
\r
8786 if (delayedTimerEvent) {
\r
8787 return delayedTimerCallback;
\r
8794 CancelDelayedEvent()
\r
8796 if (delayedTimerEvent) {
\r
8797 KillTimer(hwndMain, delayedTimerEvent);
\r
8798 delayedTimerEvent = 0;
\r
8802 DWORD GetWin32Priority(int nice)
\r
8803 { // [HGM] nice: translate Unix nice() value to indows priority class. (Code stolen from Polyglot 1.4w11)
\r
8805 REALTIME_PRIORITY_CLASS 0x00000100
\r
8806 HIGH_PRIORITY_CLASS 0x00000080
\r
8807 ABOVE_NORMAL_PRIORITY_CLASS 0x00008000
\r
8808 NORMAL_PRIORITY_CLASS 0x00000020
\r
8809 BELOW_NORMAL_PRIORITY_CLASS 0x00004000
\r
8810 IDLE_PRIORITY_CLASS 0x00000040
\r
8812 if (nice < -15) return 0x00000080;
\r
8813 if (nice < 0) return 0x00008000;
\r
8814 if (nice == 0) return 0x00000020;
\r
8815 if (nice < 15) return 0x00004000;
\r
8816 return 0x00000040;
\r
8819 /* Start a child process running the given program.
\r
8820 The process's standard output can be read from "from", and its
\r
8821 standard input can be written to "to".
\r
8822 Exit with fatal error if anything goes wrong.
\r
8823 Returns an opaque pointer that can be used to destroy the process
\r
8827 StartChildProcess(char *cmdLine, char *dir, ProcRef *pr)
\r
8829 #define BUFSIZE 4096
\r
8831 HANDLE hChildStdinRd, hChildStdinWr,
\r
8832 hChildStdoutRd, hChildStdoutWr;
\r
8833 HANDLE hChildStdinWrDup, hChildStdoutRdDup;
\r
8834 SECURITY_ATTRIBUTES saAttr;
\r
8836 PROCESS_INFORMATION piProcInfo;
\r
8837 STARTUPINFO siStartInfo;
\r
8839 char buf[MSG_SIZ];
\r
8842 if (appData.debugMode) {
\r
8843 fprintf(debugFP, "StartChildProcess (dir=\"%s\") %s\n", dir, cmdLine);
\r
8848 /* Set the bInheritHandle flag so pipe handles are inherited. */
\r
8849 saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);
\r
8850 saAttr.bInheritHandle = TRUE;
\r
8851 saAttr.lpSecurityDescriptor = NULL;
\r
8854 * The steps for redirecting child's STDOUT:
\r
8855 * 1. Create anonymous pipe to be STDOUT for child.
\r
8856 * 2. Create a noninheritable duplicate of read handle,
\r
8857 * and close the inheritable read handle.
\r
8860 /* Create a pipe for the child's STDOUT. */
\r
8861 if (! CreatePipe(&hChildStdoutRd, &hChildStdoutWr, &saAttr, 0)) {
\r
8862 return GetLastError();
\r
8865 /* Duplicate the read handle to the pipe, so it is not inherited. */
\r
8866 fSuccess = DuplicateHandle(GetCurrentProcess(), hChildStdoutRd,
\r
8867 GetCurrentProcess(), &hChildStdoutRdDup, 0,
\r
8868 FALSE, /* not inherited */
\r
8869 DUPLICATE_SAME_ACCESS);
\r
8871 return GetLastError();
\r
8873 CloseHandle(hChildStdoutRd);
\r
8876 * The steps for redirecting child's STDIN:
\r
8877 * 1. Create anonymous pipe to be STDIN for child.
\r
8878 * 2. Create a noninheritable duplicate of write handle,
\r
8879 * and close the inheritable write handle.
\r
8882 /* Create a pipe for the child's STDIN. */
\r
8883 if (! CreatePipe(&hChildStdinRd, &hChildStdinWr, &saAttr, 0)) {
\r
8884 return GetLastError();
\r
8887 /* Duplicate the write handle to the pipe, so it is not inherited. */
\r
8888 fSuccess = DuplicateHandle(GetCurrentProcess(), hChildStdinWr,
\r
8889 GetCurrentProcess(), &hChildStdinWrDup, 0,
\r
8890 FALSE, /* not inherited */
\r
8891 DUPLICATE_SAME_ACCESS);
\r
8893 return GetLastError();
\r
8895 CloseHandle(hChildStdinWr);
\r
8897 /* Arrange to (1) look in dir for the child .exe file, and
\r
8898 * (2) have dir be the child's working directory. Interpret
\r
8899 * dir relative to the directory WinBoard loaded from. */
\r
8900 GetCurrentDirectory(MSG_SIZ, buf);
\r
8901 SetCurrentDirectory(installDir);
\r
8902 SetCurrentDirectory(dir);
\r
8904 /* Now create the child process. */
\r
8906 siStartInfo.cb = sizeof(STARTUPINFO);
\r
8907 siStartInfo.lpReserved = NULL;
\r
8908 siStartInfo.lpDesktop = NULL;
\r
8909 siStartInfo.lpTitle = NULL;
\r
8910 siStartInfo.dwFlags = STARTF_USESTDHANDLES;
\r
8911 siStartInfo.cbReserved2 = 0;
\r
8912 siStartInfo.lpReserved2 = NULL;
\r
8913 siStartInfo.hStdInput = hChildStdinRd;
\r
8914 siStartInfo.hStdOutput = hChildStdoutWr;
\r
8915 siStartInfo.hStdError = hChildStdoutWr;
\r
8917 fSuccess = CreateProcess(NULL,
\r
8918 cmdLine, /* command line */
\r
8919 NULL, /* process security attributes */
\r
8920 NULL, /* primary thread security attrs */
\r
8921 TRUE, /* handles are inherited */
\r
8922 DETACHED_PROCESS|CREATE_NEW_PROCESS_GROUP,
\r
8923 NULL, /* use parent's environment */
\r
8925 &siStartInfo, /* STARTUPINFO pointer */
\r
8926 &piProcInfo); /* receives PROCESS_INFORMATION */
\r
8928 err = GetLastError();
\r
8929 SetCurrentDirectory(buf); /* return to prev directory */
\r
8934 if (appData.niceEngines){ // [HGM] nice: adjust engine proc priority
\r
8935 if(appData.debugMode) fprintf(debugFP, "nice engine proc to %d\n", appData.niceEngines);
\r
8936 SetPriorityClass(piProcInfo.hProcess, GetWin32Priority(appData.niceEngines));
\r
8939 /* Close the handles we don't need in the parent */
\r
8940 CloseHandle(piProcInfo.hThread);
\r
8941 CloseHandle(hChildStdinRd);
\r
8942 CloseHandle(hChildStdoutWr);
\r
8944 /* Prepare return value */
\r
8945 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
\r
8946 cp->kind = CPReal;
\r
8947 cp->hProcess = piProcInfo.hProcess;
\r
8948 cp->pid = piProcInfo.dwProcessId;
\r
8949 cp->hFrom = hChildStdoutRdDup;
\r
8950 cp->hTo = hChildStdinWrDup;
\r
8952 *pr = (void *) cp;
\r
8954 /* Klaus Friedel says that this Sleep solves a problem under Windows
\r
8955 2000 where engines sometimes don't see the initial command(s)
\r
8956 from WinBoard and hang. I don't understand how that can happen,
\r
8957 but the Sleep is harmless, so I've put it in. Others have also
\r
8958 reported what may be the same problem, so hopefully this will fix
\r
8959 it for them too. */
\r
8967 DestroyChildProcess(ProcRef pr, int/*boolean*/ signal)
\r
8969 ChildProc *cp; int result;
\r
8971 cp = (ChildProc *) pr;
\r
8972 if (cp == NULL) return;
\r
8974 switch (cp->kind) {
\r
8976 /* TerminateProcess is considered harmful, so... */
\r
8977 CloseHandle(cp->hTo); /* Closing this will give the child an EOF and hopefully kill it */
\r
8978 if (cp->hFrom) CloseHandle(cp->hFrom); /* if NULL, InputThread will close it */
\r
8979 /* The following doesn't work because the chess program
\r
8980 doesn't "have the same console" as WinBoard. Maybe
\r
8981 we could arrange for this even though neither WinBoard
\r
8982 nor the chess program uses a console for stdio? */
\r
8983 /*!!if (signal) GenerateConsoleCtrlEvent(CTRL_BREAK_EVENT, cp->pid);*/
\r
8985 /* [AS] Special termination modes for misbehaving programs... */
\r
8986 if( signal == 9 ) {
\r
8987 result = TerminateProcess( cp->hProcess, 0 );
\r
8989 if ( appData.debugMode) {
\r
8990 fprintf( debugFP, "Terminating process %lu, result=%d\n", cp->pid, result );
\r
8993 else if( signal == 10 ) {
\r
8994 DWORD dw = WaitForSingleObject( cp->hProcess, 3*1000 ); // Wait 3 seconds at most
\r
8996 if( dw != WAIT_OBJECT_0 ) {
\r
8997 result = TerminateProcess( cp->hProcess, 0 );
\r
8999 if ( appData.debugMode) {
\r
9000 fprintf( debugFP, "Process %lu still alive after timeout, killing... result=%d\n", cp->pid, result );
\r
9006 CloseHandle(cp->hProcess);
\r
9010 if (cp->hFrom) CloseHandle(cp->hFrom);
\r
9014 closesocket(cp->sock);
\r
9019 if (signal) send(cp->sock2, "\017", 1, 0); /* 017 = 15 = SIGTERM */
\r
9020 closesocket(cp->sock);
\r
9021 closesocket(cp->sock2);
\r
9029 InterruptChildProcess(ProcRef pr)
\r
9033 cp = (ChildProc *) pr;
\r
9034 if (cp == NULL) return;
\r
9035 switch (cp->kind) {
\r
9037 /* The following doesn't work because the chess program
\r
9038 doesn't "have the same console" as WinBoard. Maybe
\r
9039 we could arrange for this even though neither WinBoard
\r
9040 nor the chess program uses a console for stdio */
\r
9041 /*!!GenerateConsoleCtrlEvent(CTRL_C_EVENT, cp->pid);*/
\r
9046 /* Can't interrupt */
\r
9050 send(cp->sock2, "\002", 1, 0); /* 2 = SIGINT */
\r
9057 OpenTelnet(char *host, char *port, ProcRef *pr)
\r
9059 char cmdLine[MSG_SIZ];
\r
9061 if (port[0] == NULLCHAR) {
\r
9062 snprintf(cmdLine, MSG_SIZ, "%s %s", appData.telnetProgram, host);
\r
9064 snprintf(cmdLine, MSG_SIZ, "%s %s %s", appData.telnetProgram, host, port);
\r
9066 return StartChildProcess(cmdLine, "", pr);
\r
9070 /* Code to open TCP sockets */
\r
9073 OpenTCP(char *host, char *port, ProcRef *pr)
\r
9078 struct sockaddr_in sa, mysa;
\r
9079 struct hostent FAR *hp;
\r
9080 unsigned short uport;
\r
9081 WORD wVersionRequested;
\r
9084 /* Initialize socket DLL */
\r
9085 wVersionRequested = MAKEWORD(1, 1);
\r
9086 err = WSAStartup(wVersionRequested, &wsaData);
\r
9087 if (err != 0) return err;
\r
9090 if ((s = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {
\r
9091 err = WSAGetLastError();
\r
9096 /* Bind local address using (mostly) don't-care values.
\r
9098 memset((char *) &mysa, 0, sizeof(struct sockaddr_in));
\r
9099 mysa.sin_family = AF_INET;
\r
9100 mysa.sin_addr.s_addr = INADDR_ANY;
\r
9101 uport = (unsigned short) 0;
\r
9102 mysa.sin_port = htons(uport);
\r
9103 if (bind(s, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))
\r
9104 == SOCKET_ERROR) {
\r
9105 err = WSAGetLastError();
\r
9110 /* Resolve remote host name */
\r
9111 memset((char *) &sa, 0, sizeof(struct sockaddr_in));
\r
9112 if (!(hp = gethostbyname(host))) {
\r
9113 unsigned int b0, b1, b2, b3;
\r
9115 err = WSAGetLastError();
\r
9117 if (sscanf(host, "%u.%u.%u.%u", &b0, &b1, &b2, &b3) == 4) {
\r
9118 hp = (struct hostent *) calloc(1, sizeof(struct hostent));
\r
9119 hp->h_addrtype = AF_INET;
\r
9121 hp->h_addr_list = (char **) calloc(2, sizeof(char *));
\r
9122 hp->h_addr_list[0] = (char *) malloc(4);
\r
9123 hp->h_addr_list[0][0] = (char) b0;
\r
9124 hp->h_addr_list[0][1] = (char) b1;
\r
9125 hp->h_addr_list[0][2] = (char) b2;
\r
9126 hp->h_addr_list[0][3] = (char) b3;
\r
9132 sa.sin_family = hp->h_addrtype;
\r
9133 uport = (unsigned short) atoi(port);
\r
9134 sa.sin_port = htons(uport);
\r
9135 memcpy((char *) &sa.sin_addr, hp->h_addr, hp->h_length);
\r
9137 /* Make connection */
\r
9138 if (connect(s, (struct sockaddr *) &sa,
\r
9139 sizeof(struct sockaddr_in)) == SOCKET_ERROR) {
\r
9140 err = WSAGetLastError();
\r
9145 /* Prepare return value */
\r
9146 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
\r
9147 cp->kind = CPSock;
\r
9149 *pr = (ProcRef *) cp;
\r
9155 OpenCommPort(char *name, ProcRef *pr)
\r
9160 char fullname[MSG_SIZ];
\r
9162 if (*name != '\\')
\r
9163 snprintf(fullname, MSG_SIZ, "\\\\.\\%s", name);
\r
9165 safeStrCpy(fullname, name, sizeof(fullname)/sizeof(fullname[0]) );
\r
9167 h = CreateFile(name, GENERIC_READ | GENERIC_WRITE,
\r
9168 0, NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL);
\r
9169 if (h == (HANDLE) -1) {
\r
9170 return GetLastError();
\r
9174 if (!SetCommState(h, (LPDCB) &dcb)) return GetLastError();
\r
9176 /* Accumulate characters until a 100ms pause, then parse */
\r
9177 ct.ReadIntervalTimeout = 100;
\r
9178 ct.ReadTotalTimeoutMultiplier = 0;
\r
9179 ct.ReadTotalTimeoutConstant = 0;
\r
9180 ct.WriteTotalTimeoutMultiplier = 0;
\r
9181 ct.WriteTotalTimeoutConstant = 0;
\r
9182 if (!SetCommTimeouts(h, (LPCOMMTIMEOUTS) &ct)) return GetLastError();
\r
9184 /* Prepare return value */
\r
9185 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
\r
9186 cp->kind = CPComm;
\r
9189 *pr = (ProcRef *) cp;
\r
9195 OpenLoopback(ProcRef *pr)
\r
9197 DisplayFatalError(_("Not implemented"), 0, 1);
\r
9203 OpenRcmd(char* host, char* user, char* cmd, ProcRef* pr)
\r
9208 struct sockaddr_in sa, mysa;
\r
9209 struct hostent FAR *hp;
\r
9210 unsigned short uport;
\r
9211 WORD wVersionRequested;
\r
9214 char stderrPortStr[MSG_SIZ];
\r
9216 /* Initialize socket DLL */
\r
9217 wVersionRequested = MAKEWORD(1, 1);
\r
9218 err = WSAStartup(wVersionRequested, &wsaData);
\r
9219 if (err != 0) return err;
\r
9221 /* Resolve remote host name */
\r
9222 memset((char *) &sa, 0, sizeof(struct sockaddr_in));
\r
9223 if (!(hp = gethostbyname(host))) {
\r
9224 unsigned int b0, b1, b2, b3;
\r
9226 err = WSAGetLastError();
\r
9228 if (sscanf(host, "%u.%u.%u.%u", &b0, &b1, &b2, &b3) == 4) {
\r
9229 hp = (struct hostent *) calloc(1, sizeof(struct hostent));
\r
9230 hp->h_addrtype = AF_INET;
\r
9232 hp->h_addr_list = (char **) calloc(2, sizeof(char *));
\r
9233 hp->h_addr_list[0] = (char *) malloc(4);
\r
9234 hp->h_addr_list[0][0] = (char) b0;
\r
9235 hp->h_addr_list[0][1] = (char) b1;
\r
9236 hp->h_addr_list[0][2] = (char) b2;
\r
9237 hp->h_addr_list[0][3] = (char) b3;
\r
9243 sa.sin_family = hp->h_addrtype;
\r
9244 uport = (unsigned short) 514;
\r
9245 sa.sin_port = htons(uport);
\r
9246 memcpy((char *) &sa.sin_addr, hp->h_addr, hp->h_length);
\r
9248 /* Bind local socket to unused "privileged" port address
\r
9250 s = INVALID_SOCKET;
\r
9251 memset((char *) &mysa, 0, sizeof(struct sockaddr_in));
\r
9252 mysa.sin_family = AF_INET;
\r
9253 mysa.sin_addr.s_addr = INADDR_ANY;
\r
9254 for (fromPort = 1023;; fromPort--) {
\r
9255 if (fromPort < 0) {
\r
9257 return WSAEADDRINUSE;
\r
9259 if (s == INVALID_SOCKET) {
\r
9260 if ((s = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {
\r
9261 err = WSAGetLastError();
\r
9266 uport = (unsigned short) fromPort;
\r
9267 mysa.sin_port = htons(uport);
\r
9268 if (bind(s, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))
\r
9269 == SOCKET_ERROR) {
\r
9270 err = WSAGetLastError();
\r
9271 if (err == WSAEADDRINUSE) continue;
\r
9275 if (connect(s, (struct sockaddr *) &sa,
\r
9276 sizeof(struct sockaddr_in)) == SOCKET_ERROR) {
\r
9277 err = WSAGetLastError();
\r
9278 if (err == WSAEADDRINUSE) {
\r
9289 /* Bind stderr local socket to unused "privileged" port address
\r
9291 s2 = INVALID_SOCKET;
\r
9292 memset((char *) &mysa, 0, sizeof(struct sockaddr_in));
\r
9293 mysa.sin_family = AF_INET;
\r
9294 mysa.sin_addr.s_addr = INADDR_ANY;
\r
9295 for (fromPort = 1023;; fromPort--) {
\r
9296 if (fromPort == prevStderrPort) continue; // don't reuse port
\r
9297 if (fromPort < 0) {
\r
9298 (void) closesocket(s);
\r
9300 return WSAEADDRINUSE;
\r
9302 if (s2 == INVALID_SOCKET) {
\r
9303 if ((s2 = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {
\r
9304 err = WSAGetLastError();
\r
9310 uport = (unsigned short) fromPort;
\r
9311 mysa.sin_port = htons(uport);
\r
9312 if (bind(s2, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))
\r
9313 == SOCKET_ERROR) {
\r
9314 err = WSAGetLastError();
\r
9315 if (err == WSAEADDRINUSE) continue;
\r
9316 (void) closesocket(s);
\r
9320 if (listen(s2, 1) == SOCKET_ERROR) {
\r
9321 err = WSAGetLastError();
\r
9322 if (err == WSAEADDRINUSE) {
\r
9324 s2 = INVALID_SOCKET;
\r
9327 (void) closesocket(s);
\r
9328 (void) closesocket(s2);
\r
9334 prevStderrPort = fromPort; // remember port used
\r
9335 snprintf(stderrPortStr, MSG_SIZ, "%d", fromPort);
\r
9337 if (send(s, stderrPortStr, strlen(stderrPortStr) + 1, 0) == SOCKET_ERROR) {
\r
9338 err = WSAGetLastError();
\r
9339 (void) closesocket(s);
\r
9340 (void) closesocket(s2);
\r
9345 if (send(s, UserName(), strlen(UserName()) + 1, 0) == SOCKET_ERROR) {
\r
9346 err = WSAGetLastError();
\r
9347 (void) closesocket(s);
\r
9348 (void) closesocket(s2);
\r
9352 if (*user == NULLCHAR) user = UserName();
\r
9353 if (send(s, user, strlen(user) + 1, 0) == SOCKET_ERROR) {
\r
9354 err = WSAGetLastError();
\r
9355 (void) closesocket(s);
\r
9356 (void) closesocket(s2);
\r
9360 if (send(s, cmd, strlen(cmd) + 1, 0) == SOCKET_ERROR) {
\r
9361 err = WSAGetLastError();
\r
9362 (void) closesocket(s);
\r
9363 (void) closesocket(s2);
\r
9368 if ((s3 = accept(s2, NULL, NULL)) == INVALID_SOCKET) {
\r
9369 err = WSAGetLastError();
\r
9370 (void) closesocket(s);
\r
9371 (void) closesocket(s2);
\r
9375 (void) closesocket(s2); /* Stop listening */
\r
9377 /* Prepare return value */
\r
9378 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
\r
9379 cp->kind = CPRcmd;
\r
9382 *pr = (ProcRef *) cp;
\r
9389 AddInputSource(ProcRef pr, int lineByLine,
\r
9390 InputCallback func, VOIDSTAR closure)
\r
9392 InputSource *is, *is2 = NULL;
\r
9393 ChildProc *cp = (ChildProc *) pr;
\r
9395 is = (InputSource *) calloc(1, sizeof(InputSource));
\r
9396 is->lineByLine = lineByLine;
\r
9398 is->closure = closure;
\r
9399 is->second = NULL;
\r
9400 is->next = is->buf;
\r
9401 if (pr == NoProc) {
\r
9402 is->kind = CPReal;
\r
9403 consoleInputSource = is;
\r
9405 is->kind = cp->kind;
\r
9407 [AS] Try to avoid a race condition if the thread is given control too early:
\r
9408 we create all threads suspended so that the is->hThread variable can be
\r
9409 safely assigned, then let the threads start with ResumeThread.
\r
9411 switch (cp->kind) {
\r
9413 is->hFile = cp->hFrom;
\r
9414 cp->hFrom = NULL; /* now owned by InputThread */
\r
9416 CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) NonOvlInputThread,
\r
9417 (LPVOID) is, CREATE_SUSPENDED, &is->id);
\r
9421 is->hFile = cp->hFrom;
\r
9422 cp->hFrom = NULL; /* now owned by InputThread */
\r
9424 CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) InputThread,
\r
9425 (LPVOID) is, CREATE_SUSPENDED, &is->id);
\r
9429 is->sock = cp->sock;
\r
9431 CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,
\r
9432 (LPVOID) is, CREATE_SUSPENDED, &is->id);
\r
9436 is2 = (InputSource *) calloc(1, sizeof(InputSource));
\r
9438 is->sock = cp->sock;
\r
9440 is2->sock = cp->sock2;
\r
9441 is2->second = is2;
\r
9443 CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,
\r
9444 (LPVOID) is, CREATE_SUSPENDED, &is->id);
\r
9446 CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,
\r
9447 (LPVOID) is2, CREATE_SUSPENDED, &is2->id);
\r
9451 if( is->hThread != NULL ) {
\r
9452 ResumeThread( is->hThread );
\r
9455 if( is2 != NULL && is2->hThread != NULL ) {
\r
9456 ResumeThread( is2->hThread );
\r
9460 return (InputSourceRef) is;
\r
9464 RemoveInputSource(InputSourceRef isr)
\r
9468 is = (InputSource *) isr;
\r
9469 is->hThread = NULL; /* tell thread to stop */
\r
9470 CloseHandle(is->hThread);
\r
9471 if (is->second != NULL) {
\r
9472 is->second->hThread = NULL;
\r
9473 CloseHandle(is->second->hThread);
\r
9477 int no_wrap(char *message, int count)
\r
9479 ConsoleOutput(message, count, FALSE);
\r
9484 OutputToProcess(ProcRef pr, char *message, int count, int *outError)
\r
9487 int outCount = SOCKET_ERROR;
\r
9488 ChildProc *cp = (ChildProc *) pr;
\r
9489 static OVERLAPPED ovl;
\r
9490 static int line = 0;
\r
9494 if (appData.noJoin || !appData.useInternalWrap)
\r
9495 return no_wrap(message, count);
\r
9498 int width = get_term_width();
\r
9499 int len = wrap(NULL, message, count, width, &line);
\r
9500 char *msg = malloc(len);
\r
9504 return no_wrap(message, count);
\r
9507 dbgchk = wrap(msg, message, count, width, &line);
\r
9508 if (dbgchk != len && appData.debugMode)
\r
9509 fprintf(debugFP, "wrap(): dbgchk(%d) != len(%d)\n", dbgchk, len);
\r
9510 ConsoleOutput(msg, len, FALSE);
\r
9517 if (ovl.hEvent == NULL) {
\r
9518 ovl.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
\r
9520 ovl.Internal = ovl.InternalHigh = ovl.Offset = ovl.OffsetHigh = 0;
\r
9522 switch (cp->kind) {
\r
9525 outCount = send(cp->sock, message, count, 0);
\r
9526 if (outCount == SOCKET_ERROR) {
\r
9527 *outError = WSAGetLastError();
\r
9529 *outError = NO_ERROR;
\r
9534 if (WriteFile(((ChildProc *)pr)->hTo, message, count,
\r
9535 &dOutCount, NULL)) {
\r
9536 *outError = NO_ERROR;
\r
9537 outCount = (int) dOutCount;
\r
9539 *outError = GetLastError();
\r
9544 *outError = DoWriteFile(((ChildProc *)pr)->hTo, message, count,
\r
9545 &dOutCount, &ovl);
\r
9546 if (*outError == NO_ERROR) {
\r
9547 outCount = (int) dOutCount;
\r
9555 OutputToProcessDelayed(ProcRef pr, char *message, int count, int *outError,
\r
9558 /* Ignore delay, not implemented for WinBoard */
\r
9559 return OutputToProcess(pr, message, count, outError);
\r
9564 CmailSigHandlerCallBack(InputSourceRef isr, VOIDSTAR closure,
\r
9565 char *buf, int count, int error)
\r
9567 DisplayFatalError(_("Not implemented"), 0, 1);
\r
9570 /* see wgamelist.c for Game List functions */
\r
9571 /* see wedittags.c for Edit Tags functions */
\r
9578 char buf[MSG_SIZ];
\r
9581 if (SearchPath(installDir, appData.icsLogon, NULL, MSG_SIZ, buf, &dummy)) {
\r
9582 f = fopen(buf, "r");
\r
9584 ProcessICSInitScript(f);
\r
9592 StartAnalysisClock()
\r
9594 if (analysisTimerEvent) return;
\r
9595 analysisTimerEvent = SetTimer(hwndMain, (UINT) ANALYSIS_TIMER_ID,
\r
9596 (UINT) 2000, NULL);
\r
9600 SetHighlights(int fromX, int fromY, int toX, int toY)
\r
9602 highlightInfo.sq[0].x = fromX;
\r
9603 highlightInfo.sq[0].y = fromY;
\r
9604 highlightInfo.sq[1].x = toX;
\r
9605 highlightInfo.sq[1].y = toY;
\r
9611 highlightInfo.sq[0].x = highlightInfo.sq[0].y =
\r
9612 highlightInfo.sq[1].x = highlightInfo.sq[1].y = -1;
\r
9616 SetPremoveHighlights(int fromX, int fromY, int toX, int toY)
\r
9618 premoveHighlightInfo.sq[0].x = fromX;
\r
9619 premoveHighlightInfo.sq[0].y = fromY;
\r
9620 premoveHighlightInfo.sq[1].x = toX;
\r
9621 premoveHighlightInfo.sq[1].y = toY;
\r
9625 ClearPremoveHighlights()
\r
9627 premoveHighlightInfo.sq[0].x = premoveHighlightInfo.sq[0].y =
\r
9628 premoveHighlightInfo.sq[1].x = premoveHighlightInfo.sq[1].y = -1;
\r
9632 ShutDownFrontEnd()
\r
9634 if (saveSettingsOnExit) SaveSettings(settingsFileName);
\r
9635 DeleteClipboardTempFiles();
\r
9641 if (IsIconic(hwndMain))
\r
9642 ShowWindow(hwndMain, SW_RESTORE);
\r
9644 SetActiveWindow(hwndMain);
\r
9648 * Prototypes for animation support routines
\r
9650 static void ScreenSquare(int column, int row, POINT * pt);
\r
9651 static void Tween( POINT * start, POINT * mid, POINT * finish, int factor,
\r
9652 POINT frames[], int * nFrames);
\r
9658 AnimateAtomicCapture(Board board, int fromX, int fromY, int toX, int toY)
\r
9659 { // [HGM] atomic: animate blast wave
\r
9662 explodeInfo.fromX = fromX;
\r
9663 explodeInfo.fromY = fromY;
\r
9664 explodeInfo.toX = toX;
\r
9665 explodeInfo.toY = toY;
\r
9666 for(i=1; i<4*kFactor; i++) {
\r
9667 explodeInfo.radius = (i*180)/(4*kFactor-1);
\r
9668 DrawPosition(FALSE, board);
\r
9669 Sleep(appData.animSpeed);
\r
9671 explodeInfo.radius = 0;
\r
9672 DrawPosition(TRUE, board);
\r
9676 AnimateMove(board, fromX, fromY, toX, toY)
\r
9683 ChessSquare piece;
\r
9684 POINT start, finish, mid;
\r
9685 POINT frames[kFactor * 2 + 1];
\r
9688 if (!appData.animate) return;
\r
9689 if (doingSizing) return;
\r
9690 if (fromY < 0 || fromX < 0) return;
\r
9691 piece = board[fromY][fromX];
\r
9692 if (piece >= EmptySquare) return;
\r
9694 ScreenSquare(fromX, fromY, &start);
\r
9695 ScreenSquare(toX, toY, &finish);
\r
9697 /* All moves except knight jumps move in straight line */
\r
9698 if (!(abs(fromX-toX) == 1 && abs(fromY-toY) == 2 || abs(fromX-toX) == 2 && abs(fromY-toY) == 1)) {
\r
9699 mid.x = start.x + (finish.x - start.x) / 2;
\r
9700 mid.y = start.y + (finish.y - start.y) / 2;
\r
9702 /* Knight: make straight movement then diagonal */
\r
9703 if (abs(toY - fromY) < abs(toX - fromX)) {
\r
9704 mid.x = start.x + (finish.x - start.x) / 2;
\r
9708 mid.y = start.y + (finish.y - start.y) / 2;
\r
9712 /* Don't use as many frames for very short moves */
\r
9713 if (abs(toY - fromY) + abs(toX - fromX) <= 2)
\r
9714 Tween(&start, &mid, &finish, kFactor - 1, frames, &nFrames);
\r
9716 Tween(&start, &mid, &finish, kFactor, frames, &nFrames);
\r
9718 animInfo.from.x = fromX;
\r
9719 animInfo.from.y = fromY;
\r
9720 animInfo.to.x = toX;
\r
9721 animInfo.to.y = toY;
\r
9722 animInfo.lastpos = start;
\r
9723 animInfo.piece = piece;
\r
9724 for (n = 0; n < nFrames; n++) {
\r
9725 animInfo.pos = frames[n];
\r
9726 DrawPosition(FALSE, NULL);
\r
9727 animInfo.lastpos = animInfo.pos;
\r
9728 Sleep(appData.animSpeed);
\r
9730 animInfo.pos = finish;
\r
9731 DrawPosition(FALSE, NULL);
\r
9732 animInfo.piece = EmptySquare;
\r
9733 Explode(board, fromX, fromY, toX, toY);
\r
9736 /* Convert board position to corner of screen rect and color */
\r
9739 ScreenSquare(column, row, pt)
\r
9740 int column; int row; POINT * pt;
\r
9743 pt->x = lineGap + ((BOARD_WIDTH-1)-column) * (squareSize + lineGap);
\r
9744 pt->y = lineGap + row * (squareSize + lineGap);
\r
9746 pt->x = lineGap + column * (squareSize + lineGap);
\r
9747 pt->y = lineGap + ((BOARD_HEIGHT-1)-row) * (squareSize + lineGap);
\r
9751 /* Generate a series of frame coords from start->mid->finish.
\r
9752 The movement rate doubles until the half way point is
\r
9753 reached, then halves back down to the final destination,
\r
9754 which gives a nice slow in/out effect. The algorithmn
\r
9755 may seem to generate too many intermediates for short
\r
9756 moves, but remember that the purpose is to attract the
\r
9757 viewers attention to the piece about to be moved and
\r
9758 then to where it ends up. Too few frames would be less
\r
9762 Tween(start, mid, finish, factor, frames, nFrames)
\r
9763 POINT * start; POINT * mid;
\r
9764 POINT * finish; int factor;
\r
9765 POINT frames[]; int * nFrames;
\r
9767 int n, fraction = 1, count = 0;
\r
9769 /* Slow in, stepping 1/16th, then 1/8th, ... */
\r
9770 for (n = 0; n < factor; n++)
\r
9772 for (n = 0; n < factor; n++) {
\r
9773 frames[count].x = start->x + (mid->x - start->x) / fraction;
\r
9774 frames[count].y = start->y + (mid->y - start->y) / fraction;
\r
9776 fraction = fraction / 2;
\r
9780 frames[count] = *mid;
\r
9783 /* Slow out, stepping 1/2, then 1/4, ... */
\r
9785 for (n = 0; n < factor; n++) {
\r
9786 frames[count].x = finish->x - (finish->x - mid->x) / fraction;
\r
9787 frames[count].y = finish->y - (finish->y - mid->y) / fraction;
\r
9789 fraction = fraction * 2;
\r
9795 HistorySet( char movelist[][2*MOVE_LEN], int first, int last, int current )
\r
9797 MoveHistorySet( movelist, first, last, current, pvInfoList );
\r
9799 EvalGraphSet( first, last, current, pvInfoList );
\r
9803 SettingsPopUp(ChessProgramState *cps)
\r
9804 { // [HGM] wrapper needed because handles must not be passed through back-end
\r
9805 EngineOptionsPopup(savedHwnd, cps);
\r