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, 2012 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 enum ICS_TYPE ics_type;
\r
105 int MySearchPath P((char *installDir, char *name, char *fullname));
\r
106 int MyGetFullPathName P((char *name, char *fullname));
\r
107 void DisplayHoldingsCount(HDC hdc, int x, int y, int align, int copyNumber);
\r
108 VOID NewVariantPopup(HWND hwnd);
\r
109 int FinishMove P((ChessMove moveType, int fromX, int fromY, int toX, int toY,
\r
110 /*char*/int promoChar));
\r
111 void DisplayMove P((int moveNumber));
\r
112 Boolean ParseFEN P((Board board, int *blackPlaysFirst, char *fen));
\r
113 void ChatPopUp P((char *s));
\r
115 ChessSquare piece;
\r
116 POINT pos; /* window coordinates of current pos */
\r
117 POINT lastpos; /* window coordinates of last pos - used for clipping */
\r
118 POINT from; /* board coordinates of the piece's orig pos */
\r
119 POINT to; /* board coordinates of the piece's new pos */
\r
122 static AnimInfo animInfo = { EmptySquare, {-1,-1}, {-1,-1}, {-1,-1} };
\r
125 POINT start; /* window coordinates of start pos */
\r
126 POINT pos; /* window coordinates of current pos */
\r
127 POINT lastpos; /* window coordinates of last pos - used for clipping */
\r
128 POINT from; /* board coordinates of the piece's orig pos */
\r
132 static DragInfo dragInfo = { {-1,-1}, {-1,-1}, {-1,-1}, {-1,-1}, EmptySquare };
\r
135 POINT sq[2]; /* board coordinates of from, to squares */
\r
138 static HighlightInfo highlightInfo = { {{-1, -1}, {-1, -1}} };
\r
139 static HighlightInfo premoveHighlightInfo = { {{-1, -1}, {-1, -1}} };
\r
140 static HighlightInfo partnerHighlightInfo = { {{-1, -1}, {-1, -1}} };
\r
141 static HighlightInfo oldPartnerHighlight = { {{-1, -1}, {-1, -1}} };
\r
143 typedef struct { // [HGM] atomic
\r
144 int fromX, fromY, toX, toY, radius;
\r
147 static ExplodeInfo explodeInfo;
\r
149 /* Window class names */
\r
150 char szAppName[] = "WinBoard";
\r
151 char szConsoleName[] = "WBConsole";
\r
153 /* Title bar text */
\r
154 char szTitle[] = "WinBoard";
\r
155 char szConsoleTitle[] = "I C S Interaction";
\r
158 char *settingsFileName;
\r
159 Boolean saveSettingsOnExit;
\r
160 char installDir[MSG_SIZ];
\r
161 int errorExitStatus;
\r
163 BoardSize boardSize;
\r
164 Boolean chessProgram;
\r
165 //static int boardX, boardY;
\r
166 int minX, minY; // [HGM] placement: volatile limits on upper-left corner
\r
167 int squareSize, lineGap, minorSize;
\r
168 static int winW, winH;
\r
169 static RECT messageRect, whiteRect, blackRect, leftLogoRect, rightLogoRect; // [HGM] logo
\r
170 static int logoHeight = 0;
\r
171 static char messageText[MESSAGE_TEXT_MAX];
\r
172 static int clockTimerEvent = 0;
\r
173 static int loadGameTimerEvent = 0;
\r
174 static int analysisTimerEvent = 0;
\r
175 static DelayedEventCallback delayedTimerCallback;
\r
176 static int delayedTimerEvent = 0;
\r
177 static int buttonCount = 2;
\r
178 char *icsTextMenuString;
\r
180 char *firstChessProgramNames;
\r
181 char *secondChessProgramNames;
\r
183 #define PALETTESIZE 256
\r
185 HINSTANCE hInst; /* current instance */
\r
186 Boolean alwaysOnTop = FALSE;
\r
188 COLORREF lightSquareColor, darkSquareColor, whitePieceColor,
\r
189 blackPieceColor, highlightSquareColor, premoveHighlightColor;
\r
191 ColorClass currentColorClass;
\r
193 static HWND savedHwnd;
\r
194 HWND hCommPort = NULL; /* currently open comm port */
\r
195 static HWND hwndPause; /* pause button */
\r
196 static HBITMAP pieceBitmap[3][(int) BlackPawn]; /* [HGM] nr of bitmaps referred to bP in stead of wK */
\r
197 static HBRUSH lightSquareBrush, darkSquareBrush,
\r
198 blackSquareBrush, /* [HGM] for band between board and holdings */
\r
199 explodeBrush, /* [HGM] atomic */
\r
200 markerBrush, /* [HGM] markers */
\r
201 whitePieceBrush, blackPieceBrush, iconBkgndBrush /*, outlineBrush*/;
\r
202 static POINT gridEndpoints[(BOARD_RANKS + BOARD_FILES + 2) * 2];
\r
203 static DWORD gridVertexCounts[BOARD_RANKS + BOARD_FILES + 2];
\r
204 static HPEN gridPen = NULL;
\r
205 static HPEN highlightPen = NULL;
\r
206 static HPEN premovePen = NULL;
\r
207 static NPLOGPALETTE pLogPal;
\r
208 static BOOL paletteChanged = FALSE;
\r
209 static HICON iconWhite, iconBlack, iconCurrent;
\r
210 static int doingSizing = FALSE;
\r
211 static int lastSizing = 0;
\r
212 static int prevStderrPort;
\r
213 static HBITMAP userLogo;
\r
215 static HBITMAP liteBackTexture = NULL;
\r
216 static HBITMAP darkBackTexture = NULL;
\r
217 static int liteBackTextureMode = BACK_TEXTURE_MODE_PLAIN;
\r
218 static int darkBackTextureMode = BACK_TEXTURE_MODE_PLAIN;
\r
219 static int backTextureSquareSize = 0;
\r
220 static struct { int x; int y; int mode; } backTextureSquareInfo[BOARD_RANKS][BOARD_FILES];
\r
222 #if __GNUC__ && !defined(_winmajor)
\r
223 #define oldDialog 0 /* cygwin doesn't define _winmajor; mingw does */
\r
225 #if defined(_winmajor)
\r
226 #define oldDialog (_winmajor < 4)
\r
228 #define oldDialog 0
\r
232 #define INTERNATIONAL
\r
234 #ifdef INTERNATIONAL
\r
235 # define _(s) T_(s)
\r
241 # define Translate(x, y)
\r
242 # define LoadLanguageFile(s)
\r
245 #ifdef INTERNATIONAL
\r
247 Boolean barbaric; // flag indicating if translation is needed
\r
249 // list of item numbers used in each dialog (used to alter language at run time)
\r
251 #define ABOUTBOX -1 /* not sure why these are needed */
\r
252 #define ABOUTBOX2 -1
\r
254 int dialogItems[][42] = {
\r
255 { ABOUTBOX, IDOK, OPT_MESS, 400 },
\r
256 { DLG_TimeControl, IDC_Babble, OPT_TCUseMoves, OPT_TCUseInc, OPT_TCUseFixed,
\r
257 OPT_TCtext1, OPT_TCtext2, OPT_TCitext1, OPT_TCitext2, OPT_TCftext, GPB_Factors, IDC_Factor1, IDC_Factor2, IDOK, IDCANCEL },
\r
258 { DLG_LoadOptions, OPT_Autostep, OPT_AStext1, OPT_Exact, OPT_Subset, OPT_Struct, OPT_Material, OPT_Range, OPT_Difference,
\r
259 OPT_elo1t, OPT_elo2t, OPT_datet, OPT_Stretch, OPT_Stretcht, OPT_Reversed, OPT_SearchMode, OPT_Mirror, OPT_thresholds, IDOK, IDCANCEL },
\r
260 { DLG_SaveOptions, OPT_Autosave, OPT_AVPrompt, OPT_AVToFile, OPT_AVBrowse,
\r
261 801, OPT_PGN, OPT_Old, OPT_OutOfBookInfo, IDOK, IDCANCEL },
\r
262 { 1536, 1090, IDC_Directories, 1089, 1091, IDOK, IDCANCEL, 1038, IDC_IndexNr, 1037 },
\r
263 { DLG_CommPort, IDOK, IDCANCEL, IDC_Port, IDC_Rate, IDC_Bits, IDC_Parity,
\r
264 IDC_Stop, IDC_Flow, OPT_SerialHelp },
\r
265 { DLG_EditComment, IDOK, OPT_CancelComment, OPT_ClearComment, OPT_EditComment },
\r
266 { DLG_PromotionKing, PB_Chancellor, PB_Archbishop, PB_Queen, PB_Rook,
\r
267 PB_Bishop, PB_Knight, PB_King, IDCANCEL, IDC_Yes, IDC_No, IDC_Centaur },
\r
268 { ABOUTBOX2, IDC_ChessBoard },
\r
269 { DLG_GameList, OPT_GameListLoad, OPT_GameListPrev, OPT_GameListNext,
\r
270 OPT_GameListClose, IDC_GameListDoFilter },
\r
271 { DLG_EditTags, IDOK, OPT_TagsCancel, OPT_EditTags },
\r
272 { DLG_Error, IDOK },
\r
273 { DLG_Colorize, IDOK, IDCANCEL, OPT_ChooseColor, OPT_Bold, OPT_Italic,
\r
274 OPT_Underline, OPT_Strikeout, OPT_Sample },
\r
275 { DLG_Question, IDOK, IDCANCEL, OPT_QuestionText },
\r
276 { DLG_Startup, IDC_Welcome, OPT_ChessEngine, OPT_ChessServer, OPT_View,
\r
277 IDC_SPECIFY_ENG_STATIC, IDC_SPECIFY_SERVER_STATIC, OPT_AnyAdditional,
\r
278 IDOK, IDCANCEL, IDM_HELPCONTENTS },
\r
279 { DLG_IndexNumber, IDC_Index },
\r
280 { DLG_TypeInMove, IDOK, IDCANCEL },
\r
281 { DLG_TypeInName, IDOK, IDCANCEL },
\r
282 { DLG_Sound, IDC_Event, OPT_NoSound, OPT_DefaultBeep, OPT_BuiltInSound,
\r
283 OPT_WavFile, OPT_BrowseSound, OPT_DefaultSounds, IDOK, IDCANCEL, OPT_PlaySound },
\r
284 { DLG_GeneralOptions, IDOK, IDCANCEL, OPT_AlwaysOnTop, OPT_HighlightLastMove,
\r
285 OPT_AlwaysQueen, OPT_PeriodicUpdates, OPT_AnimateDragging, OPT_PonderNextMove,
\r
286 OPT_AnimateMoving, OPT_PopupExitMessage, OPT_AutoFlag, OPT_PopupMoveErrors,
\r
287 OPT_AutoFlipView, OPT_ShowButtonBar, OPT_AutoRaiseBoard, OPT_ShowCoordinates,
\r
288 OPT_Blindfold, OPT_ShowThinking, OPT_HighlightDragging, OPT_TestLegality,
\r
289 OPT_SaveExtPGN, OPT_HideThinkFromHuman, OPT_ExtraInfoInMoveHistory,
\r
290 OPT_HighlightMoveArrow, OPT_AutoLogo ,OPT_SmartMove },
\r
291 { DLG_IcsOptions, IDOK, IDCANCEL, OPT_AutoComment, OPT_AutoKibitz, OPT_AutoObserve,
\r
292 OPT_GetMoveList, OPT_LocalLineEditing, OPT_QuietPlay, OPT_SeekGraph, OPT_AutoRefresh,
\r
293 OPT_BgObserve, OPT_DualBoard, OPT_Premove, OPT_PremoveWhite, OPT_PremoveBlack,
\r
294 OPT_SmartMove, OPT_IcsAlarm, IDC_Sec, OPT_ChooseShoutColor, OPT_ChooseSShoutColor,
\r
295 OPT_ChooseChannel1Color, OPT_ChooseChannelColor, OPT_ChooseKibitzColor,
\r
296 OPT_ChooseTellColor, OPT_ChooseChallengeColor, OPT_ChooseRequestColor,
\r
297 OPT_ChooseSeekColor, OPT_ChooseNormalColor, OPT_ChooseBackgroundColor,
\r
298 OPT_DefaultColors, OPT_DontColorize, IDC_Boxes, GPB_Colors, GPB_Premove,
\r
299 GPB_General, GPB_Alarm },
\r
300 { DLG_BoardOptions, IDOK, IDCANCEL, OPT_SizeTiny, OPT_SizeTeeny, OPT_SizeDinky,
\r
301 OPT_SizePetite, OPT_SizeSlim, OPT_SizeSmall, OPT_SizeMediocre, OPT_SizeMiddling,
\r
302 OPT_SizeAverage, OPT_SizeModerate, OPT_SizeMedium, OPT_SizeBulky, OPT_SizeLarge,
\r
303 OPT_SizeBig, OPT_SizeHuge, OPT_SizeGiant, OPT_SizeColossal, OPT_SizeTitanic,
\r
304 OPT_ChooseLightSquareColor, OPT_ChooseDarkSquareColor, OPT_ChooseWhitePieceColor,
\r
305 OPT_ChooseBlackPieceColor, OPT_ChooseHighlightSquareColor, OPT_ChoosePremoveHighlightColor,
\r
306 OPT_Monochrome, OPT_AllWhite, OPT_UpsideDown, OPT_DefaultBoardColors, GPB_Colors,
\r
307 IDC_Light, IDC_Dark, IDC_White, IDC_Black, IDC_High, IDC_PreHigh, GPB_Size, OPT_Bitmaps, OPT_PieceFont, OPT_Grid },
\r
308 { DLG_NewVariant, IDOK, IDCANCEL, OPT_VariantNormal, OPT_VariantFRC, OPT_VariantWildcastle,
\r
309 OPT_VariantNocastle, OPT_VariantLosers, OPT_VariantGiveaway, OPT_VariantSuicide,
\r
310 OPT_Variant3Check, OPT_VariantTwoKings, OPT_VariantAtomic, OPT_VariantCrazyhouse,
\r
311 OPT_VariantBughouse, OPT_VariantTwilight, OPT_VariantShogi, OPT_VariantSuper,
\r
312 OPT_VariantKnightmate, OPT_VariantBerolina, OPT_VariantCylinder, OPT_VariantFairy,
\r
313 OPT_VariantMakruk, OPT_VariantGothic, OPT_VariantCapablanca, OPT_VariantJanus,
\r
314 OPT_VariantCRC, OPT_VariantFalcon, OPT_VariantCourier, OPT_VariantGreat, OPT_VariantSChess,
\r
315 OPT_VariantShatranj, OPT_VariantXiangqi, GPB_Variant, GPB_Board, IDC_Height,
\r
316 IDC_Width, IDC_Hand, IDC_Pieces, IDC_Def },
\r
317 { DLG_Fonts, IDOK, IDCANCEL, OPT_ChooseClockFont, OPT_ChooseMessageFont,
\r
318 OPT_ChooseCoordFont, OPT_ChooseTagFont, OPT_ChooseCommentsFont, OPT_ChooseConsoleFont, OPT_ChooseMoveHistoryFont, OPT_DefaultFonts,
\r
319 OPT_ClockFont, OPT_MessageFont, OPT_CoordFont, OPT_EditTagsFont, OPT_ChoosePieceFont, OPT_MessageFont8,
\r
320 OPT_SampleGameListFont, OPT_ChooseGameListFont, OPT_MessageFont7,
\r
321 OPT_CommentsFont, OPT_MessageFont5, GPB_Current, GPB_All, OPT_MessageFont6 },
\r
322 { DLG_NewGameFRC, IDC_NFG_Label, IDC_NFG_Random, IDOK, IDCANCEL },
\r
323 { DLG_GameListOptions, IDC_GLT, IDC_GLT_Up, IDC_GLT_Down, IDC_GLT_Restore,
\r
324 IDC_GLT_Default, IDOK, IDCANCEL, IDC_GLT_RestoreTo },
\r
325 { DLG_MoveHistory },
\r
326 { DLG_EvalGraph },
\r
327 { DLG_EngineOutput, IDC_EngineLabel1, IDC_Engine1_NPS, IDC_EngineLabel2, IDC_Engine2_NPS },
\r
328 { DLG_Chat, IDC_Partner, IDC_Clear, IDC_Send, },
\r
329 { DLG_EnginePlayOptions, IDC_EpPonder, IDC_EpShowThinking, IDC_EpHideThinkingHuman,
\r
330 IDC_EpPeriodicUpdates, GPB_Adjudications, IDC_Draw, IDC_Moves, IDC_Threshold,
\r
331 IDC_Centi, IDC_TestClaims, IDC_DetectMates, IDC_MaterialDraws, IDC_TrivialDraws,
\r
332 GPB_Apply, IDC_Rule, IDC_Repeats, IDC_ScoreAbs1, IDC_ScoreAbs2, IDOK, IDCANCEL },
\r
333 { DLG_OptionsUCI, IDC_PolyDir, IDC_BrowseForPolyglotDir, IDC_Hash, IDC_Path,
\r
334 IDC_BrowseForEGTB, IDC_Cache, IDC_UseBook, IDC_BrowseForBook, IDC_CPU, IDC_OwnBook1,
\r
335 IDC_OwnBook2, IDC_Depth, IDC_Variation, IDC_DefGames, IDOK, IDCANCEL },
\r
339 static char languageBuf[70000], *foreign[1000], *english[1000], *languageFile[MSG_SIZ];
\r
340 static int lastChecked;
\r
341 static char oldLanguage[MSG_SIZ], *menuText[10][30];
\r
342 extern int tinyLayout;
\r
343 extern char * menuBarText[][10];
\r
346 LoadLanguageFile(char *name)
\r
347 { //load the file with translations, and make a list of the strings to be translated, and their translations
\r
349 int i=0, j=0, n=0, k;
\r
352 if(!name || name[0] == NULLCHAR) return;
\r
353 snprintf(buf, MSG_SIZ, "%s%s", name, strchr(name, '.') ? "" : ".lng"); // auto-append lng extension
\r
354 appData.language = oldLanguage;
\r
355 if(!strcmp(buf, oldLanguage)) { barbaric = 1; return; } // this language already loaded; just switch on
\r
356 if((f = fopen(buf, "r")) == NULL) return;
\r
357 while((k = fgetc(f)) != EOF) {
\r
358 if(i >= sizeof(languageBuf)) { DisplayError("Language file too big", 0); return; }
\r
359 languageBuf[i] = k;
\r
361 if(languageBuf[n] == '"' && languageBuf[i-1] == '"') {
\r
363 if(p = strstr(languageBuf + n + 1, "\" === \"")) {
\r
364 if(p > languageBuf+n+2 && p+8 < languageBuf+i) {
\r
365 if(j >= sizeof(english)) { DisplayError("Too many translated strings", 0); return; }
\r
366 english[j] = languageBuf + n + 1; *p = 0;
\r
367 foreign[j++] = p + 7; languageBuf[i-1] = 0;
\r
368 //if(appData.debugMode) fprintf(debugFP, "translation: replace '%s' by '%s'\n", english[j-1], foreign[j-1]);
\r
373 } else if(i > 0 && languageBuf[i-1] == '\\') {
\r
375 case 'n': k = '\n'; break;
\r
376 case 'r': k = '\r'; break;
\r
377 case 't': k = '\t'; break;
\r
379 languageBuf[--i] = k;
\r
384 barbaric = (j != 0);
\r
385 safeStrCpy(oldLanguage, buf, sizeof(oldLanguage)/sizeof(oldLanguage[0]) );
\r
390 { // return the translation of the given string
\r
391 // efficiency can be improved a lot...
\r
393 static char buf[MSG_SIZ];
\r
394 //if(appData.debugMode) fprintf(debugFP, "T_(%s)\n", s);
\r
395 if(!barbaric) return s;
\r
396 if(!s) return ""; // sanity
\r
397 while(english[i]) {
\r
398 if(!strcmp(s, english[i])) return foreign[i];
\r
399 if(english[i][0] == '%' && strstr(s, english[i]+1) == s) { // allow translation of strings with variable ending
\r
400 snprintf(buf, MSG_SIZ, "%s%s", foreign[i], s + strlen(english[i]+1)); // keep unmatched portion
\r
409 Translate(HWND hDlg, int dialogID)
\r
410 { // translate all text items in the given dialog
\r
412 char buf[MSG_SIZ], *s;
\r
413 if(!barbaric) return;
\r
414 while(dialogItems[i][0] && dialogItems[i][0] != dialogID) i++; // find the dialog description
\r
415 if(dialogItems[i][0] != dialogID) return; // unknown dialog, should not happen
\r
416 GetWindowText( hDlg, buf, MSG_SIZ );
\r
418 if(strcmp(buf, s)) SetWindowText(hDlg, s); // replace by translated string (if different)
\r
419 for(j=1; k=dialogItems[i][j]; j++) { // translate all listed dialog items
\r
420 GetDlgItemText(hDlg, k, buf, MSG_SIZ);
\r
421 if(strlen(buf) == 0) continue;
\r
423 if(strcmp(buf, s)) SetDlgItemText(hDlg, k, s); // replace by translated string (if different)
\r
428 TranslateOneMenu(int i, HMENU subMenu)
\r
431 static MENUITEMINFO info;
\r
433 info.cbSize = sizeof(MENUITEMINFO);
\r
434 info.fMask = MIIM_STATE | MIIM_TYPE;
\r
435 for(j=GetMenuItemCount(subMenu)-1; j>=0; j--){
\r
437 info.dwTypeData = buf;
\r
438 info.cch = sizeof(buf);
\r
439 GetMenuItemInfo(subMenu, j, TRUE, &info);
\r
441 if(menuText[i][j]) safeStrCpy(buf, menuText[i][j], sizeof(buf)/sizeof(buf[0]) );
\r
442 else menuText[i][j] = strdup(buf); // remember original on first change
\r
444 if(buf[0] == NULLCHAR) continue;
\r
445 info.dwTypeData = T_(buf);
\r
446 info.cch = strlen(buf)+1;
\r
447 SetMenuItemInfo(subMenu, j, TRUE, &info);
\r
453 TranslateMenus(int addLanguage)
\r
456 WIN32_FIND_DATA fileData;
\r
458 #define IDM_English 1970
\r
460 HMENU mainMenu = GetMenu(hwndMain);
\r
461 for (i=GetMenuItemCount(mainMenu)-1; i>=0; i--) {
\r
462 HMENU subMenu = GetSubMenu(mainMenu, i);
\r
463 ModifyMenu(mainMenu, i, MF_STRING|MF_BYPOSITION|MF_POPUP|EnableMenuItem(mainMenu, i, MF_BYPOSITION),
\r
464 (UINT) subMenu, T_(menuBarText[tinyLayout][i]));
\r
465 TranslateOneMenu(i, subMenu);
\r
467 DrawMenuBar(hwndMain);
\r
470 if(!addLanguage) return;
\r
471 if((hFind = FindFirstFile("*.LNG", &fileData)) != INVALID_HANDLE_VALUE) {
\r
472 HMENU mainMenu = GetMenu(hwndMain);
\r
473 HMENU subMenu = GetSubMenu(mainMenu, GetMenuItemCount(mainMenu)-1);
\r
474 AppendMenu(subMenu, MF_SEPARATOR, (UINT_PTR) 0, NULL);
\r
475 AppendMenu(subMenu, MF_ENABLED|MF_STRING|(barbaric?MF_UNCHECKED:MF_CHECKED), (UINT_PTR) IDM_English, (LPCTSTR) "English");
\r
476 i = 0; lastChecked = IDM_English;
\r
478 char *p, *q = fileData.cFileName;
\r
479 int checkFlag = MF_UNCHECKED;
\r
480 languageFile[i] = strdup(q);
\r
481 if(barbaric && !strcmp(oldLanguage, q)) {
\r
482 checkFlag = MF_CHECKED;
\r
483 lastChecked = IDM_English + i + 1;
\r
484 CheckMenuItem(mainMenu, IDM_English, MF_BYCOMMAND|MF_UNCHECKED);
\r
486 *q = ToUpper(*q); while(*++q) *q = ToLower(*q);
\r
487 p = strstr(fileData.cFileName, ".lng");
\r
489 AppendMenu(subMenu, MF_ENABLED|MF_STRING|checkFlag, (UINT_PTR) IDM_English + ++i, (LPCTSTR) fileData.cFileName);
\r
490 } while(FindNextFile(hFind, &fileData));
\r
497 #define IDM_RecentEngines 3000
\r
500 RecentEngineMenu (char *s)
\r
502 if(appData.icsActive) return;
\r
503 if(appData.recentEngines > 0 && *s) { // feature is on, and list non-empty
\r
504 HMENU mainMenu = GetMenu(hwndMain);
\r
505 HMENU subMenu = GetSubMenu(mainMenu, 5); // Engine menu
\r
506 int i=IDM_RecentEngines;
\r
507 recentEngines = strdup(appData.recentEngineList); // remember them as they are in menu
\r
508 AppendMenu(subMenu, MF_SEPARATOR, (UINT_PTR) 0, NULL);
\r
510 char *p = strchr(s, '\n');
\r
511 if(p == NULL) return; // malformed!
\r
513 AppendMenu(subMenu, MF_ENABLED|MF_STRING|MF_UNCHECKED, (UINT_PTR) i++, (LPCTSTR) s);
\r
527 int cliWidth, cliHeight;
\r
530 SizeInfo sizeInfo[] =
\r
532 { "tiny", 21, 0, 1, 1, 0, 0 },
\r
533 { "teeny", 25, 1, 1, 1, 0, 0 },
\r
534 { "dinky", 29, 1, 1, 1, 0, 0 },
\r
535 { "petite", 33, 1, 1, 1, 0, 0 },
\r
536 { "slim", 37, 2, 1, 0, 0, 0 },
\r
537 { "small", 40, 2, 1, 0, 0, 0 },
\r
538 { "mediocre", 45, 2, 1, 0, 0, 0 },
\r
539 { "middling", 49, 2, 0, 0, 0, 0 },
\r
540 { "average", 54, 2, 0, 0, 0, 0 },
\r
541 { "moderate", 58, 3, 0, 0, 0, 0 },
\r
542 { "medium", 64, 3, 0, 0, 0, 0 },
\r
543 { "bulky", 72, 3, 0, 0, 0, 0 },
\r
544 { "large", 80, 3, 0, 0, 0, 0 },
\r
545 { "big", 87, 3, 0, 0, 0, 0 },
\r
546 { "huge", 95, 3, 0, 0, 0, 0 },
\r
547 { "giant", 108, 3, 0, 0, 0, 0 },
\r
548 { "colossal", 116, 4, 0, 0, 0, 0 },
\r
549 { "titanic", 129, 4, 0, 0, 0, 0 },
\r
550 { NULL, 0, 0, 0, 0, 0, 0 }
\r
553 #define MF(x) {x, {{0,}, 0. }, {0, }, 0}
\r
554 MyFont fontRec[NUM_SIZES][NUM_FONTS] =
\r
556 { 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
557 { 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
558 { 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
559 { 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
560 { 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
561 { 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
562 { 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
563 { 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
564 { 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
565 { 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
566 { 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
567 { 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
568 { 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
569 { 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
570 { 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
571 { 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
572 { 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
573 { 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
576 MyFont *font[NUM_SIZES][NUM_FONTS];
\r
585 #define BUTTON_WIDTH (tinyLayout ? 16 : 32)
\r
586 #define N_BUTTONS 5
\r
588 MyButtonDesc buttonDesc[N_BUTTONS] =
\r
590 {"<<", IDM_ToStart, NULL, NULL},
\r
591 {"<", IDM_Backward, NULL, NULL},
\r
592 {"P", IDM_Pause, NULL, NULL},
\r
593 {">", IDM_Forward, NULL, NULL},
\r
594 {">>", IDM_ToEnd, NULL, NULL},
\r
597 int tinyLayout = 0, smallLayout = 0;
\r
598 #define MENU_BAR_ITEMS 9
\r
599 char *menuBarText[2][MENU_BAR_ITEMS+1] = {
\r
600 { N_("&File"), N_("&Edit"), N_("&View"), N_("&Mode"), N_("&Action"), N_("E&ngine"), N_("&Options"), N_("&Help"), NULL },
\r
601 { N_("&F"), N_("&E"), N_("&V"), N_("&M"), N_("&A"), N_("&N"), N_("&O"), N_("&H"), NULL },
\r
605 MySound sounds[(int)NSoundClasses];
\r
606 MyTextAttribs textAttribs[(int)NColorClasses];
\r
608 MyColorizeAttribs colorizeAttribs[] = {
\r
609 { (COLORREF)0, 0, N_("Shout Text") },
\r
610 { (COLORREF)0, 0, N_("SShout/CShout") },
\r
611 { (COLORREF)0, 0, N_("Channel 1 Text") },
\r
612 { (COLORREF)0, 0, N_("Channel Text") },
\r
613 { (COLORREF)0, 0, N_("Kibitz Text") },
\r
614 { (COLORREF)0, 0, N_("Tell Text") },
\r
615 { (COLORREF)0, 0, N_("Challenge Text") },
\r
616 { (COLORREF)0, 0, N_("Request Text") },
\r
617 { (COLORREF)0, 0, N_("Seek Text") },
\r
618 { (COLORREF)0, 0, N_("Normal Text") },
\r
619 { (COLORREF)0, 0, N_("None") }
\r
624 static char *commentTitle;
\r
625 static char *commentText;
\r
626 static int commentIndex;
\r
627 static Boolean editComment = FALSE;
\r
630 char errorTitle[MSG_SIZ];
\r
631 char errorMessage[2*MSG_SIZ];
\r
632 HWND errorDialog = NULL;
\r
633 BOOLEAN moveErrorMessageUp = FALSE;
\r
634 BOOLEAN consoleEcho = TRUE;
\r
635 CHARFORMAT consoleCF;
\r
636 COLORREF consoleBackgroundColor;
\r
638 char *programVersion;
\r
644 typedef int CPKind;
\r
653 SOCKET sock2; /* stderr socket for OpenRcmd */
\r
656 #define INPUT_SOURCE_BUF_SIZE 4096
\r
658 typedef struct _InputSource {
\r
665 char buf[INPUT_SOURCE_BUF_SIZE];
\r
669 InputCallback func;
\r
670 struct _InputSource *second; /* for stderr thread on CPRcmd */
\r
674 InputSource *consoleInputSource;
\r
679 VOID ConsoleOutput(char* data, int length, int forceVisible);
\r
680 VOID ConsoleCreate();
\r
682 ConsoleWndProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam);
\r
683 VOID ColorizeTextPopup(HWND hwnd, ColorClass cc);
\r
684 VOID PrintCommSettings(FILE *f, char *name, DCB *dcb);
\r
685 VOID ParseCommSettings(char *arg, DCB *dcb);
\r
687 StartupDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam);
\r
688 VOID APIENTRY MenuPopup(HWND hwnd, POINT pt, HMENU hmenu, UINT def);
\r
689 void ParseIcsTextMenu(char *icsTextMenuString);
\r
690 VOID PopUpNameDialog(char firstchar);
\r
691 VOID UpdateSampleText(HWND hDlg, int id, MyColorizeAttribs *mca);
\r
695 int GameListOptions();
\r
697 int dummy; // [HGM] for obsolete args
\r
699 HWND hwndMain = NULL; /* root window*/
\r
700 HWND hwndConsole = NULL;
\r
701 HWND commentDialog = NULL;
\r
702 HWND moveHistoryDialog = NULL;
\r
703 HWND evalGraphDialog = NULL;
\r
704 HWND engineOutputDialog = NULL;
\r
705 HWND gameListDialog = NULL;
\r
706 HWND editTagsDialog = NULL;
\r
708 int commentUp = FALSE;
\r
710 WindowPlacement wpMain;
\r
711 WindowPlacement wpConsole;
\r
712 WindowPlacement wpComment;
\r
713 WindowPlacement wpMoveHistory;
\r
714 WindowPlacement wpEvalGraph;
\r
715 WindowPlacement wpEngineOutput;
\r
716 WindowPlacement wpGameList;
\r
717 WindowPlacement wpTags;
\r
719 VOID EngineOptionsPopup(); // [HGM] settings
\r
721 VOID GothicPopUp(char *title, VariantClass variant);
\r
723 * Setting "frozen" should disable all user input other than deleting
\r
724 * the window. We do this while engines are initializing themselves.
\r
726 static int frozen = 0;
\r
727 static int oldMenuItemState[MENU_BAR_ITEMS];
\r
733 if (frozen) return;
\r
735 hmenu = GetMenu(hwndMain);
\r
736 for (i=0; i<MENU_BAR_ITEMS; i++) {
\r
737 oldMenuItemState[i] = EnableMenuItem(hmenu, i, MF_BYPOSITION|MF_GRAYED);
\r
739 DrawMenuBar(hwndMain);
\r
742 /* Undo a FreezeUI */
\r
748 if (!frozen) return;
\r
750 hmenu = GetMenu(hwndMain);
\r
751 for (i=0; i<MENU_BAR_ITEMS; i++) {
\r
752 EnableMenuItem(hmenu, i, MF_BYPOSITION|oldMenuItemState[i]);
\r
754 DrawMenuBar(hwndMain);
\r
757 /*static*/ int fromX = -1, fromY = -1, toX, toY; // [HGM] moved upstream, so JAWS can use them
\r
759 /* JAWS preparation patch (WinBoard for the sight impaired). Define required insertions as empty */
\r
765 #define JAWS_ALT_INTERCEPT
\r
766 #define JAWS_KBUP_NAVIGATION
\r
767 #define JAWS_KBDOWN_NAVIGATION
\r
768 #define JAWS_MENU_ITEMS
\r
769 #define JAWS_SILENCE
\r
770 #define JAWS_REPLAY
\r
772 #define JAWS_COPYRIGHT
\r
773 #define JAWS_DELETE(X) X
\r
774 #define SAYMACHINEMOVE()
\r
778 /*---------------------------------------------------------------------------*\
\r
782 \*---------------------------------------------------------------------------*/
\r
785 WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
\r
786 LPSTR lpCmdLine, int nCmdShow)
\r
789 HANDLE hAccelMain, hAccelNoAlt, hAccelNoICS;
\r
790 // INITCOMMONCONTROLSEX ex;
\r
794 LoadLibrary("RICHED32.DLL");
\r
795 consoleCF.cbSize = sizeof(CHARFORMAT);
\r
797 if (!InitApplication(hInstance)) {
\r
800 if (!InitInstance(hInstance, nCmdShow, lpCmdLine)) {
\r
807 // InitCommonControlsEx(&ex);
\r
808 InitCommonControls();
\r
810 hAccelMain = LoadAccelerators (hInstance, szAppName);
\r
811 hAccelNoAlt = LoadAccelerators (hInstance, "NO_ALT");
\r
812 hAccelNoICS = LoadAccelerators( hInstance, "NO_ICS"); /* [AS] No Ctrl-V on ICS!!! */
\r
814 /* Acquire and dispatch messages until a WM_QUIT message is received. */
\r
816 while (GetMessage(&msg, /* message structure */
\r
817 NULL, /* handle of window receiving the message */
\r
818 0, /* lowest message to examine */
\r
819 0)) /* highest message to examine */
\r
822 if(msg.message == WM_CHAR && msg.wParam == '\t') {
\r
823 // [HGM] navigate: switch between all windows with tab
\r
824 HWND e1 = NULL, e2 = NULL, mh = NULL, hInput = NULL, hText = NULL;
\r
825 int i, currentElement = 0;
\r
827 // first determine what element of the chain we come from (if any)
\r
828 if(appData.icsActive) {
\r
829 hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
830 hText = GetDlgItem(hwndConsole, OPT_ConsoleText);
\r
832 if(engineOutputDialog && EngineOutputIsUp()) {
\r
833 e1 = GetDlgItem(engineOutputDialog, IDC_EngineMemo1);
\r
834 e2 = GetDlgItem(engineOutputDialog, IDC_EngineMemo2);
\r
836 if(moveHistoryDialog && MoveHistoryIsUp()) {
\r
837 mh = GetDlgItem(moveHistoryDialog, IDC_MoveHistory);
\r
839 if(msg.hwnd == hwndMain) currentElement = 7 ; else
\r
840 if(msg.hwnd == engineOutputDialog) currentElement = 2; else
\r
841 if(msg.hwnd == e1) currentElement = 2; else
\r
842 if(msg.hwnd == e2) currentElement = 3; else
\r
843 if(msg.hwnd == moveHistoryDialog) currentElement = 4; else
\r
844 if(msg.hwnd == mh) currentElement = 4; else
\r
845 if(msg.hwnd == evalGraphDialog) currentElement = 6; else
\r
846 if(msg.hwnd == hText) currentElement = 5; else
\r
847 if(msg.hwnd == hInput) currentElement = 6; else
\r
848 for (i = 0; i < N_BUTTONS; i++) {
\r
849 if (buttonDesc[i].hwnd == msg.hwnd) { currentElement = 1; break; }
\r
852 // determine where to go to
\r
853 if(currentElement) { HWND h = NULL; int direction = GetKeyState(VK_SHIFT) < 0 ? -1 : 1;
\r
855 currentElement = (currentElement + direction) % 7;
\r
856 switch(currentElement) {
\r
858 h = hwndMain; break; // passing this case always makes the loop exit
\r
860 h = buttonDesc[0].hwnd; break; // could be NULL
\r
862 if(!EngineOutputIsUp()) continue; // skip closed auxiliary windows
\r
865 if(!EngineOutputIsUp()) continue;
\r
868 if(!MoveHistoryIsUp()) continue;
\r
870 // case 6: // input to eval graph does not seem to get here!
\r
871 // if(!EvalGraphIsUp()) continue;
\r
872 // h = evalGraphDialog; break;
\r
874 if(!appData.icsActive) continue;
\r
878 if(!appData.icsActive) continue;
\r
884 if(currentElement > 4 && IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);
\r
885 if(currentElement < 5 && IsIconic(hwndMain)) ShowWindow(hwndMain, SW_RESTORE); // all open together
\r
888 continue; // this message now has been processed
\r
892 if (!(commentDialog && IsDialogMessage(commentDialog, &msg)) &&
\r
893 !(moveHistoryDialog && IsDialogMessage(moveHistoryDialog, &msg)) &&
\r
894 !(evalGraphDialog && IsDialogMessage(evalGraphDialog, &msg)) &&
\r
895 !(engineOutputDialog && IsDialogMessage(engineOutputDialog, &msg)) &&
\r
896 !(editTagsDialog && IsDialogMessage(editTagsDialog, &msg)) &&
\r
897 !(gameListDialog && IsDialogMessage(gameListDialog, &msg)) &&
\r
898 !(errorDialog && IsDialogMessage(errorDialog, &msg)) &&
\r
899 !(!frozen && TranslateAccelerator(hwndMain, hAccelMain, &msg)) && JAWS_ACCEL
\r
900 !(!hwndConsole && TranslateAccelerator(hwndMain, hAccelNoICS, &msg)) &&
\r
901 !(!hwndConsole && TranslateAccelerator(hwndMain, hAccelNoAlt, &msg))) {
\r
902 int done = 0, i; // [HGM] chat: dispatch cat-box messages
\r
903 for(i=0; i<MAX_CHAT; i++)
\r
904 if(chatHandle[i] && IsDialogMessage(chatHandle[i], &msg)) {
\r
907 if(done) continue; // [HGM] chat: end patch
\r
908 TranslateMessage(&msg); /* Translates virtual key codes */
\r
909 DispatchMessage(&msg); /* Dispatches message to window */
\r
914 return (msg.wParam); /* Returns the value from PostQuitMessage */
\r
917 /*---------------------------------------------------------------------------*\
\r
919 * Initialization functions
\r
921 \*---------------------------------------------------------------------------*/
\r
925 { // update user logo if necessary
\r
926 static char oldUserName[MSG_SIZ], dir[MSG_SIZ], *curName;
\r
928 if(appData.autoLogo) {
\r
929 curName = UserName();
\r
930 if(strcmp(curName, oldUserName)) {
\r
931 GetCurrentDirectory(MSG_SIZ, dir);
\r
932 SetCurrentDirectory(installDir);
\r
933 snprintf(oldUserName, MSG_SIZ, "logos\\%s.bmp", curName);
\r
934 userLogo = LoadImage( 0, oldUserName, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );
\r
935 safeStrCpy(oldUserName, curName, sizeof(oldUserName)/sizeof(oldUserName[0]) );
\r
936 if(userLogo == NULL)
\r
937 userLogo = LoadImage( 0, "logos\\dummy.bmp", IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );
\r
938 SetCurrentDirectory(dir); /* return to prev directory */
\r
944 InitApplication(HINSTANCE hInstance)
\r
948 /* Fill in window class structure with parameters that describe the */
\r
951 wc.style = CS_HREDRAW | CS_VREDRAW; /* Class style(s). */
\r
952 wc.lpfnWndProc = (WNDPROC)WndProc; /* Window Procedure */
\r
953 wc.cbClsExtra = 0; /* No per-class extra data. */
\r
954 wc.cbWndExtra = 0; /* No per-window extra data. */
\r
955 wc.hInstance = hInstance; /* Owner of this class */
\r
956 wc.hIcon = LoadIcon(hInstance, "icon_white");
\r
957 wc.hCursor = LoadCursor(NULL, IDC_ARROW); /* Cursor */
\r
958 wc.hbrBackground = (HBRUSH)(COLOR_WINDOW+1); /* Default color */
\r
959 wc.lpszMenuName = szAppName; /* Menu name from .RC */
\r
960 wc.lpszClassName = szAppName; /* Name to register as */
\r
962 /* Register the window class and return success/failure code. */
\r
963 if (!RegisterClass(&wc)) return FALSE;
\r
965 wc.style = CS_HREDRAW | CS_VREDRAW;
\r
966 wc.lpfnWndProc = (WNDPROC)ConsoleWndProc;
\r
968 wc.cbWndExtra = DLGWINDOWEXTRA;
\r
969 wc.hInstance = hInstance;
\r
970 wc.hIcon = LoadIcon(hInstance, "icon_white");
\r
971 wc.hCursor = LoadCursor(NULL, IDC_ARROW);
\r
972 wc.hbrBackground = (HBRUSH)(COLOR_MENU+1);
\r
973 wc.lpszMenuName = NULL;
\r
974 wc.lpszClassName = szConsoleName;
\r
976 if (!RegisterClass(&wc)) return FALSE;
\r
981 /* Set by InitInstance, used by EnsureOnScreen */
\r
982 int screenHeight, screenWidth;
\r
985 EnsureOnScreen(int *x, int *y, int minX, int minY)
\r
987 // int gap = GetSystemMetrics(SM_CYFRAME) + GetSystemMetrics(SM_CYCAPTION);
\r
988 /* Be sure window at (x,y) is not off screen (or even mostly off screen) */
\r
989 if (*x > screenWidth - 32) *x = 0;
\r
990 if (*y > screenHeight - 32) *y = 0;
\r
991 if (*x < minX) *x = minX;
\r
992 if (*y < minY) *y = minY;
\r
996 LoadLogo(ChessProgramState *cps, int n, Boolean ics)
\r
998 char buf[MSG_SIZ], dir[MSG_SIZ];
\r
999 GetCurrentDirectory(MSG_SIZ, dir);
\r
1000 SetCurrentDirectory(installDir);
\r
1001 if( appData.logo[n] && appData.logo[n][0] != NULLCHAR) {
\r
1002 cps->programLogo = LoadImage( 0, appData.logo[n], IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );
\r
1004 if (cps->programLogo == NULL && appData.debugMode) {
\r
1005 fprintf( debugFP, "Unable to load logo bitmap '%s'\n", appData.logo[n] );
\r
1007 } else if(appData.autoLogo) {
\r
1008 if(ics) { // [HGM] logo: in ICS mode second can be used for ICS
\r
1009 sprintf(buf, "logos\\%s.bmp", appData.icsHost);
\r
1010 cps->programLogo = LoadImage( 0, buf, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );
\r
1012 if(appData.directory[n] && appData.directory[n][0]) {
\r
1013 SetCurrentDirectory(appData.directory[n]);
\r
1014 cps->programLogo = LoadImage( 0, "logo.bmp", IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );
\r
1017 SetCurrentDirectory(dir); /* return to prev directory */
\r
1023 ZeroMemory( &backTextureSquareInfo, sizeof(backTextureSquareInfo) );
\r
1024 backTextureSquareSize = 0; // kludge to force recalculation of texturemode
\r
1026 if( appData.liteBackTextureFile && appData.liteBackTextureFile[0] != NULLCHAR && appData.liteBackTextureFile[0] != '*' ) {
\r
1027 liteBackTexture = LoadImage( 0, appData.liteBackTextureFile, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );
\r
1028 liteBackTextureMode = appData.liteBackTextureMode;
\r
1030 if (liteBackTexture == NULL && appData.debugMode) {
\r
1031 fprintf( debugFP, "Unable to load lite texture bitmap '%s'\n", appData.liteBackTextureFile );
\r
1035 if( appData.darkBackTextureFile && appData.darkBackTextureFile[0] != NULLCHAR && appData.darkBackTextureFile[0] != '*' ) {
\r
1036 darkBackTexture = LoadImage( 0, appData.darkBackTextureFile, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );
\r
1037 darkBackTextureMode = appData.darkBackTextureMode;
\r
1039 if (darkBackTexture == NULL && appData.debugMode) {
\r
1040 fprintf( debugFP, "Unable to load dark texture bitmap '%s'\n", appData.darkBackTextureFile );
\r
1046 InitInstance(HINSTANCE hInstance, int nCmdShow, LPSTR lpCmdLine)
\r
1048 HWND hwnd; /* Main window handle. */
\r
1050 WINDOWPLACEMENT wp;
\r
1053 hInst = hInstance; /* Store instance handle in our global variable */
\r
1054 programName = szAppName;
\r
1056 if (SearchPath(NULL, "WinBoard.exe", NULL, MSG_SIZ, installDir, &filepart)) {
\r
1057 *filepart = NULLCHAR;
\r
1059 GetCurrentDirectory(MSG_SIZ, installDir);
\r
1061 gameInfo.boardWidth = gameInfo.boardHeight = 8; // [HGM] won't have open window otherwise
\r
1062 screenWidth = screenHeight = 1000; // [HGM] placement: kludge to allow calling EnsureOnScreen from InitAppData
\r
1063 InitAppData(lpCmdLine); /* Get run-time parameters */
\r
1064 /* xboard, and older WinBoards, controlled the move sound with the
\r
1065 appData.ringBellAfterMoves option. In the current WinBoard, we
\r
1066 always turn the option on (so that the backend will call us),
\r
1067 then let the user turn the sound off by setting it to silence if
\r
1068 desired. To accommodate old winboard.ini files saved by old
\r
1069 versions of WinBoard, we also turn off the sound if the option
\r
1070 was initially set to false. [HGM] taken out of InitAppData */
\r
1071 if (!appData.ringBellAfterMoves) {
\r
1072 sounds[(int)SoundMove].name = strdup("");
\r
1073 appData.ringBellAfterMoves = TRUE;
\r
1075 if (appData.debugMode) {
\r
1076 debugFP = fopen(appData.nameOfDebugFile, "w");
\r
1077 setbuf(debugFP, NULL);
\r
1080 LoadLanguageFile(appData.language);
\r
1084 // InitEngineUCI( installDir, &first ); // [HGM] incorporated in InitBackEnd1()
\r
1085 // InitEngineUCI( installDir, &second );
\r
1087 /* Create a main window for this application instance. */
\r
1088 hwnd = CreateWindow(szAppName, szTitle,
\r
1089 (WS_OVERLAPPEDWINDOW & ~WS_MAXIMIZEBOX),
\r
1090 CW_USEDEFAULT, 0, CW_USEDEFAULT, 0,
\r
1091 NULL, NULL, hInstance, NULL);
\r
1094 /* If window could not be created, return "failure" */
\r
1099 /* [HGM] logo: Load logos if specified (must be done before InitDrawingSizes) */
\r
1100 LoadLogo(&first, 0, FALSE);
\r
1101 LoadLogo(&second, 1, appData.icsActive);
\r
1105 iconWhite = LoadIcon(hInstance, "icon_white");
\r
1106 iconBlack = LoadIcon(hInstance, "icon_black");
\r
1107 iconCurrent = iconWhite;
\r
1108 InitDrawingColors();
\r
1109 screenHeight = GetSystemMetrics(SM_CYSCREEN);
\r
1110 screenWidth = GetSystemMetrics(SM_CXSCREEN);
\r
1111 for (ibs = (int) NUM_SIZES - 1; ibs >= 0; ibs--) {
\r
1112 /* Compute window size for each board size, and use the largest
\r
1113 size that fits on this screen as the default. */
\r
1114 InitDrawingSizes((BoardSize)(ibs+1000), 0);
\r
1115 if (boardSize == (BoardSize)-1 &&
\r
1116 winH <= screenHeight
\r
1117 - GetSystemMetrics(SM_CYFRAME) - GetSystemMetrics(SM_CYCAPTION) - 10
\r
1118 && winW <= screenWidth) {
\r
1119 boardSize = (BoardSize)ibs;
\r
1123 InitDrawingSizes(boardSize, 0);
\r
1124 RecentEngineMenu(appData.recentEngineList);
\r
1126 buttonCount = GetSystemMetrics(SM_CMOUSEBUTTONS);
\r
1128 /* [AS] Load textures if specified */
\r
1131 mysrandom( (unsigned) time(NULL) );
\r
1133 /* [AS] Restore layout */
\r
1134 if( wpMoveHistory.visible ) {
\r
1135 MoveHistoryPopUp();
\r
1138 if( wpEvalGraph.visible ) {
\r
1142 if( wpEngineOutput.visible ) {
\r
1143 EngineOutputPopUp();
\r
1146 /* Make the window visible; update its client area; and return "success" */
\r
1147 EnsureOnScreen(&wpMain.x, &wpMain.y, minX, minY);
\r
1148 wp.length = sizeof(WINDOWPLACEMENT);
\r
1150 wp.showCmd = nCmdShow;
\r
1151 wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;
\r
1152 wp.rcNormalPosition.left = wpMain.x;
\r
1153 wp.rcNormalPosition.right = wpMain.x + wpMain.width;
\r
1154 wp.rcNormalPosition.top = wpMain.y;
\r
1155 wp.rcNormalPosition.bottom = wpMain.y + wpMain.height;
\r
1156 SetWindowPlacement(hwndMain, &wp);
\r
1158 InitBackEnd2(); // [HGM] moved until after all windows placed, to save correct position if fatal error on engine start
\r
1160 if(!appData.noGUI) SetWindowPos(hwndMain, alwaysOnTop ? HWND_TOPMOST : HWND_NOTOPMOST,
\r
1161 0, 0, 0, 0, SWP_NOMOVE|SWP_NOSIZE);
\r
1163 if (hwndConsole) {
\r
1165 SetWindowPos(hwndConsole, alwaysOnTop ? HWND_TOPMOST : HWND_NOTOPMOST,
\r
1166 0, 0, 0, 0, SWP_NOMOVE|SWP_NOSIZE);
\r
1168 ShowWindow(hwndConsole, nCmdShow);
\r
1169 SetActiveWindow(hwndConsole);
\r
1171 if(!appData.noGUI) UpdateWindow(hwnd); else ShowWindow(hwnd, SW_MINIMIZE);
\r
1172 if(gameListDialog) SetFocus(gameListDialog); // [HGM] jaws: for if we clicked multi-game game file
\r
1181 HMENU hmenu = GetMenu(hwndMain);
\r
1183 (void) EnableMenuItem(hmenu, IDM_CommPort,
\r
1184 MF_BYCOMMAND|((appData.icsActive &&
\r
1185 *appData.icsCommPort != NULLCHAR) ?
\r
1186 MF_ENABLED : MF_GRAYED));
\r
1187 (void) CheckMenuItem(hmenu, IDM_SaveSettingsOnExit,
\r
1188 MF_BYCOMMAND|(saveSettingsOnExit ?
\r
1189 MF_CHECKED : MF_UNCHECKED));
\r
1192 //---------------------------------------------------------------------------------------------------------
\r
1194 #define ICS_TEXT_MENU_SIZE (IDM_CommandXLast - IDM_CommandX + 1)
\r
1195 #define XBOARD FALSE
\r
1197 #define OPTCHAR "/"
\r
1198 #define SEPCHAR "="
\r
1199 #define TOPLEVEL 0
\r
1203 // front-end part of option handling
\r
1206 LFfromMFP(LOGFONT* lf, MyFontParams *mfp)
\r
1208 HDC hdc = CreateDC("DISPLAY", NULL, NULL, NULL);
\r
1209 lf->lfHeight = -(int)(mfp->pointSize * GetDeviceCaps(hdc, LOGPIXELSY) / 72.0 + 0.5);
\r
1212 lf->lfEscapement = 0;
\r
1213 lf->lfOrientation = 0;
\r
1214 lf->lfWeight = mfp->bold ? FW_BOLD : FW_NORMAL;
\r
1215 lf->lfItalic = mfp->italic;
\r
1216 lf->lfUnderline = mfp->underline;
\r
1217 lf->lfStrikeOut = mfp->strikeout;
\r
1218 lf->lfCharSet = mfp->charset;
\r
1219 lf->lfOutPrecision = OUT_DEFAULT_PRECIS;
\r
1220 lf->lfClipPrecision = CLIP_DEFAULT_PRECIS;
\r
1221 lf->lfQuality = DEFAULT_QUALITY;
\r
1222 lf->lfPitchAndFamily = DEFAULT_PITCH|FF_DONTCARE;
\r
1223 safeStrCpy(lf->lfFaceName, mfp->faceName, sizeof(lf->lfFaceName)/sizeof(lf->lfFaceName[0]) );
\r
1227 CreateFontInMF(MyFont *mf)
\r
1229 LFfromMFP(&mf->lf, &mf->mfp);
\r
1230 if (mf->hf) DeleteObject(mf->hf);
\r
1231 mf->hf = CreateFontIndirect(&mf->lf);
\r
1234 // [HGM] This platform-dependent table provides the location for storing the color info
\r
1236 colorVariable[] = {
\r
1237 &whitePieceColor,
\r
1238 &blackPieceColor,
\r
1239 &lightSquareColor,
\r
1240 &darkSquareColor,
\r
1241 &highlightSquareColor,
\r
1242 &premoveHighlightColor,
\r
1244 &consoleBackgroundColor,
\r
1245 &appData.fontForeColorWhite,
\r
1246 &appData.fontBackColorWhite,
\r
1247 &appData.fontForeColorBlack,
\r
1248 &appData.fontBackColorBlack,
\r
1249 &appData.evalHistColorWhite,
\r
1250 &appData.evalHistColorBlack,
\r
1251 &appData.highlightArrowColor,
\r
1254 /* Command line font name parser. NULL name means do nothing.
\r
1255 Syntax like "Courier New:10.0 bi" or "Arial:10" or "Arial:10b"
\r
1256 For backward compatibility, syntax without the colon is also
\r
1257 accepted, but font names with digits in them won't work in that case.
\r
1260 ParseFontName(char *name, MyFontParams *mfp)
\r
1263 if (name == NULL) return;
\r
1265 q = strchr(p, ':');
\r
1267 if (q - p >= sizeof(mfp->faceName))
\r
1268 ExitArgError(_("Font name too long:"), name, TRUE);
\r
1269 memcpy(mfp->faceName, p, q - p);
\r
1270 mfp->faceName[q - p] = NULLCHAR;
\r
1273 q = mfp->faceName;
\r
1274 while (*p && !isdigit(*p)) {
\r
1276 if (q - mfp->faceName >= sizeof(mfp->faceName))
\r
1277 ExitArgError(_("Font name too long:"), name, TRUE);
\r
1279 while (q > mfp->faceName && q[-1] == ' ') q--;
\r
1282 if (!*p) ExitArgError(_("Font point size missing:"), name, TRUE);
\r
1283 mfp->pointSize = (float) atof(p);
\r
1284 mfp->bold = (strchr(p, 'b') != NULL);
\r
1285 mfp->italic = (strchr(p, 'i') != NULL);
\r
1286 mfp->underline = (strchr(p, 'u') != NULL);
\r
1287 mfp->strikeout = (strchr(p, 's') != NULL);
\r
1288 mfp->charset = DEFAULT_CHARSET;
\r
1289 q = strchr(p, 'c');
\r
1291 mfp->charset = (BYTE) atoi(q+1);
\r
1295 ParseFont(char *name, int number)
\r
1296 { // wrapper to shield back-end from 'font'
\r
1297 ParseFontName(name, &font[boardSize][number]->mfp);
\r
1302 { // in WB we have a 2D array of fonts; this initializes their description
\r
1304 /* Point font array elements to structures and
\r
1305 parse default font names */
\r
1306 for (i=0; i<NUM_FONTS; i++) {
\r
1307 for (j=0; j<NUM_SIZES; j++) {
\r
1308 font[j][i] = &fontRec[j][i];
\r
1309 ParseFontName(font[j][i]->def, &font[j][i]->mfp);
\r
1316 { // here we create the actual fonts from the selected descriptions
\r
1318 for (i=0; i<NUM_FONTS; i++) {
\r
1319 for (j=0; j<NUM_SIZES; j++) {
\r
1320 CreateFontInMF(font[j][i]);
\r
1324 /* Color name parser.
\r
1325 X version accepts X color names, but this one
\r
1326 handles only the #rrggbb form (hex) or rrr,ggg,bbb (decimal) */
\r
1328 ParseColorName(char *name)
\r
1330 int red, green, blue, count;
\r
1331 char buf[MSG_SIZ];
\r
1333 count = sscanf(name, "#%2x%2x%2x", &red, &green, &blue);
\r
1335 count = sscanf(name, "%3d%*[^0-9]%3d%*[^0-9]%3d",
\r
1336 &red, &green, &blue);
\r
1339 snprintf(buf, MSG_SIZ, _("Can't parse color name %s"), name);
\r
1340 DisplayError(buf, 0);
\r
1341 return RGB(0, 0, 0);
\r
1343 return PALETTERGB(red, green, blue);
\r
1347 ParseColor(int n, char *name)
\r
1348 { // for WinBoard the color is an int, which needs to be derived from the string
\r
1349 if(colorVariable[n]) *(int*)colorVariable[n] = ParseColorName(name);
\r
1353 ParseAttribs(COLORREF *color, int *effects, char* argValue)
\r
1355 char *e = argValue;
\r
1359 if (*e == 'b') eff |= CFE_BOLD;
\r
1360 else if (*e == 'i') eff |= CFE_ITALIC;
\r
1361 else if (*e == 'u') eff |= CFE_UNDERLINE;
\r
1362 else if (*e == 's') eff |= CFE_STRIKEOUT;
\r
1363 else if (*e == '#' || isdigit(*e)) break;
\r
1367 *color = ParseColorName(e);
\r
1371 ParseTextAttribs(ColorClass cc, char *s)
\r
1372 { // [HGM] front-end wrapper that does the platform-dependent call
\r
1373 // for XBoard we would set (&appData.colorShout)[cc] = strdup(s);
\r
1374 ParseAttribs(&textAttribs[cc].color, &textAttribs[cc].effects, s);
\r
1378 ParseBoardSize(void *addr, char *name)
\r
1379 { // [HGM] rewritten with return-value ptr to shield back-end from BoardSize
\r
1380 BoardSize bs = SizeTiny;
\r
1381 while (sizeInfo[bs].name != NULL) {
\r
1382 if (StrCaseCmp(name, sizeInfo[bs].name) == 0) {
\r
1383 *(BoardSize *)addr = bs;
\r
1388 ExitArgError(_("Unrecognized board size value"), name, TRUE);
\r
1393 { // [HGM] import name from appData first
\r
1396 for (cc = (ColorClass)0; cc < ColorNormal; cc++) {
\r
1397 textAttribs[cc].sound.name = strdup((&appData.soundShout)[cc]);
\r
1398 textAttribs[cc].sound.data = NULL;
\r
1399 MyLoadSound(&textAttribs[cc].sound);
\r
1401 for (cc = ColorNormal; cc < NColorClasses; cc++) {
\r
1402 textAttribs[cc].sound.name = strdup("");
\r
1403 textAttribs[cc].sound.data = NULL;
\r
1405 for (sc = (SoundClass)0; sc < NSoundClasses; sc++) {
\r
1406 sounds[sc].name = strdup((&appData.soundMove)[sc]);
\r
1407 sounds[sc].data = NULL;
\r
1408 MyLoadSound(&sounds[sc]);
\r
1413 SetCommPortDefaults()
\r
1415 memset(&dcb, 0, sizeof(DCB)); // required by VS 2002 +
\r
1416 dcb.DCBlength = sizeof(DCB);
\r
1417 dcb.BaudRate = 9600;
\r
1418 dcb.fBinary = TRUE;
\r
1419 dcb.fParity = FALSE;
\r
1420 dcb.fOutxCtsFlow = FALSE;
\r
1421 dcb.fOutxDsrFlow = FALSE;
\r
1422 dcb.fDtrControl = DTR_CONTROL_ENABLE;
\r
1423 dcb.fDsrSensitivity = FALSE;
\r
1424 dcb.fTXContinueOnXoff = TRUE;
\r
1425 dcb.fOutX = FALSE;
\r
1427 dcb.fNull = FALSE;
\r
1428 dcb.fRtsControl = RTS_CONTROL_ENABLE;
\r
1429 dcb.fAbortOnError = FALSE;
\r
1431 dcb.Parity = SPACEPARITY;
\r
1432 dcb.StopBits = ONESTOPBIT;
\r
1435 // [HGM] args: these three cases taken out to stay in front-end
\r
1437 SaveFontArg(FILE *f, ArgDescriptor *ad)
\r
1438 { // in WinBoard every board size has its own font, and the "argLoc" identifies the table,
\r
1439 // while the curent board size determines the element. This system should be ported to XBoard.
\r
1440 // What the table contains pointers to, and how to print the font description, remains platform-dependent
\r
1442 for (bs=0; bs<NUM_SIZES; bs++) {
\r
1443 MyFontParams *mfp = &font[bs][(int) ad->argLoc]->mfp;
\r
1444 fprintf(f, "/size=%s ", sizeInfo[bs].name);
\r
1445 fprintf(f, "/%s=\"%s:%g%s%s%s%s%sc%d\"\n",
\r
1446 ad->argName, mfp->faceName, mfp->pointSize,
\r
1447 mfp->bold || mfp->italic || mfp->underline || mfp->strikeout ? " " : "",
\r
1448 mfp->bold ? "b" : "",
\r
1449 mfp->italic ? "i" : "",
\r
1450 mfp->underline ? "u" : "",
\r
1451 mfp->strikeout ? "s" : "",
\r
1452 (int)mfp->charset);
\r
1458 { // [HGM] copy the names from the internal WB variables to appData
\r
1461 for (cc = (ColorClass)0; cc < ColorNormal; cc++)
\r
1462 (&appData.soundShout)[cc] = textAttribs[cc].sound.name;
\r
1463 for (sc = (SoundClass)0; sc < NSoundClasses; sc++)
\r
1464 (&appData.soundMove)[sc] = sounds[sc].name;
\r
1468 SaveAttribsArg(FILE *f, ArgDescriptor *ad)
\r
1469 { // here the "argLoc" defines a table index. It could have contained the 'ta' pointer itself, though
\r
1470 MyTextAttribs* ta = &textAttribs[(ColorClass)ad->argLoc];
\r
1471 fprintf(f, "/%s=\"%s%s%s%s%s#%02lx%02lx%02lx\"\n", ad->argName,
\r
1472 (ta->effects & CFE_BOLD) ? "b" : "",
\r
1473 (ta->effects & CFE_ITALIC) ? "i" : "",
\r
1474 (ta->effects & CFE_UNDERLINE) ? "u" : "",
\r
1475 (ta->effects & CFE_STRIKEOUT) ? "s" : "",
\r
1476 (ta->effects) ? " " : "",
\r
1477 ta->color&0xff, (ta->color >> 8)&0xff, (ta->color >> 16)&0xff);
\r
1481 SaveColor(FILE *f, ArgDescriptor *ad)
\r
1482 { // in WinBoard the color is an int and has to be converted to text. In X it would be a string already?
\r
1483 COLORREF color = *(COLORREF *)colorVariable[(int)ad->argLoc];
\r
1484 fprintf(f, "/%s=#%02lx%02lx%02lx\n", ad->argName,
\r
1485 color&0xff, (color>>8)&0xff, (color>>16)&0xff);
\r
1489 SaveBoardSize(FILE *f, char *name, void *addr)
\r
1490 { // wrapper to shield back-end from BoardSize & sizeInfo
\r
1491 fprintf(f, "/%s=%s\n", name, sizeInfo[*(BoardSize *)addr].name);
\r
1495 ParseCommPortSettings(char *s)
\r
1496 { // wrapper to keep dcb from back-end
\r
1497 ParseCommSettings(s, &dcb);
\r
1502 { // wrapper to shield use of window handles from back-end (make addressible by number?)
\r
1503 GetActualPlacement(hwndMain, &wpMain);
\r
1504 GetActualPlacement(hwndConsole, &wpConsole);
\r
1505 GetActualPlacement(commentDialog, &wpComment);
\r
1506 GetActualPlacement(editTagsDialog, &wpTags);
\r
1507 GetActualPlacement(gameListDialog, &wpGameList);
\r
1508 GetActualPlacement(moveHistoryDialog, &wpMoveHistory);
\r
1509 GetActualPlacement(evalGraphDialog, &wpEvalGraph);
\r
1510 GetActualPlacement(engineOutputDialog, &wpEngineOutput);
\r
1514 PrintCommPortSettings(FILE *f, char *name)
\r
1515 { // wrapper to shield back-end from DCB
\r
1516 PrintCommSettings(f, name, &dcb);
\r
1520 MySearchPath(char *installDir, char *name, char *fullname)
\r
1522 char *dummy, buf[MSG_SIZ], *p = name, *q;
\r
1523 if(name[0]== '%') {
\r
1524 fullname[0] = 0; // [HGM] first expand any environment variables in the given name
\r
1525 while(*p == '%' && (q = strchr(p+1, '%'))) { // [HGM] recognize %*% as environment variable
\r
1526 safeStrCpy(buf, p+1, sizeof(buf)/sizeof(buf[0]) );
\r
1527 *strchr(buf, '%') = 0;
\r
1528 strcat(fullname, getenv(buf));
\r
1529 p = q+1; while(*p == '\\') { strcat(fullname, "\\"); p++; }
\r
1531 strcat(fullname, p); // after environment variables (if any), take the remainder of the given name
\r
1532 if(appData.debugMode) fprintf(debugFP, "name = '%s', expanded name = '%s'\n", name, fullname);
\r
1533 return (int) strlen(fullname);
\r
1535 return (int) SearchPath(installDir, name, NULL, MSG_SIZ, fullname, &dummy);
\r
1539 MyGetFullPathName(char *name, char *fullname)
\r
1542 return (int) GetFullPathName(name, MSG_SIZ, fullname, &dummy);
\r
1547 { // [HGM] args: allows testing if main window is realized from back-end
\r
1548 return hwndMain != NULL;
\r
1552 PopUpStartupDialog()
\r
1556 LoadLanguageFile(appData.language);
\r
1557 lpProc = MakeProcInstance((FARPROC)StartupDialog, hInst);
\r
1558 DialogBox(hInst, MAKEINTRESOURCE(DLG_Startup), NULL, (DLGPROC)lpProc);
\r
1559 FreeProcInstance(lpProc);
\r
1562 /*---------------------------------------------------------------------------*\
\r
1564 * GDI board drawing routines
\r
1566 \*---------------------------------------------------------------------------*/
\r
1568 /* [AS] Draw square using background texture */
\r
1569 static void DrawTile( int dx, int dy, int dw, int dh, HDC dst, HDC src, int mode, int sx, int sy )
\r
1574 return; /* Should never happen! */
\r
1577 SetGraphicsMode( dst, GM_ADVANCED );
\r
1584 /* X reflection */
\r
1589 x.eDx = (FLOAT) dw + dx - 1;
\r
1592 SetWorldTransform( dst, &x );
\r
1595 /* Y reflection */
\r
1601 x.eDy = (FLOAT) dh + dy - 1;
\r
1603 SetWorldTransform( dst, &x );
\r
1611 x.eDx = (FLOAT) dx;
\r
1612 x.eDy = (FLOAT) dy;
\r
1615 SetWorldTransform( dst, &x );
\r
1619 BitBlt( dst, dx, dy, dw, dh, src, sx, sy, SRCCOPY );
\r
1627 SetWorldTransform( dst, &x );
\r
1629 ModifyWorldTransform( dst, 0, MWT_IDENTITY );
\r
1632 /* [AS] [HGM] Make room for more piece types, so all pieces can be different */
\r
1634 PM_WP = (int) WhitePawn,
\r
1635 PM_WN = (int) WhiteKnight,
\r
1636 PM_WB = (int) WhiteBishop,
\r
1637 PM_WR = (int) WhiteRook,
\r
1638 PM_WQ = (int) WhiteQueen,
\r
1639 PM_WF = (int) WhiteFerz,
\r
1640 PM_WW = (int) WhiteWazir,
\r
1641 PM_WE = (int) WhiteAlfil,
\r
1642 PM_WM = (int) WhiteMan,
\r
1643 PM_WO = (int) WhiteCannon,
\r
1644 PM_WU = (int) WhiteUnicorn,
\r
1645 PM_WH = (int) WhiteNightrider,
\r
1646 PM_WA = (int) WhiteAngel,
\r
1647 PM_WC = (int) WhiteMarshall,
\r
1648 PM_WAB = (int) WhiteCardinal,
\r
1649 PM_WD = (int) WhiteDragon,
\r
1650 PM_WL = (int) WhiteLance,
\r
1651 PM_WS = (int) WhiteCobra,
\r
1652 PM_WV = (int) WhiteFalcon,
\r
1653 PM_WSG = (int) WhiteSilver,
\r
1654 PM_WG = (int) WhiteGrasshopper,
\r
1655 PM_WK = (int) WhiteKing,
\r
1656 PM_BP = (int) BlackPawn,
\r
1657 PM_BN = (int) BlackKnight,
\r
1658 PM_BB = (int) BlackBishop,
\r
1659 PM_BR = (int) BlackRook,
\r
1660 PM_BQ = (int) BlackQueen,
\r
1661 PM_BF = (int) BlackFerz,
\r
1662 PM_BW = (int) BlackWazir,
\r
1663 PM_BE = (int) BlackAlfil,
\r
1664 PM_BM = (int) BlackMan,
\r
1665 PM_BO = (int) BlackCannon,
\r
1666 PM_BU = (int) BlackUnicorn,
\r
1667 PM_BH = (int) BlackNightrider,
\r
1668 PM_BA = (int) BlackAngel,
\r
1669 PM_BC = (int) BlackMarshall,
\r
1670 PM_BG = (int) BlackGrasshopper,
\r
1671 PM_BAB = (int) BlackCardinal,
\r
1672 PM_BD = (int) BlackDragon,
\r
1673 PM_BL = (int) BlackLance,
\r
1674 PM_BS = (int) BlackCobra,
\r
1675 PM_BV = (int) BlackFalcon,
\r
1676 PM_BSG = (int) BlackSilver,
\r
1677 PM_BK = (int) BlackKing
\r
1680 static HFONT hPieceFont = NULL;
\r
1681 static HBITMAP hPieceMask[(int) EmptySquare];
\r
1682 static HBITMAP hPieceFace[(int) EmptySquare];
\r
1683 static int fontBitmapSquareSize = 0;
\r
1684 static char pieceToFontChar[(int) EmptySquare] =
\r
1685 { 'p', 'n', 'b', 'r', 'q',
\r
1686 'n', 'b', 'p', 'n', 'b', 'r', 'b', 'r', 'q', 'k',
\r
1687 'k', 'o', 'm', 'v', 't', 'w',
\r
1688 'v', 't', 'o', 'm', 'v', 't', 'v', 't', 'w', 'l',
\r
1691 extern BOOL SetCharTable( char *table, const char * map );
\r
1692 /* [HGM] moved to backend.c */
\r
1694 static void SetPieceBackground( HDC hdc, COLORREF color, int mode )
\r
1697 BYTE r1 = GetRValue( color );
\r
1698 BYTE g1 = GetGValue( color );
\r
1699 BYTE b1 = GetBValue( color );
\r
1705 /* Create a uniform background first */
\r
1706 hbrush = CreateSolidBrush( color );
\r
1707 SetRect( &rc, 0, 0, squareSize, squareSize );
\r
1708 FillRect( hdc, &rc, hbrush );
\r
1709 DeleteObject( hbrush );
\r
1712 /* Vertical gradient, good for pawn, knight and rook, less for queen and king */
\r
1713 int steps = squareSize / 2;
\r
1716 for( i=0; i<steps; i++ ) {
\r
1717 BYTE r = r1 - (r1-r2) * i / steps;
\r
1718 BYTE g = g1 - (g1-g2) * i / steps;
\r
1719 BYTE b = b1 - (b1-b2) * i / steps;
\r
1721 hbrush = CreateSolidBrush( RGB(r,g,b) );
\r
1722 SetRect( &rc, i + squareSize - steps, 0, i + squareSize - steps + 1, squareSize );
\r
1723 FillRect( hdc, &rc, hbrush );
\r
1724 DeleteObject(hbrush);
\r
1727 else if( mode == 2 ) {
\r
1728 /* Diagonal gradient, good more or less for every piece */
\r
1729 POINT triangle[3];
\r
1730 HPEN hpen = SelectObject( hdc, GetStockObject(NULL_PEN) );
\r
1731 HBRUSH hbrush_old;
\r
1732 int steps = squareSize;
\r
1735 triangle[0].x = squareSize - steps;
\r
1736 triangle[0].y = squareSize;
\r
1737 triangle[1].x = squareSize;
\r
1738 triangle[1].y = squareSize;
\r
1739 triangle[2].x = squareSize;
\r
1740 triangle[2].y = squareSize - steps;
\r
1742 for( i=0; i<steps; i++ ) {
\r
1743 BYTE r = r1 - (r1-r2) * i / steps;
\r
1744 BYTE g = g1 - (g1-g2) * i / steps;
\r
1745 BYTE b = b1 - (b1-b2) * i / steps;
\r
1747 hbrush = CreateSolidBrush( RGB(r,g,b) );
\r
1748 hbrush_old = SelectObject( hdc, hbrush );
\r
1749 Polygon( hdc, triangle, 3 );
\r
1750 SelectObject( hdc, hbrush_old );
\r
1751 DeleteObject(hbrush);
\r
1756 SelectObject( hdc, hpen );
\r
1761 [AS] The method I use to create the bitmaps it a bit tricky, but it
\r
1762 seems to work ok. The main problem here is to find the "inside" of a chess
\r
1763 piece: follow the steps as explained below.
\r
1765 static void CreatePieceMaskFromFont( HDC hdc_window, HDC hdc, int index )
\r
1769 COLORREF chroma = RGB(0xFF,0x00,0xFF);
\r
1773 int backColor = whitePieceColor;
\r
1774 int foreColor = blackPieceColor;
\r
1776 if( index < (int)BlackPawn && appData.fontBackColorWhite != appData.fontForeColorWhite ) {
\r
1777 backColor = appData.fontBackColorWhite;
\r
1778 foreColor = appData.fontForeColorWhite;
\r
1780 else if( index >= (int)BlackPawn && appData.fontBackColorBlack != appData.fontForeColorBlack ) {
\r
1781 backColor = appData.fontBackColorBlack;
\r
1782 foreColor = appData.fontForeColorBlack;
\r
1786 hbm = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );
\r
1788 hbm_old = SelectObject( hdc, hbm );
\r
1792 rc.right = squareSize;
\r
1793 rc.bottom = squareSize;
\r
1795 /* Step 1: background is now black */
\r
1796 FillRect( hdc, &rc, GetStockObject(BLACK_BRUSH) );
\r
1798 GetTextExtentPoint32( hdc, &pieceToFontChar[index], 1, &sz );
\r
1800 pt.x = (squareSize - sz.cx) / 2;
\r
1801 pt.y = (squareSize - sz.cy) / 2;
\r
1803 SetBkMode( hdc, TRANSPARENT );
\r
1804 SetTextColor( hdc, chroma );
\r
1805 /* Step 2: the piece has been drawn in purple, there are now black and purple in this bitmap */
\r
1806 TextOut( hdc, pt.x, pt.y, &pieceToFontChar[appData.allWhite && index >= (int)BlackPawn ? index - (int)BlackPawn : index], 1 );
\r
1808 SelectObject( hdc, GetStockObject(WHITE_BRUSH) );
\r
1809 /* Step 3: the area outside the piece is filled with white */
\r
1810 // FloodFill( hdc, 0, 0, chroma );
\r
1811 ExtFloodFill( hdc, 0, 0, 0, FLOODFILLSURFACE );
\r
1812 ExtFloodFill( hdc, 0, squareSize-1, 0, FLOODFILLSURFACE ); // [HGM] fill from all 4 corners, for if piece too big
\r
1813 ExtFloodFill( hdc, squareSize-1, 0, 0, FLOODFILLSURFACE );
\r
1814 ExtFloodFill( hdc, squareSize-1, squareSize-1, 0, FLOODFILLSURFACE );
\r
1815 SelectObject( hdc, GetStockObject(BLACK_BRUSH) );
\r
1817 Step 4: this is the tricky part, the area inside the piece is filled with black,
\r
1818 but if the start point is not inside the piece we're lost!
\r
1819 There should be a better way to do this... if we could create a region or path
\r
1820 from the fill operation we would be fine for example.
\r
1822 // FloodFill( hdc, squareSize / 2, squareSize / 2, RGB(0xFF,0xFF,0xFF) );
\r
1823 ExtFloodFill( hdc, squareSize / 2, squareSize / 2, RGB(0xFF,0xFF,0xFF), FLOODFILLBORDER );
\r
1825 { /* [HGM] shave off edges of mask, in an attempt to correct for the fact that FloodFill does not work correctly under Win XP */
\r
1826 HDC dc2 = CreateCompatibleDC( hdc_window );
\r
1827 HBITMAP bm2 = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );
\r
1829 SelectObject( dc2, bm2 );
\r
1830 BitBlt( dc2, 0, 0, squareSize, squareSize, hdc, 0, 0, SRCCOPY ); // make copy
\r
1831 BitBlt( hdc, 0, 1, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );
\r
1832 BitBlt( hdc, 2, 1, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );
\r
1833 BitBlt( hdc, 1, 0, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );
\r
1834 BitBlt( hdc, 1, 2, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );
\r
1837 DeleteObject( bm2 );
\r
1840 SetTextColor( hdc, 0 );
\r
1842 Step 5: some fonts have "disconnected" areas that are skipped by the fill:
\r
1843 draw the piece again in black for safety.
\r
1845 TextOut( hdc, pt.x, pt.y, &pieceToFontChar[appData.allWhite && index >= (int)BlackPawn ? index - (int)BlackPawn : index], 1 );
\r
1847 SelectObject( hdc, hbm_old );
\r
1849 if( hPieceMask[index] != NULL ) {
\r
1850 DeleteObject( hPieceMask[index] );
\r
1853 hPieceMask[index] = hbm;
\r
1856 hbm = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );
\r
1858 SelectObject( hdc, hbm );
\r
1861 HDC dc1 = CreateCompatibleDC( hdc_window );
\r
1862 HDC dc2 = CreateCompatibleDC( hdc_window );
\r
1863 HBITMAP bm2 = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );
\r
1865 SelectObject( dc1, hPieceMask[index] );
\r
1866 SelectObject( dc2, bm2 );
\r
1867 FillRect( dc2, &rc, GetStockObject(WHITE_BRUSH) );
\r
1868 BitBlt( dc2, 0, 0, squareSize, squareSize, dc1, 0, 0, SRCINVERT );
\r
1871 Now dc2 contains the inverse of the piece mask, i.e. a mask that preserves
\r
1872 the piece background and deletes (makes transparent) the rest.
\r
1873 Thanks to that mask, we are free to paint the background with the greates
\r
1874 freedom, as we'll be able to mask off the unwanted parts when finished.
\r
1875 We use this, to make gradients and give the pieces a "roundish" look.
\r
1877 SetPieceBackground( hdc, backColor, 2 );
\r
1878 BitBlt( hdc, 0, 0, squareSize, squareSize, dc2, 0, 0, SRCAND );
\r
1882 DeleteObject( bm2 );
\r
1885 SetTextColor( hdc, foreColor );
\r
1886 TextOut( hdc, pt.x, pt.y, &pieceToFontChar[appData.allWhite && index >= (int)BlackPawn ? index - (int)BlackPawn : index], 1 );
\r
1888 SelectObject( hdc, hbm_old );
\r
1890 if( hPieceFace[index] != NULL ) {
\r
1891 DeleteObject( hPieceFace[index] );
\r
1894 hPieceFace[index] = hbm;
\r
1897 static int TranslatePieceToFontPiece( int piece )
\r
1927 case BlackMarshall:
\r
1931 case BlackNightrider:
\r
1937 case BlackUnicorn:
\r
1941 case BlackGrasshopper:
\r
1953 case BlackCardinal:
\r
1960 case WhiteMarshall:
\r
1964 case WhiteNightrider:
\r
1970 case WhiteUnicorn:
\r
1974 case WhiteGrasshopper:
\r
1986 case WhiteCardinal:
\r
1995 void CreatePiecesFromFont()
\r
1998 HDC hdc_window = NULL;
\r
2004 if( fontBitmapSquareSize < 0 ) {
\r
2005 /* Something went seriously wrong in the past: do not try to recreate fonts! */
\r
2009 if( !appData.useFont || appData.renderPiecesWithFont == NULL ||
\r
2010 appData.renderPiecesWithFont[0] == NULLCHAR || appData.renderPiecesWithFont[0] == '*' ) {
\r
2011 fontBitmapSquareSize = -1;
\r
2015 if( fontBitmapSquareSize != squareSize ) {
\r
2016 hdc_window = GetDC( hwndMain );
\r
2017 hdc = CreateCompatibleDC( hdc_window );
\r
2019 if( hPieceFont != NULL ) {
\r
2020 DeleteObject( hPieceFont );
\r
2023 for( i=0; i<=(int)BlackKing; i++ ) {
\r
2024 hPieceMask[i] = NULL;
\r
2025 hPieceFace[i] = NULL;
\r
2031 if( appData.fontPieceSize >= 50 && appData.fontPieceSize <= 150 ) {
\r
2032 fontHeight = appData.fontPieceSize;
\r
2035 fontHeight = (fontHeight * squareSize) / 100;
\r
2037 lf.lfHeight = -MulDiv( fontHeight, GetDeviceCaps(hdc, LOGPIXELSY), 72 );
\r
2039 lf.lfEscapement = 0;
\r
2040 lf.lfOrientation = 0;
\r
2041 lf.lfWeight = FW_NORMAL;
\r
2043 lf.lfUnderline = 0;
\r
2044 lf.lfStrikeOut = 0;
\r
2045 lf.lfCharSet = DEFAULT_CHARSET;
\r
2046 lf.lfOutPrecision = OUT_DEFAULT_PRECIS;
\r
2047 lf.lfClipPrecision = CLIP_DEFAULT_PRECIS;
\r
2048 lf.lfQuality = PROOF_QUALITY;
\r
2049 lf.lfPitchAndFamily = DEFAULT_PITCH | FF_DONTCARE;
\r
2050 strncpy( lf.lfFaceName, appData.renderPiecesWithFont, sizeof(lf.lfFaceName) );
\r
2051 lf.lfFaceName[ sizeof(lf.lfFaceName) - 1 ] = '\0';
\r
2053 hPieceFont = CreateFontIndirect( &lf );
\r
2055 if( hPieceFont == NULL ) {
\r
2056 fontBitmapSquareSize = -2;
\r
2059 /* Setup font-to-piece character table */
\r
2060 if( ! SetCharTable(pieceToFontChar, appData.fontToPieceTable) ) {
\r
2061 /* No (or wrong) global settings, try to detect the font */
\r
2062 if( strstr(lf.lfFaceName,"Alpha") != NULL ) {
\r
2064 SetCharTable(pieceToFontChar, "phbrqkojntwl");
\r
2066 else if( strstr(lf.lfFaceName,"DiagramTT") != NULL ) {
\r
2067 /* DiagramTT* family */
\r
2068 SetCharTable(pieceToFontChar, "PNLRQKpnlrqk");
\r
2070 else if( strstr(lf.lfFaceName,"WinboardF") != NULL ) {
\r
2071 /* Fairy symbols */
\r
2072 SetCharTable(pieceToFontChar, "PNBRQFEACWMOHIJGDVSLUKpnbrqfeacwmohijgdvsluk");
\r
2074 else if( strstr(lf.lfFaceName,"GC2004D") != NULL ) {
\r
2075 /* Good Companion (Some characters get warped as literal :-( */
\r
2076 char s[] = "1cmWG0??S??oYI23wgQU";
\r
2077 s[0]=0xB9; s[1]=0xA9; s[6]=0xB1; s[11]=0xBB; s[12]=0xAB; s[17]=0xB3;
\r
2078 SetCharTable(pieceToFontChar, s);
\r
2081 /* Cases, Condal, Leipzig, Lucena, Marroquin, Merida, Usual */
\r
2082 SetCharTable(pieceToFontChar, "pnbrqkomvtwl");
\r
2086 /* Create bitmaps */
\r
2087 hfont_old = SelectObject( hdc, hPieceFont );
\r
2088 for(i=(int)WhitePawn; i<(int)EmptySquare; i++) /* [HGM] made a loop for this */
\r
2089 if(PieceToChar((ChessSquare)i) != '.') /* skip unused pieces */
\r
2090 CreatePieceMaskFromFont( hdc_window, hdc, i );
\r
2092 SelectObject( hdc, hfont_old );
\r
2094 fontBitmapSquareSize = squareSize;
\r
2098 if( hdc != NULL ) {
\r
2102 if( hdc_window != NULL ) {
\r
2103 ReleaseDC( hwndMain, hdc_window );
\r
2108 DoLoadBitmap(HINSTANCE hinst, char *piece, int squareSize, char *suffix)
\r
2112 snprintf(name, sizeof(name)/sizeof(name[0]), "%s%d%s", piece, squareSize, suffix);
\r
2113 if (gameInfo.event &&
\r
2114 strcmp(gameInfo.event, "Easter Egg Hunt") == 0 &&
\r
2115 strcmp(name, "k80s") == 0) {
\r
2116 safeStrCpy(name, "tim", sizeof(name)/sizeof(name[0]) );
\r
2118 return LoadBitmap(hinst, name);
\r
2122 /* Insert a color into the program's logical palette
\r
2123 structure. This code assumes the given color is
\r
2124 the result of the RGB or PALETTERGB macro, and it
\r
2125 knows how those macros work (which is documented).
\r
2128 InsertInPalette(COLORREF color)
\r
2130 LPPALETTEENTRY pe = &(pLogPal->palPalEntry[pLogPal->palNumEntries]);
\r
2132 if (pLogPal->palNumEntries++ >= PALETTESIZE) {
\r
2133 DisplayFatalError(_("Too many colors"), 0, 1);
\r
2134 pLogPal->palNumEntries--;
\r
2138 pe->peFlags = (char) 0;
\r
2139 pe->peRed = (char) (0xFF & color);
\r
2140 pe->peGreen = (char) (0xFF & (color >> 8));
\r
2141 pe->peBlue = (char) (0xFF & (color >> 16));
\r
2147 InitDrawingColors()
\r
2149 if (pLogPal == NULL) {
\r
2150 /* Allocate enough memory for a logical palette with
\r
2151 * PALETTESIZE entries and set the size and version fields
\r
2152 * of the logical palette structure.
\r
2154 pLogPal = (NPLOGPALETTE)
\r
2155 LocalAlloc(LMEM_FIXED, (sizeof(LOGPALETTE) +
\r
2156 (sizeof(PALETTEENTRY) * (PALETTESIZE))));
\r
2157 pLogPal->palVersion = 0x300;
\r
2159 pLogPal->palNumEntries = 0;
\r
2161 InsertInPalette(lightSquareColor);
\r
2162 InsertInPalette(darkSquareColor);
\r
2163 InsertInPalette(whitePieceColor);
\r
2164 InsertInPalette(blackPieceColor);
\r
2165 InsertInPalette(highlightSquareColor);
\r
2166 InsertInPalette(premoveHighlightColor);
\r
2168 /* create a logical color palette according the information
\r
2169 * in the LOGPALETTE structure.
\r
2171 hPal = CreatePalette((LPLOGPALETTE) pLogPal);
\r
2173 lightSquareBrush = CreateSolidBrush(lightSquareColor);
\r
2174 blackSquareBrush = CreateSolidBrush(blackPieceColor);
\r
2175 darkSquareBrush = CreateSolidBrush(darkSquareColor);
\r
2176 whitePieceBrush = CreateSolidBrush(whitePieceColor);
\r
2177 blackPieceBrush = CreateSolidBrush(blackPieceColor);
\r
2178 iconBkgndBrush = CreateSolidBrush(GetSysColor(COLOR_BACKGROUND));
\r
2179 explodeBrush = CreateSolidBrush(highlightSquareColor); // [HGM] atomic
\r
2180 markerBrush = CreateSolidBrush(premoveHighlightColor); // [HGM] markers
\r
2181 /* [AS] Force rendering of the font-based pieces */
\r
2182 if( fontBitmapSquareSize > 0 ) {
\r
2183 fontBitmapSquareSize = 0;
\r
2189 BoardWidth(int boardSize, int n)
\r
2190 { /* [HGM] argument n added to allow different width and height */
\r
2191 int lineGap = sizeInfo[boardSize].lineGap;
\r
2193 if( appData.overrideLineGap >= 0 && appData.overrideLineGap <= 5 ) {
\r
2194 lineGap = appData.overrideLineGap;
\r
2197 return (n + 1) * lineGap +
\r
2198 n * sizeInfo[boardSize].squareSize;
\r
2201 /* Respond to board resize by dragging edge */
\r
2203 ResizeBoard(int newSizeX, int newSizeY, int flags)
\r
2205 BoardSize newSize = NUM_SIZES - 1;
\r
2206 static int recurse = 0;
\r
2207 if (IsIconic(hwndMain)) return;
\r
2208 if (recurse > 0) return;
\r
2210 while (newSize > 0) {
\r
2211 InitDrawingSizes(newSize+1000, 0); // [HGM] kludge to update sizeInfo without visible effects
\r
2212 if(newSizeX >= sizeInfo[newSize].cliWidth &&
\r
2213 newSizeY >= sizeInfo[newSize].cliHeight) break;
\r
2216 boardSize = newSize;
\r
2217 InitDrawingSizes(boardSize, flags);
\r
2222 extern Boolean twoBoards, partnerUp; // [HGM] dual
\r
2225 InitDrawingSizes(BoardSize boardSize, int flags)
\r
2227 int i, boardWidth, boardHeight; /* [HGM] height treated separately */
\r
2228 ChessSquare piece;
\r
2229 static int oldBoardSize = -1, oldTinyLayout = 0;
\r
2231 SIZE clockSize, messageSize;
\r
2233 char buf[MSG_SIZ];
\r
2235 HMENU hmenu = GetMenu(hwndMain);
\r
2236 RECT crect, wrect, oldRect;
\r
2238 LOGBRUSH logbrush;
\r
2239 VariantClass v = gameInfo.variant;
\r
2241 int suppressVisibleEffects = 0; // [HGM] kludge to request updating sizeInfo only
\r
2242 if((int)boardSize >= 1000 ) { boardSize -= 1000; suppressVisibleEffects = 1; }
\r
2244 /* [HGM] call with -2 uses old size (for if nr of files, ranks changes) */
\r
2245 if(boardSize == (BoardSize)(-2) ) boardSize = oldBoardSize;
\r
2246 oldBoardSize = boardSize;
\r
2248 if(boardSize != SizeMiddling && boardSize != SizePetite && boardSize != SizeBulky && !appData.useFont)
\r
2249 { // correct board size to one where built-in pieces exist
\r
2250 if((v == VariantCapablanca || v == VariantGothic || v == VariantGrand || v == VariantCapaRandom || v == VariantJanus || v == VariantSuper)
\r
2251 && (boardSize < SizePetite || boardSize > SizeBulky) // Archbishop and Chancellor available in entire middle range
\r
2252 || (v == VariantShogi && boardSize != SizeModerate) // Japanese-style Shogi
\r
2253 || v == VariantKnightmate || v == VariantSChess || v == VariantXiangqi || v == VariantSpartan
\r
2254 || v == VariantShatranj || v == VariantMakruk || v == VariantGreat || v == VariantFairy ) {
\r
2255 if(boardSize < SizeMediocre) boardSize = SizePetite; else
\r
2256 if(boardSize > SizeModerate) boardSize = SizeBulky; else
\r
2257 boardSize = SizeMiddling;
\r
2260 if(!appData.useFont && boardSize == SizePetite && (v == VariantShogi || v == VariantKnightmate)) boardSize = SizeMiddling; // no Unicorn in Petite
\r
2262 oldRect.left = wpMain.x; //[HGM] placement: remember previous window params
\r
2263 oldRect.top = wpMain.y;
\r
2264 oldRect.right = wpMain.x + wpMain.width;
\r
2265 oldRect.bottom = wpMain.y + wpMain.height;
\r
2267 tinyLayout = sizeInfo[boardSize].tinyLayout;
\r
2268 smallLayout = sizeInfo[boardSize].smallLayout;
\r
2269 squareSize = sizeInfo[boardSize].squareSize;
\r
2270 lineGap = sizeInfo[boardSize].lineGap;
\r
2271 minorSize = 0; /* [HGM] Kludge to see if demagnified pieces need to be shifted */
\r
2273 if( appData.overrideLineGap >= 0 && appData.overrideLineGap <= 5 ) {
\r
2274 lineGap = appData.overrideLineGap;
\r
2277 if (tinyLayout != oldTinyLayout) {
\r
2278 long style = GetWindowLongPtr(hwndMain, GWL_STYLE);
\r
2280 style &= ~WS_SYSMENU;
\r
2281 InsertMenu(hmenu, IDM_Exit, MF_BYCOMMAND, IDM_Minimize,
\r
2282 "&Minimize\tCtrl+F4");
\r
2284 style |= WS_SYSMENU;
\r
2285 RemoveMenu(hmenu, IDM_Minimize, MF_BYCOMMAND);
\r
2287 SetWindowLongPtr(hwndMain, GWL_STYLE, style);
\r
2289 for (i=0; menuBarText[tinyLayout][i]; i++) {
\r
2290 ModifyMenu(hmenu, i, MF_STRING|MF_BYPOSITION|MF_POPUP,
\r
2291 (UINT)GetSubMenu(hmenu, i), T_(menuBarText[tinyLayout][i]));
\r
2293 DrawMenuBar(hwndMain);
\r
2296 boardWidth = BoardWidth(boardSize, BOARD_WIDTH);
\r
2297 boardHeight = BoardWidth(boardSize, BOARD_HEIGHT);
\r
2299 /* Get text area sizes */
\r
2300 hdc = GetDC(hwndMain);
\r
2301 if (appData.clockMode) {
\r
2302 snprintf(buf, MSG_SIZ, _("White: %s"), TimeString(23*60*60*1000L));
\r
2304 snprintf(buf, MSG_SIZ, _("White"));
\r
2306 oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);
\r
2307 GetTextExtentPoint(hdc, buf, strlen(buf), &clockSize);
\r
2308 SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);
\r
2309 str = _("We only care about the height here");
\r
2310 GetTextExtentPoint(hdc, str, strlen(str), &messageSize);
\r
2311 SelectObject(hdc, oldFont);
\r
2312 ReleaseDC(hwndMain, hdc);
\r
2314 /* Compute where everything goes */
\r
2315 if((first.programLogo || second.programLogo) && !tinyLayout) {
\r
2316 /* [HGM] logo: if either logo is on, reserve space for it */
\r
2317 logoHeight = 2*clockSize.cy;
\r
2318 leftLogoRect.left = OUTER_MARGIN;
\r
2319 leftLogoRect.right = leftLogoRect.left + 4*clockSize.cy;
\r
2320 leftLogoRect.top = OUTER_MARGIN;
\r
2321 leftLogoRect.bottom = OUTER_MARGIN + logoHeight;
\r
2323 rightLogoRect.right = OUTER_MARGIN + boardWidth;
\r
2324 rightLogoRect.left = rightLogoRect.right - 4*clockSize.cy;
\r
2325 rightLogoRect.top = OUTER_MARGIN;
\r
2326 rightLogoRect.bottom = OUTER_MARGIN + logoHeight;
\r
2329 whiteRect.left = leftLogoRect.right;
\r
2330 whiteRect.right = OUTER_MARGIN + boardWidth/2 - INNER_MARGIN/2;
\r
2331 whiteRect.top = OUTER_MARGIN;
\r
2332 whiteRect.bottom = whiteRect.top + logoHeight;
\r
2334 blackRect.right = rightLogoRect.left;
\r
2335 blackRect.left = whiteRect.right + INNER_MARGIN;
\r
2336 blackRect.top = whiteRect.top;
\r
2337 blackRect.bottom = whiteRect.bottom;
\r
2339 whiteRect.left = OUTER_MARGIN;
\r
2340 whiteRect.right = whiteRect.left + boardWidth/2 - INNER_MARGIN/2;
\r
2341 whiteRect.top = OUTER_MARGIN;
\r
2342 whiteRect.bottom = whiteRect.top + clockSize.cy;
\r
2344 blackRect.left = whiteRect.right + INNER_MARGIN;
\r
2345 blackRect.right = blackRect.left + boardWidth/2 - 1;
\r
2346 blackRect.top = whiteRect.top;
\r
2347 blackRect.bottom = whiteRect.bottom;
\r
2349 logoHeight = 0; // [HGM] logo: suppress logo after change to tiny layout!
\r
2352 messageRect.left = OUTER_MARGIN + MESSAGE_LINE_LEFTMARGIN;
\r
2353 if (appData.showButtonBar) {
\r
2354 messageRect.right = OUTER_MARGIN + boardWidth // [HGM] logo: expressed independent of clock placement
\r
2355 - N_BUTTONS*BUTTON_WIDTH - MESSAGE_LINE_LEFTMARGIN;
\r
2357 messageRect.right = OUTER_MARGIN + boardWidth;
\r
2359 messageRect.top = whiteRect.bottom + INNER_MARGIN;
\r
2360 messageRect.bottom = messageRect.top + messageSize.cy;
\r
2362 boardRect.left = OUTER_MARGIN;
\r
2363 boardRect.right = boardRect.left + boardWidth;
\r
2364 boardRect.top = messageRect.bottom + INNER_MARGIN;
\r
2365 boardRect.bottom = boardRect.top + boardHeight;
\r
2367 sizeInfo[boardSize].cliWidth = boardRect.right + OUTER_MARGIN;
\r
2368 sizeInfo[boardSize].cliHeight = boardRect.bottom + OUTER_MARGIN;
\r
2369 oldTinyLayout = tinyLayout;
\r
2370 winW = 2 * GetSystemMetrics(SM_CXFRAME) + boardRect.right + OUTER_MARGIN;
\r
2371 winH = 2 * GetSystemMetrics(SM_CYFRAME) + GetSystemMetrics(SM_CYMENU) +
\r
2372 GetSystemMetrics(SM_CYCAPTION) + boardRect.bottom + OUTER_MARGIN;
\r
2373 winW *= 1 + twoBoards;
\r
2374 if(suppressVisibleEffects) return; // [HGM] when called for filling sizeInfo only
\r
2375 wpMain.width = winW; // [HGM] placement: set through temporary which can used by initial sizing choice
\r
2376 wpMain.height = winH; // without disturbing window attachments
\r
2377 GetWindowRect(hwndMain, &wrect);
\r
2378 SetWindowPos(hwndMain, NULL, 0, 0, wpMain.width, wpMain.height,
\r
2379 SWP_NOCOPYBITS|SWP_NOZORDER|SWP_NOMOVE);
\r
2381 // [HGM] placement: let attached windows follow size change.
\r
2382 ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, moveHistoryDialog, &wpMoveHistory );
\r
2383 ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, evalGraphDialog, &wpEvalGraph );
\r
2384 ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, engineOutputDialog, &wpEngineOutput );
\r
2385 ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, gameListDialog, &wpGameList );
\r
2386 ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, hwndConsole, &wpConsole );
\r
2388 /* compensate if menu bar wrapped */
\r
2389 GetClientRect(hwndMain, &crect);
\r
2390 offby = boardRect.bottom + OUTER_MARGIN - crect.bottom;
\r
2391 wpMain.height += offby;
\r
2393 case WMSZ_TOPLEFT:
\r
2394 SetWindowPos(hwndMain, NULL,
\r
2395 wrect.right - wpMain.width, wrect.bottom - wpMain.height,
\r
2396 wpMain.width, wpMain.height, SWP_NOCOPYBITS|SWP_NOZORDER);
\r
2399 case WMSZ_TOPRIGHT:
\r
2401 SetWindowPos(hwndMain, NULL,
\r
2402 wrect.left, wrect.bottom - wpMain.height,
\r
2403 wpMain.width, wpMain.height, SWP_NOCOPYBITS|SWP_NOZORDER);
\r
2406 case WMSZ_BOTTOMLEFT:
\r
2408 SetWindowPos(hwndMain, NULL,
\r
2409 wrect.right - wpMain.width, wrect.top,
\r
2410 wpMain.width, wpMain.height, SWP_NOCOPYBITS|SWP_NOZORDER);
\r
2413 case WMSZ_BOTTOMRIGHT:
\r
2417 SetWindowPos(hwndMain, NULL, 0, 0, wpMain.width, wpMain.height,
\r
2418 SWP_NOCOPYBITS|SWP_NOZORDER|SWP_NOMOVE);
\r
2423 for (i = 0; i < N_BUTTONS; i++) {
\r
2424 if (buttonDesc[i].hwnd != NULL) {
\r
2425 DestroyWindow(buttonDesc[i].hwnd);
\r
2426 buttonDesc[i].hwnd = NULL;
\r
2428 if (appData.showButtonBar) {
\r
2429 buttonDesc[i].hwnd =
\r
2430 CreateWindow("BUTTON", buttonDesc[i].label,
\r
2431 WS_VISIBLE | WS_CHILD | BS_PUSHBUTTON,
\r
2432 boardRect.right - BUTTON_WIDTH*(N_BUTTONS-i),
\r
2433 messageRect.top, BUTTON_WIDTH, messageSize.cy, hwndMain,
\r
2434 (HMENU) buttonDesc[i].id,
\r
2435 (HINSTANCE) GetWindowLongPtr(hwndMain, GWLP_HINSTANCE), NULL);
\r
2437 SendMessage(buttonDesc[i].hwnd, WM_SETFONT,
\r
2438 (WPARAM)font[boardSize][MESSAGE_FONT]->hf,
\r
2439 MAKELPARAM(FALSE, 0));
\r
2441 if (buttonDesc[i].id == IDM_Pause)
\r
2442 hwndPause = buttonDesc[i].hwnd;
\r
2443 buttonDesc[i].wndproc = (WNDPROC)
\r
2444 SetWindowLongPtr(buttonDesc[i].hwnd, GWLP_WNDPROC, (LONG_PTR) ButtonProc);
\r
2447 if (gridPen != NULL) DeleteObject(gridPen);
\r
2448 if (highlightPen != NULL) DeleteObject(highlightPen);
\r
2449 if (premovePen != NULL) DeleteObject(premovePen);
\r
2450 if (lineGap != 0) {
\r
2451 logbrush.lbStyle = BS_SOLID;
\r
2452 logbrush.lbColor = RGB(0, 0, 0); /* grid pen color = black */
\r
2454 ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,
\r
2455 lineGap, &logbrush, 0, NULL);
\r
2456 logbrush.lbColor = highlightSquareColor;
\r
2458 ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,
\r
2459 lineGap, &logbrush, 0, NULL);
\r
2461 logbrush.lbColor = premoveHighlightColor;
\r
2463 ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,
\r
2464 lineGap, &logbrush, 0, NULL);
\r
2466 /* [HGM] Loop had to be split in part for vert. and hor. lines */
\r
2467 for (i = 0; i < BOARD_HEIGHT + 1; i++) {
\r
2468 gridEndpoints[i*2].x = boardRect.left + lineGap / 2;
\r
2469 gridEndpoints[i*2].y = gridEndpoints[i*2 + 1].y =
\r
2470 boardRect.top + lineGap / 2 + (i * (squareSize + lineGap));
\r
2471 gridEndpoints[i*2 + 1].x = boardRect.left + lineGap / 2 +
\r
2472 BOARD_WIDTH * (squareSize + lineGap);
\r
2473 gridVertexCounts[i*2] = gridVertexCounts[i*2 + 1] = 2;
\r
2475 for (i = 0; i < BOARD_WIDTH + 1; i++) {
\r
2476 gridEndpoints[i*2 + BOARD_HEIGHT*2 + 2].y = boardRect.top + lineGap / 2;
\r
2477 gridEndpoints[i*2 + BOARD_HEIGHT*2 + 2].x =
\r
2478 gridEndpoints[i*2 + 1 + BOARD_HEIGHT*2 + 2].x = boardRect.left +
\r
2479 lineGap / 2 + (i * (squareSize + lineGap));
\r
2480 gridEndpoints[i*2 + 1 + BOARD_HEIGHT*2 + 2].y =
\r
2481 boardRect.top + BOARD_HEIGHT * (squareSize + lineGap);
\r
2482 gridVertexCounts[i*2] = gridVertexCounts[i*2 + 1] = 2;
\r
2486 /* [HGM] Licensing requirement */
\r
2488 if(gameInfo.variant == VariantGothic) GothicPopUp( GOTHIC, VariantGothic); else
\r
2491 if(gameInfo.variant == VariantFalcon) GothicPopUp( FALCON, VariantFalcon); else
\r
2493 GothicPopUp( "", VariantNormal);
\r
2496 /* if (boardSize == oldBoardSize) return; [HGM] variant might have changed */
\r
2498 /* Load piece bitmaps for this board size */
\r
2499 for (i=0; i<=2; i++) {
\r
2500 for (piece = WhitePawn;
\r
2501 (int) piece < (int) BlackPawn;
\r
2502 piece = (ChessSquare) ((int) piece + 1)) {
\r
2503 if (pieceBitmap[i][piece] != NULL)
\r
2504 DeleteObject(pieceBitmap[i][piece]);
\r
2508 fontBitmapSquareSize = 0; /* [HGM] render: make sure pieces will be recreated, as we might need others now */
\r
2509 // Orthodox Chess pieces
\r
2510 pieceBitmap[0][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "s");
\r
2511 pieceBitmap[0][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "s");
\r
2512 pieceBitmap[0][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "s");
\r
2513 pieceBitmap[0][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "s");
\r
2514 pieceBitmap[0][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "s");
\r
2515 pieceBitmap[1][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "o");
\r
2516 pieceBitmap[1][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "o");
\r
2517 pieceBitmap[1][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "o");
\r
2518 pieceBitmap[1][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "o");
\r
2519 pieceBitmap[1][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "o");
\r
2520 pieceBitmap[2][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "w");
\r
2521 pieceBitmap[2][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "w");
\r
2522 pieceBitmap[2][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "w");
\r
2523 pieceBitmap[2][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "w");
\r
2524 pieceBitmap[2][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "w");
\r
2525 if( gameInfo.variant == VariantShogi && squareSize <= 72 && squareSize >= 33) {
\r
2526 // in Shogi, Hijack the unused Queen for Lance
\r
2527 pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "s");
\r
2528 pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "o");
\r
2529 pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "w");
\r
2531 pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "s");
\r
2532 pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "o");
\r
2533 pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "w");
\r
2536 if(squareSize <= 72 && squareSize >= 33) {
\r
2537 /* A & C are available in most sizes now */
\r
2538 if(squareSize != 49 && squareSize != 72 && squareSize != 33) { // Vortex-like
\r
2539 pieceBitmap[0][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "s");
\r
2540 pieceBitmap[1][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "o");
\r
2541 pieceBitmap[2][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "w");
\r
2542 pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "s");
\r
2543 pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "o");
\r
2544 pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "w");
\r
2545 pieceBitmap[0][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "s");
\r
2546 pieceBitmap[1][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "o");
\r
2547 pieceBitmap[2][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "w");
\r
2548 pieceBitmap[0][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "s");
\r
2549 pieceBitmap[1][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "o");
\r
2550 pieceBitmap[2][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "w");
\r
2551 } else { // Smirf-like
\r
2552 if(gameInfo.variant == VariantSChess) {
\r
2553 pieceBitmap[0][WhiteAngel] = DoLoadBitmap(hInst, "v", squareSize, "s");
\r
2554 pieceBitmap[1][WhiteAngel] = DoLoadBitmap(hInst, "v", squareSize, "o");
\r
2555 pieceBitmap[2][WhiteAngel] = DoLoadBitmap(hInst, "v", squareSize, "w");
\r
2557 pieceBitmap[0][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "s");
\r
2558 pieceBitmap[1][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "o");
\r
2559 pieceBitmap[2][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "w");
\r
2562 if(gameInfo.variant == VariantGothic) { // Vortex-like
\r
2563 pieceBitmap[0][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "s");
\r
2564 pieceBitmap[1][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "o");
\r
2565 pieceBitmap[2][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "w");
\r
2566 } else if(gameInfo.variant == VariantSChess && (squareSize == 49 || squareSize == 72)) {
\r
2567 pieceBitmap[0][WhiteMarshall] = DoLoadBitmap(hInst, "e", squareSize, "s");
\r
2568 pieceBitmap[1][WhiteMarshall] = DoLoadBitmap(hInst, "e", squareSize, "o");
\r
2569 pieceBitmap[2][WhiteMarshall] = DoLoadBitmap(hInst, "e", squareSize, "w");
\r
2570 } else { // WinBoard standard
\r
2571 pieceBitmap[0][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "s");
\r
2572 pieceBitmap[1][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "o");
\r
2573 pieceBitmap[2][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "w");
\r
2578 if(squareSize==72 || squareSize==49 || squareSize==33) { /* experiment with some home-made bitmaps */
\r
2579 pieceBitmap[0][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "s");
\r
2580 pieceBitmap[1][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "o");
\r
2581 pieceBitmap[2][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "w");
\r
2582 pieceBitmap[0][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "s");
\r
2583 pieceBitmap[1][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "o");
\r
2584 pieceBitmap[2][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "w");
\r
2585 pieceBitmap[0][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "s");
\r
2586 pieceBitmap[1][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "o");
\r
2587 pieceBitmap[2][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "w");
\r
2588 pieceBitmap[0][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "s");
\r
2589 pieceBitmap[1][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "o");
\r
2590 pieceBitmap[2][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "w");
\r
2591 pieceBitmap[0][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "s");
\r
2592 pieceBitmap[1][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "o");
\r
2593 pieceBitmap[2][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "w");
\r
2594 pieceBitmap[0][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "s");
\r
2595 pieceBitmap[1][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "o");
\r
2596 pieceBitmap[2][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "w");
\r
2597 pieceBitmap[0][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "s");
\r
2598 pieceBitmap[1][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "o");
\r
2599 pieceBitmap[2][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "w");
\r
2600 pieceBitmap[0][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "s");
\r
2601 pieceBitmap[1][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "o");
\r
2602 pieceBitmap[2][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "w");
\r
2603 pieceBitmap[0][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "s");
\r
2604 pieceBitmap[1][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "o");
\r
2605 pieceBitmap[2][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "w");
\r
2606 pieceBitmap[0][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "s");
\r
2607 pieceBitmap[1][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "o");
\r
2608 pieceBitmap[2][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "w");
\r
2610 if(gameInfo.variant == VariantShogi) { /* promoted Gold represemtations */
\r
2611 pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "wp", squareSize, "s");
\r
2612 pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "wp", squareSize, "o");
\r
2613 pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "w", squareSize, "w");
\r
2614 pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "wn", squareSize, "s");
\r
2615 pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "wn", squareSize, "o");
\r
2616 pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "w", squareSize, "w");
\r
2617 pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "ws", squareSize, "s");
\r
2618 pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "ws", squareSize, "o");
\r
2619 pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "w", squareSize, "w");
\r
2620 pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "wl", squareSize, "s");
\r
2621 pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "wl", squareSize, "o");
\r
2622 pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "w", squareSize, "w");
\r
2624 pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "s");
\r
2625 pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "o");
\r
2626 pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "w");
\r
2627 pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "s");
\r
2628 pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "o");
\r
2629 pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "w");
\r
2630 pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "s");
\r
2631 pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "o");
\r
2632 pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "w");
\r
2633 pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "s");
\r
2634 pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "o");
\r
2635 pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "w");
\r
2638 } else { /* other size, no special bitmaps available. Use smaller symbols */
\r
2639 if((int)boardSize < 2) minorSize = sizeInfo[0].squareSize;
\r
2640 else minorSize = sizeInfo[(int)boardSize - 2].squareSize;
\r
2641 pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "s");
\r
2642 pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "o");
\r
2643 pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "w");
\r
2644 pieceBitmap[0][WhiteCardinal] = DoLoadBitmap(hInst, "b", minorSize, "s");
\r
2645 pieceBitmap[1][WhiteCardinal] = DoLoadBitmap(hInst, "b", minorSize, "o");
\r
2646 pieceBitmap[2][WhiteCardinal] = DoLoadBitmap(hInst, "b", minorSize, "w");
\r
2647 pieceBitmap[0][WhiteDragon] = DoLoadBitmap(hInst, "r", minorSize, "s");
\r
2648 pieceBitmap[1][WhiteDragon] = DoLoadBitmap(hInst, "r", minorSize, "o");
\r
2649 pieceBitmap[2][WhiteDragon] = DoLoadBitmap(hInst, "r", minorSize, "w");
\r
2650 pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "s");
\r
2651 pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "o");
\r
2652 pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "w");
\r
2656 if(gameInfo.variant == VariantShogi && squareSize == 58)
\r
2657 /* special Shogi support in this size */
\r
2658 { for (i=0; i<=2; i++) { /* replace all bitmaps */
\r
2659 for (piece = WhitePawn;
\r
2660 (int) piece < (int) BlackPawn;
\r
2661 piece = (ChessSquare) ((int) piece + 1)) {
\r
2662 if (pieceBitmap[i][piece] != NULL)
\r
2663 DeleteObject(pieceBitmap[i][piece]);
\r
2666 pieceBitmap[0][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "o");
\r
2667 pieceBitmap[0][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "o");
\r
2668 pieceBitmap[0][WhiteBishop] = DoLoadBitmap(hInst, "sb", squareSize, "o");
\r
2669 pieceBitmap[0][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "o");
\r
2670 pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "o");
\r
2671 pieceBitmap[0][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "o");
\r
2672 pieceBitmap[0][WhiteFerz] = DoLoadBitmap(hInst, "sf", squareSize, "o");
\r
2673 pieceBitmap[0][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "o");
\r
2674 pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "su", squareSize, "o");
\r
2675 pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "sh", squareSize, "o");
\r
2676 pieceBitmap[0][WhiteCardinal] = DoLoadBitmap(hInst, "sa", squareSize, "o");
\r
2677 pieceBitmap[0][WhiteDragon] = DoLoadBitmap(hInst, "sc", squareSize, "o");
\r
2678 pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "sg", squareSize, "o");
\r
2679 pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "ss", squareSize, "o");
\r
2680 pieceBitmap[1][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "o");
\r
2681 pieceBitmap[1][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "o");
\r
2682 pieceBitmap[1][WhiteBishop] = DoLoadBitmap(hInst, "sb", squareSize, "o");
\r
2683 pieceBitmap[1][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "o");
\r
2684 pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "o");
\r
2685 pieceBitmap[1][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "o");
\r
2686 pieceBitmap[1][WhiteFerz] = DoLoadBitmap(hInst, "sf", squareSize, "o");
\r
2687 pieceBitmap[1][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "o");
\r
2688 pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "su", squareSize, "o");
\r
2689 pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "sh", squareSize, "o");
\r
2690 pieceBitmap[1][WhiteCardinal] = DoLoadBitmap(hInst, "sa", squareSize, "o");
\r
2691 pieceBitmap[1][WhiteDragon] = DoLoadBitmap(hInst, "sc", squareSize, "o");
\r
2692 pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "sg", squareSize, "o");
\r
2693 pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "ss", squareSize, "o");
\r
2694 pieceBitmap[2][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "w");
\r
2695 pieceBitmap[2][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "w");
\r
2696 pieceBitmap[2][WhiteBishop] = DoLoadBitmap(hInst, "sr", squareSize, "w");
\r
2697 pieceBitmap[2][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "w");
\r
2698 pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "w");
\r
2699 pieceBitmap[2][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "w");
\r
2700 pieceBitmap[2][WhiteFerz] = DoLoadBitmap(hInst, "sw", squareSize, "w");
\r
2701 pieceBitmap[2][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "w");
\r
2702 pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "sp", squareSize, "w");
\r
2703 pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "sn", squareSize, "w");
\r
2704 pieceBitmap[2][WhiteCardinal] = DoLoadBitmap(hInst, "sr", squareSize, "w");
\r
2705 pieceBitmap[2][WhiteDragon] = DoLoadBitmap(hInst, "sr", squareSize, "w");
\r
2706 pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "sl", squareSize, "w");
\r
2707 pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "sw", squareSize, "w");
\r
2713 PieceBitmap(ChessSquare p, int kind)
\r
2715 if ((int) p >= (int) BlackPawn)
\r
2716 p = (ChessSquare) ((int) p - (int) BlackPawn + (int) WhitePawn);
\r
2718 return pieceBitmap[kind][(int) p];
\r
2721 /***************************************************************/
\r
2723 #define MIN(a,b) (((a) < (b)) ? (a) : (b))
\r
2724 #define MAX(a,b) (((a) > (b)) ? (a) : (b))
\r
2726 #define MIN3(a,b,c) (((a) < (b) && (a) < (c)) ? (a) : (((b) < (a) && (b) < (c)) ? (b) : (c)))
\r
2727 #define MAX3(a,b,c) (((a) > (b) && (a) > (c)) ? (a) : (((b) > (a) && (b) > (c)) ? (b) : (c)))
\r
2731 SquareToPos(int row, int column, int * x, int * y)
\r
2734 *x = boardRect.left + lineGap + ((BOARD_WIDTH-1)-column) * (squareSize + lineGap);
\r
2735 *y = boardRect.top + lineGap + row * (squareSize + lineGap);
\r
2737 *x = boardRect.left + lineGap + column * (squareSize + lineGap);
\r
2738 *y = boardRect.top + lineGap + ((BOARD_HEIGHT-1)-row) * (squareSize + lineGap);
\r
2743 DrawCoordsOnDC(HDC hdc)
\r
2745 static char files[] = "0123456789012345678901221098765432109876543210";
\r
2746 static char ranks[] = "wvutsrqponmlkjihgfedcbaabcdefghijklmnopqrstuvw";
\r
2747 char str[2] = { NULLCHAR, NULLCHAR };
\r
2748 int oldMode, oldAlign, x, y, start, i;
\r
2752 if (!appData.showCoords)
\r
2755 start = flipView ? 1-(ONE!='1') : 45+(ONE!='1')-BOARD_HEIGHT;
\r
2757 oldBrush = SelectObject(hdc, GetStockObject(BLACK_BRUSH));
\r
2758 oldMode = SetBkMode(hdc, (appData.monoMode ? OPAQUE : TRANSPARENT));
\r
2759 oldAlign = GetTextAlign(hdc);
\r
2760 oldFont = SelectObject(hdc, font[boardSize][COORD_FONT]->hf);
\r
2762 y = boardRect.top + lineGap;
\r
2763 x = boardRect.left + lineGap + gameInfo.holdingsWidth*(squareSize + lineGap);
\r
2765 SetTextAlign(hdc, TA_LEFT|TA_TOP);
\r
2766 for (i = 0; i < BOARD_HEIGHT; i++) {
\r
2767 str[0] = files[start + i];
\r
2768 ExtTextOut(hdc, x + 2, y + 1, 0, NULL, str, 1, NULL);
\r
2769 y += squareSize + lineGap;
\r
2772 start = flipView ? 23-(BOARD_RGHT-BOARD_LEFT) : 23;
\r
2774 SetTextAlign(hdc, TA_RIGHT|TA_BOTTOM);
\r
2775 for (i = 0; i < BOARD_RGHT - BOARD_LEFT; i++) {
\r
2776 str[0] = ranks[start + i];
\r
2777 ExtTextOut(hdc, x + squareSize - 2, y - 1, 0, NULL, str, 1, NULL);
\r
2778 x += squareSize + lineGap;
\r
2781 SelectObject(hdc, oldBrush);
\r
2782 SetBkMode(hdc, oldMode);
\r
2783 SetTextAlign(hdc, oldAlign);
\r
2784 SelectObject(hdc, oldFont);
\r
2788 DrawGridOnDC(HDC hdc)
\r
2792 if (lineGap != 0) {
\r
2793 oldPen = SelectObject(hdc, gridPen);
\r
2794 PolyPolyline(hdc, gridEndpoints, gridVertexCounts, BOARD_WIDTH+BOARD_HEIGHT + 2);
\r
2795 SelectObject(hdc, oldPen);
\r
2799 #define HIGHLIGHT_PEN 0
\r
2800 #define PREMOVE_PEN 1
\r
2803 DrawHighlightOnDC(HDC hdc, BOOLEAN on, int x, int y, int pen)
\r
2806 HPEN oldPen, hPen;
\r
2807 if (lineGap == 0) return;
\r
2809 x1 = boardRect.left +
\r
2810 lineGap/2 + ((BOARD_WIDTH-1)-x) * (squareSize + lineGap);
\r
2811 y1 = boardRect.top +
\r
2812 lineGap/2 + y * (squareSize + lineGap);
\r
2814 x1 = boardRect.left +
\r
2815 lineGap/2 + x * (squareSize + lineGap);
\r
2816 y1 = boardRect.top +
\r
2817 lineGap/2 + ((BOARD_HEIGHT-1)-y) * (squareSize + lineGap);
\r
2819 hPen = pen ? premovePen : highlightPen;
\r
2820 oldPen = SelectObject(hdc, on ? hPen : gridPen);
\r
2821 MoveToEx(hdc, x1, y1, NULL);
\r
2822 LineTo(hdc, x1 + squareSize + lineGap, y1);
\r
2823 LineTo(hdc, x1 + squareSize + lineGap, y1 + squareSize + lineGap);
\r
2824 LineTo(hdc, x1, y1 + squareSize + lineGap);
\r
2825 LineTo(hdc, x1, y1);
\r
2826 SelectObject(hdc, oldPen);
\r
2830 DrawHighlightsOnDC(HDC hdc, HighlightInfo *h, int pen)
\r
2833 for (i=0; i<2; i++) {
\r
2834 if (h->sq[i].x >= 0 && h->sq[i].y >= 0)
\r
2835 DrawHighlightOnDC(hdc, TRUE,
\r
2836 h->sq[i].x, h->sq[i].y,
\r
2841 /* Note: sqcolor is used only in monoMode */
\r
2842 /* Note that this code is largely duplicated in woptions.c,
\r
2843 function DrawSampleSquare, so that needs to be updated too */
\r
2845 DrawPieceOnDC(HDC hdc, ChessSquare piece, int color, int sqcolor, int x, int y, HDC tmphdc)
\r
2847 HBITMAP oldBitmap;
\r
2851 if (appData.blindfold) return;
\r
2853 /* [AS] Use font-based pieces if needed */
\r
2854 if( fontBitmapSquareSize >= 0 && (squareSize > 32 || gameInfo.variant >= VariantShogi)) {
\r
2855 /* Create piece bitmaps, or do nothing if piece set is up to date */
\r
2856 CreatePiecesFromFont();
\r
2858 if( fontBitmapSquareSize == squareSize ) {
\r
2859 int index = TranslatePieceToFontPiece(piece);
\r
2861 SelectObject( tmphdc, hPieceMask[ index ] );
\r
2863 if(appData.upsideDown ? color==flipView : (flipView && gameInfo.variant == VariantShogi))
\r
2864 StretchBlt(hdc, x+squareSize, y+squareSize, -squareSize, -squareSize, tmphdc, 0, 0, squareSize, squareSize, SRCAND);
\r
2868 squareSize, squareSize,
\r
2873 SelectObject( tmphdc, hPieceFace[ index ] );
\r
2875 if(appData.upsideDown ? color==flipView : (flipView && gameInfo.variant == VariantShogi))
\r
2876 StretchBlt(hdc, x+squareSize, y+squareSize, -squareSize, -squareSize, tmphdc, 0, 0, squareSize, squareSize, SRCPAINT);
\r
2880 squareSize, squareSize,
\r
2889 if (appData.monoMode) {
\r
2890 SelectObject(tmphdc, PieceBitmap(piece,
\r
2891 color == sqcolor ? OUTLINE_PIECE : SOLID_PIECE));
\r
2892 BitBlt(hdc, x, y, squareSize, squareSize, tmphdc, 0, 0,
\r
2893 sqcolor ? SRCCOPY : NOTSRCCOPY);
\r
2895 tmpSize = squareSize;
\r
2897 ((piece >= (int)WhiteNightrider && piece <= WhiteGrasshopper) ||
\r
2898 (piece >= (int)BlackNightrider && piece <= BlackGrasshopper)) ) {
\r
2899 /* [HGM] no bitmap available for promoted pieces in Crazyhouse */
\r
2900 /* Bitmaps of smaller size are substituted, but we have to align them */
\r
2901 x += (squareSize - minorSize)>>1;
\r
2902 y += squareSize - minorSize - 2;
\r
2903 tmpSize = minorSize;
\r
2905 if (color || appData.allWhite ) {
\r
2906 oldBitmap = SelectObject(tmphdc, PieceBitmap(piece, WHITE_PIECE));
\r
2908 oldBrush = SelectObject(hdc, whitePieceBrush);
\r
2909 else oldBrush = SelectObject(hdc, blackPieceBrush);
\r
2910 if(appData.upsideDown && color==flipView)
\r
2911 StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, 0x00B8074A);
\r
2913 BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, 0x00B8074A);
\r
2914 /* Use black for outline of white pieces */
\r
2915 SelectObject(tmphdc, PieceBitmap(piece, OUTLINE_PIECE));
\r
2916 if(appData.upsideDown && color==flipView)
\r
2917 StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, SRCAND);
\r
2919 BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, SRCAND);
\r
2921 /* Use square color for details of black pieces */
\r
2922 oldBitmap = SelectObject(tmphdc, PieceBitmap(piece, SOLID_PIECE));
\r
2923 oldBrush = SelectObject(hdc, blackPieceBrush);
\r
2924 if(appData.upsideDown && !flipView)
\r
2925 StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, 0x00B8074A);
\r
2927 BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, 0x00B8074A);
\r
2929 SelectObject(hdc, oldBrush);
\r
2930 SelectObject(tmphdc, oldBitmap);
\r
2934 /* [AS] Compute a drawing mode for a square, based on specified settings (see DrawTile) */
\r
2935 int GetBackTextureMode( int algo )
\r
2937 int result = BACK_TEXTURE_MODE_DISABLED;
\r
2941 case BACK_TEXTURE_MODE_PLAIN:
\r
2942 result = 1; /* Always use identity map */
\r
2944 case BACK_TEXTURE_MODE_FULL_RANDOM:
\r
2945 result = 1 + (myrandom() % 3); /* Pick a transformation at random */
\r
2953 [AS] Compute and save texture drawing info, otherwise we may not be able
\r
2954 to handle redraws cleanly (as random numbers would always be different).
\r
2956 VOID RebuildTextureSquareInfo()
\r
2966 ZeroMemory( &backTextureSquareInfo, sizeof(backTextureSquareInfo) );
\r
2968 if( liteBackTexture != NULL ) {
\r
2969 if( GetObject( liteBackTexture, sizeof(bi), &bi ) > 0 ) {
\r
2970 lite_w = bi.bmWidth;
\r
2971 lite_h = bi.bmHeight;
\r
2975 if( darkBackTexture != NULL ) {
\r
2976 if( GetObject( darkBackTexture, sizeof(bi), &bi ) > 0 ) {
\r
2977 dark_w = bi.bmWidth;
\r
2978 dark_h = bi.bmHeight;
\r
2982 for( row=0; row<BOARD_HEIGHT; row++ ) {
\r
2983 for( col=0; col<BOARD_WIDTH; col++ ) {
\r
2984 if( (col + row) & 1 ) {
\r
2986 if( lite_w >= squareSize && lite_h >= squareSize ) {
\r
2987 if( lite_w >= squareSize*BOARD_WIDTH )
\r
2988 backTextureSquareInfo[row][col].x = (2*col+1)*lite_w/(2*BOARD_WIDTH) - squareSize/2; /* [HGM] cut out of center of virtual square */
\r
2990 backTextureSquareInfo[row][col].x = col * (lite_w - squareSize) / (BOARD_WIDTH-1); /* [HGM] divide by size-1 in stead of size! */
\r
2991 if( lite_h >= squareSize*BOARD_HEIGHT )
\r
2992 backTextureSquareInfo[row][col].y = (2*(BOARD_HEIGHT-row)-1)*lite_h/(2*BOARD_HEIGHT) - squareSize/2;
\r
2994 backTextureSquareInfo[row][col].y = (BOARD_HEIGHT-1-row) * (lite_h - squareSize) / (BOARD_HEIGHT-1);
\r
2995 backTextureSquareInfo[row][col].mode = GetBackTextureMode(liteBackTextureMode);
\r
3000 if( dark_w >= squareSize && dark_h >= squareSize ) {
\r
3001 if( dark_w >= squareSize*BOARD_WIDTH )
\r
3002 backTextureSquareInfo[row][col].x = (2*col+1) * dark_w / (2*BOARD_WIDTH) - squareSize/2;
\r
3004 backTextureSquareInfo[row][col].x = col * (dark_w - squareSize) / (BOARD_WIDTH-1);
\r
3005 if( dark_h >= squareSize*BOARD_HEIGHT )
\r
3006 backTextureSquareInfo[row][col].y = (2*(BOARD_HEIGHT-row)-1) * dark_h / (2*BOARD_HEIGHT) - squareSize/2;
\r
3008 backTextureSquareInfo[row][col].y = (BOARD_HEIGHT-1-row) * (dark_h - squareSize) / (BOARD_HEIGHT-1);
\r
3009 backTextureSquareInfo[row][col].mode = GetBackTextureMode(darkBackTextureMode);
\r
3016 /* [AS] Arrow highlighting support */
\r
3018 static double A_WIDTH = 5; /* Width of arrow body */
\r
3020 #define A_HEIGHT_FACTOR 6 /* Length of arrow "point", relative to body width */
\r
3021 #define A_WIDTH_FACTOR 3 /* Width of arrow "point", relative to body width */
\r
3023 static double Sqr( double x )
\r
3028 static int Round( double x )
\r
3030 return (int) (x + 0.5);
\r
3033 /* Draw an arrow between two points using current settings */
\r
3034 VOID DrawArrowBetweenPoints( HDC hdc, int s_x, int s_y, int d_x, int d_y )
\r
3037 double dx, dy, j, k, x, y;
\r
3039 if( d_x == s_x ) {
\r
3040 int h = (d_y > s_y) ? +A_WIDTH*A_HEIGHT_FACTOR : -A_WIDTH*A_HEIGHT_FACTOR;
\r
3042 arrow[0].x = s_x + A_WIDTH + 0.5;
\r
3045 arrow[1].x = s_x + A_WIDTH + 0.5;
\r
3046 arrow[1].y = d_y - h;
\r
3048 arrow[2].x = arrow[1].x + A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;
\r
3049 arrow[2].y = d_y - h;
\r
3054 arrow[5].x = arrow[1].x - 2*A_WIDTH + 0.5;
\r
3055 arrow[5].y = d_y - h;
\r
3057 arrow[4].x = arrow[5].x - A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;
\r
3058 arrow[4].y = d_y - h;
\r
3060 arrow[6].x = arrow[1].x - 2*A_WIDTH + 0.5;
\r
3063 else if( d_y == s_y ) {
\r
3064 int w = (d_x > s_x) ? +A_WIDTH*A_HEIGHT_FACTOR : -A_WIDTH*A_HEIGHT_FACTOR;
\r
3067 arrow[0].y = s_y + A_WIDTH + 0.5;
\r
3069 arrow[1].x = d_x - w;
\r
3070 arrow[1].y = s_y + A_WIDTH + 0.5;
\r
3072 arrow[2].x = d_x - w;
\r
3073 arrow[2].y = arrow[1].y + A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;
\r
3078 arrow[5].x = d_x - w;
\r
3079 arrow[5].y = arrow[1].y - 2*A_WIDTH + 0.5;
\r
3081 arrow[4].x = d_x - w;
\r
3082 arrow[4].y = arrow[5].y - A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;
\r
3085 arrow[6].y = arrow[1].y - 2*A_WIDTH + 0.5;
\r
3088 /* [AS] Needed a lot of paper for this! :-) */
\r
3089 dy = (double) (d_y - s_y) / (double) (d_x - s_x);
\r
3090 dx = (double) (s_x - d_x) / (double) (s_y - d_y);
\r
3092 j = sqrt( Sqr(A_WIDTH) / (1.0 + Sqr(dx)) );
\r
3094 k = sqrt( Sqr(A_WIDTH*A_HEIGHT_FACTOR) / (1.0 + Sqr(dy)) );
\r
3099 arrow[0].x = Round(x - j);
\r
3100 arrow[0].y = Round(y + j*dx);
\r
3102 arrow[1].x = Round(arrow[0].x + 2*j); // [HGM] prevent width to be affected by rounding twice
\r
3103 arrow[1].y = Round(arrow[0].y - 2*j*dx);
\r
3106 x = (double) d_x - k;
\r
3107 y = (double) d_y - k*dy;
\r
3110 x = (double) d_x + k;
\r
3111 y = (double) d_y + k*dy;
\r
3114 x = Round(x); y = Round(y); // [HGM] make sure width of shaft is rounded the same way on both ends
\r
3116 arrow[6].x = Round(x - j);
\r
3117 arrow[6].y = Round(y + j*dx);
\r
3119 arrow[2].x = Round(arrow[6].x + 2*j);
\r
3120 arrow[2].y = Round(arrow[6].y - 2*j*dx);
\r
3122 arrow[3].x = Round(arrow[2].x + j*(A_WIDTH_FACTOR-1));
\r
3123 arrow[3].y = Round(arrow[2].y - j*(A_WIDTH_FACTOR-1)*dx);
\r
3128 arrow[5].x = Round(arrow[6].x - j*(A_WIDTH_FACTOR-1));
\r
3129 arrow[5].y = Round(arrow[6].y + j*(A_WIDTH_FACTOR-1)*dx);
\r
3132 Polygon( hdc, arrow, 7 );
\r
3135 /* [AS] Draw an arrow between two squares */
\r
3136 VOID DrawArrowBetweenSquares( HDC hdc, int s_col, int s_row, int d_col, int d_row )
\r
3138 int s_x, s_y, d_x, d_y;
\r
3145 if( s_col == d_col && s_row == d_row ) {
\r
3149 /* Get source and destination points */
\r
3150 SquareToPos( s_row, s_col, &s_x, &s_y);
\r
3151 SquareToPos( d_row, d_col, &d_x, &d_y);
\r
3154 d_y += squareSize / 2 - squareSize / 4; // [HGM] round towards same centers on all sides!
\r
3156 else if( d_y < s_y ) {
\r
3157 d_y += squareSize / 2 + squareSize / 4;
\r
3160 d_y += squareSize / 2;
\r
3164 d_x += squareSize / 2 - squareSize / 4;
\r
3166 else if( d_x < s_x ) {
\r
3167 d_x += squareSize / 2 + squareSize / 4;
\r
3170 d_x += squareSize / 2;
\r
3173 s_x += squareSize / 2;
\r
3174 s_y += squareSize / 2;
\r
3176 /* Adjust width */
\r
3177 A_WIDTH = squareSize / 14.; //[HGM] make float
\r
3180 stLB.lbStyle = BS_SOLID;
\r
3181 stLB.lbColor = appData.highlightArrowColor;
\r
3184 hpen = CreatePen( PS_SOLID, 2, RGB(0x00,0x00,0x00) );
\r
3185 holdpen = SelectObject( hdc, hpen );
\r
3186 hbrush = CreateBrushIndirect( &stLB );
\r
3187 holdbrush = SelectObject( hdc, hbrush );
\r
3189 DrawArrowBetweenPoints( hdc, s_x, s_y, d_x, d_y );
\r
3191 SelectObject( hdc, holdpen );
\r
3192 SelectObject( hdc, holdbrush );
\r
3193 DeleteObject( hpen );
\r
3194 DeleteObject( hbrush );
\r
3197 BOOL HasHighlightInfo()
\r
3199 BOOL result = FALSE;
\r
3201 if( highlightInfo.sq[0].x >= 0 && highlightInfo.sq[0].y >= 0 &&
\r
3202 highlightInfo.sq[1].x >= 0 && highlightInfo.sq[1].y >= 0 )
\r
3210 BOOL IsDrawArrowEnabled()
\r
3212 BOOL result = FALSE;
\r
3214 if( appData.highlightMoveWithArrow && squareSize >= 32 ) {
\r
3221 VOID DrawArrowHighlight( HDC hdc )
\r
3223 if( IsDrawArrowEnabled() && HasHighlightInfo() ) {
\r
3224 DrawArrowBetweenSquares( hdc,
\r
3225 highlightInfo.sq[0].x, highlightInfo.sq[0].y,
\r
3226 highlightInfo.sq[1].x, highlightInfo.sq[1].y );
\r
3230 HRGN GetArrowHighlightClipRegion( HDC hdc )
\r
3232 HRGN result = NULL;
\r
3234 if( HasHighlightInfo() ) {
\r
3235 int x1, y1, x2, y2;
\r
3236 int sx, sy, dx, dy;
\r
3238 SquareToPos(highlightInfo.sq[0].y, highlightInfo.sq[0].x, &x1, &y1 );
\r
3239 SquareToPos(highlightInfo.sq[1].y, highlightInfo.sq[1].x, &x2, &y2 );
\r
3241 sx = MIN( x1, x2 );
\r
3242 sy = MIN( y1, y2 );
\r
3243 dx = MAX( x1, x2 ) + squareSize;
\r
3244 dy = MAX( y1, y2 ) + squareSize;
\r
3246 result = CreateRectRgn( sx, sy, dx, dy );
\r
3253 Warning: this function modifies the behavior of several other functions.
\r
3255 Basically, Winboard is optimized to avoid drawing the whole board if not strictly
\r
3256 needed. Unfortunately, the decision whether or not to perform a full or partial
\r
3257 repaint is scattered all over the place, which is not good for features such as
\r
3258 "arrow highlighting" that require a full repaint of the board.
\r
3260 So, I've tried to patch the code where I thought it made sense (e.g. after or during
\r
3261 user interaction, when speed is not so important) but especially to avoid errors
\r
3262 in the displayed graphics.
\r
3264 In such patched places, I always try refer to this function so there is a single
\r
3265 place to maintain knowledge.
\r
3267 To restore the original behavior, just return FALSE unconditionally.
\r
3269 BOOL IsFullRepaintPreferrable()
\r
3271 BOOL result = FALSE;
\r
3273 if( (appData.highlightLastMove || appData.highlightDragging) && IsDrawArrowEnabled() ) {
\r
3274 /* Arrow may appear on the board */
\r
3282 This function is called by DrawPosition to know whether a full repaint must
\r
3285 Only DrawPosition may directly call this function, which makes use of
\r
3286 some state information. Other function should call DrawPosition specifying
\r
3287 the repaint flag, and can use IsFullRepaintPreferrable if needed.
\r
3289 BOOL DrawPositionNeedsFullRepaint()
\r
3291 BOOL result = FALSE;
\r
3294 Probably a slightly better policy would be to trigger a full repaint
\r
3295 when animInfo.piece changes state (i.e. empty -> non-empty and viceversa),
\r
3296 but animation is fast enough that it's difficult to notice.
\r
3298 if( animInfo.piece == EmptySquare ) {
\r
3299 if( (appData.highlightLastMove || appData.highlightDragging) && IsDrawArrowEnabled() /*&& HasHighlightInfo()*/ ) {
\r
3308 DrawBoardOnDC(HDC hdc, Board board, HDC tmphdc)
\r
3310 int row, column, x, y, square_color, piece_color;
\r
3311 ChessSquare piece;
\r
3313 HDC texture_hdc = NULL;
\r
3315 /* [AS] Initialize background textures if needed */
\r
3316 if( liteBackTexture != NULL || darkBackTexture != NULL ) {
\r
3317 static int backTextureBoardSize; /* [HGM] boardsize: also new texture if board format changed */
\r
3318 if( backTextureSquareSize != squareSize
\r
3319 || backTextureBoardSize != BOARD_WIDTH+BOARD_FILES*BOARD_HEIGHT) {
\r
3320 backTextureBoardSize = BOARD_WIDTH+BOARD_FILES*BOARD_HEIGHT;
\r
3321 backTextureSquareSize = squareSize;
\r
3322 RebuildTextureSquareInfo();
\r
3325 texture_hdc = CreateCompatibleDC( hdc );
\r
3328 for (row = 0; row < BOARD_HEIGHT; row++) {
\r
3329 for (column = 0; column < BOARD_WIDTH; column++) {
\r
3331 SquareToPos(row, column, &x, &y);
\r
3333 piece = board[row][column];
\r
3335 square_color = ((column + row) % 2) == 1;
\r
3336 if( gameInfo.variant == VariantXiangqi ) {
\r
3337 square_color = !InPalace(row, column);
\r
3338 if(BOARD_HEIGHT&1) { if(row==BOARD_HEIGHT/2) square_color ^= 1; }
\r
3339 else if(row < BOARD_HEIGHT/2) square_color ^= 1;
\r
3341 piece_color = (int) piece < (int) BlackPawn;
\r
3344 /* [HGM] holdings file: light square or black */
\r
3345 if(column == BOARD_LEFT-2) {
\r
3346 if( row > BOARD_HEIGHT - gameInfo.holdingsSize - 1 )
\r
3349 DisplayHoldingsCount(hdc, x, y, 0, 0); /* black out */
\r
3353 if(column == BOARD_RGHT + 1 ) {
\r
3354 if( row < gameInfo.holdingsSize )
\r
3357 DisplayHoldingsCount(hdc, x, y, 0, 0);
\r
3361 if(column == BOARD_LEFT-1 ) /* left align */
\r
3362 DisplayHoldingsCount(hdc, x, y, flipView, (int) board[row][column]);
\r
3363 else if( column == BOARD_RGHT) /* right align */
\r
3364 DisplayHoldingsCount(hdc, x, y, !flipView, (int) board[row][column]);
\r
3366 if (appData.monoMode) {
\r
3367 if (piece == EmptySquare) {
\r
3368 BitBlt(hdc, x, y, squareSize, squareSize, 0, 0, 0,
\r
3369 square_color ? WHITENESS : BLACKNESS);
\r
3371 DrawPieceOnDC(hdc, piece, piece_color, square_color, x, y, tmphdc);
\r
3374 else if( appData.useBitmaps && backTextureSquareInfo[row][column].mode > 0 ) {
\r
3375 /* [AS] Draw the square using a texture bitmap */
\r
3376 HBITMAP hbm = SelectObject( texture_hdc, square_color ? liteBackTexture : darkBackTexture );
\r
3377 int r = row, c = column; // [HGM] do not flip board in flipView
\r
3378 if(flipView) { r = BOARD_HEIGHT-1 - r; c = BOARD_WIDTH-1 - c; }
\r
3381 squareSize, squareSize,
\r
3384 backTextureSquareInfo[r][c].mode,
\r
3385 backTextureSquareInfo[r][c].x,
\r
3386 backTextureSquareInfo[r][c].y );
\r
3388 SelectObject( texture_hdc, hbm );
\r
3390 if (piece != EmptySquare) {
\r
3391 DrawPieceOnDC(hdc, piece, piece_color, -1, x, y, tmphdc);
\r
3395 HBRUSH brush = square_color ? lightSquareBrush : darkSquareBrush;
\r
3397 oldBrush = SelectObject(hdc, brush );
\r
3398 BitBlt(hdc, x, y, squareSize, squareSize, 0, 0, 0, PATCOPY);
\r
3399 SelectObject(hdc, oldBrush);
\r
3400 if (piece != EmptySquare)
\r
3401 DrawPieceOnDC(hdc, piece, piece_color, -1, x, y, tmphdc);
\r
3406 if( texture_hdc != NULL ) {
\r
3407 DeleteDC( texture_hdc );
\r
3411 int saveDiagFlag = 0; FILE *diagFile; // [HGM] diag
\r
3412 void fputDW(FILE *f, int x)
\r
3414 fputc(x & 255, f);
\r
3415 fputc(x>>8 & 255, f);
\r
3416 fputc(x>>16 & 255, f);
\r
3417 fputc(x>>24 & 255, f);
\r
3420 #define MAX_CLIPS 200 /* more than enough */
\r
3423 DrawLogoOnDC(HDC hdc, RECT logoRect, HBITMAP logo)
\r
3425 // HBITMAP bufferBitmap;
\r
3430 int w = 100, h = 50;
\r
3432 if(logo == NULL) {
\r
3433 if(!logoHeight) return;
\r
3434 FillRect( hdc, &logoRect, whitePieceBrush );
\r
3436 // GetClientRect(hwndMain, &Rect);
\r
3437 // bufferBitmap = CreateCompatibleBitmap(hdc, Rect.right-Rect.left+1,
\r
3438 // Rect.bottom-Rect.top+1);
\r
3439 tmphdc = CreateCompatibleDC(hdc);
\r
3440 hbm = SelectObject(tmphdc, logo);
\r
3441 if( GetObject( logo, sizeof(bi), &bi ) > 0 ) {
\r
3445 StretchBlt(hdc, logoRect.left, logoRect.top, logoRect.right - logoRect.left,
\r
3446 logoRect.bottom - logoRect.top, tmphdc, 0, 0, w, h, SRCCOPY);
\r
3447 SelectObject(tmphdc, hbm);
\r
3455 HDC hdc = GetDC(hwndMain);
\r
3456 HBITMAP whiteLogo = (HBITMAP) first.programLogo, blackLogo = (HBITMAP) second.programLogo;
\r
3457 if(appData.autoLogo) {
\r
3459 switch(gameMode) { // pick logos based on game mode
\r
3460 case IcsObserving:
\r
3461 whiteLogo = second.programLogo; // ICS logo
\r
3462 blackLogo = second.programLogo;
\r
3465 case IcsPlayingWhite:
\r
3466 if(!appData.zippyPlay) whiteLogo = userLogo;
\r
3467 blackLogo = second.programLogo; // ICS logo
\r
3469 case IcsPlayingBlack:
\r
3470 whiteLogo = second.programLogo; // ICS logo
\r
3471 blackLogo = appData.zippyPlay ? first.programLogo : userLogo;
\r
3473 case TwoMachinesPlay:
\r
3474 if(first.twoMachinesColor[0] == 'b') {
\r
3475 whiteLogo = second.programLogo;
\r
3476 blackLogo = first.programLogo;
\r
3479 case MachinePlaysWhite:
\r
3480 blackLogo = userLogo;
\r
3482 case MachinePlaysBlack:
\r
3483 whiteLogo = userLogo;
\r
3484 blackLogo = first.programLogo;
\r
3487 DrawLogoOnDC(hdc, leftLogoRect, flipClock ? blackLogo : whiteLogo);
\r
3488 DrawLogoOnDC(hdc, rightLogoRect, flipClock ? whiteLogo : blackLogo);
\r
3489 ReleaseDC(hwndMain, hdc);
\r
3494 UpdateLogos(int display)
\r
3495 { // called after loading new engine(s), in tourney or from menu
\r
3496 LoadLogo(&first, 0, FALSE);
\r
3497 LoadLogo(&second, 1, appData.icsActive);
\r
3498 InitDrawingSizes(-2, 0); // adapt layout of board window to presence/absence of logos
\r
3499 if(display) DisplayLogos();
\r
3502 static HDC hdcSeek;
\r
3504 // [HGM] seekgraph
\r
3505 void DrawSeekAxis( int x, int y, int xTo, int yTo )
\r
3508 HPEN hp = SelectObject( hdcSeek, gridPen );
\r
3509 MoveToEx( hdcSeek, boardRect.left+x, boardRect.top+y, &stPt );
\r
3510 LineTo( hdcSeek, boardRect.left+xTo, boardRect.top+yTo );
\r
3511 SelectObject( hdcSeek, hp );
\r
3514 // front-end wrapper for drawing functions to do rectangles
\r
3515 void DrawSeekBackground( int left, int top, int right, int bottom )
\r
3520 if (hdcSeek == NULL) {
\r
3521 hdcSeek = GetDC(hwndMain);
\r
3522 if (!appData.monoMode) {
\r
3523 SelectPalette(hdcSeek, hPal, FALSE);
\r
3524 RealizePalette(hdcSeek);
\r
3527 hp = SelectObject( hdcSeek, gridPen );
\r
3528 rc.top = boardRect.top+top; rc.left = boardRect.left+left;
\r
3529 rc.bottom = boardRect.top+bottom; rc.right = boardRect.left+right;
\r
3530 FillRect( hdcSeek, &rc, lightSquareBrush );
\r
3531 SelectObject( hdcSeek, hp );
\r
3534 // front-end wrapper for putting text in graph
\r
3535 void DrawSeekText(char *buf, int x, int y)
\r
3538 SetBkMode( hdcSeek, TRANSPARENT );
\r
3539 GetTextExtentPoint32( hdcSeek, buf, strlen(buf), &stSize );
\r
3540 TextOut( hdcSeek, boardRect.left+x-3, boardRect.top+y-stSize.cy/2, buf, strlen(buf) );
\r
3543 void DrawSeekDot(int x, int y, int color)
\r
3545 int square = color & 0x80;
\r
3546 HBRUSH oldBrush = SelectObject(hdcSeek,
\r
3547 color == 0 ? markerBrush : color == 1 ? darkSquareBrush : explodeBrush);
\r
3550 Rectangle(hdcSeek, boardRect.left+x - squareSize/9, boardRect.top+y - squareSize/9,
\r
3551 boardRect.left+x + squareSize/9, boardRect.top+y + squareSize/9);
\r
3553 Ellipse(hdcSeek, boardRect.left+x - squareSize/8, boardRect.top+y - squareSize/8,
\r
3554 boardRect.left+x + squareSize/8, boardRect.top+y + squareSize/8);
\r
3555 SelectObject(hdcSeek, oldBrush);
\r
3558 void DrawSeekOpen()
\r
3562 void DrawSeekClose()
\r
3567 HDCDrawPosition(HDC hdc, BOOLEAN repaint, Board board)
\r
3569 static Board lastReq[2], lastDrawn[2];
\r
3570 static HighlightInfo lastDrawnHighlight, lastDrawnPremove;
\r
3571 static int lastDrawnFlipView = 0;
\r
3572 static int lastReqValid[2] = {0, 0}, lastDrawnValid[2] = {0, 0};
\r
3573 int releaseDC, x, y, x2, y2, row, column, num_clips = 0, i;
\r
3576 HBITMAP bufferBitmap;
\r
3577 HBITMAP oldBitmap;
\r
3579 HRGN clips[MAX_CLIPS];
\r
3580 ChessSquare dragged_piece = EmptySquare;
\r
3581 int nr = twoBoards*partnerUp;
\r
3583 /* I'm undecided on this - this function figures out whether a full
\r
3584 * repaint is necessary on its own, so there's no real reason to have the
\r
3585 * caller tell it that. I think this can safely be set to FALSE - but
\r
3586 * if we trust the callers not to request full repaints unnessesarily, then
\r
3587 * we could skip some clipping work. In other words, only request a full
\r
3588 * redraw when the majority of pieces have changed positions (ie. flip,
\r
3589 * gamestart and similar) --Hawk
\r
3591 Boolean fullrepaint = repaint;
\r
3593 if(DrawSeekGraph()) return; // [HG} seekgraph: suppress printing board if seek graph up
\r
3595 if( DrawPositionNeedsFullRepaint() ) {
\r
3596 fullrepaint = TRUE;
\r
3599 if (board == NULL) {
\r
3600 if (!lastReqValid[nr]) {
\r
3603 board = lastReq[nr];
\r
3605 CopyBoard(lastReq[nr], board);
\r
3606 lastReqValid[nr] = 1;
\r
3609 if (doingSizing) {
\r
3613 if (IsIconic(hwndMain)) {
\r
3617 if (hdc == NULL) {
\r
3618 hdc = GetDC(hwndMain);
\r
3619 if (!appData.monoMode) {
\r
3620 SelectPalette(hdc, hPal, FALSE);
\r
3621 RealizePalette(hdc);
\r
3625 releaseDC = FALSE;
\r
3628 /* Create some work-DCs */
\r
3629 hdcmem = CreateCompatibleDC(hdc);
\r
3630 tmphdc = CreateCompatibleDC(hdc);
\r
3632 /* If dragging is in progress, we temporarely remove the piece */
\r
3633 /* [HGM] or temporarily decrease count if stacked */
\r
3634 /* !! Moved to before board compare !! */
\r
3635 if (dragInfo.from.x >= 0 && dragInfo.pos.x >= 0) {
\r
3636 dragged_piece = board[dragInfo.from.y][dragInfo.from.x];
\r
3637 if(dragInfo.from.x == BOARD_LEFT-2 ) {
\r
3638 if(--board[dragInfo.from.y][dragInfo.from.x+1] == 0 )
\r
3639 board[dragInfo.from.y][dragInfo.from.x] = EmptySquare;
\r
3641 if(dragInfo.from.x == BOARD_RGHT+1) {
\r
3642 if(--board[dragInfo.from.y][dragInfo.from.x-1] == 0 )
\r
3643 board[dragInfo.from.y][dragInfo.from.x] = EmptySquare;
\r
3645 board[dragInfo.from.y][dragInfo.from.x] = gatingPiece;
\r
3648 /* Figure out which squares need updating by comparing the
\r
3649 * newest board with the last drawn board and checking if
\r
3650 * flipping has changed.
\r
3652 if (!fullrepaint && lastDrawnValid[nr] && (nr == 1 || lastDrawnFlipView == flipView)) {
\r
3653 for (row = 0; row < BOARD_HEIGHT; row++) { /* [HGM] true size, not 8 */
\r
3654 for (column = 0; column < BOARD_WIDTH; column++) {
\r
3655 if (lastDrawn[nr][row][column] != board[row][column]) {
\r
3656 SquareToPos(row, column, &x, &y);
\r
3657 clips[num_clips++] =
\r
3658 CreateRectRgn(x, y, x + squareSize, y + squareSize);
\r
3662 if(nr == 0) { // [HGM] dual: no highlights on second board
\r
3663 for (i=0; i<2; i++) {
\r
3664 if (lastDrawnHighlight.sq[i].x != highlightInfo.sq[i].x ||
\r
3665 lastDrawnHighlight.sq[i].y != highlightInfo.sq[i].y) {
\r
3666 if (lastDrawnHighlight.sq[i].x >= 0 &&
\r
3667 lastDrawnHighlight.sq[i].y >= 0) {
\r
3668 SquareToPos(lastDrawnHighlight.sq[i].y,
\r
3669 lastDrawnHighlight.sq[i].x, &x, &y);
\r
3670 clips[num_clips++] =
\r
3671 CreateRectRgn(x - lineGap, y - lineGap,
\r
3672 x + squareSize + lineGap, y + squareSize + lineGap);
\r
3674 if (highlightInfo.sq[i].x >= 0 && highlightInfo.sq[i].y >= 0) {
\r
3675 SquareToPos(highlightInfo.sq[i].y, highlightInfo.sq[i].x, &x, &y);
\r
3676 clips[num_clips++] =
\r
3677 CreateRectRgn(x - lineGap, y - lineGap,
\r
3678 x + squareSize + lineGap, y + squareSize + lineGap);
\r
3682 for (i=0; i<2; i++) {
\r
3683 if (lastDrawnPremove.sq[i].x != premoveHighlightInfo.sq[i].x ||
\r
3684 lastDrawnPremove.sq[i].y != premoveHighlightInfo.sq[i].y) {
\r
3685 if (lastDrawnPremove.sq[i].x >= 0 &&
\r
3686 lastDrawnPremove.sq[i].y >= 0) {
\r
3687 SquareToPos(lastDrawnPremove.sq[i].y,
\r
3688 lastDrawnPremove.sq[i].x, &x, &y);
\r
3689 clips[num_clips++] =
\r
3690 CreateRectRgn(x - lineGap, y - lineGap,
\r
3691 x + squareSize + lineGap, y + squareSize + lineGap);
\r
3693 if (premoveHighlightInfo.sq[i].x >= 0 &&
\r
3694 premoveHighlightInfo.sq[i].y >= 0) {
\r
3695 SquareToPos(premoveHighlightInfo.sq[i].y,
\r
3696 premoveHighlightInfo.sq[i].x, &x, &y);
\r
3697 clips[num_clips++] =
\r
3698 CreateRectRgn(x - lineGap, y - lineGap,
\r
3699 x + squareSize + lineGap, y + squareSize + lineGap);
\r
3703 } else { // nr == 1
\r
3704 partnerHighlightInfo.sq[0].y = board[EP_STATUS-4];
\r
3705 partnerHighlightInfo.sq[0].x = board[EP_STATUS-3];
\r
3706 partnerHighlightInfo.sq[1].y = board[EP_STATUS-2];
\r
3707 partnerHighlightInfo.sq[1].x = board[EP_STATUS-1];
\r
3708 for (i=0; i<2; i++) {
\r
3709 if (partnerHighlightInfo.sq[i].x >= 0 &&
\r
3710 partnerHighlightInfo.sq[i].y >= 0) {
\r
3711 SquareToPos(partnerHighlightInfo.sq[i].y,
\r
3712 partnerHighlightInfo.sq[i].x, &x, &y);
\r
3713 clips[num_clips++] =
\r
3714 CreateRectRgn(x - lineGap, y - lineGap,
\r
3715 x + squareSize + lineGap, y + squareSize + lineGap);
\r
3717 if (oldPartnerHighlight.sq[i].x >= 0 &&
\r
3718 oldPartnerHighlight.sq[i].y >= 0) {
\r
3719 SquareToPos(oldPartnerHighlight.sq[i].y,
\r
3720 oldPartnerHighlight.sq[i].x, &x, &y);
\r
3721 clips[num_clips++] =
\r
3722 CreateRectRgn(x - lineGap, y - lineGap,
\r
3723 x + squareSize + lineGap, y + squareSize + lineGap);
\r
3728 fullrepaint = TRUE;
\r
3731 /* Create a buffer bitmap - this is the actual bitmap
\r
3732 * being written to. When all the work is done, we can
\r
3733 * copy it to the real DC (the screen). This avoids
\r
3734 * the problems with flickering.
\r
3736 GetClientRect(hwndMain, &Rect);
\r
3737 bufferBitmap = CreateCompatibleBitmap(hdc, Rect.right-Rect.left+1,
\r
3738 Rect.bottom-Rect.top+1);
\r
3739 oldBitmap = SelectObject(hdcmem, bufferBitmap);
\r
3740 if (!appData.monoMode) {
\r
3741 SelectPalette(hdcmem, hPal, FALSE);
\r
3744 /* Create clips for dragging */
\r
3745 if (!fullrepaint) {
\r
3746 if (dragInfo.from.x >= 0) {
\r
3747 SquareToPos(dragInfo.from.y, dragInfo.from.x, &x, &y);
\r
3748 clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);
\r
3750 if (dragInfo.start.x >= 0) {
\r
3751 SquareToPos(dragInfo.start.y, dragInfo.start.x, &x, &y);
\r
3752 clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);
\r
3754 if (dragInfo.pos.x >= 0) {
\r
3755 x = dragInfo.pos.x - squareSize / 2;
\r
3756 y = dragInfo.pos.y - squareSize / 2;
\r
3757 clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);
\r
3759 if (dragInfo.lastpos.x >= 0) {
\r
3760 x = dragInfo.lastpos.x - squareSize / 2;
\r
3761 y = dragInfo.lastpos.y - squareSize / 2;
\r
3762 clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);
\r
3766 /* Are we animating a move?
\r
3768 * - remove the piece from the board (temporarely)
\r
3769 * - calculate the clipping region
\r
3771 if (!fullrepaint) {
\r
3772 if (animInfo.piece != EmptySquare) {
\r
3773 board[animInfo.from.y][animInfo.from.x] = EmptySquare;
\r
3774 x = boardRect.left + animInfo.lastpos.x;
\r
3775 y = boardRect.top + animInfo.lastpos.y;
\r
3776 x2 = boardRect.left + animInfo.pos.x;
\r
3777 y2 = boardRect.top + animInfo.pos.y;
\r
3778 clips[num_clips++] = CreateRectRgn(MIN(x,x2), MIN(y,y2), MAX(x,x2)+squareSize, MAX(y,y2)+squareSize);
\r
3779 /* Slight kludge. The real problem is that after AnimateMove is
\r
3780 done, the position on the screen does not match lastDrawn.
\r
3781 This currently causes trouble only on e.p. captures in
\r
3782 atomic, where the piece moves to an empty square and then
\r
3783 explodes. The old and new positions both had an empty square
\r
3784 at the destination, but animation has drawn a piece there and
\r
3785 we have to remember to erase it. [HGM] moved until after setting lastDrawn */
\r
3786 lastDrawn[0][animInfo.to.y][animInfo.to.x] = animInfo.piece;
\r
3790 /* No clips? Make sure we have fullrepaint set to TRUE */
\r
3791 if (num_clips == 0)
\r
3792 fullrepaint = TRUE;
\r
3794 /* Set clipping on the memory DC */
\r
3795 if (!fullrepaint) {
\r
3796 SelectClipRgn(hdcmem, clips[0]);
\r
3797 for (x = 1; x < num_clips; x++) {
\r
3798 if (ExtSelectClipRgn(hdcmem, clips[x], RGN_OR) == ERROR)
\r
3799 abort(); // this should never ever happen!
\r
3803 /* Do all the drawing to the memory DC */
\r
3804 if(explodeInfo.radius) { // [HGM] atomic
\r
3806 int x, y, r=(explodeInfo.radius * squareSize)/100;
\r
3807 ChessSquare piece = board[explodeInfo.fromY][explodeInfo.fromX];
\r
3808 board[explodeInfo.fromY][explodeInfo.fromX] = EmptySquare; // suppress display of capturer
\r
3809 SquareToPos(explodeInfo.toY, explodeInfo.toX, &x, &y);
\r
3810 x += squareSize/2;
\r
3811 y += squareSize/2;
\r
3812 if(!fullrepaint) {
\r
3813 clips[num_clips] = CreateRectRgn(x-r, y-r, x+r, y+r);
\r
3814 ExtSelectClipRgn(hdcmem, clips[num_clips++], RGN_OR);
\r
3816 DrawGridOnDC(hdcmem);
\r
3817 DrawHighlightsOnDC(hdcmem, &highlightInfo, HIGHLIGHT_PEN);
\r
3818 DrawHighlightsOnDC(hdcmem, &premoveHighlightInfo, PREMOVE_PEN);
\r
3819 DrawBoardOnDC(hdcmem, board, tmphdc);
\r
3820 board[explodeInfo.fromY][explodeInfo.fromX] = piece;
\r
3821 oldBrush = SelectObject(hdcmem, explodeBrush);
\r
3822 Ellipse(hdcmem, x-r, y-r, x+r, y+r);
\r
3823 SelectObject(hdcmem, oldBrush);
\r
3825 DrawGridOnDC(hdcmem);
\r
3826 if(nr == 0) { // [HGM] dual: decide which highlights to draw
\r
3827 DrawHighlightsOnDC(hdcmem, &highlightInfo, HIGHLIGHT_PEN);
\r
3828 DrawHighlightsOnDC(hdcmem, &premoveHighlightInfo, PREMOVE_PEN);
\r
3830 DrawHighlightsOnDC(hdcmem, &partnerHighlightInfo, HIGHLIGHT_PEN);
\r
3831 oldPartnerHighlight = partnerHighlightInfo;
\r
3833 DrawBoardOnDC(hdcmem, board, tmphdc);
\r
3835 if(nr == 0) // [HGM] dual: markers only on left board
\r
3836 for (row = 0; row < BOARD_HEIGHT; row++) {
\r
3837 for (column = 0; column < BOARD_WIDTH; column++) {
\r
3838 if (marker[row][column]) { // marker changes only occur with full repaint!
\r
3839 HBRUSH oldBrush = SelectObject(hdcmem,
\r
3840 marker[row][column] == 2 ? markerBrush : explodeBrush);
\r
3841 SquareToPos(row, column, &x, &y);
\r
3842 Ellipse(hdcmem, x + squareSize/4, y + squareSize/4,
\r
3843 x + 3*squareSize/4, y + 3*squareSize/4);
\r
3844 SelectObject(hdcmem, oldBrush);
\r
3849 if( appData.highlightMoveWithArrow ) {
\r
3850 DrawArrowHighlight(hdcmem);
\r
3853 DrawCoordsOnDC(hdcmem);
\r
3855 CopyBoard(lastDrawn[nr], board); /* [HGM] Moved to here from end of routine, */
\r
3856 /* to make sure lastDrawn contains what is actually drawn */
\r
3858 /* Put the dragged piece back into place and draw it (out of place!) */
\r
3859 if (dragged_piece != EmptySquare) {
\r
3860 /* [HGM] or restack */
\r
3861 if(dragInfo.from.x == BOARD_LEFT-2 )
\r
3862 board[dragInfo.from.y][dragInfo.from.x+1]++;
\r
3864 if(dragInfo.from.x == BOARD_RGHT+1 )
\r
3865 board[dragInfo.from.y][dragInfo.from.x-1]++;
\r
3866 board[dragInfo.from.y][dragInfo.from.x] = dragged_piece;
\r
3867 x = dragInfo.pos.x - squareSize / 2;
\r
3868 y = dragInfo.pos.y - squareSize / 2;
\r
3869 DrawPieceOnDC(hdcmem, dragInfo.piece,
\r
3870 ((int) dragInfo.piece < (int) BlackPawn),
\r
3871 (dragInfo.from.y + dragInfo.from.x) % 2, x, y, tmphdc);
\r
3874 /* Put the animated piece back into place and draw it */
\r
3875 if (animInfo.piece != EmptySquare) {
\r
3876 board[animInfo.from.y][animInfo.from.x] = animInfo.piece;
\r
3877 x = boardRect.left + animInfo.pos.x;
\r
3878 y = boardRect.top + animInfo.pos.y;
\r
3879 DrawPieceOnDC(hdcmem, animInfo.piece,
\r
3880 ((int) animInfo.piece < (int) BlackPawn),
\r
3881 (animInfo.from.y + animInfo.from.x) % 2, x, y, tmphdc);
\r
3884 /* Release the bufferBitmap by selecting in the old bitmap
\r
3885 * and delete the memory DC
\r
3887 SelectObject(hdcmem, oldBitmap);
\r
3890 /* Set clipping on the target DC */
\r
3891 if (!fullrepaint) {
\r
3892 if(nr == 1) for (x = 0; x < num_clips; x++) { // [HGM] dual: translate clips
\r
3894 GetRgnBox(clips[x], &rect);
\r
3895 DeleteObject(clips[x]);
\r
3896 clips[x] = CreateRectRgn(rect.left + wpMain.width/2, rect.top,
\r
3897 rect.right + wpMain.width/2, rect.bottom);
\r
3899 SelectClipRgn(hdc, clips[0]);
\r
3900 for (x = 1; x < num_clips; x++) {
\r
3901 if (ExtSelectClipRgn(hdc, clips[x], RGN_OR) == ERROR)
\r
3902 abort(); // this should never ever happen!
\r
3906 /* Copy the new bitmap onto the screen in one go.
\r
3907 * This way we avoid any flickering
\r
3909 oldBitmap = SelectObject(tmphdc, bufferBitmap);
\r
3910 BitBlt(hdc, boardRect.left + twoBoards*partnerUp*wpMain.width/2, boardRect.top, // [HGM] dual
\r
3911 boardRect.right - boardRect.left,
\r
3912 boardRect.bottom - boardRect.top,
\r
3913 tmphdc, boardRect.left, boardRect.top, SRCCOPY);
\r
3914 if(saveDiagFlag) {
\r
3915 BITMAP b; int i, j=0, m, w, wb, fac=0; char *pData;
\r
3916 BITMAPINFOHEADER bih; int color[16], nrColors=0;
\r
3918 GetObject(bufferBitmap, sizeof(b), &b);
\r
3919 if(pData = malloc(b.bmWidthBytes*b.bmHeight + 10000)) {
\r
3920 bih.biSize = sizeof(BITMAPINFOHEADER);
\r
3921 bih.biWidth = b.bmWidth;
\r
3922 bih.biHeight = b.bmHeight;
\r
3924 bih.biBitCount = b.bmBitsPixel;
\r
3925 bih.biCompression = 0;
\r
3926 bih.biSizeImage = b.bmWidthBytes*b.bmHeight;
\r
3927 bih.biXPelsPerMeter = 0;
\r
3928 bih.biYPelsPerMeter = 0;
\r
3929 bih.biClrUsed = 0;
\r
3930 bih.biClrImportant = 0;
\r
3931 // fprintf(diagFile, "t=%d\nw=%d\nh=%d\nB=%d\nP=%d\nX=%d\n",
\r
3932 // b.bmType, b.bmWidth, b.bmHeight, b.bmWidthBytes, b.bmPlanes, b.bmBitsPixel);
\r
3933 GetDIBits(tmphdc,bufferBitmap,0,b.bmHeight,pData,(BITMAPINFO*)&bih,DIB_RGB_COLORS);
\r
3934 // fprintf(diagFile, "%8x\n", (int) pData);
\r
3936 wb = b.bmWidthBytes;
\r
3938 for(i=0; i<wb*(b.bmHeight - boardRect.top + OUTER_MARGIN)>>2; i++) {
\r
3939 int k = ((int*) pData)[i];
\r
3940 for(j=0; j<nrColors; j++) if(color[j] == k) break;
\r
3941 if(j >= 16) break;
\r
3943 if(j >= nrColors) nrColors = j+1;
\r
3945 if(j<16) { // 16 colors is enough. Compress to 4 bits per pixel
\r
3947 for(i=0; i<b.bmHeight - boardRect.top + OUTER_MARGIN; i++) {
\r
3948 for(w=0; w<(wb>>2); w+=2) {
\r
3949 int k = ((int*) pData)[(wb*i>>2) + w];
\r
3950 for(j=0; j<nrColors; j++) if(color[j] == k) break;
\r
3951 k = ((int*) pData)[(wb*i>>2) + w + 1];
\r
3952 for(m=0; m<nrColors; m++) if(color[m] == k) break;
\r
3953 pData[p++] = m | j<<4;
\r
3955 while(p&3) pData[p++] = 0;
\r
3958 wb = ((wb+31)>>5)<<2;
\r
3960 // write BITMAPFILEHEADER
\r
3961 fprintf(diagFile, "BM");
\r
3962 fputDW(diagFile, wb*(b.bmHeight - boardRect.top + OUTER_MARGIN)+0x36 + (fac?64:0));
\r
3963 fputDW(diagFile, 0);
\r
3964 fputDW(diagFile, 0x36 + (fac?64:0));
\r
3965 // write BITMAPINFOHEADER
\r
3966 fputDW(diagFile, 40);
\r
3967 fputDW(diagFile, b.bmWidth);
\r
3968 fputDW(diagFile, b.bmHeight - boardRect.top + OUTER_MARGIN);
\r
3969 if(fac) fputDW(diagFile, 0x040001); // planes and bits/pixel
\r
3970 else fputDW(diagFile, 0x200001); // planes and bits/pixel
\r
3971 fputDW(diagFile, 0);
\r
3972 fputDW(diagFile, 0);
\r
3973 fputDW(diagFile, 0);
\r
3974 fputDW(diagFile, 0);
\r
3975 fputDW(diagFile, 0);
\r
3976 fputDW(diagFile, 0);
\r
3977 // write color table
\r
3979 for(i=0; i<16; i++) fputDW(diagFile, color[i]);
\r
3980 // write bitmap data
\r
3981 for(i=0; i<wb*(b.bmHeight - boardRect.top + OUTER_MARGIN); i++)
\r
3982 fputc(pData[i], diagFile);
\r
3987 SelectObject(tmphdc, oldBitmap);
\r
3989 /* Massive cleanup */
\r
3990 for (x = 0; x < num_clips; x++)
\r
3991 DeleteObject(clips[x]);
\r
3994 DeleteObject(bufferBitmap);
\r
3997 ReleaseDC(hwndMain, hdc);
\r
3999 if (lastDrawnFlipView != flipView && nr == 0) {
\r
4001 CheckMenuItem(GetMenu(hwndMain),IDM_FlipView, MF_BYCOMMAND|MF_CHECKED);
\r
4003 CheckMenuItem(GetMenu(hwndMain),IDM_FlipView, MF_BYCOMMAND|MF_UNCHECKED);
\r
4006 /* CopyBoard(lastDrawn, board);*/
\r
4007 lastDrawnHighlight = highlightInfo;
\r
4008 lastDrawnPremove = premoveHighlightInfo;
\r
4009 lastDrawnFlipView = flipView;
\r
4010 lastDrawnValid[nr] = 1;
\r
4013 /* [HGM] diag: Save the current board display to the given open file and close the file */
\r
4018 saveDiagFlag = 1; diagFile = f;
\r
4019 HDCDrawPosition(NULL, TRUE, NULL);
\r
4027 /*---------------------------------------------------------------------------*\
\r
4028 | CLIENT PAINT PROCEDURE
\r
4029 | This is the main event-handler for the WM_PAINT message.
\r
4031 \*---------------------------------------------------------------------------*/
\r
4033 PaintProc(HWND hwnd)
\r
4039 if((hdc = BeginPaint(hwnd, &ps))) {
\r
4040 if (IsIconic(hwnd)) {
\r
4041 DrawIcon(hdc, 2, 2, iconCurrent);
\r
4043 if (!appData.monoMode) {
\r
4044 SelectPalette(hdc, hPal, FALSE);
\r
4045 RealizePalette(hdc);
\r
4047 HDCDrawPosition(hdc, 1, NULL);
\r
4048 if(twoBoards) { // [HGM] dual: also redraw other board in other orientation
\r
4049 flipView = !flipView; partnerUp = !partnerUp;
\r
4050 HDCDrawPosition(hdc, 1, NULL);
\r
4051 flipView = !flipView; partnerUp = !partnerUp;
\r
4054 SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);
\r
4055 ExtTextOut(hdc, messageRect.left, messageRect.top,
\r
4056 ETO_CLIPPED|ETO_OPAQUE,
\r
4057 &messageRect, messageText, strlen(messageText), NULL);
\r
4058 SelectObject(hdc, oldFont);
\r
4059 DisplayBothClocks();
\r
4062 EndPaint(hwnd,&ps);
\r
4070 * If the user selects on a border boundary, return -1; if off the board,
\r
4071 * return -2. Otherwise map the event coordinate to the square.
\r
4072 * The offset boardRect.left or boardRect.top must already have been
\r
4073 * subtracted from x.
\r
4075 int EventToSquare(x, limit)
\r
4083 if ((x % (squareSize + lineGap)) >= squareSize)
\r
4085 x /= (squareSize + lineGap);
\r
4097 DropEnable dropEnables[] = {
\r
4098 { 'P', DP_Pawn, N_("Pawn") },
\r
4099 { 'N', DP_Knight, N_("Knight") },
\r
4100 { 'B', DP_Bishop, N_("Bishop") },
\r
4101 { 'R', DP_Rook, N_("Rook") },
\r
4102 { 'Q', DP_Queen, N_("Queen") },
\r
4106 SetupDropMenu(HMENU hmenu)
\r
4108 int i, count, enable;
\r
4110 extern char white_holding[], black_holding[];
\r
4111 char item[MSG_SIZ];
\r
4113 for (i=0; i<sizeof(dropEnables)/sizeof(DropEnable); i++) {
\r
4114 p = strchr(gameMode == IcsPlayingWhite ? white_holding : black_holding,
\r
4115 dropEnables[i].piece);
\r
4117 while (p && *p++ == dropEnables[i].piece) count++;
\r
4118 snprintf(item, MSG_SIZ, "%s %d", T_(dropEnables[i].name), count);
\r
4119 enable = count > 0 || !appData.testLegality
\r
4120 /*!!temp:*/ || (gameInfo.variant == VariantCrazyhouse
\r
4121 && !appData.icsActive);
\r
4122 ModifyMenu(hmenu, dropEnables[i].command,
\r
4123 MF_BYCOMMAND | (enable ? MF_ENABLED : MF_GRAYED) | MF_STRING,
\r
4124 dropEnables[i].command, item);
\r
4128 void DragPieceBegin(int x, int y, Boolean instantly)
\r
4130 dragInfo.lastpos.x = boardRect.left + x;
\r
4131 dragInfo.lastpos.y = boardRect.top + y;
\r
4132 if(instantly) dragInfo.pos = dragInfo.lastpos;
\r
4133 dragInfo.from.x = fromX;
\r
4134 dragInfo.from.y = fromY;
\r
4135 dragInfo.piece = boards[currentMove][fromY][fromX];
\r
4136 dragInfo.start = dragInfo.from;
\r
4137 SetCapture(hwndMain);
\r
4140 void DragPieceEnd(int x, int y)
\r
4143 dragInfo.start.x = dragInfo.start.y = -1;
\r
4144 dragInfo.from = dragInfo.start;
\r
4145 dragInfo.pos = dragInfo.lastpos = dragInfo.start;
\r
4148 void ChangeDragPiece(ChessSquare piece)
\r
4150 dragInfo.piece = piece;
\r
4153 /* Event handler for mouse messages */
\r
4155 MouseEvent(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
\r
4159 static int recursive = 0;
\r
4161 BOOLEAN forceFullRepaint = IsFullRepaintPreferrable(); /* [AS] */
\r
4164 if (message == WM_MBUTTONUP) {
\r
4165 /* Hideous kludge to fool TrackPopupMenu into paying attention
\r
4166 to the middle button: we simulate pressing the left button too!
\r
4168 PostMessage(hwnd, WM_LBUTTONDOWN, wParam, lParam);
\r
4169 PostMessage(hwnd, WM_LBUTTONUP, wParam, lParam);
\r
4175 pt.x = LOWORD(lParam);
\r
4176 pt.y = HIWORD(lParam);
\r
4177 x = EventToSquare(pt.x - boardRect.left, BOARD_WIDTH);
\r
4178 y = EventToSquare(pt.y - boardRect.top, BOARD_HEIGHT);
\r
4179 if (!flipView && y >= 0) {
\r
4180 y = BOARD_HEIGHT - 1 - y;
\r
4182 if (flipView && x >= 0) {
\r
4183 x = BOARD_WIDTH - 1 - x;
\r
4186 shiftKey = GetKeyState(VK_SHIFT) < 0; // [HGM] remember last shift status
\r
4188 switch (message) {
\r
4189 case WM_LBUTTONDOWN:
\r
4190 if (PtInRect((LPRECT) &whiteRect, pt)) {
\r
4191 ClockClick(flipClock); break;
\r
4192 } else if (PtInRect((LPRECT) &blackRect, pt)) {
\r
4193 ClockClick(!flipClock); break;
\r
4195 dragInfo.start.x = dragInfo.start.y = -1;
\r
4196 dragInfo.from = dragInfo.start;
\r
4197 if(fromX == -1 && frozen) { // not sure where this is for
\r
4198 fromX = fromY = -1;
\r
4199 DrawPosition(forceFullRepaint || FALSE, NULL); /* [AS] */
\r
4202 LeftClick(Press, pt.x - boardRect.left, pt.y - boardRect.top);
\r
4203 DrawPosition(TRUE, NULL);
\r
4206 case WM_LBUTTONUP:
\r
4207 LeftClick(Release, pt.x - boardRect.left, pt.y - boardRect.top);
\r
4208 DrawPosition(TRUE, NULL);
\r
4211 case WM_MOUSEMOVE:
\r
4212 if(SeekGraphClick(Press, pt.x - boardRect.left, pt.y - boardRect.top, 1)) break;
\r
4213 if(PromoScroll(pt.x - boardRect.left, pt.y - boardRect.top)) break;
\r
4214 MovePV(pt.x - boardRect.left, pt.y - boardRect.top, boardRect.bottom - boardRect.top);
\r
4215 if ((appData.animateDragging || appData.highlightDragging)
\r
4216 && (wParam & MK_LBUTTON)
\r
4217 && dragInfo.from.x >= 0)
\r
4219 BOOL full_repaint = FALSE;
\r
4221 if (appData.animateDragging) {
\r
4222 dragInfo.pos = pt;
\r
4224 if (appData.highlightDragging) {
\r
4225 SetHighlights(fromX, fromY, x, y);
\r
4226 if( IsDrawArrowEnabled() && (x < 0 || x >= BOARD_WIDTH || y < 0 || y >= BOARD_HEIGHT) ) {
\r
4227 full_repaint = TRUE;
\r
4231 DrawPosition( full_repaint, NULL);
\r
4233 dragInfo.lastpos = dragInfo.pos;
\r
4237 case WM_MOUSEWHEEL: // [DM]
\r
4238 { static int lastDir = 0; // [HGM] build in some hysteresis to avoid spurious events
\r
4239 /* Mouse Wheel is being rolled forward
\r
4240 * Play moves forward
\r
4242 if((short)HIWORD(wParam) < 0 && currentMove < forwardMostMove)
\r
4243 { if(lastDir == 1) ForwardEvent(); else lastDir = 1; } // [HGM] suppress first event in direction
\r
4244 /* Mouse Wheel is being rolled backward
\r
4245 * Play moves backward
\r
4247 if((short)HIWORD(wParam) > 0 && currentMove > backwardMostMove)
\r
4248 { if(lastDir == -1) BackwardEvent(); else lastDir = -1; }
\r
4252 case WM_MBUTTONUP:
\r
4253 case WM_RBUTTONUP:
\r
4255 RightClick(Release, pt.x - boardRect.left, pt.y - boardRect.top, &fromX, &fromY);
\r
4258 case WM_MBUTTONDOWN:
\r
4259 case WM_RBUTTONDOWN:
\r
4262 fromX = fromY = -1;
\r
4263 dragInfo.pos.x = dragInfo.pos.y = -1;
\r
4264 dragInfo.start.x = dragInfo.start.y = -1;
\r
4265 dragInfo.from = dragInfo.start;
\r
4266 dragInfo.lastpos = dragInfo.pos;
\r
4267 if (appData.highlightDragging) {
\r
4268 ClearHighlights();
\r
4271 /* [HGM] right mouse button in clock area edit-game mode ups clock */
\r
4272 if (PtInRect((LPRECT) &whiteRect, pt)) {
\r
4273 if (GetKeyState(VK_SHIFT) < 0) AdjustClock(flipClock, 1);
\r
4274 } else if (PtInRect((LPRECT) &blackRect, pt)) {
\r
4275 if (GetKeyState(VK_SHIFT) < 0) AdjustClock(!flipClock, 1);
\r
4279 DrawPosition(TRUE, NULL);
\r
4281 menuNr = RightClick(Press, pt.x - boardRect.left, pt.y - boardRect.top, &fromX, &fromY);
\r
4284 if (message == WM_MBUTTONDOWN) {
\r
4285 buttonCount = 3; /* even if system didn't think so */
\r
4286 if (wParam & MK_SHIFT)
\r
4287 MenuPopup(hwnd, pt, LoadMenu(hInst, "BlackPieceMenu"), -1);
\r
4289 MenuPopup(hwnd, pt, LoadMenu(hInst, "WhitePieceMenu"), -1);
\r
4290 } else { /* message == WM_RBUTTONDOWN */
\r
4291 /* Just have one menu, on the right button. Windows users don't
\r
4292 think to try the middle one, and sometimes other software steals
\r
4293 it, or it doesn't really exist. */
\r
4294 if(gameInfo.variant != VariantShogi)
\r
4295 MenuPopup(hwnd, pt, LoadMenu(hInst, "PieceMenu"), -1);
\r
4297 MenuPopup(hwnd, pt, LoadMenu(hInst, "ShogiPieceMenu"), -1);
\r
4301 SetCapture(hwndMain);
\r
4304 hmenu = LoadMenu(hInst, "DropPieceMenu");
\r
4305 SetupDropMenu(hmenu);
\r
4306 MenuPopup(hwnd, pt, hmenu, -1);
\r
4316 /* Preprocess messages for buttons in main window */
\r
4318 ButtonProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
\r
4320 int id = GetWindowLongPtr(hwnd, GWLP_ID);
\r
4323 for (i=0; i<N_BUTTONS; i++) {
\r
4324 if (buttonDesc[i].id == id) break;
\r
4326 if (i == N_BUTTONS) return 0;
\r
4327 switch (message) {
\r
4332 dir = (wParam == VK_LEFT) ? -1 : 1;
\r
4333 SetFocus(buttonDesc[(i + dir + N_BUTTONS) % N_BUTTONS].hwnd);
\r
4340 SendMessage(hwndMain, WM_COMMAND, MAKEWPARAM(buttonDesc[i].id, 0), 0);
\r
4343 if (appData.icsActive && (isalpha((char)wParam) || wParam == '0')) {
\r
4344 // [HGM] movenum: only letters or leading zero should go to ICS input
\r
4345 HWND h = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
4346 if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);
\r
4348 SendMessage(h, WM_CHAR, wParam, lParam);
\r
4350 } else if (isalpha((char)wParam) || isdigit((char)wParam)){
\r
4351 TypeInEvent((char)wParam);
\r
4357 return CallWindowProc(buttonDesc[i].wndproc, hwnd, message, wParam, lParam);
\r
4360 /* Process messages for Promotion dialog box */
\r
4362 Promotion(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
4366 switch (message) {
\r
4367 case WM_INITDIALOG: /* message: initialize dialog box */
\r
4368 /* Center the dialog over the application window */
\r
4369 CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));
\r
4370 Translate(hDlg, DLG_PromotionKing);
\r
4371 ShowWindow(GetDlgItem(hDlg, PB_King),
\r
4372 (!appData.testLegality || gameInfo.variant == VariantSuicide ||
\r
4373 gameInfo.variant == VariantSpartan && !WhiteOnMove(currentMove) ||
\r
4374 gameInfo.variant == VariantGiveaway || gameInfo.variant == VariantSuper ) ?
\r
4375 SW_SHOW : SW_HIDE);
\r
4376 /* [HGM] Only allow C & A promotions if these pieces are defined */
\r
4377 ShowWindow(GetDlgItem(hDlg, PB_Archbishop),
\r
4378 ((PieceToChar(WhiteAngel) >= 'A' && WhiteOnMove(currentMove) &&
\r
4379 PieceToChar(WhiteAngel) != '~') ||
\r
4380 (PieceToChar(BlackAngel) >= 'A' && !WhiteOnMove(currentMove) &&
\r
4381 PieceToChar(BlackAngel) != '~') ) ?
\r
4382 SW_SHOW : SW_HIDE);
\r
4383 ShowWindow(GetDlgItem(hDlg, PB_Chancellor),
\r
4384 ((PieceToChar(WhiteMarshall) >= 'A' && WhiteOnMove(currentMove) &&
\r
4385 PieceToChar(WhiteMarshall) != '~') ||
\r
4386 (PieceToChar(BlackMarshall) >= 'A' && !WhiteOnMove(currentMove) &&
\r
4387 PieceToChar(BlackMarshall) != '~') ) ?
\r
4388 SW_SHOW : SW_HIDE);
\r
4389 /* [HGM] Hide B & R button in Shogi, use Q as promote, N as defer */
\r
4390 ShowWindow(GetDlgItem(hDlg, PB_Rook),
\r
4391 gameInfo.variant != VariantShogi ?
\r
4392 SW_SHOW : SW_HIDE);
\r
4393 ShowWindow(GetDlgItem(hDlg, PB_Bishop),
\r
4394 gameInfo.variant != VariantShogi ?
\r
4395 SW_SHOW : SW_HIDE);
\r
4396 if(gameInfo.variant == VariantShogi) {
\r
4397 SetDlgItemText(hDlg, PB_Queen, "YES");
\r
4398 SetDlgItemText(hDlg, PB_Knight, "NO");
\r
4399 SetWindowText(hDlg, "Promote?");
\r
4401 ShowWindow(GetDlgItem(hDlg, IDC_Centaur),
\r
4402 gameInfo.variant == VariantSuper ?
\r
4403 SW_SHOW : SW_HIDE);
\r
4406 case WM_COMMAND: /* message: received a command */
\r
4407 switch (LOWORD(wParam)) {
\r
4409 EndDialog(hDlg, TRUE); /* Exit the dialog */
\r
4410 ClearHighlights();
\r
4411 DrawPosition(FALSE, NULL);
\r
4414 promoChar = gameInfo.variant == VariantSuper ? PieceToChar(BlackSilver) : PieceToChar(BlackKing);
\r
4417 promoChar = gameInfo.variant == VariantShogi ? '+' : ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteQueen : BlackQueen));
\r
4420 promoChar = ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteRook : BlackRook));
\r
4421 if(gameInfo.variant == VariantSpartan && !WhiteOnMove(currentMove)) promoChar = PieceToChar(BlackDragon);
\r
4424 promoChar = ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteBishop : BlackBishop));
\r
4425 if(gameInfo.variant == VariantSpartan && !WhiteOnMove(currentMove)) promoChar = PieceToChar(BlackAlfil);
\r
4427 case PB_Chancellor:
\r
4428 promoChar = ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteMarshall : BlackMarshall));
\r
4430 case PB_Archbishop:
\r
4431 promoChar = ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteAngel : BlackAngel));
\r
4434 promoChar = gameInfo.variant == VariantShogi ? '=' : PieceToChar(WhiteOnMove(currentMove) ? WhiteKnight : BlackKnight);
\r
4439 if(promoChar == '.') return FALSE; // invalid piece chosen
\r
4440 EndDialog(hDlg, TRUE); /* Exit the dialog */
\r
4441 UserMoveEvent(fromX, fromY, toX, toY, promoChar);
\r
4442 fromX = fromY = -1;
\r
4443 if (!appData.highlightLastMove) {
\r
4444 ClearHighlights();
\r
4445 DrawPosition(FALSE, NULL);
\r
4452 /* Pop up promotion dialog */
\r
4454 PromotionPopup(HWND hwnd)
\r
4458 lpProc = MakeProcInstance((FARPROC)Promotion, hInst);
\r
4459 DialogBox(hInst, MAKEINTRESOURCE(DLG_PromotionKing),
\r
4460 hwnd, (DLGPROC)lpProc);
\r
4461 FreeProcInstance(lpProc);
\r
4467 DrawPosition(TRUE, NULL);
\r
4468 PromotionPopup(hwndMain);
\r
4472 LoadGameDialog(HWND hwnd, char* title)
\r
4476 char fileTitle[MSG_SIZ];
\r
4477 f = OpenFileDialog(hwnd, "rb", "",
\r
4478 appData.oldSaveStyle ? "gam" : "pgn",
\r
4480 title, &number, fileTitle, NULL);
\r
4482 cmailMsgLoaded = FALSE;
\r
4483 if (number == 0) {
\r
4484 int error = GameListBuild(f);
\r
4486 DisplayError(_("Cannot build game list"), error);
\r
4487 } else if (!ListEmpty(&gameList) &&
\r
4488 ((ListGame *) gameList.tailPred)->number > 1) {
\r
4489 GameListPopUp(f, fileTitle);
\r
4492 GameListDestroy();
\r
4495 LoadGame(f, number, fileTitle, FALSE);
\r
4499 int get_term_width()
\r
4504 HFONT hfont, hold_font;
\r
4509 hText = GetDlgItem(hwndConsole, OPT_ConsoleText);
\r
4513 // get the text metrics
\r
4514 hdc = GetDC(hText);
\r
4515 lf = font[boardSize][CONSOLE_FONT]->lf;
\r
4516 if (consoleCF.dwEffects & CFE_BOLD)
\r
4517 lf.lfWeight = FW_BOLD;
\r
4518 if (consoleCF.dwEffects & CFE_ITALIC)
\r
4519 lf.lfItalic = TRUE;
\r
4520 if (consoleCF.dwEffects & CFE_STRIKEOUT)
\r
4521 lf.lfStrikeOut = TRUE;
\r
4522 if (consoleCF.dwEffects & CFE_UNDERLINE)
\r
4523 lf.lfUnderline = TRUE;
\r
4524 hfont = CreateFontIndirect(&lf);
\r
4525 hold_font = SelectObject(hdc, hfont);
\r
4526 GetTextMetrics(hdc, &tm);
\r
4527 SelectObject(hdc, hold_font);
\r
4528 DeleteObject(hfont);
\r
4529 ReleaseDC(hText, hdc);
\r
4531 // get the rectangle
\r
4532 SendMessage(hText, EM_GETRECT, 0, (LPARAM)&rc);
\r
4534 return (rc.right-rc.left) / tm.tmAveCharWidth;
\r
4537 void UpdateICSWidth(HWND hText)
\r
4539 LONG old_width, new_width;
\r
4541 new_width = get_term_width(hText, FALSE);
\r
4542 old_width = GetWindowLongPtr(hText, GWLP_USERDATA);
\r
4543 if (new_width != old_width)
\r
4545 ics_update_width(new_width);
\r
4546 SetWindowLongPtr(hText, GWLP_USERDATA, new_width);
\r
4551 ChangedConsoleFont()
\r
4554 CHARRANGE tmpsel, sel;
\r
4555 MyFont *f = font[boardSize][CONSOLE_FONT];
\r
4556 HWND hText = GetDlgItem(hwndConsole, OPT_ConsoleText);
\r
4557 HWND hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
4560 cfmt.cbSize = sizeof(CHARFORMAT);
\r
4561 cfmt.dwMask = CFM_FACE|CFM_SIZE|CFM_CHARSET;
\r
4562 safeStrCpy(cfmt.szFaceName, font[boardSize][CONSOLE_FONT]->mfp.faceName,
\r
4563 sizeof(cfmt.szFaceName)/sizeof(cfmt.szFaceName[0]) );
\r
4564 /* yHeight is expressed in twips. A twip is 1/20 of a font's point
\r
4565 * size. This was undocumented in the version of MSVC++ that I had
\r
4566 * when I wrote the code, but is apparently documented now.
\r
4568 cfmt.yHeight = (int)(f->mfp.pointSize * 20.0 + 0.5);
\r
4569 cfmt.bCharSet = f->lf.lfCharSet;
\r
4570 cfmt.bPitchAndFamily = f->lf.lfPitchAndFamily;
\r
4571 SendMessage(hText, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &cfmt);
\r
4572 SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &cfmt);
\r
4573 /* Why are the following seemingly needed too? */
\r
4574 SendMessage(hText, EM_SETCHARFORMAT, SCF_DEFAULT, (LPARAM) &cfmt);
\r
4575 SendMessage(hInput, EM_SETCHARFORMAT, SCF_DEFAULT, (LPARAM) &cfmt);
\r
4576 SendMessage(hText, EM_EXGETSEL, 0, (LPARAM)&sel);
\r
4578 tmpsel.cpMax = -1; /*999999?*/
\r
4579 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&tmpsel);
\r
4580 SendMessage(hText, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cfmt);
\r
4581 /* Trying putting this here too. It still seems to tickle a RichEdit
\r
4582 * bug: sometimes RichEdit indents the first line of a paragraph too.
\r
4584 paraf.cbSize = sizeof(paraf);
\r
4585 paraf.dwMask = PFM_OFFSET | PFM_STARTINDENT;
\r
4586 paraf.dxStartIndent = 0;
\r
4587 paraf.dxOffset = WRAP_INDENT;
\r
4588 SendMessage(hText, EM_SETPARAFORMAT, 0, (LPARAM) ¶f);
\r
4589 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
4590 UpdateICSWidth(hText);
\r
4593 /*---------------------------------------------------------------------------*\
\r
4595 * Window Proc for main window
\r
4597 \*---------------------------------------------------------------------------*/
\r
4599 /* Process messages for main window, etc. */
\r
4601 WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
\r
4604 int wmId, wmEvent;
\r
4608 char fileTitle[MSG_SIZ];
\r
4609 char buf[MSG_SIZ];
\r
4610 static SnapData sd;
\r
4611 static int peek=0;
\r
4613 switch (message) {
\r
4615 case WM_PAINT: /* message: repaint portion of window */
\r
4619 case WM_ERASEBKGND:
\r
4620 if (IsIconic(hwnd)) {
\r
4621 /* Cheat; change the message */
\r
4622 return (DefWindowProc(hwnd, WM_ICONERASEBKGND, wParam, lParam));
\r
4624 return (DefWindowProc(hwnd, message, wParam, lParam));
\r
4628 case WM_LBUTTONDOWN:
\r
4629 case WM_MBUTTONDOWN:
\r
4630 case WM_RBUTTONDOWN:
\r
4631 case WM_LBUTTONUP:
\r
4632 case WM_MBUTTONUP:
\r
4633 case WM_RBUTTONUP:
\r
4634 case WM_MOUSEMOVE:
\r
4635 case WM_MOUSEWHEEL:
\r
4636 MouseEvent(hwnd, message, wParam, lParam);
\r
4640 if((char)wParam == '\b') {
\r
4641 ForwardEvent(); peek = 0;
\r
4644 JAWS_KBUP_NAVIGATION
\r
4649 if((char)wParam == '\b') {
\r
4650 if(!peek) BackwardEvent(), peek = 1;
\r
4653 JAWS_KBDOWN_NAVIGATION
\r
4659 JAWS_ALT_INTERCEPT
\r
4661 if (appData.icsActive && ((char)wParam == '\r' || (char)wParam > ' ' && !((char)wParam >= '1' && (char)wParam <= '9'))) {
\r
4662 // [HGM] movenum: for non-zero digits we always do type-in dialog
\r
4663 HWND h = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
4664 if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);
\r
4666 SendMessage(h, message, wParam, lParam);
\r
4667 } else if(lParam != KF_REPEAT) {
\r
4668 if (isalpha((char)wParam) || isdigit((char)wParam)) {
\r
4669 TypeInEvent((char)wParam);
\r
4670 } else if((char)wParam == 003) CopyGameToClipboard();
\r
4671 else if((char)wParam == 026) PasteGameOrFENFromClipboard();
\r
4676 case WM_PALETTECHANGED:
\r
4677 if (hwnd != (HWND)wParam && !appData.monoMode) {
\r
4679 HDC hdc = GetDC(hwndMain);
\r
4680 SelectPalette(hdc, hPal, TRUE);
\r
4681 nnew = RealizePalette(hdc);
\r
4683 paletteChanged = TRUE;
\r
4684 InvalidateRect(hwnd, &boardRect, FALSE);
\r
4686 ReleaseDC(hwnd, hdc);
\r
4690 case WM_QUERYNEWPALETTE:
\r
4691 if (!appData.monoMode /*&& paletteChanged*/) {
\r
4693 HDC hdc = GetDC(hwndMain);
\r
4694 paletteChanged = FALSE;
\r
4695 SelectPalette(hdc, hPal, FALSE);
\r
4696 nnew = RealizePalette(hdc);
\r
4698 InvalidateRect(hwnd, &boardRect, FALSE);
\r
4700 ReleaseDC(hwnd, hdc);
\r
4705 case WM_COMMAND: /* message: command from application menu */
\r
4706 wmId = LOWORD(wParam);
\r
4707 wmEvent = HIWORD(wParam);
\r
4712 SAY("new game enter a move to play against the computer with white");
\r
4715 case IDM_NewGameFRC:
\r
4716 if( NewGameFRC() == 0 ) {
\r
4721 case IDM_NewVariant:
\r
4722 NewVariantPopup(hwnd);
\r
4725 case IDM_LoadGame:
\r
4726 LoadGameDialog(hwnd, _("Load Game from File"));
\r
4729 case IDM_LoadNextGame:
\r
4733 case IDM_LoadPrevGame:
\r
4737 case IDM_ReloadGame:
\r
4741 case IDM_LoadPosition:
\r
4742 if (gameMode == AnalyzeMode || gameMode == AnalyzeFile) {
\r
4743 Reset(FALSE, TRUE);
\r
4746 f = OpenFileDialog(hwnd, "rb", "",
\r
4747 appData.oldSaveStyle ? "pos" : "fen",
\r
4749 _("Load Position from File"), &number, fileTitle, NULL);
\r
4751 LoadPosition(f, number, fileTitle);
\r
4755 case IDM_LoadNextPosition:
\r
4756 ReloadPosition(1);
\r
4759 case IDM_LoadPrevPosition:
\r
4760 ReloadPosition(-1);
\r
4763 case IDM_ReloadPosition:
\r
4764 ReloadPosition(0);
\r
4767 case IDM_SaveGame:
\r
4768 defName = DefaultFileName(appData.oldSaveStyle ? "gam" : "pgn");
\r
4769 f = OpenFileDialog(hwnd, "a", defName,
\r
4770 appData.oldSaveStyle ? "gam" : "pgn",
\r
4772 _("Save Game to File"), NULL, fileTitle, NULL);
\r
4774 SaveGame(f, 0, "");
\r
4778 case IDM_SavePosition:
\r
4779 defName = DefaultFileName(appData.oldSaveStyle ? "pos" : "fen");
\r
4780 f = OpenFileDialog(hwnd, "a", defName,
\r
4781 appData.oldSaveStyle ? "pos" : "fen",
\r
4783 _("Save Position to File"), NULL, fileTitle, NULL);
\r
4785 SavePosition(f, 0, "");
\r
4789 case IDM_SaveDiagram:
\r
4790 defName = "diagram";
\r
4791 f = OpenFileDialog(hwnd, "wb", defName,
\r
4794 _("Save Diagram to File"), NULL, fileTitle, NULL);
\r
4800 case IDM_CopyGame:
\r
4801 CopyGameToClipboard();
\r
4804 case IDM_PasteGame:
\r
4805 PasteGameFromClipboard();
\r
4808 case IDM_CopyGameListToClipboard:
\r
4809 CopyGameListToClipboard();
\r
4812 /* [AS] Autodetect FEN or PGN data */
\r
4813 case IDM_PasteAny:
\r
4814 PasteGameOrFENFromClipboard();
\r
4817 /* [AS] Move history */
\r
4818 case IDM_ShowMoveHistory:
\r
4819 if( MoveHistoryIsUp() ) {
\r
4820 MoveHistoryPopDown();
\r
4823 MoveHistoryPopUp();
\r
4827 /* [AS] Eval graph */
\r
4828 case IDM_ShowEvalGraph:
\r
4829 if( EvalGraphIsUp() ) {
\r
4830 EvalGraphPopDown();
\r
4834 SetFocus(hwndMain);
\r
4838 /* [AS] Engine output */
\r
4839 case IDM_ShowEngineOutput:
\r
4840 if( EngineOutputIsUp() ) {
\r
4841 EngineOutputPopDown();
\r
4844 EngineOutputPopUp();
\r
4848 /* [AS] User adjudication */
\r
4849 case IDM_UserAdjudication_White:
\r
4850 UserAdjudicationEvent( +1 );
\r
4853 case IDM_UserAdjudication_Black:
\r
4854 UserAdjudicationEvent( -1 );
\r
4857 case IDM_UserAdjudication_Draw:
\r
4858 UserAdjudicationEvent( 0 );
\r
4861 /* [AS] Game list options dialog */
\r
4862 case IDM_GameListOptions:
\r
4863 GameListOptions();
\r
4870 case IDM_CopyPosition:
\r
4871 CopyFENToClipboard();
\r
4874 case IDM_PastePosition:
\r
4875 PasteFENFromClipboard();
\r
4878 case IDM_MailMove:
\r
4882 case IDM_ReloadCMailMsg:
\r
4883 Reset(TRUE, TRUE);
\r
4884 ReloadCmailMsgEvent(FALSE);
\r
4887 case IDM_Minimize:
\r
4888 ShowWindow(hwnd, SW_MINIMIZE);
\r
4895 case IDM_MachineWhite:
\r
4896 MachineWhiteEvent();
\r
4898 * refresh the tags dialog only if it's visible
\r
4900 if (gameMode == MachinePlaysWhite && IsWindowVisible(editTagsDialog)) {
\r
4902 tags = PGNTags(&gameInfo);
\r
4903 TagsPopUp(tags, CmailMsg());
\r
4906 SAY("computer starts playing white");
\r
4909 case IDM_MachineBlack:
\r
4910 MachineBlackEvent();
\r
4912 * refresh the tags dialog only if it's visible
\r
4914 if (gameMode == MachinePlaysBlack && IsWindowVisible(editTagsDialog)) {
\r
4916 tags = PGNTags(&gameInfo);
\r
4917 TagsPopUp(tags, CmailMsg());
\r
4920 SAY("computer starts playing black");
\r
4923 case IDM_Match: // [HGM] match: flows into next case, after setting Match Mode and nr of Games
\r
4924 MatchEvent(2); // distinguish from command-line-triggered case (matchMode=1)
\r
4927 case IDM_TwoMachines:
\r
4928 TwoMachinesEvent();
\r
4930 * refresh the tags dialog only if it's visible
\r
4932 if (gameMode == TwoMachinesPlay && IsWindowVisible(editTagsDialog)) {
\r
4934 tags = PGNTags(&gameInfo);
\r
4935 TagsPopUp(tags, CmailMsg());
\r
4938 SAY("computer starts playing both sides");
\r
4941 case IDM_AnalysisMode:
\r
4942 if(AnalyzeModeEvent()) {
\r
4943 SAY("analyzing current position");
\r
4947 case IDM_AnalyzeFile:
\r
4948 AnalyzeFileEvent();
\r
4951 case IDM_IcsClient:
\r
4955 case IDM_EditGame:
\r
4956 case IDM_EditGame2:
\r
4961 case IDM_EditPosition:
\r
4962 case IDM_EditPosition2:
\r
4963 EditPositionEvent();
\r
4964 SAY("enter a FEN string or setup a position on the board using the control R pop up menu");
\r
4967 case IDM_Training:
\r
4971 case IDM_ShowGameList:
\r
4972 ShowGameListProc();
\r
4975 case IDM_EditProgs1:
\r
4976 EditTagsPopUp(firstChessProgramNames, &firstChessProgramNames);
\r
4979 case IDM_LoadProg1:
\r
4980 LoadEnginePopUp(hwndMain, 0);
\r
4983 case IDM_LoadProg2:
\r
4984 LoadEnginePopUp(hwndMain, 1);
\r
4987 case IDM_EditServers:
\r
4988 EditTagsPopUp(icsNames, &icsNames);
\r
4991 case IDM_EditTags:
\r
4996 case IDM_EditBook:
\r
5000 case IDM_EditComment:
\r
5002 if (commentUp && editComment) {
\r
5005 EditCommentEvent();
\r
5025 case IDM_CallFlag:
\r
5045 case IDM_StopObserving:
\r
5046 StopObservingEvent();
\r
5049 case IDM_StopExamining:
\r
5050 StopExaminingEvent();
\r
5054 UploadGameEvent();
\r
5057 case IDM_TypeInMove:
\r
5058 TypeInEvent('\000');
\r
5061 case IDM_TypeInName:
\r
5062 PopUpNameDialog('\000');
\r
5065 case IDM_Backward:
\r
5067 SetFocus(hwndMain);
\r
5074 SetFocus(hwndMain);
\r
5079 SetFocus(hwndMain);
\r
5084 SetFocus(hwndMain);
\r
5087 case OPT_GameListNext: // [HGM] forward these two accelerators to Game List
\r
5088 case OPT_GameListPrev:
\r
5089 if(gameListDialog) SendMessage(gameListDialog, WM_COMMAND, wmId, 0);
\r
5093 RevertEvent(FALSE);
\r
5096 case IDM_Annotate: // [HGM] vari: revert with annotation
\r
5097 RevertEvent(TRUE);
\r
5100 case IDM_TruncateGame:
\r
5101 TruncateGameEvent();
\r
5108 case IDM_RetractMove:
\r
5109 RetractMoveEvent();
\r
5112 case IDM_FlipView:
\r
5113 flipView = !flipView;
\r
5114 DrawPosition(FALSE, NULL);
\r
5117 case IDM_FlipClock:
\r
5118 flipClock = !flipClock;
\r
5119 DisplayBothClocks();
\r
5123 case IDM_MuteSounds:
\r
5124 mute = !mute; // [HGM] mute: keep track of global muting variable
\r
5125 CheckMenuItem(GetMenu(hwndMain),IDM_MuteSounds,
\r
5126 MF_BYCOMMAND|(mute?MF_CHECKED:MF_UNCHECKED));
\r
5129 case IDM_GeneralOptions:
\r
5130 GeneralOptionsPopup(hwnd);
\r
5131 DrawPosition(TRUE, NULL);
\r
5134 case IDM_BoardOptions:
\r
5135 BoardOptionsPopup(hwnd);
\r
5138 case IDM_EnginePlayOptions:
\r
5139 EnginePlayOptionsPopup(hwnd);
\r
5142 case IDM_Engine1Options:
\r
5143 EngineOptionsPopup(hwnd, &first);
\r
5146 case IDM_Engine2Options:
\r
5148 if(WaitForEngine(&second, SettingsMenuIfReady)) break;
\r
5149 EngineOptionsPopup(hwnd, &second);
\r
5152 case IDM_OptionsUCI:
\r
5153 UciOptionsPopup(hwnd);
\r
5157 TourneyPopup(hwnd);
\r
5160 case IDM_IcsOptions:
\r
5161 IcsOptionsPopup(hwnd);
\r
5165 FontsOptionsPopup(hwnd);
\r
5169 SoundOptionsPopup(hwnd);
\r
5172 case IDM_CommPort:
\r
5173 CommPortOptionsPopup(hwnd);
\r
5176 case IDM_LoadOptions:
\r
5177 LoadOptionsPopup(hwnd);
\r
5180 case IDM_SaveOptions:
\r
5181 SaveOptionsPopup(hwnd);
\r
5184 case IDM_TimeControl:
\r
5185 TimeControlOptionsPopup(hwnd);
\r
5188 case IDM_SaveSettings:
\r
5189 SaveSettings(settingsFileName);
\r
5192 case IDM_SaveSettingsOnExit:
\r
5193 saveSettingsOnExit = !saveSettingsOnExit;
\r
5194 (void) CheckMenuItem(GetMenu(hwndMain), IDM_SaveSettingsOnExit,
\r
5195 MF_BYCOMMAND|(saveSettingsOnExit ?
\r
5196 MF_CHECKED : MF_UNCHECKED));
\r
5207 case IDM_AboutGame:
\r
5212 appData.debugMode = !appData.debugMode;
\r
5213 if (appData.debugMode) {
\r
5214 char dir[MSG_SIZ];
\r
5215 GetCurrentDirectory(MSG_SIZ, dir);
\r
5216 SetCurrentDirectory(installDir);
\r
5217 debugFP = fopen(appData.nameOfDebugFile, "w");
\r
5218 SetCurrentDirectory(dir);
\r
5219 setbuf(debugFP, NULL);
\r
5226 case IDM_HELPCONTENTS:
\r
5227 if (!MyHelp (hwnd, "winboard.hlp", HELP_KEY,(DWORD)(LPSTR)"CONTENTS") &&
\r
5228 !HtmlHelp(hwnd, "winboard.chm", 0, 0) ) {
\r
5229 MessageBox (GetFocus(),
\r
5230 _("Unable to activate help"),
\r
5231 szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);
\r
5235 case IDM_HELPSEARCH:
\r
5236 if (!MyHelp (hwnd, "winboard.hlp", HELP_PARTIALKEY, (DWORD)(LPSTR)"") &&
\r
5237 !HtmlHelp(hwnd, "winboard.chm", 0, 0) ) {
\r
5238 MessageBox (GetFocus(),
\r
5239 _("Unable to activate help"),
\r
5240 szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);
\r
5244 case IDM_HELPHELP:
\r
5245 if(!WinHelp(hwnd, (LPSTR)NULL, HELP_HELPONHELP, 0)) {
\r
5246 MessageBox (GetFocus(),
\r
5247 _("Unable to activate help"),
\r
5248 szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);
\r
5253 lpProc = MakeProcInstance((FARPROC)About, hInst);
\r
5255 (gameInfo.event && strcmp(gameInfo.event, "Easter Egg Hunt") == 0) ?
\r
5256 "AboutBox2" : "AboutBox", hwnd, (DLGPROC)lpProc);
\r
5257 FreeProcInstance(lpProc);
\r
5260 case IDM_DirectCommand1:
\r
5261 AskQuestionEvent(_("Direct Command"),
\r
5262 _("Send to chess program:"), "", "1");
\r
5264 case IDM_DirectCommand2:
\r
5265 AskQuestionEvent(_("Direct Command"),
\r
5266 _("Send to second chess program:"), "", "2");
\r
5269 case EP_WhitePawn:
\r
5270 EditPositionMenuEvent(WhitePawn, fromX, fromY);
\r
5271 fromX = fromY = -1;
\r
5274 case EP_WhiteKnight:
\r
5275 EditPositionMenuEvent(WhiteKnight, fromX, fromY);
\r
5276 fromX = fromY = -1;
\r
5279 case EP_WhiteBishop:
\r
5280 EditPositionMenuEvent(WhiteBishop, fromX, fromY);
\r
5281 fromX = fromY = -1;
\r
5284 case EP_WhiteRook:
\r
5285 EditPositionMenuEvent(WhiteRook, fromX, fromY);
\r
5286 fromX = fromY = -1;
\r
5289 case EP_WhiteQueen:
\r
5290 EditPositionMenuEvent(WhiteQueen, fromX, fromY);
\r
5291 fromX = fromY = -1;
\r
5294 case EP_WhiteFerz:
\r
5295 EditPositionMenuEvent(WhiteFerz, fromX, fromY);
\r
5296 fromX = fromY = -1;
\r
5299 case EP_WhiteWazir:
\r
5300 EditPositionMenuEvent(WhiteWazir, fromX, fromY);
\r
5301 fromX = fromY = -1;
\r
5304 case EP_WhiteAlfil:
\r
5305 EditPositionMenuEvent(WhiteAlfil, fromX, fromY);
\r
5306 fromX = fromY = -1;
\r
5309 case EP_WhiteCannon:
\r
5310 EditPositionMenuEvent(WhiteCannon, fromX, fromY);
\r
5311 fromX = fromY = -1;
\r
5314 case EP_WhiteCardinal:
\r
5315 EditPositionMenuEvent(WhiteAngel, fromX, fromY);
\r
5316 fromX = fromY = -1;
\r
5319 case EP_WhiteMarshall:
\r
5320 EditPositionMenuEvent(WhiteMarshall, fromX, fromY);
\r
5321 fromX = fromY = -1;
\r
5324 case EP_WhiteKing:
\r
5325 EditPositionMenuEvent(WhiteKing, fromX, fromY);
\r
5326 fromX = fromY = -1;
\r
5329 case EP_BlackPawn:
\r
5330 EditPositionMenuEvent(BlackPawn, fromX, fromY);
\r
5331 fromX = fromY = -1;
\r
5334 case EP_BlackKnight:
\r
5335 EditPositionMenuEvent(BlackKnight, fromX, fromY);
\r
5336 fromX = fromY = -1;
\r
5339 case EP_BlackBishop:
\r
5340 EditPositionMenuEvent(BlackBishop, fromX, fromY);
\r
5341 fromX = fromY = -1;
\r
5344 case EP_BlackRook:
\r
5345 EditPositionMenuEvent(BlackRook, fromX, fromY);
\r
5346 fromX = fromY = -1;
\r
5349 case EP_BlackQueen:
\r
5350 EditPositionMenuEvent(BlackQueen, fromX, fromY);
\r
5351 fromX = fromY = -1;
\r
5354 case EP_BlackFerz:
\r
5355 EditPositionMenuEvent(BlackFerz, fromX, fromY);
\r
5356 fromX = fromY = -1;
\r
5359 case EP_BlackWazir:
\r
5360 EditPositionMenuEvent(BlackWazir, fromX, fromY);
\r
5361 fromX = fromY = -1;
\r
5364 case EP_BlackAlfil:
\r
5365 EditPositionMenuEvent(BlackAlfil, fromX, fromY);
\r
5366 fromX = fromY = -1;
\r
5369 case EP_BlackCannon:
\r
5370 EditPositionMenuEvent(BlackCannon, fromX, fromY);
\r
5371 fromX = fromY = -1;
\r
5374 case EP_BlackCardinal:
\r
5375 EditPositionMenuEvent(BlackAngel, fromX, fromY);
\r
5376 fromX = fromY = -1;
\r
5379 case EP_BlackMarshall:
\r
5380 EditPositionMenuEvent(BlackMarshall, fromX, fromY);
\r
5381 fromX = fromY = -1;
\r
5384 case EP_BlackKing:
\r
5385 EditPositionMenuEvent(BlackKing, fromX, fromY);
\r
5386 fromX = fromY = -1;
\r
5389 case EP_EmptySquare:
\r
5390 EditPositionMenuEvent(EmptySquare, fromX, fromY);
\r
5391 fromX = fromY = -1;
\r
5394 case EP_ClearBoard:
\r
5395 EditPositionMenuEvent(ClearBoard, fromX, fromY);
\r
5396 fromX = fromY = -1;
\r
5400 EditPositionMenuEvent(WhitePlay, fromX, fromY);
\r
5401 fromX = fromY = -1;
\r
5405 EditPositionMenuEvent(BlackPlay, fromX, fromY);
\r
5406 fromX = fromY = -1;
\r
5410 EditPositionMenuEvent(PromotePiece, fromX, fromY);
\r
5411 fromX = fromY = -1;
\r
5415 EditPositionMenuEvent(DemotePiece, fromX, fromY);
\r
5416 fromX = fromY = -1;
\r
5420 DropMenuEvent(WhitePawn, fromX, fromY);
\r
5421 fromX = fromY = -1;
\r
5425 DropMenuEvent(WhiteKnight, fromX, fromY);
\r
5426 fromX = fromY = -1;
\r
5430 DropMenuEvent(WhiteBishop, fromX, fromY);
\r
5431 fromX = fromY = -1;
\r
5435 DropMenuEvent(WhiteRook, fromX, fromY);
\r
5436 fromX = fromY = -1;
\r
5440 DropMenuEvent(WhiteQueen, fromX, fromY);
\r
5441 fromX = fromY = -1;
\r
5445 barbaric = 0; appData.language = "";
\r
5446 TranslateMenus(0);
\r
5447 CheckMenuItem(GetMenu(hwndMain), lastChecked, MF_BYCOMMAND|MF_UNCHECKED);
\r
5448 CheckMenuItem(GetMenu(hwndMain), IDM_English, MF_BYCOMMAND|MF_CHECKED);
\r
5449 lastChecked = wmId;
\r
5453 if(wmId >= IDM_RecentEngines && wmId < IDM_RecentEngines + appData.recentEngines)
\r
5454 RecentEngineEvent(wmId - IDM_RecentEngines);
\r
5456 if(wmId > IDM_English && wmId < IDM_English+20) {
\r
5457 LoadLanguageFile(languageFile[wmId - IDM_English - 1]);
\r
5458 TranslateMenus(0);
\r
5459 CheckMenuItem(GetMenu(hwndMain), lastChecked, MF_BYCOMMAND|MF_UNCHECKED);
\r
5460 CheckMenuItem(GetMenu(hwndMain), wmId, MF_BYCOMMAND|MF_CHECKED);
\r
5461 lastChecked = wmId;
\r
5464 return (DefWindowProc(hwnd, message, wParam, lParam));
\r
5470 case CLOCK_TIMER_ID:
\r
5471 KillTimer(hwnd, clockTimerEvent); /* Simulate one-shot timer as in X */
\r
5472 clockTimerEvent = 0;
\r
5473 DecrementClocks(); /* call into back end */
\r
5475 case LOAD_GAME_TIMER_ID:
\r
5476 KillTimer(hwnd, loadGameTimerEvent); /* Simulate one-shot timer as in X*/
\r
5477 loadGameTimerEvent = 0;
\r
5478 AutoPlayGameLoop(); /* call into back end */
\r
5480 case ANALYSIS_TIMER_ID:
\r
5481 if ((gameMode == AnalyzeMode || gameMode == AnalyzeFile
\r
5482 || appData.icsEngineAnalyze) && appData.periodicUpdates) {
\r
5483 AnalysisPeriodicEvent(0);
\r
5485 KillTimer(hwnd, analysisTimerEvent);
\r
5486 analysisTimerEvent = 0;
\r
5489 case DELAYED_TIMER_ID:
\r
5490 KillTimer(hwnd, delayedTimerEvent);
\r
5491 delayedTimerEvent = 0;
\r
5492 delayedTimerCallback();
\r
5497 case WM_USER_Input:
\r
5498 InputEvent(hwnd, message, wParam, lParam);
\r
5501 /* [AS] Also move "attached" child windows */
\r
5502 case WM_WINDOWPOSCHANGING:
\r
5504 if( hwnd == hwndMain && appData.useStickyWindows ) {
\r
5505 LPWINDOWPOS lpwp = (LPWINDOWPOS) lParam;
\r
5507 if( ((lpwp->flags & SWP_NOMOVE) == 0) && ((lpwp->flags & SWP_NOSIZE) != 0) ) {
\r
5508 /* Window is moving */
\r
5511 // GetWindowRect( hwnd, &rcMain ); //[HGM] sticky: in XP this returned new position, not old
\r
5512 rcMain.left = wpMain.x; // replace by these 4 lines to reconstruct old rect
\r
5513 rcMain.right = wpMain.x + wpMain.width;
\r
5514 rcMain.top = wpMain.y;
\r
5515 rcMain.bottom = wpMain.y + wpMain.height;
\r
5517 ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, moveHistoryDialog, &wpMoveHistory );
\r
5518 ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, evalGraphDialog, &wpEvalGraph );
\r
5519 ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, engineOutputDialog, &wpEngineOutput );
\r
5520 ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, gameListDialog, &wpGameList );
\r
5521 ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, hwndConsole, &wpConsole );
\r
5522 wpMain.x = lpwp->x;
\r
5523 wpMain.y = lpwp->y;
\r
5528 /* [AS] Snapping */
\r
5529 case WM_ENTERSIZEMOVE:
\r
5530 if(appData.debugMode) { fprintf(debugFP, "size-move\n"); }
\r
5531 if (hwnd == hwndMain) {
\r
5532 doingSizing = TRUE;
\r
5535 return OnEnterSizeMove( &sd, hwnd, wParam, lParam );
\r
5539 if(appData.debugMode) { fprintf(debugFP, "sizing\n"); }
\r
5540 if (hwnd == hwndMain) {
\r
5541 lastSizing = wParam;
\r
5546 if(appData.debugMode) { fprintf(debugFP, "moving\n"); }
\r
5547 return OnMoving( &sd, hwnd, wParam, lParam );
\r
5549 case WM_EXITSIZEMOVE:
\r
5550 if(appData.debugMode) { fprintf(debugFP, "exit size-move, size = %d\n", squareSize); }
\r
5551 if (hwnd == hwndMain) {
\r
5553 doingSizing = FALSE;
\r
5554 InvalidateRect(hwnd, &boardRect, FALSE);
\r
5555 GetClientRect(hwnd, &client);
\r
5556 ResizeBoard(client.right, client.bottom, lastSizing);
\r
5558 if(appData.debugMode) { fprintf(debugFP, "square size = %d\n", squareSize); }
\r
5560 return OnExitSizeMove( &sd, hwnd, wParam, lParam );
\r
5563 case WM_DESTROY: /* message: window being destroyed */
\r
5564 PostQuitMessage(0);
\r
5568 if (hwnd == hwndMain) {
\r
5573 default: /* Passes it on if unprocessed */
\r
5574 return (DefWindowProc(hwnd, message, wParam, lParam));
\r
5579 /*---------------------------------------------------------------------------*\
\r
5581 * Misc utility routines
\r
5583 \*---------------------------------------------------------------------------*/
\r
5586 * Decent random number generator, at least not as bad as Windows
\r
5587 * standard rand, which returns a value in the range 0 to 0x7fff.
\r
5589 unsigned int randstate;
\r
5594 randstate = randstate * 1664525 + 1013904223;
\r
5595 return (int) randstate & 0x7fffffff;
\r
5599 mysrandom(unsigned int seed)
\r
5606 * returns TRUE if user selects a different color, FALSE otherwise
\r
5610 ChangeColor(HWND hwnd, COLORREF *which)
\r
5612 static BOOL firstTime = TRUE;
\r
5613 static DWORD customColors[16];
\r
5615 COLORREF newcolor;
\r
5620 /* Make initial colors in use available as custom colors */
\r
5621 /* Should we put the compiled-in defaults here instead? */
\r
5623 customColors[i++] = lightSquareColor & 0xffffff;
\r
5624 customColors[i++] = darkSquareColor & 0xffffff;
\r
5625 customColors[i++] = whitePieceColor & 0xffffff;
\r
5626 customColors[i++] = blackPieceColor & 0xffffff;
\r
5627 customColors[i++] = highlightSquareColor & 0xffffff;
\r
5628 customColors[i++] = premoveHighlightColor & 0xffffff;
\r
5630 for (ccl = (ColorClass) 0; ccl < NColorClasses && i < 16; ccl++) {
\r
5631 customColors[i++] = textAttribs[ccl].color;
\r
5633 while (i < 16) customColors[i++] = RGB(255, 255, 255);
\r
5634 firstTime = FALSE;
\r
5637 cc.lStructSize = sizeof(cc);
\r
5638 cc.hwndOwner = hwnd;
\r
5639 cc.hInstance = NULL;
\r
5640 cc.rgbResult = (DWORD) (*which & 0xffffff);
\r
5641 cc.lpCustColors = (LPDWORD) customColors;
\r
5642 cc.Flags = CC_RGBINIT|CC_FULLOPEN;
\r
5644 if (!ChooseColor(&cc)) return FALSE;
\r
5646 newcolor = (COLORREF) (0x2000000 | cc.rgbResult);
\r
5647 if (newcolor == *which) return FALSE;
\r
5648 *which = newcolor;
\r
5652 InitDrawingColors();
\r
5653 InvalidateRect(hwnd, &boardRect, FALSE);
\r
5658 MyLoadSound(MySound *ms)
\r
5664 if (ms->data && ms->flag) free(ms->data);
\r
5667 switch (ms->name[0]) {
\r
5673 /* System sound from Control Panel. Don't preload here. */
\r
5677 if (ms->name[1] == NULLCHAR) {
\r
5678 /* "!" alone = silence */
\r
5681 /* Builtin wave resource. Error if not found. */
\r
5682 HANDLE h = FindResource(hInst, ms->name + 1, "WAVE");
\r
5683 if (h == NULL) break;
\r
5684 ms->data = (void *)LoadResource(hInst, h);
\r
5685 ms->flag = 0; // not maloced, so cannot be freed!
\r
5686 if (h == NULL) break;
\r
5691 /* .wav file. Error if not found. */
\r
5692 f = fopen(ms->name, "rb");
\r
5693 if (f == NULL) break;
\r
5694 if (fstat(fileno(f), &st) < 0) break;
\r
5695 ms->data = malloc(st.st_size);
\r
5697 if (fread(ms->data, st.st_size, 1, f) < 1) break;
\r
5703 char buf[MSG_SIZ];
\r
5704 snprintf(buf, MSG_SIZ, _("Error loading sound %s"), ms->name);
\r
5705 DisplayError(buf, GetLastError());
\r
5711 MyPlaySound(MySound *ms)
\r
5713 BOOLEAN ok = FALSE;
\r
5715 if(mute) return TRUE; // [HGM] mute: suppress all sound play when muted
\r
5716 switch (ms->name[0]) {
\r
5718 if(appData.debugMode) fprintf(debugFP, "silence\n");
\r
5723 /* System sound from Control Panel (deprecated feature).
\r
5724 "$" alone or an unset sound name gets default beep (still in use). */
\r
5725 if (ms->name[1]) {
\r
5726 ok = PlaySound(ms->name + 1, NULL, SND_ALIAS|SND_ASYNC);
\r
5728 if (!ok) ok = MessageBeep(MB_OK);
\r
5731 /* Builtin wave resource, or "!" alone for silence */
\r
5732 if (ms->name[1]) {
\r
5733 if (ms->data == NULL) return FALSE;
\r
5734 ok = PlaySound(ms->data, NULL, SND_MEMORY|SND_ASYNC);
\r
5740 /* .wav file. Error if not found. */
\r
5741 if (ms->data == NULL) return FALSE;
\r
5742 ok = PlaySound(ms->data, NULL, SND_MEMORY|SND_ASYNC);
\r
5745 /* Don't print an error: this can happen innocently if the sound driver
\r
5746 is busy; for instance, if another instance of WinBoard is playing
\r
5747 a sound at about the same time. */
\r
5753 OldOpenFileHook(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
5756 OPENFILENAME *ofn;
\r
5757 static UINT *number; /* gross that this is static */
\r
5759 switch (message) {
\r
5760 case WM_INITDIALOG: /* message: initialize dialog box */
\r
5761 /* Center the dialog over the application window */
\r
5762 ofn = (OPENFILENAME *) lParam;
\r
5763 if (ofn->Flags & OFN_ENABLETEMPLATE) {
\r
5764 number = (UINT *) ofn->lCustData;
\r
5765 SendMessage(GetDlgItem(hDlg, edt2), WM_SETTEXT, 0, (LPARAM) "");
\r
5769 CenterWindow (hDlg, GetWindow (hDlg, GW_OWNER));
\r
5770 Translate(hDlg, 1536);
\r
5771 return FALSE; /* Allow for further processing */
\r
5774 if ((LOWORD(wParam) == IDOK) && (number != NULL)) {
\r
5775 *number = GetDlgItemInt(hDlg, OPT_IndexNumberOld, &ok, FALSE);
\r
5777 return FALSE; /* Allow for further processing */
\r
5783 OpenFileHook(HWND hdlg, UINT uiMsg, WPARAM wParam, LPARAM lParam)
\r
5785 static UINT *number;
\r
5786 OPENFILENAME *ofname;
\r
5789 case WM_INITDIALOG:
\r
5790 Translate(hdlg, DLG_IndexNumber);
\r
5791 ofname = (OPENFILENAME *)lParam;
\r
5792 number = (UINT *)(ofname->lCustData);
\r
5795 ofnot = (OFNOTIFY *)lParam;
\r
5796 if (ofnot->hdr.code == CDN_FILEOK) {
\r
5797 *number = GetDlgItemInt(hdlg, OPT_IndexNumber, NULL, FALSE);
\r
5806 OpenFileDialog(HWND hwnd, char *write, char *defName, char *defExt, // [HGM] diag: type of 'write' now string
\r
5807 char *nameFilt, char *dlgTitle, UINT *number,
\r
5808 char fileTitle[MSG_SIZ], char fileName[MSG_SIZ])
\r
5810 OPENFILENAME openFileName;
\r
5811 char buf1[MSG_SIZ];
\r
5814 if (fileName == NULL) fileName = buf1;
\r
5815 if (defName == NULL) {
\r
5816 safeStrCpy(fileName, "*.", 3 );
\r
5817 strcat(fileName, defExt);
\r
5819 safeStrCpy(fileName, defName, MSG_SIZ );
\r
5821 if (fileTitle) safeStrCpy(fileTitle, "", MSG_SIZ );
\r
5822 if (number) *number = 0;
\r
5824 openFileName.lStructSize = sizeof(OPENFILENAME);
\r
5825 openFileName.hwndOwner = hwnd;
\r
5826 openFileName.hInstance = (HANDLE) hInst;
\r
5827 openFileName.lpstrFilter = nameFilt;
\r
5828 openFileName.lpstrCustomFilter = (LPSTR) NULL;
\r
5829 openFileName.nMaxCustFilter = 0L;
\r
5830 openFileName.nFilterIndex = 1L;
\r
5831 openFileName.lpstrFile = fileName;
\r
5832 openFileName.nMaxFile = MSG_SIZ;
\r
5833 openFileName.lpstrFileTitle = fileTitle;
\r
5834 openFileName.nMaxFileTitle = fileTitle ? MSG_SIZ : 0;
\r
5835 openFileName.lpstrInitialDir = NULL;
\r
5836 openFileName.lpstrTitle = dlgTitle;
\r
5837 openFileName.Flags = OFN_PATHMUSTEXIST | OFN_HIDEREADONLY
\r
5838 | (write[0] != 'r' ? 0 : OFN_FILEMUSTEXIST)
\r
5839 | (number ? OFN_ENABLETEMPLATE | OFN_ENABLEHOOK: 0)
\r
5840 | (oldDialog ? 0 : OFN_EXPLORER);
\r
5841 openFileName.nFileOffset = 0;
\r
5842 openFileName.nFileExtension = 0;
\r
5843 openFileName.lpstrDefExt = defExt;
\r
5844 openFileName.lCustData = (LONG) number;
\r
5845 openFileName.lpfnHook = oldDialog ?
\r
5846 (LPOFNHOOKPROC) OldOpenFileHook : (LPOFNHOOKPROC) OpenFileHook;
\r
5847 openFileName.lpTemplateName = (LPSTR)(oldDialog ? 1536 : DLG_IndexNumber);
\r
5849 if (write[0] != 'r' ? GetSaveFileName(&openFileName) :
\r
5850 GetOpenFileName(&openFileName)) {
\r
5851 /* open the file */
\r
5852 f = fopen(openFileName.lpstrFile, write);
\r
5854 MessageBox(hwnd, _("File open failed"), NULL,
\r
5855 MB_OK|MB_ICONEXCLAMATION);
\r
5859 int err = CommDlgExtendedError();
\r
5860 if (err != 0) DisplayError(_("Internal error in file dialog box"), err);
\r
5869 MenuPopup(HWND hwnd, POINT pt, HMENU hmenu, UINT def)
\r
5871 HMENU hmenuTrackPopup; /* floating pop-up menu */
\r
5874 * Get the first pop-up menu in the menu template. This is the
\r
5875 * menu that TrackPopupMenu displays.
\r
5877 hmenuTrackPopup = GetSubMenu(hmenu, 0);
\r
5878 TranslateOneMenu(10, hmenuTrackPopup);
\r
5880 SetMenuDefaultItem(hmenuTrackPopup, def, FALSE);
\r
5883 * TrackPopup uses screen coordinates, so convert the
\r
5884 * coordinates of the mouse click to screen coordinates.
\r
5886 ClientToScreen(hwnd, (LPPOINT) &pt);
\r
5888 /* Draw and track the floating pop-up menu. */
\r
5889 TrackPopupMenu(hmenuTrackPopup, TPM_CENTERALIGN | TPM_RIGHTBUTTON,
\r
5890 pt.x, pt.y, 0, hwnd, NULL);
\r
5892 /* Destroy the menu.*/
\r
5893 DestroyMenu(hmenu);
\r
5898 int sizeX, sizeY, newSizeX, newSizeY;
\r
5900 } ResizeEditPlusButtonsClosure;
\r
5903 ResizeEditPlusButtonsCallback(HWND hChild, LPARAM lparam)
\r
5905 ResizeEditPlusButtonsClosure *cl = (ResizeEditPlusButtonsClosure *)lparam;
\r
5909 if (hChild == cl->hText) return TRUE;
\r
5910 GetWindowRect(hChild, &rect); /* gives screen coords */
\r
5911 pt.x = rect.left + (cl->newSizeX - cl->sizeX)/2;
\r
5912 pt.y = rect.top + cl->newSizeY - cl->sizeY;
\r
5913 ScreenToClient(cl->hDlg, &pt);
\r
5914 cl->hdwp = DeferWindowPos(cl->hdwp, hChild, NULL,
\r
5915 pt.x, pt.y, rect.right - rect.left, rect.bottom - rect.top, SWP_NOZORDER);
\r
5919 /* Resize a dialog that has a (rich) edit field filling most of
\r
5920 the top, with a row of buttons below */
\r
5922 ResizeEditPlusButtons(HWND hDlg, HWND hText, int sizeX, int sizeY, int newSizeX, int newSizeY)
\r
5925 int newTextHeight, newTextWidth;
\r
5926 ResizeEditPlusButtonsClosure cl;
\r
5928 /*if (IsIconic(hDlg)) return;*/
\r
5929 if (newSizeX == sizeX && newSizeY == sizeY) return;
\r
5931 cl.hdwp = BeginDeferWindowPos(8);
\r
5933 GetWindowRect(hText, &rectText); /* gives screen coords */
\r
5934 newTextWidth = rectText.right - rectText.left + newSizeX - sizeX;
\r
5935 newTextHeight = rectText.bottom - rectText.top + newSizeY - sizeY;
\r
5936 if (newTextHeight < 0) {
\r
5937 newSizeY += -newTextHeight;
\r
5938 newTextHeight = 0;
\r
5940 cl.hdwp = DeferWindowPos(cl.hdwp, hText, NULL, 0, 0,
\r
5941 newTextWidth, newTextHeight, SWP_NOZORDER|SWP_NOMOVE);
\r
5947 cl.newSizeX = newSizeX;
\r
5948 cl.newSizeY = newSizeY;
\r
5949 EnumChildWindows(hDlg, ResizeEditPlusButtonsCallback, (LPARAM)&cl);
\r
5951 EndDeferWindowPos(cl.hdwp);
\r
5954 BOOL CenterWindowEx(HWND hwndChild, HWND hwndParent, int mode)
\r
5956 RECT rChild, rParent;
\r
5957 int wChild, hChild, wParent, hParent;
\r
5958 int wScreen, hScreen, xNew, yNew;
\r
5961 /* Get the Height and Width of the child window */
\r
5962 GetWindowRect (hwndChild, &rChild);
\r
5963 wChild = rChild.right - rChild.left;
\r
5964 hChild = rChild.bottom - rChild.top;
\r
5966 /* Get the Height and Width of the parent window */
\r
5967 GetWindowRect (hwndParent, &rParent);
\r
5968 wParent = rParent.right - rParent.left;
\r
5969 hParent = rParent.bottom - rParent.top;
\r
5971 /* Get the display limits */
\r
5972 hdc = GetDC (hwndChild);
\r
5973 wScreen = GetDeviceCaps (hdc, HORZRES);
\r
5974 hScreen = GetDeviceCaps (hdc, VERTRES);
\r
5975 ReleaseDC(hwndChild, hdc);
\r
5977 /* Calculate new X position, then adjust for screen */
\r
5978 xNew = rParent.left + ((wParent - wChild) /2);
\r
5981 } else if ((xNew+wChild) > wScreen) {
\r
5982 xNew = wScreen - wChild;
\r
5985 /* Calculate new Y position, then adjust for screen */
\r
5987 yNew = rParent.top + ((hParent - hChild) /2);
\r
5990 yNew = rParent.top + GetSystemMetrics( SM_CYCAPTION ) * 2 / 3;
\r
5995 } else if ((yNew+hChild) > hScreen) {
\r
5996 yNew = hScreen - hChild;
\r
5999 /* Set it, and return */
\r
6000 return SetWindowPos (hwndChild, NULL,
\r
6001 xNew, yNew, 0, 0, SWP_NOSIZE | SWP_NOZORDER);
\r
6004 /* Center one window over another */
\r
6005 BOOL CenterWindow (HWND hwndChild, HWND hwndParent)
\r
6007 return CenterWindowEx( hwndChild, hwndParent, 0 );
\r
6010 /*---------------------------------------------------------------------------*\
\r
6012 * Startup Dialog functions
\r
6014 \*---------------------------------------------------------------------------*/
\r
6016 InitComboStrings(HANDLE hwndCombo, char **cd)
\r
6018 SendMessage(hwndCombo, CB_RESETCONTENT, 0, 0);
\r
6020 while (*cd != NULL) {
\r
6021 SendMessage(hwndCombo, CB_ADDSTRING, 0, (LPARAM) T_(*cd));
\r
6027 InitComboStringsFromOption(HANDLE hwndCombo, char *str)
\r
6029 char buf1[MAX_ARG_LEN];
\r
6032 if (str[0] == '@') {
\r
6033 FILE* f = fopen(str + 1, "r");
\r
6035 DisplayFatalError(str + 1, errno, 2);
\r
6038 len = fread(buf1, 1, sizeof(buf1)-1, f);
\r
6040 buf1[len] = NULLCHAR;
\r
6044 SendMessage(hwndCombo, CB_RESETCONTENT, 0, 0);
\r
6047 char buf[MSG_SIZ];
\r
6048 char *end = strchr(str, '\n');
\r
6049 if (end == NULL) return;
\r
6050 memcpy(buf, str, end - str);
\r
6051 buf[end - str] = NULLCHAR;
\r
6052 SendMessage(hwndCombo, CB_ADDSTRING, 0, (LPARAM) buf);
\r
6058 SetStartupDialogEnables(HWND hDlg)
\r
6060 EnableWindow(GetDlgItem(hDlg, OPT_ChessEngineName),
\r
6061 IsDlgButtonChecked(hDlg, OPT_ChessEngine) ||
\r
6062 (appData.zippyPlay && IsDlgButtonChecked(hDlg, OPT_ChessServer)));
\r
6063 EnableWindow(GetDlgItem(hDlg, OPT_SecondChessEngineName),
\r
6064 IsDlgButtonChecked(hDlg, OPT_ChessEngine));
\r
6065 EnableWindow(GetDlgItem(hDlg, OPT_ChessServerName),
\r
6066 IsDlgButtonChecked(hDlg, OPT_ChessServer));
\r
6067 EnableWindow(GetDlgItem(hDlg, OPT_AdditionalOptions),
\r
6068 IsDlgButtonChecked(hDlg, OPT_AnyAdditional));
\r
6069 EnableWindow(GetDlgItem(hDlg, IDOK),
\r
6070 IsDlgButtonChecked(hDlg, OPT_ChessEngine) ||
\r
6071 IsDlgButtonChecked(hDlg, OPT_ChessServer) ||
\r
6072 IsDlgButtonChecked(hDlg, OPT_View));
\r
6076 QuoteForFilename(char *filename)
\r
6078 int dquote, space;
\r
6079 dquote = strchr(filename, '"') != NULL;
\r
6080 space = strchr(filename, ' ') != NULL;
\r
6081 if (dquote || space) {
\r
6093 InitEngineBox(HWND hDlg, HWND hwndCombo, char* nthcp, char* nthd, char* nthdir, char *nthnames)
\r
6095 char buf[MSG_SIZ];
\r
6098 InitComboStringsFromOption(hwndCombo, nthnames);
\r
6099 q = QuoteForFilename(nthcp);
\r
6100 snprintf(buf, MSG_SIZ, "%s%s%s", q, nthcp, q);
\r
6101 if (*nthdir != NULLCHAR) {
\r
6102 q = QuoteForFilename(nthdir);
\r
6103 snprintf(buf + strlen(buf), MSG_SIZ, " /%s=%s%s%s", nthd, q, nthdir, q);
\r
6105 if (*nthcp == NULLCHAR) {
\r
6106 SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) 0, (LPARAM) 0);
\r
6107 } else if (SendMessage(hwndCombo, CB_SELECTSTRING, (WPARAM) -1, (LPARAM) buf) == CB_ERR) {
\r
6108 SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) -1, (LPARAM) 0);
\r
6109 SendMessage(hwndCombo, WM_SETTEXT, (WPARAM) 0, (LPARAM) buf);
\r
6114 StartupDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
6116 char buf[MSG_SIZ];
\r
6120 switch (message) {
\r
6121 case WM_INITDIALOG:
\r
6122 /* Center the dialog */
\r
6123 CenterWindow (hDlg, GetDesktopWindow());
\r
6124 Translate(hDlg, DLG_Startup);
\r
6125 /* Initialize the dialog items */
\r
6126 InitEngineBox(hDlg, GetDlgItem(hDlg, OPT_ChessEngineName),
\r
6127 appData.firstChessProgram, "fd", appData.firstDirectory,
\r
6128 firstChessProgramNames);
\r
6129 InitEngineBox(hDlg, GetDlgItem(hDlg, OPT_SecondChessEngineName),
\r
6130 appData.secondChessProgram, singleList ? "fd" : "sd", appData.secondDirectory,
\r
6131 singleList ? firstChessProgramNames : secondChessProgramNames); //[HGM] single: use first list in second combo
\r
6132 hwndCombo = GetDlgItem(hDlg, OPT_ChessServerName);
\r
6133 InitComboStringsFromOption(hwndCombo, icsNames);
\r
6134 snprintf(buf, MSG_SIZ, "%s /icsport=%s", appData.icsHost, appData.icsPort);
\r
6135 if (*appData.icsHelper != NULLCHAR) {
\r
6136 char *q = QuoteForFilename(appData.icsHelper);
\r
6137 sprintf(buf + strlen(buf), " /icshelper=%s%s%s", q, appData.icsHelper, q);
\r
6139 if (*appData.icsHost == NULLCHAR) {
\r
6140 SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) 0, (LPARAM) 0);
\r
6141 /*SendMessage(hwndCombo, CB_SHOWDROPDOWN, (WPARAM) TRUE, (LPARAM) 0); !!too soon */
\r
6142 } else if (SendMessage(hwndCombo, CB_SELECTSTRING, (WPARAM) -1, (LPARAM) buf) == CB_ERR) {
\r
6143 SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) -1, (LPARAM) 0);
\r
6144 SendMessage(hwndCombo, WM_SETTEXT, (WPARAM) 0, (LPARAM) buf);
\r
6147 if (appData.icsActive) {
\r
6148 CheckDlgButton(hDlg, OPT_ChessServer, BST_CHECKED);
\r
6150 else if (appData.noChessProgram) {
\r
6151 CheckDlgButton(hDlg, OPT_View, BST_CHECKED);
\r
6154 CheckDlgButton(hDlg, OPT_ChessEngine, BST_CHECKED);
\r
6157 SetStartupDialogEnables(hDlg);
\r
6161 switch (LOWORD(wParam)) {
\r
6163 if (IsDlgButtonChecked(hDlg, OPT_ChessEngine)) {
\r
6164 safeStrCpy(buf, "/fcp=", sizeof(buf)/sizeof(buf[0]) );
\r
6165 GetDlgItemText(hDlg, OPT_ChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));
\r
6167 comboLine = strdup(p+5); // [HGM] recent: remember complete line of first combobox
\r
6168 ParseArgs(StringGet, &p);
\r
6169 safeStrCpy(buf, singleList ? "/fcp=" : "/scp=", sizeof(buf)/sizeof(buf[0]) );
\r
6170 GetDlgItemText(hDlg, OPT_SecondChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));
\r
6172 SwapEngines(singleList); // temporarily swap first and second, to load a second 'first', ...
\r
6173 ParseArgs(StringGet, &p);
\r
6174 SwapEngines(singleList); // ... and then make it 'second'
\r
6175 appData.noChessProgram = FALSE;
\r
6176 appData.icsActive = FALSE;
\r
6177 } else if (IsDlgButtonChecked(hDlg, OPT_ChessServer)) {
\r
6178 safeStrCpy(buf, "/ics /icshost=", sizeof(buf)/sizeof(buf[0]) );
\r
6179 GetDlgItemText(hDlg, OPT_ChessServerName, buf + strlen(buf), sizeof(buf) - strlen(buf));
\r
6181 ParseArgs(StringGet, &p);
\r
6182 if (appData.zippyPlay) {
\r
6183 safeStrCpy(buf, "/fcp=", sizeof(buf)/sizeof(buf[0]) );
\r
6184 GetDlgItemText(hDlg, OPT_ChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));
\r
6186 ParseArgs(StringGet, &p);
\r
6188 } else if (IsDlgButtonChecked(hDlg, OPT_View)) {
\r
6189 appData.noChessProgram = TRUE;
\r
6190 appData.icsActive = FALSE;
\r
6192 MessageBox(hDlg, _("Choose an option, or cancel to exit"),
\r
6193 _("Option Error"), MB_OK|MB_ICONEXCLAMATION);
\r
6196 if (IsDlgButtonChecked(hDlg, OPT_AnyAdditional)) {
\r
6197 GetDlgItemText(hDlg, OPT_AdditionalOptions, buf, sizeof(buf));
\r
6199 ParseArgs(StringGet, &p);
\r
6201 EndDialog(hDlg, TRUE);
\r
6208 case IDM_HELPCONTENTS:
\r
6209 if (!WinHelp (hDlg, "winboard.hlp", HELP_KEY,(DWORD)(LPSTR)"CONTENTS")) {
\r
6210 MessageBox (GetFocus(),
\r
6211 _("Unable to activate help"),
\r
6212 szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);
\r
6217 SetStartupDialogEnables(hDlg);
\r
6225 /*---------------------------------------------------------------------------*\
\r
6227 * About box dialog functions
\r
6229 \*---------------------------------------------------------------------------*/
\r
6231 /* Process messages for "About" dialog box */
\r
6233 About(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
6235 switch (message) {
\r
6236 case WM_INITDIALOG: /* message: initialize dialog box */
\r
6237 /* Center the dialog over the application window */
\r
6238 CenterWindow (hDlg, GetWindow (hDlg, GW_OWNER));
\r
6239 SetDlgItemText(hDlg, ABOUTBOX_Version, programVersion);
\r
6240 Translate(hDlg, ABOUTBOX);
\r
6244 case WM_COMMAND: /* message: received a command */
\r
6245 if (LOWORD(wParam) == IDOK /* "OK" box selected? */
\r
6246 || LOWORD(wParam) == IDCANCEL) { /* System menu close command? */
\r
6247 EndDialog(hDlg, TRUE); /* Exit the dialog */
\r
6255 /*---------------------------------------------------------------------------*\
\r
6257 * Comment Dialog functions
\r
6259 \*---------------------------------------------------------------------------*/
\r
6262 CommentDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
6264 static HANDLE hwndText = NULL;
\r
6265 int len, newSizeX, newSizeY, flags;
\r
6266 static int sizeX, sizeY;
\r
6271 switch (message) {
\r
6272 case WM_INITDIALOG: /* message: initialize dialog box */
\r
6273 /* Initialize the dialog items */
\r
6274 Translate(hDlg, DLG_EditComment);
\r
6275 hwndText = GetDlgItem(hDlg, OPT_CommentText);
\r
6276 SetDlgItemText(hDlg, OPT_CommentText, commentText);
\r
6277 EnableWindow(GetDlgItem(hDlg, OPT_CancelComment), editComment);
\r
6278 EnableWindow(GetDlgItem(hDlg, OPT_ClearComment), editComment);
\r
6279 EnableWindow(GetDlgItem(hDlg, OPT_EditComment), !editComment);
\r
6280 SendMessage(hwndText, EM_SETREADONLY, !editComment, 0);
\r
6281 SetWindowText(hDlg, commentTitle);
\r
6282 if (editComment) {
\r
6283 SetFocus(hwndText);
\r
6285 SetFocus(GetDlgItem(hDlg, IDOK));
\r
6287 SendMessage(GetDlgItem(hDlg, OPT_CommentText),
\r
6288 WM_SETFONT, (WPARAM)font[boardSize][COMMENT_FONT]->hf,
\r
6289 MAKELPARAM(FALSE, 0));
\r
6290 /* Size and position the dialog */
\r
6291 if (!commentDialog) {
\r
6292 commentDialog = hDlg;
\r
6293 flags = SWP_NOZORDER;
\r
6294 GetClientRect(hDlg, &rect);
\r
6295 sizeX = rect.right;
\r
6296 sizeY = rect.bottom;
\r
6297 if (wpComment.x != CW_USEDEFAULT && wpComment.y != CW_USEDEFAULT &&
\r
6298 wpComment.width != CW_USEDEFAULT && wpComment.height != CW_USEDEFAULT) {
\r
6299 WINDOWPLACEMENT wp;
\r
6300 EnsureOnScreen(&wpComment.x, &wpComment.y, 0, 0);
\r
6301 wp.length = sizeof(WINDOWPLACEMENT);
\r
6303 wp.showCmd = SW_SHOW;
\r
6304 wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;
\r
6305 wp.rcNormalPosition.left = wpComment.x;
\r
6306 wp.rcNormalPosition.right = wpComment.x + wpComment.width;
\r
6307 wp.rcNormalPosition.top = wpComment.y;
\r
6308 wp.rcNormalPosition.bottom = wpComment.y + wpComment.height;
\r
6309 SetWindowPlacement(hDlg, &wp);
\r
6311 GetClientRect(hDlg, &rect);
\r
6312 newSizeX = rect.right;
\r
6313 newSizeY = rect.bottom;
\r
6314 ResizeEditPlusButtons(hDlg, hwndText, sizeX, sizeY,
\r
6315 newSizeX, newSizeY);
\r
6320 SendDlgItemMessage( hDlg, OPT_CommentText, EM_SETEVENTMASK, 0, ENM_MOUSEEVENTS | ENM_KEYEVENTS );
\r
6323 case WM_COMMAND: /* message: received a command */
\r
6324 switch (LOWORD(wParam)) {
\r
6326 if (editComment) {
\r
6328 /* Read changed options from the dialog box */
\r
6329 hwndText = GetDlgItem(hDlg, OPT_CommentText);
\r
6330 len = GetWindowTextLength(hwndText);
\r
6331 str = (char *) malloc(len + 1);
\r
6332 GetWindowText(hwndText, str, len + 1);
\r
6341 ReplaceComment(commentIndex, str);
\r
6348 case OPT_CancelComment:
\r
6352 case OPT_ClearComment:
\r
6353 SetDlgItemText(hDlg, OPT_CommentText, "");
\r
6356 case OPT_EditComment:
\r
6357 EditCommentEvent();
\r
6365 case WM_NOTIFY: // [HGM] vari: cloned from whistory.c
\r
6366 if( wParam == OPT_CommentText ) {
\r
6367 MSGFILTER * lpMF = (MSGFILTER *) lParam;
\r
6369 if( lpMF->msg == WM_RBUTTONDOWN && (lpMF->wParam & (MK_CONTROL | MK_SHIFT)) == 0 ||
\r
6370 lpMF->msg == WM_CHAR && lpMF->wParam == '\022' ) {
\r
6374 pt.x = LOWORD( lpMF->lParam );
\r
6375 pt.y = HIWORD( lpMF->lParam );
\r
6377 if(lpMF->msg == WM_CHAR) {
\r
6379 SendDlgItemMessage( hDlg, OPT_CommentText, EM_EXGETSEL, 0, (LPARAM) &sel );
\r
6380 index = sel.cpMin;
\r
6382 index = SendDlgItemMessage( hDlg, OPT_CommentText, EM_CHARFROMPOS, 0, (LPARAM) &pt );
\r
6384 hwndText = GetDlgItem(hDlg, OPT_CommentText); // cloned from above
\r
6385 len = GetWindowTextLength(hwndText);
\r
6386 str = (char *) malloc(len + 1);
\r
6387 GetWindowText(hwndText, str, len + 1);
\r
6388 ReplaceComment(commentIndex, str);
\r
6389 if(commentIndex != currentMove) ToNrEvent(commentIndex);
\r
6390 LoadVariation( index, str ); // [HGM] also does the actual moving to it, now
\r
6393 /* Zap the message for good: apparently, returning non-zero is not enough */
\r
6394 lpMF->msg = WM_USER;
\r
6402 newSizeX = LOWORD(lParam);
\r
6403 newSizeY = HIWORD(lParam);
\r
6404 ResizeEditPlusButtons(hDlg, hwndText, sizeX, sizeY, newSizeX, newSizeY);
\r
6409 case WM_GETMINMAXINFO:
\r
6410 /* Prevent resizing window too small */
\r
6411 mmi = (MINMAXINFO *) lParam;
\r
6412 mmi->ptMinTrackSize.x = 100;
\r
6413 mmi->ptMinTrackSize.y = 100;
\r
6420 EitherCommentPopUp(int index, char *title, char *str, BOOLEAN edit)
\r
6425 CheckMenuItem(GetMenu(hwndMain), IDM_Comment, edit ? MF_CHECKED : MF_UNCHECKED);
\r
6427 if (str == NULL) str = "";
\r
6428 p = (char *) malloc(2 * strlen(str) + 2);
\r
6431 if (*str == '\n') *q++ = '\r';
\r
6435 if (commentText != NULL) free(commentText);
\r
6437 commentIndex = index;
\r
6438 commentTitle = title;
\r
6440 editComment = edit;
\r
6442 if (commentDialog) {
\r
6443 SendMessage(commentDialog, WM_INITDIALOG, 0, 0);
\r
6444 if (!commentUp) ShowWindow(commentDialog, SW_SHOW);
\r
6446 lpProc = MakeProcInstance((FARPROC)CommentDialog, hInst);
\r
6447 CreateDialog(hInst, MAKEINTRESOURCE(DLG_EditComment),
\r
6448 hwndMain, (DLGPROC)lpProc);
\r
6449 FreeProcInstance(lpProc);
\r
6455 /*---------------------------------------------------------------------------*\
\r
6457 * Type-in move dialog functions
\r
6459 \*---------------------------------------------------------------------------*/
\r
6462 TypeInMoveDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
6464 char move[MSG_SIZ];
\r
6467 switch (message) {
\r
6468 case WM_INITDIALOG:
\r
6469 move[0] = (char) lParam;
\r
6470 move[1] = NULLCHAR;
\r
6471 CenterWindowEx(hDlg, GetWindow(hDlg, GW_OWNER), 1 );
\r
6472 Translate(hDlg, DLG_TypeInMove);
\r
6473 hInput = GetDlgItem(hDlg, OPT_Move);
\r
6474 SetWindowText(hInput, move);
\r
6476 SendMessage(hInput, EM_SETSEL, (WPARAM)9999, (LPARAM)9999);
\r
6480 switch (LOWORD(wParam)) {
\r
6483 shiftKey = GetKeyState(VK_SHIFT) < 0; // [HGM] remember last shift status
\r
6484 GetDlgItemText(hDlg, OPT_Move, move, sizeof(move));
\r
6485 TypeInDoneEvent(move);
\r
6486 EndDialog(hDlg, TRUE);
\r
6489 EndDialog(hDlg, FALSE);
\r
6500 PopUpMoveDialog(char firstchar)
\r
6504 lpProc = MakeProcInstance((FARPROC)TypeInMoveDialog, hInst);
\r
6505 DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_TypeInMove),
\r
6506 hwndMain, (DLGPROC)lpProc, (LPARAM)firstchar);
\r
6507 FreeProcInstance(lpProc);
\r
6510 /*---------------------------------------------------------------------------*\
\r
6512 * Type-in name dialog functions
\r
6514 \*---------------------------------------------------------------------------*/
\r
6517 TypeInNameDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
6519 char move[MSG_SIZ];
\r
6522 switch (message) {
\r
6523 case WM_INITDIALOG:
\r
6524 move[0] = (char) lParam;
\r
6525 move[1] = NULLCHAR;
\r
6526 CenterWindowEx(hDlg, GetWindow(hDlg, GW_OWNER), 1 );
\r
6527 Translate(hDlg, DLG_TypeInName);
\r
6528 hInput = GetDlgItem(hDlg, OPT_Name);
\r
6529 SetWindowText(hInput, move);
\r
6531 SendMessage(hInput, EM_SETSEL, (WPARAM)9999, (LPARAM)9999);
\r
6535 switch (LOWORD(wParam)) {
\r
6537 GetDlgItemText(hDlg, OPT_Name, move, sizeof(move));
\r
6538 appData.userName = strdup(move);
\r
6541 if(gameMode == MachinePlaysWhite || gameMode == MachinePlaysBlack) {
\r
6542 snprintf(move, MSG_SIZ, "%s vs. %s", gameInfo.white, gameInfo.black);
\r
6543 DisplayTitle(move);
\r
6547 EndDialog(hDlg, TRUE);
\r
6550 EndDialog(hDlg, FALSE);
\r
6561 PopUpNameDialog(char firstchar)
\r
6565 lpProc = MakeProcInstance((FARPROC)TypeInNameDialog, hInst);
\r
6566 DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_TypeInName),
\r
6567 hwndMain, (DLGPROC)lpProc, (LPARAM)firstchar);
\r
6568 FreeProcInstance(lpProc);
\r
6571 /*---------------------------------------------------------------------------*\
\r
6575 \*---------------------------------------------------------------------------*/
\r
6577 /* Nonmodal error box */
\r
6578 LRESULT CALLBACK ErrorDialog(HWND hDlg, UINT message,
\r
6579 WPARAM wParam, LPARAM lParam);
\r
6582 ErrorPopUp(char *title, char *content)
\r
6586 BOOLEAN modal = hwndMain == NULL;
\r
6604 strncpy(errorTitle, title, sizeof(errorTitle));
\r
6605 errorTitle[sizeof(errorTitle) - 1] = '\0';
\r
6608 MessageBox(NULL, errorMessage, errorTitle, MB_OK|MB_ICONEXCLAMATION);
\r
6610 lpProc = MakeProcInstance((FARPROC)ErrorDialog, hInst);
\r
6611 CreateDialog(hInst, MAKEINTRESOURCE(DLG_Error),
\r
6612 hwndMain, (DLGPROC)lpProc);
\r
6613 FreeProcInstance(lpProc);
\r
6620 if (!appData.popupMoveErrors && moveErrorMessageUp) DisplayMessage("", "");
\r
6621 if (errorDialog == NULL) return;
\r
6622 DestroyWindow(errorDialog);
\r
6623 errorDialog = NULL;
\r
6624 if(errorExitStatus) ExitEvent(errorExitStatus);
\r
6628 ErrorDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
6633 switch (message) {
\r
6634 case WM_INITDIALOG:
\r
6635 GetWindowRect(hDlg, &rChild);
\r
6638 SetWindowPos(hDlg, NULL, rChild.left,
\r
6639 rChild.top + boardRect.top - (rChild.bottom - rChild.top),
\r
6640 0, 0, SWP_NOZORDER|SWP_NOSIZE);
\r
6644 [AS] It seems that the above code wants to move the dialog up in the "caption
\r
6645 area" of the main window, but it uses the dialog height as an hard-coded constant,
\r
6646 and it doesn't work when you resize the dialog.
\r
6647 For now, just give it a default position.
\r
6649 SetWindowPos(hDlg, NULL, boardRect.left+8, boardRect.top+8, 0, 0, SWP_NOZORDER|SWP_NOSIZE);
\r
6650 Translate(hDlg, DLG_Error);
\r
6652 errorDialog = hDlg;
\r
6653 SetWindowText(hDlg, errorTitle);
\r
6654 hwndText = GetDlgItem(hDlg, OPT_ErrorText);
\r
6655 SetDlgItemText(hDlg, OPT_ErrorText, errorMessage);
\r
6659 switch (LOWORD(wParam)) {
\r
6662 if (errorDialog == hDlg) errorDialog = NULL;
\r
6663 DestroyWindow(hDlg);
\r
6675 HWND gothicDialog = NULL;
\r
6678 GothicDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
6682 int height = GetSystemMetrics(SM_CYCAPTION)+GetSystemMetrics(SM_CYFRAME);
\r
6684 switch (message) {
\r
6685 case WM_INITDIALOG:
\r
6686 GetWindowRect(hDlg, &rChild);
\r
6688 SetWindowPos(hDlg, NULL, wpMain.x, wpMain.y-height, wpMain.width, height,
\r
6692 [AS] It seems that the above code wants to move the dialog up in the "caption
\r
6693 area" of the main window, but it uses the dialog height as an hard-coded constant,
\r
6694 and it doesn't work when you resize the dialog.
\r
6695 For now, just give it a default position.
\r
6697 gothicDialog = hDlg;
\r
6698 SetWindowText(hDlg, errorTitle);
\r
6699 hwndText = GetDlgItem(hDlg, OPT_ErrorText);
\r
6700 SetDlgItemText(hDlg, OPT_ErrorText, errorMessage);
\r
6704 switch (LOWORD(wParam)) {
\r
6707 if (errorDialog == hDlg) errorDialog = NULL;
\r
6708 DestroyWindow(hDlg);
\r
6720 GothicPopUp(char *title, VariantClass variant)
\r
6723 static char *lastTitle;
\r
6725 strncpy(errorTitle, title, sizeof(errorTitle));
\r
6726 errorTitle[sizeof(errorTitle) - 1] = '\0';
\r
6728 if(lastTitle != title && gothicDialog != NULL) {
\r
6729 DestroyWindow(gothicDialog);
\r
6730 gothicDialog = NULL;
\r
6732 if(variant != VariantNormal && gothicDialog == NULL) {
\r
6733 title = lastTitle;
\r
6734 lpProc = MakeProcInstance((FARPROC)GothicDialog, hInst);
\r
6735 CreateDialog(hInst, MAKEINTRESOURCE(DLG_Error),
\r
6736 hwndMain, (DLGPROC)lpProc);
\r
6737 FreeProcInstance(lpProc);
\r
6742 /*---------------------------------------------------------------------------*\
\r
6744 * Ics Interaction console functions
\r
6746 \*---------------------------------------------------------------------------*/
\r
6748 #define HISTORY_SIZE 64
\r
6749 static char *history[HISTORY_SIZE];
\r
6750 int histIn = 0, histP = 0;
\r
6753 SaveInHistory(char *cmd)
\r
6755 if (history[histIn] != NULL) {
\r
6756 free(history[histIn]);
\r
6757 history[histIn] = NULL;
\r
6759 if (*cmd == NULLCHAR) return;
\r
6760 history[histIn] = StrSave(cmd);
\r
6761 histIn = (histIn + 1) % HISTORY_SIZE;
\r
6762 if (history[histIn] != NULL) {
\r
6763 free(history[histIn]);
\r
6764 history[histIn] = NULL;
\r
6770 PrevInHistory(char *cmd)
\r
6773 if (histP == histIn) {
\r
6774 if (history[histIn] != NULL) free(history[histIn]);
\r
6775 history[histIn] = StrSave(cmd);
\r
6777 newhp = (histP - 1 + HISTORY_SIZE) % HISTORY_SIZE;
\r
6778 if (newhp == histIn || history[newhp] == NULL) return NULL;
\r
6780 return history[histP];
\r
6786 if (histP == histIn) return NULL;
\r
6787 histP = (histP + 1) % HISTORY_SIZE;
\r
6788 return history[histP];
\r
6792 LoadIcsTextMenu(IcsTextMenuEntry *e)
\r
6796 hmenu = LoadMenu(hInst, "TextMenu");
\r
6797 h = GetSubMenu(hmenu, 0);
\r
6799 if (strcmp(e->item, "-") == 0) {
\r
6800 AppendMenu(h, MF_SEPARATOR, 0, 0);
\r
6801 } else { // [HGM] re-written a bit to use only one AppendMenu call for both cases (| or no |)
\r
6802 int flags = MF_STRING, j = 0;
\r
6803 if (e->item[0] == '|') {
\r
6804 flags |= MF_MENUBARBREAK;
\r
6807 if(!strcmp(e->command, "none")) flags |= MF_GRAYED; // [HGM] chatclick: provide inactive dummy
\r
6808 AppendMenu(h, flags, IDM_CommandX + i, e->item + j);
\r
6816 WNDPROC consoleTextWindowProc;
\r
6819 CommandX(HWND hwnd, char *command, BOOLEAN getname, BOOLEAN immediate)
\r
6821 char buf[MSG_SIZ], name[MSG_SIZ];
\r
6822 HWND hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
6826 SetWindowText(hInput, command);
\r
6828 SendMessage(hInput, WM_CHAR, '\r', 0);
\r
6830 sel.cpMin = 999999;
\r
6831 sel.cpMax = 999999;
\r
6832 SendMessage(hInput, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
6837 SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);
\r
6838 if (sel.cpMin == sel.cpMax) {
\r
6839 /* Expand to surrounding word */
\r
6842 tr.chrg.cpMax = sel.cpMin;
\r
6843 tr.chrg.cpMin = --sel.cpMin;
\r
6844 if (sel.cpMin < 0) break;
\r
6845 tr.lpstrText = name;
\r
6846 SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr);
\r
6847 } while (isalpha(name[0]) || isdigit(name[0]) || name[0] == '-');
\r
6851 tr.chrg.cpMin = sel.cpMax;
\r
6852 tr.chrg.cpMax = ++sel.cpMax;
\r
6853 tr.lpstrText = name;
\r
6854 if (SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr) < 1) break;
\r
6855 } while (isalpha(name[0]) || isdigit(name[0]) || name[0] == '-');
\r
6858 if (sel.cpMax == sel.cpMin || sel.cpMax - sel.cpMin > MSG_SIZ/2) {
\r
6859 MessageBeep(MB_ICONEXCLAMATION);
\r
6863 tr.lpstrText = name;
\r
6864 SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr);
\r
6866 if (sel.cpMax - sel.cpMin > MSG_SIZ/2) {
\r
6867 MessageBeep(MB_ICONEXCLAMATION);
\r
6870 SendMessage(hwnd, EM_GETSELTEXT, 0, (LPARAM) name);
\r
6873 if(strstr(command, "%s")) snprintf(buf, MSG_SIZ, command, name); else
\r
6874 snprintf(buf, MSG_SIZ, "%s %s", command, name);
\r
6875 SetWindowText(hInput, buf);
\r
6876 SendMessage(hInput, WM_CHAR, '\r', 0);
\r
6878 if(!strcmp(command, "chat")) { ChatPopUp(name); return; }
\r
6879 snprintf(buf, MSG_SIZ, "%s %s ", command, name); /* trailing space */
\r
6880 SetWindowText(hInput, buf);
\r
6881 sel.cpMin = 999999;
\r
6882 sel.cpMax = 999999;
\r
6883 SendMessage(hInput, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
6889 ConsoleTextSubclass(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
\r
6894 switch (message) {
\r
6896 if (!(GetKeyState(VK_CONTROL) & ~1)) break;
\r
6897 if(wParam=='R') return 0;
\r
6900 SendMessage(hwnd, EM_LINESCROLL, 0, -999999);
\r
6903 sel.cpMin = 999999;
\r
6904 sel.cpMax = 999999;
\r
6905 SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
6906 SendMessage(hwnd, EM_SCROLLCARET, 0, 0);
\r
6911 if(wParam != '\022') {
\r
6912 if (wParam == '\t') {
\r
6913 if (GetKeyState(VK_SHIFT) < 0) {
\r
6915 if (IsIconic(hwndMain)) ShowWindow(hwndMain, SW_RESTORE);
\r
6916 if (buttonDesc[0].hwnd) {
\r
6917 SetFocus(buttonDesc[0].hwnd);
\r
6919 SetFocus(hwndMain);
\r
6923 SetFocus(GetDlgItem(hwndConsole, OPT_ConsoleInput));
\r
6926 hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
6927 JAWS_DELETE( SetFocus(hInput); )
\r
6928 SendMessage(hInput, message, wParam, lParam);
\r
6931 } // [HGM] navigate: for Ctrl+R, flow into next case (moved up here) to summon up menu
\r
6933 case WM_RBUTTONDOWN:
\r
6934 if (!(GetKeyState(VK_SHIFT) & ~1)) {
\r
6935 /* Move selection here if it was empty */
\r
6937 pt.x = LOWORD(lParam);
\r
6938 pt.y = HIWORD(lParam);
\r
6939 SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);
\r
6940 if (sel.cpMin == sel.cpMax) {
\r
6941 if(lParam != -1) sel.cpMin = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&pt); /*doc is wrong*/
\r
6942 sel.cpMax = sel.cpMin;
\r
6943 SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
6945 SendMessage(hwnd, EM_HIDESELECTION, FALSE, FALSE);
\r
6946 { // [HGM] chatclick: code moved here from WM_RBUTTONUP case, to have menu appear on down-click
\r
6948 HMENU hmenu = LoadIcsTextMenu(icsTextMenuEntry);
\r
6949 SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);
\r
6950 if (sel.cpMin == sel.cpMax) {
\r
6951 EnableMenuItem(hmenu, IDM_Copy, MF_BYCOMMAND|MF_GRAYED);
\r
6952 EnableMenuItem(hmenu, IDM_QuickPaste, MF_BYCOMMAND|MF_GRAYED);
\r
6954 if (!IsClipboardFormatAvailable(CF_TEXT)) {
\r
6955 EnableMenuItem(hmenu, IDM_Paste, MF_BYCOMMAND|MF_GRAYED);
\r
6957 pt.x = LOWORD(lParam)-30; // [HGM] chatclick: make menu pop up with pointer above upper-right item
\r
6958 pt.y = HIWORD(lParam)-10; // make it appear as if mouse moved there, so it will be selected on up-click
\r
6959 PostMessage(hwnd, WM_MOUSEMOVE, wParam, lParam+5);
\r
6960 MenuPopup(hwnd, pt, hmenu, -1);
\r
6964 case WM_RBUTTONUP:
\r
6965 if (GetKeyState(VK_SHIFT) & ~1) {
\r
6966 SendDlgItemMessage(hwndConsole, OPT_ConsoleText,
\r
6967 WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);
\r
6971 hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
6973 return SendMessage(hInput, message, wParam, lParam);
\r
6974 case WM_MBUTTONDOWN:
\r
6975 return SendMessage(hwnd, WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);
\r
6977 switch (LOWORD(wParam)) {
\r
6978 case IDM_QuickPaste:
\r
6980 SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);
\r
6981 if (sel.cpMin == sel.cpMax) {
\r
6982 MessageBeep(MB_ICONEXCLAMATION);
\r
6985 SendMessage(hwnd, WM_COPY, 0, 0);
\r
6986 hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
6987 SendMessage(hInput, WM_PASTE, 0, 0);
\r
6992 SendMessage(hwnd, WM_CUT, 0, 0);
\r
6995 SendMessage(hwnd, WM_PASTE, 0, 0);
\r
6998 SendMessage(hwnd, WM_COPY, 0, 0);
\r
7002 int i = LOWORD(wParam) - IDM_CommandX;
\r
7003 if (i >= 0 && i < ICS_TEXT_MENU_SIZE &&
\r
7004 icsTextMenuEntry[i].command != NULL) {
\r
7005 CommandX(hwnd, icsTextMenuEntry[i].command,
\r
7006 icsTextMenuEntry[i].getname,
\r
7007 icsTextMenuEntry[i].immediate);
\r
7015 return (*consoleTextWindowProc)(hwnd, message, wParam, lParam);
\r
7018 WNDPROC consoleInputWindowProc;
\r
7021 ConsoleInputSubclass(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
\r
7023 char buf[MSG_SIZ];
\r
7025 static BOOL sendNextChar = FALSE;
\r
7026 static BOOL quoteNextChar = FALSE;
\r
7027 InputSource *is = consoleInputSource;
\r
7031 switch (message) {
\r
7033 if (!appData.localLineEditing || sendNextChar) {
\r
7034 is->buf[0] = (CHAR) wParam;
\r
7036 SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);
\r
7037 sendNextChar = FALSE;
\r
7040 if (quoteNextChar) {
\r
7041 buf[0] = (char) wParam;
\r
7042 buf[1] = NULLCHAR;
\r
7043 SendMessage(hwnd, EM_REPLACESEL, TRUE, (LPARAM) buf);
\r
7044 quoteNextChar = FALSE;
\r
7048 case '\r': /* Enter key */
\r
7049 is->count = GetWindowText(hwnd, is->buf, INPUT_SOURCE_BUF_SIZE-1);
\r
7050 if (consoleEcho) SaveInHistory(is->buf);
\r
7051 is->buf[is->count++] = '\n';
\r
7052 is->buf[is->count] = NULLCHAR;
\r
7053 SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);
\r
7054 if (consoleEcho) {
\r
7055 ConsoleOutput(is->buf, is->count, TRUE);
\r
7056 } else if (appData.localLineEditing) {
\r
7057 ConsoleOutput("\n", 1, TRUE);
\r
7060 case '\033': /* Escape key */
\r
7061 SetWindowText(hwnd, "");
\r
7062 cf.cbSize = sizeof(CHARFORMAT);
\r
7063 cf.dwMask = CFM_COLOR|CFM_BOLD|CFM_ITALIC|CFM_UNDERLINE|CFM_STRIKEOUT;
\r
7064 if (consoleEcho) {
\r
7065 cf.crTextColor = textAttribs[ColorNormal].color;
\r
7067 cf.crTextColor = COLOR_ECHOOFF;
\r
7069 cf.dwEffects = textAttribs[ColorNormal].effects;
\r
7070 SendMessage(hwnd, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf);
\r
7072 case '\t': /* Tab key */
\r
7073 if (GetKeyState(VK_SHIFT) < 0) {
\r
7075 SetFocus(GetDlgItem(hwndConsole, OPT_ConsoleText));
\r
7078 if (IsIconic(hwndMain)) ShowWindow(hwndMain, SW_RESTORE);
\r
7079 if (buttonDesc[0].hwnd) {
\r
7080 SetFocus(buttonDesc[0].hwnd);
\r
7082 SetFocus(hwndMain);
\r
7086 case '\023': /* Ctrl+S */
\r
7087 sendNextChar = TRUE;
\r
7089 case '\021': /* Ctrl+Q */
\r
7090 quoteNextChar = TRUE;
\r
7100 GetWindowText(hwnd, buf, MSG_SIZ);
\r
7101 p = PrevInHistory(buf);
\r
7103 SetWindowText(hwnd, p);
\r
7104 sel.cpMin = 999999;
\r
7105 sel.cpMax = 999999;
\r
7106 SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
7111 p = NextInHistory();
\r
7113 SetWindowText(hwnd, p);
\r
7114 sel.cpMin = 999999;
\r
7115 sel.cpMax = 999999;
\r
7116 SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
7122 if (!(GetKeyState(VK_CONTROL) & ~1)) break;
\r
7126 SendDlgItemMessage(hwndConsole, OPT_ConsoleText, message, wParam, lParam);
\r
7130 case WM_MBUTTONDOWN:
\r
7131 SendDlgItemMessage(hwndConsole, OPT_ConsoleText,
\r
7132 WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);
\r
7134 case WM_RBUTTONUP:
\r
7135 if (GetKeyState(VK_SHIFT) & ~1) {
\r
7136 SendDlgItemMessage(hwndConsole, OPT_ConsoleText,
\r
7137 WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);
\r
7141 hmenu = LoadMenu(hInst, "InputMenu");
\r
7142 SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);
\r
7143 if (sel.cpMin == sel.cpMax) {
\r
7144 EnableMenuItem(hmenu, IDM_Copy, MF_BYCOMMAND|MF_GRAYED);
\r
7145 EnableMenuItem(hmenu, IDM_Cut, MF_BYCOMMAND|MF_GRAYED);
\r
7147 if (!IsClipboardFormatAvailable(CF_TEXT)) {
\r
7148 EnableMenuItem(hmenu, IDM_Paste, MF_BYCOMMAND|MF_GRAYED);
\r
7150 pt.x = LOWORD(lParam);
\r
7151 pt.y = HIWORD(lParam);
\r
7152 MenuPopup(hwnd, pt, hmenu, -1);
\r
7156 switch (LOWORD(wParam)) {
\r
7158 SendMessage(hwnd, EM_UNDO, 0, 0);
\r
7160 case IDM_SelectAll:
\r
7162 sel.cpMax = -1; /*999999?*/
\r
7163 SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
7166 SendMessage(hwnd, WM_CUT, 0, 0);
\r
7169 SendMessage(hwnd, WM_PASTE, 0, 0);
\r
7172 SendMessage(hwnd, WM_COPY, 0, 0);
\r
7177 return (*consoleInputWindowProc)(hwnd, message, wParam, lParam);
\r
7180 #define CO_MAX 100000
\r
7181 #define CO_TRIM 1000
\r
7184 ConsoleWndProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
7186 static SnapData sd;
\r
7187 HWND hText, hInput;
\r
7189 static int sizeX, sizeY;
\r
7190 int newSizeX, newSizeY;
\r
7194 hText = GetDlgItem(hDlg, OPT_ConsoleText);
\r
7195 hInput = GetDlgItem(hDlg, OPT_ConsoleInput);
\r
7197 switch (message) {
\r
7199 if (((NMHDR*)lParam)->code == EN_LINK)
\r
7201 ENLINK *pLink = (ENLINK*)lParam;
\r
7202 if (pLink->msg == WM_LBUTTONUP)
\r
7206 tr.chrg = pLink->chrg;
\r
7207 tr.lpstrText = malloc(1+tr.chrg.cpMax-tr.chrg.cpMin);
\r
7208 SendMessage(hText, EM_GETTEXTRANGE, 0, (LPARAM)&tr);
\r
7209 ShellExecute(NULL, "open", tr.lpstrText, NULL, NULL, SW_SHOW);
\r
7210 free(tr.lpstrText);
\r
7214 case WM_INITDIALOG: /* message: initialize dialog box */
\r
7215 hwndConsole = hDlg;
\r
7217 consoleTextWindowProc = (WNDPROC)
\r
7218 SetWindowLongPtr(hText, GWLP_WNDPROC, (LONG_PTR) ConsoleTextSubclass);
\r
7219 SendMessage(hText, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);
\r
7220 consoleInputWindowProc = (WNDPROC)
\r
7221 SetWindowLongPtr(hInput, GWLP_WNDPROC, (LONG_PTR) ConsoleInputSubclass);
\r
7222 SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);
\r
7223 Colorize(ColorNormal, TRUE);
\r
7224 SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &consoleCF);
\r
7225 ChangedConsoleFont();
\r
7226 GetClientRect(hDlg, &rect);
\r
7227 sizeX = rect.right;
\r
7228 sizeY = rect.bottom;
\r
7229 if (wpConsole.x != CW_USEDEFAULT && wpConsole.y != CW_USEDEFAULT &&
\r
7230 wpConsole.width != CW_USEDEFAULT && wpConsole.height != CW_USEDEFAULT) {
\r
7231 WINDOWPLACEMENT wp;
\r
7232 EnsureOnScreen(&wpConsole.x, &wpConsole.y, 0, 0);
\r
7233 wp.length = sizeof(WINDOWPLACEMENT);
\r
7235 wp.showCmd = SW_SHOW;
\r
7236 wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;
\r
7237 wp.rcNormalPosition.left = wpConsole.x;
\r
7238 wp.rcNormalPosition.right = wpConsole.x + wpConsole.width;
\r
7239 wp.rcNormalPosition.top = wpConsole.y;
\r
7240 wp.rcNormalPosition.bottom = wpConsole.y + wpConsole.height;
\r
7241 SetWindowPlacement(hDlg, &wp);
\r
7244 // [HGM] Chessknight's change 2004-07-13
\r
7245 else { /* Determine Defaults */
\r
7246 WINDOWPLACEMENT wp;
\r
7247 wpConsole.x = wpMain.width + 1;
\r
7248 wpConsole.y = wpMain.y;
\r
7249 wpConsole.width = screenWidth - wpMain.width;
\r
7250 wpConsole.height = wpMain.height;
\r
7251 EnsureOnScreen(&wpConsole.x, &wpConsole.y, 0, 0);
\r
7252 wp.length = sizeof(WINDOWPLACEMENT);
\r
7254 wp.showCmd = SW_SHOW;
\r
7255 wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;
\r
7256 wp.rcNormalPosition.left = wpConsole.x;
\r
7257 wp.rcNormalPosition.right = wpConsole.x + wpConsole.width;
\r
7258 wp.rcNormalPosition.top = wpConsole.y;
\r
7259 wp.rcNormalPosition.bottom = wpConsole.y + wpConsole.height;
\r
7260 SetWindowPlacement(hDlg, &wp);
\r
7263 // Allow hText to highlight URLs and send notifications on them
\r
7264 wMask = (WORD) SendMessage(hText, EM_GETEVENTMASK, 0, 0L);
\r
7265 SendMessage(hText, EM_SETEVENTMASK, 0, wMask | ENM_LINK);
\r
7266 SendMessage(hText, EM_AUTOURLDETECT, TRUE, 0L);
\r
7267 SetWindowLongPtr(hText, GWLP_USERDATA, 79); // initialize the text window's width
\r
7281 if (IsIconic(hDlg)) break;
\r
7282 newSizeX = LOWORD(lParam);
\r
7283 newSizeY = HIWORD(lParam);
\r
7284 if (sizeX != newSizeX || sizeY != newSizeY) {
\r
7285 RECT rectText, rectInput;
\r
7287 int newTextHeight, newTextWidth;
\r
7288 GetWindowRect(hText, &rectText);
\r
7289 newTextWidth = rectText.right - rectText.left + newSizeX - sizeX;
\r
7290 newTextHeight = rectText.bottom - rectText.top + newSizeY - sizeY;
\r
7291 if (newTextHeight < 0) {
\r
7292 newSizeY += -newTextHeight;
\r
7293 newTextHeight = 0;
\r
7295 SetWindowPos(hText, NULL, 0, 0,
\r
7296 newTextWidth, newTextHeight, SWP_NOZORDER|SWP_NOMOVE);
\r
7297 GetWindowRect(hInput, &rectInput); /* gives screen coords */
\r
7298 pt.x = rectInput.left;
\r
7299 pt.y = rectInput.top + newSizeY - sizeY;
\r
7300 ScreenToClient(hDlg, &pt);
\r
7301 SetWindowPos(hInput, NULL,
\r
7302 pt.x, pt.y, /* needs client coords */
\r
7303 rectInput.right - rectInput.left + newSizeX - sizeX,
\r
7304 rectInput.bottom - rectInput.top, SWP_NOZORDER);
\r
7310 case WM_GETMINMAXINFO:
\r
7311 /* Prevent resizing window too small */
\r
7312 mmi = (MINMAXINFO *) lParam;
\r
7313 mmi->ptMinTrackSize.x = 100;
\r
7314 mmi->ptMinTrackSize.y = 100;
\r
7317 /* [AS] Snapping */
\r
7318 case WM_ENTERSIZEMOVE:
\r
7319 return OnEnterSizeMove( &sd, hDlg, wParam, lParam );
\r
7322 return OnSizing( &sd, hDlg, wParam, lParam );
\r
7325 return OnMoving( &sd, hDlg, wParam, lParam );
\r
7327 case WM_EXITSIZEMOVE:
\r
7328 UpdateICSWidth(hText);
\r
7329 return OnExitSizeMove( &sd, hDlg, wParam, lParam );
\r
7332 return DefWindowProc(hDlg, message, wParam, lParam);
\r
7340 if (hwndConsole) return;
\r
7341 hCons = CreateDialog(hInst, szConsoleName, 0, NULL);
\r
7342 SendMessage(hCons, WM_INITDIALOG, 0, 0);
\r
7347 ConsoleOutput(char* data, int length, int forceVisible)
\r
7352 char buf[CO_MAX+1];
\r
7355 static int delayLF = 0;
\r
7356 CHARRANGE savesel, sel;
\r
7358 if (hwndConsole == NULL || length > CO_MAX-100 || length == 0) return;
\r
7366 while (length--) {
\r
7374 } else if (*p == '\007') {
\r
7375 MyPlaySound(&sounds[(int)SoundBell]);
\r
7382 hText = GetDlgItem(hwndConsole, OPT_ConsoleText);
\r
7383 SendMessage(hText, EM_HIDESELECTION, TRUE, FALSE);
\r
7384 /* Save current selection */
\r
7385 SendMessage(hText, EM_EXGETSEL, 0, (LPARAM)&savesel);
\r
7386 exlen = GetWindowTextLength(hText);
\r
7387 /* Find out whether current end of text is visible */
\r
7388 SendMessage(hText, EM_GETRECT, 0, (LPARAM) &rect);
\r
7389 SendMessage(hText, EM_POSFROMCHAR, (WPARAM) &pEnd, exlen);
\r
7390 /* Trim existing text if it's too long */
\r
7391 if (exlen + (q - buf) > CO_MAX) {
\r
7392 trim = (CO_TRIM > (q - buf)) ? CO_TRIM : (q - buf);
\r
7395 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
7396 SendMessage(hText, EM_REPLACESEL, 0, (LPARAM)"");
\r
7398 savesel.cpMin -= trim;
\r
7399 savesel.cpMax -= trim;
\r
7400 if (exlen < 0) exlen = 0;
\r
7401 if (savesel.cpMin < 0) savesel.cpMin = 0;
\r
7402 if (savesel.cpMax < savesel.cpMin) savesel.cpMax = savesel.cpMin;
\r
7404 /* Append the new text */
\r
7405 sel.cpMin = exlen;
\r
7406 sel.cpMax = exlen;
\r
7407 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
7408 SendMessage(hText, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&consoleCF);
\r
7409 SendMessage(hText, EM_REPLACESEL, 0, (LPARAM) buf);
\r
7410 if (forceVisible || exlen == 0 ||
\r
7411 (rect.left <= pEnd.x && pEnd.x < rect.right &&
\r
7412 rect.top <= pEnd.y && pEnd.y < rect.bottom)) {
\r
7413 /* Scroll to make new end of text visible if old end of text
\r
7414 was visible or new text is an echo of user typein */
\r
7415 sel.cpMin = 9999999;
\r
7416 sel.cpMax = 9999999;
\r
7417 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
7418 SendMessage(hText, EM_HIDESELECTION, FALSE, FALSE);
\r
7419 SendMessage(hText, EM_SCROLLCARET, 0, 0);
\r
7420 SendMessage(hText, EM_HIDESELECTION, TRUE, FALSE);
\r
7422 if (savesel.cpMax == exlen || forceVisible) {
\r
7423 /* Move insert point to new end of text if it was at the old
\r
7424 end of text or if the new text is an echo of user typein */
\r
7425 sel.cpMin = 9999999;
\r
7426 sel.cpMax = 9999999;
\r
7427 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
7429 /* Restore previous selection */
\r
7430 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&savesel);
\r
7432 SendMessage(hText, EM_HIDESELECTION, FALSE, FALSE);
\r
7439 DisplayHoldingsCount(HDC hdc, int x, int y, int rightAlign, int copyNumber)
\r
7443 COLORREF oldFg, oldBg;
\r
7447 if(copyNumber > 1)
\r
7448 snprintf(buf, sizeof(buf)/sizeof(buf[0]),"%d", copyNumber); else buf[0] = 0;
\r
7450 oldFg = SetTextColor(hdc, RGB(255, 255, 255)); /* white */
\r
7451 oldBg = SetBkColor(hdc, RGB(0, 0, 0)); /* black */
\r
7452 oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);
\r
7455 rect.right = x + squareSize;
\r
7457 rect.bottom = y + squareSize;
\r
7460 ExtTextOut(hdc, x + MESSAGE_LINE_LEFTMARGIN
\r
7461 + (rightAlign ? (squareSize*2)/3 : 0),
\r
7462 y, ETO_CLIPPED|ETO_OPAQUE,
\r
7463 &rect, str, strlen(str), NULL);
\r
7465 (void) SetTextColor(hdc, oldFg);
\r
7466 (void) SetBkColor(hdc, oldBg);
\r
7467 (void) SelectObject(hdc, oldFont);
\r
7471 DisplayAClock(HDC hdc, int timeRemaining, int highlight,
\r
7472 RECT *rect, char *color, char *flagFell)
\r
7476 COLORREF oldFg, oldBg;
\r
7479 if (twoBoards && partnerUp) return;
\r
7480 if (appData.clockMode) {
\r
7482 snprintf(buf, sizeof(buf)/sizeof(buf[0]), "%c %s %s", color[0], TimeString(timeRemaining), flagFell);
\r
7484 snprintf(buf, sizeof(buf)/sizeof(buf[0]), "%s:%c%s %s", color, (logoHeight>0 ? 0 : ' '), TimeString(timeRemaining), flagFell);
\r
7491 oldFg = SetTextColor(hdc, RGB(255, 255, 255)); /* white */
\r
7492 oldBg = SetBkColor(hdc, RGB(0, 0, 0)); /* black */
\r
7494 oldFg = SetTextColor(hdc, RGB(0, 0, 0)); /* black */
\r
7495 oldBg = SetBkColor(hdc, RGB(255, 255, 255)); /* white */
\r
7497 oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);
\r
7501 ExtTextOut(hdc, rect->left + MESSAGE_LINE_LEFTMARGIN,
\r
7502 rect->top, ETO_CLIPPED|ETO_OPAQUE,
\r
7503 rect, str, strlen(str), NULL);
\r
7504 if(logoHeight > 0 && appData.clockMode) {
\r
7506 str += strlen(color)+2;
\r
7507 r.top = rect->top + logoHeight/2;
\r
7508 r.left = rect->left;
\r
7509 r.right = rect->right;
\r
7510 r.bottom = rect->bottom;
\r
7511 ExtTextOut(hdc, rect->left + MESSAGE_LINE_LEFTMARGIN,
\r
7512 r.top, ETO_CLIPPED|ETO_OPAQUE,
\r
7513 &r, str, strlen(str), NULL);
\r
7515 (void) SetTextColor(hdc, oldFg);
\r
7516 (void) SetBkColor(hdc, oldBg);
\r
7517 (void) SelectObject(hdc, oldFont);
\r
7522 DoReadFile(HANDLE hFile, char *buf, int count, DWORD *outCount,
\r
7528 if( count <= 0 ) {
\r
7529 if (appData.debugMode) {
\r
7530 fprintf( debugFP, "DoReadFile: trying to read past end of buffer, overflow = %d\n", count );
\r
7533 return ERROR_INVALID_USER_BUFFER;
\r
7536 ResetEvent(ovl->hEvent);
\r
7537 ovl->Offset = ovl->OffsetHigh = 0;
\r
7538 ok = ReadFile(hFile, buf, count, outCount, ovl);
\r
7542 err = GetLastError();
\r
7543 if (err == ERROR_IO_PENDING) {
\r
7544 ok = GetOverlappedResult(hFile, ovl, outCount, TRUE);
\r
7548 err = GetLastError();
\r
7555 DoWriteFile(HANDLE hFile, char *buf, int count, DWORD *outCount,
\r
7560 ResetEvent(ovl->hEvent);
\r
7561 ovl->Offset = ovl->OffsetHigh = 0;
\r
7562 ok = WriteFile(hFile, buf, count, outCount, ovl);
\r
7566 err = GetLastError();
\r
7567 if (err == ERROR_IO_PENDING) {
\r
7568 ok = GetOverlappedResult(hFile, ovl, outCount, TRUE);
\r
7572 err = GetLastError();
\r
7578 /* [AS] If input is line by line and a line exceed the buffer size, force an error */
\r
7579 void CheckForInputBufferFull( InputSource * is )
\r
7581 if( is->lineByLine && (is->next - is->buf) >= INPUT_SOURCE_BUF_SIZE ) {
\r
7582 /* Look for end of line */
\r
7583 char * p = is->buf;
\r
7585 while( p < is->next && *p != '\n' ) {
\r
7589 if( p >= is->next ) {
\r
7590 if (appData.debugMode) {
\r
7591 fprintf( debugFP, "Input line exceeded buffer size (source id=%lu)\n", is->id );
\r
7594 is->error = ERROR_BROKEN_PIPE; /* [AS] Just any non-successful code! */
\r
7595 is->count = (DWORD) -1;
\r
7596 is->next = is->buf;
\r
7602 InputThread(LPVOID arg)
\r
7607 is = (InputSource *) arg;
\r
7608 ovl.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
\r
7609 ovl.Internal = ovl.InternalHigh = ovl.Offset = ovl.OffsetHigh = 0;
\r
7610 while (is->hThread != NULL) {
\r
7611 is->error = DoReadFile(is->hFile, is->next,
\r
7612 INPUT_SOURCE_BUF_SIZE - (is->next - is->buf),
\r
7613 &is->count, &ovl);
\r
7614 if (is->error == NO_ERROR) {
\r
7615 is->next += is->count;
\r
7617 if (is->error == ERROR_BROKEN_PIPE) {
\r
7618 /* Correct for MS brain damage. EOF reading a pipe is not an error. */
\r
7621 is->count = (DWORD) -1;
\r
7622 /* [AS] The (is->count <= 0) check below is not useful for unsigned values! */
\r
7627 CheckForInputBufferFull( is );
\r
7629 SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);
\r
7631 if( is->count == ((DWORD) -1) ) break; /* [AS] */
\r
7633 if (is->count <= 0) break; /* Quit on EOF or error */
\r
7636 CloseHandle(ovl.hEvent);
\r
7637 CloseHandle(is->hFile);
\r
7639 if (appData.debugMode) {
\r
7640 fprintf( debugFP, "Input thread terminated (id=%lu, error=%d, count=%ld)\n", is->id, is->error, is->count );
\r
7647 /* Windows 95 beta 2 won't let you do overlapped i/o on a console or pipe */
\r
7649 NonOvlInputThread(LPVOID arg)
\r
7656 is = (InputSource *) arg;
\r
7657 while (is->hThread != NULL) {
\r
7658 is->error = ReadFile(is->hFile, is->next,
\r
7659 INPUT_SOURCE_BUF_SIZE - (is->next - is->buf),
\r
7660 &is->count, NULL) ? NO_ERROR : GetLastError();
\r
7661 if (is->error == NO_ERROR) {
\r
7662 /* Change CRLF to LF */
\r
7663 if (is->next > is->buf) {
\r
7665 i = is->count + 1;
\r
7673 if (prev == '\r' && *p == '\n') {
\r
7685 if (is->error == ERROR_BROKEN_PIPE) {
\r
7686 /* Correct for MS brain damage. EOF reading a pipe is not an error. */
\r
7689 is->count = (DWORD) -1;
\r
7693 CheckForInputBufferFull( is );
\r
7695 SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);
\r
7697 if( is->count == ((DWORD) -1) ) break; /* [AS] */
\r
7699 if (is->count < 0) break; /* Quit on error */
\r
7701 CloseHandle(is->hFile);
\r
7706 SocketInputThread(LPVOID arg)
\r
7710 is = (InputSource *) arg;
\r
7711 while (is->hThread != NULL) {
\r
7712 is->count = recv(is->sock, is->buf, INPUT_SOURCE_BUF_SIZE, 0);
\r
7713 if ((int)is->count == SOCKET_ERROR) {
\r
7714 is->count = (DWORD) -1;
\r
7715 is->error = WSAGetLastError();
\r
7717 is->error = NO_ERROR;
\r
7718 is->next += is->count;
\r
7719 if (is->count == 0 && is->second == is) {
\r
7720 /* End of file on stderr; quit with no message */
\r
7724 SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);
\r
7726 if( is->count == ((DWORD) -1) ) break; /* [AS] */
\r
7728 if (is->count <= 0) break; /* Quit on EOF or error */
\r
7734 InputEvent(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
\r
7738 is = (InputSource *) lParam;
\r
7739 if (is->lineByLine) {
\r
7740 /* Feed in lines one by one */
\r
7741 char *p = is->buf;
\r
7743 while (q < is->next) {
\r
7744 if (*q++ == '\n') {
\r
7745 (is->func)(is, is->closure, p, q - p, NO_ERROR);
\r
7750 /* Move any partial line to the start of the buffer */
\r
7752 while (p < is->next) {
\r
7757 if (is->error != NO_ERROR || is->count == 0) {
\r
7758 /* Notify backend of the error. Note: If there was a partial
\r
7759 line at the end, it is not flushed through. */
\r
7760 (is->func)(is, is->closure, is->buf, is->count, is->error);
\r
7763 /* Feed in the whole chunk of input at once */
\r
7764 (is->func)(is, is->closure, is->buf, is->count, is->error);
\r
7765 is->next = is->buf;
\r
7769 /*---------------------------------------------------------------------------*\
\r
7771 * Menu enables. Used when setting various modes.
\r
7773 \*---------------------------------------------------------------------------*/
\r
7781 GreyRevert(Boolean grey)
\r
7782 { // [HGM] vari: for retracting variations in local mode
\r
7783 HMENU hmenu = GetMenu(hwndMain);
\r
7784 EnableMenuItem(hmenu, IDM_Revert, MF_BYCOMMAND|(grey ? MF_GRAYED : MF_ENABLED));
\r
7785 EnableMenuItem(hmenu, IDM_Annotate, MF_BYCOMMAND|(grey ? MF_GRAYED : MF_ENABLED));
\r
7789 SetMenuEnables(HMENU hmenu, Enables *enab)
\r
7791 while (enab->item > 0) {
\r
7792 (void) EnableMenuItem(hmenu, enab->item, enab->flags);
\r
7797 Enables gnuEnables[] = {
\r
7798 { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },
\r
7799 { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },
\r
7800 { IDM_IcsClient, MF_BYCOMMAND|MF_GRAYED },
\r
7801 { IDM_Accept, MF_BYCOMMAND|MF_GRAYED },
\r
7802 { IDM_Decline, MF_BYCOMMAND|MF_GRAYED },
\r
7803 { IDM_Rematch, MF_BYCOMMAND|MF_GRAYED },
\r
7804 { IDM_Adjourn, MF_BYCOMMAND|MF_GRAYED },
\r
7805 { IDM_StopExamining, MF_BYCOMMAND|MF_GRAYED },
\r
7806 { IDM_StopObserving, MF_BYCOMMAND|MF_GRAYED },
\r
7807 { IDM_Upload, MF_BYCOMMAND|MF_GRAYED },
\r
7808 { IDM_Revert, MF_BYCOMMAND|MF_GRAYED },
\r
7809 { IDM_Annotate, MF_BYCOMMAND|MF_GRAYED },
\r
7810 { IDM_NewChat, MF_BYCOMMAND|MF_GRAYED },
\r
7812 // Needed to switch from ncp to GNU mode on Engine Load
\r
7813 { ACTION_POS, MF_BYPOSITION|MF_ENABLED },
\r
7814 { IDM_MachineWhite, MF_BYCOMMAND|MF_ENABLED },
\r
7815 { IDM_MachineBlack, MF_BYCOMMAND|MF_ENABLED },
\r
7816 { IDM_TwoMachines, MF_BYCOMMAND|MF_ENABLED },
\r
7817 { IDM_Match, MF_BYCOMMAND|MF_ENABLED },
\r
7818 { IDM_AnalysisMode, MF_BYCOMMAND|MF_ENABLED },
\r
7819 { IDM_AnalyzeFile, MF_BYCOMMAND|MF_ENABLED },
\r
7820 { IDM_Engine1Options, MF_BYCOMMAND|MF_ENABLED },
\r
7821 { IDM_Engine2Options, MF_BYCOMMAND|MF_ENABLED },
\r
7822 { IDM_TimeControl, MF_BYCOMMAND|MF_ENABLED },
\r
7823 { IDM_RetractMove, MF_BYCOMMAND|MF_ENABLED },
\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
7830 Enables icsEnables[] = {
\r
7831 { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },
\r
7832 { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },
\r
7833 { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },
\r
7834 { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },
\r
7835 { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },
\r
7836 { IDM_Match, MF_BYCOMMAND|MF_GRAYED },
\r
7837 { IDM_MachineBoth, MF_BYCOMMAND|MF_GRAYED },
\r
7838 { IDM_AnalysisMode, MF_BYCOMMAND|MF_ENABLED },
\r
7839 { IDM_AnalyzeFile, MF_BYCOMMAND|MF_GRAYED },
\r
7840 { IDM_TimeControl, MF_BYCOMMAND|MF_GRAYED },
\r
7841 { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },
\r
7842 { IDM_Hint, MF_BYCOMMAND|MF_GRAYED },
\r
7843 { IDM_Book, MF_BYCOMMAND|MF_GRAYED },
\r
7844 { IDM_LoadProg1, MF_BYCOMMAND|MF_GRAYED },
\r
7845 { IDM_LoadProg2, MF_BYCOMMAND|MF_GRAYED },
\r
7846 { IDM_IcsOptions, MF_BYCOMMAND|MF_ENABLED },
\r
7847 { IDM_Engine1Options, MF_BYCOMMAND|MF_GRAYED },
\r
7848 { IDM_Engine2Options, MF_BYCOMMAND|MF_GRAYED },
\r
7849 { IDM_Annotate, MF_BYCOMMAND|MF_GRAYED },
\r
7850 { IDM_Tourney, MF_BYCOMMAND|MF_GRAYED },
\r
7855 Enables zippyEnables[] = {
\r
7856 { IDM_MoveNow, MF_BYCOMMAND|MF_ENABLED },
\r
7857 { IDM_Hint, MF_BYCOMMAND|MF_ENABLED },
\r
7858 { IDM_Book, MF_BYCOMMAND|MF_ENABLED },
\r
7859 { IDM_Engine1Options, MF_BYCOMMAND|MF_ENABLED },
\r
7864 Enables ncpEnables[] = {
\r
7865 { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },
\r
7866 { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },
\r
7867 { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },
\r
7868 { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },
\r
7869 { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },
\r
7870 { IDM_Match, MF_BYCOMMAND|MF_GRAYED },
\r
7871 { IDM_AnalysisMode, MF_BYCOMMAND|MF_GRAYED },
\r
7872 { IDM_AnalyzeFile, MF_BYCOMMAND|MF_GRAYED },
\r
7873 { IDM_IcsClient, MF_BYCOMMAND|MF_GRAYED },
\r
7874 { ACTION_POS, MF_BYPOSITION|MF_GRAYED },
\r
7875 { IDM_Revert, MF_BYCOMMAND|MF_GRAYED },
\r
7876 { IDM_Annotate, MF_BYCOMMAND|MF_GRAYED },
\r
7877 { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },
\r
7878 { IDM_RetractMove, MF_BYCOMMAND|MF_GRAYED },
\r
7879 { IDM_TimeControl, MF_BYCOMMAND|MF_GRAYED },
\r
7880 { IDM_Hint, MF_BYCOMMAND|MF_GRAYED },
\r
7881 { IDM_Book, MF_BYCOMMAND|MF_GRAYED },
\r
7882 { IDM_MachineBoth, MF_BYCOMMAND|MF_GRAYED },
\r
7883 { IDM_NewChat, MF_BYCOMMAND|MF_GRAYED },
\r
7884 { IDM_Engine1Options, MF_BYCOMMAND|MF_GRAYED },
\r
7885 { IDM_Engine2Options, MF_BYCOMMAND|MF_GRAYED },
\r
7886 { IDM_Sounds, MF_BYCOMMAND|MF_GRAYED },
\r
7890 Enables trainingOnEnables[] = {
\r
7891 { IDM_EditComment, MF_BYCOMMAND|MF_GRAYED },
\r
7892 { IDM_Comment, MF_BYCOMMAND|MF_GRAYED },
\r
7893 { IDM_Pause, MF_BYCOMMAND|MF_GRAYED },
\r
7894 { IDM_Forward, MF_BYCOMMAND|MF_GRAYED },
\r
7895 { IDM_Backward, MF_BYCOMMAND|MF_GRAYED },
\r
7896 { IDM_ToEnd, MF_BYCOMMAND|MF_GRAYED },
\r
7897 { IDM_ToStart, MF_BYCOMMAND|MF_GRAYED },
\r
7898 { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },
\r
7899 { IDM_TruncateGame, MF_BYCOMMAND|MF_GRAYED },
\r
7903 Enables trainingOffEnables[] = {
\r
7904 { IDM_EditComment, MF_BYCOMMAND|MF_ENABLED },
\r
7905 { IDM_Comment, MF_BYCOMMAND|MF_ENABLED },
\r
7906 { IDM_Pause, MF_BYCOMMAND|MF_ENABLED },
\r
7907 { IDM_Forward, MF_BYCOMMAND|MF_ENABLED },
\r
7908 { IDM_Backward, MF_BYCOMMAND|MF_ENABLED },
\r
7909 { IDM_ToEnd, MF_BYCOMMAND|MF_ENABLED },
\r
7910 { IDM_ToStart, MF_BYCOMMAND|MF_ENABLED },
\r
7911 { IDM_MoveNow, MF_BYCOMMAND|MF_ENABLED },
\r
7912 { IDM_TruncateGame, MF_BYCOMMAND|MF_ENABLED },
\r
7916 /* These modify either ncpEnables or gnuEnables */
\r
7917 Enables cmailEnables[] = {
\r
7918 { IDM_MailMove, MF_BYCOMMAND|MF_ENABLED },
\r
7919 { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_ENABLED },
\r
7920 { ACTION_POS, MF_BYPOSITION|MF_ENABLED },
\r
7921 { IDM_CallFlag, MF_BYCOMMAND|MF_GRAYED },
\r
7922 { IDM_Draw, MF_BYCOMMAND|MF_ENABLED },
\r
7923 { IDM_Adjourn, MF_BYCOMMAND|MF_GRAYED },
\r
7924 { IDM_Abort, MF_BYCOMMAND|MF_GRAYED },
\r
7928 Enables machineThinkingEnables[] = {
\r
7929 { IDM_LoadGame, MF_BYCOMMAND|MF_GRAYED },
\r
7930 { IDM_LoadNextGame, MF_BYCOMMAND|MF_GRAYED },
\r
7931 { IDM_LoadPrevGame, MF_BYCOMMAND|MF_GRAYED },
\r
7932 { IDM_ReloadGame, MF_BYCOMMAND|MF_GRAYED },
\r
7933 { IDM_PasteGame, MF_BYCOMMAND|MF_GRAYED },
\r
7934 { IDM_LoadPosition, MF_BYCOMMAND|MF_GRAYED },
\r
7935 { IDM_LoadNextPosition, MF_BYCOMMAND|MF_GRAYED },
\r
7936 { IDM_LoadPrevPosition, MF_BYCOMMAND|MF_GRAYED },
\r
7937 { IDM_ReloadPosition, MF_BYCOMMAND|MF_GRAYED },
\r
7938 { IDM_PastePosition, MF_BYCOMMAND|MF_GRAYED },
\r
7939 { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },
\r
7940 { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },
\r
7941 { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },
\r
7942 // { IDM_Match, MF_BYCOMMAND|MF_GRAYED },
\r
7943 { IDM_TypeInMove, MF_BYCOMMAND|MF_GRAYED },
\r
7944 { IDM_RetractMove, MF_BYCOMMAND|MF_GRAYED },
\r
7948 Enables userThinkingEnables[] = {
\r
7949 { IDM_LoadGame, MF_BYCOMMAND|MF_ENABLED },
\r
7950 { IDM_LoadNextGame, MF_BYCOMMAND|MF_ENABLED },
\r
7951 { IDM_LoadPrevGame, MF_BYCOMMAND|MF_ENABLED },
\r
7952 { IDM_ReloadGame, MF_BYCOMMAND|MF_ENABLED },
\r
7953 { IDM_PasteGame, MF_BYCOMMAND|MF_ENABLED },
\r
7954 { IDM_LoadPosition, MF_BYCOMMAND|MF_ENABLED },
\r
7955 { IDM_LoadNextPosition, MF_BYCOMMAND|MF_ENABLED },
\r
7956 { IDM_LoadPrevPosition, MF_BYCOMMAND|MF_ENABLED },
\r
7957 { IDM_ReloadPosition, MF_BYCOMMAND|MF_ENABLED },
\r
7958 { IDM_PastePosition, MF_BYCOMMAND|MF_ENABLED },
\r
7959 { IDM_MachineWhite, MF_BYCOMMAND|MF_ENABLED },
\r
7960 { IDM_MachineBlack, MF_BYCOMMAND|MF_ENABLED },
\r
7961 { IDM_TwoMachines, MF_BYCOMMAND|MF_ENABLED },
\r
7962 // { IDM_Match, MF_BYCOMMAND|MF_ENABLED },
\r
7963 { IDM_TypeInMove, MF_BYCOMMAND|MF_ENABLED },
\r
7964 { IDM_RetractMove, MF_BYCOMMAND|MF_ENABLED },
\r
7968 /*---------------------------------------------------------------------------*\
\r
7970 * Front-end interface functions exported by XBoard.
\r
7971 * Functions appear in same order as prototypes in frontend.h.
\r
7973 \*---------------------------------------------------------------------------*/
\r
7975 CheckMark(UINT item, int state)
\r
7977 if(item) CheckMenuItem(GetMenu(hwndMain), item, MF_BYCOMMAND|state);
\r
7983 static UINT prevChecked = 0;
\r
7984 static int prevPausing = 0;
\r
7987 if (pausing != prevPausing) {
\r
7988 prevPausing = pausing;
\r
7989 (void) CheckMenuItem(GetMenu(hwndMain), IDM_Pause,
\r
7990 MF_BYCOMMAND|(pausing ? MF_CHECKED : MF_UNCHECKED));
\r
7991 if (hwndPause) SetWindowText(hwndPause, pausing ? "C" : "P");
\r
7994 switch (gameMode) {
\r
7995 case BeginningOfGame:
\r
7996 if (appData.icsActive)
\r
7997 nowChecked = IDM_IcsClient;
\r
7998 else if (appData.noChessProgram)
\r
7999 nowChecked = IDM_EditGame;
\r
8001 nowChecked = IDM_MachineBlack;
\r
8003 case MachinePlaysBlack:
\r
8004 nowChecked = IDM_MachineBlack;
\r
8006 case MachinePlaysWhite:
\r
8007 nowChecked = IDM_MachineWhite;
\r
8009 case TwoMachinesPlay:
\r
8010 nowChecked = IDM_TwoMachines;
\r
8013 nowChecked = IDM_AnalysisMode;
\r
8016 nowChecked = IDM_AnalyzeFile;
\r
8019 nowChecked = IDM_EditGame;
\r
8021 case PlayFromGameFile:
\r
8022 nowChecked = IDM_LoadGame;
\r
8024 case EditPosition:
\r
8025 nowChecked = IDM_EditPosition;
\r
8028 nowChecked = IDM_Training;
\r
8030 case IcsPlayingWhite:
\r
8031 case IcsPlayingBlack:
\r
8032 case IcsObserving:
\r
8034 nowChecked = IDM_IcsClient;
\r
8041 CheckMark(prevChecked, MF_UNCHECKED);
\r
8042 CheckMark(nowChecked, MF_CHECKED);
\r
8043 CheckMark(IDM_Match, matchMode && matchGame < appData.matchGames ? MF_CHECKED : MF_UNCHECKED);
\r
8045 if (nowChecked == IDM_LoadGame || nowChecked == IDM_Training) {
\r
8046 (void) EnableMenuItem(GetMenu(hwndMain), IDM_Training,
\r
8047 MF_BYCOMMAND|MF_ENABLED);
\r
8049 (void) EnableMenuItem(GetMenu(hwndMain),
\r
8050 IDM_Training, MF_BYCOMMAND|MF_GRAYED);
\r
8053 prevChecked = nowChecked;
\r
8055 /* [DM] icsEngineAnalyze - Do a sceure check too */
\r
8056 if (appData.icsActive) {
\r
8057 if (appData.icsEngineAnalyze) {
\r
8058 CheckMark(IDM_AnalysisMode, MF_CHECKED);
\r
8060 CheckMark(IDM_AnalysisMode, MF_UNCHECKED);
\r
8063 DisplayLogos(); // [HGM] logos: mode change could have altered logos
\r
8069 HMENU hmenu = GetMenu(hwndMain);
\r
8070 SetMenuEnables(hmenu, icsEnables);
\r
8071 EnableMenuItem(GetSubMenu(hmenu, OPTIONS_POS), IDM_IcsOptions,
\r
8072 MF_BYCOMMAND|MF_ENABLED);
\r
8074 if (appData.zippyPlay) {
\r
8075 SetMenuEnables(hmenu, zippyEnables);
\r
8076 if (!appData.noChessProgram) /* [DM] icsEngineAnalyze */
\r
8077 (void) EnableMenuItem(GetMenu(hwndMain), IDM_AnalysisMode,
\r
8078 MF_BYCOMMAND|MF_ENABLED);
\r
8086 SetMenuEnables(GetMenu(hwndMain), gnuEnables);
\r
8092 HMENU hmenu = GetMenu(hwndMain);
\r
8093 SetMenuEnables(hmenu, ncpEnables);
\r
8094 DrawMenuBar(hwndMain);
\r
8100 SetMenuEnables(GetMenu(hwndMain), cmailEnables);
\r
8104 SetTrainingModeOn()
\r
8107 SetMenuEnables(GetMenu(hwndMain), trainingOnEnables);
\r
8108 for (i = 0; i < N_BUTTONS; i++) {
\r
8109 if (buttonDesc[i].hwnd != NULL)
\r
8110 EnableWindow(buttonDesc[i].hwnd, FALSE);
\r
8115 VOID SetTrainingModeOff()
\r
8118 SetMenuEnables(GetMenu(hwndMain), trainingOffEnables);
\r
8119 for (i = 0; i < N_BUTTONS; i++) {
\r
8120 if (buttonDesc[i].hwnd != NULL)
\r
8121 EnableWindow(buttonDesc[i].hwnd, TRUE);
\r
8127 SetUserThinkingEnables()
\r
8129 SetMenuEnables(GetMenu(hwndMain), userThinkingEnables);
\r
8133 SetMachineThinkingEnables()
\r
8135 HMENU hMenu = GetMenu(hwndMain);
\r
8136 int flags = MF_BYCOMMAND|MF_ENABLED;
\r
8138 SetMenuEnables(hMenu, machineThinkingEnables);
\r
8140 if (gameMode == MachinePlaysBlack) {
\r
8141 (void)EnableMenuItem(hMenu, IDM_MachineBlack, flags);
\r
8142 } else if (gameMode == MachinePlaysWhite) {
\r
8143 (void)EnableMenuItem(hMenu, IDM_MachineWhite, flags);
\r
8144 } else if (gameMode == TwoMachinesPlay) {
\r
8145 (void)EnableMenuItem(hMenu, matchMode ? IDM_Match : IDM_TwoMachines, flags); // [HGM] match
\r
8151 DisplayTitle(char *str)
\r
8153 char title[MSG_SIZ], *host;
\r
8154 if (str[0] != NULLCHAR) {
\r
8155 safeStrCpy(title, str, sizeof(title)/sizeof(title[0]) );
\r
8156 } else if (appData.icsActive) {
\r
8157 if (appData.icsCommPort[0] != NULLCHAR)
\r
8160 host = appData.icsHost;
\r
8161 snprintf(title, MSG_SIZ, "%s: %s", szTitle, host);
\r
8162 } else if (appData.noChessProgram) {
\r
8163 safeStrCpy(title, szTitle, sizeof(title)/sizeof(title[0]) );
\r
8165 safeStrCpy(title, szTitle, sizeof(title)/sizeof(title[0]) );
\r
8166 strcat(title, ": ");
\r
8167 strcat(title, first.tidy);
\r
8169 SetWindowText(hwndMain, title);
\r
8174 DisplayMessage(char *str1, char *str2)
\r
8178 int remain = MESSAGE_TEXT_MAX - 1;
\r
8181 moveErrorMessageUp = FALSE; /* turned on later by caller if needed */
\r
8182 messageText[0] = NULLCHAR;
\r
8184 len = strlen(str1);
\r
8185 if (len > remain) len = remain;
\r
8186 strncpy(messageText, str1, len);
\r
8187 messageText[len] = NULLCHAR;
\r
8190 if (*str2 && remain >= 2) {
\r
8192 strcat(messageText, " ");
\r
8195 len = strlen(str2);
\r
8196 if (len > remain) len = remain;
\r
8197 strncat(messageText, str2, len);
\r
8199 messageText[MESSAGE_TEXT_MAX - 1] = NULLCHAR;
\r
8200 safeStrCpy(lastMsg, messageText, MSG_SIZ);
\r
8202 if (hwndMain == NULL || IsIconic(hwndMain)) return;
\r
8206 hdc = GetDC(hwndMain);
\r
8207 oldFont = SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);
\r
8208 ExtTextOut(hdc, messageRect.left, messageRect.top, ETO_CLIPPED|ETO_OPAQUE,
\r
8209 &messageRect, messageText, strlen(messageText), NULL);
\r
8210 (void) SelectObject(hdc, oldFont);
\r
8211 (void) ReleaseDC(hwndMain, hdc);
\r
8215 DisplayError(char *str, int error)
\r
8217 char buf[MSG_SIZ*2], buf2[MSG_SIZ];
\r
8221 safeStrCpy(buf, str, sizeof(buf)/sizeof(buf[0]) );
\r
8223 len = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,
\r
8224 NULL, error, LANG_NEUTRAL,
\r
8225 (LPSTR) buf2, MSG_SIZ, NULL);
\r
8227 snprintf(buf, 2*MSG_SIZ, "%s:\n%s", str, buf2);
\r
8229 ErrorMap *em = errmap;
\r
8230 while (em->err != 0 && em->err != error) em++;
\r
8231 if (em->err != 0) {
\r
8232 snprintf(buf, 2*MSG_SIZ, "%s:\n%s", str, em->msg);
\r
8234 snprintf(buf, 2*MSG_SIZ, "%s:\nError code %d", str, error);
\r
8239 ErrorPopUp(_("Error"), buf);
\r
8244 DisplayMoveError(char *str)
\r
8246 fromX = fromY = -1;
\r
8247 ClearHighlights();
\r
8248 DrawPosition(FALSE, NULL);
\r
8249 if (appData.popupMoveErrors) {
\r
8250 ErrorPopUp(_("Error"), str);
\r
8252 DisplayMessage(str, "");
\r
8253 moveErrorMessageUp = TRUE;
\r
8258 DisplayFatalError(char *str, int error, int exitStatus)
\r
8260 char buf[2*MSG_SIZ], buf2[MSG_SIZ];
\r
8262 char *label = exitStatus ? _("Fatal Error") : _("Exiting");
\r
8265 len = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,
\r
8266 NULL, error, LANG_NEUTRAL,
\r
8267 (LPSTR) buf2, MSG_SIZ, NULL);
\r
8269 snprintf(buf, 2*MSG_SIZ, "%s:\n%s", str, buf2);
\r
8271 ErrorMap *em = errmap;
\r
8272 while (em->err != 0 && em->err != error) em++;
\r
8273 if (em->err != 0) {
\r
8274 snprintf(buf, 2*MSG_SIZ, "%s:\n%s", str, em->msg);
\r
8276 snprintf(buf, 2*MSG_SIZ, "%s:\nError code %d", str, error);
\r
8281 if (appData.debugMode) {
\r
8282 fprintf(debugFP, "%s: %s\n", label, str);
\r
8284 if (appData.popupExitMessage) {
\r
8285 (void) MessageBox(hwndMain, str, label, MB_OK|
\r
8286 (exitStatus ? MB_ICONSTOP : MB_ICONINFORMATION));
\r
8288 ExitEvent(exitStatus);
\r
8293 DisplayInformation(char *str)
\r
8295 (void) MessageBox(hwndMain, str, _("Information"), MB_OK|MB_ICONINFORMATION);
\r
8300 DisplayNote(char *str)
\r
8302 ErrorPopUp(_("Note"), str);
\r
8307 char *title, *question, *replyPrefix;
\r
8312 QuestionDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
8314 static QuestionParams *qp;
\r
8315 char reply[MSG_SIZ];
\r
8318 switch (message) {
\r
8319 case WM_INITDIALOG:
\r
8320 qp = (QuestionParams *) lParam;
\r
8321 CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));
\r
8322 Translate(hDlg, DLG_Question);
\r
8323 SetWindowText(hDlg, qp->title);
\r
8324 SetDlgItemText(hDlg, OPT_QuestionText, qp->question);
\r
8325 SetFocus(GetDlgItem(hDlg, OPT_QuestionInput));
\r
8329 switch (LOWORD(wParam)) {
\r
8331 safeStrCpy(reply, qp->replyPrefix, sizeof(reply)/sizeof(reply[0]) );
\r
8332 if (*reply) strcat(reply, " ");
\r
8333 len = strlen(reply);
\r
8334 GetDlgItemText(hDlg, OPT_QuestionInput, reply + len, sizeof(reply) - len);
\r
8335 strcat(reply, "\n");
\r
8336 OutputToProcess(qp->pr, reply, strlen(reply), &err);
\r
8337 EndDialog(hDlg, TRUE);
\r
8338 if (err) DisplayFatalError(_("Error writing to chess program"), err, 1);
\r
8341 EndDialog(hDlg, FALSE);
\r
8352 AskQuestion(char* title, char *question, char *replyPrefix, ProcRef pr)
\r
8354 QuestionParams qp;
\r
8358 qp.question = question;
\r
8359 qp.replyPrefix = replyPrefix;
\r
8361 lpProc = MakeProcInstance((FARPROC)QuestionDialog, hInst);
\r
8362 DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_Question),
\r
8363 hwndMain, (DLGPROC)lpProc, (LPARAM)&qp);
\r
8364 FreeProcInstance(lpProc);
\r
8367 /* [AS] Pick FRC position */
\r
8368 LRESULT CALLBACK NewGameFRC_Proc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
8370 static int * lpIndexFRC;
\r
8376 case WM_INITDIALOG:
\r
8377 lpIndexFRC = (int *) lParam;
\r
8379 CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));
\r
8380 Translate(hDlg, DLG_NewGameFRC);
\r
8382 SendDlgItemMessage( hDlg, IDC_NFG_Edit, EM_SETLIMITTEXT, sizeof(buf)-1, 0 );
\r
8383 SetDlgItemInt( hDlg, IDC_NFG_Edit, *lpIndexFRC, TRUE );
\r
8384 SendDlgItemMessage( hDlg, IDC_NFG_Edit, EM_SETSEL, 0, -1 );
\r
8385 SetFocus(GetDlgItem(hDlg, IDC_NFG_Edit));
\r
8390 switch( LOWORD(wParam) ) {
\r
8392 *lpIndexFRC = GetDlgItemInt(hDlg, IDC_NFG_Edit, &index_is_ok, TRUE );
\r
8393 EndDialog( hDlg, 0 );
\r
8394 shuffleOpenings = TRUE; /* [HGM] shuffle: switch shuffling on for as long as we stay in current variant */
\r
8397 EndDialog( hDlg, 1 );
\r
8399 case IDC_NFG_Edit:
\r
8400 if( HIWORD(wParam) == EN_CHANGE ) {
\r
8401 GetDlgItemInt(hDlg, IDC_NFG_Edit, &index_is_ok, TRUE );
\r
8403 EnableWindow( GetDlgItem(hDlg, IDOK), index_is_ok );
\r
8406 case IDC_NFG_Random:
\r
8407 snprintf( buf, sizeof(buf)/sizeof(buf[0]), "%d", myrandom() ); /* [HGM] shuffle: no longer limit to 960 */
\r
8408 SetDlgItemText(hDlg, IDC_NFG_Edit, buf );
\r
8421 int index = appData.defaultFrcPosition;
\r
8422 FARPROC lpProc = MakeProcInstance( (FARPROC) NewGameFRC_Proc, hInst );
\r
8424 result = DialogBoxParam( hInst, MAKEINTRESOURCE(DLG_NewGameFRC), hwndMain, (DLGPROC)lpProc, (LPARAM)&index );
\r
8426 if( result == 0 ) {
\r
8427 appData.defaultFrcPosition = index;
\r
8433 /* [AS] Game list options. Refactored by HGM */
\r
8435 HWND gameListOptionsDialog;
\r
8437 // low-level front-end: clear text edit / list widget
\r
8441 SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_RESETCONTENT, 0, 0 );
\r
8444 // low-level front-end: clear text edit / list widget
\r
8446 GLT_DeSelectList()
\r
8448 SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_SETCURSEL, 0, 0 );
\r
8451 // low-level front-end: append line to text edit / list widget
\r
8453 GLT_AddToList( char *name )
\r
8456 SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_ADDSTRING, 0, (LPARAM) name );
\r
8460 // low-level front-end: get line from text edit / list widget
\r
8462 GLT_GetFromList( int index, char *name )
\r
8465 if( SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_GETTEXT, index, (LPARAM) name ) != LB_ERR )
\r
8471 void GLT_MoveSelection( HWND hDlg, int delta )
\r
8473 int idx1 = (int) SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETCURSEL, 0, 0 );
\r
8474 int idx2 = idx1 + delta;
\r
8475 int count = (int) SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETCOUNT, 0, 0 );
\r
8477 if( idx1 >=0 && idx1 < count && idx2 >= 0 && idx2 < count ) {
\r
8480 SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETTEXT, idx1, (LPARAM) buf );
\r
8481 SendDlgItemMessage( hDlg, IDC_GameListTags, LB_DELETESTRING, idx1, 0 );
\r
8482 SendDlgItemMessage( hDlg, IDC_GameListTags, LB_INSERTSTRING, idx2, (LPARAM) buf );
\r
8483 SendDlgItemMessage( hDlg, IDC_GameListTags, LB_SETCURSEL, idx2, 0 );
\r
8487 LRESULT CALLBACK GameListOptions_Proc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
8491 case WM_INITDIALOG:
\r
8492 gameListOptionsDialog = hDlg; // [HGM] pass through global to keep out off back-end
\r
8494 CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));
\r
8495 Translate(hDlg, DLG_GameListOptions);
\r
8497 /* Initialize list */
\r
8498 GLT_TagsToList( lpUserGLT );
\r
8500 SetFocus( GetDlgItem(hDlg, IDC_GameListTags) );
\r
8505 switch( LOWORD(wParam) ) {
\r
8508 EndDialog( hDlg, 0 );
\r
8511 EndDialog( hDlg, 1 );
\r
8514 case IDC_GLT_Default:
\r
8515 GLT_TagsToList( GLT_DEFAULT_TAGS );
\r
8518 case IDC_GLT_Restore:
\r
8519 GLT_TagsToList( appData.gameListTags );
\r
8523 GLT_MoveSelection( hDlg, -1 );
\r
8526 case IDC_GLT_Down:
\r
8527 GLT_MoveSelection( hDlg, +1 );
\r
8537 int GameListOptions()
\r
8540 FARPROC lpProc = MakeProcInstance( (FARPROC) GameListOptions_Proc, hInst );
\r
8542 safeStrCpy( lpUserGLT, appData.gameListTags ,LPUSERGLT_SIZE );
\r
8544 result = DialogBoxParam( hInst, MAKEINTRESOURCE(DLG_GameListOptions), hwndMain, (DLGPROC)lpProc, (LPARAM)lpUserGLT );
\r
8546 if( result == 0 ) {
\r
8547 /* [AS] Memory leak here! */
\r
8548 appData.gameListTags = strdup( lpUserGLT );
\r
8555 DisplayIcsInteractionTitle(char *str)
\r
8557 char consoleTitle[MSG_SIZ];
\r
8559 snprintf(consoleTitle, MSG_SIZ, "%s: %s", szConsoleTitle, str);
\r
8560 SetWindowText(hwndConsole, consoleTitle);
\r
8562 if(appData.chatBoxes) { // [HGM] chat: open chat boxes
\r
8563 char buf[MSG_SIZ], *p = buf, *q;
\r
8564 safeStrCpy(buf, appData.chatBoxes, sizeof(buf)/sizeof(buf[0]) );
\r
8566 q = strchr(p, ';');
\r
8568 if(*p) ChatPopUp(p);
\r
8572 SetActiveWindow(hwndMain);
\r
8576 DrawPosition(int fullRedraw, Board board)
\r
8578 HDCDrawPosition(NULL, (BOOLEAN) fullRedraw, board);
\r
8581 void NotifyFrontendLogin()
\r
8584 UpdateICSWidth(GetDlgItem(hwndConsole, OPT_ConsoleText));
\r
8590 fromX = fromY = -1;
\r
8591 if (dragInfo.pos.x != -1 || dragInfo.pos.y != -1) {
\r
8592 dragInfo.pos.x = dragInfo.pos.y = -1;
\r
8593 dragInfo.pos.x = dragInfo.pos.y = -1;
\r
8594 dragInfo.lastpos = dragInfo.pos;
\r
8595 dragInfo.start.x = dragInfo.start.y = -1;
\r
8596 dragInfo.from = dragInfo.start;
\r
8598 DrawPosition(TRUE, NULL);
\r
8605 CommentPopUp(char *title, char *str)
\r
8607 HWND hwnd = GetActiveWindow();
\r
8608 EitherCommentPopUp(currentMove, title, str, FALSE); // [HGM] vari: fake move index, rather than 0
\r
8610 SetActiveWindow(hwnd);
\r
8614 CommentPopDown(void)
\r
8616 CheckMenuItem(GetMenu(hwndMain), IDM_Comment, MF_UNCHECKED);
\r
8617 if (commentDialog) {
\r
8618 ShowWindow(commentDialog, SW_HIDE);
\r
8620 commentUp = FALSE;
\r
8624 EditCommentPopUp(int index, char *title, char *str)
\r
8626 EitherCommentPopUp(index, title, str, TRUE);
\r
8633 MyPlaySound(&sounds[(int)SoundMove]);
\r
8636 VOID PlayIcsWinSound()
\r
8638 MyPlaySound(&sounds[(int)SoundIcsWin]);
\r
8641 VOID PlayIcsLossSound()
\r
8643 MyPlaySound(&sounds[(int)SoundIcsLoss]);
\r
8646 VOID PlayIcsDrawSound()
\r
8648 MyPlaySound(&sounds[(int)SoundIcsDraw]);
\r
8651 VOID PlayIcsUnfinishedSound()
\r
8653 MyPlaySound(&sounds[(int)SoundIcsUnfinished]);
\r
8659 MyPlaySound(&sounds[(int)SoundAlarm]);
\r
8665 MyPlaySound(&textAttribs[ColorTell].sound);
\r
8673 consoleEcho = TRUE;
\r
8674 hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
8675 SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&consoleCF);
\r
8676 SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);
\r
8685 consoleEcho = FALSE;
\r
8686 hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
8687 /* This works OK: set text and background both to the same color */
\r
8689 cf.crTextColor = COLOR_ECHOOFF;
\r
8690 SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf);
\r
8691 SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, cf.crTextColor);
\r
8694 /* No Raw()...? */
\r
8696 void Colorize(ColorClass cc, int continuation)
\r
8698 currentColorClass = cc;
\r
8699 consoleCF.dwMask = CFM_COLOR|CFM_BOLD|CFM_ITALIC|CFM_UNDERLINE|CFM_STRIKEOUT;
\r
8700 consoleCF.crTextColor = textAttribs[cc].color;
\r
8701 consoleCF.dwEffects = textAttribs[cc].effects;
\r
8702 if (!continuation) MyPlaySound(&textAttribs[cc].sound);
\r
8708 static char buf[MSG_SIZ];
\r
8709 DWORD bufsiz = MSG_SIZ;
\r
8711 if(appData.userName != NULL && appData.userName[0] != 0) {
\r
8712 return appData.userName; /* [HGM] username: prefer name selected by user over his system login */
\r
8714 if (!GetUserName(buf, &bufsiz)) {
\r
8715 /*DisplayError("Error getting user name", GetLastError());*/
\r
8716 safeStrCpy(buf, _("User"), sizeof(buf)/sizeof(buf[0]) );
\r
8724 static char buf[MSG_SIZ];
\r
8725 DWORD bufsiz = MSG_SIZ;
\r
8727 if (!GetComputerName(buf, &bufsiz)) {
\r
8728 /*DisplayError("Error getting host name", GetLastError());*/
\r
8729 safeStrCpy(buf, _("Unknown"), sizeof(buf)/sizeof(buf[0]) );
\r
8736 ClockTimerRunning()
\r
8738 return clockTimerEvent != 0;
\r
8744 if (clockTimerEvent == 0) return FALSE;
\r
8745 KillTimer(hwndMain, clockTimerEvent);
\r
8746 clockTimerEvent = 0;
\r
8751 StartClockTimer(long millisec)
\r
8753 clockTimerEvent = SetTimer(hwndMain, (UINT) CLOCK_TIMER_ID,
\r
8754 (UINT) millisec, NULL);
\r
8758 DisplayWhiteClock(long timeRemaining, int highlight)
\r
8761 char *flag = whiteFlag && gameMode == TwoMachinesPlay ? "(!)" : "";
\r
8763 if(appData.noGUI) return;
\r
8764 hdc = GetDC(hwndMain);
\r
8765 if (!IsIconic(hwndMain)) {
\r
8766 DisplayAClock(hdc, timeRemaining, highlight,
\r
8767 flipClock ? &blackRect : &whiteRect, _("White"), flag);
\r
8769 if (highlight && iconCurrent == iconBlack) {
\r
8770 iconCurrent = iconWhite;
\r
8771 PostMessage(hwndMain, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);
\r
8772 if (IsIconic(hwndMain)) {
\r
8773 DrawIcon(hdc, 2, 2, iconCurrent);
\r
8776 (void) ReleaseDC(hwndMain, hdc);
\r
8778 PostMessage(hwndConsole, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);
\r
8782 DisplayBlackClock(long timeRemaining, int highlight)
\r
8785 char *flag = blackFlag && gameMode == TwoMachinesPlay ? "(!)" : "";
\r
8787 if(appData.noGUI) return;
\r
8788 hdc = GetDC(hwndMain);
\r
8789 if (!IsIconic(hwndMain)) {
\r
8790 DisplayAClock(hdc, timeRemaining, highlight,
\r
8791 flipClock ? &whiteRect : &blackRect, _("Black"), flag);
\r
8793 if (highlight && iconCurrent == iconWhite) {
\r
8794 iconCurrent = iconBlack;
\r
8795 PostMessage(hwndMain, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);
\r
8796 if (IsIconic(hwndMain)) {
\r
8797 DrawIcon(hdc, 2, 2, iconCurrent);
\r
8800 (void) ReleaseDC(hwndMain, hdc);
\r
8802 PostMessage(hwndConsole, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);
\r
8807 LoadGameTimerRunning()
\r
8809 return loadGameTimerEvent != 0;
\r
8813 StopLoadGameTimer()
\r
8815 if (loadGameTimerEvent == 0) return FALSE;
\r
8816 KillTimer(hwndMain, loadGameTimerEvent);
\r
8817 loadGameTimerEvent = 0;
\r
8822 StartLoadGameTimer(long millisec)
\r
8824 loadGameTimerEvent = SetTimer(hwndMain, (UINT) LOAD_GAME_TIMER_ID,
\r
8825 (UINT) millisec, NULL);
\r
8833 char fileTitle[MSG_SIZ];
\r
8835 defName = DefaultFileName(appData.oldSaveStyle ? "gam" : "pgn");
\r
8836 f = OpenFileDialog(hwndMain, "a", defName,
\r
8837 appData.oldSaveStyle ? "gam" : "pgn",
\r
8839 _("Save Game to File"), NULL, fileTitle, NULL);
\r
8841 SaveGame(f, 0, "");
\r
8848 ScheduleDelayedEvent(DelayedEventCallback cb, long millisec)
\r
8850 if (delayedTimerEvent != 0) {
\r
8851 if (appData.debugMode && cb != delayedTimerCallback) { // [HGM] alive: not too much debug
\r
8852 fprintf(debugFP, "ScheduleDelayedEvent: event already scheduled\n");
\r
8854 KillTimer(hwndMain, delayedTimerEvent);
\r
8855 delayedTimerEvent = 0;
\r
8856 if(delayedTimerCallback != cb) // [HGM] alive: do not "flush" same event, just postpone it
\r
8857 delayedTimerCallback();
\r
8859 delayedTimerCallback = cb;
\r
8860 delayedTimerEvent = SetTimer(hwndMain, (UINT) DELAYED_TIMER_ID,
\r
8861 (UINT) millisec, NULL);
\r
8864 DelayedEventCallback
\r
8867 if (delayedTimerEvent) {
\r
8868 return delayedTimerCallback;
\r
8875 CancelDelayedEvent()
\r
8877 if (delayedTimerEvent) {
\r
8878 KillTimer(hwndMain, delayedTimerEvent);
\r
8879 delayedTimerEvent = 0;
\r
8883 DWORD GetWin32Priority(int nice)
\r
8884 { // [HGM] nice: translate Unix nice() value to indows priority class. (Code stolen from Polyglot 1.4w11)
\r
8886 REALTIME_PRIORITY_CLASS 0x00000100
\r
8887 HIGH_PRIORITY_CLASS 0x00000080
\r
8888 ABOVE_NORMAL_PRIORITY_CLASS 0x00008000
\r
8889 NORMAL_PRIORITY_CLASS 0x00000020
\r
8890 BELOW_NORMAL_PRIORITY_CLASS 0x00004000
\r
8891 IDLE_PRIORITY_CLASS 0x00000040
\r
8893 if (nice < -15) return 0x00000080;
\r
8894 if (nice < 0) return 0x00008000;
\r
8895 if (nice == 0) return 0x00000020;
\r
8896 if (nice < 15) return 0x00004000;
\r
8897 return 0x00000040;
\r
8900 void RunCommand(char *cmdLine)
\r
8902 /* Now create the child process. */
\r
8903 STARTUPINFO siStartInfo;
\r
8904 PROCESS_INFORMATION piProcInfo;
\r
8906 siStartInfo.cb = sizeof(STARTUPINFO);
\r
8907 siStartInfo.lpReserved = NULL;
\r
8908 siStartInfo.lpDesktop = NULL;
\r
8909 siStartInfo.lpTitle = NULL;
\r
8910 siStartInfo.dwFlags = STARTF_USESTDHANDLES;
\r
8911 siStartInfo.cbReserved2 = 0;
\r
8912 siStartInfo.lpReserved2 = NULL;
\r
8913 siStartInfo.hStdInput = NULL;
\r
8914 siStartInfo.hStdOutput = NULL;
\r
8915 siStartInfo.hStdError = NULL;
\r
8917 CreateProcess(NULL,
\r
8918 cmdLine, /* command line */
\r
8919 NULL, /* process security attributes */
\r
8920 NULL, /* primary thread security attrs */
\r
8921 TRUE, /* handles are inherited */
\r
8922 DETACHED_PROCESS|CREATE_NEW_PROCESS_GROUP,
\r
8923 NULL, /* use parent's environment */
\r
8925 &siStartInfo, /* STARTUPINFO pointer */
\r
8926 &piProcInfo); /* receives PROCESS_INFORMATION */
\r
8928 CloseHandle(piProcInfo.hThread);
\r
8931 /* Start a child process running the given program.
\r
8932 The process's standard output can be read from "from", and its
\r
8933 standard input can be written to "to".
\r
8934 Exit with fatal error if anything goes wrong.
\r
8935 Returns an opaque pointer that can be used to destroy the process
\r
8939 StartChildProcess(char *cmdLine, char *dir, ProcRef *pr)
\r
8941 #define BUFSIZE 4096
\r
8943 HANDLE hChildStdinRd, hChildStdinWr,
\r
8944 hChildStdoutRd, hChildStdoutWr;
\r
8945 HANDLE hChildStdinWrDup, hChildStdoutRdDup;
\r
8946 SECURITY_ATTRIBUTES saAttr;
\r
8948 PROCESS_INFORMATION piProcInfo;
\r
8949 STARTUPINFO siStartInfo;
\r
8951 char buf[MSG_SIZ];
\r
8954 if (appData.debugMode) {
\r
8955 fprintf(debugFP, "StartChildProcess (dir=\"%s\") %s\n", dir, cmdLine);
\r
8960 /* Set the bInheritHandle flag so pipe handles are inherited. */
\r
8961 saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);
\r
8962 saAttr.bInheritHandle = TRUE;
\r
8963 saAttr.lpSecurityDescriptor = NULL;
\r
8966 * The steps for redirecting child's STDOUT:
\r
8967 * 1. Create anonymous pipe to be STDOUT for child.
\r
8968 * 2. Create a noninheritable duplicate of read handle,
\r
8969 * and close the inheritable read handle.
\r
8972 /* Create a pipe for the child's STDOUT. */
\r
8973 if (! CreatePipe(&hChildStdoutRd, &hChildStdoutWr, &saAttr, 0)) {
\r
8974 return GetLastError();
\r
8977 /* Duplicate the read handle to the pipe, so it is not inherited. */
\r
8978 fSuccess = DuplicateHandle(GetCurrentProcess(), hChildStdoutRd,
\r
8979 GetCurrentProcess(), &hChildStdoutRdDup, 0,
\r
8980 FALSE, /* not inherited */
\r
8981 DUPLICATE_SAME_ACCESS);
\r
8983 return GetLastError();
\r
8985 CloseHandle(hChildStdoutRd);
\r
8988 * The steps for redirecting child's STDIN:
\r
8989 * 1. Create anonymous pipe to be STDIN for child.
\r
8990 * 2. Create a noninheritable duplicate of write handle,
\r
8991 * and close the inheritable write handle.
\r
8994 /* Create a pipe for the child's STDIN. */
\r
8995 if (! CreatePipe(&hChildStdinRd, &hChildStdinWr, &saAttr, 0)) {
\r
8996 return GetLastError();
\r
8999 /* Duplicate the write handle to the pipe, so it is not inherited. */
\r
9000 fSuccess = DuplicateHandle(GetCurrentProcess(), hChildStdinWr,
\r
9001 GetCurrentProcess(), &hChildStdinWrDup, 0,
\r
9002 FALSE, /* not inherited */
\r
9003 DUPLICATE_SAME_ACCESS);
\r
9005 return GetLastError();
\r
9007 CloseHandle(hChildStdinWr);
\r
9009 /* Arrange to (1) look in dir for the child .exe file, and
\r
9010 * (2) have dir be the child's working directory. Interpret
\r
9011 * dir relative to the directory WinBoard loaded from. */
\r
9012 GetCurrentDirectory(MSG_SIZ, buf);
\r
9013 SetCurrentDirectory(installDir);
\r
9014 SetCurrentDirectory(dir);
\r
9016 /* Now create the child process. */
\r
9018 siStartInfo.cb = sizeof(STARTUPINFO);
\r
9019 siStartInfo.lpReserved = NULL;
\r
9020 siStartInfo.lpDesktop = NULL;
\r
9021 siStartInfo.lpTitle = NULL;
\r
9022 siStartInfo.dwFlags = STARTF_USESTDHANDLES;
\r
9023 siStartInfo.cbReserved2 = 0;
\r
9024 siStartInfo.lpReserved2 = NULL;
\r
9025 siStartInfo.hStdInput = hChildStdinRd;
\r
9026 siStartInfo.hStdOutput = hChildStdoutWr;
\r
9027 siStartInfo.hStdError = hChildStdoutWr;
\r
9029 fSuccess = CreateProcess(NULL,
\r
9030 cmdLine, /* command line */
\r
9031 NULL, /* process security attributes */
\r
9032 NULL, /* primary thread security attrs */
\r
9033 TRUE, /* handles are inherited */
\r
9034 DETACHED_PROCESS|CREATE_NEW_PROCESS_GROUP,
\r
9035 NULL, /* use parent's environment */
\r
9037 &siStartInfo, /* STARTUPINFO pointer */
\r
9038 &piProcInfo); /* receives PROCESS_INFORMATION */
\r
9040 err = GetLastError();
\r
9041 SetCurrentDirectory(buf); /* return to prev directory */
\r
9046 if (appData.niceEngines){ // [HGM] nice: adjust engine proc priority
\r
9047 if(appData.debugMode) fprintf(debugFP, "nice engine proc to %d\n", appData.niceEngines);
\r
9048 SetPriorityClass(piProcInfo.hProcess, GetWin32Priority(appData.niceEngines));
\r
9051 /* Close the handles we don't need in the parent */
\r
9052 CloseHandle(piProcInfo.hThread);
\r
9053 CloseHandle(hChildStdinRd);
\r
9054 CloseHandle(hChildStdoutWr);
\r
9056 /* Prepare return value */
\r
9057 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
\r
9058 cp->kind = CPReal;
\r
9059 cp->hProcess = piProcInfo.hProcess;
\r
9060 cp->pid = piProcInfo.dwProcessId;
\r
9061 cp->hFrom = hChildStdoutRdDup;
\r
9062 cp->hTo = hChildStdinWrDup;
\r
9064 *pr = (void *) cp;
\r
9066 /* Klaus Friedel says that this Sleep solves a problem under Windows
\r
9067 2000 where engines sometimes don't see the initial command(s)
\r
9068 from WinBoard and hang. I don't understand how that can happen,
\r
9069 but the Sleep is harmless, so I've put it in. Others have also
\r
9070 reported what may be the same problem, so hopefully this will fix
\r
9071 it for them too. */
\r
9079 DestroyChildProcess(ProcRef pr, int/*boolean*/ signal)
\r
9081 ChildProc *cp; int result;
\r
9083 cp = (ChildProc *) pr;
\r
9084 if (cp == NULL) return;
\r
9086 switch (cp->kind) {
\r
9088 /* TerminateProcess is considered harmful, so... */
\r
9089 CloseHandle(cp->hTo); /* Closing this will give the child an EOF and hopefully kill it */
\r
9090 if (cp->hFrom) CloseHandle(cp->hFrom); /* if NULL, InputThread will close it */
\r
9091 /* The following doesn't work because the chess program
\r
9092 doesn't "have the same console" as WinBoard. Maybe
\r
9093 we could arrange for this even though neither WinBoard
\r
9094 nor the chess program uses a console for stdio? */
\r
9095 /*!!if (signal) GenerateConsoleCtrlEvent(CTRL_BREAK_EVENT, cp->pid);*/
\r
9097 /* [AS] Special termination modes for misbehaving programs... */
\r
9098 if( signal == 9 ) {
\r
9099 result = TerminateProcess( cp->hProcess, 0 );
\r
9101 if ( appData.debugMode) {
\r
9102 fprintf( debugFP, "Terminating process %lu, result=%d\n", cp->pid, result );
\r
9105 else if( signal == 10 ) {
\r
9106 DWORD dw = WaitForSingleObject( cp->hProcess, 3*1000 ); // Wait 3 seconds at most
\r
9108 if( dw != WAIT_OBJECT_0 ) {
\r
9109 result = TerminateProcess( cp->hProcess, 0 );
\r
9111 if ( appData.debugMode) {
\r
9112 fprintf( debugFP, "Process %lu still alive after timeout, killing... result=%d\n", cp->pid, result );
\r
9118 CloseHandle(cp->hProcess);
\r
9122 if (cp->hFrom) CloseHandle(cp->hFrom);
\r
9126 closesocket(cp->sock);
\r
9131 if (signal) send(cp->sock2, "\017", 1, 0); /* 017 = 15 = SIGTERM */
\r
9132 closesocket(cp->sock);
\r
9133 closesocket(cp->sock2);
\r
9141 InterruptChildProcess(ProcRef pr)
\r
9145 cp = (ChildProc *) pr;
\r
9146 if (cp == NULL) return;
\r
9147 switch (cp->kind) {
\r
9149 /* The following doesn't work because the chess program
\r
9150 doesn't "have the same console" as WinBoard. Maybe
\r
9151 we could arrange for this even though neither WinBoard
\r
9152 nor the chess program uses a console for stdio */
\r
9153 /*!!GenerateConsoleCtrlEvent(CTRL_C_EVENT, cp->pid);*/
\r
9158 /* Can't interrupt */
\r
9162 send(cp->sock2, "\002", 1, 0); /* 2 = SIGINT */
\r
9169 OpenTelnet(char *host, char *port, ProcRef *pr)
\r
9171 char cmdLine[MSG_SIZ];
\r
9173 if (port[0] == NULLCHAR) {
\r
9174 snprintf(cmdLine, MSG_SIZ, "%s %s", appData.telnetProgram, host);
\r
9176 snprintf(cmdLine, MSG_SIZ, "%s %s %s", appData.telnetProgram, host, port);
\r
9178 return StartChildProcess(cmdLine, "", pr);
\r
9182 /* Code to open TCP sockets */
\r
9185 OpenTCP(char *host, char *port, ProcRef *pr)
\r
9191 struct sockaddr_in sa, mysa;
\r
9192 struct hostent FAR *hp;
\r
9193 unsigned short uport;
\r
9194 WORD wVersionRequested;
\r
9197 /* Initialize socket DLL */
\r
9198 wVersionRequested = MAKEWORD(1, 1);
\r
9199 err = WSAStartup(wVersionRequested, &wsaData);
\r
9200 if (err != 0) return err;
\r
9203 if ((s = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {
\r
9204 err = WSAGetLastError();
\r
9209 /* Bind local address using (mostly) don't-care values.
\r
9211 memset((char *) &mysa, 0, sizeof(struct sockaddr_in));
\r
9212 mysa.sin_family = AF_INET;
\r
9213 mysa.sin_addr.s_addr = INADDR_ANY;
\r
9214 uport = (unsigned short) 0;
\r
9215 mysa.sin_port = htons(uport);
\r
9216 if (bind(s, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))
\r
9217 == SOCKET_ERROR) {
\r
9218 err = WSAGetLastError();
\r
9223 /* Resolve remote host name */
\r
9224 memset((char *) &sa, 0, sizeof(struct sockaddr_in));
\r
9225 if (!(hp = gethostbyname(host))) {
\r
9226 unsigned int b0, b1, b2, b3;
\r
9228 err = WSAGetLastError();
\r
9230 if (sscanf(host, "%u.%u.%u.%u", &b0, &b1, &b2, &b3) == 4) {
\r
9231 hp = (struct hostent *) calloc(1, sizeof(struct hostent));
\r
9232 hp->h_addrtype = AF_INET;
\r
9234 hp->h_addr_list = (char **) calloc(2, sizeof(char *));
\r
9235 hp->h_addr_list[0] = (char *) malloc(4);
\r
9236 hp->h_addr_list[0][0] = (char) b0;
\r
9237 hp->h_addr_list[0][1] = (char) b1;
\r
9238 hp->h_addr_list[0][2] = (char) b2;
\r
9239 hp->h_addr_list[0][3] = (char) b3;
\r
9245 sa.sin_family = hp->h_addrtype;
\r
9246 uport = (unsigned short) atoi(port);
\r
9247 sa.sin_port = htons(uport);
\r
9248 memcpy((char *) &sa.sin_addr, hp->h_addr, hp->h_length);
\r
9250 /* Make connection */
\r
9251 if (connect(s, (struct sockaddr *) &sa,
\r
9252 sizeof(struct sockaddr_in)) == SOCKET_ERROR) {
\r
9253 err = WSAGetLastError();
\r
9258 /* Prepare return value */
\r
9259 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
\r
9260 cp->kind = CPSock;
\r
9262 *pr = (ProcRef *) cp;
\r
9268 OpenCommPort(char *name, ProcRef *pr)
\r
9273 char fullname[MSG_SIZ];
\r
9275 if (*name != '\\')
\r
9276 snprintf(fullname, MSG_SIZ, "\\\\.\\%s", name);
\r
9278 safeStrCpy(fullname, name, sizeof(fullname)/sizeof(fullname[0]) );
\r
9280 h = CreateFile(name, GENERIC_READ | GENERIC_WRITE,
\r
9281 0, NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL);
\r
9282 if (h == (HANDLE) -1) {
\r
9283 return GetLastError();
\r
9287 if (!SetCommState(h, (LPDCB) &dcb)) return GetLastError();
\r
9289 /* Accumulate characters until a 100ms pause, then parse */
\r
9290 ct.ReadIntervalTimeout = 100;
\r
9291 ct.ReadTotalTimeoutMultiplier = 0;
\r
9292 ct.ReadTotalTimeoutConstant = 0;
\r
9293 ct.WriteTotalTimeoutMultiplier = 0;
\r
9294 ct.WriteTotalTimeoutConstant = 0;
\r
9295 if (!SetCommTimeouts(h, (LPCOMMTIMEOUTS) &ct)) return GetLastError();
\r
9297 /* Prepare return value */
\r
9298 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
\r
9299 cp->kind = CPComm;
\r
9302 *pr = (ProcRef *) cp;
\r
9308 OpenLoopback(ProcRef *pr)
\r
9310 DisplayFatalError(_("Not implemented"), 0, 1);
\r
9316 OpenRcmd(char* host, char* user, char* cmd, ProcRef* pr)
\r
9321 struct sockaddr_in sa, mysa;
\r
9322 struct hostent FAR *hp;
\r
9323 unsigned short uport;
\r
9324 WORD wVersionRequested;
\r
9327 char stderrPortStr[MSG_SIZ];
\r
9329 /* Initialize socket DLL */
\r
9330 wVersionRequested = MAKEWORD(1, 1);
\r
9331 err = WSAStartup(wVersionRequested, &wsaData);
\r
9332 if (err != 0) return err;
\r
9334 /* Resolve remote host name */
\r
9335 memset((char *) &sa, 0, sizeof(struct sockaddr_in));
\r
9336 if (!(hp = gethostbyname(host))) {
\r
9337 unsigned int b0, b1, b2, b3;
\r
9339 err = WSAGetLastError();
\r
9341 if (sscanf(host, "%u.%u.%u.%u", &b0, &b1, &b2, &b3) == 4) {
\r
9342 hp = (struct hostent *) calloc(1, sizeof(struct hostent));
\r
9343 hp->h_addrtype = AF_INET;
\r
9345 hp->h_addr_list = (char **) calloc(2, sizeof(char *));
\r
9346 hp->h_addr_list[0] = (char *) malloc(4);
\r
9347 hp->h_addr_list[0][0] = (char) b0;
\r
9348 hp->h_addr_list[0][1] = (char) b1;
\r
9349 hp->h_addr_list[0][2] = (char) b2;
\r
9350 hp->h_addr_list[0][3] = (char) b3;
\r
9356 sa.sin_family = hp->h_addrtype;
\r
9357 uport = (unsigned short) 514;
\r
9358 sa.sin_port = htons(uport);
\r
9359 memcpy((char *) &sa.sin_addr, hp->h_addr, hp->h_length);
\r
9361 /* Bind local socket to unused "privileged" port address
\r
9363 s = INVALID_SOCKET;
\r
9364 memset((char *) &mysa, 0, sizeof(struct sockaddr_in));
\r
9365 mysa.sin_family = AF_INET;
\r
9366 mysa.sin_addr.s_addr = INADDR_ANY;
\r
9367 for (fromPort = 1023;; fromPort--) {
\r
9368 if (fromPort < 0) {
\r
9370 return WSAEADDRINUSE;
\r
9372 if (s == INVALID_SOCKET) {
\r
9373 if ((s = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {
\r
9374 err = WSAGetLastError();
\r
9379 uport = (unsigned short) fromPort;
\r
9380 mysa.sin_port = htons(uport);
\r
9381 if (bind(s, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))
\r
9382 == SOCKET_ERROR) {
\r
9383 err = WSAGetLastError();
\r
9384 if (err == WSAEADDRINUSE) continue;
\r
9388 if (connect(s, (struct sockaddr *) &sa,
\r
9389 sizeof(struct sockaddr_in)) == SOCKET_ERROR) {
\r
9390 err = WSAGetLastError();
\r
9391 if (err == WSAEADDRINUSE) {
\r
9402 /* Bind stderr local socket to unused "privileged" port address
\r
9404 s2 = INVALID_SOCKET;
\r
9405 memset((char *) &mysa, 0, sizeof(struct sockaddr_in));
\r
9406 mysa.sin_family = AF_INET;
\r
9407 mysa.sin_addr.s_addr = INADDR_ANY;
\r
9408 for (fromPort = 1023;; fromPort--) {
\r
9409 if (fromPort == prevStderrPort) continue; // don't reuse port
\r
9410 if (fromPort < 0) {
\r
9411 (void) closesocket(s);
\r
9413 return WSAEADDRINUSE;
\r
9415 if (s2 == INVALID_SOCKET) {
\r
9416 if ((s2 = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {
\r
9417 err = WSAGetLastError();
\r
9423 uport = (unsigned short) fromPort;
\r
9424 mysa.sin_port = htons(uport);
\r
9425 if (bind(s2, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))
\r
9426 == SOCKET_ERROR) {
\r
9427 err = WSAGetLastError();
\r
9428 if (err == WSAEADDRINUSE) continue;
\r
9429 (void) closesocket(s);
\r
9433 if (listen(s2, 1) == SOCKET_ERROR) {
\r
9434 err = WSAGetLastError();
\r
9435 if (err == WSAEADDRINUSE) {
\r
9437 s2 = INVALID_SOCKET;
\r
9440 (void) closesocket(s);
\r
9441 (void) closesocket(s2);
\r
9447 prevStderrPort = fromPort; // remember port used
\r
9448 snprintf(stderrPortStr, MSG_SIZ, "%d", fromPort);
\r
9450 if (send(s, stderrPortStr, strlen(stderrPortStr) + 1, 0) == SOCKET_ERROR) {
\r
9451 err = WSAGetLastError();
\r
9452 (void) closesocket(s);
\r
9453 (void) closesocket(s2);
\r
9458 if (send(s, UserName(), strlen(UserName()) + 1, 0) == SOCKET_ERROR) {
\r
9459 err = WSAGetLastError();
\r
9460 (void) closesocket(s);
\r
9461 (void) closesocket(s2);
\r
9465 if (*user == NULLCHAR) user = UserName();
\r
9466 if (send(s, user, strlen(user) + 1, 0) == SOCKET_ERROR) {
\r
9467 err = WSAGetLastError();
\r
9468 (void) closesocket(s);
\r
9469 (void) closesocket(s2);
\r
9473 if (send(s, cmd, strlen(cmd) + 1, 0) == SOCKET_ERROR) {
\r
9474 err = WSAGetLastError();
\r
9475 (void) closesocket(s);
\r
9476 (void) closesocket(s2);
\r
9481 if ((s3 = accept(s2, NULL, NULL)) == INVALID_SOCKET) {
\r
9482 err = WSAGetLastError();
\r
9483 (void) closesocket(s);
\r
9484 (void) closesocket(s2);
\r
9488 (void) closesocket(s2); /* Stop listening */
\r
9490 /* Prepare return value */
\r
9491 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
\r
9492 cp->kind = CPRcmd;
\r
9495 *pr = (ProcRef *) cp;
\r
9502 AddInputSource(ProcRef pr, int lineByLine,
\r
9503 InputCallback func, VOIDSTAR closure)
\r
9505 InputSource *is, *is2 = NULL;
\r
9506 ChildProc *cp = (ChildProc *) pr;
\r
9508 is = (InputSource *) calloc(1, sizeof(InputSource));
\r
9509 is->lineByLine = lineByLine;
\r
9511 is->closure = closure;
\r
9512 is->second = NULL;
\r
9513 is->next = is->buf;
\r
9514 if (pr == NoProc) {
\r
9515 is->kind = CPReal;
\r
9516 consoleInputSource = is;
\r
9518 is->kind = cp->kind;
\r
9520 [AS] Try to avoid a race condition if the thread is given control too early:
\r
9521 we create all threads suspended so that the is->hThread variable can be
\r
9522 safely assigned, then let the threads start with ResumeThread.
\r
9524 switch (cp->kind) {
\r
9526 is->hFile = cp->hFrom;
\r
9527 cp->hFrom = NULL; /* now owned by InputThread */
\r
9529 CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) NonOvlInputThread,
\r
9530 (LPVOID) is, CREATE_SUSPENDED, &is->id);
\r
9534 is->hFile = cp->hFrom;
\r
9535 cp->hFrom = NULL; /* now owned by InputThread */
\r
9537 CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) InputThread,
\r
9538 (LPVOID) is, CREATE_SUSPENDED, &is->id);
\r
9542 is->sock = cp->sock;
\r
9544 CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,
\r
9545 (LPVOID) is, CREATE_SUSPENDED, &is->id);
\r
9549 is2 = (InputSource *) calloc(1, sizeof(InputSource));
\r
9551 is->sock = cp->sock;
\r
9553 is2->sock = cp->sock2;
\r
9554 is2->second = is2;
\r
9556 CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,
\r
9557 (LPVOID) is, CREATE_SUSPENDED, &is->id);
\r
9559 CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,
\r
9560 (LPVOID) is2, CREATE_SUSPENDED, &is2->id);
\r
9564 if( is->hThread != NULL ) {
\r
9565 ResumeThread( is->hThread );
\r
9568 if( is2 != NULL && is2->hThread != NULL ) {
\r
9569 ResumeThread( is2->hThread );
\r
9573 return (InputSourceRef) is;
\r
9577 RemoveInputSource(InputSourceRef isr)
\r
9581 is = (InputSource *) isr;
\r
9582 is->hThread = NULL; /* tell thread to stop */
\r
9583 CloseHandle(is->hThread);
\r
9584 if (is->second != NULL) {
\r
9585 is->second->hThread = NULL;
\r
9586 CloseHandle(is->second->hThread);
\r
9590 int no_wrap(char *message, int count)
\r
9592 ConsoleOutput(message, count, FALSE);
\r
9597 OutputToProcess(ProcRef pr, char *message, int count, int *outError)
\r
9600 int outCount = SOCKET_ERROR;
\r
9601 ChildProc *cp = (ChildProc *) pr;
\r
9602 static OVERLAPPED ovl;
\r
9603 static int line = 0;
\r
9607 if (appData.noJoin || !appData.useInternalWrap)
\r
9608 return no_wrap(message, count);
\r
9611 int width = get_term_width();
\r
9612 int len = wrap(NULL, message, count, width, &line);
\r
9613 char *msg = malloc(len);
\r
9617 return no_wrap(message, count);
\r
9620 dbgchk = wrap(msg, message, count, width, &line);
\r
9621 if (dbgchk != len && appData.debugMode)
\r
9622 fprintf(debugFP, "wrap(): dbgchk(%d) != len(%d)\n", dbgchk, len);
\r
9623 ConsoleOutput(msg, len, FALSE);
\r
9630 if (ovl.hEvent == NULL) {
\r
9631 ovl.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
\r
9633 ovl.Internal = ovl.InternalHigh = ovl.Offset = ovl.OffsetHigh = 0;
\r
9635 switch (cp->kind) {
\r
9638 outCount = send(cp->sock, message, count, 0);
\r
9639 if (outCount == SOCKET_ERROR) {
\r
9640 *outError = WSAGetLastError();
\r
9642 *outError = NO_ERROR;
\r
9647 if (WriteFile(((ChildProc *)pr)->hTo, message, count,
\r
9648 &dOutCount, NULL)) {
\r
9649 *outError = NO_ERROR;
\r
9650 outCount = (int) dOutCount;
\r
9652 *outError = GetLastError();
\r
9657 *outError = DoWriteFile(((ChildProc *)pr)->hTo, message, count,
\r
9658 &dOutCount, &ovl);
\r
9659 if (*outError == NO_ERROR) {
\r
9660 outCount = (int) dOutCount;
\r
9670 if(n != 0) Sleep(n);
\r
9674 OutputToProcessDelayed(ProcRef pr, char *message, int count, int *outError,
\r
9677 /* Ignore delay, not implemented for WinBoard */
\r
9678 return OutputToProcess(pr, message, count, outError);
\r
9683 CmailSigHandlerCallBack(InputSourceRef isr, VOIDSTAR closure,
\r
9684 char *buf, int count, int error)
\r
9686 DisplayFatalError(_("Not implemented"), 0, 1);
\r
9689 /* see wgamelist.c for Game List functions */
\r
9690 /* see wedittags.c for Edit Tags functions */
\r
9697 char buf[MSG_SIZ];
\r
9700 if (SearchPath(installDir, appData.icsLogon, NULL, MSG_SIZ, buf, &dummy)) {
\r
9701 f = fopen(buf, "r");
\r
9703 ProcessICSInitScript(f);
\r
9711 StartAnalysisClock()
\r
9713 if (analysisTimerEvent) return;
\r
9714 analysisTimerEvent = SetTimer(hwndMain, (UINT) ANALYSIS_TIMER_ID,
\r
9715 (UINT) 2000, NULL);
\r
9719 SetHighlights(int fromX, int fromY, int toX, int toY)
\r
9721 highlightInfo.sq[0].x = fromX;
\r
9722 highlightInfo.sq[0].y = fromY;
\r
9723 highlightInfo.sq[1].x = toX;
\r
9724 highlightInfo.sq[1].y = toY;
\r
9730 highlightInfo.sq[0].x = highlightInfo.sq[0].y =
\r
9731 highlightInfo.sq[1].x = highlightInfo.sq[1].y = -1;
\r
9735 SetPremoveHighlights(int fromX, int fromY, int toX, int toY)
\r
9737 premoveHighlightInfo.sq[0].x = fromX;
\r
9738 premoveHighlightInfo.sq[0].y = fromY;
\r
9739 premoveHighlightInfo.sq[1].x = toX;
\r
9740 premoveHighlightInfo.sq[1].y = toY;
\r
9744 ClearPremoveHighlights()
\r
9746 premoveHighlightInfo.sq[0].x = premoveHighlightInfo.sq[0].y =
\r
9747 premoveHighlightInfo.sq[1].x = premoveHighlightInfo.sq[1].y = -1;
\r
9751 ShutDownFrontEnd()
\r
9753 if (saveSettingsOnExit) SaveSettings(settingsFileName);
\r
9754 DeleteClipboardTempFiles();
\r
9760 if (IsIconic(hwndMain))
\r
9761 ShowWindow(hwndMain, SW_RESTORE);
\r
9763 SetActiveWindow(hwndMain);
\r
9767 * Prototypes for animation support routines
\r
9769 static void ScreenSquare(int column, int row, POINT * pt);
\r
9770 static void Tween( POINT * start, POINT * mid, POINT * finish, int factor,
\r
9771 POINT frames[], int * nFrames);
\r
9777 AnimateAtomicCapture(Board board, int fromX, int fromY, int toX, int toY)
\r
9778 { // [HGM] atomic: animate blast wave
\r
9781 explodeInfo.fromX = fromX;
\r
9782 explodeInfo.fromY = fromY;
\r
9783 explodeInfo.toX = toX;
\r
9784 explodeInfo.toY = toY;
\r
9785 for(i=1; i<4*kFactor; i++) {
\r
9786 explodeInfo.radius = (i*180)/(4*kFactor-1);
\r
9787 DrawPosition(FALSE, board);
\r
9788 Sleep(appData.animSpeed);
\r
9790 explodeInfo.radius = 0;
\r
9791 DrawPosition(TRUE, board);
\r
9795 AnimateMove(board, fromX, fromY, toX, toY)
\r
9802 ChessSquare piece;
\r
9803 POINT start, finish, mid;
\r
9804 POINT frames[kFactor * 2 + 1];
\r
9807 if (!appData.animate) return;
\r
9808 if (doingSizing) return;
\r
9809 if (fromY < 0 || fromX < 0) return;
\r
9810 piece = board[fromY][fromX];
\r
9811 if (piece >= EmptySquare) return;
\r
9813 ScreenSquare(fromX, fromY, &start);
\r
9814 ScreenSquare(toX, toY, &finish);
\r
9816 /* All moves except knight jumps move in straight line */
\r
9817 if (!(abs(fromX-toX) == 1 && abs(fromY-toY) == 2 || abs(fromX-toX) == 2 && abs(fromY-toY) == 1)) {
\r
9818 mid.x = start.x + (finish.x - start.x) / 2;
\r
9819 mid.y = start.y + (finish.y - start.y) / 2;
\r
9821 /* Knight: make straight movement then diagonal */
\r
9822 if (abs(toY - fromY) < abs(toX - fromX)) {
\r
9823 mid.x = start.x + (finish.x - start.x) / 2;
\r
9827 mid.y = start.y + (finish.y - start.y) / 2;
\r
9831 /* Don't use as many frames for very short moves */
\r
9832 if (abs(toY - fromY) + abs(toX - fromX) <= 2)
\r
9833 Tween(&start, &mid, &finish, kFactor - 1, frames, &nFrames);
\r
9835 Tween(&start, &mid, &finish, kFactor, frames, &nFrames);
\r
9837 animInfo.from.x = fromX;
\r
9838 animInfo.from.y = fromY;
\r
9839 animInfo.to.x = toX;
\r
9840 animInfo.to.y = toY;
\r
9841 animInfo.lastpos = start;
\r
9842 animInfo.piece = piece;
\r
9843 for (n = 0; n < nFrames; n++) {
\r
9844 animInfo.pos = frames[n];
\r
9845 DrawPosition(FALSE, NULL);
\r
9846 animInfo.lastpos = animInfo.pos;
\r
9847 Sleep(appData.animSpeed);
\r
9849 animInfo.pos = finish;
\r
9850 DrawPosition(FALSE, NULL);
\r
9851 animInfo.piece = EmptySquare;
\r
9852 Explode(board, fromX, fromY, toX, toY);
\r
9855 /* Convert board position to corner of screen rect and color */
\r
9858 ScreenSquare(column, row, pt)
\r
9859 int column; int row; POINT * pt;
\r
9862 pt->x = lineGap + ((BOARD_WIDTH-1)-column) * (squareSize + lineGap);
\r
9863 pt->y = lineGap + row * (squareSize + lineGap);
\r
9865 pt->x = lineGap + column * (squareSize + lineGap);
\r
9866 pt->y = lineGap + ((BOARD_HEIGHT-1)-row) * (squareSize + lineGap);
\r
9870 /* Generate a series of frame coords from start->mid->finish.
\r
9871 The movement rate doubles until the half way point is
\r
9872 reached, then halves back down to the final destination,
\r
9873 which gives a nice slow in/out effect. The algorithmn
\r
9874 may seem to generate too many intermediates for short
\r
9875 moves, but remember that the purpose is to attract the
\r
9876 viewers attention to the piece about to be moved and
\r
9877 then to where it ends up. Too few frames would be less
\r
9881 Tween(start, mid, finish, factor, frames, nFrames)
\r
9882 POINT * start; POINT * mid;
\r
9883 POINT * finish; int factor;
\r
9884 POINT frames[]; int * nFrames;
\r
9886 int n, fraction = 1, count = 0;
\r
9888 /* Slow in, stepping 1/16th, then 1/8th, ... */
\r
9889 for (n = 0; n < factor; n++)
\r
9891 for (n = 0; n < factor; n++) {
\r
9892 frames[count].x = start->x + (mid->x - start->x) / fraction;
\r
9893 frames[count].y = start->y + (mid->y - start->y) / fraction;
\r
9895 fraction = fraction / 2;
\r
9899 frames[count] = *mid;
\r
9902 /* Slow out, stepping 1/2, then 1/4, ... */
\r
9904 for (n = 0; n < factor; n++) {
\r
9905 frames[count].x = finish->x - (finish->x - mid->x) / fraction;
\r
9906 frames[count].y = finish->y - (finish->y - mid->y) / fraction;
\r
9908 fraction = fraction * 2;
\r
9914 SettingsPopUp(ChessProgramState *cps)
\r
9915 { // [HGM] wrapper needed because handles must not be passed through back-end
\r
9916 EngineOptionsPopup(savedHwnd, cps);
\r
9919 int flock(int fid, int code)
\r
9921 HANDLE hFile = (HANDLE) _get_osfhandle(fid);
\r
9925 ov.OffsetHigh = 0;
\r
9927 case 1: LockFileEx(hFile, LOCKFILE_EXCLUSIVE_LOCK, 0, 1024, 0, &ov); break; // LOCK_SH
\r
9928 case 2: LockFileEx(hFile, LOCKFILE_EXCLUSIVE_LOCK, 0, 1024, 0, &ov); break; // LOCK_EX
\r
9929 case 3: UnlockFileEx(hFile, 0, 1024, 0, &ov); break; // LOCK_UN
\r
9930 default: return -1;
\r