2 * WinBoard.c -- Windows NT front end to XBoard
\r
4 * Copyright 1991 by Digital Equipment Corporation, Maynard,
\r
7 * Enhancements Copyright 1992-2001, 2002, 2003, 2004, 2005, 2006,
\r
8 * 2007, 2008, 2009, 2010, 2011, 2012 Free Software Foundation, Inc.
\r
10 * Enhancements Copyright 2005 Alessandro Scotti
\r
12 * XBoard borrows its colors and the bitmaps.xchess bitmap set from XChess,
\r
13 * which was written and is copyrighted by Wayne Christopher.
\r
15 * The following terms apply to Digital Equipment Corporation's copyright
\r
16 * interest in XBoard:
\r
17 * ------------------------------------------------------------------------
\r
18 * All Rights Reserved
\r
20 * Permission to use, copy, modify, and distribute this software and its
\r
21 * documentation for any purpose and without fee is hereby granted,
\r
22 * provided that the above copyright notice appear in all copies and that
\r
23 * both that copyright notice and this permission notice appear in
\r
24 * supporting documentation, and that the name of Digital not be
\r
25 * used in advertising or publicity pertaining to distribution of the
\r
26 * software without specific, written prior permission.
\r
28 * DIGITAL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
\r
29 * ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL
\r
30 * DIGITAL BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
\r
31 * ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
\r
32 * WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
\r
33 * ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
\r
35 * ------------------------------------------------------------------------
\r
37 * The following terms apply to the enhanced version of XBoard
\r
38 * distributed by the Free Software Foundation:
\r
39 * ------------------------------------------------------------------------
\r
41 * GNU XBoard is free software: you can redistribute it and/or modify
\r
42 * it under the terms of the GNU General Public License as published by
\r
43 * the Free Software Foundation, either version 3 of the License, or (at
\r
44 * your option) any later version.
\r
46 * GNU XBoard is distributed in the hope that it will be useful, but
\r
47 * WITHOUT ANY WARRANTY; without even the implied warranty of
\r
48 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
\r
49 * General Public License for more details.
\r
51 * You should have received a copy of the GNU General Public License
\r
52 * along with this program. If not, see http://www.gnu.org/licenses/. *
\r
54 *------------------------------------------------------------------------
\r
55 ** See the file ChangeLog for a revision history. */
\r
59 #include <windows.h>
\r
60 #include <winuser.h>
\r
61 #include <winsock.h>
\r
62 #include <commctrl.h>
\r
68 #include <sys/stat.h>
\r
71 #include <commdlg.h>
\r
73 #include <richedit.h>
\r
74 #include <mmsystem.h>
\r
84 #include "frontend.h"
\r
85 #include "backend.h"
\r
86 #include "winboard.h"
\r
88 #include "wclipbrd.h"
\r
89 #include "woptions.h"
\r
90 #include "wsockerr.h"
\r
91 #include "defaults.h"
\r
95 //void InitEngineUCI( const char * iniDir, ChessProgramState * cps );
\r
98 void mysrandom(unsigned int seed);
\r
100 extern int whiteFlag, blackFlag;
\r
101 Boolean flipClock = FALSE;
\r
102 extern HANDLE chatHandle[];
\r
103 extern enum ICS_TYPE ics_type;
\r
105 int MySearchPath P((char *installDir, char *name, char *fullname));
\r
106 int MyGetFullPathName P((char *name, char *fullname));
\r
107 void DisplayHoldingsCount(HDC hdc, int x, int y, int align, int copyNumber);
\r
108 VOID NewVariantPopup(HWND hwnd);
\r
109 int FinishMove P((ChessMove moveType, int fromX, int fromY, int toX, int toY,
\r
110 /*char*/int promoChar));
\r
111 void DisplayMove P((int moveNumber));
\r
112 Boolean ParseFEN P((Board board, int *blackPlaysFirst, char *fen));
\r
113 void ChatPopUp P((char *s));
\r
115 ChessSquare piece;
\r
116 POINT pos; /* window coordinates of current pos */
\r
117 POINT lastpos; /* window coordinates of last pos - used for clipping */
\r
118 POINT from; /* board coordinates of the piece's orig pos */
\r
119 POINT to; /* board coordinates of the piece's new pos */
\r
122 static AnimInfo animInfo = { EmptySquare, {-1,-1}, {-1,-1}, {-1,-1} };
\r
125 POINT start; /* window coordinates of start pos */
\r
126 POINT pos; /* window coordinates of current pos */
\r
127 POINT lastpos; /* window coordinates of last pos - used for clipping */
\r
128 POINT from; /* board coordinates of the piece's orig pos */
\r
132 static DragInfo dragInfo = { {-1,-1}, {-1,-1}, {-1,-1}, {-1,-1}, EmptySquare };
\r
135 POINT sq[2]; /* board coordinates of from, to squares */
\r
138 static HighlightInfo highlightInfo = { {{-1, -1}, {-1, -1}} };
\r
139 static HighlightInfo premoveHighlightInfo = { {{-1, -1}, {-1, -1}} };
\r
140 static HighlightInfo partnerHighlightInfo = { {{-1, -1}, {-1, -1}} };
\r
141 static HighlightInfo oldPartnerHighlight = { {{-1, -1}, {-1, -1}} };
\r
143 typedef struct { // [HGM] atomic
\r
144 int fromX, fromY, toX, toY, radius;
\r
147 static ExplodeInfo explodeInfo;
\r
149 /* Window class names */
\r
150 char szAppName[] = "WinBoard";
\r
151 char szConsoleName[] = "WBConsole";
\r
153 /* Title bar text */
\r
154 char szTitle[] = "WinBoard";
\r
155 char szConsoleTitle[] = "I C S Interaction";
\r
158 char *settingsFileName;
\r
159 Boolean saveSettingsOnExit;
\r
160 char installDir[MSG_SIZ];
\r
161 int errorExitStatus;
\r
163 BoardSize boardSize;
\r
164 Boolean chessProgram;
\r
165 //static int boardX, boardY;
\r
166 int minX, minY; // [HGM] placement: volatile limits on upper-left corner
\r
167 int squareSize, lineGap, minorSize;
\r
168 static int winW, winH;
\r
169 static RECT messageRect, whiteRect, blackRect, leftLogoRect, rightLogoRect; // [HGM] logo
\r
170 static int logoHeight = 0;
\r
171 static char messageText[MESSAGE_TEXT_MAX];
\r
172 static int clockTimerEvent = 0;
\r
173 static int loadGameTimerEvent = 0;
\r
174 static int analysisTimerEvent = 0;
\r
175 static DelayedEventCallback delayedTimerCallback;
\r
176 static int delayedTimerEvent = 0;
\r
177 static int buttonCount = 2;
\r
178 char *icsTextMenuString;
\r
180 char *firstChessProgramNames;
\r
181 char *secondChessProgramNames;
\r
183 #define PALETTESIZE 256
\r
185 HINSTANCE hInst; /* current instance */
\r
186 Boolean alwaysOnTop = FALSE;
\r
188 COLORREF lightSquareColor, darkSquareColor, whitePieceColor,
\r
189 blackPieceColor, highlightSquareColor, premoveHighlightColor;
\r
191 ColorClass currentColorClass;
\r
193 static HWND savedHwnd;
\r
194 HWND hCommPort = NULL; /* currently open comm port */
\r
195 static HWND hwndPause; /* pause button */
\r
196 static HBITMAP pieceBitmap[3][(int) BlackPawn]; /* [HGM] nr of bitmaps referred to bP in stead of wK */
\r
197 static HBRUSH lightSquareBrush, darkSquareBrush,
\r
198 blackSquareBrush, /* [HGM] for band between board and holdings */
\r
199 explodeBrush, /* [HGM] atomic */
\r
200 markerBrush, /* [HGM] markers */
\r
201 whitePieceBrush, blackPieceBrush, iconBkgndBrush /*, outlineBrush*/;
\r
202 static POINT gridEndpoints[(BOARD_RANKS + BOARD_FILES + 2) * 2];
\r
203 static DWORD gridVertexCounts[BOARD_RANKS + BOARD_FILES + 2];
\r
204 static HPEN gridPen = NULL;
\r
205 static HPEN highlightPen = NULL;
\r
206 static HPEN premovePen = NULL;
\r
207 static NPLOGPALETTE pLogPal;
\r
208 static BOOL paletteChanged = FALSE;
\r
209 static HICON iconWhite, iconBlack, iconCurrent;
\r
210 static int doingSizing = FALSE;
\r
211 static int lastSizing = 0;
\r
212 static int prevStderrPort;
\r
213 static HBITMAP userLogo;
\r
215 static HBITMAP liteBackTexture = NULL;
\r
216 static HBITMAP darkBackTexture = NULL;
\r
217 static int liteBackTextureMode = BACK_TEXTURE_MODE_PLAIN;
\r
218 static int darkBackTextureMode = BACK_TEXTURE_MODE_PLAIN;
\r
219 static int backTextureSquareSize = 0;
\r
220 static struct { int x; int y; int mode; } backTextureSquareInfo[BOARD_RANKS][BOARD_FILES];
\r
222 #if __GNUC__ && !defined(_winmajor)
\r
223 #define oldDialog 0 /* cygwin doesn't define _winmajor; mingw does */
\r
225 #if defined(_winmajor)
\r
226 #define oldDialog (_winmajor < 4)
\r
228 #define oldDialog 0
\r
232 #define INTERNATIONAL
\r
234 #ifdef INTERNATIONAL
\r
235 # define _(s) T_(s)
\r
241 # define Translate(x, y)
\r
242 # define LoadLanguageFile(s)
\r
245 #ifdef INTERNATIONAL
\r
247 Boolean barbaric; // flag indicating if translation is needed
\r
249 // list of item numbers used in each dialog (used to alter language at run time)
\r
251 #define ABOUTBOX -1 /* not sure why these are needed */
\r
252 #define ABOUTBOX2 -1
\r
254 int dialogItems[][42] = {
\r
255 { ABOUTBOX, IDOK, OPT_MESS, 400 },
\r
256 { DLG_TimeControl, IDC_Babble, OPT_TCUseMoves, OPT_TCUseInc, OPT_TCUseFixed,
\r
257 OPT_TCtext1, OPT_TCtext2, OPT_TCitext1, OPT_TCitext2, OPT_TCftext, GPB_Factors, IDC_Factor1, IDC_Factor2, IDOK, IDCANCEL },
\r
258 { DLG_LoadOptions, OPT_Autostep, OPT_AStext1, OPT_Exact, OPT_Subset, OPT_Struct, OPT_Material, OPT_Range, OPT_Difference,
\r
259 OPT_elo1t, OPT_elo2t, OPT_datet, OPT_Stretch, OPT_Stretcht, OPT_Reversed, OPT_SearchMode, OPT_Mirror, OPT_thresholds, IDOK, IDCANCEL },
\r
260 { DLG_SaveOptions, OPT_Autosave, OPT_AVPrompt, OPT_AVToFile, OPT_AVBrowse,
\r
261 801, OPT_PGN, OPT_Old, OPT_OutOfBookInfo, IDOK, IDCANCEL },
\r
262 { 1536, 1090, IDC_Directories, 1089, 1091, IDOK, IDCANCEL, 1038, IDC_IndexNr, 1037 },
\r
263 { DLG_CommPort, IDOK, IDCANCEL, IDC_Port, IDC_Rate, IDC_Bits, IDC_Parity,
\r
264 IDC_Stop, IDC_Flow, OPT_SerialHelp },
\r
265 { DLG_EditComment, IDOK, OPT_CancelComment, OPT_ClearComment, OPT_EditComment },
\r
266 { DLG_PromotionKing, PB_Chancellor, PB_Archbishop, PB_Queen, PB_Rook,
\r
267 PB_Bishop, PB_Knight, PB_King, IDCANCEL, IDC_Yes, IDC_No, IDC_Centaur },
\r
268 { ABOUTBOX2, IDC_ChessBoard },
\r
269 { DLG_GameList, OPT_GameListLoad, OPT_GameListPrev, OPT_GameListNext,
\r
270 OPT_GameListClose, IDC_GameListDoFilter },
\r
271 { DLG_EditTags, IDOK, OPT_TagsCancel, OPT_EditTags },
\r
272 { DLG_Error, IDOK },
\r
273 { DLG_Colorize, IDOK, IDCANCEL, OPT_ChooseColor, OPT_Bold, OPT_Italic,
\r
274 OPT_Underline, OPT_Strikeout, OPT_Sample },
\r
275 { DLG_Question, IDOK, IDCANCEL, OPT_QuestionText },
\r
276 { DLG_Startup, IDC_Welcome, OPT_ChessEngine, OPT_ChessServer, OPT_View,
\r
277 IDC_SPECIFY_ENG_STATIC, IDC_SPECIFY_SERVER_STATIC, OPT_AnyAdditional,
\r
278 IDOK, IDCANCEL, IDM_HELPCONTENTS },
\r
279 { DLG_IndexNumber, IDC_Index },
\r
280 { DLG_TypeInMove, IDOK, IDCANCEL },
\r
281 { DLG_TypeInName, IDOK, IDCANCEL },
\r
282 { DLG_Sound, IDC_Event, OPT_NoSound, OPT_DefaultBeep, OPT_BuiltInSound,
\r
283 OPT_WavFile, OPT_BrowseSound, OPT_DefaultSounds, IDOK, IDCANCEL, OPT_PlaySound },
\r
284 { DLG_GeneralOptions, IDOK, IDCANCEL, OPT_AlwaysOnTop, OPT_HighlightLastMove,
\r
285 OPT_AlwaysQueen, OPT_PeriodicUpdates, OPT_AnimateDragging, OPT_PonderNextMove,
\r
286 OPT_AnimateMoving, OPT_PopupExitMessage, OPT_AutoFlag, OPT_PopupMoveErrors,
\r
287 OPT_AutoFlipView, OPT_ShowButtonBar, OPT_AutoRaiseBoard, OPT_ShowCoordinates,
\r
288 OPT_Blindfold, OPT_ShowThinking, OPT_HighlightDragging, OPT_TestLegality,
\r
289 OPT_SaveExtPGN, OPT_HideThinkFromHuman, OPT_ExtraInfoInMoveHistory,
\r
290 OPT_HighlightMoveArrow, OPT_AutoLogo ,OPT_SmartMove },
\r
291 { DLG_IcsOptions, IDOK, IDCANCEL, OPT_AutoComment, OPT_AutoKibitz, OPT_AutoObserve,
\r
292 OPT_GetMoveList, OPT_LocalLineEditing, OPT_QuietPlay, OPT_SeekGraph, OPT_AutoRefresh,
\r
293 OPT_BgObserve, OPT_DualBoard, OPT_Premove, OPT_PremoveWhite, OPT_PremoveBlack,
\r
294 OPT_SmartMove, OPT_IcsAlarm, IDC_Sec, OPT_ChooseShoutColor, OPT_ChooseSShoutColor,
\r
295 OPT_ChooseChannel1Color, OPT_ChooseChannelColor, OPT_ChooseKibitzColor,
\r
296 OPT_ChooseTellColor, OPT_ChooseChallengeColor, OPT_ChooseRequestColor,
\r
297 OPT_ChooseSeekColor, OPT_ChooseNormalColor, OPT_ChooseBackgroundColor,
\r
298 OPT_DefaultColors, OPT_DontColorize, IDC_Boxes, GPB_Colors, GPB_Premove,
\r
299 GPB_General, GPB_Alarm },
\r
300 { DLG_BoardOptions, IDOK, IDCANCEL, OPT_SizeTiny, OPT_SizeTeeny, OPT_SizeDinky,
\r
301 OPT_SizePetite, OPT_SizeSlim, OPT_SizeSmall, OPT_SizeMediocre, OPT_SizeMiddling,
\r
302 OPT_SizeAverage, OPT_SizeModerate, OPT_SizeMedium, OPT_SizeBulky, OPT_SizeLarge,
\r
303 OPT_SizeBig, OPT_SizeHuge, OPT_SizeGiant, OPT_SizeColossal, OPT_SizeTitanic,
\r
304 OPT_ChooseLightSquareColor, OPT_ChooseDarkSquareColor, OPT_ChooseWhitePieceColor,
\r
305 OPT_ChooseBlackPieceColor, OPT_ChooseHighlightSquareColor, OPT_ChoosePremoveHighlightColor,
\r
306 OPT_Monochrome, OPT_AllWhite, OPT_UpsideDown, OPT_DefaultBoardColors, GPB_Colors,
\r
307 IDC_Light, IDC_Dark, IDC_White, IDC_Black, IDC_High, IDC_PreHigh, GPB_Size, OPT_Bitmaps, OPT_PieceFont, OPT_Grid },
\r
308 { DLG_NewVariant, IDOK, IDCANCEL, OPT_VariantNormal, OPT_VariantFRC, OPT_VariantWildcastle,
\r
309 OPT_VariantNocastle, OPT_VariantLosers, OPT_VariantGiveaway, OPT_VariantSuicide,
\r
310 OPT_Variant3Check, OPT_VariantTwoKings, OPT_VariantAtomic, OPT_VariantCrazyhouse,
\r
311 OPT_VariantBughouse, OPT_VariantTwilight, OPT_VariantShogi, OPT_VariantSuper,
\r
312 OPT_VariantKnightmate, OPT_VariantBerolina, OPT_VariantCylinder, OPT_VariantFairy,
\r
313 OPT_VariantMakruk, OPT_VariantGothic, OPT_VariantCapablanca, OPT_VariantJanus,
\r
314 OPT_VariantCRC, OPT_VariantFalcon, OPT_VariantCourier, OPT_VariantGreat, OPT_VariantSChess,
\r
315 OPT_VariantShatranj, OPT_VariantXiangqi, GPB_Variant, GPB_Board, IDC_Height,
\r
316 IDC_Width, IDC_Hand, IDC_Pieces, IDC_Def },
\r
317 { DLG_Fonts, IDOK, IDCANCEL, OPT_ChooseClockFont, OPT_ChooseMessageFont,
\r
318 OPT_ChooseCoordFont, OPT_ChooseTagFont, OPT_ChooseCommentsFont, OPT_ChooseConsoleFont, OPT_ChooseMoveHistoryFont, OPT_DefaultFonts,
\r
319 OPT_ClockFont, OPT_MessageFont, OPT_CoordFont, OPT_EditTagsFont, OPT_ChoosePieceFont, OPT_MessageFont8,
\r
320 OPT_SampleGameListFont, OPT_ChooseGameListFont, OPT_MessageFont7,
\r
321 OPT_CommentsFont, OPT_MessageFont5, GPB_Current, GPB_All, OPT_MessageFont6 },
\r
322 { DLG_NewGameFRC, IDC_NFG_Label, IDC_NFG_Random, IDOK, IDCANCEL },
\r
323 { DLG_GameListOptions, IDC_GLT, IDC_GLT_Up, IDC_GLT_Down, IDC_GLT_Restore,
\r
324 IDC_GLT_Default, IDOK, IDCANCEL, IDC_GLT_RestoreTo },
\r
325 { DLG_MoveHistory },
\r
326 { DLG_EvalGraph },
\r
327 { DLG_EngineOutput, IDC_EngineLabel1, IDC_Engine1_NPS, IDC_EngineLabel2, IDC_Engine2_NPS },
\r
328 { DLG_Chat, IDC_Partner, IDC_Clear, IDC_Send, },
\r
329 { DLG_EnginePlayOptions, IDC_EpPonder, IDC_EpShowThinking, IDC_EpHideThinkingHuman,
\r
330 IDC_EpPeriodicUpdates, GPB_Adjudications, IDC_Draw, IDC_Moves, IDC_Threshold,
\r
331 IDC_Centi, IDC_TestClaims, IDC_DetectMates, IDC_MaterialDraws, IDC_TrivialDraws,
\r
332 GPB_Apply, IDC_Rule, IDC_Repeats, IDC_ScoreAbs1, IDC_ScoreAbs2, IDOK, IDCANCEL },
\r
333 { DLG_OptionsUCI, IDC_PolyDir, IDC_BrowseForPolyglotDir, IDC_Hash, IDC_Path,
\r
334 IDC_BrowseForEGTB, IDC_Cache, IDC_UseBook, IDC_BrowseForBook, IDC_CPU, IDC_OwnBook1,
\r
335 IDC_OwnBook2, IDC_Depth, IDC_Variation, IDC_DefGames, IDOK, IDCANCEL },
\r
339 static char languageBuf[70000], *foreign[1000], *english[1000], *languageFile[MSG_SIZ];
\r
340 static int lastChecked;
\r
341 static char oldLanguage[MSG_SIZ], *menuText[10][30];
\r
342 extern int tinyLayout;
\r
343 extern char * menuBarText[][10];
\r
346 LoadLanguageFile(char *name)
\r
347 { //load the file with translations, and make a list of the strings to be translated, and their translations
\r
349 int i=0, j=0, n=0, k;
\r
352 if(!name || name[0] == NULLCHAR) return;
\r
353 snprintf(buf, MSG_SIZ, "%s%s", name, strchr(name, '.') ? "" : ".lng"); // auto-append lng extension
\r
354 appData.language = oldLanguage;
\r
355 if(!strcmp(buf, oldLanguage)) { barbaric = 1; return; } // this language already loaded; just switch on
\r
356 if((f = fopen(buf, "r")) == NULL) return;
\r
357 while((k = fgetc(f)) != EOF) {
\r
358 if(i >= sizeof(languageBuf)) { DisplayError("Language file too big", 0); return; }
\r
359 languageBuf[i] = k;
\r
361 if(languageBuf[n] == '"' && languageBuf[i-1] == '"') {
\r
363 if(p = strstr(languageBuf + n + 1, "\" === \"")) {
\r
364 if(p > languageBuf+n+2 && p+8 < languageBuf+i) {
\r
365 if(j >= sizeof(english)) { DisplayError("Too many translated strings", 0); return; }
\r
366 english[j] = languageBuf + n + 1; *p = 0;
\r
367 foreign[j++] = p + 7; languageBuf[i-1] = 0;
\r
368 //if(appData.debugMode) fprintf(debugFP, "translation: replace '%s' by '%s'\n", english[j-1], foreign[j-1]);
\r
373 } else if(i > 0 && languageBuf[i-1] == '\\') {
\r
375 case 'n': k = '\n'; break;
\r
376 case 'r': k = '\r'; break;
\r
377 case 't': k = '\t'; break;
\r
379 languageBuf[--i] = k;
\r
384 barbaric = (j != 0);
\r
385 safeStrCpy(oldLanguage, buf, sizeof(oldLanguage)/sizeof(oldLanguage[0]) );
\r
390 { // return the translation of the given string
\r
391 // efficiency can be improved a lot...
\r
393 static char buf[MSG_SIZ];
\r
394 //if(appData.debugMode) fprintf(debugFP, "T_(%s)\n", s);
\r
395 if(!barbaric) return s;
\r
396 if(!s) return ""; // sanity
\r
397 while(english[i]) {
\r
398 if(!strcmp(s, english[i])) return foreign[i];
\r
399 if(english[i][0] == '%' && strstr(s, english[i]+1) == s) { // allow translation of strings with variable ending
\r
400 snprintf(buf, MSG_SIZ, "%s%s", foreign[i], s + strlen(english[i]+1)); // keep unmatched portion
\r
409 Translate(HWND hDlg, int dialogID)
\r
410 { // translate all text items in the given dialog
\r
412 char buf[MSG_SIZ], *s;
\r
413 if(!barbaric) return;
\r
414 while(dialogItems[i][0] && dialogItems[i][0] != dialogID) i++; // find the dialog description
\r
415 if(dialogItems[i][0] != dialogID) return; // unknown dialog, should not happen
\r
416 GetWindowText( hDlg, buf, MSG_SIZ );
\r
418 if(strcmp(buf, s)) SetWindowText(hDlg, s); // replace by translated string (if different)
\r
419 for(j=1; k=dialogItems[i][j]; j++) { // translate all listed dialog items
\r
420 GetDlgItemText(hDlg, k, buf, MSG_SIZ);
\r
421 if(strlen(buf) == 0) continue;
\r
423 if(strcmp(buf, s)) SetDlgItemText(hDlg, k, s); // replace by translated string (if different)
\r
428 TranslateOneMenu(int i, HMENU subMenu)
\r
431 static MENUITEMINFO info;
\r
433 info.cbSize = sizeof(MENUITEMINFO);
\r
434 info.fMask = MIIM_STATE | MIIM_TYPE;
\r
435 for(j=GetMenuItemCount(subMenu)-1; j>=0; j--){
\r
437 info.dwTypeData = buf;
\r
438 info.cch = sizeof(buf);
\r
439 GetMenuItemInfo(subMenu, j, TRUE, &info);
\r
441 if(menuText[i][j]) safeStrCpy(buf, menuText[i][j], sizeof(buf)/sizeof(buf[0]) );
\r
442 else menuText[i][j] = strdup(buf); // remember original on first change
\r
444 if(buf[0] == NULLCHAR) continue;
\r
445 info.dwTypeData = T_(buf);
\r
446 info.cch = strlen(buf)+1;
\r
447 SetMenuItemInfo(subMenu, j, TRUE, &info);
\r
453 TranslateMenus(int addLanguage)
\r
456 WIN32_FIND_DATA fileData;
\r
458 #define IDM_English 1970
\r
460 HMENU mainMenu = GetMenu(hwndMain);
\r
461 for (i=GetMenuItemCount(mainMenu)-1; i>=0; i--) {
\r
462 HMENU subMenu = GetSubMenu(mainMenu, i);
\r
463 ModifyMenu(mainMenu, i, MF_STRING|MF_BYPOSITION|MF_POPUP|EnableMenuItem(mainMenu, i, MF_BYPOSITION),
\r
464 (UINT) subMenu, T_(menuBarText[tinyLayout][i]));
\r
465 TranslateOneMenu(i, subMenu);
\r
467 DrawMenuBar(hwndMain);
\r
470 if(!addLanguage) return;
\r
471 if((hFind = FindFirstFile("*.LNG", &fileData)) != INVALID_HANDLE_VALUE) {
\r
472 HMENU mainMenu = GetMenu(hwndMain);
\r
473 HMENU subMenu = GetSubMenu(mainMenu, GetMenuItemCount(mainMenu)-1);
\r
474 AppendMenu(subMenu, MF_SEPARATOR, (UINT_PTR) 0, NULL);
\r
475 AppendMenu(subMenu, MF_ENABLED|MF_STRING|(barbaric?MF_UNCHECKED:MF_CHECKED), (UINT_PTR) IDM_English, (LPCTSTR) "English");
\r
476 i = 0; lastChecked = IDM_English;
\r
478 char *p, *q = fileData.cFileName;
\r
479 int checkFlag = MF_UNCHECKED;
\r
480 languageFile[i] = strdup(q);
\r
481 if(barbaric && !strcmp(oldLanguage, q)) {
\r
482 checkFlag = MF_CHECKED;
\r
483 lastChecked = IDM_English + i + 1;
\r
484 CheckMenuItem(mainMenu, IDM_English, MF_BYCOMMAND|MF_UNCHECKED);
\r
486 *q = ToUpper(*q); while(*++q) *q = ToLower(*q);
\r
487 p = strstr(fileData.cFileName, ".lng");
\r
489 AppendMenu(subMenu, MF_ENABLED|MF_STRING|checkFlag, (UINT_PTR) IDM_English + ++i, (LPCTSTR) fileData.cFileName);
\r
490 } while(FindNextFile(hFind, &fileData));
\r
497 #define IDM_RecentEngines 3000
\r
500 RecentEngineMenu (char *s)
\r
502 if(appData.icsActive) return;
\r
503 if(appData.recentEngines > 0 && *s) { // feature is on, and list non-empty
\r
504 HMENU mainMenu = GetMenu(hwndMain);
\r
505 HMENU subMenu = GetSubMenu(mainMenu, 5); // Engine menu
\r
506 int i=IDM_RecentEngines;
\r
507 recentEngines = strdup(appData.recentEngineList); // remember them as they are in menu
\r
508 AppendMenu(subMenu, MF_SEPARATOR, (UINT_PTR) 0, NULL);
\r
510 char *p = strchr(s, '\n');
\r
511 if(p == NULL) return; // malformed!
\r
513 AppendMenu(subMenu, MF_ENABLED|MF_STRING|MF_UNCHECKED, (UINT_PTR) i++, (LPCTSTR) s);
\r
527 int cliWidth, cliHeight;
\r
530 SizeInfo sizeInfo[] =
\r
532 { "tiny", 21, 0, 1, 1, 0, 0 },
\r
533 { "teeny", 25, 1, 1, 1, 0, 0 },
\r
534 { "dinky", 29, 1, 1, 1, 0, 0 },
\r
535 { "petite", 33, 1, 1, 1, 0, 0 },
\r
536 { "slim", 37, 2, 1, 0, 0, 0 },
\r
537 { "small", 40, 2, 1, 0, 0, 0 },
\r
538 { "mediocre", 45, 2, 1, 0, 0, 0 },
\r
539 { "middling", 49, 2, 0, 0, 0, 0 },
\r
540 { "average", 54, 2, 0, 0, 0, 0 },
\r
541 { "moderate", 58, 3, 0, 0, 0, 0 },
\r
542 { "medium", 64, 3, 0, 0, 0, 0 },
\r
543 { "bulky", 72, 3, 0, 0, 0, 0 },
\r
544 { "large", 80, 3, 0, 0, 0, 0 },
\r
545 { "big", 87, 3, 0, 0, 0, 0 },
\r
546 { "huge", 95, 3, 0, 0, 0, 0 },
\r
547 { "giant", 108, 3, 0, 0, 0, 0 },
\r
548 { "colossal", 116, 4, 0, 0, 0, 0 },
\r
549 { "titanic", 129, 4, 0, 0, 0, 0 },
\r
550 { NULL, 0, 0, 0, 0, 0, 0 }
\r
553 #define MF(x) {x, {{0,}, 0. }, {0, }, 0}
\r
554 MyFont fontRec[NUM_SIZES][NUM_FONTS] =
\r
556 { MF(CLOCK_FONT_TINY), MF(MESSAGE_FONT_TINY), MF(COORD_FONT_TINY), MF(CONSOLE_FONT_TINY), MF(COMMENT_FONT_TINY), MF(EDITTAGS_FONT_TINY), MF(MOVEHISTORY_FONT_ALL), MF(GAMELIST_FONT_ALL) },
\r
557 { MF(CLOCK_FONT_TEENY), MF(MESSAGE_FONT_TEENY), MF(COORD_FONT_TEENY), MF(CONSOLE_FONT_TEENY), MF(COMMENT_FONT_TEENY), MF(EDITTAGS_FONT_TEENY), MF(MOVEHISTORY_FONT_ALL), MF(GAMELIST_FONT_ALL) },
\r
558 { MF(CLOCK_FONT_DINKY), MF(MESSAGE_FONT_DINKY), MF(COORD_FONT_DINKY), MF(CONSOLE_FONT_DINKY), MF(COMMENT_FONT_DINKY), MF(EDITTAGS_FONT_DINKY), MF(MOVEHISTORY_FONT_ALL), MF(GAMELIST_FONT_ALL) },
\r
559 { MF(CLOCK_FONT_PETITE), MF(MESSAGE_FONT_PETITE), MF(COORD_FONT_PETITE), MF(CONSOLE_FONT_PETITE), MF(COMMENT_FONT_PETITE), MF(EDITTAGS_FONT_PETITE), MF(MOVEHISTORY_FONT_ALL), MF(GAMELIST_FONT_ALL) },
\r
560 { MF(CLOCK_FONT_SLIM), MF(MESSAGE_FONT_SLIM), MF(COORD_FONT_SLIM), MF(CONSOLE_FONT_SLIM), MF(COMMENT_FONT_SLIM), MF(EDITTAGS_FONT_SLIM), MF(MOVEHISTORY_FONT_ALL), MF(GAMELIST_FONT_ALL) },
\r
561 { MF(CLOCK_FONT_SMALL), MF(MESSAGE_FONT_SMALL), MF(COORD_FONT_SMALL), MF(CONSOLE_FONT_SMALL), MF(COMMENT_FONT_SMALL), MF(EDITTAGS_FONT_SMALL), MF(MOVEHISTORY_FONT_ALL), MF(GAMELIST_FONT_ALL) },
\r
562 { MF(CLOCK_FONT_MEDIOCRE), MF(MESSAGE_FONT_MEDIOCRE), MF(COORD_FONT_MEDIOCRE), MF(CONSOLE_FONT_MEDIOCRE), MF(COMMENT_FONT_MEDIOCRE), MF(EDITTAGS_FONT_MEDIOCRE), MF(MOVEHISTORY_FONT_ALL), MF(GAMELIST_FONT_ALL) },
\r
563 { MF(CLOCK_FONT_MIDDLING), MF(MESSAGE_FONT_MIDDLING), MF(COORD_FONT_MIDDLING), MF(CONSOLE_FONT_MIDDLING), MF(COMMENT_FONT_MIDDLING), MF(EDITTAGS_FONT_MIDDLING), MF(MOVEHISTORY_FONT_ALL), MF(GAMELIST_FONT_ALL) },
\r
564 { MF(CLOCK_FONT_AVERAGE), MF(MESSAGE_FONT_AVERAGE), MF(COORD_FONT_AVERAGE), MF(CONSOLE_FONT_AVERAGE), MF(COMMENT_FONT_AVERAGE), MF(EDITTAGS_FONT_AVERAGE), MF(MOVEHISTORY_FONT_ALL), MF(GAMELIST_FONT_ALL) },
\r
565 { MF(CLOCK_FONT_MODERATE), MF(MESSAGE_FONT_MODERATE), MF(COORD_FONT_MODERATE), MF(CONSOLE_FONT_MODERATE), MF(COMMENT_FONT_MODERATE), MF(EDITTAGS_FONT_MODERATE), MF(MOVEHISTORY_FONT_ALL), MF(GAMELIST_FONT_ALL) },
\r
566 { MF(CLOCK_FONT_MEDIUM), MF(MESSAGE_FONT_MEDIUM), MF(COORD_FONT_MEDIUM), MF(CONSOLE_FONT_MEDIUM), MF(COMMENT_FONT_MEDIUM), MF(EDITTAGS_FONT_MEDIUM), MF(MOVEHISTORY_FONT_ALL), MF(GAMELIST_FONT_ALL) },
\r
567 { MF(CLOCK_FONT_BULKY), MF(MESSAGE_FONT_BULKY), MF(COORD_FONT_BULKY), MF(CONSOLE_FONT_BULKY), MF(COMMENT_FONT_BULKY), MF(EDITTAGS_FONT_BULKY), MF(MOVEHISTORY_FONT_ALL), MF(GAMELIST_FONT_ALL) },
\r
568 { MF(CLOCK_FONT_LARGE), MF(MESSAGE_FONT_LARGE), MF(COORD_FONT_LARGE), MF(CONSOLE_FONT_LARGE), MF(COMMENT_FONT_LARGE), MF(EDITTAGS_FONT_LARGE), MF(MOVEHISTORY_FONT_ALL), MF(GAMELIST_FONT_ALL) },
\r
569 { MF(CLOCK_FONT_BIG), MF(MESSAGE_FONT_BIG), MF(COORD_FONT_BIG), MF(CONSOLE_FONT_BIG), MF(COMMENT_FONT_BIG), MF(EDITTAGS_FONT_BIG), MF(MOVEHISTORY_FONT_ALL), MF(GAMELIST_FONT_ALL) },
\r
570 { MF(CLOCK_FONT_HUGE), MF(MESSAGE_FONT_HUGE), MF(COORD_FONT_HUGE), MF(CONSOLE_FONT_HUGE), MF(COMMENT_FONT_HUGE), MF(EDITTAGS_FONT_HUGE), MF(MOVEHISTORY_FONT_ALL), MF(GAMELIST_FONT_ALL) },
\r
571 { MF(CLOCK_FONT_GIANT), MF(MESSAGE_FONT_GIANT), MF(COORD_FONT_GIANT), MF(CONSOLE_FONT_GIANT), MF(COMMENT_FONT_GIANT), MF(EDITTAGS_FONT_GIANT), MF(MOVEHISTORY_FONT_ALL), MF(GAMELIST_FONT_ALL) },
\r
572 { MF(CLOCK_FONT_COLOSSAL), MF(MESSAGE_FONT_COLOSSAL), MF(COORD_FONT_COLOSSAL), MF(CONSOLE_FONT_COLOSSAL), MF(COMMENT_FONT_COLOSSAL), MF(EDITTAGS_FONT_COLOSSAL), MF(MOVEHISTORY_FONT_ALL), MF (GAMELIST_FONT_ALL) },
\r
573 { MF(CLOCK_FONT_TITANIC), MF(MESSAGE_FONT_TITANIC), MF(COORD_FONT_TITANIC), MF(CONSOLE_FONT_TITANIC), MF(COMMENT_FONT_TITANIC), MF(EDITTAGS_FONT_TITANIC), MF(MOVEHISTORY_FONT_ALL), MF(GAMELIST_FONT_ALL) },
\r
576 MyFont *font[NUM_SIZES][NUM_FONTS];
\r
585 #define BUTTON_WIDTH (tinyLayout ? 16 : 32)
\r
586 #define N_BUTTONS 5
\r
588 MyButtonDesc buttonDesc[N_BUTTONS] =
\r
590 {"<<", IDM_ToStart, NULL, NULL},
\r
591 {"<", IDM_Backward, NULL, NULL},
\r
592 {"P", IDM_Pause, NULL, NULL},
\r
593 {">", IDM_Forward, NULL, NULL},
\r
594 {">>", IDM_ToEnd, NULL, NULL},
\r
597 int tinyLayout = 0, smallLayout = 0;
\r
598 #define MENU_BAR_ITEMS 9
\r
599 char *menuBarText[2][MENU_BAR_ITEMS+1] = {
\r
600 { N_("&File"), N_("&Edit"), N_("&View"), N_("&Mode"), N_("&Action"), N_("E&ngine"), N_("&Options"), N_("&Help"), NULL },
\r
601 { N_("&F"), N_("&E"), N_("&V"), N_("&M"), N_("&A"), N_("&N"), N_("&O"), N_("&H"), NULL },
\r
605 MySound sounds[(int)NSoundClasses];
\r
606 MyTextAttribs textAttribs[(int)NColorClasses];
\r
608 MyColorizeAttribs colorizeAttribs[] = {
\r
609 { (COLORREF)0, 0, N_("Shout Text") },
\r
610 { (COLORREF)0, 0, N_("SShout/CShout") },
\r
611 { (COLORREF)0, 0, N_("Channel 1 Text") },
\r
612 { (COLORREF)0, 0, N_("Channel Text") },
\r
613 { (COLORREF)0, 0, N_("Kibitz Text") },
\r
614 { (COLORREF)0, 0, N_("Tell Text") },
\r
615 { (COLORREF)0, 0, N_("Challenge Text") },
\r
616 { (COLORREF)0, 0, N_("Request Text") },
\r
617 { (COLORREF)0, 0, N_("Seek Text") },
\r
618 { (COLORREF)0, 0, N_("Normal Text") },
\r
619 { (COLORREF)0, 0, N_("None") }
\r
624 static char *commentTitle;
\r
625 static char *commentText;
\r
626 static int commentIndex;
\r
627 static Boolean editComment = FALSE;
\r
630 char errorTitle[MSG_SIZ];
\r
631 char errorMessage[2*MSG_SIZ];
\r
632 HWND errorDialog = NULL;
\r
633 BOOLEAN moveErrorMessageUp = FALSE;
\r
634 BOOLEAN consoleEcho = TRUE;
\r
635 CHARFORMAT consoleCF;
\r
636 COLORREF consoleBackgroundColor;
\r
638 char *programVersion;
\r
644 typedef int CPKind;
\r
653 SOCKET sock2; /* stderr socket for OpenRcmd */
\r
656 #define INPUT_SOURCE_BUF_SIZE 4096
\r
658 typedef struct _InputSource {
\r
665 char buf[INPUT_SOURCE_BUF_SIZE];
\r
669 InputCallback func;
\r
670 struct _InputSource *second; /* for stderr thread on CPRcmd */
\r
674 InputSource *consoleInputSource;
\r
679 VOID ConsoleOutput(char* data, int length, int forceVisible);
\r
680 VOID ConsoleCreate();
\r
682 ConsoleWndProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam);
\r
683 VOID ColorizeTextPopup(HWND hwnd, ColorClass cc);
\r
684 VOID PrintCommSettings(FILE *f, char *name, DCB *dcb);
\r
685 VOID ParseCommSettings(char *arg, DCB *dcb);
\r
687 StartupDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam);
\r
688 VOID APIENTRY MenuPopup(HWND hwnd, POINT pt, HMENU hmenu, UINT def);
\r
689 void ParseIcsTextMenu(char *icsTextMenuString);
\r
690 VOID PopUpNameDialog(char firstchar);
\r
691 VOID UpdateSampleText(HWND hDlg, int id, MyColorizeAttribs *mca);
\r
695 int GameListOptions();
\r
697 int dummy; // [HGM] for obsolete args
\r
699 HWND hwndMain = NULL; /* root window*/
\r
700 HWND hwndConsole = NULL;
\r
701 HWND commentDialog = NULL;
\r
702 HWND moveHistoryDialog = NULL;
\r
703 HWND evalGraphDialog = NULL;
\r
704 HWND engineOutputDialog = NULL;
\r
705 HWND gameListDialog = NULL;
\r
706 HWND editTagsDialog = NULL;
\r
708 int commentUp = FALSE;
\r
710 WindowPlacement wpMain;
\r
711 WindowPlacement wpConsole;
\r
712 WindowPlacement wpComment;
\r
713 WindowPlacement wpMoveHistory;
\r
714 WindowPlacement wpEvalGraph;
\r
715 WindowPlacement wpEngineOutput;
\r
716 WindowPlacement wpGameList;
\r
717 WindowPlacement wpTags;
\r
719 VOID EngineOptionsPopup(); // [HGM] settings
\r
721 VOID GothicPopUp(char *title, VariantClass variant);
\r
723 * Setting "frozen" should disable all user input other than deleting
\r
724 * the window. We do this while engines are initializing themselves.
\r
726 static int frozen = 0;
\r
727 static int oldMenuItemState[MENU_BAR_ITEMS];
\r
733 if (frozen) return;
\r
735 hmenu = GetMenu(hwndMain);
\r
736 for (i=0; i<MENU_BAR_ITEMS; i++) {
\r
737 oldMenuItemState[i] = EnableMenuItem(hmenu, i, MF_BYPOSITION|MF_GRAYED);
\r
739 DrawMenuBar(hwndMain);
\r
742 /* Undo a FreezeUI */
\r
748 if (!frozen) return;
\r
750 hmenu = GetMenu(hwndMain);
\r
751 for (i=0; i<MENU_BAR_ITEMS; i++) {
\r
752 EnableMenuItem(hmenu, i, MF_BYPOSITION|oldMenuItemState[i]);
\r
754 DrawMenuBar(hwndMain);
\r
757 /*static*/ int fromX = -1, fromY = -1, toX, toY; // [HGM] moved upstream, so JAWS can use them
\r
759 /* JAWS preparation patch (WinBoard for the sight impaired). Define required insertions as empty */
\r
765 #define JAWS_ALT_INTERCEPT
\r
766 #define JAWS_KBUP_NAVIGATION
\r
767 #define JAWS_KBDOWN_NAVIGATION
\r
768 #define JAWS_MENU_ITEMS
\r
769 #define JAWS_SILENCE
\r
770 #define JAWS_REPLAY
\r
772 #define JAWS_COPYRIGHT
\r
773 #define JAWS_DELETE(X) X
\r
774 #define SAYMACHINEMOVE()
\r
778 /*---------------------------------------------------------------------------*\
\r
782 \*---------------------------------------------------------------------------*/
\r
785 WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
\r
786 LPSTR lpCmdLine, int nCmdShow)
\r
789 HANDLE hAccelMain, hAccelNoAlt, hAccelNoICS;
\r
790 // INITCOMMONCONTROLSEX ex;
\r
794 LoadLibrary("RICHED32.DLL");
\r
795 consoleCF.cbSize = sizeof(CHARFORMAT);
\r
797 if (!InitApplication(hInstance)) {
\r
800 if (!InitInstance(hInstance, nCmdShow, lpCmdLine)) {
\r
807 // InitCommonControlsEx(&ex);
\r
808 InitCommonControls();
\r
810 hAccelMain = LoadAccelerators (hInstance, szAppName);
\r
811 hAccelNoAlt = LoadAccelerators (hInstance, "NO_ALT");
\r
812 hAccelNoICS = LoadAccelerators( hInstance, "NO_ICS"); /* [AS] No Ctrl-V on ICS!!! */
\r
814 /* Acquire and dispatch messages until a WM_QUIT message is received. */
\r
816 while (GetMessage(&msg, /* message structure */
\r
817 NULL, /* handle of window receiving the message */
\r
818 0, /* lowest message to examine */
\r
819 0)) /* highest message to examine */
\r
822 if(msg.message == WM_CHAR && msg.wParam == '\t') {
\r
823 // [HGM] navigate: switch between all windows with tab
\r
824 HWND e1 = NULL, e2 = NULL, mh = NULL, hInput = NULL, hText = NULL;
\r
825 int i, currentElement = 0;
\r
827 // first determine what element of the chain we come from (if any)
\r
828 if(appData.icsActive) {
\r
829 hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
830 hText = GetDlgItem(hwndConsole, OPT_ConsoleText);
\r
832 if(engineOutputDialog && EngineOutputIsUp()) {
\r
833 e1 = GetDlgItem(engineOutputDialog, IDC_EngineMemo1);
\r
834 e2 = GetDlgItem(engineOutputDialog, IDC_EngineMemo2);
\r
836 if(moveHistoryDialog && MoveHistoryIsUp()) {
\r
837 mh = GetDlgItem(moveHistoryDialog, IDC_MoveHistory);
\r
839 if(msg.hwnd == hwndMain) currentElement = 7 ; else
\r
840 if(msg.hwnd == engineOutputDialog) currentElement = 2; else
\r
841 if(msg.hwnd == e1) currentElement = 2; else
\r
842 if(msg.hwnd == e2) currentElement = 3; else
\r
843 if(msg.hwnd == moveHistoryDialog) currentElement = 4; else
\r
844 if(msg.hwnd == mh) currentElement = 4; else
\r
845 if(msg.hwnd == evalGraphDialog) currentElement = 6; else
\r
846 if(msg.hwnd == hText) currentElement = 5; else
\r
847 if(msg.hwnd == hInput) currentElement = 6; else
\r
848 for (i = 0; i < N_BUTTONS; i++) {
\r
849 if (buttonDesc[i].hwnd == msg.hwnd) { currentElement = 1; break; }
\r
852 // determine where to go to
\r
853 if(currentElement) { HWND h = NULL; int direction = GetKeyState(VK_SHIFT) < 0 ? -1 : 1;
\r
855 currentElement = (currentElement + direction) % 7;
\r
856 switch(currentElement) {
\r
858 h = hwndMain; break; // passing this case always makes the loop exit
\r
860 h = buttonDesc[0].hwnd; break; // could be NULL
\r
862 if(!EngineOutputIsUp()) continue; // skip closed auxiliary windows
\r
865 if(!EngineOutputIsUp()) continue;
\r
868 if(!MoveHistoryIsUp()) continue;
\r
870 // case 6: // input to eval graph does not seem to get here!
\r
871 // if(!EvalGraphIsUp()) continue;
\r
872 // h = evalGraphDialog; break;
\r
874 if(!appData.icsActive) continue;
\r
878 if(!appData.icsActive) continue;
\r
884 if(currentElement > 4 && IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);
\r
885 if(currentElement < 5 && IsIconic(hwndMain)) ShowWindow(hwndMain, SW_RESTORE); // all open together
\r
888 continue; // this message now has been processed
\r
892 if (!(commentDialog && IsDialogMessage(commentDialog, &msg)) &&
\r
893 !(moveHistoryDialog && IsDialogMessage(moveHistoryDialog, &msg)) &&
\r
894 !(evalGraphDialog && IsDialogMessage(evalGraphDialog, &msg)) &&
\r
895 !(engineOutputDialog && IsDialogMessage(engineOutputDialog, &msg)) &&
\r
896 !(editTagsDialog && IsDialogMessage(editTagsDialog, &msg)) &&
\r
897 !(gameListDialog && IsDialogMessage(gameListDialog, &msg)) &&
\r
898 !(errorDialog && IsDialogMessage(errorDialog, &msg)) &&
\r
899 !(!frozen && TranslateAccelerator(hwndMain, hAccelMain, &msg)) && JAWS_ACCEL
\r
900 !(!hwndConsole && TranslateAccelerator(hwndMain, hAccelNoICS, &msg)) &&
\r
901 !(!hwndConsole && TranslateAccelerator(hwndMain, hAccelNoAlt, &msg))) {
\r
902 int done = 0, i; // [HGM] chat: dispatch cat-box messages
\r
903 for(i=0; i<MAX_CHAT; i++)
\r
904 if(chatHandle[i] && IsDialogMessage(chatHandle[i], &msg)) {
\r
907 if(done) continue; // [HGM] chat: end patch
\r
908 TranslateMessage(&msg); /* Translates virtual key codes */
\r
909 DispatchMessage(&msg); /* Dispatches message to window */
\r
914 return (msg.wParam); /* Returns the value from PostQuitMessage */
\r
917 /*---------------------------------------------------------------------------*\
\r
919 * Initialization functions
\r
921 \*---------------------------------------------------------------------------*/
\r
925 { // update user logo if necessary
\r
926 static char oldUserName[MSG_SIZ], dir[MSG_SIZ], *curName;
\r
928 if(appData.autoLogo) {
\r
929 curName = UserName();
\r
930 if(strcmp(curName, oldUserName)) {
\r
931 GetCurrentDirectory(MSG_SIZ, dir);
\r
932 SetCurrentDirectory(installDir);
\r
933 snprintf(oldUserName, MSG_SIZ, "logos\\%s.bmp", curName);
\r
934 userLogo = LoadImage( 0, oldUserName, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );
\r
935 safeStrCpy(oldUserName, curName, sizeof(oldUserName)/sizeof(oldUserName[0]) );
\r
936 if(userLogo == NULL)
\r
937 userLogo = LoadImage( 0, "logos\\dummy.bmp", IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );
\r
938 SetCurrentDirectory(dir); /* return to prev directory */
\r
944 InitApplication(HINSTANCE hInstance)
\r
948 /* Fill in window class structure with parameters that describe the */
\r
951 wc.style = CS_HREDRAW | CS_VREDRAW; /* Class style(s). */
\r
952 wc.lpfnWndProc = (WNDPROC)WndProc; /* Window Procedure */
\r
953 wc.cbClsExtra = 0; /* No per-class extra data. */
\r
954 wc.cbWndExtra = 0; /* No per-window extra data. */
\r
955 wc.hInstance = hInstance; /* Owner of this class */
\r
956 wc.hIcon = LoadIcon(hInstance, "icon_white");
\r
957 wc.hCursor = LoadCursor(NULL, IDC_ARROW); /* Cursor */
\r
958 wc.hbrBackground = (HBRUSH)(COLOR_WINDOW+1); /* Default color */
\r
959 wc.lpszMenuName = szAppName; /* Menu name from .RC */
\r
960 wc.lpszClassName = szAppName; /* Name to register as */
\r
962 /* Register the window class and return success/failure code. */
\r
963 if (!RegisterClass(&wc)) return FALSE;
\r
965 wc.style = CS_HREDRAW | CS_VREDRAW;
\r
966 wc.lpfnWndProc = (WNDPROC)ConsoleWndProc;
\r
968 wc.cbWndExtra = DLGWINDOWEXTRA;
\r
969 wc.hInstance = hInstance;
\r
970 wc.hIcon = LoadIcon(hInstance, "icon_white");
\r
971 wc.hCursor = LoadCursor(NULL, IDC_ARROW);
\r
972 wc.hbrBackground = (HBRUSH)(COLOR_MENU+1);
\r
973 wc.lpszMenuName = NULL;
\r
974 wc.lpszClassName = szConsoleName;
\r
976 if (!RegisterClass(&wc)) return FALSE;
\r
981 /* Set by InitInstance, used by EnsureOnScreen */
\r
982 int screenHeight, screenWidth;
\r
985 EnsureOnScreen(int *x, int *y, int minX, int minY)
\r
987 // int gap = GetSystemMetrics(SM_CYFRAME) + GetSystemMetrics(SM_CYCAPTION);
\r
988 /* Be sure window at (x,y) is not off screen (or even mostly off screen) */
\r
989 if (*x > screenWidth - 32) *x = 0;
\r
990 if (*y > screenHeight - 32) *y = 0;
\r
991 if (*x < minX) *x = minX;
\r
992 if (*y < minY) *y = minY;
\r
996 LoadLogo(ChessProgramState *cps, int n, Boolean ics)
\r
998 char buf[MSG_SIZ], dir[MSG_SIZ];
\r
999 GetCurrentDirectory(MSG_SIZ, dir);
\r
1000 SetCurrentDirectory(installDir);
\r
1001 if( appData.logo[n] && appData.logo[n][0] != NULLCHAR) {
\r
1002 cps->programLogo = LoadImage( 0, appData.logo[n], IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );
\r
1004 if (cps->programLogo == NULL && appData.debugMode) {
\r
1005 fprintf( debugFP, "Unable to load logo bitmap '%s'\n", appData.logo[n] );
\r
1007 } else if(appData.autoLogo) {
\r
1008 if(ics) { // [HGM] logo: in ICS mode second can be used for ICS
\r
1009 char *opponent = "";
\r
1010 if(gameMode == IcsPlayingWhite) opponent = gameInfo.black;
\r
1011 if(gameMode == IcsPlayingBlack) opponent = gameInfo.white;
\r
1012 sprintf(buf, "logos\\%s\\%s.bmp", appData.icsHost, opponent);
\r
1013 if(!*opponent || !(cps->programLogo = LoadImage( 0, buf, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE ))) {
\r
1014 sprintf(buf, "logos\\%s.bmp", appData.icsHost);
\r
1015 cps->programLogo = LoadImage( 0, buf, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );
\r
1018 if(appData.directory[n] && appData.directory[n][0]) {
\r
1019 SetCurrentDirectory(appData.directory[n]);
\r
1020 cps->programLogo = LoadImage( 0, "logo.bmp", IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );
\r
1023 SetCurrentDirectory(dir); /* return to prev directory */
\r
1029 ZeroMemory( &backTextureSquareInfo, sizeof(backTextureSquareInfo) );
\r
1030 backTextureSquareSize = 0; // kludge to force recalculation of texturemode
\r
1032 if( appData.liteBackTextureFile && appData.liteBackTextureFile[0] != NULLCHAR && appData.liteBackTextureFile[0] != '*' ) {
\r
1033 liteBackTexture = LoadImage( 0, appData.liteBackTextureFile, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );
\r
1034 liteBackTextureMode = appData.liteBackTextureMode;
\r
1036 if (liteBackTexture == NULL && appData.debugMode) {
\r
1037 fprintf( debugFP, "Unable to load lite texture bitmap '%s'\n", appData.liteBackTextureFile );
\r
1041 if( appData.darkBackTextureFile && appData.darkBackTextureFile[0] != NULLCHAR && appData.darkBackTextureFile[0] != '*' ) {
\r
1042 darkBackTexture = LoadImage( 0, appData.darkBackTextureFile, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );
\r
1043 darkBackTextureMode = appData.darkBackTextureMode;
\r
1045 if (darkBackTexture == NULL && appData.debugMode) {
\r
1046 fprintf( debugFP, "Unable to load dark texture bitmap '%s'\n", appData.darkBackTextureFile );
\r
1052 InitInstance(HINSTANCE hInstance, int nCmdShow, LPSTR lpCmdLine)
\r
1054 HWND hwnd; /* Main window handle. */
\r
1056 WINDOWPLACEMENT wp;
\r
1059 hInst = hInstance; /* Store instance handle in our global variable */
\r
1060 programName = szAppName;
\r
1062 if (SearchPath(NULL, "WinBoard.exe", NULL, MSG_SIZ, installDir, &filepart)) {
\r
1063 *filepart = NULLCHAR;
\r
1065 GetCurrentDirectory(MSG_SIZ, installDir);
\r
1067 gameInfo.boardWidth = gameInfo.boardHeight = 8; // [HGM] won't have open window otherwise
\r
1068 screenWidth = screenHeight = 1000; // [HGM] placement: kludge to allow calling EnsureOnScreen from InitAppData
\r
1069 InitAppData(lpCmdLine); /* Get run-time parameters */
\r
1070 /* xboard, and older WinBoards, controlled the move sound with the
\r
1071 appData.ringBellAfterMoves option. In the current WinBoard, we
\r
1072 always turn the option on (so that the backend will call us),
\r
1073 then let the user turn the sound off by setting it to silence if
\r
1074 desired. To accommodate old winboard.ini files saved by old
\r
1075 versions of WinBoard, we also turn off the sound if the option
\r
1076 was initially set to false. [HGM] taken out of InitAppData */
\r
1077 if (!appData.ringBellAfterMoves) {
\r
1078 sounds[(int)SoundMove].name = strdup("");
\r
1079 appData.ringBellAfterMoves = TRUE;
\r
1081 if (appData.debugMode) {
\r
1082 debugFP = fopen(appData.nameOfDebugFile, "w");
\r
1083 setbuf(debugFP, NULL);
\r
1086 LoadLanguageFile(appData.language);
\r
1090 // InitEngineUCI( installDir, &first ); // [HGM] incorporated in InitBackEnd1()
\r
1091 // InitEngineUCI( installDir, &second );
\r
1093 /* Create a main window for this application instance. */
\r
1094 hwnd = CreateWindow(szAppName, szTitle,
\r
1095 (WS_OVERLAPPEDWINDOW & ~WS_MAXIMIZEBOX),
\r
1096 CW_USEDEFAULT, 0, CW_USEDEFAULT, 0,
\r
1097 NULL, NULL, hInstance, NULL);
\r
1100 /* If window could not be created, return "failure" */
\r
1105 /* [HGM] logo: Load logos if specified (must be done before InitDrawingSizes) */
\r
1106 LoadLogo(&first, 0, FALSE);
\r
1107 LoadLogo(&second, 1, appData.icsActive);
\r
1111 iconWhite = LoadIcon(hInstance, "icon_white");
\r
1112 iconBlack = LoadIcon(hInstance, "icon_black");
\r
1113 iconCurrent = iconWhite;
\r
1114 InitDrawingColors();
\r
1115 screenHeight = GetSystemMetrics(SM_CYSCREEN);
\r
1116 screenWidth = GetSystemMetrics(SM_CXSCREEN);
\r
1117 for (ibs = (int) NUM_SIZES - 1; ibs >= 0; ibs--) {
\r
1118 /* Compute window size for each board size, and use the largest
\r
1119 size that fits on this screen as the default. */
\r
1120 InitDrawingSizes((BoardSize)(ibs+1000), 0);
\r
1121 if (boardSize == (BoardSize)-1 &&
\r
1122 winH <= screenHeight
\r
1123 - GetSystemMetrics(SM_CYFRAME) - GetSystemMetrics(SM_CYCAPTION) - 10
\r
1124 && winW <= screenWidth) {
\r
1125 boardSize = (BoardSize)ibs;
\r
1129 InitDrawingSizes(boardSize, 0);
\r
1130 RecentEngineMenu(appData.recentEngineList);
\r
1132 buttonCount = GetSystemMetrics(SM_CMOUSEBUTTONS);
\r
1134 /* [AS] Load textures if specified */
\r
1137 mysrandom( (unsigned) time(NULL) );
\r
1139 /* [AS] Restore layout */
\r
1140 if( wpMoveHistory.visible ) {
\r
1141 MoveHistoryPopUp();
\r
1144 if( wpEvalGraph.visible ) {
\r
1148 if( wpEngineOutput.visible ) {
\r
1149 EngineOutputPopUp();
\r
1152 /* Make the window visible; update its client area; and return "success" */
\r
1153 EnsureOnScreen(&wpMain.x, &wpMain.y, minX, minY);
\r
1154 wp.length = sizeof(WINDOWPLACEMENT);
\r
1156 wp.showCmd = nCmdShow;
\r
1157 wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;
\r
1158 wp.rcNormalPosition.left = wpMain.x;
\r
1159 wp.rcNormalPosition.right = wpMain.x + wpMain.width;
\r
1160 wp.rcNormalPosition.top = wpMain.y;
\r
1161 wp.rcNormalPosition.bottom = wpMain.y + wpMain.height;
\r
1162 SetWindowPlacement(hwndMain, &wp);
\r
1164 InitBackEnd2(); // [HGM] moved until after all windows placed, to save correct position if fatal error on engine start
\r
1166 if(!appData.noGUI) SetWindowPos(hwndMain, alwaysOnTop ? HWND_TOPMOST : HWND_NOTOPMOST,
\r
1167 0, 0, 0, 0, SWP_NOMOVE|SWP_NOSIZE);
\r
1169 if (hwndConsole) {
\r
1171 SetWindowPos(hwndConsole, alwaysOnTop ? HWND_TOPMOST : HWND_NOTOPMOST,
\r
1172 0, 0, 0, 0, SWP_NOMOVE|SWP_NOSIZE);
\r
1174 ShowWindow(hwndConsole, nCmdShow);
\r
1175 SetActiveWindow(hwndConsole);
\r
1177 if(!appData.noGUI) UpdateWindow(hwnd); else ShowWindow(hwnd, SW_MINIMIZE);
\r
1178 if(gameListDialog) SetFocus(gameListDialog); // [HGM] jaws: for if we clicked multi-game game file
\r
1187 HMENU hmenu = GetMenu(hwndMain);
\r
1189 (void) EnableMenuItem(hmenu, IDM_CommPort,
\r
1190 MF_BYCOMMAND|((appData.icsActive &&
\r
1191 *appData.icsCommPort != NULLCHAR) ?
\r
1192 MF_ENABLED : MF_GRAYED));
\r
1193 (void) CheckMenuItem(hmenu, IDM_SaveSettingsOnExit,
\r
1194 MF_BYCOMMAND|(saveSettingsOnExit ?
\r
1195 MF_CHECKED : MF_UNCHECKED));
\r
1198 //---------------------------------------------------------------------------------------------------------
\r
1200 #define ICS_TEXT_MENU_SIZE (IDM_CommandXLast - IDM_CommandX + 1)
\r
1201 #define XBOARD FALSE
\r
1203 #define OPTCHAR "/"
\r
1204 #define SEPCHAR "="
\r
1205 #define TOPLEVEL 0
\r
1209 // front-end part of option handling
\r
1212 LFfromMFP(LOGFONT* lf, MyFontParams *mfp)
\r
1214 HDC hdc = CreateDC("DISPLAY", NULL, NULL, NULL);
\r
1215 lf->lfHeight = -(int)(mfp->pointSize * GetDeviceCaps(hdc, LOGPIXELSY) / 72.0 + 0.5);
\r
1218 lf->lfEscapement = 0;
\r
1219 lf->lfOrientation = 0;
\r
1220 lf->lfWeight = mfp->bold ? FW_BOLD : FW_NORMAL;
\r
1221 lf->lfItalic = mfp->italic;
\r
1222 lf->lfUnderline = mfp->underline;
\r
1223 lf->lfStrikeOut = mfp->strikeout;
\r
1224 lf->lfCharSet = mfp->charset;
\r
1225 lf->lfOutPrecision = OUT_DEFAULT_PRECIS;
\r
1226 lf->lfClipPrecision = CLIP_DEFAULT_PRECIS;
\r
1227 lf->lfQuality = DEFAULT_QUALITY;
\r
1228 lf->lfPitchAndFamily = DEFAULT_PITCH|FF_DONTCARE;
\r
1229 safeStrCpy(lf->lfFaceName, mfp->faceName, sizeof(lf->lfFaceName)/sizeof(lf->lfFaceName[0]) );
\r
1233 CreateFontInMF(MyFont *mf)
\r
1235 LFfromMFP(&mf->lf, &mf->mfp);
\r
1236 if (mf->hf) DeleteObject(mf->hf);
\r
1237 mf->hf = CreateFontIndirect(&mf->lf);
\r
1240 // [HGM] This platform-dependent table provides the location for storing the color info
\r
1242 colorVariable[] = {
\r
1243 &whitePieceColor,
\r
1244 &blackPieceColor,
\r
1245 &lightSquareColor,
\r
1246 &darkSquareColor,
\r
1247 &highlightSquareColor,
\r
1248 &premoveHighlightColor,
\r
1250 &consoleBackgroundColor,
\r
1251 &appData.fontForeColorWhite,
\r
1252 &appData.fontBackColorWhite,
\r
1253 &appData.fontForeColorBlack,
\r
1254 &appData.fontBackColorBlack,
\r
1255 &appData.evalHistColorWhite,
\r
1256 &appData.evalHistColorBlack,
\r
1257 &appData.highlightArrowColor,
\r
1260 /* Command line font name parser. NULL name means do nothing.
\r
1261 Syntax like "Courier New:10.0 bi" or "Arial:10" or "Arial:10b"
\r
1262 For backward compatibility, syntax without the colon is also
\r
1263 accepted, but font names with digits in them won't work in that case.
\r
1266 ParseFontName(char *name, MyFontParams *mfp)
\r
1269 if (name == NULL) return;
\r
1271 q = strchr(p, ':');
\r
1273 if (q - p >= sizeof(mfp->faceName))
\r
1274 ExitArgError(_("Font name too long:"), name, TRUE);
\r
1275 memcpy(mfp->faceName, p, q - p);
\r
1276 mfp->faceName[q - p] = NULLCHAR;
\r
1279 q = mfp->faceName;
\r
1280 while (*p && !isdigit(*p)) {
\r
1282 if (q - mfp->faceName >= sizeof(mfp->faceName))
\r
1283 ExitArgError(_("Font name too long:"), name, TRUE);
\r
1285 while (q > mfp->faceName && q[-1] == ' ') q--;
\r
1288 if (!*p) ExitArgError(_("Font point size missing:"), name, TRUE);
\r
1289 mfp->pointSize = (float) atof(p);
\r
1290 mfp->bold = (strchr(p, 'b') != NULL);
\r
1291 mfp->italic = (strchr(p, 'i') != NULL);
\r
1292 mfp->underline = (strchr(p, 'u') != NULL);
\r
1293 mfp->strikeout = (strchr(p, 's') != NULL);
\r
1294 mfp->charset = DEFAULT_CHARSET;
\r
1295 q = strchr(p, 'c');
\r
1297 mfp->charset = (BYTE) atoi(q+1);
\r
1301 ParseFont(char *name, int number)
\r
1302 { // wrapper to shield back-end from 'font'
\r
1303 ParseFontName(name, &font[boardSize][number]->mfp);
\r
1308 { // in WB we have a 2D array of fonts; this initializes their description
\r
1310 /* Point font array elements to structures and
\r
1311 parse default font names */
\r
1312 for (i=0; i<NUM_FONTS; i++) {
\r
1313 for (j=0; j<NUM_SIZES; j++) {
\r
1314 font[j][i] = &fontRec[j][i];
\r
1315 ParseFontName(font[j][i]->def, &font[j][i]->mfp);
\r
1322 { // here we create the actual fonts from the selected descriptions
\r
1324 for (i=0; i<NUM_FONTS; i++) {
\r
1325 for (j=0; j<NUM_SIZES; j++) {
\r
1326 CreateFontInMF(font[j][i]);
\r
1330 /* Color name parser.
\r
1331 X version accepts X color names, but this one
\r
1332 handles only the #rrggbb form (hex) or rrr,ggg,bbb (decimal) */
\r
1334 ParseColorName(char *name)
\r
1336 int red, green, blue, count;
\r
1337 char buf[MSG_SIZ];
\r
1339 count = sscanf(name, "#%2x%2x%2x", &red, &green, &blue);
\r
1341 count = sscanf(name, "%3d%*[^0-9]%3d%*[^0-9]%3d",
\r
1342 &red, &green, &blue);
\r
1345 snprintf(buf, MSG_SIZ, _("Can't parse color name %s"), name);
\r
1346 DisplayError(buf, 0);
\r
1347 return RGB(0, 0, 0);
\r
1349 return PALETTERGB(red, green, blue);
\r
1353 ParseColor(int n, char *name)
\r
1354 { // for WinBoard the color is an int, which needs to be derived from the string
\r
1355 if(colorVariable[n]) *(int*)colorVariable[n] = ParseColorName(name);
\r
1359 ParseAttribs(COLORREF *color, int *effects, char* argValue)
\r
1361 char *e = argValue;
\r
1365 if (*e == 'b') eff |= CFE_BOLD;
\r
1366 else if (*e == 'i') eff |= CFE_ITALIC;
\r
1367 else if (*e == 'u') eff |= CFE_UNDERLINE;
\r
1368 else if (*e == 's') eff |= CFE_STRIKEOUT;
\r
1369 else if (*e == '#' || isdigit(*e)) break;
\r
1373 *color = ParseColorName(e);
\r
1377 ParseTextAttribs(ColorClass cc, char *s)
\r
1378 { // [HGM] front-end wrapper that does the platform-dependent call
\r
1379 // for XBoard we would set (&appData.colorShout)[cc] = strdup(s);
\r
1380 ParseAttribs(&textAttribs[cc].color, &textAttribs[cc].effects, s);
\r
1384 ParseBoardSize(void *addr, char *name)
\r
1385 { // [HGM] rewritten with return-value ptr to shield back-end from BoardSize
\r
1386 BoardSize bs = SizeTiny;
\r
1387 while (sizeInfo[bs].name != NULL) {
\r
1388 if (StrCaseCmp(name, sizeInfo[bs].name) == 0) {
\r
1389 *(BoardSize *)addr = bs;
\r
1394 ExitArgError(_("Unrecognized board size value"), name, TRUE);
\r
1399 { // [HGM] import name from appData first
\r
1402 for (cc = (ColorClass)0; cc < ColorNormal; cc++) {
\r
1403 textAttribs[cc].sound.name = strdup((&appData.soundShout)[cc]);
\r
1404 textAttribs[cc].sound.data = NULL;
\r
1405 MyLoadSound(&textAttribs[cc].sound);
\r
1407 for (cc = ColorNormal; cc < NColorClasses; cc++) {
\r
1408 textAttribs[cc].sound.name = strdup("");
\r
1409 textAttribs[cc].sound.data = NULL;
\r
1411 for (sc = (SoundClass)0; sc < NSoundClasses; sc++) {
\r
1412 sounds[sc].name = strdup((&appData.soundMove)[sc]);
\r
1413 sounds[sc].data = NULL;
\r
1414 MyLoadSound(&sounds[sc]);
\r
1419 SetCommPortDefaults()
\r
1421 memset(&dcb, 0, sizeof(DCB)); // required by VS 2002 +
\r
1422 dcb.DCBlength = sizeof(DCB);
\r
1423 dcb.BaudRate = 9600;
\r
1424 dcb.fBinary = TRUE;
\r
1425 dcb.fParity = FALSE;
\r
1426 dcb.fOutxCtsFlow = FALSE;
\r
1427 dcb.fOutxDsrFlow = FALSE;
\r
1428 dcb.fDtrControl = DTR_CONTROL_ENABLE;
\r
1429 dcb.fDsrSensitivity = FALSE;
\r
1430 dcb.fTXContinueOnXoff = TRUE;
\r
1431 dcb.fOutX = FALSE;
\r
1433 dcb.fNull = FALSE;
\r
1434 dcb.fRtsControl = RTS_CONTROL_ENABLE;
\r
1435 dcb.fAbortOnError = FALSE;
\r
1437 dcb.Parity = SPACEPARITY;
\r
1438 dcb.StopBits = ONESTOPBIT;
\r
1441 // [HGM] args: these three cases taken out to stay in front-end
\r
1443 SaveFontArg(FILE *f, ArgDescriptor *ad)
\r
1444 { // in WinBoard every board size has its own font, and the "argLoc" identifies the table,
\r
1445 // while the curent board size determines the element. This system should be ported to XBoard.
\r
1446 // What the table contains pointers to, and how to print the font description, remains platform-dependent
\r
1448 for (bs=0; bs<NUM_SIZES; bs++) {
\r
1449 MyFontParams *mfp = &font[bs][(int) ad->argLoc]->mfp;
\r
1450 fprintf(f, "/size=%s ", sizeInfo[bs].name);
\r
1451 fprintf(f, "/%s=\"%s:%g%s%s%s%s%sc%d\"\n",
\r
1452 ad->argName, mfp->faceName, mfp->pointSize,
\r
1453 mfp->bold || mfp->italic || mfp->underline || mfp->strikeout ? " " : "",
\r
1454 mfp->bold ? "b" : "",
\r
1455 mfp->italic ? "i" : "",
\r
1456 mfp->underline ? "u" : "",
\r
1457 mfp->strikeout ? "s" : "",
\r
1458 (int)mfp->charset);
\r
1464 { // [HGM] copy the names from the internal WB variables to appData
\r
1467 for (cc = (ColorClass)0; cc < ColorNormal; cc++)
\r
1468 (&appData.soundShout)[cc] = textAttribs[cc].sound.name;
\r
1469 for (sc = (SoundClass)0; sc < NSoundClasses; sc++)
\r
1470 (&appData.soundMove)[sc] = sounds[sc].name;
\r
1474 SaveAttribsArg(FILE *f, ArgDescriptor *ad)
\r
1475 { // here the "argLoc" defines a table index. It could have contained the 'ta' pointer itself, though
\r
1476 MyTextAttribs* ta = &textAttribs[(ColorClass)ad->argLoc];
\r
1477 fprintf(f, "/%s=\"%s%s%s%s%s#%02lx%02lx%02lx\"\n", ad->argName,
\r
1478 (ta->effects & CFE_BOLD) ? "b" : "",
\r
1479 (ta->effects & CFE_ITALIC) ? "i" : "",
\r
1480 (ta->effects & CFE_UNDERLINE) ? "u" : "",
\r
1481 (ta->effects & CFE_STRIKEOUT) ? "s" : "",
\r
1482 (ta->effects) ? " " : "",
\r
1483 ta->color&0xff, (ta->color >> 8)&0xff, (ta->color >> 16)&0xff);
\r
1487 SaveColor(FILE *f, ArgDescriptor *ad)
\r
1488 { // in WinBoard the color is an int and has to be converted to text. In X it would be a string already?
\r
1489 COLORREF color = *(COLORREF *)colorVariable[(int)ad->argLoc];
\r
1490 fprintf(f, "/%s=#%02lx%02lx%02lx\n", ad->argName,
\r
1491 color&0xff, (color>>8)&0xff, (color>>16)&0xff);
\r
1495 SaveBoardSize(FILE *f, char *name, void *addr)
\r
1496 { // wrapper to shield back-end from BoardSize & sizeInfo
\r
1497 fprintf(f, "/%s=%s\n", name, sizeInfo[*(BoardSize *)addr].name);
\r
1501 ParseCommPortSettings(char *s)
\r
1502 { // wrapper to keep dcb from back-end
\r
1503 ParseCommSettings(s, &dcb);
\r
1508 { // wrapper to shield use of window handles from back-end (make addressible by number?)
\r
1509 GetActualPlacement(hwndMain, &wpMain);
\r
1510 GetActualPlacement(hwndConsole, &wpConsole);
\r
1511 GetActualPlacement(commentDialog, &wpComment);
\r
1512 GetActualPlacement(editTagsDialog, &wpTags);
\r
1513 GetActualPlacement(gameListDialog, &wpGameList);
\r
1514 GetActualPlacement(moveHistoryDialog, &wpMoveHistory);
\r
1515 GetActualPlacement(evalGraphDialog, &wpEvalGraph);
\r
1516 GetActualPlacement(engineOutputDialog, &wpEngineOutput);
\r
1520 PrintCommPortSettings(FILE *f, char *name)
\r
1521 { // wrapper to shield back-end from DCB
\r
1522 PrintCommSettings(f, name, &dcb);
\r
1526 MySearchPath(char *installDir, char *name, char *fullname)
\r
1528 char *dummy, buf[MSG_SIZ], *p = name, *q;
\r
1529 if(name[0]== '%') {
\r
1530 fullname[0] = 0; // [HGM] first expand any environment variables in the given name
\r
1531 while(*p == '%' && (q = strchr(p+1, '%'))) { // [HGM] recognize %*% as environment variable
\r
1532 safeStrCpy(buf, p+1, sizeof(buf)/sizeof(buf[0]) );
\r
1533 *strchr(buf, '%') = 0;
\r
1534 strcat(fullname, getenv(buf));
\r
1535 p = q+1; while(*p == '\\') { strcat(fullname, "\\"); p++; }
\r
1537 strcat(fullname, p); // after environment variables (if any), take the remainder of the given name
\r
1538 if(appData.debugMode) fprintf(debugFP, "name = '%s', expanded name = '%s'\n", name, fullname);
\r
1539 return (int) strlen(fullname);
\r
1541 return (int) SearchPath(installDir, name, NULL, MSG_SIZ, fullname, &dummy);
\r
1545 MyGetFullPathName(char *name, char *fullname)
\r
1548 return (int) GetFullPathName(name, MSG_SIZ, fullname, &dummy);
\r
1553 { // [HGM] args: allows testing if main window is realized from back-end
\r
1554 return hwndMain != NULL;
\r
1558 PopUpStartupDialog()
\r
1562 LoadLanguageFile(appData.language);
\r
1563 lpProc = MakeProcInstance((FARPROC)StartupDialog, hInst);
\r
1564 DialogBox(hInst, MAKEINTRESOURCE(DLG_Startup), NULL, (DLGPROC)lpProc);
\r
1565 FreeProcInstance(lpProc);
\r
1568 /*---------------------------------------------------------------------------*\
\r
1570 * GDI board drawing routines
\r
1572 \*---------------------------------------------------------------------------*/
\r
1574 /* [AS] Draw square using background texture */
\r
1575 static void DrawTile( int dx, int dy, int dw, int dh, HDC dst, HDC src, int mode, int sx, int sy )
\r
1580 return; /* Should never happen! */
\r
1583 SetGraphicsMode( dst, GM_ADVANCED );
\r
1590 /* X reflection */
\r
1595 x.eDx = (FLOAT) dw + dx - 1;
\r
1598 SetWorldTransform( dst, &x );
\r
1601 /* Y reflection */
\r
1607 x.eDy = (FLOAT) dh + dy - 1;
\r
1609 SetWorldTransform( dst, &x );
\r
1617 x.eDx = (FLOAT) dx;
\r
1618 x.eDy = (FLOAT) dy;
\r
1621 SetWorldTransform( dst, &x );
\r
1625 BitBlt( dst, dx, dy, dw, dh, src, sx, sy, SRCCOPY );
\r
1633 SetWorldTransform( dst, &x );
\r
1635 ModifyWorldTransform( dst, 0, MWT_IDENTITY );
\r
1638 /* [AS] [HGM] Make room for more piece types, so all pieces can be different */
\r
1640 PM_WP = (int) WhitePawn,
\r
1641 PM_WN = (int) WhiteKnight,
\r
1642 PM_WB = (int) WhiteBishop,
\r
1643 PM_WR = (int) WhiteRook,
\r
1644 PM_WQ = (int) WhiteQueen,
\r
1645 PM_WF = (int) WhiteFerz,
\r
1646 PM_WW = (int) WhiteWazir,
\r
1647 PM_WE = (int) WhiteAlfil,
\r
1648 PM_WM = (int) WhiteMan,
\r
1649 PM_WO = (int) WhiteCannon,
\r
1650 PM_WU = (int) WhiteUnicorn,
\r
1651 PM_WH = (int) WhiteNightrider,
\r
1652 PM_WA = (int) WhiteAngel,
\r
1653 PM_WC = (int) WhiteMarshall,
\r
1654 PM_WAB = (int) WhiteCardinal,
\r
1655 PM_WD = (int) WhiteDragon,
\r
1656 PM_WL = (int) WhiteLance,
\r
1657 PM_WS = (int) WhiteCobra,
\r
1658 PM_WV = (int) WhiteFalcon,
\r
1659 PM_WSG = (int) WhiteSilver,
\r
1660 PM_WG = (int) WhiteGrasshopper,
\r
1661 PM_WK = (int) WhiteKing,
\r
1662 PM_BP = (int) BlackPawn,
\r
1663 PM_BN = (int) BlackKnight,
\r
1664 PM_BB = (int) BlackBishop,
\r
1665 PM_BR = (int) BlackRook,
\r
1666 PM_BQ = (int) BlackQueen,
\r
1667 PM_BF = (int) BlackFerz,
\r
1668 PM_BW = (int) BlackWazir,
\r
1669 PM_BE = (int) BlackAlfil,
\r
1670 PM_BM = (int) BlackMan,
\r
1671 PM_BO = (int) BlackCannon,
\r
1672 PM_BU = (int) BlackUnicorn,
\r
1673 PM_BH = (int) BlackNightrider,
\r
1674 PM_BA = (int) BlackAngel,
\r
1675 PM_BC = (int) BlackMarshall,
\r
1676 PM_BG = (int) BlackGrasshopper,
\r
1677 PM_BAB = (int) BlackCardinal,
\r
1678 PM_BD = (int) BlackDragon,
\r
1679 PM_BL = (int) BlackLance,
\r
1680 PM_BS = (int) BlackCobra,
\r
1681 PM_BV = (int) BlackFalcon,
\r
1682 PM_BSG = (int) BlackSilver,
\r
1683 PM_BK = (int) BlackKing
\r
1686 static HFONT hPieceFont = NULL;
\r
1687 static HBITMAP hPieceMask[(int) EmptySquare];
\r
1688 static HBITMAP hPieceFace[(int) EmptySquare];
\r
1689 static int fontBitmapSquareSize = 0;
\r
1690 static char pieceToFontChar[(int) EmptySquare] =
\r
1691 { 'p', 'n', 'b', 'r', 'q',
\r
1692 'n', 'b', 'p', 'n', 'b', 'r', 'b', 'r', 'q', 'k',
\r
1693 'k', 'o', 'm', 'v', 't', 'w',
\r
1694 'v', 't', 'o', 'm', 'v', 't', 'v', 't', 'w', 'l',
\r
1697 extern BOOL SetCharTable( char *table, const char * map );
\r
1698 /* [HGM] moved to backend.c */
\r
1700 static void SetPieceBackground( HDC hdc, COLORREF color, int mode )
\r
1703 BYTE r1 = GetRValue( color );
\r
1704 BYTE g1 = GetGValue( color );
\r
1705 BYTE b1 = GetBValue( color );
\r
1711 /* Create a uniform background first */
\r
1712 hbrush = CreateSolidBrush( color );
\r
1713 SetRect( &rc, 0, 0, squareSize, squareSize );
\r
1714 FillRect( hdc, &rc, hbrush );
\r
1715 DeleteObject( hbrush );
\r
1718 /* Vertical gradient, good for pawn, knight and rook, less for queen and king */
\r
1719 int steps = squareSize / 2;
\r
1722 for( i=0; i<steps; i++ ) {
\r
1723 BYTE r = r1 - (r1-r2) * i / steps;
\r
1724 BYTE g = g1 - (g1-g2) * i / steps;
\r
1725 BYTE b = b1 - (b1-b2) * i / steps;
\r
1727 hbrush = CreateSolidBrush( RGB(r,g,b) );
\r
1728 SetRect( &rc, i + squareSize - steps, 0, i + squareSize - steps + 1, squareSize );
\r
1729 FillRect( hdc, &rc, hbrush );
\r
1730 DeleteObject(hbrush);
\r
1733 else if( mode == 2 ) {
\r
1734 /* Diagonal gradient, good more or less for every piece */
\r
1735 POINT triangle[3];
\r
1736 HPEN hpen = SelectObject( hdc, GetStockObject(NULL_PEN) );
\r
1737 HBRUSH hbrush_old;
\r
1738 int steps = squareSize;
\r
1741 triangle[0].x = squareSize - steps;
\r
1742 triangle[0].y = squareSize;
\r
1743 triangle[1].x = squareSize;
\r
1744 triangle[1].y = squareSize;
\r
1745 triangle[2].x = squareSize;
\r
1746 triangle[2].y = squareSize - steps;
\r
1748 for( i=0; i<steps; i++ ) {
\r
1749 BYTE r = r1 - (r1-r2) * i / steps;
\r
1750 BYTE g = g1 - (g1-g2) * i / steps;
\r
1751 BYTE b = b1 - (b1-b2) * i / steps;
\r
1753 hbrush = CreateSolidBrush( RGB(r,g,b) );
\r
1754 hbrush_old = SelectObject( hdc, hbrush );
\r
1755 Polygon( hdc, triangle, 3 );
\r
1756 SelectObject( hdc, hbrush_old );
\r
1757 DeleteObject(hbrush);
\r
1762 SelectObject( hdc, hpen );
\r
1767 [AS] The method I use to create the bitmaps it a bit tricky, but it
\r
1768 seems to work ok. The main problem here is to find the "inside" of a chess
\r
1769 piece: follow the steps as explained below.
\r
1771 static void CreatePieceMaskFromFont( HDC hdc_window, HDC hdc, int index )
\r
1775 COLORREF chroma = RGB(0xFF,0x00,0xFF);
\r
1779 int backColor = whitePieceColor;
\r
1780 int foreColor = blackPieceColor;
\r
1782 if( index < (int)BlackPawn && appData.fontBackColorWhite != appData.fontForeColorWhite ) {
\r
1783 backColor = appData.fontBackColorWhite;
\r
1784 foreColor = appData.fontForeColorWhite;
\r
1786 else if( index >= (int)BlackPawn && appData.fontBackColorBlack != appData.fontForeColorBlack ) {
\r
1787 backColor = appData.fontBackColorBlack;
\r
1788 foreColor = appData.fontForeColorBlack;
\r
1792 hbm = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );
\r
1794 hbm_old = SelectObject( hdc, hbm );
\r
1798 rc.right = squareSize;
\r
1799 rc.bottom = squareSize;
\r
1801 /* Step 1: background is now black */
\r
1802 FillRect( hdc, &rc, GetStockObject(BLACK_BRUSH) );
\r
1804 GetTextExtentPoint32( hdc, &pieceToFontChar[index], 1, &sz );
\r
1806 pt.x = (squareSize - sz.cx) / 2;
\r
1807 pt.y = (squareSize - sz.cy) / 2;
\r
1809 SetBkMode( hdc, TRANSPARENT );
\r
1810 SetTextColor( hdc, chroma );
\r
1811 /* Step 2: the piece has been drawn in purple, there are now black and purple in this bitmap */
\r
1812 TextOut( hdc, pt.x, pt.y, &pieceToFontChar[appData.allWhite && index >= (int)BlackPawn ? index - (int)BlackPawn : index], 1 );
\r
1814 SelectObject( hdc, GetStockObject(WHITE_BRUSH) );
\r
1815 /* Step 3: the area outside the piece is filled with white */
\r
1816 // FloodFill( hdc, 0, 0, chroma );
\r
1817 ExtFloodFill( hdc, 0, 0, 0, FLOODFILLSURFACE );
\r
1818 ExtFloodFill( hdc, 0, squareSize-1, 0, FLOODFILLSURFACE ); // [HGM] fill from all 4 corners, for if piece too big
\r
1819 ExtFloodFill( hdc, squareSize-1, 0, 0, FLOODFILLSURFACE );
\r
1820 ExtFloodFill( hdc, squareSize-1, squareSize-1, 0, FLOODFILLSURFACE );
\r
1821 SelectObject( hdc, GetStockObject(BLACK_BRUSH) );
\r
1823 Step 4: this is the tricky part, the area inside the piece is filled with black,
\r
1824 but if the start point is not inside the piece we're lost!
\r
1825 There should be a better way to do this... if we could create a region or path
\r
1826 from the fill operation we would be fine for example.
\r
1828 // FloodFill( hdc, squareSize / 2, squareSize / 2, RGB(0xFF,0xFF,0xFF) );
\r
1829 ExtFloodFill( hdc, squareSize / 2, squareSize / 2, RGB(0xFF,0xFF,0xFF), FLOODFILLBORDER );
\r
1831 { /* [HGM] shave off edges of mask, in an attempt to correct for the fact that FloodFill does not work correctly under Win XP */
\r
1832 HDC dc2 = CreateCompatibleDC( hdc_window );
\r
1833 HBITMAP bm2 = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );
\r
1835 SelectObject( dc2, bm2 );
\r
1836 BitBlt( dc2, 0, 0, squareSize, squareSize, hdc, 0, 0, SRCCOPY ); // make copy
\r
1837 BitBlt( hdc, 0, 1, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );
\r
1838 BitBlt( hdc, 2, 1, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );
\r
1839 BitBlt( hdc, 1, 0, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );
\r
1840 BitBlt( hdc, 1, 2, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );
\r
1843 DeleteObject( bm2 );
\r
1846 SetTextColor( hdc, 0 );
\r
1848 Step 5: some fonts have "disconnected" areas that are skipped by the fill:
\r
1849 draw the piece again in black for safety.
\r
1851 TextOut( hdc, pt.x, pt.y, &pieceToFontChar[appData.allWhite && index >= (int)BlackPawn ? index - (int)BlackPawn : index], 1 );
\r
1853 SelectObject( hdc, hbm_old );
\r
1855 if( hPieceMask[index] != NULL ) {
\r
1856 DeleteObject( hPieceMask[index] );
\r
1859 hPieceMask[index] = hbm;
\r
1862 hbm = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );
\r
1864 SelectObject( hdc, hbm );
\r
1867 HDC dc1 = CreateCompatibleDC( hdc_window );
\r
1868 HDC dc2 = CreateCompatibleDC( hdc_window );
\r
1869 HBITMAP bm2 = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );
\r
1871 SelectObject( dc1, hPieceMask[index] );
\r
1872 SelectObject( dc2, bm2 );
\r
1873 FillRect( dc2, &rc, GetStockObject(WHITE_BRUSH) );
\r
1874 BitBlt( dc2, 0, 0, squareSize, squareSize, dc1, 0, 0, SRCINVERT );
\r
1877 Now dc2 contains the inverse of the piece mask, i.e. a mask that preserves
\r
1878 the piece background and deletes (makes transparent) the rest.
\r
1879 Thanks to that mask, we are free to paint the background with the greates
\r
1880 freedom, as we'll be able to mask off the unwanted parts when finished.
\r
1881 We use this, to make gradients and give the pieces a "roundish" look.
\r
1883 SetPieceBackground( hdc, backColor, 2 );
\r
1884 BitBlt( hdc, 0, 0, squareSize, squareSize, dc2, 0, 0, SRCAND );
\r
1888 DeleteObject( bm2 );
\r
1891 SetTextColor( hdc, foreColor );
\r
1892 TextOut( hdc, pt.x, pt.y, &pieceToFontChar[appData.allWhite && index >= (int)BlackPawn ? index - (int)BlackPawn : index], 1 );
\r
1894 SelectObject( hdc, hbm_old );
\r
1896 if( hPieceFace[index] != NULL ) {
\r
1897 DeleteObject( hPieceFace[index] );
\r
1900 hPieceFace[index] = hbm;
\r
1903 static int TranslatePieceToFontPiece( int piece )
\r
1933 case BlackMarshall:
\r
1937 case BlackNightrider:
\r
1943 case BlackUnicorn:
\r
1947 case BlackGrasshopper:
\r
1959 case BlackCardinal:
\r
1966 case WhiteMarshall:
\r
1970 case WhiteNightrider:
\r
1976 case WhiteUnicorn:
\r
1980 case WhiteGrasshopper:
\r
1992 case WhiteCardinal:
\r
2001 void CreatePiecesFromFont()
\r
2004 HDC hdc_window = NULL;
\r
2010 if( fontBitmapSquareSize < 0 ) {
\r
2011 /* Something went seriously wrong in the past: do not try to recreate fonts! */
\r
2015 if( !appData.useFont || appData.renderPiecesWithFont == NULL ||
\r
2016 appData.renderPiecesWithFont[0] == NULLCHAR || appData.renderPiecesWithFont[0] == '*' ) {
\r
2017 fontBitmapSquareSize = -1;
\r
2021 if( fontBitmapSquareSize != squareSize ) {
\r
2022 hdc_window = GetDC( hwndMain );
\r
2023 hdc = CreateCompatibleDC( hdc_window );
\r
2025 if( hPieceFont != NULL ) {
\r
2026 DeleteObject( hPieceFont );
\r
2029 for( i=0; i<=(int)BlackKing; i++ ) {
\r
2030 hPieceMask[i] = NULL;
\r
2031 hPieceFace[i] = NULL;
\r
2037 if( appData.fontPieceSize >= 50 && appData.fontPieceSize <= 150 ) {
\r
2038 fontHeight = appData.fontPieceSize;
\r
2041 fontHeight = (fontHeight * squareSize) / 100;
\r
2043 lf.lfHeight = -MulDiv( fontHeight, GetDeviceCaps(hdc, LOGPIXELSY), 72 );
\r
2045 lf.lfEscapement = 0;
\r
2046 lf.lfOrientation = 0;
\r
2047 lf.lfWeight = FW_NORMAL;
\r
2049 lf.lfUnderline = 0;
\r
2050 lf.lfStrikeOut = 0;
\r
2051 lf.lfCharSet = DEFAULT_CHARSET;
\r
2052 lf.lfOutPrecision = OUT_DEFAULT_PRECIS;
\r
2053 lf.lfClipPrecision = CLIP_DEFAULT_PRECIS;
\r
2054 lf.lfQuality = PROOF_QUALITY;
\r
2055 lf.lfPitchAndFamily = DEFAULT_PITCH | FF_DONTCARE;
\r
2056 strncpy( lf.lfFaceName, appData.renderPiecesWithFont, sizeof(lf.lfFaceName) );
\r
2057 lf.lfFaceName[ sizeof(lf.lfFaceName) - 1 ] = '\0';
\r
2059 hPieceFont = CreateFontIndirect( &lf );
\r
2061 if( hPieceFont == NULL ) {
\r
2062 fontBitmapSquareSize = -2;
\r
2065 /* Setup font-to-piece character table */
\r
2066 if( ! SetCharTable(pieceToFontChar, appData.fontToPieceTable) ) {
\r
2067 /* No (or wrong) global settings, try to detect the font */
\r
2068 if( strstr(lf.lfFaceName,"Alpha") != NULL ) {
\r
2070 SetCharTable(pieceToFontChar, "phbrqkojntwl");
\r
2072 else if( strstr(lf.lfFaceName,"DiagramTT") != NULL ) {
\r
2073 /* DiagramTT* family */
\r
2074 SetCharTable(pieceToFontChar, "PNLRQKpnlrqk");
\r
2076 else if( strstr(lf.lfFaceName,"WinboardF") != NULL ) {
\r
2077 /* Fairy symbols */
\r
2078 SetCharTable(pieceToFontChar, "PNBRQFEACWMOHIJGDVSLUKpnbrqfeacwmohijgdvsluk");
\r
2080 else if( strstr(lf.lfFaceName,"GC2004D") != NULL ) {
\r
2081 /* Good Companion (Some characters get warped as literal :-( */
\r
2082 char s[] = "1cmWG0??S??oYI23wgQU";
\r
2083 s[0]=0xB9; s[1]=0xA9; s[6]=0xB1; s[11]=0xBB; s[12]=0xAB; s[17]=0xB3;
\r
2084 SetCharTable(pieceToFontChar, s);
\r
2087 /* Cases, Condal, Leipzig, Lucena, Marroquin, Merida, Usual */
\r
2088 SetCharTable(pieceToFontChar, "pnbrqkomvtwl");
\r
2092 /* Create bitmaps */
\r
2093 hfont_old = SelectObject( hdc, hPieceFont );
\r
2094 for(i=(int)WhitePawn; i<(int)EmptySquare; i++) /* [HGM] made a loop for this */
\r
2095 if(PieceToChar((ChessSquare)i) != '.') /* skip unused pieces */
\r
2096 CreatePieceMaskFromFont( hdc_window, hdc, i );
\r
2098 SelectObject( hdc, hfont_old );
\r
2100 fontBitmapSquareSize = squareSize;
\r
2104 if( hdc != NULL ) {
\r
2108 if( hdc_window != NULL ) {
\r
2109 ReleaseDC( hwndMain, hdc_window );
\r
2114 DoLoadBitmap(HINSTANCE hinst, char *piece, int squareSize, char *suffix)
\r
2118 snprintf(name, sizeof(name)/sizeof(name[0]), "%s%d%s", piece, squareSize, suffix);
\r
2119 if (gameInfo.event &&
\r
2120 strcmp(gameInfo.event, "Easter Egg Hunt") == 0 &&
\r
2121 strcmp(name, "k80s") == 0) {
\r
2122 safeStrCpy(name, "tim", sizeof(name)/sizeof(name[0]) );
\r
2124 return LoadBitmap(hinst, name);
\r
2128 /* Insert a color into the program's logical palette
\r
2129 structure. This code assumes the given color is
\r
2130 the result of the RGB or PALETTERGB macro, and it
\r
2131 knows how those macros work (which is documented).
\r
2134 InsertInPalette(COLORREF color)
\r
2136 LPPALETTEENTRY pe = &(pLogPal->palPalEntry[pLogPal->palNumEntries]);
\r
2138 if (pLogPal->palNumEntries++ >= PALETTESIZE) {
\r
2139 DisplayFatalError(_("Too many colors"), 0, 1);
\r
2140 pLogPal->palNumEntries--;
\r
2144 pe->peFlags = (char) 0;
\r
2145 pe->peRed = (char) (0xFF & color);
\r
2146 pe->peGreen = (char) (0xFF & (color >> 8));
\r
2147 pe->peBlue = (char) (0xFF & (color >> 16));
\r
2153 InitDrawingColors()
\r
2155 if (pLogPal == NULL) {
\r
2156 /* Allocate enough memory for a logical palette with
\r
2157 * PALETTESIZE entries and set the size and version fields
\r
2158 * of the logical palette structure.
\r
2160 pLogPal = (NPLOGPALETTE)
\r
2161 LocalAlloc(LMEM_FIXED, (sizeof(LOGPALETTE) +
\r
2162 (sizeof(PALETTEENTRY) * (PALETTESIZE))));
\r
2163 pLogPal->palVersion = 0x300;
\r
2165 pLogPal->palNumEntries = 0;
\r
2167 InsertInPalette(lightSquareColor);
\r
2168 InsertInPalette(darkSquareColor);
\r
2169 InsertInPalette(whitePieceColor);
\r
2170 InsertInPalette(blackPieceColor);
\r
2171 InsertInPalette(highlightSquareColor);
\r
2172 InsertInPalette(premoveHighlightColor);
\r
2174 /* create a logical color palette according the information
\r
2175 * in the LOGPALETTE structure.
\r
2177 hPal = CreatePalette((LPLOGPALETTE) pLogPal);
\r
2179 lightSquareBrush = CreateSolidBrush(lightSquareColor);
\r
2180 blackSquareBrush = CreateSolidBrush(blackPieceColor);
\r
2181 darkSquareBrush = CreateSolidBrush(darkSquareColor);
\r
2182 whitePieceBrush = CreateSolidBrush(whitePieceColor);
\r
2183 blackPieceBrush = CreateSolidBrush(blackPieceColor);
\r
2184 iconBkgndBrush = CreateSolidBrush(GetSysColor(COLOR_BACKGROUND));
\r
2185 explodeBrush = CreateSolidBrush(highlightSquareColor); // [HGM] atomic
\r
2186 markerBrush = CreateSolidBrush(premoveHighlightColor); // [HGM] markers
\r
2187 /* [AS] Force rendering of the font-based pieces */
\r
2188 if( fontBitmapSquareSize > 0 ) {
\r
2189 fontBitmapSquareSize = 0;
\r
2195 BoardWidth(int boardSize, int n)
\r
2196 { /* [HGM] argument n added to allow different width and height */
\r
2197 int lineGap = sizeInfo[boardSize].lineGap;
\r
2199 if( appData.overrideLineGap >= 0 && appData.overrideLineGap <= 5 ) {
\r
2200 lineGap = appData.overrideLineGap;
\r
2203 return (n + 1) * lineGap +
\r
2204 n * sizeInfo[boardSize].squareSize;
\r
2207 /* Respond to board resize by dragging edge */
\r
2209 ResizeBoard(int newSizeX, int newSizeY, int flags)
\r
2211 BoardSize newSize = NUM_SIZES - 1;
\r
2212 static int recurse = 0;
\r
2213 if (IsIconic(hwndMain)) return;
\r
2214 if (recurse > 0) return;
\r
2216 while (newSize > 0) {
\r
2217 InitDrawingSizes(newSize+1000, 0); // [HGM] kludge to update sizeInfo without visible effects
\r
2218 if(newSizeX >= sizeInfo[newSize].cliWidth &&
\r
2219 newSizeY >= sizeInfo[newSize].cliHeight) break;
\r
2222 boardSize = newSize;
\r
2223 InitDrawingSizes(boardSize, flags);
\r
2228 extern Boolean twoBoards, partnerUp; // [HGM] dual
\r
2231 InitDrawingSizes(BoardSize boardSize, int flags)
\r
2233 int i, boardWidth, boardHeight; /* [HGM] height treated separately */
\r
2234 ChessSquare piece;
\r
2235 static int oldBoardSize = -1, oldTinyLayout = 0;
\r
2237 SIZE clockSize, messageSize;
\r
2239 char buf[MSG_SIZ];
\r
2241 HMENU hmenu = GetMenu(hwndMain);
\r
2242 RECT crect, wrect, oldRect;
\r
2244 LOGBRUSH logbrush;
\r
2245 VariantClass v = gameInfo.variant;
\r
2247 int suppressVisibleEffects = 0; // [HGM] kludge to request updating sizeInfo only
\r
2248 if((int)boardSize >= 1000 ) { boardSize -= 1000; suppressVisibleEffects = 1; }
\r
2250 /* [HGM] call with -2 uses old size (for if nr of files, ranks changes) */
\r
2251 if(boardSize == (BoardSize)(-2) ) boardSize = oldBoardSize;
\r
2252 oldBoardSize = boardSize;
\r
2254 if(boardSize != SizeMiddling && boardSize != SizePetite && boardSize != SizeBulky && !appData.useFont)
\r
2255 { // correct board size to one where built-in pieces exist
\r
2256 if((v == VariantCapablanca || v == VariantGothic || v == VariantGrand || v == VariantCapaRandom || v == VariantJanus || v == VariantSuper)
\r
2257 && (boardSize < SizePetite || boardSize > SizeBulky) // Archbishop and Chancellor available in entire middle range
\r
2258 || (v == VariantShogi && boardSize != SizeModerate) // Japanese-style Shogi
\r
2259 || v == VariantKnightmate || v == VariantSChess || v == VariantXiangqi || v == VariantSpartan
\r
2260 || v == VariantShatranj || v == VariantMakruk || v == VariantGreat || v == VariantFairy ) {
\r
2261 if(boardSize < SizeMediocre) boardSize = SizePetite; else
\r
2262 if(boardSize > SizeModerate) boardSize = SizeBulky; else
\r
2263 boardSize = SizeMiddling;
\r
2266 if(!appData.useFont && boardSize == SizePetite && (v == VariantShogi || v == VariantKnightmate)) boardSize = SizeMiddling; // no Unicorn in Petite
\r
2268 oldRect.left = wpMain.x; //[HGM] placement: remember previous window params
\r
2269 oldRect.top = wpMain.y;
\r
2270 oldRect.right = wpMain.x + wpMain.width;
\r
2271 oldRect.bottom = wpMain.y + wpMain.height;
\r
2273 tinyLayout = sizeInfo[boardSize].tinyLayout;
\r
2274 smallLayout = sizeInfo[boardSize].smallLayout;
\r
2275 squareSize = sizeInfo[boardSize].squareSize;
\r
2276 lineGap = sizeInfo[boardSize].lineGap;
\r
2277 minorSize = 0; /* [HGM] Kludge to see if demagnified pieces need to be shifted */
\r
2279 if( appData.overrideLineGap >= 0 && appData.overrideLineGap <= 5 ) {
\r
2280 lineGap = appData.overrideLineGap;
\r
2283 if (tinyLayout != oldTinyLayout) {
\r
2284 long style = GetWindowLongPtr(hwndMain, GWL_STYLE);
\r
2286 style &= ~WS_SYSMENU;
\r
2287 InsertMenu(hmenu, IDM_Exit, MF_BYCOMMAND, IDM_Minimize,
\r
2288 "&Minimize\tCtrl+F4");
\r
2290 style |= WS_SYSMENU;
\r
2291 RemoveMenu(hmenu, IDM_Minimize, MF_BYCOMMAND);
\r
2293 SetWindowLongPtr(hwndMain, GWL_STYLE, style);
\r
2295 for (i=0; menuBarText[tinyLayout][i]; i++) {
\r
2296 ModifyMenu(hmenu, i, MF_STRING|MF_BYPOSITION|MF_POPUP,
\r
2297 (UINT)GetSubMenu(hmenu, i), T_(menuBarText[tinyLayout][i]));
\r
2299 DrawMenuBar(hwndMain);
\r
2302 boardWidth = BoardWidth(boardSize, BOARD_WIDTH);
\r
2303 boardHeight = BoardWidth(boardSize, BOARD_HEIGHT);
\r
2305 /* Get text area sizes */
\r
2306 hdc = GetDC(hwndMain);
\r
2307 if (appData.clockMode) {
\r
2308 snprintf(buf, MSG_SIZ, _("White: %s"), TimeString(23*60*60*1000L));
\r
2310 snprintf(buf, MSG_SIZ, _("White"));
\r
2312 oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);
\r
2313 GetTextExtentPoint(hdc, buf, strlen(buf), &clockSize);
\r
2314 SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);
\r
2315 str = _("We only care about the height here");
\r
2316 GetTextExtentPoint(hdc, str, strlen(str), &messageSize);
\r
2317 SelectObject(hdc, oldFont);
\r
2318 ReleaseDC(hwndMain, hdc);
\r
2320 /* Compute where everything goes */
\r
2321 if((first.programLogo || second.programLogo) && !tinyLayout) {
\r
2322 /* [HGM] logo: if either logo is on, reserve space for it */
\r
2323 logoHeight = 2*clockSize.cy;
\r
2324 leftLogoRect.left = OUTER_MARGIN;
\r
2325 leftLogoRect.right = leftLogoRect.left + 4*clockSize.cy;
\r
2326 leftLogoRect.top = OUTER_MARGIN;
\r
2327 leftLogoRect.bottom = OUTER_MARGIN + logoHeight;
\r
2329 rightLogoRect.right = OUTER_MARGIN + boardWidth;
\r
2330 rightLogoRect.left = rightLogoRect.right - 4*clockSize.cy;
\r
2331 rightLogoRect.top = OUTER_MARGIN;
\r
2332 rightLogoRect.bottom = OUTER_MARGIN + logoHeight;
\r
2335 whiteRect.left = leftLogoRect.right;
\r
2336 whiteRect.right = OUTER_MARGIN + boardWidth/2 - INNER_MARGIN/2;
\r
2337 whiteRect.top = OUTER_MARGIN;
\r
2338 whiteRect.bottom = whiteRect.top + logoHeight;
\r
2340 blackRect.right = rightLogoRect.left;
\r
2341 blackRect.left = whiteRect.right + INNER_MARGIN;
\r
2342 blackRect.top = whiteRect.top;
\r
2343 blackRect.bottom = whiteRect.bottom;
\r
2345 whiteRect.left = OUTER_MARGIN;
\r
2346 whiteRect.right = whiteRect.left + boardWidth/2 - INNER_MARGIN/2;
\r
2347 whiteRect.top = OUTER_MARGIN;
\r
2348 whiteRect.bottom = whiteRect.top + clockSize.cy;
\r
2350 blackRect.left = whiteRect.right + INNER_MARGIN;
\r
2351 blackRect.right = blackRect.left + boardWidth/2 - 1;
\r
2352 blackRect.top = whiteRect.top;
\r
2353 blackRect.bottom = whiteRect.bottom;
\r
2355 logoHeight = 0; // [HGM] logo: suppress logo after change to tiny layout!
\r
2358 messageRect.left = OUTER_MARGIN + MESSAGE_LINE_LEFTMARGIN;
\r
2359 if (appData.showButtonBar) {
\r
2360 messageRect.right = OUTER_MARGIN + boardWidth // [HGM] logo: expressed independent of clock placement
\r
2361 - N_BUTTONS*BUTTON_WIDTH - MESSAGE_LINE_LEFTMARGIN;
\r
2363 messageRect.right = OUTER_MARGIN + boardWidth;
\r
2365 messageRect.top = whiteRect.bottom + INNER_MARGIN;
\r
2366 messageRect.bottom = messageRect.top + messageSize.cy;
\r
2368 boardRect.left = OUTER_MARGIN;
\r
2369 boardRect.right = boardRect.left + boardWidth;
\r
2370 boardRect.top = messageRect.bottom + INNER_MARGIN;
\r
2371 boardRect.bottom = boardRect.top + boardHeight;
\r
2373 sizeInfo[boardSize].cliWidth = boardRect.right + OUTER_MARGIN;
\r
2374 sizeInfo[boardSize].cliHeight = boardRect.bottom + OUTER_MARGIN;
\r
2375 oldTinyLayout = tinyLayout;
\r
2376 winW = 2 * GetSystemMetrics(SM_CXFRAME) + boardRect.right + OUTER_MARGIN;
\r
2377 winH = 2 * GetSystemMetrics(SM_CYFRAME) + GetSystemMetrics(SM_CYMENU) +
\r
2378 GetSystemMetrics(SM_CYCAPTION) + boardRect.bottom + OUTER_MARGIN;
\r
2379 winW *= 1 + twoBoards;
\r
2380 if(suppressVisibleEffects) return; // [HGM] when called for filling sizeInfo only
\r
2381 wpMain.width = winW; // [HGM] placement: set through temporary which can used by initial sizing choice
\r
2382 wpMain.height = winH; // without disturbing window attachments
\r
2383 GetWindowRect(hwndMain, &wrect);
\r
2384 SetWindowPos(hwndMain, NULL, 0, 0, wpMain.width, wpMain.height,
\r
2385 SWP_NOCOPYBITS|SWP_NOZORDER|SWP_NOMOVE);
\r
2387 // [HGM] placement: let attached windows follow size change.
\r
2388 ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, moveHistoryDialog, &wpMoveHistory );
\r
2389 ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, evalGraphDialog, &wpEvalGraph );
\r
2390 ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, engineOutputDialog, &wpEngineOutput );
\r
2391 ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, gameListDialog, &wpGameList );
\r
2392 ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, hwndConsole, &wpConsole );
\r
2394 /* compensate if menu bar wrapped */
\r
2395 GetClientRect(hwndMain, &crect);
\r
2396 offby = boardRect.bottom + OUTER_MARGIN - crect.bottom;
\r
2397 wpMain.height += offby;
\r
2399 case WMSZ_TOPLEFT:
\r
2400 SetWindowPos(hwndMain, NULL,
\r
2401 wrect.right - wpMain.width, wrect.bottom - wpMain.height,
\r
2402 wpMain.width, wpMain.height, SWP_NOCOPYBITS|SWP_NOZORDER);
\r
2405 case WMSZ_TOPRIGHT:
\r
2407 SetWindowPos(hwndMain, NULL,
\r
2408 wrect.left, wrect.bottom - wpMain.height,
\r
2409 wpMain.width, wpMain.height, SWP_NOCOPYBITS|SWP_NOZORDER);
\r
2412 case WMSZ_BOTTOMLEFT:
\r
2414 SetWindowPos(hwndMain, NULL,
\r
2415 wrect.right - wpMain.width, wrect.top,
\r
2416 wpMain.width, wpMain.height, SWP_NOCOPYBITS|SWP_NOZORDER);
\r
2419 case WMSZ_BOTTOMRIGHT:
\r
2423 SetWindowPos(hwndMain, NULL, 0, 0, wpMain.width, wpMain.height,
\r
2424 SWP_NOCOPYBITS|SWP_NOZORDER|SWP_NOMOVE);
\r
2429 for (i = 0; i < N_BUTTONS; i++) {
\r
2430 if (buttonDesc[i].hwnd != NULL) {
\r
2431 DestroyWindow(buttonDesc[i].hwnd);
\r
2432 buttonDesc[i].hwnd = NULL;
\r
2434 if (appData.showButtonBar) {
\r
2435 buttonDesc[i].hwnd =
\r
2436 CreateWindow("BUTTON", buttonDesc[i].label,
\r
2437 WS_VISIBLE | WS_CHILD | BS_PUSHBUTTON,
\r
2438 boardRect.right - BUTTON_WIDTH*(N_BUTTONS-i),
\r
2439 messageRect.top, BUTTON_WIDTH, messageSize.cy, hwndMain,
\r
2440 (HMENU) buttonDesc[i].id,
\r
2441 (HINSTANCE) GetWindowLongPtr(hwndMain, GWLP_HINSTANCE), NULL);
\r
2443 SendMessage(buttonDesc[i].hwnd, WM_SETFONT,
\r
2444 (WPARAM)font[boardSize][MESSAGE_FONT]->hf,
\r
2445 MAKELPARAM(FALSE, 0));
\r
2447 if (buttonDesc[i].id == IDM_Pause)
\r
2448 hwndPause = buttonDesc[i].hwnd;
\r
2449 buttonDesc[i].wndproc = (WNDPROC)
\r
2450 SetWindowLongPtr(buttonDesc[i].hwnd, GWLP_WNDPROC, (LONG_PTR) ButtonProc);
\r
2453 if (gridPen != NULL) DeleteObject(gridPen);
\r
2454 if (highlightPen != NULL) DeleteObject(highlightPen);
\r
2455 if (premovePen != NULL) DeleteObject(premovePen);
\r
2456 if (lineGap != 0) {
\r
2457 logbrush.lbStyle = BS_SOLID;
\r
2458 logbrush.lbColor = RGB(0, 0, 0); /* grid pen color = black */
\r
2460 ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,
\r
2461 lineGap, &logbrush, 0, NULL);
\r
2462 logbrush.lbColor = highlightSquareColor;
\r
2464 ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,
\r
2465 lineGap, &logbrush, 0, NULL);
\r
2467 logbrush.lbColor = premoveHighlightColor;
\r
2469 ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,
\r
2470 lineGap, &logbrush, 0, NULL);
\r
2472 /* [HGM] Loop had to be split in part for vert. and hor. lines */
\r
2473 for (i = 0; i < BOARD_HEIGHT + 1; i++) {
\r
2474 gridEndpoints[i*2].x = boardRect.left + lineGap / 2;
\r
2475 gridEndpoints[i*2].y = gridEndpoints[i*2 + 1].y =
\r
2476 boardRect.top + lineGap / 2 + (i * (squareSize + lineGap));
\r
2477 gridEndpoints[i*2 + 1].x = boardRect.left + lineGap / 2 +
\r
2478 BOARD_WIDTH * (squareSize + lineGap);
\r
2479 gridVertexCounts[i*2] = gridVertexCounts[i*2 + 1] = 2;
\r
2481 for (i = 0; i < BOARD_WIDTH + 1; i++) {
\r
2482 gridEndpoints[i*2 + BOARD_HEIGHT*2 + 2].y = boardRect.top + lineGap / 2;
\r
2483 gridEndpoints[i*2 + BOARD_HEIGHT*2 + 2].x =
\r
2484 gridEndpoints[i*2 + 1 + BOARD_HEIGHT*2 + 2].x = boardRect.left +
\r
2485 lineGap / 2 + (i * (squareSize + lineGap));
\r
2486 gridEndpoints[i*2 + 1 + BOARD_HEIGHT*2 + 2].y =
\r
2487 boardRect.top + BOARD_HEIGHT * (squareSize + lineGap);
\r
2488 gridVertexCounts[i*2] = gridVertexCounts[i*2 + 1] = 2;
\r
2492 /* [HGM] Licensing requirement */
\r
2494 if(gameInfo.variant == VariantGothic) GothicPopUp( GOTHIC, VariantGothic); else
\r
2497 if(gameInfo.variant == VariantFalcon) GothicPopUp( FALCON, VariantFalcon); else
\r
2499 GothicPopUp( "", VariantNormal);
\r
2502 /* if (boardSize == oldBoardSize) return; [HGM] variant might have changed */
\r
2504 /* Load piece bitmaps for this board size */
\r
2505 for (i=0; i<=2; i++) {
\r
2506 for (piece = WhitePawn;
\r
2507 (int) piece < (int) BlackPawn;
\r
2508 piece = (ChessSquare) ((int) piece + 1)) {
\r
2509 if (pieceBitmap[i][piece] != NULL)
\r
2510 DeleteObject(pieceBitmap[i][piece]);
\r
2514 fontBitmapSquareSize = 0; /* [HGM] render: make sure pieces will be recreated, as we might need others now */
\r
2515 // Orthodox Chess pieces
\r
2516 pieceBitmap[0][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "s");
\r
2517 pieceBitmap[0][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "s");
\r
2518 pieceBitmap[0][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "s");
\r
2519 pieceBitmap[0][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "s");
\r
2520 pieceBitmap[0][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "s");
\r
2521 pieceBitmap[1][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "o");
\r
2522 pieceBitmap[1][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "o");
\r
2523 pieceBitmap[1][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "o");
\r
2524 pieceBitmap[1][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "o");
\r
2525 pieceBitmap[1][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "o");
\r
2526 pieceBitmap[2][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "w");
\r
2527 pieceBitmap[2][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "w");
\r
2528 pieceBitmap[2][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "w");
\r
2529 pieceBitmap[2][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "w");
\r
2530 pieceBitmap[2][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "w");
\r
2531 if( gameInfo.variant == VariantShogi && squareSize <= 72 && squareSize >= 33) {
\r
2532 // in Shogi, Hijack the unused Queen for Lance
\r
2533 pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "s");
\r
2534 pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "o");
\r
2535 pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "w");
\r
2537 pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "s");
\r
2538 pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "o");
\r
2539 pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "w");
\r
2542 if(squareSize <= 72 && squareSize >= 33) {
\r
2543 /* A & C are available in most sizes now */
\r
2544 if(squareSize != 49 && squareSize != 72 && squareSize != 33) { // Vortex-like
\r
2545 pieceBitmap[0][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "s");
\r
2546 pieceBitmap[1][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "o");
\r
2547 pieceBitmap[2][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "w");
\r
2548 pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "s");
\r
2549 pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "o");
\r
2550 pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "w");
\r
2551 pieceBitmap[0][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "s");
\r
2552 pieceBitmap[1][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "o");
\r
2553 pieceBitmap[2][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "w");
\r
2554 pieceBitmap[0][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "s");
\r
2555 pieceBitmap[1][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "o");
\r
2556 pieceBitmap[2][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "w");
\r
2557 } else { // Smirf-like
\r
2558 if(gameInfo.variant == VariantSChess) {
\r
2559 pieceBitmap[0][WhiteAngel] = DoLoadBitmap(hInst, "v", squareSize, "s");
\r
2560 pieceBitmap[1][WhiteAngel] = DoLoadBitmap(hInst, "v", squareSize, "o");
\r
2561 pieceBitmap[2][WhiteAngel] = DoLoadBitmap(hInst, "v", squareSize, "w");
\r
2563 pieceBitmap[0][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "s");
\r
2564 pieceBitmap[1][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "o");
\r
2565 pieceBitmap[2][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "w");
\r
2568 if(gameInfo.variant == VariantGothic) { // Vortex-like
\r
2569 pieceBitmap[0][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "s");
\r
2570 pieceBitmap[1][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "o");
\r
2571 pieceBitmap[2][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "w");
\r
2572 } else if(gameInfo.variant == VariantSChess && (squareSize == 49 || squareSize == 72)) {
\r
2573 pieceBitmap[0][WhiteMarshall] = DoLoadBitmap(hInst, "e", squareSize, "s");
\r
2574 pieceBitmap[1][WhiteMarshall] = DoLoadBitmap(hInst, "e", squareSize, "o");
\r
2575 pieceBitmap[2][WhiteMarshall] = DoLoadBitmap(hInst, "e", squareSize, "w");
\r
2576 } else { // WinBoard standard
\r
2577 pieceBitmap[0][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "s");
\r
2578 pieceBitmap[1][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "o");
\r
2579 pieceBitmap[2][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "w");
\r
2584 if(squareSize==72 || squareSize==49 || squareSize==33) { /* experiment with some home-made bitmaps */
\r
2585 pieceBitmap[0][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "s");
\r
2586 pieceBitmap[1][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "o");
\r
2587 pieceBitmap[2][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "w");
\r
2588 pieceBitmap[0][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "s");
\r
2589 pieceBitmap[1][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "o");
\r
2590 pieceBitmap[2][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "w");
\r
2591 pieceBitmap[0][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "s");
\r
2592 pieceBitmap[1][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "o");
\r
2593 pieceBitmap[2][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "w");
\r
2594 pieceBitmap[0][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "s");
\r
2595 pieceBitmap[1][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "o");
\r
2596 pieceBitmap[2][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "w");
\r
2597 pieceBitmap[0][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "s");
\r
2598 pieceBitmap[1][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "o");
\r
2599 pieceBitmap[2][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "w");
\r
2600 pieceBitmap[0][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "s");
\r
2601 pieceBitmap[1][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "o");
\r
2602 pieceBitmap[2][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "w");
\r
2603 pieceBitmap[0][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "s");
\r
2604 pieceBitmap[1][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "o");
\r
2605 pieceBitmap[2][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "w");
\r
2606 pieceBitmap[0][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "s");
\r
2607 pieceBitmap[1][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "o");
\r
2608 pieceBitmap[2][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "w");
\r
2609 pieceBitmap[0][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "s");
\r
2610 pieceBitmap[1][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "o");
\r
2611 pieceBitmap[2][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "w");
\r
2612 pieceBitmap[0][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "s");
\r
2613 pieceBitmap[1][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "o");
\r
2614 pieceBitmap[2][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "w");
\r
2616 if(gameInfo.variant == VariantShogi) { /* promoted Gold represemtations */
\r
2617 pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "wp", squareSize, "s");
\r
2618 pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "wp", squareSize, "o");
\r
2619 pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "w", squareSize, "w");
\r
2620 pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "wn", squareSize, "s");
\r
2621 pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "wn", squareSize, "o");
\r
2622 pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "w", squareSize, "w");
\r
2623 pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "ws", squareSize, "s");
\r
2624 pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "ws", squareSize, "o");
\r
2625 pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "w", squareSize, "w");
\r
2626 pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "wl", squareSize, "s");
\r
2627 pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "wl", squareSize, "o");
\r
2628 pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "w", squareSize, "w");
\r
2630 pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "s");
\r
2631 pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "o");
\r
2632 pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "w");
\r
2633 pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "s");
\r
2634 pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "o");
\r
2635 pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "w");
\r
2636 pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "s");
\r
2637 pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "o");
\r
2638 pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "w");
\r
2639 pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "s");
\r
2640 pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "o");
\r
2641 pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "w");
\r
2644 } else { /* other size, no special bitmaps available. Use smaller symbols */
\r
2645 if((int)boardSize < 2) minorSize = sizeInfo[0].squareSize;
\r
2646 else minorSize = sizeInfo[(int)boardSize - 2].squareSize;
\r
2647 pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "s");
\r
2648 pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "o");
\r
2649 pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "w");
\r
2650 pieceBitmap[0][WhiteCardinal] = DoLoadBitmap(hInst, "b", minorSize, "s");
\r
2651 pieceBitmap[1][WhiteCardinal] = DoLoadBitmap(hInst, "b", minorSize, "o");
\r
2652 pieceBitmap[2][WhiteCardinal] = DoLoadBitmap(hInst, "b", minorSize, "w");
\r
2653 pieceBitmap[0][WhiteDragon] = DoLoadBitmap(hInst, "r", minorSize, "s");
\r
2654 pieceBitmap[1][WhiteDragon] = DoLoadBitmap(hInst, "r", minorSize, "o");
\r
2655 pieceBitmap[2][WhiteDragon] = DoLoadBitmap(hInst, "r", minorSize, "w");
\r
2656 pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "s");
\r
2657 pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "o");
\r
2658 pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "w");
\r
2662 if(gameInfo.variant == VariantShogi && squareSize == 58)
\r
2663 /* special Shogi support in this size */
\r
2664 { for (i=0; i<=2; i++) { /* replace all bitmaps */
\r
2665 for (piece = WhitePawn;
\r
2666 (int) piece < (int) BlackPawn;
\r
2667 piece = (ChessSquare) ((int) piece + 1)) {
\r
2668 if (pieceBitmap[i][piece] != NULL)
\r
2669 DeleteObject(pieceBitmap[i][piece]);
\r
2672 pieceBitmap[0][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "o");
\r
2673 pieceBitmap[0][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "o");
\r
2674 pieceBitmap[0][WhiteBishop] = DoLoadBitmap(hInst, "sb", squareSize, "o");
\r
2675 pieceBitmap[0][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "o");
\r
2676 pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "o");
\r
2677 pieceBitmap[0][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "o");
\r
2678 pieceBitmap[0][WhiteFerz] = DoLoadBitmap(hInst, "sf", squareSize, "o");
\r
2679 pieceBitmap[0][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "o");
\r
2680 pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "su", squareSize, "o");
\r
2681 pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "sh", squareSize, "o");
\r
2682 pieceBitmap[0][WhiteCardinal] = DoLoadBitmap(hInst, "sa", squareSize, "o");
\r
2683 pieceBitmap[0][WhiteDragon] = DoLoadBitmap(hInst, "sc", squareSize, "o");
\r
2684 pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "sg", squareSize, "o");
\r
2685 pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "ss", squareSize, "o");
\r
2686 pieceBitmap[1][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "o");
\r
2687 pieceBitmap[1][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "o");
\r
2688 pieceBitmap[1][WhiteBishop] = DoLoadBitmap(hInst, "sb", squareSize, "o");
\r
2689 pieceBitmap[1][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "o");
\r
2690 pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "o");
\r
2691 pieceBitmap[1][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "o");
\r
2692 pieceBitmap[1][WhiteFerz] = DoLoadBitmap(hInst, "sf", squareSize, "o");
\r
2693 pieceBitmap[1][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "o");
\r
2694 pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "su", squareSize, "o");
\r
2695 pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "sh", squareSize, "o");
\r
2696 pieceBitmap[1][WhiteCardinal] = DoLoadBitmap(hInst, "sa", squareSize, "o");
\r
2697 pieceBitmap[1][WhiteDragon] = DoLoadBitmap(hInst, "sc", squareSize, "o");
\r
2698 pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "sg", squareSize, "o");
\r
2699 pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "ss", squareSize, "o");
\r
2700 pieceBitmap[2][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "w");
\r
2701 pieceBitmap[2][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "w");
\r
2702 pieceBitmap[2][WhiteBishop] = DoLoadBitmap(hInst, "sr", squareSize, "w");
\r
2703 pieceBitmap[2][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "w");
\r
2704 pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "w");
\r
2705 pieceBitmap[2][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "w");
\r
2706 pieceBitmap[2][WhiteFerz] = DoLoadBitmap(hInst, "sw", squareSize, "w");
\r
2707 pieceBitmap[2][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "w");
\r
2708 pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "sp", squareSize, "w");
\r
2709 pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "sn", squareSize, "w");
\r
2710 pieceBitmap[2][WhiteCardinal] = DoLoadBitmap(hInst, "sr", squareSize, "w");
\r
2711 pieceBitmap[2][WhiteDragon] = DoLoadBitmap(hInst, "sr", squareSize, "w");
\r
2712 pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "sl", squareSize, "w");
\r
2713 pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "sw", squareSize, "w");
\r
2719 PieceBitmap(ChessSquare p, int kind)
\r
2721 if ((int) p >= (int) BlackPawn)
\r
2722 p = (ChessSquare) ((int) p - (int) BlackPawn + (int) WhitePawn);
\r
2724 return pieceBitmap[kind][(int) p];
\r
2727 /***************************************************************/
\r
2729 #define MIN(a,b) (((a) < (b)) ? (a) : (b))
\r
2730 #define MAX(a,b) (((a) > (b)) ? (a) : (b))
\r
2732 #define MIN3(a,b,c) (((a) < (b) && (a) < (c)) ? (a) : (((b) < (a) && (b) < (c)) ? (b) : (c)))
\r
2733 #define MAX3(a,b,c) (((a) > (b) && (a) > (c)) ? (a) : (((b) > (a) && (b) > (c)) ? (b) : (c)))
\r
2737 SquareToPos(int row, int column, int * x, int * y)
\r
2740 *x = boardRect.left + lineGap + ((BOARD_WIDTH-1)-column) * (squareSize + lineGap);
\r
2741 *y = boardRect.top + lineGap + row * (squareSize + lineGap);
\r
2743 *x = boardRect.left + lineGap + column * (squareSize + lineGap);
\r
2744 *y = boardRect.top + lineGap + ((BOARD_HEIGHT-1)-row) * (squareSize + lineGap);
\r
2749 DrawCoordsOnDC(HDC hdc)
\r
2751 static char files[] = "0123456789012345678901221098765432109876543210";
\r
2752 static char ranks[] = "wvutsrqponmlkjihgfedcbaabcdefghijklmnopqrstuvw";
\r
2753 char str[2] = { NULLCHAR, NULLCHAR };
\r
2754 int oldMode, oldAlign, x, y, start, i;
\r
2758 if (!appData.showCoords)
\r
2761 start = flipView ? 1-(ONE!='1') : 45+(ONE!='1')-BOARD_HEIGHT;
\r
2763 oldBrush = SelectObject(hdc, GetStockObject(BLACK_BRUSH));
\r
2764 oldMode = SetBkMode(hdc, (appData.monoMode ? OPAQUE : TRANSPARENT));
\r
2765 oldAlign = GetTextAlign(hdc);
\r
2766 oldFont = SelectObject(hdc, font[boardSize][COORD_FONT]->hf);
\r
2768 y = boardRect.top + lineGap;
\r
2769 x = boardRect.left + lineGap + gameInfo.holdingsWidth*(squareSize + lineGap);
\r
2771 SetTextAlign(hdc, TA_LEFT|TA_TOP);
\r
2772 for (i = 0; i < BOARD_HEIGHT; i++) {
\r
2773 str[0] = files[start + i];
\r
2774 ExtTextOut(hdc, x + 2, y + 1, 0, NULL, str, 1, NULL);
\r
2775 y += squareSize + lineGap;
\r
2778 start = flipView ? 23-(BOARD_RGHT-BOARD_LEFT) : 23;
\r
2780 SetTextAlign(hdc, TA_RIGHT|TA_BOTTOM);
\r
2781 for (i = 0; i < BOARD_RGHT - BOARD_LEFT; i++) {
\r
2782 str[0] = ranks[start + i];
\r
2783 ExtTextOut(hdc, x + squareSize - 2, y - 1, 0, NULL, str, 1, NULL);
\r
2784 x += squareSize + lineGap;
\r
2787 SelectObject(hdc, oldBrush);
\r
2788 SetBkMode(hdc, oldMode);
\r
2789 SetTextAlign(hdc, oldAlign);
\r
2790 SelectObject(hdc, oldFont);
\r
2794 DrawGridOnDC(HDC hdc)
\r
2798 if (lineGap != 0) {
\r
2799 oldPen = SelectObject(hdc, gridPen);
\r
2800 PolyPolyline(hdc, gridEndpoints, gridVertexCounts, BOARD_WIDTH+BOARD_HEIGHT + 2);
\r
2801 SelectObject(hdc, oldPen);
\r
2805 #define HIGHLIGHT_PEN 0
\r
2806 #define PREMOVE_PEN 1
\r
2809 DrawHighlightOnDC(HDC hdc, BOOLEAN on, int x, int y, int pen)
\r
2812 HPEN oldPen, hPen;
\r
2813 if (lineGap == 0) return;
\r
2815 x1 = boardRect.left +
\r
2816 lineGap/2 + ((BOARD_WIDTH-1)-x) * (squareSize + lineGap);
\r
2817 y1 = boardRect.top +
\r
2818 lineGap/2 + y * (squareSize + lineGap);
\r
2820 x1 = boardRect.left +
\r
2821 lineGap/2 + x * (squareSize + lineGap);
\r
2822 y1 = boardRect.top +
\r
2823 lineGap/2 + ((BOARD_HEIGHT-1)-y) * (squareSize + lineGap);
\r
2825 hPen = pen ? premovePen : highlightPen;
\r
2826 oldPen = SelectObject(hdc, on ? hPen : gridPen);
\r
2827 MoveToEx(hdc, x1, y1, NULL);
\r
2828 LineTo(hdc, x1 + squareSize + lineGap, y1);
\r
2829 LineTo(hdc, x1 + squareSize + lineGap, y1 + squareSize + lineGap);
\r
2830 LineTo(hdc, x1, y1 + squareSize + lineGap);
\r
2831 LineTo(hdc, x1, y1);
\r
2832 SelectObject(hdc, oldPen);
\r
2836 DrawHighlightsOnDC(HDC hdc, HighlightInfo *h, int pen)
\r
2839 for (i=0; i<2; i++) {
\r
2840 if (h->sq[i].x >= 0 && h->sq[i].y >= 0)
\r
2841 DrawHighlightOnDC(hdc, TRUE,
\r
2842 h->sq[i].x, h->sq[i].y,
\r
2847 /* Note: sqcolor is used only in monoMode */
\r
2848 /* Note that this code is largely duplicated in woptions.c,
\r
2849 function DrawSampleSquare, so that needs to be updated too */
\r
2851 DrawPieceOnDC(HDC hdc, ChessSquare piece, int color, int sqcolor, int x, int y, HDC tmphdc)
\r
2853 HBITMAP oldBitmap;
\r
2857 if (appData.blindfold) return;
\r
2859 /* [AS] Use font-based pieces if needed */
\r
2860 if( fontBitmapSquareSize >= 0 && (squareSize > 32 || gameInfo.variant >= VariantShogi)) {
\r
2861 /* Create piece bitmaps, or do nothing if piece set is up to date */
\r
2862 CreatePiecesFromFont();
\r
2864 if( fontBitmapSquareSize == squareSize ) {
\r
2865 int index = TranslatePieceToFontPiece(piece);
\r
2867 SelectObject( tmphdc, hPieceMask[ index ] );
\r
2869 if(appData.upsideDown ? color==flipView : (flipView && gameInfo.variant == VariantShogi))
\r
2870 StretchBlt(hdc, x+squareSize, y+squareSize, -squareSize, -squareSize, tmphdc, 0, 0, squareSize, squareSize, SRCAND);
\r
2874 squareSize, squareSize,
\r
2879 SelectObject( tmphdc, hPieceFace[ index ] );
\r
2881 if(appData.upsideDown ? color==flipView : (flipView && gameInfo.variant == VariantShogi))
\r
2882 StretchBlt(hdc, x+squareSize, y+squareSize, -squareSize, -squareSize, tmphdc, 0, 0, squareSize, squareSize, SRCPAINT);
\r
2886 squareSize, squareSize,
\r
2895 if (appData.monoMode) {
\r
2896 SelectObject(tmphdc, PieceBitmap(piece,
\r
2897 color == sqcolor ? OUTLINE_PIECE : SOLID_PIECE));
\r
2898 BitBlt(hdc, x, y, squareSize, squareSize, tmphdc, 0, 0,
\r
2899 sqcolor ? SRCCOPY : NOTSRCCOPY);
\r
2901 tmpSize = squareSize;
\r
2903 ((piece >= (int)WhiteNightrider && piece <= WhiteGrasshopper) ||
\r
2904 (piece >= (int)BlackNightrider && piece <= BlackGrasshopper)) ) {
\r
2905 /* [HGM] no bitmap available for promoted pieces in Crazyhouse */
\r
2906 /* Bitmaps of smaller size are substituted, but we have to align them */
\r
2907 x += (squareSize - minorSize)>>1;
\r
2908 y += squareSize - minorSize - 2;
\r
2909 tmpSize = minorSize;
\r
2911 if (color || appData.allWhite ) {
\r
2912 oldBitmap = SelectObject(tmphdc, PieceBitmap(piece, WHITE_PIECE));
\r
2914 oldBrush = SelectObject(hdc, whitePieceBrush);
\r
2915 else oldBrush = SelectObject(hdc, blackPieceBrush);
\r
2916 if(appData.upsideDown && color==flipView)
\r
2917 StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, 0x00B8074A);
\r
2919 BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, 0x00B8074A);
\r
2920 /* Use black for outline of white pieces */
\r
2921 SelectObject(tmphdc, PieceBitmap(piece, OUTLINE_PIECE));
\r
2922 if(appData.upsideDown && color==flipView)
\r
2923 StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, SRCAND);
\r
2925 BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, SRCAND);
\r
2927 /* Use square color for details of black pieces */
\r
2928 oldBitmap = SelectObject(tmphdc, PieceBitmap(piece, SOLID_PIECE));
\r
2929 oldBrush = SelectObject(hdc, blackPieceBrush);
\r
2930 if(appData.upsideDown && !flipView)
\r
2931 StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, 0x00B8074A);
\r
2933 BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, 0x00B8074A);
\r
2935 SelectObject(hdc, oldBrush);
\r
2936 SelectObject(tmphdc, oldBitmap);
\r
2940 /* [AS] Compute a drawing mode for a square, based on specified settings (see DrawTile) */
\r
2941 int GetBackTextureMode( int algo )
\r
2943 int result = BACK_TEXTURE_MODE_DISABLED;
\r
2947 case BACK_TEXTURE_MODE_PLAIN:
\r
2948 result = 1; /* Always use identity map */
\r
2950 case BACK_TEXTURE_MODE_FULL_RANDOM:
\r
2951 result = 1 + (myrandom() % 3); /* Pick a transformation at random */
\r
2959 [AS] Compute and save texture drawing info, otherwise we may not be able
\r
2960 to handle redraws cleanly (as random numbers would always be different).
\r
2962 VOID RebuildTextureSquareInfo()
\r
2972 ZeroMemory( &backTextureSquareInfo, sizeof(backTextureSquareInfo) );
\r
2974 if( liteBackTexture != NULL ) {
\r
2975 if( GetObject( liteBackTexture, sizeof(bi), &bi ) > 0 ) {
\r
2976 lite_w = bi.bmWidth;
\r
2977 lite_h = bi.bmHeight;
\r
2981 if( darkBackTexture != NULL ) {
\r
2982 if( GetObject( darkBackTexture, sizeof(bi), &bi ) > 0 ) {
\r
2983 dark_w = bi.bmWidth;
\r
2984 dark_h = bi.bmHeight;
\r
2988 for( row=0; row<BOARD_HEIGHT; row++ ) {
\r
2989 for( col=0; col<BOARD_WIDTH; col++ ) {
\r
2990 if( (col + row) & 1 ) {
\r
2992 if( lite_w >= squareSize && lite_h >= squareSize ) {
\r
2993 if( lite_w >= squareSize*BOARD_WIDTH )
\r
2994 backTextureSquareInfo[row][col].x = (2*col+1)*lite_w/(2*BOARD_WIDTH) - squareSize/2; /* [HGM] cut out of center of virtual square */
\r
2996 backTextureSquareInfo[row][col].x = col * (lite_w - squareSize) / (BOARD_WIDTH-1); /* [HGM] divide by size-1 in stead of size! */
\r
2997 if( lite_h >= squareSize*BOARD_HEIGHT )
\r
2998 backTextureSquareInfo[row][col].y = (2*(BOARD_HEIGHT-row)-1)*lite_h/(2*BOARD_HEIGHT) - squareSize/2;
\r
3000 backTextureSquareInfo[row][col].y = (BOARD_HEIGHT-1-row) * (lite_h - squareSize) / (BOARD_HEIGHT-1);
\r
3001 backTextureSquareInfo[row][col].mode = GetBackTextureMode(liteBackTextureMode);
\r
3006 if( dark_w >= squareSize && dark_h >= squareSize ) {
\r
3007 if( dark_w >= squareSize*BOARD_WIDTH )
\r
3008 backTextureSquareInfo[row][col].x = (2*col+1) * dark_w / (2*BOARD_WIDTH) - squareSize/2;
\r
3010 backTextureSquareInfo[row][col].x = col * (dark_w - squareSize) / (BOARD_WIDTH-1);
\r
3011 if( dark_h >= squareSize*BOARD_HEIGHT )
\r
3012 backTextureSquareInfo[row][col].y = (2*(BOARD_HEIGHT-row)-1) * dark_h / (2*BOARD_HEIGHT) - squareSize/2;
\r
3014 backTextureSquareInfo[row][col].y = (BOARD_HEIGHT-1-row) * (dark_h - squareSize) / (BOARD_HEIGHT-1);
\r
3015 backTextureSquareInfo[row][col].mode = GetBackTextureMode(darkBackTextureMode);
\r
3022 /* [AS] Arrow highlighting support */
\r
3024 static double A_WIDTH = 5; /* Width of arrow body */
\r
3026 #define A_HEIGHT_FACTOR 6 /* Length of arrow "point", relative to body width */
\r
3027 #define A_WIDTH_FACTOR 3 /* Width of arrow "point", relative to body width */
\r
3029 static double Sqr( double x )
\r
3034 static int Round( double x )
\r
3036 return (int) (x + 0.5);
\r
3039 /* Draw an arrow between two points using current settings */
\r
3040 VOID DrawArrowBetweenPoints( HDC hdc, int s_x, int s_y, int d_x, int d_y )
\r
3043 double dx, dy, j, k, x, y;
\r
3045 if( d_x == s_x ) {
\r
3046 int h = (d_y > s_y) ? +A_WIDTH*A_HEIGHT_FACTOR : -A_WIDTH*A_HEIGHT_FACTOR;
\r
3048 arrow[0].x = s_x + A_WIDTH + 0.5;
\r
3051 arrow[1].x = s_x + A_WIDTH + 0.5;
\r
3052 arrow[1].y = d_y - h;
\r
3054 arrow[2].x = arrow[1].x + A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;
\r
3055 arrow[2].y = d_y - h;
\r
3060 arrow[5].x = arrow[1].x - 2*A_WIDTH + 0.5;
\r
3061 arrow[5].y = d_y - h;
\r
3063 arrow[4].x = arrow[5].x - A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;
\r
3064 arrow[4].y = d_y - h;
\r
3066 arrow[6].x = arrow[1].x - 2*A_WIDTH + 0.5;
\r
3069 else if( d_y == s_y ) {
\r
3070 int w = (d_x > s_x) ? +A_WIDTH*A_HEIGHT_FACTOR : -A_WIDTH*A_HEIGHT_FACTOR;
\r
3073 arrow[0].y = s_y + A_WIDTH + 0.5;
\r
3075 arrow[1].x = d_x - w;
\r
3076 arrow[1].y = s_y + A_WIDTH + 0.5;
\r
3078 arrow[2].x = d_x - w;
\r
3079 arrow[2].y = arrow[1].y + A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;
\r
3084 arrow[5].x = d_x - w;
\r
3085 arrow[5].y = arrow[1].y - 2*A_WIDTH + 0.5;
\r
3087 arrow[4].x = d_x - w;
\r
3088 arrow[4].y = arrow[5].y - A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;
\r
3091 arrow[6].y = arrow[1].y - 2*A_WIDTH + 0.5;
\r
3094 /* [AS] Needed a lot of paper for this! :-) */
\r
3095 dy = (double) (d_y - s_y) / (double) (d_x - s_x);
\r
3096 dx = (double) (s_x - d_x) / (double) (s_y - d_y);
\r
3098 j = sqrt( Sqr(A_WIDTH) / (1.0 + Sqr(dx)) );
\r
3100 k = sqrt( Sqr(A_WIDTH*A_HEIGHT_FACTOR) / (1.0 + Sqr(dy)) );
\r
3105 arrow[0].x = Round(x - j);
\r
3106 arrow[0].y = Round(y + j*dx);
\r
3108 arrow[1].x = Round(arrow[0].x + 2*j); // [HGM] prevent width to be affected by rounding twice
\r
3109 arrow[1].y = Round(arrow[0].y - 2*j*dx);
\r
3112 x = (double) d_x - k;
\r
3113 y = (double) d_y - k*dy;
\r
3116 x = (double) d_x + k;
\r
3117 y = (double) d_y + k*dy;
\r
3120 x = Round(x); y = Round(y); // [HGM] make sure width of shaft is rounded the same way on both ends
\r
3122 arrow[6].x = Round(x - j);
\r
3123 arrow[6].y = Round(y + j*dx);
\r
3125 arrow[2].x = Round(arrow[6].x + 2*j);
\r
3126 arrow[2].y = Round(arrow[6].y - 2*j*dx);
\r
3128 arrow[3].x = Round(arrow[2].x + j*(A_WIDTH_FACTOR-1));
\r
3129 arrow[3].y = Round(arrow[2].y - j*(A_WIDTH_FACTOR-1)*dx);
\r
3134 arrow[5].x = Round(arrow[6].x - j*(A_WIDTH_FACTOR-1));
\r
3135 arrow[5].y = Round(arrow[6].y + j*(A_WIDTH_FACTOR-1)*dx);
\r
3138 Polygon( hdc, arrow, 7 );
\r
3141 /* [AS] Draw an arrow between two squares */
\r
3142 VOID DrawArrowBetweenSquares( HDC hdc, int s_col, int s_row, int d_col, int d_row )
\r
3144 int s_x, s_y, d_x, d_y;
\r
3151 if( s_col == d_col && s_row == d_row ) {
\r
3155 /* Get source and destination points */
\r
3156 SquareToPos( s_row, s_col, &s_x, &s_y);
\r
3157 SquareToPos( d_row, d_col, &d_x, &d_y);
\r
3160 d_y += squareSize / 2 - squareSize / 4; // [HGM] round towards same centers on all sides!
\r
3162 else if( d_y < s_y ) {
\r
3163 d_y += squareSize / 2 + squareSize / 4;
\r
3166 d_y += squareSize / 2;
\r
3170 d_x += squareSize / 2 - squareSize / 4;
\r
3172 else if( d_x < s_x ) {
\r
3173 d_x += squareSize / 2 + squareSize / 4;
\r
3176 d_x += squareSize / 2;
\r
3179 s_x += squareSize / 2;
\r
3180 s_y += squareSize / 2;
\r
3182 /* Adjust width */
\r
3183 A_WIDTH = squareSize / 14.; //[HGM] make float
\r
3186 stLB.lbStyle = BS_SOLID;
\r
3187 stLB.lbColor = appData.highlightArrowColor;
\r
3190 hpen = CreatePen( PS_SOLID, 2, RGB(0x00,0x00,0x00) );
\r
3191 holdpen = SelectObject( hdc, hpen );
\r
3192 hbrush = CreateBrushIndirect( &stLB );
\r
3193 holdbrush = SelectObject( hdc, hbrush );
\r
3195 DrawArrowBetweenPoints( hdc, s_x, s_y, d_x, d_y );
\r
3197 SelectObject( hdc, holdpen );
\r
3198 SelectObject( hdc, holdbrush );
\r
3199 DeleteObject( hpen );
\r
3200 DeleteObject( hbrush );
\r
3203 BOOL HasHighlightInfo()
\r
3205 BOOL result = FALSE;
\r
3207 if( highlightInfo.sq[0].x >= 0 && highlightInfo.sq[0].y >= 0 &&
\r
3208 highlightInfo.sq[1].x >= 0 && highlightInfo.sq[1].y >= 0 )
\r
3216 BOOL IsDrawArrowEnabled()
\r
3218 BOOL result = FALSE;
\r
3220 if( appData.highlightMoveWithArrow && squareSize >= 32 ) {
\r
3227 VOID DrawArrowHighlight( HDC hdc )
\r
3229 if( IsDrawArrowEnabled() && HasHighlightInfo() ) {
\r
3230 DrawArrowBetweenSquares( hdc,
\r
3231 highlightInfo.sq[0].x, highlightInfo.sq[0].y,
\r
3232 highlightInfo.sq[1].x, highlightInfo.sq[1].y );
\r
3236 HRGN GetArrowHighlightClipRegion( HDC hdc )
\r
3238 HRGN result = NULL;
\r
3240 if( HasHighlightInfo() ) {
\r
3241 int x1, y1, x2, y2;
\r
3242 int sx, sy, dx, dy;
\r
3244 SquareToPos(highlightInfo.sq[0].y, highlightInfo.sq[0].x, &x1, &y1 );
\r
3245 SquareToPos(highlightInfo.sq[1].y, highlightInfo.sq[1].x, &x2, &y2 );
\r
3247 sx = MIN( x1, x2 );
\r
3248 sy = MIN( y1, y2 );
\r
3249 dx = MAX( x1, x2 ) + squareSize;
\r
3250 dy = MAX( y1, y2 ) + squareSize;
\r
3252 result = CreateRectRgn( sx, sy, dx, dy );
\r
3259 Warning: this function modifies the behavior of several other functions.
\r
3261 Basically, Winboard is optimized to avoid drawing the whole board if not strictly
\r
3262 needed. Unfortunately, the decision whether or not to perform a full or partial
\r
3263 repaint is scattered all over the place, which is not good for features such as
\r
3264 "arrow highlighting" that require a full repaint of the board.
\r
3266 So, I've tried to patch the code where I thought it made sense (e.g. after or during
\r
3267 user interaction, when speed is not so important) but especially to avoid errors
\r
3268 in the displayed graphics.
\r
3270 In such patched places, I always try refer to this function so there is a single
\r
3271 place to maintain knowledge.
\r
3273 To restore the original behavior, just return FALSE unconditionally.
\r
3275 BOOL IsFullRepaintPreferrable()
\r
3277 BOOL result = FALSE;
\r
3279 if( (appData.highlightLastMove || appData.highlightDragging) && IsDrawArrowEnabled() ) {
\r
3280 /* Arrow may appear on the board */
\r
3288 This function is called by DrawPosition to know whether a full repaint must
\r
3291 Only DrawPosition may directly call this function, which makes use of
\r
3292 some state information. Other function should call DrawPosition specifying
\r
3293 the repaint flag, and can use IsFullRepaintPreferrable if needed.
\r
3295 BOOL DrawPositionNeedsFullRepaint()
\r
3297 BOOL result = FALSE;
\r
3300 Probably a slightly better policy would be to trigger a full repaint
\r
3301 when animInfo.piece changes state (i.e. empty -> non-empty and viceversa),
\r
3302 but animation is fast enough that it's difficult to notice.
\r
3304 if( animInfo.piece == EmptySquare ) {
\r
3305 if( (appData.highlightLastMove || appData.highlightDragging) && IsDrawArrowEnabled() /*&& HasHighlightInfo()*/ ) {
\r
3314 DrawBoardOnDC(HDC hdc, Board board, HDC tmphdc)
\r
3316 int row, column, x, y, square_color, piece_color;
\r
3317 ChessSquare piece;
\r
3319 HDC texture_hdc = NULL;
\r
3321 /* [AS] Initialize background textures if needed */
\r
3322 if( liteBackTexture != NULL || darkBackTexture != NULL ) {
\r
3323 static int backTextureBoardSize; /* [HGM] boardsize: also new texture if board format changed */
\r
3324 if( backTextureSquareSize != squareSize
\r
3325 || backTextureBoardSize != BOARD_WIDTH+BOARD_FILES*BOARD_HEIGHT) {
\r
3326 backTextureBoardSize = BOARD_WIDTH+BOARD_FILES*BOARD_HEIGHT;
\r
3327 backTextureSquareSize = squareSize;
\r
3328 RebuildTextureSquareInfo();
\r
3331 texture_hdc = CreateCompatibleDC( hdc );
\r
3334 for (row = 0; row < BOARD_HEIGHT; row++) {
\r
3335 for (column = 0; column < BOARD_WIDTH; column++) {
\r
3337 SquareToPos(row, column, &x, &y);
\r
3339 piece = board[row][column];
\r
3341 square_color = ((column + row) % 2) == 1;
\r
3342 if( gameInfo.variant == VariantXiangqi ) {
\r
3343 square_color = !InPalace(row, column);
\r
3344 if(BOARD_HEIGHT&1) { if(row==BOARD_HEIGHT/2) square_color ^= 1; }
\r
3345 else if(row < BOARD_HEIGHT/2) square_color ^= 1;
\r
3347 piece_color = (int) piece < (int) BlackPawn;
\r
3350 /* [HGM] holdings file: light square or black */
\r
3351 if(column == BOARD_LEFT-2) {
\r
3352 if( row > BOARD_HEIGHT - gameInfo.holdingsSize - 1 )
\r
3355 DisplayHoldingsCount(hdc, x, y, 0, 0); /* black out */
\r
3359 if(column == BOARD_RGHT + 1 ) {
\r
3360 if( row < gameInfo.holdingsSize )
\r
3363 DisplayHoldingsCount(hdc, x, y, 0, 0);
\r
3367 if(column == BOARD_LEFT-1 ) /* left align */
\r
3368 DisplayHoldingsCount(hdc, x, y, flipView, (int) board[row][column]);
\r
3369 else if( column == BOARD_RGHT) /* right align */
\r
3370 DisplayHoldingsCount(hdc, x, y, !flipView, (int) board[row][column]);
\r
3372 if (appData.monoMode) {
\r
3373 if (piece == EmptySquare) {
\r
3374 BitBlt(hdc, x, y, squareSize, squareSize, 0, 0, 0,
\r
3375 square_color ? WHITENESS : BLACKNESS);
\r
3377 DrawPieceOnDC(hdc, piece, piece_color, square_color, x, y, tmphdc);
\r
3380 else if( appData.useBitmaps && backTextureSquareInfo[row][column].mode > 0 ) {
\r
3381 /* [AS] Draw the square using a texture bitmap */
\r
3382 HBITMAP hbm = SelectObject( texture_hdc, square_color ? liteBackTexture : darkBackTexture );
\r
3383 int r = row, c = column; // [HGM] do not flip board in flipView
\r
3384 if(flipView) { r = BOARD_HEIGHT-1 - r; c = BOARD_WIDTH-1 - c; }
\r
3387 squareSize, squareSize,
\r
3390 backTextureSquareInfo[r][c].mode,
\r
3391 backTextureSquareInfo[r][c].x,
\r
3392 backTextureSquareInfo[r][c].y );
\r
3394 SelectObject( texture_hdc, hbm );
\r
3396 if (piece != EmptySquare) {
\r
3397 DrawPieceOnDC(hdc, piece, piece_color, -1, x, y, tmphdc);
\r
3401 HBRUSH brush = square_color ? lightSquareBrush : darkSquareBrush;
\r
3403 oldBrush = SelectObject(hdc, brush );
\r
3404 BitBlt(hdc, x, y, squareSize, squareSize, 0, 0, 0, PATCOPY);
\r
3405 SelectObject(hdc, oldBrush);
\r
3406 if (piece != EmptySquare)
\r
3407 DrawPieceOnDC(hdc, piece, piece_color, -1, x, y, tmphdc);
\r
3412 if( texture_hdc != NULL ) {
\r
3413 DeleteDC( texture_hdc );
\r
3417 int saveDiagFlag = 0; FILE *diagFile; // [HGM] diag
\r
3418 void fputDW(FILE *f, int x)
\r
3420 fputc(x & 255, f);
\r
3421 fputc(x>>8 & 255, f);
\r
3422 fputc(x>>16 & 255, f);
\r
3423 fputc(x>>24 & 255, f);
\r
3426 #define MAX_CLIPS 200 /* more than enough */
\r
3429 DrawLogoOnDC(HDC hdc, RECT logoRect, HBITMAP logo)
\r
3431 // HBITMAP bufferBitmap;
\r
3436 int w = 100, h = 50;
\r
3438 if(logo == NULL) {
\r
3439 if(!logoHeight) return;
\r
3440 FillRect( hdc, &logoRect, whitePieceBrush );
\r
3442 // GetClientRect(hwndMain, &Rect);
\r
3443 // bufferBitmap = CreateCompatibleBitmap(hdc, Rect.right-Rect.left+1,
\r
3444 // Rect.bottom-Rect.top+1);
\r
3445 tmphdc = CreateCompatibleDC(hdc);
\r
3446 hbm = SelectObject(tmphdc, logo);
\r
3447 if( GetObject( logo, sizeof(bi), &bi ) > 0 ) {
\r
3451 StretchBlt(hdc, logoRect.left, logoRect.top, logoRect.right - logoRect.left,
\r
3452 logoRect.bottom - logoRect.top, tmphdc, 0, 0, w, h, SRCCOPY);
\r
3453 SelectObject(tmphdc, hbm);
\r
3461 HDC hdc = GetDC(hwndMain);
\r
3462 HBITMAP whiteLogo = (HBITMAP) first.programLogo, blackLogo = (HBITMAP) second.programLogo;
\r
3463 if(appData.autoLogo) {
\r
3465 switch(gameMode) { // pick logos based on game mode
\r
3466 case IcsObserving:
\r
3467 whiteLogo = second.programLogo; // ICS logo
\r
3468 blackLogo = second.programLogo;
\r
3471 case IcsPlayingWhite:
\r
3472 if(!appData.zippyPlay) whiteLogo = userLogo;
\r
3473 blackLogo = second.programLogo; // ICS logo
\r
3475 case IcsPlayingBlack:
\r
3476 whiteLogo = second.programLogo; // ICS logo
\r
3477 blackLogo = appData.zippyPlay ? first.programLogo : userLogo;
\r
3479 case TwoMachinesPlay:
\r
3480 if(first.twoMachinesColor[0] == 'b') {
\r
3481 whiteLogo = second.programLogo;
\r
3482 blackLogo = first.programLogo;
\r
3485 case MachinePlaysWhite:
\r
3486 blackLogo = userLogo;
\r
3488 case MachinePlaysBlack:
\r
3489 whiteLogo = userLogo;
\r
3490 blackLogo = first.programLogo;
\r
3493 DrawLogoOnDC(hdc, leftLogoRect, flipClock ? blackLogo : whiteLogo);
\r
3494 DrawLogoOnDC(hdc, rightLogoRect, flipClock ? whiteLogo : blackLogo);
\r
3495 ReleaseDC(hwndMain, hdc);
\r
3500 UpdateLogos(int display)
\r
3501 { // called after loading new engine(s), in tourney or from menu
\r
3502 LoadLogo(&first, 0, FALSE);
\r
3503 LoadLogo(&second, 1, appData.icsActive);
\r
3504 InitDrawingSizes(-2, 0); // adapt layout of board window to presence/absence of logos
\r
3505 if(display) DisplayLogos();
\r
3508 static HDC hdcSeek;
\r
3510 // [HGM] seekgraph
\r
3511 void DrawSeekAxis( int x, int y, int xTo, int yTo )
\r
3514 HPEN hp = SelectObject( hdcSeek, gridPen );
\r
3515 MoveToEx( hdcSeek, boardRect.left+x, boardRect.top+y, &stPt );
\r
3516 LineTo( hdcSeek, boardRect.left+xTo, boardRect.top+yTo );
\r
3517 SelectObject( hdcSeek, hp );
\r
3520 // front-end wrapper for drawing functions to do rectangles
\r
3521 void DrawSeekBackground( int left, int top, int right, int bottom )
\r
3526 if (hdcSeek == NULL) {
\r
3527 hdcSeek = GetDC(hwndMain);
\r
3528 if (!appData.monoMode) {
\r
3529 SelectPalette(hdcSeek, hPal, FALSE);
\r
3530 RealizePalette(hdcSeek);
\r
3533 hp = SelectObject( hdcSeek, gridPen );
\r
3534 rc.top = boardRect.top+top; rc.left = boardRect.left+left;
\r
3535 rc.bottom = boardRect.top+bottom; rc.right = boardRect.left+right;
\r
3536 FillRect( hdcSeek, &rc, lightSquareBrush );
\r
3537 SelectObject( hdcSeek, hp );
\r
3540 // front-end wrapper for putting text in graph
\r
3541 void DrawSeekText(char *buf, int x, int y)
\r
3544 SetBkMode( hdcSeek, TRANSPARENT );
\r
3545 GetTextExtentPoint32( hdcSeek, buf, strlen(buf), &stSize );
\r
3546 TextOut( hdcSeek, boardRect.left+x-3, boardRect.top+y-stSize.cy/2, buf, strlen(buf) );
\r
3549 void DrawSeekDot(int x, int y, int color)
\r
3551 int square = color & 0x80;
\r
3552 HBRUSH oldBrush = SelectObject(hdcSeek,
\r
3553 color == 0 ? markerBrush : color == 1 ? darkSquareBrush : explodeBrush);
\r
3556 Rectangle(hdcSeek, boardRect.left+x - squareSize/9, boardRect.top+y - squareSize/9,
\r
3557 boardRect.left+x + squareSize/9, boardRect.top+y + squareSize/9);
\r
3559 Ellipse(hdcSeek, boardRect.left+x - squareSize/8, boardRect.top+y - squareSize/8,
\r
3560 boardRect.left+x + squareSize/8, boardRect.top+y + squareSize/8);
\r
3561 SelectObject(hdcSeek, oldBrush);
\r
3564 void DrawSeekOpen()
\r
3568 void DrawSeekClose()
\r
3573 HDCDrawPosition(HDC hdc, BOOLEAN repaint, Board board)
\r
3575 static Board lastReq[2], lastDrawn[2];
\r
3576 static HighlightInfo lastDrawnHighlight, lastDrawnPremove;
\r
3577 static int lastDrawnFlipView = 0;
\r
3578 static int lastReqValid[2] = {0, 0}, lastDrawnValid[2] = {0, 0};
\r
3579 int releaseDC, x, y, x2, y2, row, column, num_clips = 0, i;
\r
3582 HBITMAP bufferBitmap;
\r
3583 HBITMAP oldBitmap;
\r
3585 HRGN clips[MAX_CLIPS];
\r
3586 ChessSquare dragged_piece = EmptySquare;
\r
3587 int nr = twoBoards*partnerUp;
\r
3589 /* I'm undecided on this - this function figures out whether a full
\r
3590 * repaint is necessary on its own, so there's no real reason to have the
\r
3591 * caller tell it that. I think this can safely be set to FALSE - but
\r
3592 * if we trust the callers not to request full repaints unnessesarily, then
\r
3593 * we could skip some clipping work. In other words, only request a full
\r
3594 * redraw when the majority of pieces have changed positions (ie. flip,
\r
3595 * gamestart and similar) --Hawk
\r
3597 Boolean fullrepaint = repaint;
\r
3599 if(DrawSeekGraph()) return; // [HG} seekgraph: suppress printing board if seek graph up
\r
3601 if( DrawPositionNeedsFullRepaint() ) {
\r
3602 fullrepaint = TRUE;
\r
3605 if (board == NULL) {
\r
3606 if (!lastReqValid[nr]) {
\r
3609 board = lastReq[nr];
\r
3611 CopyBoard(lastReq[nr], board);
\r
3612 lastReqValid[nr] = 1;
\r
3615 if (doingSizing) {
\r
3619 if (IsIconic(hwndMain)) {
\r
3623 if (hdc == NULL) {
\r
3624 hdc = GetDC(hwndMain);
\r
3625 if (!appData.monoMode) {
\r
3626 SelectPalette(hdc, hPal, FALSE);
\r
3627 RealizePalette(hdc);
\r
3631 releaseDC = FALSE;
\r
3634 /* Create some work-DCs */
\r
3635 hdcmem = CreateCompatibleDC(hdc);
\r
3636 tmphdc = CreateCompatibleDC(hdc);
\r
3638 /* If dragging is in progress, we temporarely remove the piece */
\r
3639 /* [HGM] or temporarily decrease count if stacked */
\r
3640 /* !! Moved to before board compare !! */
\r
3641 if (dragInfo.from.x >= 0 && dragInfo.pos.x >= 0) {
\r
3642 dragged_piece = board[dragInfo.from.y][dragInfo.from.x];
\r
3643 if(dragInfo.from.x == BOARD_LEFT-2 ) {
\r
3644 if(--board[dragInfo.from.y][dragInfo.from.x+1] == 0 )
\r
3645 board[dragInfo.from.y][dragInfo.from.x] = EmptySquare;
\r
3647 if(dragInfo.from.x == BOARD_RGHT+1) {
\r
3648 if(--board[dragInfo.from.y][dragInfo.from.x-1] == 0 )
\r
3649 board[dragInfo.from.y][dragInfo.from.x] = EmptySquare;
\r
3651 board[dragInfo.from.y][dragInfo.from.x] = gatingPiece;
\r
3654 /* Figure out which squares need updating by comparing the
\r
3655 * newest board with the last drawn board and checking if
\r
3656 * flipping has changed.
\r
3658 if (!fullrepaint && lastDrawnValid[nr] && (nr == 1 || lastDrawnFlipView == flipView)) {
\r
3659 for (row = 0; row < BOARD_HEIGHT; row++) { /* [HGM] true size, not 8 */
\r
3660 for (column = 0; column < BOARD_WIDTH; column++) {
\r
3661 if (lastDrawn[nr][row][column] != board[row][column]) {
\r
3662 SquareToPos(row, column, &x, &y);
\r
3663 clips[num_clips++] =
\r
3664 CreateRectRgn(x, y, x + squareSize, y + squareSize);
\r
3668 if(nr == 0) { // [HGM] dual: no highlights on second board
\r
3669 for (i=0; i<2; i++) {
\r
3670 if (lastDrawnHighlight.sq[i].x != highlightInfo.sq[i].x ||
\r
3671 lastDrawnHighlight.sq[i].y != highlightInfo.sq[i].y) {
\r
3672 if (lastDrawnHighlight.sq[i].x >= 0 &&
\r
3673 lastDrawnHighlight.sq[i].y >= 0) {
\r
3674 SquareToPos(lastDrawnHighlight.sq[i].y,
\r
3675 lastDrawnHighlight.sq[i].x, &x, &y);
\r
3676 clips[num_clips++] =
\r
3677 CreateRectRgn(x - lineGap, y - lineGap,
\r
3678 x + squareSize + lineGap, y + squareSize + lineGap);
\r
3680 if (highlightInfo.sq[i].x >= 0 && highlightInfo.sq[i].y >= 0) {
\r
3681 SquareToPos(highlightInfo.sq[i].y, highlightInfo.sq[i].x, &x, &y);
\r
3682 clips[num_clips++] =
\r
3683 CreateRectRgn(x - lineGap, y - lineGap,
\r
3684 x + squareSize + lineGap, y + squareSize + lineGap);
\r
3688 for (i=0; i<2; i++) {
\r
3689 if (lastDrawnPremove.sq[i].x != premoveHighlightInfo.sq[i].x ||
\r
3690 lastDrawnPremove.sq[i].y != premoveHighlightInfo.sq[i].y) {
\r
3691 if (lastDrawnPremove.sq[i].x >= 0 &&
\r
3692 lastDrawnPremove.sq[i].y >= 0) {
\r
3693 SquareToPos(lastDrawnPremove.sq[i].y,
\r
3694 lastDrawnPremove.sq[i].x, &x, &y);
\r
3695 clips[num_clips++] =
\r
3696 CreateRectRgn(x - lineGap, y - lineGap,
\r
3697 x + squareSize + lineGap, y + squareSize + lineGap);
\r
3699 if (premoveHighlightInfo.sq[i].x >= 0 &&
\r
3700 premoveHighlightInfo.sq[i].y >= 0) {
\r
3701 SquareToPos(premoveHighlightInfo.sq[i].y,
\r
3702 premoveHighlightInfo.sq[i].x, &x, &y);
\r
3703 clips[num_clips++] =
\r
3704 CreateRectRgn(x - lineGap, y - lineGap,
\r
3705 x + squareSize + lineGap, y + squareSize + lineGap);
\r
3709 } else { // nr == 1
\r
3710 partnerHighlightInfo.sq[0].y = board[EP_STATUS-4];
\r
3711 partnerHighlightInfo.sq[0].x = board[EP_STATUS-3];
\r
3712 partnerHighlightInfo.sq[1].y = board[EP_STATUS-2];
\r
3713 partnerHighlightInfo.sq[1].x = board[EP_STATUS-1];
\r
3714 for (i=0; i<2; i++) {
\r
3715 if (partnerHighlightInfo.sq[i].x >= 0 &&
\r
3716 partnerHighlightInfo.sq[i].y >= 0) {
\r
3717 SquareToPos(partnerHighlightInfo.sq[i].y,
\r
3718 partnerHighlightInfo.sq[i].x, &x, &y);
\r
3719 clips[num_clips++] =
\r
3720 CreateRectRgn(x - lineGap, y - lineGap,
\r
3721 x + squareSize + lineGap, y + squareSize + lineGap);
\r
3723 if (oldPartnerHighlight.sq[i].x >= 0 &&
\r
3724 oldPartnerHighlight.sq[i].y >= 0) {
\r
3725 SquareToPos(oldPartnerHighlight.sq[i].y,
\r
3726 oldPartnerHighlight.sq[i].x, &x, &y);
\r
3727 clips[num_clips++] =
\r
3728 CreateRectRgn(x - lineGap, y - lineGap,
\r
3729 x + squareSize + lineGap, y + squareSize + lineGap);
\r
3734 fullrepaint = TRUE;
\r
3737 /* Create a buffer bitmap - this is the actual bitmap
\r
3738 * being written to. When all the work is done, we can
\r
3739 * copy it to the real DC (the screen). This avoids
\r
3740 * the problems with flickering.
\r
3742 GetClientRect(hwndMain, &Rect);
\r
3743 bufferBitmap = CreateCompatibleBitmap(hdc, Rect.right-Rect.left+1,
\r
3744 Rect.bottom-Rect.top+1);
\r
3745 oldBitmap = SelectObject(hdcmem, bufferBitmap);
\r
3746 if (!appData.monoMode) {
\r
3747 SelectPalette(hdcmem, hPal, FALSE);
\r
3750 /* Create clips for dragging */
\r
3751 if (!fullrepaint) {
\r
3752 if (dragInfo.from.x >= 0) {
\r
3753 SquareToPos(dragInfo.from.y, dragInfo.from.x, &x, &y);
\r
3754 clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);
\r
3756 if (dragInfo.start.x >= 0) {
\r
3757 SquareToPos(dragInfo.start.y, dragInfo.start.x, &x, &y);
\r
3758 clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);
\r
3760 if (dragInfo.pos.x >= 0) {
\r
3761 x = dragInfo.pos.x - squareSize / 2;
\r
3762 y = dragInfo.pos.y - squareSize / 2;
\r
3763 clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);
\r
3765 if (dragInfo.lastpos.x >= 0) {
\r
3766 x = dragInfo.lastpos.x - squareSize / 2;
\r
3767 y = dragInfo.lastpos.y - squareSize / 2;
\r
3768 clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);
\r
3772 /* Are we animating a move?
\r
3774 * - remove the piece from the board (temporarely)
\r
3775 * - calculate the clipping region
\r
3777 if (!fullrepaint) {
\r
3778 if (animInfo.piece != EmptySquare) {
\r
3779 board[animInfo.from.y][animInfo.from.x] = EmptySquare;
\r
3780 x = boardRect.left + animInfo.lastpos.x;
\r
3781 y = boardRect.top + animInfo.lastpos.y;
\r
3782 x2 = boardRect.left + animInfo.pos.x;
\r
3783 y2 = boardRect.top + animInfo.pos.y;
\r
3784 clips[num_clips++] = CreateRectRgn(MIN(x,x2), MIN(y,y2), MAX(x,x2)+squareSize, MAX(y,y2)+squareSize);
\r
3785 /* Slight kludge. The real problem is that after AnimateMove is
\r
3786 done, the position on the screen does not match lastDrawn.
\r
3787 This currently causes trouble only on e.p. captures in
\r
3788 atomic, where the piece moves to an empty square and then
\r
3789 explodes. The old and new positions both had an empty square
\r
3790 at the destination, but animation has drawn a piece there and
\r
3791 we have to remember to erase it. [HGM] moved until after setting lastDrawn */
\r
3792 lastDrawn[0][animInfo.to.y][animInfo.to.x] = animInfo.piece;
\r
3796 /* No clips? Make sure we have fullrepaint set to TRUE */
\r
3797 if (num_clips == 0)
\r
3798 fullrepaint = TRUE;
\r
3800 /* Set clipping on the memory DC */
\r
3801 if (!fullrepaint) {
\r
3802 SelectClipRgn(hdcmem, clips[0]);
\r
3803 for (x = 1; x < num_clips; x++) {
\r
3804 if (ExtSelectClipRgn(hdcmem, clips[x], RGN_OR) == ERROR)
\r
3805 abort(); // this should never ever happen!
\r
3809 /* Do all the drawing to the memory DC */
\r
3810 if(explodeInfo.radius) { // [HGM] atomic
\r
3812 int x, y, r=(explodeInfo.radius * squareSize)/100;
\r
3813 ChessSquare piece = board[explodeInfo.fromY][explodeInfo.fromX];
\r
3814 board[explodeInfo.fromY][explodeInfo.fromX] = EmptySquare; // suppress display of capturer
\r
3815 SquareToPos(explodeInfo.toY, explodeInfo.toX, &x, &y);
\r
3816 x += squareSize/2;
\r
3817 y += squareSize/2;
\r
3818 if(!fullrepaint) {
\r
3819 clips[num_clips] = CreateRectRgn(x-r, y-r, x+r, y+r);
\r
3820 ExtSelectClipRgn(hdcmem, clips[num_clips++], RGN_OR);
\r
3822 DrawGridOnDC(hdcmem);
\r
3823 DrawHighlightsOnDC(hdcmem, &highlightInfo, HIGHLIGHT_PEN);
\r
3824 DrawHighlightsOnDC(hdcmem, &premoveHighlightInfo, PREMOVE_PEN);
\r
3825 DrawBoardOnDC(hdcmem, board, tmphdc);
\r
3826 board[explodeInfo.fromY][explodeInfo.fromX] = piece;
\r
3827 oldBrush = SelectObject(hdcmem, explodeBrush);
\r
3828 Ellipse(hdcmem, x-r, y-r, x+r, y+r);
\r
3829 SelectObject(hdcmem, oldBrush);
\r
3831 DrawGridOnDC(hdcmem);
\r
3832 if(nr == 0) { // [HGM] dual: decide which highlights to draw
\r
3833 DrawHighlightsOnDC(hdcmem, &highlightInfo, HIGHLIGHT_PEN);
\r
3834 DrawHighlightsOnDC(hdcmem, &premoveHighlightInfo, PREMOVE_PEN);
\r
3836 DrawHighlightsOnDC(hdcmem, &partnerHighlightInfo, HIGHLIGHT_PEN);
\r
3837 oldPartnerHighlight = partnerHighlightInfo;
\r
3839 DrawBoardOnDC(hdcmem, board, tmphdc);
\r
3841 if(nr == 0) // [HGM] dual: markers only on left board
\r
3842 for (row = 0; row < BOARD_HEIGHT; row++) {
\r
3843 for (column = 0; column < BOARD_WIDTH; column++) {
\r
3844 if (marker[row][column]) { // marker changes only occur with full repaint!
\r
3845 HBRUSH oldBrush = SelectObject(hdcmem,
\r
3846 marker[row][column] == 2 ? markerBrush : explodeBrush);
\r
3847 SquareToPos(row, column, &x, &y);
\r
3848 Ellipse(hdcmem, x + squareSize/4, y + squareSize/4,
\r
3849 x + 3*squareSize/4, y + 3*squareSize/4);
\r
3850 SelectObject(hdcmem, oldBrush);
\r
3855 if( appData.highlightMoveWithArrow ) {
\r
3856 DrawArrowHighlight(hdcmem);
\r
3859 DrawCoordsOnDC(hdcmem);
\r
3861 CopyBoard(lastDrawn[nr], board); /* [HGM] Moved to here from end of routine, */
\r
3862 /* to make sure lastDrawn contains what is actually drawn */
\r
3864 /* Put the dragged piece back into place and draw it (out of place!) */
\r
3865 if (dragged_piece != EmptySquare) {
\r
3866 /* [HGM] or restack */
\r
3867 if(dragInfo.from.x == BOARD_LEFT-2 )
\r
3868 board[dragInfo.from.y][dragInfo.from.x+1]++;
\r
3870 if(dragInfo.from.x == BOARD_RGHT+1 )
\r
3871 board[dragInfo.from.y][dragInfo.from.x-1]++;
\r
3872 board[dragInfo.from.y][dragInfo.from.x] = dragged_piece;
\r
3873 x = dragInfo.pos.x - squareSize / 2;
\r
3874 y = dragInfo.pos.y - squareSize / 2;
\r
3875 DrawPieceOnDC(hdcmem, dragInfo.piece,
\r
3876 ((int) dragInfo.piece < (int) BlackPawn),
\r
3877 (dragInfo.from.y + dragInfo.from.x) % 2, x, y, tmphdc);
\r
3880 /* Put the animated piece back into place and draw it */
\r
3881 if (animInfo.piece != EmptySquare) {
\r
3882 board[animInfo.from.y][animInfo.from.x] = animInfo.piece;
\r
3883 x = boardRect.left + animInfo.pos.x;
\r
3884 y = boardRect.top + animInfo.pos.y;
\r
3885 DrawPieceOnDC(hdcmem, animInfo.piece,
\r
3886 ((int) animInfo.piece < (int) BlackPawn),
\r
3887 (animInfo.from.y + animInfo.from.x) % 2, x, y, tmphdc);
\r
3890 /* Release the bufferBitmap by selecting in the old bitmap
\r
3891 * and delete the memory DC
\r
3893 SelectObject(hdcmem, oldBitmap);
\r
3896 /* Set clipping on the target DC */
\r
3897 if (!fullrepaint) {
\r
3898 if(nr == 1) for (x = 0; x < num_clips; x++) { // [HGM] dual: translate clips
\r
3900 GetRgnBox(clips[x], &rect);
\r
3901 DeleteObject(clips[x]);
\r
3902 clips[x] = CreateRectRgn(rect.left + wpMain.width/2, rect.top,
\r
3903 rect.right + wpMain.width/2, rect.bottom);
\r
3905 SelectClipRgn(hdc, clips[0]);
\r
3906 for (x = 1; x < num_clips; x++) {
\r
3907 if (ExtSelectClipRgn(hdc, clips[x], RGN_OR) == ERROR)
\r
3908 abort(); // this should never ever happen!
\r
3912 /* Copy the new bitmap onto the screen in one go.
\r
3913 * This way we avoid any flickering
\r
3915 oldBitmap = SelectObject(tmphdc, bufferBitmap);
\r
3916 BitBlt(hdc, boardRect.left + twoBoards*partnerUp*wpMain.width/2, boardRect.top, // [HGM] dual
\r
3917 boardRect.right - boardRect.left,
\r
3918 boardRect.bottom - boardRect.top,
\r
3919 tmphdc, boardRect.left, boardRect.top, SRCCOPY);
\r
3920 if(saveDiagFlag) {
\r
3921 BITMAP b; int i, j=0, m, w, wb, fac=0; char *pData;
\r
3922 BITMAPINFOHEADER bih; int color[16], nrColors=0;
\r
3924 GetObject(bufferBitmap, sizeof(b), &b);
\r
3925 if(pData = malloc(b.bmWidthBytes*b.bmHeight + 10000)) {
\r
3926 bih.biSize = sizeof(BITMAPINFOHEADER);
\r
3927 bih.biWidth = b.bmWidth;
\r
3928 bih.biHeight = b.bmHeight;
\r
3930 bih.biBitCount = b.bmBitsPixel;
\r
3931 bih.biCompression = 0;
\r
3932 bih.biSizeImage = b.bmWidthBytes*b.bmHeight;
\r
3933 bih.biXPelsPerMeter = 0;
\r
3934 bih.biYPelsPerMeter = 0;
\r
3935 bih.biClrUsed = 0;
\r
3936 bih.biClrImportant = 0;
\r
3937 // fprintf(diagFile, "t=%d\nw=%d\nh=%d\nB=%d\nP=%d\nX=%d\n",
\r
3938 // b.bmType, b.bmWidth, b.bmHeight, b.bmWidthBytes, b.bmPlanes, b.bmBitsPixel);
\r
3939 GetDIBits(tmphdc,bufferBitmap,0,b.bmHeight,pData,(BITMAPINFO*)&bih,DIB_RGB_COLORS);
\r
3940 // fprintf(diagFile, "%8x\n", (int) pData);
\r
3942 wb = b.bmWidthBytes;
\r
3944 for(i=0; i<wb*(b.bmHeight - boardRect.top + OUTER_MARGIN)>>2; i++) {
\r
3945 int k = ((int*) pData)[i];
\r
3946 for(j=0; j<nrColors; j++) if(color[j] == k) break;
\r
3947 if(j >= 16) break;
\r
3949 if(j >= nrColors) nrColors = j+1;
\r
3951 if(j<16) { // 16 colors is enough. Compress to 4 bits per pixel
\r
3953 for(i=0; i<b.bmHeight - boardRect.top + OUTER_MARGIN; i++) {
\r
3954 for(w=0; w<(wb>>2); w+=2) {
\r
3955 int k = ((int*) pData)[(wb*i>>2) + w];
\r
3956 for(j=0; j<nrColors; j++) if(color[j] == k) break;
\r
3957 k = ((int*) pData)[(wb*i>>2) + w + 1];
\r
3958 for(m=0; m<nrColors; m++) if(color[m] == k) break;
\r
3959 pData[p++] = m | j<<4;
\r
3961 while(p&3) pData[p++] = 0;
\r
3964 wb = ((wb+31)>>5)<<2;
\r
3966 // write BITMAPFILEHEADER
\r
3967 fprintf(diagFile, "BM");
\r
3968 fputDW(diagFile, wb*(b.bmHeight - boardRect.top + OUTER_MARGIN)+0x36 + (fac?64:0));
\r
3969 fputDW(diagFile, 0);
\r
3970 fputDW(diagFile, 0x36 + (fac?64:0));
\r
3971 // write BITMAPINFOHEADER
\r
3972 fputDW(diagFile, 40);
\r
3973 fputDW(diagFile, b.bmWidth);
\r
3974 fputDW(diagFile, b.bmHeight - boardRect.top + OUTER_MARGIN);
\r
3975 if(fac) fputDW(diagFile, 0x040001); // planes and bits/pixel
\r
3976 else fputDW(diagFile, 0x200001); // planes and bits/pixel
\r
3977 fputDW(diagFile, 0);
\r
3978 fputDW(diagFile, 0);
\r
3979 fputDW(diagFile, 0);
\r
3980 fputDW(diagFile, 0);
\r
3981 fputDW(diagFile, 0);
\r
3982 fputDW(diagFile, 0);
\r
3983 // write color table
\r
3985 for(i=0; i<16; i++) fputDW(diagFile, color[i]);
\r
3986 // write bitmap data
\r
3987 for(i=0; i<wb*(b.bmHeight - boardRect.top + OUTER_MARGIN); i++)
\r
3988 fputc(pData[i], diagFile);
\r
3993 SelectObject(tmphdc, oldBitmap);
\r
3995 /* Massive cleanup */
\r
3996 for (x = 0; x < num_clips; x++)
\r
3997 DeleteObject(clips[x]);
\r
4000 DeleteObject(bufferBitmap);
\r
4003 ReleaseDC(hwndMain, hdc);
\r
4005 if (lastDrawnFlipView != flipView && nr == 0) {
\r
4007 CheckMenuItem(GetMenu(hwndMain),IDM_FlipView, MF_BYCOMMAND|MF_CHECKED);
\r
4009 CheckMenuItem(GetMenu(hwndMain),IDM_FlipView, MF_BYCOMMAND|MF_UNCHECKED);
\r
4012 /* CopyBoard(lastDrawn, board);*/
\r
4013 lastDrawnHighlight = highlightInfo;
\r
4014 lastDrawnPremove = premoveHighlightInfo;
\r
4015 lastDrawnFlipView = flipView;
\r
4016 lastDrawnValid[nr] = 1;
\r
4019 /* [HGM] diag: Save the current board display to the given open file and close the file */
\r
4024 saveDiagFlag = 1; diagFile = f;
\r
4025 HDCDrawPosition(NULL, TRUE, NULL);
\r
4033 /*---------------------------------------------------------------------------*\
\r
4034 | CLIENT PAINT PROCEDURE
\r
4035 | This is the main event-handler for the WM_PAINT message.
\r
4037 \*---------------------------------------------------------------------------*/
\r
4039 PaintProc(HWND hwnd)
\r
4045 if((hdc = BeginPaint(hwnd, &ps))) {
\r
4046 if (IsIconic(hwnd)) {
\r
4047 DrawIcon(hdc, 2, 2, iconCurrent);
\r
4049 if (!appData.monoMode) {
\r
4050 SelectPalette(hdc, hPal, FALSE);
\r
4051 RealizePalette(hdc);
\r
4053 HDCDrawPosition(hdc, 1, NULL);
\r
4054 if(twoBoards) { // [HGM] dual: also redraw other board in other orientation
\r
4055 flipView = !flipView; partnerUp = !partnerUp;
\r
4056 HDCDrawPosition(hdc, 1, NULL);
\r
4057 flipView = !flipView; partnerUp = !partnerUp;
\r
4060 SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);
\r
4061 ExtTextOut(hdc, messageRect.left, messageRect.top,
\r
4062 ETO_CLIPPED|ETO_OPAQUE,
\r
4063 &messageRect, messageText, strlen(messageText), NULL);
\r
4064 SelectObject(hdc, oldFont);
\r
4065 DisplayBothClocks();
\r
4068 EndPaint(hwnd,&ps);
\r
4076 * If the user selects on a border boundary, return -1; if off the board,
\r
4077 * return -2. Otherwise map the event coordinate to the square.
\r
4078 * The offset boardRect.left or boardRect.top must already have been
\r
4079 * subtracted from x.
\r
4081 int EventToSquare(x, limit)
\r
4089 if ((x % (squareSize + lineGap)) >= squareSize)
\r
4091 x /= (squareSize + lineGap);
\r
4103 DropEnable dropEnables[] = {
\r
4104 { 'P', DP_Pawn, N_("Pawn") },
\r
4105 { 'N', DP_Knight, N_("Knight") },
\r
4106 { 'B', DP_Bishop, N_("Bishop") },
\r
4107 { 'R', DP_Rook, N_("Rook") },
\r
4108 { 'Q', DP_Queen, N_("Queen") },
\r
4112 SetupDropMenu(HMENU hmenu)
\r
4114 int i, count, enable;
\r
4116 extern char white_holding[], black_holding[];
\r
4117 char item[MSG_SIZ];
\r
4119 for (i=0; i<sizeof(dropEnables)/sizeof(DropEnable); i++) {
\r
4120 p = strchr(gameMode == IcsPlayingWhite ? white_holding : black_holding,
\r
4121 dropEnables[i].piece);
\r
4123 while (p && *p++ == dropEnables[i].piece) count++;
\r
4124 snprintf(item, MSG_SIZ, "%s %d", T_(dropEnables[i].name), count);
\r
4125 enable = count > 0 || !appData.testLegality
\r
4126 /*!!temp:*/ || (gameInfo.variant == VariantCrazyhouse
\r
4127 && !appData.icsActive);
\r
4128 ModifyMenu(hmenu, dropEnables[i].command,
\r
4129 MF_BYCOMMAND | (enable ? MF_ENABLED : MF_GRAYED) | MF_STRING,
\r
4130 dropEnables[i].command, item);
\r
4134 void DragPieceBegin(int x, int y, Boolean instantly)
\r
4136 dragInfo.lastpos.x = boardRect.left + x;
\r
4137 dragInfo.lastpos.y = boardRect.top + y;
\r
4138 if(instantly) dragInfo.pos = dragInfo.lastpos;
\r
4139 dragInfo.from.x = fromX;
\r
4140 dragInfo.from.y = fromY;
\r
4141 dragInfo.piece = boards[currentMove][fromY][fromX];
\r
4142 dragInfo.start = dragInfo.from;
\r
4143 SetCapture(hwndMain);
\r
4146 void DragPieceEnd(int x, int y)
\r
4149 dragInfo.start.x = dragInfo.start.y = -1;
\r
4150 dragInfo.from = dragInfo.start;
\r
4151 dragInfo.pos = dragInfo.lastpos = dragInfo.start;
\r
4154 void ChangeDragPiece(ChessSquare piece)
\r
4156 dragInfo.piece = piece;
\r
4159 /* Event handler for mouse messages */
\r
4161 MouseEvent(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
\r
4165 static int recursive = 0;
\r
4167 BOOLEAN forceFullRepaint = IsFullRepaintPreferrable(); /* [AS] */
\r
4170 if (message == WM_MBUTTONUP) {
\r
4171 /* Hideous kludge to fool TrackPopupMenu into paying attention
\r
4172 to the middle button: we simulate pressing the left button too!
\r
4174 PostMessage(hwnd, WM_LBUTTONDOWN, wParam, lParam);
\r
4175 PostMessage(hwnd, WM_LBUTTONUP, wParam, lParam);
\r
4181 pt.x = LOWORD(lParam);
\r
4182 pt.y = HIWORD(lParam);
\r
4183 x = EventToSquare(pt.x - boardRect.left, BOARD_WIDTH);
\r
4184 y = EventToSquare(pt.y - boardRect.top, BOARD_HEIGHT);
\r
4185 if (!flipView && y >= 0) {
\r
4186 y = BOARD_HEIGHT - 1 - y;
\r
4188 if (flipView && x >= 0) {
\r
4189 x = BOARD_WIDTH - 1 - x;
\r
4192 shiftKey = GetKeyState(VK_SHIFT) < 0; // [HGM] remember last shift status
\r
4194 switch (message) {
\r
4195 case WM_LBUTTONDOWN:
\r
4196 if (PtInRect((LPRECT) &whiteRect, pt)) {
\r
4197 ClockClick(flipClock); break;
\r
4198 } else if (PtInRect((LPRECT) &blackRect, pt)) {
\r
4199 ClockClick(!flipClock); break;
\r
4201 dragInfo.start.x = dragInfo.start.y = -1;
\r
4202 dragInfo.from = dragInfo.start;
\r
4203 if(fromX == -1 && frozen) { // not sure where this is for
\r
4204 fromX = fromY = -1;
\r
4205 DrawPosition(forceFullRepaint || FALSE, NULL); /* [AS] */
\r
4208 LeftClick(Press, pt.x - boardRect.left, pt.y - boardRect.top);
\r
4209 DrawPosition(TRUE, NULL);
\r
4212 case WM_LBUTTONUP:
\r
4213 LeftClick(Release, pt.x - boardRect.left, pt.y - boardRect.top);
\r
4214 DrawPosition(TRUE, NULL);
\r
4217 case WM_MOUSEMOVE:
\r
4218 if(SeekGraphClick(Press, pt.x - boardRect.left, pt.y - boardRect.top, 1)) break;
\r
4219 if(PromoScroll(pt.x - boardRect.left, pt.y - boardRect.top)) break;
\r
4220 MovePV(pt.x - boardRect.left, pt.y - boardRect.top, boardRect.bottom - boardRect.top);
\r
4221 if ((appData.animateDragging || appData.highlightDragging)
\r
4222 && (wParam & MK_LBUTTON)
\r
4223 && dragInfo.from.x >= 0)
\r
4225 BOOL full_repaint = FALSE;
\r
4227 if (appData.animateDragging) {
\r
4228 dragInfo.pos = pt;
\r
4230 if (appData.highlightDragging) {
\r
4231 SetHighlights(fromX, fromY, x, y);
\r
4232 if( IsDrawArrowEnabled() && (x < 0 || x >= BOARD_WIDTH || y < 0 || y >= BOARD_HEIGHT) ) {
\r
4233 full_repaint = TRUE;
\r
4237 DrawPosition( full_repaint, NULL);
\r
4239 dragInfo.lastpos = dragInfo.pos;
\r
4243 case WM_MOUSEWHEEL: // [DM]
\r
4244 { static int lastDir = 0; // [HGM] build in some hysteresis to avoid spurious events
\r
4245 /* Mouse Wheel is being rolled forward
\r
4246 * Play moves forward
\r
4248 if((short)HIWORD(wParam) < 0 && currentMove < forwardMostMove)
\r
4249 { if(lastDir == 1) ForwardEvent(); else lastDir = 1; } // [HGM] suppress first event in direction
\r
4250 /* Mouse Wheel is being rolled backward
\r
4251 * Play moves backward
\r
4253 if((short)HIWORD(wParam) > 0 && currentMove > backwardMostMove)
\r
4254 { if(lastDir == -1) BackwardEvent(); else lastDir = -1; }
\r
4258 case WM_MBUTTONUP:
\r
4259 case WM_RBUTTONUP:
\r
4261 RightClick(Release, pt.x - boardRect.left, pt.y - boardRect.top, &fromX, &fromY);
\r
4264 case WM_MBUTTONDOWN:
\r
4265 case WM_RBUTTONDOWN:
\r
4268 fromX = fromY = -1;
\r
4269 dragInfo.pos.x = dragInfo.pos.y = -1;
\r
4270 dragInfo.start.x = dragInfo.start.y = -1;
\r
4271 dragInfo.from = dragInfo.start;
\r
4272 dragInfo.lastpos = dragInfo.pos;
\r
4273 if (appData.highlightDragging) {
\r
4274 ClearHighlights();
\r
4277 /* [HGM] right mouse button in clock area edit-game mode ups clock */
\r
4278 if (PtInRect((LPRECT) &whiteRect, pt)) {
\r
4279 if (GetKeyState(VK_SHIFT) < 0) AdjustClock(flipClock, 1);
\r
4280 } else if (PtInRect((LPRECT) &blackRect, pt)) {
\r
4281 if (GetKeyState(VK_SHIFT) < 0) AdjustClock(!flipClock, 1);
\r
4285 DrawPosition(TRUE, NULL);
\r
4287 menuNr = RightClick(Press, pt.x - boardRect.left, pt.y - boardRect.top, &fromX, &fromY);
\r
4290 if (message == WM_MBUTTONDOWN) {
\r
4291 buttonCount = 3; /* even if system didn't think so */
\r
4292 if (wParam & MK_SHIFT)
\r
4293 MenuPopup(hwnd, pt, LoadMenu(hInst, "BlackPieceMenu"), -1);
\r
4295 MenuPopup(hwnd, pt, LoadMenu(hInst, "WhitePieceMenu"), -1);
\r
4296 } else { /* message == WM_RBUTTONDOWN */
\r
4297 /* Just have one menu, on the right button. Windows users don't
\r
4298 think to try the middle one, and sometimes other software steals
\r
4299 it, or it doesn't really exist. */
\r
4300 if(gameInfo.variant != VariantShogi)
\r
4301 MenuPopup(hwnd, pt, LoadMenu(hInst, "PieceMenu"), -1);
\r
4303 MenuPopup(hwnd, pt, LoadMenu(hInst, "ShogiPieceMenu"), -1);
\r
4307 SetCapture(hwndMain);
\r
4310 hmenu = LoadMenu(hInst, "DropPieceMenu");
\r
4311 SetupDropMenu(hmenu);
\r
4312 MenuPopup(hwnd, pt, hmenu, -1);
\r
4322 /* Preprocess messages for buttons in main window */
\r
4324 ButtonProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
\r
4326 int id = GetWindowLongPtr(hwnd, GWLP_ID);
\r
4329 for (i=0; i<N_BUTTONS; i++) {
\r
4330 if (buttonDesc[i].id == id) break;
\r
4332 if (i == N_BUTTONS) return 0;
\r
4333 switch (message) {
\r
4338 dir = (wParam == VK_LEFT) ? -1 : 1;
\r
4339 SetFocus(buttonDesc[(i + dir + N_BUTTONS) % N_BUTTONS].hwnd);
\r
4346 SendMessage(hwndMain, WM_COMMAND, MAKEWPARAM(buttonDesc[i].id, 0), 0);
\r
4349 if (appData.icsActive && (isalpha((char)wParam) || wParam == '0')) {
\r
4350 // [HGM] movenum: only letters or leading zero should go to ICS input
\r
4351 HWND h = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
4352 if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);
\r
4354 SendMessage(h, WM_CHAR, wParam, lParam);
\r
4356 } else if (isalpha((char)wParam) || isdigit((char)wParam)){
\r
4357 TypeInEvent((char)wParam);
\r
4363 return CallWindowProc(buttonDesc[i].wndproc, hwnd, message, wParam, lParam);
\r
4366 /* Process messages for Promotion dialog box */
\r
4368 Promotion(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
4372 switch (message) {
\r
4373 case WM_INITDIALOG: /* message: initialize dialog box */
\r
4374 /* Center the dialog over the application window */
\r
4375 CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));
\r
4376 Translate(hDlg, DLG_PromotionKing);
\r
4377 ShowWindow(GetDlgItem(hDlg, PB_King),
\r
4378 (!appData.testLegality || gameInfo.variant == VariantSuicide ||
\r
4379 gameInfo.variant == VariantSpartan && !WhiteOnMove(currentMove) ||
\r
4380 gameInfo.variant == VariantGiveaway || gameInfo.variant == VariantSuper ) ?
\r
4381 SW_SHOW : SW_HIDE);
\r
4382 /* [HGM] Only allow C & A promotions if these pieces are defined */
\r
4383 ShowWindow(GetDlgItem(hDlg, PB_Archbishop),
\r
4384 ((PieceToChar(WhiteAngel) >= 'A' && WhiteOnMove(currentMove) &&
\r
4385 PieceToChar(WhiteAngel) != '~') ||
\r
4386 (PieceToChar(BlackAngel) >= 'A' && !WhiteOnMove(currentMove) &&
\r
4387 PieceToChar(BlackAngel) != '~') ) ?
\r
4388 SW_SHOW : SW_HIDE);
\r
4389 ShowWindow(GetDlgItem(hDlg, PB_Chancellor),
\r
4390 ((PieceToChar(WhiteMarshall) >= 'A' && WhiteOnMove(currentMove) &&
\r
4391 PieceToChar(WhiteMarshall) != '~') ||
\r
4392 (PieceToChar(BlackMarshall) >= 'A' && !WhiteOnMove(currentMove) &&
\r
4393 PieceToChar(BlackMarshall) != '~') ) ?
\r
4394 SW_SHOW : SW_HIDE);
\r
4395 /* [HGM] Hide B & R button in Shogi, use Q as promote, N as defer */
\r
4396 ShowWindow(GetDlgItem(hDlg, PB_Rook),
\r
4397 gameInfo.variant != VariantShogi ?
\r
4398 SW_SHOW : SW_HIDE);
\r
4399 ShowWindow(GetDlgItem(hDlg, PB_Bishop),
\r
4400 gameInfo.variant != VariantShogi ?
\r
4401 SW_SHOW : SW_HIDE);
\r
4402 if(gameInfo.variant == VariantShogi) {
\r
4403 SetDlgItemText(hDlg, PB_Queen, "YES");
\r
4404 SetDlgItemText(hDlg, PB_Knight, "NO");
\r
4405 SetWindowText(hDlg, "Promote?");
\r
4407 ShowWindow(GetDlgItem(hDlg, IDC_Centaur),
\r
4408 gameInfo.variant == VariantSuper ?
\r
4409 SW_SHOW : SW_HIDE);
\r
4412 case WM_COMMAND: /* message: received a command */
\r
4413 switch (LOWORD(wParam)) {
\r
4415 EndDialog(hDlg, TRUE); /* Exit the dialog */
\r
4416 ClearHighlights();
\r
4417 DrawPosition(FALSE, NULL);
\r
4420 promoChar = gameInfo.variant == VariantSuper ? PieceToChar(BlackSilver) : PieceToChar(BlackKing);
\r
4423 promoChar = gameInfo.variant == VariantShogi ? '+' : ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteQueen : BlackQueen));
\r
4426 promoChar = ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteRook : BlackRook));
\r
4427 if(gameInfo.variant == VariantSpartan && !WhiteOnMove(currentMove)) promoChar = PieceToChar(BlackDragon);
\r
4430 promoChar = ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteBishop : BlackBishop));
\r
4431 if(gameInfo.variant == VariantSpartan && !WhiteOnMove(currentMove)) promoChar = PieceToChar(BlackAlfil);
\r
4433 case PB_Chancellor:
\r
4434 promoChar = ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteMarshall : BlackMarshall));
\r
4436 case PB_Archbishop:
\r
4437 promoChar = ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteAngel : BlackAngel));
\r
4440 promoChar = gameInfo.variant == VariantShogi ? '=' : PieceToChar(WhiteOnMove(currentMove) ? WhiteKnight : BlackKnight);
\r
4445 if(promoChar == '.') return FALSE; // invalid piece chosen
\r
4446 EndDialog(hDlg, TRUE); /* Exit the dialog */
\r
4447 UserMoveEvent(fromX, fromY, toX, toY, promoChar);
\r
4448 fromX = fromY = -1;
\r
4449 if (!appData.highlightLastMove) {
\r
4450 ClearHighlights();
\r
4451 DrawPosition(FALSE, NULL);
\r
4458 /* Pop up promotion dialog */
\r
4460 PromotionPopup(HWND hwnd)
\r
4464 lpProc = MakeProcInstance((FARPROC)Promotion, hInst);
\r
4465 DialogBox(hInst, MAKEINTRESOURCE(DLG_PromotionKing),
\r
4466 hwnd, (DLGPROC)lpProc);
\r
4467 FreeProcInstance(lpProc);
\r
4473 DrawPosition(TRUE, NULL);
\r
4474 PromotionPopup(hwndMain);
\r
4478 LoadGameDialog(HWND hwnd, char* title)
\r
4482 char fileTitle[MSG_SIZ];
\r
4483 f = OpenFileDialog(hwnd, "rb", "",
\r
4484 appData.oldSaveStyle ? "gam" : "pgn",
\r
4486 title, &number, fileTitle, NULL);
\r
4488 cmailMsgLoaded = FALSE;
\r
4489 if (number == 0) {
\r
4490 int error = GameListBuild(f);
\r
4492 DisplayError(_("Cannot build game list"), error);
\r
4493 } else if (!ListEmpty(&gameList) &&
\r
4494 ((ListGame *) gameList.tailPred)->number > 1) {
\r
4495 GameListPopUp(f, fileTitle);
\r
4498 GameListDestroy();
\r
4501 LoadGame(f, number, fileTitle, FALSE);
\r
4505 int get_term_width()
\r
4510 HFONT hfont, hold_font;
\r
4515 hText = GetDlgItem(hwndConsole, OPT_ConsoleText);
\r
4519 // get the text metrics
\r
4520 hdc = GetDC(hText);
\r
4521 lf = font[boardSize][CONSOLE_FONT]->lf;
\r
4522 if (consoleCF.dwEffects & CFE_BOLD)
\r
4523 lf.lfWeight = FW_BOLD;
\r
4524 if (consoleCF.dwEffects & CFE_ITALIC)
\r
4525 lf.lfItalic = TRUE;
\r
4526 if (consoleCF.dwEffects & CFE_STRIKEOUT)
\r
4527 lf.lfStrikeOut = TRUE;
\r
4528 if (consoleCF.dwEffects & CFE_UNDERLINE)
\r
4529 lf.lfUnderline = TRUE;
\r
4530 hfont = CreateFontIndirect(&lf);
\r
4531 hold_font = SelectObject(hdc, hfont);
\r
4532 GetTextMetrics(hdc, &tm);
\r
4533 SelectObject(hdc, hold_font);
\r
4534 DeleteObject(hfont);
\r
4535 ReleaseDC(hText, hdc);
\r
4537 // get the rectangle
\r
4538 SendMessage(hText, EM_GETRECT, 0, (LPARAM)&rc);
\r
4540 return (rc.right-rc.left) / tm.tmAveCharWidth;
\r
4543 void UpdateICSWidth(HWND hText)
\r
4545 LONG old_width, new_width;
\r
4547 new_width = get_term_width(hText, FALSE);
\r
4548 old_width = GetWindowLongPtr(hText, GWLP_USERDATA);
\r
4549 if (new_width != old_width)
\r
4551 ics_update_width(new_width);
\r
4552 SetWindowLongPtr(hText, GWLP_USERDATA, new_width);
\r
4557 ChangedConsoleFont()
\r
4560 CHARRANGE tmpsel, sel;
\r
4561 MyFont *f = font[boardSize][CONSOLE_FONT];
\r
4562 HWND hText = GetDlgItem(hwndConsole, OPT_ConsoleText);
\r
4563 HWND hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
4566 cfmt.cbSize = sizeof(CHARFORMAT);
\r
4567 cfmt.dwMask = CFM_FACE|CFM_SIZE|CFM_CHARSET;
\r
4568 safeStrCpy(cfmt.szFaceName, font[boardSize][CONSOLE_FONT]->mfp.faceName,
\r
4569 sizeof(cfmt.szFaceName)/sizeof(cfmt.szFaceName[0]) );
\r
4570 /* yHeight is expressed in twips. A twip is 1/20 of a font's point
\r
4571 * size. This was undocumented in the version of MSVC++ that I had
\r
4572 * when I wrote the code, but is apparently documented now.
\r
4574 cfmt.yHeight = (int)(f->mfp.pointSize * 20.0 + 0.5);
\r
4575 cfmt.bCharSet = f->lf.lfCharSet;
\r
4576 cfmt.bPitchAndFamily = f->lf.lfPitchAndFamily;
\r
4577 SendMessage(hText, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &cfmt);
\r
4578 SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &cfmt);
\r
4579 /* Why are the following seemingly needed too? */
\r
4580 SendMessage(hText, EM_SETCHARFORMAT, SCF_DEFAULT, (LPARAM) &cfmt);
\r
4581 SendMessage(hInput, EM_SETCHARFORMAT, SCF_DEFAULT, (LPARAM) &cfmt);
\r
4582 SendMessage(hText, EM_EXGETSEL, 0, (LPARAM)&sel);
\r
4584 tmpsel.cpMax = -1; /*999999?*/
\r
4585 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&tmpsel);
\r
4586 SendMessage(hText, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cfmt);
\r
4587 /* Trying putting this here too. It still seems to tickle a RichEdit
\r
4588 * bug: sometimes RichEdit indents the first line of a paragraph too.
\r
4590 paraf.cbSize = sizeof(paraf);
\r
4591 paraf.dwMask = PFM_OFFSET | PFM_STARTINDENT;
\r
4592 paraf.dxStartIndent = 0;
\r
4593 paraf.dxOffset = WRAP_INDENT;
\r
4594 SendMessage(hText, EM_SETPARAFORMAT, 0, (LPARAM) ¶f);
\r
4595 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
4596 UpdateICSWidth(hText);
\r
4599 /*---------------------------------------------------------------------------*\
\r
4601 * Window Proc for main window
\r
4603 \*---------------------------------------------------------------------------*/
\r
4605 /* Process messages for main window, etc. */
\r
4607 WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
\r
4610 int wmId, wmEvent;
\r
4614 char fileTitle[MSG_SIZ];
\r
4615 char buf[MSG_SIZ];
\r
4616 static SnapData sd;
\r
4617 static int peek=0;
\r
4619 switch (message) {
\r
4621 case WM_PAINT: /* message: repaint portion of window */
\r
4625 case WM_ERASEBKGND:
\r
4626 if (IsIconic(hwnd)) {
\r
4627 /* Cheat; change the message */
\r
4628 return (DefWindowProc(hwnd, WM_ICONERASEBKGND, wParam, lParam));
\r
4630 return (DefWindowProc(hwnd, message, wParam, lParam));
\r
4634 case WM_LBUTTONDOWN:
\r
4635 case WM_MBUTTONDOWN:
\r
4636 case WM_RBUTTONDOWN:
\r
4637 case WM_LBUTTONUP:
\r
4638 case WM_MBUTTONUP:
\r
4639 case WM_RBUTTONUP:
\r
4640 case WM_MOUSEMOVE:
\r
4641 case WM_MOUSEWHEEL:
\r
4642 MouseEvent(hwnd, message, wParam, lParam);
\r
4646 if((char)wParam == '\b') {
\r
4647 ForwardEvent(); peek = 0;
\r
4650 JAWS_KBUP_NAVIGATION
\r
4655 if((char)wParam == '\b') {
\r
4656 if(!peek) BackwardEvent(), peek = 1;
\r
4659 JAWS_KBDOWN_NAVIGATION
\r
4665 JAWS_ALT_INTERCEPT
\r
4667 if (appData.icsActive && ((char)wParam == '\r' || (char)wParam > ' ' && !((char)wParam >= '1' && (char)wParam <= '9'))) {
\r
4668 // [HGM] movenum: for non-zero digits we always do type-in dialog
\r
4669 HWND h = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
4670 if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);
\r
4672 SendMessage(h, message, wParam, lParam);
\r
4673 } else if(lParam != KF_REPEAT) {
\r
4674 if (isalpha((char)wParam) || isdigit((char)wParam)) {
\r
4675 TypeInEvent((char)wParam);
\r
4676 } else if((char)wParam == 003) CopyGameToClipboard();
\r
4677 else if((char)wParam == 026) PasteGameOrFENFromClipboard();
\r
4682 case WM_PALETTECHANGED:
\r
4683 if (hwnd != (HWND)wParam && !appData.monoMode) {
\r
4685 HDC hdc = GetDC(hwndMain);
\r
4686 SelectPalette(hdc, hPal, TRUE);
\r
4687 nnew = RealizePalette(hdc);
\r
4689 paletteChanged = TRUE;
\r
4690 InvalidateRect(hwnd, &boardRect, FALSE);
\r
4692 ReleaseDC(hwnd, hdc);
\r
4696 case WM_QUERYNEWPALETTE:
\r
4697 if (!appData.monoMode /*&& paletteChanged*/) {
\r
4699 HDC hdc = GetDC(hwndMain);
\r
4700 paletteChanged = FALSE;
\r
4701 SelectPalette(hdc, hPal, FALSE);
\r
4702 nnew = RealizePalette(hdc);
\r
4704 InvalidateRect(hwnd, &boardRect, FALSE);
\r
4706 ReleaseDC(hwnd, hdc);
\r
4711 case WM_COMMAND: /* message: command from application menu */
\r
4712 wmId = LOWORD(wParam);
\r
4713 wmEvent = HIWORD(wParam);
\r
4718 SAY("new game enter a move to play against the computer with white");
\r
4721 case IDM_NewGameFRC:
\r
4722 if( NewGameFRC() == 0 ) {
\r
4727 case IDM_NewVariant:
\r
4728 NewVariantPopup(hwnd);
\r
4731 case IDM_LoadGame:
\r
4732 LoadGameDialog(hwnd, _("Load Game from File"));
\r
4735 case IDM_LoadNextGame:
\r
4739 case IDM_LoadPrevGame:
\r
4743 case IDM_ReloadGame:
\r
4747 case IDM_LoadPosition:
\r
4748 if (gameMode == AnalyzeMode || gameMode == AnalyzeFile) {
\r
4749 Reset(FALSE, TRUE);
\r
4752 f = OpenFileDialog(hwnd, "rb", "",
\r
4753 appData.oldSaveStyle ? "pos" : "fen",
\r
4755 _("Load Position from File"), &number, fileTitle, NULL);
\r
4757 LoadPosition(f, number, fileTitle);
\r
4761 case IDM_LoadNextPosition:
\r
4762 ReloadPosition(1);
\r
4765 case IDM_LoadPrevPosition:
\r
4766 ReloadPosition(-1);
\r
4769 case IDM_ReloadPosition:
\r
4770 ReloadPosition(0);
\r
4773 case IDM_SaveGame:
\r
4774 defName = DefaultFileName(appData.oldSaveStyle ? "gam" : "pgn");
\r
4775 f = OpenFileDialog(hwnd, "a", defName,
\r
4776 appData.oldSaveStyle ? "gam" : "pgn",
\r
4778 _("Save Game to File"), NULL, fileTitle, NULL);
\r
4780 SaveGame(f, 0, "");
\r
4784 case IDM_SavePosition:
\r
4785 defName = DefaultFileName(appData.oldSaveStyle ? "pos" : "fen");
\r
4786 f = OpenFileDialog(hwnd, "a", defName,
\r
4787 appData.oldSaveStyle ? "pos" : "fen",
\r
4789 _("Save Position to File"), NULL, fileTitle, NULL);
\r
4791 SavePosition(f, 0, "");
\r
4795 case IDM_SaveDiagram:
\r
4796 defName = "diagram";
\r
4797 f = OpenFileDialog(hwnd, "wb", defName,
\r
4800 _("Save Diagram to File"), NULL, fileTitle, NULL);
\r
4806 case IDM_CopyGame:
\r
4807 CopyGameToClipboard();
\r
4810 case IDM_PasteGame:
\r
4811 PasteGameFromClipboard();
\r
4814 case IDM_CopyGameListToClipboard:
\r
4815 CopyGameListToClipboard();
\r
4818 /* [AS] Autodetect FEN or PGN data */
\r
4819 case IDM_PasteAny:
\r
4820 PasteGameOrFENFromClipboard();
\r
4823 /* [AS] Move history */
\r
4824 case IDM_ShowMoveHistory:
\r
4825 if( MoveHistoryIsUp() ) {
\r
4826 MoveHistoryPopDown();
\r
4829 MoveHistoryPopUp();
\r
4833 /* [AS] Eval graph */
\r
4834 case IDM_ShowEvalGraph:
\r
4835 if( EvalGraphIsUp() ) {
\r
4836 EvalGraphPopDown();
\r
4840 SetFocus(hwndMain);
\r
4844 /* [AS] Engine output */
\r
4845 case IDM_ShowEngineOutput:
\r
4846 if( EngineOutputIsUp() ) {
\r
4847 EngineOutputPopDown();
\r
4850 EngineOutputPopUp();
\r
4854 /* [AS] User adjudication */
\r
4855 case IDM_UserAdjudication_White:
\r
4856 UserAdjudicationEvent( +1 );
\r
4859 case IDM_UserAdjudication_Black:
\r
4860 UserAdjudicationEvent( -1 );
\r
4863 case IDM_UserAdjudication_Draw:
\r
4864 UserAdjudicationEvent( 0 );
\r
4867 /* [AS] Game list options dialog */
\r
4868 case IDM_GameListOptions:
\r
4869 GameListOptions();
\r
4876 case IDM_CopyPosition:
\r
4877 CopyFENToClipboard();
\r
4880 case IDM_PastePosition:
\r
4881 PasteFENFromClipboard();
\r
4884 case IDM_MailMove:
\r
4888 case IDM_ReloadCMailMsg:
\r
4889 Reset(TRUE, TRUE);
\r
4890 ReloadCmailMsgEvent(FALSE);
\r
4893 case IDM_Minimize:
\r
4894 ShowWindow(hwnd, SW_MINIMIZE);
\r
4901 case IDM_MachineWhite:
\r
4902 MachineWhiteEvent();
\r
4904 * refresh the tags dialog only if it's visible
\r
4906 if (gameMode == MachinePlaysWhite && IsWindowVisible(editTagsDialog)) {
\r
4908 tags = PGNTags(&gameInfo);
\r
4909 TagsPopUp(tags, CmailMsg());
\r
4912 SAY("computer starts playing white");
\r
4915 case IDM_MachineBlack:
\r
4916 MachineBlackEvent();
\r
4918 * refresh the tags dialog only if it's visible
\r
4920 if (gameMode == MachinePlaysBlack && IsWindowVisible(editTagsDialog)) {
\r
4922 tags = PGNTags(&gameInfo);
\r
4923 TagsPopUp(tags, CmailMsg());
\r
4926 SAY("computer starts playing black");
\r
4929 case IDM_Match: // [HGM] match: flows into next case, after setting Match Mode and nr of Games
\r
4930 MatchEvent(2); // distinguish from command-line-triggered case (matchMode=1)
\r
4933 case IDM_TwoMachines:
\r
4934 TwoMachinesEvent();
\r
4936 * refresh the tags dialog only if it's visible
\r
4938 if (gameMode == TwoMachinesPlay && IsWindowVisible(editTagsDialog)) {
\r
4940 tags = PGNTags(&gameInfo);
\r
4941 TagsPopUp(tags, CmailMsg());
\r
4944 SAY("computer starts playing both sides");
\r
4947 case IDM_AnalysisMode:
\r
4948 if(AnalyzeModeEvent()) {
\r
4949 SAY("analyzing current position");
\r
4953 case IDM_AnalyzeFile:
\r
4954 AnalyzeFileEvent();
\r
4957 case IDM_IcsClient:
\r
4961 case IDM_EditGame:
\r
4962 case IDM_EditGame2:
\r
4967 case IDM_EditPosition:
\r
4968 case IDM_EditPosition2:
\r
4969 EditPositionEvent();
\r
4970 SAY("enter a FEN string or setup a position on the board using the control R pop up menu");
\r
4973 case IDM_Training:
\r
4977 case IDM_ShowGameList:
\r
4978 ShowGameListProc();
\r
4981 case IDM_EditProgs1:
\r
4982 EditTagsPopUp(firstChessProgramNames, &firstChessProgramNames);
\r
4985 case IDM_LoadProg1:
\r
4986 LoadEnginePopUp(hwndMain, 0);
\r
4989 case IDM_LoadProg2:
\r
4990 LoadEnginePopUp(hwndMain, 1);
\r
4993 case IDM_EditServers:
\r
4994 EditTagsPopUp(icsNames, &icsNames);
\r
4997 case IDM_EditTags:
\r
5002 case IDM_EditBook:
\r
5006 case IDM_EditComment:
\r
5008 if (commentUp && editComment) {
\r
5011 EditCommentEvent();
\r
5031 case IDM_CallFlag:
\r
5051 case IDM_StopObserving:
\r
5052 StopObservingEvent();
\r
5055 case IDM_StopExamining:
\r
5056 StopExaminingEvent();
\r
5060 UploadGameEvent();
\r
5063 case IDM_TypeInMove:
\r
5064 TypeInEvent('\000');
\r
5067 case IDM_TypeInName:
\r
5068 PopUpNameDialog('\000');
\r
5071 case IDM_Backward:
\r
5073 SetFocus(hwndMain);
\r
5080 SetFocus(hwndMain);
\r
5085 SetFocus(hwndMain);
\r
5090 SetFocus(hwndMain);
\r
5093 case OPT_GameListNext: // [HGM] forward these two accelerators to Game List
\r
5094 case OPT_GameListPrev:
\r
5095 if(gameListDialog) SendMessage(gameListDialog, WM_COMMAND, wmId, 0);
\r
5099 RevertEvent(FALSE);
\r
5102 case IDM_Annotate: // [HGM] vari: revert with annotation
\r
5103 RevertEvent(TRUE);
\r
5106 case IDM_TruncateGame:
\r
5107 TruncateGameEvent();
\r
5114 case IDM_RetractMove:
\r
5115 RetractMoveEvent();
\r
5118 case IDM_FlipView:
\r
5119 flipView = !flipView;
\r
5120 DrawPosition(FALSE, NULL);
\r
5123 case IDM_FlipClock:
\r
5124 flipClock = !flipClock;
\r
5125 DisplayBothClocks();
\r
5129 case IDM_MuteSounds:
\r
5130 mute = !mute; // [HGM] mute: keep track of global muting variable
\r
5131 CheckMenuItem(GetMenu(hwndMain),IDM_MuteSounds,
\r
5132 MF_BYCOMMAND|(mute?MF_CHECKED:MF_UNCHECKED));
\r
5135 case IDM_GeneralOptions:
\r
5136 GeneralOptionsPopup(hwnd);
\r
5137 DrawPosition(TRUE, NULL);
\r
5140 case IDM_BoardOptions:
\r
5141 BoardOptionsPopup(hwnd);
\r
5144 case IDM_EnginePlayOptions:
\r
5145 EnginePlayOptionsPopup(hwnd);
\r
5148 case IDM_Engine1Options:
\r
5149 EngineOptionsPopup(hwnd, &first);
\r
5152 case IDM_Engine2Options:
\r
5154 if(WaitForEngine(&second, SettingsMenuIfReady)) break;
\r
5155 EngineOptionsPopup(hwnd, &second);
\r
5158 case IDM_OptionsUCI:
\r
5159 UciOptionsPopup(hwnd);
\r
5163 TourneyPopup(hwnd);
\r
5166 case IDM_IcsOptions:
\r
5167 IcsOptionsPopup(hwnd);
\r
5171 FontsOptionsPopup(hwnd);
\r
5175 SoundOptionsPopup(hwnd);
\r
5178 case IDM_CommPort:
\r
5179 CommPortOptionsPopup(hwnd);
\r
5182 case IDM_LoadOptions:
\r
5183 LoadOptionsPopup(hwnd);
\r
5186 case IDM_SaveOptions:
\r
5187 SaveOptionsPopup(hwnd);
\r
5190 case IDM_TimeControl:
\r
5191 TimeControlOptionsPopup(hwnd);
\r
5194 case IDM_SaveSettings:
\r
5195 SaveSettings(settingsFileName);
\r
5198 case IDM_SaveSettingsOnExit:
\r
5199 saveSettingsOnExit = !saveSettingsOnExit;
\r
5200 (void) CheckMenuItem(GetMenu(hwndMain), IDM_SaveSettingsOnExit,
\r
5201 MF_BYCOMMAND|(saveSettingsOnExit ?
\r
5202 MF_CHECKED : MF_UNCHECKED));
\r
5213 case IDM_AboutGame:
\r
5218 appData.debugMode = !appData.debugMode;
\r
5219 if (appData.debugMode) {
\r
5220 char dir[MSG_SIZ];
\r
5221 GetCurrentDirectory(MSG_SIZ, dir);
\r
5222 SetCurrentDirectory(installDir);
\r
5223 debugFP = fopen(appData.nameOfDebugFile, "w");
\r
5224 SetCurrentDirectory(dir);
\r
5225 setbuf(debugFP, NULL);
\r
5232 case IDM_HELPCONTENTS:
\r
5233 if (!MyHelp (hwnd, "winboard.hlp", HELP_KEY,(DWORD)(LPSTR)"CONTENTS") &&
\r
5234 !HtmlHelp(hwnd, "winboard.chm", 0, 0) ) {
\r
5235 MessageBox (GetFocus(),
\r
5236 _("Unable to activate help"),
\r
5237 szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);
\r
5241 case IDM_HELPSEARCH:
\r
5242 if (!MyHelp (hwnd, "winboard.hlp", HELP_PARTIALKEY, (DWORD)(LPSTR)"") &&
\r
5243 !HtmlHelp(hwnd, "winboard.chm", 0, 0) ) {
\r
5244 MessageBox (GetFocus(),
\r
5245 _("Unable to activate help"),
\r
5246 szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);
\r
5250 case IDM_HELPHELP:
\r
5251 if(!WinHelp(hwnd, (LPSTR)NULL, HELP_HELPONHELP, 0)) {
\r
5252 MessageBox (GetFocus(),
\r
5253 _("Unable to activate help"),
\r
5254 szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);
\r
5259 lpProc = MakeProcInstance((FARPROC)About, hInst);
\r
5261 (gameInfo.event && strcmp(gameInfo.event, "Easter Egg Hunt") == 0) ?
\r
5262 "AboutBox2" : "AboutBox", hwnd, (DLGPROC)lpProc);
\r
5263 FreeProcInstance(lpProc);
\r
5266 case IDM_DirectCommand1:
\r
5267 AskQuestionEvent(_("Direct Command"),
\r
5268 _("Send to chess program:"), "", "1");
\r
5270 case IDM_DirectCommand2:
\r
5271 AskQuestionEvent(_("Direct Command"),
\r
5272 _("Send to second chess program:"), "", "2");
\r
5275 case EP_WhitePawn:
\r
5276 EditPositionMenuEvent(WhitePawn, fromX, fromY);
\r
5277 fromX = fromY = -1;
\r
5280 case EP_WhiteKnight:
\r
5281 EditPositionMenuEvent(WhiteKnight, fromX, fromY);
\r
5282 fromX = fromY = -1;
\r
5285 case EP_WhiteBishop:
\r
5286 EditPositionMenuEvent(WhiteBishop, fromX, fromY);
\r
5287 fromX = fromY = -1;
\r
5290 case EP_WhiteRook:
\r
5291 EditPositionMenuEvent(WhiteRook, fromX, fromY);
\r
5292 fromX = fromY = -1;
\r
5295 case EP_WhiteQueen:
\r
5296 EditPositionMenuEvent(WhiteQueen, fromX, fromY);
\r
5297 fromX = fromY = -1;
\r
5300 case EP_WhiteFerz:
\r
5301 EditPositionMenuEvent(WhiteFerz, fromX, fromY);
\r
5302 fromX = fromY = -1;
\r
5305 case EP_WhiteWazir:
\r
5306 EditPositionMenuEvent(WhiteWazir, fromX, fromY);
\r
5307 fromX = fromY = -1;
\r
5310 case EP_WhiteAlfil:
\r
5311 EditPositionMenuEvent(WhiteAlfil, fromX, fromY);
\r
5312 fromX = fromY = -1;
\r
5315 case EP_WhiteCannon:
\r
5316 EditPositionMenuEvent(WhiteCannon, fromX, fromY);
\r
5317 fromX = fromY = -1;
\r
5320 case EP_WhiteCardinal:
\r
5321 EditPositionMenuEvent(WhiteAngel, fromX, fromY);
\r
5322 fromX = fromY = -1;
\r
5325 case EP_WhiteMarshall:
\r
5326 EditPositionMenuEvent(WhiteMarshall, fromX, fromY);
\r
5327 fromX = fromY = -1;
\r
5330 case EP_WhiteKing:
\r
5331 EditPositionMenuEvent(WhiteKing, fromX, fromY);
\r
5332 fromX = fromY = -1;
\r
5335 case EP_BlackPawn:
\r
5336 EditPositionMenuEvent(BlackPawn, fromX, fromY);
\r
5337 fromX = fromY = -1;
\r
5340 case EP_BlackKnight:
\r
5341 EditPositionMenuEvent(BlackKnight, fromX, fromY);
\r
5342 fromX = fromY = -1;
\r
5345 case EP_BlackBishop:
\r
5346 EditPositionMenuEvent(BlackBishop, fromX, fromY);
\r
5347 fromX = fromY = -1;
\r
5350 case EP_BlackRook:
\r
5351 EditPositionMenuEvent(BlackRook, fromX, fromY);
\r
5352 fromX = fromY = -1;
\r
5355 case EP_BlackQueen:
\r
5356 EditPositionMenuEvent(BlackQueen, fromX, fromY);
\r
5357 fromX = fromY = -1;
\r
5360 case EP_BlackFerz:
\r
5361 EditPositionMenuEvent(BlackFerz, fromX, fromY);
\r
5362 fromX = fromY = -1;
\r
5365 case EP_BlackWazir:
\r
5366 EditPositionMenuEvent(BlackWazir, fromX, fromY);
\r
5367 fromX = fromY = -1;
\r
5370 case EP_BlackAlfil:
\r
5371 EditPositionMenuEvent(BlackAlfil, fromX, fromY);
\r
5372 fromX = fromY = -1;
\r
5375 case EP_BlackCannon:
\r
5376 EditPositionMenuEvent(BlackCannon, fromX, fromY);
\r
5377 fromX = fromY = -1;
\r
5380 case EP_BlackCardinal:
\r
5381 EditPositionMenuEvent(BlackAngel, fromX, fromY);
\r
5382 fromX = fromY = -1;
\r
5385 case EP_BlackMarshall:
\r
5386 EditPositionMenuEvent(BlackMarshall, fromX, fromY);
\r
5387 fromX = fromY = -1;
\r
5390 case EP_BlackKing:
\r
5391 EditPositionMenuEvent(BlackKing, fromX, fromY);
\r
5392 fromX = fromY = -1;
\r
5395 case EP_EmptySquare:
\r
5396 EditPositionMenuEvent(EmptySquare, fromX, fromY);
\r
5397 fromX = fromY = -1;
\r
5400 case EP_ClearBoard:
\r
5401 EditPositionMenuEvent(ClearBoard, fromX, fromY);
\r
5402 fromX = fromY = -1;
\r
5406 EditPositionMenuEvent(WhitePlay, fromX, fromY);
\r
5407 fromX = fromY = -1;
\r
5411 EditPositionMenuEvent(BlackPlay, fromX, fromY);
\r
5412 fromX = fromY = -1;
\r
5416 EditPositionMenuEvent(PromotePiece, fromX, fromY);
\r
5417 fromX = fromY = -1;
\r
5421 EditPositionMenuEvent(DemotePiece, fromX, fromY);
\r
5422 fromX = fromY = -1;
\r
5426 DropMenuEvent(WhitePawn, fromX, fromY);
\r
5427 fromX = fromY = -1;
\r
5431 DropMenuEvent(WhiteKnight, fromX, fromY);
\r
5432 fromX = fromY = -1;
\r
5436 DropMenuEvent(WhiteBishop, fromX, fromY);
\r
5437 fromX = fromY = -1;
\r
5441 DropMenuEvent(WhiteRook, fromX, fromY);
\r
5442 fromX = fromY = -1;
\r
5446 DropMenuEvent(WhiteQueen, fromX, fromY);
\r
5447 fromX = fromY = -1;
\r
5451 barbaric = 0; appData.language = "";
\r
5452 TranslateMenus(0);
\r
5453 CheckMenuItem(GetMenu(hwndMain), lastChecked, MF_BYCOMMAND|MF_UNCHECKED);
\r
5454 CheckMenuItem(GetMenu(hwndMain), IDM_English, MF_BYCOMMAND|MF_CHECKED);
\r
5455 lastChecked = wmId;
\r
5459 if(wmId >= IDM_RecentEngines && wmId < IDM_RecentEngines + appData.recentEngines)
\r
5460 RecentEngineEvent(wmId - IDM_RecentEngines);
\r
5462 if(wmId > IDM_English && wmId < IDM_English+20) {
\r
5463 LoadLanguageFile(languageFile[wmId - IDM_English - 1]);
\r
5464 TranslateMenus(0);
\r
5465 CheckMenuItem(GetMenu(hwndMain), lastChecked, MF_BYCOMMAND|MF_UNCHECKED);
\r
5466 CheckMenuItem(GetMenu(hwndMain), wmId, MF_BYCOMMAND|MF_CHECKED);
\r
5467 lastChecked = wmId;
\r
5470 return (DefWindowProc(hwnd, message, wParam, lParam));
\r
5476 case CLOCK_TIMER_ID:
\r
5477 KillTimer(hwnd, clockTimerEvent); /* Simulate one-shot timer as in X */
\r
5478 clockTimerEvent = 0;
\r
5479 DecrementClocks(); /* call into back end */
\r
5481 case LOAD_GAME_TIMER_ID:
\r
5482 KillTimer(hwnd, loadGameTimerEvent); /* Simulate one-shot timer as in X*/
\r
5483 loadGameTimerEvent = 0;
\r
5484 AutoPlayGameLoop(); /* call into back end */
\r
5486 case ANALYSIS_TIMER_ID:
\r
5487 if ((gameMode == AnalyzeMode || gameMode == AnalyzeFile
\r
5488 || appData.icsEngineAnalyze) && appData.periodicUpdates) {
\r
5489 AnalysisPeriodicEvent(0);
\r
5491 KillTimer(hwnd, analysisTimerEvent);
\r
5492 analysisTimerEvent = 0;
\r
5495 case DELAYED_TIMER_ID:
\r
5496 KillTimer(hwnd, delayedTimerEvent);
\r
5497 delayedTimerEvent = 0;
\r
5498 delayedTimerCallback();
\r
5503 case WM_USER_Input:
\r
5504 InputEvent(hwnd, message, wParam, lParam);
\r
5507 /* [AS] Also move "attached" child windows */
\r
5508 case WM_WINDOWPOSCHANGING:
\r
5510 if( hwnd == hwndMain && appData.useStickyWindows ) {
\r
5511 LPWINDOWPOS lpwp = (LPWINDOWPOS) lParam;
\r
5513 if( ((lpwp->flags & SWP_NOMOVE) == 0) && ((lpwp->flags & SWP_NOSIZE) != 0) ) {
\r
5514 /* Window is moving */
\r
5517 // GetWindowRect( hwnd, &rcMain ); //[HGM] sticky: in XP this returned new position, not old
\r
5518 rcMain.left = wpMain.x; // replace by these 4 lines to reconstruct old rect
\r
5519 rcMain.right = wpMain.x + wpMain.width;
\r
5520 rcMain.top = wpMain.y;
\r
5521 rcMain.bottom = wpMain.y + wpMain.height;
\r
5523 ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, moveHistoryDialog, &wpMoveHistory );
\r
5524 ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, evalGraphDialog, &wpEvalGraph );
\r
5525 ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, engineOutputDialog, &wpEngineOutput );
\r
5526 ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, gameListDialog, &wpGameList );
\r
5527 ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, hwndConsole, &wpConsole );
\r
5528 wpMain.x = lpwp->x;
\r
5529 wpMain.y = lpwp->y;
\r
5534 /* [AS] Snapping */
\r
5535 case WM_ENTERSIZEMOVE:
\r
5536 if(appData.debugMode) { fprintf(debugFP, "size-move\n"); }
\r
5537 if (hwnd == hwndMain) {
\r
5538 doingSizing = TRUE;
\r
5541 return OnEnterSizeMove( &sd, hwnd, wParam, lParam );
\r
5545 if(appData.debugMode) { fprintf(debugFP, "sizing\n"); }
\r
5546 if (hwnd == hwndMain) {
\r
5547 lastSizing = wParam;
\r
5552 if(appData.debugMode) { fprintf(debugFP, "moving\n"); }
\r
5553 return OnMoving( &sd, hwnd, wParam, lParam );
\r
5555 case WM_EXITSIZEMOVE:
\r
5556 if(appData.debugMode) { fprintf(debugFP, "exit size-move, size = %d\n", squareSize); }
\r
5557 if (hwnd == hwndMain) {
\r
5559 doingSizing = FALSE;
\r
5560 InvalidateRect(hwnd, &boardRect, FALSE);
\r
5561 GetClientRect(hwnd, &client);
\r
5562 ResizeBoard(client.right, client.bottom, lastSizing);
\r
5564 if(appData.debugMode) { fprintf(debugFP, "square size = %d\n", squareSize); }
\r
5566 return OnExitSizeMove( &sd, hwnd, wParam, lParam );
\r
5569 case WM_DESTROY: /* message: window being destroyed */
\r
5570 PostQuitMessage(0);
\r
5574 if (hwnd == hwndMain) {
\r
5579 default: /* Passes it on if unprocessed */
\r
5580 return (DefWindowProc(hwnd, message, wParam, lParam));
\r
5585 /*---------------------------------------------------------------------------*\
\r
5587 * Misc utility routines
\r
5589 \*---------------------------------------------------------------------------*/
\r
5592 * Decent random number generator, at least not as bad as Windows
\r
5593 * standard rand, which returns a value in the range 0 to 0x7fff.
\r
5595 unsigned int randstate;
\r
5600 randstate = randstate * 1664525 + 1013904223;
\r
5601 return (int) randstate & 0x7fffffff;
\r
5605 mysrandom(unsigned int seed)
\r
5612 * returns TRUE if user selects a different color, FALSE otherwise
\r
5616 ChangeColor(HWND hwnd, COLORREF *which)
\r
5618 static BOOL firstTime = TRUE;
\r
5619 static DWORD customColors[16];
\r
5621 COLORREF newcolor;
\r
5626 /* Make initial colors in use available as custom colors */
\r
5627 /* Should we put the compiled-in defaults here instead? */
\r
5629 customColors[i++] = lightSquareColor & 0xffffff;
\r
5630 customColors[i++] = darkSquareColor & 0xffffff;
\r
5631 customColors[i++] = whitePieceColor & 0xffffff;
\r
5632 customColors[i++] = blackPieceColor & 0xffffff;
\r
5633 customColors[i++] = highlightSquareColor & 0xffffff;
\r
5634 customColors[i++] = premoveHighlightColor & 0xffffff;
\r
5636 for (ccl = (ColorClass) 0; ccl < NColorClasses && i < 16; ccl++) {
\r
5637 customColors[i++] = textAttribs[ccl].color;
\r
5639 while (i < 16) customColors[i++] = RGB(255, 255, 255);
\r
5640 firstTime = FALSE;
\r
5643 cc.lStructSize = sizeof(cc);
\r
5644 cc.hwndOwner = hwnd;
\r
5645 cc.hInstance = NULL;
\r
5646 cc.rgbResult = (DWORD) (*which & 0xffffff);
\r
5647 cc.lpCustColors = (LPDWORD) customColors;
\r
5648 cc.Flags = CC_RGBINIT|CC_FULLOPEN;
\r
5650 if (!ChooseColor(&cc)) return FALSE;
\r
5652 newcolor = (COLORREF) (0x2000000 | cc.rgbResult);
\r
5653 if (newcolor == *which) return FALSE;
\r
5654 *which = newcolor;
\r
5658 InitDrawingColors();
\r
5659 InvalidateRect(hwnd, &boardRect, FALSE);
\r
5664 MyLoadSound(MySound *ms)
\r
5670 if (ms->data && ms->flag) free(ms->data);
\r
5673 switch (ms->name[0]) {
\r
5679 /* System sound from Control Panel. Don't preload here. */
\r
5683 if (ms->name[1] == NULLCHAR) {
\r
5684 /* "!" alone = silence */
\r
5687 /* Builtin wave resource. Error if not found. */
\r
5688 HANDLE h = FindResource(hInst, ms->name + 1, "WAVE");
\r
5689 if (h == NULL) break;
\r
5690 ms->data = (void *)LoadResource(hInst, h);
\r
5691 ms->flag = 0; // not maloced, so cannot be freed!
\r
5692 if (h == NULL) break;
\r
5697 /* .wav file. Error if not found. */
\r
5698 f = fopen(ms->name, "rb");
\r
5699 if (f == NULL) break;
\r
5700 if (fstat(fileno(f), &st) < 0) break;
\r
5701 ms->data = malloc(st.st_size);
\r
5703 if (fread(ms->data, st.st_size, 1, f) < 1) break;
\r
5709 char buf[MSG_SIZ];
\r
5710 snprintf(buf, MSG_SIZ, _("Error loading sound %s"), ms->name);
\r
5711 DisplayError(buf, GetLastError());
\r
5717 MyPlaySound(MySound *ms)
\r
5719 BOOLEAN ok = FALSE;
\r
5721 if(mute) return TRUE; // [HGM] mute: suppress all sound play when muted
\r
5722 switch (ms->name[0]) {
\r
5724 if(appData.debugMode) fprintf(debugFP, "silence\n");
\r
5729 /* System sound from Control Panel (deprecated feature).
\r
5730 "$" alone or an unset sound name gets default beep (still in use). */
\r
5731 if (ms->name[1]) {
\r
5732 ok = PlaySound(ms->name + 1, NULL, SND_ALIAS|SND_ASYNC);
\r
5734 if (!ok) ok = MessageBeep(MB_OK);
\r
5737 /* Builtin wave resource, or "!" alone for silence */
\r
5738 if (ms->name[1]) {
\r
5739 if (ms->data == NULL) return FALSE;
\r
5740 ok = PlaySound(ms->data, NULL, SND_MEMORY|SND_ASYNC);
\r
5746 /* .wav file. Error if not found. */
\r
5747 if (ms->data == NULL) return FALSE;
\r
5748 ok = PlaySound(ms->data, NULL, SND_MEMORY|SND_ASYNC);
\r
5751 /* Don't print an error: this can happen innocently if the sound driver
\r
5752 is busy; for instance, if another instance of WinBoard is playing
\r
5753 a sound at about the same time. */
\r
5759 OldOpenFileHook(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
5762 OPENFILENAME *ofn;
\r
5763 static UINT *number; /* gross that this is static */
\r
5765 switch (message) {
\r
5766 case WM_INITDIALOG: /* message: initialize dialog box */
\r
5767 /* Center the dialog over the application window */
\r
5768 ofn = (OPENFILENAME *) lParam;
\r
5769 if (ofn->Flags & OFN_ENABLETEMPLATE) {
\r
5770 number = (UINT *) ofn->lCustData;
\r
5771 SendMessage(GetDlgItem(hDlg, edt2), WM_SETTEXT, 0, (LPARAM) "");
\r
5775 CenterWindow (hDlg, GetWindow (hDlg, GW_OWNER));
\r
5776 Translate(hDlg, 1536);
\r
5777 return FALSE; /* Allow for further processing */
\r
5780 if ((LOWORD(wParam) == IDOK) && (number != NULL)) {
\r
5781 *number = GetDlgItemInt(hDlg, OPT_IndexNumberOld, &ok, FALSE);
\r
5783 return FALSE; /* Allow for further processing */
\r
5789 OpenFileHook(HWND hdlg, UINT uiMsg, WPARAM wParam, LPARAM lParam)
\r
5791 static UINT *number;
\r
5792 OPENFILENAME *ofname;
\r
5795 case WM_INITDIALOG:
\r
5796 Translate(hdlg, DLG_IndexNumber);
\r
5797 ofname = (OPENFILENAME *)lParam;
\r
5798 number = (UINT *)(ofname->lCustData);
\r
5801 ofnot = (OFNOTIFY *)lParam;
\r
5802 if (ofnot->hdr.code == CDN_FILEOK) {
\r
5803 *number = GetDlgItemInt(hdlg, OPT_IndexNumber, NULL, FALSE);
\r
5812 OpenFileDialog(HWND hwnd, char *write, char *defName, char *defExt, // [HGM] diag: type of 'write' now string
\r
5813 char *nameFilt, char *dlgTitle, UINT *number,
\r
5814 char fileTitle[MSG_SIZ], char fileName[MSG_SIZ])
\r
5816 OPENFILENAME openFileName;
\r
5817 char buf1[MSG_SIZ];
\r
5820 if (fileName == NULL) fileName = buf1;
\r
5821 if (defName == NULL) {
\r
5822 safeStrCpy(fileName, "*.", 3 );
\r
5823 strcat(fileName, defExt);
\r
5825 safeStrCpy(fileName, defName, MSG_SIZ );
\r
5827 if (fileTitle) safeStrCpy(fileTitle, "", MSG_SIZ );
\r
5828 if (number) *number = 0;
\r
5830 openFileName.lStructSize = sizeof(OPENFILENAME);
\r
5831 openFileName.hwndOwner = hwnd;
\r
5832 openFileName.hInstance = (HANDLE) hInst;
\r
5833 openFileName.lpstrFilter = nameFilt;
\r
5834 openFileName.lpstrCustomFilter = (LPSTR) NULL;
\r
5835 openFileName.nMaxCustFilter = 0L;
\r
5836 openFileName.nFilterIndex = 1L;
\r
5837 openFileName.lpstrFile = fileName;
\r
5838 openFileName.nMaxFile = MSG_SIZ;
\r
5839 openFileName.lpstrFileTitle = fileTitle;
\r
5840 openFileName.nMaxFileTitle = fileTitle ? MSG_SIZ : 0;
\r
5841 openFileName.lpstrInitialDir = NULL;
\r
5842 openFileName.lpstrTitle = dlgTitle;
\r
5843 openFileName.Flags = OFN_PATHMUSTEXIST | OFN_HIDEREADONLY
\r
5844 | (write[0] != 'r' ? 0 : OFN_FILEMUSTEXIST)
\r
5845 | (number ? OFN_ENABLETEMPLATE | OFN_ENABLEHOOK: 0)
\r
5846 | (oldDialog ? 0 : OFN_EXPLORER);
\r
5847 openFileName.nFileOffset = 0;
\r
5848 openFileName.nFileExtension = 0;
\r
5849 openFileName.lpstrDefExt = defExt;
\r
5850 openFileName.lCustData = (LONG) number;
\r
5851 openFileName.lpfnHook = oldDialog ?
\r
5852 (LPOFNHOOKPROC) OldOpenFileHook : (LPOFNHOOKPROC) OpenFileHook;
\r
5853 openFileName.lpTemplateName = (LPSTR)(oldDialog ? 1536 : DLG_IndexNumber);
\r
5855 if (write[0] != 'r' ? GetSaveFileName(&openFileName) :
\r
5856 GetOpenFileName(&openFileName)) {
\r
5857 /* open the file */
\r
5858 f = fopen(openFileName.lpstrFile, write);
\r
5860 MessageBox(hwnd, _("File open failed"), NULL,
\r
5861 MB_OK|MB_ICONEXCLAMATION);
\r
5865 int err = CommDlgExtendedError();
\r
5866 if (err != 0) DisplayError(_("Internal error in file dialog box"), err);
\r
5875 MenuPopup(HWND hwnd, POINT pt, HMENU hmenu, UINT def)
\r
5877 HMENU hmenuTrackPopup; /* floating pop-up menu */
\r
5880 * Get the first pop-up menu in the menu template. This is the
\r
5881 * menu that TrackPopupMenu displays.
\r
5883 hmenuTrackPopup = GetSubMenu(hmenu, 0);
\r
5884 TranslateOneMenu(10, hmenuTrackPopup);
\r
5886 SetMenuDefaultItem(hmenuTrackPopup, def, FALSE);
\r
5889 * TrackPopup uses screen coordinates, so convert the
\r
5890 * coordinates of the mouse click to screen coordinates.
\r
5892 ClientToScreen(hwnd, (LPPOINT) &pt);
\r
5894 /* Draw and track the floating pop-up menu. */
\r
5895 TrackPopupMenu(hmenuTrackPopup, TPM_CENTERALIGN | TPM_RIGHTBUTTON,
\r
5896 pt.x, pt.y, 0, hwnd, NULL);
\r
5898 /* Destroy the menu.*/
\r
5899 DestroyMenu(hmenu);
\r
5904 int sizeX, sizeY, newSizeX, newSizeY;
\r
5906 } ResizeEditPlusButtonsClosure;
\r
5909 ResizeEditPlusButtonsCallback(HWND hChild, LPARAM lparam)
\r
5911 ResizeEditPlusButtonsClosure *cl = (ResizeEditPlusButtonsClosure *)lparam;
\r
5915 if (hChild == cl->hText) return TRUE;
\r
5916 GetWindowRect(hChild, &rect); /* gives screen coords */
\r
5917 pt.x = rect.left + (cl->newSizeX - cl->sizeX)/2;
\r
5918 pt.y = rect.top + cl->newSizeY - cl->sizeY;
\r
5919 ScreenToClient(cl->hDlg, &pt);
\r
5920 cl->hdwp = DeferWindowPos(cl->hdwp, hChild, NULL,
\r
5921 pt.x, pt.y, rect.right - rect.left, rect.bottom - rect.top, SWP_NOZORDER);
\r
5925 /* Resize a dialog that has a (rich) edit field filling most of
\r
5926 the top, with a row of buttons below */
\r
5928 ResizeEditPlusButtons(HWND hDlg, HWND hText, int sizeX, int sizeY, int newSizeX, int newSizeY)
\r
5931 int newTextHeight, newTextWidth;
\r
5932 ResizeEditPlusButtonsClosure cl;
\r
5934 /*if (IsIconic(hDlg)) return;*/
\r
5935 if (newSizeX == sizeX && newSizeY == sizeY) return;
\r
5937 cl.hdwp = BeginDeferWindowPos(8);
\r
5939 GetWindowRect(hText, &rectText); /* gives screen coords */
\r
5940 newTextWidth = rectText.right - rectText.left + newSizeX - sizeX;
\r
5941 newTextHeight = rectText.bottom - rectText.top + newSizeY - sizeY;
\r
5942 if (newTextHeight < 0) {
\r
5943 newSizeY += -newTextHeight;
\r
5944 newTextHeight = 0;
\r
5946 cl.hdwp = DeferWindowPos(cl.hdwp, hText, NULL, 0, 0,
\r
5947 newTextWidth, newTextHeight, SWP_NOZORDER|SWP_NOMOVE);
\r
5953 cl.newSizeX = newSizeX;
\r
5954 cl.newSizeY = newSizeY;
\r
5955 EnumChildWindows(hDlg, ResizeEditPlusButtonsCallback, (LPARAM)&cl);
\r
5957 EndDeferWindowPos(cl.hdwp);
\r
5960 BOOL CenterWindowEx(HWND hwndChild, HWND hwndParent, int mode)
\r
5962 RECT rChild, rParent;
\r
5963 int wChild, hChild, wParent, hParent;
\r
5964 int wScreen, hScreen, xNew, yNew;
\r
5967 /* Get the Height and Width of the child window */
\r
5968 GetWindowRect (hwndChild, &rChild);
\r
5969 wChild = rChild.right - rChild.left;
\r
5970 hChild = rChild.bottom - rChild.top;
\r
5972 /* Get the Height and Width of the parent window */
\r
5973 GetWindowRect (hwndParent, &rParent);
\r
5974 wParent = rParent.right - rParent.left;
\r
5975 hParent = rParent.bottom - rParent.top;
\r
5977 /* Get the display limits */
\r
5978 hdc = GetDC (hwndChild);
\r
5979 wScreen = GetDeviceCaps (hdc, HORZRES);
\r
5980 hScreen = GetDeviceCaps (hdc, VERTRES);
\r
5981 ReleaseDC(hwndChild, hdc);
\r
5983 /* Calculate new X position, then adjust for screen */
\r
5984 xNew = rParent.left + ((wParent - wChild) /2);
\r
5987 } else if ((xNew+wChild) > wScreen) {
\r
5988 xNew = wScreen - wChild;
\r
5991 /* Calculate new Y position, then adjust for screen */
\r
5993 yNew = rParent.top + ((hParent - hChild) /2);
\r
5996 yNew = rParent.top + GetSystemMetrics( SM_CYCAPTION ) * 2 / 3;
\r
6001 } else if ((yNew+hChild) > hScreen) {
\r
6002 yNew = hScreen - hChild;
\r
6005 /* Set it, and return */
\r
6006 return SetWindowPos (hwndChild, NULL,
\r
6007 xNew, yNew, 0, 0, SWP_NOSIZE | SWP_NOZORDER);
\r
6010 /* Center one window over another */
\r
6011 BOOL CenterWindow (HWND hwndChild, HWND hwndParent)
\r
6013 return CenterWindowEx( hwndChild, hwndParent, 0 );
\r
6016 /*---------------------------------------------------------------------------*\
\r
6018 * Startup Dialog functions
\r
6020 \*---------------------------------------------------------------------------*/
\r
6022 InitComboStrings(HANDLE hwndCombo, char **cd)
\r
6024 SendMessage(hwndCombo, CB_RESETCONTENT, 0, 0);
\r
6026 while (*cd != NULL) {
\r
6027 SendMessage(hwndCombo, CB_ADDSTRING, 0, (LPARAM) T_(*cd));
\r
6033 InitComboStringsFromOption(HANDLE hwndCombo, char *str)
\r
6035 char buf1[MAX_ARG_LEN];
\r
6038 if (str[0] == '@') {
\r
6039 FILE* f = fopen(str + 1, "r");
\r
6041 DisplayFatalError(str + 1, errno, 2);
\r
6044 len = fread(buf1, 1, sizeof(buf1)-1, f);
\r
6046 buf1[len] = NULLCHAR;
\r
6050 SendMessage(hwndCombo, CB_RESETCONTENT, 0, 0);
\r
6053 char buf[MSG_SIZ];
\r
6054 char *end = strchr(str, '\n');
\r
6055 if (end == NULL) return;
\r
6056 memcpy(buf, str, end - str);
\r
6057 buf[end - str] = NULLCHAR;
\r
6058 SendMessage(hwndCombo, CB_ADDSTRING, 0, (LPARAM) buf);
\r
6064 SetStartupDialogEnables(HWND hDlg)
\r
6066 EnableWindow(GetDlgItem(hDlg, OPT_ChessEngineName),
\r
6067 IsDlgButtonChecked(hDlg, OPT_ChessEngine) ||
\r
6068 (appData.zippyPlay && IsDlgButtonChecked(hDlg, OPT_ChessServer)));
\r
6069 EnableWindow(GetDlgItem(hDlg, OPT_SecondChessEngineName),
\r
6070 IsDlgButtonChecked(hDlg, OPT_ChessEngine));
\r
6071 EnableWindow(GetDlgItem(hDlg, OPT_ChessServerName),
\r
6072 IsDlgButtonChecked(hDlg, OPT_ChessServer));
\r
6073 EnableWindow(GetDlgItem(hDlg, OPT_AdditionalOptions),
\r
6074 IsDlgButtonChecked(hDlg, OPT_AnyAdditional));
\r
6075 EnableWindow(GetDlgItem(hDlg, IDOK),
\r
6076 IsDlgButtonChecked(hDlg, OPT_ChessEngine) ||
\r
6077 IsDlgButtonChecked(hDlg, OPT_ChessServer) ||
\r
6078 IsDlgButtonChecked(hDlg, OPT_View));
\r
6082 QuoteForFilename(char *filename)
\r
6084 int dquote, space;
\r
6085 dquote = strchr(filename, '"') != NULL;
\r
6086 space = strchr(filename, ' ') != NULL;
\r
6087 if (dquote || space) {
\r
6099 InitEngineBox(HWND hDlg, HWND hwndCombo, char* nthcp, char* nthd, char* nthdir, char *nthnames)
\r
6101 char buf[MSG_SIZ];
\r
6104 InitComboStringsFromOption(hwndCombo, nthnames);
\r
6105 q = QuoteForFilename(nthcp);
\r
6106 snprintf(buf, MSG_SIZ, "%s%s%s", q, nthcp, q);
\r
6107 if (*nthdir != NULLCHAR) {
\r
6108 q = QuoteForFilename(nthdir);
\r
6109 snprintf(buf + strlen(buf), MSG_SIZ, " /%s=%s%s%s", nthd, q, nthdir, q);
\r
6111 if (*nthcp == NULLCHAR) {
\r
6112 SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) 0, (LPARAM) 0);
\r
6113 } else if (SendMessage(hwndCombo, CB_SELECTSTRING, (WPARAM) -1, (LPARAM) buf) == CB_ERR) {
\r
6114 SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) -1, (LPARAM) 0);
\r
6115 SendMessage(hwndCombo, WM_SETTEXT, (WPARAM) 0, (LPARAM) buf);
\r
6120 StartupDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
6122 char buf[MSG_SIZ];
\r
6126 switch (message) {
\r
6127 case WM_INITDIALOG:
\r
6128 /* Center the dialog */
\r
6129 CenterWindow (hDlg, GetDesktopWindow());
\r
6130 Translate(hDlg, DLG_Startup);
\r
6131 /* Initialize the dialog items */
\r
6132 InitEngineBox(hDlg, GetDlgItem(hDlg, OPT_ChessEngineName),
\r
6133 appData.firstChessProgram, "fd", appData.firstDirectory,
\r
6134 firstChessProgramNames);
\r
6135 InitEngineBox(hDlg, GetDlgItem(hDlg, OPT_SecondChessEngineName),
\r
6136 appData.secondChessProgram, singleList ? "fd" : "sd", appData.secondDirectory,
\r
6137 singleList ? firstChessProgramNames : secondChessProgramNames); //[HGM] single: use first list in second combo
\r
6138 hwndCombo = GetDlgItem(hDlg, OPT_ChessServerName);
\r
6139 InitComboStringsFromOption(hwndCombo, icsNames);
\r
6140 snprintf(buf, MSG_SIZ, "%s /icsport=%s", appData.icsHost, appData.icsPort);
\r
6141 if (*appData.icsHelper != NULLCHAR) {
\r
6142 char *q = QuoteForFilename(appData.icsHelper);
\r
6143 sprintf(buf + strlen(buf), " /icshelper=%s%s%s", q, appData.icsHelper, q);
\r
6145 if (*appData.icsHost == NULLCHAR) {
\r
6146 SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) 0, (LPARAM) 0);
\r
6147 /*SendMessage(hwndCombo, CB_SHOWDROPDOWN, (WPARAM) TRUE, (LPARAM) 0); !!too soon */
\r
6148 } else if (SendMessage(hwndCombo, CB_SELECTSTRING, (WPARAM) -1, (LPARAM) buf) == CB_ERR) {
\r
6149 SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) -1, (LPARAM) 0);
\r
6150 SendMessage(hwndCombo, WM_SETTEXT, (WPARAM) 0, (LPARAM) buf);
\r
6153 if (appData.icsActive) {
\r
6154 CheckDlgButton(hDlg, OPT_ChessServer, BST_CHECKED);
\r
6156 else if (appData.noChessProgram) {
\r
6157 CheckDlgButton(hDlg, OPT_View, BST_CHECKED);
\r
6160 CheckDlgButton(hDlg, OPT_ChessEngine, BST_CHECKED);
\r
6163 SetStartupDialogEnables(hDlg);
\r
6167 switch (LOWORD(wParam)) {
\r
6169 if (IsDlgButtonChecked(hDlg, OPT_ChessEngine)) {
\r
6170 safeStrCpy(buf, "/fcp=", sizeof(buf)/sizeof(buf[0]) );
\r
6171 GetDlgItemText(hDlg, OPT_ChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));
\r
6173 comboLine = strdup(p+5); // [HGM] recent: remember complete line of first combobox
\r
6174 ParseArgs(StringGet, &p);
\r
6175 safeStrCpy(buf, singleList ? "/fcp=" : "/scp=", sizeof(buf)/sizeof(buf[0]) );
\r
6176 GetDlgItemText(hDlg, OPT_SecondChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));
\r
6178 SwapEngines(singleList); // temporarily swap first and second, to load a second 'first', ...
\r
6179 ParseArgs(StringGet, &p);
\r
6180 SwapEngines(singleList); // ... and then make it 'second'
\r
6181 appData.noChessProgram = FALSE;
\r
6182 appData.icsActive = FALSE;
\r
6183 } else if (IsDlgButtonChecked(hDlg, OPT_ChessServer)) {
\r
6184 safeStrCpy(buf, "/ics /icshost=", sizeof(buf)/sizeof(buf[0]) );
\r
6185 GetDlgItemText(hDlg, OPT_ChessServerName, buf + strlen(buf), sizeof(buf) - strlen(buf));
\r
6187 ParseArgs(StringGet, &p);
\r
6188 if (appData.zippyPlay) {
\r
6189 safeStrCpy(buf, "/fcp=", sizeof(buf)/sizeof(buf[0]) );
\r
6190 GetDlgItemText(hDlg, OPT_ChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));
\r
6192 ParseArgs(StringGet, &p);
\r
6194 } else if (IsDlgButtonChecked(hDlg, OPT_View)) {
\r
6195 appData.noChessProgram = TRUE;
\r
6196 appData.icsActive = FALSE;
\r
6198 MessageBox(hDlg, _("Choose an option, or cancel to exit"),
\r
6199 _("Option Error"), MB_OK|MB_ICONEXCLAMATION);
\r
6202 if (IsDlgButtonChecked(hDlg, OPT_AnyAdditional)) {
\r
6203 GetDlgItemText(hDlg, OPT_AdditionalOptions, buf, sizeof(buf));
\r
6205 ParseArgs(StringGet, &p);
\r
6207 EndDialog(hDlg, TRUE);
\r
6214 case IDM_HELPCONTENTS:
\r
6215 if (!WinHelp (hDlg, "winboard.hlp", HELP_KEY,(DWORD)(LPSTR)"CONTENTS")) {
\r
6216 MessageBox (GetFocus(),
\r
6217 _("Unable to activate help"),
\r
6218 szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);
\r
6223 SetStartupDialogEnables(hDlg);
\r
6231 /*---------------------------------------------------------------------------*\
\r
6233 * About box dialog functions
\r
6235 \*---------------------------------------------------------------------------*/
\r
6237 /* Process messages for "About" dialog box */
\r
6239 About(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
6241 switch (message) {
\r
6242 case WM_INITDIALOG: /* message: initialize dialog box */
\r
6243 /* Center the dialog over the application window */
\r
6244 CenterWindow (hDlg, GetWindow (hDlg, GW_OWNER));
\r
6245 SetDlgItemText(hDlg, ABOUTBOX_Version, programVersion);
\r
6246 Translate(hDlg, ABOUTBOX);
\r
6250 case WM_COMMAND: /* message: received a command */
\r
6251 if (LOWORD(wParam) == IDOK /* "OK" box selected? */
\r
6252 || LOWORD(wParam) == IDCANCEL) { /* System menu close command? */
\r
6253 EndDialog(hDlg, TRUE); /* Exit the dialog */
\r
6261 /*---------------------------------------------------------------------------*\
\r
6263 * Comment Dialog functions
\r
6265 \*---------------------------------------------------------------------------*/
\r
6268 CommentDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
6270 static HANDLE hwndText = NULL;
\r
6271 int len, newSizeX, newSizeY, flags;
\r
6272 static int sizeX, sizeY;
\r
6277 switch (message) {
\r
6278 case WM_INITDIALOG: /* message: initialize dialog box */
\r
6279 /* Initialize the dialog items */
\r
6280 Translate(hDlg, DLG_EditComment);
\r
6281 hwndText = GetDlgItem(hDlg, OPT_CommentText);
\r
6282 SetDlgItemText(hDlg, OPT_CommentText, commentText);
\r
6283 EnableWindow(GetDlgItem(hDlg, OPT_CancelComment), editComment);
\r
6284 EnableWindow(GetDlgItem(hDlg, OPT_ClearComment), editComment);
\r
6285 EnableWindow(GetDlgItem(hDlg, OPT_EditComment), !editComment);
\r
6286 SendMessage(hwndText, EM_SETREADONLY, !editComment, 0);
\r
6287 SetWindowText(hDlg, commentTitle);
\r
6288 if (editComment) {
\r
6289 SetFocus(hwndText);
\r
6291 SetFocus(GetDlgItem(hDlg, IDOK));
\r
6293 SendMessage(GetDlgItem(hDlg, OPT_CommentText),
\r
6294 WM_SETFONT, (WPARAM)font[boardSize][COMMENT_FONT]->hf,
\r
6295 MAKELPARAM(FALSE, 0));
\r
6296 /* Size and position the dialog */
\r
6297 if (!commentDialog) {
\r
6298 commentDialog = hDlg;
\r
6299 flags = SWP_NOZORDER;
\r
6300 GetClientRect(hDlg, &rect);
\r
6301 sizeX = rect.right;
\r
6302 sizeY = rect.bottom;
\r
6303 if (wpComment.x != CW_USEDEFAULT && wpComment.y != CW_USEDEFAULT &&
\r
6304 wpComment.width != CW_USEDEFAULT && wpComment.height != CW_USEDEFAULT) {
\r
6305 WINDOWPLACEMENT wp;
\r
6306 EnsureOnScreen(&wpComment.x, &wpComment.y, 0, 0);
\r
6307 wp.length = sizeof(WINDOWPLACEMENT);
\r
6309 wp.showCmd = SW_SHOW;
\r
6310 wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;
\r
6311 wp.rcNormalPosition.left = wpComment.x;
\r
6312 wp.rcNormalPosition.right = wpComment.x + wpComment.width;
\r
6313 wp.rcNormalPosition.top = wpComment.y;
\r
6314 wp.rcNormalPosition.bottom = wpComment.y + wpComment.height;
\r
6315 SetWindowPlacement(hDlg, &wp);
\r
6317 GetClientRect(hDlg, &rect);
\r
6318 newSizeX = rect.right;
\r
6319 newSizeY = rect.bottom;
\r
6320 ResizeEditPlusButtons(hDlg, hwndText, sizeX, sizeY,
\r
6321 newSizeX, newSizeY);
\r
6326 SendDlgItemMessage( hDlg, OPT_CommentText, EM_SETEVENTMASK, 0, ENM_MOUSEEVENTS | ENM_KEYEVENTS );
\r
6329 case WM_COMMAND: /* message: received a command */
\r
6330 switch (LOWORD(wParam)) {
\r
6332 if (editComment) {
\r
6334 /* Read changed options from the dialog box */
\r
6335 hwndText = GetDlgItem(hDlg, OPT_CommentText);
\r
6336 len = GetWindowTextLength(hwndText);
\r
6337 str = (char *) malloc(len + 1);
\r
6338 GetWindowText(hwndText, str, len + 1);
\r
6347 ReplaceComment(commentIndex, str);
\r
6354 case OPT_CancelComment:
\r
6358 case OPT_ClearComment:
\r
6359 SetDlgItemText(hDlg, OPT_CommentText, "");
\r
6362 case OPT_EditComment:
\r
6363 EditCommentEvent();
\r
6371 case WM_NOTIFY: // [HGM] vari: cloned from whistory.c
\r
6372 if( wParam == OPT_CommentText ) {
\r
6373 MSGFILTER * lpMF = (MSGFILTER *) lParam;
\r
6375 if( lpMF->msg == WM_RBUTTONDOWN && (lpMF->wParam & (MK_CONTROL | MK_SHIFT)) == 0 ||
\r
6376 lpMF->msg == WM_CHAR && lpMF->wParam == '\022' ) {
\r
6380 pt.x = LOWORD( lpMF->lParam );
\r
6381 pt.y = HIWORD( lpMF->lParam );
\r
6383 if(lpMF->msg == WM_CHAR) {
\r
6385 SendDlgItemMessage( hDlg, OPT_CommentText, EM_EXGETSEL, 0, (LPARAM) &sel );
\r
6386 index = sel.cpMin;
\r
6388 index = SendDlgItemMessage( hDlg, OPT_CommentText, EM_CHARFROMPOS, 0, (LPARAM) &pt );
\r
6390 hwndText = GetDlgItem(hDlg, OPT_CommentText); // cloned from above
\r
6391 len = GetWindowTextLength(hwndText);
\r
6392 str = (char *) malloc(len + 1);
\r
6393 GetWindowText(hwndText, str, len + 1);
\r
6394 ReplaceComment(commentIndex, str);
\r
6395 if(commentIndex != currentMove) ToNrEvent(commentIndex);
\r
6396 LoadVariation( index, str ); // [HGM] also does the actual moving to it, now
\r
6399 /* Zap the message for good: apparently, returning non-zero is not enough */
\r
6400 lpMF->msg = WM_USER;
\r
6408 newSizeX = LOWORD(lParam);
\r
6409 newSizeY = HIWORD(lParam);
\r
6410 ResizeEditPlusButtons(hDlg, hwndText, sizeX, sizeY, newSizeX, newSizeY);
\r
6415 case WM_GETMINMAXINFO:
\r
6416 /* Prevent resizing window too small */
\r
6417 mmi = (MINMAXINFO *) lParam;
\r
6418 mmi->ptMinTrackSize.x = 100;
\r
6419 mmi->ptMinTrackSize.y = 100;
\r
6426 EitherCommentPopUp(int index, char *title, char *str, BOOLEAN edit)
\r
6431 CheckMenuItem(GetMenu(hwndMain), IDM_Comment, edit ? MF_CHECKED : MF_UNCHECKED);
\r
6433 if (str == NULL) str = "";
\r
6434 p = (char *) malloc(2 * strlen(str) + 2);
\r
6437 if (*str == '\n') *q++ = '\r';
\r
6441 if (commentText != NULL) free(commentText);
\r
6443 commentIndex = index;
\r
6444 commentTitle = title;
\r
6446 editComment = edit;
\r
6448 if (commentDialog) {
\r
6449 SendMessage(commentDialog, WM_INITDIALOG, 0, 0);
\r
6450 if (!commentUp) ShowWindow(commentDialog, SW_SHOW);
\r
6452 lpProc = MakeProcInstance((FARPROC)CommentDialog, hInst);
\r
6453 CreateDialog(hInst, MAKEINTRESOURCE(DLG_EditComment),
\r
6454 hwndMain, (DLGPROC)lpProc);
\r
6455 FreeProcInstance(lpProc);
\r
6461 /*---------------------------------------------------------------------------*\
\r
6463 * Type-in move dialog functions
\r
6465 \*---------------------------------------------------------------------------*/
\r
6468 TypeInMoveDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
6470 char move[MSG_SIZ];
\r
6473 switch (message) {
\r
6474 case WM_INITDIALOG:
\r
6475 move[0] = (char) lParam;
\r
6476 move[1] = NULLCHAR;
\r
6477 CenterWindowEx(hDlg, GetWindow(hDlg, GW_OWNER), 1 );
\r
6478 Translate(hDlg, DLG_TypeInMove);
\r
6479 hInput = GetDlgItem(hDlg, OPT_Move);
\r
6480 SetWindowText(hInput, move);
\r
6482 SendMessage(hInput, EM_SETSEL, (WPARAM)9999, (LPARAM)9999);
\r
6486 switch (LOWORD(wParam)) {
\r
6489 shiftKey = GetKeyState(VK_SHIFT) < 0; // [HGM] remember last shift status
\r
6490 GetDlgItemText(hDlg, OPT_Move, move, sizeof(move));
\r
6491 TypeInDoneEvent(move);
\r
6492 EndDialog(hDlg, TRUE);
\r
6495 EndDialog(hDlg, FALSE);
\r
6506 PopUpMoveDialog(char firstchar)
\r
6510 lpProc = MakeProcInstance((FARPROC)TypeInMoveDialog, hInst);
\r
6511 DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_TypeInMove),
\r
6512 hwndMain, (DLGPROC)lpProc, (LPARAM)firstchar);
\r
6513 FreeProcInstance(lpProc);
\r
6516 /*---------------------------------------------------------------------------*\
\r
6518 * Type-in name dialog functions
\r
6520 \*---------------------------------------------------------------------------*/
\r
6523 TypeInNameDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
6525 char move[MSG_SIZ];
\r
6528 switch (message) {
\r
6529 case WM_INITDIALOG:
\r
6530 move[0] = (char) lParam;
\r
6531 move[1] = NULLCHAR;
\r
6532 CenterWindowEx(hDlg, GetWindow(hDlg, GW_OWNER), 1 );
\r
6533 Translate(hDlg, DLG_TypeInName);
\r
6534 hInput = GetDlgItem(hDlg, OPT_Name);
\r
6535 SetWindowText(hInput, move);
\r
6537 SendMessage(hInput, EM_SETSEL, (WPARAM)9999, (LPARAM)9999);
\r
6541 switch (LOWORD(wParam)) {
\r
6543 GetDlgItemText(hDlg, OPT_Name, move, sizeof(move));
\r
6544 appData.userName = strdup(move);
\r
6547 if(gameMode == MachinePlaysWhite || gameMode == MachinePlaysBlack) {
\r
6548 snprintf(move, MSG_SIZ, "%s vs. %s", gameInfo.white, gameInfo.black);
\r
6549 DisplayTitle(move);
\r
6553 EndDialog(hDlg, TRUE);
\r
6556 EndDialog(hDlg, FALSE);
\r
6567 PopUpNameDialog(char firstchar)
\r
6571 lpProc = MakeProcInstance((FARPROC)TypeInNameDialog, hInst);
\r
6572 DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_TypeInName),
\r
6573 hwndMain, (DLGPROC)lpProc, (LPARAM)firstchar);
\r
6574 FreeProcInstance(lpProc);
\r
6577 /*---------------------------------------------------------------------------*\
\r
6581 \*---------------------------------------------------------------------------*/
\r
6583 /* Nonmodal error box */
\r
6584 LRESULT CALLBACK ErrorDialog(HWND hDlg, UINT message,
\r
6585 WPARAM wParam, LPARAM lParam);
\r
6588 ErrorPopUp(char *title, char *content)
\r
6592 BOOLEAN modal = hwndMain == NULL;
\r
6610 strncpy(errorTitle, title, sizeof(errorTitle));
\r
6611 errorTitle[sizeof(errorTitle) - 1] = '\0';
\r
6614 MessageBox(NULL, errorMessage, errorTitle, MB_OK|MB_ICONEXCLAMATION);
\r
6616 lpProc = MakeProcInstance((FARPROC)ErrorDialog, hInst);
\r
6617 CreateDialog(hInst, MAKEINTRESOURCE(DLG_Error),
\r
6618 hwndMain, (DLGPROC)lpProc);
\r
6619 FreeProcInstance(lpProc);
\r
6626 if (!appData.popupMoveErrors && moveErrorMessageUp) DisplayMessage("", "");
\r
6627 if (errorDialog == NULL) return;
\r
6628 DestroyWindow(errorDialog);
\r
6629 errorDialog = NULL;
\r
6630 if(errorExitStatus) ExitEvent(errorExitStatus);
\r
6634 ErrorDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
6639 switch (message) {
\r
6640 case WM_INITDIALOG:
\r
6641 GetWindowRect(hDlg, &rChild);
\r
6644 SetWindowPos(hDlg, NULL, rChild.left,
\r
6645 rChild.top + boardRect.top - (rChild.bottom - rChild.top),
\r
6646 0, 0, SWP_NOZORDER|SWP_NOSIZE);
\r
6650 [AS] It seems that the above code wants to move the dialog up in the "caption
\r
6651 area" of the main window, but it uses the dialog height as an hard-coded constant,
\r
6652 and it doesn't work when you resize the dialog.
\r
6653 For now, just give it a default position.
\r
6655 SetWindowPos(hDlg, NULL, boardRect.left+8, boardRect.top+8, 0, 0, SWP_NOZORDER|SWP_NOSIZE);
\r
6656 Translate(hDlg, DLG_Error);
\r
6658 errorDialog = hDlg;
\r
6659 SetWindowText(hDlg, errorTitle);
\r
6660 hwndText = GetDlgItem(hDlg, OPT_ErrorText);
\r
6661 SetDlgItemText(hDlg, OPT_ErrorText, errorMessage);
\r
6665 switch (LOWORD(wParam)) {
\r
6668 if (errorDialog == hDlg) errorDialog = NULL;
\r
6669 DestroyWindow(hDlg);
\r
6681 HWND gothicDialog = NULL;
\r
6684 GothicDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
6688 int height = GetSystemMetrics(SM_CYCAPTION)+GetSystemMetrics(SM_CYFRAME);
\r
6690 switch (message) {
\r
6691 case WM_INITDIALOG:
\r
6692 GetWindowRect(hDlg, &rChild);
\r
6694 SetWindowPos(hDlg, NULL, wpMain.x, wpMain.y-height, wpMain.width, height,
\r
6698 [AS] It seems that the above code wants to move the dialog up in the "caption
\r
6699 area" of the main window, but it uses the dialog height as an hard-coded constant,
\r
6700 and it doesn't work when you resize the dialog.
\r
6701 For now, just give it a default position.
\r
6703 gothicDialog = hDlg;
\r
6704 SetWindowText(hDlg, errorTitle);
\r
6705 hwndText = GetDlgItem(hDlg, OPT_ErrorText);
\r
6706 SetDlgItemText(hDlg, OPT_ErrorText, errorMessage);
\r
6710 switch (LOWORD(wParam)) {
\r
6713 if (errorDialog == hDlg) errorDialog = NULL;
\r
6714 DestroyWindow(hDlg);
\r
6726 GothicPopUp(char *title, VariantClass variant)
\r
6729 static char *lastTitle;
\r
6731 strncpy(errorTitle, title, sizeof(errorTitle));
\r
6732 errorTitle[sizeof(errorTitle) - 1] = '\0';
\r
6734 if(lastTitle != title && gothicDialog != NULL) {
\r
6735 DestroyWindow(gothicDialog);
\r
6736 gothicDialog = NULL;
\r
6738 if(variant != VariantNormal && gothicDialog == NULL) {
\r
6739 title = lastTitle;
\r
6740 lpProc = MakeProcInstance((FARPROC)GothicDialog, hInst);
\r
6741 CreateDialog(hInst, MAKEINTRESOURCE(DLG_Error),
\r
6742 hwndMain, (DLGPROC)lpProc);
\r
6743 FreeProcInstance(lpProc);
\r
6748 /*---------------------------------------------------------------------------*\
\r
6750 * Ics Interaction console functions
\r
6752 \*---------------------------------------------------------------------------*/
\r
6754 #define HISTORY_SIZE 64
\r
6755 static char *history[HISTORY_SIZE];
\r
6756 int histIn = 0, histP = 0;
\r
6759 SaveInHistory(char *cmd)
\r
6761 if (history[histIn] != NULL) {
\r
6762 free(history[histIn]);
\r
6763 history[histIn] = NULL;
\r
6765 if (*cmd == NULLCHAR) return;
\r
6766 history[histIn] = StrSave(cmd);
\r
6767 histIn = (histIn + 1) % HISTORY_SIZE;
\r
6768 if (history[histIn] != NULL) {
\r
6769 free(history[histIn]);
\r
6770 history[histIn] = NULL;
\r
6776 PrevInHistory(char *cmd)
\r
6779 if (histP == histIn) {
\r
6780 if (history[histIn] != NULL) free(history[histIn]);
\r
6781 history[histIn] = StrSave(cmd);
\r
6783 newhp = (histP - 1 + HISTORY_SIZE) % HISTORY_SIZE;
\r
6784 if (newhp == histIn || history[newhp] == NULL) return NULL;
\r
6786 return history[histP];
\r
6792 if (histP == histIn) return NULL;
\r
6793 histP = (histP + 1) % HISTORY_SIZE;
\r
6794 return history[histP];
\r
6798 LoadIcsTextMenu(IcsTextMenuEntry *e)
\r
6802 hmenu = LoadMenu(hInst, "TextMenu");
\r
6803 h = GetSubMenu(hmenu, 0);
\r
6805 if (strcmp(e->item, "-") == 0) {
\r
6806 AppendMenu(h, MF_SEPARATOR, 0, 0);
\r
6807 } else { // [HGM] re-written a bit to use only one AppendMenu call for both cases (| or no |)
\r
6808 int flags = MF_STRING, j = 0;
\r
6809 if (e->item[0] == '|') {
\r
6810 flags |= MF_MENUBARBREAK;
\r
6813 if(!strcmp(e->command, "none")) flags |= MF_GRAYED; // [HGM] chatclick: provide inactive dummy
\r
6814 AppendMenu(h, flags, IDM_CommandX + i, e->item + j);
\r
6822 WNDPROC consoleTextWindowProc;
\r
6825 CommandX(HWND hwnd, char *command, BOOLEAN getname, BOOLEAN immediate)
\r
6827 char buf[MSG_SIZ], name[MSG_SIZ];
\r
6828 HWND hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
6832 SetWindowText(hInput, command);
\r
6834 SendMessage(hInput, WM_CHAR, '\r', 0);
\r
6836 sel.cpMin = 999999;
\r
6837 sel.cpMax = 999999;
\r
6838 SendMessage(hInput, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
6843 SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);
\r
6844 if (sel.cpMin == sel.cpMax) {
\r
6845 /* Expand to surrounding word */
\r
6848 tr.chrg.cpMax = sel.cpMin;
\r
6849 tr.chrg.cpMin = --sel.cpMin;
\r
6850 if (sel.cpMin < 0) break;
\r
6851 tr.lpstrText = name;
\r
6852 SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr);
\r
6853 } while (isalpha(name[0]) || isdigit(name[0]) || name[0] == '-');
\r
6857 tr.chrg.cpMin = sel.cpMax;
\r
6858 tr.chrg.cpMax = ++sel.cpMax;
\r
6859 tr.lpstrText = name;
\r
6860 if (SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr) < 1) break;
\r
6861 } while (isalpha(name[0]) || isdigit(name[0]) || name[0] == '-');
\r
6864 if (sel.cpMax == sel.cpMin || sel.cpMax - sel.cpMin > MSG_SIZ/2) {
\r
6865 MessageBeep(MB_ICONEXCLAMATION);
\r
6869 tr.lpstrText = name;
\r
6870 SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr);
\r
6872 if (sel.cpMax - sel.cpMin > MSG_SIZ/2) {
\r
6873 MessageBeep(MB_ICONEXCLAMATION);
\r
6876 SendMessage(hwnd, EM_GETSELTEXT, 0, (LPARAM) name);
\r
6879 if(strstr(command, "%s")) snprintf(buf, MSG_SIZ, command, name); else
\r
6880 snprintf(buf, MSG_SIZ, "%s %s", command, name);
\r
6881 SetWindowText(hInput, buf);
\r
6882 SendMessage(hInput, WM_CHAR, '\r', 0);
\r
6884 if(!strcmp(command, "chat")) { ChatPopUp(name); return; }
\r
6885 snprintf(buf, MSG_SIZ, "%s %s ", command, name); /* trailing space */
\r
6886 SetWindowText(hInput, buf);
\r
6887 sel.cpMin = 999999;
\r
6888 sel.cpMax = 999999;
\r
6889 SendMessage(hInput, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
6895 ConsoleTextSubclass(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
\r
6900 switch (message) {
\r
6902 if (!(GetKeyState(VK_CONTROL) & ~1)) break;
\r
6903 if(wParam=='R') return 0;
\r
6906 SendMessage(hwnd, EM_LINESCROLL, 0, -999999);
\r
6909 sel.cpMin = 999999;
\r
6910 sel.cpMax = 999999;
\r
6911 SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
6912 SendMessage(hwnd, EM_SCROLLCARET, 0, 0);
\r
6917 if(wParam != '\022') {
\r
6918 if (wParam == '\t') {
\r
6919 if (GetKeyState(VK_SHIFT) < 0) {
\r
6921 if (IsIconic(hwndMain)) ShowWindow(hwndMain, SW_RESTORE);
\r
6922 if (buttonDesc[0].hwnd) {
\r
6923 SetFocus(buttonDesc[0].hwnd);
\r
6925 SetFocus(hwndMain);
\r
6929 SetFocus(GetDlgItem(hwndConsole, OPT_ConsoleInput));
\r
6932 hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
6933 JAWS_DELETE( SetFocus(hInput); )
\r
6934 SendMessage(hInput, message, wParam, lParam);
\r
6937 } // [HGM] navigate: for Ctrl+R, flow into next case (moved up here) to summon up menu
\r
6939 case WM_RBUTTONDOWN:
\r
6940 if (!(GetKeyState(VK_SHIFT) & ~1)) {
\r
6941 /* Move selection here if it was empty */
\r
6943 pt.x = LOWORD(lParam);
\r
6944 pt.y = HIWORD(lParam);
\r
6945 SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);
\r
6946 if (sel.cpMin == sel.cpMax) {
\r
6947 if(lParam != -1) sel.cpMin = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&pt); /*doc is wrong*/
\r
6948 sel.cpMax = sel.cpMin;
\r
6949 SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
6951 SendMessage(hwnd, EM_HIDESELECTION, FALSE, FALSE);
\r
6952 { // [HGM] chatclick: code moved here from WM_RBUTTONUP case, to have menu appear on down-click
\r
6954 HMENU hmenu = LoadIcsTextMenu(icsTextMenuEntry);
\r
6955 SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);
\r
6956 if (sel.cpMin == sel.cpMax) {
\r
6957 EnableMenuItem(hmenu, IDM_Copy, MF_BYCOMMAND|MF_GRAYED);
\r
6958 EnableMenuItem(hmenu, IDM_QuickPaste, MF_BYCOMMAND|MF_GRAYED);
\r
6960 if (!IsClipboardFormatAvailable(CF_TEXT)) {
\r
6961 EnableMenuItem(hmenu, IDM_Paste, MF_BYCOMMAND|MF_GRAYED);
\r
6963 pt.x = LOWORD(lParam)-30; // [HGM] chatclick: make menu pop up with pointer above upper-right item
\r
6964 pt.y = HIWORD(lParam)-10; // make it appear as if mouse moved there, so it will be selected on up-click
\r
6965 PostMessage(hwnd, WM_MOUSEMOVE, wParam, lParam+5);
\r
6966 MenuPopup(hwnd, pt, hmenu, -1);
\r
6970 case WM_RBUTTONUP:
\r
6971 if (GetKeyState(VK_SHIFT) & ~1) {
\r
6972 SendDlgItemMessage(hwndConsole, OPT_ConsoleText,
\r
6973 WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);
\r
6977 hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
6979 return SendMessage(hInput, message, wParam, lParam);
\r
6980 case WM_MBUTTONDOWN:
\r
6981 return SendMessage(hwnd, WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);
\r
6983 switch (LOWORD(wParam)) {
\r
6984 case IDM_QuickPaste:
\r
6986 SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);
\r
6987 if (sel.cpMin == sel.cpMax) {
\r
6988 MessageBeep(MB_ICONEXCLAMATION);
\r
6991 SendMessage(hwnd, WM_COPY, 0, 0);
\r
6992 hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
6993 SendMessage(hInput, WM_PASTE, 0, 0);
\r
6998 SendMessage(hwnd, WM_CUT, 0, 0);
\r
7001 SendMessage(hwnd, WM_PASTE, 0, 0);
\r
7004 SendMessage(hwnd, WM_COPY, 0, 0);
\r
7008 int i = LOWORD(wParam) - IDM_CommandX;
\r
7009 if (i >= 0 && i < ICS_TEXT_MENU_SIZE &&
\r
7010 icsTextMenuEntry[i].command != NULL) {
\r
7011 CommandX(hwnd, icsTextMenuEntry[i].command,
\r
7012 icsTextMenuEntry[i].getname,
\r
7013 icsTextMenuEntry[i].immediate);
\r
7021 return (*consoleTextWindowProc)(hwnd, message, wParam, lParam);
\r
7024 WNDPROC consoleInputWindowProc;
\r
7027 ConsoleInputSubclass(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
\r
7029 char buf[MSG_SIZ];
\r
7031 static BOOL sendNextChar = FALSE;
\r
7032 static BOOL quoteNextChar = FALSE;
\r
7033 InputSource *is = consoleInputSource;
\r
7037 switch (message) {
\r
7039 if (!appData.localLineEditing || sendNextChar) {
\r
7040 is->buf[0] = (CHAR) wParam;
\r
7042 SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);
\r
7043 sendNextChar = FALSE;
\r
7046 if (quoteNextChar) {
\r
7047 buf[0] = (char) wParam;
\r
7048 buf[1] = NULLCHAR;
\r
7049 SendMessage(hwnd, EM_REPLACESEL, TRUE, (LPARAM) buf);
\r
7050 quoteNextChar = FALSE;
\r
7054 case '\r': /* Enter key */
\r
7055 is->count = GetWindowText(hwnd, is->buf, INPUT_SOURCE_BUF_SIZE-1);
\r
7056 if (consoleEcho) SaveInHistory(is->buf);
\r
7057 is->buf[is->count++] = '\n';
\r
7058 is->buf[is->count] = NULLCHAR;
\r
7059 SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);
\r
7060 if (consoleEcho) {
\r
7061 ConsoleOutput(is->buf, is->count, TRUE);
\r
7062 } else if (appData.localLineEditing) {
\r
7063 ConsoleOutput("\n", 1, TRUE);
\r
7066 case '\033': /* Escape key */
\r
7067 SetWindowText(hwnd, "");
\r
7068 cf.cbSize = sizeof(CHARFORMAT);
\r
7069 cf.dwMask = CFM_COLOR|CFM_BOLD|CFM_ITALIC|CFM_UNDERLINE|CFM_STRIKEOUT;
\r
7070 if (consoleEcho) {
\r
7071 cf.crTextColor = textAttribs[ColorNormal].color;
\r
7073 cf.crTextColor = COLOR_ECHOOFF;
\r
7075 cf.dwEffects = textAttribs[ColorNormal].effects;
\r
7076 SendMessage(hwnd, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf);
\r
7078 case '\t': /* Tab key */
\r
7079 if (GetKeyState(VK_SHIFT) < 0) {
\r
7081 SetFocus(GetDlgItem(hwndConsole, OPT_ConsoleText));
\r
7084 if (IsIconic(hwndMain)) ShowWindow(hwndMain, SW_RESTORE);
\r
7085 if (buttonDesc[0].hwnd) {
\r
7086 SetFocus(buttonDesc[0].hwnd);
\r
7088 SetFocus(hwndMain);
\r
7092 case '\023': /* Ctrl+S */
\r
7093 sendNextChar = TRUE;
\r
7095 case '\021': /* Ctrl+Q */
\r
7096 quoteNextChar = TRUE;
\r
7106 GetWindowText(hwnd, buf, MSG_SIZ);
\r
7107 p = PrevInHistory(buf);
\r
7109 SetWindowText(hwnd, p);
\r
7110 sel.cpMin = 999999;
\r
7111 sel.cpMax = 999999;
\r
7112 SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
7117 p = NextInHistory();
\r
7119 SetWindowText(hwnd, p);
\r
7120 sel.cpMin = 999999;
\r
7121 sel.cpMax = 999999;
\r
7122 SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
7128 if (!(GetKeyState(VK_CONTROL) & ~1)) break;
\r
7132 SendDlgItemMessage(hwndConsole, OPT_ConsoleText, message, wParam, lParam);
\r
7136 case WM_MBUTTONDOWN:
\r
7137 SendDlgItemMessage(hwndConsole, OPT_ConsoleText,
\r
7138 WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);
\r
7140 case WM_RBUTTONUP:
\r
7141 if (GetKeyState(VK_SHIFT) & ~1) {
\r
7142 SendDlgItemMessage(hwndConsole, OPT_ConsoleText,
\r
7143 WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);
\r
7147 hmenu = LoadMenu(hInst, "InputMenu");
\r
7148 SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);
\r
7149 if (sel.cpMin == sel.cpMax) {
\r
7150 EnableMenuItem(hmenu, IDM_Copy, MF_BYCOMMAND|MF_GRAYED);
\r
7151 EnableMenuItem(hmenu, IDM_Cut, MF_BYCOMMAND|MF_GRAYED);
\r
7153 if (!IsClipboardFormatAvailable(CF_TEXT)) {
\r
7154 EnableMenuItem(hmenu, IDM_Paste, MF_BYCOMMAND|MF_GRAYED);
\r
7156 pt.x = LOWORD(lParam);
\r
7157 pt.y = HIWORD(lParam);
\r
7158 MenuPopup(hwnd, pt, hmenu, -1);
\r
7162 switch (LOWORD(wParam)) {
\r
7164 SendMessage(hwnd, EM_UNDO, 0, 0);
\r
7166 case IDM_SelectAll:
\r
7168 sel.cpMax = -1; /*999999?*/
\r
7169 SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
7172 SendMessage(hwnd, WM_CUT, 0, 0);
\r
7175 SendMessage(hwnd, WM_PASTE, 0, 0);
\r
7178 SendMessage(hwnd, WM_COPY, 0, 0);
\r
7183 return (*consoleInputWindowProc)(hwnd, message, wParam, lParam);
\r
7186 #define CO_MAX 100000
\r
7187 #define CO_TRIM 1000
\r
7190 ConsoleWndProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
7192 static SnapData sd;
\r
7193 HWND hText, hInput;
\r
7195 static int sizeX, sizeY;
\r
7196 int newSizeX, newSizeY;
\r
7200 hText = GetDlgItem(hDlg, OPT_ConsoleText);
\r
7201 hInput = GetDlgItem(hDlg, OPT_ConsoleInput);
\r
7203 switch (message) {
\r
7205 if (((NMHDR*)lParam)->code == EN_LINK)
\r
7207 ENLINK *pLink = (ENLINK*)lParam;
\r
7208 if (pLink->msg == WM_LBUTTONUP)
\r
7212 tr.chrg = pLink->chrg;
\r
7213 tr.lpstrText = malloc(1+tr.chrg.cpMax-tr.chrg.cpMin);
\r
7214 SendMessage(hText, EM_GETTEXTRANGE, 0, (LPARAM)&tr);
\r
7215 ShellExecute(NULL, "open", tr.lpstrText, NULL, NULL, SW_SHOW);
\r
7216 free(tr.lpstrText);
\r
7220 case WM_INITDIALOG: /* message: initialize dialog box */
\r
7221 hwndConsole = hDlg;
\r
7223 consoleTextWindowProc = (WNDPROC)
\r
7224 SetWindowLongPtr(hText, GWLP_WNDPROC, (LONG_PTR) ConsoleTextSubclass);
\r
7225 SendMessage(hText, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);
\r
7226 consoleInputWindowProc = (WNDPROC)
\r
7227 SetWindowLongPtr(hInput, GWLP_WNDPROC, (LONG_PTR) ConsoleInputSubclass);
\r
7228 SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);
\r
7229 Colorize(ColorNormal, TRUE);
\r
7230 SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &consoleCF);
\r
7231 ChangedConsoleFont();
\r
7232 GetClientRect(hDlg, &rect);
\r
7233 sizeX = rect.right;
\r
7234 sizeY = rect.bottom;
\r
7235 if (wpConsole.x != CW_USEDEFAULT && wpConsole.y != CW_USEDEFAULT &&
\r
7236 wpConsole.width != CW_USEDEFAULT && wpConsole.height != CW_USEDEFAULT) {
\r
7237 WINDOWPLACEMENT wp;
\r
7238 EnsureOnScreen(&wpConsole.x, &wpConsole.y, 0, 0);
\r
7239 wp.length = sizeof(WINDOWPLACEMENT);
\r
7241 wp.showCmd = SW_SHOW;
\r
7242 wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;
\r
7243 wp.rcNormalPosition.left = wpConsole.x;
\r
7244 wp.rcNormalPosition.right = wpConsole.x + wpConsole.width;
\r
7245 wp.rcNormalPosition.top = wpConsole.y;
\r
7246 wp.rcNormalPosition.bottom = wpConsole.y + wpConsole.height;
\r
7247 SetWindowPlacement(hDlg, &wp);
\r
7250 // [HGM] Chessknight's change 2004-07-13
\r
7251 else { /* Determine Defaults */
\r
7252 WINDOWPLACEMENT wp;
\r
7253 wpConsole.x = wpMain.width + 1;
\r
7254 wpConsole.y = wpMain.y;
\r
7255 wpConsole.width = screenWidth - wpMain.width;
\r
7256 wpConsole.height = wpMain.height;
\r
7257 EnsureOnScreen(&wpConsole.x, &wpConsole.y, 0, 0);
\r
7258 wp.length = sizeof(WINDOWPLACEMENT);
\r
7260 wp.showCmd = SW_SHOW;
\r
7261 wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;
\r
7262 wp.rcNormalPosition.left = wpConsole.x;
\r
7263 wp.rcNormalPosition.right = wpConsole.x + wpConsole.width;
\r
7264 wp.rcNormalPosition.top = wpConsole.y;
\r
7265 wp.rcNormalPosition.bottom = wpConsole.y + wpConsole.height;
\r
7266 SetWindowPlacement(hDlg, &wp);
\r
7269 // Allow hText to highlight URLs and send notifications on them
\r
7270 wMask = (WORD) SendMessage(hText, EM_GETEVENTMASK, 0, 0L);
\r
7271 SendMessage(hText, EM_SETEVENTMASK, 0, wMask | ENM_LINK);
\r
7272 SendMessage(hText, EM_AUTOURLDETECT, TRUE, 0L);
\r
7273 SetWindowLongPtr(hText, GWLP_USERDATA, 79); // initialize the text window's width
\r
7287 if (IsIconic(hDlg)) break;
\r
7288 newSizeX = LOWORD(lParam);
\r
7289 newSizeY = HIWORD(lParam);
\r
7290 if (sizeX != newSizeX || sizeY != newSizeY) {
\r
7291 RECT rectText, rectInput;
\r
7293 int newTextHeight, newTextWidth;
\r
7294 GetWindowRect(hText, &rectText);
\r
7295 newTextWidth = rectText.right - rectText.left + newSizeX - sizeX;
\r
7296 newTextHeight = rectText.bottom - rectText.top + newSizeY - sizeY;
\r
7297 if (newTextHeight < 0) {
\r
7298 newSizeY += -newTextHeight;
\r
7299 newTextHeight = 0;
\r
7301 SetWindowPos(hText, NULL, 0, 0,
\r
7302 newTextWidth, newTextHeight, SWP_NOZORDER|SWP_NOMOVE);
\r
7303 GetWindowRect(hInput, &rectInput); /* gives screen coords */
\r
7304 pt.x = rectInput.left;
\r
7305 pt.y = rectInput.top + newSizeY - sizeY;
\r
7306 ScreenToClient(hDlg, &pt);
\r
7307 SetWindowPos(hInput, NULL,
\r
7308 pt.x, pt.y, /* needs client coords */
\r
7309 rectInput.right - rectInput.left + newSizeX - sizeX,
\r
7310 rectInput.bottom - rectInput.top, SWP_NOZORDER);
\r
7316 case WM_GETMINMAXINFO:
\r
7317 /* Prevent resizing window too small */
\r
7318 mmi = (MINMAXINFO *) lParam;
\r
7319 mmi->ptMinTrackSize.x = 100;
\r
7320 mmi->ptMinTrackSize.y = 100;
\r
7323 /* [AS] Snapping */
\r
7324 case WM_ENTERSIZEMOVE:
\r
7325 return OnEnterSizeMove( &sd, hDlg, wParam, lParam );
\r
7328 return OnSizing( &sd, hDlg, wParam, lParam );
\r
7331 return OnMoving( &sd, hDlg, wParam, lParam );
\r
7333 case WM_EXITSIZEMOVE:
\r
7334 UpdateICSWidth(hText);
\r
7335 return OnExitSizeMove( &sd, hDlg, wParam, lParam );
\r
7338 return DefWindowProc(hDlg, message, wParam, lParam);
\r
7346 if (hwndConsole) return;
\r
7347 hCons = CreateDialog(hInst, szConsoleName, 0, NULL);
\r
7348 SendMessage(hCons, WM_INITDIALOG, 0, 0);
\r
7353 ConsoleOutput(char* data, int length, int forceVisible)
\r
7358 char buf[CO_MAX+1];
\r
7361 static int delayLF = 0;
\r
7362 CHARRANGE savesel, sel;
\r
7364 if (hwndConsole == NULL || length > CO_MAX-100 || length == 0) return;
\r
7372 while (length--) {
\r
7380 } else if (*p == '\007') {
\r
7381 MyPlaySound(&sounds[(int)SoundBell]);
\r
7388 hText = GetDlgItem(hwndConsole, OPT_ConsoleText);
\r
7389 SendMessage(hText, EM_HIDESELECTION, TRUE, FALSE);
\r
7390 /* Save current selection */
\r
7391 SendMessage(hText, EM_EXGETSEL, 0, (LPARAM)&savesel);
\r
7392 exlen = GetWindowTextLength(hText);
\r
7393 /* Find out whether current end of text is visible */
\r
7394 SendMessage(hText, EM_GETRECT, 0, (LPARAM) &rect);
\r
7395 SendMessage(hText, EM_POSFROMCHAR, (WPARAM) &pEnd, exlen);
\r
7396 /* Trim existing text if it's too long */
\r
7397 if (exlen + (q - buf) > CO_MAX) {
\r
7398 trim = (CO_TRIM > (q - buf)) ? CO_TRIM : (q - buf);
\r
7401 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
7402 SendMessage(hText, EM_REPLACESEL, 0, (LPARAM)"");
\r
7404 savesel.cpMin -= trim;
\r
7405 savesel.cpMax -= trim;
\r
7406 if (exlen < 0) exlen = 0;
\r
7407 if (savesel.cpMin < 0) savesel.cpMin = 0;
\r
7408 if (savesel.cpMax < savesel.cpMin) savesel.cpMax = savesel.cpMin;
\r
7410 /* Append the new text */
\r
7411 sel.cpMin = exlen;
\r
7412 sel.cpMax = exlen;
\r
7413 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
7414 SendMessage(hText, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&consoleCF);
\r
7415 SendMessage(hText, EM_REPLACESEL, 0, (LPARAM) buf);
\r
7416 if (forceVisible || exlen == 0 ||
\r
7417 (rect.left <= pEnd.x && pEnd.x < rect.right &&
\r
7418 rect.top <= pEnd.y && pEnd.y < rect.bottom)) {
\r
7419 /* Scroll to make new end of text visible if old end of text
\r
7420 was visible or new text is an echo of user typein */
\r
7421 sel.cpMin = 9999999;
\r
7422 sel.cpMax = 9999999;
\r
7423 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
7424 SendMessage(hText, EM_HIDESELECTION, FALSE, FALSE);
\r
7425 SendMessage(hText, EM_SCROLLCARET, 0, 0);
\r
7426 SendMessage(hText, EM_HIDESELECTION, TRUE, FALSE);
\r
7428 if (savesel.cpMax == exlen || forceVisible) {
\r
7429 /* Move insert point to new end of text if it was at the old
\r
7430 end of text or if the new text is an echo of user typein */
\r
7431 sel.cpMin = 9999999;
\r
7432 sel.cpMax = 9999999;
\r
7433 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
7435 /* Restore previous selection */
\r
7436 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&savesel);
\r
7438 SendMessage(hText, EM_HIDESELECTION, FALSE, FALSE);
\r
7445 DisplayHoldingsCount(HDC hdc, int x, int y, int rightAlign, int copyNumber)
\r
7449 COLORREF oldFg, oldBg;
\r
7453 if(copyNumber > 1)
\r
7454 snprintf(buf, sizeof(buf)/sizeof(buf[0]),"%d", copyNumber); else buf[0] = 0;
\r
7456 oldFg = SetTextColor(hdc, RGB(255, 255, 255)); /* white */
\r
7457 oldBg = SetBkColor(hdc, RGB(0, 0, 0)); /* black */
\r
7458 oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);
\r
7461 rect.right = x + squareSize;
\r
7463 rect.bottom = y + squareSize;
\r
7466 ExtTextOut(hdc, x + MESSAGE_LINE_LEFTMARGIN
\r
7467 + (rightAlign ? (squareSize*2)/3 : 0),
\r
7468 y, ETO_CLIPPED|ETO_OPAQUE,
\r
7469 &rect, str, strlen(str), NULL);
\r
7471 (void) SetTextColor(hdc, oldFg);
\r
7472 (void) SetBkColor(hdc, oldBg);
\r
7473 (void) SelectObject(hdc, oldFont);
\r
7477 DisplayAClock(HDC hdc, int timeRemaining, int highlight,
\r
7478 RECT *rect, char *color, char *flagFell)
\r
7482 COLORREF oldFg, oldBg;
\r
7485 if (twoBoards && partnerUp) return;
\r
7486 if (appData.clockMode) {
\r
7488 snprintf(buf, sizeof(buf)/sizeof(buf[0]), "%c %s %s", color[0], TimeString(timeRemaining), flagFell);
\r
7490 snprintf(buf, sizeof(buf)/sizeof(buf[0]), "%s:%c%s %s", color, (logoHeight>0 ? 0 : ' '), TimeString(timeRemaining), flagFell);
\r
7497 oldFg = SetTextColor(hdc, RGB(255, 255, 255)); /* white */
\r
7498 oldBg = SetBkColor(hdc, RGB(0, 0, 0)); /* black */
\r
7500 oldFg = SetTextColor(hdc, RGB(0, 0, 0)); /* black */
\r
7501 oldBg = SetBkColor(hdc, RGB(255, 255, 255)); /* white */
\r
7503 oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);
\r
7507 ExtTextOut(hdc, rect->left + MESSAGE_LINE_LEFTMARGIN,
\r
7508 rect->top, ETO_CLIPPED|ETO_OPAQUE,
\r
7509 rect, str, strlen(str), NULL);
\r
7510 if(logoHeight > 0 && appData.clockMode) {
\r
7512 str += strlen(color)+2;
\r
7513 r.top = rect->top + logoHeight/2;
\r
7514 r.left = rect->left;
\r
7515 r.right = rect->right;
\r
7516 r.bottom = rect->bottom;
\r
7517 ExtTextOut(hdc, rect->left + MESSAGE_LINE_LEFTMARGIN,
\r
7518 r.top, ETO_CLIPPED|ETO_OPAQUE,
\r
7519 &r, str, strlen(str), NULL);
\r
7521 (void) SetTextColor(hdc, oldFg);
\r
7522 (void) SetBkColor(hdc, oldBg);
\r
7523 (void) SelectObject(hdc, oldFont);
\r
7528 DoReadFile(HANDLE hFile, char *buf, int count, DWORD *outCount,
\r
7534 if( count <= 0 ) {
\r
7535 if (appData.debugMode) {
\r
7536 fprintf( debugFP, "DoReadFile: trying to read past end of buffer, overflow = %d\n", count );
\r
7539 return ERROR_INVALID_USER_BUFFER;
\r
7542 ResetEvent(ovl->hEvent);
\r
7543 ovl->Offset = ovl->OffsetHigh = 0;
\r
7544 ok = ReadFile(hFile, buf, count, outCount, ovl);
\r
7548 err = GetLastError();
\r
7549 if (err == ERROR_IO_PENDING) {
\r
7550 ok = GetOverlappedResult(hFile, ovl, outCount, TRUE);
\r
7554 err = GetLastError();
\r
7561 DoWriteFile(HANDLE hFile, char *buf, int count, DWORD *outCount,
\r
7566 ResetEvent(ovl->hEvent);
\r
7567 ovl->Offset = ovl->OffsetHigh = 0;
\r
7568 ok = WriteFile(hFile, buf, count, outCount, ovl);
\r
7572 err = GetLastError();
\r
7573 if (err == ERROR_IO_PENDING) {
\r
7574 ok = GetOverlappedResult(hFile, ovl, outCount, TRUE);
\r
7578 err = GetLastError();
\r
7584 /* [AS] If input is line by line and a line exceed the buffer size, force an error */
\r
7585 void CheckForInputBufferFull( InputSource * is )
\r
7587 if( is->lineByLine && (is->next - is->buf) >= INPUT_SOURCE_BUF_SIZE ) {
\r
7588 /* Look for end of line */
\r
7589 char * p = is->buf;
\r
7591 while( p < is->next && *p != '\n' ) {
\r
7595 if( p >= is->next ) {
\r
7596 if (appData.debugMode) {
\r
7597 fprintf( debugFP, "Input line exceeded buffer size (source id=%lu)\n", is->id );
\r
7600 is->error = ERROR_BROKEN_PIPE; /* [AS] Just any non-successful code! */
\r
7601 is->count = (DWORD) -1;
\r
7602 is->next = is->buf;
\r
7608 InputThread(LPVOID arg)
\r
7613 is = (InputSource *) arg;
\r
7614 ovl.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
\r
7615 ovl.Internal = ovl.InternalHigh = ovl.Offset = ovl.OffsetHigh = 0;
\r
7616 while (is->hThread != NULL) {
\r
7617 is->error = DoReadFile(is->hFile, is->next,
\r
7618 INPUT_SOURCE_BUF_SIZE - (is->next - is->buf),
\r
7619 &is->count, &ovl);
\r
7620 if (is->error == NO_ERROR) {
\r
7621 is->next += is->count;
\r
7623 if (is->error == ERROR_BROKEN_PIPE) {
\r
7624 /* Correct for MS brain damage. EOF reading a pipe is not an error. */
\r
7627 is->count = (DWORD) -1;
\r
7628 /* [AS] The (is->count <= 0) check below is not useful for unsigned values! */
\r
7633 CheckForInputBufferFull( is );
\r
7635 SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);
\r
7637 if( is->count == ((DWORD) -1) ) break; /* [AS] */
\r
7639 if (is->count <= 0) break; /* Quit on EOF or error */
\r
7642 CloseHandle(ovl.hEvent);
\r
7643 CloseHandle(is->hFile);
\r
7645 if (appData.debugMode) {
\r
7646 fprintf( debugFP, "Input thread terminated (id=%lu, error=%d, count=%ld)\n", is->id, is->error, is->count );
\r
7653 /* Windows 95 beta 2 won't let you do overlapped i/o on a console or pipe */
\r
7655 NonOvlInputThread(LPVOID arg)
\r
7662 is = (InputSource *) arg;
\r
7663 while (is->hThread != NULL) {
\r
7664 is->error = ReadFile(is->hFile, is->next,
\r
7665 INPUT_SOURCE_BUF_SIZE - (is->next - is->buf),
\r
7666 &is->count, NULL) ? NO_ERROR : GetLastError();
\r
7667 if (is->error == NO_ERROR) {
\r
7668 /* Change CRLF to LF */
\r
7669 if (is->next > is->buf) {
\r
7671 i = is->count + 1;
\r
7679 if (prev == '\r' && *p == '\n') {
\r
7691 if (is->error == ERROR_BROKEN_PIPE) {
\r
7692 /* Correct for MS brain damage. EOF reading a pipe is not an error. */
\r
7695 is->count = (DWORD) -1;
\r
7699 CheckForInputBufferFull( is );
\r
7701 SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);
\r
7703 if( is->count == ((DWORD) -1) ) break; /* [AS] */
\r
7705 if (is->count < 0) break; /* Quit on error */
\r
7707 CloseHandle(is->hFile);
\r
7712 SocketInputThread(LPVOID arg)
\r
7716 is = (InputSource *) arg;
\r
7717 while (is->hThread != NULL) {
\r
7718 is->count = recv(is->sock, is->buf, INPUT_SOURCE_BUF_SIZE, 0);
\r
7719 if ((int)is->count == SOCKET_ERROR) {
\r
7720 is->count = (DWORD) -1;
\r
7721 is->error = WSAGetLastError();
\r
7723 is->error = NO_ERROR;
\r
7724 is->next += is->count;
\r
7725 if (is->count == 0 && is->second == is) {
\r
7726 /* End of file on stderr; quit with no message */
\r
7730 SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);
\r
7732 if( is->count == ((DWORD) -1) ) break; /* [AS] */
\r
7734 if (is->count <= 0) break; /* Quit on EOF or error */
\r
7740 InputEvent(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
\r
7744 is = (InputSource *) lParam;
\r
7745 if (is->lineByLine) {
\r
7746 /* Feed in lines one by one */
\r
7747 char *p = is->buf;
\r
7749 while (q < is->next) {
\r
7750 if (*q++ == '\n') {
\r
7751 (is->func)(is, is->closure, p, q - p, NO_ERROR);
\r
7756 /* Move any partial line to the start of the buffer */
\r
7758 while (p < is->next) {
\r
7763 if (is->error != NO_ERROR || is->count == 0) {
\r
7764 /* Notify backend of the error. Note: If there was a partial
\r
7765 line at the end, it is not flushed through. */
\r
7766 (is->func)(is, is->closure, is->buf, is->count, is->error);
\r
7769 /* Feed in the whole chunk of input at once */
\r
7770 (is->func)(is, is->closure, is->buf, is->count, is->error);
\r
7771 is->next = is->buf;
\r
7775 /*---------------------------------------------------------------------------*\
\r
7777 * Menu enables. Used when setting various modes.
\r
7779 \*---------------------------------------------------------------------------*/
\r
7787 GreyRevert(Boolean grey)
\r
7788 { // [HGM] vari: for retracting variations in local mode
\r
7789 HMENU hmenu = GetMenu(hwndMain);
\r
7790 EnableMenuItem(hmenu, IDM_Revert, MF_BYCOMMAND|(grey ? MF_GRAYED : MF_ENABLED));
\r
7791 EnableMenuItem(hmenu, IDM_Annotate, MF_BYCOMMAND|(grey ? MF_GRAYED : MF_ENABLED));
\r
7795 SetMenuEnables(HMENU hmenu, Enables *enab)
\r
7797 while (enab->item > 0) {
\r
7798 (void) EnableMenuItem(hmenu, enab->item, enab->flags);
\r
7803 Enables gnuEnables[] = {
\r
7804 { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },
\r
7805 { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },
\r
7806 { IDM_IcsClient, MF_BYCOMMAND|MF_GRAYED },
\r
7807 { IDM_Accept, MF_BYCOMMAND|MF_GRAYED },
\r
7808 { IDM_Decline, MF_BYCOMMAND|MF_GRAYED },
\r
7809 { IDM_Rematch, MF_BYCOMMAND|MF_GRAYED },
\r
7810 { IDM_Adjourn, MF_BYCOMMAND|MF_GRAYED },
\r
7811 { IDM_StopExamining, MF_BYCOMMAND|MF_GRAYED },
\r
7812 { IDM_StopObserving, MF_BYCOMMAND|MF_GRAYED },
\r
7813 { IDM_Upload, MF_BYCOMMAND|MF_GRAYED },
\r
7814 { IDM_Revert, MF_BYCOMMAND|MF_GRAYED },
\r
7815 { IDM_Annotate, MF_BYCOMMAND|MF_GRAYED },
\r
7816 { IDM_NewChat, MF_BYCOMMAND|MF_GRAYED },
\r
7818 // Needed to switch from ncp to GNU mode on Engine Load
\r
7819 { ACTION_POS, MF_BYPOSITION|MF_ENABLED },
\r
7820 { IDM_MachineWhite, MF_BYCOMMAND|MF_ENABLED },
\r
7821 { IDM_MachineBlack, MF_BYCOMMAND|MF_ENABLED },
\r
7822 { IDM_TwoMachines, MF_BYCOMMAND|MF_ENABLED },
\r
7823 { IDM_Match, MF_BYCOMMAND|MF_ENABLED },
\r
7824 { IDM_AnalysisMode, MF_BYCOMMAND|MF_ENABLED },
\r
7825 { IDM_AnalyzeFile, MF_BYCOMMAND|MF_ENABLED },
\r
7826 { IDM_Engine1Options, MF_BYCOMMAND|MF_ENABLED },
\r
7827 { IDM_Engine2Options, MF_BYCOMMAND|MF_ENABLED },
\r
7828 { IDM_TimeControl, MF_BYCOMMAND|MF_ENABLED },
\r
7829 { IDM_RetractMove, MF_BYCOMMAND|MF_ENABLED },
\r
7830 { IDM_MoveNow, MF_BYCOMMAND|MF_ENABLED },
\r
7831 { IDM_Hint, MF_BYCOMMAND|MF_ENABLED },
\r
7832 { IDM_Book, MF_BYCOMMAND|MF_ENABLED },
\r
7836 Enables icsEnables[] = {
\r
7837 { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },
\r
7838 { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },
\r
7839 { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },
\r
7840 { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },
\r
7841 { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },
\r
7842 { IDM_Match, MF_BYCOMMAND|MF_GRAYED },
\r
7843 { IDM_MachineBoth, MF_BYCOMMAND|MF_GRAYED },
\r
7844 { IDM_AnalysisMode, MF_BYCOMMAND|MF_ENABLED },
\r
7845 { IDM_AnalyzeFile, MF_BYCOMMAND|MF_GRAYED },
\r
7846 { IDM_TimeControl, MF_BYCOMMAND|MF_GRAYED },
\r
7847 { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },
\r
7848 { IDM_Hint, MF_BYCOMMAND|MF_GRAYED },
\r
7849 { IDM_Book, MF_BYCOMMAND|MF_GRAYED },
\r
7850 { IDM_LoadProg1, MF_BYCOMMAND|MF_GRAYED },
\r
7851 { IDM_LoadProg2, MF_BYCOMMAND|MF_GRAYED },
\r
7852 { IDM_IcsOptions, MF_BYCOMMAND|MF_ENABLED },
\r
7853 { IDM_Engine1Options, MF_BYCOMMAND|MF_GRAYED },
\r
7854 { IDM_Engine2Options, MF_BYCOMMAND|MF_GRAYED },
\r
7855 { IDM_Annotate, MF_BYCOMMAND|MF_GRAYED },
\r
7856 { IDM_Tourney, MF_BYCOMMAND|MF_GRAYED },
\r
7861 Enables zippyEnables[] = {
\r
7862 { IDM_MoveNow, MF_BYCOMMAND|MF_ENABLED },
\r
7863 { IDM_Hint, MF_BYCOMMAND|MF_ENABLED },
\r
7864 { IDM_Book, MF_BYCOMMAND|MF_ENABLED },
\r
7865 { IDM_Engine1Options, MF_BYCOMMAND|MF_ENABLED },
\r
7870 Enables ncpEnables[] = {
\r
7871 { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },
\r
7872 { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },
\r
7873 { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },
\r
7874 { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },
\r
7875 { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },
\r
7876 { IDM_Match, MF_BYCOMMAND|MF_GRAYED },
\r
7877 { IDM_AnalysisMode, MF_BYCOMMAND|MF_GRAYED },
\r
7878 { IDM_AnalyzeFile, MF_BYCOMMAND|MF_GRAYED },
\r
7879 { IDM_IcsClient, MF_BYCOMMAND|MF_GRAYED },
\r
7880 { ACTION_POS, MF_BYPOSITION|MF_GRAYED },
\r
7881 { IDM_Revert, MF_BYCOMMAND|MF_GRAYED },
\r
7882 { IDM_Annotate, MF_BYCOMMAND|MF_GRAYED },
\r
7883 { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },
\r
7884 { IDM_RetractMove, MF_BYCOMMAND|MF_GRAYED },
\r
7885 { IDM_TimeControl, MF_BYCOMMAND|MF_GRAYED },
\r
7886 { IDM_Hint, MF_BYCOMMAND|MF_GRAYED },
\r
7887 { IDM_Book, MF_BYCOMMAND|MF_GRAYED },
\r
7888 { IDM_MachineBoth, MF_BYCOMMAND|MF_GRAYED },
\r
7889 { IDM_NewChat, MF_BYCOMMAND|MF_GRAYED },
\r
7890 { IDM_Engine1Options, MF_BYCOMMAND|MF_GRAYED },
\r
7891 { IDM_Engine2Options, MF_BYCOMMAND|MF_GRAYED },
\r
7892 { IDM_Sounds, MF_BYCOMMAND|MF_GRAYED },
\r
7896 Enables trainingOnEnables[] = {
\r
7897 { IDM_EditComment, MF_BYCOMMAND|MF_GRAYED },
\r
7898 { IDM_Comment, MF_BYCOMMAND|MF_GRAYED },
\r
7899 { IDM_Pause, MF_BYCOMMAND|MF_GRAYED },
\r
7900 { IDM_Forward, MF_BYCOMMAND|MF_GRAYED },
\r
7901 { IDM_Backward, MF_BYCOMMAND|MF_GRAYED },
\r
7902 { IDM_ToEnd, MF_BYCOMMAND|MF_GRAYED },
\r
7903 { IDM_ToStart, MF_BYCOMMAND|MF_GRAYED },
\r
7904 { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },
\r
7905 { IDM_TruncateGame, MF_BYCOMMAND|MF_GRAYED },
\r
7909 Enables trainingOffEnables[] = {
\r
7910 { IDM_EditComment, MF_BYCOMMAND|MF_ENABLED },
\r
7911 { IDM_Comment, MF_BYCOMMAND|MF_ENABLED },
\r
7912 { IDM_Pause, MF_BYCOMMAND|MF_ENABLED },
\r
7913 { IDM_Forward, MF_BYCOMMAND|MF_ENABLED },
\r
7914 { IDM_Backward, MF_BYCOMMAND|MF_ENABLED },
\r
7915 { IDM_ToEnd, MF_BYCOMMAND|MF_ENABLED },
\r
7916 { IDM_ToStart, MF_BYCOMMAND|MF_ENABLED },
\r
7917 { IDM_MoveNow, MF_BYCOMMAND|MF_ENABLED },
\r
7918 { IDM_TruncateGame, MF_BYCOMMAND|MF_ENABLED },
\r
7922 /* These modify either ncpEnables or gnuEnables */
\r
7923 Enables cmailEnables[] = {
\r
7924 { IDM_MailMove, MF_BYCOMMAND|MF_ENABLED },
\r
7925 { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_ENABLED },
\r
7926 { ACTION_POS, MF_BYPOSITION|MF_ENABLED },
\r
7927 { IDM_CallFlag, MF_BYCOMMAND|MF_GRAYED },
\r
7928 { IDM_Draw, MF_BYCOMMAND|MF_ENABLED },
\r
7929 { IDM_Adjourn, MF_BYCOMMAND|MF_GRAYED },
\r
7930 { IDM_Abort, MF_BYCOMMAND|MF_GRAYED },
\r
7934 Enables machineThinkingEnables[] = {
\r
7935 { IDM_LoadGame, MF_BYCOMMAND|MF_GRAYED },
\r
7936 { IDM_LoadNextGame, MF_BYCOMMAND|MF_GRAYED },
\r
7937 { IDM_LoadPrevGame, MF_BYCOMMAND|MF_GRAYED },
\r
7938 { IDM_ReloadGame, MF_BYCOMMAND|MF_GRAYED },
\r
7939 { IDM_PasteGame, MF_BYCOMMAND|MF_GRAYED },
\r
7940 { IDM_LoadPosition, MF_BYCOMMAND|MF_GRAYED },
\r
7941 { IDM_LoadNextPosition, MF_BYCOMMAND|MF_GRAYED },
\r
7942 { IDM_LoadPrevPosition, MF_BYCOMMAND|MF_GRAYED },
\r
7943 { IDM_ReloadPosition, MF_BYCOMMAND|MF_GRAYED },
\r
7944 { IDM_PastePosition, MF_BYCOMMAND|MF_GRAYED },
\r
7945 { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },
\r
7946 { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },
\r
7947 { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },
\r
7948 // { IDM_Match, MF_BYCOMMAND|MF_GRAYED },
\r
7949 { IDM_TypeInMove, MF_BYCOMMAND|MF_GRAYED },
\r
7950 { IDM_RetractMove, MF_BYCOMMAND|MF_GRAYED },
\r
7954 Enables userThinkingEnables[] = {
\r
7955 { IDM_LoadGame, MF_BYCOMMAND|MF_ENABLED },
\r
7956 { IDM_LoadNextGame, MF_BYCOMMAND|MF_ENABLED },
\r
7957 { IDM_LoadPrevGame, MF_BYCOMMAND|MF_ENABLED },
\r
7958 { IDM_ReloadGame, MF_BYCOMMAND|MF_ENABLED },
\r
7959 { IDM_PasteGame, MF_BYCOMMAND|MF_ENABLED },
\r
7960 { IDM_LoadPosition, MF_BYCOMMAND|MF_ENABLED },
\r
7961 { IDM_LoadNextPosition, MF_BYCOMMAND|MF_ENABLED },
\r
7962 { IDM_LoadPrevPosition, MF_BYCOMMAND|MF_ENABLED },
\r
7963 { IDM_ReloadPosition, MF_BYCOMMAND|MF_ENABLED },
\r
7964 { IDM_PastePosition, MF_BYCOMMAND|MF_ENABLED },
\r
7965 { IDM_MachineWhite, MF_BYCOMMAND|MF_ENABLED },
\r
7966 { IDM_MachineBlack, MF_BYCOMMAND|MF_ENABLED },
\r
7967 { IDM_TwoMachines, MF_BYCOMMAND|MF_ENABLED },
\r
7968 // { IDM_Match, MF_BYCOMMAND|MF_ENABLED },
\r
7969 { IDM_TypeInMove, MF_BYCOMMAND|MF_ENABLED },
\r
7970 { IDM_RetractMove, MF_BYCOMMAND|MF_ENABLED },
\r
7974 /*---------------------------------------------------------------------------*\
\r
7976 * Front-end interface functions exported by XBoard.
\r
7977 * Functions appear in same order as prototypes in frontend.h.
\r
7979 \*---------------------------------------------------------------------------*/
\r
7981 CheckMark(UINT item, int state)
\r
7983 if(item) CheckMenuItem(GetMenu(hwndMain), item, MF_BYCOMMAND|state);
\r
7989 static UINT prevChecked = 0;
\r
7990 static int prevPausing = 0;
\r
7993 if (pausing != prevPausing) {
\r
7994 prevPausing = pausing;
\r
7995 (void) CheckMenuItem(GetMenu(hwndMain), IDM_Pause,
\r
7996 MF_BYCOMMAND|(pausing ? MF_CHECKED : MF_UNCHECKED));
\r
7997 if (hwndPause) SetWindowText(hwndPause, pausing ? "C" : "P");
\r
8000 switch (gameMode) {
\r
8001 case BeginningOfGame:
\r
8002 if (appData.icsActive)
\r
8003 nowChecked = IDM_IcsClient;
\r
8004 else if (appData.noChessProgram)
\r
8005 nowChecked = IDM_EditGame;
\r
8007 nowChecked = IDM_MachineBlack;
\r
8009 case MachinePlaysBlack:
\r
8010 nowChecked = IDM_MachineBlack;
\r
8012 case MachinePlaysWhite:
\r
8013 nowChecked = IDM_MachineWhite;
\r
8015 case TwoMachinesPlay:
\r
8016 nowChecked = IDM_TwoMachines;
\r
8019 nowChecked = IDM_AnalysisMode;
\r
8022 nowChecked = IDM_AnalyzeFile;
\r
8025 nowChecked = IDM_EditGame;
\r
8027 case PlayFromGameFile:
\r
8028 nowChecked = IDM_LoadGame;
\r
8030 case EditPosition:
\r
8031 nowChecked = IDM_EditPosition;
\r
8034 nowChecked = IDM_Training;
\r
8036 case IcsPlayingWhite:
\r
8037 case IcsPlayingBlack:
\r
8038 case IcsObserving:
\r
8040 nowChecked = IDM_IcsClient;
\r
8047 CheckMark(prevChecked, MF_UNCHECKED);
\r
8048 CheckMark(nowChecked, MF_CHECKED);
\r
8049 CheckMark(IDM_Match, matchMode && matchGame < appData.matchGames ? MF_CHECKED : MF_UNCHECKED);
\r
8051 if (nowChecked == IDM_LoadGame || nowChecked == IDM_Training) {
\r
8052 (void) EnableMenuItem(GetMenu(hwndMain), IDM_Training,
\r
8053 MF_BYCOMMAND|MF_ENABLED);
\r
8055 (void) EnableMenuItem(GetMenu(hwndMain),
\r
8056 IDM_Training, MF_BYCOMMAND|MF_GRAYED);
\r
8059 prevChecked = nowChecked;
\r
8061 /* [DM] icsEngineAnalyze - Do a sceure check too */
\r
8062 if (appData.icsActive) {
\r
8063 if (appData.icsEngineAnalyze) {
\r
8064 CheckMark(IDM_AnalysisMode, MF_CHECKED);
\r
8066 CheckMark(IDM_AnalysisMode, MF_UNCHECKED);
\r
8069 DisplayLogos(); // [HGM] logos: mode change could have altered logos
\r
8075 HMENU hmenu = GetMenu(hwndMain);
\r
8076 SetMenuEnables(hmenu, icsEnables);
\r
8077 EnableMenuItem(GetSubMenu(hmenu, OPTIONS_POS), IDM_IcsOptions,
\r
8078 MF_BYCOMMAND|MF_ENABLED);
\r
8080 if (appData.zippyPlay) {
\r
8081 SetMenuEnables(hmenu, zippyEnables);
\r
8082 if (!appData.noChessProgram) /* [DM] icsEngineAnalyze */
\r
8083 (void) EnableMenuItem(GetMenu(hwndMain), IDM_AnalysisMode,
\r
8084 MF_BYCOMMAND|MF_ENABLED);
\r
8092 SetMenuEnables(GetMenu(hwndMain), gnuEnables);
\r
8098 HMENU hmenu = GetMenu(hwndMain);
\r
8099 SetMenuEnables(hmenu, ncpEnables);
\r
8100 DrawMenuBar(hwndMain);
\r
8106 SetMenuEnables(GetMenu(hwndMain), cmailEnables);
\r
8110 SetTrainingModeOn()
\r
8113 SetMenuEnables(GetMenu(hwndMain), trainingOnEnables);
\r
8114 for (i = 0; i < N_BUTTONS; i++) {
\r
8115 if (buttonDesc[i].hwnd != NULL)
\r
8116 EnableWindow(buttonDesc[i].hwnd, FALSE);
\r
8121 VOID SetTrainingModeOff()
\r
8124 SetMenuEnables(GetMenu(hwndMain), trainingOffEnables);
\r
8125 for (i = 0; i < N_BUTTONS; i++) {
\r
8126 if (buttonDesc[i].hwnd != NULL)
\r
8127 EnableWindow(buttonDesc[i].hwnd, TRUE);
\r
8133 SetUserThinkingEnables()
\r
8135 SetMenuEnables(GetMenu(hwndMain), userThinkingEnables);
\r
8139 SetMachineThinkingEnables()
\r
8141 HMENU hMenu = GetMenu(hwndMain);
\r
8142 int flags = MF_BYCOMMAND|MF_ENABLED;
\r
8144 SetMenuEnables(hMenu, machineThinkingEnables);
\r
8146 if (gameMode == MachinePlaysBlack) {
\r
8147 (void)EnableMenuItem(hMenu, IDM_MachineBlack, flags);
\r
8148 } else if (gameMode == MachinePlaysWhite) {
\r
8149 (void)EnableMenuItem(hMenu, IDM_MachineWhite, flags);
\r
8150 } else if (gameMode == TwoMachinesPlay) {
\r
8151 (void)EnableMenuItem(hMenu, matchMode ? IDM_Match : IDM_TwoMachines, flags); // [HGM] match
\r
8157 DisplayTitle(char *str)
\r
8159 char title[MSG_SIZ], *host;
\r
8160 if (str[0] != NULLCHAR) {
\r
8161 safeStrCpy(title, str, sizeof(title)/sizeof(title[0]) );
\r
8162 } else if (appData.icsActive) {
\r
8163 if (appData.icsCommPort[0] != NULLCHAR)
\r
8166 host = appData.icsHost;
\r
8167 snprintf(title, MSG_SIZ, "%s: %s", szTitle, host);
\r
8168 } else if (appData.noChessProgram) {
\r
8169 safeStrCpy(title, szTitle, sizeof(title)/sizeof(title[0]) );
\r
8171 safeStrCpy(title, szTitle, sizeof(title)/sizeof(title[0]) );
\r
8172 strcat(title, ": ");
\r
8173 strcat(title, first.tidy);
\r
8175 SetWindowText(hwndMain, title);
\r
8180 DisplayMessage(char *str1, char *str2)
\r
8184 int remain = MESSAGE_TEXT_MAX - 1;
\r
8187 moveErrorMessageUp = FALSE; /* turned on later by caller if needed */
\r
8188 messageText[0] = NULLCHAR;
\r
8190 len = strlen(str1);
\r
8191 if (len > remain) len = remain;
\r
8192 strncpy(messageText, str1, len);
\r
8193 messageText[len] = NULLCHAR;
\r
8196 if (*str2 && remain >= 2) {
\r
8198 strcat(messageText, " ");
\r
8201 len = strlen(str2);
\r
8202 if (len > remain) len = remain;
\r
8203 strncat(messageText, str2, len);
\r
8205 messageText[MESSAGE_TEXT_MAX - 1] = NULLCHAR;
\r
8206 safeStrCpy(lastMsg, messageText, MSG_SIZ);
\r
8208 if (hwndMain == NULL || IsIconic(hwndMain)) return;
\r
8212 hdc = GetDC(hwndMain);
\r
8213 oldFont = SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);
\r
8214 ExtTextOut(hdc, messageRect.left, messageRect.top, ETO_CLIPPED|ETO_OPAQUE,
\r
8215 &messageRect, messageText, strlen(messageText), NULL);
\r
8216 (void) SelectObject(hdc, oldFont);
\r
8217 (void) ReleaseDC(hwndMain, hdc);
\r
8221 DisplayError(char *str, int error)
\r
8223 char buf[MSG_SIZ*2], buf2[MSG_SIZ];
\r
8227 safeStrCpy(buf, str, sizeof(buf)/sizeof(buf[0]) );
\r
8229 len = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,
\r
8230 NULL, error, LANG_NEUTRAL,
\r
8231 (LPSTR) buf2, MSG_SIZ, NULL);
\r
8233 snprintf(buf, 2*MSG_SIZ, "%s:\n%s", str, buf2);
\r
8235 ErrorMap *em = errmap;
\r
8236 while (em->err != 0 && em->err != error) em++;
\r
8237 if (em->err != 0) {
\r
8238 snprintf(buf, 2*MSG_SIZ, "%s:\n%s", str, em->msg);
\r
8240 snprintf(buf, 2*MSG_SIZ, "%s:\nError code %d", str, error);
\r
8245 ErrorPopUp(_("Error"), buf);
\r
8250 DisplayMoveError(char *str)
\r
8252 fromX = fromY = -1;
\r
8253 ClearHighlights();
\r
8254 DrawPosition(FALSE, NULL);
\r
8255 if (appData.popupMoveErrors) {
\r
8256 ErrorPopUp(_("Error"), str);
\r
8258 DisplayMessage(str, "");
\r
8259 moveErrorMessageUp = TRUE;
\r
8264 DisplayFatalError(char *str, int error, int exitStatus)
\r
8266 char buf[2*MSG_SIZ], buf2[MSG_SIZ];
\r
8268 char *label = exitStatus ? _("Fatal Error") : _("Exiting");
\r
8271 len = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,
\r
8272 NULL, error, LANG_NEUTRAL,
\r
8273 (LPSTR) buf2, MSG_SIZ, NULL);
\r
8275 snprintf(buf, 2*MSG_SIZ, "%s:\n%s", str, buf2);
\r
8277 ErrorMap *em = errmap;
\r
8278 while (em->err != 0 && em->err != error) em++;
\r
8279 if (em->err != 0) {
\r
8280 snprintf(buf, 2*MSG_SIZ, "%s:\n%s", str, em->msg);
\r
8282 snprintf(buf, 2*MSG_SIZ, "%s:\nError code %d", str, error);
\r
8287 if (appData.debugMode) {
\r
8288 fprintf(debugFP, "%s: %s\n", label, str);
\r
8290 if (appData.popupExitMessage) {
\r
8291 (void) MessageBox(hwndMain, str, label, MB_OK|
\r
8292 (exitStatus ? MB_ICONSTOP : MB_ICONINFORMATION));
\r
8294 ExitEvent(exitStatus);
\r
8299 DisplayInformation(char *str)
\r
8301 (void) MessageBox(hwndMain, str, _("Information"), MB_OK|MB_ICONINFORMATION);
\r
8306 DisplayNote(char *str)
\r
8308 ErrorPopUp(_("Note"), str);
\r
8313 char *title, *question, *replyPrefix;
\r
8318 QuestionDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
8320 static QuestionParams *qp;
\r
8321 char reply[MSG_SIZ];
\r
8324 switch (message) {
\r
8325 case WM_INITDIALOG:
\r
8326 qp = (QuestionParams *) lParam;
\r
8327 CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));
\r
8328 Translate(hDlg, DLG_Question);
\r
8329 SetWindowText(hDlg, qp->title);
\r
8330 SetDlgItemText(hDlg, OPT_QuestionText, qp->question);
\r
8331 SetFocus(GetDlgItem(hDlg, OPT_QuestionInput));
\r
8335 switch (LOWORD(wParam)) {
\r
8337 safeStrCpy(reply, qp->replyPrefix, sizeof(reply)/sizeof(reply[0]) );
\r
8338 if (*reply) strcat(reply, " ");
\r
8339 len = strlen(reply);
\r
8340 GetDlgItemText(hDlg, OPT_QuestionInput, reply + len, sizeof(reply) - len);
\r
8341 strcat(reply, "\n");
\r
8342 OutputToProcess(qp->pr, reply, strlen(reply), &err);
\r
8343 EndDialog(hDlg, TRUE);
\r
8344 if (err) DisplayFatalError(_("Error writing to chess program"), err, 1);
\r
8347 EndDialog(hDlg, FALSE);
\r
8358 AskQuestion(char* title, char *question, char *replyPrefix, ProcRef pr)
\r
8360 QuestionParams qp;
\r
8364 qp.question = question;
\r
8365 qp.replyPrefix = replyPrefix;
\r
8367 lpProc = MakeProcInstance((FARPROC)QuestionDialog, hInst);
\r
8368 DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_Question),
\r
8369 hwndMain, (DLGPROC)lpProc, (LPARAM)&qp);
\r
8370 FreeProcInstance(lpProc);
\r
8373 /* [AS] Pick FRC position */
\r
8374 LRESULT CALLBACK NewGameFRC_Proc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
8376 static int * lpIndexFRC;
\r
8382 case WM_INITDIALOG:
\r
8383 lpIndexFRC = (int *) lParam;
\r
8385 CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));
\r
8386 Translate(hDlg, DLG_NewGameFRC);
\r
8388 SendDlgItemMessage( hDlg, IDC_NFG_Edit, EM_SETLIMITTEXT, sizeof(buf)-1, 0 );
\r
8389 SetDlgItemInt( hDlg, IDC_NFG_Edit, *lpIndexFRC, TRUE );
\r
8390 SendDlgItemMessage( hDlg, IDC_NFG_Edit, EM_SETSEL, 0, -1 );
\r
8391 SetFocus(GetDlgItem(hDlg, IDC_NFG_Edit));
\r
8396 switch( LOWORD(wParam) ) {
\r
8398 *lpIndexFRC = GetDlgItemInt(hDlg, IDC_NFG_Edit, &index_is_ok, TRUE );
\r
8399 EndDialog( hDlg, 0 );
\r
8400 shuffleOpenings = TRUE; /* [HGM] shuffle: switch shuffling on for as long as we stay in current variant */
\r
8403 EndDialog( hDlg, 1 );
\r
8405 case IDC_NFG_Edit:
\r
8406 if( HIWORD(wParam) == EN_CHANGE ) {
\r
8407 GetDlgItemInt(hDlg, IDC_NFG_Edit, &index_is_ok, TRUE );
\r
8409 EnableWindow( GetDlgItem(hDlg, IDOK), index_is_ok );
\r
8412 case IDC_NFG_Random:
\r
8413 snprintf( buf, sizeof(buf)/sizeof(buf[0]), "%d", myrandom() ); /* [HGM] shuffle: no longer limit to 960 */
\r
8414 SetDlgItemText(hDlg, IDC_NFG_Edit, buf );
\r
8427 int index = appData.defaultFrcPosition;
\r
8428 FARPROC lpProc = MakeProcInstance( (FARPROC) NewGameFRC_Proc, hInst );
\r
8430 result = DialogBoxParam( hInst, MAKEINTRESOURCE(DLG_NewGameFRC), hwndMain, (DLGPROC)lpProc, (LPARAM)&index );
\r
8432 if( result == 0 ) {
\r
8433 appData.defaultFrcPosition = index;
\r
8439 /* [AS] Game list options. Refactored by HGM */
\r
8441 HWND gameListOptionsDialog;
\r
8443 // low-level front-end: clear text edit / list widget
\r
8447 SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_RESETCONTENT, 0, 0 );
\r
8450 // low-level front-end: clear text edit / list widget
\r
8452 GLT_DeSelectList()
\r
8454 SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_SETCURSEL, 0, 0 );
\r
8457 // low-level front-end: append line to text edit / list widget
\r
8459 GLT_AddToList( char *name )
\r
8462 SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_ADDSTRING, 0, (LPARAM) name );
\r
8466 // low-level front-end: get line from text edit / list widget
\r
8468 GLT_GetFromList( int index, char *name )
\r
8471 if( SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_GETTEXT, index, (LPARAM) name ) != LB_ERR )
\r
8477 void GLT_MoveSelection( HWND hDlg, int delta )
\r
8479 int idx1 = (int) SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETCURSEL, 0, 0 );
\r
8480 int idx2 = idx1 + delta;
\r
8481 int count = (int) SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETCOUNT, 0, 0 );
\r
8483 if( idx1 >=0 && idx1 < count && idx2 >= 0 && idx2 < count ) {
\r
8486 SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETTEXT, idx1, (LPARAM) buf );
\r
8487 SendDlgItemMessage( hDlg, IDC_GameListTags, LB_DELETESTRING, idx1, 0 );
\r
8488 SendDlgItemMessage( hDlg, IDC_GameListTags, LB_INSERTSTRING, idx2, (LPARAM) buf );
\r
8489 SendDlgItemMessage( hDlg, IDC_GameListTags, LB_SETCURSEL, idx2, 0 );
\r
8493 LRESULT CALLBACK GameListOptions_Proc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
8497 case WM_INITDIALOG:
\r
8498 gameListOptionsDialog = hDlg; // [HGM] pass through global to keep out off back-end
\r
8500 CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));
\r
8501 Translate(hDlg, DLG_GameListOptions);
\r
8503 /* Initialize list */
\r
8504 GLT_TagsToList( lpUserGLT );
\r
8506 SetFocus( GetDlgItem(hDlg, IDC_GameListTags) );
\r
8511 switch( LOWORD(wParam) ) {
\r
8514 EndDialog( hDlg, 0 );
\r
8517 EndDialog( hDlg, 1 );
\r
8520 case IDC_GLT_Default:
\r
8521 GLT_TagsToList( GLT_DEFAULT_TAGS );
\r
8524 case IDC_GLT_Restore:
\r
8525 GLT_TagsToList( appData.gameListTags );
\r
8529 GLT_MoveSelection( hDlg, -1 );
\r
8532 case IDC_GLT_Down:
\r
8533 GLT_MoveSelection( hDlg, +1 );
\r
8543 int GameListOptions()
\r
8546 FARPROC lpProc = MakeProcInstance( (FARPROC) GameListOptions_Proc, hInst );
\r
8548 safeStrCpy( lpUserGLT, appData.gameListTags ,LPUSERGLT_SIZE );
\r
8550 result = DialogBoxParam( hInst, MAKEINTRESOURCE(DLG_GameListOptions), hwndMain, (DLGPROC)lpProc, (LPARAM)lpUserGLT );
\r
8552 if( result == 0 ) {
\r
8553 /* [AS] Memory leak here! */
\r
8554 appData.gameListTags = strdup( lpUserGLT );
\r
8561 DisplayIcsInteractionTitle(char *str)
\r
8563 char consoleTitle[MSG_SIZ];
\r
8565 snprintf(consoleTitle, MSG_SIZ, "%s: %s", szConsoleTitle, str);
\r
8566 SetWindowText(hwndConsole, consoleTitle);
\r
8568 if(appData.chatBoxes) { // [HGM] chat: open chat boxes
\r
8569 char buf[MSG_SIZ], *p = buf, *q;
\r
8570 safeStrCpy(buf, appData.chatBoxes, sizeof(buf)/sizeof(buf[0]) );
\r
8572 q = strchr(p, ';');
\r
8574 if(*p) ChatPopUp(p);
\r
8578 SetActiveWindow(hwndMain);
\r
8582 DrawPosition(int fullRedraw, Board board)
\r
8584 HDCDrawPosition(NULL, (BOOLEAN) fullRedraw, board);
\r
8587 void NotifyFrontendLogin()
\r
8590 UpdateICSWidth(GetDlgItem(hwndConsole, OPT_ConsoleText));
\r
8596 fromX = fromY = -1;
\r
8597 if (dragInfo.pos.x != -1 || dragInfo.pos.y != -1) {
\r
8598 dragInfo.pos.x = dragInfo.pos.y = -1;
\r
8599 dragInfo.pos.x = dragInfo.pos.y = -1;
\r
8600 dragInfo.lastpos = dragInfo.pos;
\r
8601 dragInfo.start.x = dragInfo.start.y = -1;
\r
8602 dragInfo.from = dragInfo.start;
\r
8604 DrawPosition(TRUE, NULL);
\r
8611 CommentPopUp(char *title, char *str)
\r
8613 HWND hwnd = GetActiveWindow();
\r
8614 EitherCommentPopUp(currentMove, title, str, FALSE); // [HGM] vari: fake move index, rather than 0
\r
8616 SetActiveWindow(hwnd);
\r
8620 CommentPopDown(void)
\r
8622 CheckMenuItem(GetMenu(hwndMain), IDM_Comment, MF_UNCHECKED);
\r
8623 if (commentDialog) {
\r
8624 ShowWindow(commentDialog, SW_HIDE);
\r
8626 commentUp = FALSE;
\r
8630 EditCommentPopUp(int index, char *title, char *str)
\r
8632 EitherCommentPopUp(index, title, str, TRUE);
\r
8639 MyPlaySound(&sounds[(int)SoundMove]);
\r
8642 VOID PlayIcsWinSound()
\r
8644 MyPlaySound(&sounds[(int)SoundIcsWin]);
\r
8647 VOID PlayIcsLossSound()
\r
8649 MyPlaySound(&sounds[(int)SoundIcsLoss]);
\r
8652 VOID PlayIcsDrawSound()
\r
8654 MyPlaySound(&sounds[(int)SoundIcsDraw]);
\r
8657 VOID PlayIcsUnfinishedSound()
\r
8659 MyPlaySound(&sounds[(int)SoundIcsUnfinished]);
\r
8665 MyPlaySound(&sounds[(int)SoundAlarm]);
\r
8671 MyPlaySound(&textAttribs[ColorTell].sound);
\r
8679 consoleEcho = TRUE;
\r
8680 hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
8681 SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&consoleCF);
\r
8682 SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);
\r
8691 consoleEcho = FALSE;
\r
8692 hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
8693 /* This works OK: set text and background both to the same color */
\r
8695 cf.crTextColor = COLOR_ECHOOFF;
\r
8696 SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf);
\r
8697 SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, cf.crTextColor);
\r
8700 /* No Raw()...? */
\r
8702 void Colorize(ColorClass cc, int continuation)
\r
8704 currentColorClass = cc;
\r
8705 consoleCF.dwMask = CFM_COLOR|CFM_BOLD|CFM_ITALIC|CFM_UNDERLINE|CFM_STRIKEOUT;
\r
8706 consoleCF.crTextColor = textAttribs[cc].color;
\r
8707 consoleCF.dwEffects = textAttribs[cc].effects;
\r
8708 if (!continuation) MyPlaySound(&textAttribs[cc].sound);
\r
8714 static char buf[MSG_SIZ];
\r
8715 DWORD bufsiz = MSG_SIZ;
\r
8717 if(appData.userName != NULL && appData.userName[0] != 0) {
\r
8718 return appData.userName; /* [HGM] username: prefer name selected by user over his system login */
\r
8720 if (!GetUserName(buf, &bufsiz)) {
\r
8721 /*DisplayError("Error getting user name", GetLastError());*/
\r
8722 safeStrCpy(buf, _("User"), sizeof(buf)/sizeof(buf[0]) );
\r
8730 static char buf[MSG_SIZ];
\r
8731 DWORD bufsiz = MSG_SIZ;
\r
8733 if (!GetComputerName(buf, &bufsiz)) {
\r
8734 /*DisplayError("Error getting host name", GetLastError());*/
\r
8735 safeStrCpy(buf, _("Unknown"), sizeof(buf)/sizeof(buf[0]) );
\r
8742 ClockTimerRunning()
\r
8744 return clockTimerEvent != 0;
\r
8750 if (clockTimerEvent == 0) return FALSE;
\r
8751 KillTimer(hwndMain, clockTimerEvent);
\r
8752 clockTimerEvent = 0;
\r
8757 StartClockTimer(long millisec)
\r
8759 clockTimerEvent = SetTimer(hwndMain, (UINT) CLOCK_TIMER_ID,
\r
8760 (UINT) millisec, NULL);
\r
8764 DisplayWhiteClock(long timeRemaining, int highlight)
\r
8767 char *flag = whiteFlag && gameMode == TwoMachinesPlay ? "(!)" : "";
\r
8769 if(appData.noGUI) return;
\r
8770 hdc = GetDC(hwndMain);
\r
8771 if (!IsIconic(hwndMain)) {
\r
8772 DisplayAClock(hdc, timeRemaining, highlight,
\r
8773 flipClock ? &blackRect : &whiteRect, _("White"), flag);
\r
8775 if (highlight && iconCurrent == iconBlack) {
\r
8776 iconCurrent = iconWhite;
\r
8777 PostMessage(hwndMain, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);
\r
8778 if (IsIconic(hwndMain)) {
\r
8779 DrawIcon(hdc, 2, 2, iconCurrent);
\r
8782 (void) ReleaseDC(hwndMain, hdc);
\r
8784 PostMessage(hwndConsole, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);
\r
8788 DisplayBlackClock(long timeRemaining, int highlight)
\r
8791 char *flag = blackFlag && gameMode == TwoMachinesPlay ? "(!)" : "";
\r
8793 if(appData.noGUI) return;
\r
8794 hdc = GetDC(hwndMain);
\r
8795 if (!IsIconic(hwndMain)) {
\r
8796 DisplayAClock(hdc, timeRemaining, highlight,
\r
8797 flipClock ? &whiteRect : &blackRect, _("Black"), flag);
\r
8799 if (highlight && iconCurrent == iconWhite) {
\r
8800 iconCurrent = iconBlack;
\r
8801 PostMessage(hwndMain, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);
\r
8802 if (IsIconic(hwndMain)) {
\r
8803 DrawIcon(hdc, 2, 2, iconCurrent);
\r
8806 (void) ReleaseDC(hwndMain, hdc);
\r
8808 PostMessage(hwndConsole, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);
\r
8813 LoadGameTimerRunning()
\r
8815 return loadGameTimerEvent != 0;
\r
8819 StopLoadGameTimer()
\r
8821 if (loadGameTimerEvent == 0) return FALSE;
\r
8822 KillTimer(hwndMain, loadGameTimerEvent);
\r
8823 loadGameTimerEvent = 0;
\r
8828 StartLoadGameTimer(long millisec)
\r
8830 loadGameTimerEvent = SetTimer(hwndMain, (UINT) LOAD_GAME_TIMER_ID,
\r
8831 (UINT) millisec, NULL);
\r
8839 char fileTitle[MSG_SIZ];
\r
8841 defName = DefaultFileName(appData.oldSaveStyle ? "gam" : "pgn");
\r
8842 f = OpenFileDialog(hwndMain, "a", defName,
\r
8843 appData.oldSaveStyle ? "gam" : "pgn",
\r
8845 _("Save Game to File"), NULL, fileTitle, NULL);
\r
8847 SaveGame(f, 0, "");
\r
8854 ScheduleDelayedEvent(DelayedEventCallback cb, long millisec)
\r
8856 if (delayedTimerEvent != 0) {
\r
8857 if (appData.debugMode && cb != delayedTimerCallback) { // [HGM] alive: not too much debug
\r
8858 fprintf(debugFP, "ScheduleDelayedEvent: event already scheduled\n");
\r
8860 KillTimer(hwndMain, delayedTimerEvent);
\r
8861 delayedTimerEvent = 0;
\r
8862 if(delayedTimerCallback != cb) // [HGM] alive: do not "flush" same event, just postpone it
\r
8863 delayedTimerCallback();
\r
8865 delayedTimerCallback = cb;
\r
8866 delayedTimerEvent = SetTimer(hwndMain, (UINT) DELAYED_TIMER_ID,
\r
8867 (UINT) millisec, NULL);
\r
8870 DelayedEventCallback
\r
8873 if (delayedTimerEvent) {
\r
8874 return delayedTimerCallback;
\r
8881 CancelDelayedEvent()
\r
8883 if (delayedTimerEvent) {
\r
8884 KillTimer(hwndMain, delayedTimerEvent);
\r
8885 delayedTimerEvent = 0;
\r
8889 DWORD GetWin32Priority(int nice)
\r
8890 { // [HGM] nice: translate Unix nice() value to indows priority class. (Code stolen from Polyglot 1.4w11)
\r
8892 REALTIME_PRIORITY_CLASS 0x00000100
\r
8893 HIGH_PRIORITY_CLASS 0x00000080
\r
8894 ABOVE_NORMAL_PRIORITY_CLASS 0x00008000
\r
8895 NORMAL_PRIORITY_CLASS 0x00000020
\r
8896 BELOW_NORMAL_PRIORITY_CLASS 0x00004000
\r
8897 IDLE_PRIORITY_CLASS 0x00000040
\r
8899 if (nice < -15) return 0x00000080;
\r
8900 if (nice < 0) return 0x00008000;
\r
8901 if (nice == 0) return 0x00000020;
\r
8902 if (nice < 15) return 0x00004000;
\r
8903 return 0x00000040;
\r
8906 void RunCommand(char *cmdLine)
\r
8908 /* Now create the child process. */
\r
8909 STARTUPINFO siStartInfo;
\r
8910 PROCESS_INFORMATION piProcInfo;
\r
8912 siStartInfo.cb = sizeof(STARTUPINFO);
\r
8913 siStartInfo.lpReserved = NULL;
\r
8914 siStartInfo.lpDesktop = NULL;
\r
8915 siStartInfo.lpTitle = NULL;
\r
8916 siStartInfo.dwFlags = STARTF_USESTDHANDLES;
\r
8917 siStartInfo.cbReserved2 = 0;
\r
8918 siStartInfo.lpReserved2 = NULL;
\r
8919 siStartInfo.hStdInput = NULL;
\r
8920 siStartInfo.hStdOutput = NULL;
\r
8921 siStartInfo.hStdError = NULL;
\r
8923 CreateProcess(NULL,
\r
8924 cmdLine, /* command line */
\r
8925 NULL, /* process security attributes */
\r
8926 NULL, /* primary thread security attrs */
\r
8927 TRUE, /* handles are inherited */
\r
8928 DETACHED_PROCESS|CREATE_NEW_PROCESS_GROUP,
\r
8929 NULL, /* use parent's environment */
\r
8931 &siStartInfo, /* STARTUPINFO pointer */
\r
8932 &piProcInfo); /* receives PROCESS_INFORMATION */
\r
8934 CloseHandle(piProcInfo.hThread);
\r
8937 /* Start a child process running the given program.
\r
8938 The process's standard output can be read from "from", and its
\r
8939 standard input can be written to "to".
\r
8940 Exit with fatal error if anything goes wrong.
\r
8941 Returns an opaque pointer that can be used to destroy the process
\r
8945 StartChildProcess(char *cmdLine, char *dir, ProcRef *pr)
\r
8947 #define BUFSIZE 4096
\r
8949 HANDLE hChildStdinRd, hChildStdinWr,
\r
8950 hChildStdoutRd, hChildStdoutWr;
\r
8951 HANDLE hChildStdinWrDup, hChildStdoutRdDup;
\r
8952 SECURITY_ATTRIBUTES saAttr;
\r
8954 PROCESS_INFORMATION piProcInfo;
\r
8955 STARTUPINFO siStartInfo;
\r
8957 char buf[MSG_SIZ];
\r
8960 if (appData.debugMode) {
\r
8961 fprintf(debugFP, "StartChildProcess (dir=\"%s\") %s\n", dir, cmdLine);
\r
8966 /* Set the bInheritHandle flag so pipe handles are inherited. */
\r
8967 saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);
\r
8968 saAttr.bInheritHandle = TRUE;
\r
8969 saAttr.lpSecurityDescriptor = NULL;
\r
8972 * The steps for redirecting child's STDOUT:
\r
8973 * 1. Create anonymous pipe to be STDOUT for child.
\r
8974 * 2. Create a noninheritable duplicate of read handle,
\r
8975 * and close the inheritable read handle.
\r
8978 /* Create a pipe for the child's STDOUT. */
\r
8979 if (! CreatePipe(&hChildStdoutRd, &hChildStdoutWr, &saAttr, 0)) {
\r
8980 return GetLastError();
\r
8983 /* Duplicate the read handle to the pipe, so it is not inherited. */
\r
8984 fSuccess = DuplicateHandle(GetCurrentProcess(), hChildStdoutRd,
\r
8985 GetCurrentProcess(), &hChildStdoutRdDup, 0,
\r
8986 FALSE, /* not inherited */
\r
8987 DUPLICATE_SAME_ACCESS);
\r
8989 return GetLastError();
\r
8991 CloseHandle(hChildStdoutRd);
\r
8994 * The steps for redirecting child's STDIN:
\r
8995 * 1. Create anonymous pipe to be STDIN for child.
\r
8996 * 2. Create a noninheritable duplicate of write handle,
\r
8997 * and close the inheritable write handle.
\r
9000 /* Create a pipe for the child's STDIN. */
\r
9001 if (! CreatePipe(&hChildStdinRd, &hChildStdinWr, &saAttr, 0)) {
\r
9002 return GetLastError();
\r
9005 /* Duplicate the write handle to the pipe, so it is not inherited. */
\r
9006 fSuccess = DuplicateHandle(GetCurrentProcess(), hChildStdinWr,
\r
9007 GetCurrentProcess(), &hChildStdinWrDup, 0,
\r
9008 FALSE, /* not inherited */
\r
9009 DUPLICATE_SAME_ACCESS);
\r
9011 return GetLastError();
\r
9013 CloseHandle(hChildStdinWr);
\r
9015 /* Arrange to (1) look in dir for the child .exe file, and
\r
9016 * (2) have dir be the child's working directory. Interpret
\r
9017 * dir relative to the directory WinBoard loaded from. */
\r
9018 GetCurrentDirectory(MSG_SIZ, buf);
\r
9019 SetCurrentDirectory(installDir);
\r
9020 SetCurrentDirectory(dir);
\r
9022 /* Now create the child process. */
\r
9024 siStartInfo.cb = sizeof(STARTUPINFO);
\r
9025 siStartInfo.lpReserved = NULL;
\r
9026 siStartInfo.lpDesktop = NULL;
\r
9027 siStartInfo.lpTitle = NULL;
\r
9028 siStartInfo.dwFlags = STARTF_USESTDHANDLES;
\r
9029 siStartInfo.cbReserved2 = 0;
\r
9030 siStartInfo.lpReserved2 = NULL;
\r
9031 siStartInfo.hStdInput = hChildStdinRd;
\r
9032 siStartInfo.hStdOutput = hChildStdoutWr;
\r
9033 siStartInfo.hStdError = hChildStdoutWr;
\r
9035 fSuccess = CreateProcess(NULL,
\r
9036 cmdLine, /* command line */
\r
9037 NULL, /* process security attributes */
\r
9038 NULL, /* primary thread security attrs */
\r
9039 TRUE, /* handles are inherited */
\r
9040 DETACHED_PROCESS|CREATE_NEW_PROCESS_GROUP,
\r
9041 NULL, /* use parent's environment */
\r
9043 &siStartInfo, /* STARTUPINFO pointer */
\r
9044 &piProcInfo); /* receives PROCESS_INFORMATION */
\r
9046 err = GetLastError();
\r
9047 SetCurrentDirectory(buf); /* return to prev directory */
\r
9052 if (appData.niceEngines){ // [HGM] nice: adjust engine proc priority
\r
9053 if(appData.debugMode) fprintf(debugFP, "nice engine proc to %d\n", appData.niceEngines);
\r
9054 SetPriorityClass(piProcInfo.hProcess, GetWin32Priority(appData.niceEngines));
\r
9057 /* Close the handles we don't need in the parent */
\r
9058 CloseHandle(piProcInfo.hThread);
\r
9059 CloseHandle(hChildStdinRd);
\r
9060 CloseHandle(hChildStdoutWr);
\r
9062 /* Prepare return value */
\r
9063 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
\r
9064 cp->kind = CPReal;
\r
9065 cp->hProcess = piProcInfo.hProcess;
\r
9066 cp->pid = piProcInfo.dwProcessId;
\r
9067 cp->hFrom = hChildStdoutRdDup;
\r
9068 cp->hTo = hChildStdinWrDup;
\r
9070 *pr = (void *) cp;
\r
9072 /* Klaus Friedel says that this Sleep solves a problem under Windows
\r
9073 2000 where engines sometimes don't see the initial command(s)
\r
9074 from WinBoard and hang. I don't understand how that can happen,
\r
9075 but the Sleep is harmless, so I've put it in. Others have also
\r
9076 reported what may be the same problem, so hopefully this will fix
\r
9077 it for them too. */
\r
9085 DestroyChildProcess(ProcRef pr, int/*boolean*/ signal)
\r
9087 ChildProc *cp; int result;
\r
9089 cp = (ChildProc *) pr;
\r
9090 if (cp == NULL) return;
\r
9092 switch (cp->kind) {
\r
9094 /* TerminateProcess is considered harmful, so... */
\r
9095 CloseHandle(cp->hTo); /* Closing this will give the child an EOF and hopefully kill it */
\r
9096 if (cp->hFrom) CloseHandle(cp->hFrom); /* if NULL, InputThread will close it */
\r
9097 /* The following doesn't work because the chess program
\r
9098 doesn't "have the same console" as WinBoard. Maybe
\r
9099 we could arrange for this even though neither WinBoard
\r
9100 nor the chess program uses a console for stdio? */
\r
9101 /*!!if (signal) GenerateConsoleCtrlEvent(CTRL_BREAK_EVENT, cp->pid);*/
\r
9103 /* [AS] Special termination modes for misbehaving programs... */
\r
9104 if( signal == 9 ) {
\r
9105 result = TerminateProcess( cp->hProcess, 0 );
\r
9107 if ( appData.debugMode) {
\r
9108 fprintf( debugFP, "Terminating process %lu, result=%d\n", cp->pid, result );
\r
9111 else if( signal == 10 ) {
\r
9112 DWORD dw = WaitForSingleObject( cp->hProcess, 3*1000 ); // Wait 3 seconds at most
\r
9114 if( dw != WAIT_OBJECT_0 ) {
\r
9115 result = TerminateProcess( cp->hProcess, 0 );
\r
9117 if ( appData.debugMode) {
\r
9118 fprintf( debugFP, "Process %lu still alive after timeout, killing... result=%d\n", cp->pid, result );
\r
9124 CloseHandle(cp->hProcess);
\r
9128 if (cp->hFrom) CloseHandle(cp->hFrom);
\r
9132 closesocket(cp->sock);
\r
9137 if (signal) send(cp->sock2, "\017", 1, 0); /* 017 = 15 = SIGTERM */
\r
9138 closesocket(cp->sock);
\r
9139 closesocket(cp->sock2);
\r
9147 InterruptChildProcess(ProcRef pr)
\r
9151 cp = (ChildProc *) pr;
\r
9152 if (cp == NULL) return;
\r
9153 switch (cp->kind) {
\r
9155 /* The following doesn't work because the chess program
\r
9156 doesn't "have the same console" as WinBoard. Maybe
\r
9157 we could arrange for this even though neither WinBoard
\r
9158 nor the chess program uses a console for stdio */
\r
9159 /*!!GenerateConsoleCtrlEvent(CTRL_C_EVENT, cp->pid);*/
\r
9164 /* Can't interrupt */
\r
9168 send(cp->sock2, "\002", 1, 0); /* 2 = SIGINT */
\r
9175 OpenTelnet(char *host, char *port, ProcRef *pr)
\r
9177 char cmdLine[MSG_SIZ];
\r
9179 if (port[0] == NULLCHAR) {
\r
9180 snprintf(cmdLine, MSG_SIZ, "%s %s", appData.telnetProgram, host);
\r
9182 snprintf(cmdLine, MSG_SIZ, "%s %s %s", appData.telnetProgram, host, port);
\r
9184 return StartChildProcess(cmdLine, "", pr);
\r
9188 /* Code to open TCP sockets */
\r
9191 OpenTCP(char *host, char *port, ProcRef *pr)
\r
9197 struct sockaddr_in sa, mysa;
\r
9198 struct hostent FAR *hp;
\r
9199 unsigned short uport;
\r
9200 WORD wVersionRequested;
\r
9203 /* Initialize socket DLL */
\r
9204 wVersionRequested = MAKEWORD(1, 1);
\r
9205 err = WSAStartup(wVersionRequested, &wsaData);
\r
9206 if (err != 0) return err;
\r
9209 if ((s = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {
\r
9210 err = WSAGetLastError();
\r
9215 /* Bind local address using (mostly) don't-care values.
\r
9217 memset((char *) &mysa, 0, sizeof(struct sockaddr_in));
\r
9218 mysa.sin_family = AF_INET;
\r
9219 mysa.sin_addr.s_addr = INADDR_ANY;
\r
9220 uport = (unsigned short) 0;
\r
9221 mysa.sin_port = htons(uport);
\r
9222 if (bind(s, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))
\r
9223 == SOCKET_ERROR) {
\r
9224 err = WSAGetLastError();
\r
9229 /* Resolve remote host name */
\r
9230 memset((char *) &sa, 0, sizeof(struct sockaddr_in));
\r
9231 if (!(hp = gethostbyname(host))) {
\r
9232 unsigned int b0, b1, b2, b3;
\r
9234 err = WSAGetLastError();
\r
9236 if (sscanf(host, "%u.%u.%u.%u", &b0, &b1, &b2, &b3) == 4) {
\r
9237 hp = (struct hostent *) calloc(1, sizeof(struct hostent));
\r
9238 hp->h_addrtype = AF_INET;
\r
9240 hp->h_addr_list = (char **) calloc(2, sizeof(char *));
\r
9241 hp->h_addr_list[0] = (char *) malloc(4);
\r
9242 hp->h_addr_list[0][0] = (char) b0;
\r
9243 hp->h_addr_list[0][1] = (char) b1;
\r
9244 hp->h_addr_list[0][2] = (char) b2;
\r
9245 hp->h_addr_list[0][3] = (char) b3;
\r
9251 sa.sin_family = hp->h_addrtype;
\r
9252 uport = (unsigned short) atoi(port);
\r
9253 sa.sin_port = htons(uport);
\r
9254 memcpy((char *) &sa.sin_addr, hp->h_addr, hp->h_length);
\r
9256 /* Make connection */
\r
9257 if (connect(s, (struct sockaddr *) &sa,
\r
9258 sizeof(struct sockaddr_in)) == SOCKET_ERROR) {
\r
9259 err = WSAGetLastError();
\r
9264 /* Prepare return value */
\r
9265 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
\r
9266 cp->kind = CPSock;
\r
9268 *pr = (ProcRef *) cp;
\r
9274 OpenCommPort(char *name, ProcRef *pr)
\r
9279 char fullname[MSG_SIZ];
\r
9281 if (*name != '\\')
\r
9282 snprintf(fullname, MSG_SIZ, "\\\\.\\%s", name);
\r
9284 safeStrCpy(fullname, name, sizeof(fullname)/sizeof(fullname[0]) );
\r
9286 h = CreateFile(name, GENERIC_READ | GENERIC_WRITE,
\r
9287 0, NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL);
\r
9288 if (h == (HANDLE) -1) {
\r
9289 return GetLastError();
\r
9293 if (!SetCommState(h, (LPDCB) &dcb)) return GetLastError();
\r
9295 /* Accumulate characters until a 100ms pause, then parse */
\r
9296 ct.ReadIntervalTimeout = 100;
\r
9297 ct.ReadTotalTimeoutMultiplier = 0;
\r
9298 ct.ReadTotalTimeoutConstant = 0;
\r
9299 ct.WriteTotalTimeoutMultiplier = 0;
\r
9300 ct.WriteTotalTimeoutConstant = 0;
\r
9301 if (!SetCommTimeouts(h, (LPCOMMTIMEOUTS) &ct)) return GetLastError();
\r
9303 /* Prepare return value */
\r
9304 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
\r
9305 cp->kind = CPComm;
\r
9308 *pr = (ProcRef *) cp;
\r
9314 OpenLoopback(ProcRef *pr)
\r
9316 DisplayFatalError(_("Not implemented"), 0, 1);
\r
9322 OpenRcmd(char* host, char* user, char* cmd, ProcRef* pr)
\r
9327 struct sockaddr_in sa, mysa;
\r
9328 struct hostent FAR *hp;
\r
9329 unsigned short uport;
\r
9330 WORD wVersionRequested;
\r
9333 char stderrPortStr[MSG_SIZ];
\r
9335 /* Initialize socket DLL */
\r
9336 wVersionRequested = MAKEWORD(1, 1);
\r
9337 err = WSAStartup(wVersionRequested, &wsaData);
\r
9338 if (err != 0) return err;
\r
9340 /* Resolve remote host name */
\r
9341 memset((char *) &sa, 0, sizeof(struct sockaddr_in));
\r
9342 if (!(hp = gethostbyname(host))) {
\r
9343 unsigned int b0, b1, b2, b3;
\r
9345 err = WSAGetLastError();
\r
9347 if (sscanf(host, "%u.%u.%u.%u", &b0, &b1, &b2, &b3) == 4) {
\r
9348 hp = (struct hostent *) calloc(1, sizeof(struct hostent));
\r
9349 hp->h_addrtype = AF_INET;
\r
9351 hp->h_addr_list = (char **) calloc(2, sizeof(char *));
\r
9352 hp->h_addr_list[0] = (char *) malloc(4);
\r
9353 hp->h_addr_list[0][0] = (char) b0;
\r
9354 hp->h_addr_list[0][1] = (char) b1;
\r
9355 hp->h_addr_list[0][2] = (char) b2;
\r
9356 hp->h_addr_list[0][3] = (char) b3;
\r
9362 sa.sin_family = hp->h_addrtype;
\r
9363 uport = (unsigned short) 514;
\r
9364 sa.sin_port = htons(uport);
\r
9365 memcpy((char *) &sa.sin_addr, hp->h_addr, hp->h_length);
\r
9367 /* Bind local socket to unused "privileged" port address
\r
9369 s = INVALID_SOCKET;
\r
9370 memset((char *) &mysa, 0, sizeof(struct sockaddr_in));
\r
9371 mysa.sin_family = AF_INET;
\r
9372 mysa.sin_addr.s_addr = INADDR_ANY;
\r
9373 for (fromPort = 1023;; fromPort--) {
\r
9374 if (fromPort < 0) {
\r
9376 return WSAEADDRINUSE;
\r
9378 if (s == INVALID_SOCKET) {
\r
9379 if ((s = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {
\r
9380 err = WSAGetLastError();
\r
9385 uport = (unsigned short) fromPort;
\r
9386 mysa.sin_port = htons(uport);
\r
9387 if (bind(s, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))
\r
9388 == SOCKET_ERROR) {
\r
9389 err = WSAGetLastError();
\r
9390 if (err == WSAEADDRINUSE) continue;
\r
9394 if (connect(s, (struct sockaddr *) &sa,
\r
9395 sizeof(struct sockaddr_in)) == SOCKET_ERROR) {
\r
9396 err = WSAGetLastError();
\r
9397 if (err == WSAEADDRINUSE) {
\r
9408 /* Bind stderr local socket to unused "privileged" port address
\r
9410 s2 = INVALID_SOCKET;
\r
9411 memset((char *) &mysa, 0, sizeof(struct sockaddr_in));
\r
9412 mysa.sin_family = AF_INET;
\r
9413 mysa.sin_addr.s_addr = INADDR_ANY;
\r
9414 for (fromPort = 1023;; fromPort--) {
\r
9415 if (fromPort == prevStderrPort) continue; // don't reuse port
\r
9416 if (fromPort < 0) {
\r
9417 (void) closesocket(s);
\r
9419 return WSAEADDRINUSE;
\r
9421 if (s2 == INVALID_SOCKET) {
\r
9422 if ((s2 = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {
\r
9423 err = WSAGetLastError();
\r
9429 uport = (unsigned short) fromPort;
\r
9430 mysa.sin_port = htons(uport);
\r
9431 if (bind(s2, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))
\r
9432 == SOCKET_ERROR) {
\r
9433 err = WSAGetLastError();
\r
9434 if (err == WSAEADDRINUSE) continue;
\r
9435 (void) closesocket(s);
\r
9439 if (listen(s2, 1) == SOCKET_ERROR) {
\r
9440 err = WSAGetLastError();
\r
9441 if (err == WSAEADDRINUSE) {
\r
9443 s2 = INVALID_SOCKET;
\r
9446 (void) closesocket(s);
\r
9447 (void) closesocket(s2);
\r
9453 prevStderrPort = fromPort; // remember port used
\r
9454 snprintf(stderrPortStr, MSG_SIZ, "%d", fromPort);
\r
9456 if (send(s, stderrPortStr, strlen(stderrPortStr) + 1, 0) == SOCKET_ERROR) {
\r
9457 err = WSAGetLastError();
\r
9458 (void) closesocket(s);
\r
9459 (void) closesocket(s2);
\r
9464 if (send(s, UserName(), strlen(UserName()) + 1, 0) == SOCKET_ERROR) {
\r
9465 err = WSAGetLastError();
\r
9466 (void) closesocket(s);
\r
9467 (void) closesocket(s2);
\r
9471 if (*user == NULLCHAR) user = UserName();
\r
9472 if (send(s, user, strlen(user) + 1, 0) == SOCKET_ERROR) {
\r
9473 err = WSAGetLastError();
\r
9474 (void) closesocket(s);
\r
9475 (void) closesocket(s2);
\r
9479 if (send(s, cmd, strlen(cmd) + 1, 0) == SOCKET_ERROR) {
\r
9480 err = WSAGetLastError();
\r
9481 (void) closesocket(s);
\r
9482 (void) closesocket(s2);
\r
9487 if ((s3 = accept(s2, NULL, NULL)) == INVALID_SOCKET) {
\r
9488 err = WSAGetLastError();
\r
9489 (void) closesocket(s);
\r
9490 (void) closesocket(s2);
\r
9494 (void) closesocket(s2); /* Stop listening */
\r
9496 /* Prepare return value */
\r
9497 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
\r
9498 cp->kind = CPRcmd;
\r
9501 *pr = (ProcRef *) cp;
\r
9508 AddInputSource(ProcRef pr, int lineByLine,
\r
9509 InputCallback func, VOIDSTAR closure)
\r
9511 InputSource *is, *is2 = NULL;
\r
9512 ChildProc *cp = (ChildProc *) pr;
\r
9514 is = (InputSource *) calloc(1, sizeof(InputSource));
\r
9515 is->lineByLine = lineByLine;
\r
9517 is->closure = closure;
\r
9518 is->second = NULL;
\r
9519 is->next = is->buf;
\r
9520 if (pr == NoProc) {
\r
9521 is->kind = CPReal;
\r
9522 consoleInputSource = is;
\r
9524 is->kind = cp->kind;
\r
9526 [AS] Try to avoid a race condition if the thread is given control too early:
\r
9527 we create all threads suspended so that the is->hThread variable can be
\r
9528 safely assigned, then let the threads start with ResumeThread.
\r
9530 switch (cp->kind) {
\r
9532 is->hFile = cp->hFrom;
\r
9533 cp->hFrom = NULL; /* now owned by InputThread */
\r
9535 CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) NonOvlInputThread,
\r
9536 (LPVOID) is, CREATE_SUSPENDED, &is->id);
\r
9540 is->hFile = cp->hFrom;
\r
9541 cp->hFrom = NULL; /* now owned by InputThread */
\r
9543 CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) InputThread,
\r
9544 (LPVOID) is, CREATE_SUSPENDED, &is->id);
\r
9548 is->sock = cp->sock;
\r
9550 CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,
\r
9551 (LPVOID) is, CREATE_SUSPENDED, &is->id);
\r
9555 is2 = (InputSource *) calloc(1, sizeof(InputSource));
\r
9557 is->sock = cp->sock;
\r
9559 is2->sock = cp->sock2;
\r
9560 is2->second = is2;
\r
9562 CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,
\r
9563 (LPVOID) is, CREATE_SUSPENDED, &is->id);
\r
9565 CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,
\r
9566 (LPVOID) is2, CREATE_SUSPENDED, &is2->id);
\r
9570 if( is->hThread != NULL ) {
\r
9571 ResumeThread( is->hThread );
\r
9574 if( is2 != NULL && is2->hThread != NULL ) {
\r
9575 ResumeThread( is2->hThread );
\r
9579 return (InputSourceRef) is;
\r
9583 RemoveInputSource(InputSourceRef isr)
\r
9587 is = (InputSource *) isr;
\r
9588 is->hThread = NULL; /* tell thread to stop */
\r
9589 CloseHandle(is->hThread);
\r
9590 if (is->second != NULL) {
\r
9591 is->second->hThread = NULL;
\r
9592 CloseHandle(is->second->hThread);
\r
9596 int no_wrap(char *message, int count)
\r
9598 ConsoleOutput(message, count, FALSE);
\r
9603 OutputToProcess(ProcRef pr, char *message, int count, int *outError)
\r
9606 int outCount = SOCKET_ERROR;
\r
9607 ChildProc *cp = (ChildProc *) pr;
\r
9608 static OVERLAPPED ovl;
\r
9609 static int line = 0;
\r
9613 if (appData.noJoin || !appData.useInternalWrap)
\r
9614 return no_wrap(message, count);
\r
9617 int width = get_term_width();
\r
9618 int len = wrap(NULL, message, count, width, &line);
\r
9619 char *msg = malloc(len);
\r
9623 return no_wrap(message, count);
\r
9626 dbgchk = wrap(msg, message, count, width, &line);
\r
9627 if (dbgchk != len && appData.debugMode)
\r
9628 fprintf(debugFP, "wrap(): dbgchk(%d) != len(%d)\n", dbgchk, len);
\r
9629 ConsoleOutput(msg, len, FALSE);
\r
9636 if (ovl.hEvent == NULL) {
\r
9637 ovl.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
\r
9639 ovl.Internal = ovl.InternalHigh = ovl.Offset = ovl.OffsetHigh = 0;
\r
9641 switch (cp->kind) {
\r
9644 outCount = send(cp->sock, message, count, 0);
\r
9645 if (outCount == SOCKET_ERROR) {
\r
9646 *outError = WSAGetLastError();
\r
9648 *outError = NO_ERROR;
\r
9653 if (WriteFile(((ChildProc *)pr)->hTo, message, count,
\r
9654 &dOutCount, NULL)) {
\r
9655 *outError = NO_ERROR;
\r
9656 outCount = (int) dOutCount;
\r
9658 *outError = GetLastError();
\r
9663 *outError = DoWriteFile(((ChildProc *)pr)->hTo, message, count,
\r
9664 &dOutCount, &ovl);
\r
9665 if (*outError == NO_ERROR) {
\r
9666 outCount = (int) dOutCount;
\r
9676 if(n != 0) Sleep(n);
\r
9680 OutputToProcessDelayed(ProcRef pr, char *message, int count, int *outError,
\r
9683 /* Ignore delay, not implemented for WinBoard */
\r
9684 return OutputToProcess(pr, message, count, outError);
\r
9689 CmailSigHandlerCallBack(InputSourceRef isr, VOIDSTAR closure,
\r
9690 char *buf, int count, int error)
\r
9692 DisplayFatalError(_("Not implemented"), 0, 1);
\r
9695 /* see wgamelist.c for Game List functions */
\r
9696 /* see wedittags.c for Edit Tags functions */
\r
9703 char buf[MSG_SIZ];
\r
9706 if (SearchPath(installDir, appData.icsLogon, NULL, MSG_SIZ, buf, &dummy)) {
\r
9707 f = fopen(buf, "r");
\r
9709 ProcessICSInitScript(f);
\r
9717 StartAnalysisClock()
\r
9719 if (analysisTimerEvent) return;
\r
9720 analysisTimerEvent = SetTimer(hwndMain, (UINT) ANALYSIS_TIMER_ID,
\r
9721 (UINT) 2000, NULL);
\r
9725 SetHighlights(int fromX, int fromY, int toX, int toY)
\r
9727 highlightInfo.sq[0].x = fromX;
\r
9728 highlightInfo.sq[0].y = fromY;
\r
9729 highlightInfo.sq[1].x = toX;
\r
9730 highlightInfo.sq[1].y = toY;
\r
9736 highlightInfo.sq[0].x = highlightInfo.sq[0].y =
\r
9737 highlightInfo.sq[1].x = highlightInfo.sq[1].y = -1;
\r
9741 SetPremoveHighlights(int fromX, int fromY, int toX, int toY)
\r
9743 premoveHighlightInfo.sq[0].x = fromX;
\r
9744 premoveHighlightInfo.sq[0].y = fromY;
\r
9745 premoveHighlightInfo.sq[1].x = toX;
\r
9746 premoveHighlightInfo.sq[1].y = toY;
\r
9750 ClearPremoveHighlights()
\r
9752 premoveHighlightInfo.sq[0].x = premoveHighlightInfo.sq[0].y =
\r
9753 premoveHighlightInfo.sq[1].x = premoveHighlightInfo.sq[1].y = -1;
\r
9757 ShutDownFrontEnd()
\r
9759 if (saveSettingsOnExit) SaveSettings(settingsFileName);
\r
9760 DeleteClipboardTempFiles();
\r
9766 if (IsIconic(hwndMain))
\r
9767 ShowWindow(hwndMain, SW_RESTORE);
\r
9769 SetActiveWindow(hwndMain);
\r
9773 * Prototypes for animation support routines
\r
9775 static void ScreenSquare(int column, int row, POINT * pt);
\r
9776 static void Tween( POINT * start, POINT * mid, POINT * finish, int factor,
\r
9777 POINT frames[], int * nFrames);
\r
9783 AnimateAtomicCapture(Board board, int fromX, int fromY, int toX, int toY)
\r
9784 { // [HGM] atomic: animate blast wave
\r
9787 explodeInfo.fromX = fromX;
\r
9788 explodeInfo.fromY = fromY;
\r
9789 explodeInfo.toX = toX;
\r
9790 explodeInfo.toY = toY;
\r
9791 for(i=1; i<4*kFactor; i++) {
\r
9792 explodeInfo.radius = (i*180)/(4*kFactor-1);
\r
9793 DrawPosition(FALSE, board);
\r
9794 Sleep(appData.animSpeed);
\r
9796 explodeInfo.radius = 0;
\r
9797 DrawPosition(TRUE, board);
\r
9801 AnimateMove(board, fromX, fromY, toX, toY)
\r
9808 ChessSquare piece;
\r
9809 POINT start, finish, mid;
\r
9810 POINT frames[kFactor * 2 + 1];
\r
9813 if (!appData.animate) return;
\r
9814 if (doingSizing) return;
\r
9815 if (fromY < 0 || fromX < 0) return;
\r
9816 piece = board[fromY][fromX];
\r
9817 if (piece >= EmptySquare) return;
\r
9819 ScreenSquare(fromX, fromY, &start);
\r
9820 ScreenSquare(toX, toY, &finish);
\r
9822 /* All moves except knight jumps move in straight line */
\r
9823 if (!(abs(fromX-toX) == 1 && abs(fromY-toY) == 2 || abs(fromX-toX) == 2 && abs(fromY-toY) == 1)) {
\r
9824 mid.x = start.x + (finish.x - start.x) / 2;
\r
9825 mid.y = start.y + (finish.y - start.y) / 2;
\r
9827 /* Knight: make straight movement then diagonal */
\r
9828 if (abs(toY - fromY) < abs(toX - fromX)) {
\r
9829 mid.x = start.x + (finish.x - start.x) / 2;
\r
9833 mid.y = start.y + (finish.y - start.y) / 2;
\r
9837 /* Don't use as many frames for very short moves */
\r
9838 if (abs(toY - fromY) + abs(toX - fromX) <= 2)
\r
9839 Tween(&start, &mid, &finish, kFactor - 1, frames, &nFrames);
\r
9841 Tween(&start, &mid, &finish, kFactor, frames, &nFrames);
\r
9843 animInfo.from.x = fromX;
\r
9844 animInfo.from.y = fromY;
\r
9845 animInfo.to.x = toX;
\r
9846 animInfo.to.y = toY;
\r
9847 animInfo.lastpos = start;
\r
9848 animInfo.piece = piece;
\r
9849 for (n = 0; n < nFrames; n++) {
\r
9850 animInfo.pos = frames[n];
\r
9851 DrawPosition(FALSE, NULL);
\r
9852 animInfo.lastpos = animInfo.pos;
\r
9853 Sleep(appData.animSpeed);
\r
9855 animInfo.pos = finish;
\r
9856 DrawPosition(FALSE, NULL);
\r
9857 animInfo.piece = EmptySquare;
\r
9858 Explode(board, fromX, fromY, toX, toY);
\r
9861 /* Convert board position to corner of screen rect and color */
\r
9864 ScreenSquare(column, row, pt)
\r
9865 int column; int row; POINT * pt;
\r
9868 pt->x = lineGap + ((BOARD_WIDTH-1)-column) * (squareSize + lineGap);
\r
9869 pt->y = lineGap + row * (squareSize + lineGap);
\r
9871 pt->x = lineGap + column * (squareSize + lineGap);
\r
9872 pt->y = lineGap + ((BOARD_HEIGHT-1)-row) * (squareSize + lineGap);
\r
9876 /* Generate a series of frame coords from start->mid->finish.
\r
9877 The movement rate doubles until the half way point is
\r
9878 reached, then halves back down to the final destination,
\r
9879 which gives a nice slow in/out effect. The algorithmn
\r
9880 may seem to generate too many intermediates for short
\r
9881 moves, but remember that the purpose is to attract the
\r
9882 viewers attention to the piece about to be moved and
\r
9883 then to where it ends up. Too few frames would be less
\r
9887 Tween(start, mid, finish, factor, frames, nFrames)
\r
9888 POINT * start; POINT * mid;
\r
9889 POINT * finish; int factor;
\r
9890 POINT frames[]; int * nFrames;
\r
9892 int n, fraction = 1, count = 0;
\r
9894 /* Slow in, stepping 1/16th, then 1/8th, ... */
\r
9895 for (n = 0; n < factor; n++)
\r
9897 for (n = 0; n < factor; n++) {
\r
9898 frames[count].x = start->x + (mid->x - start->x) / fraction;
\r
9899 frames[count].y = start->y + (mid->y - start->y) / fraction;
\r
9901 fraction = fraction / 2;
\r
9905 frames[count] = *mid;
\r
9908 /* Slow out, stepping 1/2, then 1/4, ... */
\r
9910 for (n = 0; n < factor; n++) {
\r
9911 frames[count].x = finish->x - (finish->x - mid->x) / fraction;
\r
9912 frames[count].y = finish->y - (finish->y - mid->y) / fraction;
\r
9914 fraction = fraction * 2;
\r
9920 SettingsPopUp(ChessProgramState *cps)
\r
9921 { // [HGM] wrapper needed because handles must not be passed through back-end
\r
9922 EngineOptionsPopup(savedHwnd, cps);
\r
9925 int flock(int fid, int code)
\r
9927 HANDLE hFile = (HANDLE) _get_osfhandle(fid);
\r
9931 ov.OffsetHigh = 0;
\r
9933 case 1: LockFileEx(hFile, LOCKFILE_EXCLUSIVE_LOCK, 0, 1024, 0, &ov); break; // LOCK_SH
\r
9934 case 2: LockFileEx(hFile, LOCKFILE_EXCLUSIVE_LOCK, 0, 1024, 0, &ov); break; // LOCK_EX
\r
9935 case 3: UnlockFileEx(hFile, 0, 1024, 0, &ov); break; // LOCK_UN
\r
9936 default: return -1;
\r