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, 2013 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
226 #if defined(_winmajor)
\r
227 #define oldDialog (_winmajor < 4)
\r
229 #define oldDialog 0
\r
233 #define INTERNATIONAL
\r
235 #ifdef INTERNATIONAL
\r
236 # define _(s) T_(s)
\r
242 # define Translate(x, y)
\r
243 # define LoadLanguageFile(s)
\r
246 #ifdef INTERNATIONAL
\r
248 Boolean barbaric; // flag indicating if translation is needed
\r
250 // list of item numbers used in each dialog (used to alter language at run time)
\r
252 #define ABOUTBOX -1 /* not sure why these are needed */
\r
253 #define ABOUTBOX2 -1
\r
255 int dialogItems[][42] = {
\r
256 { ABOUTBOX, IDOK, OPT_MESS, 400 },
\r
257 { DLG_TimeControl, IDC_Babble, OPT_TCUseMoves, OPT_TCUseInc, OPT_TCUseFixed,
\r
258 OPT_TCtext1, OPT_TCtext2, OPT_TCitext1, OPT_TCitext2, OPT_TCftext, GPB_Factors, IDC_Factor1, IDC_Factor2, IDOK, IDCANCEL },
\r
259 { DLG_LoadOptions, OPT_Autostep, OPT_AStext1, OPT_Exact, OPT_Subset, OPT_Struct, OPT_Material, OPT_Range, OPT_Difference,
\r
260 OPT_elo1t, OPT_elo2t, OPT_datet, OPT_Stretch, OPT_Stretcht, OPT_Reversed, OPT_SearchMode, OPT_Mirror, OPT_thresholds, IDOK, IDCANCEL },
\r
261 { DLG_SaveOptions, OPT_Autosave, OPT_AVPrompt, OPT_AVToFile, OPT_AVBrowse,
\r
262 801, OPT_PGN, OPT_Old, OPT_OutOfBookInfo, IDOK, IDCANCEL },
\r
263 { 1536, 1090, IDC_Directories, 1089, 1091, IDOK, IDCANCEL, 1038, IDC_IndexNr, 1037 },
\r
264 { DLG_CommPort, IDOK, IDCANCEL, IDC_Port, IDC_Rate, IDC_Bits, IDC_Parity,
\r
265 IDC_Stop, IDC_Flow, OPT_SerialHelp },
\r
266 { DLG_EditComment, IDOK, OPT_CancelComment, OPT_ClearComment, OPT_EditComment },
\r
267 { DLG_PromotionKing, PB_Chancellor, PB_Archbishop, PB_Queen, PB_Rook,
\r
268 PB_Bishop, PB_Knight, PB_King, IDCANCEL, IDC_Yes, IDC_No, IDC_Centaur },
\r
269 { ABOUTBOX2, IDC_ChessBoard },
\r
270 { DLG_GameList, OPT_GameListLoad, OPT_GameListPrev, OPT_GameListNext,
\r
271 OPT_GameListClose, IDC_GameListDoFilter },
\r
272 { DLG_EditTags, IDOK, OPT_TagsCancel, OPT_EditTags },
\r
273 { DLG_Error, IDOK },
\r
274 { DLG_Colorize, IDOK, IDCANCEL, OPT_ChooseColor, OPT_Bold, OPT_Italic,
\r
275 OPT_Underline, OPT_Strikeout, OPT_Sample },
\r
276 { DLG_Question, IDOK, IDCANCEL, OPT_QuestionText },
\r
277 { DLG_Startup, IDC_Welcome, OPT_ChessEngine, OPT_ChessServer, OPT_View,
\r
278 IDC_SPECIFY_ENG_STATIC, IDC_SPECIFY_SERVER_STATIC, OPT_AnyAdditional,
\r
279 IDOK, IDCANCEL, IDM_HELPCONTENTS },
\r
280 { DLG_IndexNumber, IDC_Index },
\r
281 { DLG_TypeInMove, IDOK, IDCANCEL },
\r
282 { DLG_TypeInName, IDOK, IDCANCEL },
\r
283 { DLG_Sound, IDC_Event, OPT_NoSound, OPT_DefaultBeep, OPT_BuiltInSound,
\r
284 OPT_WavFile, OPT_BrowseSound, OPT_DefaultSounds, IDOK, IDCANCEL, OPT_PlaySound },
\r
285 { DLG_GeneralOptions, IDOK, IDCANCEL, OPT_AlwaysOnTop, OPT_HighlightLastMove,
\r
286 OPT_AlwaysQueen, OPT_PeriodicUpdates, OPT_AnimateDragging, OPT_PonderNextMove,
\r
287 OPT_AnimateMoving, OPT_PopupExitMessage, OPT_AutoFlag, OPT_PopupMoveErrors,
\r
288 OPT_AutoFlipView, OPT_ShowButtonBar, OPT_AutoRaiseBoard, OPT_ShowCoordinates,
\r
289 OPT_Blindfold, OPT_ShowThinking, OPT_HighlightDragging, OPT_TestLegality,
\r
290 OPT_SaveExtPGN, OPT_HideThinkFromHuman, OPT_ExtraInfoInMoveHistory,
\r
291 OPT_HighlightMoveArrow, OPT_AutoLogo ,OPT_SmartMove },
\r
292 { DLG_IcsOptions, IDOK, IDCANCEL, OPT_AutoComment, OPT_AutoKibitz, OPT_AutoObserve,
\r
293 OPT_GetMoveList, OPT_LocalLineEditing, OPT_QuietPlay, OPT_SeekGraph, OPT_AutoRefresh,
\r
294 OPT_BgObserve, OPT_DualBoard, OPT_Premove, OPT_PremoveWhite, OPT_PremoveBlack,
\r
295 OPT_SmartMove, OPT_IcsAlarm, IDC_Sec, OPT_ChooseShoutColor, OPT_ChooseSShoutColor,
\r
296 OPT_ChooseChannel1Color, OPT_ChooseChannelColor, OPT_ChooseKibitzColor,
\r
297 OPT_ChooseTellColor, OPT_ChooseChallengeColor, OPT_ChooseRequestColor,
\r
298 OPT_ChooseSeekColor, OPT_ChooseNormalColor, OPT_ChooseBackgroundColor,
\r
299 OPT_DefaultColors, OPT_DontColorize, IDC_Boxes, GPB_Colors, GPB_Premove,
\r
300 GPB_General, GPB_Alarm, OPT_AutoCreate },
\r
301 { DLG_BoardOptions, IDOK, IDCANCEL, OPT_SizeTiny, OPT_SizeTeeny, OPT_SizeDinky,
\r
302 OPT_SizePetite, OPT_SizeSlim, OPT_SizeSmall, OPT_SizeMediocre, OPT_SizeMiddling,
\r
303 OPT_SizeAverage, OPT_SizeModerate, OPT_SizeMedium, OPT_SizeBulky, OPT_SizeLarge,
\r
304 OPT_SizeBig, OPT_SizeHuge, OPT_SizeGiant, OPT_SizeColossal, OPT_SizeTitanic,
\r
305 OPT_ChooseLightSquareColor, OPT_ChooseDarkSquareColor, OPT_ChooseWhitePieceColor,
\r
306 OPT_ChooseBlackPieceColor, OPT_ChooseHighlightSquareColor, OPT_ChoosePremoveHighlightColor,
\r
307 OPT_Monochrome, OPT_AllWhite, OPT_UpsideDown, OPT_DefaultBoardColors, GPB_Colors,
\r
308 IDC_Light, IDC_Dark, IDC_White, IDC_Black, IDC_High, IDC_PreHigh, GPB_Size, OPT_Bitmaps, OPT_PieceFont, OPT_Grid },
\r
309 { DLG_NewVariant, IDOK, IDCANCEL, OPT_VariantNormal, OPT_VariantFRC, OPT_VariantWildcastle,
\r
310 OPT_VariantNocastle, OPT_VariantLosers, OPT_VariantGiveaway, OPT_VariantSuicide,
\r
311 OPT_Variant3Check, OPT_VariantTwoKings, OPT_VariantAtomic, OPT_VariantCrazyhouse,
\r
312 OPT_VariantBughouse, OPT_VariantTwilight, OPT_VariantShogi, OPT_VariantSuper,
\r
313 OPT_VariantKnightmate, OPT_VariantBerolina, OPT_VariantCylinder, OPT_VariantFairy,
\r
314 OPT_VariantMakruk, OPT_VariantGothic, OPT_VariantCapablanca, OPT_VariantJanus,
\r
315 OPT_VariantCRC, OPT_VariantFalcon, OPT_VariantCourier, OPT_VariantGreat, OPT_VariantSChess,
\r
316 OPT_VariantShatranj, OPT_VariantXiangqi, GPB_Variant, GPB_Board, IDC_Height,
\r
317 IDC_Width, IDC_Hand, IDC_Pieces, IDC_Def },
\r
318 { DLG_Fonts, IDOK, IDCANCEL, OPT_ChooseClockFont, OPT_ChooseMessageFont,
\r
319 OPT_ChooseCoordFont, OPT_ChooseTagFont, OPT_ChooseCommentsFont, OPT_ChooseConsoleFont, OPT_ChooseMoveHistoryFont, OPT_DefaultFonts,
\r
320 OPT_ClockFont, OPT_MessageFont, OPT_CoordFont, OPT_EditTagsFont, OPT_ChoosePieceFont, OPT_MessageFont8,
\r
321 OPT_SampleGameListFont, OPT_ChooseGameListFont, OPT_MessageFont7,
\r
322 OPT_CommentsFont, OPT_MessageFont5, GPB_Current, GPB_All, OPT_MessageFont6 },
\r
323 { DLG_NewGameFRC, IDC_NFG_Label, IDC_NFG_Random, IDOK, IDCANCEL },
\r
324 { DLG_GameListOptions, IDC_GLT, IDC_GLT_Up, IDC_GLT_Down, IDC_GLT_Restore,
\r
325 IDC_GLT_Default, IDOK, IDCANCEL, IDC_GLT_RestoreTo },
\r
326 { DLG_MoveHistory },
\r
327 { DLG_EvalGraph },
\r
328 { DLG_EngineOutput, IDC_EngineLabel1, IDC_Engine1_NPS, IDC_EngineLabel2, IDC_Engine2_NPS },
\r
329 { DLG_Chat, IDC_Partner, IDC_Clear, IDC_Send, },
\r
330 { DLG_EnginePlayOptions, IDC_EpPonder, IDC_EpShowThinking, IDC_EpHideThinkingHuman,
\r
331 IDC_EpPeriodicUpdates, GPB_Adjudications, IDC_Draw, IDC_Moves, IDC_Threshold,
\r
332 IDC_Centi, IDC_TestClaims, IDC_DetectMates, IDC_MaterialDraws, IDC_TrivialDraws,
\r
333 GPB_Apply, IDC_Rule, IDC_Repeats, IDC_ScoreAbs1, IDC_ScoreAbs2, IDOK, IDCANCEL },
\r
334 { DLG_OptionsUCI, IDC_PolyDir, IDC_BrowseForPolyglotDir, IDC_Hash, IDC_Path,
\r
335 IDC_BrowseForEGTB, IDC_Cache, IDC_UseBook, IDC_BrowseForBook, IDC_CPU, IDC_OwnBook1,
\r
336 IDC_OwnBook2, IDC_Depth, IDC_Variation, IDC_DefGames, IDOK, IDCANCEL },
\r
340 static char languageBuf[70000], *foreign[1000], *english[1000], *languageFile[MSG_SIZ];
\r
341 static int lastChecked;
\r
342 static char oldLanguage[MSG_SIZ], *menuText[10][30];
\r
343 extern int tinyLayout;
\r
344 extern char * menuBarText[][10];
\r
347 LoadLanguageFile(char *name)
\r
348 { //load the file with translations, and make a list of the strings to be translated, and their translations
\r
350 int i=0, j=0, n=0, k;
\r
353 if(!name || name[0] == NULLCHAR) return;
\r
354 snprintf(buf, MSG_SIZ, "%s%s", name, strchr(name, '.') ? "" : ".lng"); // auto-append lng extension
\r
355 appData.language = oldLanguage;
\r
356 if(!strcmp(buf, oldLanguage)) { barbaric = 1; return; } // this language already loaded; just switch on
\r
357 if((f = fopen(buf, "r")) == NULL) return;
\r
358 while((k = fgetc(f)) != EOF) {
\r
359 if(i >= sizeof(languageBuf)) { DisplayError("Language file too big", 0); return; }
\r
360 languageBuf[i] = k;
\r
362 if(languageBuf[n] == '"' && languageBuf[i-1] == '"') {
\r
364 if(p = strstr(languageBuf + n + 1, "\" === \"")) {
\r
365 if(p > languageBuf+n+2 && p+8 < languageBuf+i) {
\r
366 if(j >= sizeof(english)) { DisplayError("Too many translated strings", 0); return; }
\r
367 english[j] = languageBuf + n + 1; *p = 0;
\r
368 foreign[j++] = p + 7; languageBuf[i-1] = 0;
\r
369 //if(appData.debugMode) fprintf(debugFP, "translation: replace '%s' by '%s'\n", english[j-1], foreign[j-1]);
\r
374 } else if(i > 0 && languageBuf[i-1] == '\\') {
\r
376 case 'n': k = '\n'; break;
\r
377 case 'r': k = '\r'; break;
\r
378 case 't': k = '\t'; break;
\r
380 languageBuf[--i] = k;
\r
385 barbaric = (j != 0);
\r
386 safeStrCpy(oldLanguage, buf, sizeof(oldLanguage)/sizeof(oldLanguage[0]) );
\r
391 { // return the translation of the given string
\r
392 // efficiency can be improved a lot...
\r
394 static char buf[MSG_SIZ];
\r
395 //if(appData.debugMode) fprintf(debugFP, "T_(%s)\n", s);
\r
396 if(!barbaric) return s;
\r
397 if(!s) return ""; // sanity
\r
398 while(english[i]) {
\r
399 if(!strcmp(s, english[i])) return foreign[i];
\r
400 if(english[i][0] == '%' && strstr(s, english[i]+1) == s) { // allow translation of strings with variable ending
\r
401 snprintf(buf, MSG_SIZ, "%s%s", foreign[i], s + strlen(english[i]+1)); // keep unmatched portion
\r
410 Translate(HWND hDlg, int dialogID)
\r
411 { // translate all text items in the given dialog
\r
413 char buf[MSG_SIZ], *s;
\r
414 if(!barbaric) return;
\r
415 while(dialogItems[i][0] && dialogItems[i][0] != dialogID) i++; // find the dialog description
\r
416 if(dialogItems[i][0] != dialogID) return; // unknown dialog, should not happen
\r
417 GetWindowText( hDlg, buf, MSG_SIZ );
\r
419 if(strcmp(buf, s)) SetWindowText(hDlg, s); // replace by translated string (if different)
\r
420 for(j=1; k=dialogItems[i][j]; j++) { // translate all listed dialog items
\r
421 GetDlgItemText(hDlg, k, buf, MSG_SIZ);
\r
422 if(strlen(buf) == 0) continue;
\r
424 if(strcmp(buf, s)) SetDlgItemText(hDlg, k, s); // replace by translated string (if different)
\r
429 TranslateOneMenu(int i, HMENU subMenu)
\r
432 static MENUITEMINFO info;
\r
434 info.cbSize = sizeof(MENUITEMINFO);
\r
435 info.fMask = MIIM_STATE | MIIM_TYPE;
\r
436 for(j=GetMenuItemCount(subMenu)-1; j>=0; j--){
\r
438 info.dwTypeData = buf;
\r
439 info.cch = sizeof(buf);
\r
440 GetMenuItemInfo(subMenu, j, TRUE, &info);
\r
442 if(menuText[i][j]) safeStrCpy(buf, menuText[i][j], sizeof(buf)/sizeof(buf[0]) );
\r
443 else menuText[i][j] = strdup(buf); // remember original on first change
\r
445 if(buf[0] == NULLCHAR) continue;
\r
446 info.dwTypeData = T_(buf);
\r
447 info.cch = strlen(buf)+1;
\r
448 SetMenuItemInfo(subMenu, j, TRUE, &info);
\r
454 TranslateMenus(int addLanguage)
\r
457 WIN32_FIND_DATA fileData;
\r
459 #define IDM_English 1970
\r
461 HMENU mainMenu = GetMenu(hwndMain);
\r
462 for (i=GetMenuItemCount(mainMenu)-1; i>=0; i--) {
\r
463 HMENU subMenu = GetSubMenu(mainMenu, i);
\r
464 ModifyMenu(mainMenu, i, MF_STRING|MF_BYPOSITION|MF_POPUP|EnableMenuItem(mainMenu, i, MF_BYPOSITION),
\r
465 (UINT) subMenu, T_(menuBarText[tinyLayout][i]));
\r
466 TranslateOneMenu(i, subMenu);
\r
468 DrawMenuBar(hwndMain);
\r
471 if(!addLanguage) return;
\r
472 if((hFind = FindFirstFile("*.LNG", &fileData)) != INVALID_HANDLE_VALUE) {
\r
473 HMENU mainMenu = GetMenu(hwndMain);
\r
474 HMENU subMenu = GetSubMenu(mainMenu, GetMenuItemCount(mainMenu)-1);
\r
475 AppendMenu(subMenu, MF_SEPARATOR, (UINT_PTR) 0, NULL);
\r
476 AppendMenu(subMenu, MF_ENABLED|MF_STRING|(barbaric?MF_UNCHECKED:MF_CHECKED), (UINT_PTR) IDM_English, (LPCTSTR) "English");
\r
477 i = 0; lastChecked = IDM_English;
\r
479 char *p, *q = fileData.cFileName;
\r
480 int checkFlag = MF_UNCHECKED;
\r
481 languageFile[i] = strdup(q);
\r
482 if(barbaric && !strcmp(oldLanguage, q)) {
\r
483 checkFlag = MF_CHECKED;
\r
484 lastChecked = IDM_English + i + 1;
\r
485 CheckMenuItem(mainMenu, IDM_English, MF_BYCOMMAND|MF_UNCHECKED);
\r
487 *q = ToUpper(*q); while(*++q) *q = ToLower(*q);
\r
488 p = strstr(fileData.cFileName, ".lng");
\r
490 AppendMenu(subMenu, MF_ENABLED|MF_STRING|checkFlag, (UINT_PTR) IDM_English + ++i, (LPCTSTR) fileData.cFileName);
\r
491 } while(FindNextFile(hFind, &fileData));
\r
498 #define IDM_RecentEngines 3000
\r
501 RecentEngineMenu (char *s)
\r
503 if(appData.icsActive) return;
\r
504 if(appData.recentEngines > 0 && *s) { // feature is on, and list non-empty
\r
505 HMENU mainMenu = GetMenu(hwndMain);
\r
506 HMENU subMenu = GetSubMenu(mainMenu, 5); // Engine menu
\r
507 int i=IDM_RecentEngines;
\r
508 recentEngines = strdup(appData.recentEngineList); // remember them as they are in menu
\r
509 AppendMenu(subMenu, MF_SEPARATOR, (UINT_PTR) 0, NULL);
\r
511 char *p = strchr(s, '\n');
\r
512 if(p == NULL) return; // malformed!
\r
514 AppendMenu(subMenu, MF_ENABLED|MF_STRING|MF_UNCHECKED, (UINT_PTR) i++, (LPCTSTR) s);
\r
528 int cliWidth, cliHeight;
\r
531 SizeInfo sizeInfo[] =
\r
533 { "tiny", 21, 0, 1, 1, 0, 0 },
\r
534 { "teeny", 25, 1, 1, 1, 0, 0 },
\r
535 { "dinky", 29, 1, 1, 1, 0, 0 },
\r
536 { "petite", 33, 1, 1, 1, 0, 0 },
\r
537 { "slim", 37, 2, 1, 0, 0, 0 },
\r
538 { "small", 40, 2, 1, 0, 0, 0 },
\r
539 { "mediocre", 45, 2, 1, 0, 0, 0 },
\r
540 { "middling", 49, 2, 0, 0, 0, 0 },
\r
541 { "average", 54, 2, 0, 0, 0, 0 },
\r
542 { "moderate", 58, 3, 0, 0, 0, 0 },
\r
543 { "medium", 64, 3, 0, 0, 0, 0 },
\r
544 { "bulky", 72, 3, 0, 0, 0, 0 },
\r
545 { "large", 80, 3, 0, 0, 0, 0 },
\r
546 { "big", 87, 3, 0, 0, 0, 0 },
\r
547 { "huge", 95, 3, 0, 0, 0, 0 },
\r
548 { "giant", 108, 3, 0, 0, 0, 0 },
\r
549 { "colossal", 116, 4, 0, 0, 0, 0 },
\r
550 { "titanic", 129, 4, 0, 0, 0, 0 },
\r
551 { NULL, 0, 0, 0, 0, 0, 0 }
\r
554 #define MF(x) {x, {{0,}, 0. }, {0, }, 0}
\r
555 MyFont fontRec[NUM_SIZES][NUM_FONTS] =
\r
557 { 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
558 { 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
559 { 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
560 { 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
561 { 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
562 { 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
563 { 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
564 { 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
565 { 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
566 { 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
567 { 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
568 { 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
569 { 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
570 { 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
571 { 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
572 { 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
573 { 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
574 { 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
577 MyFont *font[NUM_SIZES][NUM_FONTS];
\r
586 #define BUTTON_WIDTH (tinyLayout ? 16 : 32)
\r
587 #define N_BUTTONS 5
\r
589 MyButtonDesc buttonDesc[N_BUTTONS] =
\r
591 {"<<", IDM_ToStart, NULL, NULL},
\r
592 {"<", IDM_Backward, NULL, NULL},
\r
593 {"P", IDM_Pause, NULL, NULL},
\r
594 {">", IDM_Forward, NULL, NULL},
\r
595 {">>", IDM_ToEnd, NULL, NULL},
\r
598 int tinyLayout = 0, smallLayout = 0;
\r
599 #define MENU_BAR_ITEMS 9
\r
600 char *menuBarText[2][MENU_BAR_ITEMS+1] = {
\r
601 { N_("&File"), N_("&Edit"), N_("&View"), N_("&Mode"), N_("&Action"), N_("E&ngine"), N_("&Options"), N_("&Help"), NULL },
\r
602 { N_("&F"), N_("&E"), N_("&V"), N_("&M"), N_("&A"), N_("&N"), N_("&O"), N_("&H"), NULL },
\r
606 MySound sounds[(int)NSoundClasses];
\r
607 MyTextAttribs textAttribs[(int)NColorClasses];
\r
609 MyColorizeAttribs colorizeAttribs[] = {
\r
610 { (COLORREF)0, 0, N_("Shout Text") },
\r
611 { (COLORREF)0, 0, N_("SShout/CShout") },
\r
612 { (COLORREF)0, 0, N_("Channel 1 Text") },
\r
613 { (COLORREF)0, 0, N_("Channel Text") },
\r
614 { (COLORREF)0, 0, N_("Kibitz Text") },
\r
615 { (COLORREF)0, 0, N_("Tell Text") },
\r
616 { (COLORREF)0, 0, N_("Challenge Text") },
\r
617 { (COLORREF)0, 0, N_("Request Text") },
\r
618 { (COLORREF)0, 0, N_("Seek Text") },
\r
619 { (COLORREF)0, 0, N_("Normal Text") },
\r
620 { (COLORREF)0, 0, N_("None") }
\r
625 static char *commentTitle;
\r
626 static char *commentText;
\r
627 static int commentIndex;
\r
628 static Boolean editComment = FALSE;
\r
631 char errorTitle[MSG_SIZ];
\r
632 char errorMessage[2*MSG_SIZ];
\r
633 HWND errorDialog = NULL;
\r
634 BOOLEAN moveErrorMessageUp = FALSE;
\r
635 BOOLEAN consoleEcho = TRUE;
\r
636 CHARFORMAT consoleCF;
\r
637 COLORREF consoleBackgroundColor;
\r
639 char *programVersion;
\r
645 typedef int CPKind;
\r
654 SOCKET sock2; /* stderr socket for OpenRcmd */
\r
657 #define INPUT_SOURCE_BUF_SIZE 4096
\r
659 typedef struct _InputSource {
\r
666 char buf[INPUT_SOURCE_BUF_SIZE];
\r
670 InputCallback func;
\r
671 struct _InputSource *second; /* for stderr thread on CPRcmd */
\r
675 InputSource *consoleInputSource;
\r
680 VOID ConsoleOutput(char* data, int length, int forceVisible);
\r
681 VOID ConsoleCreate();
\r
683 ConsoleWndProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam);
\r
684 VOID ColorizeTextPopup(HWND hwnd, ColorClass cc);
\r
685 VOID PrintCommSettings(FILE *f, char *name, DCB *dcb);
\r
686 VOID ParseCommSettings(char *arg, DCB *dcb);
\r
688 StartupDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam);
\r
689 VOID APIENTRY MenuPopup(HWND hwnd, POINT pt, HMENU hmenu, UINT def);
\r
690 void ParseIcsTextMenu(char *icsTextMenuString);
\r
691 VOID PopUpNameDialog(char firstchar);
\r
692 VOID UpdateSampleText(HWND hDlg, int id, MyColorizeAttribs *mca);
\r
696 int GameListOptions();
\r
698 int dummy; // [HGM] for obsolete args
\r
700 HWND hwndMain = NULL; /* root window*/
\r
701 HWND hwndConsole = NULL;
\r
702 HWND commentDialog = NULL;
\r
703 HWND moveHistoryDialog = NULL;
\r
704 HWND evalGraphDialog = NULL;
\r
705 HWND engineOutputDialog = NULL;
\r
706 HWND gameListDialog = NULL;
\r
707 HWND editTagsDialog = NULL;
\r
709 int commentUp = FALSE;
\r
711 WindowPlacement wpMain;
\r
712 WindowPlacement wpConsole;
\r
713 WindowPlacement wpComment;
\r
714 WindowPlacement wpMoveHistory;
\r
715 WindowPlacement wpEvalGraph;
\r
716 WindowPlacement wpEngineOutput;
\r
717 WindowPlacement wpGameList;
\r
718 WindowPlacement wpTags;
\r
720 VOID EngineOptionsPopup(); // [HGM] settings
\r
722 VOID GothicPopUp(char *title, VariantClass variant);
\r
724 * Setting "frozen" should disable all user input other than deleting
\r
725 * the window. We do this while engines are initializing themselves.
\r
727 static int frozen = 0;
\r
728 static int oldMenuItemState[MENU_BAR_ITEMS];
\r
734 if (frozen) return;
\r
736 hmenu = GetMenu(hwndMain);
\r
737 for (i=0; i<MENU_BAR_ITEMS; i++) {
\r
738 oldMenuItemState[i] = EnableMenuItem(hmenu, i, MF_BYPOSITION|MF_GRAYED);
\r
740 DrawMenuBar(hwndMain);
\r
743 /* Undo a FreezeUI */
\r
749 if (!frozen) return;
\r
751 hmenu = GetMenu(hwndMain);
\r
752 for (i=0; i<MENU_BAR_ITEMS; i++) {
\r
753 EnableMenuItem(hmenu, i, MF_BYPOSITION|oldMenuItemState[i]);
\r
755 DrawMenuBar(hwndMain);
\r
758 /*static*/ int fromX = -1, fromY = -1, toX, toY; // [HGM] moved upstream, so JAWS can use them
\r
760 /* JAWS preparation patch (WinBoard for the sight impaired). Define required insertions as empty */
\r
766 #define JAWS_ALT_INTERCEPT
\r
767 #define JAWS_KBUP_NAVIGATION
\r
768 #define JAWS_KBDOWN_NAVIGATION
\r
769 #define JAWS_MENU_ITEMS
\r
770 #define JAWS_SILENCE
\r
771 #define JAWS_REPLAY
\r
773 #define JAWS_COPYRIGHT
\r
774 #define JAWS_DELETE(X) X
\r
775 #define SAYMACHINEMOVE()
\r
779 /*---------------------------------------------------------------------------*\
\r
783 \*---------------------------------------------------------------------------*/
\r
786 WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
\r
787 LPSTR lpCmdLine, int nCmdShow)
\r
790 HANDLE hAccelMain, hAccelNoAlt, hAccelNoICS;
\r
791 // INITCOMMONCONTROLSEX ex;
\r
795 LoadLibrary("RICHED32.DLL");
\r
796 consoleCF.cbSize = sizeof(CHARFORMAT);
\r
798 if (!InitApplication(hInstance)) {
\r
801 if (!InitInstance(hInstance, nCmdShow, lpCmdLine)) {
\r
808 // InitCommonControlsEx(&ex);
\r
809 InitCommonControls();
\r
811 hAccelMain = LoadAccelerators (hInstance, szAppName);
\r
812 hAccelNoAlt = LoadAccelerators (hInstance, "NO_ALT");
\r
813 hAccelNoICS = LoadAccelerators( hInstance, "NO_ICS"); /* [AS] No Ctrl-V on ICS!!! */
\r
815 /* Acquire and dispatch messages until a WM_QUIT message is received. */
\r
817 while (GetMessage(&msg, /* message structure */
\r
818 NULL, /* handle of window receiving the message */
\r
819 0, /* lowest message to examine */
\r
820 0)) /* highest message to examine */
\r
823 if(msg.message == WM_CHAR && msg.wParam == '\t') {
\r
824 // [HGM] navigate: switch between all windows with tab
\r
825 HWND e1 = NULL, e2 = NULL, mh = NULL, hInput = NULL, hText = NULL;
\r
826 int i, currentElement = 0;
\r
828 // first determine what element of the chain we come from (if any)
\r
829 if(appData.icsActive) {
\r
830 hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
831 hText = GetDlgItem(hwndConsole, OPT_ConsoleText);
\r
833 if(engineOutputDialog && EngineOutputIsUp()) {
\r
834 e1 = GetDlgItem(engineOutputDialog, IDC_EngineMemo1);
\r
835 e2 = GetDlgItem(engineOutputDialog, IDC_EngineMemo2);
\r
837 if(moveHistoryDialog && MoveHistoryIsUp()) {
\r
838 mh = GetDlgItem(moveHistoryDialog, IDC_MoveHistory);
\r
840 if(msg.hwnd == hwndMain) currentElement = 7 ; else
\r
841 if(msg.hwnd == engineOutputDialog) currentElement = 2; else
\r
842 if(msg.hwnd == e1) currentElement = 2; else
\r
843 if(msg.hwnd == e2) currentElement = 3; else
\r
844 if(msg.hwnd == moveHistoryDialog) currentElement = 4; else
\r
845 if(msg.hwnd == mh) currentElement = 4; else
\r
846 if(msg.hwnd == evalGraphDialog) currentElement = 6; else
\r
847 if(msg.hwnd == hText) currentElement = 5; else
\r
848 if(msg.hwnd == hInput) currentElement = 6; else
\r
849 for (i = 0; i < N_BUTTONS; i++) {
\r
850 if (buttonDesc[i].hwnd == msg.hwnd) { currentElement = 1; break; }
\r
853 // determine where to go to
\r
854 if(currentElement) { HWND h = NULL; int direction = GetKeyState(VK_SHIFT) < 0 ? -1 : 1;
\r
856 currentElement = (currentElement + direction) % 7;
\r
857 switch(currentElement) {
\r
859 h = hwndMain; break; // passing this case always makes the loop exit
\r
861 h = buttonDesc[0].hwnd; break; // could be NULL
\r
863 if(!EngineOutputIsUp()) continue; // skip closed auxiliary windows
\r
866 if(!EngineOutputIsUp()) continue;
\r
869 if(!MoveHistoryIsUp()) continue;
\r
871 // case 6: // input to eval graph does not seem to get here!
\r
872 // if(!EvalGraphIsUp()) continue;
\r
873 // h = evalGraphDialog; break;
\r
875 if(!appData.icsActive) continue;
\r
879 if(!appData.icsActive) continue;
\r
885 if(currentElement > 4 && IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);
\r
886 if(currentElement < 5 && IsIconic(hwndMain)) ShowWindow(hwndMain, SW_RESTORE); // all open together
\r
889 continue; // this message now has been processed
\r
893 if (!(commentDialog && IsDialogMessage(commentDialog, &msg)) &&
\r
894 !(moveHistoryDialog && IsDialogMessage(moveHistoryDialog, &msg)) &&
\r
895 !(evalGraphDialog && IsDialogMessage(evalGraphDialog, &msg)) &&
\r
896 !(engineOutputDialog && IsDialogMessage(engineOutputDialog, &msg)) &&
\r
897 !(editTagsDialog && IsDialogMessage(editTagsDialog, &msg)) &&
\r
898 !(gameListDialog && IsDialogMessage(gameListDialog, &msg)) &&
\r
899 !(errorDialog && IsDialogMessage(errorDialog, &msg)) &&
\r
900 !(!frozen && TranslateAccelerator(hwndMain, hAccelMain, &msg)) && JAWS_ACCEL
\r
901 !(!hwndConsole && TranslateAccelerator(hwndMain, hAccelNoICS, &msg)) &&
\r
902 !(!hwndConsole && TranslateAccelerator(hwndMain, hAccelNoAlt, &msg))) {
\r
903 int done = 0, i; // [HGM] chat: dispatch cat-box messages
\r
904 for(i=0; i<MAX_CHAT; i++)
\r
905 if(chatHandle[i] && IsDialogMessage(chatHandle[i], &msg)) {
\r
908 if(done) continue; // [HGM] chat: end patch
\r
909 TranslateMessage(&msg); /* Translates virtual key codes */
\r
910 DispatchMessage(&msg); /* Dispatches message to window */
\r
915 return (msg.wParam); /* Returns the value from PostQuitMessage */
\r
918 /*---------------------------------------------------------------------------*\
\r
920 * Initialization functions
\r
922 \*---------------------------------------------------------------------------*/
\r
926 { // update user logo if necessary
\r
927 static char oldUserName[MSG_SIZ], dir[MSG_SIZ], *curName;
\r
929 if(appData.autoLogo) {
\r
930 curName = UserName();
\r
931 if(strcmp(curName, oldUserName)) {
\r
932 GetCurrentDirectory(MSG_SIZ, dir);
\r
933 SetCurrentDirectory(installDir);
\r
934 snprintf(oldUserName, MSG_SIZ, "logos\\%s.bmp", curName);
\r
935 userLogo = LoadImage( 0, oldUserName, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );
\r
936 safeStrCpy(oldUserName, curName, sizeof(oldUserName)/sizeof(oldUserName[0]) );
\r
937 if(userLogo == NULL)
\r
938 userLogo = LoadImage( 0, "logos\\dummy.bmp", IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );
\r
939 SetCurrentDirectory(dir); /* return to prev directory */
\r
945 InitApplication(HINSTANCE hInstance)
\r
949 /* Fill in window class structure with parameters that describe the */
\r
952 wc.style = CS_HREDRAW | CS_VREDRAW; /* Class style(s). */
\r
953 wc.lpfnWndProc = (WNDPROC)WndProc; /* Window Procedure */
\r
954 wc.cbClsExtra = 0; /* No per-class extra data. */
\r
955 wc.cbWndExtra = 0; /* No per-window extra data. */
\r
956 wc.hInstance = hInstance; /* Owner of this class */
\r
957 wc.hIcon = LoadIcon(hInstance, "icon_white");
\r
958 wc.hCursor = LoadCursor(NULL, IDC_ARROW); /* Cursor */
\r
959 wc.hbrBackground = (HBRUSH)(COLOR_WINDOW+1); /* Default color */
\r
960 wc.lpszMenuName = szAppName; /* Menu name from .RC */
\r
961 wc.lpszClassName = szAppName; /* Name to register as */
\r
963 /* Register the window class and return success/failure code. */
\r
964 if (!RegisterClass(&wc)) return FALSE;
\r
966 wc.style = CS_HREDRAW | CS_VREDRAW;
\r
967 wc.lpfnWndProc = (WNDPROC)ConsoleWndProc;
\r
969 wc.cbWndExtra = DLGWINDOWEXTRA;
\r
970 wc.hInstance = hInstance;
\r
971 wc.hIcon = LoadIcon(hInstance, "icon_white");
\r
972 wc.hCursor = LoadCursor(NULL, IDC_ARROW);
\r
973 wc.hbrBackground = (HBRUSH)(COLOR_MENU+1);
\r
974 wc.lpszMenuName = NULL;
\r
975 wc.lpszClassName = szConsoleName;
\r
977 if (!RegisterClass(&wc)) return FALSE;
\r
982 /* Set by InitInstance, used by EnsureOnScreen */
\r
983 int screenHeight, screenWidth;
\r
986 EnsureOnScreen(int *x, int *y, int minX, int minY)
\r
988 // int gap = GetSystemMetrics(SM_CYFRAME) + GetSystemMetrics(SM_CYCAPTION);
\r
989 /* Be sure window at (x,y) is not off screen (or even mostly off screen) */
\r
990 if (*x > screenWidth - 32) *x = 0;
\r
991 if (*y > screenHeight - 32) *y = 0;
\r
992 if (*x < minX) *x = minX;
\r
993 if (*y < minY) *y = minY;
\r
997 LoadLogo(ChessProgramState *cps, int n, Boolean ics)
\r
999 char buf[MSG_SIZ], dir[MSG_SIZ];
\r
1000 GetCurrentDirectory(MSG_SIZ, dir);
\r
1001 SetCurrentDirectory(installDir);
\r
1002 if( appData.logo[n] && appData.logo[n][0] != NULLCHAR) {
\r
1003 cps->programLogo = LoadImage( 0, appData.logo[n], IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );
\r
1005 if (cps->programLogo == NULL && appData.debugMode) {
\r
1006 fprintf( debugFP, "Unable to load logo bitmap '%s'\n", appData.logo[n] );
\r
1008 } else if(appData.autoLogo) {
\r
1009 if(ics) { // [HGM] logo: in ICS mode second can be used for ICS
\r
1010 char *opponent = "";
\r
1011 if(gameMode == IcsPlayingWhite) opponent = gameInfo.black;
\r
1012 if(gameMode == IcsPlayingBlack) opponent = gameInfo.white;
\r
1013 sprintf(buf, "logos\\%s\\%s.bmp", appData.icsHost, opponent);
\r
1014 if(!*opponent || !(cps->programLogo = LoadImage( 0, buf, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE ))) {
\r
1015 sprintf(buf, "logos\\%s.bmp", appData.icsHost);
\r
1016 cps->programLogo = LoadImage( 0, buf, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );
\r
1019 if(appData.directory[n] && appData.directory[n][0]) {
\r
1020 SetCurrentDirectory(appData.directory[n]);
\r
1021 cps->programLogo = LoadImage( 0, "logo.bmp", IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );
\r
1024 SetCurrentDirectory(dir); /* return to prev directory */
\r
1030 ZeroMemory( &backTextureSquareInfo, sizeof(backTextureSquareInfo) );
\r
1031 backTextureSquareSize = 0; // kludge to force recalculation of texturemode
\r
1033 if( appData.liteBackTextureFile && appData.liteBackTextureFile[0] != NULLCHAR && appData.liteBackTextureFile[0] != '*' ) {
\r
1034 if(liteBackTexture) DeleteObject(liteBackTexture);
\r
1035 liteBackTexture = LoadImage( 0, appData.liteBackTextureFile, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );
\r
1036 liteBackTextureMode = appData.liteBackTextureMode;
\r
1038 if (liteBackTexture == NULL && appData.debugMode) {
\r
1039 fprintf( debugFP, "Unable to load lite texture bitmap '%s'\n", appData.liteBackTextureFile );
\r
1043 if( appData.darkBackTextureFile && appData.darkBackTextureFile[0] != NULLCHAR && appData.darkBackTextureFile[0] != '*' ) {
\r
1044 if(darkBackTexture) DeleteObject(darkBackTexture);
\r
1045 darkBackTexture = LoadImage( 0, appData.darkBackTextureFile, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );
\r
1046 darkBackTextureMode = appData.darkBackTextureMode;
\r
1048 if (darkBackTexture == NULL && appData.debugMode) {
\r
1049 fprintf( debugFP, "Unable to load dark texture bitmap '%s'\n", appData.darkBackTextureFile );
\r
1055 InitInstance(HINSTANCE hInstance, int nCmdShow, LPSTR lpCmdLine)
\r
1057 HWND hwnd; /* Main window handle. */
\r
1059 WINDOWPLACEMENT wp;
\r
1062 hInst = hInstance; /* Store instance handle in our global variable */
\r
1063 programName = szAppName;
\r
1065 if (SearchPath(NULL, "WinBoard.exe", NULL, MSG_SIZ, installDir, &filepart)) {
\r
1066 *filepart = NULLCHAR;
\r
1067 SetCurrentDirectory(installDir);
\r
1069 GetCurrentDirectory(MSG_SIZ, installDir);
\r
1071 gameInfo.boardWidth = gameInfo.boardHeight = 8; // [HGM] won't have open window otherwise
\r
1072 screenWidth = screenHeight = 1000; // [HGM] placement: kludge to allow calling EnsureOnScreen from InitAppData
\r
1073 InitAppData(lpCmdLine); /* Get run-time parameters */
\r
1074 /* xboard, and older WinBoards, controlled the move sound with the
\r
1075 appData.ringBellAfterMoves option. In the current WinBoard, we
\r
1076 always turn the option on (so that the backend will call us),
\r
1077 then let the user turn the sound off by setting it to silence if
\r
1078 desired. To accommodate old winboard.ini files saved by old
\r
1079 versions of WinBoard, we also turn off the sound if the option
\r
1080 was initially set to false. [HGM] taken out of InitAppData */
\r
1081 if (!appData.ringBellAfterMoves) {
\r
1082 sounds[(int)SoundMove].name = strdup("");
\r
1083 appData.ringBellAfterMoves = TRUE;
\r
1085 if (appData.debugMode) {
\r
1086 debugFP = fopen(appData.nameOfDebugFile, "w");
\r
1087 setbuf(debugFP, NULL);
\r
1090 LoadLanguageFile(appData.language);
\r
1094 // InitEngineUCI( installDir, &first ); // [HGM] incorporated in InitBackEnd1()
\r
1095 // InitEngineUCI( installDir, &second );
\r
1097 /* Create a main window for this application instance. */
\r
1098 hwnd = CreateWindow(szAppName, szTitle,
\r
1099 (WS_OVERLAPPEDWINDOW & ~WS_MAXIMIZEBOX),
\r
1100 CW_USEDEFAULT, 0, CW_USEDEFAULT, 0,
\r
1101 NULL, NULL, hInstance, NULL);
\r
1104 /* If window could not be created, return "failure" */
\r
1109 /* [HGM] logo: Load logos if specified (must be done before InitDrawingSizes) */
\r
1110 LoadLogo(&first, 0, FALSE);
\r
1111 LoadLogo(&second, 1, appData.icsActive);
\r
1115 iconWhite = LoadIcon(hInstance, "icon_white");
\r
1116 iconBlack = LoadIcon(hInstance, "icon_black");
\r
1117 iconCurrent = iconWhite;
\r
1118 InitDrawingColors();
\r
1119 screenHeight = GetSystemMetrics(SM_CYSCREEN);
\r
1120 screenWidth = GetSystemMetrics(SM_CXSCREEN);
\r
1121 InitPosition(0); // to set nr of ranks and files, which might be non-default through command-line args
\r
1122 for (ibs = (int) NUM_SIZES - 1; ibs >= 0; ibs--) {
\r
1123 /* Compute window size for each board size, and use the largest
\r
1124 size that fits on this screen as the default. */
\r
1125 InitDrawingSizes((BoardSize)(ibs+1000), 0);
\r
1126 if (boardSize == (BoardSize)-1 &&
\r
1127 winH <= screenHeight
\r
1128 - GetSystemMetrics(SM_CYFRAME) - GetSystemMetrics(SM_CYCAPTION) - 10
\r
1129 && winW <= screenWidth) {
\r
1130 boardSize = (BoardSize)ibs;
\r
1134 InitDrawingSizes(boardSize, 0);
\r
1135 RecentEngineMenu(appData.recentEngineList);
\r
1137 buttonCount = GetSystemMetrics(SM_CMOUSEBUTTONS);
\r
1139 /* [AS] Load textures if specified */
\r
1142 mysrandom( (unsigned) time(NULL) );
\r
1144 /* [AS] Restore layout */
\r
1145 if( wpMoveHistory.visible ) {
\r
1146 MoveHistoryPopUp();
\r
1149 if( wpEvalGraph.visible ) {
\r
1153 if( wpEngineOutput.visible ) {
\r
1154 EngineOutputPopUp();
\r
1157 /* Make the window visible; update its client area; and return "success" */
\r
1158 EnsureOnScreen(&wpMain.x, &wpMain.y, minX, minY);
\r
1159 wp.length = sizeof(WINDOWPLACEMENT);
\r
1161 wp.showCmd = nCmdShow;
\r
1162 wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;
\r
1163 wp.rcNormalPosition.left = wpMain.x;
\r
1164 wp.rcNormalPosition.right = wpMain.x + wpMain.width;
\r
1165 wp.rcNormalPosition.top = wpMain.y;
\r
1166 wp.rcNormalPosition.bottom = wpMain.y + wpMain.height;
\r
1167 SetWindowPlacement(hwndMain, &wp);
\r
1169 InitBackEnd2(); // [HGM] moved until after all windows placed, to save correct position if fatal error on engine start
\r
1171 if(!appData.noGUI) SetWindowPos(hwndMain, alwaysOnTop ? HWND_TOPMOST : HWND_NOTOPMOST,
\r
1172 0, 0, 0, 0, SWP_NOMOVE|SWP_NOSIZE);
\r
1174 if (hwndConsole) {
\r
1176 SetWindowPos(hwndConsole, alwaysOnTop ? HWND_TOPMOST : HWND_NOTOPMOST,
\r
1177 0, 0, 0, 0, SWP_NOMOVE|SWP_NOSIZE);
\r
1179 ShowWindow(hwndConsole, nCmdShow);
\r
1180 SetActiveWindow(hwndConsole);
\r
1182 if(!appData.noGUI) UpdateWindow(hwnd); else ShowWindow(hwnd, SW_MINIMIZE);
\r
1183 if(gameListDialog) SetFocus(gameListDialog); // [HGM] jaws: for if we clicked multi-game game file
\r
1192 HMENU hmenu = GetMenu(hwndMain);
\r
1194 (void) EnableMenuItem(hmenu, IDM_CommPort,
\r
1195 MF_BYCOMMAND|((appData.icsActive &&
\r
1196 *appData.icsCommPort != NULLCHAR) ?
\r
1197 MF_ENABLED : MF_GRAYED));
\r
1198 (void) CheckMenuItem(hmenu, IDM_SaveSettingsOnExit,
\r
1199 MF_BYCOMMAND|(saveSettingsOnExit ?
\r
1200 MF_CHECKED : MF_UNCHECKED));
\r
1203 //---------------------------------------------------------------------------------------------------------
\r
1205 #define ICS_TEXT_MENU_SIZE (IDM_CommandXLast - IDM_CommandX + 1)
\r
1206 #define XBOARD FALSE
\r
1208 #define OPTCHAR "/"
\r
1209 #define SEPCHAR "="
\r
1210 #define TOPLEVEL 0
\r
1214 // front-end part of option handling
\r
1217 LFfromMFP(LOGFONT* lf, MyFontParams *mfp)
\r
1219 HDC hdc = CreateDC("DISPLAY", NULL, NULL, NULL);
\r
1220 lf->lfHeight = -(int)(mfp->pointSize * GetDeviceCaps(hdc, LOGPIXELSY) / 72.0 + 0.5);
\r
1223 lf->lfEscapement = 0;
\r
1224 lf->lfOrientation = 0;
\r
1225 lf->lfWeight = mfp->bold ? FW_BOLD : FW_NORMAL;
\r
1226 lf->lfItalic = mfp->italic;
\r
1227 lf->lfUnderline = mfp->underline;
\r
1228 lf->lfStrikeOut = mfp->strikeout;
\r
1229 lf->lfCharSet = mfp->charset;
\r
1230 lf->lfOutPrecision = OUT_DEFAULT_PRECIS;
\r
1231 lf->lfClipPrecision = CLIP_DEFAULT_PRECIS;
\r
1232 lf->lfQuality = DEFAULT_QUALITY;
\r
1233 lf->lfPitchAndFamily = DEFAULT_PITCH|FF_DONTCARE;
\r
1234 safeStrCpy(lf->lfFaceName, mfp->faceName, sizeof(lf->lfFaceName)/sizeof(lf->lfFaceName[0]) );
\r
1238 CreateFontInMF(MyFont *mf)
\r
1240 LFfromMFP(&mf->lf, &mf->mfp);
\r
1241 if (mf->hf) DeleteObject(mf->hf);
\r
1242 mf->hf = CreateFontIndirect(&mf->lf);
\r
1245 // [HGM] This platform-dependent table provides the location for storing the color info
\r
1247 colorVariable[] = {
\r
1248 &whitePieceColor,
\r
1249 &blackPieceColor,
\r
1250 &lightSquareColor,
\r
1251 &darkSquareColor,
\r
1252 &highlightSquareColor,
\r
1253 &premoveHighlightColor,
\r
1255 &consoleBackgroundColor,
\r
1256 &appData.fontForeColorWhite,
\r
1257 &appData.fontBackColorWhite,
\r
1258 &appData.fontForeColorBlack,
\r
1259 &appData.fontBackColorBlack,
\r
1260 &appData.evalHistColorWhite,
\r
1261 &appData.evalHistColorBlack,
\r
1262 &appData.highlightArrowColor,
\r
1265 /* Command line font name parser. NULL name means do nothing.
\r
1266 Syntax like "Courier New:10.0 bi" or "Arial:10" or "Arial:10b"
\r
1267 For backward compatibility, syntax without the colon is also
\r
1268 accepted, but font names with digits in them won't work in that case.
\r
1271 ParseFontName(char *name, MyFontParams *mfp)
\r
1274 if (name == NULL) return;
\r
1276 q = strchr(p, ':');
\r
1278 if (q - p >= sizeof(mfp->faceName))
\r
1279 ExitArgError(_("Font name too long:"), name, TRUE);
\r
1280 memcpy(mfp->faceName, p, q - p);
\r
1281 mfp->faceName[q - p] = NULLCHAR;
\r
1284 q = mfp->faceName;
\r
1286 while (*p && !isdigit(*p)) {
\r
1288 if (q - mfp->faceName >= sizeof(mfp->faceName))
\r
1289 ExitArgError(_("Font name too long:"), name, TRUE);
\r
1291 while (q > mfp->faceName && q[-1] == ' ') q--;
\r
1294 if (!*p) ExitArgError(_("Font point size missing:"), name, TRUE);
\r
1295 mfp->pointSize = (float) atof(p);
\r
1296 mfp->bold = (strchr(p, 'b') != NULL);
\r
1297 mfp->italic = (strchr(p, 'i') != NULL);
\r
1298 mfp->underline = (strchr(p, 'u') != NULL);
\r
1299 mfp->strikeout = (strchr(p, 's') != NULL);
\r
1300 mfp->charset = DEFAULT_CHARSET;
\r
1301 q = strchr(p, 'c');
\r
1303 mfp->charset = (BYTE) atoi(q+1);
\r
1307 ParseFont(char *name, int number)
\r
1308 { // wrapper to shield back-end from 'font'
\r
1309 ParseFontName(name, &font[boardSize][number]->mfp);
\r
1314 { // in WB we have a 2D array of fonts; this initializes their description
\r
1316 /* Point font array elements to structures and
\r
1317 parse default font names */
\r
1318 for (i=0; i<NUM_FONTS; i++) {
\r
1319 for (j=0; j<NUM_SIZES; j++) {
\r
1320 font[j][i] = &fontRec[j][i];
\r
1321 ParseFontName(font[j][i]->def, &font[j][i]->mfp);
\r
1328 { // here we create the actual fonts from the selected descriptions
\r
1330 for (i=0; i<NUM_FONTS; i++) {
\r
1331 for (j=0; j<NUM_SIZES; j++) {
\r
1332 CreateFontInMF(font[j][i]);
\r
1336 /* Color name parser.
\r
1337 X version accepts X color names, but this one
\r
1338 handles only the #rrggbb form (hex) or rrr,ggg,bbb (decimal) */
\r
1340 ParseColorName(char *name)
\r
1342 int red, green, blue, count;
\r
1343 char buf[MSG_SIZ];
\r
1345 count = sscanf(name, "#%2x%2x%2x", &red, &green, &blue);
\r
1347 count = sscanf(name, "%3d%*[^0-9]%3d%*[^0-9]%3d",
\r
1348 &red, &green, &blue);
\r
1351 snprintf(buf, MSG_SIZ, _("Can't parse color name %s"), name);
\r
1352 DisplayError(buf, 0);
\r
1353 return RGB(0, 0, 0);
\r
1355 return PALETTERGB(red, green, blue);
\r
1359 ParseColor(int n, char *name)
\r
1360 { // for WinBoard the color is an int, which needs to be derived from the string
\r
1361 if(colorVariable[n]) *(int*)colorVariable[n] = ParseColorName(name);
\r
1365 ParseAttribs(COLORREF *color, int *effects, char* argValue)
\r
1367 char *e = argValue;
\r
1371 if (*e == 'b') eff |= CFE_BOLD;
\r
1372 else if (*e == 'i') eff |= CFE_ITALIC;
\r
1373 else if (*e == 'u') eff |= CFE_UNDERLINE;
\r
1374 else if (*e == 's') eff |= CFE_STRIKEOUT;
\r
1375 else if (*e == '#' || isdigit(*e)) break;
\r
1379 *color = ParseColorName(e);
\r
1383 ParseTextAttribs(ColorClass cc, char *s)
\r
1384 { // [HGM] front-end wrapper that does the platform-dependent call
\r
1385 // for XBoard we would set (&appData.colorShout)[cc] = strdup(s);
\r
1386 ParseAttribs(&textAttribs[cc].color, &textAttribs[cc].effects, s);
\r
1390 ParseBoardSize(void *addr, char *name)
\r
1391 { // [HGM] rewritten with return-value ptr to shield back-end from BoardSize
\r
1392 BoardSize bs = SizeTiny;
\r
1393 while (sizeInfo[bs].name != NULL) {
\r
1394 if (StrCaseCmp(name, sizeInfo[bs].name) == 0) {
\r
1395 *(BoardSize *)addr = bs;
\r
1400 ExitArgError(_("Unrecognized board size value"), name, TRUE);
\r
1405 { // [HGM] import name from appData first
\r
1408 for (cc = (ColorClass)0; cc < ColorNormal; cc++) {
\r
1409 textAttribs[cc].sound.name = strdup((&appData.soundShout)[cc]);
\r
1410 textAttribs[cc].sound.data = NULL;
\r
1411 MyLoadSound(&textAttribs[cc].sound);
\r
1413 for (cc = ColorNormal; cc < NColorClasses; cc++) {
\r
1414 textAttribs[cc].sound.name = strdup("");
\r
1415 textAttribs[cc].sound.data = NULL;
\r
1417 for (sc = (SoundClass)0; sc < NSoundClasses; sc++) {
\r
1418 sounds[sc].name = strdup((&appData.soundMove)[sc]);
\r
1419 sounds[sc].data = NULL;
\r
1420 MyLoadSound(&sounds[sc]);
\r
1425 SetCommPortDefaults()
\r
1427 memset(&dcb, 0, sizeof(DCB)); // required by VS 2002 +
\r
1428 dcb.DCBlength = sizeof(DCB);
\r
1429 dcb.BaudRate = 9600;
\r
1430 dcb.fBinary = TRUE;
\r
1431 dcb.fParity = FALSE;
\r
1432 dcb.fOutxCtsFlow = FALSE;
\r
1433 dcb.fOutxDsrFlow = FALSE;
\r
1434 dcb.fDtrControl = DTR_CONTROL_ENABLE;
\r
1435 dcb.fDsrSensitivity = FALSE;
\r
1436 dcb.fTXContinueOnXoff = TRUE;
\r
1437 dcb.fOutX = FALSE;
\r
1439 dcb.fNull = FALSE;
\r
1440 dcb.fRtsControl = RTS_CONTROL_ENABLE;
\r
1441 dcb.fAbortOnError = FALSE;
\r
1443 dcb.Parity = SPACEPARITY;
\r
1444 dcb.StopBits = ONESTOPBIT;
\r
1447 // [HGM] args: these three cases taken out to stay in front-end
\r
1449 SaveFontArg(FILE *f, ArgDescriptor *ad)
\r
1450 { // in WinBoard every board size has its own font, and the "argLoc" identifies the table,
\r
1451 // while the curent board size determines the element. This system should be ported to XBoard.
\r
1452 // What the table contains pointers to, and how to print the font description, remains platform-dependent
\r
1454 for (bs=0; bs<NUM_SIZES; bs++) {
\r
1455 MyFontParams *mfp = &font[bs][(int) ad->argLoc]->mfp;
\r
1456 fprintf(f, "/size=%s ", sizeInfo[bs].name);
\r
1457 fprintf(f, "/%s=\"%s:%g%s%s%s%s%sc%d\"\n",
\r
1458 ad->argName, mfp->faceName, mfp->pointSize,
\r
1459 mfp->bold || mfp->italic || mfp->underline || mfp->strikeout ? " " : "",
\r
1460 mfp->bold ? "b" : "",
\r
1461 mfp->italic ? "i" : "",
\r
1462 mfp->underline ? "u" : "",
\r
1463 mfp->strikeout ? "s" : "",
\r
1464 (int)mfp->charset);
\r
1470 { // [HGM] copy the names from the internal WB variables to appData
\r
1473 for (cc = (ColorClass)0; cc < ColorNormal; cc++)
\r
1474 (&appData.soundShout)[cc] = textAttribs[cc].sound.name;
\r
1475 for (sc = (SoundClass)0; sc < NSoundClasses; sc++)
\r
1476 (&appData.soundMove)[sc] = sounds[sc].name;
\r
1480 SaveAttribsArg(FILE *f, ArgDescriptor *ad)
\r
1481 { // here the "argLoc" defines a table index. It could have contained the 'ta' pointer itself, though
\r
1482 MyTextAttribs* ta = &textAttribs[(ColorClass)ad->argLoc];
\r
1483 fprintf(f, "/%s=\"%s%s%s%s%s#%02lx%02lx%02lx\"\n", ad->argName,
\r
1484 (ta->effects & CFE_BOLD) ? "b" : "",
\r
1485 (ta->effects & CFE_ITALIC) ? "i" : "",
\r
1486 (ta->effects & CFE_UNDERLINE) ? "u" : "",
\r
1487 (ta->effects & CFE_STRIKEOUT) ? "s" : "",
\r
1488 (ta->effects) ? " " : "",
\r
1489 ta->color&0xff, (ta->color >> 8)&0xff, (ta->color >> 16)&0xff);
\r
1493 SaveColor(FILE *f, ArgDescriptor *ad)
\r
1494 { // in WinBoard the color is an int and has to be converted to text. In X it would be a string already?
\r
1495 COLORREF color = *(COLORREF *)colorVariable[(int)ad->argLoc];
\r
1496 fprintf(f, "/%s=#%02lx%02lx%02lx\n", ad->argName,
\r
1497 color&0xff, (color>>8)&0xff, (color>>16)&0xff);
\r
1501 SaveBoardSize(FILE *f, char *name, void *addr)
\r
1502 { // wrapper to shield back-end from BoardSize & sizeInfo
\r
1503 fprintf(f, "/%s=%s\n", name, sizeInfo[*(BoardSize *)addr].name);
\r
1507 ParseCommPortSettings(char *s)
\r
1508 { // wrapper to keep dcb from back-end
\r
1509 ParseCommSettings(s, &dcb);
\r
1514 { // wrapper to shield use of window handles from back-end (make addressible by number?)
\r
1515 GetActualPlacement(hwndMain, &wpMain);
\r
1516 GetActualPlacement(hwndConsole, &wpConsole);
\r
1517 GetActualPlacement(commentDialog, &wpComment);
\r
1518 GetActualPlacement(editTagsDialog, &wpTags);
\r
1519 GetActualPlacement(gameListDialog, &wpGameList);
\r
1520 GetActualPlacement(moveHistoryDialog, &wpMoveHistory);
\r
1521 GetActualPlacement(evalGraphDialog, &wpEvalGraph);
\r
1522 GetActualPlacement(engineOutputDialog, &wpEngineOutput);
\r
1526 PrintCommPortSettings(FILE *f, char *name)
\r
1527 { // wrapper to shield back-end from DCB
\r
1528 PrintCommSettings(f, name, &dcb);
\r
1532 MySearchPath(char *installDir, char *name, char *fullname)
\r
1534 char *dummy, buf[MSG_SIZ], *p = name, *q;
\r
1535 if(name[0]== '%') {
\r
1536 fullname[0] = 0; // [HGM] first expand any environment variables in the given name
\r
1537 while(*p == '%' && (q = strchr(p+1, '%'))) { // [HGM] recognize %*% as environment variable
\r
1538 safeStrCpy(buf, p+1, sizeof(buf)/sizeof(buf[0]) );
\r
1539 *strchr(buf, '%') = 0;
\r
1540 strcat(fullname, getenv(buf));
\r
1541 p = q+1; while(*p == '\\') { strcat(fullname, "\\"); p++; }
\r
1543 strcat(fullname, p); // after environment variables (if any), take the remainder of the given name
\r
1544 if(appData.debugMode) fprintf(debugFP, "name = '%s', expanded name = '%s'\n", name, fullname);
\r
1545 return (int) strlen(fullname);
\r
1547 return (int) SearchPath(installDir, name, NULL, MSG_SIZ, fullname, &dummy);
\r
1551 MyGetFullPathName(char *name, char *fullname)
\r
1554 return (int) GetFullPathName(name, MSG_SIZ, fullname, &dummy);
\r
1559 { // [HGM] args: allows testing if main window is realized from back-end
\r
1560 return hwndMain != NULL;
\r
1564 PopUpStartupDialog()
\r
1568 LoadLanguageFile(appData.language);
\r
1569 lpProc = MakeProcInstance((FARPROC)StartupDialog, hInst);
\r
1570 DialogBox(hInst, MAKEINTRESOURCE(DLG_Startup), NULL, (DLGPROC)lpProc);
\r
1571 FreeProcInstance(lpProc);
\r
1574 /*---------------------------------------------------------------------------*\
\r
1576 * GDI board drawing routines
\r
1578 \*---------------------------------------------------------------------------*/
\r
1580 /* [AS] Draw square using background texture */
\r
1581 static void DrawTile( int dx, int dy, int dw, int dh, HDC dst, HDC src, int mode, int sx, int sy )
\r
1586 return; /* Should never happen! */
\r
1589 SetGraphicsMode( dst, GM_ADVANCED );
\r
1596 /* X reflection */
\r
1601 x.eDx = (FLOAT) dw + dx - 1;
\r
1604 SetWorldTransform( dst, &x );
\r
1607 /* Y reflection */
\r
1613 x.eDy = (FLOAT) dh + dy - 1;
\r
1615 SetWorldTransform( dst, &x );
\r
1623 x.eDx = (FLOAT) dx;
\r
1624 x.eDy = (FLOAT) dy;
\r
1627 SetWorldTransform( dst, &x );
\r
1631 BitBlt( dst, dx, dy, dw, dh, src, sx, sy, SRCCOPY );
\r
1639 SetWorldTransform( dst, &x );
\r
1641 ModifyWorldTransform( dst, 0, MWT_IDENTITY );
\r
1644 /* [AS] [HGM] Make room for more piece types, so all pieces can be different */
\r
1646 PM_WP = (int) WhitePawn,
\r
1647 PM_WN = (int) WhiteKnight,
\r
1648 PM_WB = (int) WhiteBishop,
\r
1649 PM_WR = (int) WhiteRook,
\r
1650 PM_WQ = (int) WhiteQueen,
\r
1651 PM_WF = (int) WhiteFerz,
\r
1652 PM_WW = (int) WhiteWazir,
\r
1653 PM_WE = (int) WhiteAlfil,
\r
1654 PM_WM = (int) WhiteMan,
\r
1655 PM_WO = (int) WhiteCannon,
\r
1656 PM_WU = (int) WhiteUnicorn,
\r
1657 PM_WH = (int) WhiteNightrider,
\r
1658 PM_WA = (int) WhiteAngel,
\r
1659 PM_WC = (int) WhiteMarshall,
\r
1660 PM_WAB = (int) WhiteCardinal,
\r
1661 PM_WD = (int) WhiteDragon,
\r
1662 PM_WL = (int) WhiteLance,
\r
1663 PM_WS = (int) WhiteCobra,
\r
1664 PM_WV = (int) WhiteFalcon,
\r
1665 PM_WSG = (int) WhiteSilver,
\r
1666 PM_WG = (int) WhiteGrasshopper,
\r
1667 PM_WK = (int) WhiteKing,
\r
1668 PM_BP = (int) BlackPawn,
\r
1669 PM_BN = (int) BlackKnight,
\r
1670 PM_BB = (int) BlackBishop,
\r
1671 PM_BR = (int) BlackRook,
\r
1672 PM_BQ = (int) BlackQueen,
\r
1673 PM_BF = (int) BlackFerz,
\r
1674 PM_BW = (int) BlackWazir,
\r
1675 PM_BE = (int) BlackAlfil,
\r
1676 PM_BM = (int) BlackMan,
\r
1677 PM_BO = (int) BlackCannon,
\r
1678 PM_BU = (int) BlackUnicorn,
\r
1679 PM_BH = (int) BlackNightrider,
\r
1680 PM_BA = (int) BlackAngel,
\r
1681 PM_BC = (int) BlackMarshall,
\r
1682 PM_BG = (int) BlackGrasshopper,
\r
1683 PM_BAB = (int) BlackCardinal,
\r
1684 PM_BD = (int) BlackDragon,
\r
1685 PM_BL = (int) BlackLance,
\r
1686 PM_BS = (int) BlackCobra,
\r
1687 PM_BV = (int) BlackFalcon,
\r
1688 PM_BSG = (int) BlackSilver,
\r
1689 PM_BK = (int) BlackKing
\r
1692 static HFONT hPieceFont = NULL;
\r
1693 static HBITMAP hPieceMask[(int) EmptySquare];
\r
1694 static HBITMAP hPieceFace[(int) EmptySquare];
\r
1695 static int fontBitmapSquareSize = 0;
\r
1696 static char pieceToFontChar[(int) EmptySquare] =
\r
1697 { 'p', 'n', 'b', 'r', 'q',
\r
1698 'n', 'b', 'p', 'n', 'b', 'r', 'b', 'r', 'q', 'k',
\r
1699 'k', 'o', 'm', 'v', 't', 'w',
\r
1700 'v', 't', 'o', 'm', 'v', 't', 'v', 't', 'w', 'l',
\r
1703 extern BOOL SetCharTable( char *table, const char * map );
\r
1704 /* [HGM] moved to backend.c */
\r
1706 static void SetPieceBackground( HDC hdc, COLORREF color, int mode )
\r
1709 BYTE r1 = GetRValue( color );
\r
1710 BYTE g1 = GetGValue( color );
\r
1711 BYTE b1 = GetBValue( color );
\r
1717 /* Create a uniform background first */
\r
1718 hbrush = CreateSolidBrush( color );
\r
1719 SetRect( &rc, 0, 0, squareSize, squareSize );
\r
1720 FillRect( hdc, &rc, hbrush );
\r
1721 DeleteObject( hbrush );
\r
1724 /* Vertical gradient, good for pawn, knight and rook, less for queen and king */
\r
1725 int steps = squareSize / 2;
\r
1728 for( i=0; i<steps; i++ ) {
\r
1729 BYTE r = r1 - (r1-r2) * i / steps;
\r
1730 BYTE g = g1 - (g1-g2) * i / steps;
\r
1731 BYTE b = b1 - (b1-b2) * i / steps;
\r
1733 hbrush = CreateSolidBrush( RGB(r,g,b) );
\r
1734 SetRect( &rc, i + squareSize - steps, 0, i + squareSize - steps + 1, squareSize );
\r
1735 FillRect( hdc, &rc, hbrush );
\r
1736 DeleteObject(hbrush);
\r
1739 else if( mode == 2 ) {
\r
1740 /* Diagonal gradient, good more or less for every piece */
\r
1741 POINT triangle[3];
\r
1742 HPEN hpen = SelectObject( hdc, GetStockObject(NULL_PEN) );
\r
1743 HBRUSH hbrush_old;
\r
1744 int steps = squareSize;
\r
1747 triangle[0].x = squareSize - steps;
\r
1748 triangle[0].y = squareSize;
\r
1749 triangle[1].x = squareSize;
\r
1750 triangle[1].y = squareSize;
\r
1751 triangle[2].x = squareSize;
\r
1752 triangle[2].y = squareSize - steps;
\r
1754 for( i=0; i<steps; i++ ) {
\r
1755 BYTE r = r1 - (r1-r2) * i / steps;
\r
1756 BYTE g = g1 - (g1-g2) * i / steps;
\r
1757 BYTE b = b1 - (b1-b2) * i / steps;
\r
1759 hbrush = CreateSolidBrush( RGB(r,g,b) );
\r
1760 hbrush_old = SelectObject( hdc, hbrush );
\r
1761 Polygon( hdc, triangle, 3 );
\r
1762 SelectObject( hdc, hbrush_old );
\r
1763 DeleteObject(hbrush);
\r
1768 SelectObject( hdc, hpen );
\r
1773 [AS] The method I use to create the bitmaps it a bit tricky, but it
\r
1774 seems to work ok. The main problem here is to find the "inside" of a chess
\r
1775 piece: follow the steps as explained below.
\r
1777 static void CreatePieceMaskFromFont( HDC hdc_window, HDC hdc, int index )
\r
1781 COLORREF chroma = RGB(0xFF,0x00,0xFF);
\r
1785 int backColor = whitePieceColor;
\r
1786 int foreColor = blackPieceColor;
\r
1788 if( index < (int)BlackPawn && appData.fontBackColorWhite != appData.fontForeColorWhite ) {
\r
1789 backColor = appData.fontBackColorWhite;
\r
1790 foreColor = appData.fontForeColorWhite;
\r
1792 else if( index >= (int)BlackPawn && appData.fontBackColorBlack != appData.fontForeColorBlack ) {
\r
1793 backColor = appData.fontBackColorBlack;
\r
1794 foreColor = appData.fontForeColorBlack;
\r
1798 hbm = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );
\r
1800 hbm_old = SelectObject( hdc, hbm );
\r
1804 rc.right = squareSize;
\r
1805 rc.bottom = squareSize;
\r
1807 /* Step 1: background is now black */
\r
1808 FillRect( hdc, &rc, GetStockObject(BLACK_BRUSH) );
\r
1810 GetTextExtentPoint32( hdc, &pieceToFontChar[index], 1, &sz );
\r
1812 pt.x = (squareSize - sz.cx) / 2;
\r
1813 pt.y = (squareSize - sz.cy) / 2;
\r
1815 SetBkMode( hdc, TRANSPARENT );
\r
1816 SetTextColor( hdc, chroma );
\r
1817 /* Step 2: the piece has been drawn in purple, there are now black and purple in this bitmap */
\r
1818 TextOut( hdc, pt.x, pt.y, &pieceToFontChar[appData.allWhite && index >= (int)BlackPawn ? index - (int)BlackPawn : index], 1 );
\r
1820 SelectObject( hdc, GetStockObject(WHITE_BRUSH) );
\r
1821 /* Step 3: the area outside the piece is filled with white */
\r
1822 // FloodFill( hdc, 0, 0, chroma );
\r
1823 ExtFloodFill( hdc, 0, 0, 0, FLOODFILLSURFACE );
\r
1824 ExtFloodFill( hdc, 0, squareSize-1, 0, FLOODFILLSURFACE ); // [HGM] fill from all 4 corners, for if piece too big
\r
1825 ExtFloodFill( hdc, squareSize-1, 0, 0, FLOODFILLSURFACE );
\r
1826 ExtFloodFill( hdc, squareSize-1, squareSize-1, 0, FLOODFILLSURFACE );
\r
1827 SelectObject( hdc, GetStockObject(BLACK_BRUSH) );
\r
1829 Step 4: this is the tricky part, the area inside the piece is filled with black,
\r
1830 but if the start point is not inside the piece we're lost!
\r
1831 There should be a better way to do this... if we could create a region or path
\r
1832 from the fill operation we would be fine for example.
\r
1834 // FloodFill( hdc, squareSize / 2, squareSize / 2, RGB(0xFF,0xFF,0xFF) );
\r
1835 ExtFloodFill( hdc, squareSize / 2, squareSize / 2, RGB(0xFF,0xFF,0xFF), FLOODFILLBORDER );
\r
1837 { /* [HGM] shave off edges of mask, in an attempt to correct for the fact that FloodFill does not work correctly under Win XP */
\r
1838 HDC dc2 = CreateCompatibleDC( hdc_window );
\r
1839 HBITMAP bm2 = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );
\r
1841 SelectObject( dc2, bm2 );
\r
1842 BitBlt( dc2, 0, 0, squareSize, squareSize, hdc, 0, 0, SRCCOPY ); // make copy
\r
1843 BitBlt( hdc, 0, 1, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );
\r
1844 BitBlt( hdc, 2, 1, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );
\r
1845 BitBlt( hdc, 1, 0, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );
\r
1846 BitBlt( hdc, 1, 2, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );
\r
1849 DeleteObject( bm2 );
\r
1852 SetTextColor( hdc, 0 );
\r
1854 Step 5: some fonts have "disconnected" areas that are skipped by the fill:
\r
1855 draw the piece again in black for safety.
\r
1857 TextOut( hdc, pt.x, pt.y, &pieceToFontChar[appData.allWhite && index >= (int)BlackPawn ? index - (int)BlackPawn : index], 1 );
\r
1859 SelectObject( hdc, hbm_old );
\r
1861 if( hPieceMask[index] != NULL ) {
\r
1862 DeleteObject( hPieceMask[index] );
\r
1865 hPieceMask[index] = hbm;
\r
1868 hbm = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );
\r
1870 SelectObject( hdc, hbm );
\r
1873 HDC dc1 = CreateCompatibleDC( hdc_window );
\r
1874 HDC dc2 = CreateCompatibleDC( hdc_window );
\r
1875 HBITMAP bm2 = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );
\r
1877 SelectObject( dc1, hPieceMask[index] );
\r
1878 SelectObject( dc2, bm2 );
\r
1879 FillRect( dc2, &rc, GetStockObject(WHITE_BRUSH) );
\r
1880 BitBlt( dc2, 0, 0, squareSize, squareSize, dc1, 0, 0, SRCINVERT );
\r
1883 Now dc2 contains the inverse of the piece mask, i.e. a mask that preserves
\r
1884 the piece background and deletes (makes transparent) the rest.
\r
1885 Thanks to that mask, we are free to paint the background with the greates
\r
1886 freedom, as we'll be able to mask off the unwanted parts when finished.
\r
1887 We use this, to make gradients and give the pieces a "roundish" look.
\r
1889 SetPieceBackground( hdc, backColor, 2 );
\r
1890 BitBlt( hdc, 0, 0, squareSize, squareSize, dc2, 0, 0, SRCAND );
\r
1894 DeleteObject( bm2 );
\r
1897 SetTextColor( hdc, foreColor );
\r
1898 TextOut( hdc, pt.x, pt.y, &pieceToFontChar[appData.allWhite && index >= (int)BlackPawn ? index - (int)BlackPawn : index], 1 );
\r
1900 SelectObject( hdc, hbm_old );
\r
1902 if( hPieceFace[index] != NULL ) {
\r
1903 DeleteObject( hPieceFace[index] );
\r
1906 hPieceFace[index] = hbm;
\r
1909 static int TranslatePieceToFontPiece( int piece )
\r
1939 case BlackMarshall:
\r
1943 case BlackNightrider:
\r
1949 case BlackUnicorn:
\r
1953 case BlackGrasshopper:
\r
1965 case BlackCardinal:
\r
1972 case WhiteMarshall:
\r
1976 case WhiteNightrider:
\r
1982 case WhiteUnicorn:
\r
1986 case WhiteGrasshopper:
\r
1998 case WhiteCardinal:
\r
2007 void CreatePiecesFromFont()
\r
2010 HDC hdc_window = NULL;
\r
2016 if( fontBitmapSquareSize < 0 ) {
\r
2017 /* Something went seriously wrong in the past: do not try to recreate fonts! */
\r
2021 if( !appData.useFont || appData.renderPiecesWithFont == NULL ||
\r
2022 appData.renderPiecesWithFont[0] == NULLCHAR || appData.renderPiecesWithFont[0] == '*' ) {
\r
2023 fontBitmapSquareSize = -1;
\r
2027 if( fontBitmapSquareSize != squareSize ) {
\r
2028 hdc_window = GetDC( hwndMain );
\r
2029 hdc = CreateCompatibleDC( hdc_window );
\r
2031 if( hPieceFont != NULL ) {
\r
2032 DeleteObject( hPieceFont );
\r
2035 for( i=0; i<=(int)BlackKing; i++ ) {
\r
2036 hPieceMask[i] = NULL;
\r
2037 hPieceFace[i] = NULL;
\r
2043 if( appData.fontPieceSize >= 50 && appData.fontPieceSize <= 150 ) {
\r
2044 fontHeight = appData.fontPieceSize;
\r
2047 fontHeight = (fontHeight * squareSize) / 100;
\r
2049 lf.lfHeight = -MulDiv( fontHeight, GetDeviceCaps(hdc, LOGPIXELSY), 72 );
\r
2051 lf.lfEscapement = 0;
\r
2052 lf.lfOrientation = 0;
\r
2053 lf.lfWeight = FW_NORMAL;
\r
2055 lf.lfUnderline = 0;
\r
2056 lf.lfStrikeOut = 0;
\r
2057 lf.lfCharSet = DEFAULT_CHARSET;
\r
2058 lf.lfOutPrecision = OUT_DEFAULT_PRECIS;
\r
2059 lf.lfClipPrecision = CLIP_DEFAULT_PRECIS;
\r
2060 lf.lfQuality = PROOF_QUALITY;
\r
2061 lf.lfPitchAndFamily = DEFAULT_PITCH | FF_DONTCARE;
\r
2062 strncpy( lf.lfFaceName, appData.renderPiecesWithFont, sizeof(lf.lfFaceName) );
\r
2063 lf.lfFaceName[ sizeof(lf.lfFaceName) - 1 ] = '\0';
\r
2065 hPieceFont = CreateFontIndirect( &lf );
\r
2067 if( hPieceFont == NULL ) {
\r
2068 fontBitmapSquareSize = -2;
\r
2071 /* Setup font-to-piece character table */
\r
2072 if( ! SetCharTable(pieceToFontChar, appData.fontToPieceTable) ) {
\r
2073 /* No (or wrong) global settings, try to detect the font */
\r
2074 if( strstr(lf.lfFaceName,"Alpha") != NULL ) {
\r
2076 SetCharTable(pieceToFontChar, "phbrqkojntwl");
\r
2078 else if( strstr(lf.lfFaceName,"DiagramTT") != NULL ) {
\r
2079 /* DiagramTT* family */
\r
2080 SetCharTable(pieceToFontChar, "PNLRQKpnlrqk");
\r
2082 else if( strstr(lf.lfFaceName,"WinboardF") != NULL ) {
\r
2083 /* Fairy symbols */
\r
2084 SetCharTable(pieceToFontChar, "PNBRQFEACWMOHIJGDVSLUKpnbrqfeacwmohijgdvsluk");
\r
2086 else if( strstr(lf.lfFaceName,"GC2004D") != NULL ) {
\r
2087 /* Good Companion (Some characters get warped as literal :-( */
\r
2088 char s[] = "1cmWG0??S??oYI23wgQU";
\r
2089 s[0]=0xB9; s[1]=0xA9; s[6]=0xB1; s[11]=0xBB; s[12]=0xAB; s[17]=0xB3;
\r
2090 SetCharTable(pieceToFontChar, s);
\r
2093 /* Cases, Condal, Leipzig, Lucena, Marroquin, Merida, Usual */
\r
2094 SetCharTable(pieceToFontChar, "pnbrqkomvtwl");
\r
2098 /* Create bitmaps */
\r
2099 hfont_old = SelectObject( hdc, hPieceFont );
\r
2100 for(i=(int)WhitePawn; i<(int)EmptySquare; i++) /* [HGM] made a loop for this */
\r
2101 if(PieceToChar((ChessSquare)i) != '.') /* skip unused pieces */
\r
2102 CreatePieceMaskFromFont( hdc_window, hdc, i );
\r
2104 SelectObject( hdc, hfont_old );
\r
2106 fontBitmapSquareSize = squareSize;
\r
2110 if( hdc != NULL ) {
\r
2114 if( hdc_window != NULL ) {
\r
2115 ReleaseDC( hwndMain, hdc_window );
\r
2120 DoLoadBitmap(HINSTANCE hinst, char *piece, int squareSize, char *suffix)
\r
2122 char name[128], buf[MSG_SIZ];
\r
2124 snprintf(name, sizeof(name)/sizeof(name[0]), "%s%d%s", piece, squareSize, suffix);
\r
2125 if(appData.pieceDirectory[0]) {
\r
2127 snprintf(buf, MSG_SIZ, "%s\\%s.bmp", appData.pieceDirectory, name);
\r
2128 res = LoadImage( 0, buf, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );
\r
2129 if(res) return res;
\r
2131 if (gameInfo.event &&
\r
2132 strcmp(gameInfo.event, "Easter Egg Hunt") == 0 &&
\r
2133 strcmp(name, "k80s") == 0) {
\r
2134 safeStrCpy(name, "tim", sizeof(name)/sizeof(name[0]) );
\r
2136 return LoadBitmap(hinst, name);
\r
2140 /* Insert a color into the program's logical palette
\r
2141 structure. This code assumes the given color is
\r
2142 the result of the RGB or PALETTERGB macro, and it
\r
2143 knows how those macros work (which is documented).
\r
2146 InsertInPalette(COLORREF color)
\r
2148 LPPALETTEENTRY pe = &(pLogPal->palPalEntry[pLogPal->palNumEntries]);
\r
2150 if (pLogPal->palNumEntries++ >= PALETTESIZE) {
\r
2151 DisplayFatalError(_("Too many colors"), 0, 1);
\r
2152 pLogPal->palNumEntries--;
\r
2156 pe->peFlags = (char) 0;
\r
2157 pe->peRed = (char) (0xFF & color);
\r
2158 pe->peGreen = (char) (0xFF & (color >> 8));
\r
2159 pe->peBlue = (char) (0xFF & (color >> 16));
\r
2165 InitDrawingColors()
\r
2167 if (pLogPal == NULL) {
\r
2168 /* Allocate enough memory for a logical palette with
\r
2169 * PALETTESIZE entries and set the size and version fields
\r
2170 * of the logical palette structure.
\r
2172 pLogPal = (NPLOGPALETTE)
\r
2173 LocalAlloc(LMEM_FIXED, (sizeof(LOGPALETTE) +
\r
2174 (sizeof(PALETTEENTRY) * (PALETTESIZE))));
\r
2175 pLogPal->palVersion = 0x300;
\r
2177 pLogPal->palNumEntries = 0;
\r
2179 InsertInPalette(lightSquareColor);
\r
2180 InsertInPalette(darkSquareColor);
\r
2181 InsertInPalette(whitePieceColor);
\r
2182 InsertInPalette(blackPieceColor);
\r
2183 InsertInPalette(highlightSquareColor);
\r
2184 InsertInPalette(premoveHighlightColor);
\r
2186 /* create a logical color palette according the information
\r
2187 * in the LOGPALETTE structure.
\r
2189 hPal = CreatePalette((LPLOGPALETTE) pLogPal);
\r
2191 lightSquareBrush = CreateSolidBrush(lightSquareColor);
\r
2192 blackSquareBrush = CreateSolidBrush(blackPieceColor);
\r
2193 darkSquareBrush = CreateSolidBrush(darkSquareColor);
\r
2194 whitePieceBrush = CreateSolidBrush(whitePieceColor);
\r
2195 blackPieceBrush = CreateSolidBrush(blackPieceColor);
\r
2196 iconBkgndBrush = CreateSolidBrush(GetSysColor(COLOR_BACKGROUND));
\r
2197 explodeBrush = CreateSolidBrush(highlightSquareColor); // [HGM] atomic
\r
2198 markerBrush = CreateSolidBrush(premoveHighlightColor); // [HGM] markers
\r
2199 /* [AS] Force rendering of the font-based pieces */
\r
2200 if( fontBitmapSquareSize > 0 ) {
\r
2201 fontBitmapSquareSize = 0;
\r
2207 BoardWidth(int boardSize, int n)
\r
2208 { /* [HGM] argument n added to allow different width and height */
\r
2209 int lineGap = sizeInfo[boardSize].lineGap;
\r
2211 if( appData.overrideLineGap >= 0 && appData.overrideLineGap <= 5 ) {
\r
2212 lineGap = appData.overrideLineGap;
\r
2215 return (n + 1) * lineGap +
\r
2216 n * sizeInfo[boardSize].squareSize;
\r
2219 /* Respond to board resize by dragging edge */
\r
2221 ResizeBoard(int newSizeX, int newSizeY, int flags)
\r
2223 BoardSize newSize = NUM_SIZES - 1;
\r
2224 static int recurse = 0;
\r
2225 if (IsIconic(hwndMain)) return;
\r
2226 if (recurse > 0) return;
\r
2228 while (newSize > 0) {
\r
2229 InitDrawingSizes(newSize+1000, 0); // [HGM] kludge to update sizeInfo without visible effects
\r
2230 if(newSizeX >= sizeInfo[newSize].cliWidth &&
\r
2231 newSizeY >= sizeInfo[newSize].cliHeight) break;
\r
2234 boardSize = newSize;
\r
2235 InitDrawingSizes(boardSize, flags);
\r
2240 extern Boolean twoBoards, partnerUp; // [HGM] dual
\r
2243 InitDrawingSizes(BoardSize boardSize, int flags)
\r
2245 int i, boardWidth, boardHeight; /* [HGM] height treated separately */
\r
2246 ChessSquare piece;
\r
2247 static int oldBoardSize = -1, oldTinyLayout = 0;
\r
2249 SIZE clockSize, messageSize;
\r
2251 char buf[MSG_SIZ];
\r
2253 HMENU hmenu = GetMenu(hwndMain);
\r
2254 RECT crect, wrect, oldRect;
\r
2256 LOGBRUSH logbrush;
\r
2257 VariantClass v = gameInfo.variant;
\r
2259 int suppressVisibleEffects = 0; // [HGM] kludge to request updating sizeInfo only
\r
2260 if((int)boardSize >= 1000 ) { boardSize -= 1000; suppressVisibleEffects = 1; }
\r
2262 /* [HGM] call with -2 uses old size (for if nr of files, ranks changes) */
\r
2263 if(boardSize == (BoardSize)(-2) ) boardSize = oldBoardSize;
\r
2264 if(boardSize == -1) return; // no size defined yet; abort (to allow early call of InitPosition)
\r
2265 oldBoardSize = boardSize;
\r
2267 if(boardSize != SizeMiddling && boardSize != SizePetite && boardSize != SizeBulky && !appData.useFont)
\r
2268 { // correct board size to one where built-in pieces exist
\r
2269 if((v == VariantCapablanca || v == VariantGothic || v == VariantGrand || v == VariantCapaRandom || v == VariantJanus || v == VariantSuper)
\r
2270 && (boardSize < SizePetite || boardSize > SizeBulky) // Archbishop and Chancellor available in entire middle range
\r
2271 || (v == VariantShogi && boardSize != SizeModerate) // Japanese-style Shogi
\r
2272 || v == VariantKnightmate || v == VariantSChess || v == VariantXiangqi || v == VariantSpartan
\r
2273 || v == VariantShatranj || v == VariantMakruk || v == VariantGreat || v == VariantFairy ) {
\r
2274 if(boardSize < SizeMediocre) boardSize = SizePetite; else
\r
2275 if(boardSize > SizeModerate) boardSize = SizeBulky; else
\r
2276 boardSize = SizeMiddling;
\r
2279 if(!appData.useFont && boardSize == SizePetite && (v == VariantKnightmate)) boardSize = SizeMiddling; // no Unicorn in Petite
\r
2281 oldRect.left = wpMain.x; //[HGM] placement: remember previous window params
\r
2282 oldRect.top = wpMain.y;
\r
2283 oldRect.right = wpMain.x + wpMain.width;
\r
2284 oldRect.bottom = wpMain.y + wpMain.height;
\r
2286 tinyLayout = sizeInfo[boardSize].tinyLayout;
\r
2287 smallLayout = sizeInfo[boardSize].smallLayout;
\r
2288 squareSize = sizeInfo[boardSize].squareSize;
\r
2289 lineGap = sizeInfo[boardSize].lineGap;
\r
2290 minorSize = 0; /* [HGM] Kludge to see if demagnified pieces need to be shifted */
\r
2291 border = appData.useBorder && appData.border[0] ? squareSize/2 : 0;
\r
2293 if( appData.overrideLineGap >= 0 && appData.overrideLineGap <= 5 ) {
\r
2294 lineGap = appData.overrideLineGap;
\r
2297 if (tinyLayout != oldTinyLayout) {
\r
2298 long style = GetWindowLongPtr(hwndMain, GWL_STYLE);
\r
2300 style &= ~WS_SYSMENU;
\r
2301 InsertMenu(hmenu, IDM_Exit, MF_BYCOMMAND, IDM_Minimize,
\r
2302 "&Minimize\tCtrl+F4");
\r
2304 style |= WS_SYSMENU;
\r
2305 RemoveMenu(hmenu, IDM_Minimize, MF_BYCOMMAND);
\r
2307 SetWindowLongPtr(hwndMain, GWL_STYLE, style);
\r
2309 for (i=0; menuBarText[tinyLayout][i]; i++) {
\r
2310 ModifyMenu(hmenu, i, MF_STRING|MF_BYPOSITION|MF_POPUP,
\r
2311 (UINT)GetSubMenu(hmenu, i), T_(menuBarText[tinyLayout][i]));
\r
2313 DrawMenuBar(hwndMain);
\r
2316 boardWidth = BoardWidth(boardSize, BOARD_WIDTH) + 2*border;
\r
2317 boardHeight = BoardWidth(boardSize, BOARD_HEIGHT) + 2*border;
\r
2319 /* Get text area sizes */
\r
2320 hdc = GetDC(hwndMain);
\r
2321 if (appData.clockMode) {
\r
2322 snprintf(buf, MSG_SIZ, _("White: %s"), TimeString(23*60*60*1000L));
\r
2324 snprintf(buf, MSG_SIZ, _("White"));
\r
2326 oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);
\r
2327 GetTextExtentPoint(hdc, buf, strlen(buf), &clockSize);
\r
2328 SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);
\r
2329 str = _("We only care about the height here");
\r
2330 GetTextExtentPoint(hdc, str, strlen(str), &messageSize);
\r
2331 SelectObject(hdc, oldFont);
\r
2332 ReleaseDC(hwndMain, hdc);
\r
2334 /* Compute where everything goes */
\r
2335 if((first.programLogo || second.programLogo) && !tinyLayout) {
\r
2336 /* [HGM] logo: if either logo is on, reserve space for it */
\r
2337 logoHeight = 2*clockSize.cy;
\r
2338 leftLogoRect.left = OUTER_MARGIN;
\r
2339 leftLogoRect.right = leftLogoRect.left + 4*clockSize.cy;
\r
2340 leftLogoRect.top = OUTER_MARGIN;
\r
2341 leftLogoRect.bottom = OUTER_MARGIN + logoHeight;
\r
2343 rightLogoRect.right = OUTER_MARGIN + boardWidth;
\r
2344 rightLogoRect.left = rightLogoRect.right - 4*clockSize.cy;
\r
2345 rightLogoRect.top = OUTER_MARGIN;
\r
2346 rightLogoRect.bottom = OUTER_MARGIN + logoHeight;
\r
2349 whiteRect.left = leftLogoRect.right;
\r
2350 whiteRect.right = OUTER_MARGIN + boardWidth/2 - INNER_MARGIN/2;
\r
2351 whiteRect.top = OUTER_MARGIN;
\r
2352 whiteRect.bottom = whiteRect.top + logoHeight;
\r
2354 blackRect.right = rightLogoRect.left;
\r
2355 blackRect.left = whiteRect.right + INNER_MARGIN;
\r
2356 blackRect.top = whiteRect.top;
\r
2357 blackRect.bottom = whiteRect.bottom;
\r
2359 whiteRect.left = OUTER_MARGIN;
\r
2360 whiteRect.right = whiteRect.left + boardWidth/2 - INNER_MARGIN/2;
\r
2361 whiteRect.top = OUTER_MARGIN;
\r
2362 whiteRect.bottom = whiteRect.top + clockSize.cy;
\r
2364 blackRect.left = whiteRect.right + INNER_MARGIN;
\r
2365 blackRect.right = blackRect.left + boardWidth/2 - 1;
\r
2366 blackRect.top = whiteRect.top;
\r
2367 blackRect.bottom = whiteRect.bottom;
\r
2369 logoHeight = 0; // [HGM] logo: suppress logo after change to tiny layout!
\r
2372 messageRect.left = OUTER_MARGIN + MESSAGE_LINE_LEFTMARGIN;
\r
2373 if (appData.showButtonBar) {
\r
2374 messageRect.right = OUTER_MARGIN + boardWidth // [HGM] logo: expressed independent of clock placement
\r
2375 - N_BUTTONS*BUTTON_WIDTH - MESSAGE_LINE_LEFTMARGIN;
\r
2377 messageRect.right = OUTER_MARGIN + boardWidth;
\r
2379 messageRect.top = whiteRect.bottom + INNER_MARGIN;
\r
2380 messageRect.bottom = messageRect.top + messageSize.cy;
\r
2382 boardRect.left = OUTER_MARGIN;
\r
2383 boardRect.right = boardRect.left + boardWidth;
\r
2384 boardRect.top = messageRect.bottom + INNER_MARGIN;
\r
2385 boardRect.bottom = boardRect.top + boardHeight;
\r
2387 sizeInfo[boardSize].cliWidth = boardRect.right + OUTER_MARGIN;
\r
2388 sizeInfo[boardSize].cliHeight = boardRect.bottom + OUTER_MARGIN;
\r
2389 oldTinyLayout = tinyLayout;
\r
2390 winW = 2 * GetSystemMetrics(SM_CXFRAME) + boardRect.right + OUTER_MARGIN;
\r
2391 winH = 2 * GetSystemMetrics(SM_CYFRAME) + GetSystemMetrics(SM_CYMENU) +
\r
2392 GetSystemMetrics(SM_CYCAPTION) + boardRect.bottom + OUTER_MARGIN;
\r
2393 winW *= 1 + twoBoards;
\r
2394 if(suppressVisibleEffects) return; // [HGM] when called for filling sizeInfo only
\r
2395 wpMain.width = winW; // [HGM] placement: set through temporary which can used by initial sizing choice
\r
2396 wpMain.height = winH; // without disturbing window attachments
\r
2397 GetWindowRect(hwndMain, &wrect);
\r
2398 SetWindowPos(hwndMain, NULL, 0, 0, wpMain.width, wpMain.height,
\r
2399 SWP_NOCOPYBITS|SWP_NOZORDER|SWP_NOMOVE);
\r
2401 // [HGM] placement: let attached windows follow size change.
\r
2402 ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, moveHistoryDialog, &wpMoveHistory );
\r
2403 ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, evalGraphDialog, &wpEvalGraph );
\r
2404 ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, engineOutputDialog, &wpEngineOutput );
\r
2405 ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, gameListDialog, &wpGameList );
\r
2406 ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, hwndConsole, &wpConsole );
\r
2408 /* compensate if menu bar wrapped */
\r
2409 GetClientRect(hwndMain, &crect);
\r
2410 offby = boardRect.bottom + OUTER_MARGIN - crect.bottom;
\r
2411 wpMain.height += offby;
\r
2413 case WMSZ_TOPLEFT:
\r
2414 SetWindowPos(hwndMain, NULL,
\r
2415 wrect.right - wpMain.width, wrect.bottom - wpMain.height,
\r
2416 wpMain.width, wpMain.height, SWP_NOCOPYBITS|SWP_NOZORDER);
\r
2419 case WMSZ_TOPRIGHT:
\r
2421 SetWindowPos(hwndMain, NULL,
\r
2422 wrect.left, wrect.bottom - wpMain.height,
\r
2423 wpMain.width, wpMain.height, SWP_NOCOPYBITS|SWP_NOZORDER);
\r
2426 case WMSZ_BOTTOMLEFT:
\r
2428 SetWindowPos(hwndMain, NULL,
\r
2429 wrect.right - wpMain.width, wrect.top,
\r
2430 wpMain.width, wpMain.height, SWP_NOCOPYBITS|SWP_NOZORDER);
\r
2433 case WMSZ_BOTTOMRIGHT:
\r
2437 SetWindowPos(hwndMain, NULL, 0, 0, wpMain.width, wpMain.height,
\r
2438 SWP_NOCOPYBITS|SWP_NOZORDER|SWP_NOMOVE);
\r
2443 for (i = 0; i < N_BUTTONS; i++) {
\r
2444 if (buttonDesc[i].hwnd != NULL) {
\r
2445 DestroyWindow(buttonDesc[i].hwnd);
\r
2446 buttonDesc[i].hwnd = NULL;
\r
2448 if (appData.showButtonBar) {
\r
2449 buttonDesc[i].hwnd =
\r
2450 CreateWindow("BUTTON", buttonDesc[i].label,
\r
2451 WS_VISIBLE | WS_CHILD | BS_PUSHBUTTON,
\r
2452 boardRect.right - BUTTON_WIDTH*(N_BUTTONS-i),
\r
2453 messageRect.top, BUTTON_WIDTH, messageSize.cy, hwndMain,
\r
2454 (HMENU) buttonDesc[i].id,
\r
2455 (HINSTANCE) GetWindowLongPtr(hwndMain, GWLP_HINSTANCE), NULL);
\r
2457 SendMessage(buttonDesc[i].hwnd, WM_SETFONT,
\r
2458 (WPARAM)font[boardSize][MESSAGE_FONT]->hf,
\r
2459 MAKELPARAM(FALSE, 0));
\r
2461 if (buttonDesc[i].id == IDM_Pause)
\r
2462 hwndPause = buttonDesc[i].hwnd;
\r
2463 buttonDesc[i].wndproc = (WNDPROC)
\r
2464 SetWindowLongPtr(buttonDesc[i].hwnd, GWLP_WNDPROC, (LONG_PTR) ButtonProc);
\r
2467 if (gridPen != NULL) DeleteObject(gridPen);
\r
2468 if (highlightPen != NULL) DeleteObject(highlightPen);
\r
2469 if (premovePen != NULL) DeleteObject(premovePen);
\r
2470 if (lineGap != 0) {
\r
2471 logbrush.lbStyle = BS_SOLID;
\r
2472 logbrush.lbColor = RGB(0, 0, 0); /* grid pen color = black */
\r
2474 ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,
\r
2475 lineGap, &logbrush, 0, NULL);
\r
2476 logbrush.lbColor = highlightSquareColor;
\r
2478 ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,
\r
2479 lineGap, &logbrush, 0, NULL);
\r
2481 logbrush.lbColor = premoveHighlightColor;
\r
2483 ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,
\r
2484 lineGap, &logbrush, 0, NULL);
\r
2486 /* [HGM] Loop had to be split in part for vert. and hor. lines */
\r
2487 for (i = 0; i < BOARD_HEIGHT + 1; i++) {
\r
2488 gridEndpoints[i*2].x = boardRect.left + lineGap / 2 + border;
\r
2489 gridEndpoints[i*2].y = gridEndpoints[i*2 + 1].y =
\r
2490 boardRect.top + lineGap / 2 + (i * (squareSize + lineGap)) + border;
\r
2491 gridEndpoints[i*2 + 1].x = boardRect.left + lineGap / 2 +
\r
2492 BOARD_WIDTH * (squareSize + lineGap) + border;
\r
2493 gridVertexCounts[i*2] = gridVertexCounts[i*2 + 1] = 2;
\r
2495 for (i = 0; i < BOARD_WIDTH + 1; i++) {
\r
2496 gridEndpoints[i*2 + BOARD_HEIGHT*2 + 2].y = boardRect.top + lineGap / 2 + border;
\r
2497 gridEndpoints[i*2 + BOARD_HEIGHT*2 + 2].x =
\r
2498 gridEndpoints[i*2 + 1 + BOARD_HEIGHT*2 + 2].x = boardRect.left +
\r
2499 lineGap / 2 + (i * (squareSize + lineGap)) + border;
\r
2500 gridEndpoints[i*2 + 1 + BOARD_HEIGHT*2 + 2].y =
\r
2501 boardRect.top + BOARD_HEIGHT * (squareSize + lineGap) + border;
\r
2502 gridVertexCounts[i*2] = gridVertexCounts[i*2 + 1] = 2;
\r
2506 /* [HGM] Licensing requirement */
\r
2508 if(gameInfo.variant == VariantGothic) GothicPopUp( GOTHIC, VariantGothic); else
\r
2511 if(gameInfo.variant == VariantFalcon) GothicPopUp( FALCON, VariantFalcon); else
\r
2513 GothicPopUp( "", VariantNormal);
\r
2516 /* if (boardSize == oldBoardSize) return; [HGM] variant might have changed */
\r
2518 /* Load piece bitmaps for this board size */
\r
2519 for (i=0; i<=2; i++) {
\r
2520 for (piece = WhitePawn;
\r
2521 (int) piece < (int) BlackPawn;
\r
2522 piece = (ChessSquare) ((int) piece + 1)) {
\r
2523 if (pieceBitmap[i][piece] != NULL)
\r
2524 DeleteObject(pieceBitmap[i][piece]);
\r
2528 fontBitmapSquareSize = 0; /* [HGM] render: make sure pieces will be recreated, as we might need others now */
\r
2529 // Orthodox Chess pieces
\r
2530 pieceBitmap[0][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "s");
\r
2531 pieceBitmap[0][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "s");
\r
2532 pieceBitmap[0][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "s");
\r
2533 pieceBitmap[0][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "s");
\r
2534 pieceBitmap[0][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "s");
\r
2535 pieceBitmap[1][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "o");
\r
2536 pieceBitmap[1][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "o");
\r
2537 pieceBitmap[1][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "o");
\r
2538 pieceBitmap[1][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "o");
\r
2539 pieceBitmap[1][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "o");
\r
2540 pieceBitmap[2][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "w");
\r
2541 pieceBitmap[2][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "w");
\r
2542 pieceBitmap[2][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "w");
\r
2543 pieceBitmap[2][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "w");
\r
2544 pieceBitmap[2][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "w");
\r
2545 if( gameInfo.variant == VariantShogi && squareSize <= 72 && squareSize >= 33) {
\r
2546 // in Shogi, Hijack the unused Queen for Lance
\r
2547 pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "s");
\r
2548 pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "o");
\r
2549 pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "w");
\r
2551 pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "s");
\r
2552 pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "o");
\r
2553 pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "w");
\r
2556 if(squareSize <= 72 && squareSize >= 33) {
\r
2557 /* A & C are available in most sizes now */
\r
2558 if(squareSize != 49 && squareSize != 72 && squareSize != 33) { // Vortex-like
\r
2559 pieceBitmap[0][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "s");
\r
2560 pieceBitmap[1][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "o");
\r
2561 pieceBitmap[2][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "w");
\r
2562 pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "s");
\r
2563 pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "o");
\r
2564 pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "w");
\r
2565 pieceBitmap[0][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "s");
\r
2566 pieceBitmap[1][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "o");
\r
2567 pieceBitmap[2][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "w");
\r
2568 pieceBitmap[0][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "s");
\r
2569 pieceBitmap[1][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "o");
\r
2570 pieceBitmap[2][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "w");
\r
2571 } else { // Smirf-like
\r
2572 if(gameInfo.variant == VariantSChess) {
\r
2573 pieceBitmap[0][WhiteAngel] = DoLoadBitmap(hInst, "v", squareSize, "s");
\r
2574 pieceBitmap[1][WhiteAngel] = DoLoadBitmap(hInst, "v", squareSize, "o");
\r
2575 pieceBitmap[2][WhiteAngel] = DoLoadBitmap(hInst, "v", squareSize, "w");
\r
2577 pieceBitmap[0][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "s");
\r
2578 pieceBitmap[1][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "o");
\r
2579 pieceBitmap[2][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "w");
\r
2582 if(gameInfo.variant == VariantGothic) { // Vortex-like
\r
2583 pieceBitmap[0][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "s");
\r
2584 pieceBitmap[1][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "o");
\r
2585 pieceBitmap[2][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "w");
\r
2586 } else if(gameInfo.variant == VariantSChess && (squareSize == 49 || squareSize == 72)) {
\r
2587 pieceBitmap[0][WhiteMarshall] = DoLoadBitmap(hInst, "e", squareSize, "s");
\r
2588 pieceBitmap[1][WhiteMarshall] = DoLoadBitmap(hInst, "e", squareSize, "o");
\r
2589 pieceBitmap[2][WhiteMarshall] = DoLoadBitmap(hInst, "e", squareSize, "w");
\r
2590 } else { // WinBoard standard
\r
2591 pieceBitmap[0][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "s");
\r
2592 pieceBitmap[1][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "o");
\r
2593 pieceBitmap[2][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "w");
\r
2598 if(squareSize==72 || squareSize==49 || squareSize==33) { /* experiment with some home-made bitmaps */
\r
2599 pieceBitmap[0][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "s");
\r
2600 pieceBitmap[1][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "o");
\r
2601 pieceBitmap[2][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "w");
\r
2602 pieceBitmap[0][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "s");
\r
2603 pieceBitmap[1][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "o");
\r
2604 pieceBitmap[2][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "w");
\r
2605 pieceBitmap[0][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "s");
\r
2606 pieceBitmap[1][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "o");
\r
2607 pieceBitmap[2][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "w");
\r
2608 pieceBitmap[0][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "s");
\r
2609 pieceBitmap[1][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "o");
\r
2610 pieceBitmap[2][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "w");
\r
2611 pieceBitmap[0][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "s");
\r
2612 pieceBitmap[1][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "o");
\r
2613 pieceBitmap[2][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "w");
\r
2614 pieceBitmap[0][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "s");
\r
2615 pieceBitmap[1][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "o");
\r
2616 pieceBitmap[2][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "w");
\r
2617 pieceBitmap[0][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "s");
\r
2618 pieceBitmap[1][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "o");
\r
2619 pieceBitmap[2][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "w");
\r
2620 pieceBitmap[0][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "s");
\r
2621 pieceBitmap[1][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "o");
\r
2622 pieceBitmap[2][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "w");
\r
2623 pieceBitmap[0][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "s");
\r
2624 pieceBitmap[1][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "o");
\r
2625 pieceBitmap[2][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "w");
\r
2626 pieceBitmap[0][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "s");
\r
2627 pieceBitmap[1][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "o");
\r
2628 pieceBitmap[2][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "w");
\r
2630 if(gameInfo.variant == VariantShogi) { /* promoted Gold represemtations */
\r
2631 pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "wp", squareSize, "s");
\r
2632 pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "wp", squareSize, "o");
\r
2633 pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "w", squareSize, "w");
\r
2634 pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "wn", squareSize, "s");
\r
2635 pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "wn", squareSize, "o");
\r
2636 pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "w", squareSize, "w");
\r
2637 pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "ws", squareSize, "s");
\r
2638 pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "ws", squareSize, "o");
\r
2639 pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "w", squareSize, "w");
\r
2640 pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "wl", squareSize, "s");
\r
2641 pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "wl", squareSize, "o");
\r
2642 pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "w", squareSize, "w");
\r
2644 pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "s");
\r
2645 pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "o");
\r
2646 pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "w");
\r
2647 pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "s");
\r
2648 pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "o");
\r
2649 pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "w");
\r
2650 pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "s");
\r
2651 pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "o");
\r
2652 pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "w");
\r
2653 pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "s");
\r
2654 pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "o");
\r
2655 pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "w");
\r
2658 } else { /* other size, no special bitmaps available. Use smaller symbols */
\r
2659 if((int)boardSize < 2) minorSize = sizeInfo[0].squareSize;
\r
2660 else minorSize = sizeInfo[(int)boardSize - 2].squareSize;
\r
2661 pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "s");
\r
2662 pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "o");
\r
2663 pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "w");
\r
2664 pieceBitmap[0][WhiteCardinal] = DoLoadBitmap(hInst, "b", minorSize, "s");
\r
2665 pieceBitmap[1][WhiteCardinal] = DoLoadBitmap(hInst, "b", minorSize, "o");
\r
2666 pieceBitmap[2][WhiteCardinal] = DoLoadBitmap(hInst, "b", minorSize, "w");
\r
2667 pieceBitmap[0][WhiteDragon] = DoLoadBitmap(hInst, "r", minorSize, "s");
\r
2668 pieceBitmap[1][WhiteDragon] = DoLoadBitmap(hInst, "r", minorSize, "o");
\r
2669 pieceBitmap[2][WhiteDragon] = DoLoadBitmap(hInst, "r", minorSize, "w");
\r
2670 pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "s");
\r
2671 pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "o");
\r
2672 pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "w");
\r
2676 if(gameInfo.variant == VariantShogi && squareSize == 58)
\r
2677 /* special Shogi support in this size */
\r
2678 { for (i=0; i<=2; i++) { /* replace all bitmaps */
\r
2679 for (piece = WhitePawn;
\r
2680 (int) piece < (int) BlackPawn;
\r
2681 piece = (ChessSquare) ((int) piece + 1)) {
\r
2682 if (pieceBitmap[i][piece] != NULL)
\r
2683 DeleteObject(pieceBitmap[i][piece]);
\r
2686 pieceBitmap[0][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "o");
\r
2687 pieceBitmap[0][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "o");
\r
2688 pieceBitmap[0][WhiteBishop] = DoLoadBitmap(hInst, "sb", squareSize, "o");
\r
2689 pieceBitmap[0][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "o");
\r
2690 pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "o");
\r
2691 pieceBitmap[0][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "o");
\r
2692 pieceBitmap[0][WhiteFerz] = DoLoadBitmap(hInst, "sf", squareSize, "o");
\r
2693 pieceBitmap[0][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "o");
\r
2694 pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "su", squareSize, "o");
\r
2695 pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "sh", squareSize, "o");
\r
2696 pieceBitmap[0][WhiteCardinal] = DoLoadBitmap(hInst, "sa", squareSize, "o");
\r
2697 pieceBitmap[0][WhiteDragon] = DoLoadBitmap(hInst, "sc", squareSize, "o");
\r
2698 pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "sg", squareSize, "o");
\r
2699 pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "ss", squareSize, "o");
\r
2700 pieceBitmap[1][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "o");
\r
2701 pieceBitmap[1][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "o");
\r
2702 pieceBitmap[1][WhiteBishop] = DoLoadBitmap(hInst, "sb", squareSize, "o");
\r
2703 pieceBitmap[1][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "o");
\r
2704 pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "o");
\r
2705 pieceBitmap[1][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "o");
\r
2706 pieceBitmap[1][WhiteFerz] = DoLoadBitmap(hInst, "sf", squareSize, "o");
\r
2707 pieceBitmap[1][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "o");
\r
2708 pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "su", squareSize, "o");
\r
2709 pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "sh", squareSize, "o");
\r
2710 pieceBitmap[1][WhiteCardinal] = DoLoadBitmap(hInst, "sa", squareSize, "o");
\r
2711 pieceBitmap[1][WhiteDragon] = DoLoadBitmap(hInst, "sc", squareSize, "o");
\r
2712 pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "sg", squareSize, "o");
\r
2713 pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "ss", squareSize, "o");
\r
2714 pieceBitmap[2][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "w");
\r
2715 pieceBitmap[2][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "w");
\r
2716 pieceBitmap[2][WhiteBishop] = DoLoadBitmap(hInst, "sr", squareSize, "w");
\r
2717 pieceBitmap[2][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "w");
\r
2718 pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "w");
\r
2719 pieceBitmap[2][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "w");
\r
2720 pieceBitmap[2][WhiteFerz] = DoLoadBitmap(hInst, "sw", squareSize, "w");
\r
2721 pieceBitmap[2][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "w");
\r
2722 pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "sp", squareSize, "w");
\r
2723 pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "sn", squareSize, "w");
\r
2724 pieceBitmap[2][WhiteCardinal] = DoLoadBitmap(hInst, "sr", squareSize, "w");
\r
2725 pieceBitmap[2][WhiteDragon] = DoLoadBitmap(hInst, "sr", squareSize, "w");
\r
2726 pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "sl", squareSize, "w");
\r
2727 pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "sw", squareSize, "w");
\r
2733 PieceBitmap(ChessSquare p, int kind)
\r
2735 if ((int) p >= (int) BlackPawn)
\r
2736 p = (ChessSquare) ((int) p - (int) BlackPawn + (int) WhitePawn);
\r
2738 return pieceBitmap[kind][(int) p];
\r
2741 /***************************************************************/
\r
2743 #define MIN(a,b) (((a) < (b)) ? (a) : (b))
\r
2744 #define MAX(a,b) (((a) > (b)) ? (a) : (b))
\r
2746 #define MIN3(a,b,c) (((a) < (b) && (a) < (c)) ? (a) : (((b) < (a) && (b) < (c)) ? (b) : (c)))
\r
2747 #define MAX3(a,b,c) (((a) > (b) && (a) > (c)) ? (a) : (((b) > (a) && (b) > (c)) ? (b) : (c)))
\r
2751 SquareToPos(int row, int column, int * x, int * y)
\r
2754 *x = boardRect.left + lineGap + ((BOARD_WIDTH-1)-column) * (squareSize + lineGap) + border;
\r
2755 *y = boardRect.top + lineGap + row * (squareSize + lineGap) + border;
\r
2757 *x = boardRect.left + lineGap + column * (squareSize + lineGap) + border;
\r
2758 *y = boardRect.top + lineGap + ((BOARD_HEIGHT-1)-row) * (squareSize + lineGap) + border;
\r
2763 DrawCoordsOnDC(HDC hdc)
\r
2765 static char files[] = "0123456789012345678901221098765432109876543210";
\r
2766 static char ranks[] = "wvutsrqponmlkjihgfedcbaabcdefghijklmnopqrstuvw";
\r
2767 char str[2] = { NULLCHAR, NULLCHAR };
\r
2768 int oldMode, oldAlign, x, y, start, i;
\r
2772 if (!appData.showCoords)
\r
2775 start = flipView ? 1-(ONE!='1') : 45+(ONE!='1')-BOARD_HEIGHT;
\r
2777 oldBrush = SelectObject(hdc, GetStockObject(BLACK_BRUSH));
\r
2778 oldMode = SetBkMode(hdc, (appData.monoMode ? OPAQUE : TRANSPARENT));
\r
2779 oldAlign = GetTextAlign(hdc);
\r
2780 oldFont = SelectObject(hdc, font[boardSize][COORD_FONT]->hf);
\r
2782 y = boardRect.top + lineGap;
\r
2783 x = boardRect.left + lineGap + gameInfo.holdingsWidth*(squareSize + lineGap);
\r
2786 SetTextAlign(hdc, TA_RIGHT|TA_TOP);
\r
2787 x += border - lineGap - 4; y += squareSize - 6;
\r
2789 SetTextAlign(hdc, TA_LEFT|TA_TOP);
\r
2790 for (i = 0; i < BOARD_HEIGHT; i++) {
\r
2791 str[0] = files[start + i];
\r
2792 ExtTextOut(hdc, x + 2 - (border ? gameInfo.holdingsWidth * (squareSize + lineGap) : 0), y + 1, 0, NULL, str, 1, NULL);
\r
2793 y += squareSize + lineGap;
\r
2796 start = flipView ? 23-(BOARD_RGHT-BOARD_LEFT) : 23;
\r
2799 SetTextAlign(hdc, TA_LEFT|TA_TOP);
\r
2800 x += -border + 4; y += border - squareSize + 6;
\r
2802 SetTextAlign(hdc, TA_RIGHT|TA_BOTTOM);
\r
2803 for (i = 0; i < BOARD_RGHT - BOARD_LEFT; i++) {
\r
2804 str[0] = ranks[start + i];
\r
2805 ExtTextOut(hdc, x + squareSize - 2, y - 1, 0, NULL, str, 1, NULL);
\r
2806 x += squareSize + lineGap;
\r
2809 SelectObject(hdc, oldBrush);
\r
2810 SetBkMode(hdc, oldMode);
\r
2811 SetTextAlign(hdc, oldAlign);
\r
2812 SelectObject(hdc, oldFont);
\r
2816 DrawGridOnDC(HDC hdc)
\r
2820 if (lineGap != 0) {
\r
2821 oldPen = SelectObject(hdc, gridPen);
\r
2822 PolyPolyline(hdc, gridEndpoints, gridVertexCounts, BOARD_WIDTH+BOARD_HEIGHT + 2);
\r
2823 SelectObject(hdc, oldPen);
\r
2827 #define HIGHLIGHT_PEN 0
\r
2828 #define PREMOVE_PEN 1
\r
2831 DrawHighlightOnDC(HDC hdc, BOOLEAN on, int x, int y, int pen)
\r
2834 HPEN oldPen, hPen;
\r
2835 if (lineGap == 0) return;
\r
2837 x1 = boardRect.left +
\r
2838 lineGap/2 + ((BOARD_WIDTH-1)-x) * (squareSize + lineGap) + border;
\r
2839 y1 = boardRect.top +
\r
2840 lineGap/2 + y * (squareSize + lineGap) + border;
\r
2842 x1 = boardRect.left +
\r
2843 lineGap/2 + x * (squareSize + lineGap) + border;
\r
2844 y1 = boardRect.top +
\r
2845 lineGap/2 + ((BOARD_HEIGHT-1)-y) * (squareSize + lineGap) + border;
\r
2847 hPen = pen ? premovePen : highlightPen;
\r
2848 oldPen = SelectObject(hdc, on ? hPen : gridPen);
\r
2849 MoveToEx(hdc, x1, y1, NULL);
\r
2850 LineTo(hdc, x1 + squareSize + lineGap, y1);
\r
2851 LineTo(hdc, x1 + squareSize + lineGap, y1 + squareSize + lineGap);
\r
2852 LineTo(hdc, x1, y1 + squareSize + lineGap);
\r
2853 LineTo(hdc, x1, y1);
\r
2854 SelectObject(hdc, oldPen);
\r
2858 DrawHighlightsOnDC(HDC hdc, HighlightInfo *h, int pen)
\r
2861 for (i=0; i<2; i++) {
\r
2862 if (h->sq[i].x >= 0 && h->sq[i].y >= 0)
\r
2863 DrawHighlightOnDC(hdc, TRUE,
\r
2864 h->sq[i].x, h->sq[i].y,
\r
2869 /* Note: sqcolor is used only in monoMode */
\r
2870 /* Note that this code is largely duplicated in woptions.c,
\r
2871 function DrawSampleSquare, so that needs to be updated too */
\r
2873 DrawPieceOnDC(HDC hdc, ChessSquare piece, int color, int sqcolor, int x, int y, HDC tmphdc)
\r
2875 HBITMAP oldBitmap;
\r
2879 if (appData.blindfold) return;
\r
2881 /* [AS] Use font-based pieces if needed */
\r
2882 if( fontBitmapSquareSize >= 0 && (squareSize > 32 || gameInfo.variant >= VariantShogi)) {
\r
2883 /* Create piece bitmaps, or do nothing if piece set is up to date */
\r
2884 CreatePiecesFromFont();
\r
2886 if( fontBitmapSquareSize == squareSize ) {
\r
2887 int index = TranslatePieceToFontPiece(piece);
\r
2889 SelectObject( tmphdc, hPieceMask[ index ] );
\r
2891 if(appData.upsideDown ? color==flipView : (flipView && gameInfo.variant == VariantShogi))
\r
2892 StretchBlt(hdc, x+squareSize, y+squareSize, -squareSize, -squareSize, tmphdc, 0, 0, squareSize, squareSize, SRCAND);
\r
2896 squareSize, squareSize,
\r
2901 SelectObject( tmphdc, hPieceFace[ index ] );
\r
2903 if(appData.upsideDown ? color==flipView : (flipView && gameInfo.variant == VariantShogi))
\r
2904 StretchBlt(hdc, x+squareSize, y+squareSize, -squareSize, -squareSize, tmphdc, 0, 0, squareSize, squareSize, SRCPAINT);
\r
2908 squareSize, squareSize,
\r
2917 if (appData.monoMode) {
\r
2918 SelectObject(tmphdc, PieceBitmap(piece,
\r
2919 color == sqcolor ? OUTLINE_PIECE : SOLID_PIECE));
\r
2920 BitBlt(hdc, x, y, squareSize, squareSize, tmphdc, 0, 0,
\r
2921 sqcolor ? SRCCOPY : NOTSRCCOPY);
\r
2923 HBRUSH xBrush = whitePieceBrush;
\r
2924 tmpSize = squareSize;
\r
2925 if(appData.pieceDirectory[0]) xBrush = GetStockObject(WHITE_BRUSH);
\r
2927 ((piece >= (int)WhiteNightrider && piece <= WhiteGrasshopper) ||
\r
2928 (piece >= (int)BlackNightrider && piece <= BlackGrasshopper)) ) {
\r
2929 /* [HGM] no bitmap available for promoted pieces in Crazyhouse */
\r
2930 /* Bitmaps of smaller size are substituted, but we have to align them */
\r
2931 x += (squareSize - minorSize)>>1;
\r
2932 y += squareSize - minorSize - 2;
\r
2933 tmpSize = minorSize;
\r
2935 if (color || appData.allWhite ) {
\r
2936 oldBitmap = SelectObject(tmphdc, PieceBitmap(piece, WHITE_PIECE));
\r
2938 oldBrush = SelectObject(hdc, xBrush);
\r
2939 else oldBrush = SelectObject(hdc, blackPieceBrush);
\r
2940 if(appData.upsideDown && color==flipView)
\r
2941 StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, 0x00B8074A);
\r
2943 BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, 0x00B8074A);
\r
2944 /* Use black for outline of white pieces */
\r
2945 SelectObject(tmphdc, PieceBitmap(piece, OUTLINE_PIECE));
\r
2946 if(appData.upsideDown && color==flipView)
\r
2947 StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, SRCAND);
\r
2949 BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, SRCAND);
\r
2950 } else if(appData.pieceDirectory[0]) {
\r
2951 oldBitmap = SelectObject(tmphdc, PieceBitmap(piece, WHITE_PIECE));
\r
2952 oldBrush = SelectObject(hdc, xBrush);
\r
2953 if(appData.upsideDown && color==flipView)
\r
2954 StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, 0x00B8074A);
\r
2956 BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, 0x00B8074A);
\r
2957 SelectObject(tmphdc, PieceBitmap(piece, SOLID_PIECE));
\r
2958 if(appData.upsideDown && color==flipView)
\r
2959 StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, SRCAND);
\r
2961 BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, SRCAND);
\r
2963 /* Use square color for details of black pieces */
\r
2964 oldBitmap = SelectObject(tmphdc, PieceBitmap(piece, SOLID_PIECE));
\r
2965 oldBrush = SelectObject(hdc, blackPieceBrush);
\r
2966 if(appData.upsideDown && !flipView)
\r
2967 StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, 0x00B8074A);
\r
2969 BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, 0x00B8074A);
\r
2971 SelectObject(hdc, oldBrush);
\r
2972 SelectObject(tmphdc, oldBitmap);
\r
2976 /* [AS] Compute a drawing mode for a square, based on specified settings (see DrawTile) */
\r
2977 int GetBackTextureMode( int algo )
\r
2979 int result = BACK_TEXTURE_MODE_DISABLED;
\r
2983 case BACK_TEXTURE_MODE_PLAIN:
\r
2984 result = 1; /* Always use identity map */
\r
2986 case BACK_TEXTURE_MODE_FULL_RANDOM:
\r
2987 result = 1 + (myrandom() % 3); /* Pick a transformation at random */
\r
2995 [AS] Compute and save texture drawing info, otherwise we may not be able
\r
2996 to handle redraws cleanly (as random numbers would always be different).
\r
2998 VOID RebuildTextureSquareInfo()
\r
3008 ZeroMemory( &backTextureSquareInfo, sizeof(backTextureSquareInfo) );
\r
3010 if( liteBackTexture != NULL ) {
\r
3011 if( GetObject( liteBackTexture, sizeof(bi), &bi ) > 0 ) {
\r
3012 lite_w = bi.bmWidth;
\r
3013 lite_h = bi.bmHeight;
\r
3017 if( darkBackTexture != NULL ) {
\r
3018 if( GetObject( darkBackTexture, sizeof(bi), &bi ) > 0 ) {
\r
3019 dark_w = bi.bmWidth;
\r
3020 dark_h = bi.bmHeight;
\r
3024 for( row=0; row<BOARD_HEIGHT; row++ ) {
\r
3025 for( col=0; col<BOARD_WIDTH; col++ ) {
\r
3026 if( (col + row) & 1 ) {
\r
3028 if( lite_w >= squareSize && lite_h >= squareSize ) {
\r
3029 if( lite_w >= squareSize*BOARD_WIDTH )
\r
3030 backTextureSquareInfo[row][col].x = (2*col+1)*lite_w/(2*BOARD_WIDTH) - squareSize/2; /* [HGM] cut out of center of virtual square */
\r
3032 backTextureSquareInfo[row][col].x = col * (lite_w - squareSize) / (BOARD_WIDTH-1); /* [HGM] divide by size-1 in stead of size! */
\r
3033 if( lite_h >= squareSize*BOARD_HEIGHT )
\r
3034 backTextureSquareInfo[row][col].y = (2*(BOARD_HEIGHT-row)-1)*lite_h/(2*BOARD_HEIGHT) - squareSize/2;
\r
3036 backTextureSquareInfo[row][col].y = (BOARD_HEIGHT-1-row) * (lite_h - squareSize) / (BOARD_HEIGHT-1);
\r
3037 backTextureSquareInfo[row][col].mode = GetBackTextureMode(liteBackTextureMode);
\r
3042 if( dark_w >= squareSize && dark_h >= squareSize ) {
\r
3043 if( dark_w >= squareSize*BOARD_WIDTH )
\r
3044 backTextureSquareInfo[row][col].x = (2*col+1) * dark_w / (2*BOARD_WIDTH) - squareSize/2;
\r
3046 backTextureSquareInfo[row][col].x = col * (dark_w - squareSize) / (BOARD_WIDTH-1);
\r
3047 if( dark_h >= squareSize*BOARD_HEIGHT )
\r
3048 backTextureSquareInfo[row][col].y = (2*(BOARD_HEIGHT-row)-1) * dark_h / (2*BOARD_HEIGHT) - squareSize/2;
\r
3050 backTextureSquareInfo[row][col].y = (BOARD_HEIGHT-1-row) * (dark_h - squareSize) / (BOARD_HEIGHT-1);
\r
3051 backTextureSquareInfo[row][col].mode = GetBackTextureMode(darkBackTextureMode);
\r
3058 /* [AS] Arrow highlighting support */
\r
3060 static double A_WIDTH = 5; /* Width of arrow body */
\r
3062 #define A_HEIGHT_FACTOR 6 /* Length of arrow "point", relative to body width */
\r
3063 #define A_WIDTH_FACTOR 3 /* Width of arrow "point", relative to body width */
\r
3065 static double Sqr( double x )
\r
3070 static int Round( double x )
\r
3072 return (int) (x + 0.5);
\r
3075 /* Draw an arrow between two points using current settings */
\r
3076 VOID DrawArrowBetweenPoints( HDC hdc, int s_x, int s_y, int d_x, int d_y )
\r
3079 double dx, dy, j, k, x, y;
\r
3081 if( d_x == s_x ) {
\r
3082 int h = (d_y > s_y) ? +A_WIDTH*A_HEIGHT_FACTOR : -A_WIDTH*A_HEIGHT_FACTOR;
\r
3084 arrow[0].x = s_x + A_WIDTH + 0.5;
\r
3087 arrow[1].x = s_x + A_WIDTH + 0.5;
\r
3088 arrow[1].y = d_y - h;
\r
3090 arrow[2].x = arrow[1].x + A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;
\r
3091 arrow[2].y = d_y - h;
\r
3096 arrow[5].x = arrow[1].x - 2*A_WIDTH + 0.5;
\r
3097 arrow[5].y = d_y - h;
\r
3099 arrow[4].x = arrow[5].x - A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;
\r
3100 arrow[4].y = d_y - h;
\r
3102 arrow[6].x = arrow[1].x - 2*A_WIDTH + 0.5;
\r
3105 else if( d_y == s_y ) {
\r
3106 int w = (d_x > s_x) ? +A_WIDTH*A_HEIGHT_FACTOR : -A_WIDTH*A_HEIGHT_FACTOR;
\r
3109 arrow[0].y = s_y + A_WIDTH + 0.5;
\r
3111 arrow[1].x = d_x - w;
\r
3112 arrow[1].y = s_y + A_WIDTH + 0.5;
\r
3114 arrow[2].x = d_x - w;
\r
3115 arrow[2].y = arrow[1].y + A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;
\r
3120 arrow[5].x = d_x - w;
\r
3121 arrow[5].y = arrow[1].y - 2*A_WIDTH + 0.5;
\r
3123 arrow[4].x = d_x - w;
\r
3124 arrow[4].y = arrow[5].y - A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;
\r
3127 arrow[6].y = arrow[1].y - 2*A_WIDTH + 0.5;
\r
3130 /* [AS] Needed a lot of paper for this! :-) */
\r
3131 dy = (double) (d_y - s_y) / (double) (d_x - s_x);
\r
3132 dx = (double) (s_x - d_x) / (double) (s_y - d_y);
\r
3134 j = sqrt( Sqr(A_WIDTH) / (1.0 + Sqr(dx)) );
\r
3136 k = sqrt( Sqr(A_WIDTH*A_HEIGHT_FACTOR) / (1.0 + Sqr(dy)) );
\r
3141 arrow[0].x = Round(x - j);
\r
3142 arrow[0].y = Round(y + j*dx);
\r
3144 arrow[1].x = Round(arrow[0].x + 2*j); // [HGM] prevent width to be affected by rounding twice
\r
3145 arrow[1].y = Round(arrow[0].y - 2*j*dx);
\r
3148 x = (double) d_x - k;
\r
3149 y = (double) d_y - k*dy;
\r
3152 x = (double) d_x + k;
\r
3153 y = (double) d_y + k*dy;
\r
3156 x = Round(x); y = Round(y); // [HGM] make sure width of shaft is rounded the same way on both ends
\r
3158 arrow[6].x = Round(x - j);
\r
3159 arrow[6].y = Round(y + j*dx);
\r
3161 arrow[2].x = Round(arrow[6].x + 2*j);
\r
3162 arrow[2].y = Round(arrow[6].y - 2*j*dx);
\r
3164 arrow[3].x = Round(arrow[2].x + j*(A_WIDTH_FACTOR-1));
\r
3165 arrow[3].y = Round(arrow[2].y - j*(A_WIDTH_FACTOR-1)*dx);
\r
3170 arrow[5].x = Round(arrow[6].x - j*(A_WIDTH_FACTOR-1));
\r
3171 arrow[5].y = Round(arrow[6].y + j*(A_WIDTH_FACTOR-1)*dx);
\r
3174 Polygon( hdc, arrow, 7 );
\r
3177 /* [AS] Draw an arrow between two squares */
\r
3178 VOID DrawArrowBetweenSquares( HDC hdc, int s_col, int s_row, int d_col, int d_row )
\r
3180 int s_x, s_y, d_x, d_y;
\r
3187 if( s_col == d_col && s_row == d_row ) {
\r
3191 /* Get source and destination points */
\r
3192 SquareToPos( s_row, s_col, &s_x, &s_y);
\r
3193 SquareToPos( d_row, d_col, &d_x, &d_y);
\r
3196 d_y += squareSize / 2 - squareSize / 4; // [HGM] round towards same centers on all sides!
\r
3198 else if( d_y < s_y ) {
\r
3199 d_y += squareSize / 2 + squareSize / 4;
\r
3202 d_y += squareSize / 2;
\r
3206 d_x += squareSize / 2 - squareSize / 4;
\r
3208 else if( d_x < s_x ) {
\r
3209 d_x += squareSize / 2 + squareSize / 4;
\r
3212 d_x += squareSize / 2;
\r
3215 s_x += squareSize / 2;
\r
3216 s_y += squareSize / 2;
\r
3218 /* Adjust width */
\r
3219 A_WIDTH = squareSize / 14.; //[HGM] make float
\r
3222 stLB.lbStyle = BS_SOLID;
\r
3223 stLB.lbColor = appData.highlightArrowColor;
\r
3226 hpen = CreatePen( PS_SOLID, 2, RGB(0x00,0x00,0x00) );
\r
3227 holdpen = SelectObject( hdc, hpen );
\r
3228 hbrush = CreateBrushIndirect( &stLB );
\r
3229 holdbrush = SelectObject( hdc, hbrush );
\r
3231 DrawArrowBetweenPoints( hdc, s_x, s_y, d_x, d_y );
\r
3233 SelectObject( hdc, holdpen );
\r
3234 SelectObject( hdc, holdbrush );
\r
3235 DeleteObject( hpen );
\r
3236 DeleteObject( hbrush );
\r
3239 BOOL HasHighlightInfo()
\r
3241 BOOL result = FALSE;
\r
3243 if( highlightInfo.sq[0].x >= 0 && highlightInfo.sq[0].y >= 0 &&
\r
3244 highlightInfo.sq[1].x >= 0 && highlightInfo.sq[1].y >= 0 )
\r
3252 BOOL IsDrawArrowEnabled()
\r
3254 BOOL result = FALSE;
\r
3256 if( appData.highlightMoveWithArrow && squareSize >= 32 ) {
\r
3263 VOID DrawArrowHighlight( HDC hdc )
\r
3265 if( IsDrawArrowEnabled() && HasHighlightInfo() ) {
\r
3266 DrawArrowBetweenSquares( hdc,
\r
3267 highlightInfo.sq[0].x, highlightInfo.sq[0].y,
\r
3268 highlightInfo.sq[1].x, highlightInfo.sq[1].y );
\r
3272 HRGN GetArrowHighlightClipRegion( HDC hdc )
\r
3274 HRGN result = NULL;
\r
3276 if( HasHighlightInfo() ) {
\r
3277 int x1, y1, x2, y2;
\r
3278 int sx, sy, dx, dy;
\r
3280 SquareToPos(highlightInfo.sq[0].y, highlightInfo.sq[0].x, &x1, &y1 );
\r
3281 SquareToPos(highlightInfo.sq[1].y, highlightInfo.sq[1].x, &x2, &y2 );
\r
3283 sx = MIN( x1, x2 );
\r
3284 sy = MIN( y1, y2 );
\r
3285 dx = MAX( x1, x2 ) + squareSize;
\r
3286 dy = MAX( y1, y2 ) + squareSize;
\r
3288 result = CreateRectRgn( sx, sy, dx, dy );
\r
3295 Warning: this function modifies the behavior of several other functions.
\r
3297 Basically, Winboard is optimized to avoid drawing the whole board if not strictly
\r
3298 needed. Unfortunately, the decision whether or not to perform a full or partial
\r
3299 repaint is scattered all over the place, which is not good for features such as
\r
3300 "arrow highlighting" that require a full repaint of the board.
\r
3302 So, I've tried to patch the code where I thought it made sense (e.g. after or during
\r
3303 user interaction, when speed is not so important) but especially to avoid errors
\r
3304 in the displayed graphics.
\r
3306 In such patched places, I always try refer to this function so there is a single
\r
3307 place to maintain knowledge.
\r
3309 To restore the original behavior, just return FALSE unconditionally.
\r
3311 BOOL IsFullRepaintPreferrable()
\r
3313 BOOL result = FALSE;
\r
3315 if( (appData.highlightLastMove || appData.highlightDragging) && IsDrawArrowEnabled() ) {
\r
3316 /* Arrow may appear on the board */
\r
3324 This function is called by DrawPosition to know whether a full repaint must
\r
3327 Only DrawPosition may directly call this function, which makes use of
\r
3328 some state information. Other function should call DrawPosition specifying
\r
3329 the repaint flag, and can use IsFullRepaintPreferrable if needed.
\r
3331 BOOL DrawPositionNeedsFullRepaint()
\r
3333 BOOL result = FALSE;
\r
3336 Probably a slightly better policy would be to trigger a full repaint
\r
3337 when animInfo.piece changes state (i.e. empty -> non-empty and viceversa),
\r
3338 but animation is fast enough that it's difficult to notice.
\r
3340 if( animInfo.piece == EmptySquare ) {
\r
3341 if( (appData.highlightLastMove || appData.highlightDragging) && IsDrawArrowEnabled() /*&& HasHighlightInfo()*/ ) {
\r
3349 static HBITMAP borderBitmap;
\r
3352 DrawBackgroundOnDC(HDC hdc)
\r
3358 static char oldBorder[MSG_SIZ];
\r
3359 int w = 600, h = 600, mode;
\r
3361 if(strcmp(appData.border, oldBorder)) { // load new one when old one no longer valid
\r
3362 strncpy(oldBorder, appData.border, MSG_SIZ-1);
\r
3363 borderBitmap = LoadImage( 0, appData.border, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );
\r
3365 if(borderBitmap == NULL) { // loading failed, use white
\r
3366 FillRect( hdc, &boardRect, whitePieceBrush );
\r
3369 tmphdc = CreateCompatibleDC(hdc);
\r
3370 hbm = SelectObject(tmphdc, borderBitmap);
\r
3371 if( GetObject( borderBitmap, sizeof(bi), &bi ) > 0 ) {
\r
3375 mode = SetStretchBltMode(hdc, COLORONCOLOR);
\r
3376 StretchBlt(hdc, boardRect.left, boardRect.top, boardRect.right - boardRect.left,
\r
3377 boardRect.bottom - boardRect.top, tmphdc, 0, 0, w, h, SRCCOPY);
\r
3378 SetStretchBltMode(hdc, mode);
\r
3379 SelectObject(tmphdc, hbm);
\r
3384 DrawBoardOnDC(HDC hdc, Board board, HDC tmphdc)
\r
3386 int row, column, x, y, square_color, piece_color;
\r
3387 ChessSquare piece;
\r
3389 HDC texture_hdc = NULL;
\r
3391 /* [AS] Initialize background textures if needed */
\r
3392 if( liteBackTexture != NULL || darkBackTexture != NULL ) {
\r
3393 static int backTextureBoardSize; /* [HGM] boardsize: also new texture if board format changed */
\r
3394 if( backTextureSquareSize != squareSize
\r
3395 || backTextureBoardSize != BOARD_WIDTH+BOARD_FILES*BOARD_HEIGHT) {
\r
3396 backTextureBoardSize = BOARD_WIDTH+BOARD_FILES*BOARD_HEIGHT;
\r
3397 backTextureSquareSize = squareSize;
\r
3398 RebuildTextureSquareInfo();
\r
3401 texture_hdc = CreateCompatibleDC( hdc );
\r
3404 for (row = 0; row < BOARD_HEIGHT; row++) {
\r
3405 for (column = 0; column < BOARD_WIDTH; column++) {
\r
3407 SquareToPos(row, column, &x, &y);
\r
3409 piece = board[row][column];
\r
3411 square_color = ((column + row) % 2) == 1;
\r
3412 if( gameInfo.variant == VariantXiangqi ) {
\r
3413 square_color = !InPalace(row, column);
\r
3414 if(BOARD_HEIGHT&1) { if(row==BOARD_HEIGHT/2) square_color ^= 1; }
\r
3415 else if(row < BOARD_HEIGHT/2) square_color ^= 1;
\r
3417 piece_color = (int) piece < (int) BlackPawn;
\r
3420 /* [HGM] holdings file: light square or black */
\r
3421 if(column == BOARD_LEFT-2) {
\r
3422 if( row > BOARD_HEIGHT - gameInfo.holdingsSize - 1 )
\r
3425 DisplayHoldingsCount(hdc, x, y, 0, 0); /* black out */
\r
3429 if(column == BOARD_RGHT + 1 ) {
\r
3430 if( row < gameInfo.holdingsSize )
\r
3433 DisplayHoldingsCount(hdc, x, y, 0, 0);
\r
3437 if(column == BOARD_LEFT-1 ) /* left align */
\r
3438 DisplayHoldingsCount(hdc, x, y, flipView, (int) board[row][column]);
\r
3439 else if( column == BOARD_RGHT) /* right align */
\r
3440 DisplayHoldingsCount(hdc, x, y, !flipView, (int) board[row][column]);
\r
3442 if (appData.monoMode) {
\r
3443 if (piece == EmptySquare) {
\r
3444 BitBlt(hdc, x, y, squareSize, squareSize, 0, 0, 0,
\r
3445 square_color ? WHITENESS : BLACKNESS);
\r
3447 DrawPieceOnDC(hdc, piece, piece_color, square_color, x, y, tmphdc);
\r
3450 else if( appData.useBitmaps && backTextureSquareInfo[row][column].mode > 0 ) {
\r
3451 /* [AS] Draw the square using a texture bitmap */
\r
3452 HBITMAP hbm = SelectObject( texture_hdc, square_color ? liteBackTexture : darkBackTexture );
\r
3453 int r = row, c = column; // [HGM] do not flip board in flipView
\r
3454 if(flipView) { r = BOARD_HEIGHT-1 - r; c = BOARD_WIDTH-1 - c; }
\r
3457 squareSize, squareSize,
\r
3460 backTextureSquareInfo[r][c].mode,
\r
3461 backTextureSquareInfo[r][c].x,
\r
3462 backTextureSquareInfo[r][c].y );
\r
3464 SelectObject( texture_hdc, hbm );
\r
3466 if (piece != EmptySquare) {
\r
3467 DrawPieceOnDC(hdc, piece, piece_color, -1, x, y, tmphdc);
\r
3471 HBRUSH brush = square_color ? lightSquareBrush : darkSquareBrush;
\r
3473 oldBrush = SelectObject(hdc, brush );
\r
3474 BitBlt(hdc, x, y, squareSize, squareSize, 0, 0, 0, PATCOPY);
\r
3475 SelectObject(hdc, oldBrush);
\r
3476 if (piece != EmptySquare)
\r
3477 DrawPieceOnDC(hdc, piece, piece_color, -1, x, y, tmphdc);
\r
3482 if( texture_hdc != NULL ) {
\r
3483 DeleteDC( texture_hdc );
\r
3487 int saveDiagFlag = 0; FILE *diagFile; // [HGM] diag
\r
3488 void fputDW(FILE *f, int x)
\r
3490 fputc(x & 255, f);
\r
3491 fputc(x>>8 & 255, f);
\r
3492 fputc(x>>16 & 255, f);
\r
3493 fputc(x>>24 & 255, f);
\r
3496 #define MAX_CLIPS 200 /* more than enough */
\r
3499 DrawLogoOnDC(HDC hdc, RECT logoRect, HBITMAP logo)
\r
3501 // HBITMAP bufferBitmap;
\r
3506 int w = 100, h = 50;
\r
3508 if(logo == NULL) {
\r
3509 if(!logoHeight) return;
\r
3510 FillRect( hdc, &logoRect, whitePieceBrush );
\r
3512 // GetClientRect(hwndMain, &Rect);
\r
3513 // bufferBitmap = CreateCompatibleBitmap(hdc, Rect.right-Rect.left+1,
\r
3514 // Rect.bottom-Rect.top+1);
\r
3515 tmphdc = CreateCompatibleDC(hdc);
\r
3516 hbm = SelectObject(tmphdc, logo);
\r
3517 if( GetObject( logo, sizeof(bi), &bi ) > 0 ) {
\r
3521 StretchBlt(hdc, logoRect.left, logoRect.top, logoRect.right - logoRect.left,
\r
3522 logoRect.bottom - logoRect.top, tmphdc, 0, 0, w, h, SRCCOPY);
\r
3523 SelectObject(tmphdc, hbm);
\r
3531 HDC hdc = GetDC(hwndMain);
\r
3532 HBITMAP whiteLogo = (HBITMAP) first.programLogo, blackLogo = (HBITMAP) second.programLogo;
\r
3533 if(appData.autoLogo) {
\r
3535 switch(gameMode) { // pick logos based on game mode
\r
3536 case IcsObserving:
\r
3537 whiteLogo = second.programLogo; // ICS logo
\r
3538 blackLogo = second.programLogo;
\r
3541 case IcsPlayingWhite:
\r
3542 if(!appData.zippyPlay) whiteLogo = userLogo;
\r
3543 blackLogo = second.programLogo; // ICS logo
\r
3545 case IcsPlayingBlack:
\r
3546 whiteLogo = second.programLogo; // ICS logo
\r
3547 blackLogo = appData.zippyPlay ? first.programLogo : userLogo;
\r
3549 case TwoMachinesPlay:
\r
3550 if(first.twoMachinesColor[0] == 'b') {
\r
3551 whiteLogo = second.programLogo;
\r
3552 blackLogo = first.programLogo;
\r
3555 case MachinePlaysWhite:
\r
3556 blackLogo = userLogo;
\r
3558 case MachinePlaysBlack:
\r
3559 whiteLogo = userLogo;
\r
3560 blackLogo = first.programLogo;
\r
3563 DrawLogoOnDC(hdc, leftLogoRect, flipClock ? blackLogo : whiteLogo);
\r
3564 DrawLogoOnDC(hdc, rightLogoRect, flipClock ? whiteLogo : blackLogo);
\r
3565 ReleaseDC(hwndMain, hdc);
\r
3570 UpdateLogos(int display)
\r
3571 { // called after loading new engine(s), in tourney or from menu
\r
3572 LoadLogo(&first, 0, FALSE);
\r
3573 LoadLogo(&second, 1, appData.icsActive);
\r
3574 InitDrawingSizes(-2, 0); // adapt layout of board window to presence/absence of logos
\r
3575 if(display) DisplayLogos();
\r
3578 static HDC hdcSeek;
\r
3580 // [HGM] seekgraph
\r
3581 void DrawSeekAxis( int x, int y, int xTo, int yTo )
\r
3584 HPEN hp = SelectObject( hdcSeek, gridPen );
\r
3585 MoveToEx( hdcSeek, boardRect.left+x, boardRect.top+y, &stPt );
\r
3586 LineTo( hdcSeek, boardRect.left+xTo, boardRect.top+yTo );
\r
3587 SelectObject( hdcSeek, hp );
\r
3590 // front-end wrapper for drawing functions to do rectangles
\r
3591 void DrawSeekBackground( int left, int top, int right, int bottom )
\r
3596 if (hdcSeek == NULL) {
\r
3597 hdcSeek = GetDC(hwndMain);
\r
3598 if (!appData.monoMode) {
\r
3599 SelectPalette(hdcSeek, hPal, FALSE);
\r
3600 RealizePalette(hdcSeek);
\r
3603 hp = SelectObject( hdcSeek, gridPen );
\r
3604 rc.top = boardRect.top+top; rc.left = boardRect.left+left;
\r
3605 rc.bottom = boardRect.top+bottom; rc.right = boardRect.left+right;
\r
3606 FillRect( hdcSeek, &rc, lightSquareBrush );
\r
3607 SelectObject( hdcSeek, hp );
\r
3610 // front-end wrapper for putting text in graph
\r
3611 void DrawSeekText(char *buf, int x, int y)
\r
3614 SetBkMode( hdcSeek, TRANSPARENT );
\r
3615 GetTextExtentPoint32( hdcSeek, buf, strlen(buf), &stSize );
\r
3616 TextOut( hdcSeek, boardRect.left+x-3, boardRect.top+y-stSize.cy/2, buf, strlen(buf) );
\r
3619 void DrawSeekDot(int x, int y, int color)
\r
3621 int square = color & 0x80;
\r
3622 HBRUSH oldBrush = SelectObject(hdcSeek,
\r
3623 color == 0 ? markerBrush : color == 1 ? darkSquareBrush : explodeBrush);
\r
3626 Rectangle(hdcSeek, boardRect.left+x - squareSize/9, boardRect.top+y - squareSize/9,
\r
3627 boardRect.left+x + squareSize/9, boardRect.top+y + squareSize/9);
\r
3629 Ellipse(hdcSeek, boardRect.left+x - squareSize/8, boardRect.top+y - squareSize/8,
\r
3630 boardRect.left+x + squareSize/8, boardRect.top+y + squareSize/8);
\r
3631 SelectObject(hdcSeek, oldBrush);
\r
3634 void DrawSeekOpen()
\r
3638 void DrawSeekClose()
\r
3643 HDCDrawPosition(HDC hdc, BOOLEAN repaint, Board board)
\r
3645 static Board lastReq[2], lastDrawn[2];
\r
3646 static HighlightInfo lastDrawnHighlight, lastDrawnPremove;
\r
3647 static int lastDrawnFlipView = 0;
\r
3648 static int lastReqValid[2] = {0, 0}, lastDrawnValid[2] = {0, 0};
\r
3649 int releaseDC, x, y, x2, y2, row, column, num_clips = 0, i;
\r
3652 HBITMAP bufferBitmap;
\r
3653 HBITMAP oldBitmap;
\r
3655 HRGN clips[MAX_CLIPS];
\r
3656 ChessSquare dragged_piece = EmptySquare;
\r
3657 int nr = twoBoards*partnerUp;
\r
3659 /* I'm undecided on this - this function figures out whether a full
\r
3660 * repaint is necessary on its own, so there's no real reason to have the
\r
3661 * caller tell it that. I think this can safely be set to FALSE - but
\r
3662 * if we trust the callers not to request full repaints unnessesarily, then
\r
3663 * we could skip some clipping work. In other words, only request a full
\r
3664 * redraw when the majority of pieces have changed positions (ie. flip,
\r
3665 * gamestart and similar) --Hawk
\r
3667 Boolean fullrepaint = repaint;
\r
3669 if(DrawSeekGraph()) return; // [HG} seekgraph: suppress printing board if seek graph up
\r
3671 if( DrawPositionNeedsFullRepaint() ) {
\r
3672 fullrepaint = TRUE;
\r
3675 if (board == NULL) {
\r
3676 if (!lastReqValid[nr]) {
\r
3679 board = lastReq[nr];
\r
3681 CopyBoard(lastReq[nr], board);
\r
3682 lastReqValid[nr] = 1;
\r
3685 if (doingSizing) {
\r
3689 if (IsIconic(hwndMain)) {
\r
3693 if (hdc == NULL) {
\r
3694 hdc = GetDC(hwndMain);
\r
3695 if (!appData.monoMode) {
\r
3696 SelectPalette(hdc, hPal, FALSE);
\r
3697 RealizePalette(hdc);
\r
3701 releaseDC = FALSE;
\r
3704 /* Create some work-DCs */
\r
3705 hdcmem = CreateCompatibleDC(hdc);
\r
3706 tmphdc = CreateCompatibleDC(hdc);
\r
3708 /* If dragging is in progress, we temporarely remove the piece */
\r
3709 /* [HGM] or temporarily decrease count if stacked */
\r
3710 /* !! Moved to before board compare !! */
\r
3711 if (dragInfo.from.x >= 0 && dragInfo.pos.x >= 0) {
\r
3712 dragged_piece = board[dragInfo.from.y][dragInfo.from.x];
\r
3713 if(dragInfo.from.x == BOARD_LEFT-2 ) {
\r
3714 if(--board[dragInfo.from.y][dragInfo.from.x+1] == 0 )
\r
3715 board[dragInfo.from.y][dragInfo.from.x] = EmptySquare;
\r
3717 if(dragInfo.from.x == BOARD_RGHT+1) {
\r
3718 if(--board[dragInfo.from.y][dragInfo.from.x-1] == 0 )
\r
3719 board[dragInfo.from.y][dragInfo.from.x] = EmptySquare;
\r
3721 board[dragInfo.from.y][dragInfo.from.x] = gatingPiece;
\r
3724 /* Figure out which squares need updating by comparing the
\r
3725 * newest board with the last drawn board and checking if
\r
3726 * flipping has changed.
\r
3728 if (!fullrepaint && lastDrawnValid[nr] && (nr == 1 || lastDrawnFlipView == flipView)) {
\r
3729 for (row = 0; row < BOARD_HEIGHT; row++) { /* [HGM] true size, not 8 */
\r
3730 for (column = 0; column < BOARD_WIDTH; column++) {
\r
3731 if (lastDrawn[nr][row][column] != board[row][column]) {
\r
3732 SquareToPos(row, column, &x, &y);
\r
3733 clips[num_clips++] =
\r
3734 CreateRectRgn(x, y, x + squareSize, y + squareSize);
\r
3738 if(nr == 0) { // [HGM] dual: no highlights on second board
\r
3739 for (i=0; i<2; i++) {
\r
3740 if (lastDrawnHighlight.sq[i].x != highlightInfo.sq[i].x ||
\r
3741 lastDrawnHighlight.sq[i].y != highlightInfo.sq[i].y) {
\r
3742 if (lastDrawnHighlight.sq[i].x >= 0 &&
\r
3743 lastDrawnHighlight.sq[i].y >= 0) {
\r
3744 SquareToPos(lastDrawnHighlight.sq[i].y,
\r
3745 lastDrawnHighlight.sq[i].x, &x, &y);
\r
3746 clips[num_clips++] =
\r
3747 CreateRectRgn(x - lineGap, y - lineGap,
\r
3748 x + squareSize + lineGap, y + squareSize + lineGap);
\r
3750 if (highlightInfo.sq[i].x >= 0 && highlightInfo.sq[i].y >= 0) {
\r
3751 SquareToPos(highlightInfo.sq[i].y, highlightInfo.sq[i].x, &x, &y);
\r
3752 clips[num_clips++] =
\r
3753 CreateRectRgn(x - lineGap, y - lineGap,
\r
3754 x + squareSize + lineGap, y + squareSize + lineGap);
\r
3758 for (i=0; i<2; i++) {
\r
3759 if (lastDrawnPremove.sq[i].x != premoveHighlightInfo.sq[i].x ||
\r
3760 lastDrawnPremove.sq[i].y != premoveHighlightInfo.sq[i].y) {
\r
3761 if (lastDrawnPremove.sq[i].x >= 0 &&
\r
3762 lastDrawnPremove.sq[i].y >= 0) {
\r
3763 SquareToPos(lastDrawnPremove.sq[i].y,
\r
3764 lastDrawnPremove.sq[i].x, &x, &y);
\r
3765 clips[num_clips++] =
\r
3766 CreateRectRgn(x - lineGap, y - lineGap,
\r
3767 x + squareSize + lineGap, y + squareSize + lineGap);
\r
3769 if (premoveHighlightInfo.sq[i].x >= 0 &&
\r
3770 premoveHighlightInfo.sq[i].y >= 0) {
\r
3771 SquareToPos(premoveHighlightInfo.sq[i].y,
\r
3772 premoveHighlightInfo.sq[i].x, &x, &y);
\r
3773 clips[num_clips++] =
\r
3774 CreateRectRgn(x - lineGap, y - lineGap,
\r
3775 x + squareSize + lineGap, y + squareSize + lineGap);
\r
3779 } else { // nr == 1
\r
3780 partnerHighlightInfo.sq[0].y = board[EP_STATUS-4];
\r
3781 partnerHighlightInfo.sq[0].x = board[EP_STATUS-3];
\r
3782 partnerHighlightInfo.sq[1].y = board[EP_STATUS-2];
\r
3783 partnerHighlightInfo.sq[1].x = board[EP_STATUS-1];
\r
3784 for (i=0; i<2; i++) {
\r
3785 if (partnerHighlightInfo.sq[i].x >= 0 &&
\r
3786 partnerHighlightInfo.sq[i].y >= 0) {
\r
3787 SquareToPos(partnerHighlightInfo.sq[i].y,
\r
3788 partnerHighlightInfo.sq[i].x, &x, &y);
\r
3789 clips[num_clips++] =
\r
3790 CreateRectRgn(x - lineGap, y - lineGap,
\r
3791 x + squareSize + lineGap, y + squareSize + lineGap);
\r
3793 if (oldPartnerHighlight.sq[i].x >= 0 &&
\r
3794 oldPartnerHighlight.sq[i].y >= 0) {
\r
3795 SquareToPos(oldPartnerHighlight.sq[i].y,
\r
3796 oldPartnerHighlight.sq[i].x, &x, &y);
\r
3797 clips[num_clips++] =
\r
3798 CreateRectRgn(x - lineGap, y - lineGap,
\r
3799 x + squareSize + lineGap, y + squareSize + lineGap);
\r
3804 fullrepaint = TRUE;
\r
3807 /* Create a buffer bitmap - this is the actual bitmap
\r
3808 * being written to. When all the work is done, we can
\r
3809 * copy it to the real DC (the screen). This avoids
\r
3810 * the problems with flickering.
\r
3812 GetClientRect(hwndMain, &Rect);
\r
3813 bufferBitmap = CreateCompatibleBitmap(hdc, Rect.right-Rect.left+1,
\r
3814 Rect.bottom-Rect.top+1);
\r
3815 oldBitmap = SelectObject(hdcmem, bufferBitmap);
\r
3816 if (!appData.monoMode) {
\r
3817 SelectPalette(hdcmem, hPal, FALSE);
\r
3820 /* Create clips for dragging */
\r
3821 if (!fullrepaint) {
\r
3822 if (dragInfo.from.x >= 0) {
\r
3823 SquareToPos(dragInfo.from.y, dragInfo.from.x, &x, &y);
\r
3824 clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);
\r
3826 if (dragInfo.start.x >= 0) {
\r
3827 SquareToPos(dragInfo.start.y, dragInfo.start.x, &x, &y);
\r
3828 clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);
\r
3830 if (dragInfo.pos.x >= 0) {
\r
3831 x = dragInfo.pos.x - squareSize / 2;
\r
3832 y = dragInfo.pos.y - squareSize / 2;
\r
3833 clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);
\r
3835 if (dragInfo.lastpos.x >= 0) {
\r
3836 x = dragInfo.lastpos.x - squareSize / 2;
\r
3837 y = dragInfo.lastpos.y - squareSize / 2;
\r
3838 clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);
\r
3842 /* Are we animating a move?
\r
3844 * - remove the piece from the board (temporarely)
\r
3845 * - calculate the clipping region
\r
3847 if (!fullrepaint) {
\r
3848 if (animInfo.piece != EmptySquare) {
\r
3849 board[animInfo.from.y][animInfo.from.x] = EmptySquare;
\r
3850 x = boardRect.left + animInfo.lastpos.x;
\r
3851 y = boardRect.top + animInfo.lastpos.y;
\r
3852 x2 = boardRect.left + animInfo.pos.x;
\r
3853 y2 = boardRect.top + animInfo.pos.y;
\r
3854 clips[num_clips++] = CreateRectRgn(MIN(x,x2), MIN(y,y2), MAX(x,x2)+squareSize, MAX(y,y2)+squareSize);
\r
3855 /* Slight kludge. The real problem is that after AnimateMove is
\r
3856 done, the position on the screen does not match lastDrawn.
\r
3857 This currently causes trouble only on e.p. captures in
\r
3858 atomic, where the piece moves to an empty square and then
\r
3859 explodes. The old and new positions both had an empty square
\r
3860 at the destination, but animation has drawn a piece there and
\r
3861 we have to remember to erase it. [HGM] moved until after setting lastDrawn */
\r
3862 lastDrawn[0][animInfo.to.y][animInfo.to.x] = animInfo.piece;
\r
3866 /* No clips? Make sure we have fullrepaint set to TRUE */
\r
3867 if (num_clips == 0)
\r
3868 fullrepaint = TRUE;
\r
3870 /* Set clipping on the memory DC */
\r
3871 if (!fullrepaint) {
\r
3872 SelectClipRgn(hdcmem, clips[0]);
\r
3873 for (x = 1; x < num_clips; x++) {
\r
3874 if (ExtSelectClipRgn(hdcmem, clips[x], RGN_OR) == ERROR)
\r
3875 abort(); // this should never ever happen!
\r
3879 /* Do all the drawing to the memory DC */
\r
3880 if(explodeInfo.radius) { // [HGM] atomic
\r
3882 int x, y, r=(explodeInfo.radius * squareSize)/100;
\r
3883 ChessSquare piece = board[explodeInfo.fromY][explodeInfo.fromX];
\r
3884 board[explodeInfo.fromY][explodeInfo.fromX] = EmptySquare; // suppress display of capturer
\r
3885 SquareToPos(explodeInfo.toY, explodeInfo.toX, &x, &y);
\r
3886 x += squareSize/2;
\r
3887 y += squareSize/2;
\r
3888 if(!fullrepaint) {
\r
3889 clips[num_clips] = CreateRectRgn(x-r, y-r, x+r, y+r);
\r
3890 ExtSelectClipRgn(hdcmem, clips[num_clips++], RGN_OR);
\r
3892 DrawGridOnDC(hdcmem);
\r
3893 DrawHighlightsOnDC(hdcmem, &highlightInfo, HIGHLIGHT_PEN);
\r
3894 DrawHighlightsOnDC(hdcmem, &premoveHighlightInfo, PREMOVE_PEN);
\r
3895 DrawBoardOnDC(hdcmem, board, tmphdc);
\r
3896 board[explodeInfo.fromY][explodeInfo.fromX] = piece;
\r
3897 oldBrush = SelectObject(hdcmem, explodeBrush);
\r
3898 Ellipse(hdcmem, x-r, y-r, x+r, y+r);
\r
3899 SelectObject(hdcmem, oldBrush);
\r
3901 if(border) DrawBackgroundOnDC(hdcmem);
\r
3902 DrawGridOnDC(hdcmem);
\r
3903 if(nr == 0) { // [HGM] dual: decide which highlights to draw
\r
3904 DrawHighlightsOnDC(hdcmem, &highlightInfo, HIGHLIGHT_PEN);
\r
3905 DrawHighlightsOnDC(hdcmem, &premoveHighlightInfo, PREMOVE_PEN);
\r
3907 DrawHighlightsOnDC(hdcmem, &partnerHighlightInfo, HIGHLIGHT_PEN);
\r
3908 oldPartnerHighlight = partnerHighlightInfo;
\r
3910 DrawBoardOnDC(hdcmem, board, tmphdc);
\r
3912 if(nr == 0) // [HGM] dual: markers only on left board
\r
3913 for (row = 0; row < BOARD_HEIGHT; row++) {
\r
3914 for (column = 0; column < BOARD_WIDTH; column++) {
\r
3915 if (marker[row][column]) { // marker changes only occur with full repaint!
\r
3916 HBRUSH oldBrush = SelectObject(hdcmem,
\r
3917 marker[row][column] == 2 ? markerBrush : explodeBrush);
\r
3918 SquareToPos(row, column, &x, &y);
\r
3919 Ellipse(hdcmem, x + squareSize/4, y + squareSize/4,
\r
3920 x + 3*squareSize/4, y + 3*squareSize/4);
\r
3921 SelectObject(hdcmem, oldBrush);
\r
3926 if( appData.highlightMoveWithArrow ) {
\r
3927 DrawArrowHighlight(hdcmem);
\r
3930 DrawCoordsOnDC(hdcmem);
\r
3932 CopyBoard(lastDrawn[nr], board); /* [HGM] Moved to here from end of routine, */
\r
3933 /* to make sure lastDrawn contains what is actually drawn */
\r
3935 /* Put the dragged piece back into place and draw it (out of place!) */
\r
3936 if (dragged_piece != EmptySquare) {
\r
3937 /* [HGM] or restack */
\r
3938 if(dragInfo.from.x == BOARD_LEFT-2 )
\r
3939 board[dragInfo.from.y][dragInfo.from.x+1]++;
\r
3941 if(dragInfo.from.x == BOARD_RGHT+1 )
\r
3942 board[dragInfo.from.y][dragInfo.from.x-1]++;
\r
3943 board[dragInfo.from.y][dragInfo.from.x] = dragged_piece;
\r
3944 x = dragInfo.pos.x - squareSize / 2;
\r
3945 y = dragInfo.pos.y - squareSize / 2;
\r
3946 DrawPieceOnDC(hdcmem, dragInfo.piece,
\r
3947 ((int) dragInfo.piece < (int) BlackPawn),
\r
3948 (dragInfo.from.y + dragInfo.from.x) % 2, x, y, tmphdc);
\r
3951 /* Put the animated piece back into place and draw it */
\r
3952 if (animInfo.piece != EmptySquare) {
\r
3953 board[animInfo.from.y][animInfo.from.x] = animInfo.piece;
\r
3954 x = boardRect.left + animInfo.pos.x;
\r
3955 y = boardRect.top + animInfo.pos.y;
\r
3956 DrawPieceOnDC(hdcmem, animInfo.piece,
\r
3957 ((int) animInfo.piece < (int) BlackPawn),
\r
3958 (animInfo.from.y + animInfo.from.x) % 2, x, y, tmphdc);
\r
3961 /* Release the bufferBitmap by selecting in the old bitmap
\r
3962 * and delete the memory DC
\r
3964 SelectObject(hdcmem, oldBitmap);
\r
3967 /* Set clipping on the target DC */
\r
3968 if (!fullrepaint) {
\r
3969 if(nr == 1) for (x = 0; x < num_clips; x++) { // [HGM] dual: translate clips
\r
3971 GetRgnBox(clips[x], &rect);
\r
3972 DeleteObject(clips[x]);
\r
3973 clips[x] = CreateRectRgn(rect.left + wpMain.width/2, rect.top,
\r
3974 rect.right + wpMain.width/2, rect.bottom);
\r
3976 SelectClipRgn(hdc, clips[0]);
\r
3977 for (x = 1; x < num_clips; x++) {
\r
3978 if (ExtSelectClipRgn(hdc, clips[x], RGN_OR) == ERROR)
\r
3979 abort(); // this should never ever happen!
\r
3983 /* Copy the new bitmap onto the screen in one go.
\r
3984 * This way we avoid any flickering
\r
3986 oldBitmap = SelectObject(tmphdc, bufferBitmap);
\r
3987 BitBlt(hdc, boardRect.left + twoBoards*partnerUp*wpMain.width/2, boardRect.top, // [HGM] dual
\r
3988 boardRect.right - boardRect.left,
\r
3989 boardRect.bottom - boardRect.top,
\r
3990 tmphdc, boardRect.left, boardRect.top, SRCCOPY);
\r
3991 if(saveDiagFlag) {
\r
3992 BITMAP b; int i, j=0, m, w, wb, fac=0; char *pData;
\r
3993 BITMAPINFOHEADER bih; int color[16], nrColors=0;
\r
3995 GetObject(bufferBitmap, sizeof(b), &b);
\r
3996 if(pData = malloc(b.bmWidthBytes*b.bmHeight + 10000)) {
\r
3997 bih.biSize = sizeof(BITMAPINFOHEADER);
\r
3998 bih.biWidth = b.bmWidth;
\r
3999 bih.biHeight = b.bmHeight;
\r
4001 bih.biBitCount = b.bmBitsPixel;
\r
4002 bih.biCompression = 0;
\r
4003 bih.biSizeImage = b.bmWidthBytes*b.bmHeight;
\r
4004 bih.biXPelsPerMeter = 0;
\r
4005 bih.biYPelsPerMeter = 0;
\r
4006 bih.biClrUsed = 0;
\r
4007 bih.biClrImportant = 0;
\r
4008 // fprintf(diagFile, "t=%d\nw=%d\nh=%d\nB=%d\nP=%d\nX=%d\n",
\r
4009 // b.bmType, b.bmWidth, b.bmHeight, b.bmWidthBytes, b.bmPlanes, b.bmBitsPixel);
\r
4010 GetDIBits(tmphdc,bufferBitmap,0,b.bmHeight,pData,(BITMAPINFO*)&bih,DIB_RGB_COLORS);
\r
4011 // fprintf(diagFile, "%8x\n", (int) pData);
\r
4013 wb = b.bmWidthBytes;
\r
4015 for(i=0; i<wb*(b.bmHeight - boardRect.top + OUTER_MARGIN)>>2; i++) {
\r
4016 int k = ((int*) pData)[i];
\r
4017 for(j=0; j<nrColors; j++) if(color[j] == k) break;
\r
4018 if(j >= 16) break;
\r
4020 if(j >= nrColors) nrColors = j+1;
\r
4022 if(j<16) { // 16 colors is enough. Compress to 4 bits per pixel
\r
4024 for(i=0; i<b.bmHeight - boardRect.top + OUTER_MARGIN; i++) {
\r
4025 for(w=0; w<(wb>>2); w+=2) {
\r
4026 int k = ((int*) pData)[(wb*i>>2) + w];
\r
4027 for(j=0; j<nrColors; j++) if(color[j] == k) break;
\r
4028 k = ((int*) pData)[(wb*i>>2) + w + 1];
\r
4029 for(m=0; m<nrColors; m++) if(color[m] == k) break;
\r
4030 pData[p++] = m | j<<4;
\r
4032 while(p&3) pData[p++] = 0;
\r
4035 wb = ((wb+31)>>5)<<2;
\r
4037 // write BITMAPFILEHEADER
\r
4038 fprintf(diagFile, "BM");
\r
4039 fputDW(diagFile, wb*(b.bmHeight - boardRect.top + OUTER_MARGIN)+0x36 + (fac?64:0));
\r
4040 fputDW(diagFile, 0);
\r
4041 fputDW(diagFile, 0x36 + (fac?64:0));
\r
4042 // write BITMAPINFOHEADER
\r
4043 fputDW(diagFile, 40);
\r
4044 fputDW(diagFile, b.bmWidth);
\r
4045 fputDW(diagFile, b.bmHeight - boardRect.top + OUTER_MARGIN);
\r
4046 if(fac) fputDW(diagFile, 0x040001); // planes and bits/pixel
\r
4047 else fputDW(diagFile, 0x200001); // planes and bits/pixel
\r
4048 fputDW(diagFile, 0);
\r
4049 fputDW(diagFile, 0);
\r
4050 fputDW(diagFile, 0);
\r
4051 fputDW(diagFile, 0);
\r
4052 fputDW(diagFile, 0);
\r
4053 fputDW(diagFile, 0);
\r
4054 // write color table
\r
4056 for(i=0; i<16; i++) fputDW(diagFile, color[i]);
\r
4057 // write bitmap data
\r
4058 for(i=0; i<wb*(b.bmHeight - boardRect.top + OUTER_MARGIN); i++)
\r
4059 fputc(pData[i], diagFile);
\r
4064 SelectObject(tmphdc, oldBitmap);
\r
4066 /* Massive cleanup */
\r
4067 for (x = 0; x < num_clips; x++)
\r
4068 DeleteObject(clips[x]);
\r
4071 DeleteObject(bufferBitmap);
\r
4074 ReleaseDC(hwndMain, hdc);
\r
4076 if (lastDrawnFlipView != flipView && nr == 0) {
\r
4078 CheckMenuItem(GetMenu(hwndMain),IDM_FlipView, MF_BYCOMMAND|MF_CHECKED);
\r
4080 CheckMenuItem(GetMenu(hwndMain),IDM_FlipView, MF_BYCOMMAND|MF_UNCHECKED);
\r
4083 /* CopyBoard(lastDrawn, board);*/
\r
4084 lastDrawnHighlight = highlightInfo;
\r
4085 lastDrawnPremove = premoveHighlightInfo;
\r
4086 lastDrawnFlipView = flipView;
\r
4087 lastDrawnValid[nr] = 1;
\r
4090 /* [HGM] diag: Save the current board display to the given open file and close the file */
\r
4095 saveDiagFlag = 1; diagFile = f;
\r
4096 HDCDrawPosition(NULL, TRUE, NULL);
\r
4104 /*---------------------------------------------------------------------------*\
\r
4105 | CLIENT PAINT PROCEDURE
\r
4106 | This is the main event-handler for the WM_PAINT message.
\r
4108 \*---------------------------------------------------------------------------*/
\r
4110 PaintProc(HWND hwnd)
\r
4116 if((hdc = BeginPaint(hwnd, &ps))) {
\r
4117 if (IsIconic(hwnd)) {
\r
4118 DrawIcon(hdc, 2, 2, iconCurrent);
\r
4120 if (!appData.monoMode) {
\r
4121 SelectPalette(hdc, hPal, FALSE);
\r
4122 RealizePalette(hdc);
\r
4124 HDCDrawPosition(hdc, 1, NULL);
\r
4125 if(twoBoards) { // [HGM] dual: also redraw other board in other orientation
\r
4126 flipView = !flipView; partnerUp = !partnerUp;
\r
4127 HDCDrawPosition(hdc, 1, NULL);
\r
4128 flipView = !flipView; partnerUp = !partnerUp;
\r
4131 SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);
\r
4132 ExtTextOut(hdc, messageRect.left, messageRect.top,
\r
4133 ETO_CLIPPED|ETO_OPAQUE,
\r
4134 &messageRect, messageText, strlen(messageText), NULL);
\r
4135 SelectObject(hdc, oldFont);
\r
4136 DisplayBothClocks();
\r
4139 EndPaint(hwnd,&ps);
\r
4147 * If the user selects on a border boundary, return -1; if off the board,
\r
4148 * return -2. Otherwise map the event coordinate to the square.
\r
4149 * The offset boardRect.left or boardRect.top must already have been
\r
4150 * subtracted from x.
\r
4152 int EventToSquare(x, limit)
\r
4157 if (x < lineGap + border)
\r
4159 x -= lineGap + border;
\r
4160 if ((x % (squareSize + lineGap)) >= squareSize)
\r
4162 x /= (squareSize + lineGap);
\r
4174 DropEnable dropEnables[] = {
\r
4175 { 'P', DP_Pawn, N_("Pawn") },
\r
4176 { 'N', DP_Knight, N_("Knight") },
\r
4177 { 'B', DP_Bishop, N_("Bishop") },
\r
4178 { 'R', DP_Rook, N_("Rook") },
\r
4179 { 'Q', DP_Queen, N_("Queen") },
\r
4183 SetupDropMenu(HMENU hmenu)
\r
4185 int i, count, enable;
\r
4187 extern char white_holding[], black_holding[];
\r
4188 char item[MSG_SIZ];
\r
4190 for (i=0; i<sizeof(dropEnables)/sizeof(DropEnable); i++) {
\r
4191 p = strchr(gameMode == IcsPlayingWhite ? white_holding : black_holding,
\r
4192 dropEnables[i].piece);
\r
4194 while (p && *p++ == dropEnables[i].piece) count++;
\r
4195 snprintf(item, MSG_SIZ, "%s %d", T_(dropEnables[i].name), count);
\r
4196 enable = count > 0 || !appData.testLegality
\r
4197 /*!!temp:*/ || (gameInfo.variant == VariantCrazyhouse
\r
4198 && !appData.icsActive);
\r
4199 ModifyMenu(hmenu, dropEnables[i].command,
\r
4200 MF_BYCOMMAND | (enable ? MF_ENABLED : MF_GRAYED) | MF_STRING,
\r
4201 dropEnables[i].command, item);
\r
4205 void DragPieceBegin(int x, int y, Boolean instantly)
\r
4207 dragInfo.lastpos.x = boardRect.left + x;
\r
4208 dragInfo.lastpos.y = boardRect.top + y;
\r
4209 if(instantly) dragInfo.pos = dragInfo.lastpos;
\r
4210 dragInfo.from.x = fromX;
\r
4211 dragInfo.from.y = fromY;
\r
4212 dragInfo.piece = boards[currentMove][fromY][fromX];
\r
4213 dragInfo.start = dragInfo.from;
\r
4214 SetCapture(hwndMain);
\r
4217 void DragPieceEnd(int x, int y)
\r
4220 dragInfo.start.x = dragInfo.start.y = -1;
\r
4221 dragInfo.from = dragInfo.start;
\r
4222 dragInfo.pos = dragInfo.lastpos = dragInfo.start;
\r
4225 void ChangeDragPiece(ChessSquare piece)
\r
4227 dragInfo.piece = piece;
\r
4230 /* Event handler for mouse messages */
\r
4232 MouseEvent(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
\r
4236 static int recursive = 0;
\r
4238 BOOLEAN forceFullRepaint = IsFullRepaintPreferrable(); /* [AS] */
\r
4241 if (message == WM_MBUTTONUP) {
\r
4242 /* Hideous kludge to fool TrackPopupMenu into paying attention
\r
4243 to the middle button: we simulate pressing the left button too!
\r
4245 PostMessage(hwnd, WM_LBUTTONDOWN, wParam, lParam);
\r
4246 PostMessage(hwnd, WM_LBUTTONUP, wParam, lParam);
\r
4252 pt.x = LOWORD(lParam);
\r
4253 pt.y = HIWORD(lParam);
\r
4254 x = EventToSquare(pt.x - boardRect.left, BOARD_WIDTH);
\r
4255 y = EventToSquare(pt.y - boardRect.top, BOARD_HEIGHT);
\r
4256 if (!flipView && y >= 0) {
\r
4257 y = BOARD_HEIGHT - 1 - y;
\r
4259 if (flipView && x >= 0) {
\r
4260 x = BOARD_WIDTH - 1 - x;
\r
4263 shiftKey = GetKeyState(VK_SHIFT) < 0; // [HGM] remember last shift status
\r
4264 controlKey = GetKeyState(VK_CONTROL) < 0; // [HGM] remember last shift status
\r
4266 switch (message) {
\r
4267 case WM_LBUTTONDOWN:
\r
4268 if (PtInRect((LPRECT) &whiteRect, pt)) {
\r
4269 ClockClick(flipClock); break;
\r
4270 } else if (PtInRect((LPRECT) &blackRect, pt)) {
\r
4271 ClockClick(!flipClock); break;
\r
4273 dragInfo.start.x = dragInfo.start.y = -1;
\r
4274 dragInfo.from = dragInfo.start;
\r
4275 if(fromX == -1 && frozen) { // not sure where this is for
\r
4276 fromX = fromY = -1;
\r
4277 DrawPosition(forceFullRepaint || FALSE, NULL); /* [AS] */
\r
4280 LeftClick(Press, pt.x - boardRect.left, pt.y - boardRect.top);
\r
4281 DrawPosition(TRUE, NULL);
\r
4284 case WM_LBUTTONUP:
\r
4285 LeftClick(Release, pt.x - boardRect.left, pt.y - boardRect.top);
\r
4286 DrawPosition(TRUE, NULL);
\r
4289 case WM_MOUSEMOVE:
\r
4290 if(SeekGraphClick(Press, pt.x - boardRect.left, pt.y - boardRect.top, 1)) break;
\r
4291 if(PromoScroll(pt.x - boardRect.left, pt.y - boardRect.top)) break;
\r
4292 MovePV(pt.x - boardRect.left, pt.y - boardRect.top, boardRect.bottom - boardRect.top);
\r
4293 if ((appData.animateDragging || appData.highlightDragging)
\r
4294 && (wParam & MK_LBUTTON)
\r
4295 && dragInfo.from.x >= 0)
\r
4297 BOOL full_repaint = FALSE;
\r
4299 if (appData.animateDragging) {
\r
4300 dragInfo.pos = pt;
\r
4302 if (appData.highlightDragging) {
\r
4303 SetHighlights(fromX, fromY, x, y);
\r
4304 if( IsDrawArrowEnabled() && (x < 0 || x >= BOARD_WIDTH || y < 0 || y >= BOARD_HEIGHT) ) {
\r
4305 full_repaint = TRUE;
\r
4309 DrawPosition( full_repaint, NULL);
\r
4311 dragInfo.lastpos = dragInfo.pos;
\r
4315 case WM_MOUSEWHEEL: // [DM]
\r
4316 { static int lastDir = 0; // [HGM] build in some hysteresis to avoid spurious events
\r
4317 /* Mouse Wheel is being rolled forward
\r
4318 * Play moves forward
\r
4320 if((short)HIWORD(wParam) < 0 && currentMove < forwardMostMove)
\r
4321 { if(lastDir == 1) ForwardEvent(); else lastDir = 1; } // [HGM] suppress first event in direction
\r
4322 /* Mouse Wheel is being rolled backward
\r
4323 * Play moves backward
\r
4325 if((short)HIWORD(wParam) > 0 && currentMove > backwardMostMove)
\r
4326 { if(lastDir == -1) BackwardEvent(); else lastDir = -1; }
\r
4330 case WM_MBUTTONUP:
\r
4331 case WM_RBUTTONUP:
\r
4333 RightClick(Release, pt.x - boardRect.left, pt.y - boardRect.top, &fromX, &fromY);
\r
4336 case WM_MBUTTONDOWN:
\r
4337 case WM_RBUTTONDOWN:
\r
4340 fromX = fromY = -1;
\r
4341 dragInfo.pos.x = dragInfo.pos.y = -1;
\r
4342 dragInfo.start.x = dragInfo.start.y = -1;
\r
4343 dragInfo.from = dragInfo.start;
\r
4344 dragInfo.lastpos = dragInfo.pos;
\r
4345 if (appData.highlightDragging) {
\r
4346 ClearHighlights();
\r
4349 /* [HGM] right mouse button in clock area edit-game mode ups clock */
\r
4350 if (PtInRect((LPRECT) &whiteRect, pt)) {
\r
4351 if (GetKeyState(VK_SHIFT) < 0) AdjustClock(flipClock, 1);
\r
4352 } else if (PtInRect((LPRECT) &blackRect, pt)) {
\r
4353 if (GetKeyState(VK_SHIFT) < 0) AdjustClock(!flipClock, 1);
\r
4357 DrawPosition(TRUE, NULL);
\r
4359 menuNr = RightClick(Press, pt.x - boardRect.left, pt.y - boardRect.top, &fromX, &fromY);
\r
4362 if (message == WM_MBUTTONDOWN) {
\r
4363 buttonCount = 3; /* even if system didn't think so */
\r
4364 if (wParam & MK_SHIFT)
\r
4365 MenuPopup(hwnd, pt, LoadMenu(hInst, "BlackPieceMenu"), -1);
\r
4367 MenuPopup(hwnd, pt, LoadMenu(hInst, "WhitePieceMenu"), -1);
\r
4368 } else { /* message == WM_RBUTTONDOWN */
\r
4369 /* Just have one menu, on the right button. Windows users don't
\r
4370 think to try the middle one, and sometimes other software steals
\r
4371 it, or it doesn't really exist. */
\r
4372 if(gameInfo.variant != VariantShogi)
\r
4373 MenuPopup(hwnd, pt, LoadMenu(hInst, "PieceMenu"), -1);
\r
4375 MenuPopup(hwnd, pt, LoadMenu(hInst, "ShogiPieceMenu"), -1);
\r
4379 SetCapture(hwndMain);
\r
4382 hmenu = LoadMenu(hInst, "DropPieceMenu");
\r
4383 SetupDropMenu(hmenu);
\r
4384 MenuPopup(hwnd, pt, hmenu, -1);
\r
4394 /* Preprocess messages for buttons in main window */
\r
4396 ButtonProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
\r
4398 int id = GetWindowLongPtr(hwnd, GWLP_ID);
\r
4401 for (i=0; i<N_BUTTONS; i++) {
\r
4402 if (buttonDesc[i].id == id) break;
\r
4404 if (i == N_BUTTONS) return 0;
\r
4405 switch (message) {
\r
4410 dir = (wParam == VK_LEFT) ? -1 : 1;
\r
4411 SetFocus(buttonDesc[(i + dir + N_BUTTONS) % N_BUTTONS].hwnd);
\r
4418 SendMessage(hwndMain, WM_COMMAND, MAKEWPARAM(buttonDesc[i].id, 0), 0);
\r
4421 if (appData.icsActive && (isalpha((char)wParam) || wParam == '0')) {
\r
4422 // [HGM] movenum: only letters or leading zero should go to ICS input
\r
4423 HWND h = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
4424 if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);
\r
4426 SendMessage(h, WM_CHAR, wParam, lParam);
\r
4428 } else if (isalpha((char)wParam) || isdigit((char)wParam)){
\r
4429 TypeInEvent((char)wParam);
\r
4435 return CallWindowProc(buttonDesc[i].wndproc, hwnd, message, wParam, lParam);
\r
4438 /* Process messages for Promotion dialog box */
\r
4440 Promotion(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
4444 switch (message) {
\r
4445 case WM_INITDIALOG: /* message: initialize dialog box */
\r
4446 /* Center the dialog over the application window */
\r
4447 CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));
\r
4448 Translate(hDlg, DLG_PromotionKing);
\r
4449 ShowWindow(GetDlgItem(hDlg, PB_King),
\r
4450 (!appData.testLegality || gameInfo.variant == VariantSuicide ||
\r
4451 gameInfo.variant == VariantSpartan && !WhiteOnMove(currentMove) ||
\r
4452 gameInfo.variant == VariantGiveaway || gameInfo.variant == VariantSuper ) ?
\r
4453 SW_SHOW : SW_HIDE);
\r
4454 /* [HGM] Only allow C & A promotions if these pieces are defined */
\r
4455 ShowWindow(GetDlgItem(hDlg, PB_Archbishop),
\r
4456 ((PieceToChar(WhiteAngel) >= 'A' && WhiteOnMove(currentMove) &&
\r
4457 PieceToChar(WhiteAngel) != '~') ||
\r
4458 (PieceToChar(BlackAngel) >= 'A' && !WhiteOnMove(currentMove) &&
\r
4459 PieceToChar(BlackAngel) != '~') ) ?
\r
4460 SW_SHOW : SW_HIDE);
\r
4461 ShowWindow(GetDlgItem(hDlg, PB_Chancellor),
\r
4462 ((PieceToChar(WhiteMarshall) >= 'A' && WhiteOnMove(currentMove) &&
\r
4463 PieceToChar(WhiteMarshall) != '~') ||
\r
4464 (PieceToChar(BlackMarshall) >= 'A' && !WhiteOnMove(currentMove) &&
\r
4465 PieceToChar(BlackMarshall) != '~') ) ?
\r
4466 SW_SHOW : SW_HIDE);
\r
4467 /* [HGM] Hide B & R button in Shogi, use Q as promote, N as defer */
\r
4468 ShowWindow(GetDlgItem(hDlg, PB_Rook),
\r
4469 gameInfo.variant != VariantShogi ?
\r
4470 SW_SHOW : SW_HIDE);
\r
4471 ShowWindow(GetDlgItem(hDlg, PB_Bishop),
\r
4472 gameInfo.variant != VariantShogi ?
\r
4473 SW_SHOW : SW_HIDE);
\r
4474 if(gameInfo.variant == VariantShogi) {
\r
4475 SetDlgItemText(hDlg, PB_Queen, "YES");
\r
4476 SetDlgItemText(hDlg, PB_Knight, "NO");
\r
4477 SetWindowText(hDlg, "Promote?");
\r
4479 ShowWindow(GetDlgItem(hDlg, IDC_Centaur),
\r
4480 gameInfo.variant == VariantSuper ?
\r
4481 SW_SHOW : SW_HIDE);
\r
4484 case WM_COMMAND: /* message: received a command */
\r
4485 switch (LOWORD(wParam)) {
\r
4487 EndDialog(hDlg, TRUE); /* Exit the dialog */
\r
4488 ClearHighlights();
\r
4489 DrawPosition(FALSE, NULL);
\r
4492 promoChar = gameInfo.variant == VariantSuper ? PieceToChar(BlackSilver) : PieceToChar(BlackKing);
\r
4495 promoChar = gameInfo.variant == VariantShogi ? '+' : ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteQueen : BlackQueen));
\r
4498 promoChar = ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteRook : BlackRook));
\r
4499 if(gameInfo.variant == VariantSpartan && !WhiteOnMove(currentMove)) promoChar = PieceToChar(BlackDragon);
\r
4502 promoChar = ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteBishop : BlackBishop));
\r
4503 if(gameInfo.variant == VariantSpartan && !WhiteOnMove(currentMove)) promoChar = PieceToChar(BlackAlfil);
\r
4505 case PB_Chancellor:
\r
4506 promoChar = ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteMarshall : BlackMarshall));
\r
4508 case PB_Archbishop:
\r
4509 promoChar = ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteAngel : BlackAngel));
\r
4512 promoChar = gameInfo.variant == VariantShogi ? '=' : PieceToChar(WhiteOnMove(currentMove) ? WhiteKnight : BlackKnight);
\r
4517 if(promoChar == '.') return FALSE; // invalid piece chosen
\r
4518 EndDialog(hDlg, TRUE); /* Exit the dialog */
\r
4519 UserMoveEvent(fromX, fromY, toX, toY, promoChar);
\r
4520 fromX = fromY = -1;
\r
4521 if (!appData.highlightLastMove) {
\r
4522 ClearHighlights();
\r
4523 DrawPosition(FALSE, NULL);
\r
4530 /* Pop up promotion dialog */
\r
4532 PromotionPopup(HWND hwnd)
\r
4536 lpProc = MakeProcInstance((FARPROC)Promotion, hInst);
\r
4537 DialogBox(hInst, MAKEINTRESOURCE(DLG_PromotionKing),
\r
4538 hwnd, (DLGPROC)lpProc);
\r
4539 FreeProcInstance(lpProc);
\r
4545 DrawPosition(TRUE, NULL);
\r
4546 PromotionPopup(hwndMain);
\r
4550 LoadGameDialog(HWND hwnd, char* title)
\r
4554 char fileTitle[MSG_SIZ];
\r
4555 f = OpenFileDialog(hwnd, "rb", "",
\r
4556 appData.oldSaveStyle ? "gam" : "pgn",
\r
4558 title, &number, fileTitle, NULL);
\r
4560 cmailMsgLoaded = FALSE;
\r
4561 if (number == 0) {
\r
4562 int error = GameListBuild(f);
\r
4564 DisplayError(_("Cannot build game list"), error);
\r
4565 } else if (!ListEmpty(&gameList) &&
\r
4566 ((ListGame *) gameList.tailPred)->number > 1) {
\r
4567 GameListPopUp(f, fileTitle);
\r
4570 GameListDestroy();
\r
4573 LoadGame(f, number, fileTitle, FALSE);
\r
4577 int get_term_width()
\r
4582 HFONT hfont, hold_font;
\r
4587 hText = GetDlgItem(hwndConsole, OPT_ConsoleText);
\r
4591 // get the text metrics
\r
4592 hdc = GetDC(hText);
\r
4593 lf = font[boardSize][CONSOLE_FONT]->lf;
\r
4594 if (consoleCF.dwEffects & CFE_BOLD)
\r
4595 lf.lfWeight = FW_BOLD;
\r
4596 if (consoleCF.dwEffects & CFE_ITALIC)
\r
4597 lf.lfItalic = TRUE;
\r
4598 if (consoleCF.dwEffects & CFE_STRIKEOUT)
\r
4599 lf.lfStrikeOut = TRUE;
\r
4600 if (consoleCF.dwEffects & CFE_UNDERLINE)
\r
4601 lf.lfUnderline = TRUE;
\r
4602 hfont = CreateFontIndirect(&lf);
\r
4603 hold_font = SelectObject(hdc, hfont);
\r
4604 GetTextMetrics(hdc, &tm);
\r
4605 SelectObject(hdc, hold_font);
\r
4606 DeleteObject(hfont);
\r
4607 ReleaseDC(hText, hdc);
\r
4609 // get the rectangle
\r
4610 SendMessage(hText, EM_GETRECT, 0, (LPARAM)&rc);
\r
4612 return (rc.right-rc.left) / tm.tmAveCharWidth;
\r
4615 void UpdateICSWidth(HWND hText)
\r
4617 LONG old_width, new_width;
\r
4619 new_width = get_term_width(hText, FALSE);
\r
4620 old_width = GetWindowLongPtr(hText, GWLP_USERDATA);
\r
4621 if (new_width != old_width)
\r
4623 ics_update_width(new_width);
\r
4624 SetWindowLongPtr(hText, GWLP_USERDATA, new_width);
\r
4629 ChangedConsoleFont()
\r
4632 CHARRANGE tmpsel, sel;
\r
4633 MyFont *f = font[boardSize][CONSOLE_FONT];
\r
4634 HWND hText = GetDlgItem(hwndConsole, OPT_ConsoleText);
\r
4635 HWND hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
4638 cfmt.cbSize = sizeof(CHARFORMAT);
\r
4639 cfmt.dwMask = CFM_FACE|CFM_SIZE|CFM_CHARSET;
\r
4640 safeStrCpy(cfmt.szFaceName, font[boardSize][CONSOLE_FONT]->mfp.faceName,
\r
4641 sizeof(cfmt.szFaceName)/sizeof(cfmt.szFaceName[0]) );
\r
4642 /* yHeight is expressed in twips. A twip is 1/20 of a font's point
\r
4643 * size. This was undocumented in the version of MSVC++ that I had
\r
4644 * when I wrote the code, but is apparently documented now.
\r
4646 cfmt.yHeight = (int)(f->mfp.pointSize * 20.0 + 0.5);
\r
4647 cfmt.bCharSet = f->lf.lfCharSet;
\r
4648 cfmt.bPitchAndFamily = f->lf.lfPitchAndFamily;
\r
4649 SendMessage(hText, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &cfmt);
\r
4650 SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &cfmt);
\r
4651 /* Why are the following seemingly needed too? */
\r
4652 SendMessage(hText, EM_SETCHARFORMAT, SCF_DEFAULT, (LPARAM) &cfmt);
\r
4653 SendMessage(hInput, EM_SETCHARFORMAT, SCF_DEFAULT, (LPARAM) &cfmt);
\r
4654 SendMessage(hText, EM_EXGETSEL, 0, (LPARAM)&sel);
\r
4656 tmpsel.cpMax = -1; /*999999?*/
\r
4657 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&tmpsel);
\r
4658 SendMessage(hText, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cfmt);
\r
4659 /* Trying putting this here too. It still seems to tickle a RichEdit
\r
4660 * bug: sometimes RichEdit indents the first line of a paragraph too.
\r
4662 paraf.cbSize = sizeof(paraf);
\r
4663 paraf.dwMask = PFM_OFFSET | PFM_STARTINDENT;
\r
4664 paraf.dxStartIndent = 0;
\r
4665 paraf.dxOffset = WRAP_INDENT;
\r
4666 SendMessage(hText, EM_SETPARAFORMAT, 0, (LPARAM) ¶f);
\r
4667 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
4668 UpdateICSWidth(hText);
\r
4671 /*---------------------------------------------------------------------------*\
\r
4673 * Window Proc for main window
\r
4675 \*---------------------------------------------------------------------------*/
\r
4677 /* Process messages for main window, etc. */
\r
4679 WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
\r
4682 int wmId, wmEvent;
\r
4686 char fileTitle[MSG_SIZ];
\r
4687 static SnapData sd;
\r
4688 static int peek=0;
\r
4690 switch (message) {
\r
4692 case WM_PAINT: /* message: repaint portion of window */
\r
4696 case WM_ERASEBKGND:
\r
4697 if (IsIconic(hwnd)) {
\r
4698 /* Cheat; change the message */
\r
4699 return (DefWindowProc(hwnd, WM_ICONERASEBKGND, wParam, lParam));
\r
4701 return (DefWindowProc(hwnd, message, wParam, lParam));
\r
4705 case WM_LBUTTONDOWN:
\r
4706 case WM_MBUTTONDOWN:
\r
4707 case WM_RBUTTONDOWN:
\r
4708 case WM_LBUTTONUP:
\r
4709 case WM_MBUTTONUP:
\r
4710 case WM_RBUTTONUP:
\r
4711 case WM_MOUSEMOVE:
\r
4712 case WM_MOUSEWHEEL:
\r
4713 MouseEvent(hwnd, message, wParam, lParam);
\r
4717 if((char)wParam == '\b') {
\r
4718 ForwardEvent(); peek = 0;
\r
4721 JAWS_KBUP_NAVIGATION
\r
4726 if((char)wParam == '\b') {
\r
4727 if(!peek) BackwardEvent(), peek = 1;
\r
4730 JAWS_KBDOWN_NAVIGATION
\r
4736 JAWS_ALT_INTERCEPT
\r
4738 if (appData.icsActive && ((char)wParam == '\r' || (char)wParam > ' ' && !((char)wParam >= '1' && (char)wParam <= '9'))) {
\r
4739 // [HGM] movenum: for non-zero digits we always do type-in dialog
\r
4740 HWND h = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
4741 if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);
\r
4743 SendMessage(h, message, wParam, lParam);
\r
4744 } else if(lParam != KF_REPEAT) {
\r
4745 if (isalpha((char)wParam) || isdigit((char)wParam)) {
\r
4746 TypeInEvent((char)wParam);
\r
4747 } else if((char)wParam == 003) CopyGameToClipboard();
\r
4748 else if((char)wParam == 026) PasteGameOrFENFromClipboard();
\r
4753 case WM_PALETTECHANGED:
\r
4754 if (hwnd != (HWND)wParam && !appData.monoMode) {
\r
4756 HDC hdc = GetDC(hwndMain);
\r
4757 SelectPalette(hdc, hPal, TRUE);
\r
4758 nnew = RealizePalette(hdc);
\r
4760 paletteChanged = TRUE;
\r
4762 InvalidateRect(hwnd, &boardRect, FALSE);
\r
4764 ReleaseDC(hwnd, hdc);
\r
4768 case WM_QUERYNEWPALETTE:
\r
4769 if (!appData.monoMode /*&& paletteChanged*/) {
\r
4771 HDC hdc = GetDC(hwndMain);
\r
4772 paletteChanged = FALSE;
\r
4773 SelectPalette(hdc, hPal, FALSE);
\r
4774 nnew = RealizePalette(hdc);
\r
4776 InvalidateRect(hwnd, &boardRect, FALSE);
\r
4778 ReleaseDC(hwnd, hdc);
\r
4783 case WM_COMMAND: /* message: command from application menu */
\r
4784 wmId = LOWORD(wParam);
\r
4785 wmEvent = HIWORD(wParam);
\r
4790 SAY("new game enter a move to play against the computer with white");
\r
4793 case IDM_NewGameFRC:
\r
4794 if( NewGameFRC() == 0 ) {
\r
4799 case IDM_NewVariant:
\r
4800 NewVariantPopup(hwnd);
\r
4803 case IDM_LoadGame:
\r
4804 LoadGameDialog(hwnd, _("Load Game from File"));
\r
4807 case IDM_LoadNextGame:
\r
4811 case IDM_LoadPrevGame:
\r
4815 case IDM_ReloadGame:
\r
4819 case IDM_LoadPosition:
\r
4820 if (gameMode == AnalyzeMode || gameMode == AnalyzeFile) {
\r
4821 Reset(FALSE, TRUE);
\r
4824 f = OpenFileDialog(hwnd, "rb", "",
\r
4825 appData.oldSaveStyle ? "pos" : "fen",
\r
4827 _("Load Position from File"), &number, fileTitle, NULL);
\r
4829 LoadPosition(f, number, fileTitle);
\r
4833 case IDM_LoadNextPosition:
\r
4834 ReloadPosition(1);
\r
4837 case IDM_LoadPrevPosition:
\r
4838 ReloadPosition(-1);
\r
4841 case IDM_ReloadPosition:
\r
4842 ReloadPosition(0);
\r
4845 case IDM_SaveGame:
\r
4846 defName = DefaultFileName(appData.oldSaveStyle ? "gam" : "pgn");
\r
4847 f = OpenFileDialog(hwnd, "a", defName,
\r
4848 appData.oldSaveStyle ? "gam" : "pgn",
\r
4850 _("Save Game to File"), NULL, fileTitle, NULL);
\r
4852 SaveGame(f, 0, "");
\r
4856 case IDM_SavePosition:
\r
4857 defName = DefaultFileName(appData.oldSaveStyle ? "pos" : "fen");
\r
4858 f = OpenFileDialog(hwnd, "a", defName,
\r
4859 appData.oldSaveStyle ? "pos" : "fen",
\r
4861 _("Save Position to File"), NULL, fileTitle, NULL);
\r
4863 SavePosition(f, 0, "");
\r
4867 case IDM_SaveDiagram:
\r
4868 defName = "diagram";
\r
4869 f = OpenFileDialog(hwnd, "wb", defName,
\r
4872 _("Save Diagram to File"), NULL, fileTitle, NULL);
\r
4878 case IDM_CreateBook:
\r
4879 CreateBookEvent();
\r
4882 case IDM_CopyGame:
\r
4883 CopyGameToClipboard();
\r
4886 case IDM_PasteGame:
\r
4887 PasteGameFromClipboard();
\r
4890 case IDM_CopyGameListToClipboard:
\r
4891 CopyGameListToClipboard();
\r
4894 /* [AS] Autodetect FEN or PGN data */
\r
4895 case IDM_PasteAny:
\r
4896 PasteGameOrFENFromClipboard();
\r
4899 /* [AS] Move history */
\r
4900 case IDM_ShowMoveHistory:
\r
4901 if( MoveHistoryIsUp() ) {
\r
4902 MoveHistoryPopDown();
\r
4905 MoveHistoryPopUp();
\r
4909 /* [AS] Eval graph */
\r
4910 case IDM_ShowEvalGraph:
\r
4911 if( EvalGraphIsUp() ) {
\r
4912 EvalGraphPopDown();
\r
4916 SetFocus(hwndMain);
\r
4920 /* [AS] Engine output */
\r
4921 case IDM_ShowEngineOutput:
\r
4922 if( EngineOutputIsUp() ) {
\r
4923 EngineOutputPopDown();
\r
4926 EngineOutputPopUp();
\r
4930 /* [AS] User adjudication */
\r
4931 case IDM_UserAdjudication_White:
\r
4932 UserAdjudicationEvent( +1 );
\r
4935 case IDM_UserAdjudication_Black:
\r
4936 UserAdjudicationEvent( -1 );
\r
4939 case IDM_UserAdjudication_Draw:
\r
4940 UserAdjudicationEvent( 0 );
\r
4943 /* [AS] Game list options dialog */
\r
4944 case IDM_GameListOptions:
\r
4945 GameListOptions();
\r
4952 case IDM_CopyPosition:
\r
4953 CopyFENToClipboard();
\r
4956 case IDM_PastePosition:
\r
4957 PasteFENFromClipboard();
\r
4960 case IDM_MailMove:
\r
4964 case IDM_ReloadCMailMsg:
\r
4965 Reset(TRUE, TRUE);
\r
4966 ReloadCmailMsgEvent(FALSE);
\r
4969 case IDM_Minimize:
\r
4970 ShowWindow(hwnd, SW_MINIMIZE);
\r
4977 case IDM_MachineWhite:
\r
4978 MachineWhiteEvent();
\r
4980 * refresh the tags dialog only if it's visible
\r
4982 if (gameMode == MachinePlaysWhite && IsWindowVisible(editTagsDialog)) {
\r
4984 tags = PGNTags(&gameInfo);
\r
4985 TagsPopUp(tags, CmailMsg());
\r
4988 SAY("computer starts playing white");
\r
4991 case IDM_MachineBlack:
\r
4992 MachineBlackEvent();
\r
4994 * refresh the tags dialog only if it's visible
\r
4996 if (gameMode == MachinePlaysBlack && IsWindowVisible(editTagsDialog)) {
\r
4998 tags = PGNTags(&gameInfo);
\r
4999 TagsPopUp(tags, CmailMsg());
\r
5002 SAY("computer starts playing black");
\r
5005 case IDM_Match: // [HGM] match: flows into next case, after setting Match Mode and nr of Games
\r
5006 MatchEvent(2); // distinguish from command-line-triggered case (matchMode=1)
\r
5009 case IDM_TwoMachines:
\r
5010 TwoMachinesEvent();
\r
5012 * refresh the tags dialog only if it's visible
\r
5014 if (gameMode == TwoMachinesPlay && IsWindowVisible(editTagsDialog)) {
\r
5016 tags = PGNTags(&gameInfo);
\r
5017 TagsPopUp(tags, CmailMsg());
\r
5020 SAY("computer starts playing both sides");
\r
5023 case IDM_AnalysisMode:
\r
5024 if(AnalyzeModeEvent()) {
\r
5025 SAY("analyzing current position");
\r
5029 case IDM_AnalyzeFile:
\r
5030 AnalyzeFileEvent();
\r
5033 case IDM_IcsClient:
\r
5037 case IDM_EditGame:
\r
5038 case IDM_EditGame2:
\r
5043 case IDM_EditPosition:
\r
5044 case IDM_EditPosition2:
\r
5045 EditPositionEvent();
\r
5046 SAY("enter a FEN string or setup a position on the board using the control R pop up menu");
\r
5049 case IDM_Training:
\r
5053 case IDM_ShowGameList:
\r
5054 ShowGameListProc();
\r
5057 case IDM_EditProgs1:
\r
5058 EditTagsPopUp(firstChessProgramNames, &firstChessProgramNames);
\r
5061 case IDM_LoadProg1:
\r
5062 LoadEnginePopUp(hwndMain, 0);
\r
5065 case IDM_LoadProg2:
\r
5066 LoadEnginePopUp(hwndMain, 1);
\r
5069 case IDM_EditServers:
\r
5070 EditTagsPopUp(icsNames, &icsNames);
\r
5073 case IDM_EditTags:
\r
5078 case IDM_EditBook:
\r
5082 case IDM_EditComment:
\r
5084 if (commentUp && editComment) {
\r
5087 EditCommentEvent();
\r
5107 case IDM_CallFlag:
\r
5127 case IDM_StopObserving:
\r
5128 StopObservingEvent();
\r
5131 case IDM_StopExamining:
\r
5132 StopExaminingEvent();
\r
5136 UploadGameEvent();
\r
5139 case IDM_TypeInMove:
\r
5140 TypeInEvent('\000');
\r
5143 case IDM_TypeInName:
\r
5144 PopUpNameDialog('\000');
\r
5147 case IDM_Backward:
\r
5149 SetFocus(hwndMain);
\r
5156 SetFocus(hwndMain);
\r
5161 SetFocus(hwndMain);
\r
5166 SetFocus(hwndMain);
\r
5169 case OPT_GameListNext: // [HGM] forward these two accelerators to Game List
\r
5170 case OPT_GameListPrev:
\r
5171 if(gameListDialog) SendMessage(gameListDialog, WM_COMMAND, wmId, 0);
\r
5175 RevertEvent(FALSE);
\r
5178 case IDM_Annotate: // [HGM] vari: revert with annotation
\r
5179 RevertEvent(TRUE);
\r
5182 case IDM_TruncateGame:
\r
5183 TruncateGameEvent();
\r
5190 case IDM_RetractMove:
\r
5191 RetractMoveEvent();
\r
5194 case IDM_FlipView:
\r
5195 flipView = !flipView;
\r
5196 DrawPosition(FALSE, NULL);
\r
5199 case IDM_FlipClock:
\r
5200 flipClock = !flipClock;
\r
5201 DisplayBothClocks();
\r
5205 case IDM_MuteSounds:
\r
5206 mute = !mute; // [HGM] mute: keep track of global muting variable
\r
5207 CheckMenuItem(GetMenu(hwndMain),IDM_MuteSounds,
\r
5208 MF_BYCOMMAND|(mute?MF_CHECKED:MF_UNCHECKED));
\r
5211 case IDM_GeneralOptions:
\r
5212 GeneralOptionsPopup(hwnd);
\r
5213 DrawPosition(TRUE, NULL);
\r
5216 case IDM_BoardOptions:
\r
5217 BoardOptionsPopup(hwnd);
\r
5220 case IDM_ThemeOptions:
\r
5221 ThemeOptionsPopup(hwnd);
\r
5224 case IDM_EnginePlayOptions:
\r
5225 EnginePlayOptionsPopup(hwnd);
\r
5228 case IDM_Engine1Options:
\r
5229 EngineOptionsPopup(hwnd, &first);
\r
5232 case IDM_Engine2Options:
\r
5234 if(WaitForEngine(&second, SettingsMenuIfReady)) break;
\r
5235 EngineOptionsPopup(hwnd, &second);
\r
5238 case IDM_OptionsUCI:
\r
5239 UciOptionsPopup(hwnd);
\r
5243 TourneyPopup(hwnd);
\r
5246 case IDM_IcsOptions:
\r
5247 IcsOptionsPopup(hwnd);
\r
5251 FontsOptionsPopup(hwnd);
\r
5255 SoundOptionsPopup(hwnd);
\r
5258 case IDM_CommPort:
\r
5259 CommPortOptionsPopup(hwnd);
\r
5262 case IDM_LoadOptions:
\r
5263 LoadOptionsPopup(hwnd);
\r
5266 case IDM_SaveOptions:
\r
5267 SaveOptionsPopup(hwnd);
\r
5270 case IDM_TimeControl:
\r
5271 TimeControlOptionsPopup(hwnd);
\r
5274 case IDM_SaveSettings:
\r
5275 SaveSettings(settingsFileName);
\r
5278 case IDM_SaveSettingsOnExit:
\r
5279 saveSettingsOnExit = !saveSettingsOnExit;
\r
5280 (void) CheckMenuItem(GetMenu(hwndMain), IDM_SaveSettingsOnExit,
\r
5281 MF_BYCOMMAND|(saveSettingsOnExit ?
\r
5282 MF_CHECKED : MF_UNCHECKED));
\r
5293 case IDM_AboutGame:
\r
5298 appData.debugMode = !appData.debugMode;
\r
5299 if (appData.debugMode) {
\r
5300 char dir[MSG_SIZ];
\r
5301 GetCurrentDirectory(MSG_SIZ, dir);
\r
5302 SetCurrentDirectory(installDir);
\r
5303 debugFP = fopen(appData.nameOfDebugFile, "w");
\r
5304 SetCurrentDirectory(dir);
\r
5305 setbuf(debugFP, NULL);
\r
5312 case IDM_HELPCONTENTS:
\r
5313 if (!MyHelp (hwnd, "winboard.hlp", HELP_KEY,(DWORD)(LPSTR)"CONTENTS") &&
\r
5314 !HtmlHelp(hwnd, "winboard.chm", 0, 0) ) {
\r
5315 MessageBox (GetFocus(),
\r
5316 _("Unable to activate help"),
\r
5317 szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);
\r
5321 case IDM_HELPSEARCH:
\r
5322 if (!MyHelp (hwnd, "winboard.hlp", HELP_PARTIALKEY, (DWORD)(LPSTR)"") &&
\r
5323 !HtmlHelp(hwnd, "winboard.chm", 0, 0) ) {
\r
5324 MessageBox (GetFocus(),
\r
5325 _("Unable to activate help"),
\r
5326 szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);
\r
5330 case IDM_HELPHELP:
\r
5331 if(!WinHelp(hwnd, (LPSTR)NULL, HELP_HELPONHELP, 0)) {
\r
5332 MessageBox (GetFocus(),
\r
5333 _("Unable to activate help"),
\r
5334 szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);
\r
5339 lpProc = MakeProcInstance((FARPROC)About, hInst);
\r
5341 (gameInfo.event && strcmp(gameInfo.event, "Easter Egg Hunt") == 0) ?
\r
5342 "AboutBox2" : "AboutBox", hwnd, (DLGPROC)lpProc);
\r
5343 FreeProcInstance(lpProc);
\r
5346 case IDM_DirectCommand1:
\r
5347 AskQuestionEvent(_("Direct Command"),
\r
5348 _("Send to chess program:"), "", "1");
\r
5350 case IDM_DirectCommand2:
\r
5351 AskQuestionEvent(_("Direct Command"),
\r
5352 _("Send to second chess program:"), "", "2");
\r
5355 case EP_WhitePawn:
\r
5356 EditPositionMenuEvent(WhitePawn, fromX, fromY);
\r
5357 fromX = fromY = -1;
\r
5360 case EP_WhiteKnight:
\r
5361 EditPositionMenuEvent(WhiteKnight, fromX, fromY);
\r
5362 fromX = fromY = -1;
\r
5365 case EP_WhiteBishop:
\r
5366 EditPositionMenuEvent(WhiteBishop, fromX, fromY);
\r
5367 fromX = fromY = -1;
\r
5370 case EP_WhiteRook:
\r
5371 EditPositionMenuEvent(WhiteRook, fromX, fromY);
\r
5372 fromX = fromY = -1;
\r
5375 case EP_WhiteQueen:
\r
5376 EditPositionMenuEvent(WhiteQueen, fromX, fromY);
\r
5377 fromX = fromY = -1;
\r
5380 case EP_WhiteFerz:
\r
5381 EditPositionMenuEvent(WhiteFerz, fromX, fromY);
\r
5382 fromX = fromY = -1;
\r
5385 case EP_WhiteWazir:
\r
5386 EditPositionMenuEvent(WhiteWazir, fromX, fromY);
\r
5387 fromX = fromY = -1;
\r
5390 case EP_WhiteAlfil:
\r
5391 EditPositionMenuEvent(WhiteAlfil, fromX, fromY);
\r
5392 fromX = fromY = -1;
\r
5395 case EP_WhiteCannon:
\r
5396 EditPositionMenuEvent(WhiteCannon, fromX, fromY);
\r
5397 fromX = fromY = -1;
\r
5400 case EP_WhiteCardinal:
\r
5401 EditPositionMenuEvent(WhiteAngel, fromX, fromY);
\r
5402 fromX = fromY = -1;
\r
5405 case EP_WhiteMarshall:
\r
5406 EditPositionMenuEvent(WhiteMarshall, fromX, fromY);
\r
5407 fromX = fromY = -1;
\r
5410 case EP_WhiteKing:
\r
5411 EditPositionMenuEvent(WhiteKing, fromX, fromY);
\r
5412 fromX = fromY = -1;
\r
5415 case EP_BlackPawn:
\r
5416 EditPositionMenuEvent(BlackPawn, fromX, fromY);
\r
5417 fromX = fromY = -1;
\r
5420 case EP_BlackKnight:
\r
5421 EditPositionMenuEvent(BlackKnight, fromX, fromY);
\r
5422 fromX = fromY = -1;
\r
5425 case EP_BlackBishop:
\r
5426 EditPositionMenuEvent(BlackBishop, fromX, fromY);
\r
5427 fromX = fromY = -1;
\r
5430 case EP_BlackRook:
\r
5431 EditPositionMenuEvent(BlackRook, fromX, fromY);
\r
5432 fromX = fromY = -1;
\r
5435 case EP_BlackQueen:
\r
5436 EditPositionMenuEvent(BlackQueen, fromX, fromY);
\r
5437 fromX = fromY = -1;
\r
5440 case EP_BlackFerz:
\r
5441 EditPositionMenuEvent(BlackFerz, fromX, fromY);
\r
5442 fromX = fromY = -1;
\r
5445 case EP_BlackWazir:
\r
5446 EditPositionMenuEvent(BlackWazir, fromX, fromY);
\r
5447 fromX = fromY = -1;
\r
5450 case EP_BlackAlfil:
\r
5451 EditPositionMenuEvent(BlackAlfil, fromX, fromY);
\r
5452 fromX = fromY = -1;
\r
5455 case EP_BlackCannon:
\r
5456 EditPositionMenuEvent(BlackCannon, fromX, fromY);
\r
5457 fromX = fromY = -1;
\r
5460 case EP_BlackCardinal:
\r
5461 EditPositionMenuEvent(BlackAngel, fromX, fromY);
\r
5462 fromX = fromY = -1;
\r
5465 case EP_BlackMarshall:
\r
5466 EditPositionMenuEvent(BlackMarshall, fromX, fromY);
\r
5467 fromX = fromY = -1;
\r
5470 case EP_BlackKing:
\r
5471 EditPositionMenuEvent(BlackKing, fromX, fromY);
\r
5472 fromX = fromY = -1;
\r
5475 case EP_EmptySquare:
\r
5476 EditPositionMenuEvent(EmptySquare, fromX, fromY);
\r
5477 fromX = fromY = -1;
\r
5480 case EP_ClearBoard:
\r
5481 EditPositionMenuEvent(ClearBoard, fromX, fromY);
\r
5482 fromX = fromY = -1;
\r
5486 EditPositionMenuEvent(WhitePlay, fromX, fromY);
\r
5487 fromX = fromY = -1;
\r
5491 EditPositionMenuEvent(BlackPlay, fromX, fromY);
\r
5492 fromX = fromY = -1;
\r
5496 EditPositionMenuEvent(PromotePiece, fromX, fromY);
\r
5497 fromX = fromY = -1;
\r
5501 EditPositionMenuEvent(DemotePiece, fromX, fromY);
\r
5502 fromX = fromY = -1;
\r
5506 DropMenuEvent(WhitePawn, fromX, fromY);
\r
5507 fromX = fromY = -1;
\r
5511 DropMenuEvent(WhiteKnight, fromX, fromY);
\r
5512 fromX = fromY = -1;
\r
5516 DropMenuEvent(WhiteBishop, fromX, fromY);
\r
5517 fromX = fromY = -1;
\r
5521 DropMenuEvent(WhiteRook, fromX, fromY);
\r
5522 fromX = fromY = -1;
\r
5526 DropMenuEvent(WhiteQueen, fromX, fromY);
\r
5527 fromX = fromY = -1;
\r
5531 barbaric = 0; appData.language = "";
\r
5532 TranslateMenus(0);
\r
5533 CheckMenuItem(GetMenu(hwndMain), lastChecked, MF_BYCOMMAND|MF_UNCHECKED);
\r
5534 CheckMenuItem(GetMenu(hwndMain), IDM_English, MF_BYCOMMAND|MF_CHECKED);
\r
5535 lastChecked = wmId;
\r
5539 if(wmId >= IDM_RecentEngines && wmId < IDM_RecentEngines + appData.recentEngines)
\r
5540 RecentEngineEvent(wmId - IDM_RecentEngines);
\r
5542 if(wmId > IDM_English && wmId < IDM_English+20) {
\r
5543 LoadLanguageFile(languageFile[wmId - IDM_English - 1]);
\r
5544 TranslateMenus(0);
\r
5545 CheckMenuItem(GetMenu(hwndMain), lastChecked, MF_BYCOMMAND|MF_UNCHECKED);
\r
5546 CheckMenuItem(GetMenu(hwndMain), wmId, MF_BYCOMMAND|MF_CHECKED);
\r
5547 lastChecked = wmId;
\r
5550 return (DefWindowProc(hwnd, message, wParam, lParam));
\r
5556 case CLOCK_TIMER_ID:
\r
5557 KillTimer(hwnd, clockTimerEvent); /* Simulate one-shot timer as in X */
\r
5558 clockTimerEvent = 0;
\r
5559 DecrementClocks(); /* call into back end */
\r
5561 case LOAD_GAME_TIMER_ID:
\r
5562 KillTimer(hwnd, loadGameTimerEvent); /* Simulate one-shot timer as in X*/
\r
5563 loadGameTimerEvent = 0;
\r
5564 AutoPlayGameLoop(); /* call into back end */
\r
5566 case ANALYSIS_TIMER_ID:
\r
5567 if ((gameMode == AnalyzeMode || gameMode == AnalyzeFile
\r
5568 || appData.icsEngineAnalyze) && appData.periodicUpdates) {
\r
5569 AnalysisPeriodicEvent(0);
\r
5571 KillTimer(hwnd, analysisTimerEvent);
\r
5572 analysisTimerEvent = 0;
\r
5575 case DELAYED_TIMER_ID:
\r
5576 KillTimer(hwnd, delayedTimerEvent);
\r
5577 delayedTimerEvent = 0;
\r
5578 delayedTimerCallback();
\r
5583 case WM_USER_Input:
\r
5584 InputEvent(hwnd, message, wParam, lParam);
\r
5587 /* [AS] Also move "attached" child windows */
\r
5588 case WM_WINDOWPOSCHANGING:
\r
5590 if( hwnd == hwndMain && appData.useStickyWindows ) {
\r
5591 LPWINDOWPOS lpwp = (LPWINDOWPOS) lParam;
\r
5593 if( ((lpwp->flags & SWP_NOMOVE) == 0) && ((lpwp->flags & SWP_NOSIZE) != 0) ) {
\r
5594 /* Window is moving */
\r
5597 // GetWindowRect( hwnd, &rcMain ); //[HGM] sticky: in XP this returned new position, not old
\r
5598 rcMain.left = wpMain.x; // replace by these 4 lines to reconstruct old rect
\r
5599 rcMain.right = wpMain.x + wpMain.width;
\r
5600 rcMain.top = wpMain.y;
\r
5601 rcMain.bottom = wpMain.y + wpMain.height;
\r
5603 ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, moveHistoryDialog, &wpMoveHistory );
\r
5604 ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, evalGraphDialog, &wpEvalGraph );
\r
5605 ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, engineOutputDialog, &wpEngineOutput );
\r
5606 ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, gameListDialog, &wpGameList );
\r
5607 ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, hwndConsole, &wpConsole );
\r
5608 wpMain.x = lpwp->x;
\r
5609 wpMain.y = lpwp->y;
\r
5614 /* [AS] Snapping */
\r
5615 case WM_ENTERSIZEMOVE:
\r
5616 if(appData.debugMode) { fprintf(debugFP, "size-move\n"); }
\r
5617 if (hwnd == hwndMain) {
\r
5618 doingSizing = TRUE;
\r
5621 return OnEnterSizeMove( &sd, hwnd, wParam, lParam );
\r
5625 if(appData.debugMode) { fprintf(debugFP, "sizing\n"); }
\r
5626 if (hwnd == hwndMain) {
\r
5627 lastSizing = wParam;
\r
5632 if(appData.debugMode) { fprintf(debugFP, "moving\n"); }
\r
5633 return OnMoving( &sd, hwnd, wParam, lParam );
\r
5635 case WM_EXITSIZEMOVE:
\r
5636 if(appData.debugMode) { fprintf(debugFP, "exit size-move, size = %d\n", squareSize); }
\r
5637 if (hwnd == hwndMain) {
\r
5639 doingSizing = FALSE;
\r
5640 InvalidateRect(hwnd, &boardRect, FALSE);
\r
5641 GetClientRect(hwnd, &client);
\r
5642 ResizeBoard(client.right, client.bottom, lastSizing);
\r
5644 if(appData.debugMode) { fprintf(debugFP, "square size = %d\n", squareSize); }
\r
5646 return OnExitSizeMove( &sd, hwnd, wParam, lParam );
\r
5649 case WM_DESTROY: /* message: window being destroyed */
\r
5650 PostQuitMessage(0);
\r
5654 if (hwnd == hwndMain) {
\r
5659 default: /* Passes it on if unprocessed */
\r
5660 return (DefWindowProc(hwnd, message, wParam, lParam));
\r
5665 /*---------------------------------------------------------------------------*\
\r
5667 * Misc utility routines
\r
5669 \*---------------------------------------------------------------------------*/
\r
5672 * Decent random number generator, at least not as bad as Windows
\r
5673 * standard rand, which returns a value in the range 0 to 0x7fff.
\r
5675 unsigned int randstate;
\r
5680 randstate = randstate * 1664525 + 1013904223;
\r
5681 return (int) randstate & 0x7fffffff;
\r
5685 mysrandom(unsigned int seed)
\r
5692 * returns TRUE if user selects a different color, FALSE otherwise
\r
5696 ChangeColor(HWND hwnd, COLORREF *which)
\r
5698 static BOOL firstTime = TRUE;
\r
5699 static DWORD customColors[16];
\r
5701 COLORREF newcolor;
\r
5706 /* Make initial colors in use available as custom colors */
\r
5707 /* Should we put the compiled-in defaults here instead? */
\r
5709 customColors[i++] = lightSquareColor & 0xffffff;
\r
5710 customColors[i++] = darkSquareColor & 0xffffff;
\r
5711 customColors[i++] = whitePieceColor & 0xffffff;
\r
5712 customColors[i++] = blackPieceColor & 0xffffff;
\r
5713 customColors[i++] = highlightSquareColor & 0xffffff;
\r
5714 customColors[i++] = premoveHighlightColor & 0xffffff;
\r
5716 for (ccl = (ColorClass) 0; ccl < NColorClasses && i < 16; ccl++) {
\r
5717 customColors[i++] = textAttribs[ccl].color;
\r
5719 while (i < 16) customColors[i++] = RGB(255, 255, 255);
\r
5720 firstTime = FALSE;
\r
5723 cc.lStructSize = sizeof(cc);
\r
5724 cc.hwndOwner = hwnd;
\r
5725 cc.hInstance = NULL;
\r
5726 cc.rgbResult = (DWORD) (*which & 0xffffff);
\r
5727 cc.lpCustColors = (LPDWORD) customColors;
\r
5728 cc.Flags = CC_RGBINIT|CC_FULLOPEN;
\r
5730 if (!ChooseColor(&cc)) return FALSE;
\r
5732 newcolor = (COLORREF) (0x2000000 | cc.rgbResult);
\r
5733 if (newcolor == *which) return FALSE;
\r
5734 *which = newcolor;
\r
5738 InitDrawingColors();
\r
5739 InvalidateRect(hwnd, &boardRect, FALSE);
\r
5744 MyLoadSound(MySound *ms)
\r
5750 if (ms->data && ms->flag) free(ms->data);
\r
5753 switch (ms->name[0]) {
\r
5759 /* System sound from Control Panel. Don't preload here. */
\r
5763 if (ms->name[1] == NULLCHAR) {
\r
5764 /* "!" alone = silence */
\r
5767 /* Builtin wave resource. Error if not found. */
\r
5768 HANDLE h = FindResource(hInst, ms->name + 1, "WAVE");
\r
5769 if (h == NULL) break;
\r
5770 ms->data = (void *)LoadResource(hInst, h);
\r
5771 ms->flag = 0; // not maloced, so cannot be freed!
\r
5772 if (h == NULL) break;
\r
5777 /* .wav file. Error if not found. */
\r
5778 f = fopen(ms->name, "rb");
\r
5779 if (f == NULL) break;
\r
5780 if (fstat(fileno(f), &st) < 0) break;
\r
5781 ms->data = malloc(st.st_size);
\r
5783 if (fread(ms->data, st.st_size, 1, f) < 1) break;
\r
5789 char buf[MSG_SIZ];
\r
5790 snprintf(buf, MSG_SIZ, _("Error loading sound %s"), ms->name);
\r
5791 DisplayError(buf, GetLastError());
\r
5797 MyPlaySound(MySound *ms)
\r
5799 BOOLEAN ok = FALSE;
\r
5801 if(mute) return TRUE; // [HGM] mute: suppress all sound play when muted
\r
5802 switch (ms->name[0]) {
\r
5804 if(appData.debugMode) fprintf(debugFP, "silence\n");
\r
5809 /* System sound from Control Panel (deprecated feature).
\r
5810 "$" alone or an unset sound name gets default beep (still in use). */
\r
5811 if (ms->name[1]) {
\r
5812 ok = PlaySound(ms->name + 1, NULL, SND_ALIAS|SND_ASYNC);
\r
5814 if (!ok) ok = MessageBeep(MB_OK);
\r
5817 /* Builtin wave resource, or "!" alone for silence */
\r
5818 if (ms->name[1]) {
\r
5819 if (ms->data == NULL) return FALSE;
\r
5820 ok = PlaySound(ms->data, NULL, SND_MEMORY|SND_ASYNC);
\r
5826 /* .wav file. Error if not found. */
\r
5827 if (ms->data == NULL) return FALSE;
\r
5828 ok = PlaySound(ms->data, NULL, SND_MEMORY|SND_ASYNC);
\r
5831 /* Don't print an error: this can happen innocently if the sound driver
\r
5832 is busy; for instance, if another instance of WinBoard is playing
\r
5833 a sound at about the same time. */
\r
5839 OldOpenFileHook(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
5842 OPENFILENAME *ofn;
\r
5843 static UINT *number; /* gross that this is static */
\r
5845 switch (message) {
\r
5846 case WM_INITDIALOG: /* message: initialize dialog box */
\r
5847 /* Center the dialog over the application window */
\r
5848 ofn = (OPENFILENAME *) lParam;
\r
5849 if (ofn->Flags & OFN_ENABLETEMPLATE) {
\r
5850 number = (UINT *) ofn->lCustData;
\r
5851 SendMessage(GetDlgItem(hDlg, edt2), WM_SETTEXT, 0, (LPARAM) "");
\r
5855 CenterWindow (hDlg, GetWindow (hDlg, GW_OWNER));
\r
5856 Translate(hDlg, 1536);
\r
5857 return FALSE; /* Allow for further processing */
\r
5860 if ((LOWORD(wParam) == IDOK) && (number != NULL)) {
\r
5861 *number = GetDlgItemInt(hDlg, OPT_IndexNumberOld, &ok, FALSE);
\r
5863 return FALSE; /* Allow for further processing */
\r
5869 OpenFileHook(HWND hdlg, UINT uiMsg, WPARAM wParam, LPARAM lParam)
\r
5871 static UINT *number;
\r
5872 OPENFILENAME *ofname;
\r
5875 case WM_INITDIALOG:
\r
5876 Translate(hdlg, DLG_IndexNumber);
\r
5877 ofname = (OPENFILENAME *)lParam;
\r
5878 number = (UINT *)(ofname->lCustData);
\r
5881 ofnot = (OFNOTIFY *)lParam;
\r
5882 if (ofnot->hdr.code == CDN_FILEOK) {
\r
5883 *number = GetDlgItemInt(hdlg, OPT_IndexNumber, NULL, FALSE);
\r
5892 OpenFileDialog(HWND hwnd, char *write, char *defName, char *defExt, // [HGM] diag: type of 'write' now string
\r
5893 char *nameFilt, char *dlgTitle, UINT *number,
\r
5894 char fileTitle[MSG_SIZ], char fileName[MSG_SIZ])
\r
5896 OPENFILENAME openFileName;
\r
5897 char buf1[MSG_SIZ];
\r
5900 if (fileName == NULL) fileName = buf1;
\r
5901 if (defName == NULL) {
\r
5902 safeStrCpy(fileName, "*.", 3 );
\r
5903 strcat(fileName, defExt);
\r
5905 safeStrCpy(fileName, defName, MSG_SIZ );
\r
5907 if (fileTitle) safeStrCpy(fileTitle, "", MSG_SIZ );
\r
5908 if (number) *number = 0;
\r
5910 openFileName.lStructSize = sizeof(OPENFILENAME);
\r
5911 openFileName.hwndOwner = hwnd;
\r
5912 openFileName.hInstance = (HANDLE) hInst;
\r
5913 openFileName.lpstrFilter = nameFilt;
\r
5914 openFileName.lpstrCustomFilter = (LPSTR) NULL;
\r
5915 openFileName.nMaxCustFilter = 0L;
\r
5916 openFileName.nFilterIndex = 1L;
\r
5917 openFileName.lpstrFile = fileName;
\r
5918 openFileName.nMaxFile = MSG_SIZ;
\r
5919 openFileName.lpstrFileTitle = fileTitle;
\r
5920 openFileName.nMaxFileTitle = fileTitle ? MSG_SIZ : 0;
\r
5921 openFileName.lpstrInitialDir = NULL;
\r
5922 openFileName.lpstrTitle = dlgTitle;
\r
5923 openFileName.Flags = OFN_PATHMUSTEXIST | OFN_HIDEREADONLY
\r
5924 | (write[0] != 'r' ? 0 : OFN_FILEMUSTEXIST)
\r
5925 | (number ? OFN_ENABLETEMPLATE | OFN_ENABLEHOOK: 0)
\r
5926 | (oldDialog ? 0 : OFN_EXPLORER);
\r
5927 openFileName.nFileOffset = 0;
\r
5928 openFileName.nFileExtension = 0;
\r
5929 openFileName.lpstrDefExt = defExt;
\r
5930 openFileName.lCustData = (LONG) number;
\r
5931 openFileName.lpfnHook = oldDialog ?
\r
5932 (LPOFNHOOKPROC) OldOpenFileHook : (LPOFNHOOKPROC) OpenFileHook;
\r
5933 openFileName.lpTemplateName = (LPSTR)(oldDialog ? 1536 : DLG_IndexNumber);
\r
5935 if (write[0] != 'r' ? GetSaveFileName(&openFileName) :
\r
5936 GetOpenFileName(&openFileName)) {
\r
5937 /* open the file */
\r
5938 f = fopen(openFileName.lpstrFile, write);
\r
5940 MessageBox(hwnd, _("File open failed"), NULL,
\r
5941 MB_OK|MB_ICONEXCLAMATION);
\r
5945 int err = CommDlgExtendedError();
\r
5946 if (err != 0) DisplayError(_("Internal error in file dialog box"), err);
\r
5955 MenuPopup(HWND hwnd, POINT pt, HMENU hmenu, UINT def)
\r
5957 HMENU hmenuTrackPopup; /* floating pop-up menu */
\r
5960 * Get the first pop-up menu in the menu template. This is the
\r
5961 * menu that TrackPopupMenu displays.
\r
5963 hmenuTrackPopup = GetSubMenu(hmenu, 0);
\r
5964 TranslateOneMenu(10, hmenuTrackPopup);
\r
5966 SetMenuDefaultItem(hmenuTrackPopup, def, FALSE);
\r
5969 * TrackPopup uses screen coordinates, so convert the
\r
5970 * coordinates of the mouse click to screen coordinates.
\r
5972 ClientToScreen(hwnd, (LPPOINT) &pt);
\r
5974 /* Draw and track the floating pop-up menu. */
\r
5975 TrackPopupMenu(hmenuTrackPopup, TPM_CENTERALIGN | TPM_RIGHTBUTTON,
\r
5976 pt.x, pt.y, 0, hwnd, NULL);
\r
5978 /* Destroy the menu.*/
\r
5979 DestroyMenu(hmenu);
\r
5984 int sizeX, sizeY, newSizeX, newSizeY;
\r
5986 } ResizeEditPlusButtonsClosure;
\r
5989 ResizeEditPlusButtonsCallback(HWND hChild, LPARAM lparam)
\r
5991 ResizeEditPlusButtonsClosure *cl = (ResizeEditPlusButtonsClosure *)lparam;
\r
5995 if (hChild == cl->hText) return TRUE;
\r
5996 GetWindowRect(hChild, &rect); /* gives screen coords */
\r
5997 pt.x = rect.left + (cl->newSizeX - cl->sizeX)/2;
\r
5998 pt.y = rect.top + cl->newSizeY - cl->sizeY;
\r
5999 ScreenToClient(cl->hDlg, &pt);
\r
6000 cl->hdwp = DeferWindowPos(cl->hdwp, hChild, NULL,
\r
6001 pt.x, pt.y, rect.right - rect.left, rect.bottom - rect.top, SWP_NOZORDER);
\r
6005 /* Resize a dialog that has a (rich) edit field filling most of
\r
6006 the top, with a row of buttons below */
\r
6008 ResizeEditPlusButtons(HWND hDlg, HWND hText, int sizeX, int sizeY, int newSizeX, int newSizeY)
\r
6011 int newTextHeight, newTextWidth;
\r
6012 ResizeEditPlusButtonsClosure cl;
\r
6014 /*if (IsIconic(hDlg)) return;*/
\r
6015 if (newSizeX == sizeX && newSizeY == sizeY) return;
\r
6017 cl.hdwp = BeginDeferWindowPos(8);
\r
6019 GetWindowRect(hText, &rectText); /* gives screen coords */
\r
6020 newTextWidth = rectText.right - rectText.left + newSizeX - sizeX;
\r
6021 newTextHeight = rectText.bottom - rectText.top + newSizeY - sizeY;
\r
6022 if (newTextHeight < 0) {
\r
6023 newSizeY += -newTextHeight;
\r
6024 newTextHeight = 0;
\r
6026 cl.hdwp = DeferWindowPos(cl.hdwp, hText, NULL, 0, 0,
\r
6027 newTextWidth, newTextHeight, SWP_NOZORDER|SWP_NOMOVE);
\r
6033 cl.newSizeX = newSizeX;
\r
6034 cl.newSizeY = newSizeY;
\r
6035 EnumChildWindows(hDlg, ResizeEditPlusButtonsCallback, (LPARAM)&cl);
\r
6037 EndDeferWindowPos(cl.hdwp);
\r
6040 BOOL CenterWindowEx(HWND hwndChild, HWND hwndParent, int mode)
\r
6042 RECT rChild, rParent;
\r
6043 int wChild, hChild, wParent, hParent;
\r
6044 int wScreen, hScreen, xNew, yNew;
\r
6047 /* Get the Height and Width of the child window */
\r
6048 GetWindowRect (hwndChild, &rChild);
\r
6049 wChild = rChild.right - rChild.left;
\r
6050 hChild = rChild.bottom - rChild.top;
\r
6052 /* Get the Height and Width of the parent window */
\r
6053 GetWindowRect (hwndParent, &rParent);
\r
6054 wParent = rParent.right - rParent.left;
\r
6055 hParent = rParent.bottom - rParent.top;
\r
6057 /* Get the display limits */
\r
6058 hdc = GetDC (hwndChild);
\r
6059 wScreen = GetDeviceCaps (hdc, HORZRES);
\r
6060 hScreen = GetDeviceCaps (hdc, VERTRES);
\r
6061 ReleaseDC(hwndChild, hdc);
\r
6063 /* Calculate new X position, then adjust for screen */
\r
6064 xNew = rParent.left + ((wParent - wChild) /2);
\r
6067 } else if ((xNew+wChild) > wScreen) {
\r
6068 xNew = wScreen - wChild;
\r
6071 /* Calculate new Y position, then adjust for screen */
\r
6073 yNew = rParent.top + ((hParent - hChild) /2);
\r
6076 yNew = rParent.top + GetSystemMetrics( SM_CYCAPTION ) * 2 / 3;
\r
6081 } else if ((yNew+hChild) > hScreen) {
\r
6082 yNew = hScreen - hChild;
\r
6085 /* Set it, and return */
\r
6086 return SetWindowPos (hwndChild, NULL,
\r
6087 xNew, yNew, 0, 0, SWP_NOSIZE | SWP_NOZORDER);
\r
6090 /* Center one window over another */
\r
6091 BOOL CenterWindow (HWND hwndChild, HWND hwndParent)
\r
6093 return CenterWindowEx( hwndChild, hwndParent, 0 );
\r
6096 /*---------------------------------------------------------------------------*\
\r
6098 * Startup Dialog functions
\r
6100 \*---------------------------------------------------------------------------*/
\r
6102 InitComboStrings(HANDLE hwndCombo, char **cd)
\r
6104 SendMessage(hwndCombo, CB_RESETCONTENT, 0, 0);
\r
6106 while (*cd != NULL) {
\r
6107 SendMessage(hwndCombo, CB_ADDSTRING, 0, (LPARAM) T_(*cd));
\r
6113 InitComboStringsFromOption(HANDLE hwndCombo, char *str)
\r
6115 char buf1[MAX_ARG_LEN];
\r
6118 if (str[0] == '@') {
\r
6119 FILE* f = fopen(str + 1, "r");
\r
6121 DisplayFatalError(str + 1, errno, 2);
\r
6124 len = fread(buf1, 1, sizeof(buf1)-1, f);
\r
6126 buf1[len] = NULLCHAR;
\r
6130 SendMessage(hwndCombo, CB_RESETCONTENT, 0, 0);
\r
6133 char buf[MSG_SIZ];
\r
6134 char *end = strchr(str, '\n');
\r
6135 if (end == NULL) return;
\r
6136 memcpy(buf, str, end - str);
\r
6137 buf[end - str] = NULLCHAR;
\r
6138 SendMessage(hwndCombo, CB_ADDSTRING, 0, (LPARAM) buf);
\r
6144 SetStartupDialogEnables(HWND hDlg)
\r
6146 EnableWindow(GetDlgItem(hDlg, OPT_ChessEngineName),
\r
6147 IsDlgButtonChecked(hDlg, OPT_ChessEngine) ||
\r
6148 (appData.zippyPlay && IsDlgButtonChecked(hDlg, OPT_ChessServer)));
\r
6149 EnableWindow(GetDlgItem(hDlg, OPT_SecondChessEngineName),
\r
6150 IsDlgButtonChecked(hDlg, OPT_ChessEngine));
\r
6151 EnableWindow(GetDlgItem(hDlg, OPT_ChessServerName),
\r
6152 IsDlgButtonChecked(hDlg, OPT_ChessServer));
\r
6153 EnableWindow(GetDlgItem(hDlg, OPT_AdditionalOptions),
\r
6154 IsDlgButtonChecked(hDlg, OPT_AnyAdditional));
\r
6155 EnableWindow(GetDlgItem(hDlg, IDOK),
\r
6156 IsDlgButtonChecked(hDlg, OPT_ChessEngine) ||
\r
6157 IsDlgButtonChecked(hDlg, OPT_ChessServer) ||
\r
6158 IsDlgButtonChecked(hDlg, OPT_View));
\r
6162 QuoteForFilename(char *filename)
\r
6164 int dquote, space;
\r
6165 dquote = strchr(filename, '"') != NULL;
\r
6166 space = strchr(filename, ' ') != NULL;
\r
6167 if (dquote || space) {
\r
6179 InitEngineBox(HWND hDlg, HWND hwndCombo, char* nthcp, char* nthd, char* nthdir, char *nthnames)
\r
6181 char buf[MSG_SIZ];
\r
6184 InitComboStringsFromOption(hwndCombo, nthnames);
\r
6185 q = QuoteForFilename(nthcp);
\r
6186 snprintf(buf, MSG_SIZ, "%s%s%s", q, nthcp, q);
\r
6187 if (*nthdir != NULLCHAR) {
\r
6188 q = QuoteForFilename(nthdir);
\r
6189 snprintf(buf + strlen(buf), MSG_SIZ, " /%s=%s%s%s", nthd, q, nthdir, q);
\r
6191 if (*nthcp == NULLCHAR) {
\r
6192 SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) 0, (LPARAM) 0);
\r
6193 } else if (SendMessage(hwndCombo, CB_SELECTSTRING, (WPARAM) -1, (LPARAM) buf) == CB_ERR) {
\r
6194 SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) -1, (LPARAM) 0);
\r
6195 SendMessage(hwndCombo, WM_SETTEXT, (WPARAM) 0, (LPARAM) buf);
\r
6200 StartupDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
6202 char buf[MSG_SIZ];
\r
6206 switch (message) {
\r
6207 case WM_INITDIALOG:
\r
6208 /* Center the dialog */
\r
6209 CenterWindow (hDlg, GetDesktopWindow());
\r
6210 Translate(hDlg, DLG_Startup);
\r
6211 /* Initialize the dialog items */
\r
6212 InitEngineBox(hDlg, GetDlgItem(hDlg, OPT_ChessEngineName),
\r
6213 appData.firstChessProgram, "fd", appData.firstDirectory,
\r
6214 firstChessProgramNames);
\r
6215 InitEngineBox(hDlg, GetDlgItem(hDlg, OPT_SecondChessEngineName),
\r
6216 appData.secondChessProgram, singleList ? "fd" : "sd", appData.secondDirectory,
\r
6217 singleList ? firstChessProgramNames : secondChessProgramNames); //[HGM] single: use first list in second combo
\r
6218 hwndCombo = GetDlgItem(hDlg, OPT_ChessServerName);
\r
6219 InitComboStringsFromOption(hwndCombo, icsNames);
\r
6220 snprintf(buf, MSG_SIZ, "%s /icsport=%s", appData.icsHost, appData.icsPort);
\r
6221 if (*appData.icsHelper != NULLCHAR) {
\r
6222 char *q = QuoteForFilename(appData.icsHelper);
\r
6223 sprintf(buf + strlen(buf), " /icshelper=%s%s%s", q, appData.icsHelper, q);
\r
6225 if (*appData.icsHost == NULLCHAR) {
\r
6226 SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) 0, (LPARAM) 0);
\r
6227 /*SendMessage(hwndCombo, CB_SHOWDROPDOWN, (WPARAM) TRUE, (LPARAM) 0); !!too soon */
\r
6228 } else if (SendMessage(hwndCombo, CB_SELECTSTRING, (WPARAM) -1, (LPARAM) buf) == CB_ERR) {
\r
6229 SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) -1, (LPARAM) 0);
\r
6230 SendMessage(hwndCombo, WM_SETTEXT, (WPARAM) 0, (LPARAM) buf);
\r
6233 if (appData.icsActive) {
\r
6234 CheckDlgButton(hDlg, OPT_ChessServer, BST_CHECKED);
\r
6236 else if (appData.noChessProgram) {
\r
6237 CheckDlgButton(hDlg, OPT_View, BST_CHECKED);
\r
6240 CheckDlgButton(hDlg, OPT_ChessEngine, BST_CHECKED);
\r
6243 SetStartupDialogEnables(hDlg);
\r
6247 switch (LOWORD(wParam)) {
\r
6249 if (IsDlgButtonChecked(hDlg, OPT_ChessEngine)) {
\r
6250 safeStrCpy(buf, "/fcp=", sizeof(buf)/sizeof(buf[0]) );
\r
6251 GetDlgItemText(hDlg, OPT_ChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));
\r
6253 comboLine = strdup(p+5); // [HGM] recent: remember complete line of first combobox
\r
6254 ParseArgs(StringGet, &p);
\r
6255 safeStrCpy(buf, singleList ? "/fcp=" : "/scp=", sizeof(buf)/sizeof(buf[0]) );
\r
6256 GetDlgItemText(hDlg, OPT_SecondChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));
\r
6258 SwapEngines(singleList); // temporarily swap first and second, to load a second 'first', ...
\r
6259 ParseArgs(StringGet, &p);
\r
6260 SwapEngines(singleList); // ... and then make it 'second'
\r
6261 appData.noChessProgram = FALSE;
\r
6262 appData.icsActive = FALSE;
\r
6263 } else if (IsDlgButtonChecked(hDlg, OPT_ChessServer)) {
\r
6264 safeStrCpy(buf, "/ics /icshost=", sizeof(buf)/sizeof(buf[0]) );
\r
6265 GetDlgItemText(hDlg, OPT_ChessServerName, buf + strlen(buf), sizeof(buf) - strlen(buf));
\r
6267 ParseArgs(StringGet, &p);
\r
6268 if (appData.zippyPlay) {
\r
6269 safeStrCpy(buf, "/fcp=", sizeof(buf)/sizeof(buf[0]) );
\r
6270 GetDlgItemText(hDlg, OPT_ChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));
\r
6272 ParseArgs(StringGet, &p);
\r
6274 } else if (IsDlgButtonChecked(hDlg, OPT_View)) {
\r
6275 appData.noChessProgram = TRUE;
\r
6276 appData.icsActive = FALSE;
\r
6278 MessageBox(hDlg, _("Choose an option, or cancel to exit"),
\r
6279 _("Option Error"), MB_OK|MB_ICONEXCLAMATION);
\r
6282 if (IsDlgButtonChecked(hDlg, OPT_AnyAdditional)) {
\r
6283 GetDlgItemText(hDlg, OPT_AdditionalOptions, buf, sizeof(buf));
\r
6285 ParseArgs(StringGet, &p);
\r
6287 EndDialog(hDlg, TRUE);
\r
6294 case IDM_HELPCONTENTS:
\r
6295 if (!WinHelp (hDlg, "winboard.hlp", HELP_KEY,(DWORD)(LPSTR)"CONTENTS")) {
\r
6296 MessageBox (GetFocus(),
\r
6297 _("Unable to activate help"),
\r
6298 szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);
\r
6303 SetStartupDialogEnables(hDlg);
\r
6311 /*---------------------------------------------------------------------------*\
\r
6313 * About box dialog functions
\r
6315 \*---------------------------------------------------------------------------*/
\r
6317 /* Process messages for "About" dialog box */
\r
6319 About(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
6321 switch (message) {
\r
6322 case WM_INITDIALOG: /* message: initialize dialog box */
\r
6323 /* Center the dialog over the application window */
\r
6324 CenterWindow (hDlg, GetWindow (hDlg, GW_OWNER));
\r
6325 SetDlgItemText(hDlg, ABOUTBOX_Version, programVersion);
\r
6326 Translate(hDlg, ABOUTBOX);
\r
6330 case WM_COMMAND: /* message: received a command */
\r
6331 if (LOWORD(wParam) == IDOK /* "OK" box selected? */
\r
6332 || LOWORD(wParam) == IDCANCEL) { /* System menu close command? */
\r
6333 EndDialog(hDlg, TRUE); /* Exit the dialog */
\r
6341 /*---------------------------------------------------------------------------*\
\r
6343 * Comment Dialog functions
\r
6345 \*---------------------------------------------------------------------------*/
\r
6348 CommentDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
6350 static HANDLE hwndText = NULL;
\r
6351 int len, newSizeX, newSizeY, flags;
\r
6352 static int sizeX, sizeY;
\r
6357 switch (message) {
\r
6358 case WM_INITDIALOG: /* message: initialize dialog box */
\r
6359 /* Initialize the dialog items */
\r
6360 Translate(hDlg, DLG_EditComment);
\r
6361 hwndText = GetDlgItem(hDlg, OPT_CommentText);
\r
6362 SetDlgItemText(hDlg, OPT_CommentText, commentText);
\r
6363 EnableWindow(GetDlgItem(hDlg, OPT_CancelComment), editComment);
\r
6364 EnableWindow(GetDlgItem(hDlg, OPT_ClearComment), editComment);
\r
6365 EnableWindow(GetDlgItem(hDlg, OPT_EditComment), !editComment);
\r
6366 SendMessage(hwndText, EM_SETREADONLY, !editComment, 0);
\r
6367 SetWindowText(hDlg, commentTitle);
\r
6368 if (editComment) {
\r
6369 SetFocus(hwndText);
\r
6371 SetFocus(GetDlgItem(hDlg, IDOK));
\r
6373 SendMessage(GetDlgItem(hDlg, OPT_CommentText),
\r
6374 WM_SETFONT, (WPARAM)font[boardSize][COMMENT_FONT]->hf,
\r
6375 MAKELPARAM(FALSE, 0));
\r
6376 /* Size and position the dialog */
\r
6377 if (!commentDialog) {
\r
6378 commentDialog = hDlg;
\r
6379 flags = SWP_NOZORDER;
\r
6380 GetClientRect(hDlg, &rect);
\r
6381 sizeX = rect.right;
\r
6382 sizeY = rect.bottom;
\r
6383 if (wpComment.x != CW_USEDEFAULT && wpComment.y != CW_USEDEFAULT &&
\r
6384 wpComment.width != CW_USEDEFAULT && wpComment.height != CW_USEDEFAULT) {
\r
6385 WINDOWPLACEMENT wp;
\r
6386 EnsureOnScreen(&wpComment.x, &wpComment.y, 0, 0);
\r
6387 wp.length = sizeof(WINDOWPLACEMENT);
\r
6389 wp.showCmd = SW_SHOW;
\r
6390 wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;
\r
6391 wp.rcNormalPosition.left = wpComment.x;
\r
6392 wp.rcNormalPosition.right = wpComment.x + wpComment.width;
\r
6393 wp.rcNormalPosition.top = wpComment.y;
\r
6394 wp.rcNormalPosition.bottom = wpComment.y + wpComment.height;
\r
6395 SetWindowPlacement(hDlg, &wp);
\r
6397 GetClientRect(hDlg, &rect);
\r
6398 newSizeX = rect.right;
\r
6399 newSizeY = rect.bottom;
\r
6400 ResizeEditPlusButtons(hDlg, hwndText, sizeX, sizeY,
\r
6401 newSizeX, newSizeY);
\r
6406 SendDlgItemMessage( hDlg, OPT_CommentText, EM_SETEVENTMASK, 0, ENM_MOUSEEVENTS | ENM_KEYEVENTS );
\r
6409 case WM_COMMAND: /* message: received a command */
\r
6410 switch (LOWORD(wParam)) {
\r
6412 if (editComment) {
\r
6414 /* Read changed options from the dialog box */
\r
6415 hwndText = GetDlgItem(hDlg, OPT_CommentText);
\r
6416 len = GetWindowTextLength(hwndText);
\r
6417 str = (char *) malloc(len + 1);
\r
6418 GetWindowText(hwndText, str, len + 1);
\r
6427 ReplaceComment(commentIndex, str);
\r
6434 case OPT_CancelComment:
\r
6438 case OPT_ClearComment:
\r
6439 SetDlgItemText(hDlg, OPT_CommentText, "");
\r
6442 case OPT_EditComment:
\r
6443 EditCommentEvent();
\r
6451 case WM_NOTIFY: // [HGM] vari: cloned from whistory.c
\r
6452 if( wParam == OPT_CommentText ) {
\r
6453 MSGFILTER * lpMF = (MSGFILTER *) lParam;
\r
6455 if( lpMF->msg == WM_RBUTTONDOWN && (lpMF->wParam & (MK_CONTROL | MK_SHIFT)) == 0 ||
\r
6456 lpMF->msg == WM_CHAR && lpMF->wParam == '\022' ) {
\r
6460 pt.x = LOWORD( lpMF->lParam );
\r
6461 pt.y = HIWORD( lpMF->lParam );
\r
6463 if(lpMF->msg == WM_CHAR) {
\r
6465 SendDlgItemMessage( hDlg, OPT_CommentText, EM_EXGETSEL, 0, (LPARAM) &sel );
\r
6466 index = sel.cpMin;
\r
6468 index = SendDlgItemMessage( hDlg, OPT_CommentText, EM_CHARFROMPOS, 0, (LPARAM) &pt );
\r
6470 hwndText = GetDlgItem(hDlg, OPT_CommentText); // cloned from above
\r
6471 len = GetWindowTextLength(hwndText);
\r
6472 str = (char *) malloc(len + 1);
\r
6473 GetWindowText(hwndText, str, len + 1);
\r
6474 ReplaceComment(commentIndex, str);
\r
6475 if(commentIndex != currentMove) ToNrEvent(commentIndex);
\r
6476 LoadVariation( index, str ); // [HGM] also does the actual moving to it, now
\r
6479 /* Zap the message for good: apparently, returning non-zero is not enough */
\r
6480 lpMF->msg = WM_USER;
\r
6488 newSizeX = LOWORD(lParam);
\r
6489 newSizeY = HIWORD(lParam);
\r
6490 ResizeEditPlusButtons(hDlg, hwndText, sizeX, sizeY, newSizeX, newSizeY);
\r
6495 case WM_GETMINMAXINFO:
\r
6496 /* Prevent resizing window too small */
\r
6497 mmi = (MINMAXINFO *) lParam;
\r
6498 mmi->ptMinTrackSize.x = 100;
\r
6499 mmi->ptMinTrackSize.y = 100;
\r
6506 EitherCommentPopUp(int index, char *title, char *str, BOOLEAN edit)
\r
6511 CheckMenuItem(GetMenu(hwndMain), IDM_Comment, edit ? MF_CHECKED : MF_UNCHECKED);
\r
6513 if (str == NULL) str = "";
\r
6514 p = (char *) malloc(2 * strlen(str) + 2);
\r
6517 if (*str == '\n') *q++ = '\r';
\r
6521 if (commentText != NULL) free(commentText);
\r
6523 commentIndex = index;
\r
6524 commentTitle = title;
\r
6526 editComment = edit;
\r
6528 if (commentDialog) {
\r
6529 SendMessage(commentDialog, WM_INITDIALOG, 0, 0);
\r
6530 if (!commentUp) ShowWindow(commentDialog, SW_SHOW);
\r
6532 lpProc = MakeProcInstance((FARPROC)CommentDialog, hInst);
\r
6533 CreateDialog(hInst, MAKEINTRESOURCE(DLG_EditComment),
\r
6534 hwndMain, (DLGPROC)lpProc);
\r
6535 FreeProcInstance(lpProc);
\r
6541 /*---------------------------------------------------------------------------*\
\r
6543 * Type-in move dialog functions
\r
6545 \*---------------------------------------------------------------------------*/
\r
6548 TypeInMoveDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
6550 char move[MSG_SIZ];
\r
6553 switch (message) {
\r
6554 case WM_INITDIALOG:
\r
6555 move[0] = (char) lParam;
\r
6556 move[1] = NULLCHAR;
\r
6557 CenterWindowEx(hDlg, GetWindow(hDlg, GW_OWNER), 1 );
\r
6558 Translate(hDlg, DLG_TypeInMove);
\r
6559 hInput = GetDlgItem(hDlg, OPT_Move);
\r
6560 SetWindowText(hInput, move);
\r
6562 SendMessage(hInput, EM_SETSEL, (WPARAM)9999, (LPARAM)9999);
\r
6566 switch (LOWORD(wParam)) {
\r
6569 shiftKey = GetKeyState(VK_SHIFT) < 0; // [HGM] remember last shift status
\r
6570 GetDlgItemText(hDlg, OPT_Move, move, sizeof(move));
\r
6571 TypeInDoneEvent(move);
\r
6572 EndDialog(hDlg, TRUE);
\r
6575 EndDialog(hDlg, FALSE);
\r
6586 PopUpMoveDialog(char firstchar)
\r
6590 lpProc = MakeProcInstance((FARPROC)TypeInMoveDialog, hInst);
\r
6591 DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_TypeInMove),
\r
6592 hwndMain, (DLGPROC)lpProc, (LPARAM)firstchar);
\r
6593 FreeProcInstance(lpProc);
\r
6596 /*---------------------------------------------------------------------------*\
\r
6598 * Type-in name dialog functions
\r
6600 \*---------------------------------------------------------------------------*/
\r
6603 TypeInNameDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
6605 char move[MSG_SIZ];
\r
6608 switch (message) {
\r
6609 case WM_INITDIALOG:
\r
6610 move[0] = (char) lParam;
\r
6611 move[1] = NULLCHAR;
\r
6612 CenterWindowEx(hDlg, GetWindow(hDlg, GW_OWNER), 1 );
\r
6613 Translate(hDlg, DLG_TypeInName);
\r
6614 hInput = GetDlgItem(hDlg, OPT_Name);
\r
6615 SetWindowText(hInput, move);
\r
6617 SendMessage(hInput, EM_SETSEL, (WPARAM)9999, (LPARAM)9999);
\r
6621 switch (LOWORD(wParam)) {
\r
6623 GetDlgItemText(hDlg, OPT_Name, move, sizeof(move));
\r
6624 appData.userName = strdup(move);
\r
6627 if(gameMode == MachinePlaysWhite || gameMode == MachinePlaysBlack) {
\r
6628 snprintf(move, MSG_SIZ, "%s vs. %s", gameInfo.white, gameInfo.black);
\r
6629 DisplayTitle(move);
\r
6633 EndDialog(hDlg, TRUE);
\r
6636 EndDialog(hDlg, FALSE);
\r
6647 PopUpNameDialog(char firstchar)
\r
6651 lpProc = MakeProcInstance((FARPROC)TypeInNameDialog, hInst);
\r
6652 DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_TypeInName),
\r
6653 hwndMain, (DLGPROC)lpProc, (LPARAM)firstchar);
\r
6654 FreeProcInstance(lpProc);
\r
6657 /*---------------------------------------------------------------------------*\
\r
6661 \*---------------------------------------------------------------------------*/
\r
6663 /* Nonmodal error box */
\r
6664 LRESULT CALLBACK ErrorDialog(HWND hDlg, UINT message,
\r
6665 WPARAM wParam, LPARAM lParam);
\r
6668 ErrorPopUp(char *title, char *content)
\r
6672 BOOLEAN modal = hwndMain == NULL;
\r
6690 strncpy(errorTitle, title, sizeof(errorTitle));
\r
6691 errorTitle[sizeof(errorTitle) - 1] = '\0';
\r
6694 MessageBox(NULL, errorMessage, errorTitle, MB_OK|MB_ICONEXCLAMATION);
\r
6696 lpProc = MakeProcInstance((FARPROC)ErrorDialog, hInst);
\r
6697 CreateDialog(hInst, MAKEINTRESOURCE(DLG_Error),
\r
6698 hwndMain, (DLGPROC)lpProc);
\r
6699 FreeProcInstance(lpProc);
\r
6706 if (!appData.popupMoveErrors && moveErrorMessageUp) DisplayMessage("", "");
\r
6707 if (errorDialog == NULL) return;
\r
6708 DestroyWindow(errorDialog);
\r
6709 errorDialog = NULL;
\r
6710 if(errorExitStatus) ExitEvent(errorExitStatus);
\r
6714 ErrorDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
6719 switch (message) {
\r
6720 case WM_INITDIALOG:
\r
6721 GetWindowRect(hDlg, &rChild);
\r
6724 SetWindowPos(hDlg, NULL, rChild.left,
\r
6725 rChild.top + boardRect.top - (rChild.bottom - rChild.top),
\r
6726 0, 0, SWP_NOZORDER|SWP_NOSIZE);
\r
6730 [AS] It seems that the above code wants to move the dialog up in the "caption
\r
6731 area" of the main window, but it uses the dialog height as an hard-coded constant,
\r
6732 and it doesn't work when you resize the dialog.
\r
6733 For now, just give it a default position.
\r
6735 SetWindowPos(hDlg, NULL, boardRect.left+8, boardRect.top+8, 0, 0, SWP_NOZORDER|SWP_NOSIZE);
\r
6736 Translate(hDlg, DLG_Error);
\r
6738 errorDialog = hDlg;
\r
6739 SetWindowText(hDlg, errorTitle);
\r
6740 hwndText = GetDlgItem(hDlg, OPT_ErrorText);
\r
6741 SetDlgItemText(hDlg, OPT_ErrorText, errorMessage);
\r
6745 switch (LOWORD(wParam)) {
\r
6748 if (errorDialog == hDlg) errorDialog = NULL;
\r
6749 DestroyWindow(hDlg);
\r
6761 HWND gothicDialog = NULL;
\r
6764 GothicDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
6768 int height = GetSystemMetrics(SM_CYCAPTION)+GetSystemMetrics(SM_CYFRAME);
\r
6770 switch (message) {
\r
6771 case WM_INITDIALOG:
\r
6772 GetWindowRect(hDlg, &rChild);
\r
6774 SetWindowPos(hDlg, NULL, wpMain.x, wpMain.y-height, wpMain.width, height,
\r
6778 [AS] It seems that the above code wants to move the dialog up in the "caption
\r
6779 area" of the main window, but it uses the dialog height as an hard-coded constant,
\r
6780 and it doesn't work when you resize the dialog.
\r
6781 For now, just give it a default position.
\r
6783 gothicDialog = hDlg;
\r
6784 SetWindowText(hDlg, errorTitle);
\r
6785 hwndText = GetDlgItem(hDlg, OPT_ErrorText);
\r
6786 SetDlgItemText(hDlg, OPT_ErrorText, errorMessage);
\r
6790 switch (LOWORD(wParam)) {
\r
6793 if (errorDialog == hDlg) errorDialog = NULL;
\r
6794 DestroyWindow(hDlg);
\r
6806 GothicPopUp(char *title, VariantClass variant)
\r
6809 static char *lastTitle;
\r
6811 strncpy(errorTitle, title, sizeof(errorTitle));
\r
6812 errorTitle[sizeof(errorTitle) - 1] = '\0';
\r
6814 if(lastTitle != title && gothicDialog != NULL) {
\r
6815 DestroyWindow(gothicDialog);
\r
6816 gothicDialog = NULL;
\r
6818 if(variant != VariantNormal && gothicDialog == NULL) {
\r
6819 title = lastTitle;
\r
6820 lpProc = MakeProcInstance((FARPROC)GothicDialog, hInst);
\r
6821 CreateDialog(hInst, MAKEINTRESOURCE(DLG_Error),
\r
6822 hwndMain, (DLGPROC)lpProc);
\r
6823 FreeProcInstance(lpProc);
\r
6828 /*---------------------------------------------------------------------------*\
\r
6830 * Ics Interaction console functions
\r
6832 \*---------------------------------------------------------------------------*/
\r
6834 #define HISTORY_SIZE 64
\r
6835 static char *history[HISTORY_SIZE];
\r
6836 int histIn = 0, histP = 0;
\r
6839 SaveInHistory(char *cmd)
\r
6841 if (history[histIn] != NULL) {
\r
6842 free(history[histIn]);
\r
6843 history[histIn] = NULL;
\r
6845 if (*cmd == NULLCHAR) return;
\r
6846 history[histIn] = StrSave(cmd);
\r
6847 histIn = (histIn + 1) % HISTORY_SIZE;
\r
6848 if (history[histIn] != NULL) {
\r
6849 free(history[histIn]);
\r
6850 history[histIn] = NULL;
\r
6856 PrevInHistory(char *cmd)
\r
6859 if (histP == histIn) {
\r
6860 if (history[histIn] != NULL) free(history[histIn]);
\r
6861 history[histIn] = StrSave(cmd);
\r
6863 newhp = (histP - 1 + HISTORY_SIZE) % HISTORY_SIZE;
\r
6864 if (newhp == histIn || history[newhp] == NULL) return NULL;
\r
6866 return history[histP];
\r
6872 if (histP == histIn) return NULL;
\r
6873 histP = (histP + 1) % HISTORY_SIZE;
\r
6874 return history[histP];
\r
6878 LoadIcsTextMenu(IcsTextMenuEntry *e)
\r
6882 hmenu = LoadMenu(hInst, "TextMenu");
\r
6883 h = GetSubMenu(hmenu, 0);
\r
6885 if (strcmp(e->item, "-") == 0) {
\r
6886 AppendMenu(h, MF_SEPARATOR, 0, 0);
\r
6887 } else { // [HGM] re-written a bit to use only one AppendMenu call for both cases (| or no |)
\r
6888 int flags = MF_STRING, j = 0;
\r
6889 if (e->item[0] == '|') {
\r
6890 flags |= MF_MENUBARBREAK;
\r
6893 if(!strcmp(e->command, "none")) flags |= MF_GRAYED; // [HGM] chatclick: provide inactive dummy
\r
6894 AppendMenu(h, flags, IDM_CommandX + i, e->item + j);
\r
6902 WNDPROC consoleTextWindowProc;
\r
6905 CommandX(HWND hwnd, char *command, BOOLEAN getname, BOOLEAN immediate)
\r
6907 char buf[MSG_SIZ], name[MSG_SIZ];
\r
6908 HWND hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
6912 SetWindowText(hInput, command);
\r
6914 SendMessage(hInput, WM_CHAR, '\r', 0);
\r
6916 sel.cpMin = 999999;
\r
6917 sel.cpMax = 999999;
\r
6918 SendMessage(hInput, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
6923 SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);
\r
6924 if (sel.cpMin == sel.cpMax) {
\r
6925 /* Expand to surrounding word */
\r
6928 tr.chrg.cpMax = sel.cpMin;
\r
6929 tr.chrg.cpMin = --sel.cpMin;
\r
6930 if (sel.cpMin < 0) break;
\r
6931 tr.lpstrText = name;
\r
6932 SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr);
\r
6933 } while (isalpha(name[0]) || isdigit(name[0]) || name[0] == '-');
\r
6937 tr.chrg.cpMin = sel.cpMax;
\r
6938 tr.chrg.cpMax = ++sel.cpMax;
\r
6939 tr.lpstrText = name;
\r
6940 if (SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr) < 1) break;
\r
6941 } while (isalpha(name[0]) || isdigit(name[0]) || name[0] == '-');
\r
6944 if (sel.cpMax == sel.cpMin || sel.cpMax - sel.cpMin > MSG_SIZ/2) {
\r
6945 MessageBeep(MB_ICONEXCLAMATION);
\r
6949 tr.lpstrText = name;
\r
6950 SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr);
\r
6952 if (sel.cpMax - sel.cpMin > MSG_SIZ/2) {
\r
6953 MessageBeep(MB_ICONEXCLAMATION);
\r
6956 SendMessage(hwnd, EM_GETSELTEXT, 0, (LPARAM) name);
\r
6959 if(strstr(command, "%s")) snprintf(buf, MSG_SIZ, command, name); else
\r
6960 snprintf(buf, MSG_SIZ, "%s %s", command, name);
\r
6961 SetWindowText(hInput, buf);
\r
6962 SendMessage(hInput, WM_CHAR, '\r', 0);
\r
6964 if(!strcmp(command, "chat")) { ChatPopUp(name); return; }
\r
6965 snprintf(buf, MSG_SIZ, "%s %s ", command, name); /* trailing space */
\r
6966 SetWindowText(hInput, buf);
\r
6967 sel.cpMin = 999999;
\r
6968 sel.cpMax = 999999;
\r
6969 SendMessage(hInput, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
6975 ConsoleTextSubclass(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
\r
6980 switch (message) {
\r
6982 if (!(GetKeyState(VK_CONTROL) & ~1)) break;
\r
6983 if(wParam=='R') return 0;
\r
6986 SendMessage(hwnd, EM_LINESCROLL, 0, -999999);
\r
6989 sel.cpMin = 999999;
\r
6990 sel.cpMax = 999999;
\r
6991 SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
6992 SendMessage(hwnd, EM_SCROLLCARET, 0, 0);
\r
6997 if(wParam != '\022') {
\r
6998 if (wParam == '\t') {
\r
6999 if (GetKeyState(VK_SHIFT) < 0) {
\r
7001 if (IsIconic(hwndMain)) ShowWindow(hwndMain, SW_RESTORE);
\r
7002 if (buttonDesc[0].hwnd) {
\r
7003 SetFocus(buttonDesc[0].hwnd);
\r
7005 SetFocus(hwndMain);
\r
7009 SetFocus(GetDlgItem(hwndConsole, OPT_ConsoleInput));
\r
7012 hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
7013 JAWS_DELETE( SetFocus(hInput); )
\r
7014 SendMessage(hInput, message, wParam, lParam);
\r
7017 } // [HGM] navigate: for Ctrl+R, flow into next case (moved up here) to summon up menu
\r
7019 case WM_RBUTTONDOWN:
\r
7020 if (!(GetKeyState(VK_SHIFT) & ~1)) {
\r
7021 /* Move selection here if it was empty */
\r
7023 pt.x = LOWORD(lParam);
\r
7024 pt.y = HIWORD(lParam);
\r
7025 SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);
\r
7026 if (sel.cpMin == sel.cpMax) {
\r
7027 if(lParam != -1) sel.cpMin = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&pt); /*doc is wrong*/
\r
7028 sel.cpMax = sel.cpMin;
\r
7029 SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
7031 SendMessage(hwnd, EM_HIDESELECTION, FALSE, FALSE);
\r
7032 { // [HGM] chatclick: code moved here from WM_RBUTTONUP case, to have menu appear on down-click
\r
7034 HMENU hmenu = LoadIcsTextMenu(icsTextMenuEntry);
\r
7035 SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);
\r
7036 if (sel.cpMin == sel.cpMax) {
\r
7037 EnableMenuItem(hmenu, IDM_Copy, MF_BYCOMMAND|MF_GRAYED);
\r
7038 EnableMenuItem(hmenu, IDM_QuickPaste, MF_BYCOMMAND|MF_GRAYED);
\r
7040 if (!IsClipboardFormatAvailable(CF_TEXT)) {
\r
7041 EnableMenuItem(hmenu, IDM_Paste, MF_BYCOMMAND|MF_GRAYED);
\r
7043 pt.x = LOWORD(lParam)-30; // [HGM] chatclick: make menu pop up with pointer above upper-right item
\r
7044 pt.y = HIWORD(lParam)-10; // make it appear as if mouse moved there, so it will be selected on up-click
\r
7045 PostMessage(hwnd, WM_MOUSEMOVE, wParam, lParam+5);
\r
7046 MenuPopup(hwnd, pt, hmenu, -1);
\r
7050 case WM_RBUTTONUP:
\r
7051 if (GetKeyState(VK_SHIFT) & ~1) {
\r
7052 SendDlgItemMessage(hwndConsole, OPT_ConsoleText,
\r
7053 WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);
\r
7057 hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
7059 return SendMessage(hInput, message, wParam, lParam);
\r
7060 case WM_MBUTTONDOWN:
\r
7061 return SendMessage(hwnd, WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);
\r
7063 switch (LOWORD(wParam)) {
\r
7064 case IDM_QuickPaste:
\r
7066 SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);
\r
7067 if (sel.cpMin == sel.cpMax) {
\r
7068 MessageBeep(MB_ICONEXCLAMATION);
\r
7071 SendMessage(hwnd, WM_COPY, 0, 0);
\r
7072 hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
7073 SendMessage(hInput, WM_PASTE, 0, 0);
\r
7078 SendMessage(hwnd, WM_CUT, 0, 0);
\r
7081 SendMessage(hwnd, WM_PASTE, 0, 0);
\r
7084 SendMessage(hwnd, WM_COPY, 0, 0);
\r
7088 int i = LOWORD(wParam) - IDM_CommandX;
\r
7089 if (i >= 0 && i < ICS_TEXT_MENU_SIZE &&
\r
7090 icsTextMenuEntry[i].command != NULL) {
\r
7091 CommandX(hwnd, icsTextMenuEntry[i].command,
\r
7092 icsTextMenuEntry[i].getname,
\r
7093 icsTextMenuEntry[i].immediate);
\r
7101 return (*consoleTextWindowProc)(hwnd, message, wParam, lParam);
\r
7104 WNDPROC consoleInputWindowProc;
\r
7107 ConsoleInputSubclass(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
\r
7109 char buf[MSG_SIZ];
\r
7111 static BOOL sendNextChar = FALSE;
\r
7112 static BOOL quoteNextChar = FALSE;
\r
7113 InputSource *is = consoleInputSource;
\r
7117 switch (message) {
\r
7119 if (!appData.localLineEditing || sendNextChar) {
\r
7120 is->buf[0] = (CHAR) wParam;
\r
7122 SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);
\r
7123 sendNextChar = FALSE;
\r
7126 if (quoteNextChar) {
\r
7127 buf[0] = (char) wParam;
\r
7128 buf[1] = NULLCHAR;
\r
7129 SendMessage(hwnd, EM_REPLACESEL, TRUE, (LPARAM) buf);
\r
7130 quoteNextChar = FALSE;
\r
7134 case '\r': /* Enter key */
\r
7135 is->count = GetWindowText(hwnd, is->buf, INPUT_SOURCE_BUF_SIZE-1);
\r
7136 if (consoleEcho) SaveInHistory(is->buf);
\r
7137 is->buf[is->count++] = '\n';
\r
7138 is->buf[is->count] = NULLCHAR;
\r
7139 SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);
\r
7140 if (consoleEcho) {
\r
7141 ConsoleOutput(is->buf, is->count, TRUE);
\r
7142 } else if (appData.localLineEditing) {
\r
7143 ConsoleOutput("\n", 1, TRUE);
\r
7146 case '\033': /* Escape key */
\r
7147 SetWindowText(hwnd, "");
\r
7148 cf.cbSize = sizeof(CHARFORMAT);
\r
7149 cf.dwMask = CFM_COLOR|CFM_BOLD|CFM_ITALIC|CFM_UNDERLINE|CFM_STRIKEOUT;
\r
7150 if (consoleEcho) {
\r
7151 cf.crTextColor = textAttribs[ColorNormal].color;
\r
7153 cf.crTextColor = COLOR_ECHOOFF;
\r
7155 cf.dwEffects = textAttribs[ColorNormal].effects;
\r
7156 SendMessage(hwnd, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf);
\r
7158 case '\t': /* Tab key */
\r
7159 if (GetKeyState(VK_SHIFT) < 0) {
\r
7161 SetFocus(GetDlgItem(hwndConsole, OPT_ConsoleText));
\r
7164 if (IsIconic(hwndMain)) ShowWindow(hwndMain, SW_RESTORE);
\r
7165 if (buttonDesc[0].hwnd) {
\r
7166 SetFocus(buttonDesc[0].hwnd);
\r
7168 SetFocus(hwndMain);
\r
7172 case '\023': /* Ctrl+S */
\r
7173 sendNextChar = TRUE;
\r
7175 case '\021': /* Ctrl+Q */
\r
7176 quoteNextChar = TRUE;
\r
7186 GetWindowText(hwnd, buf, MSG_SIZ);
\r
7187 p = PrevInHistory(buf);
\r
7189 SetWindowText(hwnd, p);
\r
7190 sel.cpMin = 999999;
\r
7191 sel.cpMax = 999999;
\r
7192 SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
7197 p = NextInHistory();
\r
7199 SetWindowText(hwnd, p);
\r
7200 sel.cpMin = 999999;
\r
7201 sel.cpMax = 999999;
\r
7202 SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
7208 if (!(GetKeyState(VK_CONTROL) & ~1)) break;
\r
7212 SendDlgItemMessage(hwndConsole, OPT_ConsoleText, message, wParam, lParam);
\r
7216 case WM_MBUTTONDOWN:
\r
7217 SendDlgItemMessage(hwndConsole, OPT_ConsoleText,
\r
7218 WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);
\r
7220 case WM_RBUTTONUP:
\r
7221 if (GetKeyState(VK_SHIFT) & ~1) {
\r
7222 SendDlgItemMessage(hwndConsole, OPT_ConsoleText,
\r
7223 WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);
\r
7227 hmenu = LoadMenu(hInst, "InputMenu");
\r
7228 SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);
\r
7229 if (sel.cpMin == sel.cpMax) {
\r
7230 EnableMenuItem(hmenu, IDM_Copy, MF_BYCOMMAND|MF_GRAYED);
\r
7231 EnableMenuItem(hmenu, IDM_Cut, MF_BYCOMMAND|MF_GRAYED);
\r
7233 if (!IsClipboardFormatAvailable(CF_TEXT)) {
\r
7234 EnableMenuItem(hmenu, IDM_Paste, MF_BYCOMMAND|MF_GRAYED);
\r
7236 pt.x = LOWORD(lParam);
\r
7237 pt.y = HIWORD(lParam);
\r
7238 MenuPopup(hwnd, pt, hmenu, -1);
\r
7242 switch (LOWORD(wParam)) {
\r
7244 SendMessage(hwnd, EM_UNDO, 0, 0);
\r
7246 case IDM_SelectAll:
\r
7248 sel.cpMax = -1; /*999999?*/
\r
7249 SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
7252 SendMessage(hwnd, WM_CUT, 0, 0);
\r
7255 SendMessage(hwnd, WM_PASTE, 0, 0);
\r
7258 SendMessage(hwnd, WM_COPY, 0, 0);
\r
7263 return (*consoleInputWindowProc)(hwnd, message, wParam, lParam);
\r
7266 #define CO_MAX 100000
\r
7267 #define CO_TRIM 1000
\r
7270 ConsoleWndProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
7272 static SnapData sd;
\r
7273 HWND hText, hInput;
\r
7275 static int sizeX, sizeY;
\r
7276 int newSizeX, newSizeY;
\r
7280 hText = GetDlgItem(hDlg, OPT_ConsoleText);
\r
7281 hInput = GetDlgItem(hDlg, OPT_ConsoleInput);
\r
7283 switch (message) {
\r
7285 if (((NMHDR*)lParam)->code == EN_LINK)
\r
7287 ENLINK *pLink = (ENLINK*)lParam;
\r
7288 if (pLink->msg == WM_LBUTTONUP)
\r
7292 tr.chrg = pLink->chrg;
\r
7293 tr.lpstrText = malloc(1+tr.chrg.cpMax-tr.chrg.cpMin);
\r
7294 SendMessage(hText, EM_GETTEXTRANGE, 0, (LPARAM)&tr);
\r
7295 ShellExecute(NULL, "open", tr.lpstrText, NULL, NULL, SW_SHOW);
\r
7296 free(tr.lpstrText);
\r
7300 case WM_INITDIALOG: /* message: initialize dialog box */
\r
7301 hwndConsole = hDlg;
\r
7303 consoleTextWindowProc = (WNDPROC)
\r
7304 SetWindowLongPtr(hText, GWLP_WNDPROC, (LONG_PTR) ConsoleTextSubclass);
\r
7305 SendMessage(hText, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);
\r
7306 consoleInputWindowProc = (WNDPROC)
\r
7307 SetWindowLongPtr(hInput, GWLP_WNDPROC, (LONG_PTR) ConsoleInputSubclass);
\r
7308 SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);
\r
7309 Colorize(ColorNormal, TRUE);
\r
7310 SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &consoleCF);
\r
7311 ChangedConsoleFont();
\r
7312 GetClientRect(hDlg, &rect);
\r
7313 sizeX = rect.right;
\r
7314 sizeY = rect.bottom;
\r
7315 if (wpConsole.x != CW_USEDEFAULT && wpConsole.y != CW_USEDEFAULT &&
\r
7316 wpConsole.width != CW_USEDEFAULT && wpConsole.height != CW_USEDEFAULT) {
\r
7317 WINDOWPLACEMENT wp;
\r
7318 EnsureOnScreen(&wpConsole.x, &wpConsole.y, 0, 0);
\r
7319 wp.length = sizeof(WINDOWPLACEMENT);
\r
7321 wp.showCmd = SW_SHOW;
\r
7322 wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;
\r
7323 wp.rcNormalPosition.left = wpConsole.x;
\r
7324 wp.rcNormalPosition.right = wpConsole.x + wpConsole.width;
\r
7325 wp.rcNormalPosition.top = wpConsole.y;
\r
7326 wp.rcNormalPosition.bottom = wpConsole.y + wpConsole.height;
\r
7327 SetWindowPlacement(hDlg, &wp);
\r
7330 // [HGM] Chessknight's change 2004-07-13
\r
7331 else { /* Determine Defaults */
\r
7332 WINDOWPLACEMENT wp;
\r
7333 wpConsole.x = wpMain.width + 1;
\r
7334 wpConsole.y = wpMain.y;
\r
7335 wpConsole.width = screenWidth - wpMain.width;
\r
7336 wpConsole.height = wpMain.height;
\r
7337 EnsureOnScreen(&wpConsole.x, &wpConsole.y, 0, 0);
\r
7338 wp.length = sizeof(WINDOWPLACEMENT);
\r
7340 wp.showCmd = SW_SHOW;
\r
7341 wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;
\r
7342 wp.rcNormalPosition.left = wpConsole.x;
\r
7343 wp.rcNormalPosition.right = wpConsole.x + wpConsole.width;
\r
7344 wp.rcNormalPosition.top = wpConsole.y;
\r
7345 wp.rcNormalPosition.bottom = wpConsole.y + wpConsole.height;
\r
7346 SetWindowPlacement(hDlg, &wp);
\r
7349 // Allow hText to highlight URLs and send notifications on them
\r
7350 wMask = (WORD) SendMessage(hText, EM_GETEVENTMASK, 0, 0L);
\r
7351 SendMessage(hText, EM_SETEVENTMASK, 0, wMask | ENM_LINK);
\r
7352 SendMessage(hText, EM_AUTOURLDETECT, TRUE, 0L);
\r
7353 SetWindowLongPtr(hText, GWLP_USERDATA, 79); // initialize the text window's width
\r
7367 if (IsIconic(hDlg)) break;
\r
7368 newSizeX = LOWORD(lParam);
\r
7369 newSizeY = HIWORD(lParam);
\r
7370 if (sizeX != newSizeX || sizeY != newSizeY) {
\r
7371 RECT rectText, rectInput;
\r
7373 int newTextHeight, newTextWidth;
\r
7374 GetWindowRect(hText, &rectText);
\r
7375 newTextWidth = rectText.right - rectText.left + newSizeX - sizeX;
\r
7376 newTextHeight = rectText.bottom - rectText.top + newSizeY - sizeY;
\r
7377 if (newTextHeight < 0) {
\r
7378 newSizeY += -newTextHeight;
\r
7379 newTextHeight = 0;
\r
7381 SetWindowPos(hText, NULL, 0, 0,
\r
7382 newTextWidth, newTextHeight, SWP_NOZORDER|SWP_NOMOVE);
\r
7383 GetWindowRect(hInput, &rectInput); /* gives screen coords */
\r
7384 pt.x = rectInput.left;
\r
7385 pt.y = rectInput.top + newSizeY - sizeY;
\r
7386 ScreenToClient(hDlg, &pt);
\r
7387 SetWindowPos(hInput, NULL,
\r
7388 pt.x, pt.y, /* needs client coords */
\r
7389 rectInput.right - rectInput.left + newSizeX - sizeX,
\r
7390 rectInput.bottom - rectInput.top, SWP_NOZORDER);
\r
7396 case WM_GETMINMAXINFO:
\r
7397 /* Prevent resizing window too small */
\r
7398 mmi = (MINMAXINFO *) lParam;
\r
7399 mmi->ptMinTrackSize.x = 100;
\r
7400 mmi->ptMinTrackSize.y = 100;
\r
7403 /* [AS] Snapping */
\r
7404 case WM_ENTERSIZEMOVE:
\r
7405 return OnEnterSizeMove( &sd, hDlg, wParam, lParam );
\r
7408 return OnSizing( &sd, hDlg, wParam, lParam );
\r
7411 return OnMoving( &sd, hDlg, wParam, lParam );
\r
7413 case WM_EXITSIZEMOVE:
\r
7414 UpdateICSWidth(hText);
\r
7415 return OnExitSizeMove( &sd, hDlg, wParam, lParam );
\r
7418 return DefWindowProc(hDlg, message, wParam, lParam);
\r
7426 if (hwndConsole) return;
\r
7427 hCons = CreateDialog(hInst, szConsoleName, 0, NULL);
\r
7428 SendMessage(hCons, WM_INITDIALOG, 0, 0);
\r
7433 ConsoleOutput(char* data, int length, int forceVisible)
\r
7438 char buf[CO_MAX+1];
\r
7441 static int delayLF = 0;
\r
7442 CHARRANGE savesel, sel;
\r
7444 if (hwndConsole == NULL || length > CO_MAX-100 || length == 0) return;
\r
7452 while (length--) {
\r
7460 } else if (*p == '\007') {
\r
7461 MyPlaySound(&sounds[(int)SoundBell]);
\r
7468 hText = GetDlgItem(hwndConsole, OPT_ConsoleText);
\r
7469 SendMessage(hText, EM_HIDESELECTION, TRUE, FALSE);
\r
7470 /* Save current selection */
\r
7471 SendMessage(hText, EM_EXGETSEL, 0, (LPARAM)&savesel);
\r
7472 exlen = GetWindowTextLength(hText);
\r
7473 /* Find out whether current end of text is visible */
\r
7474 SendMessage(hText, EM_GETRECT, 0, (LPARAM) &rect);
\r
7475 SendMessage(hText, EM_POSFROMCHAR, (WPARAM) &pEnd, exlen);
\r
7476 /* Trim existing text if it's too long */
\r
7477 if (exlen + (q - buf) > CO_MAX) {
\r
7478 trim = (CO_TRIM > (q - buf)) ? CO_TRIM : (q - buf);
\r
7481 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
7482 SendMessage(hText, EM_REPLACESEL, 0, (LPARAM)"");
\r
7484 savesel.cpMin -= trim;
\r
7485 savesel.cpMax -= trim;
\r
7486 if (exlen < 0) exlen = 0;
\r
7487 if (savesel.cpMin < 0) savesel.cpMin = 0;
\r
7488 if (savesel.cpMax < savesel.cpMin) savesel.cpMax = savesel.cpMin;
\r
7490 /* Append the new text */
\r
7491 sel.cpMin = exlen;
\r
7492 sel.cpMax = exlen;
\r
7493 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
7494 SendMessage(hText, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&consoleCF);
\r
7495 SendMessage(hText, EM_REPLACESEL, 0, (LPARAM) buf);
\r
7496 if (forceVisible || exlen == 0 ||
\r
7497 (rect.left <= pEnd.x && pEnd.x < rect.right &&
\r
7498 rect.top <= pEnd.y && pEnd.y < rect.bottom)) {
\r
7499 /* Scroll to make new end of text visible if old end of text
\r
7500 was visible or new text is an echo of user typein */
\r
7501 sel.cpMin = 9999999;
\r
7502 sel.cpMax = 9999999;
\r
7503 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
7504 SendMessage(hText, EM_HIDESELECTION, FALSE, FALSE);
\r
7505 SendMessage(hText, EM_SCROLLCARET, 0, 0);
\r
7506 SendMessage(hText, EM_HIDESELECTION, TRUE, FALSE);
\r
7508 if (savesel.cpMax == exlen || forceVisible) {
\r
7509 /* Move insert point to new end of text if it was at the old
\r
7510 end of text or if the new text is an echo of user typein */
\r
7511 sel.cpMin = 9999999;
\r
7512 sel.cpMax = 9999999;
\r
7513 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
7515 /* Restore previous selection */
\r
7516 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&savesel);
\r
7518 SendMessage(hText, EM_HIDESELECTION, FALSE, FALSE);
\r
7525 DisplayHoldingsCount(HDC hdc, int x, int y, int rightAlign, int copyNumber)
\r
7529 COLORREF oldFg, oldBg;
\r
7533 if(copyNumber > 1)
\r
7534 snprintf(buf, sizeof(buf)/sizeof(buf[0]),"%d", copyNumber); else buf[0] = 0;
\r
7536 oldFg = SetTextColor(hdc, RGB(255, 255, 255)); /* white */
\r
7537 oldBg = SetBkColor(hdc, RGB(0, 0, 0)); /* black */
\r
7538 oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);
\r
7541 rect.right = x + squareSize;
\r
7543 rect.bottom = y + squareSize;
\r
7546 ExtTextOut(hdc, x + MESSAGE_LINE_LEFTMARGIN
\r
7547 + (rightAlign ? (squareSize*2)/3 : 0),
\r
7548 y, ETO_CLIPPED|ETO_OPAQUE,
\r
7549 &rect, str, strlen(str), NULL);
\r
7551 (void) SetTextColor(hdc, oldFg);
\r
7552 (void) SetBkColor(hdc, oldBg);
\r
7553 (void) SelectObject(hdc, oldFont);
\r
7557 DisplayAClock(HDC hdc, int timeRemaining, int highlight,
\r
7558 RECT *rect, char *color, char *flagFell)
\r
7562 COLORREF oldFg, oldBg;
\r
7565 if (twoBoards && partnerUp) return;
\r
7566 if (appData.clockMode) {
\r
7568 snprintf(buf, sizeof(buf)/sizeof(buf[0]), "%c %s %s", color[0], TimeString(timeRemaining), flagFell);
\r
7570 snprintf(buf, sizeof(buf)/sizeof(buf[0]), "%s:%c%s %s", color, (logoHeight>0 ? 0 : ' '), TimeString(timeRemaining), flagFell);
\r
7577 oldFg = SetTextColor(hdc, RGB(255, 255, 255)); /* white */
\r
7578 oldBg = SetBkColor(hdc, RGB(0, 0, 0)); /* black */
\r
7580 oldFg = SetTextColor(hdc, RGB(0, 0, 0)); /* black */
\r
7581 oldBg = SetBkColor(hdc, RGB(255, 255, 255)); /* white */
\r
7583 oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);
\r
7587 ExtTextOut(hdc, rect->left + MESSAGE_LINE_LEFTMARGIN,
\r
7588 rect->top, ETO_CLIPPED|ETO_OPAQUE,
\r
7589 rect, str, strlen(str), NULL);
\r
7590 if(logoHeight > 0 && appData.clockMode) {
\r
7592 str += strlen(color)+2;
\r
7593 r.top = rect->top + logoHeight/2;
\r
7594 r.left = rect->left;
\r
7595 r.right = rect->right;
\r
7596 r.bottom = rect->bottom;
\r
7597 ExtTextOut(hdc, rect->left + MESSAGE_LINE_LEFTMARGIN,
\r
7598 r.top, ETO_CLIPPED|ETO_OPAQUE,
\r
7599 &r, str, strlen(str), NULL);
\r
7601 (void) SetTextColor(hdc, oldFg);
\r
7602 (void) SetBkColor(hdc, oldBg);
\r
7603 (void) SelectObject(hdc, oldFont);
\r
7608 DoReadFile(HANDLE hFile, char *buf, int count, DWORD *outCount,
\r
7614 if( count <= 0 ) {
\r
7615 if (appData.debugMode) {
\r
7616 fprintf( debugFP, "DoReadFile: trying to read past end of buffer, overflow = %d\n", count );
\r
7619 return ERROR_INVALID_USER_BUFFER;
\r
7622 ResetEvent(ovl->hEvent);
\r
7623 ovl->Offset = ovl->OffsetHigh = 0;
\r
7624 ok = ReadFile(hFile, buf, count, outCount, ovl);
\r
7628 err = GetLastError();
\r
7629 if (err == ERROR_IO_PENDING) {
\r
7630 ok = GetOverlappedResult(hFile, ovl, outCount, TRUE);
\r
7634 err = GetLastError();
\r
7641 DoWriteFile(HANDLE hFile, char *buf, int count, DWORD *outCount,
\r
7646 ResetEvent(ovl->hEvent);
\r
7647 ovl->Offset = ovl->OffsetHigh = 0;
\r
7648 ok = WriteFile(hFile, buf, count, outCount, ovl);
\r
7652 err = GetLastError();
\r
7653 if (err == ERROR_IO_PENDING) {
\r
7654 ok = GetOverlappedResult(hFile, ovl, outCount, TRUE);
\r
7658 err = GetLastError();
\r
7664 /* [AS] If input is line by line and a line exceed the buffer size, force an error */
\r
7665 void CheckForInputBufferFull( InputSource * is )
\r
7667 if( is->lineByLine && (is->next - is->buf) >= INPUT_SOURCE_BUF_SIZE ) {
\r
7668 /* Look for end of line */
\r
7669 char * p = is->buf;
\r
7671 while( p < is->next && *p != '\n' ) {
\r
7675 if( p >= is->next ) {
\r
7676 if (appData.debugMode) {
\r
7677 fprintf( debugFP, "Input line exceeded buffer size (source id=%lu)\n", is->id );
\r
7680 is->error = ERROR_BROKEN_PIPE; /* [AS] Just any non-successful code! */
\r
7681 is->count = (DWORD) -1;
\r
7682 is->next = is->buf;
\r
7688 InputThread(LPVOID arg)
\r
7693 is = (InputSource *) arg;
\r
7694 ovl.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
\r
7695 ovl.Internal = ovl.InternalHigh = ovl.Offset = ovl.OffsetHigh = 0;
\r
7696 while (is->hThread != NULL) {
\r
7697 is->error = DoReadFile(is->hFile, is->next,
\r
7698 INPUT_SOURCE_BUF_SIZE - (is->next - is->buf),
\r
7699 &is->count, &ovl);
\r
7700 if (is->error == NO_ERROR) {
\r
7701 is->next += is->count;
\r
7703 if (is->error == ERROR_BROKEN_PIPE) {
\r
7704 /* Correct for MS brain damage. EOF reading a pipe is not an error. */
\r
7707 is->count = (DWORD) -1;
\r
7708 /* [AS] The (is->count <= 0) check below is not useful for unsigned values! */
\r
7713 CheckForInputBufferFull( is );
\r
7715 SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);
\r
7717 if( is->count == ((DWORD) -1) ) break; /* [AS] */
\r
7719 if (is->count <= 0) break; /* Quit on EOF or error */
\r
7722 CloseHandle(ovl.hEvent);
\r
7723 CloseHandle(is->hFile);
\r
7725 if (appData.debugMode) {
\r
7726 fprintf( debugFP, "Input thread terminated (id=%lu, error=%d, count=%ld)\n", is->id, is->error, is->count );
\r
7733 /* Windows 95 beta 2 won't let you do overlapped i/o on a console or pipe */
\r
7735 NonOvlInputThread(LPVOID arg)
\r
7742 is = (InputSource *) arg;
\r
7743 while (is->hThread != NULL) {
\r
7744 is->error = ReadFile(is->hFile, is->next,
\r
7745 INPUT_SOURCE_BUF_SIZE - (is->next - is->buf),
\r
7746 &is->count, NULL) ? NO_ERROR : GetLastError();
\r
7747 if (is->error == NO_ERROR) {
\r
7748 /* Change CRLF to LF */
\r
7749 if (is->next > is->buf) {
\r
7751 i = is->count + 1;
\r
7759 if (prev == '\r' && *p == '\n') {
\r
7771 if (is->error == ERROR_BROKEN_PIPE) {
\r
7772 /* Correct for MS brain damage. EOF reading a pipe is not an error. */
\r
7775 is->count = (DWORD) -1;
\r
7779 CheckForInputBufferFull( is );
\r
7781 SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);
\r
7783 if( is->count == ((DWORD) -1) ) break; /* [AS] */
\r
7785 if (is->count < 0) break; /* Quit on error */
\r
7787 CloseHandle(is->hFile);
\r
7792 SocketInputThread(LPVOID arg)
\r
7796 is = (InputSource *) arg;
\r
7797 while (is->hThread != NULL) {
\r
7798 is->count = recv(is->sock, is->buf, INPUT_SOURCE_BUF_SIZE, 0);
\r
7799 if ((int)is->count == SOCKET_ERROR) {
\r
7800 is->count = (DWORD) -1;
\r
7801 is->error = WSAGetLastError();
\r
7803 is->error = NO_ERROR;
\r
7804 is->next += is->count;
\r
7805 if (is->count == 0 && is->second == is) {
\r
7806 /* End of file on stderr; quit with no message */
\r
7810 SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);
\r
7812 if( is->count == ((DWORD) -1) ) break; /* [AS] */
\r
7814 if (is->count <= 0) break; /* Quit on EOF or error */
\r
7820 InputEvent(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
\r
7824 is = (InputSource *) lParam;
\r
7825 if (is->lineByLine) {
\r
7826 /* Feed in lines one by one */
\r
7827 char *p = is->buf;
\r
7829 while (q < is->next) {
\r
7830 if (*q++ == '\n') {
\r
7831 (is->func)(is, is->closure, p, q - p, NO_ERROR);
\r
7836 /* Move any partial line to the start of the buffer */
\r
7838 while (p < is->next) {
\r
7843 if (is->error != NO_ERROR || is->count == 0) {
\r
7844 /* Notify backend of the error. Note: If there was a partial
\r
7845 line at the end, it is not flushed through. */
\r
7846 (is->func)(is, is->closure, is->buf, is->count, is->error);
\r
7849 /* Feed in the whole chunk of input at once */
\r
7850 (is->func)(is, is->closure, is->buf, is->count, is->error);
\r
7851 is->next = is->buf;
\r
7855 /*---------------------------------------------------------------------------*\
\r
7857 * Menu enables. Used when setting various modes.
\r
7859 \*---------------------------------------------------------------------------*/
\r
7867 GreyRevert(Boolean grey)
\r
7868 { // [HGM] vari: for retracting variations in local mode
\r
7869 HMENU hmenu = GetMenu(hwndMain);
\r
7870 EnableMenuItem(hmenu, IDM_Revert, MF_BYCOMMAND|(grey ? MF_GRAYED : MF_ENABLED));
\r
7871 EnableMenuItem(hmenu, IDM_Annotate, MF_BYCOMMAND|(grey ? MF_GRAYED : MF_ENABLED));
\r
7875 SetMenuEnables(HMENU hmenu, Enables *enab)
\r
7877 while (enab->item > 0) {
\r
7878 (void) EnableMenuItem(hmenu, enab->item, enab->flags);
\r
7883 Enables gnuEnables[] = {
\r
7884 { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },
\r
7885 { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },
\r
7886 { IDM_IcsClient, MF_BYCOMMAND|MF_GRAYED },
\r
7887 { IDM_Accept, MF_BYCOMMAND|MF_GRAYED },
\r
7888 { IDM_Decline, MF_BYCOMMAND|MF_GRAYED },
\r
7889 { IDM_Rematch, MF_BYCOMMAND|MF_GRAYED },
\r
7890 { IDM_Adjourn, MF_BYCOMMAND|MF_GRAYED },
\r
7891 { IDM_StopExamining, MF_BYCOMMAND|MF_GRAYED },
\r
7892 { IDM_StopObserving, MF_BYCOMMAND|MF_GRAYED },
\r
7893 { IDM_Upload, MF_BYCOMMAND|MF_GRAYED },
\r
7894 { IDM_Revert, MF_BYCOMMAND|MF_GRAYED },
\r
7895 { IDM_Annotate, MF_BYCOMMAND|MF_GRAYED },
\r
7896 { IDM_NewChat, MF_BYCOMMAND|MF_GRAYED },
\r
7898 // Needed to switch from ncp to GNU mode on Engine Load
\r
7899 { ACTION_POS, MF_BYPOSITION|MF_ENABLED },
\r
7900 { IDM_MachineWhite, MF_BYCOMMAND|MF_ENABLED },
\r
7901 { IDM_MachineBlack, MF_BYCOMMAND|MF_ENABLED },
\r
7902 { IDM_TwoMachines, MF_BYCOMMAND|MF_ENABLED },
\r
7903 { IDM_Match, MF_BYCOMMAND|MF_ENABLED },
\r
7904 { IDM_AnalysisMode, MF_BYCOMMAND|MF_ENABLED },
\r
7905 { IDM_AnalyzeFile, MF_BYCOMMAND|MF_ENABLED },
\r
7906 { IDM_Engine1Options, MF_BYCOMMAND|MF_ENABLED },
\r
7907 { IDM_Engine2Options, MF_BYCOMMAND|MF_ENABLED },
\r
7908 { IDM_TimeControl, MF_BYCOMMAND|MF_ENABLED },
\r
7909 { IDM_RetractMove, MF_BYCOMMAND|MF_ENABLED },
\r
7910 { IDM_MoveNow, MF_BYCOMMAND|MF_ENABLED },
\r
7911 { IDM_Hint, MF_BYCOMMAND|MF_ENABLED },
\r
7912 { IDM_Book, MF_BYCOMMAND|MF_ENABLED },
\r
7916 Enables icsEnables[] = {
\r
7917 { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },
\r
7918 { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },
\r
7919 { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },
\r
7920 { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },
\r
7921 { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },
\r
7922 { IDM_Match, MF_BYCOMMAND|MF_GRAYED },
\r
7923 { IDM_MachineBoth, MF_BYCOMMAND|MF_GRAYED },
\r
7924 { IDM_AnalysisMode, MF_BYCOMMAND|MF_ENABLED },
\r
7925 { IDM_AnalyzeFile, MF_BYCOMMAND|MF_GRAYED },
\r
7926 { IDM_TimeControl, MF_BYCOMMAND|MF_GRAYED },
\r
7927 { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },
\r
7928 { IDM_Hint, MF_BYCOMMAND|MF_GRAYED },
\r
7929 { IDM_Book, MF_BYCOMMAND|MF_GRAYED },
\r
7930 { IDM_LoadProg1, MF_BYCOMMAND|MF_GRAYED },
\r
7931 { IDM_LoadProg2, MF_BYCOMMAND|MF_GRAYED },
\r
7932 { IDM_IcsOptions, MF_BYCOMMAND|MF_ENABLED },
\r
7933 { IDM_Engine1Options, MF_BYCOMMAND|MF_GRAYED },
\r
7934 { IDM_Engine2Options, MF_BYCOMMAND|MF_GRAYED },
\r
7935 { IDM_Annotate, MF_BYCOMMAND|MF_GRAYED },
\r
7936 { IDM_Tourney, MF_BYCOMMAND|MF_GRAYED },
\r
7941 Enables zippyEnables[] = {
\r
7942 { IDM_MoveNow, MF_BYCOMMAND|MF_ENABLED },
\r
7943 { IDM_Hint, MF_BYCOMMAND|MF_ENABLED },
\r
7944 { IDM_Book, MF_BYCOMMAND|MF_ENABLED },
\r
7945 { IDM_Engine1Options, MF_BYCOMMAND|MF_ENABLED },
\r
7950 Enables ncpEnables[] = {
\r
7951 { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },
\r
7952 { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },
\r
7953 { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },
\r
7954 { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },
\r
7955 { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },
\r
7956 { IDM_Match, MF_BYCOMMAND|MF_GRAYED },
\r
7957 { IDM_AnalysisMode, MF_BYCOMMAND|MF_GRAYED },
\r
7958 { IDM_AnalyzeFile, MF_BYCOMMAND|MF_GRAYED },
\r
7959 { IDM_IcsClient, MF_BYCOMMAND|MF_GRAYED },
\r
7960 { ACTION_POS, MF_BYPOSITION|MF_GRAYED },
\r
7961 { IDM_Revert, MF_BYCOMMAND|MF_GRAYED },
\r
7962 { IDM_Annotate, MF_BYCOMMAND|MF_GRAYED },
\r
7963 { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },
\r
7964 { IDM_RetractMove, MF_BYCOMMAND|MF_GRAYED },
\r
7965 { IDM_TimeControl, MF_BYCOMMAND|MF_GRAYED },
\r
7966 { IDM_Hint, MF_BYCOMMAND|MF_GRAYED },
\r
7967 { IDM_Book, MF_BYCOMMAND|MF_GRAYED },
\r
7968 { IDM_MachineBoth, MF_BYCOMMAND|MF_GRAYED },
\r
7969 { IDM_NewChat, MF_BYCOMMAND|MF_GRAYED },
\r
7970 { IDM_Engine1Options, MF_BYCOMMAND|MF_GRAYED },
\r
7971 { IDM_Engine2Options, MF_BYCOMMAND|MF_GRAYED },
\r
7972 { IDM_Sounds, MF_BYCOMMAND|MF_GRAYED },
\r
7976 Enables trainingOnEnables[] = {
\r
7977 { IDM_EditComment, MF_BYCOMMAND|MF_GRAYED },
\r
7978 { IDM_Comment, MF_BYCOMMAND|MF_GRAYED },
\r
7979 { IDM_Pause, MF_BYCOMMAND|MF_GRAYED },
\r
7980 { IDM_Forward, MF_BYCOMMAND|MF_GRAYED },
\r
7981 { IDM_Backward, MF_BYCOMMAND|MF_GRAYED },
\r
7982 { IDM_ToEnd, MF_BYCOMMAND|MF_GRAYED },
\r
7983 { IDM_ToStart, MF_BYCOMMAND|MF_GRAYED },
\r
7984 { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },
\r
7985 { IDM_TruncateGame, MF_BYCOMMAND|MF_GRAYED },
\r
7989 Enables trainingOffEnables[] = {
\r
7990 { IDM_EditComment, MF_BYCOMMAND|MF_ENABLED },
\r
7991 { IDM_Comment, MF_BYCOMMAND|MF_ENABLED },
\r
7992 { IDM_Pause, MF_BYCOMMAND|MF_ENABLED },
\r
7993 { IDM_Forward, MF_BYCOMMAND|MF_ENABLED },
\r
7994 { IDM_Backward, MF_BYCOMMAND|MF_ENABLED },
\r
7995 { IDM_ToEnd, MF_BYCOMMAND|MF_ENABLED },
\r
7996 { IDM_ToStart, MF_BYCOMMAND|MF_ENABLED },
\r
7997 { IDM_MoveNow, MF_BYCOMMAND|MF_ENABLED },
\r
7998 { IDM_TruncateGame, MF_BYCOMMAND|MF_ENABLED },
\r
8002 /* These modify either ncpEnables or gnuEnables */
\r
8003 Enables cmailEnables[] = {
\r
8004 { IDM_MailMove, MF_BYCOMMAND|MF_ENABLED },
\r
8005 { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_ENABLED },
\r
8006 { ACTION_POS, MF_BYPOSITION|MF_ENABLED },
\r
8007 { IDM_CallFlag, MF_BYCOMMAND|MF_GRAYED },
\r
8008 { IDM_Draw, MF_BYCOMMAND|MF_ENABLED },
\r
8009 { IDM_Adjourn, MF_BYCOMMAND|MF_GRAYED },
\r
8010 { IDM_Abort, MF_BYCOMMAND|MF_GRAYED },
\r
8014 Enables machineThinkingEnables[] = {
\r
8015 { IDM_LoadGame, MF_BYCOMMAND|MF_GRAYED },
\r
8016 { IDM_LoadNextGame, MF_BYCOMMAND|MF_GRAYED },
\r
8017 { IDM_LoadPrevGame, MF_BYCOMMAND|MF_GRAYED },
\r
8018 { IDM_ReloadGame, MF_BYCOMMAND|MF_GRAYED },
\r
8019 { IDM_PasteGame, MF_BYCOMMAND|MF_GRAYED },
\r
8020 { IDM_LoadPosition, MF_BYCOMMAND|MF_GRAYED },
\r
8021 { IDM_LoadNextPosition, MF_BYCOMMAND|MF_GRAYED },
\r
8022 { IDM_LoadPrevPosition, MF_BYCOMMAND|MF_GRAYED },
\r
8023 { IDM_ReloadPosition, MF_BYCOMMAND|MF_GRAYED },
\r
8024 { IDM_PastePosition, MF_BYCOMMAND|MF_GRAYED },
\r
8025 { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },
\r
8026 { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },
\r
8027 { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },
\r
8028 // { IDM_Match, MF_BYCOMMAND|MF_GRAYED },
\r
8029 { IDM_TypeInMove, MF_BYCOMMAND|MF_GRAYED },
\r
8030 { IDM_RetractMove, MF_BYCOMMAND|MF_GRAYED },
\r
8034 Enables userThinkingEnables[] = {
\r
8035 { IDM_LoadGame, MF_BYCOMMAND|MF_ENABLED },
\r
8036 { IDM_LoadNextGame, MF_BYCOMMAND|MF_ENABLED },
\r
8037 { IDM_LoadPrevGame, MF_BYCOMMAND|MF_ENABLED },
\r
8038 { IDM_ReloadGame, MF_BYCOMMAND|MF_ENABLED },
\r
8039 { IDM_PasteGame, MF_BYCOMMAND|MF_ENABLED },
\r
8040 { IDM_LoadPosition, MF_BYCOMMAND|MF_ENABLED },
\r
8041 { IDM_LoadNextPosition, MF_BYCOMMAND|MF_ENABLED },
\r
8042 { IDM_LoadPrevPosition, MF_BYCOMMAND|MF_ENABLED },
\r
8043 { IDM_ReloadPosition, MF_BYCOMMAND|MF_ENABLED },
\r
8044 { IDM_PastePosition, MF_BYCOMMAND|MF_ENABLED },
\r
8045 { IDM_MachineWhite, MF_BYCOMMAND|MF_ENABLED },
\r
8046 { IDM_MachineBlack, MF_BYCOMMAND|MF_ENABLED },
\r
8047 { IDM_TwoMachines, MF_BYCOMMAND|MF_ENABLED },
\r
8048 // { IDM_Match, MF_BYCOMMAND|MF_ENABLED },
\r
8049 { IDM_TypeInMove, MF_BYCOMMAND|MF_ENABLED },
\r
8050 { IDM_RetractMove, MF_BYCOMMAND|MF_ENABLED },
\r
8054 /*---------------------------------------------------------------------------*\
\r
8056 * Front-end interface functions exported by XBoard.
\r
8057 * Functions appear in same order as prototypes in frontend.h.
\r
8059 \*---------------------------------------------------------------------------*/
\r
8061 CheckMark(UINT item, int state)
\r
8063 if(item) CheckMenuItem(GetMenu(hwndMain), item, MF_BYCOMMAND|state);
\r
8069 static UINT prevChecked = 0;
\r
8070 static int prevPausing = 0;
\r
8073 if (pausing != prevPausing) {
\r
8074 prevPausing = pausing;
\r
8075 (void) CheckMenuItem(GetMenu(hwndMain), IDM_Pause,
\r
8076 MF_BYCOMMAND|(pausing ? MF_CHECKED : MF_UNCHECKED));
\r
8077 if (hwndPause) SetWindowText(hwndPause, pausing ? "C" : "P");
\r
8080 switch (gameMode) {
\r
8081 case BeginningOfGame:
\r
8082 if (appData.icsActive)
\r
8083 nowChecked = IDM_IcsClient;
\r
8084 else if (appData.noChessProgram)
\r
8085 nowChecked = IDM_EditGame;
\r
8087 nowChecked = IDM_MachineBlack;
\r
8089 case MachinePlaysBlack:
\r
8090 nowChecked = IDM_MachineBlack;
\r
8092 case MachinePlaysWhite:
\r
8093 nowChecked = IDM_MachineWhite;
\r
8095 case TwoMachinesPlay:
\r
8096 nowChecked = IDM_TwoMachines;
\r
8099 nowChecked = IDM_AnalysisMode;
\r
8102 nowChecked = IDM_AnalyzeFile;
\r
8105 nowChecked = IDM_EditGame;
\r
8107 case PlayFromGameFile:
\r
8108 nowChecked = IDM_LoadGame;
\r
8110 case EditPosition:
\r
8111 nowChecked = IDM_EditPosition;
\r
8114 nowChecked = IDM_Training;
\r
8116 case IcsPlayingWhite:
\r
8117 case IcsPlayingBlack:
\r
8118 case IcsObserving:
\r
8120 nowChecked = IDM_IcsClient;
\r
8127 CheckMark(prevChecked, MF_UNCHECKED);
\r
8128 CheckMark(nowChecked, MF_CHECKED);
\r
8129 CheckMark(IDM_Match, matchMode && matchGame < appData.matchGames ? MF_CHECKED : MF_UNCHECKED);
\r
8131 if (nowChecked == IDM_LoadGame || nowChecked == IDM_Training) {
\r
8132 (void) EnableMenuItem(GetMenu(hwndMain), IDM_Training,
\r
8133 MF_BYCOMMAND|MF_ENABLED);
\r
8135 (void) EnableMenuItem(GetMenu(hwndMain),
\r
8136 IDM_Training, MF_BYCOMMAND|MF_GRAYED);
\r
8139 prevChecked = nowChecked;
\r
8141 /* [DM] icsEngineAnalyze - Do a sceure check too */
\r
8142 if (appData.icsActive) {
\r
8143 if (appData.icsEngineAnalyze) {
\r
8144 CheckMark(IDM_AnalysisMode, MF_CHECKED);
\r
8146 CheckMark(IDM_AnalysisMode, MF_UNCHECKED);
\r
8149 DisplayLogos(); // [HGM] logos: mode change could have altered logos
\r
8155 HMENU hmenu = GetMenu(hwndMain);
\r
8156 SetMenuEnables(hmenu, icsEnables);
\r
8157 EnableMenuItem(GetSubMenu(hmenu, OPTIONS_POS), IDM_IcsOptions,
\r
8158 MF_BYCOMMAND|MF_ENABLED);
\r
8160 if (appData.zippyPlay) {
\r
8161 SetMenuEnables(hmenu, zippyEnables);
\r
8162 if (!appData.noChessProgram) /* [DM] icsEngineAnalyze */
\r
8163 (void) EnableMenuItem(GetMenu(hwndMain), IDM_AnalysisMode,
\r
8164 MF_BYCOMMAND|MF_ENABLED);
\r
8172 SetMenuEnables(GetMenu(hwndMain), gnuEnables);
\r
8178 HMENU hmenu = GetMenu(hwndMain);
\r
8179 SetMenuEnables(hmenu, ncpEnables);
\r
8180 DrawMenuBar(hwndMain);
\r
8186 SetMenuEnables(GetMenu(hwndMain), cmailEnables);
\r
8190 SetTrainingModeOn()
\r
8193 SetMenuEnables(GetMenu(hwndMain), trainingOnEnables);
\r
8194 for (i = 0; i < N_BUTTONS; i++) {
\r
8195 if (buttonDesc[i].hwnd != NULL)
\r
8196 EnableWindow(buttonDesc[i].hwnd, FALSE);
\r
8201 VOID SetTrainingModeOff()
\r
8204 SetMenuEnables(GetMenu(hwndMain), trainingOffEnables);
\r
8205 for (i = 0; i < N_BUTTONS; i++) {
\r
8206 if (buttonDesc[i].hwnd != NULL)
\r
8207 EnableWindow(buttonDesc[i].hwnd, TRUE);
\r
8213 SetUserThinkingEnables()
\r
8215 SetMenuEnables(GetMenu(hwndMain), userThinkingEnables);
\r
8219 SetMachineThinkingEnables()
\r
8221 HMENU hMenu = GetMenu(hwndMain);
\r
8222 int flags = MF_BYCOMMAND|MF_ENABLED;
\r
8224 SetMenuEnables(hMenu, machineThinkingEnables);
\r
8226 if (gameMode == MachinePlaysBlack) {
\r
8227 (void)EnableMenuItem(hMenu, IDM_MachineBlack, flags);
\r
8228 } else if (gameMode == MachinePlaysWhite) {
\r
8229 (void)EnableMenuItem(hMenu, IDM_MachineWhite, flags);
\r
8230 } else if (gameMode == TwoMachinesPlay) {
\r
8231 (void)EnableMenuItem(hMenu, matchMode ? IDM_Match : IDM_TwoMachines, flags); // [HGM] match
\r
8237 DisplayTitle(char *str)
\r
8239 char title[MSG_SIZ], *host;
\r
8240 if (str[0] != NULLCHAR) {
\r
8241 safeStrCpy(title, str, sizeof(title)/sizeof(title[0]) );
\r
8242 } else if (appData.icsActive) {
\r
8243 if (appData.icsCommPort[0] != NULLCHAR)
\r
8246 host = appData.icsHost;
\r
8247 snprintf(title, MSG_SIZ, "%s: %s", szTitle, host);
\r
8248 } else if (appData.noChessProgram) {
\r
8249 safeStrCpy(title, szTitle, sizeof(title)/sizeof(title[0]) );
\r
8251 safeStrCpy(title, szTitle, sizeof(title)/sizeof(title[0]) );
\r
8252 strcat(title, ": ");
\r
8253 strcat(title, first.tidy);
\r
8255 SetWindowText(hwndMain, title);
\r
8260 DisplayMessage(char *str1, char *str2)
\r
8264 int remain = MESSAGE_TEXT_MAX - 1;
\r
8267 moveErrorMessageUp = FALSE; /* turned on later by caller if needed */
\r
8268 messageText[0] = NULLCHAR;
\r
8270 len = strlen(str1);
\r
8271 if (len > remain) len = remain;
\r
8272 strncpy(messageText, str1, len);
\r
8273 messageText[len] = NULLCHAR;
\r
8276 if (*str2 && remain >= 2) {
\r
8278 strcat(messageText, " ");
\r
8281 len = strlen(str2);
\r
8282 if (len > remain) len = remain;
\r
8283 strncat(messageText, str2, len);
\r
8285 messageText[MESSAGE_TEXT_MAX - 1] = NULLCHAR;
\r
8286 safeStrCpy(lastMsg, messageText, MSG_SIZ);
\r
8288 if (hwndMain == NULL || IsIconic(hwndMain)) return;
\r
8292 hdc = GetDC(hwndMain);
\r
8293 oldFont = SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);
\r
8294 ExtTextOut(hdc, messageRect.left, messageRect.top, ETO_CLIPPED|ETO_OPAQUE,
\r
8295 &messageRect, messageText, strlen(messageText), NULL);
\r
8296 (void) SelectObject(hdc, oldFont);
\r
8297 (void) ReleaseDC(hwndMain, hdc);
\r
8301 DisplayError(char *str, int error)
\r
8303 char buf[MSG_SIZ*2], buf2[MSG_SIZ];
\r
8307 safeStrCpy(buf, str, sizeof(buf)/sizeof(buf[0]) );
\r
8309 len = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,
\r
8310 NULL, error, LANG_NEUTRAL,
\r
8311 (LPSTR) buf2, MSG_SIZ, NULL);
\r
8313 snprintf(buf, 2*MSG_SIZ, "%s:\n%s", str, buf2);
\r
8315 ErrorMap *em = errmap;
\r
8316 while (em->err != 0 && em->err != error) em++;
\r
8317 if (em->err != 0) {
\r
8318 snprintf(buf, 2*MSG_SIZ, "%s:\n%s", str, em->msg);
\r
8320 snprintf(buf, 2*MSG_SIZ, "%s:\nError code %d", str, error);
\r
8325 ErrorPopUp(_("Error"), buf);
\r
8330 DisplayMoveError(char *str)
\r
8332 fromX = fromY = -1;
\r
8333 ClearHighlights();
\r
8334 DrawPosition(FALSE, NULL);
\r
8335 if (appData.popupMoveErrors) {
\r
8336 ErrorPopUp(_("Error"), str);
\r
8338 DisplayMessage(str, "");
\r
8339 moveErrorMessageUp = TRUE;
\r
8344 DisplayFatalError(char *str, int error, int exitStatus)
\r
8346 char buf[2*MSG_SIZ], buf2[MSG_SIZ];
\r
8348 char *label = exitStatus ? _("Fatal Error") : _("Exiting");
\r
8351 len = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,
\r
8352 NULL, error, LANG_NEUTRAL,
\r
8353 (LPSTR) buf2, MSG_SIZ, NULL);
\r
8355 snprintf(buf, 2*MSG_SIZ, "%s:\n%s", str, buf2);
\r
8357 ErrorMap *em = errmap;
\r
8358 while (em->err != 0 && em->err != error) em++;
\r
8359 if (em->err != 0) {
\r
8360 snprintf(buf, 2*MSG_SIZ, "%s:\n%s", str, em->msg);
\r
8362 snprintf(buf, 2*MSG_SIZ, "%s:\nError code %d", str, error);
\r
8367 if (appData.debugMode) {
\r
8368 fprintf(debugFP, "%s: %s\n", label, str);
\r
8370 if (appData.popupExitMessage) {
\r
8371 (void) MessageBox(hwndMain, str, label, MB_OK|
\r
8372 (exitStatus ? MB_ICONSTOP : MB_ICONINFORMATION));
\r
8374 ExitEvent(exitStatus);
\r
8379 DisplayInformation(char *str)
\r
8381 (void) MessageBox(hwndMain, str, _("Information"), MB_OK|MB_ICONINFORMATION);
\r
8386 DisplayNote(char *str)
\r
8388 ErrorPopUp(_("Note"), str);
\r
8393 char *title, *question, *replyPrefix;
\r
8398 QuestionDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
8400 static QuestionParams *qp;
\r
8401 char reply[MSG_SIZ];
\r
8404 switch (message) {
\r
8405 case WM_INITDIALOG:
\r
8406 qp = (QuestionParams *) lParam;
\r
8407 CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));
\r
8408 Translate(hDlg, DLG_Question);
\r
8409 SetWindowText(hDlg, qp->title);
\r
8410 SetDlgItemText(hDlg, OPT_QuestionText, qp->question);
\r
8411 SetFocus(GetDlgItem(hDlg, OPT_QuestionInput));
\r
8415 switch (LOWORD(wParam)) {
\r
8417 safeStrCpy(reply, qp->replyPrefix, sizeof(reply)/sizeof(reply[0]) );
\r
8418 if (*reply) strcat(reply, " ");
\r
8419 len = strlen(reply);
\r
8420 GetDlgItemText(hDlg, OPT_QuestionInput, reply + len, sizeof(reply) - len);
\r
8421 strcat(reply, "\n");
\r
8422 OutputToProcess(qp->pr, reply, strlen(reply), &err);
\r
8423 EndDialog(hDlg, TRUE);
\r
8424 if (err) DisplayFatalError(_("Error writing to chess program"), err, 1);
\r
8427 EndDialog(hDlg, FALSE);
\r
8438 AskQuestion(char* title, char *question, char *replyPrefix, ProcRef pr)
\r
8440 QuestionParams qp;
\r
8444 qp.question = question;
\r
8445 qp.replyPrefix = replyPrefix;
\r
8447 lpProc = MakeProcInstance((FARPROC)QuestionDialog, hInst);
\r
8448 DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_Question),
\r
8449 hwndMain, (DLGPROC)lpProc, (LPARAM)&qp);
\r
8450 FreeProcInstance(lpProc);
\r
8453 /* [AS] Pick FRC position */
\r
8454 LRESULT CALLBACK NewGameFRC_Proc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
8456 static int * lpIndexFRC;
\r
8462 case WM_INITDIALOG:
\r
8463 lpIndexFRC = (int *) lParam;
\r
8465 CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));
\r
8466 Translate(hDlg, DLG_NewGameFRC);
\r
8468 SendDlgItemMessage( hDlg, IDC_NFG_Edit, EM_SETLIMITTEXT, sizeof(buf)-1, 0 );
\r
8469 SetDlgItemInt( hDlg, IDC_NFG_Edit, *lpIndexFRC, TRUE );
\r
8470 SendDlgItemMessage( hDlg, IDC_NFG_Edit, EM_SETSEL, 0, -1 );
\r
8471 SetFocus(GetDlgItem(hDlg, IDC_NFG_Edit));
\r
8476 switch( LOWORD(wParam) ) {
\r
8478 *lpIndexFRC = GetDlgItemInt(hDlg, IDC_NFG_Edit, &index_is_ok, TRUE );
\r
8479 EndDialog( hDlg, 0 );
\r
8480 shuffleOpenings = TRUE; /* [HGM] shuffle: switch shuffling on for as long as we stay in current variant */
\r
8483 EndDialog( hDlg, 1 );
\r
8485 case IDC_NFG_Edit:
\r
8486 if( HIWORD(wParam) == EN_CHANGE ) {
\r
8487 GetDlgItemInt(hDlg, IDC_NFG_Edit, &index_is_ok, TRUE );
\r
8489 EnableWindow( GetDlgItem(hDlg, IDOK), index_is_ok );
\r
8492 case IDC_NFG_Random:
\r
8493 snprintf( buf, sizeof(buf)/sizeof(buf[0]), "%d", myrandom() ); /* [HGM] shuffle: no longer limit to 960 */
\r
8494 SetDlgItemText(hDlg, IDC_NFG_Edit, buf );
\r
8507 int index = appData.defaultFrcPosition;
\r
8508 FARPROC lpProc = MakeProcInstance( (FARPROC) NewGameFRC_Proc, hInst );
\r
8510 result = DialogBoxParam( hInst, MAKEINTRESOURCE(DLG_NewGameFRC), hwndMain, (DLGPROC)lpProc, (LPARAM)&index );
\r
8512 if( result == 0 ) {
\r
8513 appData.defaultFrcPosition = index;
\r
8519 /* [AS] Game list options. Refactored by HGM */
\r
8521 HWND gameListOptionsDialog;
\r
8523 // low-level front-end: clear text edit / list widget
\r
8527 SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_RESETCONTENT, 0, 0 );
\r
8530 // low-level front-end: clear text edit / list widget
\r
8532 GLT_DeSelectList()
\r
8534 SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_SETCURSEL, 0, 0 );
\r
8537 // low-level front-end: append line to text edit / list widget
\r
8539 GLT_AddToList( char *name )
\r
8542 SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_ADDSTRING, 0, (LPARAM) name );
\r
8546 // low-level front-end: get line from text edit / list widget
\r
8548 GLT_GetFromList( int index, char *name )
\r
8551 if( SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_GETTEXT, index, (LPARAM) name ) != LB_ERR )
\r
8557 void GLT_MoveSelection( HWND hDlg, int delta )
\r
8559 int idx1 = (int) SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETCURSEL, 0, 0 );
\r
8560 int idx2 = idx1 + delta;
\r
8561 int count = (int) SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETCOUNT, 0, 0 );
\r
8563 if( idx1 >=0 && idx1 < count && idx2 >= 0 && idx2 < count ) {
\r
8566 SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETTEXT, idx1, (LPARAM) buf );
\r
8567 SendDlgItemMessage( hDlg, IDC_GameListTags, LB_DELETESTRING, idx1, 0 );
\r
8568 SendDlgItemMessage( hDlg, IDC_GameListTags, LB_INSERTSTRING, idx2, (LPARAM) buf );
\r
8569 SendDlgItemMessage( hDlg, IDC_GameListTags, LB_SETCURSEL, idx2, 0 );
\r
8573 LRESULT CALLBACK GameListOptions_Proc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
8577 case WM_INITDIALOG:
\r
8578 gameListOptionsDialog = hDlg; // [HGM] pass through global to keep out off back-end
\r
8580 CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));
\r
8581 Translate(hDlg, DLG_GameListOptions);
\r
8583 /* Initialize list */
\r
8584 GLT_TagsToList( lpUserGLT );
\r
8586 SetFocus( GetDlgItem(hDlg, IDC_GameListTags) );
\r
8591 switch( LOWORD(wParam) ) {
\r
8594 EndDialog( hDlg, 0 );
\r
8597 EndDialog( hDlg, 1 );
\r
8600 case IDC_GLT_Default:
\r
8601 GLT_TagsToList( GLT_DEFAULT_TAGS );
\r
8604 case IDC_GLT_Restore:
\r
8605 GLT_TagsToList( appData.gameListTags );
\r
8609 GLT_MoveSelection( hDlg, -1 );
\r
8612 case IDC_GLT_Down:
\r
8613 GLT_MoveSelection( hDlg, +1 );
\r
8623 int GameListOptions()
\r
8626 FARPROC lpProc = MakeProcInstance( (FARPROC) GameListOptions_Proc, hInst );
\r
8628 safeStrCpy( lpUserGLT, appData.gameListTags ,LPUSERGLT_SIZE );
\r
8630 result = DialogBoxParam( hInst, MAKEINTRESOURCE(DLG_GameListOptions), hwndMain, (DLGPROC)lpProc, (LPARAM)lpUserGLT );
\r
8632 if( result == 0 ) {
\r
8633 /* [AS] Memory leak here! */
\r
8634 appData.gameListTags = strdup( lpUserGLT );
\r
8641 DisplayIcsInteractionTitle(char *str)
\r
8643 char consoleTitle[MSG_SIZ];
\r
8645 snprintf(consoleTitle, MSG_SIZ, "%s: %s", szConsoleTitle, str);
\r
8646 SetWindowText(hwndConsole, consoleTitle);
\r
8648 if(appData.chatBoxes) { // [HGM] chat: open chat boxes
\r
8649 char buf[MSG_SIZ], *p = buf, *q;
\r
8650 safeStrCpy(buf, appData.chatBoxes, sizeof(buf)/sizeof(buf[0]) );
\r
8652 q = strchr(p, ';');
\r
8654 if(*p) ChatPopUp(p);
\r
8658 SetActiveWindow(hwndMain);
\r
8662 DrawPosition(int fullRedraw, Board board)
\r
8664 HDCDrawPosition(NULL, (BOOLEAN) fullRedraw, board);
\r
8667 void NotifyFrontendLogin()
\r
8670 UpdateICSWidth(GetDlgItem(hwndConsole, OPT_ConsoleText));
\r
8676 fromX = fromY = -1;
\r
8677 if (dragInfo.pos.x != -1 || dragInfo.pos.y != -1) {
\r
8678 dragInfo.pos.x = dragInfo.pos.y = -1;
\r
8679 dragInfo.pos.x = dragInfo.pos.y = -1;
\r
8680 dragInfo.lastpos = dragInfo.pos;
\r
8681 dragInfo.start.x = dragInfo.start.y = -1;
\r
8682 dragInfo.from = dragInfo.start;
\r
8684 DrawPosition(TRUE, NULL);
\r
8691 CommentPopUp(char *title, char *str)
\r
8693 HWND hwnd = GetActiveWindow();
\r
8694 EitherCommentPopUp(currentMove, title, str, FALSE); // [HGM] vari: fake move index, rather than 0
\r
8696 SetActiveWindow(hwnd);
\r
8700 CommentPopDown(void)
\r
8702 CheckMenuItem(GetMenu(hwndMain), IDM_Comment, MF_UNCHECKED);
\r
8703 if (commentDialog) {
\r
8704 ShowWindow(commentDialog, SW_HIDE);
\r
8706 commentUp = FALSE;
\r
8710 EditCommentPopUp(int index, char *title, char *str)
\r
8712 EitherCommentPopUp(index, title, str, TRUE);
\r
8719 MyPlaySound(&sounds[(int)SoundMove]);
\r
8722 VOID PlayIcsWinSound()
\r
8724 MyPlaySound(&sounds[(int)SoundIcsWin]);
\r
8727 VOID PlayIcsLossSound()
\r
8729 MyPlaySound(&sounds[(int)SoundIcsLoss]);
\r
8732 VOID PlayIcsDrawSound()
\r
8734 MyPlaySound(&sounds[(int)SoundIcsDraw]);
\r
8737 VOID PlayIcsUnfinishedSound()
\r
8739 MyPlaySound(&sounds[(int)SoundIcsUnfinished]);
\r
8745 MyPlaySound(&sounds[(int)SoundAlarm]);
\r
8751 MyPlaySound(&textAttribs[ColorTell].sound);
\r
8759 consoleEcho = TRUE;
\r
8760 hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
8761 SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&consoleCF);
\r
8762 SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);
\r
8771 consoleEcho = FALSE;
\r
8772 hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
8773 /* This works OK: set text and background both to the same color */
\r
8775 cf.crTextColor = COLOR_ECHOOFF;
\r
8776 SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf);
\r
8777 SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, cf.crTextColor);
\r
8780 /* No Raw()...? */
\r
8782 void Colorize(ColorClass cc, int continuation)
\r
8784 currentColorClass = cc;
\r
8785 consoleCF.dwMask = CFM_COLOR|CFM_BOLD|CFM_ITALIC|CFM_UNDERLINE|CFM_STRIKEOUT;
\r
8786 consoleCF.crTextColor = textAttribs[cc].color;
\r
8787 consoleCF.dwEffects = textAttribs[cc].effects;
\r
8788 if (!continuation) MyPlaySound(&textAttribs[cc].sound);
\r
8794 static char buf[MSG_SIZ];
\r
8795 DWORD bufsiz = MSG_SIZ;
\r
8797 if(appData.userName != NULL && appData.userName[0] != 0) {
\r
8798 return appData.userName; /* [HGM] username: prefer name selected by user over his system login */
\r
8800 if (!GetUserName(buf, &bufsiz)) {
\r
8801 /*DisplayError("Error getting user name", GetLastError());*/
\r
8802 safeStrCpy(buf, _("User"), sizeof(buf)/sizeof(buf[0]) );
\r
8810 static char buf[MSG_SIZ];
\r
8811 DWORD bufsiz = MSG_SIZ;
\r
8813 if (!GetComputerName(buf, &bufsiz)) {
\r
8814 /*DisplayError("Error getting host name", GetLastError());*/
\r
8815 safeStrCpy(buf, _("Unknown"), sizeof(buf)/sizeof(buf[0]) );
\r
8822 ClockTimerRunning()
\r
8824 return clockTimerEvent != 0;
\r
8830 if (clockTimerEvent == 0) return FALSE;
\r
8831 KillTimer(hwndMain, clockTimerEvent);
\r
8832 clockTimerEvent = 0;
\r
8837 StartClockTimer(long millisec)
\r
8839 clockTimerEvent = SetTimer(hwndMain, (UINT) CLOCK_TIMER_ID,
\r
8840 (UINT) millisec, NULL);
\r
8844 DisplayWhiteClock(long timeRemaining, int highlight)
\r
8847 char *flag = whiteFlag && gameMode == TwoMachinesPlay ? "(!)" : "";
\r
8849 if(appData.noGUI) return;
\r
8850 hdc = GetDC(hwndMain);
\r
8851 if (!IsIconic(hwndMain)) {
\r
8852 DisplayAClock(hdc, timeRemaining, highlight,
\r
8853 flipClock ? &blackRect : &whiteRect, _("White"), flag);
\r
8855 if (highlight && iconCurrent == iconBlack) {
\r
8856 iconCurrent = iconWhite;
\r
8857 PostMessage(hwndMain, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);
\r
8858 if (IsIconic(hwndMain)) {
\r
8859 DrawIcon(hdc, 2, 2, iconCurrent);
\r
8862 (void) ReleaseDC(hwndMain, hdc);
\r
8864 PostMessage(hwndConsole, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);
\r
8868 DisplayBlackClock(long timeRemaining, int highlight)
\r
8871 char *flag = blackFlag && gameMode == TwoMachinesPlay ? "(!)" : "";
\r
8873 if(appData.noGUI) return;
\r
8874 hdc = GetDC(hwndMain);
\r
8875 if (!IsIconic(hwndMain)) {
\r
8876 DisplayAClock(hdc, timeRemaining, highlight,
\r
8877 flipClock ? &whiteRect : &blackRect, _("Black"), flag);
\r
8879 if (highlight && iconCurrent == iconWhite) {
\r
8880 iconCurrent = iconBlack;
\r
8881 PostMessage(hwndMain, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);
\r
8882 if (IsIconic(hwndMain)) {
\r
8883 DrawIcon(hdc, 2, 2, iconCurrent);
\r
8886 (void) ReleaseDC(hwndMain, hdc);
\r
8888 PostMessage(hwndConsole, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);
\r
8893 LoadGameTimerRunning()
\r
8895 return loadGameTimerEvent != 0;
\r
8899 StopLoadGameTimer()
\r
8901 if (loadGameTimerEvent == 0) return FALSE;
\r
8902 KillTimer(hwndMain, loadGameTimerEvent);
\r
8903 loadGameTimerEvent = 0;
\r
8908 StartLoadGameTimer(long millisec)
\r
8910 loadGameTimerEvent = SetTimer(hwndMain, (UINT) LOAD_GAME_TIMER_ID,
\r
8911 (UINT) millisec, NULL);
\r
8919 char fileTitle[MSG_SIZ];
\r
8921 defName = DefaultFileName(appData.oldSaveStyle ? "gam" : "pgn");
\r
8922 f = OpenFileDialog(hwndMain, "a", defName,
\r
8923 appData.oldSaveStyle ? "gam" : "pgn",
\r
8925 _("Save Game to File"), NULL, fileTitle, NULL);
\r
8927 SaveGame(f, 0, "");
\r
8934 ScheduleDelayedEvent(DelayedEventCallback cb, long millisec)
\r
8936 if (delayedTimerEvent != 0) {
\r
8937 if (appData.debugMode && cb != delayedTimerCallback) { // [HGM] alive: not too much debug
\r
8938 fprintf(debugFP, "ScheduleDelayedEvent: event already scheduled\n");
\r
8940 KillTimer(hwndMain, delayedTimerEvent);
\r
8941 delayedTimerEvent = 0;
\r
8942 if(delayedTimerCallback != cb) // [HGM] alive: do not "flush" same event, just postpone it
\r
8943 delayedTimerCallback();
\r
8945 delayedTimerCallback = cb;
\r
8946 delayedTimerEvent = SetTimer(hwndMain, (UINT) DELAYED_TIMER_ID,
\r
8947 (UINT) millisec, NULL);
\r
8950 DelayedEventCallback
\r
8953 if (delayedTimerEvent) {
\r
8954 return delayedTimerCallback;
\r
8961 CancelDelayedEvent()
\r
8963 if (delayedTimerEvent) {
\r
8964 KillTimer(hwndMain, delayedTimerEvent);
\r
8965 delayedTimerEvent = 0;
\r
8969 DWORD GetWin32Priority(int nice)
\r
8970 { // [HGM] nice: translate Unix nice() value to indows priority class. (Code stolen from Polyglot 1.4w11)
\r
8972 REALTIME_PRIORITY_CLASS 0x00000100
\r
8973 HIGH_PRIORITY_CLASS 0x00000080
\r
8974 ABOVE_NORMAL_PRIORITY_CLASS 0x00008000
\r
8975 NORMAL_PRIORITY_CLASS 0x00000020
\r
8976 BELOW_NORMAL_PRIORITY_CLASS 0x00004000
\r
8977 IDLE_PRIORITY_CLASS 0x00000040
\r
8979 if (nice < -15) return 0x00000080;
\r
8980 if (nice < 0) return 0x00008000;
\r
8981 if (nice == 0) return 0x00000020;
\r
8982 if (nice < 15) return 0x00004000;
\r
8983 return 0x00000040;
\r
8986 void RunCommand(char *cmdLine)
\r
8988 /* Now create the child process. */
\r
8989 STARTUPINFO siStartInfo;
\r
8990 PROCESS_INFORMATION piProcInfo;
\r
8992 siStartInfo.cb = sizeof(STARTUPINFO);
\r
8993 siStartInfo.lpReserved = NULL;
\r
8994 siStartInfo.lpDesktop = NULL;
\r
8995 siStartInfo.lpTitle = NULL;
\r
8996 siStartInfo.dwFlags = STARTF_USESTDHANDLES;
\r
8997 siStartInfo.cbReserved2 = 0;
\r
8998 siStartInfo.lpReserved2 = NULL;
\r
8999 siStartInfo.hStdInput = NULL;
\r
9000 siStartInfo.hStdOutput = NULL;
\r
9001 siStartInfo.hStdError = NULL;
\r
9003 CreateProcess(NULL,
\r
9004 cmdLine, /* command line */
\r
9005 NULL, /* process security attributes */
\r
9006 NULL, /* primary thread security attrs */
\r
9007 TRUE, /* handles are inherited */
\r
9008 DETACHED_PROCESS|CREATE_NEW_PROCESS_GROUP,
\r
9009 NULL, /* use parent's environment */
\r
9011 &siStartInfo, /* STARTUPINFO pointer */
\r
9012 &piProcInfo); /* receives PROCESS_INFORMATION */
\r
9014 CloseHandle(piProcInfo.hThread);
\r
9017 /* Start a child process running the given program.
\r
9018 The process's standard output can be read from "from", and its
\r
9019 standard input can be written to "to".
\r
9020 Exit with fatal error if anything goes wrong.
\r
9021 Returns an opaque pointer that can be used to destroy the process
\r
9025 StartChildProcess(char *cmdLine, char *dir, ProcRef *pr)
\r
9027 #define BUFSIZE 4096
\r
9029 HANDLE hChildStdinRd, hChildStdinWr,
\r
9030 hChildStdoutRd, hChildStdoutWr;
\r
9031 HANDLE hChildStdinWrDup, hChildStdoutRdDup;
\r
9032 SECURITY_ATTRIBUTES saAttr;
\r
9034 PROCESS_INFORMATION piProcInfo;
\r
9035 STARTUPINFO siStartInfo;
\r
9037 char buf[MSG_SIZ];
\r
9040 if (appData.debugMode) {
\r
9041 fprintf(debugFP, "StartChildProcess (dir=\"%s\") %s\n", dir, cmdLine);
\r
9046 /* Set the bInheritHandle flag so pipe handles are inherited. */
\r
9047 saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);
\r
9048 saAttr.bInheritHandle = TRUE;
\r
9049 saAttr.lpSecurityDescriptor = NULL;
\r
9052 * The steps for redirecting child's STDOUT:
\r
9053 * 1. Create anonymous pipe to be STDOUT for child.
\r
9054 * 2. Create a noninheritable duplicate of read handle,
\r
9055 * and close the inheritable read handle.
\r
9058 /* Create a pipe for the child's STDOUT. */
\r
9059 if (! CreatePipe(&hChildStdoutRd, &hChildStdoutWr, &saAttr, 0)) {
\r
9060 return GetLastError();
\r
9063 /* Duplicate the read handle to the pipe, so it is not inherited. */
\r
9064 fSuccess = DuplicateHandle(GetCurrentProcess(), hChildStdoutRd,
\r
9065 GetCurrentProcess(), &hChildStdoutRdDup, 0,
\r
9066 FALSE, /* not inherited */
\r
9067 DUPLICATE_SAME_ACCESS);
\r
9069 return GetLastError();
\r
9071 CloseHandle(hChildStdoutRd);
\r
9074 * The steps for redirecting child's STDIN:
\r
9075 * 1. Create anonymous pipe to be STDIN for child.
\r
9076 * 2. Create a noninheritable duplicate of write handle,
\r
9077 * and close the inheritable write handle.
\r
9080 /* Create a pipe for the child's STDIN. */
\r
9081 if (! CreatePipe(&hChildStdinRd, &hChildStdinWr, &saAttr, 0)) {
\r
9082 return GetLastError();
\r
9085 /* Duplicate the write handle to the pipe, so it is not inherited. */
\r
9086 fSuccess = DuplicateHandle(GetCurrentProcess(), hChildStdinWr,
\r
9087 GetCurrentProcess(), &hChildStdinWrDup, 0,
\r
9088 FALSE, /* not inherited */
\r
9089 DUPLICATE_SAME_ACCESS);
\r
9091 return GetLastError();
\r
9093 CloseHandle(hChildStdinWr);
\r
9095 /* Arrange to (1) look in dir for the child .exe file, and
\r
9096 * (2) have dir be the child's working directory. Interpret
\r
9097 * dir relative to the directory WinBoard loaded from. */
\r
9098 GetCurrentDirectory(MSG_SIZ, buf);
\r
9099 SetCurrentDirectory(installDir);
\r
9100 SetCurrentDirectory(dir);
\r
9102 /* Now create the child process. */
\r
9104 siStartInfo.cb = sizeof(STARTUPINFO);
\r
9105 siStartInfo.lpReserved = NULL;
\r
9106 siStartInfo.lpDesktop = NULL;
\r
9107 siStartInfo.lpTitle = NULL;
\r
9108 siStartInfo.dwFlags = STARTF_USESTDHANDLES;
\r
9109 siStartInfo.cbReserved2 = 0;
\r
9110 siStartInfo.lpReserved2 = NULL;
\r
9111 siStartInfo.hStdInput = hChildStdinRd;
\r
9112 siStartInfo.hStdOutput = hChildStdoutWr;
\r
9113 siStartInfo.hStdError = hChildStdoutWr;
\r
9115 fSuccess = CreateProcess(NULL,
\r
9116 cmdLine, /* command line */
\r
9117 NULL, /* process security attributes */
\r
9118 NULL, /* primary thread security attrs */
\r
9119 TRUE, /* handles are inherited */
\r
9120 DETACHED_PROCESS|CREATE_NEW_PROCESS_GROUP,
\r
9121 NULL, /* use parent's environment */
\r
9123 &siStartInfo, /* STARTUPINFO pointer */
\r
9124 &piProcInfo); /* receives PROCESS_INFORMATION */
\r
9126 err = GetLastError();
\r
9127 SetCurrentDirectory(buf); /* return to prev directory */
\r
9132 if (appData.niceEngines){ // [HGM] nice: adjust engine proc priority
\r
9133 if(appData.debugMode) fprintf(debugFP, "nice engine proc to %d\n", appData.niceEngines);
\r
9134 SetPriorityClass(piProcInfo.hProcess, GetWin32Priority(appData.niceEngines));
\r
9137 /* Close the handles we don't need in the parent */
\r
9138 CloseHandle(piProcInfo.hThread);
\r
9139 CloseHandle(hChildStdinRd);
\r
9140 CloseHandle(hChildStdoutWr);
\r
9142 /* Prepare return value */
\r
9143 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
\r
9144 cp->kind = CPReal;
\r
9145 cp->hProcess = piProcInfo.hProcess;
\r
9146 cp->pid = piProcInfo.dwProcessId;
\r
9147 cp->hFrom = hChildStdoutRdDup;
\r
9148 cp->hTo = hChildStdinWrDup;
\r
9150 *pr = (void *) cp;
\r
9152 /* Klaus Friedel says that this Sleep solves a problem under Windows
\r
9153 2000 where engines sometimes don't see the initial command(s)
\r
9154 from WinBoard and hang. I don't understand how that can happen,
\r
9155 but the Sleep is harmless, so I've put it in. Others have also
\r
9156 reported what may be the same problem, so hopefully this will fix
\r
9157 it for them too. */
\r
9165 DestroyChildProcess(ProcRef pr, int/*boolean*/ signal)
\r
9167 ChildProc *cp; int result;
\r
9169 cp = (ChildProc *) pr;
\r
9170 if (cp == NULL) return;
\r
9172 switch (cp->kind) {
\r
9174 /* TerminateProcess is considered harmful, so... */
\r
9175 CloseHandle(cp->hTo); /* Closing this will give the child an EOF and hopefully kill it */
\r
9176 if (cp->hFrom) CloseHandle(cp->hFrom); /* if NULL, InputThread will close it */
\r
9177 /* The following doesn't work because the chess program
\r
9178 doesn't "have the same console" as WinBoard. Maybe
\r
9179 we could arrange for this even though neither WinBoard
\r
9180 nor the chess program uses a console for stdio? */
\r
9181 /*!!if (signal) GenerateConsoleCtrlEvent(CTRL_BREAK_EVENT, cp->pid);*/
\r
9183 /* [AS] Special termination modes for misbehaving programs... */
\r
9184 if( signal == 9 ) {
\r
9185 result = TerminateProcess( cp->hProcess, 0 );
\r
9187 if ( appData.debugMode) {
\r
9188 fprintf( debugFP, "Terminating process %lu, result=%d\n", cp->pid, result );
\r
9191 else if( signal == 10 ) {
\r
9192 DWORD dw = WaitForSingleObject( cp->hProcess, 3*1000 ); // Wait 3 seconds at most
\r
9194 if( dw != WAIT_OBJECT_0 ) {
\r
9195 result = TerminateProcess( cp->hProcess, 0 );
\r
9197 if ( appData.debugMode) {
\r
9198 fprintf( debugFP, "Process %lu still alive after timeout, killing... result=%d\n", cp->pid, result );
\r
9204 CloseHandle(cp->hProcess);
\r
9208 if (cp->hFrom) CloseHandle(cp->hFrom);
\r
9212 closesocket(cp->sock);
\r
9217 if (signal) send(cp->sock2, "\017", 1, 0); /* 017 = 15 = SIGTERM */
\r
9218 closesocket(cp->sock);
\r
9219 closesocket(cp->sock2);
\r
9227 InterruptChildProcess(ProcRef pr)
\r
9231 cp = (ChildProc *) pr;
\r
9232 if (cp == NULL) return;
\r
9233 switch (cp->kind) {
\r
9235 /* The following doesn't work because the chess program
\r
9236 doesn't "have the same console" as WinBoard. Maybe
\r
9237 we could arrange for this even though neither WinBoard
\r
9238 nor the chess program uses a console for stdio */
\r
9239 /*!!GenerateConsoleCtrlEvent(CTRL_C_EVENT, cp->pid);*/
\r
9244 /* Can't interrupt */
\r
9248 send(cp->sock2, "\002", 1, 0); /* 2 = SIGINT */
\r
9255 OpenTelnet(char *host, char *port, ProcRef *pr)
\r
9257 char cmdLine[MSG_SIZ];
\r
9259 if (port[0] == NULLCHAR) {
\r
9260 snprintf(cmdLine, MSG_SIZ, "%s %s", appData.telnetProgram, host);
\r
9262 snprintf(cmdLine, MSG_SIZ, "%s %s %s", appData.telnetProgram, host, port);
\r
9264 return StartChildProcess(cmdLine, "", pr);
\r
9268 /* Code to open TCP sockets */
\r
9271 OpenTCP(char *host, char *port, ProcRef *pr)
\r
9277 struct sockaddr_in sa, mysa;
\r
9278 struct hostent FAR *hp;
\r
9279 unsigned short uport;
\r
9280 WORD wVersionRequested;
\r
9283 /* Initialize socket DLL */
\r
9284 wVersionRequested = MAKEWORD(1, 1);
\r
9285 err = WSAStartup(wVersionRequested, &wsaData);
\r
9286 if (err != 0) return err;
\r
9289 if ((s = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {
\r
9290 err = WSAGetLastError();
\r
9295 /* Bind local address using (mostly) don't-care values.
\r
9297 memset((char *) &mysa, 0, sizeof(struct sockaddr_in));
\r
9298 mysa.sin_family = AF_INET;
\r
9299 mysa.sin_addr.s_addr = INADDR_ANY;
\r
9300 uport = (unsigned short) 0;
\r
9301 mysa.sin_port = htons(uport);
\r
9302 if (bind(s, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))
\r
9303 == SOCKET_ERROR) {
\r
9304 err = WSAGetLastError();
\r
9309 /* Resolve remote host name */
\r
9310 memset((char *) &sa, 0, sizeof(struct sockaddr_in));
\r
9311 if (!(hp = gethostbyname(host))) {
\r
9312 unsigned int b0, b1, b2, b3;
\r
9314 err = WSAGetLastError();
\r
9316 if (sscanf(host, "%u.%u.%u.%u", &b0, &b1, &b2, &b3) == 4) {
\r
9317 hp = (struct hostent *) calloc(1, sizeof(struct hostent));
\r
9318 hp->h_addrtype = AF_INET;
\r
9320 hp->h_addr_list = (char **) calloc(2, sizeof(char *));
\r
9321 hp->h_addr_list[0] = (char *) malloc(4);
\r
9322 hp->h_addr_list[0][0] = (char) b0;
\r
9323 hp->h_addr_list[0][1] = (char) b1;
\r
9324 hp->h_addr_list[0][2] = (char) b2;
\r
9325 hp->h_addr_list[0][3] = (char) b3;
\r
9331 sa.sin_family = hp->h_addrtype;
\r
9332 uport = (unsigned short) atoi(port);
\r
9333 sa.sin_port = htons(uport);
\r
9334 memcpy((char *) &sa.sin_addr, hp->h_addr, hp->h_length);
\r
9336 /* Make connection */
\r
9337 if (connect(s, (struct sockaddr *) &sa,
\r
9338 sizeof(struct sockaddr_in)) == SOCKET_ERROR) {
\r
9339 err = WSAGetLastError();
\r
9344 /* Prepare return value */
\r
9345 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
\r
9346 cp->kind = CPSock;
\r
9348 *pr = (ProcRef *) cp;
\r
9354 OpenCommPort(char *name, ProcRef *pr)
\r
9359 char fullname[MSG_SIZ];
\r
9361 if (*name != '\\')
\r
9362 snprintf(fullname, MSG_SIZ, "\\\\.\\%s", name);
\r
9364 safeStrCpy(fullname, name, sizeof(fullname)/sizeof(fullname[0]) );
\r
9366 h = CreateFile(name, GENERIC_READ | GENERIC_WRITE,
\r
9367 0, NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL);
\r
9368 if (h == (HANDLE) -1) {
\r
9369 return GetLastError();
\r
9373 if (!SetCommState(h, (LPDCB) &dcb)) return GetLastError();
\r
9375 /* Accumulate characters until a 100ms pause, then parse */
\r
9376 ct.ReadIntervalTimeout = 100;
\r
9377 ct.ReadTotalTimeoutMultiplier = 0;
\r
9378 ct.ReadTotalTimeoutConstant = 0;
\r
9379 ct.WriteTotalTimeoutMultiplier = 0;
\r
9380 ct.WriteTotalTimeoutConstant = 0;
\r
9381 if (!SetCommTimeouts(h, (LPCOMMTIMEOUTS) &ct)) return GetLastError();
\r
9383 /* Prepare return value */
\r
9384 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
\r
9385 cp->kind = CPComm;
\r
9388 *pr = (ProcRef *) cp;
\r
9394 OpenLoopback(ProcRef *pr)
\r
9396 DisplayFatalError(_("Not implemented"), 0, 1);
\r
9402 OpenRcmd(char* host, char* user, char* cmd, ProcRef* pr)
\r
9407 struct sockaddr_in sa, mysa;
\r
9408 struct hostent FAR *hp;
\r
9409 unsigned short uport;
\r
9410 WORD wVersionRequested;
\r
9413 char stderrPortStr[MSG_SIZ];
\r
9415 /* Initialize socket DLL */
\r
9416 wVersionRequested = MAKEWORD(1, 1);
\r
9417 err = WSAStartup(wVersionRequested, &wsaData);
\r
9418 if (err != 0) return err;
\r
9420 /* Resolve remote host name */
\r
9421 memset((char *) &sa, 0, sizeof(struct sockaddr_in));
\r
9422 if (!(hp = gethostbyname(host))) {
\r
9423 unsigned int b0, b1, b2, b3;
\r
9425 err = WSAGetLastError();
\r
9427 if (sscanf(host, "%u.%u.%u.%u", &b0, &b1, &b2, &b3) == 4) {
\r
9428 hp = (struct hostent *) calloc(1, sizeof(struct hostent));
\r
9429 hp->h_addrtype = AF_INET;
\r
9431 hp->h_addr_list = (char **) calloc(2, sizeof(char *));
\r
9432 hp->h_addr_list[0] = (char *) malloc(4);
\r
9433 hp->h_addr_list[0][0] = (char) b0;
\r
9434 hp->h_addr_list[0][1] = (char) b1;
\r
9435 hp->h_addr_list[0][2] = (char) b2;
\r
9436 hp->h_addr_list[0][3] = (char) b3;
\r
9442 sa.sin_family = hp->h_addrtype;
\r
9443 uport = (unsigned short) 514;
\r
9444 sa.sin_port = htons(uport);
\r
9445 memcpy((char *) &sa.sin_addr, hp->h_addr, hp->h_length);
\r
9447 /* Bind local socket to unused "privileged" port address
\r
9449 s = INVALID_SOCKET;
\r
9450 memset((char *) &mysa, 0, sizeof(struct sockaddr_in));
\r
9451 mysa.sin_family = AF_INET;
\r
9452 mysa.sin_addr.s_addr = INADDR_ANY;
\r
9453 for (fromPort = 1023;; fromPort--) {
\r
9454 if (fromPort < 0) {
\r
9456 return WSAEADDRINUSE;
\r
9458 if (s == INVALID_SOCKET) {
\r
9459 if ((s = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {
\r
9460 err = WSAGetLastError();
\r
9465 uport = (unsigned short) fromPort;
\r
9466 mysa.sin_port = htons(uport);
\r
9467 if (bind(s, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))
\r
9468 == SOCKET_ERROR) {
\r
9469 err = WSAGetLastError();
\r
9470 if (err == WSAEADDRINUSE) continue;
\r
9474 if (connect(s, (struct sockaddr *) &sa,
\r
9475 sizeof(struct sockaddr_in)) == SOCKET_ERROR) {
\r
9476 err = WSAGetLastError();
\r
9477 if (err == WSAEADDRINUSE) {
\r
9488 /* Bind stderr local socket to unused "privileged" port address
\r
9490 s2 = INVALID_SOCKET;
\r
9491 memset((char *) &mysa, 0, sizeof(struct sockaddr_in));
\r
9492 mysa.sin_family = AF_INET;
\r
9493 mysa.sin_addr.s_addr = INADDR_ANY;
\r
9494 for (fromPort = 1023;; fromPort--) {
\r
9495 if (fromPort == prevStderrPort) continue; // don't reuse port
\r
9496 if (fromPort < 0) {
\r
9497 (void) closesocket(s);
\r
9499 return WSAEADDRINUSE;
\r
9501 if (s2 == INVALID_SOCKET) {
\r
9502 if ((s2 = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {
\r
9503 err = WSAGetLastError();
\r
9509 uport = (unsigned short) fromPort;
\r
9510 mysa.sin_port = htons(uport);
\r
9511 if (bind(s2, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))
\r
9512 == SOCKET_ERROR) {
\r
9513 err = WSAGetLastError();
\r
9514 if (err == WSAEADDRINUSE) continue;
\r
9515 (void) closesocket(s);
\r
9519 if (listen(s2, 1) == SOCKET_ERROR) {
\r
9520 err = WSAGetLastError();
\r
9521 if (err == WSAEADDRINUSE) {
\r
9523 s2 = INVALID_SOCKET;
\r
9526 (void) closesocket(s);
\r
9527 (void) closesocket(s2);
\r
9533 prevStderrPort = fromPort; // remember port used
\r
9534 snprintf(stderrPortStr, MSG_SIZ, "%d", fromPort);
\r
9536 if (send(s, stderrPortStr, strlen(stderrPortStr) + 1, 0) == SOCKET_ERROR) {
\r
9537 err = WSAGetLastError();
\r
9538 (void) closesocket(s);
\r
9539 (void) closesocket(s2);
\r
9544 if (send(s, UserName(), strlen(UserName()) + 1, 0) == SOCKET_ERROR) {
\r
9545 err = WSAGetLastError();
\r
9546 (void) closesocket(s);
\r
9547 (void) closesocket(s2);
\r
9551 if (*user == NULLCHAR) user = UserName();
\r
9552 if (send(s, user, strlen(user) + 1, 0) == SOCKET_ERROR) {
\r
9553 err = WSAGetLastError();
\r
9554 (void) closesocket(s);
\r
9555 (void) closesocket(s2);
\r
9559 if (send(s, cmd, strlen(cmd) + 1, 0) == SOCKET_ERROR) {
\r
9560 err = WSAGetLastError();
\r
9561 (void) closesocket(s);
\r
9562 (void) closesocket(s2);
\r
9567 if ((s3 = accept(s2, NULL, NULL)) == INVALID_SOCKET) {
\r
9568 err = WSAGetLastError();
\r
9569 (void) closesocket(s);
\r
9570 (void) closesocket(s2);
\r
9574 (void) closesocket(s2); /* Stop listening */
\r
9576 /* Prepare return value */
\r
9577 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
\r
9578 cp->kind = CPRcmd;
\r
9581 *pr = (ProcRef *) cp;
\r
9588 AddInputSource(ProcRef pr, int lineByLine,
\r
9589 InputCallback func, VOIDSTAR closure)
\r
9591 InputSource *is, *is2 = NULL;
\r
9592 ChildProc *cp = (ChildProc *) pr;
\r
9594 is = (InputSource *) calloc(1, sizeof(InputSource));
\r
9595 is->lineByLine = lineByLine;
\r
9597 is->closure = closure;
\r
9598 is->second = NULL;
\r
9599 is->next = is->buf;
\r
9600 if (pr == NoProc) {
\r
9601 is->kind = CPReal;
\r
9602 consoleInputSource = is;
\r
9604 is->kind = cp->kind;
\r
9606 [AS] Try to avoid a race condition if the thread is given control too early:
\r
9607 we create all threads suspended so that the is->hThread variable can be
\r
9608 safely assigned, then let the threads start with ResumeThread.
\r
9610 switch (cp->kind) {
\r
9612 is->hFile = cp->hFrom;
\r
9613 cp->hFrom = NULL; /* now owned by InputThread */
\r
9615 CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) NonOvlInputThread,
\r
9616 (LPVOID) is, CREATE_SUSPENDED, &is->id);
\r
9620 is->hFile = cp->hFrom;
\r
9621 cp->hFrom = NULL; /* now owned by InputThread */
\r
9623 CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) InputThread,
\r
9624 (LPVOID) is, CREATE_SUSPENDED, &is->id);
\r
9628 is->sock = cp->sock;
\r
9630 CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,
\r
9631 (LPVOID) is, CREATE_SUSPENDED, &is->id);
\r
9635 is2 = (InputSource *) calloc(1, sizeof(InputSource));
\r
9637 is->sock = cp->sock;
\r
9639 is2->sock = cp->sock2;
\r
9640 is2->second = is2;
\r
9642 CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,
\r
9643 (LPVOID) is, CREATE_SUSPENDED, &is->id);
\r
9645 CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,
\r
9646 (LPVOID) is2, CREATE_SUSPENDED, &is2->id);
\r
9650 if( is->hThread != NULL ) {
\r
9651 ResumeThread( is->hThread );
\r
9654 if( is2 != NULL && is2->hThread != NULL ) {
\r
9655 ResumeThread( is2->hThread );
\r
9659 return (InputSourceRef) is;
\r
9663 RemoveInputSource(InputSourceRef isr)
\r
9667 is = (InputSource *) isr;
\r
9668 is->hThread = NULL; /* tell thread to stop */
\r
9669 CloseHandle(is->hThread);
\r
9670 if (is->second != NULL) {
\r
9671 is->second->hThread = NULL;
\r
9672 CloseHandle(is->second->hThread);
\r
9676 int no_wrap(char *message, int count)
\r
9678 ConsoleOutput(message, count, FALSE);
\r
9683 OutputToProcess(ProcRef pr, char *message, int count, int *outError)
\r
9686 int outCount = SOCKET_ERROR;
\r
9687 ChildProc *cp = (ChildProc *) pr;
\r
9688 static OVERLAPPED ovl;
\r
9689 static int line = 0;
\r
9693 if (appData.noJoin || !appData.useInternalWrap)
\r
9694 return no_wrap(message, count);
\r
9697 int width = get_term_width();
\r
9698 int len = wrap(NULL, message, count, width, &line);
\r
9699 char *msg = malloc(len);
\r
9703 return no_wrap(message, count);
\r
9706 dbgchk = wrap(msg, message, count, width, &line);
\r
9707 if (dbgchk != len && appData.debugMode)
\r
9708 fprintf(debugFP, "wrap(): dbgchk(%d) != len(%d)\n", dbgchk, len);
\r
9709 ConsoleOutput(msg, len, FALSE);
\r
9716 if (ovl.hEvent == NULL) {
\r
9717 ovl.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
\r
9719 ovl.Internal = ovl.InternalHigh = ovl.Offset = ovl.OffsetHigh = 0;
\r
9721 switch (cp->kind) {
\r
9724 outCount = send(cp->sock, message, count, 0);
\r
9725 if (outCount == SOCKET_ERROR) {
\r
9726 *outError = WSAGetLastError();
\r
9728 *outError = NO_ERROR;
\r
9733 if (WriteFile(((ChildProc *)pr)->hTo, message, count,
\r
9734 &dOutCount, NULL)) {
\r
9735 *outError = NO_ERROR;
\r
9736 outCount = (int) dOutCount;
\r
9738 *outError = GetLastError();
\r
9743 *outError = DoWriteFile(((ChildProc *)pr)->hTo, message, count,
\r
9744 &dOutCount, &ovl);
\r
9745 if (*outError == NO_ERROR) {
\r
9746 outCount = (int) dOutCount;
\r
9756 if(n != 0) Sleep(n);
\r
9760 OutputToProcessDelayed(ProcRef pr, char *message, int count, int *outError,
\r
9763 /* Ignore delay, not implemented for WinBoard */
\r
9764 return OutputToProcess(pr, message, count, outError);
\r
9769 CmailSigHandlerCallBack(InputSourceRef isr, VOIDSTAR closure,
\r
9770 char *buf, int count, int error)
\r
9772 DisplayFatalError(_("Not implemented"), 0, 1);
\r
9775 /* see wgamelist.c for Game List functions */
\r
9776 /* see wedittags.c for Edit Tags functions */
\r
9783 char buf[MSG_SIZ];
\r
9786 if (SearchPath(installDir, appData.icsLogon, NULL, MSG_SIZ, buf, &dummy)) {
\r
9787 f = fopen(buf, "r");
\r
9789 ProcessICSInitScript(f);
\r
9799 StartAnalysisClock()
\r
9801 if (analysisTimerEvent) return;
\r
9802 analysisTimerEvent = SetTimer(hwndMain, (UINT) ANALYSIS_TIMER_ID,
\r
9803 (UINT) 2000, NULL);
\r
9807 SetHighlights(int fromX, int fromY, int toX, int toY)
\r
9809 highlightInfo.sq[0].x = fromX;
\r
9810 highlightInfo.sq[0].y = fromY;
\r
9811 highlightInfo.sq[1].x = toX;
\r
9812 highlightInfo.sq[1].y = toY;
\r
9818 highlightInfo.sq[0].x = highlightInfo.sq[0].y =
\r
9819 highlightInfo.sq[1].x = highlightInfo.sq[1].y = -1;
\r
9823 SetPremoveHighlights(int fromX, int fromY, int toX, int toY)
\r
9825 premoveHighlightInfo.sq[0].x = fromX;
\r
9826 premoveHighlightInfo.sq[0].y = fromY;
\r
9827 premoveHighlightInfo.sq[1].x = toX;
\r
9828 premoveHighlightInfo.sq[1].y = toY;
\r
9832 ClearPremoveHighlights()
\r
9834 premoveHighlightInfo.sq[0].x = premoveHighlightInfo.sq[0].y =
\r
9835 premoveHighlightInfo.sq[1].x = premoveHighlightInfo.sq[1].y = -1;
\r
9839 ShutDownFrontEnd()
\r
9841 if (saveSettingsOnExit) SaveSettings(settingsFileName);
\r
9842 DeleteClipboardTempFiles();
\r
9848 if (IsIconic(hwndMain))
\r
9849 ShowWindow(hwndMain, SW_RESTORE);
\r
9851 SetActiveWindow(hwndMain);
\r
9855 * Prototypes for animation support routines
\r
9857 static void ScreenSquare(int column, int row, POINT * pt);
\r
9858 static void Tween( POINT * start, POINT * mid, POINT * finish, int factor,
\r
9859 POINT frames[], int * nFrames);
\r
9865 AnimateAtomicCapture(Board board, int fromX, int fromY, int toX, int toY)
\r
9866 { // [HGM] atomic: animate blast wave
\r
9869 explodeInfo.fromX = fromX;
\r
9870 explodeInfo.fromY = fromY;
\r
9871 explodeInfo.toX = toX;
\r
9872 explodeInfo.toY = toY;
\r
9873 for(i=1; i<4*kFactor; i++) {
\r
9874 explodeInfo.radius = (i*180)/(4*kFactor-1);
\r
9875 DrawPosition(FALSE, board);
\r
9876 Sleep(appData.animSpeed);
\r
9878 explodeInfo.radius = 0;
\r
9879 DrawPosition(TRUE, board);
\r
9883 AnimateMove(board, fromX, fromY, toX, toY)
\r
9890 ChessSquare piece;
\r
9891 POINT start, finish, mid;
\r
9892 POINT frames[kFactor * 2 + 1];
\r
9895 if (!appData.animate) return;
\r
9896 if (doingSizing) return;
\r
9897 if (fromY < 0 || fromX < 0) return;
\r
9898 piece = board[fromY][fromX];
\r
9899 if (piece >= EmptySquare) return;
\r
9901 ScreenSquare(fromX, fromY, &start);
\r
9902 ScreenSquare(toX, toY, &finish);
\r
9904 /* All moves except knight jumps move in straight line */
\r
9905 if (!(abs(fromX-toX) == 1 && abs(fromY-toY) == 2 || abs(fromX-toX) == 2 && abs(fromY-toY) == 1)) {
\r
9906 mid.x = start.x + (finish.x - start.x) / 2;
\r
9907 mid.y = start.y + (finish.y - start.y) / 2;
\r
9909 /* Knight: make straight movement then diagonal */
\r
9910 if (abs(toY - fromY) < abs(toX - fromX)) {
\r
9911 mid.x = start.x + (finish.x - start.x) / 2;
\r
9915 mid.y = start.y + (finish.y - start.y) / 2;
\r
9919 /* Don't use as many frames for very short moves */
\r
9920 if (abs(toY - fromY) + abs(toX - fromX) <= 2)
\r
9921 Tween(&start, &mid, &finish, kFactor - 1, frames, &nFrames);
\r
9923 Tween(&start, &mid, &finish, kFactor, frames, &nFrames);
\r
9925 animInfo.from.x = fromX;
\r
9926 animInfo.from.y = fromY;
\r
9927 animInfo.to.x = toX;
\r
9928 animInfo.to.y = toY;
\r
9929 animInfo.lastpos = start;
\r
9930 animInfo.piece = piece;
\r
9931 for (n = 0; n < nFrames; n++) {
\r
9932 animInfo.pos = frames[n];
\r
9933 DrawPosition(FALSE, NULL);
\r
9934 animInfo.lastpos = animInfo.pos;
\r
9935 Sleep(appData.animSpeed);
\r
9937 animInfo.pos = finish;
\r
9938 DrawPosition(FALSE, NULL);
\r
9939 animInfo.piece = EmptySquare;
\r
9940 Explode(board, fromX, fromY, toX, toY);
\r
9943 /* Convert board position to corner of screen rect and color */
\r
9946 ScreenSquare(column, row, pt)
\r
9947 int column; int row; POINT * pt;
\r
9950 pt->x = lineGap + ((BOARD_WIDTH-1)-column) * (squareSize + lineGap) + border;
\r
9951 pt->y = lineGap + row * (squareSize + lineGap) + border;
\r
9953 pt->x = lineGap + column * (squareSize + lineGap) + border;
\r
9954 pt->y = lineGap + ((BOARD_HEIGHT-1)-row) * (squareSize + lineGap) + border;
\r
9958 /* Generate a series of frame coords from start->mid->finish.
\r
9959 The movement rate doubles until the half way point is
\r
9960 reached, then halves back down to the final destination,
\r
9961 which gives a nice slow in/out effect. The algorithmn
\r
9962 may seem to generate too many intermediates for short
\r
9963 moves, but remember that the purpose is to attract the
\r
9964 viewers attention to the piece about to be moved and
\r
9965 then to where it ends up. Too few frames would be less
\r
9969 Tween(start, mid, finish, factor, frames, nFrames)
\r
9970 POINT * start; POINT * mid;
\r
9971 POINT * finish; int factor;
\r
9972 POINT frames[]; int * nFrames;
\r
9974 int n, fraction = 1, count = 0;
\r
9976 /* Slow in, stepping 1/16th, then 1/8th, ... */
\r
9977 for (n = 0; n < factor; n++)
\r
9979 for (n = 0; n < factor; n++) {
\r
9980 frames[count].x = start->x + (mid->x - start->x) / fraction;
\r
9981 frames[count].y = start->y + (mid->y - start->y) / fraction;
\r
9983 fraction = fraction / 2;
\r
9987 frames[count] = *mid;
\r
9990 /* Slow out, stepping 1/2, then 1/4, ... */
\r
9992 for (n = 0; n < factor; n++) {
\r
9993 frames[count].x = finish->x - (finish->x - mid->x) / fraction;
\r
9994 frames[count].y = finish->y - (finish->y - mid->y) / fraction;
\r
9996 fraction = fraction * 2;
\r
10002 SettingsPopUp(ChessProgramState *cps)
\r
10003 { // [HGM] wrapper needed because handles must not be passed through back-end
\r
10004 EngineOptionsPopup(savedHwnd, cps);
\r
10007 int flock(int fid, int code)
\r
10009 HANDLE hFile = (HANDLE) _get_osfhandle(fid);
\r
10011 ov.hEvent = NULL;
\r
10013 ov.OffsetHigh = 0;
\r
10015 case 1: LockFileEx(hFile, LOCKFILE_EXCLUSIVE_LOCK, 0, 1024, 0, &ov); break; // LOCK_SH
\r
10016 case 2: LockFileEx(hFile, LOCKFILE_EXCLUSIVE_LOCK, 0, 1024, 0, &ov); break; // LOCK_EX
\r
10017 case 3: UnlockFileEx(hFile, 0, 1024, 0, &ov); break; // LOCK_UN
\r
10018 default: return -1;
\r
10027 static char col[8][20];
\r
10028 COLORREF color = *(COLORREF *) colorVariable[n];
\r
10030 snprintf(col[i], 20, "#%02lx%02lx%02lx", color&0xff, (color>>8)&0xff, (color>>16)&0xff);
\r
10035 ActivateTheme (int new)
\r
10036 { // Redo initialization of features depending on options that can occur in themes
\r
10038 if(new) InitDrawingColors();
\r
10039 fontBitmapSquareSize = 0; // request creation of new font pieces
\r
10040 InitDrawingSizes(boardSize, 0);
\r
10041 InvalidateRect(hwndMain, NULL, TRUE);
\r