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, 2011 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
84 #include "frontend.h"
\r
85 #include "backend.h"
\r
86 #include "winboard.h"
\r
88 #include "wclipbrd.h"
\r
89 #include "woptions.h"
\r
90 #include "wsockerr.h"
\r
91 #include "defaults.h"
\r
95 //void InitEngineUCI( const char * iniDir, ChessProgramState * cps );
\r
98 void mysrandom(unsigned int seed);
\r
100 extern int whiteFlag, blackFlag;
\r
101 Boolean flipClock = FALSE;
\r
102 extern HANDLE chatHandle[];
\r
103 extern int ics_type;
\r
105 void DisplayHoldingsCount(HDC hdc, int x, int y, int align, int copyNumber);
\r
106 VOID NewVariantPopup(HWND hwnd);
\r
107 int FinishMove P((ChessMove moveType, int fromX, int fromY, int toX, int toY,
\r
108 /*char*/int promoChar));
\r
109 void DisplayMove P((int moveNumber));
\r
110 Boolean ParseFEN P((Board board, int *blackPlaysFirst, char *fen));
\r
111 void ChatPopUp P((char *s));
\r
113 ChessSquare piece;
\r
114 POINT pos; /* window coordinates of current pos */
\r
115 POINT lastpos; /* window coordinates of last pos - used for clipping */
\r
116 POINT from; /* board coordinates of the piece's orig pos */
\r
117 POINT to; /* board coordinates of the piece's new pos */
\r
120 static AnimInfo animInfo = { EmptySquare, {-1,-1}, {-1,-1}, {-1,-1} };
\r
123 POINT start; /* window coordinates of start pos */
\r
124 POINT pos; /* window coordinates of current pos */
\r
125 POINT lastpos; /* window coordinates of last pos - used for clipping */
\r
126 POINT from; /* board coordinates of the piece's orig pos */
\r
130 static DragInfo dragInfo = { {-1,-1}, {-1,-1}, {-1,-1}, {-1,-1}, EmptySquare };
\r
133 POINT sq[2]; /* board coordinates of from, to squares */
\r
136 static HighlightInfo highlightInfo = { {{-1, -1}, {-1, -1}} };
\r
137 static HighlightInfo premoveHighlightInfo = { {{-1, -1}, {-1, -1}} };
\r
138 static HighlightInfo partnerHighlightInfo = { {{-1, -1}, {-1, -1}} };
\r
139 static HighlightInfo oldPartnerHighlight = { {{-1, -1}, {-1, -1}} };
\r
141 typedef struct { // [HGM] atomic
\r
142 int fromX, fromY, toX, toY, radius;
\r
145 static ExplodeInfo explodeInfo;
\r
147 /* Window class names */
\r
148 char szAppName[] = "WinBoard";
\r
149 char szConsoleName[] = "WBConsole";
\r
151 /* Title bar text */
\r
152 char szTitle[] = "WinBoard";
\r
153 char szConsoleTitle[] = "I C S Interaction";
\r
156 char *settingsFileName;
\r
157 Boolean saveSettingsOnExit;
\r
158 char installDir[MSG_SIZ];
\r
159 char homeDir[MSG_SIZ];
\r
160 int errorExitStatus;
\r
162 BoardSize boardSize;
\r
163 Boolean chessProgram;
\r
164 //static int boardX, boardY;
\r
165 int minX, minY; // [HGM] placement: volatile limits on upper-left corner
\r
166 int squareSize, lineGap, minorSize;
\r
167 static int winW, winH;
\r
168 static RECT messageRect, whiteRect, blackRect, leftLogoRect, rightLogoRect; // [HGM] logo
\r
169 static int logoHeight = 0;
\r
170 static char messageText[MESSAGE_TEXT_MAX];
\r
171 static int clockTimerEvent = 0;
\r
172 static int loadGameTimerEvent = 0;
\r
173 static int analysisTimerEvent = 0;
\r
174 static DelayedEventCallback delayedTimerCallback;
\r
175 static int delayedTimerEvent = 0;
\r
176 static int buttonCount = 2;
\r
177 char *icsTextMenuString;
\r
179 char *firstChessProgramNames;
\r
180 char *secondChessProgramNames;
\r
182 #define PALETTESIZE 256
\r
184 HINSTANCE hInst; /* current instance */
\r
185 Boolean alwaysOnTop = FALSE;
\r
187 COLORREF lightSquareColor, darkSquareColor, whitePieceColor,
\r
188 blackPieceColor, highlightSquareColor, premoveHighlightColor;
\r
190 ColorClass currentColorClass;
\r
192 static HWND savedHwnd;
\r
193 HWND hCommPort = NULL; /* currently open comm port */
\r
194 static HWND hwndPause; /* pause button */
\r
195 static HBITMAP pieceBitmap[3][(int) BlackPawn]; /* [HGM] nr of bitmaps referred to bP in stead of wK */
\r
196 static HBRUSH lightSquareBrush, darkSquareBrush,
\r
197 blackSquareBrush, /* [HGM] for band between board and holdings */
\r
198 explodeBrush, /* [HGM] atomic */
\r
199 markerBrush, /* [HGM] markers */
\r
200 whitePieceBrush, blackPieceBrush, iconBkgndBrush /*, outlineBrush*/;
\r
201 static POINT gridEndpoints[(BOARD_RANKS + BOARD_FILES + 2) * 2];
\r
202 static DWORD gridVertexCounts[BOARD_RANKS + BOARD_FILES + 2];
\r
203 static HPEN gridPen = NULL;
\r
204 static HPEN highlightPen = NULL;
\r
205 static HPEN premovePen = NULL;
\r
206 static NPLOGPALETTE pLogPal;
\r
207 static BOOL paletteChanged = FALSE;
\r
208 static HICON iconWhite, iconBlack, iconCurrent;
\r
209 static int doingSizing = FALSE;
\r
210 static int lastSizing = 0;
\r
211 static int prevStderrPort;
\r
212 static HBITMAP userLogo;
\r
214 static HBITMAP liteBackTexture = NULL;
\r
215 static HBITMAP darkBackTexture = NULL;
\r
216 static int liteBackTextureMode = BACK_TEXTURE_MODE_PLAIN;
\r
217 static int darkBackTextureMode = BACK_TEXTURE_MODE_PLAIN;
\r
218 static int backTextureSquareSize = 0;
\r
219 static struct { int x; int y; int mode; } backTextureSquareInfo[BOARD_RANKS][BOARD_FILES];
\r
221 #if __GNUC__ && !defined(_winmajor)
\r
222 #define oldDialog 0 /* cygwin doesn't define _winmajor; mingw does */
\r
224 #if defined(_winmajor)
\r
225 #define oldDialog (_winmajor < 4)
\r
227 #define oldDialog 0
\r
231 #define INTERNATIONAL
\r
233 #ifdef INTERNATIONAL
\r
234 # define _(s) T_(s)
\r
240 # define Translate(x, y)
\r
241 # define LoadLanguageFile(s)
\r
244 #ifdef INTERNATIONAL
\r
246 Boolean barbaric; // flag indicating if translation is needed
\r
248 // list of item numbers used in each dialog (used to alter language at run time)
\r
250 #define ABOUTBOX -1 /* not sure why these are needed */
\r
251 #define ABOUTBOX2 -1
\r
253 int dialogItems[][40] = {
\r
254 { ABOUTBOX, IDOK, OPT_MESS, 400 },
\r
255 { DLG_TimeControl, IDC_Babble, OPT_TCUseMoves, OPT_TCUseInc, OPT_TCUseFixed,
\r
256 OPT_TCtext1, OPT_TCtext2, OPT_TCitext1, OPT_TCitext2, OPT_TCftext, GPB_Factors, IDC_Factor1, IDC_Factor2, IDOK, IDCANCEL },
\r
257 { DLG_LoadOptions, OPT_Autostep, OPT_AStext1, IDOK, IDCANCEL },
\r
258 { DLG_SaveOptions, OPT_Autosave, OPT_AVPrompt, OPT_AVToFile, OPT_AVBrowse,
\r
259 801, OPT_PGN, OPT_Old, OPT_OutOfBookInfo, IDOK, IDCANCEL },
\r
260 { 1536, 1090, IDC_Directories, 1089, 1091, IDOK, IDCANCEL, 1038, IDC_IndexNr, 1037 },
\r
261 { DLG_CommPort, IDOK, IDCANCEL, IDC_Port, IDC_Rate, IDC_Bits, IDC_Parity,
\r
262 IDC_Stop, IDC_Flow, OPT_SerialHelp },
\r
263 { DLG_EditComment, IDOK, OPT_CancelComment, OPT_ClearComment, OPT_EditComment },
\r
264 { DLG_PromotionKing, PB_Chancellor, PB_Archbishop, PB_Queen, PB_Rook,
\r
265 PB_Bishop, PB_Knight, PB_King, IDCANCEL, IDC_Yes, IDC_No, IDC_Centaur },
\r
266 { ABOUTBOX2, IDC_ChessBoard },
\r
267 { DLG_GameList, OPT_GameListLoad, OPT_GameListPrev, OPT_GameListNext,
\r
268 OPT_GameListClose, IDC_GameListDoFilter },
\r
269 { DLG_EditTags, IDOK, OPT_TagsCancel, OPT_EditTags },
\r
270 { DLG_Error, IDOK },
\r
271 { DLG_Colorize, IDOK, IDCANCEL, OPT_ChooseColor, OPT_Bold, OPT_Italic,
\r
272 OPT_Underline, OPT_Strikeout, OPT_Sample },
\r
273 { DLG_Question, IDOK, IDCANCEL, OPT_QuestionText },
\r
274 { DLG_Startup, IDC_Welcome, OPT_ChessEngine, OPT_ChessServer, OPT_View,
\r
275 IDC_SPECIFY_ENG_STATIC, IDC_SPECIFY_SERVER_STATIC, OPT_AnyAdditional,
\r
276 IDOK, IDCANCEL, IDM_HELPCONTENTS },
\r
277 { DLG_IndexNumber, IDC_Index },
\r
278 { DLG_TypeInMove, IDOK, IDCANCEL },
\r
279 { DLG_TypeInName, IDOK, IDCANCEL },
\r
280 { DLG_Sound, IDC_Event, OPT_NoSound, OPT_DefaultBeep, OPT_BuiltInSound,
\r
281 OPT_WavFile, OPT_BrowseSound, OPT_DefaultSounds, IDOK, IDCANCEL, OPT_PlaySound },
\r
282 { DLG_GeneralOptions, IDOK, IDCANCEL, OPT_AlwaysOnTop, OPT_HighlightLastMove,
\r
283 OPT_AlwaysQueen, OPT_PeriodicUpdates, OPT_AnimateDragging, OPT_PonderNextMove,
\r
284 OPT_AnimateMoving, OPT_PopupExitMessage, OPT_AutoFlag, OPT_PopupMoveErrors,
\r
285 OPT_AutoFlipView, OPT_ShowButtonBar, OPT_AutoRaiseBoard, OPT_ShowCoordinates,
\r
286 OPT_Blindfold, OPT_ShowThinking, OPT_HighlightDragging, OPT_TestLegality,
\r
287 OPT_SaveExtPGN, OPT_HideThinkFromHuman, OPT_ExtraInfoInMoveHistory,
\r
288 OPT_HighlightMoveArrow, OPT_AutoLogo ,OPT_SmartMove },
\r
289 { DLG_IcsOptions, IDOK, IDCANCEL, OPT_AutoComment, OPT_AutoKibitz, OPT_AutoObserve,
\r
290 OPT_GetMoveList, OPT_LocalLineEditing, OPT_QuietPlay, OPT_SeekGraph, OPT_AutoRefresh,
\r
291 OPT_BgObserve, OPT_DualBoard, OPT_Premove, OPT_PremoveWhite, OPT_PremoveBlack,
\r
292 OPT_SmartMove, OPT_IcsAlarm, IDC_Sec, OPT_ChooseShoutColor, OPT_ChooseSShoutColor,
\r
293 OPT_ChooseChannel1Color, OPT_ChooseChannelColor, OPT_ChooseKibitzColor,
\r
294 OPT_ChooseTellColor, OPT_ChooseChallengeColor, OPT_ChooseRequestColor,
\r
295 OPT_ChooseSeekColor, OPT_ChooseNormalColor, OPT_ChooseBackgroundColor,
\r
296 OPT_DefaultColors, OPT_DontColorize, IDC_Boxes, GPB_Colors, GPB_Premove,
\r
297 GPB_General, GPB_Alarm },
\r
298 { DLG_BoardOptions, IDOK, IDCANCEL, OPT_SizeTiny, OPT_SizeTeeny, OPT_SizeDinky,
\r
299 OPT_SizePetite, OPT_SizeSlim, OPT_SizeSmall, OPT_SizeMediocre, OPT_SizeMiddling,
\r
300 OPT_SizeAverage, OPT_SizeModerate, OPT_SizeMedium, OPT_SizeBulky, OPT_SizeLarge,
\r
301 OPT_SizeBig, OPT_SizeHuge, OPT_SizeGiant, OPT_SizeColossal, OPT_SizeTitanic,
\r
302 OPT_ChooseLightSquareColor, OPT_ChooseDarkSquareColor, OPT_ChooseWhitePieceColor,
\r
303 OPT_ChooseBlackPieceColor, OPT_ChooseHighlightSquareColor, OPT_ChoosePremoveHighlightColor,
\r
304 OPT_Monochrome, OPT_AllWhite, OPT_UpsideDown, OPT_DefaultBoardColors, GPB_Colors,
\r
305 IDC_Light, IDC_Dark, IDC_White, IDC_Black, IDC_High, IDC_PreHigh, GPB_Size },
\r
306 { DLG_NewVariant, IDOK, IDCANCEL, OPT_VariantNormal, OPT_VariantFRC, OPT_VariantWildcastle,
\r
307 OPT_VariantNocastle, OPT_VariantLosers, OPT_VariantGiveaway, OPT_VariantSuicide,
\r
308 OPT_Variant3Check, OPT_VariantTwoKings, OPT_VariantAtomic, OPT_VariantCrazyhouse,
\r
309 OPT_VariantBughouse, OPT_VariantTwilight, OPT_VariantShogi, OPT_VariantSuper,
\r
310 OPT_VariantKnightmate, OPT_VariantBerolina, OPT_VariantCylinder, OPT_VariantFairy,
\r
311 OPT_VariantMakruk, OPT_VariantGothic, OPT_VariantCapablanca, OPT_VariantJanus,
\r
312 OPT_VariantCRC, OPT_VariantFalcon, OPT_VariantCourier, OPT_VariantGreat, OPT_VariantSChess,
\r
313 OPT_VariantShatranj, OPT_VariantXiangqi, GPB_Variant, GPB_Board, IDC_Height,
\r
314 IDC_Width, IDC_Hand, IDC_Pieces, IDC_Def },
\r
315 { DLG_Fonts, IDOK, IDCANCEL, OPT_ChooseClockFont, OPT_ChooseMessageFont,
\r
316 OPT_ChooseCoordFont, OPT_ChooseTagFont, OPT_ChooseCommentsFont, OPT_ChooseConsoleFont, OPT_ChooseMoveHistoryFont, OPT_DefaultFonts,
\r
317 OPT_ClockFont, OPT_MessageFont, OPT_CoordFont, OPT_EditTagsFont,
\r
318 OPT_CommentsFont, OPT_MessageFont5, GPB_Current, GPB_All, OPT_MessageFont6 },
\r
319 { DLG_NewGameFRC, IDC_NFG_Label, IDC_NFG_Random, IDOK, IDCANCEL },
\r
320 { DLG_GameListOptions, IDC_GLT, IDC_GLT_Up, IDC_GLT_Down, IDC_GLT_Restore,
\r
321 IDC_GLT_Default, IDOK, IDCANCEL, IDC_GLT_RestoreTo },
\r
322 { DLG_MoveHistory },
\r
323 { DLG_EvalGraph },
\r
324 { DLG_EngineOutput, IDC_EngineLabel1, IDC_Engine1_NPS, IDC_EngineLabel2, IDC_Engine2_NPS },
\r
325 { DLG_Chat, IDC_Partner, IDC_Clear, IDC_Send, },
\r
326 { DLG_EnginePlayOptions, IDC_EpPonder, IDC_EpShowThinking, IDC_EpHideThinkingHuman,
\r
327 IDC_EpPeriodicUpdates, GPB_Adjudications, IDC_Draw, IDC_Moves, IDC_Threshold,
\r
328 IDC_Centi, IDC_TestClaims, IDC_DetectMates, IDC_MaterialDraws, IDC_TrivialDraws,
\r
329 GPB_Apply, IDC_Rule, IDC_Repeats, IDC_ScoreAbs1, IDC_ScoreAbs2, IDOK, IDCANCEL },
\r
330 { DLG_OptionsUCI, IDC_PolyDir, IDC_BrowseForPolyglotDir, IDC_Hash, IDC_Path,
\r
331 IDC_BrowseForEGTB, IDC_Cache, IDC_UseBook, IDC_BrowseForBook, IDC_CPU, IDC_OwnBook1,
\r
332 IDC_OwnBook2, IDC_Depth, IDC_Variation, IDC_DefGames, IDOK, IDCANCEL },
\r
336 static char languageBuf[50000], *foreign[1000], *english[1000], *languageFile[MSG_SIZ];
\r
337 static int lastChecked;
\r
338 static char oldLanguage[MSG_SIZ], *menuText[10][30];
\r
339 extern int tinyLayout;
\r
340 extern char * menuBarText[][10];
\r
343 LoadLanguageFile(char *name)
\r
344 { //load the file with translations, and make a list of the strings to be translated, and their translations
\r
346 int i=0, j=0, n=0, k;
\r
349 if(!name || name[0] == NULLCHAR) return;
\r
350 snprintf(buf, MSG_SIZ, "%s%s", name, strchr(name, '.') ? "" : ".lng"); // auto-append lng extension
\r
351 appData.language = oldLanguage;
\r
352 if(!strcmp(buf, oldLanguage)) { barbaric = 1; return; } // this language already loaded; just switch on
\r
353 if((f = fopen(buf, "r")) == NULL) return;
\r
354 while((k = fgetc(f)) != EOF) {
\r
355 if(i >= sizeof(languageBuf)) { DisplayError("Language file too big", 0); return; }
\r
356 languageBuf[i] = k;
\r
358 if(languageBuf[n] == '"' && languageBuf[i-1] == '"') {
\r
360 if(p = strstr(languageBuf + n + 1, "\" === \"")) {
\r
361 if(p > languageBuf+n+2 && p+8 < languageBuf+i) {
\r
362 if(j >= sizeof(english)) { DisplayError("Too many translated strings", 0); return; }
\r
363 english[j] = languageBuf + n + 1; *p = 0;
\r
364 foreign[j++] = p + 7; languageBuf[i-1] = 0;
\r
365 //if(appData.debugMode) fprintf(debugFP, "translation: replace '%s' by '%s'\n", english[j-1], foreign[j-1]);
\r
370 } else if(i > 0 && languageBuf[i-1] == '\\') {
\r
372 case 'n': k = '\n'; break;
\r
373 case 'r': k = '\r'; break;
\r
374 case 't': k = '\t'; break;
\r
376 languageBuf[--i] = k;
\r
381 barbaric = (j != 0);
\r
382 safeStrCpy(oldLanguage, buf, sizeof(oldLanguage)/sizeof(oldLanguage[0]) );
\r
387 { // return the translation of the given string
\r
388 // efficiency can be improved a lot...
\r
390 //if(appData.debugMode) fprintf(debugFP, "T_(%s)\n", s);
\r
391 if(!barbaric) return s;
\r
392 if(!s) return ""; // sanity
\r
393 while(english[i]) {
\r
394 if(!strcmp(s, english[i])) return foreign[i];
\r
401 Translate(HWND hDlg, int dialogID)
\r
402 { // translate all text items in the given dialog
\r
404 char buf[MSG_SIZ], *s;
\r
405 if(!barbaric) return;
\r
406 while(dialogItems[i][0] && dialogItems[i][0] != dialogID) i++; // find the dialog description
\r
407 if(dialogItems[i][0] != dialogID) return; // unknown dialog, should not happen
\r
408 GetWindowText( hDlg, buf, MSG_SIZ );
\r
410 if(strcmp(buf, s)) SetWindowText(hDlg, s); // replace by translated string (if different)
\r
411 for(j=1; k=dialogItems[i][j]; j++) { // translate all listed dialog items
\r
412 GetDlgItemText(hDlg, k, buf, MSG_SIZ);
\r
413 if(strlen(buf) == 0) continue;
\r
415 if(strcmp(buf, s)) SetDlgItemText(hDlg, k, s); // replace by translated string (if different)
\r
420 TranslateOneMenu(int i, HMENU subMenu)
\r
423 static MENUITEMINFO info;
\r
425 info.cbSize = sizeof(MENUITEMINFO);
\r
426 info.fMask = MIIM_STATE | MIIM_TYPE;
\r
427 for(j=GetMenuItemCount(subMenu)-1; j>=0; j--){
\r
429 info.dwTypeData = buf;
\r
430 info.cch = sizeof(buf);
\r
431 GetMenuItemInfo(subMenu, j, TRUE, &info);
\r
433 if(menuText[i][j]) safeStrCpy(buf, menuText[i][j], sizeof(buf)/sizeof(buf[0]) );
\r
434 else menuText[i][j] = strdup(buf); // remember original on first change
\r
436 if(buf[0] == NULLCHAR) continue;
\r
437 info.dwTypeData = T_(buf);
\r
438 info.cch = strlen(buf)+1;
\r
439 SetMenuItemInfo(subMenu, j, TRUE, &info);
\r
445 TranslateMenus(int addLanguage)
\r
448 WIN32_FIND_DATA fileData;
\r
450 #define IDM_English 1970
\r
452 HMENU mainMenu = GetMenu(hwndMain);
\r
453 for (i=GetMenuItemCount(mainMenu)-1; i>=0; i--) {
\r
454 HMENU subMenu = GetSubMenu(mainMenu, i);
\r
455 ModifyMenu(mainMenu, i, MF_STRING|MF_BYPOSITION|MF_POPUP|EnableMenuItem(mainMenu, i, MF_BYPOSITION),
\r
456 (UINT) subMenu, T_(menuBarText[tinyLayout][i]));
\r
457 TranslateOneMenu(i, subMenu);
\r
459 DrawMenuBar(hwndMain);
\r
462 if(!addLanguage) return;
\r
463 if((hFind = FindFirstFile("*.LNG", &fileData)) != INVALID_HANDLE_VALUE) {
\r
464 HMENU mainMenu = GetMenu(hwndMain);
\r
465 HMENU subMenu = GetSubMenu(mainMenu, GetMenuItemCount(mainMenu)-1);
\r
466 AppendMenu(subMenu, MF_SEPARATOR, (UINT_PTR) 0, NULL);
\r
467 AppendMenu(subMenu, MF_ENABLED|MF_STRING|(barbaric?MF_UNCHECKED:MF_CHECKED), (UINT_PTR) IDM_English, (LPCTSTR) "English");
\r
468 i = 0; lastChecked = IDM_English;
\r
470 char *p, *q = fileData.cFileName;
\r
471 int checkFlag = MF_UNCHECKED;
\r
472 languageFile[i] = strdup(q);
\r
473 if(barbaric && !strcmp(oldLanguage, q)) {
\r
474 checkFlag = MF_CHECKED;
\r
475 lastChecked = IDM_English + i + 1;
\r
476 CheckMenuItem(mainMenu, IDM_English, MF_BYCOMMAND|MF_UNCHECKED);
\r
478 *q = ToUpper(*q); while(*++q) *q = ToLower(*q);
\r
479 p = strstr(fileData.cFileName, ".lng");
\r
481 AppendMenu(subMenu, MF_ENABLED|MF_STRING|checkFlag, (UINT_PTR) IDM_English + ++i, (LPCTSTR) fileData.cFileName);
\r
482 } while(FindNextFile(hFind, &fileData));
\r
495 int cliWidth, cliHeight;
\r
498 SizeInfo sizeInfo[] =
\r
500 { "tiny", 21, 0, 1, 1, 0, 0 },
\r
501 { "teeny", 25, 1, 1, 1, 0, 0 },
\r
502 { "dinky", 29, 1, 1, 1, 0, 0 },
\r
503 { "petite", 33, 1, 1, 1, 0, 0 },
\r
504 { "slim", 37, 2, 1, 0, 0, 0 },
\r
505 { "small", 40, 2, 1, 0, 0, 0 },
\r
506 { "mediocre", 45, 2, 1, 0, 0, 0 },
\r
507 { "middling", 49, 2, 0, 0, 0, 0 },
\r
508 { "average", 54, 2, 0, 0, 0, 0 },
\r
509 { "moderate", 58, 3, 0, 0, 0, 0 },
\r
510 { "medium", 64, 3, 0, 0, 0, 0 },
\r
511 { "bulky", 72, 3, 0, 0, 0, 0 },
\r
512 { "large", 80, 3, 0, 0, 0, 0 },
\r
513 { "big", 87, 3, 0, 0, 0, 0 },
\r
514 { "huge", 95, 3, 0, 0, 0, 0 },
\r
515 { "giant", 108, 3, 0, 0, 0, 0 },
\r
516 { "colossal", 116, 4, 0, 0, 0, 0 },
\r
517 { "titanic", 129, 4, 0, 0, 0, 0 },
\r
518 { NULL, 0, 0, 0, 0, 0, 0 }
\r
521 #define MF(x) {x, {{0,}, 0. }, {0, }, 0}
\r
522 MyFont fontRec[NUM_SIZES][NUM_FONTS] =
\r
524 { 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
525 { 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
526 { 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
527 { 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
528 { 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
529 { 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
530 { 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
531 { 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
532 { 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
533 { 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
534 { 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
535 { 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
536 { 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
537 { 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
538 { 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
539 { 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
540 { 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
541 { 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
544 MyFont *font[NUM_SIZES][NUM_FONTS];
\r
553 #define BUTTON_WIDTH (tinyLayout ? 16 : 32)
\r
554 #define N_BUTTONS 5
\r
556 MyButtonDesc buttonDesc[N_BUTTONS] =
\r
558 {"<<", IDM_ToStart, NULL, NULL},
\r
559 {"<", IDM_Backward, NULL, NULL},
\r
560 {"P", IDM_Pause, NULL, NULL},
\r
561 {">", IDM_Forward, NULL, NULL},
\r
562 {">>", IDM_ToEnd, NULL, NULL},
\r
565 int tinyLayout = 0, smallLayout = 0;
\r
566 #define MENU_BAR_ITEMS 9
\r
567 char *menuBarText[2][MENU_BAR_ITEMS+1] = {
\r
568 { N_("&File"), N_("&Edit"), N_("&View"), N_("&Mode"), N_("&Action"), N_("E&ngine"), N_("&Options"), N_("&Help"), NULL },
\r
569 { N_("&F"), N_("&E"), N_("&V"), N_("&M"), N_("&A"), N_("&N"), N_("&O"), N_("&H"), NULL },
\r
573 MySound sounds[(int)NSoundClasses];
\r
574 MyTextAttribs textAttribs[(int)NColorClasses];
\r
576 MyColorizeAttribs colorizeAttribs[] = {
\r
577 { (COLORREF)0, 0, N_("Shout Text") },
\r
578 { (COLORREF)0, 0, N_("SShout/CShout") },
\r
579 { (COLORREF)0, 0, N_("Channel 1 Text") },
\r
580 { (COLORREF)0, 0, N_("Channel Text") },
\r
581 { (COLORREF)0, 0, N_("Kibitz Text") },
\r
582 { (COLORREF)0, 0, N_("Tell Text") },
\r
583 { (COLORREF)0, 0, N_("Challenge Text") },
\r
584 { (COLORREF)0, 0, N_("Request Text") },
\r
585 { (COLORREF)0, 0, N_("Seek Text") },
\r
586 { (COLORREF)0, 0, N_("Normal Text") },
\r
587 { (COLORREF)0, 0, N_("None") }
\r
592 static char *commentTitle;
\r
593 static char *commentText;
\r
594 static int commentIndex;
\r
595 static Boolean editComment = FALSE;
\r
598 char errorTitle[MSG_SIZ];
\r
599 char errorMessage[2*MSG_SIZ];
\r
600 HWND errorDialog = NULL;
\r
601 BOOLEAN moveErrorMessageUp = FALSE;
\r
602 BOOLEAN consoleEcho = TRUE;
\r
603 CHARFORMAT consoleCF;
\r
604 COLORREF consoleBackgroundColor;
\r
606 char *programVersion;
\r
612 typedef int CPKind;
\r
621 SOCKET sock2; /* stderr socket for OpenRcmd */
\r
624 #define INPUT_SOURCE_BUF_SIZE 4096
\r
626 typedef struct _InputSource {
\r
633 char buf[INPUT_SOURCE_BUF_SIZE];
\r
637 InputCallback func;
\r
638 struct _InputSource *second; /* for stderr thread on CPRcmd */
\r
642 InputSource *consoleInputSource;
\r
647 VOID ConsoleOutput(char* data, int length, int forceVisible);
\r
648 VOID ConsoleCreate();
\r
650 ConsoleWndProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam);
\r
651 VOID ColorizeTextPopup(HWND hwnd, ColorClass cc);
\r
652 VOID PrintCommSettings(FILE *f, char *name, DCB *dcb);
\r
653 VOID ParseCommSettings(char *arg, DCB *dcb);
\r
655 StartupDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam);
\r
656 VOID APIENTRY MenuPopup(HWND hwnd, POINT pt, HMENU hmenu, UINT def);
\r
657 void ParseIcsTextMenu(char *icsTextMenuString);
\r
658 VOID PopUpNameDialog(char firstchar);
\r
659 VOID UpdateSampleText(HWND hDlg, int id, MyColorizeAttribs *mca);
\r
663 int GameListOptions();
\r
665 int dummy; // [HGM] for obsolete args
\r
667 HWND hwndMain = NULL; /* root window*/
\r
668 HWND hwndConsole = NULL;
\r
669 HWND commentDialog = NULL;
\r
670 HWND moveHistoryDialog = NULL;
\r
671 HWND evalGraphDialog = NULL;
\r
672 HWND engineOutputDialog = NULL;
\r
673 HWND gameListDialog = NULL;
\r
674 HWND editTagsDialog = NULL;
\r
676 int commentUp = FALSE;
\r
678 WindowPlacement wpMain;
\r
679 WindowPlacement wpConsole;
\r
680 WindowPlacement wpComment;
\r
681 WindowPlacement wpMoveHistory;
\r
682 WindowPlacement wpEvalGraph;
\r
683 WindowPlacement wpEngineOutput;
\r
684 WindowPlacement wpGameList;
\r
685 WindowPlacement wpTags;
\r
687 VOID EngineOptionsPopup(); // [HGM] settings
\r
689 VOID GothicPopUp(char *title, VariantClass variant);
\r
691 * Setting "frozen" should disable all user input other than deleting
\r
692 * the window. We do this while engines are initializing themselves.
\r
694 static int frozen = 0;
\r
695 static int oldMenuItemState[MENU_BAR_ITEMS];
\r
701 if (frozen) return;
\r
703 hmenu = GetMenu(hwndMain);
\r
704 for (i=0; i<MENU_BAR_ITEMS; i++) {
\r
705 oldMenuItemState[i] = EnableMenuItem(hmenu, i, MF_BYPOSITION|MF_GRAYED);
\r
707 DrawMenuBar(hwndMain);
\r
710 /* Undo a FreezeUI */
\r
716 if (!frozen) return;
\r
718 hmenu = GetMenu(hwndMain);
\r
719 for (i=0; i<MENU_BAR_ITEMS; i++) {
\r
720 EnableMenuItem(hmenu, i, MF_BYPOSITION|oldMenuItemState[i]);
\r
722 DrawMenuBar(hwndMain);
\r
725 /*static*/ int fromX = -1, fromY = -1, toX, toY; // [HGM] moved upstream, so JAWS can use them
\r
727 /* JAWS preparation patch (WinBoard for the sight impaired). Define required insertions as empty */
\r
733 #define JAWS_ALT_INTERCEPT
\r
734 #define JAWS_KB_NAVIGATION
\r
735 #define JAWS_MENU_ITEMS
\r
736 #define JAWS_SILENCE
\r
737 #define JAWS_REPLAY
\r
739 #define JAWS_COPYRIGHT
\r
740 #define JAWS_DELETE(X) X
\r
741 #define SAYMACHINEMOVE()
\r
745 /*---------------------------------------------------------------------------*\
\r
749 \*---------------------------------------------------------------------------*/
\r
752 WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
\r
753 LPSTR lpCmdLine, int nCmdShow)
\r
756 HANDLE hAccelMain, hAccelNoAlt, hAccelNoICS;
\r
757 // INITCOMMONCONTROLSEX ex;
\r
761 LoadLibrary("RICHED32.DLL");
\r
762 consoleCF.cbSize = sizeof(CHARFORMAT);
\r
764 if (!InitApplication(hInstance)) {
\r
767 if (!InitInstance(hInstance, nCmdShow, lpCmdLine)) {
\r
774 // InitCommonControlsEx(&ex);
\r
775 InitCommonControls();
\r
777 hAccelMain = LoadAccelerators (hInstance, szAppName);
\r
778 hAccelNoAlt = LoadAccelerators (hInstance, "NO_ALT");
\r
779 hAccelNoICS = LoadAccelerators( hInstance, "NO_ICS"); /* [AS] No Ctrl-V on ICS!!! */
\r
781 /* Acquire and dispatch messages until a WM_QUIT message is received. */
\r
783 while (GetMessage(&msg, /* message structure */
\r
784 NULL, /* handle of window receiving the message */
\r
785 0, /* lowest message to examine */
\r
786 0)) /* highest message to examine */
\r
789 if(msg.message == WM_CHAR && msg.wParam == '\t') {
\r
790 // [HGM] navigate: switch between all windows with tab
\r
791 HWND e1 = NULL, e2 = NULL, mh = NULL, hInput = NULL, hText = NULL;
\r
792 int i, currentElement = 0;
\r
794 // first determine what element of the chain we come from (if any)
\r
795 if(appData.icsActive) {
\r
796 hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
797 hText = GetDlgItem(hwndConsole, OPT_ConsoleText);
\r
799 if(engineOutputDialog && EngineOutputIsUp()) {
\r
800 e1 = GetDlgItem(engineOutputDialog, IDC_EngineMemo1);
\r
801 e2 = GetDlgItem(engineOutputDialog, IDC_EngineMemo2);
\r
803 if(moveHistoryDialog && MoveHistoryIsUp()) {
\r
804 mh = GetDlgItem(moveHistoryDialog, IDC_MoveHistory);
\r
806 if(msg.hwnd == hwndMain) currentElement = 7 ; else
\r
807 if(msg.hwnd == engineOutputDialog) currentElement = 2; else
\r
808 if(msg.hwnd == e1) currentElement = 2; else
\r
809 if(msg.hwnd == e2) currentElement = 3; else
\r
810 if(msg.hwnd == moveHistoryDialog) currentElement = 4; else
\r
811 if(msg.hwnd == mh) currentElement = 4; else
\r
812 if(msg.hwnd == evalGraphDialog) currentElement = 6; else
\r
813 if(msg.hwnd == hText) currentElement = 5; else
\r
814 if(msg.hwnd == hInput) currentElement = 6; else
\r
815 for (i = 0; i < N_BUTTONS; i++) {
\r
816 if (buttonDesc[i].hwnd == msg.hwnd) { currentElement = 1; break; }
\r
819 // determine where to go to
\r
820 if(currentElement) { HWND h = NULL; int direction = GetKeyState(VK_SHIFT) < 0 ? -1 : 1;
\r
822 currentElement = (currentElement + direction) % 7;
\r
823 switch(currentElement) {
\r
825 h = hwndMain; break; // passing this case always makes the loop exit
\r
827 h = buttonDesc[0].hwnd; break; // could be NULL
\r
829 if(!EngineOutputIsUp()) continue; // skip closed auxiliary windows
\r
832 if(!EngineOutputIsUp()) continue;
\r
835 if(!MoveHistoryIsUp()) continue;
\r
837 // case 6: // input to eval graph does not seem to get here!
\r
838 // if(!EvalGraphIsUp()) continue;
\r
839 // h = evalGraphDialog; break;
\r
841 if(!appData.icsActive) continue;
\r
845 if(!appData.icsActive) continue;
\r
851 if(currentElement > 4 && IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);
\r
852 if(currentElement < 5 && IsIconic(hwndMain)) ShowWindow(hwndMain, SW_RESTORE); // all open together
\r
855 continue; // this message now has been processed
\r
859 if (!(commentDialog && IsDialogMessage(commentDialog, &msg)) &&
\r
860 !(moveHistoryDialog && IsDialogMessage(moveHistoryDialog, &msg)) &&
\r
861 !(evalGraphDialog && IsDialogMessage(evalGraphDialog, &msg)) &&
\r
862 !(engineOutputDialog && IsDialogMessage(engineOutputDialog, &msg)) &&
\r
863 !(editTagsDialog && IsDialogMessage(editTagsDialog, &msg)) &&
\r
864 !(gameListDialog && IsDialogMessage(gameListDialog, &msg)) &&
\r
865 !(errorDialog && IsDialogMessage(errorDialog, &msg)) &&
\r
866 !(!frozen && TranslateAccelerator(hwndMain, hAccelMain, &msg)) && JAWS_ACCEL
\r
867 !(!hwndConsole && TranslateAccelerator(hwndMain, hAccelNoICS, &msg)) &&
\r
868 !(!hwndConsole && TranslateAccelerator(hwndMain, hAccelNoAlt, &msg))) {
\r
869 int done = 0, i; // [HGM] chat: dispatch cat-box messages
\r
870 for(i=0; i<MAX_CHAT; i++)
\r
871 if(chatHandle[i] && IsDialogMessage(chatHandle[i], &msg)) {
\r
874 if(done) continue; // [HGM] chat: end patch
\r
875 TranslateMessage(&msg); /* Translates virtual key codes */
\r
876 DispatchMessage(&msg); /* Dispatches message to window */
\r
881 return (msg.wParam); /* Returns the value from PostQuitMessage */
\r
884 /*---------------------------------------------------------------------------*\
\r
886 * Initialization functions
\r
888 \*---------------------------------------------------------------------------*/
\r
892 { // update user logo if necessary
\r
893 static char oldUserName[MSG_SIZ], *curName;
\r
895 if(appData.autoLogo) {
\r
896 curName = UserName();
\r
897 if(strcmp(curName, oldUserName)) {
\r
898 snprintf(oldUserName, MSG_SIZ, "logos\\%s.bmp", curName);
\r
899 userLogo = LoadImage( 0, oldUserName, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );
\r
900 safeStrCpy(oldUserName, curName, sizeof(oldUserName)/sizeof(oldUserName[0]) );
\r
901 if(userLogo == NULL)
\r
902 userLogo = LoadImage( 0, "logos\\dummy.bmp", IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );
\r
908 InitApplication(HINSTANCE hInstance)
\r
912 /* Fill in window class structure with parameters that describe the */
\r
915 wc.style = CS_HREDRAW | CS_VREDRAW; /* Class style(s). */
\r
916 wc.lpfnWndProc = (WNDPROC)WndProc; /* Window Procedure */
\r
917 wc.cbClsExtra = 0; /* No per-class extra data. */
\r
918 wc.cbWndExtra = 0; /* No per-window extra data. */
\r
919 wc.hInstance = hInstance; /* Owner of this class */
\r
920 wc.hIcon = LoadIcon(hInstance, "icon_white");
\r
921 wc.hCursor = LoadCursor(NULL, IDC_ARROW); /* Cursor */
\r
922 wc.hbrBackground = (HBRUSH)(COLOR_WINDOW+1); /* Default color */
\r
923 wc.lpszMenuName = szAppName; /* Menu name from .RC */
\r
924 wc.lpszClassName = szAppName; /* Name to register as */
\r
926 /* Register the window class and return success/failure code. */
\r
927 if (!RegisterClass(&wc)) return FALSE;
\r
929 wc.style = CS_HREDRAW | CS_VREDRAW;
\r
930 wc.lpfnWndProc = (WNDPROC)ConsoleWndProc;
\r
932 wc.cbWndExtra = DLGWINDOWEXTRA;
\r
933 wc.hInstance = hInstance;
\r
934 wc.hIcon = LoadIcon(hInstance, "icon_white");
\r
935 wc.hCursor = LoadCursor(NULL, IDC_ARROW);
\r
936 wc.hbrBackground = (HBRUSH)(COLOR_MENU+1);
\r
937 wc.lpszMenuName = NULL;
\r
938 wc.lpszClassName = szConsoleName;
\r
940 if (!RegisterClass(&wc)) return FALSE;
\r
945 /* Set by InitInstance, used by EnsureOnScreen */
\r
946 int screenHeight, screenWidth;
\r
949 EnsureOnScreen(int *x, int *y, int minX, int minY)
\r
951 // int gap = GetSystemMetrics(SM_CYFRAME) + GetSystemMetrics(SM_CYCAPTION);
\r
952 /* Be sure window at (x,y) is not off screen (or even mostly off screen) */
\r
953 if (*x > screenWidth - 32) *x = 0;
\r
954 if (*y > screenHeight - 32) *y = 0;
\r
955 if (*x < minX) *x = minX;
\r
956 if (*y < minY) *y = minY;
\r
960 LoadLogo(ChessProgramState *cps, int n)
\r
962 if( appData.logo[n] && appData.logo[n][0] != NULLCHAR) {
\r
963 cps->programLogo = LoadImage( 0, appData.logo[n], IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );
\r
965 if (cps->programLogo == NULL && appData.debugMode) {
\r
966 fprintf( debugFP, "Unable to load logo bitmap '%s'\n", appData.logo[n] );
\r
968 } else if(appData.autoLogo) {
\r
969 if(appData.firstDirectory && appData.directory[n][0]) {
\r
971 snprintf(buf, MSG_SIZ, "%s/logo.bmp", appData.directory[n]);
\r
972 cps->programLogo = LoadImage( 0, buf, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );
\r
978 InitInstance(HINSTANCE hInstance, int nCmdShow, LPSTR lpCmdLine)
\r
980 HWND hwnd; /* Main window handle. */
\r
982 WINDOWPLACEMENT wp;
\r
985 hInst = hInstance; /* Store instance handle in our global variable */
\r
986 programName = szAppName;
\r
988 if (SearchPath(NULL, "WinBoard.exe", NULL, MSG_SIZ, installDir, &filepart)) {
\r
989 *filepart = NULLCHAR;
\r
991 GetCurrentDirectory(MSG_SIZ, installDir);
\r
993 safeStrCpy(homeDir, installDir, MSG_SIZ);
\r
994 gameInfo.boardWidth = gameInfo.boardHeight = 8; // [HGM] won't have open window otherwise
\r
995 screenWidth = screenHeight = 1000; // [HGM] placement: kludge to allow calling EnsureOnScreen from InitAppData
\r
996 InitAppData(lpCmdLine); /* Get run-time parameters */
\r
997 /* xboard, and older WinBoards, controlled the move sound with the
\r
998 appData.ringBellAfterMoves option. In the current WinBoard, we
\r
999 always turn the option on (so that the backend will call us),
\r
1000 then let the user turn the sound off by setting it to silence if
\r
1001 desired. To accommodate old winboard.ini files saved by old
\r
1002 versions of WinBoard, we also turn off the sound if the option
\r
1003 was initially set to false. [HGM] taken out of InitAppData */
\r
1004 if (!appData.ringBellAfterMoves) {
\r
1005 sounds[(int)SoundMove].name = strdup("");
\r
1006 appData.ringBellAfterMoves = TRUE;
\r
1008 if (appData.debugMode) {
\r
1009 debugFP = fopen(appData.nameOfDebugFile, "w");
\r
1010 setbuf(debugFP, NULL);
\r
1013 LoadLanguageFile(appData.language);
\r
1017 // InitEngineUCI( installDir, &first ); // [HGM] incorporated in InitBackEnd1()
\r
1018 // InitEngineUCI( installDir, &second );
\r
1020 /* Create a main window for this application instance. */
\r
1021 hwnd = CreateWindow(szAppName, szTitle,
\r
1022 (WS_OVERLAPPEDWINDOW & ~WS_MAXIMIZEBOX),
\r
1023 CW_USEDEFAULT, 0, CW_USEDEFAULT, 0,
\r
1024 NULL, NULL, hInstance, NULL);
\r
1027 /* If window could not be created, return "failure" */
\r
1032 /* [HGM] logo: Load logos if specified (must be done before InitDrawingSizes) */
\r
1033 LoadLogo(&first, 0);
\r
1035 if( appData.secondLogo && appData.secondLogo[0] != NULLCHAR) {
\r
1036 second.programLogo = LoadImage( 0, appData.secondLogo, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );
\r
1038 if (second.programLogo == NULL && appData.debugMode) {
\r
1039 fprintf( debugFP, "Unable to load logo bitmap '%s'\n", appData.secondLogo );
\r
1041 } else if(appData.autoLogo) {
\r
1042 char buf[MSG_SIZ];
\r
1043 if(appData.icsActive) { // [HGM] logo: in ICS mode second can be used for ICS
\r
1044 snprintf(buf, MSG_SIZ, "logos\\%s.bmp", appData.icsHost);
\r
1045 second.programLogo = LoadImage( 0, buf, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );
\r
1047 if(appData.secondDirectory && appData.secondDirectory[0]) {
\r
1048 snprintf(buf, MSG_SIZ, "%s\\logo.bmp", appData.secondDirectory);
\r
1049 second.programLogo = LoadImage( 0, buf, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );
\r
1055 iconWhite = LoadIcon(hInstance, "icon_white");
\r
1056 iconBlack = LoadIcon(hInstance, "icon_black");
\r
1057 iconCurrent = iconWhite;
\r
1058 InitDrawingColors();
\r
1059 screenHeight = GetSystemMetrics(SM_CYSCREEN);
\r
1060 screenWidth = GetSystemMetrics(SM_CXSCREEN);
\r
1061 for (ibs = (int) NUM_SIZES - 1; ibs >= 0; ibs--) {
\r
1062 /* Compute window size for each board size, and use the largest
\r
1063 size that fits on this screen as the default. */
\r
1064 InitDrawingSizes((BoardSize)(ibs+1000), 0);
\r
1065 if (boardSize == (BoardSize)-1 &&
\r
1066 winH <= screenHeight
\r
1067 - GetSystemMetrics(SM_CYFRAME) - GetSystemMetrics(SM_CYCAPTION) - 10
\r
1068 && winW <= screenWidth) {
\r
1069 boardSize = (BoardSize)ibs;
\r
1073 InitDrawingSizes(boardSize, 0);
\r
1075 buttonCount = GetSystemMetrics(SM_CMOUSEBUTTONS);
\r
1077 /* [AS] Load textures if specified */
\r
1078 ZeroMemory( &backTextureSquareInfo, sizeof(backTextureSquareInfo) );
\r
1080 if( appData.liteBackTextureFile && appData.liteBackTextureFile[0] != NULLCHAR && appData.liteBackTextureFile[0] != '*' ) {
\r
1081 liteBackTexture = LoadImage( 0, appData.liteBackTextureFile, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );
\r
1082 liteBackTextureMode = appData.liteBackTextureMode;
\r
1084 if (liteBackTexture == NULL && appData.debugMode) {
\r
1085 fprintf( debugFP, "Unable to load lite texture bitmap '%s'\n", appData.liteBackTextureFile );
\r
1089 if( appData.darkBackTextureFile && appData.darkBackTextureFile[0] != NULLCHAR && appData.darkBackTextureFile[0] != '*' ) {
\r
1090 darkBackTexture = LoadImage( 0, appData.darkBackTextureFile, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );
\r
1091 darkBackTextureMode = appData.darkBackTextureMode;
\r
1093 if (darkBackTexture == NULL && appData.debugMode) {
\r
1094 fprintf( debugFP, "Unable to load dark texture bitmap '%s'\n", appData.darkBackTextureFile );
\r
1098 mysrandom( (unsigned) time(NULL) );
\r
1100 /* [AS] Restore layout */
\r
1101 if( wpMoveHistory.visible ) {
\r
1102 MoveHistoryPopUp();
\r
1105 if( wpEvalGraph.visible ) {
\r
1109 if( wpEngineOutput.visible ) {
\r
1110 EngineOutputPopUp();
\r
1113 /* Make the window visible; update its client area; and return "success" */
\r
1114 EnsureOnScreen(&wpMain.x, &wpMain.y, minX, minY);
\r
1115 wp.length = sizeof(WINDOWPLACEMENT);
\r
1117 wp.showCmd = nCmdShow;
\r
1118 wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;
\r
1119 wp.rcNormalPosition.left = wpMain.x;
\r
1120 wp.rcNormalPosition.right = wpMain.x + wpMain.width;
\r
1121 wp.rcNormalPosition.top = wpMain.y;
\r
1122 wp.rcNormalPosition.bottom = wpMain.y + wpMain.height;
\r
1123 SetWindowPlacement(hwndMain, &wp);
\r
1125 InitBackEnd2(); // [HGM] moved until after all windows placed, to save correct position if fatal error on engine start
\r
1127 if(!appData.noGUI) SetWindowPos(hwndMain, alwaysOnTop ? HWND_TOPMOST : HWND_NOTOPMOST,
\r
1128 0, 0, 0, 0, SWP_NOMOVE|SWP_NOSIZE);
\r
1130 if (hwndConsole) {
\r
1132 SetWindowPos(hwndConsole, alwaysOnTop ? HWND_TOPMOST : HWND_NOTOPMOST,
\r
1133 0, 0, 0, 0, SWP_NOMOVE|SWP_NOSIZE);
\r
1135 ShowWindow(hwndConsole, nCmdShow);
\r
1136 if(appData.chatBoxes) { // [HGM] chat: open chat boxes
\r
1137 char buf[MSG_SIZ], *p = buf, *q;
\r
1138 safeStrCpy(buf, appData.chatBoxes, sizeof(buf)/sizeof(buf[0]) );
\r
1140 q = strchr(p, ';');
\r
1142 if(*p) ChatPopUp(p);
\r
1145 SetActiveWindow(hwndConsole);
\r
1147 if(!appData.noGUI) UpdateWindow(hwnd); else ShowWindow(hwnd, SW_MINIMIZE);
\r
1148 if(gameListDialog) SetFocus(gameListDialog); // [HGM] jaws: for if we clicked multi-game game file
\r
1157 HMENU hmenu = GetMenu(hwndMain);
\r
1159 (void) EnableMenuItem(hmenu, IDM_CommPort,
\r
1160 MF_BYCOMMAND|((appData.icsActive &&
\r
1161 *appData.icsCommPort != NULLCHAR) ?
\r
1162 MF_ENABLED : MF_GRAYED));
\r
1163 (void) CheckMenuItem(hmenu, IDM_SaveSettingsOnExit,
\r
1164 MF_BYCOMMAND|(saveSettingsOnExit ?
\r
1165 MF_CHECKED : MF_UNCHECKED));
\r
1168 //---------------------------------------------------------------------------------------------------------
\r
1170 #define ICS_TEXT_MENU_SIZE (IDM_CommandXLast - IDM_CommandX + 1)
\r
1171 #define XBOARD FALSE
\r
1173 #define OPTCHAR "/"
\r
1174 #define SEPCHAR "="
\r
1178 // front-end part of option handling
\r
1181 LFfromMFP(LOGFONT* lf, MyFontParams *mfp)
\r
1183 HDC hdc = CreateDC("DISPLAY", NULL, NULL, NULL);
\r
1184 lf->lfHeight = -(int)(mfp->pointSize * GetDeviceCaps(hdc, LOGPIXELSY) / 72.0 + 0.5);
\r
1187 lf->lfEscapement = 0;
\r
1188 lf->lfOrientation = 0;
\r
1189 lf->lfWeight = mfp->bold ? FW_BOLD : FW_NORMAL;
\r
1190 lf->lfItalic = mfp->italic;
\r
1191 lf->lfUnderline = mfp->underline;
\r
1192 lf->lfStrikeOut = mfp->strikeout;
\r
1193 lf->lfCharSet = mfp->charset;
\r
1194 lf->lfOutPrecision = OUT_DEFAULT_PRECIS;
\r
1195 lf->lfClipPrecision = CLIP_DEFAULT_PRECIS;
\r
1196 lf->lfQuality = DEFAULT_QUALITY;
\r
1197 lf->lfPitchAndFamily = DEFAULT_PITCH|FF_DONTCARE;
\r
1198 safeStrCpy(lf->lfFaceName, mfp->faceName, sizeof(lf->lfFaceName)/sizeof(lf->lfFaceName[0]) );
\r
1202 CreateFontInMF(MyFont *mf)
\r
1204 LFfromMFP(&mf->lf, &mf->mfp);
\r
1205 if (mf->hf) DeleteObject(mf->hf);
\r
1206 mf->hf = CreateFontIndirect(&mf->lf);
\r
1209 // [HGM] This platform-dependent table provides the location for storing the color info
\r
1211 colorVariable[] = {
\r
1212 &whitePieceColor,
\r
1213 &blackPieceColor,
\r
1214 &lightSquareColor,
\r
1215 &darkSquareColor,
\r
1216 &highlightSquareColor,
\r
1217 &premoveHighlightColor,
\r
1219 &consoleBackgroundColor,
\r
1220 &appData.fontForeColorWhite,
\r
1221 &appData.fontBackColorWhite,
\r
1222 &appData.fontForeColorBlack,
\r
1223 &appData.fontBackColorBlack,
\r
1224 &appData.evalHistColorWhite,
\r
1225 &appData.evalHistColorBlack,
\r
1226 &appData.highlightArrowColor,
\r
1229 /* Command line font name parser. NULL name means do nothing.
\r
1230 Syntax like "Courier New:10.0 bi" or "Arial:10" or "Arial:10b"
\r
1231 For backward compatibility, syntax without the colon is also
\r
1232 accepted, but font names with digits in them won't work in that case.
\r
1235 ParseFontName(char *name, MyFontParams *mfp)
\r
1238 if (name == NULL) return;
\r
1240 q = strchr(p, ':');
\r
1242 if (q - p >= sizeof(mfp->faceName))
\r
1243 ExitArgError(_("Font name too long:"), name);
\r
1244 memcpy(mfp->faceName, p, q - p);
\r
1245 mfp->faceName[q - p] = NULLCHAR;
\r
1248 q = mfp->faceName;
\r
1249 while (*p && !isdigit(*p)) {
\r
1251 if (q - mfp->faceName >= sizeof(mfp->faceName))
\r
1252 ExitArgError(_("Font name too long:"), name);
\r
1254 while (q > mfp->faceName && q[-1] == ' ') q--;
\r
1257 if (!*p) ExitArgError(_("Font point size missing:"), name);
\r
1258 mfp->pointSize = (float) atof(p);
\r
1259 mfp->bold = (strchr(p, 'b') != NULL);
\r
1260 mfp->italic = (strchr(p, 'i') != NULL);
\r
1261 mfp->underline = (strchr(p, 'u') != NULL);
\r
1262 mfp->strikeout = (strchr(p, 's') != NULL);
\r
1263 mfp->charset = DEFAULT_CHARSET;
\r
1264 q = strchr(p, 'c');
\r
1266 mfp->charset = (BYTE) atoi(q+1);
\r
1270 ParseFont(char *name, int number)
\r
1271 { // wrapper to shield back-end from 'font'
\r
1272 ParseFontName(name, &font[boardSize][number]->mfp);
\r
1277 { // in WB we have a 2D array of fonts; this initializes their description
\r
1279 /* Point font array elements to structures and
\r
1280 parse default font names */
\r
1281 for (i=0; i<NUM_FONTS; i++) {
\r
1282 for (j=0; j<NUM_SIZES; j++) {
\r
1283 font[j][i] = &fontRec[j][i];
\r
1284 ParseFontName(font[j][i]->def, &font[j][i]->mfp);
\r
1291 { // here we create the actual fonts from the selected descriptions
\r
1293 for (i=0; i<NUM_FONTS; i++) {
\r
1294 for (j=0; j<NUM_SIZES; j++) {
\r
1295 CreateFontInMF(font[j][i]);
\r
1299 /* Color name parser.
\r
1300 X version accepts X color names, but this one
\r
1301 handles only the #rrggbb form (hex) or rrr,ggg,bbb (decimal) */
\r
1303 ParseColorName(char *name)
\r
1305 int red, green, blue, count;
\r
1306 char buf[MSG_SIZ];
\r
1308 count = sscanf(name, "#%2x%2x%2x", &red, &green, &blue);
\r
1310 count = sscanf(name, "%3d%*[^0-9]%3d%*[^0-9]%3d",
\r
1311 &red, &green, &blue);
\r
1314 snprintf(buf, MSG_SIZ, _("Can't parse color name %s"), name);
\r
1315 DisplayError(buf, 0);
\r
1316 return RGB(0, 0, 0);
\r
1318 return PALETTERGB(red, green, blue);
\r
1322 ParseColor(int n, char *name)
\r
1323 { // for WinBoard the color is an int, which needs to be derived from the string
\r
1324 if(colorVariable[n]) *(int*)colorVariable[n] = ParseColorName(name);
\r
1328 ParseAttribs(COLORREF *color, int *effects, char* argValue)
\r
1330 char *e = argValue;
\r
1334 if (*e == 'b') eff |= CFE_BOLD;
\r
1335 else if (*e == 'i') eff |= CFE_ITALIC;
\r
1336 else if (*e == 'u') eff |= CFE_UNDERLINE;
\r
1337 else if (*e == 's') eff |= CFE_STRIKEOUT;
\r
1338 else if (*e == '#' || isdigit(*e)) break;
\r
1342 *color = ParseColorName(e);
\r
1346 ParseTextAttribs(ColorClass cc, char *s)
\r
1347 { // [HGM] front-end wrapper that does the platform-dependent call
\r
1348 // for XBoard we would set (&appData.colorShout)[cc] = strdup(s);
\r
1349 ParseAttribs(&textAttribs[cc].color, &textAttribs[cc].effects, s);
\r
1353 ParseBoardSize(void *addr, char *name)
\r
1354 { // [HGM] rewritten with return-value ptr to shield back-end from BoardSize
\r
1355 BoardSize bs = SizeTiny;
\r
1356 while (sizeInfo[bs].name != NULL) {
\r
1357 if (StrCaseCmp(name, sizeInfo[bs].name) == 0) {
\r
1358 *(BoardSize *)addr = bs;
\r
1363 ExitArgError(_("Unrecognized board size value"), name);
\r
1368 { // [HGM] import name from appData first
\r
1371 for (cc = (ColorClass)0; cc < ColorNormal; cc++) {
\r
1372 textAttribs[cc].sound.name = strdup((&appData.soundShout)[cc]);
\r
1373 textAttribs[cc].sound.data = NULL;
\r
1374 MyLoadSound(&textAttribs[cc].sound);
\r
1376 for (cc = ColorNormal; cc < NColorClasses; cc++) {
\r
1377 textAttribs[cc].sound.name = strdup("");
\r
1378 textAttribs[cc].sound.data = NULL;
\r
1380 for (sc = (SoundClass)0; sc < NSoundClasses; sc++) {
\r
1381 sounds[sc].name = strdup((&appData.soundMove)[sc]);
\r
1382 sounds[sc].data = NULL;
\r
1383 MyLoadSound(&sounds[sc]);
\r
1388 SetCommPortDefaults()
\r
1390 memset(&dcb, 0, sizeof(DCB)); // required by VS 2002 +
\r
1391 dcb.DCBlength = sizeof(DCB);
\r
1392 dcb.BaudRate = 9600;
\r
1393 dcb.fBinary = TRUE;
\r
1394 dcb.fParity = FALSE;
\r
1395 dcb.fOutxCtsFlow = FALSE;
\r
1396 dcb.fOutxDsrFlow = FALSE;
\r
1397 dcb.fDtrControl = DTR_CONTROL_ENABLE;
\r
1398 dcb.fDsrSensitivity = FALSE;
\r
1399 dcb.fTXContinueOnXoff = TRUE;
\r
1400 dcb.fOutX = FALSE;
\r
1402 dcb.fNull = FALSE;
\r
1403 dcb.fRtsControl = RTS_CONTROL_ENABLE;
\r
1404 dcb.fAbortOnError = FALSE;
\r
1406 dcb.Parity = SPACEPARITY;
\r
1407 dcb.StopBits = ONESTOPBIT;
\r
1410 // [HGM] args: these three cases taken out to stay in front-end
\r
1412 SaveFontArg(FILE *f, ArgDescriptor *ad)
\r
1413 { // in WinBoard every board size has its own font, and the "argLoc" identifies the table,
\r
1414 // while the curent board size determines the element. This system should be ported to XBoard.
\r
1415 // What the table contains pointers to, and how to print the font description, remains platform-dependent
\r
1417 for (bs=0; bs<NUM_SIZES; bs++) {
\r
1418 MyFontParams *mfp = &font[bs][(int) ad->argLoc]->mfp;
\r
1419 fprintf(f, "/size=%s ", sizeInfo[bs].name);
\r
1420 fprintf(f, "/%s=\"%s:%g%s%s%s%s%sc%d\"\n",
\r
1421 ad->argName, mfp->faceName, mfp->pointSize,
\r
1422 mfp->bold || mfp->italic || mfp->underline || mfp->strikeout ? " " : "",
\r
1423 mfp->bold ? "b" : "",
\r
1424 mfp->italic ? "i" : "",
\r
1425 mfp->underline ? "u" : "",
\r
1426 mfp->strikeout ? "s" : "",
\r
1427 (int)mfp->charset);
\r
1433 { // [HGM] copy the names from the internal WB variables to appData
\r
1436 for (cc = (ColorClass)0; cc < ColorNormal; cc++)
\r
1437 (&appData.soundShout)[cc] = textAttribs[cc].sound.name;
\r
1438 for (sc = (SoundClass)0; sc < NSoundClasses; sc++)
\r
1439 (&appData.soundMove)[sc] = sounds[sc].name;
\r
1443 SaveAttribsArg(FILE *f, ArgDescriptor *ad)
\r
1444 { // here the "argLoc" defines a table index. It could have contained the 'ta' pointer itself, though
\r
1445 MyTextAttribs* ta = &textAttribs[(ColorClass)ad->argLoc];
\r
1446 fprintf(f, "/%s=\"%s%s%s%s%s#%02lx%02lx%02lx\"\n", ad->argName,
\r
1447 (ta->effects & CFE_BOLD) ? "b" : "",
\r
1448 (ta->effects & CFE_ITALIC) ? "i" : "",
\r
1449 (ta->effects & CFE_UNDERLINE) ? "u" : "",
\r
1450 (ta->effects & CFE_STRIKEOUT) ? "s" : "",
\r
1451 (ta->effects) ? " " : "",
\r
1452 ta->color&0xff, (ta->color >> 8)&0xff, (ta->color >> 16)&0xff);
\r
1456 SaveColor(FILE *f, ArgDescriptor *ad)
\r
1457 { // in WinBoard the color is an int and has to be converted to text. In X it would be a string already?
\r
1458 COLORREF color = *(COLORREF *)colorVariable[(int)ad->argLoc];
\r
1459 fprintf(f, "/%s=#%02lx%02lx%02lx\n", ad->argName,
\r
1460 color&0xff, (color>>8)&0xff, (color>>16)&0xff);
\r
1464 SaveBoardSize(FILE *f, char *name, void *addr)
\r
1465 { // wrapper to shield back-end from BoardSize & sizeInfo
\r
1466 fprintf(f, "/%s=%s\n", name, sizeInfo[*(BoardSize *)addr].name);
\r
1470 ParseCommPortSettings(char *s)
\r
1471 { // wrapper to keep dcb from back-end
\r
1472 ParseCommSettings(s, &dcb);
\r
1477 { // wrapper to shield use of window handles from back-end (make addressible by number?)
\r
1478 GetActualPlacement(hwndMain, &wpMain);
\r
1479 GetActualPlacement(hwndConsole, &wpConsole);
\r
1480 GetActualPlacement(commentDialog, &wpComment);
\r
1481 GetActualPlacement(editTagsDialog, &wpTags);
\r
1482 GetActualPlacement(gameListDialog, &wpGameList);
\r
1483 GetActualPlacement(moveHistoryDialog, &wpMoveHistory);
\r
1484 GetActualPlacement(evalGraphDialog, &wpEvalGraph);
\r
1485 GetActualPlacement(engineOutputDialog, &wpEngineOutput);
\r
1489 PrintCommPortSettings(FILE *f, char *name)
\r
1490 { // wrapper to shield back-end from DCB
\r
1491 PrintCommSettings(f, name, &dcb);
\r
1495 MySearchPath(char *installDir, char *name, char *fullname)
\r
1497 char *dummy, buf[MSG_SIZ], *p = name, *q;
\r
1498 if(name[0]== '%') {
\r
1499 fullname[0] = 0; // [HGM] first expand any environment variables in the given name
\r
1500 while(*p == '%' && (q = strchr(p+1, '%'))) { // [HGM] recognize %*% as environment variable
\r
1501 safeStrCpy(buf, p+1, sizeof(buf)/sizeof(buf[0]) );
\r
1502 *strchr(buf, '%') = 0;
\r
1503 strcat(fullname, getenv(buf));
\r
1504 p = q+1; while(*p == '\\') { strcat(fullname, "\\"); p++; }
\r
1506 strcat(fullname, p); // after environment variables (if any), take the remainder of the given name
\r
1507 if(appData.debugMode) fprintf(debugFP, "name = '%s', expanded name = '%s'\n", name, fullname);
\r
1508 return (int) strlen(fullname);
\r
1510 return (int) SearchPath(installDir, name, NULL, MSG_SIZ, fullname, &dummy);
\r
1514 MyGetFullPathName(char *name, char *fullname)
\r
1517 return (int) GetFullPathName(name, MSG_SIZ, fullname, &dummy);
\r
1522 { // [HGM] args: allows testing if main window is realized from back-end
\r
1523 return hwndMain != NULL;
\r
1527 PopUpStartupDialog()
\r
1531 LoadLanguageFile(appData.language);
\r
1532 lpProc = MakeProcInstance((FARPROC)StartupDialog, hInst);
\r
1533 DialogBox(hInst, MAKEINTRESOURCE(DLG_Startup), NULL, (DLGPROC)lpProc);
\r
1534 FreeProcInstance(lpProc);
\r
1537 /*---------------------------------------------------------------------------*\
\r
1539 * GDI board drawing routines
\r
1541 \*---------------------------------------------------------------------------*/
\r
1543 /* [AS] Draw square using background texture */
\r
1544 static void DrawTile( int dx, int dy, int dw, int dh, HDC dst, HDC src, int mode, int sx, int sy )
\r
1549 return; /* Should never happen! */
\r
1552 SetGraphicsMode( dst, GM_ADVANCED );
\r
1559 /* X reflection */
\r
1564 x.eDx = (FLOAT) dw + dx - 1;
\r
1567 SetWorldTransform( dst, &x );
\r
1570 /* Y reflection */
\r
1576 x.eDy = (FLOAT) dh + dy - 1;
\r
1578 SetWorldTransform( dst, &x );
\r
1586 x.eDx = (FLOAT) dx;
\r
1587 x.eDy = (FLOAT) dy;
\r
1590 SetWorldTransform( dst, &x );
\r
1594 BitBlt( dst, dx, dy, dw, dh, src, sx, sy, SRCCOPY );
\r
1602 SetWorldTransform( dst, &x );
\r
1604 ModifyWorldTransform( dst, 0, MWT_IDENTITY );
\r
1607 /* [AS] [HGM] Make room for more piece types, so all pieces can be different */
\r
1609 PM_WP = (int) WhitePawn,
\r
1610 PM_WN = (int) WhiteKnight,
\r
1611 PM_WB = (int) WhiteBishop,
\r
1612 PM_WR = (int) WhiteRook,
\r
1613 PM_WQ = (int) WhiteQueen,
\r
1614 PM_WF = (int) WhiteFerz,
\r
1615 PM_WW = (int) WhiteWazir,
\r
1616 PM_WE = (int) WhiteAlfil,
\r
1617 PM_WM = (int) WhiteMan,
\r
1618 PM_WO = (int) WhiteCannon,
\r
1619 PM_WU = (int) WhiteUnicorn,
\r
1620 PM_WH = (int) WhiteNightrider,
\r
1621 PM_WA = (int) WhiteAngel,
\r
1622 PM_WC = (int) WhiteMarshall,
\r
1623 PM_WAB = (int) WhiteCardinal,
\r
1624 PM_WD = (int) WhiteDragon,
\r
1625 PM_WL = (int) WhiteLance,
\r
1626 PM_WS = (int) WhiteCobra,
\r
1627 PM_WV = (int) WhiteFalcon,
\r
1628 PM_WSG = (int) WhiteSilver,
\r
1629 PM_WG = (int) WhiteGrasshopper,
\r
1630 PM_WK = (int) WhiteKing,
\r
1631 PM_BP = (int) BlackPawn,
\r
1632 PM_BN = (int) BlackKnight,
\r
1633 PM_BB = (int) BlackBishop,
\r
1634 PM_BR = (int) BlackRook,
\r
1635 PM_BQ = (int) BlackQueen,
\r
1636 PM_BF = (int) BlackFerz,
\r
1637 PM_BW = (int) BlackWazir,
\r
1638 PM_BE = (int) BlackAlfil,
\r
1639 PM_BM = (int) BlackMan,
\r
1640 PM_BO = (int) BlackCannon,
\r
1641 PM_BU = (int) BlackUnicorn,
\r
1642 PM_BH = (int) BlackNightrider,
\r
1643 PM_BA = (int) BlackAngel,
\r
1644 PM_BC = (int) BlackMarshall,
\r
1645 PM_BG = (int) BlackGrasshopper,
\r
1646 PM_BAB = (int) BlackCardinal,
\r
1647 PM_BD = (int) BlackDragon,
\r
1648 PM_BL = (int) BlackLance,
\r
1649 PM_BS = (int) BlackCobra,
\r
1650 PM_BV = (int) BlackFalcon,
\r
1651 PM_BSG = (int) BlackSilver,
\r
1652 PM_BK = (int) BlackKing
\r
1655 static HFONT hPieceFont = NULL;
\r
1656 static HBITMAP hPieceMask[(int) EmptySquare];
\r
1657 static HBITMAP hPieceFace[(int) EmptySquare];
\r
1658 static int fontBitmapSquareSize = 0;
\r
1659 static char pieceToFontChar[(int) EmptySquare] =
\r
1660 { 'p', 'n', 'b', 'r', 'q',
\r
1661 'n', 'b', 'p', 'n', 'b', 'r', 'b', 'r', 'q', 'k',
\r
1662 'k', 'o', 'm', 'v', 't', 'w',
\r
1663 'v', 't', 'o', 'm', 'v', 't', 'v', 't', 'w', 'l',
\r
1666 extern BOOL SetCharTable( char *table, const char * map );
\r
1667 /* [HGM] moved to backend.c */
\r
1669 static void SetPieceBackground( HDC hdc, COLORREF color, int mode )
\r
1672 BYTE r1 = GetRValue( color );
\r
1673 BYTE g1 = GetGValue( color );
\r
1674 BYTE b1 = GetBValue( color );
\r
1680 /* Create a uniform background first */
\r
1681 hbrush = CreateSolidBrush( color );
\r
1682 SetRect( &rc, 0, 0, squareSize, squareSize );
\r
1683 FillRect( hdc, &rc, hbrush );
\r
1684 DeleteObject( hbrush );
\r
1687 /* Vertical gradient, good for pawn, knight and rook, less for queen and king */
\r
1688 int steps = squareSize / 2;
\r
1691 for( i=0; i<steps; i++ ) {
\r
1692 BYTE r = r1 - (r1-r2) * i / steps;
\r
1693 BYTE g = g1 - (g1-g2) * i / steps;
\r
1694 BYTE b = b1 - (b1-b2) * i / steps;
\r
1696 hbrush = CreateSolidBrush( RGB(r,g,b) );
\r
1697 SetRect( &rc, i + squareSize - steps, 0, i + squareSize - steps + 1, squareSize );
\r
1698 FillRect( hdc, &rc, hbrush );
\r
1699 DeleteObject(hbrush);
\r
1702 else if( mode == 2 ) {
\r
1703 /* Diagonal gradient, good more or less for every piece */
\r
1704 POINT triangle[3];
\r
1705 HPEN hpen = SelectObject( hdc, GetStockObject(NULL_PEN) );
\r
1706 HBRUSH hbrush_old;
\r
1707 int steps = squareSize;
\r
1710 triangle[0].x = squareSize - steps;
\r
1711 triangle[0].y = squareSize;
\r
1712 triangle[1].x = squareSize;
\r
1713 triangle[1].y = squareSize;
\r
1714 triangle[2].x = squareSize;
\r
1715 triangle[2].y = squareSize - steps;
\r
1717 for( i=0; i<steps; i++ ) {
\r
1718 BYTE r = r1 - (r1-r2) * i / steps;
\r
1719 BYTE g = g1 - (g1-g2) * i / steps;
\r
1720 BYTE b = b1 - (b1-b2) * i / steps;
\r
1722 hbrush = CreateSolidBrush( RGB(r,g,b) );
\r
1723 hbrush_old = SelectObject( hdc, hbrush );
\r
1724 Polygon( hdc, triangle, 3 );
\r
1725 SelectObject( hdc, hbrush_old );
\r
1726 DeleteObject(hbrush);
\r
1731 SelectObject( hdc, hpen );
\r
1736 [AS] The method I use to create the bitmaps it a bit tricky, but it
\r
1737 seems to work ok. The main problem here is to find the "inside" of a chess
\r
1738 piece: follow the steps as explained below.
\r
1740 static void CreatePieceMaskFromFont( HDC hdc_window, HDC hdc, int index )
\r
1744 COLORREF chroma = RGB(0xFF,0x00,0xFF);
\r
1748 int backColor = whitePieceColor;
\r
1749 int foreColor = blackPieceColor;
\r
1751 if( index < (int)BlackPawn && appData.fontBackColorWhite != appData.fontForeColorWhite ) {
\r
1752 backColor = appData.fontBackColorWhite;
\r
1753 foreColor = appData.fontForeColorWhite;
\r
1755 else if( index >= (int)BlackPawn && appData.fontBackColorBlack != appData.fontForeColorBlack ) {
\r
1756 backColor = appData.fontBackColorBlack;
\r
1757 foreColor = appData.fontForeColorBlack;
\r
1761 hbm = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );
\r
1763 hbm_old = SelectObject( hdc, hbm );
\r
1767 rc.right = squareSize;
\r
1768 rc.bottom = squareSize;
\r
1770 /* Step 1: background is now black */
\r
1771 FillRect( hdc, &rc, GetStockObject(BLACK_BRUSH) );
\r
1773 GetTextExtentPoint32( hdc, &pieceToFontChar[index], 1, &sz );
\r
1775 pt.x = (squareSize - sz.cx) / 2;
\r
1776 pt.y = (squareSize - sz.cy) / 2;
\r
1778 SetBkMode( hdc, TRANSPARENT );
\r
1779 SetTextColor( hdc, chroma );
\r
1780 /* Step 2: the piece has been drawn in purple, there are now black and purple in this bitmap */
\r
1781 TextOut( hdc, pt.x, pt.y, &pieceToFontChar[appData.allWhite && index >= (int)BlackPawn ? index - (int)BlackPawn : index], 1 );
\r
1783 SelectObject( hdc, GetStockObject(WHITE_BRUSH) );
\r
1784 /* Step 3: the area outside the piece is filled with white */
\r
1785 // FloodFill( hdc, 0, 0, chroma );
\r
1786 ExtFloodFill( hdc, 0, 0, 0, FLOODFILLSURFACE );
\r
1787 ExtFloodFill( hdc, 0, squareSize-1, 0, FLOODFILLSURFACE ); // [HGM] fill from all 4 corners, for if piece too big
\r
1788 ExtFloodFill( hdc, squareSize-1, 0, 0, FLOODFILLSURFACE );
\r
1789 ExtFloodFill( hdc, squareSize-1, squareSize-1, 0, FLOODFILLSURFACE );
\r
1790 SelectObject( hdc, GetStockObject(BLACK_BRUSH) );
\r
1792 Step 4: this is the tricky part, the area inside the piece is filled with black,
\r
1793 but if the start point is not inside the piece we're lost!
\r
1794 There should be a better way to do this... if we could create a region or path
\r
1795 from the fill operation we would be fine for example.
\r
1797 // FloodFill( hdc, squareSize / 2, squareSize / 2, RGB(0xFF,0xFF,0xFF) );
\r
1798 ExtFloodFill( hdc, squareSize / 2, squareSize / 2, RGB(0xFF,0xFF,0xFF), FLOODFILLBORDER );
\r
1800 { /* [HGM] shave off edges of mask, in an attempt to correct for the fact that FloodFill does not work correctly under Win XP */
\r
1801 HDC dc2 = CreateCompatibleDC( hdc_window );
\r
1802 HBITMAP bm2 = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );
\r
1804 SelectObject( dc2, bm2 );
\r
1805 BitBlt( dc2, 0, 0, squareSize, squareSize, hdc, 0, 0, SRCCOPY ); // make copy
\r
1806 BitBlt( hdc, 0, 1, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );
\r
1807 BitBlt( hdc, 2, 1, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );
\r
1808 BitBlt( hdc, 1, 0, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );
\r
1809 BitBlt( hdc, 1, 2, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );
\r
1812 DeleteObject( bm2 );
\r
1815 SetTextColor( hdc, 0 );
\r
1817 Step 5: some fonts have "disconnected" areas that are skipped by the fill:
\r
1818 draw the piece again in black for safety.
\r
1820 TextOut( hdc, pt.x, pt.y, &pieceToFontChar[appData.allWhite && index >= (int)BlackPawn ? index - (int)BlackPawn : index], 1 );
\r
1822 SelectObject( hdc, hbm_old );
\r
1824 if( hPieceMask[index] != NULL ) {
\r
1825 DeleteObject( hPieceMask[index] );
\r
1828 hPieceMask[index] = hbm;
\r
1831 hbm = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );
\r
1833 SelectObject( hdc, hbm );
\r
1836 HDC dc1 = CreateCompatibleDC( hdc_window );
\r
1837 HDC dc2 = CreateCompatibleDC( hdc_window );
\r
1838 HBITMAP bm2 = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );
\r
1840 SelectObject( dc1, hPieceMask[index] );
\r
1841 SelectObject( dc2, bm2 );
\r
1842 FillRect( dc2, &rc, GetStockObject(WHITE_BRUSH) );
\r
1843 BitBlt( dc2, 0, 0, squareSize, squareSize, dc1, 0, 0, SRCINVERT );
\r
1846 Now dc2 contains the inverse of the piece mask, i.e. a mask that preserves
\r
1847 the piece background and deletes (makes transparent) the rest.
\r
1848 Thanks to that mask, we are free to paint the background with the greates
\r
1849 freedom, as we'll be able to mask off the unwanted parts when finished.
\r
1850 We use this, to make gradients and give the pieces a "roundish" look.
\r
1852 SetPieceBackground( hdc, backColor, 2 );
\r
1853 BitBlt( hdc, 0, 0, squareSize, squareSize, dc2, 0, 0, SRCAND );
\r
1857 DeleteObject( bm2 );
\r
1860 SetTextColor( hdc, foreColor );
\r
1861 TextOut( hdc, pt.x, pt.y, &pieceToFontChar[appData.allWhite && index >= (int)BlackPawn ? index - (int)BlackPawn : index], 1 );
\r
1863 SelectObject( hdc, hbm_old );
\r
1865 if( hPieceFace[index] != NULL ) {
\r
1866 DeleteObject( hPieceFace[index] );
\r
1869 hPieceFace[index] = hbm;
\r
1872 static int TranslatePieceToFontPiece( int piece )
\r
1902 case BlackMarshall:
\r
1906 case BlackNightrider:
\r
1912 case BlackUnicorn:
\r
1916 case BlackGrasshopper:
\r
1928 case BlackCardinal:
\r
1935 case WhiteMarshall:
\r
1939 case WhiteNightrider:
\r
1945 case WhiteUnicorn:
\r
1949 case WhiteGrasshopper:
\r
1961 case WhiteCardinal:
\r
1970 void CreatePiecesFromFont()
\r
1973 HDC hdc_window = NULL;
\r
1979 if( fontBitmapSquareSize < 0 ) {
\r
1980 /* Something went seriously wrong in the past: do not try to recreate fonts! */
\r
1984 if( appData.renderPiecesWithFont == NULL || appData.renderPiecesWithFont[0] == NULLCHAR || appData.renderPiecesWithFont[0] == '*' ) {
\r
1985 fontBitmapSquareSize = -1;
\r
1989 if( fontBitmapSquareSize != squareSize ) {
\r
1990 hdc_window = GetDC( hwndMain );
\r
1991 hdc = CreateCompatibleDC( hdc_window );
\r
1993 if( hPieceFont != NULL ) {
\r
1994 DeleteObject( hPieceFont );
\r
1997 for( i=0; i<=(int)BlackKing; i++ ) {
\r
1998 hPieceMask[i] = NULL;
\r
1999 hPieceFace[i] = NULL;
\r
2005 if( appData.fontPieceSize >= 50 && appData.fontPieceSize <= 150 ) {
\r
2006 fontHeight = appData.fontPieceSize;
\r
2009 fontHeight = (fontHeight * squareSize) / 100;
\r
2011 lf.lfHeight = -MulDiv( fontHeight, GetDeviceCaps(hdc, LOGPIXELSY), 72 );
\r
2013 lf.lfEscapement = 0;
\r
2014 lf.lfOrientation = 0;
\r
2015 lf.lfWeight = FW_NORMAL;
\r
2017 lf.lfUnderline = 0;
\r
2018 lf.lfStrikeOut = 0;
\r
2019 lf.lfCharSet = DEFAULT_CHARSET;
\r
2020 lf.lfOutPrecision = OUT_DEFAULT_PRECIS;
\r
2021 lf.lfClipPrecision = CLIP_DEFAULT_PRECIS;
\r
2022 lf.lfQuality = PROOF_QUALITY;
\r
2023 lf.lfPitchAndFamily = DEFAULT_PITCH | FF_DONTCARE;
\r
2024 strncpy( lf.lfFaceName, appData.renderPiecesWithFont, sizeof(lf.lfFaceName) );
\r
2025 lf.lfFaceName[ sizeof(lf.lfFaceName) - 1 ] = '\0';
\r
2027 hPieceFont = CreateFontIndirect( &lf );
\r
2029 if( hPieceFont == NULL ) {
\r
2030 fontBitmapSquareSize = -2;
\r
2033 /* Setup font-to-piece character table */
\r
2034 if( ! SetCharTable(pieceToFontChar, appData.fontToPieceTable) ) {
\r
2035 /* No (or wrong) global settings, try to detect the font */
\r
2036 if( strstr(lf.lfFaceName,"Alpha") != NULL ) {
\r
2038 SetCharTable(pieceToFontChar, "phbrqkojntwl");
\r
2040 else if( strstr(lf.lfFaceName,"DiagramTT") != NULL ) {
\r
2041 /* DiagramTT* family */
\r
2042 SetCharTable(pieceToFontChar, "PNLRQKpnlrqk");
\r
2044 else if( strstr(lf.lfFaceName,"WinboardF") != NULL ) {
\r
2045 /* Fairy symbols */
\r
2046 SetCharTable(pieceToFontChar, "PNBRQFEACWMOHIJGDVSLUKpnbrqfeacwmohijgdvsluk");
\r
2048 else if( strstr(lf.lfFaceName,"GC2004D") != NULL ) {
\r
2049 /* Good Companion (Some characters get warped as literal :-( */
\r
2050 char s[] = "1cmWG0??S??oYI23wgQU";
\r
2051 s[0]=0xB9; s[1]=0xA9; s[6]=0xB1; s[11]=0xBB; s[12]=0xAB; s[17]=0xB3;
\r
2052 SetCharTable(pieceToFontChar, s);
\r
2055 /* Cases, Condal, Leipzig, Lucena, Marroquin, Merida, Usual */
\r
2056 SetCharTable(pieceToFontChar, "pnbrqkomvtwl");
\r
2060 /* Create bitmaps */
\r
2061 hfont_old = SelectObject( hdc, hPieceFont );
\r
2062 for(i=(int)WhitePawn; i<(int)EmptySquare; i++) /* [HGM] made a loop for this */
\r
2063 if(PieceToChar((ChessSquare)i) != '.') /* skip unused pieces */
\r
2064 CreatePieceMaskFromFont( hdc_window, hdc, i );
\r
2066 SelectObject( hdc, hfont_old );
\r
2068 fontBitmapSquareSize = squareSize;
\r
2072 if( hdc != NULL ) {
\r
2076 if( hdc_window != NULL ) {
\r
2077 ReleaseDC( hwndMain, hdc_window );
\r
2082 DoLoadBitmap(HINSTANCE hinst, char *piece, int squareSize, char *suffix)
\r
2086 snprintf(name, sizeof(name)/sizeof(name[0]), "%s%d%s", piece, squareSize, suffix);
\r
2087 if (gameInfo.event &&
\r
2088 strcmp(gameInfo.event, "Easter Egg Hunt") == 0 &&
\r
2089 strcmp(name, "k80s") == 0) {
\r
2090 safeStrCpy(name, "tim", sizeof(name)/sizeof(name[0]) );
\r
2092 return LoadBitmap(hinst, name);
\r
2096 /* Insert a color into the program's logical palette
\r
2097 structure. This code assumes the given color is
\r
2098 the result of the RGB or PALETTERGB macro, and it
\r
2099 knows how those macros work (which is documented).
\r
2102 InsertInPalette(COLORREF color)
\r
2104 LPPALETTEENTRY pe = &(pLogPal->palPalEntry[pLogPal->palNumEntries]);
\r
2106 if (pLogPal->palNumEntries++ >= PALETTESIZE) {
\r
2107 DisplayFatalError(_("Too many colors"), 0, 1);
\r
2108 pLogPal->palNumEntries--;
\r
2112 pe->peFlags = (char) 0;
\r
2113 pe->peRed = (char) (0xFF & color);
\r
2114 pe->peGreen = (char) (0xFF & (color >> 8));
\r
2115 pe->peBlue = (char) (0xFF & (color >> 16));
\r
2121 InitDrawingColors()
\r
2123 if (pLogPal == NULL) {
\r
2124 /* Allocate enough memory for a logical palette with
\r
2125 * PALETTESIZE entries and set the size and version fields
\r
2126 * of the logical palette structure.
\r
2128 pLogPal = (NPLOGPALETTE)
\r
2129 LocalAlloc(LMEM_FIXED, (sizeof(LOGPALETTE) +
\r
2130 (sizeof(PALETTEENTRY) * (PALETTESIZE))));
\r
2131 pLogPal->palVersion = 0x300;
\r
2133 pLogPal->palNumEntries = 0;
\r
2135 InsertInPalette(lightSquareColor);
\r
2136 InsertInPalette(darkSquareColor);
\r
2137 InsertInPalette(whitePieceColor);
\r
2138 InsertInPalette(blackPieceColor);
\r
2139 InsertInPalette(highlightSquareColor);
\r
2140 InsertInPalette(premoveHighlightColor);
\r
2142 /* create a logical color palette according the information
\r
2143 * in the LOGPALETTE structure.
\r
2145 hPal = CreatePalette((LPLOGPALETTE) pLogPal);
\r
2147 lightSquareBrush = CreateSolidBrush(lightSquareColor);
\r
2148 blackSquareBrush = CreateSolidBrush(blackPieceColor);
\r
2149 darkSquareBrush = CreateSolidBrush(darkSquareColor);
\r
2150 whitePieceBrush = CreateSolidBrush(whitePieceColor);
\r
2151 blackPieceBrush = CreateSolidBrush(blackPieceColor);
\r
2152 iconBkgndBrush = CreateSolidBrush(GetSysColor(COLOR_BACKGROUND));
\r
2153 explodeBrush = CreateSolidBrush(highlightSquareColor); // [HGM] atomic
\r
2154 markerBrush = CreateSolidBrush(premoveHighlightColor); // [HGM] markers
\r
2155 /* [AS] Force rendering of the font-based pieces */
\r
2156 if( fontBitmapSquareSize > 0 ) {
\r
2157 fontBitmapSquareSize = 0;
\r
2163 BoardWidth(int boardSize, int n)
\r
2164 { /* [HGM] argument n added to allow different width and height */
\r
2165 int lineGap = sizeInfo[boardSize].lineGap;
\r
2167 if( appData.overrideLineGap >= 0 && appData.overrideLineGap <= 5 ) {
\r
2168 lineGap = appData.overrideLineGap;
\r
2171 return (n + 1) * lineGap +
\r
2172 n * sizeInfo[boardSize].squareSize;
\r
2175 /* Respond to board resize by dragging edge */
\r
2177 ResizeBoard(int newSizeX, int newSizeY, int flags)
\r
2179 BoardSize newSize = NUM_SIZES - 1;
\r
2180 static int recurse = 0;
\r
2181 if (IsIconic(hwndMain)) return;
\r
2182 if (recurse > 0) return;
\r
2184 while (newSize > 0) {
\r
2185 InitDrawingSizes(newSize+1000, 0); // [HGM] kludge to update sizeInfo without visible effects
\r
2186 if(newSizeX >= sizeInfo[newSize].cliWidth &&
\r
2187 newSizeY >= sizeInfo[newSize].cliHeight) break;
\r
2190 boardSize = newSize;
\r
2191 InitDrawingSizes(boardSize, flags);
\r
2196 extern Boolean twoBoards, partnerUp; // [HGM] dual
\r
2199 InitDrawingSizes(BoardSize boardSize, int flags)
\r
2201 int i, boardWidth, boardHeight; /* [HGM] height treated separately */
\r
2202 ChessSquare piece;
\r
2203 static int oldBoardSize = -1, oldTinyLayout = 0;
\r
2205 SIZE clockSize, messageSize;
\r
2207 char buf[MSG_SIZ];
\r
2209 HMENU hmenu = GetMenu(hwndMain);
\r
2210 RECT crect, wrect, oldRect;
\r
2212 LOGBRUSH logbrush;
\r
2214 int suppressVisibleEffects = 0; // [HGM] kludge to request updating sizeInfo only
\r
2215 if((int)boardSize >= 1000 ) { boardSize -= 1000; suppressVisibleEffects = 1; }
\r
2217 /* [HGM] call with -2 uses old size (for if nr of files, ranks changes) */
\r
2218 if(boardSize == (BoardSize)(-2) ) boardSize = oldBoardSize;
\r
2220 oldRect.left = wpMain.x; //[HGM] placement: remember previous window params
\r
2221 oldRect.top = wpMain.y;
\r
2222 oldRect.right = wpMain.x + wpMain.width;
\r
2223 oldRect.bottom = wpMain.y + wpMain.height;
\r
2225 tinyLayout = sizeInfo[boardSize].tinyLayout;
\r
2226 smallLayout = sizeInfo[boardSize].smallLayout;
\r
2227 squareSize = sizeInfo[boardSize].squareSize;
\r
2228 lineGap = sizeInfo[boardSize].lineGap;
\r
2229 minorSize = 0; /* [HGM] Kludge to see if demagnified pieces need to be shifted */
\r
2231 if( appData.overrideLineGap >= 0 && appData.overrideLineGap <= 5 ) {
\r
2232 lineGap = appData.overrideLineGap;
\r
2235 if (tinyLayout != oldTinyLayout) {
\r
2236 long style = GetWindowLongPtr(hwndMain, GWL_STYLE);
\r
2238 style &= ~WS_SYSMENU;
\r
2239 InsertMenu(hmenu, IDM_Exit, MF_BYCOMMAND, IDM_Minimize,
\r
2240 "&Minimize\tCtrl+F4");
\r
2242 style |= WS_SYSMENU;
\r
2243 RemoveMenu(hmenu, IDM_Minimize, MF_BYCOMMAND);
\r
2245 SetWindowLongPtr(hwndMain, GWL_STYLE, style);
\r
2247 for (i=0; menuBarText[tinyLayout][i]; i++) {
\r
2248 ModifyMenu(hmenu, i, MF_STRING|MF_BYPOSITION|MF_POPUP,
\r
2249 (UINT)GetSubMenu(hmenu, i), T_(menuBarText[tinyLayout][i]));
\r
2251 DrawMenuBar(hwndMain);
\r
2254 boardWidth = BoardWidth(boardSize, BOARD_WIDTH);
\r
2255 boardHeight = BoardWidth(boardSize, BOARD_HEIGHT);
\r
2257 /* Get text area sizes */
\r
2258 hdc = GetDC(hwndMain);
\r
2259 if (appData.clockMode) {
\r
2260 snprintf(buf, MSG_SIZ, _("White: %s"), TimeString(23*60*60*1000L));
\r
2262 snprintf(buf, MSG_SIZ, _("White"));
\r
2264 oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);
\r
2265 GetTextExtentPoint(hdc, buf, strlen(buf), &clockSize);
\r
2266 SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);
\r
2267 str = _("We only care about the height here");
\r
2268 GetTextExtentPoint(hdc, str, strlen(str), &messageSize);
\r
2269 SelectObject(hdc, oldFont);
\r
2270 ReleaseDC(hwndMain, hdc);
\r
2272 /* Compute where everything goes */
\r
2273 if((first.programLogo || second.programLogo) && !tinyLayout) {
\r
2274 /* [HGM] logo: if either logo is on, reserve space for it */
\r
2275 logoHeight = 2*clockSize.cy;
\r
2276 leftLogoRect.left = OUTER_MARGIN;
\r
2277 leftLogoRect.right = leftLogoRect.left + 4*clockSize.cy;
\r
2278 leftLogoRect.top = OUTER_MARGIN;
\r
2279 leftLogoRect.bottom = OUTER_MARGIN + logoHeight;
\r
2281 rightLogoRect.right = OUTER_MARGIN + boardWidth;
\r
2282 rightLogoRect.left = rightLogoRect.right - 4*clockSize.cy;
\r
2283 rightLogoRect.top = OUTER_MARGIN;
\r
2284 rightLogoRect.bottom = OUTER_MARGIN + logoHeight;
\r
2287 whiteRect.left = leftLogoRect.right;
\r
2288 whiteRect.right = OUTER_MARGIN + boardWidth/2 - INNER_MARGIN/2;
\r
2289 whiteRect.top = OUTER_MARGIN;
\r
2290 whiteRect.bottom = whiteRect.top + logoHeight;
\r
2292 blackRect.right = rightLogoRect.left;
\r
2293 blackRect.left = whiteRect.right + INNER_MARGIN;
\r
2294 blackRect.top = whiteRect.top;
\r
2295 blackRect.bottom = whiteRect.bottom;
\r
2297 whiteRect.left = OUTER_MARGIN;
\r
2298 whiteRect.right = whiteRect.left + boardWidth/2 - INNER_MARGIN/2;
\r
2299 whiteRect.top = OUTER_MARGIN;
\r
2300 whiteRect.bottom = whiteRect.top + clockSize.cy;
\r
2302 blackRect.left = whiteRect.right + INNER_MARGIN;
\r
2303 blackRect.right = blackRect.left + boardWidth/2 - 1;
\r
2304 blackRect.top = whiteRect.top;
\r
2305 blackRect.bottom = whiteRect.bottom;
\r
2307 logoHeight = 0; // [HGM] logo: suppress logo after change to tiny layout!
\r
2310 messageRect.left = OUTER_MARGIN + MESSAGE_LINE_LEFTMARGIN;
\r
2311 if (appData.showButtonBar) {
\r
2312 messageRect.right = OUTER_MARGIN + boardWidth // [HGM] logo: expressed independent of clock placement
\r
2313 - N_BUTTONS*BUTTON_WIDTH - MESSAGE_LINE_LEFTMARGIN;
\r
2315 messageRect.right = OUTER_MARGIN + boardWidth;
\r
2317 messageRect.top = whiteRect.bottom + INNER_MARGIN;
\r
2318 messageRect.bottom = messageRect.top + messageSize.cy;
\r
2320 boardRect.left = OUTER_MARGIN;
\r
2321 boardRect.right = boardRect.left + boardWidth;
\r
2322 boardRect.top = messageRect.bottom + INNER_MARGIN;
\r
2323 boardRect.bottom = boardRect.top + boardHeight;
\r
2325 sizeInfo[boardSize].cliWidth = boardRect.right + OUTER_MARGIN;
\r
2326 sizeInfo[boardSize].cliHeight = boardRect.bottom + OUTER_MARGIN;
\r
2327 oldBoardSize = boardSize;
\r
2328 oldTinyLayout = tinyLayout;
\r
2329 winW = 2 * GetSystemMetrics(SM_CXFRAME) + boardRect.right + OUTER_MARGIN;
\r
2330 winH = 2 * GetSystemMetrics(SM_CYFRAME) + GetSystemMetrics(SM_CYMENU) +
\r
2331 GetSystemMetrics(SM_CYCAPTION) + boardRect.bottom + OUTER_MARGIN;
\r
2332 winW *= 1 + twoBoards;
\r
2333 if(suppressVisibleEffects) return; // [HGM] when called for filling sizeInfo only
\r
2334 wpMain.width = winW; // [HGM] placement: set through temporary which can used by initial sizing choice
\r
2335 wpMain.height = winH; // without disturbing window attachments
\r
2336 GetWindowRect(hwndMain, &wrect);
\r
2337 SetWindowPos(hwndMain, NULL, 0, 0, wpMain.width, wpMain.height,
\r
2338 SWP_NOCOPYBITS|SWP_NOZORDER|SWP_NOMOVE);
\r
2340 // [HGM] placement: let attached windows follow size change.
\r
2341 ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, moveHistoryDialog, &wpMoveHistory );
\r
2342 ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, evalGraphDialog, &wpEvalGraph );
\r
2343 ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, engineOutputDialog, &wpEngineOutput );
\r
2344 ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, gameListDialog, &wpGameList );
\r
2345 ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, hwndConsole, &wpConsole );
\r
2347 /* compensate if menu bar wrapped */
\r
2348 GetClientRect(hwndMain, &crect);
\r
2349 offby = boardRect.bottom + OUTER_MARGIN - crect.bottom;
\r
2350 wpMain.height += offby;
\r
2352 case WMSZ_TOPLEFT:
\r
2353 SetWindowPos(hwndMain, NULL,
\r
2354 wrect.right - wpMain.width, wrect.bottom - wpMain.height,
\r
2355 wpMain.width, wpMain.height, SWP_NOCOPYBITS|SWP_NOZORDER);
\r
2358 case WMSZ_TOPRIGHT:
\r
2360 SetWindowPos(hwndMain, NULL,
\r
2361 wrect.left, wrect.bottom - wpMain.height,
\r
2362 wpMain.width, wpMain.height, SWP_NOCOPYBITS|SWP_NOZORDER);
\r
2365 case WMSZ_BOTTOMLEFT:
\r
2367 SetWindowPos(hwndMain, NULL,
\r
2368 wrect.right - wpMain.width, wrect.top,
\r
2369 wpMain.width, wpMain.height, SWP_NOCOPYBITS|SWP_NOZORDER);
\r
2372 case WMSZ_BOTTOMRIGHT:
\r
2376 SetWindowPos(hwndMain, NULL, 0, 0, wpMain.width, wpMain.height,
\r
2377 SWP_NOCOPYBITS|SWP_NOZORDER|SWP_NOMOVE);
\r
2382 for (i = 0; i < N_BUTTONS; i++) {
\r
2383 if (buttonDesc[i].hwnd != NULL) {
\r
2384 DestroyWindow(buttonDesc[i].hwnd);
\r
2385 buttonDesc[i].hwnd = NULL;
\r
2387 if (appData.showButtonBar) {
\r
2388 buttonDesc[i].hwnd =
\r
2389 CreateWindow("BUTTON", buttonDesc[i].label,
\r
2390 WS_VISIBLE | WS_CHILD | BS_PUSHBUTTON,
\r
2391 boardRect.right - BUTTON_WIDTH*(N_BUTTONS-i),
\r
2392 messageRect.top, BUTTON_WIDTH, messageSize.cy, hwndMain,
\r
2393 (HMENU) buttonDesc[i].id,
\r
2394 (HINSTANCE) GetWindowLongPtr(hwndMain, GWLP_HINSTANCE), NULL);
\r
2396 SendMessage(buttonDesc[i].hwnd, WM_SETFONT,
\r
2397 (WPARAM)font[boardSize][MESSAGE_FONT]->hf,
\r
2398 MAKELPARAM(FALSE, 0));
\r
2400 if (buttonDesc[i].id == IDM_Pause)
\r
2401 hwndPause = buttonDesc[i].hwnd;
\r
2402 buttonDesc[i].wndproc = (WNDPROC)
\r
2403 SetWindowLongPtr(buttonDesc[i].hwnd, GWLP_WNDPROC, (LONG_PTR) ButtonProc);
\r
2406 if (gridPen != NULL) DeleteObject(gridPen);
\r
2407 if (highlightPen != NULL) DeleteObject(highlightPen);
\r
2408 if (premovePen != NULL) DeleteObject(premovePen);
\r
2409 if (lineGap != 0) {
\r
2410 logbrush.lbStyle = BS_SOLID;
\r
2411 logbrush.lbColor = RGB(0, 0, 0); /* grid pen color = black */
\r
2413 ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,
\r
2414 lineGap, &logbrush, 0, NULL);
\r
2415 logbrush.lbColor = highlightSquareColor;
\r
2417 ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,
\r
2418 lineGap, &logbrush, 0, NULL);
\r
2420 logbrush.lbColor = premoveHighlightColor;
\r
2422 ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,
\r
2423 lineGap, &logbrush, 0, NULL);
\r
2425 /* [HGM] Loop had to be split in part for vert. and hor. lines */
\r
2426 for (i = 0; i < BOARD_HEIGHT + 1; i++) {
\r
2427 gridEndpoints[i*2].x = boardRect.left + lineGap / 2;
\r
2428 gridEndpoints[i*2].y = gridEndpoints[i*2 + 1].y =
\r
2429 boardRect.top + lineGap / 2 + (i * (squareSize + lineGap));
\r
2430 gridEndpoints[i*2 + 1].x = boardRect.left + lineGap / 2 +
\r
2431 BOARD_WIDTH * (squareSize + lineGap);
\r
2432 gridVertexCounts[i*2] = gridVertexCounts[i*2 + 1] = 2;
\r
2434 for (i = 0; i < BOARD_WIDTH + 1; i++) {
\r
2435 gridEndpoints[i*2 + BOARD_HEIGHT*2 + 2].y = boardRect.top + lineGap / 2;
\r
2436 gridEndpoints[i*2 + BOARD_HEIGHT*2 + 2].x =
\r
2437 gridEndpoints[i*2 + 1 + BOARD_HEIGHT*2 + 2].x = boardRect.left +
\r
2438 lineGap / 2 + (i * (squareSize + lineGap));
\r
2439 gridEndpoints[i*2 + 1 + BOARD_HEIGHT*2 + 2].y =
\r
2440 boardRect.top + BOARD_HEIGHT * (squareSize + lineGap);
\r
2441 gridVertexCounts[i*2] = gridVertexCounts[i*2 + 1] = 2;
\r
2445 /* [HGM] Licensing requirement */
\r
2447 if(gameInfo.variant == VariantGothic) GothicPopUp( GOTHIC, VariantGothic); else
\r
2450 if(gameInfo.variant == VariantFalcon) GothicPopUp( FALCON, VariantFalcon); else
\r
2452 GothicPopUp( "", VariantNormal);
\r
2455 /* if (boardSize == oldBoardSize) return; [HGM] variant might have changed */
\r
2457 /* Load piece bitmaps for this board size */
\r
2458 for (i=0; i<=2; i++) {
\r
2459 for (piece = WhitePawn;
\r
2460 (int) piece < (int) BlackPawn;
\r
2461 piece = (ChessSquare) ((int) piece + 1)) {
\r
2462 if (pieceBitmap[i][piece] != NULL)
\r
2463 DeleteObject(pieceBitmap[i][piece]);
\r
2467 fontBitmapSquareSize = 0; /* [HGM] render: make sure pieces will be recreated, as we might need others now */
\r
2468 // Orthodox Chess pieces
\r
2469 pieceBitmap[0][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "s");
\r
2470 pieceBitmap[0][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "s");
\r
2471 pieceBitmap[0][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "s");
\r
2472 pieceBitmap[0][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "s");
\r
2473 pieceBitmap[0][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "s");
\r
2474 pieceBitmap[1][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "o");
\r
2475 pieceBitmap[1][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "o");
\r
2476 pieceBitmap[1][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "o");
\r
2477 pieceBitmap[1][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "o");
\r
2478 pieceBitmap[1][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "o");
\r
2479 pieceBitmap[2][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "w");
\r
2480 pieceBitmap[2][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "w");
\r
2481 pieceBitmap[2][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "w");
\r
2482 pieceBitmap[2][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "w");
\r
2483 pieceBitmap[2][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "w");
\r
2484 if( gameInfo.variant == VariantShogi && squareSize <= 72 && squareSize >= 33) {
\r
2485 // in Shogi, Hijack the unused Queen for Lance
\r
2486 pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "s");
\r
2487 pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "o");
\r
2488 pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "w");
\r
2490 pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "s");
\r
2491 pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "o");
\r
2492 pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "w");
\r
2495 if(squareSize <= 72 && squareSize >= 33) {
\r
2496 /* A & C are available in most sizes now */
\r
2497 if(squareSize != 49 && squareSize != 72 && squareSize != 33) { // Vortex-like
\r
2498 pieceBitmap[0][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "s");
\r
2499 pieceBitmap[1][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "o");
\r
2500 pieceBitmap[2][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "w");
\r
2501 pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "s");
\r
2502 pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "o");
\r
2503 pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "w");
\r
2504 pieceBitmap[0][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "s");
\r
2505 pieceBitmap[1][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "o");
\r
2506 pieceBitmap[2][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "w");
\r
2507 pieceBitmap[0][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "s");
\r
2508 pieceBitmap[1][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "o");
\r
2509 pieceBitmap[2][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "w");
\r
2510 } else { // Smirf-like
\r
2511 if(gameInfo.variant == VariantSChess) {
\r
2512 pieceBitmap[0][WhiteAngel] = DoLoadBitmap(hInst, "v", squareSize, "s");
\r
2513 pieceBitmap[1][WhiteAngel] = DoLoadBitmap(hInst, "v", squareSize, "o");
\r
2514 pieceBitmap[2][WhiteAngel] = DoLoadBitmap(hInst, "v", squareSize, "w");
\r
2516 pieceBitmap[0][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "s");
\r
2517 pieceBitmap[1][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "o");
\r
2518 pieceBitmap[2][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "w");
\r
2521 if(gameInfo.variant == VariantGothic) { // Vortex-like
\r
2522 pieceBitmap[0][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "s");
\r
2523 pieceBitmap[1][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "o");
\r
2524 pieceBitmap[2][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "w");
\r
2525 } else if(gameInfo.variant == VariantSChess && (squareSize == 49 || squareSize == 72)) {
\r
2526 pieceBitmap[0][WhiteMarshall] = DoLoadBitmap(hInst, "e", squareSize, "s");
\r
2527 pieceBitmap[1][WhiteMarshall] = DoLoadBitmap(hInst, "e", squareSize, "o");
\r
2528 pieceBitmap[2][WhiteMarshall] = DoLoadBitmap(hInst, "e", squareSize, "w");
\r
2529 } else { // WinBoard standard
\r
2530 pieceBitmap[0][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "s");
\r
2531 pieceBitmap[1][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "o");
\r
2532 pieceBitmap[2][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "w");
\r
2537 if(squareSize==72 || squareSize==49 || squareSize==33) { /* experiment with some home-made bitmaps */
\r
2538 pieceBitmap[0][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "s");
\r
2539 pieceBitmap[1][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "o");
\r
2540 pieceBitmap[2][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "w");
\r
2541 pieceBitmap[0][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "s");
\r
2542 pieceBitmap[1][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "o");
\r
2543 pieceBitmap[2][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "w");
\r
2544 pieceBitmap[0][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "s");
\r
2545 pieceBitmap[1][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "o");
\r
2546 pieceBitmap[2][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "w");
\r
2547 pieceBitmap[0][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "s");
\r
2548 pieceBitmap[1][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "o");
\r
2549 pieceBitmap[2][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "w");
\r
2550 pieceBitmap[0][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "s");
\r
2551 pieceBitmap[1][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "o");
\r
2552 pieceBitmap[2][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "w");
\r
2553 pieceBitmap[0][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "s");
\r
2554 pieceBitmap[1][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "o");
\r
2555 pieceBitmap[2][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "w");
\r
2556 pieceBitmap[0][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "s");
\r
2557 pieceBitmap[1][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "o");
\r
2558 pieceBitmap[2][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "w");
\r
2559 pieceBitmap[0][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "s");
\r
2560 pieceBitmap[1][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "o");
\r
2561 pieceBitmap[2][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "w");
\r
2562 pieceBitmap[0][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "s");
\r
2563 pieceBitmap[1][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "o");
\r
2564 pieceBitmap[2][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "w");
\r
2565 pieceBitmap[0][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "s");
\r
2566 pieceBitmap[1][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "o");
\r
2567 pieceBitmap[2][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "w");
\r
2569 if(gameInfo.variant == VariantShogi) { /* promoted Gold represemtations */
\r
2570 pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "wp", squareSize, "s");
\r
2571 pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "wp", squareSize, "o");
\r
2572 pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "w", squareSize, "w");
\r
2573 pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "wn", squareSize, "s");
\r
2574 pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "wn", squareSize, "o");
\r
2575 pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "w", squareSize, "w");
\r
2576 pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "ws", squareSize, "s");
\r
2577 pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "ws", squareSize, "o");
\r
2578 pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "w", squareSize, "w");
\r
2579 pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "wl", squareSize, "s");
\r
2580 pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "wl", squareSize, "o");
\r
2581 pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "w", squareSize, "w");
\r
2583 pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "s");
\r
2584 pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "o");
\r
2585 pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "w");
\r
2586 pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "s");
\r
2587 pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "o");
\r
2588 pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "w");
\r
2589 pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "s");
\r
2590 pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "o");
\r
2591 pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "w");
\r
2592 pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "s");
\r
2593 pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "o");
\r
2594 pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "w");
\r
2597 } else { /* other size, no special bitmaps available. Use smaller symbols */
\r
2598 if((int)boardSize < 2) minorSize = sizeInfo[0].squareSize;
\r
2599 else minorSize = sizeInfo[(int)boardSize - 2].squareSize;
\r
2600 pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "s");
\r
2601 pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "o");
\r
2602 pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "w");
\r
2603 pieceBitmap[0][WhiteCardinal] = DoLoadBitmap(hInst, "b", minorSize, "s");
\r
2604 pieceBitmap[1][WhiteCardinal] = DoLoadBitmap(hInst, "b", minorSize, "o");
\r
2605 pieceBitmap[2][WhiteCardinal] = DoLoadBitmap(hInst, "b", minorSize, "w");
\r
2606 pieceBitmap[0][WhiteDragon] = DoLoadBitmap(hInst, "r", minorSize, "s");
\r
2607 pieceBitmap[1][WhiteDragon] = DoLoadBitmap(hInst, "r", minorSize, "o");
\r
2608 pieceBitmap[2][WhiteDragon] = DoLoadBitmap(hInst, "r", minorSize, "w");
\r
2609 pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "s");
\r
2610 pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "o");
\r
2611 pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "w");
\r
2615 if(gameInfo.variant == VariantShogi && squareSize == 58)
\r
2616 /* special Shogi support in this size */
\r
2617 { for (i=0; i<=2; i++) { /* replace all bitmaps */
\r
2618 for (piece = WhitePawn;
\r
2619 (int) piece < (int) BlackPawn;
\r
2620 piece = (ChessSquare) ((int) piece + 1)) {
\r
2621 if (pieceBitmap[i][piece] != NULL)
\r
2622 DeleteObject(pieceBitmap[i][piece]);
\r
2625 pieceBitmap[0][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "o");
\r
2626 pieceBitmap[0][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "o");
\r
2627 pieceBitmap[0][WhiteBishop] = DoLoadBitmap(hInst, "sb", squareSize, "o");
\r
2628 pieceBitmap[0][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "o");
\r
2629 pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "o");
\r
2630 pieceBitmap[0][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "o");
\r
2631 pieceBitmap[0][WhiteFerz] = DoLoadBitmap(hInst, "sf", squareSize, "o");
\r
2632 pieceBitmap[0][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "o");
\r
2633 pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "su", squareSize, "o");
\r
2634 pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "sh", squareSize, "o");
\r
2635 pieceBitmap[0][WhiteCardinal] = DoLoadBitmap(hInst, "sa", squareSize, "o");
\r
2636 pieceBitmap[0][WhiteDragon] = DoLoadBitmap(hInst, "sc", squareSize, "o");
\r
2637 pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "sg", squareSize, "o");
\r
2638 pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "ss", squareSize, "o");
\r
2639 pieceBitmap[1][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "o");
\r
2640 pieceBitmap[1][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "o");
\r
2641 pieceBitmap[1][WhiteBishop] = DoLoadBitmap(hInst, "sb", squareSize, "o");
\r
2642 pieceBitmap[1][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "o");
\r
2643 pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "o");
\r
2644 pieceBitmap[1][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "o");
\r
2645 pieceBitmap[1][WhiteFerz] = DoLoadBitmap(hInst, "sf", squareSize, "o");
\r
2646 pieceBitmap[1][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "o");
\r
2647 pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "su", squareSize, "o");
\r
2648 pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "sh", squareSize, "o");
\r
2649 pieceBitmap[1][WhiteCardinal] = DoLoadBitmap(hInst, "sa", squareSize, "o");
\r
2650 pieceBitmap[1][WhiteDragon] = DoLoadBitmap(hInst, "sc", squareSize, "o");
\r
2651 pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "sg", squareSize, "o");
\r
2652 pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "ss", squareSize, "o");
\r
2653 pieceBitmap[2][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "w");
\r
2654 pieceBitmap[2][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "w");
\r
2655 pieceBitmap[2][WhiteBishop] = DoLoadBitmap(hInst, "sr", squareSize, "w");
\r
2656 pieceBitmap[2][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "w");
\r
2657 pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "w");
\r
2658 pieceBitmap[2][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "w");
\r
2659 pieceBitmap[2][WhiteFerz] = DoLoadBitmap(hInst, "sw", squareSize, "w");
\r
2660 pieceBitmap[2][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "w");
\r
2661 pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "sp", squareSize, "w");
\r
2662 pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "sn", squareSize, "w");
\r
2663 pieceBitmap[2][WhiteCardinal] = DoLoadBitmap(hInst, "sr", squareSize, "w");
\r
2664 pieceBitmap[2][WhiteDragon] = DoLoadBitmap(hInst, "sr", squareSize, "w");
\r
2665 pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "sl", squareSize, "w");
\r
2666 pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "sw", squareSize, "w");
\r
2672 PieceBitmap(ChessSquare p, int kind)
\r
2674 if ((int) p >= (int) BlackPawn)
\r
2675 p = (ChessSquare) ((int) p - (int) BlackPawn + (int) WhitePawn);
\r
2677 return pieceBitmap[kind][(int) p];
\r
2680 /***************************************************************/
\r
2682 #define MIN(a,b) (((a) < (b)) ? (a) : (b))
\r
2683 #define MAX(a,b) (((a) > (b)) ? (a) : (b))
\r
2685 #define MIN3(a,b,c) (((a) < (b) && (a) < (c)) ? (a) : (((b) < (a) && (b) < (c)) ? (b) : (c)))
\r
2686 #define MAX3(a,b,c) (((a) > (b) && (a) > (c)) ? (a) : (((b) > (a) && (b) > (c)) ? (b) : (c)))
\r
2690 SquareToPos(int row, int column, int * x, int * y)
\r
2693 *x = boardRect.left + lineGap + ((BOARD_WIDTH-1)-column) * (squareSize + lineGap);
\r
2694 *y = boardRect.top + lineGap + row * (squareSize + lineGap);
\r
2696 *x = boardRect.left + lineGap + column * (squareSize + lineGap);
\r
2697 *y = boardRect.top + lineGap + ((BOARD_HEIGHT-1)-row) * (squareSize + lineGap);
\r
2702 DrawCoordsOnDC(HDC hdc)
\r
2704 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
2705 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
2706 char str[2] = { NULLCHAR, NULLCHAR };
\r
2707 int oldMode, oldAlign, x, y, start, i;
\r
2711 if (!appData.showCoords)
\r
2714 start = flipView ? 1-(ONE!='1') : 23+(ONE!='1')-BOARD_HEIGHT;
\r
2716 oldBrush = SelectObject(hdc, GetStockObject(BLACK_BRUSH));
\r
2717 oldMode = SetBkMode(hdc, (appData.monoMode ? OPAQUE : TRANSPARENT));
\r
2718 oldAlign = GetTextAlign(hdc);
\r
2719 oldFont = SelectObject(hdc, font[boardSize][COORD_FONT]->hf);
\r
2721 y = boardRect.top + lineGap;
\r
2722 x = boardRect.left + lineGap + gameInfo.holdingsWidth*(squareSize + lineGap);
\r
2724 SetTextAlign(hdc, TA_LEFT|TA_TOP);
\r
2725 for (i = 0; i < BOARD_HEIGHT; i++) {
\r
2726 str[0] = files[start + i];
\r
2727 ExtTextOut(hdc, x + 2, y + 1, 0, NULL, str, 1, NULL);
\r
2728 y += squareSize + lineGap;
\r
2731 start = flipView ? 12-(BOARD_RGHT-BOARD_LEFT) : 12;
\r
2733 SetTextAlign(hdc, TA_RIGHT|TA_BOTTOM);
\r
2734 for (i = 0; i < BOARD_RGHT - BOARD_LEFT; i++) {
\r
2735 str[0] = ranks[start + i];
\r
2736 ExtTextOut(hdc, x + squareSize - 2, y - 1, 0, NULL, str, 1, NULL);
\r
2737 x += squareSize + lineGap;
\r
2740 SelectObject(hdc, oldBrush);
\r
2741 SetBkMode(hdc, oldMode);
\r
2742 SetTextAlign(hdc, oldAlign);
\r
2743 SelectObject(hdc, oldFont);
\r
2747 DrawGridOnDC(HDC hdc)
\r
2751 if (lineGap != 0) {
\r
2752 oldPen = SelectObject(hdc, gridPen);
\r
2753 PolyPolyline(hdc, gridEndpoints, gridVertexCounts, BOARD_WIDTH+BOARD_HEIGHT + 2);
\r
2754 SelectObject(hdc, oldPen);
\r
2758 #define HIGHLIGHT_PEN 0
\r
2759 #define PREMOVE_PEN 1
\r
2762 DrawHighlightOnDC(HDC hdc, BOOLEAN on, int x, int y, int pen)
\r
2765 HPEN oldPen, hPen;
\r
2766 if (lineGap == 0) return;
\r
2768 x1 = boardRect.left +
\r
2769 lineGap/2 + ((BOARD_WIDTH-1)-x) * (squareSize + lineGap);
\r
2770 y1 = boardRect.top +
\r
2771 lineGap/2 + y * (squareSize + lineGap);
\r
2773 x1 = boardRect.left +
\r
2774 lineGap/2 + x * (squareSize + lineGap);
\r
2775 y1 = boardRect.top +
\r
2776 lineGap/2 + ((BOARD_HEIGHT-1)-y) * (squareSize + lineGap);
\r
2778 hPen = pen ? premovePen : highlightPen;
\r
2779 oldPen = SelectObject(hdc, on ? hPen : gridPen);
\r
2780 MoveToEx(hdc, x1, y1, NULL);
\r
2781 LineTo(hdc, x1 + squareSize + lineGap, y1);
\r
2782 LineTo(hdc, x1 + squareSize + lineGap, y1 + squareSize + lineGap);
\r
2783 LineTo(hdc, x1, y1 + squareSize + lineGap);
\r
2784 LineTo(hdc, x1, y1);
\r
2785 SelectObject(hdc, oldPen);
\r
2789 DrawHighlightsOnDC(HDC hdc, HighlightInfo *h, int pen)
\r
2792 for (i=0; i<2; i++) {
\r
2793 if (h->sq[i].x >= 0 && h->sq[i].y >= 0)
\r
2794 DrawHighlightOnDC(hdc, TRUE,
\r
2795 h->sq[i].x, h->sq[i].y,
\r
2800 /* Note: sqcolor is used only in monoMode */
\r
2801 /* Note that this code is largely duplicated in woptions.c,
\r
2802 function DrawSampleSquare, so that needs to be updated too */
\r
2804 DrawPieceOnDC(HDC hdc, ChessSquare piece, int color, int sqcolor, int x, int y, HDC tmphdc)
\r
2806 HBITMAP oldBitmap;
\r
2810 if (appData.blindfold) return;
\r
2812 /* [AS] Use font-based pieces if needed */
\r
2813 if( fontBitmapSquareSize >= 0 && (squareSize > 32 || gameInfo.variant >= VariantShogi)) {
\r
2814 /* Create piece bitmaps, or do nothing if piece set is up to date */
\r
2815 CreatePiecesFromFont();
\r
2817 if( fontBitmapSquareSize == squareSize ) {
\r
2818 int index = TranslatePieceToFontPiece(piece);
\r
2820 SelectObject( tmphdc, hPieceMask[ index ] );
\r
2822 if(appData.upsideDown ? color==flipView : (flipView && gameInfo.variant == VariantShogi))
\r
2823 StretchBlt(hdc, x+squareSize, y+squareSize, -squareSize, -squareSize, tmphdc, 0, 0, squareSize, squareSize, SRCAND);
\r
2827 squareSize, squareSize,
\r
2832 SelectObject( tmphdc, hPieceFace[ index ] );
\r
2834 if(appData.upsideDown ? color==flipView : (flipView && gameInfo.variant == VariantShogi))
\r
2835 StretchBlt(hdc, x+squareSize, y+squareSize, -squareSize, -squareSize, tmphdc, 0, 0, squareSize, squareSize, SRCPAINT);
\r
2839 squareSize, squareSize,
\r
2848 if (appData.monoMode) {
\r
2849 SelectObject(tmphdc, PieceBitmap(piece,
\r
2850 color == sqcolor ? OUTLINE_PIECE : SOLID_PIECE));
\r
2851 BitBlt(hdc, x, y, squareSize, squareSize, tmphdc, 0, 0,
\r
2852 sqcolor ? SRCCOPY : NOTSRCCOPY);
\r
2854 tmpSize = squareSize;
\r
2856 ((piece >= (int)WhiteNightrider && piece <= WhiteGrasshopper) ||
\r
2857 (piece >= (int)BlackNightrider && piece <= BlackGrasshopper)) ) {
\r
2858 /* [HGM] no bitmap available for promoted pieces in Crazyhouse */
\r
2859 /* Bitmaps of smaller size are substituted, but we have to align them */
\r
2860 x += (squareSize - minorSize)>>1;
\r
2861 y += squareSize - minorSize - 2;
\r
2862 tmpSize = minorSize;
\r
2864 if (color || appData.allWhite ) {
\r
2865 oldBitmap = SelectObject(tmphdc, PieceBitmap(piece, WHITE_PIECE));
\r
2867 oldBrush = SelectObject(hdc, whitePieceBrush);
\r
2868 else oldBrush = SelectObject(hdc, blackPieceBrush);
\r
2869 if(appData.upsideDown && color==flipView)
\r
2870 StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, 0x00B8074A);
\r
2872 BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, 0x00B8074A);
\r
2873 /* Use black for outline of white pieces */
\r
2874 SelectObject(tmphdc, PieceBitmap(piece, OUTLINE_PIECE));
\r
2875 if(appData.upsideDown && color==flipView)
\r
2876 StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, SRCAND);
\r
2878 BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, SRCAND);
\r
2880 /* Use square color for details of black pieces */
\r
2881 oldBitmap = SelectObject(tmphdc, PieceBitmap(piece, SOLID_PIECE));
\r
2882 oldBrush = SelectObject(hdc, blackPieceBrush);
\r
2883 if(appData.upsideDown && !flipView)
\r
2884 StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, 0x00B8074A);
\r
2886 BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, 0x00B8074A);
\r
2888 SelectObject(hdc, oldBrush);
\r
2889 SelectObject(tmphdc, oldBitmap);
\r
2893 /* [AS] Compute a drawing mode for a square, based on specified settings (see DrawTile) */
\r
2894 int GetBackTextureMode( int algo )
\r
2896 int result = BACK_TEXTURE_MODE_DISABLED;
\r
2900 case BACK_TEXTURE_MODE_PLAIN:
\r
2901 result = 1; /* Always use identity map */
\r
2903 case BACK_TEXTURE_MODE_FULL_RANDOM:
\r
2904 result = 1 + (myrandom() % 3); /* Pick a transformation at random */
\r
2912 [AS] Compute and save texture drawing info, otherwise we may not be able
\r
2913 to handle redraws cleanly (as random numbers would always be different).
\r
2915 VOID RebuildTextureSquareInfo()
\r
2925 ZeroMemory( &backTextureSquareInfo, sizeof(backTextureSquareInfo) );
\r
2927 if( liteBackTexture != NULL ) {
\r
2928 if( GetObject( liteBackTexture, sizeof(bi), &bi ) > 0 ) {
\r
2929 lite_w = bi.bmWidth;
\r
2930 lite_h = bi.bmHeight;
\r
2934 if( darkBackTexture != NULL ) {
\r
2935 if( GetObject( darkBackTexture, sizeof(bi), &bi ) > 0 ) {
\r
2936 dark_w = bi.bmWidth;
\r
2937 dark_h = bi.bmHeight;
\r
2941 for( row=0; row<BOARD_HEIGHT; row++ ) {
\r
2942 for( col=0; col<BOARD_WIDTH; col++ ) {
\r
2943 if( (col + row) & 1 ) {
\r
2945 if( lite_w >= squareSize && lite_h >= squareSize ) {
\r
2946 if( lite_w >= squareSize*BOARD_WIDTH )
\r
2947 backTextureSquareInfo[row][col].x = (2*col+1)*lite_w/(2*BOARD_WIDTH) - squareSize/2; /* [HGM] cut out of center of virtual square */
\r
2949 backTextureSquareInfo[row][col].x = col * (lite_w - squareSize) / (BOARD_WIDTH-1); /* [HGM] divide by size-1 in stead of size! */
\r
2950 if( lite_h >= squareSize*BOARD_HEIGHT )
\r
2951 backTextureSquareInfo[row][col].y = (2*(BOARD_HEIGHT-row)-1)*lite_h/(2*BOARD_HEIGHT) - squareSize/2;
\r
2953 backTextureSquareInfo[row][col].y = (BOARD_HEIGHT-1-row) * (lite_h - squareSize) / (BOARD_HEIGHT-1);
\r
2954 backTextureSquareInfo[row][col].mode = GetBackTextureMode(liteBackTextureMode);
\r
2959 if( dark_w >= squareSize && dark_h >= squareSize ) {
\r
2960 if( dark_w >= squareSize*BOARD_WIDTH )
\r
2961 backTextureSquareInfo[row][col].x = (2*col+1) * dark_w / (2*BOARD_WIDTH) - squareSize/2;
\r
2963 backTextureSquareInfo[row][col].x = col * (dark_w - squareSize) / (BOARD_WIDTH-1);
\r
2964 if( dark_h >= squareSize*BOARD_HEIGHT )
\r
2965 backTextureSquareInfo[row][col].y = (2*(BOARD_HEIGHT-row)-1) * dark_h / (2*BOARD_HEIGHT) - squareSize/2;
\r
2967 backTextureSquareInfo[row][col].y = (BOARD_HEIGHT-1-row) * (dark_h - squareSize) / (BOARD_HEIGHT-1);
\r
2968 backTextureSquareInfo[row][col].mode = GetBackTextureMode(darkBackTextureMode);
\r
2975 /* [AS] Arrow highlighting support */
\r
2977 static double A_WIDTH = 5; /* Width of arrow body */
\r
2979 #define A_HEIGHT_FACTOR 6 /* Length of arrow "point", relative to body width */
\r
2980 #define A_WIDTH_FACTOR 3 /* Width of arrow "point", relative to body width */
\r
2982 static double Sqr( double x )
\r
2987 static int Round( double x )
\r
2989 return (int) (x + 0.5);
\r
2992 /* Draw an arrow between two points using current settings */
\r
2993 VOID DrawArrowBetweenPoints( HDC hdc, int s_x, int s_y, int d_x, int d_y )
\r
2996 double dx, dy, j, k, x, y;
\r
2998 if( d_x == s_x ) {
\r
2999 int h = (d_y > s_y) ? +A_WIDTH*A_HEIGHT_FACTOR : -A_WIDTH*A_HEIGHT_FACTOR;
\r
3001 arrow[0].x = s_x + A_WIDTH + 0.5;
\r
3004 arrow[1].x = s_x + A_WIDTH + 0.5;
\r
3005 arrow[1].y = d_y - h;
\r
3007 arrow[2].x = arrow[1].x + A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;
\r
3008 arrow[2].y = d_y - h;
\r
3013 arrow[5].x = arrow[1].x - 2*A_WIDTH + 0.5;
\r
3014 arrow[5].y = d_y - h;
\r
3016 arrow[4].x = arrow[5].x - A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;
\r
3017 arrow[4].y = d_y - h;
\r
3019 arrow[6].x = arrow[1].x - 2*A_WIDTH + 0.5;
\r
3022 else if( d_y == s_y ) {
\r
3023 int w = (d_x > s_x) ? +A_WIDTH*A_HEIGHT_FACTOR : -A_WIDTH*A_HEIGHT_FACTOR;
\r
3026 arrow[0].y = s_y + A_WIDTH + 0.5;
\r
3028 arrow[1].x = d_x - w;
\r
3029 arrow[1].y = s_y + A_WIDTH + 0.5;
\r
3031 arrow[2].x = d_x - w;
\r
3032 arrow[2].y = arrow[1].y + A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;
\r
3037 arrow[5].x = d_x - w;
\r
3038 arrow[5].y = arrow[1].y - 2*A_WIDTH + 0.5;
\r
3040 arrow[4].x = d_x - w;
\r
3041 arrow[4].y = arrow[5].y - A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;
\r
3044 arrow[6].y = arrow[1].y - 2*A_WIDTH + 0.5;
\r
3047 /* [AS] Needed a lot of paper for this! :-) */
\r
3048 dy = (double) (d_y - s_y) / (double) (d_x - s_x);
\r
3049 dx = (double) (s_x - d_x) / (double) (s_y - d_y);
\r
3051 j = sqrt( Sqr(A_WIDTH) / (1.0 + Sqr(dx)) );
\r
3053 k = sqrt( Sqr(A_WIDTH*A_HEIGHT_FACTOR) / (1.0 + Sqr(dy)) );
\r
3058 arrow[0].x = Round(x - j);
\r
3059 arrow[0].y = Round(y + j*dx);
\r
3061 arrow[1].x = Round(arrow[0].x + 2*j); // [HGM] prevent width to be affected by rounding twice
\r
3062 arrow[1].y = Round(arrow[0].y - 2*j*dx);
\r
3065 x = (double) d_x - k;
\r
3066 y = (double) d_y - k*dy;
\r
3069 x = (double) d_x + k;
\r
3070 y = (double) d_y + k*dy;
\r
3073 x = Round(x); y = Round(y); // [HGM] make sure width of shaft is rounded the same way on both ends
\r
3075 arrow[6].x = Round(x - j);
\r
3076 arrow[6].y = Round(y + j*dx);
\r
3078 arrow[2].x = Round(arrow[6].x + 2*j);
\r
3079 arrow[2].y = Round(arrow[6].y - 2*j*dx);
\r
3081 arrow[3].x = Round(arrow[2].x + j*(A_WIDTH_FACTOR-1));
\r
3082 arrow[3].y = Round(arrow[2].y - j*(A_WIDTH_FACTOR-1)*dx);
\r
3087 arrow[5].x = Round(arrow[6].x - j*(A_WIDTH_FACTOR-1));
\r
3088 arrow[5].y = Round(arrow[6].y + j*(A_WIDTH_FACTOR-1)*dx);
\r
3091 Polygon( hdc, arrow, 7 );
\r
3094 /* [AS] Draw an arrow between two squares */
\r
3095 VOID DrawArrowBetweenSquares( HDC hdc, int s_col, int s_row, int d_col, int d_row )
\r
3097 int s_x, s_y, d_x, d_y;
\r
3104 if( s_col == d_col && s_row == d_row ) {
\r
3108 /* Get source and destination points */
\r
3109 SquareToPos( s_row, s_col, &s_x, &s_y);
\r
3110 SquareToPos( d_row, d_col, &d_x, &d_y);
\r
3113 d_y += squareSize / 2 - squareSize / 4; // [HGM] round towards same centers on all sides!
\r
3115 else if( d_y < s_y ) {
\r
3116 d_y += squareSize / 2 + squareSize / 4;
\r
3119 d_y += squareSize / 2;
\r
3123 d_x += squareSize / 2 - squareSize / 4;
\r
3125 else if( d_x < s_x ) {
\r
3126 d_x += squareSize / 2 + squareSize / 4;
\r
3129 d_x += squareSize / 2;
\r
3132 s_x += squareSize / 2;
\r
3133 s_y += squareSize / 2;
\r
3135 /* Adjust width */
\r
3136 A_WIDTH = squareSize / 14.; //[HGM] make float
\r
3139 stLB.lbStyle = BS_SOLID;
\r
3140 stLB.lbColor = appData.highlightArrowColor;
\r
3143 hpen = CreatePen( PS_SOLID, 2, RGB(0x00,0x00,0x00) );
\r
3144 holdpen = SelectObject( hdc, hpen );
\r
3145 hbrush = CreateBrushIndirect( &stLB );
\r
3146 holdbrush = SelectObject( hdc, hbrush );
\r
3148 DrawArrowBetweenPoints( hdc, s_x, s_y, d_x, d_y );
\r
3150 SelectObject( hdc, holdpen );
\r
3151 SelectObject( hdc, holdbrush );
\r
3152 DeleteObject( hpen );
\r
3153 DeleteObject( hbrush );
\r
3156 BOOL HasHighlightInfo()
\r
3158 BOOL result = FALSE;
\r
3160 if( highlightInfo.sq[0].x >= 0 && highlightInfo.sq[0].y >= 0 &&
\r
3161 highlightInfo.sq[1].x >= 0 && highlightInfo.sq[1].y >= 0 )
\r
3169 BOOL IsDrawArrowEnabled()
\r
3171 BOOL result = FALSE;
\r
3173 if( appData.highlightMoveWithArrow && squareSize >= 32 ) {
\r
3180 VOID DrawArrowHighlight( HDC hdc )
\r
3182 if( IsDrawArrowEnabled() && HasHighlightInfo() ) {
\r
3183 DrawArrowBetweenSquares( hdc,
\r
3184 highlightInfo.sq[0].x, highlightInfo.sq[0].y,
\r
3185 highlightInfo.sq[1].x, highlightInfo.sq[1].y );
\r
3189 HRGN GetArrowHighlightClipRegion( HDC hdc )
\r
3191 HRGN result = NULL;
\r
3193 if( HasHighlightInfo() ) {
\r
3194 int x1, y1, x2, y2;
\r
3195 int sx, sy, dx, dy;
\r
3197 SquareToPos(highlightInfo.sq[0].y, highlightInfo.sq[0].x, &x1, &y1 );
\r
3198 SquareToPos(highlightInfo.sq[1].y, highlightInfo.sq[1].x, &x2, &y2 );
\r
3200 sx = MIN( x1, x2 );
\r
3201 sy = MIN( y1, y2 );
\r
3202 dx = MAX( x1, x2 ) + squareSize;
\r
3203 dy = MAX( y1, y2 ) + squareSize;
\r
3205 result = CreateRectRgn( sx, sy, dx, dy );
\r
3212 Warning: this function modifies the behavior of several other functions.
\r
3214 Basically, Winboard is optimized to avoid drawing the whole board if not strictly
\r
3215 needed. Unfortunately, the decision whether or not to perform a full or partial
\r
3216 repaint is scattered all over the place, which is not good for features such as
\r
3217 "arrow highlighting" that require a full repaint of the board.
\r
3219 So, I've tried to patch the code where I thought it made sense (e.g. after or during
\r
3220 user interaction, when speed is not so important) but especially to avoid errors
\r
3221 in the displayed graphics.
\r
3223 In such patched places, I always try refer to this function so there is a single
\r
3224 place to maintain knowledge.
\r
3226 To restore the original behavior, just return FALSE unconditionally.
\r
3228 BOOL IsFullRepaintPreferrable()
\r
3230 BOOL result = FALSE;
\r
3232 if( (appData.highlightLastMove || appData.highlightDragging) && IsDrawArrowEnabled() ) {
\r
3233 /* Arrow may appear on the board */
\r
3241 This function is called by DrawPosition to know whether a full repaint must
\r
3244 Only DrawPosition may directly call this function, which makes use of
\r
3245 some state information. Other function should call DrawPosition specifying
\r
3246 the repaint flag, and can use IsFullRepaintPreferrable if needed.
\r
3248 BOOL DrawPositionNeedsFullRepaint()
\r
3250 BOOL result = FALSE;
\r
3253 Probably a slightly better policy would be to trigger a full repaint
\r
3254 when animInfo.piece changes state (i.e. empty -> non-empty and viceversa),
\r
3255 but animation is fast enough that it's difficult to notice.
\r
3257 if( animInfo.piece == EmptySquare ) {
\r
3258 if( (appData.highlightLastMove || appData.highlightDragging) && IsDrawArrowEnabled() /*&& HasHighlightInfo()*/ ) {
\r
3267 DrawBoardOnDC(HDC hdc, Board board, HDC tmphdc)
\r
3269 int row, column, x, y, square_color, piece_color;
\r
3270 ChessSquare piece;
\r
3272 HDC texture_hdc = NULL;
\r
3274 /* [AS] Initialize background textures if needed */
\r
3275 if( liteBackTexture != NULL || darkBackTexture != NULL ) {
\r
3276 static int backTextureBoardSize; /* [HGM] boardsize: also new texture if board format changed */
\r
3277 if( backTextureSquareSize != squareSize
\r
3278 || backTextureBoardSize != BOARD_WIDTH+BOARD_FILES*BOARD_HEIGHT) {
\r
3279 backTextureBoardSize = BOARD_WIDTH+BOARD_FILES*BOARD_HEIGHT;
\r
3280 backTextureSquareSize = squareSize;
\r
3281 RebuildTextureSquareInfo();
\r
3284 texture_hdc = CreateCompatibleDC( hdc );
\r
3287 for (row = 0; row < BOARD_HEIGHT; row++) {
\r
3288 for (column = 0; column < BOARD_WIDTH; column++) {
\r
3290 SquareToPos(row, column, &x, &y);
\r
3292 piece = board[row][column];
\r
3294 square_color = ((column + row) % 2) == 1;
\r
3295 if( gameInfo.variant == VariantXiangqi ) {
\r
3296 square_color = !InPalace(row, column);
\r
3297 if(BOARD_HEIGHT&1) { if(row==BOARD_HEIGHT/2) square_color ^= 1; }
\r
3298 else if(row < BOARD_HEIGHT/2) square_color ^= 1;
\r
3300 piece_color = (int) piece < (int) BlackPawn;
\r
3303 /* [HGM] holdings file: light square or black */
\r
3304 if(column == BOARD_LEFT-2) {
\r
3305 if( row > BOARD_HEIGHT - gameInfo.holdingsSize - 1 )
\r
3308 DisplayHoldingsCount(hdc, x, y, 0, 0); /* black out */
\r
3312 if(column == BOARD_RGHT + 1 ) {
\r
3313 if( row < gameInfo.holdingsSize )
\r
3316 DisplayHoldingsCount(hdc, x, y, 0, 0);
\r
3320 if(column == BOARD_LEFT-1 ) /* left align */
\r
3321 DisplayHoldingsCount(hdc, x, y, flipView, (int) board[row][column]);
\r
3322 else if( column == BOARD_RGHT) /* right align */
\r
3323 DisplayHoldingsCount(hdc, x, y, !flipView, (int) board[row][column]);
\r
3325 if (appData.monoMode) {
\r
3326 if (piece == EmptySquare) {
\r
3327 BitBlt(hdc, x, y, squareSize, squareSize, 0, 0, 0,
\r
3328 square_color ? WHITENESS : BLACKNESS);
\r
3330 DrawPieceOnDC(hdc, piece, piece_color, square_color, x, y, tmphdc);
\r
3333 else if( backTextureSquareInfo[row][column].mode > 0 ) {
\r
3334 /* [AS] Draw the square using a texture bitmap */
\r
3335 HBITMAP hbm = SelectObject( texture_hdc, square_color ? liteBackTexture : darkBackTexture );
\r
3336 int r = row, c = column; // [HGM] do not flip board in flipView
\r
3337 if(flipView) { r = BOARD_HEIGHT-1 - r; c = BOARD_WIDTH-1 - c; }
\r
3340 squareSize, squareSize,
\r
3343 backTextureSquareInfo[r][c].mode,
\r
3344 backTextureSquareInfo[r][c].x,
\r
3345 backTextureSquareInfo[r][c].y );
\r
3347 SelectObject( texture_hdc, hbm );
\r
3349 if (piece != EmptySquare) {
\r
3350 DrawPieceOnDC(hdc, piece, piece_color, -1, x, y, tmphdc);
\r
3354 HBRUSH brush = square_color ? lightSquareBrush : darkSquareBrush;
\r
3356 oldBrush = SelectObject(hdc, brush );
\r
3357 BitBlt(hdc, x, y, squareSize, squareSize, 0, 0, 0, PATCOPY);
\r
3358 SelectObject(hdc, oldBrush);
\r
3359 if (piece != EmptySquare)
\r
3360 DrawPieceOnDC(hdc, piece, piece_color, -1, x, y, tmphdc);
\r
3365 if( texture_hdc != NULL ) {
\r
3366 DeleteDC( texture_hdc );
\r
3370 int saveDiagFlag = 0; FILE *diagFile; // [HGM] diag
\r
3371 void fputDW(FILE *f, int x)
\r
3373 fputc(x & 255, f);
\r
3374 fputc(x>>8 & 255, f);
\r
3375 fputc(x>>16 & 255, f);
\r
3376 fputc(x>>24 & 255, f);
\r
3379 #define MAX_CLIPS 200 /* more than enough */
\r
3382 DrawLogoOnDC(HDC hdc, RECT logoRect, HBITMAP logo)
\r
3384 // HBITMAP bufferBitmap;
\r
3389 int w = 100, h = 50;
\r
3391 if(logo == NULL) return;
\r
3392 // GetClientRect(hwndMain, &Rect);
\r
3393 // bufferBitmap = CreateCompatibleBitmap(hdc, Rect.right-Rect.left+1,
\r
3394 // Rect.bottom-Rect.top+1);
\r
3395 tmphdc = CreateCompatibleDC(hdc);
\r
3396 hbm = SelectObject(tmphdc, logo);
\r
3397 if( GetObject( logo, sizeof(bi), &bi ) > 0 ) {
\r
3401 StretchBlt(hdc, logoRect.left, logoRect.top, logoRect.right - logoRect.left,
\r
3402 logoRect.bottom - logoRect.top, tmphdc, 0, 0, w, h, SRCCOPY);
\r
3403 SelectObject(tmphdc, hbm);
\r
3411 HDC hdc = GetDC(hwndMain);
\r
3412 HBITMAP whiteLogo = (HBITMAP) first.programLogo, blackLogo = (HBITMAP) second.programLogo;
\r
3413 if(appData.autoLogo) {
\r
3415 switch(gameMode) { // pick logos based on game mode
\r
3416 case IcsObserving:
\r
3417 whiteLogo = second.programLogo; // ICS logo
\r
3418 blackLogo = second.programLogo;
\r
3421 case IcsPlayingWhite:
\r
3422 if(!appData.zippyPlay) whiteLogo = userLogo;
\r
3423 blackLogo = second.programLogo; // ICS logo
\r
3425 case IcsPlayingBlack:
\r
3426 whiteLogo = second.programLogo; // ICS logo
\r
3427 blackLogo = appData.zippyPlay ? first.programLogo : userLogo;
\r
3429 case TwoMachinesPlay:
\r
3430 if(first.twoMachinesColor[0] == 'b') {
\r
3431 whiteLogo = second.programLogo;
\r
3432 blackLogo = first.programLogo;
\r
3435 case MachinePlaysWhite:
\r
3436 blackLogo = userLogo;
\r
3438 case MachinePlaysBlack:
\r
3439 whiteLogo = userLogo;
\r
3440 blackLogo = first.programLogo;
\r
3443 DrawLogoOnDC(hdc, leftLogoRect, flipClock ? blackLogo : whiteLogo);
\r
3444 DrawLogoOnDC(hdc, rightLogoRect, flipClock ? whiteLogo : blackLogo);
\r
3445 ReleaseDC(hwndMain, hdc);
\r
3449 static HDC hdcSeek;
\r
3451 // [HGM] seekgraph
\r
3452 void DrawSeekAxis( int x, int y, int xTo, int yTo )
\r
3455 HPEN hp = SelectObject( hdcSeek, gridPen );
\r
3456 MoveToEx( hdcSeek, boardRect.left+x, boardRect.top+y, &stPt );
\r
3457 LineTo( hdcSeek, boardRect.left+xTo, boardRect.top+yTo );
\r
3458 SelectObject( hdcSeek, hp );
\r
3461 // front-end wrapper for drawing functions to do rectangles
\r
3462 void DrawSeekBackground( int left, int top, int right, int bottom )
\r
3467 if (hdcSeek == NULL) {
\r
3468 hdcSeek = GetDC(hwndMain);
\r
3469 if (!appData.monoMode) {
\r
3470 SelectPalette(hdcSeek, hPal, FALSE);
\r
3471 RealizePalette(hdcSeek);
\r
3474 hp = SelectObject( hdcSeek, gridPen );
\r
3475 rc.top = boardRect.top+top; rc.left = boardRect.left+left;
\r
3476 rc.bottom = boardRect.top+bottom; rc.right = boardRect.left+right;
\r
3477 FillRect( hdcSeek, &rc, lightSquareBrush );
\r
3478 SelectObject( hdcSeek, hp );
\r
3481 // front-end wrapper for putting text in graph
\r
3482 void DrawSeekText(char *buf, int x, int y)
\r
3485 SetBkMode( hdcSeek, TRANSPARENT );
\r
3486 GetTextExtentPoint32( hdcSeek, buf, strlen(buf), &stSize );
\r
3487 TextOut( hdcSeek, boardRect.left+x-3, boardRect.top+y-stSize.cy/2, buf, strlen(buf) );
\r
3490 void DrawSeekDot(int x, int y, int color)
\r
3492 int square = color & 0x80;
\r
3493 HBRUSH oldBrush = SelectObject(hdcSeek,
\r
3494 color == 0 ? markerBrush : color == 1 ? darkSquareBrush : explodeBrush);
\r
3497 Rectangle(hdcSeek, boardRect.left+x - squareSize/9, boardRect.top+y - squareSize/9,
\r
3498 boardRect.left+x + squareSize/9, boardRect.top+y + squareSize/9);
\r
3500 Ellipse(hdcSeek, boardRect.left+x - squareSize/8, boardRect.top+y - squareSize/8,
\r
3501 boardRect.left+x + squareSize/8, boardRect.top+y + squareSize/8);
\r
3502 SelectObject(hdcSeek, oldBrush);
\r
3506 HDCDrawPosition(HDC hdc, BOOLEAN repaint, Board board)
\r
3508 static Board lastReq[2], lastDrawn[2];
\r
3509 static HighlightInfo lastDrawnHighlight, lastDrawnPremove;
\r
3510 static int lastDrawnFlipView = 0;
\r
3511 static int lastReqValid[2] = {0, 0}, lastDrawnValid[2] = {0, 0};
\r
3512 int releaseDC, x, y, x2, y2, row, column, num_clips = 0, i;
\r
3515 HBITMAP bufferBitmap;
\r
3516 HBITMAP oldBitmap;
\r
3518 HRGN clips[MAX_CLIPS];
\r
3519 ChessSquare dragged_piece = EmptySquare;
\r
3520 int nr = twoBoards*partnerUp;
\r
3522 /* I'm undecided on this - this function figures out whether a full
\r
3523 * repaint is necessary on its own, so there's no real reason to have the
\r
3524 * caller tell it that. I think this can safely be set to FALSE - but
\r
3525 * if we trust the callers not to request full repaints unnessesarily, then
\r
3526 * we could skip some clipping work. In other words, only request a full
\r
3527 * redraw when the majority of pieces have changed positions (ie. flip,
\r
3528 * gamestart and similar) --Hawk
\r
3530 Boolean fullrepaint = repaint;
\r
3532 if(DrawSeekGraph()) return; // [HG} seekgraph: suppress printing board if seek graph up
\r
3534 if( DrawPositionNeedsFullRepaint() ) {
\r
3535 fullrepaint = TRUE;
\r
3538 if (board == NULL) {
\r
3539 if (!lastReqValid[nr]) {
\r
3542 board = lastReq[nr];
\r
3544 CopyBoard(lastReq[nr], board);
\r
3545 lastReqValid[nr] = 1;
\r
3548 if (doingSizing) {
\r
3552 if (IsIconic(hwndMain)) {
\r
3556 if (hdc == NULL) {
\r
3557 hdc = GetDC(hwndMain);
\r
3558 if (!appData.monoMode) {
\r
3559 SelectPalette(hdc, hPal, FALSE);
\r
3560 RealizePalette(hdc);
\r
3564 releaseDC = FALSE;
\r
3567 /* Create some work-DCs */
\r
3568 hdcmem = CreateCompatibleDC(hdc);
\r
3569 tmphdc = CreateCompatibleDC(hdc);
\r
3571 /* If dragging is in progress, we temporarely remove the piece */
\r
3572 /* [HGM] or temporarily decrease count if stacked */
\r
3573 /* !! Moved to before board compare !! */
\r
3574 if (dragInfo.from.x >= 0 && dragInfo.pos.x >= 0) {
\r
3575 dragged_piece = board[dragInfo.from.y][dragInfo.from.x];
\r
3576 if(dragInfo.from.x == BOARD_LEFT-2 ) {
\r
3577 if(--board[dragInfo.from.y][dragInfo.from.x+1] == 0 )
\r
3578 board[dragInfo.from.y][dragInfo.from.x] = EmptySquare;
\r
3580 if(dragInfo.from.x == BOARD_RGHT+1) {
\r
3581 if(--board[dragInfo.from.y][dragInfo.from.x-1] == 0 )
\r
3582 board[dragInfo.from.y][dragInfo.from.x] = EmptySquare;
\r
3584 board[dragInfo.from.y][dragInfo.from.x] = gatingPiece;
\r
3587 /* Figure out which squares need updating by comparing the
\r
3588 * newest board with the last drawn board and checking if
\r
3589 * flipping has changed.
\r
3591 if (!fullrepaint && lastDrawnValid[nr] && (nr == 1 || lastDrawnFlipView == flipView)) {
\r
3592 for (row = 0; row < BOARD_HEIGHT; row++) { /* [HGM] true size, not 8 */
\r
3593 for (column = 0; column < BOARD_WIDTH; column++) {
\r
3594 if (lastDrawn[nr][row][column] != board[row][column]) {
\r
3595 SquareToPos(row, column, &x, &y);
\r
3596 clips[num_clips++] =
\r
3597 CreateRectRgn(x, y, x + squareSize, y + squareSize);
\r
3601 if(nr == 0) { // [HGM] dual: no highlights on second board
\r
3602 for (i=0; i<2; i++) {
\r
3603 if (lastDrawnHighlight.sq[i].x != highlightInfo.sq[i].x ||
\r
3604 lastDrawnHighlight.sq[i].y != highlightInfo.sq[i].y) {
\r
3605 if (lastDrawnHighlight.sq[i].x >= 0 &&
\r
3606 lastDrawnHighlight.sq[i].y >= 0) {
\r
3607 SquareToPos(lastDrawnHighlight.sq[i].y,
\r
3608 lastDrawnHighlight.sq[i].x, &x, &y);
\r
3609 clips[num_clips++] =
\r
3610 CreateRectRgn(x - lineGap, y - lineGap,
\r
3611 x + squareSize + lineGap, y + squareSize + lineGap);
\r
3613 if (highlightInfo.sq[i].x >= 0 && highlightInfo.sq[i].y >= 0) {
\r
3614 SquareToPos(highlightInfo.sq[i].y, highlightInfo.sq[i].x, &x, &y);
\r
3615 clips[num_clips++] =
\r
3616 CreateRectRgn(x - lineGap, y - lineGap,
\r
3617 x + squareSize + lineGap, y + squareSize + lineGap);
\r
3621 for (i=0; i<2; i++) {
\r
3622 if (lastDrawnPremove.sq[i].x != premoveHighlightInfo.sq[i].x ||
\r
3623 lastDrawnPremove.sq[i].y != premoveHighlightInfo.sq[i].y) {
\r
3624 if (lastDrawnPremove.sq[i].x >= 0 &&
\r
3625 lastDrawnPremove.sq[i].y >= 0) {
\r
3626 SquareToPos(lastDrawnPremove.sq[i].y,
\r
3627 lastDrawnPremove.sq[i].x, &x, &y);
\r
3628 clips[num_clips++] =
\r
3629 CreateRectRgn(x - lineGap, y - lineGap,
\r
3630 x + squareSize + lineGap, y + squareSize + lineGap);
\r
3632 if (premoveHighlightInfo.sq[i].x >= 0 &&
\r
3633 premoveHighlightInfo.sq[i].y >= 0) {
\r
3634 SquareToPos(premoveHighlightInfo.sq[i].y,
\r
3635 premoveHighlightInfo.sq[i].x, &x, &y);
\r
3636 clips[num_clips++] =
\r
3637 CreateRectRgn(x - lineGap, y - lineGap,
\r
3638 x + squareSize + lineGap, y + squareSize + lineGap);
\r
3642 } else { // nr == 1
\r
3643 partnerHighlightInfo.sq[0].y = board[EP_STATUS-4];
\r
3644 partnerHighlightInfo.sq[0].x = board[EP_STATUS-3];
\r
3645 partnerHighlightInfo.sq[1].y = board[EP_STATUS-2];
\r
3646 partnerHighlightInfo.sq[1].x = board[EP_STATUS-1];
\r
3647 for (i=0; i<2; i++) {
\r
3648 if (partnerHighlightInfo.sq[i].x >= 0 &&
\r
3649 partnerHighlightInfo.sq[i].y >= 0) {
\r
3650 SquareToPos(partnerHighlightInfo.sq[i].y,
\r
3651 partnerHighlightInfo.sq[i].x, &x, &y);
\r
3652 clips[num_clips++] =
\r
3653 CreateRectRgn(x - lineGap, y - lineGap,
\r
3654 x + squareSize + lineGap, y + squareSize + lineGap);
\r
3656 if (oldPartnerHighlight.sq[i].x >= 0 &&
\r
3657 oldPartnerHighlight.sq[i].y >= 0) {
\r
3658 SquareToPos(oldPartnerHighlight.sq[i].y,
\r
3659 oldPartnerHighlight.sq[i].x, &x, &y);
\r
3660 clips[num_clips++] =
\r
3661 CreateRectRgn(x - lineGap, y - lineGap,
\r
3662 x + squareSize + lineGap, y + squareSize + lineGap);
\r
3667 fullrepaint = TRUE;
\r
3670 /* Create a buffer bitmap - this is the actual bitmap
\r
3671 * being written to. When all the work is done, we can
\r
3672 * copy it to the real DC (the screen). This avoids
\r
3673 * the problems with flickering.
\r
3675 GetClientRect(hwndMain, &Rect);
\r
3676 bufferBitmap = CreateCompatibleBitmap(hdc, Rect.right-Rect.left+1,
\r
3677 Rect.bottom-Rect.top+1);
\r
3678 oldBitmap = SelectObject(hdcmem, bufferBitmap);
\r
3679 if (!appData.monoMode) {
\r
3680 SelectPalette(hdcmem, hPal, FALSE);
\r
3683 /* Create clips for dragging */
\r
3684 if (!fullrepaint) {
\r
3685 if (dragInfo.from.x >= 0) {
\r
3686 SquareToPos(dragInfo.from.y, dragInfo.from.x, &x, &y);
\r
3687 clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);
\r
3689 if (dragInfo.start.x >= 0) {
\r
3690 SquareToPos(dragInfo.start.y, dragInfo.start.x, &x, &y);
\r
3691 clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);
\r
3693 if (dragInfo.pos.x >= 0) {
\r
3694 x = dragInfo.pos.x - squareSize / 2;
\r
3695 y = dragInfo.pos.y - squareSize / 2;
\r
3696 clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);
\r
3698 if (dragInfo.lastpos.x >= 0) {
\r
3699 x = dragInfo.lastpos.x - squareSize / 2;
\r
3700 y = dragInfo.lastpos.y - squareSize / 2;
\r
3701 clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);
\r
3705 /* Are we animating a move?
\r
3707 * - remove the piece from the board (temporarely)
\r
3708 * - calculate the clipping region
\r
3710 if (!fullrepaint) {
\r
3711 if (animInfo.piece != EmptySquare) {
\r
3712 board[animInfo.from.y][animInfo.from.x] = EmptySquare;
\r
3713 x = boardRect.left + animInfo.lastpos.x;
\r
3714 y = boardRect.top + animInfo.lastpos.y;
\r
3715 x2 = boardRect.left + animInfo.pos.x;
\r
3716 y2 = boardRect.top + animInfo.pos.y;
\r
3717 clips[num_clips++] = CreateRectRgn(MIN(x,x2), MIN(y,y2), MAX(x,x2)+squareSize, MAX(y,y2)+squareSize);
\r
3718 /* Slight kludge. The real problem is that after AnimateMove is
\r
3719 done, the position on the screen does not match lastDrawn.
\r
3720 This currently causes trouble only on e.p. captures in
\r
3721 atomic, where the piece moves to an empty square and then
\r
3722 explodes. The old and new positions both had an empty square
\r
3723 at the destination, but animation has drawn a piece there and
\r
3724 we have to remember to erase it. [HGM] moved until after setting lastDrawn */
\r
3725 lastDrawn[0][animInfo.to.y][animInfo.to.x] = animInfo.piece;
\r
3729 /* No clips? Make sure we have fullrepaint set to TRUE */
\r
3730 if (num_clips == 0)
\r
3731 fullrepaint = TRUE;
\r
3733 /* Set clipping on the memory DC */
\r
3734 if (!fullrepaint) {
\r
3735 SelectClipRgn(hdcmem, clips[0]);
\r
3736 for (x = 1; x < num_clips; x++) {
\r
3737 if (ExtSelectClipRgn(hdcmem, clips[x], RGN_OR) == ERROR)
\r
3738 abort(); // this should never ever happen!
\r
3742 /* Do all the drawing to the memory DC */
\r
3743 if(explodeInfo.radius) { // [HGM] atomic
\r
3745 int x, y, r=(explodeInfo.radius * squareSize)/100;
\r
3746 ChessSquare piece = board[explodeInfo.fromY][explodeInfo.fromX];
\r
3747 board[explodeInfo.fromY][explodeInfo.fromX] = EmptySquare; // suppress display of capturer
\r
3748 SquareToPos(explodeInfo.toY, explodeInfo.toX, &x, &y);
\r
3749 x += squareSize/2;
\r
3750 y += squareSize/2;
\r
3751 if(!fullrepaint) {
\r
3752 clips[num_clips] = CreateRectRgn(x-r, y-r, x+r, y+r);
\r
3753 ExtSelectClipRgn(hdcmem, clips[num_clips++], RGN_OR);
\r
3755 DrawGridOnDC(hdcmem);
\r
3756 DrawHighlightsOnDC(hdcmem, &highlightInfo, HIGHLIGHT_PEN);
\r
3757 DrawHighlightsOnDC(hdcmem, &premoveHighlightInfo, PREMOVE_PEN);
\r
3758 DrawBoardOnDC(hdcmem, board, tmphdc);
\r
3759 board[explodeInfo.fromY][explodeInfo.fromX] = piece;
\r
3760 oldBrush = SelectObject(hdcmem, explodeBrush);
\r
3761 Ellipse(hdcmem, x-r, y-r, x+r, y+r);
\r
3762 SelectObject(hdcmem, oldBrush);
\r
3764 DrawGridOnDC(hdcmem);
\r
3765 if(nr == 0) { // [HGM] dual: decide which highlights to draw
\r
3766 DrawHighlightsOnDC(hdcmem, &highlightInfo, HIGHLIGHT_PEN);
\r
3767 DrawHighlightsOnDC(hdcmem, &premoveHighlightInfo, PREMOVE_PEN);
\r
3769 DrawHighlightsOnDC(hdcmem, &partnerHighlightInfo, HIGHLIGHT_PEN);
\r
3770 oldPartnerHighlight = partnerHighlightInfo;
\r
3772 DrawBoardOnDC(hdcmem, board, tmphdc);
\r
3774 if(nr == 0) // [HGM] dual: markers only on left board
\r
3775 for (row = 0; row < BOARD_HEIGHT; row++) {
\r
3776 for (column = 0; column < BOARD_WIDTH; column++) {
\r
3777 if (marker[row][column]) { // marker changes only occur with full repaint!
\r
3778 HBRUSH oldBrush = SelectObject(hdcmem,
\r
3779 marker[row][column] == 2 ? markerBrush : explodeBrush);
\r
3780 SquareToPos(row, column, &x, &y);
\r
3781 Ellipse(hdcmem, x + squareSize/4, y + squareSize/4,
\r
3782 x + 3*squareSize/4, y + 3*squareSize/4);
\r
3783 SelectObject(hdcmem, oldBrush);
\r
3788 if( appData.highlightMoveWithArrow ) {
\r
3789 DrawArrowHighlight(hdcmem);
\r
3792 DrawCoordsOnDC(hdcmem);
\r
3794 CopyBoard(lastDrawn[nr], board); /* [HGM] Moved to here from end of routine, */
\r
3795 /* to make sure lastDrawn contains what is actually drawn */
\r
3797 /* Put the dragged piece back into place and draw it (out of place!) */
\r
3798 if (dragged_piece != EmptySquare) {
\r
3799 /* [HGM] or restack */
\r
3800 if(dragInfo.from.x == BOARD_LEFT-2 )
\r
3801 board[dragInfo.from.y][dragInfo.from.x+1]++;
\r
3803 if(dragInfo.from.x == BOARD_RGHT+1 )
\r
3804 board[dragInfo.from.y][dragInfo.from.x-1]++;
\r
3805 board[dragInfo.from.y][dragInfo.from.x] = dragged_piece;
\r
3806 x = dragInfo.pos.x - squareSize / 2;
\r
3807 y = dragInfo.pos.y - squareSize / 2;
\r
3808 DrawPieceOnDC(hdcmem, dragInfo.piece,
\r
3809 ((int) dragInfo.piece < (int) BlackPawn),
\r
3810 (dragInfo.from.y + dragInfo.from.x) % 2, x, y, tmphdc);
\r
3813 /* Put the animated piece back into place and draw it */
\r
3814 if (animInfo.piece != EmptySquare) {
\r
3815 board[animInfo.from.y][animInfo.from.x] = animInfo.piece;
\r
3816 x = boardRect.left + animInfo.pos.x;
\r
3817 y = boardRect.top + animInfo.pos.y;
\r
3818 DrawPieceOnDC(hdcmem, animInfo.piece,
\r
3819 ((int) animInfo.piece < (int) BlackPawn),
\r
3820 (animInfo.from.y + animInfo.from.x) % 2, x, y, tmphdc);
\r
3823 /* Release the bufferBitmap by selecting in the old bitmap
\r
3824 * and delete the memory DC
\r
3826 SelectObject(hdcmem, oldBitmap);
\r
3829 /* Set clipping on the target DC */
\r
3830 if (!fullrepaint) {
\r
3831 if(nr == 1) for (x = 0; x < num_clips; x++) { // [HGM] dual: translate clips
\r
3833 GetRgnBox(clips[x], &rect);
\r
3834 DeleteObject(clips[x]);
\r
3835 clips[x] = CreateRectRgn(rect.left + wpMain.width/2, rect.top,
\r
3836 rect.right + wpMain.width/2, rect.bottom);
\r
3838 SelectClipRgn(hdc, clips[0]);
\r
3839 for (x = 1; x < num_clips; x++) {
\r
3840 if (ExtSelectClipRgn(hdc, clips[x], RGN_OR) == ERROR)
\r
3841 abort(); // this should never ever happen!
\r
3845 /* Copy the new bitmap onto the screen in one go.
\r
3846 * This way we avoid any flickering
\r
3848 oldBitmap = SelectObject(tmphdc, bufferBitmap);
\r
3849 BitBlt(hdc, boardRect.left + twoBoards*partnerUp*wpMain.width/2, boardRect.top, // [HGM] dual
\r
3850 boardRect.right - boardRect.left,
\r
3851 boardRect.bottom - boardRect.top,
\r
3852 tmphdc, boardRect.left, boardRect.top, SRCCOPY);
\r
3853 if(saveDiagFlag) {
\r
3854 BITMAP b; int i, j=0, m, w, wb, fac=0; char *pData;
\r
3855 BITMAPINFOHEADER bih; int color[16], nrColors=0;
\r
3857 GetObject(bufferBitmap, sizeof(b), &b);
\r
3858 if(pData = malloc(b.bmWidthBytes*b.bmHeight + 10000)) {
\r
3859 bih.biSize = sizeof(BITMAPINFOHEADER);
\r
3860 bih.biWidth = b.bmWidth;
\r
3861 bih.biHeight = b.bmHeight;
\r
3863 bih.biBitCount = b.bmBitsPixel;
\r
3864 bih.biCompression = 0;
\r
3865 bih.biSizeImage = b.bmWidthBytes*b.bmHeight;
\r
3866 bih.biXPelsPerMeter = 0;
\r
3867 bih.biYPelsPerMeter = 0;
\r
3868 bih.biClrUsed = 0;
\r
3869 bih.biClrImportant = 0;
\r
3870 // fprintf(diagFile, "t=%d\nw=%d\nh=%d\nB=%d\nP=%d\nX=%d\n",
\r
3871 // b.bmType, b.bmWidth, b.bmHeight, b.bmWidthBytes, b.bmPlanes, b.bmBitsPixel);
\r
3872 GetDIBits(tmphdc,bufferBitmap,0,b.bmHeight,pData,(BITMAPINFO*)&bih,DIB_RGB_COLORS);
\r
3873 // fprintf(diagFile, "%8x\n", (int) pData);
\r
3875 wb = b.bmWidthBytes;
\r
3877 for(i=0; i<wb*(b.bmHeight - boardRect.top + OUTER_MARGIN)>>2; i++) {
\r
3878 int k = ((int*) pData)[i];
\r
3879 for(j=0; j<nrColors; j++) if(color[j] == k) break;
\r
3880 if(j >= 16) break;
\r
3882 if(j >= nrColors) nrColors = j+1;
\r
3884 if(j<16) { // 16 colors is enough. Compress to 4 bits per pixel
\r
3886 for(i=0; i<b.bmHeight - boardRect.top + OUTER_MARGIN; i++) {
\r
3887 for(w=0; w<(wb>>2); w+=2) {
\r
3888 int k = ((int*) pData)[(wb*i>>2) + w];
\r
3889 for(j=0; j<nrColors; j++) if(color[j] == k) break;
\r
3890 k = ((int*) pData)[(wb*i>>2) + w + 1];
\r
3891 for(m=0; m<nrColors; m++) if(color[m] == k) break;
\r
3892 pData[p++] = m | j<<4;
\r
3894 while(p&3) pData[p++] = 0;
\r
3897 wb = ((wb+31)>>5)<<2;
\r
3899 // write BITMAPFILEHEADER
\r
3900 fprintf(diagFile, "BM");
\r
3901 fputDW(diagFile, wb*(b.bmHeight - boardRect.top + OUTER_MARGIN)+0x36 + (fac?64:0));
\r
3902 fputDW(diagFile, 0);
\r
3903 fputDW(diagFile, 0x36 + (fac?64:0));
\r
3904 // write BITMAPINFOHEADER
\r
3905 fputDW(diagFile, 40);
\r
3906 fputDW(diagFile, b.bmWidth);
\r
3907 fputDW(diagFile, b.bmHeight - boardRect.top + OUTER_MARGIN);
\r
3908 if(fac) fputDW(diagFile, 0x040001); // planes and bits/pixel
\r
3909 else fputDW(diagFile, 0x200001); // planes and bits/pixel
\r
3910 fputDW(diagFile, 0);
\r
3911 fputDW(diagFile, 0);
\r
3912 fputDW(diagFile, 0);
\r
3913 fputDW(diagFile, 0);
\r
3914 fputDW(diagFile, 0);
\r
3915 fputDW(diagFile, 0);
\r
3916 // write color table
\r
3918 for(i=0; i<16; i++) fputDW(diagFile, color[i]);
\r
3919 // write bitmap data
\r
3920 for(i=0; i<wb*(b.bmHeight - boardRect.top + OUTER_MARGIN); i++)
\r
3921 fputc(pData[i], diagFile);
\r
3926 SelectObject(tmphdc, oldBitmap);
\r
3928 /* Massive cleanup */
\r
3929 for (x = 0; x < num_clips; x++)
\r
3930 DeleteObject(clips[x]);
\r
3933 DeleteObject(bufferBitmap);
\r
3936 ReleaseDC(hwndMain, hdc);
\r
3938 if (lastDrawnFlipView != flipView && nr == 0) {
\r
3940 CheckMenuItem(GetMenu(hwndMain),IDM_FlipView, MF_BYCOMMAND|MF_CHECKED);
\r
3942 CheckMenuItem(GetMenu(hwndMain),IDM_FlipView, MF_BYCOMMAND|MF_UNCHECKED);
\r
3945 /* CopyBoard(lastDrawn, board);*/
\r
3946 lastDrawnHighlight = highlightInfo;
\r
3947 lastDrawnPremove = premoveHighlightInfo;
\r
3948 lastDrawnFlipView = flipView;
\r
3949 lastDrawnValid[nr] = 1;
\r
3952 /* [HGM] diag: Save the current board display to the given open file and close the file */
\r
3957 saveDiagFlag = 1; diagFile = f;
\r
3958 HDCDrawPosition(NULL, TRUE, NULL);
\r
3966 /*---------------------------------------------------------------------------*\
\r
3967 | CLIENT PAINT PROCEDURE
\r
3968 | This is the main event-handler for the WM_PAINT message.
\r
3970 \*---------------------------------------------------------------------------*/
\r
3972 PaintProc(HWND hwnd)
\r
3978 if((hdc = BeginPaint(hwnd, &ps))) {
\r
3979 if (IsIconic(hwnd)) {
\r
3980 DrawIcon(hdc, 2, 2, iconCurrent);
\r
3982 if (!appData.monoMode) {
\r
3983 SelectPalette(hdc, hPal, FALSE);
\r
3984 RealizePalette(hdc);
\r
3986 HDCDrawPosition(hdc, 1, NULL);
\r
3987 if(twoBoards) { // [HGM] dual: also redraw other board in other orientation
\r
3988 flipView = !flipView; partnerUp = !partnerUp;
\r
3989 HDCDrawPosition(hdc, 1, NULL);
\r
3990 flipView = !flipView; partnerUp = !partnerUp;
\r
3993 SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);
\r
3994 ExtTextOut(hdc, messageRect.left, messageRect.top,
\r
3995 ETO_CLIPPED|ETO_OPAQUE,
\r
3996 &messageRect, messageText, strlen(messageText), NULL);
\r
3997 SelectObject(hdc, oldFont);
\r
3998 DisplayBothClocks();
\r
4001 EndPaint(hwnd,&ps);
\r
4009 * If the user selects on a border boundary, return -1; if off the board,
\r
4010 * return -2. Otherwise map the event coordinate to the square.
\r
4011 * The offset boardRect.left or boardRect.top must already have been
\r
4012 * subtracted from x.
\r
4014 int EventToSquare(x, limit)
\r
4022 if ((x % (squareSize + lineGap)) >= squareSize)
\r
4024 x /= (squareSize + lineGap);
\r
4036 DropEnable dropEnables[] = {
\r
4037 { 'P', DP_Pawn, N_("Pawn") },
\r
4038 { 'N', DP_Knight, N_("Knight") },
\r
4039 { 'B', DP_Bishop, N_("Bishop") },
\r
4040 { 'R', DP_Rook, N_("Rook") },
\r
4041 { 'Q', DP_Queen, N_("Queen") },
\r
4045 SetupDropMenu(HMENU hmenu)
\r
4047 int i, count, enable;
\r
4049 extern char white_holding[], black_holding[];
\r
4050 char item[MSG_SIZ];
\r
4052 for (i=0; i<sizeof(dropEnables)/sizeof(DropEnable); i++) {
\r
4053 p = strchr(gameMode == IcsPlayingWhite ? white_holding : black_holding,
\r
4054 dropEnables[i].piece);
\r
4056 while (p && *p++ == dropEnables[i].piece) count++;
\r
4057 snprintf(item, MSG_SIZ, "%s %d", T_(dropEnables[i].name), count);
\r
4058 enable = count > 0 || !appData.testLegality
\r
4059 /*!!temp:*/ || (gameInfo.variant == VariantCrazyhouse
\r
4060 && !appData.icsActive);
\r
4061 ModifyMenu(hmenu, dropEnables[i].command,
\r
4062 MF_BYCOMMAND | (enable ? MF_ENABLED : MF_GRAYED) | MF_STRING,
\r
4063 dropEnables[i].command, item);
\r
4067 void DragPieceBegin(int x, int y)
\r
4069 dragInfo.lastpos.x = boardRect.left + x;
\r
4070 dragInfo.lastpos.y = boardRect.top + y;
\r
4071 dragInfo.from.x = fromX;
\r
4072 dragInfo.from.y = fromY;
\r
4073 dragInfo.piece = boards[currentMove][fromY][fromX];
\r
4074 dragInfo.start = dragInfo.from;
\r
4075 SetCapture(hwndMain);
\r
4078 void DragPieceEnd(int x, int y)
\r
4081 dragInfo.start.x = dragInfo.start.y = -1;
\r
4082 dragInfo.from = dragInfo.start;
\r
4083 dragInfo.pos = dragInfo.lastpos = dragInfo.start;
\r
4086 void ChangeDragPiece(ChessSquare piece)
\r
4088 dragInfo.piece = piece;
\r
4091 /* Event handler for mouse messages */
\r
4093 MouseEvent(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
\r
4097 static int recursive = 0;
\r
4099 BOOLEAN forceFullRepaint = IsFullRepaintPreferrable(); /* [AS] */
\r
4102 if (message == WM_MBUTTONUP) {
\r
4103 /* Hideous kludge to fool TrackPopupMenu into paying attention
\r
4104 to the middle button: we simulate pressing the left button too!
\r
4106 PostMessage(hwnd, WM_LBUTTONDOWN, wParam, lParam);
\r
4107 PostMessage(hwnd, WM_LBUTTONUP, wParam, lParam);
\r
4113 pt.x = LOWORD(lParam);
\r
4114 pt.y = HIWORD(lParam);
\r
4115 x = EventToSquare(pt.x - boardRect.left, BOARD_WIDTH);
\r
4116 y = EventToSquare(pt.y - boardRect.top, BOARD_HEIGHT);
\r
4117 if (!flipView && y >= 0) {
\r
4118 y = BOARD_HEIGHT - 1 - y;
\r
4120 if (flipView && x >= 0) {
\r
4121 x = BOARD_WIDTH - 1 - x;
\r
4124 shiftKey = GetKeyState(VK_SHIFT) < 0; // [HGM] remember last shift status
\r
4126 switch (message) {
\r
4127 case WM_LBUTTONDOWN:
\r
4128 if (PtInRect((LPRECT) &whiteRect, pt)) {
\r
4129 ClockClick(flipClock);
\r
4130 } else if (PtInRect((LPRECT) &blackRect, pt)) {
\r
4131 ClockClick(!flipClock);
\r
4133 dragInfo.start.x = dragInfo.start.y = -1;
\r
4134 dragInfo.from = dragInfo.start;
\r
4135 if(fromX == -1 && frozen) { // not sure where this is for
\r
4136 fromX = fromY = -1;
\r
4137 DrawPosition(forceFullRepaint || FALSE, NULL); /* [AS] */
\r
4140 LeftClick(Press, pt.x - boardRect.left, pt.y - boardRect.top);
\r
4141 DrawPosition(TRUE, NULL);
\r
4144 case WM_LBUTTONUP:
\r
4145 LeftClick(Release, pt.x - boardRect.left, pt.y - boardRect.top);
\r
4146 DrawPosition(TRUE, NULL);
\r
4149 case WM_MOUSEMOVE:
\r
4150 if(SeekGraphClick(Press, pt.x - boardRect.left, pt.y - boardRect.top, 1)) break;
\r
4151 if(PromoScroll(pt.x - boardRect.left, pt.y - boardRect.top)) break;
\r
4152 MovePV(pt.x - boardRect.left, pt.y - boardRect.top, boardRect.bottom - boardRect.top);
\r
4153 if ((appData.animateDragging || appData.highlightDragging)
\r
4154 && (wParam & MK_LBUTTON)
\r
4155 && dragInfo.from.x >= 0)
\r
4157 BOOL full_repaint = FALSE;
\r
4159 if (appData.animateDragging) {
\r
4160 dragInfo.pos = pt;
\r
4162 if (appData.highlightDragging) {
\r
4163 SetHighlights(fromX, fromY, x, y);
\r
4164 if( IsDrawArrowEnabled() && (x < 0 || x >= BOARD_WIDTH || y < 0 || y >= BOARD_HEIGHT) ) {
\r
4165 full_repaint = TRUE;
\r
4169 DrawPosition( full_repaint, NULL);
\r
4171 dragInfo.lastpos = dragInfo.pos;
\r
4175 case WM_MOUSEWHEEL: // [DM]
\r
4176 { static int lastDir = 0; // [HGM] build in some hysteresis to avoid spurious events
\r
4177 /* Mouse Wheel is being rolled forward
\r
4178 * Play moves forward
\r
4180 if((short)HIWORD(wParam) < 0 && currentMove < forwardMostMove)
\r
4181 { if(lastDir == 1) ForwardEvent(); else lastDir = 1; } // [HGM] suppress first event in direction
\r
4182 /* Mouse Wheel is being rolled backward
\r
4183 * Play moves backward
\r
4185 if((short)HIWORD(wParam) > 0 && currentMove > backwardMostMove)
\r
4186 { if(lastDir == -1) BackwardEvent(); else lastDir = -1; }
\r
4190 case WM_MBUTTONUP:
\r
4191 case WM_RBUTTONUP:
\r
4193 RightClick(Release, pt.x - boardRect.left, pt.y - boardRect.top, &fromX, &fromY);
\r
4196 case WM_MBUTTONDOWN:
\r
4197 case WM_RBUTTONDOWN:
\r
4200 fromX = fromY = -1;
\r
4201 dragInfo.pos.x = dragInfo.pos.y = -1;
\r
4202 dragInfo.start.x = dragInfo.start.y = -1;
\r
4203 dragInfo.from = dragInfo.start;
\r
4204 dragInfo.lastpos = dragInfo.pos;
\r
4205 if (appData.highlightDragging) {
\r
4206 ClearHighlights();
\r
4209 /* [HGM] right mouse button in clock area edit-game mode ups clock */
\r
4210 if (PtInRect((LPRECT) &whiteRect, pt)) {
\r
4211 if (gameMode == EditGame || GetKeyState(VK_SHIFT) < 0) AdjustClock(flipClock, 1);
\r
4212 } else if (PtInRect((LPRECT) &blackRect, pt)) {
\r
4213 if (gameMode == EditGame || GetKeyState(VK_SHIFT) < 0) AdjustClock(!flipClock, 1);
\r
4217 DrawPosition(TRUE, NULL);
\r
4219 menuNr = RightClick(Press, pt.x - boardRect.left, pt.y - boardRect.top, &fromX, &fromY);
\r
4222 if (message == WM_MBUTTONDOWN) {
\r
4223 buttonCount = 3; /* even if system didn't think so */
\r
4224 if (wParam & MK_SHIFT)
\r
4225 MenuPopup(hwnd, pt, LoadMenu(hInst, "BlackPieceMenu"), -1);
\r
4227 MenuPopup(hwnd, pt, LoadMenu(hInst, "WhitePieceMenu"), -1);
\r
4228 } else { /* message == WM_RBUTTONDOWN */
\r
4229 /* Just have one menu, on the right button. Windows users don't
\r
4230 think to try the middle one, and sometimes other software steals
\r
4231 it, or it doesn't really exist. */
\r
4232 if(gameInfo.variant != VariantShogi)
\r
4233 MenuPopup(hwnd, pt, LoadMenu(hInst, "PieceMenu"), -1);
\r
4235 MenuPopup(hwnd, pt, LoadMenu(hInst, "ShogiPieceMenu"), -1);
\r
4239 SetCapture(hwndMain);
4242 hmenu = LoadMenu(hInst, "DropPieceMenu");
\r
4243 SetupDropMenu(hmenu);
\r
4244 MenuPopup(hwnd, pt, hmenu, -1);
\r
4254 /* Preprocess messages for buttons in main window */
\r
4256 ButtonProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
\r
4258 int id = GetWindowLongPtr(hwnd, GWLP_ID);
\r
4261 for (i=0; i<N_BUTTONS; i++) {
\r
4262 if (buttonDesc[i].id == id) break;
\r
4264 if (i == N_BUTTONS) return 0;
\r
4265 switch (message) {
\r
4270 dir = (wParam == VK_LEFT) ? -1 : 1;
\r
4271 SetFocus(buttonDesc[(i + dir + N_BUTTONS) % N_BUTTONS].hwnd);
\r
4278 SendMessage(hwndMain, WM_COMMAND, MAKEWPARAM(buttonDesc[i].id, 0), 0);
\r
4281 if (appData.icsActive && (isalpha((char)wParam) || wParam == '0')) {
\r
4282 // [HGM] movenum: only letters or leading zero should go to ICS input
\r
4283 HWND h = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
4284 if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);
\r
4286 SendMessage(h, WM_CHAR, wParam, lParam);
\r
4288 } else if (isalpha((char)wParam) || isdigit((char)wParam)){
\r
4289 TypeInEvent((char)wParam);
\r
4295 return CallWindowProc(buttonDesc[i].wndproc, hwnd, message, wParam, lParam);
\r
4298 /* Process messages for Promotion dialog box */
\r
4300 Promotion(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
4304 switch (message) {
\r
4305 case WM_INITDIALOG: /* message: initialize dialog box */
\r
4306 /* Center the dialog over the application window */
\r
4307 CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));
\r
4308 Translate(hDlg, DLG_PromotionKing);
\r
4309 ShowWindow(GetDlgItem(hDlg, PB_King),
\r
4310 (!appData.testLegality || gameInfo.variant == VariantSuicide ||
\r
4311 gameInfo.variant == VariantSpartan && !WhiteOnMove(currentMove) ||
\r
4312 gameInfo.variant == VariantGiveaway || gameInfo.variant == VariantSuper ) ?
\r
4313 SW_SHOW : SW_HIDE);
\r
4314 /* [HGM] Only allow C & A promotions if these pieces are defined */
\r
4315 ShowWindow(GetDlgItem(hDlg, PB_Archbishop),
\r
4316 ((PieceToChar(WhiteAngel) >= 'A' && WhiteOnMove(currentMove) &&
\r
4317 PieceToChar(WhiteAngel) != '~') ||
\r
4318 (PieceToChar(BlackAngel) >= 'A' && !WhiteOnMove(currentMove) &&
\r
4319 PieceToChar(BlackAngel) != '~') ) ?
\r
4320 SW_SHOW : SW_HIDE);
\r
4321 ShowWindow(GetDlgItem(hDlg, PB_Chancellor),
\r
4322 ((PieceToChar(WhiteMarshall) >= 'A' && WhiteOnMove(currentMove) &&
\r
4323 PieceToChar(WhiteMarshall) != '~') ||
\r
4324 (PieceToChar(BlackMarshall) >= 'A' && !WhiteOnMove(currentMove) &&
\r
4325 PieceToChar(BlackMarshall) != '~') ) ?
\r
4326 SW_SHOW : SW_HIDE);
\r
4327 /* [HGM] Hide B & R button in Shogi, use Q as promote, N as defer */
\r
4328 ShowWindow(GetDlgItem(hDlg, PB_Rook),
\r
4329 gameInfo.variant != VariantShogi ?
\r
4330 SW_SHOW : SW_HIDE);
\r
4331 ShowWindow(GetDlgItem(hDlg, PB_Bishop),
\r
4332 gameInfo.variant != VariantShogi ?
\r
4333 SW_SHOW : SW_HIDE);
\r
4334 if(gameInfo.variant == VariantShogi) {
\r
4335 SetDlgItemText(hDlg, PB_Queen, "YES");
\r
4336 SetDlgItemText(hDlg, PB_Knight, "NO");
\r
4337 SetWindowText(hDlg, "Promote?");
\r
4339 ShowWindow(GetDlgItem(hDlg, IDC_Centaur),
\r
4340 gameInfo.variant == VariantSuper ?
\r
4341 SW_SHOW : SW_HIDE);
\r
4344 case WM_COMMAND: /* message: received a command */
\r
4345 switch (LOWORD(wParam)) {
\r
4347 EndDialog(hDlg, TRUE); /* Exit the dialog */
\r
4348 ClearHighlights();
\r
4349 DrawPosition(FALSE, NULL);
\r
4352 promoChar = gameInfo.variant == VariantSuper ? PieceToChar(BlackSilver) : PieceToChar(BlackKing);
\r
4355 promoChar = gameInfo.variant == VariantShogi ? '+' : ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteQueen : BlackQueen));
\r
4358 promoChar = ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteRook : BlackRook));
\r
4359 if(gameInfo.variant == VariantSpartan && !WhiteOnMove(currentMove)) promoChar = PieceToChar(BlackDragon);
\r
4362 promoChar = ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteBishop : BlackBishop));
\r
4363 if(gameInfo.variant == VariantSpartan && !WhiteOnMove(currentMove)) promoChar = PieceToChar(BlackAlfil);
\r
4365 case PB_Chancellor:
\r
4366 promoChar = ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteMarshall : BlackMarshall));
\r
4368 case PB_Archbishop:
\r
4369 promoChar = ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteAngel : BlackAngel));
\r
4372 promoChar = gameInfo.variant == VariantShogi ? '=' : PieceToChar(WhiteOnMove(currentMove) ? WhiteKnight : BlackKnight);
\r
4377 if(promoChar == '.') return FALSE; // invalid piece chosen
\r
4378 EndDialog(hDlg, TRUE); /* Exit the dialog */
\r
4379 UserMoveEvent(fromX, fromY, toX, toY, promoChar);
\r
4380 fromX = fromY = -1;
\r
4381 if (!appData.highlightLastMove) {
\r
4382 ClearHighlights();
\r
4383 DrawPosition(FALSE, NULL);
\r
4390 /* Pop up promotion dialog */
\r
4392 PromotionPopup(HWND hwnd)
\r
4396 lpProc = MakeProcInstance((FARPROC)Promotion, hInst);
\r
4397 DialogBox(hInst, MAKEINTRESOURCE(DLG_PromotionKing),
\r
4398 hwnd, (DLGPROC)lpProc);
\r
4399 FreeProcInstance(lpProc);
\r
4405 DrawPosition(TRUE, NULL);
\r
4406 PromotionPopup(hwndMain);
\r
4409 /* Toggle ShowThinking */
\r
4411 ToggleShowThinking()
\r
4413 appData.showThinking = !appData.showThinking;
\r
4414 ShowThinkingEvent();
\r
4418 LoadGameDialog(HWND hwnd, char* title)
\r
4422 char fileTitle[MSG_SIZ];
\r
4423 f = OpenFileDialog(hwnd, "rb", "",
\r
4424 appData.oldSaveStyle ? "gam" : "pgn",
\r
4426 title, &number, fileTitle, NULL);
\r
4428 cmailMsgLoaded = FALSE;
\r
4429 if (number == 0) {
\r
4430 int error = GameListBuild(f);
\r
4432 DisplayError(_("Cannot build game list"), error);
\r
4433 } else if (!ListEmpty(&gameList) &&
\r
4434 ((ListGame *) gameList.tailPred)->number > 1) {
\r
4435 GameListPopUp(f, fileTitle);
\r
4438 GameListDestroy();
\r
4441 LoadGame(f, number, fileTitle, FALSE);
\r
4445 int get_term_width()
\r
4450 HFONT hfont, hold_font;
\r
4455 hText = GetDlgItem(hwndConsole, OPT_ConsoleText);
\r
4459 // get the text metrics
\r
4460 hdc = GetDC(hText);
\r
4461 lf = font[boardSize][CONSOLE_FONT]->lf;
\r
4462 if (consoleCF.dwEffects & CFE_BOLD)
\r
4463 lf.lfWeight = FW_BOLD;
\r
4464 if (consoleCF.dwEffects & CFE_ITALIC)
\r
4465 lf.lfItalic = TRUE;
\r
4466 if (consoleCF.dwEffects & CFE_STRIKEOUT)
\r
4467 lf.lfStrikeOut = TRUE;
\r
4468 if (consoleCF.dwEffects & CFE_UNDERLINE)
\r
4469 lf.lfUnderline = TRUE;
\r
4470 hfont = CreateFontIndirect(&lf);
\r
4471 hold_font = SelectObject(hdc, hfont);
\r
4472 GetTextMetrics(hdc, &tm);
\r
4473 SelectObject(hdc, hold_font);
\r
4474 DeleteObject(hfont);
\r
4475 ReleaseDC(hText, hdc);
\r
4477 // get the rectangle
\r
4478 SendMessage(hText, EM_GETRECT, 0, (LPARAM)&rc);
\r
4480 return (rc.right-rc.left) / tm.tmAveCharWidth;
\r
4483 void UpdateICSWidth(HWND hText)
\r
4485 LONG old_width, new_width;
\r
4487 new_width = get_term_width(hText, FALSE);
\r
4488 old_width = GetWindowLongPtr(hText, GWLP_USERDATA);
\r
4489 if (new_width != old_width)
\r
4491 ics_update_width(new_width);
\r
4492 SetWindowLongPtr(hText, GWLP_USERDATA, new_width);
\r
4497 ChangedConsoleFont()
\r
4500 CHARRANGE tmpsel, sel;
\r
4501 MyFont *f = font[boardSize][CONSOLE_FONT];
\r
4502 HWND hText = GetDlgItem(hwndConsole, OPT_ConsoleText);
\r
4503 HWND hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
4506 cfmt.cbSize = sizeof(CHARFORMAT);
\r
4507 cfmt.dwMask = CFM_FACE|CFM_SIZE|CFM_CHARSET;
\r
4508 safeStrCpy(cfmt.szFaceName, font[boardSize][CONSOLE_FONT]->mfp.faceName,
\r
4509 sizeof(cfmt.szFaceName)/sizeof(cfmt.szFaceName[0]) );
\r
4510 /* yHeight is expressed in twips. A twip is 1/20 of a font's point
\r
4511 * size. This was undocumented in the version of MSVC++ that I had
\r
4512 * when I wrote the code, but is apparently documented now.
\r
4514 cfmt.yHeight = (int)(f->mfp.pointSize * 20.0 + 0.5);
\r
4515 cfmt.bCharSet = f->lf.lfCharSet;
\r
4516 cfmt.bPitchAndFamily = f->lf.lfPitchAndFamily;
\r
4517 SendMessage(hText, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &cfmt);
\r
4518 SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &cfmt);
\r
4519 /* Why are the following seemingly needed too? */
\r
4520 SendMessage(hText, EM_SETCHARFORMAT, SCF_DEFAULT, (LPARAM) &cfmt);
\r
4521 SendMessage(hInput, EM_SETCHARFORMAT, SCF_DEFAULT, (LPARAM) &cfmt);
\r
4522 SendMessage(hText, EM_EXGETSEL, 0, (LPARAM)&sel);
\r
4524 tmpsel.cpMax = -1; /*999999?*/
\r
4525 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&tmpsel);
\r
4526 SendMessage(hText, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cfmt);
\r
4527 /* Trying putting this here too. It still seems to tickle a RichEdit
\r
4528 * bug: sometimes RichEdit indents the first line of a paragraph too.
\r
4530 paraf.cbSize = sizeof(paraf);
\r
4531 paraf.dwMask = PFM_OFFSET | PFM_STARTINDENT;
\r
4532 paraf.dxStartIndent = 0;
\r
4533 paraf.dxOffset = WRAP_INDENT;
\r
4534 SendMessage(hText, EM_SETPARAFORMAT, 0, (LPARAM) ¶f);
\r
4535 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
4536 UpdateICSWidth(hText);
\r
4539 /*---------------------------------------------------------------------------*\
\r
4541 * Window Proc for main window
\r
4543 \*---------------------------------------------------------------------------*/
\r
4545 /* Process messages for main window, etc. */
\r
4547 WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
\r
4550 int wmId, wmEvent;
\r
4554 char fileTitle[MSG_SIZ];
\r
4555 char buf[MSG_SIZ];
\r
4556 static SnapData sd;
\r
4558 switch (message) {
\r
4560 case WM_PAINT: /* message: repaint portion of window */
\r
4564 case WM_ERASEBKGND:
\r
4565 if (IsIconic(hwnd)) {
\r
4566 /* Cheat; change the message */
\r
4567 return (DefWindowProc(hwnd, WM_ICONERASEBKGND, wParam, lParam));
\r
4569 return (DefWindowProc(hwnd, message, wParam, lParam));
\r
4573 case WM_LBUTTONDOWN:
\r
4574 case WM_MBUTTONDOWN:
\r
4575 case WM_RBUTTONDOWN:
\r
4576 case WM_LBUTTONUP:
\r
4577 case WM_MBUTTONUP:
\r
4578 case WM_RBUTTONUP:
\r
4579 case WM_MOUSEMOVE:
\r
4580 case WM_MOUSEWHEEL:
\r
4581 MouseEvent(hwnd, message, wParam, lParam);
\r
4584 JAWS_KB_NAVIGATION
\r
4588 JAWS_ALT_INTERCEPT
\r
4590 if (appData.icsActive && ((char)wParam == '\r' || (char)wParam > ' ' && !((char)wParam >= '1' && (char)wParam <= '9'))) {
\r
4591 // [HGM] movenum: for non-zero digits we always do type-in dialog
\r
4592 HWND h = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
4593 if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);
\r
4595 SendMessage(h, message, wParam, lParam);
\r
4596 } else if(lParam != KF_REPEAT) {
\r
4597 if (isalpha((char)wParam) || isdigit((char)wParam)) {
\r
4598 TypeInEvent((char)wParam);
\r
4599 } else if((char)wParam == 003) CopyGameToClipboard();
\r
4600 else if((char)wParam == 026) PasteGameOrFENFromClipboard();
\r
4605 case WM_PALETTECHANGED:
\r
4606 if (hwnd != (HWND)wParam && !appData.monoMode) {
\r
4608 HDC hdc = GetDC(hwndMain);
\r
4609 SelectPalette(hdc, hPal, TRUE);
\r
4610 nnew = RealizePalette(hdc);
\r
4612 paletteChanged = TRUE;
\r
4613 InvalidateRect(hwnd, &boardRect, FALSE);
\r
4615 ReleaseDC(hwnd, hdc);
\r
4619 case WM_QUERYNEWPALETTE:
\r
4620 if (!appData.monoMode /*&& paletteChanged*/) {
\r
4622 HDC hdc = GetDC(hwndMain);
\r
4623 paletteChanged = FALSE;
\r
4624 SelectPalette(hdc, hPal, FALSE);
\r
4625 nnew = RealizePalette(hdc);
\r
4627 InvalidateRect(hwnd, &boardRect, FALSE);
\r
4629 ReleaseDC(hwnd, hdc);
\r
4634 case WM_COMMAND: /* message: command from application menu */
\r
4635 wmId = LOWORD(wParam);
\r
4636 wmEvent = HIWORD(wParam);
\r
4641 SAY("new game enter a move to play against the computer with white");
\r
4644 case IDM_NewGameFRC:
\r
4645 if( NewGameFRC() == 0 ) {
\r
4650 case IDM_NewVariant:
\r
4651 NewVariantPopup(hwnd);
\r
4654 case IDM_LoadGame:
\r
4655 LoadGameDialog(hwnd, _("Load Game from File"));
\r
4658 case IDM_LoadNextGame:
\r
4662 case IDM_LoadPrevGame:
\r
4666 case IDM_ReloadGame:
\r
4670 case IDM_LoadPosition:
\r
4671 if (gameMode == AnalyzeMode || gameMode == AnalyzeFile) {
\r
4672 Reset(FALSE, TRUE);
\r
4675 f = OpenFileDialog(hwnd, "rb", "",
\r
4676 appData.oldSaveStyle ? "pos" : "fen",
\r
4678 _("Load Position from File"), &number, fileTitle, NULL);
\r
4680 LoadPosition(f, number, fileTitle);
\r
4684 case IDM_LoadNextPosition:
\r
4685 ReloadPosition(1);
\r
4688 case IDM_LoadPrevPosition:
\r
4689 ReloadPosition(-1);
\r
4692 case IDM_ReloadPosition:
\r
4693 ReloadPosition(0);
\r
4696 case IDM_SaveGame:
\r
4697 defName = DefaultFileName(appData.oldSaveStyle ? "gam" : "pgn");
\r
4698 f = OpenFileDialog(hwnd, "a", defName,
\r
4699 appData.oldSaveStyle ? "gam" : "pgn",
\r
4701 _("Save Game to File"), NULL, fileTitle, NULL);
\r
4703 SaveGame(f, 0, "");
\r
4707 case IDM_SavePosition:
\r
4708 defName = DefaultFileName(appData.oldSaveStyle ? "pos" : "fen");
\r
4709 f = OpenFileDialog(hwnd, "a", defName,
\r
4710 appData.oldSaveStyle ? "pos" : "fen",
\r
4712 _("Save Position to File"), NULL, fileTitle, NULL);
\r
4714 SavePosition(f, 0, "");
\r
4718 case IDM_SaveDiagram:
\r
4719 defName = "diagram";
\r
4720 f = OpenFileDialog(hwnd, "wb", defName,
\r
4723 _("Save Diagram to File"), NULL, fileTitle, NULL);
\r
4729 case IDM_CopyGame:
\r
4730 CopyGameToClipboard();
\r
4733 case IDM_PasteGame:
\r
4734 PasteGameFromClipboard();
\r
4737 case IDM_CopyGameListToClipboard:
\r
4738 CopyGameListToClipboard();
\r
4741 /* [AS] Autodetect FEN or PGN data */
\r
4742 case IDM_PasteAny:
\r
4743 PasteGameOrFENFromClipboard();
\r
4746 /* [AS] Move history */
\r
4747 case IDM_ShowMoveHistory:
\r
4748 if( MoveHistoryIsUp() ) {
\r
4749 MoveHistoryPopDown();
\r
4752 MoveHistoryPopUp();
\r
4756 /* [AS] Eval graph */
\r
4757 case IDM_ShowEvalGraph:
\r
4758 if( EvalGraphIsUp() ) {
\r
4759 EvalGraphPopDown();
\r
4763 SetFocus(hwndMain);
\r
4767 /* [AS] Engine output */
\r
4768 case IDM_ShowEngineOutput:
\r
4769 if( EngineOutputIsUp() ) {
\r
4770 EngineOutputPopDown();
\r
4773 EngineOutputPopUp();
\r
4777 /* [AS] User adjudication */
\r
4778 case IDM_UserAdjudication_White:
\r
4779 UserAdjudicationEvent( +1 );
\r
4782 case IDM_UserAdjudication_Black:
\r
4783 UserAdjudicationEvent( -1 );
\r
4786 case IDM_UserAdjudication_Draw:
\r
4787 UserAdjudicationEvent( 0 );
\r
4790 /* [AS] Game list options dialog */
\r
4791 case IDM_GameListOptions:
\r
4792 GameListOptions();
\r
4799 case IDM_CopyPosition:
\r
4800 CopyFENToClipboard();
\r
4803 case IDM_PastePosition:
\r
4804 PasteFENFromClipboard();
\r
4807 case IDM_MailMove:
\r
4811 case IDM_ReloadCMailMsg:
\r
4812 Reset(TRUE, TRUE);
\r
4813 ReloadCmailMsgEvent(FALSE);
\r
4816 case IDM_Minimize:
\r
4817 ShowWindow(hwnd, SW_MINIMIZE);
\r
4824 case IDM_MachineWhite:
\r
4825 MachineWhiteEvent();
\r
4827 * refresh the tags dialog only if it's visible
\r
4829 if (gameMode == MachinePlaysWhite && IsWindowVisible(editTagsDialog)) {
\r
4831 tags = PGNTags(&gameInfo);
\r
4832 TagsPopUp(tags, CmailMsg());
\r
4835 SAY("computer starts playing white");
\r
4838 case IDM_MachineBlack:
\r
4839 MachineBlackEvent();
\r
4841 * refresh the tags dialog only if it's visible
\r
4843 if (gameMode == MachinePlaysBlack && IsWindowVisible(editTagsDialog)) {
\r
4845 tags = PGNTags(&gameInfo);
\r
4846 TagsPopUp(tags, CmailMsg());
\r
4849 SAY("computer starts playing black");
\r
4852 case IDM_Match: // [HGM] match: flows into next case, after setting Match Mode and nr of Games
\r
4853 MatchEvent(2); // distinguish from command-line-triggered case (matchMode=1)
\r
4856 case IDM_TwoMachines:
\r
4857 TwoMachinesEvent();
\r
4859 * refresh the tags dialog only if it's visible
\r
4861 if (gameMode == TwoMachinesPlay && IsWindowVisible(editTagsDialog)) {
\r
4863 tags = PGNTags(&gameInfo);
\r
4864 TagsPopUp(tags, CmailMsg());
\r
4867 SAY("computer starts playing both sides");
\r
4870 case IDM_AnalysisMode:
\r
4871 if (!first.analysisSupport) {
\r
4872 snprintf(buf, MSG_SIZ, _("%s does not support analysis"), first.tidy);
\r
4873 DisplayError(buf, 0);
\r
4875 SAY("analyzing current position");
\r
4876 /* [DM] icsEngineAnlyze [HGM] Why is this front-end??? */
\r
4877 if (appData.icsActive) {
\r
4878 if (gameMode != IcsObserving) {
\r
4879 snprintf(buf, MSG_SIZ, "You are not observing a game");
\r
4880 DisplayError(buf, 0);
\r
4881 /* secure check */
\r
4882 if (appData.icsEngineAnalyze) {
\r
4883 if (appData.debugMode)
\r
4884 fprintf(debugFP, "Found unexpected active ICS engine analyze \n");
\r
4885 ExitAnalyzeMode();
\r
4891 /* if enable, user want disable icsEngineAnalyze */
\r
4892 if (appData.icsEngineAnalyze) {
\r
4893 ExitAnalyzeMode();
\r
4897 appData.icsEngineAnalyze = TRUE;
\r
4898 if (appData.debugMode) fprintf(debugFP, "ICS engine analyze starting...\n");
\r
4901 if (!appData.showThinking) ToggleShowThinking();
\r
4902 AnalyzeModeEvent();
\r
4906 case IDM_AnalyzeFile:
\r
4907 if (!first.analysisSupport) {
\r
4908 char buf[MSG_SIZ];
\r
4909 snprintf(buf, MSG_SIZ, _("%s does not support analysis"), first.tidy);
\r
4910 DisplayError(buf, 0);
\r
4912 if (!appData.showThinking) ToggleShowThinking();
\r
4913 AnalyzeFileEvent();
\r
4914 LoadGameDialog(hwnd, _("Analyze Game from File"));
\r
4915 AnalysisPeriodicEvent(1);
\r
4919 case IDM_IcsClient:
\r
4923 case IDM_EditGame:
\r
4924 case IDM_EditGame2:
\r
4929 case IDM_EditPosition:
\r
4930 case IDM_EditPosition2:
\r
4931 EditPositionEvent();
\r
4932 SAY("enter a FEN string or setup a position on the board using the control R pop up menu");
\r
4935 case IDM_Training:
\r
4939 case IDM_ShowGameList:
\r
4940 ShowGameListProc();
\r
4943 case IDM_EditProgs1:
\r
4944 EditTagsPopUp(firstChessProgramNames, &firstChessProgramNames);
\r
4947 case IDM_EditProgs2:
\r
4948 LoadEnginePopUp(hwndMain);
\r
4949 // EditTagsPopUp(secondChessProgramNames, &secondChessProgramNames);
\r
4952 case IDM_EditServers:
\r
4953 EditTagsPopUp(icsNames, &icsNames);
\r
4956 case IDM_EditTags:
\r
4961 case IDM_EditBook:
\r
4965 case IDM_EditComment:
\r
4967 if (commentUp && editComment) {
\r
4970 EditCommentEvent();
\r
4990 case IDM_CallFlag:
\r
5010 case IDM_StopObserving:
\r
5011 StopObservingEvent();
\r
5014 case IDM_StopExamining:
\r
5015 StopExaminingEvent();
\r
5019 UploadGameEvent();
\r
5022 case IDM_TypeInMove:
\r
5023 TypeInEvent('\000');
\r
5026 case IDM_TypeInName:
\r
5027 PopUpNameDialog('\000');
\r
5030 case IDM_Backward:
\r
5032 SetFocus(hwndMain);
\r
5039 SetFocus(hwndMain);
\r
5044 SetFocus(hwndMain);
\r
5049 SetFocus(hwndMain);
\r
5053 RevertEvent(FALSE);
\r
5056 case IDM_Annotate: // [HGM] vari: revert with annotation
\r
5057 RevertEvent(TRUE);
\r
5060 case IDM_TruncateGame:
\r
5061 TruncateGameEvent();
\r
5068 case IDM_RetractMove:
\r
5069 RetractMoveEvent();
\r
5072 case IDM_FlipView:
\r
5073 flipView = !flipView;
\r
5074 DrawPosition(FALSE, NULL);
\r
5077 case IDM_FlipClock:
\r
5078 flipClock = !flipClock;
\r
5079 DisplayBothClocks();
\r
5083 case IDM_MuteSounds:
\r
5084 mute = !mute; // [HGM] mute: keep track of global muting variable
\r
5085 CheckMenuItem(GetMenu(hwndMain),IDM_MuteSounds,
\r
5086 MF_BYCOMMAND|(mute?MF_CHECKED:MF_UNCHECKED));
\r
5089 case IDM_GeneralOptions:
\r
5090 GeneralOptionsPopup(hwnd);
\r
5091 DrawPosition(TRUE, NULL);
\r
5094 case IDM_BoardOptions:
\r
5095 BoardOptionsPopup(hwnd);
\r
5098 case IDM_EnginePlayOptions:
\r
5099 EnginePlayOptionsPopup(hwnd);
\r
5102 case IDM_Engine1Options:
\r
5103 EngineOptionsPopup(hwnd, &first);
\r
5106 case IDM_Engine2Options:
\r
5108 if(WaitForEngine(&second, SettingsMenuIfReady)) break;
\r
5109 EngineOptionsPopup(hwnd, &second);
\r
5112 case IDM_OptionsUCI:
\r
5113 UciOptionsPopup(hwnd);
\r
5117 TourneyPopup(hwnd);
\r
5120 case IDM_IcsOptions:
\r
5121 IcsOptionsPopup(hwnd);
\r
5125 FontsOptionsPopup(hwnd);
\r
5129 SoundOptionsPopup(hwnd);
\r
5132 case IDM_CommPort:
\r
5133 CommPortOptionsPopup(hwnd);
\r
5136 case IDM_LoadOptions:
\r
5137 LoadOptionsPopup(hwnd);
\r
5140 case IDM_SaveOptions:
\r
5141 SaveOptionsPopup(hwnd);
\r
5144 case IDM_TimeControl:
\r
5145 TimeControlOptionsPopup(hwnd);
\r
5148 case IDM_SaveSettings:
\r
5149 SaveSettings(settingsFileName);
\r
5152 case IDM_SaveSettingsOnExit:
\r
5153 saveSettingsOnExit = !saveSettingsOnExit;
\r
5154 (void) CheckMenuItem(GetMenu(hwndMain), IDM_SaveSettingsOnExit,
\r
5155 MF_BYCOMMAND|(saveSettingsOnExit ?
\r
5156 MF_CHECKED : MF_UNCHECKED));
\r
5167 case IDM_AboutGame:
\r
5172 appData.debugMode = !appData.debugMode;
\r
5173 if (appData.debugMode) {
\r
5174 char dir[MSG_SIZ];
\r
5175 GetCurrentDirectory(MSG_SIZ, dir);
\r
5176 SetCurrentDirectory(installDir);
\r
5177 debugFP = fopen(appData.nameOfDebugFile, "w");
\r
5178 SetCurrentDirectory(dir);
\r
5179 setbuf(debugFP, NULL);
\r
5186 case IDM_HELPCONTENTS:
\r
5187 if (!MyHelp (hwnd, "winboard.hlp", HELP_KEY,(DWORD)(LPSTR)"CONTENTS") &&
\r
5188 !HtmlHelp(hwnd, "winboard.chm", 0, 0) ) {
\r
5189 MessageBox (GetFocus(),
\r
5190 _("Unable to activate help"),
\r
5191 szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);
\r
5195 case IDM_HELPSEARCH:
\r
5196 if (!MyHelp (hwnd, "winboard.hlp", HELP_PARTIALKEY, (DWORD)(LPSTR)"") &&
\r
5197 !HtmlHelp(hwnd, "winboard.chm", 0, 0) ) {
\r
5198 MessageBox (GetFocus(),
\r
5199 _("Unable to activate help"),
\r
5200 szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);
\r
5204 case IDM_HELPHELP:
\r
5205 if(!WinHelp(hwnd, (LPSTR)NULL, HELP_HELPONHELP, 0)) {
\r
5206 MessageBox (GetFocus(),
\r
5207 _("Unable to activate help"),
\r
5208 szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);
\r
5213 lpProc = MakeProcInstance((FARPROC)About, hInst);
\r
5215 (gameInfo.event && strcmp(gameInfo.event, "Easter Egg Hunt") == 0) ?
\r
5216 "AboutBox2" : "AboutBox", hwnd, (DLGPROC)lpProc);
\r
5217 FreeProcInstance(lpProc);
\r
5220 case IDM_DirectCommand1:
\r
5221 AskQuestionEvent(_("Direct Command"),
\r
5222 _("Send to chess program:"), "", "1");
\r
5224 case IDM_DirectCommand2:
\r
5225 AskQuestionEvent(_("Direct Command"),
\r
5226 _("Send to second chess program:"), "", "2");
\r
5229 case EP_WhitePawn:
\r
5230 EditPositionMenuEvent(WhitePawn, fromX, fromY);
\r
5231 fromX = fromY = -1;
\r
5234 case EP_WhiteKnight:
\r
5235 EditPositionMenuEvent(WhiteKnight, fromX, fromY);
\r
5236 fromX = fromY = -1;
\r
5239 case EP_WhiteBishop:
\r
5240 EditPositionMenuEvent(WhiteBishop, fromX, fromY);
\r
5241 fromX = fromY = -1;
\r
5244 case EP_WhiteRook:
\r
5245 EditPositionMenuEvent(WhiteRook, fromX, fromY);
\r
5246 fromX = fromY = -1;
\r
5249 case EP_WhiteQueen:
\r
5250 EditPositionMenuEvent(WhiteQueen, fromX, fromY);
\r
5251 fromX = fromY = -1;
\r
5254 case EP_WhiteFerz:
\r
5255 EditPositionMenuEvent(WhiteFerz, fromX, fromY);
\r
5256 fromX = fromY = -1;
\r
5259 case EP_WhiteWazir:
\r
5260 EditPositionMenuEvent(WhiteWazir, fromX, fromY);
\r
5261 fromX = fromY = -1;
\r
5264 case EP_WhiteAlfil:
\r
5265 EditPositionMenuEvent(WhiteAlfil, fromX, fromY);
\r
5266 fromX = fromY = -1;
\r
5269 case EP_WhiteCannon:
\r
5270 EditPositionMenuEvent(WhiteCannon, fromX, fromY);
\r
5271 fromX = fromY = -1;
\r
5274 case EP_WhiteCardinal:
\r
5275 EditPositionMenuEvent(WhiteAngel, fromX, fromY);
\r
5276 fromX = fromY = -1;
\r
5279 case EP_WhiteMarshall:
\r
5280 EditPositionMenuEvent(WhiteMarshall, fromX, fromY);
\r
5281 fromX = fromY = -1;
\r
5284 case EP_WhiteKing:
\r
5285 EditPositionMenuEvent(WhiteKing, fromX, fromY);
\r
5286 fromX = fromY = -1;
\r
5289 case EP_BlackPawn:
\r
5290 EditPositionMenuEvent(BlackPawn, fromX, fromY);
\r
5291 fromX = fromY = -1;
\r
5294 case EP_BlackKnight:
\r
5295 EditPositionMenuEvent(BlackKnight, fromX, fromY);
\r
5296 fromX = fromY = -1;
\r
5299 case EP_BlackBishop:
\r
5300 EditPositionMenuEvent(BlackBishop, fromX, fromY);
\r
5301 fromX = fromY = -1;
\r
5304 case EP_BlackRook:
\r
5305 EditPositionMenuEvent(BlackRook, fromX, fromY);
\r
5306 fromX = fromY = -1;
\r
5309 case EP_BlackQueen:
\r
5310 EditPositionMenuEvent(BlackQueen, fromX, fromY);
\r
5311 fromX = fromY = -1;
\r
5314 case EP_BlackFerz:
\r
5315 EditPositionMenuEvent(BlackFerz, fromX, fromY);
\r
5316 fromX = fromY = -1;
\r
5319 case EP_BlackWazir:
\r
5320 EditPositionMenuEvent(BlackWazir, fromX, fromY);
\r
5321 fromX = fromY = -1;
\r
5324 case EP_BlackAlfil:
\r
5325 EditPositionMenuEvent(BlackAlfil, fromX, fromY);
\r
5326 fromX = fromY = -1;
\r
5329 case EP_BlackCannon:
\r
5330 EditPositionMenuEvent(BlackCannon, fromX, fromY);
\r
5331 fromX = fromY = -1;
\r
5334 case EP_BlackCardinal:
\r
5335 EditPositionMenuEvent(BlackAngel, fromX, fromY);
\r
5336 fromX = fromY = -1;
\r
5339 case EP_BlackMarshall:
\r
5340 EditPositionMenuEvent(BlackMarshall, fromX, fromY);
\r
5341 fromX = fromY = -1;
\r
5344 case EP_BlackKing:
\r
5345 EditPositionMenuEvent(BlackKing, fromX, fromY);
\r
5346 fromX = fromY = -1;
\r
5349 case EP_EmptySquare:
\r
5350 EditPositionMenuEvent(EmptySquare, fromX, fromY);
\r
5351 fromX = fromY = -1;
\r
5354 case EP_ClearBoard:
\r
5355 EditPositionMenuEvent(ClearBoard, fromX, fromY);
\r
5356 fromX = fromY = -1;
\r
5360 EditPositionMenuEvent(WhitePlay, fromX, fromY);
\r
5361 fromX = fromY = -1;
\r
5365 EditPositionMenuEvent(BlackPlay, fromX, fromY);
\r
5366 fromX = fromY = -1;
\r
5370 EditPositionMenuEvent(PromotePiece, fromX, fromY);
\r
5371 fromX = fromY = -1;
\r
5375 EditPositionMenuEvent(DemotePiece, fromX, fromY);
\r
5376 fromX = fromY = -1;
\r
5380 DropMenuEvent(WhitePawn, fromX, fromY);
\r
5381 fromX = fromY = -1;
\r
5385 DropMenuEvent(WhiteKnight, fromX, fromY);
\r
5386 fromX = fromY = -1;
\r
5390 DropMenuEvent(WhiteBishop, fromX, fromY);
\r
5391 fromX = fromY = -1;
\r
5395 DropMenuEvent(WhiteRook, fromX, fromY);
\r
5396 fromX = fromY = -1;
\r
5400 DropMenuEvent(WhiteQueen, fromX, fromY);
\r
5401 fromX = fromY = -1;
\r
5405 barbaric = 0; appData.language = "";
\r
5406 TranslateMenus(0);
\r
5407 CheckMenuItem(GetMenu(hwndMain), lastChecked, MF_BYCOMMAND|MF_UNCHECKED);
\r
5408 CheckMenuItem(GetMenu(hwndMain), IDM_English, MF_BYCOMMAND|MF_CHECKED);
\r
5409 lastChecked = wmId;
\r
5413 if(wmId > IDM_English && wmId < IDM_English+20) {
\r
5414 LoadLanguageFile(languageFile[wmId - IDM_English - 1]);
\r
5415 TranslateMenus(0);
\r
5416 CheckMenuItem(GetMenu(hwndMain), lastChecked, MF_BYCOMMAND|MF_UNCHECKED);
\r
5417 CheckMenuItem(GetMenu(hwndMain), wmId, MF_BYCOMMAND|MF_CHECKED);
\r
5418 lastChecked = wmId;
\r
5421 return (DefWindowProc(hwnd, message, wParam, lParam));
\r
5427 case CLOCK_TIMER_ID:
\r
5428 KillTimer(hwnd, clockTimerEvent); /* Simulate one-shot timer as in X */
\r
5429 clockTimerEvent = 0;
\r
5430 DecrementClocks(); /* call into back end */
\r
5432 case LOAD_GAME_TIMER_ID:
\r
5433 KillTimer(hwnd, loadGameTimerEvent); /* Simulate one-shot timer as in X*/
\r
5434 loadGameTimerEvent = 0;
\r
5435 AutoPlayGameLoop(); /* call into back end */
\r
5437 case ANALYSIS_TIMER_ID:
\r
5438 if ((gameMode == AnalyzeMode || gameMode == AnalyzeFile
\r
5439 || appData.icsEngineAnalyze) && appData.periodicUpdates) {
\r
5440 AnalysisPeriodicEvent(0);
\r
5442 KillTimer(hwnd, analysisTimerEvent);
\r
5443 analysisTimerEvent = 0;
\r
5446 case DELAYED_TIMER_ID:
\r
5447 KillTimer(hwnd, delayedTimerEvent);
\r
5448 delayedTimerEvent = 0;
\r
5449 delayedTimerCallback();
\r
5454 case WM_USER_Input:
\r
5455 InputEvent(hwnd, message, wParam, lParam);
\r
5458 /* [AS] Also move "attached" child windows */
\r
5459 case WM_WINDOWPOSCHANGING:
\r
5461 if( hwnd == hwndMain && appData.useStickyWindows ) {
\r
5462 LPWINDOWPOS lpwp = (LPWINDOWPOS) lParam;
\r
5464 if( ((lpwp->flags & SWP_NOMOVE) == 0) && ((lpwp->flags & SWP_NOSIZE) != 0) ) {
\r
5465 /* Window is moving */
\r
5468 // GetWindowRect( hwnd, &rcMain ); //[HGM] sticky: in XP this returned new position, not old
\r
5469 rcMain.left = wpMain.x; // replace by these 4 lines to reconstruct old rect
\r
5470 rcMain.right = wpMain.x + wpMain.width;
\r
5471 rcMain.top = wpMain.y;
\r
5472 rcMain.bottom = wpMain.y + wpMain.height;
\r
5474 ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, moveHistoryDialog, &wpMoveHistory );
\r
5475 ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, evalGraphDialog, &wpEvalGraph );
\r
5476 ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, engineOutputDialog, &wpEngineOutput );
\r
5477 ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, gameListDialog, &wpGameList );
\r
5478 ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, hwndConsole, &wpConsole );
\r
5479 wpMain.x = lpwp->x;
\r
5480 wpMain.y = lpwp->y;
\r
5485 /* [AS] Snapping */
\r
5486 case WM_ENTERSIZEMOVE:
\r
5487 if(appData.debugMode) { fprintf(debugFP, "size-move\n"); }
\r
5488 if (hwnd == hwndMain) {
\r
5489 doingSizing = TRUE;
\r
5492 return OnEnterSizeMove( &sd, hwnd, wParam, lParam );
\r
5496 if(appData.debugMode) { fprintf(debugFP, "sizing\n"); }
\r
5497 if (hwnd == hwndMain) {
\r
5498 lastSizing = wParam;
\r
5503 if(appData.debugMode) { fprintf(debugFP, "moving\n"); }
\r
5504 return OnMoving( &sd, hwnd, wParam, lParam );
\r
5506 case WM_EXITSIZEMOVE:
\r
5507 if(appData.debugMode) { fprintf(debugFP, "exit size-move, size = %d\n", squareSize); }
\r
5508 if (hwnd == hwndMain) {
\r
5510 doingSizing = FALSE;
\r
5511 InvalidateRect(hwnd, &boardRect, FALSE);
\r
5512 GetClientRect(hwnd, &client);
\r
5513 ResizeBoard(client.right, client.bottom, lastSizing);
\r
5515 if(appData.debugMode) { fprintf(debugFP, "square size = %d\n", squareSize); }
\r
5517 return OnExitSizeMove( &sd, hwnd, wParam, lParam );
\r
5520 case WM_DESTROY: /* message: window being destroyed */
\r
5521 PostQuitMessage(0);
\r
5525 if (hwnd == hwndMain) {
\r
5530 default: /* Passes it on if unprocessed */
\r
5531 return (DefWindowProc(hwnd, message, wParam, lParam));
\r
5536 /*---------------------------------------------------------------------------*\
\r
5538 * Misc utility routines
\r
5540 \*---------------------------------------------------------------------------*/
\r
5543 * Decent random number generator, at least not as bad as Windows
\r
5544 * standard rand, which returns a value in the range 0 to 0x7fff.
\r
5546 unsigned int randstate;
\r
5551 randstate = randstate * 1664525 + 1013904223;
\r
5552 return (int) randstate & 0x7fffffff;
\r
5556 mysrandom(unsigned int seed)
\r
5563 * returns TRUE if user selects a different color, FALSE otherwise
\r
5567 ChangeColor(HWND hwnd, COLORREF *which)
\r
5569 static BOOL firstTime = TRUE;
\r
5570 static DWORD customColors[16];
\r
5572 COLORREF newcolor;
\r
5577 /* Make initial colors in use available as custom colors */
\r
5578 /* Should we put the compiled-in defaults here instead? */
\r
5580 customColors[i++] = lightSquareColor & 0xffffff;
\r
5581 customColors[i++] = darkSquareColor & 0xffffff;
\r
5582 customColors[i++] = whitePieceColor & 0xffffff;
\r
5583 customColors[i++] = blackPieceColor & 0xffffff;
\r
5584 customColors[i++] = highlightSquareColor & 0xffffff;
\r
5585 customColors[i++] = premoveHighlightColor & 0xffffff;
\r
5587 for (ccl = (ColorClass) 0; ccl < NColorClasses && i < 16; ccl++) {
\r
5588 customColors[i++] = textAttribs[ccl].color;
\r
5590 while (i < 16) customColors[i++] = RGB(255, 255, 255);
\r
5591 firstTime = FALSE;
\r
5594 cc.lStructSize = sizeof(cc);
\r
5595 cc.hwndOwner = hwnd;
\r
5596 cc.hInstance = NULL;
\r
5597 cc.rgbResult = (DWORD) (*which & 0xffffff);
\r
5598 cc.lpCustColors = (LPDWORD) customColors;
\r
5599 cc.Flags = CC_RGBINIT|CC_FULLOPEN;
\r
5601 if (!ChooseColor(&cc)) return FALSE;
\r
5603 newcolor = (COLORREF) (0x2000000 | cc.rgbResult);
\r
5604 if (newcolor == *which) return FALSE;
\r
5605 *which = newcolor;
\r
5609 InitDrawingColors();
\r
5610 InvalidateRect(hwnd, &boardRect, FALSE);
\r
5615 MyLoadSound(MySound *ms)
\r
5621 if (ms->data) free(ms->data);
\r
5624 switch (ms->name[0]) {
\r
5630 /* System sound from Control Panel. Don't preload here. */
\r
5634 if (ms->name[1] == NULLCHAR) {
\r
5635 /* "!" alone = silence */
\r
5638 /* Builtin wave resource. Error if not found. */
\r
5639 HANDLE h = FindResource(hInst, ms->name + 1, "WAVE");
\r
5640 if (h == NULL) break;
\r
5641 ms->data = (void *)LoadResource(hInst, h);
\r
5642 if (h == NULL) break;
\r
5647 /* .wav file. Error if not found. */
\r
5648 f = fopen(ms->name, "rb");
\r
5649 if (f == NULL) break;
\r
5650 if (fstat(fileno(f), &st) < 0) break;
\r
5651 ms->data = malloc(st.st_size);
\r
5652 if (fread(ms->data, st.st_size, 1, f) < 1) break;
\r
5658 char buf[MSG_SIZ];
\r
5659 snprintf(buf, MSG_SIZ, _("Error loading sound %s"), ms->name);
\r
5660 DisplayError(buf, GetLastError());
\r
5666 MyPlaySound(MySound *ms)
\r
5668 BOOLEAN ok = FALSE;
\r
5670 if(mute) return TRUE; // [HGM] mute: suppress all sound play when muted
\r
5671 switch (ms->name[0]) {
\r
5673 if(appData.debugMode) fprintf(debugFP, "silence\n");
\r
5678 /* System sound from Control Panel (deprecated feature).
\r
5679 "$" alone or an unset sound name gets default beep (still in use). */
\r
5680 if (ms->name[1]) {
\r
5681 ok = PlaySound(ms->name + 1, NULL, SND_ALIAS|SND_ASYNC);
\r
5683 if (!ok) ok = MessageBeep(MB_OK);
\r
5686 /* Builtin wave resource, or "!" alone for silence */
\r
5687 if (ms->name[1]) {
\r
5688 if (ms->data == NULL) return FALSE;
\r
5689 ok = PlaySound(ms->data, NULL, SND_MEMORY|SND_ASYNC);
\r
5695 /* .wav file. Error if not found. */
\r
5696 if (ms->data == NULL) return FALSE;
\r
5697 ok = PlaySound(ms->data, NULL, SND_MEMORY|SND_ASYNC);
\r
5700 /* Don't print an error: this can happen innocently if the sound driver
\r
5701 is busy; for instance, if another instance of WinBoard is playing
\r
5702 a sound at about the same time. */
\r
5708 OldOpenFileHook(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
5711 OPENFILENAME *ofn;
\r
5712 static UINT *number; /* gross that this is static */
\r
5714 switch (message) {
\r
5715 case WM_INITDIALOG: /* message: initialize dialog box */
\r
5716 /* Center the dialog over the application window */
\r
5717 ofn = (OPENFILENAME *) lParam;
\r
5718 if (ofn->Flags & OFN_ENABLETEMPLATE) {
\r
5719 number = (UINT *) ofn->lCustData;
\r
5720 SendMessage(GetDlgItem(hDlg, edt2), WM_SETTEXT, 0, (LPARAM) "");
\r
5724 CenterWindow (hDlg, GetWindow (hDlg, GW_OWNER));
\r
5725 Translate(hDlg, 1536);
\r
5726 return FALSE; /* Allow for further processing */
\r
5729 if ((LOWORD(wParam) == IDOK) && (number != NULL)) {
\r
5730 *number = GetDlgItemInt(hDlg, OPT_IndexNumberOld, &ok, FALSE);
\r
5732 return FALSE; /* Allow for further processing */
\r
5738 OpenFileHook(HWND hdlg, UINT uiMsg, WPARAM wParam, LPARAM lParam)
\r
5740 static UINT *number;
\r
5741 OPENFILENAME *ofname;
\r
5744 case WM_INITDIALOG:
\r
5745 Translate(hdlg, DLG_IndexNumber);
\r
5746 ofname = (OPENFILENAME *)lParam;
\r
5747 number = (UINT *)(ofname->lCustData);
\r
5750 ofnot = (OFNOTIFY *)lParam;
\r
5751 if (ofnot->hdr.code == CDN_FILEOK) {
\r
5752 *number = GetDlgItemInt(hdlg, OPT_IndexNumber, NULL, FALSE);
\r
5761 OpenFileDialog(HWND hwnd, char *write, char *defName, char *defExt, // [HGM] diag: type of 'write' now string
\r
5762 char *nameFilt, char *dlgTitle, UINT *number,
\r
5763 char fileTitle[MSG_SIZ], char fileName[MSG_SIZ])
\r
5765 OPENFILENAME openFileName;
\r
5766 char buf1[MSG_SIZ];
\r
5769 if (fileName == NULL) fileName = buf1;
\r
5770 if (defName == NULL) {
\r
5771 safeStrCpy(fileName, "*.", 3 );
\r
5772 strcat(fileName, defExt);
\r
5774 safeStrCpy(fileName, defName, MSG_SIZ );
\r
5776 if (fileTitle) safeStrCpy(fileTitle, "", MSG_SIZ );
\r
5777 if (number) *number = 0;
\r
5779 openFileName.lStructSize = sizeof(OPENFILENAME);
\r
5780 openFileName.hwndOwner = hwnd;
\r
5781 openFileName.hInstance = (HANDLE) hInst;
\r
5782 openFileName.lpstrFilter = nameFilt;
\r
5783 openFileName.lpstrCustomFilter = (LPSTR) NULL;
\r
5784 openFileName.nMaxCustFilter = 0L;
\r
5785 openFileName.nFilterIndex = 1L;
\r
5786 openFileName.lpstrFile = fileName;
\r
5787 openFileName.nMaxFile = MSG_SIZ;
\r
5788 openFileName.lpstrFileTitle = fileTitle;
\r
5789 openFileName.nMaxFileTitle = fileTitle ? MSG_SIZ : 0;
\r
5790 openFileName.lpstrInitialDir = NULL;
\r
5791 openFileName.lpstrTitle = dlgTitle;
\r
5792 openFileName.Flags = OFN_PATHMUSTEXIST | OFN_HIDEREADONLY
\r
5793 | (write[0] != 'r' ? 0 : OFN_FILEMUSTEXIST)
\r
5794 | (number ? OFN_ENABLETEMPLATE | OFN_ENABLEHOOK: 0)
\r
5795 | (oldDialog ? 0 : OFN_EXPLORER);
\r
5796 openFileName.nFileOffset = 0;
\r
5797 openFileName.nFileExtension = 0;
\r
5798 openFileName.lpstrDefExt = defExt;
\r
5799 openFileName.lCustData = (LONG) number;
\r
5800 openFileName.lpfnHook = oldDialog ?
\r
5801 (LPOFNHOOKPROC) OldOpenFileHook : (LPOFNHOOKPROC) OpenFileHook;
\r
5802 openFileName.lpTemplateName = (LPSTR)(oldDialog ? 1536 : DLG_IndexNumber);
\r
5804 if (write[0] != 'r' ? GetSaveFileName(&openFileName) :
\r
5805 GetOpenFileName(&openFileName)) {
\r
5806 /* open the file */
\r
5807 f = fopen(openFileName.lpstrFile, write);
\r
5809 MessageBox(hwnd, _("File open failed"), NULL,
\r
5810 MB_OK|MB_ICONEXCLAMATION);
\r
5814 int err = CommDlgExtendedError();
\r
5815 if (err != 0) DisplayError(_("Internal error in file dialog box"), err);
\r
5824 MenuPopup(HWND hwnd, POINT pt, HMENU hmenu, UINT def)
\r
5826 HMENU hmenuTrackPopup; /* floating pop-up menu */
\r
5829 * Get the first pop-up menu in the menu template. This is the
\r
5830 * menu that TrackPopupMenu displays.
\r
5832 hmenuTrackPopup = GetSubMenu(hmenu, 0);
\r
5833 TranslateOneMenu(10, hmenuTrackPopup);
\r
5835 SetMenuDefaultItem(hmenuTrackPopup, def, FALSE);
\r
5838 * TrackPopup uses screen coordinates, so convert the
\r
5839 * coordinates of the mouse click to screen coordinates.
\r
5841 ClientToScreen(hwnd, (LPPOINT) &pt);
\r
5843 /* Draw and track the floating pop-up menu. */
\r
5844 TrackPopupMenu(hmenuTrackPopup, TPM_CENTERALIGN | TPM_RIGHTBUTTON,
\r
5845 pt.x, pt.y, 0, hwnd, NULL);
\r
5847 /* Destroy the menu.*/
\r
5848 DestroyMenu(hmenu);
\r
5853 int sizeX, sizeY, newSizeX, newSizeY;
\r
5855 } ResizeEditPlusButtonsClosure;
\r
5858 ResizeEditPlusButtonsCallback(HWND hChild, LPARAM lparam)
\r
5860 ResizeEditPlusButtonsClosure *cl = (ResizeEditPlusButtonsClosure *)lparam;
\r
5864 if (hChild == cl->hText) return TRUE;
\r
5865 GetWindowRect(hChild, &rect); /* gives screen coords */
\r
5866 pt.x = rect.left + (cl->newSizeX - cl->sizeX)/2;
\r
5867 pt.y = rect.top + cl->newSizeY - cl->sizeY;
\r
5868 ScreenToClient(cl->hDlg, &pt);
\r
5869 cl->hdwp = DeferWindowPos(cl->hdwp, hChild, NULL,
\r
5870 pt.x, pt.y, rect.right - rect.left, rect.bottom - rect.top, SWP_NOZORDER);
\r
5874 /* Resize a dialog that has a (rich) edit field filling most of
\r
5875 the top, with a row of buttons below */
\r
5877 ResizeEditPlusButtons(HWND hDlg, HWND hText, int sizeX, int sizeY, int newSizeX, int newSizeY)
\r
5880 int newTextHeight, newTextWidth;
\r
5881 ResizeEditPlusButtonsClosure cl;
\r
5883 /*if (IsIconic(hDlg)) return;*/
\r
5884 if (newSizeX == sizeX && newSizeY == sizeY) return;
\r
5886 cl.hdwp = BeginDeferWindowPos(8);
\r
5888 GetWindowRect(hText, &rectText); /* gives screen coords */
\r
5889 newTextWidth = rectText.right - rectText.left + newSizeX - sizeX;
\r
5890 newTextHeight = rectText.bottom - rectText.top + newSizeY - sizeY;
\r
5891 if (newTextHeight < 0) {
\r
5892 newSizeY += -newTextHeight;
\r
5893 newTextHeight = 0;
\r
5895 cl.hdwp = DeferWindowPos(cl.hdwp, hText, NULL, 0, 0,
\r
5896 newTextWidth, newTextHeight, SWP_NOZORDER|SWP_NOMOVE);
\r
5902 cl.newSizeX = newSizeX;
\r
5903 cl.newSizeY = newSizeY;
\r
5904 EnumChildWindows(hDlg, ResizeEditPlusButtonsCallback, (LPARAM)&cl);
\r
5906 EndDeferWindowPos(cl.hdwp);
\r
5909 BOOL CenterWindowEx(HWND hwndChild, HWND hwndParent, int mode)
\r
5911 RECT rChild, rParent;
\r
5912 int wChild, hChild, wParent, hParent;
\r
5913 int wScreen, hScreen, xNew, yNew;
\r
5916 /* Get the Height and Width of the child window */
\r
5917 GetWindowRect (hwndChild, &rChild);
\r
5918 wChild = rChild.right - rChild.left;
\r
5919 hChild = rChild.bottom - rChild.top;
\r
5921 /* Get the Height and Width of the parent window */
\r
5922 GetWindowRect (hwndParent, &rParent);
\r
5923 wParent = rParent.right - rParent.left;
\r
5924 hParent = rParent.bottom - rParent.top;
\r
5926 /* Get the display limits */
\r
5927 hdc = GetDC (hwndChild);
\r
5928 wScreen = GetDeviceCaps (hdc, HORZRES);
\r
5929 hScreen = GetDeviceCaps (hdc, VERTRES);
\r
5930 ReleaseDC(hwndChild, hdc);
\r
5932 /* Calculate new X position, then adjust for screen */
\r
5933 xNew = rParent.left + ((wParent - wChild) /2);
\r
5936 } else if ((xNew+wChild) > wScreen) {
\r
5937 xNew = wScreen - wChild;
\r
5940 /* Calculate new Y position, then adjust for screen */
\r
5942 yNew = rParent.top + ((hParent - hChild) /2);
\r
5945 yNew = rParent.top + GetSystemMetrics( SM_CYCAPTION ) * 2 / 3;
\r
5950 } else if ((yNew+hChild) > hScreen) {
\r
5951 yNew = hScreen - hChild;
\r
5954 /* Set it, and return */
\r
5955 return SetWindowPos (hwndChild, NULL,
\r
5956 xNew, yNew, 0, 0, SWP_NOSIZE | SWP_NOZORDER);
\r
5959 /* Center one window over another */
\r
5960 BOOL CenterWindow (HWND hwndChild, HWND hwndParent)
\r
5962 return CenterWindowEx( hwndChild, hwndParent, 0 );
\r
5965 /*---------------------------------------------------------------------------*\
\r
5967 * Startup Dialog functions
\r
5969 \*---------------------------------------------------------------------------*/
\r
5971 InitComboStrings(HANDLE hwndCombo, char **cd)
\r
5973 SendMessage(hwndCombo, CB_RESETCONTENT, 0, 0);
\r
5975 while (*cd != NULL) {
\r
5976 SendMessage(hwndCombo, CB_ADDSTRING, 0, (LPARAM) T_(*cd));
\r
5982 InitComboStringsFromOption(HANDLE hwndCombo, char *str)
\r
5984 char buf1[MAX_ARG_LEN];
\r
5987 if (str[0] == '@') {
\r
5988 FILE* f = fopen(str + 1, "r");
\r
5990 DisplayFatalError(str + 1, errno, 2);
\r
5993 len = fread(buf1, 1, sizeof(buf1)-1, f);
\r
5995 buf1[len] = NULLCHAR;
\r
5999 SendMessage(hwndCombo, CB_RESETCONTENT, 0, 0);
\r
6002 char buf[MSG_SIZ];
\r
6003 char *end = strchr(str, '\n');
\r
6004 if (end == NULL) return;
\r
6005 memcpy(buf, str, end - str);
\r
6006 buf[end - str] = NULLCHAR;
\r
6007 SendMessage(hwndCombo, CB_ADDSTRING, 0, (LPARAM) buf);
\r
6013 SetStartupDialogEnables(HWND hDlg)
\r
6015 EnableWindow(GetDlgItem(hDlg, OPT_ChessEngineName),
\r
6016 IsDlgButtonChecked(hDlg, OPT_ChessEngine) ||
\r
6017 (appData.zippyPlay && IsDlgButtonChecked(hDlg, OPT_ChessServer)));
\r
6018 EnableWindow(GetDlgItem(hDlg, OPT_SecondChessEngineName),
\r
6019 IsDlgButtonChecked(hDlg, OPT_ChessEngine));
\r
6020 EnableWindow(GetDlgItem(hDlg, OPT_ChessServerName),
\r
6021 IsDlgButtonChecked(hDlg, OPT_ChessServer));
\r
6022 EnableWindow(GetDlgItem(hDlg, OPT_AdditionalOptions),
\r
6023 IsDlgButtonChecked(hDlg, OPT_AnyAdditional));
\r
6024 EnableWindow(GetDlgItem(hDlg, IDOK),
\r
6025 IsDlgButtonChecked(hDlg, OPT_ChessEngine) ||
\r
6026 IsDlgButtonChecked(hDlg, OPT_ChessServer) ||
\r
6027 IsDlgButtonChecked(hDlg, OPT_View));
\r
6031 QuoteForFilename(char *filename)
\r
6033 int dquote, space;
\r
6034 dquote = strchr(filename, '"') != NULL;
\r
6035 space = strchr(filename, ' ') != NULL;
\r
6036 if (dquote || space) {
\r
6048 InitEngineBox(HWND hDlg, HWND hwndCombo, char* nthcp, char* nthd, char* nthdir, char *nthnames)
\r
6050 char buf[MSG_SIZ];
\r
6053 InitComboStringsFromOption(hwndCombo, nthnames);
\r
6054 q = QuoteForFilename(nthcp);
\r
6055 snprintf(buf, MSG_SIZ, "%s%s%s", q, nthcp, q);
\r
6056 if (*nthdir != NULLCHAR) {
\r
6057 q = QuoteForFilename(nthdir);
\r
6058 snprintf(buf + strlen(buf), MSG_SIZ, " /%s=%s%s%s", nthd, q, nthdir, q);
\r
6060 if (*nthcp == NULLCHAR) {
\r
6061 SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) 0, (LPARAM) 0);
\r
6062 } else if (SendMessage(hwndCombo, CB_SELECTSTRING, (WPARAM) -1, (LPARAM) buf) == CB_ERR) {
\r
6063 SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) -1, (LPARAM) 0);
\r
6064 SendMessage(hwndCombo, WM_SETTEXT, (WPARAM) 0, (LPARAM) buf);
\r
6069 StartupDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
6071 char buf[MSG_SIZ];
\r
6075 switch (message) {
\r
6076 case WM_INITDIALOG:
\r
6077 /* Center the dialog */
\r
6078 CenterWindow (hDlg, GetDesktopWindow());
\r
6079 Translate(hDlg, DLG_Startup);
\r
6080 /* Initialize the dialog items */
\r
6081 InitEngineBox(hDlg, GetDlgItem(hDlg, OPT_ChessEngineName),
\r
6082 appData.firstChessProgram, "fd", appData.firstDirectory,
\r
6083 firstChessProgramNames);
\r
6084 InitEngineBox(hDlg, GetDlgItem(hDlg, OPT_SecondChessEngineName),
\r
6085 appData.secondChessProgram, singleList ? "fd" : "sd", appData.secondDirectory,
\r
6086 singleList ? firstChessProgramNames : secondChessProgramNames); //[HGM] single: use first list in second combo
\r
6087 hwndCombo = GetDlgItem(hDlg, OPT_ChessServerName);
\r
6088 InitComboStringsFromOption(hwndCombo, icsNames);
\r
6089 snprintf(buf, MSG_SIZ, "%s /icsport=%s", appData.icsHost, appData.icsPort);
\r
6090 if (*appData.icsHelper != NULLCHAR) {
\r
6091 char *q = QuoteForFilename(appData.icsHelper);
\r
6092 sprintf(buf + strlen(buf), " /icshelper=%s%s%s", q, appData.icsHelper, q);
\r
6094 if (*appData.icsHost == NULLCHAR) {
\r
6095 SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) 0, (LPARAM) 0);
\r
6096 /*SendMessage(hwndCombo, CB_SHOWDROPDOWN, (WPARAM) TRUE, (LPARAM) 0); !!too soon */
\r
6097 } else if (SendMessage(hwndCombo, CB_SELECTSTRING, (WPARAM) -1, (LPARAM) buf) == CB_ERR) {
\r
6098 SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) -1, (LPARAM) 0);
\r
6099 SendMessage(hwndCombo, WM_SETTEXT, (WPARAM) 0, (LPARAM) buf);
\r
6102 if (appData.icsActive) {
\r
6103 CheckDlgButton(hDlg, OPT_ChessServer, BST_CHECKED);
\r
6105 else if (appData.noChessProgram) {
\r
6106 CheckDlgButton(hDlg, OPT_View, BST_CHECKED);
\r
6109 CheckDlgButton(hDlg, OPT_ChessEngine, BST_CHECKED);
\r
6112 SetStartupDialogEnables(hDlg);
\r
6116 switch (LOWORD(wParam)) {
\r
6118 if (IsDlgButtonChecked(hDlg, OPT_ChessEngine)) {
\r
6119 safeStrCpy(buf, "/fcp=", sizeof(buf)/sizeof(buf[0]) );
\r
6120 GetDlgItemText(hDlg, OPT_ChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));
\r
6122 ParseArgs(StringGet, &p);
\r
6123 safeStrCpy(buf, singleList ? "/fcp=" : "/scp=", sizeof(buf)/sizeof(buf[0]) );
\r
6124 GetDlgItemText(hDlg, OPT_SecondChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));
\r
6126 SwapEngines(singleList); // temporarily swap first and second, to load a second 'first', ...
\r
6127 ParseArgs(StringGet, &p);
\r
6128 SwapEngines(singleList); // ... and then make it 'second'
\r
6129 appData.noChessProgram = FALSE;
\r
6130 appData.icsActive = FALSE;
\r
6131 } else if (IsDlgButtonChecked(hDlg, OPT_ChessServer)) {
\r
6132 safeStrCpy(buf, "/ics /icshost=", sizeof(buf)/sizeof(buf[0]) );
\r
6133 GetDlgItemText(hDlg, OPT_ChessServerName, buf + strlen(buf), sizeof(buf) - strlen(buf));
\r
6135 ParseArgs(StringGet, &p);
\r
6136 if (appData.zippyPlay) {
\r
6137 safeStrCpy(buf, "/fcp=", sizeof(buf)/sizeof(buf[0]) );
\r
6138 GetDlgItemText(hDlg, OPT_ChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));
\r
6140 ParseArgs(StringGet, &p);
\r
6142 } else if (IsDlgButtonChecked(hDlg, OPT_View)) {
\r
6143 appData.noChessProgram = TRUE;
\r
6144 appData.icsActive = FALSE;
\r
6146 MessageBox(hDlg, _("Choose an option, or cancel to exit"),
\r
6147 _("Option Error"), MB_OK|MB_ICONEXCLAMATION);
\r
6150 if (IsDlgButtonChecked(hDlg, OPT_AnyAdditional)) {
\r
6151 GetDlgItemText(hDlg, OPT_AdditionalOptions, buf, sizeof(buf));
\r
6153 ParseArgs(StringGet, &p);
\r
6155 EndDialog(hDlg, TRUE);
\r
6162 case IDM_HELPCONTENTS:
\r
6163 if (!WinHelp (hDlg, "winboard.hlp", HELP_KEY,(DWORD)(LPSTR)"CONTENTS")) {
\r
6164 MessageBox (GetFocus(),
\r
6165 _("Unable to activate help"),
\r
6166 szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);
\r
6171 SetStartupDialogEnables(hDlg);
\r
6179 /*---------------------------------------------------------------------------*\
\r
6181 * About box dialog functions
\r
6183 \*---------------------------------------------------------------------------*/
\r
6185 /* Process messages for "About" dialog box */
\r
6187 About(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
6189 switch (message) {
\r
6190 case WM_INITDIALOG: /* message: initialize dialog box */
\r
6191 /* Center the dialog over the application window */
\r
6192 CenterWindow (hDlg, GetWindow (hDlg, GW_OWNER));
\r
6193 SetDlgItemText(hDlg, ABOUTBOX_Version, programVersion);
\r
6194 Translate(hDlg, ABOUTBOX);
\r
6198 case WM_COMMAND: /* message: received a command */
\r
6199 if (LOWORD(wParam) == IDOK /* "OK" box selected? */
\r
6200 || LOWORD(wParam) == IDCANCEL) { /* System menu close command? */
\r
6201 EndDialog(hDlg, TRUE); /* Exit the dialog */
\r
6209 /*---------------------------------------------------------------------------*\
\r
6211 * Comment Dialog functions
\r
6213 \*---------------------------------------------------------------------------*/
\r
6216 CommentDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
6218 static HANDLE hwndText = NULL;
\r
6219 int len, newSizeX, newSizeY, flags;
\r
6220 static int sizeX, sizeY;
\r
6225 switch (message) {
\r
6226 case WM_INITDIALOG: /* message: initialize dialog box */
\r
6227 /* Initialize the dialog items */
\r
6228 Translate(hDlg, DLG_EditComment);
\r
6229 hwndText = GetDlgItem(hDlg, OPT_CommentText);
\r
6230 SetDlgItemText(hDlg, OPT_CommentText, commentText);
\r
6231 EnableWindow(GetDlgItem(hDlg, OPT_CancelComment), editComment);
\r
6232 EnableWindow(GetDlgItem(hDlg, OPT_ClearComment), editComment);
\r
6233 EnableWindow(GetDlgItem(hDlg, OPT_EditComment), !editComment);
\r
6234 SendMessage(hwndText, EM_SETREADONLY, !editComment, 0);
\r
6235 SetWindowText(hDlg, commentTitle);
\r
6236 if (editComment) {
\r
6237 SetFocus(hwndText);
\r
6239 SetFocus(GetDlgItem(hDlg, IDOK));
\r
6241 SendMessage(GetDlgItem(hDlg, OPT_CommentText),
\r
6242 WM_SETFONT, (WPARAM)font[boardSize][COMMENT_FONT]->hf,
\r
6243 MAKELPARAM(FALSE, 0));
\r
6244 /* Size and position the dialog */
\r
6245 if (!commentDialog) {
\r
6246 commentDialog = hDlg;
\r
6247 flags = SWP_NOZORDER;
\r
6248 GetClientRect(hDlg, &rect);
\r
6249 sizeX = rect.right;
\r
6250 sizeY = rect.bottom;
\r
6251 if (wpComment.x != CW_USEDEFAULT && wpComment.y != CW_USEDEFAULT &&
\r
6252 wpComment.width != CW_USEDEFAULT && wpComment.height != CW_USEDEFAULT) {
\r
6253 WINDOWPLACEMENT wp;
\r
6254 EnsureOnScreen(&wpComment.x, &wpComment.y, 0, 0);
\r
6255 wp.length = sizeof(WINDOWPLACEMENT);
\r
6257 wp.showCmd = SW_SHOW;
\r
6258 wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;
\r
6259 wp.rcNormalPosition.left = wpComment.x;
\r
6260 wp.rcNormalPosition.right = wpComment.x + wpComment.width;
\r
6261 wp.rcNormalPosition.top = wpComment.y;
\r
6262 wp.rcNormalPosition.bottom = wpComment.y + wpComment.height;
\r
6263 SetWindowPlacement(hDlg, &wp);
\r
6265 GetClientRect(hDlg, &rect);
\r
6266 newSizeX = rect.right;
\r
6267 newSizeY = rect.bottom;
\r
6268 ResizeEditPlusButtons(hDlg, hwndText, sizeX, sizeY,
\r
6269 newSizeX, newSizeY);
\r
6274 SendDlgItemMessage( hDlg, OPT_CommentText, EM_SETEVENTMASK, 0, ENM_MOUSEEVENTS | ENM_KEYEVENTS );
\r
6277 case WM_COMMAND: /* message: received a command */
\r
6278 switch (LOWORD(wParam)) {
\r
6280 if (editComment) {
\r
6282 /* Read changed options from the dialog box */
\r
6283 hwndText = GetDlgItem(hDlg, OPT_CommentText);
\r
6284 len = GetWindowTextLength(hwndText);
\r
6285 str = (char *) malloc(len + 1);
\r
6286 GetWindowText(hwndText, str, len + 1);
\r
6295 ReplaceComment(commentIndex, str);
\r
6302 case OPT_CancelComment:
\r
6306 case OPT_ClearComment:
\r
6307 SetDlgItemText(hDlg, OPT_CommentText, "");
\r
6310 case OPT_EditComment:
\r
6311 EditCommentEvent();
\r
6319 case WM_NOTIFY: // [HGM] vari: cloned from whistory.c
\r
6320 if( wParam == OPT_CommentText ) {
\r
6321 MSGFILTER * lpMF = (MSGFILTER *) lParam;
\r
6323 if( lpMF->msg == WM_RBUTTONDOWN && (lpMF->wParam & (MK_CONTROL | MK_SHIFT)) == 0 ||
\r
6324 lpMF->msg == WM_CHAR && lpMF->wParam == '\022' ) {
\r
6328 pt.x = LOWORD( lpMF->lParam );
\r
6329 pt.y = HIWORD( lpMF->lParam );
\r
6331 if(lpMF->msg == WM_CHAR) {
\r
6333 SendDlgItemMessage( hDlg, OPT_CommentText, EM_EXGETSEL, 0, (LPARAM) &sel );
\r
6334 index = sel.cpMin;
\r
6336 index = SendDlgItemMessage( hDlg, OPT_CommentText, EM_CHARFROMPOS, 0, (LPARAM) &pt );
\r
6338 hwndText = GetDlgItem(hDlg, OPT_CommentText); // cloned from above
\r
6339 len = GetWindowTextLength(hwndText);
\r
6340 str = (char *) malloc(len + 1);
\r
6341 GetWindowText(hwndText, str, len + 1);
\r
6342 ReplaceComment(commentIndex, str);
\r
6343 if(commentIndex != currentMove) ToNrEvent(commentIndex);
\r
6344 LoadVariation( index, str ); // [HGM] also does the actual moving to it, now
\r
6347 /* Zap the message for good: apparently, returning non-zero is not enough */
\r
6348 lpMF->msg = WM_USER;
\r
6356 newSizeX = LOWORD(lParam);
\r
6357 newSizeY = HIWORD(lParam);
\r
6358 ResizeEditPlusButtons(hDlg, hwndText, sizeX, sizeY, newSizeX, newSizeY);
\r
6363 case WM_GETMINMAXINFO:
\r
6364 /* Prevent resizing window too small */
\r
6365 mmi = (MINMAXINFO *) lParam;
\r
6366 mmi->ptMinTrackSize.x = 100;
\r
6367 mmi->ptMinTrackSize.y = 100;
\r
6374 EitherCommentPopUp(int index, char *title, char *str, BOOLEAN edit)
\r
6379 CheckMenuItem(GetMenu(hwndMain), IDM_Comment, edit ? MF_CHECKED : MF_UNCHECKED);
\r
6381 if (str == NULL) str = "";
\r
6382 p = (char *) malloc(2 * strlen(str) + 2);
\r
6385 if (*str == '\n') *q++ = '\r';
\r
6389 if (commentText != NULL) free(commentText);
\r
6391 commentIndex = index;
\r
6392 commentTitle = title;
\r
6394 editComment = edit;
\r
6396 if (commentDialog) {
\r
6397 SendMessage(commentDialog, WM_INITDIALOG, 0, 0);
\r
6398 if (!commentUp) ShowWindow(commentDialog, SW_SHOW);
\r
6400 lpProc = MakeProcInstance((FARPROC)CommentDialog, hInst);
\r
6401 CreateDialog(hInst, MAKEINTRESOURCE(DLG_EditComment),
\r
6402 hwndMain, (DLGPROC)lpProc);
\r
6403 FreeProcInstance(lpProc);
\r
6409 /*---------------------------------------------------------------------------*\
\r
6411 * Type-in move dialog functions
\r
6413 \*---------------------------------------------------------------------------*/
\r
6416 TypeInMoveDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
6418 char move[MSG_SIZ];
\r
6421 switch (message) {
\r
6422 case WM_INITDIALOG:
\r
6423 move[0] = (char) lParam;
\r
6424 move[1] = NULLCHAR;
\r
6425 CenterWindowEx(hDlg, GetWindow(hDlg, GW_OWNER), 1 );
\r
6426 Translate(hDlg, DLG_TypeInMove);
\r
6427 hInput = GetDlgItem(hDlg, OPT_Move);
\r
6428 SetWindowText(hInput, move);
\r
6430 SendMessage(hInput, EM_SETSEL, (WPARAM)9999, (LPARAM)9999);
\r
6434 switch (LOWORD(wParam)) {
\r
6437 shiftKey = GetKeyState(VK_SHIFT) < 0; // [HGM] remember last shift status
\r
6438 GetDlgItemText(hDlg, OPT_Move, move, sizeof(move));
6439 TypeInDoneEvent(move);
\r
6440 EndDialog(hDlg, TRUE);
\r
6443 EndDialog(hDlg, FALSE);
\r
6454 PopUpMoveDialog(char firstchar)
\r
6458 lpProc = MakeProcInstance((FARPROC)TypeInMoveDialog, hInst);
\r
6459 DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_TypeInMove),
\r
6460 hwndMain, (DLGPROC)lpProc, (LPARAM)firstchar);
\r
6461 FreeProcInstance(lpProc);
\r
6464 /*---------------------------------------------------------------------------*\
\r
6466 * Type-in name dialog functions
\r
6468 \*---------------------------------------------------------------------------*/
\r
6471 TypeInNameDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
6473 char move[MSG_SIZ];
\r
6476 switch (message) {
\r
6477 case WM_INITDIALOG:
\r
6478 move[0] = (char) lParam;
\r
6479 move[1] = NULLCHAR;
\r
6480 CenterWindowEx(hDlg, GetWindow(hDlg, GW_OWNER), 1 );
\r
6481 Translate(hDlg, DLG_TypeInName);
\r
6482 hInput = GetDlgItem(hDlg, OPT_Name);
\r
6483 SetWindowText(hInput, move);
\r
6485 SendMessage(hInput, EM_SETSEL, (WPARAM)9999, (LPARAM)9999);
\r
6489 switch (LOWORD(wParam)) {
\r
6491 GetDlgItemText(hDlg, OPT_Name, move, sizeof(move));
\r
6492 appData.userName = strdup(move);
\r
6495 if(gameMode == MachinePlaysWhite || gameMode == MachinePlaysBlack) {
\r
6496 snprintf(move, MSG_SIZ, "%s vs. %s", gameInfo.white, gameInfo.black);
\r
6497 DisplayTitle(move);
\r
6501 EndDialog(hDlg, TRUE);
\r
6504 EndDialog(hDlg, FALSE);
\r
6515 PopUpNameDialog(char firstchar)
\r
6519 lpProc = MakeProcInstance((FARPROC)TypeInNameDialog, hInst);
\r
6520 DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_TypeInName),
\r
6521 hwndMain, (DLGPROC)lpProc, (LPARAM)firstchar);
\r
6522 FreeProcInstance(lpProc);
\r
6525 /*---------------------------------------------------------------------------*\
\r
6529 \*---------------------------------------------------------------------------*/
\r
6531 /* Nonmodal error box */
\r
6532 LRESULT CALLBACK ErrorDialog(HWND hDlg, UINT message,
\r
6533 WPARAM wParam, LPARAM lParam);
\r
6536 ErrorPopUp(char *title, char *content)
\r
6540 BOOLEAN modal = hwndMain == NULL;
\r
6558 strncpy(errorTitle, title, sizeof(errorTitle));
\r
6559 errorTitle[sizeof(errorTitle) - 1] = '\0';
\r
6562 MessageBox(NULL, errorMessage, errorTitle, MB_OK|MB_ICONEXCLAMATION);
\r
6564 lpProc = MakeProcInstance((FARPROC)ErrorDialog, hInst);
\r
6565 CreateDialog(hInst, MAKEINTRESOURCE(DLG_Error),
\r
6566 hwndMain, (DLGPROC)lpProc);
\r
6567 FreeProcInstance(lpProc);
\r
6574 if (!appData.popupMoveErrors && moveErrorMessageUp) DisplayMessage("", "");
\r
6575 if (errorDialog == NULL) return;
\r
6576 DestroyWindow(errorDialog);
\r
6577 errorDialog = NULL;
\r
6578 if(errorExitStatus) ExitEvent(errorExitStatus);
\r
6582 ErrorDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
6587 switch (message) {
\r
6588 case WM_INITDIALOG:
\r
6589 GetWindowRect(hDlg, &rChild);
\r
6592 SetWindowPos(hDlg, NULL, rChild.left,
\r
6593 rChild.top + boardRect.top - (rChild.bottom - rChild.top),
\r
6594 0, 0, SWP_NOZORDER|SWP_NOSIZE);
\r
6598 [AS] It seems that the above code wants to move the dialog up in the "caption
\r
6599 area" of the main window, but it uses the dialog height as an hard-coded constant,
\r
6600 and it doesn't work when you resize the dialog.
\r
6601 For now, just give it a default position.
\r
6603 SetWindowPos(hDlg, NULL, boardRect.left+8, boardRect.top+8, 0, 0, SWP_NOZORDER|SWP_NOSIZE);
\r
6604 Translate(hDlg, DLG_Error);
\r
6606 errorDialog = hDlg;
\r
6607 SetWindowText(hDlg, errorTitle);
\r
6608 hwndText = GetDlgItem(hDlg, OPT_ErrorText);
\r
6609 SetDlgItemText(hDlg, OPT_ErrorText, errorMessage);
\r
6613 switch (LOWORD(wParam)) {
\r
6616 if (errorDialog == hDlg) errorDialog = NULL;
\r
6617 DestroyWindow(hDlg);
\r
6629 HWND gothicDialog = NULL;
\r
6632 GothicDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
6636 int height = GetSystemMetrics(SM_CYCAPTION)+GetSystemMetrics(SM_CYFRAME);
\r
6638 switch (message) {
\r
6639 case WM_INITDIALOG:
\r
6640 GetWindowRect(hDlg, &rChild);
\r
6642 SetWindowPos(hDlg, NULL, wpMain.x, wpMain.y-height, wpMain.width, height,
\r
6646 [AS] It seems that the above code wants to move the dialog up in the "caption
\r
6647 area" of the main window, but it uses the dialog height as an hard-coded constant,
\r
6648 and it doesn't work when you resize the dialog.
\r
6649 For now, just give it a default position.
\r
6651 gothicDialog = hDlg;
\r
6652 SetWindowText(hDlg, errorTitle);
\r
6653 hwndText = GetDlgItem(hDlg, OPT_ErrorText);
\r
6654 SetDlgItemText(hDlg, OPT_ErrorText, errorMessage);
\r
6658 switch (LOWORD(wParam)) {
\r
6661 if (errorDialog == hDlg) errorDialog = NULL;
\r
6662 DestroyWindow(hDlg);
\r
6674 GothicPopUp(char *title, VariantClass variant)
\r
6677 static char *lastTitle;
\r
6679 strncpy(errorTitle, title, sizeof(errorTitle));
\r
6680 errorTitle[sizeof(errorTitle) - 1] = '\0';
\r
6682 if(lastTitle != title && gothicDialog != NULL) {
\r
6683 DestroyWindow(gothicDialog);
\r
6684 gothicDialog = NULL;
\r
6686 if(variant != VariantNormal && gothicDialog == NULL) {
\r
6687 title = lastTitle;
\r
6688 lpProc = MakeProcInstance((FARPROC)GothicDialog, hInst);
\r
6689 CreateDialog(hInst, MAKEINTRESOURCE(DLG_Error),
\r
6690 hwndMain, (DLGPROC)lpProc);
\r
6691 FreeProcInstance(lpProc);
\r
6696 /*---------------------------------------------------------------------------*\
\r
6698 * Ics Interaction console functions
\r
6700 \*---------------------------------------------------------------------------*/
\r
6702 #define HISTORY_SIZE 64
\r
6703 static char *history[HISTORY_SIZE];
\r
6704 int histIn = 0, histP = 0;
\r
6707 SaveInHistory(char *cmd)
\r
6709 if (history[histIn] != NULL) {
\r
6710 free(history[histIn]);
\r
6711 history[histIn] = NULL;
\r
6713 if (*cmd == NULLCHAR) return;
\r
6714 history[histIn] = StrSave(cmd);
\r
6715 histIn = (histIn + 1) % HISTORY_SIZE;
\r
6716 if (history[histIn] != NULL) {
\r
6717 free(history[histIn]);
\r
6718 history[histIn] = NULL;
\r
6724 PrevInHistory(char *cmd)
\r
6727 if (histP == histIn) {
\r
6728 if (history[histIn] != NULL) free(history[histIn]);
\r
6729 history[histIn] = StrSave(cmd);
\r
6731 newhp = (histP - 1 + HISTORY_SIZE) % HISTORY_SIZE;
\r
6732 if (newhp == histIn || history[newhp] == NULL) return NULL;
\r
6734 return history[histP];
\r
6740 if (histP == histIn) return NULL;
\r
6741 histP = (histP + 1) % HISTORY_SIZE;
\r
6742 return history[histP];
\r
6746 LoadIcsTextMenu(IcsTextMenuEntry *e)
\r
6750 hmenu = LoadMenu(hInst, "TextMenu");
\r
6751 h = GetSubMenu(hmenu, 0);
\r
6753 if (strcmp(e->item, "-") == 0) {
\r
6754 AppendMenu(h, MF_SEPARATOR, 0, 0);
\r
6755 } else { // [HGM] re-written a bit to use only one AppendMenu call for both cases (| or no |)
\r
6756 int flags = MF_STRING, j = 0;
\r
6757 if (e->item[0] == '|') {
\r
6758 flags |= MF_MENUBARBREAK;
\r
6761 if(!strcmp(e->command, "none")) flags |= MF_GRAYED; // [HGM] chatclick: provide inactive dummy
\r
6762 AppendMenu(h, flags, IDM_CommandX + i, e->item + j);
\r
6770 WNDPROC consoleTextWindowProc;
\r
6773 CommandX(HWND hwnd, char *command, BOOLEAN getname, BOOLEAN immediate)
\r
6775 char buf[MSG_SIZ], name[MSG_SIZ];
\r
6776 HWND hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
6780 SetWindowText(hInput, command);
\r
6782 SendMessage(hInput, WM_CHAR, '\r', 0);
\r
6784 sel.cpMin = 999999;
\r
6785 sel.cpMax = 999999;
\r
6786 SendMessage(hInput, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
6791 SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);
\r
6792 if (sel.cpMin == sel.cpMax) {
\r
6793 /* Expand to surrounding word */
\r
6796 tr.chrg.cpMax = sel.cpMin;
\r
6797 tr.chrg.cpMin = --sel.cpMin;
\r
6798 if (sel.cpMin < 0) break;
\r
6799 tr.lpstrText = name;
\r
6800 SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr);
\r
6801 } while (isalpha(name[0]) || isdigit(name[0]) || name[0] == '-');
\r
6805 tr.chrg.cpMin = sel.cpMax;
\r
6806 tr.chrg.cpMax = ++sel.cpMax;
\r
6807 tr.lpstrText = name;
\r
6808 if (SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr) < 1) break;
\r
6809 } while (isalpha(name[0]) || isdigit(name[0]) || name[0] == '-');
\r
6812 if (sel.cpMax == sel.cpMin || sel.cpMax - sel.cpMin > MSG_SIZ/2) {
\r
6813 MessageBeep(MB_ICONEXCLAMATION);
\r
6817 tr.lpstrText = name;
\r
6818 SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr);
\r
6820 if (sel.cpMax - sel.cpMin > MSG_SIZ/2) {
\r
6821 MessageBeep(MB_ICONEXCLAMATION);
\r
6824 SendMessage(hwnd, EM_GETSELTEXT, 0, (LPARAM) name);
\r
6827 if(strstr(command, "%s")) snprintf(buf, MSG_SIZ, command, name); else
\r
6828 snprintf(buf, MSG_SIZ, "%s %s", command, name);
\r
6829 SetWindowText(hInput, buf);
\r
6830 SendMessage(hInput, WM_CHAR, '\r', 0);
\r
6832 if(!strcmp(command, "chat")) { ChatPopUp(name); return; }
\r
6833 snprintf(buf, MSG_SIZ, "%s %s ", command, name); /* trailing space */
\r
6834 SetWindowText(hInput, buf);
\r
6835 sel.cpMin = 999999;
\r
6836 sel.cpMax = 999999;
\r
6837 SendMessage(hInput, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
6843 ConsoleTextSubclass(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
\r
6848 switch (message) {
\r
6850 if (!(GetKeyState(VK_CONTROL) & ~1)) break;
\r
6851 if(wParam=='R') return 0;
\r
6854 SendMessage(hwnd, EM_LINESCROLL, 0, -999999);
\r
6857 sel.cpMin = 999999;
\r
6858 sel.cpMax = 999999;
\r
6859 SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
6860 SendMessage(hwnd, EM_SCROLLCARET, 0, 0);
\r
6865 if(wParam != '\022') {
\r
6866 if (wParam == '\t') {
\r
6867 if (GetKeyState(VK_SHIFT) < 0) {
\r
6869 if (IsIconic(hwndMain)) ShowWindow(hwndMain, SW_RESTORE);
\r
6870 if (buttonDesc[0].hwnd) {
\r
6871 SetFocus(buttonDesc[0].hwnd);
\r
6873 SetFocus(hwndMain);
\r
6877 SetFocus(GetDlgItem(hwndConsole, OPT_ConsoleInput));
\r
6880 hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
6881 JAWS_DELETE( SetFocus(hInput); )
\r
6882 SendMessage(hInput, message, wParam, lParam);
\r
6885 } // [HGM] navigate: for Ctrl+R, flow into next case (moved up here) to summon up menu
\r
6887 case WM_RBUTTONDOWN:
\r
6888 if (!(GetKeyState(VK_SHIFT) & ~1)) {
\r
6889 /* Move selection here if it was empty */
\r
6891 pt.x = LOWORD(lParam);
\r
6892 pt.y = HIWORD(lParam);
\r
6893 SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);
\r
6894 if (sel.cpMin == sel.cpMax) {
\r
6895 if(lParam != -1) sel.cpMin = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&pt); /*doc is wrong*/
\r
6896 sel.cpMax = sel.cpMin;
\r
6897 SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
6899 SendMessage(hwnd, EM_HIDESELECTION, FALSE, FALSE);
\r
6900 { // [HGM] chatclick: code moved here from WM_RBUTTONUP case, to have menu appear on down-click
\r
6902 HMENU hmenu = LoadIcsTextMenu(icsTextMenuEntry);
\r
6903 SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);
\r
6904 if (sel.cpMin == sel.cpMax) {
\r
6905 EnableMenuItem(hmenu, IDM_Copy, MF_BYCOMMAND|MF_GRAYED);
\r
6906 EnableMenuItem(hmenu, IDM_QuickPaste, MF_BYCOMMAND|MF_GRAYED);
\r
6908 if (!IsClipboardFormatAvailable(CF_TEXT)) {
\r
6909 EnableMenuItem(hmenu, IDM_Paste, MF_BYCOMMAND|MF_GRAYED);
\r
6911 pt.x = LOWORD(lParam)-30; // [HGM] chatclick: make menu pop up with pointer above upper-right item
\r
6912 pt.y = HIWORD(lParam)-10; // make it appear as if mouse moved there, so it will be selected on up-click
\r
6913 PostMessage(hwnd, WM_MOUSEMOVE, wParam, lParam+5);
\r
6914 MenuPopup(hwnd, pt, hmenu, -1);
\r
6918 case WM_RBUTTONUP:
\r
6919 if (GetKeyState(VK_SHIFT) & ~1) {
\r
6920 SendDlgItemMessage(hwndConsole, OPT_ConsoleText,
\r
6921 WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);
\r
6925 hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
6927 return SendMessage(hInput, message, wParam, lParam);
\r
6928 case WM_MBUTTONDOWN:
\r
6929 return SendMessage(hwnd, WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);
\r
6931 switch (LOWORD(wParam)) {
\r
6932 case IDM_QuickPaste:
\r
6934 SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);
\r
6935 if (sel.cpMin == sel.cpMax) {
\r
6936 MessageBeep(MB_ICONEXCLAMATION);
\r
6939 SendMessage(hwnd, WM_COPY, 0, 0);
\r
6940 hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
6941 SendMessage(hInput, WM_PASTE, 0, 0);
\r
6946 SendMessage(hwnd, WM_CUT, 0, 0);
\r
6949 SendMessage(hwnd, WM_PASTE, 0, 0);
\r
6952 SendMessage(hwnd, WM_COPY, 0, 0);
\r
6956 int i = LOWORD(wParam) - IDM_CommandX;
\r
6957 if (i >= 0 && i < ICS_TEXT_MENU_SIZE &&
\r
6958 icsTextMenuEntry[i].command != NULL) {
\r
6959 CommandX(hwnd, icsTextMenuEntry[i].command,
\r
6960 icsTextMenuEntry[i].getname,
\r
6961 icsTextMenuEntry[i].immediate);
\r
6969 return (*consoleTextWindowProc)(hwnd, message, wParam, lParam);
\r
6972 WNDPROC consoleInputWindowProc;
\r
6975 ConsoleInputSubclass(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
\r
6977 char buf[MSG_SIZ];
\r
6979 static BOOL sendNextChar = FALSE;
\r
6980 static BOOL quoteNextChar = FALSE;
\r
6981 InputSource *is = consoleInputSource;
\r
6985 switch (message) {
\r
6987 if (!appData.localLineEditing || sendNextChar) {
\r
6988 is->buf[0] = (CHAR) wParam;
\r
6990 SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);
\r
6991 sendNextChar = FALSE;
\r
6994 if (quoteNextChar) {
\r
6995 buf[0] = (char) wParam;
\r
6996 buf[1] = NULLCHAR;
\r
6997 SendMessage(hwnd, EM_REPLACESEL, TRUE, (LPARAM) buf);
\r
6998 quoteNextChar = FALSE;
\r
7002 case '\r': /* Enter key */
\r
7003 is->count = GetWindowText(hwnd, is->buf, INPUT_SOURCE_BUF_SIZE-1);
\r
7004 if (consoleEcho) SaveInHistory(is->buf);
\r
7005 is->buf[is->count++] = '\n';
\r
7006 is->buf[is->count] = NULLCHAR;
\r
7007 SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);
\r
7008 if (consoleEcho) {
\r
7009 ConsoleOutput(is->buf, is->count, TRUE);
\r
7010 } else if (appData.localLineEditing) {
\r
7011 ConsoleOutput("\n", 1, TRUE);
\r
7014 case '\033': /* Escape key */
\r
7015 SetWindowText(hwnd, "");
\r
7016 cf.cbSize = sizeof(CHARFORMAT);
\r
7017 cf.dwMask = CFM_COLOR|CFM_BOLD|CFM_ITALIC|CFM_UNDERLINE|CFM_STRIKEOUT;
\r
7018 if (consoleEcho) {
\r
7019 cf.crTextColor = textAttribs[ColorNormal].color;
\r
7021 cf.crTextColor = COLOR_ECHOOFF;
\r
7023 cf.dwEffects = textAttribs[ColorNormal].effects;
\r
7024 SendMessage(hwnd, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf);
\r
7026 case '\t': /* Tab key */
\r
7027 if (GetKeyState(VK_SHIFT) < 0) {
\r
7029 SetFocus(GetDlgItem(hwndConsole, OPT_ConsoleText));
\r
7032 if (IsIconic(hwndMain)) ShowWindow(hwndMain, SW_RESTORE);
\r
7033 if (buttonDesc[0].hwnd) {
\r
7034 SetFocus(buttonDesc[0].hwnd);
\r
7036 SetFocus(hwndMain);
\r
7040 case '\023': /* Ctrl+S */
\r
7041 sendNextChar = TRUE;
\r
7043 case '\021': /* Ctrl+Q */
\r
7044 quoteNextChar = TRUE;
\r
7054 GetWindowText(hwnd, buf, MSG_SIZ);
\r
7055 p = PrevInHistory(buf);
\r
7057 SetWindowText(hwnd, p);
\r
7058 sel.cpMin = 999999;
\r
7059 sel.cpMax = 999999;
\r
7060 SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
7065 p = NextInHistory();
\r
7067 SetWindowText(hwnd, p);
\r
7068 sel.cpMin = 999999;
\r
7069 sel.cpMax = 999999;
\r
7070 SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
7076 if (!(GetKeyState(VK_CONTROL) & ~1)) break;
\r
7080 SendDlgItemMessage(hwndConsole, OPT_ConsoleText, message, wParam, lParam);
\r
7084 case WM_MBUTTONDOWN:
\r
7085 SendDlgItemMessage(hwndConsole, OPT_ConsoleText,
\r
7086 WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);
\r
7088 case WM_RBUTTONUP:
\r
7089 if (GetKeyState(VK_SHIFT) & ~1) {
\r
7090 SendDlgItemMessage(hwndConsole, OPT_ConsoleText,
\r
7091 WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);
\r
7095 hmenu = LoadMenu(hInst, "InputMenu");
\r
7096 SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);
\r
7097 if (sel.cpMin == sel.cpMax) {
\r
7098 EnableMenuItem(hmenu, IDM_Copy, MF_BYCOMMAND|MF_GRAYED);
\r
7099 EnableMenuItem(hmenu, IDM_Cut, MF_BYCOMMAND|MF_GRAYED);
\r
7101 if (!IsClipboardFormatAvailable(CF_TEXT)) {
\r
7102 EnableMenuItem(hmenu, IDM_Paste, MF_BYCOMMAND|MF_GRAYED);
\r
7104 pt.x = LOWORD(lParam);
\r
7105 pt.y = HIWORD(lParam);
\r
7106 MenuPopup(hwnd, pt, hmenu, -1);
\r
7110 switch (LOWORD(wParam)) {
\r
7112 SendMessage(hwnd, EM_UNDO, 0, 0);
\r
7114 case IDM_SelectAll:
\r
7116 sel.cpMax = -1; /*999999?*/
\r
7117 SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
7120 SendMessage(hwnd, WM_CUT, 0, 0);
\r
7123 SendMessage(hwnd, WM_PASTE, 0, 0);
\r
7126 SendMessage(hwnd, WM_COPY, 0, 0);
\r
7131 return (*consoleInputWindowProc)(hwnd, message, wParam, lParam);
\r
7134 #define CO_MAX 100000
\r
7135 #define CO_TRIM 1000
\r
7138 ConsoleWndProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
7140 static SnapData sd;
\r
7141 HWND hText, hInput;
\r
7143 static int sizeX, sizeY;
\r
7144 int newSizeX, newSizeY;
\r
7148 hText = GetDlgItem(hDlg, OPT_ConsoleText);
\r
7149 hInput = GetDlgItem(hDlg, OPT_ConsoleInput);
\r
7151 switch (message) {
\r
7153 if (((NMHDR*)lParam)->code == EN_LINK)
\r
7155 ENLINK *pLink = (ENLINK*)lParam;
\r
7156 if (pLink->msg == WM_LBUTTONUP)
\r
7160 tr.chrg = pLink->chrg;
\r
7161 tr.lpstrText = malloc(1+tr.chrg.cpMax-tr.chrg.cpMin);
\r
7162 SendMessage(hText, EM_GETTEXTRANGE, 0, (LPARAM)&tr);
\r
7163 ShellExecute(NULL, "open", tr.lpstrText, NULL, NULL, SW_SHOW);
\r
7164 free(tr.lpstrText);
\r
7168 case WM_INITDIALOG: /* message: initialize dialog box */
\r
7169 hwndConsole = hDlg;
\r
7171 consoleTextWindowProc = (WNDPROC)
\r
7172 SetWindowLongPtr(hText, GWLP_WNDPROC, (LONG_PTR) ConsoleTextSubclass);
\r
7173 SendMessage(hText, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);
\r
7174 consoleInputWindowProc = (WNDPROC)
\r
7175 SetWindowLongPtr(hInput, GWLP_WNDPROC, (LONG_PTR) ConsoleInputSubclass);
\r
7176 SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);
\r
7177 Colorize(ColorNormal, TRUE);
\r
7178 SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &consoleCF);
\r
7179 ChangedConsoleFont();
\r
7180 GetClientRect(hDlg, &rect);
\r
7181 sizeX = rect.right;
\r
7182 sizeY = rect.bottom;
\r
7183 if (wpConsole.x != CW_USEDEFAULT && wpConsole.y != CW_USEDEFAULT &&
\r
7184 wpConsole.width != CW_USEDEFAULT && wpConsole.height != CW_USEDEFAULT) {
\r
7185 WINDOWPLACEMENT wp;
\r
7186 EnsureOnScreen(&wpConsole.x, &wpConsole.y, 0, 0);
\r
7187 wp.length = sizeof(WINDOWPLACEMENT);
\r
7189 wp.showCmd = SW_SHOW;
\r
7190 wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;
\r
7191 wp.rcNormalPosition.left = wpConsole.x;
\r
7192 wp.rcNormalPosition.right = wpConsole.x + wpConsole.width;
\r
7193 wp.rcNormalPosition.top = wpConsole.y;
\r
7194 wp.rcNormalPosition.bottom = wpConsole.y + wpConsole.height;
\r
7195 SetWindowPlacement(hDlg, &wp);
\r
7198 // [HGM] Chessknight's change 2004-07-13
\r
7199 else { /* Determine Defaults */
\r
7200 WINDOWPLACEMENT wp;
\r
7201 wpConsole.x = wpMain.width + 1;
\r
7202 wpConsole.y = wpMain.y;
\r
7203 wpConsole.width = screenWidth - wpMain.width;
\r
7204 wpConsole.height = wpMain.height;
\r
7205 EnsureOnScreen(&wpConsole.x, &wpConsole.y, 0, 0);
\r
7206 wp.length = sizeof(WINDOWPLACEMENT);
\r
7208 wp.showCmd = SW_SHOW;
\r
7209 wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;
\r
7210 wp.rcNormalPosition.left = wpConsole.x;
\r
7211 wp.rcNormalPosition.right = wpConsole.x + wpConsole.width;
\r
7212 wp.rcNormalPosition.top = wpConsole.y;
\r
7213 wp.rcNormalPosition.bottom = wpConsole.y + wpConsole.height;
\r
7214 SetWindowPlacement(hDlg, &wp);
\r
7217 // Allow hText to highlight URLs and send notifications on them
\r
7218 wMask = (WORD) SendMessage(hText, EM_GETEVENTMASK, 0, 0L);
\r
7219 SendMessage(hText, EM_SETEVENTMASK, 0, wMask | ENM_LINK);
\r
7220 SendMessage(hText, EM_AUTOURLDETECT, TRUE, 0L);
\r
7221 SetWindowLongPtr(hText, GWLP_USERDATA, 79); // initialize the text window's width
\r
7235 if (IsIconic(hDlg)) break;
\r
7236 newSizeX = LOWORD(lParam);
\r
7237 newSizeY = HIWORD(lParam);
\r
7238 if (sizeX != newSizeX || sizeY != newSizeY) {
\r
7239 RECT rectText, rectInput;
\r
7241 int newTextHeight, newTextWidth;
\r
7242 GetWindowRect(hText, &rectText);
\r
7243 newTextWidth = rectText.right - rectText.left + newSizeX - sizeX;
\r
7244 newTextHeight = rectText.bottom - rectText.top + newSizeY - sizeY;
\r
7245 if (newTextHeight < 0) {
\r
7246 newSizeY += -newTextHeight;
\r
7247 newTextHeight = 0;
\r
7249 SetWindowPos(hText, NULL, 0, 0,
\r
7250 newTextWidth, newTextHeight, SWP_NOZORDER|SWP_NOMOVE);
\r
7251 GetWindowRect(hInput, &rectInput); /* gives screen coords */
\r
7252 pt.x = rectInput.left;
\r
7253 pt.y = rectInput.top + newSizeY - sizeY;
\r
7254 ScreenToClient(hDlg, &pt);
\r
7255 SetWindowPos(hInput, NULL,
\r
7256 pt.x, pt.y, /* needs client coords */
\r
7257 rectInput.right - rectInput.left + newSizeX - sizeX,
\r
7258 rectInput.bottom - rectInput.top, SWP_NOZORDER);
\r
7264 case WM_GETMINMAXINFO:
\r
7265 /* Prevent resizing window too small */
\r
7266 mmi = (MINMAXINFO *) lParam;
\r
7267 mmi->ptMinTrackSize.x = 100;
\r
7268 mmi->ptMinTrackSize.y = 100;
\r
7271 /* [AS] Snapping */
\r
7272 case WM_ENTERSIZEMOVE:
\r
7273 return OnEnterSizeMove( &sd, hDlg, wParam, lParam );
\r
7276 return OnSizing( &sd, hDlg, wParam, lParam );
\r
7279 return OnMoving( &sd, hDlg, wParam, lParam );
\r
7281 case WM_EXITSIZEMOVE:
\r
7282 UpdateICSWidth(hText);
\r
7283 return OnExitSizeMove( &sd, hDlg, wParam, lParam );
\r
7286 return DefWindowProc(hDlg, message, wParam, lParam);
\r
7294 if (hwndConsole) return;
\r
7295 hCons = CreateDialog(hInst, szConsoleName, 0, NULL);
\r
7296 SendMessage(hCons, WM_INITDIALOG, 0, 0);
\r
7301 ConsoleOutput(char* data, int length, int forceVisible)
\r
7306 char buf[CO_MAX+1];
\r
7309 static int delayLF = 0;
\r
7310 CHARRANGE savesel, sel;
\r
7312 if (hwndConsole == NULL || length > CO_MAX-100 || length == 0) return;
\r
7320 while (length--) {
\r
7328 } else if (*p == '\007') {
\r
7329 MyPlaySound(&sounds[(int)SoundBell]);
\r
7336 hText = GetDlgItem(hwndConsole, OPT_ConsoleText);
\r
7337 SendMessage(hText, EM_HIDESELECTION, TRUE, FALSE);
\r
7338 /* Save current selection */
\r
7339 SendMessage(hText, EM_EXGETSEL, 0, (LPARAM)&savesel);
\r
7340 exlen = GetWindowTextLength(hText);
\r
7341 /* Find out whether current end of text is visible */
\r
7342 SendMessage(hText, EM_GETRECT, 0, (LPARAM) &rect);
\r
7343 SendMessage(hText, EM_POSFROMCHAR, (WPARAM) &pEnd, exlen);
\r
7344 /* Trim existing text if it's too long */
\r
7345 if (exlen + (q - buf) > CO_MAX) {
\r
7346 trim = (CO_TRIM > (q - buf)) ? CO_TRIM : (q - buf);
\r
7349 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
7350 SendMessage(hText, EM_REPLACESEL, 0, (LPARAM)"");
\r
7352 savesel.cpMin -= trim;
\r
7353 savesel.cpMax -= trim;
\r
7354 if (exlen < 0) exlen = 0;
\r
7355 if (savesel.cpMin < 0) savesel.cpMin = 0;
\r
7356 if (savesel.cpMax < savesel.cpMin) savesel.cpMax = savesel.cpMin;
\r
7358 /* Append the new text */
\r
7359 sel.cpMin = exlen;
\r
7360 sel.cpMax = exlen;
\r
7361 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
7362 SendMessage(hText, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&consoleCF);
\r
7363 SendMessage(hText, EM_REPLACESEL, 0, (LPARAM) buf);
\r
7364 if (forceVisible || exlen == 0 ||
\r
7365 (rect.left <= pEnd.x && pEnd.x < rect.right &&
\r
7366 rect.top <= pEnd.y && pEnd.y < rect.bottom)) {
\r
7367 /* Scroll to make new end of text visible if old end of text
\r
7368 was visible or new text is an echo of user typein */
\r
7369 sel.cpMin = 9999999;
\r
7370 sel.cpMax = 9999999;
\r
7371 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
7372 SendMessage(hText, EM_HIDESELECTION, FALSE, FALSE);
\r
7373 SendMessage(hText, EM_SCROLLCARET, 0, 0);
\r
7374 SendMessage(hText, EM_HIDESELECTION, TRUE, FALSE);
\r
7376 if (savesel.cpMax == exlen || forceVisible) {
\r
7377 /* Move insert point to new end of text if it was at the old
\r
7378 end of text or if the new text is an echo of user typein */
\r
7379 sel.cpMin = 9999999;
\r
7380 sel.cpMax = 9999999;
\r
7381 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
7383 /* Restore previous selection */
\r
7384 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&savesel);
\r
7386 SendMessage(hText, EM_HIDESELECTION, FALSE, FALSE);
\r
7393 DisplayHoldingsCount(HDC hdc, int x, int y, int rightAlign, int copyNumber)
\r
7397 COLORREF oldFg, oldBg;
\r
7402 snprintf(buf, sizeof(buf)/sizeof(buf[0]),"%d", copyNumber); else buf[0] = 0;
\r
7404 oldFg = SetTextColor(hdc, RGB(255, 255, 255)); /* white */
\r
7405 oldBg = SetBkColor(hdc, RGB(0, 0, 0)); /* black */
\r
7406 oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);
\r
7409 rect.right = x + squareSize;
\r
7411 rect.bottom = y + squareSize;
\r
7414 ExtTextOut(hdc, x + MESSAGE_LINE_LEFTMARGIN
\r
7415 + (rightAlign ? (squareSize*2)/3 : 0),
\r
7416 y, ETO_CLIPPED|ETO_OPAQUE,
\r
7417 &rect, str, strlen(str), NULL);
\r
7419 (void) SetTextColor(hdc, oldFg);
\r
7420 (void) SetBkColor(hdc, oldBg);
\r
7421 (void) SelectObject(hdc, oldFont);
\r
7425 DisplayAClock(HDC hdc, int timeRemaining, int highlight,
\r
7426 RECT *rect, char *color, char *flagFell)
\r
7430 COLORREF oldFg, oldBg;
\r
7433 if (appData.clockMode) {
\r
7435 snprintf(buf, sizeof(buf)/sizeof(buf[0]), "%c %s %s", color[0], TimeString(timeRemaining), flagFell);
\r
7437 snprintf(buf, sizeof(buf)/sizeof(buf[0]), "%s:%c%s %s", color, (logoHeight>0 ? 0 : ' '), TimeString(timeRemaining), flagFell);
\r
7444 oldFg = SetTextColor(hdc, RGB(255, 255, 255)); /* white */
\r
7445 oldBg = SetBkColor(hdc, RGB(0, 0, 0)); /* black */
\r
7447 oldFg = SetTextColor(hdc, RGB(0, 0, 0)); /* black */
\r
7448 oldBg = SetBkColor(hdc, RGB(255, 255, 255)); /* white */
\r
7450 oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);
\r
7454 ExtTextOut(hdc, rect->left + MESSAGE_LINE_LEFTMARGIN,
\r
7455 rect->top, ETO_CLIPPED|ETO_OPAQUE,
\r
7456 rect, str, strlen(str), NULL);
\r
7457 if(logoHeight > 0 && appData.clockMode) {
\r
7459 str += strlen(color)+2;
\r
7460 r.top = rect->top + logoHeight/2;
\r
7461 r.left = rect->left;
\r
7462 r.right = rect->right;
\r
7463 r.bottom = rect->bottom;
\r
7464 ExtTextOut(hdc, rect->left + MESSAGE_LINE_LEFTMARGIN,
\r
7465 r.top, ETO_CLIPPED|ETO_OPAQUE,
\r
7466 &r, str, strlen(str), NULL);
\r
7468 (void) SetTextColor(hdc, oldFg);
\r
7469 (void) SetBkColor(hdc, oldBg);
\r
7470 (void) SelectObject(hdc, oldFont);
\r
7475 DoReadFile(HANDLE hFile, char *buf, int count, DWORD *outCount,
\r
7481 if( count <= 0 ) {
\r
7482 if (appData.debugMode) {
\r
7483 fprintf( debugFP, "DoReadFile: trying to read past end of buffer, overflow = %d\n", count );
\r
7486 return ERROR_INVALID_USER_BUFFER;
\r
7489 ResetEvent(ovl->hEvent);
\r
7490 ovl->Offset = ovl->OffsetHigh = 0;
\r
7491 ok = ReadFile(hFile, buf, count, outCount, ovl);
\r
7495 err = GetLastError();
\r
7496 if (err == ERROR_IO_PENDING) {
\r
7497 ok = GetOverlappedResult(hFile, ovl, outCount, TRUE);
\r
7501 err = GetLastError();
\r
7508 DoWriteFile(HANDLE hFile, char *buf, int count, DWORD *outCount,
\r
7513 ResetEvent(ovl->hEvent);
\r
7514 ovl->Offset = ovl->OffsetHigh = 0;
\r
7515 ok = WriteFile(hFile, buf, count, outCount, ovl);
\r
7519 err = GetLastError();
\r
7520 if (err == ERROR_IO_PENDING) {
\r
7521 ok = GetOverlappedResult(hFile, ovl, outCount, TRUE);
\r
7525 err = GetLastError();
\r
7531 /* [AS] If input is line by line and a line exceed the buffer size, force an error */
\r
7532 void CheckForInputBufferFull( InputSource * is )
\r
7534 if( is->lineByLine && (is->next - is->buf) >= INPUT_SOURCE_BUF_SIZE ) {
\r
7535 /* Look for end of line */
\r
7536 char * p = is->buf;
\r
7538 while( p < is->next && *p != '\n' ) {
\r
7542 if( p >= is->next ) {
\r
7543 if (appData.debugMode) {
\r
7544 fprintf( debugFP, "Input line exceeded buffer size (source id=%lu)\n", is->id );
\r
7547 is->error = ERROR_BROKEN_PIPE; /* [AS] Just any non-successful code! */
\r
7548 is->count = (DWORD) -1;
\r
7549 is->next = is->buf;
\r
7555 InputThread(LPVOID arg)
\r
7560 is = (InputSource *) arg;
\r
7561 ovl.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
\r
7562 ovl.Internal = ovl.InternalHigh = ovl.Offset = ovl.OffsetHigh = 0;
\r
7563 while (is->hThread != NULL) {
\r
7564 is->error = DoReadFile(is->hFile, is->next,
\r
7565 INPUT_SOURCE_BUF_SIZE - (is->next - is->buf),
\r
7566 &is->count, &ovl);
\r
7567 if (is->error == NO_ERROR) {
\r
7568 is->next += is->count;
\r
7570 if (is->error == ERROR_BROKEN_PIPE) {
\r
7571 /* Correct for MS brain damage. EOF reading a pipe is not an error. */
\r
7574 is->count = (DWORD) -1;
\r
7575 /* [AS] The (is->count <= 0) check below is not useful for unsigned values! */
\r
7580 CheckForInputBufferFull( is );
\r
7582 SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);
\r
7584 if( is->count == ((DWORD) -1) ) break; /* [AS] */
\r
7586 if (is->count <= 0) break; /* Quit on EOF or error */
\r
7589 CloseHandle(ovl.hEvent);
\r
7590 CloseHandle(is->hFile);
\r
7592 if (appData.debugMode) {
\r
7593 fprintf( debugFP, "Input thread terminated (id=%lu, error=%d, count=%ld)\n", is->id, is->error, is->count );
\r
7600 /* Windows 95 beta 2 won't let you do overlapped i/o on a console or pipe */
\r
7602 NonOvlInputThread(LPVOID arg)
\r
7609 is = (InputSource *) arg;
\r
7610 while (is->hThread != NULL) {
\r
7611 is->error = ReadFile(is->hFile, is->next,
\r
7612 INPUT_SOURCE_BUF_SIZE - (is->next - is->buf),
\r
7613 &is->count, NULL) ? NO_ERROR : GetLastError();
\r
7614 if (is->error == NO_ERROR) {
\r
7615 /* Change CRLF to LF */
\r
7616 if (is->next > is->buf) {
\r
7618 i = is->count + 1;
\r
7626 if (prev == '\r' && *p == '\n') {
\r
7638 if (is->error == ERROR_BROKEN_PIPE) {
\r
7639 /* Correct for MS brain damage. EOF reading a pipe is not an error. */
\r
7642 is->count = (DWORD) -1;
\r
7646 CheckForInputBufferFull( is );
\r
7648 SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);
\r
7650 if( is->count == ((DWORD) -1) ) break; /* [AS] */
\r
7652 if (is->count < 0) break; /* Quit on error */
\r
7654 CloseHandle(is->hFile);
\r
7659 SocketInputThread(LPVOID arg)
\r
7663 is = (InputSource *) arg;
\r
7664 while (is->hThread != NULL) {
\r
7665 is->count = recv(is->sock, is->buf, INPUT_SOURCE_BUF_SIZE, 0);
\r
7666 if ((int)is->count == SOCKET_ERROR) {
\r
7667 is->count = (DWORD) -1;
\r
7668 is->error = WSAGetLastError();
\r
7670 is->error = NO_ERROR;
\r
7671 is->next += is->count;
\r
7672 if (is->count == 0 && is->second == is) {
\r
7673 /* End of file on stderr; quit with no message */
\r
7677 SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);
\r
7679 if( is->count == ((DWORD) -1) ) break; /* [AS] */
\r
7681 if (is->count <= 0) break; /* Quit on EOF or error */
\r
7687 InputEvent(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
\r
7691 is = (InputSource *) lParam;
\r
7692 if (is->lineByLine) {
\r
7693 /* Feed in lines one by one */
\r
7694 char *p = is->buf;
\r
7696 while (q < is->next) {
\r
7697 if (*q++ == '\n') {
\r
7698 (is->func)(is, is->closure, p, q - p, NO_ERROR);
\r
7703 /* Move any partial line to the start of the buffer */
\r
7705 while (p < is->next) {
\r
7710 if (is->error != NO_ERROR || is->count == 0) {
\r
7711 /* Notify backend of the error. Note: If there was a partial
\r
7712 line at the end, it is not flushed through. */
\r
7713 (is->func)(is, is->closure, is->buf, is->count, is->error);
\r
7716 /* Feed in the whole chunk of input at once */
\r
7717 (is->func)(is, is->closure, is->buf, is->count, is->error);
\r
7718 is->next = is->buf;
\r
7722 /*---------------------------------------------------------------------------*\
\r
7724 * Menu enables. Used when setting various modes.
\r
7726 \*---------------------------------------------------------------------------*/
\r
7734 GreyRevert(Boolean grey)
\r
7735 { // [HGM] vari: for retracting variations in local mode
\r
7736 HMENU hmenu = GetMenu(hwndMain);
\r
7737 EnableMenuItem(hmenu, IDM_Revert, MF_BYCOMMAND|(grey ? MF_GRAYED : MF_ENABLED));
\r
7738 EnableMenuItem(hmenu, IDM_Annotate, MF_BYCOMMAND|(grey ? MF_GRAYED : MF_ENABLED));
\r
7742 SetMenuEnables(HMENU hmenu, Enables *enab)
\r
7744 while (enab->item > 0) {
\r
7745 (void) EnableMenuItem(hmenu, enab->item, enab->flags);
\r
7750 Enables gnuEnables[] = {
\r
7751 { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },
\r
7752 { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },
\r
7753 { IDM_IcsClient, MF_BYCOMMAND|MF_GRAYED },
\r
7754 { IDM_Accept, MF_BYCOMMAND|MF_GRAYED },
\r
7755 { IDM_Decline, MF_BYCOMMAND|MF_GRAYED },
\r
7756 { IDM_Rematch, MF_BYCOMMAND|MF_GRAYED },
\r
7757 { IDM_Adjourn, MF_BYCOMMAND|MF_GRAYED },
\r
7758 { IDM_StopExamining, MF_BYCOMMAND|MF_GRAYED },
\r
7759 { IDM_StopObserving, MF_BYCOMMAND|MF_GRAYED },
\r
7760 { IDM_Upload, MF_BYCOMMAND|MF_GRAYED },
\r
7761 { IDM_Revert, MF_BYCOMMAND|MF_GRAYED },
\r
7762 { IDM_Annotate, MF_BYCOMMAND|MF_GRAYED },
\r
7763 { IDM_NewChat, MF_BYCOMMAND|MF_GRAYED },
\r
7765 // Needed to switch from ncp to GNU mode on Engine Load
\r
7766 { ACTION_POS, MF_BYPOSITION|MF_ENABLED },
\r
7767 { IDM_MachineWhite, MF_BYCOMMAND|MF_ENABLED },
\r
7768 { IDM_MachineBlack, MF_BYCOMMAND|MF_ENABLED },
\r
7769 { IDM_TwoMachines, MF_BYCOMMAND|MF_ENABLED },
\r
7770 { IDM_Match, MF_BYCOMMAND|MF_ENABLED },
\r
7771 { IDM_AnalysisMode, MF_BYCOMMAND|MF_ENABLED },
\r
7772 { IDM_AnalyzeFile, MF_BYCOMMAND|MF_ENABLED },
\r
7773 { IDM_Engine1Options, MF_BYCOMMAND|MF_ENABLED },
\r
7774 { IDM_Engine2Options, MF_BYCOMMAND|MF_ENABLED },
\r
7775 { IDM_TimeControl, MF_BYCOMMAND|MF_ENABLED },
\r
7776 { IDM_RetractMove, MF_BYCOMMAND|MF_ENABLED },
\r
7777 { IDM_MoveNow, MF_BYCOMMAND|MF_ENABLED },
\r
7778 { IDM_Hint, MF_BYCOMMAND|MF_ENABLED },
\r
7779 { IDM_Book, MF_BYCOMMAND|MF_ENABLED },
\r
7783 Enables icsEnables[] = {
\r
7784 { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },
\r
7785 { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },
\r
7786 { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },
\r
7787 { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },
\r
7788 { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },
\r
7789 { IDM_Match, MF_BYCOMMAND|MF_GRAYED },
\r
7790 { IDM_MachineBoth, MF_BYCOMMAND|MF_GRAYED },
\r
7791 { IDM_AnalysisMode, MF_BYCOMMAND|MF_ENABLED },
\r
7792 { IDM_AnalyzeFile, MF_BYCOMMAND|MF_GRAYED },
\r
7793 { IDM_TimeControl, MF_BYCOMMAND|MF_GRAYED },
\r
7794 { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },
\r
7795 { IDM_Hint, MF_BYCOMMAND|MF_GRAYED },
\r
7796 { IDM_Book, MF_BYCOMMAND|MF_GRAYED },
\r
7797 { IDM_EditProgs2, MF_BYCOMMAND|MF_GRAYED },
\r
7798 { IDM_IcsOptions, MF_BYCOMMAND|MF_ENABLED },
\r
7799 { IDM_Engine1Options, MF_BYCOMMAND|MF_GRAYED },
\r
7800 { IDM_Engine2Options, MF_BYCOMMAND|MF_GRAYED },
\r
7801 { IDM_Annotate, MF_BYCOMMAND|MF_GRAYED },
\r
7802 { IDM_Tourney, MF_BYCOMMAND|MF_GRAYED },
\r
7807 Enables zippyEnables[] = {
\r
7808 { IDM_MoveNow, MF_BYCOMMAND|MF_ENABLED },
\r
7809 { IDM_Hint, MF_BYCOMMAND|MF_ENABLED },
\r
7810 { IDM_Book, MF_BYCOMMAND|MF_ENABLED },
\r
7811 { IDM_Engine1Options, MF_BYCOMMAND|MF_ENABLED },
\r
7816 Enables ncpEnables[] = {
\r
7817 { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },
\r
7818 { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },
\r
7819 { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },
\r
7820 { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },
\r
7821 { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },
\r
7822 { IDM_Match, MF_BYCOMMAND|MF_GRAYED },
\r
7823 { IDM_AnalysisMode, MF_BYCOMMAND|MF_GRAYED },
\r
7824 { IDM_AnalyzeFile, MF_BYCOMMAND|MF_GRAYED },
\r
7825 { IDM_IcsClient, MF_BYCOMMAND|MF_GRAYED },
\r
7826 { ACTION_POS, MF_BYPOSITION|MF_GRAYED },
\r
7827 { IDM_Revert, MF_BYCOMMAND|MF_GRAYED },
\r
7828 { IDM_Annotate, MF_BYCOMMAND|MF_GRAYED },
\r
7829 { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },
\r
7830 { IDM_RetractMove, MF_BYCOMMAND|MF_GRAYED },
\r
7831 { IDM_TimeControl, MF_BYCOMMAND|MF_GRAYED },
\r
7832 { IDM_Hint, MF_BYCOMMAND|MF_GRAYED },
\r
7833 { IDM_Book, MF_BYCOMMAND|MF_GRAYED },
\r
7834 { IDM_MachineBoth, MF_BYCOMMAND|MF_GRAYED },
\r
7835 { IDM_NewChat, MF_BYCOMMAND|MF_GRAYED },
\r
7836 { IDM_Engine1Options, MF_BYCOMMAND|MF_GRAYED },
\r
7837 { IDM_Engine2Options, MF_BYCOMMAND|MF_GRAYED },
\r
7838 { IDM_Sounds, MF_BYCOMMAND|MF_GRAYED },
\r
7842 Enables trainingOnEnables[] = {
\r
7843 { IDM_EditComment, MF_BYCOMMAND|MF_GRAYED },
\r
7844 { IDM_Comment, MF_BYCOMMAND|MF_GRAYED },
\r
7845 { IDM_Pause, MF_BYCOMMAND|MF_GRAYED },
\r
7846 { IDM_Forward, MF_BYCOMMAND|MF_GRAYED },
\r
7847 { IDM_Backward, MF_BYCOMMAND|MF_GRAYED },
\r
7848 { IDM_ToEnd, MF_BYCOMMAND|MF_GRAYED },
\r
7849 { IDM_ToStart, MF_BYCOMMAND|MF_GRAYED },
\r
7850 { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },
\r
7851 { IDM_TruncateGame, MF_BYCOMMAND|MF_GRAYED },
\r
7855 Enables trainingOffEnables[] = {
\r
7856 { IDM_EditComment, MF_BYCOMMAND|MF_ENABLED },
\r
7857 { IDM_Comment, MF_BYCOMMAND|MF_ENABLED },
\r
7858 { IDM_Pause, MF_BYCOMMAND|MF_ENABLED },
\r
7859 { IDM_Forward, MF_BYCOMMAND|MF_ENABLED },
\r
7860 { IDM_Backward, MF_BYCOMMAND|MF_ENABLED },
\r
7861 { IDM_ToEnd, MF_BYCOMMAND|MF_ENABLED },
\r
7862 { IDM_ToStart, MF_BYCOMMAND|MF_ENABLED },
\r
7863 { IDM_MoveNow, MF_BYCOMMAND|MF_ENABLED },
\r
7864 { IDM_TruncateGame, MF_BYCOMMAND|MF_ENABLED },
\r
7868 /* These modify either ncpEnables or gnuEnables */
\r
7869 Enables cmailEnables[] = {
\r
7870 { IDM_MailMove, MF_BYCOMMAND|MF_ENABLED },
\r
7871 { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_ENABLED },
\r
7872 { ACTION_POS, MF_BYPOSITION|MF_ENABLED },
\r
7873 { IDM_CallFlag, MF_BYCOMMAND|MF_GRAYED },
\r
7874 { IDM_Draw, MF_BYCOMMAND|MF_ENABLED },
\r
7875 { IDM_Adjourn, MF_BYCOMMAND|MF_GRAYED },
\r
7876 { IDM_Abort, MF_BYCOMMAND|MF_GRAYED },
\r
7880 Enables machineThinkingEnables[] = {
\r
7881 { IDM_LoadGame, MF_BYCOMMAND|MF_GRAYED },
\r
7882 { IDM_LoadNextGame, MF_BYCOMMAND|MF_GRAYED },
\r
7883 { IDM_LoadPrevGame, MF_BYCOMMAND|MF_GRAYED },
\r
7884 { IDM_ReloadGame, MF_BYCOMMAND|MF_GRAYED },
\r
7885 { IDM_PasteGame, MF_BYCOMMAND|MF_GRAYED },
\r
7886 { IDM_LoadPosition, MF_BYCOMMAND|MF_GRAYED },
\r
7887 { IDM_LoadNextPosition, MF_BYCOMMAND|MF_GRAYED },
\r
7888 { IDM_LoadPrevPosition, MF_BYCOMMAND|MF_GRAYED },
\r
7889 { IDM_ReloadPosition, MF_BYCOMMAND|MF_GRAYED },
\r
7890 { IDM_PastePosition, MF_BYCOMMAND|MF_GRAYED },
\r
7891 { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },
\r
7892 { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },
\r
7893 { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },
\r
7894 // { IDM_Match, MF_BYCOMMAND|MF_GRAYED },
\r
7895 { IDM_TypeInMove, MF_BYCOMMAND|MF_GRAYED },
\r
7896 { IDM_RetractMove, MF_BYCOMMAND|MF_GRAYED },
\r
7900 Enables userThinkingEnables[] = {
\r
7901 { IDM_LoadGame, MF_BYCOMMAND|MF_ENABLED },
\r
7902 { IDM_LoadNextGame, MF_BYCOMMAND|MF_ENABLED },
\r
7903 { IDM_LoadPrevGame, MF_BYCOMMAND|MF_ENABLED },
\r
7904 { IDM_ReloadGame, MF_BYCOMMAND|MF_ENABLED },
\r
7905 { IDM_PasteGame, MF_BYCOMMAND|MF_ENABLED },
\r
7906 { IDM_LoadPosition, MF_BYCOMMAND|MF_ENABLED },
\r
7907 { IDM_LoadNextPosition, MF_BYCOMMAND|MF_ENABLED },
\r
7908 { IDM_LoadPrevPosition, MF_BYCOMMAND|MF_ENABLED },
\r
7909 { IDM_ReloadPosition, MF_BYCOMMAND|MF_ENABLED },
\r
7910 { IDM_PastePosition, MF_BYCOMMAND|MF_ENABLED },
\r
7911 { IDM_MachineWhite, MF_BYCOMMAND|MF_ENABLED },
\r
7912 { IDM_MachineBlack, MF_BYCOMMAND|MF_ENABLED },
\r
7913 { IDM_TwoMachines, MF_BYCOMMAND|MF_ENABLED },
\r
7914 // { IDM_Match, MF_BYCOMMAND|MF_ENABLED },
\r
7915 { IDM_TypeInMove, MF_BYCOMMAND|MF_ENABLED },
\r
7916 { IDM_RetractMove, MF_BYCOMMAND|MF_ENABLED },
\r
7920 /*---------------------------------------------------------------------------*\
\r
7922 * Front-end interface functions exported by XBoard.
\r
7923 * Functions appear in same order as prototypes in frontend.h.
\r
7925 \*---------------------------------------------------------------------------*/
\r
7927 CheckMark(UINT item, int state)
\r
7929 if(item) CheckMenuItem(GetMenu(hwndMain), item, MF_BYCOMMAND|state);
\r
7935 static UINT prevChecked = 0;
\r
7936 static int prevPausing = 0;
\r
7939 if (pausing != prevPausing) {
\r
7940 prevPausing = pausing;
\r
7941 (void) CheckMenuItem(GetMenu(hwndMain), IDM_Pause,
\r
7942 MF_BYCOMMAND|(pausing ? MF_CHECKED : MF_UNCHECKED));
\r
7943 if (hwndPause) SetWindowText(hwndPause, pausing ? "C" : "P");
\r
7946 switch (gameMode) {
\r
7947 case BeginningOfGame:
\r
7948 if (appData.icsActive)
\r
7949 nowChecked = IDM_IcsClient;
\r
7950 else if (appData.noChessProgram)
\r
7951 nowChecked = IDM_EditGame;
\r
7953 nowChecked = IDM_MachineBlack;
\r
7955 case MachinePlaysBlack:
\r
7956 nowChecked = IDM_MachineBlack;
\r
7958 case MachinePlaysWhite:
\r
7959 nowChecked = IDM_MachineWhite;
\r
7961 case TwoMachinesPlay:
\r
7962 nowChecked = IDM_TwoMachines;
\r
7965 nowChecked = IDM_AnalysisMode;
\r
7968 nowChecked = IDM_AnalyzeFile;
\r
7971 nowChecked = IDM_EditGame;
\r
7973 case PlayFromGameFile:
\r
7974 nowChecked = IDM_LoadGame;
\r
7976 case EditPosition:
\r
7977 nowChecked = IDM_EditPosition;
\r
7980 nowChecked = IDM_Training;
\r
7982 case IcsPlayingWhite:
\r
7983 case IcsPlayingBlack:
\r
7984 case IcsObserving:
\r
7986 nowChecked = IDM_IcsClient;
\r
7993 CheckMark(prevChecked, MF_UNCHECKED);
\r
7994 CheckMark(nowChecked, MF_CHECKED);
\r
7995 CheckMark(IDM_Match, matchMode && matchGame < appData.matchGames ? MF_CHECKED : MF_UNCHECKED);
\r
7997 if (nowChecked == IDM_LoadGame || nowChecked == IDM_Training) {
\r
7998 (void) EnableMenuItem(GetMenu(hwndMain), IDM_Training,
\r
7999 MF_BYCOMMAND|MF_ENABLED);
\r
8001 (void) EnableMenuItem(GetMenu(hwndMain),
\r
8002 IDM_Training, MF_BYCOMMAND|MF_GRAYED);
\r
8005 prevChecked = nowChecked;
\r
8007 /* [DM] icsEngineAnalyze - Do a sceure check too */
\r
8008 if (appData.icsActive) {
\r
8009 if (appData.icsEngineAnalyze) {
\r
8010 CheckMark(IDM_AnalysisMode, MF_CHECKED);
\r
8012 CheckMark(IDM_AnalysisMode, MF_UNCHECKED);
\r
8015 DisplayLogos(); // [HGM] logos: mode change could have altered logos
\r
8021 HMENU hmenu = GetMenu(hwndMain);
\r
8022 SetMenuEnables(hmenu, icsEnables);
\r
8023 EnableMenuItem(GetSubMenu(hmenu, OPTIONS_POS), IDM_IcsOptions,
\r
8024 MF_BYCOMMAND|MF_ENABLED);
\r
8026 if (appData.zippyPlay) {
\r
8027 SetMenuEnables(hmenu, zippyEnables);
\r
8028 if (!appData.noChessProgram) /* [DM] icsEngineAnalyze */
\r
8029 (void) EnableMenuItem(GetMenu(hwndMain), IDM_AnalysisMode,
\r
8030 MF_BYCOMMAND|MF_ENABLED);
\r
8038 SetMenuEnables(GetMenu(hwndMain), gnuEnables);
\r
8044 HMENU hmenu = GetMenu(hwndMain);
\r
8045 SetMenuEnables(hmenu, ncpEnables);
\r
8046 DrawMenuBar(hwndMain);
\r
8052 SetMenuEnables(GetMenu(hwndMain), cmailEnables);
\r
8056 SetTrainingModeOn()
\r
8059 SetMenuEnables(GetMenu(hwndMain), trainingOnEnables);
\r
8060 for (i = 0; i < N_BUTTONS; i++) {
\r
8061 if (buttonDesc[i].hwnd != NULL)
\r
8062 EnableWindow(buttonDesc[i].hwnd, FALSE);
\r
8067 VOID SetTrainingModeOff()
\r
8070 SetMenuEnables(GetMenu(hwndMain), trainingOffEnables);
\r
8071 for (i = 0; i < N_BUTTONS; i++) {
\r
8072 if (buttonDesc[i].hwnd != NULL)
\r
8073 EnableWindow(buttonDesc[i].hwnd, TRUE);
\r
8079 SetUserThinkingEnables()
\r
8081 SetMenuEnables(GetMenu(hwndMain), userThinkingEnables);
\r
8085 SetMachineThinkingEnables()
\r
8087 HMENU hMenu = GetMenu(hwndMain);
\r
8088 int flags = MF_BYCOMMAND|MF_ENABLED;
\r
8090 SetMenuEnables(hMenu, machineThinkingEnables);
\r
8092 if (gameMode == MachinePlaysBlack) {
\r
8093 (void)EnableMenuItem(hMenu, IDM_MachineBlack, flags);
\r
8094 } else if (gameMode == MachinePlaysWhite) {
\r
8095 (void)EnableMenuItem(hMenu, IDM_MachineWhite, flags);
\r
8096 } else if (gameMode == TwoMachinesPlay) {
\r
8097 (void)EnableMenuItem(hMenu, matchMode ? IDM_Match : IDM_TwoMachines, flags); // [HGM] match
\r
8103 DisplayTitle(char *str)
\r
8105 char title[MSG_SIZ], *host;
\r
8106 if (str[0] != NULLCHAR) {
\r
8107 safeStrCpy(title, str, sizeof(title)/sizeof(title[0]) );
\r
8108 } else if (appData.icsActive) {
\r
8109 if (appData.icsCommPort[0] != NULLCHAR)
\r
8112 host = appData.icsHost;
\r
8113 snprintf(title, MSG_SIZ, "%s: %s", szTitle, host);
\r
8114 } else if (appData.noChessProgram) {
\r
8115 safeStrCpy(title, szTitle, sizeof(title)/sizeof(title[0]) );
\r
8117 safeStrCpy(title, szTitle, sizeof(title)/sizeof(title[0]) );
\r
8118 strcat(title, ": ");
\r
8119 strcat(title, first.tidy);
\r
8121 SetWindowText(hwndMain, title);
\r
8126 DisplayMessage(char *str1, char *str2)
\r
8130 int remain = MESSAGE_TEXT_MAX - 1;
\r
8133 moveErrorMessageUp = FALSE; /* turned on later by caller if needed */
\r
8134 messageText[0] = NULLCHAR;
\r
8136 len = strlen(str1);
\r
8137 if (len > remain) len = remain;
\r
8138 strncpy(messageText, str1, len);
\r
8139 messageText[len] = NULLCHAR;
\r
8142 if (*str2 && remain >= 2) {
\r
8144 strcat(messageText, " ");
\r
8147 len = strlen(str2);
\r
8148 if (len > remain) len = remain;
\r
8149 strncat(messageText, str2, len);
\r
8151 messageText[MESSAGE_TEXT_MAX - 1] = NULLCHAR;
8152 safeStrCpy(lastMsg, messageText, MSG_SIZ);
8154 if (hwndMain == NULL || IsIconic(hwndMain)) return;
\r
8158 hdc = GetDC(hwndMain);
\r
8159 oldFont = SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);
\r
8160 ExtTextOut(hdc, messageRect.left, messageRect.top, ETO_CLIPPED|ETO_OPAQUE,
\r
8161 &messageRect, messageText, strlen(messageText), NULL);
\r
8162 (void) SelectObject(hdc, oldFont);
\r
8163 (void) ReleaseDC(hwndMain, hdc);
\r
8167 DisplayError(char *str, int error)
\r
8169 char buf[MSG_SIZ*2], buf2[MSG_SIZ];
\r
8173 safeStrCpy(buf, str, sizeof(buf)/sizeof(buf[0]) );
\r
8175 len = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,
\r
8176 NULL, error, LANG_NEUTRAL,
\r
8177 (LPSTR) buf2, MSG_SIZ, NULL);
\r
8179 snprintf(buf, 2*MSG_SIZ, "%s:\n%s", str, buf2);
\r
8181 ErrorMap *em = errmap;
\r
8182 while (em->err != 0 && em->err != error) em++;
\r
8183 if (em->err != 0) {
\r
8184 snprintf(buf, 2*MSG_SIZ, "%s:\n%s", str, em->msg);
\r
8186 snprintf(buf, 2*MSG_SIZ, "%s:\nError code %d", str, error);
\r
8191 ErrorPopUp(_("Error"), buf);
\r
8196 DisplayMoveError(char *str)
\r
8198 fromX = fromY = -1;
\r
8199 ClearHighlights();
\r
8200 DrawPosition(FALSE, NULL);
\r
8201 if (appData.popupMoveErrors) {
\r
8202 ErrorPopUp(_("Error"), str);
\r
8204 DisplayMessage(str, "");
\r
8205 moveErrorMessageUp = TRUE;
\r
8210 DisplayFatalError(char *str, int error, int exitStatus)
\r
8212 char buf[2*MSG_SIZ], buf2[MSG_SIZ];
\r
8214 char *label = exitStatus ? _("Fatal Error") : _("Exiting");
\r
8217 len = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,
\r
8218 NULL, error, LANG_NEUTRAL,
\r
8219 (LPSTR) buf2, MSG_SIZ, NULL);
\r
8221 snprintf(buf, 2*MSG_SIZ, "%s:\n%s", str, buf2);
\r
8223 ErrorMap *em = errmap;
\r
8224 while (em->err != 0 && em->err != error) em++;
\r
8225 if (em->err != 0) {
\r
8226 snprintf(buf, 2*MSG_SIZ, "%s:\n%s", str, em->msg);
\r
8228 snprintf(buf, 2*MSG_SIZ, "%s:\nError code %d", str, error);
\r
8233 if (appData.debugMode) {
\r
8234 fprintf(debugFP, "%s: %s\n", label, str);
\r
8236 if (appData.popupExitMessage) {
\r
8237 (void) MessageBox(hwndMain, str, label, MB_OK|
\r
8238 (exitStatus ? MB_ICONSTOP : MB_ICONINFORMATION));
\r
8240 ExitEvent(exitStatus);
\r
8245 DisplayInformation(char *str)
\r
8247 (void) MessageBox(hwndMain, str, _("Information"), MB_OK|MB_ICONINFORMATION);
\r
8252 DisplayNote(char *str)
\r
8254 ErrorPopUp(_("Note"), str);
\r
8259 char *title, *question, *replyPrefix;
\r
8264 QuestionDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
8266 static QuestionParams *qp;
\r
8267 char reply[MSG_SIZ];
\r
8270 switch (message) {
\r
8271 case WM_INITDIALOG:
\r
8272 qp = (QuestionParams *) lParam;
\r
8273 CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));
\r
8274 Translate(hDlg, DLG_Question);
\r
8275 SetWindowText(hDlg, qp->title);
\r
8276 SetDlgItemText(hDlg, OPT_QuestionText, qp->question);
\r
8277 SetFocus(GetDlgItem(hDlg, OPT_QuestionInput));
\r
8281 switch (LOWORD(wParam)) {
\r
8283 safeStrCpy(reply, qp->replyPrefix, sizeof(reply)/sizeof(reply[0]) );
\r
8284 if (*reply) strcat(reply, " ");
\r
8285 len = strlen(reply);
\r
8286 GetDlgItemText(hDlg, OPT_QuestionInput, reply + len, sizeof(reply) - len);
\r
8287 strcat(reply, "\n");
\r
8288 OutputToProcess(qp->pr, reply, strlen(reply), &err);
\r
8289 EndDialog(hDlg, TRUE);
\r
8290 if (err) DisplayFatalError(_("Error writing to chess program"), err, 1);
\r
8293 EndDialog(hDlg, FALSE);
\r
8304 AskQuestion(char* title, char *question, char *replyPrefix, ProcRef pr)
\r
8306 QuestionParams qp;
\r
8310 qp.question = question;
\r
8311 qp.replyPrefix = replyPrefix;
\r
8313 lpProc = MakeProcInstance((FARPROC)QuestionDialog, hInst);
\r
8314 DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_Question),
\r
8315 hwndMain, (DLGPROC)lpProc, (LPARAM)&qp);
\r
8316 FreeProcInstance(lpProc);
\r
8319 /* [AS] Pick FRC position */
\r
8320 LRESULT CALLBACK NewGameFRC_Proc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
8322 static int * lpIndexFRC;
\r
8328 case WM_INITDIALOG:
\r
8329 lpIndexFRC = (int *) lParam;
\r
8331 CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));
\r
8332 Translate(hDlg, DLG_NewGameFRC);
\r
8334 SendDlgItemMessage( hDlg, IDC_NFG_Edit, EM_SETLIMITTEXT, sizeof(buf)-1, 0 );
\r
8335 SetDlgItemInt( hDlg, IDC_NFG_Edit, *lpIndexFRC, TRUE );
\r
8336 SendDlgItemMessage( hDlg, IDC_NFG_Edit, EM_SETSEL, 0, -1 );
\r
8337 SetFocus(GetDlgItem(hDlg, IDC_NFG_Edit));
\r
8342 switch( LOWORD(wParam) ) {
\r
8344 *lpIndexFRC = GetDlgItemInt(hDlg, IDC_NFG_Edit, &index_is_ok, TRUE );
\r
8345 EndDialog( hDlg, 0 );
\r
8346 shuffleOpenings = TRUE; /* [HGM] shuffle: switch shuffling on for as long as we stay in current variant */
\r
8349 EndDialog( hDlg, 1 );
\r
8351 case IDC_NFG_Edit:
\r
8352 if( HIWORD(wParam) == EN_CHANGE ) {
\r
8353 GetDlgItemInt(hDlg, IDC_NFG_Edit, &index_is_ok, TRUE );
\r
8355 EnableWindow( GetDlgItem(hDlg, IDOK), index_is_ok );
\r
8358 case IDC_NFG_Random:
\r
8359 snprintf( buf, sizeof(buf)/sizeof(buf[0]), "%d", myrandom() ); /* [HGM] shuffle: no longer limit to 960 */
\r
8360 SetDlgItemText(hDlg, IDC_NFG_Edit, buf );
\r
8373 int index = appData.defaultFrcPosition;
\r
8374 FARPROC lpProc = MakeProcInstance( (FARPROC) NewGameFRC_Proc, hInst );
\r
8376 result = DialogBoxParam( hInst, MAKEINTRESOURCE(DLG_NewGameFRC), hwndMain, (DLGPROC)lpProc, (LPARAM)&index );
\r
8378 if( result == 0 ) {
\r
8379 appData.defaultFrcPosition = index;
\r
8385 /* [AS] Game list options. Refactored by HGM */
\r
8387 HWND gameListOptionsDialog;
\r
8389 // low-level front-end: clear text edit / list widget
\r
8393 SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_RESETCONTENT, 0, 0 );
\r
8396 // low-level front-end: clear text edit / list widget
\r
8398 GLT_DeSelectList()
\r
8400 SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_SETCURSEL, 0, 0 );
\r
8403 // low-level front-end: append line to text edit / list widget
\r
8405 GLT_AddToList( char *name )
\r
8408 SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_ADDSTRING, 0, (LPARAM) name );
\r
8412 // low-level front-end: get line from text edit / list widget
\r
8414 GLT_GetFromList( int index, char *name )
\r
8417 if( SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_GETTEXT, index, (LPARAM) name ) != LB_ERR )
\r
8423 void GLT_MoveSelection( HWND hDlg, int delta )
\r
8425 int idx1 = (int) SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETCURSEL, 0, 0 );
\r
8426 int idx2 = idx1 + delta;
\r
8427 int count = (int) SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETCOUNT, 0, 0 );
\r
8429 if( idx1 >=0 && idx1 < count && idx2 >= 0 && idx2 < count ) {
\r
8432 SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETTEXT, idx1, (LPARAM) buf );
\r
8433 SendDlgItemMessage( hDlg, IDC_GameListTags, LB_DELETESTRING, idx1, 0 );
\r
8434 SendDlgItemMessage( hDlg, IDC_GameListTags, LB_INSERTSTRING, idx2, (LPARAM) buf );
\r
8435 SendDlgItemMessage( hDlg, IDC_GameListTags, LB_SETCURSEL, idx2, 0 );
\r
8439 LRESULT CALLBACK GameListOptions_Proc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
8443 case WM_INITDIALOG:
\r
8444 gameListOptionsDialog = hDlg; // [HGM] pass through global to keep out off back-end
\r
8446 CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));
\r
8447 Translate(hDlg, DLG_GameListOptions);
\r
8449 /* Initialize list */
\r
8450 GLT_TagsToList( lpUserGLT );
\r
8452 SetFocus( GetDlgItem(hDlg, IDC_GameListTags) );
\r
8457 switch( LOWORD(wParam) ) {
\r
8460 EndDialog( hDlg, 0 );
\r
8463 EndDialog( hDlg, 1 );
\r
8466 case IDC_GLT_Default:
\r
8467 GLT_TagsToList( GLT_DEFAULT_TAGS );
\r
8470 case IDC_GLT_Restore:
\r
8471 GLT_TagsToList( appData.gameListTags );
\r
8475 GLT_MoveSelection( hDlg, -1 );
\r
8478 case IDC_GLT_Down:
\r
8479 GLT_MoveSelection( hDlg, +1 );
\r
8489 int GameListOptions()
\r
8492 FARPROC lpProc = MakeProcInstance( (FARPROC) GameListOptions_Proc, hInst );
\r
8494 safeStrCpy( lpUserGLT, appData.gameListTags ,LPUSERGLT_SIZE );
\r
8496 result = DialogBoxParam( hInst, MAKEINTRESOURCE(DLG_GameListOptions), hwndMain, (DLGPROC)lpProc, (LPARAM)lpUserGLT );
\r
8498 if( result == 0 ) {
\r
8499 /* [AS] Memory leak here! */
\r
8500 appData.gameListTags = strdup( lpUserGLT );
\r
8507 DisplayIcsInteractionTitle(char *str)
\r
8509 char consoleTitle[MSG_SIZ];
\r
8511 snprintf(consoleTitle, MSG_SIZ, "%s: %s", szConsoleTitle, str);
\r
8512 SetWindowText(hwndConsole, consoleTitle);
\r
8516 DrawPosition(int fullRedraw, Board board)
\r
8518 HDCDrawPosition(NULL, (BOOLEAN) fullRedraw, board);
\r
8521 void NotifyFrontendLogin()
\r
8524 UpdateICSWidth(GetDlgItem(hwndConsole, OPT_ConsoleText));
\r
8530 fromX = fromY = -1;
\r
8531 if (dragInfo.pos.x != -1 || dragInfo.pos.y != -1) {
\r
8532 dragInfo.pos.x = dragInfo.pos.y = -1;
\r
8533 dragInfo.pos.x = dragInfo.pos.y = -1;
\r
8534 dragInfo.lastpos = dragInfo.pos;
\r
8535 dragInfo.start.x = dragInfo.start.y = -1;
\r
8536 dragInfo.from = dragInfo.start;
\r
8538 DrawPosition(TRUE, NULL);
\r
8545 CommentPopUp(char *title, char *str)
\r
8547 HWND hwnd = GetActiveWindow();
\r
8548 EitherCommentPopUp(currentMove, title, str, FALSE); // [HGM] vari: fake move index, rather than 0
\r
8550 SetActiveWindow(hwnd);
\r
8554 CommentPopDown(void)
\r
8556 CheckMenuItem(GetMenu(hwndMain), IDM_Comment, MF_UNCHECKED);
\r
8557 if (commentDialog) {
\r
8558 ShowWindow(commentDialog, SW_HIDE);
\r
8560 commentUp = FALSE;
\r
8564 EditCommentPopUp(int index, char *title, char *str)
\r
8566 EitherCommentPopUp(index, title, str, TRUE);
\r
8573 MyPlaySound(&sounds[(int)SoundMove]);
\r
8576 VOID PlayIcsWinSound()
\r
8578 MyPlaySound(&sounds[(int)SoundIcsWin]);
\r
8581 VOID PlayIcsLossSound()
\r
8583 MyPlaySound(&sounds[(int)SoundIcsLoss]);
\r
8586 VOID PlayIcsDrawSound()
\r
8588 MyPlaySound(&sounds[(int)SoundIcsDraw]);
\r
8591 VOID PlayIcsUnfinishedSound()
\r
8593 MyPlaySound(&sounds[(int)SoundIcsUnfinished]);
\r
8599 MyPlaySound(&sounds[(int)SoundAlarm]);
\r
8607 consoleEcho = TRUE;
\r
8608 hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
8609 SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&consoleCF);
\r
8610 SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);
\r
8619 consoleEcho = FALSE;
\r
8620 hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
8621 /* This works OK: set text and background both to the same color */
\r
8623 cf.crTextColor = COLOR_ECHOOFF;
\r
8624 SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf);
\r
8625 SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, cf.crTextColor);
\r
8628 /* No Raw()...? */
\r
8630 void Colorize(ColorClass cc, int continuation)
\r
8632 currentColorClass = cc;
\r
8633 consoleCF.dwMask = CFM_COLOR|CFM_BOLD|CFM_ITALIC|CFM_UNDERLINE|CFM_STRIKEOUT;
\r
8634 consoleCF.crTextColor = textAttribs[cc].color;
\r
8635 consoleCF.dwEffects = textAttribs[cc].effects;
\r
8636 if (!continuation) MyPlaySound(&textAttribs[cc].sound);
\r
8642 static char buf[MSG_SIZ];
\r
8643 DWORD bufsiz = MSG_SIZ;
\r
8645 if(appData.userName != NULL && appData.userName[0] != 0) {
\r
8646 return appData.userName; /* [HGM] username: prefer name selected by user over his system login */
\r
8648 if (!GetUserName(buf, &bufsiz)) {
\r
8649 /*DisplayError("Error getting user name", GetLastError());*/
\r
8650 safeStrCpy(buf, _("User"), sizeof(buf)/sizeof(buf[0]) );
\r
8658 static char buf[MSG_SIZ];
\r
8659 DWORD bufsiz = MSG_SIZ;
\r
8661 if (!GetComputerName(buf, &bufsiz)) {
\r
8662 /*DisplayError("Error getting host name", GetLastError());*/
\r
8663 safeStrCpy(buf, _("Unknown"), sizeof(buf)/sizeof(buf[0]) );
\r
8670 ClockTimerRunning()
\r
8672 return clockTimerEvent != 0;
\r
8678 if (clockTimerEvent == 0) return FALSE;
\r
8679 KillTimer(hwndMain, clockTimerEvent);
\r
8680 clockTimerEvent = 0;
\r
8685 StartClockTimer(long millisec)
\r
8687 clockTimerEvent = SetTimer(hwndMain, (UINT) CLOCK_TIMER_ID,
\r
8688 (UINT) millisec, NULL);
\r
8692 DisplayWhiteClock(long timeRemaining, int highlight)
\r
8695 char *flag = whiteFlag && gameMode == TwoMachinesPlay ? "(!)" : "";
\r
8697 if(appData.noGUI) return;
\r
8698 hdc = GetDC(hwndMain);
\r
8699 if (!IsIconic(hwndMain)) {
\r
8700 DisplayAClock(hdc, timeRemaining, highlight,
\r
8701 flipClock ? &blackRect : &whiteRect, _("White"), flag);
\r
8703 if (highlight && iconCurrent == iconBlack) {
\r
8704 iconCurrent = iconWhite;
\r
8705 PostMessage(hwndMain, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);
\r
8706 if (IsIconic(hwndMain)) {
\r
8707 DrawIcon(hdc, 2, 2, iconCurrent);
\r
8710 (void) ReleaseDC(hwndMain, hdc);
\r
8712 PostMessage(hwndConsole, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);
\r
8716 DisplayBlackClock(long timeRemaining, int highlight)
\r
8719 char *flag = blackFlag && gameMode == TwoMachinesPlay ? "(!)" : "";
\r
8721 if(appData.noGUI) return;
\r
8722 hdc = GetDC(hwndMain);
\r
8723 if (!IsIconic(hwndMain)) {
\r
8724 DisplayAClock(hdc, timeRemaining, highlight,
\r
8725 flipClock ? &whiteRect : &blackRect, _("Black"), flag);
\r
8727 if (highlight && iconCurrent == iconWhite) {
\r
8728 iconCurrent = iconBlack;
\r
8729 PostMessage(hwndMain, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);
\r
8730 if (IsIconic(hwndMain)) {
\r
8731 DrawIcon(hdc, 2, 2, iconCurrent);
\r
8734 (void) ReleaseDC(hwndMain, hdc);
\r
8736 PostMessage(hwndConsole, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);
\r
8741 LoadGameTimerRunning()
\r
8743 return loadGameTimerEvent != 0;
\r
8747 StopLoadGameTimer()
\r
8749 if (loadGameTimerEvent == 0) return FALSE;
\r
8750 KillTimer(hwndMain, loadGameTimerEvent);
\r
8751 loadGameTimerEvent = 0;
\r
8756 StartLoadGameTimer(long millisec)
\r
8758 loadGameTimerEvent = SetTimer(hwndMain, (UINT) LOAD_GAME_TIMER_ID,
\r
8759 (UINT) millisec, NULL);
\r
8767 char fileTitle[MSG_SIZ];
\r
8769 defName = DefaultFileName(appData.oldSaveStyle ? "gam" : "pgn");
\r
8770 f = OpenFileDialog(hwndMain, "a", defName,
\r
8771 appData.oldSaveStyle ? "gam" : "pgn",
\r
8773 _("Save Game to File"), NULL, fileTitle, NULL);
\r
8775 SaveGame(f, 0, "");
\r
8782 ScheduleDelayedEvent(DelayedEventCallback cb, long millisec)
\r
8784 if (delayedTimerEvent != 0) {
\r
8785 if (appData.debugMode && cb != delayedTimerCallback) { // [HGM] alive: not too much debug
\r
8786 fprintf(debugFP, "ScheduleDelayedEvent: event already scheduled\n");
\r
8788 KillTimer(hwndMain, delayedTimerEvent);
\r
8789 delayedTimerEvent = 0;
\r
8790 if(delayedTimerCallback != cb) // [HGM] alive: do not "flush" same event, just postpone it
\r
8791 delayedTimerCallback();
\r
8793 delayedTimerCallback = cb;
\r
8794 delayedTimerEvent = SetTimer(hwndMain, (UINT) DELAYED_TIMER_ID,
\r
8795 (UINT) millisec, NULL);
\r
8798 DelayedEventCallback
\r
8801 if (delayedTimerEvent) {
\r
8802 return delayedTimerCallback;
\r
8809 CancelDelayedEvent()
\r
8811 if (delayedTimerEvent) {
\r
8812 KillTimer(hwndMain, delayedTimerEvent);
\r
8813 delayedTimerEvent = 0;
\r
8817 DWORD GetWin32Priority(int nice)
\r
8818 { // [HGM] nice: translate Unix nice() value to indows priority class. (Code stolen from Polyglot 1.4w11)
\r
8820 REALTIME_PRIORITY_CLASS 0x00000100
\r
8821 HIGH_PRIORITY_CLASS 0x00000080
\r
8822 ABOVE_NORMAL_PRIORITY_CLASS 0x00008000
\r
8823 NORMAL_PRIORITY_CLASS 0x00000020
\r
8824 BELOW_NORMAL_PRIORITY_CLASS 0x00004000
\r
8825 IDLE_PRIORITY_CLASS 0x00000040
\r
8827 if (nice < -15) return 0x00000080;
\r
8828 if (nice < 0) return 0x00008000;
\r
8829 if (nice == 0) return 0x00000020;
\r
8830 if (nice < 15) return 0x00004000;
\r
8831 return 0x00000040;
\r
8834 /* Start a child process running the given program.
\r
8835 The process's standard output can be read from "from", and its
\r
8836 standard input can be written to "to".
\r
8837 Exit with fatal error if anything goes wrong.
\r
8838 Returns an opaque pointer that can be used to destroy the process
\r
8842 StartChildProcess(char *cmdLine, char *dir, ProcRef *pr)
\r
8844 #define BUFSIZE 4096
\r
8846 HANDLE hChildStdinRd, hChildStdinWr,
\r
8847 hChildStdoutRd, hChildStdoutWr;
\r
8848 HANDLE hChildStdinWrDup, hChildStdoutRdDup;
\r
8849 SECURITY_ATTRIBUTES saAttr;
\r
8851 PROCESS_INFORMATION piProcInfo;
\r
8852 STARTUPINFO siStartInfo;
\r
8854 char buf[MSG_SIZ];
\r
8857 if (appData.debugMode) {
\r
8858 fprintf(debugFP, "StartChildProcess (dir=\"%s\") %s\n", dir, cmdLine);
\r
8863 /* Set the bInheritHandle flag so pipe handles are inherited. */
\r
8864 saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);
\r
8865 saAttr.bInheritHandle = TRUE;
\r
8866 saAttr.lpSecurityDescriptor = NULL;
\r
8869 * The steps for redirecting child's STDOUT:
\r
8870 * 1. Create anonymous pipe to be STDOUT for child.
\r
8871 * 2. Create a noninheritable duplicate of read handle,
\r
8872 * and close the inheritable read handle.
\r
8875 /* Create a pipe for the child's STDOUT. */
\r
8876 if (! CreatePipe(&hChildStdoutRd, &hChildStdoutWr, &saAttr, 0)) {
\r
8877 return GetLastError();
\r
8880 /* Duplicate the read handle to the pipe, so it is not inherited. */
\r
8881 fSuccess = DuplicateHandle(GetCurrentProcess(), hChildStdoutRd,
\r
8882 GetCurrentProcess(), &hChildStdoutRdDup, 0,
\r
8883 FALSE, /* not inherited */
\r
8884 DUPLICATE_SAME_ACCESS);
\r
8886 return GetLastError();
\r
8888 CloseHandle(hChildStdoutRd);
\r
8891 * The steps for redirecting child's STDIN:
\r
8892 * 1. Create anonymous pipe to be STDIN for child.
\r
8893 * 2. Create a noninheritable duplicate of write handle,
\r
8894 * and close the inheritable write handle.
\r
8897 /* Create a pipe for the child's STDIN. */
\r
8898 if (! CreatePipe(&hChildStdinRd, &hChildStdinWr, &saAttr, 0)) {
\r
8899 return GetLastError();
\r
8902 /* Duplicate the write handle to the pipe, so it is not inherited. */
\r
8903 fSuccess = DuplicateHandle(GetCurrentProcess(), hChildStdinWr,
\r
8904 GetCurrentProcess(), &hChildStdinWrDup, 0,
\r
8905 FALSE, /* not inherited */
\r
8906 DUPLICATE_SAME_ACCESS);
\r
8908 return GetLastError();
\r
8910 CloseHandle(hChildStdinWr);
\r
8912 /* Arrange to (1) look in dir for the child .exe file, and
\r
8913 * (2) have dir be the child's working directory. Interpret
\r
8914 * dir relative to the directory WinBoard loaded from. */
\r
8915 GetCurrentDirectory(MSG_SIZ, buf);
\r
8916 SetCurrentDirectory(installDir);
\r
8917 // kludgey way to update logos in tourney, as long as back-end can't do it
\r
8918 if(!strcmp(cmdLine, first.program)) LoadLogo(&first, 0); else
\r
8919 if(!strcmp(cmdLine, second.program)) LoadLogo(&second, 1);
\r
8920 SetCurrentDirectory(dir);
\r
8922 /* Now create the child process. */
\r
8924 siStartInfo.cb = sizeof(STARTUPINFO);
\r
8925 siStartInfo.lpReserved = NULL;
\r
8926 siStartInfo.lpDesktop = NULL;
\r
8927 siStartInfo.lpTitle = NULL;
\r
8928 siStartInfo.dwFlags = STARTF_USESTDHANDLES;
\r
8929 siStartInfo.cbReserved2 = 0;
\r
8930 siStartInfo.lpReserved2 = NULL;
\r
8931 siStartInfo.hStdInput = hChildStdinRd;
\r
8932 siStartInfo.hStdOutput = hChildStdoutWr;
\r
8933 siStartInfo.hStdError = hChildStdoutWr;
\r
8935 fSuccess = CreateProcess(NULL,
\r
8936 cmdLine, /* command line */
\r
8937 NULL, /* process security attributes */
\r
8938 NULL, /* primary thread security attrs */
\r
8939 TRUE, /* handles are inherited */
\r
8940 DETACHED_PROCESS|CREATE_NEW_PROCESS_GROUP,
\r
8941 NULL, /* use parent's environment */
\r
8943 &siStartInfo, /* STARTUPINFO pointer */
\r
8944 &piProcInfo); /* receives PROCESS_INFORMATION */
\r
8946 err = GetLastError();
\r
8947 SetCurrentDirectory(buf); /* return to prev directory */
\r
8952 if (appData.niceEngines){ // [HGM] nice: adjust engine proc priority
\r
8953 if(appData.debugMode) fprintf(debugFP, "nice engine proc to %d\n", appData.niceEngines);
\r
8954 SetPriorityClass(piProcInfo.hProcess, GetWin32Priority(appData.niceEngines));
\r
8957 /* Close the handles we don't need in the parent */
\r
8958 CloseHandle(piProcInfo.hThread);
\r
8959 CloseHandle(hChildStdinRd);
\r
8960 CloseHandle(hChildStdoutWr);
\r
8962 /* Prepare return value */
\r
8963 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
\r
8964 cp->kind = CPReal;
\r
8965 cp->hProcess = piProcInfo.hProcess;
\r
8966 cp->pid = piProcInfo.dwProcessId;
\r
8967 cp->hFrom = hChildStdoutRdDup;
\r
8968 cp->hTo = hChildStdinWrDup;
\r
8970 *pr = (void *) cp;
\r
8972 /* Klaus Friedel says that this Sleep solves a problem under Windows
\r
8973 2000 where engines sometimes don't see the initial command(s)
\r
8974 from WinBoard and hang. I don't understand how that can happen,
\r
8975 but the Sleep is harmless, so I've put it in. Others have also
\r
8976 reported what may be the same problem, so hopefully this will fix
\r
8977 it for them too. */
\r
8985 DestroyChildProcess(ProcRef pr, int/*boolean*/ signal)
\r
8987 ChildProc *cp; int result;
\r
8989 cp = (ChildProc *) pr;
\r
8990 if (cp == NULL) return;
\r
8992 switch (cp->kind) {
\r
8994 /* TerminateProcess is considered harmful, so... */
\r
8995 CloseHandle(cp->hTo); /* Closing this will give the child an EOF and hopefully kill it */
\r
8996 if (cp->hFrom) CloseHandle(cp->hFrom); /* if NULL, InputThread will close it */
\r
8997 /* The following doesn't work because the chess program
\r
8998 doesn't "have the same console" as WinBoard. Maybe
\r
8999 we could arrange for this even though neither WinBoard
\r
9000 nor the chess program uses a console for stdio? */
\r
9001 /*!!if (signal) GenerateConsoleCtrlEvent(CTRL_BREAK_EVENT, cp->pid);*/
\r
9003 /* [AS] Special termination modes for misbehaving programs... */
\r
9004 if( signal == 9 ) {
\r
9005 result = TerminateProcess( cp->hProcess, 0 );
\r
9007 if ( appData.debugMode) {
\r
9008 fprintf( debugFP, "Terminating process %lu, result=%d\n", cp->pid, result );
\r
9011 else if( signal == 10 ) {
\r
9012 DWORD dw = WaitForSingleObject( cp->hProcess, 3*1000 ); // Wait 3 seconds at most
\r
9014 if( dw != WAIT_OBJECT_0 ) {
\r
9015 result = TerminateProcess( cp->hProcess, 0 );
\r
9017 if ( appData.debugMode) {
\r
9018 fprintf( debugFP, "Process %lu still alive after timeout, killing... result=%d\n", cp->pid, result );
\r
9024 CloseHandle(cp->hProcess);
\r
9028 if (cp->hFrom) CloseHandle(cp->hFrom);
\r
9032 closesocket(cp->sock);
\r
9037 if (signal) send(cp->sock2, "\017", 1, 0); /* 017 = 15 = SIGTERM */
\r
9038 closesocket(cp->sock);
\r
9039 closesocket(cp->sock2);
\r
9047 InterruptChildProcess(ProcRef pr)
\r
9051 cp = (ChildProc *) pr;
\r
9052 if (cp == NULL) return;
\r
9053 switch (cp->kind) {
\r
9055 /* The following doesn't work because the chess program
\r
9056 doesn't "have the same console" as WinBoard. Maybe
\r
9057 we could arrange for this even though neither WinBoard
\r
9058 nor the chess program uses a console for stdio */
\r
9059 /*!!GenerateConsoleCtrlEvent(CTRL_C_EVENT, cp->pid);*/
\r
9064 /* Can't interrupt */
\r
9068 send(cp->sock2, "\002", 1, 0); /* 2 = SIGINT */
\r
9075 OpenTelnet(char *host, char *port, ProcRef *pr)
\r
9077 char cmdLine[MSG_SIZ];
\r
9079 if (port[0] == NULLCHAR) {
\r
9080 snprintf(cmdLine, MSG_SIZ, "%s %s", appData.telnetProgram, host);
\r
9082 snprintf(cmdLine, MSG_SIZ, "%s %s %s", appData.telnetProgram, host, port);
\r
9084 return StartChildProcess(cmdLine, "", pr);
\r
9088 /* Code to open TCP sockets */
\r
9091 OpenTCP(char *host, char *port, ProcRef *pr)
\r
9096 struct sockaddr_in sa, mysa;
\r
9097 struct hostent FAR *hp;
\r
9098 unsigned short uport;
\r
9099 WORD wVersionRequested;
\r
9102 /* Initialize socket DLL */
\r
9103 wVersionRequested = MAKEWORD(1, 1);
\r
9104 err = WSAStartup(wVersionRequested, &wsaData);
\r
9105 if (err != 0) return err;
\r
9108 if ((s = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {
\r
9109 err = WSAGetLastError();
\r
9114 /* Bind local address using (mostly) don't-care values.
\r
9116 memset((char *) &mysa, 0, sizeof(struct sockaddr_in));
\r
9117 mysa.sin_family = AF_INET;
\r
9118 mysa.sin_addr.s_addr = INADDR_ANY;
\r
9119 uport = (unsigned short) 0;
\r
9120 mysa.sin_port = htons(uport);
\r
9121 if (bind(s, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))
\r
9122 == SOCKET_ERROR) {
\r
9123 err = WSAGetLastError();
\r
9128 /* Resolve remote host name */
\r
9129 memset((char *) &sa, 0, sizeof(struct sockaddr_in));
\r
9130 if (!(hp = gethostbyname(host))) {
\r
9131 unsigned int b0, b1, b2, b3;
\r
9133 err = WSAGetLastError();
\r
9135 if (sscanf(host, "%u.%u.%u.%u", &b0, &b1, &b2, &b3) == 4) {
\r
9136 hp = (struct hostent *) calloc(1, sizeof(struct hostent));
\r
9137 hp->h_addrtype = AF_INET;
\r
9139 hp->h_addr_list = (char **) calloc(2, sizeof(char *));
\r
9140 hp->h_addr_list[0] = (char *) malloc(4);
\r
9141 hp->h_addr_list[0][0] = (char) b0;
\r
9142 hp->h_addr_list[0][1] = (char) b1;
\r
9143 hp->h_addr_list[0][2] = (char) b2;
\r
9144 hp->h_addr_list[0][3] = (char) b3;
\r
9150 sa.sin_family = hp->h_addrtype;
\r
9151 uport = (unsigned short) atoi(port);
\r
9152 sa.sin_port = htons(uport);
\r
9153 memcpy((char *) &sa.sin_addr, hp->h_addr, hp->h_length);
\r
9155 /* Make connection */
\r
9156 if (connect(s, (struct sockaddr *) &sa,
\r
9157 sizeof(struct sockaddr_in)) == SOCKET_ERROR) {
\r
9158 err = WSAGetLastError();
\r
9163 /* Prepare return value */
\r
9164 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
\r
9165 cp->kind = CPSock;
\r
9167 *pr = (ProcRef *) cp;
\r
9173 OpenCommPort(char *name, ProcRef *pr)
\r
9178 char fullname[MSG_SIZ];
\r
9180 if (*name != '\\')
\r
9181 snprintf(fullname, MSG_SIZ, "\\\\.\\%s", name);
\r
9183 safeStrCpy(fullname, name, sizeof(fullname)/sizeof(fullname[0]) );
\r
9185 h = CreateFile(name, GENERIC_READ | GENERIC_WRITE,
\r
9186 0, NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL);
\r
9187 if (h == (HANDLE) -1) {
\r
9188 return GetLastError();
\r
9192 if (!SetCommState(h, (LPDCB) &dcb)) return GetLastError();
\r
9194 /* Accumulate characters until a 100ms pause, then parse */
\r
9195 ct.ReadIntervalTimeout = 100;
\r
9196 ct.ReadTotalTimeoutMultiplier = 0;
\r
9197 ct.ReadTotalTimeoutConstant = 0;
\r
9198 ct.WriteTotalTimeoutMultiplier = 0;
\r
9199 ct.WriteTotalTimeoutConstant = 0;
\r
9200 if (!SetCommTimeouts(h, (LPCOMMTIMEOUTS) &ct)) return GetLastError();
\r
9202 /* Prepare return value */
\r
9203 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
\r
9204 cp->kind = CPComm;
\r
9207 *pr = (ProcRef *) cp;
\r
9213 OpenLoopback(ProcRef *pr)
\r
9215 DisplayFatalError(_("Not implemented"), 0, 1);
\r
9221 OpenRcmd(char* host, char* user, char* cmd, ProcRef* pr)
\r
9226 struct sockaddr_in sa, mysa;
\r
9227 struct hostent FAR *hp;
\r
9228 unsigned short uport;
\r
9229 WORD wVersionRequested;
\r
9232 char stderrPortStr[MSG_SIZ];
\r
9234 /* Initialize socket DLL */
\r
9235 wVersionRequested = MAKEWORD(1, 1);
\r
9236 err = WSAStartup(wVersionRequested, &wsaData);
\r
9237 if (err != 0) return err;
\r
9239 /* Resolve remote host name */
\r
9240 memset((char *) &sa, 0, sizeof(struct sockaddr_in));
\r
9241 if (!(hp = gethostbyname(host))) {
\r
9242 unsigned int b0, b1, b2, b3;
\r
9244 err = WSAGetLastError();
\r
9246 if (sscanf(host, "%u.%u.%u.%u", &b0, &b1, &b2, &b3) == 4) {
\r
9247 hp = (struct hostent *) calloc(1, sizeof(struct hostent));
\r
9248 hp->h_addrtype = AF_INET;
\r
9250 hp->h_addr_list = (char **) calloc(2, sizeof(char *));
\r
9251 hp->h_addr_list[0] = (char *) malloc(4);
\r
9252 hp->h_addr_list[0][0] = (char) b0;
\r
9253 hp->h_addr_list[0][1] = (char) b1;
\r
9254 hp->h_addr_list[0][2] = (char) b2;
\r
9255 hp->h_addr_list[0][3] = (char) b3;
\r
9261 sa.sin_family = hp->h_addrtype;
\r
9262 uport = (unsigned short) 514;
\r
9263 sa.sin_port = htons(uport);
\r
9264 memcpy((char *) &sa.sin_addr, hp->h_addr, hp->h_length);
\r
9266 /* Bind local socket to unused "privileged" port address
\r
9268 s = INVALID_SOCKET;
\r
9269 memset((char *) &mysa, 0, sizeof(struct sockaddr_in));
\r
9270 mysa.sin_family = AF_INET;
\r
9271 mysa.sin_addr.s_addr = INADDR_ANY;
\r
9272 for (fromPort = 1023;; fromPort--) {
\r
9273 if (fromPort < 0) {
\r
9275 return WSAEADDRINUSE;
\r
9277 if (s == INVALID_SOCKET) {
\r
9278 if ((s = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {
\r
9279 err = WSAGetLastError();
\r
9284 uport = (unsigned short) fromPort;
\r
9285 mysa.sin_port = htons(uport);
\r
9286 if (bind(s, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))
\r
9287 == SOCKET_ERROR) {
\r
9288 err = WSAGetLastError();
\r
9289 if (err == WSAEADDRINUSE) continue;
\r
9293 if (connect(s, (struct sockaddr *) &sa,
\r
9294 sizeof(struct sockaddr_in)) == SOCKET_ERROR) {
\r
9295 err = WSAGetLastError();
\r
9296 if (err == WSAEADDRINUSE) {
\r
9307 /* Bind stderr local socket to unused "privileged" port address
\r
9309 s2 = INVALID_SOCKET;
\r
9310 memset((char *) &mysa, 0, sizeof(struct sockaddr_in));
\r
9311 mysa.sin_family = AF_INET;
\r
9312 mysa.sin_addr.s_addr = INADDR_ANY;
\r
9313 for (fromPort = 1023;; fromPort--) {
\r
9314 if (fromPort == prevStderrPort) continue; // don't reuse port
\r
9315 if (fromPort < 0) {
\r
9316 (void) closesocket(s);
\r
9318 return WSAEADDRINUSE;
\r
9320 if (s2 == INVALID_SOCKET) {
\r
9321 if ((s2 = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {
\r
9322 err = WSAGetLastError();
\r
9328 uport = (unsigned short) fromPort;
\r
9329 mysa.sin_port = htons(uport);
\r
9330 if (bind(s2, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))
\r
9331 == SOCKET_ERROR) {
\r
9332 err = WSAGetLastError();
\r
9333 if (err == WSAEADDRINUSE) continue;
\r
9334 (void) closesocket(s);
\r
9338 if (listen(s2, 1) == SOCKET_ERROR) {
\r
9339 err = WSAGetLastError();
\r
9340 if (err == WSAEADDRINUSE) {
\r
9342 s2 = INVALID_SOCKET;
\r
9345 (void) closesocket(s);
\r
9346 (void) closesocket(s2);
\r
9352 prevStderrPort = fromPort; // remember port used
\r
9353 snprintf(stderrPortStr, MSG_SIZ, "%d", fromPort);
\r
9355 if (send(s, stderrPortStr, strlen(stderrPortStr) + 1, 0) == SOCKET_ERROR) {
\r
9356 err = WSAGetLastError();
\r
9357 (void) closesocket(s);
\r
9358 (void) closesocket(s2);
\r
9363 if (send(s, UserName(), strlen(UserName()) + 1, 0) == SOCKET_ERROR) {
\r
9364 err = WSAGetLastError();
\r
9365 (void) closesocket(s);
\r
9366 (void) closesocket(s2);
\r
9370 if (*user == NULLCHAR) user = UserName();
\r
9371 if (send(s, user, strlen(user) + 1, 0) == SOCKET_ERROR) {
\r
9372 err = WSAGetLastError();
\r
9373 (void) closesocket(s);
\r
9374 (void) closesocket(s2);
\r
9378 if (send(s, cmd, strlen(cmd) + 1, 0) == SOCKET_ERROR) {
\r
9379 err = WSAGetLastError();
\r
9380 (void) closesocket(s);
\r
9381 (void) closesocket(s2);
\r
9386 if ((s3 = accept(s2, NULL, NULL)) == INVALID_SOCKET) {
\r
9387 err = WSAGetLastError();
\r
9388 (void) closesocket(s);
\r
9389 (void) closesocket(s2);
\r
9393 (void) closesocket(s2); /* Stop listening */
\r
9395 /* Prepare return value */
\r
9396 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
\r
9397 cp->kind = CPRcmd;
\r
9400 *pr = (ProcRef *) cp;
\r
9407 AddInputSource(ProcRef pr, int lineByLine,
\r
9408 InputCallback func, VOIDSTAR closure)
\r
9410 InputSource *is, *is2 = NULL;
\r
9411 ChildProc *cp = (ChildProc *) pr;
\r
9413 is = (InputSource *) calloc(1, sizeof(InputSource));
\r
9414 is->lineByLine = lineByLine;
\r
9416 is->closure = closure;
\r
9417 is->second = NULL;
\r
9418 is->next = is->buf;
\r
9419 if (pr == NoProc) {
\r
9420 is->kind = CPReal;
\r
9421 consoleInputSource = is;
\r
9423 is->kind = cp->kind;
\r
9425 [AS] Try to avoid a race condition if the thread is given control too early:
\r
9426 we create all threads suspended so that the is->hThread variable can be
\r
9427 safely assigned, then let the threads start with ResumeThread.
\r
9429 switch (cp->kind) {
\r
9431 is->hFile = cp->hFrom;
\r
9432 cp->hFrom = NULL; /* now owned by InputThread */
\r
9434 CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) NonOvlInputThread,
\r
9435 (LPVOID) is, CREATE_SUSPENDED, &is->id);
\r
9439 is->hFile = cp->hFrom;
\r
9440 cp->hFrom = NULL; /* now owned by InputThread */
\r
9442 CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) InputThread,
\r
9443 (LPVOID) is, CREATE_SUSPENDED, &is->id);
\r
9447 is->sock = cp->sock;
\r
9449 CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,
\r
9450 (LPVOID) is, CREATE_SUSPENDED, &is->id);
\r
9454 is2 = (InputSource *) calloc(1, sizeof(InputSource));
\r
9456 is->sock = cp->sock;
\r
9458 is2->sock = cp->sock2;
\r
9459 is2->second = is2;
\r
9461 CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,
\r
9462 (LPVOID) is, CREATE_SUSPENDED, &is->id);
\r
9464 CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,
\r
9465 (LPVOID) is2, CREATE_SUSPENDED, &is2->id);
\r
9469 if( is->hThread != NULL ) {
\r
9470 ResumeThread( is->hThread );
\r
9473 if( is2 != NULL && is2->hThread != NULL ) {
\r
9474 ResumeThread( is2->hThread );
\r
9478 return (InputSourceRef) is;
\r
9482 RemoveInputSource(InputSourceRef isr)
\r
9486 is = (InputSource *) isr;
\r
9487 is->hThread = NULL; /* tell thread to stop */
\r
9488 CloseHandle(is->hThread);
\r
9489 if (is->second != NULL) {
\r
9490 is->second->hThread = NULL;
\r
9491 CloseHandle(is->second->hThread);
\r
9495 int no_wrap(char *message, int count)
\r
9497 ConsoleOutput(message, count, FALSE);
\r
9502 OutputToProcess(ProcRef pr, char *message, int count, int *outError)
\r
9505 int outCount = SOCKET_ERROR;
\r
9506 ChildProc *cp = (ChildProc *) pr;
\r
9507 static OVERLAPPED ovl;
\r
9508 static int line = 0;
\r
9512 if (appData.noJoin || !appData.useInternalWrap)
\r
9513 return no_wrap(message, count);
\r
9516 int width = get_term_width();
\r
9517 int len = wrap(NULL, message, count, width, &line);
\r
9518 char *msg = malloc(len);
\r
9522 return no_wrap(message, count);
\r
9525 dbgchk = wrap(msg, message, count, width, &line);
\r
9526 if (dbgchk != len && appData.debugMode)
\r
9527 fprintf(debugFP, "wrap(): dbgchk(%d) != len(%d)\n", dbgchk, len);
\r
9528 ConsoleOutput(msg, len, FALSE);
\r
9535 if (ovl.hEvent == NULL) {
\r
9536 ovl.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
\r
9538 ovl.Internal = ovl.InternalHigh = ovl.Offset = ovl.OffsetHigh = 0;
\r
9540 switch (cp->kind) {
\r
9543 outCount = send(cp->sock, message, count, 0);
\r
9544 if (outCount == SOCKET_ERROR) {
\r
9545 *outError = WSAGetLastError();
\r
9547 *outError = NO_ERROR;
\r
9552 if (WriteFile(((ChildProc *)pr)->hTo, message, count,
\r
9553 &dOutCount, NULL)) {
\r
9554 *outError = NO_ERROR;
\r
9555 outCount = (int) dOutCount;
\r
9557 *outError = GetLastError();
\r
9562 *outError = DoWriteFile(((ChildProc *)pr)->hTo, message, count,
\r
9563 &dOutCount, &ovl);
\r
9564 if (*outError == NO_ERROR) {
\r
9565 outCount = (int) dOutCount;
\r
9573 OutputToProcessDelayed(ProcRef pr, char *message, int count, int *outError,
\r
9576 /* Ignore delay, not implemented for WinBoard */
\r
9577 return OutputToProcess(pr, message, count, outError);
\r
9582 CmailSigHandlerCallBack(InputSourceRef isr, VOIDSTAR closure,
\r
9583 char *buf, int count, int error)
\r
9585 DisplayFatalError(_("Not implemented"), 0, 1);
\r
9588 /* see wgamelist.c for Game List functions */
\r
9589 /* see wedittags.c for Edit Tags functions */
\r
9596 char buf[MSG_SIZ];
\r
9599 if (SearchPath(installDir, appData.icsLogon, NULL, MSG_SIZ, buf, &dummy)) {
\r
9600 f = fopen(buf, "r");
\r
9602 ProcessICSInitScript(f);
\r
9610 StartAnalysisClock()
\r
9612 if (analysisTimerEvent) return;
\r
9613 analysisTimerEvent = SetTimer(hwndMain, (UINT) ANALYSIS_TIMER_ID,
\r
9614 (UINT) 2000, NULL);
\r
9618 SetHighlights(int fromX, int fromY, int toX, int toY)
\r
9620 highlightInfo.sq[0].x = fromX;
\r
9621 highlightInfo.sq[0].y = fromY;
\r
9622 highlightInfo.sq[1].x = toX;
\r
9623 highlightInfo.sq[1].y = toY;
\r
9629 highlightInfo.sq[0].x = highlightInfo.sq[0].y =
\r
9630 highlightInfo.sq[1].x = highlightInfo.sq[1].y = -1;
\r
9634 SetPremoveHighlights(int fromX, int fromY, int toX, int toY)
\r
9636 premoveHighlightInfo.sq[0].x = fromX;
\r
9637 premoveHighlightInfo.sq[0].y = fromY;
\r
9638 premoveHighlightInfo.sq[1].x = toX;
\r
9639 premoveHighlightInfo.sq[1].y = toY;
\r
9643 ClearPremoveHighlights()
\r
9645 premoveHighlightInfo.sq[0].x = premoveHighlightInfo.sq[0].y =
\r
9646 premoveHighlightInfo.sq[1].x = premoveHighlightInfo.sq[1].y = -1;
\r
9650 ShutDownFrontEnd()
\r
9652 if (saveSettingsOnExit) SaveSettings(settingsFileName);
\r
9653 DeleteClipboardTempFiles();
\r
9659 if (IsIconic(hwndMain))
\r
9660 ShowWindow(hwndMain, SW_RESTORE);
\r
9662 SetActiveWindow(hwndMain);
\r
9666 * Prototypes for animation support routines
\r
9668 static void ScreenSquare(int column, int row, POINT * pt);
\r
9669 static void Tween( POINT * start, POINT * mid, POINT * finish, int factor,
\r
9670 POINT frames[], int * nFrames);
\r
9676 AnimateAtomicCapture(Board board, int fromX, int fromY, int toX, int toY)
\r
9677 { // [HGM] atomic: animate blast wave
\r
9680 explodeInfo.fromX = fromX;
\r
9681 explodeInfo.fromY = fromY;
\r
9682 explodeInfo.toX = toX;
\r
9683 explodeInfo.toY = toY;
\r
9684 for(i=1; i<4*kFactor; i++) {
\r
9685 explodeInfo.radius = (i*180)/(4*kFactor-1);
\r
9686 DrawPosition(FALSE, board);
\r
9687 Sleep(appData.animSpeed);
\r
9689 explodeInfo.radius = 0;
\r
9690 DrawPosition(TRUE, board);
\r
9694 AnimateMove(board, fromX, fromY, toX, toY)
\r
9701 ChessSquare piece;
\r
9702 POINT start, finish, mid;
\r
9703 POINT frames[kFactor * 2 + 1];
\r
9706 if (!appData.animate) return;
\r
9707 if (doingSizing) return;
\r
9708 if (fromY < 0 || fromX < 0) return;
\r
9709 piece = board[fromY][fromX];
\r
9710 if (piece >= EmptySquare) return;
\r
9712 ScreenSquare(fromX, fromY, &start);
\r
9713 ScreenSquare(toX, toY, &finish);
\r
9715 /* All moves except knight jumps move in straight line */
\r
9716 if (!(abs(fromX-toX) == 1 && abs(fromY-toY) == 2 || abs(fromX-toX) == 2 && abs(fromY-toY) == 1)) {
\r
9717 mid.x = start.x + (finish.x - start.x) / 2;
\r
9718 mid.y = start.y + (finish.y - start.y) / 2;
\r
9720 /* Knight: make straight movement then diagonal */
\r
9721 if (abs(toY - fromY) < abs(toX - fromX)) {
\r
9722 mid.x = start.x + (finish.x - start.x) / 2;
\r
9726 mid.y = start.y + (finish.y - start.y) / 2;
\r
9730 /* Don't use as many frames for very short moves */
\r
9731 if (abs(toY - fromY) + abs(toX - fromX) <= 2)
\r
9732 Tween(&start, &mid, &finish, kFactor - 1, frames, &nFrames);
\r
9734 Tween(&start, &mid, &finish, kFactor, frames, &nFrames);
\r
9736 animInfo.from.x = fromX;
\r
9737 animInfo.from.y = fromY;
\r
9738 animInfo.to.x = toX;
\r
9739 animInfo.to.y = toY;
\r
9740 animInfo.lastpos = start;
\r
9741 animInfo.piece = piece;
\r
9742 for (n = 0; n < nFrames; n++) {
\r
9743 animInfo.pos = frames[n];
\r
9744 DrawPosition(FALSE, NULL);
\r
9745 animInfo.lastpos = animInfo.pos;
\r
9746 Sleep(appData.animSpeed);
\r
9748 animInfo.pos = finish;
\r
9749 DrawPosition(FALSE, NULL);
\r
9750 animInfo.piece = EmptySquare;
\r
9751 Explode(board, fromX, fromY, toX, toY);
\r
9754 /* Convert board position to corner of screen rect and color */
\r
9757 ScreenSquare(column, row, pt)
\r
9758 int column; int row; POINT * pt;
\r
9761 pt->x = lineGap + ((BOARD_WIDTH-1)-column) * (squareSize + lineGap);
\r
9762 pt->y = lineGap + row * (squareSize + lineGap);
\r
9764 pt->x = lineGap + column * (squareSize + lineGap);
\r
9765 pt->y = lineGap + ((BOARD_HEIGHT-1)-row) * (squareSize + lineGap);
\r
9769 /* Generate a series of frame coords from start->mid->finish.
\r
9770 The movement rate doubles until the half way point is
\r
9771 reached, then halves back down to the final destination,
\r
9772 which gives a nice slow in/out effect. The algorithmn
\r
9773 may seem to generate too many intermediates for short
\r
9774 moves, but remember that the purpose is to attract the
\r
9775 viewers attention to the piece about to be moved and
\r
9776 then to where it ends up. Too few frames would be less
\r
9780 Tween(start, mid, finish, factor, frames, nFrames)
\r
9781 POINT * start; POINT * mid;
\r
9782 POINT * finish; int factor;
\r
9783 POINT frames[]; int * nFrames;
\r
9785 int n, fraction = 1, count = 0;
\r
9787 /* Slow in, stepping 1/16th, then 1/8th, ... */
\r
9788 for (n = 0; n < factor; n++)
\r
9790 for (n = 0; n < factor; n++) {
\r
9791 frames[count].x = start->x + (mid->x - start->x) / fraction;
\r
9792 frames[count].y = start->y + (mid->y - start->y) / fraction;
\r
9794 fraction = fraction / 2;
\r
9798 frames[count] = *mid;
\r
9801 /* Slow out, stepping 1/2, then 1/4, ... */
\r
9803 for (n = 0; n < factor; n++) {
\r
9804 frames[count].x = finish->x - (finish->x - mid->x) / fraction;
\r
9805 frames[count].y = finish->y - (finish->y - mid->y) / fraction;
\r
9807 fraction = fraction * 2;
\r
9813 HistorySet( char movelist[][2*MOVE_LEN], int first, int last, int current )
\r
9815 MoveHistorySet( movelist, first, last, current, pvInfoList );
\r
9817 EvalGraphSet( first, last, current, pvInfoList );
\r
9821 SettingsPopUp(ChessProgramState *cps)
\r
9822 { // [HGM] wrapper needed because handles must not be passed through back-end
\r
9823 EngineOptionsPopup(savedHwnd, cps);
\r
9826 int flock(int fid, int code)
\r
9828 HANDLE hFile = (HANDLE) _get_osfhandle(fid);
\r
9832 ov.OffsetHigh = 0;
\r
9834 case 1: LockFileEx(hFile, LOCKFILE_EXCLUSIVE_LOCK, 0, 1024, 0, &ov); break; // LOCK_SH
\r
9835 case 2: LockFileEx(hFile, LOCKFILE_EXCLUSIVE_LOCK, 0, 1024, 0, &ov); break; // LOCK_EX
\r
9836 case 3: UnlockFileEx(hFile, 0, 1024, 0, &ov); break; // LOCK_UN
\r
9837 default: return -1;
\r