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 if(liteBackTexture) DeleteObject(liteBackTexture);
\r
1034 liteBackTexture = LoadImage( 0, appData.liteBackTextureFile, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );
\r
1035 liteBackTextureMode = appData.liteBackTextureMode;
\r
1037 if (liteBackTexture == NULL && appData.debugMode) {
\r
1038 fprintf( debugFP, "Unable to load lite texture bitmap '%s'\n", appData.liteBackTextureFile );
\r
1042 if( appData.darkBackTextureFile && appData.darkBackTextureFile[0] != NULLCHAR && appData.darkBackTextureFile[0] != '*' ) {
\r
1043 if(darkBackTexture) DeleteObject(darkBackTexture);
\r
1044 darkBackTexture = LoadImage( 0, appData.darkBackTextureFile, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );
\r
1045 darkBackTextureMode = appData.darkBackTextureMode;
\r
1047 if (darkBackTexture == NULL && appData.debugMode) {
\r
1048 fprintf( debugFP, "Unable to load dark texture bitmap '%s'\n", appData.darkBackTextureFile );
\r
1054 InitInstance(HINSTANCE hInstance, int nCmdShow, LPSTR lpCmdLine)
\r
1056 HWND hwnd; /* Main window handle. */
\r
1058 WINDOWPLACEMENT wp;
\r
1061 hInst = hInstance; /* Store instance handle in our global variable */
\r
1062 programName = szAppName;
\r
1064 if (SearchPath(NULL, "WinBoard.exe", NULL, MSG_SIZ, installDir, &filepart)) {
\r
1065 *filepart = NULLCHAR;
\r
1067 GetCurrentDirectory(MSG_SIZ, installDir);
\r
1069 gameInfo.boardWidth = gameInfo.boardHeight = 8; // [HGM] won't have open window otherwise
\r
1070 screenWidth = screenHeight = 1000; // [HGM] placement: kludge to allow calling EnsureOnScreen from InitAppData
\r
1071 InitAppData(lpCmdLine); /* Get run-time parameters */
\r
1072 /* xboard, and older WinBoards, controlled the move sound with the
\r
1073 appData.ringBellAfterMoves option. In the current WinBoard, we
\r
1074 always turn the option on (so that the backend will call us),
\r
1075 then let the user turn the sound off by setting it to silence if
\r
1076 desired. To accommodate old winboard.ini files saved by old
\r
1077 versions of WinBoard, we also turn off the sound if the option
\r
1078 was initially set to false. [HGM] taken out of InitAppData */
\r
1079 if (!appData.ringBellAfterMoves) {
\r
1080 sounds[(int)SoundMove].name = strdup("");
\r
1081 appData.ringBellAfterMoves = TRUE;
\r
1083 if (appData.debugMode) {
\r
1084 debugFP = fopen(appData.nameOfDebugFile, "w");
\r
1085 setbuf(debugFP, NULL);
\r
1088 LoadLanguageFile(appData.language);
\r
1092 // InitEngineUCI( installDir, &first ); // [HGM] incorporated in InitBackEnd1()
\r
1093 // InitEngineUCI( installDir, &second );
\r
1095 /* Create a main window for this application instance. */
\r
1096 hwnd = CreateWindow(szAppName, szTitle,
\r
1097 (WS_OVERLAPPEDWINDOW & ~WS_MAXIMIZEBOX),
\r
1098 CW_USEDEFAULT, 0, CW_USEDEFAULT, 0,
\r
1099 NULL, NULL, hInstance, NULL);
\r
1102 /* If window could not be created, return "failure" */
\r
1107 /* [HGM] logo: Load logos if specified (must be done before InitDrawingSizes) */
\r
1108 LoadLogo(&first, 0, FALSE);
\r
1109 LoadLogo(&second, 1, appData.icsActive);
\r
1113 iconWhite = LoadIcon(hInstance, "icon_white");
\r
1114 iconBlack = LoadIcon(hInstance, "icon_black");
\r
1115 iconCurrent = iconWhite;
\r
1116 InitDrawingColors();
\r
1117 screenHeight = GetSystemMetrics(SM_CYSCREEN);
\r
1118 screenWidth = GetSystemMetrics(SM_CXSCREEN);
\r
1119 for (ibs = (int) NUM_SIZES - 1; ibs >= 0; ibs--) {
\r
1120 /* Compute window size for each board size, and use the largest
\r
1121 size that fits on this screen as the default. */
\r
1122 InitDrawingSizes((BoardSize)(ibs+1000), 0);
\r
1123 if (boardSize == (BoardSize)-1 &&
\r
1124 winH <= screenHeight
\r
1125 - GetSystemMetrics(SM_CYFRAME) - GetSystemMetrics(SM_CYCAPTION) - 10
\r
1126 && winW <= screenWidth) {
\r
1127 boardSize = (BoardSize)ibs;
\r
1131 InitDrawingSizes(boardSize, 0);
\r
1132 RecentEngineMenu(appData.recentEngineList);
\r
1134 buttonCount = GetSystemMetrics(SM_CMOUSEBUTTONS);
\r
1136 /* [AS] Load textures if specified */
\r
1139 mysrandom( (unsigned) time(NULL) );
\r
1141 /* [AS] Restore layout */
\r
1142 if( wpMoveHistory.visible ) {
\r
1143 MoveHistoryPopUp();
\r
1146 if( wpEvalGraph.visible ) {
\r
1150 if( wpEngineOutput.visible ) {
\r
1151 EngineOutputPopUp();
\r
1154 /* Make the window visible; update its client area; and return "success" */
\r
1155 EnsureOnScreen(&wpMain.x, &wpMain.y, minX, minY);
\r
1156 wp.length = sizeof(WINDOWPLACEMENT);
\r
1158 wp.showCmd = nCmdShow;
\r
1159 wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;
\r
1160 wp.rcNormalPosition.left = wpMain.x;
\r
1161 wp.rcNormalPosition.right = wpMain.x + wpMain.width;
\r
1162 wp.rcNormalPosition.top = wpMain.y;
\r
1163 wp.rcNormalPosition.bottom = wpMain.y + wpMain.height;
\r
1164 SetWindowPlacement(hwndMain, &wp);
\r
1166 InitBackEnd2(); // [HGM] moved until after all windows placed, to save correct position if fatal error on engine start
\r
1168 if(!appData.noGUI) SetWindowPos(hwndMain, alwaysOnTop ? HWND_TOPMOST : HWND_NOTOPMOST,
\r
1169 0, 0, 0, 0, SWP_NOMOVE|SWP_NOSIZE);
\r
1171 if (hwndConsole) {
\r
1173 SetWindowPos(hwndConsole, alwaysOnTop ? HWND_TOPMOST : HWND_NOTOPMOST,
\r
1174 0, 0, 0, 0, SWP_NOMOVE|SWP_NOSIZE);
\r
1176 ShowWindow(hwndConsole, nCmdShow);
\r
1177 SetActiveWindow(hwndConsole);
\r
1179 if(!appData.noGUI) UpdateWindow(hwnd); else ShowWindow(hwnd, SW_MINIMIZE);
\r
1180 if(gameListDialog) SetFocus(gameListDialog); // [HGM] jaws: for if we clicked multi-game game file
\r
1189 HMENU hmenu = GetMenu(hwndMain);
\r
1191 (void) EnableMenuItem(hmenu, IDM_CommPort,
\r
1192 MF_BYCOMMAND|((appData.icsActive &&
\r
1193 *appData.icsCommPort != NULLCHAR) ?
\r
1194 MF_ENABLED : MF_GRAYED));
\r
1195 (void) CheckMenuItem(hmenu, IDM_SaveSettingsOnExit,
\r
1196 MF_BYCOMMAND|(saveSettingsOnExit ?
\r
1197 MF_CHECKED : MF_UNCHECKED));
\r
1200 //---------------------------------------------------------------------------------------------------------
\r
1202 #define ICS_TEXT_MENU_SIZE (IDM_CommandXLast - IDM_CommandX + 1)
\r
1203 #define XBOARD FALSE
\r
1205 #define OPTCHAR "/"
\r
1206 #define SEPCHAR "="
\r
1207 #define TOPLEVEL 0
\r
1211 // front-end part of option handling
\r
1214 LFfromMFP(LOGFONT* lf, MyFontParams *mfp)
\r
1216 HDC hdc = CreateDC("DISPLAY", NULL, NULL, NULL);
\r
1217 lf->lfHeight = -(int)(mfp->pointSize * GetDeviceCaps(hdc, LOGPIXELSY) / 72.0 + 0.5);
\r
1220 lf->lfEscapement = 0;
\r
1221 lf->lfOrientation = 0;
\r
1222 lf->lfWeight = mfp->bold ? FW_BOLD : FW_NORMAL;
\r
1223 lf->lfItalic = mfp->italic;
\r
1224 lf->lfUnderline = mfp->underline;
\r
1225 lf->lfStrikeOut = mfp->strikeout;
\r
1226 lf->lfCharSet = mfp->charset;
\r
1227 lf->lfOutPrecision = OUT_DEFAULT_PRECIS;
\r
1228 lf->lfClipPrecision = CLIP_DEFAULT_PRECIS;
\r
1229 lf->lfQuality = DEFAULT_QUALITY;
\r
1230 lf->lfPitchAndFamily = DEFAULT_PITCH|FF_DONTCARE;
\r
1231 safeStrCpy(lf->lfFaceName, mfp->faceName, sizeof(lf->lfFaceName)/sizeof(lf->lfFaceName[0]) );
\r
1235 CreateFontInMF(MyFont *mf)
\r
1237 LFfromMFP(&mf->lf, &mf->mfp);
\r
1238 if (mf->hf) DeleteObject(mf->hf);
\r
1239 mf->hf = CreateFontIndirect(&mf->lf);
\r
1242 // [HGM] This platform-dependent table provides the location for storing the color info
\r
1244 colorVariable[] = {
\r
1245 &whitePieceColor,
\r
1246 &blackPieceColor,
\r
1247 &lightSquareColor,
\r
1248 &darkSquareColor,
\r
1249 &highlightSquareColor,
\r
1250 &premoveHighlightColor,
\r
1252 &consoleBackgroundColor,
\r
1253 &appData.fontForeColorWhite,
\r
1254 &appData.fontBackColorWhite,
\r
1255 &appData.fontForeColorBlack,
\r
1256 &appData.fontBackColorBlack,
\r
1257 &appData.evalHistColorWhite,
\r
1258 &appData.evalHistColorBlack,
\r
1259 &appData.highlightArrowColor,
\r
1262 /* Command line font name parser. NULL name means do nothing.
\r
1263 Syntax like "Courier New:10.0 bi" or "Arial:10" or "Arial:10b"
\r
1264 For backward compatibility, syntax without the colon is also
\r
1265 accepted, but font names with digits in them won't work in that case.
\r
1268 ParseFontName(char *name, MyFontParams *mfp)
\r
1271 if (name == NULL) return;
\r
1273 q = strchr(p, ':');
\r
1275 if (q - p >= sizeof(mfp->faceName))
\r
1276 ExitArgError(_("Font name too long:"), name, TRUE);
\r
1277 memcpy(mfp->faceName, p, q - p);
\r
1278 mfp->faceName[q - p] = NULLCHAR;
\r
1281 q = mfp->faceName;
\r
1282 while (*p && !isdigit(*p)) {
\r
1284 if (q - mfp->faceName >= sizeof(mfp->faceName))
\r
1285 ExitArgError(_("Font name too long:"), name, TRUE);
\r
1287 while (q > mfp->faceName && q[-1] == ' ') q--;
\r
1290 if (!*p) ExitArgError(_("Font point size missing:"), name, TRUE);
\r
1291 mfp->pointSize = (float) atof(p);
\r
1292 mfp->bold = (strchr(p, 'b') != NULL);
\r
1293 mfp->italic = (strchr(p, 'i') != NULL);
\r
1294 mfp->underline = (strchr(p, 'u') != NULL);
\r
1295 mfp->strikeout = (strchr(p, 's') != NULL);
\r
1296 mfp->charset = DEFAULT_CHARSET;
\r
1297 q = strchr(p, 'c');
\r
1299 mfp->charset = (BYTE) atoi(q+1);
\r
1303 ParseFont(char *name, int number)
\r
1304 { // wrapper to shield back-end from 'font'
\r
1305 ParseFontName(name, &font[boardSize][number]->mfp);
\r
1310 { // in WB we have a 2D array of fonts; this initializes their description
\r
1312 /* Point font array elements to structures and
\r
1313 parse default font names */
\r
1314 for (i=0; i<NUM_FONTS; i++) {
\r
1315 for (j=0; j<NUM_SIZES; j++) {
\r
1316 font[j][i] = &fontRec[j][i];
\r
1317 ParseFontName(font[j][i]->def, &font[j][i]->mfp);
\r
1324 { // here we create the actual fonts from the selected descriptions
\r
1326 for (i=0; i<NUM_FONTS; i++) {
\r
1327 for (j=0; j<NUM_SIZES; j++) {
\r
1328 CreateFontInMF(font[j][i]);
\r
1332 /* Color name parser.
\r
1333 X version accepts X color names, but this one
\r
1334 handles only the #rrggbb form (hex) or rrr,ggg,bbb (decimal) */
\r
1336 ParseColorName(char *name)
\r
1338 int red, green, blue, count;
\r
1339 char buf[MSG_SIZ];
\r
1341 count = sscanf(name, "#%2x%2x%2x", &red, &green, &blue);
\r
1343 count = sscanf(name, "%3d%*[^0-9]%3d%*[^0-9]%3d",
\r
1344 &red, &green, &blue);
\r
1347 snprintf(buf, MSG_SIZ, _("Can't parse color name %s"), name);
\r
1348 DisplayError(buf, 0);
\r
1349 return RGB(0, 0, 0);
\r
1351 return PALETTERGB(red, green, blue);
\r
1355 ParseColor(int n, char *name)
\r
1356 { // for WinBoard the color is an int, which needs to be derived from the string
\r
1357 if(colorVariable[n]) *(int*)colorVariable[n] = ParseColorName(name);
\r
1361 ParseAttribs(COLORREF *color, int *effects, char* argValue)
\r
1363 char *e = argValue;
\r
1367 if (*e == 'b') eff |= CFE_BOLD;
\r
1368 else if (*e == 'i') eff |= CFE_ITALIC;
\r
1369 else if (*e == 'u') eff |= CFE_UNDERLINE;
\r
1370 else if (*e == 's') eff |= CFE_STRIKEOUT;
\r
1371 else if (*e == '#' || isdigit(*e)) break;
\r
1375 *color = ParseColorName(e);
\r
1379 ParseTextAttribs(ColorClass cc, char *s)
\r
1380 { // [HGM] front-end wrapper that does the platform-dependent call
\r
1381 // for XBoard we would set (&appData.colorShout)[cc] = strdup(s);
\r
1382 ParseAttribs(&textAttribs[cc].color, &textAttribs[cc].effects, s);
\r
1386 ParseBoardSize(void *addr, char *name)
\r
1387 { // [HGM] rewritten with return-value ptr to shield back-end from BoardSize
\r
1388 BoardSize bs = SizeTiny;
\r
1389 while (sizeInfo[bs].name != NULL) {
\r
1390 if (StrCaseCmp(name, sizeInfo[bs].name) == 0) {
\r
1391 *(BoardSize *)addr = bs;
\r
1396 ExitArgError(_("Unrecognized board size value"), name, TRUE);
\r
1401 { // [HGM] import name from appData first
\r
1404 for (cc = (ColorClass)0; cc < ColorNormal; cc++) {
\r
1405 textAttribs[cc].sound.name = strdup((&appData.soundShout)[cc]);
\r
1406 textAttribs[cc].sound.data = NULL;
\r
1407 MyLoadSound(&textAttribs[cc].sound);
\r
1409 for (cc = ColorNormal; cc < NColorClasses; cc++) {
\r
1410 textAttribs[cc].sound.name = strdup("");
\r
1411 textAttribs[cc].sound.data = NULL;
\r
1413 for (sc = (SoundClass)0; sc < NSoundClasses; sc++) {
\r
1414 sounds[sc].name = strdup((&appData.soundMove)[sc]);
\r
1415 sounds[sc].data = NULL;
\r
1416 MyLoadSound(&sounds[sc]);
\r
1421 SetCommPortDefaults()
\r
1423 memset(&dcb, 0, sizeof(DCB)); // required by VS 2002 +
\r
1424 dcb.DCBlength = sizeof(DCB);
\r
1425 dcb.BaudRate = 9600;
\r
1426 dcb.fBinary = TRUE;
\r
1427 dcb.fParity = FALSE;
\r
1428 dcb.fOutxCtsFlow = FALSE;
\r
1429 dcb.fOutxDsrFlow = FALSE;
\r
1430 dcb.fDtrControl = DTR_CONTROL_ENABLE;
\r
1431 dcb.fDsrSensitivity = FALSE;
\r
1432 dcb.fTXContinueOnXoff = TRUE;
\r
1433 dcb.fOutX = FALSE;
\r
1435 dcb.fNull = FALSE;
\r
1436 dcb.fRtsControl = RTS_CONTROL_ENABLE;
\r
1437 dcb.fAbortOnError = FALSE;
\r
1439 dcb.Parity = SPACEPARITY;
\r
1440 dcb.StopBits = ONESTOPBIT;
\r
1443 // [HGM] args: these three cases taken out to stay in front-end
\r
1445 SaveFontArg(FILE *f, ArgDescriptor *ad)
\r
1446 { // in WinBoard every board size has its own font, and the "argLoc" identifies the table,
\r
1447 // while the curent board size determines the element. This system should be ported to XBoard.
\r
1448 // What the table contains pointers to, and how to print the font description, remains platform-dependent
\r
1450 for (bs=0; bs<NUM_SIZES; bs++) {
\r
1451 MyFontParams *mfp = &font[bs][(int) ad->argLoc]->mfp;
\r
1452 fprintf(f, "/size=%s ", sizeInfo[bs].name);
\r
1453 fprintf(f, "/%s=\"%s:%g%s%s%s%s%sc%d\"\n",
\r
1454 ad->argName, mfp->faceName, mfp->pointSize,
\r
1455 mfp->bold || mfp->italic || mfp->underline || mfp->strikeout ? " " : "",
\r
1456 mfp->bold ? "b" : "",
\r
1457 mfp->italic ? "i" : "",
\r
1458 mfp->underline ? "u" : "",
\r
1459 mfp->strikeout ? "s" : "",
\r
1460 (int)mfp->charset);
\r
1466 { // [HGM] copy the names from the internal WB variables to appData
\r
1469 for (cc = (ColorClass)0; cc < ColorNormal; cc++)
\r
1470 (&appData.soundShout)[cc] = textAttribs[cc].sound.name;
\r
1471 for (sc = (SoundClass)0; sc < NSoundClasses; sc++)
\r
1472 (&appData.soundMove)[sc] = sounds[sc].name;
\r
1476 SaveAttribsArg(FILE *f, ArgDescriptor *ad)
\r
1477 { // here the "argLoc" defines a table index. It could have contained the 'ta' pointer itself, though
\r
1478 MyTextAttribs* ta = &textAttribs[(ColorClass)ad->argLoc];
\r
1479 fprintf(f, "/%s=\"%s%s%s%s%s#%02lx%02lx%02lx\"\n", ad->argName,
\r
1480 (ta->effects & CFE_BOLD) ? "b" : "",
\r
1481 (ta->effects & CFE_ITALIC) ? "i" : "",
\r
1482 (ta->effects & CFE_UNDERLINE) ? "u" : "",
\r
1483 (ta->effects & CFE_STRIKEOUT) ? "s" : "",
\r
1484 (ta->effects) ? " " : "",
\r
1485 ta->color&0xff, (ta->color >> 8)&0xff, (ta->color >> 16)&0xff);
\r
1489 SaveColor(FILE *f, ArgDescriptor *ad)
\r
1490 { // in WinBoard the color is an int and has to be converted to text. In X it would be a string already?
\r
1491 COLORREF color = *(COLORREF *)colorVariable[(int)ad->argLoc];
\r
1492 fprintf(f, "/%s=#%02lx%02lx%02lx\n", ad->argName,
\r
1493 color&0xff, (color>>8)&0xff, (color>>16)&0xff);
\r
1497 SaveBoardSize(FILE *f, char *name, void *addr)
\r
1498 { // wrapper to shield back-end from BoardSize & sizeInfo
\r
1499 fprintf(f, "/%s=%s\n", name, sizeInfo[*(BoardSize *)addr].name);
\r
1503 ParseCommPortSettings(char *s)
\r
1504 { // wrapper to keep dcb from back-end
\r
1505 ParseCommSettings(s, &dcb);
\r
1510 { // wrapper to shield use of window handles from back-end (make addressible by number?)
\r
1511 GetActualPlacement(hwndMain, &wpMain);
\r
1512 GetActualPlacement(hwndConsole, &wpConsole);
\r
1513 GetActualPlacement(commentDialog, &wpComment);
\r
1514 GetActualPlacement(editTagsDialog, &wpTags);
\r
1515 GetActualPlacement(gameListDialog, &wpGameList);
\r
1516 GetActualPlacement(moveHistoryDialog, &wpMoveHistory);
\r
1517 GetActualPlacement(evalGraphDialog, &wpEvalGraph);
\r
1518 GetActualPlacement(engineOutputDialog, &wpEngineOutput);
\r
1522 PrintCommPortSettings(FILE *f, char *name)
\r
1523 { // wrapper to shield back-end from DCB
\r
1524 PrintCommSettings(f, name, &dcb);
\r
1528 MySearchPath(char *installDir, char *name, char *fullname)
\r
1530 char *dummy, buf[MSG_SIZ], *p = name, *q;
\r
1531 if(name[0]== '%') {
\r
1532 fullname[0] = 0; // [HGM] first expand any environment variables in the given name
\r
1533 while(*p == '%' && (q = strchr(p+1, '%'))) { // [HGM] recognize %*% as environment variable
\r
1534 safeStrCpy(buf, p+1, sizeof(buf)/sizeof(buf[0]) );
\r
1535 *strchr(buf, '%') = 0;
\r
1536 strcat(fullname, getenv(buf));
\r
1537 p = q+1; while(*p == '\\') { strcat(fullname, "\\"); p++; }
\r
1539 strcat(fullname, p); // after environment variables (if any), take the remainder of the given name
\r
1540 if(appData.debugMode) fprintf(debugFP, "name = '%s', expanded name = '%s'\n", name, fullname);
\r
1541 return (int) strlen(fullname);
\r
1543 return (int) SearchPath(installDir, name, NULL, MSG_SIZ, fullname, &dummy);
\r
1547 MyGetFullPathName(char *name, char *fullname)
\r
1550 return (int) GetFullPathName(name, MSG_SIZ, fullname, &dummy);
\r
1555 { // [HGM] args: allows testing if main window is realized from back-end
\r
1556 return hwndMain != NULL;
\r
1560 PopUpStartupDialog()
\r
1564 LoadLanguageFile(appData.language);
\r
1565 lpProc = MakeProcInstance((FARPROC)StartupDialog, hInst);
\r
1566 DialogBox(hInst, MAKEINTRESOURCE(DLG_Startup), NULL, (DLGPROC)lpProc);
\r
1567 FreeProcInstance(lpProc);
\r
1570 /*---------------------------------------------------------------------------*\
\r
1572 * GDI board drawing routines
\r
1574 \*---------------------------------------------------------------------------*/
\r
1576 /* [AS] Draw square using background texture */
\r
1577 static void DrawTile( int dx, int dy, int dw, int dh, HDC dst, HDC src, int mode, int sx, int sy )
\r
1582 return; /* Should never happen! */
\r
1585 SetGraphicsMode( dst, GM_ADVANCED );
\r
1592 /* X reflection */
\r
1597 x.eDx = (FLOAT) dw + dx - 1;
\r
1600 SetWorldTransform( dst, &x );
\r
1603 /* Y reflection */
\r
1609 x.eDy = (FLOAT) dh + dy - 1;
\r
1611 SetWorldTransform( dst, &x );
\r
1619 x.eDx = (FLOAT) dx;
\r
1620 x.eDy = (FLOAT) dy;
\r
1623 SetWorldTransform( dst, &x );
\r
1627 BitBlt( dst, dx, dy, dw, dh, src, sx, sy, SRCCOPY );
\r
1635 SetWorldTransform( dst, &x );
\r
1637 ModifyWorldTransform( dst, 0, MWT_IDENTITY );
\r
1640 /* [AS] [HGM] Make room for more piece types, so all pieces can be different */
\r
1642 PM_WP = (int) WhitePawn,
\r
1643 PM_WN = (int) WhiteKnight,
\r
1644 PM_WB = (int) WhiteBishop,
\r
1645 PM_WR = (int) WhiteRook,
\r
1646 PM_WQ = (int) WhiteQueen,
\r
1647 PM_WF = (int) WhiteFerz,
\r
1648 PM_WW = (int) WhiteWazir,
\r
1649 PM_WE = (int) WhiteAlfil,
\r
1650 PM_WM = (int) WhiteMan,
\r
1651 PM_WO = (int) WhiteCannon,
\r
1652 PM_WU = (int) WhiteUnicorn,
\r
1653 PM_WH = (int) WhiteNightrider,
\r
1654 PM_WA = (int) WhiteAngel,
\r
1655 PM_WC = (int) WhiteMarshall,
\r
1656 PM_WAB = (int) WhiteCardinal,
\r
1657 PM_WD = (int) WhiteDragon,
\r
1658 PM_WL = (int) WhiteLance,
\r
1659 PM_WS = (int) WhiteCobra,
\r
1660 PM_WV = (int) WhiteFalcon,
\r
1661 PM_WSG = (int) WhiteSilver,
\r
1662 PM_WG = (int) WhiteGrasshopper,
\r
1663 PM_WK = (int) WhiteKing,
\r
1664 PM_BP = (int) BlackPawn,
\r
1665 PM_BN = (int) BlackKnight,
\r
1666 PM_BB = (int) BlackBishop,
\r
1667 PM_BR = (int) BlackRook,
\r
1668 PM_BQ = (int) BlackQueen,
\r
1669 PM_BF = (int) BlackFerz,
\r
1670 PM_BW = (int) BlackWazir,
\r
1671 PM_BE = (int) BlackAlfil,
\r
1672 PM_BM = (int) BlackMan,
\r
1673 PM_BO = (int) BlackCannon,
\r
1674 PM_BU = (int) BlackUnicorn,
\r
1675 PM_BH = (int) BlackNightrider,
\r
1676 PM_BA = (int) BlackAngel,
\r
1677 PM_BC = (int) BlackMarshall,
\r
1678 PM_BG = (int) BlackGrasshopper,
\r
1679 PM_BAB = (int) BlackCardinal,
\r
1680 PM_BD = (int) BlackDragon,
\r
1681 PM_BL = (int) BlackLance,
\r
1682 PM_BS = (int) BlackCobra,
\r
1683 PM_BV = (int) BlackFalcon,
\r
1684 PM_BSG = (int) BlackSilver,
\r
1685 PM_BK = (int) BlackKing
\r
1688 static HFONT hPieceFont = NULL;
\r
1689 static HBITMAP hPieceMask[(int) EmptySquare];
\r
1690 static HBITMAP hPieceFace[(int) EmptySquare];
\r
1691 static int fontBitmapSquareSize = 0;
\r
1692 static char pieceToFontChar[(int) EmptySquare] =
\r
1693 { 'p', 'n', 'b', 'r', 'q',
\r
1694 'n', 'b', 'p', 'n', 'b', 'r', 'b', 'r', 'q', 'k',
\r
1695 'k', 'o', 'm', 'v', 't', 'w',
\r
1696 'v', 't', 'o', 'm', 'v', 't', 'v', 't', 'w', 'l',
\r
1699 extern BOOL SetCharTable( char *table, const char * map );
\r
1700 /* [HGM] moved to backend.c */
\r
1702 static void SetPieceBackground( HDC hdc, COLORREF color, int mode )
\r
1705 BYTE r1 = GetRValue( color );
\r
1706 BYTE g1 = GetGValue( color );
\r
1707 BYTE b1 = GetBValue( color );
\r
1713 /* Create a uniform background first */
\r
1714 hbrush = CreateSolidBrush( color );
\r
1715 SetRect( &rc, 0, 0, squareSize, squareSize );
\r
1716 FillRect( hdc, &rc, hbrush );
\r
1717 DeleteObject( hbrush );
\r
1720 /* Vertical gradient, good for pawn, knight and rook, less for queen and king */
\r
1721 int steps = squareSize / 2;
\r
1724 for( i=0; i<steps; i++ ) {
\r
1725 BYTE r = r1 - (r1-r2) * i / steps;
\r
1726 BYTE g = g1 - (g1-g2) * i / steps;
\r
1727 BYTE b = b1 - (b1-b2) * i / steps;
\r
1729 hbrush = CreateSolidBrush( RGB(r,g,b) );
\r
1730 SetRect( &rc, i + squareSize - steps, 0, i + squareSize - steps + 1, squareSize );
\r
1731 FillRect( hdc, &rc, hbrush );
\r
1732 DeleteObject(hbrush);
\r
1735 else if( mode == 2 ) {
\r
1736 /* Diagonal gradient, good more or less for every piece */
\r
1737 POINT triangle[3];
\r
1738 HPEN hpen = SelectObject( hdc, GetStockObject(NULL_PEN) );
\r
1739 HBRUSH hbrush_old;
\r
1740 int steps = squareSize;
\r
1743 triangle[0].x = squareSize - steps;
\r
1744 triangle[0].y = squareSize;
\r
1745 triangle[1].x = squareSize;
\r
1746 triangle[1].y = squareSize;
\r
1747 triangle[2].x = squareSize;
\r
1748 triangle[2].y = squareSize - steps;
\r
1750 for( i=0; i<steps; i++ ) {
\r
1751 BYTE r = r1 - (r1-r2) * i / steps;
\r
1752 BYTE g = g1 - (g1-g2) * i / steps;
\r
1753 BYTE b = b1 - (b1-b2) * i / steps;
\r
1755 hbrush = CreateSolidBrush( RGB(r,g,b) );
\r
1756 hbrush_old = SelectObject( hdc, hbrush );
\r
1757 Polygon( hdc, triangle, 3 );
\r
1758 SelectObject( hdc, hbrush_old );
\r
1759 DeleteObject(hbrush);
\r
1764 SelectObject( hdc, hpen );
\r
1769 [AS] The method I use to create the bitmaps it a bit tricky, but it
\r
1770 seems to work ok. The main problem here is to find the "inside" of a chess
\r
1771 piece: follow the steps as explained below.
\r
1773 static void CreatePieceMaskFromFont( HDC hdc_window, HDC hdc, int index )
\r
1777 COLORREF chroma = RGB(0xFF,0x00,0xFF);
\r
1781 int backColor = whitePieceColor;
\r
1782 int foreColor = blackPieceColor;
\r
1784 if( index < (int)BlackPawn && appData.fontBackColorWhite != appData.fontForeColorWhite ) {
\r
1785 backColor = appData.fontBackColorWhite;
\r
1786 foreColor = appData.fontForeColorWhite;
\r
1788 else if( index >= (int)BlackPawn && appData.fontBackColorBlack != appData.fontForeColorBlack ) {
\r
1789 backColor = appData.fontBackColorBlack;
\r
1790 foreColor = appData.fontForeColorBlack;
\r
1794 hbm = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );
\r
1796 hbm_old = SelectObject( hdc, hbm );
\r
1800 rc.right = squareSize;
\r
1801 rc.bottom = squareSize;
\r
1803 /* Step 1: background is now black */
\r
1804 FillRect( hdc, &rc, GetStockObject(BLACK_BRUSH) );
\r
1806 GetTextExtentPoint32( hdc, &pieceToFontChar[index], 1, &sz );
\r
1808 pt.x = (squareSize - sz.cx) / 2;
\r
1809 pt.y = (squareSize - sz.cy) / 2;
\r
1811 SetBkMode( hdc, TRANSPARENT );
\r
1812 SetTextColor( hdc, chroma );
\r
1813 /* Step 2: the piece has been drawn in purple, there are now black and purple in this bitmap */
\r
1814 TextOut( hdc, pt.x, pt.y, &pieceToFontChar[appData.allWhite && index >= (int)BlackPawn ? index - (int)BlackPawn : index], 1 );
\r
1816 SelectObject( hdc, GetStockObject(WHITE_BRUSH) );
\r
1817 /* Step 3: the area outside the piece is filled with white */
\r
1818 // FloodFill( hdc, 0, 0, chroma );
\r
1819 ExtFloodFill( hdc, 0, 0, 0, FLOODFILLSURFACE );
\r
1820 ExtFloodFill( hdc, 0, squareSize-1, 0, FLOODFILLSURFACE ); // [HGM] fill from all 4 corners, for if piece too big
\r
1821 ExtFloodFill( hdc, squareSize-1, 0, 0, FLOODFILLSURFACE );
\r
1822 ExtFloodFill( hdc, squareSize-1, squareSize-1, 0, FLOODFILLSURFACE );
\r
1823 SelectObject( hdc, GetStockObject(BLACK_BRUSH) );
\r
1825 Step 4: this is the tricky part, the area inside the piece is filled with black,
\r
1826 but if the start point is not inside the piece we're lost!
\r
1827 There should be a better way to do this... if we could create a region or path
\r
1828 from the fill operation we would be fine for example.
\r
1830 // FloodFill( hdc, squareSize / 2, squareSize / 2, RGB(0xFF,0xFF,0xFF) );
\r
1831 ExtFloodFill( hdc, squareSize / 2, squareSize / 2, RGB(0xFF,0xFF,0xFF), FLOODFILLBORDER );
\r
1833 { /* [HGM] shave off edges of mask, in an attempt to correct for the fact that FloodFill does not work correctly under Win XP */
\r
1834 HDC dc2 = CreateCompatibleDC( hdc_window );
\r
1835 HBITMAP bm2 = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );
\r
1837 SelectObject( dc2, bm2 );
\r
1838 BitBlt( dc2, 0, 0, squareSize, squareSize, hdc, 0, 0, SRCCOPY ); // make copy
\r
1839 BitBlt( hdc, 0, 1, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );
\r
1840 BitBlt( hdc, 2, 1, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );
\r
1841 BitBlt( hdc, 1, 0, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );
\r
1842 BitBlt( hdc, 1, 2, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );
\r
1845 DeleteObject( bm2 );
\r
1848 SetTextColor( hdc, 0 );
\r
1850 Step 5: some fonts have "disconnected" areas that are skipped by the fill:
\r
1851 draw the piece again in black for safety.
\r
1853 TextOut( hdc, pt.x, pt.y, &pieceToFontChar[appData.allWhite && index >= (int)BlackPawn ? index - (int)BlackPawn : index], 1 );
\r
1855 SelectObject( hdc, hbm_old );
\r
1857 if( hPieceMask[index] != NULL ) {
\r
1858 DeleteObject( hPieceMask[index] );
\r
1861 hPieceMask[index] = hbm;
\r
1864 hbm = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );
\r
1866 SelectObject( hdc, hbm );
\r
1869 HDC dc1 = CreateCompatibleDC( hdc_window );
\r
1870 HDC dc2 = CreateCompatibleDC( hdc_window );
\r
1871 HBITMAP bm2 = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );
\r
1873 SelectObject( dc1, hPieceMask[index] );
\r
1874 SelectObject( dc2, bm2 );
\r
1875 FillRect( dc2, &rc, GetStockObject(WHITE_BRUSH) );
\r
1876 BitBlt( dc2, 0, 0, squareSize, squareSize, dc1, 0, 0, SRCINVERT );
\r
1879 Now dc2 contains the inverse of the piece mask, i.e. a mask that preserves
\r
1880 the piece background and deletes (makes transparent) the rest.
\r
1881 Thanks to that mask, we are free to paint the background with the greates
\r
1882 freedom, as we'll be able to mask off the unwanted parts when finished.
\r
1883 We use this, to make gradients and give the pieces a "roundish" look.
\r
1885 SetPieceBackground( hdc, backColor, 2 );
\r
1886 BitBlt( hdc, 0, 0, squareSize, squareSize, dc2, 0, 0, SRCAND );
\r
1890 DeleteObject( bm2 );
\r
1893 SetTextColor( hdc, foreColor );
\r
1894 TextOut( hdc, pt.x, pt.y, &pieceToFontChar[appData.allWhite && index >= (int)BlackPawn ? index - (int)BlackPawn : index], 1 );
\r
1896 SelectObject( hdc, hbm_old );
\r
1898 if( hPieceFace[index] != NULL ) {
\r
1899 DeleteObject( hPieceFace[index] );
\r
1902 hPieceFace[index] = hbm;
\r
1905 static int TranslatePieceToFontPiece( int piece )
\r
1935 case BlackMarshall:
\r
1939 case BlackNightrider:
\r
1945 case BlackUnicorn:
\r
1949 case BlackGrasshopper:
\r
1961 case BlackCardinal:
\r
1968 case WhiteMarshall:
\r
1972 case WhiteNightrider:
\r
1978 case WhiteUnicorn:
\r
1982 case WhiteGrasshopper:
\r
1994 case WhiteCardinal:
\r
2003 void CreatePiecesFromFont()
\r
2006 HDC hdc_window = NULL;
\r
2012 if( fontBitmapSquareSize < 0 ) {
\r
2013 /* Something went seriously wrong in the past: do not try to recreate fonts! */
\r
2017 if( !appData.useFont || appData.renderPiecesWithFont == NULL ||
\r
2018 appData.renderPiecesWithFont[0] == NULLCHAR || appData.renderPiecesWithFont[0] == '*' ) {
\r
2019 fontBitmapSquareSize = -1;
\r
2023 if( fontBitmapSquareSize != squareSize ) {
\r
2024 hdc_window = GetDC( hwndMain );
\r
2025 hdc = CreateCompatibleDC( hdc_window );
\r
2027 if( hPieceFont != NULL ) {
\r
2028 DeleteObject( hPieceFont );
\r
2031 for( i=0; i<=(int)BlackKing; i++ ) {
\r
2032 hPieceMask[i] = NULL;
\r
2033 hPieceFace[i] = NULL;
\r
2039 if( appData.fontPieceSize >= 50 && appData.fontPieceSize <= 150 ) {
\r
2040 fontHeight = appData.fontPieceSize;
\r
2043 fontHeight = (fontHeight * squareSize) / 100;
\r
2045 lf.lfHeight = -MulDiv( fontHeight, GetDeviceCaps(hdc, LOGPIXELSY), 72 );
\r
2047 lf.lfEscapement = 0;
\r
2048 lf.lfOrientation = 0;
\r
2049 lf.lfWeight = FW_NORMAL;
\r
2051 lf.lfUnderline = 0;
\r
2052 lf.lfStrikeOut = 0;
\r
2053 lf.lfCharSet = DEFAULT_CHARSET;
\r
2054 lf.lfOutPrecision = OUT_DEFAULT_PRECIS;
\r
2055 lf.lfClipPrecision = CLIP_DEFAULT_PRECIS;
\r
2056 lf.lfQuality = PROOF_QUALITY;
\r
2057 lf.lfPitchAndFamily = DEFAULT_PITCH | FF_DONTCARE;
\r
2058 strncpy( lf.lfFaceName, appData.renderPiecesWithFont, sizeof(lf.lfFaceName) );
\r
2059 lf.lfFaceName[ sizeof(lf.lfFaceName) - 1 ] = '\0';
\r
2061 hPieceFont = CreateFontIndirect( &lf );
\r
2063 if( hPieceFont == NULL ) {
\r
2064 fontBitmapSquareSize = -2;
\r
2067 /* Setup font-to-piece character table */
\r
2068 if( ! SetCharTable(pieceToFontChar, appData.fontToPieceTable) ) {
\r
2069 /* No (or wrong) global settings, try to detect the font */
\r
2070 if( strstr(lf.lfFaceName,"Alpha") != NULL ) {
\r
2072 SetCharTable(pieceToFontChar, "phbrqkojntwl");
\r
2074 else if( strstr(lf.lfFaceName,"DiagramTT") != NULL ) {
\r
2075 /* DiagramTT* family */
\r
2076 SetCharTable(pieceToFontChar, "PNLRQKpnlrqk");
\r
2078 else if( strstr(lf.lfFaceName,"WinboardF") != NULL ) {
\r
2079 /* Fairy symbols */
\r
2080 SetCharTable(pieceToFontChar, "PNBRQFEACWMOHIJGDVSLUKpnbrqfeacwmohijgdvsluk");
\r
2082 else if( strstr(lf.lfFaceName,"GC2004D") != NULL ) {
\r
2083 /* Good Companion (Some characters get warped as literal :-( */
\r
2084 char s[] = "1cmWG0??S??oYI23wgQU";
\r
2085 s[0]=0xB9; s[1]=0xA9; s[6]=0xB1; s[11]=0xBB; s[12]=0xAB; s[17]=0xB3;
\r
2086 SetCharTable(pieceToFontChar, s);
\r
2089 /* Cases, Condal, Leipzig, Lucena, Marroquin, Merida, Usual */
\r
2090 SetCharTable(pieceToFontChar, "pnbrqkomvtwl");
\r
2094 /* Create bitmaps */
\r
2095 hfont_old = SelectObject( hdc, hPieceFont );
\r
2096 for(i=(int)WhitePawn; i<(int)EmptySquare; i++) /* [HGM] made a loop for this */
\r
2097 if(PieceToChar((ChessSquare)i) != '.') /* skip unused pieces */
\r
2098 CreatePieceMaskFromFont( hdc_window, hdc, i );
\r
2100 SelectObject( hdc, hfont_old );
\r
2102 fontBitmapSquareSize = squareSize;
\r
2106 if( hdc != NULL ) {
\r
2110 if( hdc_window != NULL ) {
\r
2111 ReleaseDC( hwndMain, hdc_window );
\r
2116 DoLoadBitmap(HINSTANCE hinst, char *piece, int squareSize, char *suffix)
\r
2118 char name[128], buf[MSG_SIZ];
\r
2120 snprintf(name, sizeof(name)/sizeof(name[0]), "%s%d%s", piece, squareSize, suffix);
\r
2121 if(appData.pieceDirectory[0]) {
\r
2123 snprintf(buf, MSG_SIZ, "%s\\%s.bmp", appData.pieceDirectory, name);
\r
2124 res = LoadImage( 0, buf, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );
\r
2125 if(res) return res;
\r
2127 if (gameInfo.event &&
\r
2128 strcmp(gameInfo.event, "Easter Egg Hunt") == 0 &&
\r
2129 strcmp(name, "k80s") == 0) {
\r
2130 safeStrCpy(name, "tim", sizeof(name)/sizeof(name[0]) );
\r
2132 return LoadBitmap(hinst, name);
\r
2136 /* Insert a color into the program's logical palette
\r
2137 structure. This code assumes the given color is
\r
2138 the result of the RGB or PALETTERGB macro, and it
\r
2139 knows how those macros work (which is documented).
\r
2142 InsertInPalette(COLORREF color)
\r
2144 LPPALETTEENTRY pe = &(pLogPal->palPalEntry[pLogPal->palNumEntries]);
\r
2146 if (pLogPal->palNumEntries++ >= PALETTESIZE) {
\r
2147 DisplayFatalError(_("Too many colors"), 0, 1);
\r
2148 pLogPal->palNumEntries--;
\r
2152 pe->peFlags = (char) 0;
\r
2153 pe->peRed = (char) (0xFF & color);
\r
2154 pe->peGreen = (char) (0xFF & (color >> 8));
\r
2155 pe->peBlue = (char) (0xFF & (color >> 16));
\r
2161 InitDrawingColors()
\r
2163 if (pLogPal == NULL) {
\r
2164 /* Allocate enough memory for a logical palette with
\r
2165 * PALETTESIZE entries and set the size and version fields
\r
2166 * of the logical palette structure.
\r
2168 pLogPal = (NPLOGPALETTE)
\r
2169 LocalAlloc(LMEM_FIXED, (sizeof(LOGPALETTE) +
\r
2170 (sizeof(PALETTEENTRY) * (PALETTESIZE))));
\r
2171 pLogPal->palVersion = 0x300;
\r
2173 pLogPal->palNumEntries = 0;
\r
2175 InsertInPalette(lightSquareColor);
\r
2176 InsertInPalette(darkSquareColor);
\r
2177 InsertInPalette(whitePieceColor);
\r
2178 InsertInPalette(blackPieceColor);
\r
2179 InsertInPalette(highlightSquareColor);
\r
2180 InsertInPalette(premoveHighlightColor);
\r
2182 /* create a logical color palette according the information
\r
2183 * in the LOGPALETTE structure.
\r
2185 hPal = CreatePalette((LPLOGPALETTE) pLogPal);
\r
2187 lightSquareBrush = CreateSolidBrush(lightSquareColor);
\r
2188 blackSquareBrush = CreateSolidBrush(blackPieceColor);
\r
2189 darkSquareBrush = CreateSolidBrush(darkSquareColor);
\r
2190 whitePieceBrush = CreateSolidBrush(whitePieceColor);
\r
2191 blackPieceBrush = CreateSolidBrush(blackPieceColor);
\r
2192 iconBkgndBrush = CreateSolidBrush(GetSysColor(COLOR_BACKGROUND));
\r
2193 explodeBrush = CreateSolidBrush(highlightSquareColor); // [HGM] atomic
\r
2194 markerBrush = CreateSolidBrush(premoveHighlightColor); // [HGM] markers
\r
2195 /* [AS] Force rendering of the font-based pieces */
\r
2196 if( fontBitmapSquareSize > 0 ) {
\r
2197 fontBitmapSquareSize = 0;
\r
2203 BoardWidth(int boardSize, int n)
\r
2204 { /* [HGM] argument n added to allow different width and height */
\r
2205 int lineGap = sizeInfo[boardSize].lineGap;
\r
2207 if( appData.overrideLineGap >= 0 && appData.overrideLineGap <= 5 ) {
\r
2208 lineGap = appData.overrideLineGap;
\r
2211 return (n + 1) * lineGap +
\r
2212 n * sizeInfo[boardSize].squareSize;
\r
2215 /* Respond to board resize by dragging edge */
\r
2217 ResizeBoard(int newSizeX, int newSizeY, int flags)
\r
2219 BoardSize newSize = NUM_SIZES - 1;
\r
2220 static int recurse = 0;
\r
2221 if (IsIconic(hwndMain)) return;
\r
2222 if (recurse > 0) return;
\r
2224 while (newSize > 0) {
\r
2225 InitDrawingSizes(newSize+1000, 0); // [HGM] kludge to update sizeInfo without visible effects
\r
2226 if(newSizeX >= sizeInfo[newSize].cliWidth &&
\r
2227 newSizeY >= sizeInfo[newSize].cliHeight) break;
\r
2230 boardSize = newSize;
\r
2231 InitDrawingSizes(boardSize, flags);
\r
2236 extern Boolean twoBoards, partnerUp; // [HGM] dual
\r
2239 InitDrawingSizes(BoardSize boardSize, int flags)
\r
2241 int i, boardWidth, boardHeight; /* [HGM] height treated separately */
\r
2242 ChessSquare piece;
\r
2243 static int oldBoardSize = -1, oldTinyLayout = 0;
\r
2245 SIZE clockSize, messageSize;
\r
2247 char buf[MSG_SIZ];
\r
2249 HMENU hmenu = GetMenu(hwndMain);
\r
2250 RECT crect, wrect, oldRect;
\r
2252 LOGBRUSH logbrush;
\r
2253 VariantClass v = gameInfo.variant;
\r
2255 int suppressVisibleEffects = 0; // [HGM] kludge to request updating sizeInfo only
\r
2256 if((int)boardSize >= 1000 ) { boardSize -= 1000; suppressVisibleEffects = 1; }
\r
2258 /* [HGM] call with -2 uses old size (for if nr of files, ranks changes) */
\r
2259 if(boardSize == (BoardSize)(-2) ) boardSize = oldBoardSize;
\r
2260 oldBoardSize = boardSize;
\r
2262 if(boardSize != SizeMiddling && boardSize != SizePetite && boardSize != SizeBulky && !appData.useFont)
\r
2263 { // correct board size to one where built-in pieces exist
\r
2264 if((v == VariantCapablanca || v == VariantGothic || v == VariantGrand || v == VariantCapaRandom || v == VariantJanus || v == VariantSuper)
\r
2265 && (boardSize < SizePetite || boardSize > SizeBulky) // Archbishop and Chancellor available in entire middle range
\r
2266 || (v == VariantShogi && boardSize != SizeModerate) // Japanese-style Shogi
\r
2267 || v == VariantKnightmate || v == VariantSChess || v == VariantXiangqi || v == VariantSpartan
\r
2268 || v == VariantShatranj || v == VariantMakruk || v == VariantGreat || v == VariantFairy ) {
\r
2269 if(boardSize < SizeMediocre) boardSize = SizePetite; else
\r
2270 if(boardSize > SizeModerate) boardSize = SizeBulky; else
\r
2271 boardSize = SizeMiddling;
\r
2274 if(!appData.useFont && boardSize == SizePetite && (v == VariantShogi || v == VariantKnightmate)) boardSize = SizeMiddling; // no Unicorn in Petite
\r
2276 oldRect.left = wpMain.x; //[HGM] placement: remember previous window params
\r
2277 oldRect.top = wpMain.y;
\r
2278 oldRect.right = wpMain.x + wpMain.width;
\r
2279 oldRect.bottom = wpMain.y + wpMain.height;
\r
2281 tinyLayout = sizeInfo[boardSize].tinyLayout;
\r
2282 smallLayout = sizeInfo[boardSize].smallLayout;
\r
2283 squareSize = sizeInfo[boardSize].squareSize;
\r
2284 lineGap = sizeInfo[boardSize].lineGap;
\r
2285 minorSize = 0; /* [HGM] Kludge to see if demagnified pieces need to be shifted */
\r
2286 border = appData.useBorder && appData.border[0] ? squareSize/2 : 0;
\r
2288 if( appData.overrideLineGap >= 0 && appData.overrideLineGap <= 5 ) {
\r
2289 lineGap = appData.overrideLineGap;
\r
2292 if (tinyLayout != oldTinyLayout) {
\r
2293 long style = GetWindowLongPtr(hwndMain, GWL_STYLE);
\r
2295 style &= ~WS_SYSMENU;
\r
2296 InsertMenu(hmenu, IDM_Exit, MF_BYCOMMAND, IDM_Minimize,
\r
2297 "&Minimize\tCtrl+F4");
\r
2299 style |= WS_SYSMENU;
\r
2300 RemoveMenu(hmenu, IDM_Minimize, MF_BYCOMMAND);
\r
2302 SetWindowLongPtr(hwndMain, GWL_STYLE, style);
\r
2304 for (i=0; menuBarText[tinyLayout][i]; i++) {
\r
2305 ModifyMenu(hmenu, i, MF_STRING|MF_BYPOSITION|MF_POPUP,
\r
2306 (UINT)GetSubMenu(hmenu, i), T_(menuBarText[tinyLayout][i]));
\r
2308 DrawMenuBar(hwndMain);
\r
2311 boardWidth = BoardWidth(boardSize, BOARD_WIDTH) + 2*border;
\r
2312 boardHeight = BoardWidth(boardSize, BOARD_HEIGHT) + 2*border;
\r
2314 /* Get text area sizes */
\r
2315 hdc = GetDC(hwndMain);
\r
2316 if (appData.clockMode) {
\r
2317 snprintf(buf, MSG_SIZ, _("White: %s"), TimeString(23*60*60*1000L));
\r
2319 snprintf(buf, MSG_SIZ, _("White"));
\r
2321 oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);
\r
2322 GetTextExtentPoint(hdc, buf, strlen(buf), &clockSize);
\r
2323 SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);
\r
2324 str = _("We only care about the height here");
\r
2325 GetTextExtentPoint(hdc, str, strlen(str), &messageSize);
\r
2326 SelectObject(hdc, oldFont);
\r
2327 ReleaseDC(hwndMain, hdc);
\r
2329 /* Compute where everything goes */
\r
2330 if((first.programLogo || second.programLogo) && !tinyLayout) {
\r
2331 /* [HGM] logo: if either logo is on, reserve space for it */
\r
2332 logoHeight = 2*clockSize.cy;
\r
2333 leftLogoRect.left = OUTER_MARGIN;
\r
2334 leftLogoRect.right = leftLogoRect.left + 4*clockSize.cy;
\r
2335 leftLogoRect.top = OUTER_MARGIN;
\r
2336 leftLogoRect.bottom = OUTER_MARGIN + logoHeight;
\r
2338 rightLogoRect.right = OUTER_MARGIN + boardWidth;
\r
2339 rightLogoRect.left = rightLogoRect.right - 4*clockSize.cy;
\r
2340 rightLogoRect.top = OUTER_MARGIN;
\r
2341 rightLogoRect.bottom = OUTER_MARGIN + logoHeight;
\r
2344 whiteRect.left = leftLogoRect.right;
\r
2345 whiteRect.right = OUTER_MARGIN + boardWidth/2 - INNER_MARGIN/2;
\r
2346 whiteRect.top = OUTER_MARGIN;
\r
2347 whiteRect.bottom = whiteRect.top + logoHeight;
\r
2349 blackRect.right = rightLogoRect.left;
\r
2350 blackRect.left = whiteRect.right + INNER_MARGIN;
\r
2351 blackRect.top = whiteRect.top;
\r
2352 blackRect.bottom = whiteRect.bottom;
\r
2354 whiteRect.left = OUTER_MARGIN;
\r
2355 whiteRect.right = whiteRect.left + boardWidth/2 - INNER_MARGIN/2;
\r
2356 whiteRect.top = OUTER_MARGIN;
\r
2357 whiteRect.bottom = whiteRect.top + clockSize.cy;
\r
2359 blackRect.left = whiteRect.right + INNER_MARGIN;
\r
2360 blackRect.right = blackRect.left + boardWidth/2 - 1;
\r
2361 blackRect.top = whiteRect.top;
\r
2362 blackRect.bottom = whiteRect.bottom;
\r
2364 logoHeight = 0; // [HGM] logo: suppress logo after change to tiny layout!
\r
2367 messageRect.left = OUTER_MARGIN + MESSAGE_LINE_LEFTMARGIN;
\r
2368 if (appData.showButtonBar) {
\r
2369 messageRect.right = OUTER_MARGIN + boardWidth // [HGM] logo: expressed independent of clock placement
\r
2370 - N_BUTTONS*BUTTON_WIDTH - MESSAGE_LINE_LEFTMARGIN;
\r
2372 messageRect.right = OUTER_MARGIN + boardWidth;
\r
2374 messageRect.top = whiteRect.bottom + INNER_MARGIN;
\r
2375 messageRect.bottom = messageRect.top + messageSize.cy;
\r
2377 boardRect.left = OUTER_MARGIN;
\r
2378 boardRect.right = boardRect.left + boardWidth;
\r
2379 boardRect.top = messageRect.bottom + INNER_MARGIN;
\r
2380 boardRect.bottom = boardRect.top + boardHeight;
\r
2382 sizeInfo[boardSize].cliWidth = boardRect.right + OUTER_MARGIN;
\r
2383 sizeInfo[boardSize].cliHeight = boardRect.bottom + OUTER_MARGIN;
\r
2384 oldTinyLayout = tinyLayout;
\r
2385 winW = 2 * GetSystemMetrics(SM_CXFRAME) + boardRect.right + OUTER_MARGIN;
\r
2386 winH = 2 * GetSystemMetrics(SM_CYFRAME) + GetSystemMetrics(SM_CYMENU) +
\r
2387 GetSystemMetrics(SM_CYCAPTION) + boardRect.bottom + OUTER_MARGIN;
\r
2388 winW *= 1 + twoBoards;
\r
2389 if(suppressVisibleEffects) return; // [HGM] when called for filling sizeInfo only
\r
2390 wpMain.width = winW; // [HGM] placement: set through temporary which can used by initial sizing choice
\r
2391 wpMain.height = winH; // without disturbing window attachments
\r
2392 GetWindowRect(hwndMain, &wrect);
\r
2393 SetWindowPos(hwndMain, NULL, 0, 0, wpMain.width, wpMain.height,
\r
2394 SWP_NOCOPYBITS|SWP_NOZORDER|SWP_NOMOVE);
\r
2396 // [HGM] placement: let attached windows follow size change.
\r
2397 ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, moveHistoryDialog, &wpMoveHistory );
\r
2398 ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, evalGraphDialog, &wpEvalGraph );
\r
2399 ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, engineOutputDialog, &wpEngineOutput );
\r
2400 ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, gameListDialog, &wpGameList );
\r
2401 ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, hwndConsole, &wpConsole );
\r
2403 /* compensate if menu bar wrapped */
\r
2404 GetClientRect(hwndMain, &crect);
\r
2405 offby = boardRect.bottom + OUTER_MARGIN - crect.bottom;
\r
2406 wpMain.height += offby;
\r
2408 case WMSZ_TOPLEFT:
\r
2409 SetWindowPos(hwndMain, NULL,
\r
2410 wrect.right - wpMain.width, wrect.bottom - wpMain.height,
\r
2411 wpMain.width, wpMain.height, SWP_NOCOPYBITS|SWP_NOZORDER);
\r
2414 case WMSZ_TOPRIGHT:
\r
2416 SetWindowPos(hwndMain, NULL,
\r
2417 wrect.left, wrect.bottom - wpMain.height,
\r
2418 wpMain.width, wpMain.height, SWP_NOCOPYBITS|SWP_NOZORDER);
\r
2421 case WMSZ_BOTTOMLEFT:
\r
2423 SetWindowPos(hwndMain, NULL,
\r
2424 wrect.right - wpMain.width, wrect.top,
\r
2425 wpMain.width, wpMain.height, SWP_NOCOPYBITS|SWP_NOZORDER);
\r
2428 case WMSZ_BOTTOMRIGHT:
\r
2432 SetWindowPos(hwndMain, NULL, 0, 0, wpMain.width, wpMain.height,
\r
2433 SWP_NOCOPYBITS|SWP_NOZORDER|SWP_NOMOVE);
\r
2438 for (i = 0; i < N_BUTTONS; i++) {
\r
2439 if (buttonDesc[i].hwnd != NULL) {
\r
2440 DestroyWindow(buttonDesc[i].hwnd);
\r
2441 buttonDesc[i].hwnd = NULL;
\r
2443 if (appData.showButtonBar) {
\r
2444 buttonDesc[i].hwnd =
\r
2445 CreateWindow("BUTTON", buttonDesc[i].label,
\r
2446 WS_VISIBLE | WS_CHILD | BS_PUSHBUTTON,
\r
2447 boardRect.right - BUTTON_WIDTH*(N_BUTTONS-i),
\r
2448 messageRect.top, BUTTON_WIDTH, messageSize.cy, hwndMain,
\r
2449 (HMENU) buttonDesc[i].id,
\r
2450 (HINSTANCE) GetWindowLongPtr(hwndMain, GWLP_HINSTANCE), NULL);
\r
2452 SendMessage(buttonDesc[i].hwnd, WM_SETFONT,
\r
2453 (WPARAM)font[boardSize][MESSAGE_FONT]->hf,
\r
2454 MAKELPARAM(FALSE, 0));
\r
2456 if (buttonDesc[i].id == IDM_Pause)
\r
2457 hwndPause = buttonDesc[i].hwnd;
\r
2458 buttonDesc[i].wndproc = (WNDPROC)
\r
2459 SetWindowLongPtr(buttonDesc[i].hwnd, GWLP_WNDPROC, (LONG_PTR) ButtonProc);
\r
2462 if (gridPen != NULL) DeleteObject(gridPen);
\r
2463 if (highlightPen != NULL) DeleteObject(highlightPen);
\r
2464 if (premovePen != NULL) DeleteObject(premovePen);
\r
2465 if (lineGap != 0) {
\r
2466 logbrush.lbStyle = BS_SOLID;
\r
2467 logbrush.lbColor = RGB(0, 0, 0); /* grid pen color = black */
\r
2469 ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,
\r
2470 lineGap, &logbrush, 0, NULL);
\r
2471 logbrush.lbColor = highlightSquareColor;
\r
2473 ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,
\r
2474 lineGap, &logbrush, 0, NULL);
\r
2476 logbrush.lbColor = premoveHighlightColor;
\r
2478 ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,
\r
2479 lineGap, &logbrush, 0, NULL);
\r
2481 /* [HGM] Loop had to be split in part for vert. and hor. lines */
\r
2482 for (i = 0; i < BOARD_HEIGHT + 1; i++) {
\r
2483 gridEndpoints[i*2].x = boardRect.left + lineGap / 2 + border;
\r
2484 gridEndpoints[i*2].y = gridEndpoints[i*2 + 1].y =
\r
2485 boardRect.top + lineGap / 2 + (i * (squareSize + lineGap)) + border;
\r
2486 gridEndpoints[i*2 + 1].x = boardRect.left + lineGap / 2 +
\r
2487 BOARD_WIDTH * (squareSize + lineGap) + border;
\r
2488 gridVertexCounts[i*2] = gridVertexCounts[i*2 + 1] = 2;
\r
2490 for (i = 0; i < BOARD_WIDTH + 1; i++) {
\r
2491 gridEndpoints[i*2 + BOARD_HEIGHT*2 + 2].y = boardRect.top + lineGap / 2 + border;
\r
2492 gridEndpoints[i*2 + BOARD_HEIGHT*2 + 2].x =
\r
2493 gridEndpoints[i*2 + 1 + BOARD_HEIGHT*2 + 2].x = boardRect.left +
\r
2494 lineGap / 2 + (i * (squareSize + lineGap)) + border;
\r
2495 gridEndpoints[i*2 + 1 + BOARD_HEIGHT*2 + 2].y =
\r
2496 boardRect.top + BOARD_HEIGHT * (squareSize + lineGap) + border;
\r
2497 gridVertexCounts[i*2] = gridVertexCounts[i*2 + 1] = 2;
\r
2501 /* [HGM] Licensing requirement */
\r
2503 if(gameInfo.variant == VariantGothic) GothicPopUp( GOTHIC, VariantGothic); else
\r
2506 if(gameInfo.variant == VariantFalcon) GothicPopUp( FALCON, VariantFalcon); else
\r
2508 GothicPopUp( "", VariantNormal);
\r
2511 /* if (boardSize == oldBoardSize) return; [HGM] variant might have changed */
\r
2513 /* Load piece bitmaps for this board size */
\r
2514 for (i=0; i<=2; i++) {
\r
2515 for (piece = WhitePawn;
\r
2516 (int) piece < (int) BlackPawn;
\r
2517 piece = (ChessSquare) ((int) piece + 1)) {
\r
2518 if (pieceBitmap[i][piece] != NULL)
\r
2519 DeleteObject(pieceBitmap[i][piece]);
\r
2523 fontBitmapSquareSize = 0; /* [HGM] render: make sure pieces will be recreated, as we might need others now */
\r
2524 // Orthodox Chess pieces
\r
2525 pieceBitmap[0][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "s");
\r
2526 pieceBitmap[0][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "s");
\r
2527 pieceBitmap[0][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "s");
\r
2528 pieceBitmap[0][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "s");
\r
2529 pieceBitmap[0][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "s");
\r
2530 pieceBitmap[1][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "o");
\r
2531 pieceBitmap[1][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "o");
\r
2532 pieceBitmap[1][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "o");
\r
2533 pieceBitmap[1][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "o");
\r
2534 pieceBitmap[1][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "o");
\r
2535 pieceBitmap[2][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "w");
\r
2536 pieceBitmap[2][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "w");
\r
2537 pieceBitmap[2][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "w");
\r
2538 pieceBitmap[2][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "w");
\r
2539 pieceBitmap[2][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "w");
\r
2540 if( gameInfo.variant == VariantShogi && squareSize <= 72 && squareSize >= 33) {
\r
2541 // in Shogi, Hijack the unused Queen for Lance
\r
2542 pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "s");
\r
2543 pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "o");
\r
2544 pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "w");
\r
2546 pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "s");
\r
2547 pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "o");
\r
2548 pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "w");
\r
2551 if(squareSize <= 72 && squareSize >= 33) {
\r
2552 /* A & C are available in most sizes now */
\r
2553 if(squareSize != 49 && squareSize != 72 && squareSize != 33) { // Vortex-like
\r
2554 pieceBitmap[0][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "s");
\r
2555 pieceBitmap[1][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "o");
\r
2556 pieceBitmap[2][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "w");
\r
2557 pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "s");
\r
2558 pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "o");
\r
2559 pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "w");
\r
2560 pieceBitmap[0][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "s");
\r
2561 pieceBitmap[1][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "o");
\r
2562 pieceBitmap[2][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "w");
\r
2563 pieceBitmap[0][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "s");
\r
2564 pieceBitmap[1][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "o");
\r
2565 pieceBitmap[2][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "w");
\r
2566 } else { // Smirf-like
\r
2567 if(gameInfo.variant == VariantSChess) {
\r
2568 pieceBitmap[0][WhiteAngel] = DoLoadBitmap(hInst, "v", squareSize, "s");
\r
2569 pieceBitmap[1][WhiteAngel] = DoLoadBitmap(hInst, "v", squareSize, "o");
\r
2570 pieceBitmap[2][WhiteAngel] = DoLoadBitmap(hInst, "v", squareSize, "w");
\r
2572 pieceBitmap[0][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "s");
\r
2573 pieceBitmap[1][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "o");
\r
2574 pieceBitmap[2][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "w");
\r
2577 if(gameInfo.variant == VariantGothic) { // Vortex-like
\r
2578 pieceBitmap[0][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "s");
\r
2579 pieceBitmap[1][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "o");
\r
2580 pieceBitmap[2][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "w");
\r
2581 } else if(gameInfo.variant == VariantSChess && (squareSize == 49 || squareSize == 72)) {
\r
2582 pieceBitmap[0][WhiteMarshall] = DoLoadBitmap(hInst, "e", squareSize, "s");
\r
2583 pieceBitmap[1][WhiteMarshall] = DoLoadBitmap(hInst, "e", squareSize, "o");
\r
2584 pieceBitmap[2][WhiteMarshall] = DoLoadBitmap(hInst, "e", squareSize, "w");
\r
2585 } else { // WinBoard standard
\r
2586 pieceBitmap[0][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "s");
\r
2587 pieceBitmap[1][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "o");
\r
2588 pieceBitmap[2][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "w");
\r
2593 if(squareSize==72 || squareSize==49 || squareSize==33) { /* experiment with some home-made bitmaps */
\r
2594 pieceBitmap[0][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "s");
\r
2595 pieceBitmap[1][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "o");
\r
2596 pieceBitmap[2][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "w");
\r
2597 pieceBitmap[0][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "s");
\r
2598 pieceBitmap[1][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "o");
\r
2599 pieceBitmap[2][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "w");
\r
2600 pieceBitmap[0][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "s");
\r
2601 pieceBitmap[1][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "o");
\r
2602 pieceBitmap[2][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "w");
\r
2603 pieceBitmap[0][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "s");
\r
2604 pieceBitmap[1][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "o");
\r
2605 pieceBitmap[2][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "w");
\r
2606 pieceBitmap[0][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "s");
\r
2607 pieceBitmap[1][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "o");
\r
2608 pieceBitmap[2][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "w");
\r
2609 pieceBitmap[0][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "s");
\r
2610 pieceBitmap[1][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "o");
\r
2611 pieceBitmap[2][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "w");
\r
2612 pieceBitmap[0][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "s");
\r
2613 pieceBitmap[1][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "o");
\r
2614 pieceBitmap[2][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "w");
\r
2615 pieceBitmap[0][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "s");
\r
2616 pieceBitmap[1][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "o");
\r
2617 pieceBitmap[2][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "w");
\r
2618 pieceBitmap[0][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "s");
\r
2619 pieceBitmap[1][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "o");
\r
2620 pieceBitmap[2][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "w");
\r
2621 pieceBitmap[0][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "s");
\r
2622 pieceBitmap[1][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "o");
\r
2623 pieceBitmap[2][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "w");
\r
2625 if(gameInfo.variant == VariantShogi) { /* promoted Gold represemtations */
\r
2626 pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "wp", squareSize, "s");
\r
2627 pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "wp", squareSize, "o");
\r
2628 pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "w", squareSize, "w");
\r
2629 pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "wn", squareSize, "s");
\r
2630 pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "wn", squareSize, "o");
\r
2631 pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "w", squareSize, "w");
\r
2632 pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "ws", squareSize, "s");
\r
2633 pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "ws", squareSize, "o");
\r
2634 pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "w", squareSize, "w");
\r
2635 pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "wl", squareSize, "s");
\r
2636 pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "wl", squareSize, "o");
\r
2637 pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "w", squareSize, "w");
\r
2639 pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "s");
\r
2640 pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "o");
\r
2641 pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "w");
\r
2642 pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "s");
\r
2643 pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "o");
\r
2644 pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "w");
\r
2645 pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "s");
\r
2646 pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "o");
\r
2647 pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "w");
\r
2648 pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "s");
\r
2649 pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "o");
\r
2650 pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "w");
\r
2653 } else { /* other size, no special bitmaps available. Use smaller symbols */
\r
2654 if((int)boardSize < 2) minorSize = sizeInfo[0].squareSize;
\r
2655 else minorSize = sizeInfo[(int)boardSize - 2].squareSize;
\r
2656 pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "s");
\r
2657 pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "o");
\r
2658 pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "w");
\r
2659 pieceBitmap[0][WhiteCardinal] = DoLoadBitmap(hInst, "b", minorSize, "s");
\r
2660 pieceBitmap[1][WhiteCardinal] = DoLoadBitmap(hInst, "b", minorSize, "o");
\r
2661 pieceBitmap[2][WhiteCardinal] = DoLoadBitmap(hInst, "b", minorSize, "w");
\r
2662 pieceBitmap[0][WhiteDragon] = DoLoadBitmap(hInst, "r", minorSize, "s");
\r
2663 pieceBitmap[1][WhiteDragon] = DoLoadBitmap(hInst, "r", minorSize, "o");
\r
2664 pieceBitmap[2][WhiteDragon] = DoLoadBitmap(hInst, "r", minorSize, "w");
\r
2665 pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "s");
\r
2666 pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "o");
\r
2667 pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "w");
\r
2671 if(gameInfo.variant == VariantShogi && squareSize == 58)
\r
2672 /* special Shogi support in this size */
\r
2673 { for (i=0; i<=2; i++) { /* replace all bitmaps */
\r
2674 for (piece = WhitePawn;
\r
2675 (int) piece < (int) BlackPawn;
\r
2676 piece = (ChessSquare) ((int) piece + 1)) {
\r
2677 if (pieceBitmap[i][piece] != NULL)
\r
2678 DeleteObject(pieceBitmap[i][piece]);
\r
2681 pieceBitmap[0][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "o");
\r
2682 pieceBitmap[0][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "o");
\r
2683 pieceBitmap[0][WhiteBishop] = DoLoadBitmap(hInst, "sb", squareSize, "o");
\r
2684 pieceBitmap[0][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "o");
\r
2685 pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "o");
\r
2686 pieceBitmap[0][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "o");
\r
2687 pieceBitmap[0][WhiteFerz] = DoLoadBitmap(hInst, "sf", squareSize, "o");
\r
2688 pieceBitmap[0][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "o");
\r
2689 pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "su", squareSize, "o");
\r
2690 pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "sh", squareSize, "o");
\r
2691 pieceBitmap[0][WhiteCardinal] = DoLoadBitmap(hInst, "sa", squareSize, "o");
\r
2692 pieceBitmap[0][WhiteDragon] = DoLoadBitmap(hInst, "sc", squareSize, "o");
\r
2693 pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "sg", squareSize, "o");
\r
2694 pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "ss", squareSize, "o");
\r
2695 pieceBitmap[1][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "o");
\r
2696 pieceBitmap[1][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "o");
\r
2697 pieceBitmap[1][WhiteBishop] = DoLoadBitmap(hInst, "sb", squareSize, "o");
\r
2698 pieceBitmap[1][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "o");
\r
2699 pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "o");
\r
2700 pieceBitmap[1][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "o");
\r
2701 pieceBitmap[1][WhiteFerz] = DoLoadBitmap(hInst, "sf", squareSize, "o");
\r
2702 pieceBitmap[1][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "o");
\r
2703 pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "su", squareSize, "o");
\r
2704 pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "sh", squareSize, "o");
\r
2705 pieceBitmap[1][WhiteCardinal] = DoLoadBitmap(hInst, "sa", squareSize, "o");
\r
2706 pieceBitmap[1][WhiteDragon] = DoLoadBitmap(hInst, "sc", squareSize, "o");
\r
2707 pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "sg", squareSize, "o");
\r
2708 pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "ss", squareSize, "o");
\r
2709 pieceBitmap[2][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "w");
\r
2710 pieceBitmap[2][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "w");
\r
2711 pieceBitmap[2][WhiteBishop] = DoLoadBitmap(hInst, "sr", squareSize, "w");
\r
2712 pieceBitmap[2][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "w");
\r
2713 pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "w");
\r
2714 pieceBitmap[2][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "w");
\r
2715 pieceBitmap[2][WhiteFerz] = DoLoadBitmap(hInst, "sw", squareSize, "w");
\r
2716 pieceBitmap[2][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "w");
\r
2717 pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "sp", squareSize, "w");
\r
2718 pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "sn", squareSize, "w");
\r
2719 pieceBitmap[2][WhiteCardinal] = DoLoadBitmap(hInst, "sr", squareSize, "w");
\r
2720 pieceBitmap[2][WhiteDragon] = DoLoadBitmap(hInst, "sr", squareSize, "w");
\r
2721 pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "sl", squareSize, "w");
\r
2722 pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "sw", squareSize, "w");
\r
2728 PieceBitmap(ChessSquare p, int kind)
\r
2730 if ((int) p >= (int) BlackPawn)
\r
2731 p = (ChessSquare) ((int) p - (int) BlackPawn + (int) WhitePawn);
\r
2733 return pieceBitmap[kind][(int) p];
\r
2736 /***************************************************************/
\r
2738 #define MIN(a,b) (((a) < (b)) ? (a) : (b))
\r
2739 #define MAX(a,b) (((a) > (b)) ? (a) : (b))
\r
2741 #define MIN3(a,b,c) (((a) < (b) && (a) < (c)) ? (a) : (((b) < (a) && (b) < (c)) ? (b) : (c)))
\r
2742 #define MAX3(a,b,c) (((a) > (b) && (a) > (c)) ? (a) : (((b) > (a) && (b) > (c)) ? (b) : (c)))
\r
2746 SquareToPos(int row, int column, int * x, int * y)
\r
2749 *x = boardRect.left + lineGap + ((BOARD_WIDTH-1)-column) * (squareSize + lineGap) + border;
\r
2750 *y = boardRect.top + lineGap + row * (squareSize + lineGap) + border;
\r
2752 *x = boardRect.left + lineGap + column * (squareSize + lineGap) + border;
\r
2753 *y = boardRect.top + lineGap + ((BOARD_HEIGHT-1)-row) * (squareSize + lineGap) + border;
\r
2758 DrawCoordsOnDC(HDC hdc)
\r
2760 static char files[] = "0123456789012345678901221098765432109876543210";
\r
2761 static char ranks[] = "wvutsrqponmlkjihgfedcbaabcdefghijklmnopqrstuvw";
\r
2762 char str[2] = { NULLCHAR, NULLCHAR };
\r
2763 int oldMode, oldAlign, x, y, start, i;
\r
2767 if (!appData.showCoords)
\r
2770 start = flipView ? 1-(ONE!='1') : 45+(ONE!='1')-BOARD_HEIGHT;
\r
2772 oldBrush = SelectObject(hdc, GetStockObject(BLACK_BRUSH));
\r
2773 oldMode = SetBkMode(hdc, (appData.monoMode ? OPAQUE : TRANSPARENT));
\r
2774 oldAlign = GetTextAlign(hdc);
\r
2775 oldFont = SelectObject(hdc, font[boardSize][COORD_FONT]->hf);
\r
2777 y = boardRect.top + lineGap;
\r
2778 x = boardRect.left + lineGap + gameInfo.holdingsWidth*(squareSize + lineGap);
\r
2781 SetTextAlign(hdc, TA_RIGHT|TA_TOP);
\r
2782 x += border - lineGap - 4; y += squareSize - 6;
\r
2784 SetTextAlign(hdc, TA_LEFT|TA_TOP);
\r
2785 for (i = 0; i < BOARD_HEIGHT; i++) {
\r
2786 str[0] = files[start + i];
\r
2787 ExtTextOut(hdc, x + 2 - (border ? gameInfo.holdingsWidth * (squareSize + lineGap) : 0), y + 1, 0, NULL, str, 1, NULL);
\r
2788 y += squareSize + lineGap;
\r
2791 start = flipView ? 23-(BOARD_RGHT-BOARD_LEFT) : 23;
\r
2794 SetTextAlign(hdc, TA_LEFT|TA_TOP);
\r
2795 x += -border + 4; y += border - squareSize + 6;
\r
2797 SetTextAlign(hdc, TA_RIGHT|TA_BOTTOM);
\r
2798 for (i = 0; i < BOARD_RGHT - BOARD_LEFT; i++) {
\r
2799 str[0] = ranks[start + i];
\r
2800 ExtTextOut(hdc, x + squareSize - 2, y - 1, 0, NULL, str, 1, NULL);
\r
2801 x += squareSize + lineGap;
\r
2804 SelectObject(hdc, oldBrush);
\r
2805 SetBkMode(hdc, oldMode);
\r
2806 SetTextAlign(hdc, oldAlign);
\r
2807 SelectObject(hdc, oldFont);
\r
2811 DrawGridOnDC(HDC hdc)
\r
2815 if (lineGap != 0) {
\r
2816 oldPen = SelectObject(hdc, gridPen);
\r
2817 PolyPolyline(hdc, gridEndpoints, gridVertexCounts, BOARD_WIDTH+BOARD_HEIGHT + 2);
\r
2818 SelectObject(hdc, oldPen);
\r
2822 #define HIGHLIGHT_PEN 0
\r
2823 #define PREMOVE_PEN 1
\r
2826 DrawHighlightOnDC(HDC hdc, BOOLEAN on, int x, int y, int pen)
\r
2829 HPEN oldPen, hPen;
\r
2830 if (lineGap == 0) return;
\r
2832 x1 = boardRect.left +
\r
2833 lineGap/2 + ((BOARD_WIDTH-1)-x) * (squareSize + lineGap) + border;
\r
2834 y1 = boardRect.top +
\r
2835 lineGap/2 + y * (squareSize + lineGap) + border;
\r
2837 x1 = boardRect.left +
\r
2838 lineGap/2 + x * (squareSize + lineGap) + border;
\r
2839 y1 = boardRect.top +
\r
2840 lineGap/2 + ((BOARD_HEIGHT-1)-y) * (squareSize + lineGap) + border;
\r
2842 hPen = pen ? premovePen : highlightPen;
\r
2843 oldPen = SelectObject(hdc, on ? hPen : gridPen);
\r
2844 MoveToEx(hdc, x1, y1, NULL);
\r
2845 LineTo(hdc, x1 + squareSize + lineGap, y1);
\r
2846 LineTo(hdc, x1 + squareSize + lineGap, y1 + squareSize + lineGap);
\r
2847 LineTo(hdc, x1, y1 + squareSize + lineGap);
\r
2848 LineTo(hdc, x1, y1);
\r
2849 SelectObject(hdc, oldPen);
\r
2853 DrawHighlightsOnDC(HDC hdc, HighlightInfo *h, int pen)
\r
2856 for (i=0; i<2; i++) {
\r
2857 if (h->sq[i].x >= 0 && h->sq[i].y >= 0)
\r
2858 DrawHighlightOnDC(hdc, TRUE,
\r
2859 h->sq[i].x, h->sq[i].y,
\r
2864 /* Note: sqcolor is used only in monoMode */
\r
2865 /* Note that this code is largely duplicated in woptions.c,
\r
2866 function DrawSampleSquare, so that needs to be updated too */
\r
2868 DrawPieceOnDC(HDC hdc, ChessSquare piece, int color, int sqcolor, int x, int y, HDC tmphdc)
\r
2870 HBITMAP oldBitmap;
\r
2874 if (appData.blindfold) return;
\r
2876 /* [AS] Use font-based pieces if needed */
\r
2877 if( fontBitmapSquareSize >= 0 && (squareSize > 32 || gameInfo.variant >= VariantShogi)) {
\r
2878 /* Create piece bitmaps, or do nothing if piece set is up to date */
\r
2879 CreatePiecesFromFont();
\r
2881 if( fontBitmapSquareSize == squareSize ) {
\r
2882 int index = TranslatePieceToFontPiece(piece);
\r
2884 SelectObject( tmphdc, hPieceMask[ index ] );
\r
2886 if(appData.upsideDown ? color==flipView : (flipView && gameInfo.variant == VariantShogi))
\r
2887 StretchBlt(hdc, x+squareSize, y+squareSize, -squareSize, -squareSize, tmphdc, 0, 0, squareSize, squareSize, SRCAND);
\r
2891 squareSize, squareSize,
\r
2896 SelectObject( tmphdc, hPieceFace[ index ] );
\r
2898 if(appData.upsideDown ? color==flipView : (flipView && gameInfo.variant == VariantShogi))
\r
2899 StretchBlt(hdc, x+squareSize, y+squareSize, -squareSize, -squareSize, tmphdc, 0, 0, squareSize, squareSize, SRCPAINT);
\r
2903 squareSize, squareSize,
\r
2912 if (appData.monoMode) {
\r
2913 SelectObject(tmphdc, PieceBitmap(piece,
\r
2914 color == sqcolor ? OUTLINE_PIECE : SOLID_PIECE));
\r
2915 BitBlt(hdc, x, y, squareSize, squareSize, tmphdc, 0, 0,
\r
2916 sqcolor ? SRCCOPY : NOTSRCCOPY);
\r
2918 HBRUSH xBrush = whitePieceBrush;
\r
2919 tmpSize = squareSize;
\r
2920 if(appData.pieceDirectory[0]) xBrush = GetStockObject(WHITE_BRUSH);
\r
2922 ((piece >= (int)WhiteNightrider && piece <= WhiteGrasshopper) ||
\r
2923 (piece >= (int)BlackNightrider && piece <= BlackGrasshopper)) ) {
\r
2924 /* [HGM] no bitmap available for promoted pieces in Crazyhouse */
\r
2925 /* Bitmaps of smaller size are substituted, but we have to align them */
\r
2926 x += (squareSize - minorSize)>>1;
\r
2927 y += squareSize - minorSize - 2;
\r
2928 tmpSize = minorSize;
\r
2930 if (color || appData.allWhite ) {
\r
2931 oldBitmap = SelectObject(tmphdc, PieceBitmap(piece, WHITE_PIECE));
\r
2933 oldBrush = SelectObject(hdc, xBrush);
\r
2934 else oldBrush = SelectObject(hdc, blackPieceBrush);
\r
2935 if(appData.upsideDown && color==flipView)
\r
2936 StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, 0x00B8074A);
\r
2938 BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, 0x00B8074A);
\r
2939 /* Use black for outline of white pieces */
\r
2940 SelectObject(tmphdc, PieceBitmap(piece, OUTLINE_PIECE));
\r
2941 if(appData.upsideDown && color==flipView)
\r
2942 StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, SRCAND);
\r
2944 BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, SRCAND);
\r
2945 } else if(appData.pieceDirectory[0]) {
\r
2946 oldBitmap = SelectObject(tmphdc, PieceBitmap(piece, WHITE_PIECE));
\r
2947 oldBrush = SelectObject(hdc, xBrush);
\r
2948 if(appData.upsideDown && color==flipView)
\r
2949 StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, 0x00B8074A);
\r
2951 BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, 0x00B8074A);
\r
2952 SelectObject(tmphdc, PieceBitmap(piece, SOLID_PIECE));
\r
2953 if(appData.upsideDown && color==flipView)
\r
2954 StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, SRCAND);
\r
2956 BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, SRCAND);
\r
2958 /* Use square color for details of black pieces */
\r
2959 oldBitmap = SelectObject(tmphdc, PieceBitmap(piece, SOLID_PIECE));
\r
2960 oldBrush = SelectObject(hdc, blackPieceBrush);
\r
2961 if(appData.upsideDown && !flipView)
\r
2962 StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, 0x00B8074A);
\r
2964 BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, 0x00B8074A);
\r
2966 SelectObject(hdc, oldBrush);
\r
2967 SelectObject(tmphdc, oldBitmap);
\r
2971 /* [AS] Compute a drawing mode for a square, based on specified settings (see DrawTile) */
\r
2972 int GetBackTextureMode( int algo )
\r
2974 int result = BACK_TEXTURE_MODE_DISABLED;
\r
2978 case BACK_TEXTURE_MODE_PLAIN:
\r
2979 result = 1; /* Always use identity map */
\r
2981 case BACK_TEXTURE_MODE_FULL_RANDOM:
\r
2982 result = 1 + (myrandom() % 3); /* Pick a transformation at random */
\r
2990 [AS] Compute and save texture drawing info, otherwise we may not be able
\r
2991 to handle redraws cleanly (as random numbers would always be different).
\r
2993 VOID RebuildTextureSquareInfo()
\r
3003 ZeroMemory( &backTextureSquareInfo, sizeof(backTextureSquareInfo) );
\r
3005 if( liteBackTexture != NULL ) {
\r
3006 if( GetObject( liteBackTexture, sizeof(bi), &bi ) > 0 ) {
\r
3007 lite_w = bi.bmWidth;
\r
3008 lite_h = bi.bmHeight;
\r
3012 if( darkBackTexture != NULL ) {
\r
3013 if( GetObject( darkBackTexture, sizeof(bi), &bi ) > 0 ) {
\r
3014 dark_w = bi.bmWidth;
\r
3015 dark_h = bi.bmHeight;
\r
3019 for( row=0; row<BOARD_HEIGHT; row++ ) {
\r
3020 for( col=0; col<BOARD_WIDTH; col++ ) {
\r
3021 if( (col + row) & 1 ) {
\r
3023 if( lite_w >= squareSize && lite_h >= squareSize ) {
\r
3024 if( lite_w >= squareSize*BOARD_WIDTH )
\r
3025 backTextureSquareInfo[row][col].x = (2*col+1)*lite_w/(2*BOARD_WIDTH) - squareSize/2; /* [HGM] cut out of center of virtual square */
\r
3027 backTextureSquareInfo[row][col].x = col * (lite_w - squareSize) / (BOARD_WIDTH-1); /* [HGM] divide by size-1 in stead of size! */
\r
3028 if( lite_h >= squareSize*BOARD_HEIGHT )
\r
3029 backTextureSquareInfo[row][col].y = (2*(BOARD_HEIGHT-row)-1)*lite_h/(2*BOARD_HEIGHT) - squareSize/2;
\r
3031 backTextureSquareInfo[row][col].y = (BOARD_HEIGHT-1-row) * (lite_h - squareSize) / (BOARD_HEIGHT-1);
\r
3032 backTextureSquareInfo[row][col].mode = GetBackTextureMode(liteBackTextureMode);
\r
3037 if( dark_w >= squareSize && dark_h >= squareSize ) {
\r
3038 if( dark_w >= squareSize*BOARD_WIDTH )
\r
3039 backTextureSquareInfo[row][col].x = (2*col+1) * dark_w / (2*BOARD_WIDTH) - squareSize/2;
\r
3041 backTextureSquareInfo[row][col].x = col * (dark_w - squareSize) / (BOARD_WIDTH-1);
\r
3042 if( dark_h >= squareSize*BOARD_HEIGHT )
\r
3043 backTextureSquareInfo[row][col].y = (2*(BOARD_HEIGHT-row)-1) * dark_h / (2*BOARD_HEIGHT) - squareSize/2;
\r
3045 backTextureSquareInfo[row][col].y = (BOARD_HEIGHT-1-row) * (dark_h - squareSize) / (BOARD_HEIGHT-1);
\r
3046 backTextureSquareInfo[row][col].mode = GetBackTextureMode(darkBackTextureMode);
\r
3053 /* [AS] Arrow highlighting support */
\r
3055 static double A_WIDTH = 5; /* Width of arrow body */
\r
3057 #define A_HEIGHT_FACTOR 6 /* Length of arrow "point", relative to body width */
\r
3058 #define A_WIDTH_FACTOR 3 /* Width of arrow "point", relative to body width */
\r
3060 static double Sqr( double x )
\r
3065 static int Round( double x )
\r
3067 return (int) (x + 0.5);
\r
3070 /* Draw an arrow between two points using current settings */
\r
3071 VOID DrawArrowBetweenPoints( HDC hdc, int s_x, int s_y, int d_x, int d_y )
\r
3074 double dx, dy, j, k, x, y;
\r
3076 if( d_x == s_x ) {
\r
3077 int h = (d_y > s_y) ? +A_WIDTH*A_HEIGHT_FACTOR : -A_WIDTH*A_HEIGHT_FACTOR;
\r
3079 arrow[0].x = s_x + A_WIDTH + 0.5;
\r
3082 arrow[1].x = s_x + A_WIDTH + 0.5;
\r
3083 arrow[1].y = d_y - h;
\r
3085 arrow[2].x = arrow[1].x + A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;
\r
3086 arrow[2].y = d_y - h;
\r
3091 arrow[5].x = arrow[1].x - 2*A_WIDTH + 0.5;
\r
3092 arrow[5].y = d_y - h;
\r
3094 arrow[4].x = arrow[5].x - A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;
\r
3095 arrow[4].y = d_y - h;
\r
3097 arrow[6].x = arrow[1].x - 2*A_WIDTH + 0.5;
\r
3100 else if( d_y == s_y ) {
\r
3101 int w = (d_x > s_x) ? +A_WIDTH*A_HEIGHT_FACTOR : -A_WIDTH*A_HEIGHT_FACTOR;
\r
3104 arrow[0].y = s_y + A_WIDTH + 0.5;
\r
3106 arrow[1].x = d_x - w;
\r
3107 arrow[1].y = s_y + A_WIDTH + 0.5;
\r
3109 arrow[2].x = d_x - w;
\r
3110 arrow[2].y = arrow[1].y + A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;
\r
3115 arrow[5].x = d_x - w;
\r
3116 arrow[5].y = arrow[1].y - 2*A_WIDTH + 0.5;
\r
3118 arrow[4].x = d_x - w;
\r
3119 arrow[4].y = arrow[5].y - A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;
\r
3122 arrow[6].y = arrow[1].y - 2*A_WIDTH + 0.5;
\r
3125 /* [AS] Needed a lot of paper for this! :-) */
\r
3126 dy = (double) (d_y - s_y) / (double) (d_x - s_x);
\r
3127 dx = (double) (s_x - d_x) / (double) (s_y - d_y);
\r
3129 j = sqrt( Sqr(A_WIDTH) / (1.0 + Sqr(dx)) );
\r
3131 k = sqrt( Sqr(A_WIDTH*A_HEIGHT_FACTOR) / (1.0 + Sqr(dy)) );
\r
3136 arrow[0].x = Round(x - j);
\r
3137 arrow[0].y = Round(y + j*dx);
\r
3139 arrow[1].x = Round(arrow[0].x + 2*j); // [HGM] prevent width to be affected by rounding twice
\r
3140 arrow[1].y = Round(arrow[0].y - 2*j*dx);
\r
3143 x = (double) d_x - k;
\r
3144 y = (double) d_y - k*dy;
\r
3147 x = (double) d_x + k;
\r
3148 y = (double) d_y + k*dy;
\r
3151 x = Round(x); y = Round(y); // [HGM] make sure width of shaft is rounded the same way on both ends
\r
3153 arrow[6].x = Round(x - j);
\r
3154 arrow[6].y = Round(y + j*dx);
\r
3156 arrow[2].x = Round(arrow[6].x + 2*j);
\r
3157 arrow[2].y = Round(arrow[6].y - 2*j*dx);
\r
3159 arrow[3].x = Round(arrow[2].x + j*(A_WIDTH_FACTOR-1));
\r
3160 arrow[3].y = Round(arrow[2].y - j*(A_WIDTH_FACTOR-1)*dx);
\r
3165 arrow[5].x = Round(arrow[6].x - j*(A_WIDTH_FACTOR-1));
\r
3166 arrow[5].y = Round(arrow[6].y + j*(A_WIDTH_FACTOR-1)*dx);
\r
3169 Polygon( hdc, arrow, 7 );
\r
3172 /* [AS] Draw an arrow between two squares */
\r
3173 VOID DrawArrowBetweenSquares( HDC hdc, int s_col, int s_row, int d_col, int d_row )
\r
3175 int s_x, s_y, d_x, d_y;
\r
3182 if( s_col == d_col && s_row == d_row ) {
\r
3186 /* Get source and destination points */
\r
3187 SquareToPos( s_row, s_col, &s_x, &s_y);
\r
3188 SquareToPos( d_row, d_col, &d_x, &d_y);
\r
3191 d_y += squareSize / 2 - squareSize / 4; // [HGM] round towards same centers on all sides!
\r
3193 else if( d_y < s_y ) {
\r
3194 d_y += squareSize / 2 + squareSize / 4;
\r
3197 d_y += squareSize / 2;
\r
3201 d_x += squareSize / 2 - squareSize / 4;
\r
3203 else if( d_x < s_x ) {
\r
3204 d_x += squareSize / 2 + squareSize / 4;
\r
3207 d_x += squareSize / 2;
\r
3210 s_x += squareSize / 2;
\r
3211 s_y += squareSize / 2;
\r
3213 /* Adjust width */
\r
3214 A_WIDTH = squareSize / 14.; //[HGM] make float
\r
3217 stLB.lbStyle = BS_SOLID;
\r
3218 stLB.lbColor = appData.highlightArrowColor;
\r
3221 hpen = CreatePen( PS_SOLID, 2, RGB(0x00,0x00,0x00) );
\r
3222 holdpen = SelectObject( hdc, hpen );
\r
3223 hbrush = CreateBrushIndirect( &stLB );
\r
3224 holdbrush = SelectObject( hdc, hbrush );
\r
3226 DrawArrowBetweenPoints( hdc, s_x, s_y, d_x, d_y );
\r
3228 SelectObject( hdc, holdpen );
\r
3229 SelectObject( hdc, holdbrush );
\r
3230 DeleteObject( hpen );
\r
3231 DeleteObject( hbrush );
\r
3234 BOOL HasHighlightInfo()
\r
3236 BOOL result = FALSE;
\r
3238 if( highlightInfo.sq[0].x >= 0 && highlightInfo.sq[0].y >= 0 &&
\r
3239 highlightInfo.sq[1].x >= 0 && highlightInfo.sq[1].y >= 0 )
\r
3247 BOOL IsDrawArrowEnabled()
\r
3249 BOOL result = FALSE;
\r
3251 if( appData.highlightMoveWithArrow && squareSize >= 32 ) {
\r
3258 VOID DrawArrowHighlight( HDC hdc )
\r
3260 if( IsDrawArrowEnabled() && HasHighlightInfo() ) {
\r
3261 DrawArrowBetweenSquares( hdc,
\r
3262 highlightInfo.sq[0].x, highlightInfo.sq[0].y,
\r
3263 highlightInfo.sq[1].x, highlightInfo.sq[1].y );
\r
3267 HRGN GetArrowHighlightClipRegion( HDC hdc )
\r
3269 HRGN result = NULL;
\r
3271 if( HasHighlightInfo() ) {
\r
3272 int x1, y1, x2, y2;
\r
3273 int sx, sy, dx, dy;
\r
3275 SquareToPos(highlightInfo.sq[0].y, highlightInfo.sq[0].x, &x1, &y1 );
\r
3276 SquareToPos(highlightInfo.sq[1].y, highlightInfo.sq[1].x, &x2, &y2 );
\r
3278 sx = MIN( x1, x2 );
\r
3279 sy = MIN( y1, y2 );
\r
3280 dx = MAX( x1, x2 ) + squareSize;
\r
3281 dy = MAX( y1, y2 ) + squareSize;
\r
3283 result = CreateRectRgn( sx, sy, dx, dy );
\r
3290 Warning: this function modifies the behavior of several other functions.
\r
3292 Basically, Winboard is optimized to avoid drawing the whole board if not strictly
\r
3293 needed. Unfortunately, the decision whether or not to perform a full or partial
\r
3294 repaint is scattered all over the place, which is not good for features such as
\r
3295 "arrow highlighting" that require a full repaint of the board.
\r
3297 So, I've tried to patch the code where I thought it made sense (e.g. after or during
\r
3298 user interaction, when speed is not so important) but especially to avoid errors
\r
3299 in the displayed graphics.
\r
3301 In such patched places, I always try refer to this function so there is a single
\r
3302 place to maintain knowledge.
\r
3304 To restore the original behavior, just return FALSE unconditionally.
\r
3306 BOOL IsFullRepaintPreferrable()
\r
3308 BOOL result = FALSE;
\r
3310 if( (appData.highlightLastMove || appData.highlightDragging) && IsDrawArrowEnabled() ) {
\r
3311 /* Arrow may appear on the board */
\r
3319 This function is called by DrawPosition to know whether a full repaint must
\r
3322 Only DrawPosition may directly call this function, which makes use of
\r
3323 some state information. Other function should call DrawPosition specifying
\r
3324 the repaint flag, and can use IsFullRepaintPreferrable if needed.
\r
3326 BOOL DrawPositionNeedsFullRepaint()
\r
3328 BOOL result = FALSE;
\r
3331 Probably a slightly better policy would be to trigger a full repaint
\r
3332 when animInfo.piece changes state (i.e. empty -> non-empty and viceversa),
\r
3333 but animation is fast enough that it's difficult to notice.
\r
3335 if( animInfo.piece == EmptySquare ) {
\r
3336 if( (appData.highlightLastMove || appData.highlightDragging) && IsDrawArrowEnabled() /*&& HasHighlightInfo()*/ ) {
\r
3344 static HBITMAP borderBitmap;
\r
3347 DrawBackgroundOnDC(HDC hdc)
\r
3353 static char oldBorder[MSG_SIZ];
\r
3354 int w = 600, h = 600;
\r
3356 if(strcmp(appData.border, oldBorder)) { // load new one when old one no longer valid
\r
3357 strncpy(oldBorder, appData.border, MSG_SIZ-1);
\r
3358 borderBitmap = LoadImage( 0, appData.border, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );
\r
3360 if(borderBitmap == NULL) { // loading failed, use white
\r
3361 FillRect( hdc, &boardRect, whitePieceBrush );
\r
3364 tmphdc = CreateCompatibleDC(hdc);
\r
3365 hbm = SelectObject(tmphdc, borderBitmap);
\r
3366 if( GetObject( borderBitmap, sizeof(bi), &bi ) > 0 ) {
\r
3370 StretchBlt(hdc, boardRect.left, boardRect.top, boardRect.right - boardRect.left,
\r
3371 boardRect.bottom - boardRect.top, tmphdc, 0, 0, w, h, SRCCOPY);
\r
3372 SelectObject(tmphdc, hbm);
\r
3377 DrawBoardOnDC(HDC hdc, Board board, HDC tmphdc)
\r
3379 int row, column, x, y, square_color, piece_color;
\r
3380 ChessSquare piece;
\r
3382 HDC texture_hdc = NULL;
\r
3384 /* [AS] Initialize background textures if needed */
\r
3385 if( liteBackTexture != NULL || darkBackTexture != NULL ) {
\r
3386 static int backTextureBoardSize; /* [HGM] boardsize: also new texture if board format changed */
\r
3387 if( backTextureSquareSize != squareSize
\r
3388 || backTextureBoardSize != BOARD_WIDTH+BOARD_FILES*BOARD_HEIGHT) {
\r
3389 backTextureBoardSize = BOARD_WIDTH+BOARD_FILES*BOARD_HEIGHT;
\r
3390 backTextureSquareSize = squareSize;
\r
3391 RebuildTextureSquareInfo();
\r
3394 texture_hdc = CreateCompatibleDC( hdc );
\r
3397 for (row = 0; row < BOARD_HEIGHT; row++) {
\r
3398 for (column = 0; column < BOARD_WIDTH; column++) {
\r
3400 SquareToPos(row, column, &x, &y);
\r
3402 piece = board[row][column];
\r
3404 square_color = ((column + row) % 2) == 1;
\r
3405 if( gameInfo.variant == VariantXiangqi ) {
\r
3406 square_color = !InPalace(row, column);
\r
3407 if(BOARD_HEIGHT&1) { if(row==BOARD_HEIGHT/2) square_color ^= 1; }
\r
3408 else if(row < BOARD_HEIGHT/2) square_color ^= 1;
\r
3410 piece_color = (int) piece < (int) BlackPawn;
\r
3413 /* [HGM] holdings file: light square or black */
\r
3414 if(column == BOARD_LEFT-2) {
\r
3415 if( row > BOARD_HEIGHT - gameInfo.holdingsSize - 1 )
\r
3418 DisplayHoldingsCount(hdc, x, y, 0, 0); /* black out */
\r
3422 if(column == BOARD_RGHT + 1 ) {
\r
3423 if( row < gameInfo.holdingsSize )
\r
3426 DisplayHoldingsCount(hdc, x, y, 0, 0);
\r
3430 if(column == BOARD_LEFT-1 ) /* left align */
\r
3431 DisplayHoldingsCount(hdc, x, y, flipView, (int) board[row][column]);
\r
3432 else if( column == BOARD_RGHT) /* right align */
\r
3433 DisplayHoldingsCount(hdc, x, y, !flipView, (int) board[row][column]);
\r
3435 if (appData.monoMode) {
\r
3436 if (piece == EmptySquare) {
\r
3437 BitBlt(hdc, x, y, squareSize, squareSize, 0, 0, 0,
\r
3438 square_color ? WHITENESS : BLACKNESS);
\r
3440 DrawPieceOnDC(hdc, piece, piece_color, square_color, x, y, tmphdc);
\r
3443 else if( appData.useBitmaps && backTextureSquareInfo[row][column].mode > 0 ) {
\r
3444 /* [AS] Draw the square using a texture bitmap */
\r
3445 HBITMAP hbm = SelectObject( texture_hdc, square_color ? liteBackTexture : darkBackTexture );
\r
3446 int r = row, c = column; // [HGM] do not flip board in flipView
\r
3447 if(flipView) { r = BOARD_HEIGHT-1 - r; c = BOARD_WIDTH-1 - c; }
\r
3450 squareSize, squareSize,
\r
3453 backTextureSquareInfo[r][c].mode,
\r
3454 backTextureSquareInfo[r][c].x,
\r
3455 backTextureSquareInfo[r][c].y );
\r
3457 SelectObject( texture_hdc, hbm );
\r
3459 if (piece != EmptySquare) {
\r
3460 DrawPieceOnDC(hdc, piece, piece_color, -1, x, y, tmphdc);
\r
3464 HBRUSH brush = square_color ? lightSquareBrush : darkSquareBrush;
\r
3466 oldBrush = SelectObject(hdc, brush );
\r
3467 BitBlt(hdc, x, y, squareSize, squareSize, 0, 0, 0, PATCOPY);
\r
3468 SelectObject(hdc, oldBrush);
\r
3469 if (piece != EmptySquare)
\r
3470 DrawPieceOnDC(hdc, piece, piece_color, -1, x, y, tmphdc);
\r
3475 if( texture_hdc != NULL ) {
\r
3476 DeleteDC( texture_hdc );
\r
3480 int saveDiagFlag = 0; FILE *diagFile; // [HGM] diag
\r
3481 void fputDW(FILE *f, int x)
\r
3483 fputc(x & 255, f);
\r
3484 fputc(x>>8 & 255, f);
\r
3485 fputc(x>>16 & 255, f);
\r
3486 fputc(x>>24 & 255, f);
\r
3489 #define MAX_CLIPS 200 /* more than enough */
\r
3492 DrawLogoOnDC(HDC hdc, RECT logoRect, HBITMAP logo)
\r
3494 // HBITMAP bufferBitmap;
\r
3499 int w = 100, h = 50;
\r
3501 if(logo == NULL) {
\r
3502 if(!logoHeight) return;
\r
3503 FillRect( hdc, &logoRect, whitePieceBrush );
\r
3505 // GetClientRect(hwndMain, &Rect);
\r
3506 // bufferBitmap = CreateCompatibleBitmap(hdc, Rect.right-Rect.left+1,
\r
3507 // Rect.bottom-Rect.top+1);
\r
3508 tmphdc = CreateCompatibleDC(hdc);
\r
3509 hbm = SelectObject(tmphdc, logo);
\r
3510 if( GetObject( logo, sizeof(bi), &bi ) > 0 ) {
\r
3514 StretchBlt(hdc, logoRect.left, logoRect.top, logoRect.right - logoRect.left,
\r
3515 logoRect.bottom - logoRect.top, tmphdc, 0, 0, w, h, SRCCOPY);
\r
3516 SelectObject(tmphdc, hbm);
\r
3524 HDC hdc = GetDC(hwndMain);
\r
3525 HBITMAP whiteLogo = (HBITMAP) first.programLogo, blackLogo = (HBITMAP) second.programLogo;
\r
3526 if(appData.autoLogo) {
\r
3528 switch(gameMode) { // pick logos based on game mode
\r
3529 case IcsObserving:
\r
3530 whiteLogo = second.programLogo; // ICS logo
\r
3531 blackLogo = second.programLogo;
\r
3534 case IcsPlayingWhite:
\r
3535 if(!appData.zippyPlay) whiteLogo = userLogo;
\r
3536 blackLogo = second.programLogo; // ICS logo
\r
3538 case IcsPlayingBlack:
\r
3539 whiteLogo = second.programLogo; // ICS logo
\r
3540 blackLogo = appData.zippyPlay ? first.programLogo : userLogo;
\r
3542 case TwoMachinesPlay:
\r
3543 if(first.twoMachinesColor[0] == 'b') {
\r
3544 whiteLogo = second.programLogo;
\r
3545 blackLogo = first.programLogo;
\r
3548 case MachinePlaysWhite:
\r
3549 blackLogo = userLogo;
\r
3551 case MachinePlaysBlack:
\r
3552 whiteLogo = userLogo;
\r
3553 blackLogo = first.programLogo;
\r
3556 DrawLogoOnDC(hdc, leftLogoRect, flipClock ? blackLogo : whiteLogo);
\r
3557 DrawLogoOnDC(hdc, rightLogoRect, flipClock ? whiteLogo : blackLogo);
\r
3558 ReleaseDC(hwndMain, hdc);
\r
3563 UpdateLogos(int display)
\r
3564 { // called after loading new engine(s), in tourney or from menu
\r
3565 LoadLogo(&first, 0, FALSE);
\r
3566 LoadLogo(&second, 1, appData.icsActive);
\r
3567 InitDrawingSizes(-2, 0); // adapt layout of board window to presence/absence of logos
\r
3568 if(display) DisplayLogos();
\r
3571 static HDC hdcSeek;
\r
3573 // [HGM] seekgraph
\r
3574 void DrawSeekAxis( int x, int y, int xTo, int yTo )
\r
3577 HPEN hp = SelectObject( hdcSeek, gridPen );
\r
3578 MoveToEx( hdcSeek, boardRect.left+x, boardRect.top+y, &stPt );
\r
3579 LineTo( hdcSeek, boardRect.left+xTo, boardRect.top+yTo );
\r
3580 SelectObject( hdcSeek, hp );
\r
3583 // front-end wrapper for drawing functions to do rectangles
\r
3584 void DrawSeekBackground( int left, int top, int right, int bottom )
\r
3589 if (hdcSeek == NULL) {
\r
3590 hdcSeek = GetDC(hwndMain);
\r
3591 if (!appData.monoMode) {
\r
3592 SelectPalette(hdcSeek, hPal, FALSE);
\r
3593 RealizePalette(hdcSeek);
\r
3596 hp = SelectObject( hdcSeek, gridPen );
\r
3597 rc.top = boardRect.top+top; rc.left = boardRect.left+left;
\r
3598 rc.bottom = boardRect.top+bottom; rc.right = boardRect.left+right;
\r
3599 FillRect( hdcSeek, &rc, lightSquareBrush );
\r
3600 SelectObject( hdcSeek, hp );
\r
3603 // front-end wrapper for putting text in graph
\r
3604 void DrawSeekText(char *buf, int x, int y)
\r
3607 SetBkMode( hdcSeek, TRANSPARENT );
\r
3608 GetTextExtentPoint32( hdcSeek, buf, strlen(buf), &stSize );
\r
3609 TextOut( hdcSeek, boardRect.left+x-3, boardRect.top+y-stSize.cy/2, buf, strlen(buf) );
\r
3612 void DrawSeekDot(int x, int y, int color)
\r
3614 int square = color & 0x80;
\r
3615 HBRUSH oldBrush = SelectObject(hdcSeek,
\r
3616 color == 0 ? markerBrush : color == 1 ? darkSquareBrush : explodeBrush);
\r
3619 Rectangle(hdcSeek, boardRect.left+x - squareSize/9, boardRect.top+y - squareSize/9,
\r
3620 boardRect.left+x + squareSize/9, boardRect.top+y + squareSize/9);
\r
3622 Ellipse(hdcSeek, boardRect.left+x - squareSize/8, boardRect.top+y - squareSize/8,
\r
3623 boardRect.left+x + squareSize/8, boardRect.top+y + squareSize/8);
\r
3624 SelectObject(hdcSeek, oldBrush);
\r
3627 void DrawSeekOpen()
\r
3631 void DrawSeekClose()
\r
3636 HDCDrawPosition(HDC hdc, BOOLEAN repaint, Board board)
\r
3638 static Board lastReq[2], lastDrawn[2];
\r
3639 static HighlightInfo lastDrawnHighlight, lastDrawnPremove;
\r
3640 static int lastDrawnFlipView = 0;
\r
3641 static int lastReqValid[2] = {0, 0}, lastDrawnValid[2] = {0, 0};
\r
3642 int releaseDC, x, y, x2, y2, row, column, num_clips = 0, i;
\r
3645 HBITMAP bufferBitmap;
\r
3646 HBITMAP oldBitmap;
\r
3648 HRGN clips[MAX_CLIPS];
\r
3649 ChessSquare dragged_piece = EmptySquare;
\r
3650 int nr = twoBoards*partnerUp;
\r
3652 /* I'm undecided on this - this function figures out whether a full
\r
3653 * repaint is necessary on its own, so there's no real reason to have the
\r
3654 * caller tell it that. I think this can safely be set to FALSE - but
\r
3655 * if we trust the callers not to request full repaints unnessesarily, then
\r
3656 * we could skip some clipping work. In other words, only request a full
\r
3657 * redraw when the majority of pieces have changed positions (ie. flip,
\r
3658 * gamestart and similar) --Hawk
\r
3660 Boolean fullrepaint = repaint;
\r
3662 if(DrawSeekGraph()) return; // [HG} seekgraph: suppress printing board if seek graph up
\r
3664 if( DrawPositionNeedsFullRepaint() ) {
\r
3665 fullrepaint = TRUE;
\r
3668 if (board == NULL) {
\r
3669 if (!lastReqValid[nr]) {
\r
3672 board = lastReq[nr];
\r
3674 CopyBoard(lastReq[nr], board);
\r
3675 lastReqValid[nr] = 1;
\r
3678 if (doingSizing) {
\r
3682 if (IsIconic(hwndMain)) {
\r
3686 if (hdc == NULL) {
\r
3687 hdc = GetDC(hwndMain);
\r
3688 if (!appData.monoMode) {
\r
3689 SelectPalette(hdc, hPal, FALSE);
\r
3690 RealizePalette(hdc);
\r
3694 releaseDC = FALSE;
\r
3697 /* Create some work-DCs */
\r
3698 hdcmem = CreateCompatibleDC(hdc);
\r
3699 tmphdc = CreateCompatibleDC(hdc);
\r
3701 /* If dragging is in progress, we temporarely remove the piece */
\r
3702 /* [HGM] or temporarily decrease count if stacked */
\r
3703 /* !! Moved to before board compare !! */
\r
3704 if (dragInfo.from.x >= 0 && dragInfo.pos.x >= 0) {
\r
3705 dragged_piece = board[dragInfo.from.y][dragInfo.from.x];
\r
3706 if(dragInfo.from.x == BOARD_LEFT-2 ) {
\r
3707 if(--board[dragInfo.from.y][dragInfo.from.x+1] == 0 )
\r
3708 board[dragInfo.from.y][dragInfo.from.x] = EmptySquare;
\r
3710 if(dragInfo.from.x == BOARD_RGHT+1) {
\r
3711 if(--board[dragInfo.from.y][dragInfo.from.x-1] == 0 )
\r
3712 board[dragInfo.from.y][dragInfo.from.x] = EmptySquare;
\r
3714 board[dragInfo.from.y][dragInfo.from.x] = gatingPiece;
\r
3717 /* Figure out which squares need updating by comparing the
\r
3718 * newest board with the last drawn board and checking if
\r
3719 * flipping has changed.
\r
3721 if (!fullrepaint && lastDrawnValid[nr] && (nr == 1 || lastDrawnFlipView == flipView)) {
\r
3722 for (row = 0; row < BOARD_HEIGHT; row++) { /* [HGM] true size, not 8 */
\r
3723 for (column = 0; column < BOARD_WIDTH; column++) {
\r
3724 if (lastDrawn[nr][row][column] != board[row][column]) {
\r
3725 SquareToPos(row, column, &x, &y);
\r
3726 clips[num_clips++] =
\r
3727 CreateRectRgn(x, y, x + squareSize, y + squareSize);
\r
3731 if(nr == 0) { // [HGM] dual: no highlights on second board
\r
3732 for (i=0; i<2; i++) {
\r
3733 if (lastDrawnHighlight.sq[i].x != highlightInfo.sq[i].x ||
\r
3734 lastDrawnHighlight.sq[i].y != highlightInfo.sq[i].y) {
\r
3735 if (lastDrawnHighlight.sq[i].x >= 0 &&
\r
3736 lastDrawnHighlight.sq[i].y >= 0) {
\r
3737 SquareToPos(lastDrawnHighlight.sq[i].y,
\r
3738 lastDrawnHighlight.sq[i].x, &x, &y);
\r
3739 clips[num_clips++] =
\r
3740 CreateRectRgn(x - lineGap, y - lineGap,
\r
3741 x + squareSize + lineGap, y + squareSize + lineGap);
\r
3743 if (highlightInfo.sq[i].x >= 0 && highlightInfo.sq[i].y >= 0) {
\r
3744 SquareToPos(highlightInfo.sq[i].y, highlightInfo.sq[i].x, &x, &y);
\r
3745 clips[num_clips++] =
\r
3746 CreateRectRgn(x - lineGap, y - lineGap,
\r
3747 x + squareSize + lineGap, y + squareSize + lineGap);
\r
3751 for (i=0; i<2; i++) {
\r
3752 if (lastDrawnPremove.sq[i].x != premoveHighlightInfo.sq[i].x ||
\r
3753 lastDrawnPremove.sq[i].y != premoveHighlightInfo.sq[i].y) {
\r
3754 if (lastDrawnPremove.sq[i].x >= 0 &&
\r
3755 lastDrawnPremove.sq[i].y >= 0) {
\r
3756 SquareToPos(lastDrawnPremove.sq[i].y,
\r
3757 lastDrawnPremove.sq[i].x, &x, &y);
\r
3758 clips[num_clips++] =
\r
3759 CreateRectRgn(x - lineGap, y - lineGap,
\r
3760 x + squareSize + lineGap, y + squareSize + lineGap);
\r
3762 if (premoveHighlightInfo.sq[i].x >= 0 &&
\r
3763 premoveHighlightInfo.sq[i].y >= 0) {
\r
3764 SquareToPos(premoveHighlightInfo.sq[i].y,
\r
3765 premoveHighlightInfo.sq[i].x, &x, &y);
\r
3766 clips[num_clips++] =
\r
3767 CreateRectRgn(x - lineGap, y - lineGap,
\r
3768 x + squareSize + lineGap, y + squareSize + lineGap);
\r
3772 } else { // nr == 1
\r
3773 partnerHighlightInfo.sq[0].y = board[EP_STATUS-4];
\r
3774 partnerHighlightInfo.sq[0].x = board[EP_STATUS-3];
\r
3775 partnerHighlightInfo.sq[1].y = board[EP_STATUS-2];
\r
3776 partnerHighlightInfo.sq[1].x = board[EP_STATUS-1];
\r
3777 for (i=0; i<2; i++) {
\r
3778 if (partnerHighlightInfo.sq[i].x >= 0 &&
\r
3779 partnerHighlightInfo.sq[i].y >= 0) {
\r
3780 SquareToPos(partnerHighlightInfo.sq[i].y,
\r
3781 partnerHighlightInfo.sq[i].x, &x, &y);
\r
3782 clips[num_clips++] =
\r
3783 CreateRectRgn(x - lineGap, y - lineGap,
\r
3784 x + squareSize + lineGap, y + squareSize + lineGap);
\r
3786 if (oldPartnerHighlight.sq[i].x >= 0 &&
\r
3787 oldPartnerHighlight.sq[i].y >= 0) {
\r
3788 SquareToPos(oldPartnerHighlight.sq[i].y,
\r
3789 oldPartnerHighlight.sq[i].x, &x, &y);
\r
3790 clips[num_clips++] =
\r
3791 CreateRectRgn(x - lineGap, y - lineGap,
\r
3792 x + squareSize + lineGap, y + squareSize + lineGap);
\r
3797 fullrepaint = TRUE;
\r
3800 /* Create a buffer bitmap - this is the actual bitmap
\r
3801 * being written to. When all the work is done, we can
\r
3802 * copy it to the real DC (the screen). This avoids
\r
3803 * the problems with flickering.
\r
3805 GetClientRect(hwndMain, &Rect);
\r
3806 bufferBitmap = CreateCompatibleBitmap(hdc, Rect.right-Rect.left+1,
\r
3807 Rect.bottom-Rect.top+1);
\r
3808 oldBitmap = SelectObject(hdcmem, bufferBitmap);
\r
3809 if (!appData.monoMode) {
\r
3810 SelectPalette(hdcmem, hPal, FALSE);
\r
3813 /* Create clips for dragging */
\r
3814 if (!fullrepaint) {
\r
3815 if (dragInfo.from.x >= 0) {
\r
3816 SquareToPos(dragInfo.from.y, dragInfo.from.x, &x, &y);
\r
3817 clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);
\r
3819 if (dragInfo.start.x >= 0) {
\r
3820 SquareToPos(dragInfo.start.y, dragInfo.start.x, &x, &y);
\r
3821 clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);
\r
3823 if (dragInfo.pos.x >= 0) {
\r
3824 x = dragInfo.pos.x - squareSize / 2;
\r
3825 y = dragInfo.pos.y - squareSize / 2;
\r
3826 clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);
\r
3828 if (dragInfo.lastpos.x >= 0) {
\r
3829 x = dragInfo.lastpos.x - squareSize / 2;
\r
3830 y = dragInfo.lastpos.y - squareSize / 2;
\r
3831 clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);
\r
3835 /* Are we animating a move?
\r
3837 * - remove the piece from the board (temporarely)
\r
3838 * - calculate the clipping region
\r
3840 if (!fullrepaint) {
\r
3841 if (animInfo.piece != EmptySquare) {
\r
3842 board[animInfo.from.y][animInfo.from.x] = EmptySquare;
\r
3843 x = boardRect.left + animInfo.lastpos.x;
\r
3844 y = boardRect.top + animInfo.lastpos.y;
\r
3845 x2 = boardRect.left + animInfo.pos.x;
\r
3846 y2 = boardRect.top + animInfo.pos.y;
\r
3847 clips[num_clips++] = CreateRectRgn(MIN(x,x2), MIN(y,y2), MAX(x,x2)+squareSize, MAX(y,y2)+squareSize);
\r
3848 /* Slight kludge. The real problem is that after AnimateMove is
\r
3849 done, the position on the screen does not match lastDrawn.
\r
3850 This currently causes trouble only on e.p. captures in
\r
3851 atomic, where the piece moves to an empty square and then
\r
3852 explodes. The old and new positions both had an empty square
\r
3853 at the destination, but animation has drawn a piece there and
\r
3854 we have to remember to erase it. [HGM] moved until after setting lastDrawn */
\r
3855 lastDrawn[0][animInfo.to.y][animInfo.to.x] = animInfo.piece;
\r
3859 /* No clips? Make sure we have fullrepaint set to TRUE */
\r
3860 if (num_clips == 0)
\r
3861 fullrepaint = TRUE;
\r
3863 /* Set clipping on the memory DC */
\r
3864 if (!fullrepaint) {
\r
3865 SelectClipRgn(hdcmem, clips[0]);
\r
3866 for (x = 1; x < num_clips; x++) {
\r
3867 if (ExtSelectClipRgn(hdcmem, clips[x], RGN_OR) == ERROR)
\r
3868 abort(); // this should never ever happen!
\r
3872 /* Do all the drawing to the memory DC */
\r
3873 if(explodeInfo.radius) { // [HGM] atomic
\r
3875 int x, y, r=(explodeInfo.radius * squareSize)/100;
\r
3876 ChessSquare piece = board[explodeInfo.fromY][explodeInfo.fromX];
\r
3877 board[explodeInfo.fromY][explodeInfo.fromX] = EmptySquare; // suppress display of capturer
\r
3878 SquareToPos(explodeInfo.toY, explodeInfo.toX, &x, &y);
\r
3879 x += squareSize/2;
\r
3880 y += squareSize/2;
\r
3881 if(!fullrepaint) {
\r
3882 clips[num_clips] = CreateRectRgn(x-r, y-r, x+r, y+r);
\r
3883 ExtSelectClipRgn(hdcmem, clips[num_clips++], RGN_OR);
\r
3885 DrawGridOnDC(hdcmem);
\r
3886 DrawHighlightsOnDC(hdcmem, &highlightInfo, HIGHLIGHT_PEN);
\r
3887 DrawHighlightsOnDC(hdcmem, &premoveHighlightInfo, PREMOVE_PEN);
\r
3888 DrawBoardOnDC(hdcmem, board, tmphdc);
\r
3889 board[explodeInfo.fromY][explodeInfo.fromX] = piece;
\r
3890 oldBrush = SelectObject(hdcmem, explodeBrush);
\r
3891 Ellipse(hdcmem, x-r, y-r, x+r, y+r);
\r
3892 SelectObject(hdcmem, oldBrush);
\r
3894 if(border) DrawBackgroundOnDC(hdcmem);
\r
3895 DrawGridOnDC(hdcmem);
\r
3896 if(nr == 0) { // [HGM] dual: decide which highlights to draw
\r
3897 DrawHighlightsOnDC(hdcmem, &highlightInfo, HIGHLIGHT_PEN);
\r
3898 DrawHighlightsOnDC(hdcmem, &premoveHighlightInfo, PREMOVE_PEN);
\r
3900 DrawHighlightsOnDC(hdcmem, &partnerHighlightInfo, HIGHLIGHT_PEN);
\r
3901 oldPartnerHighlight = partnerHighlightInfo;
\r
3903 DrawBoardOnDC(hdcmem, board, tmphdc);
\r
3905 if(nr == 0) // [HGM] dual: markers only on left board
\r
3906 for (row = 0; row < BOARD_HEIGHT; row++) {
\r
3907 for (column = 0; column < BOARD_WIDTH; column++) {
\r
3908 if (marker[row][column]) { // marker changes only occur with full repaint!
\r
3909 HBRUSH oldBrush = SelectObject(hdcmem,
\r
3910 marker[row][column] == 2 ? markerBrush : explodeBrush);
\r
3911 SquareToPos(row, column, &x, &y);
\r
3912 Ellipse(hdcmem, x + squareSize/4, y + squareSize/4,
\r
3913 x + 3*squareSize/4, y + 3*squareSize/4);
\r
3914 SelectObject(hdcmem, oldBrush);
\r
3919 if( appData.highlightMoveWithArrow ) {
\r
3920 DrawArrowHighlight(hdcmem);
\r
3923 DrawCoordsOnDC(hdcmem);
\r
3925 CopyBoard(lastDrawn[nr], board); /* [HGM] Moved to here from end of routine, */
\r
3926 /* to make sure lastDrawn contains what is actually drawn */
\r
3928 /* Put the dragged piece back into place and draw it (out of place!) */
\r
3929 if (dragged_piece != EmptySquare) {
\r
3930 /* [HGM] or restack */
\r
3931 if(dragInfo.from.x == BOARD_LEFT-2 )
\r
3932 board[dragInfo.from.y][dragInfo.from.x+1]++;
\r
3934 if(dragInfo.from.x == BOARD_RGHT+1 )
\r
3935 board[dragInfo.from.y][dragInfo.from.x-1]++;
\r
3936 board[dragInfo.from.y][dragInfo.from.x] = dragged_piece;
\r
3937 x = dragInfo.pos.x - squareSize / 2;
\r
3938 y = dragInfo.pos.y - squareSize / 2;
\r
3939 DrawPieceOnDC(hdcmem, dragInfo.piece,
\r
3940 ((int) dragInfo.piece < (int) BlackPawn),
\r
3941 (dragInfo.from.y + dragInfo.from.x) % 2, x, y, tmphdc);
\r
3944 /* Put the animated piece back into place and draw it */
\r
3945 if (animInfo.piece != EmptySquare) {
\r
3946 board[animInfo.from.y][animInfo.from.x] = animInfo.piece;
\r
3947 x = boardRect.left + animInfo.pos.x;
\r
3948 y = boardRect.top + animInfo.pos.y;
\r
3949 DrawPieceOnDC(hdcmem, animInfo.piece,
\r
3950 ((int) animInfo.piece < (int) BlackPawn),
\r
3951 (animInfo.from.y + animInfo.from.x) % 2, x, y, tmphdc);
\r
3954 /* Release the bufferBitmap by selecting in the old bitmap
\r
3955 * and delete the memory DC
\r
3957 SelectObject(hdcmem, oldBitmap);
\r
3960 /* Set clipping on the target DC */
\r
3961 if (!fullrepaint) {
\r
3962 if(nr == 1) for (x = 0; x < num_clips; x++) { // [HGM] dual: translate clips
\r
3964 GetRgnBox(clips[x], &rect);
\r
3965 DeleteObject(clips[x]);
\r
3966 clips[x] = CreateRectRgn(rect.left + wpMain.width/2, rect.top,
\r
3967 rect.right + wpMain.width/2, rect.bottom);
\r
3969 SelectClipRgn(hdc, clips[0]);
\r
3970 for (x = 1; x < num_clips; x++) {
\r
3971 if (ExtSelectClipRgn(hdc, clips[x], RGN_OR) == ERROR)
\r
3972 abort(); // this should never ever happen!
\r
3976 /* Copy the new bitmap onto the screen in one go.
\r
3977 * This way we avoid any flickering
\r
3979 oldBitmap = SelectObject(tmphdc, bufferBitmap);
\r
3980 BitBlt(hdc, boardRect.left + twoBoards*partnerUp*wpMain.width/2, boardRect.top, // [HGM] dual
\r
3981 boardRect.right - boardRect.left,
\r
3982 boardRect.bottom - boardRect.top,
\r
3983 tmphdc, boardRect.left, boardRect.top, SRCCOPY);
\r
3984 if(saveDiagFlag) {
\r
3985 BITMAP b; int i, j=0, m, w, wb, fac=0; char *pData;
\r
3986 BITMAPINFOHEADER bih; int color[16], nrColors=0;
\r
3988 GetObject(bufferBitmap, sizeof(b), &b);
\r
3989 if(pData = malloc(b.bmWidthBytes*b.bmHeight + 10000)) {
\r
3990 bih.biSize = sizeof(BITMAPINFOHEADER);
\r
3991 bih.biWidth = b.bmWidth;
\r
3992 bih.biHeight = b.bmHeight;
\r
3994 bih.biBitCount = b.bmBitsPixel;
\r
3995 bih.biCompression = 0;
\r
3996 bih.biSizeImage = b.bmWidthBytes*b.bmHeight;
\r
3997 bih.biXPelsPerMeter = 0;
\r
3998 bih.biYPelsPerMeter = 0;
\r
3999 bih.biClrUsed = 0;
\r
4000 bih.biClrImportant = 0;
\r
4001 // fprintf(diagFile, "t=%d\nw=%d\nh=%d\nB=%d\nP=%d\nX=%d\n",
\r
4002 // b.bmType, b.bmWidth, b.bmHeight, b.bmWidthBytes, b.bmPlanes, b.bmBitsPixel);
\r
4003 GetDIBits(tmphdc,bufferBitmap,0,b.bmHeight,pData,(BITMAPINFO*)&bih,DIB_RGB_COLORS);
\r
4004 // fprintf(diagFile, "%8x\n", (int) pData);
\r
4006 wb = b.bmWidthBytes;
\r
4008 for(i=0; i<wb*(b.bmHeight - boardRect.top + OUTER_MARGIN)>>2; i++) {
\r
4009 int k = ((int*) pData)[i];
\r
4010 for(j=0; j<nrColors; j++) if(color[j] == k) break;
\r
4011 if(j >= 16) break;
\r
4013 if(j >= nrColors) nrColors = j+1;
\r
4015 if(j<16) { // 16 colors is enough. Compress to 4 bits per pixel
\r
4017 for(i=0; i<b.bmHeight - boardRect.top + OUTER_MARGIN; i++) {
\r
4018 for(w=0; w<(wb>>2); w+=2) {
\r
4019 int k = ((int*) pData)[(wb*i>>2) + w];
\r
4020 for(j=0; j<nrColors; j++) if(color[j] == k) break;
\r
4021 k = ((int*) pData)[(wb*i>>2) + w + 1];
\r
4022 for(m=0; m<nrColors; m++) if(color[m] == k) break;
\r
4023 pData[p++] = m | j<<4;
\r
4025 while(p&3) pData[p++] = 0;
\r
4028 wb = ((wb+31)>>5)<<2;
\r
4030 // write BITMAPFILEHEADER
\r
4031 fprintf(diagFile, "BM");
\r
4032 fputDW(diagFile, wb*(b.bmHeight - boardRect.top + OUTER_MARGIN)+0x36 + (fac?64:0));
\r
4033 fputDW(diagFile, 0);
\r
4034 fputDW(diagFile, 0x36 + (fac?64:0));
\r
4035 // write BITMAPINFOHEADER
\r
4036 fputDW(diagFile, 40);
\r
4037 fputDW(diagFile, b.bmWidth);
\r
4038 fputDW(diagFile, b.bmHeight - boardRect.top + OUTER_MARGIN);
\r
4039 if(fac) fputDW(diagFile, 0x040001); // planes and bits/pixel
\r
4040 else fputDW(diagFile, 0x200001); // planes and bits/pixel
\r
4041 fputDW(diagFile, 0);
\r
4042 fputDW(diagFile, 0);
\r
4043 fputDW(diagFile, 0);
\r
4044 fputDW(diagFile, 0);
\r
4045 fputDW(diagFile, 0);
\r
4046 fputDW(diagFile, 0);
\r
4047 // write color table
\r
4049 for(i=0; i<16; i++) fputDW(diagFile, color[i]);
\r
4050 // write bitmap data
\r
4051 for(i=0; i<wb*(b.bmHeight - boardRect.top + OUTER_MARGIN); i++)
\r
4052 fputc(pData[i], diagFile);
\r
4057 SelectObject(tmphdc, oldBitmap);
\r
4059 /* Massive cleanup */
\r
4060 for (x = 0; x < num_clips; x++)
\r
4061 DeleteObject(clips[x]);
\r
4064 DeleteObject(bufferBitmap);
\r
4067 ReleaseDC(hwndMain, hdc);
\r
4069 if (lastDrawnFlipView != flipView && nr == 0) {
\r
4071 CheckMenuItem(GetMenu(hwndMain),IDM_FlipView, MF_BYCOMMAND|MF_CHECKED);
\r
4073 CheckMenuItem(GetMenu(hwndMain),IDM_FlipView, MF_BYCOMMAND|MF_UNCHECKED);
\r
4076 /* CopyBoard(lastDrawn, board);*/
\r
4077 lastDrawnHighlight = highlightInfo;
\r
4078 lastDrawnPremove = premoveHighlightInfo;
\r
4079 lastDrawnFlipView = flipView;
\r
4080 lastDrawnValid[nr] = 1;
\r
4083 /* [HGM] diag: Save the current board display to the given open file and close the file */
\r
4088 saveDiagFlag = 1; diagFile = f;
\r
4089 HDCDrawPosition(NULL, TRUE, NULL);
\r
4097 /*---------------------------------------------------------------------------*\
\r
4098 | CLIENT PAINT PROCEDURE
\r
4099 | This is the main event-handler for the WM_PAINT message.
\r
4101 \*---------------------------------------------------------------------------*/
\r
4103 PaintProc(HWND hwnd)
\r
4109 if((hdc = BeginPaint(hwnd, &ps))) {
\r
4110 if (IsIconic(hwnd)) {
\r
4111 DrawIcon(hdc, 2, 2, iconCurrent);
\r
4113 if (!appData.monoMode) {
\r
4114 SelectPalette(hdc, hPal, FALSE);
\r
4115 RealizePalette(hdc);
\r
4117 HDCDrawPosition(hdc, 1, NULL);
\r
4118 if(twoBoards) { // [HGM] dual: also redraw other board in other orientation
\r
4119 flipView = !flipView; partnerUp = !partnerUp;
\r
4120 HDCDrawPosition(hdc, 1, NULL);
\r
4121 flipView = !flipView; partnerUp = !partnerUp;
\r
4124 SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);
\r
4125 ExtTextOut(hdc, messageRect.left, messageRect.top,
\r
4126 ETO_CLIPPED|ETO_OPAQUE,
\r
4127 &messageRect, messageText, strlen(messageText), NULL);
\r
4128 SelectObject(hdc, oldFont);
\r
4129 DisplayBothClocks();
\r
4132 EndPaint(hwnd,&ps);
\r
4140 * If the user selects on a border boundary, return -1; if off the board,
\r
4141 * return -2. Otherwise map the event coordinate to the square.
\r
4142 * The offset boardRect.left or boardRect.top must already have been
\r
4143 * subtracted from x.
\r
4145 int EventToSquare(x, limit)
\r
4150 if (x < lineGap + border)
\r
4152 x -= lineGap + border;
\r
4153 if ((x % (squareSize + lineGap)) >= squareSize)
\r
4155 x /= (squareSize + lineGap);
\r
4167 DropEnable dropEnables[] = {
\r
4168 { 'P', DP_Pawn, N_("Pawn") },
\r
4169 { 'N', DP_Knight, N_("Knight") },
\r
4170 { 'B', DP_Bishop, N_("Bishop") },
\r
4171 { 'R', DP_Rook, N_("Rook") },
\r
4172 { 'Q', DP_Queen, N_("Queen") },
\r
4176 SetupDropMenu(HMENU hmenu)
\r
4178 int i, count, enable;
\r
4180 extern char white_holding[], black_holding[];
\r
4181 char item[MSG_SIZ];
\r
4183 for (i=0; i<sizeof(dropEnables)/sizeof(DropEnable); i++) {
\r
4184 p = strchr(gameMode == IcsPlayingWhite ? white_holding : black_holding,
\r
4185 dropEnables[i].piece);
\r
4187 while (p && *p++ == dropEnables[i].piece) count++;
\r
4188 snprintf(item, MSG_SIZ, "%s %d", T_(dropEnables[i].name), count);
\r
4189 enable = count > 0 || !appData.testLegality
\r
4190 /*!!temp:*/ || (gameInfo.variant == VariantCrazyhouse
\r
4191 && !appData.icsActive);
\r
4192 ModifyMenu(hmenu, dropEnables[i].command,
\r
4193 MF_BYCOMMAND | (enable ? MF_ENABLED : MF_GRAYED) | MF_STRING,
\r
4194 dropEnables[i].command, item);
\r
4198 void DragPieceBegin(int x, int y, Boolean instantly)
\r
4200 dragInfo.lastpos.x = boardRect.left + x;
\r
4201 dragInfo.lastpos.y = boardRect.top + y;
\r
4202 if(instantly) dragInfo.pos = dragInfo.lastpos;
\r
4203 dragInfo.from.x = fromX;
\r
4204 dragInfo.from.y = fromY;
\r
4205 dragInfo.piece = boards[currentMove][fromY][fromX];
\r
4206 dragInfo.start = dragInfo.from;
\r
4207 SetCapture(hwndMain);
\r
4210 void DragPieceEnd(int x, int y)
\r
4213 dragInfo.start.x = dragInfo.start.y = -1;
\r
4214 dragInfo.from = dragInfo.start;
\r
4215 dragInfo.pos = dragInfo.lastpos = dragInfo.start;
\r
4218 void ChangeDragPiece(ChessSquare piece)
\r
4220 dragInfo.piece = piece;
\r
4223 /* Event handler for mouse messages */
\r
4225 MouseEvent(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
\r
4229 static int recursive = 0;
\r
4231 BOOLEAN forceFullRepaint = IsFullRepaintPreferrable(); /* [AS] */
\r
4234 if (message == WM_MBUTTONUP) {
\r
4235 /* Hideous kludge to fool TrackPopupMenu into paying attention
\r
4236 to the middle button: we simulate pressing the left button too!
\r
4238 PostMessage(hwnd, WM_LBUTTONDOWN, wParam, lParam);
\r
4239 PostMessage(hwnd, WM_LBUTTONUP, wParam, lParam);
\r
4245 pt.x = LOWORD(lParam);
\r
4246 pt.y = HIWORD(lParam);
\r
4247 x = EventToSquare(pt.x - boardRect.left, BOARD_WIDTH);
\r
4248 y = EventToSquare(pt.y - boardRect.top, BOARD_HEIGHT);
\r
4249 if (!flipView && y >= 0) {
\r
4250 y = BOARD_HEIGHT - 1 - y;
\r
4252 if (flipView && x >= 0) {
\r
4253 x = BOARD_WIDTH - 1 - x;
\r
4256 shiftKey = GetKeyState(VK_SHIFT) < 0; // [HGM] remember last shift status
\r
4258 switch (message) {
\r
4259 case WM_LBUTTONDOWN:
\r
4260 if (PtInRect((LPRECT) &whiteRect, pt)) {
\r
4261 ClockClick(flipClock); break;
\r
4262 } else if (PtInRect((LPRECT) &blackRect, pt)) {
\r
4263 ClockClick(!flipClock); break;
\r
4265 dragInfo.start.x = dragInfo.start.y = -1;
\r
4266 dragInfo.from = dragInfo.start;
\r
4267 if(fromX == -1 && frozen) { // not sure where this is for
\r
4268 fromX = fromY = -1;
\r
4269 DrawPosition(forceFullRepaint || FALSE, NULL); /* [AS] */
\r
4272 LeftClick(Press, pt.x - boardRect.left, pt.y - boardRect.top);
\r
4273 DrawPosition(TRUE, NULL);
\r
4276 case WM_LBUTTONUP:
\r
4277 LeftClick(Release, pt.x - boardRect.left, pt.y - boardRect.top);
\r
4278 DrawPosition(TRUE, NULL);
\r
4281 case WM_MOUSEMOVE:
\r
4282 if(SeekGraphClick(Press, pt.x - boardRect.left, pt.y - boardRect.top, 1)) break;
\r
4283 if(PromoScroll(pt.x - boardRect.left, pt.y - boardRect.top)) break;
\r
4284 MovePV(pt.x - boardRect.left, pt.y - boardRect.top, boardRect.bottom - boardRect.top);
\r
4285 if ((appData.animateDragging || appData.highlightDragging)
\r
4286 && (wParam & MK_LBUTTON)
\r
4287 && dragInfo.from.x >= 0)
\r
4289 BOOL full_repaint = FALSE;
\r
4291 if (appData.animateDragging) {
\r
4292 dragInfo.pos = pt;
\r
4294 if (appData.highlightDragging) {
\r
4295 SetHighlights(fromX, fromY, x, y);
\r
4296 if( IsDrawArrowEnabled() && (x < 0 || x >= BOARD_WIDTH || y < 0 || y >= BOARD_HEIGHT) ) {
\r
4297 full_repaint = TRUE;
\r
4301 DrawPosition( full_repaint, NULL);
\r
4303 dragInfo.lastpos = dragInfo.pos;
\r
4307 case WM_MOUSEWHEEL: // [DM]
\r
4308 { static int lastDir = 0; // [HGM] build in some hysteresis to avoid spurious events
\r
4309 /* Mouse Wheel is being rolled forward
\r
4310 * Play moves forward
\r
4312 if((short)HIWORD(wParam) < 0 && currentMove < forwardMostMove)
\r
4313 { if(lastDir == 1) ForwardEvent(); else lastDir = 1; } // [HGM] suppress first event in direction
\r
4314 /* Mouse Wheel is being rolled backward
\r
4315 * Play moves backward
\r
4317 if((short)HIWORD(wParam) > 0 && currentMove > backwardMostMove)
\r
4318 { if(lastDir == -1) BackwardEvent(); else lastDir = -1; }
\r
4322 case WM_MBUTTONUP:
\r
4323 case WM_RBUTTONUP:
\r
4325 RightClick(Release, pt.x - boardRect.left, pt.y - boardRect.top, &fromX, &fromY);
\r
4328 case WM_MBUTTONDOWN:
\r
4329 case WM_RBUTTONDOWN:
\r
4332 fromX = fromY = -1;
\r
4333 dragInfo.pos.x = dragInfo.pos.y = -1;
\r
4334 dragInfo.start.x = dragInfo.start.y = -1;
\r
4335 dragInfo.from = dragInfo.start;
\r
4336 dragInfo.lastpos = dragInfo.pos;
\r
4337 if (appData.highlightDragging) {
\r
4338 ClearHighlights();
\r
4341 /* [HGM] right mouse button in clock area edit-game mode ups clock */
\r
4342 if (PtInRect((LPRECT) &whiteRect, pt)) {
\r
4343 if (GetKeyState(VK_SHIFT) < 0) AdjustClock(flipClock, 1);
\r
4344 } else if (PtInRect((LPRECT) &blackRect, pt)) {
\r
4345 if (GetKeyState(VK_SHIFT) < 0) AdjustClock(!flipClock, 1);
\r
4349 DrawPosition(TRUE, NULL);
\r
4351 menuNr = RightClick(Press, pt.x - boardRect.left, pt.y - boardRect.top, &fromX, &fromY);
\r
4354 if (message == WM_MBUTTONDOWN) {
\r
4355 buttonCount = 3; /* even if system didn't think so */
\r
4356 if (wParam & MK_SHIFT)
\r
4357 MenuPopup(hwnd, pt, LoadMenu(hInst, "BlackPieceMenu"), -1);
\r
4359 MenuPopup(hwnd, pt, LoadMenu(hInst, "WhitePieceMenu"), -1);
\r
4360 } else { /* message == WM_RBUTTONDOWN */
\r
4361 /* Just have one menu, on the right button. Windows users don't
\r
4362 think to try the middle one, and sometimes other software steals
\r
4363 it, or it doesn't really exist. */
\r
4364 if(gameInfo.variant != VariantShogi)
\r
4365 MenuPopup(hwnd, pt, LoadMenu(hInst, "PieceMenu"), -1);
\r
4367 MenuPopup(hwnd, pt, LoadMenu(hInst, "ShogiPieceMenu"), -1);
\r
4371 SetCapture(hwndMain);
\r
4374 hmenu = LoadMenu(hInst, "DropPieceMenu");
\r
4375 SetupDropMenu(hmenu);
\r
4376 MenuPopup(hwnd, pt, hmenu, -1);
\r
4386 /* Preprocess messages for buttons in main window */
\r
4388 ButtonProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
\r
4390 int id = GetWindowLongPtr(hwnd, GWLP_ID);
\r
4393 for (i=0; i<N_BUTTONS; i++) {
\r
4394 if (buttonDesc[i].id == id) break;
\r
4396 if (i == N_BUTTONS) return 0;
\r
4397 switch (message) {
\r
4402 dir = (wParam == VK_LEFT) ? -1 : 1;
\r
4403 SetFocus(buttonDesc[(i + dir + N_BUTTONS) % N_BUTTONS].hwnd);
\r
4410 SendMessage(hwndMain, WM_COMMAND, MAKEWPARAM(buttonDesc[i].id, 0), 0);
\r
4413 if (appData.icsActive && (isalpha((char)wParam) || wParam == '0')) {
\r
4414 // [HGM] movenum: only letters or leading zero should go to ICS input
\r
4415 HWND h = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
4416 if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);
\r
4418 SendMessage(h, WM_CHAR, wParam, lParam);
\r
4420 } else if (isalpha((char)wParam) || isdigit((char)wParam)){
\r
4421 TypeInEvent((char)wParam);
\r
4427 return CallWindowProc(buttonDesc[i].wndproc, hwnd, message, wParam, lParam);
\r
4430 /* Process messages for Promotion dialog box */
\r
4432 Promotion(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
4436 switch (message) {
\r
4437 case WM_INITDIALOG: /* message: initialize dialog box */
\r
4438 /* Center the dialog over the application window */
\r
4439 CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));
\r
4440 Translate(hDlg, DLG_PromotionKing);
\r
4441 ShowWindow(GetDlgItem(hDlg, PB_King),
\r
4442 (!appData.testLegality || gameInfo.variant == VariantSuicide ||
\r
4443 gameInfo.variant == VariantSpartan && !WhiteOnMove(currentMove) ||
\r
4444 gameInfo.variant == VariantGiveaway || gameInfo.variant == VariantSuper ) ?
\r
4445 SW_SHOW : SW_HIDE);
\r
4446 /* [HGM] Only allow C & A promotions if these pieces are defined */
\r
4447 ShowWindow(GetDlgItem(hDlg, PB_Archbishop),
\r
4448 ((PieceToChar(WhiteAngel) >= 'A' && WhiteOnMove(currentMove) &&
\r
4449 PieceToChar(WhiteAngel) != '~') ||
\r
4450 (PieceToChar(BlackAngel) >= 'A' && !WhiteOnMove(currentMove) &&
\r
4451 PieceToChar(BlackAngel) != '~') ) ?
\r
4452 SW_SHOW : SW_HIDE);
\r
4453 ShowWindow(GetDlgItem(hDlg, PB_Chancellor),
\r
4454 ((PieceToChar(WhiteMarshall) >= 'A' && WhiteOnMove(currentMove) &&
\r
4455 PieceToChar(WhiteMarshall) != '~') ||
\r
4456 (PieceToChar(BlackMarshall) >= 'A' && !WhiteOnMove(currentMove) &&
\r
4457 PieceToChar(BlackMarshall) != '~') ) ?
\r
4458 SW_SHOW : SW_HIDE);
\r
4459 /* [HGM] Hide B & R button in Shogi, use Q as promote, N as defer */
\r
4460 ShowWindow(GetDlgItem(hDlg, PB_Rook),
\r
4461 gameInfo.variant != VariantShogi ?
\r
4462 SW_SHOW : SW_HIDE);
\r
4463 ShowWindow(GetDlgItem(hDlg, PB_Bishop),
\r
4464 gameInfo.variant != VariantShogi ?
\r
4465 SW_SHOW : SW_HIDE);
\r
4466 if(gameInfo.variant == VariantShogi) {
\r
4467 SetDlgItemText(hDlg, PB_Queen, "YES");
\r
4468 SetDlgItemText(hDlg, PB_Knight, "NO");
\r
4469 SetWindowText(hDlg, "Promote?");
\r
4471 ShowWindow(GetDlgItem(hDlg, IDC_Centaur),
\r
4472 gameInfo.variant == VariantSuper ?
\r
4473 SW_SHOW : SW_HIDE);
\r
4476 case WM_COMMAND: /* message: received a command */
\r
4477 switch (LOWORD(wParam)) {
\r
4479 EndDialog(hDlg, TRUE); /* Exit the dialog */
\r
4480 ClearHighlights();
\r
4481 DrawPosition(FALSE, NULL);
\r
4484 promoChar = gameInfo.variant == VariantSuper ? PieceToChar(BlackSilver) : PieceToChar(BlackKing);
\r
4487 promoChar = gameInfo.variant == VariantShogi ? '+' : ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteQueen : BlackQueen));
\r
4490 promoChar = ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteRook : BlackRook));
\r
4491 if(gameInfo.variant == VariantSpartan && !WhiteOnMove(currentMove)) promoChar = PieceToChar(BlackDragon);
\r
4494 promoChar = ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteBishop : BlackBishop));
\r
4495 if(gameInfo.variant == VariantSpartan && !WhiteOnMove(currentMove)) promoChar = PieceToChar(BlackAlfil);
\r
4497 case PB_Chancellor:
\r
4498 promoChar = ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteMarshall : BlackMarshall));
\r
4500 case PB_Archbishop:
\r
4501 promoChar = ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteAngel : BlackAngel));
\r
4504 promoChar = gameInfo.variant == VariantShogi ? '=' : PieceToChar(WhiteOnMove(currentMove) ? WhiteKnight : BlackKnight);
\r
4509 if(promoChar == '.') return FALSE; // invalid piece chosen
\r
4510 EndDialog(hDlg, TRUE); /* Exit the dialog */
\r
4511 UserMoveEvent(fromX, fromY, toX, toY, promoChar);
\r
4512 fromX = fromY = -1;
\r
4513 if (!appData.highlightLastMove) {
\r
4514 ClearHighlights();
\r
4515 DrawPosition(FALSE, NULL);
\r
4522 /* Pop up promotion dialog */
\r
4524 PromotionPopup(HWND hwnd)
\r
4528 lpProc = MakeProcInstance((FARPROC)Promotion, hInst);
\r
4529 DialogBox(hInst, MAKEINTRESOURCE(DLG_PromotionKing),
\r
4530 hwnd, (DLGPROC)lpProc);
\r
4531 FreeProcInstance(lpProc);
\r
4537 DrawPosition(TRUE, NULL);
\r
4538 PromotionPopup(hwndMain);
\r
4542 LoadGameDialog(HWND hwnd, char* title)
\r
4546 char fileTitle[MSG_SIZ];
\r
4547 f = OpenFileDialog(hwnd, "rb", "",
\r
4548 appData.oldSaveStyle ? "gam" : "pgn",
\r
4550 title, &number, fileTitle, NULL);
\r
4552 cmailMsgLoaded = FALSE;
\r
4553 if (number == 0) {
\r
4554 int error = GameListBuild(f);
\r
4556 DisplayError(_("Cannot build game list"), error);
\r
4557 } else if (!ListEmpty(&gameList) &&
\r
4558 ((ListGame *) gameList.tailPred)->number > 1) {
\r
4559 GameListPopUp(f, fileTitle);
\r
4562 GameListDestroy();
\r
4565 LoadGame(f, number, fileTitle, FALSE);
\r
4569 int get_term_width()
\r
4574 HFONT hfont, hold_font;
\r
4579 hText = GetDlgItem(hwndConsole, OPT_ConsoleText);
\r
4583 // get the text metrics
\r
4584 hdc = GetDC(hText);
\r
4585 lf = font[boardSize][CONSOLE_FONT]->lf;
\r
4586 if (consoleCF.dwEffects & CFE_BOLD)
\r
4587 lf.lfWeight = FW_BOLD;
\r
4588 if (consoleCF.dwEffects & CFE_ITALIC)
\r
4589 lf.lfItalic = TRUE;
\r
4590 if (consoleCF.dwEffects & CFE_STRIKEOUT)
\r
4591 lf.lfStrikeOut = TRUE;
\r
4592 if (consoleCF.dwEffects & CFE_UNDERLINE)
\r
4593 lf.lfUnderline = TRUE;
\r
4594 hfont = CreateFontIndirect(&lf);
\r
4595 hold_font = SelectObject(hdc, hfont);
\r
4596 GetTextMetrics(hdc, &tm);
\r
4597 SelectObject(hdc, hold_font);
\r
4598 DeleteObject(hfont);
\r
4599 ReleaseDC(hText, hdc);
\r
4601 // get the rectangle
\r
4602 SendMessage(hText, EM_GETRECT, 0, (LPARAM)&rc);
\r
4604 return (rc.right-rc.left) / tm.tmAveCharWidth;
\r
4607 void UpdateICSWidth(HWND hText)
\r
4609 LONG old_width, new_width;
\r
4611 new_width = get_term_width(hText, FALSE);
\r
4612 old_width = GetWindowLongPtr(hText, GWLP_USERDATA);
\r
4613 if (new_width != old_width)
\r
4615 ics_update_width(new_width);
\r
4616 SetWindowLongPtr(hText, GWLP_USERDATA, new_width);
\r
4621 ChangedConsoleFont()
\r
4624 CHARRANGE tmpsel, sel;
\r
4625 MyFont *f = font[boardSize][CONSOLE_FONT];
\r
4626 HWND hText = GetDlgItem(hwndConsole, OPT_ConsoleText);
\r
4627 HWND hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
4630 cfmt.cbSize = sizeof(CHARFORMAT);
\r
4631 cfmt.dwMask = CFM_FACE|CFM_SIZE|CFM_CHARSET;
\r
4632 safeStrCpy(cfmt.szFaceName, font[boardSize][CONSOLE_FONT]->mfp.faceName,
\r
4633 sizeof(cfmt.szFaceName)/sizeof(cfmt.szFaceName[0]) );
\r
4634 /* yHeight is expressed in twips. A twip is 1/20 of a font's point
\r
4635 * size. This was undocumented in the version of MSVC++ that I had
\r
4636 * when I wrote the code, but is apparently documented now.
\r
4638 cfmt.yHeight = (int)(f->mfp.pointSize * 20.0 + 0.5);
\r
4639 cfmt.bCharSet = f->lf.lfCharSet;
\r
4640 cfmt.bPitchAndFamily = f->lf.lfPitchAndFamily;
\r
4641 SendMessage(hText, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &cfmt);
\r
4642 SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &cfmt);
\r
4643 /* Why are the following seemingly needed too? */
\r
4644 SendMessage(hText, EM_SETCHARFORMAT, SCF_DEFAULT, (LPARAM) &cfmt);
\r
4645 SendMessage(hInput, EM_SETCHARFORMAT, SCF_DEFAULT, (LPARAM) &cfmt);
\r
4646 SendMessage(hText, EM_EXGETSEL, 0, (LPARAM)&sel);
\r
4648 tmpsel.cpMax = -1; /*999999?*/
\r
4649 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&tmpsel);
\r
4650 SendMessage(hText, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cfmt);
\r
4651 /* Trying putting this here too. It still seems to tickle a RichEdit
\r
4652 * bug: sometimes RichEdit indents the first line of a paragraph too.
\r
4654 paraf.cbSize = sizeof(paraf);
\r
4655 paraf.dwMask = PFM_OFFSET | PFM_STARTINDENT;
\r
4656 paraf.dxStartIndent = 0;
\r
4657 paraf.dxOffset = WRAP_INDENT;
\r
4658 SendMessage(hText, EM_SETPARAFORMAT, 0, (LPARAM) ¶f);
\r
4659 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
4660 UpdateICSWidth(hText);
\r
4663 /*---------------------------------------------------------------------------*\
\r
4665 * Window Proc for main window
\r
4667 \*---------------------------------------------------------------------------*/
\r
4669 /* Process messages for main window, etc. */
\r
4671 WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
\r
4674 int wmId, wmEvent;
\r
4678 char fileTitle[MSG_SIZ];
\r
4679 char buf[MSG_SIZ];
\r
4680 static SnapData sd;
\r
4681 static int peek=0;
\r
4683 switch (message) {
\r
4685 case WM_PAINT: /* message: repaint portion of window */
\r
4689 case WM_ERASEBKGND:
\r
4690 if (IsIconic(hwnd)) {
\r
4691 /* Cheat; change the message */
\r
4692 return (DefWindowProc(hwnd, WM_ICONERASEBKGND, wParam, lParam));
\r
4694 return (DefWindowProc(hwnd, message, wParam, lParam));
\r
4698 case WM_LBUTTONDOWN:
\r
4699 case WM_MBUTTONDOWN:
\r
4700 case WM_RBUTTONDOWN:
\r
4701 case WM_LBUTTONUP:
\r
4702 case WM_MBUTTONUP:
\r
4703 case WM_RBUTTONUP:
\r
4704 case WM_MOUSEMOVE:
\r
4705 case WM_MOUSEWHEEL:
\r
4706 MouseEvent(hwnd, message, wParam, lParam);
\r
4710 if((char)wParam == '\b') {
\r
4711 ForwardEvent(); peek = 0;
\r
4714 JAWS_KBUP_NAVIGATION
\r
4719 if((char)wParam == '\b') {
\r
4720 if(!peek) BackwardEvent(), peek = 1;
\r
4723 JAWS_KBDOWN_NAVIGATION
\r
4729 JAWS_ALT_INTERCEPT
\r
4731 if (appData.icsActive && ((char)wParam == '\r' || (char)wParam > ' ' && !((char)wParam >= '1' && (char)wParam <= '9'))) {
\r
4732 // [HGM] movenum: for non-zero digits we always do type-in dialog
\r
4733 HWND h = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
4734 if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);
\r
4736 SendMessage(h, message, wParam, lParam);
\r
4737 } else if(lParam != KF_REPEAT) {
\r
4738 if (isalpha((char)wParam) || isdigit((char)wParam)) {
\r
4739 TypeInEvent((char)wParam);
\r
4740 } else if((char)wParam == 003) CopyGameToClipboard();
\r
4741 else if((char)wParam == 026) PasteGameOrFENFromClipboard();
\r
4746 case WM_PALETTECHANGED:
\r
4747 if (hwnd != (HWND)wParam && !appData.monoMode) {
\r
4749 HDC hdc = GetDC(hwndMain);
\r
4750 SelectPalette(hdc, hPal, TRUE);
\r
4751 nnew = RealizePalette(hdc);
\r
4753 paletteChanged = TRUE;
\r
4754 InvalidateRect(hwnd, &boardRect, FALSE);
\r
4756 ReleaseDC(hwnd, hdc);
\r
4760 case WM_QUERYNEWPALETTE:
\r
4761 if (!appData.monoMode /*&& paletteChanged*/) {
\r
4763 HDC hdc = GetDC(hwndMain);
\r
4764 paletteChanged = FALSE;
\r
4765 SelectPalette(hdc, hPal, FALSE);
\r
4766 nnew = RealizePalette(hdc);
\r
4768 InvalidateRect(hwnd, &boardRect, FALSE);
\r
4770 ReleaseDC(hwnd, hdc);
\r
4775 case WM_COMMAND: /* message: command from application menu */
\r
4776 wmId = LOWORD(wParam);
\r
4777 wmEvent = HIWORD(wParam);
\r
4782 SAY("new game enter a move to play against the computer with white");
\r
4785 case IDM_NewGameFRC:
\r
4786 if( NewGameFRC() == 0 ) {
\r
4791 case IDM_NewVariant:
\r
4792 NewVariantPopup(hwnd);
\r
4795 case IDM_LoadGame:
\r
4796 LoadGameDialog(hwnd, _("Load Game from File"));
\r
4799 case IDM_LoadNextGame:
\r
4803 case IDM_LoadPrevGame:
\r
4807 case IDM_ReloadGame:
\r
4811 case IDM_LoadPosition:
\r
4812 if (gameMode == AnalyzeMode || gameMode == AnalyzeFile) {
\r
4813 Reset(FALSE, TRUE);
\r
4816 f = OpenFileDialog(hwnd, "rb", "",
\r
4817 appData.oldSaveStyle ? "pos" : "fen",
\r
4819 _("Load Position from File"), &number, fileTitle, NULL);
\r
4821 LoadPosition(f, number, fileTitle);
\r
4825 case IDM_LoadNextPosition:
\r
4826 ReloadPosition(1);
\r
4829 case IDM_LoadPrevPosition:
\r
4830 ReloadPosition(-1);
\r
4833 case IDM_ReloadPosition:
\r
4834 ReloadPosition(0);
\r
4837 case IDM_SaveGame:
\r
4838 defName = DefaultFileName(appData.oldSaveStyle ? "gam" : "pgn");
\r
4839 f = OpenFileDialog(hwnd, "a", defName,
\r
4840 appData.oldSaveStyle ? "gam" : "pgn",
\r
4842 _("Save Game to File"), NULL, fileTitle, NULL);
\r
4844 SaveGame(f, 0, "");
\r
4848 case IDM_SavePosition:
\r
4849 defName = DefaultFileName(appData.oldSaveStyle ? "pos" : "fen");
\r
4850 f = OpenFileDialog(hwnd, "a", defName,
\r
4851 appData.oldSaveStyle ? "pos" : "fen",
\r
4853 _("Save Position to File"), NULL, fileTitle, NULL);
\r
4855 SavePosition(f, 0, "");
\r
4859 case IDM_SaveDiagram:
\r
4860 defName = "diagram";
\r
4861 f = OpenFileDialog(hwnd, "wb", defName,
\r
4864 _("Save Diagram to File"), NULL, fileTitle, NULL);
\r
4870 case IDM_CopyGame:
\r
4871 CopyGameToClipboard();
\r
4874 case IDM_PasteGame:
\r
4875 PasteGameFromClipboard();
\r
4878 case IDM_CopyGameListToClipboard:
\r
4879 CopyGameListToClipboard();
\r
4882 /* [AS] Autodetect FEN or PGN data */
\r
4883 case IDM_PasteAny:
\r
4884 PasteGameOrFENFromClipboard();
\r
4887 /* [AS] Move history */
\r
4888 case IDM_ShowMoveHistory:
\r
4889 if( MoveHistoryIsUp() ) {
\r
4890 MoveHistoryPopDown();
\r
4893 MoveHistoryPopUp();
\r
4897 /* [AS] Eval graph */
\r
4898 case IDM_ShowEvalGraph:
\r
4899 if( EvalGraphIsUp() ) {
\r
4900 EvalGraphPopDown();
\r
4904 SetFocus(hwndMain);
\r
4908 /* [AS] Engine output */
\r
4909 case IDM_ShowEngineOutput:
\r
4910 if( EngineOutputIsUp() ) {
\r
4911 EngineOutputPopDown();
\r
4914 EngineOutputPopUp();
\r
4918 /* [AS] User adjudication */
\r
4919 case IDM_UserAdjudication_White:
\r
4920 UserAdjudicationEvent( +1 );
\r
4923 case IDM_UserAdjudication_Black:
\r
4924 UserAdjudicationEvent( -1 );
\r
4927 case IDM_UserAdjudication_Draw:
\r
4928 UserAdjudicationEvent( 0 );
\r
4931 /* [AS] Game list options dialog */
\r
4932 case IDM_GameListOptions:
\r
4933 GameListOptions();
\r
4940 case IDM_CopyPosition:
\r
4941 CopyFENToClipboard();
\r
4944 case IDM_PastePosition:
\r
4945 PasteFENFromClipboard();
\r
4948 case IDM_MailMove:
\r
4952 case IDM_ReloadCMailMsg:
\r
4953 Reset(TRUE, TRUE);
\r
4954 ReloadCmailMsgEvent(FALSE);
\r
4957 case IDM_Minimize:
\r
4958 ShowWindow(hwnd, SW_MINIMIZE);
\r
4965 case IDM_MachineWhite:
\r
4966 MachineWhiteEvent();
\r
4968 * refresh the tags dialog only if it's visible
\r
4970 if (gameMode == MachinePlaysWhite && IsWindowVisible(editTagsDialog)) {
\r
4972 tags = PGNTags(&gameInfo);
\r
4973 TagsPopUp(tags, CmailMsg());
\r
4976 SAY("computer starts playing white");
\r
4979 case IDM_MachineBlack:
\r
4980 MachineBlackEvent();
\r
4982 * refresh the tags dialog only if it's visible
\r
4984 if (gameMode == MachinePlaysBlack && IsWindowVisible(editTagsDialog)) {
\r
4986 tags = PGNTags(&gameInfo);
\r
4987 TagsPopUp(tags, CmailMsg());
\r
4990 SAY("computer starts playing black");
\r
4993 case IDM_Match: // [HGM] match: flows into next case, after setting Match Mode and nr of Games
\r
4994 MatchEvent(2); // distinguish from command-line-triggered case (matchMode=1)
\r
4997 case IDM_TwoMachines:
\r
4998 TwoMachinesEvent();
\r
5000 * refresh the tags dialog only if it's visible
\r
5002 if (gameMode == TwoMachinesPlay && IsWindowVisible(editTagsDialog)) {
\r
5004 tags = PGNTags(&gameInfo);
\r
5005 TagsPopUp(tags, CmailMsg());
\r
5008 SAY("computer starts playing both sides");
\r
5011 case IDM_AnalysisMode:
\r
5012 if(AnalyzeModeEvent()) {
\r
5013 SAY("analyzing current position");
\r
5017 case IDM_AnalyzeFile:
\r
5018 AnalyzeFileEvent();
\r
5021 case IDM_IcsClient:
\r
5025 case IDM_EditGame:
\r
5026 case IDM_EditGame2:
\r
5031 case IDM_EditPosition:
\r
5032 case IDM_EditPosition2:
\r
5033 EditPositionEvent();
\r
5034 SAY("enter a FEN string or setup a position on the board using the control R pop up menu");
\r
5037 case IDM_Training:
\r
5041 case IDM_ShowGameList:
\r
5042 ShowGameListProc();
\r
5045 case IDM_EditProgs1:
\r
5046 EditTagsPopUp(firstChessProgramNames, &firstChessProgramNames);
\r
5049 case IDM_LoadProg1:
\r
5050 LoadEnginePopUp(hwndMain, 0);
\r
5053 case IDM_LoadProg2:
\r
5054 LoadEnginePopUp(hwndMain, 1);
\r
5057 case IDM_EditServers:
\r
5058 EditTagsPopUp(icsNames, &icsNames);
\r
5061 case IDM_EditTags:
\r
5066 case IDM_EditBook:
\r
5070 case IDM_EditComment:
\r
5072 if (commentUp && editComment) {
\r
5075 EditCommentEvent();
\r
5095 case IDM_CallFlag:
\r
5115 case IDM_StopObserving:
\r
5116 StopObservingEvent();
\r
5119 case IDM_StopExamining:
\r
5120 StopExaminingEvent();
\r
5124 UploadGameEvent();
\r
5127 case IDM_TypeInMove:
\r
5128 TypeInEvent('\000');
\r
5131 case IDM_TypeInName:
\r
5132 PopUpNameDialog('\000');
\r
5135 case IDM_Backward:
\r
5137 SetFocus(hwndMain);
\r
5144 SetFocus(hwndMain);
\r
5149 SetFocus(hwndMain);
\r
5154 SetFocus(hwndMain);
\r
5157 case OPT_GameListNext: // [HGM] forward these two accelerators to Game List
\r
5158 case OPT_GameListPrev:
\r
5159 if(gameListDialog) SendMessage(gameListDialog, WM_COMMAND, wmId, 0);
\r
5163 RevertEvent(FALSE);
\r
5166 case IDM_Annotate: // [HGM] vari: revert with annotation
\r
5167 RevertEvent(TRUE);
\r
5170 case IDM_TruncateGame:
\r
5171 TruncateGameEvent();
\r
5178 case IDM_RetractMove:
\r
5179 RetractMoveEvent();
\r
5182 case IDM_FlipView:
\r
5183 flipView = !flipView;
\r
5184 DrawPosition(FALSE, NULL);
\r
5187 case IDM_FlipClock:
\r
5188 flipClock = !flipClock;
\r
5189 DisplayBothClocks();
\r
5193 case IDM_MuteSounds:
\r
5194 mute = !mute; // [HGM] mute: keep track of global muting variable
\r
5195 CheckMenuItem(GetMenu(hwndMain),IDM_MuteSounds,
\r
5196 MF_BYCOMMAND|(mute?MF_CHECKED:MF_UNCHECKED));
\r
5199 case IDM_GeneralOptions:
\r
5200 GeneralOptionsPopup(hwnd);
\r
5201 DrawPosition(TRUE, NULL);
\r
5204 case IDM_BoardOptions:
\r
5205 BoardOptionsPopup(hwnd);
\r
5208 case IDM_ThemeOptions:
\r
5209 ThemeOptionsPopup(hwnd);
\r
5212 case IDM_EnginePlayOptions:
\r
5213 EnginePlayOptionsPopup(hwnd);
\r
5216 case IDM_Engine1Options:
\r
5217 EngineOptionsPopup(hwnd, &first);
\r
5220 case IDM_Engine2Options:
\r
5222 if(WaitForEngine(&second, SettingsMenuIfReady)) break;
\r
5223 EngineOptionsPopup(hwnd, &second);
\r
5226 case IDM_OptionsUCI:
\r
5227 UciOptionsPopup(hwnd);
\r
5231 TourneyPopup(hwnd);
\r
5234 case IDM_IcsOptions:
\r
5235 IcsOptionsPopup(hwnd);
\r
5239 FontsOptionsPopup(hwnd);
\r
5243 SoundOptionsPopup(hwnd);
\r
5246 case IDM_CommPort:
\r
5247 CommPortOptionsPopup(hwnd);
\r
5250 case IDM_LoadOptions:
\r
5251 LoadOptionsPopup(hwnd);
\r
5254 case IDM_SaveOptions:
\r
5255 SaveOptionsPopup(hwnd);
\r
5258 case IDM_TimeControl:
\r
5259 TimeControlOptionsPopup(hwnd);
\r
5262 case IDM_SaveSettings:
\r
5263 SaveSettings(settingsFileName);
\r
5266 case IDM_SaveSettingsOnExit:
\r
5267 saveSettingsOnExit = !saveSettingsOnExit;
\r
5268 (void) CheckMenuItem(GetMenu(hwndMain), IDM_SaveSettingsOnExit,
\r
5269 MF_BYCOMMAND|(saveSettingsOnExit ?
\r
5270 MF_CHECKED : MF_UNCHECKED));
\r
5281 case IDM_AboutGame:
\r
5286 appData.debugMode = !appData.debugMode;
\r
5287 if (appData.debugMode) {
\r
5288 char dir[MSG_SIZ];
\r
5289 GetCurrentDirectory(MSG_SIZ, dir);
\r
5290 SetCurrentDirectory(installDir);
\r
5291 debugFP = fopen(appData.nameOfDebugFile, "w");
\r
5292 SetCurrentDirectory(dir);
\r
5293 setbuf(debugFP, NULL);
\r
5300 case IDM_HELPCONTENTS:
\r
5301 if (!MyHelp (hwnd, "winboard.hlp", HELP_KEY,(DWORD)(LPSTR)"CONTENTS") &&
\r
5302 !HtmlHelp(hwnd, "winboard.chm", 0, 0) ) {
\r
5303 MessageBox (GetFocus(),
\r
5304 _("Unable to activate help"),
\r
5305 szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);
\r
5309 case IDM_HELPSEARCH:
\r
5310 if (!MyHelp (hwnd, "winboard.hlp", HELP_PARTIALKEY, (DWORD)(LPSTR)"") &&
\r
5311 !HtmlHelp(hwnd, "winboard.chm", 0, 0) ) {
\r
5312 MessageBox (GetFocus(),
\r
5313 _("Unable to activate help"),
\r
5314 szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);
\r
5318 case IDM_HELPHELP:
\r
5319 if(!WinHelp(hwnd, (LPSTR)NULL, HELP_HELPONHELP, 0)) {
\r
5320 MessageBox (GetFocus(),
\r
5321 _("Unable to activate help"),
\r
5322 szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);
\r
5327 lpProc = MakeProcInstance((FARPROC)About, hInst);
\r
5329 (gameInfo.event && strcmp(gameInfo.event, "Easter Egg Hunt") == 0) ?
\r
5330 "AboutBox2" : "AboutBox", hwnd, (DLGPROC)lpProc);
\r
5331 FreeProcInstance(lpProc);
\r
5334 case IDM_DirectCommand1:
\r
5335 AskQuestionEvent(_("Direct Command"),
\r
5336 _("Send to chess program:"), "", "1");
\r
5338 case IDM_DirectCommand2:
\r
5339 AskQuestionEvent(_("Direct Command"),
\r
5340 _("Send to second chess program:"), "", "2");
\r
5343 case EP_WhitePawn:
\r
5344 EditPositionMenuEvent(WhitePawn, fromX, fromY);
\r
5345 fromX = fromY = -1;
\r
5348 case EP_WhiteKnight:
\r
5349 EditPositionMenuEvent(WhiteKnight, fromX, fromY);
\r
5350 fromX = fromY = -1;
\r
5353 case EP_WhiteBishop:
\r
5354 EditPositionMenuEvent(WhiteBishop, fromX, fromY);
\r
5355 fromX = fromY = -1;
\r
5358 case EP_WhiteRook:
\r
5359 EditPositionMenuEvent(WhiteRook, fromX, fromY);
\r
5360 fromX = fromY = -1;
\r
5363 case EP_WhiteQueen:
\r
5364 EditPositionMenuEvent(WhiteQueen, fromX, fromY);
\r
5365 fromX = fromY = -1;
\r
5368 case EP_WhiteFerz:
\r
5369 EditPositionMenuEvent(WhiteFerz, fromX, fromY);
\r
5370 fromX = fromY = -1;
\r
5373 case EP_WhiteWazir:
\r
5374 EditPositionMenuEvent(WhiteWazir, fromX, fromY);
\r
5375 fromX = fromY = -1;
\r
5378 case EP_WhiteAlfil:
\r
5379 EditPositionMenuEvent(WhiteAlfil, fromX, fromY);
\r
5380 fromX = fromY = -1;
\r
5383 case EP_WhiteCannon:
\r
5384 EditPositionMenuEvent(WhiteCannon, fromX, fromY);
\r
5385 fromX = fromY = -1;
\r
5388 case EP_WhiteCardinal:
\r
5389 EditPositionMenuEvent(WhiteAngel, fromX, fromY);
\r
5390 fromX = fromY = -1;
\r
5393 case EP_WhiteMarshall:
\r
5394 EditPositionMenuEvent(WhiteMarshall, fromX, fromY);
\r
5395 fromX = fromY = -1;
\r
5398 case EP_WhiteKing:
\r
5399 EditPositionMenuEvent(WhiteKing, fromX, fromY);
\r
5400 fromX = fromY = -1;
\r
5403 case EP_BlackPawn:
\r
5404 EditPositionMenuEvent(BlackPawn, fromX, fromY);
\r
5405 fromX = fromY = -1;
\r
5408 case EP_BlackKnight:
\r
5409 EditPositionMenuEvent(BlackKnight, fromX, fromY);
\r
5410 fromX = fromY = -1;
\r
5413 case EP_BlackBishop:
\r
5414 EditPositionMenuEvent(BlackBishop, fromX, fromY);
\r
5415 fromX = fromY = -1;
\r
5418 case EP_BlackRook:
\r
5419 EditPositionMenuEvent(BlackRook, fromX, fromY);
\r
5420 fromX = fromY = -1;
\r
5423 case EP_BlackQueen:
\r
5424 EditPositionMenuEvent(BlackQueen, fromX, fromY);
\r
5425 fromX = fromY = -1;
\r
5428 case EP_BlackFerz:
\r
5429 EditPositionMenuEvent(BlackFerz, fromX, fromY);
\r
5430 fromX = fromY = -1;
\r
5433 case EP_BlackWazir:
\r
5434 EditPositionMenuEvent(BlackWazir, fromX, fromY);
\r
5435 fromX = fromY = -1;
\r
5438 case EP_BlackAlfil:
\r
5439 EditPositionMenuEvent(BlackAlfil, fromX, fromY);
\r
5440 fromX = fromY = -1;
\r
5443 case EP_BlackCannon:
\r
5444 EditPositionMenuEvent(BlackCannon, fromX, fromY);
\r
5445 fromX = fromY = -1;
\r
5448 case EP_BlackCardinal:
\r
5449 EditPositionMenuEvent(BlackAngel, fromX, fromY);
\r
5450 fromX = fromY = -1;
\r
5453 case EP_BlackMarshall:
\r
5454 EditPositionMenuEvent(BlackMarshall, fromX, fromY);
\r
5455 fromX = fromY = -1;
\r
5458 case EP_BlackKing:
\r
5459 EditPositionMenuEvent(BlackKing, fromX, fromY);
\r
5460 fromX = fromY = -1;
\r
5463 case EP_EmptySquare:
\r
5464 EditPositionMenuEvent(EmptySquare, fromX, fromY);
\r
5465 fromX = fromY = -1;
\r
5468 case EP_ClearBoard:
\r
5469 EditPositionMenuEvent(ClearBoard, fromX, fromY);
\r
5470 fromX = fromY = -1;
\r
5474 EditPositionMenuEvent(WhitePlay, fromX, fromY);
\r
5475 fromX = fromY = -1;
\r
5479 EditPositionMenuEvent(BlackPlay, fromX, fromY);
\r
5480 fromX = fromY = -1;
\r
5484 EditPositionMenuEvent(PromotePiece, fromX, fromY);
\r
5485 fromX = fromY = -1;
\r
5489 EditPositionMenuEvent(DemotePiece, fromX, fromY);
\r
5490 fromX = fromY = -1;
\r
5494 DropMenuEvent(WhitePawn, fromX, fromY);
\r
5495 fromX = fromY = -1;
\r
5499 DropMenuEvent(WhiteKnight, fromX, fromY);
\r
5500 fromX = fromY = -1;
\r
5504 DropMenuEvent(WhiteBishop, fromX, fromY);
\r
5505 fromX = fromY = -1;
\r
5509 DropMenuEvent(WhiteRook, fromX, fromY);
\r
5510 fromX = fromY = -1;
\r
5514 DropMenuEvent(WhiteQueen, fromX, fromY);
\r
5515 fromX = fromY = -1;
\r
5519 barbaric = 0; appData.language = "";
\r
5520 TranslateMenus(0);
\r
5521 CheckMenuItem(GetMenu(hwndMain), lastChecked, MF_BYCOMMAND|MF_UNCHECKED);
\r
5522 CheckMenuItem(GetMenu(hwndMain), IDM_English, MF_BYCOMMAND|MF_CHECKED);
\r
5523 lastChecked = wmId;
\r
5527 if(wmId >= IDM_RecentEngines && wmId < IDM_RecentEngines + appData.recentEngines)
\r
5528 RecentEngineEvent(wmId - IDM_RecentEngines);
\r
5530 if(wmId > IDM_English && wmId < IDM_English+20) {
\r
5531 LoadLanguageFile(languageFile[wmId - IDM_English - 1]);
\r
5532 TranslateMenus(0);
\r
5533 CheckMenuItem(GetMenu(hwndMain), lastChecked, MF_BYCOMMAND|MF_UNCHECKED);
\r
5534 CheckMenuItem(GetMenu(hwndMain), wmId, MF_BYCOMMAND|MF_CHECKED);
\r
5535 lastChecked = wmId;
\r
5538 return (DefWindowProc(hwnd, message, wParam, lParam));
\r
5544 case CLOCK_TIMER_ID:
\r
5545 KillTimer(hwnd, clockTimerEvent); /* Simulate one-shot timer as in X */
\r
5546 clockTimerEvent = 0;
\r
5547 DecrementClocks(); /* call into back end */
\r
5549 case LOAD_GAME_TIMER_ID:
\r
5550 KillTimer(hwnd, loadGameTimerEvent); /* Simulate one-shot timer as in X*/
\r
5551 loadGameTimerEvent = 0;
\r
5552 AutoPlayGameLoop(); /* call into back end */
\r
5554 case ANALYSIS_TIMER_ID:
\r
5555 if ((gameMode == AnalyzeMode || gameMode == AnalyzeFile
\r
5556 || appData.icsEngineAnalyze) && appData.periodicUpdates) {
\r
5557 AnalysisPeriodicEvent(0);
\r
5559 KillTimer(hwnd, analysisTimerEvent);
\r
5560 analysisTimerEvent = 0;
\r
5563 case DELAYED_TIMER_ID:
\r
5564 KillTimer(hwnd, delayedTimerEvent);
\r
5565 delayedTimerEvent = 0;
\r
5566 delayedTimerCallback();
\r
5571 case WM_USER_Input:
\r
5572 InputEvent(hwnd, message, wParam, lParam);
\r
5575 /* [AS] Also move "attached" child windows */
\r
5576 case WM_WINDOWPOSCHANGING:
\r
5578 if( hwnd == hwndMain && appData.useStickyWindows ) {
\r
5579 LPWINDOWPOS lpwp = (LPWINDOWPOS) lParam;
\r
5581 if( ((lpwp->flags & SWP_NOMOVE) == 0) && ((lpwp->flags & SWP_NOSIZE) != 0) ) {
\r
5582 /* Window is moving */
\r
5585 // GetWindowRect( hwnd, &rcMain ); //[HGM] sticky: in XP this returned new position, not old
\r
5586 rcMain.left = wpMain.x; // replace by these 4 lines to reconstruct old rect
\r
5587 rcMain.right = wpMain.x + wpMain.width;
\r
5588 rcMain.top = wpMain.y;
\r
5589 rcMain.bottom = wpMain.y + wpMain.height;
\r
5591 ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, moveHistoryDialog, &wpMoveHistory );
\r
5592 ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, evalGraphDialog, &wpEvalGraph );
\r
5593 ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, engineOutputDialog, &wpEngineOutput );
\r
5594 ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, gameListDialog, &wpGameList );
\r
5595 ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, hwndConsole, &wpConsole );
\r
5596 wpMain.x = lpwp->x;
\r
5597 wpMain.y = lpwp->y;
\r
5602 /* [AS] Snapping */
\r
5603 case WM_ENTERSIZEMOVE:
\r
5604 if(appData.debugMode) { fprintf(debugFP, "size-move\n"); }
\r
5605 if (hwnd == hwndMain) {
\r
5606 doingSizing = TRUE;
\r
5609 return OnEnterSizeMove( &sd, hwnd, wParam, lParam );
\r
5613 if(appData.debugMode) { fprintf(debugFP, "sizing\n"); }
\r
5614 if (hwnd == hwndMain) {
\r
5615 lastSizing = wParam;
\r
5620 if(appData.debugMode) { fprintf(debugFP, "moving\n"); }
\r
5621 return OnMoving( &sd, hwnd, wParam, lParam );
\r
5623 case WM_EXITSIZEMOVE:
\r
5624 if(appData.debugMode) { fprintf(debugFP, "exit size-move, size = %d\n", squareSize); }
\r
5625 if (hwnd == hwndMain) {
\r
5627 doingSizing = FALSE;
\r
5628 InvalidateRect(hwnd, &boardRect, FALSE);
\r
5629 GetClientRect(hwnd, &client);
\r
5630 ResizeBoard(client.right, client.bottom, lastSizing);
\r
5632 if(appData.debugMode) { fprintf(debugFP, "square size = %d\n", squareSize); }
\r
5634 return OnExitSizeMove( &sd, hwnd, wParam, lParam );
\r
5637 case WM_DESTROY: /* message: window being destroyed */
\r
5638 PostQuitMessage(0);
\r
5642 if (hwnd == hwndMain) {
\r
5647 default: /* Passes it on if unprocessed */
\r
5648 return (DefWindowProc(hwnd, message, wParam, lParam));
\r
5653 /*---------------------------------------------------------------------------*\
\r
5655 * Misc utility routines
\r
5657 \*---------------------------------------------------------------------------*/
\r
5660 * Decent random number generator, at least not as bad as Windows
\r
5661 * standard rand, which returns a value in the range 0 to 0x7fff.
\r
5663 unsigned int randstate;
\r
5668 randstate = randstate * 1664525 + 1013904223;
\r
5669 return (int) randstate & 0x7fffffff;
\r
5673 mysrandom(unsigned int seed)
\r
5680 * returns TRUE if user selects a different color, FALSE otherwise
\r
5684 ChangeColor(HWND hwnd, COLORREF *which)
\r
5686 static BOOL firstTime = TRUE;
\r
5687 static DWORD customColors[16];
\r
5689 COLORREF newcolor;
\r
5694 /* Make initial colors in use available as custom colors */
\r
5695 /* Should we put the compiled-in defaults here instead? */
\r
5697 customColors[i++] = lightSquareColor & 0xffffff;
\r
5698 customColors[i++] = darkSquareColor & 0xffffff;
\r
5699 customColors[i++] = whitePieceColor & 0xffffff;
\r
5700 customColors[i++] = blackPieceColor & 0xffffff;
\r
5701 customColors[i++] = highlightSquareColor & 0xffffff;
\r
5702 customColors[i++] = premoveHighlightColor & 0xffffff;
\r
5704 for (ccl = (ColorClass) 0; ccl < NColorClasses && i < 16; ccl++) {
\r
5705 customColors[i++] = textAttribs[ccl].color;
\r
5707 while (i < 16) customColors[i++] = RGB(255, 255, 255);
\r
5708 firstTime = FALSE;
\r
5711 cc.lStructSize = sizeof(cc);
\r
5712 cc.hwndOwner = hwnd;
\r
5713 cc.hInstance = NULL;
\r
5714 cc.rgbResult = (DWORD) (*which & 0xffffff);
\r
5715 cc.lpCustColors = (LPDWORD) customColors;
\r
5716 cc.Flags = CC_RGBINIT|CC_FULLOPEN;
\r
5718 if (!ChooseColor(&cc)) return FALSE;
\r
5720 newcolor = (COLORREF) (0x2000000 | cc.rgbResult);
\r
5721 if (newcolor == *which) return FALSE;
\r
5722 *which = newcolor;
\r
5726 InitDrawingColors();
\r
5727 InvalidateRect(hwnd, &boardRect, FALSE);
\r
5732 MyLoadSound(MySound *ms)
\r
5738 if (ms->data && ms->flag) free(ms->data);
\r
5741 switch (ms->name[0]) {
\r
5747 /* System sound from Control Panel. Don't preload here. */
\r
5751 if (ms->name[1] == NULLCHAR) {
\r
5752 /* "!" alone = silence */
\r
5755 /* Builtin wave resource. Error if not found. */
\r
5756 HANDLE h = FindResource(hInst, ms->name + 1, "WAVE");
\r
5757 if (h == NULL) break;
\r
5758 ms->data = (void *)LoadResource(hInst, h);
\r
5759 ms->flag = 0; // not maloced, so cannot be freed!
\r
5760 if (h == NULL) break;
\r
5765 /* .wav file. Error if not found. */
\r
5766 f = fopen(ms->name, "rb");
\r
5767 if (f == NULL) break;
\r
5768 if (fstat(fileno(f), &st) < 0) break;
\r
5769 ms->data = malloc(st.st_size);
\r
5771 if (fread(ms->data, st.st_size, 1, f) < 1) break;
\r
5777 char buf[MSG_SIZ];
\r
5778 snprintf(buf, MSG_SIZ, _("Error loading sound %s"), ms->name);
\r
5779 DisplayError(buf, GetLastError());
\r
5785 MyPlaySound(MySound *ms)
\r
5787 BOOLEAN ok = FALSE;
\r
5789 if(mute) return TRUE; // [HGM] mute: suppress all sound play when muted
\r
5790 switch (ms->name[0]) {
\r
5792 if(appData.debugMode) fprintf(debugFP, "silence\n");
\r
5797 /* System sound from Control Panel (deprecated feature).
\r
5798 "$" alone or an unset sound name gets default beep (still in use). */
\r
5799 if (ms->name[1]) {
\r
5800 ok = PlaySound(ms->name + 1, NULL, SND_ALIAS|SND_ASYNC);
\r
5802 if (!ok) ok = MessageBeep(MB_OK);
\r
5805 /* Builtin wave resource, or "!" alone for silence */
\r
5806 if (ms->name[1]) {
\r
5807 if (ms->data == NULL) return FALSE;
\r
5808 ok = PlaySound(ms->data, NULL, SND_MEMORY|SND_ASYNC);
\r
5814 /* .wav file. Error if not found. */
\r
5815 if (ms->data == NULL) return FALSE;
\r
5816 ok = PlaySound(ms->data, NULL, SND_MEMORY|SND_ASYNC);
\r
5819 /* Don't print an error: this can happen innocently if the sound driver
\r
5820 is busy; for instance, if another instance of WinBoard is playing
\r
5821 a sound at about the same time. */
\r
5827 OldOpenFileHook(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
5830 OPENFILENAME *ofn;
\r
5831 static UINT *number; /* gross that this is static */
\r
5833 switch (message) {
\r
5834 case WM_INITDIALOG: /* message: initialize dialog box */
\r
5835 /* Center the dialog over the application window */
\r
5836 ofn = (OPENFILENAME *) lParam;
\r
5837 if (ofn->Flags & OFN_ENABLETEMPLATE) {
\r
5838 number = (UINT *) ofn->lCustData;
\r
5839 SendMessage(GetDlgItem(hDlg, edt2), WM_SETTEXT, 0, (LPARAM) "");
\r
5843 CenterWindow (hDlg, GetWindow (hDlg, GW_OWNER));
\r
5844 Translate(hDlg, 1536);
\r
5845 return FALSE; /* Allow for further processing */
\r
5848 if ((LOWORD(wParam) == IDOK) && (number != NULL)) {
\r
5849 *number = GetDlgItemInt(hDlg, OPT_IndexNumberOld, &ok, FALSE);
\r
5851 return FALSE; /* Allow for further processing */
\r
5857 OpenFileHook(HWND hdlg, UINT uiMsg, WPARAM wParam, LPARAM lParam)
\r
5859 static UINT *number;
\r
5860 OPENFILENAME *ofname;
\r
5863 case WM_INITDIALOG:
\r
5864 Translate(hdlg, DLG_IndexNumber);
\r
5865 ofname = (OPENFILENAME *)lParam;
\r
5866 number = (UINT *)(ofname->lCustData);
\r
5869 ofnot = (OFNOTIFY *)lParam;
\r
5870 if (ofnot->hdr.code == CDN_FILEOK) {
\r
5871 *number = GetDlgItemInt(hdlg, OPT_IndexNumber, NULL, FALSE);
\r
5880 OpenFileDialog(HWND hwnd, char *write, char *defName, char *defExt, // [HGM] diag: type of 'write' now string
\r
5881 char *nameFilt, char *dlgTitle, UINT *number,
\r
5882 char fileTitle[MSG_SIZ], char fileName[MSG_SIZ])
\r
5884 OPENFILENAME openFileName;
\r
5885 char buf1[MSG_SIZ];
\r
5888 if (fileName == NULL) fileName = buf1;
\r
5889 if (defName == NULL) {
\r
5890 safeStrCpy(fileName, "*.", 3 );
\r
5891 strcat(fileName, defExt);
\r
5893 safeStrCpy(fileName, defName, MSG_SIZ );
\r
5895 if (fileTitle) safeStrCpy(fileTitle, "", MSG_SIZ );
\r
5896 if (number) *number = 0;
\r
5898 openFileName.lStructSize = sizeof(OPENFILENAME);
\r
5899 openFileName.hwndOwner = hwnd;
\r
5900 openFileName.hInstance = (HANDLE) hInst;
\r
5901 openFileName.lpstrFilter = nameFilt;
\r
5902 openFileName.lpstrCustomFilter = (LPSTR) NULL;
\r
5903 openFileName.nMaxCustFilter = 0L;
\r
5904 openFileName.nFilterIndex = 1L;
\r
5905 openFileName.lpstrFile = fileName;
\r
5906 openFileName.nMaxFile = MSG_SIZ;
\r
5907 openFileName.lpstrFileTitle = fileTitle;
\r
5908 openFileName.nMaxFileTitle = fileTitle ? MSG_SIZ : 0;
\r
5909 openFileName.lpstrInitialDir = NULL;
\r
5910 openFileName.lpstrTitle = dlgTitle;
\r
5911 openFileName.Flags = OFN_PATHMUSTEXIST | OFN_HIDEREADONLY
\r
5912 | (write[0] != 'r' ? 0 : OFN_FILEMUSTEXIST)
\r
5913 | (number ? OFN_ENABLETEMPLATE | OFN_ENABLEHOOK: 0)
\r
5914 | (oldDialog ? 0 : OFN_EXPLORER);
\r
5915 openFileName.nFileOffset = 0;
\r
5916 openFileName.nFileExtension = 0;
\r
5917 openFileName.lpstrDefExt = defExt;
\r
5918 openFileName.lCustData = (LONG) number;
\r
5919 openFileName.lpfnHook = oldDialog ?
\r
5920 (LPOFNHOOKPROC) OldOpenFileHook : (LPOFNHOOKPROC) OpenFileHook;
\r
5921 openFileName.lpTemplateName = (LPSTR)(oldDialog ? 1536 : DLG_IndexNumber);
\r
5923 if (write[0] != 'r' ? GetSaveFileName(&openFileName) :
\r
5924 GetOpenFileName(&openFileName)) {
\r
5925 /* open the file */
\r
5926 f = fopen(openFileName.lpstrFile, write);
\r
5928 MessageBox(hwnd, _("File open failed"), NULL,
\r
5929 MB_OK|MB_ICONEXCLAMATION);
\r
5933 int err = CommDlgExtendedError();
\r
5934 if (err != 0) DisplayError(_("Internal error in file dialog box"), err);
\r
5943 MenuPopup(HWND hwnd, POINT pt, HMENU hmenu, UINT def)
\r
5945 HMENU hmenuTrackPopup; /* floating pop-up menu */
\r
5948 * Get the first pop-up menu in the menu template. This is the
\r
5949 * menu that TrackPopupMenu displays.
\r
5951 hmenuTrackPopup = GetSubMenu(hmenu, 0);
\r
5952 TranslateOneMenu(10, hmenuTrackPopup);
\r
5954 SetMenuDefaultItem(hmenuTrackPopup, def, FALSE);
\r
5957 * TrackPopup uses screen coordinates, so convert the
\r
5958 * coordinates of the mouse click to screen coordinates.
\r
5960 ClientToScreen(hwnd, (LPPOINT) &pt);
\r
5962 /* Draw and track the floating pop-up menu. */
\r
5963 TrackPopupMenu(hmenuTrackPopup, TPM_CENTERALIGN | TPM_RIGHTBUTTON,
\r
5964 pt.x, pt.y, 0, hwnd, NULL);
\r
5966 /* Destroy the menu.*/
\r
5967 DestroyMenu(hmenu);
\r
5972 int sizeX, sizeY, newSizeX, newSizeY;
\r
5974 } ResizeEditPlusButtonsClosure;
\r
5977 ResizeEditPlusButtonsCallback(HWND hChild, LPARAM lparam)
\r
5979 ResizeEditPlusButtonsClosure *cl = (ResizeEditPlusButtonsClosure *)lparam;
\r
5983 if (hChild == cl->hText) return TRUE;
\r
5984 GetWindowRect(hChild, &rect); /* gives screen coords */
\r
5985 pt.x = rect.left + (cl->newSizeX - cl->sizeX)/2;
\r
5986 pt.y = rect.top + cl->newSizeY - cl->sizeY;
\r
5987 ScreenToClient(cl->hDlg, &pt);
\r
5988 cl->hdwp = DeferWindowPos(cl->hdwp, hChild, NULL,
\r
5989 pt.x, pt.y, rect.right - rect.left, rect.bottom - rect.top, SWP_NOZORDER);
\r
5993 /* Resize a dialog that has a (rich) edit field filling most of
\r
5994 the top, with a row of buttons below */
\r
5996 ResizeEditPlusButtons(HWND hDlg, HWND hText, int sizeX, int sizeY, int newSizeX, int newSizeY)
\r
5999 int newTextHeight, newTextWidth;
\r
6000 ResizeEditPlusButtonsClosure cl;
\r
6002 /*if (IsIconic(hDlg)) return;*/
\r
6003 if (newSizeX == sizeX && newSizeY == sizeY) return;
\r
6005 cl.hdwp = BeginDeferWindowPos(8);
\r
6007 GetWindowRect(hText, &rectText); /* gives screen coords */
\r
6008 newTextWidth = rectText.right - rectText.left + newSizeX - sizeX;
\r
6009 newTextHeight = rectText.bottom - rectText.top + newSizeY - sizeY;
\r
6010 if (newTextHeight < 0) {
\r
6011 newSizeY += -newTextHeight;
\r
6012 newTextHeight = 0;
\r
6014 cl.hdwp = DeferWindowPos(cl.hdwp, hText, NULL, 0, 0,
\r
6015 newTextWidth, newTextHeight, SWP_NOZORDER|SWP_NOMOVE);
\r
6021 cl.newSizeX = newSizeX;
\r
6022 cl.newSizeY = newSizeY;
\r
6023 EnumChildWindows(hDlg, ResizeEditPlusButtonsCallback, (LPARAM)&cl);
\r
6025 EndDeferWindowPos(cl.hdwp);
\r
6028 BOOL CenterWindowEx(HWND hwndChild, HWND hwndParent, int mode)
\r
6030 RECT rChild, rParent;
\r
6031 int wChild, hChild, wParent, hParent;
\r
6032 int wScreen, hScreen, xNew, yNew;
\r
6035 /* Get the Height and Width of the child window */
\r
6036 GetWindowRect (hwndChild, &rChild);
\r
6037 wChild = rChild.right - rChild.left;
\r
6038 hChild = rChild.bottom - rChild.top;
\r
6040 /* Get the Height and Width of the parent window */
\r
6041 GetWindowRect (hwndParent, &rParent);
\r
6042 wParent = rParent.right - rParent.left;
\r
6043 hParent = rParent.bottom - rParent.top;
\r
6045 /* Get the display limits */
\r
6046 hdc = GetDC (hwndChild);
\r
6047 wScreen = GetDeviceCaps (hdc, HORZRES);
\r
6048 hScreen = GetDeviceCaps (hdc, VERTRES);
\r
6049 ReleaseDC(hwndChild, hdc);
\r
6051 /* Calculate new X position, then adjust for screen */
\r
6052 xNew = rParent.left + ((wParent - wChild) /2);
\r
6055 } else if ((xNew+wChild) > wScreen) {
\r
6056 xNew = wScreen - wChild;
\r
6059 /* Calculate new Y position, then adjust for screen */
\r
6061 yNew = rParent.top + ((hParent - hChild) /2);
\r
6064 yNew = rParent.top + GetSystemMetrics( SM_CYCAPTION ) * 2 / 3;
\r
6069 } else if ((yNew+hChild) > hScreen) {
\r
6070 yNew = hScreen - hChild;
\r
6073 /* Set it, and return */
\r
6074 return SetWindowPos (hwndChild, NULL,
\r
6075 xNew, yNew, 0, 0, SWP_NOSIZE | SWP_NOZORDER);
\r
6078 /* Center one window over another */
\r
6079 BOOL CenterWindow (HWND hwndChild, HWND hwndParent)
\r
6081 return CenterWindowEx( hwndChild, hwndParent, 0 );
\r
6084 /*---------------------------------------------------------------------------*\
\r
6086 * Startup Dialog functions
\r
6088 \*---------------------------------------------------------------------------*/
\r
6090 InitComboStrings(HANDLE hwndCombo, char **cd)
\r
6092 SendMessage(hwndCombo, CB_RESETCONTENT, 0, 0);
\r
6094 while (*cd != NULL) {
\r
6095 SendMessage(hwndCombo, CB_ADDSTRING, 0, (LPARAM) T_(*cd));
\r
6101 InitComboStringsFromOption(HANDLE hwndCombo, char *str)
\r
6103 char buf1[MAX_ARG_LEN];
\r
6106 if (str[0] == '@') {
\r
6107 FILE* f = fopen(str + 1, "r");
\r
6109 DisplayFatalError(str + 1, errno, 2);
\r
6112 len = fread(buf1, 1, sizeof(buf1)-1, f);
\r
6114 buf1[len] = NULLCHAR;
\r
6118 SendMessage(hwndCombo, CB_RESETCONTENT, 0, 0);
\r
6121 char buf[MSG_SIZ];
\r
6122 char *end = strchr(str, '\n');
\r
6123 if (end == NULL) return;
\r
6124 memcpy(buf, str, end - str);
\r
6125 buf[end - str] = NULLCHAR;
\r
6126 SendMessage(hwndCombo, CB_ADDSTRING, 0, (LPARAM) buf);
\r
6132 SetStartupDialogEnables(HWND hDlg)
\r
6134 EnableWindow(GetDlgItem(hDlg, OPT_ChessEngineName),
\r
6135 IsDlgButtonChecked(hDlg, OPT_ChessEngine) ||
\r
6136 (appData.zippyPlay && IsDlgButtonChecked(hDlg, OPT_ChessServer)));
\r
6137 EnableWindow(GetDlgItem(hDlg, OPT_SecondChessEngineName),
\r
6138 IsDlgButtonChecked(hDlg, OPT_ChessEngine));
\r
6139 EnableWindow(GetDlgItem(hDlg, OPT_ChessServerName),
\r
6140 IsDlgButtonChecked(hDlg, OPT_ChessServer));
\r
6141 EnableWindow(GetDlgItem(hDlg, OPT_AdditionalOptions),
\r
6142 IsDlgButtonChecked(hDlg, OPT_AnyAdditional));
\r
6143 EnableWindow(GetDlgItem(hDlg, IDOK),
\r
6144 IsDlgButtonChecked(hDlg, OPT_ChessEngine) ||
\r
6145 IsDlgButtonChecked(hDlg, OPT_ChessServer) ||
\r
6146 IsDlgButtonChecked(hDlg, OPT_View));
\r
6150 QuoteForFilename(char *filename)
\r
6152 int dquote, space;
\r
6153 dquote = strchr(filename, '"') != NULL;
\r
6154 space = strchr(filename, ' ') != NULL;
\r
6155 if (dquote || space) {
\r
6167 InitEngineBox(HWND hDlg, HWND hwndCombo, char* nthcp, char* nthd, char* nthdir, char *nthnames)
\r
6169 char buf[MSG_SIZ];
\r
6172 InitComboStringsFromOption(hwndCombo, nthnames);
\r
6173 q = QuoteForFilename(nthcp);
\r
6174 snprintf(buf, MSG_SIZ, "%s%s%s", q, nthcp, q);
\r
6175 if (*nthdir != NULLCHAR) {
\r
6176 q = QuoteForFilename(nthdir);
\r
6177 snprintf(buf + strlen(buf), MSG_SIZ, " /%s=%s%s%s", nthd, q, nthdir, q);
\r
6179 if (*nthcp == NULLCHAR) {
\r
6180 SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) 0, (LPARAM) 0);
\r
6181 } else if (SendMessage(hwndCombo, CB_SELECTSTRING, (WPARAM) -1, (LPARAM) buf) == CB_ERR) {
\r
6182 SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) -1, (LPARAM) 0);
\r
6183 SendMessage(hwndCombo, WM_SETTEXT, (WPARAM) 0, (LPARAM) buf);
\r
6188 StartupDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
6190 char buf[MSG_SIZ];
\r
6194 switch (message) {
\r
6195 case WM_INITDIALOG:
\r
6196 /* Center the dialog */
\r
6197 CenterWindow (hDlg, GetDesktopWindow());
\r
6198 Translate(hDlg, DLG_Startup);
\r
6199 /* Initialize the dialog items */
\r
6200 InitEngineBox(hDlg, GetDlgItem(hDlg, OPT_ChessEngineName),
\r
6201 appData.firstChessProgram, "fd", appData.firstDirectory,
\r
6202 firstChessProgramNames);
\r
6203 InitEngineBox(hDlg, GetDlgItem(hDlg, OPT_SecondChessEngineName),
\r
6204 appData.secondChessProgram, singleList ? "fd" : "sd", appData.secondDirectory,
\r
6205 singleList ? firstChessProgramNames : secondChessProgramNames); //[HGM] single: use first list in second combo
\r
6206 hwndCombo = GetDlgItem(hDlg, OPT_ChessServerName);
\r
6207 InitComboStringsFromOption(hwndCombo, icsNames);
\r
6208 snprintf(buf, MSG_SIZ, "%s /icsport=%s", appData.icsHost, appData.icsPort);
\r
6209 if (*appData.icsHelper != NULLCHAR) {
\r
6210 char *q = QuoteForFilename(appData.icsHelper);
\r
6211 sprintf(buf + strlen(buf), " /icshelper=%s%s%s", q, appData.icsHelper, q);
\r
6213 if (*appData.icsHost == NULLCHAR) {
\r
6214 SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) 0, (LPARAM) 0);
\r
6215 /*SendMessage(hwndCombo, CB_SHOWDROPDOWN, (WPARAM) TRUE, (LPARAM) 0); !!too soon */
\r
6216 } else if (SendMessage(hwndCombo, CB_SELECTSTRING, (WPARAM) -1, (LPARAM) buf) == CB_ERR) {
\r
6217 SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) -1, (LPARAM) 0);
\r
6218 SendMessage(hwndCombo, WM_SETTEXT, (WPARAM) 0, (LPARAM) buf);
\r
6221 if (appData.icsActive) {
\r
6222 CheckDlgButton(hDlg, OPT_ChessServer, BST_CHECKED);
\r
6224 else if (appData.noChessProgram) {
\r
6225 CheckDlgButton(hDlg, OPT_View, BST_CHECKED);
\r
6228 CheckDlgButton(hDlg, OPT_ChessEngine, BST_CHECKED);
\r
6231 SetStartupDialogEnables(hDlg);
\r
6235 switch (LOWORD(wParam)) {
\r
6237 if (IsDlgButtonChecked(hDlg, OPT_ChessEngine)) {
\r
6238 safeStrCpy(buf, "/fcp=", sizeof(buf)/sizeof(buf[0]) );
\r
6239 GetDlgItemText(hDlg, OPT_ChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));
\r
6241 comboLine = strdup(p+5); // [HGM] recent: remember complete line of first combobox
\r
6242 ParseArgs(StringGet, &p);
\r
6243 safeStrCpy(buf, singleList ? "/fcp=" : "/scp=", sizeof(buf)/sizeof(buf[0]) );
\r
6244 GetDlgItemText(hDlg, OPT_SecondChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));
\r
6246 SwapEngines(singleList); // temporarily swap first and second, to load a second 'first', ...
\r
6247 ParseArgs(StringGet, &p);
\r
6248 SwapEngines(singleList); // ... and then make it 'second'
\r
6249 appData.noChessProgram = FALSE;
\r
6250 appData.icsActive = FALSE;
\r
6251 } else if (IsDlgButtonChecked(hDlg, OPT_ChessServer)) {
\r
6252 safeStrCpy(buf, "/ics /icshost=", sizeof(buf)/sizeof(buf[0]) );
\r
6253 GetDlgItemText(hDlg, OPT_ChessServerName, buf + strlen(buf), sizeof(buf) - strlen(buf));
\r
6255 ParseArgs(StringGet, &p);
\r
6256 if (appData.zippyPlay) {
\r
6257 safeStrCpy(buf, "/fcp=", sizeof(buf)/sizeof(buf[0]) );
\r
6258 GetDlgItemText(hDlg, OPT_ChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));
\r
6260 ParseArgs(StringGet, &p);
\r
6262 } else if (IsDlgButtonChecked(hDlg, OPT_View)) {
\r
6263 appData.noChessProgram = TRUE;
\r
6264 appData.icsActive = FALSE;
\r
6266 MessageBox(hDlg, _("Choose an option, or cancel to exit"),
\r
6267 _("Option Error"), MB_OK|MB_ICONEXCLAMATION);
\r
6270 if (IsDlgButtonChecked(hDlg, OPT_AnyAdditional)) {
\r
6271 GetDlgItemText(hDlg, OPT_AdditionalOptions, buf, sizeof(buf));
\r
6273 ParseArgs(StringGet, &p);
\r
6275 EndDialog(hDlg, TRUE);
\r
6282 case IDM_HELPCONTENTS:
\r
6283 if (!WinHelp (hDlg, "winboard.hlp", HELP_KEY,(DWORD)(LPSTR)"CONTENTS")) {
\r
6284 MessageBox (GetFocus(),
\r
6285 _("Unable to activate help"),
\r
6286 szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);
\r
6291 SetStartupDialogEnables(hDlg);
\r
6299 /*---------------------------------------------------------------------------*\
\r
6301 * About box dialog functions
\r
6303 \*---------------------------------------------------------------------------*/
\r
6305 /* Process messages for "About" dialog box */
\r
6307 About(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
6309 switch (message) {
\r
6310 case WM_INITDIALOG: /* message: initialize dialog box */
\r
6311 /* Center the dialog over the application window */
\r
6312 CenterWindow (hDlg, GetWindow (hDlg, GW_OWNER));
\r
6313 SetDlgItemText(hDlg, ABOUTBOX_Version, programVersion);
\r
6314 Translate(hDlg, ABOUTBOX);
\r
6318 case WM_COMMAND: /* message: received a command */
\r
6319 if (LOWORD(wParam) == IDOK /* "OK" box selected? */
\r
6320 || LOWORD(wParam) == IDCANCEL) { /* System menu close command? */
\r
6321 EndDialog(hDlg, TRUE); /* Exit the dialog */
\r
6329 /*---------------------------------------------------------------------------*\
\r
6331 * Comment Dialog functions
\r
6333 \*---------------------------------------------------------------------------*/
\r
6336 CommentDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
6338 static HANDLE hwndText = NULL;
\r
6339 int len, newSizeX, newSizeY, flags;
\r
6340 static int sizeX, sizeY;
\r
6345 switch (message) {
\r
6346 case WM_INITDIALOG: /* message: initialize dialog box */
\r
6347 /* Initialize the dialog items */
\r
6348 Translate(hDlg, DLG_EditComment);
\r
6349 hwndText = GetDlgItem(hDlg, OPT_CommentText);
\r
6350 SetDlgItemText(hDlg, OPT_CommentText, commentText);
\r
6351 EnableWindow(GetDlgItem(hDlg, OPT_CancelComment), editComment);
\r
6352 EnableWindow(GetDlgItem(hDlg, OPT_ClearComment), editComment);
\r
6353 EnableWindow(GetDlgItem(hDlg, OPT_EditComment), !editComment);
\r
6354 SendMessage(hwndText, EM_SETREADONLY, !editComment, 0);
\r
6355 SetWindowText(hDlg, commentTitle);
\r
6356 if (editComment) {
\r
6357 SetFocus(hwndText);
\r
6359 SetFocus(GetDlgItem(hDlg, IDOK));
\r
6361 SendMessage(GetDlgItem(hDlg, OPT_CommentText),
\r
6362 WM_SETFONT, (WPARAM)font[boardSize][COMMENT_FONT]->hf,
\r
6363 MAKELPARAM(FALSE, 0));
\r
6364 /* Size and position the dialog */
\r
6365 if (!commentDialog) {
\r
6366 commentDialog = hDlg;
\r
6367 flags = SWP_NOZORDER;
\r
6368 GetClientRect(hDlg, &rect);
\r
6369 sizeX = rect.right;
\r
6370 sizeY = rect.bottom;
\r
6371 if (wpComment.x != CW_USEDEFAULT && wpComment.y != CW_USEDEFAULT &&
\r
6372 wpComment.width != CW_USEDEFAULT && wpComment.height != CW_USEDEFAULT) {
\r
6373 WINDOWPLACEMENT wp;
\r
6374 EnsureOnScreen(&wpComment.x, &wpComment.y, 0, 0);
\r
6375 wp.length = sizeof(WINDOWPLACEMENT);
\r
6377 wp.showCmd = SW_SHOW;
\r
6378 wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;
\r
6379 wp.rcNormalPosition.left = wpComment.x;
\r
6380 wp.rcNormalPosition.right = wpComment.x + wpComment.width;
\r
6381 wp.rcNormalPosition.top = wpComment.y;
\r
6382 wp.rcNormalPosition.bottom = wpComment.y + wpComment.height;
\r
6383 SetWindowPlacement(hDlg, &wp);
\r
6385 GetClientRect(hDlg, &rect);
\r
6386 newSizeX = rect.right;
\r
6387 newSizeY = rect.bottom;
\r
6388 ResizeEditPlusButtons(hDlg, hwndText, sizeX, sizeY,
\r
6389 newSizeX, newSizeY);
\r
6394 SendDlgItemMessage( hDlg, OPT_CommentText, EM_SETEVENTMASK, 0, ENM_MOUSEEVENTS | ENM_KEYEVENTS );
\r
6397 case WM_COMMAND: /* message: received a command */
\r
6398 switch (LOWORD(wParam)) {
\r
6400 if (editComment) {
\r
6402 /* Read changed options from the dialog box */
\r
6403 hwndText = GetDlgItem(hDlg, OPT_CommentText);
\r
6404 len = GetWindowTextLength(hwndText);
\r
6405 str = (char *) malloc(len + 1);
\r
6406 GetWindowText(hwndText, str, len + 1);
\r
6415 ReplaceComment(commentIndex, str);
\r
6422 case OPT_CancelComment:
\r
6426 case OPT_ClearComment:
\r
6427 SetDlgItemText(hDlg, OPT_CommentText, "");
\r
6430 case OPT_EditComment:
\r
6431 EditCommentEvent();
\r
6439 case WM_NOTIFY: // [HGM] vari: cloned from whistory.c
\r
6440 if( wParam == OPT_CommentText ) {
\r
6441 MSGFILTER * lpMF = (MSGFILTER *) lParam;
\r
6443 if( lpMF->msg == WM_RBUTTONDOWN && (lpMF->wParam & (MK_CONTROL | MK_SHIFT)) == 0 ||
\r
6444 lpMF->msg == WM_CHAR && lpMF->wParam == '\022' ) {
\r
6448 pt.x = LOWORD( lpMF->lParam );
\r
6449 pt.y = HIWORD( lpMF->lParam );
\r
6451 if(lpMF->msg == WM_CHAR) {
\r
6453 SendDlgItemMessage( hDlg, OPT_CommentText, EM_EXGETSEL, 0, (LPARAM) &sel );
\r
6454 index = sel.cpMin;
\r
6456 index = SendDlgItemMessage( hDlg, OPT_CommentText, EM_CHARFROMPOS, 0, (LPARAM) &pt );
\r
6458 hwndText = GetDlgItem(hDlg, OPT_CommentText); // cloned from above
\r
6459 len = GetWindowTextLength(hwndText);
\r
6460 str = (char *) malloc(len + 1);
\r
6461 GetWindowText(hwndText, str, len + 1);
\r
6462 ReplaceComment(commentIndex, str);
\r
6463 if(commentIndex != currentMove) ToNrEvent(commentIndex);
\r
6464 LoadVariation( index, str ); // [HGM] also does the actual moving to it, now
\r
6467 /* Zap the message for good: apparently, returning non-zero is not enough */
\r
6468 lpMF->msg = WM_USER;
\r
6476 newSizeX = LOWORD(lParam);
\r
6477 newSizeY = HIWORD(lParam);
\r
6478 ResizeEditPlusButtons(hDlg, hwndText, sizeX, sizeY, newSizeX, newSizeY);
\r
6483 case WM_GETMINMAXINFO:
\r
6484 /* Prevent resizing window too small */
\r
6485 mmi = (MINMAXINFO *) lParam;
\r
6486 mmi->ptMinTrackSize.x = 100;
\r
6487 mmi->ptMinTrackSize.y = 100;
\r
6494 EitherCommentPopUp(int index, char *title, char *str, BOOLEAN edit)
\r
6499 CheckMenuItem(GetMenu(hwndMain), IDM_Comment, edit ? MF_CHECKED : MF_UNCHECKED);
\r
6501 if (str == NULL) str = "";
\r
6502 p = (char *) malloc(2 * strlen(str) + 2);
\r
6505 if (*str == '\n') *q++ = '\r';
\r
6509 if (commentText != NULL) free(commentText);
\r
6511 commentIndex = index;
\r
6512 commentTitle = title;
\r
6514 editComment = edit;
\r
6516 if (commentDialog) {
\r
6517 SendMessage(commentDialog, WM_INITDIALOG, 0, 0);
\r
6518 if (!commentUp) ShowWindow(commentDialog, SW_SHOW);
\r
6520 lpProc = MakeProcInstance((FARPROC)CommentDialog, hInst);
\r
6521 CreateDialog(hInst, MAKEINTRESOURCE(DLG_EditComment),
\r
6522 hwndMain, (DLGPROC)lpProc);
\r
6523 FreeProcInstance(lpProc);
\r
6529 /*---------------------------------------------------------------------------*\
\r
6531 * Type-in move dialog functions
\r
6533 \*---------------------------------------------------------------------------*/
\r
6536 TypeInMoveDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
6538 char move[MSG_SIZ];
\r
6541 switch (message) {
\r
6542 case WM_INITDIALOG:
\r
6543 move[0] = (char) lParam;
\r
6544 move[1] = NULLCHAR;
\r
6545 CenterWindowEx(hDlg, GetWindow(hDlg, GW_OWNER), 1 );
\r
6546 Translate(hDlg, DLG_TypeInMove);
\r
6547 hInput = GetDlgItem(hDlg, OPT_Move);
\r
6548 SetWindowText(hInput, move);
\r
6550 SendMessage(hInput, EM_SETSEL, (WPARAM)9999, (LPARAM)9999);
\r
6554 switch (LOWORD(wParam)) {
\r
6557 shiftKey = GetKeyState(VK_SHIFT) < 0; // [HGM] remember last shift status
\r
6558 GetDlgItemText(hDlg, OPT_Move, move, sizeof(move));
\r
6559 TypeInDoneEvent(move);
\r
6560 EndDialog(hDlg, TRUE);
\r
6563 EndDialog(hDlg, FALSE);
\r
6574 PopUpMoveDialog(char firstchar)
\r
6578 lpProc = MakeProcInstance((FARPROC)TypeInMoveDialog, hInst);
\r
6579 DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_TypeInMove),
\r
6580 hwndMain, (DLGPROC)lpProc, (LPARAM)firstchar);
\r
6581 FreeProcInstance(lpProc);
\r
6584 /*---------------------------------------------------------------------------*\
\r
6586 * Type-in name dialog functions
\r
6588 \*---------------------------------------------------------------------------*/
\r
6591 TypeInNameDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
6593 char move[MSG_SIZ];
\r
6596 switch (message) {
\r
6597 case WM_INITDIALOG:
\r
6598 move[0] = (char) lParam;
\r
6599 move[1] = NULLCHAR;
\r
6600 CenterWindowEx(hDlg, GetWindow(hDlg, GW_OWNER), 1 );
\r
6601 Translate(hDlg, DLG_TypeInName);
\r
6602 hInput = GetDlgItem(hDlg, OPT_Name);
\r
6603 SetWindowText(hInput, move);
\r
6605 SendMessage(hInput, EM_SETSEL, (WPARAM)9999, (LPARAM)9999);
\r
6609 switch (LOWORD(wParam)) {
\r
6611 GetDlgItemText(hDlg, OPT_Name, move, sizeof(move));
\r
6612 appData.userName = strdup(move);
\r
6615 if(gameMode == MachinePlaysWhite || gameMode == MachinePlaysBlack) {
\r
6616 snprintf(move, MSG_SIZ, "%s vs. %s", gameInfo.white, gameInfo.black);
\r
6617 DisplayTitle(move);
\r
6621 EndDialog(hDlg, TRUE);
\r
6624 EndDialog(hDlg, FALSE);
\r
6635 PopUpNameDialog(char firstchar)
\r
6639 lpProc = MakeProcInstance((FARPROC)TypeInNameDialog, hInst);
\r
6640 DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_TypeInName),
\r
6641 hwndMain, (DLGPROC)lpProc, (LPARAM)firstchar);
\r
6642 FreeProcInstance(lpProc);
\r
6645 /*---------------------------------------------------------------------------*\
\r
6649 \*---------------------------------------------------------------------------*/
\r
6651 /* Nonmodal error box */
\r
6652 LRESULT CALLBACK ErrorDialog(HWND hDlg, UINT message,
\r
6653 WPARAM wParam, LPARAM lParam);
\r
6656 ErrorPopUp(char *title, char *content)
\r
6660 BOOLEAN modal = hwndMain == NULL;
\r
6678 strncpy(errorTitle, title, sizeof(errorTitle));
\r
6679 errorTitle[sizeof(errorTitle) - 1] = '\0';
\r
6682 MessageBox(NULL, errorMessage, errorTitle, MB_OK|MB_ICONEXCLAMATION);
\r
6684 lpProc = MakeProcInstance((FARPROC)ErrorDialog, hInst);
\r
6685 CreateDialog(hInst, MAKEINTRESOURCE(DLG_Error),
\r
6686 hwndMain, (DLGPROC)lpProc);
\r
6687 FreeProcInstance(lpProc);
\r
6694 if (!appData.popupMoveErrors && moveErrorMessageUp) DisplayMessage("", "");
\r
6695 if (errorDialog == NULL) return;
\r
6696 DestroyWindow(errorDialog);
\r
6697 errorDialog = NULL;
\r
6698 if(errorExitStatus) ExitEvent(errorExitStatus);
\r
6702 ErrorDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
6707 switch (message) {
\r
6708 case WM_INITDIALOG:
\r
6709 GetWindowRect(hDlg, &rChild);
\r
6712 SetWindowPos(hDlg, NULL, rChild.left,
\r
6713 rChild.top + boardRect.top - (rChild.bottom - rChild.top),
\r
6714 0, 0, SWP_NOZORDER|SWP_NOSIZE);
\r
6718 [AS] It seems that the above code wants to move the dialog up in the "caption
\r
6719 area" of the main window, but it uses the dialog height as an hard-coded constant,
\r
6720 and it doesn't work when you resize the dialog.
\r
6721 For now, just give it a default position.
\r
6723 SetWindowPos(hDlg, NULL, boardRect.left+8, boardRect.top+8, 0, 0, SWP_NOZORDER|SWP_NOSIZE);
\r
6724 Translate(hDlg, DLG_Error);
\r
6726 errorDialog = hDlg;
\r
6727 SetWindowText(hDlg, errorTitle);
\r
6728 hwndText = GetDlgItem(hDlg, OPT_ErrorText);
\r
6729 SetDlgItemText(hDlg, OPT_ErrorText, errorMessage);
\r
6733 switch (LOWORD(wParam)) {
\r
6736 if (errorDialog == hDlg) errorDialog = NULL;
\r
6737 DestroyWindow(hDlg);
\r
6749 HWND gothicDialog = NULL;
\r
6752 GothicDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
6756 int height = GetSystemMetrics(SM_CYCAPTION)+GetSystemMetrics(SM_CYFRAME);
\r
6758 switch (message) {
\r
6759 case WM_INITDIALOG:
\r
6760 GetWindowRect(hDlg, &rChild);
\r
6762 SetWindowPos(hDlg, NULL, wpMain.x, wpMain.y-height, wpMain.width, height,
\r
6766 [AS] It seems that the above code wants to move the dialog up in the "caption
\r
6767 area" of the main window, but it uses the dialog height as an hard-coded constant,
\r
6768 and it doesn't work when you resize the dialog.
\r
6769 For now, just give it a default position.
\r
6771 gothicDialog = hDlg;
\r
6772 SetWindowText(hDlg, errorTitle);
\r
6773 hwndText = GetDlgItem(hDlg, OPT_ErrorText);
\r
6774 SetDlgItemText(hDlg, OPT_ErrorText, errorMessage);
\r
6778 switch (LOWORD(wParam)) {
\r
6781 if (errorDialog == hDlg) errorDialog = NULL;
\r
6782 DestroyWindow(hDlg);
\r
6794 GothicPopUp(char *title, VariantClass variant)
\r
6797 static char *lastTitle;
\r
6799 strncpy(errorTitle, title, sizeof(errorTitle));
\r
6800 errorTitle[sizeof(errorTitle) - 1] = '\0';
\r
6802 if(lastTitle != title && gothicDialog != NULL) {
\r
6803 DestroyWindow(gothicDialog);
\r
6804 gothicDialog = NULL;
\r
6806 if(variant != VariantNormal && gothicDialog == NULL) {
\r
6807 title = lastTitle;
\r
6808 lpProc = MakeProcInstance((FARPROC)GothicDialog, hInst);
\r
6809 CreateDialog(hInst, MAKEINTRESOURCE(DLG_Error),
\r
6810 hwndMain, (DLGPROC)lpProc);
\r
6811 FreeProcInstance(lpProc);
\r
6816 /*---------------------------------------------------------------------------*\
\r
6818 * Ics Interaction console functions
\r
6820 \*---------------------------------------------------------------------------*/
\r
6822 #define HISTORY_SIZE 64
\r
6823 static char *history[HISTORY_SIZE];
\r
6824 int histIn = 0, histP = 0;
\r
6827 SaveInHistory(char *cmd)
\r
6829 if (history[histIn] != NULL) {
\r
6830 free(history[histIn]);
\r
6831 history[histIn] = NULL;
\r
6833 if (*cmd == NULLCHAR) return;
\r
6834 history[histIn] = StrSave(cmd);
\r
6835 histIn = (histIn + 1) % HISTORY_SIZE;
\r
6836 if (history[histIn] != NULL) {
\r
6837 free(history[histIn]);
\r
6838 history[histIn] = NULL;
\r
6844 PrevInHistory(char *cmd)
\r
6847 if (histP == histIn) {
\r
6848 if (history[histIn] != NULL) free(history[histIn]);
\r
6849 history[histIn] = StrSave(cmd);
\r
6851 newhp = (histP - 1 + HISTORY_SIZE) % HISTORY_SIZE;
\r
6852 if (newhp == histIn || history[newhp] == NULL) return NULL;
\r
6854 return history[histP];
\r
6860 if (histP == histIn) return NULL;
\r
6861 histP = (histP + 1) % HISTORY_SIZE;
\r
6862 return history[histP];
\r
6866 LoadIcsTextMenu(IcsTextMenuEntry *e)
\r
6870 hmenu = LoadMenu(hInst, "TextMenu");
\r
6871 h = GetSubMenu(hmenu, 0);
\r
6873 if (strcmp(e->item, "-") == 0) {
\r
6874 AppendMenu(h, MF_SEPARATOR, 0, 0);
\r
6875 } else { // [HGM] re-written a bit to use only one AppendMenu call for both cases (| or no |)
\r
6876 int flags = MF_STRING, j = 0;
\r
6877 if (e->item[0] == '|') {
\r
6878 flags |= MF_MENUBARBREAK;
\r
6881 if(!strcmp(e->command, "none")) flags |= MF_GRAYED; // [HGM] chatclick: provide inactive dummy
\r
6882 AppendMenu(h, flags, IDM_CommandX + i, e->item + j);
\r
6890 WNDPROC consoleTextWindowProc;
\r
6893 CommandX(HWND hwnd, char *command, BOOLEAN getname, BOOLEAN immediate)
\r
6895 char buf[MSG_SIZ], name[MSG_SIZ];
\r
6896 HWND hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
6900 SetWindowText(hInput, command);
\r
6902 SendMessage(hInput, WM_CHAR, '\r', 0);
\r
6904 sel.cpMin = 999999;
\r
6905 sel.cpMax = 999999;
\r
6906 SendMessage(hInput, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
6911 SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);
\r
6912 if (sel.cpMin == sel.cpMax) {
\r
6913 /* Expand to surrounding word */
\r
6916 tr.chrg.cpMax = sel.cpMin;
\r
6917 tr.chrg.cpMin = --sel.cpMin;
\r
6918 if (sel.cpMin < 0) break;
\r
6919 tr.lpstrText = name;
\r
6920 SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr);
\r
6921 } while (isalpha(name[0]) || isdigit(name[0]) || name[0] == '-');
\r
6925 tr.chrg.cpMin = sel.cpMax;
\r
6926 tr.chrg.cpMax = ++sel.cpMax;
\r
6927 tr.lpstrText = name;
\r
6928 if (SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr) < 1) break;
\r
6929 } while (isalpha(name[0]) || isdigit(name[0]) || name[0] == '-');
\r
6932 if (sel.cpMax == sel.cpMin || sel.cpMax - sel.cpMin > MSG_SIZ/2) {
\r
6933 MessageBeep(MB_ICONEXCLAMATION);
\r
6937 tr.lpstrText = name;
\r
6938 SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr);
\r
6940 if (sel.cpMax - sel.cpMin > MSG_SIZ/2) {
\r
6941 MessageBeep(MB_ICONEXCLAMATION);
\r
6944 SendMessage(hwnd, EM_GETSELTEXT, 0, (LPARAM) name);
\r
6947 if(strstr(command, "%s")) snprintf(buf, MSG_SIZ, command, name); else
\r
6948 snprintf(buf, MSG_SIZ, "%s %s", command, name);
\r
6949 SetWindowText(hInput, buf);
\r
6950 SendMessage(hInput, WM_CHAR, '\r', 0);
\r
6952 if(!strcmp(command, "chat")) { ChatPopUp(name); return; }
\r
6953 snprintf(buf, MSG_SIZ, "%s %s ", command, name); /* trailing space */
\r
6954 SetWindowText(hInput, buf);
\r
6955 sel.cpMin = 999999;
\r
6956 sel.cpMax = 999999;
\r
6957 SendMessage(hInput, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
6963 ConsoleTextSubclass(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
\r
6968 switch (message) {
\r
6970 if (!(GetKeyState(VK_CONTROL) & ~1)) break;
\r
6971 if(wParam=='R') return 0;
\r
6974 SendMessage(hwnd, EM_LINESCROLL, 0, -999999);
\r
6977 sel.cpMin = 999999;
\r
6978 sel.cpMax = 999999;
\r
6979 SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
6980 SendMessage(hwnd, EM_SCROLLCARET, 0, 0);
\r
6985 if(wParam != '\022') {
\r
6986 if (wParam == '\t') {
\r
6987 if (GetKeyState(VK_SHIFT) < 0) {
\r
6989 if (IsIconic(hwndMain)) ShowWindow(hwndMain, SW_RESTORE);
\r
6990 if (buttonDesc[0].hwnd) {
\r
6991 SetFocus(buttonDesc[0].hwnd);
\r
6993 SetFocus(hwndMain);
\r
6997 SetFocus(GetDlgItem(hwndConsole, OPT_ConsoleInput));
\r
7000 hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
7001 JAWS_DELETE( SetFocus(hInput); )
\r
7002 SendMessage(hInput, message, wParam, lParam);
\r
7005 } // [HGM] navigate: for Ctrl+R, flow into next case (moved up here) to summon up menu
\r
7007 case WM_RBUTTONDOWN:
\r
7008 if (!(GetKeyState(VK_SHIFT) & ~1)) {
\r
7009 /* Move selection here if it was empty */
\r
7011 pt.x = LOWORD(lParam);
\r
7012 pt.y = HIWORD(lParam);
\r
7013 SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);
\r
7014 if (sel.cpMin == sel.cpMax) {
\r
7015 if(lParam != -1) sel.cpMin = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&pt); /*doc is wrong*/
\r
7016 sel.cpMax = sel.cpMin;
\r
7017 SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
7019 SendMessage(hwnd, EM_HIDESELECTION, FALSE, FALSE);
\r
7020 { // [HGM] chatclick: code moved here from WM_RBUTTONUP case, to have menu appear on down-click
\r
7022 HMENU hmenu = LoadIcsTextMenu(icsTextMenuEntry);
\r
7023 SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);
\r
7024 if (sel.cpMin == sel.cpMax) {
\r
7025 EnableMenuItem(hmenu, IDM_Copy, MF_BYCOMMAND|MF_GRAYED);
\r
7026 EnableMenuItem(hmenu, IDM_QuickPaste, MF_BYCOMMAND|MF_GRAYED);
\r
7028 if (!IsClipboardFormatAvailable(CF_TEXT)) {
\r
7029 EnableMenuItem(hmenu, IDM_Paste, MF_BYCOMMAND|MF_GRAYED);
\r
7031 pt.x = LOWORD(lParam)-30; // [HGM] chatclick: make menu pop up with pointer above upper-right item
\r
7032 pt.y = HIWORD(lParam)-10; // make it appear as if mouse moved there, so it will be selected on up-click
\r
7033 PostMessage(hwnd, WM_MOUSEMOVE, wParam, lParam+5);
\r
7034 MenuPopup(hwnd, pt, hmenu, -1);
\r
7038 case WM_RBUTTONUP:
\r
7039 if (GetKeyState(VK_SHIFT) & ~1) {
\r
7040 SendDlgItemMessage(hwndConsole, OPT_ConsoleText,
\r
7041 WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);
\r
7045 hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
7047 return SendMessage(hInput, message, wParam, lParam);
\r
7048 case WM_MBUTTONDOWN:
\r
7049 return SendMessage(hwnd, WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);
\r
7051 switch (LOWORD(wParam)) {
\r
7052 case IDM_QuickPaste:
\r
7054 SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);
\r
7055 if (sel.cpMin == sel.cpMax) {
\r
7056 MessageBeep(MB_ICONEXCLAMATION);
\r
7059 SendMessage(hwnd, WM_COPY, 0, 0);
\r
7060 hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
7061 SendMessage(hInput, WM_PASTE, 0, 0);
\r
7066 SendMessage(hwnd, WM_CUT, 0, 0);
\r
7069 SendMessage(hwnd, WM_PASTE, 0, 0);
\r
7072 SendMessage(hwnd, WM_COPY, 0, 0);
\r
7076 int i = LOWORD(wParam) - IDM_CommandX;
\r
7077 if (i >= 0 && i < ICS_TEXT_MENU_SIZE &&
\r
7078 icsTextMenuEntry[i].command != NULL) {
\r
7079 CommandX(hwnd, icsTextMenuEntry[i].command,
\r
7080 icsTextMenuEntry[i].getname,
\r
7081 icsTextMenuEntry[i].immediate);
\r
7089 return (*consoleTextWindowProc)(hwnd, message, wParam, lParam);
\r
7092 WNDPROC consoleInputWindowProc;
\r
7095 ConsoleInputSubclass(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
\r
7097 char buf[MSG_SIZ];
\r
7099 static BOOL sendNextChar = FALSE;
\r
7100 static BOOL quoteNextChar = FALSE;
\r
7101 InputSource *is = consoleInputSource;
\r
7105 switch (message) {
\r
7107 if (!appData.localLineEditing || sendNextChar) {
\r
7108 is->buf[0] = (CHAR) wParam;
\r
7110 SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);
\r
7111 sendNextChar = FALSE;
\r
7114 if (quoteNextChar) {
\r
7115 buf[0] = (char) wParam;
\r
7116 buf[1] = NULLCHAR;
\r
7117 SendMessage(hwnd, EM_REPLACESEL, TRUE, (LPARAM) buf);
\r
7118 quoteNextChar = FALSE;
\r
7122 case '\r': /* Enter key */
\r
7123 is->count = GetWindowText(hwnd, is->buf, INPUT_SOURCE_BUF_SIZE-1);
\r
7124 if (consoleEcho) SaveInHistory(is->buf);
\r
7125 is->buf[is->count++] = '\n';
\r
7126 is->buf[is->count] = NULLCHAR;
\r
7127 SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);
\r
7128 if (consoleEcho) {
\r
7129 ConsoleOutput(is->buf, is->count, TRUE);
\r
7130 } else if (appData.localLineEditing) {
\r
7131 ConsoleOutput("\n", 1, TRUE);
\r
7134 case '\033': /* Escape key */
\r
7135 SetWindowText(hwnd, "");
\r
7136 cf.cbSize = sizeof(CHARFORMAT);
\r
7137 cf.dwMask = CFM_COLOR|CFM_BOLD|CFM_ITALIC|CFM_UNDERLINE|CFM_STRIKEOUT;
\r
7138 if (consoleEcho) {
\r
7139 cf.crTextColor = textAttribs[ColorNormal].color;
\r
7141 cf.crTextColor = COLOR_ECHOOFF;
\r
7143 cf.dwEffects = textAttribs[ColorNormal].effects;
\r
7144 SendMessage(hwnd, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf);
\r
7146 case '\t': /* Tab key */
\r
7147 if (GetKeyState(VK_SHIFT) < 0) {
\r
7149 SetFocus(GetDlgItem(hwndConsole, OPT_ConsoleText));
\r
7152 if (IsIconic(hwndMain)) ShowWindow(hwndMain, SW_RESTORE);
\r
7153 if (buttonDesc[0].hwnd) {
\r
7154 SetFocus(buttonDesc[0].hwnd);
\r
7156 SetFocus(hwndMain);
\r
7160 case '\023': /* Ctrl+S */
\r
7161 sendNextChar = TRUE;
\r
7163 case '\021': /* Ctrl+Q */
\r
7164 quoteNextChar = TRUE;
\r
7174 GetWindowText(hwnd, buf, MSG_SIZ);
\r
7175 p = PrevInHistory(buf);
\r
7177 SetWindowText(hwnd, p);
\r
7178 sel.cpMin = 999999;
\r
7179 sel.cpMax = 999999;
\r
7180 SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
7185 p = NextInHistory();
\r
7187 SetWindowText(hwnd, p);
\r
7188 sel.cpMin = 999999;
\r
7189 sel.cpMax = 999999;
\r
7190 SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
7196 if (!(GetKeyState(VK_CONTROL) & ~1)) break;
\r
7200 SendDlgItemMessage(hwndConsole, OPT_ConsoleText, message, wParam, lParam);
\r
7204 case WM_MBUTTONDOWN:
\r
7205 SendDlgItemMessage(hwndConsole, OPT_ConsoleText,
\r
7206 WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);
\r
7208 case WM_RBUTTONUP:
\r
7209 if (GetKeyState(VK_SHIFT) & ~1) {
\r
7210 SendDlgItemMessage(hwndConsole, OPT_ConsoleText,
\r
7211 WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);
\r
7215 hmenu = LoadMenu(hInst, "InputMenu");
\r
7216 SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);
\r
7217 if (sel.cpMin == sel.cpMax) {
\r
7218 EnableMenuItem(hmenu, IDM_Copy, MF_BYCOMMAND|MF_GRAYED);
\r
7219 EnableMenuItem(hmenu, IDM_Cut, MF_BYCOMMAND|MF_GRAYED);
\r
7221 if (!IsClipboardFormatAvailable(CF_TEXT)) {
\r
7222 EnableMenuItem(hmenu, IDM_Paste, MF_BYCOMMAND|MF_GRAYED);
\r
7224 pt.x = LOWORD(lParam);
\r
7225 pt.y = HIWORD(lParam);
\r
7226 MenuPopup(hwnd, pt, hmenu, -1);
\r
7230 switch (LOWORD(wParam)) {
\r
7232 SendMessage(hwnd, EM_UNDO, 0, 0);
\r
7234 case IDM_SelectAll:
\r
7236 sel.cpMax = -1; /*999999?*/
\r
7237 SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
7240 SendMessage(hwnd, WM_CUT, 0, 0);
\r
7243 SendMessage(hwnd, WM_PASTE, 0, 0);
\r
7246 SendMessage(hwnd, WM_COPY, 0, 0);
\r
7251 return (*consoleInputWindowProc)(hwnd, message, wParam, lParam);
\r
7254 #define CO_MAX 100000
\r
7255 #define CO_TRIM 1000
\r
7258 ConsoleWndProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
7260 static SnapData sd;
\r
7261 HWND hText, hInput;
\r
7263 static int sizeX, sizeY;
\r
7264 int newSizeX, newSizeY;
\r
7268 hText = GetDlgItem(hDlg, OPT_ConsoleText);
\r
7269 hInput = GetDlgItem(hDlg, OPT_ConsoleInput);
\r
7271 switch (message) {
\r
7273 if (((NMHDR*)lParam)->code == EN_LINK)
\r
7275 ENLINK *pLink = (ENLINK*)lParam;
\r
7276 if (pLink->msg == WM_LBUTTONUP)
\r
7280 tr.chrg = pLink->chrg;
\r
7281 tr.lpstrText = malloc(1+tr.chrg.cpMax-tr.chrg.cpMin);
\r
7282 SendMessage(hText, EM_GETTEXTRANGE, 0, (LPARAM)&tr);
\r
7283 ShellExecute(NULL, "open", tr.lpstrText, NULL, NULL, SW_SHOW);
\r
7284 free(tr.lpstrText);
\r
7288 case WM_INITDIALOG: /* message: initialize dialog box */
\r
7289 hwndConsole = hDlg;
\r
7291 consoleTextWindowProc = (WNDPROC)
\r
7292 SetWindowLongPtr(hText, GWLP_WNDPROC, (LONG_PTR) ConsoleTextSubclass);
\r
7293 SendMessage(hText, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);
\r
7294 consoleInputWindowProc = (WNDPROC)
\r
7295 SetWindowLongPtr(hInput, GWLP_WNDPROC, (LONG_PTR) ConsoleInputSubclass);
\r
7296 SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);
\r
7297 Colorize(ColorNormal, TRUE);
\r
7298 SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &consoleCF);
\r
7299 ChangedConsoleFont();
\r
7300 GetClientRect(hDlg, &rect);
\r
7301 sizeX = rect.right;
\r
7302 sizeY = rect.bottom;
\r
7303 if (wpConsole.x != CW_USEDEFAULT && wpConsole.y != CW_USEDEFAULT &&
\r
7304 wpConsole.width != CW_USEDEFAULT && wpConsole.height != CW_USEDEFAULT) {
\r
7305 WINDOWPLACEMENT wp;
\r
7306 EnsureOnScreen(&wpConsole.x, &wpConsole.y, 0, 0);
\r
7307 wp.length = sizeof(WINDOWPLACEMENT);
\r
7309 wp.showCmd = SW_SHOW;
\r
7310 wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;
\r
7311 wp.rcNormalPosition.left = wpConsole.x;
\r
7312 wp.rcNormalPosition.right = wpConsole.x + wpConsole.width;
\r
7313 wp.rcNormalPosition.top = wpConsole.y;
\r
7314 wp.rcNormalPosition.bottom = wpConsole.y + wpConsole.height;
\r
7315 SetWindowPlacement(hDlg, &wp);
\r
7318 // [HGM] Chessknight's change 2004-07-13
\r
7319 else { /* Determine Defaults */
\r
7320 WINDOWPLACEMENT wp;
\r
7321 wpConsole.x = wpMain.width + 1;
\r
7322 wpConsole.y = wpMain.y;
\r
7323 wpConsole.width = screenWidth - wpMain.width;
\r
7324 wpConsole.height = wpMain.height;
\r
7325 EnsureOnScreen(&wpConsole.x, &wpConsole.y, 0, 0);
\r
7326 wp.length = sizeof(WINDOWPLACEMENT);
\r
7328 wp.showCmd = SW_SHOW;
\r
7329 wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;
\r
7330 wp.rcNormalPosition.left = wpConsole.x;
\r
7331 wp.rcNormalPosition.right = wpConsole.x + wpConsole.width;
\r
7332 wp.rcNormalPosition.top = wpConsole.y;
\r
7333 wp.rcNormalPosition.bottom = wpConsole.y + wpConsole.height;
\r
7334 SetWindowPlacement(hDlg, &wp);
\r
7337 // Allow hText to highlight URLs and send notifications on them
\r
7338 wMask = (WORD) SendMessage(hText, EM_GETEVENTMASK, 0, 0L);
\r
7339 SendMessage(hText, EM_SETEVENTMASK, 0, wMask | ENM_LINK);
\r
7340 SendMessage(hText, EM_AUTOURLDETECT, TRUE, 0L);
\r
7341 SetWindowLongPtr(hText, GWLP_USERDATA, 79); // initialize the text window's width
\r
7355 if (IsIconic(hDlg)) break;
\r
7356 newSizeX = LOWORD(lParam);
\r
7357 newSizeY = HIWORD(lParam);
\r
7358 if (sizeX != newSizeX || sizeY != newSizeY) {
\r
7359 RECT rectText, rectInput;
\r
7361 int newTextHeight, newTextWidth;
\r
7362 GetWindowRect(hText, &rectText);
\r
7363 newTextWidth = rectText.right - rectText.left + newSizeX - sizeX;
\r
7364 newTextHeight = rectText.bottom - rectText.top + newSizeY - sizeY;
\r
7365 if (newTextHeight < 0) {
\r
7366 newSizeY += -newTextHeight;
\r
7367 newTextHeight = 0;
\r
7369 SetWindowPos(hText, NULL, 0, 0,
\r
7370 newTextWidth, newTextHeight, SWP_NOZORDER|SWP_NOMOVE);
\r
7371 GetWindowRect(hInput, &rectInput); /* gives screen coords */
\r
7372 pt.x = rectInput.left;
\r
7373 pt.y = rectInput.top + newSizeY - sizeY;
\r
7374 ScreenToClient(hDlg, &pt);
\r
7375 SetWindowPos(hInput, NULL,
\r
7376 pt.x, pt.y, /* needs client coords */
\r
7377 rectInput.right - rectInput.left + newSizeX - sizeX,
\r
7378 rectInput.bottom - rectInput.top, SWP_NOZORDER);
\r
7384 case WM_GETMINMAXINFO:
\r
7385 /* Prevent resizing window too small */
\r
7386 mmi = (MINMAXINFO *) lParam;
\r
7387 mmi->ptMinTrackSize.x = 100;
\r
7388 mmi->ptMinTrackSize.y = 100;
\r
7391 /* [AS] Snapping */
\r
7392 case WM_ENTERSIZEMOVE:
\r
7393 return OnEnterSizeMove( &sd, hDlg, wParam, lParam );
\r
7396 return OnSizing( &sd, hDlg, wParam, lParam );
\r
7399 return OnMoving( &sd, hDlg, wParam, lParam );
\r
7401 case WM_EXITSIZEMOVE:
\r
7402 UpdateICSWidth(hText);
\r
7403 return OnExitSizeMove( &sd, hDlg, wParam, lParam );
\r
7406 return DefWindowProc(hDlg, message, wParam, lParam);
\r
7414 if (hwndConsole) return;
\r
7415 hCons = CreateDialog(hInst, szConsoleName, 0, NULL);
\r
7416 SendMessage(hCons, WM_INITDIALOG, 0, 0);
\r
7421 ConsoleOutput(char* data, int length, int forceVisible)
\r
7426 char buf[CO_MAX+1];
\r
7429 static int delayLF = 0;
\r
7430 CHARRANGE savesel, sel;
\r
7432 if (hwndConsole == NULL || length > CO_MAX-100 || length == 0) return;
\r
7440 while (length--) {
\r
7448 } else if (*p == '\007') {
\r
7449 MyPlaySound(&sounds[(int)SoundBell]);
\r
7456 hText = GetDlgItem(hwndConsole, OPT_ConsoleText);
\r
7457 SendMessage(hText, EM_HIDESELECTION, TRUE, FALSE);
\r
7458 /* Save current selection */
\r
7459 SendMessage(hText, EM_EXGETSEL, 0, (LPARAM)&savesel);
\r
7460 exlen = GetWindowTextLength(hText);
\r
7461 /* Find out whether current end of text is visible */
\r
7462 SendMessage(hText, EM_GETRECT, 0, (LPARAM) &rect);
\r
7463 SendMessage(hText, EM_POSFROMCHAR, (WPARAM) &pEnd, exlen);
\r
7464 /* Trim existing text if it's too long */
\r
7465 if (exlen + (q - buf) > CO_MAX) {
\r
7466 trim = (CO_TRIM > (q - buf)) ? CO_TRIM : (q - buf);
\r
7469 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
7470 SendMessage(hText, EM_REPLACESEL, 0, (LPARAM)"");
\r
7472 savesel.cpMin -= trim;
\r
7473 savesel.cpMax -= trim;
\r
7474 if (exlen < 0) exlen = 0;
\r
7475 if (savesel.cpMin < 0) savesel.cpMin = 0;
\r
7476 if (savesel.cpMax < savesel.cpMin) savesel.cpMax = savesel.cpMin;
\r
7478 /* Append the new text */
\r
7479 sel.cpMin = exlen;
\r
7480 sel.cpMax = exlen;
\r
7481 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
7482 SendMessage(hText, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&consoleCF);
\r
7483 SendMessage(hText, EM_REPLACESEL, 0, (LPARAM) buf);
\r
7484 if (forceVisible || exlen == 0 ||
\r
7485 (rect.left <= pEnd.x && pEnd.x < rect.right &&
\r
7486 rect.top <= pEnd.y && pEnd.y < rect.bottom)) {
\r
7487 /* Scroll to make new end of text visible if old end of text
\r
7488 was visible or new text is an echo of user typein */
\r
7489 sel.cpMin = 9999999;
\r
7490 sel.cpMax = 9999999;
\r
7491 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
7492 SendMessage(hText, EM_HIDESELECTION, FALSE, FALSE);
\r
7493 SendMessage(hText, EM_SCROLLCARET, 0, 0);
\r
7494 SendMessage(hText, EM_HIDESELECTION, TRUE, FALSE);
\r
7496 if (savesel.cpMax == exlen || forceVisible) {
\r
7497 /* Move insert point to new end of text if it was at the old
\r
7498 end of text or if the new text is an echo of user typein */
\r
7499 sel.cpMin = 9999999;
\r
7500 sel.cpMax = 9999999;
\r
7501 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
7503 /* Restore previous selection */
\r
7504 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&savesel);
\r
7506 SendMessage(hText, EM_HIDESELECTION, FALSE, FALSE);
\r
7513 DisplayHoldingsCount(HDC hdc, int x, int y, int rightAlign, int copyNumber)
\r
7517 COLORREF oldFg, oldBg;
\r
7521 if(copyNumber > 1)
\r
7522 snprintf(buf, sizeof(buf)/sizeof(buf[0]),"%d", copyNumber); else buf[0] = 0;
\r
7524 oldFg = SetTextColor(hdc, RGB(255, 255, 255)); /* white */
\r
7525 oldBg = SetBkColor(hdc, RGB(0, 0, 0)); /* black */
\r
7526 oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);
\r
7529 rect.right = x + squareSize;
\r
7531 rect.bottom = y + squareSize;
\r
7534 ExtTextOut(hdc, x + MESSAGE_LINE_LEFTMARGIN
\r
7535 + (rightAlign ? (squareSize*2)/3 : 0),
\r
7536 y, ETO_CLIPPED|ETO_OPAQUE,
\r
7537 &rect, str, strlen(str), NULL);
\r
7539 (void) SetTextColor(hdc, oldFg);
\r
7540 (void) SetBkColor(hdc, oldBg);
\r
7541 (void) SelectObject(hdc, oldFont);
\r
7545 DisplayAClock(HDC hdc, int timeRemaining, int highlight,
\r
7546 RECT *rect, char *color, char *flagFell)
\r
7550 COLORREF oldFg, oldBg;
\r
7553 if (twoBoards && partnerUp) return;
\r
7554 if (appData.clockMode) {
\r
7556 snprintf(buf, sizeof(buf)/sizeof(buf[0]), "%c %s %s", color[0], TimeString(timeRemaining), flagFell);
\r
7558 snprintf(buf, sizeof(buf)/sizeof(buf[0]), "%s:%c%s %s", color, (logoHeight>0 ? 0 : ' '), TimeString(timeRemaining), flagFell);
\r
7565 oldFg = SetTextColor(hdc, RGB(255, 255, 255)); /* white */
\r
7566 oldBg = SetBkColor(hdc, RGB(0, 0, 0)); /* black */
\r
7568 oldFg = SetTextColor(hdc, RGB(0, 0, 0)); /* black */
\r
7569 oldBg = SetBkColor(hdc, RGB(255, 255, 255)); /* white */
\r
7571 oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);
\r
7575 ExtTextOut(hdc, rect->left + MESSAGE_LINE_LEFTMARGIN,
\r
7576 rect->top, ETO_CLIPPED|ETO_OPAQUE,
\r
7577 rect, str, strlen(str), NULL);
\r
7578 if(logoHeight > 0 && appData.clockMode) {
\r
7580 str += strlen(color)+2;
\r
7581 r.top = rect->top + logoHeight/2;
\r
7582 r.left = rect->left;
\r
7583 r.right = rect->right;
\r
7584 r.bottom = rect->bottom;
\r
7585 ExtTextOut(hdc, rect->left + MESSAGE_LINE_LEFTMARGIN,
\r
7586 r.top, ETO_CLIPPED|ETO_OPAQUE,
\r
7587 &r, str, strlen(str), NULL);
\r
7589 (void) SetTextColor(hdc, oldFg);
\r
7590 (void) SetBkColor(hdc, oldBg);
\r
7591 (void) SelectObject(hdc, oldFont);
\r
7596 DoReadFile(HANDLE hFile, char *buf, int count, DWORD *outCount,
\r
7602 if( count <= 0 ) {
\r
7603 if (appData.debugMode) {
\r
7604 fprintf( debugFP, "DoReadFile: trying to read past end of buffer, overflow = %d\n", count );
\r
7607 return ERROR_INVALID_USER_BUFFER;
\r
7610 ResetEvent(ovl->hEvent);
\r
7611 ovl->Offset = ovl->OffsetHigh = 0;
\r
7612 ok = ReadFile(hFile, buf, count, outCount, ovl);
\r
7616 err = GetLastError();
\r
7617 if (err == ERROR_IO_PENDING) {
\r
7618 ok = GetOverlappedResult(hFile, ovl, outCount, TRUE);
\r
7622 err = GetLastError();
\r
7629 DoWriteFile(HANDLE hFile, char *buf, int count, DWORD *outCount,
\r
7634 ResetEvent(ovl->hEvent);
\r
7635 ovl->Offset = ovl->OffsetHigh = 0;
\r
7636 ok = WriteFile(hFile, buf, count, outCount, ovl);
\r
7640 err = GetLastError();
\r
7641 if (err == ERROR_IO_PENDING) {
\r
7642 ok = GetOverlappedResult(hFile, ovl, outCount, TRUE);
\r
7646 err = GetLastError();
\r
7652 /* [AS] If input is line by line and a line exceed the buffer size, force an error */
\r
7653 void CheckForInputBufferFull( InputSource * is )
\r
7655 if( is->lineByLine && (is->next - is->buf) >= INPUT_SOURCE_BUF_SIZE ) {
\r
7656 /* Look for end of line */
\r
7657 char * p = is->buf;
\r
7659 while( p < is->next && *p != '\n' ) {
\r
7663 if( p >= is->next ) {
\r
7664 if (appData.debugMode) {
\r
7665 fprintf( debugFP, "Input line exceeded buffer size (source id=%lu)\n", is->id );
\r
7668 is->error = ERROR_BROKEN_PIPE; /* [AS] Just any non-successful code! */
\r
7669 is->count = (DWORD) -1;
\r
7670 is->next = is->buf;
\r
7676 InputThread(LPVOID arg)
\r
7681 is = (InputSource *) arg;
\r
7682 ovl.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
\r
7683 ovl.Internal = ovl.InternalHigh = ovl.Offset = ovl.OffsetHigh = 0;
\r
7684 while (is->hThread != NULL) {
\r
7685 is->error = DoReadFile(is->hFile, is->next,
\r
7686 INPUT_SOURCE_BUF_SIZE - (is->next - is->buf),
\r
7687 &is->count, &ovl);
\r
7688 if (is->error == NO_ERROR) {
\r
7689 is->next += is->count;
\r
7691 if (is->error == ERROR_BROKEN_PIPE) {
\r
7692 /* Correct for MS brain damage. EOF reading a pipe is not an error. */
\r
7695 is->count = (DWORD) -1;
\r
7696 /* [AS] The (is->count <= 0) check below is not useful for unsigned values! */
\r
7701 CheckForInputBufferFull( is );
\r
7703 SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);
\r
7705 if( is->count == ((DWORD) -1) ) break; /* [AS] */
\r
7707 if (is->count <= 0) break; /* Quit on EOF or error */
\r
7710 CloseHandle(ovl.hEvent);
\r
7711 CloseHandle(is->hFile);
\r
7713 if (appData.debugMode) {
\r
7714 fprintf( debugFP, "Input thread terminated (id=%lu, error=%d, count=%ld)\n", is->id, is->error, is->count );
\r
7721 /* Windows 95 beta 2 won't let you do overlapped i/o on a console or pipe */
\r
7723 NonOvlInputThread(LPVOID arg)
\r
7730 is = (InputSource *) arg;
\r
7731 while (is->hThread != NULL) {
\r
7732 is->error = ReadFile(is->hFile, is->next,
\r
7733 INPUT_SOURCE_BUF_SIZE - (is->next - is->buf),
\r
7734 &is->count, NULL) ? NO_ERROR : GetLastError();
\r
7735 if (is->error == NO_ERROR) {
\r
7736 /* Change CRLF to LF */
\r
7737 if (is->next > is->buf) {
\r
7739 i = is->count + 1;
\r
7747 if (prev == '\r' && *p == '\n') {
\r
7759 if (is->error == ERROR_BROKEN_PIPE) {
\r
7760 /* Correct for MS brain damage. EOF reading a pipe is not an error. */
\r
7763 is->count = (DWORD) -1;
\r
7767 CheckForInputBufferFull( is );
\r
7769 SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);
\r
7771 if( is->count == ((DWORD) -1) ) break; /* [AS] */
\r
7773 if (is->count < 0) break; /* Quit on error */
\r
7775 CloseHandle(is->hFile);
\r
7780 SocketInputThread(LPVOID arg)
\r
7784 is = (InputSource *) arg;
\r
7785 while (is->hThread != NULL) {
\r
7786 is->count = recv(is->sock, is->buf, INPUT_SOURCE_BUF_SIZE, 0);
\r
7787 if ((int)is->count == SOCKET_ERROR) {
\r
7788 is->count = (DWORD) -1;
\r
7789 is->error = WSAGetLastError();
\r
7791 is->error = NO_ERROR;
\r
7792 is->next += is->count;
\r
7793 if (is->count == 0 && is->second == is) {
\r
7794 /* End of file on stderr; quit with no message */
\r
7798 SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);
\r
7800 if( is->count == ((DWORD) -1) ) break; /* [AS] */
\r
7802 if (is->count <= 0) break; /* Quit on EOF or error */
\r
7808 InputEvent(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
\r
7812 is = (InputSource *) lParam;
\r
7813 if (is->lineByLine) {
\r
7814 /* Feed in lines one by one */
\r
7815 char *p = is->buf;
\r
7817 while (q < is->next) {
\r
7818 if (*q++ == '\n') {
\r
7819 (is->func)(is, is->closure, p, q - p, NO_ERROR);
\r
7824 /* Move any partial line to the start of the buffer */
\r
7826 while (p < is->next) {
\r
7831 if (is->error != NO_ERROR || is->count == 0) {
\r
7832 /* Notify backend of the error. Note: If there was a partial
\r
7833 line at the end, it is not flushed through. */
\r
7834 (is->func)(is, is->closure, is->buf, is->count, is->error);
\r
7837 /* Feed in the whole chunk of input at once */
\r
7838 (is->func)(is, is->closure, is->buf, is->count, is->error);
\r
7839 is->next = is->buf;
\r
7843 /*---------------------------------------------------------------------------*\
\r
7845 * Menu enables. Used when setting various modes.
\r
7847 \*---------------------------------------------------------------------------*/
\r
7855 GreyRevert(Boolean grey)
\r
7856 { // [HGM] vari: for retracting variations in local mode
\r
7857 HMENU hmenu = GetMenu(hwndMain);
\r
7858 EnableMenuItem(hmenu, IDM_Revert, MF_BYCOMMAND|(grey ? MF_GRAYED : MF_ENABLED));
\r
7859 EnableMenuItem(hmenu, IDM_Annotate, MF_BYCOMMAND|(grey ? MF_GRAYED : MF_ENABLED));
\r
7863 SetMenuEnables(HMENU hmenu, Enables *enab)
\r
7865 while (enab->item > 0) {
\r
7866 (void) EnableMenuItem(hmenu, enab->item, enab->flags);
\r
7871 Enables gnuEnables[] = {
\r
7872 { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },
\r
7873 { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },
\r
7874 { IDM_IcsClient, MF_BYCOMMAND|MF_GRAYED },
\r
7875 { IDM_Accept, MF_BYCOMMAND|MF_GRAYED },
\r
7876 { IDM_Decline, MF_BYCOMMAND|MF_GRAYED },
\r
7877 { IDM_Rematch, MF_BYCOMMAND|MF_GRAYED },
\r
7878 { IDM_Adjourn, MF_BYCOMMAND|MF_GRAYED },
\r
7879 { IDM_StopExamining, MF_BYCOMMAND|MF_GRAYED },
\r
7880 { IDM_StopObserving, MF_BYCOMMAND|MF_GRAYED },
\r
7881 { IDM_Upload, MF_BYCOMMAND|MF_GRAYED },
\r
7882 { IDM_Revert, MF_BYCOMMAND|MF_GRAYED },
\r
7883 { IDM_Annotate, MF_BYCOMMAND|MF_GRAYED },
\r
7884 { IDM_NewChat, MF_BYCOMMAND|MF_GRAYED },
\r
7886 // Needed to switch from ncp to GNU mode on Engine Load
\r
7887 { ACTION_POS, MF_BYPOSITION|MF_ENABLED },
\r
7888 { IDM_MachineWhite, MF_BYCOMMAND|MF_ENABLED },
\r
7889 { IDM_MachineBlack, MF_BYCOMMAND|MF_ENABLED },
\r
7890 { IDM_TwoMachines, MF_BYCOMMAND|MF_ENABLED },
\r
7891 { IDM_Match, MF_BYCOMMAND|MF_ENABLED },
\r
7892 { IDM_AnalysisMode, MF_BYCOMMAND|MF_ENABLED },
\r
7893 { IDM_AnalyzeFile, MF_BYCOMMAND|MF_ENABLED },
\r
7894 { IDM_Engine1Options, MF_BYCOMMAND|MF_ENABLED },
\r
7895 { IDM_Engine2Options, MF_BYCOMMAND|MF_ENABLED },
\r
7896 { IDM_TimeControl, MF_BYCOMMAND|MF_ENABLED },
\r
7897 { IDM_RetractMove, MF_BYCOMMAND|MF_ENABLED },
\r
7898 { IDM_MoveNow, MF_BYCOMMAND|MF_ENABLED },
\r
7899 { IDM_Hint, MF_BYCOMMAND|MF_ENABLED },
\r
7900 { IDM_Book, MF_BYCOMMAND|MF_ENABLED },
\r
7904 Enables icsEnables[] = {
\r
7905 { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },
\r
7906 { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },
\r
7907 { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },
\r
7908 { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },
\r
7909 { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },
\r
7910 { IDM_Match, MF_BYCOMMAND|MF_GRAYED },
\r
7911 { IDM_MachineBoth, MF_BYCOMMAND|MF_GRAYED },
\r
7912 { IDM_AnalysisMode, MF_BYCOMMAND|MF_ENABLED },
\r
7913 { IDM_AnalyzeFile, MF_BYCOMMAND|MF_GRAYED },
\r
7914 { IDM_TimeControl, MF_BYCOMMAND|MF_GRAYED },
\r
7915 { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },
\r
7916 { IDM_Hint, MF_BYCOMMAND|MF_GRAYED },
\r
7917 { IDM_Book, MF_BYCOMMAND|MF_GRAYED },
\r
7918 { IDM_LoadProg1, MF_BYCOMMAND|MF_GRAYED },
\r
7919 { IDM_LoadProg2, MF_BYCOMMAND|MF_GRAYED },
\r
7920 { IDM_IcsOptions, MF_BYCOMMAND|MF_ENABLED },
\r
7921 { IDM_Engine1Options, MF_BYCOMMAND|MF_GRAYED },
\r
7922 { IDM_Engine2Options, MF_BYCOMMAND|MF_GRAYED },
\r
7923 { IDM_Annotate, MF_BYCOMMAND|MF_GRAYED },
\r
7924 { IDM_Tourney, MF_BYCOMMAND|MF_GRAYED },
\r
7929 Enables zippyEnables[] = {
\r
7930 { IDM_MoveNow, MF_BYCOMMAND|MF_ENABLED },
\r
7931 { IDM_Hint, MF_BYCOMMAND|MF_ENABLED },
\r
7932 { IDM_Book, MF_BYCOMMAND|MF_ENABLED },
\r
7933 { IDM_Engine1Options, MF_BYCOMMAND|MF_ENABLED },
\r
7938 Enables ncpEnables[] = {
\r
7939 { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },
\r
7940 { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },
\r
7941 { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },
\r
7942 { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },
\r
7943 { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },
\r
7944 { IDM_Match, MF_BYCOMMAND|MF_GRAYED },
\r
7945 { IDM_AnalysisMode, MF_BYCOMMAND|MF_GRAYED },
\r
7946 { IDM_AnalyzeFile, MF_BYCOMMAND|MF_GRAYED },
\r
7947 { IDM_IcsClient, MF_BYCOMMAND|MF_GRAYED },
\r
7948 { ACTION_POS, MF_BYPOSITION|MF_GRAYED },
\r
7949 { IDM_Revert, MF_BYCOMMAND|MF_GRAYED },
\r
7950 { IDM_Annotate, MF_BYCOMMAND|MF_GRAYED },
\r
7951 { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },
\r
7952 { IDM_RetractMove, MF_BYCOMMAND|MF_GRAYED },
\r
7953 { IDM_TimeControl, MF_BYCOMMAND|MF_GRAYED },
\r
7954 { IDM_Hint, MF_BYCOMMAND|MF_GRAYED },
\r
7955 { IDM_Book, MF_BYCOMMAND|MF_GRAYED },
\r
7956 { IDM_MachineBoth, MF_BYCOMMAND|MF_GRAYED },
\r
7957 { IDM_NewChat, MF_BYCOMMAND|MF_GRAYED },
\r
7958 { IDM_Engine1Options, MF_BYCOMMAND|MF_GRAYED },
\r
7959 { IDM_Engine2Options, MF_BYCOMMAND|MF_GRAYED },
\r
7960 { IDM_Sounds, MF_BYCOMMAND|MF_GRAYED },
\r
7964 Enables trainingOnEnables[] = {
\r
7965 { IDM_EditComment, MF_BYCOMMAND|MF_GRAYED },
\r
7966 { IDM_Comment, MF_BYCOMMAND|MF_GRAYED },
\r
7967 { IDM_Pause, MF_BYCOMMAND|MF_GRAYED },
\r
7968 { IDM_Forward, MF_BYCOMMAND|MF_GRAYED },
\r
7969 { IDM_Backward, MF_BYCOMMAND|MF_GRAYED },
\r
7970 { IDM_ToEnd, MF_BYCOMMAND|MF_GRAYED },
\r
7971 { IDM_ToStart, MF_BYCOMMAND|MF_GRAYED },
\r
7972 { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },
\r
7973 { IDM_TruncateGame, MF_BYCOMMAND|MF_GRAYED },
\r
7977 Enables trainingOffEnables[] = {
\r
7978 { IDM_EditComment, MF_BYCOMMAND|MF_ENABLED },
\r
7979 { IDM_Comment, MF_BYCOMMAND|MF_ENABLED },
\r
7980 { IDM_Pause, MF_BYCOMMAND|MF_ENABLED },
\r
7981 { IDM_Forward, MF_BYCOMMAND|MF_ENABLED },
\r
7982 { IDM_Backward, MF_BYCOMMAND|MF_ENABLED },
\r
7983 { IDM_ToEnd, MF_BYCOMMAND|MF_ENABLED },
\r
7984 { IDM_ToStart, MF_BYCOMMAND|MF_ENABLED },
\r
7985 { IDM_MoveNow, MF_BYCOMMAND|MF_ENABLED },
\r
7986 { IDM_TruncateGame, MF_BYCOMMAND|MF_ENABLED },
\r
7990 /* These modify either ncpEnables or gnuEnables */
\r
7991 Enables cmailEnables[] = {
\r
7992 { IDM_MailMove, MF_BYCOMMAND|MF_ENABLED },
\r
7993 { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_ENABLED },
\r
7994 { ACTION_POS, MF_BYPOSITION|MF_ENABLED },
\r
7995 { IDM_CallFlag, MF_BYCOMMAND|MF_GRAYED },
\r
7996 { IDM_Draw, MF_BYCOMMAND|MF_ENABLED },
\r
7997 { IDM_Adjourn, MF_BYCOMMAND|MF_GRAYED },
\r
7998 { IDM_Abort, MF_BYCOMMAND|MF_GRAYED },
\r
8002 Enables machineThinkingEnables[] = {
\r
8003 { IDM_LoadGame, MF_BYCOMMAND|MF_GRAYED },
\r
8004 { IDM_LoadNextGame, MF_BYCOMMAND|MF_GRAYED },
\r
8005 { IDM_LoadPrevGame, MF_BYCOMMAND|MF_GRAYED },
\r
8006 { IDM_ReloadGame, MF_BYCOMMAND|MF_GRAYED },
\r
8007 { IDM_PasteGame, MF_BYCOMMAND|MF_GRAYED },
\r
8008 { IDM_LoadPosition, MF_BYCOMMAND|MF_GRAYED },
\r
8009 { IDM_LoadNextPosition, MF_BYCOMMAND|MF_GRAYED },
\r
8010 { IDM_LoadPrevPosition, MF_BYCOMMAND|MF_GRAYED },
\r
8011 { IDM_ReloadPosition, MF_BYCOMMAND|MF_GRAYED },
\r
8012 { IDM_PastePosition, MF_BYCOMMAND|MF_GRAYED },
\r
8013 { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },
\r
8014 { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },
\r
8015 { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },
\r
8016 // { IDM_Match, MF_BYCOMMAND|MF_GRAYED },
\r
8017 { IDM_TypeInMove, MF_BYCOMMAND|MF_GRAYED },
\r
8018 { IDM_RetractMove, MF_BYCOMMAND|MF_GRAYED },
\r
8022 Enables userThinkingEnables[] = {
\r
8023 { IDM_LoadGame, MF_BYCOMMAND|MF_ENABLED },
\r
8024 { IDM_LoadNextGame, MF_BYCOMMAND|MF_ENABLED },
\r
8025 { IDM_LoadPrevGame, MF_BYCOMMAND|MF_ENABLED },
\r
8026 { IDM_ReloadGame, MF_BYCOMMAND|MF_ENABLED },
\r
8027 { IDM_PasteGame, MF_BYCOMMAND|MF_ENABLED },
\r
8028 { IDM_LoadPosition, MF_BYCOMMAND|MF_ENABLED },
\r
8029 { IDM_LoadNextPosition, MF_BYCOMMAND|MF_ENABLED },
\r
8030 { IDM_LoadPrevPosition, MF_BYCOMMAND|MF_ENABLED },
\r
8031 { IDM_ReloadPosition, MF_BYCOMMAND|MF_ENABLED },
\r
8032 { IDM_PastePosition, MF_BYCOMMAND|MF_ENABLED },
\r
8033 { IDM_MachineWhite, MF_BYCOMMAND|MF_ENABLED },
\r
8034 { IDM_MachineBlack, MF_BYCOMMAND|MF_ENABLED },
\r
8035 { IDM_TwoMachines, MF_BYCOMMAND|MF_ENABLED },
\r
8036 // { IDM_Match, MF_BYCOMMAND|MF_ENABLED },
\r
8037 { IDM_TypeInMove, MF_BYCOMMAND|MF_ENABLED },
\r
8038 { IDM_RetractMove, MF_BYCOMMAND|MF_ENABLED },
\r
8042 /*---------------------------------------------------------------------------*\
\r
8044 * Front-end interface functions exported by XBoard.
\r
8045 * Functions appear in same order as prototypes in frontend.h.
\r
8047 \*---------------------------------------------------------------------------*/
\r
8049 CheckMark(UINT item, int state)
\r
8051 if(item) CheckMenuItem(GetMenu(hwndMain), item, MF_BYCOMMAND|state);
\r
8057 static UINT prevChecked = 0;
\r
8058 static int prevPausing = 0;
\r
8061 if (pausing != prevPausing) {
\r
8062 prevPausing = pausing;
\r
8063 (void) CheckMenuItem(GetMenu(hwndMain), IDM_Pause,
\r
8064 MF_BYCOMMAND|(pausing ? MF_CHECKED : MF_UNCHECKED));
\r
8065 if (hwndPause) SetWindowText(hwndPause, pausing ? "C" : "P");
\r
8068 switch (gameMode) {
\r
8069 case BeginningOfGame:
\r
8070 if (appData.icsActive)
\r
8071 nowChecked = IDM_IcsClient;
\r
8072 else if (appData.noChessProgram)
\r
8073 nowChecked = IDM_EditGame;
\r
8075 nowChecked = IDM_MachineBlack;
\r
8077 case MachinePlaysBlack:
\r
8078 nowChecked = IDM_MachineBlack;
\r
8080 case MachinePlaysWhite:
\r
8081 nowChecked = IDM_MachineWhite;
\r
8083 case TwoMachinesPlay:
\r
8084 nowChecked = IDM_TwoMachines;
\r
8087 nowChecked = IDM_AnalysisMode;
\r
8090 nowChecked = IDM_AnalyzeFile;
\r
8093 nowChecked = IDM_EditGame;
\r
8095 case PlayFromGameFile:
\r
8096 nowChecked = IDM_LoadGame;
\r
8098 case EditPosition:
\r
8099 nowChecked = IDM_EditPosition;
\r
8102 nowChecked = IDM_Training;
\r
8104 case IcsPlayingWhite:
\r
8105 case IcsPlayingBlack:
\r
8106 case IcsObserving:
\r
8108 nowChecked = IDM_IcsClient;
\r
8115 CheckMark(prevChecked, MF_UNCHECKED);
\r
8116 CheckMark(nowChecked, MF_CHECKED);
\r
8117 CheckMark(IDM_Match, matchMode && matchGame < appData.matchGames ? MF_CHECKED : MF_UNCHECKED);
\r
8119 if (nowChecked == IDM_LoadGame || nowChecked == IDM_Training) {
\r
8120 (void) EnableMenuItem(GetMenu(hwndMain), IDM_Training,
\r
8121 MF_BYCOMMAND|MF_ENABLED);
\r
8123 (void) EnableMenuItem(GetMenu(hwndMain),
\r
8124 IDM_Training, MF_BYCOMMAND|MF_GRAYED);
\r
8127 prevChecked = nowChecked;
\r
8129 /* [DM] icsEngineAnalyze - Do a sceure check too */
\r
8130 if (appData.icsActive) {
\r
8131 if (appData.icsEngineAnalyze) {
\r
8132 CheckMark(IDM_AnalysisMode, MF_CHECKED);
\r
8134 CheckMark(IDM_AnalysisMode, MF_UNCHECKED);
\r
8137 DisplayLogos(); // [HGM] logos: mode change could have altered logos
\r
8143 HMENU hmenu = GetMenu(hwndMain);
\r
8144 SetMenuEnables(hmenu, icsEnables);
\r
8145 EnableMenuItem(GetSubMenu(hmenu, OPTIONS_POS), IDM_IcsOptions,
\r
8146 MF_BYCOMMAND|MF_ENABLED);
\r
8148 if (appData.zippyPlay) {
\r
8149 SetMenuEnables(hmenu, zippyEnables);
\r
8150 if (!appData.noChessProgram) /* [DM] icsEngineAnalyze */
\r
8151 (void) EnableMenuItem(GetMenu(hwndMain), IDM_AnalysisMode,
\r
8152 MF_BYCOMMAND|MF_ENABLED);
\r
8160 SetMenuEnables(GetMenu(hwndMain), gnuEnables);
\r
8166 HMENU hmenu = GetMenu(hwndMain);
\r
8167 SetMenuEnables(hmenu, ncpEnables);
\r
8168 DrawMenuBar(hwndMain);
\r
8174 SetMenuEnables(GetMenu(hwndMain), cmailEnables);
\r
8178 SetTrainingModeOn()
\r
8181 SetMenuEnables(GetMenu(hwndMain), trainingOnEnables);
\r
8182 for (i = 0; i < N_BUTTONS; i++) {
\r
8183 if (buttonDesc[i].hwnd != NULL)
\r
8184 EnableWindow(buttonDesc[i].hwnd, FALSE);
\r
8189 VOID SetTrainingModeOff()
\r
8192 SetMenuEnables(GetMenu(hwndMain), trainingOffEnables);
\r
8193 for (i = 0; i < N_BUTTONS; i++) {
\r
8194 if (buttonDesc[i].hwnd != NULL)
\r
8195 EnableWindow(buttonDesc[i].hwnd, TRUE);
\r
8201 SetUserThinkingEnables()
\r
8203 SetMenuEnables(GetMenu(hwndMain), userThinkingEnables);
\r
8207 SetMachineThinkingEnables()
\r
8209 HMENU hMenu = GetMenu(hwndMain);
\r
8210 int flags = MF_BYCOMMAND|MF_ENABLED;
\r
8212 SetMenuEnables(hMenu, machineThinkingEnables);
\r
8214 if (gameMode == MachinePlaysBlack) {
\r
8215 (void)EnableMenuItem(hMenu, IDM_MachineBlack, flags);
\r
8216 } else if (gameMode == MachinePlaysWhite) {
\r
8217 (void)EnableMenuItem(hMenu, IDM_MachineWhite, flags);
\r
8218 } else if (gameMode == TwoMachinesPlay) {
\r
8219 (void)EnableMenuItem(hMenu, matchMode ? IDM_Match : IDM_TwoMachines, flags); // [HGM] match
\r
8225 DisplayTitle(char *str)
\r
8227 char title[MSG_SIZ], *host;
\r
8228 if (str[0] != NULLCHAR) {
\r
8229 safeStrCpy(title, str, sizeof(title)/sizeof(title[0]) );
\r
8230 } else if (appData.icsActive) {
\r
8231 if (appData.icsCommPort[0] != NULLCHAR)
\r
8234 host = appData.icsHost;
\r
8235 snprintf(title, MSG_SIZ, "%s: %s", szTitle, host);
\r
8236 } else if (appData.noChessProgram) {
\r
8237 safeStrCpy(title, szTitle, sizeof(title)/sizeof(title[0]) );
\r
8239 safeStrCpy(title, szTitle, sizeof(title)/sizeof(title[0]) );
\r
8240 strcat(title, ": ");
\r
8241 strcat(title, first.tidy);
\r
8243 SetWindowText(hwndMain, title);
\r
8248 DisplayMessage(char *str1, char *str2)
\r
8252 int remain = MESSAGE_TEXT_MAX - 1;
\r
8255 moveErrorMessageUp = FALSE; /* turned on later by caller if needed */
\r
8256 messageText[0] = NULLCHAR;
\r
8258 len = strlen(str1);
\r
8259 if (len > remain) len = remain;
\r
8260 strncpy(messageText, str1, len);
\r
8261 messageText[len] = NULLCHAR;
\r
8264 if (*str2 && remain >= 2) {
\r
8266 strcat(messageText, " ");
\r
8269 len = strlen(str2);
\r
8270 if (len > remain) len = remain;
\r
8271 strncat(messageText, str2, len);
\r
8273 messageText[MESSAGE_TEXT_MAX - 1] = NULLCHAR;
\r
8274 safeStrCpy(lastMsg, messageText, MSG_SIZ);
\r
8276 if (hwndMain == NULL || IsIconic(hwndMain)) return;
\r
8280 hdc = GetDC(hwndMain);
\r
8281 oldFont = SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);
\r
8282 ExtTextOut(hdc, messageRect.left, messageRect.top, ETO_CLIPPED|ETO_OPAQUE,
\r
8283 &messageRect, messageText, strlen(messageText), NULL);
\r
8284 (void) SelectObject(hdc, oldFont);
\r
8285 (void) ReleaseDC(hwndMain, hdc);
\r
8289 DisplayError(char *str, int error)
\r
8291 char buf[MSG_SIZ*2], buf2[MSG_SIZ];
\r
8295 safeStrCpy(buf, str, sizeof(buf)/sizeof(buf[0]) );
\r
8297 len = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,
\r
8298 NULL, error, LANG_NEUTRAL,
\r
8299 (LPSTR) buf2, MSG_SIZ, NULL);
\r
8301 snprintf(buf, 2*MSG_SIZ, "%s:\n%s", str, buf2);
\r
8303 ErrorMap *em = errmap;
\r
8304 while (em->err != 0 && em->err != error) em++;
\r
8305 if (em->err != 0) {
\r
8306 snprintf(buf, 2*MSG_SIZ, "%s:\n%s", str, em->msg);
\r
8308 snprintf(buf, 2*MSG_SIZ, "%s:\nError code %d", str, error);
\r
8313 ErrorPopUp(_("Error"), buf);
\r
8318 DisplayMoveError(char *str)
\r
8320 fromX = fromY = -1;
\r
8321 ClearHighlights();
\r
8322 DrawPosition(FALSE, NULL);
\r
8323 if (appData.popupMoveErrors) {
\r
8324 ErrorPopUp(_("Error"), str);
\r
8326 DisplayMessage(str, "");
\r
8327 moveErrorMessageUp = TRUE;
\r
8332 DisplayFatalError(char *str, int error, int exitStatus)
\r
8334 char buf[2*MSG_SIZ], buf2[MSG_SIZ];
\r
8336 char *label = exitStatus ? _("Fatal Error") : _("Exiting");
\r
8339 len = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,
\r
8340 NULL, error, LANG_NEUTRAL,
\r
8341 (LPSTR) buf2, MSG_SIZ, NULL);
\r
8343 snprintf(buf, 2*MSG_SIZ, "%s:\n%s", str, buf2);
\r
8345 ErrorMap *em = errmap;
\r
8346 while (em->err != 0 && em->err != error) em++;
\r
8347 if (em->err != 0) {
\r
8348 snprintf(buf, 2*MSG_SIZ, "%s:\n%s", str, em->msg);
\r
8350 snprintf(buf, 2*MSG_SIZ, "%s:\nError code %d", str, error);
\r
8355 if (appData.debugMode) {
\r
8356 fprintf(debugFP, "%s: %s\n", label, str);
\r
8358 if (appData.popupExitMessage) {
\r
8359 (void) MessageBox(hwndMain, str, label, MB_OK|
\r
8360 (exitStatus ? MB_ICONSTOP : MB_ICONINFORMATION));
\r
8362 ExitEvent(exitStatus);
\r
8367 DisplayInformation(char *str)
\r
8369 (void) MessageBox(hwndMain, str, _("Information"), MB_OK|MB_ICONINFORMATION);
\r
8374 DisplayNote(char *str)
\r
8376 ErrorPopUp(_("Note"), str);
\r
8381 char *title, *question, *replyPrefix;
\r
8386 QuestionDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
8388 static QuestionParams *qp;
\r
8389 char reply[MSG_SIZ];
\r
8392 switch (message) {
\r
8393 case WM_INITDIALOG:
\r
8394 qp = (QuestionParams *) lParam;
\r
8395 CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));
\r
8396 Translate(hDlg, DLG_Question);
\r
8397 SetWindowText(hDlg, qp->title);
\r
8398 SetDlgItemText(hDlg, OPT_QuestionText, qp->question);
\r
8399 SetFocus(GetDlgItem(hDlg, OPT_QuestionInput));
\r
8403 switch (LOWORD(wParam)) {
\r
8405 safeStrCpy(reply, qp->replyPrefix, sizeof(reply)/sizeof(reply[0]) );
\r
8406 if (*reply) strcat(reply, " ");
\r
8407 len = strlen(reply);
\r
8408 GetDlgItemText(hDlg, OPT_QuestionInput, reply + len, sizeof(reply) - len);
\r
8409 strcat(reply, "\n");
\r
8410 OutputToProcess(qp->pr, reply, strlen(reply), &err);
\r
8411 EndDialog(hDlg, TRUE);
\r
8412 if (err) DisplayFatalError(_("Error writing to chess program"), err, 1);
\r
8415 EndDialog(hDlg, FALSE);
\r
8426 AskQuestion(char* title, char *question, char *replyPrefix, ProcRef pr)
\r
8428 QuestionParams qp;
\r
8432 qp.question = question;
\r
8433 qp.replyPrefix = replyPrefix;
\r
8435 lpProc = MakeProcInstance((FARPROC)QuestionDialog, hInst);
\r
8436 DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_Question),
\r
8437 hwndMain, (DLGPROC)lpProc, (LPARAM)&qp);
\r
8438 FreeProcInstance(lpProc);
\r
8441 /* [AS] Pick FRC position */
\r
8442 LRESULT CALLBACK NewGameFRC_Proc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
8444 static int * lpIndexFRC;
\r
8450 case WM_INITDIALOG:
\r
8451 lpIndexFRC = (int *) lParam;
\r
8453 CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));
\r
8454 Translate(hDlg, DLG_NewGameFRC);
\r
8456 SendDlgItemMessage( hDlg, IDC_NFG_Edit, EM_SETLIMITTEXT, sizeof(buf)-1, 0 );
\r
8457 SetDlgItemInt( hDlg, IDC_NFG_Edit, *lpIndexFRC, TRUE );
\r
8458 SendDlgItemMessage( hDlg, IDC_NFG_Edit, EM_SETSEL, 0, -1 );
\r
8459 SetFocus(GetDlgItem(hDlg, IDC_NFG_Edit));
\r
8464 switch( LOWORD(wParam) ) {
\r
8466 *lpIndexFRC = GetDlgItemInt(hDlg, IDC_NFG_Edit, &index_is_ok, TRUE );
\r
8467 EndDialog( hDlg, 0 );
\r
8468 shuffleOpenings = TRUE; /* [HGM] shuffle: switch shuffling on for as long as we stay in current variant */
\r
8471 EndDialog( hDlg, 1 );
\r
8473 case IDC_NFG_Edit:
\r
8474 if( HIWORD(wParam) == EN_CHANGE ) {
\r
8475 GetDlgItemInt(hDlg, IDC_NFG_Edit, &index_is_ok, TRUE );
\r
8477 EnableWindow( GetDlgItem(hDlg, IDOK), index_is_ok );
\r
8480 case IDC_NFG_Random:
\r
8481 snprintf( buf, sizeof(buf)/sizeof(buf[0]), "%d", myrandom() ); /* [HGM] shuffle: no longer limit to 960 */
\r
8482 SetDlgItemText(hDlg, IDC_NFG_Edit, buf );
\r
8495 int index = appData.defaultFrcPosition;
\r
8496 FARPROC lpProc = MakeProcInstance( (FARPROC) NewGameFRC_Proc, hInst );
\r
8498 result = DialogBoxParam( hInst, MAKEINTRESOURCE(DLG_NewGameFRC), hwndMain, (DLGPROC)lpProc, (LPARAM)&index );
\r
8500 if( result == 0 ) {
\r
8501 appData.defaultFrcPosition = index;
\r
8507 /* [AS] Game list options. Refactored by HGM */
\r
8509 HWND gameListOptionsDialog;
\r
8511 // low-level front-end: clear text edit / list widget
\r
8515 SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_RESETCONTENT, 0, 0 );
\r
8518 // low-level front-end: clear text edit / list widget
\r
8520 GLT_DeSelectList()
\r
8522 SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_SETCURSEL, 0, 0 );
\r
8525 // low-level front-end: append line to text edit / list widget
\r
8527 GLT_AddToList( char *name )
\r
8530 SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_ADDSTRING, 0, (LPARAM) name );
\r
8534 // low-level front-end: get line from text edit / list widget
\r
8536 GLT_GetFromList( int index, char *name )
\r
8539 if( SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_GETTEXT, index, (LPARAM) name ) != LB_ERR )
\r
8545 void GLT_MoveSelection( HWND hDlg, int delta )
\r
8547 int idx1 = (int) SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETCURSEL, 0, 0 );
\r
8548 int idx2 = idx1 + delta;
\r
8549 int count = (int) SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETCOUNT, 0, 0 );
\r
8551 if( idx1 >=0 && idx1 < count && idx2 >= 0 && idx2 < count ) {
\r
8554 SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETTEXT, idx1, (LPARAM) buf );
\r
8555 SendDlgItemMessage( hDlg, IDC_GameListTags, LB_DELETESTRING, idx1, 0 );
\r
8556 SendDlgItemMessage( hDlg, IDC_GameListTags, LB_INSERTSTRING, idx2, (LPARAM) buf );
\r
8557 SendDlgItemMessage( hDlg, IDC_GameListTags, LB_SETCURSEL, idx2, 0 );
\r
8561 LRESULT CALLBACK GameListOptions_Proc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
8565 case WM_INITDIALOG:
\r
8566 gameListOptionsDialog = hDlg; // [HGM] pass through global to keep out off back-end
\r
8568 CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));
\r
8569 Translate(hDlg, DLG_GameListOptions);
\r
8571 /* Initialize list */
\r
8572 GLT_TagsToList( lpUserGLT );
\r
8574 SetFocus( GetDlgItem(hDlg, IDC_GameListTags) );
\r
8579 switch( LOWORD(wParam) ) {
\r
8582 EndDialog( hDlg, 0 );
\r
8585 EndDialog( hDlg, 1 );
\r
8588 case IDC_GLT_Default:
\r
8589 GLT_TagsToList( GLT_DEFAULT_TAGS );
\r
8592 case IDC_GLT_Restore:
\r
8593 GLT_TagsToList( appData.gameListTags );
\r
8597 GLT_MoveSelection( hDlg, -1 );
\r
8600 case IDC_GLT_Down:
\r
8601 GLT_MoveSelection( hDlg, +1 );
\r
8611 int GameListOptions()
\r
8614 FARPROC lpProc = MakeProcInstance( (FARPROC) GameListOptions_Proc, hInst );
\r
8616 safeStrCpy( lpUserGLT, appData.gameListTags ,LPUSERGLT_SIZE );
\r
8618 result = DialogBoxParam( hInst, MAKEINTRESOURCE(DLG_GameListOptions), hwndMain, (DLGPROC)lpProc, (LPARAM)lpUserGLT );
\r
8620 if( result == 0 ) {
\r
8621 /* [AS] Memory leak here! */
\r
8622 appData.gameListTags = strdup( lpUserGLT );
\r
8629 DisplayIcsInteractionTitle(char *str)
\r
8631 char consoleTitle[MSG_SIZ];
\r
8633 snprintf(consoleTitle, MSG_SIZ, "%s: %s", szConsoleTitle, str);
\r
8634 SetWindowText(hwndConsole, consoleTitle);
\r
8636 if(appData.chatBoxes) { // [HGM] chat: open chat boxes
\r
8637 char buf[MSG_SIZ], *p = buf, *q;
\r
8638 safeStrCpy(buf, appData.chatBoxes, sizeof(buf)/sizeof(buf[0]) );
\r
8640 q = strchr(p, ';');
\r
8642 if(*p) ChatPopUp(p);
\r
8646 SetActiveWindow(hwndMain);
\r
8650 DrawPosition(int fullRedraw, Board board)
\r
8652 HDCDrawPosition(NULL, (BOOLEAN) fullRedraw, board);
\r
8655 void NotifyFrontendLogin()
\r
8658 UpdateICSWidth(GetDlgItem(hwndConsole, OPT_ConsoleText));
\r
8664 fromX = fromY = -1;
\r
8665 if (dragInfo.pos.x != -1 || dragInfo.pos.y != -1) {
\r
8666 dragInfo.pos.x = dragInfo.pos.y = -1;
\r
8667 dragInfo.pos.x = dragInfo.pos.y = -1;
\r
8668 dragInfo.lastpos = dragInfo.pos;
\r
8669 dragInfo.start.x = dragInfo.start.y = -1;
\r
8670 dragInfo.from = dragInfo.start;
\r
8672 DrawPosition(TRUE, NULL);
\r
8679 CommentPopUp(char *title, char *str)
\r
8681 HWND hwnd = GetActiveWindow();
\r
8682 EitherCommentPopUp(currentMove, title, str, FALSE); // [HGM] vari: fake move index, rather than 0
\r
8684 SetActiveWindow(hwnd);
\r
8688 CommentPopDown(void)
\r
8690 CheckMenuItem(GetMenu(hwndMain), IDM_Comment, MF_UNCHECKED);
\r
8691 if (commentDialog) {
\r
8692 ShowWindow(commentDialog, SW_HIDE);
\r
8694 commentUp = FALSE;
\r
8698 EditCommentPopUp(int index, char *title, char *str)
\r
8700 EitherCommentPopUp(index, title, str, TRUE);
\r
8707 MyPlaySound(&sounds[(int)SoundMove]);
\r
8710 VOID PlayIcsWinSound()
\r
8712 MyPlaySound(&sounds[(int)SoundIcsWin]);
\r
8715 VOID PlayIcsLossSound()
\r
8717 MyPlaySound(&sounds[(int)SoundIcsLoss]);
\r
8720 VOID PlayIcsDrawSound()
\r
8722 MyPlaySound(&sounds[(int)SoundIcsDraw]);
\r
8725 VOID PlayIcsUnfinishedSound()
\r
8727 MyPlaySound(&sounds[(int)SoundIcsUnfinished]);
\r
8733 MyPlaySound(&sounds[(int)SoundAlarm]);
\r
8739 MyPlaySound(&textAttribs[ColorTell].sound);
\r
8747 consoleEcho = TRUE;
\r
8748 hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
8749 SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&consoleCF);
\r
8750 SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);
\r
8759 consoleEcho = FALSE;
\r
8760 hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
8761 /* This works OK: set text and background both to the same color */
\r
8763 cf.crTextColor = COLOR_ECHOOFF;
\r
8764 SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf);
\r
8765 SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, cf.crTextColor);
\r
8768 /* No Raw()...? */
\r
8770 void Colorize(ColorClass cc, int continuation)
\r
8772 currentColorClass = cc;
\r
8773 consoleCF.dwMask = CFM_COLOR|CFM_BOLD|CFM_ITALIC|CFM_UNDERLINE|CFM_STRIKEOUT;
\r
8774 consoleCF.crTextColor = textAttribs[cc].color;
\r
8775 consoleCF.dwEffects = textAttribs[cc].effects;
\r
8776 if (!continuation) MyPlaySound(&textAttribs[cc].sound);
\r
8782 static char buf[MSG_SIZ];
\r
8783 DWORD bufsiz = MSG_SIZ;
\r
8785 if(appData.userName != NULL && appData.userName[0] != 0) {
\r
8786 return appData.userName; /* [HGM] username: prefer name selected by user over his system login */
\r
8788 if (!GetUserName(buf, &bufsiz)) {
\r
8789 /*DisplayError("Error getting user name", GetLastError());*/
\r
8790 safeStrCpy(buf, _("User"), sizeof(buf)/sizeof(buf[0]) );
\r
8798 static char buf[MSG_SIZ];
\r
8799 DWORD bufsiz = MSG_SIZ;
\r
8801 if (!GetComputerName(buf, &bufsiz)) {
\r
8802 /*DisplayError("Error getting host name", GetLastError());*/
\r
8803 safeStrCpy(buf, _("Unknown"), sizeof(buf)/sizeof(buf[0]) );
\r
8810 ClockTimerRunning()
\r
8812 return clockTimerEvent != 0;
\r
8818 if (clockTimerEvent == 0) return FALSE;
\r
8819 KillTimer(hwndMain, clockTimerEvent);
\r
8820 clockTimerEvent = 0;
\r
8825 StartClockTimer(long millisec)
\r
8827 clockTimerEvent = SetTimer(hwndMain, (UINT) CLOCK_TIMER_ID,
\r
8828 (UINT) millisec, NULL);
\r
8832 DisplayWhiteClock(long timeRemaining, int highlight)
\r
8835 char *flag = whiteFlag && gameMode == TwoMachinesPlay ? "(!)" : "";
\r
8837 if(appData.noGUI) return;
\r
8838 hdc = GetDC(hwndMain);
\r
8839 if (!IsIconic(hwndMain)) {
\r
8840 DisplayAClock(hdc, timeRemaining, highlight,
\r
8841 flipClock ? &blackRect : &whiteRect, _("White"), flag);
\r
8843 if (highlight && iconCurrent == iconBlack) {
\r
8844 iconCurrent = iconWhite;
\r
8845 PostMessage(hwndMain, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);
\r
8846 if (IsIconic(hwndMain)) {
\r
8847 DrawIcon(hdc, 2, 2, iconCurrent);
\r
8850 (void) ReleaseDC(hwndMain, hdc);
\r
8852 PostMessage(hwndConsole, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);
\r
8856 DisplayBlackClock(long timeRemaining, int highlight)
\r
8859 char *flag = blackFlag && gameMode == TwoMachinesPlay ? "(!)" : "";
\r
8861 if(appData.noGUI) return;
\r
8862 hdc = GetDC(hwndMain);
\r
8863 if (!IsIconic(hwndMain)) {
\r
8864 DisplayAClock(hdc, timeRemaining, highlight,
\r
8865 flipClock ? &whiteRect : &blackRect, _("Black"), flag);
\r
8867 if (highlight && iconCurrent == iconWhite) {
\r
8868 iconCurrent = iconBlack;
\r
8869 PostMessage(hwndMain, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);
\r
8870 if (IsIconic(hwndMain)) {
\r
8871 DrawIcon(hdc, 2, 2, iconCurrent);
\r
8874 (void) ReleaseDC(hwndMain, hdc);
\r
8876 PostMessage(hwndConsole, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);
\r
8881 LoadGameTimerRunning()
\r
8883 return loadGameTimerEvent != 0;
\r
8887 StopLoadGameTimer()
\r
8889 if (loadGameTimerEvent == 0) return FALSE;
\r
8890 KillTimer(hwndMain, loadGameTimerEvent);
\r
8891 loadGameTimerEvent = 0;
\r
8896 StartLoadGameTimer(long millisec)
\r
8898 loadGameTimerEvent = SetTimer(hwndMain, (UINT) LOAD_GAME_TIMER_ID,
\r
8899 (UINT) millisec, NULL);
\r
8907 char fileTitle[MSG_SIZ];
\r
8909 defName = DefaultFileName(appData.oldSaveStyle ? "gam" : "pgn");
\r
8910 f = OpenFileDialog(hwndMain, "a", defName,
\r
8911 appData.oldSaveStyle ? "gam" : "pgn",
\r
8913 _("Save Game to File"), NULL, fileTitle, NULL);
\r
8915 SaveGame(f, 0, "");
\r
8922 ScheduleDelayedEvent(DelayedEventCallback cb, long millisec)
\r
8924 if (delayedTimerEvent != 0) {
\r
8925 if (appData.debugMode && cb != delayedTimerCallback) { // [HGM] alive: not too much debug
\r
8926 fprintf(debugFP, "ScheduleDelayedEvent: event already scheduled\n");
\r
8928 KillTimer(hwndMain, delayedTimerEvent);
\r
8929 delayedTimerEvent = 0;
\r
8930 if(delayedTimerCallback != cb) // [HGM] alive: do not "flush" same event, just postpone it
\r
8931 delayedTimerCallback();
\r
8933 delayedTimerCallback = cb;
\r
8934 delayedTimerEvent = SetTimer(hwndMain, (UINT) DELAYED_TIMER_ID,
\r
8935 (UINT) millisec, NULL);
\r
8938 DelayedEventCallback
\r
8941 if (delayedTimerEvent) {
\r
8942 return delayedTimerCallback;
\r
8949 CancelDelayedEvent()
\r
8951 if (delayedTimerEvent) {
\r
8952 KillTimer(hwndMain, delayedTimerEvent);
\r
8953 delayedTimerEvent = 0;
\r
8957 DWORD GetWin32Priority(int nice)
\r
8958 { // [HGM] nice: translate Unix nice() value to indows priority class. (Code stolen from Polyglot 1.4w11)
\r
8960 REALTIME_PRIORITY_CLASS 0x00000100
\r
8961 HIGH_PRIORITY_CLASS 0x00000080
\r
8962 ABOVE_NORMAL_PRIORITY_CLASS 0x00008000
\r
8963 NORMAL_PRIORITY_CLASS 0x00000020
\r
8964 BELOW_NORMAL_PRIORITY_CLASS 0x00004000
\r
8965 IDLE_PRIORITY_CLASS 0x00000040
\r
8967 if (nice < -15) return 0x00000080;
\r
8968 if (nice < 0) return 0x00008000;
\r
8969 if (nice == 0) return 0x00000020;
\r
8970 if (nice < 15) return 0x00004000;
\r
8971 return 0x00000040;
\r
8974 void RunCommand(char *cmdLine)
\r
8976 /* Now create the child process. */
\r
8977 STARTUPINFO siStartInfo;
\r
8978 PROCESS_INFORMATION piProcInfo;
\r
8980 siStartInfo.cb = sizeof(STARTUPINFO);
\r
8981 siStartInfo.lpReserved = NULL;
\r
8982 siStartInfo.lpDesktop = NULL;
\r
8983 siStartInfo.lpTitle = NULL;
\r
8984 siStartInfo.dwFlags = STARTF_USESTDHANDLES;
\r
8985 siStartInfo.cbReserved2 = 0;
\r
8986 siStartInfo.lpReserved2 = NULL;
\r
8987 siStartInfo.hStdInput = NULL;
\r
8988 siStartInfo.hStdOutput = NULL;
\r
8989 siStartInfo.hStdError = NULL;
\r
8991 CreateProcess(NULL,
\r
8992 cmdLine, /* command line */
\r
8993 NULL, /* process security attributes */
\r
8994 NULL, /* primary thread security attrs */
\r
8995 TRUE, /* handles are inherited */
\r
8996 DETACHED_PROCESS|CREATE_NEW_PROCESS_GROUP,
\r
8997 NULL, /* use parent's environment */
\r
8999 &siStartInfo, /* STARTUPINFO pointer */
\r
9000 &piProcInfo); /* receives PROCESS_INFORMATION */
\r
9002 CloseHandle(piProcInfo.hThread);
\r
9005 /* Start a child process running the given program.
\r
9006 The process's standard output can be read from "from", and its
\r
9007 standard input can be written to "to".
\r
9008 Exit with fatal error if anything goes wrong.
\r
9009 Returns an opaque pointer that can be used to destroy the process
\r
9013 StartChildProcess(char *cmdLine, char *dir, ProcRef *pr)
\r
9015 #define BUFSIZE 4096
\r
9017 HANDLE hChildStdinRd, hChildStdinWr,
\r
9018 hChildStdoutRd, hChildStdoutWr;
\r
9019 HANDLE hChildStdinWrDup, hChildStdoutRdDup;
\r
9020 SECURITY_ATTRIBUTES saAttr;
\r
9022 PROCESS_INFORMATION piProcInfo;
\r
9023 STARTUPINFO siStartInfo;
\r
9025 char buf[MSG_SIZ];
\r
9028 if (appData.debugMode) {
\r
9029 fprintf(debugFP, "StartChildProcess (dir=\"%s\") %s\n", dir, cmdLine);
\r
9034 /* Set the bInheritHandle flag so pipe handles are inherited. */
\r
9035 saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);
\r
9036 saAttr.bInheritHandle = TRUE;
\r
9037 saAttr.lpSecurityDescriptor = NULL;
\r
9040 * The steps for redirecting child's STDOUT:
\r
9041 * 1. Create anonymous pipe to be STDOUT for child.
\r
9042 * 2. Create a noninheritable duplicate of read handle,
\r
9043 * and close the inheritable read handle.
\r
9046 /* Create a pipe for the child's STDOUT. */
\r
9047 if (! CreatePipe(&hChildStdoutRd, &hChildStdoutWr, &saAttr, 0)) {
\r
9048 return GetLastError();
\r
9051 /* Duplicate the read handle to the pipe, so it is not inherited. */
\r
9052 fSuccess = DuplicateHandle(GetCurrentProcess(), hChildStdoutRd,
\r
9053 GetCurrentProcess(), &hChildStdoutRdDup, 0,
\r
9054 FALSE, /* not inherited */
\r
9055 DUPLICATE_SAME_ACCESS);
\r
9057 return GetLastError();
\r
9059 CloseHandle(hChildStdoutRd);
\r
9062 * The steps for redirecting child's STDIN:
\r
9063 * 1. Create anonymous pipe to be STDIN for child.
\r
9064 * 2. Create a noninheritable duplicate of write handle,
\r
9065 * and close the inheritable write handle.
\r
9068 /* Create a pipe for the child's STDIN. */
\r
9069 if (! CreatePipe(&hChildStdinRd, &hChildStdinWr, &saAttr, 0)) {
\r
9070 return GetLastError();
\r
9073 /* Duplicate the write handle to the pipe, so it is not inherited. */
\r
9074 fSuccess = DuplicateHandle(GetCurrentProcess(), hChildStdinWr,
\r
9075 GetCurrentProcess(), &hChildStdinWrDup, 0,
\r
9076 FALSE, /* not inherited */
\r
9077 DUPLICATE_SAME_ACCESS);
\r
9079 return GetLastError();
\r
9081 CloseHandle(hChildStdinWr);
\r
9083 /* Arrange to (1) look in dir for the child .exe file, and
\r
9084 * (2) have dir be the child's working directory. Interpret
\r
9085 * dir relative to the directory WinBoard loaded from. */
\r
9086 GetCurrentDirectory(MSG_SIZ, buf);
\r
9087 SetCurrentDirectory(installDir);
\r
9088 SetCurrentDirectory(dir);
\r
9090 /* Now create the child process. */
\r
9092 siStartInfo.cb = sizeof(STARTUPINFO);
\r
9093 siStartInfo.lpReserved = NULL;
\r
9094 siStartInfo.lpDesktop = NULL;
\r
9095 siStartInfo.lpTitle = NULL;
\r
9096 siStartInfo.dwFlags = STARTF_USESTDHANDLES;
\r
9097 siStartInfo.cbReserved2 = 0;
\r
9098 siStartInfo.lpReserved2 = NULL;
\r
9099 siStartInfo.hStdInput = hChildStdinRd;
\r
9100 siStartInfo.hStdOutput = hChildStdoutWr;
\r
9101 siStartInfo.hStdError = hChildStdoutWr;
\r
9103 fSuccess = CreateProcess(NULL,
\r
9104 cmdLine, /* command line */
\r
9105 NULL, /* process security attributes */
\r
9106 NULL, /* primary thread security attrs */
\r
9107 TRUE, /* handles are inherited */
\r
9108 DETACHED_PROCESS|CREATE_NEW_PROCESS_GROUP,
\r
9109 NULL, /* use parent's environment */
\r
9111 &siStartInfo, /* STARTUPINFO pointer */
\r
9112 &piProcInfo); /* receives PROCESS_INFORMATION */
\r
9114 err = GetLastError();
\r
9115 SetCurrentDirectory(buf); /* return to prev directory */
\r
9120 if (appData.niceEngines){ // [HGM] nice: adjust engine proc priority
\r
9121 if(appData.debugMode) fprintf(debugFP, "nice engine proc to %d\n", appData.niceEngines);
\r
9122 SetPriorityClass(piProcInfo.hProcess, GetWin32Priority(appData.niceEngines));
\r
9125 /* Close the handles we don't need in the parent */
\r
9126 CloseHandle(piProcInfo.hThread);
\r
9127 CloseHandle(hChildStdinRd);
\r
9128 CloseHandle(hChildStdoutWr);
\r
9130 /* Prepare return value */
\r
9131 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
\r
9132 cp->kind = CPReal;
\r
9133 cp->hProcess = piProcInfo.hProcess;
\r
9134 cp->pid = piProcInfo.dwProcessId;
\r
9135 cp->hFrom = hChildStdoutRdDup;
\r
9136 cp->hTo = hChildStdinWrDup;
\r
9138 *pr = (void *) cp;
\r
9140 /* Klaus Friedel says that this Sleep solves a problem under Windows
\r
9141 2000 where engines sometimes don't see the initial command(s)
\r
9142 from WinBoard and hang. I don't understand how that can happen,
\r
9143 but the Sleep is harmless, so I've put it in. Others have also
\r
9144 reported what may be the same problem, so hopefully this will fix
\r
9145 it for them too. */
\r
9153 DestroyChildProcess(ProcRef pr, int/*boolean*/ signal)
\r
9155 ChildProc *cp; int result;
\r
9157 cp = (ChildProc *) pr;
\r
9158 if (cp == NULL) return;
\r
9160 switch (cp->kind) {
\r
9162 /* TerminateProcess is considered harmful, so... */
\r
9163 CloseHandle(cp->hTo); /* Closing this will give the child an EOF and hopefully kill it */
\r
9164 if (cp->hFrom) CloseHandle(cp->hFrom); /* if NULL, InputThread will close it */
\r
9165 /* The following doesn't work because the chess program
\r
9166 doesn't "have the same console" as WinBoard. Maybe
\r
9167 we could arrange for this even though neither WinBoard
\r
9168 nor the chess program uses a console for stdio? */
\r
9169 /*!!if (signal) GenerateConsoleCtrlEvent(CTRL_BREAK_EVENT, cp->pid);*/
\r
9171 /* [AS] Special termination modes for misbehaving programs... */
\r
9172 if( signal == 9 ) {
\r
9173 result = TerminateProcess( cp->hProcess, 0 );
\r
9175 if ( appData.debugMode) {
\r
9176 fprintf( debugFP, "Terminating process %lu, result=%d\n", cp->pid, result );
\r
9179 else if( signal == 10 ) {
\r
9180 DWORD dw = WaitForSingleObject( cp->hProcess, 3*1000 ); // Wait 3 seconds at most
\r
9182 if( dw != WAIT_OBJECT_0 ) {
\r
9183 result = TerminateProcess( cp->hProcess, 0 );
\r
9185 if ( appData.debugMode) {
\r
9186 fprintf( debugFP, "Process %lu still alive after timeout, killing... result=%d\n", cp->pid, result );
\r
9192 CloseHandle(cp->hProcess);
\r
9196 if (cp->hFrom) CloseHandle(cp->hFrom);
\r
9200 closesocket(cp->sock);
\r
9205 if (signal) send(cp->sock2, "\017", 1, 0); /* 017 = 15 = SIGTERM */
\r
9206 closesocket(cp->sock);
\r
9207 closesocket(cp->sock2);
\r
9215 InterruptChildProcess(ProcRef pr)
\r
9219 cp = (ChildProc *) pr;
\r
9220 if (cp == NULL) return;
\r
9221 switch (cp->kind) {
\r
9223 /* The following doesn't work because the chess program
\r
9224 doesn't "have the same console" as WinBoard. Maybe
\r
9225 we could arrange for this even though neither WinBoard
\r
9226 nor the chess program uses a console for stdio */
\r
9227 /*!!GenerateConsoleCtrlEvent(CTRL_C_EVENT, cp->pid);*/
\r
9232 /* Can't interrupt */
\r
9236 send(cp->sock2, "\002", 1, 0); /* 2 = SIGINT */
\r
9243 OpenTelnet(char *host, char *port, ProcRef *pr)
\r
9245 char cmdLine[MSG_SIZ];
\r
9247 if (port[0] == NULLCHAR) {
\r
9248 snprintf(cmdLine, MSG_SIZ, "%s %s", appData.telnetProgram, host);
\r
9250 snprintf(cmdLine, MSG_SIZ, "%s %s %s", appData.telnetProgram, host, port);
\r
9252 return StartChildProcess(cmdLine, "", pr);
\r
9256 /* Code to open TCP sockets */
\r
9259 OpenTCP(char *host, char *port, ProcRef *pr)
\r
9265 struct sockaddr_in sa, mysa;
\r
9266 struct hostent FAR *hp;
\r
9267 unsigned short uport;
\r
9268 WORD wVersionRequested;
\r
9271 /* Initialize socket DLL */
\r
9272 wVersionRequested = MAKEWORD(1, 1);
\r
9273 err = WSAStartup(wVersionRequested, &wsaData);
\r
9274 if (err != 0) return err;
\r
9277 if ((s = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {
\r
9278 err = WSAGetLastError();
\r
9283 /* Bind local address using (mostly) don't-care values.
\r
9285 memset((char *) &mysa, 0, sizeof(struct sockaddr_in));
\r
9286 mysa.sin_family = AF_INET;
\r
9287 mysa.sin_addr.s_addr = INADDR_ANY;
\r
9288 uport = (unsigned short) 0;
\r
9289 mysa.sin_port = htons(uport);
\r
9290 if (bind(s, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))
\r
9291 == SOCKET_ERROR) {
\r
9292 err = WSAGetLastError();
\r
9297 /* Resolve remote host name */
\r
9298 memset((char *) &sa, 0, sizeof(struct sockaddr_in));
\r
9299 if (!(hp = gethostbyname(host))) {
\r
9300 unsigned int b0, b1, b2, b3;
\r
9302 err = WSAGetLastError();
\r
9304 if (sscanf(host, "%u.%u.%u.%u", &b0, &b1, &b2, &b3) == 4) {
\r
9305 hp = (struct hostent *) calloc(1, sizeof(struct hostent));
\r
9306 hp->h_addrtype = AF_INET;
\r
9308 hp->h_addr_list = (char **) calloc(2, sizeof(char *));
\r
9309 hp->h_addr_list[0] = (char *) malloc(4);
\r
9310 hp->h_addr_list[0][0] = (char) b0;
\r
9311 hp->h_addr_list[0][1] = (char) b1;
\r
9312 hp->h_addr_list[0][2] = (char) b2;
\r
9313 hp->h_addr_list[0][3] = (char) b3;
\r
9319 sa.sin_family = hp->h_addrtype;
\r
9320 uport = (unsigned short) atoi(port);
\r
9321 sa.sin_port = htons(uport);
\r
9322 memcpy((char *) &sa.sin_addr, hp->h_addr, hp->h_length);
\r
9324 /* Make connection */
\r
9325 if (connect(s, (struct sockaddr *) &sa,
\r
9326 sizeof(struct sockaddr_in)) == SOCKET_ERROR) {
\r
9327 err = WSAGetLastError();
\r
9332 /* Prepare return value */
\r
9333 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
\r
9334 cp->kind = CPSock;
\r
9336 *pr = (ProcRef *) cp;
\r
9342 OpenCommPort(char *name, ProcRef *pr)
\r
9347 char fullname[MSG_SIZ];
\r
9349 if (*name != '\\')
\r
9350 snprintf(fullname, MSG_SIZ, "\\\\.\\%s", name);
\r
9352 safeStrCpy(fullname, name, sizeof(fullname)/sizeof(fullname[0]) );
\r
9354 h = CreateFile(name, GENERIC_READ | GENERIC_WRITE,
\r
9355 0, NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL);
\r
9356 if (h == (HANDLE) -1) {
\r
9357 return GetLastError();
\r
9361 if (!SetCommState(h, (LPDCB) &dcb)) return GetLastError();
\r
9363 /* Accumulate characters until a 100ms pause, then parse */
\r
9364 ct.ReadIntervalTimeout = 100;
\r
9365 ct.ReadTotalTimeoutMultiplier = 0;
\r
9366 ct.ReadTotalTimeoutConstant = 0;
\r
9367 ct.WriteTotalTimeoutMultiplier = 0;
\r
9368 ct.WriteTotalTimeoutConstant = 0;
\r
9369 if (!SetCommTimeouts(h, (LPCOMMTIMEOUTS) &ct)) return GetLastError();
\r
9371 /* Prepare return value */
\r
9372 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
\r
9373 cp->kind = CPComm;
\r
9376 *pr = (ProcRef *) cp;
\r
9382 OpenLoopback(ProcRef *pr)
\r
9384 DisplayFatalError(_("Not implemented"), 0, 1);
\r
9390 OpenRcmd(char* host, char* user, char* cmd, ProcRef* pr)
\r
9395 struct sockaddr_in sa, mysa;
\r
9396 struct hostent FAR *hp;
\r
9397 unsigned short uport;
\r
9398 WORD wVersionRequested;
\r
9401 char stderrPortStr[MSG_SIZ];
\r
9403 /* Initialize socket DLL */
\r
9404 wVersionRequested = MAKEWORD(1, 1);
\r
9405 err = WSAStartup(wVersionRequested, &wsaData);
\r
9406 if (err != 0) return err;
\r
9408 /* Resolve remote host name */
\r
9409 memset((char *) &sa, 0, sizeof(struct sockaddr_in));
\r
9410 if (!(hp = gethostbyname(host))) {
\r
9411 unsigned int b0, b1, b2, b3;
\r
9413 err = WSAGetLastError();
\r
9415 if (sscanf(host, "%u.%u.%u.%u", &b0, &b1, &b2, &b3) == 4) {
\r
9416 hp = (struct hostent *) calloc(1, sizeof(struct hostent));
\r
9417 hp->h_addrtype = AF_INET;
\r
9419 hp->h_addr_list = (char **) calloc(2, sizeof(char *));
\r
9420 hp->h_addr_list[0] = (char *) malloc(4);
\r
9421 hp->h_addr_list[0][0] = (char) b0;
\r
9422 hp->h_addr_list[0][1] = (char) b1;
\r
9423 hp->h_addr_list[0][2] = (char) b2;
\r
9424 hp->h_addr_list[0][3] = (char) b3;
\r
9430 sa.sin_family = hp->h_addrtype;
\r
9431 uport = (unsigned short) 514;
\r
9432 sa.sin_port = htons(uport);
\r
9433 memcpy((char *) &sa.sin_addr, hp->h_addr, hp->h_length);
\r
9435 /* Bind local socket to unused "privileged" port address
\r
9437 s = INVALID_SOCKET;
\r
9438 memset((char *) &mysa, 0, sizeof(struct sockaddr_in));
\r
9439 mysa.sin_family = AF_INET;
\r
9440 mysa.sin_addr.s_addr = INADDR_ANY;
\r
9441 for (fromPort = 1023;; fromPort--) {
\r
9442 if (fromPort < 0) {
\r
9444 return WSAEADDRINUSE;
\r
9446 if (s == INVALID_SOCKET) {
\r
9447 if ((s = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {
\r
9448 err = WSAGetLastError();
\r
9453 uport = (unsigned short) fromPort;
\r
9454 mysa.sin_port = htons(uport);
\r
9455 if (bind(s, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))
\r
9456 == SOCKET_ERROR) {
\r
9457 err = WSAGetLastError();
\r
9458 if (err == WSAEADDRINUSE) continue;
\r
9462 if (connect(s, (struct sockaddr *) &sa,
\r
9463 sizeof(struct sockaddr_in)) == SOCKET_ERROR) {
\r
9464 err = WSAGetLastError();
\r
9465 if (err == WSAEADDRINUSE) {
\r
9476 /* Bind stderr local socket to unused "privileged" port address
\r
9478 s2 = INVALID_SOCKET;
\r
9479 memset((char *) &mysa, 0, sizeof(struct sockaddr_in));
\r
9480 mysa.sin_family = AF_INET;
\r
9481 mysa.sin_addr.s_addr = INADDR_ANY;
\r
9482 for (fromPort = 1023;; fromPort--) {
\r
9483 if (fromPort == prevStderrPort) continue; // don't reuse port
\r
9484 if (fromPort < 0) {
\r
9485 (void) closesocket(s);
\r
9487 return WSAEADDRINUSE;
\r
9489 if (s2 == INVALID_SOCKET) {
\r
9490 if ((s2 = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {
\r
9491 err = WSAGetLastError();
\r
9497 uport = (unsigned short) fromPort;
\r
9498 mysa.sin_port = htons(uport);
\r
9499 if (bind(s2, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))
\r
9500 == SOCKET_ERROR) {
\r
9501 err = WSAGetLastError();
\r
9502 if (err == WSAEADDRINUSE) continue;
\r
9503 (void) closesocket(s);
\r
9507 if (listen(s2, 1) == SOCKET_ERROR) {
\r
9508 err = WSAGetLastError();
\r
9509 if (err == WSAEADDRINUSE) {
\r
9511 s2 = INVALID_SOCKET;
\r
9514 (void) closesocket(s);
\r
9515 (void) closesocket(s2);
\r
9521 prevStderrPort = fromPort; // remember port used
\r
9522 snprintf(stderrPortStr, MSG_SIZ, "%d", fromPort);
\r
9524 if (send(s, stderrPortStr, strlen(stderrPortStr) + 1, 0) == SOCKET_ERROR) {
\r
9525 err = WSAGetLastError();
\r
9526 (void) closesocket(s);
\r
9527 (void) closesocket(s2);
\r
9532 if (send(s, UserName(), strlen(UserName()) + 1, 0) == SOCKET_ERROR) {
\r
9533 err = WSAGetLastError();
\r
9534 (void) closesocket(s);
\r
9535 (void) closesocket(s2);
\r
9539 if (*user == NULLCHAR) user = UserName();
\r
9540 if (send(s, user, strlen(user) + 1, 0) == SOCKET_ERROR) {
\r
9541 err = WSAGetLastError();
\r
9542 (void) closesocket(s);
\r
9543 (void) closesocket(s2);
\r
9547 if (send(s, cmd, strlen(cmd) + 1, 0) == SOCKET_ERROR) {
\r
9548 err = WSAGetLastError();
\r
9549 (void) closesocket(s);
\r
9550 (void) closesocket(s2);
\r
9555 if ((s3 = accept(s2, NULL, NULL)) == INVALID_SOCKET) {
\r
9556 err = WSAGetLastError();
\r
9557 (void) closesocket(s);
\r
9558 (void) closesocket(s2);
\r
9562 (void) closesocket(s2); /* Stop listening */
\r
9564 /* Prepare return value */
\r
9565 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
\r
9566 cp->kind = CPRcmd;
\r
9569 *pr = (ProcRef *) cp;
\r
9576 AddInputSource(ProcRef pr, int lineByLine,
\r
9577 InputCallback func, VOIDSTAR closure)
\r
9579 InputSource *is, *is2 = NULL;
\r
9580 ChildProc *cp = (ChildProc *) pr;
\r
9582 is = (InputSource *) calloc(1, sizeof(InputSource));
\r
9583 is->lineByLine = lineByLine;
\r
9585 is->closure = closure;
\r
9586 is->second = NULL;
\r
9587 is->next = is->buf;
\r
9588 if (pr == NoProc) {
\r
9589 is->kind = CPReal;
\r
9590 consoleInputSource = is;
\r
9592 is->kind = cp->kind;
\r
9594 [AS] Try to avoid a race condition if the thread is given control too early:
\r
9595 we create all threads suspended so that the is->hThread variable can be
\r
9596 safely assigned, then let the threads start with ResumeThread.
\r
9598 switch (cp->kind) {
\r
9600 is->hFile = cp->hFrom;
\r
9601 cp->hFrom = NULL; /* now owned by InputThread */
\r
9603 CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) NonOvlInputThread,
\r
9604 (LPVOID) is, CREATE_SUSPENDED, &is->id);
\r
9608 is->hFile = cp->hFrom;
\r
9609 cp->hFrom = NULL; /* now owned by InputThread */
\r
9611 CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) InputThread,
\r
9612 (LPVOID) is, CREATE_SUSPENDED, &is->id);
\r
9616 is->sock = cp->sock;
\r
9618 CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,
\r
9619 (LPVOID) is, CREATE_SUSPENDED, &is->id);
\r
9623 is2 = (InputSource *) calloc(1, sizeof(InputSource));
\r
9625 is->sock = cp->sock;
\r
9627 is2->sock = cp->sock2;
\r
9628 is2->second = is2;
\r
9630 CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,
\r
9631 (LPVOID) is, CREATE_SUSPENDED, &is->id);
\r
9633 CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,
\r
9634 (LPVOID) is2, CREATE_SUSPENDED, &is2->id);
\r
9638 if( is->hThread != NULL ) {
\r
9639 ResumeThread( is->hThread );
\r
9642 if( is2 != NULL && is2->hThread != NULL ) {
\r
9643 ResumeThread( is2->hThread );
\r
9647 return (InputSourceRef) is;
\r
9651 RemoveInputSource(InputSourceRef isr)
\r
9655 is = (InputSource *) isr;
\r
9656 is->hThread = NULL; /* tell thread to stop */
\r
9657 CloseHandle(is->hThread);
\r
9658 if (is->second != NULL) {
\r
9659 is->second->hThread = NULL;
\r
9660 CloseHandle(is->second->hThread);
\r
9664 int no_wrap(char *message, int count)
\r
9666 ConsoleOutput(message, count, FALSE);
\r
9671 OutputToProcess(ProcRef pr, char *message, int count, int *outError)
\r
9674 int outCount = SOCKET_ERROR;
\r
9675 ChildProc *cp = (ChildProc *) pr;
\r
9676 static OVERLAPPED ovl;
\r
9677 static int line = 0;
\r
9681 if (appData.noJoin || !appData.useInternalWrap)
\r
9682 return no_wrap(message, count);
\r
9685 int width = get_term_width();
\r
9686 int len = wrap(NULL, message, count, width, &line);
\r
9687 char *msg = malloc(len);
\r
9691 return no_wrap(message, count);
\r
9694 dbgchk = wrap(msg, message, count, width, &line);
\r
9695 if (dbgchk != len && appData.debugMode)
\r
9696 fprintf(debugFP, "wrap(): dbgchk(%d) != len(%d)\n", dbgchk, len);
\r
9697 ConsoleOutput(msg, len, FALSE);
\r
9704 if (ovl.hEvent == NULL) {
\r
9705 ovl.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
\r
9707 ovl.Internal = ovl.InternalHigh = ovl.Offset = ovl.OffsetHigh = 0;
\r
9709 switch (cp->kind) {
\r
9712 outCount = send(cp->sock, message, count, 0);
\r
9713 if (outCount == SOCKET_ERROR) {
\r
9714 *outError = WSAGetLastError();
\r
9716 *outError = NO_ERROR;
\r
9721 if (WriteFile(((ChildProc *)pr)->hTo, message, count,
\r
9722 &dOutCount, NULL)) {
\r
9723 *outError = NO_ERROR;
\r
9724 outCount = (int) dOutCount;
\r
9726 *outError = GetLastError();
\r
9731 *outError = DoWriteFile(((ChildProc *)pr)->hTo, message, count,
\r
9732 &dOutCount, &ovl);
\r
9733 if (*outError == NO_ERROR) {
\r
9734 outCount = (int) dOutCount;
\r
9744 if(n != 0) Sleep(n);
\r
9748 OutputToProcessDelayed(ProcRef pr, char *message, int count, int *outError,
\r
9751 /* Ignore delay, not implemented for WinBoard */
\r
9752 return OutputToProcess(pr, message, count, outError);
\r
9757 CmailSigHandlerCallBack(InputSourceRef isr, VOIDSTAR closure,
\r
9758 char *buf, int count, int error)
\r
9760 DisplayFatalError(_("Not implemented"), 0, 1);
\r
9763 /* see wgamelist.c for Game List functions */
\r
9764 /* see wedittags.c for Edit Tags functions */
\r
9771 char buf[MSG_SIZ];
\r
9774 if (SearchPath(installDir, appData.icsLogon, NULL, MSG_SIZ, buf, &dummy)) {
\r
9775 f = fopen(buf, "r");
\r
9777 ProcessICSInitScript(f);
\r
9787 StartAnalysisClock()
\r
9789 if (analysisTimerEvent) return;
\r
9790 analysisTimerEvent = SetTimer(hwndMain, (UINT) ANALYSIS_TIMER_ID,
\r
9791 (UINT) 2000, NULL);
\r
9795 SetHighlights(int fromX, int fromY, int toX, int toY)
\r
9797 highlightInfo.sq[0].x = fromX;
\r
9798 highlightInfo.sq[0].y = fromY;
\r
9799 highlightInfo.sq[1].x = toX;
\r
9800 highlightInfo.sq[1].y = toY;
\r
9806 highlightInfo.sq[0].x = highlightInfo.sq[0].y =
\r
9807 highlightInfo.sq[1].x = highlightInfo.sq[1].y = -1;
\r
9811 SetPremoveHighlights(int fromX, int fromY, int toX, int toY)
\r
9813 premoveHighlightInfo.sq[0].x = fromX;
\r
9814 premoveHighlightInfo.sq[0].y = fromY;
\r
9815 premoveHighlightInfo.sq[1].x = toX;
\r
9816 premoveHighlightInfo.sq[1].y = toY;
\r
9820 ClearPremoveHighlights()
\r
9822 premoveHighlightInfo.sq[0].x = premoveHighlightInfo.sq[0].y =
\r
9823 premoveHighlightInfo.sq[1].x = premoveHighlightInfo.sq[1].y = -1;
\r
9827 ShutDownFrontEnd()
\r
9829 if (saveSettingsOnExit) SaveSettings(settingsFileName);
\r
9830 DeleteClipboardTempFiles();
\r
9836 if (IsIconic(hwndMain))
\r
9837 ShowWindow(hwndMain, SW_RESTORE);
\r
9839 SetActiveWindow(hwndMain);
\r
9843 * Prototypes for animation support routines
\r
9845 static void ScreenSquare(int column, int row, POINT * pt);
\r
9846 static void Tween( POINT * start, POINT * mid, POINT * finish, int factor,
\r
9847 POINT frames[], int * nFrames);
\r
9853 AnimateAtomicCapture(Board board, int fromX, int fromY, int toX, int toY)
\r
9854 { // [HGM] atomic: animate blast wave
\r
9857 explodeInfo.fromX = fromX;
\r
9858 explodeInfo.fromY = fromY;
\r
9859 explodeInfo.toX = toX;
\r
9860 explodeInfo.toY = toY;
\r
9861 for(i=1; i<4*kFactor; i++) {
\r
9862 explodeInfo.radius = (i*180)/(4*kFactor-1);
\r
9863 DrawPosition(FALSE, board);
\r
9864 Sleep(appData.animSpeed);
\r
9866 explodeInfo.radius = 0;
\r
9867 DrawPosition(TRUE, board);
\r
9871 AnimateMove(board, fromX, fromY, toX, toY)
\r
9878 ChessSquare piece;
\r
9879 POINT start, finish, mid;
\r
9880 POINT frames[kFactor * 2 + 1];
\r
9883 if (!appData.animate) return;
\r
9884 if (doingSizing) return;
\r
9885 if (fromY < 0 || fromX < 0) return;
\r
9886 piece = board[fromY][fromX];
\r
9887 if (piece >= EmptySquare) return;
\r
9889 ScreenSquare(fromX, fromY, &start);
\r
9890 ScreenSquare(toX, toY, &finish);
\r
9892 /* All moves except knight jumps move in straight line */
\r
9893 if (!(abs(fromX-toX) == 1 && abs(fromY-toY) == 2 || abs(fromX-toX) == 2 && abs(fromY-toY) == 1)) {
\r
9894 mid.x = start.x + (finish.x - start.x) / 2;
\r
9895 mid.y = start.y + (finish.y - start.y) / 2;
\r
9897 /* Knight: make straight movement then diagonal */
\r
9898 if (abs(toY - fromY) < abs(toX - fromX)) {
\r
9899 mid.x = start.x + (finish.x - start.x) / 2;
\r
9903 mid.y = start.y + (finish.y - start.y) / 2;
\r
9907 /* Don't use as many frames for very short moves */
\r
9908 if (abs(toY - fromY) + abs(toX - fromX) <= 2)
\r
9909 Tween(&start, &mid, &finish, kFactor - 1, frames, &nFrames);
\r
9911 Tween(&start, &mid, &finish, kFactor, frames, &nFrames);
\r
9913 animInfo.from.x = fromX;
\r
9914 animInfo.from.y = fromY;
\r
9915 animInfo.to.x = toX;
\r
9916 animInfo.to.y = toY;
\r
9917 animInfo.lastpos = start;
\r
9918 animInfo.piece = piece;
\r
9919 for (n = 0; n < nFrames; n++) {
\r
9920 animInfo.pos = frames[n];
\r
9921 DrawPosition(FALSE, NULL);
\r
9922 animInfo.lastpos = animInfo.pos;
\r
9923 Sleep(appData.animSpeed);
\r
9925 animInfo.pos = finish;
\r
9926 DrawPosition(FALSE, NULL);
\r
9927 animInfo.piece = EmptySquare;
\r
9928 Explode(board, fromX, fromY, toX, toY);
\r
9931 /* Convert board position to corner of screen rect and color */
\r
9934 ScreenSquare(column, row, pt)
\r
9935 int column; int row; POINT * pt;
\r
9938 pt->x = lineGap + ((BOARD_WIDTH-1)-column) * (squareSize + lineGap) + border;
\r
9939 pt->y = lineGap + row * (squareSize + lineGap) + border;
\r
9941 pt->x = lineGap + column * (squareSize + lineGap) + border;
\r
9942 pt->y = lineGap + ((BOARD_HEIGHT-1)-row) * (squareSize + lineGap) + border;
\r
9946 /* Generate a series of frame coords from start->mid->finish.
\r
9947 The movement rate doubles until the half way point is
\r
9948 reached, then halves back down to the final destination,
\r
9949 which gives a nice slow in/out effect. The algorithmn
\r
9950 may seem to generate too many intermediates for short
\r
9951 moves, but remember that the purpose is to attract the
\r
9952 viewers attention to the piece about to be moved and
\r
9953 then to where it ends up. Too few frames would be less
\r
9957 Tween(start, mid, finish, factor, frames, nFrames)
\r
9958 POINT * start; POINT * mid;
\r
9959 POINT * finish; int factor;
\r
9960 POINT frames[]; int * nFrames;
\r
9962 int n, fraction = 1, count = 0;
\r
9964 /* Slow in, stepping 1/16th, then 1/8th, ... */
\r
9965 for (n = 0; n < factor; n++)
\r
9967 for (n = 0; n < factor; n++) {
\r
9968 frames[count].x = start->x + (mid->x - start->x) / fraction;
\r
9969 frames[count].y = start->y + (mid->y - start->y) / fraction;
\r
9971 fraction = fraction / 2;
\r
9975 frames[count] = *mid;
\r
9978 /* Slow out, stepping 1/2, then 1/4, ... */
\r
9980 for (n = 0; n < factor; n++) {
\r
9981 frames[count].x = finish->x - (finish->x - mid->x) / fraction;
\r
9982 frames[count].y = finish->y - (finish->y - mid->y) / fraction;
\r
9984 fraction = fraction * 2;
\r
9990 SettingsPopUp(ChessProgramState *cps)
\r
9991 { // [HGM] wrapper needed because handles must not be passed through back-end
\r
9992 EngineOptionsPopup(savedHwnd, cps);
\r
9995 int flock(int fid, int code)
\r
9997 HANDLE hFile = (HANDLE) _get_osfhandle(fid);
\r
10001 ov.OffsetHigh = 0;
\r
10003 case 1: LockFileEx(hFile, LOCKFILE_EXCLUSIVE_LOCK, 0, 1024, 0, &ov); break; // LOCK_SH
\r
10004 case 2: LockFileEx(hFile, LOCKFILE_EXCLUSIVE_LOCK, 0, 1024, 0, &ov); break; // LOCK_EX
\r
10005 case 3: UnlockFileEx(hFile, 0, 1024, 0, &ov); break; // LOCK_UN
\r
10006 default: return -1;
\r
10015 static char col[8][20];
\r
10016 COLORREF color = *(COLORREF *) colorVariable[n];
\r
10018 snprintf(col[i], 20, "#%02lx%02lx%02lx", color&0xff, (color>>8)&0xff, (color>>16)&0xff);
\r
10023 ActivateTheme (int new)
\r
10024 { // Redo initialization of features depending on options that can occur in themes
\r
10026 if(new) InitDrawingColors();
\r
10027 fontBitmapSquareSize = 0; // request creation of new font pieces
\r
10028 InitDrawingSizes(-2, 0);
\r
10029 InvalidateRect(hwndMain, NULL, TRUE);
\r