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_EditComment:
\r
4963 if (commentUp && editComment) {
\r
4966 EditCommentEvent();
\r
4986 case IDM_CallFlag:
\r
5006 case IDM_StopObserving:
\r
5007 StopObservingEvent();
\r
5010 case IDM_StopExamining:
\r
5011 StopExaminingEvent();
\r
5015 UploadGameEvent();
\r
5018 case IDM_TypeInMove:
\r
5019 TypeInEvent('\000');
\r
5022 case IDM_TypeInName:
\r
5023 PopUpNameDialog('\000');
\r
5026 case IDM_Backward:
\r
5028 SetFocus(hwndMain);
\r
5035 SetFocus(hwndMain);
\r
5040 SetFocus(hwndMain);
\r
5045 SetFocus(hwndMain);
\r
5049 RevertEvent(FALSE);
\r
5052 case IDM_Annotate: // [HGM] vari: revert with annotation
\r
5053 RevertEvent(TRUE);
\r
5056 case IDM_TruncateGame:
\r
5057 TruncateGameEvent();
\r
5064 case IDM_RetractMove:
\r
5065 RetractMoveEvent();
\r
5068 case IDM_FlipView:
\r
5069 flipView = !flipView;
\r
5070 DrawPosition(FALSE, NULL);
\r
5073 case IDM_FlipClock:
\r
5074 flipClock = !flipClock;
\r
5075 DisplayBothClocks();
\r
5079 case IDM_MuteSounds:
\r
5080 mute = !mute; // [HGM] mute: keep track of global muting variable
\r
5081 CheckMenuItem(GetMenu(hwndMain),IDM_MuteSounds,
\r
5082 MF_BYCOMMAND|(mute?MF_CHECKED:MF_UNCHECKED));
\r
5085 case IDM_GeneralOptions:
\r
5086 GeneralOptionsPopup(hwnd);
\r
5087 DrawPosition(TRUE, NULL);
\r
5090 case IDM_BoardOptions:
\r
5091 BoardOptionsPopup(hwnd);
\r
5094 case IDM_EnginePlayOptions:
\r
5095 EnginePlayOptionsPopup(hwnd);
\r
5098 case IDM_Engine1Options:
\r
5099 EngineOptionsPopup(hwnd, &first);
\r
5102 case IDM_Engine2Options:
\r
5104 if(WaitForEngine(&second, SettingsMenuIfReady)) break;
\r
5105 EngineOptionsPopup(hwnd, &second);
\r
5108 case IDM_OptionsUCI:
\r
5109 UciOptionsPopup(hwnd);
\r
5113 TourneyPopup(hwnd);
\r
5116 case IDM_IcsOptions:
\r
5117 IcsOptionsPopup(hwnd);
\r
5121 FontsOptionsPopup(hwnd);
\r
5125 SoundOptionsPopup(hwnd);
\r
5128 case IDM_CommPort:
\r
5129 CommPortOptionsPopup(hwnd);
\r
5132 case IDM_LoadOptions:
\r
5133 LoadOptionsPopup(hwnd);
\r
5136 case IDM_SaveOptions:
\r
5137 SaveOptionsPopup(hwnd);
\r
5140 case IDM_TimeControl:
\r
5141 TimeControlOptionsPopup(hwnd);
\r
5144 case IDM_SaveSettings:
\r
5145 SaveSettings(settingsFileName);
\r
5148 case IDM_SaveSettingsOnExit:
\r
5149 saveSettingsOnExit = !saveSettingsOnExit;
\r
5150 (void) CheckMenuItem(GetMenu(hwndMain), IDM_SaveSettingsOnExit,
\r
5151 MF_BYCOMMAND|(saveSettingsOnExit ?
\r
5152 MF_CHECKED : MF_UNCHECKED));
\r
5163 case IDM_AboutGame:
\r
5168 appData.debugMode = !appData.debugMode;
\r
5169 if (appData.debugMode) {
\r
5170 char dir[MSG_SIZ];
\r
5171 GetCurrentDirectory(MSG_SIZ, dir);
\r
5172 SetCurrentDirectory(installDir);
\r
5173 debugFP = fopen(appData.nameOfDebugFile, "w");
\r
5174 SetCurrentDirectory(dir);
\r
5175 setbuf(debugFP, NULL);
\r
5182 case IDM_HELPCONTENTS:
\r
5183 if (!MyHelp (hwnd, "winboard.hlp", HELP_KEY,(DWORD)(LPSTR)"CONTENTS") &&
\r
5184 !HtmlHelp(hwnd, "winboard.chm", 0, 0) ) {
\r
5185 MessageBox (GetFocus(),
\r
5186 _("Unable to activate help"),
\r
5187 szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);
\r
5191 case IDM_HELPSEARCH:
\r
5192 if (!MyHelp (hwnd, "winboard.hlp", HELP_PARTIALKEY, (DWORD)(LPSTR)"") &&
\r
5193 !HtmlHelp(hwnd, "winboard.chm", 0, 0) ) {
\r
5194 MessageBox (GetFocus(),
\r
5195 _("Unable to activate help"),
\r
5196 szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);
\r
5200 case IDM_HELPHELP:
\r
5201 if(!WinHelp(hwnd, (LPSTR)NULL, HELP_HELPONHELP, 0)) {
\r
5202 MessageBox (GetFocus(),
\r
5203 _("Unable to activate help"),
\r
5204 szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);
\r
5209 lpProc = MakeProcInstance((FARPROC)About, hInst);
\r
5211 (gameInfo.event && strcmp(gameInfo.event, "Easter Egg Hunt") == 0) ?
\r
5212 "AboutBox2" : "AboutBox", hwnd, (DLGPROC)lpProc);
\r
5213 FreeProcInstance(lpProc);
\r
5216 case IDM_DirectCommand1:
\r
5217 AskQuestionEvent(_("Direct Command"),
\r
5218 _("Send to chess program:"), "", "1");
\r
5220 case IDM_DirectCommand2:
\r
5221 AskQuestionEvent(_("Direct Command"),
\r
5222 _("Send to second chess program:"), "", "2");
\r
5225 case EP_WhitePawn:
\r
5226 EditPositionMenuEvent(WhitePawn, fromX, fromY);
\r
5227 fromX = fromY = -1;
\r
5230 case EP_WhiteKnight:
\r
5231 EditPositionMenuEvent(WhiteKnight, fromX, fromY);
\r
5232 fromX = fromY = -1;
\r
5235 case EP_WhiteBishop:
\r
5236 EditPositionMenuEvent(WhiteBishop, fromX, fromY);
\r
5237 fromX = fromY = -1;
\r
5240 case EP_WhiteRook:
\r
5241 EditPositionMenuEvent(WhiteRook, fromX, fromY);
\r
5242 fromX = fromY = -1;
\r
5245 case EP_WhiteQueen:
\r
5246 EditPositionMenuEvent(WhiteQueen, fromX, fromY);
\r
5247 fromX = fromY = -1;
\r
5250 case EP_WhiteFerz:
\r
5251 EditPositionMenuEvent(WhiteFerz, fromX, fromY);
\r
5252 fromX = fromY = -1;
\r
5255 case EP_WhiteWazir:
\r
5256 EditPositionMenuEvent(WhiteWazir, fromX, fromY);
\r
5257 fromX = fromY = -1;
\r
5260 case EP_WhiteAlfil:
\r
5261 EditPositionMenuEvent(WhiteAlfil, fromX, fromY);
\r
5262 fromX = fromY = -1;
\r
5265 case EP_WhiteCannon:
\r
5266 EditPositionMenuEvent(WhiteCannon, fromX, fromY);
\r
5267 fromX = fromY = -1;
\r
5270 case EP_WhiteCardinal:
\r
5271 EditPositionMenuEvent(WhiteAngel, fromX, fromY);
\r
5272 fromX = fromY = -1;
\r
5275 case EP_WhiteMarshall:
\r
5276 EditPositionMenuEvent(WhiteMarshall, fromX, fromY);
\r
5277 fromX = fromY = -1;
\r
5280 case EP_WhiteKing:
\r
5281 EditPositionMenuEvent(WhiteKing, fromX, fromY);
\r
5282 fromX = fromY = -1;
\r
5285 case EP_BlackPawn:
\r
5286 EditPositionMenuEvent(BlackPawn, fromX, fromY);
\r
5287 fromX = fromY = -1;
\r
5290 case EP_BlackKnight:
\r
5291 EditPositionMenuEvent(BlackKnight, fromX, fromY);
\r
5292 fromX = fromY = -1;
\r
5295 case EP_BlackBishop:
\r
5296 EditPositionMenuEvent(BlackBishop, fromX, fromY);
\r
5297 fromX = fromY = -1;
\r
5300 case EP_BlackRook:
\r
5301 EditPositionMenuEvent(BlackRook, fromX, fromY);
\r
5302 fromX = fromY = -1;
\r
5305 case EP_BlackQueen:
\r
5306 EditPositionMenuEvent(BlackQueen, fromX, fromY);
\r
5307 fromX = fromY = -1;
\r
5310 case EP_BlackFerz:
\r
5311 EditPositionMenuEvent(BlackFerz, fromX, fromY);
\r
5312 fromX = fromY = -1;
\r
5315 case EP_BlackWazir:
\r
5316 EditPositionMenuEvent(BlackWazir, fromX, fromY);
\r
5317 fromX = fromY = -1;
\r
5320 case EP_BlackAlfil:
\r
5321 EditPositionMenuEvent(BlackAlfil, fromX, fromY);
\r
5322 fromX = fromY = -1;
\r
5325 case EP_BlackCannon:
\r
5326 EditPositionMenuEvent(BlackCannon, fromX, fromY);
\r
5327 fromX = fromY = -1;
\r
5330 case EP_BlackCardinal:
\r
5331 EditPositionMenuEvent(BlackAngel, fromX, fromY);
\r
5332 fromX = fromY = -1;
\r
5335 case EP_BlackMarshall:
\r
5336 EditPositionMenuEvent(BlackMarshall, fromX, fromY);
\r
5337 fromX = fromY = -1;
\r
5340 case EP_BlackKing:
\r
5341 EditPositionMenuEvent(BlackKing, fromX, fromY);
\r
5342 fromX = fromY = -1;
\r
5345 case EP_EmptySquare:
\r
5346 EditPositionMenuEvent(EmptySquare, fromX, fromY);
\r
5347 fromX = fromY = -1;
\r
5350 case EP_ClearBoard:
\r
5351 EditPositionMenuEvent(ClearBoard, fromX, fromY);
\r
5352 fromX = fromY = -1;
\r
5356 EditPositionMenuEvent(WhitePlay, fromX, fromY);
\r
5357 fromX = fromY = -1;
\r
5361 EditPositionMenuEvent(BlackPlay, fromX, fromY);
\r
5362 fromX = fromY = -1;
\r
5366 EditPositionMenuEvent(PromotePiece, fromX, fromY);
\r
5367 fromX = fromY = -1;
\r
5371 EditPositionMenuEvent(DemotePiece, fromX, fromY);
\r
5372 fromX = fromY = -1;
\r
5376 DropMenuEvent(WhitePawn, fromX, fromY);
\r
5377 fromX = fromY = -1;
\r
5381 DropMenuEvent(WhiteKnight, fromX, fromY);
\r
5382 fromX = fromY = -1;
\r
5386 DropMenuEvent(WhiteBishop, fromX, fromY);
\r
5387 fromX = fromY = -1;
\r
5391 DropMenuEvent(WhiteRook, fromX, fromY);
\r
5392 fromX = fromY = -1;
\r
5396 DropMenuEvent(WhiteQueen, fromX, fromY);
\r
5397 fromX = fromY = -1;
\r
5401 barbaric = 0; appData.language = "";
\r
5402 TranslateMenus(0);
\r
5403 CheckMenuItem(GetMenu(hwndMain), lastChecked, MF_BYCOMMAND|MF_UNCHECKED);
\r
5404 CheckMenuItem(GetMenu(hwndMain), IDM_English, MF_BYCOMMAND|MF_CHECKED);
\r
5405 lastChecked = wmId;
\r
5409 if(wmId > IDM_English && wmId < IDM_English+20) {
\r
5410 LoadLanguageFile(languageFile[wmId - IDM_English - 1]);
\r
5411 TranslateMenus(0);
\r
5412 CheckMenuItem(GetMenu(hwndMain), lastChecked, MF_BYCOMMAND|MF_UNCHECKED);
\r
5413 CheckMenuItem(GetMenu(hwndMain), wmId, MF_BYCOMMAND|MF_CHECKED);
\r
5414 lastChecked = wmId;
\r
5417 return (DefWindowProc(hwnd, message, wParam, lParam));
\r
5423 case CLOCK_TIMER_ID:
\r
5424 KillTimer(hwnd, clockTimerEvent); /* Simulate one-shot timer as in X */
\r
5425 clockTimerEvent = 0;
\r
5426 DecrementClocks(); /* call into back end */
\r
5428 case LOAD_GAME_TIMER_ID:
\r
5429 KillTimer(hwnd, loadGameTimerEvent); /* Simulate one-shot timer as in X*/
\r
5430 loadGameTimerEvent = 0;
\r
5431 AutoPlayGameLoop(); /* call into back end */
\r
5433 case ANALYSIS_TIMER_ID:
\r
5434 if ((gameMode == AnalyzeMode || gameMode == AnalyzeFile
\r
5435 || appData.icsEngineAnalyze) && appData.periodicUpdates) {
\r
5436 AnalysisPeriodicEvent(0);
\r
5438 KillTimer(hwnd, analysisTimerEvent);
\r
5439 analysisTimerEvent = 0;
\r
5442 case DELAYED_TIMER_ID:
\r
5443 KillTimer(hwnd, delayedTimerEvent);
\r
5444 delayedTimerEvent = 0;
\r
5445 delayedTimerCallback();
\r
5450 case WM_USER_Input:
\r
5451 InputEvent(hwnd, message, wParam, lParam);
\r
5454 /* [AS] Also move "attached" child windows */
\r
5455 case WM_WINDOWPOSCHANGING:
\r
5457 if( hwnd == hwndMain && appData.useStickyWindows ) {
\r
5458 LPWINDOWPOS lpwp = (LPWINDOWPOS) lParam;
\r
5460 if( ((lpwp->flags & SWP_NOMOVE) == 0) && ((lpwp->flags & SWP_NOSIZE) != 0) ) {
\r
5461 /* Window is moving */
\r
5464 // GetWindowRect( hwnd, &rcMain ); //[HGM] sticky: in XP this returned new position, not old
\r
5465 rcMain.left = wpMain.x; // replace by these 4 lines to reconstruct old rect
\r
5466 rcMain.right = wpMain.x + wpMain.width;
\r
5467 rcMain.top = wpMain.y;
\r
5468 rcMain.bottom = wpMain.y + wpMain.height;
\r
5470 ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, moveHistoryDialog, &wpMoveHistory );
\r
5471 ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, evalGraphDialog, &wpEvalGraph );
\r
5472 ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, engineOutputDialog, &wpEngineOutput );
\r
5473 ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, gameListDialog, &wpGameList );
\r
5474 ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, hwndConsole, &wpConsole );
\r
5475 wpMain.x = lpwp->x;
\r
5476 wpMain.y = lpwp->y;
\r
5481 /* [AS] Snapping */
\r
5482 case WM_ENTERSIZEMOVE:
\r
5483 if(appData.debugMode) { fprintf(debugFP, "size-move\n"); }
\r
5484 if (hwnd == hwndMain) {
\r
5485 doingSizing = TRUE;
\r
5488 return OnEnterSizeMove( &sd, hwnd, wParam, lParam );
\r
5492 if(appData.debugMode) { fprintf(debugFP, "sizing\n"); }
\r
5493 if (hwnd == hwndMain) {
\r
5494 lastSizing = wParam;
\r
5499 if(appData.debugMode) { fprintf(debugFP, "moving\n"); }
\r
5500 return OnMoving( &sd, hwnd, wParam, lParam );
\r
5502 case WM_EXITSIZEMOVE:
\r
5503 if(appData.debugMode) { fprintf(debugFP, "exit size-move, size = %d\n", squareSize); }
\r
5504 if (hwnd == hwndMain) {
\r
5506 doingSizing = FALSE;
\r
5507 InvalidateRect(hwnd, &boardRect, FALSE);
\r
5508 GetClientRect(hwnd, &client);
\r
5509 ResizeBoard(client.right, client.bottom, lastSizing);
\r
5511 if(appData.debugMode) { fprintf(debugFP, "square size = %d\n", squareSize); }
\r
5513 return OnExitSizeMove( &sd, hwnd, wParam, lParam );
\r
5516 case WM_DESTROY: /* message: window being destroyed */
\r
5517 PostQuitMessage(0);
\r
5521 if (hwnd == hwndMain) {
\r
5526 default: /* Passes it on if unprocessed */
\r
5527 return (DefWindowProc(hwnd, message, wParam, lParam));
\r
5532 /*---------------------------------------------------------------------------*\
\r
5534 * Misc utility routines
\r
5536 \*---------------------------------------------------------------------------*/
\r
5539 * Decent random number generator, at least not as bad as Windows
\r
5540 * standard rand, which returns a value in the range 0 to 0x7fff.
\r
5542 unsigned int randstate;
\r
5547 randstate = randstate * 1664525 + 1013904223;
\r
5548 return (int) randstate & 0x7fffffff;
\r
5552 mysrandom(unsigned int seed)
\r
5559 * returns TRUE if user selects a different color, FALSE otherwise
\r
5563 ChangeColor(HWND hwnd, COLORREF *which)
\r
5565 static BOOL firstTime = TRUE;
\r
5566 static DWORD customColors[16];
\r
5568 COLORREF newcolor;
\r
5573 /* Make initial colors in use available as custom colors */
\r
5574 /* Should we put the compiled-in defaults here instead? */
\r
5576 customColors[i++] = lightSquareColor & 0xffffff;
\r
5577 customColors[i++] = darkSquareColor & 0xffffff;
\r
5578 customColors[i++] = whitePieceColor & 0xffffff;
\r
5579 customColors[i++] = blackPieceColor & 0xffffff;
\r
5580 customColors[i++] = highlightSquareColor & 0xffffff;
\r
5581 customColors[i++] = premoveHighlightColor & 0xffffff;
\r
5583 for (ccl = (ColorClass) 0; ccl < NColorClasses && i < 16; ccl++) {
\r
5584 customColors[i++] = textAttribs[ccl].color;
\r
5586 while (i < 16) customColors[i++] = RGB(255, 255, 255);
\r
5587 firstTime = FALSE;
\r
5590 cc.lStructSize = sizeof(cc);
\r
5591 cc.hwndOwner = hwnd;
\r
5592 cc.hInstance = NULL;
\r
5593 cc.rgbResult = (DWORD) (*which & 0xffffff);
\r
5594 cc.lpCustColors = (LPDWORD) customColors;
\r
5595 cc.Flags = CC_RGBINIT|CC_FULLOPEN;
\r
5597 if (!ChooseColor(&cc)) return FALSE;
\r
5599 newcolor = (COLORREF) (0x2000000 | cc.rgbResult);
\r
5600 if (newcolor == *which) return FALSE;
\r
5601 *which = newcolor;
\r
5605 InitDrawingColors();
\r
5606 InvalidateRect(hwnd, &boardRect, FALSE);
\r
5611 MyLoadSound(MySound *ms)
\r
5617 if (ms->data) free(ms->data);
\r
5620 switch (ms->name[0]) {
\r
5626 /* System sound from Control Panel. Don't preload here. */
\r
5630 if (ms->name[1] == NULLCHAR) {
\r
5631 /* "!" alone = silence */
\r
5634 /* Builtin wave resource. Error if not found. */
\r
5635 HANDLE h = FindResource(hInst, ms->name + 1, "WAVE");
\r
5636 if (h == NULL) break;
\r
5637 ms->data = (void *)LoadResource(hInst, h);
\r
5638 if (h == NULL) break;
\r
5643 /* .wav file. Error if not found. */
\r
5644 f = fopen(ms->name, "rb");
\r
5645 if (f == NULL) break;
\r
5646 if (fstat(fileno(f), &st) < 0) break;
\r
5647 ms->data = malloc(st.st_size);
\r
5648 if (fread(ms->data, st.st_size, 1, f) < 1) break;
\r
5654 char buf[MSG_SIZ];
\r
5655 snprintf(buf, MSG_SIZ, _("Error loading sound %s"), ms->name);
\r
5656 DisplayError(buf, GetLastError());
\r
5662 MyPlaySound(MySound *ms)
\r
5664 BOOLEAN ok = FALSE;
\r
5666 if(mute) return TRUE; // [HGM] mute: suppress all sound play when muted
\r
5667 switch (ms->name[0]) {
\r
5669 if(appData.debugMode) fprintf(debugFP, "silence\n");
\r
5674 /* System sound from Control Panel (deprecated feature).
\r
5675 "$" alone or an unset sound name gets default beep (still in use). */
\r
5676 if (ms->name[1]) {
\r
5677 ok = PlaySound(ms->name + 1, NULL, SND_ALIAS|SND_ASYNC);
\r
5679 if (!ok) ok = MessageBeep(MB_OK);
\r
5682 /* Builtin wave resource, or "!" alone for silence */
\r
5683 if (ms->name[1]) {
\r
5684 if (ms->data == NULL) return FALSE;
\r
5685 ok = PlaySound(ms->data, NULL, SND_MEMORY|SND_ASYNC);
\r
5691 /* .wav file. Error if not found. */
\r
5692 if (ms->data == NULL) return FALSE;
\r
5693 ok = PlaySound(ms->data, NULL, SND_MEMORY|SND_ASYNC);
\r
5696 /* Don't print an error: this can happen innocently if the sound driver
\r
5697 is busy; for instance, if another instance of WinBoard is playing
\r
5698 a sound at about the same time. */
\r
5704 OldOpenFileHook(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
5707 OPENFILENAME *ofn;
\r
5708 static UINT *number; /* gross that this is static */
\r
5710 switch (message) {
\r
5711 case WM_INITDIALOG: /* message: initialize dialog box */
\r
5712 /* Center the dialog over the application window */
\r
5713 ofn = (OPENFILENAME *) lParam;
\r
5714 if (ofn->Flags & OFN_ENABLETEMPLATE) {
\r
5715 number = (UINT *) ofn->lCustData;
\r
5716 SendMessage(GetDlgItem(hDlg, edt2), WM_SETTEXT, 0, (LPARAM) "");
\r
5720 CenterWindow (hDlg, GetWindow (hDlg, GW_OWNER));
\r
5721 Translate(hDlg, 1536);
\r
5722 return FALSE; /* Allow for further processing */
\r
5725 if ((LOWORD(wParam) == IDOK) && (number != NULL)) {
\r
5726 *number = GetDlgItemInt(hDlg, OPT_IndexNumberOld, &ok, FALSE);
\r
5728 return FALSE; /* Allow for further processing */
\r
5734 OpenFileHook(HWND hdlg, UINT uiMsg, WPARAM wParam, LPARAM lParam)
\r
5736 static UINT *number;
\r
5737 OPENFILENAME *ofname;
\r
5740 case WM_INITDIALOG:
\r
5741 Translate(hdlg, DLG_IndexNumber);
\r
5742 ofname = (OPENFILENAME *)lParam;
\r
5743 number = (UINT *)(ofname->lCustData);
\r
5746 ofnot = (OFNOTIFY *)lParam;
\r
5747 if (ofnot->hdr.code == CDN_FILEOK) {
\r
5748 *number = GetDlgItemInt(hdlg, OPT_IndexNumber, NULL, FALSE);
\r
5757 OpenFileDialog(HWND hwnd, char *write, char *defName, char *defExt, // [HGM] diag: type of 'write' now string
\r
5758 char *nameFilt, char *dlgTitle, UINT *number,
\r
5759 char fileTitle[MSG_SIZ], char fileName[MSG_SIZ])
\r
5761 OPENFILENAME openFileName;
\r
5762 char buf1[MSG_SIZ];
\r
5765 if (fileName == NULL) fileName = buf1;
\r
5766 if (defName == NULL) {
\r
5767 safeStrCpy(fileName, "*.", 3 );
\r
5768 strcat(fileName, defExt);
\r
5770 safeStrCpy(fileName, defName, MSG_SIZ );
\r
5772 if (fileTitle) safeStrCpy(fileTitle, "", MSG_SIZ );
\r
5773 if (number) *number = 0;
\r
5775 openFileName.lStructSize = sizeof(OPENFILENAME);
\r
5776 openFileName.hwndOwner = hwnd;
\r
5777 openFileName.hInstance = (HANDLE) hInst;
\r
5778 openFileName.lpstrFilter = nameFilt;
\r
5779 openFileName.lpstrCustomFilter = (LPSTR) NULL;
\r
5780 openFileName.nMaxCustFilter = 0L;
\r
5781 openFileName.nFilterIndex = 1L;
\r
5782 openFileName.lpstrFile = fileName;
\r
5783 openFileName.nMaxFile = MSG_SIZ;
\r
5784 openFileName.lpstrFileTitle = fileTitle;
\r
5785 openFileName.nMaxFileTitle = fileTitle ? MSG_SIZ : 0;
\r
5786 openFileName.lpstrInitialDir = NULL;
\r
5787 openFileName.lpstrTitle = dlgTitle;
\r
5788 openFileName.Flags = OFN_PATHMUSTEXIST | OFN_HIDEREADONLY
\r
5789 | (write[0] != 'r' ? 0 : OFN_FILEMUSTEXIST)
\r
5790 | (number ? OFN_ENABLETEMPLATE | OFN_ENABLEHOOK: 0)
\r
5791 | (oldDialog ? 0 : OFN_EXPLORER);
\r
5792 openFileName.nFileOffset = 0;
\r
5793 openFileName.nFileExtension = 0;
\r
5794 openFileName.lpstrDefExt = defExt;
\r
5795 openFileName.lCustData = (LONG) number;
\r
5796 openFileName.lpfnHook = oldDialog ?
\r
5797 (LPOFNHOOKPROC) OldOpenFileHook : (LPOFNHOOKPROC) OpenFileHook;
\r
5798 openFileName.lpTemplateName = (LPSTR)(oldDialog ? 1536 : DLG_IndexNumber);
\r
5800 if (write[0] != 'r' ? GetSaveFileName(&openFileName) :
\r
5801 GetOpenFileName(&openFileName)) {
\r
5802 /* open the file */
\r
5803 f = fopen(openFileName.lpstrFile, write);
\r
5805 MessageBox(hwnd, _("File open failed"), NULL,
\r
5806 MB_OK|MB_ICONEXCLAMATION);
\r
5810 int err = CommDlgExtendedError();
\r
5811 if (err != 0) DisplayError(_("Internal error in file dialog box"), err);
\r
5820 MenuPopup(HWND hwnd, POINT pt, HMENU hmenu, UINT def)
\r
5822 HMENU hmenuTrackPopup; /* floating pop-up menu */
\r
5825 * Get the first pop-up menu in the menu template. This is the
\r
5826 * menu that TrackPopupMenu displays.
\r
5828 hmenuTrackPopup = GetSubMenu(hmenu, 0);
\r
5829 TranslateOneMenu(10, hmenuTrackPopup);
\r
5831 SetMenuDefaultItem(hmenuTrackPopup, def, FALSE);
\r
5834 * TrackPopup uses screen coordinates, so convert the
\r
5835 * coordinates of the mouse click to screen coordinates.
\r
5837 ClientToScreen(hwnd, (LPPOINT) &pt);
\r
5839 /* Draw and track the floating pop-up menu. */
\r
5840 TrackPopupMenu(hmenuTrackPopup, TPM_CENTERALIGN | TPM_RIGHTBUTTON,
\r
5841 pt.x, pt.y, 0, hwnd, NULL);
\r
5843 /* Destroy the menu.*/
\r
5844 DestroyMenu(hmenu);
\r
5849 int sizeX, sizeY, newSizeX, newSizeY;
\r
5851 } ResizeEditPlusButtonsClosure;
\r
5854 ResizeEditPlusButtonsCallback(HWND hChild, LPARAM lparam)
\r
5856 ResizeEditPlusButtonsClosure *cl = (ResizeEditPlusButtonsClosure *)lparam;
\r
5860 if (hChild == cl->hText) return TRUE;
\r
5861 GetWindowRect(hChild, &rect); /* gives screen coords */
\r
5862 pt.x = rect.left + (cl->newSizeX - cl->sizeX)/2;
\r
5863 pt.y = rect.top + cl->newSizeY - cl->sizeY;
\r
5864 ScreenToClient(cl->hDlg, &pt);
\r
5865 cl->hdwp = DeferWindowPos(cl->hdwp, hChild, NULL,
\r
5866 pt.x, pt.y, rect.right - rect.left, rect.bottom - rect.top, SWP_NOZORDER);
\r
5870 /* Resize a dialog that has a (rich) edit field filling most of
\r
5871 the top, with a row of buttons below */
\r
5873 ResizeEditPlusButtons(HWND hDlg, HWND hText, int sizeX, int sizeY, int newSizeX, int newSizeY)
\r
5876 int newTextHeight, newTextWidth;
\r
5877 ResizeEditPlusButtonsClosure cl;
\r
5879 /*if (IsIconic(hDlg)) return;*/
\r
5880 if (newSizeX == sizeX && newSizeY == sizeY) return;
\r
5882 cl.hdwp = BeginDeferWindowPos(8);
\r
5884 GetWindowRect(hText, &rectText); /* gives screen coords */
\r
5885 newTextWidth = rectText.right - rectText.left + newSizeX - sizeX;
\r
5886 newTextHeight = rectText.bottom - rectText.top + newSizeY - sizeY;
\r
5887 if (newTextHeight < 0) {
\r
5888 newSizeY += -newTextHeight;
\r
5889 newTextHeight = 0;
\r
5891 cl.hdwp = DeferWindowPos(cl.hdwp, hText, NULL, 0, 0,
\r
5892 newTextWidth, newTextHeight, SWP_NOZORDER|SWP_NOMOVE);
\r
5898 cl.newSizeX = newSizeX;
\r
5899 cl.newSizeY = newSizeY;
\r
5900 EnumChildWindows(hDlg, ResizeEditPlusButtonsCallback, (LPARAM)&cl);
\r
5902 EndDeferWindowPos(cl.hdwp);
\r
5905 BOOL CenterWindowEx(HWND hwndChild, HWND hwndParent, int mode)
\r
5907 RECT rChild, rParent;
\r
5908 int wChild, hChild, wParent, hParent;
\r
5909 int wScreen, hScreen, xNew, yNew;
\r
5912 /* Get the Height and Width of the child window */
\r
5913 GetWindowRect (hwndChild, &rChild);
\r
5914 wChild = rChild.right - rChild.left;
\r
5915 hChild = rChild.bottom - rChild.top;
\r
5917 /* Get the Height and Width of the parent window */
\r
5918 GetWindowRect (hwndParent, &rParent);
\r
5919 wParent = rParent.right - rParent.left;
\r
5920 hParent = rParent.bottom - rParent.top;
\r
5922 /* Get the display limits */
\r
5923 hdc = GetDC (hwndChild);
\r
5924 wScreen = GetDeviceCaps (hdc, HORZRES);
\r
5925 hScreen = GetDeviceCaps (hdc, VERTRES);
\r
5926 ReleaseDC(hwndChild, hdc);
\r
5928 /* Calculate new X position, then adjust for screen */
\r
5929 xNew = rParent.left + ((wParent - wChild) /2);
\r
5932 } else if ((xNew+wChild) > wScreen) {
\r
5933 xNew = wScreen - wChild;
\r
5936 /* Calculate new Y position, then adjust for screen */
\r
5938 yNew = rParent.top + ((hParent - hChild) /2);
\r
5941 yNew = rParent.top + GetSystemMetrics( SM_CYCAPTION ) * 2 / 3;
\r
5946 } else if ((yNew+hChild) > hScreen) {
\r
5947 yNew = hScreen - hChild;
\r
5950 /* Set it, and return */
\r
5951 return SetWindowPos (hwndChild, NULL,
\r
5952 xNew, yNew, 0, 0, SWP_NOSIZE | SWP_NOZORDER);
\r
5955 /* Center one window over another */
\r
5956 BOOL CenterWindow (HWND hwndChild, HWND hwndParent)
\r
5958 return CenterWindowEx( hwndChild, hwndParent, 0 );
\r
5961 /*---------------------------------------------------------------------------*\
\r
5963 * Startup Dialog functions
\r
5965 \*---------------------------------------------------------------------------*/
\r
5967 InitComboStrings(HANDLE hwndCombo, char **cd)
\r
5969 SendMessage(hwndCombo, CB_RESETCONTENT, 0, 0);
\r
5971 while (*cd != NULL) {
\r
5972 SendMessage(hwndCombo, CB_ADDSTRING, 0, (LPARAM) T_(*cd));
\r
5978 InitComboStringsFromOption(HANDLE hwndCombo, char *str)
\r
5980 char buf1[MAX_ARG_LEN];
\r
5983 if (str[0] == '@') {
\r
5984 FILE* f = fopen(str + 1, "r");
\r
5986 DisplayFatalError(str + 1, errno, 2);
\r
5989 len = fread(buf1, 1, sizeof(buf1)-1, f);
\r
5991 buf1[len] = NULLCHAR;
\r
5995 SendMessage(hwndCombo, CB_RESETCONTENT, 0, 0);
\r
5998 char buf[MSG_SIZ];
\r
5999 char *end = strchr(str, '\n');
\r
6000 if (end == NULL) return;
\r
6001 memcpy(buf, str, end - str);
\r
6002 buf[end - str] = NULLCHAR;
\r
6003 SendMessage(hwndCombo, CB_ADDSTRING, 0, (LPARAM) buf);
\r
6009 SetStartupDialogEnables(HWND hDlg)
\r
6011 EnableWindow(GetDlgItem(hDlg, OPT_ChessEngineName),
\r
6012 IsDlgButtonChecked(hDlg, OPT_ChessEngine) ||
\r
6013 (appData.zippyPlay && IsDlgButtonChecked(hDlg, OPT_ChessServer)));
\r
6014 EnableWindow(GetDlgItem(hDlg, OPT_SecondChessEngineName),
\r
6015 IsDlgButtonChecked(hDlg, OPT_ChessEngine));
\r
6016 EnableWindow(GetDlgItem(hDlg, OPT_ChessServerName),
\r
6017 IsDlgButtonChecked(hDlg, OPT_ChessServer));
\r
6018 EnableWindow(GetDlgItem(hDlg, OPT_AdditionalOptions),
\r
6019 IsDlgButtonChecked(hDlg, OPT_AnyAdditional));
\r
6020 EnableWindow(GetDlgItem(hDlg, IDOK),
\r
6021 IsDlgButtonChecked(hDlg, OPT_ChessEngine) ||
\r
6022 IsDlgButtonChecked(hDlg, OPT_ChessServer) ||
\r
6023 IsDlgButtonChecked(hDlg, OPT_View));
\r
6027 QuoteForFilename(char *filename)
\r
6029 int dquote, space;
\r
6030 dquote = strchr(filename, '"') != NULL;
\r
6031 space = strchr(filename, ' ') != NULL;
\r
6032 if (dquote || space) {
\r
6044 InitEngineBox(HWND hDlg, HWND hwndCombo, char* nthcp, char* nthd, char* nthdir, char *nthnames)
\r
6046 char buf[MSG_SIZ];
\r
6049 InitComboStringsFromOption(hwndCombo, nthnames);
\r
6050 q = QuoteForFilename(nthcp);
\r
6051 snprintf(buf, MSG_SIZ, "%s%s%s", q, nthcp, q);
\r
6052 if (*nthdir != NULLCHAR) {
\r
6053 q = QuoteForFilename(nthdir);
\r
6054 snprintf(buf + strlen(buf), MSG_SIZ, " /%s=%s%s%s", nthd, q, nthdir, q);
\r
6056 if (*nthcp == NULLCHAR) {
\r
6057 SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) 0, (LPARAM) 0);
\r
6058 } else if (SendMessage(hwndCombo, CB_SELECTSTRING, (WPARAM) -1, (LPARAM) buf) == CB_ERR) {
\r
6059 SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) -1, (LPARAM) 0);
\r
6060 SendMessage(hwndCombo, WM_SETTEXT, (WPARAM) 0, (LPARAM) buf);
\r
6065 StartupDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
6067 char buf[MSG_SIZ];
\r
6071 switch (message) {
\r
6072 case WM_INITDIALOG:
\r
6073 /* Center the dialog */
\r
6074 CenterWindow (hDlg, GetDesktopWindow());
\r
6075 Translate(hDlg, DLG_Startup);
\r
6076 /* Initialize the dialog items */
\r
6077 InitEngineBox(hDlg, GetDlgItem(hDlg, OPT_ChessEngineName),
\r
6078 appData.firstChessProgram, "fd", appData.firstDirectory,
\r
6079 firstChessProgramNames);
\r
6080 InitEngineBox(hDlg, GetDlgItem(hDlg, OPT_SecondChessEngineName),
\r
6081 appData.secondChessProgram, singleList ? "fd" : "sd", appData.secondDirectory,
\r
6082 singleList ? firstChessProgramNames : secondChessProgramNames); //[HGM] single: use first list in second combo
\r
6083 hwndCombo = GetDlgItem(hDlg, OPT_ChessServerName);
\r
6084 InitComboStringsFromOption(hwndCombo, icsNames);
\r
6085 snprintf(buf, MSG_SIZ, "%s /icsport=%s", appData.icsHost, appData.icsPort);
\r
6086 if (*appData.icsHelper != NULLCHAR) {
\r
6087 char *q = QuoteForFilename(appData.icsHelper);
\r
6088 sprintf(buf + strlen(buf), " /icshelper=%s%s%s", q, appData.icsHelper, q);
\r
6090 if (*appData.icsHost == NULLCHAR) {
\r
6091 SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) 0, (LPARAM) 0);
\r
6092 /*SendMessage(hwndCombo, CB_SHOWDROPDOWN, (WPARAM) TRUE, (LPARAM) 0); !!too soon */
\r
6093 } else if (SendMessage(hwndCombo, CB_SELECTSTRING, (WPARAM) -1, (LPARAM) buf) == CB_ERR) {
\r
6094 SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) -1, (LPARAM) 0);
\r
6095 SendMessage(hwndCombo, WM_SETTEXT, (WPARAM) 0, (LPARAM) buf);
\r
6098 if (appData.icsActive) {
\r
6099 CheckDlgButton(hDlg, OPT_ChessServer, BST_CHECKED);
\r
6101 else if (appData.noChessProgram) {
\r
6102 CheckDlgButton(hDlg, OPT_View, BST_CHECKED);
\r
6105 CheckDlgButton(hDlg, OPT_ChessEngine, BST_CHECKED);
\r
6108 SetStartupDialogEnables(hDlg);
\r
6112 switch (LOWORD(wParam)) {
\r
6114 if (IsDlgButtonChecked(hDlg, OPT_ChessEngine)) {
\r
6115 safeStrCpy(buf, "/fcp=", sizeof(buf)/sizeof(buf[0]) );
\r
6116 GetDlgItemText(hDlg, OPT_ChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));
\r
6118 ParseArgs(StringGet, &p);
\r
6119 safeStrCpy(buf, singleList ? "/fcp=" : "/scp=", sizeof(buf)/sizeof(buf[0]) );
\r
6120 GetDlgItemText(hDlg, OPT_SecondChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));
\r
6122 SwapEngines(singleList); // temporarily swap first and second, to load a second 'first', ...
\r
6123 ParseArgs(StringGet, &p);
\r
6124 SwapEngines(singleList); // ... and then make it 'second'
\r
6125 appData.noChessProgram = FALSE;
\r
6126 appData.icsActive = FALSE;
\r
6127 } else if (IsDlgButtonChecked(hDlg, OPT_ChessServer)) {
\r
6128 safeStrCpy(buf, "/ics /icshost=", sizeof(buf)/sizeof(buf[0]) );
\r
6129 GetDlgItemText(hDlg, OPT_ChessServerName, buf + strlen(buf), sizeof(buf) - strlen(buf));
\r
6131 ParseArgs(StringGet, &p);
\r
6132 if (appData.zippyPlay) {
\r
6133 safeStrCpy(buf, "/fcp=", sizeof(buf)/sizeof(buf[0]) );
\r
6134 GetDlgItemText(hDlg, OPT_ChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));
\r
6136 ParseArgs(StringGet, &p);
\r
6138 } else if (IsDlgButtonChecked(hDlg, OPT_View)) {
\r
6139 appData.noChessProgram = TRUE;
\r
6140 appData.icsActive = FALSE;
\r
6142 MessageBox(hDlg, _("Choose an option, or cancel to exit"),
\r
6143 _("Option Error"), MB_OK|MB_ICONEXCLAMATION);
\r
6146 if (IsDlgButtonChecked(hDlg, OPT_AnyAdditional)) {
\r
6147 GetDlgItemText(hDlg, OPT_AdditionalOptions, buf, sizeof(buf));
\r
6149 ParseArgs(StringGet, &p);
\r
6151 EndDialog(hDlg, TRUE);
\r
6158 case IDM_HELPCONTENTS:
\r
6159 if (!WinHelp (hDlg, "winboard.hlp", HELP_KEY,(DWORD)(LPSTR)"CONTENTS")) {
\r
6160 MessageBox (GetFocus(),
\r
6161 _("Unable to activate help"),
\r
6162 szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);
\r
6167 SetStartupDialogEnables(hDlg);
\r
6175 /*---------------------------------------------------------------------------*\
\r
6177 * About box dialog functions
\r
6179 \*---------------------------------------------------------------------------*/
\r
6181 /* Process messages for "About" dialog box */
\r
6183 About(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
6185 switch (message) {
\r
6186 case WM_INITDIALOG: /* message: initialize dialog box */
\r
6187 /* Center the dialog over the application window */
\r
6188 CenterWindow (hDlg, GetWindow (hDlg, GW_OWNER));
\r
6189 SetDlgItemText(hDlg, ABOUTBOX_Version, programVersion);
\r
6190 Translate(hDlg, ABOUTBOX);
\r
6194 case WM_COMMAND: /* message: received a command */
\r
6195 if (LOWORD(wParam) == IDOK /* "OK" box selected? */
\r
6196 || LOWORD(wParam) == IDCANCEL) { /* System menu close command? */
\r
6197 EndDialog(hDlg, TRUE); /* Exit the dialog */
\r
6205 /*---------------------------------------------------------------------------*\
\r
6207 * Comment Dialog functions
\r
6209 \*---------------------------------------------------------------------------*/
\r
6212 CommentDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
6214 static HANDLE hwndText = NULL;
\r
6215 int len, newSizeX, newSizeY, flags;
\r
6216 static int sizeX, sizeY;
\r
6221 switch (message) {
\r
6222 case WM_INITDIALOG: /* message: initialize dialog box */
\r
6223 /* Initialize the dialog items */
\r
6224 Translate(hDlg, DLG_EditComment);
\r
6225 hwndText = GetDlgItem(hDlg, OPT_CommentText);
\r
6226 SetDlgItemText(hDlg, OPT_CommentText, commentText);
\r
6227 EnableWindow(GetDlgItem(hDlg, OPT_CancelComment), editComment);
\r
6228 EnableWindow(GetDlgItem(hDlg, OPT_ClearComment), editComment);
\r
6229 EnableWindow(GetDlgItem(hDlg, OPT_EditComment), !editComment);
\r
6230 SendMessage(hwndText, EM_SETREADONLY, !editComment, 0);
\r
6231 SetWindowText(hDlg, commentTitle);
\r
6232 if (editComment) {
\r
6233 SetFocus(hwndText);
\r
6235 SetFocus(GetDlgItem(hDlg, IDOK));
\r
6237 SendMessage(GetDlgItem(hDlg, OPT_CommentText),
\r
6238 WM_SETFONT, (WPARAM)font[boardSize][COMMENT_FONT]->hf,
\r
6239 MAKELPARAM(FALSE, 0));
\r
6240 /* Size and position the dialog */
\r
6241 if (!commentDialog) {
\r
6242 commentDialog = hDlg;
\r
6243 flags = SWP_NOZORDER;
\r
6244 GetClientRect(hDlg, &rect);
\r
6245 sizeX = rect.right;
\r
6246 sizeY = rect.bottom;
\r
6247 if (wpComment.x != CW_USEDEFAULT && wpComment.y != CW_USEDEFAULT &&
\r
6248 wpComment.width != CW_USEDEFAULT && wpComment.height != CW_USEDEFAULT) {
\r
6249 WINDOWPLACEMENT wp;
\r
6250 EnsureOnScreen(&wpComment.x, &wpComment.y, 0, 0);
\r
6251 wp.length = sizeof(WINDOWPLACEMENT);
\r
6253 wp.showCmd = SW_SHOW;
\r
6254 wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;
\r
6255 wp.rcNormalPosition.left = wpComment.x;
\r
6256 wp.rcNormalPosition.right = wpComment.x + wpComment.width;
\r
6257 wp.rcNormalPosition.top = wpComment.y;
\r
6258 wp.rcNormalPosition.bottom = wpComment.y + wpComment.height;
\r
6259 SetWindowPlacement(hDlg, &wp);
\r
6261 GetClientRect(hDlg, &rect);
\r
6262 newSizeX = rect.right;
\r
6263 newSizeY = rect.bottom;
\r
6264 ResizeEditPlusButtons(hDlg, hwndText, sizeX, sizeY,
\r
6265 newSizeX, newSizeY);
\r
6270 SendDlgItemMessage( hDlg, OPT_CommentText, EM_SETEVENTMASK, 0, ENM_MOUSEEVENTS | ENM_KEYEVENTS );
\r
6273 case WM_COMMAND: /* message: received a command */
\r
6274 switch (LOWORD(wParam)) {
\r
6276 if (editComment) {
\r
6278 /* Read changed options from the dialog box */
\r
6279 hwndText = GetDlgItem(hDlg, OPT_CommentText);
\r
6280 len = GetWindowTextLength(hwndText);
\r
6281 str = (char *) malloc(len + 1);
\r
6282 GetWindowText(hwndText, str, len + 1);
\r
6291 ReplaceComment(commentIndex, str);
\r
6298 case OPT_CancelComment:
\r
6302 case OPT_ClearComment:
\r
6303 SetDlgItemText(hDlg, OPT_CommentText, "");
\r
6306 case OPT_EditComment:
\r
6307 EditCommentEvent();
\r
6315 case WM_NOTIFY: // [HGM] vari: cloned from whistory.c
\r
6316 if( wParam == OPT_CommentText ) {
\r
6317 MSGFILTER * lpMF = (MSGFILTER *) lParam;
\r
6319 if( lpMF->msg == WM_RBUTTONDOWN && (lpMF->wParam & (MK_CONTROL | MK_SHIFT)) == 0 ||
\r
6320 lpMF->msg == WM_CHAR && lpMF->wParam == '\022' ) {
\r
6324 pt.x = LOWORD( lpMF->lParam );
\r
6325 pt.y = HIWORD( lpMF->lParam );
\r
6327 if(lpMF->msg == WM_CHAR) {
\r
6329 SendDlgItemMessage( hDlg, OPT_CommentText, EM_EXGETSEL, 0, (LPARAM) &sel );
\r
6330 index = sel.cpMin;
\r
6332 index = SendDlgItemMessage( hDlg, OPT_CommentText, EM_CHARFROMPOS, 0, (LPARAM) &pt );
\r
6334 hwndText = GetDlgItem(hDlg, OPT_CommentText); // cloned from above
\r
6335 len = GetWindowTextLength(hwndText);
\r
6336 str = (char *) malloc(len + 1);
\r
6337 GetWindowText(hwndText, str, len + 1);
\r
6338 ReplaceComment(commentIndex, str);
\r
6339 if(commentIndex != currentMove) ToNrEvent(commentIndex);
\r
6340 LoadVariation( index, str ); // [HGM] also does the actual moving to it, now
\r
6343 /* Zap the message for good: apparently, returning non-zero is not enough */
\r
6344 lpMF->msg = WM_USER;
\r
6352 newSizeX = LOWORD(lParam);
\r
6353 newSizeY = HIWORD(lParam);
\r
6354 ResizeEditPlusButtons(hDlg, hwndText, sizeX, sizeY, newSizeX, newSizeY);
\r
6359 case WM_GETMINMAXINFO:
\r
6360 /* Prevent resizing window too small */
\r
6361 mmi = (MINMAXINFO *) lParam;
\r
6362 mmi->ptMinTrackSize.x = 100;
\r
6363 mmi->ptMinTrackSize.y = 100;
\r
6370 EitherCommentPopUp(int index, char *title, char *str, BOOLEAN edit)
\r
6375 CheckMenuItem(GetMenu(hwndMain), IDM_Comment, edit ? MF_CHECKED : MF_UNCHECKED);
\r
6377 if (str == NULL) str = "";
\r
6378 p = (char *) malloc(2 * strlen(str) + 2);
\r
6381 if (*str == '\n') *q++ = '\r';
\r
6385 if (commentText != NULL) free(commentText);
\r
6387 commentIndex = index;
\r
6388 commentTitle = title;
\r
6390 editComment = edit;
\r
6392 if (commentDialog) {
\r
6393 SendMessage(commentDialog, WM_INITDIALOG, 0, 0);
\r
6394 if (!commentUp) ShowWindow(commentDialog, SW_SHOW);
\r
6396 lpProc = MakeProcInstance((FARPROC)CommentDialog, hInst);
\r
6397 CreateDialog(hInst, MAKEINTRESOURCE(DLG_EditComment),
\r
6398 hwndMain, (DLGPROC)lpProc);
\r
6399 FreeProcInstance(lpProc);
\r
6405 /*---------------------------------------------------------------------------*\
\r
6407 * Type-in move dialog functions
\r
6409 \*---------------------------------------------------------------------------*/
\r
6412 TypeInMoveDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
6414 char move[MSG_SIZ];
\r
6417 switch (message) {
\r
6418 case WM_INITDIALOG:
\r
6419 move[0] = (char) lParam;
\r
6420 move[1] = NULLCHAR;
\r
6421 CenterWindowEx(hDlg, GetWindow(hDlg, GW_OWNER), 1 );
\r
6422 Translate(hDlg, DLG_TypeInMove);
\r
6423 hInput = GetDlgItem(hDlg, OPT_Move);
\r
6424 SetWindowText(hInput, move);
\r
6426 SendMessage(hInput, EM_SETSEL, (WPARAM)9999, (LPARAM)9999);
\r
6430 switch (LOWORD(wParam)) {
\r
6433 shiftKey = GetKeyState(VK_SHIFT) < 0; // [HGM] remember last shift status
\r
6434 GetDlgItemText(hDlg, OPT_Move, move, sizeof(move));
6435 TypeInDoneEvent(move);
\r
6436 EndDialog(hDlg, TRUE);
\r
6439 EndDialog(hDlg, FALSE);
\r
6450 PopUpMoveDialog(char firstchar)
\r
6454 lpProc = MakeProcInstance((FARPROC)TypeInMoveDialog, hInst);
\r
6455 DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_TypeInMove),
\r
6456 hwndMain, (DLGPROC)lpProc, (LPARAM)firstchar);
\r
6457 FreeProcInstance(lpProc);
\r
6460 /*---------------------------------------------------------------------------*\
\r
6462 * Type-in name dialog functions
\r
6464 \*---------------------------------------------------------------------------*/
\r
6467 TypeInNameDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
6469 char move[MSG_SIZ];
\r
6472 switch (message) {
\r
6473 case WM_INITDIALOG:
\r
6474 move[0] = (char) lParam;
\r
6475 move[1] = NULLCHAR;
\r
6476 CenterWindowEx(hDlg, GetWindow(hDlg, GW_OWNER), 1 );
\r
6477 Translate(hDlg, DLG_TypeInName);
\r
6478 hInput = GetDlgItem(hDlg, OPT_Name);
\r
6479 SetWindowText(hInput, move);
\r
6481 SendMessage(hInput, EM_SETSEL, (WPARAM)9999, (LPARAM)9999);
\r
6485 switch (LOWORD(wParam)) {
\r
6487 GetDlgItemText(hDlg, OPT_Name, move, sizeof(move));
\r
6488 appData.userName = strdup(move);
\r
6491 if(gameMode == MachinePlaysWhite || gameMode == MachinePlaysBlack) {
\r
6492 snprintf(move, MSG_SIZ, "%s vs. %s", gameInfo.white, gameInfo.black);
\r
6493 DisplayTitle(move);
\r
6497 EndDialog(hDlg, TRUE);
\r
6500 EndDialog(hDlg, FALSE);
\r
6511 PopUpNameDialog(char firstchar)
\r
6515 lpProc = MakeProcInstance((FARPROC)TypeInNameDialog, hInst);
\r
6516 DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_TypeInName),
\r
6517 hwndMain, (DLGPROC)lpProc, (LPARAM)firstchar);
\r
6518 FreeProcInstance(lpProc);
\r
6521 /*---------------------------------------------------------------------------*\
\r
6525 \*---------------------------------------------------------------------------*/
\r
6527 /* Nonmodal error box */
\r
6528 LRESULT CALLBACK ErrorDialog(HWND hDlg, UINT message,
\r
6529 WPARAM wParam, LPARAM lParam);
\r
6532 ErrorPopUp(char *title, char *content)
\r
6536 BOOLEAN modal = hwndMain == NULL;
\r
6554 strncpy(errorTitle, title, sizeof(errorTitle));
\r
6555 errorTitle[sizeof(errorTitle) - 1] = '\0';
\r
6558 MessageBox(NULL, errorMessage, errorTitle, MB_OK|MB_ICONEXCLAMATION);
\r
6560 lpProc = MakeProcInstance((FARPROC)ErrorDialog, hInst);
\r
6561 CreateDialog(hInst, MAKEINTRESOURCE(DLG_Error),
\r
6562 hwndMain, (DLGPROC)lpProc);
\r
6563 FreeProcInstance(lpProc);
\r
6570 if (!appData.popupMoveErrors && moveErrorMessageUp) DisplayMessage("", "");
\r
6571 if (errorDialog == NULL) return;
\r
6572 DestroyWindow(errorDialog);
\r
6573 errorDialog = NULL;
\r
6574 if(errorExitStatus) ExitEvent(errorExitStatus);
\r
6578 ErrorDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
6583 switch (message) {
\r
6584 case WM_INITDIALOG:
\r
6585 GetWindowRect(hDlg, &rChild);
\r
6588 SetWindowPos(hDlg, NULL, rChild.left,
\r
6589 rChild.top + boardRect.top - (rChild.bottom - rChild.top),
\r
6590 0, 0, SWP_NOZORDER|SWP_NOSIZE);
\r
6594 [AS] It seems that the above code wants to move the dialog up in the "caption
\r
6595 area" of the main window, but it uses the dialog height as an hard-coded constant,
\r
6596 and it doesn't work when you resize the dialog.
\r
6597 For now, just give it a default position.
\r
6599 SetWindowPos(hDlg, NULL, boardRect.left+8, boardRect.top+8, 0, 0, SWP_NOZORDER|SWP_NOSIZE);
\r
6600 Translate(hDlg, DLG_Error);
\r
6602 errorDialog = hDlg;
\r
6603 SetWindowText(hDlg, errorTitle);
\r
6604 hwndText = GetDlgItem(hDlg, OPT_ErrorText);
\r
6605 SetDlgItemText(hDlg, OPT_ErrorText, errorMessage);
\r
6609 switch (LOWORD(wParam)) {
\r
6612 if (errorDialog == hDlg) errorDialog = NULL;
\r
6613 DestroyWindow(hDlg);
\r
6625 HWND gothicDialog = NULL;
\r
6628 GothicDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
6632 int height = GetSystemMetrics(SM_CYCAPTION)+GetSystemMetrics(SM_CYFRAME);
\r
6634 switch (message) {
\r
6635 case WM_INITDIALOG:
\r
6636 GetWindowRect(hDlg, &rChild);
\r
6638 SetWindowPos(hDlg, NULL, wpMain.x, wpMain.y-height, wpMain.width, height,
\r
6642 [AS] It seems that the above code wants to move the dialog up in the "caption
\r
6643 area" of the main window, but it uses the dialog height as an hard-coded constant,
\r
6644 and it doesn't work when you resize the dialog.
\r
6645 For now, just give it a default position.
\r
6647 gothicDialog = hDlg;
\r
6648 SetWindowText(hDlg, errorTitle);
\r
6649 hwndText = GetDlgItem(hDlg, OPT_ErrorText);
\r
6650 SetDlgItemText(hDlg, OPT_ErrorText, errorMessage);
\r
6654 switch (LOWORD(wParam)) {
\r
6657 if (errorDialog == hDlg) errorDialog = NULL;
\r
6658 DestroyWindow(hDlg);
\r
6670 GothicPopUp(char *title, VariantClass variant)
\r
6673 static char *lastTitle;
\r
6675 strncpy(errorTitle, title, sizeof(errorTitle));
\r
6676 errorTitle[sizeof(errorTitle) - 1] = '\0';
\r
6678 if(lastTitle != title && gothicDialog != NULL) {
\r
6679 DestroyWindow(gothicDialog);
\r
6680 gothicDialog = NULL;
\r
6682 if(variant != VariantNormal && gothicDialog == NULL) {
\r
6683 title = lastTitle;
\r
6684 lpProc = MakeProcInstance((FARPROC)GothicDialog, hInst);
\r
6685 CreateDialog(hInst, MAKEINTRESOURCE(DLG_Error),
\r
6686 hwndMain, (DLGPROC)lpProc);
\r
6687 FreeProcInstance(lpProc);
\r
6692 /*---------------------------------------------------------------------------*\
\r
6694 * Ics Interaction console functions
\r
6696 \*---------------------------------------------------------------------------*/
\r
6698 #define HISTORY_SIZE 64
\r
6699 static char *history[HISTORY_SIZE];
\r
6700 int histIn = 0, histP = 0;
\r
6703 SaveInHistory(char *cmd)
\r
6705 if (history[histIn] != NULL) {
\r
6706 free(history[histIn]);
\r
6707 history[histIn] = NULL;
\r
6709 if (*cmd == NULLCHAR) return;
\r
6710 history[histIn] = StrSave(cmd);
\r
6711 histIn = (histIn + 1) % HISTORY_SIZE;
\r
6712 if (history[histIn] != NULL) {
\r
6713 free(history[histIn]);
\r
6714 history[histIn] = NULL;
\r
6720 PrevInHistory(char *cmd)
\r
6723 if (histP == histIn) {
\r
6724 if (history[histIn] != NULL) free(history[histIn]);
\r
6725 history[histIn] = StrSave(cmd);
\r
6727 newhp = (histP - 1 + HISTORY_SIZE) % HISTORY_SIZE;
\r
6728 if (newhp == histIn || history[newhp] == NULL) return NULL;
\r
6730 return history[histP];
\r
6736 if (histP == histIn) return NULL;
\r
6737 histP = (histP + 1) % HISTORY_SIZE;
\r
6738 return history[histP];
\r
6742 LoadIcsTextMenu(IcsTextMenuEntry *e)
\r
6746 hmenu = LoadMenu(hInst, "TextMenu");
\r
6747 h = GetSubMenu(hmenu, 0);
\r
6749 if (strcmp(e->item, "-") == 0) {
\r
6750 AppendMenu(h, MF_SEPARATOR, 0, 0);
\r
6751 } else { // [HGM] re-written a bit to use only one AppendMenu call for both cases (| or no |)
\r
6752 int flags = MF_STRING, j = 0;
\r
6753 if (e->item[0] == '|') {
\r
6754 flags |= MF_MENUBARBREAK;
\r
6757 if(!strcmp(e->command, "none")) flags |= MF_GRAYED; // [HGM] chatclick: provide inactive dummy
\r
6758 AppendMenu(h, flags, IDM_CommandX + i, e->item + j);
\r
6766 WNDPROC consoleTextWindowProc;
\r
6769 CommandX(HWND hwnd, char *command, BOOLEAN getname, BOOLEAN immediate)
\r
6771 char buf[MSG_SIZ], name[MSG_SIZ];
\r
6772 HWND hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
6776 SetWindowText(hInput, command);
\r
6778 SendMessage(hInput, WM_CHAR, '\r', 0);
\r
6780 sel.cpMin = 999999;
\r
6781 sel.cpMax = 999999;
\r
6782 SendMessage(hInput, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
6787 SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);
\r
6788 if (sel.cpMin == sel.cpMax) {
\r
6789 /* Expand to surrounding word */
\r
6792 tr.chrg.cpMax = sel.cpMin;
\r
6793 tr.chrg.cpMin = --sel.cpMin;
\r
6794 if (sel.cpMin < 0) break;
\r
6795 tr.lpstrText = name;
\r
6796 SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr);
\r
6797 } while (isalpha(name[0]) || isdigit(name[0]) || name[0] == '-');
\r
6801 tr.chrg.cpMin = sel.cpMax;
\r
6802 tr.chrg.cpMax = ++sel.cpMax;
\r
6803 tr.lpstrText = name;
\r
6804 if (SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr) < 1) break;
\r
6805 } while (isalpha(name[0]) || isdigit(name[0]) || name[0] == '-');
\r
6808 if (sel.cpMax == sel.cpMin || sel.cpMax - sel.cpMin > MSG_SIZ/2) {
\r
6809 MessageBeep(MB_ICONEXCLAMATION);
\r
6813 tr.lpstrText = name;
\r
6814 SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr);
\r
6816 if (sel.cpMax - sel.cpMin > MSG_SIZ/2) {
\r
6817 MessageBeep(MB_ICONEXCLAMATION);
\r
6820 SendMessage(hwnd, EM_GETSELTEXT, 0, (LPARAM) name);
\r
6823 if(strstr(command, "%s")) snprintf(buf, MSG_SIZ, command, name); else
\r
6824 snprintf(buf, MSG_SIZ, "%s %s", command, name);
\r
6825 SetWindowText(hInput, buf);
\r
6826 SendMessage(hInput, WM_CHAR, '\r', 0);
\r
6828 if(!strcmp(command, "chat")) { ChatPopUp(name); return; }
\r
6829 snprintf(buf, MSG_SIZ, "%s %s ", command, name); /* trailing space */
\r
6830 SetWindowText(hInput, buf);
\r
6831 sel.cpMin = 999999;
\r
6832 sel.cpMax = 999999;
\r
6833 SendMessage(hInput, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
6839 ConsoleTextSubclass(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
\r
6844 switch (message) {
\r
6846 if (!(GetKeyState(VK_CONTROL) & ~1)) break;
\r
6847 if(wParam=='R') return 0;
\r
6850 SendMessage(hwnd, EM_LINESCROLL, 0, -999999);
\r
6853 sel.cpMin = 999999;
\r
6854 sel.cpMax = 999999;
\r
6855 SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
6856 SendMessage(hwnd, EM_SCROLLCARET, 0, 0);
\r
6861 if(wParam != '\022') {
\r
6862 if (wParam == '\t') {
\r
6863 if (GetKeyState(VK_SHIFT) < 0) {
\r
6865 if (IsIconic(hwndMain)) ShowWindow(hwndMain, SW_RESTORE);
\r
6866 if (buttonDesc[0].hwnd) {
\r
6867 SetFocus(buttonDesc[0].hwnd);
\r
6869 SetFocus(hwndMain);
\r
6873 SetFocus(GetDlgItem(hwndConsole, OPT_ConsoleInput));
\r
6876 hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
6877 JAWS_DELETE( SetFocus(hInput); )
\r
6878 SendMessage(hInput, message, wParam, lParam);
\r
6881 } // [HGM] navigate: for Ctrl+R, flow into next case (moved up here) to summon up menu
\r
6883 case WM_RBUTTONDOWN:
\r
6884 if (!(GetKeyState(VK_SHIFT) & ~1)) {
\r
6885 /* Move selection here if it was empty */
\r
6887 pt.x = LOWORD(lParam);
\r
6888 pt.y = HIWORD(lParam);
\r
6889 SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);
\r
6890 if (sel.cpMin == sel.cpMax) {
\r
6891 if(lParam != -1) sel.cpMin = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&pt); /*doc is wrong*/
\r
6892 sel.cpMax = sel.cpMin;
\r
6893 SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
6895 SendMessage(hwnd, EM_HIDESELECTION, FALSE, FALSE);
\r
6896 { // [HGM] chatclick: code moved here from WM_RBUTTONUP case, to have menu appear on down-click
\r
6898 HMENU hmenu = LoadIcsTextMenu(icsTextMenuEntry);
\r
6899 SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);
\r
6900 if (sel.cpMin == sel.cpMax) {
\r
6901 EnableMenuItem(hmenu, IDM_Copy, MF_BYCOMMAND|MF_GRAYED);
\r
6902 EnableMenuItem(hmenu, IDM_QuickPaste, MF_BYCOMMAND|MF_GRAYED);
\r
6904 if (!IsClipboardFormatAvailable(CF_TEXT)) {
\r
6905 EnableMenuItem(hmenu, IDM_Paste, MF_BYCOMMAND|MF_GRAYED);
\r
6907 pt.x = LOWORD(lParam)-30; // [HGM] chatclick: make menu pop up with pointer above upper-right item
\r
6908 pt.y = HIWORD(lParam)-10; // make it appear as if mouse moved there, so it will be selected on up-click
\r
6909 PostMessage(hwnd, WM_MOUSEMOVE, wParam, lParam+5);
\r
6910 MenuPopup(hwnd, pt, hmenu, -1);
\r
6914 case WM_RBUTTONUP:
\r
6915 if (GetKeyState(VK_SHIFT) & ~1) {
\r
6916 SendDlgItemMessage(hwndConsole, OPT_ConsoleText,
\r
6917 WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);
\r
6921 hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
6923 return SendMessage(hInput, message, wParam, lParam);
\r
6924 case WM_MBUTTONDOWN:
\r
6925 return SendMessage(hwnd, WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);
\r
6927 switch (LOWORD(wParam)) {
\r
6928 case IDM_QuickPaste:
\r
6930 SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);
\r
6931 if (sel.cpMin == sel.cpMax) {
\r
6932 MessageBeep(MB_ICONEXCLAMATION);
\r
6935 SendMessage(hwnd, WM_COPY, 0, 0);
\r
6936 hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
6937 SendMessage(hInput, WM_PASTE, 0, 0);
\r
6942 SendMessage(hwnd, WM_CUT, 0, 0);
\r
6945 SendMessage(hwnd, WM_PASTE, 0, 0);
\r
6948 SendMessage(hwnd, WM_COPY, 0, 0);
\r
6952 int i = LOWORD(wParam) - IDM_CommandX;
\r
6953 if (i >= 0 && i < ICS_TEXT_MENU_SIZE &&
\r
6954 icsTextMenuEntry[i].command != NULL) {
\r
6955 CommandX(hwnd, icsTextMenuEntry[i].command,
\r
6956 icsTextMenuEntry[i].getname,
\r
6957 icsTextMenuEntry[i].immediate);
\r
6965 return (*consoleTextWindowProc)(hwnd, message, wParam, lParam);
\r
6968 WNDPROC consoleInputWindowProc;
\r
6971 ConsoleInputSubclass(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
\r
6973 char buf[MSG_SIZ];
\r
6975 static BOOL sendNextChar = FALSE;
\r
6976 static BOOL quoteNextChar = FALSE;
\r
6977 InputSource *is = consoleInputSource;
\r
6981 switch (message) {
\r
6983 if (!appData.localLineEditing || sendNextChar) {
\r
6984 is->buf[0] = (CHAR) wParam;
\r
6986 SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);
\r
6987 sendNextChar = FALSE;
\r
6990 if (quoteNextChar) {
\r
6991 buf[0] = (char) wParam;
\r
6992 buf[1] = NULLCHAR;
\r
6993 SendMessage(hwnd, EM_REPLACESEL, TRUE, (LPARAM) buf);
\r
6994 quoteNextChar = FALSE;
\r
6998 case '\r': /* Enter key */
\r
6999 is->count = GetWindowText(hwnd, is->buf, INPUT_SOURCE_BUF_SIZE-1);
\r
7000 if (consoleEcho) SaveInHistory(is->buf);
\r
7001 is->buf[is->count++] = '\n';
\r
7002 is->buf[is->count] = NULLCHAR;
\r
7003 SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);
\r
7004 if (consoleEcho) {
\r
7005 ConsoleOutput(is->buf, is->count, TRUE);
\r
7006 } else if (appData.localLineEditing) {
\r
7007 ConsoleOutput("\n", 1, TRUE);
\r
7010 case '\033': /* Escape key */
\r
7011 SetWindowText(hwnd, "");
\r
7012 cf.cbSize = sizeof(CHARFORMAT);
\r
7013 cf.dwMask = CFM_COLOR|CFM_BOLD|CFM_ITALIC|CFM_UNDERLINE|CFM_STRIKEOUT;
\r
7014 if (consoleEcho) {
\r
7015 cf.crTextColor = textAttribs[ColorNormal].color;
\r
7017 cf.crTextColor = COLOR_ECHOOFF;
\r
7019 cf.dwEffects = textAttribs[ColorNormal].effects;
\r
7020 SendMessage(hwnd, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf);
\r
7022 case '\t': /* Tab key */
\r
7023 if (GetKeyState(VK_SHIFT) < 0) {
\r
7025 SetFocus(GetDlgItem(hwndConsole, OPT_ConsoleText));
\r
7028 if (IsIconic(hwndMain)) ShowWindow(hwndMain, SW_RESTORE);
\r
7029 if (buttonDesc[0].hwnd) {
\r
7030 SetFocus(buttonDesc[0].hwnd);
\r
7032 SetFocus(hwndMain);
\r
7036 case '\023': /* Ctrl+S */
\r
7037 sendNextChar = TRUE;
\r
7039 case '\021': /* Ctrl+Q */
\r
7040 quoteNextChar = TRUE;
\r
7050 GetWindowText(hwnd, buf, MSG_SIZ);
\r
7051 p = PrevInHistory(buf);
\r
7053 SetWindowText(hwnd, p);
\r
7054 sel.cpMin = 999999;
\r
7055 sel.cpMax = 999999;
\r
7056 SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
7061 p = NextInHistory();
\r
7063 SetWindowText(hwnd, p);
\r
7064 sel.cpMin = 999999;
\r
7065 sel.cpMax = 999999;
\r
7066 SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
7072 if (!(GetKeyState(VK_CONTROL) & ~1)) break;
\r
7076 SendDlgItemMessage(hwndConsole, OPT_ConsoleText, message, wParam, lParam);
\r
7080 case WM_MBUTTONDOWN:
\r
7081 SendDlgItemMessage(hwndConsole, OPT_ConsoleText,
\r
7082 WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);
\r
7084 case WM_RBUTTONUP:
\r
7085 if (GetKeyState(VK_SHIFT) & ~1) {
\r
7086 SendDlgItemMessage(hwndConsole, OPT_ConsoleText,
\r
7087 WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);
\r
7091 hmenu = LoadMenu(hInst, "InputMenu");
\r
7092 SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);
\r
7093 if (sel.cpMin == sel.cpMax) {
\r
7094 EnableMenuItem(hmenu, IDM_Copy, MF_BYCOMMAND|MF_GRAYED);
\r
7095 EnableMenuItem(hmenu, IDM_Cut, MF_BYCOMMAND|MF_GRAYED);
\r
7097 if (!IsClipboardFormatAvailable(CF_TEXT)) {
\r
7098 EnableMenuItem(hmenu, IDM_Paste, MF_BYCOMMAND|MF_GRAYED);
\r
7100 pt.x = LOWORD(lParam);
\r
7101 pt.y = HIWORD(lParam);
\r
7102 MenuPopup(hwnd, pt, hmenu, -1);
\r
7106 switch (LOWORD(wParam)) {
\r
7108 SendMessage(hwnd, EM_UNDO, 0, 0);
\r
7110 case IDM_SelectAll:
\r
7112 sel.cpMax = -1; /*999999?*/
\r
7113 SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
7116 SendMessage(hwnd, WM_CUT, 0, 0);
\r
7119 SendMessage(hwnd, WM_PASTE, 0, 0);
\r
7122 SendMessage(hwnd, WM_COPY, 0, 0);
\r
7127 return (*consoleInputWindowProc)(hwnd, message, wParam, lParam);
\r
7130 #define CO_MAX 100000
\r
7131 #define CO_TRIM 1000
\r
7134 ConsoleWndProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
7136 static SnapData sd;
\r
7137 HWND hText, hInput;
\r
7139 static int sizeX, sizeY;
\r
7140 int newSizeX, newSizeY;
\r
7144 hText = GetDlgItem(hDlg, OPT_ConsoleText);
\r
7145 hInput = GetDlgItem(hDlg, OPT_ConsoleInput);
\r
7147 switch (message) {
\r
7149 if (((NMHDR*)lParam)->code == EN_LINK)
\r
7151 ENLINK *pLink = (ENLINK*)lParam;
\r
7152 if (pLink->msg == WM_LBUTTONUP)
\r
7156 tr.chrg = pLink->chrg;
\r
7157 tr.lpstrText = malloc(1+tr.chrg.cpMax-tr.chrg.cpMin);
\r
7158 SendMessage(hText, EM_GETTEXTRANGE, 0, (LPARAM)&tr);
\r
7159 ShellExecute(NULL, "open", tr.lpstrText, NULL, NULL, SW_SHOW);
\r
7160 free(tr.lpstrText);
\r
7164 case WM_INITDIALOG: /* message: initialize dialog box */
\r
7165 hwndConsole = hDlg;
\r
7167 consoleTextWindowProc = (WNDPROC)
\r
7168 SetWindowLongPtr(hText, GWLP_WNDPROC, (LONG_PTR) ConsoleTextSubclass);
\r
7169 SendMessage(hText, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);
\r
7170 consoleInputWindowProc = (WNDPROC)
\r
7171 SetWindowLongPtr(hInput, GWLP_WNDPROC, (LONG_PTR) ConsoleInputSubclass);
\r
7172 SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);
\r
7173 Colorize(ColorNormal, TRUE);
\r
7174 SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &consoleCF);
\r
7175 ChangedConsoleFont();
\r
7176 GetClientRect(hDlg, &rect);
\r
7177 sizeX = rect.right;
\r
7178 sizeY = rect.bottom;
\r
7179 if (wpConsole.x != CW_USEDEFAULT && wpConsole.y != CW_USEDEFAULT &&
\r
7180 wpConsole.width != CW_USEDEFAULT && wpConsole.height != CW_USEDEFAULT) {
\r
7181 WINDOWPLACEMENT wp;
\r
7182 EnsureOnScreen(&wpConsole.x, &wpConsole.y, 0, 0);
\r
7183 wp.length = sizeof(WINDOWPLACEMENT);
\r
7185 wp.showCmd = SW_SHOW;
\r
7186 wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;
\r
7187 wp.rcNormalPosition.left = wpConsole.x;
\r
7188 wp.rcNormalPosition.right = wpConsole.x + wpConsole.width;
\r
7189 wp.rcNormalPosition.top = wpConsole.y;
\r
7190 wp.rcNormalPosition.bottom = wpConsole.y + wpConsole.height;
\r
7191 SetWindowPlacement(hDlg, &wp);
\r
7194 // [HGM] Chessknight's change 2004-07-13
\r
7195 else { /* Determine Defaults */
\r
7196 WINDOWPLACEMENT wp;
\r
7197 wpConsole.x = wpMain.width + 1;
\r
7198 wpConsole.y = wpMain.y;
\r
7199 wpConsole.width = screenWidth - wpMain.width;
\r
7200 wpConsole.height = wpMain.height;
\r
7201 EnsureOnScreen(&wpConsole.x, &wpConsole.y, 0, 0);
\r
7202 wp.length = sizeof(WINDOWPLACEMENT);
\r
7204 wp.showCmd = SW_SHOW;
\r
7205 wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;
\r
7206 wp.rcNormalPosition.left = wpConsole.x;
\r
7207 wp.rcNormalPosition.right = wpConsole.x + wpConsole.width;
\r
7208 wp.rcNormalPosition.top = wpConsole.y;
\r
7209 wp.rcNormalPosition.bottom = wpConsole.y + wpConsole.height;
\r
7210 SetWindowPlacement(hDlg, &wp);
\r
7213 // Allow hText to highlight URLs and send notifications on them
\r
7214 wMask = (WORD) SendMessage(hText, EM_GETEVENTMASK, 0, 0L);
\r
7215 SendMessage(hText, EM_SETEVENTMASK, 0, wMask | ENM_LINK);
\r
7216 SendMessage(hText, EM_AUTOURLDETECT, TRUE, 0L);
\r
7217 SetWindowLongPtr(hText, GWLP_USERDATA, 79); // initialize the text window's width
\r
7231 if (IsIconic(hDlg)) break;
\r
7232 newSizeX = LOWORD(lParam);
\r
7233 newSizeY = HIWORD(lParam);
\r
7234 if (sizeX != newSizeX || sizeY != newSizeY) {
\r
7235 RECT rectText, rectInput;
\r
7237 int newTextHeight, newTextWidth;
\r
7238 GetWindowRect(hText, &rectText);
\r
7239 newTextWidth = rectText.right - rectText.left + newSizeX - sizeX;
\r
7240 newTextHeight = rectText.bottom - rectText.top + newSizeY - sizeY;
\r
7241 if (newTextHeight < 0) {
\r
7242 newSizeY += -newTextHeight;
\r
7243 newTextHeight = 0;
\r
7245 SetWindowPos(hText, NULL, 0, 0,
\r
7246 newTextWidth, newTextHeight, SWP_NOZORDER|SWP_NOMOVE);
\r
7247 GetWindowRect(hInput, &rectInput); /* gives screen coords */
\r
7248 pt.x = rectInput.left;
\r
7249 pt.y = rectInput.top + newSizeY - sizeY;
\r
7250 ScreenToClient(hDlg, &pt);
\r
7251 SetWindowPos(hInput, NULL,
\r
7252 pt.x, pt.y, /* needs client coords */
\r
7253 rectInput.right - rectInput.left + newSizeX - sizeX,
\r
7254 rectInput.bottom - rectInput.top, SWP_NOZORDER);
\r
7260 case WM_GETMINMAXINFO:
\r
7261 /* Prevent resizing window too small */
\r
7262 mmi = (MINMAXINFO *) lParam;
\r
7263 mmi->ptMinTrackSize.x = 100;
\r
7264 mmi->ptMinTrackSize.y = 100;
\r
7267 /* [AS] Snapping */
\r
7268 case WM_ENTERSIZEMOVE:
\r
7269 return OnEnterSizeMove( &sd, hDlg, wParam, lParam );
\r
7272 return OnSizing( &sd, hDlg, wParam, lParam );
\r
7275 return OnMoving( &sd, hDlg, wParam, lParam );
\r
7277 case WM_EXITSIZEMOVE:
\r
7278 UpdateICSWidth(hText);
\r
7279 return OnExitSizeMove( &sd, hDlg, wParam, lParam );
\r
7282 return DefWindowProc(hDlg, message, wParam, lParam);
\r
7290 if (hwndConsole) return;
\r
7291 hCons = CreateDialog(hInst, szConsoleName, 0, NULL);
\r
7292 SendMessage(hCons, WM_INITDIALOG, 0, 0);
\r
7297 ConsoleOutput(char* data, int length, int forceVisible)
\r
7302 char buf[CO_MAX+1];
\r
7305 static int delayLF = 0;
\r
7306 CHARRANGE savesel, sel;
\r
7308 if (hwndConsole == NULL || length > CO_MAX-100 || length == 0) return;
\r
7316 while (length--) {
\r
7324 } else if (*p == '\007') {
\r
7325 MyPlaySound(&sounds[(int)SoundBell]);
\r
7332 hText = GetDlgItem(hwndConsole, OPT_ConsoleText);
\r
7333 SendMessage(hText, EM_HIDESELECTION, TRUE, FALSE);
\r
7334 /* Save current selection */
\r
7335 SendMessage(hText, EM_EXGETSEL, 0, (LPARAM)&savesel);
\r
7336 exlen = GetWindowTextLength(hText);
\r
7337 /* Find out whether current end of text is visible */
\r
7338 SendMessage(hText, EM_GETRECT, 0, (LPARAM) &rect);
\r
7339 SendMessage(hText, EM_POSFROMCHAR, (WPARAM) &pEnd, exlen);
\r
7340 /* Trim existing text if it's too long */
\r
7341 if (exlen + (q - buf) > CO_MAX) {
\r
7342 trim = (CO_TRIM > (q - buf)) ? CO_TRIM : (q - buf);
\r
7345 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
7346 SendMessage(hText, EM_REPLACESEL, 0, (LPARAM)"");
\r
7348 savesel.cpMin -= trim;
\r
7349 savesel.cpMax -= trim;
\r
7350 if (exlen < 0) exlen = 0;
\r
7351 if (savesel.cpMin < 0) savesel.cpMin = 0;
\r
7352 if (savesel.cpMax < savesel.cpMin) savesel.cpMax = savesel.cpMin;
\r
7354 /* Append the new text */
\r
7355 sel.cpMin = exlen;
\r
7356 sel.cpMax = exlen;
\r
7357 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
7358 SendMessage(hText, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&consoleCF);
\r
7359 SendMessage(hText, EM_REPLACESEL, 0, (LPARAM) buf);
\r
7360 if (forceVisible || exlen == 0 ||
\r
7361 (rect.left <= pEnd.x && pEnd.x < rect.right &&
\r
7362 rect.top <= pEnd.y && pEnd.y < rect.bottom)) {
\r
7363 /* Scroll to make new end of text visible if old end of text
\r
7364 was visible or new text is an echo of user typein */
\r
7365 sel.cpMin = 9999999;
\r
7366 sel.cpMax = 9999999;
\r
7367 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
7368 SendMessage(hText, EM_HIDESELECTION, FALSE, FALSE);
\r
7369 SendMessage(hText, EM_SCROLLCARET, 0, 0);
\r
7370 SendMessage(hText, EM_HIDESELECTION, TRUE, FALSE);
\r
7372 if (savesel.cpMax == exlen || forceVisible) {
\r
7373 /* Move insert point to new end of text if it was at the old
\r
7374 end of text or if the new text is an echo of user typein */
\r
7375 sel.cpMin = 9999999;
\r
7376 sel.cpMax = 9999999;
\r
7377 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
7379 /* Restore previous selection */
\r
7380 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&savesel);
\r
7382 SendMessage(hText, EM_HIDESELECTION, FALSE, FALSE);
\r
7389 DisplayHoldingsCount(HDC hdc, int x, int y, int rightAlign, int copyNumber)
\r
7393 COLORREF oldFg, oldBg;
\r
7398 snprintf(buf, sizeof(buf)/sizeof(buf[0]),"%d", copyNumber); else buf[0] = 0;
\r
7400 oldFg = SetTextColor(hdc, RGB(255, 255, 255)); /* white */
\r
7401 oldBg = SetBkColor(hdc, RGB(0, 0, 0)); /* black */
\r
7402 oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);
\r
7405 rect.right = x + squareSize;
\r
7407 rect.bottom = y + squareSize;
\r
7410 ExtTextOut(hdc, x + MESSAGE_LINE_LEFTMARGIN
\r
7411 + (rightAlign ? (squareSize*2)/3 : 0),
\r
7412 y, ETO_CLIPPED|ETO_OPAQUE,
\r
7413 &rect, str, strlen(str), NULL);
\r
7415 (void) SetTextColor(hdc, oldFg);
\r
7416 (void) SetBkColor(hdc, oldBg);
\r
7417 (void) SelectObject(hdc, oldFont);
\r
7421 DisplayAClock(HDC hdc, int timeRemaining, int highlight,
\r
7422 RECT *rect, char *color, char *flagFell)
\r
7426 COLORREF oldFg, oldBg;
\r
7429 if (appData.clockMode) {
\r
7431 snprintf(buf, sizeof(buf)/sizeof(buf[0]), "%c %s %s", color[0], TimeString(timeRemaining), flagFell);
\r
7433 snprintf(buf, sizeof(buf)/sizeof(buf[0]), "%s:%c%s %s", color, (logoHeight>0 ? 0 : ' '), TimeString(timeRemaining), flagFell);
\r
7440 oldFg = SetTextColor(hdc, RGB(255, 255, 255)); /* white */
\r
7441 oldBg = SetBkColor(hdc, RGB(0, 0, 0)); /* black */
\r
7443 oldFg = SetTextColor(hdc, RGB(0, 0, 0)); /* black */
\r
7444 oldBg = SetBkColor(hdc, RGB(255, 255, 255)); /* white */
\r
7446 oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);
\r
7450 ExtTextOut(hdc, rect->left + MESSAGE_LINE_LEFTMARGIN,
\r
7451 rect->top, ETO_CLIPPED|ETO_OPAQUE,
\r
7452 rect, str, strlen(str), NULL);
\r
7453 if(logoHeight > 0 && appData.clockMode) {
\r
7455 str += strlen(color)+2;
\r
7456 r.top = rect->top + logoHeight/2;
\r
7457 r.left = rect->left;
\r
7458 r.right = rect->right;
\r
7459 r.bottom = rect->bottom;
\r
7460 ExtTextOut(hdc, rect->left + MESSAGE_LINE_LEFTMARGIN,
\r
7461 r.top, ETO_CLIPPED|ETO_OPAQUE,
\r
7462 &r, str, strlen(str), NULL);
\r
7464 (void) SetTextColor(hdc, oldFg);
\r
7465 (void) SetBkColor(hdc, oldBg);
\r
7466 (void) SelectObject(hdc, oldFont);
\r
7471 DoReadFile(HANDLE hFile, char *buf, int count, DWORD *outCount,
\r
7477 if( count <= 0 ) {
\r
7478 if (appData.debugMode) {
\r
7479 fprintf( debugFP, "DoReadFile: trying to read past end of buffer, overflow = %d\n", count );
\r
7482 return ERROR_INVALID_USER_BUFFER;
\r
7485 ResetEvent(ovl->hEvent);
\r
7486 ovl->Offset = ovl->OffsetHigh = 0;
\r
7487 ok = ReadFile(hFile, buf, count, outCount, ovl);
\r
7491 err = GetLastError();
\r
7492 if (err == ERROR_IO_PENDING) {
\r
7493 ok = GetOverlappedResult(hFile, ovl, outCount, TRUE);
\r
7497 err = GetLastError();
\r
7504 DoWriteFile(HANDLE hFile, char *buf, int count, DWORD *outCount,
\r
7509 ResetEvent(ovl->hEvent);
\r
7510 ovl->Offset = ovl->OffsetHigh = 0;
\r
7511 ok = WriteFile(hFile, buf, count, outCount, ovl);
\r
7515 err = GetLastError();
\r
7516 if (err == ERROR_IO_PENDING) {
\r
7517 ok = GetOverlappedResult(hFile, ovl, outCount, TRUE);
\r
7521 err = GetLastError();
\r
7527 /* [AS] If input is line by line and a line exceed the buffer size, force an error */
\r
7528 void CheckForInputBufferFull( InputSource * is )
\r
7530 if( is->lineByLine && (is->next - is->buf) >= INPUT_SOURCE_BUF_SIZE ) {
\r
7531 /* Look for end of line */
\r
7532 char * p = is->buf;
\r
7534 while( p < is->next && *p != '\n' ) {
\r
7538 if( p >= is->next ) {
\r
7539 if (appData.debugMode) {
\r
7540 fprintf( debugFP, "Input line exceeded buffer size (source id=%lu)\n", is->id );
\r
7543 is->error = ERROR_BROKEN_PIPE; /* [AS] Just any non-successful code! */
\r
7544 is->count = (DWORD) -1;
\r
7545 is->next = is->buf;
\r
7551 InputThread(LPVOID arg)
\r
7556 is = (InputSource *) arg;
\r
7557 ovl.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
\r
7558 ovl.Internal = ovl.InternalHigh = ovl.Offset = ovl.OffsetHigh = 0;
\r
7559 while (is->hThread != NULL) {
\r
7560 is->error = DoReadFile(is->hFile, is->next,
\r
7561 INPUT_SOURCE_BUF_SIZE - (is->next - is->buf),
\r
7562 &is->count, &ovl);
\r
7563 if (is->error == NO_ERROR) {
\r
7564 is->next += is->count;
\r
7566 if (is->error == ERROR_BROKEN_PIPE) {
\r
7567 /* Correct for MS brain damage. EOF reading a pipe is not an error. */
\r
7570 is->count = (DWORD) -1;
\r
7571 /* [AS] The (is->count <= 0) check below is not useful for unsigned values! */
\r
7576 CheckForInputBufferFull( is );
\r
7578 SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);
\r
7580 if( is->count == ((DWORD) -1) ) break; /* [AS] */
\r
7582 if (is->count <= 0) break; /* Quit on EOF or error */
\r
7585 CloseHandle(ovl.hEvent);
\r
7586 CloseHandle(is->hFile);
\r
7588 if (appData.debugMode) {
\r
7589 fprintf( debugFP, "Input thread terminated (id=%lu, error=%d, count=%ld)\n", is->id, is->error, is->count );
\r
7596 /* Windows 95 beta 2 won't let you do overlapped i/o on a console or pipe */
\r
7598 NonOvlInputThread(LPVOID arg)
\r
7605 is = (InputSource *) arg;
\r
7606 while (is->hThread != NULL) {
\r
7607 is->error = ReadFile(is->hFile, is->next,
\r
7608 INPUT_SOURCE_BUF_SIZE - (is->next - is->buf),
\r
7609 &is->count, NULL) ? NO_ERROR : GetLastError();
\r
7610 if (is->error == NO_ERROR) {
\r
7611 /* Change CRLF to LF */
\r
7612 if (is->next > is->buf) {
\r
7614 i = is->count + 1;
\r
7622 if (prev == '\r' && *p == '\n') {
\r
7634 if (is->error == ERROR_BROKEN_PIPE) {
\r
7635 /* Correct for MS brain damage. EOF reading a pipe is not an error. */
\r
7638 is->count = (DWORD) -1;
\r
7642 CheckForInputBufferFull( is );
\r
7644 SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);
\r
7646 if( is->count == ((DWORD) -1) ) break; /* [AS] */
\r
7648 if (is->count < 0) break; /* Quit on error */
\r
7650 CloseHandle(is->hFile);
\r
7655 SocketInputThread(LPVOID arg)
\r
7659 is = (InputSource *) arg;
\r
7660 while (is->hThread != NULL) {
\r
7661 is->count = recv(is->sock, is->buf, INPUT_SOURCE_BUF_SIZE, 0);
\r
7662 if ((int)is->count == SOCKET_ERROR) {
\r
7663 is->count = (DWORD) -1;
\r
7664 is->error = WSAGetLastError();
\r
7666 is->error = NO_ERROR;
\r
7667 is->next += is->count;
\r
7668 if (is->count == 0 && is->second == is) {
\r
7669 /* End of file on stderr; quit with no message */
\r
7673 SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);
\r
7675 if( is->count == ((DWORD) -1) ) break; /* [AS] */
\r
7677 if (is->count <= 0) break; /* Quit on EOF or error */
\r
7683 InputEvent(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
\r
7687 is = (InputSource *) lParam;
\r
7688 if (is->lineByLine) {
\r
7689 /* Feed in lines one by one */
\r
7690 char *p = is->buf;
\r
7692 while (q < is->next) {
\r
7693 if (*q++ == '\n') {
\r
7694 (is->func)(is, is->closure, p, q - p, NO_ERROR);
\r
7699 /* Move any partial line to the start of the buffer */
\r
7701 while (p < is->next) {
\r
7706 if (is->error != NO_ERROR || is->count == 0) {
\r
7707 /* Notify backend of the error. Note: If there was a partial
\r
7708 line at the end, it is not flushed through. */
\r
7709 (is->func)(is, is->closure, is->buf, is->count, is->error);
\r
7712 /* Feed in the whole chunk of input at once */
\r
7713 (is->func)(is, is->closure, is->buf, is->count, is->error);
\r
7714 is->next = is->buf;
\r
7718 /*---------------------------------------------------------------------------*\
\r
7720 * Menu enables. Used when setting various modes.
\r
7722 \*---------------------------------------------------------------------------*/
\r
7730 GreyRevert(Boolean grey)
\r
7731 { // [HGM] vari: for retracting variations in local mode
\r
7732 HMENU hmenu = GetMenu(hwndMain);
\r
7733 EnableMenuItem(hmenu, IDM_Revert, MF_BYCOMMAND|(grey ? MF_GRAYED : MF_ENABLED));
\r
7734 EnableMenuItem(hmenu, IDM_Annotate, MF_BYCOMMAND|(grey ? MF_GRAYED : MF_ENABLED));
\r
7738 SetMenuEnables(HMENU hmenu, Enables *enab)
\r
7740 while (enab->item > 0) {
\r
7741 (void) EnableMenuItem(hmenu, enab->item, enab->flags);
\r
7746 Enables gnuEnables[] = {
\r
7747 { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },
\r
7748 { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },
\r
7749 { IDM_IcsClient, MF_BYCOMMAND|MF_GRAYED },
\r
7750 { IDM_Accept, MF_BYCOMMAND|MF_GRAYED },
\r
7751 { IDM_Decline, MF_BYCOMMAND|MF_GRAYED },
\r
7752 { IDM_Rematch, MF_BYCOMMAND|MF_GRAYED },
\r
7753 { IDM_Adjourn, MF_BYCOMMAND|MF_GRAYED },
\r
7754 { IDM_StopExamining, MF_BYCOMMAND|MF_GRAYED },
\r
7755 { IDM_StopObserving, MF_BYCOMMAND|MF_GRAYED },
\r
7756 { IDM_Upload, MF_BYCOMMAND|MF_GRAYED },
\r
7757 { IDM_Revert, MF_BYCOMMAND|MF_GRAYED },
\r
7758 { IDM_Annotate, MF_BYCOMMAND|MF_GRAYED },
\r
7759 { IDM_NewChat, MF_BYCOMMAND|MF_GRAYED },
\r
7761 // Needed to switch from ncp to GNU mode on Engine Load
\r
7762 { ACTION_POS, MF_BYPOSITION|MF_ENABLED },
\r
7763 { IDM_MachineWhite, MF_BYCOMMAND|MF_ENABLED },
\r
7764 { IDM_MachineBlack, MF_BYCOMMAND|MF_ENABLED },
\r
7765 { IDM_TwoMachines, MF_BYCOMMAND|MF_ENABLED },
\r
7766 { IDM_Match, MF_BYCOMMAND|MF_ENABLED },
\r
7767 { IDM_AnalysisMode, MF_BYCOMMAND|MF_ENABLED },
\r
7768 { IDM_AnalyzeFile, MF_BYCOMMAND|MF_ENABLED },
\r
7769 { IDM_Engine1Options, MF_BYCOMMAND|MF_ENABLED },
\r
7770 { IDM_Engine2Options, MF_BYCOMMAND|MF_ENABLED },
\r
7771 { IDM_TimeControl, MF_BYCOMMAND|MF_ENABLED },
\r
7772 { IDM_RetractMove, MF_BYCOMMAND|MF_ENABLED },
\r
7773 { IDM_MoveNow, MF_BYCOMMAND|MF_ENABLED },
\r
7774 { IDM_Hint, MF_BYCOMMAND|MF_ENABLED },
\r
7775 { IDM_Book, MF_BYCOMMAND|MF_ENABLED },
\r
7779 Enables icsEnables[] = {
\r
7780 { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },
\r
7781 { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },
\r
7782 { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },
\r
7783 { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },
\r
7784 { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },
\r
7785 { IDM_Match, MF_BYCOMMAND|MF_GRAYED },
\r
7786 { IDM_MachineBoth, MF_BYCOMMAND|MF_GRAYED },
\r
7787 { IDM_AnalysisMode, MF_BYCOMMAND|MF_ENABLED },
\r
7788 { IDM_AnalyzeFile, MF_BYCOMMAND|MF_GRAYED },
\r
7789 { IDM_TimeControl, MF_BYCOMMAND|MF_GRAYED },
\r
7790 { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },
\r
7791 { IDM_Hint, MF_BYCOMMAND|MF_GRAYED },
\r
7792 { IDM_Book, MF_BYCOMMAND|MF_GRAYED },
\r
7793 { IDM_EditProgs2, MF_BYCOMMAND|MF_GRAYED },
\r
7794 { IDM_IcsOptions, MF_BYCOMMAND|MF_ENABLED },
\r
7795 { IDM_Engine1Options, MF_BYCOMMAND|MF_GRAYED },
\r
7796 { IDM_Engine2Options, MF_BYCOMMAND|MF_GRAYED },
\r
7797 { IDM_Annotate, MF_BYCOMMAND|MF_GRAYED },
\r
7798 { IDM_Tourney, MF_BYCOMMAND|MF_GRAYED },
\r
7803 Enables zippyEnables[] = {
\r
7804 { IDM_MoveNow, MF_BYCOMMAND|MF_ENABLED },
\r
7805 { IDM_Hint, MF_BYCOMMAND|MF_ENABLED },
\r
7806 { IDM_Book, MF_BYCOMMAND|MF_ENABLED },
\r
7807 { IDM_Engine1Options, MF_BYCOMMAND|MF_ENABLED },
\r
7812 Enables ncpEnables[] = {
\r
7813 { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },
\r
7814 { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },
\r
7815 { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },
\r
7816 { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },
\r
7817 { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },
\r
7818 { IDM_Match, MF_BYCOMMAND|MF_GRAYED },
\r
7819 { IDM_AnalysisMode, MF_BYCOMMAND|MF_GRAYED },
\r
7820 { IDM_AnalyzeFile, MF_BYCOMMAND|MF_GRAYED },
\r
7821 { IDM_IcsClient, MF_BYCOMMAND|MF_GRAYED },
\r
7822 { ACTION_POS, MF_BYPOSITION|MF_GRAYED },
\r
7823 { IDM_Revert, MF_BYCOMMAND|MF_GRAYED },
\r
7824 { IDM_Annotate, MF_BYCOMMAND|MF_GRAYED },
\r
7825 { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },
\r
7826 { IDM_RetractMove, MF_BYCOMMAND|MF_GRAYED },
\r
7827 { IDM_TimeControl, MF_BYCOMMAND|MF_GRAYED },
\r
7828 { IDM_Hint, MF_BYCOMMAND|MF_GRAYED },
\r
7829 { IDM_Book, MF_BYCOMMAND|MF_GRAYED },
\r
7830 { IDM_MachineBoth, MF_BYCOMMAND|MF_GRAYED },
\r
7831 { IDM_NewChat, MF_BYCOMMAND|MF_GRAYED },
\r
7832 { IDM_Engine1Options, MF_BYCOMMAND|MF_GRAYED },
\r
7833 { IDM_Engine2Options, MF_BYCOMMAND|MF_GRAYED },
\r
7834 { IDM_Sounds, MF_BYCOMMAND|MF_GRAYED },
\r
7838 Enables trainingOnEnables[] = {
\r
7839 { IDM_EditComment, MF_BYCOMMAND|MF_GRAYED },
\r
7840 { IDM_Comment, MF_BYCOMMAND|MF_GRAYED },
\r
7841 { IDM_Pause, MF_BYCOMMAND|MF_GRAYED },
\r
7842 { IDM_Forward, MF_BYCOMMAND|MF_GRAYED },
\r
7843 { IDM_Backward, MF_BYCOMMAND|MF_GRAYED },
\r
7844 { IDM_ToEnd, MF_BYCOMMAND|MF_GRAYED },
\r
7845 { IDM_ToStart, MF_BYCOMMAND|MF_GRAYED },
\r
7846 { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },
\r
7847 { IDM_TruncateGame, MF_BYCOMMAND|MF_GRAYED },
\r
7851 Enables trainingOffEnables[] = {
\r
7852 { IDM_EditComment, MF_BYCOMMAND|MF_ENABLED },
\r
7853 { IDM_Comment, MF_BYCOMMAND|MF_ENABLED },
\r
7854 { IDM_Pause, MF_BYCOMMAND|MF_ENABLED },
\r
7855 { IDM_Forward, MF_BYCOMMAND|MF_ENABLED },
\r
7856 { IDM_Backward, MF_BYCOMMAND|MF_ENABLED },
\r
7857 { IDM_ToEnd, MF_BYCOMMAND|MF_ENABLED },
\r
7858 { IDM_ToStart, MF_BYCOMMAND|MF_ENABLED },
\r
7859 { IDM_MoveNow, MF_BYCOMMAND|MF_ENABLED },
\r
7860 { IDM_TruncateGame, MF_BYCOMMAND|MF_ENABLED },
\r
7864 /* These modify either ncpEnables or gnuEnables */
\r
7865 Enables cmailEnables[] = {
\r
7866 { IDM_MailMove, MF_BYCOMMAND|MF_ENABLED },
\r
7867 { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_ENABLED },
\r
7868 { ACTION_POS, MF_BYPOSITION|MF_ENABLED },
\r
7869 { IDM_CallFlag, MF_BYCOMMAND|MF_GRAYED },
\r
7870 { IDM_Draw, MF_BYCOMMAND|MF_ENABLED },
\r
7871 { IDM_Adjourn, MF_BYCOMMAND|MF_GRAYED },
\r
7872 { IDM_Abort, MF_BYCOMMAND|MF_GRAYED },
\r
7876 Enables machineThinkingEnables[] = {
\r
7877 { IDM_LoadGame, MF_BYCOMMAND|MF_GRAYED },
\r
7878 { IDM_LoadNextGame, MF_BYCOMMAND|MF_GRAYED },
\r
7879 { IDM_LoadPrevGame, MF_BYCOMMAND|MF_GRAYED },
\r
7880 { IDM_ReloadGame, MF_BYCOMMAND|MF_GRAYED },
\r
7881 { IDM_PasteGame, MF_BYCOMMAND|MF_GRAYED },
\r
7882 { IDM_LoadPosition, MF_BYCOMMAND|MF_GRAYED },
\r
7883 { IDM_LoadNextPosition, MF_BYCOMMAND|MF_GRAYED },
\r
7884 { IDM_LoadPrevPosition, MF_BYCOMMAND|MF_GRAYED },
\r
7885 { IDM_ReloadPosition, MF_BYCOMMAND|MF_GRAYED },
\r
7886 { IDM_PastePosition, MF_BYCOMMAND|MF_GRAYED },
\r
7887 { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },
\r
7888 { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },
\r
7889 { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },
\r
7890 // { IDM_Match, MF_BYCOMMAND|MF_GRAYED },
\r
7891 { IDM_TypeInMove, MF_BYCOMMAND|MF_GRAYED },
\r
7892 { IDM_RetractMove, MF_BYCOMMAND|MF_GRAYED },
\r
7896 Enables userThinkingEnables[] = {
\r
7897 { IDM_LoadGame, MF_BYCOMMAND|MF_ENABLED },
\r
7898 { IDM_LoadNextGame, MF_BYCOMMAND|MF_ENABLED },
\r
7899 { IDM_LoadPrevGame, MF_BYCOMMAND|MF_ENABLED },
\r
7900 { IDM_ReloadGame, MF_BYCOMMAND|MF_ENABLED },
\r
7901 { IDM_PasteGame, MF_BYCOMMAND|MF_ENABLED },
\r
7902 { IDM_LoadPosition, MF_BYCOMMAND|MF_ENABLED },
\r
7903 { IDM_LoadNextPosition, MF_BYCOMMAND|MF_ENABLED },
\r
7904 { IDM_LoadPrevPosition, MF_BYCOMMAND|MF_ENABLED },
\r
7905 { IDM_ReloadPosition, MF_BYCOMMAND|MF_ENABLED },
\r
7906 { IDM_PastePosition, MF_BYCOMMAND|MF_ENABLED },
\r
7907 { IDM_MachineWhite, MF_BYCOMMAND|MF_ENABLED },
\r
7908 { IDM_MachineBlack, MF_BYCOMMAND|MF_ENABLED },
\r
7909 { IDM_TwoMachines, MF_BYCOMMAND|MF_ENABLED },
\r
7910 // { IDM_Match, MF_BYCOMMAND|MF_ENABLED },
\r
7911 { IDM_TypeInMove, MF_BYCOMMAND|MF_ENABLED },
\r
7912 { IDM_RetractMove, MF_BYCOMMAND|MF_ENABLED },
\r
7916 /*---------------------------------------------------------------------------*\
\r
7918 * Front-end interface functions exported by XBoard.
\r
7919 * Functions appear in same order as prototypes in frontend.h.
\r
7921 \*---------------------------------------------------------------------------*/
\r
7923 CheckMark(UINT item, int state)
\r
7925 if(item) CheckMenuItem(GetMenu(hwndMain), item, MF_BYCOMMAND|state);
\r
7931 static UINT prevChecked = 0;
\r
7932 static int prevPausing = 0;
\r
7935 if (pausing != prevPausing) {
\r
7936 prevPausing = pausing;
\r
7937 (void) CheckMenuItem(GetMenu(hwndMain), IDM_Pause,
\r
7938 MF_BYCOMMAND|(pausing ? MF_CHECKED : MF_UNCHECKED));
\r
7939 if (hwndPause) SetWindowText(hwndPause, pausing ? "C" : "P");
\r
7942 switch (gameMode) {
\r
7943 case BeginningOfGame:
\r
7944 if (appData.icsActive)
\r
7945 nowChecked = IDM_IcsClient;
\r
7946 else if (appData.noChessProgram)
\r
7947 nowChecked = IDM_EditGame;
\r
7949 nowChecked = IDM_MachineBlack;
\r
7951 case MachinePlaysBlack:
\r
7952 nowChecked = IDM_MachineBlack;
\r
7954 case MachinePlaysWhite:
\r
7955 nowChecked = IDM_MachineWhite;
\r
7957 case TwoMachinesPlay:
\r
7958 nowChecked = IDM_TwoMachines;
\r
7961 nowChecked = IDM_AnalysisMode;
\r
7964 nowChecked = IDM_AnalyzeFile;
\r
7967 nowChecked = IDM_EditGame;
\r
7969 case PlayFromGameFile:
\r
7970 nowChecked = IDM_LoadGame;
\r
7972 case EditPosition:
\r
7973 nowChecked = IDM_EditPosition;
\r
7976 nowChecked = IDM_Training;
\r
7978 case IcsPlayingWhite:
\r
7979 case IcsPlayingBlack:
\r
7980 case IcsObserving:
\r
7982 nowChecked = IDM_IcsClient;
\r
7989 CheckMark(prevChecked, MF_UNCHECKED);
\r
7990 CheckMark(nowChecked, MF_CHECKED);
\r
7991 CheckMark(IDM_Match, matchMode && matchGame < appData.matchGames ? MF_CHECKED : MF_UNCHECKED);
\r
7993 if (nowChecked == IDM_LoadGame || nowChecked == IDM_Training) {
\r
7994 (void) EnableMenuItem(GetMenu(hwndMain), IDM_Training,
\r
7995 MF_BYCOMMAND|MF_ENABLED);
\r
7997 (void) EnableMenuItem(GetMenu(hwndMain),
\r
7998 IDM_Training, MF_BYCOMMAND|MF_GRAYED);
\r
8001 prevChecked = nowChecked;
\r
8003 /* [DM] icsEngineAnalyze - Do a sceure check too */
\r
8004 if (appData.icsActive) {
\r
8005 if (appData.icsEngineAnalyze) {
\r
8006 CheckMark(IDM_AnalysisMode, MF_CHECKED);
\r
8008 CheckMark(IDM_AnalysisMode, MF_UNCHECKED);
\r
8011 DisplayLogos(); // [HGM] logos: mode change could have altered logos
\r
8017 HMENU hmenu = GetMenu(hwndMain);
\r
8018 SetMenuEnables(hmenu, icsEnables);
\r
8019 EnableMenuItem(GetSubMenu(hmenu, OPTIONS_POS), IDM_IcsOptions,
\r
8020 MF_BYCOMMAND|MF_ENABLED);
\r
8022 if (appData.zippyPlay) {
\r
8023 SetMenuEnables(hmenu, zippyEnables);
\r
8024 if (!appData.noChessProgram) /* [DM] icsEngineAnalyze */
\r
8025 (void) EnableMenuItem(GetMenu(hwndMain), IDM_AnalysisMode,
\r
8026 MF_BYCOMMAND|MF_ENABLED);
\r
8034 SetMenuEnables(GetMenu(hwndMain), gnuEnables);
\r
8040 HMENU hmenu = GetMenu(hwndMain);
\r
8041 SetMenuEnables(hmenu, ncpEnables);
\r
8042 DrawMenuBar(hwndMain);
\r
8048 SetMenuEnables(GetMenu(hwndMain), cmailEnables);
\r
8052 SetTrainingModeOn()
\r
8055 SetMenuEnables(GetMenu(hwndMain), trainingOnEnables);
\r
8056 for (i = 0; i < N_BUTTONS; i++) {
\r
8057 if (buttonDesc[i].hwnd != NULL)
\r
8058 EnableWindow(buttonDesc[i].hwnd, FALSE);
\r
8063 VOID SetTrainingModeOff()
\r
8066 SetMenuEnables(GetMenu(hwndMain), trainingOffEnables);
\r
8067 for (i = 0; i < N_BUTTONS; i++) {
\r
8068 if (buttonDesc[i].hwnd != NULL)
\r
8069 EnableWindow(buttonDesc[i].hwnd, TRUE);
\r
8075 SetUserThinkingEnables()
\r
8077 SetMenuEnables(GetMenu(hwndMain), userThinkingEnables);
\r
8081 SetMachineThinkingEnables()
\r
8083 HMENU hMenu = GetMenu(hwndMain);
\r
8084 int flags = MF_BYCOMMAND|MF_ENABLED;
\r
8086 SetMenuEnables(hMenu, machineThinkingEnables);
\r
8088 if (gameMode == MachinePlaysBlack) {
\r
8089 (void)EnableMenuItem(hMenu, IDM_MachineBlack, flags);
\r
8090 } else if (gameMode == MachinePlaysWhite) {
\r
8091 (void)EnableMenuItem(hMenu, IDM_MachineWhite, flags);
\r
8092 } else if (gameMode == TwoMachinesPlay) {
\r
8093 (void)EnableMenuItem(hMenu, matchMode ? IDM_Match : IDM_TwoMachines, flags); // [HGM] match
\r
8099 DisplayTitle(char *str)
\r
8101 char title[MSG_SIZ], *host;
\r
8102 if (str[0] != NULLCHAR) {
\r
8103 safeStrCpy(title, str, sizeof(title)/sizeof(title[0]) );
\r
8104 } else if (appData.icsActive) {
\r
8105 if (appData.icsCommPort[0] != NULLCHAR)
\r
8108 host = appData.icsHost;
\r
8109 snprintf(title, MSG_SIZ, "%s: %s", szTitle, host);
\r
8110 } else if (appData.noChessProgram) {
\r
8111 safeStrCpy(title, szTitle, sizeof(title)/sizeof(title[0]) );
\r
8113 safeStrCpy(title, szTitle, sizeof(title)/sizeof(title[0]) );
\r
8114 strcat(title, ": ");
\r
8115 strcat(title, first.tidy);
\r
8117 SetWindowText(hwndMain, title);
\r
8122 DisplayMessage(char *str1, char *str2)
\r
8126 int remain = MESSAGE_TEXT_MAX - 1;
\r
8129 moveErrorMessageUp = FALSE; /* turned on later by caller if needed */
\r
8130 messageText[0] = NULLCHAR;
\r
8132 len = strlen(str1);
\r
8133 if (len > remain) len = remain;
\r
8134 strncpy(messageText, str1, len);
\r
8135 messageText[len] = NULLCHAR;
\r
8138 if (*str2 && remain >= 2) {
\r
8140 strcat(messageText, " ");
\r
8143 len = strlen(str2);
\r
8144 if (len > remain) len = remain;
\r
8145 strncat(messageText, str2, len);
\r
8147 messageText[MESSAGE_TEXT_MAX - 1] = NULLCHAR;
8148 safeStrCpy(lastMsg, messageText, MSG_SIZ);
8150 if (hwndMain == NULL || IsIconic(hwndMain)) return;
\r
8154 hdc = GetDC(hwndMain);
\r
8155 oldFont = SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);
\r
8156 ExtTextOut(hdc, messageRect.left, messageRect.top, ETO_CLIPPED|ETO_OPAQUE,
\r
8157 &messageRect, messageText, strlen(messageText), NULL);
\r
8158 (void) SelectObject(hdc, oldFont);
\r
8159 (void) ReleaseDC(hwndMain, hdc);
\r
8163 DisplayError(char *str, int error)
\r
8165 char buf[MSG_SIZ*2], buf2[MSG_SIZ];
\r
8169 safeStrCpy(buf, str, sizeof(buf)/sizeof(buf[0]) );
\r
8171 len = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,
\r
8172 NULL, error, LANG_NEUTRAL,
\r
8173 (LPSTR) buf2, MSG_SIZ, NULL);
\r
8175 snprintf(buf, 2*MSG_SIZ, "%s:\n%s", str, buf2);
\r
8177 ErrorMap *em = errmap;
\r
8178 while (em->err != 0 && em->err != error) em++;
\r
8179 if (em->err != 0) {
\r
8180 snprintf(buf, 2*MSG_SIZ, "%s:\n%s", str, em->msg);
\r
8182 snprintf(buf, 2*MSG_SIZ, "%s:\nError code %d", str, error);
\r
8187 ErrorPopUp(_("Error"), buf);
\r
8192 DisplayMoveError(char *str)
\r
8194 fromX = fromY = -1;
\r
8195 ClearHighlights();
\r
8196 DrawPosition(FALSE, NULL);
\r
8197 if (appData.popupMoveErrors) {
\r
8198 ErrorPopUp(_("Error"), str);
\r
8200 DisplayMessage(str, "");
\r
8201 moveErrorMessageUp = TRUE;
\r
8206 DisplayFatalError(char *str, int error, int exitStatus)
\r
8208 char buf[2*MSG_SIZ], buf2[MSG_SIZ];
\r
8210 char *label = exitStatus ? _("Fatal Error") : _("Exiting");
\r
8213 len = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,
\r
8214 NULL, error, LANG_NEUTRAL,
\r
8215 (LPSTR) buf2, MSG_SIZ, NULL);
\r
8217 snprintf(buf, 2*MSG_SIZ, "%s:\n%s", str, buf2);
\r
8219 ErrorMap *em = errmap;
\r
8220 while (em->err != 0 && em->err != error) em++;
\r
8221 if (em->err != 0) {
\r
8222 snprintf(buf, 2*MSG_SIZ, "%s:\n%s", str, em->msg);
\r
8224 snprintf(buf, 2*MSG_SIZ, "%s:\nError code %d", str, error);
\r
8229 if (appData.debugMode) {
\r
8230 fprintf(debugFP, "%s: %s\n", label, str);
\r
8232 if (appData.popupExitMessage) {
\r
8233 (void) MessageBox(hwndMain, str, label, MB_OK|
\r
8234 (exitStatus ? MB_ICONSTOP : MB_ICONINFORMATION));
\r
8236 ExitEvent(exitStatus);
\r
8241 DisplayInformation(char *str)
\r
8243 (void) MessageBox(hwndMain, str, _("Information"), MB_OK|MB_ICONINFORMATION);
\r
8248 DisplayNote(char *str)
\r
8250 ErrorPopUp(_("Note"), str);
\r
8255 char *title, *question, *replyPrefix;
\r
8260 QuestionDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
8262 static QuestionParams *qp;
\r
8263 char reply[MSG_SIZ];
\r
8266 switch (message) {
\r
8267 case WM_INITDIALOG:
\r
8268 qp = (QuestionParams *) lParam;
\r
8269 CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));
\r
8270 Translate(hDlg, DLG_Question);
\r
8271 SetWindowText(hDlg, qp->title);
\r
8272 SetDlgItemText(hDlg, OPT_QuestionText, qp->question);
\r
8273 SetFocus(GetDlgItem(hDlg, OPT_QuestionInput));
\r
8277 switch (LOWORD(wParam)) {
\r
8279 safeStrCpy(reply, qp->replyPrefix, sizeof(reply)/sizeof(reply[0]) );
\r
8280 if (*reply) strcat(reply, " ");
\r
8281 len = strlen(reply);
\r
8282 GetDlgItemText(hDlg, OPT_QuestionInput, reply + len, sizeof(reply) - len);
\r
8283 strcat(reply, "\n");
\r
8284 OutputToProcess(qp->pr, reply, strlen(reply), &err);
\r
8285 EndDialog(hDlg, TRUE);
\r
8286 if (err) DisplayFatalError(_("Error writing to chess program"), err, 1);
\r
8289 EndDialog(hDlg, FALSE);
\r
8300 AskQuestion(char* title, char *question, char *replyPrefix, ProcRef pr)
\r
8302 QuestionParams qp;
\r
8306 qp.question = question;
\r
8307 qp.replyPrefix = replyPrefix;
\r
8309 lpProc = MakeProcInstance((FARPROC)QuestionDialog, hInst);
\r
8310 DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_Question),
\r
8311 hwndMain, (DLGPROC)lpProc, (LPARAM)&qp);
\r
8312 FreeProcInstance(lpProc);
\r
8315 /* [AS] Pick FRC position */
\r
8316 LRESULT CALLBACK NewGameFRC_Proc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
8318 static int * lpIndexFRC;
\r
8324 case WM_INITDIALOG:
\r
8325 lpIndexFRC = (int *) lParam;
\r
8327 CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));
\r
8328 Translate(hDlg, DLG_NewGameFRC);
\r
8330 SendDlgItemMessage( hDlg, IDC_NFG_Edit, EM_SETLIMITTEXT, sizeof(buf)-1, 0 );
\r
8331 SetDlgItemInt( hDlg, IDC_NFG_Edit, *lpIndexFRC, TRUE );
\r
8332 SendDlgItemMessage( hDlg, IDC_NFG_Edit, EM_SETSEL, 0, -1 );
\r
8333 SetFocus(GetDlgItem(hDlg, IDC_NFG_Edit));
\r
8338 switch( LOWORD(wParam) ) {
\r
8340 *lpIndexFRC = GetDlgItemInt(hDlg, IDC_NFG_Edit, &index_is_ok, TRUE );
\r
8341 EndDialog( hDlg, 0 );
\r
8342 shuffleOpenings = TRUE; /* [HGM] shuffle: switch shuffling on for as long as we stay in current variant */
\r
8345 EndDialog( hDlg, 1 );
\r
8347 case IDC_NFG_Edit:
\r
8348 if( HIWORD(wParam) == EN_CHANGE ) {
\r
8349 GetDlgItemInt(hDlg, IDC_NFG_Edit, &index_is_ok, TRUE );
\r
8351 EnableWindow( GetDlgItem(hDlg, IDOK), index_is_ok );
\r
8354 case IDC_NFG_Random:
\r
8355 snprintf( buf, sizeof(buf)/sizeof(buf[0]), "%d", myrandom() ); /* [HGM] shuffle: no longer limit to 960 */
\r
8356 SetDlgItemText(hDlg, IDC_NFG_Edit, buf );
\r
8369 int index = appData.defaultFrcPosition;
\r
8370 FARPROC lpProc = MakeProcInstance( (FARPROC) NewGameFRC_Proc, hInst );
\r
8372 result = DialogBoxParam( hInst, MAKEINTRESOURCE(DLG_NewGameFRC), hwndMain, (DLGPROC)lpProc, (LPARAM)&index );
\r
8374 if( result == 0 ) {
\r
8375 appData.defaultFrcPosition = index;
\r
8381 /* [AS] Game list options. Refactored by HGM */
\r
8383 HWND gameListOptionsDialog;
\r
8385 // low-level front-end: clear text edit / list widget
\r
8389 SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_RESETCONTENT, 0, 0 );
\r
8392 // low-level front-end: clear text edit / list widget
\r
8394 GLT_DeSelectList()
\r
8396 SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_SETCURSEL, 0, 0 );
\r
8399 // low-level front-end: append line to text edit / list widget
\r
8401 GLT_AddToList( char *name )
\r
8404 SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_ADDSTRING, 0, (LPARAM) name );
\r
8408 // low-level front-end: get line from text edit / list widget
\r
8410 GLT_GetFromList( int index, char *name )
\r
8413 if( SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_GETTEXT, index, (LPARAM) name ) != LB_ERR )
\r
8419 void GLT_MoveSelection( HWND hDlg, int delta )
\r
8421 int idx1 = (int) SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETCURSEL, 0, 0 );
\r
8422 int idx2 = idx1 + delta;
\r
8423 int count = (int) SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETCOUNT, 0, 0 );
\r
8425 if( idx1 >=0 && idx1 < count && idx2 >= 0 && idx2 < count ) {
\r
8428 SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETTEXT, idx1, (LPARAM) buf );
\r
8429 SendDlgItemMessage( hDlg, IDC_GameListTags, LB_DELETESTRING, idx1, 0 );
\r
8430 SendDlgItemMessage( hDlg, IDC_GameListTags, LB_INSERTSTRING, idx2, (LPARAM) buf );
\r
8431 SendDlgItemMessage( hDlg, IDC_GameListTags, LB_SETCURSEL, idx2, 0 );
\r
8435 LRESULT CALLBACK GameListOptions_Proc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
8439 case WM_INITDIALOG:
\r
8440 gameListOptionsDialog = hDlg; // [HGM] pass through global to keep out off back-end
\r
8442 CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));
\r
8443 Translate(hDlg, DLG_GameListOptions);
\r
8445 /* Initialize list */
\r
8446 GLT_TagsToList( lpUserGLT );
\r
8448 SetFocus( GetDlgItem(hDlg, IDC_GameListTags) );
\r
8453 switch( LOWORD(wParam) ) {
\r
8456 EndDialog( hDlg, 0 );
\r
8459 EndDialog( hDlg, 1 );
\r
8462 case IDC_GLT_Default:
\r
8463 GLT_TagsToList( GLT_DEFAULT_TAGS );
\r
8466 case IDC_GLT_Restore:
\r
8467 GLT_TagsToList( appData.gameListTags );
\r
8471 GLT_MoveSelection( hDlg, -1 );
\r
8474 case IDC_GLT_Down:
\r
8475 GLT_MoveSelection( hDlg, +1 );
\r
8485 int GameListOptions()
\r
8488 FARPROC lpProc = MakeProcInstance( (FARPROC) GameListOptions_Proc, hInst );
\r
8490 safeStrCpy( lpUserGLT, appData.gameListTags ,LPUSERGLT_SIZE );
\r
8492 result = DialogBoxParam( hInst, MAKEINTRESOURCE(DLG_GameListOptions), hwndMain, (DLGPROC)lpProc, (LPARAM)lpUserGLT );
\r
8494 if( result == 0 ) {
\r
8495 /* [AS] Memory leak here! */
\r
8496 appData.gameListTags = strdup( lpUserGLT );
\r
8503 DisplayIcsInteractionTitle(char *str)
\r
8505 char consoleTitle[MSG_SIZ];
\r
8507 snprintf(consoleTitle, MSG_SIZ, "%s: %s", szConsoleTitle, str);
\r
8508 SetWindowText(hwndConsole, consoleTitle);
\r
8512 DrawPosition(int fullRedraw, Board board)
\r
8514 HDCDrawPosition(NULL, (BOOLEAN) fullRedraw, board);
\r
8517 void NotifyFrontendLogin()
\r
8520 UpdateICSWidth(GetDlgItem(hwndConsole, OPT_ConsoleText));
\r
8526 fromX = fromY = -1;
\r
8527 if (dragInfo.pos.x != -1 || dragInfo.pos.y != -1) {
\r
8528 dragInfo.pos.x = dragInfo.pos.y = -1;
\r
8529 dragInfo.pos.x = dragInfo.pos.y = -1;
\r
8530 dragInfo.lastpos = dragInfo.pos;
\r
8531 dragInfo.start.x = dragInfo.start.y = -1;
\r
8532 dragInfo.from = dragInfo.start;
\r
8534 DrawPosition(TRUE, NULL);
\r
8541 CommentPopUp(char *title, char *str)
\r
8543 HWND hwnd = GetActiveWindow();
\r
8544 EitherCommentPopUp(currentMove, title, str, FALSE); // [HGM] vari: fake move index, rather than 0
\r
8546 SetActiveWindow(hwnd);
\r
8550 CommentPopDown(void)
\r
8552 CheckMenuItem(GetMenu(hwndMain), IDM_Comment, MF_UNCHECKED);
\r
8553 if (commentDialog) {
\r
8554 ShowWindow(commentDialog, SW_HIDE);
\r
8556 commentUp = FALSE;
\r
8560 EditCommentPopUp(int index, char *title, char *str)
\r
8562 EitherCommentPopUp(index, title, str, TRUE);
\r
8569 MyPlaySound(&sounds[(int)SoundMove]);
\r
8572 VOID PlayIcsWinSound()
\r
8574 MyPlaySound(&sounds[(int)SoundIcsWin]);
\r
8577 VOID PlayIcsLossSound()
\r
8579 MyPlaySound(&sounds[(int)SoundIcsLoss]);
\r
8582 VOID PlayIcsDrawSound()
\r
8584 MyPlaySound(&sounds[(int)SoundIcsDraw]);
\r
8587 VOID PlayIcsUnfinishedSound()
\r
8589 MyPlaySound(&sounds[(int)SoundIcsUnfinished]);
\r
8595 MyPlaySound(&sounds[(int)SoundAlarm]);
\r
8603 consoleEcho = TRUE;
\r
8604 hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
8605 SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&consoleCF);
\r
8606 SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);
\r
8615 consoleEcho = FALSE;
\r
8616 hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
8617 /* This works OK: set text and background both to the same color */
\r
8619 cf.crTextColor = COLOR_ECHOOFF;
\r
8620 SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf);
\r
8621 SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, cf.crTextColor);
\r
8624 /* No Raw()...? */
\r
8626 void Colorize(ColorClass cc, int continuation)
\r
8628 currentColorClass = cc;
\r
8629 consoleCF.dwMask = CFM_COLOR|CFM_BOLD|CFM_ITALIC|CFM_UNDERLINE|CFM_STRIKEOUT;
\r
8630 consoleCF.crTextColor = textAttribs[cc].color;
\r
8631 consoleCF.dwEffects = textAttribs[cc].effects;
\r
8632 if (!continuation) MyPlaySound(&textAttribs[cc].sound);
\r
8638 static char buf[MSG_SIZ];
\r
8639 DWORD bufsiz = MSG_SIZ;
\r
8641 if(appData.userName != NULL && appData.userName[0] != 0) {
\r
8642 return appData.userName; /* [HGM] username: prefer name selected by user over his system login */
\r
8644 if (!GetUserName(buf, &bufsiz)) {
\r
8645 /*DisplayError("Error getting user name", GetLastError());*/
\r
8646 safeStrCpy(buf, _("User"), sizeof(buf)/sizeof(buf[0]) );
\r
8654 static char buf[MSG_SIZ];
\r
8655 DWORD bufsiz = MSG_SIZ;
\r
8657 if (!GetComputerName(buf, &bufsiz)) {
\r
8658 /*DisplayError("Error getting host name", GetLastError());*/
\r
8659 safeStrCpy(buf, _("Unknown"), sizeof(buf)/sizeof(buf[0]) );
\r
8666 ClockTimerRunning()
\r
8668 return clockTimerEvent != 0;
\r
8674 if (clockTimerEvent == 0) return FALSE;
\r
8675 KillTimer(hwndMain, clockTimerEvent);
\r
8676 clockTimerEvent = 0;
\r
8681 StartClockTimer(long millisec)
\r
8683 clockTimerEvent = SetTimer(hwndMain, (UINT) CLOCK_TIMER_ID,
\r
8684 (UINT) millisec, NULL);
\r
8688 DisplayWhiteClock(long timeRemaining, int highlight)
\r
8691 char *flag = whiteFlag && gameMode == TwoMachinesPlay ? "(!)" : "";
\r
8693 if(appData.noGUI) return;
\r
8694 hdc = GetDC(hwndMain);
\r
8695 if (!IsIconic(hwndMain)) {
\r
8696 DisplayAClock(hdc, timeRemaining, highlight,
\r
8697 flipClock ? &blackRect : &whiteRect, _("White"), flag);
\r
8699 if (highlight && iconCurrent == iconBlack) {
\r
8700 iconCurrent = iconWhite;
\r
8701 PostMessage(hwndMain, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);
\r
8702 if (IsIconic(hwndMain)) {
\r
8703 DrawIcon(hdc, 2, 2, iconCurrent);
\r
8706 (void) ReleaseDC(hwndMain, hdc);
\r
8708 PostMessage(hwndConsole, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);
\r
8712 DisplayBlackClock(long timeRemaining, int highlight)
\r
8715 char *flag = blackFlag && gameMode == TwoMachinesPlay ? "(!)" : "";
\r
8717 if(appData.noGUI) return;
\r
8718 hdc = GetDC(hwndMain);
\r
8719 if (!IsIconic(hwndMain)) {
\r
8720 DisplayAClock(hdc, timeRemaining, highlight,
\r
8721 flipClock ? &whiteRect : &blackRect, _("Black"), flag);
\r
8723 if (highlight && iconCurrent == iconWhite) {
\r
8724 iconCurrent = iconBlack;
\r
8725 PostMessage(hwndMain, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);
\r
8726 if (IsIconic(hwndMain)) {
\r
8727 DrawIcon(hdc, 2, 2, iconCurrent);
\r
8730 (void) ReleaseDC(hwndMain, hdc);
\r
8732 PostMessage(hwndConsole, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);
\r
8737 LoadGameTimerRunning()
\r
8739 return loadGameTimerEvent != 0;
\r
8743 StopLoadGameTimer()
\r
8745 if (loadGameTimerEvent == 0) return FALSE;
\r
8746 KillTimer(hwndMain, loadGameTimerEvent);
\r
8747 loadGameTimerEvent = 0;
\r
8752 StartLoadGameTimer(long millisec)
\r
8754 loadGameTimerEvent = SetTimer(hwndMain, (UINT) LOAD_GAME_TIMER_ID,
\r
8755 (UINT) millisec, NULL);
\r
8763 char fileTitle[MSG_SIZ];
\r
8765 defName = DefaultFileName(appData.oldSaveStyle ? "gam" : "pgn");
\r
8766 f = OpenFileDialog(hwndMain, "a", defName,
\r
8767 appData.oldSaveStyle ? "gam" : "pgn",
\r
8769 _("Save Game to File"), NULL, fileTitle, NULL);
\r
8771 SaveGame(f, 0, "");
\r
8778 ScheduleDelayedEvent(DelayedEventCallback cb, long millisec)
\r
8780 if (delayedTimerEvent != 0) {
\r
8781 if (appData.debugMode && cb != delayedTimerCallback) { // [HGM] alive: not too much debug
\r
8782 fprintf(debugFP, "ScheduleDelayedEvent: event already scheduled\n");
\r
8784 KillTimer(hwndMain, delayedTimerEvent);
\r
8785 delayedTimerEvent = 0;
\r
8786 if(delayedTimerCallback != cb) // [HGM] alive: do not "flush" same event, just postpone it
\r
8787 delayedTimerCallback();
\r
8789 delayedTimerCallback = cb;
\r
8790 delayedTimerEvent = SetTimer(hwndMain, (UINT) DELAYED_TIMER_ID,
\r
8791 (UINT) millisec, NULL);
\r
8794 DelayedEventCallback
\r
8797 if (delayedTimerEvent) {
\r
8798 return delayedTimerCallback;
\r
8805 CancelDelayedEvent()
\r
8807 if (delayedTimerEvent) {
\r
8808 KillTimer(hwndMain, delayedTimerEvent);
\r
8809 delayedTimerEvent = 0;
\r
8813 DWORD GetWin32Priority(int nice)
\r
8814 { // [HGM] nice: translate Unix nice() value to indows priority class. (Code stolen from Polyglot 1.4w11)
\r
8816 REALTIME_PRIORITY_CLASS 0x00000100
\r
8817 HIGH_PRIORITY_CLASS 0x00000080
\r
8818 ABOVE_NORMAL_PRIORITY_CLASS 0x00008000
\r
8819 NORMAL_PRIORITY_CLASS 0x00000020
\r
8820 BELOW_NORMAL_PRIORITY_CLASS 0x00004000
\r
8821 IDLE_PRIORITY_CLASS 0x00000040
\r
8823 if (nice < -15) return 0x00000080;
\r
8824 if (nice < 0) return 0x00008000;
\r
8825 if (nice == 0) return 0x00000020;
\r
8826 if (nice < 15) return 0x00004000;
\r
8827 return 0x00000040;
\r
8830 /* Start a child process running the given program.
\r
8831 The process's standard output can be read from "from", and its
\r
8832 standard input can be written to "to".
\r
8833 Exit with fatal error if anything goes wrong.
\r
8834 Returns an opaque pointer that can be used to destroy the process
\r
8838 StartChildProcess(char *cmdLine, char *dir, ProcRef *pr)
\r
8840 #define BUFSIZE 4096
\r
8842 HANDLE hChildStdinRd, hChildStdinWr,
\r
8843 hChildStdoutRd, hChildStdoutWr;
\r
8844 HANDLE hChildStdinWrDup, hChildStdoutRdDup;
\r
8845 SECURITY_ATTRIBUTES saAttr;
\r
8847 PROCESS_INFORMATION piProcInfo;
\r
8848 STARTUPINFO siStartInfo;
\r
8850 char buf[MSG_SIZ];
\r
8853 if (appData.debugMode) {
\r
8854 fprintf(debugFP, "StartChildProcess (dir=\"%s\") %s\n", dir, cmdLine);
\r
8859 /* Set the bInheritHandle flag so pipe handles are inherited. */
\r
8860 saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);
\r
8861 saAttr.bInheritHandle = TRUE;
\r
8862 saAttr.lpSecurityDescriptor = NULL;
\r
8865 * The steps for redirecting child's STDOUT:
\r
8866 * 1. Create anonymous pipe to be STDOUT for child.
\r
8867 * 2. Create a noninheritable duplicate of read handle,
\r
8868 * and close the inheritable read handle.
\r
8871 /* Create a pipe for the child's STDOUT. */
\r
8872 if (! CreatePipe(&hChildStdoutRd, &hChildStdoutWr, &saAttr, 0)) {
\r
8873 return GetLastError();
\r
8876 /* Duplicate the read handle to the pipe, so it is not inherited. */
\r
8877 fSuccess = DuplicateHandle(GetCurrentProcess(), hChildStdoutRd,
\r
8878 GetCurrentProcess(), &hChildStdoutRdDup, 0,
\r
8879 FALSE, /* not inherited */
\r
8880 DUPLICATE_SAME_ACCESS);
\r
8882 return GetLastError();
\r
8884 CloseHandle(hChildStdoutRd);
\r
8887 * The steps for redirecting child's STDIN:
\r
8888 * 1. Create anonymous pipe to be STDIN for child.
\r
8889 * 2. Create a noninheritable duplicate of write handle,
\r
8890 * and close the inheritable write handle.
\r
8893 /* Create a pipe for the child's STDIN. */
\r
8894 if (! CreatePipe(&hChildStdinRd, &hChildStdinWr, &saAttr, 0)) {
\r
8895 return GetLastError();
\r
8898 /* Duplicate the write handle to the pipe, so it is not inherited. */
\r
8899 fSuccess = DuplicateHandle(GetCurrentProcess(), hChildStdinWr,
\r
8900 GetCurrentProcess(), &hChildStdinWrDup, 0,
\r
8901 FALSE, /* not inherited */
\r
8902 DUPLICATE_SAME_ACCESS);
\r
8904 return GetLastError();
\r
8906 CloseHandle(hChildStdinWr);
\r
8908 /* Arrange to (1) look in dir for the child .exe file, and
\r
8909 * (2) have dir be the child's working directory. Interpret
\r
8910 * dir relative to the directory WinBoard loaded from. */
\r
8911 GetCurrentDirectory(MSG_SIZ, buf);
\r
8912 SetCurrentDirectory(installDir);
\r
8913 // kludgey way to update logos in tourney, as long as back-end can't do it
\r
8914 if(!strcmp(cmdLine, first.program)) LoadLogo(&first, 0); else
\r
8915 if(!strcmp(cmdLine, second.program)) LoadLogo(&second, 1);
\r
8916 SetCurrentDirectory(dir);
\r
8918 /* Now create the child process. */
\r
8920 siStartInfo.cb = sizeof(STARTUPINFO);
\r
8921 siStartInfo.lpReserved = NULL;
\r
8922 siStartInfo.lpDesktop = NULL;
\r
8923 siStartInfo.lpTitle = NULL;
\r
8924 siStartInfo.dwFlags = STARTF_USESTDHANDLES;
\r
8925 siStartInfo.cbReserved2 = 0;
\r
8926 siStartInfo.lpReserved2 = NULL;
\r
8927 siStartInfo.hStdInput = hChildStdinRd;
\r
8928 siStartInfo.hStdOutput = hChildStdoutWr;
\r
8929 siStartInfo.hStdError = hChildStdoutWr;
\r
8931 fSuccess = CreateProcess(NULL,
\r
8932 cmdLine, /* command line */
\r
8933 NULL, /* process security attributes */
\r
8934 NULL, /* primary thread security attrs */
\r
8935 TRUE, /* handles are inherited */
\r
8936 DETACHED_PROCESS|CREATE_NEW_PROCESS_GROUP,
\r
8937 NULL, /* use parent's environment */
\r
8939 &siStartInfo, /* STARTUPINFO pointer */
\r
8940 &piProcInfo); /* receives PROCESS_INFORMATION */
\r
8942 err = GetLastError();
\r
8943 SetCurrentDirectory(buf); /* return to prev directory */
\r
8948 if (appData.niceEngines){ // [HGM] nice: adjust engine proc priority
\r
8949 if(appData.debugMode) fprintf(debugFP, "nice engine proc to %d\n", appData.niceEngines);
\r
8950 SetPriorityClass(piProcInfo.hProcess, GetWin32Priority(appData.niceEngines));
\r
8953 /* Close the handles we don't need in the parent */
\r
8954 CloseHandle(piProcInfo.hThread);
\r
8955 CloseHandle(hChildStdinRd);
\r
8956 CloseHandle(hChildStdoutWr);
\r
8958 /* Prepare return value */
\r
8959 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
\r
8960 cp->kind = CPReal;
\r
8961 cp->hProcess = piProcInfo.hProcess;
\r
8962 cp->pid = piProcInfo.dwProcessId;
\r
8963 cp->hFrom = hChildStdoutRdDup;
\r
8964 cp->hTo = hChildStdinWrDup;
\r
8966 *pr = (void *) cp;
\r
8968 /* Klaus Friedel says that this Sleep solves a problem under Windows
\r
8969 2000 where engines sometimes don't see the initial command(s)
\r
8970 from WinBoard and hang. I don't understand how that can happen,
\r
8971 but the Sleep is harmless, so I've put it in. Others have also
\r
8972 reported what may be the same problem, so hopefully this will fix
\r
8973 it for them too. */
\r
8981 DestroyChildProcess(ProcRef pr, int/*boolean*/ signal)
\r
8983 ChildProc *cp; int result;
\r
8985 cp = (ChildProc *) pr;
\r
8986 if (cp == NULL) return;
\r
8988 switch (cp->kind) {
\r
8990 /* TerminateProcess is considered harmful, so... */
\r
8991 CloseHandle(cp->hTo); /* Closing this will give the child an EOF and hopefully kill it */
\r
8992 if (cp->hFrom) CloseHandle(cp->hFrom); /* if NULL, InputThread will close it */
\r
8993 /* The following doesn't work because the chess program
\r
8994 doesn't "have the same console" as WinBoard. Maybe
\r
8995 we could arrange for this even though neither WinBoard
\r
8996 nor the chess program uses a console for stdio? */
\r
8997 /*!!if (signal) GenerateConsoleCtrlEvent(CTRL_BREAK_EVENT, cp->pid);*/
\r
8999 /* [AS] Special termination modes for misbehaving programs... */
\r
9000 if( signal == 9 ) {
\r
9001 result = TerminateProcess( cp->hProcess, 0 );
\r
9003 if ( appData.debugMode) {
\r
9004 fprintf( debugFP, "Terminating process %lu, result=%d\n", cp->pid, result );
\r
9007 else if( signal == 10 ) {
\r
9008 DWORD dw = WaitForSingleObject( cp->hProcess, 3*1000 ); // Wait 3 seconds at most
\r
9010 if( dw != WAIT_OBJECT_0 ) {
\r
9011 result = TerminateProcess( cp->hProcess, 0 );
\r
9013 if ( appData.debugMode) {
\r
9014 fprintf( debugFP, "Process %lu still alive after timeout, killing... result=%d\n", cp->pid, result );
\r
9020 CloseHandle(cp->hProcess);
\r
9024 if (cp->hFrom) CloseHandle(cp->hFrom);
\r
9028 closesocket(cp->sock);
\r
9033 if (signal) send(cp->sock2, "\017", 1, 0); /* 017 = 15 = SIGTERM */
\r
9034 closesocket(cp->sock);
\r
9035 closesocket(cp->sock2);
\r
9043 InterruptChildProcess(ProcRef pr)
\r
9047 cp = (ChildProc *) pr;
\r
9048 if (cp == NULL) return;
\r
9049 switch (cp->kind) {
\r
9051 /* The following doesn't work because the chess program
\r
9052 doesn't "have the same console" as WinBoard. Maybe
\r
9053 we could arrange for this even though neither WinBoard
\r
9054 nor the chess program uses a console for stdio */
\r
9055 /*!!GenerateConsoleCtrlEvent(CTRL_C_EVENT, cp->pid);*/
\r
9060 /* Can't interrupt */
\r
9064 send(cp->sock2, "\002", 1, 0); /* 2 = SIGINT */
\r
9071 OpenTelnet(char *host, char *port, ProcRef *pr)
\r
9073 char cmdLine[MSG_SIZ];
\r
9075 if (port[0] == NULLCHAR) {
\r
9076 snprintf(cmdLine, MSG_SIZ, "%s %s", appData.telnetProgram, host);
\r
9078 snprintf(cmdLine, MSG_SIZ, "%s %s %s", appData.telnetProgram, host, port);
\r
9080 return StartChildProcess(cmdLine, "", pr);
\r
9084 /* Code to open TCP sockets */
\r
9087 OpenTCP(char *host, char *port, ProcRef *pr)
\r
9092 struct sockaddr_in sa, mysa;
\r
9093 struct hostent FAR *hp;
\r
9094 unsigned short uport;
\r
9095 WORD wVersionRequested;
\r
9098 /* Initialize socket DLL */
\r
9099 wVersionRequested = MAKEWORD(1, 1);
\r
9100 err = WSAStartup(wVersionRequested, &wsaData);
\r
9101 if (err != 0) return err;
\r
9104 if ((s = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {
\r
9105 err = WSAGetLastError();
\r
9110 /* Bind local address using (mostly) don't-care values.
\r
9112 memset((char *) &mysa, 0, sizeof(struct sockaddr_in));
\r
9113 mysa.sin_family = AF_INET;
\r
9114 mysa.sin_addr.s_addr = INADDR_ANY;
\r
9115 uport = (unsigned short) 0;
\r
9116 mysa.sin_port = htons(uport);
\r
9117 if (bind(s, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))
\r
9118 == SOCKET_ERROR) {
\r
9119 err = WSAGetLastError();
\r
9124 /* Resolve remote host name */
\r
9125 memset((char *) &sa, 0, sizeof(struct sockaddr_in));
\r
9126 if (!(hp = gethostbyname(host))) {
\r
9127 unsigned int b0, b1, b2, b3;
\r
9129 err = WSAGetLastError();
\r
9131 if (sscanf(host, "%u.%u.%u.%u", &b0, &b1, &b2, &b3) == 4) {
\r
9132 hp = (struct hostent *) calloc(1, sizeof(struct hostent));
\r
9133 hp->h_addrtype = AF_INET;
\r
9135 hp->h_addr_list = (char **) calloc(2, sizeof(char *));
\r
9136 hp->h_addr_list[0] = (char *) malloc(4);
\r
9137 hp->h_addr_list[0][0] = (char) b0;
\r
9138 hp->h_addr_list[0][1] = (char) b1;
\r
9139 hp->h_addr_list[0][2] = (char) b2;
\r
9140 hp->h_addr_list[0][3] = (char) b3;
\r
9146 sa.sin_family = hp->h_addrtype;
\r
9147 uport = (unsigned short) atoi(port);
\r
9148 sa.sin_port = htons(uport);
\r
9149 memcpy((char *) &sa.sin_addr, hp->h_addr, hp->h_length);
\r
9151 /* Make connection */
\r
9152 if (connect(s, (struct sockaddr *) &sa,
\r
9153 sizeof(struct sockaddr_in)) == SOCKET_ERROR) {
\r
9154 err = WSAGetLastError();
\r
9159 /* Prepare return value */
\r
9160 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
\r
9161 cp->kind = CPSock;
\r
9163 *pr = (ProcRef *) cp;
\r
9169 OpenCommPort(char *name, ProcRef *pr)
\r
9174 char fullname[MSG_SIZ];
\r
9176 if (*name != '\\')
\r
9177 snprintf(fullname, MSG_SIZ, "\\\\.\\%s", name);
\r
9179 safeStrCpy(fullname, name, sizeof(fullname)/sizeof(fullname[0]) );
\r
9181 h = CreateFile(name, GENERIC_READ | GENERIC_WRITE,
\r
9182 0, NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL);
\r
9183 if (h == (HANDLE) -1) {
\r
9184 return GetLastError();
\r
9188 if (!SetCommState(h, (LPDCB) &dcb)) return GetLastError();
\r
9190 /* Accumulate characters until a 100ms pause, then parse */
\r
9191 ct.ReadIntervalTimeout = 100;
\r
9192 ct.ReadTotalTimeoutMultiplier = 0;
\r
9193 ct.ReadTotalTimeoutConstant = 0;
\r
9194 ct.WriteTotalTimeoutMultiplier = 0;
\r
9195 ct.WriteTotalTimeoutConstant = 0;
\r
9196 if (!SetCommTimeouts(h, (LPCOMMTIMEOUTS) &ct)) return GetLastError();
\r
9198 /* Prepare return value */
\r
9199 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
\r
9200 cp->kind = CPComm;
\r
9203 *pr = (ProcRef *) cp;
\r
9209 OpenLoopback(ProcRef *pr)
\r
9211 DisplayFatalError(_("Not implemented"), 0, 1);
\r
9217 OpenRcmd(char* host, char* user, char* cmd, ProcRef* pr)
\r
9222 struct sockaddr_in sa, mysa;
\r
9223 struct hostent FAR *hp;
\r
9224 unsigned short uport;
\r
9225 WORD wVersionRequested;
\r
9228 char stderrPortStr[MSG_SIZ];
\r
9230 /* Initialize socket DLL */
\r
9231 wVersionRequested = MAKEWORD(1, 1);
\r
9232 err = WSAStartup(wVersionRequested, &wsaData);
\r
9233 if (err != 0) return err;
\r
9235 /* Resolve remote host name */
\r
9236 memset((char *) &sa, 0, sizeof(struct sockaddr_in));
\r
9237 if (!(hp = gethostbyname(host))) {
\r
9238 unsigned int b0, b1, b2, b3;
\r
9240 err = WSAGetLastError();
\r
9242 if (sscanf(host, "%u.%u.%u.%u", &b0, &b1, &b2, &b3) == 4) {
\r
9243 hp = (struct hostent *) calloc(1, sizeof(struct hostent));
\r
9244 hp->h_addrtype = AF_INET;
\r
9246 hp->h_addr_list = (char **) calloc(2, sizeof(char *));
\r
9247 hp->h_addr_list[0] = (char *) malloc(4);
\r
9248 hp->h_addr_list[0][0] = (char) b0;
\r
9249 hp->h_addr_list[0][1] = (char) b1;
\r
9250 hp->h_addr_list[0][2] = (char) b2;
\r
9251 hp->h_addr_list[0][3] = (char) b3;
\r
9257 sa.sin_family = hp->h_addrtype;
\r
9258 uport = (unsigned short) 514;
\r
9259 sa.sin_port = htons(uport);
\r
9260 memcpy((char *) &sa.sin_addr, hp->h_addr, hp->h_length);
\r
9262 /* Bind local socket to unused "privileged" port address
\r
9264 s = INVALID_SOCKET;
\r
9265 memset((char *) &mysa, 0, sizeof(struct sockaddr_in));
\r
9266 mysa.sin_family = AF_INET;
\r
9267 mysa.sin_addr.s_addr = INADDR_ANY;
\r
9268 for (fromPort = 1023;; fromPort--) {
\r
9269 if (fromPort < 0) {
\r
9271 return WSAEADDRINUSE;
\r
9273 if (s == INVALID_SOCKET) {
\r
9274 if ((s = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {
\r
9275 err = WSAGetLastError();
\r
9280 uport = (unsigned short) fromPort;
\r
9281 mysa.sin_port = htons(uport);
\r
9282 if (bind(s, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))
\r
9283 == SOCKET_ERROR) {
\r
9284 err = WSAGetLastError();
\r
9285 if (err == WSAEADDRINUSE) continue;
\r
9289 if (connect(s, (struct sockaddr *) &sa,
\r
9290 sizeof(struct sockaddr_in)) == SOCKET_ERROR) {
\r
9291 err = WSAGetLastError();
\r
9292 if (err == WSAEADDRINUSE) {
\r
9303 /* Bind stderr local socket to unused "privileged" port address
\r
9305 s2 = INVALID_SOCKET;
\r
9306 memset((char *) &mysa, 0, sizeof(struct sockaddr_in));
\r
9307 mysa.sin_family = AF_INET;
\r
9308 mysa.sin_addr.s_addr = INADDR_ANY;
\r
9309 for (fromPort = 1023;; fromPort--) {
\r
9310 if (fromPort == prevStderrPort) continue; // don't reuse port
\r
9311 if (fromPort < 0) {
\r
9312 (void) closesocket(s);
\r
9314 return WSAEADDRINUSE;
\r
9316 if (s2 == INVALID_SOCKET) {
\r
9317 if ((s2 = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {
\r
9318 err = WSAGetLastError();
\r
9324 uport = (unsigned short) fromPort;
\r
9325 mysa.sin_port = htons(uport);
\r
9326 if (bind(s2, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))
\r
9327 == SOCKET_ERROR) {
\r
9328 err = WSAGetLastError();
\r
9329 if (err == WSAEADDRINUSE) continue;
\r
9330 (void) closesocket(s);
\r
9334 if (listen(s2, 1) == SOCKET_ERROR) {
\r
9335 err = WSAGetLastError();
\r
9336 if (err == WSAEADDRINUSE) {
\r
9338 s2 = INVALID_SOCKET;
\r
9341 (void) closesocket(s);
\r
9342 (void) closesocket(s2);
\r
9348 prevStderrPort = fromPort; // remember port used
\r
9349 snprintf(stderrPortStr, MSG_SIZ, "%d", fromPort);
\r
9351 if (send(s, stderrPortStr, strlen(stderrPortStr) + 1, 0) == SOCKET_ERROR) {
\r
9352 err = WSAGetLastError();
\r
9353 (void) closesocket(s);
\r
9354 (void) closesocket(s2);
\r
9359 if (send(s, UserName(), strlen(UserName()) + 1, 0) == SOCKET_ERROR) {
\r
9360 err = WSAGetLastError();
\r
9361 (void) closesocket(s);
\r
9362 (void) closesocket(s2);
\r
9366 if (*user == NULLCHAR) user = UserName();
\r
9367 if (send(s, user, strlen(user) + 1, 0) == SOCKET_ERROR) {
\r
9368 err = WSAGetLastError();
\r
9369 (void) closesocket(s);
\r
9370 (void) closesocket(s2);
\r
9374 if (send(s, cmd, strlen(cmd) + 1, 0) == SOCKET_ERROR) {
\r
9375 err = WSAGetLastError();
\r
9376 (void) closesocket(s);
\r
9377 (void) closesocket(s2);
\r
9382 if ((s3 = accept(s2, NULL, NULL)) == INVALID_SOCKET) {
\r
9383 err = WSAGetLastError();
\r
9384 (void) closesocket(s);
\r
9385 (void) closesocket(s2);
\r
9389 (void) closesocket(s2); /* Stop listening */
\r
9391 /* Prepare return value */
\r
9392 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
\r
9393 cp->kind = CPRcmd;
\r
9396 *pr = (ProcRef *) cp;
\r
9403 AddInputSource(ProcRef pr, int lineByLine,
\r
9404 InputCallback func, VOIDSTAR closure)
\r
9406 InputSource *is, *is2 = NULL;
\r
9407 ChildProc *cp = (ChildProc *) pr;
\r
9409 is = (InputSource *) calloc(1, sizeof(InputSource));
\r
9410 is->lineByLine = lineByLine;
\r
9412 is->closure = closure;
\r
9413 is->second = NULL;
\r
9414 is->next = is->buf;
\r
9415 if (pr == NoProc) {
\r
9416 is->kind = CPReal;
\r
9417 consoleInputSource = is;
\r
9419 is->kind = cp->kind;
\r
9421 [AS] Try to avoid a race condition if the thread is given control too early:
\r
9422 we create all threads suspended so that the is->hThread variable can be
\r
9423 safely assigned, then let the threads start with ResumeThread.
\r
9425 switch (cp->kind) {
\r
9427 is->hFile = cp->hFrom;
\r
9428 cp->hFrom = NULL; /* now owned by InputThread */
\r
9430 CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) NonOvlInputThread,
\r
9431 (LPVOID) is, CREATE_SUSPENDED, &is->id);
\r
9435 is->hFile = cp->hFrom;
\r
9436 cp->hFrom = NULL; /* now owned by InputThread */
\r
9438 CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) InputThread,
\r
9439 (LPVOID) is, CREATE_SUSPENDED, &is->id);
\r
9443 is->sock = cp->sock;
\r
9445 CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,
\r
9446 (LPVOID) is, CREATE_SUSPENDED, &is->id);
\r
9450 is2 = (InputSource *) calloc(1, sizeof(InputSource));
\r
9452 is->sock = cp->sock;
\r
9454 is2->sock = cp->sock2;
\r
9455 is2->second = is2;
\r
9457 CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,
\r
9458 (LPVOID) is, CREATE_SUSPENDED, &is->id);
\r
9460 CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,
\r
9461 (LPVOID) is2, CREATE_SUSPENDED, &is2->id);
\r
9465 if( is->hThread != NULL ) {
\r
9466 ResumeThread( is->hThread );
\r
9469 if( is2 != NULL && is2->hThread != NULL ) {
\r
9470 ResumeThread( is2->hThread );
\r
9474 return (InputSourceRef) is;
\r
9478 RemoveInputSource(InputSourceRef isr)
\r
9482 is = (InputSource *) isr;
\r
9483 is->hThread = NULL; /* tell thread to stop */
\r
9484 CloseHandle(is->hThread);
\r
9485 if (is->second != NULL) {
\r
9486 is->second->hThread = NULL;
\r
9487 CloseHandle(is->second->hThread);
\r
9491 int no_wrap(char *message, int count)
\r
9493 ConsoleOutput(message, count, FALSE);
\r
9498 OutputToProcess(ProcRef pr, char *message, int count, int *outError)
\r
9501 int outCount = SOCKET_ERROR;
\r
9502 ChildProc *cp = (ChildProc *) pr;
\r
9503 static OVERLAPPED ovl;
\r
9504 static int line = 0;
\r
9508 if (appData.noJoin || !appData.useInternalWrap)
\r
9509 return no_wrap(message, count);
\r
9512 int width = get_term_width();
\r
9513 int len = wrap(NULL, message, count, width, &line);
\r
9514 char *msg = malloc(len);
\r
9518 return no_wrap(message, count);
\r
9521 dbgchk = wrap(msg, message, count, width, &line);
\r
9522 if (dbgchk != len && appData.debugMode)
\r
9523 fprintf(debugFP, "wrap(): dbgchk(%d) != len(%d)\n", dbgchk, len);
\r
9524 ConsoleOutput(msg, len, FALSE);
\r
9531 if (ovl.hEvent == NULL) {
\r
9532 ovl.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
\r
9534 ovl.Internal = ovl.InternalHigh = ovl.Offset = ovl.OffsetHigh = 0;
\r
9536 switch (cp->kind) {
\r
9539 outCount = send(cp->sock, message, count, 0);
\r
9540 if (outCount == SOCKET_ERROR) {
\r
9541 *outError = WSAGetLastError();
\r
9543 *outError = NO_ERROR;
\r
9548 if (WriteFile(((ChildProc *)pr)->hTo, message, count,
\r
9549 &dOutCount, NULL)) {
\r
9550 *outError = NO_ERROR;
\r
9551 outCount = (int) dOutCount;
\r
9553 *outError = GetLastError();
\r
9558 *outError = DoWriteFile(((ChildProc *)pr)->hTo, message, count,
\r
9559 &dOutCount, &ovl);
\r
9560 if (*outError == NO_ERROR) {
\r
9561 outCount = (int) dOutCount;
\r
9569 OutputToProcessDelayed(ProcRef pr, char *message, int count, int *outError,
\r
9572 /* Ignore delay, not implemented for WinBoard */
\r
9573 return OutputToProcess(pr, message, count, outError);
\r
9578 CmailSigHandlerCallBack(InputSourceRef isr, VOIDSTAR closure,
\r
9579 char *buf, int count, int error)
\r
9581 DisplayFatalError(_("Not implemented"), 0, 1);
\r
9584 /* see wgamelist.c for Game List functions */
\r
9585 /* see wedittags.c for Edit Tags functions */
\r
9592 char buf[MSG_SIZ];
\r
9595 if (SearchPath(installDir, appData.icsLogon, NULL, MSG_SIZ, buf, &dummy)) {
\r
9596 f = fopen(buf, "r");
\r
9598 ProcessICSInitScript(f);
\r
9606 StartAnalysisClock()
\r
9608 if (analysisTimerEvent) return;
\r
9609 analysisTimerEvent = SetTimer(hwndMain, (UINT) ANALYSIS_TIMER_ID,
\r
9610 (UINT) 2000, NULL);
\r
9614 SetHighlights(int fromX, int fromY, int toX, int toY)
\r
9616 highlightInfo.sq[0].x = fromX;
\r
9617 highlightInfo.sq[0].y = fromY;
\r
9618 highlightInfo.sq[1].x = toX;
\r
9619 highlightInfo.sq[1].y = toY;
\r
9625 highlightInfo.sq[0].x = highlightInfo.sq[0].y =
\r
9626 highlightInfo.sq[1].x = highlightInfo.sq[1].y = -1;
\r
9630 SetPremoveHighlights(int fromX, int fromY, int toX, int toY)
\r
9632 premoveHighlightInfo.sq[0].x = fromX;
\r
9633 premoveHighlightInfo.sq[0].y = fromY;
\r
9634 premoveHighlightInfo.sq[1].x = toX;
\r
9635 premoveHighlightInfo.sq[1].y = toY;
\r
9639 ClearPremoveHighlights()
\r
9641 premoveHighlightInfo.sq[0].x = premoveHighlightInfo.sq[0].y =
\r
9642 premoveHighlightInfo.sq[1].x = premoveHighlightInfo.sq[1].y = -1;
\r
9646 ShutDownFrontEnd()
\r
9648 if (saveSettingsOnExit) SaveSettings(settingsFileName);
\r
9649 DeleteClipboardTempFiles();
\r
9655 if (IsIconic(hwndMain))
\r
9656 ShowWindow(hwndMain, SW_RESTORE);
\r
9658 SetActiveWindow(hwndMain);
\r
9662 * Prototypes for animation support routines
\r
9664 static void ScreenSquare(int column, int row, POINT * pt);
\r
9665 static void Tween( POINT * start, POINT * mid, POINT * finish, int factor,
\r
9666 POINT frames[], int * nFrames);
\r
9672 AnimateAtomicCapture(Board board, int fromX, int fromY, int toX, int toY)
\r
9673 { // [HGM] atomic: animate blast wave
\r
9676 explodeInfo.fromX = fromX;
\r
9677 explodeInfo.fromY = fromY;
\r
9678 explodeInfo.toX = toX;
\r
9679 explodeInfo.toY = toY;
\r
9680 for(i=1; i<4*kFactor; i++) {
\r
9681 explodeInfo.radius = (i*180)/(4*kFactor-1);
\r
9682 DrawPosition(FALSE, board);
\r
9683 Sleep(appData.animSpeed);
\r
9685 explodeInfo.radius = 0;
\r
9686 DrawPosition(TRUE, board);
\r
9690 AnimateMove(board, fromX, fromY, toX, toY)
\r
9697 ChessSquare piece;
\r
9698 POINT start, finish, mid;
\r
9699 POINT frames[kFactor * 2 + 1];
\r
9702 if (!appData.animate) return;
\r
9703 if (doingSizing) return;
\r
9704 if (fromY < 0 || fromX < 0) return;
\r
9705 piece = board[fromY][fromX];
\r
9706 if (piece >= EmptySquare) return;
\r
9708 ScreenSquare(fromX, fromY, &start);
\r
9709 ScreenSquare(toX, toY, &finish);
\r
9711 /* All moves except knight jumps move in straight line */
\r
9712 if (!(abs(fromX-toX) == 1 && abs(fromY-toY) == 2 || abs(fromX-toX) == 2 && abs(fromY-toY) == 1)) {
\r
9713 mid.x = start.x + (finish.x - start.x) / 2;
\r
9714 mid.y = start.y + (finish.y - start.y) / 2;
\r
9716 /* Knight: make straight movement then diagonal */
\r
9717 if (abs(toY - fromY) < abs(toX - fromX)) {
\r
9718 mid.x = start.x + (finish.x - start.x) / 2;
\r
9722 mid.y = start.y + (finish.y - start.y) / 2;
\r
9726 /* Don't use as many frames for very short moves */
\r
9727 if (abs(toY - fromY) + abs(toX - fromX) <= 2)
\r
9728 Tween(&start, &mid, &finish, kFactor - 1, frames, &nFrames);
\r
9730 Tween(&start, &mid, &finish, kFactor, frames, &nFrames);
\r
9732 animInfo.from.x = fromX;
\r
9733 animInfo.from.y = fromY;
\r
9734 animInfo.to.x = toX;
\r
9735 animInfo.to.y = toY;
\r
9736 animInfo.lastpos = start;
\r
9737 animInfo.piece = piece;
\r
9738 for (n = 0; n < nFrames; n++) {
\r
9739 animInfo.pos = frames[n];
\r
9740 DrawPosition(FALSE, NULL);
\r
9741 animInfo.lastpos = animInfo.pos;
\r
9742 Sleep(appData.animSpeed);
\r
9744 animInfo.pos = finish;
\r
9745 DrawPosition(FALSE, NULL);
\r
9746 animInfo.piece = EmptySquare;
\r
9747 Explode(board, fromX, fromY, toX, toY);
\r
9750 /* Convert board position to corner of screen rect and color */
\r
9753 ScreenSquare(column, row, pt)
\r
9754 int column; int row; POINT * pt;
\r
9757 pt->x = lineGap + ((BOARD_WIDTH-1)-column) * (squareSize + lineGap);
\r
9758 pt->y = lineGap + row * (squareSize + lineGap);
\r
9760 pt->x = lineGap + column * (squareSize + lineGap);
\r
9761 pt->y = lineGap + ((BOARD_HEIGHT-1)-row) * (squareSize + lineGap);
\r
9765 /* Generate a series of frame coords from start->mid->finish.
\r
9766 The movement rate doubles until the half way point is
\r
9767 reached, then halves back down to the final destination,
\r
9768 which gives a nice slow in/out effect. The algorithmn
\r
9769 may seem to generate too many intermediates for short
\r
9770 moves, but remember that the purpose is to attract the
\r
9771 viewers attention to the piece about to be moved and
\r
9772 then to where it ends up. Too few frames would be less
\r
9776 Tween(start, mid, finish, factor, frames, nFrames)
\r
9777 POINT * start; POINT * mid;
\r
9778 POINT * finish; int factor;
\r
9779 POINT frames[]; int * nFrames;
\r
9781 int n, fraction = 1, count = 0;
\r
9783 /* Slow in, stepping 1/16th, then 1/8th, ... */
\r
9784 for (n = 0; n < factor; n++)
\r
9786 for (n = 0; n < factor; n++) {
\r
9787 frames[count].x = start->x + (mid->x - start->x) / fraction;
\r
9788 frames[count].y = start->y + (mid->y - start->y) / fraction;
\r
9790 fraction = fraction / 2;
\r
9794 frames[count] = *mid;
\r
9797 /* Slow out, stepping 1/2, then 1/4, ... */
\r
9799 for (n = 0; n < factor; n++) {
\r
9800 frames[count].x = finish->x - (finish->x - mid->x) / fraction;
\r
9801 frames[count].y = finish->y - (finish->y - mid->y) / fraction;
\r
9803 fraction = fraction * 2;
\r
9809 HistorySet( char movelist[][2*MOVE_LEN], int first, int last, int current )
\r
9811 MoveHistorySet( movelist, first, last, current, pvInfoList );
\r
9813 EvalGraphSet( first, last, current, pvInfoList );
\r
9817 SettingsPopUp(ChessProgramState *cps)
\r
9818 { // [HGM] wrapper needed because handles must not be passed through back-end
\r
9819 EngineOptionsPopup(savedHwnd, cps);
\r
9822 int flock(int fid, int code)
\r
9824 HANDLE hFile = (HANDLE) _get_osfhandle(fid);
\r
9828 ov.OffsetHigh = 0;
\r
9830 case 1: LockFileEx(hFile, LOCKFILE_EXCLUSIVE_LOCK, 0, 1024, 0, &ov); break; // LOCK_SH
\r
9831 case 2: LockFileEx(hFile, LOCKFILE_EXCLUSIVE_LOCK, 0, 1024, 0, &ov); break; // LOCK_EX
\r
9832 case 3: UnlockFileEx(hFile, 0, 1024, 0, &ov); break; // LOCK_UN
\r
9833 default: return -1;
\r