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 int 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;
\r
2982 arrow[1].x = s_x + A_WIDTH;
\r
2983 arrow[1].y = d_y - h;
\r
2985 arrow[2].x = s_x + A_WIDTH*A_WIDTH_FACTOR;
\r
2986 arrow[2].y = d_y - h;
\r
2991 arrow[4].x = s_x - A_WIDTH*A_WIDTH_FACTOR;
\r
2992 arrow[4].y = d_y - h;
\r
2994 arrow[5].x = s_x - A_WIDTH;
\r
2995 arrow[5].y = d_y - h;
\r
2997 arrow[6].x = s_x - A_WIDTH;
\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;
\r
3006 arrow[1].x = d_x - w;
\r
3007 arrow[1].y = s_y + A_WIDTH;
\r
3009 arrow[2].x = d_x - w;
\r
3010 arrow[2].y = s_y + A_WIDTH*A_WIDTH_FACTOR;
\r
3015 arrow[4].x = d_x - w;
\r
3016 arrow[4].y = s_y - A_WIDTH*A_WIDTH_FACTOR;
\r
3018 arrow[5].x = d_x - w;
\r
3019 arrow[5].y = s_y - A_WIDTH;
\r
3022 arrow[6].y = s_y - A_WIDTH;
\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(x + j);
\r
3040 arrow[1].y = Round(y - 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 arrow[2].x = Round(x + j);
\r
3052 arrow[2].y = Round(y - j*dx);
\r
3054 arrow[3].x = Round(x + j*A_WIDTH_FACTOR);
\r
3055 arrow[3].y = Round(y - j*A_WIDTH_FACTOR*dx);
\r
3060 arrow[5].x = Round(x - j*A_WIDTH_FACTOR);
\r
3061 arrow[5].y = Round(y + j*A_WIDTH_FACTOR*dx);
\r
3063 arrow[6].x = Round(x - j);
\r
3064 arrow[6].y = Round(y + j*dx);
\r
3067 Polygon( hdc, arrow, 7 );
\r
3070 /* [AS] Draw an arrow between two squares */
\r
3071 VOID DrawArrowBetweenSquares( HDC hdc, int s_col, int s_row, int d_col, int d_row )
\r
3073 int s_x, s_y, d_x, d_y;
\r
3080 if( s_col == d_col && s_row == d_row ) {
\r
3084 /* Get source and destination points */
\r
3085 SquareToPos( s_row, s_col, &s_x, &s_y);
\r
3086 SquareToPos( d_row, d_col, &d_x, &d_y);
\r
3089 d_y += squareSize / 4;
\r
3091 else if( d_y < s_y ) {
\r
3092 d_y += 3 * squareSize / 4;
\r
3095 d_y += squareSize / 2;
\r
3099 d_x += squareSize / 4;
\r
3101 else if( d_x < s_x ) {
\r
3102 d_x += 3 * squareSize / 4;
\r
3105 d_x += squareSize / 2;
\r
3108 s_x += squareSize / 2;
\r
3109 s_y += squareSize / 2;
\r
3111 /* Adjust width */
\r
3112 A_WIDTH = squareSize / 14;
\r
3115 stLB.lbStyle = BS_SOLID;
\r
3116 stLB.lbColor = appData.highlightArrowColor;
\r
3119 hpen = CreatePen( PS_SOLID, 2, RGB(0x00,0x00,0x00) );
\r
3120 holdpen = SelectObject( hdc, hpen );
\r
3121 hbrush = CreateBrushIndirect( &stLB );
\r
3122 holdbrush = SelectObject( hdc, hbrush );
\r
3124 DrawArrowBetweenPoints( hdc, s_x, s_y, d_x, d_y );
\r
3126 SelectObject( hdc, holdpen );
\r
3127 SelectObject( hdc, holdbrush );
\r
3128 DeleteObject( hpen );
\r
3129 DeleteObject( hbrush );
\r
3132 BOOL HasHighlightInfo()
\r
3134 BOOL result = FALSE;
\r
3136 if( highlightInfo.sq[0].x >= 0 && highlightInfo.sq[0].y >= 0 &&
\r
3137 highlightInfo.sq[1].x >= 0 && highlightInfo.sq[1].y >= 0 )
\r
3145 BOOL IsDrawArrowEnabled()
\r
3147 BOOL result = FALSE;
\r
3149 if( appData.highlightMoveWithArrow && squareSize >= 32 ) {
\r
3156 VOID DrawArrowHighlight( HDC hdc )
\r
3158 if( IsDrawArrowEnabled() && HasHighlightInfo() ) {
\r
3159 DrawArrowBetweenSquares( hdc,
\r
3160 highlightInfo.sq[0].x, highlightInfo.sq[0].y,
\r
3161 highlightInfo.sq[1].x, highlightInfo.sq[1].y );
\r
3165 HRGN GetArrowHighlightClipRegion( HDC hdc )
\r
3167 HRGN result = NULL;
\r
3169 if( HasHighlightInfo() ) {
\r
3170 int x1, y1, x2, y2;
\r
3171 int sx, sy, dx, dy;
\r
3173 SquareToPos(highlightInfo.sq[0].y, highlightInfo.sq[0].x, &x1, &y1 );
\r
3174 SquareToPos(highlightInfo.sq[1].y, highlightInfo.sq[1].x, &x2, &y2 );
\r
3176 sx = MIN( x1, x2 );
\r
3177 sy = MIN( y1, y2 );
\r
3178 dx = MAX( x1, x2 ) + squareSize;
\r
3179 dy = MAX( y1, y2 ) + squareSize;
\r
3181 result = CreateRectRgn( sx, sy, dx, dy );
\r
3188 Warning: this function modifies the behavior of several other functions.
\r
3190 Basically, Winboard is optimized to avoid drawing the whole board if not strictly
\r
3191 needed. Unfortunately, the decision whether or not to perform a full or partial
\r
3192 repaint is scattered all over the place, which is not good for features such as
\r
3193 "arrow highlighting" that require a full repaint of the board.
\r
3195 So, I've tried to patch the code where I thought it made sense (e.g. after or during
\r
3196 user interaction, when speed is not so important) but especially to avoid errors
\r
3197 in the displayed graphics.
\r
3199 In such patched places, I always try refer to this function so there is a single
\r
3200 place to maintain knowledge.
\r
3202 To restore the original behavior, just return FALSE unconditionally.
\r
3204 BOOL IsFullRepaintPreferrable()
\r
3206 BOOL result = FALSE;
\r
3208 if( (appData.highlightLastMove || appData.highlightDragging) && IsDrawArrowEnabled() ) {
\r
3209 /* Arrow may appear on the board */
\r
3217 This function is called by DrawPosition to know whether a full repaint must
\r
3220 Only DrawPosition may directly call this function, which makes use of
\r
3221 some state information. Other function should call DrawPosition specifying
\r
3222 the repaint flag, and can use IsFullRepaintPreferrable if needed.
\r
3224 BOOL DrawPositionNeedsFullRepaint()
\r
3226 BOOL result = FALSE;
\r
3229 Probably a slightly better policy would be to trigger a full repaint
\r
3230 when animInfo.piece changes state (i.e. empty -> non-empty and viceversa),
\r
3231 but animation is fast enough that it's difficult to notice.
\r
3233 if( animInfo.piece == EmptySquare ) {
\r
3234 if( (appData.highlightLastMove || appData.highlightDragging) && IsDrawArrowEnabled() /*&& HasHighlightInfo()*/ ) {
\r
3243 DrawBoardOnDC(HDC hdc, Board board, HDC tmphdc)
\r
3245 int row, column, x, y, square_color, piece_color;
\r
3246 ChessSquare piece;
\r
3248 HDC texture_hdc = NULL;
\r
3250 /* [AS] Initialize background textures if needed */
\r
3251 if( liteBackTexture != NULL || darkBackTexture != NULL ) {
\r
3252 static int backTextureBoardSize; /* [HGM] boardsize: also new texture if board format changed */
\r
3253 if( backTextureSquareSize != squareSize
\r
3254 || backTextureBoardSize != BOARD_WIDTH+BOARD_FILES*BOARD_HEIGHT) {
\r
3255 backTextureBoardSize = BOARD_WIDTH+BOARD_FILES*BOARD_HEIGHT;
\r
3256 backTextureSquareSize = squareSize;
\r
3257 RebuildTextureSquareInfo();
\r
3260 texture_hdc = CreateCompatibleDC( hdc );
\r
3263 for (row = 0; row < BOARD_HEIGHT; row++) {
\r
3264 for (column = 0; column < BOARD_WIDTH; column++) {
\r
3266 SquareToPos(row, column, &x, &y);
\r
3268 piece = board[row][column];
\r
3270 square_color = ((column + row) % 2) == 1;
\r
3271 if( gameInfo.variant == VariantXiangqi ) {
\r
3272 square_color = !InPalace(row, column);
\r
3273 if(BOARD_HEIGHT&1) { if(row==BOARD_HEIGHT/2) square_color ^= 1; }
\r
3274 else if(row < BOARD_HEIGHT/2) square_color ^= 1;
\r
3276 piece_color = (int) piece < (int) BlackPawn;
\r
3279 /* [HGM] holdings file: light square or black */
\r
3280 if(column == BOARD_LEFT-2) {
\r
3281 if( row > BOARD_HEIGHT - gameInfo.holdingsSize - 1 )
\r
3284 DisplayHoldingsCount(hdc, x, y, 0, 0); /* black out */
\r
3288 if(column == BOARD_RGHT + 1 ) {
\r
3289 if( row < gameInfo.holdingsSize )
\r
3292 DisplayHoldingsCount(hdc, x, y, 0, 0);
\r
3296 if(column == BOARD_LEFT-1 ) /* left align */
\r
3297 DisplayHoldingsCount(hdc, x, y, flipView, (int) board[row][column]);
\r
3298 else if( column == BOARD_RGHT) /* right align */
\r
3299 DisplayHoldingsCount(hdc, x, y, !flipView, (int) board[row][column]);
\r
3301 if (appData.monoMode) {
\r
3302 if (piece == EmptySquare) {
\r
3303 BitBlt(hdc, x, y, squareSize, squareSize, 0, 0, 0,
\r
3304 square_color ? WHITENESS : BLACKNESS);
\r
3306 DrawPieceOnDC(hdc, piece, piece_color, square_color, x, y, tmphdc);
\r
3309 else if( backTextureSquareInfo[row][column].mode > 0 ) {
\r
3310 /* [AS] Draw the square using a texture bitmap */
\r
3311 HBITMAP hbm = SelectObject( texture_hdc, square_color ? liteBackTexture : darkBackTexture );
\r
3312 int r = row, c = column; // [HGM] do not flip board in flipView
\r
3313 if(flipView) { r = BOARD_HEIGHT-1 - r; c = BOARD_WIDTH-1 - c; }
\r
3316 squareSize, squareSize,
\r
3319 backTextureSquareInfo[r][c].mode,
\r
3320 backTextureSquareInfo[r][c].x,
\r
3321 backTextureSquareInfo[r][c].y );
\r
3323 SelectObject( texture_hdc, hbm );
\r
3325 if (piece != EmptySquare) {
\r
3326 DrawPieceOnDC(hdc, piece, piece_color, -1, x, y, tmphdc);
\r
3330 HBRUSH brush = square_color ? lightSquareBrush : darkSquareBrush;
\r
3332 oldBrush = SelectObject(hdc, brush );
\r
3333 BitBlt(hdc, x, y, squareSize, squareSize, 0, 0, 0, PATCOPY);
\r
3334 SelectObject(hdc, oldBrush);
\r
3335 if (piece != EmptySquare)
\r
3336 DrawPieceOnDC(hdc, piece, piece_color, -1, x, y, tmphdc);
\r
3341 if( texture_hdc != NULL ) {
\r
3342 DeleteDC( texture_hdc );
\r
3346 int saveDiagFlag = 0; FILE *diagFile; // [HGM] diag
\r
3347 void fputDW(FILE *f, int x)
\r
3349 fputc(x & 255, f);
\r
3350 fputc(x>>8 & 255, f);
\r
3351 fputc(x>>16 & 255, f);
\r
3352 fputc(x>>24 & 255, f);
\r
3355 #define MAX_CLIPS 200 /* more than enough */
\r
3358 DrawLogoOnDC(HDC hdc, RECT logoRect, HBITMAP logo)
\r
3360 // HBITMAP bufferBitmap;
\r
3365 int w = 100, h = 50;
\r
3367 if(logo == NULL) return;
\r
3368 // GetClientRect(hwndMain, &Rect);
\r
3369 // bufferBitmap = CreateCompatibleBitmap(hdc, Rect.right-Rect.left+1,
\r
3370 // Rect.bottom-Rect.top+1);
\r
3371 tmphdc = CreateCompatibleDC(hdc);
\r
3372 hbm = SelectObject(tmphdc, logo);
\r
3373 if( GetObject( logo, sizeof(bi), &bi ) > 0 ) {
\r
3377 StretchBlt(hdc, logoRect.left, logoRect.top, logoRect.right - logoRect.left,
\r
3378 logoRect.bottom - logoRect.top, tmphdc, 0, 0, w, h, SRCCOPY);
\r
3379 SelectObject(tmphdc, hbm);
\r
3383 static HDC hdcSeek;
\r
3385 // [HGM] seekgraph
\r
3386 void DrawSeekAxis( int x, int y, int xTo, int yTo )
\r
3389 HPEN hp = SelectObject( hdcSeek, gridPen );
\r
3390 MoveToEx( hdcSeek, boardRect.left+x, boardRect.top+y, &stPt );
\r
3391 LineTo( hdcSeek, boardRect.left+xTo, boardRect.top+yTo );
\r
3392 SelectObject( hdcSeek, hp );
\r
3395 // front-end wrapper for drawing functions to do rectangles
\r
3396 void DrawSeekBackground( int left, int top, int right, int bottom )
\r
3401 if (hdcSeek == NULL) {
\r
3402 hdcSeek = GetDC(hwndMain);
\r
3403 if (!appData.monoMode) {
\r
3404 SelectPalette(hdcSeek, hPal, FALSE);
\r
3405 RealizePalette(hdcSeek);
\r
3408 hp = SelectObject( hdcSeek, gridPen );
\r
3409 rc.top = boardRect.top+top; rc.left = boardRect.left+left;
\r
3410 rc.bottom = boardRect.top+bottom; rc.right = boardRect.left+right;
\r
3411 FillRect( hdcSeek, &rc, lightSquareBrush );
\r
3412 SelectObject( hdcSeek, hp );
\r
3415 // front-end wrapper for putting text in graph
\r
3416 void DrawSeekText(char *buf, int x, int y)
\r
3419 SetBkMode( hdcSeek, TRANSPARENT );
\r
3420 GetTextExtentPoint32( hdcSeek, buf, strlen(buf), &stSize );
\r
3421 TextOut( hdcSeek, boardRect.left+x-3, boardRect.top+y-stSize.cy/2, buf, strlen(buf) );
\r
3424 void DrawSeekDot(int x, int y, int color)
\r
3426 int square = color & 0x80;
\r
3427 HBRUSH oldBrush = SelectObject(hdcSeek,
\r
3428 color == 0 ? markerBrush : color == 1 ? darkSquareBrush : explodeBrush);
\r
3431 Rectangle(hdcSeek, boardRect.left+x - squareSize/9, boardRect.top+y - squareSize/9,
\r
3432 boardRect.left+x + squareSize/9, boardRect.top+y + squareSize/9);
\r
3434 Ellipse(hdcSeek, boardRect.left+x - squareSize/8, boardRect.top+y - squareSize/8,
\r
3435 boardRect.left+x + squareSize/8, boardRect.top+y + squareSize/8);
\r
3436 SelectObject(hdcSeek, oldBrush);
\r
3440 HDCDrawPosition(HDC hdc, BOOLEAN repaint, Board board)
\r
3442 static Board lastReq[2], lastDrawn[2];
\r
3443 static HighlightInfo lastDrawnHighlight, lastDrawnPremove;
\r
3444 static int lastDrawnFlipView = 0;
\r
3445 static int lastReqValid[2] = {0, 0}, lastDrawnValid[2] = {0, 0};
\r
3446 int releaseDC, x, y, x2, y2, row, column, num_clips = 0, i;
\r
3449 HBITMAP bufferBitmap;
\r
3450 HBITMAP oldBitmap;
\r
3452 HRGN clips[MAX_CLIPS];
\r
3453 ChessSquare dragged_piece = EmptySquare;
\r
3454 int nr = twoBoards*partnerUp;
\r
3456 /* I'm undecided on this - this function figures out whether a full
\r
3457 * repaint is necessary on its own, so there's no real reason to have the
\r
3458 * caller tell it that. I think this can safely be set to FALSE - but
\r
3459 * if we trust the callers not to request full repaints unnessesarily, then
\r
3460 * we could skip some clipping work. In other words, only request a full
\r
3461 * redraw when the majority of pieces have changed positions (ie. flip,
\r
3462 * gamestart and similar) --Hawk
\r
3464 Boolean fullrepaint = repaint;
\r
3466 if(DrawSeekGraph()) return; // [HG} seekgraph: suppress printing board if seek graph up
\r
3468 if( DrawPositionNeedsFullRepaint() ) {
\r
3469 fullrepaint = TRUE;
\r
3472 if (board == NULL) {
\r
3473 if (!lastReqValid[nr]) {
\r
3476 board = lastReq[nr];
\r
3478 CopyBoard(lastReq[nr], board);
\r
3479 lastReqValid[nr] = 1;
\r
3482 if (doingSizing) {
\r
3486 if (IsIconic(hwndMain)) {
\r
3490 if (hdc == NULL) {
\r
3491 hdc = GetDC(hwndMain);
\r
3492 if (!appData.monoMode) {
\r
3493 SelectPalette(hdc, hPal, FALSE);
\r
3494 RealizePalette(hdc);
\r
3498 releaseDC = FALSE;
\r
3501 /* Create some work-DCs */
\r
3502 hdcmem = CreateCompatibleDC(hdc);
\r
3503 tmphdc = CreateCompatibleDC(hdc);
\r
3505 /* If dragging is in progress, we temporarely remove the piece */
\r
3506 /* [HGM] or temporarily decrease count if stacked */
\r
3507 /* !! Moved to before board compare !! */
\r
3508 if (dragInfo.from.x >= 0 && dragInfo.pos.x >= 0) {
\r
3509 dragged_piece = board[dragInfo.from.y][dragInfo.from.x];
\r
3510 if(dragInfo.from.x == BOARD_LEFT-2 ) {
\r
3511 if(--board[dragInfo.from.y][dragInfo.from.x+1] == 0 )
\r
3512 board[dragInfo.from.y][dragInfo.from.x] = EmptySquare;
\r
3514 if(dragInfo.from.x == BOARD_RGHT+1) {
\r
3515 if(--board[dragInfo.from.y][dragInfo.from.x-1] == 0 )
\r
3516 board[dragInfo.from.y][dragInfo.from.x] = EmptySquare;
\r
3518 board[dragInfo.from.y][dragInfo.from.x] = gatingPiece;
\r
3521 /* Figure out which squares need updating by comparing the
\r
3522 * newest board with the last drawn board and checking if
\r
3523 * flipping has changed.
\r
3525 if (!fullrepaint && lastDrawnValid[nr] && (nr == 1 || lastDrawnFlipView == flipView)) {
\r
3526 for (row = 0; row < BOARD_HEIGHT; row++) { /* [HGM] true size, not 8 */
\r
3527 for (column = 0; column < BOARD_WIDTH; column++) {
\r
3528 if (lastDrawn[nr][row][column] != board[row][column]) {
\r
3529 SquareToPos(row, column, &x, &y);
\r
3530 clips[num_clips++] =
\r
3531 CreateRectRgn(x, y, x + squareSize, y + squareSize);
\r
3535 if(nr == 0) { // [HGM] dual: no highlights on second board
\r
3536 for (i=0; i<2; i++) {
\r
3537 if (lastDrawnHighlight.sq[i].x != highlightInfo.sq[i].x ||
\r
3538 lastDrawnHighlight.sq[i].y != highlightInfo.sq[i].y) {
\r
3539 if (lastDrawnHighlight.sq[i].x >= 0 &&
\r
3540 lastDrawnHighlight.sq[i].y >= 0) {
\r
3541 SquareToPos(lastDrawnHighlight.sq[i].y,
\r
3542 lastDrawnHighlight.sq[i].x, &x, &y);
\r
3543 clips[num_clips++] =
\r
3544 CreateRectRgn(x - lineGap, y - lineGap,
\r
3545 x + squareSize + lineGap, y + squareSize + lineGap);
\r
3547 if (highlightInfo.sq[i].x >= 0 && highlightInfo.sq[i].y >= 0) {
\r
3548 SquareToPos(highlightInfo.sq[i].y, highlightInfo.sq[i].x, &x, &y);
\r
3549 clips[num_clips++] =
\r
3550 CreateRectRgn(x - lineGap, y - lineGap,
\r
3551 x + squareSize + lineGap, y + squareSize + lineGap);
\r
3555 for (i=0; i<2; i++) {
\r
3556 if (lastDrawnPremove.sq[i].x != premoveHighlightInfo.sq[i].x ||
\r
3557 lastDrawnPremove.sq[i].y != premoveHighlightInfo.sq[i].y) {
\r
3558 if (lastDrawnPremove.sq[i].x >= 0 &&
\r
3559 lastDrawnPremove.sq[i].y >= 0) {
\r
3560 SquareToPos(lastDrawnPremove.sq[i].y,
\r
3561 lastDrawnPremove.sq[i].x, &x, &y);
\r
3562 clips[num_clips++] =
\r
3563 CreateRectRgn(x - lineGap, y - lineGap,
\r
3564 x + squareSize + lineGap, y + squareSize + lineGap);
\r
3566 if (premoveHighlightInfo.sq[i].x >= 0 &&
\r
3567 premoveHighlightInfo.sq[i].y >= 0) {
\r
3568 SquareToPos(premoveHighlightInfo.sq[i].y,
\r
3569 premoveHighlightInfo.sq[i].x, &x, &y);
\r
3570 clips[num_clips++] =
\r
3571 CreateRectRgn(x - lineGap, y - lineGap,
\r
3572 x + squareSize + lineGap, y + squareSize + lineGap);
\r
3576 } else { // nr == 1
\r
3577 partnerHighlightInfo.sq[0].y = board[EP_STATUS-4];
\r
3578 partnerHighlightInfo.sq[0].x = board[EP_STATUS-3];
\r
3579 partnerHighlightInfo.sq[1].y = board[EP_STATUS-2];
\r
3580 partnerHighlightInfo.sq[1].x = board[EP_STATUS-1];
\r
3581 for (i=0; i<2; i++) {
\r
3582 if (partnerHighlightInfo.sq[i].x >= 0 &&
\r
3583 partnerHighlightInfo.sq[i].y >= 0) {
\r
3584 SquareToPos(partnerHighlightInfo.sq[i].y,
\r
3585 partnerHighlightInfo.sq[i].x, &x, &y);
\r
3586 clips[num_clips++] =
\r
3587 CreateRectRgn(x - lineGap, y - lineGap,
\r
3588 x + squareSize + lineGap, y + squareSize + lineGap);
\r
3590 if (oldPartnerHighlight.sq[i].x >= 0 &&
\r
3591 oldPartnerHighlight.sq[i].y >= 0) {
\r
3592 SquareToPos(oldPartnerHighlight.sq[i].y,
\r
3593 oldPartnerHighlight.sq[i].x, &x, &y);
\r
3594 clips[num_clips++] =
\r
3595 CreateRectRgn(x - lineGap, y - lineGap,
\r
3596 x + squareSize + lineGap, y + squareSize + lineGap);
\r
3601 fullrepaint = TRUE;
\r
3604 /* Create a buffer bitmap - this is the actual bitmap
\r
3605 * being written to. When all the work is done, we can
\r
3606 * copy it to the real DC (the screen). This avoids
\r
3607 * the problems with flickering.
\r
3609 GetClientRect(hwndMain, &Rect);
\r
3610 bufferBitmap = CreateCompatibleBitmap(hdc, Rect.right-Rect.left+1,
\r
3611 Rect.bottom-Rect.top+1);
\r
3612 oldBitmap = SelectObject(hdcmem, bufferBitmap);
\r
3613 if (!appData.monoMode) {
\r
3614 SelectPalette(hdcmem, hPal, FALSE);
\r
3617 /* Create clips for dragging */
\r
3618 if (!fullrepaint) {
\r
3619 if (dragInfo.from.x >= 0) {
\r
3620 SquareToPos(dragInfo.from.y, dragInfo.from.x, &x, &y);
\r
3621 clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);
\r
3623 if (dragInfo.start.x >= 0) {
\r
3624 SquareToPos(dragInfo.start.y, dragInfo.start.x, &x, &y);
\r
3625 clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);
\r
3627 if (dragInfo.pos.x >= 0) {
\r
3628 x = dragInfo.pos.x - squareSize / 2;
\r
3629 y = dragInfo.pos.y - squareSize / 2;
\r
3630 clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);
\r
3632 if (dragInfo.lastpos.x >= 0) {
\r
3633 x = dragInfo.lastpos.x - squareSize / 2;
\r
3634 y = dragInfo.lastpos.y - squareSize / 2;
\r
3635 clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);
\r
3639 /* Are we animating a move?
\r
3641 * - remove the piece from the board (temporarely)
\r
3642 * - calculate the clipping region
\r
3644 if (!fullrepaint) {
\r
3645 if (animInfo.piece != EmptySquare) {
\r
3646 board[animInfo.from.y][animInfo.from.x] = EmptySquare;
\r
3647 x = boardRect.left + animInfo.lastpos.x;
\r
3648 y = boardRect.top + animInfo.lastpos.y;
\r
3649 x2 = boardRect.left + animInfo.pos.x;
\r
3650 y2 = boardRect.top + animInfo.pos.y;
\r
3651 clips[num_clips++] = CreateRectRgn(MIN(x,x2), MIN(y,y2), MAX(x,x2)+squareSize, MAX(y,y2)+squareSize);
\r
3652 /* Slight kludge. The real problem is that after AnimateMove is
\r
3653 done, the position on the screen does not match lastDrawn.
\r
3654 This currently causes trouble only on e.p. captures in
\r
3655 atomic, where the piece moves to an empty square and then
\r
3656 explodes. The old and new positions both had an empty square
\r
3657 at the destination, but animation has drawn a piece there and
\r
3658 we have to remember to erase it. [HGM] moved until after setting lastDrawn */
\r
3659 lastDrawn[0][animInfo.to.y][animInfo.to.x] = animInfo.piece;
\r
3663 /* No clips? Make sure we have fullrepaint set to TRUE */
\r
3664 if (num_clips == 0)
\r
3665 fullrepaint = TRUE;
\r
3667 /* Set clipping on the memory DC */
\r
3668 if (!fullrepaint) {
\r
3669 SelectClipRgn(hdcmem, clips[0]);
\r
3670 for (x = 1; x < num_clips; x++) {
\r
3671 if (ExtSelectClipRgn(hdcmem, clips[x], RGN_OR) == ERROR)
\r
3672 abort(); // this should never ever happen!
\r
3676 /* Do all the drawing to the memory DC */
\r
3677 if(explodeInfo.radius) { // [HGM] atomic
\r
3679 int x, y, r=(explodeInfo.radius * squareSize)/100;
\r
3680 ChessSquare piece = board[explodeInfo.fromY][explodeInfo.fromX];
\r
3681 board[explodeInfo.fromY][explodeInfo.fromX] = EmptySquare; // suppress display of capturer
\r
3682 SquareToPos(explodeInfo.toY, explodeInfo.toX, &x, &y);
\r
3683 x += squareSize/2;
\r
3684 y += squareSize/2;
\r
3685 if(!fullrepaint) {
\r
3686 clips[num_clips] = CreateRectRgn(x-r, y-r, x+r, y+r);
\r
3687 ExtSelectClipRgn(hdcmem, clips[num_clips++], RGN_OR);
\r
3689 DrawGridOnDC(hdcmem);
\r
3690 DrawHighlightsOnDC(hdcmem, &highlightInfo, HIGHLIGHT_PEN);
\r
3691 DrawHighlightsOnDC(hdcmem, &premoveHighlightInfo, PREMOVE_PEN);
\r
3692 DrawBoardOnDC(hdcmem, board, tmphdc);
\r
3693 board[explodeInfo.fromY][explodeInfo.fromX] = piece;
\r
3694 oldBrush = SelectObject(hdcmem, explodeBrush);
\r
3695 Ellipse(hdcmem, x-r, y-r, x+r, y+r);
\r
3696 SelectObject(hdcmem, oldBrush);
\r
3698 DrawGridOnDC(hdcmem);
\r
3699 if(nr == 0) { // [HGM] dual: decide which highlights to draw
\r
3700 DrawHighlightsOnDC(hdcmem, &highlightInfo, HIGHLIGHT_PEN);
\r
3701 DrawHighlightsOnDC(hdcmem, &premoveHighlightInfo, PREMOVE_PEN);
\r
3703 DrawHighlightsOnDC(hdcmem, &partnerHighlightInfo, HIGHLIGHT_PEN);
\r
3704 oldPartnerHighlight = partnerHighlightInfo;
\r
3706 DrawBoardOnDC(hdcmem, board, tmphdc);
\r
3708 if(nr == 0) // [HGM] dual: markers only on left board
\r
3709 for (row = 0; row < BOARD_HEIGHT; row++) {
\r
3710 for (column = 0; column < BOARD_WIDTH; column++) {
\r
3711 if (marker[row][column]) { // marker changes only occur with full repaint!
\r
3712 HBRUSH oldBrush = SelectObject(hdcmem,
\r
3713 marker[row][column] == 2 ? markerBrush : explodeBrush);
\r
3714 SquareToPos(row, column, &x, &y);
\r
3715 Ellipse(hdcmem, x + squareSize/4, y + squareSize/4,
\r
3716 x + 3*squareSize/4, y + 3*squareSize/4);
\r
3717 SelectObject(hdcmem, oldBrush);
\r
3722 HBITMAP whiteLogo = (HBITMAP) first.programLogo, blackLogo = (HBITMAP) second.programLogo;
\r
3723 if(appData.autoLogo) {
\r
3725 switch(gameMode) { // pick logos based on game mode
\r
3726 case IcsObserving:
\r
3727 whiteLogo = second.programLogo; // ICS logo
\r
3728 blackLogo = second.programLogo;
\r
3731 case IcsPlayingWhite:
\r
3732 if(!appData.zippyPlay) whiteLogo = userLogo;
\r
3733 blackLogo = second.programLogo; // ICS logo
\r
3735 case IcsPlayingBlack:
\r
3736 whiteLogo = second.programLogo; // ICS logo
\r
3737 blackLogo = appData.zippyPlay ? first.programLogo : userLogo;
\r
3739 case TwoMachinesPlay:
\r
3740 if(first.twoMachinesColor[0] == 'b') {
\r
3741 whiteLogo = second.programLogo;
\r
3742 blackLogo = first.programLogo;
\r
3745 case MachinePlaysWhite:
\r
3746 blackLogo = userLogo;
\r
3748 case MachinePlaysBlack:
\r
3749 whiteLogo = userLogo;
\r
3750 blackLogo = first.programLogo;
\r
3753 DrawLogoOnDC(hdc, leftLogoRect, flipClock ? blackLogo : whiteLogo);
\r
3754 DrawLogoOnDC(hdc, rightLogoRect, flipClock ? whiteLogo : blackLogo);
\r
3757 if( appData.highlightMoveWithArrow ) {
\r
3758 DrawArrowHighlight(hdcmem);
\r
3761 DrawCoordsOnDC(hdcmem);
\r
3763 CopyBoard(lastDrawn[nr], board); /* [HGM] Moved to here from end of routine, */
\r
3764 /* to make sure lastDrawn contains what is actually drawn */
\r
3766 /* Put the dragged piece back into place and draw it (out of place!) */
\r
3767 if (dragged_piece != EmptySquare) {
\r
3768 /* [HGM] or restack */
\r
3769 if(dragInfo.from.x == BOARD_LEFT-2 )
\r
3770 board[dragInfo.from.y][dragInfo.from.x+1]++;
\r
3772 if(dragInfo.from.x == BOARD_RGHT+1 )
\r
3773 board[dragInfo.from.y][dragInfo.from.x-1]++;
\r
3774 board[dragInfo.from.y][dragInfo.from.x] = dragged_piece;
\r
3775 x = dragInfo.pos.x - squareSize / 2;
\r
3776 y = dragInfo.pos.y - squareSize / 2;
\r
3777 DrawPieceOnDC(hdcmem, dragged_piece,
\r
3778 ((int) dragged_piece < (int) BlackPawn),
\r
3779 (dragInfo.from.y + dragInfo.from.x) % 2, x, y, tmphdc);
\r
3782 /* Put the animated piece back into place and draw it */
\r
3783 if (animInfo.piece != EmptySquare) {
\r
3784 board[animInfo.from.y][animInfo.from.x] = animInfo.piece;
\r
3785 x = boardRect.left + animInfo.pos.x;
\r
3786 y = boardRect.top + animInfo.pos.y;
\r
3787 DrawPieceOnDC(hdcmem, animInfo.piece,
\r
3788 ((int) animInfo.piece < (int) BlackPawn),
\r
3789 (animInfo.from.y + animInfo.from.x) % 2, x, y, tmphdc);
\r
3792 /* Release the bufferBitmap by selecting in the old bitmap
\r
3793 * and delete the memory DC
\r
3795 SelectObject(hdcmem, oldBitmap);
\r
3798 /* Set clipping on the target DC */
\r
3799 if (!fullrepaint) {
\r
3800 if(nr == 1) for (x = 0; x < num_clips; x++) { // [HGM] dual: translate clips
\r
3802 GetRgnBox(clips[x], &rect);
\r
3803 DeleteObject(clips[x]);
\r
3804 clips[x] = CreateRectRgn(rect.left + wpMain.width/2, rect.top,
\r
3805 rect.right + wpMain.width/2, rect.bottom);
\r
3807 SelectClipRgn(hdc, clips[0]);
\r
3808 for (x = 1; x < num_clips; x++) {
\r
3809 if (ExtSelectClipRgn(hdc, clips[x], RGN_OR) == ERROR)
\r
3810 abort(); // this should never ever happen!
\r
3814 /* Copy the new bitmap onto the screen in one go.
\r
3815 * This way we avoid any flickering
\r
3817 oldBitmap = SelectObject(tmphdc, bufferBitmap);
\r
3818 BitBlt(hdc, boardRect.left + twoBoards*partnerUp*wpMain.width/2, boardRect.top, // [HGM] dual
\r
3819 boardRect.right - boardRect.left,
\r
3820 boardRect.bottom - boardRect.top,
\r
3821 tmphdc, boardRect.left, boardRect.top, SRCCOPY);
\r
3822 if(saveDiagFlag) {
\r
3823 BITMAP b; int i, j=0, m, w, wb, fac=0; char pData[1000000];
\r
3824 BITMAPINFOHEADER bih; int color[16], nrColors=0;
\r
3826 GetObject(bufferBitmap, sizeof(b), &b);
\r
3827 if(b.bmWidthBytes*b.bmHeight <= 990000) {
\r
3828 bih.biSize = sizeof(BITMAPINFOHEADER);
\r
3829 bih.biWidth = b.bmWidth;
\r
3830 bih.biHeight = b.bmHeight;
\r
3832 bih.biBitCount = b.bmBitsPixel;
\r
3833 bih.biCompression = 0;
\r
3834 bih.biSizeImage = b.bmWidthBytes*b.bmHeight;
\r
3835 bih.biXPelsPerMeter = 0;
\r
3836 bih.biYPelsPerMeter = 0;
\r
3837 bih.biClrUsed = 0;
\r
3838 bih.biClrImportant = 0;
\r
3839 // fprintf(diagFile, "t=%d\nw=%d\nh=%d\nB=%d\nP=%d\nX=%d\n",
\r
3840 // b.bmType, b.bmWidth, b.bmHeight, b.bmWidthBytes, b.bmPlanes, b.bmBitsPixel);
\r
3841 GetDIBits(tmphdc,bufferBitmap,0,b.bmHeight,pData,(BITMAPINFO*)&bih,DIB_RGB_COLORS);
\r
3842 // fprintf(diagFile, "%8x\n", (int) pData);
\r
3844 wb = b.bmWidthBytes;
\r
3846 for(i=0; i<wb*(b.bmHeight - boardRect.top + OUTER_MARGIN)>>2; i++) {
\r
3847 int k = ((int*) pData)[i];
\r
3848 for(j=0; j<nrColors; j++) if(color[j] == k) break;
\r
3849 if(j >= 16) break;
\r
3851 if(j >= nrColors) nrColors = j+1;
\r
3853 if(j<16) { // 16 colors is enough. Compress to 4 bits per pixel
\r
3855 for(i=0; i<b.bmHeight - boardRect.top + OUTER_MARGIN; i++) {
\r
3856 for(w=0; w<(wb>>2); w+=2) {
\r
3857 int k = ((int*) pData)[(wb*i>>2) + w];
\r
3858 for(j=0; j<nrColors; j++) if(color[j] == k) break;
\r
3859 k = ((int*) pData)[(wb*i>>2) + w + 1];
\r
3860 for(m=0; m<nrColors; m++) if(color[m] == k) break;
\r
3861 pData[p++] = m | j<<4;
\r
3863 while(p&3) pData[p++] = 0;
\r
3866 wb = ((wb+31)>>5)<<2;
\r
3868 // write BITMAPFILEHEADER
\r
3869 fprintf(diagFile, "BM");
\r
3870 fputDW(diagFile, wb*(b.bmHeight - boardRect.top + OUTER_MARGIN)+0x36 + (fac?64:0));
\r
3871 fputDW(diagFile, 0);
\r
3872 fputDW(diagFile, 0x36 + (fac?64:0));
\r
3873 // write BITMAPINFOHEADER
\r
3874 fputDW(diagFile, 40);
\r
3875 fputDW(diagFile, b.bmWidth);
\r
3876 fputDW(diagFile, b.bmHeight - boardRect.top + OUTER_MARGIN);
\r
3877 if(fac) fputDW(diagFile, 0x040001); // planes and bits/pixel
\r
3878 else fputDW(diagFile, 0x200001); // planes and bits/pixel
\r
3879 fputDW(diagFile, 0);
\r
3880 fputDW(diagFile, 0);
\r
3881 fputDW(diagFile, 0);
\r
3882 fputDW(diagFile, 0);
\r
3883 fputDW(diagFile, 0);
\r
3884 fputDW(diagFile, 0);
\r
3885 // write color table
\r
3887 for(i=0; i<16; i++) fputDW(diagFile, color[i]);
\r
3888 // write bitmap data
\r
3889 for(i=0; i<wb*(b.bmHeight - boardRect.top + OUTER_MARGIN); i++)
\r
3890 fputc(pData[i], diagFile);
\r
3894 SelectObject(tmphdc, oldBitmap);
\r
3896 /* Massive cleanup */
\r
3897 for (x = 0; x < num_clips; x++)
\r
3898 DeleteObject(clips[x]);
\r
3901 DeleteObject(bufferBitmap);
\r
3904 ReleaseDC(hwndMain, hdc);
\r
3906 if (lastDrawnFlipView != flipView && nr == 0) {
\r
3908 CheckMenuItem(GetMenu(hwndMain),IDM_FlipView, MF_BYCOMMAND|MF_CHECKED);
\r
3910 CheckMenuItem(GetMenu(hwndMain),IDM_FlipView, MF_BYCOMMAND|MF_UNCHECKED);
\r
3913 /* CopyBoard(lastDrawn, board);*/
\r
3914 lastDrawnHighlight = highlightInfo;
\r
3915 lastDrawnPremove = premoveHighlightInfo;
\r
3916 lastDrawnFlipView = flipView;
\r
3917 lastDrawnValid[nr] = 1;
\r
3920 /* [HGM] diag: Save the current board display to the given open file and close the file */
\r
3925 saveDiagFlag = 1; diagFile = f;
\r
3926 HDCDrawPosition(NULL, TRUE, NULL);
\r
3930 // if(f != NULL) fprintf(f, "Sorry, but this feature is still in preparation\n");
\r
3937 /*---------------------------------------------------------------------------*\
\r
3938 | CLIENT PAINT PROCEDURE
\r
3939 | This is the main event-handler for the WM_PAINT message.
\r
3941 \*---------------------------------------------------------------------------*/
\r
3943 PaintProc(HWND hwnd)
\r
3949 if((hdc = BeginPaint(hwnd, &ps))) {
\r
3950 if (IsIconic(hwnd)) {
\r
3951 DrawIcon(hdc, 2, 2, iconCurrent);
\r
3953 if (!appData.monoMode) {
\r
3954 SelectPalette(hdc, hPal, FALSE);
\r
3955 RealizePalette(hdc);
\r
3957 HDCDrawPosition(hdc, 1, NULL);
\r
3958 if(twoBoards) { // [HGM] dual: also redraw other board in other orientation
\r
3959 flipView = !flipView; partnerUp = !partnerUp;
\r
3960 HDCDrawPosition(hdc, 1, NULL);
\r
3961 flipView = !flipView; partnerUp = !partnerUp;
\r
3964 SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);
\r
3965 ExtTextOut(hdc, messageRect.left, messageRect.top,
\r
3966 ETO_CLIPPED|ETO_OPAQUE,
\r
3967 &messageRect, messageText, strlen(messageText), NULL);
\r
3968 SelectObject(hdc, oldFont);
\r
3969 DisplayBothClocks();
\r
3971 EndPaint(hwnd,&ps);
\r
3979 * If the user selects on a border boundary, return -1; if off the board,
\r
3980 * return -2. Otherwise map the event coordinate to the square.
\r
3981 * The offset boardRect.left or boardRect.top must already have been
\r
3982 * subtracted from x.
\r
3984 int EventToSquare(x, limit)
\r
3992 if ((x % (squareSize + lineGap)) >= squareSize)
\r
3994 x /= (squareSize + lineGap);
\r
4006 DropEnable dropEnables[] = {
\r
4007 { 'P', DP_Pawn, N_("Pawn") },
\r
4008 { 'N', DP_Knight, N_("Knight") },
\r
4009 { 'B', DP_Bishop, N_("Bishop") },
\r
4010 { 'R', DP_Rook, N_("Rook") },
\r
4011 { 'Q', DP_Queen, N_("Queen") },
\r
4015 SetupDropMenu(HMENU hmenu)
\r
4017 int i, count, enable;
\r
4019 extern char white_holding[], black_holding[];
\r
4020 char item[MSG_SIZ];
\r
4022 for (i=0; i<sizeof(dropEnables)/sizeof(DropEnable); i++) {
\r
4023 p = strchr(gameMode == IcsPlayingWhite ? white_holding : black_holding,
\r
4024 dropEnables[i].piece);
\r
4026 while (p && *p++ == dropEnables[i].piece) count++;
\r
4027 snprintf(item, MSG_SIZ, "%s %d", T_(dropEnables[i].name), count);
\r
4028 enable = count > 0 || !appData.testLegality
\r
4029 /*!!temp:*/ || (gameInfo.variant == VariantCrazyhouse
\r
4030 && !appData.icsActive);
\r
4031 ModifyMenu(hmenu, dropEnables[i].command,
\r
4032 MF_BYCOMMAND | (enable ? MF_ENABLED : MF_GRAYED) | MF_STRING,
\r
4033 dropEnables[i].command, item);
\r
4037 void DragPieceBegin(int x, int y)
\r
4039 dragInfo.lastpos.x = boardRect.left + x;
\r
4040 dragInfo.lastpos.y = boardRect.top + y;
\r
4041 dragInfo.from.x = fromX;
\r
4042 dragInfo.from.y = fromY;
\r
4043 dragInfo.start = dragInfo.from;
\r
4044 SetCapture(hwndMain);
\r
4047 void DragPieceEnd(int x, int y)
\r
4050 dragInfo.start.x = dragInfo.start.y = -1;
\r
4051 dragInfo.from = dragInfo.start;
\r
4052 dragInfo.pos = dragInfo.lastpos = dragInfo.start;
\r
4055 /* Event handler for mouse messages */
\r
4057 MouseEvent(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
\r
4061 static int recursive = 0;
\r
4063 BOOLEAN forceFullRepaint = IsFullRepaintPreferrable(); /* [AS] */
\r
4066 if (message == WM_MBUTTONUP) {
\r
4067 /* Hideous kludge to fool TrackPopupMenu into paying attention
\r
4068 to the middle button: we simulate pressing the left button too!
\r
4070 PostMessage(hwnd, WM_LBUTTONDOWN, wParam, lParam);
\r
4071 PostMessage(hwnd, WM_LBUTTONUP, wParam, lParam);
\r
4077 pt.x = LOWORD(lParam);
\r
4078 pt.y = HIWORD(lParam);
\r
4079 x = EventToSquare(pt.x - boardRect.left, BOARD_WIDTH);
\r
4080 y = EventToSquare(pt.y - boardRect.top, BOARD_HEIGHT);
\r
4081 if (!flipView && y >= 0) {
\r
4082 y = BOARD_HEIGHT - 1 - y;
\r
4084 if (flipView && x >= 0) {
\r
4085 x = BOARD_WIDTH - 1 - x;
\r
4088 shiftKey = GetKeyState(VK_SHIFT) < 0; // [HGM] remember last shift status
\r
4090 switch (message) {
\r
4091 case WM_LBUTTONDOWN:
\r
4092 if (PtInRect((LPRECT) &whiteRect, pt)) {
\r
4093 if (gameMode == EditPosition) {
\r
4094 SetWhiteToPlayEvent();
\r
4095 } else if (gameMode == EditGame || GetKeyState(VK_SHIFT) < 0) {
\r
4096 AdjustClock(flipClock, -1);
\r
4097 } else if (gameMode == IcsPlayingBlack ||
\r
4098 gameMode == MachinePlaysWhite) {
\r
4101 } else if (PtInRect((LPRECT) &blackRect, pt)) {
\r
4102 if (gameMode == EditPosition) {
\r
4103 SetBlackToPlayEvent();
\r
4104 } else if (gameMode == EditGame || GetKeyState(VK_SHIFT) < 0) {
\r
4105 AdjustClock(!flipClock, -1);
\r
4106 } else if (gameMode == IcsPlayingWhite ||
\r
4107 gameMode == MachinePlaysBlack) {
\r
4111 dragInfo.start.x = dragInfo.start.y = -1;
\r
4112 dragInfo.from = dragInfo.start;
\r
4113 if(fromX == -1 && frozen) { // not sure where this is for
\r
4114 fromX = fromY = -1;
\r
4115 DrawPosition(forceFullRepaint || FALSE, NULL); /* [AS] */
\r
4118 LeftClick(Press, pt.x - boardRect.left, pt.y - boardRect.top);
\r
4119 DrawPosition(TRUE, NULL);
\r
4122 case WM_LBUTTONUP:
\r
4123 LeftClick(Release, pt.x - boardRect.left, pt.y - boardRect.top);
\r
4124 DrawPosition(TRUE, NULL);
\r
4127 case WM_MOUSEMOVE:
\r
4128 if(SeekGraphClick(Press, pt.x - boardRect.left, pt.y - boardRect.top, 1)) break;
\r
4129 MovePV(pt.x - boardRect.left, pt.y - boardRect.top, boardRect.bottom - boardRect.top);
\r
4130 if ((appData.animateDragging || appData.highlightDragging)
\r
4131 && (wParam & MK_LBUTTON)
\r
4132 && dragInfo.from.x >= 0)
\r
4134 BOOL full_repaint = FALSE;
\r
4136 if (appData.animateDragging) {
\r
4137 dragInfo.pos = pt;
\r
4139 if (appData.highlightDragging) {
\r
4140 SetHighlights(fromX, fromY, x, y);
\r
4141 if( IsDrawArrowEnabled() && (x < 0 || x >= BOARD_WIDTH || y < 0 || y >= BOARD_HEIGHT) ) {
\r
4142 full_repaint = TRUE;
\r
4146 DrawPosition( full_repaint, NULL);
\r
4148 dragInfo.lastpos = dragInfo.pos;
\r
4152 case WM_MOUSEWHEEL: // [DM]
\r
4153 { static int lastDir = 0; // [HGM] build in some hysteresis to avoid spurious events
\r
4154 /* Mouse Wheel is being rolled forward
\r
4155 * Play moves forward
\r
4157 if((short)HIWORD(wParam) < 0 && currentMove < forwardMostMove)
\r
4158 { if(lastDir == 1) ForwardEvent(); else lastDir = 1; } // [HGM] suppress first event in direction
\r
4159 /* Mouse Wheel is being rolled backward
\r
4160 * Play moves backward
\r
4162 if((short)HIWORD(wParam) > 0 && currentMove > backwardMostMove)
\r
4163 { if(lastDir == -1) BackwardEvent(); else lastDir = -1; }
\r
4167 case WM_MBUTTONUP:
\r
4168 case WM_RBUTTONUP:
\r
4170 RightClick(Release, pt.x - boardRect.left, pt.y - boardRect.top, &fromX, &fromY);
\r
4173 case WM_MBUTTONDOWN:
\r
4174 case WM_RBUTTONDOWN:
\r
4177 fromX = fromY = -1;
\r
4178 dragInfo.pos.x = dragInfo.pos.y = -1;
\r
4179 dragInfo.start.x = dragInfo.start.y = -1;
\r
4180 dragInfo.from = dragInfo.start;
\r
4181 dragInfo.lastpos = dragInfo.pos;
\r
4182 if (appData.highlightDragging) {
\r
4183 ClearHighlights();
\r
4186 /* [HGM] right mouse button in clock area edit-game mode ups clock */
\r
4187 if (PtInRect((LPRECT) &whiteRect, pt)) {
\r
4188 if (gameMode == EditGame || GetKeyState(VK_SHIFT) < 0) AdjustClock(flipClock, 1);
\r
4189 } else if (PtInRect((LPRECT) &blackRect, pt)) {
\r
4190 if (gameMode == EditGame || GetKeyState(VK_SHIFT) < 0) AdjustClock(!flipClock, 1);
\r
4194 DrawPosition(TRUE, NULL);
\r
4196 menuNr = RightClick(Press, pt.x - boardRect.left, pt.y - boardRect.top, &fromX, &fromY);
\r
4199 if (message == WM_MBUTTONDOWN) {
\r
4200 buttonCount = 3; /* even if system didn't think so */
\r
4201 if (wParam & MK_SHIFT)
\r
4202 MenuPopup(hwnd, pt, LoadMenu(hInst, "BlackPieceMenu"), -1);
\r
4204 MenuPopup(hwnd, pt, LoadMenu(hInst, "WhitePieceMenu"), -1);
\r
4205 } else { /* message == WM_RBUTTONDOWN */
\r
4206 /* Just have one menu, on the right button. Windows users don't
\r
4207 think to try the middle one, and sometimes other software steals
\r
4208 it, or it doesn't really exist. */
\r
4209 if(gameInfo.variant != VariantShogi)
\r
4210 MenuPopup(hwnd, pt, LoadMenu(hInst, "PieceMenu"), -1);
\r
4212 MenuPopup(hwnd, pt, LoadMenu(hInst, "ShogiPieceMenu"), -1);
\r
4216 SetCapture(hwndMain);
4219 hmenu = LoadMenu(hInst, "DropPieceMenu");
\r
4220 SetupDropMenu(hmenu);
\r
4221 MenuPopup(hwnd, pt, hmenu, -1);
\r
4231 /* Preprocess messages for buttons in main window */
\r
4233 ButtonProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
\r
4235 int id = GetWindowLong(hwnd, GWL_ID);
\r
4238 for (i=0; i<N_BUTTONS; i++) {
\r
4239 if (buttonDesc[i].id == id) break;
\r
4241 if (i == N_BUTTONS) return 0;
\r
4242 switch (message) {
\r
4247 dir = (wParam == VK_LEFT) ? -1 : 1;
\r
4248 SetFocus(buttonDesc[(i + dir + N_BUTTONS) % N_BUTTONS].hwnd);
\r
4255 SendMessage(hwndMain, WM_COMMAND, MAKEWPARAM(buttonDesc[i].id, 0), 0);
\r
4258 if (appData.icsActive && (isalpha((char)wParam) || wParam == '0')) {
\r
4259 // [HGM] movenum: only letters or leading zero should go to ICS input
\r
4260 HWND h = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
4261 if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);
\r
4263 SendMessage(h, WM_CHAR, wParam, lParam);
\r
4265 } else if (isalpha((char)wParam) || isdigit((char)wParam)){
\r
4266 PopUpMoveDialog((char)wParam);
\r
4272 return CallWindowProc(buttonDesc[i].wndproc, hwnd, message, wParam, lParam);
\r
4275 /* Process messages for Promotion dialog box */
\r
4277 Promotion(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
4281 switch (message) {
\r
4282 case WM_INITDIALOG: /* message: initialize dialog box */
\r
4283 /* Center the dialog over the application window */
\r
4284 CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));
\r
4285 Translate(hDlg, DLG_PromotionKing);
\r
4286 ShowWindow(GetDlgItem(hDlg, PB_King),
\r
4287 (!appData.testLegality || gameInfo.variant == VariantSuicide ||
\r
4288 gameInfo.variant == VariantGiveaway || gameInfo.variant == VariantSuper ) ?
\r
4289 SW_SHOW : SW_HIDE);
\r
4290 /* [HGM] Only allow C & A promotions if these pieces are defined */
\r
4291 ShowWindow(GetDlgItem(hDlg, PB_Archbishop),
\r
4292 ((PieceToChar(WhiteAngel) >= 'A' &&
\r
4293 PieceToChar(WhiteAngel) != '~') ||
\r
4294 (PieceToChar(BlackAngel) >= 'A' &&
\r
4295 PieceToChar(BlackAngel) != '~') ) ?
\r
4296 SW_SHOW : SW_HIDE);
\r
4297 ShowWindow(GetDlgItem(hDlg, PB_Chancellor),
\r
4298 ((PieceToChar(WhiteMarshall) >= 'A' &&
\r
4299 PieceToChar(WhiteMarshall) != '~') ||
\r
4300 (PieceToChar(BlackMarshall) >= 'A' &&
\r
4301 PieceToChar(BlackMarshall) != '~') ) ?
\r
4302 SW_SHOW : SW_HIDE);
\r
4303 /* [HGM] Hide B & R button in Shogi, use Q as promote, N as defer */
\r
4304 ShowWindow(GetDlgItem(hDlg, PB_Rook),
\r
4305 gameInfo.variant != VariantShogi ?
\r
4306 SW_SHOW : SW_HIDE);
\r
4307 ShowWindow(GetDlgItem(hDlg, PB_Bishop),
\r
4308 gameInfo.variant != VariantShogi ?
\r
4309 SW_SHOW : SW_HIDE);
\r
4310 if(gameInfo.variant == VariantShogi) {
\r
4311 SetDlgItemText(hDlg, PB_Queen, "YES");
\r
4312 SetDlgItemText(hDlg, PB_Knight, "NO");
\r
4313 SetWindowText(hDlg, "Promote?");
\r
4315 ShowWindow(GetDlgItem(hDlg, IDC_Centaur),
\r
4316 gameInfo.variant == VariantSuper ?
\r
4317 SW_SHOW : SW_HIDE);
\r
4320 case WM_COMMAND: /* message: received a command */
\r
4321 switch (LOWORD(wParam)) {
\r
4323 EndDialog(hDlg, TRUE); /* Exit the dialog */
\r
4324 ClearHighlights();
\r
4325 DrawPosition(FALSE, NULL);
\r
4328 promoChar = gameInfo.variant == VariantSuper ? PieceToChar(BlackSilver) : PieceToChar(BlackKing);
\r
4331 promoChar = gameInfo.variant == VariantShogi ? '+' : PieceToChar(BlackQueen);
\r
4334 promoChar = PieceToChar(BlackRook);
\r
4337 promoChar = PieceToChar(BlackBishop);
\r
4339 case PB_Chancellor:
\r
4340 promoChar = PieceToChar(BlackMarshall);
\r
4342 case PB_Archbishop:
\r
4343 promoChar = PieceToChar(BlackAngel);
\r
4346 promoChar = gameInfo.variant == VariantShogi ? '=' : PieceToChar(BlackKnight);
\r
4351 EndDialog(hDlg, TRUE); /* Exit the dialog */
\r
4352 /* [HGM] <popupFix> Call FinishMove rather than UserMoveEvent, as we
\r
4353 only show the popup when we are already sure the move is valid or
\r
4354 legal. We pass a faulty move type, but the kludge is that FinishMove
\r
4355 will figure out it is a promotion from the promoChar. */
\r
4356 UserMoveEvent(fromX, fromY, toX, toY, promoChar);
\r
4357 fromX = fromY = -1;
\r
4358 if (!appData.highlightLastMove) {
\r
4359 ClearHighlights();
\r
4360 DrawPosition(FALSE, NULL);
\r
4367 /* Pop up promotion dialog */
\r
4369 PromotionPopup(HWND hwnd)
\r
4373 lpProc = MakeProcInstance((FARPROC)Promotion, hInst);
\r
4374 DialogBox(hInst, MAKEINTRESOURCE(DLG_PromotionKing),
\r
4375 hwnd, (DLGPROC)lpProc);
\r
4376 FreeProcInstance(lpProc);
\r
4382 DrawPosition(TRUE, NULL);
\r
4383 PromotionPopup(hwndMain);
\r
4386 /* Toggle ShowThinking */
\r
4388 ToggleShowThinking()
\r
4390 appData.showThinking = !appData.showThinking;
\r
4391 ShowThinkingEvent();
\r
4395 LoadGameDialog(HWND hwnd, char* title)
\r
4399 char fileTitle[MSG_SIZ];
\r
4400 f = OpenFileDialog(hwnd, "rb", "",
\r
4401 appData.oldSaveStyle ? "gam" : "pgn",
\r
4403 title, &number, fileTitle, NULL);
\r
4405 cmailMsgLoaded = FALSE;
\r
4406 if (number == 0) {
\r
4407 int error = GameListBuild(f);
\r
4409 DisplayError(_("Cannot build game list"), error);
\r
4410 } else if (!ListEmpty(&gameList) &&
\r
4411 ((ListGame *) gameList.tailPred)->number > 1) {
\r
4412 GameListPopUp(f, fileTitle);
\r
4415 GameListDestroy();
\r
4418 LoadGame(f, number, fileTitle, FALSE);
\r
4422 int get_term_width()
\r
4427 HFONT hfont, hold_font;
\r
4432 hText = GetDlgItem(hwndConsole, OPT_ConsoleText);
\r
4436 // get the text metrics
\r
4437 hdc = GetDC(hText);
\r
4438 lf = font[boardSize][CONSOLE_FONT]->lf;
\r
4439 if (consoleCF.dwEffects & CFE_BOLD)
\r
4440 lf.lfWeight = FW_BOLD;
\r
4441 if (consoleCF.dwEffects & CFE_ITALIC)
\r
4442 lf.lfItalic = TRUE;
\r
4443 if (consoleCF.dwEffects & CFE_STRIKEOUT)
\r
4444 lf.lfStrikeOut = TRUE;
\r
4445 if (consoleCF.dwEffects & CFE_UNDERLINE)
\r
4446 lf.lfUnderline = TRUE;
\r
4447 hfont = CreateFontIndirect(&lf);
\r
4448 hold_font = SelectObject(hdc, hfont);
\r
4449 GetTextMetrics(hdc, &tm);
\r
4450 SelectObject(hdc, hold_font);
\r
4451 DeleteObject(hfont);
\r
4452 ReleaseDC(hText, hdc);
\r
4454 // get the rectangle
\r
4455 SendMessage(hText, EM_GETRECT, 0, (LPARAM)&rc);
\r
4457 return (rc.right-rc.left) / tm.tmAveCharWidth;
\r
4460 void UpdateICSWidth(HWND hText)
\r
4462 LONG old_width, new_width;
\r
4464 new_width = get_term_width(hText, FALSE);
\r
4465 old_width = GetWindowLong(hText, GWL_USERDATA);
\r
4466 if (new_width != old_width)
\r
4468 ics_update_width(new_width);
\r
4469 SetWindowLong(hText, GWL_USERDATA, new_width);
\r
4474 ChangedConsoleFont()
\r
4477 CHARRANGE tmpsel, sel;
\r
4478 MyFont *f = font[boardSize][CONSOLE_FONT];
\r
4479 HWND hText = GetDlgItem(hwndConsole, OPT_ConsoleText);
\r
4480 HWND hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
4483 cfmt.cbSize = sizeof(CHARFORMAT);
\r
4484 cfmt.dwMask = CFM_FACE|CFM_SIZE|CFM_CHARSET;
\r
4485 safeStrCpy(cfmt.szFaceName, font[boardSize][CONSOLE_FONT]->mfp.faceName,
\r
4486 sizeof(cfmt.szFaceName)/sizeof(cfmt.szFaceName[0]) );
\r
4487 /* yHeight is expressed in twips. A twip is 1/20 of a font's point
\r
4488 * size. This was undocumented in the version of MSVC++ that I had
\r
4489 * when I wrote the code, but is apparently documented now.
\r
4491 cfmt.yHeight = (int)(f->mfp.pointSize * 20.0 + 0.5);
\r
4492 cfmt.bCharSet = f->lf.lfCharSet;
\r
4493 cfmt.bPitchAndFamily = f->lf.lfPitchAndFamily;
\r
4494 SendMessage(hText, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &cfmt);
\r
4495 SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &cfmt);
\r
4496 /* Why are the following seemingly needed too? */
\r
4497 SendMessage(hText, EM_SETCHARFORMAT, SCF_DEFAULT, (LPARAM) &cfmt);
\r
4498 SendMessage(hInput, EM_SETCHARFORMAT, SCF_DEFAULT, (LPARAM) &cfmt);
\r
4499 SendMessage(hText, EM_EXGETSEL, 0, (LPARAM)&sel);
\r
4501 tmpsel.cpMax = -1; /*999999?*/
\r
4502 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&tmpsel);
\r
4503 SendMessage(hText, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cfmt);
\r
4504 /* Trying putting this here too. It still seems to tickle a RichEdit
\r
4505 * bug: sometimes RichEdit indents the first line of a paragraph too.
\r
4507 paraf.cbSize = sizeof(paraf);
\r
4508 paraf.dwMask = PFM_OFFSET | PFM_STARTINDENT;
\r
4509 paraf.dxStartIndent = 0;
\r
4510 paraf.dxOffset = WRAP_INDENT;
\r
4511 SendMessage(hText, EM_SETPARAFORMAT, 0, (LPARAM) ¶f);
\r
4512 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
4513 UpdateICSWidth(hText);
\r
4516 /*---------------------------------------------------------------------------*\
\r
4518 * Window Proc for main window
\r
4520 \*---------------------------------------------------------------------------*/
\r
4522 /* Process messages for main window, etc. */
\r
4524 WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
\r
4527 int wmId, wmEvent;
\r
4531 char fileTitle[MSG_SIZ];
\r
4532 char buf[MSG_SIZ];
\r
4533 static SnapData sd;
\r
4535 switch (message) {
\r
4537 case WM_PAINT: /* message: repaint portion of window */
\r
4541 case WM_ERASEBKGND:
\r
4542 if (IsIconic(hwnd)) {
\r
4543 /* Cheat; change the message */
\r
4544 return (DefWindowProc(hwnd, WM_ICONERASEBKGND, wParam, lParam));
\r
4546 return (DefWindowProc(hwnd, message, wParam, lParam));
\r
4550 case WM_LBUTTONDOWN:
\r
4551 case WM_MBUTTONDOWN:
\r
4552 case WM_RBUTTONDOWN:
\r
4553 case WM_LBUTTONUP:
\r
4554 case WM_MBUTTONUP:
\r
4555 case WM_RBUTTONUP:
\r
4556 case WM_MOUSEMOVE:
\r
4557 case WM_MOUSEWHEEL:
\r
4558 MouseEvent(hwnd, message, wParam, lParam);
\r
4561 JAWS_KB_NAVIGATION
\r
4565 JAWS_ALT_INTERCEPT
\r
4567 if (appData.icsActive && ((char)wParam == '\r' || (char)wParam > ' ' && !((char)wParam >= '1' && (char)wParam <= '9'))) {
\r
4568 // [HGM] movenum: for non-zero digits we always do type-in dialog
\r
4569 HWND h = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
4570 if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);
\r
4572 SendMessage(h, message, wParam, lParam);
\r
4573 } else if(lParam != KF_REPEAT) {
\r
4574 if (isalpha((char)wParam) || isdigit((char)wParam)) {
\r
4575 PopUpMoveDialog((char)wParam);
\r
4576 } else if((char)wParam == 003) CopyGameToClipboard();
\r
4577 else if((char)wParam == 026) PasteGameOrFENFromClipboard();
\r
4582 case WM_PALETTECHANGED:
\r
4583 if (hwnd != (HWND)wParam && !appData.monoMode) {
\r
4585 HDC hdc = GetDC(hwndMain);
\r
4586 SelectPalette(hdc, hPal, TRUE);
\r
4587 nnew = RealizePalette(hdc);
\r
4589 paletteChanged = TRUE;
\r
4590 InvalidateRect(hwnd, &boardRect, FALSE);
\r
4592 ReleaseDC(hwnd, hdc);
\r
4596 case WM_QUERYNEWPALETTE:
\r
4597 if (!appData.monoMode /*&& paletteChanged*/) {
\r
4599 HDC hdc = GetDC(hwndMain);
\r
4600 paletteChanged = FALSE;
\r
4601 SelectPalette(hdc, hPal, FALSE);
\r
4602 nnew = RealizePalette(hdc);
\r
4604 InvalidateRect(hwnd, &boardRect, FALSE);
\r
4606 ReleaseDC(hwnd, hdc);
\r
4611 case WM_COMMAND: /* message: command from application menu */
\r
4612 wmId = LOWORD(wParam);
\r
4613 wmEvent = HIWORD(wParam);
\r
4618 SAY("new game enter a move to play against the computer with white");
\r
4621 case IDM_NewGameFRC:
\r
4622 if( NewGameFRC() == 0 ) {
\r
4627 case IDM_NewVariant:
\r
4628 NewVariantPopup(hwnd);
\r
4631 case IDM_LoadGame:
\r
4632 LoadGameDialog(hwnd, _("Load Game from File"));
\r
4635 case IDM_LoadNextGame:
\r
4639 case IDM_LoadPrevGame:
\r
4643 case IDM_ReloadGame:
\r
4647 case IDM_LoadPosition:
\r
4648 if (gameMode == AnalyzeMode || gameMode == AnalyzeFile) {
\r
4649 Reset(FALSE, TRUE);
\r
4652 f = OpenFileDialog(hwnd, "rb", "",
\r
4653 appData.oldSaveStyle ? "pos" : "fen",
\r
4655 _("Load Position from File"), &number, fileTitle, NULL);
\r
4657 LoadPosition(f, number, fileTitle);
\r
4661 case IDM_LoadNextPosition:
\r
4662 ReloadPosition(1);
\r
4665 case IDM_LoadPrevPosition:
\r
4666 ReloadPosition(-1);
\r
4669 case IDM_ReloadPosition:
\r
4670 ReloadPosition(0);
\r
4673 case IDM_SaveGame:
\r
4674 defName = DefaultFileName(appData.oldSaveStyle ? "gam" : "pgn");
\r
4675 f = OpenFileDialog(hwnd, "a", defName,
\r
4676 appData.oldSaveStyle ? "gam" : "pgn",
\r
4678 _("Save Game to File"), NULL, fileTitle, NULL);
\r
4680 SaveGame(f, 0, "");
\r
4684 case IDM_SavePosition:
\r
4685 defName = DefaultFileName(appData.oldSaveStyle ? "pos" : "fen");
\r
4686 f = OpenFileDialog(hwnd, "a", defName,
\r
4687 appData.oldSaveStyle ? "pos" : "fen",
\r
4689 _("Save Position to File"), NULL, fileTitle, NULL);
\r
4691 SavePosition(f, 0, "");
\r
4695 case IDM_SaveDiagram:
\r
4696 defName = "diagram";
\r
4697 f = OpenFileDialog(hwnd, "wb", defName,
\r
4700 "Save Diagram to File", NULL, fileTitle, NULL);
\r
4706 case IDM_CopyGame:
\r
4707 CopyGameToClipboard();
\r
4710 case IDM_PasteGame:
\r
4711 PasteGameFromClipboard();
\r
4714 case IDM_CopyGameListToClipboard:
\r
4715 CopyGameListToClipboard();
\r
4718 /* [AS] Autodetect FEN or PGN data */
\r
4719 case IDM_PasteAny:
\r
4720 PasteGameOrFENFromClipboard();
\r
4723 /* [AS] Move history */
\r
4724 case IDM_ShowMoveHistory:
\r
4725 if( MoveHistoryIsUp() ) {
\r
4726 MoveHistoryPopDown();
\r
4729 MoveHistoryPopUp();
\r
4733 /* [AS] Eval graph */
\r
4734 case IDM_ShowEvalGraph:
\r
4735 if( EvalGraphIsUp() ) {
\r
4736 EvalGraphPopDown();
\r
4740 SetFocus(hwndMain);
\r
4744 /* [AS] Engine output */
\r
4745 case IDM_ShowEngineOutput:
\r
4746 if( EngineOutputIsUp() ) {
\r
4747 EngineOutputPopDown();
\r
4750 EngineOutputPopUp();
\r
4754 /* [AS] User adjudication */
\r
4755 case IDM_UserAdjudication_White:
\r
4756 UserAdjudicationEvent( +1 );
\r
4759 case IDM_UserAdjudication_Black:
\r
4760 UserAdjudicationEvent( -1 );
\r
4763 case IDM_UserAdjudication_Draw:
\r
4764 UserAdjudicationEvent( 0 );
\r
4767 /* [AS] Game list options dialog */
\r
4768 case IDM_GameListOptions:
\r
4769 GameListOptions();
\r
4776 case IDM_CopyPosition:
\r
4777 CopyFENToClipboard();
\r
4780 case IDM_PastePosition:
\r
4781 PasteFENFromClipboard();
\r
4784 case IDM_MailMove:
\r
4788 case IDM_ReloadCMailMsg:
\r
4789 Reset(TRUE, TRUE);
\r
4790 ReloadCmailMsgEvent(FALSE);
\r
4793 case IDM_Minimize:
\r
4794 ShowWindow(hwnd, SW_MINIMIZE);
\r
4801 case IDM_MachineWhite:
\r
4802 MachineWhiteEvent();
\r
4804 * refresh the tags dialog only if it's visible
\r
4806 if (gameMode == MachinePlaysWhite && IsWindowVisible(editTagsDialog)) {
\r
4808 tags = PGNTags(&gameInfo);
\r
4809 TagsPopUp(tags, CmailMsg());
\r
4812 SAY("computer starts playing white");
\r
4815 case IDM_MachineBlack:
\r
4816 MachineBlackEvent();
\r
4818 * refresh the tags dialog only if it's visible
\r
4820 if (gameMode == MachinePlaysBlack && IsWindowVisible(editTagsDialog)) {
\r
4822 tags = PGNTags(&gameInfo);
\r
4823 TagsPopUp(tags, CmailMsg());
\r
4826 SAY("computer starts playing black");
\r
4829 case IDM_Match: // [HGM] match: flows into next case, after setting Match Mode and nr of Games
\r
4830 if(gameMode != BeginningOfGame) { // allow menu item to remain enabled for better mode highligting
\r
4831 DisplayError(_("You can only start a match from the initial position."), 0); break;
\r
4833 matchMode = 2;// distinguish from command-line-triggered case (matchMode=1)
\r
4834 appData.matchGames = appData.defaultMatchGames;
\r
4837 case IDM_TwoMachines:
\r
4838 TwoMachinesEvent();
\r
4840 * refresh the tags dialog only if it's visible
\r
4842 if (gameMode == TwoMachinesPlay && IsWindowVisible(editTagsDialog)) {
\r
4844 tags = PGNTags(&gameInfo);
\r
4845 TagsPopUp(tags, CmailMsg());
\r
4848 SAY("computer starts playing both sides");
\r
4851 case IDM_AnalysisMode:
\r
4852 if (!first.analysisSupport) {
\r
4853 snprintf(buf, MSG_SIZ, _("%s does not support analysis"), first.tidy);
\r
4854 DisplayError(buf, 0);
\r
4856 SAY("analyzing current position");
\r
4857 /* [DM] icsEngineAnlyze [HGM] Why is this front-end??? */
\r
4858 if (appData.icsActive) {
\r
4859 if (gameMode != IcsObserving) {
\r
4860 snprintf(buf, MSG_SIZ, "You are not observing a game");
\r
4861 DisplayError(buf, 0);
\r
4862 /* secure check */
\r
4863 if (appData.icsEngineAnalyze) {
\r
4864 if (appData.debugMode)
\r
4865 fprintf(debugFP, "Found unexpected active ICS engine analyze \n");
\r
4866 ExitAnalyzeMode();
\r
4872 /* if enable, user want disable icsEngineAnalyze */
\r
4873 if (appData.icsEngineAnalyze) {
\r
4874 ExitAnalyzeMode();
\r
4878 appData.icsEngineAnalyze = TRUE;
\r
4879 if (appData.debugMode) fprintf(debugFP, "ICS engine analyze starting...\n");
\r
4882 if (!appData.showThinking) ToggleShowThinking();
\r
4883 AnalyzeModeEvent();
\r
4887 case IDM_AnalyzeFile:
\r
4888 if (!first.analysisSupport) {
\r
4889 char buf[MSG_SIZ];
\r
4890 snprintf(buf, MSG_SIZ, _("%s does not support analysis"), first.tidy);
\r
4891 DisplayError(buf, 0);
\r
4893 if (!appData.showThinking) ToggleShowThinking();
\r
4894 AnalyzeFileEvent();
\r
4895 LoadGameDialog(hwnd, _("Analyze Game from File"));
\r
4896 AnalysisPeriodicEvent(1);
\r
4900 case IDM_IcsClient:
\r
4904 case IDM_EditGame:
\r
4909 case IDM_EditPosition:
\r
4910 EditPositionEvent();
\r
4911 SAY("enter a FEN string or setup a position on the board using the control R pop up menu");
\r
4914 case IDM_Training:
\r
4918 case IDM_ShowGameList:
\r
4919 ShowGameListProc();
\r
4922 case IDM_EditTags:
\r
4927 case IDM_EditComment:
\r
4929 if (commentUp && editComment) {
\r
4932 EditCommentEvent();
\r
4952 case IDM_CallFlag:
\r
4972 case IDM_StopObserving:
\r
4973 StopObservingEvent();
\r
4976 case IDM_StopExamining:
\r
4977 StopExaminingEvent();
\r
4981 UploadGameEvent();
\r
4984 case IDM_TypeInMove:
\r
4985 PopUpMoveDialog('\000');
\r
4988 case IDM_TypeInName:
\r
4989 PopUpNameDialog('\000');
\r
4992 case IDM_Backward:
\r
4994 SetFocus(hwndMain);
\r
5001 SetFocus(hwndMain);
\r
5006 SetFocus(hwndMain);
\r
5011 SetFocus(hwndMain);
\r
5015 RevertEvent(FALSE);
\r
5018 case IDM_Annotate: // [HGM] vari: revert with annotation
\r
5019 RevertEvent(TRUE);
\r
5022 case IDM_TruncateGame:
\r
5023 TruncateGameEvent();
\r
5030 case IDM_RetractMove:
\r
5031 RetractMoveEvent();
\r
5034 case IDM_FlipView:
\r
5035 flipView = !flipView;
\r
5036 DrawPosition(FALSE, NULL);
\r
5039 case IDM_FlipClock:
\r
5040 flipClock = !flipClock;
\r
5041 DisplayBothClocks();
\r
5042 DrawPosition(FALSE, NULL);
\r
5045 case IDM_MuteSounds:
\r
5046 mute = !mute; // [HGM] mute: keep track of global muting variable
\r
5047 CheckMenuItem(GetMenu(hwndMain),IDM_MuteSounds,
\r
5048 MF_BYCOMMAND|(mute?MF_CHECKED:MF_UNCHECKED));
\r
5051 case IDM_GeneralOptions:
\r
5052 GeneralOptionsPopup(hwnd);
\r
5053 DrawPosition(TRUE, NULL);
\r
5056 case IDM_BoardOptions:
\r
5057 BoardOptionsPopup(hwnd);
\r
5060 case IDM_EnginePlayOptions:
\r
5061 EnginePlayOptionsPopup(hwnd);
\r
5064 case IDM_Engine1Options:
\r
5065 EngineOptionsPopup(hwnd, &first);
\r
5068 case IDM_Engine2Options:
\r
5070 if(WaitForSecond(SettingsMenuIfReady)) break;
\r
5071 EngineOptionsPopup(hwnd, &second);
\r
5074 case IDM_OptionsUCI:
\r
5075 UciOptionsPopup(hwnd);
\r
5078 case IDM_IcsOptions:
\r
5079 IcsOptionsPopup(hwnd);
\r
5083 FontsOptionsPopup(hwnd);
\r
5087 SoundOptionsPopup(hwnd);
\r
5090 case IDM_CommPort:
\r
5091 CommPortOptionsPopup(hwnd);
\r
5094 case IDM_LoadOptions:
\r
5095 LoadOptionsPopup(hwnd);
\r
5098 case IDM_SaveOptions:
\r
5099 SaveOptionsPopup(hwnd);
\r
5102 case IDM_TimeControl:
\r
5103 TimeControlOptionsPopup(hwnd);
\r
5106 case IDM_SaveSettings:
\r
5107 SaveSettings(settingsFileName);
\r
5110 case IDM_SaveSettingsOnExit:
\r
5111 saveSettingsOnExit = !saveSettingsOnExit;
\r
5112 (void) CheckMenuItem(GetMenu(hwndMain), IDM_SaveSettingsOnExit,
\r
5113 MF_BYCOMMAND|(saveSettingsOnExit ?
\r
5114 MF_CHECKED : MF_UNCHECKED));
\r
5125 case IDM_AboutGame:
\r
5130 appData.debugMode = !appData.debugMode;
\r
5131 if (appData.debugMode) {
\r
5132 char dir[MSG_SIZ];
\r
5133 GetCurrentDirectory(MSG_SIZ, dir);
\r
5134 SetCurrentDirectory(installDir);
\r
5135 debugFP = fopen(appData.nameOfDebugFile, "w");
\r
5136 SetCurrentDirectory(dir);
\r
5137 setbuf(debugFP, NULL);
\r
5144 case IDM_HELPCONTENTS:
\r
5145 if (!MyHelp (hwnd, "winboard.hlp", HELP_KEY,(DWORD)(LPSTR)"CONTENTS") &&
\r
5146 !HtmlHelp(hwnd, "winboard.chm", 0, 0) ) {
\r
5147 MessageBox (GetFocus(),
\r
5148 _("Unable to activate help"),
\r
5149 szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);
\r
5153 case IDM_HELPSEARCH:
\r
5154 if (!MyHelp (hwnd, "winboard.hlp", HELP_PARTIALKEY, (DWORD)(LPSTR)"") &&
\r
5155 !HtmlHelp(hwnd, "winboard.chm", 0, 0) ) {
\r
5156 MessageBox (GetFocus(),
\r
5157 _("Unable to activate help"),
\r
5158 szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);
\r
5162 case IDM_HELPHELP:
\r
5163 if(!WinHelp(hwnd, (LPSTR)NULL, HELP_HELPONHELP, 0)) {
\r
5164 MessageBox (GetFocus(),
\r
5165 _("Unable to activate help"),
\r
5166 szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);
\r
5171 lpProc = MakeProcInstance((FARPROC)About, hInst);
\r
5173 (gameInfo.event && strcmp(gameInfo.event, "Easter Egg Hunt") == 0) ?
\r
5174 "AboutBox2" : "AboutBox", hwnd, (DLGPROC)lpProc);
\r
5175 FreeProcInstance(lpProc);
\r
5178 case IDM_DirectCommand1:
\r
5179 AskQuestionEvent(_("Direct Command"),
\r
5180 _("Send to chess program:"), "", "1");
\r
5182 case IDM_DirectCommand2:
\r
5183 AskQuestionEvent(_("Direct Command"),
\r
5184 _("Send to second chess program:"), "", "2");
\r
5187 case EP_WhitePawn:
\r
5188 EditPositionMenuEvent(WhitePawn, fromX, fromY);
\r
5189 fromX = fromY = -1;
\r
5192 case EP_WhiteKnight:
\r
5193 EditPositionMenuEvent(WhiteKnight, fromX, fromY);
\r
5194 fromX = fromY = -1;
\r
5197 case EP_WhiteBishop:
\r
5198 EditPositionMenuEvent(WhiteBishop, fromX, fromY);
\r
5199 fromX = fromY = -1;
\r
5202 case EP_WhiteRook:
\r
5203 EditPositionMenuEvent(WhiteRook, fromX, fromY);
\r
5204 fromX = fromY = -1;
\r
5207 case EP_WhiteQueen:
\r
5208 EditPositionMenuEvent(WhiteQueen, fromX, fromY);
\r
5209 fromX = fromY = -1;
\r
5212 case EP_WhiteFerz:
\r
5213 EditPositionMenuEvent(WhiteFerz, fromX, fromY);
\r
5214 fromX = fromY = -1;
\r
5217 case EP_WhiteWazir:
\r
5218 EditPositionMenuEvent(WhiteWazir, fromX, fromY);
\r
5219 fromX = fromY = -1;
\r
5222 case EP_WhiteAlfil:
\r
5223 EditPositionMenuEvent(WhiteAlfil, fromX, fromY);
\r
5224 fromX = fromY = -1;
\r
5227 case EP_WhiteCannon:
\r
5228 EditPositionMenuEvent(WhiteCannon, fromX, fromY);
\r
5229 fromX = fromY = -1;
\r
5232 case EP_WhiteCardinal:
\r
5233 EditPositionMenuEvent(WhiteAngel, fromX, fromY);
\r
5234 fromX = fromY = -1;
\r
5237 case EP_WhiteMarshall:
\r
5238 EditPositionMenuEvent(WhiteMarshall, fromX, fromY);
\r
5239 fromX = fromY = -1;
\r
5242 case EP_WhiteKing:
\r
5243 EditPositionMenuEvent(WhiteKing, fromX, fromY);
\r
5244 fromX = fromY = -1;
\r
5247 case EP_BlackPawn:
\r
5248 EditPositionMenuEvent(BlackPawn, fromX, fromY);
\r
5249 fromX = fromY = -1;
\r
5252 case EP_BlackKnight:
\r
5253 EditPositionMenuEvent(BlackKnight, fromX, fromY);
\r
5254 fromX = fromY = -1;
\r
5257 case EP_BlackBishop:
\r
5258 EditPositionMenuEvent(BlackBishop, fromX, fromY);
\r
5259 fromX = fromY = -1;
\r
5262 case EP_BlackRook:
\r
5263 EditPositionMenuEvent(BlackRook, fromX, fromY);
\r
5264 fromX = fromY = -1;
\r
5267 case EP_BlackQueen:
\r
5268 EditPositionMenuEvent(BlackQueen, fromX, fromY);
\r
5269 fromX = fromY = -1;
\r
5272 case EP_BlackFerz:
\r
5273 EditPositionMenuEvent(BlackFerz, fromX, fromY);
\r
5274 fromX = fromY = -1;
\r
5277 case EP_BlackWazir:
\r
5278 EditPositionMenuEvent(BlackWazir, fromX, fromY);
\r
5279 fromX = fromY = -1;
\r
5282 case EP_BlackAlfil:
\r
5283 EditPositionMenuEvent(BlackAlfil, fromX, fromY);
\r
5284 fromX = fromY = -1;
\r
5287 case EP_BlackCannon:
\r
5288 EditPositionMenuEvent(BlackCannon, fromX, fromY);
\r
5289 fromX = fromY = -1;
\r
5292 case EP_BlackCardinal:
\r
5293 EditPositionMenuEvent(BlackAngel, fromX, fromY);
\r
5294 fromX = fromY = -1;
\r
5297 case EP_BlackMarshall:
\r
5298 EditPositionMenuEvent(BlackMarshall, fromX, fromY);
\r
5299 fromX = fromY = -1;
\r
5302 case EP_BlackKing:
\r
5303 EditPositionMenuEvent(BlackKing, fromX, fromY);
\r
5304 fromX = fromY = -1;
\r
5307 case EP_EmptySquare:
\r
5308 EditPositionMenuEvent(EmptySquare, fromX, fromY);
\r
5309 fromX = fromY = -1;
\r
5312 case EP_ClearBoard:
\r
5313 EditPositionMenuEvent(ClearBoard, fromX, fromY);
\r
5314 fromX = fromY = -1;
\r
5318 EditPositionMenuEvent(WhitePlay, fromX, fromY);
\r
5319 fromX = fromY = -1;
\r
5323 EditPositionMenuEvent(BlackPlay, fromX, fromY);
\r
5324 fromX = fromY = -1;
\r
5328 EditPositionMenuEvent(PromotePiece, fromX, fromY);
\r
5329 fromX = fromY = -1;
\r
5333 EditPositionMenuEvent(DemotePiece, fromX, fromY);
\r
5334 fromX = fromY = -1;
\r
5338 DropMenuEvent(WhitePawn, fromX, fromY);
\r
5339 fromX = fromY = -1;
\r
5343 DropMenuEvent(WhiteKnight, fromX, fromY);
\r
5344 fromX = fromY = -1;
\r
5348 DropMenuEvent(WhiteBishop, fromX, fromY);
\r
5349 fromX = fromY = -1;
\r
5353 DropMenuEvent(WhiteRook, fromX, fromY);
\r
5354 fromX = fromY = -1;
\r
5358 DropMenuEvent(WhiteQueen, fromX, fromY);
\r
5359 fromX = fromY = -1;
\r
5364 TranslateMenus(0);
\r
5365 CheckMenuItem(GetMenu(hwndMain), lastChecked, MF_BYCOMMAND|MF_UNCHECKED);
\r
5366 CheckMenuItem(GetMenu(hwndMain), IDM_English, MF_BYCOMMAND|MF_CHECKED);
\r
5367 lastChecked = wmId;
\r
5371 if(wmId > IDM_English && wmId < IDM_English+5) {
\r
5372 LoadLanguageFile(languageFile[wmId - IDM_English - 1]);
\r
5373 TranslateMenus(0);
\r
5374 CheckMenuItem(GetMenu(hwndMain), lastChecked, MF_BYCOMMAND|MF_UNCHECKED);
\r
5375 CheckMenuItem(GetMenu(hwndMain), wmId, MF_BYCOMMAND|MF_CHECKED);
\r
5376 lastChecked = wmId;
\r
5379 return (DefWindowProc(hwnd, message, wParam, lParam));
\r
5385 case CLOCK_TIMER_ID:
\r
5386 KillTimer(hwnd, clockTimerEvent); /* Simulate one-shot timer as in X */
\r
5387 clockTimerEvent = 0;
\r
5388 DecrementClocks(); /* call into back end */
\r
5390 case LOAD_GAME_TIMER_ID:
\r
5391 KillTimer(hwnd, loadGameTimerEvent); /* Simulate one-shot timer as in X*/
\r
5392 loadGameTimerEvent = 0;
\r
5393 AutoPlayGameLoop(); /* call into back end */
\r
5395 case ANALYSIS_TIMER_ID:
\r
5396 if ((gameMode == AnalyzeMode || gameMode == AnalyzeFile
\r
5397 || appData.icsEngineAnalyze) && appData.periodicUpdates) {
\r
5398 AnalysisPeriodicEvent(0);
\r
5400 KillTimer(hwnd, analysisTimerEvent);
\r
5401 analysisTimerEvent = 0;
\r
5404 case DELAYED_TIMER_ID:
\r
5405 KillTimer(hwnd, delayedTimerEvent);
\r
5406 delayedTimerEvent = 0;
\r
5407 delayedTimerCallback();
\r
5412 case WM_USER_Input:
\r
5413 InputEvent(hwnd, message, wParam, lParam);
\r
5416 /* [AS] Also move "attached" child windows */
\r
5417 case WM_WINDOWPOSCHANGING:
\r
5419 if( hwnd == hwndMain && appData.useStickyWindows ) {
\r
5420 LPWINDOWPOS lpwp = (LPWINDOWPOS) lParam;
\r
5422 if( ((lpwp->flags & SWP_NOMOVE) == 0) && ((lpwp->flags & SWP_NOSIZE) != 0) ) {
\r
5423 /* Window is moving */
\r
5426 // GetWindowRect( hwnd, &rcMain ); //[HGM] sticky: in XP this returned new position, not old
\r
5427 rcMain.left = wpMain.x; // replace by these 4 lines to reconstruct old rect
\r
5428 rcMain.right = wpMain.x + wpMain.width;
\r
5429 rcMain.top = wpMain.y;
\r
5430 rcMain.bottom = wpMain.y + wpMain.height;
\r
5432 ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, moveHistoryDialog, &wpMoveHistory );
\r
5433 ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, evalGraphDialog, &wpEvalGraph );
\r
5434 ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, engineOutputDialog, &wpEngineOutput );
\r
5435 ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, gameListDialog, &wpGameList );
\r
5436 ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, hwndConsole, &wpConsole );
\r
5437 wpMain.x = lpwp->x;
\r
5438 wpMain.y = lpwp->y;
\r
5443 /* [AS] Snapping */
\r
5444 case WM_ENTERSIZEMOVE:
\r
5445 if(appData.debugMode) { fprintf(debugFP, "size-move\n"); }
\r
5446 if (hwnd == hwndMain) {
\r
5447 doingSizing = TRUE;
\r
5450 return OnEnterSizeMove( &sd, hwnd, wParam, lParam );
\r
5454 if(appData.debugMode) { fprintf(debugFP, "sizing\n"); }
\r
5455 if (hwnd == hwndMain) {
\r
5456 lastSizing = wParam;
\r
5461 if(appData.debugMode) { fprintf(debugFP, "moving\n"); }
\r
5462 return OnMoving( &sd, hwnd, wParam, lParam );
\r
5464 case WM_EXITSIZEMOVE:
\r
5465 if(appData.debugMode) { fprintf(debugFP, "exit size-move, size = %d\n", squareSize); }
\r
5466 if (hwnd == hwndMain) {
\r
5468 doingSizing = FALSE;
\r
5469 InvalidateRect(hwnd, &boardRect, FALSE);
\r
5470 GetClientRect(hwnd, &client);
\r
5471 ResizeBoard(client.right, client.bottom, lastSizing);
\r
5473 if(appData.debugMode) { fprintf(debugFP, "square size = %d\n", squareSize); }
\r
5475 return OnExitSizeMove( &sd, hwnd, wParam, lParam );
\r
5478 case WM_DESTROY: /* message: window being destroyed */
\r
5479 PostQuitMessage(0);
\r
5483 if (hwnd == hwndMain) {
\r
5488 default: /* Passes it on if unprocessed */
\r
5489 return (DefWindowProc(hwnd, message, wParam, lParam));
\r
5494 /*---------------------------------------------------------------------------*\
\r
5496 * Misc utility routines
\r
5498 \*---------------------------------------------------------------------------*/
\r
5501 * Decent random number generator, at least not as bad as Windows
\r
5502 * standard rand, which returns a value in the range 0 to 0x7fff.
\r
5504 unsigned int randstate;
\r
5509 randstate = randstate * 1664525 + 1013904223;
\r
5510 return (int) randstate & 0x7fffffff;
\r
5514 mysrandom(unsigned int seed)
\r
5521 * returns TRUE if user selects a different color, FALSE otherwise
\r
5525 ChangeColor(HWND hwnd, COLORREF *which)
\r
5527 static BOOL firstTime = TRUE;
\r
5528 static DWORD customColors[16];
\r
5530 COLORREF newcolor;
\r
5535 /* Make initial colors in use available as custom colors */
\r
5536 /* Should we put the compiled-in defaults here instead? */
\r
5538 customColors[i++] = lightSquareColor & 0xffffff;
\r
5539 customColors[i++] = darkSquareColor & 0xffffff;
\r
5540 customColors[i++] = whitePieceColor & 0xffffff;
\r
5541 customColors[i++] = blackPieceColor & 0xffffff;
\r
5542 customColors[i++] = highlightSquareColor & 0xffffff;
\r
5543 customColors[i++] = premoveHighlightColor & 0xffffff;
\r
5545 for (ccl = (ColorClass) 0; ccl < NColorClasses && i < 16; ccl++) {
\r
5546 customColors[i++] = textAttribs[ccl].color;
\r
5548 while (i < 16) customColors[i++] = RGB(255, 255, 255);
\r
5549 firstTime = FALSE;
\r
5552 cc.lStructSize = sizeof(cc);
\r
5553 cc.hwndOwner = hwnd;
\r
5554 cc.hInstance = NULL;
\r
5555 cc.rgbResult = (DWORD) (*which & 0xffffff);
\r
5556 cc.lpCustColors = (LPDWORD) customColors;
\r
5557 cc.Flags = CC_RGBINIT|CC_FULLOPEN;
\r
5559 if (!ChooseColor(&cc)) return FALSE;
\r
5561 newcolor = (COLORREF) (0x2000000 | cc.rgbResult);
\r
5562 if (newcolor == *which) return FALSE;
\r
5563 *which = newcolor;
\r
5567 InitDrawingColors();
\r
5568 InvalidateRect(hwnd, &boardRect, FALSE);
\r
5573 MyLoadSound(MySound *ms)
\r
5579 if (ms->data) free(ms->data);
\r
5582 switch (ms->name[0]) {
\r
5588 /* System sound from Control Panel. Don't preload here. */
\r
5592 if (ms->name[1] == NULLCHAR) {
\r
5593 /* "!" alone = silence */
\r
5596 /* Builtin wave resource. Error if not found. */
\r
5597 HANDLE h = FindResource(hInst, ms->name + 1, "WAVE");
\r
5598 if (h == NULL) break;
\r
5599 ms->data = (void *)LoadResource(hInst, h);
\r
5600 if (h == NULL) break;
\r
5605 /* .wav file. Error if not found. */
\r
5606 f = fopen(ms->name, "rb");
\r
5607 if (f == NULL) break;
\r
5608 if (fstat(fileno(f), &st) < 0) break;
\r
5609 ms->data = malloc(st.st_size);
\r
5610 if (fread(ms->data, st.st_size, 1, f) < 1) break;
\r
5616 char buf[MSG_SIZ];
\r
5617 snprintf(buf, MSG_SIZ, _("Error loading sound %s"), ms->name);
\r
5618 DisplayError(buf, GetLastError());
\r
5624 MyPlaySound(MySound *ms)
\r
5626 BOOLEAN ok = FALSE;
\r
5628 if(mute) return TRUE; // [HGM] mute: suppress all sound play when muted
\r
5629 switch (ms->name[0]) {
\r
5631 if(appData.debugMode) fprintf(debugFP, "silence\n");
\r
5636 /* System sound from Control Panel (deprecated feature).
\r
5637 "$" alone or an unset sound name gets default beep (still in use). */
\r
5638 if (ms->name[1]) {
\r
5639 ok = PlaySound(ms->name + 1, NULL, SND_ALIAS|SND_ASYNC);
\r
5641 if (!ok) ok = MessageBeep(MB_OK);
\r
5644 /* Builtin wave resource, or "!" alone for silence */
\r
5645 if (ms->name[1]) {
\r
5646 if (ms->data == NULL) return FALSE;
\r
5647 ok = PlaySound(ms->data, NULL, SND_MEMORY|SND_ASYNC);
\r
5653 /* .wav file. Error if not found. */
\r
5654 if (ms->data == NULL) return FALSE;
\r
5655 ok = PlaySound(ms->data, NULL, SND_MEMORY|SND_ASYNC);
\r
5658 /* Don't print an error: this can happen innocently if the sound driver
\r
5659 is busy; for instance, if another instance of WinBoard is playing
\r
5660 a sound at about the same time. */
\r
5666 OldOpenFileHook(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
5669 OPENFILENAME *ofn;
\r
5670 static UINT *number; /* gross that this is static */
\r
5672 switch (message) {
\r
5673 case WM_INITDIALOG: /* message: initialize dialog box */
\r
5674 /* Center the dialog over the application window */
\r
5675 ofn = (OPENFILENAME *) lParam;
\r
5676 if (ofn->Flags & OFN_ENABLETEMPLATE) {
\r
5677 number = (UINT *) ofn->lCustData;
\r
5678 SendMessage(GetDlgItem(hDlg, edt2), WM_SETTEXT, 0, (LPARAM) "");
\r
5682 CenterWindow (hDlg, GetWindow (hDlg, GW_OWNER));
\r
5683 Translate(hDlg, 1536);
\r
5684 return FALSE; /* Allow for further processing */
\r
5687 if ((LOWORD(wParam) == IDOK) && (number != NULL)) {
\r
5688 *number = GetDlgItemInt(hDlg, OPT_IndexNumberOld, &ok, FALSE);
\r
5690 return FALSE; /* Allow for further processing */
\r
5696 OpenFileHook(HWND hdlg, UINT uiMsg, WPARAM wParam, LPARAM lParam)
\r
5698 static UINT *number;
\r
5699 OPENFILENAME *ofname;
\r
5702 case WM_INITDIALOG:
\r
5703 Translate(hdlg, DLG_IndexNumber);
\r
5704 ofname = (OPENFILENAME *)lParam;
\r
5705 number = (UINT *)(ofname->lCustData);
\r
5708 ofnot = (OFNOTIFY *)lParam;
\r
5709 if (ofnot->hdr.code == CDN_FILEOK) {
\r
5710 *number = GetDlgItemInt(hdlg, OPT_IndexNumber, NULL, FALSE);
\r
5719 OpenFileDialog(HWND hwnd, char *write, char *defName, char *defExt, // [HGM] diag: type of 'write' now string
\r
5720 char *nameFilt, char *dlgTitle, UINT *number,
\r
5721 char fileTitle[MSG_SIZ], char fileName[MSG_SIZ])
\r
5723 OPENFILENAME openFileName;
\r
5724 char buf1[MSG_SIZ];
\r
5727 if (fileName == NULL) fileName = buf1;
\r
5728 if (defName == NULL) {
\r
5729 safeStrCpy(fileName, "*.", 3 );
\r
5730 strcat(fileName, defExt);
\r
5732 safeStrCpy(fileName, defName, MSG_SIZ );
\r
5734 if (fileTitle) safeStrCpy(fileTitle, "", MSG_SIZ );
\r
5735 if (number) *number = 0;
\r
5737 openFileName.lStructSize = sizeof(OPENFILENAME);
\r
5738 openFileName.hwndOwner = hwnd;
\r
5739 openFileName.hInstance = (HANDLE) hInst;
\r
5740 openFileName.lpstrFilter = nameFilt;
\r
5741 openFileName.lpstrCustomFilter = (LPSTR) NULL;
\r
5742 openFileName.nMaxCustFilter = 0L;
\r
5743 openFileName.nFilterIndex = 1L;
\r
5744 openFileName.lpstrFile = fileName;
\r
5745 openFileName.nMaxFile = MSG_SIZ;
\r
5746 openFileName.lpstrFileTitle = fileTitle;
\r
5747 openFileName.nMaxFileTitle = fileTitle ? MSG_SIZ : 0;
\r
5748 openFileName.lpstrInitialDir = NULL;
\r
5749 openFileName.lpstrTitle = dlgTitle;
\r
5750 openFileName.Flags = OFN_PATHMUSTEXIST | OFN_HIDEREADONLY
\r
5751 | (write[0] != 'r' ? 0 : OFN_FILEMUSTEXIST)
\r
5752 | (number ? OFN_ENABLETEMPLATE | OFN_ENABLEHOOK: 0)
\r
5753 | (oldDialog ? 0 : OFN_EXPLORER);
\r
5754 openFileName.nFileOffset = 0;
\r
5755 openFileName.nFileExtension = 0;
\r
5756 openFileName.lpstrDefExt = defExt;
\r
5757 openFileName.lCustData = (LONG) number;
\r
5758 openFileName.lpfnHook = oldDialog ?
\r
5759 (LPOFNHOOKPROC) OldOpenFileHook : (LPOFNHOOKPROC) OpenFileHook;
\r
5760 openFileName.lpTemplateName = (LPSTR)(oldDialog ? 1536 : DLG_IndexNumber);
\r
5762 if (write[0] != 'r' ? GetSaveFileName(&openFileName) :
\r
5763 GetOpenFileName(&openFileName)) {
\r
5764 /* open the file */
\r
5765 f = fopen(openFileName.lpstrFile, write);
\r
5767 MessageBox(hwnd, _("File open failed"), NULL,
\r
5768 MB_OK|MB_ICONEXCLAMATION);
\r
5772 int err = CommDlgExtendedError();
\r
5773 if (err != 0) DisplayError(_("Internal error in file dialog box"), err);
\r
5782 MenuPopup(HWND hwnd, POINT pt, HMENU hmenu, UINT def)
\r
5784 HMENU hmenuTrackPopup; /* floating pop-up menu */
\r
5787 * Get the first pop-up menu in the menu template. This is the
\r
5788 * menu that TrackPopupMenu displays.
\r
5790 hmenuTrackPopup = GetSubMenu(hmenu, 0);
\r
5792 SetMenuDefaultItem(hmenuTrackPopup, def, FALSE);
\r
5795 * TrackPopup uses screen coordinates, so convert the
\r
5796 * coordinates of the mouse click to screen coordinates.
\r
5798 ClientToScreen(hwnd, (LPPOINT) &pt);
\r
5800 /* Draw and track the floating pop-up menu. */
\r
5801 TrackPopupMenu(hmenuTrackPopup, TPM_CENTERALIGN | TPM_RIGHTBUTTON,
\r
5802 pt.x, pt.y, 0, hwnd, NULL);
\r
5804 /* Destroy the menu.*/
\r
5805 DestroyMenu(hmenu);
\r
5810 int sizeX, sizeY, newSizeX, newSizeY;
\r
5812 } ResizeEditPlusButtonsClosure;
\r
5815 ResizeEditPlusButtonsCallback(HWND hChild, LPARAM lparam)
\r
5817 ResizeEditPlusButtonsClosure *cl = (ResizeEditPlusButtonsClosure *)lparam;
\r
5821 if (hChild == cl->hText) return TRUE;
\r
5822 GetWindowRect(hChild, &rect); /* gives screen coords */
\r
5823 pt.x = rect.left + (cl->newSizeX - cl->sizeX)/2;
\r
5824 pt.y = rect.top + cl->newSizeY - cl->sizeY;
\r
5825 ScreenToClient(cl->hDlg, &pt);
\r
5826 cl->hdwp = DeferWindowPos(cl->hdwp, hChild, NULL,
\r
5827 pt.x, pt.y, rect.right - rect.left, rect.bottom - rect.top, SWP_NOZORDER);
\r
5831 /* Resize a dialog that has a (rich) edit field filling most of
\r
5832 the top, with a row of buttons below */
\r
5834 ResizeEditPlusButtons(HWND hDlg, HWND hText, int sizeX, int sizeY, int newSizeX, int newSizeY)
\r
5837 int newTextHeight, newTextWidth;
\r
5838 ResizeEditPlusButtonsClosure cl;
\r
5840 /*if (IsIconic(hDlg)) return;*/
\r
5841 if (newSizeX == sizeX && newSizeY == sizeY) return;
\r
5843 cl.hdwp = BeginDeferWindowPos(8);
\r
5845 GetWindowRect(hText, &rectText); /* gives screen coords */
\r
5846 newTextWidth = rectText.right - rectText.left + newSizeX - sizeX;
\r
5847 newTextHeight = rectText.bottom - rectText.top + newSizeY - sizeY;
\r
5848 if (newTextHeight < 0) {
\r
5849 newSizeY += -newTextHeight;
\r
5850 newTextHeight = 0;
\r
5852 cl.hdwp = DeferWindowPos(cl.hdwp, hText, NULL, 0, 0,
\r
5853 newTextWidth, newTextHeight, SWP_NOZORDER|SWP_NOMOVE);
\r
5859 cl.newSizeX = newSizeX;
\r
5860 cl.newSizeY = newSizeY;
\r
5861 EnumChildWindows(hDlg, ResizeEditPlusButtonsCallback, (LPARAM)&cl);
\r
5863 EndDeferWindowPos(cl.hdwp);
\r
5866 BOOL CenterWindowEx(HWND hwndChild, HWND hwndParent, int mode)
\r
5868 RECT rChild, rParent;
\r
5869 int wChild, hChild, wParent, hParent;
\r
5870 int wScreen, hScreen, xNew, yNew;
\r
5873 /* Get the Height and Width of the child window */
\r
5874 GetWindowRect (hwndChild, &rChild);
\r
5875 wChild = rChild.right - rChild.left;
\r
5876 hChild = rChild.bottom - rChild.top;
\r
5878 /* Get the Height and Width of the parent window */
\r
5879 GetWindowRect (hwndParent, &rParent);
\r
5880 wParent = rParent.right - rParent.left;
\r
5881 hParent = rParent.bottom - rParent.top;
\r
5883 /* Get the display limits */
\r
5884 hdc = GetDC (hwndChild);
\r
5885 wScreen = GetDeviceCaps (hdc, HORZRES);
\r
5886 hScreen = GetDeviceCaps (hdc, VERTRES);
\r
5887 ReleaseDC(hwndChild, hdc);
\r
5889 /* Calculate new X position, then adjust for screen */
\r
5890 xNew = rParent.left + ((wParent - wChild) /2);
\r
5893 } else if ((xNew+wChild) > wScreen) {
\r
5894 xNew = wScreen - wChild;
\r
5897 /* Calculate new Y position, then adjust for screen */
\r
5899 yNew = rParent.top + ((hParent - hChild) /2);
\r
5902 yNew = rParent.top + GetSystemMetrics( SM_CYCAPTION ) * 2 / 3;
\r
5907 } else if ((yNew+hChild) > hScreen) {
\r
5908 yNew = hScreen - hChild;
\r
5911 /* Set it, and return */
\r
5912 return SetWindowPos (hwndChild, NULL,
\r
5913 xNew, yNew, 0, 0, SWP_NOSIZE | SWP_NOZORDER);
\r
5916 /* Center one window over another */
\r
5917 BOOL CenterWindow (HWND hwndChild, HWND hwndParent)
\r
5919 return CenterWindowEx( hwndChild, hwndParent, 0 );
\r
5922 /*---------------------------------------------------------------------------*\
\r
5924 * Startup Dialog functions
\r
5926 \*---------------------------------------------------------------------------*/
\r
5928 InitComboStrings(HANDLE hwndCombo, char **cd)
\r
5930 SendMessage(hwndCombo, CB_RESETCONTENT, 0, 0);
\r
5932 while (*cd != NULL) {
\r
5933 SendMessage(hwndCombo, CB_ADDSTRING, 0, (LPARAM) T_(*cd));
\r
5939 InitComboStringsFromOption(HANDLE hwndCombo, char *str)
\r
5941 char buf1[MAX_ARG_LEN];
\r
5944 if (str[0] == '@') {
\r
5945 FILE* f = fopen(str + 1, "r");
\r
5947 DisplayFatalError(str + 1, errno, 2);
\r
5950 len = fread(buf1, 1, sizeof(buf1)-1, f);
\r
5952 buf1[len] = NULLCHAR;
\r
5956 SendMessage(hwndCombo, CB_RESETCONTENT, 0, 0);
\r
5959 char buf[MSG_SIZ];
\r
5960 char *end = strchr(str, '\n');
\r
5961 if (end == NULL) return;
\r
5962 memcpy(buf, str, end - str);
\r
5963 buf[end - str] = NULLCHAR;
\r
5964 SendMessage(hwndCombo, CB_ADDSTRING, 0, (LPARAM) buf);
\r
5970 SetStartupDialogEnables(HWND hDlg)
\r
5972 EnableWindow(GetDlgItem(hDlg, OPT_ChessEngineName),
\r
5973 IsDlgButtonChecked(hDlg, OPT_ChessEngine) ||
\r
5974 (appData.zippyPlay && IsDlgButtonChecked(hDlg, OPT_ChessServer)));
\r
5975 EnableWindow(GetDlgItem(hDlg, OPT_SecondChessEngineName),
\r
5976 IsDlgButtonChecked(hDlg, OPT_ChessEngine));
\r
5977 EnableWindow(GetDlgItem(hDlg, OPT_ChessServerName),
\r
5978 IsDlgButtonChecked(hDlg, OPT_ChessServer));
\r
5979 EnableWindow(GetDlgItem(hDlg, OPT_AdditionalOptions),
\r
5980 IsDlgButtonChecked(hDlg, OPT_AnyAdditional));
\r
5981 EnableWindow(GetDlgItem(hDlg, IDOK),
\r
5982 IsDlgButtonChecked(hDlg, OPT_ChessEngine) ||
\r
5983 IsDlgButtonChecked(hDlg, OPT_ChessServer) ||
\r
5984 IsDlgButtonChecked(hDlg, OPT_View));
\r
5988 QuoteForFilename(char *filename)
\r
5990 int dquote, space;
\r
5991 dquote = strchr(filename, '"') != NULL;
\r
5992 space = strchr(filename, ' ') != NULL;
\r
5993 if (dquote || space) {
\r
6005 InitEngineBox(HWND hDlg, HWND hwndCombo, char* nthcp, char* nthd, char* nthdir, char *nthnames)
\r
6007 char buf[MSG_SIZ];
\r
6010 InitComboStringsFromOption(hwndCombo, nthnames);
\r
6011 q = QuoteForFilename(nthcp);
\r
6012 snprintf(buf, MSG_SIZ, "%s%s%s", q, nthcp, q);
\r
6013 if (*nthdir != NULLCHAR) {
\r
6014 q = QuoteForFilename(nthdir);
\r
6015 snprintf(buf + strlen(buf), MSG_SIZ, " /%s=%s%s%s", nthd, q, nthdir, q);
\r
6017 if (*nthcp == NULLCHAR) {
\r
6018 SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) 0, (LPARAM) 0);
\r
6019 } else if (SendMessage(hwndCombo, CB_SELECTSTRING, (WPARAM) -1, (LPARAM) buf) == CB_ERR) {
\r
6020 SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) -1, (LPARAM) 0);
\r
6021 SendMessage(hwndCombo, WM_SETTEXT, (WPARAM) 0, (LPARAM) buf);
\r
6026 StartupDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
6028 char buf[MSG_SIZ];
\r
6032 switch (message) {
\r
6033 case WM_INITDIALOG:
\r
6034 /* Center the dialog */
\r
6035 CenterWindow (hDlg, GetDesktopWindow());
\r
6036 Translate(hDlg, DLG_Startup);
\r
6037 /* Initialize the dialog items */
\r
6038 InitEngineBox(hDlg, GetDlgItem(hDlg, OPT_ChessEngineName),
\r
6039 appData.firstChessProgram, "fd", appData.firstDirectory,
\r
6040 firstChessProgramNames);
\r
6041 InitEngineBox(hDlg, GetDlgItem(hDlg, OPT_SecondChessEngineName),
\r
6042 appData.secondChessProgram, "sd", appData.secondDirectory,
\r
6043 secondChessProgramNames);
\r
6044 hwndCombo = GetDlgItem(hDlg, OPT_ChessServerName);
\r
6045 InitComboStringsFromOption(hwndCombo, icsNames);
\r
6046 snprintf(buf, MSG_SIZ, "%s /icsport=%s", appData.icsHost, appData.icsPort);
\r
6047 if (*appData.icsHelper != NULLCHAR) {
\r
6048 char *q = QuoteForFilename(appData.icsHelper);
\r
6049 sprintf(buf + strlen(buf), " /icshelper=%s%s%s", q, appData.icsHelper, q);
\r
6051 if (*appData.icsHost == NULLCHAR) {
\r
6052 SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) 0, (LPARAM) 0);
\r
6053 /*SendMessage(hwndCombo, CB_SHOWDROPDOWN, (WPARAM) TRUE, (LPARAM) 0); !!too soon */
\r
6054 } else if (SendMessage(hwndCombo, CB_SELECTSTRING, (WPARAM) -1, (LPARAM) buf) == CB_ERR) {
\r
6055 SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) -1, (LPARAM) 0);
\r
6056 SendMessage(hwndCombo, WM_SETTEXT, (WPARAM) 0, (LPARAM) buf);
\r
6059 if (appData.icsActive) {
\r
6060 CheckDlgButton(hDlg, OPT_ChessServer, BST_CHECKED);
\r
6062 else if (appData.noChessProgram) {
\r
6063 CheckDlgButton(hDlg, OPT_View, BST_CHECKED);
\r
6066 CheckDlgButton(hDlg, OPT_ChessEngine, BST_CHECKED);
\r
6069 SetStartupDialogEnables(hDlg);
\r
6073 switch (LOWORD(wParam)) {
\r
6075 if (IsDlgButtonChecked(hDlg, OPT_ChessEngine)) {
\r
6076 safeStrCpy(buf, "/fcp=", sizeof(buf)/sizeof(buf[0]) );
\r
6077 GetDlgItemText(hDlg, OPT_ChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));
\r
6079 ParseArgs(StringGet, &p);
\r
6080 safeStrCpy(buf, "/scp=", sizeof(buf)/sizeof(buf[0]) );
\r
6081 GetDlgItemText(hDlg, OPT_SecondChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));
\r
6083 ParseArgs(StringGet, &p);
\r
6084 appData.noChessProgram = FALSE;
\r
6085 appData.icsActive = FALSE;
\r
6086 } else if (IsDlgButtonChecked(hDlg, OPT_ChessServer)) {
\r
6087 safeStrCpy(buf, "/ics /icshost=", sizeof(buf)/sizeof(buf[0]) );
\r
6088 GetDlgItemText(hDlg, OPT_ChessServerName, buf + strlen(buf), sizeof(buf) - strlen(buf));
\r
6090 ParseArgs(StringGet, &p);
\r
6091 if (appData.zippyPlay) {
\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
6097 } else if (IsDlgButtonChecked(hDlg, OPT_View)) {
\r
6098 appData.noChessProgram = TRUE;
\r
6099 appData.icsActive = FALSE;
\r
6101 MessageBox(hDlg, _("Choose an option, or cancel to exit"),
\r
6102 _("Option Error"), MB_OK|MB_ICONEXCLAMATION);
\r
6105 if (IsDlgButtonChecked(hDlg, OPT_AnyAdditional)) {
\r
6106 GetDlgItemText(hDlg, OPT_AdditionalOptions, buf, sizeof(buf));
\r
6108 ParseArgs(StringGet, &p);
\r
6110 EndDialog(hDlg, TRUE);
\r
6117 case IDM_HELPCONTENTS:
\r
6118 if (!WinHelp (hDlg, "winboard.hlp", HELP_KEY,(DWORD)(LPSTR)"CONTENTS")) {
\r
6119 MessageBox (GetFocus(),
\r
6120 _("Unable to activate help"),
\r
6121 szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);
\r
6126 SetStartupDialogEnables(hDlg);
\r
6134 /*---------------------------------------------------------------------------*\
\r
6136 * About box dialog functions
\r
6138 \*---------------------------------------------------------------------------*/
\r
6140 /* Process messages for "About" dialog box */
\r
6142 About(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
6144 switch (message) {
\r
6145 case WM_INITDIALOG: /* message: initialize dialog box */
\r
6146 /* Center the dialog over the application window */
\r
6147 CenterWindow (hDlg, GetWindow (hDlg, GW_OWNER));
\r
6148 SetDlgItemText(hDlg, ABOUTBOX_Version, programVersion);
\r
6149 Translate(hDlg, ABOUTBOX);
\r
6153 case WM_COMMAND: /* message: received a command */
\r
6154 if (LOWORD(wParam) == IDOK /* "OK" box selected? */
\r
6155 || LOWORD(wParam) == IDCANCEL) { /* System menu close command? */
\r
6156 EndDialog(hDlg, TRUE); /* Exit the dialog */
\r
6164 /*---------------------------------------------------------------------------*\
\r
6166 * Comment Dialog functions
\r
6168 \*---------------------------------------------------------------------------*/
\r
6171 CommentDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
6173 static HANDLE hwndText = NULL;
\r
6174 int len, newSizeX, newSizeY, flags;
\r
6175 static int sizeX, sizeY;
\r
6180 switch (message) {
\r
6181 case WM_INITDIALOG: /* message: initialize dialog box */
\r
6182 /* Initialize the dialog items */
\r
6183 Translate(hDlg, DLG_EditComment);
\r
6184 hwndText = GetDlgItem(hDlg, OPT_CommentText);
\r
6185 SetDlgItemText(hDlg, OPT_CommentText, commentText);
\r
6186 EnableWindow(GetDlgItem(hDlg, OPT_CancelComment), editComment);
\r
6187 EnableWindow(GetDlgItem(hDlg, OPT_ClearComment), editComment);
\r
6188 EnableWindow(GetDlgItem(hDlg, OPT_EditComment), !editComment);
\r
6189 SendMessage(hwndText, EM_SETREADONLY, !editComment, 0);
\r
6190 SetWindowText(hDlg, commentTitle);
\r
6191 if (editComment) {
\r
6192 SetFocus(hwndText);
\r
6194 SetFocus(GetDlgItem(hDlg, IDOK));
\r
6196 SendMessage(GetDlgItem(hDlg, OPT_CommentText),
\r
6197 WM_SETFONT, (WPARAM)font[boardSize][COMMENT_FONT]->hf,
\r
6198 MAKELPARAM(FALSE, 0));
\r
6199 /* Size and position the dialog */
\r
6200 if (!commentDialog) {
\r
6201 commentDialog = hDlg;
\r
6202 flags = SWP_NOZORDER;
\r
6203 GetClientRect(hDlg, &rect);
\r
6204 sizeX = rect.right;
\r
6205 sizeY = rect.bottom;
\r
6206 if (wpComment.x != CW_USEDEFAULT && wpComment.y != CW_USEDEFAULT &&
\r
6207 wpComment.width != CW_USEDEFAULT && wpComment.height != CW_USEDEFAULT) {
\r
6208 WINDOWPLACEMENT wp;
\r
6209 EnsureOnScreen(&wpComment.x, &wpComment.y, 0, 0);
\r
6210 wp.length = sizeof(WINDOWPLACEMENT);
\r
6212 wp.showCmd = SW_SHOW;
\r
6213 wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;
\r
6214 wp.rcNormalPosition.left = wpComment.x;
\r
6215 wp.rcNormalPosition.right = wpComment.x + wpComment.width;
\r
6216 wp.rcNormalPosition.top = wpComment.y;
\r
6217 wp.rcNormalPosition.bottom = wpComment.y + wpComment.height;
\r
6218 SetWindowPlacement(hDlg, &wp);
\r
6220 GetClientRect(hDlg, &rect);
\r
6221 newSizeX = rect.right;
\r
6222 newSizeY = rect.bottom;
\r
6223 ResizeEditPlusButtons(hDlg, hwndText, sizeX, sizeY,
\r
6224 newSizeX, newSizeY);
\r
6229 SendDlgItemMessage( hDlg, OPT_CommentText, EM_SETEVENTMASK, 0, ENM_MOUSEEVENTS );
\r
6232 case WM_COMMAND: /* message: received a command */
\r
6233 switch (LOWORD(wParam)) {
\r
6235 if (editComment) {
\r
6237 /* Read changed options from the dialog box */
\r
6238 hwndText = GetDlgItem(hDlg, OPT_CommentText);
\r
6239 len = GetWindowTextLength(hwndText);
\r
6240 str = (char *) malloc(len + 1);
\r
6241 GetWindowText(hwndText, str, len + 1);
\r
6250 ReplaceComment(commentIndex, str);
\r
6257 case OPT_CancelComment:
\r
6261 case OPT_ClearComment:
\r
6262 SetDlgItemText(hDlg, OPT_CommentText, "");
\r
6265 case OPT_EditComment:
\r
6266 EditCommentEvent();
\r
6274 case WM_NOTIFY: // [HGM] vari: cloned from whistory.c
\r
6275 if( wParam == OPT_CommentText ) {
\r
6276 MSGFILTER * lpMF = (MSGFILTER *) lParam;
\r
6278 if( lpMF->msg == WM_RBUTTONDOWN && (lpMF->wParam & (MK_CONTROL | MK_SHIFT)) == 0 ) {
\r
6282 pt.x = LOWORD( lpMF->lParam );
\r
6283 pt.y = HIWORD( lpMF->lParam );
\r
6285 index = SendDlgItemMessage( hDlg, OPT_CommentText, EM_CHARFROMPOS, 0, (LPARAM) &pt );
\r
6287 hwndText = GetDlgItem(hDlg, OPT_CommentText); // cloned from above
\r
6288 len = GetWindowTextLength(hwndText);
\r
6289 str = (char *) malloc(len + 1);
\r
6290 GetWindowText(hwndText, str, len + 1);
\r
6291 ReplaceComment(commentIndex, str);
\r
6292 if(commentIndex != currentMove) ToNrEvent(commentIndex);
\r
6293 LoadVariation( index, str ); // [HGM] also does the actual moving to it, now
\r
6296 /* Zap the message for good: apparently, returning non-zero is not enough */
\r
6297 lpMF->msg = WM_USER;
\r
6305 newSizeX = LOWORD(lParam);
\r
6306 newSizeY = HIWORD(lParam);
\r
6307 ResizeEditPlusButtons(hDlg, hwndText, sizeX, sizeY, newSizeX, newSizeY);
\r
6312 case WM_GETMINMAXINFO:
\r
6313 /* Prevent resizing window too small */
\r
6314 mmi = (MINMAXINFO *) lParam;
\r
6315 mmi->ptMinTrackSize.x = 100;
\r
6316 mmi->ptMinTrackSize.y = 100;
\r
6323 EitherCommentPopUp(int index, char *title, char *str, BOOLEAN edit)
\r
6328 CheckMenuItem(GetMenu(hwndMain), IDM_Comment, edit ? MF_CHECKED : MF_UNCHECKED);
\r
6330 if (str == NULL) str = "";
\r
6331 p = (char *) malloc(2 * strlen(str) + 2);
\r
6334 if (*str == '\n') *q++ = '\r';
\r
6338 if (commentText != NULL) free(commentText);
\r
6340 commentIndex = index;
\r
6341 commentTitle = title;
\r
6343 editComment = edit;
\r
6345 if (commentDialog) {
\r
6346 SendMessage(commentDialog, WM_INITDIALOG, 0, 0);
\r
6347 if (!commentUp) ShowWindow(commentDialog, SW_SHOW);
\r
6349 lpProc = MakeProcInstance((FARPROC)CommentDialog, hInst);
\r
6350 CreateDialog(hInst, MAKEINTRESOURCE(DLG_EditComment),
\r
6351 hwndMain, (DLGPROC)lpProc);
\r
6352 FreeProcInstance(lpProc);
\r
6358 /*---------------------------------------------------------------------------*\
\r
6360 * Type-in move dialog functions
\r
6362 \*---------------------------------------------------------------------------*/
\r
6365 TypeInMoveDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
6367 char move[MSG_SIZ];
\r
6369 ChessMove moveType;
\r
6370 int fromX, fromY, toX, toY;
\r
6373 switch (message) {
\r
6374 case WM_INITDIALOG:
\r
6375 move[0] = (char) lParam;
\r
6376 move[1] = NULLCHAR;
\r
6377 CenterWindowEx(hDlg, GetWindow(hDlg, GW_OWNER), 1 );
\r
6378 Translate(hDlg, DLG_TypeInMove);
\r
6379 hInput = GetDlgItem(hDlg, OPT_Move);
\r
6380 SetWindowText(hInput, move);
\r
6382 SendMessage(hInput, EM_SETSEL, (WPARAM)9999, (LPARAM)9999);
\r
6386 switch (LOWORD(wParam)) {
\r
6388 shiftKey = GetKeyState(VK_SHIFT) < 0; // [HGM] remember last shift status
\r
6389 GetDlgItemText(hDlg, OPT_Move, move, sizeof(move));
\r
6390 { int n; Board board;
\r
6392 if(gameMode == EditPosition && ParseFEN(board, &n, move) ) {
\r
6393 EditPositionPasteFEN(move);
\r
6394 EndDialog(hDlg, TRUE);
\r
6397 // [HGM] movenum: allow move number to be typed in any mode
\r
6398 if(sscanf(move, "%d", &n) == 1 && n != 0 ) {
\r
6400 EndDialog(hDlg, TRUE);
\r
6404 if (gameMode != EditGame && currentMove != forwardMostMove &&
\r
6405 gameMode != Training) {
\r
6406 DisplayMoveError(_("Displayed move is not current"));
\r
6408 // GetDlgItemText(hDlg, OPT_Move, move, sizeof(move)); // moved upstream
\r
6409 int ok = ParseOneMove(move, gameMode == EditPosition ? blackPlaysFirst : currentMove,
\r
6410 &moveType, &fromX, &fromY, &toX, &toY, &promoChar);
\r
6411 if(!ok && move[0] >= 'a') { move[0] += 'A' - 'a'; ok = 2; } // [HGM] try also capitalized
\r
6412 if (ok==1 || ok && ParseOneMove(move, gameMode == EditPosition ? blackPlaysFirst : currentMove,
\r
6413 &moveType, &fromX, &fromY, &toX, &toY, &promoChar)) {
\r
6414 if (gameMode != Training)
\r
6415 forwardMostMove = currentMove;
\r
6416 UserMoveEvent(fromX, fromY, toX, toY, promoChar);
\r
6418 DisplayMoveError(_("Could not parse move"));
\r
6421 EndDialog(hDlg, TRUE);
\r
6424 EndDialog(hDlg, FALSE);
\r
6435 PopUpMoveDialog(char firstchar)
\r
6439 if ((gameMode == BeginningOfGame && !appData.icsActive) ||
\r
6440 gameMode == MachinePlaysWhite || gameMode == MachinePlaysBlack ||
\r
6441 gameMode == AnalyzeMode || gameMode == EditGame ||
\r
6442 gameMode == EditPosition || gameMode == IcsExamining ||
\r
6443 gameMode == IcsPlayingWhite || gameMode == IcsPlayingBlack ||
\r
6444 isdigit(firstchar) && // [HGM] movenum: allow typing in of move nr in 'passive' modes
\r
6445 ( gameMode == AnalyzeFile || gameMode == PlayFromGameFile ||
\r
6446 gameMode == IcsObserving || gameMode == TwoMachinesPlay ) ||
\r
6447 gameMode == Training) {
\r
6448 lpProc = MakeProcInstance((FARPROC)TypeInMoveDialog, hInst);
\r
6449 DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_TypeInMove),
\r
6450 hwndMain, (DLGPROC)lpProc, (LPARAM)firstchar);
\r
6451 FreeProcInstance(lpProc);
\r
6455 /*---------------------------------------------------------------------------*\
\r
6457 * Type-in name dialog functions
\r
6459 \*---------------------------------------------------------------------------*/
\r
6462 TypeInNameDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
6464 char move[MSG_SIZ];
\r
6467 switch (message) {
\r
6468 case WM_INITDIALOG:
\r
6469 move[0] = (char) lParam;
\r
6470 move[1] = NULLCHAR;
\r
6471 CenterWindowEx(hDlg, GetWindow(hDlg, GW_OWNER), 1 );
\r
6472 Translate(hDlg, DLG_TypeInName);
\r
6473 hInput = GetDlgItem(hDlg, OPT_Name);
\r
6474 SetWindowText(hInput, move);
\r
6476 SendMessage(hInput, EM_SETSEL, (WPARAM)9999, (LPARAM)9999);
\r
6480 switch (LOWORD(wParam)) {
\r
6482 GetDlgItemText(hDlg, OPT_Name, move, sizeof(move));
\r
6483 appData.userName = strdup(move);
\r
6486 if(gameMode == MachinePlaysWhite || gameMode == MachinePlaysBlack) {
\r
6487 snprintf(move, MSG_SIZ, "%s vs. %s", gameInfo.white, gameInfo.black);
\r
6488 DisplayTitle(move);
\r
6492 EndDialog(hDlg, TRUE);
\r
6495 EndDialog(hDlg, FALSE);
\r
6506 PopUpNameDialog(char firstchar)
\r
6510 lpProc = MakeProcInstance((FARPROC)TypeInNameDialog, hInst);
\r
6511 DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_TypeInName),
\r
6512 hwndMain, (DLGPROC)lpProc, (LPARAM)firstchar);
\r
6513 FreeProcInstance(lpProc);
\r
6516 /*---------------------------------------------------------------------------*\
\r
6520 \*---------------------------------------------------------------------------*/
\r
6522 /* Nonmodal error box */
\r
6523 LRESULT CALLBACK ErrorDialog(HWND hDlg, UINT message,
\r
6524 WPARAM wParam, LPARAM lParam);
\r
6527 ErrorPopUp(char *title, char *content)
\r
6531 BOOLEAN modal = hwndMain == NULL;
\r
6549 strncpy(errorTitle, title, sizeof(errorTitle));
\r
6550 errorTitle[sizeof(errorTitle) - 1] = '\0';
\r
6553 MessageBox(NULL, errorMessage, errorTitle, MB_OK|MB_ICONEXCLAMATION);
\r
6555 lpProc = MakeProcInstance((FARPROC)ErrorDialog, hInst);
\r
6556 CreateDialog(hInst, MAKEINTRESOURCE(DLG_Error),
\r
6557 hwndMain, (DLGPROC)lpProc);
\r
6558 FreeProcInstance(lpProc);
\r
6565 if (!appData.popupMoveErrors && moveErrorMessageUp) DisplayMessage("", "");
\r
6566 if (errorDialog == NULL) return;
\r
6567 DestroyWindow(errorDialog);
\r
6568 errorDialog = NULL;
\r
6569 if(errorExitStatus) ExitEvent(errorExitStatus);
\r
6573 ErrorDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
6578 switch (message) {
\r
6579 case WM_INITDIALOG:
\r
6580 GetWindowRect(hDlg, &rChild);
\r
6583 SetWindowPos(hDlg, NULL, rChild.left,
\r
6584 rChild.top + boardRect.top - (rChild.bottom - rChild.top),
\r
6585 0, 0, SWP_NOZORDER|SWP_NOSIZE);
\r
6589 [AS] It seems that the above code wants to move the dialog up in the "caption
\r
6590 area" of the main window, but it uses the dialog height as an hard-coded constant,
\r
6591 and it doesn't work when you resize the dialog.
\r
6592 For now, just give it a default position.
\r
6594 SetWindowPos(hDlg, NULL, boardRect.left+8, boardRect.top+8, 0, 0, SWP_NOZORDER|SWP_NOSIZE);
\r
6595 Translate(hDlg, DLG_Error);
\r
6597 errorDialog = hDlg;
\r
6598 SetWindowText(hDlg, errorTitle);
\r
6599 hwndText = GetDlgItem(hDlg, OPT_ErrorText);
\r
6600 SetDlgItemText(hDlg, OPT_ErrorText, errorMessage);
\r
6604 switch (LOWORD(wParam)) {
\r
6607 if (errorDialog == hDlg) errorDialog = NULL;
\r
6608 DestroyWindow(hDlg);
\r
6620 HWND gothicDialog = NULL;
\r
6623 GothicDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
6627 int height = GetSystemMetrics(SM_CYCAPTION)+GetSystemMetrics(SM_CYFRAME);
\r
6629 switch (message) {
\r
6630 case WM_INITDIALOG:
\r
6631 GetWindowRect(hDlg, &rChild);
\r
6633 SetWindowPos(hDlg, NULL, wpMain.x, wpMain.y-height, wpMain.width, height,
\r
6637 [AS] It seems that the above code wants to move the dialog up in the "caption
\r
6638 area" of the main window, but it uses the dialog height as an hard-coded constant,
\r
6639 and it doesn't work when you resize the dialog.
\r
6640 For now, just give it a default position.
\r
6642 gothicDialog = hDlg;
\r
6643 SetWindowText(hDlg, errorTitle);
\r
6644 hwndText = GetDlgItem(hDlg, OPT_ErrorText);
\r
6645 SetDlgItemText(hDlg, OPT_ErrorText, errorMessage);
\r
6649 switch (LOWORD(wParam)) {
\r
6652 if (errorDialog == hDlg) errorDialog = NULL;
\r
6653 DestroyWindow(hDlg);
\r
6665 GothicPopUp(char *title, VariantClass variant)
\r
6668 static char *lastTitle;
\r
6670 strncpy(errorTitle, title, sizeof(errorTitle));
\r
6671 errorTitle[sizeof(errorTitle) - 1] = '\0';
\r
6673 if(lastTitle != title && gothicDialog != NULL) {
\r
6674 DestroyWindow(gothicDialog);
\r
6675 gothicDialog = NULL;
\r
6677 if(variant != VariantNormal && gothicDialog == NULL) {
\r
6678 title = lastTitle;
\r
6679 lpProc = MakeProcInstance((FARPROC)GothicDialog, hInst);
\r
6680 CreateDialog(hInst, MAKEINTRESOURCE(DLG_Error),
\r
6681 hwndMain, (DLGPROC)lpProc);
\r
6682 FreeProcInstance(lpProc);
\r
6687 /*---------------------------------------------------------------------------*\
\r
6689 * Ics Interaction console functions
\r
6691 \*---------------------------------------------------------------------------*/
\r
6693 #define HISTORY_SIZE 64
\r
6694 static char *history[HISTORY_SIZE];
\r
6695 int histIn = 0, histP = 0;
\r
6698 SaveInHistory(char *cmd)
\r
6700 if (history[histIn] != NULL) {
\r
6701 free(history[histIn]);
\r
6702 history[histIn] = NULL;
\r
6704 if (*cmd == NULLCHAR) return;
\r
6705 history[histIn] = StrSave(cmd);
\r
6706 histIn = (histIn + 1) % HISTORY_SIZE;
\r
6707 if (history[histIn] != NULL) {
\r
6708 free(history[histIn]);
\r
6709 history[histIn] = NULL;
\r
6715 PrevInHistory(char *cmd)
\r
6718 if (histP == histIn) {
\r
6719 if (history[histIn] != NULL) free(history[histIn]);
\r
6720 history[histIn] = StrSave(cmd);
\r
6722 newhp = (histP - 1 + HISTORY_SIZE) % HISTORY_SIZE;
\r
6723 if (newhp == histIn || history[newhp] == NULL) return NULL;
\r
6725 return history[histP];
\r
6731 if (histP == histIn) return NULL;
\r
6732 histP = (histP + 1) % HISTORY_SIZE;
\r
6733 return history[histP];
\r
6737 LoadIcsTextMenu(IcsTextMenuEntry *e)
\r
6741 hmenu = LoadMenu(hInst, "TextMenu");
\r
6742 h = GetSubMenu(hmenu, 0);
\r
6744 if (strcmp(e->item, "-") == 0) {
\r
6745 AppendMenu(h, MF_SEPARATOR, 0, 0);
\r
6746 } else { // [HGM] re-written a bit to use only one AppendMenu call for both cases (| or no |)
\r
6747 int flags = MF_STRING, j = 0;
\r
6748 if (e->item[0] == '|') {
\r
6749 flags |= MF_MENUBARBREAK;
\r
6752 if(!strcmp(e->command, "none")) flags |= MF_GRAYED; // [HGM] chatclick: provide inactive dummy
\r
6753 AppendMenu(h, flags, IDM_CommandX + i, e->item + j);
\r
6761 WNDPROC consoleTextWindowProc;
\r
6764 CommandX(HWND hwnd, char *command, BOOLEAN getname, BOOLEAN immediate)
\r
6766 char buf[MSG_SIZ], name[MSG_SIZ];
\r
6767 HWND hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
6771 SetWindowText(hInput, command);
\r
6773 SendMessage(hInput, WM_CHAR, '\r', 0);
\r
6775 sel.cpMin = 999999;
\r
6776 sel.cpMax = 999999;
\r
6777 SendMessage(hInput, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
6782 SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);
\r
6783 if (sel.cpMin == sel.cpMax) {
\r
6784 /* Expand to surrounding word */
\r
6787 tr.chrg.cpMax = sel.cpMin;
\r
6788 tr.chrg.cpMin = --sel.cpMin;
\r
6789 if (sel.cpMin < 0) break;
\r
6790 tr.lpstrText = name;
\r
6791 SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr);
\r
6792 } while (isalpha(name[0]) || isdigit(name[0]) || name[0] == '-');
\r
6796 tr.chrg.cpMin = sel.cpMax;
\r
6797 tr.chrg.cpMax = ++sel.cpMax;
\r
6798 tr.lpstrText = name;
\r
6799 if (SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr) < 1) break;
\r
6800 } while (isalpha(name[0]) || isdigit(name[0]) || name[0] == '-');
\r
6803 if (sel.cpMax == sel.cpMin || sel.cpMax - sel.cpMin > MSG_SIZ/2) {
\r
6804 MessageBeep(MB_ICONEXCLAMATION);
\r
6808 tr.lpstrText = name;
\r
6809 SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr);
\r
6811 if (sel.cpMax - sel.cpMin > MSG_SIZ/2) {
\r
6812 MessageBeep(MB_ICONEXCLAMATION);
\r
6815 SendMessage(hwnd, EM_GETSELTEXT, 0, (LPARAM) name);
\r
6818 if(strstr(command, "%s")) snprintf(buf, MSG_SIZ, command, name); else
\r
6819 snprintf(buf, MSG_SIZ, "%s %s", command, name);
\r
6820 SetWindowText(hInput, buf);
\r
6821 SendMessage(hInput, WM_CHAR, '\r', 0);
\r
6823 if(!strcmp(command, "chat")) { ChatPopUp(name); return; }
\r
6824 snprintf(buf, MSG_SIZ, "%s %s ", command, name); /* trailing space */
\r
6825 SetWindowText(hInput, buf);
\r
6826 sel.cpMin = 999999;
\r
6827 sel.cpMax = 999999;
\r
6828 SendMessage(hInput, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
6834 ConsoleTextSubclass(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
\r
6839 switch (message) {
\r
6841 if (!(GetKeyState(VK_CONTROL) & ~1)) break;
\r
6844 SendMessage(hwnd, EM_LINESCROLL, 0, -999999);
\r
6847 sel.cpMin = 999999;
\r
6848 sel.cpMax = 999999;
\r
6849 SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
6850 SendMessage(hwnd, EM_SCROLLCARET, 0, 0);
\r
6855 if(wParam != '\022') {
\r
6856 if (wParam == '\t') {
\r
6857 if (GetKeyState(VK_SHIFT) < 0) {
\r
6859 if (IsIconic(hwndMain)) ShowWindow(hwndMain, SW_RESTORE);
\r
6860 if (buttonDesc[0].hwnd) {
\r
6861 SetFocus(buttonDesc[0].hwnd);
\r
6863 SetFocus(hwndMain);
\r
6867 SetFocus(GetDlgItem(hwndConsole, OPT_ConsoleInput));
\r
6870 hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
6871 JAWS_DELETE( SetFocus(hInput); )
\r
6872 SendMessage(hInput, message, wParam, lParam);
\r
6875 } // [HGM] navigate: for Ctrl+R, flow into nex case (moved up here) to summon up menu
\r
6876 case WM_RBUTTONDOWN:
\r
6877 if (!(GetKeyState(VK_SHIFT) & ~1)) {
\r
6878 /* Move selection here if it was empty */
\r
6880 pt.x = LOWORD(lParam);
\r
6881 pt.y = HIWORD(lParam);
\r
6882 SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);
\r
6883 if (sel.cpMin == sel.cpMax) {
\r
6884 sel.cpMin = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&pt); /*doc is wrong*/
\r
6885 sel.cpMax = sel.cpMin;
\r
6886 SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
6888 SendMessage(hwnd, EM_HIDESELECTION, FALSE, FALSE);
\r
6889 { // [HGM] chatclick: code moved here from WM_RBUTTONUP case, to have menu appear on down-click
\r
6891 HMENU hmenu = LoadIcsTextMenu(icsTextMenuEntry);
\r
6892 SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);
\r
6893 if (sel.cpMin == sel.cpMax) {
\r
6894 EnableMenuItem(hmenu, IDM_Copy, MF_BYCOMMAND|MF_GRAYED);
\r
6895 EnableMenuItem(hmenu, IDM_QuickPaste, MF_BYCOMMAND|MF_GRAYED);
\r
6897 if (!IsClipboardFormatAvailable(CF_TEXT)) {
\r
6898 EnableMenuItem(hmenu, IDM_Paste, MF_BYCOMMAND|MF_GRAYED);
\r
6900 pt.x = LOWORD(lParam)-30; // [HGM] chatclick: make menu pop up with pointer above upper-right item
\r
6901 pt.y = HIWORD(lParam)-10; // make it appear as if mouse moved there, so it will be selected on up-click
\r
6902 PostMessage(hwnd, WM_MOUSEMOVE, wParam, lParam+5);
\r
6903 MenuPopup(hwnd, pt, hmenu, -1);
\r
6907 case WM_RBUTTONUP:
\r
6908 if (GetKeyState(VK_SHIFT) & ~1) {
\r
6909 SendDlgItemMessage(hwndConsole, OPT_ConsoleText,
\r
6910 WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);
\r
6914 hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
6916 return SendMessage(hInput, message, wParam, lParam);
\r
6917 case WM_MBUTTONDOWN:
\r
6918 return SendMessage(hwnd, WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);
\r
6920 switch (LOWORD(wParam)) {
\r
6921 case IDM_QuickPaste:
\r
6923 SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);
\r
6924 if (sel.cpMin == sel.cpMax) {
\r
6925 MessageBeep(MB_ICONEXCLAMATION);
\r
6928 SendMessage(hwnd, WM_COPY, 0, 0);
\r
6929 hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
6930 SendMessage(hInput, WM_PASTE, 0, 0);
\r
6935 SendMessage(hwnd, WM_CUT, 0, 0);
\r
6938 SendMessage(hwnd, WM_PASTE, 0, 0);
\r
6941 SendMessage(hwnd, WM_COPY, 0, 0);
\r
6945 int i = LOWORD(wParam) - IDM_CommandX;
\r
6946 if (i >= 0 && i < ICS_TEXT_MENU_SIZE &&
\r
6947 icsTextMenuEntry[i].command != NULL) {
\r
6948 CommandX(hwnd, icsTextMenuEntry[i].command,
\r
6949 icsTextMenuEntry[i].getname,
\r
6950 icsTextMenuEntry[i].immediate);
\r
6958 return (*consoleTextWindowProc)(hwnd, message, wParam, lParam);
\r
6961 WNDPROC consoleInputWindowProc;
\r
6964 ConsoleInputSubclass(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
\r
6966 char buf[MSG_SIZ];
\r
6968 static BOOL sendNextChar = FALSE;
\r
6969 static BOOL quoteNextChar = FALSE;
\r
6970 InputSource *is = consoleInputSource;
\r
6974 switch (message) {
\r
6976 if (!appData.localLineEditing || sendNextChar) {
\r
6977 is->buf[0] = (CHAR) wParam;
\r
6979 SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);
\r
6980 sendNextChar = FALSE;
\r
6983 if (quoteNextChar) {
\r
6984 buf[0] = (char) wParam;
\r
6985 buf[1] = NULLCHAR;
\r
6986 SendMessage(hwnd, EM_REPLACESEL, TRUE, (LPARAM) buf);
\r
6987 quoteNextChar = FALSE;
\r
6991 case '\r': /* Enter key */
\r
6992 is->count = GetWindowText(hwnd, is->buf, INPUT_SOURCE_BUF_SIZE-1);
\r
6993 if (consoleEcho) SaveInHistory(is->buf);
\r
6994 is->buf[is->count++] = '\n';
\r
6995 is->buf[is->count] = NULLCHAR;
\r
6996 SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);
\r
6997 if (consoleEcho) {
\r
6998 ConsoleOutput(is->buf, is->count, TRUE);
\r
6999 } else if (appData.localLineEditing) {
\r
7000 ConsoleOutput("\n", 1, TRUE);
\r
7003 case '\033': /* Escape key */
\r
7004 SetWindowText(hwnd, "");
\r
7005 cf.cbSize = sizeof(CHARFORMAT);
\r
7006 cf.dwMask = CFM_COLOR|CFM_BOLD|CFM_ITALIC|CFM_UNDERLINE|CFM_STRIKEOUT;
\r
7007 if (consoleEcho) {
\r
7008 cf.crTextColor = textAttribs[ColorNormal].color;
\r
7010 cf.crTextColor = COLOR_ECHOOFF;
\r
7012 cf.dwEffects = textAttribs[ColorNormal].effects;
\r
7013 SendMessage(hwnd, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf);
\r
7015 case '\t': /* Tab key */
\r
7016 if (GetKeyState(VK_SHIFT) < 0) {
\r
7018 SetFocus(GetDlgItem(hwndConsole, OPT_ConsoleText));
\r
7021 if (IsIconic(hwndMain)) ShowWindow(hwndMain, SW_RESTORE);
\r
7022 if (buttonDesc[0].hwnd) {
\r
7023 SetFocus(buttonDesc[0].hwnd);
\r
7025 SetFocus(hwndMain);
\r
7029 case '\023': /* Ctrl+S */
\r
7030 sendNextChar = TRUE;
\r
7032 case '\021': /* Ctrl+Q */
\r
7033 quoteNextChar = TRUE;
\r
7043 GetWindowText(hwnd, buf, MSG_SIZ);
\r
7044 p = PrevInHistory(buf);
\r
7046 SetWindowText(hwnd, p);
\r
7047 sel.cpMin = 999999;
\r
7048 sel.cpMax = 999999;
\r
7049 SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
7054 p = NextInHistory();
\r
7056 SetWindowText(hwnd, p);
\r
7057 sel.cpMin = 999999;
\r
7058 sel.cpMax = 999999;
\r
7059 SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
7065 if (!(GetKeyState(VK_CONTROL) & ~1)) break;
\r
7069 SendDlgItemMessage(hwndConsole, OPT_ConsoleText, message, wParam, lParam);
\r
7073 case WM_MBUTTONDOWN:
\r
7074 SendDlgItemMessage(hwndConsole, OPT_ConsoleText,
\r
7075 WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);
\r
7077 case WM_RBUTTONUP:
\r
7078 if (GetKeyState(VK_SHIFT) & ~1) {
\r
7079 SendDlgItemMessage(hwndConsole, OPT_ConsoleText,
\r
7080 WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);
\r
7084 hmenu = LoadMenu(hInst, "InputMenu");
\r
7085 SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);
\r
7086 if (sel.cpMin == sel.cpMax) {
\r
7087 EnableMenuItem(hmenu, IDM_Copy, MF_BYCOMMAND|MF_GRAYED);
\r
7088 EnableMenuItem(hmenu, IDM_Cut, MF_BYCOMMAND|MF_GRAYED);
\r
7090 if (!IsClipboardFormatAvailable(CF_TEXT)) {
\r
7091 EnableMenuItem(hmenu, IDM_Paste, MF_BYCOMMAND|MF_GRAYED);
\r
7093 pt.x = LOWORD(lParam);
\r
7094 pt.y = HIWORD(lParam);
\r
7095 MenuPopup(hwnd, pt, hmenu, -1);
\r
7099 switch (LOWORD(wParam)) {
\r
7101 SendMessage(hwnd, EM_UNDO, 0, 0);
\r
7103 case IDM_SelectAll:
\r
7105 sel.cpMax = -1; /*999999?*/
\r
7106 SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
7109 SendMessage(hwnd, WM_CUT, 0, 0);
\r
7112 SendMessage(hwnd, WM_PASTE, 0, 0);
\r
7115 SendMessage(hwnd, WM_COPY, 0, 0);
\r
7120 return (*consoleInputWindowProc)(hwnd, message, wParam, lParam);
\r
7123 #define CO_MAX 100000
\r
7124 #define CO_TRIM 1000
\r
7127 ConsoleWndProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
7129 static SnapData sd;
\r
7130 HWND hText, hInput;
\r
7132 static int sizeX, sizeY;
\r
7133 int newSizeX, newSizeY;
\r
7137 hText = GetDlgItem(hDlg, OPT_ConsoleText);
\r
7138 hInput = GetDlgItem(hDlg, OPT_ConsoleInput);
\r
7140 switch (message) {
\r
7142 if (((NMHDR*)lParam)->code == EN_LINK)
\r
7144 ENLINK *pLink = (ENLINK*)lParam;
\r
7145 if (pLink->msg == WM_LBUTTONUP)
\r
7149 tr.chrg = pLink->chrg;
\r
7150 tr.lpstrText = malloc(1+tr.chrg.cpMax-tr.chrg.cpMin);
\r
7151 SendMessage(hText, EM_GETTEXTRANGE, 0, (LPARAM)&tr);
\r
7152 ShellExecute(NULL, "open", tr.lpstrText, NULL, NULL, SW_SHOW);
\r
7153 free(tr.lpstrText);
\r
7157 case WM_INITDIALOG: /* message: initialize dialog box */
\r
7158 hwndConsole = hDlg;
\r
7160 consoleTextWindowProc = (WNDPROC)
\r
7161 SetWindowLong(hText, GWL_WNDPROC, (LONG) ConsoleTextSubclass);
\r
7162 SendMessage(hText, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);
\r
7163 consoleInputWindowProc = (WNDPROC)
\r
7164 SetWindowLong(hInput, GWL_WNDPROC, (LONG) ConsoleInputSubclass);
\r
7165 SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);
\r
7166 Colorize(ColorNormal, TRUE);
\r
7167 SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &consoleCF);
\r
7168 ChangedConsoleFont();
\r
7169 GetClientRect(hDlg, &rect);
\r
7170 sizeX = rect.right;
\r
7171 sizeY = rect.bottom;
\r
7172 if (wpConsole.x != CW_USEDEFAULT && wpConsole.y != CW_USEDEFAULT &&
\r
7173 wpConsole.width != CW_USEDEFAULT && wpConsole.height != CW_USEDEFAULT) {
\r
7174 WINDOWPLACEMENT wp;
\r
7175 EnsureOnScreen(&wpConsole.x, &wpConsole.y, 0, 0);
\r
7176 wp.length = sizeof(WINDOWPLACEMENT);
\r
7178 wp.showCmd = SW_SHOW;
\r
7179 wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;
\r
7180 wp.rcNormalPosition.left = wpConsole.x;
\r
7181 wp.rcNormalPosition.right = wpConsole.x + wpConsole.width;
\r
7182 wp.rcNormalPosition.top = wpConsole.y;
\r
7183 wp.rcNormalPosition.bottom = wpConsole.y + wpConsole.height;
\r
7184 SetWindowPlacement(hDlg, &wp);
\r
7187 // [HGM] Chessknight's change 2004-07-13
\r
7188 else { /* Determine Defaults */
\r
7189 WINDOWPLACEMENT wp;
\r
7190 wpConsole.x = wpMain.width + 1;
\r
7191 wpConsole.y = wpMain.y;
\r
7192 wpConsole.width = screenWidth - wpMain.width;
\r
7193 wpConsole.height = wpMain.height;
\r
7194 EnsureOnScreen(&wpConsole.x, &wpConsole.y, 0, 0);
\r
7195 wp.length = sizeof(WINDOWPLACEMENT);
\r
7197 wp.showCmd = SW_SHOW;
\r
7198 wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;
\r
7199 wp.rcNormalPosition.left = wpConsole.x;
\r
7200 wp.rcNormalPosition.right = wpConsole.x + wpConsole.width;
\r
7201 wp.rcNormalPosition.top = wpConsole.y;
\r
7202 wp.rcNormalPosition.bottom = wpConsole.y + wpConsole.height;
\r
7203 SetWindowPlacement(hDlg, &wp);
\r
7206 // Allow hText to highlight URLs and send notifications on them
\r
7207 wMask = (WORD) SendMessage(hText, EM_GETEVENTMASK, 0, 0L);
\r
7208 SendMessage(hText, EM_SETEVENTMASK, 0, wMask | ENM_LINK);
\r
7209 SendMessage(hText, EM_AUTOURLDETECT, TRUE, 0L);
\r
7210 SetWindowLong(hText, GWL_USERDATA, 79); // initialize the text window's width
\r
7224 if (IsIconic(hDlg)) break;
\r
7225 newSizeX = LOWORD(lParam);
\r
7226 newSizeY = HIWORD(lParam);
\r
7227 if (sizeX != newSizeX || sizeY != newSizeY) {
\r
7228 RECT rectText, rectInput;
\r
7230 int newTextHeight, newTextWidth;
\r
7231 GetWindowRect(hText, &rectText);
\r
7232 newTextWidth = rectText.right - rectText.left + newSizeX - sizeX;
\r
7233 newTextHeight = rectText.bottom - rectText.top + newSizeY - sizeY;
\r
7234 if (newTextHeight < 0) {
\r
7235 newSizeY += -newTextHeight;
\r
7236 newTextHeight = 0;
\r
7238 SetWindowPos(hText, NULL, 0, 0,
\r
7239 newTextWidth, newTextHeight, SWP_NOZORDER|SWP_NOMOVE);
\r
7240 GetWindowRect(hInput, &rectInput); /* gives screen coords */
\r
7241 pt.x = rectInput.left;
\r
7242 pt.y = rectInput.top + newSizeY - sizeY;
\r
7243 ScreenToClient(hDlg, &pt);
\r
7244 SetWindowPos(hInput, NULL,
\r
7245 pt.x, pt.y, /* needs client coords */
\r
7246 rectInput.right - rectInput.left + newSizeX - sizeX,
\r
7247 rectInput.bottom - rectInput.top, SWP_NOZORDER);
\r
7253 case WM_GETMINMAXINFO:
\r
7254 /* Prevent resizing window too small */
\r
7255 mmi = (MINMAXINFO *) lParam;
\r
7256 mmi->ptMinTrackSize.x = 100;
\r
7257 mmi->ptMinTrackSize.y = 100;
\r
7260 /* [AS] Snapping */
\r
7261 case WM_ENTERSIZEMOVE:
\r
7262 return OnEnterSizeMove( &sd, hDlg, wParam, lParam );
\r
7265 return OnSizing( &sd, hDlg, wParam, lParam );
\r
7268 return OnMoving( &sd, hDlg, wParam, lParam );
\r
7270 case WM_EXITSIZEMOVE:
\r
7271 UpdateICSWidth(hText);
\r
7272 return OnExitSizeMove( &sd, hDlg, wParam, lParam );
\r
7275 return DefWindowProc(hDlg, message, wParam, lParam);
\r
7283 if (hwndConsole) return;
\r
7284 hCons = CreateDialog(hInst, szConsoleName, 0, NULL);
\r
7285 SendMessage(hCons, WM_INITDIALOG, 0, 0);
\r
7290 ConsoleOutput(char* data, int length, int forceVisible)
\r
7295 char buf[CO_MAX+1];
\r
7298 static int delayLF = 0;
\r
7299 CHARRANGE savesel, sel;
\r
7301 if (hwndConsole == NULL || length > CO_MAX-100 || length == 0) return;
\r
7309 while (length--) {
\r
7317 } else if (*p == '\007') {
\r
7318 MyPlaySound(&sounds[(int)SoundBell]);
\r
7325 hText = GetDlgItem(hwndConsole, OPT_ConsoleText);
\r
7326 SendMessage(hText, EM_HIDESELECTION, TRUE, FALSE);
\r
7327 /* Save current selection */
\r
7328 SendMessage(hText, EM_EXGETSEL, 0, (LPARAM)&savesel);
\r
7329 exlen = GetWindowTextLength(hText);
\r
7330 /* Find out whether current end of text is visible */
\r
7331 SendMessage(hText, EM_GETRECT, 0, (LPARAM) &rect);
\r
7332 SendMessage(hText, EM_POSFROMCHAR, (WPARAM) &pEnd, exlen);
\r
7333 /* Trim existing text if it's too long */
\r
7334 if (exlen + (q - buf) > CO_MAX) {
\r
7335 trim = (CO_TRIM > (q - buf)) ? CO_TRIM : (q - buf);
\r
7338 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
7339 SendMessage(hText, EM_REPLACESEL, 0, (LPARAM)"");
\r
7341 savesel.cpMin -= trim;
\r
7342 savesel.cpMax -= trim;
\r
7343 if (exlen < 0) exlen = 0;
\r
7344 if (savesel.cpMin < 0) savesel.cpMin = 0;
\r
7345 if (savesel.cpMax < savesel.cpMin) savesel.cpMax = savesel.cpMin;
\r
7347 /* Append the new text */
\r
7348 sel.cpMin = exlen;
\r
7349 sel.cpMax = exlen;
\r
7350 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
7351 SendMessage(hText, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&consoleCF);
\r
7352 SendMessage(hText, EM_REPLACESEL, 0, (LPARAM) buf);
\r
7353 if (forceVisible || exlen == 0 ||
\r
7354 (rect.left <= pEnd.x && pEnd.x < rect.right &&
\r
7355 rect.top <= pEnd.y && pEnd.y < rect.bottom)) {
\r
7356 /* Scroll to make new end of text visible if old end of text
\r
7357 was visible or new text is an echo of user typein */
\r
7358 sel.cpMin = 9999999;
\r
7359 sel.cpMax = 9999999;
\r
7360 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
7361 SendMessage(hText, EM_HIDESELECTION, FALSE, FALSE);
\r
7362 SendMessage(hText, EM_SCROLLCARET, 0, 0);
\r
7363 SendMessage(hText, EM_HIDESELECTION, TRUE, FALSE);
\r
7365 if (savesel.cpMax == exlen || forceVisible) {
\r
7366 /* Move insert point to new end of text if it was at the old
\r
7367 end of text or if the new text is an echo of user typein */
\r
7368 sel.cpMin = 9999999;
\r
7369 sel.cpMax = 9999999;
\r
7370 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
7372 /* Restore previous selection */
\r
7373 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&savesel);
\r
7375 SendMessage(hText, EM_HIDESELECTION, FALSE, FALSE);
\r
7382 DisplayHoldingsCount(HDC hdc, int x, int y, int rightAlign, int copyNumber)
\r
7386 COLORREF oldFg, oldBg;
\r
7391 snprintf(buf, sizeof(buf)/sizeof(buf[0]),"%d", copyNumber); else buf[0] = 0;
\r
7393 oldFg = SetTextColor(hdc, RGB(255, 255, 255)); /* white */
\r
7394 oldBg = SetBkColor(hdc, RGB(0, 0, 0)); /* black */
\r
7395 oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);
\r
7398 rect.right = x + squareSize;
\r
7400 rect.bottom = y + squareSize;
\r
7403 ExtTextOut(hdc, x + MESSAGE_LINE_LEFTMARGIN
\r
7404 + (rightAlign ? (squareSize*2)/3 : 0),
\r
7405 y, ETO_CLIPPED|ETO_OPAQUE,
\r
7406 &rect, str, strlen(str), NULL);
\r
7408 (void) SetTextColor(hdc, oldFg);
\r
7409 (void) SetBkColor(hdc, oldBg);
\r
7410 (void) SelectObject(hdc, oldFont);
\r
7414 DisplayAClock(HDC hdc, int timeRemaining, int highlight,
\r
7415 RECT *rect, char *color, char *flagFell)
\r
7419 COLORREF oldFg, oldBg;
\r
7422 if (appData.clockMode) {
\r
7424 snprintf(buf, sizeof(buf)/sizeof(buf[0]), "%c %s %s", color[0], TimeString(timeRemaining), flagFell);
\r
7426 snprintf(buf, sizeof(buf)/sizeof(buf[0]), "%s:%c%s %s", color, (logoHeight>0 ? 0 : ' '), TimeString(timeRemaining), flagFell);
\r
7433 oldFg = SetTextColor(hdc, RGB(255, 255, 255)); /* white */
\r
7434 oldBg = SetBkColor(hdc, RGB(0, 0, 0)); /* black */
\r
7436 oldFg = SetTextColor(hdc, RGB(0, 0, 0)); /* black */
\r
7437 oldBg = SetBkColor(hdc, RGB(255, 255, 255)); /* white */
\r
7439 oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);
\r
7443 ExtTextOut(hdc, rect->left + MESSAGE_LINE_LEFTMARGIN,
\r
7444 rect->top, ETO_CLIPPED|ETO_OPAQUE,
\r
7445 rect, str, strlen(str), NULL);
\r
7446 if(logoHeight > 0 && appData.clockMode) {
\r
7448 snprintf(buf, sizeof(buf)/sizeof(buf[0]), "%s %s", buf+7, flagFell);
\r
7449 r.top = rect->top + logoHeight/2;
\r
7450 r.left = rect->left;
\r
7451 r.right = rect->right;
\r
7452 r.bottom = rect->bottom;
\r
7453 ExtTextOut(hdc, rect->left + MESSAGE_LINE_LEFTMARGIN,
\r
7454 r.top, ETO_CLIPPED|ETO_OPAQUE,
\r
7455 &r, str, strlen(str), NULL);
\r
7457 (void) SetTextColor(hdc, oldFg);
\r
7458 (void) SetBkColor(hdc, oldBg);
\r
7459 (void) SelectObject(hdc, oldFont);
\r
7464 DoReadFile(HANDLE hFile, char *buf, int count, DWORD *outCount,
\r
7470 if( count <= 0 ) {
\r
7471 if (appData.debugMode) {
\r
7472 fprintf( debugFP, "DoReadFile: trying to read past end of buffer, overflow = %d\n", count );
\r
7475 return ERROR_INVALID_USER_BUFFER;
\r
7478 ResetEvent(ovl->hEvent);
\r
7479 ovl->Offset = ovl->OffsetHigh = 0;
\r
7480 ok = ReadFile(hFile, buf, count, outCount, ovl);
\r
7484 err = GetLastError();
\r
7485 if (err == ERROR_IO_PENDING) {
\r
7486 ok = GetOverlappedResult(hFile, ovl, outCount, TRUE);
\r
7490 err = GetLastError();
\r
7497 DoWriteFile(HANDLE hFile, char *buf, int count, DWORD *outCount,
\r
7502 ResetEvent(ovl->hEvent);
\r
7503 ovl->Offset = ovl->OffsetHigh = 0;
\r
7504 ok = WriteFile(hFile, buf, count, outCount, ovl);
\r
7508 err = GetLastError();
\r
7509 if (err == ERROR_IO_PENDING) {
\r
7510 ok = GetOverlappedResult(hFile, ovl, outCount, TRUE);
\r
7514 err = GetLastError();
\r
7520 /* [AS] If input is line by line and a line exceed the buffer size, force an error */
\r
7521 void CheckForInputBufferFull( InputSource * is )
\r
7523 if( is->lineByLine && (is->next - is->buf) >= INPUT_SOURCE_BUF_SIZE ) {
\r
7524 /* Look for end of line */
\r
7525 char * p = is->buf;
\r
7527 while( p < is->next && *p != '\n' ) {
\r
7531 if( p >= is->next ) {
\r
7532 if (appData.debugMode) {
\r
7533 fprintf( debugFP, "Input line exceeded buffer size (source id=%lu)\n", is->id );
\r
7536 is->error = ERROR_BROKEN_PIPE; /* [AS] Just any non-successful code! */
\r
7537 is->count = (DWORD) -1;
\r
7538 is->next = is->buf;
\r
7544 InputThread(LPVOID arg)
\r
7549 is = (InputSource *) arg;
\r
7550 ovl.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
\r
7551 ovl.Internal = ovl.InternalHigh = ovl.Offset = ovl.OffsetHigh = 0;
\r
7552 while (is->hThread != NULL) {
\r
7553 is->error = DoReadFile(is->hFile, is->next,
\r
7554 INPUT_SOURCE_BUF_SIZE - (is->next - is->buf),
\r
7555 &is->count, &ovl);
\r
7556 if (is->error == NO_ERROR) {
\r
7557 is->next += is->count;
\r
7559 if (is->error == ERROR_BROKEN_PIPE) {
\r
7560 /* Correct for MS brain damage. EOF reading a pipe is not an error. */
\r
7563 is->count = (DWORD) -1;
\r
7564 /* [AS] The (is->count <= 0) check below is not useful for unsigned values! */
\r
7569 CheckForInputBufferFull( is );
\r
7571 SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);
\r
7573 if( is->count == ((DWORD) -1) ) break; /* [AS] */
\r
7575 if (is->count <= 0) break; /* Quit on EOF or error */
\r
7578 CloseHandle(ovl.hEvent);
\r
7579 CloseHandle(is->hFile);
\r
7581 if (appData.debugMode) {
\r
7582 fprintf( debugFP, "Input thread terminated (id=%lu, error=%d, count=%ld)\n", is->id, is->error, is->count );
\r
7589 /* Windows 95 beta 2 won't let you do overlapped i/o on a console or pipe */
\r
7591 NonOvlInputThread(LPVOID arg)
\r
7598 is = (InputSource *) arg;
\r
7599 while (is->hThread != NULL) {
\r
7600 is->error = ReadFile(is->hFile, is->next,
\r
7601 INPUT_SOURCE_BUF_SIZE - (is->next - is->buf),
\r
7602 &is->count, NULL) ? NO_ERROR : GetLastError();
\r
7603 if (is->error == NO_ERROR) {
\r
7604 /* Change CRLF to LF */
\r
7605 if (is->next > is->buf) {
\r
7607 i = is->count + 1;
\r
7615 if (prev == '\r' && *p == '\n') {
\r
7627 if (is->error == ERROR_BROKEN_PIPE) {
\r
7628 /* Correct for MS brain damage. EOF reading a pipe is not an error. */
\r
7631 is->count = (DWORD) -1;
\r
7635 CheckForInputBufferFull( is );
\r
7637 SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);
\r
7639 if( is->count == ((DWORD) -1) ) break; /* [AS] */
\r
7641 if (is->count < 0) break; /* Quit on error */
\r
7643 CloseHandle(is->hFile);
\r
7648 SocketInputThread(LPVOID arg)
\r
7652 is = (InputSource *) arg;
\r
7653 while (is->hThread != NULL) {
\r
7654 is->count = recv(is->sock, is->buf, INPUT_SOURCE_BUF_SIZE, 0);
\r
7655 if ((int)is->count == SOCKET_ERROR) {
\r
7656 is->count = (DWORD) -1;
\r
7657 is->error = WSAGetLastError();
\r
7659 is->error = NO_ERROR;
\r
7660 is->next += is->count;
\r
7661 if (is->count == 0 && is->second == is) {
\r
7662 /* End of file on stderr; quit with no message */
\r
7666 SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);
\r
7668 if( is->count == ((DWORD) -1) ) break; /* [AS] */
\r
7670 if (is->count <= 0) break; /* Quit on EOF or error */
\r
7676 InputEvent(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
\r
7680 is = (InputSource *) lParam;
\r
7681 if (is->lineByLine) {
\r
7682 /* Feed in lines one by one */
\r
7683 char *p = is->buf;
\r
7685 while (q < is->next) {
\r
7686 if (*q++ == '\n') {
\r
7687 (is->func)(is, is->closure, p, q - p, NO_ERROR);
\r
7692 /* Move any partial line to the start of the buffer */
\r
7694 while (p < is->next) {
\r
7699 if (is->error != NO_ERROR || is->count == 0) {
\r
7700 /* Notify backend of the error. Note: If there was a partial
\r
7701 line at the end, it is not flushed through. */
\r
7702 (is->func)(is, is->closure, is->buf, is->count, is->error);
\r
7705 /* Feed in the whole chunk of input at once */
\r
7706 (is->func)(is, is->closure, is->buf, is->count, is->error);
\r
7707 is->next = is->buf;
\r
7711 /*---------------------------------------------------------------------------*\
\r
7713 * Menu enables. Used when setting various modes.
\r
7715 \*---------------------------------------------------------------------------*/
\r
7723 GreyRevert(Boolean grey)
\r
7724 { // [HGM] vari: for retracting variations in local mode
\r
7725 HMENU hmenu = GetMenu(hwndMain);
\r
7726 EnableMenuItem(hmenu, IDM_Revert, MF_BYCOMMAND|(grey ? MF_GRAYED : MF_ENABLED));
\r
7727 EnableMenuItem(hmenu, IDM_Annotate, MF_BYCOMMAND|(grey ? MF_GRAYED : MF_ENABLED));
\r
7731 SetMenuEnables(HMENU hmenu, Enables *enab)
\r
7733 while (enab->item > 0) {
\r
7734 (void) EnableMenuItem(hmenu, enab->item, enab->flags);
\r
7739 Enables gnuEnables[] = {
\r
7740 { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },
\r
7741 { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },
\r
7742 { IDM_IcsClient, MF_BYCOMMAND|MF_GRAYED },
\r
7743 { IDM_Accept, MF_BYCOMMAND|MF_GRAYED },
\r
7744 { IDM_Decline, MF_BYCOMMAND|MF_GRAYED },
\r
7745 { IDM_Rematch, MF_BYCOMMAND|MF_GRAYED },
\r
7746 { IDM_Adjourn, MF_BYCOMMAND|MF_GRAYED },
\r
7747 { IDM_StopExamining, MF_BYCOMMAND|MF_GRAYED },
\r
7748 { IDM_StopObserving, MF_BYCOMMAND|MF_GRAYED },
\r
7749 { IDM_Upload, MF_BYCOMMAND|MF_GRAYED },
\r
7750 { IDM_Revert, MF_BYCOMMAND|MF_GRAYED },
\r
7751 { IDM_Annotate, MF_BYCOMMAND|MF_GRAYED },
\r
7752 { IDM_NewChat, MF_BYCOMMAND|MF_GRAYED },
\r
7756 Enables icsEnables[] = {
\r
7757 { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },
\r
7758 { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },
\r
7759 { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },
\r
7760 { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },
\r
7761 { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },
\r
7762 { IDM_Match, MF_BYCOMMAND|MF_GRAYED },
\r
7763 { IDM_MachineBoth, MF_BYCOMMAND|MF_GRAYED },
\r
7764 { IDM_AnalysisMode, MF_BYCOMMAND|MF_ENABLED },
\r
7765 { IDM_AnalyzeFile, MF_BYCOMMAND|MF_GRAYED },
\r
7766 { IDM_TimeControl, MF_BYCOMMAND|MF_GRAYED },
\r
7767 { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },
\r
7768 { IDM_Hint, MF_BYCOMMAND|MF_GRAYED },
\r
7769 { IDM_Book, MF_BYCOMMAND|MF_GRAYED },
\r
7770 { IDM_IcsOptions, MF_BYCOMMAND|MF_ENABLED },
\r
7771 { IDM_Engine1Options, MF_BYCOMMAND|MF_GRAYED },
\r
7772 { IDM_Engine2Options, MF_BYCOMMAND|MF_GRAYED },
\r
7773 { IDM_Annotate, MF_BYCOMMAND|MF_GRAYED },
\r
7778 Enables zippyEnables[] = {
\r
7779 { IDM_MoveNow, MF_BYCOMMAND|MF_ENABLED },
\r
7780 { IDM_Hint, MF_BYCOMMAND|MF_ENABLED },
\r
7781 { IDM_Book, MF_BYCOMMAND|MF_ENABLED },
\r
7782 { IDM_Engine1Options, MF_BYCOMMAND|MF_ENABLED },
\r
7787 Enables ncpEnables[] = {
\r
7788 { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },
\r
7789 { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },
\r
7790 { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },
\r
7791 { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },
\r
7792 { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },
\r
7793 { IDM_Match, MF_BYCOMMAND|MF_GRAYED },
\r
7794 { IDM_AnalysisMode, MF_BYCOMMAND|MF_GRAYED },
\r
7795 { IDM_AnalyzeFile, MF_BYCOMMAND|MF_GRAYED },
\r
7796 { IDM_IcsClient, MF_BYCOMMAND|MF_GRAYED },
\r
7797 { ACTION_POS, MF_BYPOSITION|MF_GRAYED },
\r
7798 { IDM_Revert, MF_BYCOMMAND|MF_GRAYED },
\r
7799 { IDM_Annotate, MF_BYCOMMAND|MF_GRAYED },
\r
7800 { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },
\r
7801 { IDM_RetractMove, MF_BYCOMMAND|MF_GRAYED },
\r
7802 { IDM_TimeControl, MF_BYCOMMAND|MF_GRAYED },
\r
7803 { IDM_Hint, MF_BYCOMMAND|MF_GRAYED },
\r
7804 { IDM_Book, MF_BYCOMMAND|MF_GRAYED },
\r
7805 { IDM_MachineBoth, MF_BYCOMMAND|MF_GRAYED },
\r
7806 { IDM_NewChat, MF_BYCOMMAND|MF_GRAYED },
\r
7807 { IDM_Engine1Options, MF_BYCOMMAND|MF_GRAYED },
\r
7808 { IDM_Engine2Options, MF_BYCOMMAND|MF_GRAYED },
\r
7812 Enables trainingOnEnables[] = {
\r
7813 { IDM_EditComment, MF_BYCOMMAND|MF_GRAYED },
\r
7814 { IDM_Comment, MF_BYCOMMAND|MF_GRAYED },
\r
7815 { IDM_Pause, MF_BYCOMMAND|MF_GRAYED },
\r
7816 { IDM_Forward, MF_BYCOMMAND|MF_GRAYED },
\r
7817 { IDM_Backward, MF_BYCOMMAND|MF_GRAYED },
\r
7818 { IDM_ToEnd, MF_BYCOMMAND|MF_GRAYED },
\r
7819 { IDM_ToStart, MF_BYCOMMAND|MF_GRAYED },
\r
7820 { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },
\r
7821 { IDM_TruncateGame, MF_BYCOMMAND|MF_GRAYED },
\r
7825 Enables trainingOffEnables[] = {
\r
7826 { IDM_EditComment, MF_BYCOMMAND|MF_ENABLED },
\r
7827 { IDM_Comment, MF_BYCOMMAND|MF_ENABLED },
\r
7828 { IDM_Pause, MF_BYCOMMAND|MF_ENABLED },
\r
7829 { IDM_Forward, MF_BYCOMMAND|MF_ENABLED },
\r
7830 { IDM_Backward, MF_BYCOMMAND|MF_ENABLED },
\r
7831 { IDM_ToEnd, MF_BYCOMMAND|MF_ENABLED },
\r
7832 { IDM_ToStart, MF_BYCOMMAND|MF_ENABLED },
\r
7833 { IDM_MoveNow, MF_BYCOMMAND|MF_ENABLED },
\r
7834 { IDM_TruncateGame, MF_BYCOMMAND|MF_ENABLED },
\r
7838 /* These modify either ncpEnables or gnuEnables */
\r
7839 Enables cmailEnables[] = {
\r
7840 { IDM_MailMove, MF_BYCOMMAND|MF_ENABLED },
\r
7841 { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_ENABLED },
\r
7842 { ACTION_POS, MF_BYPOSITION|MF_ENABLED },
\r
7843 { IDM_CallFlag, MF_BYCOMMAND|MF_GRAYED },
\r
7844 { IDM_Draw, MF_BYCOMMAND|MF_ENABLED },
\r
7845 { IDM_Adjourn, MF_BYCOMMAND|MF_GRAYED },
\r
7846 { IDM_Abort, MF_BYCOMMAND|MF_GRAYED },
\r
7850 Enables machineThinkingEnables[] = {
\r
7851 { IDM_LoadGame, MF_BYCOMMAND|MF_GRAYED },
\r
7852 { IDM_LoadNextGame, MF_BYCOMMAND|MF_GRAYED },
\r
7853 { IDM_LoadPrevGame, MF_BYCOMMAND|MF_GRAYED },
\r
7854 { IDM_ReloadGame, MF_BYCOMMAND|MF_GRAYED },
\r
7855 { IDM_PasteGame, MF_BYCOMMAND|MF_GRAYED },
\r
7856 { IDM_LoadPosition, MF_BYCOMMAND|MF_GRAYED },
\r
7857 { IDM_LoadNextPosition, MF_BYCOMMAND|MF_GRAYED },
\r
7858 { IDM_LoadPrevPosition, MF_BYCOMMAND|MF_GRAYED },
\r
7859 { IDM_ReloadPosition, MF_BYCOMMAND|MF_GRAYED },
\r
7860 { IDM_PastePosition, MF_BYCOMMAND|MF_GRAYED },
\r
7861 { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },
\r
7862 { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },
\r
7863 { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },
\r
7864 { IDM_Match, MF_BYCOMMAND|MF_GRAYED },
\r
7865 { IDM_TypeInMove, MF_BYCOMMAND|MF_GRAYED },
\r
7866 { IDM_RetractMove, MF_BYCOMMAND|MF_GRAYED },
\r
7870 Enables userThinkingEnables[] = {
\r
7871 { IDM_LoadGame, MF_BYCOMMAND|MF_ENABLED },
\r
7872 { IDM_LoadNextGame, MF_BYCOMMAND|MF_ENABLED },
\r
7873 { IDM_LoadPrevGame, MF_BYCOMMAND|MF_ENABLED },
\r
7874 { IDM_ReloadGame, MF_BYCOMMAND|MF_ENABLED },
\r
7875 { IDM_PasteGame, MF_BYCOMMAND|MF_ENABLED },
\r
7876 { IDM_LoadPosition, MF_BYCOMMAND|MF_ENABLED },
\r
7877 { IDM_LoadNextPosition, MF_BYCOMMAND|MF_ENABLED },
\r
7878 { IDM_LoadPrevPosition, MF_BYCOMMAND|MF_ENABLED },
\r
7879 { IDM_ReloadPosition, MF_BYCOMMAND|MF_ENABLED },
\r
7880 { IDM_PastePosition, MF_BYCOMMAND|MF_ENABLED },
\r
7881 { IDM_MachineWhite, MF_BYCOMMAND|MF_ENABLED },
\r
7882 { IDM_MachineBlack, MF_BYCOMMAND|MF_ENABLED },
\r
7883 { IDM_TwoMachines, MF_BYCOMMAND|MF_ENABLED },
\r
7884 { IDM_Match, MF_BYCOMMAND|MF_ENABLED },
\r
7885 { IDM_TypeInMove, MF_BYCOMMAND|MF_ENABLED },
\r
7886 { IDM_RetractMove, MF_BYCOMMAND|MF_ENABLED },
\r
7890 /*---------------------------------------------------------------------------*\
\r
7892 * Front-end interface functions exported by XBoard.
\r
7893 * Functions appear in same order as prototypes in frontend.h.
\r
7895 \*---------------------------------------------------------------------------*/
\r
7899 static UINT prevChecked = 0;
\r
7900 static int prevPausing = 0;
\r
7903 if (pausing != prevPausing) {
\r
7904 prevPausing = pausing;
\r
7905 (void) CheckMenuItem(GetMenu(hwndMain), IDM_Pause,
\r
7906 MF_BYCOMMAND|(pausing ? MF_CHECKED : MF_UNCHECKED));
\r
7907 if (hwndPause) SetWindowText(hwndPause, pausing ? "C" : "P");
\r
7910 switch (gameMode) {
\r
7911 case BeginningOfGame:
\r
7912 if (appData.icsActive)
\r
7913 nowChecked = IDM_IcsClient;
\r
7914 else if (appData.noChessProgram)
\r
7915 nowChecked = IDM_EditGame;
\r
7917 nowChecked = IDM_MachineBlack;
\r
7919 case MachinePlaysBlack:
\r
7920 nowChecked = IDM_MachineBlack;
\r
7922 case MachinePlaysWhite:
\r
7923 nowChecked = IDM_MachineWhite;
\r
7925 case TwoMachinesPlay:
\r
7926 nowChecked = matchMode ? IDM_Match : IDM_TwoMachines; // [HGM] match
\r
7929 nowChecked = IDM_AnalysisMode;
\r
7932 nowChecked = IDM_AnalyzeFile;
\r
7935 nowChecked = IDM_EditGame;
\r
7937 case PlayFromGameFile:
\r
7938 nowChecked = IDM_LoadGame;
\r
7940 case EditPosition:
\r
7941 nowChecked = IDM_EditPosition;
\r
7944 nowChecked = IDM_Training;
\r
7946 case IcsPlayingWhite:
\r
7947 case IcsPlayingBlack:
\r
7948 case IcsObserving:
\r
7950 nowChecked = IDM_IcsClient;
\r
7957 if (prevChecked != 0)
\r
7958 (void) CheckMenuItem(GetMenu(hwndMain),
\r
7959 prevChecked, MF_BYCOMMAND|MF_UNCHECKED);
\r
7960 if (nowChecked != 0)
\r
7961 (void) CheckMenuItem(GetMenu(hwndMain),
\r
7962 nowChecked, MF_BYCOMMAND|MF_CHECKED);
\r
7964 if (nowChecked == IDM_LoadGame || nowChecked == IDM_Training) {
\r
7965 (void) EnableMenuItem(GetMenu(hwndMain), IDM_Training,
\r
7966 MF_BYCOMMAND|MF_ENABLED);
\r
7968 (void) EnableMenuItem(GetMenu(hwndMain),
\r
7969 IDM_Training, MF_BYCOMMAND|MF_GRAYED);
\r
7972 prevChecked = nowChecked;
\r
7974 /* [DM] icsEngineAnalyze - Do a sceure check too */
\r
7975 if (appData.icsActive) {
\r
7976 if (appData.icsEngineAnalyze) {
\r
7977 (void) CheckMenuItem(GetMenu(hwndMain), IDM_AnalysisMode,
\r
7978 MF_BYCOMMAND|MF_CHECKED);
\r
7980 (void) CheckMenuItem(GetMenu(hwndMain), IDM_AnalysisMode,
\r
7981 MF_BYCOMMAND|MF_UNCHECKED);
\r
7989 HMENU hmenu = GetMenu(hwndMain);
\r
7990 SetMenuEnables(hmenu, icsEnables);
\r
7991 EnableMenuItem(GetSubMenu(hmenu, OPTIONS_POS), ICS_POS,
\r
7992 MF_BYPOSITION|MF_ENABLED);
\r
7994 if (appData.zippyPlay) {
\r
7995 SetMenuEnables(hmenu, zippyEnables);
\r
7996 if (!appData.noChessProgram) /* [DM] icsEngineAnalyze */
\r
7997 (void) EnableMenuItem(GetMenu(hwndMain), IDM_AnalysisMode,
\r
7998 MF_BYCOMMAND|MF_ENABLED);
\r
8006 SetMenuEnables(GetMenu(hwndMain), gnuEnables);
\r
8012 HMENU hmenu = GetMenu(hwndMain);
\r
8013 SetMenuEnables(hmenu, ncpEnables);
\r
8014 EnableMenuItem(GetSubMenu(hmenu, OPTIONS_POS), SOUNDS_POS,
\r
8015 MF_BYPOSITION|MF_GRAYED);
\r
8016 DrawMenuBar(hwndMain);
\r
8022 SetMenuEnables(GetMenu(hwndMain), cmailEnables);
\r
8026 SetTrainingModeOn()
\r
8029 SetMenuEnables(GetMenu(hwndMain), trainingOnEnables);
\r
8030 for (i = 0; i < N_BUTTONS; i++) {
\r
8031 if (buttonDesc[i].hwnd != NULL)
\r
8032 EnableWindow(buttonDesc[i].hwnd, FALSE);
\r
8037 VOID SetTrainingModeOff()
\r
8040 SetMenuEnables(GetMenu(hwndMain), trainingOffEnables);
\r
8041 for (i = 0; i < N_BUTTONS; i++) {
\r
8042 if (buttonDesc[i].hwnd != NULL)
\r
8043 EnableWindow(buttonDesc[i].hwnd, TRUE);
\r
8049 SetUserThinkingEnables()
\r
8051 SetMenuEnables(GetMenu(hwndMain), userThinkingEnables);
\r
8055 SetMachineThinkingEnables()
\r
8057 HMENU hMenu = GetMenu(hwndMain);
\r
8058 int flags = MF_BYCOMMAND|MF_ENABLED;
\r
8060 SetMenuEnables(hMenu, machineThinkingEnables);
\r
8062 if (gameMode == MachinePlaysBlack) {
\r
8063 (void)EnableMenuItem(hMenu, IDM_MachineBlack, flags);
\r
8064 } else if (gameMode == MachinePlaysWhite) {
\r
8065 (void)EnableMenuItem(hMenu, IDM_MachineWhite, flags);
\r
8066 } else if (gameMode == TwoMachinesPlay) {
\r
8067 (void)EnableMenuItem(hMenu, matchMode ? IDM_Match : IDM_TwoMachines, flags); // [HGM] match
\r
8073 DisplayTitle(char *str)
\r
8075 char title[MSG_SIZ], *host;
\r
8076 if (str[0] != NULLCHAR) {
\r
8077 safeStrCpy(title, str, sizeof(title)/sizeof(title[0]) );
\r
8078 } else if (appData.icsActive) {
\r
8079 if (appData.icsCommPort[0] != NULLCHAR)
\r
8082 host = appData.icsHost;
\r
8083 snprintf(title, MSG_SIZ, "%s: %s", szTitle, host);
\r
8084 } else if (appData.noChessProgram) {
\r
8085 safeStrCpy(title, szTitle, sizeof(title)/sizeof(title[0]) );
\r
8087 safeStrCpy(title, szTitle, sizeof(title)/sizeof(title[0]) );
\r
8088 strcat(title, ": ");
\r
8089 strcat(title, first.tidy);
\r
8091 SetWindowText(hwndMain, title);
\r
8096 DisplayMessage(char *str1, char *str2)
\r
8100 int remain = MESSAGE_TEXT_MAX - 1;
\r
8103 moveErrorMessageUp = FALSE; /* turned on later by caller if needed */
\r
8104 messageText[0] = NULLCHAR;
\r
8106 len = strlen(str1);
\r
8107 if (len > remain) len = remain;
\r
8108 strncpy(messageText, str1, len);
\r
8109 messageText[len] = NULLCHAR;
\r
8112 if (*str2 && remain >= 2) {
\r
8114 strcat(messageText, " ");
\r
8117 len = strlen(str2);
\r
8118 if (len > remain) len = remain;
\r
8119 strncat(messageText, str2, len);
\r
8121 messageText[MESSAGE_TEXT_MAX - 1] = NULLCHAR;
\r
8123 if (hwndMain == NULL || IsIconic(hwndMain)) return;
\r
8127 hdc = GetDC(hwndMain);
\r
8128 oldFont = SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);
\r
8129 ExtTextOut(hdc, messageRect.left, messageRect.top, ETO_CLIPPED|ETO_OPAQUE,
\r
8130 &messageRect, messageText, strlen(messageText), NULL);
\r
8131 (void) SelectObject(hdc, oldFont);
\r
8132 (void) ReleaseDC(hwndMain, hdc);
\r
8136 DisplayError(char *str, int error)
\r
8138 char buf[MSG_SIZ*2], buf2[MSG_SIZ];
\r
8142 safeStrCpy(buf, str, sizeof(buf)/sizeof(buf[0]) );
\r
8144 len = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,
\r
8145 NULL, error, LANG_NEUTRAL,
\r
8146 (LPSTR) buf2, MSG_SIZ, NULL);
\r
8148 snprintf(buf, 2*MSG_SIZ, "%s:\n%s", str, buf2);
\r
8150 ErrorMap *em = errmap;
\r
8151 while (em->err != 0 && em->err != error) em++;
\r
8152 if (em->err != 0) {
\r
8153 snprintf(buf, 2*MSG_SIZ, "%s:\n%s", str, em->msg);
\r
8155 snprintf(buf, 2*MSG_SIZ, "%s:\nError code %d", str, error);
\r
8160 ErrorPopUp(_("Error"), buf);
\r
8165 DisplayMoveError(char *str)
\r
8167 fromX = fromY = -1;
\r
8168 ClearHighlights();
\r
8169 DrawPosition(FALSE, NULL);
\r
8170 if (appData.popupMoveErrors) {
\r
8171 ErrorPopUp(_("Error"), str);
\r
8173 DisplayMessage(str, "");
\r
8174 moveErrorMessageUp = TRUE;
\r
8179 DisplayFatalError(char *str, int error, int exitStatus)
\r
8181 char buf[2*MSG_SIZ], buf2[MSG_SIZ];
\r
8183 char *label = exitStatus ? _("Fatal Error") : _("Exiting");
\r
8186 len = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,
\r
8187 NULL, error, LANG_NEUTRAL,
\r
8188 (LPSTR) buf2, MSG_SIZ, NULL);
\r
8190 snprintf(buf, 2*MSG_SIZ, "%s:\n%s", str, buf2);
\r
8192 ErrorMap *em = errmap;
\r
8193 while (em->err != 0 && em->err != error) em++;
\r
8194 if (em->err != 0) {
\r
8195 snprintf(buf, 2*MSG_SIZ, "%s:\n%s", str, em->msg);
\r
8197 snprintf(buf, 2*MSG_SIZ, "%s:\nError code %d", str, error);
\r
8202 if (appData.debugMode) {
\r
8203 fprintf(debugFP, "%s: %s\n", label, str);
\r
8205 if (appData.popupExitMessage) {
\r
8206 (void) MessageBox(hwndMain, str, label, MB_OK|
\r
8207 (exitStatus ? MB_ICONSTOP : MB_ICONINFORMATION));
\r
8209 ExitEvent(exitStatus);
\r
8214 DisplayInformation(char *str)
\r
8216 (void) MessageBox(hwndMain, str, _("Information"), MB_OK|MB_ICONINFORMATION);
\r
8221 DisplayNote(char *str)
\r
8223 ErrorPopUp(_("Note"), str);
\r
8228 char *title, *question, *replyPrefix;
\r
8233 QuestionDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
8235 static QuestionParams *qp;
\r
8236 char reply[MSG_SIZ];
\r
8239 switch (message) {
\r
8240 case WM_INITDIALOG:
\r
8241 qp = (QuestionParams *) lParam;
\r
8242 CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));
\r
8243 Translate(hDlg, DLG_Question);
\r
8244 SetWindowText(hDlg, qp->title);
\r
8245 SetDlgItemText(hDlg, OPT_QuestionText, qp->question);
\r
8246 SetFocus(GetDlgItem(hDlg, OPT_QuestionInput));
\r
8250 switch (LOWORD(wParam)) {
\r
8252 safeStrCpy(reply, qp->replyPrefix, sizeof(reply)/sizeof(reply[0]) );
\r
8253 if (*reply) strcat(reply, " ");
\r
8254 len = strlen(reply);
\r
8255 GetDlgItemText(hDlg, OPT_QuestionInput, reply + len, sizeof(reply) - len);
\r
8256 strcat(reply, "\n");
\r
8257 OutputToProcess(qp->pr, reply, strlen(reply), &err);
\r
8258 EndDialog(hDlg, TRUE);
\r
8259 if (err) DisplayFatalError(_("Error writing to chess program"), err, 1);
\r
8262 EndDialog(hDlg, FALSE);
\r
8273 AskQuestion(char* title, char *question, char *replyPrefix, ProcRef pr)
\r
8275 QuestionParams qp;
\r
8279 qp.question = question;
\r
8280 qp.replyPrefix = replyPrefix;
\r
8282 lpProc = MakeProcInstance((FARPROC)QuestionDialog, hInst);
\r
8283 DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_Question),
\r
8284 hwndMain, (DLGPROC)lpProc, (LPARAM)&qp);
\r
8285 FreeProcInstance(lpProc);
\r
8288 /* [AS] Pick FRC position */
\r
8289 LRESULT CALLBACK NewGameFRC_Proc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
8291 static int * lpIndexFRC;
\r
8297 case WM_INITDIALOG:
\r
8298 lpIndexFRC = (int *) lParam;
\r
8300 CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));
\r
8301 Translate(hDlg, DLG_NewGameFRC);
\r
8303 SendDlgItemMessage( hDlg, IDC_NFG_Edit, EM_SETLIMITTEXT, sizeof(buf)-1, 0 );
\r
8304 SetDlgItemInt( hDlg, IDC_NFG_Edit, *lpIndexFRC, TRUE );
\r
8305 SendDlgItemMessage( hDlg, IDC_NFG_Edit, EM_SETSEL, 0, -1 );
\r
8306 SetFocus(GetDlgItem(hDlg, IDC_NFG_Edit));
\r
8311 switch( LOWORD(wParam) ) {
\r
8313 *lpIndexFRC = GetDlgItemInt(hDlg, IDC_NFG_Edit, &index_is_ok, TRUE );
\r
8314 EndDialog( hDlg, 0 );
\r
8315 shuffleOpenings = TRUE; /* [HGM] shuffle: switch shuffling on for as long as we stay in current variant */
\r
8318 EndDialog( hDlg, 1 );
\r
8320 case IDC_NFG_Edit:
\r
8321 if( HIWORD(wParam) == EN_CHANGE ) {
\r
8322 GetDlgItemInt(hDlg, IDC_NFG_Edit, &index_is_ok, TRUE );
\r
8324 EnableWindow( GetDlgItem(hDlg, IDOK), index_is_ok );
\r
8327 case IDC_NFG_Random:
\r
8328 snprintf( buf, sizeof(buf)/sizeof(buf[0]), "%d", myrandom() ); /* [HGM] shuffle: no longer limit to 960 */
\r
8329 SetDlgItemText(hDlg, IDC_NFG_Edit, buf );
\r
8342 int index = appData.defaultFrcPosition;
\r
8343 FARPROC lpProc = MakeProcInstance( (FARPROC) NewGameFRC_Proc, hInst );
\r
8345 result = DialogBoxParam( hInst, MAKEINTRESOURCE(DLG_NewGameFRC), hwndMain, (DLGPROC)lpProc, (LPARAM)&index );
\r
8347 if( result == 0 ) {
\r
8348 appData.defaultFrcPosition = index;
\r
8354 /* [AS] Game list options. Refactored by HGM */
\r
8356 HWND gameListOptionsDialog;
\r
8358 // low-level front-end: clear text edit / list widget
\r
8362 SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_RESETCONTENT, 0, 0 );
\r
8365 // low-level front-end: clear text edit / list widget
\r
8367 GLT_DeSelectList()
\r
8369 SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_SETCURSEL, 0, 0 );
\r
8372 // low-level front-end: append line to text edit / list widget
\r
8374 GLT_AddToList( char *name )
\r
8377 SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_ADDSTRING, 0, (LPARAM) name );
\r
8381 // low-level front-end: get line from text edit / list widget
\r
8383 GLT_GetFromList( int index, char *name )
\r
8386 if( SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_GETTEXT, index, (LPARAM) name ) != LB_ERR )
\r
8392 void GLT_MoveSelection( HWND hDlg, int delta )
\r
8394 int idx1 = (int) SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETCURSEL, 0, 0 );
\r
8395 int idx2 = idx1 + delta;
\r
8396 int count = (int) SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETCOUNT, 0, 0 );
\r
8398 if( idx1 >=0 && idx1 < count && idx2 >= 0 && idx2 < count ) {
\r
8401 SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETTEXT, idx1, (LPARAM) buf );
\r
8402 SendDlgItemMessage( hDlg, IDC_GameListTags, LB_DELETESTRING, idx1, 0 );
\r
8403 SendDlgItemMessage( hDlg, IDC_GameListTags, LB_INSERTSTRING, idx2, (LPARAM) buf );
\r
8404 SendDlgItemMessage( hDlg, IDC_GameListTags, LB_SETCURSEL, idx2, 0 );
\r
8408 LRESULT CALLBACK GameListOptions_Proc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
8412 case WM_INITDIALOG:
\r
8413 gameListOptionsDialog = hDlg; // [HGM] pass through global to keep out off back-end
\r
8415 CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));
\r
8416 Translate(hDlg, DLG_GameListOptions);
\r
8418 /* Initialize list */
\r
8419 GLT_TagsToList( lpUserGLT );
\r
8421 SetFocus( GetDlgItem(hDlg, IDC_GameListTags) );
\r
8426 switch( LOWORD(wParam) ) {
\r
8429 EndDialog( hDlg, 0 );
\r
8432 EndDialog( hDlg, 1 );
\r
8435 case IDC_GLT_Default:
\r
8436 GLT_TagsToList( GLT_DEFAULT_TAGS );
\r
8439 case IDC_GLT_Restore:
\r
8440 GLT_TagsToList( appData.gameListTags );
\r
8444 GLT_MoveSelection( hDlg, -1 );
\r
8447 case IDC_GLT_Down:
\r
8448 GLT_MoveSelection( hDlg, +1 );
\r
8458 int GameListOptions()
\r
8461 FARPROC lpProc = MakeProcInstance( (FARPROC) GameListOptions_Proc, hInst );
\r
8463 safeStrCpy( lpUserGLT, appData.gameListTags ,LPUSERGLT_SIZE );
\r
8465 result = DialogBoxParam( hInst, MAKEINTRESOURCE(DLG_GameListOptions), hwndMain, (DLGPROC)lpProc, (LPARAM)lpUserGLT );
\r
8467 if( result == 0 ) {
\r
8468 /* [AS] Memory leak here! */
\r
8469 appData.gameListTags = strdup( lpUserGLT );
\r
8476 DisplayIcsInteractionTitle(char *str)
\r
8478 char consoleTitle[MSG_SIZ];
\r
8480 snprintf(consoleTitle, MSG_SIZ, "%s: %s", szConsoleTitle, str);
\r
8481 SetWindowText(hwndConsole, consoleTitle);
\r
8485 DrawPosition(int fullRedraw, Board board)
\r
8487 HDCDrawPosition(NULL, (BOOLEAN) fullRedraw, board);
\r
8490 void NotifyFrontendLogin()
\r
8493 UpdateICSWidth(GetDlgItem(hwndConsole, OPT_ConsoleText));
\r
8499 fromX = fromY = -1;
\r
8500 if (dragInfo.pos.x != -1 || dragInfo.pos.y != -1) {
\r
8501 dragInfo.pos.x = dragInfo.pos.y = -1;
\r
8502 dragInfo.pos.x = dragInfo.pos.y = -1;
\r
8503 dragInfo.lastpos = dragInfo.pos;
\r
8504 dragInfo.start.x = dragInfo.start.y = -1;
\r
8505 dragInfo.from = dragInfo.start;
\r
8507 DrawPosition(TRUE, NULL);
\r
8514 CommentPopUp(char *title, char *str)
\r
8516 HWND hwnd = GetActiveWindow();
\r
8517 EitherCommentPopUp(currentMove, title, str, FALSE); // [HGM] vari: fake move index, rather than 0
\r
8519 SetActiveWindow(hwnd);
\r
8523 CommentPopDown(void)
\r
8525 CheckMenuItem(GetMenu(hwndMain), IDM_Comment, MF_UNCHECKED);
\r
8526 if (commentDialog) {
\r
8527 ShowWindow(commentDialog, SW_HIDE);
\r
8529 commentUp = FALSE;
\r
8533 EditCommentPopUp(int index, char *title, char *str)
\r
8535 EitherCommentPopUp(index, title, str, TRUE);
\r
8542 MyPlaySound(&sounds[(int)SoundMove]);
\r
8545 VOID PlayIcsWinSound()
\r
8547 MyPlaySound(&sounds[(int)SoundIcsWin]);
\r
8550 VOID PlayIcsLossSound()
\r
8552 MyPlaySound(&sounds[(int)SoundIcsLoss]);
\r
8555 VOID PlayIcsDrawSound()
\r
8557 MyPlaySound(&sounds[(int)SoundIcsDraw]);
\r
8560 VOID PlayIcsUnfinishedSound()
\r
8562 MyPlaySound(&sounds[(int)SoundIcsUnfinished]);
\r
8568 MyPlaySound(&sounds[(int)SoundAlarm]);
\r
8576 consoleEcho = TRUE;
\r
8577 hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
8578 SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&consoleCF);
\r
8579 SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);
\r
8588 consoleEcho = FALSE;
\r
8589 hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
8590 /* This works OK: set text and background both to the same color */
\r
8592 cf.crTextColor = COLOR_ECHOOFF;
\r
8593 SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf);
\r
8594 SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, cf.crTextColor);
\r
8597 /* No Raw()...? */
\r
8599 void Colorize(ColorClass cc, int continuation)
\r
8601 currentColorClass = cc;
\r
8602 consoleCF.dwMask = CFM_COLOR|CFM_BOLD|CFM_ITALIC|CFM_UNDERLINE|CFM_STRIKEOUT;
\r
8603 consoleCF.crTextColor = textAttribs[cc].color;
\r
8604 consoleCF.dwEffects = textAttribs[cc].effects;
\r
8605 if (!continuation) MyPlaySound(&textAttribs[cc].sound);
\r
8611 static char buf[MSG_SIZ];
\r
8612 DWORD bufsiz = MSG_SIZ;
\r
8614 if(appData.userName != NULL && appData.userName[0] != 0) {
\r
8615 return appData.userName; /* [HGM] username: prefer name selected by user over his system login */
\r
8617 if (!GetUserName(buf, &bufsiz)) {
\r
8618 /*DisplayError("Error getting user name", GetLastError());*/
\r
8619 safeStrCpy(buf, _("User"), sizeof(buf)/sizeof(buf[0]) );
\r
8627 static char buf[MSG_SIZ];
\r
8628 DWORD bufsiz = MSG_SIZ;
\r
8630 if (!GetComputerName(buf, &bufsiz)) {
\r
8631 /*DisplayError("Error getting host name", GetLastError());*/
\r
8632 safeStrCpy(buf, _("Unknown"), sizeof(buf)/sizeof(buf[0]) );
\r
8639 ClockTimerRunning()
\r
8641 return clockTimerEvent != 0;
\r
8647 if (clockTimerEvent == 0) return FALSE;
\r
8648 KillTimer(hwndMain, clockTimerEvent);
\r
8649 clockTimerEvent = 0;
\r
8654 StartClockTimer(long millisec)
\r
8656 clockTimerEvent = SetTimer(hwndMain, (UINT) CLOCK_TIMER_ID,
\r
8657 (UINT) millisec, NULL);
\r
8661 DisplayWhiteClock(long timeRemaining, int highlight)
\r
8664 char *flag = whiteFlag && gameMode == TwoMachinesPlay ? "(!)" : "";
\r
8666 if(appData.noGUI) return;
\r
8667 hdc = GetDC(hwndMain);
\r
8668 if (!IsIconic(hwndMain)) {
\r
8669 DisplayAClock(hdc, timeRemaining, highlight,
\r
8670 flipClock ? &blackRect : &whiteRect, _("White"), flag);
\r
8672 if (highlight && iconCurrent == iconBlack) {
\r
8673 iconCurrent = iconWhite;
\r
8674 PostMessage(hwndMain, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);
\r
8675 if (IsIconic(hwndMain)) {
\r
8676 DrawIcon(hdc, 2, 2, iconCurrent);
\r
8679 (void) ReleaseDC(hwndMain, hdc);
\r
8681 PostMessage(hwndConsole, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);
\r
8685 DisplayBlackClock(long timeRemaining, int highlight)
\r
8688 char *flag = blackFlag && gameMode == TwoMachinesPlay ? "(!)" : "";
\r
8690 if(appData.noGUI) return;
\r
8691 hdc = GetDC(hwndMain);
\r
8692 if (!IsIconic(hwndMain)) {
\r
8693 DisplayAClock(hdc, timeRemaining, highlight,
\r
8694 flipClock ? &whiteRect : &blackRect, _("Black"), flag);
\r
8696 if (highlight && iconCurrent == iconWhite) {
\r
8697 iconCurrent = iconBlack;
\r
8698 PostMessage(hwndMain, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);
\r
8699 if (IsIconic(hwndMain)) {
\r
8700 DrawIcon(hdc, 2, 2, iconCurrent);
\r
8703 (void) ReleaseDC(hwndMain, hdc);
\r
8705 PostMessage(hwndConsole, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);
\r
8710 LoadGameTimerRunning()
\r
8712 return loadGameTimerEvent != 0;
\r
8716 StopLoadGameTimer()
\r
8718 if (loadGameTimerEvent == 0) return FALSE;
\r
8719 KillTimer(hwndMain, loadGameTimerEvent);
\r
8720 loadGameTimerEvent = 0;
\r
8725 StartLoadGameTimer(long millisec)
\r
8727 loadGameTimerEvent = SetTimer(hwndMain, (UINT) LOAD_GAME_TIMER_ID,
\r
8728 (UINT) millisec, NULL);
\r
8736 char fileTitle[MSG_SIZ];
\r
8738 defName = DefaultFileName(appData.oldSaveStyle ? "gam" : "pgn");
\r
8739 f = OpenFileDialog(hwndMain, "a", defName,
\r
8740 appData.oldSaveStyle ? "gam" : "pgn",
\r
8742 _("Save Game to File"), NULL, fileTitle, NULL);
\r
8744 SaveGame(f, 0, "");
\r
8751 ScheduleDelayedEvent(DelayedEventCallback cb, long millisec)
\r
8753 if (delayedTimerEvent != 0) {
\r
8754 if (appData.debugMode && cb != delayedTimerCallback) { // [HGM] alive: not too much debug
\r
8755 fprintf(debugFP, "ScheduleDelayedEvent: event already scheduled\n");
\r
8757 KillTimer(hwndMain, delayedTimerEvent);
\r
8758 delayedTimerEvent = 0;
\r
8759 if(delayedTimerCallback != cb) // [HGM] alive: do not "flush" same event, just postpone it
\r
8760 delayedTimerCallback();
\r
8762 delayedTimerCallback = cb;
\r
8763 delayedTimerEvent = SetTimer(hwndMain, (UINT) DELAYED_TIMER_ID,
\r
8764 (UINT) millisec, NULL);
\r
8767 DelayedEventCallback
\r
8770 if (delayedTimerEvent) {
\r
8771 return delayedTimerCallback;
\r
8778 CancelDelayedEvent()
\r
8780 if (delayedTimerEvent) {
\r
8781 KillTimer(hwndMain, delayedTimerEvent);
\r
8782 delayedTimerEvent = 0;
\r
8786 DWORD GetWin32Priority(int nice)
\r
8787 { // [HGM] nice: translate Unix nice() value to indows priority class. (Code stolen from Polyglot 1.4w11)
\r
8789 REALTIME_PRIORITY_CLASS 0x00000100
\r
8790 HIGH_PRIORITY_CLASS 0x00000080
\r
8791 ABOVE_NORMAL_PRIORITY_CLASS 0x00008000
\r
8792 NORMAL_PRIORITY_CLASS 0x00000020
\r
8793 BELOW_NORMAL_PRIORITY_CLASS 0x00004000
\r
8794 IDLE_PRIORITY_CLASS 0x00000040
\r
8796 if (nice < -15) return 0x00000080;
\r
8797 if (nice < 0) return 0x00008000;
\r
8798 if (nice == 0) return 0x00000020;
\r
8799 if (nice < 15) return 0x00004000;
\r
8800 return 0x00000040;
\r
8803 /* Start a child process running the given program.
\r
8804 The process's standard output can be read from "from", and its
\r
8805 standard input can be written to "to".
\r
8806 Exit with fatal error if anything goes wrong.
\r
8807 Returns an opaque pointer that can be used to destroy the process
\r
8811 StartChildProcess(char *cmdLine, char *dir, ProcRef *pr)
\r
8813 #define BUFSIZE 4096
\r
8815 HANDLE hChildStdinRd, hChildStdinWr,
\r
8816 hChildStdoutRd, hChildStdoutWr;
\r
8817 HANDLE hChildStdinWrDup, hChildStdoutRdDup;
\r
8818 SECURITY_ATTRIBUTES saAttr;
\r
8820 PROCESS_INFORMATION piProcInfo;
\r
8821 STARTUPINFO siStartInfo;
\r
8823 char buf[MSG_SIZ];
\r
8826 if (appData.debugMode) {
\r
8827 fprintf(debugFP, "StartChildProcess (dir=\"%s\") %s\n", dir, cmdLine);
\r
8832 /* Set the bInheritHandle flag so pipe handles are inherited. */
\r
8833 saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);
\r
8834 saAttr.bInheritHandle = TRUE;
\r
8835 saAttr.lpSecurityDescriptor = NULL;
\r
8838 * The steps for redirecting child's STDOUT:
\r
8839 * 1. Create anonymous pipe to be STDOUT for child.
\r
8840 * 2. Create a noninheritable duplicate of read handle,
\r
8841 * and close the inheritable read handle.
\r
8844 /* Create a pipe for the child's STDOUT. */
\r
8845 if (! CreatePipe(&hChildStdoutRd, &hChildStdoutWr, &saAttr, 0)) {
\r
8846 return GetLastError();
\r
8849 /* Duplicate the read handle to the pipe, so it is not inherited. */
\r
8850 fSuccess = DuplicateHandle(GetCurrentProcess(), hChildStdoutRd,
\r
8851 GetCurrentProcess(), &hChildStdoutRdDup, 0,
\r
8852 FALSE, /* not inherited */
\r
8853 DUPLICATE_SAME_ACCESS);
\r
8855 return GetLastError();
\r
8857 CloseHandle(hChildStdoutRd);
\r
8860 * The steps for redirecting child's STDIN:
\r
8861 * 1. Create anonymous pipe to be STDIN for child.
\r
8862 * 2. Create a noninheritable duplicate of write handle,
\r
8863 * and close the inheritable write handle.
\r
8866 /* Create a pipe for the child's STDIN. */
\r
8867 if (! CreatePipe(&hChildStdinRd, &hChildStdinWr, &saAttr, 0)) {
\r
8868 return GetLastError();
\r
8871 /* Duplicate the write handle to the pipe, so it is not inherited. */
\r
8872 fSuccess = DuplicateHandle(GetCurrentProcess(), hChildStdinWr,
\r
8873 GetCurrentProcess(), &hChildStdinWrDup, 0,
\r
8874 FALSE, /* not inherited */
\r
8875 DUPLICATE_SAME_ACCESS);
\r
8877 return GetLastError();
\r
8879 CloseHandle(hChildStdinWr);
\r
8881 /* Arrange to (1) look in dir for the child .exe file, and
\r
8882 * (2) have dir be the child's working directory. Interpret
\r
8883 * dir relative to the directory WinBoard loaded from. */
\r
8884 GetCurrentDirectory(MSG_SIZ, buf);
\r
8885 SetCurrentDirectory(installDir);
\r
8886 SetCurrentDirectory(dir);
\r
8888 /* Now create the child process. */
\r
8890 siStartInfo.cb = sizeof(STARTUPINFO);
\r
8891 siStartInfo.lpReserved = NULL;
\r
8892 siStartInfo.lpDesktop = NULL;
\r
8893 siStartInfo.lpTitle = NULL;
\r
8894 siStartInfo.dwFlags = STARTF_USESTDHANDLES;
\r
8895 siStartInfo.cbReserved2 = 0;
\r
8896 siStartInfo.lpReserved2 = NULL;
\r
8897 siStartInfo.hStdInput = hChildStdinRd;
\r
8898 siStartInfo.hStdOutput = hChildStdoutWr;
\r
8899 siStartInfo.hStdError = hChildStdoutWr;
\r
8901 fSuccess = CreateProcess(NULL,
\r
8902 cmdLine, /* command line */
\r
8903 NULL, /* process security attributes */
\r
8904 NULL, /* primary thread security attrs */
\r
8905 TRUE, /* handles are inherited */
\r
8906 DETACHED_PROCESS|CREATE_NEW_PROCESS_GROUP,
\r
8907 NULL, /* use parent's environment */
\r
8909 &siStartInfo, /* STARTUPINFO pointer */
\r
8910 &piProcInfo); /* receives PROCESS_INFORMATION */
\r
8912 err = GetLastError();
\r
8913 SetCurrentDirectory(buf); /* return to prev directory */
\r
8918 if (appData.niceEngines){ // [HGM] nice: adjust engine proc priority
\r
8919 if(appData.debugMode) fprintf(debugFP, "nice engine proc to %d\n", appData.niceEngines);
\r
8920 SetPriorityClass(piProcInfo.hProcess, GetWin32Priority(appData.niceEngines));
\r
8923 /* Close the handles we don't need in the parent */
\r
8924 CloseHandle(piProcInfo.hThread);
\r
8925 CloseHandle(hChildStdinRd);
\r
8926 CloseHandle(hChildStdoutWr);
\r
8928 /* Prepare return value */
\r
8929 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
\r
8930 cp->kind = CPReal;
\r
8931 cp->hProcess = piProcInfo.hProcess;
\r
8932 cp->pid = piProcInfo.dwProcessId;
\r
8933 cp->hFrom = hChildStdoutRdDup;
\r
8934 cp->hTo = hChildStdinWrDup;
\r
8936 *pr = (void *) cp;
\r
8938 /* Klaus Friedel says that this Sleep solves a problem under Windows
\r
8939 2000 where engines sometimes don't see the initial command(s)
\r
8940 from WinBoard and hang. I don't understand how that can happen,
\r
8941 but the Sleep is harmless, so I've put it in. Others have also
\r
8942 reported what may be the same problem, so hopefully this will fix
\r
8943 it for them too. */
\r
8951 DestroyChildProcess(ProcRef pr, int/*boolean*/ signal)
\r
8953 ChildProc *cp; int result;
\r
8955 cp = (ChildProc *) pr;
\r
8956 if (cp == NULL) return;
\r
8958 switch (cp->kind) {
\r
8960 /* TerminateProcess is considered harmful, so... */
\r
8961 CloseHandle(cp->hTo); /* Closing this will give the child an EOF and hopefully kill it */
\r
8962 if (cp->hFrom) CloseHandle(cp->hFrom); /* if NULL, InputThread will close it */
\r
8963 /* The following doesn't work because the chess program
\r
8964 doesn't "have the same console" as WinBoard. Maybe
\r
8965 we could arrange for this even though neither WinBoard
\r
8966 nor the chess program uses a console for stdio? */
\r
8967 /*!!if (signal) GenerateConsoleCtrlEvent(CTRL_BREAK_EVENT, cp->pid);*/
\r
8969 /* [AS] Special termination modes for misbehaving programs... */
\r
8970 if( signal == 9 ) {
\r
8971 result = TerminateProcess( cp->hProcess, 0 );
\r
8973 if ( appData.debugMode) {
\r
8974 fprintf( debugFP, "Terminating process %lu, result=%d\n", cp->pid, result );
\r
8977 else if( signal == 10 ) {
\r
8978 DWORD dw = WaitForSingleObject( cp->hProcess, 3*1000 ); // Wait 3 seconds at most
\r
8980 if( dw != WAIT_OBJECT_0 ) {
\r
8981 result = TerminateProcess( cp->hProcess, 0 );
\r
8983 if ( appData.debugMode) {
\r
8984 fprintf( debugFP, "Process %lu still alive after timeout, killing... result=%d\n", cp->pid, result );
\r
8990 CloseHandle(cp->hProcess);
\r
8994 if (cp->hFrom) CloseHandle(cp->hFrom);
\r
8998 closesocket(cp->sock);
\r
9003 if (signal) send(cp->sock2, "\017", 1, 0); /* 017 = 15 = SIGTERM */
\r
9004 closesocket(cp->sock);
\r
9005 closesocket(cp->sock2);
\r
9013 InterruptChildProcess(ProcRef pr)
\r
9017 cp = (ChildProc *) pr;
\r
9018 if (cp == NULL) return;
\r
9019 switch (cp->kind) {
\r
9021 /* The following doesn't work because the chess program
\r
9022 doesn't "have the same console" as WinBoard. Maybe
\r
9023 we could arrange for this even though neither WinBoard
\r
9024 nor the chess program uses a console for stdio */
\r
9025 /*!!GenerateConsoleCtrlEvent(CTRL_C_EVENT, cp->pid);*/
\r
9030 /* Can't interrupt */
\r
9034 send(cp->sock2, "\002", 1, 0); /* 2 = SIGINT */
\r
9041 OpenTelnet(char *host, char *port, ProcRef *pr)
\r
9043 char cmdLine[MSG_SIZ];
\r
9045 if (port[0] == NULLCHAR) {
\r
9046 snprintf(cmdLine, MSG_SIZ, "%s %s", appData.telnetProgram, host);
\r
9048 snprintf(cmdLine, MSG_SIZ, "%s %s %s", appData.telnetProgram, host, port);
\r
9050 return StartChildProcess(cmdLine, "", pr);
\r
9054 /* Code to open TCP sockets */
\r
9057 OpenTCP(char *host, char *port, ProcRef *pr)
\r
9062 struct sockaddr_in sa, mysa;
\r
9063 struct hostent FAR *hp;
\r
9064 unsigned short uport;
\r
9065 WORD wVersionRequested;
\r
9068 /* Initialize socket DLL */
\r
9069 wVersionRequested = MAKEWORD(1, 1);
\r
9070 err = WSAStartup(wVersionRequested, &wsaData);
\r
9071 if (err != 0) return err;
\r
9074 if ((s = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {
\r
9075 err = WSAGetLastError();
\r
9080 /* Bind local address using (mostly) don't-care values.
\r
9082 memset((char *) &mysa, 0, sizeof(struct sockaddr_in));
\r
9083 mysa.sin_family = AF_INET;
\r
9084 mysa.sin_addr.s_addr = INADDR_ANY;
\r
9085 uport = (unsigned short) 0;
\r
9086 mysa.sin_port = htons(uport);
\r
9087 if (bind(s, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))
\r
9088 == SOCKET_ERROR) {
\r
9089 err = WSAGetLastError();
\r
9094 /* Resolve remote host name */
\r
9095 memset((char *) &sa, 0, sizeof(struct sockaddr_in));
\r
9096 if (!(hp = gethostbyname(host))) {
\r
9097 unsigned int b0, b1, b2, b3;
\r
9099 err = WSAGetLastError();
\r
9101 if (sscanf(host, "%u.%u.%u.%u", &b0, &b1, &b2, &b3) == 4) {
\r
9102 hp = (struct hostent *) calloc(1, sizeof(struct hostent));
\r
9103 hp->h_addrtype = AF_INET;
\r
9105 hp->h_addr_list = (char **) calloc(2, sizeof(char *));
\r
9106 hp->h_addr_list[0] = (char *) malloc(4);
\r
9107 hp->h_addr_list[0][0] = (char) b0;
\r
9108 hp->h_addr_list[0][1] = (char) b1;
\r
9109 hp->h_addr_list[0][2] = (char) b2;
\r
9110 hp->h_addr_list[0][3] = (char) b3;
\r
9116 sa.sin_family = hp->h_addrtype;
\r
9117 uport = (unsigned short) atoi(port);
\r
9118 sa.sin_port = htons(uport);
\r
9119 memcpy((char *) &sa.sin_addr, hp->h_addr, hp->h_length);
\r
9121 /* Make connection */
\r
9122 if (connect(s, (struct sockaddr *) &sa,
\r
9123 sizeof(struct sockaddr_in)) == SOCKET_ERROR) {
\r
9124 err = WSAGetLastError();
\r
9129 /* Prepare return value */
\r
9130 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
\r
9131 cp->kind = CPSock;
\r
9133 *pr = (ProcRef *) cp;
\r
9139 OpenCommPort(char *name, ProcRef *pr)
\r
9144 char fullname[MSG_SIZ];
\r
9146 if (*name != '\\')
\r
9147 snprintf(fullname, MSG_SIZ, "\\\\.\\%s", name);
\r
9149 safeStrCpy(fullname, name, sizeof(fullname)/sizeof(fullname[0]) );
\r
9151 h = CreateFile(name, GENERIC_READ | GENERIC_WRITE,
\r
9152 0, NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL);
\r
9153 if (h == (HANDLE) -1) {
\r
9154 return GetLastError();
\r
9158 if (!SetCommState(h, (LPDCB) &dcb)) return GetLastError();
\r
9160 /* Accumulate characters until a 100ms pause, then parse */
\r
9161 ct.ReadIntervalTimeout = 100;
\r
9162 ct.ReadTotalTimeoutMultiplier = 0;
\r
9163 ct.ReadTotalTimeoutConstant = 0;
\r
9164 ct.WriteTotalTimeoutMultiplier = 0;
\r
9165 ct.WriteTotalTimeoutConstant = 0;
\r
9166 if (!SetCommTimeouts(h, (LPCOMMTIMEOUTS) &ct)) return GetLastError();
\r
9168 /* Prepare return value */
\r
9169 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
\r
9170 cp->kind = CPComm;
\r
9173 *pr = (ProcRef *) cp;
\r
9179 OpenLoopback(ProcRef *pr)
\r
9181 DisplayFatalError(_("Not implemented"), 0, 1);
\r
9187 OpenRcmd(char* host, char* user, char* cmd, ProcRef* pr)
\r
9192 struct sockaddr_in sa, mysa;
\r
9193 struct hostent FAR *hp;
\r
9194 unsigned short uport;
\r
9195 WORD wVersionRequested;
\r
9198 char stderrPortStr[MSG_SIZ];
\r
9200 /* Initialize socket DLL */
\r
9201 wVersionRequested = MAKEWORD(1, 1);
\r
9202 err = WSAStartup(wVersionRequested, &wsaData);
\r
9203 if (err != 0) return err;
\r
9205 /* Resolve remote host name */
\r
9206 memset((char *) &sa, 0, sizeof(struct sockaddr_in));
\r
9207 if (!(hp = gethostbyname(host))) {
\r
9208 unsigned int b0, b1, b2, b3;
\r
9210 err = WSAGetLastError();
\r
9212 if (sscanf(host, "%u.%u.%u.%u", &b0, &b1, &b2, &b3) == 4) {
\r
9213 hp = (struct hostent *) calloc(1, sizeof(struct hostent));
\r
9214 hp->h_addrtype = AF_INET;
\r
9216 hp->h_addr_list = (char **) calloc(2, sizeof(char *));
\r
9217 hp->h_addr_list[0] = (char *) malloc(4);
\r
9218 hp->h_addr_list[0][0] = (char) b0;
\r
9219 hp->h_addr_list[0][1] = (char) b1;
\r
9220 hp->h_addr_list[0][2] = (char) b2;
\r
9221 hp->h_addr_list[0][3] = (char) b3;
\r
9227 sa.sin_family = hp->h_addrtype;
\r
9228 uport = (unsigned short) 514;
\r
9229 sa.sin_port = htons(uport);
\r
9230 memcpy((char *) &sa.sin_addr, hp->h_addr, hp->h_length);
\r
9232 /* Bind local socket to unused "privileged" port address
\r
9234 s = INVALID_SOCKET;
\r
9235 memset((char *) &mysa, 0, sizeof(struct sockaddr_in));
\r
9236 mysa.sin_family = AF_INET;
\r
9237 mysa.sin_addr.s_addr = INADDR_ANY;
\r
9238 for (fromPort = 1023;; fromPort--) {
\r
9239 if (fromPort < 0) {
\r
9241 return WSAEADDRINUSE;
\r
9243 if (s == INVALID_SOCKET) {
\r
9244 if ((s = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {
\r
9245 err = WSAGetLastError();
\r
9250 uport = (unsigned short) fromPort;
\r
9251 mysa.sin_port = htons(uport);
\r
9252 if (bind(s, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))
\r
9253 == SOCKET_ERROR) {
\r
9254 err = WSAGetLastError();
\r
9255 if (err == WSAEADDRINUSE) continue;
\r
9259 if (connect(s, (struct sockaddr *) &sa,
\r
9260 sizeof(struct sockaddr_in)) == SOCKET_ERROR) {
\r
9261 err = WSAGetLastError();
\r
9262 if (err == WSAEADDRINUSE) {
\r
9273 /* Bind stderr local socket to unused "privileged" port address
\r
9275 s2 = INVALID_SOCKET;
\r
9276 memset((char *) &mysa, 0, sizeof(struct sockaddr_in));
\r
9277 mysa.sin_family = AF_INET;
\r
9278 mysa.sin_addr.s_addr = INADDR_ANY;
\r
9279 for (fromPort = 1023;; fromPort--) {
\r
9280 if (fromPort == prevStderrPort) continue; // don't reuse port
\r
9281 if (fromPort < 0) {
\r
9282 (void) closesocket(s);
\r
9284 return WSAEADDRINUSE;
\r
9286 if (s2 == INVALID_SOCKET) {
\r
9287 if ((s2 = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {
\r
9288 err = WSAGetLastError();
\r
9294 uport = (unsigned short) fromPort;
\r
9295 mysa.sin_port = htons(uport);
\r
9296 if (bind(s2, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))
\r
9297 == SOCKET_ERROR) {
\r
9298 err = WSAGetLastError();
\r
9299 if (err == WSAEADDRINUSE) continue;
\r
9300 (void) closesocket(s);
\r
9304 if (listen(s2, 1) == SOCKET_ERROR) {
\r
9305 err = WSAGetLastError();
\r
9306 if (err == WSAEADDRINUSE) {
\r
9308 s2 = INVALID_SOCKET;
\r
9311 (void) closesocket(s);
\r
9312 (void) closesocket(s2);
\r
9318 prevStderrPort = fromPort; // remember port used
\r
9319 snprintf(stderrPortStr, MSG_SIZ, "%d", fromPort);
\r
9321 if (send(s, stderrPortStr, strlen(stderrPortStr) + 1, 0) == SOCKET_ERROR) {
\r
9322 err = WSAGetLastError();
\r
9323 (void) closesocket(s);
\r
9324 (void) closesocket(s2);
\r
9329 if (send(s, UserName(), strlen(UserName()) + 1, 0) == SOCKET_ERROR) {
\r
9330 err = WSAGetLastError();
\r
9331 (void) closesocket(s);
\r
9332 (void) closesocket(s2);
\r
9336 if (*user == NULLCHAR) user = UserName();
\r
9337 if (send(s, user, strlen(user) + 1, 0) == SOCKET_ERROR) {
\r
9338 err = WSAGetLastError();
\r
9339 (void) closesocket(s);
\r
9340 (void) closesocket(s2);
\r
9344 if (send(s, cmd, strlen(cmd) + 1, 0) == SOCKET_ERROR) {
\r
9345 err = WSAGetLastError();
\r
9346 (void) closesocket(s);
\r
9347 (void) closesocket(s2);
\r
9352 if ((s3 = accept(s2, NULL, NULL)) == INVALID_SOCKET) {
\r
9353 err = WSAGetLastError();
\r
9354 (void) closesocket(s);
\r
9355 (void) closesocket(s2);
\r
9359 (void) closesocket(s2); /* Stop listening */
\r
9361 /* Prepare return value */
\r
9362 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
\r
9363 cp->kind = CPRcmd;
\r
9366 *pr = (ProcRef *) cp;
\r
9373 AddInputSource(ProcRef pr, int lineByLine,
\r
9374 InputCallback func, VOIDSTAR closure)
\r
9376 InputSource *is, *is2 = NULL;
\r
9377 ChildProc *cp = (ChildProc *) pr;
\r
9379 is = (InputSource *) calloc(1, sizeof(InputSource));
\r
9380 is->lineByLine = lineByLine;
\r
9382 is->closure = closure;
\r
9383 is->second = NULL;
\r
9384 is->next = is->buf;
\r
9385 if (pr == NoProc) {
\r
9386 is->kind = CPReal;
\r
9387 consoleInputSource = is;
\r
9389 is->kind = cp->kind;
\r
9391 [AS] Try to avoid a race condition if the thread is given control too early:
\r
9392 we create all threads suspended so that the is->hThread variable can be
\r
9393 safely assigned, then let the threads start with ResumeThread.
\r
9395 switch (cp->kind) {
\r
9397 is->hFile = cp->hFrom;
\r
9398 cp->hFrom = NULL; /* now owned by InputThread */
\r
9400 CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) NonOvlInputThread,
\r
9401 (LPVOID) is, CREATE_SUSPENDED, &is->id);
\r
9405 is->hFile = cp->hFrom;
\r
9406 cp->hFrom = NULL; /* now owned by InputThread */
\r
9408 CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) InputThread,
\r
9409 (LPVOID) is, CREATE_SUSPENDED, &is->id);
\r
9413 is->sock = cp->sock;
\r
9415 CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,
\r
9416 (LPVOID) is, CREATE_SUSPENDED, &is->id);
\r
9420 is2 = (InputSource *) calloc(1, sizeof(InputSource));
\r
9422 is->sock = cp->sock;
\r
9424 is2->sock = cp->sock2;
\r
9425 is2->second = is2;
\r
9427 CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,
\r
9428 (LPVOID) is, CREATE_SUSPENDED, &is->id);
\r
9430 CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,
\r
9431 (LPVOID) is2, CREATE_SUSPENDED, &is2->id);
\r
9435 if( is->hThread != NULL ) {
\r
9436 ResumeThread( is->hThread );
\r
9439 if( is2 != NULL && is2->hThread != NULL ) {
\r
9440 ResumeThread( is2->hThread );
\r
9444 return (InputSourceRef) is;
\r
9448 RemoveInputSource(InputSourceRef isr)
\r
9452 is = (InputSource *) isr;
\r
9453 is->hThread = NULL; /* tell thread to stop */
\r
9454 CloseHandle(is->hThread);
\r
9455 if (is->second != NULL) {
\r
9456 is->second->hThread = NULL;
\r
9457 CloseHandle(is->second->hThread);
\r
9461 int no_wrap(char *message, int count)
\r
9463 ConsoleOutput(message, count, FALSE);
\r
9468 OutputToProcess(ProcRef pr, char *message, int count, int *outError)
\r
9471 int outCount = SOCKET_ERROR;
\r
9472 ChildProc *cp = (ChildProc *) pr;
\r
9473 static OVERLAPPED ovl;
\r
9474 static int line = 0;
\r
9478 if (appData.noJoin || !appData.useInternalWrap)
\r
9479 return no_wrap(message, count);
\r
9482 int width = get_term_width();
\r
9483 int len = wrap(NULL, message, count, width, &line);
\r
9484 char *msg = malloc(len);
\r
9488 return no_wrap(message, count);
\r
9491 dbgchk = wrap(msg, message, count, width, &line);
\r
9492 if (dbgchk != len && appData.debugMode)
\r
9493 fprintf(debugFP, "wrap(): dbgchk(%d) != len(%d)\n", dbgchk, len);
\r
9494 ConsoleOutput(msg, len, FALSE);
\r
9501 if (ovl.hEvent == NULL) {
\r
9502 ovl.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
\r
9504 ovl.Internal = ovl.InternalHigh = ovl.Offset = ovl.OffsetHigh = 0;
\r
9506 switch (cp->kind) {
\r
9509 outCount = send(cp->sock, message, count, 0);
\r
9510 if (outCount == SOCKET_ERROR) {
\r
9511 *outError = WSAGetLastError();
\r
9513 *outError = NO_ERROR;
\r
9518 if (WriteFile(((ChildProc *)pr)->hTo, message, count,
\r
9519 &dOutCount, NULL)) {
\r
9520 *outError = NO_ERROR;
\r
9521 outCount = (int) dOutCount;
\r
9523 *outError = GetLastError();
\r
9528 *outError = DoWriteFile(((ChildProc *)pr)->hTo, message, count,
\r
9529 &dOutCount, &ovl);
\r
9530 if (*outError == NO_ERROR) {
\r
9531 outCount = (int) dOutCount;
\r
9539 OutputToProcessDelayed(ProcRef pr, char *message, int count, int *outError,
\r
9542 /* Ignore delay, not implemented for WinBoard */
\r
9543 return OutputToProcess(pr, message, count, outError);
\r
9548 CmailSigHandlerCallBack(InputSourceRef isr, VOIDSTAR closure,
\r
9549 char *buf, int count, int error)
\r
9551 DisplayFatalError(_("Not implemented"), 0, 1);
\r
9554 /* see wgamelist.c for Game List functions */
\r
9555 /* see wedittags.c for Edit Tags functions */
\r
9562 char buf[MSG_SIZ];
\r
9565 if (SearchPath(installDir, appData.icsLogon, NULL, MSG_SIZ, buf, &dummy)) {
\r
9566 f = fopen(buf, "r");
\r
9568 ProcessICSInitScript(f);
\r
9576 StartAnalysisClock()
\r
9578 if (analysisTimerEvent) return;
\r
9579 analysisTimerEvent = SetTimer(hwndMain, (UINT) ANALYSIS_TIMER_ID,
\r
9580 (UINT) 2000, NULL);
\r
9584 SetHighlights(int fromX, int fromY, int toX, int toY)
\r
9586 highlightInfo.sq[0].x = fromX;
\r
9587 highlightInfo.sq[0].y = fromY;
\r
9588 highlightInfo.sq[1].x = toX;
\r
9589 highlightInfo.sq[1].y = toY;
\r
9595 highlightInfo.sq[0].x = highlightInfo.sq[0].y =
\r
9596 highlightInfo.sq[1].x = highlightInfo.sq[1].y = -1;
\r
9600 SetPremoveHighlights(int fromX, int fromY, int toX, int toY)
\r
9602 premoveHighlightInfo.sq[0].x = fromX;
\r
9603 premoveHighlightInfo.sq[0].y = fromY;
\r
9604 premoveHighlightInfo.sq[1].x = toX;
\r
9605 premoveHighlightInfo.sq[1].y = toY;
\r
9609 ClearPremoveHighlights()
\r
9611 premoveHighlightInfo.sq[0].x = premoveHighlightInfo.sq[0].y =
\r
9612 premoveHighlightInfo.sq[1].x = premoveHighlightInfo.sq[1].y = -1;
\r
9616 ShutDownFrontEnd()
\r
9618 if (saveSettingsOnExit) SaveSettings(settingsFileName);
\r
9619 DeleteClipboardTempFiles();
\r
9625 if (IsIconic(hwndMain))
\r
9626 ShowWindow(hwndMain, SW_RESTORE);
\r
9628 SetActiveWindow(hwndMain);
\r
9632 * Prototypes for animation support routines
\r
9634 static void ScreenSquare(int column, int row, POINT * pt);
\r
9635 static void Tween( POINT * start, POINT * mid, POINT * finish, int factor,
\r
9636 POINT frames[], int * nFrames);
\r
9642 AnimateAtomicCapture(Board board, int fromX, int fromY, int toX, int toY)
\r
9643 { // [HGM] atomic: animate blast wave
\r
9646 explodeInfo.fromX = fromX;
\r
9647 explodeInfo.fromY = fromY;
\r
9648 explodeInfo.toX = toX;
\r
9649 explodeInfo.toY = toY;
\r
9650 for(i=1; i<4*kFactor; i++) {
\r
9651 explodeInfo.radius = (i*180)/(4*kFactor-1);
\r
9652 DrawPosition(FALSE, board);
\r
9653 Sleep(appData.animSpeed);
\r
9655 explodeInfo.radius = 0;
\r
9656 DrawPosition(TRUE, board);
\r
9660 AnimateMove(board, fromX, fromY, toX, toY)
\r
9667 ChessSquare piece;
\r
9668 POINT start, finish, mid;
\r
9669 POINT frames[kFactor * 2 + 1];
\r
9672 if (!appData.animate) return;
\r
9673 if (doingSizing) return;
\r
9674 if (fromY < 0 || fromX < 0) return;
\r
9675 piece = board[fromY][fromX];
\r
9676 if (piece >= EmptySquare) return;
\r
9678 ScreenSquare(fromX, fromY, &start);
\r
9679 ScreenSquare(toX, toY, &finish);
\r
9681 /* All moves except knight jumps move in straight line */
\r
9682 if (!(abs(fromX-toX) == 1 && abs(fromY-toY) == 2 || abs(fromX-toX) == 2 && abs(fromY-toY) == 1)) {
\r
9683 mid.x = start.x + (finish.x - start.x) / 2;
\r
9684 mid.y = start.y + (finish.y - start.y) / 2;
\r
9686 /* Knight: make straight movement then diagonal */
\r
9687 if (abs(toY - fromY) < abs(toX - fromX)) {
\r
9688 mid.x = start.x + (finish.x - start.x) / 2;
\r
9692 mid.y = start.y + (finish.y - start.y) / 2;
\r
9696 /* Don't use as many frames for very short moves */
\r
9697 if (abs(toY - fromY) + abs(toX - fromX) <= 2)
\r
9698 Tween(&start, &mid, &finish, kFactor - 1, frames, &nFrames);
\r
9700 Tween(&start, &mid, &finish, kFactor, frames, &nFrames);
\r
9702 animInfo.from.x = fromX;
\r
9703 animInfo.from.y = fromY;
\r
9704 animInfo.to.x = toX;
\r
9705 animInfo.to.y = toY;
\r
9706 animInfo.lastpos = start;
\r
9707 animInfo.piece = piece;
\r
9708 for (n = 0; n < nFrames; n++) {
\r
9709 animInfo.pos = frames[n];
\r
9710 DrawPosition(FALSE, NULL);
\r
9711 animInfo.lastpos = animInfo.pos;
\r
9712 Sleep(appData.animSpeed);
\r
9714 animInfo.pos = finish;
\r
9715 DrawPosition(FALSE, NULL);
\r
9716 animInfo.piece = EmptySquare;
\r
9717 Explode(board, fromX, fromY, toX, toY);
\r
9720 /* Convert board position to corner of screen rect and color */
\r
9723 ScreenSquare(column, row, pt)
\r
9724 int column; int row; POINT * pt;
\r
9727 pt->x = lineGap + ((BOARD_WIDTH-1)-column) * (squareSize + lineGap);
\r
9728 pt->y = lineGap + row * (squareSize + lineGap);
\r
9730 pt->x = lineGap + column * (squareSize + lineGap);
\r
9731 pt->y = lineGap + ((BOARD_HEIGHT-1)-row) * (squareSize + lineGap);
\r
9735 /* Generate a series of frame coords from start->mid->finish.
\r
9736 The movement rate doubles until the half way point is
\r
9737 reached, then halves back down to the final destination,
\r
9738 which gives a nice slow in/out effect. The algorithmn
\r
9739 may seem to generate too many intermediates for short
\r
9740 moves, but remember that the purpose is to attract the
\r
9741 viewers attention to the piece about to be moved and
\r
9742 then to where it ends up. Too few frames would be less
\r
9746 Tween(start, mid, finish, factor, frames, nFrames)
\r
9747 POINT * start; POINT * mid;
\r
9748 POINT * finish; int factor;
\r
9749 POINT frames[]; int * nFrames;
\r
9751 int n, fraction = 1, count = 0;
\r
9753 /* Slow in, stepping 1/16th, then 1/8th, ... */
\r
9754 for (n = 0; n < factor; n++)
\r
9756 for (n = 0; n < factor; n++) {
\r
9757 frames[count].x = start->x + (mid->x - start->x) / fraction;
\r
9758 frames[count].y = start->y + (mid->y - start->y) / fraction;
\r
9760 fraction = fraction / 2;
\r
9764 frames[count] = *mid;
\r
9767 /* Slow out, stepping 1/2, then 1/4, ... */
\r
9769 for (n = 0; n < factor; n++) {
\r
9770 frames[count].x = finish->x - (finish->x - mid->x) / fraction;
\r
9771 frames[count].y = finish->y - (finish->y - mid->y) / fraction;
\r
9773 fraction = fraction * 2;
\r
9779 HistorySet( char movelist[][2*MOVE_LEN], int first, int last, int current )
\r
9781 MoveHistorySet( movelist, first, last, current, pvInfoList );
\r
9783 EvalGraphSet( first, last, current, pvInfoList );
\r
9787 SettingsPopUp(ChessProgramState *cps)
\r
9788 { // [HGM] wrapper needed because handles must not be passed through back-end
\r
9789 EngineOptionsPopup(savedHwnd, cps);
\r