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, border;
\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
2116 char name[128], buf[MSG_SIZ];
\r
2118 snprintf(name, sizeof(name)/sizeof(name[0]), "%s%d%s", piece, squareSize, suffix);
\r
2119 if(appData.pieceDirectory[0]) {
\r
2121 snprintf(buf, MSG_SIZ, "%s\\%s.bmp", appData.pieceDirectory, name);
\r
2122 res = LoadImage( 0, buf, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );
\r
2123 if(res) return res;
\r
2125 if (gameInfo.event &&
\r
2126 strcmp(gameInfo.event, "Easter Egg Hunt") == 0 &&
\r
2127 strcmp(name, "k80s") == 0) {
\r
2128 safeStrCpy(name, "tim", sizeof(name)/sizeof(name[0]) );
\r
2130 return LoadBitmap(hinst, name);
\r
2134 /* Insert a color into the program's logical palette
\r
2135 structure. This code assumes the given color is
\r
2136 the result of the RGB or PALETTERGB macro, and it
\r
2137 knows how those macros work (which is documented).
\r
2140 InsertInPalette(COLORREF color)
\r
2142 LPPALETTEENTRY pe = &(pLogPal->palPalEntry[pLogPal->palNumEntries]);
\r
2144 if (pLogPal->palNumEntries++ >= PALETTESIZE) {
\r
2145 DisplayFatalError(_("Too many colors"), 0, 1);
\r
2146 pLogPal->palNumEntries--;
\r
2150 pe->peFlags = (char) 0;
\r
2151 pe->peRed = (char) (0xFF & color);
\r
2152 pe->peGreen = (char) (0xFF & (color >> 8));
\r
2153 pe->peBlue = (char) (0xFF & (color >> 16));
\r
2159 InitDrawingColors()
\r
2161 if (pLogPal == NULL) {
\r
2162 /* Allocate enough memory for a logical palette with
\r
2163 * PALETTESIZE entries and set the size and version fields
\r
2164 * of the logical palette structure.
\r
2166 pLogPal = (NPLOGPALETTE)
\r
2167 LocalAlloc(LMEM_FIXED, (sizeof(LOGPALETTE) +
\r
2168 (sizeof(PALETTEENTRY) * (PALETTESIZE))));
\r
2169 pLogPal->palVersion = 0x300;
\r
2171 pLogPal->palNumEntries = 0;
\r
2173 InsertInPalette(lightSquareColor);
\r
2174 InsertInPalette(darkSquareColor);
\r
2175 InsertInPalette(whitePieceColor);
\r
2176 InsertInPalette(blackPieceColor);
\r
2177 InsertInPalette(highlightSquareColor);
\r
2178 InsertInPalette(premoveHighlightColor);
\r
2180 /* create a logical color palette according the information
\r
2181 * in the LOGPALETTE structure.
\r
2183 hPal = CreatePalette((LPLOGPALETTE) pLogPal);
\r
2185 lightSquareBrush = CreateSolidBrush(lightSquareColor);
\r
2186 blackSquareBrush = CreateSolidBrush(blackPieceColor);
\r
2187 darkSquareBrush = CreateSolidBrush(darkSquareColor);
\r
2188 whitePieceBrush = CreateSolidBrush(whitePieceColor);
\r
2189 blackPieceBrush = CreateSolidBrush(blackPieceColor);
\r
2190 iconBkgndBrush = CreateSolidBrush(GetSysColor(COLOR_BACKGROUND));
\r
2191 explodeBrush = CreateSolidBrush(highlightSquareColor); // [HGM] atomic
\r
2192 markerBrush = CreateSolidBrush(premoveHighlightColor); // [HGM] markers
\r
2193 /* [AS] Force rendering of the font-based pieces */
\r
2194 if( fontBitmapSquareSize > 0 ) {
\r
2195 fontBitmapSquareSize = 0;
\r
2201 BoardWidth(int boardSize, int n)
\r
2202 { /* [HGM] argument n added to allow different width and height */
\r
2203 int lineGap = sizeInfo[boardSize].lineGap;
\r
2205 if( appData.overrideLineGap >= 0 && appData.overrideLineGap <= 5 ) {
\r
2206 lineGap = appData.overrideLineGap;
\r
2209 return (n + 1) * lineGap +
\r
2210 n * sizeInfo[boardSize].squareSize;
\r
2213 /* Respond to board resize by dragging edge */
\r
2215 ResizeBoard(int newSizeX, int newSizeY, int flags)
\r
2217 BoardSize newSize = NUM_SIZES - 1;
\r
2218 static int recurse = 0;
\r
2219 if (IsIconic(hwndMain)) return;
\r
2220 if (recurse > 0) return;
\r
2222 while (newSize > 0) {
\r
2223 InitDrawingSizes(newSize+1000, 0); // [HGM] kludge to update sizeInfo without visible effects
\r
2224 if(newSizeX >= sizeInfo[newSize].cliWidth &&
\r
2225 newSizeY >= sizeInfo[newSize].cliHeight) break;
\r
2228 boardSize = newSize;
\r
2229 InitDrawingSizes(boardSize, flags);
\r
2234 extern Boolean twoBoards, partnerUp; // [HGM] dual
\r
2237 InitDrawingSizes(BoardSize boardSize, int flags)
\r
2239 int i, boardWidth, boardHeight; /* [HGM] height treated separately */
\r
2240 ChessSquare piece;
\r
2241 static int oldBoardSize = -1, oldTinyLayout = 0;
\r
2243 SIZE clockSize, messageSize;
\r
2245 char buf[MSG_SIZ];
\r
2247 HMENU hmenu = GetMenu(hwndMain);
\r
2248 RECT crect, wrect, oldRect;
\r
2250 LOGBRUSH logbrush;
\r
2251 VariantClass v = gameInfo.variant;
\r
2253 int suppressVisibleEffects = 0; // [HGM] kludge to request updating sizeInfo only
\r
2254 if((int)boardSize >= 1000 ) { boardSize -= 1000; suppressVisibleEffects = 1; }
\r
2256 /* [HGM] call with -2 uses old size (for if nr of files, ranks changes) */
\r
2257 if(boardSize == (BoardSize)(-2) ) boardSize = oldBoardSize;
\r
2258 oldBoardSize = boardSize;
\r
2260 if(boardSize != SizeMiddling && boardSize != SizePetite && boardSize != SizeBulky && !appData.useFont)
\r
2261 { // correct board size to one where built-in pieces exist
\r
2262 if((v == VariantCapablanca || v == VariantGothic || v == VariantGrand || v == VariantCapaRandom || v == VariantJanus || v == VariantSuper)
\r
2263 && (boardSize < SizePetite || boardSize > SizeBulky) // Archbishop and Chancellor available in entire middle range
\r
2264 || (v == VariantShogi && boardSize != SizeModerate) // Japanese-style Shogi
\r
2265 || v == VariantKnightmate || v == VariantSChess || v == VariantXiangqi || v == VariantSpartan
\r
2266 || v == VariantShatranj || v == VariantMakruk || v == VariantGreat || v == VariantFairy ) {
\r
2267 if(boardSize < SizeMediocre) boardSize = SizePetite; else
\r
2268 if(boardSize > SizeModerate) boardSize = SizeBulky; else
\r
2269 boardSize = SizeMiddling;
\r
2272 if(!appData.useFont && boardSize == SizePetite && (v == VariantShogi || v == VariantKnightmate)) boardSize = SizeMiddling; // no Unicorn in Petite
\r
2274 oldRect.left = wpMain.x; //[HGM] placement: remember previous window params
\r
2275 oldRect.top = wpMain.y;
\r
2276 oldRect.right = wpMain.x + wpMain.width;
\r
2277 oldRect.bottom = wpMain.y + wpMain.height;
\r
2279 tinyLayout = sizeInfo[boardSize].tinyLayout;
\r
2280 smallLayout = sizeInfo[boardSize].smallLayout;
\r
2281 squareSize = sizeInfo[boardSize].squareSize;
\r
2282 lineGap = sizeInfo[boardSize].lineGap;
\r
2283 minorSize = 0; /* [HGM] Kludge to see if demagnified pieces need to be shifted */
\r
2284 border = appData.useBorder && appData.border[0] ? squareSize/2 : 0;
\r
2286 if( appData.overrideLineGap >= 0 && appData.overrideLineGap <= 5 ) {
\r
2287 lineGap = appData.overrideLineGap;
\r
2290 if (tinyLayout != oldTinyLayout) {
\r
2291 long style = GetWindowLongPtr(hwndMain, GWL_STYLE);
\r
2293 style &= ~WS_SYSMENU;
\r
2294 InsertMenu(hmenu, IDM_Exit, MF_BYCOMMAND, IDM_Minimize,
\r
2295 "&Minimize\tCtrl+F4");
\r
2297 style |= WS_SYSMENU;
\r
2298 RemoveMenu(hmenu, IDM_Minimize, MF_BYCOMMAND);
\r
2300 SetWindowLongPtr(hwndMain, GWL_STYLE, style);
\r
2302 for (i=0; menuBarText[tinyLayout][i]; i++) {
\r
2303 ModifyMenu(hmenu, i, MF_STRING|MF_BYPOSITION|MF_POPUP,
\r
2304 (UINT)GetSubMenu(hmenu, i), T_(menuBarText[tinyLayout][i]));
\r
2306 DrawMenuBar(hwndMain);
\r
2309 boardWidth = BoardWidth(boardSize, BOARD_WIDTH) + 2*border;
\r
2310 boardHeight = BoardWidth(boardSize, BOARD_HEIGHT) + 2*border;
\r
2312 /* Get text area sizes */
\r
2313 hdc = GetDC(hwndMain);
\r
2314 if (appData.clockMode) {
\r
2315 snprintf(buf, MSG_SIZ, _("White: %s"), TimeString(23*60*60*1000L));
\r
2317 snprintf(buf, MSG_SIZ, _("White"));
\r
2319 oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);
\r
2320 GetTextExtentPoint(hdc, buf, strlen(buf), &clockSize);
\r
2321 SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);
\r
2322 str = _("We only care about the height here");
\r
2323 GetTextExtentPoint(hdc, str, strlen(str), &messageSize);
\r
2324 SelectObject(hdc, oldFont);
\r
2325 ReleaseDC(hwndMain, hdc);
\r
2327 /* Compute where everything goes */
\r
2328 if((first.programLogo || second.programLogo) && !tinyLayout) {
\r
2329 /* [HGM] logo: if either logo is on, reserve space for it */
\r
2330 logoHeight = 2*clockSize.cy;
\r
2331 leftLogoRect.left = OUTER_MARGIN;
\r
2332 leftLogoRect.right = leftLogoRect.left + 4*clockSize.cy;
\r
2333 leftLogoRect.top = OUTER_MARGIN;
\r
2334 leftLogoRect.bottom = OUTER_MARGIN + logoHeight;
\r
2336 rightLogoRect.right = OUTER_MARGIN + boardWidth;
\r
2337 rightLogoRect.left = rightLogoRect.right - 4*clockSize.cy;
\r
2338 rightLogoRect.top = OUTER_MARGIN;
\r
2339 rightLogoRect.bottom = OUTER_MARGIN + logoHeight;
\r
2342 whiteRect.left = leftLogoRect.right;
\r
2343 whiteRect.right = OUTER_MARGIN + boardWidth/2 - INNER_MARGIN/2;
\r
2344 whiteRect.top = OUTER_MARGIN;
\r
2345 whiteRect.bottom = whiteRect.top + logoHeight;
\r
2347 blackRect.right = rightLogoRect.left;
\r
2348 blackRect.left = whiteRect.right + INNER_MARGIN;
\r
2349 blackRect.top = whiteRect.top;
\r
2350 blackRect.bottom = whiteRect.bottom;
\r
2352 whiteRect.left = OUTER_MARGIN;
\r
2353 whiteRect.right = whiteRect.left + boardWidth/2 - INNER_MARGIN/2;
\r
2354 whiteRect.top = OUTER_MARGIN;
\r
2355 whiteRect.bottom = whiteRect.top + clockSize.cy;
\r
2357 blackRect.left = whiteRect.right + INNER_MARGIN;
\r
2358 blackRect.right = blackRect.left + boardWidth/2 - 1;
\r
2359 blackRect.top = whiteRect.top;
\r
2360 blackRect.bottom = whiteRect.bottom;
\r
2362 logoHeight = 0; // [HGM] logo: suppress logo after change to tiny layout!
\r
2365 messageRect.left = OUTER_MARGIN + MESSAGE_LINE_LEFTMARGIN;
\r
2366 if (appData.showButtonBar) {
\r
2367 messageRect.right = OUTER_MARGIN + boardWidth // [HGM] logo: expressed independent of clock placement
\r
2368 - N_BUTTONS*BUTTON_WIDTH - MESSAGE_LINE_LEFTMARGIN;
\r
2370 messageRect.right = OUTER_MARGIN + boardWidth;
\r
2372 messageRect.top = whiteRect.bottom + INNER_MARGIN;
\r
2373 messageRect.bottom = messageRect.top + messageSize.cy;
\r
2375 boardRect.left = OUTER_MARGIN;
\r
2376 boardRect.right = boardRect.left + boardWidth;
\r
2377 boardRect.top = messageRect.bottom + INNER_MARGIN;
\r
2378 boardRect.bottom = boardRect.top + boardHeight;
\r
2380 sizeInfo[boardSize].cliWidth = boardRect.right + OUTER_MARGIN;
\r
2381 sizeInfo[boardSize].cliHeight = boardRect.bottom + OUTER_MARGIN;
\r
2382 oldTinyLayout = tinyLayout;
\r
2383 winW = 2 * GetSystemMetrics(SM_CXFRAME) + boardRect.right + OUTER_MARGIN;
\r
2384 winH = 2 * GetSystemMetrics(SM_CYFRAME) + GetSystemMetrics(SM_CYMENU) +
\r
2385 GetSystemMetrics(SM_CYCAPTION) + boardRect.bottom + OUTER_MARGIN;
\r
2386 winW *= 1 + twoBoards;
\r
2387 if(suppressVisibleEffects) return; // [HGM] when called for filling sizeInfo only
\r
2388 wpMain.width = winW; // [HGM] placement: set through temporary which can used by initial sizing choice
\r
2389 wpMain.height = winH; // without disturbing window attachments
\r
2390 GetWindowRect(hwndMain, &wrect);
\r
2391 SetWindowPos(hwndMain, NULL, 0, 0, wpMain.width, wpMain.height,
\r
2392 SWP_NOCOPYBITS|SWP_NOZORDER|SWP_NOMOVE);
\r
2394 // [HGM] placement: let attached windows follow size change.
\r
2395 ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, moveHistoryDialog, &wpMoveHistory );
\r
2396 ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, evalGraphDialog, &wpEvalGraph );
\r
2397 ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, engineOutputDialog, &wpEngineOutput );
\r
2398 ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, gameListDialog, &wpGameList );
\r
2399 ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, hwndConsole, &wpConsole );
\r
2401 /* compensate if menu bar wrapped */
\r
2402 GetClientRect(hwndMain, &crect);
\r
2403 offby = boardRect.bottom + OUTER_MARGIN - crect.bottom;
\r
2404 wpMain.height += offby;
\r
2406 case WMSZ_TOPLEFT:
\r
2407 SetWindowPos(hwndMain, NULL,
\r
2408 wrect.right - wpMain.width, wrect.bottom - wpMain.height,
\r
2409 wpMain.width, wpMain.height, SWP_NOCOPYBITS|SWP_NOZORDER);
\r
2412 case WMSZ_TOPRIGHT:
\r
2414 SetWindowPos(hwndMain, NULL,
\r
2415 wrect.left, wrect.bottom - wpMain.height,
\r
2416 wpMain.width, wpMain.height, SWP_NOCOPYBITS|SWP_NOZORDER);
\r
2419 case WMSZ_BOTTOMLEFT:
\r
2421 SetWindowPos(hwndMain, NULL,
\r
2422 wrect.right - wpMain.width, wrect.top,
\r
2423 wpMain.width, wpMain.height, SWP_NOCOPYBITS|SWP_NOZORDER);
\r
2426 case WMSZ_BOTTOMRIGHT:
\r
2430 SetWindowPos(hwndMain, NULL, 0, 0, wpMain.width, wpMain.height,
\r
2431 SWP_NOCOPYBITS|SWP_NOZORDER|SWP_NOMOVE);
\r
2436 for (i = 0; i < N_BUTTONS; i++) {
\r
2437 if (buttonDesc[i].hwnd != NULL) {
\r
2438 DestroyWindow(buttonDesc[i].hwnd);
\r
2439 buttonDesc[i].hwnd = NULL;
\r
2441 if (appData.showButtonBar) {
\r
2442 buttonDesc[i].hwnd =
\r
2443 CreateWindow("BUTTON", buttonDesc[i].label,
\r
2444 WS_VISIBLE | WS_CHILD | BS_PUSHBUTTON,
\r
2445 boardRect.right - BUTTON_WIDTH*(N_BUTTONS-i),
\r
2446 messageRect.top, BUTTON_WIDTH, messageSize.cy, hwndMain,
\r
2447 (HMENU) buttonDesc[i].id,
\r
2448 (HINSTANCE) GetWindowLongPtr(hwndMain, GWLP_HINSTANCE), NULL);
\r
2450 SendMessage(buttonDesc[i].hwnd, WM_SETFONT,
\r
2451 (WPARAM)font[boardSize][MESSAGE_FONT]->hf,
\r
2452 MAKELPARAM(FALSE, 0));
\r
2454 if (buttonDesc[i].id == IDM_Pause)
\r
2455 hwndPause = buttonDesc[i].hwnd;
\r
2456 buttonDesc[i].wndproc = (WNDPROC)
\r
2457 SetWindowLongPtr(buttonDesc[i].hwnd, GWLP_WNDPROC, (LONG_PTR) ButtonProc);
\r
2460 if (gridPen != NULL) DeleteObject(gridPen);
\r
2461 if (highlightPen != NULL) DeleteObject(highlightPen);
\r
2462 if (premovePen != NULL) DeleteObject(premovePen);
\r
2463 if (lineGap != 0) {
\r
2464 logbrush.lbStyle = BS_SOLID;
\r
2465 logbrush.lbColor = RGB(0, 0, 0); /* grid pen color = black */
\r
2467 ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,
\r
2468 lineGap, &logbrush, 0, NULL);
\r
2469 logbrush.lbColor = highlightSquareColor;
\r
2471 ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,
\r
2472 lineGap, &logbrush, 0, NULL);
\r
2474 logbrush.lbColor = premoveHighlightColor;
\r
2476 ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,
\r
2477 lineGap, &logbrush, 0, NULL);
\r
2479 /* [HGM] Loop had to be split in part for vert. and hor. lines */
\r
2480 for (i = 0; i < BOARD_HEIGHT + 1; i++) {
\r
2481 gridEndpoints[i*2].x = boardRect.left + lineGap / 2 + border;
\r
2482 gridEndpoints[i*2].y = gridEndpoints[i*2 + 1].y =
\r
2483 boardRect.top + lineGap / 2 + (i * (squareSize + lineGap)) + border;
\r
2484 gridEndpoints[i*2 + 1].x = boardRect.left + lineGap / 2 +
\r
2485 BOARD_WIDTH * (squareSize + lineGap) + border;
\r
2486 gridVertexCounts[i*2] = gridVertexCounts[i*2 + 1] = 2;
\r
2488 for (i = 0; i < BOARD_WIDTH + 1; i++) {
\r
2489 gridEndpoints[i*2 + BOARD_HEIGHT*2 + 2].y = boardRect.top + lineGap / 2 + border;
\r
2490 gridEndpoints[i*2 + BOARD_HEIGHT*2 + 2].x =
\r
2491 gridEndpoints[i*2 + 1 + BOARD_HEIGHT*2 + 2].x = boardRect.left +
\r
2492 lineGap / 2 + (i * (squareSize + lineGap)) + border;
\r
2493 gridEndpoints[i*2 + 1 + BOARD_HEIGHT*2 + 2].y =
\r
2494 boardRect.top + BOARD_HEIGHT * (squareSize + lineGap) + border;
\r
2495 gridVertexCounts[i*2] = gridVertexCounts[i*2 + 1] = 2;
\r
2499 /* [HGM] Licensing requirement */
\r
2501 if(gameInfo.variant == VariantGothic) GothicPopUp( GOTHIC, VariantGothic); else
\r
2504 if(gameInfo.variant == VariantFalcon) GothicPopUp( FALCON, VariantFalcon); else
\r
2506 GothicPopUp( "", VariantNormal);
\r
2509 /* if (boardSize == oldBoardSize) return; [HGM] variant might have changed */
\r
2511 /* Load piece bitmaps for this board size */
\r
2512 for (i=0; i<=2; i++) {
\r
2513 for (piece = WhitePawn;
\r
2514 (int) piece < (int) BlackPawn;
\r
2515 piece = (ChessSquare) ((int) piece + 1)) {
\r
2516 if (pieceBitmap[i][piece] != NULL)
\r
2517 DeleteObject(pieceBitmap[i][piece]);
\r
2521 fontBitmapSquareSize = 0; /* [HGM] render: make sure pieces will be recreated, as we might need others now */
\r
2522 // Orthodox Chess pieces
\r
2523 pieceBitmap[0][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "s");
\r
2524 pieceBitmap[0][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "s");
\r
2525 pieceBitmap[0][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "s");
\r
2526 pieceBitmap[0][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "s");
\r
2527 pieceBitmap[0][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "s");
\r
2528 pieceBitmap[1][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "o");
\r
2529 pieceBitmap[1][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "o");
\r
2530 pieceBitmap[1][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "o");
\r
2531 pieceBitmap[1][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "o");
\r
2532 pieceBitmap[1][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "o");
\r
2533 pieceBitmap[2][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "w");
\r
2534 pieceBitmap[2][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "w");
\r
2535 pieceBitmap[2][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "w");
\r
2536 pieceBitmap[2][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "w");
\r
2537 pieceBitmap[2][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "w");
\r
2538 if( gameInfo.variant == VariantShogi && squareSize <= 72 && squareSize >= 33) {
\r
2539 // in Shogi, Hijack the unused Queen for Lance
\r
2540 pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "s");
\r
2541 pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "o");
\r
2542 pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "w");
\r
2544 pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "s");
\r
2545 pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "o");
\r
2546 pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "w");
\r
2549 if(squareSize <= 72 && squareSize >= 33) {
\r
2550 /* A & C are available in most sizes now */
\r
2551 if(squareSize != 49 && squareSize != 72 && squareSize != 33) { // Vortex-like
\r
2552 pieceBitmap[0][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "s");
\r
2553 pieceBitmap[1][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "o");
\r
2554 pieceBitmap[2][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "w");
\r
2555 pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "s");
\r
2556 pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "o");
\r
2557 pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "w");
\r
2558 pieceBitmap[0][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "s");
\r
2559 pieceBitmap[1][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "o");
\r
2560 pieceBitmap[2][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "w");
\r
2561 pieceBitmap[0][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "s");
\r
2562 pieceBitmap[1][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "o");
\r
2563 pieceBitmap[2][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "w");
\r
2564 } else { // Smirf-like
\r
2565 if(gameInfo.variant == VariantSChess) {
\r
2566 pieceBitmap[0][WhiteAngel] = DoLoadBitmap(hInst, "v", squareSize, "s");
\r
2567 pieceBitmap[1][WhiteAngel] = DoLoadBitmap(hInst, "v", squareSize, "o");
\r
2568 pieceBitmap[2][WhiteAngel] = DoLoadBitmap(hInst, "v", squareSize, "w");
\r
2570 pieceBitmap[0][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "s");
\r
2571 pieceBitmap[1][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "o");
\r
2572 pieceBitmap[2][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "w");
\r
2575 if(gameInfo.variant == VariantGothic) { // Vortex-like
\r
2576 pieceBitmap[0][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "s");
\r
2577 pieceBitmap[1][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "o");
\r
2578 pieceBitmap[2][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "w");
\r
2579 } else if(gameInfo.variant == VariantSChess && (squareSize == 49 || squareSize == 72)) {
\r
2580 pieceBitmap[0][WhiteMarshall] = DoLoadBitmap(hInst, "e", squareSize, "s");
\r
2581 pieceBitmap[1][WhiteMarshall] = DoLoadBitmap(hInst, "e", squareSize, "o");
\r
2582 pieceBitmap[2][WhiteMarshall] = DoLoadBitmap(hInst, "e", squareSize, "w");
\r
2583 } else { // WinBoard standard
\r
2584 pieceBitmap[0][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "s");
\r
2585 pieceBitmap[1][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "o");
\r
2586 pieceBitmap[2][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "w");
\r
2591 if(squareSize==72 || squareSize==49 || squareSize==33) { /* experiment with some home-made bitmaps */
\r
2592 pieceBitmap[0][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "s");
\r
2593 pieceBitmap[1][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "o");
\r
2594 pieceBitmap[2][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "w");
\r
2595 pieceBitmap[0][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "s");
\r
2596 pieceBitmap[1][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "o");
\r
2597 pieceBitmap[2][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "w");
\r
2598 pieceBitmap[0][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "s");
\r
2599 pieceBitmap[1][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "o");
\r
2600 pieceBitmap[2][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "w");
\r
2601 pieceBitmap[0][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "s");
\r
2602 pieceBitmap[1][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "o");
\r
2603 pieceBitmap[2][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "w");
\r
2604 pieceBitmap[0][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "s");
\r
2605 pieceBitmap[1][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "o");
\r
2606 pieceBitmap[2][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "w");
\r
2607 pieceBitmap[0][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "s");
\r
2608 pieceBitmap[1][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "o");
\r
2609 pieceBitmap[2][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "w");
\r
2610 pieceBitmap[0][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "s");
\r
2611 pieceBitmap[1][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "o");
\r
2612 pieceBitmap[2][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "w");
\r
2613 pieceBitmap[0][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "s");
\r
2614 pieceBitmap[1][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "o");
\r
2615 pieceBitmap[2][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "w");
\r
2616 pieceBitmap[0][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "s");
\r
2617 pieceBitmap[1][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "o");
\r
2618 pieceBitmap[2][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "w");
\r
2619 pieceBitmap[0][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "s");
\r
2620 pieceBitmap[1][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "o");
\r
2621 pieceBitmap[2][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "w");
\r
2623 if(gameInfo.variant == VariantShogi) { /* promoted Gold represemtations */
\r
2624 pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "wp", squareSize, "s");
\r
2625 pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "wp", squareSize, "o");
\r
2626 pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "w", squareSize, "w");
\r
2627 pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "wn", squareSize, "s");
\r
2628 pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "wn", squareSize, "o");
\r
2629 pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "w", squareSize, "w");
\r
2630 pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "ws", squareSize, "s");
\r
2631 pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "ws", squareSize, "o");
\r
2632 pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "w", squareSize, "w");
\r
2633 pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "wl", squareSize, "s");
\r
2634 pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "wl", squareSize, "o");
\r
2635 pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "w", squareSize, "w");
\r
2637 pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "s");
\r
2638 pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "o");
\r
2639 pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "w");
\r
2640 pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "s");
\r
2641 pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "o");
\r
2642 pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "w");
\r
2643 pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "s");
\r
2644 pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "o");
\r
2645 pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "w");
\r
2646 pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "s");
\r
2647 pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "o");
\r
2648 pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "w");
\r
2651 } else { /* other size, no special bitmaps available. Use smaller symbols */
\r
2652 if((int)boardSize < 2) minorSize = sizeInfo[0].squareSize;
\r
2653 else minorSize = sizeInfo[(int)boardSize - 2].squareSize;
\r
2654 pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "s");
\r
2655 pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "o");
\r
2656 pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "w");
\r
2657 pieceBitmap[0][WhiteCardinal] = DoLoadBitmap(hInst, "b", minorSize, "s");
\r
2658 pieceBitmap[1][WhiteCardinal] = DoLoadBitmap(hInst, "b", minorSize, "o");
\r
2659 pieceBitmap[2][WhiteCardinal] = DoLoadBitmap(hInst, "b", minorSize, "w");
\r
2660 pieceBitmap[0][WhiteDragon] = DoLoadBitmap(hInst, "r", minorSize, "s");
\r
2661 pieceBitmap[1][WhiteDragon] = DoLoadBitmap(hInst, "r", minorSize, "o");
\r
2662 pieceBitmap[2][WhiteDragon] = DoLoadBitmap(hInst, "r", minorSize, "w");
\r
2663 pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "s");
\r
2664 pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "o");
\r
2665 pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "w");
\r
2669 if(gameInfo.variant == VariantShogi && squareSize == 58)
\r
2670 /* special Shogi support in this size */
\r
2671 { for (i=0; i<=2; i++) { /* replace all bitmaps */
\r
2672 for (piece = WhitePawn;
\r
2673 (int) piece < (int) BlackPawn;
\r
2674 piece = (ChessSquare) ((int) piece + 1)) {
\r
2675 if (pieceBitmap[i][piece] != NULL)
\r
2676 DeleteObject(pieceBitmap[i][piece]);
\r
2679 pieceBitmap[0][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "o");
\r
2680 pieceBitmap[0][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "o");
\r
2681 pieceBitmap[0][WhiteBishop] = DoLoadBitmap(hInst, "sb", squareSize, "o");
\r
2682 pieceBitmap[0][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "o");
\r
2683 pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "o");
\r
2684 pieceBitmap[0][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "o");
\r
2685 pieceBitmap[0][WhiteFerz] = DoLoadBitmap(hInst, "sf", squareSize, "o");
\r
2686 pieceBitmap[0][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "o");
\r
2687 pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "su", squareSize, "o");
\r
2688 pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "sh", squareSize, "o");
\r
2689 pieceBitmap[0][WhiteCardinal] = DoLoadBitmap(hInst, "sa", squareSize, "o");
\r
2690 pieceBitmap[0][WhiteDragon] = DoLoadBitmap(hInst, "sc", squareSize, "o");
\r
2691 pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "sg", squareSize, "o");
\r
2692 pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "ss", squareSize, "o");
\r
2693 pieceBitmap[1][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "o");
\r
2694 pieceBitmap[1][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "o");
\r
2695 pieceBitmap[1][WhiteBishop] = DoLoadBitmap(hInst, "sb", squareSize, "o");
\r
2696 pieceBitmap[1][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "o");
\r
2697 pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "o");
\r
2698 pieceBitmap[1][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "o");
\r
2699 pieceBitmap[1][WhiteFerz] = DoLoadBitmap(hInst, "sf", squareSize, "o");
\r
2700 pieceBitmap[1][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "o");
\r
2701 pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "su", squareSize, "o");
\r
2702 pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "sh", squareSize, "o");
\r
2703 pieceBitmap[1][WhiteCardinal] = DoLoadBitmap(hInst, "sa", squareSize, "o");
\r
2704 pieceBitmap[1][WhiteDragon] = DoLoadBitmap(hInst, "sc", squareSize, "o");
\r
2705 pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "sg", squareSize, "o");
\r
2706 pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "ss", squareSize, "o");
\r
2707 pieceBitmap[2][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "w");
\r
2708 pieceBitmap[2][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "w");
\r
2709 pieceBitmap[2][WhiteBishop] = DoLoadBitmap(hInst, "sr", squareSize, "w");
\r
2710 pieceBitmap[2][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "w");
\r
2711 pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "w");
\r
2712 pieceBitmap[2][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "w");
\r
2713 pieceBitmap[2][WhiteFerz] = DoLoadBitmap(hInst, "sw", squareSize, "w");
\r
2714 pieceBitmap[2][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "w");
\r
2715 pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "sp", squareSize, "w");
\r
2716 pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "sn", squareSize, "w");
\r
2717 pieceBitmap[2][WhiteCardinal] = DoLoadBitmap(hInst, "sr", squareSize, "w");
\r
2718 pieceBitmap[2][WhiteDragon] = DoLoadBitmap(hInst, "sr", squareSize, "w");
\r
2719 pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "sl", squareSize, "w");
\r
2720 pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "sw", squareSize, "w");
\r
2726 PieceBitmap(ChessSquare p, int kind)
\r
2728 if ((int) p >= (int) BlackPawn)
\r
2729 p = (ChessSquare) ((int) p - (int) BlackPawn + (int) WhitePawn);
\r
2731 return pieceBitmap[kind][(int) p];
\r
2734 /***************************************************************/
\r
2736 #define MIN(a,b) (((a) < (b)) ? (a) : (b))
\r
2737 #define MAX(a,b) (((a) > (b)) ? (a) : (b))
\r
2739 #define MIN3(a,b,c) (((a) < (b) && (a) < (c)) ? (a) : (((b) < (a) && (b) < (c)) ? (b) : (c)))
\r
2740 #define MAX3(a,b,c) (((a) > (b) && (a) > (c)) ? (a) : (((b) > (a) && (b) > (c)) ? (b) : (c)))
\r
2744 SquareToPos(int row, int column, int * x, int * y)
\r
2747 *x = boardRect.left + lineGap + ((BOARD_WIDTH-1)-column) * (squareSize + lineGap) + border;
\r
2748 *y = boardRect.top + lineGap + row * (squareSize + lineGap) + border;
\r
2750 *x = boardRect.left + lineGap + column * (squareSize + lineGap) + border;
\r
2751 *y = boardRect.top + lineGap + ((BOARD_HEIGHT-1)-row) * (squareSize + lineGap) + border;
\r
2756 DrawCoordsOnDC(HDC hdc)
\r
2758 static char files[] = "0123456789012345678901221098765432109876543210";
\r
2759 static char ranks[] = "wvutsrqponmlkjihgfedcbaabcdefghijklmnopqrstuvw";
\r
2760 char str[2] = { NULLCHAR, NULLCHAR };
\r
2761 int oldMode, oldAlign, x, y, start, i;
\r
2765 if (!appData.showCoords)
\r
2768 start = flipView ? 1-(ONE!='1') : 45+(ONE!='1')-BOARD_HEIGHT;
\r
2770 oldBrush = SelectObject(hdc, GetStockObject(BLACK_BRUSH));
\r
2771 oldMode = SetBkMode(hdc, (appData.monoMode ? OPAQUE : TRANSPARENT));
\r
2772 oldAlign = GetTextAlign(hdc);
\r
2773 oldFont = SelectObject(hdc, font[boardSize][COORD_FONT]->hf);
\r
2775 y = boardRect.top + lineGap;
\r
2776 x = boardRect.left + lineGap + gameInfo.holdingsWidth*(squareSize + lineGap);
\r
2779 SetTextAlign(hdc, TA_RIGHT|TA_TOP);
\r
2780 x += border - lineGap - 4; y += squareSize - 6;
\r
2782 SetTextAlign(hdc, TA_LEFT|TA_TOP);
\r
2783 for (i = 0; i < BOARD_HEIGHT; i++) {
\r
2784 str[0] = files[start + i];
\r
2785 ExtTextOut(hdc, x + 2 - (border ? gameInfo.holdingsWidth * (squareSize + lineGap) : 0), y + 1, 0, NULL, str, 1, NULL);
\r
2786 y += squareSize + lineGap;
\r
2789 start = flipView ? 23-(BOARD_RGHT-BOARD_LEFT) : 23;
\r
2792 SetTextAlign(hdc, TA_LEFT|TA_TOP);
\r
2793 x += -border + 4; y += border - squareSize + 6;
\r
2795 SetTextAlign(hdc, TA_RIGHT|TA_BOTTOM);
\r
2796 for (i = 0; i < BOARD_RGHT - BOARD_LEFT; i++) {
\r
2797 str[0] = ranks[start + i];
\r
2798 ExtTextOut(hdc, x + squareSize - 2, y - 1, 0, NULL, str, 1, NULL);
\r
2799 x += squareSize + lineGap;
\r
2802 SelectObject(hdc, oldBrush);
\r
2803 SetBkMode(hdc, oldMode);
\r
2804 SetTextAlign(hdc, oldAlign);
\r
2805 SelectObject(hdc, oldFont);
\r
2809 DrawGridOnDC(HDC hdc)
\r
2813 if (lineGap != 0) {
\r
2814 oldPen = SelectObject(hdc, gridPen);
\r
2815 PolyPolyline(hdc, gridEndpoints, gridVertexCounts, BOARD_WIDTH+BOARD_HEIGHT + 2);
\r
2816 SelectObject(hdc, oldPen);
\r
2820 #define HIGHLIGHT_PEN 0
\r
2821 #define PREMOVE_PEN 1
\r
2824 DrawHighlightOnDC(HDC hdc, BOOLEAN on, int x, int y, int pen)
\r
2827 HPEN oldPen, hPen;
\r
2828 if (lineGap == 0) return;
\r
2830 x1 = boardRect.left +
\r
2831 lineGap/2 + ((BOARD_WIDTH-1)-x) * (squareSize + lineGap) + border;
\r
2832 y1 = boardRect.top +
\r
2833 lineGap/2 + y * (squareSize + lineGap) + border;
\r
2835 x1 = boardRect.left +
\r
2836 lineGap/2 + x * (squareSize + lineGap) + border;
\r
2837 y1 = boardRect.top +
\r
2838 lineGap/2 + ((BOARD_HEIGHT-1)-y) * (squareSize + lineGap) + border;
\r
2840 hPen = pen ? premovePen : highlightPen;
\r
2841 oldPen = SelectObject(hdc, on ? hPen : gridPen);
\r
2842 MoveToEx(hdc, x1, y1, NULL);
\r
2843 LineTo(hdc, x1 + squareSize + lineGap, y1);
\r
2844 LineTo(hdc, x1 + squareSize + lineGap, y1 + squareSize + lineGap);
\r
2845 LineTo(hdc, x1, y1 + squareSize + lineGap);
\r
2846 LineTo(hdc, x1, y1);
\r
2847 SelectObject(hdc, oldPen);
\r
2851 DrawHighlightsOnDC(HDC hdc, HighlightInfo *h, int pen)
\r
2854 for (i=0; i<2; i++) {
\r
2855 if (h->sq[i].x >= 0 && h->sq[i].y >= 0)
\r
2856 DrawHighlightOnDC(hdc, TRUE,
\r
2857 h->sq[i].x, h->sq[i].y,
\r
2862 /* Note: sqcolor is used only in monoMode */
\r
2863 /* Note that this code is largely duplicated in woptions.c,
\r
2864 function DrawSampleSquare, so that needs to be updated too */
\r
2866 DrawPieceOnDC(HDC hdc, ChessSquare piece, int color, int sqcolor, int x, int y, HDC tmphdc)
\r
2868 HBITMAP oldBitmap;
\r
2872 if (appData.blindfold) return;
\r
2874 /* [AS] Use font-based pieces if needed */
\r
2875 if( fontBitmapSquareSize >= 0 && (squareSize > 32 || gameInfo.variant >= VariantShogi)) {
\r
2876 /* Create piece bitmaps, or do nothing if piece set is up to date */
\r
2877 CreatePiecesFromFont();
\r
2879 if( fontBitmapSquareSize == squareSize ) {
\r
2880 int index = TranslatePieceToFontPiece(piece);
\r
2882 SelectObject( tmphdc, hPieceMask[ index ] );
\r
2884 if(appData.upsideDown ? color==flipView : (flipView && gameInfo.variant == VariantShogi))
\r
2885 StretchBlt(hdc, x+squareSize, y+squareSize, -squareSize, -squareSize, tmphdc, 0, 0, squareSize, squareSize, SRCAND);
\r
2889 squareSize, squareSize,
\r
2894 SelectObject( tmphdc, hPieceFace[ index ] );
\r
2896 if(appData.upsideDown ? color==flipView : (flipView && gameInfo.variant == VariantShogi))
\r
2897 StretchBlt(hdc, x+squareSize, y+squareSize, -squareSize, -squareSize, tmphdc, 0, 0, squareSize, squareSize, SRCPAINT);
\r
2901 squareSize, squareSize,
\r
2910 if (appData.monoMode) {
\r
2911 SelectObject(tmphdc, PieceBitmap(piece,
\r
2912 color == sqcolor ? OUTLINE_PIECE : SOLID_PIECE));
\r
2913 BitBlt(hdc, x, y, squareSize, squareSize, tmphdc, 0, 0,
\r
2914 sqcolor ? SRCCOPY : NOTSRCCOPY);
\r
2916 HBRUSH xBrush = whitePieceBrush;
\r
2917 tmpSize = squareSize;
\r
2918 if(appData.pieceDirectory[0]) xBrush = GetStockObject(WHITE_BRUSH);
\r
2920 ((piece >= (int)WhiteNightrider && piece <= WhiteGrasshopper) ||
\r
2921 (piece >= (int)BlackNightrider && piece <= BlackGrasshopper)) ) {
\r
2922 /* [HGM] no bitmap available for promoted pieces in Crazyhouse */
\r
2923 /* Bitmaps of smaller size are substituted, but we have to align them */
\r
2924 x += (squareSize - minorSize)>>1;
\r
2925 y += squareSize - minorSize - 2;
\r
2926 tmpSize = minorSize;
\r
2928 if (color || appData.allWhite ) {
\r
2929 oldBitmap = SelectObject(tmphdc, PieceBitmap(piece, WHITE_PIECE));
\r
2931 oldBrush = SelectObject(hdc, xBrush);
\r
2932 else oldBrush = SelectObject(hdc, blackPieceBrush);
\r
2933 if(appData.upsideDown && color==flipView)
\r
2934 StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, 0x00B8074A);
\r
2936 BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, 0x00B8074A);
\r
2937 /* Use black for outline of white pieces */
\r
2938 SelectObject(tmphdc, PieceBitmap(piece, OUTLINE_PIECE));
\r
2939 if(appData.upsideDown && color==flipView)
\r
2940 StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, SRCAND);
\r
2942 BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, SRCAND);
\r
2943 } else if(appData.pieceDirectory[0]) {
\r
2944 oldBitmap = SelectObject(tmphdc, PieceBitmap(piece, WHITE_PIECE));
\r
2945 oldBrush = SelectObject(hdc, xBrush);
\r
2946 if(appData.upsideDown && color==flipView)
\r
2947 StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, 0x00B8074A);
\r
2949 BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, 0x00B8074A);
\r
2950 SelectObject(tmphdc, PieceBitmap(piece, SOLID_PIECE));
\r
2951 if(appData.upsideDown && color==flipView)
\r
2952 StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, SRCAND);
\r
2954 BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, SRCAND);
\r
2956 /* Use square color for details of black pieces */
\r
2957 oldBitmap = SelectObject(tmphdc, PieceBitmap(piece, SOLID_PIECE));
\r
2958 oldBrush = SelectObject(hdc, blackPieceBrush);
\r
2959 if(appData.upsideDown && !flipView)
\r
2960 StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, 0x00B8074A);
\r
2962 BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, 0x00B8074A);
\r
2964 SelectObject(hdc, oldBrush);
\r
2965 SelectObject(tmphdc, oldBitmap);
\r
2969 /* [AS] Compute a drawing mode for a square, based on specified settings (see DrawTile) */
\r
2970 int GetBackTextureMode( int algo )
\r
2972 int result = BACK_TEXTURE_MODE_DISABLED;
\r
2976 case BACK_TEXTURE_MODE_PLAIN:
\r
2977 result = 1; /* Always use identity map */
\r
2979 case BACK_TEXTURE_MODE_FULL_RANDOM:
\r
2980 result = 1 + (myrandom() % 3); /* Pick a transformation at random */
\r
2988 [AS] Compute and save texture drawing info, otherwise we may not be able
\r
2989 to handle redraws cleanly (as random numbers would always be different).
\r
2991 VOID RebuildTextureSquareInfo()
\r
3001 ZeroMemory( &backTextureSquareInfo, sizeof(backTextureSquareInfo) );
\r
3003 if( liteBackTexture != NULL ) {
\r
3004 if( GetObject( liteBackTexture, sizeof(bi), &bi ) > 0 ) {
\r
3005 lite_w = bi.bmWidth;
\r
3006 lite_h = bi.bmHeight;
\r
3010 if( darkBackTexture != NULL ) {
\r
3011 if( GetObject( darkBackTexture, sizeof(bi), &bi ) > 0 ) {
\r
3012 dark_w = bi.bmWidth;
\r
3013 dark_h = bi.bmHeight;
\r
3017 for( row=0; row<BOARD_HEIGHT; row++ ) {
\r
3018 for( col=0; col<BOARD_WIDTH; col++ ) {
\r
3019 if( (col + row) & 1 ) {
\r
3021 if( lite_w >= squareSize && lite_h >= squareSize ) {
\r
3022 if( lite_w >= squareSize*BOARD_WIDTH )
\r
3023 backTextureSquareInfo[row][col].x = (2*col+1)*lite_w/(2*BOARD_WIDTH) - squareSize/2; /* [HGM] cut out of center of virtual square */
\r
3025 backTextureSquareInfo[row][col].x = col * (lite_w - squareSize) / (BOARD_WIDTH-1); /* [HGM] divide by size-1 in stead of size! */
\r
3026 if( lite_h >= squareSize*BOARD_HEIGHT )
\r
3027 backTextureSquareInfo[row][col].y = (2*(BOARD_HEIGHT-row)-1)*lite_h/(2*BOARD_HEIGHT) - squareSize/2;
\r
3029 backTextureSquareInfo[row][col].y = (BOARD_HEIGHT-1-row) * (lite_h - squareSize) / (BOARD_HEIGHT-1);
\r
3030 backTextureSquareInfo[row][col].mode = GetBackTextureMode(liteBackTextureMode);
\r
3035 if( dark_w >= squareSize && dark_h >= squareSize ) {
\r
3036 if( dark_w >= squareSize*BOARD_WIDTH )
\r
3037 backTextureSquareInfo[row][col].x = (2*col+1) * dark_w / (2*BOARD_WIDTH) - squareSize/2;
\r
3039 backTextureSquareInfo[row][col].x = col * (dark_w - squareSize) / (BOARD_WIDTH-1);
\r
3040 if( dark_h >= squareSize*BOARD_HEIGHT )
\r
3041 backTextureSquareInfo[row][col].y = (2*(BOARD_HEIGHT-row)-1) * dark_h / (2*BOARD_HEIGHT) - squareSize/2;
\r
3043 backTextureSquareInfo[row][col].y = (BOARD_HEIGHT-1-row) * (dark_h - squareSize) / (BOARD_HEIGHT-1);
\r
3044 backTextureSquareInfo[row][col].mode = GetBackTextureMode(darkBackTextureMode);
\r
3051 /* [AS] Arrow highlighting support */
\r
3053 static double A_WIDTH = 5; /* Width of arrow body */
\r
3055 #define A_HEIGHT_FACTOR 6 /* Length of arrow "point", relative to body width */
\r
3056 #define A_WIDTH_FACTOR 3 /* Width of arrow "point", relative to body width */
\r
3058 static double Sqr( double x )
\r
3063 static int Round( double x )
\r
3065 return (int) (x + 0.5);
\r
3068 /* Draw an arrow between two points using current settings */
\r
3069 VOID DrawArrowBetweenPoints( HDC hdc, int s_x, int s_y, int d_x, int d_y )
\r
3072 double dx, dy, j, k, x, y;
\r
3074 if( d_x == s_x ) {
\r
3075 int h = (d_y > s_y) ? +A_WIDTH*A_HEIGHT_FACTOR : -A_WIDTH*A_HEIGHT_FACTOR;
\r
3077 arrow[0].x = s_x + A_WIDTH + 0.5;
\r
3080 arrow[1].x = s_x + A_WIDTH + 0.5;
\r
3081 arrow[1].y = d_y - h;
\r
3083 arrow[2].x = arrow[1].x + A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;
\r
3084 arrow[2].y = d_y - h;
\r
3089 arrow[5].x = arrow[1].x - 2*A_WIDTH + 0.5;
\r
3090 arrow[5].y = d_y - h;
\r
3092 arrow[4].x = arrow[5].x - A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;
\r
3093 arrow[4].y = d_y - h;
\r
3095 arrow[6].x = arrow[1].x - 2*A_WIDTH + 0.5;
\r
3098 else if( d_y == s_y ) {
\r
3099 int w = (d_x > s_x) ? +A_WIDTH*A_HEIGHT_FACTOR : -A_WIDTH*A_HEIGHT_FACTOR;
\r
3102 arrow[0].y = s_y + A_WIDTH + 0.5;
\r
3104 arrow[1].x = d_x - w;
\r
3105 arrow[1].y = s_y + A_WIDTH + 0.5;
\r
3107 arrow[2].x = d_x - w;
\r
3108 arrow[2].y = arrow[1].y + A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;
\r
3113 arrow[5].x = d_x - w;
\r
3114 arrow[5].y = arrow[1].y - 2*A_WIDTH + 0.5;
\r
3116 arrow[4].x = d_x - w;
\r
3117 arrow[4].y = arrow[5].y - A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;
\r
3120 arrow[6].y = arrow[1].y - 2*A_WIDTH + 0.5;
\r
3123 /* [AS] Needed a lot of paper for this! :-) */
\r
3124 dy = (double) (d_y - s_y) / (double) (d_x - s_x);
\r
3125 dx = (double) (s_x - d_x) / (double) (s_y - d_y);
\r
3127 j = sqrt( Sqr(A_WIDTH) / (1.0 + Sqr(dx)) );
\r
3129 k = sqrt( Sqr(A_WIDTH*A_HEIGHT_FACTOR) / (1.0 + Sqr(dy)) );
\r
3134 arrow[0].x = Round(x - j);
\r
3135 arrow[0].y = Round(y + j*dx);
\r
3137 arrow[1].x = Round(arrow[0].x + 2*j); // [HGM] prevent width to be affected by rounding twice
\r
3138 arrow[1].y = Round(arrow[0].y - 2*j*dx);
\r
3141 x = (double) d_x - k;
\r
3142 y = (double) d_y - k*dy;
\r
3145 x = (double) d_x + k;
\r
3146 y = (double) d_y + k*dy;
\r
3149 x = Round(x); y = Round(y); // [HGM] make sure width of shaft is rounded the same way on both ends
\r
3151 arrow[6].x = Round(x - j);
\r
3152 arrow[6].y = Round(y + j*dx);
\r
3154 arrow[2].x = Round(arrow[6].x + 2*j);
\r
3155 arrow[2].y = Round(arrow[6].y - 2*j*dx);
\r
3157 arrow[3].x = Round(arrow[2].x + j*(A_WIDTH_FACTOR-1));
\r
3158 arrow[3].y = Round(arrow[2].y - j*(A_WIDTH_FACTOR-1)*dx);
\r
3163 arrow[5].x = Round(arrow[6].x - j*(A_WIDTH_FACTOR-1));
\r
3164 arrow[5].y = Round(arrow[6].y + j*(A_WIDTH_FACTOR-1)*dx);
\r
3167 Polygon( hdc, arrow, 7 );
\r
3170 /* [AS] Draw an arrow between two squares */
\r
3171 VOID DrawArrowBetweenSquares( HDC hdc, int s_col, int s_row, int d_col, int d_row )
\r
3173 int s_x, s_y, d_x, d_y;
\r
3180 if( s_col == d_col && s_row == d_row ) {
\r
3184 /* Get source and destination points */
\r
3185 SquareToPos( s_row, s_col, &s_x, &s_y);
\r
3186 SquareToPos( d_row, d_col, &d_x, &d_y);
\r
3189 d_y += squareSize / 2 - squareSize / 4; // [HGM] round towards same centers on all sides!
\r
3191 else if( d_y < s_y ) {
\r
3192 d_y += squareSize / 2 + squareSize / 4;
\r
3195 d_y += squareSize / 2;
\r
3199 d_x += squareSize / 2 - squareSize / 4;
\r
3201 else if( d_x < s_x ) {
\r
3202 d_x += squareSize / 2 + squareSize / 4;
\r
3205 d_x += squareSize / 2;
\r
3208 s_x += squareSize / 2;
\r
3209 s_y += squareSize / 2;
\r
3211 /* Adjust width */
\r
3212 A_WIDTH = squareSize / 14.; //[HGM] make float
\r
3215 stLB.lbStyle = BS_SOLID;
\r
3216 stLB.lbColor = appData.highlightArrowColor;
\r
3219 hpen = CreatePen( PS_SOLID, 2, RGB(0x00,0x00,0x00) );
\r
3220 holdpen = SelectObject( hdc, hpen );
\r
3221 hbrush = CreateBrushIndirect( &stLB );
\r
3222 holdbrush = SelectObject( hdc, hbrush );
\r
3224 DrawArrowBetweenPoints( hdc, s_x, s_y, d_x, d_y );
\r
3226 SelectObject( hdc, holdpen );
\r
3227 SelectObject( hdc, holdbrush );
\r
3228 DeleteObject( hpen );
\r
3229 DeleteObject( hbrush );
\r
3232 BOOL HasHighlightInfo()
\r
3234 BOOL result = FALSE;
\r
3236 if( highlightInfo.sq[0].x >= 0 && highlightInfo.sq[0].y >= 0 &&
\r
3237 highlightInfo.sq[1].x >= 0 && highlightInfo.sq[1].y >= 0 )
\r
3245 BOOL IsDrawArrowEnabled()
\r
3247 BOOL result = FALSE;
\r
3249 if( appData.highlightMoveWithArrow && squareSize >= 32 ) {
\r
3256 VOID DrawArrowHighlight( HDC hdc )
\r
3258 if( IsDrawArrowEnabled() && HasHighlightInfo() ) {
\r
3259 DrawArrowBetweenSquares( hdc,
\r
3260 highlightInfo.sq[0].x, highlightInfo.sq[0].y,
\r
3261 highlightInfo.sq[1].x, highlightInfo.sq[1].y );
\r
3265 HRGN GetArrowHighlightClipRegion( HDC hdc )
\r
3267 HRGN result = NULL;
\r
3269 if( HasHighlightInfo() ) {
\r
3270 int x1, y1, x2, y2;
\r
3271 int sx, sy, dx, dy;
\r
3273 SquareToPos(highlightInfo.sq[0].y, highlightInfo.sq[0].x, &x1, &y1 );
\r
3274 SquareToPos(highlightInfo.sq[1].y, highlightInfo.sq[1].x, &x2, &y2 );
\r
3276 sx = MIN( x1, x2 );
\r
3277 sy = MIN( y1, y2 );
\r
3278 dx = MAX( x1, x2 ) + squareSize;
\r
3279 dy = MAX( y1, y2 ) + squareSize;
\r
3281 result = CreateRectRgn( sx, sy, dx, dy );
\r
3288 Warning: this function modifies the behavior of several other functions.
\r
3290 Basically, Winboard is optimized to avoid drawing the whole board if not strictly
\r
3291 needed. Unfortunately, the decision whether or not to perform a full or partial
\r
3292 repaint is scattered all over the place, which is not good for features such as
\r
3293 "arrow highlighting" that require a full repaint of the board.
\r
3295 So, I've tried to patch the code where I thought it made sense (e.g. after or during
\r
3296 user interaction, when speed is not so important) but especially to avoid errors
\r
3297 in the displayed graphics.
\r
3299 In such patched places, I always try refer to this function so there is a single
\r
3300 place to maintain knowledge.
\r
3302 To restore the original behavior, just return FALSE unconditionally.
\r
3304 BOOL IsFullRepaintPreferrable()
\r
3306 BOOL result = FALSE;
\r
3308 if( (appData.highlightLastMove || appData.highlightDragging) && IsDrawArrowEnabled() ) {
\r
3309 /* Arrow may appear on the board */
\r
3317 This function is called by DrawPosition to know whether a full repaint must
\r
3320 Only DrawPosition may directly call this function, which makes use of
\r
3321 some state information. Other function should call DrawPosition specifying
\r
3322 the repaint flag, and can use IsFullRepaintPreferrable if needed.
\r
3324 BOOL DrawPositionNeedsFullRepaint()
\r
3326 BOOL result = FALSE;
\r
3329 Probably a slightly better policy would be to trigger a full repaint
\r
3330 when animInfo.piece changes state (i.e. empty -> non-empty and viceversa),
\r
3331 but animation is fast enough that it's difficult to notice.
\r
3333 if( animInfo.piece == EmptySquare ) {
\r
3334 if( (appData.highlightLastMove || appData.highlightDragging) && IsDrawArrowEnabled() /*&& HasHighlightInfo()*/ ) {
\r
3342 static HBITMAP borderBitmap;
\r
3345 DrawBackgroundOnDC(HDC hdc)
\r
3351 static char oldBorder[MSG_SIZ];
\r
3352 int w = 600, h = 600;
\r
3354 if(strcmp(appData.border, oldBorder)) { // load new one when old one no longer valid
\r
3355 strncpy(oldBorder, appData.border, MSG_SIZ-1);
\r
3356 borderBitmap = LoadImage( 0, appData.border, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );
\r
3358 if(borderBitmap == NULL) { // loading failed, use white
\r
3359 FillRect( hdc, &boardRect, whitePieceBrush );
\r
3362 tmphdc = CreateCompatibleDC(hdc);
\r
3363 hbm = SelectObject(tmphdc, borderBitmap);
\r
3364 if( GetObject( borderBitmap, sizeof(bi), &bi ) > 0 ) {
\r
3368 StretchBlt(hdc, boardRect.left, boardRect.top, boardRect.right - boardRect.left,
\r
3369 boardRect.bottom - boardRect.top, tmphdc, 0, 0, w, h, SRCCOPY);
\r
3370 SelectObject(tmphdc, hbm);
\r
3375 DrawBoardOnDC(HDC hdc, Board board, HDC tmphdc)
\r
3377 int row, column, x, y, square_color, piece_color;
\r
3378 ChessSquare piece;
\r
3380 HDC texture_hdc = NULL;
\r
3382 /* [AS] Initialize background textures if needed */
\r
3383 if( liteBackTexture != NULL || darkBackTexture != NULL ) {
\r
3384 static int backTextureBoardSize; /* [HGM] boardsize: also new texture if board format changed */
\r
3385 if( backTextureSquareSize != squareSize
\r
3386 || backTextureBoardSize != BOARD_WIDTH+BOARD_FILES*BOARD_HEIGHT) {
\r
3387 backTextureBoardSize = BOARD_WIDTH+BOARD_FILES*BOARD_HEIGHT;
\r
3388 backTextureSquareSize = squareSize;
\r
3389 RebuildTextureSquareInfo();
\r
3392 texture_hdc = CreateCompatibleDC( hdc );
\r
3395 for (row = 0; row < BOARD_HEIGHT; row++) {
\r
3396 for (column = 0; column < BOARD_WIDTH; column++) {
\r
3398 SquareToPos(row, column, &x, &y);
\r
3400 piece = board[row][column];
\r
3402 square_color = ((column + row) % 2) == 1;
\r
3403 if( gameInfo.variant == VariantXiangqi ) {
\r
3404 square_color = !InPalace(row, column);
\r
3405 if(BOARD_HEIGHT&1) { if(row==BOARD_HEIGHT/2) square_color ^= 1; }
\r
3406 else if(row < BOARD_HEIGHT/2) square_color ^= 1;
\r
3408 piece_color = (int) piece < (int) BlackPawn;
\r
3411 /* [HGM] holdings file: light square or black */
\r
3412 if(column == BOARD_LEFT-2) {
\r
3413 if( row > BOARD_HEIGHT - gameInfo.holdingsSize - 1 )
\r
3416 DisplayHoldingsCount(hdc, x, y, 0, 0); /* black out */
\r
3420 if(column == BOARD_RGHT + 1 ) {
\r
3421 if( row < gameInfo.holdingsSize )
\r
3424 DisplayHoldingsCount(hdc, x, y, 0, 0);
\r
3428 if(column == BOARD_LEFT-1 ) /* left align */
\r
3429 DisplayHoldingsCount(hdc, x, y, flipView, (int) board[row][column]);
\r
3430 else if( column == BOARD_RGHT) /* right align */
\r
3431 DisplayHoldingsCount(hdc, x, y, !flipView, (int) board[row][column]);
\r
3433 if (appData.monoMode) {
\r
3434 if (piece == EmptySquare) {
\r
3435 BitBlt(hdc, x, y, squareSize, squareSize, 0, 0, 0,
\r
3436 square_color ? WHITENESS : BLACKNESS);
\r
3438 DrawPieceOnDC(hdc, piece, piece_color, square_color, x, y, tmphdc);
\r
3441 else if( appData.useBitmaps && backTextureSquareInfo[row][column].mode > 0 ) {
\r
3442 /* [AS] Draw the square using a texture bitmap */
\r
3443 HBITMAP hbm = SelectObject( texture_hdc, square_color ? liteBackTexture : darkBackTexture );
\r
3444 int r = row, c = column; // [HGM] do not flip board in flipView
\r
3445 if(flipView) { r = BOARD_HEIGHT-1 - r; c = BOARD_WIDTH-1 - c; }
\r
3448 squareSize, squareSize,
\r
3451 backTextureSquareInfo[r][c].mode,
\r
3452 backTextureSquareInfo[r][c].x,
\r
3453 backTextureSquareInfo[r][c].y );
\r
3455 SelectObject( texture_hdc, hbm );
\r
3457 if (piece != EmptySquare) {
\r
3458 DrawPieceOnDC(hdc, piece, piece_color, -1, x, y, tmphdc);
\r
3462 HBRUSH brush = square_color ? lightSquareBrush : darkSquareBrush;
\r
3464 oldBrush = SelectObject(hdc, brush );
\r
3465 BitBlt(hdc, x, y, squareSize, squareSize, 0, 0, 0, PATCOPY);
\r
3466 SelectObject(hdc, oldBrush);
\r
3467 if (piece != EmptySquare)
\r
3468 DrawPieceOnDC(hdc, piece, piece_color, -1, x, y, tmphdc);
\r
3473 if( texture_hdc != NULL ) {
\r
3474 DeleteDC( texture_hdc );
\r
3478 int saveDiagFlag = 0; FILE *diagFile; // [HGM] diag
\r
3479 void fputDW(FILE *f, int x)
\r
3481 fputc(x & 255, f);
\r
3482 fputc(x>>8 & 255, f);
\r
3483 fputc(x>>16 & 255, f);
\r
3484 fputc(x>>24 & 255, f);
\r
3487 #define MAX_CLIPS 200 /* more than enough */
\r
3490 DrawLogoOnDC(HDC hdc, RECT logoRect, HBITMAP logo)
\r
3492 // HBITMAP bufferBitmap;
\r
3497 int w = 100, h = 50;
\r
3499 if(logo == NULL) {
\r
3500 if(!logoHeight) return;
\r
3501 FillRect( hdc, &logoRect, whitePieceBrush );
\r
3503 // GetClientRect(hwndMain, &Rect);
\r
3504 // bufferBitmap = CreateCompatibleBitmap(hdc, Rect.right-Rect.left+1,
\r
3505 // Rect.bottom-Rect.top+1);
\r
3506 tmphdc = CreateCompatibleDC(hdc);
\r
3507 hbm = SelectObject(tmphdc, logo);
\r
3508 if( GetObject( logo, sizeof(bi), &bi ) > 0 ) {
\r
3512 StretchBlt(hdc, logoRect.left, logoRect.top, logoRect.right - logoRect.left,
\r
3513 logoRect.bottom - logoRect.top, tmphdc, 0, 0, w, h, SRCCOPY);
\r
3514 SelectObject(tmphdc, hbm);
\r
3522 HDC hdc = GetDC(hwndMain);
\r
3523 HBITMAP whiteLogo = (HBITMAP) first.programLogo, blackLogo = (HBITMAP) second.programLogo;
\r
3524 if(appData.autoLogo) {
\r
3526 switch(gameMode) { // pick logos based on game mode
\r
3527 case IcsObserving:
\r
3528 whiteLogo = second.programLogo; // ICS logo
\r
3529 blackLogo = second.programLogo;
\r
3532 case IcsPlayingWhite:
\r
3533 if(!appData.zippyPlay) whiteLogo = userLogo;
\r
3534 blackLogo = second.programLogo; // ICS logo
\r
3536 case IcsPlayingBlack:
\r
3537 whiteLogo = second.programLogo; // ICS logo
\r
3538 blackLogo = appData.zippyPlay ? first.programLogo : userLogo;
\r
3540 case TwoMachinesPlay:
\r
3541 if(first.twoMachinesColor[0] == 'b') {
\r
3542 whiteLogo = second.programLogo;
\r
3543 blackLogo = first.programLogo;
\r
3546 case MachinePlaysWhite:
\r
3547 blackLogo = userLogo;
\r
3549 case MachinePlaysBlack:
\r
3550 whiteLogo = userLogo;
\r
3551 blackLogo = first.programLogo;
\r
3554 DrawLogoOnDC(hdc, leftLogoRect, flipClock ? blackLogo : whiteLogo);
\r
3555 DrawLogoOnDC(hdc, rightLogoRect, flipClock ? whiteLogo : blackLogo);
\r
3556 ReleaseDC(hwndMain, hdc);
\r
3561 UpdateLogos(int display)
\r
3562 { // called after loading new engine(s), in tourney or from menu
\r
3563 LoadLogo(&first, 0, FALSE);
\r
3564 LoadLogo(&second, 1, appData.icsActive);
\r
3565 InitDrawingSizes(-2, 0); // adapt layout of board window to presence/absence of logos
\r
3566 if(display) DisplayLogos();
\r
3569 static HDC hdcSeek;
\r
3571 // [HGM] seekgraph
\r
3572 void DrawSeekAxis( int x, int y, int xTo, int yTo )
\r
3575 HPEN hp = SelectObject( hdcSeek, gridPen );
\r
3576 MoveToEx( hdcSeek, boardRect.left+x, boardRect.top+y, &stPt );
\r
3577 LineTo( hdcSeek, boardRect.left+xTo, boardRect.top+yTo );
\r
3578 SelectObject( hdcSeek, hp );
\r
3581 // front-end wrapper for drawing functions to do rectangles
\r
3582 void DrawSeekBackground( int left, int top, int right, int bottom )
\r
3587 if (hdcSeek == NULL) {
\r
3588 hdcSeek = GetDC(hwndMain);
\r
3589 if (!appData.monoMode) {
\r
3590 SelectPalette(hdcSeek, hPal, FALSE);
\r
3591 RealizePalette(hdcSeek);
\r
3594 hp = SelectObject( hdcSeek, gridPen );
\r
3595 rc.top = boardRect.top+top; rc.left = boardRect.left+left;
\r
3596 rc.bottom = boardRect.top+bottom; rc.right = boardRect.left+right;
\r
3597 FillRect( hdcSeek, &rc, lightSquareBrush );
\r
3598 SelectObject( hdcSeek, hp );
\r
3601 // front-end wrapper for putting text in graph
\r
3602 void DrawSeekText(char *buf, int x, int y)
\r
3605 SetBkMode( hdcSeek, TRANSPARENT );
\r
3606 GetTextExtentPoint32( hdcSeek, buf, strlen(buf), &stSize );
\r
3607 TextOut( hdcSeek, boardRect.left+x-3, boardRect.top+y-stSize.cy/2, buf, strlen(buf) );
\r
3610 void DrawSeekDot(int x, int y, int color)
\r
3612 int square = color & 0x80;
\r
3613 HBRUSH oldBrush = SelectObject(hdcSeek,
\r
3614 color == 0 ? markerBrush : color == 1 ? darkSquareBrush : explodeBrush);
\r
3617 Rectangle(hdcSeek, boardRect.left+x - squareSize/9, boardRect.top+y - squareSize/9,
\r
3618 boardRect.left+x + squareSize/9, boardRect.top+y + squareSize/9);
\r
3620 Ellipse(hdcSeek, boardRect.left+x - squareSize/8, boardRect.top+y - squareSize/8,
\r
3621 boardRect.left+x + squareSize/8, boardRect.top+y + squareSize/8);
\r
3622 SelectObject(hdcSeek, oldBrush);
\r
3625 void DrawSeekOpen()
\r
3629 void DrawSeekClose()
\r
3634 HDCDrawPosition(HDC hdc, BOOLEAN repaint, Board board)
\r
3636 static Board lastReq[2], lastDrawn[2];
\r
3637 static HighlightInfo lastDrawnHighlight, lastDrawnPremove;
\r
3638 static int lastDrawnFlipView = 0;
\r
3639 static int lastReqValid[2] = {0, 0}, lastDrawnValid[2] = {0, 0};
\r
3640 int releaseDC, x, y, x2, y2, row, column, num_clips = 0, i;
\r
3643 HBITMAP bufferBitmap;
\r
3644 HBITMAP oldBitmap;
\r
3646 HRGN clips[MAX_CLIPS];
\r
3647 ChessSquare dragged_piece = EmptySquare;
\r
3648 int nr = twoBoards*partnerUp;
\r
3650 /* I'm undecided on this - this function figures out whether a full
\r
3651 * repaint is necessary on its own, so there's no real reason to have the
\r
3652 * caller tell it that. I think this can safely be set to FALSE - but
\r
3653 * if we trust the callers not to request full repaints unnessesarily, then
\r
3654 * we could skip some clipping work. In other words, only request a full
\r
3655 * redraw when the majority of pieces have changed positions (ie. flip,
\r
3656 * gamestart and similar) --Hawk
\r
3658 Boolean fullrepaint = repaint;
\r
3660 if(DrawSeekGraph()) return; // [HG} seekgraph: suppress printing board if seek graph up
\r
3662 if( DrawPositionNeedsFullRepaint() ) {
\r
3663 fullrepaint = TRUE;
\r
3666 if (board == NULL) {
\r
3667 if (!lastReqValid[nr]) {
\r
3670 board = lastReq[nr];
\r
3672 CopyBoard(lastReq[nr], board);
\r
3673 lastReqValid[nr] = 1;
\r
3676 if (doingSizing) {
\r
3680 if (IsIconic(hwndMain)) {
\r
3684 if (hdc == NULL) {
\r
3685 hdc = GetDC(hwndMain);
\r
3686 if (!appData.monoMode) {
\r
3687 SelectPalette(hdc, hPal, FALSE);
\r
3688 RealizePalette(hdc);
\r
3692 releaseDC = FALSE;
\r
3695 /* Create some work-DCs */
\r
3696 hdcmem = CreateCompatibleDC(hdc);
\r
3697 tmphdc = CreateCompatibleDC(hdc);
\r
3699 /* If dragging is in progress, we temporarely remove the piece */
\r
3700 /* [HGM] or temporarily decrease count if stacked */
\r
3701 /* !! Moved to before board compare !! */
\r
3702 if (dragInfo.from.x >= 0 && dragInfo.pos.x >= 0) {
\r
3703 dragged_piece = board[dragInfo.from.y][dragInfo.from.x];
\r
3704 if(dragInfo.from.x == BOARD_LEFT-2 ) {
\r
3705 if(--board[dragInfo.from.y][dragInfo.from.x+1] == 0 )
\r
3706 board[dragInfo.from.y][dragInfo.from.x] = EmptySquare;
\r
3708 if(dragInfo.from.x == BOARD_RGHT+1) {
\r
3709 if(--board[dragInfo.from.y][dragInfo.from.x-1] == 0 )
\r
3710 board[dragInfo.from.y][dragInfo.from.x] = EmptySquare;
\r
3712 board[dragInfo.from.y][dragInfo.from.x] = gatingPiece;
\r
3715 /* Figure out which squares need updating by comparing the
\r
3716 * newest board with the last drawn board and checking if
\r
3717 * flipping has changed.
\r
3719 if (!fullrepaint && lastDrawnValid[nr] && (nr == 1 || lastDrawnFlipView == flipView)) {
\r
3720 for (row = 0; row < BOARD_HEIGHT; row++) { /* [HGM] true size, not 8 */
\r
3721 for (column = 0; column < BOARD_WIDTH; column++) {
\r
3722 if (lastDrawn[nr][row][column] != board[row][column]) {
\r
3723 SquareToPos(row, column, &x, &y);
\r
3724 clips[num_clips++] =
\r
3725 CreateRectRgn(x, y, x + squareSize, y + squareSize);
\r
3729 if(nr == 0) { // [HGM] dual: no highlights on second board
\r
3730 for (i=0; i<2; i++) {
\r
3731 if (lastDrawnHighlight.sq[i].x != highlightInfo.sq[i].x ||
\r
3732 lastDrawnHighlight.sq[i].y != highlightInfo.sq[i].y) {
\r
3733 if (lastDrawnHighlight.sq[i].x >= 0 &&
\r
3734 lastDrawnHighlight.sq[i].y >= 0) {
\r
3735 SquareToPos(lastDrawnHighlight.sq[i].y,
\r
3736 lastDrawnHighlight.sq[i].x, &x, &y);
\r
3737 clips[num_clips++] =
\r
3738 CreateRectRgn(x - lineGap, y - lineGap,
\r
3739 x + squareSize + lineGap, y + squareSize + lineGap);
\r
3741 if (highlightInfo.sq[i].x >= 0 && highlightInfo.sq[i].y >= 0) {
\r
3742 SquareToPos(highlightInfo.sq[i].y, highlightInfo.sq[i].x, &x, &y);
\r
3743 clips[num_clips++] =
\r
3744 CreateRectRgn(x - lineGap, y - lineGap,
\r
3745 x + squareSize + lineGap, y + squareSize + lineGap);
\r
3749 for (i=0; i<2; i++) {
\r
3750 if (lastDrawnPremove.sq[i].x != premoveHighlightInfo.sq[i].x ||
\r
3751 lastDrawnPremove.sq[i].y != premoveHighlightInfo.sq[i].y) {
\r
3752 if (lastDrawnPremove.sq[i].x >= 0 &&
\r
3753 lastDrawnPremove.sq[i].y >= 0) {
\r
3754 SquareToPos(lastDrawnPremove.sq[i].y,
\r
3755 lastDrawnPremove.sq[i].x, &x, &y);
\r
3756 clips[num_clips++] =
\r
3757 CreateRectRgn(x - lineGap, y - lineGap,
\r
3758 x + squareSize + lineGap, y + squareSize + lineGap);
\r
3760 if (premoveHighlightInfo.sq[i].x >= 0 &&
\r
3761 premoveHighlightInfo.sq[i].y >= 0) {
\r
3762 SquareToPos(premoveHighlightInfo.sq[i].y,
\r
3763 premoveHighlightInfo.sq[i].x, &x, &y);
\r
3764 clips[num_clips++] =
\r
3765 CreateRectRgn(x - lineGap, y - lineGap,
\r
3766 x + squareSize + lineGap, y + squareSize + lineGap);
\r
3770 } else { // nr == 1
\r
3771 partnerHighlightInfo.sq[0].y = board[EP_STATUS-4];
\r
3772 partnerHighlightInfo.sq[0].x = board[EP_STATUS-3];
\r
3773 partnerHighlightInfo.sq[1].y = board[EP_STATUS-2];
\r
3774 partnerHighlightInfo.sq[1].x = board[EP_STATUS-1];
\r
3775 for (i=0; i<2; i++) {
\r
3776 if (partnerHighlightInfo.sq[i].x >= 0 &&
\r
3777 partnerHighlightInfo.sq[i].y >= 0) {
\r
3778 SquareToPos(partnerHighlightInfo.sq[i].y,
\r
3779 partnerHighlightInfo.sq[i].x, &x, &y);
\r
3780 clips[num_clips++] =
\r
3781 CreateRectRgn(x - lineGap, y - lineGap,
\r
3782 x + squareSize + lineGap, y + squareSize + lineGap);
\r
3784 if (oldPartnerHighlight.sq[i].x >= 0 &&
\r
3785 oldPartnerHighlight.sq[i].y >= 0) {
\r
3786 SquareToPos(oldPartnerHighlight.sq[i].y,
\r
3787 oldPartnerHighlight.sq[i].x, &x, &y);
\r
3788 clips[num_clips++] =
\r
3789 CreateRectRgn(x - lineGap, y - lineGap,
\r
3790 x + squareSize + lineGap, y + squareSize + lineGap);
\r
3795 fullrepaint = TRUE;
\r
3798 /* Create a buffer bitmap - this is the actual bitmap
\r
3799 * being written to. When all the work is done, we can
\r
3800 * copy it to the real DC (the screen). This avoids
\r
3801 * the problems with flickering.
\r
3803 GetClientRect(hwndMain, &Rect);
\r
3804 bufferBitmap = CreateCompatibleBitmap(hdc, Rect.right-Rect.left+1,
\r
3805 Rect.bottom-Rect.top+1);
\r
3806 oldBitmap = SelectObject(hdcmem, bufferBitmap);
\r
3807 if (!appData.monoMode) {
\r
3808 SelectPalette(hdcmem, hPal, FALSE);
\r
3811 /* Create clips for dragging */
\r
3812 if (!fullrepaint) {
\r
3813 if (dragInfo.from.x >= 0) {
\r
3814 SquareToPos(dragInfo.from.y, dragInfo.from.x, &x, &y);
\r
3815 clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);
\r
3817 if (dragInfo.start.x >= 0) {
\r
3818 SquareToPos(dragInfo.start.y, dragInfo.start.x, &x, &y);
\r
3819 clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);
\r
3821 if (dragInfo.pos.x >= 0) {
\r
3822 x = dragInfo.pos.x - squareSize / 2;
\r
3823 y = dragInfo.pos.y - squareSize / 2;
\r
3824 clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);
\r
3826 if (dragInfo.lastpos.x >= 0) {
\r
3827 x = dragInfo.lastpos.x - squareSize / 2;
\r
3828 y = dragInfo.lastpos.y - squareSize / 2;
\r
3829 clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);
\r
3833 /* Are we animating a move?
\r
3835 * - remove the piece from the board (temporarely)
\r
3836 * - calculate the clipping region
\r
3838 if (!fullrepaint) {
\r
3839 if (animInfo.piece != EmptySquare) {
\r
3840 board[animInfo.from.y][animInfo.from.x] = EmptySquare;
\r
3841 x = boardRect.left + animInfo.lastpos.x;
\r
3842 y = boardRect.top + animInfo.lastpos.y;
\r
3843 x2 = boardRect.left + animInfo.pos.x;
\r
3844 y2 = boardRect.top + animInfo.pos.y;
\r
3845 clips[num_clips++] = CreateRectRgn(MIN(x,x2), MIN(y,y2), MAX(x,x2)+squareSize, MAX(y,y2)+squareSize);
\r
3846 /* Slight kludge. The real problem is that after AnimateMove is
\r
3847 done, the position on the screen does not match lastDrawn.
\r
3848 This currently causes trouble only on e.p. captures in
\r
3849 atomic, where the piece moves to an empty square and then
\r
3850 explodes. The old and new positions both had an empty square
\r
3851 at the destination, but animation has drawn a piece there and
\r
3852 we have to remember to erase it. [HGM] moved until after setting lastDrawn */
\r
3853 lastDrawn[0][animInfo.to.y][animInfo.to.x] = animInfo.piece;
\r
3857 /* No clips? Make sure we have fullrepaint set to TRUE */
\r
3858 if (num_clips == 0)
\r
3859 fullrepaint = TRUE;
\r
3861 /* Set clipping on the memory DC */
\r
3862 if (!fullrepaint) {
\r
3863 SelectClipRgn(hdcmem, clips[0]);
\r
3864 for (x = 1; x < num_clips; x++) {
\r
3865 if (ExtSelectClipRgn(hdcmem, clips[x], RGN_OR) == ERROR)
\r
3866 abort(); // this should never ever happen!
\r
3870 /* Do all the drawing to the memory DC */
\r
3871 if(explodeInfo.radius) { // [HGM] atomic
\r
3873 int x, y, r=(explodeInfo.radius * squareSize)/100;
\r
3874 ChessSquare piece = board[explodeInfo.fromY][explodeInfo.fromX];
\r
3875 board[explodeInfo.fromY][explodeInfo.fromX] = EmptySquare; // suppress display of capturer
\r
3876 SquareToPos(explodeInfo.toY, explodeInfo.toX, &x, &y);
\r
3877 x += squareSize/2;
\r
3878 y += squareSize/2;
\r
3879 if(!fullrepaint) {
\r
3880 clips[num_clips] = CreateRectRgn(x-r, y-r, x+r, y+r);
\r
3881 ExtSelectClipRgn(hdcmem, clips[num_clips++], RGN_OR);
\r
3883 DrawGridOnDC(hdcmem);
\r
3884 DrawHighlightsOnDC(hdcmem, &highlightInfo, HIGHLIGHT_PEN);
\r
3885 DrawHighlightsOnDC(hdcmem, &premoveHighlightInfo, PREMOVE_PEN);
\r
3886 DrawBoardOnDC(hdcmem, board, tmphdc);
\r
3887 board[explodeInfo.fromY][explodeInfo.fromX] = piece;
\r
3888 oldBrush = SelectObject(hdcmem, explodeBrush);
\r
3889 Ellipse(hdcmem, x-r, y-r, x+r, y+r);
\r
3890 SelectObject(hdcmem, oldBrush);
\r
3892 if(border) DrawBackgroundOnDC(hdcmem);
\r
3893 DrawGridOnDC(hdcmem);
\r
3894 if(nr == 0) { // [HGM] dual: decide which highlights to draw
\r
3895 DrawHighlightsOnDC(hdcmem, &highlightInfo, HIGHLIGHT_PEN);
\r
3896 DrawHighlightsOnDC(hdcmem, &premoveHighlightInfo, PREMOVE_PEN);
\r
3898 DrawHighlightsOnDC(hdcmem, &partnerHighlightInfo, HIGHLIGHT_PEN);
\r
3899 oldPartnerHighlight = partnerHighlightInfo;
\r
3901 DrawBoardOnDC(hdcmem, board, tmphdc);
\r
3903 if(nr == 0) // [HGM] dual: markers only on left board
\r
3904 for (row = 0; row < BOARD_HEIGHT; row++) {
\r
3905 for (column = 0; column < BOARD_WIDTH; column++) {
\r
3906 if (marker[row][column]) { // marker changes only occur with full repaint!
\r
3907 HBRUSH oldBrush = SelectObject(hdcmem,
\r
3908 marker[row][column] == 2 ? markerBrush : explodeBrush);
\r
3909 SquareToPos(row, column, &x, &y);
\r
3910 Ellipse(hdcmem, x + squareSize/4, y + squareSize/4,
\r
3911 x + 3*squareSize/4, y + 3*squareSize/4);
\r
3912 SelectObject(hdcmem, oldBrush);
\r
3917 if( appData.highlightMoveWithArrow ) {
\r
3918 DrawArrowHighlight(hdcmem);
\r
3921 DrawCoordsOnDC(hdcmem);
\r
3923 CopyBoard(lastDrawn[nr], board); /* [HGM] Moved to here from end of routine, */
\r
3924 /* to make sure lastDrawn contains what is actually drawn */
\r
3926 /* Put the dragged piece back into place and draw it (out of place!) */
\r
3927 if (dragged_piece != EmptySquare) {
\r
3928 /* [HGM] or restack */
\r
3929 if(dragInfo.from.x == BOARD_LEFT-2 )
\r
3930 board[dragInfo.from.y][dragInfo.from.x+1]++;
\r
3932 if(dragInfo.from.x == BOARD_RGHT+1 )
\r
3933 board[dragInfo.from.y][dragInfo.from.x-1]++;
\r
3934 board[dragInfo.from.y][dragInfo.from.x] = dragged_piece;
\r
3935 x = dragInfo.pos.x - squareSize / 2;
\r
3936 y = dragInfo.pos.y - squareSize / 2;
\r
3937 DrawPieceOnDC(hdcmem, dragInfo.piece,
\r
3938 ((int) dragInfo.piece < (int) BlackPawn),
\r
3939 (dragInfo.from.y + dragInfo.from.x) % 2, x, y, tmphdc);
\r
3942 /* Put the animated piece back into place and draw it */
\r
3943 if (animInfo.piece != EmptySquare) {
\r
3944 board[animInfo.from.y][animInfo.from.x] = animInfo.piece;
\r
3945 x = boardRect.left + animInfo.pos.x;
\r
3946 y = boardRect.top + animInfo.pos.y;
\r
3947 DrawPieceOnDC(hdcmem, animInfo.piece,
\r
3948 ((int) animInfo.piece < (int) BlackPawn),
\r
3949 (animInfo.from.y + animInfo.from.x) % 2, x, y, tmphdc);
\r
3952 /* Release the bufferBitmap by selecting in the old bitmap
\r
3953 * and delete the memory DC
\r
3955 SelectObject(hdcmem, oldBitmap);
\r
3958 /* Set clipping on the target DC */
\r
3959 if (!fullrepaint) {
\r
3960 if(nr == 1) for (x = 0; x < num_clips; x++) { // [HGM] dual: translate clips
\r
3962 GetRgnBox(clips[x], &rect);
\r
3963 DeleteObject(clips[x]);
\r
3964 clips[x] = CreateRectRgn(rect.left + wpMain.width/2, rect.top,
\r
3965 rect.right + wpMain.width/2, rect.bottom);
\r
3967 SelectClipRgn(hdc, clips[0]);
\r
3968 for (x = 1; x < num_clips; x++) {
\r
3969 if (ExtSelectClipRgn(hdc, clips[x], RGN_OR) == ERROR)
\r
3970 abort(); // this should never ever happen!
\r
3974 /* Copy the new bitmap onto the screen in one go.
\r
3975 * This way we avoid any flickering
\r
3977 oldBitmap = SelectObject(tmphdc, bufferBitmap);
\r
3978 BitBlt(hdc, boardRect.left + twoBoards*partnerUp*wpMain.width/2, boardRect.top, // [HGM] dual
\r
3979 boardRect.right - boardRect.left,
\r
3980 boardRect.bottom - boardRect.top,
\r
3981 tmphdc, boardRect.left, boardRect.top, SRCCOPY);
\r
3982 if(saveDiagFlag) {
\r
3983 BITMAP b; int i, j=0, m, w, wb, fac=0; char *pData;
\r
3984 BITMAPINFOHEADER bih; int color[16], nrColors=0;
\r
3986 GetObject(bufferBitmap, sizeof(b), &b);
\r
3987 if(pData = malloc(b.bmWidthBytes*b.bmHeight + 10000)) {
\r
3988 bih.biSize = sizeof(BITMAPINFOHEADER);
\r
3989 bih.biWidth = b.bmWidth;
\r
3990 bih.biHeight = b.bmHeight;
\r
3992 bih.biBitCount = b.bmBitsPixel;
\r
3993 bih.biCompression = 0;
\r
3994 bih.biSizeImage = b.bmWidthBytes*b.bmHeight;
\r
3995 bih.biXPelsPerMeter = 0;
\r
3996 bih.biYPelsPerMeter = 0;
\r
3997 bih.biClrUsed = 0;
\r
3998 bih.biClrImportant = 0;
\r
3999 // fprintf(diagFile, "t=%d\nw=%d\nh=%d\nB=%d\nP=%d\nX=%d\n",
\r
4000 // b.bmType, b.bmWidth, b.bmHeight, b.bmWidthBytes, b.bmPlanes, b.bmBitsPixel);
\r
4001 GetDIBits(tmphdc,bufferBitmap,0,b.bmHeight,pData,(BITMAPINFO*)&bih,DIB_RGB_COLORS);
\r
4002 // fprintf(diagFile, "%8x\n", (int) pData);
\r
4004 wb = b.bmWidthBytes;
\r
4006 for(i=0; i<wb*(b.bmHeight - boardRect.top + OUTER_MARGIN)>>2; i++) {
\r
4007 int k = ((int*) pData)[i];
\r
4008 for(j=0; j<nrColors; j++) if(color[j] == k) break;
\r
4009 if(j >= 16) break;
\r
4011 if(j >= nrColors) nrColors = j+1;
\r
4013 if(j<16) { // 16 colors is enough. Compress to 4 bits per pixel
\r
4015 for(i=0; i<b.bmHeight - boardRect.top + OUTER_MARGIN; i++) {
\r
4016 for(w=0; w<(wb>>2); w+=2) {
\r
4017 int k = ((int*) pData)[(wb*i>>2) + w];
\r
4018 for(j=0; j<nrColors; j++) if(color[j] == k) break;
\r
4019 k = ((int*) pData)[(wb*i>>2) + w + 1];
\r
4020 for(m=0; m<nrColors; m++) if(color[m] == k) break;
\r
4021 pData[p++] = m | j<<4;
\r
4023 while(p&3) pData[p++] = 0;
\r
4026 wb = ((wb+31)>>5)<<2;
\r
4028 // write BITMAPFILEHEADER
\r
4029 fprintf(diagFile, "BM");
\r
4030 fputDW(diagFile, wb*(b.bmHeight - boardRect.top + OUTER_MARGIN)+0x36 + (fac?64:0));
\r
4031 fputDW(diagFile, 0);
\r
4032 fputDW(diagFile, 0x36 + (fac?64:0));
\r
4033 // write BITMAPINFOHEADER
\r
4034 fputDW(diagFile, 40);
\r
4035 fputDW(diagFile, b.bmWidth);
\r
4036 fputDW(diagFile, b.bmHeight - boardRect.top + OUTER_MARGIN);
\r
4037 if(fac) fputDW(diagFile, 0x040001); // planes and bits/pixel
\r
4038 else fputDW(diagFile, 0x200001); // planes and bits/pixel
\r
4039 fputDW(diagFile, 0);
\r
4040 fputDW(diagFile, 0);
\r
4041 fputDW(diagFile, 0);
\r
4042 fputDW(diagFile, 0);
\r
4043 fputDW(diagFile, 0);
\r
4044 fputDW(diagFile, 0);
\r
4045 // write color table
\r
4047 for(i=0; i<16; i++) fputDW(diagFile, color[i]);
\r
4048 // write bitmap data
\r
4049 for(i=0; i<wb*(b.bmHeight - boardRect.top + OUTER_MARGIN); i++)
\r
4050 fputc(pData[i], diagFile);
\r
4055 SelectObject(tmphdc, oldBitmap);
\r
4057 /* Massive cleanup */
\r
4058 for (x = 0; x < num_clips; x++)
\r
4059 DeleteObject(clips[x]);
\r
4062 DeleteObject(bufferBitmap);
\r
4065 ReleaseDC(hwndMain, hdc);
\r
4067 if (lastDrawnFlipView != flipView && nr == 0) {
\r
4069 CheckMenuItem(GetMenu(hwndMain),IDM_FlipView, MF_BYCOMMAND|MF_CHECKED);
\r
4071 CheckMenuItem(GetMenu(hwndMain),IDM_FlipView, MF_BYCOMMAND|MF_UNCHECKED);
\r
4074 /* CopyBoard(lastDrawn, board);*/
\r
4075 lastDrawnHighlight = highlightInfo;
\r
4076 lastDrawnPremove = premoveHighlightInfo;
\r
4077 lastDrawnFlipView = flipView;
\r
4078 lastDrawnValid[nr] = 1;
\r
4081 /* [HGM] diag: Save the current board display to the given open file and close the file */
\r
4086 saveDiagFlag = 1; diagFile = f;
\r
4087 HDCDrawPosition(NULL, TRUE, NULL);
\r
4095 /*---------------------------------------------------------------------------*\
\r
4096 | CLIENT PAINT PROCEDURE
\r
4097 | This is the main event-handler for the WM_PAINT message.
\r
4099 \*---------------------------------------------------------------------------*/
\r
4101 PaintProc(HWND hwnd)
\r
4107 if((hdc = BeginPaint(hwnd, &ps))) {
\r
4108 if (IsIconic(hwnd)) {
\r
4109 DrawIcon(hdc, 2, 2, iconCurrent);
\r
4111 if (!appData.monoMode) {
\r
4112 SelectPalette(hdc, hPal, FALSE);
\r
4113 RealizePalette(hdc);
\r
4115 HDCDrawPosition(hdc, 1, NULL);
\r
4116 if(twoBoards) { // [HGM] dual: also redraw other board in other orientation
\r
4117 flipView = !flipView; partnerUp = !partnerUp;
\r
4118 HDCDrawPosition(hdc, 1, NULL);
\r
4119 flipView = !flipView; partnerUp = !partnerUp;
\r
4122 SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);
\r
4123 ExtTextOut(hdc, messageRect.left, messageRect.top,
\r
4124 ETO_CLIPPED|ETO_OPAQUE,
\r
4125 &messageRect, messageText, strlen(messageText), NULL);
\r
4126 SelectObject(hdc, oldFont);
\r
4127 DisplayBothClocks();
\r
4130 EndPaint(hwnd,&ps);
\r
4138 * If the user selects on a border boundary, return -1; if off the board,
\r
4139 * return -2. Otherwise map the event coordinate to the square.
\r
4140 * The offset boardRect.left or boardRect.top must already have been
\r
4141 * subtracted from x.
\r
4143 int EventToSquare(x, limit)
\r
4148 if (x < lineGap + border)
\r
4150 x -= lineGap + border;
\r
4151 if ((x % (squareSize + lineGap)) >= squareSize)
\r
4153 x /= (squareSize + lineGap);
\r
4165 DropEnable dropEnables[] = {
\r
4166 { 'P', DP_Pawn, N_("Pawn") },
\r
4167 { 'N', DP_Knight, N_("Knight") },
\r
4168 { 'B', DP_Bishop, N_("Bishop") },
\r
4169 { 'R', DP_Rook, N_("Rook") },
\r
4170 { 'Q', DP_Queen, N_("Queen") },
\r
4174 SetupDropMenu(HMENU hmenu)
\r
4176 int i, count, enable;
\r
4178 extern char white_holding[], black_holding[];
\r
4179 char item[MSG_SIZ];
\r
4181 for (i=0; i<sizeof(dropEnables)/sizeof(DropEnable); i++) {
\r
4182 p = strchr(gameMode == IcsPlayingWhite ? white_holding : black_holding,
\r
4183 dropEnables[i].piece);
\r
4185 while (p && *p++ == dropEnables[i].piece) count++;
\r
4186 snprintf(item, MSG_SIZ, "%s %d", T_(dropEnables[i].name), count);
\r
4187 enable = count > 0 || !appData.testLegality
\r
4188 /*!!temp:*/ || (gameInfo.variant == VariantCrazyhouse
\r
4189 && !appData.icsActive);
\r
4190 ModifyMenu(hmenu, dropEnables[i].command,
\r
4191 MF_BYCOMMAND | (enable ? MF_ENABLED : MF_GRAYED) | MF_STRING,
\r
4192 dropEnables[i].command, item);
\r
4196 void DragPieceBegin(int x, int y, Boolean instantly)
\r
4198 dragInfo.lastpos.x = boardRect.left + x;
\r
4199 dragInfo.lastpos.y = boardRect.top + y;
\r
4200 if(instantly) dragInfo.pos = dragInfo.lastpos;
\r
4201 dragInfo.from.x = fromX;
\r
4202 dragInfo.from.y = fromY;
\r
4203 dragInfo.piece = boards[currentMove][fromY][fromX];
\r
4204 dragInfo.start = dragInfo.from;
\r
4205 SetCapture(hwndMain);
\r
4208 void DragPieceEnd(int x, int y)
\r
4211 dragInfo.start.x = dragInfo.start.y = -1;
\r
4212 dragInfo.from = dragInfo.start;
\r
4213 dragInfo.pos = dragInfo.lastpos = dragInfo.start;
\r
4216 void ChangeDragPiece(ChessSquare piece)
\r
4218 dragInfo.piece = piece;
\r
4221 /* Event handler for mouse messages */
\r
4223 MouseEvent(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
\r
4227 static int recursive = 0;
\r
4229 BOOLEAN forceFullRepaint = IsFullRepaintPreferrable(); /* [AS] */
\r
4232 if (message == WM_MBUTTONUP) {
\r
4233 /* Hideous kludge to fool TrackPopupMenu into paying attention
\r
4234 to the middle button: we simulate pressing the left button too!
\r
4236 PostMessage(hwnd, WM_LBUTTONDOWN, wParam, lParam);
\r
4237 PostMessage(hwnd, WM_LBUTTONUP, wParam, lParam);
\r
4243 pt.x = LOWORD(lParam);
\r
4244 pt.y = HIWORD(lParam);
\r
4245 x = EventToSquare(pt.x - boardRect.left, BOARD_WIDTH);
\r
4246 y = EventToSquare(pt.y - boardRect.top, BOARD_HEIGHT);
\r
4247 if (!flipView && y >= 0) {
\r
4248 y = BOARD_HEIGHT - 1 - y;
\r
4250 if (flipView && x >= 0) {
\r
4251 x = BOARD_WIDTH - 1 - x;
\r
4254 shiftKey = GetKeyState(VK_SHIFT) < 0; // [HGM] remember last shift status
\r
4256 switch (message) {
\r
4257 case WM_LBUTTONDOWN:
\r
4258 if (PtInRect((LPRECT) &whiteRect, pt)) {
\r
4259 ClockClick(flipClock); break;
\r
4260 } else if (PtInRect((LPRECT) &blackRect, pt)) {
\r
4261 ClockClick(!flipClock); break;
\r
4263 dragInfo.start.x = dragInfo.start.y = -1;
\r
4264 dragInfo.from = dragInfo.start;
\r
4265 if(fromX == -1 && frozen) { // not sure where this is for
\r
4266 fromX = fromY = -1;
\r
4267 DrawPosition(forceFullRepaint || FALSE, NULL); /* [AS] */
\r
4270 LeftClick(Press, pt.x - boardRect.left, pt.y - boardRect.top);
\r
4271 DrawPosition(TRUE, NULL);
\r
4274 case WM_LBUTTONUP:
\r
4275 LeftClick(Release, pt.x - boardRect.left, pt.y - boardRect.top);
\r
4276 DrawPosition(TRUE, NULL);
\r
4279 case WM_MOUSEMOVE:
\r
4280 if(SeekGraphClick(Press, pt.x - boardRect.left, pt.y - boardRect.top, 1)) break;
\r
4281 if(PromoScroll(pt.x - boardRect.left, pt.y - boardRect.top)) break;
\r
4282 MovePV(pt.x - boardRect.left, pt.y - boardRect.top, boardRect.bottom - boardRect.top);
\r
4283 if ((appData.animateDragging || appData.highlightDragging)
\r
4284 && (wParam & MK_LBUTTON)
\r
4285 && dragInfo.from.x >= 0)
\r
4287 BOOL full_repaint = FALSE;
\r
4289 if (appData.animateDragging) {
\r
4290 dragInfo.pos = pt;
\r
4292 if (appData.highlightDragging) {
\r
4293 SetHighlights(fromX, fromY, x, y);
\r
4294 if( IsDrawArrowEnabled() && (x < 0 || x >= BOARD_WIDTH || y < 0 || y >= BOARD_HEIGHT) ) {
\r
4295 full_repaint = TRUE;
\r
4299 DrawPosition( full_repaint, NULL);
\r
4301 dragInfo.lastpos = dragInfo.pos;
\r
4305 case WM_MOUSEWHEEL: // [DM]
\r
4306 { static int lastDir = 0; // [HGM] build in some hysteresis to avoid spurious events
\r
4307 /* Mouse Wheel is being rolled forward
\r
4308 * Play moves forward
\r
4310 if((short)HIWORD(wParam) < 0 && currentMove < forwardMostMove)
\r
4311 { if(lastDir == 1) ForwardEvent(); else lastDir = 1; } // [HGM] suppress first event in direction
\r
4312 /* Mouse Wheel is being rolled backward
\r
4313 * Play moves backward
\r
4315 if((short)HIWORD(wParam) > 0 && currentMove > backwardMostMove)
\r
4316 { if(lastDir == -1) BackwardEvent(); else lastDir = -1; }
\r
4320 case WM_MBUTTONUP:
\r
4321 case WM_RBUTTONUP:
\r
4323 RightClick(Release, pt.x - boardRect.left, pt.y - boardRect.top, &fromX, &fromY);
\r
4326 case WM_MBUTTONDOWN:
\r
4327 case WM_RBUTTONDOWN:
\r
4330 fromX = fromY = -1;
\r
4331 dragInfo.pos.x = dragInfo.pos.y = -1;
\r
4332 dragInfo.start.x = dragInfo.start.y = -1;
\r
4333 dragInfo.from = dragInfo.start;
\r
4334 dragInfo.lastpos = dragInfo.pos;
\r
4335 if (appData.highlightDragging) {
\r
4336 ClearHighlights();
\r
4339 /* [HGM] right mouse button in clock area edit-game mode ups clock */
\r
4340 if (PtInRect((LPRECT) &whiteRect, pt)) {
\r
4341 if (GetKeyState(VK_SHIFT) < 0) AdjustClock(flipClock, 1);
\r
4342 } else if (PtInRect((LPRECT) &blackRect, pt)) {
\r
4343 if (GetKeyState(VK_SHIFT) < 0) AdjustClock(!flipClock, 1);
\r
4347 DrawPosition(TRUE, NULL);
\r
4349 menuNr = RightClick(Press, pt.x - boardRect.left, pt.y - boardRect.top, &fromX, &fromY);
\r
4352 if (message == WM_MBUTTONDOWN) {
\r
4353 buttonCount = 3; /* even if system didn't think so */
\r
4354 if (wParam & MK_SHIFT)
\r
4355 MenuPopup(hwnd, pt, LoadMenu(hInst, "BlackPieceMenu"), -1);
\r
4357 MenuPopup(hwnd, pt, LoadMenu(hInst, "WhitePieceMenu"), -1);
\r
4358 } else { /* message == WM_RBUTTONDOWN */
\r
4359 /* Just have one menu, on the right button. Windows users don't
\r
4360 think to try the middle one, and sometimes other software steals
\r
4361 it, or it doesn't really exist. */
\r
4362 if(gameInfo.variant != VariantShogi)
\r
4363 MenuPopup(hwnd, pt, LoadMenu(hInst, "PieceMenu"), -1);
\r
4365 MenuPopup(hwnd, pt, LoadMenu(hInst, "ShogiPieceMenu"), -1);
\r
4369 SetCapture(hwndMain);
\r
4372 hmenu = LoadMenu(hInst, "DropPieceMenu");
\r
4373 SetupDropMenu(hmenu);
\r
4374 MenuPopup(hwnd, pt, hmenu, -1);
\r
4384 /* Preprocess messages for buttons in main window */
\r
4386 ButtonProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
\r
4388 int id = GetWindowLongPtr(hwnd, GWLP_ID);
\r
4391 for (i=0; i<N_BUTTONS; i++) {
\r
4392 if (buttonDesc[i].id == id) break;
\r
4394 if (i == N_BUTTONS) return 0;
\r
4395 switch (message) {
\r
4400 dir = (wParam == VK_LEFT) ? -1 : 1;
\r
4401 SetFocus(buttonDesc[(i + dir + N_BUTTONS) % N_BUTTONS].hwnd);
\r
4408 SendMessage(hwndMain, WM_COMMAND, MAKEWPARAM(buttonDesc[i].id, 0), 0);
\r
4411 if (appData.icsActive && (isalpha((char)wParam) || wParam == '0')) {
\r
4412 // [HGM] movenum: only letters or leading zero should go to ICS input
\r
4413 HWND h = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
4414 if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);
\r
4416 SendMessage(h, WM_CHAR, wParam, lParam);
\r
4418 } else if (isalpha((char)wParam) || isdigit((char)wParam)){
\r
4419 TypeInEvent((char)wParam);
\r
4425 return CallWindowProc(buttonDesc[i].wndproc, hwnd, message, wParam, lParam);
\r
4428 /* Process messages for Promotion dialog box */
\r
4430 Promotion(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
4434 switch (message) {
\r
4435 case WM_INITDIALOG: /* message: initialize dialog box */
\r
4436 /* Center the dialog over the application window */
\r
4437 CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));
\r
4438 Translate(hDlg, DLG_PromotionKing);
\r
4439 ShowWindow(GetDlgItem(hDlg, PB_King),
\r
4440 (!appData.testLegality || gameInfo.variant == VariantSuicide ||
\r
4441 gameInfo.variant == VariantSpartan && !WhiteOnMove(currentMove) ||
\r
4442 gameInfo.variant == VariantGiveaway || gameInfo.variant == VariantSuper ) ?
\r
4443 SW_SHOW : SW_HIDE);
\r
4444 /* [HGM] Only allow C & A promotions if these pieces are defined */
\r
4445 ShowWindow(GetDlgItem(hDlg, PB_Archbishop),
\r
4446 ((PieceToChar(WhiteAngel) >= 'A' && WhiteOnMove(currentMove) &&
\r
4447 PieceToChar(WhiteAngel) != '~') ||
\r
4448 (PieceToChar(BlackAngel) >= 'A' && !WhiteOnMove(currentMove) &&
\r
4449 PieceToChar(BlackAngel) != '~') ) ?
\r
4450 SW_SHOW : SW_HIDE);
\r
4451 ShowWindow(GetDlgItem(hDlg, PB_Chancellor),
\r
4452 ((PieceToChar(WhiteMarshall) >= 'A' && WhiteOnMove(currentMove) &&
\r
4453 PieceToChar(WhiteMarshall) != '~') ||
\r
4454 (PieceToChar(BlackMarshall) >= 'A' && !WhiteOnMove(currentMove) &&
\r
4455 PieceToChar(BlackMarshall) != '~') ) ?
\r
4456 SW_SHOW : SW_HIDE);
\r
4457 /* [HGM] Hide B & R button in Shogi, use Q as promote, N as defer */
\r
4458 ShowWindow(GetDlgItem(hDlg, PB_Rook),
\r
4459 gameInfo.variant != VariantShogi ?
\r
4460 SW_SHOW : SW_HIDE);
\r
4461 ShowWindow(GetDlgItem(hDlg, PB_Bishop),
\r
4462 gameInfo.variant != VariantShogi ?
\r
4463 SW_SHOW : SW_HIDE);
\r
4464 if(gameInfo.variant == VariantShogi) {
\r
4465 SetDlgItemText(hDlg, PB_Queen, "YES");
\r
4466 SetDlgItemText(hDlg, PB_Knight, "NO");
\r
4467 SetWindowText(hDlg, "Promote?");
\r
4469 ShowWindow(GetDlgItem(hDlg, IDC_Centaur),
\r
4470 gameInfo.variant == VariantSuper ?
\r
4471 SW_SHOW : SW_HIDE);
\r
4474 case WM_COMMAND: /* message: received a command */
\r
4475 switch (LOWORD(wParam)) {
\r
4477 EndDialog(hDlg, TRUE); /* Exit the dialog */
\r
4478 ClearHighlights();
\r
4479 DrawPosition(FALSE, NULL);
\r
4482 promoChar = gameInfo.variant == VariantSuper ? PieceToChar(BlackSilver) : PieceToChar(BlackKing);
\r
4485 promoChar = gameInfo.variant == VariantShogi ? '+' : ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteQueen : BlackQueen));
\r
4488 promoChar = ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteRook : BlackRook));
\r
4489 if(gameInfo.variant == VariantSpartan && !WhiteOnMove(currentMove)) promoChar = PieceToChar(BlackDragon);
\r
4492 promoChar = ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteBishop : BlackBishop));
\r
4493 if(gameInfo.variant == VariantSpartan && !WhiteOnMove(currentMove)) promoChar = PieceToChar(BlackAlfil);
\r
4495 case PB_Chancellor:
\r
4496 promoChar = ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteMarshall : BlackMarshall));
\r
4498 case PB_Archbishop:
\r
4499 promoChar = ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteAngel : BlackAngel));
\r
4502 promoChar = gameInfo.variant == VariantShogi ? '=' : PieceToChar(WhiteOnMove(currentMove) ? WhiteKnight : BlackKnight);
\r
4507 if(promoChar == '.') return FALSE; // invalid piece chosen
\r
4508 EndDialog(hDlg, TRUE); /* Exit the dialog */
\r
4509 UserMoveEvent(fromX, fromY, toX, toY, promoChar);
\r
4510 fromX = fromY = -1;
\r
4511 if (!appData.highlightLastMove) {
\r
4512 ClearHighlights();
\r
4513 DrawPosition(FALSE, NULL);
\r
4520 /* Pop up promotion dialog */
\r
4522 PromotionPopup(HWND hwnd)
\r
4526 lpProc = MakeProcInstance((FARPROC)Promotion, hInst);
\r
4527 DialogBox(hInst, MAKEINTRESOURCE(DLG_PromotionKing),
\r
4528 hwnd, (DLGPROC)lpProc);
\r
4529 FreeProcInstance(lpProc);
\r
4535 DrawPosition(TRUE, NULL);
\r
4536 PromotionPopup(hwndMain);
\r
4540 LoadGameDialog(HWND hwnd, char* title)
\r
4544 char fileTitle[MSG_SIZ];
\r
4545 f = OpenFileDialog(hwnd, "rb", "",
\r
4546 appData.oldSaveStyle ? "gam" : "pgn",
\r
4548 title, &number, fileTitle, NULL);
\r
4550 cmailMsgLoaded = FALSE;
\r
4551 if (number == 0) {
\r
4552 int error = GameListBuild(f);
\r
4554 DisplayError(_("Cannot build game list"), error);
\r
4555 } else if (!ListEmpty(&gameList) &&
\r
4556 ((ListGame *) gameList.tailPred)->number > 1) {
\r
4557 GameListPopUp(f, fileTitle);
\r
4560 GameListDestroy();
\r
4563 LoadGame(f, number, fileTitle, FALSE);
\r
4567 int get_term_width()
\r
4572 HFONT hfont, hold_font;
\r
4577 hText = GetDlgItem(hwndConsole, OPT_ConsoleText);
\r
4581 // get the text metrics
\r
4582 hdc = GetDC(hText);
\r
4583 lf = font[boardSize][CONSOLE_FONT]->lf;
\r
4584 if (consoleCF.dwEffects & CFE_BOLD)
\r
4585 lf.lfWeight = FW_BOLD;
\r
4586 if (consoleCF.dwEffects & CFE_ITALIC)
\r
4587 lf.lfItalic = TRUE;
\r
4588 if (consoleCF.dwEffects & CFE_STRIKEOUT)
\r
4589 lf.lfStrikeOut = TRUE;
\r
4590 if (consoleCF.dwEffects & CFE_UNDERLINE)
\r
4591 lf.lfUnderline = TRUE;
\r
4592 hfont = CreateFontIndirect(&lf);
\r
4593 hold_font = SelectObject(hdc, hfont);
\r
4594 GetTextMetrics(hdc, &tm);
\r
4595 SelectObject(hdc, hold_font);
\r
4596 DeleteObject(hfont);
\r
4597 ReleaseDC(hText, hdc);
\r
4599 // get the rectangle
\r
4600 SendMessage(hText, EM_GETRECT, 0, (LPARAM)&rc);
\r
4602 return (rc.right-rc.left) / tm.tmAveCharWidth;
\r
4605 void UpdateICSWidth(HWND hText)
\r
4607 LONG old_width, new_width;
\r
4609 new_width = get_term_width(hText, FALSE);
\r
4610 old_width = GetWindowLongPtr(hText, GWLP_USERDATA);
\r
4611 if (new_width != old_width)
\r
4613 ics_update_width(new_width);
\r
4614 SetWindowLongPtr(hText, GWLP_USERDATA, new_width);
\r
4619 ChangedConsoleFont()
\r
4622 CHARRANGE tmpsel, sel;
\r
4623 MyFont *f = font[boardSize][CONSOLE_FONT];
\r
4624 HWND hText = GetDlgItem(hwndConsole, OPT_ConsoleText);
\r
4625 HWND hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
4628 cfmt.cbSize = sizeof(CHARFORMAT);
\r
4629 cfmt.dwMask = CFM_FACE|CFM_SIZE|CFM_CHARSET;
\r
4630 safeStrCpy(cfmt.szFaceName, font[boardSize][CONSOLE_FONT]->mfp.faceName,
\r
4631 sizeof(cfmt.szFaceName)/sizeof(cfmt.szFaceName[0]) );
\r
4632 /* yHeight is expressed in twips. A twip is 1/20 of a font's point
\r
4633 * size. This was undocumented in the version of MSVC++ that I had
\r
4634 * when I wrote the code, but is apparently documented now.
\r
4636 cfmt.yHeight = (int)(f->mfp.pointSize * 20.0 + 0.5);
\r
4637 cfmt.bCharSet = f->lf.lfCharSet;
\r
4638 cfmt.bPitchAndFamily = f->lf.lfPitchAndFamily;
\r
4639 SendMessage(hText, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &cfmt);
\r
4640 SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &cfmt);
\r
4641 /* Why are the following seemingly needed too? */
\r
4642 SendMessage(hText, EM_SETCHARFORMAT, SCF_DEFAULT, (LPARAM) &cfmt);
\r
4643 SendMessage(hInput, EM_SETCHARFORMAT, SCF_DEFAULT, (LPARAM) &cfmt);
\r
4644 SendMessage(hText, EM_EXGETSEL, 0, (LPARAM)&sel);
\r
4646 tmpsel.cpMax = -1; /*999999?*/
\r
4647 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&tmpsel);
\r
4648 SendMessage(hText, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cfmt);
\r
4649 /* Trying putting this here too. It still seems to tickle a RichEdit
\r
4650 * bug: sometimes RichEdit indents the first line of a paragraph too.
\r
4652 paraf.cbSize = sizeof(paraf);
\r
4653 paraf.dwMask = PFM_OFFSET | PFM_STARTINDENT;
\r
4654 paraf.dxStartIndent = 0;
\r
4655 paraf.dxOffset = WRAP_INDENT;
\r
4656 SendMessage(hText, EM_SETPARAFORMAT, 0, (LPARAM) ¶f);
\r
4657 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
4658 UpdateICSWidth(hText);
\r
4661 /*---------------------------------------------------------------------------*\
\r
4663 * Window Proc for main window
\r
4665 \*---------------------------------------------------------------------------*/
\r
4667 /* Process messages for main window, etc. */
\r
4669 WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
\r
4672 int wmId, wmEvent;
\r
4676 char fileTitle[MSG_SIZ];
\r
4677 char buf[MSG_SIZ];
\r
4678 static SnapData sd;
\r
4679 static int peek=0;
\r
4681 switch (message) {
\r
4683 case WM_PAINT: /* message: repaint portion of window */
\r
4687 case WM_ERASEBKGND:
\r
4688 if (IsIconic(hwnd)) {
\r
4689 /* Cheat; change the message */
\r
4690 return (DefWindowProc(hwnd, WM_ICONERASEBKGND, wParam, lParam));
\r
4692 return (DefWindowProc(hwnd, message, wParam, lParam));
\r
4696 case WM_LBUTTONDOWN:
\r
4697 case WM_MBUTTONDOWN:
\r
4698 case WM_RBUTTONDOWN:
\r
4699 case WM_LBUTTONUP:
\r
4700 case WM_MBUTTONUP:
\r
4701 case WM_RBUTTONUP:
\r
4702 case WM_MOUSEMOVE:
\r
4703 case WM_MOUSEWHEEL:
\r
4704 MouseEvent(hwnd, message, wParam, lParam);
\r
4708 if((char)wParam == '\b') {
\r
4709 ForwardEvent(); peek = 0;
\r
4712 JAWS_KBUP_NAVIGATION
\r
4717 if((char)wParam == '\b') {
\r
4718 if(!peek) BackwardEvent(), peek = 1;
\r
4721 JAWS_KBDOWN_NAVIGATION
\r
4727 JAWS_ALT_INTERCEPT
\r
4729 if (appData.icsActive && ((char)wParam == '\r' || (char)wParam > ' ' && !((char)wParam >= '1' && (char)wParam <= '9'))) {
\r
4730 // [HGM] movenum: for non-zero digits we always do type-in dialog
\r
4731 HWND h = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
4732 if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);
\r
4734 SendMessage(h, message, wParam, lParam);
\r
4735 } else if(lParam != KF_REPEAT) {
\r
4736 if (isalpha((char)wParam) || isdigit((char)wParam)) {
\r
4737 TypeInEvent((char)wParam);
\r
4738 } else if((char)wParam == 003) CopyGameToClipboard();
\r
4739 else if((char)wParam == 026) PasteGameOrFENFromClipboard();
\r
4744 case WM_PALETTECHANGED:
\r
4745 if (hwnd != (HWND)wParam && !appData.monoMode) {
\r
4747 HDC hdc = GetDC(hwndMain);
\r
4748 SelectPalette(hdc, hPal, TRUE);
\r
4749 nnew = RealizePalette(hdc);
\r
4751 paletteChanged = TRUE;
\r
4752 InvalidateRect(hwnd, &boardRect, FALSE);
\r
4754 ReleaseDC(hwnd, hdc);
\r
4758 case WM_QUERYNEWPALETTE:
\r
4759 if (!appData.monoMode /*&& paletteChanged*/) {
\r
4761 HDC hdc = GetDC(hwndMain);
\r
4762 paletteChanged = FALSE;
\r
4763 SelectPalette(hdc, hPal, FALSE);
\r
4764 nnew = RealizePalette(hdc);
\r
4766 InvalidateRect(hwnd, &boardRect, FALSE);
\r
4768 ReleaseDC(hwnd, hdc);
\r
4773 case WM_COMMAND: /* message: command from application menu */
\r
4774 wmId = LOWORD(wParam);
\r
4775 wmEvent = HIWORD(wParam);
\r
4780 SAY("new game enter a move to play against the computer with white");
\r
4783 case IDM_NewGameFRC:
\r
4784 if( NewGameFRC() == 0 ) {
\r
4789 case IDM_NewVariant:
\r
4790 NewVariantPopup(hwnd);
\r
4793 case IDM_LoadGame:
\r
4794 LoadGameDialog(hwnd, _("Load Game from File"));
\r
4797 case IDM_LoadNextGame:
\r
4801 case IDM_LoadPrevGame:
\r
4805 case IDM_ReloadGame:
\r
4809 case IDM_LoadPosition:
\r
4810 if (gameMode == AnalyzeMode || gameMode == AnalyzeFile) {
\r
4811 Reset(FALSE, TRUE);
\r
4814 f = OpenFileDialog(hwnd, "rb", "",
\r
4815 appData.oldSaveStyle ? "pos" : "fen",
\r
4817 _("Load Position from File"), &number, fileTitle, NULL);
\r
4819 LoadPosition(f, number, fileTitle);
\r
4823 case IDM_LoadNextPosition:
\r
4824 ReloadPosition(1);
\r
4827 case IDM_LoadPrevPosition:
\r
4828 ReloadPosition(-1);
\r
4831 case IDM_ReloadPosition:
\r
4832 ReloadPosition(0);
\r
4835 case IDM_SaveGame:
\r
4836 defName = DefaultFileName(appData.oldSaveStyle ? "gam" : "pgn");
\r
4837 f = OpenFileDialog(hwnd, "a", defName,
\r
4838 appData.oldSaveStyle ? "gam" : "pgn",
\r
4840 _("Save Game to File"), NULL, fileTitle, NULL);
\r
4842 SaveGame(f, 0, "");
\r
4846 case IDM_SavePosition:
\r
4847 defName = DefaultFileName(appData.oldSaveStyle ? "pos" : "fen");
\r
4848 f = OpenFileDialog(hwnd, "a", defName,
\r
4849 appData.oldSaveStyle ? "pos" : "fen",
\r
4851 _("Save Position to File"), NULL, fileTitle, NULL);
\r
4853 SavePosition(f, 0, "");
\r
4857 case IDM_SaveDiagram:
\r
4858 defName = "diagram";
\r
4859 f = OpenFileDialog(hwnd, "wb", defName,
\r
4862 _("Save Diagram to File"), NULL, fileTitle, NULL);
\r
4868 case IDM_CopyGame:
\r
4869 CopyGameToClipboard();
\r
4872 case IDM_PasteGame:
\r
4873 PasteGameFromClipboard();
\r
4876 case IDM_CopyGameListToClipboard:
\r
4877 CopyGameListToClipboard();
\r
4880 /* [AS] Autodetect FEN or PGN data */
\r
4881 case IDM_PasteAny:
\r
4882 PasteGameOrFENFromClipboard();
\r
4885 /* [AS] Move history */
\r
4886 case IDM_ShowMoveHistory:
\r
4887 if( MoveHistoryIsUp() ) {
\r
4888 MoveHistoryPopDown();
\r
4891 MoveHistoryPopUp();
\r
4895 /* [AS] Eval graph */
\r
4896 case IDM_ShowEvalGraph:
\r
4897 if( EvalGraphIsUp() ) {
\r
4898 EvalGraphPopDown();
\r
4902 SetFocus(hwndMain);
\r
4906 /* [AS] Engine output */
\r
4907 case IDM_ShowEngineOutput:
\r
4908 if( EngineOutputIsUp() ) {
\r
4909 EngineOutputPopDown();
\r
4912 EngineOutputPopUp();
\r
4916 /* [AS] User adjudication */
\r
4917 case IDM_UserAdjudication_White:
\r
4918 UserAdjudicationEvent( +1 );
\r
4921 case IDM_UserAdjudication_Black:
\r
4922 UserAdjudicationEvent( -1 );
\r
4925 case IDM_UserAdjudication_Draw:
\r
4926 UserAdjudicationEvent( 0 );
\r
4929 /* [AS] Game list options dialog */
\r
4930 case IDM_GameListOptions:
\r
4931 GameListOptions();
\r
4938 case IDM_CopyPosition:
\r
4939 CopyFENToClipboard();
\r
4942 case IDM_PastePosition:
\r
4943 PasteFENFromClipboard();
\r
4946 case IDM_MailMove:
\r
4950 case IDM_ReloadCMailMsg:
\r
4951 Reset(TRUE, TRUE);
\r
4952 ReloadCmailMsgEvent(FALSE);
\r
4955 case IDM_Minimize:
\r
4956 ShowWindow(hwnd, SW_MINIMIZE);
\r
4963 case IDM_MachineWhite:
\r
4964 MachineWhiteEvent();
\r
4966 * refresh the tags dialog only if it's visible
\r
4968 if (gameMode == MachinePlaysWhite && IsWindowVisible(editTagsDialog)) {
\r
4970 tags = PGNTags(&gameInfo);
\r
4971 TagsPopUp(tags, CmailMsg());
\r
4974 SAY("computer starts playing white");
\r
4977 case IDM_MachineBlack:
\r
4978 MachineBlackEvent();
\r
4980 * refresh the tags dialog only if it's visible
\r
4982 if (gameMode == MachinePlaysBlack && IsWindowVisible(editTagsDialog)) {
\r
4984 tags = PGNTags(&gameInfo);
\r
4985 TagsPopUp(tags, CmailMsg());
\r
4988 SAY("computer starts playing black");
\r
4991 case IDM_Match: // [HGM] match: flows into next case, after setting Match Mode and nr of Games
\r
4992 MatchEvent(2); // distinguish from command-line-triggered case (matchMode=1)
\r
4995 case IDM_TwoMachines:
\r
4996 TwoMachinesEvent();
\r
4998 * refresh the tags dialog only if it's visible
\r
5000 if (gameMode == TwoMachinesPlay && IsWindowVisible(editTagsDialog)) {
\r
5002 tags = PGNTags(&gameInfo);
\r
5003 TagsPopUp(tags, CmailMsg());
\r
5006 SAY("computer starts playing both sides");
\r
5009 case IDM_AnalysisMode:
\r
5010 if(AnalyzeModeEvent()) {
\r
5011 SAY("analyzing current position");
\r
5015 case IDM_AnalyzeFile:
\r
5016 AnalyzeFileEvent();
\r
5019 case IDM_IcsClient:
\r
5023 case IDM_EditGame:
\r
5024 case IDM_EditGame2:
\r
5029 case IDM_EditPosition:
\r
5030 case IDM_EditPosition2:
\r
5031 EditPositionEvent();
\r
5032 SAY("enter a FEN string or setup a position on the board using the control R pop up menu");
\r
5035 case IDM_Training:
\r
5039 case IDM_ShowGameList:
\r
5040 ShowGameListProc();
\r
5043 case IDM_EditProgs1:
\r
5044 EditTagsPopUp(firstChessProgramNames, &firstChessProgramNames);
\r
5047 case IDM_LoadProg1:
\r
5048 LoadEnginePopUp(hwndMain, 0);
\r
5051 case IDM_LoadProg2:
\r
5052 LoadEnginePopUp(hwndMain, 1);
\r
5055 case IDM_EditServers:
\r
5056 EditTagsPopUp(icsNames, &icsNames);
\r
5059 case IDM_EditTags:
\r
5064 case IDM_EditBook:
\r
5068 case IDM_EditComment:
\r
5070 if (commentUp && editComment) {
\r
5073 EditCommentEvent();
\r
5093 case IDM_CallFlag:
\r
5113 case IDM_StopObserving:
\r
5114 StopObservingEvent();
\r
5117 case IDM_StopExamining:
\r
5118 StopExaminingEvent();
\r
5122 UploadGameEvent();
\r
5125 case IDM_TypeInMove:
\r
5126 TypeInEvent('\000');
\r
5129 case IDM_TypeInName:
\r
5130 PopUpNameDialog('\000');
\r
5133 case IDM_Backward:
\r
5135 SetFocus(hwndMain);
\r
5142 SetFocus(hwndMain);
\r
5147 SetFocus(hwndMain);
\r
5152 SetFocus(hwndMain);
\r
5155 case OPT_GameListNext: // [HGM] forward these two accelerators to Game List
\r
5156 case OPT_GameListPrev:
\r
5157 if(gameListDialog) SendMessage(gameListDialog, WM_COMMAND, wmId, 0);
\r
5161 RevertEvent(FALSE);
\r
5164 case IDM_Annotate: // [HGM] vari: revert with annotation
\r
5165 RevertEvent(TRUE);
\r
5168 case IDM_TruncateGame:
\r
5169 TruncateGameEvent();
\r
5176 case IDM_RetractMove:
\r
5177 RetractMoveEvent();
\r
5180 case IDM_FlipView:
\r
5181 flipView = !flipView;
\r
5182 DrawPosition(FALSE, NULL);
\r
5185 case IDM_FlipClock:
\r
5186 flipClock = !flipClock;
\r
5187 DisplayBothClocks();
\r
5191 case IDM_MuteSounds:
\r
5192 mute = !mute; // [HGM] mute: keep track of global muting variable
\r
5193 CheckMenuItem(GetMenu(hwndMain),IDM_MuteSounds,
\r
5194 MF_BYCOMMAND|(mute?MF_CHECKED:MF_UNCHECKED));
\r
5197 case IDM_GeneralOptions:
\r
5198 GeneralOptionsPopup(hwnd);
\r
5199 DrawPosition(TRUE, NULL);
\r
5202 case IDM_BoardOptions:
\r
5203 BoardOptionsPopup(hwnd);
\r
5206 case IDM_EnginePlayOptions:
\r
5207 EnginePlayOptionsPopup(hwnd);
\r
5210 case IDM_Engine1Options:
\r
5211 EngineOptionsPopup(hwnd, &first);
\r
5214 case IDM_Engine2Options:
\r
5216 if(WaitForEngine(&second, SettingsMenuIfReady)) break;
\r
5217 EngineOptionsPopup(hwnd, &second);
\r
5220 case IDM_OptionsUCI:
\r
5221 UciOptionsPopup(hwnd);
\r
5225 TourneyPopup(hwnd);
\r
5228 case IDM_IcsOptions:
\r
5229 IcsOptionsPopup(hwnd);
\r
5233 FontsOptionsPopup(hwnd);
\r
5237 SoundOptionsPopup(hwnd);
\r
5240 case IDM_CommPort:
\r
5241 CommPortOptionsPopup(hwnd);
\r
5244 case IDM_LoadOptions:
\r
5245 LoadOptionsPopup(hwnd);
\r
5248 case IDM_SaveOptions:
\r
5249 SaveOptionsPopup(hwnd);
\r
5252 case IDM_TimeControl:
\r
5253 TimeControlOptionsPopup(hwnd);
\r
5256 case IDM_SaveSettings:
\r
5257 SaveSettings(settingsFileName);
\r
5260 case IDM_SaveSettingsOnExit:
\r
5261 saveSettingsOnExit = !saveSettingsOnExit;
\r
5262 (void) CheckMenuItem(GetMenu(hwndMain), IDM_SaveSettingsOnExit,
\r
5263 MF_BYCOMMAND|(saveSettingsOnExit ?
\r
5264 MF_CHECKED : MF_UNCHECKED));
\r
5275 case IDM_AboutGame:
\r
5280 appData.debugMode = !appData.debugMode;
\r
5281 if (appData.debugMode) {
\r
5282 char dir[MSG_SIZ];
\r
5283 GetCurrentDirectory(MSG_SIZ, dir);
\r
5284 SetCurrentDirectory(installDir);
\r
5285 debugFP = fopen(appData.nameOfDebugFile, "w");
\r
5286 SetCurrentDirectory(dir);
\r
5287 setbuf(debugFP, NULL);
\r
5294 case IDM_HELPCONTENTS:
\r
5295 if (!MyHelp (hwnd, "winboard.hlp", HELP_KEY,(DWORD)(LPSTR)"CONTENTS") &&
\r
5296 !HtmlHelp(hwnd, "winboard.chm", 0, 0) ) {
\r
5297 MessageBox (GetFocus(),
\r
5298 _("Unable to activate help"),
\r
5299 szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);
\r
5303 case IDM_HELPSEARCH:
\r
5304 if (!MyHelp (hwnd, "winboard.hlp", HELP_PARTIALKEY, (DWORD)(LPSTR)"") &&
\r
5305 !HtmlHelp(hwnd, "winboard.chm", 0, 0) ) {
\r
5306 MessageBox (GetFocus(),
\r
5307 _("Unable to activate help"),
\r
5308 szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);
\r
5312 case IDM_HELPHELP:
\r
5313 if(!WinHelp(hwnd, (LPSTR)NULL, HELP_HELPONHELP, 0)) {
\r
5314 MessageBox (GetFocus(),
\r
5315 _("Unable to activate help"),
\r
5316 szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);
\r
5321 lpProc = MakeProcInstance((FARPROC)About, hInst);
\r
5323 (gameInfo.event && strcmp(gameInfo.event, "Easter Egg Hunt") == 0) ?
\r
5324 "AboutBox2" : "AboutBox", hwnd, (DLGPROC)lpProc);
\r
5325 FreeProcInstance(lpProc);
\r
5328 case IDM_DirectCommand1:
\r
5329 AskQuestionEvent(_("Direct Command"),
\r
5330 _("Send to chess program:"), "", "1");
\r
5332 case IDM_DirectCommand2:
\r
5333 AskQuestionEvent(_("Direct Command"),
\r
5334 _("Send to second chess program:"), "", "2");
\r
5337 case EP_WhitePawn:
\r
5338 EditPositionMenuEvent(WhitePawn, fromX, fromY);
\r
5339 fromX = fromY = -1;
\r
5342 case EP_WhiteKnight:
\r
5343 EditPositionMenuEvent(WhiteKnight, fromX, fromY);
\r
5344 fromX = fromY = -1;
\r
5347 case EP_WhiteBishop:
\r
5348 EditPositionMenuEvent(WhiteBishop, fromX, fromY);
\r
5349 fromX = fromY = -1;
\r
5352 case EP_WhiteRook:
\r
5353 EditPositionMenuEvent(WhiteRook, fromX, fromY);
\r
5354 fromX = fromY = -1;
\r
5357 case EP_WhiteQueen:
\r
5358 EditPositionMenuEvent(WhiteQueen, fromX, fromY);
\r
5359 fromX = fromY = -1;
\r
5362 case EP_WhiteFerz:
\r
5363 EditPositionMenuEvent(WhiteFerz, fromX, fromY);
\r
5364 fromX = fromY = -1;
\r
5367 case EP_WhiteWazir:
\r
5368 EditPositionMenuEvent(WhiteWazir, fromX, fromY);
\r
5369 fromX = fromY = -1;
\r
5372 case EP_WhiteAlfil:
\r
5373 EditPositionMenuEvent(WhiteAlfil, fromX, fromY);
\r
5374 fromX = fromY = -1;
\r
5377 case EP_WhiteCannon:
\r
5378 EditPositionMenuEvent(WhiteCannon, fromX, fromY);
\r
5379 fromX = fromY = -1;
\r
5382 case EP_WhiteCardinal:
\r
5383 EditPositionMenuEvent(WhiteAngel, fromX, fromY);
\r
5384 fromX = fromY = -1;
\r
5387 case EP_WhiteMarshall:
\r
5388 EditPositionMenuEvent(WhiteMarshall, fromX, fromY);
\r
5389 fromX = fromY = -1;
\r
5392 case EP_WhiteKing:
\r
5393 EditPositionMenuEvent(WhiteKing, fromX, fromY);
\r
5394 fromX = fromY = -1;
\r
5397 case EP_BlackPawn:
\r
5398 EditPositionMenuEvent(BlackPawn, fromX, fromY);
\r
5399 fromX = fromY = -1;
\r
5402 case EP_BlackKnight:
\r
5403 EditPositionMenuEvent(BlackKnight, fromX, fromY);
\r
5404 fromX = fromY = -1;
\r
5407 case EP_BlackBishop:
\r
5408 EditPositionMenuEvent(BlackBishop, fromX, fromY);
\r
5409 fromX = fromY = -1;
\r
5412 case EP_BlackRook:
\r
5413 EditPositionMenuEvent(BlackRook, fromX, fromY);
\r
5414 fromX = fromY = -1;
\r
5417 case EP_BlackQueen:
\r
5418 EditPositionMenuEvent(BlackQueen, fromX, fromY);
\r
5419 fromX = fromY = -1;
\r
5422 case EP_BlackFerz:
\r
5423 EditPositionMenuEvent(BlackFerz, fromX, fromY);
\r
5424 fromX = fromY = -1;
\r
5427 case EP_BlackWazir:
\r
5428 EditPositionMenuEvent(BlackWazir, fromX, fromY);
\r
5429 fromX = fromY = -1;
\r
5432 case EP_BlackAlfil:
\r
5433 EditPositionMenuEvent(BlackAlfil, fromX, fromY);
\r
5434 fromX = fromY = -1;
\r
5437 case EP_BlackCannon:
\r
5438 EditPositionMenuEvent(BlackCannon, fromX, fromY);
\r
5439 fromX = fromY = -1;
\r
5442 case EP_BlackCardinal:
\r
5443 EditPositionMenuEvent(BlackAngel, fromX, fromY);
\r
5444 fromX = fromY = -1;
\r
5447 case EP_BlackMarshall:
\r
5448 EditPositionMenuEvent(BlackMarshall, fromX, fromY);
\r
5449 fromX = fromY = -1;
\r
5452 case EP_BlackKing:
\r
5453 EditPositionMenuEvent(BlackKing, fromX, fromY);
\r
5454 fromX = fromY = -1;
\r
5457 case EP_EmptySquare:
\r
5458 EditPositionMenuEvent(EmptySquare, fromX, fromY);
\r
5459 fromX = fromY = -1;
\r
5462 case EP_ClearBoard:
\r
5463 EditPositionMenuEvent(ClearBoard, fromX, fromY);
\r
5464 fromX = fromY = -1;
\r
5468 EditPositionMenuEvent(WhitePlay, fromX, fromY);
\r
5469 fromX = fromY = -1;
\r
5473 EditPositionMenuEvent(BlackPlay, fromX, fromY);
\r
5474 fromX = fromY = -1;
\r
5478 EditPositionMenuEvent(PromotePiece, fromX, fromY);
\r
5479 fromX = fromY = -1;
\r
5483 EditPositionMenuEvent(DemotePiece, fromX, fromY);
\r
5484 fromX = fromY = -1;
\r
5488 DropMenuEvent(WhitePawn, fromX, fromY);
\r
5489 fromX = fromY = -1;
\r
5493 DropMenuEvent(WhiteKnight, fromX, fromY);
\r
5494 fromX = fromY = -1;
\r
5498 DropMenuEvent(WhiteBishop, fromX, fromY);
\r
5499 fromX = fromY = -1;
\r
5503 DropMenuEvent(WhiteRook, fromX, fromY);
\r
5504 fromX = fromY = -1;
\r
5508 DropMenuEvent(WhiteQueen, fromX, fromY);
\r
5509 fromX = fromY = -1;
\r
5513 barbaric = 0; appData.language = "";
\r
5514 TranslateMenus(0);
\r
5515 CheckMenuItem(GetMenu(hwndMain), lastChecked, MF_BYCOMMAND|MF_UNCHECKED);
\r
5516 CheckMenuItem(GetMenu(hwndMain), IDM_English, MF_BYCOMMAND|MF_CHECKED);
\r
5517 lastChecked = wmId;
\r
5521 if(wmId >= IDM_RecentEngines && wmId < IDM_RecentEngines + appData.recentEngines)
\r
5522 RecentEngineEvent(wmId - IDM_RecentEngines);
\r
5524 if(wmId > IDM_English && wmId < IDM_English+20) {
\r
5525 LoadLanguageFile(languageFile[wmId - IDM_English - 1]);
\r
5526 TranslateMenus(0);
\r
5527 CheckMenuItem(GetMenu(hwndMain), lastChecked, MF_BYCOMMAND|MF_UNCHECKED);
\r
5528 CheckMenuItem(GetMenu(hwndMain), wmId, MF_BYCOMMAND|MF_CHECKED);
\r
5529 lastChecked = wmId;
\r
5532 return (DefWindowProc(hwnd, message, wParam, lParam));
\r
5538 case CLOCK_TIMER_ID:
\r
5539 KillTimer(hwnd, clockTimerEvent); /* Simulate one-shot timer as in X */
\r
5540 clockTimerEvent = 0;
\r
5541 DecrementClocks(); /* call into back end */
\r
5543 case LOAD_GAME_TIMER_ID:
\r
5544 KillTimer(hwnd, loadGameTimerEvent); /* Simulate one-shot timer as in X*/
\r
5545 loadGameTimerEvent = 0;
\r
5546 AutoPlayGameLoop(); /* call into back end */
\r
5548 case ANALYSIS_TIMER_ID:
\r
5549 if ((gameMode == AnalyzeMode || gameMode == AnalyzeFile
\r
5550 || appData.icsEngineAnalyze) && appData.periodicUpdates) {
\r
5551 AnalysisPeriodicEvent(0);
\r
5553 KillTimer(hwnd, analysisTimerEvent);
\r
5554 analysisTimerEvent = 0;
\r
5557 case DELAYED_TIMER_ID:
\r
5558 KillTimer(hwnd, delayedTimerEvent);
\r
5559 delayedTimerEvent = 0;
\r
5560 delayedTimerCallback();
\r
5565 case WM_USER_Input:
\r
5566 InputEvent(hwnd, message, wParam, lParam);
\r
5569 /* [AS] Also move "attached" child windows */
\r
5570 case WM_WINDOWPOSCHANGING:
\r
5572 if( hwnd == hwndMain && appData.useStickyWindows ) {
\r
5573 LPWINDOWPOS lpwp = (LPWINDOWPOS) lParam;
\r
5575 if( ((lpwp->flags & SWP_NOMOVE) == 0) && ((lpwp->flags & SWP_NOSIZE) != 0) ) {
\r
5576 /* Window is moving */
\r
5579 // GetWindowRect( hwnd, &rcMain ); //[HGM] sticky: in XP this returned new position, not old
\r
5580 rcMain.left = wpMain.x; // replace by these 4 lines to reconstruct old rect
\r
5581 rcMain.right = wpMain.x + wpMain.width;
\r
5582 rcMain.top = wpMain.y;
\r
5583 rcMain.bottom = wpMain.y + wpMain.height;
\r
5585 ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, moveHistoryDialog, &wpMoveHistory );
\r
5586 ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, evalGraphDialog, &wpEvalGraph );
\r
5587 ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, engineOutputDialog, &wpEngineOutput );
\r
5588 ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, gameListDialog, &wpGameList );
\r
5589 ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, hwndConsole, &wpConsole );
\r
5590 wpMain.x = lpwp->x;
\r
5591 wpMain.y = lpwp->y;
\r
5596 /* [AS] Snapping */
\r
5597 case WM_ENTERSIZEMOVE:
\r
5598 if(appData.debugMode) { fprintf(debugFP, "size-move\n"); }
\r
5599 if (hwnd == hwndMain) {
\r
5600 doingSizing = TRUE;
\r
5603 return OnEnterSizeMove( &sd, hwnd, wParam, lParam );
\r
5607 if(appData.debugMode) { fprintf(debugFP, "sizing\n"); }
\r
5608 if (hwnd == hwndMain) {
\r
5609 lastSizing = wParam;
\r
5614 if(appData.debugMode) { fprintf(debugFP, "moving\n"); }
\r
5615 return OnMoving( &sd, hwnd, wParam, lParam );
\r
5617 case WM_EXITSIZEMOVE:
\r
5618 if(appData.debugMode) { fprintf(debugFP, "exit size-move, size = %d\n", squareSize); }
\r
5619 if (hwnd == hwndMain) {
\r
5621 doingSizing = FALSE;
\r
5622 InvalidateRect(hwnd, &boardRect, FALSE);
\r
5623 GetClientRect(hwnd, &client);
\r
5624 ResizeBoard(client.right, client.bottom, lastSizing);
\r
5626 if(appData.debugMode) { fprintf(debugFP, "square size = %d\n", squareSize); }
\r
5628 return OnExitSizeMove( &sd, hwnd, wParam, lParam );
\r
5631 case WM_DESTROY: /* message: window being destroyed */
\r
5632 PostQuitMessage(0);
\r
5636 if (hwnd == hwndMain) {
\r
5641 default: /* Passes it on if unprocessed */
\r
5642 return (DefWindowProc(hwnd, message, wParam, lParam));
\r
5647 /*---------------------------------------------------------------------------*\
\r
5649 * Misc utility routines
\r
5651 \*---------------------------------------------------------------------------*/
\r
5654 * Decent random number generator, at least not as bad as Windows
\r
5655 * standard rand, which returns a value in the range 0 to 0x7fff.
\r
5657 unsigned int randstate;
\r
5662 randstate = randstate * 1664525 + 1013904223;
\r
5663 return (int) randstate & 0x7fffffff;
\r
5667 mysrandom(unsigned int seed)
\r
5674 * returns TRUE if user selects a different color, FALSE otherwise
\r
5678 ChangeColor(HWND hwnd, COLORREF *which)
\r
5680 static BOOL firstTime = TRUE;
\r
5681 static DWORD customColors[16];
\r
5683 COLORREF newcolor;
\r
5688 /* Make initial colors in use available as custom colors */
\r
5689 /* Should we put the compiled-in defaults here instead? */
\r
5691 customColors[i++] = lightSquareColor & 0xffffff;
\r
5692 customColors[i++] = darkSquareColor & 0xffffff;
\r
5693 customColors[i++] = whitePieceColor & 0xffffff;
\r
5694 customColors[i++] = blackPieceColor & 0xffffff;
\r
5695 customColors[i++] = highlightSquareColor & 0xffffff;
\r
5696 customColors[i++] = premoveHighlightColor & 0xffffff;
\r
5698 for (ccl = (ColorClass) 0; ccl < NColorClasses && i < 16; ccl++) {
\r
5699 customColors[i++] = textAttribs[ccl].color;
\r
5701 while (i < 16) customColors[i++] = RGB(255, 255, 255);
\r
5702 firstTime = FALSE;
\r
5705 cc.lStructSize = sizeof(cc);
\r
5706 cc.hwndOwner = hwnd;
\r
5707 cc.hInstance = NULL;
\r
5708 cc.rgbResult = (DWORD) (*which & 0xffffff);
\r
5709 cc.lpCustColors = (LPDWORD) customColors;
\r
5710 cc.Flags = CC_RGBINIT|CC_FULLOPEN;
\r
5712 if (!ChooseColor(&cc)) return FALSE;
\r
5714 newcolor = (COLORREF) (0x2000000 | cc.rgbResult);
\r
5715 if (newcolor == *which) return FALSE;
\r
5716 *which = newcolor;
\r
5720 InitDrawingColors();
\r
5721 InvalidateRect(hwnd, &boardRect, FALSE);
\r
5726 MyLoadSound(MySound *ms)
\r
5732 if (ms->data && ms->flag) free(ms->data);
\r
5735 switch (ms->name[0]) {
\r
5741 /* System sound from Control Panel. Don't preload here. */
\r
5745 if (ms->name[1] == NULLCHAR) {
\r
5746 /* "!" alone = silence */
\r
5749 /* Builtin wave resource. Error if not found. */
\r
5750 HANDLE h = FindResource(hInst, ms->name + 1, "WAVE");
\r
5751 if (h == NULL) break;
\r
5752 ms->data = (void *)LoadResource(hInst, h);
\r
5753 ms->flag = 0; // not maloced, so cannot be freed!
\r
5754 if (h == NULL) break;
\r
5759 /* .wav file. Error if not found. */
\r
5760 f = fopen(ms->name, "rb");
\r
5761 if (f == NULL) break;
\r
5762 if (fstat(fileno(f), &st) < 0) break;
\r
5763 ms->data = malloc(st.st_size);
\r
5765 if (fread(ms->data, st.st_size, 1, f) < 1) break;
\r
5771 char buf[MSG_SIZ];
\r
5772 snprintf(buf, MSG_SIZ, _("Error loading sound %s"), ms->name);
\r
5773 DisplayError(buf, GetLastError());
\r
5779 MyPlaySound(MySound *ms)
\r
5781 BOOLEAN ok = FALSE;
\r
5783 if(mute) return TRUE; // [HGM] mute: suppress all sound play when muted
\r
5784 switch (ms->name[0]) {
\r
5786 if(appData.debugMode) fprintf(debugFP, "silence\n");
\r
5791 /* System sound from Control Panel (deprecated feature).
\r
5792 "$" alone or an unset sound name gets default beep (still in use). */
\r
5793 if (ms->name[1]) {
\r
5794 ok = PlaySound(ms->name + 1, NULL, SND_ALIAS|SND_ASYNC);
\r
5796 if (!ok) ok = MessageBeep(MB_OK);
\r
5799 /* Builtin wave resource, or "!" alone for silence */
\r
5800 if (ms->name[1]) {
\r
5801 if (ms->data == NULL) return FALSE;
\r
5802 ok = PlaySound(ms->data, NULL, SND_MEMORY|SND_ASYNC);
\r
5808 /* .wav file. Error if not found. */
\r
5809 if (ms->data == NULL) return FALSE;
\r
5810 ok = PlaySound(ms->data, NULL, SND_MEMORY|SND_ASYNC);
\r
5813 /* Don't print an error: this can happen innocently if the sound driver
\r
5814 is busy; for instance, if another instance of WinBoard is playing
\r
5815 a sound at about the same time. */
\r
5821 OldOpenFileHook(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
5824 OPENFILENAME *ofn;
\r
5825 static UINT *number; /* gross that this is static */
\r
5827 switch (message) {
\r
5828 case WM_INITDIALOG: /* message: initialize dialog box */
\r
5829 /* Center the dialog over the application window */
\r
5830 ofn = (OPENFILENAME *) lParam;
\r
5831 if (ofn->Flags & OFN_ENABLETEMPLATE) {
\r
5832 number = (UINT *) ofn->lCustData;
\r
5833 SendMessage(GetDlgItem(hDlg, edt2), WM_SETTEXT, 0, (LPARAM) "");
\r
5837 CenterWindow (hDlg, GetWindow (hDlg, GW_OWNER));
\r
5838 Translate(hDlg, 1536);
\r
5839 return FALSE; /* Allow for further processing */
\r
5842 if ((LOWORD(wParam) == IDOK) && (number != NULL)) {
\r
5843 *number = GetDlgItemInt(hDlg, OPT_IndexNumberOld, &ok, FALSE);
\r
5845 return FALSE; /* Allow for further processing */
\r
5851 OpenFileHook(HWND hdlg, UINT uiMsg, WPARAM wParam, LPARAM lParam)
\r
5853 static UINT *number;
\r
5854 OPENFILENAME *ofname;
\r
5857 case WM_INITDIALOG:
\r
5858 Translate(hdlg, DLG_IndexNumber);
\r
5859 ofname = (OPENFILENAME *)lParam;
\r
5860 number = (UINT *)(ofname->lCustData);
\r
5863 ofnot = (OFNOTIFY *)lParam;
\r
5864 if (ofnot->hdr.code == CDN_FILEOK) {
\r
5865 *number = GetDlgItemInt(hdlg, OPT_IndexNumber, NULL, FALSE);
\r
5874 OpenFileDialog(HWND hwnd, char *write, char *defName, char *defExt, // [HGM] diag: type of 'write' now string
\r
5875 char *nameFilt, char *dlgTitle, UINT *number,
\r
5876 char fileTitle[MSG_SIZ], char fileName[MSG_SIZ])
\r
5878 OPENFILENAME openFileName;
\r
5879 char buf1[MSG_SIZ];
\r
5882 if (fileName == NULL) fileName = buf1;
\r
5883 if (defName == NULL) {
\r
5884 safeStrCpy(fileName, "*.", 3 );
\r
5885 strcat(fileName, defExt);
\r
5887 safeStrCpy(fileName, defName, MSG_SIZ );
\r
5889 if (fileTitle) safeStrCpy(fileTitle, "", MSG_SIZ );
\r
5890 if (number) *number = 0;
\r
5892 openFileName.lStructSize = sizeof(OPENFILENAME);
\r
5893 openFileName.hwndOwner = hwnd;
\r
5894 openFileName.hInstance = (HANDLE) hInst;
\r
5895 openFileName.lpstrFilter = nameFilt;
\r
5896 openFileName.lpstrCustomFilter = (LPSTR) NULL;
\r
5897 openFileName.nMaxCustFilter = 0L;
\r
5898 openFileName.nFilterIndex = 1L;
\r
5899 openFileName.lpstrFile = fileName;
\r
5900 openFileName.nMaxFile = MSG_SIZ;
\r
5901 openFileName.lpstrFileTitle = fileTitle;
\r
5902 openFileName.nMaxFileTitle = fileTitle ? MSG_SIZ : 0;
\r
5903 openFileName.lpstrInitialDir = NULL;
\r
5904 openFileName.lpstrTitle = dlgTitle;
\r
5905 openFileName.Flags = OFN_PATHMUSTEXIST | OFN_HIDEREADONLY
\r
5906 | (write[0] != 'r' ? 0 : OFN_FILEMUSTEXIST)
\r
5907 | (number ? OFN_ENABLETEMPLATE | OFN_ENABLEHOOK: 0)
\r
5908 | (oldDialog ? 0 : OFN_EXPLORER);
\r
5909 openFileName.nFileOffset = 0;
\r
5910 openFileName.nFileExtension = 0;
\r
5911 openFileName.lpstrDefExt = defExt;
\r
5912 openFileName.lCustData = (LONG) number;
\r
5913 openFileName.lpfnHook = oldDialog ?
\r
5914 (LPOFNHOOKPROC) OldOpenFileHook : (LPOFNHOOKPROC) OpenFileHook;
\r
5915 openFileName.lpTemplateName = (LPSTR)(oldDialog ? 1536 : DLG_IndexNumber);
\r
5917 if (write[0] != 'r' ? GetSaveFileName(&openFileName) :
\r
5918 GetOpenFileName(&openFileName)) {
\r
5919 /* open the file */
\r
5920 f = fopen(openFileName.lpstrFile, write);
\r
5922 MessageBox(hwnd, _("File open failed"), NULL,
\r
5923 MB_OK|MB_ICONEXCLAMATION);
\r
5927 int err = CommDlgExtendedError();
\r
5928 if (err != 0) DisplayError(_("Internal error in file dialog box"), err);
\r
5937 MenuPopup(HWND hwnd, POINT pt, HMENU hmenu, UINT def)
\r
5939 HMENU hmenuTrackPopup; /* floating pop-up menu */
\r
5942 * Get the first pop-up menu in the menu template. This is the
\r
5943 * menu that TrackPopupMenu displays.
\r
5945 hmenuTrackPopup = GetSubMenu(hmenu, 0);
\r
5946 TranslateOneMenu(10, hmenuTrackPopup);
\r
5948 SetMenuDefaultItem(hmenuTrackPopup, def, FALSE);
\r
5951 * TrackPopup uses screen coordinates, so convert the
\r
5952 * coordinates of the mouse click to screen coordinates.
\r
5954 ClientToScreen(hwnd, (LPPOINT) &pt);
\r
5956 /* Draw and track the floating pop-up menu. */
\r
5957 TrackPopupMenu(hmenuTrackPopup, TPM_CENTERALIGN | TPM_RIGHTBUTTON,
\r
5958 pt.x, pt.y, 0, hwnd, NULL);
\r
5960 /* Destroy the menu.*/
\r
5961 DestroyMenu(hmenu);
\r
5966 int sizeX, sizeY, newSizeX, newSizeY;
\r
5968 } ResizeEditPlusButtonsClosure;
\r
5971 ResizeEditPlusButtonsCallback(HWND hChild, LPARAM lparam)
\r
5973 ResizeEditPlusButtonsClosure *cl = (ResizeEditPlusButtonsClosure *)lparam;
\r
5977 if (hChild == cl->hText) return TRUE;
\r
5978 GetWindowRect(hChild, &rect); /* gives screen coords */
\r
5979 pt.x = rect.left + (cl->newSizeX - cl->sizeX)/2;
\r
5980 pt.y = rect.top + cl->newSizeY - cl->sizeY;
\r
5981 ScreenToClient(cl->hDlg, &pt);
\r
5982 cl->hdwp = DeferWindowPos(cl->hdwp, hChild, NULL,
\r
5983 pt.x, pt.y, rect.right - rect.left, rect.bottom - rect.top, SWP_NOZORDER);
\r
5987 /* Resize a dialog that has a (rich) edit field filling most of
\r
5988 the top, with a row of buttons below */
\r
5990 ResizeEditPlusButtons(HWND hDlg, HWND hText, int sizeX, int sizeY, int newSizeX, int newSizeY)
\r
5993 int newTextHeight, newTextWidth;
\r
5994 ResizeEditPlusButtonsClosure cl;
\r
5996 /*if (IsIconic(hDlg)) return;*/
\r
5997 if (newSizeX == sizeX && newSizeY == sizeY) return;
\r
5999 cl.hdwp = BeginDeferWindowPos(8);
\r
6001 GetWindowRect(hText, &rectText); /* gives screen coords */
\r
6002 newTextWidth = rectText.right - rectText.left + newSizeX - sizeX;
\r
6003 newTextHeight = rectText.bottom - rectText.top + newSizeY - sizeY;
\r
6004 if (newTextHeight < 0) {
\r
6005 newSizeY += -newTextHeight;
\r
6006 newTextHeight = 0;
\r
6008 cl.hdwp = DeferWindowPos(cl.hdwp, hText, NULL, 0, 0,
\r
6009 newTextWidth, newTextHeight, SWP_NOZORDER|SWP_NOMOVE);
\r
6015 cl.newSizeX = newSizeX;
\r
6016 cl.newSizeY = newSizeY;
\r
6017 EnumChildWindows(hDlg, ResizeEditPlusButtonsCallback, (LPARAM)&cl);
\r
6019 EndDeferWindowPos(cl.hdwp);
\r
6022 BOOL CenterWindowEx(HWND hwndChild, HWND hwndParent, int mode)
\r
6024 RECT rChild, rParent;
\r
6025 int wChild, hChild, wParent, hParent;
\r
6026 int wScreen, hScreen, xNew, yNew;
\r
6029 /* Get the Height and Width of the child window */
\r
6030 GetWindowRect (hwndChild, &rChild);
\r
6031 wChild = rChild.right - rChild.left;
\r
6032 hChild = rChild.bottom - rChild.top;
\r
6034 /* Get the Height and Width of the parent window */
\r
6035 GetWindowRect (hwndParent, &rParent);
\r
6036 wParent = rParent.right - rParent.left;
\r
6037 hParent = rParent.bottom - rParent.top;
\r
6039 /* Get the display limits */
\r
6040 hdc = GetDC (hwndChild);
\r
6041 wScreen = GetDeviceCaps (hdc, HORZRES);
\r
6042 hScreen = GetDeviceCaps (hdc, VERTRES);
\r
6043 ReleaseDC(hwndChild, hdc);
\r
6045 /* Calculate new X position, then adjust for screen */
\r
6046 xNew = rParent.left + ((wParent - wChild) /2);
\r
6049 } else if ((xNew+wChild) > wScreen) {
\r
6050 xNew = wScreen - wChild;
\r
6053 /* Calculate new Y position, then adjust for screen */
\r
6055 yNew = rParent.top + ((hParent - hChild) /2);
\r
6058 yNew = rParent.top + GetSystemMetrics( SM_CYCAPTION ) * 2 / 3;
\r
6063 } else if ((yNew+hChild) > hScreen) {
\r
6064 yNew = hScreen - hChild;
\r
6067 /* Set it, and return */
\r
6068 return SetWindowPos (hwndChild, NULL,
\r
6069 xNew, yNew, 0, 0, SWP_NOSIZE | SWP_NOZORDER);
\r
6072 /* Center one window over another */
\r
6073 BOOL CenterWindow (HWND hwndChild, HWND hwndParent)
\r
6075 return CenterWindowEx( hwndChild, hwndParent, 0 );
\r
6078 /*---------------------------------------------------------------------------*\
\r
6080 * Startup Dialog functions
\r
6082 \*---------------------------------------------------------------------------*/
\r
6084 InitComboStrings(HANDLE hwndCombo, char **cd)
\r
6086 SendMessage(hwndCombo, CB_RESETCONTENT, 0, 0);
\r
6088 while (*cd != NULL) {
\r
6089 SendMessage(hwndCombo, CB_ADDSTRING, 0, (LPARAM) T_(*cd));
\r
6095 InitComboStringsFromOption(HANDLE hwndCombo, char *str)
\r
6097 char buf1[MAX_ARG_LEN];
\r
6100 if (str[0] == '@') {
\r
6101 FILE* f = fopen(str + 1, "r");
\r
6103 DisplayFatalError(str + 1, errno, 2);
\r
6106 len = fread(buf1, 1, sizeof(buf1)-1, f);
\r
6108 buf1[len] = NULLCHAR;
\r
6112 SendMessage(hwndCombo, CB_RESETCONTENT, 0, 0);
\r
6115 char buf[MSG_SIZ];
\r
6116 char *end = strchr(str, '\n');
\r
6117 if (end == NULL) return;
\r
6118 memcpy(buf, str, end - str);
\r
6119 buf[end - str] = NULLCHAR;
\r
6120 SendMessage(hwndCombo, CB_ADDSTRING, 0, (LPARAM) buf);
\r
6126 SetStartupDialogEnables(HWND hDlg)
\r
6128 EnableWindow(GetDlgItem(hDlg, OPT_ChessEngineName),
\r
6129 IsDlgButtonChecked(hDlg, OPT_ChessEngine) ||
\r
6130 (appData.zippyPlay && IsDlgButtonChecked(hDlg, OPT_ChessServer)));
\r
6131 EnableWindow(GetDlgItem(hDlg, OPT_SecondChessEngineName),
\r
6132 IsDlgButtonChecked(hDlg, OPT_ChessEngine));
\r
6133 EnableWindow(GetDlgItem(hDlg, OPT_ChessServerName),
\r
6134 IsDlgButtonChecked(hDlg, OPT_ChessServer));
\r
6135 EnableWindow(GetDlgItem(hDlg, OPT_AdditionalOptions),
\r
6136 IsDlgButtonChecked(hDlg, OPT_AnyAdditional));
\r
6137 EnableWindow(GetDlgItem(hDlg, IDOK),
\r
6138 IsDlgButtonChecked(hDlg, OPT_ChessEngine) ||
\r
6139 IsDlgButtonChecked(hDlg, OPT_ChessServer) ||
\r
6140 IsDlgButtonChecked(hDlg, OPT_View));
\r
6144 QuoteForFilename(char *filename)
\r
6146 int dquote, space;
\r
6147 dquote = strchr(filename, '"') != NULL;
\r
6148 space = strchr(filename, ' ') != NULL;
\r
6149 if (dquote || space) {
\r
6161 InitEngineBox(HWND hDlg, HWND hwndCombo, char* nthcp, char* nthd, char* nthdir, char *nthnames)
\r
6163 char buf[MSG_SIZ];
\r
6166 InitComboStringsFromOption(hwndCombo, nthnames);
\r
6167 q = QuoteForFilename(nthcp);
\r
6168 snprintf(buf, MSG_SIZ, "%s%s%s", q, nthcp, q);
\r
6169 if (*nthdir != NULLCHAR) {
\r
6170 q = QuoteForFilename(nthdir);
\r
6171 snprintf(buf + strlen(buf), MSG_SIZ, " /%s=%s%s%s", nthd, q, nthdir, q);
\r
6173 if (*nthcp == NULLCHAR) {
\r
6174 SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) 0, (LPARAM) 0);
\r
6175 } else if (SendMessage(hwndCombo, CB_SELECTSTRING, (WPARAM) -1, (LPARAM) buf) == CB_ERR) {
\r
6176 SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) -1, (LPARAM) 0);
\r
6177 SendMessage(hwndCombo, WM_SETTEXT, (WPARAM) 0, (LPARAM) buf);
\r
6182 StartupDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
6184 char buf[MSG_SIZ];
\r
6188 switch (message) {
\r
6189 case WM_INITDIALOG:
\r
6190 /* Center the dialog */
\r
6191 CenterWindow (hDlg, GetDesktopWindow());
\r
6192 Translate(hDlg, DLG_Startup);
\r
6193 /* Initialize the dialog items */
\r
6194 InitEngineBox(hDlg, GetDlgItem(hDlg, OPT_ChessEngineName),
\r
6195 appData.firstChessProgram, "fd", appData.firstDirectory,
\r
6196 firstChessProgramNames);
\r
6197 InitEngineBox(hDlg, GetDlgItem(hDlg, OPT_SecondChessEngineName),
\r
6198 appData.secondChessProgram, singleList ? "fd" : "sd", appData.secondDirectory,
\r
6199 singleList ? firstChessProgramNames : secondChessProgramNames); //[HGM] single: use first list in second combo
\r
6200 hwndCombo = GetDlgItem(hDlg, OPT_ChessServerName);
\r
6201 InitComboStringsFromOption(hwndCombo, icsNames);
\r
6202 snprintf(buf, MSG_SIZ, "%s /icsport=%s", appData.icsHost, appData.icsPort);
\r
6203 if (*appData.icsHelper != NULLCHAR) {
\r
6204 char *q = QuoteForFilename(appData.icsHelper);
\r
6205 sprintf(buf + strlen(buf), " /icshelper=%s%s%s", q, appData.icsHelper, q);
\r
6207 if (*appData.icsHost == NULLCHAR) {
\r
6208 SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) 0, (LPARAM) 0);
\r
6209 /*SendMessage(hwndCombo, CB_SHOWDROPDOWN, (WPARAM) TRUE, (LPARAM) 0); !!too soon */
\r
6210 } else if (SendMessage(hwndCombo, CB_SELECTSTRING, (WPARAM) -1, (LPARAM) buf) == CB_ERR) {
\r
6211 SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) -1, (LPARAM) 0);
\r
6212 SendMessage(hwndCombo, WM_SETTEXT, (WPARAM) 0, (LPARAM) buf);
\r
6215 if (appData.icsActive) {
\r
6216 CheckDlgButton(hDlg, OPT_ChessServer, BST_CHECKED);
\r
6218 else if (appData.noChessProgram) {
\r
6219 CheckDlgButton(hDlg, OPT_View, BST_CHECKED);
\r
6222 CheckDlgButton(hDlg, OPT_ChessEngine, BST_CHECKED);
\r
6225 SetStartupDialogEnables(hDlg);
\r
6229 switch (LOWORD(wParam)) {
\r
6231 if (IsDlgButtonChecked(hDlg, OPT_ChessEngine)) {
\r
6232 safeStrCpy(buf, "/fcp=", sizeof(buf)/sizeof(buf[0]) );
\r
6233 GetDlgItemText(hDlg, OPT_ChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));
\r
6235 comboLine = strdup(p+5); // [HGM] recent: remember complete line of first combobox
\r
6236 ParseArgs(StringGet, &p);
\r
6237 safeStrCpy(buf, singleList ? "/fcp=" : "/scp=", sizeof(buf)/sizeof(buf[0]) );
\r
6238 GetDlgItemText(hDlg, OPT_SecondChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));
\r
6240 SwapEngines(singleList); // temporarily swap first and second, to load a second 'first', ...
\r
6241 ParseArgs(StringGet, &p);
\r
6242 SwapEngines(singleList); // ... and then make it 'second'
\r
6243 appData.noChessProgram = FALSE;
\r
6244 appData.icsActive = FALSE;
\r
6245 } else if (IsDlgButtonChecked(hDlg, OPT_ChessServer)) {
\r
6246 safeStrCpy(buf, "/ics /icshost=", sizeof(buf)/sizeof(buf[0]) );
\r
6247 GetDlgItemText(hDlg, OPT_ChessServerName, buf + strlen(buf), sizeof(buf) - strlen(buf));
\r
6249 ParseArgs(StringGet, &p);
\r
6250 if (appData.zippyPlay) {
\r
6251 safeStrCpy(buf, "/fcp=", sizeof(buf)/sizeof(buf[0]) );
\r
6252 GetDlgItemText(hDlg, OPT_ChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));
\r
6254 ParseArgs(StringGet, &p);
\r
6256 } else if (IsDlgButtonChecked(hDlg, OPT_View)) {
\r
6257 appData.noChessProgram = TRUE;
\r
6258 appData.icsActive = FALSE;
\r
6260 MessageBox(hDlg, _("Choose an option, or cancel to exit"),
\r
6261 _("Option Error"), MB_OK|MB_ICONEXCLAMATION);
\r
6264 if (IsDlgButtonChecked(hDlg, OPT_AnyAdditional)) {
\r
6265 GetDlgItemText(hDlg, OPT_AdditionalOptions, buf, sizeof(buf));
\r
6267 ParseArgs(StringGet, &p);
\r
6269 EndDialog(hDlg, TRUE);
\r
6276 case IDM_HELPCONTENTS:
\r
6277 if (!WinHelp (hDlg, "winboard.hlp", HELP_KEY,(DWORD)(LPSTR)"CONTENTS")) {
\r
6278 MessageBox (GetFocus(),
\r
6279 _("Unable to activate help"),
\r
6280 szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);
\r
6285 SetStartupDialogEnables(hDlg);
\r
6293 /*---------------------------------------------------------------------------*\
\r
6295 * About box dialog functions
\r
6297 \*---------------------------------------------------------------------------*/
\r
6299 /* Process messages for "About" dialog box */
\r
6301 About(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
6303 switch (message) {
\r
6304 case WM_INITDIALOG: /* message: initialize dialog box */
\r
6305 /* Center the dialog over the application window */
\r
6306 CenterWindow (hDlg, GetWindow (hDlg, GW_OWNER));
\r
6307 SetDlgItemText(hDlg, ABOUTBOX_Version, programVersion);
\r
6308 Translate(hDlg, ABOUTBOX);
\r
6312 case WM_COMMAND: /* message: received a command */
\r
6313 if (LOWORD(wParam) == IDOK /* "OK" box selected? */
\r
6314 || LOWORD(wParam) == IDCANCEL) { /* System menu close command? */
\r
6315 EndDialog(hDlg, TRUE); /* Exit the dialog */
\r
6323 /*---------------------------------------------------------------------------*\
\r
6325 * Comment Dialog functions
\r
6327 \*---------------------------------------------------------------------------*/
\r
6330 CommentDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
6332 static HANDLE hwndText = NULL;
\r
6333 int len, newSizeX, newSizeY, flags;
\r
6334 static int sizeX, sizeY;
\r
6339 switch (message) {
\r
6340 case WM_INITDIALOG: /* message: initialize dialog box */
\r
6341 /* Initialize the dialog items */
\r
6342 Translate(hDlg, DLG_EditComment);
\r
6343 hwndText = GetDlgItem(hDlg, OPT_CommentText);
\r
6344 SetDlgItemText(hDlg, OPT_CommentText, commentText);
\r
6345 EnableWindow(GetDlgItem(hDlg, OPT_CancelComment), editComment);
\r
6346 EnableWindow(GetDlgItem(hDlg, OPT_ClearComment), editComment);
\r
6347 EnableWindow(GetDlgItem(hDlg, OPT_EditComment), !editComment);
\r
6348 SendMessage(hwndText, EM_SETREADONLY, !editComment, 0);
\r
6349 SetWindowText(hDlg, commentTitle);
\r
6350 if (editComment) {
\r
6351 SetFocus(hwndText);
\r
6353 SetFocus(GetDlgItem(hDlg, IDOK));
\r
6355 SendMessage(GetDlgItem(hDlg, OPT_CommentText),
\r
6356 WM_SETFONT, (WPARAM)font[boardSize][COMMENT_FONT]->hf,
\r
6357 MAKELPARAM(FALSE, 0));
\r
6358 /* Size and position the dialog */
\r
6359 if (!commentDialog) {
\r
6360 commentDialog = hDlg;
\r
6361 flags = SWP_NOZORDER;
\r
6362 GetClientRect(hDlg, &rect);
\r
6363 sizeX = rect.right;
\r
6364 sizeY = rect.bottom;
\r
6365 if (wpComment.x != CW_USEDEFAULT && wpComment.y != CW_USEDEFAULT &&
\r
6366 wpComment.width != CW_USEDEFAULT && wpComment.height != CW_USEDEFAULT) {
\r
6367 WINDOWPLACEMENT wp;
\r
6368 EnsureOnScreen(&wpComment.x, &wpComment.y, 0, 0);
\r
6369 wp.length = sizeof(WINDOWPLACEMENT);
\r
6371 wp.showCmd = SW_SHOW;
\r
6372 wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;
\r
6373 wp.rcNormalPosition.left = wpComment.x;
\r
6374 wp.rcNormalPosition.right = wpComment.x + wpComment.width;
\r
6375 wp.rcNormalPosition.top = wpComment.y;
\r
6376 wp.rcNormalPosition.bottom = wpComment.y + wpComment.height;
\r
6377 SetWindowPlacement(hDlg, &wp);
\r
6379 GetClientRect(hDlg, &rect);
\r
6380 newSizeX = rect.right;
\r
6381 newSizeY = rect.bottom;
\r
6382 ResizeEditPlusButtons(hDlg, hwndText, sizeX, sizeY,
\r
6383 newSizeX, newSizeY);
\r
6388 SendDlgItemMessage( hDlg, OPT_CommentText, EM_SETEVENTMASK, 0, ENM_MOUSEEVENTS | ENM_KEYEVENTS );
\r
6391 case WM_COMMAND: /* message: received a command */
\r
6392 switch (LOWORD(wParam)) {
\r
6394 if (editComment) {
\r
6396 /* Read changed options from the dialog box */
\r
6397 hwndText = GetDlgItem(hDlg, OPT_CommentText);
\r
6398 len = GetWindowTextLength(hwndText);
\r
6399 str = (char *) malloc(len + 1);
\r
6400 GetWindowText(hwndText, str, len + 1);
\r
6409 ReplaceComment(commentIndex, str);
\r
6416 case OPT_CancelComment:
\r
6420 case OPT_ClearComment:
\r
6421 SetDlgItemText(hDlg, OPT_CommentText, "");
\r
6424 case OPT_EditComment:
\r
6425 EditCommentEvent();
\r
6433 case WM_NOTIFY: // [HGM] vari: cloned from whistory.c
\r
6434 if( wParam == OPT_CommentText ) {
\r
6435 MSGFILTER * lpMF = (MSGFILTER *) lParam;
\r
6437 if( lpMF->msg == WM_RBUTTONDOWN && (lpMF->wParam & (MK_CONTROL | MK_SHIFT)) == 0 ||
\r
6438 lpMF->msg == WM_CHAR && lpMF->wParam == '\022' ) {
\r
6442 pt.x = LOWORD( lpMF->lParam );
\r
6443 pt.y = HIWORD( lpMF->lParam );
\r
6445 if(lpMF->msg == WM_CHAR) {
\r
6447 SendDlgItemMessage( hDlg, OPT_CommentText, EM_EXGETSEL, 0, (LPARAM) &sel );
\r
6448 index = sel.cpMin;
\r
6450 index = SendDlgItemMessage( hDlg, OPT_CommentText, EM_CHARFROMPOS, 0, (LPARAM) &pt );
\r
6452 hwndText = GetDlgItem(hDlg, OPT_CommentText); // cloned from above
\r
6453 len = GetWindowTextLength(hwndText);
\r
6454 str = (char *) malloc(len + 1);
\r
6455 GetWindowText(hwndText, str, len + 1);
\r
6456 ReplaceComment(commentIndex, str);
\r
6457 if(commentIndex != currentMove) ToNrEvent(commentIndex);
\r
6458 LoadVariation( index, str ); // [HGM] also does the actual moving to it, now
\r
6461 /* Zap the message for good: apparently, returning non-zero is not enough */
\r
6462 lpMF->msg = WM_USER;
\r
6470 newSizeX = LOWORD(lParam);
\r
6471 newSizeY = HIWORD(lParam);
\r
6472 ResizeEditPlusButtons(hDlg, hwndText, sizeX, sizeY, newSizeX, newSizeY);
\r
6477 case WM_GETMINMAXINFO:
\r
6478 /* Prevent resizing window too small */
\r
6479 mmi = (MINMAXINFO *) lParam;
\r
6480 mmi->ptMinTrackSize.x = 100;
\r
6481 mmi->ptMinTrackSize.y = 100;
\r
6488 EitherCommentPopUp(int index, char *title, char *str, BOOLEAN edit)
\r
6493 CheckMenuItem(GetMenu(hwndMain), IDM_Comment, edit ? MF_CHECKED : MF_UNCHECKED);
\r
6495 if (str == NULL) str = "";
\r
6496 p = (char *) malloc(2 * strlen(str) + 2);
\r
6499 if (*str == '\n') *q++ = '\r';
\r
6503 if (commentText != NULL) free(commentText);
\r
6505 commentIndex = index;
\r
6506 commentTitle = title;
\r
6508 editComment = edit;
\r
6510 if (commentDialog) {
\r
6511 SendMessage(commentDialog, WM_INITDIALOG, 0, 0);
\r
6512 if (!commentUp) ShowWindow(commentDialog, SW_SHOW);
\r
6514 lpProc = MakeProcInstance((FARPROC)CommentDialog, hInst);
\r
6515 CreateDialog(hInst, MAKEINTRESOURCE(DLG_EditComment),
\r
6516 hwndMain, (DLGPROC)lpProc);
\r
6517 FreeProcInstance(lpProc);
\r
6523 /*---------------------------------------------------------------------------*\
\r
6525 * Type-in move dialog functions
\r
6527 \*---------------------------------------------------------------------------*/
\r
6530 TypeInMoveDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
6532 char move[MSG_SIZ];
\r
6535 switch (message) {
\r
6536 case WM_INITDIALOG:
\r
6537 move[0] = (char) lParam;
\r
6538 move[1] = NULLCHAR;
\r
6539 CenterWindowEx(hDlg, GetWindow(hDlg, GW_OWNER), 1 );
\r
6540 Translate(hDlg, DLG_TypeInMove);
\r
6541 hInput = GetDlgItem(hDlg, OPT_Move);
\r
6542 SetWindowText(hInput, move);
\r
6544 SendMessage(hInput, EM_SETSEL, (WPARAM)9999, (LPARAM)9999);
\r
6548 switch (LOWORD(wParam)) {
\r
6551 shiftKey = GetKeyState(VK_SHIFT) < 0; // [HGM] remember last shift status
\r
6552 GetDlgItemText(hDlg, OPT_Move, move, sizeof(move));
\r
6553 TypeInDoneEvent(move);
\r
6554 EndDialog(hDlg, TRUE);
\r
6557 EndDialog(hDlg, FALSE);
\r
6568 PopUpMoveDialog(char firstchar)
\r
6572 lpProc = MakeProcInstance((FARPROC)TypeInMoveDialog, hInst);
\r
6573 DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_TypeInMove),
\r
6574 hwndMain, (DLGPROC)lpProc, (LPARAM)firstchar);
\r
6575 FreeProcInstance(lpProc);
\r
6578 /*---------------------------------------------------------------------------*\
\r
6580 * Type-in name dialog functions
\r
6582 \*---------------------------------------------------------------------------*/
\r
6585 TypeInNameDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
6587 char move[MSG_SIZ];
\r
6590 switch (message) {
\r
6591 case WM_INITDIALOG:
\r
6592 move[0] = (char) lParam;
\r
6593 move[1] = NULLCHAR;
\r
6594 CenterWindowEx(hDlg, GetWindow(hDlg, GW_OWNER), 1 );
\r
6595 Translate(hDlg, DLG_TypeInName);
\r
6596 hInput = GetDlgItem(hDlg, OPT_Name);
\r
6597 SetWindowText(hInput, move);
\r
6599 SendMessage(hInput, EM_SETSEL, (WPARAM)9999, (LPARAM)9999);
\r
6603 switch (LOWORD(wParam)) {
\r
6605 GetDlgItemText(hDlg, OPT_Name, move, sizeof(move));
\r
6606 appData.userName = strdup(move);
\r
6609 if(gameMode == MachinePlaysWhite || gameMode == MachinePlaysBlack) {
\r
6610 snprintf(move, MSG_SIZ, "%s vs. %s", gameInfo.white, gameInfo.black);
\r
6611 DisplayTitle(move);
\r
6615 EndDialog(hDlg, TRUE);
\r
6618 EndDialog(hDlg, FALSE);
\r
6629 PopUpNameDialog(char firstchar)
\r
6633 lpProc = MakeProcInstance((FARPROC)TypeInNameDialog, hInst);
\r
6634 DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_TypeInName),
\r
6635 hwndMain, (DLGPROC)lpProc, (LPARAM)firstchar);
\r
6636 FreeProcInstance(lpProc);
\r
6639 /*---------------------------------------------------------------------------*\
\r
6643 \*---------------------------------------------------------------------------*/
\r
6645 /* Nonmodal error box */
\r
6646 LRESULT CALLBACK ErrorDialog(HWND hDlg, UINT message,
\r
6647 WPARAM wParam, LPARAM lParam);
\r
6650 ErrorPopUp(char *title, char *content)
\r
6654 BOOLEAN modal = hwndMain == NULL;
\r
6672 strncpy(errorTitle, title, sizeof(errorTitle));
\r
6673 errorTitle[sizeof(errorTitle) - 1] = '\0';
\r
6676 MessageBox(NULL, errorMessage, errorTitle, MB_OK|MB_ICONEXCLAMATION);
\r
6678 lpProc = MakeProcInstance((FARPROC)ErrorDialog, hInst);
\r
6679 CreateDialog(hInst, MAKEINTRESOURCE(DLG_Error),
\r
6680 hwndMain, (DLGPROC)lpProc);
\r
6681 FreeProcInstance(lpProc);
\r
6688 if (!appData.popupMoveErrors && moveErrorMessageUp) DisplayMessage("", "");
\r
6689 if (errorDialog == NULL) return;
\r
6690 DestroyWindow(errorDialog);
\r
6691 errorDialog = NULL;
\r
6692 if(errorExitStatus) ExitEvent(errorExitStatus);
\r
6696 ErrorDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
6701 switch (message) {
\r
6702 case WM_INITDIALOG:
\r
6703 GetWindowRect(hDlg, &rChild);
\r
6706 SetWindowPos(hDlg, NULL, rChild.left,
\r
6707 rChild.top + boardRect.top - (rChild.bottom - rChild.top),
\r
6708 0, 0, SWP_NOZORDER|SWP_NOSIZE);
\r
6712 [AS] It seems that the above code wants to move the dialog up in the "caption
\r
6713 area" of the main window, but it uses the dialog height as an hard-coded constant,
\r
6714 and it doesn't work when you resize the dialog.
\r
6715 For now, just give it a default position.
\r
6717 SetWindowPos(hDlg, NULL, boardRect.left+8, boardRect.top+8, 0, 0, SWP_NOZORDER|SWP_NOSIZE);
\r
6718 Translate(hDlg, DLG_Error);
\r
6720 errorDialog = hDlg;
\r
6721 SetWindowText(hDlg, errorTitle);
\r
6722 hwndText = GetDlgItem(hDlg, OPT_ErrorText);
\r
6723 SetDlgItemText(hDlg, OPT_ErrorText, errorMessage);
\r
6727 switch (LOWORD(wParam)) {
\r
6730 if (errorDialog == hDlg) errorDialog = NULL;
\r
6731 DestroyWindow(hDlg);
\r
6743 HWND gothicDialog = NULL;
\r
6746 GothicDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
6750 int height = GetSystemMetrics(SM_CYCAPTION)+GetSystemMetrics(SM_CYFRAME);
\r
6752 switch (message) {
\r
6753 case WM_INITDIALOG:
\r
6754 GetWindowRect(hDlg, &rChild);
\r
6756 SetWindowPos(hDlg, NULL, wpMain.x, wpMain.y-height, wpMain.width, height,
\r
6760 [AS] It seems that the above code wants to move the dialog up in the "caption
\r
6761 area" of the main window, but it uses the dialog height as an hard-coded constant,
\r
6762 and it doesn't work when you resize the dialog.
\r
6763 For now, just give it a default position.
\r
6765 gothicDialog = hDlg;
\r
6766 SetWindowText(hDlg, errorTitle);
\r
6767 hwndText = GetDlgItem(hDlg, OPT_ErrorText);
\r
6768 SetDlgItemText(hDlg, OPT_ErrorText, errorMessage);
\r
6772 switch (LOWORD(wParam)) {
\r
6775 if (errorDialog == hDlg) errorDialog = NULL;
\r
6776 DestroyWindow(hDlg);
\r
6788 GothicPopUp(char *title, VariantClass variant)
\r
6791 static char *lastTitle;
\r
6793 strncpy(errorTitle, title, sizeof(errorTitle));
\r
6794 errorTitle[sizeof(errorTitle) - 1] = '\0';
\r
6796 if(lastTitle != title && gothicDialog != NULL) {
\r
6797 DestroyWindow(gothicDialog);
\r
6798 gothicDialog = NULL;
\r
6800 if(variant != VariantNormal && gothicDialog == NULL) {
\r
6801 title = lastTitle;
\r
6802 lpProc = MakeProcInstance((FARPROC)GothicDialog, hInst);
\r
6803 CreateDialog(hInst, MAKEINTRESOURCE(DLG_Error),
\r
6804 hwndMain, (DLGPROC)lpProc);
\r
6805 FreeProcInstance(lpProc);
\r
6810 /*---------------------------------------------------------------------------*\
\r
6812 * Ics Interaction console functions
\r
6814 \*---------------------------------------------------------------------------*/
\r
6816 #define HISTORY_SIZE 64
\r
6817 static char *history[HISTORY_SIZE];
\r
6818 int histIn = 0, histP = 0;
\r
6821 SaveInHistory(char *cmd)
\r
6823 if (history[histIn] != NULL) {
\r
6824 free(history[histIn]);
\r
6825 history[histIn] = NULL;
\r
6827 if (*cmd == NULLCHAR) return;
\r
6828 history[histIn] = StrSave(cmd);
\r
6829 histIn = (histIn + 1) % HISTORY_SIZE;
\r
6830 if (history[histIn] != NULL) {
\r
6831 free(history[histIn]);
\r
6832 history[histIn] = NULL;
\r
6838 PrevInHistory(char *cmd)
\r
6841 if (histP == histIn) {
\r
6842 if (history[histIn] != NULL) free(history[histIn]);
\r
6843 history[histIn] = StrSave(cmd);
\r
6845 newhp = (histP - 1 + HISTORY_SIZE) % HISTORY_SIZE;
\r
6846 if (newhp == histIn || history[newhp] == NULL) return NULL;
\r
6848 return history[histP];
\r
6854 if (histP == histIn) return NULL;
\r
6855 histP = (histP + 1) % HISTORY_SIZE;
\r
6856 return history[histP];
\r
6860 LoadIcsTextMenu(IcsTextMenuEntry *e)
\r
6864 hmenu = LoadMenu(hInst, "TextMenu");
\r
6865 h = GetSubMenu(hmenu, 0);
\r
6867 if (strcmp(e->item, "-") == 0) {
\r
6868 AppendMenu(h, MF_SEPARATOR, 0, 0);
\r
6869 } else { // [HGM] re-written a bit to use only one AppendMenu call for both cases (| or no |)
\r
6870 int flags = MF_STRING, j = 0;
\r
6871 if (e->item[0] == '|') {
\r
6872 flags |= MF_MENUBARBREAK;
\r
6875 if(!strcmp(e->command, "none")) flags |= MF_GRAYED; // [HGM] chatclick: provide inactive dummy
\r
6876 AppendMenu(h, flags, IDM_CommandX + i, e->item + j);
\r
6884 WNDPROC consoleTextWindowProc;
\r
6887 CommandX(HWND hwnd, char *command, BOOLEAN getname, BOOLEAN immediate)
\r
6889 char buf[MSG_SIZ], name[MSG_SIZ];
\r
6890 HWND hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
6894 SetWindowText(hInput, command);
\r
6896 SendMessage(hInput, WM_CHAR, '\r', 0);
\r
6898 sel.cpMin = 999999;
\r
6899 sel.cpMax = 999999;
\r
6900 SendMessage(hInput, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
6905 SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);
\r
6906 if (sel.cpMin == sel.cpMax) {
\r
6907 /* Expand to surrounding word */
\r
6910 tr.chrg.cpMax = sel.cpMin;
\r
6911 tr.chrg.cpMin = --sel.cpMin;
\r
6912 if (sel.cpMin < 0) break;
\r
6913 tr.lpstrText = name;
\r
6914 SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr);
\r
6915 } while (isalpha(name[0]) || isdigit(name[0]) || name[0] == '-');
\r
6919 tr.chrg.cpMin = sel.cpMax;
\r
6920 tr.chrg.cpMax = ++sel.cpMax;
\r
6921 tr.lpstrText = name;
\r
6922 if (SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr) < 1) break;
\r
6923 } while (isalpha(name[0]) || isdigit(name[0]) || name[0] == '-');
\r
6926 if (sel.cpMax == sel.cpMin || sel.cpMax - sel.cpMin > MSG_SIZ/2) {
\r
6927 MessageBeep(MB_ICONEXCLAMATION);
\r
6931 tr.lpstrText = name;
\r
6932 SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr);
\r
6934 if (sel.cpMax - sel.cpMin > MSG_SIZ/2) {
\r
6935 MessageBeep(MB_ICONEXCLAMATION);
\r
6938 SendMessage(hwnd, EM_GETSELTEXT, 0, (LPARAM) name);
\r
6941 if(strstr(command, "%s")) snprintf(buf, MSG_SIZ, command, name); else
\r
6942 snprintf(buf, MSG_SIZ, "%s %s", command, name);
\r
6943 SetWindowText(hInput, buf);
\r
6944 SendMessage(hInput, WM_CHAR, '\r', 0);
\r
6946 if(!strcmp(command, "chat")) { ChatPopUp(name); return; }
\r
6947 snprintf(buf, MSG_SIZ, "%s %s ", command, name); /* trailing space */
\r
6948 SetWindowText(hInput, buf);
\r
6949 sel.cpMin = 999999;
\r
6950 sel.cpMax = 999999;
\r
6951 SendMessage(hInput, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
6957 ConsoleTextSubclass(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
\r
6962 switch (message) {
\r
6964 if (!(GetKeyState(VK_CONTROL) & ~1)) break;
\r
6965 if(wParam=='R') return 0;
\r
6968 SendMessage(hwnd, EM_LINESCROLL, 0, -999999);
\r
6971 sel.cpMin = 999999;
\r
6972 sel.cpMax = 999999;
\r
6973 SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
6974 SendMessage(hwnd, EM_SCROLLCARET, 0, 0);
\r
6979 if(wParam != '\022') {
\r
6980 if (wParam == '\t') {
\r
6981 if (GetKeyState(VK_SHIFT) < 0) {
\r
6983 if (IsIconic(hwndMain)) ShowWindow(hwndMain, SW_RESTORE);
\r
6984 if (buttonDesc[0].hwnd) {
\r
6985 SetFocus(buttonDesc[0].hwnd);
\r
6987 SetFocus(hwndMain);
\r
6991 SetFocus(GetDlgItem(hwndConsole, OPT_ConsoleInput));
\r
6994 hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
6995 JAWS_DELETE( SetFocus(hInput); )
\r
6996 SendMessage(hInput, message, wParam, lParam);
\r
6999 } // [HGM] navigate: for Ctrl+R, flow into next case (moved up here) to summon up menu
\r
7001 case WM_RBUTTONDOWN:
\r
7002 if (!(GetKeyState(VK_SHIFT) & ~1)) {
\r
7003 /* Move selection here if it was empty */
\r
7005 pt.x = LOWORD(lParam);
\r
7006 pt.y = HIWORD(lParam);
\r
7007 SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);
\r
7008 if (sel.cpMin == sel.cpMax) {
\r
7009 if(lParam != -1) sel.cpMin = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&pt); /*doc is wrong*/
\r
7010 sel.cpMax = sel.cpMin;
\r
7011 SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
7013 SendMessage(hwnd, EM_HIDESELECTION, FALSE, FALSE);
\r
7014 { // [HGM] chatclick: code moved here from WM_RBUTTONUP case, to have menu appear on down-click
\r
7016 HMENU hmenu = LoadIcsTextMenu(icsTextMenuEntry);
\r
7017 SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);
\r
7018 if (sel.cpMin == sel.cpMax) {
\r
7019 EnableMenuItem(hmenu, IDM_Copy, MF_BYCOMMAND|MF_GRAYED);
\r
7020 EnableMenuItem(hmenu, IDM_QuickPaste, MF_BYCOMMAND|MF_GRAYED);
\r
7022 if (!IsClipboardFormatAvailable(CF_TEXT)) {
\r
7023 EnableMenuItem(hmenu, IDM_Paste, MF_BYCOMMAND|MF_GRAYED);
\r
7025 pt.x = LOWORD(lParam)-30; // [HGM] chatclick: make menu pop up with pointer above upper-right item
\r
7026 pt.y = HIWORD(lParam)-10; // make it appear as if mouse moved there, so it will be selected on up-click
\r
7027 PostMessage(hwnd, WM_MOUSEMOVE, wParam, lParam+5);
\r
7028 MenuPopup(hwnd, pt, hmenu, -1);
\r
7032 case WM_RBUTTONUP:
\r
7033 if (GetKeyState(VK_SHIFT) & ~1) {
\r
7034 SendDlgItemMessage(hwndConsole, OPT_ConsoleText,
\r
7035 WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);
\r
7039 hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
7041 return SendMessage(hInput, message, wParam, lParam);
\r
7042 case WM_MBUTTONDOWN:
\r
7043 return SendMessage(hwnd, WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);
\r
7045 switch (LOWORD(wParam)) {
\r
7046 case IDM_QuickPaste:
\r
7048 SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);
\r
7049 if (sel.cpMin == sel.cpMax) {
\r
7050 MessageBeep(MB_ICONEXCLAMATION);
\r
7053 SendMessage(hwnd, WM_COPY, 0, 0);
\r
7054 hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
7055 SendMessage(hInput, WM_PASTE, 0, 0);
\r
7060 SendMessage(hwnd, WM_CUT, 0, 0);
\r
7063 SendMessage(hwnd, WM_PASTE, 0, 0);
\r
7066 SendMessage(hwnd, WM_COPY, 0, 0);
\r
7070 int i = LOWORD(wParam) - IDM_CommandX;
\r
7071 if (i >= 0 && i < ICS_TEXT_MENU_SIZE &&
\r
7072 icsTextMenuEntry[i].command != NULL) {
\r
7073 CommandX(hwnd, icsTextMenuEntry[i].command,
\r
7074 icsTextMenuEntry[i].getname,
\r
7075 icsTextMenuEntry[i].immediate);
\r
7083 return (*consoleTextWindowProc)(hwnd, message, wParam, lParam);
\r
7086 WNDPROC consoleInputWindowProc;
\r
7089 ConsoleInputSubclass(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
\r
7091 char buf[MSG_SIZ];
\r
7093 static BOOL sendNextChar = FALSE;
\r
7094 static BOOL quoteNextChar = FALSE;
\r
7095 InputSource *is = consoleInputSource;
\r
7099 switch (message) {
\r
7101 if (!appData.localLineEditing || sendNextChar) {
\r
7102 is->buf[0] = (CHAR) wParam;
\r
7104 SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);
\r
7105 sendNextChar = FALSE;
\r
7108 if (quoteNextChar) {
\r
7109 buf[0] = (char) wParam;
\r
7110 buf[1] = NULLCHAR;
\r
7111 SendMessage(hwnd, EM_REPLACESEL, TRUE, (LPARAM) buf);
\r
7112 quoteNextChar = FALSE;
\r
7116 case '\r': /* Enter key */
\r
7117 is->count = GetWindowText(hwnd, is->buf, INPUT_SOURCE_BUF_SIZE-1);
\r
7118 if (consoleEcho) SaveInHistory(is->buf);
\r
7119 is->buf[is->count++] = '\n';
\r
7120 is->buf[is->count] = NULLCHAR;
\r
7121 SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);
\r
7122 if (consoleEcho) {
\r
7123 ConsoleOutput(is->buf, is->count, TRUE);
\r
7124 } else if (appData.localLineEditing) {
\r
7125 ConsoleOutput("\n", 1, TRUE);
\r
7128 case '\033': /* Escape key */
\r
7129 SetWindowText(hwnd, "");
\r
7130 cf.cbSize = sizeof(CHARFORMAT);
\r
7131 cf.dwMask = CFM_COLOR|CFM_BOLD|CFM_ITALIC|CFM_UNDERLINE|CFM_STRIKEOUT;
\r
7132 if (consoleEcho) {
\r
7133 cf.crTextColor = textAttribs[ColorNormal].color;
\r
7135 cf.crTextColor = COLOR_ECHOOFF;
\r
7137 cf.dwEffects = textAttribs[ColorNormal].effects;
\r
7138 SendMessage(hwnd, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf);
\r
7140 case '\t': /* Tab key */
\r
7141 if (GetKeyState(VK_SHIFT) < 0) {
\r
7143 SetFocus(GetDlgItem(hwndConsole, OPT_ConsoleText));
\r
7146 if (IsIconic(hwndMain)) ShowWindow(hwndMain, SW_RESTORE);
\r
7147 if (buttonDesc[0].hwnd) {
\r
7148 SetFocus(buttonDesc[0].hwnd);
\r
7150 SetFocus(hwndMain);
\r
7154 case '\023': /* Ctrl+S */
\r
7155 sendNextChar = TRUE;
\r
7157 case '\021': /* Ctrl+Q */
\r
7158 quoteNextChar = TRUE;
\r
7168 GetWindowText(hwnd, buf, MSG_SIZ);
\r
7169 p = PrevInHistory(buf);
\r
7171 SetWindowText(hwnd, p);
\r
7172 sel.cpMin = 999999;
\r
7173 sel.cpMax = 999999;
\r
7174 SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
7179 p = NextInHistory();
\r
7181 SetWindowText(hwnd, p);
\r
7182 sel.cpMin = 999999;
\r
7183 sel.cpMax = 999999;
\r
7184 SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
7190 if (!(GetKeyState(VK_CONTROL) & ~1)) break;
\r
7194 SendDlgItemMessage(hwndConsole, OPT_ConsoleText, message, wParam, lParam);
\r
7198 case WM_MBUTTONDOWN:
\r
7199 SendDlgItemMessage(hwndConsole, OPT_ConsoleText,
\r
7200 WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);
\r
7202 case WM_RBUTTONUP:
\r
7203 if (GetKeyState(VK_SHIFT) & ~1) {
\r
7204 SendDlgItemMessage(hwndConsole, OPT_ConsoleText,
\r
7205 WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);
\r
7209 hmenu = LoadMenu(hInst, "InputMenu");
\r
7210 SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);
\r
7211 if (sel.cpMin == sel.cpMax) {
\r
7212 EnableMenuItem(hmenu, IDM_Copy, MF_BYCOMMAND|MF_GRAYED);
\r
7213 EnableMenuItem(hmenu, IDM_Cut, MF_BYCOMMAND|MF_GRAYED);
\r
7215 if (!IsClipboardFormatAvailable(CF_TEXT)) {
\r
7216 EnableMenuItem(hmenu, IDM_Paste, MF_BYCOMMAND|MF_GRAYED);
\r
7218 pt.x = LOWORD(lParam);
\r
7219 pt.y = HIWORD(lParam);
\r
7220 MenuPopup(hwnd, pt, hmenu, -1);
\r
7224 switch (LOWORD(wParam)) {
\r
7226 SendMessage(hwnd, EM_UNDO, 0, 0);
\r
7228 case IDM_SelectAll:
\r
7230 sel.cpMax = -1; /*999999?*/
\r
7231 SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
7234 SendMessage(hwnd, WM_CUT, 0, 0);
\r
7237 SendMessage(hwnd, WM_PASTE, 0, 0);
\r
7240 SendMessage(hwnd, WM_COPY, 0, 0);
\r
7245 return (*consoleInputWindowProc)(hwnd, message, wParam, lParam);
\r
7248 #define CO_MAX 100000
\r
7249 #define CO_TRIM 1000
\r
7252 ConsoleWndProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
7254 static SnapData sd;
\r
7255 HWND hText, hInput;
\r
7257 static int sizeX, sizeY;
\r
7258 int newSizeX, newSizeY;
\r
7262 hText = GetDlgItem(hDlg, OPT_ConsoleText);
\r
7263 hInput = GetDlgItem(hDlg, OPT_ConsoleInput);
\r
7265 switch (message) {
\r
7267 if (((NMHDR*)lParam)->code == EN_LINK)
\r
7269 ENLINK *pLink = (ENLINK*)lParam;
\r
7270 if (pLink->msg == WM_LBUTTONUP)
\r
7274 tr.chrg = pLink->chrg;
\r
7275 tr.lpstrText = malloc(1+tr.chrg.cpMax-tr.chrg.cpMin);
\r
7276 SendMessage(hText, EM_GETTEXTRANGE, 0, (LPARAM)&tr);
\r
7277 ShellExecute(NULL, "open", tr.lpstrText, NULL, NULL, SW_SHOW);
\r
7278 free(tr.lpstrText);
\r
7282 case WM_INITDIALOG: /* message: initialize dialog box */
\r
7283 hwndConsole = hDlg;
\r
7285 consoleTextWindowProc = (WNDPROC)
\r
7286 SetWindowLongPtr(hText, GWLP_WNDPROC, (LONG_PTR) ConsoleTextSubclass);
\r
7287 SendMessage(hText, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);
\r
7288 consoleInputWindowProc = (WNDPROC)
\r
7289 SetWindowLongPtr(hInput, GWLP_WNDPROC, (LONG_PTR) ConsoleInputSubclass);
\r
7290 SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);
\r
7291 Colorize(ColorNormal, TRUE);
\r
7292 SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &consoleCF);
\r
7293 ChangedConsoleFont();
\r
7294 GetClientRect(hDlg, &rect);
\r
7295 sizeX = rect.right;
\r
7296 sizeY = rect.bottom;
\r
7297 if (wpConsole.x != CW_USEDEFAULT && wpConsole.y != CW_USEDEFAULT &&
\r
7298 wpConsole.width != CW_USEDEFAULT && wpConsole.height != CW_USEDEFAULT) {
\r
7299 WINDOWPLACEMENT wp;
\r
7300 EnsureOnScreen(&wpConsole.x, &wpConsole.y, 0, 0);
\r
7301 wp.length = sizeof(WINDOWPLACEMENT);
\r
7303 wp.showCmd = SW_SHOW;
\r
7304 wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;
\r
7305 wp.rcNormalPosition.left = wpConsole.x;
\r
7306 wp.rcNormalPosition.right = wpConsole.x + wpConsole.width;
\r
7307 wp.rcNormalPosition.top = wpConsole.y;
\r
7308 wp.rcNormalPosition.bottom = wpConsole.y + wpConsole.height;
\r
7309 SetWindowPlacement(hDlg, &wp);
\r
7312 // [HGM] Chessknight's change 2004-07-13
\r
7313 else { /* Determine Defaults */
\r
7314 WINDOWPLACEMENT wp;
\r
7315 wpConsole.x = wpMain.width + 1;
\r
7316 wpConsole.y = wpMain.y;
\r
7317 wpConsole.width = screenWidth - wpMain.width;
\r
7318 wpConsole.height = wpMain.height;
\r
7319 EnsureOnScreen(&wpConsole.x, &wpConsole.y, 0, 0);
\r
7320 wp.length = sizeof(WINDOWPLACEMENT);
\r
7322 wp.showCmd = SW_SHOW;
\r
7323 wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;
\r
7324 wp.rcNormalPosition.left = wpConsole.x;
\r
7325 wp.rcNormalPosition.right = wpConsole.x + wpConsole.width;
\r
7326 wp.rcNormalPosition.top = wpConsole.y;
\r
7327 wp.rcNormalPosition.bottom = wpConsole.y + wpConsole.height;
\r
7328 SetWindowPlacement(hDlg, &wp);
\r
7331 // Allow hText to highlight URLs and send notifications on them
\r
7332 wMask = (WORD) SendMessage(hText, EM_GETEVENTMASK, 0, 0L);
\r
7333 SendMessage(hText, EM_SETEVENTMASK, 0, wMask | ENM_LINK);
\r
7334 SendMessage(hText, EM_AUTOURLDETECT, TRUE, 0L);
\r
7335 SetWindowLongPtr(hText, GWLP_USERDATA, 79); // initialize the text window's width
\r
7349 if (IsIconic(hDlg)) break;
\r
7350 newSizeX = LOWORD(lParam);
\r
7351 newSizeY = HIWORD(lParam);
\r
7352 if (sizeX != newSizeX || sizeY != newSizeY) {
\r
7353 RECT rectText, rectInput;
\r
7355 int newTextHeight, newTextWidth;
\r
7356 GetWindowRect(hText, &rectText);
\r
7357 newTextWidth = rectText.right - rectText.left + newSizeX - sizeX;
\r
7358 newTextHeight = rectText.bottom - rectText.top + newSizeY - sizeY;
\r
7359 if (newTextHeight < 0) {
\r
7360 newSizeY += -newTextHeight;
\r
7361 newTextHeight = 0;
\r
7363 SetWindowPos(hText, NULL, 0, 0,
\r
7364 newTextWidth, newTextHeight, SWP_NOZORDER|SWP_NOMOVE);
\r
7365 GetWindowRect(hInput, &rectInput); /* gives screen coords */
\r
7366 pt.x = rectInput.left;
\r
7367 pt.y = rectInput.top + newSizeY - sizeY;
\r
7368 ScreenToClient(hDlg, &pt);
\r
7369 SetWindowPos(hInput, NULL,
\r
7370 pt.x, pt.y, /* needs client coords */
\r
7371 rectInput.right - rectInput.left + newSizeX - sizeX,
\r
7372 rectInput.bottom - rectInput.top, SWP_NOZORDER);
\r
7378 case WM_GETMINMAXINFO:
\r
7379 /* Prevent resizing window too small */
\r
7380 mmi = (MINMAXINFO *) lParam;
\r
7381 mmi->ptMinTrackSize.x = 100;
\r
7382 mmi->ptMinTrackSize.y = 100;
\r
7385 /* [AS] Snapping */
\r
7386 case WM_ENTERSIZEMOVE:
\r
7387 return OnEnterSizeMove( &sd, hDlg, wParam, lParam );
\r
7390 return OnSizing( &sd, hDlg, wParam, lParam );
\r
7393 return OnMoving( &sd, hDlg, wParam, lParam );
\r
7395 case WM_EXITSIZEMOVE:
\r
7396 UpdateICSWidth(hText);
\r
7397 return OnExitSizeMove( &sd, hDlg, wParam, lParam );
\r
7400 return DefWindowProc(hDlg, message, wParam, lParam);
\r
7408 if (hwndConsole) return;
\r
7409 hCons = CreateDialog(hInst, szConsoleName, 0, NULL);
\r
7410 SendMessage(hCons, WM_INITDIALOG, 0, 0);
\r
7415 ConsoleOutput(char* data, int length, int forceVisible)
\r
7420 char buf[CO_MAX+1];
\r
7423 static int delayLF = 0;
\r
7424 CHARRANGE savesel, sel;
\r
7426 if (hwndConsole == NULL || length > CO_MAX-100 || length == 0) return;
\r
7434 while (length--) {
\r
7442 } else if (*p == '\007') {
\r
7443 MyPlaySound(&sounds[(int)SoundBell]);
\r
7450 hText = GetDlgItem(hwndConsole, OPT_ConsoleText);
\r
7451 SendMessage(hText, EM_HIDESELECTION, TRUE, FALSE);
\r
7452 /* Save current selection */
\r
7453 SendMessage(hText, EM_EXGETSEL, 0, (LPARAM)&savesel);
\r
7454 exlen = GetWindowTextLength(hText);
\r
7455 /* Find out whether current end of text is visible */
\r
7456 SendMessage(hText, EM_GETRECT, 0, (LPARAM) &rect);
\r
7457 SendMessage(hText, EM_POSFROMCHAR, (WPARAM) &pEnd, exlen);
\r
7458 /* Trim existing text if it's too long */
\r
7459 if (exlen + (q - buf) > CO_MAX) {
\r
7460 trim = (CO_TRIM > (q - buf)) ? CO_TRIM : (q - buf);
\r
7463 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
7464 SendMessage(hText, EM_REPLACESEL, 0, (LPARAM)"");
\r
7466 savesel.cpMin -= trim;
\r
7467 savesel.cpMax -= trim;
\r
7468 if (exlen < 0) exlen = 0;
\r
7469 if (savesel.cpMin < 0) savesel.cpMin = 0;
\r
7470 if (savesel.cpMax < savesel.cpMin) savesel.cpMax = savesel.cpMin;
\r
7472 /* Append the new text */
\r
7473 sel.cpMin = exlen;
\r
7474 sel.cpMax = exlen;
\r
7475 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
7476 SendMessage(hText, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&consoleCF);
\r
7477 SendMessage(hText, EM_REPLACESEL, 0, (LPARAM) buf);
\r
7478 if (forceVisible || exlen == 0 ||
\r
7479 (rect.left <= pEnd.x && pEnd.x < rect.right &&
\r
7480 rect.top <= pEnd.y && pEnd.y < rect.bottom)) {
\r
7481 /* Scroll to make new end of text visible if old end of text
\r
7482 was visible or new text is an echo of user typein */
\r
7483 sel.cpMin = 9999999;
\r
7484 sel.cpMax = 9999999;
\r
7485 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
7486 SendMessage(hText, EM_HIDESELECTION, FALSE, FALSE);
\r
7487 SendMessage(hText, EM_SCROLLCARET, 0, 0);
\r
7488 SendMessage(hText, EM_HIDESELECTION, TRUE, FALSE);
\r
7490 if (savesel.cpMax == exlen || forceVisible) {
\r
7491 /* Move insert point to new end of text if it was at the old
\r
7492 end of text or if the new text is an echo of user typein */
\r
7493 sel.cpMin = 9999999;
\r
7494 sel.cpMax = 9999999;
\r
7495 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
7497 /* Restore previous selection */
\r
7498 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&savesel);
\r
7500 SendMessage(hText, EM_HIDESELECTION, FALSE, FALSE);
\r
7507 DisplayHoldingsCount(HDC hdc, int x, int y, int rightAlign, int copyNumber)
\r
7511 COLORREF oldFg, oldBg;
\r
7515 if(copyNumber > 1)
\r
7516 snprintf(buf, sizeof(buf)/sizeof(buf[0]),"%d", copyNumber); else buf[0] = 0;
\r
7518 oldFg = SetTextColor(hdc, RGB(255, 255, 255)); /* white */
\r
7519 oldBg = SetBkColor(hdc, RGB(0, 0, 0)); /* black */
\r
7520 oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);
\r
7523 rect.right = x + squareSize;
\r
7525 rect.bottom = y + squareSize;
\r
7528 ExtTextOut(hdc, x + MESSAGE_LINE_LEFTMARGIN
\r
7529 + (rightAlign ? (squareSize*2)/3 : 0),
\r
7530 y, ETO_CLIPPED|ETO_OPAQUE,
\r
7531 &rect, str, strlen(str), NULL);
\r
7533 (void) SetTextColor(hdc, oldFg);
\r
7534 (void) SetBkColor(hdc, oldBg);
\r
7535 (void) SelectObject(hdc, oldFont);
\r
7539 DisplayAClock(HDC hdc, int timeRemaining, int highlight,
\r
7540 RECT *rect, char *color, char *flagFell)
\r
7544 COLORREF oldFg, oldBg;
\r
7547 if (twoBoards && partnerUp) return;
\r
7548 if (appData.clockMode) {
\r
7550 snprintf(buf, sizeof(buf)/sizeof(buf[0]), "%c %s %s", color[0], TimeString(timeRemaining), flagFell);
\r
7552 snprintf(buf, sizeof(buf)/sizeof(buf[0]), "%s:%c%s %s", color, (logoHeight>0 ? 0 : ' '), TimeString(timeRemaining), flagFell);
\r
7559 oldFg = SetTextColor(hdc, RGB(255, 255, 255)); /* white */
\r
7560 oldBg = SetBkColor(hdc, RGB(0, 0, 0)); /* black */
\r
7562 oldFg = SetTextColor(hdc, RGB(0, 0, 0)); /* black */
\r
7563 oldBg = SetBkColor(hdc, RGB(255, 255, 255)); /* white */
\r
7565 oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);
\r
7569 ExtTextOut(hdc, rect->left + MESSAGE_LINE_LEFTMARGIN,
\r
7570 rect->top, ETO_CLIPPED|ETO_OPAQUE,
\r
7571 rect, str, strlen(str), NULL);
\r
7572 if(logoHeight > 0 && appData.clockMode) {
\r
7574 str += strlen(color)+2;
\r
7575 r.top = rect->top + logoHeight/2;
\r
7576 r.left = rect->left;
\r
7577 r.right = rect->right;
\r
7578 r.bottom = rect->bottom;
\r
7579 ExtTextOut(hdc, rect->left + MESSAGE_LINE_LEFTMARGIN,
\r
7580 r.top, ETO_CLIPPED|ETO_OPAQUE,
\r
7581 &r, str, strlen(str), NULL);
\r
7583 (void) SetTextColor(hdc, oldFg);
\r
7584 (void) SetBkColor(hdc, oldBg);
\r
7585 (void) SelectObject(hdc, oldFont);
\r
7590 DoReadFile(HANDLE hFile, char *buf, int count, DWORD *outCount,
\r
7596 if( count <= 0 ) {
\r
7597 if (appData.debugMode) {
\r
7598 fprintf( debugFP, "DoReadFile: trying to read past end of buffer, overflow = %d\n", count );
\r
7601 return ERROR_INVALID_USER_BUFFER;
\r
7604 ResetEvent(ovl->hEvent);
\r
7605 ovl->Offset = ovl->OffsetHigh = 0;
\r
7606 ok = ReadFile(hFile, buf, count, outCount, ovl);
\r
7610 err = GetLastError();
\r
7611 if (err == ERROR_IO_PENDING) {
\r
7612 ok = GetOverlappedResult(hFile, ovl, outCount, TRUE);
\r
7616 err = GetLastError();
\r
7623 DoWriteFile(HANDLE hFile, char *buf, int count, DWORD *outCount,
\r
7628 ResetEvent(ovl->hEvent);
\r
7629 ovl->Offset = ovl->OffsetHigh = 0;
\r
7630 ok = WriteFile(hFile, buf, count, outCount, ovl);
\r
7634 err = GetLastError();
\r
7635 if (err == ERROR_IO_PENDING) {
\r
7636 ok = GetOverlappedResult(hFile, ovl, outCount, TRUE);
\r
7640 err = GetLastError();
\r
7646 /* [AS] If input is line by line and a line exceed the buffer size, force an error */
\r
7647 void CheckForInputBufferFull( InputSource * is )
\r
7649 if( is->lineByLine && (is->next - is->buf) >= INPUT_SOURCE_BUF_SIZE ) {
\r
7650 /* Look for end of line */
\r
7651 char * p = is->buf;
\r
7653 while( p < is->next && *p != '\n' ) {
\r
7657 if( p >= is->next ) {
\r
7658 if (appData.debugMode) {
\r
7659 fprintf( debugFP, "Input line exceeded buffer size (source id=%lu)\n", is->id );
\r
7662 is->error = ERROR_BROKEN_PIPE; /* [AS] Just any non-successful code! */
\r
7663 is->count = (DWORD) -1;
\r
7664 is->next = is->buf;
\r
7670 InputThread(LPVOID arg)
\r
7675 is = (InputSource *) arg;
\r
7676 ovl.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
\r
7677 ovl.Internal = ovl.InternalHigh = ovl.Offset = ovl.OffsetHigh = 0;
\r
7678 while (is->hThread != NULL) {
\r
7679 is->error = DoReadFile(is->hFile, is->next,
\r
7680 INPUT_SOURCE_BUF_SIZE - (is->next - is->buf),
\r
7681 &is->count, &ovl);
\r
7682 if (is->error == NO_ERROR) {
\r
7683 is->next += is->count;
\r
7685 if (is->error == ERROR_BROKEN_PIPE) {
\r
7686 /* Correct for MS brain damage. EOF reading a pipe is not an error. */
\r
7689 is->count = (DWORD) -1;
\r
7690 /* [AS] The (is->count <= 0) check below is not useful for unsigned values! */
\r
7695 CheckForInputBufferFull( is );
\r
7697 SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);
\r
7699 if( is->count == ((DWORD) -1) ) break; /* [AS] */
\r
7701 if (is->count <= 0) break; /* Quit on EOF or error */
\r
7704 CloseHandle(ovl.hEvent);
\r
7705 CloseHandle(is->hFile);
\r
7707 if (appData.debugMode) {
\r
7708 fprintf( debugFP, "Input thread terminated (id=%lu, error=%d, count=%ld)\n", is->id, is->error, is->count );
\r
7715 /* Windows 95 beta 2 won't let you do overlapped i/o on a console or pipe */
\r
7717 NonOvlInputThread(LPVOID arg)
\r
7724 is = (InputSource *) arg;
\r
7725 while (is->hThread != NULL) {
\r
7726 is->error = ReadFile(is->hFile, is->next,
\r
7727 INPUT_SOURCE_BUF_SIZE - (is->next - is->buf),
\r
7728 &is->count, NULL) ? NO_ERROR : GetLastError();
\r
7729 if (is->error == NO_ERROR) {
\r
7730 /* Change CRLF to LF */
\r
7731 if (is->next > is->buf) {
\r
7733 i = is->count + 1;
\r
7741 if (prev == '\r' && *p == '\n') {
\r
7753 if (is->error == ERROR_BROKEN_PIPE) {
\r
7754 /* Correct for MS brain damage. EOF reading a pipe is not an error. */
\r
7757 is->count = (DWORD) -1;
\r
7761 CheckForInputBufferFull( is );
\r
7763 SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);
\r
7765 if( is->count == ((DWORD) -1) ) break; /* [AS] */
\r
7767 if (is->count < 0) break; /* Quit on error */
\r
7769 CloseHandle(is->hFile);
\r
7774 SocketInputThread(LPVOID arg)
\r
7778 is = (InputSource *) arg;
\r
7779 while (is->hThread != NULL) {
\r
7780 is->count = recv(is->sock, is->buf, INPUT_SOURCE_BUF_SIZE, 0);
\r
7781 if ((int)is->count == SOCKET_ERROR) {
\r
7782 is->count = (DWORD) -1;
\r
7783 is->error = WSAGetLastError();
\r
7785 is->error = NO_ERROR;
\r
7786 is->next += is->count;
\r
7787 if (is->count == 0 && is->second == is) {
\r
7788 /* End of file on stderr; quit with no message */
\r
7792 SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);
\r
7794 if( is->count == ((DWORD) -1) ) break; /* [AS] */
\r
7796 if (is->count <= 0) break; /* Quit on EOF or error */
\r
7802 InputEvent(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
\r
7806 is = (InputSource *) lParam;
\r
7807 if (is->lineByLine) {
\r
7808 /* Feed in lines one by one */
\r
7809 char *p = is->buf;
\r
7811 while (q < is->next) {
\r
7812 if (*q++ == '\n') {
\r
7813 (is->func)(is, is->closure, p, q - p, NO_ERROR);
\r
7818 /* Move any partial line to the start of the buffer */
\r
7820 while (p < is->next) {
\r
7825 if (is->error != NO_ERROR || is->count == 0) {
\r
7826 /* Notify backend of the error. Note: If there was a partial
\r
7827 line at the end, it is not flushed through. */
\r
7828 (is->func)(is, is->closure, is->buf, is->count, is->error);
\r
7831 /* Feed in the whole chunk of input at once */
\r
7832 (is->func)(is, is->closure, is->buf, is->count, is->error);
\r
7833 is->next = is->buf;
\r
7837 /*---------------------------------------------------------------------------*\
\r
7839 * Menu enables. Used when setting various modes.
\r
7841 \*---------------------------------------------------------------------------*/
\r
7849 GreyRevert(Boolean grey)
\r
7850 { // [HGM] vari: for retracting variations in local mode
\r
7851 HMENU hmenu = GetMenu(hwndMain);
\r
7852 EnableMenuItem(hmenu, IDM_Revert, MF_BYCOMMAND|(grey ? MF_GRAYED : MF_ENABLED));
\r
7853 EnableMenuItem(hmenu, IDM_Annotate, MF_BYCOMMAND|(grey ? MF_GRAYED : MF_ENABLED));
\r
7857 SetMenuEnables(HMENU hmenu, Enables *enab)
\r
7859 while (enab->item > 0) {
\r
7860 (void) EnableMenuItem(hmenu, enab->item, enab->flags);
\r
7865 Enables gnuEnables[] = {
\r
7866 { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },
\r
7867 { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },
\r
7868 { IDM_IcsClient, MF_BYCOMMAND|MF_GRAYED },
\r
7869 { IDM_Accept, MF_BYCOMMAND|MF_GRAYED },
\r
7870 { IDM_Decline, MF_BYCOMMAND|MF_GRAYED },
\r
7871 { IDM_Rematch, MF_BYCOMMAND|MF_GRAYED },
\r
7872 { IDM_Adjourn, MF_BYCOMMAND|MF_GRAYED },
\r
7873 { IDM_StopExamining, MF_BYCOMMAND|MF_GRAYED },
\r
7874 { IDM_StopObserving, MF_BYCOMMAND|MF_GRAYED },
\r
7875 { IDM_Upload, MF_BYCOMMAND|MF_GRAYED },
\r
7876 { IDM_Revert, MF_BYCOMMAND|MF_GRAYED },
\r
7877 { IDM_Annotate, MF_BYCOMMAND|MF_GRAYED },
\r
7878 { IDM_NewChat, MF_BYCOMMAND|MF_GRAYED },
\r
7880 // Needed to switch from ncp to GNU mode on Engine Load
\r
7881 { ACTION_POS, MF_BYPOSITION|MF_ENABLED },
\r
7882 { IDM_MachineWhite, MF_BYCOMMAND|MF_ENABLED },
\r
7883 { IDM_MachineBlack, MF_BYCOMMAND|MF_ENABLED },
\r
7884 { IDM_TwoMachines, MF_BYCOMMAND|MF_ENABLED },
\r
7885 { IDM_Match, MF_BYCOMMAND|MF_ENABLED },
\r
7886 { IDM_AnalysisMode, MF_BYCOMMAND|MF_ENABLED },
\r
7887 { IDM_AnalyzeFile, MF_BYCOMMAND|MF_ENABLED },
\r
7888 { IDM_Engine1Options, MF_BYCOMMAND|MF_ENABLED },
\r
7889 { IDM_Engine2Options, MF_BYCOMMAND|MF_ENABLED },
\r
7890 { IDM_TimeControl, MF_BYCOMMAND|MF_ENABLED },
\r
7891 { IDM_RetractMove, MF_BYCOMMAND|MF_ENABLED },
\r
7892 { IDM_MoveNow, MF_BYCOMMAND|MF_ENABLED },
\r
7893 { IDM_Hint, MF_BYCOMMAND|MF_ENABLED },
\r
7894 { IDM_Book, MF_BYCOMMAND|MF_ENABLED },
\r
7898 Enables icsEnables[] = {
\r
7899 { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },
\r
7900 { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },
\r
7901 { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },
\r
7902 { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },
\r
7903 { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },
\r
7904 { IDM_Match, MF_BYCOMMAND|MF_GRAYED },
\r
7905 { IDM_MachineBoth, MF_BYCOMMAND|MF_GRAYED },
\r
7906 { IDM_AnalysisMode, MF_BYCOMMAND|MF_ENABLED },
\r
7907 { IDM_AnalyzeFile, MF_BYCOMMAND|MF_GRAYED },
\r
7908 { IDM_TimeControl, MF_BYCOMMAND|MF_GRAYED },
\r
7909 { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },
\r
7910 { IDM_Hint, MF_BYCOMMAND|MF_GRAYED },
\r
7911 { IDM_Book, MF_BYCOMMAND|MF_GRAYED },
\r
7912 { IDM_LoadProg1, MF_BYCOMMAND|MF_GRAYED },
\r
7913 { IDM_LoadProg2, MF_BYCOMMAND|MF_GRAYED },
\r
7914 { IDM_IcsOptions, MF_BYCOMMAND|MF_ENABLED },
\r
7915 { IDM_Engine1Options, MF_BYCOMMAND|MF_GRAYED },
\r
7916 { IDM_Engine2Options, MF_BYCOMMAND|MF_GRAYED },
\r
7917 { IDM_Annotate, MF_BYCOMMAND|MF_GRAYED },
\r
7918 { IDM_Tourney, MF_BYCOMMAND|MF_GRAYED },
\r
7923 Enables zippyEnables[] = {
\r
7924 { IDM_MoveNow, MF_BYCOMMAND|MF_ENABLED },
\r
7925 { IDM_Hint, MF_BYCOMMAND|MF_ENABLED },
\r
7926 { IDM_Book, MF_BYCOMMAND|MF_ENABLED },
\r
7927 { IDM_Engine1Options, MF_BYCOMMAND|MF_ENABLED },
\r
7932 Enables ncpEnables[] = {
\r
7933 { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },
\r
7934 { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },
\r
7935 { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },
\r
7936 { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },
\r
7937 { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },
\r
7938 { IDM_Match, MF_BYCOMMAND|MF_GRAYED },
\r
7939 { IDM_AnalysisMode, MF_BYCOMMAND|MF_GRAYED },
\r
7940 { IDM_AnalyzeFile, MF_BYCOMMAND|MF_GRAYED },
\r
7941 { IDM_IcsClient, MF_BYCOMMAND|MF_GRAYED },
\r
7942 { ACTION_POS, MF_BYPOSITION|MF_GRAYED },
\r
7943 { IDM_Revert, MF_BYCOMMAND|MF_GRAYED },
\r
7944 { IDM_Annotate, MF_BYCOMMAND|MF_GRAYED },
\r
7945 { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },
\r
7946 { IDM_RetractMove, MF_BYCOMMAND|MF_GRAYED },
\r
7947 { IDM_TimeControl, MF_BYCOMMAND|MF_GRAYED },
\r
7948 { IDM_Hint, MF_BYCOMMAND|MF_GRAYED },
\r
7949 { IDM_Book, MF_BYCOMMAND|MF_GRAYED },
\r
7950 { IDM_MachineBoth, MF_BYCOMMAND|MF_GRAYED },
\r
7951 { IDM_NewChat, MF_BYCOMMAND|MF_GRAYED },
\r
7952 { IDM_Engine1Options, MF_BYCOMMAND|MF_GRAYED },
\r
7953 { IDM_Engine2Options, MF_BYCOMMAND|MF_GRAYED },
\r
7954 { IDM_Sounds, MF_BYCOMMAND|MF_GRAYED },
\r
7958 Enables trainingOnEnables[] = {
\r
7959 { IDM_EditComment, MF_BYCOMMAND|MF_GRAYED },
\r
7960 { IDM_Comment, MF_BYCOMMAND|MF_GRAYED },
\r
7961 { IDM_Pause, MF_BYCOMMAND|MF_GRAYED },
\r
7962 { IDM_Forward, MF_BYCOMMAND|MF_GRAYED },
\r
7963 { IDM_Backward, MF_BYCOMMAND|MF_GRAYED },
\r
7964 { IDM_ToEnd, MF_BYCOMMAND|MF_GRAYED },
\r
7965 { IDM_ToStart, MF_BYCOMMAND|MF_GRAYED },
\r
7966 { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },
\r
7967 { IDM_TruncateGame, MF_BYCOMMAND|MF_GRAYED },
\r
7971 Enables trainingOffEnables[] = {
\r
7972 { IDM_EditComment, MF_BYCOMMAND|MF_ENABLED },
\r
7973 { IDM_Comment, MF_BYCOMMAND|MF_ENABLED },
\r
7974 { IDM_Pause, MF_BYCOMMAND|MF_ENABLED },
\r
7975 { IDM_Forward, MF_BYCOMMAND|MF_ENABLED },
\r
7976 { IDM_Backward, MF_BYCOMMAND|MF_ENABLED },
\r
7977 { IDM_ToEnd, MF_BYCOMMAND|MF_ENABLED },
\r
7978 { IDM_ToStart, MF_BYCOMMAND|MF_ENABLED },
\r
7979 { IDM_MoveNow, MF_BYCOMMAND|MF_ENABLED },
\r
7980 { IDM_TruncateGame, MF_BYCOMMAND|MF_ENABLED },
\r
7984 /* These modify either ncpEnables or gnuEnables */
\r
7985 Enables cmailEnables[] = {
\r
7986 { IDM_MailMove, MF_BYCOMMAND|MF_ENABLED },
\r
7987 { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_ENABLED },
\r
7988 { ACTION_POS, MF_BYPOSITION|MF_ENABLED },
\r
7989 { IDM_CallFlag, MF_BYCOMMAND|MF_GRAYED },
\r
7990 { IDM_Draw, MF_BYCOMMAND|MF_ENABLED },
\r
7991 { IDM_Adjourn, MF_BYCOMMAND|MF_GRAYED },
\r
7992 { IDM_Abort, MF_BYCOMMAND|MF_GRAYED },
\r
7996 Enables machineThinkingEnables[] = {
\r
7997 { IDM_LoadGame, MF_BYCOMMAND|MF_GRAYED },
\r
7998 { IDM_LoadNextGame, MF_BYCOMMAND|MF_GRAYED },
\r
7999 { IDM_LoadPrevGame, MF_BYCOMMAND|MF_GRAYED },
\r
8000 { IDM_ReloadGame, MF_BYCOMMAND|MF_GRAYED },
\r
8001 { IDM_PasteGame, MF_BYCOMMAND|MF_GRAYED },
\r
8002 { IDM_LoadPosition, MF_BYCOMMAND|MF_GRAYED },
\r
8003 { IDM_LoadNextPosition, MF_BYCOMMAND|MF_GRAYED },
\r
8004 { IDM_LoadPrevPosition, MF_BYCOMMAND|MF_GRAYED },
\r
8005 { IDM_ReloadPosition, MF_BYCOMMAND|MF_GRAYED },
\r
8006 { IDM_PastePosition, MF_BYCOMMAND|MF_GRAYED },
\r
8007 { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },
\r
8008 { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },
\r
8009 { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },
\r
8010 // { IDM_Match, MF_BYCOMMAND|MF_GRAYED },
\r
8011 { IDM_TypeInMove, MF_BYCOMMAND|MF_GRAYED },
\r
8012 { IDM_RetractMove, MF_BYCOMMAND|MF_GRAYED },
\r
8016 Enables userThinkingEnables[] = {
\r
8017 { IDM_LoadGame, MF_BYCOMMAND|MF_ENABLED },
\r
8018 { IDM_LoadNextGame, MF_BYCOMMAND|MF_ENABLED },
\r
8019 { IDM_LoadPrevGame, MF_BYCOMMAND|MF_ENABLED },
\r
8020 { IDM_ReloadGame, MF_BYCOMMAND|MF_ENABLED },
\r
8021 { IDM_PasteGame, MF_BYCOMMAND|MF_ENABLED },
\r
8022 { IDM_LoadPosition, MF_BYCOMMAND|MF_ENABLED },
\r
8023 { IDM_LoadNextPosition, MF_BYCOMMAND|MF_ENABLED },
\r
8024 { IDM_LoadPrevPosition, MF_BYCOMMAND|MF_ENABLED },
\r
8025 { IDM_ReloadPosition, MF_BYCOMMAND|MF_ENABLED },
\r
8026 { IDM_PastePosition, MF_BYCOMMAND|MF_ENABLED },
\r
8027 { IDM_MachineWhite, MF_BYCOMMAND|MF_ENABLED },
\r
8028 { IDM_MachineBlack, MF_BYCOMMAND|MF_ENABLED },
\r
8029 { IDM_TwoMachines, MF_BYCOMMAND|MF_ENABLED },
\r
8030 // { IDM_Match, MF_BYCOMMAND|MF_ENABLED },
\r
8031 { IDM_TypeInMove, MF_BYCOMMAND|MF_ENABLED },
\r
8032 { IDM_RetractMove, MF_BYCOMMAND|MF_ENABLED },
\r
8036 /*---------------------------------------------------------------------------*\
\r
8038 * Front-end interface functions exported by XBoard.
\r
8039 * Functions appear in same order as prototypes in frontend.h.
\r
8041 \*---------------------------------------------------------------------------*/
\r
8043 CheckMark(UINT item, int state)
\r
8045 if(item) CheckMenuItem(GetMenu(hwndMain), item, MF_BYCOMMAND|state);
\r
8051 static UINT prevChecked = 0;
\r
8052 static int prevPausing = 0;
\r
8055 if (pausing != prevPausing) {
\r
8056 prevPausing = pausing;
\r
8057 (void) CheckMenuItem(GetMenu(hwndMain), IDM_Pause,
\r
8058 MF_BYCOMMAND|(pausing ? MF_CHECKED : MF_UNCHECKED));
\r
8059 if (hwndPause) SetWindowText(hwndPause, pausing ? "C" : "P");
\r
8062 switch (gameMode) {
\r
8063 case BeginningOfGame:
\r
8064 if (appData.icsActive)
\r
8065 nowChecked = IDM_IcsClient;
\r
8066 else if (appData.noChessProgram)
\r
8067 nowChecked = IDM_EditGame;
\r
8069 nowChecked = IDM_MachineBlack;
\r
8071 case MachinePlaysBlack:
\r
8072 nowChecked = IDM_MachineBlack;
\r
8074 case MachinePlaysWhite:
\r
8075 nowChecked = IDM_MachineWhite;
\r
8077 case TwoMachinesPlay:
\r
8078 nowChecked = IDM_TwoMachines;
\r
8081 nowChecked = IDM_AnalysisMode;
\r
8084 nowChecked = IDM_AnalyzeFile;
\r
8087 nowChecked = IDM_EditGame;
\r
8089 case PlayFromGameFile:
\r
8090 nowChecked = IDM_LoadGame;
\r
8092 case EditPosition:
\r
8093 nowChecked = IDM_EditPosition;
\r
8096 nowChecked = IDM_Training;
\r
8098 case IcsPlayingWhite:
\r
8099 case IcsPlayingBlack:
\r
8100 case IcsObserving:
\r
8102 nowChecked = IDM_IcsClient;
\r
8109 CheckMark(prevChecked, MF_UNCHECKED);
\r
8110 CheckMark(nowChecked, MF_CHECKED);
\r
8111 CheckMark(IDM_Match, matchMode && matchGame < appData.matchGames ? MF_CHECKED : MF_UNCHECKED);
\r
8113 if (nowChecked == IDM_LoadGame || nowChecked == IDM_Training) {
\r
8114 (void) EnableMenuItem(GetMenu(hwndMain), IDM_Training,
\r
8115 MF_BYCOMMAND|MF_ENABLED);
\r
8117 (void) EnableMenuItem(GetMenu(hwndMain),
\r
8118 IDM_Training, MF_BYCOMMAND|MF_GRAYED);
\r
8121 prevChecked = nowChecked;
\r
8123 /* [DM] icsEngineAnalyze - Do a sceure check too */
\r
8124 if (appData.icsActive) {
\r
8125 if (appData.icsEngineAnalyze) {
\r
8126 CheckMark(IDM_AnalysisMode, MF_CHECKED);
\r
8128 CheckMark(IDM_AnalysisMode, MF_UNCHECKED);
\r
8131 DisplayLogos(); // [HGM] logos: mode change could have altered logos
\r
8137 HMENU hmenu = GetMenu(hwndMain);
\r
8138 SetMenuEnables(hmenu, icsEnables);
\r
8139 EnableMenuItem(GetSubMenu(hmenu, OPTIONS_POS), IDM_IcsOptions,
\r
8140 MF_BYCOMMAND|MF_ENABLED);
\r
8142 if (appData.zippyPlay) {
\r
8143 SetMenuEnables(hmenu, zippyEnables);
\r
8144 if (!appData.noChessProgram) /* [DM] icsEngineAnalyze */
\r
8145 (void) EnableMenuItem(GetMenu(hwndMain), IDM_AnalysisMode,
\r
8146 MF_BYCOMMAND|MF_ENABLED);
\r
8154 SetMenuEnables(GetMenu(hwndMain), gnuEnables);
\r
8160 HMENU hmenu = GetMenu(hwndMain);
\r
8161 SetMenuEnables(hmenu, ncpEnables);
\r
8162 DrawMenuBar(hwndMain);
\r
8168 SetMenuEnables(GetMenu(hwndMain), cmailEnables);
\r
8172 SetTrainingModeOn()
\r
8175 SetMenuEnables(GetMenu(hwndMain), trainingOnEnables);
\r
8176 for (i = 0; i < N_BUTTONS; i++) {
\r
8177 if (buttonDesc[i].hwnd != NULL)
\r
8178 EnableWindow(buttonDesc[i].hwnd, FALSE);
\r
8183 VOID SetTrainingModeOff()
\r
8186 SetMenuEnables(GetMenu(hwndMain), trainingOffEnables);
\r
8187 for (i = 0; i < N_BUTTONS; i++) {
\r
8188 if (buttonDesc[i].hwnd != NULL)
\r
8189 EnableWindow(buttonDesc[i].hwnd, TRUE);
\r
8195 SetUserThinkingEnables()
\r
8197 SetMenuEnables(GetMenu(hwndMain), userThinkingEnables);
\r
8201 SetMachineThinkingEnables()
\r
8203 HMENU hMenu = GetMenu(hwndMain);
\r
8204 int flags = MF_BYCOMMAND|MF_ENABLED;
\r
8206 SetMenuEnables(hMenu, machineThinkingEnables);
\r
8208 if (gameMode == MachinePlaysBlack) {
\r
8209 (void)EnableMenuItem(hMenu, IDM_MachineBlack, flags);
\r
8210 } else if (gameMode == MachinePlaysWhite) {
\r
8211 (void)EnableMenuItem(hMenu, IDM_MachineWhite, flags);
\r
8212 } else if (gameMode == TwoMachinesPlay) {
\r
8213 (void)EnableMenuItem(hMenu, matchMode ? IDM_Match : IDM_TwoMachines, flags); // [HGM] match
\r
8219 DisplayTitle(char *str)
\r
8221 char title[MSG_SIZ], *host;
\r
8222 if (str[0] != NULLCHAR) {
\r
8223 safeStrCpy(title, str, sizeof(title)/sizeof(title[0]) );
\r
8224 } else if (appData.icsActive) {
\r
8225 if (appData.icsCommPort[0] != NULLCHAR)
\r
8228 host = appData.icsHost;
\r
8229 snprintf(title, MSG_SIZ, "%s: %s", szTitle, host);
\r
8230 } else if (appData.noChessProgram) {
\r
8231 safeStrCpy(title, szTitle, sizeof(title)/sizeof(title[0]) );
\r
8233 safeStrCpy(title, szTitle, sizeof(title)/sizeof(title[0]) );
\r
8234 strcat(title, ": ");
\r
8235 strcat(title, first.tidy);
\r
8237 SetWindowText(hwndMain, title);
\r
8242 DisplayMessage(char *str1, char *str2)
\r
8246 int remain = MESSAGE_TEXT_MAX - 1;
\r
8249 moveErrorMessageUp = FALSE; /* turned on later by caller if needed */
\r
8250 messageText[0] = NULLCHAR;
\r
8252 len = strlen(str1);
\r
8253 if (len > remain) len = remain;
\r
8254 strncpy(messageText, str1, len);
\r
8255 messageText[len] = NULLCHAR;
\r
8258 if (*str2 && remain >= 2) {
\r
8260 strcat(messageText, " ");
\r
8263 len = strlen(str2);
\r
8264 if (len > remain) len = remain;
\r
8265 strncat(messageText, str2, len);
\r
8267 messageText[MESSAGE_TEXT_MAX - 1] = NULLCHAR;
\r
8268 safeStrCpy(lastMsg, messageText, MSG_SIZ);
\r
8270 if (hwndMain == NULL || IsIconic(hwndMain)) return;
\r
8274 hdc = GetDC(hwndMain);
\r
8275 oldFont = SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);
\r
8276 ExtTextOut(hdc, messageRect.left, messageRect.top, ETO_CLIPPED|ETO_OPAQUE,
\r
8277 &messageRect, messageText, strlen(messageText), NULL);
\r
8278 (void) SelectObject(hdc, oldFont);
\r
8279 (void) ReleaseDC(hwndMain, hdc);
\r
8283 DisplayError(char *str, int error)
\r
8285 char buf[MSG_SIZ*2], buf2[MSG_SIZ];
\r
8289 safeStrCpy(buf, str, sizeof(buf)/sizeof(buf[0]) );
\r
8291 len = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,
\r
8292 NULL, error, LANG_NEUTRAL,
\r
8293 (LPSTR) buf2, MSG_SIZ, NULL);
\r
8295 snprintf(buf, 2*MSG_SIZ, "%s:\n%s", str, buf2);
\r
8297 ErrorMap *em = errmap;
\r
8298 while (em->err != 0 && em->err != error) em++;
\r
8299 if (em->err != 0) {
\r
8300 snprintf(buf, 2*MSG_SIZ, "%s:\n%s", str, em->msg);
\r
8302 snprintf(buf, 2*MSG_SIZ, "%s:\nError code %d", str, error);
\r
8307 ErrorPopUp(_("Error"), buf);
\r
8312 DisplayMoveError(char *str)
\r
8314 fromX = fromY = -1;
\r
8315 ClearHighlights();
\r
8316 DrawPosition(FALSE, NULL);
\r
8317 if (appData.popupMoveErrors) {
\r
8318 ErrorPopUp(_("Error"), str);
\r
8320 DisplayMessage(str, "");
\r
8321 moveErrorMessageUp = TRUE;
\r
8326 DisplayFatalError(char *str, int error, int exitStatus)
\r
8328 char buf[2*MSG_SIZ], buf2[MSG_SIZ];
\r
8330 char *label = exitStatus ? _("Fatal Error") : _("Exiting");
\r
8333 len = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,
\r
8334 NULL, error, LANG_NEUTRAL,
\r
8335 (LPSTR) buf2, MSG_SIZ, NULL);
\r
8337 snprintf(buf, 2*MSG_SIZ, "%s:\n%s", str, buf2);
\r
8339 ErrorMap *em = errmap;
\r
8340 while (em->err != 0 && em->err != error) em++;
\r
8341 if (em->err != 0) {
\r
8342 snprintf(buf, 2*MSG_SIZ, "%s:\n%s", str, em->msg);
\r
8344 snprintf(buf, 2*MSG_SIZ, "%s:\nError code %d", str, error);
\r
8349 if (appData.debugMode) {
\r
8350 fprintf(debugFP, "%s: %s\n", label, str);
\r
8352 if (appData.popupExitMessage) {
\r
8353 (void) MessageBox(hwndMain, str, label, MB_OK|
\r
8354 (exitStatus ? MB_ICONSTOP : MB_ICONINFORMATION));
\r
8356 ExitEvent(exitStatus);
\r
8361 DisplayInformation(char *str)
\r
8363 (void) MessageBox(hwndMain, str, _("Information"), MB_OK|MB_ICONINFORMATION);
\r
8368 DisplayNote(char *str)
\r
8370 ErrorPopUp(_("Note"), str);
\r
8375 char *title, *question, *replyPrefix;
\r
8380 QuestionDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
8382 static QuestionParams *qp;
\r
8383 char reply[MSG_SIZ];
\r
8386 switch (message) {
\r
8387 case WM_INITDIALOG:
\r
8388 qp = (QuestionParams *) lParam;
\r
8389 CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));
\r
8390 Translate(hDlg, DLG_Question);
\r
8391 SetWindowText(hDlg, qp->title);
\r
8392 SetDlgItemText(hDlg, OPT_QuestionText, qp->question);
\r
8393 SetFocus(GetDlgItem(hDlg, OPT_QuestionInput));
\r
8397 switch (LOWORD(wParam)) {
\r
8399 safeStrCpy(reply, qp->replyPrefix, sizeof(reply)/sizeof(reply[0]) );
\r
8400 if (*reply) strcat(reply, " ");
\r
8401 len = strlen(reply);
\r
8402 GetDlgItemText(hDlg, OPT_QuestionInput, reply + len, sizeof(reply) - len);
\r
8403 strcat(reply, "\n");
\r
8404 OutputToProcess(qp->pr, reply, strlen(reply), &err);
\r
8405 EndDialog(hDlg, TRUE);
\r
8406 if (err) DisplayFatalError(_("Error writing to chess program"), err, 1);
\r
8409 EndDialog(hDlg, FALSE);
\r
8420 AskQuestion(char* title, char *question, char *replyPrefix, ProcRef pr)
\r
8422 QuestionParams qp;
\r
8426 qp.question = question;
\r
8427 qp.replyPrefix = replyPrefix;
\r
8429 lpProc = MakeProcInstance((FARPROC)QuestionDialog, hInst);
\r
8430 DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_Question),
\r
8431 hwndMain, (DLGPROC)lpProc, (LPARAM)&qp);
\r
8432 FreeProcInstance(lpProc);
\r
8435 /* [AS] Pick FRC position */
\r
8436 LRESULT CALLBACK NewGameFRC_Proc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
8438 static int * lpIndexFRC;
\r
8444 case WM_INITDIALOG:
\r
8445 lpIndexFRC = (int *) lParam;
\r
8447 CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));
\r
8448 Translate(hDlg, DLG_NewGameFRC);
\r
8450 SendDlgItemMessage( hDlg, IDC_NFG_Edit, EM_SETLIMITTEXT, sizeof(buf)-1, 0 );
\r
8451 SetDlgItemInt( hDlg, IDC_NFG_Edit, *lpIndexFRC, TRUE );
\r
8452 SendDlgItemMessage( hDlg, IDC_NFG_Edit, EM_SETSEL, 0, -1 );
\r
8453 SetFocus(GetDlgItem(hDlg, IDC_NFG_Edit));
\r
8458 switch( LOWORD(wParam) ) {
\r
8460 *lpIndexFRC = GetDlgItemInt(hDlg, IDC_NFG_Edit, &index_is_ok, TRUE );
\r
8461 EndDialog( hDlg, 0 );
\r
8462 shuffleOpenings = TRUE; /* [HGM] shuffle: switch shuffling on for as long as we stay in current variant */
\r
8465 EndDialog( hDlg, 1 );
\r
8467 case IDC_NFG_Edit:
\r
8468 if( HIWORD(wParam) == EN_CHANGE ) {
\r
8469 GetDlgItemInt(hDlg, IDC_NFG_Edit, &index_is_ok, TRUE );
\r
8471 EnableWindow( GetDlgItem(hDlg, IDOK), index_is_ok );
\r
8474 case IDC_NFG_Random:
\r
8475 snprintf( buf, sizeof(buf)/sizeof(buf[0]), "%d", myrandom() ); /* [HGM] shuffle: no longer limit to 960 */
\r
8476 SetDlgItemText(hDlg, IDC_NFG_Edit, buf );
\r
8489 int index = appData.defaultFrcPosition;
\r
8490 FARPROC lpProc = MakeProcInstance( (FARPROC) NewGameFRC_Proc, hInst );
\r
8492 result = DialogBoxParam( hInst, MAKEINTRESOURCE(DLG_NewGameFRC), hwndMain, (DLGPROC)lpProc, (LPARAM)&index );
\r
8494 if( result == 0 ) {
\r
8495 appData.defaultFrcPosition = index;
\r
8501 /* [AS] Game list options. Refactored by HGM */
\r
8503 HWND gameListOptionsDialog;
\r
8505 // low-level front-end: clear text edit / list widget
\r
8509 SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_RESETCONTENT, 0, 0 );
\r
8512 // low-level front-end: clear text edit / list widget
\r
8514 GLT_DeSelectList()
\r
8516 SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_SETCURSEL, 0, 0 );
\r
8519 // low-level front-end: append line to text edit / list widget
\r
8521 GLT_AddToList( char *name )
\r
8524 SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_ADDSTRING, 0, (LPARAM) name );
\r
8528 // low-level front-end: get line from text edit / list widget
\r
8530 GLT_GetFromList( int index, char *name )
\r
8533 if( SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_GETTEXT, index, (LPARAM) name ) != LB_ERR )
\r
8539 void GLT_MoveSelection( HWND hDlg, int delta )
\r
8541 int idx1 = (int) SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETCURSEL, 0, 0 );
\r
8542 int idx2 = idx1 + delta;
\r
8543 int count = (int) SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETCOUNT, 0, 0 );
\r
8545 if( idx1 >=0 && idx1 < count && idx2 >= 0 && idx2 < count ) {
\r
8548 SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETTEXT, idx1, (LPARAM) buf );
\r
8549 SendDlgItemMessage( hDlg, IDC_GameListTags, LB_DELETESTRING, idx1, 0 );
\r
8550 SendDlgItemMessage( hDlg, IDC_GameListTags, LB_INSERTSTRING, idx2, (LPARAM) buf );
\r
8551 SendDlgItemMessage( hDlg, IDC_GameListTags, LB_SETCURSEL, idx2, 0 );
\r
8555 LRESULT CALLBACK GameListOptions_Proc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
8559 case WM_INITDIALOG:
\r
8560 gameListOptionsDialog = hDlg; // [HGM] pass through global to keep out off back-end
\r
8562 CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));
\r
8563 Translate(hDlg, DLG_GameListOptions);
\r
8565 /* Initialize list */
\r
8566 GLT_TagsToList( lpUserGLT );
\r
8568 SetFocus( GetDlgItem(hDlg, IDC_GameListTags) );
\r
8573 switch( LOWORD(wParam) ) {
\r
8576 EndDialog( hDlg, 0 );
\r
8579 EndDialog( hDlg, 1 );
\r
8582 case IDC_GLT_Default:
\r
8583 GLT_TagsToList( GLT_DEFAULT_TAGS );
\r
8586 case IDC_GLT_Restore:
\r
8587 GLT_TagsToList( appData.gameListTags );
\r
8591 GLT_MoveSelection( hDlg, -1 );
\r
8594 case IDC_GLT_Down:
\r
8595 GLT_MoveSelection( hDlg, +1 );
\r
8605 int GameListOptions()
\r
8608 FARPROC lpProc = MakeProcInstance( (FARPROC) GameListOptions_Proc, hInst );
\r
8610 safeStrCpy( lpUserGLT, appData.gameListTags ,LPUSERGLT_SIZE );
\r
8612 result = DialogBoxParam( hInst, MAKEINTRESOURCE(DLG_GameListOptions), hwndMain, (DLGPROC)lpProc, (LPARAM)lpUserGLT );
\r
8614 if( result == 0 ) {
\r
8615 /* [AS] Memory leak here! */
\r
8616 appData.gameListTags = strdup( lpUserGLT );
\r
8623 DisplayIcsInteractionTitle(char *str)
\r
8625 char consoleTitle[MSG_SIZ];
\r
8627 snprintf(consoleTitle, MSG_SIZ, "%s: %s", szConsoleTitle, str);
\r
8628 SetWindowText(hwndConsole, consoleTitle);
\r
8630 if(appData.chatBoxes) { // [HGM] chat: open chat boxes
\r
8631 char buf[MSG_SIZ], *p = buf, *q;
\r
8632 safeStrCpy(buf, appData.chatBoxes, sizeof(buf)/sizeof(buf[0]) );
\r
8634 q = strchr(p, ';');
\r
8636 if(*p) ChatPopUp(p);
\r
8640 SetActiveWindow(hwndMain);
\r
8644 DrawPosition(int fullRedraw, Board board)
\r
8646 HDCDrawPosition(NULL, (BOOLEAN) fullRedraw, board);
\r
8649 void NotifyFrontendLogin()
\r
8652 UpdateICSWidth(GetDlgItem(hwndConsole, OPT_ConsoleText));
\r
8658 fromX = fromY = -1;
\r
8659 if (dragInfo.pos.x != -1 || dragInfo.pos.y != -1) {
\r
8660 dragInfo.pos.x = dragInfo.pos.y = -1;
\r
8661 dragInfo.pos.x = dragInfo.pos.y = -1;
\r
8662 dragInfo.lastpos = dragInfo.pos;
\r
8663 dragInfo.start.x = dragInfo.start.y = -1;
\r
8664 dragInfo.from = dragInfo.start;
\r
8666 DrawPosition(TRUE, NULL);
\r
8673 CommentPopUp(char *title, char *str)
\r
8675 HWND hwnd = GetActiveWindow();
\r
8676 EitherCommentPopUp(currentMove, title, str, FALSE); // [HGM] vari: fake move index, rather than 0
\r
8678 SetActiveWindow(hwnd);
\r
8682 CommentPopDown(void)
\r
8684 CheckMenuItem(GetMenu(hwndMain), IDM_Comment, MF_UNCHECKED);
\r
8685 if (commentDialog) {
\r
8686 ShowWindow(commentDialog, SW_HIDE);
\r
8688 commentUp = FALSE;
\r
8692 EditCommentPopUp(int index, char *title, char *str)
\r
8694 EitherCommentPopUp(index, title, str, TRUE);
\r
8701 MyPlaySound(&sounds[(int)SoundMove]);
\r
8704 VOID PlayIcsWinSound()
\r
8706 MyPlaySound(&sounds[(int)SoundIcsWin]);
\r
8709 VOID PlayIcsLossSound()
\r
8711 MyPlaySound(&sounds[(int)SoundIcsLoss]);
\r
8714 VOID PlayIcsDrawSound()
\r
8716 MyPlaySound(&sounds[(int)SoundIcsDraw]);
\r
8719 VOID PlayIcsUnfinishedSound()
\r
8721 MyPlaySound(&sounds[(int)SoundIcsUnfinished]);
\r
8727 MyPlaySound(&sounds[(int)SoundAlarm]);
\r
8733 MyPlaySound(&textAttribs[ColorTell].sound);
\r
8741 consoleEcho = TRUE;
\r
8742 hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
8743 SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&consoleCF);
\r
8744 SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);
\r
8753 consoleEcho = FALSE;
\r
8754 hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
8755 /* This works OK: set text and background both to the same color */
\r
8757 cf.crTextColor = COLOR_ECHOOFF;
\r
8758 SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf);
\r
8759 SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, cf.crTextColor);
\r
8762 /* No Raw()...? */
\r
8764 void Colorize(ColorClass cc, int continuation)
\r
8766 currentColorClass = cc;
\r
8767 consoleCF.dwMask = CFM_COLOR|CFM_BOLD|CFM_ITALIC|CFM_UNDERLINE|CFM_STRIKEOUT;
\r
8768 consoleCF.crTextColor = textAttribs[cc].color;
\r
8769 consoleCF.dwEffects = textAttribs[cc].effects;
\r
8770 if (!continuation) MyPlaySound(&textAttribs[cc].sound);
\r
8776 static char buf[MSG_SIZ];
\r
8777 DWORD bufsiz = MSG_SIZ;
\r
8779 if(appData.userName != NULL && appData.userName[0] != 0) {
\r
8780 return appData.userName; /* [HGM] username: prefer name selected by user over his system login */
\r
8782 if (!GetUserName(buf, &bufsiz)) {
\r
8783 /*DisplayError("Error getting user name", GetLastError());*/
\r
8784 safeStrCpy(buf, _("User"), sizeof(buf)/sizeof(buf[0]) );
\r
8792 static char buf[MSG_SIZ];
\r
8793 DWORD bufsiz = MSG_SIZ;
\r
8795 if (!GetComputerName(buf, &bufsiz)) {
\r
8796 /*DisplayError("Error getting host name", GetLastError());*/
\r
8797 safeStrCpy(buf, _("Unknown"), sizeof(buf)/sizeof(buf[0]) );
\r
8804 ClockTimerRunning()
\r
8806 return clockTimerEvent != 0;
\r
8812 if (clockTimerEvent == 0) return FALSE;
\r
8813 KillTimer(hwndMain, clockTimerEvent);
\r
8814 clockTimerEvent = 0;
\r
8819 StartClockTimer(long millisec)
\r
8821 clockTimerEvent = SetTimer(hwndMain, (UINT) CLOCK_TIMER_ID,
\r
8822 (UINT) millisec, NULL);
\r
8826 DisplayWhiteClock(long timeRemaining, int highlight)
\r
8829 char *flag = whiteFlag && gameMode == TwoMachinesPlay ? "(!)" : "";
\r
8831 if(appData.noGUI) return;
\r
8832 hdc = GetDC(hwndMain);
\r
8833 if (!IsIconic(hwndMain)) {
\r
8834 DisplayAClock(hdc, timeRemaining, highlight,
\r
8835 flipClock ? &blackRect : &whiteRect, _("White"), flag);
\r
8837 if (highlight && iconCurrent == iconBlack) {
\r
8838 iconCurrent = iconWhite;
\r
8839 PostMessage(hwndMain, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);
\r
8840 if (IsIconic(hwndMain)) {
\r
8841 DrawIcon(hdc, 2, 2, iconCurrent);
\r
8844 (void) ReleaseDC(hwndMain, hdc);
\r
8846 PostMessage(hwndConsole, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);
\r
8850 DisplayBlackClock(long timeRemaining, int highlight)
\r
8853 char *flag = blackFlag && gameMode == TwoMachinesPlay ? "(!)" : "";
\r
8855 if(appData.noGUI) return;
\r
8856 hdc = GetDC(hwndMain);
\r
8857 if (!IsIconic(hwndMain)) {
\r
8858 DisplayAClock(hdc, timeRemaining, highlight,
\r
8859 flipClock ? &whiteRect : &blackRect, _("Black"), flag);
\r
8861 if (highlight && iconCurrent == iconWhite) {
\r
8862 iconCurrent = iconBlack;
\r
8863 PostMessage(hwndMain, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);
\r
8864 if (IsIconic(hwndMain)) {
\r
8865 DrawIcon(hdc, 2, 2, iconCurrent);
\r
8868 (void) ReleaseDC(hwndMain, hdc);
\r
8870 PostMessage(hwndConsole, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);
\r
8875 LoadGameTimerRunning()
\r
8877 return loadGameTimerEvent != 0;
\r
8881 StopLoadGameTimer()
\r
8883 if (loadGameTimerEvent == 0) return FALSE;
\r
8884 KillTimer(hwndMain, loadGameTimerEvent);
\r
8885 loadGameTimerEvent = 0;
\r
8890 StartLoadGameTimer(long millisec)
\r
8892 loadGameTimerEvent = SetTimer(hwndMain, (UINT) LOAD_GAME_TIMER_ID,
\r
8893 (UINT) millisec, NULL);
\r
8901 char fileTitle[MSG_SIZ];
\r
8903 defName = DefaultFileName(appData.oldSaveStyle ? "gam" : "pgn");
\r
8904 f = OpenFileDialog(hwndMain, "a", defName,
\r
8905 appData.oldSaveStyle ? "gam" : "pgn",
\r
8907 _("Save Game to File"), NULL, fileTitle, NULL);
\r
8909 SaveGame(f, 0, "");
\r
8916 ScheduleDelayedEvent(DelayedEventCallback cb, long millisec)
\r
8918 if (delayedTimerEvent != 0) {
\r
8919 if (appData.debugMode && cb != delayedTimerCallback) { // [HGM] alive: not too much debug
\r
8920 fprintf(debugFP, "ScheduleDelayedEvent: event already scheduled\n");
\r
8922 KillTimer(hwndMain, delayedTimerEvent);
\r
8923 delayedTimerEvent = 0;
\r
8924 if(delayedTimerCallback != cb) // [HGM] alive: do not "flush" same event, just postpone it
\r
8925 delayedTimerCallback();
\r
8927 delayedTimerCallback = cb;
\r
8928 delayedTimerEvent = SetTimer(hwndMain, (UINT) DELAYED_TIMER_ID,
\r
8929 (UINT) millisec, NULL);
\r
8932 DelayedEventCallback
\r
8935 if (delayedTimerEvent) {
\r
8936 return delayedTimerCallback;
\r
8943 CancelDelayedEvent()
\r
8945 if (delayedTimerEvent) {
\r
8946 KillTimer(hwndMain, delayedTimerEvent);
\r
8947 delayedTimerEvent = 0;
\r
8951 DWORD GetWin32Priority(int nice)
\r
8952 { // [HGM] nice: translate Unix nice() value to indows priority class. (Code stolen from Polyglot 1.4w11)
\r
8954 REALTIME_PRIORITY_CLASS 0x00000100
\r
8955 HIGH_PRIORITY_CLASS 0x00000080
\r
8956 ABOVE_NORMAL_PRIORITY_CLASS 0x00008000
\r
8957 NORMAL_PRIORITY_CLASS 0x00000020
\r
8958 BELOW_NORMAL_PRIORITY_CLASS 0x00004000
\r
8959 IDLE_PRIORITY_CLASS 0x00000040
\r
8961 if (nice < -15) return 0x00000080;
\r
8962 if (nice < 0) return 0x00008000;
\r
8963 if (nice == 0) return 0x00000020;
\r
8964 if (nice < 15) return 0x00004000;
\r
8965 return 0x00000040;
\r
8968 void RunCommand(char *cmdLine)
\r
8970 /* Now create the child process. */
\r
8971 STARTUPINFO siStartInfo;
\r
8972 PROCESS_INFORMATION piProcInfo;
\r
8974 siStartInfo.cb = sizeof(STARTUPINFO);
\r
8975 siStartInfo.lpReserved = NULL;
\r
8976 siStartInfo.lpDesktop = NULL;
\r
8977 siStartInfo.lpTitle = NULL;
\r
8978 siStartInfo.dwFlags = STARTF_USESTDHANDLES;
\r
8979 siStartInfo.cbReserved2 = 0;
\r
8980 siStartInfo.lpReserved2 = NULL;
\r
8981 siStartInfo.hStdInput = NULL;
\r
8982 siStartInfo.hStdOutput = NULL;
\r
8983 siStartInfo.hStdError = NULL;
\r
8985 CreateProcess(NULL,
\r
8986 cmdLine, /* command line */
\r
8987 NULL, /* process security attributes */
\r
8988 NULL, /* primary thread security attrs */
\r
8989 TRUE, /* handles are inherited */
\r
8990 DETACHED_PROCESS|CREATE_NEW_PROCESS_GROUP,
\r
8991 NULL, /* use parent's environment */
\r
8993 &siStartInfo, /* STARTUPINFO pointer */
\r
8994 &piProcInfo); /* receives PROCESS_INFORMATION */
\r
8996 CloseHandle(piProcInfo.hThread);
\r
8999 /* Start a child process running the given program.
\r
9000 The process's standard output can be read from "from", and its
\r
9001 standard input can be written to "to".
\r
9002 Exit with fatal error if anything goes wrong.
\r
9003 Returns an opaque pointer that can be used to destroy the process
\r
9007 StartChildProcess(char *cmdLine, char *dir, ProcRef *pr)
\r
9009 #define BUFSIZE 4096
\r
9011 HANDLE hChildStdinRd, hChildStdinWr,
\r
9012 hChildStdoutRd, hChildStdoutWr;
\r
9013 HANDLE hChildStdinWrDup, hChildStdoutRdDup;
\r
9014 SECURITY_ATTRIBUTES saAttr;
\r
9016 PROCESS_INFORMATION piProcInfo;
\r
9017 STARTUPINFO siStartInfo;
\r
9019 char buf[MSG_SIZ];
\r
9022 if (appData.debugMode) {
\r
9023 fprintf(debugFP, "StartChildProcess (dir=\"%s\") %s\n", dir, cmdLine);
\r
9028 /* Set the bInheritHandle flag so pipe handles are inherited. */
\r
9029 saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);
\r
9030 saAttr.bInheritHandle = TRUE;
\r
9031 saAttr.lpSecurityDescriptor = NULL;
\r
9034 * The steps for redirecting child's STDOUT:
\r
9035 * 1. Create anonymous pipe to be STDOUT for child.
\r
9036 * 2. Create a noninheritable duplicate of read handle,
\r
9037 * and close the inheritable read handle.
\r
9040 /* Create a pipe for the child's STDOUT. */
\r
9041 if (! CreatePipe(&hChildStdoutRd, &hChildStdoutWr, &saAttr, 0)) {
\r
9042 return GetLastError();
\r
9045 /* Duplicate the read handle to the pipe, so it is not inherited. */
\r
9046 fSuccess = DuplicateHandle(GetCurrentProcess(), hChildStdoutRd,
\r
9047 GetCurrentProcess(), &hChildStdoutRdDup, 0,
\r
9048 FALSE, /* not inherited */
\r
9049 DUPLICATE_SAME_ACCESS);
\r
9051 return GetLastError();
\r
9053 CloseHandle(hChildStdoutRd);
\r
9056 * The steps for redirecting child's STDIN:
\r
9057 * 1. Create anonymous pipe to be STDIN for child.
\r
9058 * 2. Create a noninheritable duplicate of write handle,
\r
9059 * and close the inheritable write handle.
\r
9062 /* Create a pipe for the child's STDIN. */
\r
9063 if (! CreatePipe(&hChildStdinRd, &hChildStdinWr, &saAttr, 0)) {
\r
9064 return GetLastError();
\r
9067 /* Duplicate the write handle to the pipe, so it is not inherited. */
\r
9068 fSuccess = DuplicateHandle(GetCurrentProcess(), hChildStdinWr,
\r
9069 GetCurrentProcess(), &hChildStdinWrDup, 0,
\r
9070 FALSE, /* not inherited */
\r
9071 DUPLICATE_SAME_ACCESS);
\r
9073 return GetLastError();
\r
9075 CloseHandle(hChildStdinWr);
\r
9077 /* Arrange to (1) look in dir for the child .exe file, and
\r
9078 * (2) have dir be the child's working directory. Interpret
\r
9079 * dir relative to the directory WinBoard loaded from. */
\r
9080 GetCurrentDirectory(MSG_SIZ, buf);
\r
9081 SetCurrentDirectory(installDir);
\r
9082 SetCurrentDirectory(dir);
\r
9084 /* Now create the child process. */
\r
9086 siStartInfo.cb = sizeof(STARTUPINFO);
\r
9087 siStartInfo.lpReserved = NULL;
\r
9088 siStartInfo.lpDesktop = NULL;
\r
9089 siStartInfo.lpTitle = NULL;
\r
9090 siStartInfo.dwFlags = STARTF_USESTDHANDLES;
\r
9091 siStartInfo.cbReserved2 = 0;
\r
9092 siStartInfo.lpReserved2 = NULL;
\r
9093 siStartInfo.hStdInput = hChildStdinRd;
\r
9094 siStartInfo.hStdOutput = hChildStdoutWr;
\r
9095 siStartInfo.hStdError = hChildStdoutWr;
\r
9097 fSuccess = CreateProcess(NULL,
\r
9098 cmdLine, /* command line */
\r
9099 NULL, /* process security attributes */
\r
9100 NULL, /* primary thread security attrs */
\r
9101 TRUE, /* handles are inherited */
\r
9102 DETACHED_PROCESS|CREATE_NEW_PROCESS_GROUP,
\r
9103 NULL, /* use parent's environment */
\r
9105 &siStartInfo, /* STARTUPINFO pointer */
\r
9106 &piProcInfo); /* receives PROCESS_INFORMATION */
\r
9108 err = GetLastError();
\r
9109 SetCurrentDirectory(buf); /* return to prev directory */
\r
9114 if (appData.niceEngines){ // [HGM] nice: adjust engine proc priority
\r
9115 if(appData.debugMode) fprintf(debugFP, "nice engine proc to %d\n", appData.niceEngines);
\r
9116 SetPriorityClass(piProcInfo.hProcess, GetWin32Priority(appData.niceEngines));
\r
9119 /* Close the handles we don't need in the parent */
\r
9120 CloseHandle(piProcInfo.hThread);
\r
9121 CloseHandle(hChildStdinRd);
\r
9122 CloseHandle(hChildStdoutWr);
\r
9124 /* Prepare return value */
\r
9125 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
\r
9126 cp->kind = CPReal;
\r
9127 cp->hProcess = piProcInfo.hProcess;
\r
9128 cp->pid = piProcInfo.dwProcessId;
\r
9129 cp->hFrom = hChildStdoutRdDup;
\r
9130 cp->hTo = hChildStdinWrDup;
\r
9132 *pr = (void *) cp;
\r
9134 /* Klaus Friedel says that this Sleep solves a problem under Windows
\r
9135 2000 where engines sometimes don't see the initial command(s)
\r
9136 from WinBoard and hang. I don't understand how that can happen,
\r
9137 but the Sleep is harmless, so I've put it in. Others have also
\r
9138 reported what may be the same problem, so hopefully this will fix
\r
9139 it for them too. */
\r
9147 DestroyChildProcess(ProcRef pr, int/*boolean*/ signal)
\r
9149 ChildProc *cp; int result;
\r
9151 cp = (ChildProc *) pr;
\r
9152 if (cp == NULL) return;
\r
9154 switch (cp->kind) {
\r
9156 /* TerminateProcess is considered harmful, so... */
\r
9157 CloseHandle(cp->hTo); /* Closing this will give the child an EOF and hopefully kill it */
\r
9158 if (cp->hFrom) CloseHandle(cp->hFrom); /* if NULL, InputThread will close it */
\r
9159 /* The following doesn't work because the chess program
\r
9160 doesn't "have the same console" as WinBoard. Maybe
\r
9161 we could arrange for this even though neither WinBoard
\r
9162 nor the chess program uses a console for stdio? */
\r
9163 /*!!if (signal) GenerateConsoleCtrlEvent(CTRL_BREAK_EVENT, cp->pid);*/
\r
9165 /* [AS] Special termination modes for misbehaving programs... */
\r
9166 if( signal == 9 ) {
\r
9167 result = TerminateProcess( cp->hProcess, 0 );
\r
9169 if ( appData.debugMode) {
\r
9170 fprintf( debugFP, "Terminating process %lu, result=%d\n", cp->pid, result );
\r
9173 else if( signal == 10 ) {
\r
9174 DWORD dw = WaitForSingleObject( cp->hProcess, 3*1000 ); // Wait 3 seconds at most
\r
9176 if( dw != WAIT_OBJECT_0 ) {
\r
9177 result = TerminateProcess( cp->hProcess, 0 );
\r
9179 if ( appData.debugMode) {
\r
9180 fprintf( debugFP, "Process %lu still alive after timeout, killing... result=%d\n", cp->pid, result );
\r
9186 CloseHandle(cp->hProcess);
\r
9190 if (cp->hFrom) CloseHandle(cp->hFrom);
\r
9194 closesocket(cp->sock);
\r
9199 if (signal) send(cp->sock2, "\017", 1, 0); /* 017 = 15 = SIGTERM */
\r
9200 closesocket(cp->sock);
\r
9201 closesocket(cp->sock2);
\r
9209 InterruptChildProcess(ProcRef pr)
\r
9213 cp = (ChildProc *) pr;
\r
9214 if (cp == NULL) return;
\r
9215 switch (cp->kind) {
\r
9217 /* The following doesn't work because the chess program
\r
9218 doesn't "have the same console" as WinBoard. Maybe
\r
9219 we could arrange for this even though neither WinBoard
\r
9220 nor the chess program uses a console for stdio */
\r
9221 /*!!GenerateConsoleCtrlEvent(CTRL_C_EVENT, cp->pid);*/
\r
9226 /* Can't interrupt */
\r
9230 send(cp->sock2, "\002", 1, 0); /* 2 = SIGINT */
\r
9237 OpenTelnet(char *host, char *port, ProcRef *pr)
\r
9239 char cmdLine[MSG_SIZ];
\r
9241 if (port[0] == NULLCHAR) {
\r
9242 snprintf(cmdLine, MSG_SIZ, "%s %s", appData.telnetProgram, host);
\r
9244 snprintf(cmdLine, MSG_SIZ, "%s %s %s", appData.telnetProgram, host, port);
\r
9246 return StartChildProcess(cmdLine, "", pr);
\r
9250 /* Code to open TCP sockets */
\r
9253 OpenTCP(char *host, char *port, ProcRef *pr)
\r
9259 struct sockaddr_in sa, mysa;
\r
9260 struct hostent FAR *hp;
\r
9261 unsigned short uport;
\r
9262 WORD wVersionRequested;
\r
9265 /* Initialize socket DLL */
\r
9266 wVersionRequested = MAKEWORD(1, 1);
\r
9267 err = WSAStartup(wVersionRequested, &wsaData);
\r
9268 if (err != 0) return err;
\r
9271 if ((s = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {
\r
9272 err = WSAGetLastError();
\r
9277 /* Bind local address using (mostly) don't-care values.
\r
9279 memset((char *) &mysa, 0, sizeof(struct sockaddr_in));
\r
9280 mysa.sin_family = AF_INET;
\r
9281 mysa.sin_addr.s_addr = INADDR_ANY;
\r
9282 uport = (unsigned short) 0;
\r
9283 mysa.sin_port = htons(uport);
\r
9284 if (bind(s, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))
\r
9285 == SOCKET_ERROR) {
\r
9286 err = WSAGetLastError();
\r
9291 /* Resolve remote host name */
\r
9292 memset((char *) &sa, 0, sizeof(struct sockaddr_in));
\r
9293 if (!(hp = gethostbyname(host))) {
\r
9294 unsigned int b0, b1, b2, b3;
\r
9296 err = WSAGetLastError();
\r
9298 if (sscanf(host, "%u.%u.%u.%u", &b0, &b1, &b2, &b3) == 4) {
\r
9299 hp = (struct hostent *) calloc(1, sizeof(struct hostent));
\r
9300 hp->h_addrtype = AF_INET;
\r
9302 hp->h_addr_list = (char **) calloc(2, sizeof(char *));
\r
9303 hp->h_addr_list[0] = (char *) malloc(4);
\r
9304 hp->h_addr_list[0][0] = (char) b0;
\r
9305 hp->h_addr_list[0][1] = (char) b1;
\r
9306 hp->h_addr_list[0][2] = (char) b2;
\r
9307 hp->h_addr_list[0][3] = (char) b3;
\r
9313 sa.sin_family = hp->h_addrtype;
\r
9314 uport = (unsigned short) atoi(port);
\r
9315 sa.sin_port = htons(uport);
\r
9316 memcpy((char *) &sa.sin_addr, hp->h_addr, hp->h_length);
\r
9318 /* Make connection */
\r
9319 if (connect(s, (struct sockaddr *) &sa,
\r
9320 sizeof(struct sockaddr_in)) == SOCKET_ERROR) {
\r
9321 err = WSAGetLastError();
\r
9326 /* Prepare return value */
\r
9327 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
\r
9328 cp->kind = CPSock;
\r
9330 *pr = (ProcRef *) cp;
\r
9336 OpenCommPort(char *name, ProcRef *pr)
\r
9341 char fullname[MSG_SIZ];
\r
9343 if (*name != '\\')
\r
9344 snprintf(fullname, MSG_SIZ, "\\\\.\\%s", name);
\r
9346 safeStrCpy(fullname, name, sizeof(fullname)/sizeof(fullname[0]) );
\r
9348 h = CreateFile(name, GENERIC_READ | GENERIC_WRITE,
\r
9349 0, NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL);
\r
9350 if (h == (HANDLE) -1) {
\r
9351 return GetLastError();
\r
9355 if (!SetCommState(h, (LPDCB) &dcb)) return GetLastError();
\r
9357 /* Accumulate characters until a 100ms pause, then parse */
\r
9358 ct.ReadIntervalTimeout = 100;
\r
9359 ct.ReadTotalTimeoutMultiplier = 0;
\r
9360 ct.ReadTotalTimeoutConstant = 0;
\r
9361 ct.WriteTotalTimeoutMultiplier = 0;
\r
9362 ct.WriteTotalTimeoutConstant = 0;
\r
9363 if (!SetCommTimeouts(h, (LPCOMMTIMEOUTS) &ct)) return GetLastError();
\r
9365 /* Prepare return value */
\r
9366 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
\r
9367 cp->kind = CPComm;
\r
9370 *pr = (ProcRef *) cp;
\r
9376 OpenLoopback(ProcRef *pr)
\r
9378 DisplayFatalError(_("Not implemented"), 0, 1);
\r
9384 OpenRcmd(char* host, char* user, char* cmd, ProcRef* pr)
\r
9389 struct sockaddr_in sa, mysa;
\r
9390 struct hostent FAR *hp;
\r
9391 unsigned short uport;
\r
9392 WORD wVersionRequested;
\r
9395 char stderrPortStr[MSG_SIZ];
\r
9397 /* Initialize socket DLL */
\r
9398 wVersionRequested = MAKEWORD(1, 1);
\r
9399 err = WSAStartup(wVersionRequested, &wsaData);
\r
9400 if (err != 0) return err;
\r
9402 /* Resolve remote host name */
\r
9403 memset((char *) &sa, 0, sizeof(struct sockaddr_in));
\r
9404 if (!(hp = gethostbyname(host))) {
\r
9405 unsigned int b0, b1, b2, b3;
\r
9407 err = WSAGetLastError();
\r
9409 if (sscanf(host, "%u.%u.%u.%u", &b0, &b1, &b2, &b3) == 4) {
\r
9410 hp = (struct hostent *) calloc(1, sizeof(struct hostent));
\r
9411 hp->h_addrtype = AF_INET;
\r
9413 hp->h_addr_list = (char **) calloc(2, sizeof(char *));
\r
9414 hp->h_addr_list[0] = (char *) malloc(4);
\r
9415 hp->h_addr_list[0][0] = (char) b0;
\r
9416 hp->h_addr_list[0][1] = (char) b1;
\r
9417 hp->h_addr_list[0][2] = (char) b2;
\r
9418 hp->h_addr_list[0][3] = (char) b3;
\r
9424 sa.sin_family = hp->h_addrtype;
\r
9425 uport = (unsigned short) 514;
\r
9426 sa.sin_port = htons(uport);
\r
9427 memcpy((char *) &sa.sin_addr, hp->h_addr, hp->h_length);
\r
9429 /* Bind local socket to unused "privileged" port address
\r
9431 s = INVALID_SOCKET;
\r
9432 memset((char *) &mysa, 0, sizeof(struct sockaddr_in));
\r
9433 mysa.sin_family = AF_INET;
\r
9434 mysa.sin_addr.s_addr = INADDR_ANY;
\r
9435 for (fromPort = 1023;; fromPort--) {
\r
9436 if (fromPort < 0) {
\r
9438 return WSAEADDRINUSE;
\r
9440 if (s == INVALID_SOCKET) {
\r
9441 if ((s = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {
\r
9442 err = WSAGetLastError();
\r
9447 uport = (unsigned short) fromPort;
\r
9448 mysa.sin_port = htons(uport);
\r
9449 if (bind(s, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))
\r
9450 == SOCKET_ERROR) {
\r
9451 err = WSAGetLastError();
\r
9452 if (err == WSAEADDRINUSE) continue;
\r
9456 if (connect(s, (struct sockaddr *) &sa,
\r
9457 sizeof(struct sockaddr_in)) == SOCKET_ERROR) {
\r
9458 err = WSAGetLastError();
\r
9459 if (err == WSAEADDRINUSE) {
\r
9470 /* Bind stderr local socket to unused "privileged" port address
\r
9472 s2 = INVALID_SOCKET;
\r
9473 memset((char *) &mysa, 0, sizeof(struct sockaddr_in));
\r
9474 mysa.sin_family = AF_INET;
\r
9475 mysa.sin_addr.s_addr = INADDR_ANY;
\r
9476 for (fromPort = 1023;; fromPort--) {
\r
9477 if (fromPort == prevStderrPort) continue; // don't reuse port
\r
9478 if (fromPort < 0) {
\r
9479 (void) closesocket(s);
\r
9481 return WSAEADDRINUSE;
\r
9483 if (s2 == INVALID_SOCKET) {
\r
9484 if ((s2 = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {
\r
9485 err = WSAGetLastError();
\r
9491 uport = (unsigned short) fromPort;
\r
9492 mysa.sin_port = htons(uport);
\r
9493 if (bind(s2, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))
\r
9494 == SOCKET_ERROR) {
\r
9495 err = WSAGetLastError();
\r
9496 if (err == WSAEADDRINUSE) continue;
\r
9497 (void) closesocket(s);
\r
9501 if (listen(s2, 1) == SOCKET_ERROR) {
\r
9502 err = WSAGetLastError();
\r
9503 if (err == WSAEADDRINUSE) {
\r
9505 s2 = INVALID_SOCKET;
\r
9508 (void) closesocket(s);
\r
9509 (void) closesocket(s2);
\r
9515 prevStderrPort = fromPort; // remember port used
\r
9516 snprintf(stderrPortStr, MSG_SIZ, "%d", fromPort);
\r
9518 if (send(s, stderrPortStr, strlen(stderrPortStr) + 1, 0) == SOCKET_ERROR) {
\r
9519 err = WSAGetLastError();
\r
9520 (void) closesocket(s);
\r
9521 (void) closesocket(s2);
\r
9526 if (send(s, UserName(), strlen(UserName()) + 1, 0) == SOCKET_ERROR) {
\r
9527 err = WSAGetLastError();
\r
9528 (void) closesocket(s);
\r
9529 (void) closesocket(s2);
\r
9533 if (*user == NULLCHAR) user = UserName();
\r
9534 if (send(s, user, strlen(user) + 1, 0) == SOCKET_ERROR) {
\r
9535 err = WSAGetLastError();
\r
9536 (void) closesocket(s);
\r
9537 (void) closesocket(s2);
\r
9541 if (send(s, cmd, strlen(cmd) + 1, 0) == SOCKET_ERROR) {
\r
9542 err = WSAGetLastError();
\r
9543 (void) closesocket(s);
\r
9544 (void) closesocket(s2);
\r
9549 if ((s3 = accept(s2, NULL, NULL)) == INVALID_SOCKET) {
\r
9550 err = WSAGetLastError();
\r
9551 (void) closesocket(s);
\r
9552 (void) closesocket(s2);
\r
9556 (void) closesocket(s2); /* Stop listening */
\r
9558 /* Prepare return value */
\r
9559 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
\r
9560 cp->kind = CPRcmd;
\r
9563 *pr = (ProcRef *) cp;
\r
9570 AddInputSource(ProcRef pr, int lineByLine,
\r
9571 InputCallback func, VOIDSTAR closure)
\r
9573 InputSource *is, *is2 = NULL;
\r
9574 ChildProc *cp = (ChildProc *) pr;
\r
9576 is = (InputSource *) calloc(1, sizeof(InputSource));
\r
9577 is->lineByLine = lineByLine;
\r
9579 is->closure = closure;
\r
9580 is->second = NULL;
\r
9581 is->next = is->buf;
\r
9582 if (pr == NoProc) {
\r
9583 is->kind = CPReal;
\r
9584 consoleInputSource = is;
\r
9586 is->kind = cp->kind;
\r
9588 [AS] Try to avoid a race condition if the thread is given control too early:
\r
9589 we create all threads suspended so that the is->hThread variable can be
\r
9590 safely assigned, then let the threads start with ResumeThread.
\r
9592 switch (cp->kind) {
\r
9594 is->hFile = cp->hFrom;
\r
9595 cp->hFrom = NULL; /* now owned by InputThread */
\r
9597 CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) NonOvlInputThread,
\r
9598 (LPVOID) is, CREATE_SUSPENDED, &is->id);
\r
9602 is->hFile = cp->hFrom;
\r
9603 cp->hFrom = NULL; /* now owned by InputThread */
\r
9605 CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) InputThread,
\r
9606 (LPVOID) is, CREATE_SUSPENDED, &is->id);
\r
9610 is->sock = cp->sock;
\r
9612 CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,
\r
9613 (LPVOID) is, CREATE_SUSPENDED, &is->id);
\r
9617 is2 = (InputSource *) calloc(1, sizeof(InputSource));
\r
9619 is->sock = cp->sock;
\r
9621 is2->sock = cp->sock2;
\r
9622 is2->second = is2;
\r
9624 CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,
\r
9625 (LPVOID) is, CREATE_SUSPENDED, &is->id);
\r
9627 CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,
\r
9628 (LPVOID) is2, CREATE_SUSPENDED, &is2->id);
\r
9632 if( is->hThread != NULL ) {
\r
9633 ResumeThread( is->hThread );
\r
9636 if( is2 != NULL && is2->hThread != NULL ) {
\r
9637 ResumeThread( is2->hThread );
\r
9641 return (InputSourceRef) is;
\r
9645 RemoveInputSource(InputSourceRef isr)
\r
9649 is = (InputSource *) isr;
\r
9650 is->hThread = NULL; /* tell thread to stop */
\r
9651 CloseHandle(is->hThread);
\r
9652 if (is->second != NULL) {
\r
9653 is->second->hThread = NULL;
\r
9654 CloseHandle(is->second->hThread);
\r
9658 int no_wrap(char *message, int count)
\r
9660 ConsoleOutput(message, count, FALSE);
\r
9665 OutputToProcess(ProcRef pr, char *message, int count, int *outError)
\r
9668 int outCount = SOCKET_ERROR;
\r
9669 ChildProc *cp = (ChildProc *) pr;
\r
9670 static OVERLAPPED ovl;
\r
9671 static int line = 0;
\r
9675 if (appData.noJoin || !appData.useInternalWrap)
\r
9676 return no_wrap(message, count);
\r
9679 int width = get_term_width();
\r
9680 int len = wrap(NULL, message, count, width, &line);
\r
9681 char *msg = malloc(len);
\r
9685 return no_wrap(message, count);
\r
9688 dbgchk = wrap(msg, message, count, width, &line);
\r
9689 if (dbgchk != len && appData.debugMode)
\r
9690 fprintf(debugFP, "wrap(): dbgchk(%d) != len(%d)\n", dbgchk, len);
\r
9691 ConsoleOutput(msg, len, FALSE);
\r
9698 if (ovl.hEvent == NULL) {
\r
9699 ovl.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
\r
9701 ovl.Internal = ovl.InternalHigh = ovl.Offset = ovl.OffsetHigh = 0;
\r
9703 switch (cp->kind) {
\r
9706 outCount = send(cp->sock, message, count, 0);
\r
9707 if (outCount == SOCKET_ERROR) {
\r
9708 *outError = WSAGetLastError();
\r
9710 *outError = NO_ERROR;
\r
9715 if (WriteFile(((ChildProc *)pr)->hTo, message, count,
\r
9716 &dOutCount, NULL)) {
\r
9717 *outError = NO_ERROR;
\r
9718 outCount = (int) dOutCount;
\r
9720 *outError = GetLastError();
\r
9725 *outError = DoWriteFile(((ChildProc *)pr)->hTo, message, count,
\r
9726 &dOutCount, &ovl);
\r
9727 if (*outError == NO_ERROR) {
\r
9728 outCount = (int) dOutCount;
\r
9738 if(n != 0) Sleep(n);
\r
9742 OutputToProcessDelayed(ProcRef pr, char *message, int count, int *outError,
\r
9745 /* Ignore delay, not implemented for WinBoard */
\r
9746 return OutputToProcess(pr, message, count, outError);
\r
9751 CmailSigHandlerCallBack(InputSourceRef isr, VOIDSTAR closure,
\r
9752 char *buf, int count, int error)
\r
9754 DisplayFatalError(_("Not implemented"), 0, 1);
\r
9757 /* see wgamelist.c for Game List functions */
\r
9758 /* see wedittags.c for Edit Tags functions */
\r
9765 char buf[MSG_SIZ];
\r
9768 if (SearchPath(installDir, appData.icsLogon, NULL, MSG_SIZ, buf, &dummy)) {
\r
9769 f = fopen(buf, "r");
\r
9771 ProcessICSInitScript(f);
\r
9779 StartAnalysisClock()
\r
9781 if (analysisTimerEvent) return;
\r
9782 analysisTimerEvent = SetTimer(hwndMain, (UINT) ANALYSIS_TIMER_ID,
\r
9783 (UINT) 2000, NULL);
\r
9787 SetHighlights(int fromX, int fromY, int toX, int toY)
\r
9789 highlightInfo.sq[0].x = fromX;
\r
9790 highlightInfo.sq[0].y = fromY;
\r
9791 highlightInfo.sq[1].x = toX;
\r
9792 highlightInfo.sq[1].y = toY;
\r
9798 highlightInfo.sq[0].x = highlightInfo.sq[0].y =
\r
9799 highlightInfo.sq[1].x = highlightInfo.sq[1].y = -1;
\r
9803 SetPremoveHighlights(int fromX, int fromY, int toX, int toY)
\r
9805 premoveHighlightInfo.sq[0].x = fromX;
\r
9806 premoveHighlightInfo.sq[0].y = fromY;
\r
9807 premoveHighlightInfo.sq[1].x = toX;
\r
9808 premoveHighlightInfo.sq[1].y = toY;
\r
9812 ClearPremoveHighlights()
\r
9814 premoveHighlightInfo.sq[0].x = premoveHighlightInfo.sq[0].y =
\r
9815 premoveHighlightInfo.sq[1].x = premoveHighlightInfo.sq[1].y = -1;
\r
9819 ShutDownFrontEnd()
\r
9821 if (saveSettingsOnExit) SaveSettings(settingsFileName);
\r
9822 DeleteClipboardTempFiles();
\r
9828 if (IsIconic(hwndMain))
\r
9829 ShowWindow(hwndMain, SW_RESTORE);
\r
9831 SetActiveWindow(hwndMain);
\r
9835 * Prototypes for animation support routines
\r
9837 static void ScreenSquare(int column, int row, POINT * pt);
\r
9838 static void Tween( POINT * start, POINT * mid, POINT * finish, int factor,
\r
9839 POINT frames[], int * nFrames);
\r
9845 AnimateAtomicCapture(Board board, int fromX, int fromY, int toX, int toY)
\r
9846 { // [HGM] atomic: animate blast wave
\r
9849 explodeInfo.fromX = fromX;
\r
9850 explodeInfo.fromY = fromY;
\r
9851 explodeInfo.toX = toX;
\r
9852 explodeInfo.toY = toY;
\r
9853 for(i=1; i<4*kFactor; i++) {
\r
9854 explodeInfo.radius = (i*180)/(4*kFactor-1);
\r
9855 DrawPosition(FALSE, board);
\r
9856 Sleep(appData.animSpeed);
\r
9858 explodeInfo.radius = 0;
\r
9859 DrawPosition(TRUE, board);
\r
9863 AnimateMove(board, fromX, fromY, toX, toY)
\r
9870 ChessSquare piece;
\r
9871 POINT start, finish, mid;
\r
9872 POINT frames[kFactor * 2 + 1];
\r
9875 if (!appData.animate) return;
\r
9876 if (doingSizing) return;
\r
9877 if (fromY < 0 || fromX < 0) return;
\r
9878 piece = board[fromY][fromX];
\r
9879 if (piece >= EmptySquare) return;
\r
9881 ScreenSquare(fromX, fromY, &start);
\r
9882 ScreenSquare(toX, toY, &finish);
\r
9884 /* All moves except knight jumps move in straight line */
\r
9885 if (!(abs(fromX-toX) == 1 && abs(fromY-toY) == 2 || abs(fromX-toX) == 2 && abs(fromY-toY) == 1)) {
\r
9886 mid.x = start.x + (finish.x - start.x) / 2;
\r
9887 mid.y = start.y + (finish.y - start.y) / 2;
\r
9889 /* Knight: make straight movement then diagonal */
\r
9890 if (abs(toY - fromY) < abs(toX - fromX)) {
\r
9891 mid.x = start.x + (finish.x - start.x) / 2;
\r
9895 mid.y = start.y + (finish.y - start.y) / 2;
\r
9899 /* Don't use as many frames for very short moves */
\r
9900 if (abs(toY - fromY) + abs(toX - fromX) <= 2)
\r
9901 Tween(&start, &mid, &finish, kFactor - 1, frames, &nFrames);
\r
9903 Tween(&start, &mid, &finish, kFactor, frames, &nFrames);
\r
9905 animInfo.from.x = fromX;
\r
9906 animInfo.from.y = fromY;
\r
9907 animInfo.to.x = toX;
\r
9908 animInfo.to.y = toY;
\r
9909 animInfo.lastpos = start;
\r
9910 animInfo.piece = piece;
\r
9911 for (n = 0; n < nFrames; n++) {
\r
9912 animInfo.pos = frames[n];
\r
9913 DrawPosition(FALSE, NULL);
\r
9914 animInfo.lastpos = animInfo.pos;
\r
9915 Sleep(appData.animSpeed);
\r
9917 animInfo.pos = finish;
\r
9918 DrawPosition(FALSE, NULL);
\r
9919 animInfo.piece = EmptySquare;
\r
9920 Explode(board, fromX, fromY, toX, toY);
\r
9923 /* Convert board position to corner of screen rect and color */
\r
9926 ScreenSquare(column, row, pt)
\r
9927 int column; int row; POINT * pt;
\r
9930 pt->x = lineGap + ((BOARD_WIDTH-1)-column) * (squareSize + lineGap) + border;
\r
9931 pt->y = lineGap + row * (squareSize + lineGap) + border;
\r
9933 pt->x = lineGap + column * (squareSize + lineGap) + border;
\r
9934 pt->y = lineGap + ((BOARD_HEIGHT-1)-row) * (squareSize + lineGap) + border;
\r
9938 /* Generate a series of frame coords from start->mid->finish.
\r
9939 The movement rate doubles until the half way point is
\r
9940 reached, then halves back down to the final destination,
\r
9941 which gives a nice slow in/out effect. The algorithmn
\r
9942 may seem to generate too many intermediates for short
\r
9943 moves, but remember that the purpose is to attract the
\r
9944 viewers attention to the piece about to be moved and
\r
9945 then to where it ends up. Too few frames would be less
\r
9949 Tween(start, mid, finish, factor, frames, nFrames)
\r
9950 POINT * start; POINT * mid;
\r
9951 POINT * finish; int factor;
\r
9952 POINT frames[]; int * nFrames;
\r
9954 int n, fraction = 1, count = 0;
\r
9956 /* Slow in, stepping 1/16th, then 1/8th, ... */
\r
9957 for (n = 0; n < factor; n++)
\r
9959 for (n = 0; n < factor; n++) {
\r
9960 frames[count].x = start->x + (mid->x - start->x) / fraction;
\r
9961 frames[count].y = start->y + (mid->y - start->y) / fraction;
\r
9963 fraction = fraction / 2;
\r
9967 frames[count] = *mid;
\r
9970 /* Slow out, stepping 1/2, then 1/4, ... */
\r
9972 for (n = 0; n < factor; n++) {
\r
9973 frames[count].x = finish->x - (finish->x - mid->x) / fraction;
\r
9974 frames[count].y = finish->y - (finish->y - mid->y) / fraction;
\r
9976 fraction = fraction * 2;
\r
9982 SettingsPopUp(ChessProgramState *cps)
\r
9983 { // [HGM] wrapper needed because handles must not be passed through back-end
\r
9984 EngineOptionsPopup(savedHwnd, cps);
\r
9987 int flock(int fid, int code)
\r
9989 HANDLE hFile = (HANDLE) _get_osfhandle(fid);
\r
9993 ov.OffsetHigh = 0;
\r
9995 case 1: LockFileEx(hFile, LOCKFILE_EXCLUSIVE_LOCK, 0, 1024, 0, &ov); break; // LOCK_SH
\r
9996 case 2: LockFileEx(hFile, LOCKFILE_EXCLUSIVE_LOCK, 0, 1024, 0, &ov); break; // LOCK_EX
\r
9997 case 3: UnlockFileEx(hFile, 0, 1024, 0, &ov); break; // LOCK_UN
\r
9998 default: return -1;
\r