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 int 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
2240 int suppressVisibleEffects = 0; // [HGM] kludge to request updating sizeInfo only
\r
2241 if((int)boardSize >= 1000 ) { boardSize -= 1000; suppressVisibleEffects = 1; }
\r
2243 /* [HGM] call with -2 uses old size (for if nr of files, ranks changes) */
\r
2244 if(boardSize == (BoardSize)(-2) ) boardSize = oldBoardSize;
\r
2246 oldRect.left = wpMain.x; //[HGM] placement: remember previous window params
\r
2247 oldRect.top = wpMain.y;
\r
2248 oldRect.right = wpMain.x + wpMain.width;
\r
2249 oldRect.bottom = wpMain.y + wpMain.height;
\r
2251 tinyLayout = sizeInfo[boardSize].tinyLayout;
\r
2252 smallLayout = sizeInfo[boardSize].smallLayout;
\r
2253 squareSize = sizeInfo[boardSize].squareSize;
\r
2254 lineGap = sizeInfo[boardSize].lineGap;
\r
2255 minorSize = 0; /* [HGM] Kludge to see if demagnified pieces need to be shifted */
\r
2257 if( appData.overrideLineGap >= 0 && appData.overrideLineGap <= 5 ) {
\r
2258 lineGap = appData.overrideLineGap;
\r
2261 if (tinyLayout != oldTinyLayout) {
\r
2262 long style = GetWindowLongPtr(hwndMain, GWL_STYLE);
\r
2264 style &= ~WS_SYSMENU;
\r
2265 InsertMenu(hmenu, IDM_Exit, MF_BYCOMMAND, IDM_Minimize,
\r
2266 "&Minimize\tCtrl+F4");
\r
2268 style |= WS_SYSMENU;
\r
2269 RemoveMenu(hmenu, IDM_Minimize, MF_BYCOMMAND);
\r
2271 SetWindowLongPtr(hwndMain, GWL_STYLE, style);
\r
2273 for (i=0; menuBarText[tinyLayout][i]; i++) {
\r
2274 ModifyMenu(hmenu, i, MF_STRING|MF_BYPOSITION|MF_POPUP,
\r
2275 (UINT)GetSubMenu(hmenu, i), T_(menuBarText[tinyLayout][i]));
\r
2277 DrawMenuBar(hwndMain);
\r
2280 boardWidth = BoardWidth(boardSize, BOARD_WIDTH);
\r
2281 boardHeight = BoardWidth(boardSize, BOARD_HEIGHT);
\r
2283 /* Get text area sizes */
\r
2284 hdc = GetDC(hwndMain);
\r
2285 if (appData.clockMode) {
\r
2286 snprintf(buf, MSG_SIZ, _("White: %s"), TimeString(23*60*60*1000L));
\r
2288 snprintf(buf, MSG_SIZ, _("White"));
\r
2290 oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);
\r
2291 GetTextExtentPoint(hdc, buf, strlen(buf), &clockSize);
\r
2292 SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);
\r
2293 str = _("We only care about the height here");
\r
2294 GetTextExtentPoint(hdc, str, strlen(str), &messageSize);
\r
2295 SelectObject(hdc, oldFont);
\r
2296 ReleaseDC(hwndMain, hdc);
\r
2298 /* Compute where everything goes */
\r
2299 if((first.programLogo || second.programLogo) && !tinyLayout) {
\r
2300 /* [HGM] logo: if either logo is on, reserve space for it */
\r
2301 logoHeight = 2*clockSize.cy;
\r
2302 leftLogoRect.left = OUTER_MARGIN;
\r
2303 leftLogoRect.right = leftLogoRect.left + 4*clockSize.cy;
\r
2304 leftLogoRect.top = OUTER_MARGIN;
\r
2305 leftLogoRect.bottom = OUTER_MARGIN + logoHeight;
\r
2307 rightLogoRect.right = OUTER_MARGIN + boardWidth;
\r
2308 rightLogoRect.left = rightLogoRect.right - 4*clockSize.cy;
\r
2309 rightLogoRect.top = OUTER_MARGIN;
\r
2310 rightLogoRect.bottom = OUTER_MARGIN + logoHeight;
\r
2313 whiteRect.left = leftLogoRect.right;
\r
2314 whiteRect.right = OUTER_MARGIN + boardWidth/2 - INNER_MARGIN/2;
\r
2315 whiteRect.top = OUTER_MARGIN;
\r
2316 whiteRect.bottom = whiteRect.top + logoHeight;
\r
2318 blackRect.right = rightLogoRect.left;
\r
2319 blackRect.left = whiteRect.right + INNER_MARGIN;
\r
2320 blackRect.top = whiteRect.top;
\r
2321 blackRect.bottom = whiteRect.bottom;
\r
2323 whiteRect.left = OUTER_MARGIN;
\r
2324 whiteRect.right = whiteRect.left + boardWidth/2 - INNER_MARGIN/2;
\r
2325 whiteRect.top = OUTER_MARGIN;
\r
2326 whiteRect.bottom = whiteRect.top + clockSize.cy;
\r
2328 blackRect.left = whiteRect.right + INNER_MARGIN;
\r
2329 blackRect.right = blackRect.left + boardWidth/2 - 1;
\r
2330 blackRect.top = whiteRect.top;
\r
2331 blackRect.bottom = whiteRect.bottom;
\r
2333 logoHeight = 0; // [HGM] logo: suppress logo after change to tiny layout!
\r
2336 messageRect.left = OUTER_MARGIN + MESSAGE_LINE_LEFTMARGIN;
\r
2337 if (appData.showButtonBar) {
\r
2338 messageRect.right = OUTER_MARGIN + boardWidth // [HGM] logo: expressed independent of clock placement
\r
2339 - N_BUTTONS*BUTTON_WIDTH - MESSAGE_LINE_LEFTMARGIN;
\r
2341 messageRect.right = OUTER_MARGIN + boardWidth;
\r
2343 messageRect.top = whiteRect.bottom + INNER_MARGIN;
\r
2344 messageRect.bottom = messageRect.top + messageSize.cy;
\r
2346 boardRect.left = OUTER_MARGIN;
\r
2347 boardRect.right = boardRect.left + boardWidth;
\r
2348 boardRect.top = messageRect.bottom + INNER_MARGIN;
\r
2349 boardRect.bottom = boardRect.top + boardHeight;
\r
2351 sizeInfo[boardSize].cliWidth = boardRect.right + OUTER_MARGIN;
\r
2352 sizeInfo[boardSize].cliHeight = boardRect.bottom + OUTER_MARGIN;
\r
2353 oldBoardSize = boardSize;
\r
2354 oldTinyLayout = tinyLayout;
\r
2355 winW = 2 * GetSystemMetrics(SM_CXFRAME) + boardRect.right + OUTER_MARGIN;
\r
2356 winH = 2 * GetSystemMetrics(SM_CYFRAME) + GetSystemMetrics(SM_CYMENU) +
\r
2357 GetSystemMetrics(SM_CYCAPTION) + boardRect.bottom + OUTER_MARGIN;
\r
2358 winW *= 1 + twoBoards;
\r
2359 if(suppressVisibleEffects) return; // [HGM] when called for filling sizeInfo only
\r
2360 wpMain.width = winW; // [HGM] placement: set through temporary which can used by initial sizing choice
\r
2361 wpMain.height = winH; // without disturbing window attachments
\r
2362 GetWindowRect(hwndMain, &wrect);
\r
2363 SetWindowPos(hwndMain, NULL, 0, 0, wpMain.width, wpMain.height,
\r
2364 SWP_NOCOPYBITS|SWP_NOZORDER|SWP_NOMOVE);
\r
2366 // [HGM] placement: let attached windows follow size change.
\r
2367 ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, moveHistoryDialog, &wpMoveHistory );
\r
2368 ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, evalGraphDialog, &wpEvalGraph );
\r
2369 ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, engineOutputDialog, &wpEngineOutput );
\r
2370 ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, gameListDialog, &wpGameList );
\r
2371 ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, hwndConsole, &wpConsole );
\r
2373 /* compensate if menu bar wrapped */
\r
2374 GetClientRect(hwndMain, &crect);
\r
2375 offby = boardRect.bottom + OUTER_MARGIN - crect.bottom;
\r
2376 wpMain.height += offby;
\r
2378 case WMSZ_TOPLEFT:
\r
2379 SetWindowPos(hwndMain, NULL,
\r
2380 wrect.right - wpMain.width, wrect.bottom - wpMain.height,
\r
2381 wpMain.width, wpMain.height, SWP_NOCOPYBITS|SWP_NOZORDER);
\r
2384 case WMSZ_TOPRIGHT:
\r
2386 SetWindowPos(hwndMain, NULL,
\r
2387 wrect.left, wrect.bottom - wpMain.height,
\r
2388 wpMain.width, wpMain.height, SWP_NOCOPYBITS|SWP_NOZORDER);
\r
2391 case WMSZ_BOTTOMLEFT:
\r
2393 SetWindowPos(hwndMain, NULL,
\r
2394 wrect.right - wpMain.width, wrect.top,
\r
2395 wpMain.width, wpMain.height, SWP_NOCOPYBITS|SWP_NOZORDER);
\r
2398 case WMSZ_BOTTOMRIGHT:
\r
2402 SetWindowPos(hwndMain, NULL, 0, 0, wpMain.width, wpMain.height,
\r
2403 SWP_NOCOPYBITS|SWP_NOZORDER|SWP_NOMOVE);
\r
2408 for (i = 0; i < N_BUTTONS; i++) {
\r
2409 if (buttonDesc[i].hwnd != NULL) {
\r
2410 DestroyWindow(buttonDesc[i].hwnd);
\r
2411 buttonDesc[i].hwnd = NULL;
\r
2413 if (appData.showButtonBar) {
\r
2414 buttonDesc[i].hwnd =
\r
2415 CreateWindow("BUTTON", buttonDesc[i].label,
\r
2416 WS_VISIBLE | WS_CHILD | BS_PUSHBUTTON,
\r
2417 boardRect.right - BUTTON_WIDTH*(N_BUTTONS-i),
\r
2418 messageRect.top, BUTTON_WIDTH, messageSize.cy, hwndMain,
\r
2419 (HMENU) buttonDesc[i].id,
\r
2420 (HINSTANCE) GetWindowLongPtr(hwndMain, GWLP_HINSTANCE), NULL);
\r
2422 SendMessage(buttonDesc[i].hwnd, WM_SETFONT,
\r
2423 (WPARAM)font[boardSize][MESSAGE_FONT]->hf,
\r
2424 MAKELPARAM(FALSE, 0));
\r
2426 if (buttonDesc[i].id == IDM_Pause)
\r
2427 hwndPause = buttonDesc[i].hwnd;
\r
2428 buttonDesc[i].wndproc = (WNDPROC)
\r
2429 SetWindowLongPtr(buttonDesc[i].hwnd, GWLP_WNDPROC, (LONG_PTR) ButtonProc);
\r
2432 if (gridPen != NULL) DeleteObject(gridPen);
\r
2433 if (highlightPen != NULL) DeleteObject(highlightPen);
\r
2434 if (premovePen != NULL) DeleteObject(premovePen);
\r
2435 if (lineGap != 0) {
\r
2436 logbrush.lbStyle = BS_SOLID;
\r
2437 logbrush.lbColor = RGB(0, 0, 0); /* grid pen color = black */
\r
2439 ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,
\r
2440 lineGap, &logbrush, 0, NULL);
\r
2441 logbrush.lbColor = highlightSquareColor;
\r
2443 ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,
\r
2444 lineGap, &logbrush, 0, NULL);
\r
2446 logbrush.lbColor = premoveHighlightColor;
\r
2448 ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,
\r
2449 lineGap, &logbrush, 0, NULL);
\r
2451 /* [HGM] Loop had to be split in part for vert. and hor. lines */
\r
2452 for (i = 0; i < BOARD_HEIGHT + 1; i++) {
\r
2453 gridEndpoints[i*2].x = boardRect.left + lineGap / 2;
\r
2454 gridEndpoints[i*2].y = gridEndpoints[i*2 + 1].y =
\r
2455 boardRect.top + lineGap / 2 + (i * (squareSize + lineGap));
\r
2456 gridEndpoints[i*2 + 1].x = boardRect.left + lineGap / 2 +
\r
2457 BOARD_WIDTH * (squareSize + lineGap);
\r
2458 gridVertexCounts[i*2] = gridVertexCounts[i*2 + 1] = 2;
\r
2460 for (i = 0; i < BOARD_WIDTH + 1; i++) {
\r
2461 gridEndpoints[i*2 + BOARD_HEIGHT*2 + 2].y = boardRect.top + lineGap / 2;
\r
2462 gridEndpoints[i*2 + BOARD_HEIGHT*2 + 2].x =
\r
2463 gridEndpoints[i*2 + 1 + BOARD_HEIGHT*2 + 2].x = boardRect.left +
\r
2464 lineGap / 2 + (i * (squareSize + lineGap));
\r
2465 gridEndpoints[i*2 + 1 + BOARD_HEIGHT*2 + 2].y =
\r
2466 boardRect.top + BOARD_HEIGHT * (squareSize + lineGap);
\r
2467 gridVertexCounts[i*2] = gridVertexCounts[i*2 + 1] = 2;
\r
2471 /* [HGM] Licensing requirement */
\r
2473 if(gameInfo.variant == VariantGothic) GothicPopUp( GOTHIC, VariantGothic); else
\r
2476 if(gameInfo.variant == VariantFalcon) GothicPopUp( FALCON, VariantFalcon); else
\r
2478 GothicPopUp( "", VariantNormal);
\r
2481 /* if (boardSize == oldBoardSize) return; [HGM] variant might have changed */
\r
2483 /* Load piece bitmaps for this board size */
\r
2484 for (i=0; i<=2; i++) {
\r
2485 for (piece = WhitePawn;
\r
2486 (int) piece < (int) BlackPawn;
\r
2487 piece = (ChessSquare) ((int) piece + 1)) {
\r
2488 if (pieceBitmap[i][piece] != NULL)
\r
2489 DeleteObject(pieceBitmap[i][piece]);
\r
2493 fontBitmapSquareSize = 0; /* [HGM] render: make sure pieces will be recreated, as we might need others now */
\r
2494 // Orthodox Chess pieces
\r
2495 pieceBitmap[0][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "s");
\r
2496 pieceBitmap[0][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "s");
\r
2497 pieceBitmap[0][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "s");
\r
2498 pieceBitmap[0][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "s");
\r
2499 pieceBitmap[0][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "s");
\r
2500 pieceBitmap[1][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "o");
\r
2501 pieceBitmap[1][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "o");
\r
2502 pieceBitmap[1][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "o");
\r
2503 pieceBitmap[1][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "o");
\r
2504 pieceBitmap[1][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "o");
\r
2505 pieceBitmap[2][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "w");
\r
2506 pieceBitmap[2][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "w");
\r
2507 pieceBitmap[2][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "w");
\r
2508 pieceBitmap[2][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "w");
\r
2509 pieceBitmap[2][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "w");
\r
2510 if( gameInfo.variant == VariantShogi && squareSize <= 72 && squareSize >= 33) {
\r
2511 // in Shogi, Hijack the unused Queen for Lance
\r
2512 pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "s");
\r
2513 pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "o");
\r
2514 pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "w");
\r
2516 pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "s");
\r
2517 pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "o");
\r
2518 pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "w");
\r
2521 if(squareSize <= 72 && squareSize >= 33) {
\r
2522 /* A & C are available in most sizes now */
\r
2523 if(squareSize != 49 && squareSize != 72 && squareSize != 33) { // Vortex-like
\r
2524 pieceBitmap[0][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "s");
\r
2525 pieceBitmap[1][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "o");
\r
2526 pieceBitmap[2][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "w");
\r
2527 pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "s");
\r
2528 pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "o");
\r
2529 pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "w");
\r
2530 pieceBitmap[0][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "s");
\r
2531 pieceBitmap[1][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "o");
\r
2532 pieceBitmap[2][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "w");
\r
2533 pieceBitmap[0][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "s");
\r
2534 pieceBitmap[1][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "o");
\r
2535 pieceBitmap[2][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "w");
\r
2536 } else { // Smirf-like
\r
2537 if(gameInfo.variant == VariantSChess) {
\r
2538 pieceBitmap[0][WhiteAngel] = DoLoadBitmap(hInst, "v", squareSize, "s");
\r
2539 pieceBitmap[1][WhiteAngel] = DoLoadBitmap(hInst, "v", squareSize, "o");
\r
2540 pieceBitmap[2][WhiteAngel] = DoLoadBitmap(hInst, "v", squareSize, "w");
\r
2542 pieceBitmap[0][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "s");
\r
2543 pieceBitmap[1][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "o");
\r
2544 pieceBitmap[2][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "w");
\r
2547 if(gameInfo.variant == VariantGothic) { // Vortex-like
\r
2548 pieceBitmap[0][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "s");
\r
2549 pieceBitmap[1][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "o");
\r
2550 pieceBitmap[2][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "w");
\r
2551 } else if(gameInfo.variant == VariantSChess && (squareSize == 49 || squareSize == 72)) {
\r
2552 pieceBitmap[0][WhiteMarshall] = DoLoadBitmap(hInst, "e", squareSize, "s");
\r
2553 pieceBitmap[1][WhiteMarshall] = DoLoadBitmap(hInst, "e", squareSize, "o");
\r
2554 pieceBitmap[2][WhiteMarshall] = DoLoadBitmap(hInst, "e", squareSize, "w");
\r
2555 } else { // WinBoard standard
\r
2556 pieceBitmap[0][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "s");
\r
2557 pieceBitmap[1][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "o");
\r
2558 pieceBitmap[2][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "w");
\r
2563 if(squareSize==72 || squareSize==49 || squareSize==33) { /* experiment with some home-made bitmaps */
\r
2564 pieceBitmap[0][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "s");
\r
2565 pieceBitmap[1][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "o");
\r
2566 pieceBitmap[2][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "w");
\r
2567 pieceBitmap[0][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "s");
\r
2568 pieceBitmap[1][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "o");
\r
2569 pieceBitmap[2][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "w");
\r
2570 pieceBitmap[0][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "s");
\r
2571 pieceBitmap[1][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "o");
\r
2572 pieceBitmap[2][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "w");
\r
2573 pieceBitmap[0][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "s");
\r
2574 pieceBitmap[1][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "o");
\r
2575 pieceBitmap[2][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "w");
\r
2576 pieceBitmap[0][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "s");
\r
2577 pieceBitmap[1][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "o");
\r
2578 pieceBitmap[2][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "w");
\r
2579 pieceBitmap[0][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "s");
\r
2580 pieceBitmap[1][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "o");
\r
2581 pieceBitmap[2][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "w");
\r
2582 pieceBitmap[0][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "s");
\r
2583 pieceBitmap[1][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "o");
\r
2584 pieceBitmap[2][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "w");
\r
2585 pieceBitmap[0][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "s");
\r
2586 pieceBitmap[1][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "o");
\r
2587 pieceBitmap[2][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "w");
\r
2588 pieceBitmap[0][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "s");
\r
2589 pieceBitmap[1][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "o");
\r
2590 pieceBitmap[2][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "w");
\r
2591 pieceBitmap[0][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "s");
\r
2592 pieceBitmap[1][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "o");
\r
2593 pieceBitmap[2][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "w");
\r
2595 if(gameInfo.variant == VariantShogi) { /* promoted Gold represemtations */
\r
2596 pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "wp", squareSize, "s");
\r
2597 pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "wp", squareSize, "o");
\r
2598 pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "w", squareSize, "w");
\r
2599 pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "wn", squareSize, "s");
\r
2600 pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "wn", squareSize, "o");
\r
2601 pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "w", squareSize, "w");
\r
2602 pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "ws", squareSize, "s");
\r
2603 pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "ws", squareSize, "o");
\r
2604 pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "w", squareSize, "w");
\r
2605 pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "wl", squareSize, "s");
\r
2606 pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "wl", squareSize, "o");
\r
2607 pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "w", squareSize, "w");
\r
2609 pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "s");
\r
2610 pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "o");
\r
2611 pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "w");
\r
2612 pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "s");
\r
2613 pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "o");
\r
2614 pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "w");
\r
2615 pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "s");
\r
2616 pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "o");
\r
2617 pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "w");
\r
2618 pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "s");
\r
2619 pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "o");
\r
2620 pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "w");
\r
2623 } else { /* other size, no special bitmaps available. Use smaller symbols */
\r
2624 if((int)boardSize < 2) minorSize = sizeInfo[0].squareSize;
\r
2625 else minorSize = sizeInfo[(int)boardSize - 2].squareSize;
\r
2626 pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "s");
\r
2627 pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "o");
\r
2628 pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "w");
\r
2629 pieceBitmap[0][WhiteCardinal] = DoLoadBitmap(hInst, "b", minorSize, "s");
\r
2630 pieceBitmap[1][WhiteCardinal] = DoLoadBitmap(hInst, "b", minorSize, "o");
\r
2631 pieceBitmap[2][WhiteCardinal] = DoLoadBitmap(hInst, "b", minorSize, "w");
\r
2632 pieceBitmap[0][WhiteDragon] = DoLoadBitmap(hInst, "r", minorSize, "s");
\r
2633 pieceBitmap[1][WhiteDragon] = DoLoadBitmap(hInst, "r", minorSize, "o");
\r
2634 pieceBitmap[2][WhiteDragon] = DoLoadBitmap(hInst, "r", minorSize, "w");
\r
2635 pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "s");
\r
2636 pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "o");
\r
2637 pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "w");
\r
2641 if(gameInfo.variant == VariantShogi && squareSize == 58)
\r
2642 /* special Shogi support in this size */
\r
2643 { for (i=0; i<=2; i++) { /* replace all bitmaps */
\r
2644 for (piece = WhitePawn;
\r
2645 (int) piece < (int) BlackPawn;
\r
2646 piece = (ChessSquare) ((int) piece + 1)) {
\r
2647 if (pieceBitmap[i][piece] != NULL)
\r
2648 DeleteObject(pieceBitmap[i][piece]);
\r
2651 pieceBitmap[0][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "o");
\r
2652 pieceBitmap[0][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "o");
\r
2653 pieceBitmap[0][WhiteBishop] = DoLoadBitmap(hInst, "sb", squareSize, "o");
\r
2654 pieceBitmap[0][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "o");
\r
2655 pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "o");
\r
2656 pieceBitmap[0][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "o");
\r
2657 pieceBitmap[0][WhiteFerz] = DoLoadBitmap(hInst, "sf", squareSize, "o");
\r
2658 pieceBitmap[0][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "o");
\r
2659 pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "su", squareSize, "o");
\r
2660 pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "sh", squareSize, "o");
\r
2661 pieceBitmap[0][WhiteCardinal] = DoLoadBitmap(hInst, "sa", squareSize, "o");
\r
2662 pieceBitmap[0][WhiteDragon] = DoLoadBitmap(hInst, "sc", squareSize, "o");
\r
2663 pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "sg", squareSize, "o");
\r
2664 pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "ss", squareSize, "o");
\r
2665 pieceBitmap[1][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "o");
\r
2666 pieceBitmap[1][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "o");
\r
2667 pieceBitmap[1][WhiteBishop] = DoLoadBitmap(hInst, "sb", squareSize, "o");
\r
2668 pieceBitmap[1][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "o");
\r
2669 pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "o");
\r
2670 pieceBitmap[1][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "o");
\r
2671 pieceBitmap[1][WhiteFerz] = DoLoadBitmap(hInst, "sf", squareSize, "o");
\r
2672 pieceBitmap[1][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "o");
\r
2673 pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "su", squareSize, "o");
\r
2674 pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "sh", squareSize, "o");
\r
2675 pieceBitmap[1][WhiteCardinal] = DoLoadBitmap(hInst, "sa", squareSize, "o");
\r
2676 pieceBitmap[1][WhiteDragon] = DoLoadBitmap(hInst, "sc", squareSize, "o");
\r
2677 pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "sg", squareSize, "o");
\r
2678 pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "ss", squareSize, "o");
\r
2679 pieceBitmap[2][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "w");
\r
2680 pieceBitmap[2][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "w");
\r
2681 pieceBitmap[2][WhiteBishop] = DoLoadBitmap(hInst, "sr", squareSize, "w");
\r
2682 pieceBitmap[2][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "w");
\r
2683 pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "w");
\r
2684 pieceBitmap[2][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "w");
\r
2685 pieceBitmap[2][WhiteFerz] = DoLoadBitmap(hInst, "sw", squareSize, "w");
\r
2686 pieceBitmap[2][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "w");
\r
2687 pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "sp", squareSize, "w");
\r
2688 pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "sn", squareSize, "w");
\r
2689 pieceBitmap[2][WhiteCardinal] = DoLoadBitmap(hInst, "sr", squareSize, "w");
\r
2690 pieceBitmap[2][WhiteDragon] = DoLoadBitmap(hInst, "sr", squareSize, "w");
\r
2691 pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "sl", squareSize, "w");
\r
2692 pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "sw", squareSize, "w");
\r
2698 PieceBitmap(ChessSquare p, int kind)
\r
2700 if ((int) p >= (int) BlackPawn)
\r
2701 p = (ChessSquare) ((int) p - (int) BlackPawn + (int) WhitePawn);
\r
2703 return pieceBitmap[kind][(int) p];
\r
2706 /***************************************************************/
\r
2708 #define MIN(a,b) (((a) < (b)) ? (a) : (b))
\r
2709 #define MAX(a,b) (((a) > (b)) ? (a) : (b))
\r
2711 #define MIN3(a,b,c) (((a) < (b) && (a) < (c)) ? (a) : (((b) < (a) && (b) < (c)) ? (b) : (c)))
\r
2712 #define MAX3(a,b,c) (((a) > (b) && (a) > (c)) ? (a) : (((b) > (a) && (b) > (c)) ? (b) : (c)))
\r
2716 SquareToPos(int row, int column, int * x, int * y)
\r
2719 *x = boardRect.left + lineGap + ((BOARD_WIDTH-1)-column) * (squareSize + lineGap);
\r
2720 *y = boardRect.top + lineGap + row * (squareSize + lineGap);
\r
2722 *x = boardRect.left + lineGap + column * (squareSize + lineGap);
\r
2723 *y = boardRect.top + lineGap + ((BOARD_HEIGHT-1)-row) * (squareSize + lineGap);
\r
2728 DrawCoordsOnDC(HDC hdc)
\r
2730 static char files[] = "0123456789012345678901221098765432109876543210";
\r
2731 static char ranks[] = "wvutsrqponmlkjihgfedcbaabcdefghijklmnopqrstuvw";
\r
2732 char str[2] = { NULLCHAR, NULLCHAR };
\r
2733 int oldMode, oldAlign, x, y, start, i;
\r
2737 if (!appData.showCoords)
\r
2740 start = flipView ? 1-(ONE!='1') : 45+(ONE!='1')-BOARD_HEIGHT;
\r
2742 oldBrush = SelectObject(hdc, GetStockObject(BLACK_BRUSH));
\r
2743 oldMode = SetBkMode(hdc, (appData.monoMode ? OPAQUE : TRANSPARENT));
\r
2744 oldAlign = GetTextAlign(hdc);
\r
2745 oldFont = SelectObject(hdc, font[boardSize][COORD_FONT]->hf);
\r
2747 y = boardRect.top + lineGap;
\r
2748 x = boardRect.left + lineGap + gameInfo.holdingsWidth*(squareSize + lineGap);
\r
2750 SetTextAlign(hdc, TA_LEFT|TA_TOP);
\r
2751 for (i = 0; i < BOARD_HEIGHT; i++) {
\r
2752 str[0] = files[start + i];
\r
2753 ExtTextOut(hdc, x + 2, y + 1, 0, NULL, str, 1, NULL);
\r
2754 y += squareSize + lineGap;
\r
2757 start = flipView ? 23-(BOARD_RGHT-BOARD_LEFT) : 23;
\r
2759 SetTextAlign(hdc, TA_RIGHT|TA_BOTTOM);
\r
2760 for (i = 0; i < BOARD_RGHT - BOARD_LEFT; i++) {
\r
2761 str[0] = ranks[start + i];
\r
2762 ExtTextOut(hdc, x + squareSize - 2, y - 1, 0, NULL, str, 1, NULL);
\r
2763 x += squareSize + lineGap;
\r
2766 SelectObject(hdc, oldBrush);
\r
2767 SetBkMode(hdc, oldMode);
\r
2768 SetTextAlign(hdc, oldAlign);
\r
2769 SelectObject(hdc, oldFont);
\r
2773 DrawGridOnDC(HDC hdc)
\r
2777 if (lineGap != 0) {
\r
2778 oldPen = SelectObject(hdc, gridPen);
\r
2779 PolyPolyline(hdc, gridEndpoints, gridVertexCounts, BOARD_WIDTH+BOARD_HEIGHT + 2);
\r
2780 SelectObject(hdc, oldPen);
\r
2784 #define HIGHLIGHT_PEN 0
\r
2785 #define PREMOVE_PEN 1
\r
2788 DrawHighlightOnDC(HDC hdc, BOOLEAN on, int x, int y, int pen)
\r
2791 HPEN oldPen, hPen;
\r
2792 if (lineGap == 0) return;
\r
2794 x1 = boardRect.left +
\r
2795 lineGap/2 + ((BOARD_WIDTH-1)-x) * (squareSize + lineGap);
\r
2796 y1 = boardRect.top +
\r
2797 lineGap/2 + y * (squareSize + lineGap);
\r
2799 x1 = boardRect.left +
\r
2800 lineGap/2 + x * (squareSize + lineGap);
\r
2801 y1 = boardRect.top +
\r
2802 lineGap/2 + ((BOARD_HEIGHT-1)-y) * (squareSize + lineGap);
\r
2804 hPen = pen ? premovePen : highlightPen;
\r
2805 oldPen = SelectObject(hdc, on ? hPen : gridPen);
\r
2806 MoveToEx(hdc, x1, y1, NULL);
\r
2807 LineTo(hdc, x1 + squareSize + lineGap, y1);
\r
2808 LineTo(hdc, x1 + squareSize + lineGap, y1 + squareSize + lineGap);
\r
2809 LineTo(hdc, x1, y1 + squareSize + lineGap);
\r
2810 LineTo(hdc, x1, y1);
\r
2811 SelectObject(hdc, oldPen);
\r
2815 DrawHighlightsOnDC(HDC hdc, HighlightInfo *h, int pen)
\r
2818 for (i=0; i<2; i++) {
\r
2819 if (h->sq[i].x >= 0 && h->sq[i].y >= 0)
\r
2820 DrawHighlightOnDC(hdc, TRUE,
\r
2821 h->sq[i].x, h->sq[i].y,
\r
2826 /* Note: sqcolor is used only in monoMode */
\r
2827 /* Note that this code is largely duplicated in woptions.c,
\r
2828 function DrawSampleSquare, so that needs to be updated too */
\r
2830 DrawPieceOnDC(HDC hdc, ChessSquare piece, int color, int sqcolor, int x, int y, HDC tmphdc)
\r
2832 HBITMAP oldBitmap;
\r
2836 if (appData.blindfold) return;
\r
2838 /* [AS] Use font-based pieces if needed */
\r
2839 if( fontBitmapSquareSize >= 0 && (squareSize > 32 || gameInfo.variant >= VariantShogi)) {
\r
2840 /* Create piece bitmaps, or do nothing if piece set is up to date */
\r
2841 CreatePiecesFromFont();
\r
2843 if( fontBitmapSquareSize == squareSize ) {
\r
2844 int index = TranslatePieceToFontPiece(piece);
\r
2846 SelectObject( tmphdc, hPieceMask[ index ] );
\r
2848 if(appData.upsideDown ? color==flipView : (flipView && gameInfo.variant == VariantShogi))
\r
2849 StretchBlt(hdc, x+squareSize, y+squareSize, -squareSize, -squareSize, tmphdc, 0, 0, squareSize, squareSize, SRCAND);
\r
2853 squareSize, squareSize,
\r
2858 SelectObject( tmphdc, hPieceFace[ index ] );
\r
2860 if(appData.upsideDown ? color==flipView : (flipView && gameInfo.variant == VariantShogi))
\r
2861 StretchBlt(hdc, x+squareSize, y+squareSize, -squareSize, -squareSize, tmphdc, 0, 0, squareSize, squareSize, SRCPAINT);
\r
2865 squareSize, squareSize,
\r
2874 if (appData.monoMode) {
\r
2875 SelectObject(tmphdc, PieceBitmap(piece,
\r
2876 color == sqcolor ? OUTLINE_PIECE : SOLID_PIECE));
\r
2877 BitBlt(hdc, x, y, squareSize, squareSize, tmphdc, 0, 0,
\r
2878 sqcolor ? SRCCOPY : NOTSRCCOPY);
\r
2880 tmpSize = squareSize;
\r
2882 ((piece >= (int)WhiteNightrider && piece <= WhiteGrasshopper) ||
\r
2883 (piece >= (int)BlackNightrider && piece <= BlackGrasshopper)) ) {
\r
2884 /* [HGM] no bitmap available for promoted pieces in Crazyhouse */
\r
2885 /* Bitmaps of smaller size are substituted, but we have to align them */
\r
2886 x += (squareSize - minorSize)>>1;
\r
2887 y += squareSize - minorSize - 2;
\r
2888 tmpSize = minorSize;
\r
2890 if (color || appData.allWhite ) {
\r
2891 oldBitmap = SelectObject(tmphdc, PieceBitmap(piece, WHITE_PIECE));
\r
2893 oldBrush = SelectObject(hdc, whitePieceBrush);
\r
2894 else oldBrush = SelectObject(hdc, blackPieceBrush);
\r
2895 if(appData.upsideDown && color==flipView)
\r
2896 StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, 0x00B8074A);
\r
2898 BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, 0x00B8074A);
\r
2899 /* Use black for outline of white pieces */
\r
2900 SelectObject(tmphdc, PieceBitmap(piece, OUTLINE_PIECE));
\r
2901 if(appData.upsideDown && color==flipView)
\r
2902 StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, SRCAND);
\r
2904 BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, SRCAND);
\r
2906 /* Use square color for details of black pieces */
\r
2907 oldBitmap = SelectObject(tmphdc, PieceBitmap(piece, SOLID_PIECE));
\r
2908 oldBrush = SelectObject(hdc, blackPieceBrush);
\r
2909 if(appData.upsideDown && !flipView)
\r
2910 StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, 0x00B8074A);
\r
2912 BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, 0x00B8074A);
\r
2914 SelectObject(hdc, oldBrush);
\r
2915 SelectObject(tmphdc, oldBitmap);
\r
2919 /* [AS] Compute a drawing mode for a square, based on specified settings (see DrawTile) */
\r
2920 int GetBackTextureMode( int algo )
\r
2922 int result = BACK_TEXTURE_MODE_DISABLED;
\r
2926 case BACK_TEXTURE_MODE_PLAIN:
\r
2927 result = 1; /* Always use identity map */
\r
2929 case BACK_TEXTURE_MODE_FULL_RANDOM:
\r
2930 result = 1 + (myrandom() % 3); /* Pick a transformation at random */
\r
2938 [AS] Compute and save texture drawing info, otherwise we may not be able
\r
2939 to handle redraws cleanly (as random numbers would always be different).
\r
2941 VOID RebuildTextureSquareInfo()
\r
2951 ZeroMemory( &backTextureSquareInfo, sizeof(backTextureSquareInfo) );
\r
2953 if( liteBackTexture != NULL ) {
\r
2954 if( GetObject( liteBackTexture, sizeof(bi), &bi ) > 0 ) {
\r
2955 lite_w = bi.bmWidth;
\r
2956 lite_h = bi.bmHeight;
\r
2960 if( darkBackTexture != NULL ) {
\r
2961 if( GetObject( darkBackTexture, sizeof(bi), &bi ) > 0 ) {
\r
2962 dark_w = bi.bmWidth;
\r
2963 dark_h = bi.bmHeight;
\r
2967 for( row=0; row<BOARD_HEIGHT; row++ ) {
\r
2968 for( col=0; col<BOARD_WIDTH; col++ ) {
\r
2969 if( (col + row) & 1 ) {
\r
2971 if( lite_w >= squareSize && lite_h >= squareSize ) {
\r
2972 if( lite_w >= squareSize*BOARD_WIDTH )
\r
2973 backTextureSquareInfo[row][col].x = (2*col+1)*lite_w/(2*BOARD_WIDTH) - squareSize/2; /* [HGM] cut out of center of virtual square */
\r
2975 backTextureSquareInfo[row][col].x = col * (lite_w - squareSize) / (BOARD_WIDTH-1); /* [HGM] divide by size-1 in stead of size! */
\r
2976 if( lite_h >= squareSize*BOARD_HEIGHT )
\r
2977 backTextureSquareInfo[row][col].y = (2*(BOARD_HEIGHT-row)-1)*lite_h/(2*BOARD_HEIGHT) - squareSize/2;
\r
2979 backTextureSquareInfo[row][col].y = (BOARD_HEIGHT-1-row) * (lite_h - squareSize) / (BOARD_HEIGHT-1);
\r
2980 backTextureSquareInfo[row][col].mode = GetBackTextureMode(liteBackTextureMode);
\r
2985 if( dark_w >= squareSize && dark_h >= squareSize ) {
\r
2986 if( dark_w >= squareSize*BOARD_WIDTH )
\r
2987 backTextureSquareInfo[row][col].x = (2*col+1) * dark_w / (2*BOARD_WIDTH) - squareSize/2;
\r
2989 backTextureSquareInfo[row][col].x = col * (dark_w - squareSize) / (BOARD_WIDTH-1);
\r
2990 if( dark_h >= squareSize*BOARD_HEIGHT )
\r
2991 backTextureSquareInfo[row][col].y = (2*(BOARD_HEIGHT-row)-1) * dark_h / (2*BOARD_HEIGHT) - squareSize/2;
\r
2993 backTextureSquareInfo[row][col].y = (BOARD_HEIGHT-1-row) * (dark_h - squareSize) / (BOARD_HEIGHT-1);
\r
2994 backTextureSquareInfo[row][col].mode = GetBackTextureMode(darkBackTextureMode);
\r
3001 /* [AS] Arrow highlighting support */
\r
3003 static double A_WIDTH = 5; /* Width of arrow body */
\r
3005 #define A_HEIGHT_FACTOR 6 /* Length of arrow "point", relative to body width */
\r
3006 #define A_WIDTH_FACTOR 3 /* Width of arrow "point", relative to body width */
\r
3008 static double Sqr( double x )
\r
3013 static int Round( double x )
\r
3015 return (int) (x + 0.5);
\r
3018 /* Draw an arrow between two points using current settings */
\r
3019 VOID DrawArrowBetweenPoints( HDC hdc, int s_x, int s_y, int d_x, int d_y )
\r
3022 double dx, dy, j, k, x, y;
\r
3024 if( d_x == s_x ) {
\r
3025 int h = (d_y > s_y) ? +A_WIDTH*A_HEIGHT_FACTOR : -A_WIDTH*A_HEIGHT_FACTOR;
\r
3027 arrow[0].x = s_x + A_WIDTH + 0.5;
\r
3030 arrow[1].x = s_x + A_WIDTH + 0.5;
\r
3031 arrow[1].y = d_y - h;
\r
3033 arrow[2].x = arrow[1].x + A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;
\r
3034 arrow[2].y = d_y - h;
\r
3039 arrow[5].x = arrow[1].x - 2*A_WIDTH + 0.5;
\r
3040 arrow[5].y = d_y - h;
\r
3042 arrow[4].x = arrow[5].x - A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;
\r
3043 arrow[4].y = d_y - h;
\r
3045 arrow[6].x = arrow[1].x - 2*A_WIDTH + 0.5;
\r
3048 else if( d_y == s_y ) {
\r
3049 int w = (d_x > s_x) ? +A_WIDTH*A_HEIGHT_FACTOR : -A_WIDTH*A_HEIGHT_FACTOR;
\r
3052 arrow[0].y = s_y + A_WIDTH + 0.5;
\r
3054 arrow[1].x = d_x - w;
\r
3055 arrow[1].y = s_y + A_WIDTH + 0.5;
\r
3057 arrow[2].x = d_x - w;
\r
3058 arrow[2].y = arrow[1].y + A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;
\r
3063 arrow[5].x = d_x - w;
\r
3064 arrow[5].y = arrow[1].y - 2*A_WIDTH + 0.5;
\r
3066 arrow[4].x = d_x - w;
\r
3067 arrow[4].y = arrow[5].y - A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;
\r
3070 arrow[6].y = arrow[1].y - 2*A_WIDTH + 0.5;
\r
3073 /* [AS] Needed a lot of paper for this! :-) */
\r
3074 dy = (double) (d_y - s_y) / (double) (d_x - s_x);
\r
3075 dx = (double) (s_x - d_x) / (double) (s_y - d_y);
\r
3077 j = sqrt( Sqr(A_WIDTH) / (1.0 + Sqr(dx)) );
\r
3079 k = sqrt( Sqr(A_WIDTH*A_HEIGHT_FACTOR) / (1.0 + Sqr(dy)) );
\r
3084 arrow[0].x = Round(x - j);
\r
3085 arrow[0].y = Round(y + j*dx);
\r
3087 arrow[1].x = Round(arrow[0].x + 2*j); // [HGM] prevent width to be affected by rounding twice
\r
3088 arrow[1].y = Round(arrow[0].y - 2*j*dx);
\r
3091 x = (double) d_x - k;
\r
3092 y = (double) d_y - k*dy;
\r
3095 x = (double) d_x + k;
\r
3096 y = (double) d_y + k*dy;
\r
3099 x = Round(x); y = Round(y); // [HGM] make sure width of shaft is rounded the same way on both ends
\r
3101 arrow[6].x = Round(x - j);
\r
3102 arrow[6].y = Round(y + j*dx);
\r
3104 arrow[2].x = Round(arrow[6].x + 2*j);
\r
3105 arrow[2].y = Round(arrow[6].y - 2*j*dx);
\r
3107 arrow[3].x = Round(arrow[2].x + j*(A_WIDTH_FACTOR-1));
\r
3108 arrow[3].y = Round(arrow[2].y - j*(A_WIDTH_FACTOR-1)*dx);
\r
3113 arrow[5].x = Round(arrow[6].x - j*(A_WIDTH_FACTOR-1));
\r
3114 arrow[5].y = Round(arrow[6].y + j*(A_WIDTH_FACTOR-1)*dx);
\r
3117 Polygon( hdc, arrow, 7 );
\r
3120 /* [AS] Draw an arrow between two squares */
\r
3121 VOID DrawArrowBetweenSquares( HDC hdc, int s_col, int s_row, int d_col, int d_row )
\r
3123 int s_x, s_y, d_x, d_y;
\r
3130 if( s_col == d_col && s_row == d_row ) {
\r
3134 /* Get source and destination points */
\r
3135 SquareToPos( s_row, s_col, &s_x, &s_y);
\r
3136 SquareToPos( d_row, d_col, &d_x, &d_y);
\r
3139 d_y += squareSize / 2 - squareSize / 4; // [HGM] round towards same centers on all sides!
\r
3141 else if( d_y < s_y ) {
\r
3142 d_y += squareSize / 2 + squareSize / 4;
\r
3145 d_y += squareSize / 2;
\r
3149 d_x += squareSize / 2 - squareSize / 4;
\r
3151 else if( d_x < s_x ) {
\r
3152 d_x += squareSize / 2 + squareSize / 4;
\r
3155 d_x += squareSize / 2;
\r
3158 s_x += squareSize / 2;
\r
3159 s_y += squareSize / 2;
\r
3161 /* Adjust width */
\r
3162 A_WIDTH = squareSize / 14.; //[HGM] make float
\r
3165 stLB.lbStyle = BS_SOLID;
\r
3166 stLB.lbColor = appData.highlightArrowColor;
\r
3169 hpen = CreatePen( PS_SOLID, 2, RGB(0x00,0x00,0x00) );
\r
3170 holdpen = SelectObject( hdc, hpen );
\r
3171 hbrush = CreateBrushIndirect( &stLB );
\r
3172 holdbrush = SelectObject( hdc, hbrush );
\r
3174 DrawArrowBetweenPoints( hdc, s_x, s_y, d_x, d_y );
\r
3176 SelectObject( hdc, holdpen );
\r
3177 SelectObject( hdc, holdbrush );
\r
3178 DeleteObject( hpen );
\r
3179 DeleteObject( hbrush );
\r
3182 BOOL HasHighlightInfo()
\r
3184 BOOL result = FALSE;
\r
3186 if( highlightInfo.sq[0].x >= 0 && highlightInfo.sq[0].y >= 0 &&
\r
3187 highlightInfo.sq[1].x >= 0 && highlightInfo.sq[1].y >= 0 )
\r
3195 BOOL IsDrawArrowEnabled()
\r
3197 BOOL result = FALSE;
\r
3199 if( appData.highlightMoveWithArrow && squareSize >= 32 ) {
\r
3206 VOID DrawArrowHighlight( HDC hdc )
\r
3208 if( IsDrawArrowEnabled() && HasHighlightInfo() ) {
\r
3209 DrawArrowBetweenSquares( hdc,
\r
3210 highlightInfo.sq[0].x, highlightInfo.sq[0].y,
\r
3211 highlightInfo.sq[1].x, highlightInfo.sq[1].y );
\r
3215 HRGN GetArrowHighlightClipRegion( HDC hdc )
\r
3217 HRGN result = NULL;
\r
3219 if( HasHighlightInfo() ) {
\r
3220 int x1, y1, x2, y2;
\r
3221 int sx, sy, dx, dy;
\r
3223 SquareToPos(highlightInfo.sq[0].y, highlightInfo.sq[0].x, &x1, &y1 );
\r
3224 SquareToPos(highlightInfo.sq[1].y, highlightInfo.sq[1].x, &x2, &y2 );
\r
3226 sx = MIN( x1, x2 );
\r
3227 sy = MIN( y1, y2 );
\r
3228 dx = MAX( x1, x2 ) + squareSize;
\r
3229 dy = MAX( y1, y2 ) + squareSize;
\r
3231 result = CreateRectRgn( sx, sy, dx, dy );
\r
3238 Warning: this function modifies the behavior of several other functions.
\r
3240 Basically, Winboard is optimized to avoid drawing the whole board if not strictly
\r
3241 needed. Unfortunately, the decision whether or not to perform a full or partial
\r
3242 repaint is scattered all over the place, which is not good for features such as
\r
3243 "arrow highlighting" that require a full repaint of the board.
\r
3245 So, I've tried to patch the code where I thought it made sense (e.g. after or during
\r
3246 user interaction, when speed is not so important) but especially to avoid errors
\r
3247 in the displayed graphics.
\r
3249 In such patched places, I always try refer to this function so there is a single
\r
3250 place to maintain knowledge.
\r
3252 To restore the original behavior, just return FALSE unconditionally.
\r
3254 BOOL IsFullRepaintPreferrable()
\r
3256 BOOL result = FALSE;
\r
3258 if( (appData.highlightLastMove || appData.highlightDragging) && IsDrawArrowEnabled() ) {
\r
3259 /* Arrow may appear on the board */
\r
3267 This function is called by DrawPosition to know whether a full repaint must
\r
3270 Only DrawPosition may directly call this function, which makes use of
\r
3271 some state information. Other function should call DrawPosition specifying
\r
3272 the repaint flag, and can use IsFullRepaintPreferrable if needed.
\r
3274 BOOL DrawPositionNeedsFullRepaint()
\r
3276 BOOL result = FALSE;
\r
3279 Probably a slightly better policy would be to trigger a full repaint
\r
3280 when animInfo.piece changes state (i.e. empty -> non-empty and viceversa),
\r
3281 but animation is fast enough that it's difficult to notice.
\r
3283 if( animInfo.piece == EmptySquare ) {
\r
3284 if( (appData.highlightLastMove || appData.highlightDragging) && IsDrawArrowEnabled() /*&& HasHighlightInfo()*/ ) {
\r
3293 DrawBoardOnDC(HDC hdc, Board board, HDC tmphdc)
\r
3295 int row, column, x, y, square_color, piece_color;
\r
3296 ChessSquare piece;
\r
3298 HDC texture_hdc = NULL;
\r
3300 /* [AS] Initialize background textures if needed */
\r
3301 if( liteBackTexture != NULL || darkBackTexture != NULL ) {
\r
3302 static int backTextureBoardSize; /* [HGM] boardsize: also new texture if board format changed */
\r
3303 if( backTextureSquareSize != squareSize
\r
3304 || backTextureBoardSize != BOARD_WIDTH+BOARD_FILES*BOARD_HEIGHT) {
\r
3305 backTextureBoardSize = BOARD_WIDTH+BOARD_FILES*BOARD_HEIGHT;
\r
3306 backTextureSquareSize = squareSize;
\r
3307 RebuildTextureSquareInfo();
\r
3310 texture_hdc = CreateCompatibleDC( hdc );
\r
3313 for (row = 0; row < BOARD_HEIGHT; row++) {
\r
3314 for (column = 0; column < BOARD_WIDTH; column++) {
\r
3316 SquareToPos(row, column, &x, &y);
\r
3318 piece = board[row][column];
\r
3320 square_color = ((column + row) % 2) == 1;
\r
3321 if( gameInfo.variant == VariantXiangqi ) {
\r
3322 square_color = !InPalace(row, column);
\r
3323 if(BOARD_HEIGHT&1) { if(row==BOARD_HEIGHT/2) square_color ^= 1; }
\r
3324 else if(row < BOARD_HEIGHT/2) square_color ^= 1;
\r
3326 piece_color = (int) piece < (int) BlackPawn;
\r
3329 /* [HGM] holdings file: light square or black */
\r
3330 if(column == BOARD_LEFT-2) {
\r
3331 if( row > BOARD_HEIGHT - gameInfo.holdingsSize - 1 )
\r
3334 DisplayHoldingsCount(hdc, x, y, 0, 0); /* black out */
\r
3338 if(column == BOARD_RGHT + 1 ) {
\r
3339 if( row < gameInfo.holdingsSize )
\r
3342 DisplayHoldingsCount(hdc, x, y, 0, 0);
\r
3346 if(column == BOARD_LEFT-1 ) /* left align */
\r
3347 DisplayHoldingsCount(hdc, x, y, flipView, (int) board[row][column]);
\r
3348 else if( column == BOARD_RGHT) /* right align */
\r
3349 DisplayHoldingsCount(hdc, x, y, !flipView, (int) board[row][column]);
\r
3351 if (appData.monoMode) {
\r
3352 if (piece == EmptySquare) {
\r
3353 BitBlt(hdc, x, y, squareSize, squareSize, 0, 0, 0,
\r
3354 square_color ? WHITENESS : BLACKNESS);
\r
3356 DrawPieceOnDC(hdc, piece, piece_color, square_color, x, y, tmphdc);
\r
3359 else if( appData.useBitmaps && backTextureSquareInfo[row][column].mode > 0 ) {
\r
3360 /* [AS] Draw the square using a texture bitmap */
\r
3361 HBITMAP hbm = SelectObject( texture_hdc, square_color ? liteBackTexture : darkBackTexture );
\r
3362 int r = row, c = column; // [HGM] do not flip board in flipView
\r
3363 if(flipView) { r = BOARD_HEIGHT-1 - r; c = BOARD_WIDTH-1 - c; }
\r
3366 squareSize, squareSize,
\r
3369 backTextureSquareInfo[r][c].mode,
\r
3370 backTextureSquareInfo[r][c].x,
\r
3371 backTextureSquareInfo[r][c].y );
\r
3373 SelectObject( texture_hdc, hbm );
\r
3375 if (piece != EmptySquare) {
\r
3376 DrawPieceOnDC(hdc, piece, piece_color, -1, x, y, tmphdc);
\r
3380 HBRUSH brush = square_color ? lightSquareBrush : darkSquareBrush;
\r
3382 oldBrush = SelectObject(hdc, brush );
\r
3383 BitBlt(hdc, x, y, squareSize, squareSize, 0, 0, 0, PATCOPY);
\r
3384 SelectObject(hdc, oldBrush);
\r
3385 if (piece != EmptySquare)
\r
3386 DrawPieceOnDC(hdc, piece, piece_color, -1, x, y, tmphdc);
\r
3391 if( texture_hdc != NULL ) {
\r
3392 DeleteDC( texture_hdc );
\r
3396 int saveDiagFlag = 0; FILE *diagFile; // [HGM] diag
\r
3397 void fputDW(FILE *f, int x)
\r
3399 fputc(x & 255, f);
\r
3400 fputc(x>>8 & 255, f);
\r
3401 fputc(x>>16 & 255, f);
\r
3402 fputc(x>>24 & 255, f);
\r
3405 #define MAX_CLIPS 200 /* more than enough */
\r
3408 DrawLogoOnDC(HDC hdc, RECT logoRect, HBITMAP logo)
\r
3410 // HBITMAP bufferBitmap;
\r
3415 int w = 100, h = 50;
\r
3417 if(logo == NULL) {
\r
3418 if(!logoHeight) return;
\r
3419 FillRect( hdc, &logoRect, whitePieceBrush );
\r
3421 // GetClientRect(hwndMain, &Rect);
\r
3422 // bufferBitmap = CreateCompatibleBitmap(hdc, Rect.right-Rect.left+1,
\r
3423 // Rect.bottom-Rect.top+1);
\r
3424 tmphdc = CreateCompatibleDC(hdc);
\r
3425 hbm = SelectObject(tmphdc, logo);
\r
3426 if( GetObject( logo, sizeof(bi), &bi ) > 0 ) {
\r
3430 StretchBlt(hdc, logoRect.left, logoRect.top, logoRect.right - logoRect.left,
\r
3431 logoRect.bottom - logoRect.top, tmphdc, 0, 0, w, h, SRCCOPY);
\r
3432 SelectObject(tmphdc, hbm);
\r
3440 HDC hdc = GetDC(hwndMain);
\r
3441 HBITMAP whiteLogo = (HBITMAP) first.programLogo, blackLogo = (HBITMAP) second.programLogo;
\r
3442 if(appData.autoLogo) {
\r
3444 switch(gameMode) { // pick logos based on game mode
\r
3445 case IcsObserving:
\r
3446 whiteLogo = second.programLogo; // ICS logo
\r
3447 blackLogo = second.programLogo;
\r
3450 case IcsPlayingWhite:
\r
3451 if(!appData.zippyPlay) whiteLogo = userLogo;
\r
3452 blackLogo = second.programLogo; // ICS logo
\r
3454 case IcsPlayingBlack:
\r
3455 whiteLogo = second.programLogo; // ICS logo
\r
3456 blackLogo = appData.zippyPlay ? first.programLogo : userLogo;
\r
3458 case TwoMachinesPlay:
\r
3459 if(first.twoMachinesColor[0] == 'b') {
\r
3460 whiteLogo = second.programLogo;
\r
3461 blackLogo = first.programLogo;
\r
3464 case MachinePlaysWhite:
\r
3465 blackLogo = userLogo;
\r
3467 case MachinePlaysBlack:
\r
3468 whiteLogo = userLogo;
\r
3469 blackLogo = first.programLogo;
\r
3472 DrawLogoOnDC(hdc, leftLogoRect, flipClock ? blackLogo : whiteLogo);
\r
3473 DrawLogoOnDC(hdc, rightLogoRect, flipClock ? whiteLogo : blackLogo);
\r
3474 ReleaseDC(hwndMain, hdc);
\r
3479 UpdateLogos(int display)
\r
3480 { // called after loading new engine(s), in tourney or from menu
\r
3481 LoadLogo(&first, 0, FALSE);
\r
3482 LoadLogo(&second, 1, appData.icsActive);
\r
3483 InitDrawingSizes(-2, 0); // adapt layout of board window to presence/absence of logos
\r
3484 if(display) DisplayLogos();
\r
3487 static HDC hdcSeek;
\r
3489 // [HGM] seekgraph
\r
3490 void DrawSeekAxis( int x, int y, int xTo, int yTo )
\r
3493 HPEN hp = SelectObject( hdcSeek, gridPen );
\r
3494 MoveToEx( hdcSeek, boardRect.left+x, boardRect.top+y, &stPt );
\r
3495 LineTo( hdcSeek, boardRect.left+xTo, boardRect.top+yTo );
\r
3496 SelectObject( hdcSeek, hp );
\r
3499 // front-end wrapper for drawing functions to do rectangles
\r
3500 void DrawSeekBackground( int left, int top, int right, int bottom )
\r
3505 if (hdcSeek == NULL) {
\r
3506 hdcSeek = GetDC(hwndMain);
\r
3507 if (!appData.monoMode) {
\r
3508 SelectPalette(hdcSeek, hPal, FALSE);
\r
3509 RealizePalette(hdcSeek);
\r
3512 hp = SelectObject( hdcSeek, gridPen );
\r
3513 rc.top = boardRect.top+top; rc.left = boardRect.left+left;
\r
3514 rc.bottom = boardRect.top+bottom; rc.right = boardRect.left+right;
\r
3515 FillRect( hdcSeek, &rc, lightSquareBrush );
\r
3516 SelectObject( hdcSeek, hp );
\r
3519 // front-end wrapper for putting text in graph
\r
3520 void DrawSeekText(char *buf, int x, int y)
\r
3523 SetBkMode( hdcSeek, TRANSPARENT );
\r
3524 GetTextExtentPoint32( hdcSeek, buf, strlen(buf), &stSize );
\r
3525 TextOut( hdcSeek, boardRect.left+x-3, boardRect.top+y-stSize.cy/2, buf, strlen(buf) );
\r
3528 void DrawSeekDot(int x, int y, int color)
\r
3530 int square = color & 0x80;
\r
3531 HBRUSH oldBrush = SelectObject(hdcSeek,
\r
3532 color == 0 ? markerBrush : color == 1 ? darkSquareBrush : explodeBrush);
\r
3535 Rectangle(hdcSeek, boardRect.left+x - squareSize/9, boardRect.top+y - squareSize/9,
\r
3536 boardRect.left+x + squareSize/9, boardRect.top+y + squareSize/9);
\r
3538 Ellipse(hdcSeek, boardRect.left+x - squareSize/8, boardRect.top+y - squareSize/8,
\r
3539 boardRect.left+x + squareSize/8, boardRect.top+y + squareSize/8);
\r
3540 SelectObject(hdcSeek, oldBrush);
\r
3544 HDCDrawPosition(HDC hdc, BOOLEAN repaint, Board board)
\r
3546 static Board lastReq[2], lastDrawn[2];
\r
3547 static HighlightInfo lastDrawnHighlight, lastDrawnPremove;
\r
3548 static int lastDrawnFlipView = 0;
\r
3549 static int lastReqValid[2] = {0, 0}, lastDrawnValid[2] = {0, 0};
\r
3550 int releaseDC, x, y, x2, y2, row, column, num_clips = 0, i;
\r
3553 HBITMAP bufferBitmap;
\r
3554 HBITMAP oldBitmap;
\r
3556 HRGN clips[MAX_CLIPS];
\r
3557 ChessSquare dragged_piece = EmptySquare;
\r
3558 int nr = twoBoards*partnerUp;
\r
3560 /* I'm undecided on this - this function figures out whether a full
\r
3561 * repaint is necessary on its own, so there's no real reason to have the
\r
3562 * caller tell it that. I think this can safely be set to FALSE - but
\r
3563 * if we trust the callers not to request full repaints unnessesarily, then
\r
3564 * we could skip some clipping work. In other words, only request a full
\r
3565 * redraw when the majority of pieces have changed positions (ie. flip,
\r
3566 * gamestart and similar) --Hawk
\r
3568 Boolean fullrepaint = repaint;
\r
3570 if(DrawSeekGraph()) return; // [HG} seekgraph: suppress printing board if seek graph up
\r
3572 if( DrawPositionNeedsFullRepaint() ) {
\r
3573 fullrepaint = TRUE;
\r
3576 if (board == NULL) {
\r
3577 if (!lastReqValid[nr]) {
\r
3580 board = lastReq[nr];
\r
3582 CopyBoard(lastReq[nr], board);
\r
3583 lastReqValid[nr] = 1;
\r
3586 if (doingSizing) {
\r
3590 if (IsIconic(hwndMain)) {
\r
3594 if (hdc == NULL) {
\r
3595 hdc = GetDC(hwndMain);
\r
3596 if (!appData.monoMode) {
\r
3597 SelectPalette(hdc, hPal, FALSE);
\r
3598 RealizePalette(hdc);
\r
3602 releaseDC = FALSE;
\r
3605 /* Create some work-DCs */
\r
3606 hdcmem = CreateCompatibleDC(hdc);
\r
3607 tmphdc = CreateCompatibleDC(hdc);
\r
3609 /* If dragging is in progress, we temporarely remove the piece */
\r
3610 /* [HGM] or temporarily decrease count if stacked */
\r
3611 /* !! Moved to before board compare !! */
\r
3612 if (dragInfo.from.x >= 0 && dragInfo.pos.x >= 0) {
\r
3613 dragged_piece = board[dragInfo.from.y][dragInfo.from.x];
\r
3614 if(dragInfo.from.x == BOARD_LEFT-2 ) {
\r
3615 if(--board[dragInfo.from.y][dragInfo.from.x+1] == 0 )
\r
3616 board[dragInfo.from.y][dragInfo.from.x] = EmptySquare;
\r
3618 if(dragInfo.from.x == BOARD_RGHT+1) {
\r
3619 if(--board[dragInfo.from.y][dragInfo.from.x-1] == 0 )
\r
3620 board[dragInfo.from.y][dragInfo.from.x] = EmptySquare;
\r
3622 board[dragInfo.from.y][dragInfo.from.x] = gatingPiece;
\r
3625 /* Figure out which squares need updating by comparing the
\r
3626 * newest board with the last drawn board and checking if
\r
3627 * flipping has changed.
\r
3629 if (!fullrepaint && lastDrawnValid[nr] && (nr == 1 || lastDrawnFlipView == flipView)) {
\r
3630 for (row = 0; row < BOARD_HEIGHT; row++) { /* [HGM] true size, not 8 */
\r
3631 for (column = 0; column < BOARD_WIDTH; column++) {
\r
3632 if (lastDrawn[nr][row][column] != board[row][column]) {
\r
3633 SquareToPos(row, column, &x, &y);
\r
3634 clips[num_clips++] =
\r
3635 CreateRectRgn(x, y, x + squareSize, y + squareSize);
\r
3639 if(nr == 0) { // [HGM] dual: no highlights on second board
\r
3640 for (i=0; i<2; i++) {
\r
3641 if (lastDrawnHighlight.sq[i].x != highlightInfo.sq[i].x ||
\r
3642 lastDrawnHighlight.sq[i].y != highlightInfo.sq[i].y) {
\r
3643 if (lastDrawnHighlight.sq[i].x >= 0 &&
\r
3644 lastDrawnHighlight.sq[i].y >= 0) {
\r
3645 SquareToPos(lastDrawnHighlight.sq[i].y,
\r
3646 lastDrawnHighlight.sq[i].x, &x, &y);
\r
3647 clips[num_clips++] =
\r
3648 CreateRectRgn(x - lineGap, y - lineGap,
\r
3649 x + squareSize + lineGap, y + squareSize + lineGap);
\r
3651 if (highlightInfo.sq[i].x >= 0 && highlightInfo.sq[i].y >= 0) {
\r
3652 SquareToPos(highlightInfo.sq[i].y, highlightInfo.sq[i].x, &x, &y);
\r
3653 clips[num_clips++] =
\r
3654 CreateRectRgn(x - lineGap, y - lineGap,
\r
3655 x + squareSize + lineGap, y + squareSize + lineGap);
\r
3659 for (i=0; i<2; i++) {
\r
3660 if (lastDrawnPremove.sq[i].x != premoveHighlightInfo.sq[i].x ||
\r
3661 lastDrawnPremove.sq[i].y != premoveHighlightInfo.sq[i].y) {
\r
3662 if (lastDrawnPremove.sq[i].x >= 0 &&
\r
3663 lastDrawnPremove.sq[i].y >= 0) {
\r
3664 SquareToPos(lastDrawnPremove.sq[i].y,
\r
3665 lastDrawnPremove.sq[i].x, &x, &y);
\r
3666 clips[num_clips++] =
\r
3667 CreateRectRgn(x - lineGap, y - lineGap,
\r
3668 x + squareSize + lineGap, y + squareSize + lineGap);
\r
3670 if (premoveHighlightInfo.sq[i].x >= 0 &&
\r
3671 premoveHighlightInfo.sq[i].y >= 0) {
\r
3672 SquareToPos(premoveHighlightInfo.sq[i].y,
\r
3673 premoveHighlightInfo.sq[i].x, &x, &y);
\r
3674 clips[num_clips++] =
\r
3675 CreateRectRgn(x - lineGap, y - lineGap,
\r
3676 x + squareSize + lineGap, y + squareSize + lineGap);
\r
3680 } else { // nr == 1
\r
3681 partnerHighlightInfo.sq[0].y = board[EP_STATUS-4];
\r
3682 partnerHighlightInfo.sq[0].x = board[EP_STATUS-3];
\r
3683 partnerHighlightInfo.sq[1].y = board[EP_STATUS-2];
\r
3684 partnerHighlightInfo.sq[1].x = board[EP_STATUS-1];
\r
3685 for (i=0; i<2; i++) {
\r
3686 if (partnerHighlightInfo.sq[i].x >= 0 &&
\r
3687 partnerHighlightInfo.sq[i].y >= 0) {
\r
3688 SquareToPos(partnerHighlightInfo.sq[i].y,
\r
3689 partnerHighlightInfo.sq[i].x, &x, &y);
\r
3690 clips[num_clips++] =
\r
3691 CreateRectRgn(x - lineGap, y - lineGap,
\r
3692 x + squareSize + lineGap, y + squareSize + lineGap);
\r
3694 if (oldPartnerHighlight.sq[i].x >= 0 &&
\r
3695 oldPartnerHighlight.sq[i].y >= 0) {
\r
3696 SquareToPos(oldPartnerHighlight.sq[i].y,
\r
3697 oldPartnerHighlight.sq[i].x, &x, &y);
\r
3698 clips[num_clips++] =
\r
3699 CreateRectRgn(x - lineGap, y - lineGap,
\r
3700 x + squareSize + lineGap, y + squareSize + lineGap);
\r
3705 fullrepaint = TRUE;
\r
3708 /* Create a buffer bitmap - this is the actual bitmap
\r
3709 * being written to. When all the work is done, we can
\r
3710 * copy it to the real DC (the screen). This avoids
\r
3711 * the problems with flickering.
\r
3713 GetClientRect(hwndMain, &Rect);
\r
3714 bufferBitmap = CreateCompatibleBitmap(hdc, Rect.right-Rect.left+1,
\r
3715 Rect.bottom-Rect.top+1);
\r
3716 oldBitmap = SelectObject(hdcmem, bufferBitmap);
\r
3717 if (!appData.monoMode) {
\r
3718 SelectPalette(hdcmem, hPal, FALSE);
\r
3721 /* Create clips for dragging */
\r
3722 if (!fullrepaint) {
\r
3723 if (dragInfo.from.x >= 0) {
\r
3724 SquareToPos(dragInfo.from.y, dragInfo.from.x, &x, &y);
\r
3725 clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);
\r
3727 if (dragInfo.start.x >= 0) {
\r
3728 SquareToPos(dragInfo.start.y, dragInfo.start.x, &x, &y);
\r
3729 clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);
\r
3731 if (dragInfo.pos.x >= 0) {
\r
3732 x = dragInfo.pos.x - squareSize / 2;
\r
3733 y = dragInfo.pos.y - squareSize / 2;
\r
3734 clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);
\r
3736 if (dragInfo.lastpos.x >= 0) {
\r
3737 x = dragInfo.lastpos.x - squareSize / 2;
\r
3738 y = dragInfo.lastpos.y - squareSize / 2;
\r
3739 clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);
\r
3743 /* Are we animating a move?
\r
3745 * - remove the piece from the board (temporarely)
\r
3746 * - calculate the clipping region
\r
3748 if (!fullrepaint) {
\r
3749 if (animInfo.piece != EmptySquare) {
\r
3750 board[animInfo.from.y][animInfo.from.x] = EmptySquare;
\r
3751 x = boardRect.left + animInfo.lastpos.x;
\r
3752 y = boardRect.top + animInfo.lastpos.y;
\r
3753 x2 = boardRect.left + animInfo.pos.x;
\r
3754 y2 = boardRect.top + animInfo.pos.y;
\r
3755 clips[num_clips++] = CreateRectRgn(MIN(x,x2), MIN(y,y2), MAX(x,x2)+squareSize, MAX(y,y2)+squareSize);
\r
3756 /* Slight kludge. The real problem is that after AnimateMove is
\r
3757 done, the position on the screen does not match lastDrawn.
\r
3758 This currently causes trouble only on e.p. captures in
\r
3759 atomic, where the piece moves to an empty square and then
\r
3760 explodes. The old and new positions both had an empty square
\r
3761 at the destination, but animation has drawn a piece there and
\r
3762 we have to remember to erase it. [HGM] moved until after setting lastDrawn */
\r
3763 lastDrawn[0][animInfo.to.y][animInfo.to.x] = animInfo.piece;
\r
3767 /* No clips? Make sure we have fullrepaint set to TRUE */
\r
3768 if (num_clips == 0)
\r
3769 fullrepaint = TRUE;
\r
3771 /* Set clipping on the memory DC */
\r
3772 if (!fullrepaint) {
\r
3773 SelectClipRgn(hdcmem, clips[0]);
\r
3774 for (x = 1; x < num_clips; x++) {
\r
3775 if (ExtSelectClipRgn(hdcmem, clips[x], RGN_OR) == ERROR)
\r
3776 abort(); // this should never ever happen!
\r
3780 /* Do all the drawing to the memory DC */
\r
3781 if(explodeInfo.radius) { // [HGM] atomic
\r
3783 int x, y, r=(explodeInfo.radius * squareSize)/100;
\r
3784 ChessSquare piece = board[explodeInfo.fromY][explodeInfo.fromX];
\r
3785 board[explodeInfo.fromY][explodeInfo.fromX] = EmptySquare; // suppress display of capturer
\r
3786 SquareToPos(explodeInfo.toY, explodeInfo.toX, &x, &y);
\r
3787 x += squareSize/2;
\r
3788 y += squareSize/2;
\r
3789 if(!fullrepaint) {
\r
3790 clips[num_clips] = CreateRectRgn(x-r, y-r, x+r, y+r);
\r
3791 ExtSelectClipRgn(hdcmem, clips[num_clips++], RGN_OR);
\r
3793 DrawGridOnDC(hdcmem);
\r
3794 DrawHighlightsOnDC(hdcmem, &highlightInfo, HIGHLIGHT_PEN);
\r
3795 DrawHighlightsOnDC(hdcmem, &premoveHighlightInfo, PREMOVE_PEN);
\r
3796 DrawBoardOnDC(hdcmem, board, tmphdc);
\r
3797 board[explodeInfo.fromY][explodeInfo.fromX] = piece;
\r
3798 oldBrush = SelectObject(hdcmem, explodeBrush);
\r
3799 Ellipse(hdcmem, x-r, y-r, x+r, y+r);
\r
3800 SelectObject(hdcmem, oldBrush);
\r
3802 DrawGridOnDC(hdcmem);
\r
3803 if(nr == 0) { // [HGM] dual: decide which highlights to draw
\r
3804 DrawHighlightsOnDC(hdcmem, &highlightInfo, HIGHLIGHT_PEN);
\r
3805 DrawHighlightsOnDC(hdcmem, &premoveHighlightInfo, PREMOVE_PEN);
\r
3807 DrawHighlightsOnDC(hdcmem, &partnerHighlightInfo, HIGHLIGHT_PEN);
\r
3808 oldPartnerHighlight = partnerHighlightInfo;
\r
3810 DrawBoardOnDC(hdcmem, board, tmphdc);
\r
3812 if(nr == 0) // [HGM] dual: markers only on left board
\r
3813 for (row = 0; row < BOARD_HEIGHT; row++) {
\r
3814 for (column = 0; column < BOARD_WIDTH; column++) {
\r
3815 if (marker[row][column]) { // marker changes only occur with full repaint!
\r
3816 HBRUSH oldBrush = SelectObject(hdcmem,
\r
3817 marker[row][column] == 2 ? markerBrush : explodeBrush);
\r
3818 SquareToPos(row, column, &x, &y);
\r
3819 Ellipse(hdcmem, x + squareSize/4, y + squareSize/4,
\r
3820 x + 3*squareSize/4, y + 3*squareSize/4);
\r
3821 SelectObject(hdcmem, oldBrush);
\r
3826 if( appData.highlightMoveWithArrow ) {
\r
3827 DrawArrowHighlight(hdcmem);
\r
3830 DrawCoordsOnDC(hdcmem);
\r
3832 CopyBoard(lastDrawn[nr], board); /* [HGM] Moved to here from end of routine, */
\r
3833 /* to make sure lastDrawn contains what is actually drawn */
\r
3835 /* Put the dragged piece back into place and draw it (out of place!) */
\r
3836 if (dragged_piece != EmptySquare) {
\r
3837 /* [HGM] or restack */
\r
3838 if(dragInfo.from.x == BOARD_LEFT-2 )
\r
3839 board[dragInfo.from.y][dragInfo.from.x+1]++;
\r
3841 if(dragInfo.from.x == BOARD_RGHT+1 )
\r
3842 board[dragInfo.from.y][dragInfo.from.x-1]++;
\r
3843 board[dragInfo.from.y][dragInfo.from.x] = dragged_piece;
\r
3844 x = dragInfo.pos.x - squareSize / 2;
\r
3845 y = dragInfo.pos.y - squareSize / 2;
\r
3846 DrawPieceOnDC(hdcmem, dragInfo.piece,
\r
3847 ((int) dragInfo.piece < (int) BlackPawn),
\r
3848 (dragInfo.from.y + dragInfo.from.x) % 2, x, y, tmphdc);
\r
3851 /* Put the animated piece back into place and draw it */
\r
3852 if (animInfo.piece != EmptySquare) {
\r
3853 board[animInfo.from.y][animInfo.from.x] = animInfo.piece;
\r
3854 x = boardRect.left + animInfo.pos.x;
\r
3855 y = boardRect.top + animInfo.pos.y;
\r
3856 DrawPieceOnDC(hdcmem, animInfo.piece,
\r
3857 ((int) animInfo.piece < (int) BlackPawn),
\r
3858 (animInfo.from.y + animInfo.from.x) % 2, x, y, tmphdc);
\r
3861 /* Release the bufferBitmap by selecting in the old bitmap
\r
3862 * and delete the memory DC
\r
3864 SelectObject(hdcmem, oldBitmap);
\r
3867 /* Set clipping on the target DC */
\r
3868 if (!fullrepaint) {
\r
3869 if(nr == 1) for (x = 0; x < num_clips; x++) { // [HGM] dual: translate clips
\r
3871 GetRgnBox(clips[x], &rect);
\r
3872 DeleteObject(clips[x]);
\r
3873 clips[x] = CreateRectRgn(rect.left + wpMain.width/2, rect.top,
\r
3874 rect.right + wpMain.width/2, rect.bottom);
\r
3876 SelectClipRgn(hdc, clips[0]);
\r
3877 for (x = 1; x < num_clips; x++) {
\r
3878 if (ExtSelectClipRgn(hdc, clips[x], RGN_OR) == ERROR)
\r
3879 abort(); // this should never ever happen!
\r
3883 /* Copy the new bitmap onto the screen in one go.
\r
3884 * This way we avoid any flickering
\r
3886 oldBitmap = SelectObject(tmphdc, bufferBitmap);
\r
3887 BitBlt(hdc, boardRect.left + twoBoards*partnerUp*wpMain.width/2, boardRect.top, // [HGM] dual
\r
3888 boardRect.right - boardRect.left,
\r
3889 boardRect.bottom - boardRect.top,
\r
3890 tmphdc, boardRect.left, boardRect.top, SRCCOPY);
\r
3891 if(saveDiagFlag) {
\r
3892 BITMAP b; int i, j=0, m, w, wb, fac=0; char *pData;
\r
3893 BITMAPINFOHEADER bih; int color[16], nrColors=0;
\r
3895 GetObject(bufferBitmap, sizeof(b), &b);
\r
3896 if(pData = malloc(b.bmWidthBytes*b.bmHeight + 10000)) {
\r
3897 bih.biSize = sizeof(BITMAPINFOHEADER);
\r
3898 bih.biWidth = b.bmWidth;
\r
3899 bih.biHeight = b.bmHeight;
\r
3901 bih.biBitCount = b.bmBitsPixel;
\r
3902 bih.biCompression = 0;
\r
3903 bih.biSizeImage = b.bmWidthBytes*b.bmHeight;
\r
3904 bih.biXPelsPerMeter = 0;
\r
3905 bih.biYPelsPerMeter = 0;
\r
3906 bih.biClrUsed = 0;
\r
3907 bih.biClrImportant = 0;
\r
3908 // fprintf(diagFile, "t=%d\nw=%d\nh=%d\nB=%d\nP=%d\nX=%d\n",
\r
3909 // b.bmType, b.bmWidth, b.bmHeight, b.bmWidthBytes, b.bmPlanes, b.bmBitsPixel);
\r
3910 GetDIBits(tmphdc,bufferBitmap,0,b.bmHeight,pData,(BITMAPINFO*)&bih,DIB_RGB_COLORS);
\r
3911 // fprintf(diagFile, "%8x\n", (int) pData);
\r
3913 wb = b.bmWidthBytes;
\r
3915 for(i=0; i<wb*(b.bmHeight - boardRect.top + OUTER_MARGIN)>>2; i++) {
\r
3916 int k = ((int*) pData)[i];
\r
3917 for(j=0; j<nrColors; j++) if(color[j] == k) break;
\r
3918 if(j >= 16) break;
\r
3920 if(j >= nrColors) nrColors = j+1;
\r
3922 if(j<16) { // 16 colors is enough. Compress to 4 bits per pixel
\r
3924 for(i=0; i<b.bmHeight - boardRect.top + OUTER_MARGIN; i++) {
\r
3925 for(w=0; w<(wb>>2); w+=2) {
\r
3926 int k = ((int*) pData)[(wb*i>>2) + w];
\r
3927 for(j=0; j<nrColors; j++) if(color[j] == k) break;
\r
3928 k = ((int*) pData)[(wb*i>>2) + w + 1];
\r
3929 for(m=0; m<nrColors; m++) if(color[m] == k) break;
\r
3930 pData[p++] = m | j<<4;
\r
3932 while(p&3) pData[p++] = 0;
\r
3935 wb = ((wb+31)>>5)<<2;
\r
3937 // write BITMAPFILEHEADER
\r
3938 fprintf(diagFile, "BM");
\r
3939 fputDW(diagFile, wb*(b.bmHeight - boardRect.top + OUTER_MARGIN)+0x36 + (fac?64:0));
\r
3940 fputDW(diagFile, 0);
\r
3941 fputDW(diagFile, 0x36 + (fac?64:0));
\r
3942 // write BITMAPINFOHEADER
\r
3943 fputDW(diagFile, 40);
\r
3944 fputDW(diagFile, b.bmWidth);
\r
3945 fputDW(diagFile, b.bmHeight - boardRect.top + OUTER_MARGIN);
\r
3946 if(fac) fputDW(diagFile, 0x040001); // planes and bits/pixel
\r
3947 else fputDW(diagFile, 0x200001); // planes and bits/pixel
\r
3948 fputDW(diagFile, 0);
\r
3949 fputDW(diagFile, 0);
\r
3950 fputDW(diagFile, 0);
\r
3951 fputDW(diagFile, 0);
\r
3952 fputDW(diagFile, 0);
\r
3953 fputDW(diagFile, 0);
\r
3954 // write color table
\r
3956 for(i=0; i<16; i++) fputDW(diagFile, color[i]);
\r
3957 // write bitmap data
\r
3958 for(i=0; i<wb*(b.bmHeight - boardRect.top + OUTER_MARGIN); i++)
\r
3959 fputc(pData[i], diagFile);
\r
3964 SelectObject(tmphdc, oldBitmap);
\r
3966 /* Massive cleanup */
\r
3967 for (x = 0; x < num_clips; x++)
\r
3968 DeleteObject(clips[x]);
\r
3971 DeleteObject(bufferBitmap);
\r
3974 ReleaseDC(hwndMain, hdc);
\r
3976 if (lastDrawnFlipView != flipView && nr == 0) {
\r
3978 CheckMenuItem(GetMenu(hwndMain),IDM_FlipView, MF_BYCOMMAND|MF_CHECKED);
\r
3980 CheckMenuItem(GetMenu(hwndMain),IDM_FlipView, MF_BYCOMMAND|MF_UNCHECKED);
\r
3983 /* CopyBoard(lastDrawn, board);*/
\r
3984 lastDrawnHighlight = highlightInfo;
\r
3985 lastDrawnPremove = premoveHighlightInfo;
\r
3986 lastDrawnFlipView = flipView;
\r
3987 lastDrawnValid[nr] = 1;
\r
3990 /* [HGM] diag: Save the current board display to the given open file and close the file */
\r
3995 saveDiagFlag = 1; diagFile = f;
\r
3996 HDCDrawPosition(NULL, TRUE, NULL);
\r
4004 /*---------------------------------------------------------------------------*\
\r
4005 | CLIENT PAINT PROCEDURE
\r
4006 | This is the main event-handler for the WM_PAINT message.
\r
4008 \*---------------------------------------------------------------------------*/
\r
4010 PaintProc(HWND hwnd)
\r
4016 if((hdc = BeginPaint(hwnd, &ps))) {
\r
4017 if (IsIconic(hwnd)) {
\r
4018 DrawIcon(hdc, 2, 2, iconCurrent);
\r
4020 if (!appData.monoMode) {
\r
4021 SelectPalette(hdc, hPal, FALSE);
\r
4022 RealizePalette(hdc);
\r
4024 HDCDrawPosition(hdc, 1, NULL);
\r
4025 if(twoBoards) { // [HGM] dual: also redraw other board in other orientation
\r
4026 flipView = !flipView; partnerUp = !partnerUp;
\r
4027 HDCDrawPosition(hdc, 1, NULL);
\r
4028 flipView = !flipView; partnerUp = !partnerUp;
\r
4031 SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);
\r
4032 ExtTextOut(hdc, messageRect.left, messageRect.top,
\r
4033 ETO_CLIPPED|ETO_OPAQUE,
\r
4034 &messageRect, messageText, strlen(messageText), NULL);
\r
4035 SelectObject(hdc, oldFont);
\r
4036 DisplayBothClocks();
\r
4039 EndPaint(hwnd,&ps);
\r
4047 * If the user selects on a border boundary, return -1; if off the board,
\r
4048 * return -2. Otherwise map the event coordinate to the square.
\r
4049 * The offset boardRect.left or boardRect.top must already have been
\r
4050 * subtracted from x.
\r
4052 int EventToSquare(x, limit)
\r
4060 if ((x % (squareSize + lineGap)) >= squareSize)
\r
4062 x /= (squareSize + lineGap);
\r
4074 DropEnable dropEnables[] = {
\r
4075 { 'P', DP_Pawn, N_("Pawn") },
\r
4076 { 'N', DP_Knight, N_("Knight") },
\r
4077 { 'B', DP_Bishop, N_("Bishop") },
\r
4078 { 'R', DP_Rook, N_("Rook") },
\r
4079 { 'Q', DP_Queen, N_("Queen") },
\r
4083 SetupDropMenu(HMENU hmenu)
\r
4085 int i, count, enable;
\r
4087 extern char white_holding[], black_holding[];
\r
4088 char item[MSG_SIZ];
\r
4090 for (i=0; i<sizeof(dropEnables)/sizeof(DropEnable); i++) {
\r
4091 p = strchr(gameMode == IcsPlayingWhite ? white_holding : black_holding,
\r
4092 dropEnables[i].piece);
\r
4094 while (p && *p++ == dropEnables[i].piece) count++;
\r
4095 snprintf(item, MSG_SIZ, "%s %d", T_(dropEnables[i].name), count);
\r
4096 enable = count > 0 || !appData.testLegality
\r
4097 /*!!temp:*/ || (gameInfo.variant == VariantCrazyhouse
\r
4098 && !appData.icsActive);
\r
4099 ModifyMenu(hmenu, dropEnables[i].command,
\r
4100 MF_BYCOMMAND | (enable ? MF_ENABLED : MF_GRAYED) | MF_STRING,
\r
4101 dropEnables[i].command, item);
\r
4105 void DragPieceBegin(int x, int y, Boolean instantly)
\r
4107 dragInfo.lastpos.x = boardRect.left + x;
\r
4108 dragInfo.lastpos.y = boardRect.top + y;
\r
4109 if(instantly) dragInfo.pos = dragInfo.lastpos;
\r
4110 dragInfo.from.x = fromX;
\r
4111 dragInfo.from.y = fromY;
\r
4112 dragInfo.piece = boards[currentMove][fromY][fromX];
\r
4113 dragInfo.start = dragInfo.from;
\r
4114 SetCapture(hwndMain);
\r
4117 void DragPieceEnd(int x, int y)
\r
4120 dragInfo.start.x = dragInfo.start.y = -1;
\r
4121 dragInfo.from = dragInfo.start;
\r
4122 dragInfo.pos = dragInfo.lastpos = dragInfo.start;
\r
4125 void ChangeDragPiece(ChessSquare piece)
\r
4127 dragInfo.piece = piece;
\r
4130 /* Event handler for mouse messages */
\r
4132 MouseEvent(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
\r
4136 static int recursive = 0;
\r
4138 BOOLEAN forceFullRepaint = IsFullRepaintPreferrable(); /* [AS] */
\r
4141 if (message == WM_MBUTTONUP) {
\r
4142 /* Hideous kludge to fool TrackPopupMenu into paying attention
\r
4143 to the middle button: we simulate pressing the left button too!
\r
4145 PostMessage(hwnd, WM_LBUTTONDOWN, wParam, lParam);
\r
4146 PostMessage(hwnd, WM_LBUTTONUP, wParam, lParam);
\r
4152 pt.x = LOWORD(lParam);
\r
4153 pt.y = HIWORD(lParam);
\r
4154 x = EventToSquare(pt.x - boardRect.left, BOARD_WIDTH);
\r
4155 y = EventToSquare(pt.y - boardRect.top, BOARD_HEIGHT);
\r
4156 if (!flipView && y >= 0) {
\r
4157 y = BOARD_HEIGHT - 1 - y;
\r
4159 if (flipView && x >= 0) {
\r
4160 x = BOARD_WIDTH - 1 - x;
\r
4163 shiftKey = GetKeyState(VK_SHIFT) < 0; // [HGM] remember last shift status
\r
4165 switch (message) {
\r
4166 case WM_LBUTTONDOWN:
\r
4167 if (PtInRect((LPRECT) &whiteRect, pt)) {
\r
4168 ClockClick(flipClock); break;
\r
4169 } else if (PtInRect((LPRECT) &blackRect, pt)) {
\r
4170 ClockClick(!flipClock); break;
\r
4172 dragInfo.start.x = dragInfo.start.y = -1;
\r
4173 dragInfo.from = dragInfo.start;
\r
4174 if(fromX == -1 && frozen) { // not sure where this is for
\r
4175 fromX = fromY = -1;
\r
4176 DrawPosition(forceFullRepaint || FALSE, NULL); /* [AS] */
\r
4179 LeftClick(Press, pt.x - boardRect.left, pt.y - boardRect.top);
\r
4180 DrawPosition(TRUE, NULL);
\r
4183 case WM_LBUTTONUP:
\r
4184 LeftClick(Release, pt.x - boardRect.left, pt.y - boardRect.top);
\r
4185 DrawPosition(TRUE, NULL);
\r
4188 case WM_MOUSEMOVE:
\r
4189 if(SeekGraphClick(Press, pt.x - boardRect.left, pt.y - boardRect.top, 1)) break;
\r
4190 if(PromoScroll(pt.x - boardRect.left, pt.y - boardRect.top)) break;
\r
4191 MovePV(pt.x - boardRect.left, pt.y - boardRect.top, boardRect.bottom - boardRect.top);
\r
4192 if ((appData.animateDragging || appData.highlightDragging)
\r
4193 && (wParam & MK_LBUTTON)
\r
4194 && dragInfo.from.x >= 0)
\r
4196 BOOL full_repaint = FALSE;
\r
4198 if (appData.animateDragging) {
\r
4199 dragInfo.pos = pt;
\r
4201 if (appData.highlightDragging) {
\r
4202 SetHighlights(fromX, fromY, x, y);
\r
4203 if( IsDrawArrowEnabled() && (x < 0 || x >= BOARD_WIDTH || y < 0 || y >= BOARD_HEIGHT) ) {
\r
4204 full_repaint = TRUE;
\r
4208 DrawPosition( full_repaint, NULL);
\r
4210 dragInfo.lastpos = dragInfo.pos;
\r
4214 case WM_MOUSEWHEEL: // [DM]
\r
4215 { static int lastDir = 0; // [HGM] build in some hysteresis to avoid spurious events
\r
4216 /* Mouse Wheel is being rolled forward
\r
4217 * Play moves forward
\r
4219 if((short)HIWORD(wParam) < 0 && currentMove < forwardMostMove)
\r
4220 { if(lastDir == 1) ForwardEvent(); else lastDir = 1; } // [HGM] suppress first event in direction
\r
4221 /* Mouse Wheel is being rolled backward
\r
4222 * Play moves backward
\r
4224 if((short)HIWORD(wParam) > 0 && currentMove > backwardMostMove)
\r
4225 { if(lastDir == -1) BackwardEvent(); else lastDir = -1; }
\r
4229 case WM_MBUTTONUP:
\r
4230 case WM_RBUTTONUP:
\r
4232 RightClick(Release, pt.x - boardRect.left, pt.y - boardRect.top, &fromX, &fromY);
\r
4235 case WM_MBUTTONDOWN:
\r
4236 case WM_RBUTTONDOWN:
\r
4239 fromX = fromY = -1;
\r
4240 dragInfo.pos.x = dragInfo.pos.y = -1;
\r
4241 dragInfo.start.x = dragInfo.start.y = -1;
\r
4242 dragInfo.from = dragInfo.start;
\r
4243 dragInfo.lastpos = dragInfo.pos;
\r
4244 if (appData.highlightDragging) {
\r
4245 ClearHighlights();
\r
4248 /* [HGM] right mouse button in clock area edit-game mode ups clock */
\r
4249 if (PtInRect((LPRECT) &whiteRect, pt)) {
\r
4250 if (GetKeyState(VK_SHIFT) < 0) AdjustClock(flipClock, 1);
\r
4251 } else if (PtInRect((LPRECT) &blackRect, pt)) {
\r
4252 if (GetKeyState(VK_SHIFT) < 0) AdjustClock(!flipClock, 1);
\r
4256 DrawPosition(TRUE, NULL);
\r
4258 menuNr = RightClick(Press, pt.x - boardRect.left, pt.y - boardRect.top, &fromX, &fromY);
\r
4261 if (message == WM_MBUTTONDOWN) {
\r
4262 buttonCount = 3; /* even if system didn't think so */
\r
4263 if (wParam & MK_SHIFT)
\r
4264 MenuPopup(hwnd, pt, LoadMenu(hInst, "BlackPieceMenu"), -1);
\r
4266 MenuPopup(hwnd, pt, LoadMenu(hInst, "WhitePieceMenu"), -1);
\r
4267 } else { /* message == WM_RBUTTONDOWN */
\r
4268 /* Just have one menu, on the right button. Windows users don't
\r
4269 think to try the middle one, and sometimes other software steals
\r
4270 it, or it doesn't really exist. */
\r
4271 if(gameInfo.variant != VariantShogi)
\r
4272 MenuPopup(hwnd, pt, LoadMenu(hInst, "PieceMenu"), -1);
\r
4274 MenuPopup(hwnd, pt, LoadMenu(hInst, "ShogiPieceMenu"), -1);
\r
4278 SetCapture(hwndMain);
\r
4281 hmenu = LoadMenu(hInst, "DropPieceMenu");
\r
4282 SetupDropMenu(hmenu);
\r
4283 MenuPopup(hwnd, pt, hmenu, -1);
\r
4293 /* Preprocess messages for buttons in main window */
\r
4295 ButtonProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
\r
4297 int id = GetWindowLongPtr(hwnd, GWLP_ID);
\r
4300 for (i=0; i<N_BUTTONS; i++) {
\r
4301 if (buttonDesc[i].id == id) break;
\r
4303 if (i == N_BUTTONS) return 0;
\r
4304 switch (message) {
\r
4309 dir = (wParam == VK_LEFT) ? -1 : 1;
\r
4310 SetFocus(buttonDesc[(i + dir + N_BUTTONS) % N_BUTTONS].hwnd);
\r
4317 SendMessage(hwndMain, WM_COMMAND, MAKEWPARAM(buttonDesc[i].id, 0), 0);
\r
4320 if (appData.icsActive && (isalpha((char)wParam) || wParam == '0')) {
\r
4321 // [HGM] movenum: only letters or leading zero should go to ICS input
\r
4322 HWND h = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
4323 if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);
\r
4325 SendMessage(h, WM_CHAR, wParam, lParam);
\r
4327 } else if (isalpha((char)wParam) || isdigit((char)wParam)){
\r
4328 TypeInEvent((char)wParam);
\r
4334 return CallWindowProc(buttonDesc[i].wndproc, hwnd, message, wParam, lParam);
\r
4337 /* Process messages for Promotion dialog box */
\r
4339 Promotion(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
4343 switch (message) {
\r
4344 case WM_INITDIALOG: /* message: initialize dialog box */
\r
4345 /* Center the dialog over the application window */
\r
4346 CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));
\r
4347 Translate(hDlg, DLG_PromotionKing);
\r
4348 ShowWindow(GetDlgItem(hDlg, PB_King),
\r
4349 (!appData.testLegality || gameInfo.variant == VariantSuicide ||
\r
4350 gameInfo.variant == VariantSpartan && !WhiteOnMove(currentMove) ||
\r
4351 gameInfo.variant == VariantGiveaway || gameInfo.variant == VariantSuper ) ?
\r
4352 SW_SHOW : SW_HIDE);
\r
4353 /* [HGM] Only allow C & A promotions if these pieces are defined */
\r
4354 ShowWindow(GetDlgItem(hDlg, PB_Archbishop),
\r
4355 ((PieceToChar(WhiteAngel) >= 'A' && WhiteOnMove(currentMove) &&
\r
4356 PieceToChar(WhiteAngel) != '~') ||
\r
4357 (PieceToChar(BlackAngel) >= 'A' && !WhiteOnMove(currentMove) &&
\r
4358 PieceToChar(BlackAngel) != '~') ) ?
\r
4359 SW_SHOW : SW_HIDE);
\r
4360 ShowWindow(GetDlgItem(hDlg, PB_Chancellor),
\r
4361 ((PieceToChar(WhiteMarshall) >= 'A' && WhiteOnMove(currentMove) &&
\r
4362 PieceToChar(WhiteMarshall) != '~') ||
\r
4363 (PieceToChar(BlackMarshall) >= 'A' && !WhiteOnMove(currentMove) &&
\r
4364 PieceToChar(BlackMarshall) != '~') ) ?
\r
4365 SW_SHOW : SW_HIDE);
\r
4366 /* [HGM] Hide B & R button in Shogi, use Q as promote, N as defer */
\r
4367 ShowWindow(GetDlgItem(hDlg, PB_Rook),
\r
4368 gameInfo.variant != VariantShogi ?
\r
4369 SW_SHOW : SW_HIDE);
\r
4370 ShowWindow(GetDlgItem(hDlg, PB_Bishop),
\r
4371 gameInfo.variant != VariantShogi ?
\r
4372 SW_SHOW : SW_HIDE);
\r
4373 if(gameInfo.variant == VariantShogi) {
\r
4374 SetDlgItemText(hDlg, PB_Queen, "YES");
\r
4375 SetDlgItemText(hDlg, PB_Knight, "NO");
\r
4376 SetWindowText(hDlg, "Promote?");
\r
4378 ShowWindow(GetDlgItem(hDlg, IDC_Centaur),
\r
4379 gameInfo.variant == VariantSuper ?
\r
4380 SW_SHOW : SW_HIDE);
\r
4383 case WM_COMMAND: /* message: received a command */
\r
4384 switch (LOWORD(wParam)) {
\r
4386 EndDialog(hDlg, TRUE); /* Exit the dialog */
\r
4387 ClearHighlights();
\r
4388 DrawPosition(FALSE, NULL);
\r
4391 promoChar = gameInfo.variant == VariantSuper ? PieceToChar(BlackSilver) : PieceToChar(BlackKing);
\r
4394 promoChar = gameInfo.variant == VariantShogi ? '+' : ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteQueen : BlackQueen));
\r
4397 promoChar = ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteRook : BlackRook));
\r
4398 if(gameInfo.variant == VariantSpartan && !WhiteOnMove(currentMove)) promoChar = PieceToChar(BlackDragon);
\r
4401 promoChar = ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteBishop : BlackBishop));
\r
4402 if(gameInfo.variant == VariantSpartan && !WhiteOnMove(currentMove)) promoChar = PieceToChar(BlackAlfil);
\r
4404 case PB_Chancellor:
\r
4405 promoChar = ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteMarshall : BlackMarshall));
\r
4407 case PB_Archbishop:
\r
4408 promoChar = ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteAngel : BlackAngel));
\r
4411 promoChar = gameInfo.variant == VariantShogi ? '=' : PieceToChar(WhiteOnMove(currentMove) ? WhiteKnight : BlackKnight);
\r
4416 if(promoChar == '.') return FALSE; // invalid piece chosen
\r
4417 EndDialog(hDlg, TRUE); /* Exit the dialog */
\r
4418 UserMoveEvent(fromX, fromY, toX, toY, promoChar);
\r
4419 fromX = fromY = -1;
\r
4420 if (!appData.highlightLastMove) {
\r
4421 ClearHighlights();
\r
4422 DrawPosition(FALSE, NULL);
\r
4429 /* Pop up promotion dialog */
\r
4431 PromotionPopup(HWND hwnd)
\r
4435 lpProc = MakeProcInstance((FARPROC)Promotion, hInst);
\r
4436 DialogBox(hInst, MAKEINTRESOURCE(DLG_PromotionKing),
\r
4437 hwnd, (DLGPROC)lpProc);
\r
4438 FreeProcInstance(lpProc);
\r
4444 DrawPosition(TRUE, NULL);
\r
4445 PromotionPopup(hwndMain);
\r
4448 /* Toggle ShowThinking */
\r
4450 ToggleShowThinking()
\r
4452 appData.showThinking = !appData.showThinking;
\r
4453 ShowThinkingEvent();
\r
4457 LoadGameDialog(HWND hwnd, char* title)
\r
4461 char fileTitle[MSG_SIZ];
\r
4462 f = OpenFileDialog(hwnd, "rb", "",
\r
4463 appData.oldSaveStyle ? "gam" : "pgn",
\r
4465 title, &number, fileTitle, NULL);
\r
4467 cmailMsgLoaded = FALSE;
\r
4468 if (number == 0) {
\r
4469 int error = GameListBuild(f);
\r
4471 DisplayError(_("Cannot build game list"), error);
\r
4472 } else if (!ListEmpty(&gameList) &&
\r
4473 ((ListGame *) gameList.tailPred)->number > 1) {
\r
4474 GameListPopUp(f, fileTitle);
\r
4477 GameListDestroy();
\r
4480 LoadGame(f, number, fileTitle, FALSE);
\r
4484 int get_term_width()
\r
4489 HFONT hfont, hold_font;
\r
4494 hText = GetDlgItem(hwndConsole, OPT_ConsoleText);
\r
4498 // get the text metrics
\r
4499 hdc = GetDC(hText);
\r
4500 lf = font[boardSize][CONSOLE_FONT]->lf;
\r
4501 if (consoleCF.dwEffects & CFE_BOLD)
\r
4502 lf.lfWeight = FW_BOLD;
\r
4503 if (consoleCF.dwEffects & CFE_ITALIC)
\r
4504 lf.lfItalic = TRUE;
\r
4505 if (consoleCF.dwEffects & CFE_STRIKEOUT)
\r
4506 lf.lfStrikeOut = TRUE;
\r
4507 if (consoleCF.dwEffects & CFE_UNDERLINE)
\r
4508 lf.lfUnderline = TRUE;
\r
4509 hfont = CreateFontIndirect(&lf);
\r
4510 hold_font = SelectObject(hdc, hfont);
\r
4511 GetTextMetrics(hdc, &tm);
\r
4512 SelectObject(hdc, hold_font);
\r
4513 DeleteObject(hfont);
\r
4514 ReleaseDC(hText, hdc);
\r
4516 // get the rectangle
\r
4517 SendMessage(hText, EM_GETRECT, 0, (LPARAM)&rc);
\r
4519 return (rc.right-rc.left) / tm.tmAveCharWidth;
\r
4522 void UpdateICSWidth(HWND hText)
\r
4524 LONG old_width, new_width;
\r
4526 new_width = get_term_width(hText, FALSE);
\r
4527 old_width = GetWindowLongPtr(hText, GWLP_USERDATA);
\r
4528 if (new_width != old_width)
\r
4530 ics_update_width(new_width);
\r
4531 SetWindowLongPtr(hText, GWLP_USERDATA, new_width);
\r
4536 ChangedConsoleFont()
\r
4539 CHARRANGE tmpsel, sel;
\r
4540 MyFont *f = font[boardSize][CONSOLE_FONT];
\r
4541 HWND hText = GetDlgItem(hwndConsole, OPT_ConsoleText);
\r
4542 HWND hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
4545 cfmt.cbSize = sizeof(CHARFORMAT);
\r
4546 cfmt.dwMask = CFM_FACE|CFM_SIZE|CFM_CHARSET;
\r
4547 safeStrCpy(cfmt.szFaceName, font[boardSize][CONSOLE_FONT]->mfp.faceName,
\r
4548 sizeof(cfmt.szFaceName)/sizeof(cfmt.szFaceName[0]) );
\r
4549 /* yHeight is expressed in twips. A twip is 1/20 of a font's point
\r
4550 * size. This was undocumented in the version of MSVC++ that I had
\r
4551 * when I wrote the code, but is apparently documented now.
\r
4553 cfmt.yHeight = (int)(f->mfp.pointSize * 20.0 + 0.5);
\r
4554 cfmt.bCharSet = f->lf.lfCharSet;
\r
4555 cfmt.bPitchAndFamily = f->lf.lfPitchAndFamily;
\r
4556 SendMessage(hText, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &cfmt);
\r
4557 SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &cfmt);
\r
4558 /* Why are the following seemingly needed too? */
\r
4559 SendMessage(hText, EM_SETCHARFORMAT, SCF_DEFAULT, (LPARAM) &cfmt);
\r
4560 SendMessage(hInput, EM_SETCHARFORMAT, SCF_DEFAULT, (LPARAM) &cfmt);
\r
4561 SendMessage(hText, EM_EXGETSEL, 0, (LPARAM)&sel);
\r
4563 tmpsel.cpMax = -1; /*999999?*/
\r
4564 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&tmpsel);
\r
4565 SendMessage(hText, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cfmt);
\r
4566 /* Trying putting this here too. It still seems to tickle a RichEdit
\r
4567 * bug: sometimes RichEdit indents the first line of a paragraph too.
\r
4569 paraf.cbSize = sizeof(paraf);
\r
4570 paraf.dwMask = PFM_OFFSET | PFM_STARTINDENT;
\r
4571 paraf.dxStartIndent = 0;
\r
4572 paraf.dxOffset = WRAP_INDENT;
\r
4573 SendMessage(hText, EM_SETPARAFORMAT, 0, (LPARAM) ¶f);
\r
4574 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
4575 UpdateICSWidth(hText);
\r
4578 /*---------------------------------------------------------------------------*\
\r
4580 * Window Proc for main window
\r
4582 \*---------------------------------------------------------------------------*/
\r
4584 /* Process messages for main window, etc. */
\r
4586 WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
\r
4589 int wmId, wmEvent;
\r
4593 char fileTitle[MSG_SIZ];
\r
4594 char buf[MSG_SIZ];
\r
4595 static SnapData sd;
\r
4596 static int peek=0;
\r
4598 switch (message) {
\r
4600 case WM_PAINT: /* message: repaint portion of window */
\r
4604 case WM_ERASEBKGND:
\r
4605 if (IsIconic(hwnd)) {
\r
4606 /* Cheat; change the message */
\r
4607 return (DefWindowProc(hwnd, WM_ICONERASEBKGND, wParam, lParam));
\r
4609 return (DefWindowProc(hwnd, message, wParam, lParam));
\r
4613 case WM_LBUTTONDOWN:
\r
4614 case WM_MBUTTONDOWN:
\r
4615 case WM_RBUTTONDOWN:
\r
4616 case WM_LBUTTONUP:
\r
4617 case WM_MBUTTONUP:
\r
4618 case WM_RBUTTONUP:
\r
4619 case WM_MOUSEMOVE:
\r
4620 case WM_MOUSEWHEEL:
\r
4621 MouseEvent(hwnd, message, wParam, lParam);
\r
4625 if((char)wParam == '\b') {
\r
4626 ForwardEvent(); peek = 0;
\r
4629 JAWS_KBUP_NAVIGATION
\r
4634 if((char)wParam == '\b') {
\r
4635 if(!peek) BackwardEvent(), peek = 1;
\r
4638 JAWS_KBDOWN_NAVIGATION
\r
4644 JAWS_ALT_INTERCEPT
\r
4646 if (appData.icsActive && ((char)wParam == '\r' || (char)wParam > ' ' && !((char)wParam >= '1' && (char)wParam <= '9'))) {
\r
4647 // [HGM] movenum: for non-zero digits we always do type-in dialog
\r
4648 HWND h = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
4649 if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);
\r
4651 SendMessage(h, message, wParam, lParam);
\r
4652 } else if(lParam != KF_REPEAT) {
\r
4653 if (isalpha((char)wParam) || isdigit((char)wParam)) {
\r
4654 TypeInEvent((char)wParam);
\r
4655 } else if((char)wParam == 003) CopyGameToClipboard();
\r
4656 else if((char)wParam == 026) PasteGameOrFENFromClipboard();
\r
4661 case WM_PALETTECHANGED:
\r
4662 if (hwnd != (HWND)wParam && !appData.monoMode) {
\r
4664 HDC hdc = GetDC(hwndMain);
\r
4665 SelectPalette(hdc, hPal, TRUE);
\r
4666 nnew = RealizePalette(hdc);
\r
4668 paletteChanged = TRUE;
\r
4669 InvalidateRect(hwnd, &boardRect, FALSE);
\r
4671 ReleaseDC(hwnd, hdc);
\r
4675 case WM_QUERYNEWPALETTE:
\r
4676 if (!appData.monoMode /*&& paletteChanged*/) {
\r
4678 HDC hdc = GetDC(hwndMain);
\r
4679 paletteChanged = FALSE;
\r
4680 SelectPalette(hdc, hPal, FALSE);
\r
4681 nnew = RealizePalette(hdc);
\r
4683 InvalidateRect(hwnd, &boardRect, FALSE);
\r
4685 ReleaseDC(hwnd, hdc);
\r
4690 case WM_COMMAND: /* message: command from application menu */
\r
4691 wmId = LOWORD(wParam);
\r
4692 wmEvent = HIWORD(wParam);
\r
4697 SAY("new game enter a move to play against the computer with white");
\r
4700 case IDM_NewGameFRC:
\r
4701 if( NewGameFRC() == 0 ) {
\r
4706 case IDM_NewVariant:
\r
4707 NewVariantPopup(hwnd);
\r
4710 case IDM_LoadGame:
\r
4711 LoadGameDialog(hwnd, _("Load Game from File"));
\r
4714 case IDM_LoadNextGame:
\r
4718 case IDM_LoadPrevGame:
\r
4722 case IDM_ReloadGame:
\r
4726 case IDM_LoadPosition:
\r
4727 if (gameMode == AnalyzeMode || gameMode == AnalyzeFile) {
\r
4728 Reset(FALSE, TRUE);
\r
4731 f = OpenFileDialog(hwnd, "rb", "",
\r
4732 appData.oldSaveStyle ? "pos" : "fen",
\r
4734 _("Load Position from File"), &number, fileTitle, NULL);
\r
4736 LoadPosition(f, number, fileTitle);
\r
4740 case IDM_LoadNextPosition:
\r
4741 ReloadPosition(1);
\r
4744 case IDM_LoadPrevPosition:
\r
4745 ReloadPosition(-1);
\r
4748 case IDM_ReloadPosition:
\r
4749 ReloadPosition(0);
\r
4752 case IDM_SaveGame:
\r
4753 defName = DefaultFileName(appData.oldSaveStyle ? "gam" : "pgn");
\r
4754 f = OpenFileDialog(hwnd, "a", defName,
\r
4755 appData.oldSaveStyle ? "gam" : "pgn",
\r
4757 _("Save Game to File"), NULL, fileTitle, NULL);
\r
4759 SaveGame(f, 0, "");
\r
4763 case IDM_SavePosition:
\r
4764 defName = DefaultFileName(appData.oldSaveStyle ? "pos" : "fen");
\r
4765 f = OpenFileDialog(hwnd, "a", defName,
\r
4766 appData.oldSaveStyle ? "pos" : "fen",
\r
4768 _("Save Position to File"), NULL, fileTitle, NULL);
\r
4770 SavePosition(f, 0, "");
\r
4774 case IDM_SaveDiagram:
\r
4775 defName = "diagram";
\r
4776 f = OpenFileDialog(hwnd, "wb", defName,
\r
4779 _("Save Diagram to File"), NULL, fileTitle, NULL);
\r
4785 case IDM_CopyGame:
\r
4786 CopyGameToClipboard();
\r
4789 case IDM_PasteGame:
\r
4790 PasteGameFromClipboard();
\r
4793 case IDM_CopyGameListToClipboard:
\r
4794 CopyGameListToClipboard();
\r
4797 /* [AS] Autodetect FEN or PGN data */
\r
4798 case IDM_PasteAny:
\r
4799 PasteGameOrFENFromClipboard();
\r
4802 /* [AS] Move history */
\r
4803 case IDM_ShowMoveHistory:
\r
4804 if( MoveHistoryIsUp() ) {
\r
4805 MoveHistoryPopDown();
\r
4808 MoveHistoryPopUp();
\r
4812 /* [AS] Eval graph */
\r
4813 case IDM_ShowEvalGraph:
\r
4814 if( EvalGraphIsUp() ) {
\r
4815 EvalGraphPopDown();
\r
4819 SetFocus(hwndMain);
\r
4823 /* [AS] Engine output */
\r
4824 case IDM_ShowEngineOutput:
\r
4825 if( EngineOutputIsUp() ) {
\r
4826 EngineOutputPopDown();
\r
4829 EngineOutputPopUp();
\r
4833 /* [AS] User adjudication */
\r
4834 case IDM_UserAdjudication_White:
\r
4835 UserAdjudicationEvent( +1 );
\r
4838 case IDM_UserAdjudication_Black:
\r
4839 UserAdjudicationEvent( -1 );
\r
4842 case IDM_UserAdjudication_Draw:
\r
4843 UserAdjudicationEvent( 0 );
\r
4846 /* [AS] Game list options dialog */
\r
4847 case IDM_GameListOptions:
\r
4848 GameListOptions();
\r
4855 case IDM_CopyPosition:
\r
4856 CopyFENToClipboard();
\r
4859 case IDM_PastePosition:
\r
4860 PasteFENFromClipboard();
\r
4863 case IDM_MailMove:
\r
4867 case IDM_ReloadCMailMsg:
\r
4868 Reset(TRUE, TRUE);
\r
4869 ReloadCmailMsgEvent(FALSE);
\r
4872 case IDM_Minimize:
\r
4873 ShowWindow(hwnd, SW_MINIMIZE);
\r
4880 case IDM_MachineWhite:
\r
4881 MachineWhiteEvent();
\r
4883 * refresh the tags dialog only if it's visible
\r
4885 if (gameMode == MachinePlaysWhite && IsWindowVisible(editTagsDialog)) {
\r
4887 tags = PGNTags(&gameInfo);
\r
4888 TagsPopUp(tags, CmailMsg());
\r
4891 SAY("computer starts playing white");
\r
4894 case IDM_MachineBlack:
\r
4895 MachineBlackEvent();
\r
4897 * refresh the tags dialog only if it's visible
\r
4899 if (gameMode == MachinePlaysBlack && IsWindowVisible(editTagsDialog)) {
\r
4901 tags = PGNTags(&gameInfo);
\r
4902 TagsPopUp(tags, CmailMsg());
\r
4905 SAY("computer starts playing black");
\r
4908 case IDM_Match: // [HGM] match: flows into next case, after setting Match Mode and nr of Games
\r
4909 MatchEvent(2); // distinguish from command-line-triggered case (matchMode=1)
\r
4912 case IDM_TwoMachines:
\r
4913 TwoMachinesEvent();
\r
4915 * refresh the tags dialog only if it's visible
\r
4917 if (gameMode == TwoMachinesPlay && IsWindowVisible(editTagsDialog)) {
\r
4919 tags = PGNTags(&gameInfo);
\r
4920 TagsPopUp(tags, CmailMsg());
\r
4923 SAY("computer starts playing both sides");
\r
4926 case IDM_AnalysisMode:
\r
4927 if (!first.analysisSupport) {
\r
4928 snprintf(buf, MSG_SIZ, _("%s does not support analysis"), first.tidy);
\r
4929 DisplayError(buf, 0);
\r
4931 SAY("analyzing current position");
\r
4932 /* [DM] icsEngineAnlyze [HGM] Why is this front-end??? */
\r
4933 if (appData.icsActive) {
\r
4934 if (gameMode != IcsObserving) {
\r
4935 snprintf(buf, MSG_SIZ, "You are not observing a game");
\r
4936 DisplayError(buf, 0);
\r
4937 /* secure check */
\r
4938 if (appData.icsEngineAnalyze) {
\r
4939 if (appData.debugMode)
\r
4940 fprintf(debugFP, "Found unexpected active ICS engine analyze \n");
\r
4941 ExitAnalyzeMode();
\r
4947 /* if enable, user want disable icsEngineAnalyze */
\r
4948 if (appData.icsEngineAnalyze) {
\r
4949 ExitAnalyzeMode();
\r
4953 appData.icsEngineAnalyze = TRUE;
\r
4954 if (appData.debugMode) fprintf(debugFP, "ICS engine analyze starting...\n");
\r
4957 if (!appData.showThinking) ToggleShowThinking();
\r
4958 AnalyzeModeEvent();
\r
4962 case IDM_AnalyzeFile:
\r
4963 if (!first.analysisSupport) {
\r
4964 char buf[MSG_SIZ];
\r
4965 snprintf(buf, MSG_SIZ, _("%s does not support analysis"), first.tidy);
\r
4966 DisplayError(buf, 0);
\r
4968 if (!appData.showThinking) ToggleShowThinking();
\r
4969 AnalyzeFileEvent();
\r
4970 // LoadGameDialog(hwnd, _("Analyze Game from File"));
\r
4971 AnalysisPeriodicEvent(1);
\r
4975 case IDM_IcsClient:
\r
4979 case IDM_EditGame:
\r
4980 case IDM_EditGame2:
\r
4985 case IDM_EditPosition:
\r
4986 case IDM_EditPosition2:
\r
4987 EditPositionEvent();
\r
4988 SAY("enter a FEN string or setup a position on the board using the control R pop up menu");
\r
4991 case IDM_Training:
\r
4995 case IDM_ShowGameList:
\r
4996 ShowGameListProc();
\r
4999 case IDM_EditProgs1:
\r
5000 EditTagsPopUp(firstChessProgramNames, &firstChessProgramNames);
\r
5003 case IDM_LoadProg1:
\r
5004 LoadEnginePopUp(hwndMain, 0);
\r
5007 case IDM_LoadProg2:
\r
5008 LoadEnginePopUp(hwndMain, 1);
\r
5011 case IDM_EditServers:
\r
5012 EditTagsPopUp(icsNames, &icsNames);
\r
5015 case IDM_EditTags:
\r
5020 case IDM_EditBook:
\r
5024 case IDM_EditComment:
\r
5026 if (commentUp && editComment) {
\r
5029 EditCommentEvent();
\r
5049 case IDM_CallFlag:
\r
5069 case IDM_StopObserving:
\r
5070 StopObservingEvent();
\r
5073 case IDM_StopExamining:
\r
5074 StopExaminingEvent();
\r
5078 UploadGameEvent();
\r
5081 case IDM_TypeInMove:
\r
5082 TypeInEvent('\000');
\r
5085 case IDM_TypeInName:
\r
5086 PopUpNameDialog('\000');
\r
5089 case IDM_Backward:
\r
5091 SetFocus(hwndMain);
\r
5098 SetFocus(hwndMain);
\r
5103 SetFocus(hwndMain);
\r
5108 SetFocus(hwndMain);
\r
5111 case OPT_GameListNext: // [HGM] forward these two accelerators to Game List
\r
5112 case OPT_GameListPrev:
\r
5113 if(gameListDialog) SendMessage(gameListDialog, WM_COMMAND, wmId, 0);
\r
5117 RevertEvent(FALSE);
\r
5120 case IDM_Annotate: // [HGM] vari: revert with annotation
\r
5121 RevertEvent(TRUE);
\r
5124 case IDM_TruncateGame:
\r
5125 TruncateGameEvent();
\r
5132 case IDM_RetractMove:
\r
5133 RetractMoveEvent();
\r
5136 case IDM_FlipView:
\r
5137 flipView = !flipView;
\r
5138 DrawPosition(FALSE, NULL);
\r
5141 case IDM_FlipClock:
\r
5142 flipClock = !flipClock;
\r
5143 DisplayBothClocks();
\r
5147 case IDM_MuteSounds:
\r
5148 mute = !mute; // [HGM] mute: keep track of global muting variable
\r
5149 CheckMenuItem(GetMenu(hwndMain),IDM_MuteSounds,
\r
5150 MF_BYCOMMAND|(mute?MF_CHECKED:MF_UNCHECKED));
\r
5153 case IDM_GeneralOptions:
\r
5154 GeneralOptionsPopup(hwnd);
\r
5155 DrawPosition(TRUE, NULL);
\r
5158 case IDM_BoardOptions:
\r
5159 BoardOptionsPopup(hwnd);
\r
5162 case IDM_EnginePlayOptions:
\r
5163 EnginePlayOptionsPopup(hwnd);
\r
5166 case IDM_Engine1Options:
\r
5167 EngineOptionsPopup(hwnd, &first);
\r
5170 case IDM_Engine2Options:
\r
5172 if(WaitForEngine(&second, SettingsMenuIfReady)) break;
\r
5173 EngineOptionsPopup(hwnd, &second);
\r
5176 case IDM_OptionsUCI:
\r
5177 UciOptionsPopup(hwnd);
\r
5181 TourneyPopup(hwnd);
\r
5184 case IDM_IcsOptions:
\r
5185 IcsOptionsPopup(hwnd);
\r
5189 FontsOptionsPopup(hwnd);
\r
5193 SoundOptionsPopup(hwnd);
\r
5196 case IDM_CommPort:
\r
5197 CommPortOptionsPopup(hwnd);
\r
5200 case IDM_LoadOptions:
\r
5201 LoadOptionsPopup(hwnd);
\r
5204 case IDM_SaveOptions:
\r
5205 SaveOptionsPopup(hwnd);
\r
5208 case IDM_TimeControl:
\r
5209 TimeControlOptionsPopup(hwnd);
\r
5212 case IDM_SaveSettings:
\r
5213 SaveSettings(settingsFileName);
\r
5216 case IDM_SaveSettingsOnExit:
\r
5217 saveSettingsOnExit = !saveSettingsOnExit;
\r
5218 (void) CheckMenuItem(GetMenu(hwndMain), IDM_SaveSettingsOnExit,
\r
5219 MF_BYCOMMAND|(saveSettingsOnExit ?
\r
5220 MF_CHECKED : MF_UNCHECKED));
\r
5231 case IDM_AboutGame:
\r
5236 appData.debugMode = !appData.debugMode;
\r
5237 if (appData.debugMode) {
\r
5238 char dir[MSG_SIZ];
\r
5239 GetCurrentDirectory(MSG_SIZ, dir);
\r
5240 SetCurrentDirectory(installDir);
\r
5241 debugFP = fopen(appData.nameOfDebugFile, "w");
\r
5242 SetCurrentDirectory(dir);
\r
5243 setbuf(debugFP, NULL);
\r
5250 case IDM_HELPCONTENTS:
\r
5251 if (!MyHelp (hwnd, "winboard.hlp", HELP_KEY,(DWORD)(LPSTR)"CONTENTS") &&
\r
5252 !HtmlHelp(hwnd, "winboard.chm", 0, 0) ) {
\r
5253 MessageBox (GetFocus(),
\r
5254 _("Unable to activate help"),
\r
5255 szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);
\r
5259 case IDM_HELPSEARCH:
\r
5260 if (!MyHelp (hwnd, "winboard.hlp", HELP_PARTIALKEY, (DWORD)(LPSTR)"") &&
\r
5261 !HtmlHelp(hwnd, "winboard.chm", 0, 0) ) {
\r
5262 MessageBox (GetFocus(),
\r
5263 _("Unable to activate help"),
\r
5264 szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);
\r
5268 case IDM_HELPHELP:
\r
5269 if(!WinHelp(hwnd, (LPSTR)NULL, HELP_HELPONHELP, 0)) {
\r
5270 MessageBox (GetFocus(),
\r
5271 _("Unable to activate help"),
\r
5272 szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);
\r
5277 lpProc = MakeProcInstance((FARPROC)About, hInst);
\r
5279 (gameInfo.event && strcmp(gameInfo.event, "Easter Egg Hunt") == 0) ?
\r
5280 "AboutBox2" : "AboutBox", hwnd, (DLGPROC)lpProc);
\r
5281 FreeProcInstance(lpProc);
\r
5284 case IDM_DirectCommand1:
\r
5285 AskQuestionEvent(_("Direct Command"),
\r
5286 _("Send to chess program:"), "", "1");
\r
5288 case IDM_DirectCommand2:
\r
5289 AskQuestionEvent(_("Direct Command"),
\r
5290 _("Send to second chess program:"), "", "2");
\r
5293 case EP_WhitePawn:
\r
5294 EditPositionMenuEvent(WhitePawn, fromX, fromY);
\r
5295 fromX = fromY = -1;
\r
5298 case EP_WhiteKnight:
\r
5299 EditPositionMenuEvent(WhiteKnight, fromX, fromY);
\r
5300 fromX = fromY = -1;
\r
5303 case EP_WhiteBishop:
\r
5304 EditPositionMenuEvent(WhiteBishop, fromX, fromY);
\r
5305 fromX = fromY = -1;
\r
5308 case EP_WhiteRook:
\r
5309 EditPositionMenuEvent(WhiteRook, fromX, fromY);
\r
5310 fromX = fromY = -1;
\r
5313 case EP_WhiteQueen:
\r
5314 EditPositionMenuEvent(WhiteQueen, fromX, fromY);
\r
5315 fromX = fromY = -1;
\r
5318 case EP_WhiteFerz:
\r
5319 EditPositionMenuEvent(WhiteFerz, fromX, fromY);
\r
5320 fromX = fromY = -1;
\r
5323 case EP_WhiteWazir:
\r
5324 EditPositionMenuEvent(WhiteWazir, fromX, fromY);
\r
5325 fromX = fromY = -1;
\r
5328 case EP_WhiteAlfil:
\r
5329 EditPositionMenuEvent(WhiteAlfil, fromX, fromY);
\r
5330 fromX = fromY = -1;
\r
5333 case EP_WhiteCannon:
\r
5334 EditPositionMenuEvent(WhiteCannon, fromX, fromY);
\r
5335 fromX = fromY = -1;
\r
5338 case EP_WhiteCardinal:
\r
5339 EditPositionMenuEvent(WhiteAngel, fromX, fromY);
\r
5340 fromX = fromY = -1;
\r
5343 case EP_WhiteMarshall:
\r
5344 EditPositionMenuEvent(WhiteMarshall, fromX, fromY);
\r
5345 fromX = fromY = -1;
\r
5348 case EP_WhiteKing:
\r
5349 EditPositionMenuEvent(WhiteKing, fromX, fromY);
\r
5350 fromX = fromY = -1;
\r
5353 case EP_BlackPawn:
\r
5354 EditPositionMenuEvent(BlackPawn, fromX, fromY);
\r
5355 fromX = fromY = -1;
\r
5358 case EP_BlackKnight:
\r
5359 EditPositionMenuEvent(BlackKnight, fromX, fromY);
\r
5360 fromX = fromY = -1;
\r
5363 case EP_BlackBishop:
\r
5364 EditPositionMenuEvent(BlackBishop, fromX, fromY);
\r
5365 fromX = fromY = -1;
\r
5368 case EP_BlackRook:
\r
5369 EditPositionMenuEvent(BlackRook, fromX, fromY);
\r
5370 fromX = fromY = -1;
\r
5373 case EP_BlackQueen:
\r
5374 EditPositionMenuEvent(BlackQueen, fromX, fromY);
\r
5375 fromX = fromY = -1;
\r
5378 case EP_BlackFerz:
\r
5379 EditPositionMenuEvent(BlackFerz, fromX, fromY);
\r
5380 fromX = fromY = -1;
\r
5383 case EP_BlackWazir:
\r
5384 EditPositionMenuEvent(BlackWazir, fromX, fromY);
\r
5385 fromX = fromY = -1;
\r
5388 case EP_BlackAlfil:
\r
5389 EditPositionMenuEvent(BlackAlfil, fromX, fromY);
\r
5390 fromX = fromY = -1;
\r
5393 case EP_BlackCannon:
\r
5394 EditPositionMenuEvent(BlackCannon, fromX, fromY);
\r
5395 fromX = fromY = -1;
\r
5398 case EP_BlackCardinal:
\r
5399 EditPositionMenuEvent(BlackAngel, fromX, fromY);
\r
5400 fromX = fromY = -1;
\r
5403 case EP_BlackMarshall:
\r
5404 EditPositionMenuEvent(BlackMarshall, fromX, fromY);
\r
5405 fromX = fromY = -1;
\r
5408 case EP_BlackKing:
\r
5409 EditPositionMenuEvent(BlackKing, fromX, fromY);
\r
5410 fromX = fromY = -1;
\r
5413 case EP_EmptySquare:
\r
5414 EditPositionMenuEvent(EmptySquare, fromX, fromY);
\r
5415 fromX = fromY = -1;
\r
5418 case EP_ClearBoard:
\r
5419 EditPositionMenuEvent(ClearBoard, fromX, fromY);
\r
5420 fromX = fromY = -1;
\r
5424 EditPositionMenuEvent(WhitePlay, fromX, fromY);
\r
5425 fromX = fromY = -1;
\r
5429 EditPositionMenuEvent(BlackPlay, fromX, fromY);
\r
5430 fromX = fromY = -1;
\r
5434 EditPositionMenuEvent(PromotePiece, fromX, fromY);
\r
5435 fromX = fromY = -1;
\r
5439 EditPositionMenuEvent(DemotePiece, fromX, fromY);
\r
5440 fromX = fromY = -1;
\r
5444 DropMenuEvent(WhitePawn, fromX, fromY);
\r
5445 fromX = fromY = -1;
\r
5449 DropMenuEvent(WhiteKnight, fromX, fromY);
\r
5450 fromX = fromY = -1;
\r
5454 DropMenuEvent(WhiteBishop, fromX, fromY);
\r
5455 fromX = fromY = -1;
\r
5459 DropMenuEvent(WhiteRook, fromX, fromY);
\r
5460 fromX = fromY = -1;
\r
5464 DropMenuEvent(WhiteQueen, fromX, fromY);
\r
5465 fromX = fromY = -1;
\r
5469 barbaric = 0; appData.language = "";
\r
5470 TranslateMenus(0);
\r
5471 CheckMenuItem(GetMenu(hwndMain), lastChecked, MF_BYCOMMAND|MF_UNCHECKED);
\r
5472 CheckMenuItem(GetMenu(hwndMain), IDM_English, MF_BYCOMMAND|MF_CHECKED);
\r
5473 lastChecked = wmId;
\r
5477 if(wmId >= IDM_RecentEngines && wmId < IDM_RecentEngines + appData.recentEngines)
\r
5478 RecentEngineEvent(wmId - IDM_RecentEngines);
\r
5480 if(wmId > IDM_English && wmId < IDM_English+20) {
\r
5481 LoadLanguageFile(languageFile[wmId - IDM_English - 1]);
\r
5482 TranslateMenus(0);
\r
5483 CheckMenuItem(GetMenu(hwndMain), lastChecked, MF_BYCOMMAND|MF_UNCHECKED);
\r
5484 CheckMenuItem(GetMenu(hwndMain), wmId, MF_BYCOMMAND|MF_CHECKED);
\r
5485 lastChecked = wmId;
\r
5488 return (DefWindowProc(hwnd, message, wParam, lParam));
\r
5494 case CLOCK_TIMER_ID:
\r
5495 KillTimer(hwnd, clockTimerEvent); /* Simulate one-shot timer as in X */
\r
5496 clockTimerEvent = 0;
\r
5497 DecrementClocks(); /* call into back end */
\r
5499 case LOAD_GAME_TIMER_ID:
\r
5500 KillTimer(hwnd, loadGameTimerEvent); /* Simulate one-shot timer as in X*/
\r
5501 loadGameTimerEvent = 0;
\r
5502 AutoPlayGameLoop(); /* call into back end */
\r
5504 case ANALYSIS_TIMER_ID:
\r
5505 if ((gameMode == AnalyzeMode || gameMode == AnalyzeFile
\r
5506 || appData.icsEngineAnalyze) && appData.periodicUpdates) {
\r
5507 AnalysisPeriodicEvent(0);
\r
5509 KillTimer(hwnd, analysisTimerEvent);
\r
5510 analysisTimerEvent = 0;
\r
5513 case DELAYED_TIMER_ID:
\r
5514 KillTimer(hwnd, delayedTimerEvent);
\r
5515 delayedTimerEvent = 0;
\r
5516 delayedTimerCallback();
\r
5521 case WM_USER_Input:
\r
5522 InputEvent(hwnd, message, wParam, lParam);
\r
5525 /* [AS] Also move "attached" child windows */
\r
5526 case WM_WINDOWPOSCHANGING:
\r
5528 if( hwnd == hwndMain && appData.useStickyWindows ) {
\r
5529 LPWINDOWPOS lpwp = (LPWINDOWPOS) lParam;
\r
5531 if( ((lpwp->flags & SWP_NOMOVE) == 0) && ((lpwp->flags & SWP_NOSIZE) != 0) ) {
\r
5532 /* Window is moving */
\r
5535 // GetWindowRect( hwnd, &rcMain ); //[HGM] sticky: in XP this returned new position, not old
\r
5536 rcMain.left = wpMain.x; // replace by these 4 lines to reconstruct old rect
\r
5537 rcMain.right = wpMain.x + wpMain.width;
\r
5538 rcMain.top = wpMain.y;
\r
5539 rcMain.bottom = wpMain.y + wpMain.height;
\r
5541 ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, moveHistoryDialog, &wpMoveHistory );
\r
5542 ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, evalGraphDialog, &wpEvalGraph );
\r
5543 ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, engineOutputDialog, &wpEngineOutput );
\r
5544 ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, gameListDialog, &wpGameList );
\r
5545 ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, hwndConsole, &wpConsole );
\r
5546 wpMain.x = lpwp->x;
\r
5547 wpMain.y = lpwp->y;
\r
5552 /* [AS] Snapping */
\r
5553 case WM_ENTERSIZEMOVE:
\r
5554 if(appData.debugMode) { fprintf(debugFP, "size-move\n"); }
\r
5555 if (hwnd == hwndMain) {
\r
5556 doingSizing = TRUE;
\r
5559 return OnEnterSizeMove( &sd, hwnd, wParam, lParam );
\r
5563 if(appData.debugMode) { fprintf(debugFP, "sizing\n"); }
\r
5564 if (hwnd == hwndMain) {
\r
5565 lastSizing = wParam;
\r
5570 if(appData.debugMode) { fprintf(debugFP, "moving\n"); }
\r
5571 return OnMoving( &sd, hwnd, wParam, lParam );
\r
5573 case WM_EXITSIZEMOVE:
\r
5574 if(appData.debugMode) { fprintf(debugFP, "exit size-move, size = %d\n", squareSize); }
\r
5575 if (hwnd == hwndMain) {
\r
5577 doingSizing = FALSE;
\r
5578 InvalidateRect(hwnd, &boardRect, FALSE);
\r
5579 GetClientRect(hwnd, &client);
\r
5580 ResizeBoard(client.right, client.bottom, lastSizing);
\r
5582 if(appData.debugMode) { fprintf(debugFP, "square size = %d\n", squareSize); }
\r
5584 return OnExitSizeMove( &sd, hwnd, wParam, lParam );
\r
5587 case WM_DESTROY: /* message: window being destroyed */
\r
5588 PostQuitMessage(0);
\r
5592 if (hwnd == hwndMain) {
\r
5597 default: /* Passes it on if unprocessed */
\r
5598 return (DefWindowProc(hwnd, message, wParam, lParam));
\r
5603 /*---------------------------------------------------------------------------*\
\r
5605 * Misc utility routines
\r
5607 \*---------------------------------------------------------------------------*/
\r
5610 * Decent random number generator, at least not as bad as Windows
\r
5611 * standard rand, which returns a value in the range 0 to 0x7fff.
\r
5613 unsigned int randstate;
\r
5618 randstate = randstate * 1664525 + 1013904223;
\r
5619 return (int) randstate & 0x7fffffff;
\r
5623 mysrandom(unsigned int seed)
\r
5630 * returns TRUE if user selects a different color, FALSE otherwise
\r
5634 ChangeColor(HWND hwnd, COLORREF *which)
\r
5636 static BOOL firstTime = TRUE;
\r
5637 static DWORD customColors[16];
\r
5639 COLORREF newcolor;
\r
5644 /* Make initial colors in use available as custom colors */
\r
5645 /* Should we put the compiled-in defaults here instead? */
\r
5647 customColors[i++] = lightSquareColor & 0xffffff;
\r
5648 customColors[i++] = darkSquareColor & 0xffffff;
\r
5649 customColors[i++] = whitePieceColor & 0xffffff;
\r
5650 customColors[i++] = blackPieceColor & 0xffffff;
\r
5651 customColors[i++] = highlightSquareColor & 0xffffff;
\r
5652 customColors[i++] = premoveHighlightColor & 0xffffff;
\r
5654 for (ccl = (ColorClass) 0; ccl < NColorClasses && i < 16; ccl++) {
\r
5655 customColors[i++] = textAttribs[ccl].color;
\r
5657 while (i < 16) customColors[i++] = RGB(255, 255, 255);
\r
5658 firstTime = FALSE;
\r
5661 cc.lStructSize = sizeof(cc);
\r
5662 cc.hwndOwner = hwnd;
\r
5663 cc.hInstance = NULL;
\r
5664 cc.rgbResult = (DWORD) (*which & 0xffffff);
\r
5665 cc.lpCustColors = (LPDWORD) customColors;
\r
5666 cc.Flags = CC_RGBINIT|CC_FULLOPEN;
\r
5668 if (!ChooseColor(&cc)) return FALSE;
\r
5670 newcolor = (COLORREF) (0x2000000 | cc.rgbResult);
\r
5671 if (newcolor == *which) return FALSE;
\r
5672 *which = newcolor;
\r
5676 InitDrawingColors();
\r
5677 InvalidateRect(hwnd, &boardRect, FALSE);
\r
5682 MyLoadSound(MySound *ms)
\r
5688 if (ms->data && ms->flag) free(ms->data);
\r
5691 switch (ms->name[0]) {
\r
5697 /* System sound from Control Panel. Don't preload here. */
\r
5701 if (ms->name[1] == NULLCHAR) {
\r
5702 /* "!" alone = silence */
\r
5705 /* Builtin wave resource. Error if not found. */
\r
5706 HANDLE h = FindResource(hInst, ms->name + 1, "WAVE");
\r
5707 if (h == NULL) break;
\r
5708 ms->data = (void *)LoadResource(hInst, h);
\r
5709 ms->flag = 0; // not maloced, so cannot be freed!
\r
5710 if (h == NULL) break;
\r
5715 /* .wav file. Error if not found. */
\r
5716 f = fopen(ms->name, "rb");
\r
5717 if (f == NULL) break;
\r
5718 if (fstat(fileno(f), &st) < 0) break;
\r
5719 ms->data = malloc(st.st_size);
\r
5721 if (fread(ms->data, st.st_size, 1, f) < 1) break;
\r
5727 char buf[MSG_SIZ];
\r
5728 snprintf(buf, MSG_SIZ, _("Error loading sound %s"), ms->name);
\r
5729 DisplayError(buf, GetLastError());
\r
5735 MyPlaySound(MySound *ms)
\r
5737 BOOLEAN ok = FALSE;
\r
5739 if(mute) return TRUE; // [HGM] mute: suppress all sound play when muted
\r
5740 switch (ms->name[0]) {
\r
5742 if(appData.debugMode) fprintf(debugFP, "silence\n");
\r
5747 /* System sound from Control Panel (deprecated feature).
\r
5748 "$" alone or an unset sound name gets default beep (still in use). */
\r
5749 if (ms->name[1]) {
\r
5750 ok = PlaySound(ms->name + 1, NULL, SND_ALIAS|SND_ASYNC);
\r
5752 if (!ok) ok = MessageBeep(MB_OK);
\r
5755 /* Builtin wave resource, or "!" alone for silence */
\r
5756 if (ms->name[1]) {
\r
5757 if (ms->data == NULL) return FALSE;
\r
5758 ok = PlaySound(ms->data, NULL, SND_MEMORY|SND_ASYNC);
\r
5764 /* .wav file. Error if not found. */
\r
5765 if (ms->data == NULL) return FALSE;
\r
5766 ok = PlaySound(ms->data, NULL, SND_MEMORY|SND_ASYNC);
\r
5769 /* Don't print an error: this can happen innocently if the sound driver
\r
5770 is busy; for instance, if another instance of WinBoard is playing
\r
5771 a sound at about the same time. */
\r
5777 OldOpenFileHook(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
5780 OPENFILENAME *ofn;
\r
5781 static UINT *number; /* gross that this is static */
\r
5783 switch (message) {
\r
5784 case WM_INITDIALOG: /* message: initialize dialog box */
\r
5785 /* Center the dialog over the application window */
\r
5786 ofn = (OPENFILENAME *) lParam;
\r
5787 if (ofn->Flags & OFN_ENABLETEMPLATE) {
\r
5788 number = (UINT *) ofn->lCustData;
\r
5789 SendMessage(GetDlgItem(hDlg, edt2), WM_SETTEXT, 0, (LPARAM) "");
\r
5793 CenterWindow (hDlg, GetWindow (hDlg, GW_OWNER));
\r
5794 Translate(hDlg, 1536);
\r
5795 return FALSE; /* Allow for further processing */
\r
5798 if ((LOWORD(wParam) == IDOK) && (number != NULL)) {
\r
5799 *number = GetDlgItemInt(hDlg, OPT_IndexNumberOld, &ok, FALSE);
\r
5801 return FALSE; /* Allow for further processing */
\r
5807 OpenFileHook(HWND hdlg, UINT uiMsg, WPARAM wParam, LPARAM lParam)
\r
5809 static UINT *number;
\r
5810 OPENFILENAME *ofname;
\r
5813 case WM_INITDIALOG:
\r
5814 Translate(hdlg, DLG_IndexNumber);
\r
5815 ofname = (OPENFILENAME *)lParam;
\r
5816 number = (UINT *)(ofname->lCustData);
\r
5819 ofnot = (OFNOTIFY *)lParam;
\r
5820 if (ofnot->hdr.code == CDN_FILEOK) {
\r
5821 *number = GetDlgItemInt(hdlg, OPT_IndexNumber, NULL, FALSE);
\r
5830 OpenFileDialog(HWND hwnd, char *write, char *defName, char *defExt, // [HGM] diag: type of 'write' now string
\r
5831 char *nameFilt, char *dlgTitle, UINT *number,
\r
5832 char fileTitle[MSG_SIZ], char fileName[MSG_SIZ])
\r
5834 OPENFILENAME openFileName;
\r
5835 char buf1[MSG_SIZ];
\r
5838 if (fileName == NULL) fileName = buf1;
\r
5839 if (defName == NULL) {
\r
5840 safeStrCpy(fileName, "*.", 3 );
\r
5841 strcat(fileName, defExt);
\r
5843 safeStrCpy(fileName, defName, MSG_SIZ );
\r
5845 if (fileTitle) safeStrCpy(fileTitle, "", MSG_SIZ );
\r
5846 if (number) *number = 0;
\r
5848 openFileName.lStructSize = sizeof(OPENFILENAME);
\r
5849 openFileName.hwndOwner = hwnd;
\r
5850 openFileName.hInstance = (HANDLE) hInst;
\r
5851 openFileName.lpstrFilter = nameFilt;
\r
5852 openFileName.lpstrCustomFilter = (LPSTR) NULL;
\r
5853 openFileName.nMaxCustFilter = 0L;
\r
5854 openFileName.nFilterIndex = 1L;
\r
5855 openFileName.lpstrFile = fileName;
\r
5856 openFileName.nMaxFile = MSG_SIZ;
\r
5857 openFileName.lpstrFileTitle = fileTitle;
\r
5858 openFileName.nMaxFileTitle = fileTitle ? MSG_SIZ : 0;
\r
5859 openFileName.lpstrInitialDir = NULL;
\r
5860 openFileName.lpstrTitle = dlgTitle;
\r
5861 openFileName.Flags = OFN_PATHMUSTEXIST | OFN_HIDEREADONLY
\r
5862 | (write[0] != 'r' ? 0 : OFN_FILEMUSTEXIST)
\r
5863 | (number ? OFN_ENABLETEMPLATE | OFN_ENABLEHOOK: 0)
\r
5864 | (oldDialog ? 0 : OFN_EXPLORER);
\r
5865 openFileName.nFileOffset = 0;
\r
5866 openFileName.nFileExtension = 0;
\r
5867 openFileName.lpstrDefExt = defExt;
\r
5868 openFileName.lCustData = (LONG) number;
\r
5869 openFileName.lpfnHook = oldDialog ?
\r
5870 (LPOFNHOOKPROC) OldOpenFileHook : (LPOFNHOOKPROC) OpenFileHook;
\r
5871 openFileName.lpTemplateName = (LPSTR)(oldDialog ? 1536 : DLG_IndexNumber);
\r
5873 if (write[0] != 'r' ? GetSaveFileName(&openFileName) :
\r
5874 GetOpenFileName(&openFileName)) {
\r
5875 /* open the file */
\r
5876 f = fopen(openFileName.lpstrFile, write);
\r
5878 MessageBox(hwnd, _("File open failed"), NULL,
\r
5879 MB_OK|MB_ICONEXCLAMATION);
\r
5883 int err = CommDlgExtendedError();
\r
5884 if (err != 0) DisplayError(_("Internal error in file dialog box"), err);
\r
5893 MenuPopup(HWND hwnd, POINT pt, HMENU hmenu, UINT def)
\r
5895 HMENU hmenuTrackPopup; /* floating pop-up menu */
\r
5898 * Get the first pop-up menu in the menu template. This is the
\r
5899 * menu that TrackPopupMenu displays.
\r
5901 hmenuTrackPopup = GetSubMenu(hmenu, 0);
\r
5902 TranslateOneMenu(10, hmenuTrackPopup);
\r
5904 SetMenuDefaultItem(hmenuTrackPopup, def, FALSE);
\r
5907 * TrackPopup uses screen coordinates, so convert the
\r
5908 * coordinates of the mouse click to screen coordinates.
\r
5910 ClientToScreen(hwnd, (LPPOINT) &pt);
\r
5912 /* Draw and track the floating pop-up menu. */
\r
5913 TrackPopupMenu(hmenuTrackPopup, TPM_CENTERALIGN | TPM_RIGHTBUTTON,
\r
5914 pt.x, pt.y, 0, hwnd, NULL);
\r
5916 /* Destroy the menu.*/
\r
5917 DestroyMenu(hmenu);
\r
5922 int sizeX, sizeY, newSizeX, newSizeY;
\r
5924 } ResizeEditPlusButtonsClosure;
\r
5927 ResizeEditPlusButtonsCallback(HWND hChild, LPARAM lparam)
\r
5929 ResizeEditPlusButtonsClosure *cl = (ResizeEditPlusButtonsClosure *)lparam;
\r
5933 if (hChild == cl->hText) return TRUE;
\r
5934 GetWindowRect(hChild, &rect); /* gives screen coords */
\r
5935 pt.x = rect.left + (cl->newSizeX - cl->sizeX)/2;
\r
5936 pt.y = rect.top + cl->newSizeY - cl->sizeY;
\r
5937 ScreenToClient(cl->hDlg, &pt);
\r
5938 cl->hdwp = DeferWindowPos(cl->hdwp, hChild, NULL,
\r
5939 pt.x, pt.y, rect.right - rect.left, rect.bottom - rect.top, SWP_NOZORDER);
\r
5943 /* Resize a dialog that has a (rich) edit field filling most of
\r
5944 the top, with a row of buttons below */
\r
5946 ResizeEditPlusButtons(HWND hDlg, HWND hText, int sizeX, int sizeY, int newSizeX, int newSizeY)
\r
5949 int newTextHeight, newTextWidth;
\r
5950 ResizeEditPlusButtonsClosure cl;
\r
5952 /*if (IsIconic(hDlg)) return;*/
\r
5953 if (newSizeX == sizeX && newSizeY == sizeY) return;
\r
5955 cl.hdwp = BeginDeferWindowPos(8);
\r
5957 GetWindowRect(hText, &rectText); /* gives screen coords */
\r
5958 newTextWidth = rectText.right - rectText.left + newSizeX - sizeX;
\r
5959 newTextHeight = rectText.bottom - rectText.top + newSizeY - sizeY;
\r
5960 if (newTextHeight < 0) {
\r
5961 newSizeY += -newTextHeight;
\r
5962 newTextHeight = 0;
\r
5964 cl.hdwp = DeferWindowPos(cl.hdwp, hText, NULL, 0, 0,
\r
5965 newTextWidth, newTextHeight, SWP_NOZORDER|SWP_NOMOVE);
\r
5971 cl.newSizeX = newSizeX;
\r
5972 cl.newSizeY = newSizeY;
\r
5973 EnumChildWindows(hDlg, ResizeEditPlusButtonsCallback, (LPARAM)&cl);
\r
5975 EndDeferWindowPos(cl.hdwp);
\r
5978 BOOL CenterWindowEx(HWND hwndChild, HWND hwndParent, int mode)
\r
5980 RECT rChild, rParent;
\r
5981 int wChild, hChild, wParent, hParent;
\r
5982 int wScreen, hScreen, xNew, yNew;
\r
5985 /* Get the Height and Width of the child window */
\r
5986 GetWindowRect (hwndChild, &rChild);
\r
5987 wChild = rChild.right - rChild.left;
\r
5988 hChild = rChild.bottom - rChild.top;
\r
5990 /* Get the Height and Width of the parent window */
\r
5991 GetWindowRect (hwndParent, &rParent);
\r
5992 wParent = rParent.right - rParent.left;
\r
5993 hParent = rParent.bottom - rParent.top;
\r
5995 /* Get the display limits */
\r
5996 hdc = GetDC (hwndChild);
\r
5997 wScreen = GetDeviceCaps (hdc, HORZRES);
\r
5998 hScreen = GetDeviceCaps (hdc, VERTRES);
\r
5999 ReleaseDC(hwndChild, hdc);
\r
6001 /* Calculate new X position, then adjust for screen */
\r
6002 xNew = rParent.left + ((wParent - wChild) /2);
\r
6005 } else if ((xNew+wChild) > wScreen) {
\r
6006 xNew = wScreen - wChild;
\r
6009 /* Calculate new Y position, then adjust for screen */
\r
6011 yNew = rParent.top + ((hParent - hChild) /2);
\r
6014 yNew = rParent.top + GetSystemMetrics( SM_CYCAPTION ) * 2 / 3;
\r
6019 } else if ((yNew+hChild) > hScreen) {
\r
6020 yNew = hScreen - hChild;
\r
6023 /* Set it, and return */
\r
6024 return SetWindowPos (hwndChild, NULL,
\r
6025 xNew, yNew, 0, 0, SWP_NOSIZE | SWP_NOZORDER);
\r
6028 /* Center one window over another */
\r
6029 BOOL CenterWindow (HWND hwndChild, HWND hwndParent)
\r
6031 return CenterWindowEx( hwndChild, hwndParent, 0 );
\r
6034 /*---------------------------------------------------------------------------*\
\r
6036 * Startup Dialog functions
\r
6038 \*---------------------------------------------------------------------------*/
\r
6040 InitComboStrings(HANDLE hwndCombo, char **cd)
\r
6042 SendMessage(hwndCombo, CB_RESETCONTENT, 0, 0);
\r
6044 while (*cd != NULL) {
\r
6045 SendMessage(hwndCombo, CB_ADDSTRING, 0, (LPARAM) T_(*cd));
\r
6051 InitComboStringsFromOption(HANDLE hwndCombo, char *str)
\r
6053 char buf1[MAX_ARG_LEN];
\r
6056 if (str[0] == '@') {
\r
6057 FILE* f = fopen(str + 1, "r");
\r
6059 DisplayFatalError(str + 1, errno, 2);
\r
6062 len = fread(buf1, 1, sizeof(buf1)-1, f);
\r
6064 buf1[len] = NULLCHAR;
\r
6068 SendMessage(hwndCombo, CB_RESETCONTENT, 0, 0);
\r
6071 char buf[MSG_SIZ];
\r
6072 char *end = strchr(str, '\n');
\r
6073 if (end == NULL) return;
\r
6074 memcpy(buf, str, end - str);
\r
6075 buf[end - str] = NULLCHAR;
\r
6076 SendMessage(hwndCombo, CB_ADDSTRING, 0, (LPARAM) buf);
\r
6082 SetStartupDialogEnables(HWND hDlg)
\r
6084 EnableWindow(GetDlgItem(hDlg, OPT_ChessEngineName),
\r
6085 IsDlgButtonChecked(hDlg, OPT_ChessEngine) ||
\r
6086 (appData.zippyPlay && IsDlgButtonChecked(hDlg, OPT_ChessServer)));
\r
6087 EnableWindow(GetDlgItem(hDlg, OPT_SecondChessEngineName),
\r
6088 IsDlgButtonChecked(hDlg, OPT_ChessEngine));
\r
6089 EnableWindow(GetDlgItem(hDlg, OPT_ChessServerName),
\r
6090 IsDlgButtonChecked(hDlg, OPT_ChessServer));
\r
6091 EnableWindow(GetDlgItem(hDlg, OPT_AdditionalOptions),
\r
6092 IsDlgButtonChecked(hDlg, OPT_AnyAdditional));
\r
6093 EnableWindow(GetDlgItem(hDlg, IDOK),
\r
6094 IsDlgButtonChecked(hDlg, OPT_ChessEngine) ||
\r
6095 IsDlgButtonChecked(hDlg, OPT_ChessServer) ||
\r
6096 IsDlgButtonChecked(hDlg, OPT_View));
\r
6100 QuoteForFilename(char *filename)
\r
6102 int dquote, space;
\r
6103 dquote = strchr(filename, '"') != NULL;
\r
6104 space = strchr(filename, ' ') != NULL;
\r
6105 if (dquote || space) {
\r
6117 InitEngineBox(HWND hDlg, HWND hwndCombo, char* nthcp, char* nthd, char* nthdir, char *nthnames)
\r
6119 char buf[MSG_SIZ];
\r
6122 InitComboStringsFromOption(hwndCombo, nthnames);
\r
6123 q = QuoteForFilename(nthcp);
\r
6124 snprintf(buf, MSG_SIZ, "%s%s%s", q, nthcp, q);
\r
6125 if (*nthdir != NULLCHAR) {
\r
6126 q = QuoteForFilename(nthdir);
\r
6127 snprintf(buf + strlen(buf), MSG_SIZ, " /%s=%s%s%s", nthd, q, nthdir, q);
\r
6129 if (*nthcp == NULLCHAR) {
\r
6130 SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) 0, (LPARAM) 0);
\r
6131 } else if (SendMessage(hwndCombo, CB_SELECTSTRING, (WPARAM) -1, (LPARAM) buf) == CB_ERR) {
\r
6132 SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) -1, (LPARAM) 0);
\r
6133 SendMessage(hwndCombo, WM_SETTEXT, (WPARAM) 0, (LPARAM) buf);
\r
6138 StartupDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
6140 char buf[MSG_SIZ];
\r
6144 switch (message) {
\r
6145 case WM_INITDIALOG:
\r
6146 /* Center the dialog */
\r
6147 CenterWindow (hDlg, GetDesktopWindow());
\r
6148 Translate(hDlg, DLG_Startup);
\r
6149 /* Initialize the dialog items */
\r
6150 InitEngineBox(hDlg, GetDlgItem(hDlg, OPT_ChessEngineName),
\r
6151 appData.firstChessProgram, "fd", appData.firstDirectory,
\r
6152 firstChessProgramNames);
\r
6153 InitEngineBox(hDlg, GetDlgItem(hDlg, OPT_SecondChessEngineName),
\r
6154 appData.secondChessProgram, singleList ? "fd" : "sd", appData.secondDirectory,
\r
6155 singleList ? firstChessProgramNames : secondChessProgramNames); //[HGM] single: use first list in second combo
\r
6156 hwndCombo = GetDlgItem(hDlg, OPT_ChessServerName);
\r
6157 InitComboStringsFromOption(hwndCombo, icsNames);
\r
6158 snprintf(buf, MSG_SIZ, "%s /icsport=%s", appData.icsHost, appData.icsPort);
\r
6159 if (*appData.icsHelper != NULLCHAR) {
\r
6160 char *q = QuoteForFilename(appData.icsHelper);
\r
6161 sprintf(buf + strlen(buf), " /icshelper=%s%s%s", q, appData.icsHelper, q);
\r
6163 if (*appData.icsHost == NULLCHAR) {
\r
6164 SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) 0, (LPARAM) 0);
\r
6165 /*SendMessage(hwndCombo, CB_SHOWDROPDOWN, (WPARAM) TRUE, (LPARAM) 0); !!too soon */
\r
6166 } else if (SendMessage(hwndCombo, CB_SELECTSTRING, (WPARAM) -1, (LPARAM) buf) == CB_ERR) {
\r
6167 SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) -1, (LPARAM) 0);
\r
6168 SendMessage(hwndCombo, WM_SETTEXT, (WPARAM) 0, (LPARAM) buf);
\r
6171 if (appData.icsActive) {
\r
6172 CheckDlgButton(hDlg, OPT_ChessServer, BST_CHECKED);
\r
6174 else if (appData.noChessProgram) {
\r
6175 CheckDlgButton(hDlg, OPT_View, BST_CHECKED);
\r
6178 CheckDlgButton(hDlg, OPT_ChessEngine, BST_CHECKED);
\r
6181 SetStartupDialogEnables(hDlg);
\r
6185 switch (LOWORD(wParam)) {
\r
6187 if (IsDlgButtonChecked(hDlg, OPT_ChessEngine)) {
\r
6188 safeStrCpy(buf, "/fcp=", sizeof(buf)/sizeof(buf[0]) );
\r
6189 GetDlgItemText(hDlg, OPT_ChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));
\r
6191 comboLine = strdup(p+5); // [HGM] recent: remember complete line of first combobox
\r
6192 ParseArgs(StringGet, &p);
\r
6193 safeStrCpy(buf, singleList ? "/fcp=" : "/scp=", sizeof(buf)/sizeof(buf[0]) );
\r
6194 GetDlgItemText(hDlg, OPT_SecondChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));
\r
6196 SwapEngines(singleList); // temporarily swap first and second, to load a second 'first', ...
\r
6197 ParseArgs(StringGet, &p);
\r
6198 SwapEngines(singleList); // ... and then make it 'second'
\r
6199 appData.noChessProgram = FALSE;
\r
6200 appData.icsActive = FALSE;
\r
6201 } else if (IsDlgButtonChecked(hDlg, OPT_ChessServer)) {
\r
6202 safeStrCpy(buf, "/ics /icshost=", sizeof(buf)/sizeof(buf[0]) );
\r
6203 GetDlgItemText(hDlg, OPT_ChessServerName, buf + strlen(buf), sizeof(buf) - strlen(buf));
\r
6205 ParseArgs(StringGet, &p);
\r
6206 if (appData.zippyPlay) {
\r
6207 safeStrCpy(buf, "/fcp=", sizeof(buf)/sizeof(buf[0]) );
\r
6208 GetDlgItemText(hDlg, OPT_ChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));
\r
6210 ParseArgs(StringGet, &p);
\r
6212 } else if (IsDlgButtonChecked(hDlg, OPT_View)) {
\r
6213 appData.noChessProgram = TRUE;
\r
6214 appData.icsActive = FALSE;
\r
6216 MessageBox(hDlg, _("Choose an option, or cancel to exit"),
\r
6217 _("Option Error"), MB_OK|MB_ICONEXCLAMATION);
\r
6220 if (IsDlgButtonChecked(hDlg, OPT_AnyAdditional)) {
\r
6221 GetDlgItemText(hDlg, OPT_AdditionalOptions, buf, sizeof(buf));
\r
6223 ParseArgs(StringGet, &p);
\r
6225 EndDialog(hDlg, TRUE);
\r
6232 case IDM_HELPCONTENTS:
\r
6233 if (!WinHelp (hDlg, "winboard.hlp", HELP_KEY,(DWORD)(LPSTR)"CONTENTS")) {
\r
6234 MessageBox (GetFocus(),
\r
6235 _("Unable to activate help"),
\r
6236 szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);
\r
6241 SetStartupDialogEnables(hDlg);
\r
6249 /*---------------------------------------------------------------------------*\
\r
6251 * About box dialog functions
\r
6253 \*---------------------------------------------------------------------------*/
\r
6255 /* Process messages for "About" dialog box */
\r
6257 About(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
6259 switch (message) {
\r
6260 case WM_INITDIALOG: /* message: initialize dialog box */
\r
6261 /* Center the dialog over the application window */
\r
6262 CenterWindow (hDlg, GetWindow (hDlg, GW_OWNER));
\r
6263 SetDlgItemText(hDlg, ABOUTBOX_Version, programVersion);
\r
6264 Translate(hDlg, ABOUTBOX);
\r
6268 case WM_COMMAND: /* message: received a command */
\r
6269 if (LOWORD(wParam) == IDOK /* "OK" box selected? */
\r
6270 || LOWORD(wParam) == IDCANCEL) { /* System menu close command? */
\r
6271 EndDialog(hDlg, TRUE); /* Exit the dialog */
\r
6279 /*---------------------------------------------------------------------------*\
\r
6281 * Comment Dialog functions
\r
6283 \*---------------------------------------------------------------------------*/
\r
6286 CommentDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
6288 static HANDLE hwndText = NULL;
\r
6289 int len, newSizeX, newSizeY, flags;
\r
6290 static int sizeX, sizeY;
\r
6295 switch (message) {
\r
6296 case WM_INITDIALOG: /* message: initialize dialog box */
\r
6297 /* Initialize the dialog items */
\r
6298 Translate(hDlg, DLG_EditComment);
\r
6299 hwndText = GetDlgItem(hDlg, OPT_CommentText);
\r
6300 SetDlgItemText(hDlg, OPT_CommentText, commentText);
\r
6301 EnableWindow(GetDlgItem(hDlg, OPT_CancelComment), editComment);
\r
6302 EnableWindow(GetDlgItem(hDlg, OPT_ClearComment), editComment);
\r
6303 EnableWindow(GetDlgItem(hDlg, OPT_EditComment), !editComment);
\r
6304 SendMessage(hwndText, EM_SETREADONLY, !editComment, 0);
\r
6305 SetWindowText(hDlg, commentTitle);
\r
6306 if (editComment) {
\r
6307 SetFocus(hwndText);
\r
6309 SetFocus(GetDlgItem(hDlg, IDOK));
\r
6311 SendMessage(GetDlgItem(hDlg, OPT_CommentText),
\r
6312 WM_SETFONT, (WPARAM)font[boardSize][COMMENT_FONT]->hf,
\r
6313 MAKELPARAM(FALSE, 0));
\r
6314 /* Size and position the dialog */
\r
6315 if (!commentDialog) {
\r
6316 commentDialog = hDlg;
\r
6317 flags = SWP_NOZORDER;
\r
6318 GetClientRect(hDlg, &rect);
\r
6319 sizeX = rect.right;
\r
6320 sizeY = rect.bottom;
\r
6321 if (wpComment.x != CW_USEDEFAULT && wpComment.y != CW_USEDEFAULT &&
\r
6322 wpComment.width != CW_USEDEFAULT && wpComment.height != CW_USEDEFAULT) {
\r
6323 WINDOWPLACEMENT wp;
\r
6324 EnsureOnScreen(&wpComment.x, &wpComment.y, 0, 0);
\r
6325 wp.length = sizeof(WINDOWPLACEMENT);
\r
6327 wp.showCmd = SW_SHOW;
\r
6328 wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;
\r
6329 wp.rcNormalPosition.left = wpComment.x;
\r
6330 wp.rcNormalPosition.right = wpComment.x + wpComment.width;
\r
6331 wp.rcNormalPosition.top = wpComment.y;
\r
6332 wp.rcNormalPosition.bottom = wpComment.y + wpComment.height;
\r
6333 SetWindowPlacement(hDlg, &wp);
\r
6335 GetClientRect(hDlg, &rect);
\r
6336 newSizeX = rect.right;
\r
6337 newSizeY = rect.bottom;
\r
6338 ResizeEditPlusButtons(hDlg, hwndText, sizeX, sizeY,
\r
6339 newSizeX, newSizeY);
\r
6344 SendDlgItemMessage( hDlg, OPT_CommentText, EM_SETEVENTMASK, 0, ENM_MOUSEEVENTS | ENM_KEYEVENTS );
\r
6347 case WM_COMMAND: /* message: received a command */
\r
6348 switch (LOWORD(wParam)) {
\r
6350 if (editComment) {
\r
6352 /* Read changed options from the dialog box */
\r
6353 hwndText = GetDlgItem(hDlg, OPT_CommentText);
\r
6354 len = GetWindowTextLength(hwndText);
\r
6355 str = (char *) malloc(len + 1);
\r
6356 GetWindowText(hwndText, str, len + 1);
\r
6365 ReplaceComment(commentIndex, str);
\r
6372 case OPT_CancelComment:
\r
6376 case OPT_ClearComment:
\r
6377 SetDlgItemText(hDlg, OPT_CommentText, "");
\r
6380 case OPT_EditComment:
\r
6381 EditCommentEvent();
\r
6389 case WM_NOTIFY: // [HGM] vari: cloned from whistory.c
\r
6390 if( wParam == OPT_CommentText ) {
\r
6391 MSGFILTER * lpMF = (MSGFILTER *) lParam;
\r
6393 if( lpMF->msg == WM_RBUTTONDOWN && (lpMF->wParam & (MK_CONTROL | MK_SHIFT)) == 0 ||
\r
6394 lpMF->msg == WM_CHAR && lpMF->wParam == '\022' ) {
\r
6398 pt.x = LOWORD( lpMF->lParam );
\r
6399 pt.y = HIWORD( lpMF->lParam );
\r
6401 if(lpMF->msg == WM_CHAR) {
\r
6403 SendDlgItemMessage( hDlg, OPT_CommentText, EM_EXGETSEL, 0, (LPARAM) &sel );
\r
6404 index = sel.cpMin;
\r
6406 index = SendDlgItemMessage( hDlg, OPT_CommentText, EM_CHARFROMPOS, 0, (LPARAM) &pt );
\r
6408 hwndText = GetDlgItem(hDlg, OPT_CommentText); // cloned from above
\r
6409 len = GetWindowTextLength(hwndText);
\r
6410 str = (char *) malloc(len + 1);
\r
6411 GetWindowText(hwndText, str, len + 1);
\r
6412 ReplaceComment(commentIndex, str);
\r
6413 if(commentIndex != currentMove) ToNrEvent(commentIndex);
\r
6414 LoadVariation( index, str ); // [HGM] also does the actual moving to it, now
\r
6417 /* Zap the message for good: apparently, returning non-zero is not enough */
\r
6418 lpMF->msg = WM_USER;
\r
6426 newSizeX = LOWORD(lParam);
\r
6427 newSizeY = HIWORD(lParam);
\r
6428 ResizeEditPlusButtons(hDlg, hwndText, sizeX, sizeY, newSizeX, newSizeY);
\r
6433 case WM_GETMINMAXINFO:
\r
6434 /* Prevent resizing window too small */
\r
6435 mmi = (MINMAXINFO *) lParam;
\r
6436 mmi->ptMinTrackSize.x = 100;
\r
6437 mmi->ptMinTrackSize.y = 100;
\r
6444 EitherCommentPopUp(int index, char *title, char *str, BOOLEAN edit)
\r
6449 CheckMenuItem(GetMenu(hwndMain), IDM_Comment, edit ? MF_CHECKED : MF_UNCHECKED);
\r
6451 if (str == NULL) str = "";
\r
6452 p = (char *) malloc(2 * strlen(str) + 2);
\r
6455 if (*str == '\n') *q++ = '\r';
\r
6459 if (commentText != NULL) free(commentText);
\r
6461 commentIndex = index;
\r
6462 commentTitle = title;
\r
6464 editComment = edit;
\r
6466 if (commentDialog) {
\r
6467 SendMessage(commentDialog, WM_INITDIALOG, 0, 0);
\r
6468 if (!commentUp) ShowWindow(commentDialog, SW_SHOW);
\r
6470 lpProc = MakeProcInstance((FARPROC)CommentDialog, hInst);
\r
6471 CreateDialog(hInst, MAKEINTRESOURCE(DLG_EditComment),
\r
6472 hwndMain, (DLGPROC)lpProc);
\r
6473 FreeProcInstance(lpProc);
\r
6479 /*---------------------------------------------------------------------------*\
\r
6481 * Type-in move dialog functions
\r
6483 \*---------------------------------------------------------------------------*/
\r
6486 TypeInMoveDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
6488 char move[MSG_SIZ];
\r
6491 switch (message) {
\r
6492 case WM_INITDIALOG:
\r
6493 move[0] = (char) lParam;
\r
6494 move[1] = NULLCHAR;
\r
6495 CenterWindowEx(hDlg, GetWindow(hDlg, GW_OWNER), 1 );
\r
6496 Translate(hDlg, DLG_TypeInMove);
\r
6497 hInput = GetDlgItem(hDlg, OPT_Move);
\r
6498 SetWindowText(hInput, move);
\r
6500 SendMessage(hInput, EM_SETSEL, (WPARAM)9999, (LPARAM)9999);
\r
6504 switch (LOWORD(wParam)) {
\r
6507 shiftKey = GetKeyState(VK_SHIFT) < 0; // [HGM] remember last shift status
\r
6508 GetDlgItemText(hDlg, OPT_Move, move, sizeof(move));
\r
6509 TypeInDoneEvent(move);
\r
6510 EndDialog(hDlg, TRUE);
\r
6513 EndDialog(hDlg, FALSE);
\r
6524 PopUpMoveDialog(char firstchar)
\r
6528 lpProc = MakeProcInstance((FARPROC)TypeInMoveDialog, hInst);
\r
6529 DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_TypeInMove),
\r
6530 hwndMain, (DLGPROC)lpProc, (LPARAM)firstchar);
\r
6531 FreeProcInstance(lpProc);
\r
6534 /*---------------------------------------------------------------------------*\
\r
6536 * Type-in name dialog functions
\r
6538 \*---------------------------------------------------------------------------*/
\r
6541 TypeInNameDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
6543 char move[MSG_SIZ];
\r
6546 switch (message) {
\r
6547 case WM_INITDIALOG:
\r
6548 move[0] = (char) lParam;
\r
6549 move[1] = NULLCHAR;
\r
6550 CenterWindowEx(hDlg, GetWindow(hDlg, GW_OWNER), 1 );
\r
6551 Translate(hDlg, DLG_TypeInName);
\r
6552 hInput = GetDlgItem(hDlg, OPT_Name);
\r
6553 SetWindowText(hInput, move);
\r
6555 SendMessage(hInput, EM_SETSEL, (WPARAM)9999, (LPARAM)9999);
\r
6559 switch (LOWORD(wParam)) {
\r
6561 GetDlgItemText(hDlg, OPT_Name, move, sizeof(move));
\r
6562 appData.userName = strdup(move);
\r
6565 if(gameMode == MachinePlaysWhite || gameMode == MachinePlaysBlack) {
\r
6566 snprintf(move, MSG_SIZ, "%s vs. %s", gameInfo.white, gameInfo.black);
\r
6567 DisplayTitle(move);
\r
6571 EndDialog(hDlg, TRUE);
\r
6574 EndDialog(hDlg, FALSE);
\r
6585 PopUpNameDialog(char firstchar)
\r
6589 lpProc = MakeProcInstance((FARPROC)TypeInNameDialog, hInst);
\r
6590 DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_TypeInName),
\r
6591 hwndMain, (DLGPROC)lpProc, (LPARAM)firstchar);
\r
6592 FreeProcInstance(lpProc);
\r
6595 /*---------------------------------------------------------------------------*\
\r
6599 \*---------------------------------------------------------------------------*/
\r
6601 /* Nonmodal error box */
\r
6602 LRESULT CALLBACK ErrorDialog(HWND hDlg, UINT message,
\r
6603 WPARAM wParam, LPARAM lParam);
\r
6606 ErrorPopUp(char *title, char *content)
\r
6610 BOOLEAN modal = hwndMain == NULL;
\r
6628 strncpy(errorTitle, title, sizeof(errorTitle));
\r
6629 errorTitle[sizeof(errorTitle) - 1] = '\0';
\r
6632 MessageBox(NULL, errorMessage, errorTitle, MB_OK|MB_ICONEXCLAMATION);
\r
6634 lpProc = MakeProcInstance((FARPROC)ErrorDialog, hInst);
\r
6635 CreateDialog(hInst, MAKEINTRESOURCE(DLG_Error),
\r
6636 hwndMain, (DLGPROC)lpProc);
\r
6637 FreeProcInstance(lpProc);
\r
6644 if (!appData.popupMoveErrors && moveErrorMessageUp) DisplayMessage("", "");
\r
6645 if (errorDialog == NULL) return;
\r
6646 DestroyWindow(errorDialog);
\r
6647 errorDialog = NULL;
\r
6648 if(errorExitStatus) ExitEvent(errorExitStatus);
\r
6652 ErrorDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
6657 switch (message) {
\r
6658 case WM_INITDIALOG:
\r
6659 GetWindowRect(hDlg, &rChild);
\r
6662 SetWindowPos(hDlg, NULL, rChild.left,
\r
6663 rChild.top + boardRect.top - (rChild.bottom - rChild.top),
\r
6664 0, 0, SWP_NOZORDER|SWP_NOSIZE);
\r
6668 [AS] It seems that the above code wants to move the dialog up in the "caption
\r
6669 area" of the main window, but it uses the dialog height as an hard-coded constant,
\r
6670 and it doesn't work when you resize the dialog.
\r
6671 For now, just give it a default position.
\r
6673 SetWindowPos(hDlg, NULL, boardRect.left+8, boardRect.top+8, 0, 0, SWP_NOZORDER|SWP_NOSIZE);
\r
6674 Translate(hDlg, DLG_Error);
\r
6676 errorDialog = hDlg;
\r
6677 SetWindowText(hDlg, errorTitle);
\r
6678 hwndText = GetDlgItem(hDlg, OPT_ErrorText);
\r
6679 SetDlgItemText(hDlg, OPT_ErrorText, errorMessage);
\r
6683 switch (LOWORD(wParam)) {
\r
6686 if (errorDialog == hDlg) errorDialog = NULL;
\r
6687 DestroyWindow(hDlg);
\r
6699 HWND gothicDialog = NULL;
\r
6702 GothicDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
6706 int height = GetSystemMetrics(SM_CYCAPTION)+GetSystemMetrics(SM_CYFRAME);
\r
6708 switch (message) {
\r
6709 case WM_INITDIALOG:
\r
6710 GetWindowRect(hDlg, &rChild);
\r
6712 SetWindowPos(hDlg, NULL, wpMain.x, wpMain.y-height, wpMain.width, height,
\r
6716 [AS] It seems that the above code wants to move the dialog up in the "caption
\r
6717 area" of the main window, but it uses the dialog height as an hard-coded constant,
\r
6718 and it doesn't work when you resize the dialog.
\r
6719 For now, just give it a default position.
\r
6721 gothicDialog = hDlg;
\r
6722 SetWindowText(hDlg, errorTitle);
\r
6723 hwndText = GetDlgItem(hDlg, OPT_ErrorText);
\r
6724 SetDlgItemText(hDlg, OPT_ErrorText, errorMessage);
\r
6728 switch (LOWORD(wParam)) {
\r
6731 if (errorDialog == hDlg) errorDialog = NULL;
\r
6732 DestroyWindow(hDlg);
\r
6744 GothicPopUp(char *title, VariantClass variant)
\r
6747 static char *lastTitle;
\r
6749 strncpy(errorTitle, title, sizeof(errorTitle));
\r
6750 errorTitle[sizeof(errorTitle) - 1] = '\0';
\r
6752 if(lastTitle != title && gothicDialog != NULL) {
\r
6753 DestroyWindow(gothicDialog);
\r
6754 gothicDialog = NULL;
\r
6756 if(variant != VariantNormal && gothicDialog == NULL) {
\r
6757 title = lastTitle;
\r
6758 lpProc = MakeProcInstance((FARPROC)GothicDialog, hInst);
\r
6759 CreateDialog(hInst, MAKEINTRESOURCE(DLG_Error),
\r
6760 hwndMain, (DLGPROC)lpProc);
\r
6761 FreeProcInstance(lpProc);
\r
6766 /*---------------------------------------------------------------------------*\
\r
6768 * Ics Interaction console functions
\r
6770 \*---------------------------------------------------------------------------*/
\r
6772 #define HISTORY_SIZE 64
\r
6773 static char *history[HISTORY_SIZE];
\r
6774 int histIn = 0, histP = 0;
\r
6777 SaveInHistory(char *cmd)
\r
6779 if (history[histIn] != NULL) {
\r
6780 free(history[histIn]);
\r
6781 history[histIn] = NULL;
\r
6783 if (*cmd == NULLCHAR) return;
\r
6784 history[histIn] = StrSave(cmd);
\r
6785 histIn = (histIn + 1) % HISTORY_SIZE;
\r
6786 if (history[histIn] != NULL) {
\r
6787 free(history[histIn]);
\r
6788 history[histIn] = NULL;
\r
6794 PrevInHistory(char *cmd)
\r
6797 if (histP == histIn) {
\r
6798 if (history[histIn] != NULL) free(history[histIn]);
\r
6799 history[histIn] = StrSave(cmd);
\r
6801 newhp = (histP - 1 + HISTORY_SIZE) % HISTORY_SIZE;
\r
6802 if (newhp == histIn || history[newhp] == NULL) return NULL;
\r
6804 return history[histP];
\r
6810 if (histP == histIn) return NULL;
\r
6811 histP = (histP + 1) % HISTORY_SIZE;
\r
6812 return history[histP];
\r
6816 LoadIcsTextMenu(IcsTextMenuEntry *e)
\r
6820 hmenu = LoadMenu(hInst, "TextMenu");
\r
6821 h = GetSubMenu(hmenu, 0);
\r
6823 if (strcmp(e->item, "-") == 0) {
\r
6824 AppendMenu(h, MF_SEPARATOR, 0, 0);
\r
6825 } else { // [HGM] re-written a bit to use only one AppendMenu call for both cases (| or no |)
\r
6826 int flags = MF_STRING, j = 0;
\r
6827 if (e->item[0] == '|') {
\r
6828 flags |= MF_MENUBARBREAK;
\r
6831 if(!strcmp(e->command, "none")) flags |= MF_GRAYED; // [HGM] chatclick: provide inactive dummy
\r
6832 AppendMenu(h, flags, IDM_CommandX + i, e->item + j);
\r
6840 WNDPROC consoleTextWindowProc;
\r
6843 CommandX(HWND hwnd, char *command, BOOLEAN getname, BOOLEAN immediate)
\r
6845 char buf[MSG_SIZ], name[MSG_SIZ];
\r
6846 HWND hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
6850 SetWindowText(hInput, command);
\r
6852 SendMessage(hInput, WM_CHAR, '\r', 0);
\r
6854 sel.cpMin = 999999;
\r
6855 sel.cpMax = 999999;
\r
6856 SendMessage(hInput, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
6861 SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);
\r
6862 if (sel.cpMin == sel.cpMax) {
\r
6863 /* Expand to surrounding word */
\r
6866 tr.chrg.cpMax = sel.cpMin;
\r
6867 tr.chrg.cpMin = --sel.cpMin;
\r
6868 if (sel.cpMin < 0) break;
\r
6869 tr.lpstrText = name;
\r
6870 SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr);
\r
6871 } while (isalpha(name[0]) || isdigit(name[0]) || name[0] == '-');
\r
6875 tr.chrg.cpMin = sel.cpMax;
\r
6876 tr.chrg.cpMax = ++sel.cpMax;
\r
6877 tr.lpstrText = name;
\r
6878 if (SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr) < 1) break;
\r
6879 } while (isalpha(name[0]) || isdigit(name[0]) || name[0] == '-');
\r
6882 if (sel.cpMax == sel.cpMin || sel.cpMax - sel.cpMin > MSG_SIZ/2) {
\r
6883 MessageBeep(MB_ICONEXCLAMATION);
\r
6887 tr.lpstrText = name;
\r
6888 SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr);
\r
6890 if (sel.cpMax - sel.cpMin > MSG_SIZ/2) {
\r
6891 MessageBeep(MB_ICONEXCLAMATION);
\r
6894 SendMessage(hwnd, EM_GETSELTEXT, 0, (LPARAM) name);
\r
6897 if(strstr(command, "%s")) snprintf(buf, MSG_SIZ, command, name); else
\r
6898 snprintf(buf, MSG_SIZ, "%s %s", command, name);
\r
6899 SetWindowText(hInput, buf);
\r
6900 SendMessage(hInput, WM_CHAR, '\r', 0);
\r
6902 if(!strcmp(command, "chat")) { ChatPopUp(name); return; }
\r
6903 snprintf(buf, MSG_SIZ, "%s %s ", command, name); /* trailing space */
\r
6904 SetWindowText(hInput, buf);
\r
6905 sel.cpMin = 999999;
\r
6906 sel.cpMax = 999999;
\r
6907 SendMessage(hInput, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
6913 ConsoleTextSubclass(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
\r
6918 switch (message) {
\r
6920 if (!(GetKeyState(VK_CONTROL) & ~1)) break;
\r
6921 if(wParam=='R') return 0;
\r
6924 SendMessage(hwnd, EM_LINESCROLL, 0, -999999);
\r
6927 sel.cpMin = 999999;
\r
6928 sel.cpMax = 999999;
\r
6929 SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
6930 SendMessage(hwnd, EM_SCROLLCARET, 0, 0);
\r
6935 if(wParam != '\022') {
\r
6936 if (wParam == '\t') {
\r
6937 if (GetKeyState(VK_SHIFT) < 0) {
\r
6939 if (IsIconic(hwndMain)) ShowWindow(hwndMain, SW_RESTORE);
\r
6940 if (buttonDesc[0].hwnd) {
\r
6941 SetFocus(buttonDesc[0].hwnd);
\r
6943 SetFocus(hwndMain);
\r
6947 SetFocus(GetDlgItem(hwndConsole, OPT_ConsoleInput));
\r
6950 hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
6951 JAWS_DELETE( SetFocus(hInput); )
\r
6952 SendMessage(hInput, message, wParam, lParam);
\r
6955 } // [HGM] navigate: for Ctrl+R, flow into next case (moved up here) to summon up menu
\r
6957 case WM_RBUTTONDOWN:
\r
6958 if (!(GetKeyState(VK_SHIFT) & ~1)) {
\r
6959 /* Move selection here if it was empty */
\r
6961 pt.x = LOWORD(lParam);
\r
6962 pt.y = HIWORD(lParam);
\r
6963 SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);
\r
6964 if (sel.cpMin == sel.cpMax) {
\r
6965 if(lParam != -1) sel.cpMin = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&pt); /*doc is wrong*/
\r
6966 sel.cpMax = sel.cpMin;
\r
6967 SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
6969 SendMessage(hwnd, EM_HIDESELECTION, FALSE, FALSE);
\r
6970 { // [HGM] chatclick: code moved here from WM_RBUTTONUP case, to have menu appear on down-click
\r
6972 HMENU hmenu = LoadIcsTextMenu(icsTextMenuEntry);
\r
6973 SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);
\r
6974 if (sel.cpMin == sel.cpMax) {
\r
6975 EnableMenuItem(hmenu, IDM_Copy, MF_BYCOMMAND|MF_GRAYED);
\r
6976 EnableMenuItem(hmenu, IDM_QuickPaste, MF_BYCOMMAND|MF_GRAYED);
\r
6978 if (!IsClipboardFormatAvailable(CF_TEXT)) {
\r
6979 EnableMenuItem(hmenu, IDM_Paste, MF_BYCOMMAND|MF_GRAYED);
\r
6981 pt.x = LOWORD(lParam)-30; // [HGM] chatclick: make menu pop up with pointer above upper-right item
\r
6982 pt.y = HIWORD(lParam)-10; // make it appear as if mouse moved there, so it will be selected on up-click
\r
6983 PostMessage(hwnd, WM_MOUSEMOVE, wParam, lParam+5);
\r
6984 MenuPopup(hwnd, pt, hmenu, -1);
\r
6988 case WM_RBUTTONUP:
\r
6989 if (GetKeyState(VK_SHIFT) & ~1) {
\r
6990 SendDlgItemMessage(hwndConsole, OPT_ConsoleText,
\r
6991 WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);
\r
6995 hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
6997 return SendMessage(hInput, message, wParam, lParam);
\r
6998 case WM_MBUTTONDOWN:
\r
6999 return SendMessage(hwnd, WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);
\r
7001 switch (LOWORD(wParam)) {
\r
7002 case IDM_QuickPaste:
\r
7004 SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);
\r
7005 if (sel.cpMin == sel.cpMax) {
\r
7006 MessageBeep(MB_ICONEXCLAMATION);
\r
7009 SendMessage(hwnd, WM_COPY, 0, 0);
\r
7010 hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
7011 SendMessage(hInput, WM_PASTE, 0, 0);
\r
7016 SendMessage(hwnd, WM_CUT, 0, 0);
\r
7019 SendMessage(hwnd, WM_PASTE, 0, 0);
\r
7022 SendMessage(hwnd, WM_COPY, 0, 0);
\r
7026 int i = LOWORD(wParam) - IDM_CommandX;
\r
7027 if (i >= 0 && i < ICS_TEXT_MENU_SIZE &&
\r
7028 icsTextMenuEntry[i].command != NULL) {
\r
7029 CommandX(hwnd, icsTextMenuEntry[i].command,
\r
7030 icsTextMenuEntry[i].getname,
\r
7031 icsTextMenuEntry[i].immediate);
\r
7039 return (*consoleTextWindowProc)(hwnd, message, wParam, lParam);
\r
7042 WNDPROC consoleInputWindowProc;
\r
7045 ConsoleInputSubclass(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
\r
7047 char buf[MSG_SIZ];
\r
7049 static BOOL sendNextChar = FALSE;
\r
7050 static BOOL quoteNextChar = FALSE;
\r
7051 InputSource *is = consoleInputSource;
\r
7055 switch (message) {
\r
7057 if (!appData.localLineEditing || sendNextChar) {
\r
7058 is->buf[0] = (CHAR) wParam;
\r
7060 SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);
\r
7061 sendNextChar = FALSE;
\r
7064 if (quoteNextChar) {
\r
7065 buf[0] = (char) wParam;
\r
7066 buf[1] = NULLCHAR;
\r
7067 SendMessage(hwnd, EM_REPLACESEL, TRUE, (LPARAM) buf);
\r
7068 quoteNextChar = FALSE;
\r
7072 case '\r': /* Enter key */
\r
7073 is->count = GetWindowText(hwnd, is->buf, INPUT_SOURCE_BUF_SIZE-1);
\r
7074 if (consoleEcho) SaveInHistory(is->buf);
\r
7075 is->buf[is->count++] = '\n';
\r
7076 is->buf[is->count] = NULLCHAR;
\r
7077 SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);
\r
7078 if (consoleEcho) {
\r
7079 ConsoleOutput(is->buf, is->count, TRUE);
\r
7080 } else if (appData.localLineEditing) {
\r
7081 ConsoleOutput("\n", 1, TRUE);
\r
7084 case '\033': /* Escape key */
\r
7085 SetWindowText(hwnd, "");
\r
7086 cf.cbSize = sizeof(CHARFORMAT);
\r
7087 cf.dwMask = CFM_COLOR|CFM_BOLD|CFM_ITALIC|CFM_UNDERLINE|CFM_STRIKEOUT;
\r
7088 if (consoleEcho) {
\r
7089 cf.crTextColor = textAttribs[ColorNormal].color;
\r
7091 cf.crTextColor = COLOR_ECHOOFF;
\r
7093 cf.dwEffects = textAttribs[ColorNormal].effects;
\r
7094 SendMessage(hwnd, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf);
\r
7096 case '\t': /* Tab key */
\r
7097 if (GetKeyState(VK_SHIFT) < 0) {
\r
7099 SetFocus(GetDlgItem(hwndConsole, OPT_ConsoleText));
\r
7102 if (IsIconic(hwndMain)) ShowWindow(hwndMain, SW_RESTORE);
\r
7103 if (buttonDesc[0].hwnd) {
\r
7104 SetFocus(buttonDesc[0].hwnd);
\r
7106 SetFocus(hwndMain);
\r
7110 case '\023': /* Ctrl+S */
\r
7111 sendNextChar = TRUE;
\r
7113 case '\021': /* Ctrl+Q */
\r
7114 quoteNextChar = TRUE;
\r
7124 GetWindowText(hwnd, buf, MSG_SIZ);
\r
7125 p = PrevInHistory(buf);
\r
7127 SetWindowText(hwnd, p);
\r
7128 sel.cpMin = 999999;
\r
7129 sel.cpMax = 999999;
\r
7130 SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
7135 p = NextInHistory();
\r
7137 SetWindowText(hwnd, p);
\r
7138 sel.cpMin = 999999;
\r
7139 sel.cpMax = 999999;
\r
7140 SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
7146 if (!(GetKeyState(VK_CONTROL) & ~1)) break;
\r
7150 SendDlgItemMessage(hwndConsole, OPT_ConsoleText, message, wParam, lParam);
\r
7154 case WM_MBUTTONDOWN:
\r
7155 SendDlgItemMessage(hwndConsole, OPT_ConsoleText,
\r
7156 WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);
\r
7158 case WM_RBUTTONUP:
\r
7159 if (GetKeyState(VK_SHIFT) & ~1) {
\r
7160 SendDlgItemMessage(hwndConsole, OPT_ConsoleText,
\r
7161 WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);
\r
7165 hmenu = LoadMenu(hInst, "InputMenu");
\r
7166 SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);
\r
7167 if (sel.cpMin == sel.cpMax) {
\r
7168 EnableMenuItem(hmenu, IDM_Copy, MF_BYCOMMAND|MF_GRAYED);
\r
7169 EnableMenuItem(hmenu, IDM_Cut, MF_BYCOMMAND|MF_GRAYED);
\r
7171 if (!IsClipboardFormatAvailable(CF_TEXT)) {
\r
7172 EnableMenuItem(hmenu, IDM_Paste, MF_BYCOMMAND|MF_GRAYED);
\r
7174 pt.x = LOWORD(lParam);
\r
7175 pt.y = HIWORD(lParam);
\r
7176 MenuPopup(hwnd, pt, hmenu, -1);
\r
7180 switch (LOWORD(wParam)) {
\r
7182 SendMessage(hwnd, EM_UNDO, 0, 0);
\r
7184 case IDM_SelectAll:
\r
7186 sel.cpMax = -1; /*999999?*/
\r
7187 SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
7190 SendMessage(hwnd, WM_CUT, 0, 0);
\r
7193 SendMessage(hwnd, WM_PASTE, 0, 0);
\r
7196 SendMessage(hwnd, WM_COPY, 0, 0);
\r
7201 return (*consoleInputWindowProc)(hwnd, message, wParam, lParam);
\r
7204 #define CO_MAX 100000
\r
7205 #define CO_TRIM 1000
\r
7208 ConsoleWndProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
7210 static SnapData sd;
\r
7211 HWND hText, hInput;
\r
7213 static int sizeX, sizeY;
\r
7214 int newSizeX, newSizeY;
\r
7218 hText = GetDlgItem(hDlg, OPT_ConsoleText);
\r
7219 hInput = GetDlgItem(hDlg, OPT_ConsoleInput);
\r
7221 switch (message) {
\r
7223 if (((NMHDR*)lParam)->code == EN_LINK)
\r
7225 ENLINK *pLink = (ENLINK*)lParam;
\r
7226 if (pLink->msg == WM_LBUTTONUP)
\r
7230 tr.chrg = pLink->chrg;
\r
7231 tr.lpstrText = malloc(1+tr.chrg.cpMax-tr.chrg.cpMin);
\r
7232 SendMessage(hText, EM_GETTEXTRANGE, 0, (LPARAM)&tr);
\r
7233 ShellExecute(NULL, "open", tr.lpstrText, NULL, NULL, SW_SHOW);
\r
7234 free(tr.lpstrText);
\r
7238 case WM_INITDIALOG: /* message: initialize dialog box */
\r
7239 hwndConsole = hDlg;
\r
7241 consoleTextWindowProc = (WNDPROC)
\r
7242 SetWindowLongPtr(hText, GWLP_WNDPROC, (LONG_PTR) ConsoleTextSubclass);
\r
7243 SendMessage(hText, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);
\r
7244 consoleInputWindowProc = (WNDPROC)
\r
7245 SetWindowLongPtr(hInput, GWLP_WNDPROC, (LONG_PTR) ConsoleInputSubclass);
\r
7246 SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);
\r
7247 Colorize(ColorNormal, TRUE);
\r
7248 SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &consoleCF);
\r
7249 ChangedConsoleFont();
\r
7250 GetClientRect(hDlg, &rect);
\r
7251 sizeX = rect.right;
\r
7252 sizeY = rect.bottom;
\r
7253 if (wpConsole.x != CW_USEDEFAULT && wpConsole.y != CW_USEDEFAULT &&
\r
7254 wpConsole.width != CW_USEDEFAULT && wpConsole.height != CW_USEDEFAULT) {
\r
7255 WINDOWPLACEMENT wp;
\r
7256 EnsureOnScreen(&wpConsole.x, &wpConsole.y, 0, 0);
\r
7257 wp.length = sizeof(WINDOWPLACEMENT);
\r
7259 wp.showCmd = SW_SHOW;
\r
7260 wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;
\r
7261 wp.rcNormalPosition.left = wpConsole.x;
\r
7262 wp.rcNormalPosition.right = wpConsole.x + wpConsole.width;
\r
7263 wp.rcNormalPosition.top = wpConsole.y;
\r
7264 wp.rcNormalPosition.bottom = wpConsole.y + wpConsole.height;
\r
7265 SetWindowPlacement(hDlg, &wp);
\r
7268 // [HGM] Chessknight's change 2004-07-13
\r
7269 else { /* Determine Defaults */
\r
7270 WINDOWPLACEMENT wp;
\r
7271 wpConsole.x = wpMain.width + 1;
\r
7272 wpConsole.y = wpMain.y;
\r
7273 wpConsole.width = screenWidth - wpMain.width;
\r
7274 wpConsole.height = wpMain.height;
\r
7275 EnsureOnScreen(&wpConsole.x, &wpConsole.y, 0, 0);
\r
7276 wp.length = sizeof(WINDOWPLACEMENT);
\r
7278 wp.showCmd = SW_SHOW;
\r
7279 wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;
\r
7280 wp.rcNormalPosition.left = wpConsole.x;
\r
7281 wp.rcNormalPosition.right = wpConsole.x + wpConsole.width;
\r
7282 wp.rcNormalPosition.top = wpConsole.y;
\r
7283 wp.rcNormalPosition.bottom = wpConsole.y + wpConsole.height;
\r
7284 SetWindowPlacement(hDlg, &wp);
\r
7287 // Allow hText to highlight URLs and send notifications on them
\r
7288 wMask = (WORD) SendMessage(hText, EM_GETEVENTMASK, 0, 0L);
\r
7289 SendMessage(hText, EM_SETEVENTMASK, 0, wMask | ENM_LINK);
\r
7290 SendMessage(hText, EM_AUTOURLDETECT, TRUE, 0L);
\r
7291 SetWindowLongPtr(hText, GWLP_USERDATA, 79); // initialize the text window's width
\r
7305 if (IsIconic(hDlg)) break;
\r
7306 newSizeX = LOWORD(lParam);
\r
7307 newSizeY = HIWORD(lParam);
\r
7308 if (sizeX != newSizeX || sizeY != newSizeY) {
\r
7309 RECT rectText, rectInput;
\r
7311 int newTextHeight, newTextWidth;
\r
7312 GetWindowRect(hText, &rectText);
\r
7313 newTextWidth = rectText.right - rectText.left + newSizeX - sizeX;
\r
7314 newTextHeight = rectText.bottom - rectText.top + newSizeY - sizeY;
\r
7315 if (newTextHeight < 0) {
\r
7316 newSizeY += -newTextHeight;
\r
7317 newTextHeight = 0;
\r
7319 SetWindowPos(hText, NULL, 0, 0,
\r
7320 newTextWidth, newTextHeight, SWP_NOZORDER|SWP_NOMOVE);
\r
7321 GetWindowRect(hInput, &rectInput); /* gives screen coords */
\r
7322 pt.x = rectInput.left;
\r
7323 pt.y = rectInput.top + newSizeY - sizeY;
\r
7324 ScreenToClient(hDlg, &pt);
\r
7325 SetWindowPos(hInput, NULL,
\r
7326 pt.x, pt.y, /* needs client coords */
\r
7327 rectInput.right - rectInput.left + newSizeX - sizeX,
\r
7328 rectInput.bottom - rectInput.top, SWP_NOZORDER);
\r
7334 case WM_GETMINMAXINFO:
\r
7335 /* Prevent resizing window too small */
\r
7336 mmi = (MINMAXINFO *) lParam;
\r
7337 mmi->ptMinTrackSize.x = 100;
\r
7338 mmi->ptMinTrackSize.y = 100;
\r
7341 /* [AS] Snapping */
\r
7342 case WM_ENTERSIZEMOVE:
\r
7343 return OnEnterSizeMove( &sd, hDlg, wParam, lParam );
\r
7346 return OnSizing( &sd, hDlg, wParam, lParam );
\r
7349 return OnMoving( &sd, hDlg, wParam, lParam );
\r
7351 case WM_EXITSIZEMOVE:
\r
7352 UpdateICSWidth(hText);
\r
7353 return OnExitSizeMove( &sd, hDlg, wParam, lParam );
\r
7356 return DefWindowProc(hDlg, message, wParam, lParam);
\r
7364 if (hwndConsole) return;
\r
7365 hCons = CreateDialog(hInst, szConsoleName, 0, NULL);
\r
7366 SendMessage(hCons, WM_INITDIALOG, 0, 0);
\r
7371 ConsoleOutput(char* data, int length, int forceVisible)
\r
7376 char buf[CO_MAX+1];
\r
7379 static int delayLF = 0;
\r
7380 CHARRANGE savesel, sel;
\r
7382 if (hwndConsole == NULL || length > CO_MAX-100 || length == 0) return;
\r
7390 while (length--) {
\r
7398 } else if (*p == '\007') {
\r
7399 MyPlaySound(&sounds[(int)SoundBell]);
\r
7406 hText = GetDlgItem(hwndConsole, OPT_ConsoleText);
\r
7407 SendMessage(hText, EM_HIDESELECTION, TRUE, FALSE);
\r
7408 /* Save current selection */
\r
7409 SendMessage(hText, EM_EXGETSEL, 0, (LPARAM)&savesel);
\r
7410 exlen = GetWindowTextLength(hText);
\r
7411 /* Find out whether current end of text is visible */
\r
7412 SendMessage(hText, EM_GETRECT, 0, (LPARAM) &rect);
\r
7413 SendMessage(hText, EM_POSFROMCHAR, (WPARAM) &pEnd, exlen);
\r
7414 /* Trim existing text if it's too long */
\r
7415 if (exlen + (q - buf) > CO_MAX) {
\r
7416 trim = (CO_TRIM > (q - buf)) ? CO_TRIM : (q - buf);
\r
7419 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
7420 SendMessage(hText, EM_REPLACESEL, 0, (LPARAM)"");
\r
7422 savesel.cpMin -= trim;
\r
7423 savesel.cpMax -= trim;
\r
7424 if (exlen < 0) exlen = 0;
\r
7425 if (savesel.cpMin < 0) savesel.cpMin = 0;
\r
7426 if (savesel.cpMax < savesel.cpMin) savesel.cpMax = savesel.cpMin;
\r
7428 /* Append the new text */
\r
7429 sel.cpMin = exlen;
\r
7430 sel.cpMax = exlen;
\r
7431 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
7432 SendMessage(hText, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&consoleCF);
\r
7433 SendMessage(hText, EM_REPLACESEL, 0, (LPARAM) buf);
\r
7434 if (forceVisible || exlen == 0 ||
\r
7435 (rect.left <= pEnd.x && pEnd.x < rect.right &&
\r
7436 rect.top <= pEnd.y && pEnd.y < rect.bottom)) {
\r
7437 /* Scroll to make new end of text visible if old end of text
\r
7438 was visible or new text is an echo of user typein */
\r
7439 sel.cpMin = 9999999;
\r
7440 sel.cpMax = 9999999;
\r
7441 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
7442 SendMessage(hText, EM_HIDESELECTION, FALSE, FALSE);
\r
7443 SendMessage(hText, EM_SCROLLCARET, 0, 0);
\r
7444 SendMessage(hText, EM_HIDESELECTION, TRUE, FALSE);
\r
7446 if (savesel.cpMax == exlen || forceVisible) {
\r
7447 /* Move insert point to new end of text if it was at the old
\r
7448 end of text or if the new text is an echo of user typein */
\r
7449 sel.cpMin = 9999999;
\r
7450 sel.cpMax = 9999999;
\r
7451 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
7453 /* Restore previous selection */
\r
7454 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&savesel);
\r
7456 SendMessage(hText, EM_HIDESELECTION, FALSE, FALSE);
\r
7463 DisplayHoldingsCount(HDC hdc, int x, int y, int rightAlign, int copyNumber)
\r
7467 COLORREF oldFg, oldBg;
\r
7471 if(copyNumber > 1)
\r
7472 snprintf(buf, sizeof(buf)/sizeof(buf[0]),"%d", copyNumber); else buf[0] = 0;
\r
7474 oldFg = SetTextColor(hdc, RGB(255, 255, 255)); /* white */
\r
7475 oldBg = SetBkColor(hdc, RGB(0, 0, 0)); /* black */
\r
7476 oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);
\r
7479 rect.right = x + squareSize;
\r
7481 rect.bottom = y + squareSize;
\r
7484 ExtTextOut(hdc, x + MESSAGE_LINE_LEFTMARGIN
\r
7485 + (rightAlign ? (squareSize*2)/3 : 0),
\r
7486 y, ETO_CLIPPED|ETO_OPAQUE,
\r
7487 &rect, str, strlen(str), NULL);
\r
7489 (void) SetTextColor(hdc, oldFg);
\r
7490 (void) SetBkColor(hdc, oldBg);
\r
7491 (void) SelectObject(hdc, oldFont);
\r
7495 DisplayAClock(HDC hdc, int timeRemaining, int highlight,
\r
7496 RECT *rect, char *color, char *flagFell)
\r
7500 COLORREF oldFg, oldBg;
\r
7503 if (twoBoards && partnerUp) return;
\r
7504 if (appData.clockMode) {
\r
7506 snprintf(buf, sizeof(buf)/sizeof(buf[0]), "%c %s %s", color[0], TimeString(timeRemaining), flagFell);
\r
7508 snprintf(buf, sizeof(buf)/sizeof(buf[0]), "%s:%c%s %s", color, (logoHeight>0 ? 0 : ' '), TimeString(timeRemaining), flagFell);
\r
7515 oldFg = SetTextColor(hdc, RGB(255, 255, 255)); /* white */
\r
7516 oldBg = SetBkColor(hdc, RGB(0, 0, 0)); /* black */
\r
7518 oldFg = SetTextColor(hdc, RGB(0, 0, 0)); /* black */
\r
7519 oldBg = SetBkColor(hdc, RGB(255, 255, 255)); /* white */
\r
7521 oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);
\r
7525 ExtTextOut(hdc, rect->left + MESSAGE_LINE_LEFTMARGIN,
\r
7526 rect->top, ETO_CLIPPED|ETO_OPAQUE,
\r
7527 rect, str, strlen(str), NULL);
\r
7528 if(logoHeight > 0 && appData.clockMode) {
\r
7530 str += strlen(color)+2;
\r
7531 r.top = rect->top + logoHeight/2;
\r
7532 r.left = rect->left;
\r
7533 r.right = rect->right;
\r
7534 r.bottom = rect->bottom;
\r
7535 ExtTextOut(hdc, rect->left + MESSAGE_LINE_LEFTMARGIN,
\r
7536 r.top, ETO_CLIPPED|ETO_OPAQUE,
\r
7537 &r, str, strlen(str), NULL);
\r
7539 (void) SetTextColor(hdc, oldFg);
\r
7540 (void) SetBkColor(hdc, oldBg);
\r
7541 (void) SelectObject(hdc, oldFont);
\r
7546 DoReadFile(HANDLE hFile, char *buf, int count, DWORD *outCount,
\r
7552 if( count <= 0 ) {
\r
7553 if (appData.debugMode) {
\r
7554 fprintf( debugFP, "DoReadFile: trying to read past end of buffer, overflow = %d\n", count );
\r
7557 return ERROR_INVALID_USER_BUFFER;
\r
7560 ResetEvent(ovl->hEvent);
\r
7561 ovl->Offset = ovl->OffsetHigh = 0;
\r
7562 ok = ReadFile(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
7579 DoWriteFile(HANDLE hFile, char *buf, int count, DWORD *outCount,
\r
7584 ResetEvent(ovl->hEvent);
\r
7585 ovl->Offset = ovl->OffsetHigh = 0;
\r
7586 ok = WriteFile(hFile, buf, count, outCount, ovl);
\r
7590 err = GetLastError();
\r
7591 if (err == ERROR_IO_PENDING) {
\r
7592 ok = GetOverlappedResult(hFile, ovl, outCount, TRUE);
\r
7596 err = GetLastError();
\r
7602 /* [AS] If input is line by line and a line exceed the buffer size, force an error */
\r
7603 void CheckForInputBufferFull( InputSource * is )
\r
7605 if( is->lineByLine && (is->next - is->buf) >= INPUT_SOURCE_BUF_SIZE ) {
\r
7606 /* Look for end of line */
\r
7607 char * p = is->buf;
\r
7609 while( p < is->next && *p != '\n' ) {
\r
7613 if( p >= is->next ) {
\r
7614 if (appData.debugMode) {
\r
7615 fprintf( debugFP, "Input line exceeded buffer size (source id=%lu)\n", is->id );
\r
7618 is->error = ERROR_BROKEN_PIPE; /* [AS] Just any non-successful code! */
\r
7619 is->count = (DWORD) -1;
\r
7620 is->next = is->buf;
\r
7626 InputThread(LPVOID arg)
\r
7631 is = (InputSource *) arg;
\r
7632 ovl.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
\r
7633 ovl.Internal = ovl.InternalHigh = ovl.Offset = ovl.OffsetHigh = 0;
\r
7634 while (is->hThread != NULL) {
\r
7635 is->error = DoReadFile(is->hFile, is->next,
\r
7636 INPUT_SOURCE_BUF_SIZE - (is->next - is->buf),
\r
7637 &is->count, &ovl);
\r
7638 if (is->error == NO_ERROR) {
\r
7639 is->next += is->count;
\r
7641 if (is->error == ERROR_BROKEN_PIPE) {
\r
7642 /* Correct for MS brain damage. EOF reading a pipe is not an error. */
\r
7645 is->count = (DWORD) -1;
\r
7646 /* [AS] The (is->count <= 0) check below is not useful for unsigned values! */
\r
7651 CheckForInputBufferFull( is );
\r
7653 SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);
\r
7655 if( is->count == ((DWORD) -1) ) break; /* [AS] */
\r
7657 if (is->count <= 0) break; /* Quit on EOF or error */
\r
7660 CloseHandle(ovl.hEvent);
\r
7661 CloseHandle(is->hFile);
\r
7663 if (appData.debugMode) {
\r
7664 fprintf( debugFP, "Input thread terminated (id=%lu, error=%d, count=%ld)\n", is->id, is->error, is->count );
\r
7671 /* Windows 95 beta 2 won't let you do overlapped i/o on a console or pipe */
\r
7673 NonOvlInputThread(LPVOID arg)
\r
7680 is = (InputSource *) arg;
\r
7681 while (is->hThread != NULL) {
\r
7682 is->error = ReadFile(is->hFile, is->next,
\r
7683 INPUT_SOURCE_BUF_SIZE - (is->next - is->buf),
\r
7684 &is->count, NULL) ? NO_ERROR : GetLastError();
\r
7685 if (is->error == NO_ERROR) {
\r
7686 /* Change CRLF to LF */
\r
7687 if (is->next > is->buf) {
\r
7689 i = is->count + 1;
\r
7697 if (prev == '\r' && *p == '\n') {
\r
7709 if (is->error == ERROR_BROKEN_PIPE) {
\r
7710 /* Correct for MS brain damage. EOF reading a pipe is not an error. */
\r
7713 is->count = (DWORD) -1;
\r
7717 CheckForInputBufferFull( is );
\r
7719 SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);
\r
7721 if( is->count == ((DWORD) -1) ) break; /* [AS] */
\r
7723 if (is->count < 0) break; /* Quit on error */
\r
7725 CloseHandle(is->hFile);
\r
7730 SocketInputThread(LPVOID arg)
\r
7734 is = (InputSource *) arg;
\r
7735 while (is->hThread != NULL) {
\r
7736 is->count = recv(is->sock, is->buf, INPUT_SOURCE_BUF_SIZE, 0);
\r
7737 if ((int)is->count == SOCKET_ERROR) {
\r
7738 is->count = (DWORD) -1;
\r
7739 is->error = WSAGetLastError();
\r
7741 is->error = NO_ERROR;
\r
7742 is->next += is->count;
\r
7743 if (is->count == 0 && is->second == is) {
\r
7744 /* End of file on stderr; quit with no message */
\r
7748 SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);
\r
7750 if( is->count == ((DWORD) -1) ) break; /* [AS] */
\r
7752 if (is->count <= 0) break; /* Quit on EOF or error */
\r
7758 InputEvent(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
\r
7762 is = (InputSource *) lParam;
\r
7763 if (is->lineByLine) {
\r
7764 /* Feed in lines one by one */
\r
7765 char *p = is->buf;
\r
7767 while (q < is->next) {
\r
7768 if (*q++ == '\n') {
\r
7769 (is->func)(is, is->closure, p, q - p, NO_ERROR);
\r
7774 /* Move any partial line to the start of the buffer */
\r
7776 while (p < is->next) {
\r
7781 if (is->error != NO_ERROR || is->count == 0) {
\r
7782 /* Notify backend of the error. Note: If there was a partial
\r
7783 line at the end, it is not flushed through. */
\r
7784 (is->func)(is, is->closure, is->buf, is->count, is->error);
\r
7787 /* Feed in the whole chunk of input at once */
\r
7788 (is->func)(is, is->closure, is->buf, is->count, is->error);
\r
7789 is->next = is->buf;
\r
7793 /*---------------------------------------------------------------------------*\
\r
7795 * Menu enables. Used when setting various modes.
\r
7797 \*---------------------------------------------------------------------------*/
\r
7805 GreyRevert(Boolean grey)
\r
7806 { // [HGM] vari: for retracting variations in local mode
\r
7807 HMENU hmenu = GetMenu(hwndMain);
\r
7808 EnableMenuItem(hmenu, IDM_Revert, MF_BYCOMMAND|(grey ? MF_GRAYED : MF_ENABLED));
\r
7809 EnableMenuItem(hmenu, IDM_Annotate, MF_BYCOMMAND|(grey ? MF_GRAYED : MF_ENABLED));
\r
7813 SetMenuEnables(HMENU hmenu, Enables *enab)
\r
7815 while (enab->item > 0) {
\r
7816 (void) EnableMenuItem(hmenu, enab->item, enab->flags);
\r
7821 Enables gnuEnables[] = {
\r
7822 { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },
\r
7823 { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },
\r
7824 { IDM_IcsClient, MF_BYCOMMAND|MF_GRAYED },
\r
7825 { IDM_Accept, MF_BYCOMMAND|MF_GRAYED },
\r
7826 { IDM_Decline, MF_BYCOMMAND|MF_GRAYED },
\r
7827 { IDM_Rematch, MF_BYCOMMAND|MF_GRAYED },
\r
7828 { IDM_Adjourn, MF_BYCOMMAND|MF_GRAYED },
\r
7829 { IDM_StopExamining, MF_BYCOMMAND|MF_GRAYED },
\r
7830 { IDM_StopObserving, MF_BYCOMMAND|MF_GRAYED },
\r
7831 { IDM_Upload, MF_BYCOMMAND|MF_GRAYED },
\r
7832 { IDM_Revert, MF_BYCOMMAND|MF_GRAYED },
\r
7833 { IDM_Annotate, MF_BYCOMMAND|MF_GRAYED },
\r
7834 { IDM_NewChat, MF_BYCOMMAND|MF_GRAYED },
\r
7836 // Needed to switch from ncp to GNU mode on Engine Load
\r
7837 { ACTION_POS, MF_BYPOSITION|MF_ENABLED },
\r
7838 { IDM_MachineWhite, MF_BYCOMMAND|MF_ENABLED },
\r
7839 { IDM_MachineBlack, MF_BYCOMMAND|MF_ENABLED },
\r
7840 { IDM_TwoMachines, MF_BYCOMMAND|MF_ENABLED },
\r
7841 { IDM_Match, MF_BYCOMMAND|MF_ENABLED },
\r
7842 { IDM_AnalysisMode, MF_BYCOMMAND|MF_ENABLED },
\r
7843 { IDM_AnalyzeFile, MF_BYCOMMAND|MF_ENABLED },
\r
7844 { IDM_Engine1Options, MF_BYCOMMAND|MF_ENABLED },
\r
7845 { IDM_Engine2Options, MF_BYCOMMAND|MF_ENABLED },
\r
7846 { IDM_TimeControl, MF_BYCOMMAND|MF_ENABLED },
\r
7847 { IDM_RetractMove, MF_BYCOMMAND|MF_ENABLED },
\r
7848 { IDM_MoveNow, MF_BYCOMMAND|MF_ENABLED },
\r
7849 { IDM_Hint, MF_BYCOMMAND|MF_ENABLED },
\r
7850 { IDM_Book, MF_BYCOMMAND|MF_ENABLED },
\r
7854 Enables icsEnables[] = {
\r
7855 { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },
\r
7856 { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },
\r
7857 { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },
\r
7858 { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },
\r
7859 { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },
\r
7860 { IDM_Match, MF_BYCOMMAND|MF_GRAYED },
\r
7861 { IDM_MachineBoth, MF_BYCOMMAND|MF_GRAYED },
\r
7862 { IDM_AnalysisMode, MF_BYCOMMAND|MF_ENABLED },
\r
7863 { IDM_AnalyzeFile, MF_BYCOMMAND|MF_GRAYED },
\r
7864 { IDM_TimeControl, MF_BYCOMMAND|MF_GRAYED },
\r
7865 { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },
\r
7866 { IDM_Hint, MF_BYCOMMAND|MF_GRAYED },
\r
7867 { IDM_Book, MF_BYCOMMAND|MF_GRAYED },
\r
7868 { IDM_LoadProg1, MF_BYCOMMAND|MF_GRAYED },
\r
7869 { IDM_LoadProg2, MF_BYCOMMAND|MF_GRAYED },
\r
7870 { IDM_IcsOptions, MF_BYCOMMAND|MF_ENABLED },
\r
7871 { IDM_Engine1Options, MF_BYCOMMAND|MF_GRAYED },
\r
7872 { IDM_Engine2Options, MF_BYCOMMAND|MF_GRAYED },
\r
7873 { IDM_Annotate, MF_BYCOMMAND|MF_GRAYED },
\r
7874 { IDM_Tourney, MF_BYCOMMAND|MF_GRAYED },
\r
7879 Enables zippyEnables[] = {
\r
7880 { IDM_MoveNow, MF_BYCOMMAND|MF_ENABLED },
\r
7881 { IDM_Hint, MF_BYCOMMAND|MF_ENABLED },
\r
7882 { IDM_Book, MF_BYCOMMAND|MF_ENABLED },
\r
7883 { IDM_Engine1Options, MF_BYCOMMAND|MF_ENABLED },
\r
7888 Enables ncpEnables[] = {
\r
7889 { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },
\r
7890 { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },
\r
7891 { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },
\r
7892 { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },
\r
7893 { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },
\r
7894 { IDM_Match, MF_BYCOMMAND|MF_GRAYED },
\r
7895 { IDM_AnalysisMode, MF_BYCOMMAND|MF_GRAYED },
\r
7896 { IDM_AnalyzeFile, MF_BYCOMMAND|MF_GRAYED },
\r
7897 { IDM_IcsClient, MF_BYCOMMAND|MF_GRAYED },
\r
7898 { ACTION_POS, MF_BYPOSITION|MF_GRAYED },
\r
7899 { IDM_Revert, MF_BYCOMMAND|MF_GRAYED },
\r
7900 { IDM_Annotate, MF_BYCOMMAND|MF_GRAYED },
\r
7901 { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },
\r
7902 { IDM_RetractMove, MF_BYCOMMAND|MF_GRAYED },
\r
7903 { IDM_TimeControl, MF_BYCOMMAND|MF_GRAYED },
\r
7904 { IDM_Hint, MF_BYCOMMAND|MF_GRAYED },
\r
7905 { IDM_Book, MF_BYCOMMAND|MF_GRAYED },
\r
7906 { IDM_MachineBoth, MF_BYCOMMAND|MF_GRAYED },
\r
7907 { IDM_NewChat, MF_BYCOMMAND|MF_GRAYED },
\r
7908 { IDM_Engine1Options, MF_BYCOMMAND|MF_GRAYED },
\r
7909 { IDM_Engine2Options, MF_BYCOMMAND|MF_GRAYED },
\r
7910 { IDM_Sounds, MF_BYCOMMAND|MF_GRAYED },
\r
7914 Enables trainingOnEnables[] = {
\r
7915 { IDM_EditComment, MF_BYCOMMAND|MF_GRAYED },
\r
7916 { IDM_Comment, MF_BYCOMMAND|MF_GRAYED },
\r
7917 { IDM_Pause, MF_BYCOMMAND|MF_GRAYED },
\r
7918 { IDM_Forward, MF_BYCOMMAND|MF_GRAYED },
\r
7919 { IDM_Backward, MF_BYCOMMAND|MF_GRAYED },
\r
7920 { IDM_ToEnd, MF_BYCOMMAND|MF_GRAYED },
\r
7921 { IDM_ToStart, MF_BYCOMMAND|MF_GRAYED },
\r
7922 { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },
\r
7923 { IDM_TruncateGame, MF_BYCOMMAND|MF_GRAYED },
\r
7927 Enables trainingOffEnables[] = {
\r
7928 { IDM_EditComment, MF_BYCOMMAND|MF_ENABLED },
\r
7929 { IDM_Comment, MF_BYCOMMAND|MF_ENABLED },
\r
7930 { IDM_Pause, MF_BYCOMMAND|MF_ENABLED },
\r
7931 { IDM_Forward, MF_BYCOMMAND|MF_ENABLED },
\r
7932 { IDM_Backward, MF_BYCOMMAND|MF_ENABLED },
\r
7933 { IDM_ToEnd, MF_BYCOMMAND|MF_ENABLED },
\r
7934 { IDM_ToStart, MF_BYCOMMAND|MF_ENABLED },
\r
7935 { IDM_MoveNow, MF_BYCOMMAND|MF_ENABLED },
\r
7936 { IDM_TruncateGame, MF_BYCOMMAND|MF_ENABLED },
\r
7940 /* These modify either ncpEnables or gnuEnables */
\r
7941 Enables cmailEnables[] = {
\r
7942 { IDM_MailMove, MF_BYCOMMAND|MF_ENABLED },
\r
7943 { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_ENABLED },
\r
7944 { ACTION_POS, MF_BYPOSITION|MF_ENABLED },
\r
7945 { IDM_CallFlag, MF_BYCOMMAND|MF_GRAYED },
\r
7946 { IDM_Draw, MF_BYCOMMAND|MF_ENABLED },
\r
7947 { IDM_Adjourn, MF_BYCOMMAND|MF_GRAYED },
\r
7948 { IDM_Abort, MF_BYCOMMAND|MF_GRAYED },
\r
7952 Enables machineThinkingEnables[] = {
\r
7953 { IDM_LoadGame, MF_BYCOMMAND|MF_GRAYED },
\r
7954 { IDM_LoadNextGame, MF_BYCOMMAND|MF_GRAYED },
\r
7955 { IDM_LoadPrevGame, MF_BYCOMMAND|MF_GRAYED },
\r
7956 { IDM_ReloadGame, MF_BYCOMMAND|MF_GRAYED },
\r
7957 { IDM_PasteGame, MF_BYCOMMAND|MF_GRAYED },
\r
7958 { IDM_LoadPosition, MF_BYCOMMAND|MF_GRAYED },
\r
7959 { IDM_LoadNextPosition, MF_BYCOMMAND|MF_GRAYED },
\r
7960 { IDM_LoadPrevPosition, MF_BYCOMMAND|MF_GRAYED },
\r
7961 { IDM_ReloadPosition, MF_BYCOMMAND|MF_GRAYED },
\r
7962 { IDM_PastePosition, MF_BYCOMMAND|MF_GRAYED },
\r
7963 { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },
\r
7964 { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },
\r
7965 { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },
\r
7966 // { IDM_Match, MF_BYCOMMAND|MF_GRAYED },
\r
7967 { IDM_TypeInMove, MF_BYCOMMAND|MF_GRAYED },
\r
7968 { IDM_RetractMove, MF_BYCOMMAND|MF_GRAYED },
\r
7972 Enables userThinkingEnables[] = {
\r
7973 { IDM_LoadGame, MF_BYCOMMAND|MF_ENABLED },
\r
7974 { IDM_LoadNextGame, MF_BYCOMMAND|MF_ENABLED },
\r
7975 { IDM_LoadPrevGame, MF_BYCOMMAND|MF_ENABLED },
\r
7976 { IDM_ReloadGame, MF_BYCOMMAND|MF_ENABLED },
\r
7977 { IDM_PasteGame, MF_BYCOMMAND|MF_ENABLED },
\r
7978 { IDM_LoadPosition, MF_BYCOMMAND|MF_ENABLED },
\r
7979 { IDM_LoadNextPosition, MF_BYCOMMAND|MF_ENABLED },
\r
7980 { IDM_LoadPrevPosition, MF_BYCOMMAND|MF_ENABLED },
\r
7981 { IDM_ReloadPosition, MF_BYCOMMAND|MF_ENABLED },
\r
7982 { IDM_PastePosition, MF_BYCOMMAND|MF_ENABLED },
\r
7983 { IDM_MachineWhite, MF_BYCOMMAND|MF_ENABLED },
\r
7984 { IDM_MachineBlack, MF_BYCOMMAND|MF_ENABLED },
\r
7985 { IDM_TwoMachines, MF_BYCOMMAND|MF_ENABLED },
\r
7986 // { IDM_Match, MF_BYCOMMAND|MF_ENABLED },
\r
7987 { IDM_TypeInMove, MF_BYCOMMAND|MF_ENABLED },
\r
7988 { IDM_RetractMove, MF_BYCOMMAND|MF_ENABLED },
\r
7992 /*---------------------------------------------------------------------------*\
\r
7994 * Front-end interface functions exported by XBoard.
\r
7995 * Functions appear in same order as prototypes in frontend.h.
\r
7997 \*---------------------------------------------------------------------------*/
\r
7999 CheckMark(UINT item, int state)
\r
8001 if(item) CheckMenuItem(GetMenu(hwndMain), item, MF_BYCOMMAND|state);
\r
8007 static UINT prevChecked = 0;
\r
8008 static int prevPausing = 0;
\r
8011 if (pausing != prevPausing) {
\r
8012 prevPausing = pausing;
\r
8013 (void) CheckMenuItem(GetMenu(hwndMain), IDM_Pause,
\r
8014 MF_BYCOMMAND|(pausing ? MF_CHECKED : MF_UNCHECKED));
\r
8015 if (hwndPause) SetWindowText(hwndPause, pausing ? "C" : "P");
\r
8018 switch (gameMode) {
\r
8019 case BeginningOfGame:
\r
8020 if (appData.icsActive)
\r
8021 nowChecked = IDM_IcsClient;
\r
8022 else if (appData.noChessProgram)
\r
8023 nowChecked = IDM_EditGame;
\r
8025 nowChecked = IDM_MachineBlack;
\r
8027 case MachinePlaysBlack:
\r
8028 nowChecked = IDM_MachineBlack;
\r
8030 case MachinePlaysWhite:
\r
8031 nowChecked = IDM_MachineWhite;
\r
8033 case TwoMachinesPlay:
\r
8034 nowChecked = IDM_TwoMachines;
\r
8037 nowChecked = IDM_AnalysisMode;
\r
8040 nowChecked = IDM_AnalyzeFile;
\r
8043 nowChecked = IDM_EditGame;
\r
8045 case PlayFromGameFile:
\r
8046 nowChecked = IDM_LoadGame;
\r
8048 case EditPosition:
\r
8049 nowChecked = IDM_EditPosition;
\r
8052 nowChecked = IDM_Training;
\r
8054 case IcsPlayingWhite:
\r
8055 case IcsPlayingBlack:
\r
8056 case IcsObserving:
\r
8058 nowChecked = IDM_IcsClient;
\r
8065 CheckMark(prevChecked, MF_UNCHECKED);
\r
8066 CheckMark(nowChecked, MF_CHECKED);
\r
8067 CheckMark(IDM_Match, matchMode && matchGame < appData.matchGames ? MF_CHECKED : MF_UNCHECKED);
\r
8069 if (nowChecked == IDM_LoadGame || nowChecked == IDM_Training) {
\r
8070 (void) EnableMenuItem(GetMenu(hwndMain), IDM_Training,
\r
8071 MF_BYCOMMAND|MF_ENABLED);
\r
8073 (void) EnableMenuItem(GetMenu(hwndMain),
\r
8074 IDM_Training, MF_BYCOMMAND|MF_GRAYED);
\r
8077 prevChecked = nowChecked;
\r
8079 /* [DM] icsEngineAnalyze - Do a sceure check too */
\r
8080 if (appData.icsActive) {
\r
8081 if (appData.icsEngineAnalyze) {
\r
8082 CheckMark(IDM_AnalysisMode, MF_CHECKED);
\r
8084 CheckMark(IDM_AnalysisMode, MF_UNCHECKED);
\r
8087 DisplayLogos(); // [HGM] logos: mode change could have altered logos
\r
8093 HMENU hmenu = GetMenu(hwndMain);
\r
8094 SetMenuEnables(hmenu, icsEnables);
\r
8095 EnableMenuItem(GetSubMenu(hmenu, OPTIONS_POS), IDM_IcsOptions,
\r
8096 MF_BYCOMMAND|MF_ENABLED);
\r
8098 if (appData.zippyPlay) {
\r
8099 SetMenuEnables(hmenu, zippyEnables);
\r
8100 if (!appData.noChessProgram) /* [DM] icsEngineAnalyze */
\r
8101 (void) EnableMenuItem(GetMenu(hwndMain), IDM_AnalysisMode,
\r
8102 MF_BYCOMMAND|MF_ENABLED);
\r
8110 SetMenuEnables(GetMenu(hwndMain), gnuEnables);
\r
8116 HMENU hmenu = GetMenu(hwndMain);
\r
8117 SetMenuEnables(hmenu, ncpEnables);
\r
8118 DrawMenuBar(hwndMain);
\r
8124 SetMenuEnables(GetMenu(hwndMain), cmailEnables);
\r
8128 SetTrainingModeOn()
\r
8131 SetMenuEnables(GetMenu(hwndMain), trainingOnEnables);
\r
8132 for (i = 0; i < N_BUTTONS; i++) {
\r
8133 if (buttonDesc[i].hwnd != NULL)
\r
8134 EnableWindow(buttonDesc[i].hwnd, FALSE);
\r
8139 VOID SetTrainingModeOff()
\r
8142 SetMenuEnables(GetMenu(hwndMain), trainingOffEnables);
\r
8143 for (i = 0; i < N_BUTTONS; i++) {
\r
8144 if (buttonDesc[i].hwnd != NULL)
\r
8145 EnableWindow(buttonDesc[i].hwnd, TRUE);
\r
8151 SetUserThinkingEnables()
\r
8153 SetMenuEnables(GetMenu(hwndMain), userThinkingEnables);
\r
8157 SetMachineThinkingEnables()
\r
8159 HMENU hMenu = GetMenu(hwndMain);
\r
8160 int flags = MF_BYCOMMAND|MF_ENABLED;
\r
8162 SetMenuEnables(hMenu, machineThinkingEnables);
\r
8164 if (gameMode == MachinePlaysBlack) {
\r
8165 (void)EnableMenuItem(hMenu, IDM_MachineBlack, flags);
\r
8166 } else if (gameMode == MachinePlaysWhite) {
\r
8167 (void)EnableMenuItem(hMenu, IDM_MachineWhite, flags);
\r
8168 } else if (gameMode == TwoMachinesPlay) {
\r
8169 (void)EnableMenuItem(hMenu, matchMode ? IDM_Match : IDM_TwoMachines, flags); // [HGM] match
\r
8175 DisplayTitle(char *str)
\r
8177 char title[MSG_SIZ], *host;
\r
8178 if (str[0] != NULLCHAR) {
\r
8179 safeStrCpy(title, str, sizeof(title)/sizeof(title[0]) );
\r
8180 } else if (appData.icsActive) {
\r
8181 if (appData.icsCommPort[0] != NULLCHAR)
\r
8184 host = appData.icsHost;
\r
8185 snprintf(title, MSG_SIZ, "%s: %s", szTitle, host);
\r
8186 } else if (appData.noChessProgram) {
\r
8187 safeStrCpy(title, szTitle, sizeof(title)/sizeof(title[0]) );
\r
8189 safeStrCpy(title, szTitle, sizeof(title)/sizeof(title[0]) );
\r
8190 strcat(title, ": ");
\r
8191 strcat(title, first.tidy);
\r
8193 SetWindowText(hwndMain, title);
\r
8198 DisplayMessage(char *str1, char *str2)
\r
8202 int remain = MESSAGE_TEXT_MAX - 1;
\r
8205 moveErrorMessageUp = FALSE; /* turned on later by caller if needed */
\r
8206 messageText[0] = NULLCHAR;
\r
8208 len = strlen(str1);
\r
8209 if (len > remain) len = remain;
\r
8210 strncpy(messageText, str1, len);
\r
8211 messageText[len] = NULLCHAR;
\r
8214 if (*str2 && remain >= 2) {
\r
8216 strcat(messageText, " ");
\r
8219 len = strlen(str2);
\r
8220 if (len > remain) len = remain;
\r
8221 strncat(messageText, str2, len);
\r
8223 messageText[MESSAGE_TEXT_MAX - 1] = NULLCHAR;
\r
8224 safeStrCpy(lastMsg, messageText, MSG_SIZ);
\r
8226 if (hwndMain == NULL || IsIconic(hwndMain)) return;
\r
8230 hdc = GetDC(hwndMain);
\r
8231 oldFont = SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);
\r
8232 ExtTextOut(hdc, messageRect.left, messageRect.top, ETO_CLIPPED|ETO_OPAQUE,
\r
8233 &messageRect, messageText, strlen(messageText), NULL);
\r
8234 (void) SelectObject(hdc, oldFont);
\r
8235 (void) ReleaseDC(hwndMain, hdc);
\r
8239 DisplayError(char *str, int error)
\r
8241 char buf[MSG_SIZ*2], buf2[MSG_SIZ];
\r
8245 safeStrCpy(buf, str, sizeof(buf)/sizeof(buf[0]) );
\r
8247 len = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,
\r
8248 NULL, error, LANG_NEUTRAL,
\r
8249 (LPSTR) buf2, MSG_SIZ, NULL);
\r
8251 snprintf(buf, 2*MSG_SIZ, "%s:\n%s", str, buf2);
\r
8253 ErrorMap *em = errmap;
\r
8254 while (em->err != 0 && em->err != error) em++;
\r
8255 if (em->err != 0) {
\r
8256 snprintf(buf, 2*MSG_SIZ, "%s:\n%s", str, em->msg);
\r
8258 snprintf(buf, 2*MSG_SIZ, "%s:\nError code %d", str, error);
\r
8263 ErrorPopUp(_("Error"), buf);
\r
8268 DisplayMoveError(char *str)
\r
8270 fromX = fromY = -1;
\r
8271 ClearHighlights();
\r
8272 DrawPosition(FALSE, NULL);
\r
8273 if (appData.popupMoveErrors) {
\r
8274 ErrorPopUp(_("Error"), str);
\r
8276 DisplayMessage(str, "");
\r
8277 moveErrorMessageUp = TRUE;
\r
8282 DisplayFatalError(char *str, int error, int exitStatus)
\r
8284 char buf[2*MSG_SIZ], buf2[MSG_SIZ];
\r
8286 char *label = exitStatus ? _("Fatal Error") : _("Exiting");
\r
8289 len = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,
\r
8290 NULL, error, LANG_NEUTRAL,
\r
8291 (LPSTR) buf2, MSG_SIZ, NULL);
\r
8293 snprintf(buf, 2*MSG_SIZ, "%s:\n%s", str, buf2);
\r
8295 ErrorMap *em = errmap;
\r
8296 while (em->err != 0 && em->err != error) em++;
\r
8297 if (em->err != 0) {
\r
8298 snprintf(buf, 2*MSG_SIZ, "%s:\n%s", str, em->msg);
\r
8300 snprintf(buf, 2*MSG_SIZ, "%s:\nError code %d", str, error);
\r
8305 if (appData.debugMode) {
\r
8306 fprintf(debugFP, "%s: %s\n", label, str);
\r
8308 if (appData.popupExitMessage) {
\r
8309 (void) MessageBox(hwndMain, str, label, MB_OK|
\r
8310 (exitStatus ? MB_ICONSTOP : MB_ICONINFORMATION));
\r
8312 ExitEvent(exitStatus);
\r
8317 DisplayInformation(char *str)
\r
8319 (void) MessageBox(hwndMain, str, _("Information"), MB_OK|MB_ICONINFORMATION);
\r
8324 DisplayNote(char *str)
\r
8326 ErrorPopUp(_("Note"), str);
\r
8331 char *title, *question, *replyPrefix;
\r
8336 QuestionDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
8338 static QuestionParams *qp;
\r
8339 char reply[MSG_SIZ];
\r
8342 switch (message) {
\r
8343 case WM_INITDIALOG:
\r
8344 qp = (QuestionParams *) lParam;
\r
8345 CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));
\r
8346 Translate(hDlg, DLG_Question);
\r
8347 SetWindowText(hDlg, qp->title);
\r
8348 SetDlgItemText(hDlg, OPT_QuestionText, qp->question);
\r
8349 SetFocus(GetDlgItem(hDlg, OPT_QuestionInput));
\r
8353 switch (LOWORD(wParam)) {
\r
8355 safeStrCpy(reply, qp->replyPrefix, sizeof(reply)/sizeof(reply[0]) );
\r
8356 if (*reply) strcat(reply, " ");
\r
8357 len = strlen(reply);
\r
8358 GetDlgItemText(hDlg, OPT_QuestionInput, reply + len, sizeof(reply) - len);
\r
8359 strcat(reply, "\n");
\r
8360 OutputToProcess(qp->pr, reply, strlen(reply), &err);
\r
8361 EndDialog(hDlg, TRUE);
\r
8362 if (err) DisplayFatalError(_("Error writing to chess program"), err, 1);
\r
8365 EndDialog(hDlg, FALSE);
\r
8376 AskQuestion(char* title, char *question, char *replyPrefix, ProcRef pr)
\r
8378 QuestionParams qp;
\r
8382 qp.question = question;
\r
8383 qp.replyPrefix = replyPrefix;
\r
8385 lpProc = MakeProcInstance((FARPROC)QuestionDialog, hInst);
\r
8386 DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_Question),
\r
8387 hwndMain, (DLGPROC)lpProc, (LPARAM)&qp);
\r
8388 FreeProcInstance(lpProc);
\r
8391 /* [AS] Pick FRC position */
\r
8392 LRESULT CALLBACK NewGameFRC_Proc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
8394 static int * lpIndexFRC;
\r
8400 case WM_INITDIALOG:
\r
8401 lpIndexFRC = (int *) lParam;
\r
8403 CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));
\r
8404 Translate(hDlg, DLG_NewGameFRC);
\r
8406 SendDlgItemMessage( hDlg, IDC_NFG_Edit, EM_SETLIMITTEXT, sizeof(buf)-1, 0 );
\r
8407 SetDlgItemInt( hDlg, IDC_NFG_Edit, *lpIndexFRC, TRUE );
\r
8408 SendDlgItemMessage( hDlg, IDC_NFG_Edit, EM_SETSEL, 0, -1 );
\r
8409 SetFocus(GetDlgItem(hDlg, IDC_NFG_Edit));
\r
8414 switch( LOWORD(wParam) ) {
\r
8416 *lpIndexFRC = GetDlgItemInt(hDlg, IDC_NFG_Edit, &index_is_ok, TRUE );
\r
8417 EndDialog( hDlg, 0 );
\r
8418 shuffleOpenings = TRUE; /* [HGM] shuffle: switch shuffling on for as long as we stay in current variant */
\r
8421 EndDialog( hDlg, 1 );
\r
8423 case IDC_NFG_Edit:
\r
8424 if( HIWORD(wParam) == EN_CHANGE ) {
\r
8425 GetDlgItemInt(hDlg, IDC_NFG_Edit, &index_is_ok, TRUE );
\r
8427 EnableWindow( GetDlgItem(hDlg, IDOK), index_is_ok );
\r
8430 case IDC_NFG_Random:
\r
8431 snprintf( buf, sizeof(buf)/sizeof(buf[0]), "%d", myrandom() ); /* [HGM] shuffle: no longer limit to 960 */
\r
8432 SetDlgItemText(hDlg, IDC_NFG_Edit, buf );
\r
8445 int index = appData.defaultFrcPosition;
\r
8446 FARPROC lpProc = MakeProcInstance( (FARPROC) NewGameFRC_Proc, hInst );
\r
8448 result = DialogBoxParam( hInst, MAKEINTRESOURCE(DLG_NewGameFRC), hwndMain, (DLGPROC)lpProc, (LPARAM)&index );
\r
8450 if( result == 0 ) {
\r
8451 appData.defaultFrcPosition = index;
\r
8457 /* [AS] Game list options. Refactored by HGM */
\r
8459 HWND gameListOptionsDialog;
\r
8461 // low-level front-end: clear text edit / list widget
\r
8465 SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_RESETCONTENT, 0, 0 );
\r
8468 // low-level front-end: clear text edit / list widget
\r
8470 GLT_DeSelectList()
\r
8472 SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_SETCURSEL, 0, 0 );
\r
8475 // low-level front-end: append line to text edit / list widget
\r
8477 GLT_AddToList( char *name )
\r
8480 SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_ADDSTRING, 0, (LPARAM) name );
\r
8484 // low-level front-end: get line from text edit / list widget
\r
8486 GLT_GetFromList( int index, char *name )
\r
8489 if( SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_GETTEXT, index, (LPARAM) name ) != LB_ERR )
\r
8495 void GLT_MoveSelection( HWND hDlg, int delta )
\r
8497 int idx1 = (int) SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETCURSEL, 0, 0 );
\r
8498 int idx2 = idx1 + delta;
\r
8499 int count = (int) SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETCOUNT, 0, 0 );
\r
8501 if( idx1 >=0 && idx1 < count && idx2 >= 0 && idx2 < count ) {
\r
8504 SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETTEXT, idx1, (LPARAM) buf );
\r
8505 SendDlgItemMessage( hDlg, IDC_GameListTags, LB_DELETESTRING, idx1, 0 );
\r
8506 SendDlgItemMessage( hDlg, IDC_GameListTags, LB_INSERTSTRING, idx2, (LPARAM) buf );
\r
8507 SendDlgItemMessage( hDlg, IDC_GameListTags, LB_SETCURSEL, idx2, 0 );
\r
8511 LRESULT CALLBACK GameListOptions_Proc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
8515 case WM_INITDIALOG:
\r
8516 gameListOptionsDialog = hDlg; // [HGM] pass through global to keep out off back-end
\r
8518 CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));
\r
8519 Translate(hDlg, DLG_GameListOptions);
\r
8521 /* Initialize list */
\r
8522 GLT_TagsToList( lpUserGLT );
\r
8524 SetFocus( GetDlgItem(hDlg, IDC_GameListTags) );
\r
8529 switch( LOWORD(wParam) ) {
\r
8532 EndDialog( hDlg, 0 );
\r
8535 EndDialog( hDlg, 1 );
\r
8538 case IDC_GLT_Default:
\r
8539 GLT_TagsToList( GLT_DEFAULT_TAGS );
\r
8542 case IDC_GLT_Restore:
\r
8543 GLT_TagsToList( appData.gameListTags );
\r
8547 GLT_MoveSelection( hDlg, -1 );
\r
8550 case IDC_GLT_Down:
\r
8551 GLT_MoveSelection( hDlg, +1 );
\r
8561 int GameListOptions()
\r
8564 FARPROC lpProc = MakeProcInstance( (FARPROC) GameListOptions_Proc, hInst );
\r
8566 safeStrCpy( lpUserGLT, appData.gameListTags ,LPUSERGLT_SIZE );
\r
8568 result = DialogBoxParam( hInst, MAKEINTRESOURCE(DLG_GameListOptions), hwndMain, (DLGPROC)lpProc, (LPARAM)lpUserGLT );
\r
8570 if( result == 0 ) {
\r
8571 /* [AS] Memory leak here! */
\r
8572 appData.gameListTags = strdup( lpUserGLT );
\r
8579 DisplayIcsInteractionTitle(char *str)
\r
8581 char consoleTitle[MSG_SIZ];
\r
8583 snprintf(consoleTitle, MSG_SIZ, "%s: %s", szConsoleTitle, str);
\r
8584 SetWindowText(hwndConsole, consoleTitle);
\r
8586 if(appData.chatBoxes) { // [HGM] chat: open chat boxes
\r
8587 char buf[MSG_SIZ], *p = buf, *q;
\r
8588 safeStrCpy(buf, appData.chatBoxes, sizeof(buf)/sizeof(buf[0]) );
\r
8590 q = strchr(p, ';');
\r
8592 if(*p) ChatPopUp(p);
\r
8596 SetActiveWindow(hwndMain);
\r
8600 DrawPosition(int fullRedraw, Board board)
\r
8602 HDCDrawPosition(NULL, (BOOLEAN) fullRedraw, board);
\r
8605 void NotifyFrontendLogin()
\r
8608 UpdateICSWidth(GetDlgItem(hwndConsole, OPT_ConsoleText));
\r
8614 fromX = fromY = -1;
\r
8615 if (dragInfo.pos.x != -1 || dragInfo.pos.y != -1) {
\r
8616 dragInfo.pos.x = dragInfo.pos.y = -1;
\r
8617 dragInfo.pos.x = dragInfo.pos.y = -1;
\r
8618 dragInfo.lastpos = dragInfo.pos;
\r
8619 dragInfo.start.x = dragInfo.start.y = -1;
\r
8620 dragInfo.from = dragInfo.start;
\r
8622 DrawPosition(TRUE, NULL);
\r
8629 CommentPopUp(char *title, char *str)
\r
8631 HWND hwnd = GetActiveWindow();
\r
8632 EitherCommentPopUp(currentMove, title, str, FALSE); // [HGM] vari: fake move index, rather than 0
\r
8634 SetActiveWindow(hwnd);
\r
8638 CommentPopDown(void)
\r
8640 CheckMenuItem(GetMenu(hwndMain), IDM_Comment, MF_UNCHECKED);
\r
8641 if (commentDialog) {
\r
8642 ShowWindow(commentDialog, SW_HIDE);
\r
8644 commentUp = FALSE;
\r
8648 EditCommentPopUp(int index, char *title, char *str)
\r
8650 EitherCommentPopUp(index, title, str, TRUE);
\r
8657 MyPlaySound(&sounds[(int)SoundMove]);
\r
8660 VOID PlayIcsWinSound()
\r
8662 MyPlaySound(&sounds[(int)SoundIcsWin]);
\r
8665 VOID PlayIcsLossSound()
\r
8667 MyPlaySound(&sounds[(int)SoundIcsLoss]);
\r
8670 VOID PlayIcsDrawSound()
\r
8672 MyPlaySound(&sounds[(int)SoundIcsDraw]);
\r
8675 VOID PlayIcsUnfinishedSound()
\r
8677 MyPlaySound(&sounds[(int)SoundIcsUnfinished]);
\r
8683 MyPlaySound(&sounds[(int)SoundAlarm]);
\r
8689 MyPlaySound(&textAttribs[ColorTell].sound);
\r
8697 consoleEcho = TRUE;
\r
8698 hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
8699 SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&consoleCF);
\r
8700 SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);
\r
8709 consoleEcho = FALSE;
\r
8710 hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
8711 /* This works OK: set text and background both to the same color */
\r
8713 cf.crTextColor = COLOR_ECHOOFF;
\r
8714 SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf);
\r
8715 SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, cf.crTextColor);
\r
8718 /* No Raw()...? */
\r
8720 void Colorize(ColorClass cc, int continuation)
\r
8722 currentColorClass = cc;
\r
8723 consoleCF.dwMask = CFM_COLOR|CFM_BOLD|CFM_ITALIC|CFM_UNDERLINE|CFM_STRIKEOUT;
\r
8724 consoleCF.crTextColor = textAttribs[cc].color;
\r
8725 consoleCF.dwEffects = textAttribs[cc].effects;
\r
8726 if (!continuation) MyPlaySound(&textAttribs[cc].sound);
\r
8732 static char buf[MSG_SIZ];
\r
8733 DWORD bufsiz = MSG_SIZ;
\r
8735 if(appData.userName != NULL && appData.userName[0] != 0) {
\r
8736 return appData.userName; /* [HGM] username: prefer name selected by user over his system login */
\r
8738 if (!GetUserName(buf, &bufsiz)) {
\r
8739 /*DisplayError("Error getting user name", GetLastError());*/
\r
8740 safeStrCpy(buf, _("User"), sizeof(buf)/sizeof(buf[0]) );
\r
8748 static char buf[MSG_SIZ];
\r
8749 DWORD bufsiz = MSG_SIZ;
\r
8751 if (!GetComputerName(buf, &bufsiz)) {
\r
8752 /*DisplayError("Error getting host name", GetLastError());*/
\r
8753 safeStrCpy(buf, _("Unknown"), sizeof(buf)/sizeof(buf[0]) );
\r
8760 ClockTimerRunning()
\r
8762 return clockTimerEvent != 0;
\r
8768 if (clockTimerEvent == 0) return FALSE;
\r
8769 KillTimer(hwndMain, clockTimerEvent);
\r
8770 clockTimerEvent = 0;
\r
8775 StartClockTimer(long millisec)
\r
8777 clockTimerEvent = SetTimer(hwndMain, (UINT) CLOCK_TIMER_ID,
\r
8778 (UINT) millisec, NULL);
\r
8782 DisplayWhiteClock(long timeRemaining, int highlight)
\r
8785 char *flag = whiteFlag && 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 ? &blackRect : &whiteRect, _("White"), flag);
\r
8793 if (highlight && iconCurrent == iconBlack) {
\r
8794 iconCurrent = iconWhite;
\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
8806 DisplayBlackClock(long timeRemaining, int highlight)
\r
8809 char *flag = blackFlag && gameMode == TwoMachinesPlay ? "(!)" : "";
\r
8811 if(appData.noGUI) return;
\r
8812 hdc = GetDC(hwndMain);
\r
8813 if (!IsIconic(hwndMain)) {
\r
8814 DisplayAClock(hdc, timeRemaining, highlight,
\r
8815 flipClock ? &whiteRect : &blackRect, _("Black"), flag);
\r
8817 if (highlight && iconCurrent == iconWhite) {
\r
8818 iconCurrent = iconBlack;
\r
8819 PostMessage(hwndMain, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);
\r
8820 if (IsIconic(hwndMain)) {
\r
8821 DrawIcon(hdc, 2, 2, iconCurrent);
\r
8824 (void) ReleaseDC(hwndMain, hdc);
\r
8826 PostMessage(hwndConsole, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);
\r
8831 LoadGameTimerRunning()
\r
8833 return loadGameTimerEvent != 0;
\r
8837 StopLoadGameTimer()
\r
8839 if (loadGameTimerEvent == 0) return FALSE;
\r
8840 KillTimer(hwndMain, loadGameTimerEvent);
\r
8841 loadGameTimerEvent = 0;
\r
8846 StartLoadGameTimer(long millisec)
\r
8848 loadGameTimerEvent = SetTimer(hwndMain, (UINT) LOAD_GAME_TIMER_ID,
\r
8849 (UINT) millisec, NULL);
\r
8857 char fileTitle[MSG_SIZ];
\r
8859 defName = DefaultFileName(appData.oldSaveStyle ? "gam" : "pgn");
\r
8860 f = OpenFileDialog(hwndMain, "a", defName,
\r
8861 appData.oldSaveStyle ? "gam" : "pgn",
\r
8863 _("Save Game to File"), NULL, fileTitle, NULL);
\r
8865 SaveGame(f, 0, "");
\r
8872 ScheduleDelayedEvent(DelayedEventCallback cb, long millisec)
\r
8874 if (delayedTimerEvent != 0) {
\r
8875 if (appData.debugMode && cb != delayedTimerCallback) { // [HGM] alive: not too much debug
\r
8876 fprintf(debugFP, "ScheduleDelayedEvent: event already scheduled\n");
\r
8878 KillTimer(hwndMain, delayedTimerEvent);
\r
8879 delayedTimerEvent = 0;
\r
8880 if(delayedTimerCallback != cb) // [HGM] alive: do not "flush" same event, just postpone it
\r
8881 delayedTimerCallback();
\r
8883 delayedTimerCallback = cb;
\r
8884 delayedTimerEvent = SetTimer(hwndMain, (UINT) DELAYED_TIMER_ID,
\r
8885 (UINT) millisec, NULL);
\r
8888 DelayedEventCallback
\r
8891 if (delayedTimerEvent) {
\r
8892 return delayedTimerCallback;
\r
8899 CancelDelayedEvent()
\r
8901 if (delayedTimerEvent) {
\r
8902 KillTimer(hwndMain, delayedTimerEvent);
\r
8903 delayedTimerEvent = 0;
\r
8907 DWORD GetWin32Priority(int nice)
\r
8908 { // [HGM] nice: translate Unix nice() value to indows priority class. (Code stolen from Polyglot 1.4w11)
\r
8910 REALTIME_PRIORITY_CLASS 0x00000100
\r
8911 HIGH_PRIORITY_CLASS 0x00000080
\r
8912 ABOVE_NORMAL_PRIORITY_CLASS 0x00008000
\r
8913 NORMAL_PRIORITY_CLASS 0x00000020
\r
8914 BELOW_NORMAL_PRIORITY_CLASS 0x00004000
\r
8915 IDLE_PRIORITY_CLASS 0x00000040
\r
8917 if (nice < -15) return 0x00000080;
\r
8918 if (nice < 0) return 0x00008000;
\r
8919 if (nice == 0) return 0x00000020;
\r
8920 if (nice < 15) return 0x00004000;
\r
8921 return 0x00000040;
\r
8924 void RunCommand(char *cmdLine)
\r
8926 /* Now create the child process. */
\r
8927 STARTUPINFO siStartInfo;
\r
8928 PROCESS_INFORMATION piProcInfo;
\r
8930 siStartInfo.cb = sizeof(STARTUPINFO);
\r
8931 siStartInfo.lpReserved = NULL;
\r
8932 siStartInfo.lpDesktop = NULL;
\r
8933 siStartInfo.lpTitle = NULL;
\r
8934 siStartInfo.dwFlags = STARTF_USESTDHANDLES;
\r
8935 siStartInfo.cbReserved2 = 0;
\r
8936 siStartInfo.lpReserved2 = NULL;
\r
8937 siStartInfo.hStdInput = NULL;
\r
8938 siStartInfo.hStdOutput = NULL;
\r
8939 siStartInfo.hStdError = NULL;
\r
8941 CreateProcess(NULL,
\r
8942 cmdLine, /* command line */
\r
8943 NULL, /* process security attributes */
\r
8944 NULL, /* primary thread security attrs */
\r
8945 TRUE, /* handles are inherited */
\r
8946 DETACHED_PROCESS|CREATE_NEW_PROCESS_GROUP,
\r
8947 NULL, /* use parent's environment */
\r
8949 &siStartInfo, /* STARTUPINFO pointer */
\r
8950 &piProcInfo); /* receives PROCESS_INFORMATION */
\r
8952 CloseHandle(piProcInfo.hThread);
\r
8955 /* Start a child process running the given program.
\r
8956 The process's standard output can be read from "from", and its
\r
8957 standard input can be written to "to".
\r
8958 Exit with fatal error if anything goes wrong.
\r
8959 Returns an opaque pointer that can be used to destroy the process
\r
8963 StartChildProcess(char *cmdLine, char *dir, ProcRef *pr)
\r
8965 #define BUFSIZE 4096
\r
8967 HANDLE hChildStdinRd, hChildStdinWr,
\r
8968 hChildStdoutRd, hChildStdoutWr;
\r
8969 HANDLE hChildStdinWrDup, hChildStdoutRdDup;
\r
8970 SECURITY_ATTRIBUTES saAttr;
\r
8972 PROCESS_INFORMATION piProcInfo;
\r
8973 STARTUPINFO siStartInfo;
\r
8975 char buf[MSG_SIZ];
\r
8978 if (appData.debugMode) {
\r
8979 fprintf(debugFP, "StartChildProcess (dir=\"%s\") %s\n", dir, cmdLine);
\r
8984 /* Set the bInheritHandle flag so pipe handles are inherited. */
\r
8985 saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);
\r
8986 saAttr.bInheritHandle = TRUE;
\r
8987 saAttr.lpSecurityDescriptor = NULL;
\r
8990 * The steps for redirecting child's STDOUT:
\r
8991 * 1. Create anonymous pipe to be STDOUT for child.
\r
8992 * 2. Create a noninheritable duplicate of read handle,
\r
8993 * and close the inheritable read handle.
\r
8996 /* Create a pipe for the child's STDOUT. */
\r
8997 if (! CreatePipe(&hChildStdoutRd, &hChildStdoutWr, &saAttr, 0)) {
\r
8998 return GetLastError();
\r
9001 /* Duplicate the read handle to the pipe, so it is not inherited. */
\r
9002 fSuccess = DuplicateHandle(GetCurrentProcess(), hChildStdoutRd,
\r
9003 GetCurrentProcess(), &hChildStdoutRdDup, 0,
\r
9004 FALSE, /* not inherited */
\r
9005 DUPLICATE_SAME_ACCESS);
\r
9007 return GetLastError();
\r
9009 CloseHandle(hChildStdoutRd);
\r
9012 * The steps for redirecting child's STDIN:
\r
9013 * 1. Create anonymous pipe to be STDIN for child.
\r
9014 * 2. Create a noninheritable duplicate of write handle,
\r
9015 * and close the inheritable write handle.
\r
9018 /* Create a pipe for the child's STDIN. */
\r
9019 if (! CreatePipe(&hChildStdinRd, &hChildStdinWr, &saAttr, 0)) {
\r
9020 return GetLastError();
\r
9023 /* Duplicate the write handle to the pipe, so it is not inherited. */
\r
9024 fSuccess = DuplicateHandle(GetCurrentProcess(), hChildStdinWr,
\r
9025 GetCurrentProcess(), &hChildStdinWrDup, 0,
\r
9026 FALSE, /* not inherited */
\r
9027 DUPLICATE_SAME_ACCESS);
\r
9029 return GetLastError();
\r
9031 CloseHandle(hChildStdinWr);
\r
9033 /* Arrange to (1) look in dir for the child .exe file, and
\r
9034 * (2) have dir be the child's working directory. Interpret
\r
9035 * dir relative to the directory WinBoard loaded from. */
\r
9036 GetCurrentDirectory(MSG_SIZ, buf);
\r
9037 SetCurrentDirectory(installDir);
\r
9038 SetCurrentDirectory(dir);
\r
9040 /* Now create the child process. */
\r
9042 siStartInfo.cb = sizeof(STARTUPINFO);
\r
9043 siStartInfo.lpReserved = NULL;
\r
9044 siStartInfo.lpDesktop = NULL;
\r
9045 siStartInfo.lpTitle = NULL;
\r
9046 siStartInfo.dwFlags = STARTF_USESTDHANDLES;
\r
9047 siStartInfo.cbReserved2 = 0;
\r
9048 siStartInfo.lpReserved2 = NULL;
\r
9049 siStartInfo.hStdInput = hChildStdinRd;
\r
9050 siStartInfo.hStdOutput = hChildStdoutWr;
\r
9051 siStartInfo.hStdError = hChildStdoutWr;
\r
9053 fSuccess = CreateProcess(NULL,
\r
9054 cmdLine, /* command line */
\r
9055 NULL, /* process security attributes */
\r
9056 NULL, /* primary thread security attrs */
\r
9057 TRUE, /* handles are inherited */
\r
9058 DETACHED_PROCESS|CREATE_NEW_PROCESS_GROUP,
\r
9059 NULL, /* use parent's environment */
\r
9061 &siStartInfo, /* STARTUPINFO pointer */
\r
9062 &piProcInfo); /* receives PROCESS_INFORMATION */
\r
9064 err = GetLastError();
\r
9065 SetCurrentDirectory(buf); /* return to prev directory */
\r
9070 if (appData.niceEngines){ // [HGM] nice: adjust engine proc priority
\r
9071 if(appData.debugMode) fprintf(debugFP, "nice engine proc to %d\n", appData.niceEngines);
\r
9072 SetPriorityClass(piProcInfo.hProcess, GetWin32Priority(appData.niceEngines));
\r
9075 /* Close the handles we don't need in the parent */
\r
9076 CloseHandle(piProcInfo.hThread);
\r
9077 CloseHandle(hChildStdinRd);
\r
9078 CloseHandle(hChildStdoutWr);
\r
9080 /* Prepare return value */
\r
9081 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
\r
9082 cp->kind = CPReal;
\r
9083 cp->hProcess = piProcInfo.hProcess;
\r
9084 cp->pid = piProcInfo.dwProcessId;
\r
9085 cp->hFrom = hChildStdoutRdDup;
\r
9086 cp->hTo = hChildStdinWrDup;
\r
9088 *pr = (void *) cp;
\r
9090 /* Klaus Friedel says that this Sleep solves a problem under Windows
\r
9091 2000 where engines sometimes don't see the initial command(s)
\r
9092 from WinBoard and hang. I don't understand how that can happen,
\r
9093 but the Sleep is harmless, so I've put it in. Others have also
\r
9094 reported what may be the same problem, so hopefully this will fix
\r
9095 it for them too. */
\r
9103 DestroyChildProcess(ProcRef pr, int/*boolean*/ signal)
\r
9105 ChildProc *cp; int result;
\r
9107 cp = (ChildProc *) pr;
\r
9108 if (cp == NULL) return;
\r
9110 switch (cp->kind) {
\r
9112 /* TerminateProcess is considered harmful, so... */
\r
9113 CloseHandle(cp->hTo); /* Closing this will give the child an EOF and hopefully kill it */
\r
9114 if (cp->hFrom) CloseHandle(cp->hFrom); /* if NULL, InputThread will close it */
\r
9115 /* The following doesn't work because the chess program
\r
9116 doesn't "have the same console" as WinBoard. Maybe
\r
9117 we could arrange for this even though neither WinBoard
\r
9118 nor the chess program uses a console for stdio? */
\r
9119 /*!!if (signal) GenerateConsoleCtrlEvent(CTRL_BREAK_EVENT, cp->pid);*/
\r
9121 /* [AS] Special termination modes for misbehaving programs... */
\r
9122 if( signal == 9 ) {
\r
9123 result = TerminateProcess( cp->hProcess, 0 );
\r
9125 if ( appData.debugMode) {
\r
9126 fprintf( debugFP, "Terminating process %lu, result=%d\n", cp->pid, result );
\r
9129 else if( signal == 10 ) {
\r
9130 DWORD dw = WaitForSingleObject( cp->hProcess, 3*1000 ); // Wait 3 seconds at most
\r
9132 if( dw != WAIT_OBJECT_0 ) {
\r
9133 result = TerminateProcess( cp->hProcess, 0 );
\r
9135 if ( appData.debugMode) {
\r
9136 fprintf( debugFP, "Process %lu still alive after timeout, killing... result=%d\n", cp->pid, result );
\r
9142 CloseHandle(cp->hProcess);
\r
9146 if (cp->hFrom) CloseHandle(cp->hFrom);
\r
9150 closesocket(cp->sock);
\r
9155 if (signal) send(cp->sock2, "\017", 1, 0); /* 017 = 15 = SIGTERM */
\r
9156 closesocket(cp->sock);
\r
9157 closesocket(cp->sock2);
\r
9165 InterruptChildProcess(ProcRef pr)
\r
9169 cp = (ChildProc *) pr;
\r
9170 if (cp == NULL) return;
\r
9171 switch (cp->kind) {
\r
9173 /* The following doesn't work because the chess program
\r
9174 doesn't "have the same console" as WinBoard. Maybe
\r
9175 we could arrange for this even though neither WinBoard
\r
9176 nor the chess program uses a console for stdio */
\r
9177 /*!!GenerateConsoleCtrlEvent(CTRL_C_EVENT, cp->pid);*/
\r
9182 /* Can't interrupt */
\r
9186 send(cp->sock2, "\002", 1, 0); /* 2 = SIGINT */
\r
9193 OpenTelnet(char *host, char *port, ProcRef *pr)
\r
9195 char cmdLine[MSG_SIZ];
\r
9197 if (port[0] == NULLCHAR) {
\r
9198 snprintf(cmdLine, MSG_SIZ, "%s %s", appData.telnetProgram, host);
\r
9200 snprintf(cmdLine, MSG_SIZ, "%s %s %s", appData.telnetProgram, host, port);
\r
9202 return StartChildProcess(cmdLine, "", pr);
\r
9206 /* Code to open TCP sockets */
\r
9209 OpenTCP(char *host, char *port, ProcRef *pr)
\r
9215 struct sockaddr_in sa, mysa;
\r
9216 struct hostent FAR *hp;
\r
9217 unsigned short uport;
\r
9218 WORD wVersionRequested;
\r
9221 /* Initialize socket DLL */
\r
9222 wVersionRequested = MAKEWORD(1, 1);
\r
9223 err = WSAStartup(wVersionRequested, &wsaData);
\r
9224 if (err != 0) return err;
\r
9227 if ((s = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {
\r
9228 err = WSAGetLastError();
\r
9233 /* Bind local address using (mostly) don't-care values.
\r
9235 memset((char *) &mysa, 0, sizeof(struct sockaddr_in));
\r
9236 mysa.sin_family = AF_INET;
\r
9237 mysa.sin_addr.s_addr = INADDR_ANY;
\r
9238 uport = (unsigned short) 0;
\r
9239 mysa.sin_port = htons(uport);
\r
9240 if (bind(s, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))
\r
9241 == SOCKET_ERROR) {
\r
9242 err = WSAGetLastError();
\r
9247 /* Resolve remote host name */
\r
9248 memset((char *) &sa, 0, sizeof(struct sockaddr_in));
\r
9249 if (!(hp = gethostbyname(host))) {
\r
9250 unsigned int b0, b1, b2, b3;
\r
9252 err = WSAGetLastError();
\r
9254 if (sscanf(host, "%u.%u.%u.%u", &b0, &b1, &b2, &b3) == 4) {
\r
9255 hp = (struct hostent *) calloc(1, sizeof(struct hostent));
\r
9256 hp->h_addrtype = AF_INET;
\r
9258 hp->h_addr_list = (char **) calloc(2, sizeof(char *));
\r
9259 hp->h_addr_list[0] = (char *) malloc(4);
\r
9260 hp->h_addr_list[0][0] = (char) b0;
\r
9261 hp->h_addr_list[0][1] = (char) b1;
\r
9262 hp->h_addr_list[0][2] = (char) b2;
\r
9263 hp->h_addr_list[0][3] = (char) b3;
\r
9269 sa.sin_family = hp->h_addrtype;
\r
9270 uport = (unsigned short) atoi(port);
\r
9271 sa.sin_port = htons(uport);
\r
9272 memcpy((char *) &sa.sin_addr, hp->h_addr, hp->h_length);
\r
9274 /* Make connection */
\r
9275 if (connect(s, (struct sockaddr *) &sa,
\r
9276 sizeof(struct sockaddr_in)) == SOCKET_ERROR) {
\r
9277 err = WSAGetLastError();
\r
9282 /* Prepare return value */
\r
9283 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
\r
9284 cp->kind = CPSock;
\r
9286 *pr = (ProcRef *) cp;
\r
9292 OpenCommPort(char *name, ProcRef *pr)
\r
9297 char fullname[MSG_SIZ];
\r
9299 if (*name != '\\')
\r
9300 snprintf(fullname, MSG_SIZ, "\\\\.\\%s", name);
\r
9302 safeStrCpy(fullname, name, sizeof(fullname)/sizeof(fullname[0]) );
\r
9304 h = CreateFile(name, GENERIC_READ | GENERIC_WRITE,
\r
9305 0, NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL);
\r
9306 if (h == (HANDLE) -1) {
\r
9307 return GetLastError();
\r
9311 if (!SetCommState(h, (LPDCB) &dcb)) return GetLastError();
\r
9313 /* Accumulate characters until a 100ms pause, then parse */
\r
9314 ct.ReadIntervalTimeout = 100;
\r
9315 ct.ReadTotalTimeoutMultiplier = 0;
\r
9316 ct.ReadTotalTimeoutConstant = 0;
\r
9317 ct.WriteTotalTimeoutMultiplier = 0;
\r
9318 ct.WriteTotalTimeoutConstant = 0;
\r
9319 if (!SetCommTimeouts(h, (LPCOMMTIMEOUTS) &ct)) return GetLastError();
\r
9321 /* Prepare return value */
\r
9322 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
\r
9323 cp->kind = CPComm;
\r
9326 *pr = (ProcRef *) cp;
\r
9332 OpenLoopback(ProcRef *pr)
\r
9334 DisplayFatalError(_("Not implemented"), 0, 1);
\r
9340 OpenRcmd(char* host, char* user, char* cmd, ProcRef* pr)
\r
9345 struct sockaddr_in sa, mysa;
\r
9346 struct hostent FAR *hp;
\r
9347 unsigned short uport;
\r
9348 WORD wVersionRequested;
\r
9351 char stderrPortStr[MSG_SIZ];
\r
9353 /* Initialize socket DLL */
\r
9354 wVersionRequested = MAKEWORD(1, 1);
\r
9355 err = WSAStartup(wVersionRequested, &wsaData);
\r
9356 if (err != 0) return err;
\r
9358 /* Resolve remote host name */
\r
9359 memset((char *) &sa, 0, sizeof(struct sockaddr_in));
\r
9360 if (!(hp = gethostbyname(host))) {
\r
9361 unsigned int b0, b1, b2, b3;
\r
9363 err = WSAGetLastError();
\r
9365 if (sscanf(host, "%u.%u.%u.%u", &b0, &b1, &b2, &b3) == 4) {
\r
9366 hp = (struct hostent *) calloc(1, sizeof(struct hostent));
\r
9367 hp->h_addrtype = AF_INET;
\r
9369 hp->h_addr_list = (char **) calloc(2, sizeof(char *));
\r
9370 hp->h_addr_list[0] = (char *) malloc(4);
\r
9371 hp->h_addr_list[0][0] = (char) b0;
\r
9372 hp->h_addr_list[0][1] = (char) b1;
\r
9373 hp->h_addr_list[0][2] = (char) b2;
\r
9374 hp->h_addr_list[0][3] = (char) b3;
\r
9380 sa.sin_family = hp->h_addrtype;
\r
9381 uport = (unsigned short) 514;
\r
9382 sa.sin_port = htons(uport);
\r
9383 memcpy((char *) &sa.sin_addr, hp->h_addr, hp->h_length);
\r
9385 /* Bind local socket to unused "privileged" port address
\r
9387 s = INVALID_SOCKET;
\r
9388 memset((char *) &mysa, 0, sizeof(struct sockaddr_in));
\r
9389 mysa.sin_family = AF_INET;
\r
9390 mysa.sin_addr.s_addr = INADDR_ANY;
\r
9391 for (fromPort = 1023;; fromPort--) {
\r
9392 if (fromPort < 0) {
\r
9394 return WSAEADDRINUSE;
\r
9396 if (s == INVALID_SOCKET) {
\r
9397 if ((s = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {
\r
9398 err = WSAGetLastError();
\r
9403 uport = (unsigned short) fromPort;
\r
9404 mysa.sin_port = htons(uport);
\r
9405 if (bind(s, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))
\r
9406 == SOCKET_ERROR) {
\r
9407 err = WSAGetLastError();
\r
9408 if (err == WSAEADDRINUSE) continue;
\r
9412 if (connect(s, (struct sockaddr *) &sa,
\r
9413 sizeof(struct sockaddr_in)) == SOCKET_ERROR) {
\r
9414 err = WSAGetLastError();
\r
9415 if (err == WSAEADDRINUSE) {
\r
9426 /* Bind stderr local socket to unused "privileged" port address
\r
9428 s2 = INVALID_SOCKET;
\r
9429 memset((char *) &mysa, 0, sizeof(struct sockaddr_in));
\r
9430 mysa.sin_family = AF_INET;
\r
9431 mysa.sin_addr.s_addr = INADDR_ANY;
\r
9432 for (fromPort = 1023;; fromPort--) {
\r
9433 if (fromPort == prevStderrPort) continue; // don't reuse port
\r
9434 if (fromPort < 0) {
\r
9435 (void) closesocket(s);
\r
9437 return WSAEADDRINUSE;
\r
9439 if (s2 == INVALID_SOCKET) {
\r
9440 if ((s2 = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {
\r
9441 err = WSAGetLastError();
\r
9447 uport = (unsigned short) fromPort;
\r
9448 mysa.sin_port = htons(uport);
\r
9449 if (bind(s2, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))
\r
9450 == SOCKET_ERROR) {
\r
9451 err = WSAGetLastError();
\r
9452 if (err == WSAEADDRINUSE) continue;
\r
9453 (void) closesocket(s);
\r
9457 if (listen(s2, 1) == SOCKET_ERROR) {
\r
9458 err = WSAGetLastError();
\r
9459 if (err == WSAEADDRINUSE) {
\r
9461 s2 = INVALID_SOCKET;
\r
9464 (void) closesocket(s);
\r
9465 (void) closesocket(s2);
\r
9471 prevStderrPort = fromPort; // remember port used
\r
9472 snprintf(stderrPortStr, MSG_SIZ, "%d", fromPort);
\r
9474 if (send(s, stderrPortStr, strlen(stderrPortStr) + 1, 0) == SOCKET_ERROR) {
\r
9475 err = WSAGetLastError();
\r
9476 (void) closesocket(s);
\r
9477 (void) closesocket(s2);
\r
9482 if (send(s, UserName(), strlen(UserName()) + 1, 0) == SOCKET_ERROR) {
\r
9483 err = WSAGetLastError();
\r
9484 (void) closesocket(s);
\r
9485 (void) closesocket(s2);
\r
9489 if (*user == NULLCHAR) user = UserName();
\r
9490 if (send(s, user, strlen(user) + 1, 0) == SOCKET_ERROR) {
\r
9491 err = WSAGetLastError();
\r
9492 (void) closesocket(s);
\r
9493 (void) closesocket(s2);
\r
9497 if (send(s, cmd, strlen(cmd) + 1, 0) == SOCKET_ERROR) {
\r
9498 err = WSAGetLastError();
\r
9499 (void) closesocket(s);
\r
9500 (void) closesocket(s2);
\r
9505 if ((s3 = accept(s2, NULL, NULL)) == INVALID_SOCKET) {
\r
9506 err = WSAGetLastError();
\r
9507 (void) closesocket(s);
\r
9508 (void) closesocket(s2);
\r
9512 (void) closesocket(s2); /* Stop listening */
\r
9514 /* Prepare return value */
\r
9515 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
\r
9516 cp->kind = CPRcmd;
\r
9519 *pr = (ProcRef *) cp;
\r
9526 AddInputSource(ProcRef pr, int lineByLine,
\r
9527 InputCallback func, VOIDSTAR closure)
\r
9529 InputSource *is, *is2 = NULL;
\r
9530 ChildProc *cp = (ChildProc *) pr;
\r
9532 is = (InputSource *) calloc(1, sizeof(InputSource));
\r
9533 is->lineByLine = lineByLine;
\r
9535 is->closure = closure;
\r
9536 is->second = NULL;
\r
9537 is->next = is->buf;
\r
9538 if (pr == NoProc) {
\r
9539 is->kind = CPReal;
\r
9540 consoleInputSource = is;
\r
9542 is->kind = cp->kind;
\r
9544 [AS] Try to avoid a race condition if the thread is given control too early:
\r
9545 we create all threads suspended so that the is->hThread variable can be
\r
9546 safely assigned, then let the threads start with ResumeThread.
\r
9548 switch (cp->kind) {
\r
9550 is->hFile = cp->hFrom;
\r
9551 cp->hFrom = NULL; /* now owned by InputThread */
\r
9553 CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) NonOvlInputThread,
\r
9554 (LPVOID) is, CREATE_SUSPENDED, &is->id);
\r
9558 is->hFile = cp->hFrom;
\r
9559 cp->hFrom = NULL; /* now owned by InputThread */
\r
9561 CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) InputThread,
\r
9562 (LPVOID) is, CREATE_SUSPENDED, &is->id);
\r
9566 is->sock = cp->sock;
\r
9568 CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,
\r
9569 (LPVOID) is, CREATE_SUSPENDED, &is->id);
\r
9573 is2 = (InputSource *) calloc(1, sizeof(InputSource));
\r
9575 is->sock = cp->sock;
\r
9577 is2->sock = cp->sock2;
\r
9578 is2->second = is2;
\r
9580 CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,
\r
9581 (LPVOID) is, CREATE_SUSPENDED, &is->id);
\r
9583 CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,
\r
9584 (LPVOID) is2, CREATE_SUSPENDED, &is2->id);
\r
9588 if( is->hThread != NULL ) {
\r
9589 ResumeThread( is->hThread );
\r
9592 if( is2 != NULL && is2->hThread != NULL ) {
\r
9593 ResumeThread( is2->hThread );
\r
9597 return (InputSourceRef) is;
\r
9601 RemoveInputSource(InputSourceRef isr)
\r
9605 is = (InputSource *) isr;
\r
9606 is->hThread = NULL; /* tell thread to stop */
\r
9607 CloseHandle(is->hThread);
\r
9608 if (is->second != NULL) {
\r
9609 is->second->hThread = NULL;
\r
9610 CloseHandle(is->second->hThread);
\r
9614 int no_wrap(char *message, int count)
\r
9616 ConsoleOutput(message, count, FALSE);
\r
9621 OutputToProcess(ProcRef pr, char *message, int count, int *outError)
\r
9624 int outCount = SOCKET_ERROR;
\r
9625 ChildProc *cp = (ChildProc *) pr;
\r
9626 static OVERLAPPED ovl;
\r
9627 static int line = 0;
\r
9631 if (appData.noJoin || !appData.useInternalWrap)
\r
9632 return no_wrap(message, count);
\r
9635 int width = get_term_width();
\r
9636 int len = wrap(NULL, message, count, width, &line);
\r
9637 char *msg = malloc(len);
\r
9641 return no_wrap(message, count);
\r
9644 dbgchk = wrap(msg, message, count, width, &line);
\r
9645 if (dbgchk != len && appData.debugMode)
\r
9646 fprintf(debugFP, "wrap(): dbgchk(%d) != len(%d)\n", dbgchk, len);
\r
9647 ConsoleOutput(msg, len, FALSE);
\r
9654 if (ovl.hEvent == NULL) {
\r
9655 ovl.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
\r
9657 ovl.Internal = ovl.InternalHigh = ovl.Offset = ovl.OffsetHigh = 0;
\r
9659 switch (cp->kind) {
\r
9662 outCount = send(cp->sock, message, count, 0);
\r
9663 if (outCount == SOCKET_ERROR) {
\r
9664 *outError = WSAGetLastError();
\r
9666 *outError = NO_ERROR;
\r
9671 if (WriteFile(((ChildProc *)pr)->hTo, message, count,
\r
9672 &dOutCount, NULL)) {
\r
9673 *outError = NO_ERROR;
\r
9674 outCount = (int) dOutCount;
\r
9676 *outError = GetLastError();
\r
9681 *outError = DoWriteFile(((ChildProc *)pr)->hTo, message, count,
\r
9682 &dOutCount, &ovl);
\r
9683 if (*outError == NO_ERROR) {
\r
9684 outCount = (int) dOutCount;
\r
9694 if(n != 0) Sleep(n);
\r
9698 OutputToProcessDelayed(ProcRef pr, char *message, int count, int *outError,
\r
9701 /* Ignore delay, not implemented for WinBoard */
\r
9702 return OutputToProcess(pr, message, count, outError);
\r
9707 CmailSigHandlerCallBack(InputSourceRef isr, VOIDSTAR closure,
\r
9708 char *buf, int count, int error)
\r
9710 DisplayFatalError(_("Not implemented"), 0, 1);
\r
9713 /* see wgamelist.c for Game List functions */
\r
9714 /* see wedittags.c for Edit Tags functions */
\r
9721 char buf[MSG_SIZ];
\r
9724 if (SearchPath(installDir, appData.icsLogon, NULL, MSG_SIZ, buf, &dummy)) {
\r
9725 f = fopen(buf, "r");
\r
9727 ProcessICSInitScript(f);
\r
9735 StartAnalysisClock()
\r
9737 if (analysisTimerEvent) return;
\r
9738 analysisTimerEvent = SetTimer(hwndMain, (UINT) ANALYSIS_TIMER_ID,
\r
9739 (UINT) 2000, NULL);
\r
9743 SetHighlights(int fromX, int fromY, int toX, int toY)
\r
9745 highlightInfo.sq[0].x = fromX;
\r
9746 highlightInfo.sq[0].y = fromY;
\r
9747 highlightInfo.sq[1].x = toX;
\r
9748 highlightInfo.sq[1].y = toY;
\r
9754 highlightInfo.sq[0].x = highlightInfo.sq[0].y =
\r
9755 highlightInfo.sq[1].x = highlightInfo.sq[1].y = -1;
\r
9759 SetPremoveHighlights(int fromX, int fromY, int toX, int toY)
\r
9761 premoveHighlightInfo.sq[0].x = fromX;
\r
9762 premoveHighlightInfo.sq[0].y = fromY;
\r
9763 premoveHighlightInfo.sq[1].x = toX;
\r
9764 premoveHighlightInfo.sq[1].y = toY;
\r
9768 ClearPremoveHighlights()
\r
9770 premoveHighlightInfo.sq[0].x = premoveHighlightInfo.sq[0].y =
\r
9771 premoveHighlightInfo.sq[1].x = premoveHighlightInfo.sq[1].y = -1;
\r
9775 ShutDownFrontEnd()
\r
9777 if (saveSettingsOnExit) SaveSettings(settingsFileName);
\r
9778 DeleteClipboardTempFiles();
\r
9784 if (IsIconic(hwndMain))
\r
9785 ShowWindow(hwndMain, SW_RESTORE);
\r
9787 SetActiveWindow(hwndMain);
\r
9791 * Prototypes for animation support routines
\r
9793 static void ScreenSquare(int column, int row, POINT * pt);
\r
9794 static void Tween( POINT * start, POINT * mid, POINT * finish, int factor,
\r
9795 POINT frames[], int * nFrames);
\r
9801 AnimateAtomicCapture(Board board, int fromX, int fromY, int toX, int toY)
\r
9802 { // [HGM] atomic: animate blast wave
\r
9805 explodeInfo.fromX = fromX;
\r
9806 explodeInfo.fromY = fromY;
\r
9807 explodeInfo.toX = toX;
\r
9808 explodeInfo.toY = toY;
\r
9809 for(i=1; i<4*kFactor; i++) {
\r
9810 explodeInfo.radius = (i*180)/(4*kFactor-1);
\r
9811 DrawPosition(FALSE, board);
\r
9812 Sleep(appData.animSpeed);
\r
9814 explodeInfo.radius = 0;
\r
9815 DrawPosition(TRUE, board);
\r
9819 AnimateMove(board, fromX, fromY, toX, toY)
\r
9826 ChessSquare piece;
\r
9827 POINT start, finish, mid;
\r
9828 POINT frames[kFactor * 2 + 1];
\r
9831 if (!appData.animate) return;
\r
9832 if (doingSizing) return;
\r
9833 if (fromY < 0 || fromX < 0) return;
\r
9834 piece = board[fromY][fromX];
\r
9835 if (piece >= EmptySquare) return;
\r
9837 ScreenSquare(fromX, fromY, &start);
\r
9838 ScreenSquare(toX, toY, &finish);
\r
9840 /* All moves except knight jumps move in straight line */
\r
9841 if (!(abs(fromX-toX) == 1 && abs(fromY-toY) == 2 || abs(fromX-toX) == 2 && abs(fromY-toY) == 1)) {
\r
9842 mid.x = start.x + (finish.x - start.x) / 2;
\r
9843 mid.y = start.y + (finish.y - start.y) / 2;
\r
9845 /* Knight: make straight movement then diagonal */
\r
9846 if (abs(toY - fromY) < abs(toX - fromX)) {
\r
9847 mid.x = start.x + (finish.x - start.x) / 2;
\r
9851 mid.y = start.y + (finish.y - start.y) / 2;
\r
9855 /* Don't use as many frames for very short moves */
\r
9856 if (abs(toY - fromY) + abs(toX - fromX) <= 2)
\r
9857 Tween(&start, &mid, &finish, kFactor - 1, frames, &nFrames);
\r
9859 Tween(&start, &mid, &finish, kFactor, frames, &nFrames);
\r
9861 animInfo.from.x = fromX;
\r
9862 animInfo.from.y = fromY;
\r
9863 animInfo.to.x = toX;
\r
9864 animInfo.to.y = toY;
\r
9865 animInfo.lastpos = start;
\r
9866 animInfo.piece = piece;
\r
9867 for (n = 0; n < nFrames; n++) {
\r
9868 animInfo.pos = frames[n];
\r
9869 DrawPosition(FALSE, NULL);
\r
9870 animInfo.lastpos = animInfo.pos;
\r
9871 Sleep(appData.animSpeed);
\r
9873 animInfo.pos = finish;
\r
9874 DrawPosition(FALSE, NULL);
\r
9875 animInfo.piece = EmptySquare;
\r
9876 Explode(board, fromX, fromY, toX, toY);
\r
9879 /* Convert board position to corner of screen rect and color */
\r
9882 ScreenSquare(column, row, pt)
\r
9883 int column; int row; POINT * pt;
\r
9886 pt->x = lineGap + ((BOARD_WIDTH-1)-column) * (squareSize + lineGap);
\r
9887 pt->y = lineGap + row * (squareSize + lineGap);
\r
9889 pt->x = lineGap + column * (squareSize + lineGap);
\r
9890 pt->y = lineGap + ((BOARD_HEIGHT-1)-row) * (squareSize + lineGap);
\r
9894 /* Generate a series of frame coords from start->mid->finish.
\r
9895 The movement rate doubles until the half way point is
\r
9896 reached, then halves back down to the final destination,
\r
9897 which gives a nice slow in/out effect. The algorithmn
\r
9898 may seem to generate too many intermediates for short
\r
9899 moves, but remember that the purpose is to attract the
\r
9900 viewers attention to the piece about to be moved and
\r
9901 then to where it ends up. Too few frames would be less
\r
9905 Tween(start, mid, finish, factor, frames, nFrames)
\r
9906 POINT * start; POINT * mid;
\r
9907 POINT * finish; int factor;
\r
9908 POINT frames[]; int * nFrames;
\r
9910 int n, fraction = 1, count = 0;
\r
9912 /* Slow in, stepping 1/16th, then 1/8th, ... */
\r
9913 for (n = 0; n < factor; n++)
\r
9915 for (n = 0; n < factor; n++) {
\r
9916 frames[count].x = start->x + (mid->x - start->x) / fraction;
\r
9917 frames[count].y = start->y + (mid->y - start->y) / fraction;
\r
9919 fraction = fraction / 2;
\r
9923 frames[count] = *mid;
\r
9926 /* Slow out, stepping 1/2, then 1/4, ... */
\r
9928 for (n = 0; n < factor; n++) {
\r
9929 frames[count].x = finish->x - (finish->x - mid->x) / fraction;
\r
9930 frames[count].y = finish->y - (finish->y - mid->y) / fraction;
\r
9932 fraction = fraction * 2;
\r
9938 SettingsPopUp(ChessProgramState *cps)
\r
9939 { // [HGM] wrapper needed because handles must not be passed through back-end
\r
9940 EngineOptionsPopup(savedHwnd, cps);
\r
9943 int flock(int fid, int code)
\r
9945 HANDLE hFile = (HANDLE) _get_osfhandle(fid);
\r
9949 ov.OffsetHigh = 0;
\r
9951 case 1: LockFileEx(hFile, LOCKFILE_EXCLUSIVE_LOCK, 0, 1024, 0, &ov); break; // LOCK_SH
\r
9952 case 2: LockFileEx(hFile, LOCKFILE_EXCLUSIVE_LOCK, 0, 1024, 0, &ov); break; // LOCK_EX
\r
9953 case 3: UnlockFileEx(hFile, 0, 1024, 0, &ov); break; // LOCK_UN
\r
9954 default: return -1;
\r