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 int errorExitStatus;
\r
161 BoardSize boardSize;
\r
162 Boolean chessProgram;
\r
163 //static int boardX, boardY;
\r
164 int minX, minY; // [HGM] placement: volatile limits on upper-left corner
\r
165 int squareSize, lineGap, minorSize;
\r
166 static int winW, winH;
\r
167 static RECT messageRect, whiteRect, blackRect, leftLogoRect, rightLogoRect; // [HGM] logo
\r
168 static int logoHeight = 0;
\r
169 static char messageText[MESSAGE_TEXT_MAX];
\r
170 static int clockTimerEvent = 0;
\r
171 static int loadGameTimerEvent = 0;
\r
172 static int analysisTimerEvent = 0;
\r
173 static DelayedEventCallback delayedTimerCallback;
\r
174 static int delayedTimerEvent = 0;
\r
175 static int buttonCount = 2;
\r
176 char *icsTextMenuString;
\r
178 char *firstChessProgramNames;
\r
179 char *secondChessProgramNames;
\r
181 #define PALETTESIZE 256
\r
183 HINSTANCE hInst; /* current instance */
\r
184 Boolean alwaysOnTop = FALSE;
\r
186 COLORREF lightSquareColor, darkSquareColor, whitePieceColor,
\r
187 blackPieceColor, highlightSquareColor, premoveHighlightColor;
\r
189 ColorClass currentColorClass;
\r
191 static HWND savedHwnd;
\r
192 HWND hCommPort = NULL; /* currently open comm port */
\r
193 static HWND hwndPause; /* pause button */
\r
194 static HBITMAP pieceBitmap[3][(int) BlackPawn]; /* [HGM] nr of bitmaps referred to bP in stead of wK */
\r
195 static HBRUSH lightSquareBrush, darkSquareBrush,
\r
196 blackSquareBrush, /* [HGM] for band between board and holdings */
\r
197 explodeBrush, /* [HGM] atomic */
\r
198 markerBrush, /* [HGM] markers */
\r
199 whitePieceBrush, blackPieceBrush, iconBkgndBrush /*, outlineBrush*/;
\r
200 static POINT gridEndpoints[(BOARD_RANKS + BOARD_FILES + 2) * 2];
\r
201 static DWORD gridVertexCounts[BOARD_RANKS + BOARD_FILES + 2];
\r
202 static HPEN gridPen = NULL;
\r
203 static HPEN highlightPen = NULL;
\r
204 static HPEN premovePen = NULL;
\r
205 static NPLOGPALETTE pLogPal;
\r
206 static BOOL paletteChanged = FALSE;
\r
207 static HICON iconWhite, iconBlack, iconCurrent;
\r
208 static int doingSizing = FALSE;
\r
209 static int lastSizing = 0;
\r
210 static int prevStderrPort;
\r
211 static HBITMAP userLogo;
\r
213 static HBITMAP liteBackTexture = NULL;
\r
214 static HBITMAP darkBackTexture = NULL;
\r
215 static int liteBackTextureMode = BACK_TEXTURE_MODE_PLAIN;
\r
216 static int darkBackTextureMode = BACK_TEXTURE_MODE_PLAIN;
\r
217 static int backTextureSquareSize = 0;
\r
218 static struct { int x; int y; int mode; } backTextureSquareInfo[BOARD_RANKS][BOARD_FILES];
\r
220 #if __GNUC__ && !defined(_winmajor)
\r
221 #define oldDialog 0 /* cygwin doesn't define _winmajor; mingw does */
\r
223 #if defined(_winmajor)
\r
224 #define oldDialog (_winmajor < 4)
\r
226 #define oldDialog 0
\r
230 #define INTERNATIONAL
\r
232 #ifdef INTERNATIONAL
\r
233 # define _(s) T_(s)
\r
239 # define Translate(x, y)
\r
240 # define LoadLanguageFile(s)
\r
243 #ifdef INTERNATIONAL
\r
245 Boolean barbaric; // flag indicating if translation is needed
\r
247 // list of item numbers used in each dialog (used to alter language at run time)
\r
249 #define ABOUTBOX -1 /* not sure why these are needed */
\r
250 #define ABOUTBOX2 -1
\r
252 int dialogItems[][41 ] = {
\r
253 { ABOUTBOX, IDOK, OPT_MESS, 400 },
\r
254 { DLG_TimeControl, IDC_Babble, OPT_TCUseMoves, OPT_TCUseInc, OPT_TCUseFixed,
\r
255 OPT_TCtext1, OPT_TCtext2, OPT_TCitext1, OPT_TCitext2, OPT_TCftext, GPB_Factors, IDC_Factor1, IDC_Factor2, IDOK, IDCANCEL },
\r
256 { DLG_LoadOptions, OPT_Autostep, OPT_AStext1,
\r
257 OPT_elo1, OPT_elo2, OPT_date, 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, OPT_Bitmaps, OPT_PieceFont },
\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, OPT_ChoosePieceFont, OPT_MessageFont8,
\r
318 OPT_SampleGameListFont, OPT_ChooseGameListFont, OPT_MessageFont7,
\r
319 OPT_CommentsFont, OPT_MessageFont5, GPB_Current, GPB_All, OPT_MessageFont6 },
\r
320 { DLG_NewGameFRC, IDC_NFG_Label, IDC_NFG_Random, IDOK, IDCANCEL },
\r
321 { DLG_GameListOptions, IDC_GLT, IDC_GLT_Up, IDC_GLT_Down, IDC_GLT_Restore,
\r
322 IDC_GLT_Default, IDOK, IDCANCEL, IDC_GLT_RestoreTo },
\r
323 { DLG_MoveHistory },
\r
324 { DLG_EvalGraph },
\r
325 { DLG_EngineOutput, IDC_EngineLabel1, IDC_Engine1_NPS, IDC_EngineLabel2, IDC_Engine2_NPS },
\r
326 { DLG_Chat, IDC_Partner, IDC_Clear, IDC_Send, },
\r
327 { DLG_EnginePlayOptions, IDC_EpPonder, IDC_EpShowThinking, IDC_EpHideThinkingHuman,
\r
328 IDC_EpPeriodicUpdates, GPB_Adjudications, IDC_Draw, IDC_Moves, IDC_Threshold,
\r
329 IDC_Centi, IDC_TestClaims, IDC_DetectMates, IDC_MaterialDraws, IDC_TrivialDraws,
\r
330 GPB_Apply, IDC_Rule, IDC_Repeats, IDC_ScoreAbs1, IDC_ScoreAbs2, IDOK, IDCANCEL },
\r
331 { DLG_OptionsUCI, IDC_PolyDir, IDC_BrowseForPolyglotDir, IDC_Hash, IDC_Path,
\r
332 IDC_BrowseForEGTB, IDC_Cache, IDC_UseBook, IDC_BrowseForBook, IDC_CPU, IDC_OwnBook1,
\r
333 IDC_OwnBook2, IDC_Depth, IDC_Variation, IDC_DefGames, IDOK, IDCANCEL },
\r
337 static char languageBuf[50000], *foreign[1000], *english[1000], *languageFile[MSG_SIZ];
\r
338 static int lastChecked;
\r
339 static char oldLanguage[MSG_SIZ], *menuText[10][30];
\r
340 extern int tinyLayout;
\r
341 extern char * menuBarText[][10];
\r
344 LoadLanguageFile(char *name)
\r
345 { //load the file with translations, and make a list of the strings to be translated, and their translations
\r
347 int i=0, j=0, n=0, k;
\r
350 if(!name || name[0] == NULLCHAR) return;
\r
351 snprintf(buf, MSG_SIZ, "%s%s", name, strchr(name, '.') ? "" : ".lng"); // auto-append lng extension
\r
352 appData.language = oldLanguage;
\r
353 if(!strcmp(buf, oldLanguage)) { barbaric = 1; return; } // this language already loaded; just switch on
\r
354 if((f = fopen(buf, "r")) == NULL) return;
\r
355 while((k = fgetc(f)) != EOF) {
\r
356 if(i >= sizeof(languageBuf)) { DisplayError("Language file too big", 0); return; }
\r
357 languageBuf[i] = k;
\r
359 if(languageBuf[n] == '"' && languageBuf[i-1] == '"') {
\r
361 if(p = strstr(languageBuf + n + 1, "\" === \"")) {
\r
362 if(p > languageBuf+n+2 && p+8 < languageBuf+i) {
\r
363 if(j >= sizeof(english)) { DisplayError("Too many translated strings", 0); return; }
\r
364 english[j] = languageBuf + n + 1; *p = 0;
\r
365 foreign[j++] = p + 7; languageBuf[i-1] = 0;
\r
366 //if(appData.debugMode) fprintf(debugFP, "translation: replace '%s' by '%s'\n", english[j-1], foreign[j-1]);
\r
371 } else if(i > 0 && languageBuf[i-1] == '\\') {
\r
373 case 'n': k = '\n'; break;
\r
374 case 'r': k = '\r'; break;
\r
375 case 't': k = '\t'; break;
\r
377 languageBuf[--i] = k;
\r
382 barbaric = (j != 0);
\r
383 safeStrCpy(oldLanguage, buf, sizeof(oldLanguage)/sizeof(oldLanguage[0]) );
\r
388 { // return the translation of the given string
\r
389 // efficiency can be improved a lot...
\r
391 //if(appData.debugMode) fprintf(debugFP, "T_(%s)\n", s);
\r
392 if(!barbaric) return s;
\r
393 if(!s) return ""; // sanity
\r
394 while(english[i]) {
\r
395 if(!strcmp(s, english[i])) return foreign[i];
\r
402 Translate(HWND hDlg, int dialogID)
\r
403 { // translate all text items in the given dialog
\r
405 char buf[MSG_SIZ], *s;
\r
406 if(!barbaric) return;
\r
407 while(dialogItems[i][0] && dialogItems[i][0] != dialogID) i++; // find the dialog description
\r
408 if(dialogItems[i][0] != dialogID) return; // unknown dialog, should not happen
\r
409 GetWindowText( hDlg, buf, MSG_SIZ );
\r
411 if(strcmp(buf, s)) SetWindowText(hDlg, s); // replace by translated string (if different)
\r
412 for(j=1; k=dialogItems[i][j]; j++) { // translate all listed dialog items
\r
413 GetDlgItemText(hDlg, k, buf, MSG_SIZ);
\r
414 if(strlen(buf) == 0) continue;
\r
416 if(strcmp(buf, s)) SetDlgItemText(hDlg, k, s); // replace by translated string (if different)
\r
421 TranslateOneMenu(int i, HMENU subMenu)
\r
424 static MENUITEMINFO info;
\r
426 info.cbSize = sizeof(MENUITEMINFO);
\r
427 info.fMask = MIIM_STATE | MIIM_TYPE;
\r
428 for(j=GetMenuItemCount(subMenu)-1; j>=0; j--){
\r
430 info.dwTypeData = buf;
\r
431 info.cch = sizeof(buf);
\r
432 GetMenuItemInfo(subMenu, j, TRUE, &info);
\r
434 if(menuText[i][j]) safeStrCpy(buf, menuText[i][j], sizeof(buf)/sizeof(buf[0]) );
\r
435 else menuText[i][j] = strdup(buf); // remember original on first change
\r
437 if(buf[0] == NULLCHAR) continue;
\r
438 info.dwTypeData = T_(buf);
\r
439 info.cch = strlen(buf)+1;
\r
440 SetMenuItemInfo(subMenu, j, TRUE, &info);
\r
446 TranslateMenus(int addLanguage)
\r
449 WIN32_FIND_DATA fileData;
\r
451 #define IDM_English 1970
\r
453 HMENU mainMenu = GetMenu(hwndMain);
\r
454 for (i=GetMenuItemCount(mainMenu)-1; i>=0; i--) {
\r
455 HMENU subMenu = GetSubMenu(mainMenu, i);
\r
456 ModifyMenu(mainMenu, i, MF_STRING|MF_BYPOSITION|MF_POPUP|EnableMenuItem(mainMenu, i, MF_BYPOSITION),
\r
457 (UINT) subMenu, T_(menuBarText[tinyLayout][i]));
\r
458 TranslateOneMenu(i, subMenu);
\r
460 DrawMenuBar(hwndMain);
\r
463 if(!addLanguage) return;
\r
464 if((hFind = FindFirstFile("*.LNG", &fileData)) != INVALID_HANDLE_VALUE) {
\r
465 HMENU mainMenu = GetMenu(hwndMain);
\r
466 HMENU subMenu = GetSubMenu(mainMenu, GetMenuItemCount(mainMenu)-1);
\r
467 AppendMenu(subMenu, MF_SEPARATOR, (UINT_PTR) 0, NULL);
\r
468 AppendMenu(subMenu, MF_ENABLED|MF_STRING|(barbaric?MF_UNCHECKED:MF_CHECKED), (UINT_PTR) IDM_English, (LPCTSTR) "English");
\r
469 i = 0; lastChecked = IDM_English;
\r
471 char *p, *q = fileData.cFileName;
\r
472 int checkFlag = MF_UNCHECKED;
\r
473 languageFile[i] = strdup(q);
\r
474 if(barbaric && !strcmp(oldLanguage, q)) {
\r
475 checkFlag = MF_CHECKED;
\r
476 lastChecked = IDM_English + i + 1;
\r
477 CheckMenuItem(mainMenu, IDM_English, MF_BYCOMMAND|MF_UNCHECKED);
\r
479 *q = ToUpper(*q); while(*++q) *q = ToLower(*q);
\r
480 p = strstr(fileData.cFileName, ".lng");
\r
482 AppendMenu(subMenu, MF_ENABLED|MF_STRING|checkFlag, (UINT_PTR) IDM_English + ++i, (LPCTSTR) fileData.cFileName);
\r
483 } while(FindNextFile(hFind, &fileData));
\r
496 int cliWidth, cliHeight;
\r
499 SizeInfo sizeInfo[] =
\r
501 { "tiny", 21, 0, 1, 1, 0, 0 },
\r
502 { "teeny", 25, 1, 1, 1, 0, 0 },
\r
503 { "dinky", 29, 1, 1, 1, 0, 0 },
\r
504 { "petite", 33, 1, 1, 1, 0, 0 },
\r
505 { "slim", 37, 2, 1, 0, 0, 0 },
\r
506 { "small", 40, 2, 1, 0, 0, 0 },
\r
507 { "mediocre", 45, 2, 1, 0, 0, 0 },
\r
508 { "middling", 49, 2, 0, 0, 0, 0 },
\r
509 { "average", 54, 2, 0, 0, 0, 0 },
\r
510 { "moderate", 58, 3, 0, 0, 0, 0 },
\r
511 { "medium", 64, 3, 0, 0, 0, 0 },
\r
512 { "bulky", 72, 3, 0, 0, 0, 0 },
\r
513 { "large", 80, 3, 0, 0, 0, 0 },
\r
514 { "big", 87, 3, 0, 0, 0, 0 },
\r
515 { "huge", 95, 3, 0, 0, 0, 0 },
\r
516 { "giant", 108, 3, 0, 0, 0, 0 },
\r
517 { "colossal", 116, 4, 0, 0, 0, 0 },
\r
518 { "titanic", 129, 4, 0, 0, 0, 0 },
\r
519 { NULL, 0, 0, 0, 0, 0, 0 }
\r
522 #define MF(x) {x, {{0,}, 0. }, {0, }, 0}
\r
523 MyFont fontRec[NUM_SIZES][NUM_FONTS] =
\r
525 { 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), MF(GAMELIST_FONT_ALL) },
\r
526 { 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), MF(GAMELIST_FONT_ALL) },
\r
527 { 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), MF(GAMELIST_FONT_ALL) },
\r
528 { 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), MF(GAMELIST_FONT_ALL) },
\r
529 { 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), MF(GAMELIST_FONT_ALL) },
\r
530 { 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), MF(GAMELIST_FONT_ALL) },
\r
531 { 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), MF(GAMELIST_FONT_ALL) },
\r
532 { 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), MF(GAMELIST_FONT_ALL) },
\r
533 { 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), MF(GAMELIST_FONT_ALL) },
\r
534 { 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), MF(GAMELIST_FONT_ALL) },
\r
535 { 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), MF(GAMELIST_FONT_ALL) },
\r
536 { 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), MF(GAMELIST_FONT_ALL) },
\r
537 { 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), MF(GAMELIST_FONT_ALL) },
\r
538 { 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), MF(GAMELIST_FONT_ALL) },
\r
539 { 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), MF(GAMELIST_FONT_ALL) },
\r
540 { 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), MF(GAMELIST_FONT_ALL) },
\r
541 { 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), MF (GAMELIST_FONT_ALL) },
\r
542 { 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), MF(GAMELIST_FONT_ALL) },
\r
545 MyFont *font[NUM_SIZES][NUM_FONTS];
\r
554 #define BUTTON_WIDTH (tinyLayout ? 16 : 32)
\r
555 #define N_BUTTONS 5
\r
557 MyButtonDesc buttonDesc[N_BUTTONS] =
\r
559 {"<<", IDM_ToStart, NULL, NULL},
\r
560 {"<", IDM_Backward, NULL, NULL},
\r
561 {"P", IDM_Pause, NULL, NULL},
\r
562 {">", IDM_Forward, NULL, NULL},
\r
563 {">>", IDM_ToEnd, NULL, NULL},
\r
566 int tinyLayout = 0, smallLayout = 0;
\r
567 #define MENU_BAR_ITEMS 9
\r
568 char *menuBarText[2][MENU_BAR_ITEMS+1] = {
\r
569 { N_("&File"), N_("&Edit"), N_("&View"), N_("&Mode"), N_("&Action"), N_("E&ngine"), N_("&Options"), N_("&Help"), NULL },
\r
570 { N_("&F"), N_("&E"), N_("&V"), N_("&M"), N_("&A"), N_("&N"), N_("&O"), N_("&H"), NULL },
\r
574 MySound sounds[(int)NSoundClasses];
\r
575 MyTextAttribs textAttribs[(int)NColorClasses];
\r
577 MyColorizeAttribs colorizeAttribs[] = {
\r
578 { (COLORREF)0, 0, N_("Shout Text") },
\r
579 { (COLORREF)0, 0, N_("SShout/CShout") },
\r
580 { (COLORREF)0, 0, N_("Channel 1 Text") },
\r
581 { (COLORREF)0, 0, N_("Channel Text") },
\r
582 { (COLORREF)0, 0, N_("Kibitz Text") },
\r
583 { (COLORREF)0, 0, N_("Tell Text") },
\r
584 { (COLORREF)0, 0, N_("Challenge Text") },
\r
585 { (COLORREF)0, 0, N_("Request Text") },
\r
586 { (COLORREF)0, 0, N_("Seek Text") },
\r
587 { (COLORREF)0, 0, N_("Normal Text") },
\r
588 { (COLORREF)0, 0, N_("None") }
\r
593 static char *commentTitle;
\r
594 static char *commentText;
\r
595 static int commentIndex;
\r
596 static Boolean editComment = FALSE;
\r
599 char errorTitle[MSG_SIZ];
\r
600 char errorMessage[2*MSG_SIZ];
\r
601 HWND errorDialog = NULL;
\r
602 BOOLEAN moveErrorMessageUp = FALSE;
\r
603 BOOLEAN consoleEcho = TRUE;
\r
604 CHARFORMAT consoleCF;
\r
605 COLORREF consoleBackgroundColor;
\r
607 char *programVersion;
\r
613 typedef int CPKind;
\r
622 SOCKET sock2; /* stderr socket for OpenRcmd */
\r
625 #define INPUT_SOURCE_BUF_SIZE 4096
\r
627 typedef struct _InputSource {
\r
634 char buf[INPUT_SOURCE_BUF_SIZE];
\r
638 InputCallback func;
\r
639 struct _InputSource *second; /* for stderr thread on CPRcmd */
\r
643 InputSource *consoleInputSource;
\r
648 VOID ConsoleOutput(char* data, int length, int forceVisible);
\r
649 VOID ConsoleCreate();
\r
651 ConsoleWndProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam);
\r
652 VOID ColorizeTextPopup(HWND hwnd, ColorClass cc);
\r
653 VOID PrintCommSettings(FILE *f, char *name, DCB *dcb);
\r
654 VOID ParseCommSettings(char *arg, DCB *dcb);
\r
656 StartupDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam);
\r
657 VOID APIENTRY MenuPopup(HWND hwnd, POINT pt, HMENU hmenu, UINT def);
\r
658 void ParseIcsTextMenu(char *icsTextMenuString);
\r
659 VOID PopUpNameDialog(char firstchar);
\r
660 VOID UpdateSampleText(HWND hDlg, int id, MyColorizeAttribs *mca);
\r
664 int GameListOptions();
\r
666 int dummy; // [HGM] for obsolete args
\r
668 HWND hwndMain = NULL; /* root window*/
\r
669 HWND hwndConsole = NULL;
\r
670 HWND commentDialog = NULL;
\r
671 HWND moveHistoryDialog = NULL;
\r
672 HWND evalGraphDialog = NULL;
\r
673 HWND engineOutputDialog = NULL;
\r
674 HWND gameListDialog = NULL;
\r
675 HWND editTagsDialog = NULL;
\r
677 int commentUp = FALSE;
\r
679 WindowPlacement wpMain;
\r
680 WindowPlacement wpConsole;
\r
681 WindowPlacement wpComment;
\r
682 WindowPlacement wpMoveHistory;
\r
683 WindowPlacement wpEvalGraph;
\r
684 WindowPlacement wpEngineOutput;
\r
685 WindowPlacement wpGameList;
\r
686 WindowPlacement wpTags;
\r
688 VOID EngineOptionsPopup(); // [HGM] settings
\r
690 VOID GothicPopUp(char *title, VariantClass variant);
\r
692 * Setting "frozen" should disable all user input other than deleting
\r
693 * the window. We do this while engines are initializing themselves.
\r
695 static int frozen = 0;
\r
696 static int oldMenuItemState[MENU_BAR_ITEMS];
\r
702 if (frozen) return;
\r
704 hmenu = GetMenu(hwndMain);
\r
705 for (i=0; i<MENU_BAR_ITEMS; i++) {
\r
706 oldMenuItemState[i] = EnableMenuItem(hmenu, i, MF_BYPOSITION|MF_GRAYED);
\r
708 DrawMenuBar(hwndMain);
\r
711 /* Undo a FreezeUI */
\r
717 if (!frozen) return;
\r
719 hmenu = GetMenu(hwndMain);
\r
720 for (i=0; i<MENU_BAR_ITEMS; i++) {
\r
721 EnableMenuItem(hmenu, i, MF_BYPOSITION|oldMenuItemState[i]);
\r
723 DrawMenuBar(hwndMain);
\r
726 /*static*/ int fromX = -1, fromY = -1, toX, toY; // [HGM] moved upstream, so JAWS can use them
\r
728 /* JAWS preparation patch (WinBoard for the sight impaired). Define required insertions as empty */
\r
734 #define JAWS_ALT_INTERCEPT
\r
735 #define JAWS_KB_NAVIGATION
\r
736 #define JAWS_MENU_ITEMS
\r
737 #define JAWS_SILENCE
\r
738 #define JAWS_REPLAY
\r
740 #define JAWS_COPYRIGHT
\r
741 #define JAWS_DELETE(X) X
\r
742 #define SAYMACHINEMOVE()
\r
746 /*---------------------------------------------------------------------------*\
\r
750 \*---------------------------------------------------------------------------*/
\r
753 WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
\r
754 LPSTR lpCmdLine, int nCmdShow)
\r
757 HANDLE hAccelMain, hAccelNoAlt, hAccelNoICS;
\r
758 // INITCOMMONCONTROLSEX ex;
\r
762 LoadLibrary("RICHED32.DLL");
\r
763 consoleCF.cbSize = sizeof(CHARFORMAT);
\r
765 if (!InitApplication(hInstance)) {
\r
768 if (!InitInstance(hInstance, nCmdShow, lpCmdLine)) {
\r
775 // InitCommonControlsEx(&ex);
\r
776 InitCommonControls();
\r
778 hAccelMain = LoadAccelerators (hInstance, szAppName);
\r
779 hAccelNoAlt = LoadAccelerators (hInstance, "NO_ALT");
\r
780 hAccelNoICS = LoadAccelerators( hInstance, "NO_ICS"); /* [AS] No Ctrl-V on ICS!!! */
\r
782 /* Acquire and dispatch messages until a WM_QUIT message is received. */
\r
784 while (GetMessage(&msg, /* message structure */
\r
785 NULL, /* handle of window receiving the message */
\r
786 0, /* lowest message to examine */
\r
787 0)) /* highest message to examine */
\r
790 if(msg.message == WM_CHAR && msg.wParam == '\t') {
\r
791 // [HGM] navigate: switch between all windows with tab
\r
792 HWND e1 = NULL, e2 = NULL, mh = NULL, hInput = NULL, hText = NULL;
\r
793 int i, currentElement = 0;
\r
795 // first determine what element of the chain we come from (if any)
\r
796 if(appData.icsActive) {
\r
797 hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
798 hText = GetDlgItem(hwndConsole, OPT_ConsoleText);
\r
800 if(engineOutputDialog && EngineOutputIsUp()) {
\r
801 e1 = GetDlgItem(engineOutputDialog, IDC_EngineMemo1);
\r
802 e2 = GetDlgItem(engineOutputDialog, IDC_EngineMemo2);
\r
804 if(moveHistoryDialog && MoveHistoryIsUp()) {
\r
805 mh = GetDlgItem(moveHistoryDialog, IDC_MoveHistory);
\r
807 if(msg.hwnd == hwndMain) currentElement = 7 ; else
\r
808 if(msg.hwnd == engineOutputDialog) currentElement = 2; else
\r
809 if(msg.hwnd == e1) currentElement = 2; else
\r
810 if(msg.hwnd == e2) currentElement = 3; else
\r
811 if(msg.hwnd == moveHistoryDialog) currentElement = 4; else
\r
812 if(msg.hwnd == mh) currentElement = 4; else
\r
813 if(msg.hwnd == evalGraphDialog) currentElement = 6; else
\r
814 if(msg.hwnd == hText) currentElement = 5; else
\r
815 if(msg.hwnd == hInput) currentElement = 6; else
\r
816 for (i = 0; i < N_BUTTONS; i++) {
\r
817 if (buttonDesc[i].hwnd == msg.hwnd) { currentElement = 1; break; }
\r
820 // determine where to go to
\r
821 if(currentElement) { HWND h = NULL; int direction = GetKeyState(VK_SHIFT) < 0 ? -1 : 1;
\r
823 currentElement = (currentElement + direction) % 7;
\r
824 switch(currentElement) {
\r
826 h = hwndMain; break; // passing this case always makes the loop exit
\r
828 h = buttonDesc[0].hwnd; break; // could be NULL
\r
830 if(!EngineOutputIsUp()) continue; // skip closed auxiliary windows
\r
833 if(!EngineOutputIsUp()) continue;
\r
836 if(!MoveHistoryIsUp()) continue;
\r
838 // case 6: // input to eval graph does not seem to get here!
\r
839 // if(!EvalGraphIsUp()) continue;
\r
840 // h = evalGraphDialog; break;
\r
842 if(!appData.icsActive) continue;
\r
846 if(!appData.icsActive) continue;
\r
852 if(currentElement > 4 && IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);
\r
853 if(currentElement < 5 && IsIconic(hwndMain)) ShowWindow(hwndMain, SW_RESTORE); // all open together
\r
856 continue; // this message now has been processed
\r
860 if (!(commentDialog && IsDialogMessage(commentDialog, &msg)) &&
\r
861 !(moveHistoryDialog && IsDialogMessage(moveHistoryDialog, &msg)) &&
\r
862 !(evalGraphDialog && IsDialogMessage(evalGraphDialog, &msg)) &&
\r
863 !(engineOutputDialog && IsDialogMessage(engineOutputDialog, &msg)) &&
\r
864 !(editTagsDialog && IsDialogMessage(editTagsDialog, &msg)) &&
\r
865 !(gameListDialog && IsDialogMessage(gameListDialog, &msg)) &&
\r
866 !(errorDialog && IsDialogMessage(errorDialog, &msg)) &&
\r
867 !(!frozen && TranslateAccelerator(hwndMain, hAccelMain, &msg)) && JAWS_ACCEL
\r
868 !(!hwndConsole && TranslateAccelerator(hwndMain, hAccelNoICS, &msg)) &&
\r
869 !(!hwndConsole && TranslateAccelerator(hwndMain, hAccelNoAlt, &msg))) {
\r
870 int done = 0, i; // [HGM] chat: dispatch cat-box messages
\r
871 for(i=0; i<MAX_CHAT; i++)
\r
872 if(chatHandle[i] && IsDialogMessage(chatHandle[i], &msg)) {
\r
875 if(done) continue; // [HGM] chat: end patch
\r
876 TranslateMessage(&msg); /* Translates virtual key codes */
\r
877 DispatchMessage(&msg); /* Dispatches message to window */
\r
882 return (msg.wParam); /* Returns the value from PostQuitMessage */
\r
885 /*---------------------------------------------------------------------------*\
\r
887 * Initialization functions
\r
889 \*---------------------------------------------------------------------------*/
\r
893 { // update user logo if necessary
\r
894 static char oldUserName[MSG_SIZ], dir[MSG_SIZ], *curName;
\r
896 if(appData.autoLogo) {
\r
897 curName = UserName();
\r
898 if(strcmp(curName, oldUserName)) {
\r
899 GetCurrentDirectory(MSG_SIZ, dir);
\r
900 SetCurrentDirectory(installDir);
\r
901 snprintf(oldUserName, MSG_SIZ, "logos\\%s.bmp", curName);
\r
902 userLogo = LoadImage( 0, oldUserName, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );
\r
903 safeStrCpy(oldUserName, curName, sizeof(oldUserName)/sizeof(oldUserName[0]) );
\r
904 if(userLogo == NULL)
\r
905 userLogo = LoadImage( 0, "logos\\dummy.bmp", IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );
\r
906 SetCurrentDirectory(dir); /* return to prev directory */
\r
912 InitApplication(HINSTANCE hInstance)
\r
916 /* Fill in window class structure with parameters that describe the */
\r
919 wc.style = CS_HREDRAW | CS_VREDRAW; /* Class style(s). */
\r
920 wc.lpfnWndProc = (WNDPROC)WndProc; /* Window Procedure */
\r
921 wc.cbClsExtra = 0; /* No per-class extra data. */
\r
922 wc.cbWndExtra = 0; /* No per-window extra data. */
\r
923 wc.hInstance = hInstance; /* Owner of this class */
\r
924 wc.hIcon = LoadIcon(hInstance, "icon_white");
\r
925 wc.hCursor = LoadCursor(NULL, IDC_ARROW); /* Cursor */
\r
926 wc.hbrBackground = (HBRUSH)(COLOR_WINDOW+1); /* Default color */
\r
927 wc.lpszMenuName = szAppName; /* Menu name from .RC */
\r
928 wc.lpszClassName = szAppName; /* Name to register as */
\r
930 /* Register the window class and return success/failure code. */
\r
931 if (!RegisterClass(&wc)) return FALSE;
\r
933 wc.style = CS_HREDRAW | CS_VREDRAW;
\r
934 wc.lpfnWndProc = (WNDPROC)ConsoleWndProc;
\r
936 wc.cbWndExtra = DLGWINDOWEXTRA;
\r
937 wc.hInstance = hInstance;
\r
938 wc.hIcon = LoadIcon(hInstance, "icon_white");
\r
939 wc.hCursor = LoadCursor(NULL, IDC_ARROW);
\r
940 wc.hbrBackground = (HBRUSH)(COLOR_MENU+1);
\r
941 wc.lpszMenuName = NULL;
\r
942 wc.lpszClassName = szConsoleName;
\r
944 if (!RegisterClass(&wc)) return FALSE;
\r
949 /* Set by InitInstance, used by EnsureOnScreen */
\r
950 int screenHeight, screenWidth;
\r
953 EnsureOnScreen(int *x, int *y, int minX, int minY)
\r
955 // int gap = GetSystemMetrics(SM_CYFRAME) + GetSystemMetrics(SM_CYCAPTION);
\r
956 /* Be sure window at (x,y) is not off screen (or even mostly off screen) */
\r
957 if (*x > screenWidth - 32) *x = 0;
\r
958 if (*y > screenHeight - 32) *y = 0;
\r
959 if (*x < minX) *x = minX;
\r
960 if (*y < minY) *y = minY;
\r
964 LoadLogo(ChessProgramState *cps, int n, Boolean ics)
\r
966 char buf[MSG_SIZ], dir[MSG_SIZ];
\r
967 GetCurrentDirectory(MSG_SIZ, dir);
\r
968 SetCurrentDirectory(installDir);
\r
969 if( appData.logo[n] && appData.logo[n][0] != NULLCHAR) {
\r
970 cps->programLogo = LoadImage( 0, appData.logo[n], IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );
\r
972 if (cps->programLogo == NULL && appData.debugMode) {
\r
973 fprintf( debugFP, "Unable to load logo bitmap '%s'\n", appData.logo[n] );
\r
975 } else if(appData.autoLogo) {
\r
976 if(ics) { // [HGM] logo: in ICS mode second can be used for ICS
\r
977 sprintf(buf, "logos\\%s.bmp", appData.icsHost);
\r
978 cps->programLogo = LoadImage( 0, buf, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );
\r
980 if(appData.directory[n] && appData.directory[n][0]) {
\r
981 SetCurrentDirectory(appData.directory[n]);
\r
982 cps->programLogo = LoadImage( 0, "logo.bmp", IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );
\r
985 SetCurrentDirectory(dir); /* return to prev directory */
\r
991 ZeroMemory( &backTextureSquareInfo, sizeof(backTextureSquareInfo) );
\r
992 backTextureSquareSize = 0; // kludge to force recalculation of texturemode
\r
994 if( appData.liteBackTextureFile && appData.liteBackTextureFile[0] != NULLCHAR && appData.liteBackTextureFile[0] != '*' ) {
\r
995 liteBackTexture = LoadImage( 0, appData.liteBackTextureFile, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );
\r
996 liteBackTextureMode = appData.liteBackTextureMode;
\r
998 if (liteBackTexture == NULL && appData.debugMode) {
\r
999 fprintf( debugFP, "Unable to load lite texture bitmap '%s'\n", appData.liteBackTextureFile );
\r
1003 if( appData.darkBackTextureFile && appData.darkBackTextureFile[0] != NULLCHAR && appData.darkBackTextureFile[0] != '*' ) {
\r
1004 darkBackTexture = LoadImage( 0, appData.darkBackTextureFile, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );
\r
1005 darkBackTextureMode = appData.darkBackTextureMode;
\r
1007 if (darkBackTexture == NULL && appData.debugMode) {
\r
1008 fprintf( debugFP, "Unable to load dark texture bitmap '%s'\n", appData.darkBackTextureFile );
\r
1014 InitInstance(HINSTANCE hInstance, int nCmdShow, LPSTR lpCmdLine)
\r
1016 HWND hwnd; /* Main window handle. */
\r
1018 WINDOWPLACEMENT wp;
\r
1021 hInst = hInstance; /* Store instance handle in our global variable */
\r
1022 programName = szAppName;
\r
1024 if (SearchPath(NULL, "WinBoard.exe", NULL, MSG_SIZ, installDir, &filepart)) {
\r
1025 *filepart = NULLCHAR;
\r
1027 GetCurrentDirectory(MSG_SIZ, installDir);
\r
1029 gameInfo.boardWidth = gameInfo.boardHeight = 8; // [HGM] won't have open window otherwise
\r
1030 screenWidth = screenHeight = 1000; // [HGM] placement: kludge to allow calling EnsureOnScreen from InitAppData
\r
1031 InitAppData(lpCmdLine); /* Get run-time parameters */
\r
1032 /* xboard, and older WinBoards, controlled the move sound with the
\r
1033 appData.ringBellAfterMoves option. In the current WinBoard, we
\r
1034 always turn the option on (so that the backend will call us),
\r
1035 then let the user turn the sound off by setting it to silence if
\r
1036 desired. To accommodate old winboard.ini files saved by old
\r
1037 versions of WinBoard, we also turn off the sound if the option
\r
1038 was initially set to false. [HGM] taken out of InitAppData */
\r
1039 if (!appData.ringBellAfterMoves) {
\r
1040 sounds[(int)SoundMove].name = strdup("");
\r
1041 appData.ringBellAfterMoves = TRUE;
\r
1043 if (appData.debugMode) {
\r
1044 debugFP = fopen(appData.nameOfDebugFile, "w");
\r
1045 setbuf(debugFP, NULL);
\r
1048 LoadLanguageFile(appData.language);
\r
1052 // InitEngineUCI( installDir, &first ); // [HGM] incorporated in InitBackEnd1()
\r
1053 // InitEngineUCI( installDir, &second );
\r
1055 /* Create a main window for this application instance. */
\r
1056 hwnd = CreateWindow(szAppName, szTitle,
\r
1057 (WS_OVERLAPPEDWINDOW & ~WS_MAXIMIZEBOX),
\r
1058 CW_USEDEFAULT, 0, CW_USEDEFAULT, 0,
\r
1059 NULL, NULL, hInstance, NULL);
\r
1062 /* If window could not be created, return "failure" */
\r
1067 /* [HGM] logo: Load logos if specified (must be done before InitDrawingSizes) */
\r
1068 LoadLogo(&first, 0, FALSE);
\r
1069 LoadLogo(&second, 1, appData.icsActive);
\r
1073 iconWhite = LoadIcon(hInstance, "icon_white");
\r
1074 iconBlack = LoadIcon(hInstance, "icon_black");
\r
1075 iconCurrent = iconWhite;
\r
1076 InitDrawingColors();
\r
1077 screenHeight = GetSystemMetrics(SM_CYSCREEN);
\r
1078 screenWidth = GetSystemMetrics(SM_CXSCREEN);
\r
1079 for (ibs = (int) NUM_SIZES - 1; ibs >= 0; ibs--) {
\r
1080 /* Compute window size for each board size, and use the largest
\r
1081 size that fits on this screen as the default. */
\r
1082 InitDrawingSizes((BoardSize)(ibs+1000), 0);
\r
1083 if (boardSize == (BoardSize)-1 &&
\r
1084 winH <= screenHeight
\r
1085 - GetSystemMetrics(SM_CYFRAME) - GetSystemMetrics(SM_CYCAPTION) - 10
\r
1086 && winW <= screenWidth) {
\r
1087 boardSize = (BoardSize)ibs;
\r
1091 InitDrawingSizes(boardSize, 0);
\r
1093 buttonCount = GetSystemMetrics(SM_CMOUSEBUTTONS);
\r
1095 /* [AS] Load textures if specified */
\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, TRUE);
\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, TRUE);
\r
1254 while (q > mfp->faceName && q[-1] == ' ') q--;
\r
1257 if (!*p) ExitArgError(_("Font point size missing:"), name, TRUE);
\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, TRUE);
\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.useFont || appData.renderPiecesWithFont == NULL ||
\r
1985 appData.renderPiecesWithFont[0] == NULLCHAR || appData.renderPiecesWithFont[0] == '*' ) {
\r
1986 fontBitmapSquareSize = -1;
\r
1990 if( fontBitmapSquareSize != squareSize ) {
\r
1991 hdc_window = GetDC( hwndMain );
\r
1992 hdc = CreateCompatibleDC( hdc_window );
\r
1994 if( hPieceFont != NULL ) {
\r
1995 DeleteObject( hPieceFont );
\r
1998 for( i=0; i<=(int)BlackKing; i++ ) {
\r
1999 hPieceMask[i] = NULL;
\r
2000 hPieceFace[i] = NULL;
\r
2006 if( appData.fontPieceSize >= 50 && appData.fontPieceSize <= 150 ) {
\r
2007 fontHeight = appData.fontPieceSize;
\r
2010 fontHeight = (fontHeight * squareSize) / 100;
\r
2012 lf.lfHeight = -MulDiv( fontHeight, GetDeviceCaps(hdc, LOGPIXELSY), 72 );
\r
2014 lf.lfEscapement = 0;
\r
2015 lf.lfOrientation = 0;
\r
2016 lf.lfWeight = FW_NORMAL;
\r
2018 lf.lfUnderline = 0;
\r
2019 lf.lfStrikeOut = 0;
\r
2020 lf.lfCharSet = DEFAULT_CHARSET;
\r
2021 lf.lfOutPrecision = OUT_DEFAULT_PRECIS;
\r
2022 lf.lfClipPrecision = CLIP_DEFAULT_PRECIS;
\r
2023 lf.lfQuality = PROOF_QUALITY;
\r
2024 lf.lfPitchAndFamily = DEFAULT_PITCH | FF_DONTCARE;
\r
2025 strncpy( lf.lfFaceName, appData.renderPiecesWithFont, sizeof(lf.lfFaceName) );
\r
2026 lf.lfFaceName[ sizeof(lf.lfFaceName) - 1 ] = '\0';
\r
2028 hPieceFont = CreateFontIndirect( &lf );
\r
2030 if( hPieceFont == NULL ) {
\r
2031 fontBitmapSquareSize = -2;
\r
2034 /* Setup font-to-piece character table */
\r
2035 if( ! SetCharTable(pieceToFontChar, appData.fontToPieceTable) ) {
\r
2036 /* No (or wrong) global settings, try to detect the font */
\r
2037 if( strstr(lf.lfFaceName,"Alpha") != NULL ) {
\r
2039 SetCharTable(pieceToFontChar, "phbrqkojntwl");
\r
2041 else if( strstr(lf.lfFaceName,"DiagramTT") != NULL ) {
\r
2042 /* DiagramTT* family */
\r
2043 SetCharTable(pieceToFontChar, "PNLRQKpnlrqk");
\r
2045 else if( strstr(lf.lfFaceName,"WinboardF") != NULL ) {
\r
2046 /* Fairy symbols */
\r
2047 SetCharTable(pieceToFontChar, "PNBRQFEACWMOHIJGDVSLUKpnbrqfeacwmohijgdvsluk");
\r
2049 else if( strstr(lf.lfFaceName,"GC2004D") != NULL ) {
\r
2050 /* Good Companion (Some characters get warped as literal :-( */
\r
2051 char s[] = "1cmWG0??S??oYI23wgQU";
\r
2052 s[0]=0xB9; s[1]=0xA9; s[6]=0xB1; s[11]=0xBB; s[12]=0xAB; s[17]=0xB3;
\r
2053 SetCharTable(pieceToFontChar, s);
\r
2056 /* Cases, Condal, Leipzig, Lucena, Marroquin, Merida, Usual */
\r
2057 SetCharTable(pieceToFontChar, "pnbrqkomvtwl");
\r
2061 /* Create bitmaps */
\r
2062 hfont_old = SelectObject( hdc, hPieceFont );
\r
2063 for(i=(int)WhitePawn; i<(int)EmptySquare; i++) /* [HGM] made a loop for this */
\r
2064 if(PieceToChar((ChessSquare)i) != '.') /* skip unused pieces */
\r
2065 CreatePieceMaskFromFont( hdc_window, hdc, i );
\r
2067 SelectObject( hdc, hfont_old );
\r
2069 fontBitmapSquareSize = squareSize;
\r
2073 if( hdc != NULL ) {
\r
2077 if( hdc_window != NULL ) {
\r
2078 ReleaseDC( hwndMain, hdc_window );
\r
2083 DoLoadBitmap(HINSTANCE hinst, char *piece, int squareSize, char *suffix)
\r
2087 snprintf(name, sizeof(name)/sizeof(name[0]), "%s%d%s", piece, squareSize, suffix);
\r
2088 if (gameInfo.event &&
\r
2089 strcmp(gameInfo.event, "Easter Egg Hunt") == 0 &&
\r
2090 strcmp(name, "k80s") == 0) {
\r
2091 safeStrCpy(name, "tim", sizeof(name)/sizeof(name[0]) );
\r
2093 return LoadBitmap(hinst, name);
\r
2097 /* Insert a color into the program's logical palette
\r
2098 structure. This code assumes the given color is
\r
2099 the result of the RGB or PALETTERGB macro, and it
\r
2100 knows how those macros work (which is documented).
\r
2103 InsertInPalette(COLORREF color)
\r
2105 LPPALETTEENTRY pe = &(pLogPal->palPalEntry[pLogPal->palNumEntries]);
\r
2107 if (pLogPal->palNumEntries++ >= PALETTESIZE) {
\r
2108 DisplayFatalError(_("Too many colors"), 0, 1);
\r
2109 pLogPal->palNumEntries--;
\r
2113 pe->peFlags = (char) 0;
\r
2114 pe->peRed = (char) (0xFF & color);
\r
2115 pe->peGreen = (char) (0xFF & (color >> 8));
\r
2116 pe->peBlue = (char) (0xFF & (color >> 16));
\r
2122 InitDrawingColors()
\r
2124 if (pLogPal == NULL) {
\r
2125 /* Allocate enough memory for a logical palette with
\r
2126 * PALETTESIZE entries and set the size and version fields
\r
2127 * of the logical palette structure.
\r
2129 pLogPal = (NPLOGPALETTE)
\r
2130 LocalAlloc(LMEM_FIXED, (sizeof(LOGPALETTE) +
\r
2131 (sizeof(PALETTEENTRY) * (PALETTESIZE))));
\r
2132 pLogPal->palVersion = 0x300;
\r
2134 pLogPal->palNumEntries = 0;
\r
2136 InsertInPalette(lightSquareColor);
\r
2137 InsertInPalette(darkSquareColor);
\r
2138 InsertInPalette(whitePieceColor);
\r
2139 InsertInPalette(blackPieceColor);
\r
2140 InsertInPalette(highlightSquareColor);
\r
2141 InsertInPalette(premoveHighlightColor);
\r
2143 /* create a logical color palette according the information
\r
2144 * in the LOGPALETTE structure.
\r
2146 hPal = CreatePalette((LPLOGPALETTE) pLogPal);
\r
2148 lightSquareBrush = CreateSolidBrush(lightSquareColor);
\r
2149 blackSquareBrush = CreateSolidBrush(blackPieceColor);
\r
2150 darkSquareBrush = CreateSolidBrush(darkSquareColor);
\r
2151 whitePieceBrush = CreateSolidBrush(whitePieceColor);
\r
2152 blackPieceBrush = CreateSolidBrush(blackPieceColor);
\r
2153 iconBkgndBrush = CreateSolidBrush(GetSysColor(COLOR_BACKGROUND));
\r
2154 explodeBrush = CreateSolidBrush(highlightSquareColor); // [HGM] atomic
\r
2155 markerBrush = CreateSolidBrush(premoveHighlightColor); // [HGM] markers
\r
2156 /* [AS] Force rendering of the font-based pieces */
\r
2157 if( fontBitmapSquareSize > 0 ) {
\r
2158 fontBitmapSquareSize = 0;
\r
2164 BoardWidth(int boardSize, int n)
\r
2165 { /* [HGM] argument n added to allow different width and height */
\r
2166 int lineGap = sizeInfo[boardSize].lineGap;
\r
2168 if( appData.overrideLineGap >= 0 && appData.overrideLineGap <= 5 ) {
\r
2169 lineGap = appData.overrideLineGap;
\r
2172 return (n + 1) * lineGap +
\r
2173 n * sizeInfo[boardSize].squareSize;
\r
2176 /* Respond to board resize by dragging edge */
\r
2178 ResizeBoard(int newSizeX, int newSizeY, int flags)
\r
2180 BoardSize newSize = NUM_SIZES - 1;
\r
2181 static int recurse = 0;
\r
2182 if (IsIconic(hwndMain)) return;
\r
2183 if (recurse > 0) return;
\r
2185 while (newSize > 0) {
\r
2186 InitDrawingSizes(newSize+1000, 0); // [HGM] kludge to update sizeInfo without visible effects
\r
2187 if(newSizeX >= sizeInfo[newSize].cliWidth &&
\r
2188 newSizeY >= sizeInfo[newSize].cliHeight) break;
\r
2191 boardSize = newSize;
\r
2192 InitDrawingSizes(boardSize, flags);
\r
2197 extern Boolean twoBoards, partnerUp; // [HGM] dual
\r
2200 InitDrawingSizes(BoardSize boardSize, int flags)
\r
2202 int i, boardWidth, boardHeight; /* [HGM] height treated separately */
\r
2203 ChessSquare piece;
\r
2204 static int oldBoardSize = -1, oldTinyLayout = 0;
\r
2206 SIZE clockSize, messageSize;
\r
2208 char buf[MSG_SIZ];
\r
2210 HMENU hmenu = GetMenu(hwndMain);
\r
2211 RECT crect, wrect, oldRect;
\r
2213 LOGBRUSH logbrush;
\r
2215 int suppressVisibleEffects = 0; // [HGM] kludge to request updating sizeInfo only
\r
2216 if((int)boardSize >= 1000 ) { boardSize -= 1000; suppressVisibleEffects = 1; }
\r
2218 /* [HGM] call with -2 uses old size (for if nr of files, ranks changes) */
\r
2219 if(boardSize == (BoardSize)(-2) ) boardSize = oldBoardSize;
\r
2221 oldRect.left = wpMain.x; //[HGM] placement: remember previous window params
\r
2222 oldRect.top = wpMain.y;
\r
2223 oldRect.right = wpMain.x + wpMain.width;
\r
2224 oldRect.bottom = wpMain.y + wpMain.height;
\r
2226 tinyLayout = sizeInfo[boardSize].tinyLayout;
\r
2227 smallLayout = sizeInfo[boardSize].smallLayout;
\r
2228 squareSize = sizeInfo[boardSize].squareSize;
\r
2229 lineGap = sizeInfo[boardSize].lineGap;
\r
2230 minorSize = 0; /* [HGM] Kludge to see if demagnified pieces need to be shifted */
\r
2232 if( appData.overrideLineGap >= 0 && appData.overrideLineGap <= 5 ) {
\r
2233 lineGap = appData.overrideLineGap;
\r
2236 if (tinyLayout != oldTinyLayout) {
\r
2237 long style = GetWindowLongPtr(hwndMain, GWL_STYLE);
\r
2239 style &= ~WS_SYSMENU;
\r
2240 InsertMenu(hmenu, IDM_Exit, MF_BYCOMMAND, IDM_Minimize,
\r
2241 "&Minimize\tCtrl+F4");
\r
2243 style |= WS_SYSMENU;
\r
2244 RemoveMenu(hmenu, IDM_Minimize, MF_BYCOMMAND);
\r
2246 SetWindowLongPtr(hwndMain, GWL_STYLE, style);
\r
2248 for (i=0; menuBarText[tinyLayout][i]; i++) {
\r
2249 ModifyMenu(hmenu, i, MF_STRING|MF_BYPOSITION|MF_POPUP,
\r
2250 (UINT)GetSubMenu(hmenu, i), T_(menuBarText[tinyLayout][i]));
\r
2252 DrawMenuBar(hwndMain);
\r
2255 boardWidth = BoardWidth(boardSize, BOARD_WIDTH);
\r
2256 boardHeight = BoardWidth(boardSize, BOARD_HEIGHT);
\r
2258 /* Get text area sizes */
\r
2259 hdc = GetDC(hwndMain);
\r
2260 if (appData.clockMode) {
\r
2261 snprintf(buf, MSG_SIZ, _("White: %s"), TimeString(23*60*60*1000L));
\r
2263 snprintf(buf, MSG_SIZ, _("White"));
\r
2265 oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);
\r
2266 GetTextExtentPoint(hdc, buf, strlen(buf), &clockSize);
\r
2267 SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);
\r
2268 str = _("We only care about the height here");
\r
2269 GetTextExtentPoint(hdc, str, strlen(str), &messageSize);
\r
2270 SelectObject(hdc, oldFont);
\r
2271 ReleaseDC(hwndMain, hdc);
\r
2273 /* Compute where everything goes */
\r
2274 if((first.programLogo || second.programLogo) && !tinyLayout) {
\r
2275 /* [HGM] logo: if either logo is on, reserve space for it */
\r
2276 logoHeight = 2*clockSize.cy;
\r
2277 leftLogoRect.left = OUTER_MARGIN;
\r
2278 leftLogoRect.right = leftLogoRect.left + 4*clockSize.cy;
\r
2279 leftLogoRect.top = OUTER_MARGIN;
\r
2280 leftLogoRect.bottom = OUTER_MARGIN + logoHeight;
\r
2282 rightLogoRect.right = OUTER_MARGIN + boardWidth;
\r
2283 rightLogoRect.left = rightLogoRect.right - 4*clockSize.cy;
\r
2284 rightLogoRect.top = OUTER_MARGIN;
\r
2285 rightLogoRect.bottom = OUTER_MARGIN + logoHeight;
\r
2288 whiteRect.left = leftLogoRect.right;
\r
2289 whiteRect.right = OUTER_MARGIN + boardWidth/2 - INNER_MARGIN/2;
\r
2290 whiteRect.top = OUTER_MARGIN;
\r
2291 whiteRect.bottom = whiteRect.top + logoHeight;
\r
2293 blackRect.right = rightLogoRect.left;
\r
2294 blackRect.left = whiteRect.right + INNER_MARGIN;
\r
2295 blackRect.top = whiteRect.top;
\r
2296 blackRect.bottom = whiteRect.bottom;
\r
2298 whiteRect.left = OUTER_MARGIN;
\r
2299 whiteRect.right = whiteRect.left + boardWidth/2 - INNER_MARGIN/2;
\r
2300 whiteRect.top = OUTER_MARGIN;
\r
2301 whiteRect.bottom = whiteRect.top + clockSize.cy;
\r
2303 blackRect.left = whiteRect.right + INNER_MARGIN;
\r
2304 blackRect.right = blackRect.left + boardWidth/2 - 1;
\r
2305 blackRect.top = whiteRect.top;
\r
2306 blackRect.bottom = whiteRect.bottom;
\r
2308 logoHeight = 0; // [HGM] logo: suppress logo after change to tiny layout!
\r
2311 messageRect.left = OUTER_MARGIN + MESSAGE_LINE_LEFTMARGIN;
\r
2312 if (appData.showButtonBar) {
\r
2313 messageRect.right = OUTER_MARGIN + boardWidth // [HGM] logo: expressed independent of clock placement
\r
2314 - N_BUTTONS*BUTTON_WIDTH - MESSAGE_LINE_LEFTMARGIN;
\r
2316 messageRect.right = OUTER_MARGIN + boardWidth;
\r
2318 messageRect.top = whiteRect.bottom + INNER_MARGIN;
\r
2319 messageRect.bottom = messageRect.top + messageSize.cy;
\r
2321 boardRect.left = OUTER_MARGIN;
\r
2322 boardRect.right = boardRect.left + boardWidth;
\r
2323 boardRect.top = messageRect.bottom + INNER_MARGIN;
\r
2324 boardRect.bottom = boardRect.top + boardHeight;
\r
2326 sizeInfo[boardSize].cliWidth = boardRect.right + OUTER_MARGIN;
\r
2327 sizeInfo[boardSize].cliHeight = boardRect.bottom + OUTER_MARGIN;
\r
2328 oldBoardSize = boardSize;
\r
2329 oldTinyLayout = tinyLayout;
\r
2330 winW = 2 * GetSystemMetrics(SM_CXFRAME) + boardRect.right + OUTER_MARGIN;
\r
2331 winH = 2 * GetSystemMetrics(SM_CYFRAME) + GetSystemMetrics(SM_CYMENU) +
\r
2332 GetSystemMetrics(SM_CYCAPTION) + boardRect.bottom + OUTER_MARGIN;
\r
2333 winW *= 1 + twoBoards;
\r
2334 if(suppressVisibleEffects) return; // [HGM] when called for filling sizeInfo only
\r
2335 wpMain.width = winW; // [HGM] placement: set through temporary which can used by initial sizing choice
\r
2336 wpMain.height = winH; // without disturbing window attachments
\r
2337 GetWindowRect(hwndMain, &wrect);
\r
2338 SetWindowPos(hwndMain, NULL, 0, 0, wpMain.width, wpMain.height,
\r
2339 SWP_NOCOPYBITS|SWP_NOZORDER|SWP_NOMOVE);
\r
2341 // [HGM] placement: let attached windows follow size change.
\r
2342 ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, moveHistoryDialog, &wpMoveHistory );
\r
2343 ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, evalGraphDialog, &wpEvalGraph );
\r
2344 ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, engineOutputDialog, &wpEngineOutput );
\r
2345 ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, gameListDialog, &wpGameList );
\r
2346 ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, hwndConsole, &wpConsole );
\r
2348 /* compensate if menu bar wrapped */
\r
2349 GetClientRect(hwndMain, &crect);
\r
2350 offby = boardRect.bottom + OUTER_MARGIN - crect.bottom;
\r
2351 wpMain.height += offby;
\r
2353 case WMSZ_TOPLEFT:
\r
2354 SetWindowPos(hwndMain, NULL,
\r
2355 wrect.right - wpMain.width, wrect.bottom - wpMain.height,
\r
2356 wpMain.width, wpMain.height, SWP_NOCOPYBITS|SWP_NOZORDER);
\r
2359 case WMSZ_TOPRIGHT:
\r
2361 SetWindowPos(hwndMain, NULL,
\r
2362 wrect.left, wrect.bottom - wpMain.height,
\r
2363 wpMain.width, wpMain.height, SWP_NOCOPYBITS|SWP_NOZORDER);
\r
2366 case WMSZ_BOTTOMLEFT:
\r
2368 SetWindowPos(hwndMain, NULL,
\r
2369 wrect.right - wpMain.width, wrect.top,
\r
2370 wpMain.width, wpMain.height, SWP_NOCOPYBITS|SWP_NOZORDER);
\r
2373 case WMSZ_BOTTOMRIGHT:
\r
2377 SetWindowPos(hwndMain, NULL, 0, 0, wpMain.width, wpMain.height,
\r
2378 SWP_NOCOPYBITS|SWP_NOZORDER|SWP_NOMOVE);
\r
2383 for (i = 0; i < N_BUTTONS; i++) {
\r
2384 if (buttonDesc[i].hwnd != NULL) {
\r
2385 DestroyWindow(buttonDesc[i].hwnd);
\r
2386 buttonDesc[i].hwnd = NULL;
\r
2388 if (appData.showButtonBar) {
\r
2389 buttonDesc[i].hwnd =
\r
2390 CreateWindow("BUTTON", buttonDesc[i].label,
\r
2391 WS_VISIBLE | WS_CHILD | BS_PUSHBUTTON,
\r
2392 boardRect.right - BUTTON_WIDTH*(N_BUTTONS-i),
\r
2393 messageRect.top, BUTTON_WIDTH, messageSize.cy, hwndMain,
\r
2394 (HMENU) buttonDesc[i].id,
\r
2395 (HINSTANCE) GetWindowLongPtr(hwndMain, GWLP_HINSTANCE), NULL);
\r
2397 SendMessage(buttonDesc[i].hwnd, WM_SETFONT,
\r
2398 (WPARAM)font[boardSize][MESSAGE_FONT]->hf,
\r
2399 MAKELPARAM(FALSE, 0));
\r
2401 if (buttonDesc[i].id == IDM_Pause)
\r
2402 hwndPause = buttonDesc[i].hwnd;
\r
2403 buttonDesc[i].wndproc = (WNDPROC)
\r
2404 SetWindowLongPtr(buttonDesc[i].hwnd, GWLP_WNDPROC, (LONG_PTR) ButtonProc);
\r
2407 if (gridPen != NULL) DeleteObject(gridPen);
\r
2408 if (highlightPen != NULL) DeleteObject(highlightPen);
\r
2409 if (premovePen != NULL) DeleteObject(premovePen);
\r
2410 if (lineGap != 0) {
\r
2411 logbrush.lbStyle = BS_SOLID;
\r
2412 logbrush.lbColor = RGB(0, 0, 0); /* grid pen color = black */
\r
2414 ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,
\r
2415 lineGap, &logbrush, 0, NULL);
\r
2416 logbrush.lbColor = highlightSquareColor;
\r
2418 ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,
\r
2419 lineGap, &logbrush, 0, NULL);
\r
2421 logbrush.lbColor = premoveHighlightColor;
\r
2423 ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,
\r
2424 lineGap, &logbrush, 0, NULL);
\r
2426 /* [HGM] Loop had to be split in part for vert. and hor. lines */
\r
2427 for (i = 0; i < BOARD_HEIGHT + 1; i++) {
\r
2428 gridEndpoints[i*2].x = boardRect.left + lineGap / 2;
\r
2429 gridEndpoints[i*2].y = gridEndpoints[i*2 + 1].y =
\r
2430 boardRect.top + lineGap / 2 + (i * (squareSize + lineGap));
\r
2431 gridEndpoints[i*2 + 1].x = boardRect.left + lineGap / 2 +
\r
2432 BOARD_WIDTH * (squareSize + lineGap);
\r
2433 gridVertexCounts[i*2] = gridVertexCounts[i*2 + 1] = 2;
\r
2435 for (i = 0; i < BOARD_WIDTH + 1; i++) {
\r
2436 gridEndpoints[i*2 + BOARD_HEIGHT*2 + 2].y = boardRect.top + lineGap / 2;
\r
2437 gridEndpoints[i*2 + BOARD_HEIGHT*2 + 2].x =
\r
2438 gridEndpoints[i*2 + 1 + BOARD_HEIGHT*2 + 2].x = boardRect.left +
\r
2439 lineGap / 2 + (i * (squareSize + lineGap));
\r
2440 gridEndpoints[i*2 + 1 + BOARD_HEIGHT*2 + 2].y =
\r
2441 boardRect.top + BOARD_HEIGHT * (squareSize + lineGap);
\r
2442 gridVertexCounts[i*2] = gridVertexCounts[i*2 + 1] = 2;
\r
2446 /* [HGM] Licensing requirement */
\r
2448 if(gameInfo.variant == VariantGothic) GothicPopUp( GOTHIC, VariantGothic); else
\r
2451 if(gameInfo.variant == VariantFalcon) GothicPopUp( FALCON, VariantFalcon); else
\r
2453 GothicPopUp( "", VariantNormal);
\r
2456 /* if (boardSize == oldBoardSize) return; [HGM] variant might have changed */
\r
2458 /* Load piece bitmaps for this board size */
\r
2459 for (i=0; i<=2; i++) {
\r
2460 for (piece = WhitePawn;
\r
2461 (int) piece < (int) BlackPawn;
\r
2462 piece = (ChessSquare) ((int) piece + 1)) {
\r
2463 if (pieceBitmap[i][piece] != NULL)
\r
2464 DeleteObject(pieceBitmap[i][piece]);
\r
2468 fontBitmapSquareSize = 0; /* [HGM] render: make sure pieces will be recreated, as we might need others now */
\r
2469 // Orthodox Chess pieces
\r
2470 pieceBitmap[0][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "s");
\r
2471 pieceBitmap[0][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "s");
\r
2472 pieceBitmap[0][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "s");
\r
2473 pieceBitmap[0][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "s");
\r
2474 pieceBitmap[0][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "s");
\r
2475 pieceBitmap[1][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "o");
\r
2476 pieceBitmap[1][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "o");
\r
2477 pieceBitmap[1][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "o");
\r
2478 pieceBitmap[1][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "o");
\r
2479 pieceBitmap[1][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "o");
\r
2480 pieceBitmap[2][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "w");
\r
2481 pieceBitmap[2][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "w");
\r
2482 pieceBitmap[2][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "w");
\r
2483 pieceBitmap[2][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "w");
\r
2484 pieceBitmap[2][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "w");
\r
2485 if( gameInfo.variant == VariantShogi && squareSize <= 72 && squareSize >= 33) {
\r
2486 // in Shogi, Hijack the unused Queen for Lance
\r
2487 pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "s");
\r
2488 pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "o");
\r
2489 pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "w");
\r
2491 pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "s");
\r
2492 pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "o");
\r
2493 pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "w");
\r
2496 if(squareSize <= 72 && squareSize >= 33) {
\r
2497 /* A & C are available in most sizes now */
\r
2498 if(squareSize != 49 && squareSize != 72 && squareSize != 33) { // Vortex-like
\r
2499 pieceBitmap[0][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "s");
\r
2500 pieceBitmap[1][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "o");
\r
2501 pieceBitmap[2][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "w");
\r
2502 pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "s");
\r
2503 pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "o");
\r
2504 pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "w");
\r
2505 pieceBitmap[0][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "s");
\r
2506 pieceBitmap[1][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "o");
\r
2507 pieceBitmap[2][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "w");
\r
2508 pieceBitmap[0][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "s");
\r
2509 pieceBitmap[1][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "o");
\r
2510 pieceBitmap[2][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "w");
\r
2511 } else { // Smirf-like
\r
2512 if(gameInfo.variant == VariantSChess) {
\r
2513 pieceBitmap[0][WhiteAngel] = DoLoadBitmap(hInst, "v", squareSize, "s");
\r
2514 pieceBitmap[1][WhiteAngel] = DoLoadBitmap(hInst, "v", squareSize, "o");
\r
2515 pieceBitmap[2][WhiteAngel] = DoLoadBitmap(hInst, "v", squareSize, "w");
\r
2517 pieceBitmap[0][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "s");
\r
2518 pieceBitmap[1][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "o");
\r
2519 pieceBitmap[2][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "w");
\r
2522 if(gameInfo.variant == VariantGothic) { // Vortex-like
\r
2523 pieceBitmap[0][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "s");
\r
2524 pieceBitmap[1][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "o");
\r
2525 pieceBitmap[2][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "w");
\r
2526 } else if(gameInfo.variant == VariantSChess && (squareSize == 49 || squareSize == 72)) {
\r
2527 pieceBitmap[0][WhiteMarshall] = DoLoadBitmap(hInst, "e", squareSize, "s");
\r
2528 pieceBitmap[1][WhiteMarshall] = DoLoadBitmap(hInst, "e", squareSize, "o");
\r
2529 pieceBitmap[2][WhiteMarshall] = DoLoadBitmap(hInst, "e", squareSize, "w");
\r
2530 } else { // WinBoard standard
\r
2531 pieceBitmap[0][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "s");
\r
2532 pieceBitmap[1][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "o");
\r
2533 pieceBitmap[2][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "w");
\r
2538 if(squareSize==72 || squareSize==49 || squareSize==33) { /* experiment with some home-made bitmaps */
\r
2539 pieceBitmap[0][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "s");
\r
2540 pieceBitmap[1][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "o");
\r
2541 pieceBitmap[2][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "w");
\r
2542 pieceBitmap[0][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "s");
\r
2543 pieceBitmap[1][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "o");
\r
2544 pieceBitmap[2][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "w");
\r
2545 pieceBitmap[0][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "s");
\r
2546 pieceBitmap[1][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "o");
\r
2547 pieceBitmap[2][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "w");
\r
2548 pieceBitmap[0][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "s");
\r
2549 pieceBitmap[1][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "o");
\r
2550 pieceBitmap[2][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "w");
\r
2551 pieceBitmap[0][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "s");
\r
2552 pieceBitmap[1][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "o");
\r
2553 pieceBitmap[2][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "w");
\r
2554 pieceBitmap[0][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "s");
\r
2555 pieceBitmap[1][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "o");
\r
2556 pieceBitmap[2][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "w");
\r
2557 pieceBitmap[0][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "s");
\r
2558 pieceBitmap[1][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "o");
\r
2559 pieceBitmap[2][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "w");
\r
2560 pieceBitmap[0][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "s");
\r
2561 pieceBitmap[1][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "o");
\r
2562 pieceBitmap[2][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "w");
\r
2563 pieceBitmap[0][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "s");
\r
2564 pieceBitmap[1][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "o");
\r
2565 pieceBitmap[2][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "w");
\r
2566 pieceBitmap[0][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "s");
\r
2567 pieceBitmap[1][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "o");
\r
2568 pieceBitmap[2][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "w");
\r
2570 if(gameInfo.variant == VariantShogi) { /* promoted Gold represemtations */
\r
2571 pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "wp", squareSize, "s");
\r
2572 pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "wp", squareSize, "o");
\r
2573 pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "w", squareSize, "w");
\r
2574 pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "wn", squareSize, "s");
\r
2575 pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "wn", squareSize, "o");
\r
2576 pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "w", squareSize, "w");
\r
2577 pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "ws", squareSize, "s");
\r
2578 pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "ws", squareSize, "o");
\r
2579 pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "w", squareSize, "w");
\r
2580 pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "wl", squareSize, "s");
\r
2581 pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "wl", squareSize, "o");
\r
2582 pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "w", squareSize, "w");
\r
2584 pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "s");
\r
2585 pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "o");
\r
2586 pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "w");
\r
2587 pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "s");
\r
2588 pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "o");
\r
2589 pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "w");
\r
2590 pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "s");
\r
2591 pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "o");
\r
2592 pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "w");
\r
2593 pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "s");
\r
2594 pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "o");
\r
2595 pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "w");
\r
2598 } else { /* other size, no special bitmaps available. Use smaller symbols */
\r
2599 if((int)boardSize < 2) minorSize = sizeInfo[0].squareSize;
\r
2600 else minorSize = sizeInfo[(int)boardSize - 2].squareSize;
\r
2601 pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "s");
\r
2602 pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "o");
\r
2603 pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "w");
\r
2604 pieceBitmap[0][WhiteCardinal] = DoLoadBitmap(hInst, "b", minorSize, "s");
\r
2605 pieceBitmap[1][WhiteCardinal] = DoLoadBitmap(hInst, "b", minorSize, "o");
\r
2606 pieceBitmap[2][WhiteCardinal] = DoLoadBitmap(hInst, "b", minorSize, "w");
\r
2607 pieceBitmap[0][WhiteDragon] = DoLoadBitmap(hInst, "r", minorSize, "s");
\r
2608 pieceBitmap[1][WhiteDragon] = DoLoadBitmap(hInst, "r", minorSize, "o");
\r
2609 pieceBitmap[2][WhiteDragon] = DoLoadBitmap(hInst, "r", minorSize, "w");
\r
2610 pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "s");
\r
2611 pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "o");
\r
2612 pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "w");
\r
2616 if(gameInfo.variant == VariantShogi && squareSize == 58)
\r
2617 /* special Shogi support in this size */
\r
2618 { for (i=0; i<=2; i++) { /* replace all bitmaps */
\r
2619 for (piece = WhitePawn;
\r
2620 (int) piece < (int) BlackPawn;
\r
2621 piece = (ChessSquare) ((int) piece + 1)) {
\r
2622 if (pieceBitmap[i][piece] != NULL)
\r
2623 DeleteObject(pieceBitmap[i][piece]);
\r
2626 pieceBitmap[0][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "o");
\r
2627 pieceBitmap[0][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "o");
\r
2628 pieceBitmap[0][WhiteBishop] = DoLoadBitmap(hInst, "sb", squareSize, "o");
\r
2629 pieceBitmap[0][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "o");
\r
2630 pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "o");
\r
2631 pieceBitmap[0][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "o");
\r
2632 pieceBitmap[0][WhiteFerz] = DoLoadBitmap(hInst, "sf", squareSize, "o");
\r
2633 pieceBitmap[0][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "o");
\r
2634 pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "su", squareSize, "o");
\r
2635 pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "sh", squareSize, "o");
\r
2636 pieceBitmap[0][WhiteCardinal] = DoLoadBitmap(hInst, "sa", squareSize, "o");
\r
2637 pieceBitmap[0][WhiteDragon] = DoLoadBitmap(hInst, "sc", squareSize, "o");
\r
2638 pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "sg", squareSize, "o");
\r
2639 pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "ss", squareSize, "o");
\r
2640 pieceBitmap[1][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "o");
\r
2641 pieceBitmap[1][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "o");
\r
2642 pieceBitmap[1][WhiteBishop] = DoLoadBitmap(hInst, "sb", squareSize, "o");
\r
2643 pieceBitmap[1][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "o");
\r
2644 pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "o");
\r
2645 pieceBitmap[1][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "o");
\r
2646 pieceBitmap[1][WhiteFerz] = DoLoadBitmap(hInst, "sf", squareSize, "o");
\r
2647 pieceBitmap[1][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "o");
\r
2648 pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "su", squareSize, "o");
\r
2649 pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "sh", squareSize, "o");
\r
2650 pieceBitmap[1][WhiteCardinal] = DoLoadBitmap(hInst, "sa", squareSize, "o");
\r
2651 pieceBitmap[1][WhiteDragon] = DoLoadBitmap(hInst, "sc", squareSize, "o");
\r
2652 pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "sg", squareSize, "o");
\r
2653 pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "ss", squareSize, "o");
\r
2654 pieceBitmap[2][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "w");
\r
2655 pieceBitmap[2][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "w");
\r
2656 pieceBitmap[2][WhiteBishop] = DoLoadBitmap(hInst, "sr", squareSize, "w");
\r
2657 pieceBitmap[2][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "w");
\r
2658 pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "w");
\r
2659 pieceBitmap[2][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "w");
\r
2660 pieceBitmap[2][WhiteFerz] = DoLoadBitmap(hInst, "sw", squareSize, "w");
\r
2661 pieceBitmap[2][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "w");
\r
2662 pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "sp", squareSize, "w");
\r
2663 pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "sn", squareSize, "w");
\r
2664 pieceBitmap[2][WhiteCardinal] = DoLoadBitmap(hInst, "sr", squareSize, "w");
\r
2665 pieceBitmap[2][WhiteDragon] = DoLoadBitmap(hInst, "sr", squareSize, "w");
\r
2666 pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "sl", squareSize, "w");
\r
2667 pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "sw", squareSize, "w");
\r
2673 PieceBitmap(ChessSquare p, int kind)
\r
2675 if ((int) p >= (int) BlackPawn)
\r
2676 p = (ChessSquare) ((int) p - (int) BlackPawn + (int) WhitePawn);
\r
2678 return pieceBitmap[kind][(int) p];
\r
2681 /***************************************************************/
\r
2683 #define MIN(a,b) (((a) < (b)) ? (a) : (b))
\r
2684 #define MAX(a,b) (((a) > (b)) ? (a) : (b))
\r
2686 #define MIN3(a,b,c) (((a) < (b) && (a) < (c)) ? (a) : (((b) < (a) && (b) < (c)) ? (b) : (c)))
\r
2687 #define MAX3(a,b,c) (((a) > (b) && (a) > (c)) ? (a) : (((b) > (a) && (b) > (c)) ? (b) : (c)))
\r
2691 SquareToPos(int row, int column, int * x, int * y)
\r
2694 *x = boardRect.left + lineGap + ((BOARD_WIDTH-1)-column) * (squareSize + lineGap);
\r
2695 *y = boardRect.top + lineGap + row * (squareSize + lineGap);
\r
2697 *x = boardRect.left + lineGap + column * (squareSize + lineGap);
\r
2698 *y = boardRect.top + lineGap + ((BOARD_HEIGHT-1)-row) * (squareSize + lineGap);
\r
2703 DrawCoordsOnDC(HDC hdc)
\r
2705 static char files[] = "0123456789012345678901221098765432109876543210";
\r
2706 static char ranks[] = "wvutsrqponmlkjihgfedcbaabcdefghijklmnopqrstuvw";
\r
2707 char str[2] = { NULLCHAR, NULLCHAR };
\r
2708 int oldMode, oldAlign, x, y, start, i;
\r
2712 if (!appData.showCoords)
\r
2715 start = flipView ? 1-(ONE!='1') : 45+(ONE!='1')-BOARD_HEIGHT;
\r
2717 oldBrush = SelectObject(hdc, GetStockObject(BLACK_BRUSH));
\r
2718 oldMode = SetBkMode(hdc, (appData.monoMode ? OPAQUE : TRANSPARENT));
\r
2719 oldAlign = GetTextAlign(hdc);
\r
2720 oldFont = SelectObject(hdc, font[boardSize][COORD_FONT]->hf);
\r
2722 y = boardRect.top + lineGap;
\r
2723 x = boardRect.left + lineGap + gameInfo.holdingsWidth*(squareSize + lineGap);
\r
2725 SetTextAlign(hdc, TA_LEFT|TA_TOP);
\r
2726 for (i = 0; i < BOARD_HEIGHT; i++) {
\r
2727 str[0] = files[start + i];
\r
2728 ExtTextOut(hdc, x + 2, y + 1, 0, NULL, str, 1, NULL);
\r
2729 y += squareSize + lineGap;
\r
2732 start = flipView ? 23-(BOARD_RGHT-BOARD_LEFT) : 23;
\r
2734 SetTextAlign(hdc, TA_RIGHT|TA_BOTTOM);
\r
2735 for (i = 0; i < BOARD_RGHT - BOARD_LEFT; i++) {
\r
2736 str[0] = ranks[start + i];
\r
2737 ExtTextOut(hdc, x + squareSize - 2, y - 1, 0, NULL, str, 1, NULL);
\r
2738 x += squareSize + lineGap;
\r
2741 SelectObject(hdc, oldBrush);
\r
2742 SetBkMode(hdc, oldMode);
\r
2743 SetTextAlign(hdc, oldAlign);
\r
2744 SelectObject(hdc, oldFont);
\r
2748 DrawGridOnDC(HDC hdc)
\r
2752 if (lineGap != 0) {
\r
2753 oldPen = SelectObject(hdc, gridPen);
\r
2754 PolyPolyline(hdc, gridEndpoints, gridVertexCounts, BOARD_WIDTH+BOARD_HEIGHT + 2);
\r
2755 SelectObject(hdc, oldPen);
\r
2759 #define HIGHLIGHT_PEN 0
\r
2760 #define PREMOVE_PEN 1
\r
2763 DrawHighlightOnDC(HDC hdc, BOOLEAN on, int x, int y, int pen)
\r
2766 HPEN oldPen, hPen;
\r
2767 if (lineGap == 0) return;
\r
2769 x1 = boardRect.left +
\r
2770 lineGap/2 + ((BOARD_WIDTH-1)-x) * (squareSize + lineGap);
\r
2771 y1 = boardRect.top +
\r
2772 lineGap/2 + y * (squareSize + lineGap);
\r
2774 x1 = boardRect.left +
\r
2775 lineGap/2 + x * (squareSize + lineGap);
\r
2776 y1 = boardRect.top +
\r
2777 lineGap/2 + ((BOARD_HEIGHT-1)-y) * (squareSize + lineGap);
\r
2779 hPen = pen ? premovePen : highlightPen;
\r
2780 oldPen = SelectObject(hdc, on ? hPen : gridPen);
\r
2781 MoveToEx(hdc, x1, y1, NULL);
\r
2782 LineTo(hdc, x1 + squareSize + lineGap, y1);
\r
2783 LineTo(hdc, x1 + squareSize + lineGap, y1 + squareSize + lineGap);
\r
2784 LineTo(hdc, x1, y1 + squareSize + lineGap);
\r
2785 LineTo(hdc, x1, y1);
\r
2786 SelectObject(hdc, oldPen);
\r
2790 DrawHighlightsOnDC(HDC hdc, HighlightInfo *h, int pen)
\r
2793 for (i=0; i<2; i++) {
\r
2794 if (h->sq[i].x >= 0 && h->sq[i].y >= 0)
\r
2795 DrawHighlightOnDC(hdc, TRUE,
\r
2796 h->sq[i].x, h->sq[i].y,
\r
2801 /* Note: sqcolor is used only in monoMode */
\r
2802 /* Note that this code is largely duplicated in woptions.c,
\r
2803 function DrawSampleSquare, so that needs to be updated too */
\r
2805 DrawPieceOnDC(HDC hdc, ChessSquare piece, int color, int sqcolor, int x, int y, HDC tmphdc)
\r
2807 HBITMAP oldBitmap;
\r
2811 if (appData.blindfold) return;
\r
2813 /* [AS] Use font-based pieces if needed */
\r
2814 if( fontBitmapSquareSize >= 0 && (squareSize > 32 || gameInfo.variant >= VariantShogi)) {
\r
2815 /* Create piece bitmaps, or do nothing if piece set is up to date */
\r
2816 CreatePiecesFromFont();
\r
2818 if( fontBitmapSquareSize == squareSize ) {
\r
2819 int index = TranslatePieceToFontPiece(piece);
\r
2821 SelectObject( tmphdc, hPieceMask[ index ] );
\r
2823 if(appData.upsideDown ? color==flipView : (flipView && gameInfo.variant == VariantShogi))
\r
2824 StretchBlt(hdc, x+squareSize, y+squareSize, -squareSize, -squareSize, tmphdc, 0, 0, squareSize, squareSize, SRCAND);
\r
2828 squareSize, squareSize,
\r
2833 SelectObject( tmphdc, hPieceFace[ index ] );
\r
2835 if(appData.upsideDown ? color==flipView : (flipView && gameInfo.variant == VariantShogi))
\r
2836 StretchBlt(hdc, x+squareSize, y+squareSize, -squareSize, -squareSize, tmphdc, 0, 0, squareSize, squareSize, SRCPAINT);
\r
2840 squareSize, squareSize,
\r
2849 if (appData.monoMode) {
\r
2850 SelectObject(tmphdc, PieceBitmap(piece,
\r
2851 color == sqcolor ? OUTLINE_PIECE : SOLID_PIECE));
\r
2852 BitBlt(hdc, x, y, squareSize, squareSize, tmphdc, 0, 0,
\r
2853 sqcolor ? SRCCOPY : NOTSRCCOPY);
\r
2855 tmpSize = squareSize;
\r
2857 ((piece >= (int)WhiteNightrider && piece <= WhiteGrasshopper) ||
\r
2858 (piece >= (int)BlackNightrider && piece <= BlackGrasshopper)) ) {
\r
2859 /* [HGM] no bitmap available for promoted pieces in Crazyhouse */
\r
2860 /* Bitmaps of smaller size are substituted, but we have to align them */
\r
2861 x += (squareSize - minorSize)>>1;
\r
2862 y += squareSize - minorSize - 2;
\r
2863 tmpSize = minorSize;
\r
2865 if (color || appData.allWhite ) {
\r
2866 oldBitmap = SelectObject(tmphdc, PieceBitmap(piece, WHITE_PIECE));
\r
2868 oldBrush = SelectObject(hdc, whitePieceBrush);
\r
2869 else oldBrush = SelectObject(hdc, blackPieceBrush);
\r
2870 if(appData.upsideDown && color==flipView)
\r
2871 StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, 0x00B8074A);
\r
2873 BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, 0x00B8074A);
\r
2874 /* Use black for outline of white pieces */
\r
2875 SelectObject(tmphdc, PieceBitmap(piece, OUTLINE_PIECE));
\r
2876 if(appData.upsideDown && color==flipView)
\r
2877 StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, SRCAND);
\r
2879 BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, SRCAND);
\r
2881 /* Use square color for details of black pieces */
\r
2882 oldBitmap = SelectObject(tmphdc, PieceBitmap(piece, SOLID_PIECE));
\r
2883 oldBrush = SelectObject(hdc, blackPieceBrush);
\r
2884 if(appData.upsideDown && !flipView)
\r
2885 StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, 0x00B8074A);
\r
2887 BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, 0x00B8074A);
\r
2889 SelectObject(hdc, oldBrush);
\r
2890 SelectObject(tmphdc, oldBitmap);
\r
2894 /* [AS] Compute a drawing mode for a square, based on specified settings (see DrawTile) */
\r
2895 int GetBackTextureMode( int algo )
\r
2897 int result = BACK_TEXTURE_MODE_DISABLED;
\r
2901 case BACK_TEXTURE_MODE_PLAIN:
\r
2902 result = 1; /* Always use identity map */
\r
2904 case BACK_TEXTURE_MODE_FULL_RANDOM:
\r
2905 result = 1 + (myrandom() % 3); /* Pick a transformation at random */
\r
2913 [AS] Compute and save texture drawing info, otherwise we may not be able
\r
2914 to handle redraws cleanly (as random numbers would always be different).
\r
2916 VOID RebuildTextureSquareInfo()
\r
2926 ZeroMemory( &backTextureSquareInfo, sizeof(backTextureSquareInfo) );
\r
2928 if( liteBackTexture != NULL ) {
\r
2929 if( GetObject( liteBackTexture, sizeof(bi), &bi ) > 0 ) {
\r
2930 lite_w = bi.bmWidth;
\r
2931 lite_h = bi.bmHeight;
\r
2935 if( darkBackTexture != NULL ) {
\r
2936 if( GetObject( darkBackTexture, sizeof(bi), &bi ) > 0 ) {
\r
2937 dark_w = bi.bmWidth;
\r
2938 dark_h = bi.bmHeight;
\r
2942 for( row=0; row<BOARD_HEIGHT; row++ ) {
\r
2943 for( col=0; col<BOARD_WIDTH; col++ ) {
\r
2944 if( (col + row) & 1 ) {
\r
2946 if( lite_w >= squareSize && lite_h >= squareSize ) {
\r
2947 if( lite_w >= squareSize*BOARD_WIDTH )
\r
2948 backTextureSquareInfo[row][col].x = (2*col+1)*lite_w/(2*BOARD_WIDTH) - squareSize/2; /* [HGM] cut out of center of virtual square */
\r
2950 backTextureSquareInfo[row][col].x = col * (lite_w - squareSize) / (BOARD_WIDTH-1); /* [HGM] divide by size-1 in stead of size! */
\r
2951 if( lite_h >= squareSize*BOARD_HEIGHT )
\r
2952 backTextureSquareInfo[row][col].y = (2*(BOARD_HEIGHT-row)-1)*lite_h/(2*BOARD_HEIGHT) - squareSize/2;
\r
2954 backTextureSquareInfo[row][col].y = (BOARD_HEIGHT-1-row) * (lite_h - squareSize) / (BOARD_HEIGHT-1);
\r
2955 backTextureSquareInfo[row][col].mode = GetBackTextureMode(liteBackTextureMode);
\r
2960 if( dark_w >= squareSize && dark_h >= squareSize ) {
\r
2961 if( dark_w >= squareSize*BOARD_WIDTH )
\r
2962 backTextureSquareInfo[row][col].x = (2*col+1) * dark_w / (2*BOARD_WIDTH) - squareSize/2;
\r
2964 backTextureSquareInfo[row][col].x = col * (dark_w - squareSize) / (BOARD_WIDTH-1);
\r
2965 if( dark_h >= squareSize*BOARD_HEIGHT )
\r
2966 backTextureSquareInfo[row][col].y = (2*(BOARD_HEIGHT-row)-1) * dark_h / (2*BOARD_HEIGHT) - squareSize/2;
\r
2968 backTextureSquareInfo[row][col].y = (BOARD_HEIGHT-1-row) * (dark_h - squareSize) / (BOARD_HEIGHT-1);
\r
2969 backTextureSquareInfo[row][col].mode = GetBackTextureMode(darkBackTextureMode);
\r
2976 /* [AS] Arrow highlighting support */
\r
2978 static double A_WIDTH = 5; /* Width of arrow body */
\r
2980 #define A_HEIGHT_FACTOR 6 /* Length of arrow "point", relative to body width */
\r
2981 #define A_WIDTH_FACTOR 3 /* Width of arrow "point", relative to body width */
\r
2983 static double Sqr( double x )
\r
2988 static int Round( double x )
\r
2990 return (int) (x + 0.5);
\r
2993 /* Draw an arrow between two points using current settings */
\r
2994 VOID DrawArrowBetweenPoints( HDC hdc, int s_x, int s_y, int d_x, int d_y )
\r
2997 double dx, dy, j, k, x, y;
\r
2999 if( d_x == s_x ) {
\r
3000 int h = (d_y > s_y) ? +A_WIDTH*A_HEIGHT_FACTOR : -A_WIDTH*A_HEIGHT_FACTOR;
\r
3002 arrow[0].x = s_x + A_WIDTH + 0.5;
\r
3005 arrow[1].x = s_x + A_WIDTH + 0.5;
\r
3006 arrow[1].y = d_y - h;
\r
3008 arrow[2].x = arrow[1].x + A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;
\r
3009 arrow[2].y = d_y - h;
\r
3014 arrow[5].x = arrow[1].x - 2*A_WIDTH + 0.5;
\r
3015 arrow[5].y = d_y - h;
\r
3017 arrow[4].x = arrow[5].x - A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;
\r
3018 arrow[4].y = d_y - h;
\r
3020 arrow[6].x = arrow[1].x - 2*A_WIDTH + 0.5;
\r
3023 else if( d_y == s_y ) {
\r
3024 int w = (d_x > s_x) ? +A_WIDTH*A_HEIGHT_FACTOR : -A_WIDTH*A_HEIGHT_FACTOR;
\r
3027 arrow[0].y = s_y + A_WIDTH + 0.5;
\r
3029 arrow[1].x = d_x - w;
\r
3030 arrow[1].y = s_y + A_WIDTH + 0.5;
\r
3032 arrow[2].x = d_x - w;
\r
3033 arrow[2].y = arrow[1].y + A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;
\r
3038 arrow[5].x = d_x - w;
\r
3039 arrow[5].y = arrow[1].y - 2*A_WIDTH + 0.5;
\r
3041 arrow[4].x = d_x - w;
\r
3042 arrow[4].y = arrow[5].y - A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;
\r
3045 arrow[6].y = arrow[1].y - 2*A_WIDTH + 0.5;
\r
3048 /* [AS] Needed a lot of paper for this! :-) */
\r
3049 dy = (double) (d_y - s_y) / (double) (d_x - s_x);
\r
3050 dx = (double) (s_x - d_x) / (double) (s_y - d_y);
\r
3052 j = sqrt( Sqr(A_WIDTH) / (1.0 + Sqr(dx)) );
\r
3054 k = sqrt( Sqr(A_WIDTH*A_HEIGHT_FACTOR) / (1.0 + Sqr(dy)) );
\r
3059 arrow[0].x = Round(x - j);
\r
3060 arrow[0].y = Round(y + j*dx);
\r
3062 arrow[1].x = Round(arrow[0].x + 2*j); // [HGM] prevent width to be affected by rounding twice
\r
3063 arrow[1].y = Round(arrow[0].y - 2*j*dx);
\r
3066 x = (double) d_x - k;
\r
3067 y = (double) d_y - k*dy;
\r
3070 x = (double) d_x + k;
\r
3071 y = (double) d_y + k*dy;
\r
3074 x = Round(x); y = Round(y); // [HGM] make sure width of shaft is rounded the same way on both ends
\r
3076 arrow[6].x = Round(x - j);
\r
3077 arrow[6].y = Round(y + j*dx);
\r
3079 arrow[2].x = Round(arrow[6].x + 2*j);
\r
3080 arrow[2].y = Round(arrow[6].y - 2*j*dx);
\r
3082 arrow[3].x = Round(arrow[2].x + j*(A_WIDTH_FACTOR-1));
\r
3083 arrow[3].y = Round(arrow[2].y - j*(A_WIDTH_FACTOR-1)*dx);
\r
3088 arrow[5].x = Round(arrow[6].x - j*(A_WIDTH_FACTOR-1));
\r
3089 arrow[5].y = Round(arrow[6].y + j*(A_WIDTH_FACTOR-1)*dx);
\r
3092 Polygon( hdc, arrow, 7 );
\r
3095 /* [AS] Draw an arrow between two squares */
\r
3096 VOID DrawArrowBetweenSquares( HDC hdc, int s_col, int s_row, int d_col, int d_row )
\r
3098 int s_x, s_y, d_x, d_y;
\r
3105 if( s_col == d_col && s_row == d_row ) {
\r
3109 /* Get source and destination points */
\r
3110 SquareToPos( s_row, s_col, &s_x, &s_y);
\r
3111 SquareToPos( d_row, d_col, &d_x, &d_y);
\r
3114 d_y += squareSize / 2 - squareSize / 4; // [HGM] round towards same centers on all sides!
\r
3116 else if( d_y < s_y ) {
\r
3117 d_y += squareSize / 2 + squareSize / 4;
\r
3120 d_y += squareSize / 2;
\r
3124 d_x += squareSize / 2 - squareSize / 4;
\r
3126 else if( d_x < s_x ) {
\r
3127 d_x += squareSize / 2 + squareSize / 4;
\r
3130 d_x += squareSize / 2;
\r
3133 s_x += squareSize / 2;
\r
3134 s_y += squareSize / 2;
\r
3136 /* Adjust width */
\r
3137 A_WIDTH = squareSize / 14.; //[HGM] make float
\r
3140 stLB.lbStyle = BS_SOLID;
\r
3141 stLB.lbColor = appData.highlightArrowColor;
\r
3144 hpen = CreatePen( PS_SOLID, 2, RGB(0x00,0x00,0x00) );
\r
3145 holdpen = SelectObject( hdc, hpen );
\r
3146 hbrush = CreateBrushIndirect( &stLB );
\r
3147 holdbrush = SelectObject( hdc, hbrush );
\r
3149 DrawArrowBetweenPoints( hdc, s_x, s_y, d_x, d_y );
\r
3151 SelectObject( hdc, holdpen );
\r
3152 SelectObject( hdc, holdbrush );
\r
3153 DeleteObject( hpen );
\r
3154 DeleteObject( hbrush );
\r
3157 BOOL HasHighlightInfo()
\r
3159 BOOL result = FALSE;
\r
3161 if( highlightInfo.sq[0].x >= 0 && highlightInfo.sq[0].y >= 0 &&
\r
3162 highlightInfo.sq[1].x >= 0 && highlightInfo.sq[1].y >= 0 )
\r
3170 BOOL IsDrawArrowEnabled()
\r
3172 BOOL result = FALSE;
\r
3174 if( appData.highlightMoveWithArrow && squareSize >= 32 ) {
\r
3181 VOID DrawArrowHighlight( HDC hdc )
\r
3183 if( IsDrawArrowEnabled() && HasHighlightInfo() ) {
\r
3184 DrawArrowBetweenSquares( hdc,
\r
3185 highlightInfo.sq[0].x, highlightInfo.sq[0].y,
\r
3186 highlightInfo.sq[1].x, highlightInfo.sq[1].y );
\r
3190 HRGN GetArrowHighlightClipRegion( HDC hdc )
\r
3192 HRGN result = NULL;
\r
3194 if( HasHighlightInfo() ) {
\r
3195 int x1, y1, x2, y2;
\r
3196 int sx, sy, dx, dy;
\r
3198 SquareToPos(highlightInfo.sq[0].y, highlightInfo.sq[0].x, &x1, &y1 );
\r
3199 SquareToPos(highlightInfo.sq[1].y, highlightInfo.sq[1].x, &x2, &y2 );
\r
3201 sx = MIN( x1, x2 );
\r
3202 sy = MIN( y1, y2 );
\r
3203 dx = MAX( x1, x2 ) + squareSize;
\r
3204 dy = MAX( y1, y2 ) + squareSize;
\r
3206 result = CreateRectRgn( sx, sy, dx, dy );
\r
3213 Warning: this function modifies the behavior of several other functions.
\r
3215 Basically, Winboard is optimized to avoid drawing the whole board if not strictly
\r
3216 needed. Unfortunately, the decision whether or not to perform a full or partial
\r
3217 repaint is scattered all over the place, which is not good for features such as
\r
3218 "arrow highlighting" that require a full repaint of the board.
\r
3220 So, I've tried to patch the code where I thought it made sense (e.g. after or during
\r
3221 user interaction, when speed is not so important) but especially to avoid errors
\r
3222 in the displayed graphics.
\r
3224 In such patched places, I always try refer to this function so there is a single
\r
3225 place to maintain knowledge.
\r
3227 To restore the original behavior, just return FALSE unconditionally.
\r
3229 BOOL IsFullRepaintPreferrable()
\r
3231 BOOL result = FALSE;
\r
3233 if( (appData.highlightLastMove || appData.highlightDragging) && IsDrawArrowEnabled() ) {
\r
3234 /* Arrow may appear on the board */
\r
3242 This function is called by DrawPosition to know whether a full repaint must
\r
3245 Only DrawPosition may directly call this function, which makes use of
\r
3246 some state information. Other function should call DrawPosition specifying
\r
3247 the repaint flag, and can use IsFullRepaintPreferrable if needed.
\r
3249 BOOL DrawPositionNeedsFullRepaint()
\r
3251 BOOL result = FALSE;
\r
3254 Probably a slightly better policy would be to trigger a full repaint
\r
3255 when animInfo.piece changes state (i.e. empty -> non-empty and viceversa),
\r
3256 but animation is fast enough that it's difficult to notice.
\r
3258 if( animInfo.piece == EmptySquare ) {
\r
3259 if( (appData.highlightLastMove || appData.highlightDragging) && IsDrawArrowEnabled() /*&& HasHighlightInfo()*/ ) {
\r
3268 DrawBoardOnDC(HDC hdc, Board board, HDC tmphdc)
\r
3270 int row, column, x, y, square_color, piece_color;
\r
3271 ChessSquare piece;
\r
3273 HDC texture_hdc = NULL;
\r
3275 /* [AS] Initialize background textures if needed */
\r
3276 if( liteBackTexture != NULL || darkBackTexture != NULL ) {
\r
3277 static int backTextureBoardSize; /* [HGM] boardsize: also new texture if board format changed */
\r
3278 if( backTextureSquareSize != squareSize
\r
3279 || backTextureBoardSize != BOARD_WIDTH+BOARD_FILES*BOARD_HEIGHT) {
\r
3280 backTextureBoardSize = BOARD_WIDTH+BOARD_FILES*BOARD_HEIGHT;
\r
3281 backTextureSquareSize = squareSize;
\r
3282 RebuildTextureSquareInfo();
\r
3285 texture_hdc = CreateCompatibleDC( hdc );
\r
3288 for (row = 0; row < BOARD_HEIGHT; row++) {
\r
3289 for (column = 0; column < BOARD_WIDTH; column++) {
\r
3291 SquareToPos(row, column, &x, &y);
\r
3293 piece = board[row][column];
\r
3295 square_color = ((column + row) % 2) == 1;
\r
3296 if( gameInfo.variant == VariantXiangqi ) {
\r
3297 square_color = !InPalace(row, column);
\r
3298 if(BOARD_HEIGHT&1) { if(row==BOARD_HEIGHT/2) square_color ^= 1; }
\r
3299 else if(row < BOARD_HEIGHT/2) square_color ^= 1;
\r
3301 piece_color = (int) piece < (int) BlackPawn;
\r
3304 /* [HGM] holdings file: light square or black */
\r
3305 if(column == BOARD_LEFT-2) {
\r
3306 if( row > BOARD_HEIGHT - gameInfo.holdingsSize - 1 )
\r
3309 DisplayHoldingsCount(hdc, x, y, 0, 0); /* black out */
\r
3313 if(column == BOARD_RGHT + 1 ) {
\r
3314 if( row < gameInfo.holdingsSize )
\r
3317 DisplayHoldingsCount(hdc, x, y, 0, 0);
\r
3321 if(column == BOARD_LEFT-1 ) /* left align */
\r
3322 DisplayHoldingsCount(hdc, x, y, flipView, (int) board[row][column]);
\r
3323 else if( column == BOARD_RGHT) /* right align */
\r
3324 DisplayHoldingsCount(hdc, x, y, !flipView, (int) board[row][column]);
\r
3326 if (appData.monoMode) {
\r
3327 if (piece == EmptySquare) {
\r
3328 BitBlt(hdc, x, y, squareSize, squareSize, 0, 0, 0,
\r
3329 square_color ? WHITENESS : BLACKNESS);
\r
3331 DrawPieceOnDC(hdc, piece, piece_color, square_color, x, y, tmphdc);
\r
3334 else if( appData.useBitmaps && backTextureSquareInfo[row][column].mode > 0 ) {
\r
3335 /* [AS] Draw the square using a texture bitmap */
\r
3336 HBITMAP hbm = SelectObject( texture_hdc, square_color ? liteBackTexture : darkBackTexture );
\r
3337 int r = row, c = column; // [HGM] do not flip board in flipView
\r
3338 if(flipView) { r = BOARD_HEIGHT-1 - r; c = BOARD_WIDTH-1 - c; }
\r
3341 squareSize, squareSize,
\r
3344 backTextureSquareInfo[r][c].mode,
\r
3345 backTextureSquareInfo[r][c].x,
\r
3346 backTextureSquareInfo[r][c].y );
\r
3348 SelectObject( texture_hdc, hbm );
\r
3350 if (piece != EmptySquare) {
\r
3351 DrawPieceOnDC(hdc, piece, piece_color, -1, x, y, tmphdc);
\r
3355 HBRUSH brush = square_color ? lightSquareBrush : darkSquareBrush;
\r
3357 oldBrush = SelectObject(hdc, brush );
\r
3358 BitBlt(hdc, x, y, squareSize, squareSize, 0, 0, 0, PATCOPY);
\r
3359 SelectObject(hdc, oldBrush);
\r
3360 if (piece != EmptySquare)
\r
3361 DrawPieceOnDC(hdc, piece, piece_color, -1, x, y, tmphdc);
\r
3366 if( texture_hdc != NULL ) {
\r
3367 DeleteDC( texture_hdc );
\r
3371 int saveDiagFlag = 0; FILE *diagFile; // [HGM] diag
\r
3372 void fputDW(FILE *f, int x)
\r
3374 fputc(x & 255, f);
\r
3375 fputc(x>>8 & 255, f);
\r
3376 fputc(x>>16 & 255, f);
\r
3377 fputc(x>>24 & 255, f);
\r
3380 #define MAX_CLIPS 200 /* more than enough */
\r
3383 DrawLogoOnDC(HDC hdc, RECT logoRect, HBITMAP logo)
\r
3385 // HBITMAP bufferBitmap;
\r
3390 int w = 100, h = 50;
\r
3392 if(logo == NULL) {
\r
3393 if(!logoHeight) return;
\r
3394 FillRect( hdc, &logoRect, whitePieceBrush );
\r
3396 // GetClientRect(hwndMain, &Rect);
\r
3397 // bufferBitmap = CreateCompatibleBitmap(hdc, Rect.right-Rect.left+1,
\r
3398 // Rect.bottom-Rect.top+1);
\r
3399 tmphdc = CreateCompatibleDC(hdc);
\r
3400 hbm = SelectObject(tmphdc, logo);
\r
3401 if( GetObject( logo, sizeof(bi), &bi ) > 0 ) {
\r
3405 StretchBlt(hdc, logoRect.left, logoRect.top, logoRect.right - logoRect.left,
\r
3406 logoRect.bottom - logoRect.top, tmphdc, 0, 0, w, h, SRCCOPY);
\r
3407 SelectObject(tmphdc, hbm);
\r
3415 HDC hdc = GetDC(hwndMain);
\r
3416 HBITMAP whiteLogo = (HBITMAP) first.programLogo, blackLogo = (HBITMAP) second.programLogo;
\r
3417 if(appData.autoLogo) {
\r
3419 switch(gameMode) { // pick logos based on game mode
\r
3420 case IcsObserving:
\r
3421 whiteLogo = second.programLogo; // ICS logo
\r
3422 blackLogo = second.programLogo;
\r
3425 case IcsPlayingWhite:
\r
3426 if(!appData.zippyPlay) whiteLogo = userLogo;
\r
3427 blackLogo = second.programLogo; // ICS logo
\r
3429 case IcsPlayingBlack:
\r
3430 whiteLogo = second.programLogo; // ICS logo
\r
3431 blackLogo = appData.zippyPlay ? first.programLogo : userLogo;
\r
3433 case TwoMachinesPlay:
\r
3434 if(first.twoMachinesColor[0] == 'b') {
\r
3435 whiteLogo = second.programLogo;
\r
3436 blackLogo = first.programLogo;
\r
3439 case MachinePlaysWhite:
\r
3440 blackLogo = userLogo;
\r
3442 case MachinePlaysBlack:
\r
3443 whiteLogo = userLogo;
\r
3444 blackLogo = first.programLogo;
\r
3447 DrawLogoOnDC(hdc, leftLogoRect, flipClock ? blackLogo : whiteLogo);
\r
3448 DrawLogoOnDC(hdc, rightLogoRect, flipClock ? whiteLogo : blackLogo);
\r
3449 ReleaseDC(hwndMain, hdc);
\r
3454 UpdateLogos(int display)
\r
3455 { // called after loading new engine(s), in tourney or from menu
\r
3456 LoadLogo(&first, 0, FALSE);
\r
3457 LoadLogo(&second, 1, appData.icsActive);
\r
3458 InitDrawingSizes(-2, 0); // adapt layout of board window to presence/absence of logos
\r
3459 if(display) DisplayLogos();
\r
3462 static HDC hdcSeek;
\r
3464 // [HGM] seekgraph
\r
3465 void DrawSeekAxis( int x, int y, int xTo, int yTo )
\r
3468 HPEN hp = SelectObject( hdcSeek, gridPen );
\r
3469 MoveToEx( hdcSeek, boardRect.left+x, boardRect.top+y, &stPt );
\r
3470 LineTo( hdcSeek, boardRect.left+xTo, boardRect.top+yTo );
\r
3471 SelectObject( hdcSeek, hp );
\r
3474 // front-end wrapper for drawing functions to do rectangles
\r
3475 void DrawSeekBackground( int left, int top, int right, int bottom )
\r
3480 if (hdcSeek == NULL) {
\r
3481 hdcSeek = GetDC(hwndMain);
\r
3482 if (!appData.monoMode) {
\r
3483 SelectPalette(hdcSeek, hPal, FALSE);
\r
3484 RealizePalette(hdcSeek);
\r
3487 hp = SelectObject( hdcSeek, gridPen );
\r
3488 rc.top = boardRect.top+top; rc.left = boardRect.left+left;
\r
3489 rc.bottom = boardRect.top+bottom; rc.right = boardRect.left+right;
\r
3490 FillRect( hdcSeek, &rc, lightSquareBrush );
\r
3491 SelectObject( hdcSeek, hp );
\r
3494 // front-end wrapper for putting text in graph
\r
3495 void DrawSeekText(char *buf, int x, int y)
\r
3498 SetBkMode( hdcSeek, TRANSPARENT );
\r
3499 GetTextExtentPoint32( hdcSeek, buf, strlen(buf), &stSize );
\r
3500 TextOut( hdcSeek, boardRect.left+x-3, boardRect.top+y-stSize.cy/2, buf, strlen(buf) );
\r
3503 void DrawSeekDot(int x, int y, int color)
\r
3505 int square = color & 0x80;
\r
3506 HBRUSH oldBrush = SelectObject(hdcSeek,
\r
3507 color == 0 ? markerBrush : color == 1 ? darkSquareBrush : explodeBrush);
\r
3510 Rectangle(hdcSeek, boardRect.left+x - squareSize/9, boardRect.top+y - squareSize/9,
\r
3511 boardRect.left+x + squareSize/9, boardRect.top+y + squareSize/9);
\r
3513 Ellipse(hdcSeek, boardRect.left+x - squareSize/8, boardRect.top+y - squareSize/8,
\r
3514 boardRect.left+x + squareSize/8, boardRect.top+y + squareSize/8);
\r
3515 SelectObject(hdcSeek, oldBrush);
\r
3519 HDCDrawPosition(HDC hdc, BOOLEAN repaint, Board board)
\r
3521 static Board lastReq[2], lastDrawn[2];
\r
3522 static HighlightInfo lastDrawnHighlight, lastDrawnPremove;
\r
3523 static int lastDrawnFlipView = 0;
\r
3524 static int lastReqValid[2] = {0, 0}, lastDrawnValid[2] = {0, 0};
\r
3525 int releaseDC, x, y, x2, y2, row, column, num_clips = 0, i;
\r
3528 HBITMAP bufferBitmap;
\r
3529 HBITMAP oldBitmap;
\r
3531 HRGN clips[MAX_CLIPS];
\r
3532 ChessSquare dragged_piece = EmptySquare;
\r
3533 int nr = twoBoards*partnerUp;
\r
3535 /* I'm undecided on this - this function figures out whether a full
\r
3536 * repaint is necessary on its own, so there's no real reason to have the
\r
3537 * caller tell it that. I think this can safely be set to FALSE - but
\r
3538 * if we trust the callers not to request full repaints unnessesarily, then
\r
3539 * we could skip some clipping work. In other words, only request a full
\r
3540 * redraw when the majority of pieces have changed positions (ie. flip,
\r
3541 * gamestart and similar) --Hawk
\r
3543 Boolean fullrepaint = repaint;
\r
3545 if(DrawSeekGraph()) return; // [HG} seekgraph: suppress printing board if seek graph up
\r
3547 if( DrawPositionNeedsFullRepaint() ) {
\r
3548 fullrepaint = TRUE;
\r
3551 if (board == NULL) {
\r
3552 if (!lastReqValid[nr]) {
\r
3555 board = lastReq[nr];
\r
3557 CopyBoard(lastReq[nr], board);
\r
3558 lastReqValid[nr] = 1;
\r
3561 if (doingSizing) {
\r
3565 if (IsIconic(hwndMain)) {
\r
3569 if (hdc == NULL) {
\r
3570 hdc = GetDC(hwndMain);
\r
3571 if (!appData.monoMode) {
\r
3572 SelectPalette(hdc, hPal, FALSE);
\r
3573 RealizePalette(hdc);
\r
3577 releaseDC = FALSE;
\r
3580 /* Create some work-DCs */
\r
3581 hdcmem = CreateCompatibleDC(hdc);
\r
3582 tmphdc = CreateCompatibleDC(hdc);
\r
3584 /* If dragging is in progress, we temporarely remove the piece */
\r
3585 /* [HGM] or temporarily decrease count if stacked */
\r
3586 /* !! Moved to before board compare !! */
\r
3587 if (dragInfo.from.x >= 0 && dragInfo.pos.x >= 0) {
\r
3588 dragged_piece = board[dragInfo.from.y][dragInfo.from.x];
\r
3589 if(dragInfo.from.x == BOARD_LEFT-2 ) {
\r
3590 if(--board[dragInfo.from.y][dragInfo.from.x+1] == 0 )
\r
3591 board[dragInfo.from.y][dragInfo.from.x] = EmptySquare;
\r
3593 if(dragInfo.from.x == BOARD_RGHT+1) {
\r
3594 if(--board[dragInfo.from.y][dragInfo.from.x-1] == 0 )
\r
3595 board[dragInfo.from.y][dragInfo.from.x] = EmptySquare;
\r
3597 board[dragInfo.from.y][dragInfo.from.x] = gatingPiece;
\r
3600 /* Figure out which squares need updating by comparing the
\r
3601 * newest board with the last drawn board and checking if
\r
3602 * flipping has changed.
\r
3604 if (!fullrepaint && lastDrawnValid[nr] && (nr == 1 || lastDrawnFlipView == flipView)) {
\r
3605 for (row = 0; row < BOARD_HEIGHT; row++) { /* [HGM] true size, not 8 */
\r
3606 for (column = 0; column < BOARD_WIDTH; column++) {
\r
3607 if (lastDrawn[nr][row][column] != board[row][column]) {
\r
3608 SquareToPos(row, column, &x, &y);
\r
3609 clips[num_clips++] =
\r
3610 CreateRectRgn(x, y, x + squareSize, y + squareSize);
\r
3614 if(nr == 0) { // [HGM] dual: no highlights on second board
\r
3615 for (i=0; i<2; i++) {
\r
3616 if (lastDrawnHighlight.sq[i].x != highlightInfo.sq[i].x ||
\r
3617 lastDrawnHighlight.sq[i].y != highlightInfo.sq[i].y) {
\r
3618 if (lastDrawnHighlight.sq[i].x >= 0 &&
\r
3619 lastDrawnHighlight.sq[i].y >= 0) {
\r
3620 SquareToPos(lastDrawnHighlight.sq[i].y,
\r
3621 lastDrawnHighlight.sq[i].x, &x, &y);
\r
3622 clips[num_clips++] =
\r
3623 CreateRectRgn(x - lineGap, y - lineGap,
\r
3624 x + squareSize + lineGap, y + squareSize + lineGap);
\r
3626 if (highlightInfo.sq[i].x >= 0 && highlightInfo.sq[i].y >= 0) {
\r
3627 SquareToPos(highlightInfo.sq[i].y, highlightInfo.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
3634 for (i=0; i<2; i++) {
\r
3635 if (lastDrawnPremove.sq[i].x != premoveHighlightInfo.sq[i].x ||
\r
3636 lastDrawnPremove.sq[i].y != premoveHighlightInfo.sq[i].y) {
\r
3637 if (lastDrawnPremove.sq[i].x >= 0 &&
\r
3638 lastDrawnPremove.sq[i].y >= 0) {
\r
3639 SquareToPos(lastDrawnPremove.sq[i].y,
\r
3640 lastDrawnPremove.sq[i].x, &x, &y);
\r
3641 clips[num_clips++] =
\r
3642 CreateRectRgn(x - lineGap, y - lineGap,
\r
3643 x + squareSize + lineGap, y + squareSize + lineGap);
\r
3645 if (premoveHighlightInfo.sq[i].x >= 0 &&
\r
3646 premoveHighlightInfo.sq[i].y >= 0) {
\r
3647 SquareToPos(premoveHighlightInfo.sq[i].y,
\r
3648 premoveHighlightInfo.sq[i].x, &x, &y);
\r
3649 clips[num_clips++] =
\r
3650 CreateRectRgn(x - lineGap, y - lineGap,
\r
3651 x + squareSize + lineGap, y + squareSize + lineGap);
\r
3655 } else { // nr == 1
\r
3656 partnerHighlightInfo.sq[0].y = board[EP_STATUS-4];
\r
3657 partnerHighlightInfo.sq[0].x = board[EP_STATUS-3];
\r
3658 partnerHighlightInfo.sq[1].y = board[EP_STATUS-2];
\r
3659 partnerHighlightInfo.sq[1].x = board[EP_STATUS-1];
\r
3660 for (i=0; i<2; i++) {
\r
3661 if (partnerHighlightInfo.sq[i].x >= 0 &&
\r
3662 partnerHighlightInfo.sq[i].y >= 0) {
\r
3663 SquareToPos(partnerHighlightInfo.sq[i].y,
\r
3664 partnerHighlightInfo.sq[i].x, &x, &y);
\r
3665 clips[num_clips++] =
\r
3666 CreateRectRgn(x - lineGap, y - lineGap,
\r
3667 x + squareSize + lineGap, y + squareSize + lineGap);
\r
3669 if (oldPartnerHighlight.sq[i].x >= 0 &&
\r
3670 oldPartnerHighlight.sq[i].y >= 0) {
\r
3671 SquareToPos(oldPartnerHighlight.sq[i].y,
\r
3672 oldPartnerHighlight.sq[i].x, &x, &y);
\r
3673 clips[num_clips++] =
\r
3674 CreateRectRgn(x - lineGap, y - lineGap,
\r
3675 x + squareSize + lineGap, y + squareSize + lineGap);
\r
3680 fullrepaint = TRUE;
\r
3683 /* Create a buffer bitmap - this is the actual bitmap
\r
3684 * being written to. When all the work is done, we can
\r
3685 * copy it to the real DC (the screen). This avoids
\r
3686 * the problems with flickering.
\r
3688 GetClientRect(hwndMain, &Rect);
\r
3689 bufferBitmap = CreateCompatibleBitmap(hdc, Rect.right-Rect.left+1,
\r
3690 Rect.bottom-Rect.top+1);
\r
3691 oldBitmap = SelectObject(hdcmem, bufferBitmap);
\r
3692 if (!appData.monoMode) {
\r
3693 SelectPalette(hdcmem, hPal, FALSE);
\r
3696 /* Create clips for dragging */
\r
3697 if (!fullrepaint) {
\r
3698 if (dragInfo.from.x >= 0) {
\r
3699 SquareToPos(dragInfo.from.y, dragInfo.from.x, &x, &y);
\r
3700 clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);
\r
3702 if (dragInfo.start.x >= 0) {
\r
3703 SquareToPos(dragInfo.start.y, dragInfo.start.x, &x, &y);
\r
3704 clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);
\r
3706 if (dragInfo.pos.x >= 0) {
\r
3707 x = dragInfo.pos.x - squareSize / 2;
\r
3708 y = dragInfo.pos.y - squareSize / 2;
\r
3709 clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);
\r
3711 if (dragInfo.lastpos.x >= 0) {
\r
3712 x = dragInfo.lastpos.x - squareSize / 2;
\r
3713 y = dragInfo.lastpos.y - squareSize / 2;
\r
3714 clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);
\r
3718 /* Are we animating a move?
\r
3720 * - remove the piece from the board (temporarely)
\r
3721 * - calculate the clipping region
\r
3723 if (!fullrepaint) {
\r
3724 if (animInfo.piece != EmptySquare) {
\r
3725 board[animInfo.from.y][animInfo.from.x] = EmptySquare;
\r
3726 x = boardRect.left + animInfo.lastpos.x;
\r
3727 y = boardRect.top + animInfo.lastpos.y;
\r
3728 x2 = boardRect.left + animInfo.pos.x;
\r
3729 y2 = boardRect.top + animInfo.pos.y;
\r
3730 clips[num_clips++] = CreateRectRgn(MIN(x,x2), MIN(y,y2), MAX(x,x2)+squareSize, MAX(y,y2)+squareSize);
\r
3731 /* Slight kludge. The real problem is that after AnimateMove is
\r
3732 done, the position on the screen does not match lastDrawn.
\r
3733 This currently causes trouble only on e.p. captures in
\r
3734 atomic, where the piece moves to an empty square and then
\r
3735 explodes. The old and new positions both had an empty square
\r
3736 at the destination, but animation has drawn a piece there and
\r
3737 we have to remember to erase it. [HGM] moved until after setting lastDrawn */
\r
3738 lastDrawn[0][animInfo.to.y][animInfo.to.x] = animInfo.piece;
\r
3742 /* No clips? Make sure we have fullrepaint set to TRUE */
\r
3743 if (num_clips == 0)
\r
3744 fullrepaint = TRUE;
\r
3746 /* Set clipping on the memory DC */
\r
3747 if (!fullrepaint) {
\r
3748 SelectClipRgn(hdcmem, clips[0]);
\r
3749 for (x = 1; x < num_clips; x++) {
\r
3750 if (ExtSelectClipRgn(hdcmem, clips[x], RGN_OR) == ERROR)
\r
3751 abort(); // this should never ever happen!
\r
3755 /* Do all the drawing to the memory DC */
\r
3756 if(explodeInfo.radius) { // [HGM] atomic
\r
3758 int x, y, r=(explodeInfo.radius * squareSize)/100;
\r
3759 ChessSquare piece = board[explodeInfo.fromY][explodeInfo.fromX];
\r
3760 board[explodeInfo.fromY][explodeInfo.fromX] = EmptySquare; // suppress display of capturer
\r
3761 SquareToPos(explodeInfo.toY, explodeInfo.toX, &x, &y);
\r
3762 x += squareSize/2;
\r
3763 y += squareSize/2;
\r
3764 if(!fullrepaint) {
\r
3765 clips[num_clips] = CreateRectRgn(x-r, y-r, x+r, y+r);
\r
3766 ExtSelectClipRgn(hdcmem, clips[num_clips++], RGN_OR);
\r
3768 DrawGridOnDC(hdcmem);
\r
3769 DrawHighlightsOnDC(hdcmem, &highlightInfo, HIGHLIGHT_PEN);
\r
3770 DrawHighlightsOnDC(hdcmem, &premoveHighlightInfo, PREMOVE_PEN);
\r
3771 DrawBoardOnDC(hdcmem, board, tmphdc);
\r
3772 board[explodeInfo.fromY][explodeInfo.fromX] = piece;
\r
3773 oldBrush = SelectObject(hdcmem, explodeBrush);
\r
3774 Ellipse(hdcmem, x-r, y-r, x+r, y+r);
\r
3775 SelectObject(hdcmem, oldBrush);
\r
3777 DrawGridOnDC(hdcmem);
\r
3778 if(nr == 0) { // [HGM] dual: decide which highlights to draw
\r
3779 DrawHighlightsOnDC(hdcmem, &highlightInfo, HIGHLIGHT_PEN);
\r
3780 DrawHighlightsOnDC(hdcmem, &premoveHighlightInfo, PREMOVE_PEN);
\r
3782 DrawHighlightsOnDC(hdcmem, &partnerHighlightInfo, HIGHLIGHT_PEN);
\r
3783 oldPartnerHighlight = partnerHighlightInfo;
\r
3785 DrawBoardOnDC(hdcmem, board, tmphdc);
\r
3787 if(nr == 0) // [HGM] dual: markers only on left board
\r
3788 for (row = 0; row < BOARD_HEIGHT; row++) {
\r
3789 for (column = 0; column < BOARD_WIDTH; column++) {
\r
3790 if (marker[row][column]) { // marker changes only occur with full repaint!
\r
3791 HBRUSH oldBrush = SelectObject(hdcmem,
\r
3792 marker[row][column] == 2 ? markerBrush : explodeBrush);
\r
3793 SquareToPos(row, column, &x, &y);
\r
3794 Ellipse(hdcmem, x + squareSize/4, y + squareSize/4,
\r
3795 x + 3*squareSize/4, y + 3*squareSize/4);
\r
3796 SelectObject(hdcmem, oldBrush);
\r
3801 if( appData.highlightMoveWithArrow ) {
\r
3802 DrawArrowHighlight(hdcmem);
\r
3805 DrawCoordsOnDC(hdcmem);
\r
3807 CopyBoard(lastDrawn[nr], board); /* [HGM] Moved to here from end of routine, */
\r
3808 /* to make sure lastDrawn contains what is actually drawn */
\r
3810 /* Put the dragged piece back into place and draw it (out of place!) */
\r
3811 if (dragged_piece != EmptySquare) {
\r
3812 /* [HGM] or restack */
\r
3813 if(dragInfo.from.x == BOARD_LEFT-2 )
\r
3814 board[dragInfo.from.y][dragInfo.from.x+1]++;
\r
3816 if(dragInfo.from.x == BOARD_RGHT+1 )
\r
3817 board[dragInfo.from.y][dragInfo.from.x-1]++;
\r
3818 board[dragInfo.from.y][dragInfo.from.x] = dragged_piece;
\r
3819 x = dragInfo.pos.x - squareSize / 2;
\r
3820 y = dragInfo.pos.y - squareSize / 2;
\r
3821 DrawPieceOnDC(hdcmem, dragInfo.piece,
\r
3822 ((int) dragInfo.piece < (int) BlackPawn),
\r
3823 (dragInfo.from.y + dragInfo.from.x) % 2, x, y, tmphdc);
\r
3826 /* Put the animated piece back into place and draw it */
\r
3827 if (animInfo.piece != EmptySquare) {
\r
3828 board[animInfo.from.y][animInfo.from.x] = animInfo.piece;
\r
3829 x = boardRect.left + animInfo.pos.x;
\r
3830 y = boardRect.top + animInfo.pos.y;
\r
3831 DrawPieceOnDC(hdcmem, animInfo.piece,
\r
3832 ((int) animInfo.piece < (int) BlackPawn),
\r
3833 (animInfo.from.y + animInfo.from.x) % 2, x, y, tmphdc);
\r
3836 /* Release the bufferBitmap by selecting in the old bitmap
\r
3837 * and delete the memory DC
\r
3839 SelectObject(hdcmem, oldBitmap);
\r
3842 /* Set clipping on the target DC */
\r
3843 if (!fullrepaint) {
\r
3844 if(nr == 1) for (x = 0; x < num_clips; x++) { // [HGM] dual: translate clips
\r
3846 GetRgnBox(clips[x], &rect);
\r
3847 DeleteObject(clips[x]);
\r
3848 clips[x] = CreateRectRgn(rect.left + wpMain.width/2, rect.top,
\r
3849 rect.right + wpMain.width/2, rect.bottom);
\r
3851 SelectClipRgn(hdc, clips[0]);
\r
3852 for (x = 1; x < num_clips; x++) {
\r
3853 if (ExtSelectClipRgn(hdc, clips[x], RGN_OR) == ERROR)
\r
3854 abort(); // this should never ever happen!
\r
3858 /* Copy the new bitmap onto the screen in one go.
\r
3859 * This way we avoid any flickering
\r
3861 oldBitmap = SelectObject(tmphdc, bufferBitmap);
\r
3862 BitBlt(hdc, boardRect.left + twoBoards*partnerUp*wpMain.width/2, boardRect.top, // [HGM] dual
\r
3863 boardRect.right - boardRect.left,
\r
3864 boardRect.bottom - boardRect.top,
\r
3865 tmphdc, boardRect.left, boardRect.top, SRCCOPY);
\r
3866 if(saveDiagFlag) {
\r
3867 BITMAP b; int i, j=0, m, w, wb, fac=0; char *pData;
\r
3868 BITMAPINFOHEADER bih; int color[16], nrColors=0;
\r
3870 GetObject(bufferBitmap, sizeof(b), &b);
\r
3871 if(pData = malloc(b.bmWidthBytes*b.bmHeight + 10000)) {
\r
3872 bih.biSize = sizeof(BITMAPINFOHEADER);
\r
3873 bih.biWidth = b.bmWidth;
\r
3874 bih.biHeight = b.bmHeight;
\r
3876 bih.biBitCount = b.bmBitsPixel;
\r
3877 bih.biCompression = 0;
\r
3878 bih.biSizeImage = b.bmWidthBytes*b.bmHeight;
\r
3879 bih.biXPelsPerMeter = 0;
\r
3880 bih.biYPelsPerMeter = 0;
\r
3881 bih.biClrUsed = 0;
\r
3882 bih.biClrImportant = 0;
\r
3883 // fprintf(diagFile, "t=%d\nw=%d\nh=%d\nB=%d\nP=%d\nX=%d\n",
\r
3884 // b.bmType, b.bmWidth, b.bmHeight, b.bmWidthBytes, b.bmPlanes, b.bmBitsPixel);
\r
3885 GetDIBits(tmphdc,bufferBitmap,0,b.bmHeight,pData,(BITMAPINFO*)&bih,DIB_RGB_COLORS);
\r
3886 // fprintf(diagFile, "%8x\n", (int) pData);
\r
3888 wb = b.bmWidthBytes;
\r
3890 for(i=0; i<wb*(b.bmHeight - boardRect.top + OUTER_MARGIN)>>2; i++) {
\r
3891 int k = ((int*) pData)[i];
\r
3892 for(j=0; j<nrColors; j++) if(color[j] == k) break;
\r
3893 if(j >= 16) break;
\r
3895 if(j >= nrColors) nrColors = j+1;
\r
3897 if(j<16) { // 16 colors is enough. Compress to 4 bits per pixel
\r
3899 for(i=0; i<b.bmHeight - boardRect.top + OUTER_MARGIN; i++) {
\r
3900 for(w=0; w<(wb>>2); w+=2) {
\r
3901 int k = ((int*) pData)[(wb*i>>2) + w];
\r
3902 for(j=0; j<nrColors; j++) if(color[j] == k) break;
\r
3903 k = ((int*) pData)[(wb*i>>2) + w + 1];
\r
3904 for(m=0; m<nrColors; m++) if(color[m] == k) break;
\r
3905 pData[p++] = m | j<<4;
\r
3907 while(p&3) pData[p++] = 0;
\r
3910 wb = ((wb+31)>>5)<<2;
\r
3912 // write BITMAPFILEHEADER
\r
3913 fprintf(diagFile, "BM");
\r
3914 fputDW(diagFile, wb*(b.bmHeight - boardRect.top + OUTER_MARGIN)+0x36 + (fac?64:0));
\r
3915 fputDW(diagFile, 0);
\r
3916 fputDW(diagFile, 0x36 + (fac?64:0));
\r
3917 // write BITMAPINFOHEADER
\r
3918 fputDW(diagFile, 40);
\r
3919 fputDW(diagFile, b.bmWidth);
\r
3920 fputDW(diagFile, b.bmHeight - boardRect.top + OUTER_MARGIN);
\r
3921 if(fac) fputDW(diagFile, 0x040001); // planes and bits/pixel
\r
3922 else fputDW(diagFile, 0x200001); // planes and bits/pixel
\r
3923 fputDW(diagFile, 0);
\r
3924 fputDW(diagFile, 0);
\r
3925 fputDW(diagFile, 0);
\r
3926 fputDW(diagFile, 0);
\r
3927 fputDW(diagFile, 0);
\r
3928 fputDW(diagFile, 0);
\r
3929 // write color table
\r
3931 for(i=0; i<16; i++) fputDW(diagFile, color[i]);
\r
3932 // write bitmap data
\r
3933 for(i=0; i<wb*(b.bmHeight - boardRect.top + OUTER_MARGIN); i++)
\r
3934 fputc(pData[i], diagFile);
\r
3939 SelectObject(tmphdc, oldBitmap);
\r
3941 /* Massive cleanup */
\r
3942 for (x = 0; x < num_clips; x++)
\r
3943 DeleteObject(clips[x]);
\r
3946 DeleteObject(bufferBitmap);
\r
3949 ReleaseDC(hwndMain, hdc);
\r
3951 if (lastDrawnFlipView != flipView && nr == 0) {
\r
3953 CheckMenuItem(GetMenu(hwndMain),IDM_FlipView, MF_BYCOMMAND|MF_CHECKED);
\r
3955 CheckMenuItem(GetMenu(hwndMain),IDM_FlipView, MF_BYCOMMAND|MF_UNCHECKED);
\r
3958 /* CopyBoard(lastDrawn, board);*/
\r
3959 lastDrawnHighlight = highlightInfo;
\r
3960 lastDrawnPremove = premoveHighlightInfo;
\r
3961 lastDrawnFlipView = flipView;
\r
3962 lastDrawnValid[nr] = 1;
\r
3965 /* [HGM] diag: Save the current board display to the given open file and close the file */
\r
3970 saveDiagFlag = 1; diagFile = f;
\r
3971 HDCDrawPosition(NULL, TRUE, NULL);
\r
3979 /*---------------------------------------------------------------------------*\
\r
3980 | CLIENT PAINT PROCEDURE
\r
3981 | This is the main event-handler for the WM_PAINT message.
\r
3983 \*---------------------------------------------------------------------------*/
\r
3985 PaintProc(HWND hwnd)
\r
3991 if((hdc = BeginPaint(hwnd, &ps))) {
\r
3992 if (IsIconic(hwnd)) {
\r
3993 DrawIcon(hdc, 2, 2, iconCurrent);
\r
3995 if (!appData.monoMode) {
\r
3996 SelectPalette(hdc, hPal, FALSE);
\r
3997 RealizePalette(hdc);
\r
3999 HDCDrawPosition(hdc, 1, NULL);
\r
4000 if(twoBoards) { // [HGM] dual: also redraw other board in other orientation
\r
4001 flipView = !flipView; partnerUp = !partnerUp;
\r
4002 HDCDrawPosition(hdc, 1, NULL);
\r
4003 flipView = !flipView; partnerUp = !partnerUp;
\r
4006 SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);
\r
4007 ExtTextOut(hdc, messageRect.left, messageRect.top,
\r
4008 ETO_CLIPPED|ETO_OPAQUE,
\r
4009 &messageRect, messageText, strlen(messageText), NULL);
\r
4010 SelectObject(hdc, oldFont);
\r
4011 DisplayBothClocks();
\r
4014 EndPaint(hwnd,&ps);
\r
4022 * If the user selects on a border boundary, return -1; if off the board,
\r
4023 * return -2. Otherwise map the event coordinate to the square.
\r
4024 * The offset boardRect.left or boardRect.top must already have been
\r
4025 * subtracted from x.
\r
4027 int EventToSquare(x, limit)
\r
4035 if ((x % (squareSize + lineGap)) >= squareSize)
\r
4037 x /= (squareSize + lineGap);
\r
4049 DropEnable dropEnables[] = {
\r
4050 { 'P', DP_Pawn, N_("Pawn") },
\r
4051 { 'N', DP_Knight, N_("Knight") },
\r
4052 { 'B', DP_Bishop, N_("Bishop") },
\r
4053 { 'R', DP_Rook, N_("Rook") },
\r
4054 { 'Q', DP_Queen, N_("Queen") },
\r
4058 SetupDropMenu(HMENU hmenu)
\r
4060 int i, count, enable;
\r
4062 extern char white_holding[], black_holding[];
\r
4063 char item[MSG_SIZ];
\r
4065 for (i=0; i<sizeof(dropEnables)/sizeof(DropEnable); i++) {
\r
4066 p = strchr(gameMode == IcsPlayingWhite ? white_holding : black_holding,
\r
4067 dropEnables[i].piece);
\r
4069 while (p && *p++ == dropEnables[i].piece) count++;
\r
4070 snprintf(item, MSG_SIZ, "%s %d", T_(dropEnables[i].name), count);
\r
4071 enable = count > 0 || !appData.testLegality
\r
4072 /*!!temp:*/ || (gameInfo.variant == VariantCrazyhouse
\r
4073 && !appData.icsActive);
\r
4074 ModifyMenu(hmenu, dropEnables[i].command,
\r
4075 MF_BYCOMMAND | (enable ? MF_ENABLED : MF_GRAYED) | MF_STRING,
\r
4076 dropEnables[i].command, item);
\r
4080 void DragPieceBegin(int x, int y, Boolean instantly)
\r
4082 dragInfo.lastpos.x = boardRect.left + x;
\r
4083 dragInfo.lastpos.y = boardRect.top + y;
\r
4084 if(instantly) dragInfo.pos = dragInfo.lastpos;
\r
4085 dragInfo.from.x = fromX;
\r
4086 dragInfo.from.y = fromY;
\r
4087 dragInfo.piece = boards[currentMove][fromY][fromX];
\r
4088 dragInfo.start = dragInfo.from;
\r
4089 SetCapture(hwndMain);
\r
4092 void DragPieceEnd(int x, int y)
\r
4095 dragInfo.start.x = dragInfo.start.y = -1;
\r
4096 dragInfo.from = dragInfo.start;
\r
4097 dragInfo.pos = dragInfo.lastpos = dragInfo.start;
\r
4100 void ChangeDragPiece(ChessSquare piece)
\r
4102 dragInfo.piece = piece;
\r
4105 /* Event handler for mouse messages */
\r
4107 MouseEvent(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
\r
4111 static int recursive = 0;
\r
4113 BOOLEAN forceFullRepaint = IsFullRepaintPreferrable(); /* [AS] */
\r
4116 if (message == WM_MBUTTONUP) {
\r
4117 /* Hideous kludge to fool TrackPopupMenu into paying attention
\r
4118 to the middle button: we simulate pressing the left button too!
\r
4120 PostMessage(hwnd, WM_LBUTTONDOWN, wParam, lParam);
\r
4121 PostMessage(hwnd, WM_LBUTTONUP, wParam, lParam);
\r
4127 pt.x = LOWORD(lParam);
\r
4128 pt.y = HIWORD(lParam);
\r
4129 x = EventToSquare(pt.x - boardRect.left, BOARD_WIDTH);
\r
4130 y = EventToSquare(pt.y - boardRect.top, BOARD_HEIGHT);
\r
4131 if (!flipView && y >= 0) {
\r
4132 y = BOARD_HEIGHT - 1 - y;
\r
4134 if (flipView && x >= 0) {
\r
4135 x = BOARD_WIDTH - 1 - x;
\r
4138 shiftKey = GetKeyState(VK_SHIFT) < 0; // [HGM] remember last shift status
\r
4140 switch (message) {
\r
4141 case WM_LBUTTONDOWN:
\r
4142 if (PtInRect((LPRECT) &whiteRect, pt)) {
\r
4143 ClockClick(flipClock);
\r
4144 } else if (PtInRect((LPRECT) &blackRect, pt)) {
\r
4145 ClockClick(!flipClock);
\r
4147 dragInfo.start.x = dragInfo.start.y = -1;
\r
4148 dragInfo.from = dragInfo.start;
\r
4149 if(fromX == -1 && frozen) { // not sure where this is for
\r
4150 fromX = fromY = -1;
\r
4151 DrawPosition(forceFullRepaint || FALSE, NULL); /* [AS] */
\r
4154 LeftClick(Press, pt.x - boardRect.left, pt.y - boardRect.top);
\r
4155 DrawPosition(TRUE, NULL);
\r
4158 case WM_LBUTTONUP:
\r
4159 LeftClick(Release, pt.x - boardRect.left, pt.y - boardRect.top);
\r
4160 DrawPosition(TRUE, NULL);
\r
4163 case WM_MOUSEMOVE:
\r
4164 if(SeekGraphClick(Press, pt.x - boardRect.left, pt.y - boardRect.top, 1)) break;
\r
4165 if(PromoScroll(pt.x - boardRect.left, pt.y - boardRect.top)) break;
\r
4166 MovePV(pt.x - boardRect.left, pt.y - boardRect.top, boardRect.bottom - boardRect.top);
\r
4167 if ((appData.animateDragging || appData.highlightDragging)
\r
4168 && (wParam & MK_LBUTTON)
\r
4169 && dragInfo.from.x >= 0)
\r
4171 BOOL full_repaint = FALSE;
\r
4173 if (appData.animateDragging) {
\r
4174 dragInfo.pos = pt;
\r
4176 if (appData.highlightDragging) {
\r
4177 SetHighlights(fromX, fromY, x, y);
\r
4178 if( IsDrawArrowEnabled() && (x < 0 || x >= BOARD_WIDTH || y < 0 || y >= BOARD_HEIGHT) ) {
\r
4179 full_repaint = TRUE;
\r
4183 DrawPosition( full_repaint, NULL);
\r
4185 dragInfo.lastpos = dragInfo.pos;
\r
4189 case WM_MOUSEWHEEL: // [DM]
\r
4190 { static int lastDir = 0; // [HGM] build in some hysteresis to avoid spurious events
\r
4191 /* Mouse Wheel is being rolled forward
\r
4192 * Play moves forward
\r
4194 if((short)HIWORD(wParam) < 0 && currentMove < forwardMostMove)
\r
4195 { if(lastDir == 1) ForwardEvent(); else lastDir = 1; } // [HGM] suppress first event in direction
\r
4196 /* Mouse Wheel is being rolled backward
\r
4197 * Play moves backward
\r
4199 if((short)HIWORD(wParam) > 0 && currentMove > backwardMostMove)
\r
4200 { if(lastDir == -1) BackwardEvent(); else lastDir = -1; }
\r
4204 case WM_MBUTTONUP:
\r
4205 case WM_RBUTTONUP:
\r
4207 RightClick(Release, pt.x - boardRect.left, pt.y - boardRect.top, &fromX, &fromY);
\r
4210 case WM_MBUTTONDOWN:
\r
4211 case WM_RBUTTONDOWN:
\r
4214 fromX = fromY = -1;
\r
4215 dragInfo.pos.x = dragInfo.pos.y = -1;
\r
4216 dragInfo.start.x = dragInfo.start.y = -1;
\r
4217 dragInfo.from = dragInfo.start;
\r
4218 dragInfo.lastpos = dragInfo.pos;
\r
4219 if (appData.highlightDragging) {
\r
4220 ClearHighlights();
\r
4223 /* [HGM] right mouse button in clock area edit-game mode ups clock */
\r
4224 if (PtInRect((LPRECT) &whiteRect, pt)) {
\r
4225 if (gameMode == EditGame || GetKeyState(VK_SHIFT) < 0) AdjustClock(flipClock, 1);
\r
4226 } else if (PtInRect((LPRECT) &blackRect, pt)) {
\r
4227 if (gameMode == EditGame || GetKeyState(VK_SHIFT) < 0) AdjustClock(!flipClock, 1);
\r
4231 DrawPosition(TRUE, NULL);
\r
4233 menuNr = RightClick(Press, pt.x - boardRect.left, pt.y - boardRect.top, &fromX, &fromY);
\r
4236 if (message == WM_MBUTTONDOWN) {
\r
4237 buttonCount = 3; /* even if system didn't think so */
\r
4238 if (wParam & MK_SHIFT)
\r
4239 MenuPopup(hwnd, pt, LoadMenu(hInst, "BlackPieceMenu"), -1);
\r
4241 MenuPopup(hwnd, pt, LoadMenu(hInst, "WhitePieceMenu"), -1);
\r
4242 } else { /* message == WM_RBUTTONDOWN */
\r
4243 /* Just have one menu, on the right button. Windows users don't
\r
4244 think to try the middle one, and sometimes other software steals
\r
4245 it, or it doesn't really exist. */
\r
4246 if(gameInfo.variant != VariantShogi)
\r
4247 MenuPopup(hwnd, pt, LoadMenu(hInst, "PieceMenu"), -1);
\r
4249 MenuPopup(hwnd, pt, LoadMenu(hInst, "ShogiPieceMenu"), -1);
\r
4253 SetCapture(hwndMain);
4256 hmenu = LoadMenu(hInst, "DropPieceMenu");
\r
4257 SetupDropMenu(hmenu);
\r
4258 MenuPopup(hwnd, pt, hmenu, -1);
\r
4268 /* Preprocess messages for buttons in main window */
\r
4270 ButtonProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
\r
4272 int id = GetWindowLongPtr(hwnd, GWLP_ID);
\r
4275 for (i=0; i<N_BUTTONS; i++) {
\r
4276 if (buttonDesc[i].id == id) break;
\r
4278 if (i == N_BUTTONS) return 0;
\r
4279 switch (message) {
\r
4284 dir = (wParam == VK_LEFT) ? -1 : 1;
\r
4285 SetFocus(buttonDesc[(i + dir + N_BUTTONS) % N_BUTTONS].hwnd);
\r
4292 SendMessage(hwndMain, WM_COMMAND, MAKEWPARAM(buttonDesc[i].id, 0), 0);
\r
4295 if (appData.icsActive && (isalpha((char)wParam) || wParam == '0')) {
\r
4296 // [HGM] movenum: only letters or leading zero should go to ICS input
\r
4297 HWND h = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
4298 if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);
\r
4300 SendMessage(h, WM_CHAR, wParam, lParam);
\r
4302 } else if (isalpha((char)wParam) || isdigit((char)wParam)){
\r
4303 TypeInEvent((char)wParam);
\r
4309 return CallWindowProc(buttonDesc[i].wndproc, hwnd, message, wParam, lParam);
\r
4312 /* Process messages for Promotion dialog box */
\r
4314 Promotion(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
4318 switch (message) {
\r
4319 case WM_INITDIALOG: /* message: initialize dialog box */
\r
4320 /* Center the dialog over the application window */
\r
4321 CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));
\r
4322 Translate(hDlg, DLG_PromotionKing);
\r
4323 ShowWindow(GetDlgItem(hDlg, PB_King),
\r
4324 (!appData.testLegality || gameInfo.variant == VariantSuicide ||
\r
4325 gameInfo.variant == VariantSpartan && !WhiteOnMove(currentMove) ||
\r
4326 gameInfo.variant == VariantGiveaway || gameInfo.variant == VariantSuper ) ?
\r
4327 SW_SHOW : SW_HIDE);
\r
4328 /* [HGM] Only allow C & A promotions if these pieces are defined */
\r
4329 ShowWindow(GetDlgItem(hDlg, PB_Archbishop),
\r
4330 ((PieceToChar(WhiteAngel) >= 'A' && WhiteOnMove(currentMove) &&
\r
4331 PieceToChar(WhiteAngel) != '~') ||
\r
4332 (PieceToChar(BlackAngel) >= 'A' && !WhiteOnMove(currentMove) &&
\r
4333 PieceToChar(BlackAngel) != '~') ) ?
\r
4334 SW_SHOW : SW_HIDE);
\r
4335 ShowWindow(GetDlgItem(hDlg, PB_Chancellor),
\r
4336 ((PieceToChar(WhiteMarshall) >= 'A' && WhiteOnMove(currentMove) &&
\r
4337 PieceToChar(WhiteMarshall) != '~') ||
\r
4338 (PieceToChar(BlackMarshall) >= 'A' && !WhiteOnMove(currentMove) &&
\r
4339 PieceToChar(BlackMarshall) != '~') ) ?
\r
4340 SW_SHOW : SW_HIDE);
\r
4341 /* [HGM] Hide B & R button in Shogi, use Q as promote, N as defer */
\r
4342 ShowWindow(GetDlgItem(hDlg, PB_Rook),
\r
4343 gameInfo.variant != VariantShogi ?
\r
4344 SW_SHOW : SW_HIDE);
\r
4345 ShowWindow(GetDlgItem(hDlg, PB_Bishop),
\r
4346 gameInfo.variant != VariantShogi ?
\r
4347 SW_SHOW : SW_HIDE);
\r
4348 if(gameInfo.variant == VariantShogi) {
\r
4349 SetDlgItemText(hDlg, PB_Queen, "YES");
\r
4350 SetDlgItemText(hDlg, PB_Knight, "NO");
\r
4351 SetWindowText(hDlg, "Promote?");
\r
4353 ShowWindow(GetDlgItem(hDlg, IDC_Centaur),
\r
4354 gameInfo.variant == VariantSuper ?
\r
4355 SW_SHOW : SW_HIDE);
\r
4358 case WM_COMMAND: /* message: received a command */
\r
4359 switch (LOWORD(wParam)) {
\r
4361 EndDialog(hDlg, TRUE); /* Exit the dialog */
\r
4362 ClearHighlights();
\r
4363 DrawPosition(FALSE, NULL);
\r
4366 promoChar = gameInfo.variant == VariantSuper ? PieceToChar(BlackSilver) : PieceToChar(BlackKing);
\r
4369 promoChar = gameInfo.variant == VariantShogi ? '+' : ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteQueen : BlackQueen));
\r
4372 promoChar = ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteRook : BlackRook));
\r
4373 if(gameInfo.variant == VariantSpartan && !WhiteOnMove(currentMove)) promoChar = PieceToChar(BlackDragon);
\r
4376 promoChar = ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteBishop : BlackBishop));
\r
4377 if(gameInfo.variant == VariantSpartan && !WhiteOnMove(currentMove)) promoChar = PieceToChar(BlackAlfil);
\r
4379 case PB_Chancellor:
\r
4380 promoChar = ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteMarshall : BlackMarshall));
\r
4382 case PB_Archbishop:
\r
4383 promoChar = ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteAngel : BlackAngel));
\r
4386 promoChar = gameInfo.variant == VariantShogi ? '=' : PieceToChar(WhiteOnMove(currentMove) ? WhiteKnight : BlackKnight);
\r
4391 if(promoChar == '.') return FALSE; // invalid piece chosen
\r
4392 EndDialog(hDlg, TRUE); /* Exit the dialog */
\r
4393 UserMoveEvent(fromX, fromY, toX, toY, promoChar);
\r
4394 fromX = fromY = -1;
\r
4395 if (!appData.highlightLastMove) {
\r
4396 ClearHighlights();
\r
4397 DrawPosition(FALSE, NULL);
\r
4404 /* Pop up promotion dialog */
\r
4406 PromotionPopup(HWND hwnd)
\r
4410 lpProc = MakeProcInstance((FARPROC)Promotion, hInst);
\r
4411 DialogBox(hInst, MAKEINTRESOURCE(DLG_PromotionKing),
\r
4412 hwnd, (DLGPROC)lpProc);
\r
4413 FreeProcInstance(lpProc);
\r
4419 DrawPosition(TRUE, NULL);
\r
4420 PromotionPopup(hwndMain);
\r
4423 /* Toggle ShowThinking */
\r
4425 ToggleShowThinking()
\r
4427 appData.showThinking = !appData.showThinking;
\r
4428 ShowThinkingEvent();
\r
4432 LoadGameDialog(HWND hwnd, char* title)
\r
4436 char fileTitle[MSG_SIZ];
\r
4437 f = OpenFileDialog(hwnd, "rb", "",
\r
4438 appData.oldSaveStyle ? "gam" : "pgn",
\r
4440 title, &number, fileTitle, NULL);
\r
4442 cmailMsgLoaded = FALSE;
\r
4443 if (number == 0) {
\r
4444 int error = GameListBuild(f);
\r
4446 DisplayError(_("Cannot build game list"), error);
\r
4447 } else if (!ListEmpty(&gameList) &&
\r
4448 ((ListGame *) gameList.tailPred)->number > 1) {
\r
4449 GameListPopUp(f, fileTitle);
\r
4452 GameListDestroy();
\r
4455 LoadGame(f, number, fileTitle, FALSE);
\r
4459 int get_term_width()
\r
4464 HFONT hfont, hold_font;
\r
4469 hText = GetDlgItem(hwndConsole, OPT_ConsoleText);
\r
4473 // get the text metrics
\r
4474 hdc = GetDC(hText);
\r
4475 lf = font[boardSize][CONSOLE_FONT]->lf;
\r
4476 if (consoleCF.dwEffects & CFE_BOLD)
\r
4477 lf.lfWeight = FW_BOLD;
\r
4478 if (consoleCF.dwEffects & CFE_ITALIC)
\r
4479 lf.lfItalic = TRUE;
\r
4480 if (consoleCF.dwEffects & CFE_STRIKEOUT)
\r
4481 lf.lfStrikeOut = TRUE;
\r
4482 if (consoleCF.dwEffects & CFE_UNDERLINE)
\r
4483 lf.lfUnderline = TRUE;
\r
4484 hfont = CreateFontIndirect(&lf);
\r
4485 hold_font = SelectObject(hdc, hfont);
\r
4486 GetTextMetrics(hdc, &tm);
\r
4487 SelectObject(hdc, hold_font);
\r
4488 DeleteObject(hfont);
\r
4489 ReleaseDC(hText, hdc);
\r
4491 // get the rectangle
\r
4492 SendMessage(hText, EM_GETRECT, 0, (LPARAM)&rc);
\r
4494 return (rc.right-rc.left) / tm.tmAveCharWidth;
\r
4497 void UpdateICSWidth(HWND hText)
\r
4499 LONG old_width, new_width;
\r
4501 new_width = get_term_width(hText, FALSE);
\r
4502 old_width = GetWindowLongPtr(hText, GWLP_USERDATA);
\r
4503 if (new_width != old_width)
\r
4505 ics_update_width(new_width);
\r
4506 SetWindowLongPtr(hText, GWLP_USERDATA, new_width);
\r
4511 ChangedConsoleFont()
\r
4514 CHARRANGE tmpsel, sel;
\r
4515 MyFont *f = font[boardSize][CONSOLE_FONT];
\r
4516 HWND hText = GetDlgItem(hwndConsole, OPT_ConsoleText);
\r
4517 HWND hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
4520 cfmt.cbSize = sizeof(CHARFORMAT);
\r
4521 cfmt.dwMask = CFM_FACE|CFM_SIZE|CFM_CHARSET;
\r
4522 safeStrCpy(cfmt.szFaceName, font[boardSize][CONSOLE_FONT]->mfp.faceName,
\r
4523 sizeof(cfmt.szFaceName)/sizeof(cfmt.szFaceName[0]) );
\r
4524 /* yHeight is expressed in twips. A twip is 1/20 of a font's point
\r
4525 * size. This was undocumented in the version of MSVC++ that I had
\r
4526 * when I wrote the code, but is apparently documented now.
\r
4528 cfmt.yHeight = (int)(f->mfp.pointSize * 20.0 + 0.5);
\r
4529 cfmt.bCharSet = f->lf.lfCharSet;
\r
4530 cfmt.bPitchAndFamily = f->lf.lfPitchAndFamily;
\r
4531 SendMessage(hText, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &cfmt);
\r
4532 SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &cfmt);
\r
4533 /* Why are the following seemingly needed too? */
\r
4534 SendMessage(hText, EM_SETCHARFORMAT, SCF_DEFAULT, (LPARAM) &cfmt);
\r
4535 SendMessage(hInput, EM_SETCHARFORMAT, SCF_DEFAULT, (LPARAM) &cfmt);
\r
4536 SendMessage(hText, EM_EXGETSEL, 0, (LPARAM)&sel);
\r
4538 tmpsel.cpMax = -1; /*999999?*/
\r
4539 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&tmpsel);
\r
4540 SendMessage(hText, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cfmt);
\r
4541 /* Trying putting this here too. It still seems to tickle a RichEdit
\r
4542 * bug: sometimes RichEdit indents the first line of a paragraph too.
\r
4544 paraf.cbSize = sizeof(paraf);
\r
4545 paraf.dwMask = PFM_OFFSET | PFM_STARTINDENT;
\r
4546 paraf.dxStartIndent = 0;
\r
4547 paraf.dxOffset = WRAP_INDENT;
\r
4548 SendMessage(hText, EM_SETPARAFORMAT, 0, (LPARAM) ¶f);
\r
4549 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
4550 UpdateICSWidth(hText);
\r
4553 /*---------------------------------------------------------------------------*\
\r
4555 * Window Proc for main window
\r
4557 \*---------------------------------------------------------------------------*/
\r
4559 /* Process messages for main window, etc. */
\r
4561 WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
\r
4564 int wmId, wmEvent;
\r
4568 char fileTitle[MSG_SIZ];
\r
4569 char buf[MSG_SIZ];
\r
4570 static SnapData sd;
\r
4572 switch (message) {
\r
4574 case WM_PAINT: /* message: repaint portion of window */
\r
4578 case WM_ERASEBKGND:
\r
4579 if (IsIconic(hwnd)) {
\r
4580 /* Cheat; change the message */
\r
4581 return (DefWindowProc(hwnd, WM_ICONERASEBKGND, wParam, lParam));
\r
4583 return (DefWindowProc(hwnd, message, wParam, lParam));
\r
4587 case WM_LBUTTONDOWN:
\r
4588 case WM_MBUTTONDOWN:
\r
4589 case WM_RBUTTONDOWN:
\r
4590 case WM_LBUTTONUP:
\r
4591 case WM_MBUTTONUP:
\r
4592 case WM_RBUTTONUP:
\r
4593 case WM_MOUSEMOVE:
\r
4594 case WM_MOUSEWHEEL:
\r
4595 MouseEvent(hwnd, message, wParam, lParam);
\r
4598 JAWS_KB_NAVIGATION
\r
4602 JAWS_ALT_INTERCEPT
\r
4604 if (appData.icsActive && ((char)wParam == '\r' || (char)wParam > ' ' && !((char)wParam >= '1' && (char)wParam <= '9'))) {
\r
4605 // [HGM] movenum: for non-zero digits we always do type-in dialog
\r
4606 HWND h = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
4607 if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);
\r
4609 SendMessage(h, message, wParam, lParam);
\r
4610 } else if(lParam != KF_REPEAT) {
\r
4611 if (isalpha((char)wParam) || isdigit((char)wParam)) {
\r
4612 TypeInEvent((char)wParam);
\r
4613 } else if((char)wParam == 003) CopyGameToClipboard();
\r
4614 else if((char)wParam == 026) PasteGameOrFENFromClipboard();
\r
4619 case WM_PALETTECHANGED:
\r
4620 if (hwnd != (HWND)wParam && !appData.monoMode) {
\r
4622 HDC hdc = GetDC(hwndMain);
\r
4623 SelectPalette(hdc, hPal, TRUE);
\r
4624 nnew = RealizePalette(hdc);
\r
4626 paletteChanged = TRUE;
\r
4627 InvalidateRect(hwnd, &boardRect, FALSE);
\r
4629 ReleaseDC(hwnd, hdc);
\r
4633 case WM_QUERYNEWPALETTE:
\r
4634 if (!appData.monoMode /*&& paletteChanged*/) {
\r
4636 HDC hdc = GetDC(hwndMain);
\r
4637 paletteChanged = FALSE;
\r
4638 SelectPalette(hdc, hPal, FALSE);
\r
4639 nnew = RealizePalette(hdc);
\r
4641 InvalidateRect(hwnd, &boardRect, FALSE);
\r
4643 ReleaseDC(hwnd, hdc);
\r
4648 case WM_COMMAND: /* message: command from application menu */
\r
4649 wmId = LOWORD(wParam);
\r
4650 wmEvent = HIWORD(wParam);
\r
4655 SAY("new game enter a move to play against the computer with white");
\r
4658 case IDM_NewGameFRC:
\r
4659 if( NewGameFRC() == 0 ) {
\r
4664 case IDM_NewVariant:
\r
4665 NewVariantPopup(hwnd);
\r
4668 case IDM_LoadGame:
\r
4669 LoadGameDialog(hwnd, _("Load Game from File"));
\r
4672 case IDM_LoadNextGame:
\r
4676 case IDM_LoadPrevGame:
\r
4680 case IDM_ReloadGame:
\r
4684 case IDM_LoadPosition:
\r
4685 if (gameMode == AnalyzeMode || gameMode == AnalyzeFile) {
\r
4686 Reset(FALSE, TRUE);
\r
4689 f = OpenFileDialog(hwnd, "rb", "",
\r
4690 appData.oldSaveStyle ? "pos" : "fen",
\r
4692 _("Load Position from File"), &number, fileTitle, NULL);
\r
4694 LoadPosition(f, number, fileTitle);
\r
4698 case IDM_LoadNextPosition:
\r
4699 ReloadPosition(1);
\r
4702 case IDM_LoadPrevPosition:
\r
4703 ReloadPosition(-1);
\r
4706 case IDM_ReloadPosition:
\r
4707 ReloadPosition(0);
\r
4710 case IDM_SaveGame:
\r
4711 defName = DefaultFileName(appData.oldSaveStyle ? "gam" : "pgn");
\r
4712 f = OpenFileDialog(hwnd, "a", defName,
\r
4713 appData.oldSaveStyle ? "gam" : "pgn",
\r
4715 _("Save Game to File"), NULL, fileTitle, NULL);
\r
4717 SaveGame(f, 0, "");
\r
4721 case IDM_SavePosition:
\r
4722 defName = DefaultFileName(appData.oldSaveStyle ? "pos" : "fen");
\r
4723 f = OpenFileDialog(hwnd, "a", defName,
\r
4724 appData.oldSaveStyle ? "pos" : "fen",
\r
4726 _("Save Position to File"), NULL, fileTitle, NULL);
\r
4728 SavePosition(f, 0, "");
\r
4732 case IDM_SaveDiagram:
\r
4733 defName = "diagram";
\r
4734 f = OpenFileDialog(hwnd, "wb", defName,
\r
4737 _("Save Diagram to File"), NULL, fileTitle, NULL);
\r
4743 case IDM_CopyGame:
\r
4744 CopyGameToClipboard();
\r
4747 case IDM_PasteGame:
\r
4748 PasteGameFromClipboard();
\r
4751 case IDM_CopyGameListToClipboard:
\r
4752 CopyGameListToClipboard();
\r
4755 /* [AS] Autodetect FEN or PGN data */
\r
4756 case IDM_PasteAny:
\r
4757 PasteGameOrFENFromClipboard();
\r
4760 /* [AS] Move history */
\r
4761 case IDM_ShowMoveHistory:
\r
4762 if( MoveHistoryIsUp() ) {
\r
4763 MoveHistoryPopDown();
\r
4766 MoveHistoryPopUp();
\r
4770 /* [AS] Eval graph */
\r
4771 case IDM_ShowEvalGraph:
\r
4772 if( EvalGraphIsUp() ) {
\r
4773 EvalGraphPopDown();
\r
4777 SetFocus(hwndMain);
\r
4781 /* [AS] Engine output */
\r
4782 case IDM_ShowEngineOutput:
\r
4783 if( EngineOutputIsUp() ) {
\r
4784 EngineOutputPopDown();
\r
4787 EngineOutputPopUp();
\r
4791 /* [AS] User adjudication */
\r
4792 case IDM_UserAdjudication_White:
\r
4793 UserAdjudicationEvent( +1 );
\r
4796 case IDM_UserAdjudication_Black:
\r
4797 UserAdjudicationEvent( -1 );
\r
4800 case IDM_UserAdjudication_Draw:
\r
4801 UserAdjudicationEvent( 0 );
\r
4804 /* [AS] Game list options dialog */
\r
4805 case IDM_GameListOptions:
\r
4806 GameListOptions();
\r
4813 case IDM_CopyPosition:
\r
4814 CopyFENToClipboard();
\r
4817 case IDM_PastePosition:
\r
4818 PasteFENFromClipboard();
\r
4821 case IDM_MailMove:
\r
4825 case IDM_ReloadCMailMsg:
\r
4826 Reset(TRUE, TRUE);
\r
4827 ReloadCmailMsgEvent(FALSE);
\r
4830 case IDM_Minimize:
\r
4831 ShowWindow(hwnd, SW_MINIMIZE);
\r
4838 case IDM_MachineWhite:
\r
4839 MachineWhiteEvent();
\r
4841 * refresh the tags dialog only if it's visible
\r
4843 if (gameMode == MachinePlaysWhite && IsWindowVisible(editTagsDialog)) {
\r
4845 tags = PGNTags(&gameInfo);
\r
4846 TagsPopUp(tags, CmailMsg());
\r
4849 SAY("computer starts playing white");
\r
4852 case IDM_MachineBlack:
\r
4853 MachineBlackEvent();
\r
4855 * refresh the tags dialog only if it's visible
\r
4857 if (gameMode == MachinePlaysBlack && IsWindowVisible(editTagsDialog)) {
\r
4859 tags = PGNTags(&gameInfo);
\r
4860 TagsPopUp(tags, CmailMsg());
\r
4863 SAY("computer starts playing black");
\r
4866 case IDM_Match: // [HGM] match: flows into next case, after setting Match Mode and nr of Games
\r
4867 MatchEvent(2); // distinguish from command-line-triggered case (matchMode=1)
\r
4870 case IDM_TwoMachines:
\r
4871 TwoMachinesEvent();
\r
4873 * refresh the tags dialog only if it's visible
\r
4875 if (gameMode == TwoMachinesPlay && IsWindowVisible(editTagsDialog)) {
\r
4877 tags = PGNTags(&gameInfo);
\r
4878 TagsPopUp(tags, CmailMsg());
\r
4881 SAY("computer starts playing both sides");
\r
4884 case IDM_AnalysisMode:
\r
4885 if (!first.analysisSupport) {
\r
4886 snprintf(buf, MSG_SIZ, _("%s does not support analysis"), first.tidy);
\r
4887 DisplayError(buf, 0);
\r
4889 SAY("analyzing current position");
\r
4890 /* [DM] icsEngineAnlyze [HGM] Why is this front-end??? */
\r
4891 if (appData.icsActive) {
\r
4892 if (gameMode != IcsObserving) {
\r
4893 snprintf(buf, MSG_SIZ, "You are not observing a game");
\r
4894 DisplayError(buf, 0);
\r
4895 /* secure check */
\r
4896 if (appData.icsEngineAnalyze) {
\r
4897 if (appData.debugMode)
\r
4898 fprintf(debugFP, "Found unexpected active ICS engine analyze \n");
\r
4899 ExitAnalyzeMode();
\r
4905 /* if enable, user want disable icsEngineAnalyze */
\r
4906 if (appData.icsEngineAnalyze) {
\r
4907 ExitAnalyzeMode();
\r
4911 appData.icsEngineAnalyze = TRUE;
\r
4912 if (appData.debugMode) fprintf(debugFP, "ICS engine analyze starting...\n");
\r
4915 if (!appData.showThinking) ToggleShowThinking();
\r
4916 AnalyzeModeEvent();
\r
4920 case IDM_AnalyzeFile:
\r
4921 if (!first.analysisSupport) {
\r
4922 char buf[MSG_SIZ];
\r
4923 snprintf(buf, MSG_SIZ, _("%s does not support analysis"), first.tidy);
\r
4924 DisplayError(buf, 0);
\r
4926 if (!appData.showThinking) ToggleShowThinking();
\r
4927 AnalyzeFileEvent();
\r
4928 LoadGameDialog(hwnd, _("Analyze Game from File"));
\r
4929 AnalysisPeriodicEvent(1);
\r
4933 case IDM_IcsClient:
\r
4937 case IDM_EditGame:
\r
4938 case IDM_EditGame2:
\r
4943 case IDM_EditPosition:
\r
4944 case IDM_EditPosition2:
\r
4945 EditPositionEvent();
\r
4946 SAY("enter a FEN string or setup a position on the board using the control R pop up menu");
\r
4949 case IDM_Training:
\r
4953 case IDM_ShowGameList:
\r
4954 ShowGameListProc();
\r
4957 case IDM_EditProgs1:
\r
4958 EditTagsPopUp(firstChessProgramNames, &firstChessProgramNames);
\r
4961 case IDM_EditProgs2:
\r
4962 LoadEnginePopUp(hwndMain);
\r
4963 // EditTagsPopUp(secondChessProgramNames, &secondChessProgramNames);
\r
4966 case IDM_EditServers:
\r
4967 EditTagsPopUp(icsNames, &icsNames);
\r
4970 case IDM_EditTags:
\r
4975 case IDM_EditBook:
\r
4979 case IDM_EditComment:
\r
4981 if (commentUp && editComment) {
\r
4984 EditCommentEvent();
\r
5004 case IDM_CallFlag:
\r
5024 case IDM_StopObserving:
\r
5025 StopObservingEvent();
\r
5028 case IDM_StopExamining:
\r
5029 StopExaminingEvent();
\r
5033 UploadGameEvent();
\r
5036 case IDM_TypeInMove:
\r
5037 TypeInEvent('\000');
\r
5040 case IDM_TypeInName:
\r
5041 PopUpNameDialog('\000');
\r
5044 case IDM_Backward:
\r
5046 SetFocus(hwndMain);
\r
5053 SetFocus(hwndMain);
\r
5058 SetFocus(hwndMain);
\r
5063 SetFocus(hwndMain);
\r
5067 RevertEvent(FALSE);
\r
5070 case IDM_Annotate: // [HGM] vari: revert with annotation
\r
5071 RevertEvent(TRUE);
\r
5074 case IDM_TruncateGame:
\r
5075 TruncateGameEvent();
\r
5082 case IDM_RetractMove:
\r
5083 RetractMoveEvent();
\r
5086 case IDM_FlipView:
\r
5087 flipView = !flipView;
\r
5088 DrawPosition(FALSE, NULL);
\r
5091 case IDM_FlipClock:
\r
5092 flipClock = !flipClock;
\r
5093 DisplayBothClocks();
\r
5097 case IDM_MuteSounds:
\r
5098 mute = !mute; // [HGM] mute: keep track of global muting variable
\r
5099 CheckMenuItem(GetMenu(hwndMain),IDM_MuteSounds,
\r
5100 MF_BYCOMMAND|(mute?MF_CHECKED:MF_UNCHECKED));
\r
5103 case IDM_GeneralOptions:
\r
5104 GeneralOptionsPopup(hwnd);
\r
5105 DrawPosition(TRUE, NULL);
\r
5108 case IDM_BoardOptions:
\r
5109 BoardOptionsPopup(hwnd);
\r
5112 case IDM_EnginePlayOptions:
\r
5113 EnginePlayOptionsPopup(hwnd);
\r
5116 case IDM_Engine1Options:
\r
5117 EngineOptionsPopup(hwnd, &first);
\r
5120 case IDM_Engine2Options:
\r
5122 if(WaitForEngine(&second, SettingsMenuIfReady)) break;
\r
5123 EngineOptionsPopup(hwnd, &second);
\r
5126 case IDM_OptionsUCI:
\r
5127 UciOptionsPopup(hwnd);
\r
5131 TourneyPopup(hwnd);
\r
5134 case IDM_IcsOptions:
\r
5135 IcsOptionsPopup(hwnd);
\r
5139 FontsOptionsPopup(hwnd);
\r
5143 SoundOptionsPopup(hwnd);
\r
5146 case IDM_CommPort:
\r
5147 CommPortOptionsPopup(hwnd);
\r
5150 case IDM_LoadOptions:
\r
5151 LoadOptionsPopup(hwnd);
\r
5154 case IDM_SaveOptions:
\r
5155 SaveOptionsPopup(hwnd);
\r
5158 case IDM_TimeControl:
\r
5159 TimeControlOptionsPopup(hwnd);
\r
5162 case IDM_SaveSettings:
\r
5163 SaveSettings(settingsFileName);
\r
5166 case IDM_SaveSettingsOnExit:
\r
5167 saveSettingsOnExit = !saveSettingsOnExit;
\r
5168 (void) CheckMenuItem(GetMenu(hwndMain), IDM_SaveSettingsOnExit,
\r
5169 MF_BYCOMMAND|(saveSettingsOnExit ?
\r
5170 MF_CHECKED : MF_UNCHECKED));
\r
5181 case IDM_AboutGame:
\r
5186 appData.debugMode = !appData.debugMode;
\r
5187 if (appData.debugMode) {
\r
5188 char dir[MSG_SIZ];
\r
5189 GetCurrentDirectory(MSG_SIZ, dir);
\r
5190 SetCurrentDirectory(installDir);
\r
5191 debugFP = fopen(appData.nameOfDebugFile, "w");
\r
5192 SetCurrentDirectory(dir);
\r
5193 setbuf(debugFP, NULL);
\r
5200 case IDM_HELPCONTENTS:
\r
5201 if (!MyHelp (hwnd, "winboard.hlp", HELP_KEY,(DWORD)(LPSTR)"CONTENTS") &&
\r
5202 !HtmlHelp(hwnd, "winboard.chm", 0, 0) ) {
\r
5203 MessageBox (GetFocus(),
\r
5204 _("Unable to activate help"),
\r
5205 szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);
\r
5209 case IDM_HELPSEARCH:
\r
5210 if (!MyHelp (hwnd, "winboard.hlp", HELP_PARTIALKEY, (DWORD)(LPSTR)"") &&
\r
5211 !HtmlHelp(hwnd, "winboard.chm", 0, 0) ) {
\r
5212 MessageBox (GetFocus(),
\r
5213 _("Unable to activate help"),
\r
5214 szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);
\r
5218 case IDM_HELPHELP:
\r
5219 if(!WinHelp(hwnd, (LPSTR)NULL, HELP_HELPONHELP, 0)) {
\r
5220 MessageBox (GetFocus(),
\r
5221 _("Unable to activate help"),
\r
5222 szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);
\r
5227 lpProc = MakeProcInstance((FARPROC)About, hInst);
\r
5229 (gameInfo.event && strcmp(gameInfo.event, "Easter Egg Hunt") == 0) ?
\r
5230 "AboutBox2" : "AboutBox", hwnd, (DLGPROC)lpProc);
\r
5231 FreeProcInstance(lpProc);
\r
5234 case IDM_DirectCommand1:
\r
5235 AskQuestionEvent(_("Direct Command"),
\r
5236 _("Send to chess program:"), "", "1");
\r
5238 case IDM_DirectCommand2:
\r
5239 AskQuestionEvent(_("Direct Command"),
\r
5240 _("Send to second chess program:"), "", "2");
\r
5243 case EP_WhitePawn:
\r
5244 EditPositionMenuEvent(WhitePawn, fromX, fromY);
\r
5245 fromX = fromY = -1;
\r
5248 case EP_WhiteKnight:
\r
5249 EditPositionMenuEvent(WhiteKnight, fromX, fromY);
\r
5250 fromX = fromY = -1;
\r
5253 case EP_WhiteBishop:
\r
5254 EditPositionMenuEvent(WhiteBishop, fromX, fromY);
\r
5255 fromX = fromY = -1;
\r
5258 case EP_WhiteRook:
\r
5259 EditPositionMenuEvent(WhiteRook, fromX, fromY);
\r
5260 fromX = fromY = -1;
\r
5263 case EP_WhiteQueen:
\r
5264 EditPositionMenuEvent(WhiteQueen, fromX, fromY);
\r
5265 fromX = fromY = -1;
\r
5268 case EP_WhiteFerz:
\r
5269 EditPositionMenuEvent(WhiteFerz, fromX, fromY);
\r
5270 fromX = fromY = -1;
\r
5273 case EP_WhiteWazir:
\r
5274 EditPositionMenuEvent(WhiteWazir, fromX, fromY);
\r
5275 fromX = fromY = -1;
\r
5278 case EP_WhiteAlfil:
\r
5279 EditPositionMenuEvent(WhiteAlfil, fromX, fromY);
\r
5280 fromX = fromY = -1;
\r
5283 case EP_WhiteCannon:
\r
5284 EditPositionMenuEvent(WhiteCannon, fromX, fromY);
\r
5285 fromX = fromY = -1;
\r
5288 case EP_WhiteCardinal:
\r
5289 EditPositionMenuEvent(WhiteAngel, fromX, fromY);
\r
5290 fromX = fromY = -1;
\r
5293 case EP_WhiteMarshall:
\r
5294 EditPositionMenuEvent(WhiteMarshall, fromX, fromY);
\r
5295 fromX = fromY = -1;
\r
5298 case EP_WhiteKing:
\r
5299 EditPositionMenuEvent(WhiteKing, fromX, fromY);
\r
5300 fromX = fromY = -1;
\r
5303 case EP_BlackPawn:
\r
5304 EditPositionMenuEvent(BlackPawn, fromX, fromY);
\r
5305 fromX = fromY = -1;
\r
5308 case EP_BlackKnight:
\r
5309 EditPositionMenuEvent(BlackKnight, fromX, fromY);
\r
5310 fromX = fromY = -1;
\r
5313 case EP_BlackBishop:
\r
5314 EditPositionMenuEvent(BlackBishop, fromX, fromY);
\r
5315 fromX = fromY = -1;
\r
5318 case EP_BlackRook:
\r
5319 EditPositionMenuEvent(BlackRook, fromX, fromY);
\r
5320 fromX = fromY = -1;
\r
5323 case EP_BlackQueen:
\r
5324 EditPositionMenuEvent(BlackQueen, fromX, fromY);
\r
5325 fromX = fromY = -1;
\r
5328 case EP_BlackFerz:
\r
5329 EditPositionMenuEvent(BlackFerz, fromX, fromY);
\r
5330 fromX = fromY = -1;
\r
5333 case EP_BlackWazir:
\r
5334 EditPositionMenuEvent(BlackWazir, fromX, fromY);
\r
5335 fromX = fromY = -1;
\r
5338 case EP_BlackAlfil:
\r
5339 EditPositionMenuEvent(BlackAlfil, fromX, fromY);
\r
5340 fromX = fromY = -1;
\r
5343 case EP_BlackCannon:
\r
5344 EditPositionMenuEvent(BlackCannon, fromX, fromY);
\r
5345 fromX = fromY = -1;
\r
5348 case EP_BlackCardinal:
\r
5349 EditPositionMenuEvent(BlackAngel, fromX, fromY);
\r
5350 fromX = fromY = -1;
\r
5353 case EP_BlackMarshall:
\r
5354 EditPositionMenuEvent(BlackMarshall, fromX, fromY);
\r
5355 fromX = fromY = -1;
\r
5358 case EP_BlackKing:
\r
5359 EditPositionMenuEvent(BlackKing, fromX, fromY);
\r
5360 fromX = fromY = -1;
\r
5363 case EP_EmptySquare:
\r
5364 EditPositionMenuEvent(EmptySquare, fromX, fromY);
\r
5365 fromX = fromY = -1;
\r
5368 case EP_ClearBoard:
\r
5369 EditPositionMenuEvent(ClearBoard, fromX, fromY);
\r
5370 fromX = fromY = -1;
\r
5374 EditPositionMenuEvent(WhitePlay, fromX, fromY);
\r
5375 fromX = fromY = -1;
\r
5379 EditPositionMenuEvent(BlackPlay, fromX, fromY);
\r
5380 fromX = fromY = -1;
\r
5384 EditPositionMenuEvent(PromotePiece, fromX, fromY);
\r
5385 fromX = fromY = -1;
\r
5389 EditPositionMenuEvent(DemotePiece, fromX, fromY);
\r
5390 fromX = fromY = -1;
\r
5394 DropMenuEvent(WhitePawn, fromX, fromY);
\r
5395 fromX = fromY = -1;
\r
5399 DropMenuEvent(WhiteKnight, fromX, fromY);
\r
5400 fromX = fromY = -1;
\r
5404 DropMenuEvent(WhiteBishop, fromX, fromY);
\r
5405 fromX = fromY = -1;
\r
5409 DropMenuEvent(WhiteRook, fromX, fromY);
\r
5410 fromX = fromY = -1;
\r
5414 DropMenuEvent(WhiteQueen, fromX, fromY);
\r
5415 fromX = fromY = -1;
\r
5419 barbaric = 0; appData.language = "";
\r
5420 TranslateMenus(0);
\r
5421 CheckMenuItem(GetMenu(hwndMain), lastChecked, MF_BYCOMMAND|MF_UNCHECKED);
\r
5422 CheckMenuItem(GetMenu(hwndMain), IDM_English, MF_BYCOMMAND|MF_CHECKED);
\r
5423 lastChecked = wmId;
\r
5427 if(wmId > IDM_English && wmId < IDM_English+20) {
\r
5428 LoadLanguageFile(languageFile[wmId - IDM_English - 1]);
\r
5429 TranslateMenus(0);
\r
5430 CheckMenuItem(GetMenu(hwndMain), lastChecked, MF_BYCOMMAND|MF_UNCHECKED);
\r
5431 CheckMenuItem(GetMenu(hwndMain), wmId, MF_BYCOMMAND|MF_CHECKED);
\r
5432 lastChecked = wmId;
\r
5435 return (DefWindowProc(hwnd, message, wParam, lParam));
\r
5441 case CLOCK_TIMER_ID:
\r
5442 KillTimer(hwnd, clockTimerEvent); /* Simulate one-shot timer as in X */
\r
5443 clockTimerEvent = 0;
\r
5444 DecrementClocks(); /* call into back end */
\r
5446 case LOAD_GAME_TIMER_ID:
\r
5447 KillTimer(hwnd, loadGameTimerEvent); /* Simulate one-shot timer as in X*/
\r
5448 loadGameTimerEvent = 0;
\r
5449 AutoPlayGameLoop(); /* call into back end */
\r
5451 case ANALYSIS_TIMER_ID:
\r
5452 if ((gameMode == AnalyzeMode || gameMode == AnalyzeFile
\r
5453 || appData.icsEngineAnalyze) && appData.periodicUpdates) {
\r
5454 AnalysisPeriodicEvent(0);
\r
5456 KillTimer(hwnd, analysisTimerEvent);
\r
5457 analysisTimerEvent = 0;
\r
5460 case DELAYED_TIMER_ID:
\r
5461 KillTimer(hwnd, delayedTimerEvent);
\r
5462 delayedTimerEvent = 0;
\r
5463 delayedTimerCallback();
\r
5468 case WM_USER_Input:
\r
5469 InputEvent(hwnd, message, wParam, lParam);
\r
5472 /* [AS] Also move "attached" child windows */
\r
5473 case WM_WINDOWPOSCHANGING:
\r
5475 if( hwnd == hwndMain && appData.useStickyWindows ) {
\r
5476 LPWINDOWPOS lpwp = (LPWINDOWPOS) lParam;
\r
5478 if( ((lpwp->flags & SWP_NOMOVE) == 0) && ((lpwp->flags & SWP_NOSIZE) != 0) ) {
\r
5479 /* Window is moving */
\r
5482 // GetWindowRect( hwnd, &rcMain ); //[HGM] sticky: in XP this returned new position, not old
\r
5483 rcMain.left = wpMain.x; // replace by these 4 lines to reconstruct old rect
\r
5484 rcMain.right = wpMain.x + wpMain.width;
\r
5485 rcMain.top = wpMain.y;
\r
5486 rcMain.bottom = wpMain.y + wpMain.height;
\r
5488 ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, moveHistoryDialog, &wpMoveHistory );
\r
5489 ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, evalGraphDialog, &wpEvalGraph );
\r
5490 ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, engineOutputDialog, &wpEngineOutput );
\r
5491 ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, gameListDialog, &wpGameList );
\r
5492 ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, hwndConsole, &wpConsole );
\r
5493 wpMain.x = lpwp->x;
\r
5494 wpMain.y = lpwp->y;
\r
5499 /* [AS] Snapping */
\r
5500 case WM_ENTERSIZEMOVE:
\r
5501 if(appData.debugMode) { fprintf(debugFP, "size-move\n"); }
\r
5502 if (hwnd == hwndMain) {
\r
5503 doingSizing = TRUE;
\r
5506 return OnEnterSizeMove( &sd, hwnd, wParam, lParam );
\r
5510 if(appData.debugMode) { fprintf(debugFP, "sizing\n"); }
\r
5511 if (hwnd == hwndMain) {
\r
5512 lastSizing = wParam;
\r
5517 if(appData.debugMode) { fprintf(debugFP, "moving\n"); }
\r
5518 return OnMoving( &sd, hwnd, wParam, lParam );
\r
5520 case WM_EXITSIZEMOVE:
\r
5521 if(appData.debugMode) { fprintf(debugFP, "exit size-move, size = %d\n", squareSize); }
\r
5522 if (hwnd == hwndMain) {
\r
5524 doingSizing = FALSE;
\r
5525 InvalidateRect(hwnd, &boardRect, FALSE);
\r
5526 GetClientRect(hwnd, &client);
\r
5527 ResizeBoard(client.right, client.bottom, lastSizing);
\r
5529 if(appData.debugMode) { fprintf(debugFP, "square size = %d\n", squareSize); }
\r
5531 return OnExitSizeMove( &sd, hwnd, wParam, lParam );
\r
5534 case WM_DESTROY: /* message: window being destroyed */
\r
5535 PostQuitMessage(0);
\r
5539 if (hwnd == hwndMain) {
\r
5544 default: /* Passes it on if unprocessed */
\r
5545 return (DefWindowProc(hwnd, message, wParam, lParam));
\r
5550 /*---------------------------------------------------------------------------*\
\r
5552 * Misc utility routines
\r
5554 \*---------------------------------------------------------------------------*/
\r
5557 * Decent random number generator, at least not as bad as Windows
\r
5558 * standard rand, which returns a value in the range 0 to 0x7fff.
\r
5560 unsigned int randstate;
\r
5565 randstate = randstate * 1664525 + 1013904223;
\r
5566 return (int) randstate & 0x7fffffff;
\r
5570 mysrandom(unsigned int seed)
\r
5577 * returns TRUE if user selects a different color, FALSE otherwise
\r
5581 ChangeColor(HWND hwnd, COLORREF *which)
\r
5583 static BOOL firstTime = TRUE;
\r
5584 static DWORD customColors[16];
\r
5586 COLORREF newcolor;
\r
5591 /* Make initial colors in use available as custom colors */
\r
5592 /* Should we put the compiled-in defaults here instead? */
\r
5594 customColors[i++] = lightSquareColor & 0xffffff;
\r
5595 customColors[i++] = darkSquareColor & 0xffffff;
\r
5596 customColors[i++] = whitePieceColor & 0xffffff;
\r
5597 customColors[i++] = blackPieceColor & 0xffffff;
\r
5598 customColors[i++] = highlightSquareColor & 0xffffff;
\r
5599 customColors[i++] = premoveHighlightColor & 0xffffff;
\r
5601 for (ccl = (ColorClass) 0; ccl < NColorClasses && i < 16; ccl++) {
\r
5602 customColors[i++] = textAttribs[ccl].color;
\r
5604 while (i < 16) customColors[i++] = RGB(255, 255, 255);
\r
5605 firstTime = FALSE;
\r
5608 cc.lStructSize = sizeof(cc);
\r
5609 cc.hwndOwner = hwnd;
\r
5610 cc.hInstance = NULL;
\r
5611 cc.rgbResult = (DWORD) (*which & 0xffffff);
\r
5612 cc.lpCustColors = (LPDWORD) customColors;
\r
5613 cc.Flags = CC_RGBINIT|CC_FULLOPEN;
\r
5615 if (!ChooseColor(&cc)) return FALSE;
\r
5617 newcolor = (COLORREF) (0x2000000 | cc.rgbResult);
\r
5618 if (newcolor == *which) return FALSE;
\r
5619 *which = newcolor;
\r
5623 InitDrawingColors();
\r
5624 InvalidateRect(hwnd, &boardRect, FALSE);
\r
5629 MyLoadSound(MySound *ms)
\r
5635 if (ms->data && ms->flag) free(ms->data);
\r
5638 switch (ms->name[0]) {
\r
5644 /* System sound from Control Panel. Don't preload here. */
\r
5648 if (ms->name[1] == NULLCHAR) {
\r
5649 /* "!" alone = silence */
\r
5652 /* Builtin wave resource. Error if not found. */
\r
5653 HANDLE h = FindResource(hInst, ms->name + 1, "WAVE");
\r
5654 if (h == NULL) break;
\r
5655 ms->data = (void *)LoadResource(hInst, h);
\r
5656 ms->flag = 0; // not maloced, so cannot be freed!
\r
5657 if (h == NULL) break;
\r
5662 /* .wav file. Error if not found. */
\r
5663 f = fopen(ms->name, "rb");
\r
5664 if (f == NULL) break;
\r
5665 if (fstat(fileno(f), &st) < 0) break;
\r
5666 ms->data = malloc(st.st_size);
\r
5668 if (fread(ms->data, st.st_size, 1, f) < 1) break;
\r
5674 char buf[MSG_SIZ];
\r
5675 snprintf(buf, MSG_SIZ, _("Error loading sound %s"), ms->name);
\r
5676 DisplayError(buf, GetLastError());
\r
5682 MyPlaySound(MySound *ms)
\r
5684 BOOLEAN ok = FALSE;
\r
5686 if(mute) return TRUE; // [HGM] mute: suppress all sound play when muted
\r
5687 switch (ms->name[0]) {
\r
5689 if(appData.debugMode) fprintf(debugFP, "silence\n");
\r
5694 /* System sound from Control Panel (deprecated feature).
\r
5695 "$" alone or an unset sound name gets default beep (still in use). */
\r
5696 if (ms->name[1]) {
\r
5697 ok = PlaySound(ms->name + 1, NULL, SND_ALIAS|SND_ASYNC);
\r
5699 if (!ok) ok = MessageBeep(MB_OK);
\r
5702 /* Builtin wave resource, or "!" alone for silence */
\r
5703 if (ms->name[1]) {
\r
5704 if (ms->data == NULL) return FALSE;
\r
5705 ok = PlaySound(ms->data, NULL, SND_MEMORY|SND_ASYNC);
\r
5711 /* .wav file. Error if not found. */
\r
5712 if (ms->data == NULL) return FALSE;
\r
5713 ok = PlaySound(ms->data, NULL, SND_MEMORY|SND_ASYNC);
\r
5716 /* Don't print an error: this can happen innocently if the sound driver
\r
5717 is busy; for instance, if another instance of WinBoard is playing
\r
5718 a sound at about the same time. */
\r
5724 OldOpenFileHook(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
5727 OPENFILENAME *ofn;
\r
5728 static UINT *number; /* gross that this is static */
\r
5730 switch (message) {
\r
5731 case WM_INITDIALOG: /* message: initialize dialog box */
\r
5732 /* Center the dialog over the application window */
\r
5733 ofn = (OPENFILENAME *) lParam;
\r
5734 if (ofn->Flags & OFN_ENABLETEMPLATE) {
\r
5735 number = (UINT *) ofn->lCustData;
\r
5736 SendMessage(GetDlgItem(hDlg, edt2), WM_SETTEXT, 0, (LPARAM) "");
\r
5740 CenterWindow (hDlg, GetWindow (hDlg, GW_OWNER));
\r
5741 Translate(hDlg, 1536);
\r
5742 return FALSE; /* Allow for further processing */
\r
5745 if ((LOWORD(wParam) == IDOK) && (number != NULL)) {
\r
5746 *number = GetDlgItemInt(hDlg, OPT_IndexNumberOld, &ok, FALSE);
\r
5748 return FALSE; /* Allow for further processing */
\r
5754 OpenFileHook(HWND hdlg, UINT uiMsg, WPARAM wParam, LPARAM lParam)
\r
5756 static UINT *number;
\r
5757 OPENFILENAME *ofname;
\r
5760 case WM_INITDIALOG:
\r
5761 Translate(hdlg, DLG_IndexNumber);
\r
5762 ofname = (OPENFILENAME *)lParam;
\r
5763 number = (UINT *)(ofname->lCustData);
\r
5766 ofnot = (OFNOTIFY *)lParam;
\r
5767 if (ofnot->hdr.code == CDN_FILEOK) {
\r
5768 *number = GetDlgItemInt(hdlg, OPT_IndexNumber, NULL, FALSE);
\r
5777 OpenFileDialog(HWND hwnd, char *write, char *defName, char *defExt, // [HGM] diag: type of 'write' now string
\r
5778 char *nameFilt, char *dlgTitle, UINT *number,
\r
5779 char fileTitle[MSG_SIZ], char fileName[MSG_SIZ])
\r
5781 OPENFILENAME openFileName;
\r
5782 char buf1[MSG_SIZ];
\r
5785 if (fileName == NULL) fileName = buf1;
\r
5786 if (defName == NULL) {
\r
5787 safeStrCpy(fileName, "*.", 3 );
\r
5788 strcat(fileName, defExt);
\r
5790 safeStrCpy(fileName, defName, MSG_SIZ );
\r
5792 if (fileTitle) safeStrCpy(fileTitle, "", MSG_SIZ );
\r
5793 if (number) *number = 0;
\r
5795 openFileName.lStructSize = sizeof(OPENFILENAME);
\r
5796 openFileName.hwndOwner = hwnd;
\r
5797 openFileName.hInstance = (HANDLE) hInst;
\r
5798 openFileName.lpstrFilter = nameFilt;
\r
5799 openFileName.lpstrCustomFilter = (LPSTR) NULL;
\r
5800 openFileName.nMaxCustFilter = 0L;
\r
5801 openFileName.nFilterIndex = 1L;
\r
5802 openFileName.lpstrFile = fileName;
\r
5803 openFileName.nMaxFile = MSG_SIZ;
\r
5804 openFileName.lpstrFileTitle = fileTitle;
\r
5805 openFileName.nMaxFileTitle = fileTitle ? MSG_SIZ : 0;
\r
5806 openFileName.lpstrInitialDir = NULL;
\r
5807 openFileName.lpstrTitle = dlgTitle;
\r
5808 openFileName.Flags = OFN_PATHMUSTEXIST | OFN_HIDEREADONLY
\r
5809 | (write[0] != 'r' ? 0 : OFN_FILEMUSTEXIST)
\r
5810 | (number ? OFN_ENABLETEMPLATE | OFN_ENABLEHOOK: 0)
\r
5811 | (oldDialog ? 0 : OFN_EXPLORER);
\r
5812 openFileName.nFileOffset = 0;
\r
5813 openFileName.nFileExtension = 0;
\r
5814 openFileName.lpstrDefExt = defExt;
\r
5815 openFileName.lCustData = (LONG) number;
\r
5816 openFileName.lpfnHook = oldDialog ?
\r
5817 (LPOFNHOOKPROC) OldOpenFileHook : (LPOFNHOOKPROC) OpenFileHook;
\r
5818 openFileName.lpTemplateName = (LPSTR)(oldDialog ? 1536 : DLG_IndexNumber);
\r
5820 if (write[0] != 'r' ? GetSaveFileName(&openFileName) :
\r
5821 GetOpenFileName(&openFileName)) {
\r
5822 /* open the file */
\r
5823 f = fopen(openFileName.lpstrFile, write);
\r
5825 MessageBox(hwnd, _("File open failed"), NULL,
\r
5826 MB_OK|MB_ICONEXCLAMATION);
\r
5830 int err = CommDlgExtendedError();
\r
5831 if (err != 0) DisplayError(_("Internal error in file dialog box"), err);
\r
5840 MenuPopup(HWND hwnd, POINT pt, HMENU hmenu, UINT def)
\r
5842 HMENU hmenuTrackPopup; /* floating pop-up menu */
\r
5845 * Get the first pop-up menu in the menu template. This is the
\r
5846 * menu that TrackPopupMenu displays.
\r
5848 hmenuTrackPopup = GetSubMenu(hmenu, 0);
\r
5849 TranslateOneMenu(10, hmenuTrackPopup);
\r
5851 SetMenuDefaultItem(hmenuTrackPopup, def, FALSE);
\r
5854 * TrackPopup uses screen coordinates, so convert the
\r
5855 * coordinates of the mouse click to screen coordinates.
\r
5857 ClientToScreen(hwnd, (LPPOINT) &pt);
\r
5859 /* Draw and track the floating pop-up menu. */
\r
5860 TrackPopupMenu(hmenuTrackPopup, TPM_CENTERALIGN | TPM_RIGHTBUTTON,
\r
5861 pt.x, pt.y, 0, hwnd, NULL);
\r
5863 /* Destroy the menu.*/
\r
5864 DestroyMenu(hmenu);
\r
5869 int sizeX, sizeY, newSizeX, newSizeY;
\r
5871 } ResizeEditPlusButtonsClosure;
\r
5874 ResizeEditPlusButtonsCallback(HWND hChild, LPARAM lparam)
\r
5876 ResizeEditPlusButtonsClosure *cl = (ResizeEditPlusButtonsClosure *)lparam;
\r
5880 if (hChild == cl->hText) return TRUE;
\r
5881 GetWindowRect(hChild, &rect); /* gives screen coords */
\r
5882 pt.x = rect.left + (cl->newSizeX - cl->sizeX)/2;
\r
5883 pt.y = rect.top + cl->newSizeY - cl->sizeY;
\r
5884 ScreenToClient(cl->hDlg, &pt);
\r
5885 cl->hdwp = DeferWindowPos(cl->hdwp, hChild, NULL,
\r
5886 pt.x, pt.y, rect.right - rect.left, rect.bottom - rect.top, SWP_NOZORDER);
\r
5890 /* Resize a dialog that has a (rich) edit field filling most of
\r
5891 the top, with a row of buttons below */
\r
5893 ResizeEditPlusButtons(HWND hDlg, HWND hText, int sizeX, int sizeY, int newSizeX, int newSizeY)
\r
5896 int newTextHeight, newTextWidth;
\r
5897 ResizeEditPlusButtonsClosure cl;
\r
5899 /*if (IsIconic(hDlg)) return;*/
\r
5900 if (newSizeX == sizeX && newSizeY == sizeY) return;
\r
5902 cl.hdwp = BeginDeferWindowPos(8);
\r
5904 GetWindowRect(hText, &rectText); /* gives screen coords */
\r
5905 newTextWidth = rectText.right - rectText.left + newSizeX - sizeX;
\r
5906 newTextHeight = rectText.bottom - rectText.top + newSizeY - sizeY;
\r
5907 if (newTextHeight < 0) {
\r
5908 newSizeY += -newTextHeight;
\r
5909 newTextHeight = 0;
\r
5911 cl.hdwp = DeferWindowPos(cl.hdwp, hText, NULL, 0, 0,
\r
5912 newTextWidth, newTextHeight, SWP_NOZORDER|SWP_NOMOVE);
\r
5918 cl.newSizeX = newSizeX;
\r
5919 cl.newSizeY = newSizeY;
\r
5920 EnumChildWindows(hDlg, ResizeEditPlusButtonsCallback, (LPARAM)&cl);
\r
5922 EndDeferWindowPos(cl.hdwp);
\r
5925 BOOL CenterWindowEx(HWND hwndChild, HWND hwndParent, int mode)
\r
5927 RECT rChild, rParent;
\r
5928 int wChild, hChild, wParent, hParent;
\r
5929 int wScreen, hScreen, xNew, yNew;
\r
5932 /* Get the Height and Width of the child window */
\r
5933 GetWindowRect (hwndChild, &rChild);
\r
5934 wChild = rChild.right - rChild.left;
\r
5935 hChild = rChild.bottom - rChild.top;
\r
5937 /* Get the Height and Width of the parent window */
\r
5938 GetWindowRect (hwndParent, &rParent);
\r
5939 wParent = rParent.right - rParent.left;
\r
5940 hParent = rParent.bottom - rParent.top;
\r
5942 /* Get the display limits */
\r
5943 hdc = GetDC (hwndChild);
\r
5944 wScreen = GetDeviceCaps (hdc, HORZRES);
\r
5945 hScreen = GetDeviceCaps (hdc, VERTRES);
\r
5946 ReleaseDC(hwndChild, hdc);
\r
5948 /* Calculate new X position, then adjust for screen */
\r
5949 xNew = rParent.left + ((wParent - wChild) /2);
\r
5952 } else if ((xNew+wChild) > wScreen) {
\r
5953 xNew = wScreen - wChild;
\r
5956 /* Calculate new Y position, then adjust for screen */
\r
5958 yNew = rParent.top + ((hParent - hChild) /2);
\r
5961 yNew = rParent.top + GetSystemMetrics( SM_CYCAPTION ) * 2 / 3;
\r
5966 } else if ((yNew+hChild) > hScreen) {
\r
5967 yNew = hScreen - hChild;
\r
5970 /* Set it, and return */
\r
5971 return SetWindowPos (hwndChild, NULL,
\r
5972 xNew, yNew, 0, 0, SWP_NOSIZE | SWP_NOZORDER);
\r
5975 /* Center one window over another */
\r
5976 BOOL CenterWindow (HWND hwndChild, HWND hwndParent)
\r
5978 return CenterWindowEx( hwndChild, hwndParent, 0 );
\r
5981 /*---------------------------------------------------------------------------*\
\r
5983 * Startup Dialog functions
\r
5985 \*---------------------------------------------------------------------------*/
\r
5987 InitComboStrings(HANDLE hwndCombo, char **cd)
\r
5989 SendMessage(hwndCombo, CB_RESETCONTENT, 0, 0);
\r
5991 while (*cd != NULL) {
\r
5992 SendMessage(hwndCombo, CB_ADDSTRING, 0, (LPARAM) T_(*cd));
\r
5998 InitComboStringsFromOption(HANDLE hwndCombo, char *str)
\r
6000 char buf1[MAX_ARG_LEN];
\r
6003 if (str[0] == '@') {
\r
6004 FILE* f = fopen(str + 1, "r");
\r
6006 DisplayFatalError(str + 1, errno, 2);
\r
6009 len = fread(buf1, 1, sizeof(buf1)-1, f);
\r
6011 buf1[len] = NULLCHAR;
\r
6015 SendMessage(hwndCombo, CB_RESETCONTENT, 0, 0);
\r
6018 char buf[MSG_SIZ];
\r
6019 char *end = strchr(str, '\n');
\r
6020 if (end == NULL) return;
\r
6021 memcpy(buf, str, end - str);
\r
6022 buf[end - str] = NULLCHAR;
\r
6023 SendMessage(hwndCombo, CB_ADDSTRING, 0, (LPARAM) buf);
\r
6029 SetStartupDialogEnables(HWND hDlg)
\r
6031 EnableWindow(GetDlgItem(hDlg, OPT_ChessEngineName),
\r
6032 IsDlgButtonChecked(hDlg, OPT_ChessEngine) ||
\r
6033 (appData.zippyPlay && IsDlgButtonChecked(hDlg, OPT_ChessServer)));
\r
6034 EnableWindow(GetDlgItem(hDlg, OPT_SecondChessEngineName),
\r
6035 IsDlgButtonChecked(hDlg, OPT_ChessEngine));
\r
6036 EnableWindow(GetDlgItem(hDlg, OPT_ChessServerName),
\r
6037 IsDlgButtonChecked(hDlg, OPT_ChessServer));
\r
6038 EnableWindow(GetDlgItem(hDlg, OPT_AdditionalOptions),
\r
6039 IsDlgButtonChecked(hDlg, OPT_AnyAdditional));
\r
6040 EnableWindow(GetDlgItem(hDlg, IDOK),
\r
6041 IsDlgButtonChecked(hDlg, OPT_ChessEngine) ||
\r
6042 IsDlgButtonChecked(hDlg, OPT_ChessServer) ||
\r
6043 IsDlgButtonChecked(hDlg, OPT_View));
\r
6047 QuoteForFilename(char *filename)
\r
6049 int dquote, space;
\r
6050 dquote = strchr(filename, '"') != NULL;
\r
6051 space = strchr(filename, ' ') != NULL;
\r
6052 if (dquote || space) {
\r
6064 InitEngineBox(HWND hDlg, HWND hwndCombo, char* nthcp, char* nthd, char* nthdir, char *nthnames)
\r
6066 char buf[MSG_SIZ];
\r
6069 InitComboStringsFromOption(hwndCombo, nthnames);
\r
6070 q = QuoteForFilename(nthcp);
\r
6071 snprintf(buf, MSG_SIZ, "%s%s%s", q, nthcp, q);
\r
6072 if (*nthdir != NULLCHAR) {
\r
6073 q = QuoteForFilename(nthdir);
\r
6074 snprintf(buf + strlen(buf), MSG_SIZ, " /%s=%s%s%s", nthd, q, nthdir, q);
\r
6076 if (*nthcp == NULLCHAR) {
\r
6077 SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) 0, (LPARAM) 0);
\r
6078 } else if (SendMessage(hwndCombo, CB_SELECTSTRING, (WPARAM) -1, (LPARAM) buf) == CB_ERR) {
\r
6079 SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) -1, (LPARAM) 0);
\r
6080 SendMessage(hwndCombo, WM_SETTEXT, (WPARAM) 0, (LPARAM) buf);
\r
6085 StartupDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
6087 char buf[MSG_SIZ];
\r
6091 switch (message) {
\r
6092 case WM_INITDIALOG:
\r
6093 /* Center the dialog */
\r
6094 CenterWindow (hDlg, GetDesktopWindow());
\r
6095 Translate(hDlg, DLG_Startup);
\r
6096 /* Initialize the dialog items */
\r
6097 InitEngineBox(hDlg, GetDlgItem(hDlg, OPT_ChessEngineName),
\r
6098 appData.firstChessProgram, "fd", appData.firstDirectory,
\r
6099 firstChessProgramNames);
\r
6100 InitEngineBox(hDlg, GetDlgItem(hDlg, OPT_SecondChessEngineName),
\r
6101 appData.secondChessProgram, singleList ? "fd" : "sd", appData.secondDirectory,
\r
6102 singleList ? firstChessProgramNames : secondChessProgramNames); //[HGM] single: use first list in second combo
\r
6103 hwndCombo = GetDlgItem(hDlg, OPT_ChessServerName);
\r
6104 InitComboStringsFromOption(hwndCombo, icsNames);
\r
6105 snprintf(buf, MSG_SIZ, "%s /icsport=%s", appData.icsHost, appData.icsPort);
\r
6106 if (*appData.icsHelper != NULLCHAR) {
\r
6107 char *q = QuoteForFilename(appData.icsHelper);
\r
6108 sprintf(buf + strlen(buf), " /icshelper=%s%s%s", q, appData.icsHelper, q);
\r
6110 if (*appData.icsHost == NULLCHAR) {
\r
6111 SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) 0, (LPARAM) 0);
\r
6112 /*SendMessage(hwndCombo, CB_SHOWDROPDOWN, (WPARAM) TRUE, (LPARAM) 0); !!too soon */
\r
6113 } else if (SendMessage(hwndCombo, CB_SELECTSTRING, (WPARAM) -1, (LPARAM) buf) == CB_ERR) {
\r
6114 SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) -1, (LPARAM) 0);
\r
6115 SendMessage(hwndCombo, WM_SETTEXT, (WPARAM) 0, (LPARAM) buf);
\r
6118 if (appData.icsActive) {
\r
6119 CheckDlgButton(hDlg, OPT_ChessServer, BST_CHECKED);
\r
6121 else if (appData.noChessProgram) {
\r
6122 CheckDlgButton(hDlg, OPT_View, BST_CHECKED);
\r
6125 CheckDlgButton(hDlg, OPT_ChessEngine, BST_CHECKED);
\r
6128 SetStartupDialogEnables(hDlg);
\r
6132 switch (LOWORD(wParam)) {
\r
6134 if (IsDlgButtonChecked(hDlg, OPT_ChessEngine)) {
\r
6135 safeStrCpy(buf, "/fcp=", sizeof(buf)/sizeof(buf[0]) );
\r
6136 GetDlgItemText(hDlg, OPT_ChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));
\r
6138 ParseArgs(StringGet, &p);
\r
6139 safeStrCpy(buf, singleList ? "/fcp=" : "/scp=", sizeof(buf)/sizeof(buf[0]) );
\r
6140 GetDlgItemText(hDlg, OPT_SecondChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));
\r
6142 SwapEngines(singleList); // temporarily swap first and second, to load a second 'first', ...
\r
6143 ParseArgs(StringGet, &p);
\r
6144 SwapEngines(singleList); // ... and then make it 'second'
\r
6145 appData.noChessProgram = FALSE;
\r
6146 appData.icsActive = FALSE;
\r
6147 } else if (IsDlgButtonChecked(hDlg, OPT_ChessServer)) {
\r
6148 safeStrCpy(buf, "/ics /icshost=", sizeof(buf)/sizeof(buf[0]) );
\r
6149 GetDlgItemText(hDlg, OPT_ChessServerName, buf + strlen(buf), sizeof(buf) - strlen(buf));
\r
6151 ParseArgs(StringGet, &p);
\r
6152 if (appData.zippyPlay) {
\r
6153 safeStrCpy(buf, "/fcp=", sizeof(buf)/sizeof(buf[0]) );
\r
6154 GetDlgItemText(hDlg, OPT_ChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));
\r
6156 ParseArgs(StringGet, &p);
\r
6158 } else if (IsDlgButtonChecked(hDlg, OPT_View)) {
\r
6159 appData.noChessProgram = TRUE;
\r
6160 appData.icsActive = FALSE;
\r
6162 MessageBox(hDlg, _("Choose an option, or cancel to exit"),
\r
6163 _("Option Error"), MB_OK|MB_ICONEXCLAMATION);
\r
6166 if (IsDlgButtonChecked(hDlg, OPT_AnyAdditional)) {
\r
6167 GetDlgItemText(hDlg, OPT_AdditionalOptions, buf, sizeof(buf));
\r
6169 ParseArgs(StringGet, &p);
\r
6171 EndDialog(hDlg, TRUE);
\r
6178 case IDM_HELPCONTENTS:
\r
6179 if (!WinHelp (hDlg, "winboard.hlp", HELP_KEY,(DWORD)(LPSTR)"CONTENTS")) {
\r
6180 MessageBox (GetFocus(),
\r
6181 _("Unable to activate help"),
\r
6182 szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);
\r
6187 SetStartupDialogEnables(hDlg);
\r
6195 /*---------------------------------------------------------------------------*\
\r
6197 * About box dialog functions
\r
6199 \*---------------------------------------------------------------------------*/
\r
6201 /* Process messages for "About" dialog box */
\r
6203 About(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
6205 switch (message) {
\r
6206 case WM_INITDIALOG: /* message: initialize dialog box */
\r
6207 /* Center the dialog over the application window */
\r
6208 CenterWindow (hDlg, GetWindow (hDlg, GW_OWNER));
\r
6209 SetDlgItemText(hDlg, ABOUTBOX_Version, programVersion);
\r
6210 Translate(hDlg, ABOUTBOX);
\r
6214 case WM_COMMAND: /* message: received a command */
\r
6215 if (LOWORD(wParam) == IDOK /* "OK" box selected? */
\r
6216 || LOWORD(wParam) == IDCANCEL) { /* System menu close command? */
\r
6217 EndDialog(hDlg, TRUE); /* Exit the dialog */
\r
6225 /*---------------------------------------------------------------------------*\
\r
6227 * Comment Dialog functions
\r
6229 \*---------------------------------------------------------------------------*/
\r
6232 CommentDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
6234 static HANDLE hwndText = NULL;
\r
6235 int len, newSizeX, newSizeY, flags;
\r
6236 static int sizeX, sizeY;
\r
6241 switch (message) {
\r
6242 case WM_INITDIALOG: /* message: initialize dialog box */
\r
6243 /* Initialize the dialog items */
\r
6244 Translate(hDlg, DLG_EditComment);
\r
6245 hwndText = GetDlgItem(hDlg, OPT_CommentText);
\r
6246 SetDlgItemText(hDlg, OPT_CommentText, commentText);
\r
6247 EnableWindow(GetDlgItem(hDlg, OPT_CancelComment), editComment);
\r
6248 EnableWindow(GetDlgItem(hDlg, OPT_ClearComment), editComment);
\r
6249 EnableWindow(GetDlgItem(hDlg, OPT_EditComment), !editComment);
\r
6250 SendMessage(hwndText, EM_SETREADONLY, !editComment, 0);
\r
6251 SetWindowText(hDlg, commentTitle);
\r
6252 if (editComment) {
\r
6253 SetFocus(hwndText);
\r
6255 SetFocus(GetDlgItem(hDlg, IDOK));
\r
6257 SendMessage(GetDlgItem(hDlg, OPT_CommentText),
\r
6258 WM_SETFONT, (WPARAM)font[boardSize][COMMENT_FONT]->hf,
\r
6259 MAKELPARAM(FALSE, 0));
\r
6260 /* Size and position the dialog */
\r
6261 if (!commentDialog) {
\r
6262 commentDialog = hDlg;
\r
6263 flags = SWP_NOZORDER;
\r
6264 GetClientRect(hDlg, &rect);
\r
6265 sizeX = rect.right;
\r
6266 sizeY = rect.bottom;
\r
6267 if (wpComment.x != CW_USEDEFAULT && wpComment.y != CW_USEDEFAULT &&
\r
6268 wpComment.width != CW_USEDEFAULT && wpComment.height != CW_USEDEFAULT) {
\r
6269 WINDOWPLACEMENT wp;
\r
6270 EnsureOnScreen(&wpComment.x, &wpComment.y, 0, 0);
\r
6271 wp.length = sizeof(WINDOWPLACEMENT);
\r
6273 wp.showCmd = SW_SHOW;
\r
6274 wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;
\r
6275 wp.rcNormalPosition.left = wpComment.x;
\r
6276 wp.rcNormalPosition.right = wpComment.x + wpComment.width;
\r
6277 wp.rcNormalPosition.top = wpComment.y;
\r
6278 wp.rcNormalPosition.bottom = wpComment.y + wpComment.height;
\r
6279 SetWindowPlacement(hDlg, &wp);
\r
6281 GetClientRect(hDlg, &rect);
\r
6282 newSizeX = rect.right;
\r
6283 newSizeY = rect.bottom;
\r
6284 ResizeEditPlusButtons(hDlg, hwndText, sizeX, sizeY,
\r
6285 newSizeX, newSizeY);
\r
6290 SendDlgItemMessage( hDlg, OPT_CommentText, EM_SETEVENTMASK, 0, ENM_MOUSEEVENTS | ENM_KEYEVENTS );
\r
6293 case WM_COMMAND: /* message: received a command */
\r
6294 switch (LOWORD(wParam)) {
\r
6296 if (editComment) {
\r
6298 /* Read changed options from the dialog box */
\r
6299 hwndText = GetDlgItem(hDlg, OPT_CommentText);
\r
6300 len = GetWindowTextLength(hwndText);
\r
6301 str = (char *) malloc(len + 1);
\r
6302 GetWindowText(hwndText, str, len + 1);
\r
6311 ReplaceComment(commentIndex, str);
\r
6318 case OPT_CancelComment:
\r
6322 case OPT_ClearComment:
\r
6323 SetDlgItemText(hDlg, OPT_CommentText, "");
\r
6326 case OPT_EditComment:
\r
6327 EditCommentEvent();
\r
6335 case WM_NOTIFY: // [HGM] vari: cloned from whistory.c
\r
6336 if( wParam == OPT_CommentText ) {
\r
6337 MSGFILTER * lpMF = (MSGFILTER *) lParam;
\r
6339 if( lpMF->msg == WM_RBUTTONDOWN && (lpMF->wParam & (MK_CONTROL | MK_SHIFT)) == 0 ||
\r
6340 lpMF->msg == WM_CHAR && lpMF->wParam == '\022' ) {
\r
6344 pt.x = LOWORD( lpMF->lParam );
\r
6345 pt.y = HIWORD( lpMF->lParam );
\r
6347 if(lpMF->msg == WM_CHAR) {
\r
6349 SendDlgItemMessage( hDlg, OPT_CommentText, EM_EXGETSEL, 0, (LPARAM) &sel );
\r
6350 index = sel.cpMin;
\r
6352 index = SendDlgItemMessage( hDlg, OPT_CommentText, EM_CHARFROMPOS, 0, (LPARAM) &pt );
\r
6354 hwndText = GetDlgItem(hDlg, OPT_CommentText); // cloned from above
\r
6355 len = GetWindowTextLength(hwndText);
\r
6356 str = (char *) malloc(len + 1);
\r
6357 GetWindowText(hwndText, str, len + 1);
\r
6358 ReplaceComment(commentIndex, str);
\r
6359 if(commentIndex != currentMove) ToNrEvent(commentIndex);
\r
6360 LoadVariation( index, str ); // [HGM] also does the actual moving to it, now
\r
6363 /* Zap the message for good: apparently, returning non-zero is not enough */
\r
6364 lpMF->msg = WM_USER;
\r
6372 newSizeX = LOWORD(lParam);
\r
6373 newSizeY = HIWORD(lParam);
\r
6374 ResizeEditPlusButtons(hDlg, hwndText, sizeX, sizeY, newSizeX, newSizeY);
\r
6379 case WM_GETMINMAXINFO:
\r
6380 /* Prevent resizing window too small */
\r
6381 mmi = (MINMAXINFO *) lParam;
\r
6382 mmi->ptMinTrackSize.x = 100;
\r
6383 mmi->ptMinTrackSize.y = 100;
\r
6390 EitherCommentPopUp(int index, char *title, char *str, BOOLEAN edit)
\r
6395 CheckMenuItem(GetMenu(hwndMain), IDM_Comment, edit ? MF_CHECKED : MF_UNCHECKED);
\r
6397 if (str == NULL) str = "";
\r
6398 p = (char *) malloc(2 * strlen(str) + 2);
\r
6401 if (*str == '\n') *q++ = '\r';
\r
6405 if (commentText != NULL) free(commentText);
\r
6407 commentIndex = index;
\r
6408 commentTitle = title;
\r
6410 editComment = edit;
\r
6412 if (commentDialog) {
\r
6413 SendMessage(commentDialog, WM_INITDIALOG, 0, 0);
\r
6414 if (!commentUp) ShowWindow(commentDialog, SW_SHOW);
\r
6416 lpProc = MakeProcInstance((FARPROC)CommentDialog, hInst);
\r
6417 CreateDialog(hInst, MAKEINTRESOURCE(DLG_EditComment),
\r
6418 hwndMain, (DLGPROC)lpProc);
\r
6419 FreeProcInstance(lpProc);
\r
6425 /*---------------------------------------------------------------------------*\
\r
6427 * Type-in move dialog functions
\r
6429 \*---------------------------------------------------------------------------*/
\r
6432 TypeInMoveDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
6434 char move[MSG_SIZ];
\r
6437 switch (message) {
\r
6438 case WM_INITDIALOG:
\r
6439 move[0] = (char) lParam;
\r
6440 move[1] = NULLCHAR;
\r
6441 CenterWindowEx(hDlg, GetWindow(hDlg, GW_OWNER), 1 );
\r
6442 Translate(hDlg, DLG_TypeInMove);
\r
6443 hInput = GetDlgItem(hDlg, OPT_Move);
\r
6444 SetWindowText(hInput, move);
\r
6446 SendMessage(hInput, EM_SETSEL, (WPARAM)9999, (LPARAM)9999);
\r
6450 switch (LOWORD(wParam)) {
\r
6453 shiftKey = GetKeyState(VK_SHIFT) < 0; // [HGM] remember last shift status
\r
6454 GetDlgItemText(hDlg, OPT_Move, move, sizeof(move));
6455 TypeInDoneEvent(move);
\r
6456 EndDialog(hDlg, TRUE);
\r
6459 EndDialog(hDlg, FALSE);
\r
6470 PopUpMoveDialog(char firstchar)
\r
6474 lpProc = MakeProcInstance((FARPROC)TypeInMoveDialog, hInst);
\r
6475 DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_TypeInMove),
\r
6476 hwndMain, (DLGPROC)lpProc, (LPARAM)firstchar);
\r
6477 FreeProcInstance(lpProc);
\r
6480 /*---------------------------------------------------------------------------*\
\r
6482 * Type-in name dialog functions
\r
6484 \*---------------------------------------------------------------------------*/
\r
6487 TypeInNameDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
6489 char move[MSG_SIZ];
\r
6492 switch (message) {
\r
6493 case WM_INITDIALOG:
\r
6494 move[0] = (char) lParam;
\r
6495 move[1] = NULLCHAR;
\r
6496 CenterWindowEx(hDlg, GetWindow(hDlg, GW_OWNER), 1 );
\r
6497 Translate(hDlg, DLG_TypeInName);
\r
6498 hInput = GetDlgItem(hDlg, OPT_Name);
\r
6499 SetWindowText(hInput, move);
\r
6501 SendMessage(hInput, EM_SETSEL, (WPARAM)9999, (LPARAM)9999);
\r
6505 switch (LOWORD(wParam)) {
\r
6507 GetDlgItemText(hDlg, OPT_Name, move, sizeof(move));
\r
6508 appData.userName = strdup(move);
\r
6511 if(gameMode == MachinePlaysWhite || gameMode == MachinePlaysBlack) {
\r
6512 snprintf(move, MSG_SIZ, "%s vs. %s", gameInfo.white, gameInfo.black);
\r
6513 DisplayTitle(move);
\r
6517 EndDialog(hDlg, TRUE);
\r
6520 EndDialog(hDlg, FALSE);
\r
6531 PopUpNameDialog(char firstchar)
\r
6535 lpProc = MakeProcInstance((FARPROC)TypeInNameDialog, hInst);
\r
6536 DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_TypeInName),
\r
6537 hwndMain, (DLGPROC)lpProc, (LPARAM)firstchar);
\r
6538 FreeProcInstance(lpProc);
\r
6541 /*---------------------------------------------------------------------------*\
\r
6545 \*---------------------------------------------------------------------------*/
\r
6547 /* Nonmodal error box */
\r
6548 LRESULT CALLBACK ErrorDialog(HWND hDlg, UINT message,
\r
6549 WPARAM wParam, LPARAM lParam);
\r
6552 ErrorPopUp(char *title, char *content)
\r
6556 BOOLEAN modal = hwndMain == NULL;
\r
6574 strncpy(errorTitle, title, sizeof(errorTitle));
\r
6575 errorTitle[sizeof(errorTitle) - 1] = '\0';
\r
6578 MessageBox(NULL, errorMessage, errorTitle, MB_OK|MB_ICONEXCLAMATION);
\r
6580 lpProc = MakeProcInstance((FARPROC)ErrorDialog, hInst);
\r
6581 CreateDialog(hInst, MAKEINTRESOURCE(DLG_Error),
\r
6582 hwndMain, (DLGPROC)lpProc);
\r
6583 FreeProcInstance(lpProc);
\r
6590 if (!appData.popupMoveErrors && moveErrorMessageUp) DisplayMessage("", "");
\r
6591 if (errorDialog == NULL) return;
\r
6592 DestroyWindow(errorDialog);
\r
6593 errorDialog = NULL;
\r
6594 if(errorExitStatus) ExitEvent(errorExitStatus);
\r
6598 ErrorDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
6603 switch (message) {
\r
6604 case WM_INITDIALOG:
\r
6605 GetWindowRect(hDlg, &rChild);
\r
6608 SetWindowPos(hDlg, NULL, rChild.left,
\r
6609 rChild.top + boardRect.top - (rChild.bottom - rChild.top),
\r
6610 0, 0, SWP_NOZORDER|SWP_NOSIZE);
\r
6614 [AS] It seems that the above code wants to move the dialog up in the "caption
\r
6615 area" of the main window, but it uses the dialog height as an hard-coded constant,
\r
6616 and it doesn't work when you resize the dialog.
\r
6617 For now, just give it a default position.
\r
6619 SetWindowPos(hDlg, NULL, boardRect.left+8, boardRect.top+8, 0, 0, SWP_NOZORDER|SWP_NOSIZE);
\r
6620 Translate(hDlg, DLG_Error);
\r
6622 errorDialog = hDlg;
\r
6623 SetWindowText(hDlg, errorTitle);
\r
6624 hwndText = GetDlgItem(hDlg, OPT_ErrorText);
\r
6625 SetDlgItemText(hDlg, OPT_ErrorText, errorMessage);
\r
6629 switch (LOWORD(wParam)) {
\r
6632 if (errorDialog == hDlg) errorDialog = NULL;
\r
6633 DestroyWindow(hDlg);
\r
6645 HWND gothicDialog = NULL;
\r
6648 GothicDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
6652 int height = GetSystemMetrics(SM_CYCAPTION)+GetSystemMetrics(SM_CYFRAME);
\r
6654 switch (message) {
\r
6655 case WM_INITDIALOG:
\r
6656 GetWindowRect(hDlg, &rChild);
\r
6658 SetWindowPos(hDlg, NULL, wpMain.x, wpMain.y-height, wpMain.width, height,
\r
6662 [AS] It seems that the above code wants to move the dialog up in the "caption
\r
6663 area" of the main window, but it uses the dialog height as an hard-coded constant,
\r
6664 and it doesn't work when you resize the dialog.
\r
6665 For now, just give it a default position.
\r
6667 gothicDialog = hDlg;
\r
6668 SetWindowText(hDlg, errorTitle);
\r
6669 hwndText = GetDlgItem(hDlg, OPT_ErrorText);
\r
6670 SetDlgItemText(hDlg, OPT_ErrorText, errorMessage);
\r
6674 switch (LOWORD(wParam)) {
\r
6677 if (errorDialog == hDlg) errorDialog = NULL;
\r
6678 DestroyWindow(hDlg);
\r
6690 GothicPopUp(char *title, VariantClass variant)
\r
6693 static char *lastTitle;
\r
6695 strncpy(errorTitle, title, sizeof(errorTitle));
\r
6696 errorTitle[sizeof(errorTitle) - 1] = '\0';
\r
6698 if(lastTitle != title && gothicDialog != NULL) {
\r
6699 DestroyWindow(gothicDialog);
\r
6700 gothicDialog = NULL;
\r
6702 if(variant != VariantNormal && gothicDialog == NULL) {
\r
6703 title = lastTitle;
\r
6704 lpProc = MakeProcInstance((FARPROC)GothicDialog, hInst);
\r
6705 CreateDialog(hInst, MAKEINTRESOURCE(DLG_Error),
\r
6706 hwndMain, (DLGPROC)lpProc);
\r
6707 FreeProcInstance(lpProc);
\r
6712 /*---------------------------------------------------------------------------*\
\r
6714 * Ics Interaction console functions
\r
6716 \*---------------------------------------------------------------------------*/
\r
6718 #define HISTORY_SIZE 64
\r
6719 static char *history[HISTORY_SIZE];
\r
6720 int histIn = 0, histP = 0;
\r
6723 SaveInHistory(char *cmd)
\r
6725 if (history[histIn] != NULL) {
\r
6726 free(history[histIn]);
\r
6727 history[histIn] = NULL;
\r
6729 if (*cmd == NULLCHAR) return;
\r
6730 history[histIn] = StrSave(cmd);
\r
6731 histIn = (histIn + 1) % HISTORY_SIZE;
\r
6732 if (history[histIn] != NULL) {
\r
6733 free(history[histIn]);
\r
6734 history[histIn] = NULL;
\r
6740 PrevInHistory(char *cmd)
\r
6743 if (histP == histIn) {
\r
6744 if (history[histIn] != NULL) free(history[histIn]);
\r
6745 history[histIn] = StrSave(cmd);
\r
6747 newhp = (histP - 1 + HISTORY_SIZE) % HISTORY_SIZE;
\r
6748 if (newhp == histIn || history[newhp] == NULL) return NULL;
\r
6750 return history[histP];
\r
6756 if (histP == histIn) return NULL;
\r
6757 histP = (histP + 1) % HISTORY_SIZE;
\r
6758 return history[histP];
\r
6762 LoadIcsTextMenu(IcsTextMenuEntry *e)
\r
6766 hmenu = LoadMenu(hInst, "TextMenu");
\r
6767 h = GetSubMenu(hmenu, 0);
\r
6769 if (strcmp(e->item, "-") == 0) {
\r
6770 AppendMenu(h, MF_SEPARATOR, 0, 0);
\r
6771 } else { // [HGM] re-written a bit to use only one AppendMenu call for both cases (| or no |)
\r
6772 int flags = MF_STRING, j = 0;
\r
6773 if (e->item[0] == '|') {
\r
6774 flags |= MF_MENUBARBREAK;
\r
6777 if(!strcmp(e->command, "none")) flags |= MF_GRAYED; // [HGM] chatclick: provide inactive dummy
\r
6778 AppendMenu(h, flags, IDM_CommandX + i, e->item + j);
\r
6786 WNDPROC consoleTextWindowProc;
\r
6789 CommandX(HWND hwnd, char *command, BOOLEAN getname, BOOLEAN immediate)
\r
6791 char buf[MSG_SIZ], name[MSG_SIZ];
\r
6792 HWND hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
6796 SetWindowText(hInput, command);
\r
6798 SendMessage(hInput, WM_CHAR, '\r', 0);
\r
6800 sel.cpMin = 999999;
\r
6801 sel.cpMax = 999999;
\r
6802 SendMessage(hInput, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
6807 SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);
\r
6808 if (sel.cpMin == sel.cpMax) {
\r
6809 /* Expand to surrounding word */
\r
6812 tr.chrg.cpMax = sel.cpMin;
\r
6813 tr.chrg.cpMin = --sel.cpMin;
\r
6814 if (sel.cpMin < 0) break;
\r
6815 tr.lpstrText = name;
\r
6816 SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr);
\r
6817 } while (isalpha(name[0]) || isdigit(name[0]) || name[0] == '-');
\r
6821 tr.chrg.cpMin = sel.cpMax;
\r
6822 tr.chrg.cpMax = ++sel.cpMax;
\r
6823 tr.lpstrText = name;
\r
6824 if (SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr) < 1) break;
\r
6825 } while (isalpha(name[0]) || isdigit(name[0]) || name[0] == '-');
\r
6828 if (sel.cpMax == sel.cpMin || sel.cpMax - sel.cpMin > MSG_SIZ/2) {
\r
6829 MessageBeep(MB_ICONEXCLAMATION);
\r
6833 tr.lpstrText = name;
\r
6834 SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr);
\r
6836 if (sel.cpMax - sel.cpMin > MSG_SIZ/2) {
\r
6837 MessageBeep(MB_ICONEXCLAMATION);
\r
6840 SendMessage(hwnd, EM_GETSELTEXT, 0, (LPARAM) name);
\r
6843 if(strstr(command, "%s")) snprintf(buf, MSG_SIZ, command, name); else
\r
6844 snprintf(buf, MSG_SIZ, "%s %s", command, name);
\r
6845 SetWindowText(hInput, buf);
\r
6846 SendMessage(hInput, WM_CHAR, '\r', 0);
\r
6848 if(!strcmp(command, "chat")) { ChatPopUp(name); return; }
\r
6849 snprintf(buf, MSG_SIZ, "%s %s ", command, name); /* trailing space */
\r
6850 SetWindowText(hInput, buf);
\r
6851 sel.cpMin = 999999;
\r
6852 sel.cpMax = 999999;
\r
6853 SendMessage(hInput, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
6859 ConsoleTextSubclass(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
\r
6864 switch (message) {
\r
6866 if (!(GetKeyState(VK_CONTROL) & ~1)) break;
\r
6867 if(wParam=='R') return 0;
\r
6870 SendMessage(hwnd, EM_LINESCROLL, 0, -999999);
\r
6873 sel.cpMin = 999999;
\r
6874 sel.cpMax = 999999;
\r
6875 SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
6876 SendMessage(hwnd, EM_SCROLLCARET, 0, 0);
\r
6881 if(wParam != '\022') {
\r
6882 if (wParam == '\t') {
\r
6883 if (GetKeyState(VK_SHIFT) < 0) {
\r
6885 if (IsIconic(hwndMain)) ShowWindow(hwndMain, SW_RESTORE);
\r
6886 if (buttonDesc[0].hwnd) {
\r
6887 SetFocus(buttonDesc[0].hwnd);
\r
6889 SetFocus(hwndMain);
\r
6893 SetFocus(GetDlgItem(hwndConsole, OPT_ConsoleInput));
\r
6896 hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
6897 JAWS_DELETE( SetFocus(hInput); )
\r
6898 SendMessage(hInput, message, wParam, lParam);
\r
6901 } // [HGM] navigate: for Ctrl+R, flow into next case (moved up here) to summon up menu
\r
6903 case WM_RBUTTONDOWN:
\r
6904 if (!(GetKeyState(VK_SHIFT) & ~1)) {
\r
6905 /* Move selection here if it was empty */
\r
6907 pt.x = LOWORD(lParam);
\r
6908 pt.y = HIWORD(lParam);
\r
6909 SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);
\r
6910 if (sel.cpMin == sel.cpMax) {
\r
6911 if(lParam != -1) sel.cpMin = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&pt); /*doc is wrong*/
\r
6912 sel.cpMax = sel.cpMin;
\r
6913 SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
6915 SendMessage(hwnd, EM_HIDESELECTION, FALSE, FALSE);
\r
6916 { // [HGM] chatclick: code moved here from WM_RBUTTONUP case, to have menu appear on down-click
\r
6918 HMENU hmenu = LoadIcsTextMenu(icsTextMenuEntry);
\r
6919 SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);
\r
6920 if (sel.cpMin == sel.cpMax) {
\r
6921 EnableMenuItem(hmenu, IDM_Copy, MF_BYCOMMAND|MF_GRAYED);
\r
6922 EnableMenuItem(hmenu, IDM_QuickPaste, MF_BYCOMMAND|MF_GRAYED);
\r
6924 if (!IsClipboardFormatAvailable(CF_TEXT)) {
\r
6925 EnableMenuItem(hmenu, IDM_Paste, MF_BYCOMMAND|MF_GRAYED);
\r
6927 pt.x = LOWORD(lParam)-30; // [HGM] chatclick: make menu pop up with pointer above upper-right item
\r
6928 pt.y = HIWORD(lParam)-10; // make it appear as if mouse moved there, so it will be selected on up-click
\r
6929 PostMessage(hwnd, WM_MOUSEMOVE, wParam, lParam+5);
\r
6930 MenuPopup(hwnd, pt, hmenu, -1);
\r
6934 case WM_RBUTTONUP:
\r
6935 if (GetKeyState(VK_SHIFT) & ~1) {
\r
6936 SendDlgItemMessage(hwndConsole, OPT_ConsoleText,
\r
6937 WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);
\r
6941 hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
6943 return SendMessage(hInput, message, wParam, lParam);
\r
6944 case WM_MBUTTONDOWN:
\r
6945 return SendMessage(hwnd, WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);
\r
6947 switch (LOWORD(wParam)) {
\r
6948 case IDM_QuickPaste:
\r
6950 SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);
\r
6951 if (sel.cpMin == sel.cpMax) {
\r
6952 MessageBeep(MB_ICONEXCLAMATION);
\r
6955 SendMessage(hwnd, WM_COPY, 0, 0);
\r
6956 hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
6957 SendMessage(hInput, WM_PASTE, 0, 0);
\r
6962 SendMessage(hwnd, WM_CUT, 0, 0);
\r
6965 SendMessage(hwnd, WM_PASTE, 0, 0);
\r
6968 SendMessage(hwnd, WM_COPY, 0, 0);
\r
6972 int i = LOWORD(wParam) - IDM_CommandX;
\r
6973 if (i >= 0 && i < ICS_TEXT_MENU_SIZE &&
\r
6974 icsTextMenuEntry[i].command != NULL) {
\r
6975 CommandX(hwnd, icsTextMenuEntry[i].command,
\r
6976 icsTextMenuEntry[i].getname,
\r
6977 icsTextMenuEntry[i].immediate);
\r
6985 return (*consoleTextWindowProc)(hwnd, message, wParam, lParam);
\r
6988 WNDPROC consoleInputWindowProc;
\r
6991 ConsoleInputSubclass(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
\r
6993 char buf[MSG_SIZ];
\r
6995 static BOOL sendNextChar = FALSE;
\r
6996 static BOOL quoteNextChar = FALSE;
\r
6997 InputSource *is = consoleInputSource;
\r
7001 switch (message) {
\r
7003 if (!appData.localLineEditing || sendNextChar) {
\r
7004 is->buf[0] = (CHAR) wParam;
\r
7006 SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);
\r
7007 sendNextChar = FALSE;
\r
7010 if (quoteNextChar) {
\r
7011 buf[0] = (char) wParam;
\r
7012 buf[1] = NULLCHAR;
\r
7013 SendMessage(hwnd, EM_REPLACESEL, TRUE, (LPARAM) buf);
\r
7014 quoteNextChar = FALSE;
\r
7018 case '\r': /* Enter key */
\r
7019 is->count = GetWindowText(hwnd, is->buf, INPUT_SOURCE_BUF_SIZE-1);
\r
7020 if (consoleEcho) SaveInHistory(is->buf);
\r
7021 is->buf[is->count++] = '\n';
\r
7022 is->buf[is->count] = NULLCHAR;
\r
7023 SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);
\r
7024 if (consoleEcho) {
\r
7025 ConsoleOutput(is->buf, is->count, TRUE);
\r
7026 } else if (appData.localLineEditing) {
\r
7027 ConsoleOutput("\n", 1, TRUE);
\r
7030 case '\033': /* Escape key */
\r
7031 SetWindowText(hwnd, "");
\r
7032 cf.cbSize = sizeof(CHARFORMAT);
\r
7033 cf.dwMask = CFM_COLOR|CFM_BOLD|CFM_ITALIC|CFM_UNDERLINE|CFM_STRIKEOUT;
\r
7034 if (consoleEcho) {
\r
7035 cf.crTextColor = textAttribs[ColorNormal].color;
\r
7037 cf.crTextColor = COLOR_ECHOOFF;
\r
7039 cf.dwEffects = textAttribs[ColorNormal].effects;
\r
7040 SendMessage(hwnd, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf);
\r
7042 case '\t': /* Tab key */
\r
7043 if (GetKeyState(VK_SHIFT) < 0) {
\r
7045 SetFocus(GetDlgItem(hwndConsole, OPT_ConsoleText));
\r
7048 if (IsIconic(hwndMain)) ShowWindow(hwndMain, SW_RESTORE);
\r
7049 if (buttonDesc[0].hwnd) {
\r
7050 SetFocus(buttonDesc[0].hwnd);
\r
7052 SetFocus(hwndMain);
\r
7056 case '\023': /* Ctrl+S */
\r
7057 sendNextChar = TRUE;
\r
7059 case '\021': /* Ctrl+Q */
\r
7060 quoteNextChar = TRUE;
\r
7070 GetWindowText(hwnd, buf, MSG_SIZ);
\r
7071 p = PrevInHistory(buf);
\r
7073 SetWindowText(hwnd, p);
\r
7074 sel.cpMin = 999999;
\r
7075 sel.cpMax = 999999;
\r
7076 SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
7081 p = NextInHistory();
\r
7083 SetWindowText(hwnd, p);
\r
7084 sel.cpMin = 999999;
\r
7085 sel.cpMax = 999999;
\r
7086 SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
7092 if (!(GetKeyState(VK_CONTROL) & ~1)) break;
\r
7096 SendDlgItemMessage(hwndConsole, OPT_ConsoleText, message, wParam, lParam);
\r
7100 case WM_MBUTTONDOWN:
\r
7101 SendDlgItemMessage(hwndConsole, OPT_ConsoleText,
\r
7102 WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);
\r
7104 case WM_RBUTTONUP:
\r
7105 if (GetKeyState(VK_SHIFT) & ~1) {
\r
7106 SendDlgItemMessage(hwndConsole, OPT_ConsoleText,
\r
7107 WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);
\r
7111 hmenu = LoadMenu(hInst, "InputMenu");
\r
7112 SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);
\r
7113 if (sel.cpMin == sel.cpMax) {
\r
7114 EnableMenuItem(hmenu, IDM_Copy, MF_BYCOMMAND|MF_GRAYED);
\r
7115 EnableMenuItem(hmenu, IDM_Cut, MF_BYCOMMAND|MF_GRAYED);
\r
7117 if (!IsClipboardFormatAvailable(CF_TEXT)) {
\r
7118 EnableMenuItem(hmenu, IDM_Paste, MF_BYCOMMAND|MF_GRAYED);
\r
7120 pt.x = LOWORD(lParam);
\r
7121 pt.y = HIWORD(lParam);
\r
7122 MenuPopup(hwnd, pt, hmenu, -1);
\r
7126 switch (LOWORD(wParam)) {
\r
7128 SendMessage(hwnd, EM_UNDO, 0, 0);
\r
7130 case IDM_SelectAll:
\r
7132 sel.cpMax = -1; /*999999?*/
\r
7133 SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
7136 SendMessage(hwnd, WM_CUT, 0, 0);
\r
7139 SendMessage(hwnd, WM_PASTE, 0, 0);
\r
7142 SendMessage(hwnd, WM_COPY, 0, 0);
\r
7147 return (*consoleInputWindowProc)(hwnd, message, wParam, lParam);
\r
7150 #define CO_MAX 100000
\r
7151 #define CO_TRIM 1000
\r
7154 ConsoleWndProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
7156 static SnapData sd;
\r
7157 HWND hText, hInput;
\r
7159 static int sizeX, sizeY;
\r
7160 int newSizeX, newSizeY;
\r
7164 hText = GetDlgItem(hDlg, OPT_ConsoleText);
\r
7165 hInput = GetDlgItem(hDlg, OPT_ConsoleInput);
\r
7167 switch (message) {
\r
7169 if (((NMHDR*)lParam)->code == EN_LINK)
\r
7171 ENLINK *pLink = (ENLINK*)lParam;
\r
7172 if (pLink->msg == WM_LBUTTONUP)
\r
7176 tr.chrg = pLink->chrg;
\r
7177 tr.lpstrText = malloc(1+tr.chrg.cpMax-tr.chrg.cpMin);
\r
7178 SendMessage(hText, EM_GETTEXTRANGE, 0, (LPARAM)&tr);
\r
7179 ShellExecute(NULL, "open", tr.lpstrText, NULL, NULL, SW_SHOW);
\r
7180 free(tr.lpstrText);
\r
7184 case WM_INITDIALOG: /* message: initialize dialog box */
\r
7185 hwndConsole = hDlg;
\r
7187 consoleTextWindowProc = (WNDPROC)
\r
7188 SetWindowLongPtr(hText, GWLP_WNDPROC, (LONG_PTR) ConsoleTextSubclass);
\r
7189 SendMessage(hText, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);
\r
7190 consoleInputWindowProc = (WNDPROC)
\r
7191 SetWindowLongPtr(hInput, GWLP_WNDPROC, (LONG_PTR) ConsoleInputSubclass);
\r
7192 SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);
\r
7193 Colorize(ColorNormal, TRUE);
\r
7194 SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &consoleCF);
\r
7195 ChangedConsoleFont();
\r
7196 GetClientRect(hDlg, &rect);
\r
7197 sizeX = rect.right;
\r
7198 sizeY = rect.bottom;
\r
7199 if (wpConsole.x != CW_USEDEFAULT && wpConsole.y != CW_USEDEFAULT &&
\r
7200 wpConsole.width != CW_USEDEFAULT && wpConsole.height != CW_USEDEFAULT) {
\r
7201 WINDOWPLACEMENT wp;
\r
7202 EnsureOnScreen(&wpConsole.x, &wpConsole.y, 0, 0);
\r
7203 wp.length = sizeof(WINDOWPLACEMENT);
\r
7205 wp.showCmd = SW_SHOW;
\r
7206 wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;
\r
7207 wp.rcNormalPosition.left = wpConsole.x;
\r
7208 wp.rcNormalPosition.right = wpConsole.x + wpConsole.width;
\r
7209 wp.rcNormalPosition.top = wpConsole.y;
\r
7210 wp.rcNormalPosition.bottom = wpConsole.y + wpConsole.height;
\r
7211 SetWindowPlacement(hDlg, &wp);
\r
7214 // [HGM] Chessknight's change 2004-07-13
\r
7215 else { /* Determine Defaults */
\r
7216 WINDOWPLACEMENT wp;
\r
7217 wpConsole.x = wpMain.width + 1;
\r
7218 wpConsole.y = wpMain.y;
\r
7219 wpConsole.width = screenWidth - wpMain.width;
\r
7220 wpConsole.height = wpMain.height;
\r
7221 EnsureOnScreen(&wpConsole.x, &wpConsole.y, 0, 0);
\r
7222 wp.length = sizeof(WINDOWPLACEMENT);
\r
7224 wp.showCmd = SW_SHOW;
\r
7225 wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;
\r
7226 wp.rcNormalPosition.left = wpConsole.x;
\r
7227 wp.rcNormalPosition.right = wpConsole.x + wpConsole.width;
\r
7228 wp.rcNormalPosition.top = wpConsole.y;
\r
7229 wp.rcNormalPosition.bottom = wpConsole.y + wpConsole.height;
\r
7230 SetWindowPlacement(hDlg, &wp);
\r
7233 // Allow hText to highlight URLs and send notifications on them
\r
7234 wMask = (WORD) SendMessage(hText, EM_GETEVENTMASK, 0, 0L);
\r
7235 SendMessage(hText, EM_SETEVENTMASK, 0, wMask | ENM_LINK);
\r
7236 SendMessage(hText, EM_AUTOURLDETECT, TRUE, 0L);
\r
7237 SetWindowLongPtr(hText, GWLP_USERDATA, 79); // initialize the text window's width
\r
7251 if (IsIconic(hDlg)) break;
\r
7252 newSizeX = LOWORD(lParam);
\r
7253 newSizeY = HIWORD(lParam);
\r
7254 if (sizeX != newSizeX || sizeY != newSizeY) {
\r
7255 RECT rectText, rectInput;
\r
7257 int newTextHeight, newTextWidth;
\r
7258 GetWindowRect(hText, &rectText);
\r
7259 newTextWidth = rectText.right - rectText.left + newSizeX - sizeX;
\r
7260 newTextHeight = rectText.bottom - rectText.top + newSizeY - sizeY;
\r
7261 if (newTextHeight < 0) {
\r
7262 newSizeY += -newTextHeight;
\r
7263 newTextHeight = 0;
\r
7265 SetWindowPos(hText, NULL, 0, 0,
\r
7266 newTextWidth, newTextHeight, SWP_NOZORDER|SWP_NOMOVE);
\r
7267 GetWindowRect(hInput, &rectInput); /* gives screen coords */
\r
7268 pt.x = rectInput.left;
\r
7269 pt.y = rectInput.top + newSizeY - sizeY;
\r
7270 ScreenToClient(hDlg, &pt);
\r
7271 SetWindowPos(hInput, NULL,
\r
7272 pt.x, pt.y, /* needs client coords */
\r
7273 rectInput.right - rectInput.left + newSizeX - sizeX,
\r
7274 rectInput.bottom - rectInput.top, SWP_NOZORDER);
\r
7280 case WM_GETMINMAXINFO:
\r
7281 /* Prevent resizing window too small */
\r
7282 mmi = (MINMAXINFO *) lParam;
\r
7283 mmi->ptMinTrackSize.x = 100;
\r
7284 mmi->ptMinTrackSize.y = 100;
\r
7287 /* [AS] Snapping */
\r
7288 case WM_ENTERSIZEMOVE:
\r
7289 return OnEnterSizeMove( &sd, hDlg, wParam, lParam );
\r
7292 return OnSizing( &sd, hDlg, wParam, lParam );
\r
7295 return OnMoving( &sd, hDlg, wParam, lParam );
\r
7297 case WM_EXITSIZEMOVE:
\r
7298 UpdateICSWidth(hText);
\r
7299 return OnExitSizeMove( &sd, hDlg, wParam, lParam );
\r
7302 return DefWindowProc(hDlg, message, wParam, lParam);
\r
7310 if (hwndConsole) return;
\r
7311 hCons = CreateDialog(hInst, szConsoleName, 0, NULL);
\r
7312 SendMessage(hCons, WM_INITDIALOG, 0, 0);
\r
7317 ConsoleOutput(char* data, int length, int forceVisible)
\r
7322 char buf[CO_MAX+1];
\r
7325 static int delayLF = 0;
\r
7326 CHARRANGE savesel, sel;
\r
7328 if (hwndConsole == NULL || length > CO_MAX-100 || length == 0) return;
\r
7336 while (length--) {
\r
7344 } else if (*p == '\007') {
\r
7345 MyPlaySound(&sounds[(int)SoundBell]);
\r
7352 hText = GetDlgItem(hwndConsole, OPT_ConsoleText);
\r
7353 SendMessage(hText, EM_HIDESELECTION, TRUE, FALSE);
\r
7354 /* Save current selection */
\r
7355 SendMessage(hText, EM_EXGETSEL, 0, (LPARAM)&savesel);
\r
7356 exlen = GetWindowTextLength(hText);
\r
7357 /* Find out whether current end of text is visible */
\r
7358 SendMessage(hText, EM_GETRECT, 0, (LPARAM) &rect);
\r
7359 SendMessage(hText, EM_POSFROMCHAR, (WPARAM) &pEnd, exlen);
\r
7360 /* Trim existing text if it's too long */
\r
7361 if (exlen + (q - buf) > CO_MAX) {
\r
7362 trim = (CO_TRIM > (q - buf)) ? CO_TRIM : (q - buf);
\r
7365 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
7366 SendMessage(hText, EM_REPLACESEL, 0, (LPARAM)"");
\r
7368 savesel.cpMin -= trim;
\r
7369 savesel.cpMax -= trim;
\r
7370 if (exlen < 0) exlen = 0;
\r
7371 if (savesel.cpMin < 0) savesel.cpMin = 0;
\r
7372 if (savesel.cpMax < savesel.cpMin) savesel.cpMax = savesel.cpMin;
\r
7374 /* Append the new text */
\r
7375 sel.cpMin = exlen;
\r
7376 sel.cpMax = exlen;
\r
7377 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
7378 SendMessage(hText, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&consoleCF);
\r
7379 SendMessage(hText, EM_REPLACESEL, 0, (LPARAM) buf);
\r
7380 if (forceVisible || exlen == 0 ||
\r
7381 (rect.left <= pEnd.x && pEnd.x < rect.right &&
\r
7382 rect.top <= pEnd.y && pEnd.y < rect.bottom)) {
\r
7383 /* Scroll to make new end of text visible if old end of text
\r
7384 was visible or new text is an echo of user typein */
\r
7385 sel.cpMin = 9999999;
\r
7386 sel.cpMax = 9999999;
\r
7387 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
7388 SendMessage(hText, EM_HIDESELECTION, FALSE, FALSE);
\r
7389 SendMessage(hText, EM_SCROLLCARET, 0, 0);
\r
7390 SendMessage(hText, EM_HIDESELECTION, TRUE, FALSE);
\r
7392 if (savesel.cpMax == exlen || forceVisible) {
\r
7393 /* Move insert point to new end of text if it was at the old
\r
7394 end of text or if the new text is an echo of user typein */
\r
7395 sel.cpMin = 9999999;
\r
7396 sel.cpMax = 9999999;
\r
7397 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
7399 /* Restore previous selection */
\r
7400 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&savesel);
\r
7402 SendMessage(hText, EM_HIDESELECTION, FALSE, FALSE);
\r
7409 DisplayHoldingsCount(HDC hdc, int x, int y, int rightAlign, int copyNumber)
\r
7413 COLORREF oldFg, oldBg;
\r
7418 snprintf(buf, sizeof(buf)/sizeof(buf[0]),"%d", copyNumber); else buf[0] = 0;
\r
7420 oldFg = SetTextColor(hdc, RGB(255, 255, 255)); /* white */
\r
7421 oldBg = SetBkColor(hdc, RGB(0, 0, 0)); /* black */
\r
7422 oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);
\r
7425 rect.right = x + squareSize;
\r
7427 rect.bottom = y + squareSize;
\r
7430 ExtTextOut(hdc, x + MESSAGE_LINE_LEFTMARGIN
\r
7431 + (rightAlign ? (squareSize*2)/3 : 0),
\r
7432 y, ETO_CLIPPED|ETO_OPAQUE,
\r
7433 &rect, str, strlen(str), NULL);
\r
7435 (void) SetTextColor(hdc, oldFg);
\r
7436 (void) SetBkColor(hdc, oldBg);
\r
7437 (void) SelectObject(hdc, oldFont);
\r
7441 DisplayAClock(HDC hdc, int timeRemaining, int highlight,
\r
7442 RECT *rect, char *color, char *flagFell)
\r
7446 COLORREF oldFg, oldBg;
\r
7449 if (appData.clockMode) {
\r
7451 snprintf(buf, sizeof(buf)/sizeof(buf[0]), "%c %s %s", color[0], TimeString(timeRemaining), flagFell);
\r
7453 snprintf(buf, sizeof(buf)/sizeof(buf[0]), "%s:%c%s %s", color, (logoHeight>0 ? 0 : ' '), TimeString(timeRemaining), flagFell);
\r
7460 oldFg = SetTextColor(hdc, RGB(255, 255, 255)); /* white */
\r
7461 oldBg = SetBkColor(hdc, RGB(0, 0, 0)); /* black */
\r
7463 oldFg = SetTextColor(hdc, RGB(0, 0, 0)); /* black */
\r
7464 oldBg = SetBkColor(hdc, RGB(255, 255, 255)); /* white */
\r
7466 oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);
\r
7470 ExtTextOut(hdc, rect->left + MESSAGE_LINE_LEFTMARGIN,
\r
7471 rect->top, ETO_CLIPPED|ETO_OPAQUE,
\r
7472 rect, str, strlen(str), NULL);
\r
7473 if(logoHeight > 0 && appData.clockMode) {
\r
7475 str += strlen(color)+2;
\r
7476 r.top = rect->top + logoHeight/2;
\r
7477 r.left = rect->left;
\r
7478 r.right = rect->right;
\r
7479 r.bottom = rect->bottom;
\r
7480 ExtTextOut(hdc, rect->left + MESSAGE_LINE_LEFTMARGIN,
\r
7481 r.top, ETO_CLIPPED|ETO_OPAQUE,
\r
7482 &r, str, strlen(str), NULL);
\r
7484 (void) SetTextColor(hdc, oldFg);
\r
7485 (void) SetBkColor(hdc, oldBg);
\r
7486 (void) SelectObject(hdc, oldFont);
\r
7491 DoReadFile(HANDLE hFile, char *buf, int count, DWORD *outCount,
\r
7497 if( count <= 0 ) {
\r
7498 if (appData.debugMode) {
\r
7499 fprintf( debugFP, "DoReadFile: trying to read past end of buffer, overflow = %d\n", count );
\r
7502 return ERROR_INVALID_USER_BUFFER;
\r
7505 ResetEvent(ovl->hEvent);
\r
7506 ovl->Offset = ovl->OffsetHigh = 0;
\r
7507 ok = ReadFile(hFile, buf, count, outCount, ovl);
\r
7511 err = GetLastError();
\r
7512 if (err == ERROR_IO_PENDING) {
\r
7513 ok = GetOverlappedResult(hFile, ovl, outCount, TRUE);
\r
7517 err = GetLastError();
\r
7524 DoWriteFile(HANDLE hFile, char *buf, int count, DWORD *outCount,
\r
7529 ResetEvent(ovl->hEvent);
\r
7530 ovl->Offset = ovl->OffsetHigh = 0;
\r
7531 ok = WriteFile(hFile, buf, count, outCount, ovl);
\r
7535 err = GetLastError();
\r
7536 if (err == ERROR_IO_PENDING) {
\r
7537 ok = GetOverlappedResult(hFile, ovl, outCount, TRUE);
\r
7541 err = GetLastError();
\r
7547 /* [AS] If input is line by line and a line exceed the buffer size, force an error */
\r
7548 void CheckForInputBufferFull( InputSource * is )
\r
7550 if( is->lineByLine && (is->next - is->buf) >= INPUT_SOURCE_BUF_SIZE ) {
\r
7551 /* Look for end of line */
\r
7552 char * p = is->buf;
\r
7554 while( p < is->next && *p != '\n' ) {
\r
7558 if( p >= is->next ) {
\r
7559 if (appData.debugMode) {
\r
7560 fprintf( debugFP, "Input line exceeded buffer size (source id=%lu)\n", is->id );
\r
7563 is->error = ERROR_BROKEN_PIPE; /* [AS] Just any non-successful code! */
\r
7564 is->count = (DWORD) -1;
\r
7565 is->next = is->buf;
\r
7571 InputThread(LPVOID arg)
\r
7576 is = (InputSource *) arg;
\r
7577 ovl.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
\r
7578 ovl.Internal = ovl.InternalHigh = ovl.Offset = ovl.OffsetHigh = 0;
\r
7579 while (is->hThread != NULL) {
\r
7580 is->error = DoReadFile(is->hFile, is->next,
\r
7581 INPUT_SOURCE_BUF_SIZE - (is->next - is->buf),
\r
7582 &is->count, &ovl);
\r
7583 if (is->error == NO_ERROR) {
\r
7584 is->next += is->count;
\r
7586 if (is->error == ERROR_BROKEN_PIPE) {
\r
7587 /* Correct for MS brain damage. EOF reading a pipe is not an error. */
\r
7590 is->count = (DWORD) -1;
\r
7591 /* [AS] The (is->count <= 0) check below is not useful for unsigned values! */
\r
7596 CheckForInputBufferFull( is );
\r
7598 SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);
\r
7600 if( is->count == ((DWORD) -1) ) break; /* [AS] */
\r
7602 if (is->count <= 0) break; /* Quit on EOF or error */
\r
7605 CloseHandle(ovl.hEvent);
\r
7606 CloseHandle(is->hFile);
\r
7608 if (appData.debugMode) {
\r
7609 fprintf( debugFP, "Input thread terminated (id=%lu, error=%d, count=%ld)\n", is->id, is->error, is->count );
\r
7616 /* Windows 95 beta 2 won't let you do overlapped i/o on a console or pipe */
\r
7618 NonOvlInputThread(LPVOID arg)
\r
7625 is = (InputSource *) arg;
\r
7626 while (is->hThread != NULL) {
\r
7627 is->error = ReadFile(is->hFile, is->next,
\r
7628 INPUT_SOURCE_BUF_SIZE - (is->next - is->buf),
\r
7629 &is->count, NULL) ? NO_ERROR : GetLastError();
\r
7630 if (is->error == NO_ERROR) {
\r
7631 /* Change CRLF to LF */
\r
7632 if (is->next > is->buf) {
\r
7634 i = is->count + 1;
\r
7642 if (prev == '\r' && *p == '\n') {
\r
7654 if (is->error == ERROR_BROKEN_PIPE) {
\r
7655 /* Correct for MS brain damage. EOF reading a pipe is not an error. */
\r
7658 is->count = (DWORD) -1;
\r
7662 CheckForInputBufferFull( is );
\r
7664 SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);
\r
7666 if( is->count == ((DWORD) -1) ) break; /* [AS] */
\r
7668 if (is->count < 0) break; /* Quit on error */
\r
7670 CloseHandle(is->hFile);
\r
7675 SocketInputThread(LPVOID arg)
\r
7679 is = (InputSource *) arg;
\r
7680 while (is->hThread != NULL) {
\r
7681 is->count = recv(is->sock, is->buf, INPUT_SOURCE_BUF_SIZE, 0);
\r
7682 if ((int)is->count == SOCKET_ERROR) {
\r
7683 is->count = (DWORD) -1;
\r
7684 is->error = WSAGetLastError();
\r
7686 is->error = NO_ERROR;
\r
7687 is->next += is->count;
\r
7688 if (is->count == 0 && is->second == is) {
\r
7689 /* End of file on stderr; quit with no message */
\r
7693 SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);
\r
7695 if( is->count == ((DWORD) -1) ) break; /* [AS] */
\r
7697 if (is->count <= 0) break; /* Quit on EOF or error */
\r
7703 InputEvent(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
\r
7707 is = (InputSource *) lParam;
\r
7708 if (is->lineByLine) {
\r
7709 /* Feed in lines one by one */
\r
7710 char *p = is->buf;
\r
7712 while (q < is->next) {
\r
7713 if (*q++ == '\n') {
\r
7714 (is->func)(is, is->closure, p, q - p, NO_ERROR);
\r
7719 /* Move any partial line to the start of the buffer */
\r
7721 while (p < is->next) {
\r
7726 if (is->error != NO_ERROR || is->count == 0) {
\r
7727 /* Notify backend of the error. Note: If there was a partial
\r
7728 line at the end, it is not flushed through. */
\r
7729 (is->func)(is, is->closure, is->buf, is->count, is->error);
\r
7732 /* Feed in the whole chunk of input at once */
\r
7733 (is->func)(is, is->closure, is->buf, is->count, is->error);
\r
7734 is->next = is->buf;
\r
7738 /*---------------------------------------------------------------------------*\
\r
7740 * Menu enables. Used when setting various modes.
\r
7742 \*---------------------------------------------------------------------------*/
\r
7750 GreyRevert(Boolean grey)
\r
7751 { // [HGM] vari: for retracting variations in local mode
\r
7752 HMENU hmenu = GetMenu(hwndMain);
\r
7753 EnableMenuItem(hmenu, IDM_Revert, MF_BYCOMMAND|(grey ? MF_GRAYED : MF_ENABLED));
\r
7754 EnableMenuItem(hmenu, IDM_Annotate, MF_BYCOMMAND|(grey ? MF_GRAYED : MF_ENABLED));
\r
7758 SetMenuEnables(HMENU hmenu, Enables *enab)
\r
7760 while (enab->item > 0) {
\r
7761 (void) EnableMenuItem(hmenu, enab->item, enab->flags);
\r
7766 Enables gnuEnables[] = {
\r
7767 { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },
\r
7768 { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },
\r
7769 { IDM_IcsClient, MF_BYCOMMAND|MF_GRAYED },
\r
7770 { IDM_Accept, MF_BYCOMMAND|MF_GRAYED },
\r
7771 { IDM_Decline, MF_BYCOMMAND|MF_GRAYED },
\r
7772 { IDM_Rematch, MF_BYCOMMAND|MF_GRAYED },
\r
7773 { IDM_Adjourn, MF_BYCOMMAND|MF_GRAYED },
\r
7774 { IDM_StopExamining, MF_BYCOMMAND|MF_GRAYED },
\r
7775 { IDM_StopObserving, MF_BYCOMMAND|MF_GRAYED },
\r
7776 { IDM_Upload, MF_BYCOMMAND|MF_GRAYED },
\r
7777 { IDM_Revert, MF_BYCOMMAND|MF_GRAYED },
\r
7778 { IDM_Annotate, MF_BYCOMMAND|MF_GRAYED },
\r
7779 { IDM_NewChat, MF_BYCOMMAND|MF_GRAYED },
\r
7781 // Needed to switch from ncp to GNU mode on Engine Load
\r
7782 { ACTION_POS, MF_BYPOSITION|MF_ENABLED },
\r
7783 { IDM_MachineWhite, MF_BYCOMMAND|MF_ENABLED },
\r
7784 { IDM_MachineBlack, MF_BYCOMMAND|MF_ENABLED },
\r
7785 { IDM_TwoMachines, MF_BYCOMMAND|MF_ENABLED },
\r
7786 { IDM_Match, MF_BYCOMMAND|MF_ENABLED },
\r
7787 { IDM_AnalysisMode, MF_BYCOMMAND|MF_ENABLED },
\r
7788 { IDM_AnalyzeFile, MF_BYCOMMAND|MF_ENABLED },
\r
7789 { IDM_Engine1Options, MF_BYCOMMAND|MF_ENABLED },
\r
7790 { IDM_Engine2Options, MF_BYCOMMAND|MF_ENABLED },
\r
7791 { IDM_TimeControl, MF_BYCOMMAND|MF_ENABLED },
\r
7792 { IDM_RetractMove, MF_BYCOMMAND|MF_ENABLED },
\r
7793 { IDM_MoveNow, MF_BYCOMMAND|MF_ENABLED },
\r
7794 { IDM_Hint, MF_BYCOMMAND|MF_ENABLED },
\r
7795 { IDM_Book, MF_BYCOMMAND|MF_ENABLED },
\r
7799 Enables icsEnables[] = {
\r
7800 { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },
\r
7801 { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },
\r
7802 { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },
\r
7803 { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },
\r
7804 { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },
\r
7805 { IDM_Match, MF_BYCOMMAND|MF_GRAYED },
\r
7806 { IDM_MachineBoth, MF_BYCOMMAND|MF_GRAYED },
\r
7807 { IDM_AnalysisMode, MF_BYCOMMAND|MF_ENABLED },
\r
7808 { IDM_AnalyzeFile, MF_BYCOMMAND|MF_GRAYED },
\r
7809 { IDM_TimeControl, MF_BYCOMMAND|MF_GRAYED },
\r
7810 { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },
\r
7811 { IDM_Hint, MF_BYCOMMAND|MF_GRAYED },
\r
7812 { IDM_Book, MF_BYCOMMAND|MF_GRAYED },
\r
7813 { IDM_EditProgs2, MF_BYCOMMAND|MF_GRAYED },
\r
7814 { IDM_IcsOptions, MF_BYCOMMAND|MF_ENABLED },
\r
7815 { IDM_Engine1Options, MF_BYCOMMAND|MF_GRAYED },
\r
7816 { IDM_Engine2Options, MF_BYCOMMAND|MF_GRAYED },
\r
7817 { IDM_Annotate, MF_BYCOMMAND|MF_GRAYED },
\r
7818 { IDM_Tourney, MF_BYCOMMAND|MF_GRAYED },
\r
7823 Enables zippyEnables[] = {
\r
7824 { IDM_MoveNow, MF_BYCOMMAND|MF_ENABLED },
\r
7825 { IDM_Hint, MF_BYCOMMAND|MF_ENABLED },
\r
7826 { IDM_Book, MF_BYCOMMAND|MF_ENABLED },
\r
7827 { IDM_Engine1Options, MF_BYCOMMAND|MF_ENABLED },
\r
7832 Enables ncpEnables[] = {
\r
7833 { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },
\r
7834 { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },
\r
7835 { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },
\r
7836 { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },
\r
7837 { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },
\r
7838 { IDM_Match, MF_BYCOMMAND|MF_GRAYED },
\r
7839 { IDM_AnalysisMode, MF_BYCOMMAND|MF_GRAYED },
\r
7840 { IDM_AnalyzeFile, MF_BYCOMMAND|MF_GRAYED },
\r
7841 { IDM_IcsClient, MF_BYCOMMAND|MF_GRAYED },
\r
7842 { ACTION_POS, MF_BYPOSITION|MF_GRAYED },
\r
7843 { IDM_Revert, MF_BYCOMMAND|MF_GRAYED },
\r
7844 { IDM_Annotate, MF_BYCOMMAND|MF_GRAYED },
\r
7845 { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },
\r
7846 { IDM_RetractMove, MF_BYCOMMAND|MF_GRAYED },
\r
7847 { IDM_TimeControl, MF_BYCOMMAND|MF_GRAYED },
\r
7848 { IDM_Hint, MF_BYCOMMAND|MF_GRAYED },
\r
7849 { IDM_Book, MF_BYCOMMAND|MF_GRAYED },
\r
7850 { IDM_MachineBoth, MF_BYCOMMAND|MF_GRAYED },
\r
7851 { IDM_NewChat, MF_BYCOMMAND|MF_GRAYED },
\r
7852 { IDM_Engine1Options, MF_BYCOMMAND|MF_GRAYED },
\r
7853 { IDM_Engine2Options, MF_BYCOMMAND|MF_GRAYED },
\r
7854 { IDM_Sounds, MF_BYCOMMAND|MF_GRAYED },
\r
7858 Enables trainingOnEnables[] = {
\r
7859 { IDM_EditComment, MF_BYCOMMAND|MF_GRAYED },
\r
7860 { IDM_Comment, MF_BYCOMMAND|MF_GRAYED },
\r
7861 { IDM_Pause, MF_BYCOMMAND|MF_GRAYED },
\r
7862 { IDM_Forward, MF_BYCOMMAND|MF_GRAYED },
\r
7863 { IDM_Backward, MF_BYCOMMAND|MF_GRAYED },
\r
7864 { IDM_ToEnd, MF_BYCOMMAND|MF_GRAYED },
\r
7865 { IDM_ToStart, MF_BYCOMMAND|MF_GRAYED },
\r
7866 { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },
\r
7867 { IDM_TruncateGame, MF_BYCOMMAND|MF_GRAYED },
\r
7871 Enables trainingOffEnables[] = {
\r
7872 { IDM_EditComment, MF_BYCOMMAND|MF_ENABLED },
\r
7873 { IDM_Comment, MF_BYCOMMAND|MF_ENABLED },
\r
7874 { IDM_Pause, MF_BYCOMMAND|MF_ENABLED },
\r
7875 { IDM_Forward, MF_BYCOMMAND|MF_ENABLED },
\r
7876 { IDM_Backward, MF_BYCOMMAND|MF_ENABLED },
\r
7877 { IDM_ToEnd, MF_BYCOMMAND|MF_ENABLED },
\r
7878 { IDM_ToStart, MF_BYCOMMAND|MF_ENABLED },
\r
7879 { IDM_MoveNow, MF_BYCOMMAND|MF_ENABLED },
\r
7880 { IDM_TruncateGame, MF_BYCOMMAND|MF_ENABLED },
\r
7884 /* These modify either ncpEnables or gnuEnables */
\r
7885 Enables cmailEnables[] = {
\r
7886 { IDM_MailMove, MF_BYCOMMAND|MF_ENABLED },
\r
7887 { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_ENABLED },
\r
7888 { ACTION_POS, MF_BYPOSITION|MF_ENABLED },
\r
7889 { IDM_CallFlag, MF_BYCOMMAND|MF_GRAYED },
\r
7890 { IDM_Draw, MF_BYCOMMAND|MF_ENABLED },
\r
7891 { IDM_Adjourn, MF_BYCOMMAND|MF_GRAYED },
\r
7892 { IDM_Abort, MF_BYCOMMAND|MF_GRAYED },
\r
7896 Enables machineThinkingEnables[] = {
\r
7897 { IDM_LoadGame, MF_BYCOMMAND|MF_GRAYED },
\r
7898 { IDM_LoadNextGame, MF_BYCOMMAND|MF_GRAYED },
\r
7899 { IDM_LoadPrevGame, MF_BYCOMMAND|MF_GRAYED },
\r
7900 { IDM_ReloadGame, MF_BYCOMMAND|MF_GRAYED },
\r
7901 { IDM_PasteGame, MF_BYCOMMAND|MF_GRAYED },
\r
7902 { IDM_LoadPosition, MF_BYCOMMAND|MF_GRAYED },
\r
7903 { IDM_LoadNextPosition, MF_BYCOMMAND|MF_GRAYED },
\r
7904 { IDM_LoadPrevPosition, MF_BYCOMMAND|MF_GRAYED },
\r
7905 { IDM_ReloadPosition, MF_BYCOMMAND|MF_GRAYED },
\r
7906 { IDM_PastePosition, MF_BYCOMMAND|MF_GRAYED },
\r
7907 { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },
\r
7908 { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },
\r
7909 { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },
\r
7910 // { IDM_Match, MF_BYCOMMAND|MF_GRAYED },
\r
7911 { IDM_TypeInMove, MF_BYCOMMAND|MF_GRAYED },
\r
7912 { IDM_RetractMove, MF_BYCOMMAND|MF_GRAYED },
\r
7916 Enables userThinkingEnables[] = {
\r
7917 { IDM_LoadGame, MF_BYCOMMAND|MF_ENABLED },
\r
7918 { IDM_LoadNextGame, MF_BYCOMMAND|MF_ENABLED },
\r
7919 { IDM_LoadPrevGame, MF_BYCOMMAND|MF_ENABLED },
\r
7920 { IDM_ReloadGame, MF_BYCOMMAND|MF_ENABLED },
\r
7921 { IDM_PasteGame, MF_BYCOMMAND|MF_ENABLED },
\r
7922 { IDM_LoadPosition, MF_BYCOMMAND|MF_ENABLED },
\r
7923 { IDM_LoadNextPosition, MF_BYCOMMAND|MF_ENABLED },
\r
7924 { IDM_LoadPrevPosition, MF_BYCOMMAND|MF_ENABLED },
\r
7925 { IDM_ReloadPosition, MF_BYCOMMAND|MF_ENABLED },
\r
7926 { IDM_PastePosition, MF_BYCOMMAND|MF_ENABLED },
\r
7927 { IDM_MachineWhite, MF_BYCOMMAND|MF_ENABLED },
\r
7928 { IDM_MachineBlack, MF_BYCOMMAND|MF_ENABLED },
\r
7929 { IDM_TwoMachines, MF_BYCOMMAND|MF_ENABLED },
\r
7930 // { IDM_Match, MF_BYCOMMAND|MF_ENABLED },
\r
7931 { IDM_TypeInMove, MF_BYCOMMAND|MF_ENABLED },
\r
7932 { IDM_RetractMove, MF_BYCOMMAND|MF_ENABLED },
\r
7936 /*---------------------------------------------------------------------------*\
\r
7938 * Front-end interface functions exported by XBoard.
\r
7939 * Functions appear in same order as prototypes in frontend.h.
\r
7941 \*---------------------------------------------------------------------------*/
\r
7943 CheckMark(UINT item, int state)
\r
7945 if(item) CheckMenuItem(GetMenu(hwndMain), item, MF_BYCOMMAND|state);
\r
7951 static UINT prevChecked = 0;
\r
7952 static int prevPausing = 0;
\r
7955 if (pausing != prevPausing) {
\r
7956 prevPausing = pausing;
\r
7957 (void) CheckMenuItem(GetMenu(hwndMain), IDM_Pause,
\r
7958 MF_BYCOMMAND|(pausing ? MF_CHECKED : MF_UNCHECKED));
\r
7959 if (hwndPause) SetWindowText(hwndPause, pausing ? "C" : "P");
\r
7962 switch (gameMode) {
\r
7963 case BeginningOfGame:
\r
7964 if (appData.icsActive)
\r
7965 nowChecked = IDM_IcsClient;
\r
7966 else if (appData.noChessProgram)
\r
7967 nowChecked = IDM_EditGame;
\r
7969 nowChecked = IDM_MachineBlack;
\r
7971 case MachinePlaysBlack:
\r
7972 nowChecked = IDM_MachineBlack;
\r
7974 case MachinePlaysWhite:
\r
7975 nowChecked = IDM_MachineWhite;
\r
7977 case TwoMachinesPlay:
\r
7978 nowChecked = IDM_TwoMachines;
\r
7981 nowChecked = IDM_AnalysisMode;
\r
7984 nowChecked = IDM_AnalyzeFile;
\r
7987 nowChecked = IDM_EditGame;
\r
7989 case PlayFromGameFile:
\r
7990 nowChecked = IDM_LoadGame;
\r
7992 case EditPosition:
\r
7993 nowChecked = IDM_EditPosition;
\r
7996 nowChecked = IDM_Training;
\r
7998 case IcsPlayingWhite:
\r
7999 case IcsPlayingBlack:
\r
8000 case IcsObserving:
\r
8002 nowChecked = IDM_IcsClient;
\r
8009 CheckMark(prevChecked, MF_UNCHECKED);
\r
8010 CheckMark(nowChecked, MF_CHECKED);
\r
8011 CheckMark(IDM_Match, matchMode && matchGame < appData.matchGames ? MF_CHECKED : MF_UNCHECKED);
\r
8013 if (nowChecked == IDM_LoadGame || nowChecked == IDM_Training) {
\r
8014 (void) EnableMenuItem(GetMenu(hwndMain), IDM_Training,
\r
8015 MF_BYCOMMAND|MF_ENABLED);
\r
8017 (void) EnableMenuItem(GetMenu(hwndMain),
\r
8018 IDM_Training, MF_BYCOMMAND|MF_GRAYED);
\r
8021 prevChecked = nowChecked;
\r
8023 /* [DM] icsEngineAnalyze - Do a sceure check too */
\r
8024 if (appData.icsActive) {
\r
8025 if (appData.icsEngineAnalyze) {
\r
8026 CheckMark(IDM_AnalysisMode, MF_CHECKED);
\r
8028 CheckMark(IDM_AnalysisMode, MF_UNCHECKED);
\r
8031 DisplayLogos(); // [HGM] logos: mode change could have altered logos
\r
8037 HMENU hmenu = GetMenu(hwndMain);
\r
8038 SetMenuEnables(hmenu, icsEnables);
\r
8039 EnableMenuItem(GetSubMenu(hmenu, OPTIONS_POS), IDM_IcsOptions,
\r
8040 MF_BYCOMMAND|MF_ENABLED);
\r
8042 if (appData.zippyPlay) {
\r
8043 SetMenuEnables(hmenu, zippyEnables);
\r
8044 if (!appData.noChessProgram) /* [DM] icsEngineAnalyze */
\r
8045 (void) EnableMenuItem(GetMenu(hwndMain), IDM_AnalysisMode,
\r
8046 MF_BYCOMMAND|MF_ENABLED);
\r
8054 SetMenuEnables(GetMenu(hwndMain), gnuEnables);
\r
8060 HMENU hmenu = GetMenu(hwndMain);
\r
8061 SetMenuEnables(hmenu, ncpEnables);
\r
8062 DrawMenuBar(hwndMain);
\r
8068 SetMenuEnables(GetMenu(hwndMain), cmailEnables);
\r
8072 SetTrainingModeOn()
\r
8075 SetMenuEnables(GetMenu(hwndMain), trainingOnEnables);
\r
8076 for (i = 0; i < N_BUTTONS; i++) {
\r
8077 if (buttonDesc[i].hwnd != NULL)
\r
8078 EnableWindow(buttonDesc[i].hwnd, FALSE);
\r
8083 VOID SetTrainingModeOff()
\r
8086 SetMenuEnables(GetMenu(hwndMain), trainingOffEnables);
\r
8087 for (i = 0; i < N_BUTTONS; i++) {
\r
8088 if (buttonDesc[i].hwnd != NULL)
\r
8089 EnableWindow(buttonDesc[i].hwnd, TRUE);
\r
8095 SetUserThinkingEnables()
\r
8097 SetMenuEnables(GetMenu(hwndMain), userThinkingEnables);
\r
8101 SetMachineThinkingEnables()
\r
8103 HMENU hMenu = GetMenu(hwndMain);
\r
8104 int flags = MF_BYCOMMAND|MF_ENABLED;
\r
8106 SetMenuEnables(hMenu, machineThinkingEnables);
\r
8108 if (gameMode == MachinePlaysBlack) {
\r
8109 (void)EnableMenuItem(hMenu, IDM_MachineBlack, flags);
\r
8110 } else if (gameMode == MachinePlaysWhite) {
\r
8111 (void)EnableMenuItem(hMenu, IDM_MachineWhite, flags);
\r
8112 } else if (gameMode == TwoMachinesPlay) {
\r
8113 (void)EnableMenuItem(hMenu, matchMode ? IDM_Match : IDM_TwoMachines, flags); // [HGM] match
\r
8119 DisplayTitle(char *str)
\r
8121 char title[MSG_SIZ], *host;
\r
8122 if (str[0] != NULLCHAR) {
\r
8123 safeStrCpy(title, str, sizeof(title)/sizeof(title[0]) );
\r
8124 } else if (appData.icsActive) {
\r
8125 if (appData.icsCommPort[0] != NULLCHAR)
\r
8128 host = appData.icsHost;
\r
8129 snprintf(title, MSG_SIZ, "%s: %s", szTitle, host);
\r
8130 } else if (appData.noChessProgram) {
\r
8131 safeStrCpy(title, szTitle, sizeof(title)/sizeof(title[0]) );
\r
8133 safeStrCpy(title, szTitle, sizeof(title)/sizeof(title[0]) );
\r
8134 strcat(title, ": ");
\r
8135 strcat(title, first.tidy);
\r
8137 SetWindowText(hwndMain, title);
\r
8142 DisplayMessage(char *str1, char *str2)
\r
8146 int remain = MESSAGE_TEXT_MAX - 1;
\r
8149 moveErrorMessageUp = FALSE; /* turned on later by caller if needed */
\r
8150 messageText[0] = NULLCHAR;
\r
8152 len = strlen(str1);
\r
8153 if (len > remain) len = remain;
\r
8154 strncpy(messageText, str1, len);
\r
8155 messageText[len] = NULLCHAR;
\r
8158 if (*str2 && remain >= 2) {
\r
8160 strcat(messageText, " ");
\r
8163 len = strlen(str2);
\r
8164 if (len > remain) len = remain;
\r
8165 strncat(messageText, str2, len);
\r
8167 messageText[MESSAGE_TEXT_MAX - 1] = NULLCHAR;
8168 safeStrCpy(lastMsg, messageText, MSG_SIZ);
8170 if (hwndMain == NULL || IsIconic(hwndMain)) return;
\r
8174 hdc = GetDC(hwndMain);
\r
8175 oldFont = SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);
\r
8176 ExtTextOut(hdc, messageRect.left, messageRect.top, ETO_CLIPPED|ETO_OPAQUE,
\r
8177 &messageRect, messageText, strlen(messageText), NULL);
\r
8178 (void) SelectObject(hdc, oldFont);
\r
8179 (void) ReleaseDC(hwndMain, hdc);
\r
8183 DisplayError(char *str, int error)
\r
8185 char buf[MSG_SIZ*2], buf2[MSG_SIZ];
\r
8189 safeStrCpy(buf, str, sizeof(buf)/sizeof(buf[0]) );
\r
8191 len = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,
\r
8192 NULL, error, LANG_NEUTRAL,
\r
8193 (LPSTR) buf2, MSG_SIZ, NULL);
\r
8195 snprintf(buf, 2*MSG_SIZ, "%s:\n%s", str, buf2);
\r
8197 ErrorMap *em = errmap;
\r
8198 while (em->err != 0 && em->err != error) em++;
\r
8199 if (em->err != 0) {
\r
8200 snprintf(buf, 2*MSG_SIZ, "%s:\n%s", str, em->msg);
\r
8202 snprintf(buf, 2*MSG_SIZ, "%s:\nError code %d", str, error);
\r
8207 ErrorPopUp(_("Error"), buf);
\r
8212 DisplayMoveError(char *str)
\r
8214 fromX = fromY = -1;
\r
8215 ClearHighlights();
\r
8216 DrawPosition(FALSE, NULL);
\r
8217 if (appData.popupMoveErrors) {
\r
8218 ErrorPopUp(_("Error"), str);
\r
8220 DisplayMessage(str, "");
\r
8221 moveErrorMessageUp = TRUE;
\r
8226 DisplayFatalError(char *str, int error, int exitStatus)
\r
8228 char buf[2*MSG_SIZ], buf2[MSG_SIZ];
\r
8230 char *label = exitStatus ? _("Fatal Error") : _("Exiting");
\r
8233 len = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,
\r
8234 NULL, error, LANG_NEUTRAL,
\r
8235 (LPSTR) buf2, MSG_SIZ, NULL);
\r
8237 snprintf(buf, 2*MSG_SIZ, "%s:\n%s", str, buf2);
\r
8239 ErrorMap *em = errmap;
\r
8240 while (em->err != 0 && em->err != error) em++;
\r
8241 if (em->err != 0) {
\r
8242 snprintf(buf, 2*MSG_SIZ, "%s:\n%s", str, em->msg);
\r
8244 snprintf(buf, 2*MSG_SIZ, "%s:\nError code %d", str, error);
\r
8249 if (appData.debugMode) {
\r
8250 fprintf(debugFP, "%s: %s\n", label, str);
\r
8252 if (appData.popupExitMessage) {
\r
8253 (void) MessageBox(hwndMain, str, label, MB_OK|
\r
8254 (exitStatus ? MB_ICONSTOP : MB_ICONINFORMATION));
\r
8256 ExitEvent(exitStatus);
\r
8261 DisplayInformation(char *str)
\r
8263 (void) MessageBox(hwndMain, str, _("Information"), MB_OK|MB_ICONINFORMATION);
\r
8268 DisplayNote(char *str)
\r
8270 ErrorPopUp(_("Note"), str);
\r
8275 char *title, *question, *replyPrefix;
\r
8280 QuestionDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
8282 static QuestionParams *qp;
\r
8283 char reply[MSG_SIZ];
\r
8286 switch (message) {
\r
8287 case WM_INITDIALOG:
\r
8288 qp = (QuestionParams *) lParam;
\r
8289 CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));
\r
8290 Translate(hDlg, DLG_Question);
\r
8291 SetWindowText(hDlg, qp->title);
\r
8292 SetDlgItemText(hDlg, OPT_QuestionText, qp->question);
\r
8293 SetFocus(GetDlgItem(hDlg, OPT_QuestionInput));
\r
8297 switch (LOWORD(wParam)) {
\r
8299 safeStrCpy(reply, qp->replyPrefix, sizeof(reply)/sizeof(reply[0]) );
\r
8300 if (*reply) strcat(reply, " ");
\r
8301 len = strlen(reply);
\r
8302 GetDlgItemText(hDlg, OPT_QuestionInput, reply + len, sizeof(reply) - len);
\r
8303 strcat(reply, "\n");
\r
8304 OutputToProcess(qp->pr, reply, strlen(reply), &err);
\r
8305 EndDialog(hDlg, TRUE);
\r
8306 if (err) DisplayFatalError(_("Error writing to chess program"), err, 1);
\r
8309 EndDialog(hDlg, FALSE);
\r
8320 AskQuestion(char* title, char *question, char *replyPrefix, ProcRef pr)
\r
8322 QuestionParams qp;
\r
8326 qp.question = question;
\r
8327 qp.replyPrefix = replyPrefix;
\r
8329 lpProc = MakeProcInstance((FARPROC)QuestionDialog, hInst);
\r
8330 DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_Question),
\r
8331 hwndMain, (DLGPROC)lpProc, (LPARAM)&qp);
\r
8332 FreeProcInstance(lpProc);
\r
8335 /* [AS] Pick FRC position */
\r
8336 LRESULT CALLBACK NewGameFRC_Proc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
8338 static int * lpIndexFRC;
\r
8344 case WM_INITDIALOG:
\r
8345 lpIndexFRC = (int *) lParam;
\r
8347 CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));
\r
8348 Translate(hDlg, DLG_NewGameFRC);
\r
8350 SendDlgItemMessage( hDlg, IDC_NFG_Edit, EM_SETLIMITTEXT, sizeof(buf)-1, 0 );
\r
8351 SetDlgItemInt( hDlg, IDC_NFG_Edit, *lpIndexFRC, TRUE );
\r
8352 SendDlgItemMessage( hDlg, IDC_NFG_Edit, EM_SETSEL, 0, -1 );
\r
8353 SetFocus(GetDlgItem(hDlg, IDC_NFG_Edit));
\r
8358 switch( LOWORD(wParam) ) {
\r
8360 *lpIndexFRC = GetDlgItemInt(hDlg, IDC_NFG_Edit, &index_is_ok, TRUE );
\r
8361 EndDialog( hDlg, 0 );
\r
8362 shuffleOpenings = TRUE; /* [HGM] shuffle: switch shuffling on for as long as we stay in current variant */
\r
8365 EndDialog( hDlg, 1 );
\r
8367 case IDC_NFG_Edit:
\r
8368 if( HIWORD(wParam) == EN_CHANGE ) {
\r
8369 GetDlgItemInt(hDlg, IDC_NFG_Edit, &index_is_ok, TRUE );
\r
8371 EnableWindow( GetDlgItem(hDlg, IDOK), index_is_ok );
\r
8374 case IDC_NFG_Random:
\r
8375 snprintf( buf, sizeof(buf)/sizeof(buf[0]), "%d", myrandom() ); /* [HGM] shuffle: no longer limit to 960 */
\r
8376 SetDlgItemText(hDlg, IDC_NFG_Edit, buf );
\r
8389 int index = appData.defaultFrcPosition;
\r
8390 FARPROC lpProc = MakeProcInstance( (FARPROC) NewGameFRC_Proc, hInst );
\r
8392 result = DialogBoxParam( hInst, MAKEINTRESOURCE(DLG_NewGameFRC), hwndMain, (DLGPROC)lpProc, (LPARAM)&index );
\r
8394 if( result == 0 ) {
\r
8395 appData.defaultFrcPosition = index;
\r
8401 /* [AS] Game list options. Refactored by HGM */
\r
8403 HWND gameListOptionsDialog;
\r
8405 // low-level front-end: clear text edit / list widget
\r
8409 SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_RESETCONTENT, 0, 0 );
\r
8412 // low-level front-end: clear text edit / list widget
\r
8414 GLT_DeSelectList()
\r
8416 SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_SETCURSEL, 0, 0 );
\r
8419 // low-level front-end: append line to text edit / list widget
\r
8421 GLT_AddToList( char *name )
\r
8424 SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_ADDSTRING, 0, (LPARAM) name );
\r
8428 // low-level front-end: get line from text edit / list widget
\r
8430 GLT_GetFromList( int index, char *name )
\r
8433 if( SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_GETTEXT, index, (LPARAM) name ) != LB_ERR )
\r
8439 void GLT_MoveSelection( HWND hDlg, int delta )
\r
8441 int idx1 = (int) SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETCURSEL, 0, 0 );
\r
8442 int idx2 = idx1 + delta;
\r
8443 int count = (int) SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETCOUNT, 0, 0 );
\r
8445 if( idx1 >=0 && idx1 < count && idx2 >= 0 && idx2 < count ) {
\r
8448 SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETTEXT, idx1, (LPARAM) buf );
\r
8449 SendDlgItemMessage( hDlg, IDC_GameListTags, LB_DELETESTRING, idx1, 0 );
\r
8450 SendDlgItemMessage( hDlg, IDC_GameListTags, LB_INSERTSTRING, idx2, (LPARAM) buf );
\r
8451 SendDlgItemMessage( hDlg, IDC_GameListTags, LB_SETCURSEL, idx2, 0 );
\r
8455 LRESULT CALLBACK GameListOptions_Proc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
8459 case WM_INITDIALOG:
\r
8460 gameListOptionsDialog = hDlg; // [HGM] pass through global to keep out off back-end
\r
8462 CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));
\r
8463 Translate(hDlg, DLG_GameListOptions);
\r
8465 /* Initialize list */
\r
8466 GLT_TagsToList( lpUserGLT );
\r
8468 SetFocus( GetDlgItem(hDlg, IDC_GameListTags) );
\r
8473 switch( LOWORD(wParam) ) {
\r
8476 EndDialog( hDlg, 0 );
\r
8479 EndDialog( hDlg, 1 );
\r
8482 case IDC_GLT_Default:
\r
8483 GLT_TagsToList( GLT_DEFAULT_TAGS );
\r
8486 case IDC_GLT_Restore:
\r
8487 GLT_TagsToList( appData.gameListTags );
\r
8491 GLT_MoveSelection( hDlg, -1 );
\r
8494 case IDC_GLT_Down:
\r
8495 GLT_MoveSelection( hDlg, +1 );
\r
8505 int GameListOptions()
\r
8508 FARPROC lpProc = MakeProcInstance( (FARPROC) GameListOptions_Proc, hInst );
\r
8510 safeStrCpy( lpUserGLT, appData.gameListTags ,LPUSERGLT_SIZE );
\r
8512 result = DialogBoxParam( hInst, MAKEINTRESOURCE(DLG_GameListOptions), hwndMain, (DLGPROC)lpProc, (LPARAM)lpUserGLT );
\r
8514 if( result == 0 ) {
\r
8515 /* [AS] Memory leak here! */
\r
8516 appData.gameListTags = strdup( lpUserGLT );
\r
8523 DisplayIcsInteractionTitle(char *str)
\r
8525 char consoleTitle[MSG_SIZ];
\r
8527 snprintf(consoleTitle, MSG_SIZ, "%s: %s", szConsoleTitle, str);
\r
8528 SetWindowText(hwndConsole, consoleTitle);
\r
8532 DrawPosition(int fullRedraw, Board board)
\r
8534 HDCDrawPosition(NULL, (BOOLEAN) fullRedraw, board);
\r
8537 void NotifyFrontendLogin()
\r
8540 UpdateICSWidth(GetDlgItem(hwndConsole, OPT_ConsoleText));
\r
8546 fromX = fromY = -1;
\r
8547 if (dragInfo.pos.x != -1 || dragInfo.pos.y != -1) {
\r
8548 dragInfo.pos.x = dragInfo.pos.y = -1;
\r
8549 dragInfo.pos.x = dragInfo.pos.y = -1;
\r
8550 dragInfo.lastpos = dragInfo.pos;
\r
8551 dragInfo.start.x = dragInfo.start.y = -1;
\r
8552 dragInfo.from = dragInfo.start;
\r
8554 DrawPosition(TRUE, NULL);
\r
8561 CommentPopUp(char *title, char *str)
\r
8563 HWND hwnd = GetActiveWindow();
\r
8564 EitherCommentPopUp(currentMove, title, str, FALSE); // [HGM] vari: fake move index, rather than 0
\r
8566 SetActiveWindow(hwnd);
\r
8570 CommentPopDown(void)
\r
8572 CheckMenuItem(GetMenu(hwndMain), IDM_Comment, MF_UNCHECKED);
\r
8573 if (commentDialog) {
\r
8574 ShowWindow(commentDialog, SW_HIDE);
\r
8576 commentUp = FALSE;
\r
8580 EditCommentPopUp(int index, char *title, char *str)
\r
8582 EitherCommentPopUp(index, title, str, TRUE);
\r
8589 MyPlaySound(&sounds[(int)SoundMove]);
\r
8592 VOID PlayIcsWinSound()
\r
8594 MyPlaySound(&sounds[(int)SoundIcsWin]);
\r
8597 VOID PlayIcsLossSound()
\r
8599 MyPlaySound(&sounds[(int)SoundIcsLoss]);
\r
8602 VOID PlayIcsDrawSound()
\r
8604 MyPlaySound(&sounds[(int)SoundIcsDraw]);
\r
8607 VOID PlayIcsUnfinishedSound()
\r
8609 MyPlaySound(&sounds[(int)SoundIcsUnfinished]);
\r
8615 MyPlaySound(&sounds[(int)SoundAlarm]);
\r
8621 MyPlaySound(&textAttribs[ColorTell].sound);
\r
8629 consoleEcho = TRUE;
\r
8630 hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
8631 SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&consoleCF);
\r
8632 SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);
\r
8641 consoleEcho = FALSE;
\r
8642 hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
8643 /* This works OK: set text and background both to the same color */
\r
8645 cf.crTextColor = COLOR_ECHOOFF;
\r
8646 SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf);
\r
8647 SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, cf.crTextColor);
\r
8650 /* No Raw()...? */
\r
8652 void Colorize(ColorClass cc, int continuation)
\r
8654 currentColorClass = cc;
\r
8655 consoleCF.dwMask = CFM_COLOR|CFM_BOLD|CFM_ITALIC|CFM_UNDERLINE|CFM_STRIKEOUT;
\r
8656 consoleCF.crTextColor = textAttribs[cc].color;
\r
8657 consoleCF.dwEffects = textAttribs[cc].effects;
\r
8658 if (!continuation) MyPlaySound(&textAttribs[cc].sound);
\r
8664 static char buf[MSG_SIZ];
\r
8665 DWORD bufsiz = MSG_SIZ;
\r
8667 if(appData.userName != NULL && appData.userName[0] != 0) {
\r
8668 return appData.userName; /* [HGM] username: prefer name selected by user over his system login */
\r
8670 if (!GetUserName(buf, &bufsiz)) {
\r
8671 /*DisplayError("Error getting user name", GetLastError());*/
\r
8672 safeStrCpy(buf, _("User"), sizeof(buf)/sizeof(buf[0]) );
\r
8680 static char buf[MSG_SIZ];
\r
8681 DWORD bufsiz = MSG_SIZ;
\r
8683 if (!GetComputerName(buf, &bufsiz)) {
\r
8684 /*DisplayError("Error getting host name", GetLastError());*/
\r
8685 safeStrCpy(buf, _("Unknown"), sizeof(buf)/sizeof(buf[0]) );
\r
8692 ClockTimerRunning()
\r
8694 return clockTimerEvent != 0;
\r
8700 if (clockTimerEvent == 0) return FALSE;
\r
8701 KillTimer(hwndMain, clockTimerEvent);
\r
8702 clockTimerEvent = 0;
\r
8707 StartClockTimer(long millisec)
\r
8709 clockTimerEvent = SetTimer(hwndMain, (UINT) CLOCK_TIMER_ID,
\r
8710 (UINT) millisec, NULL);
\r
8714 DisplayWhiteClock(long timeRemaining, int highlight)
\r
8717 char *flag = whiteFlag && gameMode == TwoMachinesPlay ? "(!)" : "";
\r
8719 if(appData.noGUI) return;
\r
8720 hdc = GetDC(hwndMain);
\r
8721 if (!IsIconic(hwndMain)) {
\r
8722 DisplayAClock(hdc, timeRemaining, highlight,
\r
8723 flipClock ? &blackRect : &whiteRect, _("White"), flag);
\r
8725 if (highlight && iconCurrent == iconBlack) {
\r
8726 iconCurrent = iconWhite;
\r
8727 PostMessage(hwndMain, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);
\r
8728 if (IsIconic(hwndMain)) {
\r
8729 DrawIcon(hdc, 2, 2, iconCurrent);
\r
8732 (void) ReleaseDC(hwndMain, hdc);
\r
8734 PostMessage(hwndConsole, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);
\r
8738 DisplayBlackClock(long timeRemaining, int highlight)
\r
8741 char *flag = blackFlag && gameMode == TwoMachinesPlay ? "(!)" : "";
\r
8743 if(appData.noGUI) return;
\r
8744 hdc = GetDC(hwndMain);
\r
8745 if (!IsIconic(hwndMain)) {
\r
8746 DisplayAClock(hdc, timeRemaining, highlight,
\r
8747 flipClock ? &whiteRect : &blackRect, _("Black"), flag);
\r
8749 if (highlight && iconCurrent == iconWhite) {
\r
8750 iconCurrent = iconBlack;
\r
8751 PostMessage(hwndMain, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);
\r
8752 if (IsIconic(hwndMain)) {
\r
8753 DrawIcon(hdc, 2, 2, iconCurrent);
\r
8756 (void) ReleaseDC(hwndMain, hdc);
\r
8758 PostMessage(hwndConsole, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);
\r
8763 LoadGameTimerRunning()
\r
8765 return loadGameTimerEvent != 0;
\r
8769 StopLoadGameTimer()
\r
8771 if (loadGameTimerEvent == 0) return FALSE;
\r
8772 KillTimer(hwndMain, loadGameTimerEvent);
\r
8773 loadGameTimerEvent = 0;
\r
8778 StartLoadGameTimer(long millisec)
\r
8780 loadGameTimerEvent = SetTimer(hwndMain, (UINT) LOAD_GAME_TIMER_ID,
\r
8781 (UINT) millisec, NULL);
\r
8789 char fileTitle[MSG_SIZ];
\r
8791 defName = DefaultFileName(appData.oldSaveStyle ? "gam" : "pgn");
\r
8792 f = OpenFileDialog(hwndMain, "a", defName,
\r
8793 appData.oldSaveStyle ? "gam" : "pgn",
\r
8795 _("Save Game to File"), NULL, fileTitle, NULL);
\r
8797 SaveGame(f, 0, "");
\r
8804 ScheduleDelayedEvent(DelayedEventCallback cb, long millisec)
\r
8806 if (delayedTimerEvent != 0) {
\r
8807 if (appData.debugMode && cb != delayedTimerCallback) { // [HGM] alive: not too much debug
\r
8808 fprintf(debugFP, "ScheduleDelayedEvent: event already scheduled\n");
\r
8810 KillTimer(hwndMain, delayedTimerEvent);
\r
8811 delayedTimerEvent = 0;
\r
8812 if(delayedTimerCallback != cb) // [HGM] alive: do not "flush" same event, just postpone it
\r
8813 delayedTimerCallback();
\r
8815 delayedTimerCallback = cb;
\r
8816 delayedTimerEvent = SetTimer(hwndMain, (UINT) DELAYED_TIMER_ID,
\r
8817 (UINT) millisec, NULL);
\r
8820 DelayedEventCallback
\r
8823 if (delayedTimerEvent) {
\r
8824 return delayedTimerCallback;
\r
8831 CancelDelayedEvent()
\r
8833 if (delayedTimerEvent) {
\r
8834 KillTimer(hwndMain, delayedTimerEvent);
\r
8835 delayedTimerEvent = 0;
\r
8839 DWORD GetWin32Priority(int nice)
\r
8840 { // [HGM] nice: translate Unix nice() value to indows priority class. (Code stolen from Polyglot 1.4w11)
\r
8842 REALTIME_PRIORITY_CLASS 0x00000100
\r
8843 HIGH_PRIORITY_CLASS 0x00000080
\r
8844 ABOVE_NORMAL_PRIORITY_CLASS 0x00008000
\r
8845 NORMAL_PRIORITY_CLASS 0x00000020
\r
8846 BELOW_NORMAL_PRIORITY_CLASS 0x00004000
\r
8847 IDLE_PRIORITY_CLASS 0x00000040
\r
8849 if (nice < -15) return 0x00000080;
\r
8850 if (nice < 0) return 0x00008000;
\r
8851 if (nice == 0) return 0x00000020;
\r
8852 if (nice < 15) return 0x00004000;
\r
8853 return 0x00000040;
\r
8856 /* Start a child process running the given program.
\r
8857 The process's standard output can be read from "from", and its
\r
8858 standard input can be written to "to".
\r
8859 Exit with fatal error if anything goes wrong.
\r
8860 Returns an opaque pointer that can be used to destroy the process
\r
8864 StartChildProcess(char *cmdLine, char *dir, ProcRef *pr)
\r
8866 #define BUFSIZE 4096
\r
8868 HANDLE hChildStdinRd, hChildStdinWr,
\r
8869 hChildStdoutRd, hChildStdoutWr;
\r
8870 HANDLE hChildStdinWrDup, hChildStdoutRdDup;
\r
8871 SECURITY_ATTRIBUTES saAttr;
\r
8873 PROCESS_INFORMATION piProcInfo;
\r
8874 STARTUPINFO siStartInfo;
\r
8876 char buf[MSG_SIZ];
\r
8879 if (appData.debugMode) {
\r
8880 fprintf(debugFP, "StartChildProcess (dir=\"%s\") %s\n", dir, cmdLine);
\r
8885 /* Set the bInheritHandle flag so pipe handles are inherited. */
\r
8886 saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);
\r
8887 saAttr.bInheritHandle = TRUE;
\r
8888 saAttr.lpSecurityDescriptor = NULL;
\r
8891 * The steps for redirecting child's STDOUT:
\r
8892 * 1. Create anonymous pipe to be STDOUT for child.
\r
8893 * 2. Create a noninheritable duplicate of read handle,
\r
8894 * and close the inheritable read handle.
\r
8897 /* Create a pipe for the child's STDOUT. */
\r
8898 if (! CreatePipe(&hChildStdoutRd, &hChildStdoutWr, &saAttr, 0)) {
\r
8899 return GetLastError();
\r
8902 /* Duplicate the read handle to the pipe, so it is not inherited. */
\r
8903 fSuccess = DuplicateHandle(GetCurrentProcess(), hChildStdoutRd,
\r
8904 GetCurrentProcess(), &hChildStdoutRdDup, 0,
\r
8905 FALSE, /* not inherited */
\r
8906 DUPLICATE_SAME_ACCESS);
\r
8908 return GetLastError();
\r
8910 CloseHandle(hChildStdoutRd);
\r
8913 * The steps for redirecting child's STDIN:
\r
8914 * 1. Create anonymous pipe to be STDIN for child.
\r
8915 * 2. Create a noninheritable duplicate of write handle,
\r
8916 * and close the inheritable write handle.
\r
8919 /* Create a pipe for the child's STDIN. */
\r
8920 if (! CreatePipe(&hChildStdinRd, &hChildStdinWr, &saAttr, 0)) {
\r
8921 return GetLastError();
\r
8924 /* Duplicate the write handle to the pipe, so it is not inherited. */
\r
8925 fSuccess = DuplicateHandle(GetCurrentProcess(), hChildStdinWr,
\r
8926 GetCurrentProcess(), &hChildStdinWrDup, 0,
\r
8927 FALSE, /* not inherited */
\r
8928 DUPLICATE_SAME_ACCESS);
\r
8930 return GetLastError();
\r
8932 CloseHandle(hChildStdinWr);
\r
8934 /* Arrange to (1) look in dir for the child .exe file, and
\r
8935 * (2) have dir be the child's working directory. Interpret
\r
8936 * dir relative to the directory WinBoard loaded from. */
\r
8937 GetCurrentDirectory(MSG_SIZ, buf);
\r
8938 SetCurrentDirectory(installDir);
\r
8939 SetCurrentDirectory(dir);
\r
8941 /* Now create the child process. */
\r
8943 siStartInfo.cb = sizeof(STARTUPINFO);
\r
8944 siStartInfo.lpReserved = NULL;
\r
8945 siStartInfo.lpDesktop = NULL;
\r
8946 siStartInfo.lpTitle = NULL;
\r
8947 siStartInfo.dwFlags = STARTF_USESTDHANDLES;
\r
8948 siStartInfo.cbReserved2 = 0;
\r
8949 siStartInfo.lpReserved2 = NULL;
\r
8950 siStartInfo.hStdInput = hChildStdinRd;
\r
8951 siStartInfo.hStdOutput = hChildStdoutWr;
\r
8952 siStartInfo.hStdError = hChildStdoutWr;
\r
8954 fSuccess = CreateProcess(NULL,
\r
8955 cmdLine, /* command line */
\r
8956 NULL, /* process security attributes */
\r
8957 NULL, /* primary thread security attrs */
\r
8958 TRUE, /* handles are inherited */
\r
8959 DETACHED_PROCESS|CREATE_NEW_PROCESS_GROUP,
\r
8960 NULL, /* use parent's environment */
\r
8962 &siStartInfo, /* STARTUPINFO pointer */
\r
8963 &piProcInfo); /* receives PROCESS_INFORMATION */
\r
8965 err = GetLastError();
\r
8966 SetCurrentDirectory(buf); /* return to prev directory */
\r
8971 if (appData.niceEngines){ // [HGM] nice: adjust engine proc priority
\r
8972 if(appData.debugMode) fprintf(debugFP, "nice engine proc to %d\n", appData.niceEngines);
\r
8973 SetPriorityClass(piProcInfo.hProcess, GetWin32Priority(appData.niceEngines));
\r
8976 /* Close the handles we don't need in the parent */
\r
8977 CloseHandle(piProcInfo.hThread);
\r
8978 CloseHandle(hChildStdinRd);
\r
8979 CloseHandle(hChildStdoutWr);
\r
8981 /* Prepare return value */
\r
8982 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
\r
8983 cp->kind = CPReal;
\r
8984 cp->hProcess = piProcInfo.hProcess;
\r
8985 cp->pid = piProcInfo.dwProcessId;
\r
8986 cp->hFrom = hChildStdoutRdDup;
\r
8987 cp->hTo = hChildStdinWrDup;
\r
8989 *pr = (void *) cp;
\r
8991 /* Klaus Friedel says that this Sleep solves a problem under Windows
\r
8992 2000 where engines sometimes don't see the initial command(s)
\r
8993 from WinBoard and hang. I don't understand how that can happen,
\r
8994 but the Sleep is harmless, so I've put it in. Others have also
\r
8995 reported what may be the same problem, so hopefully this will fix
\r
8996 it for them too. */
\r
9004 DestroyChildProcess(ProcRef pr, int/*boolean*/ signal)
\r
9006 ChildProc *cp; int result;
\r
9008 cp = (ChildProc *) pr;
\r
9009 if (cp == NULL) return;
\r
9011 switch (cp->kind) {
\r
9013 /* TerminateProcess is considered harmful, so... */
\r
9014 CloseHandle(cp->hTo); /* Closing this will give the child an EOF and hopefully kill it */
\r
9015 if (cp->hFrom) CloseHandle(cp->hFrom); /* if NULL, InputThread will close it */
\r
9016 /* The following doesn't work because the chess program
\r
9017 doesn't "have the same console" as WinBoard. Maybe
\r
9018 we could arrange for this even though neither WinBoard
\r
9019 nor the chess program uses a console for stdio? */
\r
9020 /*!!if (signal) GenerateConsoleCtrlEvent(CTRL_BREAK_EVENT, cp->pid);*/
\r
9022 /* [AS] Special termination modes for misbehaving programs... */
\r
9023 if( signal == 9 ) {
\r
9024 result = TerminateProcess( cp->hProcess, 0 );
\r
9026 if ( appData.debugMode) {
\r
9027 fprintf( debugFP, "Terminating process %lu, result=%d\n", cp->pid, result );
\r
9030 else if( signal == 10 ) {
\r
9031 DWORD dw = WaitForSingleObject( cp->hProcess, 3*1000 ); // Wait 3 seconds at most
\r
9033 if( dw != WAIT_OBJECT_0 ) {
\r
9034 result = TerminateProcess( cp->hProcess, 0 );
\r
9036 if ( appData.debugMode) {
\r
9037 fprintf( debugFP, "Process %lu still alive after timeout, killing... result=%d\n", cp->pid, result );
\r
9043 CloseHandle(cp->hProcess);
\r
9047 if (cp->hFrom) CloseHandle(cp->hFrom);
\r
9051 closesocket(cp->sock);
\r
9056 if (signal) send(cp->sock2, "\017", 1, 0); /* 017 = 15 = SIGTERM */
\r
9057 closesocket(cp->sock);
\r
9058 closesocket(cp->sock2);
\r
9066 InterruptChildProcess(ProcRef pr)
\r
9070 cp = (ChildProc *) pr;
\r
9071 if (cp == NULL) return;
\r
9072 switch (cp->kind) {
\r
9074 /* The following doesn't work because the chess program
\r
9075 doesn't "have the same console" as WinBoard. Maybe
\r
9076 we could arrange for this even though neither WinBoard
\r
9077 nor the chess program uses a console for stdio */
\r
9078 /*!!GenerateConsoleCtrlEvent(CTRL_C_EVENT, cp->pid);*/
\r
9083 /* Can't interrupt */
\r
9087 send(cp->sock2, "\002", 1, 0); /* 2 = SIGINT */
\r
9094 OpenTelnet(char *host, char *port, ProcRef *pr)
\r
9096 char cmdLine[MSG_SIZ];
\r
9098 if (port[0] == NULLCHAR) {
\r
9099 snprintf(cmdLine, MSG_SIZ, "%s %s", appData.telnetProgram, host);
\r
9101 snprintf(cmdLine, MSG_SIZ, "%s %s %s", appData.telnetProgram, host, port);
\r
9103 return StartChildProcess(cmdLine, "", pr);
\r
9107 /* Code to open TCP sockets */
\r
9110 OpenTCP(char *host, char *port, ProcRef *pr)
\r
9115 struct sockaddr_in sa, mysa;
\r
9116 struct hostent FAR *hp;
\r
9117 unsigned short uport;
\r
9118 WORD wVersionRequested;
\r
9121 /* Initialize socket DLL */
\r
9122 wVersionRequested = MAKEWORD(1, 1);
\r
9123 err = WSAStartup(wVersionRequested, &wsaData);
\r
9124 if (err != 0) return err;
\r
9127 if ((s = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {
\r
9128 err = WSAGetLastError();
\r
9133 /* Bind local address using (mostly) don't-care values.
\r
9135 memset((char *) &mysa, 0, sizeof(struct sockaddr_in));
\r
9136 mysa.sin_family = AF_INET;
\r
9137 mysa.sin_addr.s_addr = INADDR_ANY;
\r
9138 uport = (unsigned short) 0;
\r
9139 mysa.sin_port = htons(uport);
\r
9140 if (bind(s, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))
\r
9141 == SOCKET_ERROR) {
\r
9142 err = WSAGetLastError();
\r
9147 /* Resolve remote host name */
\r
9148 memset((char *) &sa, 0, sizeof(struct sockaddr_in));
\r
9149 if (!(hp = gethostbyname(host))) {
\r
9150 unsigned int b0, b1, b2, b3;
\r
9152 err = WSAGetLastError();
\r
9154 if (sscanf(host, "%u.%u.%u.%u", &b0, &b1, &b2, &b3) == 4) {
\r
9155 hp = (struct hostent *) calloc(1, sizeof(struct hostent));
\r
9156 hp->h_addrtype = AF_INET;
\r
9158 hp->h_addr_list = (char **) calloc(2, sizeof(char *));
\r
9159 hp->h_addr_list[0] = (char *) malloc(4);
\r
9160 hp->h_addr_list[0][0] = (char) b0;
\r
9161 hp->h_addr_list[0][1] = (char) b1;
\r
9162 hp->h_addr_list[0][2] = (char) b2;
\r
9163 hp->h_addr_list[0][3] = (char) b3;
\r
9169 sa.sin_family = hp->h_addrtype;
\r
9170 uport = (unsigned short) atoi(port);
\r
9171 sa.sin_port = htons(uport);
\r
9172 memcpy((char *) &sa.sin_addr, hp->h_addr, hp->h_length);
\r
9174 /* Make connection */
\r
9175 if (connect(s, (struct sockaddr *) &sa,
\r
9176 sizeof(struct sockaddr_in)) == SOCKET_ERROR) {
\r
9177 err = WSAGetLastError();
\r
9182 /* Prepare return value */
\r
9183 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
\r
9184 cp->kind = CPSock;
\r
9186 *pr = (ProcRef *) cp;
\r
9192 OpenCommPort(char *name, ProcRef *pr)
\r
9197 char fullname[MSG_SIZ];
\r
9199 if (*name != '\\')
\r
9200 snprintf(fullname, MSG_SIZ, "\\\\.\\%s", name);
\r
9202 safeStrCpy(fullname, name, sizeof(fullname)/sizeof(fullname[0]) );
\r
9204 h = CreateFile(name, GENERIC_READ | GENERIC_WRITE,
\r
9205 0, NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL);
\r
9206 if (h == (HANDLE) -1) {
\r
9207 return GetLastError();
\r
9211 if (!SetCommState(h, (LPDCB) &dcb)) return GetLastError();
\r
9213 /* Accumulate characters until a 100ms pause, then parse */
\r
9214 ct.ReadIntervalTimeout = 100;
\r
9215 ct.ReadTotalTimeoutMultiplier = 0;
\r
9216 ct.ReadTotalTimeoutConstant = 0;
\r
9217 ct.WriteTotalTimeoutMultiplier = 0;
\r
9218 ct.WriteTotalTimeoutConstant = 0;
\r
9219 if (!SetCommTimeouts(h, (LPCOMMTIMEOUTS) &ct)) return GetLastError();
\r
9221 /* Prepare return value */
\r
9222 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
\r
9223 cp->kind = CPComm;
\r
9226 *pr = (ProcRef *) cp;
\r
9232 OpenLoopback(ProcRef *pr)
\r
9234 DisplayFatalError(_("Not implemented"), 0, 1);
\r
9240 OpenRcmd(char* host, char* user, char* cmd, ProcRef* pr)
\r
9245 struct sockaddr_in sa, mysa;
\r
9246 struct hostent FAR *hp;
\r
9247 unsigned short uport;
\r
9248 WORD wVersionRequested;
\r
9251 char stderrPortStr[MSG_SIZ];
\r
9253 /* Initialize socket DLL */
\r
9254 wVersionRequested = MAKEWORD(1, 1);
\r
9255 err = WSAStartup(wVersionRequested, &wsaData);
\r
9256 if (err != 0) return err;
\r
9258 /* Resolve remote host name */
\r
9259 memset((char *) &sa, 0, sizeof(struct sockaddr_in));
\r
9260 if (!(hp = gethostbyname(host))) {
\r
9261 unsigned int b0, b1, b2, b3;
\r
9263 err = WSAGetLastError();
\r
9265 if (sscanf(host, "%u.%u.%u.%u", &b0, &b1, &b2, &b3) == 4) {
\r
9266 hp = (struct hostent *) calloc(1, sizeof(struct hostent));
\r
9267 hp->h_addrtype = AF_INET;
\r
9269 hp->h_addr_list = (char **) calloc(2, sizeof(char *));
\r
9270 hp->h_addr_list[0] = (char *) malloc(4);
\r
9271 hp->h_addr_list[0][0] = (char) b0;
\r
9272 hp->h_addr_list[0][1] = (char) b1;
\r
9273 hp->h_addr_list[0][2] = (char) b2;
\r
9274 hp->h_addr_list[0][3] = (char) b3;
\r
9280 sa.sin_family = hp->h_addrtype;
\r
9281 uport = (unsigned short) 514;
\r
9282 sa.sin_port = htons(uport);
\r
9283 memcpy((char *) &sa.sin_addr, hp->h_addr, hp->h_length);
\r
9285 /* Bind local socket to unused "privileged" port address
\r
9287 s = INVALID_SOCKET;
\r
9288 memset((char *) &mysa, 0, sizeof(struct sockaddr_in));
\r
9289 mysa.sin_family = AF_INET;
\r
9290 mysa.sin_addr.s_addr = INADDR_ANY;
\r
9291 for (fromPort = 1023;; fromPort--) {
\r
9292 if (fromPort < 0) {
\r
9294 return WSAEADDRINUSE;
\r
9296 if (s == INVALID_SOCKET) {
\r
9297 if ((s = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {
\r
9298 err = WSAGetLastError();
\r
9303 uport = (unsigned short) fromPort;
\r
9304 mysa.sin_port = htons(uport);
\r
9305 if (bind(s, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))
\r
9306 == SOCKET_ERROR) {
\r
9307 err = WSAGetLastError();
\r
9308 if (err == WSAEADDRINUSE) continue;
\r
9312 if (connect(s, (struct sockaddr *) &sa,
\r
9313 sizeof(struct sockaddr_in)) == SOCKET_ERROR) {
\r
9314 err = WSAGetLastError();
\r
9315 if (err == WSAEADDRINUSE) {
\r
9326 /* Bind stderr local socket to unused "privileged" port address
\r
9328 s2 = INVALID_SOCKET;
\r
9329 memset((char *) &mysa, 0, sizeof(struct sockaddr_in));
\r
9330 mysa.sin_family = AF_INET;
\r
9331 mysa.sin_addr.s_addr = INADDR_ANY;
\r
9332 for (fromPort = 1023;; fromPort--) {
\r
9333 if (fromPort == prevStderrPort) continue; // don't reuse port
\r
9334 if (fromPort < 0) {
\r
9335 (void) closesocket(s);
\r
9337 return WSAEADDRINUSE;
\r
9339 if (s2 == INVALID_SOCKET) {
\r
9340 if ((s2 = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {
\r
9341 err = WSAGetLastError();
\r
9347 uport = (unsigned short) fromPort;
\r
9348 mysa.sin_port = htons(uport);
\r
9349 if (bind(s2, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))
\r
9350 == SOCKET_ERROR) {
\r
9351 err = WSAGetLastError();
\r
9352 if (err == WSAEADDRINUSE) continue;
\r
9353 (void) closesocket(s);
\r
9357 if (listen(s2, 1) == SOCKET_ERROR) {
\r
9358 err = WSAGetLastError();
\r
9359 if (err == WSAEADDRINUSE) {
\r
9361 s2 = INVALID_SOCKET;
\r
9364 (void) closesocket(s);
\r
9365 (void) closesocket(s2);
\r
9371 prevStderrPort = fromPort; // remember port used
\r
9372 snprintf(stderrPortStr, MSG_SIZ, "%d", fromPort);
\r
9374 if (send(s, stderrPortStr, strlen(stderrPortStr) + 1, 0) == SOCKET_ERROR) {
\r
9375 err = WSAGetLastError();
\r
9376 (void) closesocket(s);
\r
9377 (void) closesocket(s2);
\r
9382 if (send(s, UserName(), strlen(UserName()) + 1, 0) == SOCKET_ERROR) {
\r
9383 err = WSAGetLastError();
\r
9384 (void) closesocket(s);
\r
9385 (void) closesocket(s2);
\r
9389 if (*user == NULLCHAR) user = UserName();
\r
9390 if (send(s, user, strlen(user) + 1, 0) == SOCKET_ERROR) {
\r
9391 err = WSAGetLastError();
\r
9392 (void) closesocket(s);
\r
9393 (void) closesocket(s2);
\r
9397 if (send(s, cmd, strlen(cmd) + 1, 0) == SOCKET_ERROR) {
\r
9398 err = WSAGetLastError();
\r
9399 (void) closesocket(s);
\r
9400 (void) closesocket(s2);
\r
9405 if ((s3 = accept(s2, NULL, NULL)) == INVALID_SOCKET) {
\r
9406 err = WSAGetLastError();
\r
9407 (void) closesocket(s);
\r
9408 (void) closesocket(s2);
\r
9412 (void) closesocket(s2); /* Stop listening */
\r
9414 /* Prepare return value */
\r
9415 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
\r
9416 cp->kind = CPRcmd;
\r
9419 *pr = (ProcRef *) cp;
\r
9426 AddInputSource(ProcRef pr, int lineByLine,
\r
9427 InputCallback func, VOIDSTAR closure)
\r
9429 InputSource *is, *is2 = NULL;
\r
9430 ChildProc *cp = (ChildProc *) pr;
\r
9432 is = (InputSource *) calloc(1, sizeof(InputSource));
\r
9433 is->lineByLine = lineByLine;
\r
9435 is->closure = closure;
\r
9436 is->second = NULL;
\r
9437 is->next = is->buf;
\r
9438 if (pr == NoProc) {
\r
9439 is->kind = CPReal;
\r
9440 consoleInputSource = is;
\r
9442 is->kind = cp->kind;
\r
9444 [AS] Try to avoid a race condition if the thread is given control too early:
\r
9445 we create all threads suspended so that the is->hThread variable can be
\r
9446 safely assigned, then let the threads start with ResumeThread.
\r
9448 switch (cp->kind) {
\r
9450 is->hFile = cp->hFrom;
\r
9451 cp->hFrom = NULL; /* now owned by InputThread */
\r
9453 CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) NonOvlInputThread,
\r
9454 (LPVOID) is, CREATE_SUSPENDED, &is->id);
\r
9458 is->hFile = cp->hFrom;
\r
9459 cp->hFrom = NULL; /* now owned by InputThread */
\r
9461 CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) InputThread,
\r
9462 (LPVOID) is, CREATE_SUSPENDED, &is->id);
\r
9466 is->sock = cp->sock;
\r
9468 CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,
\r
9469 (LPVOID) is, CREATE_SUSPENDED, &is->id);
\r
9473 is2 = (InputSource *) calloc(1, sizeof(InputSource));
\r
9475 is->sock = cp->sock;
\r
9477 is2->sock = cp->sock2;
\r
9478 is2->second = is2;
\r
9480 CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,
\r
9481 (LPVOID) is, CREATE_SUSPENDED, &is->id);
\r
9483 CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,
\r
9484 (LPVOID) is2, CREATE_SUSPENDED, &is2->id);
\r
9488 if( is->hThread != NULL ) {
\r
9489 ResumeThread( is->hThread );
\r
9492 if( is2 != NULL && is2->hThread != NULL ) {
\r
9493 ResumeThread( is2->hThread );
\r
9497 return (InputSourceRef) is;
\r
9501 RemoveInputSource(InputSourceRef isr)
\r
9505 is = (InputSource *) isr;
\r
9506 is->hThread = NULL; /* tell thread to stop */
\r
9507 CloseHandle(is->hThread);
\r
9508 if (is->second != NULL) {
\r
9509 is->second->hThread = NULL;
\r
9510 CloseHandle(is->second->hThread);
\r
9514 int no_wrap(char *message, int count)
\r
9516 ConsoleOutput(message, count, FALSE);
\r
9521 OutputToProcess(ProcRef pr, char *message, int count, int *outError)
\r
9524 int outCount = SOCKET_ERROR;
\r
9525 ChildProc *cp = (ChildProc *) pr;
\r
9526 static OVERLAPPED ovl;
\r
9527 static int line = 0;
\r
9531 if (appData.noJoin || !appData.useInternalWrap)
\r
9532 return no_wrap(message, count);
\r
9535 int width = get_term_width();
\r
9536 int len = wrap(NULL, message, count, width, &line);
\r
9537 char *msg = malloc(len);
\r
9541 return no_wrap(message, count);
\r
9544 dbgchk = wrap(msg, message, count, width, &line);
\r
9545 if (dbgchk != len && appData.debugMode)
\r
9546 fprintf(debugFP, "wrap(): dbgchk(%d) != len(%d)\n", dbgchk, len);
\r
9547 ConsoleOutput(msg, len, FALSE);
\r
9554 if (ovl.hEvent == NULL) {
\r
9555 ovl.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
\r
9557 ovl.Internal = ovl.InternalHigh = ovl.Offset = ovl.OffsetHigh = 0;
\r
9559 switch (cp->kind) {
\r
9562 outCount = send(cp->sock, message, count, 0);
\r
9563 if (outCount == SOCKET_ERROR) {
\r
9564 *outError = WSAGetLastError();
\r
9566 *outError = NO_ERROR;
\r
9571 if (WriteFile(((ChildProc *)pr)->hTo, message, count,
\r
9572 &dOutCount, NULL)) {
\r
9573 *outError = NO_ERROR;
\r
9574 outCount = (int) dOutCount;
\r
9576 *outError = GetLastError();
\r
9581 *outError = DoWriteFile(((ChildProc *)pr)->hTo, message, count,
\r
9582 &dOutCount, &ovl);
\r
9583 if (*outError == NO_ERROR) {
\r
9584 outCount = (int) dOutCount;
\r
9592 OutputToProcessDelayed(ProcRef pr, char *message, int count, int *outError,
\r
9595 /* Ignore delay, not implemented for WinBoard */
\r
9596 return OutputToProcess(pr, message, count, outError);
\r
9601 CmailSigHandlerCallBack(InputSourceRef isr, VOIDSTAR closure,
\r
9602 char *buf, int count, int error)
\r
9604 DisplayFatalError(_("Not implemented"), 0, 1);
\r
9607 /* see wgamelist.c for Game List functions */
\r
9608 /* see wedittags.c for Edit Tags functions */
\r
9615 char buf[MSG_SIZ];
\r
9618 if (SearchPath(installDir, appData.icsLogon, NULL, MSG_SIZ, buf, &dummy)) {
\r
9619 f = fopen(buf, "r");
\r
9621 ProcessICSInitScript(f);
\r
9629 StartAnalysisClock()
\r
9631 if (analysisTimerEvent) return;
\r
9632 analysisTimerEvent = SetTimer(hwndMain, (UINT) ANALYSIS_TIMER_ID,
\r
9633 (UINT) 2000, NULL);
\r
9637 SetHighlights(int fromX, int fromY, int toX, int toY)
\r
9639 highlightInfo.sq[0].x = fromX;
\r
9640 highlightInfo.sq[0].y = fromY;
\r
9641 highlightInfo.sq[1].x = toX;
\r
9642 highlightInfo.sq[1].y = toY;
\r
9648 highlightInfo.sq[0].x = highlightInfo.sq[0].y =
\r
9649 highlightInfo.sq[1].x = highlightInfo.sq[1].y = -1;
\r
9653 SetPremoveHighlights(int fromX, int fromY, int toX, int toY)
\r
9655 premoveHighlightInfo.sq[0].x = fromX;
\r
9656 premoveHighlightInfo.sq[0].y = fromY;
\r
9657 premoveHighlightInfo.sq[1].x = toX;
\r
9658 premoveHighlightInfo.sq[1].y = toY;
\r
9662 ClearPremoveHighlights()
\r
9664 premoveHighlightInfo.sq[0].x = premoveHighlightInfo.sq[0].y =
\r
9665 premoveHighlightInfo.sq[1].x = premoveHighlightInfo.sq[1].y = -1;
\r
9669 ShutDownFrontEnd()
\r
9671 if (saveSettingsOnExit) SaveSettings(settingsFileName);
\r
9672 DeleteClipboardTempFiles();
\r
9678 if (IsIconic(hwndMain))
\r
9679 ShowWindow(hwndMain, SW_RESTORE);
\r
9681 SetActiveWindow(hwndMain);
\r
9685 * Prototypes for animation support routines
\r
9687 static void ScreenSquare(int column, int row, POINT * pt);
\r
9688 static void Tween( POINT * start, POINT * mid, POINT * finish, int factor,
\r
9689 POINT frames[], int * nFrames);
\r
9695 AnimateAtomicCapture(Board board, int fromX, int fromY, int toX, int toY)
\r
9696 { // [HGM] atomic: animate blast wave
\r
9699 explodeInfo.fromX = fromX;
\r
9700 explodeInfo.fromY = fromY;
\r
9701 explodeInfo.toX = toX;
\r
9702 explodeInfo.toY = toY;
\r
9703 for(i=1; i<4*kFactor; i++) {
\r
9704 explodeInfo.radius = (i*180)/(4*kFactor-1);
\r
9705 DrawPosition(FALSE, board);
\r
9706 Sleep(appData.animSpeed);
\r
9708 explodeInfo.radius = 0;
\r
9709 DrawPosition(TRUE, board);
\r
9713 AnimateMove(board, fromX, fromY, toX, toY)
\r
9720 ChessSquare piece;
\r
9721 POINT start, finish, mid;
\r
9722 POINT frames[kFactor * 2 + 1];
\r
9725 if (!appData.animate) return;
\r
9726 if (doingSizing) return;
\r
9727 if (fromY < 0 || fromX < 0) return;
\r
9728 piece = board[fromY][fromX];
\r
9729 if (piece >= EmptySquare) return;
\r
9731 ScreenSquare(fromX, fromY, &start);
\r
9732 ScreenSquare(toX, toY, &finish);
\r
9734 /* All moves except knight jumps move in straight line */
\r
9735 if (!(abs(fromX-toX) == 1 && abs(fromY-toY) == 2 || abs(fromX-toX) == 2 && abs(fromY-toY) == 1)) {
\r
9736 mid.x = start.x + (finish.x - start.x) / 2;
\r
9737 mid.y = start.y + (finish.y - start.y) / 2;
\r
9739 /* Knight: make straight movement then diagonal */
\r
9740 if (abs(toY - fromY) < abs(toX - fromX)) {
\r
9741 mid.x = start.x + (finish.x - start.x) / 2;
\r
9745 mid.y = start.y + (finish.y - start.y) / 2;
\r
9749 /* Don't use as many frames for very short moves */
\r
9750 if (abs(toY - fromY) + abs(toX - fromX) <= 2)
\r
9751 Tween(&start, &mid, &finish, kFactor - 1, frames, &nFrames);
\r
9753 Tween(&start, &mid, &finish, kFactor, frames, &nFrames);
\r
9755 animInfo.from.x = fromX;
\r
9756 animInfo.from.y = fromY;
\r
9757 animInfo.to.x = toX;
\r
9758 animInfo.to.y = toY;
\r
9759 animInfo.lastpos = start;
\r
9760 animInfo.piece = piece;
\r
9761 for (n = 0; n < nFrames; n++) {
\r
9762 animInfo.pos = frames[n];
\r
9763 DrawPosition(FALSE, NULL);
\r
9764 animInfo.lastpos = animInfo.pos;
\r
9765 Sleep(appData.animSpeed);
\r
9767 animInfo.pos = finish;
\r
9768 DrawPosition(FALSE, NULL);
\r
9769 animInfo.piece = EmptySquare;
\r
9770 Explode(board, fromX, fromY, toX, toY);
\r
9773 /* Convert board position to corner of screen rect and color */
\r
9776 ScreenSquare(column, row, pt)
\r
9777 int column; int row; POINT * pt;
\r
9780 pt->x = lineGap + ((BOARD_WIDTH-1)-column) * (squareSize + lineGap);
\r
9781 pt->y = lineGap + row * (squareSize + lineGap);
\r
9783 pt->x = lineGap + column * (squareSize + lineGap);
\r
9784 pt->y = lineGap + ((BOARD_HEIGHT-1)-row) * (squareSize + lineGap);
\r
9788 /* Generate a series of frame coords from start->mid->finish.
\r
9789 The movement rate doubles until the half way point is
\r
9790 reached, then halves back down to the final destination,
\r
9791 which gives a nice slow in/out effect. The algorithmn
\r
9792 may seem to generate too many intermediates for short
\r
9793 moves, but remember that the purpose is to attract the
\r
9794 viewers attention to the piece about to be moved and
\r
9795 then to where it ends up. Too few frames would be less
\r
9799 Tween(start, mid, finish, factor, frames, nFrames)
\r
9800 POINT * start; POINT * mid;
\r
9801 POINT * finish; int factor;
\r
9802 POINT frames[]; int * nFrames;
\r
9804 int n, fraction = 1, count = 0;
\r
9806 /* Slow in, stepping 1/16th, then 1/8th, ... */
\r
9807 for (n = 0; n < factor; n++)
\r
9809 for (n = 0; n < factor; n++) {
\r
9810 frames[count].x = start->x + (mid->x - start->x) / fraction;
\r
9811 frames[count].y = start->y + (mid->y - start->y) / fraction;
\r
9813 fraction = fraction / 2;
\r
9817 frames[count] = *mid;
\r
9820 /* Slow out, stepping 1/2, then 1/4, ... */
\r
9822 for (n = 0; n < factor; n++) {
\r
9823 frames[count].x = finish->x - (finish->x - mid->x) / fraction;
\r
9824 frames[count].y = finish->y - (finish->y - mid->y) / fraction;
\r
9826 fraction = fraction * 2;
\r
9832 HistorySet( char movelist[][2*MOVE_LEN], int first, int last, int current )
\r
9834 MoveHistorySet( movelist, first, last, current, pvInfoList );
\r
9836 EvalGraphSet( first, last, current, pvInfoList );
\r
9838 MakeEngineOutputTitle();
\r
9842 SettingsPopUp(ChessProgramState *cps)
\r
9843 { // [HGM] wrapper needed because handles must not be passed through back-end
\r
9844 EngineOptionsPopup(savedHwnd, cps);
\r
9847 int flock(int fid, int code)
\r
9849 HANDLE hFile = (HANDLE) _get_osfhandle(fid);
\r
9853 ov.OffsetHigh = 0;
\r
9855 case 1: LockFileEx(hFile, LOCKFILE_EXCLUSIVE_LOCK, 0, 1024, 0, &ov); break; // LOCK_SH
\r
9856 case 2: LockFileEx(hFile, LOCKFILE_EXCLUSIVE_LOCK, 0, 1024, 0, &ov); break; // LOCK_EX
\r
9857 case 3: UnlockFileEx(hFile, 0, 1024, 0, &ov); break; // LOCK_UN
\r
9858 default: return -1;
\r