2 * WinBoard.c -- Windows NT front end to XBoard
\r
4 * Copyright 1991 by Digital Equipment Corporation, Maynard,
\r
7 * Enhancements Copyright 1992-2001, 2002, 2003, 2004, 2005, 2006,
\r
8 * 2007, 2008, 2009, 2010, 2011, 2012 Free Software Foundation, Inc.
\r
10 * Enhancements Copyright 2005 Alessandro Scotti
\r
12 * XBoard borrows its colors and the bitmaps.xchess bitmap set from XChess,
\r
13 * which was written and is copyrighted by Wayne Christopher.
\r
15 * The following terms apply to Digital Equipment Corporation's copyright
\r
16 * interest in XBoard:
\r
17 * ------------------------------------------------------------------------
\r
18 * All Rights Reserved
\r
20 * Permission to use, copy, modify, and distribute this software and its
\r
21 * documentation for any purpose and without fee is hereby granted,
\r
22 * provided that the above copyright notice appear in all copies and that
\r
23 * both that copyright notice and this permission notice appear in
\r
24 * supporting documentation, and that the name of Digital not be
\r
25 * used in advertising or publicity pertaining to distribution of the
\r
26 * software without specific, written prior permission.
\r
28 * DIGITAL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
\r
29 * ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL
\r
30 * DIGITAL BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
\r
31 * ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
\r
32 * WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
\r
33 * ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
\r
35 * ------------------------------------------------------------------------
\r
37 * The following terms apply to the enhanced version of XBoard
\r
38 * distributed by the Free Software Foundation:
\r
39 * ------------------------------------------------------------------------
\r
41 * GNU XBoard is free software: you can redistribute it and/or modify
\r
42 * it under the terms of the GNU General Public License as published by
\r
43 * the Free Software Foundation, either version 3 of the License, or (at
\r
44 * your option) any later version.
\r
46 * GNU XBoard is distributed in the hope that it will be useful, but
\r
47 * WITHOUT ANY WARRANTY; without even the implied warranty of
\r
48 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
\r
49 * General Public License for more details.
\r
51 * You should have received a copy of the GNU General Public License
\r
52 * along with this program. If not, see http://www.gnu.org/licenses/. *
\r
54 *------------------------------------------------------------------------
\r
55 ** See the file ChangeLog for a revision history. */
\r
59 #include <windows.h>
\r
60 #include <winuser.h>
\r
61 #include <winsock.h>
\r
62 #include <commctrl.h>
\r
68 #include <sys/stat.h>
\r
71 #include <commdlg.h>
\r
73 #include <richedit.h>
\r
74 #include <mmsystem.h>
\r
84 #include "frontend.h"
\r
85 #include "backend.h"
\r
86 #include "winboard.h"
\r
88 #include "wclipbrd.h"
\r
89 #include "woptions.h"
\r
90 #include "wsockerr.h"
\r
91 #include "defaults.h"
\r
95 //void InitEngineUCI( const char * iniDir, ChessProgramState * cps );
\r
98 void mysrandom(unsigned int seed);
\r
100 extern int whiteFlag, blackFlag;
\r
101 Boolean flipClock = FALSE;
\r
102 extern HANDLE chatHandle[];
\r
103 extern enum ICS_TYPE ics_type;
\r
105 int MySearchPath P((char *installDir, char *name, char *fullname));
\r
106 int MyGetFullPathName P((char *name, char *fullname));
\r
107 void DisplayHoldingsCount(HDC hdc, int x, int y, int align, int copyNumber);
\r
108 VOID NewVariantPopup(HWND hwnd);
\r
109 int FinishMove P((ChessMove moveType, int fromX, int fromY, int toX, int toY,
\r
110 /*char*/int promoChar));
\r
111 void DisplayMove P((int moveNumber));
\r
112 Boolean ParseFEN P((Board board, int *blackPlaysFirst, char *fen));
\r
113 void ChatPopUp P((char *s));
\r
115 ChessSquare piece;
\r
116 POINT pos; /* window coordinates of current pos */
\r
117 POINT lastpos; /* window coordinates of last pos - used for clipping */
\r
118 POINT from; /* board coordinates of the piece's orig pos */
\r
119 POINT to; /* board coordinates of the piece's new pos */
\r
122 static AnimInfo animInfo = { EmptySquare, {-1,-1}, {-1,-1}, {-1,-1} };
\r
125 POINT start; /* window coordinates of start pos */
\r
126 POINT pos; /* window coordinates of current pos */
\r
127 POINT lastpos; /* window coordinates of last pos - used for clipping */
\r
128 POINT from; /* board coordinates of the piece's orig pos */
\r
132 static DragInfo dragInfo = { {-1,-1}, {-1,-1}, {-1,-1}, {-1,-1}, EmptySquare };
\r
135 POINT sq[2]; /* board coordinates of from, to squares */
\r
138 static HighlightInfo highlightInfo = { {{-1, -1}, {-1, -1}} };
\r
139 static HighlightInfo premoveHighlightInfo = { {{-1, -1}, {-1, -1}} };
\r
140 static HighlightInfo partnerHighlightInfo = { {{-1, -1}, {-1, -1}} };
\r
141 static HighlightInfo oldPartnerHighlight = { {{-1, -1}, {-1, -1}} };
\r
143 typedef struct { // [HGM] atomic
\r
144 int fromX, fromY, toX, toY, radius;
\r
147 static ExplodeInfo explodeInfo;
\r
149 /* Window class names */
\r
150 char szAppName[] = "WinBoard";
\r
151 char szConsoleName[] = "WBConsole";
\r
153 /* Title bar text */
\r
154 char szTitle[] = "WinBoard";
\r
155 char szConsoleTitle[] = "I C S Interaction";
\r
158 char *settingsFileName;
\r
159 Boolean saveSettingsOnExit;
\r
160 char installDir[MSG_SIZ];
\r
161 int errorExitStatus;
\r
163 BoardSize boardSize;
\r
164 Boolean chessProgram;
\r
165 //static int boardX, boardY;
\r
166 int minX, minY; // [HGM] placement: volatile limits on upper-left corner
\r
167 int squareSize, lineGap, minorSize;
\r
168 static int winW, winH;
\r
169 static RECT messageRect, whiteRect, blackRect, leftLogoRect, rightLogoRect; // [HGM] logo
\r
170 static int logoHeight = 0;
\r
171 static char messageText[MESSAGE_TEXT_MAX];
\r
172 static int clockTimerEvent = 0;
\r
173 static int loadGameTimerEvent = 0;
\r
174 static int analysisTimerEvent = 0;
\r
175 static DelayedEventCallback delayedTimerCallback;
\r
176 static int delayedTimerEvent = 0;
\r
177 static int buttonCount = 2;
\r
178 char *icsTextMenuString;
\r
180 char *firstChessProgramNames;
\r
181 char *secondChessProgramNames;
\r
183 #define PALETTESIZE 256
\r
185 HINSTANCE hInst; /* current instance */
\r
186 Boolean alwaysOnTop = FALSE;
\r
188 COLORREF lightSquareColor, darkSquareColor, whitePieceColor,
\r
189 blackPieceColor, highlightSquareColor, premoveHighlightColor;
\r
191 ColorClass currentColorClass;
\r
193 static HWND savedHwnd;
\r
194 HWND hCommPort = NULL; /* currently open comm port */
\r
195 static HWND hwndPause; /* pause button */
\r
196 static HBITMAP pieceBitmap[3][(int) BlackPawn]; /* [HGM] nr of bitmaps referred to bP in stead of wK */
\r
197 static HBRUSH lightSquareBrush, darkSquareBrush,
\r
198 blackSquareBrush, /* [HGM] for band between board and holdings */
\r
199 explodeBrush, /* [HGM] atomic */
\r
200 markerBrush, /* [HGM] markers */
\r
201 whitePieceBrush, blackPieceBrush, iconBkgndBrush /*, outlineBrush*/;
\r
202 static POINT gridEndpoints[(BOARD_RANKS + BOARD_FILES + 2) * 2];
\r
203 static DWORD gridVertexCounts[BOARD_RANKS + BOARD_FILES + 2];
\r
204 static HPEN gridPen = NULL;
\r
205 static HPEN highlightPen = NULL;
\r
206 static HPEN premovePen = NULL;
\r
207 static NPLOGPALETTE pLogPal;
\r
208 static BOOL paletteChanged = FALSE;
\r
209 static HICON iconWhite, iconBlack, iconCurrent;
\r
210 static int doingSizing = FALSE;
\r
211 static int lastSizing = 0;
\r
212 static int prevStderrPort;
\r
213 static HBITMAP userLogo;
\r
215 static HBITMAP liteBackTexture = NULL;
\r
216 static HBITMAP darkBackTexture = NULL;
\r
217 static int liteBackTextureMode = BACK_TEXTURE_MODE_PLAIN;
\r
218 static int darkBackTextureMode = BACK_TEXTURE_MODE_PLAIN;
\r
219 static int backTextureSquareSize = 0;
\r
220 static struct { int x; int y; int mode; } backTextureSquareInfo[BOARD_RANKS][BOARD_FILES];
\r
222 #if __GNUC__ && !defined(_winmajor)
\r
223 #define oldDialog 0 /* cygwin doesn't define _winmajor; mingw does */
\r
225 #if defined(_winmajor)
\r
226 #define oldDialog (_winmajor < 4)
\r
228 #define oldDialog 0
\r
232 #define INTERNATIONAL
\r
234 #ifdef INTERNATIONAL
\r
235 # define _(s) T_(s)
\r
241 # define Translate(x, y)
\r
242 # define LoadLanguageFile(s)
\r
245 #ifdef INTERNATIONAL
\r
247 Boolean barbaric; // flag indicating if translation is needed
\r
249 // list of item numbers used in each dialog (used to alter language at run time)
\r
251 #define ABOUTBOX -1 /* not sure why these are needed */
\r
252 #define ABOUTBOX2 -1
\r
254 int dialogItems[][42] = {
\r
255 { ABOUTBOX, IDOK, OPT_MESS, 400 },
\r
256 { DLG_TimeControl, IDC_Babble, OPT_TCUseMoves, OPT_TCUseInc, OPT_TCUseFixed,
\r
257 OPT_TCtext1, OPT_TCtext2, OPT_TCitext1, OPT_TCitext2, OPT_TCftext, GPB_Factors, IDC_Factor1, IDC_Factor2, IDOK, IDCANCEL },
\r
258 { DLG_LoadOptions, OPT_Autostep, OPT_AStext1, OPT_Exact, OPT_Subset, OPT_Struct, OPT_Material, OPT_Range, OPT_Difference,
\r
259 OPT_elo1t, OPT_elo2t, OPT_datet, OPT_Stretch, OPT_Stretcht, OPT_Reversed, OPT_SearchMode, OPT_Mirror, OPT_thresholds, IDOK, IDCANCEL },
\r
260 { DLG_SaveOptions, OPT_Autosave, OPT_AVPrompt, OPT_AVToFile, OPT_AVBrowse,
\r
261 801, OPT_PGN, OPT_Old, OPT_OutOfBookInfo, IDOK, IDCANCEL },
\r
262 { 1536, 1090, IDC_Directories, 1089, 1091, IDOK, IDCANCEL, 1038, IDC_IndexNr, 1037 },
\r
263 { DLG_CommPort, IDOK, IDCANCEL, IDC_Port, IDC_Rate, IDC_Bits, IDC_Parity,
\r
264 IDC_Stop, IDC_Flow, OPT_SerialHelp },
\r
265 { DLG_EditComment, IDOK, OPT_CancelComment, OPT_ClearComment, OPT_EditComment },
\r
266 { DLG_PromotionKing, PB_Chancellor, PB_Archbishop, PB_Queen, PB_Rook,
\r
267 PB_Bishop, PB_Knight, PB_King, IDCANCEL, IDC_Yes, IDC_No, IDC_Centaur },
\r
268 { ABOUTBOX2, IDC_ChessBoard },
\r
269 { DLG_GameList, OPT_GameListLoad, OPT_GameListPrev, OPT_GameListNext,
\r
270 OPT_GameListClose, IDC_GameListDoFilter },
\r
271 { DLG_EditTags, IDOK, OPT_TagsCancel, OPT_EditTags },
\r
272 { DLG_Error, IDOK },
\r
273 { DLG_Colorize, IDOK, IDCANCEL, OPT_ChooseColor, OPT_Bold, OPT_Italic,
\r
274 OPT_Underline, OPT_Strikeout, OPT_Sample },
\r
275 { DLG_Question, IDOK, IDCANCEL, OPT_QuestionText },
\r
276 { DLG_Startup, IDC_Welcome, OPT_ChessEngine, OPT_ChessServer, OPT_View,
\r
277 IDC_SPECIFY_ENG_STATIC, IDC_SPECIFY_SERVER_STATIC, OPT_AnyAdditional,
\r
278 IDOK, IDCANCEL, IDM_HELPCONTENTS },
\r
279 { DLG_IndexNumber, IDC_Index },
\r
280 { DLG_TypeInMove, IDOK, IDCANCEL },
\r
281 { DLG_TypeInName, IDOK, IDCANCEL },
\r
282 { DLG_Sound, IDC_Event, OPT_NoSound, OPT_DefaultBeep, OPT_BuiltInSound,
\r
283 OPT_WavFile, OPT_BrowseSound, OPT_DefaultSounds, IDOK, IDCANCEL, OPT_PlaySound },
\r
284 { DLG_GeneralOptions, IDOK, IDCANCEL, OPT_AlwaysOnTop, OPT_HighlightLastMove,
\r
285 OPT_AlwaysQueen, OPT_PeriodicUpdates, OPT_AnimateDragging, OPT_PonderNextMove,
\r
286 OPT_AnimateMoving, OPT_PopupExitMessage, OPT_AutoFlag, OPT_PopupMoveErrors,
\r
287 OPT_AutoFlipView, OPT_ShowButtonBar, OPT_AutoRaiseBoard, OPT_ShowCoordinates,
\r
288 OPT_Blindfold, OPT_ShowThinking, OPT_HighlightDragging, OPT_TestLegality,
\r
289 OPT_SaveExtPGN, OPT_HideThinkFromHuman, OPT_ExtraInfoInMoveHistory,
\r
290 OPT_HighlightMoveArrow, OPT_AutoLogo ,OPT_SmartMove },
\r
291 { DLG_IcsOptions, IDOK, IDCANCEL, OPT_AutoComment, OPT_AutoKibitz, OPT_AutoObserve,
\r
292 OPT_GetMoveList, OPT_LocalLineEditing, OPT_QuietPlay, OPT_SeekGraph, OPT_AutoRefresh,
\r
293 OPT_BgObserve, OPT_DualBoard, OPT_Premove, OPT_PremoveWhite, OPT_PremoveBlack,
\r
294 OPT_SmartMove, OPT_IcsAlarm, IDC_Sec, OPT_ChooseShoutColor, OPT_ChooseSShoutColor,
\r
295 OPT_ChooseChannel1Color, OPT_ChooseChannelColor, OPT_ChooseKibitzColor,
\r
296 OPT_ChooseTellColor, OPT_ChooseChallengeColor, OPT_ChooseRequestColor,
\r
297 OPT_ChooseSeekColor, OPT_ChooseNormalColor, OPT_ChooseBackgroundColor,
\r
298 OPT_DefaultColors, OPT_DontColorize, IDC_Boxes, GPB_Colors, GPB_Premove,
\r
299 GPB_General, GPB_Alarm },
\r
300 { DLG_BoardOptions, IDOK, IDCANCEL, OPT_SizeTiny, OPT_SizeTeeny, OPT_SizeDinky,
\r
301 OPT_SizePetite, OPT_SizeSlim, OPT_SizeSmall, OPT_SizeMediocre, OPT_SizeMiddling,
\r
302 OPT_SizeAverage, OPT_SizeModerate, OPT_SizeMedium, OPT_SizeBulky, OPT_SizeLarge,
\r
303 OPT_SizeBig, OPT_SizeHuge, OPT_SizeGiant, OPT_SizeColossal, OPT_SizeTitanic,
\r
304 OPT_ChooseLightSquareColor, OPT_ChooseDarkSquareColor, OPT_ChooseWhitePieceColor,
\r
305 OPT_ChooseBlackPieceColor, OPT_ChooseHighlightSquareColor, OPT_ChoosePremoveHighlightColor,
\r
306 OPT_Monochrome, OPT_AllWhite, OPT_UpsideDown, OPT_DefaultBoardColors, GPB_Colors,
\r
307 IDC_Light, IDC_Dark, IDC_White, IDC_Black, IDC_High, IDC_PreHigh, GPB_Size, OPT_Bitmaps, OPT_PieceFont, OPT_Grid },
\r
308 { DLG_NewVariant, IDOK, IDCANCEL, OPT_VariantNormal, OPT_VariantFRC, OPT_VariantWildcastle,
\r
309 OPT_VariantNocastle, OPT_VariantLosers, OPT_VariantGiveaway, OPT_VariantSuicide,
\r
310 OPT_Variant3Check, OPT_VariantTwoKings, OPT_VariantAtomic, OPT_VariantCrazyhouse,
\r
311 OPT_VariantBughouse, OPT_VariantTwilight, OPT_VariantShogi, OPT_VariantSuper,
\r
312 OPT_VariantKnightmate, OPT_VariantBerolina, OPT_VariantCylinder, OPT_VariantFairy,
\r
313 OPT_VariantMakruk, OPT_VariantGothic, OPT_VariantCapablanca, OPT_VariantJanus,
\r
314 OPT_VariantCRC, OPT_VariantFalcon, OPT_VariantCourier, OPT_VariantGreat, OPT_VariantSChess,
\r
315 OPT_VariantShatranj, OPT_VariantXiangqi, GPB_Variant, GPB_Board, IDC_Height,
\r
316 IDC_Width, IDC_Hand, IDC_Pieces, IDC_Def },
\r
317 { DLG_Fonts, IDOK, IDCANCEL, OPT_ChooseClockFont, OPT_ChooseMessageFont,
\r
318 OPT_ChooseCoordFont, OPT_ChooseTagFont, OPT_ChooseCommentsFont, OPT_ChooseConsoleFont, OPT_ChooseMoveHistoryFont, OPT_DefaultFonts,
\r
319 OPT_ClockFont, OPT_MessageFont, OPT_CoordFont, OPT_EditTagsFont, OPT_ChoosePieceFont, OPT_MessageFont8,
\r
320 OPT_SampleGameListFont, OPT_ChooseGameListFont, OPT_MessageFont7,
\r
321 OPT_CommentsFont, OPT_MessageFont5, GPB_Current, GPB_All, OPT_MessageFont6 },
\r
322 { DLG_NewGameFRC, IDC_NFG_Label, IDC_NFG_Random, IDOK, IDCANCEL },
\r
323 { DLG_GameListOptions, IDC_GLT, IDC_GLT_Up, IDC_GLT_Down, IDC_GLT_Restore,
\r
324 IDC_GLT_Default, IDOK, IDCANCEL, IDC_GLT_RestoreTo },
\r
325 { DLG_MoveHistory },
\r
326 { DLG_EvalGraph },
\r
327 { DLG_EngineOutput, IDC_EngineLabel1, IDC_Engine1_NPS, IDC_EngineLabel2, IDC_Engine2_NPS },
\r
328 { DLG_Chat, IDC_Partner, IDC_Clear, IDC_Send, },
\r
329 { DLG_EnginePlayOptions, IDC_EpPonder, IDC_EpShowThinking, IDC_EpHideThinkingHuman,
\r
330 IDC_EpPeriodicUpdates, GPB_Adjudications, IDC_Draw, IDC_Moves, IDC_Threshold,
\r
331 IDC_Centi, IDC_TestClaims, IDC_DetectMates, IDC_MaterialDraws, IDC_TrivialDraws,
\r
332 GPB_Apply, IDC_Rule, IDC_Repeats, IDC_ScoreAbs1, IDC_ScoreAbs2, IDOK, IDCANCEL },
\r
333 { DLG_OptionsUCI, IDC_PolyDir, IDC_BrowseForPolyglotDir, IDC_Hash, IDC_Path,
\r
334 IDC_BrowseForEGTB, IDC_Cache, IDC_UseBook, IDC_BrowseForBook, IDC_CPU, IDC_OwnBook1,
\r
335 IDC_OwnBook2, IDC_Depth, IDC_Variation, IDC_DefGames, IDOK, IDCANCEL },
\r
339 static char languageBuf[70000], *foreign[1000], *english[1000], *languageFile[MSG_SIZ];
\r
340 static int lastChecked;
\r
341 static char oldLanguage[MSG_SIZ], *menuText[10][30];
\r
342 extern int tinyLayout;
\r
343 extern char * menuBarText[][10];
\r
346 LoadLanguageFile(char *name)
\r
347 { //load the file with translations, and make a list of the strings to be translated, and their translations
\r
349 int i=0, j=0, n=0, k;
\r
352 if(!name || name[0] == NULLCHAR) return;
\r
353 snprintf(buf, MSG_SIZ, "%s%s", name, strchr(name, '.') ? "" : ".lng"); // auto-append lng extension
\r
354 appData.language = oldLanguage;
\r
355 if(!strcmp(buf, oldLanguage)) { barbaric = 1; return; } // this language already loaded; just switch on
\r
356 if((f = fopen(buf, "r")) == NULL) return;
\r
357 while((k = fgetc(f)) != EOF) {
\r
358 if(i >= sizeof(languageBuf)) { DisplayError("Language file too big", 0); return; }
\r
359 languageBuf[i] = k;
\r
361 if(languageBuf[n] == '"' && languageBuf[i-1] == '"') {
\r
363 if(p = strstr(languageBuf + n + 1, "\" === \"")) {
\r
364 if(p > languageBuf+n+2 && p+8 < languageBuf+i) {
\r
365 if(j >= sizeof(english)) { DisplayError("Too many translated strings", 0); return; }
\r
366 english[j] = languageBuf + n + 1; *p = 0;
\r
367 foreign[j++] = p + 7; languageBuf[i-1] = 0;
\r
368 //if(appData.debugMode) fprintf(debugFP, "translation: replace '%s' by '%s'\n", english[j-1], foreign[j-1]);
\r
373 } else if(i > 0 && languageBuf[i-1] == '\\') {
\r
375 case 'n': k = '\n'; break;
\r
376 case 'r': k = '\r'; break;
\r
377 case 't': k = '\t'; break;
\r
379 languageBuf[--i] = k;
\r
384 barbaric = (j != 0);
\r
385 safeStrCpy(oldLanguage, buf, sizeof(oldLanguage)/sizeof(oldLanguage[0]) );
\r
390 { // return the translation of the given string
\r
391 // efficiency can be improved a lot...
\r
393 static char buf[MSG_SIZ];
\r
394 //if(appData.debugMode) fprintf(debugFP, "T_(%s)\n", s);
\r
395 if(!barbaric) return s;
\r
396 if(!s) return ""; // sanity
\r
397 while(english[i]) {
\r
398 if(!strcmp(s, english[i])) return foreign[i];
\r
399 if(english[i][0] == '%' && strstr(s, english[i]+1) == s) { // allow translation of strings with variable ending
\r
400 snprintf(buf, MSG_SIZ, "%s%s", foreign[i], s + strlen(english[i]+1)); // keep unmatched portion
\r
409 Translate(HWND hDlg, int dialogID)
\r
410 { // translate all text items in the given dialog
\r
412 char buf[MSG_SIZ], *s;
\r
413 if(!barbaric) return;
\r
414 while(dialogItems[i][0] && dialogItems[i][0] != dialogID) i++; // find the dialog description
\r
415 if(dialogItems[i][0] != dialogID) return; // unknown dialog, should not happen
\r
416 GetWindowText( hDlg, buf, MSG_SIZ );
\r
418 if(strcmp(buf, s)) SetWindowText(hDlg, s); // replace by translated string (if different)
\r
419 for(j=1; k=dialogItems[i][j]; j++) { // translate all listed dialog items
\r
420 GetDlgItemText(hDlg, k, buf, MSG_SIZ);
\r
421 if(strlen(buf) == 0) continue;
\r
423 if(strcmp(buf, s)) SetDlgItemText(hDlg, k, s); // replace by translated string (if different)
\r
428 TranslateOneMenu(int i, HMENU subMenu)
\r
431 static MENUITEMINFO info;
\r
433 info.cbSize = sizeof(MENUITEMINFO);
\r
434 info.fMask = MIIM_STATE | MIIM_TYPE;
\r
435 for(j=GetMenuItemCount(subMenu)-1; j>=0; j--){
\r
437 info.dwTypeData = buf;
\r
438 info.cch = sizeof(buf);
\r
439 GetMenuItemInfo(subMenu, j, TRUE, &info);
\r
441 if(menuText[i][j]) safeStrCpy(buf, menuText[i][j], sizeof(buf)/sizeof(buf[0]) );
\r
442 else menuText[i][j] = strdup(buf); // remember original on first change
\r
444 if(buf[0] == NULLCHAR) continue;
\r
445 info.dwTypeData = T_(buf);
\r
446 info.cch = strlen(buf)+1;
\r
447 SetMenuItemInfo(subMenu, j, TRUE, &info);
\r
453 TranslateMenus(int addLanguage)
\r
456 WIN32_FIND_DATA fileData;
\r
458 #define IDM_English 1970
\r
460 HMENU mainMenu = GetMenu(hwndMain);
\r
461 for (i=GetMenuItemCount(mainMenu)-1; i>=0; i--) {
\r
462 HMENU subMenu = GetSubMenu(mainMenu, i);
\r
463 ModifyMenu(mainMenu, i, MF_STRING|MF_BYPOSITION|MF_POPUP|EnableMenuItem(mainMenu, i, MF_BYPOSITION),
\r
464 (UINT) subMenu, T_(menuBarText[tinyLayout][i]));
\r
465 TranslateOneMenu(i, subMenu);
\r
467 DrawMenuBar(hwndMain);
\r
470 if(!addLanguage) return;
\r
471 if((hFind = FindFirstFile("*.LNG", &fileData)) != INVALID_HANDLE_VALUE) {
\r
472 HMENU mainMenu = GetMenu(hwndMain);
\r
473 HMENU subMenu = GetSubMenu(mainMenu, GetMenuItemCount(mainMenu)-1);
\r
474 AppendMenu(subMenu, MF_SEPARATOR, (UINT_PTR) 0, NULL);
\r
475 AppendMenu(subMenu, MF_ENABLED|MF_STRING|(barbaric?MF_UNCHECKED:MF_CHECKED), (UINT_PTR) IDM_English, (LPCTSTR) "English");
\r
476 i = 0; lastChecked = IDM_English;
\r
478 char *p, *q = fileData.cFileName;
\r
479 int checkFlag = MF_UNCHECKED;
\r
480 languageFile[i] = strdup(q);
\r
481 if(barbaric && !strcmp(oldLanguage, q)) {
\r
482 checkFlag = MF_CHECKED;
\r
483 lastChecked = IDM_English + i + 1;
\r
484 CheckMenuItem(mainMenu, IDM_English, MF_BYCOMMAND|MF_UNCHECKED);
\r
486 *q = ToUpper(*q); while(*++q) *q = ToLower(*q);
\r
487 p = strstr(fileData.cFileName, ".lng");
\r
489 AppendMenu(subMenu, MF_ENABLED|MF_STRING|checkFlag, (UINT_PTR) IDM_English + ++i, (LPCTSTR) fileData.cFileName);
\r
490 } while(FindNextFile(hFind, &fileData));
\r
497 #define IDM_RecentEngines 3000
\r
500 RecentEngineMenu (char *s)
\r
502 if(appData.icsActive) return;
\r
503 if(appData.recentEngines > 0 && *s) { // feature is on, and list non-empty
\r
504 HMENU mainMenu = GetMenu(hwndMain);
\r
505 HMENU subMenu = GetSubMenu(mainMenu, 5); // Engine menu
\r
506 int i=IDM_RecentEngines;
\r
507 recentEngines = strdup(appData.recentEngineList); // remember them as they are in menu
\r
508 AppendMenu(subMenu, MF_SEPARATOR, (UINT_PTR) 0, NULL);
\r
510 char *p = strchr(s, '\n');
\r
511 if(p == NULL) return; // malformed!
\r
513 AppendMenu(subMenu, MF_ENABLED|MF_STRING|MF_UNCHECKED, (UINT_PTR) i++, (LPCTSTR) s);
\r
527 int cliWidth, cliHeight;
\r
530 SizeInfo sizeInfo[] =
\r
532 { "tiny", 21, 0, 1, 1, 0, 0 },
\r
533 { "teeny", 25, 1, 1, 1, 0, 0 },
\r
534 { "dinky", 29, 1, 1, 1, 0, 0 },
\r
535 { "petite", 33, 1, 1, 1, 0, 0 },
\r
536 { "slim", 37, 2, 1, 0, 0, 0 },
\r
537 { "small", 40, 2, 1, 0, 0, 0 },
\r
538 { "mediocre", 45, 2, 1, 0, 0, 0 },
\r
539 { "middling", 49, 2, 0, 0, 0, 0 },
\r
540 { "average", 54, 2, 0, 0, 0, 0 },
\r
541 { "moderate", 58, 3, 0, 0, 0, 0 },
\r
542 { "medium", 64, 3, 0, 0, 0, 0 },
\r
543 { "bulky", 72, 3, 0, 0, 0, 0 },
\r
544 { "large", 80, 3, 0, 0, 0, 0 },
\r
545 { "big", 87, 3, 0, 0, 0, 0 },
\r
546 { "huge", 95, 3, 0, 0, 0, 0 },
\r
547 { "giant", 108, 3, 0, 0, 0, 0 },
\r
548 { "colossal", 116, 4, 0, 0, 0, 0 },
\r
549 { "titanic", 129, 4, 0, 0, 0, 0 },
\r
550 { NULL, 0, 0, 0, 0, 0, 0 }
\r
553 #define MF(x) {x, {{0,}, 0. }, {0, }, 0}
\r
554 MyFont fontRec[NUM_SIZES][NUM_FONTS] =
\r
556 { MF(CLOCK_FONT_TINY), MF(MESSAGE_FONT_TINY), MF(COORD_FONT_TINY), MF(CONSOLE_FONT_TINY), MF(COMMENT_FONT_TINY), MF(EDITTAGS_FONT_TINY), MF(MOVEHISTORY_FONT_ALL), MF(GAMELIST_FONT_ALL) },
\r
557 { MF(CLOCK_FONT_TEENY), MF(MESSAGE_FONT_TEENY), MF(COORD_FONT_TEENY), MF(CONSOLE_FONT_TEENY), MF(COMMENT_FONT_TEENY), MF(EDITTAGS_FONT_TEENY), MF(MOVEHISTORY_FONT_ALL), MF(GAMELIST_FONT_ALL) },
\r
558 { MF(CLOCK_FONT_DINKY), MF(MESSAGE_FONT_DINKY), MF(COORD_FONT_DINKY), MF(CONSOLE_FONT_DINKY), MF(COMMENT_FONT_DINKY), MF(EDITTAGS_FONT_DINKY), MF(MOVEHISTORY_FONT_ALL), MF(GAMELIST_FONT_ALL) },
\r
559 { MF(CLOCK_FONT_PETITE), MF(MESSAGE_FONT_PETITE), MF(COORD_FONT_PETITE), MF(CONSOLE_FONT_PETITE), MF(COMMENT_FONT_PETITE), MF(EDITTAGS_FONT_PETITE), MF(MOVEHISTORY_FONT_ALL), MF(GAMELIST_FONT_ALL) },
\r
560 { MF(CLOCK_FONT_SLIM), MF(MESSAGE_FONT_SLIM), MF(COORD_FONT_SLIM), MF(CONSOLE_FONT_SLIM), MF(COMMENT_FONT_SLIM), MF(EDITTAGS_FONT_SLIM), MF(MOVEHISTORY_FONT_ALL), MF(GAMELIST_FONT_ALL) },
\r
561 { MF(CLOCK_FONT_SMALL), MF(MESSAGE_FONT_SMALL), MF(COORD_FONT_SMALL), MF(CONSOLE_FONT_SMALL), MF(COMMENT_FONT_SMALL), MF(EDITTAGS_FONT_SMALL), MF(MOVEHISTORY_FONT_ALL), MF(GAMELIST_FONT_ALL) },
\r
562 { MF(CLOCK_FONT_MEDIOCRE), MF(MESSAGE_FONT_MEDIOCRE), MF(COORD_FONT_MEDIOCRE), MF(CONSOLE_FONT_MEDIOCRE), MF(COMMENT_FONT_MEDIOCRE), MF(EDITTAGS_FONT_MEDIOCRE), MF(MOVEHISTORY_FONT_ALL), MF(GAMELIST_FONT_ALL) },
\r
563 { MF(CLOCK_FONT_MIDDLING), MF(MESSAGE_FONT_MIDDLING), MF(COORD_FONT_MIDDLING), MF(CONSOLE_FONT_MIDDLING), MF(COMMENT_FONT_MIDDLING), MF(EDITTAGS_FONT_MIDDLING), MF(MOVEHISTORY_FONT_ALL), MF(GAMELIST_FONT_ALL) },
\r
564 { MF(CLOCK_FONT_AVERAGE), MF(MESSAGE_FONT_AVERAGE), MF(COORD_FONT_AVERAGE), MF(CONSOLE_FONT_AVERAGE), MF(COMMENT_FONT_AVERAGE), MF(EDITTAGS_FONT_AVERAGE), MF(MOVEHISTORY_FONT_ALL), MF(GAMELIST_FONT_ALL) },
\r
565 { MF(CLOCK_FONT_MODERATE), MF(MESSAGE_FONT_MODERATE), MF(COORD_FONT_MODERATE), MF(CONSOLE_FONT_MODERATE), MF(COMMENT_FONT_MODERATE), MF(EDITTAGS_FONT_MODERATE), MF(MOVEHISTORY_FONT_ALL), MF(GAMELIST_FONT_ALL) },
\r
566 { MF(CLOCK_FONT_MEDIUM), MF(MESSAGE_FONT_MEDIUM), MF(COORD_FONT_MEDIUM), MF(CONSOLE_FONT_MEDIUM), MF(COMMENT_FONT_MEDIUM), MF(EDITTAGS_FONT_MEDIUM), MF(MOVEHISTORY_FONT_ALL), MF(GAMELIST_FONT_ALL) },
\r
567 { MF(CLOCK_FONT_BULKY), MF(MESSAGE_FONT_BULKY), MF(COORD_FONT_BULKY), MF(CONSOLE_FONT_BULKY), MF(COMMENT_FONT_BULKY), MF(EDITTAGS_FONT_BULKY), MF(MOVEHISTORY_FONT_ALL), MF(GAMELIST_FONT_ALL) },
\r
568 { MF(CLOCK_FONT_LARGE), MF(MESSAGE_FONT_LARGE), MF(COORD_FONT_LARGE), MF(CONSOLE_FONT_LARGE), MF(COMMENT_FONT_LARGE), MF(EDITTAGS_FONT_LARGE), MF(MOVEHISTORY_FONT_ALL), MF(GAMELIST_FONT_ALL) },
\r
569 { MF(CLOCK_FONT_BIG), MF(MESSAGE_FONT_BIG), MF(COORD_FONT_BIG), MF(CONSOLE_FONT_BIG), MF(COMMENT_FONT_BIG), MF(EDITTAGS_FONT_BIG), MF(MOVEHISTORY_FONT_ALL), MF(GAMELIST_FONT_ALL) },
\r
570 { MF(CLOCK_FONT_HUGE), MF(MESSAGE_FONT_HUGE), MF(COORD_FONT_HUGE), MF(CONSOLE_FONT_HUGE), MF(COMMENT_FONT_HUGE), MF(EDITTAGS_FONT_HUGE), MF(MOVEHISTORY_FONT_ALL), MF(GAMELIST_FONT_ALL) },
\r
571 { MF(CLOCK_FONT_GIANT), MF(MESSAGE_FONT_GIANT), MF(COORD_FONT_GIANT), MF(CONSOLE_FONT_GIANT), MF(COMMENT_FONT_GIANT), MF(EDITTAGS_FONT_GIANT), MF(MOVEHISTORY_FONT_ALL), MF(GAMELIST_FONT_ALL) },
\r
572 { MF(CLOCK_FONT_COLOSSAL), MF(MESSAGE_FONT_COLOSSAL), MF(COORD_FONT_COLOSSAL), MF(CONSOLE_FONT_COLOSSAL), MF(COMMENT_FONT_COLOSSAL), MF(EDITTAGS_FONT_COLOSSAL), MF(MOVEHISTORY_FONT_ALL), MF (GAMELIST_FONT_ALL) },
\r
573 { MF(CLOCK_FONT_TITANIC), MF(MESSAGE_FONT_TITANIC), MF(COORD_FONT_TITANIC), MF(CONSOLE_FONT_TITANIC), MF(COMMENT_FONT_TITANIC), MF(EDITTAGS_FONT_TITANIC), MF(MOVEHISTORY_FONT_ALL), MF(GAMELIST_FONT_ALL) },
\r
576 MyFont *font[NUM_SIZES][NUM_FONTS];
\r
585 #define BUTTON_WIDTH (tinyLayout ? 16 : 32)
\r
586 #define N_BUTTONS 5
\r
588 MyButtonDesc buttonDesc[N_BUTTONS] =
\r
590 {"<<", IDM_ToStart, NULL, NULL},
\r
591 {"<", IDM_Backward, NULL, NULL},
\r
592 {"P", IDM_Pause, NULL, NULL},
\r
593 {">", IDM_Forward, NULL, NULL},
\r
594 {">>", IDM_ToEnd, NULL, NULL},
\r
597 int tinyLayout = 0, smallLayout = 0;
\r
598 #define MENU_BAR_ITEMS 9
\r
599 char *menuBarText[2][MENU_BAR_ITEMS+1] = {
\r
600 { N_("&File"), N_("&Edit"), N_("&View"), N_("&Mode"), N_("&Action"), N_("E&ngine"), N_("&Options"), N_("&Help"), NULL },
\r
601 { N_("&F"), N_("&E"), N_("&V"), N_("&M"), N_("&A"), N_("&N"), N_("&O"), N_("&H"), NULL },
\r
605 MySound sounds[(int)NSoundClasses];
\r
606 MyTextAttribs textAttribs[(int)NColorClasses];
\r
608 MyColorizeAttribs colorizeAttribs[] = {
\r
609 { (COLORREF)0, 0, N_("Shout Text") },
\r
610 { (COLORREF)0, 0, N_("SShout/CShout") },
\r
611 { (COLORREF)0, 0, N_("Channel 1 Text") },
\r
612 { (COLORREF)0, 0, N_("Channel Text") },
\r
613 { (COLORREF)0, 0, N_("Kibitz Text") },
\r
614 { (COLORREF)0, 0, N_("Tell Text") },
\r
615 { (COLORREF)0, 0, N_("Challenge Text") },
\r
616 { (COLORREF)0, 0, N_("Request Text") },
\r
617 { (COLORREF)0, 0, N_("Seek Text") },
\r
618 { (COLORREF)0, 0, N_("Normal Text") },
\r
619 { (COLORREF)0, 0, N_("None") }
\r
624 static char *commentTitle;
\r
625 static char *commentText;
\r
626 static int commentIndex;
\r
627 static Boolean editComment = FALSE;
\r
630 char errorTitle[MSG_SIZ];
\r
631 char errorMessage[2*MSG_SIZ];
\r
632 HWND errorDialog = NULL;
\r
633 BOOLEAN moveErrorMessageUp = FALSE;
\r
634 BOOLEAN consoleEcho = TRUE;
\r
635 CHARFORMAT consoleCF;
\r
636 COLORREF consoleBackgroundColor;
\r
638 char *programVersion;
\r
644 typedef int CPKind;
\r
653 SOCKET sock2; /* stderr socket for OpenRcmd */
\r
656 #define INPUT_SOURCE_BUF_SIZE 4096
\r
658 typedef struct _InputSource {
\r
665 char buf[INPUT_SOURCE_BUF_SIZE];
\r
669 InputCallback func;
\r
670 struct _InputSource *second; /* for stderr thread on CPRcmd */
\r
674 InputSource *consoleInputSource;
\r
679 VOID ConsoleOutput(char* data, int length, int forceVisible);
\r
680 VOID ConsoleCreate();
\r
682 ConsoleWndProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam);
\r
683 VOID ColorizeTextPopup(HWND hwnd, ColorClass cc);
\r
684 VOID PrintCommSettings(FILE *f, char *name, DCB *dcb);
\r
685 VOID ParseCommSettings(char *arg, DCB *dcb);
\r
687 StartupDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam);
\r
688 VOID APIENTRY MenuPopup(HWND hwnd, POINT pt, HMENU hmenu, UINT def);
\r
689 void ParseIcsTextMenu(char *icsTextMenuString);
\r
690 VOID PopUpNameDialog(char firstchar);
\r
691 VOID UpdateSampleText(HWND hDlg, int id, MyColorizeAttribs *mca);
\r
695 int GameListOptions();
\r
697 int dummy; // [HGM] for obsolete args
\r
699 HWND hwndMain = NULL; /* root window*/
\r
700 HWND hwndConsole = NULL;
\r
701 HWND commentDialog = NULL;
\r
702 HWND moveHistoryDialog = NULL;
\r
703 HWND evalGraphDialog = NULL;
\r
704 HWND engineOutputDialog = NULL;
\r
705 HWND gameListDialog = NULL;
\r
706 HWND editTagsDialog = NULL;
\r
708 int commentUp = FALSE;
\r
710 WindowPlacement wpMain;
\r
711 WindowPlacement wpConsole;
\r
712 WindowPlacement wpComment;
\r
713 WindowPlacement wpMoveHistory;
\r
714 WindowPlacement wpEvalGraph;
\r
715 WindowPlacement wpEngineOutput;
\r
716 WindowPlacement wpGameList;
\r
717 WindowPlacement wpTags;
\r
719 VOID EngineOptionsPopup(); // [HGM] settings
\r
721 VOID GothicPopUp(char *title, VariantClass variant);
\r
723 * Setting "frozen" should disable all user input other than deleting
\r
724 * the window. We do this while engines are initializing themselves.
\r
726 static int frozen = 0;
\r
727 static int oldMenuItemState[MENU_BAR_ITEMS];
\r
733 if (frozen) return;
\r
735 hmenu = GetMenu(hwndMain);
\r
736 for (i=0; i<MENU_BAR_ITEMS; i++) {
\r
737 oldMenuItemState[i] = EnableMenuItem(hmenu, i, MF_BYPOSITION|MF_GRAYED);
\r
739 DrawMenuBar(hwndMain);
\r
742 /* Undo a FreezeUI */
\r
748 if (!frozen) return;
\r
750 hmenu = GetMenu(hwndMain);
\r
751 for (i=0; i<MENU_BAR_ITEMS; i++) {
\r
752 EnableMenuItem(hmenu, i, MF_BYPOSITION|oldMenuItemState[i]);
\r
754 DrawMenuBar(hwndMain);
\r
757 /*static*/ int fromX = -1, fromY = -1, toX, toY; // [HGM] moved upstream, so JAWS can use them
\r
759 /* JAWS preparation patch (WinBoard for the sight impaired). Define required insertions as empty */
\r
765 #define JAWS_ALT_INTERCEPT
\r
766 #define JAWS_KBUP_NAVIGATION
\r
767 #define JAWS_KBDOWN_NAVIGATION
\r
768 #define JAWS_MENU_ITEMS
\r
769 #define JAWS_SILENCE
\r
770 #define JAWS_REPLAY
\r
772 #define JAWS_COPYRIGHT
\r
773 #define JAWS_DELETE(X) X
\r
774 #define SAYMACHINEMOVE()
\r
778 /*---------------------------------------------------------------------------*\
\r
782 \*---------------------------------------------------------------------------*/
\r
785 WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
\r
786 LPSTR lpCmdLine, int nCmdShow)
\r
789 HANDLE hAccelMain, hAccelNoAlt, hAccelNoICS;
\r
790 // INITCOMMONCONTROLSEX ex;
\r
794 LoadLibrary("RICHED32.DLL");
\r
795 consoleCF.cbSize = sizeof(CHARFORMAT);
\r
797 if (!InitApplication(hInstance)) {
\r
800 if (!InitInstance(hInstance, nCmdShow, lpCmdLine)) {
\r
807 // InitCommonControlsEx(&ex);
\r
808 InitCommonControls();
\r
810 hAccelMain = LoadAccelerators (hInstance, szAppName);
\r
811 hAccelNoAlt = LoadAccelerators (hInstance, "NO_ALT");
\r
812 hAccelNoICS = LoadAccelerators( hInstance, "NO_ICS"); /* [AS] No Ctrl-V on ICS!!! */
\r
814 /* Acquire and dispatch messages until a WM_QUIT message is received. */
\r
816 while (GetMessage(&msg, /* message structure */
\r
817 NULL, /* handle of window receiving the message */
\r
818 0, /* lowest message to examine */
\r
819 0)) /* highest message to examine */
\r
822 if(msg.message == WM_CHAR && msg.wParam == '\t') {
\r
823 // [HGM] navigate: switch between all windows with tab
\r
824 HWND e1 = NULL, e2 = NULL, mh = NULL, hInput = NULL, hText = NULL;
\r
825 int i, currentElement = 0;
\r
827 // first determine what element of the chain we come from (if any)
\r
828 if(appData.icsActive) {
\r
829 hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
830 hText = GetDlgItem(hwndConsole, OPT_ConsoleText);
\r
832 if(engineOutputDialog && EngineOutputIsUp()) {
\r
833 e1 = GetDlgItem(engineOutputDialog, IDC_EngineMemo1);
\r
834 e2 = GetDlgItem(engineOutputDialog, IDC_EngineMemo2);
\r
836 if(moveHistoryDialog && MoveHistoryIsUp()) {
\r
837 mh = GetDlgItem(moveHistoryDialog, IDC_MoveHistory);
\r
839 if(msg.hwnd == hwndMain) currentElement = 7 ; else
\r
840 if(msg.hwnd == engineOutputDialog) currentElement = 2; else
\r
841 if(msg.hwnd == e1) currentElement = 2; else
\r
842 if(msg.hwnd == e2) currentElement = 3; else
\r
843 if(msg.hwnd == moveHistoryDialog) currentElement = 4; else
\r
844 if(msg.hwnd == mh) currentElement = 4; else
\r
845 if(msg.hwnd == evalGraphDialog) currentElement = 6; else
\r
846 if(msg.hwnd == hText) currentElement = 5; else
\r
847 if(msg.hwnd == hInput) currentElement = 6; else
\r
848 for (i = 0; i < N_BUTTONS; i++) {
\r
849 if (buttonDesc[i].hwnd == msg.hwnd) { currentElement = 1; break; }
\r
852 // determine where to go to
\r
853 if(currentElement) { HWND h = NULL; int direction = GetKeyState(VK_SHIFT) < 0 ? -1 : 1;
\r
855 currentElement = (currentElement + direction) % 7;
\r
856 switch(currentElement) {
\r
858 h = hwndMain; break; // passing this case always makes the loop exit
\r
860 h = buttonDesc[0].hwnd; break; // could be NULL
\r
862 if(!EngineOutputIsUp()) continue; // skip closed auxiliary windows
\r
865 if(!EngineOutputIsUp()) continue;
\r
868 if(!MoveHistoryIsUp()) continue;
\r
870 // case 6: // input to eval graph does not seem to get here!
\r
871 // if(!EvalGraphIsUp()) continue;
\r
872 // h = evalGraphDialog; break;
\r
874 if(!appData.icsActive) continue;
\r
878 if(!appData.icsActive) continue;
\r
884 if(currentElement > 4 && IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);
\r
885 if(currentElement < 5 && IsIconic(hwndMain)) ShowWindow(hwndMain, SW_RESTORE); // all open together
\r
888 continue; // this message now has been processed
\r
892 if (!(commentDialog && IsDialogMessage(commentDialog, &msg)) &&
\r
893 !(moveHistoryDialog && IsDialogMessage(moveHistoryDialog, &msg)) &&
\r
894 !(evalGraphDialog && IsDialogMessage(evalGraphDialog, &msg)) &&
\r
895 !(engineOutputDialog && IsDialogMessage(engineOutputDialog, &msg)) &&
\r
896 !(editTagsDialog && IsDialogMessage(editTagsDialog, &msg)) &&
\r
897 !(gameListDialog && IsDialogMessage(gameListDialog, &msg)) &&
\r
898 !(errorDialog && IsDialogMessage(errorDialog, &msg)) &&
\r
899 !(!frozen && TranslateAccelerator(hwndMain, hAccelMain, &msg)) && JAWS_ACCEL
\r
900 !(!hwndConsole && TranslateAccelerator(hwndMain, hAccelNoICS, &msg)) &&
\r
901 !(!hwndConsole && TranslateAccelerator(hwndMain, hAccelNoAlt, &msg))) {
\r
902 int done = 0, i; // [HGM] chat: dispatch cat-box messages
\r
903 for(i=0; i<MAX_CHAT; i++)
\r
904 if(chatHandle[i] && IsDialogMessage(chatHandle[i], &msg)) {
\r
907 if(done) continue; // [HGM] chat: end patch
\r
908 TranslateMessage(&msg); /* Translates virtual key codes */
\r
909 DispatchMessage(&msg); /* Dispatches message to window */
\r
914 return (msg.wParam); /* Returns the value from PostQuitMessage */
\r
917 /*---------------------------------------------------------------------------*\
\r
919 * Initialization functions
\r
921 \*---------------------------------------------------------------------------*/
\r
925 { // update user logo if necessary
\r
926 static char oldUserName[MSG_SIZ], dir[MSG_SIZ], *curName;
\r
928 if(appData.autoLogo) {
\r
929 curName = UserName();
\r
930 if(strcmp(curName, oldUserName)) {
\r
931 GetCurrentDirectory(MSG_SIZ, dir);
\r
932 SetCurrentDirectory(installDir);
\r
933 snprintf(oldUserName, MSG_SIZ, "logos\\%s.bmp", curName);
\r
934 userLogo = LoadImage( 0, oldUserName, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );
\r
935 safeStrCpy(oldUserName, curName, sizeof(oldUserName)/sizeof(oldUserName[0]) );
\r
936 if(userLogo == NULL)
\r
937 userLogo = LoadImage( 0, "logos\\dummy.bmp", IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );
\r
938 SetCurrentDirectory(dir); /* return to prev directory */
\r
944 InitApplication(HINSTANCE hInstance)
\r
948 /* Fill in window class structure with parameters that describe the */
\r
951 wc.style = CS_HREDRAW | CS_VREDRAW; /* Class style(s). */
\r
952 wc.lpfnWndProc = (WNDPROC)WndProc; /* Window Procedure */
\r
953 wc.cbClsExtra = 0; /* No per-class extra data. */
\r
954 wc.cbWndExtra = 0; /* No per-window extra data. */
\r
955 wc.hInstance = hInstance; /* Owner of this class */
\r
956 wc.hIcon = LoadIcon(hInstance, "icon_white");
\r
957 wc.hCursor = LoadCursor(NULL, IDC_ARROW); /* Cursor */
\r
958 wc.hbrBackground = (HBRUSH)(COLOR_WINDOW+1); /* Default color */
\r
959 wc.lpszMenuName = szAppName; /* Menu name from .RC */
\r
960 wc.lpszClassName = szAppName; /* Name to register as */
\r
962 /* Register the window class and return success/failure code. */
\r
963 if (!RegisterClass(&wc)) return FALSE;
\r
965 wc.style = CS_HREDRAW | CS_VREDRAW;
\r
966 wc.lpfnWndProc = (WNDPROC)ConsoleWndProc;
\r
968 wc.cbWndExtra = DLGWINDOWEXTRA;
\r
969 wc.hInstance = hInstance;
\r
970 wc.hIcon = LoadIcon(hInstance, "icon_white");
\r
971 wc.hCursor = LoadCursor(NULL, IDC_ARROW);
\r
972 wc.hbrBackground = (HBRUSH)(COLOR_MENU+1);
\r
973 wc.lpszMenuName = NULL;
\r
974 wc.lpszClassName = szConsoleName;
\r
976 if (!RegisterClass(&wc)) return FALSE;
\r
981 /* Set by InitInstance, used by EnsureOnScreen */
\r
982 int screenHeight, screenWidth;
\r
985 EnsureOnScreen(int *x, int *y, int minX, int minY)
\r
987 // int gap = GetSystemMetrics(SM_CYFRAME) + GetSystemMetrics(SM_CYCAPTION);
\r
988 /* Be sure window at (x,y) is not off screen (or even mostly off screen) */
\r
989 if (*x > screenWidth - 32) *x = 0;
\r
990 if (*y > screenHeight - 32) *y = 0;
\r
991 if (*x < minX) *x = minX;
\r
992 if (*y < minY) *y = minY;
\r
996 LoadLogo(ChessProgramState *cps, int n, Boolean ics)
\r
998 char buf[MSG_SIZ], dir[MSG_SIZ];
\r
999 GetCurrentDirectory(MSG_SIZ, dir);
\r
1000 SetCurrentDirectory(installDir);
\r
1001 if( appData.logo[n] && appData.logo[n][0] != NULLCHAR) {
\r
1002 cps->programLogo = LoadImage( 0, appData.logo[n], IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );
\r
1004 if (cps->programLogo == NULL && appData.debugMode) {
\r
1005 fprintf( debugFP, "Unable to load logo bitmap '%s'\n", appData.logo[n] );
\r
1007 } else if(appData.autoLogo) {
\r
1008 if(ics) { // [HGM] logo: in ICS mode second can be used for ICS
\r
1009 sprintf(buf, "logos\\%s.bmp", appData.icsHost);
\r
1010 cps->programLogo = LoadImage( 0, buf, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );
\r
1012 if(appData.directory[n] && appData.directory[n][0]) {
\r
1013 SetCurrentDirectory(appData.directory[n]);
\r
1014 cps->programLogo = LoadImage( 0, "logo.bmp", IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );
\r
1017 SetCurrentDirectory(dir); /* return to prev directory */
\r
1023 ZeroMemory( &backTextureSquareInfo, sizeof(backTextureSquareInfo) );
\r
1024 backTextureSquareSize = 0; // kludge to force recalculation of texturemode
\r
1026 if( appData.liteBackTextureFile && appData.liteBackTextureFile[0] != NULLCHAR && appData.liteBackTextureFile[0] != '*' ) {
\r
1027 liteBackTexture = LoadImage( 0, appData.liteBackTextureFile, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );
\r
1028 liteBackTextureMode = appData.liteBackTextureMode;
\r
1030 if (liteBackTexture == NULL && appData.debugMode) {
\r
1031 fprintf( debugFP, "Unable to load lite texture bitmap '%s'\n", appData.liteBackTextureFile );
\r
1035 if( appData.darkBackTextureFile && appData.darkBackTextureFile[0] != NULLCHAR && appData.darkBackTextureFile[0] != '*' ) {
\r
1036 darkBackTexture = LoadImage( 0, appData.darkBackTextureFile, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );
\r
1037 darkBackTextureMode = appData.darkBackTextureMode;
\r
1039 if (darkBackTexture == NULL && appData.debugMode) {
\r
1040 fprintf( debugFP, "Unable to load dark texture bitmap '%s'\n", appData.darkBackTextureFile );
\r
1046 InitInstance(HINSTANCE hInstance, int nCmdShow, LPSTR lpCmdLine)
\r
1048 HWND hwnd; /* Main window handle. */
\r
1050 WINDOWPLACEMENT wp;
\r
1053 hInst = hInstance; /* Store instance handle in our global variable */
\r
1054 programName = szAppName;
\r
1056 if (SearchPath(NULL, "WinBoard.exe", NULL, MSG_SIZ, installDir, &filepart)) {
\r
1057 *filepart = NULLCHAR;
\r
1059 GetCurrentDirectory(MSG_SIZ, installDir);
\r
1061 gameInfo.boardWidth = gameInfo.boardHeight = 8; // [HGM] won't have open window otherwise
\r
1062 screenWidth = screenHeight = 1000; // [HGM] placement: kludge to allow calling EnsureOnScreen from InitAppData
\r
1063 InitAppData(lpCmdLine); /* Get run-time parameters */
\r
1064 /* xboard, and older WinBoards, controlled the move sound with the
\r
1065 appData.ringBellAfterMoves option. In the current WinBoard, we
\r
1066 always turn the option on (so that the backend will call us),
\r
1067 then let the user turn the sound off by setting it to silence if
\r
1068 desired. To accommodate old winboard.ini files saved by old
\r
1069 versions of WinBoard, we also turn off the sound if the option
\r
1070 was initially set to false. [HGM] taken out of InitAppData */
\r
1071 if (!appData.ringBellAfterMoves) {
\r
1072 sounds[(int)SoundMove].name = strdup("");
\r
1073 appData.ringBellAfterMoves = TRUE;
\r
1075 if (appData.debugMode) {
\r
1076 debugFP = fopen(appData.nameOfDebugFile, "w");
\r
1077 setbuf(debugFP, NULL);
\r
1080 LoadLanguageFile(appData.language);
\r
1084 // InitEngineUCI( installDir, &first ); // [HGM] incorporated in InitBackEnd1()
\r
1085 // InitEngineUCI( installDir, &second );
\r
1087 /* Create a main window for this application instance. */
\r
1088 hwnd = CreateWindow(szAppName, szTitle,
\r
1089 (WS_OVERLAPPEDWINDOW & ~WS_MAXIMIZEBOX),
\r
1090 CW_USEDEFAULT, 0, CW_USEDEFAULT, 0,
\r
1091 NULL, NULL, hInstance, NULL);
\r
1094 /* If window could not be created, return "failure" */
\r
1099 /* [HGM] logo: Load logos if specified (must be done before InitDrawingSizes) */
\r
1100 LoadLogo(&first, 0, FALSE);
\r
1101 LoadLogo(&second, 1, appData.icsActive);
\r
1105 iconWhite = LoadIcon(hInstance, "icon_white");
\r
1106 iconBlack = LoadIcon(hInstance, "icon_black");
\r
1107 iconCurrent = iconWhite;
\r
1108 InitDrawingColors();
\r
1109 screenHeight = GetSystemMetrics(SM_CYSCREEN);
\r
1110 screenWidth = GetSystemMetrics(SM_CXSCREEN);
\r
1111 for (ibs = (int) NUM_SIZES - 1; ibs >= 0; ibs--) {
\r
1112 /* Compute window size for each board size, and use the largest
\r
1113 size that fits on this screen as the default. */
\r
1114 InitDrawingSizes((BoardSize)(ibs+1000), 0);
\r
1115 if (boardSize == (BoardSize)-1 &&
\r
1116 winH <= screenHeight
\r
1117 - GetSystemMetrics(SM_CYFRAME) - GetSystemMetrics(SM_CYCAPTION) - 10
\r
1118 && winW <= screenWidth) {
\r
1119 boardSize = (BoardSize)ibs;
\r
1123 InitDrawingSizes(boardSize, 0);
\r
1124 RecentEngineMenu(appData.recentEngineList);
\r
1126 buttonCount = GetSystemMetrics(SM_CMOUSEBUTTONS);
\r
1128 /* [AS] Load textures if specified */
\r
1131 mysrandom( (unsigned) time(NULL) );
\r
1133 /* [AS] Restore layout */
\r
1134 if( wpMoveHistory.visible ) {
\r
1135 MoveHistoryPopUp();
\r
1138 if( wpEvalGraph.visible ) {
\r
1142 if( wpEngineOutput.visible ) {
\r
1143 EngineOutputPopUp();
\r
1146 /* Make the window visible; update its client area; and return "success" */
\r
1147 EnsureOnScreen(&wpMain.x, &wpMain.y, minX, minY);
\r
1148 wp.length = sizeof(WINDOWPLACEMENT);
\r
1150 wp.showCmd = nCmdShow;
\r
1151 wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;
\r
1152 wp.rcNormalPosition.left = wpMain.x;
\r
1153 wp.rcNormalPosition.right = wpMain.x + wpMain.width;
\r
1154 wp.rcNormalPosition.top = wpMain.y;
\r
1155 wp.rcNormalPosition.bottom = wpMain.y + wpMain.height;
\r
1156 SetWindowPlacement(hwndMain, &wp);
\r
1158 InitBackEnd2(); // [HGM] moved until after all windows placed, to save correct position if fatal error on engine start
\r
1160 if(!appData.noGUI) SetWindowPos(hwndMain, alwaysOnTop ? HWND_TOPMOST : HWND_NOTOPMOST,
\r
1161 0, 0, 0, 0, SWP_NOMOVE|SWP_NOSIZE);
\r
1163 if (hwndConsole) {
\r
1165 SetWindowPos(hwndConsole, alwaysOnTop ? HWND_TOPMOST : HWND_NOTOPMOST,
\r
1166 0, 0, 0, 0, SWP_NOMOVE|SWP_NOSIZE);
\r
1168 ShowWindow(hwndConsole, nCmdShow);
\r
1169 SetActiveWindow(hwndConsole);
\r
1171 if(!appData.noGUI) UpdateWindow(hwnd); else ShowWindow(hwnd, SW_MINIMIZE);
\r
1172 if(gameListDialog) SetFocus(gameListDialog); // [HGM] jaws: for if we clicked multi-game game file
\r
1181 HMENU hmenu = GetMenu(hwndMain);
\r
1183 (void) EnableMenuItem(hmenu, IDM_CommPort,
\r
1184 MF_BYCOMMAND|((appData.icsActive &&
\r
1185 *appData.icsCommPort != NULLCHAR) ?
\r
1186 MF_ENABLED : MF_GRAYED));
\r
1187 (void) CheckMenuItem(hmenu, IDM_SaveSettingsOnExit,
\r
1188 MF_BYCOMMAND|(saveSettingsOnExit ?
\r
1189 MF_CHECKED : MF_UNCHECKED));
\r
1192 //---------------------------------------------------------------------------------------------------------
\r
1194 #define ICS_TEXT_MENU_SIZE (IDM_CommandXLast - IDM_CommandX + 1)
\r
1195 #define XBOARD FALSE
\r
1197 #define OPTCHAR "/"
\r
1198 #define SEPCHAR "="
\r
1199 #define TOPLEVEL 0
\r
1203 // front-end part of option handling
\r
1206 LFfromMFP(LOGFONT* lf, MyFontParams *mfp)
\r
1208 HDC hdc = CreateDC("DISPLAY", NULL, NULL, NULL);
\r
1209 lf->lfHeight = -(int)(mfp->pointSize * GetDeviceCaps(hdc, LOGPIXELSY) / 72.0 + 0.5);
\r
1212 lf->lfEscapement = 0;
\r
1213 lf->lfOrientation = 0;
\r
1214 lf->lfWeight = mfp->bold ? FW_BOLD : FW_NORMAL;
\r
1215 lf->lfItalic = mfp->italic;
\r
1216 lf->lfUnderline = mfp->underline;
\r
1217 lf->lfStrikeOut = mfp->strikeout;
\r
1218 lf->lfCharSet = mfp->charset;
\r
1219 lf->lfOutPrecision = OUT_DEFAULT_PRECIS;
\r
1220 lf->lfClipPrecision = CLIP_DEFAULT_PRECIS;
\r
1221 lf->lfQuality = DEFAULT_QUALITY;
\r
1222 lf->lfPitchAndFamily = DEFAULT_PITCH|FF_DONTCARE;
\r
1223 safeStrCpy(lf->lfFaceName, mfp->faceName, sizeof(lf->lfFaceName)/sizeof(lf->lfFaceName[0]) );
\r
1227 CreateFontInMF(MyFont *mf)
\r
1229 LFfromMFP(&mf->lf, &mf->mfp);
\r
1230 if (mf->hf) DeleteObject(mf->hf);
\r
1231 mf->hf = CreateFontIndirect(&mf->lf);
\r
1234 // [HGM] This platform-dependent table provides the location for storing the color info
\r
1236 colorVariable[] = {
\r
1237 &whitePieceColor,
\r
1238 &blackPieceColor,
\r
1239 &lightSquareColor,
\r
1240 &darkSquareColor,
\r
1241 &highlightSquareColor,
\r
1242 &premoveHighlightColor,
\r
1244 &consoleBackgroundColor,
\r
1245 &appData.fontForeColorWhite,
\r
1246 &appData.fontBackColorWhite,
\r
1247 &appData.fontForeColorBlack,
\r
1248 &appData.fontBackColorBlack,
\r
1249 &appData.evalHistColorWhite,
\r
1250 &appData.evalHistColorBlack,
\r
1251 &appData.highlightArrowColor,
\r
1254 /* Command line font name parser. NULL name means do nothing.
\r
1255 Syntax like "Courier New:10.0 bi" or "Arial:10" or "Arial:10b"
\r
1256 For backward compatibility, syntax without the colon is also
\r
1257 accepted, but font names with digits in them won't work in that case.
\r
1260 ParseFontName(char *name, MyFontParams *mfp)
\r
1263 if (name == NULL) return;
\r
1265 q = strchr(p, ':');
\r
1267 if (q - p >= sizeof(mfp->faceName))
\r
1268 ExitArgError(_("Font name too long:"), name, TRUE);
\r
1269 memcpy(mfp->faceName, p, q - p);
\r
1270 mfp->faceName[q - p] = NULLCHAR;
\r
1273 q = mfp->faceName;
\r
1274 while (*p && !isdigit(*p)) {
\r
1276 if (q - mfp->faceName >= sizeof(mfp->faceName))
\r
1277 ExitArgError(_("Font name too long:"), name, TRUE);
\r
1279 while (q > mfp->faceName && q[-1] == ' ') q--;
\r
1282 if (!*p) ExitArgError(_("Font point size missing:"), name, TRUE);
\r
1283 mfp->pointSize = (float) atof(p);
\r
1284 mfp->bold = (strchr(p, 'b') != NULL);
\r
1285 mfp->italic = (strchr(p, 'i') != NULL);
\r
1286 mfp->underline = (strchr(p, 'u') != NULL);
\r
1287 mfp->strikeout = (strchr(p, 's') != NULL);
\r
1288 mfp->charset = DEFAULT_CHARSET;
\r
1289 q = strchr(p, 'c');
\r
1291 mfp->charset = (BYTE) atoi(q+1);
\r
1295 ParseFont(char *name, int number)
\r
1296 { // wrapper to shield back-end from 'font'
\r
1297 ParseFontName(name, &font[boardSize][number]->mfp);
\r
1302 { // in WB we have a 2D array of fonts; this initializes their description
\r
1304 /* Point font array elements to structures and
\r
1305 parse default font names */
\r
1306 for (i=0; i<NUM_FONTS; i++) {
\r
1307 for (j=0; j<NUM_SIZES; j++) {
\r
1308 font[j][i] = &fontRec[j][i];
\r
1309 ParseFontName(font[j][i]->def, &font[j][i]->mfp);
\r
1316 { // here we create the actual fonts from the selected descriptions
\r
1318 for (i=0; i<NUM_FONTS; i++) {
\r
1319 for (j=0; j<NUM_SIZES; j++) {
\r
1320 CreateFontInMF(font[j][i]);
\r
1324 /* Color name parser.
\r
1325 X version accepts X color names, but this one
\r
1326 handles only the #rrggbb form (hex) or rrr,ggg,bbb (decimal) */
\r
1328 ParseColorName(char *name)
\r
1330 int red, green, blue, count;
\r
1331 char buf[MSG_SIZ];
\r
1333 count = sscanf(name, "#%2x%2x%2x", &red, &green, &blue);
\r
1335 count = sscanf(name, "%3d%*[^0-9]%3d%*[^0-9]%3d",
\r
1336 &red, &green, &blue);
\r
1339 snprintf(buf, MSG_SIZ, _("Can't parse color name %s"), name);
\r
1340 DisplayError(buf, 0);
\r
1341 return RGB(0, 0, 0);
\r
1343 return PALETTERGB(red, green, blue);
\r
1347 ParseColor(int n, char *name)
\r
1348 { // for WinBoard the color is an int, which needs to be derived from the string
\r
1349 if(colorVariable[n]) *(int*)colorVariable[n] = ParseColorName(name);
\r
1353 ParseAttribs(COLORREF *color, int *effects, char* argValue)
\r
1355 char *e = argValue;
\r
1359 if (*e == 'b') eff |= CFE_BOLD;
\r
1360 else if (*e == 'i') eff |= CFE_ITALIC;
\r
1361 else if (*e == 'u') eff |= CFE_UNDERLINE;
\r
1362 else if (*e == 's') eff |= CFE_STRIKEOUT;
\r
1363 else if (*e == '#' || isdigit(*e)) break;
\r
1367 *color = ParseColorName(e);
\r
1371 ParseTextAttribs(ColorClass cc, char *s)
\r
1372 { // [HGM] front-end wrapper that does the platform-dependent call
\r
1373 // for XBoard we would set (&appData.colorShout)[cc] = strdup(s);
\r
1374 ParseAttribs(&textAttribs[cc].color, &textAttribs[cc].effects, s);
\r
1378 ParseBoardSize(void *addr, char *name)
\r
1379 { // [HGM] rewritten with return-value ptr to shield back-end from BoardSize
\r
1380 BoardSize bs = SizeTiny;
\r
1381 while (sizeInfo[bs].name != NULL) {
\r
1382 if (StrCaseCmp(name, sizeInfo[bs].name) == 0) {
\r
1383 *(BoardSize *)addr = bs;
\r
1388 ExitArgError(_("Unrecognized board size value"), name, TRUE);
\r
1393 { // [HGM] import name from appData first
\r
1396 for (cc = (ColorClass)0; cc < ColorNormal; cc++) {
\r
1397 textAttribs[cc].sound.name = strdup((&appData.soundShout)[cc]);
\r
1398 textAttribs[cc].sound.data = NULL;
\r
1399 MyLoadSound(&textAttribs[cc].sound);
\r
1401 for (cc = ColorNormal; cc < NColorClasses; cc++) {
\r
1402 textAttribs[cc].sound.name = strdup("");
\r
1403 textAttribs[cc].sound.data = NULL;
\r
1405 for (sc = (SoundClass)0; sc < NSoundClasses; sc++) {
\r
1406 sounds[sc].name = strdup((&appData.soundMove)[sc]);
\r
1407 sounds[sc].data = NULL;
\r
1408 MyLoadSound(&sounds[sc]);
\r
1413 SetCommPortDefaults()
\r
1415 memset(&dcb, 0, sizeof(DCB)); // required by VS 2002 +
\r
1416 dcb.DCBlength = sizeof(DCB);
\r
1417 dcb.BaudRate = 9600;
\r
1418 dcb.fBinary = TRUE;
\r
1419 dcb.fParity = FALSE;
\r
1420 dcb.fOutxCtsFlow = FALSE;
\r
1421 dcb.fOutxDsrFlow = FALSE;
\r
1422 dcb.fDtrControl = DTR_CONTROL_ENABLE;
\r
1423 dcb.fDsrSensitivity = FALSE;
\r
1424 dcb.fTXContinueOnXoff = TRUE;
\r
1425 dcb.fOutX = FALSE;
\r
1427 dcb.fNull = FALSE;
\r
1428 dcb.fRtsControl = RTS_CONTROL_ENABLE;
\r
1429 dcb.fAbortOnError = FALSE;
\r
1431 dcb.Parity = SPACEPARITY;
\r
1432 dcb.StopBits = ONESTOPBIT;
\r
1435 // [HGM] args: these three cases taken out to stay in front-end
\r
1437 SaveFontArg(FILE *f, ArgDescriptor *ad)
\r
1438 { // in WinBoard every board size has its own font, and the "argLoc" identifies the table,
\r
1439 // while the curent board size determines the element. This system should be ported to XBoard.
\r
1440 // What the table contains pointers to, and how to print the font description, remains platform-dependent
\r
1442 for (bs=0; bs<NUM_SIZES; bs++) {
\r
1443 MyFontParams *mfp = &font[bs][(int) ad->argLoc]->mfp;
\r
1444 fprintf(f, "/size=%s ", sizeInfo[bs].name);
\r
1445 fprintf(f, "/%s=\"%s:%g%s%s%s%s%sc%d\"\n",
\r
1446 ad->argName, mfp->faceName, mfp->pointSize,
\r
1447 mfp->bold || mfp->italic || mfp->underline || mfp->strikeout ? " " : "",
\r
1448 mfp->bold ? "b" : "",
\r
1449 mfp->italic ? "i" : "",
\r
1450 mfp->underline ? "u" : "",
\r
1451 mfp->strikeout ? "s" : "",
\r
1452 (int)mfp->charset);
\r
1458 { // [HGM] copy the names from the internal WB variables to appData
\r
1461 for (cc = (ColorClass)0; cc < ColorNormal; cc++)
\r
1462 (&appData.soundShout)[cc] = textAttribs[cc].sound.name;
\r
1463 for (sc = (SoundClass)0; sc < NSoundClasses; sc++)
\r
1464 (&appData.soundMove)[sc] = sounds[sc].name;
\r
1468 SaveAttribsArg(FILE *f, ArgDescriptor *ad)
\r
1469 { // here the "argLoc" defines a table index. It could have contained the 'ta' pointer itself, though
\r
1470 MyTextAttribs* ta = &textAttribs[(ColorClass)ad->argLoc];
\r
1471 fprintf(f, "/%s=\"%s%s%s%s%s#%02lx%02lx%02lx\"\n", ad->argName,
\r
1472 (ta->effects & CFE_BOLD) ? "b" : "",
\r
1473 (ta->effects & CFE_ITALIC) ? "i" : "",
\r
1474 (ta->effects & CFE_UNDERLINE) ? "u" : "",
\r
1475 (ta->effects & CFE_STRIKEOUT) ? "s" : "",
\r
1476 (ta->effects) ? " " : "",
\r
1477 ta->color&0xff, (ta->color >> 8)&0xff, (ta->color >> 16)&0xff);
\r
1481 SaveColor(FILE *f, ArgDescriptor *ad)
\r
1482 { // in WinBoard the color is an int and has to be converted to text. In X it would be a string already?
\r
1483 COLORREF color = *(COLORREF *)colorVariable[(int)ad->argLoc];
\r
1484 fprintf(f, "/%s=#%02lx%02lx%02lx\n", ad->argName,
\r
1485 color&0xff, (color>>8)&0xff, (color>>16)&0xff);
\r
1489 SaveBoardSize(FILE *f, char *name, void *addr)
\r
1490 { // wrapper to shield back-end from BoardSize & sizeInfo
\r
1491 fprintf(f, "/%s=%s\n", name, sizeInfo[*(BoardSize *)addr].name);
\r
1495 ParseCommPortSettings(char *s)
\r
1496 { // wrapper to keep dcb from back-end
\r
1497 ParseCommSettings(s, &dcb);
\r
1502 { // wrapper to shield use of window handles from back-end (make addressible by number?)
\r
1503 GetActualPlacement(hwndMain, &wpMain);
\r
1504 GetActualPlacement(hwndConsole, &wpConsole);
\r
1505 GetActualPlacement(commentDialog, &wpComment);
\r
1506 GetActualPlacement(editTagsDialog, &wpTags);
\r
1507 GetActualPlacement(gameListDialog, &wpGameList);
\r
1508 GetActualPlacement(moveHistoryDialog, &wpMoveHistory);
\r
1509 GetActualPlacement(evalGraphDialog, &wpEvalGraph);
\r
1510 GetActualPlacement(engineOutputDialog, &wpEngineOutput);
\r
1514 PrintCommPortSettings(FILE *f, char *name)
\r
1515 { // wrapper to shield back-end from DCB
\r
1516 PrintCommSettings(f, name, &dcb);
\r
1520 MySearchPath(char *installDir, char *name, char *fullname)
\r
1522 char *dummy, buf[MSG_SIZ], *p = name, *q;
\r
1523 if(name[0]== '%') {
\r
1524 fullname[0] = 0; // [HGM] first expand any environment variables in the given name
\r
1525 while(*p == '%' && (q = strchr(p+1, '%'))) { // [HGM] recognize %*% as environment variable
\r
1526 safeStrCpy(buf, p+1, sizeof(buf)/sizeof(buf[0]) );
\r
1527 *strchr(buf, '%') = 0;
\r
1528 strcat(fullname, getenv(buf));
\r
1529 p = q+1; while(*p == '\\') { strcat(fullname, "\\"); p++; }
\r
1531 strcat(fullname, p); // after environment variables (if any), take the remainder of the given name
\r
1532 if(appData.debugMode) fprintf(debugFP, "name = '%s', expanded name = '%s'\n", name, fullname);
\r
1533 return (int) strlen(fullname);
\r
1535 return (int) SearchPath(installDir, name, NULL, MSG_SIZ, fullname, &dummy);
\r
1539 MyGetFullPathName(char *name, char *fullname)
\r
1542 return (int) GetFullPathName(name, MSG_SIZ, fullname, &dummy);
\r
1547 { // [HGM] args: allows testing if main window is realized from back-end
\r
1548 return hwndMain != NULL;
\r
1552 PopUpStartupDialog()
\r
1556 LoadLanguageFile(appData.language);
\r
1557 lpProc = MakeProcInstance((FARPROC)StartupDialog, hInst);
\r
1558 DialogBox(hInst, MAKEINTRESOURCE(DLG_Startup), NULL, (DLGPROC)lpProc);
\r
1559 FreeProcInstance(lpProc);
\r
1562 /*---------------------------------------------------------------------------*\
\r
1564 * GDI board drawing routines
\r
1566 \*---------------------------------------------------------------------------*/
\r
1568 /* [AS] Draw square using background texture */
\r
1569 static void DrawTile( int dx, int dy, int dw, int dh, HDC dst, HDC src, int mode, int sx, int sy )
\r
1574 return; /* Should never happen! */
\r
1577 SetGraphicsMode( dst, GM_ADVANCED );
\r
1584 /* X reflection */
\r
1589 x.eDx = (FLOAT) dw + dx - 1;
\r
1592 SetWorldTransform( dst, &x );
\r
1595 /* Y reflection */
\r
1601 x.eDy = (FLOAT) dh + dy - 1;
\r
1603 SetWorldTransform( dst, &x );
\r
1611 x.eDx = (FLOAT) dx;
\r
1612 x.eDy = (FLOAT) dy;
\r
1615 SetWorldTransform( dst, &x );
\r
1619 BitBlt( dst, dx, dy, dw, dh, src, sx, sy, SRCCOPY );
\r
1627 SetWorldTransform( dst, &x );
\r
1629 ModifyWorldTransform( dst, 0, MWT_IDENTITY );
\r
1632 /* [AS] [HGM] Make room for more piece types, so all pieces can be different */
\r
1634 PM_WP = (int) WhitePawn,
\r
1635 PM_WN = (int) WhiteKnight,
\r
1636 PM_WB = (int) WhiteBishop,
\r
1637 PM_WR = (int) WhiteRook,
\r
1638 PM_WQ = (int) WhiteQueen,
\r
1639 PM_WF = (int) WhiteFerz,
\r
1640 PM_WW = (int) WhiteWazir,
\r
1641 PM_WE = (int) WhiteAlfil,
\r
1642 PM_WM = (int) WhiteMan,
\r
1643 PM_WO = (int) WhiteCannon,
\r
1644 PM_WU = (int) WhiteUnicorn,
\r
1645 PM_WH = (int) WhiteNightrider,
\r
1646 PM_WA = (int) WhiteAngel,
\r
1647 PM_WC = (int) WhiteMarshall,
\r
1648 PM_WAB = (int) WhiteCardinal,
\r
1649 PM_WD = (int) WhiteDragon,
\r
1650 PM_WL = (int) WhiteLance,
\r
1651 PM_WS = (int) WhiteCobra,
\r
1652 PM_WV = (int) WhiteFalcon,
\r
1653 PM_WSG = (int) WhiteSilver,
\r
1654 PM_WG = (int) WhiteGrasshopper,
\r
1655 PM_WK = (int) WhiteKing,
\r
1656 PM_BP = (int) BlackPawn,
\r
1657 PM_BN = (int) BlackKnight,
\r
1658 PM_BB = (int) BlackBishop,
\r
1659 PM_BR = (int) BlackRook,
\r
1660 PM_BQ = (int) BlackQueen,
\r
1661 PM_BF = (int) BlackFerz,
\r
1662 PM_BW = (int) BlackWazir,
\r
1663 PM_BE = (int) BlackAlfil,
\r
1664 PM_BM = (int) BlackMan,
\r
1665 PM_BO = (int) BlackCannon,
\r
1666 PM_BU = (int) BlackUnicorn,
\r
1667 PM_BH = (int) BlackNightrider,
\r
1668 PM_BA = (int) BlackAngel,
\r
1669 PM_BC = (int) BlackMarshall,
\r
1670 PM_BG = (int) BlackGrasshopper,
\r
1671 PM_BAB = (int) BlackCardinal,
\r
1672 PM_BD = (int) BlackDragon,
\r
1673 PM_BL = (int) BlackLance,
\r
1674 PM_BS = (int) BlackCobra,
\r
1675 PM_BV = (int) BlackFalcon,
\r
1676 PM_BSG = (int) BlackSilver,
\r
1677 PM_BK = (int) BlackKing
\r
1680 static HFONT hPieceFont = NULL;
\r
1681 static HBITMAP hPieceMask[(int) EmptySquare];
\r
1682 static HBITMAP hPieceFace[(int) EmptySquare];
\r
1683 static int fontBitmapSquareSize = 0;
\r
1684 static char pieceToFontChar[(int) EmptySquare] =
\r
1685 { 'p', 'n', 'b', 'r', 'q',
\r
1686 'n', 'b', 'p', 'n', 'b', 'r', 'b', 'r', 'q', 'k',
\r
1687 'k', 'o', 'm', 'v', 't', 'w',
\r
1688 'v', 't', 'o', 'm', 'v', 't', 'v', 't', 'w', 'l',
\r
1691 extern BOOL SetCharTable( char *table, const char * map );
\r
1692 /* [HGM] moved to backend.c */
\r
1694 static void SetPieceBackground( HDC hdc, COLORREF color, int mode )
\r
1697 BYTE r1 = GetRValue( color );
\r
1698 BYTE g1 = GetGValue( color );
\r
1699 BYTE b1 = GetBValue( color );
\r
1705 /* Create a uniform background first */
\r
1706 hbrush = CreateSolidBrush( color );
\r
1707 SetRect( &rc, 0, 0, squareSize, squareSize );
\r
1708 FillRect( hdc, &rc, hbrush );
\r
1709 DeleteObject( hbrush );
\r
1712 /* Vertical gradient, good for pawn, knight and rook, less for queen and king */
\r
1713 int steps = squareSize / 2;
\r
1716 for( i=0; i<steps; i++ ) {
\r
1717 BYTE r = r1 - (r1-r2) * i / steps;
\r
1718 BYTE g = g1 - (g1-g2) * i / steps;
\r
1719 BYTE b = b1 - (b1-b2) * i / steps;
\r
1721 hbrush = CreateSolidBrush( RGB(r,g,b) );
\r
1722 SetRect( &rc, i + squareSize - steps, 0, i + squareSize - steps + 1, squareSize );
\r
1723 FillRect( hdc, &rc, hbrush );
\r
1724 DeleteObject(hbrush);
\r
1727 else if( mode == 2 ) {
\r
1728 /* Diagonal gradient, good more or less for every piece */
\r
1729 POINT triangle[3];
\r
1730 HPEN hpen = SelectObject( hdc, GetStockObject(NULL_PEN) );
\r
1731 HBRUSH hbrush_old;
\r
1732 int steps = squareSize;
\r
1735 triangle[0].x = squareSize - steps;
\r
1736 triangle[0].y = squareSize;
\r
1737 triangle[1].x = squareSize;
\r
1738 triangle[1].y = squareSize;
\r
1739 triangle[2].x = squareSize;
\r
1740 triangle[2].y = squareSize - steps;
\r
1742 for( i=0; i<steps; i++ ) {
\r
1743 BYTE r = r1 - (r1-r2) * i / steps;
\r
1744 BYTE g = g1 - (g1-g2) * i / steps;
\r
1745 BYTE b = b1 - (b1-b2) * i / steps;
\r
1747 hbrush = CreateSolidBrush( RGB(r,g,b) );
\r
1748 hbrush_old = SelectObject( hdc, hbrush );
\r
1749 Polygon( hdc, triangle, 3 );
\r
1750 SelectObject( hdc, hbrush_old );
\r
1751 DeleteObject(hbrush);
\r
1756 SelectObject( hdc, hpen );
\r
1761 [AS] The method I use to create the bitmaps it a bit tricky, but it
\r
1762 seems to work ok. The main problem here is to find the "inside" of a chess
\r
1763 piece: follow the steps as explained below.
\r
1765 static void CreatePieceMaskFromFont( HDC hdc_window, HDC hdc, int index )
\r
1769 COLORREF chroma = RGB(0xFF,0x00,0xFF);
\r
1773 int backColor = whitePieceColor;
\r
1774 int foreColor = blackPieceColor;
\r
1776 if( index < (int)BlackPawn && appData.fontBackColorWhite != appData.fontForeColorWhite ) {
\r
1777 backColor = appData.fontBackColorWhite;
\r
1778 foreColor = appData.fontForeColorWhite;
\r
1780 else if( index >= (int)BlackPawn && appData.fontBackColorBlack != appData.fontForeColorBlack ) {
\r
1781 backColor = appData.fontBackColorBlack;
\r
1782 foreColor = appData.fontForeColorBlack;
\r
1786 hbm = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );
\r
1788 hbm_old = SelectObject( hdc, hbm );
\r
1792 rc.right = squareSize;
\r
1793 rc.bottom = squareSize;
\r
1795 /* Step 1: background is now black */
\r
1796 FillRect( hdc, &rc, GetStockObject(BLACK_BRUSH) );
\r
1798 GetTextExtentPoint32( hdc, &pieceToFontChar[index], 1, &sz );
\r
1800 pt.x = (squareSize - sz.cx) / 2;
\r
1801 pt.y = (squareSize - sz.cy) / 2;
\r
1803 SetBkMode( hdc, TRANSPARENT );
\r
1804 SetTextColor( hdc, chroma );
\r
1805 /* Step 2: the piece has been drawn in purple, there are now black and purple in this bitmap */
\r
1806 TextOut( hdc, pt.x, pt.y, &pieceToFontChar[appData.allWhite && index >= (int)BlackPawn ? index - (int)BlackPawn : index], 1 );
\r
1808 SelectObject( hdc, GetStockObject(WHITE_BRUSH) );
\r
1809 /* Step 3: the area outside the piece is filled with white */
\r
1810 // FloodFill( hdc, 0, 0, chroma );
\r
1811 ExtFloodFill( hdc, 0, 0, 0, FLOODFILLSURFACE );
\r
1812 ExtFloodFill( hdc, 0, squareSize-1, 0, FLOODFILLSURFACE ); // [HGM] fill from all 4 corners, for if piece too big
\r
1813 ExtFloodFill( hdc, squareSize-1, 0, 0, FLOODFILLSURFACE );
\r
1814 ExtFloodFill( hdc, squareSize-1, squareSize-1, 0, FLOODFILLSURFACE );
\r
1815 SelectObject( hdc, GetStockObject(BLACK_BRUSH) );
\r
1817 Step 4: this is the tricky part, the area inside the piece is filled with black,
\r
1818 but if the start point is not inside the piece we're lost!
\r
1819 There should be a better way to do this... if we could create a region or path
\r
1820 from the fill operation we would be fine for example.
\r
1822 // FloodFill( hdc, squareSize / 2, squareSize / 2, RGB(0xFF,0xFF,0xFF) );
\r
1823 ExtFloodFill( hdc, squareSize / 2, squareSize / 2, RGB(0xFF,0xFF,0xFF), FLOODFILLBORDER );
\r
1825 { /* [HGM] shave off edges of mask, in an attempt to correct for the fact that FloodFill does not work correctly under Win XP */
\r
1826 HDC dc2 = CreateCompatibleDC( hdc_window );
\r
1827 HBITMAP bm2 = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );
\r
1829 SelectObject( dc2, bm2 );
\r
1830 BitBlt( dc2, 0, 0, squareSize, squareSize, hdc, 0, 0, SRCCOPY ); // make copy
\r
1831 BitBlt( hdc, 0, 1, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );
\r
1832 BitBlt( hdc, 2, 1, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );
\r
1833 BitBlt( hdc, 1, 0, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );
\r
1834 BitBlt( hdc, 1, 2, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );
\r
1837 DeleteObject( bm2 );
\r
1840 SetTextColor( hdc, 0 );
\r
1842 Step 5: some fonts have "disconnected" areas that are skipped by the fill:
\r
1843 draw the piece again in black for safety.
\r
1845 TextOut( hdc, pt.x, pt.y, &pieceToFontChar[appData.allWhite && index >= (int)BlackPawn ? index - (int)BlackPawn : index], 1 );
\r
1847 SelectObject( hdc, hbm_old );
\r
1849 if( hPieceMask[index] != NULL ) {
\r
1850 DeleteObject( hPieceMask[index] );
\r
1853 hPieceMask[index] = hbm;
\r
1856 hbm = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );
\r
1858 SelectObject( hdc, hbm );
\r
1861 HDC dc1 = CreateCompatibleDC( hdc_window );
\r
1862 HDC dc2 = CreateCompatibleDC( hdc_window );
\r
1863 HBITMAP bm2 = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );
\r
1865 SelectObject( dc1, hPieceMask[index] );
\r
1866 SelectObject( dc2, bm2 );
\r
1867 FillRect( dc2, &rc, GetStockObject(WHITE_BRUSH) );
\r
1868 BitBlt( dc2, 0, 0, squareSize, squareSize, dc1, 0, 0, SRCINVERT );
\r
1871 Now dc2 contains the inverse of the piece mask, i.e. a mask that preserves
\r
1872 the piece background and deletes (makes transparent) the rest.
\r
1873 Thanks to that mask, we are free to paint the background with the greates
\r
1874 freedom, as we'll be able to mask off the unwanted parts when finished.
\r
1875 We use this, to make gradients and give the pieces a "roundish" look.
\r
1877 SetPieceBackground( hdc, backColor, 2 );
\r
1878 BitBlt( hdc, 0, 0, squareSize, squareSize, dc2, 0, 0, SRCAND );
\r
1882 DeleteObject( bm2 );
\r
1885 SetTextColor( hdc, foreColor );
\r
1886 TextOut( hdc, pt.x, pt.y, &pieceToFontChar[appData.allWhite && index >= (int)BlackPawn ? index - (int)BlackPawn : index], 1 );
\r
1888 SelectObject( hdc, hbm_old );
\r
1890 if( hPieceFace[index] != NULL ) {
\r
1891 DeleteObject( hPieceFace[index] );
\r
1894 hPieceFace[index] = hbm;
\r
1897 static int TranslatePieceToFontPiece( int piece )
\r
1927 case BlackMarshall:
\r
1931 case BlackNightrider:
\r
1937 case BlackUnicorn:
\r
1941 case BlackGrasshopper:
\r
1953 case BlackCardinal:
\r
1960 case WhiteMarshall:
\r
1964 case WhiteNightrider:
\r
1970 case WhiteUnicorn:
\r
1974 case WhiteGrasshopper:
\r
1986 case WhiteCardinal:
\r
1995 void CreatePiecesFromFont()
\r
1998 HDC hdc_window = NULL;
\r
2004 if( fontBitmapSquareSize < 0 ) {
\r
2005 /* Something went seriously wrong in the past: do not try to recreate fonts! */
\r
2009 if( !appData.useFont || appData.renderPiecesWithFont == NULL ||
\r
2010 appData.renderPiecesWithFont[0] == NULLCHAR || appData.renderPiecesWithFont[0] == '*' ) {
\r
2011 fontBitmapSquareSize = -1;
\r
2015 if( fontBitmapSquareSize != squareSize ) {
\r
2016 hdc_window = GetDC( hwndMain );
\r
2017 hdc = CreateCompatibleDC( hdc_window );
\r
2019 if( hPieceFont != NULL ) {
\r
2020 DeleteObject( hPieceFont );
\r
2023 for( i=0; i<=(int)BlackKing; i++ ) {
\r
2024 hPieceMask[i] = NULL;
\r
2025 hPieceFace[i] = NULL;
\r
2031 if( appData.fontPieceSize >= 50 && appData.fontPieceSize <= 150 ) {
\r
2032 fontHeight = appData.fontPieceSize;
\r
2035 fontHeight = (fontHeight * squareSize) / 100;
\r
2037 lf.lfHeight = -MulDiv( fontHeight, GetDeviceCaps(hdc, LOGPIXELSY), 72 );
\r
2039 lf.lfEscapement = 0;
\r
2040 lf.lfOrientation = 0;
\r
2041 lf.lfWeight = FW_NORMAL;
\r
2043 lf.lfUnderline = 0;
\r
2044 lf.lfStrikeOut = 0;
\r
2045 lf.lfCharSet = DEFAULT_CHARSET;
\r
2046 lf.lfOutPrecision = OUT_DEFAULT_PRECIS;
\r
2047 lf.lfClipPrecision = CLIP_DEFAULT_PRECIS;
\r
2048 lf.lfQuality = PROOF_QUALITY;
\r
2049 lf.lfPitchAndFamily = DEFAULT_PITCH | FF_DONTCARE;
\r
2050 strncpy( lf.lfFaceName, appData.renderPiecesWithFont, sizeof(lf.lfFaceName) );
\r
2051 lf.lfFaceName[ sizeof(lf.lfFaceName) - 1 ] = '\0';
\r
2053 hPieceFont = CreateFontIndirect( &lf );
\r
2055 if( hPieceFont == NULL ) {
\r
2056 fontBitmapSquareSize = -2;
\r
2059 /* Setup font-to-piece character table */
\r
2060 if( ! SetCharTable(pieceToFontChar, appData.fontToPieceTable) ) {
\r
2061 /* No (or wrong) global settings, try to detect the font */
\r
2062 if( strstr(lf.lfFaceName,"Alpha") != NULL ) {
\r
2064 SetCharTable(pieceToFontChar, "phbrqkojntwl");
\r
2066 else if( strstr(lf.lfFaceName,"DiagramTT") != NULL ) {
\r
2067 /* DiagramTT* family */
\r
2068 SetCharTable(pieceToFontChar, "PNLRQKpnlrqk");
\r
2070 else if( strstr(lf.lfFaceName,"WinboardF") != NULL ) {
\r
2071 /* Fairy symbols */
\r
2072 SetCharTable(pieceToFontChar, "PNBRQFEACWMOHIJGDVSLUKpnbrqfeacwmohijgdvsluk");
\r
2074 else if( strstr(lf.lfFaceName,"GC2004D") != NULL ) {
\r
2075 /* Good Companion (Some characters get warped as literal :-( */
\r
2076 char s[] = "1cmWG0??S??oYI23wgQU";
\r
2077 s[0]=0xB9; s[1]=0xA9; s[6]=0xB1; s[11]=0xBB; s[12]=0xAB; s[17]=0xB3;
\r
2078 SetCharTable(pieceToFontChar, s);
\r
2081 /* Cases, Condal, Leipzig, Lucena, Marroquin, Merida, Usual */
\r
2082 SetCharTable(pieceToFontChar, "pnbrqkomvtwl");
\r
2086 /* Create bitmaps */
\r
2087 hfont_old = SelectObject( hdc, hPieceFont );
\r
2088 for(i=(int)WhitePawn; i<(int)EmptySquare; i++) /* [HGM] made a loop for this */
\r
2089 if(PieceToChar((ChessSquare)i) != '.') /* skip unused pieces */
\r
2090 CreatePieceMaskFromFont( hdc_window, hdc, i );
\r
2092 SelectObject( hdc, hfont_old );
\r
2094 fontBitmapSquareSize = squareSize;
\r
2098 if( hdc != NULL ) {
\r
2102 if( hdc_window != NULL ) {
\r
2103 ReleaseDC( hwndMain, hdc_window );
\r
2108 DoLoadBitmap(HINSTANCE hinst, char *piece, int squareSize, char *suffix)
\r
2112 snprintf(name, sizeof(name)/sizeof(name[0]), "%s%d%s", piece, squareSize, suffix);
\r
2113 if (gameInfo.event &&
\r
2114 strcmp(gameInfo.event, "Easter Egg Hunt") == 0 &&
\r
2115 strcmp(name, "k80s") == 0) {
\r
2116 safeStrCpy(name, "tim", sizeof(name)/sizeof(name[0]) );
\r
2118 return LoadBitmap(hinst, name);
\r
2122 /* Insert a color into the program's logical palette
\r
2123 structure. This code assumes the given color is
\r
2124 the result of the RGB or PALETTERGB macro, and it
\r
2125 knows how those macros work (which is documented).
\r
2128 InsertInPalette(COLORREF color)
\r
2130 LPPALETTEENTRY pe = &(pLogPal->palPalEntry[pLogPal->palNumEntries]);
\r
2132 if (pLogPal->palNumEntries++ >= PALETTESIZE) {
\r
2133 DisplayFatalError(_("Too many colors"), 0, 1);
\r
2134 pLogPal->palNumEntries--;
\r
2138 pe->peFlags = (char) 0;
\r
2139 pe->peRed = (char) (0xFF & color);
\r
2140 pe->peGreen = (char) (0xFF & (color >> 8));
\r
2141 pe->peBlue = (char) (0xFF & (color >> 16));
\r
2147 InitDrawingColors()
\r
2149 if (pLogPal == NULL) {
\r
2150 /* Allocate enough memory for a logical palette with
\r
2151 * PALETTESIZE entries and set the size and version fields
\r
2152 * of the logical palette structure.
\r
2154 pLogPal = (NPLOGPALETTE)
\r
2155 LocalAlloc(LMEM_FIXED, (sizeof(LOGPALETTE) +
\r
2156 (sizeof(PALETTEENTRY) * (PALETTESIZE))));
\r
2157 pLogPal->palVersion = 0x300;
\r
2159 pLogPal->palNumEntries = 0;
\r
2161 InsertInPalette(lightSquareColor);
\r
2162 InsertInPalette(darkSquareColor);
\r
2163 InsertInPalette(whitePieceColor);
\r
2164 InsertInPalette(blackPieceColor);
\r
2165 InsertInPalette(highlightSquareColor);
\r
2166 InsertInPalette(premoveHighlightColor);
\r
2168 /* create a logical color palette according the information
\r
2169 * in the LOGPALETTE structure.
\r
2171 hPal = CreatePalette((LPLOGPALETTE) pLogPal);
\r
2173 lightSquareBrush = CreateSolidBrush(lightSquareColor);
\r
2174 blackSquareBrush = CreateSolidBrush(blackPieceColor);
\r
2175 darkSquareBrush = CreateSolidBrush(darkSquareColor);
\r
2176 whitePieceBrush = CreateSolidBrush(whitePieceColor);
\r
2177 blackPieceBrush = CreateSolidBrush(blackPieceColor);
\r
2178 iconBkgndBrush = CreateSolidBrush(GetSysColor(COLOR_BACKGROUND));
\r
2179 explodeBrush = CreateSolidBrush(highlightSquareColor); // [HGM] atomic
\r
2180 markerBrush = CreateSolidBrush(premoveHighlightColor); // [HGM] markers
\r
2181 /* [AS] Force rendering of the font-based pieces */
\r
2182 if( fontBitmapSquareSize > 0 ) {
\r
2183 fontBitmapSquareSize = 0;
\r
2189 BoardWidth(int boardSize, int n)
\r
2190 { /* [HGM] argument n added to allow different width and height */
\r
2191 int lineGap = sizeInfo[boardSize].lineGap;
\r
2193 if( appData.overrideLineGap >= 0 && appData.overrideLineGap <= 5 ) {
\r
2194 lineGap = appData.overrideLineGap;
\r
2197 return (n + 1) * lineGap +
\r
2198 n * sizeInfo[boardSize].squareSize;
\r
2201 /* Respond to board resize by dragging edge */
\r
2203 ResizeBoard(int newSizeX, int newSizeY, int flags)
\r
2205 BoardSize newSize = NUM_SIZES - 1;
\r
2206 static int recurse = 0;
\r
2207 if (IsIconic(hwndMain)) return;
\r
2208 if (recurse > 0) return;
\r
2210 while (newSize > 0) {
\r
2211 InitDrawingSizes(newSize+1000, 0); // [HGM] kludge to update sizeInfo without visible effects
\r
2212 if(newSizeX >= sizeInfo[newSize].cliWidth &&
\r
2213 newSizeY >= sizeInfo[newSize].cliHeight) break;
\r
2216 boardSize = newSize;
\r
2217 InitDrawingSizes(boardSize, flags);
\r
2222 extern Boolean twoBoards, partnerUp; // [HGM] dual
\r
2225 InitDrawingSizes(BoardSize boardSize, int flags)
\r
2227 int i, boardWidth, boardHeight; /* [HGM] height treated separately */
\r
2228 ChessSquare piece;
\r
2229 static int oldBoardSize = -1, oldTinyLayout = 0;
\r
2231 SIZE clockSize, messageSize;
\r
2233 char buf[MSG_SIZ];
\r
2235 HMENU hmenu = GetMenu(hwndMain);
\r
2236 RECT crect, wrect, oldRect;
\r
2238 LOGBRUSH logbrush;
\r
2239 VariantClass v = gameInfo.variant;
\r
2241 int suppressVisibleEffects = 0; // [HGM] kludge to request updating sizeInfo only
\r
2242 if((int)boardSize >= 1000 ) { boardSize -= 1000; suppressVisibleEffects = 1; }
\r
2244 /* [HGM] call with -2 uses old size (for if nr of files, ranks changes) */
\r
2245 if(boardSize == (BoardSize)(-2) ) boardSize = oldBoardSize;
\r
2246 oldBoardSize = boardSize;
\r
2248 if(boardSize != SizeMiddling && boardSize != SizePetite && boardSize != SizeBulky && !appData.useFont)
\r
2249 { // correct board size to one where built-in pieces exist
\r
2250 if((v == VariantCapablanca || v == VariantGothic || v == VariantGrand || v == VariantCapaRandom || v == VariantJanus || v == VariantSuper)
\r
2251 && (boardSize < SizePetite || boardSize > SizeBulky) // Archbishop and Chancellor available in entire middle range
\r
2252 || (v == VariantShogi && boardSize != SizeModerate) // Japanese-style Shogi
\r
2253 || v == VariantKnightmate || v == VariantSChess || v == VariantXiangqi || v == VariantSpartan
\r
2254 || v == VariantShatranj || v == VariantMakruk || v == VariantGreat || v == VariantFairy ) {
\r
2255 if(boardSize < SizeMediocre) boardSize = SizePetite; else
\r
2256 if(boardSize > SizeModerate) boardSize = SizeBulky; else
\r
2257 boardSize = SizeMiddling;
\r
2260 if(!appData.useFont && boardSize == SizePetite && (v == VariantShogi || v == VariantKnightmate)) boardSize = SizeMiddling; // no Unicorn in Petite
\r
2262 oldRect.left = wpMain.x; //[HGM] placement: remember previous window params
\r
2263 oldRect.top = wpMain.y;
\r
2264 oldRect.right = wpMain.x + wpMain.width;
\r
2265 oldRect.bottom = wpMain.y + wpMain.height;
\r
2267 tinyLayout = sizeInfo[boardSize].tinyLayout;
\r
2268 smallLayout = sizeInfo[boardSize].smallLayout;
\r
2269 squareSize = sizeInfo[boardSize].squareSize;
\r
2270 lineGap = sizeInfo[boardSize].lineGap;
\r
2271 minorSize = 0; /* [HGM] Kludge to see if demagnified pieces need to be shifted */
\r
2273 if( appData.overrideLineGap >= 0 && appData.overrideLineGap <= 5 ) {
\r
2274 lineGap = appData.overrideLineGap;
\r
2277 if (tinyLayout != oldTinyLayout) {
\r
2278 long style = GetWindowLongPtr(hwndMain, GWL_STYLE);
\r
2280 style &= ~WS_SYSMENU;
\r
2281 InsertMenu(hmenu, IDM_Exit, MF_BYCOMMAND, IDM_Minimize,
\r
2282 "&Minimize\tCtrl+F4");
\r
2284 style |= WS_SYSMENU;
\r
2285 RemoveMenu(hmenu, IDM_Minimize, MF_BYCOMMAND);
\r
2287 SetWindowLongPtr(hwndMain, GWL_STYLE, style);
\r
2289 for (i=0; menuBarText[tinyLayout][i]; i++) {
\r
2290 ModifyMenu(hmenu, i, MF_STRING|MF_BYPOSITION|MF_POPUP,
\r
2291 (UINT)GetSubMenu(hmenu, i), T_(menuBarText[tinyLayout][i]));
\r
2293 DrawMenuBar(hwndMain);
\r
2296 boardWidth = BoardWidth(boardSize, BOARD_WIDTH);
\r
2297 boardHeight = BoardWidth(boardSize, BOARD_HEIGHT);
\r
2299 /* Get text area sizes */
\r
2300 hdc = GetDC(hwndMain);
\r
2301 if (appData.clockMode) {
\r
2302 snprintf(buf, MSG_SIZ, _("White: %s"), TimeString(23*60*60*1000L));
\r
2304 snprintf(buf, MSG_SIZ, _("White"));
\r
2306 oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);
\r
2307 GetTextExtentPoint(hdc, buf, strlen(buf), &clockSize);
\r
2308 SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);
\r
2309 str = _("We only care about the height here");
\r
2310 GetTextExtentPoint(hdc, str, strlen(str), &messageSize);
\r
2311 SelectObject(hdc, oldFont);
\r
2312 ReleaseDC(hwndMain, hdc);
\r
2314 /* Compute where everything goes */
\r
2315 if((first.programLogo || second.programLogo) && !tinyLayout) {
\r
2316 /* [HGM] logo: if either logo is on, reserve space for it */
\r
2317 logoHeight = 2*clockSize.cy;
\r
2318 leftLogoRect.left = OUTER_MARGIN;
\r
2319 leftLogoRect.right = leftLogoRect.left + 4*clockSize.cy;
\r
2320 leftLogoRect.top = OUTER_MARGIN;
\r
2321 leftLogoRect.bottom = OUTER_MARGIN + logoHeight;
\r
2323 rightLogoRect.right = OUTER_MARGIN + boardWidth;
\r
2324 rightLogoRect.left = rightLogoRect.right - 4*clockSize.cy;
\r
2325 rightLogoRect.top = OUTER_MARGIN;
\r
2326 rightLogoRect.bottom = OUTER_MARGIN + logoHeight;
\r
2329 whiteRect.left = leftLogoRect.right;
\r
2330 whiteRect.right = OUTER_MARGIN + boardWidth/2 - INNER_MARGIN/2;
\r
2331 whiteRect.top = OUTER_MARGIN;
\r
2332 whiteRect.bottom = whiteRect.top + logoHeight;
\r
2334 blackRect.right = rightLogoRect.left;
\r
2335 blackRect.left = whiteRect.right + INNER_MARGIN;
\r
2336 blackRect.top = whiteRect.top;
\r
2337 blackRect.bottom = whiteRect.bottom;
\r
2339 whiteRect.left = OUTER_MARGIN;
\r
2340 whiteRect.right = whiteRect.left + boardWidth/2 - INNER_MARGIN/2;
\r
2341 whiteRect.top = OUTER_MARGIN;
\r
2342 whiteRect.bottom = whiteRect.top + clockSize.cy;
\r
2344 blackRect.left = whiteRect.right + INNER_MARGIN;
\r
2345 blackRect.right = blackRect.left + boardWidth/2 - 1;
\r
2346 blackRect.top = whiteRect.top;
\r
2347 blackRect.bottom = whiteRect.bottom;
\r
2349 logoHeight = 0; // [HGM] logo: suppress logo after change to tiny layout!
\r
2352 messageRect.left = OUTER_MARGIN + MESSAGE_LINE_LEFTMARGIN;
\r
2353 if (appData.showButtonBar) {
\r
2354 messageRect.right = OUTER_MARGIN + boardWidth // [HGM] logo: expressed independent of clock placement
\r
2355 - N_BUTTONS*BUTTON_WIDTH - MESSAGE_LINE_LEFTMARGIN;
\r
2357 messageRect.right = OUTER_MARGIN + boardWidth;
\r
2359 messageRect.top = whiteRect.bottom + INNER_MARGIN;
\r
2360 messageRect.bottom = messageRect.top + messageSize.cy;
\r
2362 boardRect.left = OUTER_MARGIN;
\r
2363 boardRect.right = boardRect.left + boardWidth;
\r
2364 boardRect.top = messageRect.bottom + INNER_MARGIN;
\r
2365 boardRect.bottom = boardRect.top + boardHeight;
\r
2367 sizeInfo[boardSize].cliWidth = boardRect.right + OUTER_MARGIN;
\r
2368 sizeInfo[boardSize].cliHeight = boardRect.bottom + OUTER_MARGIN;
\r
2369 oldTinyLayout = tinyLayout;
\r
2370 winW = 2 * GetSystemMetrics(SM_CXFRAME) + boardRect.right + OUTER_MARGIN;
\r
2371 winH = 2 * GetSystemMetrics(SM_CYFRAME) + GetSystemMetrics(SM_CYMENU) +
\r
2372 GetSystemMetrics(SM_CYCAPTION) + boardRect.bottom + OUTER_MARGIN;
\r
2373 winW *= 1 + twoBoards;
\r
2374 if(suppressVisibleEffects) return; // [HGM] when called for filling sizeInfo only
\r
2375 wpMain.width = winW; // [HGM] placement: set through temporary which can used by initial sizing choice
\r
2376 wpMain.height = winH; // without disturbing window attachments
\r
2377 GetWindowRect(hwndMain, &wrect);
\r
2378 SetWindowPos(hwndMain, NULL, 0, 0, wpMain.width, wpMain.height,
\r
2379 SWP_NOCOPYBITS|SWP_NOZORDER|SWP_NOMOVE);
\r
2381 // [HGM] placement: let attached windows follow size change.
\r
2382 ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, moveHistoryDialog, &wpMoveHistory );
\r
2383 ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, evalGraphDialog, &wpEvalGraph );
\r
2384 ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, engineOutputDialog, &wpEngineOutput );
\r
2385 ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, gameListDialog, &wpGameList );
\r
2386 ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, hwndConsole, &wpConsole );
\r
2388 /* compensate if menu bar wrapped */
\r
2389 GetClientRect(hwndMain, &crect);
\r
2390 offby = boardRect.bottom + OUTER_MARGIN - crect.bottom;
\r
2391 wpMain.height += offby;
\r
2393 case WMSZ_TOPLEFT:
\r
2394 SetWindowPos(hwndMain, NULL,
\r
2395 wrect.right - wpMain.width, wrect.bottom - wpMain.height,
\r
2396 wpMain.width, wpMain.height, SWP_NOCOPYBITS|SWP_NOZORDER);
\r
2399 case WMSZ_TOPRIGHT:
\r
2401 SetWindowPos(hwndMain, NULL,
\r
2402 wrect.left, wrect.bottom - wpMain.height,
\r
2403 wpMain.width, wpMain.height, SWP_NOCOPYBITS|SWP_NOZORDER);
\r
2406 case WMSZ_BOTTOMLEFT:
\r
2408 SetWindowPos(hwndMain, NULL,
\r
2409 wrect.right - wpMain.width, wrect.top,
\r
2410 wpMain.width, wpMain.height, SWP_NOCOPYBITS|SWP_NOZORDER);
\r
2413 case WMSZ_BOTTOMRIGHT:
\r
2417 SetWindowPos(hwndMain, NULL, 0, 0, wpMain.width, wpMain.height,
\r
2418 SWP_NOCOPYBITS|SWP_NOZORDER|SWP_NOMOVE);
\r
2423 for (i = 0; i < N_BUTTONS; i++) {
\r
2424 if (buttonDesc[i].hwnd != NULL) {
\r
2425 DestroyWindow(buttonDesc[i].hwnd);
\r
2426 buttonDesc[i].hwnd = NULL;
\r
2428 if (appData.showButtonBar) {
\r
2429 buttonDesc[i].hwnd =
\r
2430 CreateWindow("BUTTON", buttonDesc[i].label,
\r
2431 WS_VISIBLE | WS_CHILD | BS_PUSHBUTTON,
\r
2432 boardRect.right - BUTTON_WIDTH*(N_BUTTONS-i),
\r
2433 messageRect.top, BUTTON_WIDTH, messageSize.cy, hwndMain,
\r
2434 (HMENU) buttonDesc[i].id,
\r
2435 (HINSTANCE) GetWindowLongPtr(hwndMain, GWLP_HINSTANCE), NULL);
\r
2437 SendMessage(buttonDesc[i].hwnd, WM_SETFONT,
\r
2438 (WPARAM)font[boardSize][MESSAGE_FONT]->hf,
\r
2439 MAKELPARAM(FALSE, 0));
\r
2441 if (buttonDesc[i].id == IDM_Pause)
\r
2442 hwndPause = buttonDesc[i].hwnd;
\r
2443 buttonDesc[i].wndproc = (WNDPROC)
\r
2444 SetWindowLongPtr(buttonDesc[i].hwnd, GWLP_WNDPROC, (LONG_PTR) ButtonProc);
\r
2447 if (gridPen != NULL) DeleteObject(gridPen);
\r
2448 if (highlightPen != NULL) DeleteObject(highlightPen);
\r
2449 if (premovePen != NULL) DeleteObject(premovePen);
\r
2450 if (lineGap != 0) {
\r
2451 logbrush.lbStyle = BS_SOLID;
\r
2452 logbrush.lbColor = RGB(0, 0, 0); /* grid pen color = black */
\r
2454 ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,
\r
2455 lineGap, &logbrush, 0, NULL);
\r
2456 logbrush.lbColor = highlightSquareColor;
\r
2458 ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,
\r
2459 lineGap, &logbrush, 0, NULL);
\r
2461 logbrush.lbColor = premoveHighlightColor;
\r
2463 ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,
\r
2464 lineGap, &logbrush, 0, NULL);
\r
2466 /* [HGM] Loop had to be split in part for vert. and hor. lines */
\r
2467 for (i = 0; i < BOARD_HEIGHT + 1; i++) {
\r
2468 gridEndpoints[i*2].x = boardRect.left + lineGap / 2;
\r
2469 gridEndpoints[i*2].y = gridEndpoints[i*2 + 1].y =
\r
2470 boardRect.top + lineGap / 2 + (i * (squareSize + lineGap));
\r
2471 gridEndpoints[i*2 + 1].x = boardRect.left + lineGap / 2 +
\r
2472 BOARD_WIDTH * (squareSize + lineGap);
\r
2473 gridVertexCounts[i*2] = gridVertexCounts[i*2 + 1] = 2;
\r
2475 for (i = 0; i < BOARD_WIDTH + 1; i++) {
\r
2476 gridEndpoints[i*2 + BOARD_HEIGHT*2 + 2].y = boardRect.top + lineGap / 2;
\r
2477 gridEndpoints[i*2 + BOARD_HEIGHT*2 + 2].x =
\r
2478 gridEndpoints[i*2 + 1 + BOARD_HEIGHT*2 + 2].x = boardRect.left +
\r
2479 lineGap / 2 + (i * (squareSize + lineGap));
\r
2480 gridEndpoints[i*2 + 1 + BOARD_HEIGHT*2 + 2].y =
\r
2481 boardRect.top + BOARD_HEIGHT * (squareSize + lineGap);
\r
2482 gridVertexCounts[i*2] = gridVertexCounts[i*2 + 1] = 2;
\r
2486 /* [HGM] Licensing requirement */
\r
2488 if(gameInfo.variant == VariantGothic) GothicPopUp( GOTHIC, VariantGothic); else
\r
2491 if(gameInfo.variant == VariantFalcon) GothicPopUp( FALCON, VariantFalcon); else
\r
2493 GothicPopUp( "", VariantNormal);
\r
2496 /* if (boardSize == oldBoardSize) return; [HGM] variant might have changed */
\r
2498 /* Load piece bitmaps for this board size */
\r
2499 for (i=0; i<=2; i++) {
\r
2500 for (piece = WhitePawn;
\r
2501 (int) piece < (int) BlackPawn;
\r
2502 piece = (ChessSquare) ((int) piece + 1)) {
\r
2503 if (pieceBitmap[i][piece] != NULL)
\r
2504 DeleteObject(pieceBitmap[i][piece]);
\r
2508 fontBitmapSquareSize = 0; /* [HGM] render: make sure pieces will be recreated, as we might need others now */
\r
2509 // Orthodox Chess pieces
\r
2510 pieceBitmap[0][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "s");
\r
2511 pieceBitmap[0][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "s");
\r
2512 pieceBitmap[0][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "s");
\r
2513 pieceBitmap[0][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "s");
\r
2514 pieceBitmap[0][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "s");
\r
2515 pieceBitmap[1][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "o");
\r
2516 pieceBitmap[1][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "o");
\r
2517 pieceBitmap[1][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "o");
\r
2518 pieceBitmap[1][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "o");
\r
2519 pieceBitmap[1][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "o");
\r
2520 pieceBitmap[2][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "w");
\r
2521 pieceBitmap[2][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "w");
\r
2522 pieceBitmap[2][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "w");
\r
2523 pieceBitmap[2][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "w");
\r
2524 pieceBitmap[2][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "w");
\r
2525 if( gameInfo.variant == VariantShogi && squareSize <= 72 && squareSize >= 33) {
\r
2526 // in Shogi, Hijack the unused Queen for Lance
\r
2527 pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "s");
\r
2528 pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "o");
\r
2529 pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "w");
\r
2531 pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "s");
\r
2532 pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "o");
\r
2533 pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "w");
\r
2536 if(squareSize <= 72 && squareSize >= 33) {
\r
2537 /* A & C are available in most sizes now */
\r
2538 if(squareSize != 49 && squareSize != 72 && squareSize != 33) { // Vortex-like
\r
2539 pieceBitmap[0][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "s");
\r
2540 pieceBitmap[1][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "o");
\r
2541 pieceBitmap[2][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "w");
\r
2542 pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "s");
\r
2543 pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "o");
\r
2544 pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "w");
\r
2545 pieceBitmap[0][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "s");
\r
2546 pieceBitmap[1][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "o");
\r
2547 pieceBitmap[2][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "w");
\r
2548 pieceBitmap[0][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "s");
\r
2549 pieceBitmap[1][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "o");
\r
2550 pieceBitmap[2][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "w");
\r
2551 } else { // Smirf-like
\r
2552 if(gameInfo.variant == VariantSChess) {
\r
2553 pieceBitmap[0][WhiteAngel] = DoLoadBitmap(hInst, "v", squareSize, "s");
\r
2554 pieceBitmap[1][WhiteAngel] = DoLoadBitmap(hInst, "v", squareSize, "o");
\r
2555 pieceBitmap[2][WhiteAngel] = DoLoadBitmap(hInst, "v", squareSize, "w");
\r
2557 pieceBitmap[0][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "s");
\r
2558 pieceBitmap[1][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "o");
\r
2559 pieceBitmap[2][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "w");
\r
2562 if(gameInfo.variant == VariantGothic) { // Vortex-like
\r
2563 pieceBitmap[0][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "s");
\r
2564 pieceBitmap[1][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "o");
\r
2565 pieceBitmap[2][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "w");
\r
2566 } else if(gameInfo.variant == VariantSChess && (squareSize == 49 || squareSize == 72)) {
\r
2567 pieceBitmap[0][WhiteMarshall] = DoLoadBitmap(hInst, "e", squareSize, "s");
\r
2568 pieceBitmap[1][WhiteMarshall] = DoLoadBitmap(hInst, "e", squareSize, "o");
\r
2569 pieceBitmap[2][WhiteMarshall] = DoLoadBitmap(hInst, "e", squareSize, "w");
\r
2570 } else { // WinBoard standard
\r
2571 pieceBitmap[0][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "s");
\r
2572 pieceBitmap[1][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "o");
\r
2573 pieceBitmap[2][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "w");
\r
2578 if(squareSize==72 || squareSize==49 || squareSize==33) { /* experiment with some home-made bitmaps */
\r
2579 pieceBitmap[0][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "s");
\r
2580 pieceBitmap[1][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "o");
\r
2581 pieceBitmap[2][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "w");
\r
2582 pieceBitmap[0][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "s");
\r
2583 pieceBitmap[1][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "o");
\r
2584 pieceBitmap[2][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "w");
\r
2585 pieceBitmap[0][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "s");
\r
2586 pieceBitmap[1][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "o");
\r
2587 pieceBitmap[2][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "w");
\r
2588 pieceBitmap[0][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "s");
\r
2589 pieceBitmap[1][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "o");
\r
2590 pieceBitmap[2][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "w");
\r
2591 pieceBitmap[0][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "s");
\r
2592 pieceBitmap[1][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "o");
\r
2593 pieceBitmap[2][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "w");
\r
2594 pieceBitmap[0][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "s");
\r
2595 pieceBitmap[1][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "o");
\r
2596 pieceBitmap[2][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "w");
\r
2597 pieceBitmap[0][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "s");
\r
2598 pieceBitmap[1][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "o");
\r
2599 pieceBitmap[2][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "w");
\r
2600 pieceBitmap[0][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "s");
\r
2601 pieceBitmap[1][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "o");
\r
2602 pieceBitmap[2][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "w");
\r
2603 pieceBitmap[0][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "s");
\r
2604 pieceBitmap[1][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "o");
\r
2605 pieceBitmap[2][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "w");
\r
2606 pieceBitmap[0][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "s");
\r
2607 pieceBitmap[1][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "o");
\r
2608 pieceBitmap[2][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "w");
\r
2610 if(gameInfo.variant == VariantShogi) { /* promoted Gold represemtations */
\r
2611 pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "wp", squareSize, "s");
\r
2612 pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "wp", squareSize, "o");
\r
2613 pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "w", squareSize, "w");
\r
2614 pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "wn", squareSize, "s");
\r
2615 pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "wn", squareSize, "o");
\r
2616 pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "w", squareSize, "w");
\r
2617 pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "ws", squareSize, "s");
\r
2618 pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "ws", squareSize, "o");
\r
2619 pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "w", squareSize, "w");
\r
2620 pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "wl", squareSize, "s");
\r
2621 pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "wl", squareSize, "o");
\r
2622 pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "w", squareSize, "w");
\r
2624 pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "s");
\r
2625 pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "o");
\r
2626 pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "w");
\r
2627 pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "s");
\r
2628 pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "o");
\r
2629 pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "w");
\r
2630 pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "s");
\r
2631 pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "o");
\r
2632 pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "w");
\r
2633 pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "s");
\r
2634 pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "o");
\r
2635 pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "w");
\r
2638 } else { /* other size, no special bitmaps available. Use smaller symbols */
\r
2639 if((int)boardSize < 2) minorSize = sizeInfo[0].squareSize;
\r
2640 else minorSize = sizeInfo[(int)boardSize - 2].squareSize;
\r
2641 pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "s");
\r
2642 pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "o");
\r
2643 pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "w");
\r
2644 pieceBitmap[0][WhiteCardinal] = DoLoadBitmap(hInst, "b", minorSize, "s");
\r
2645 pieceBitmap[1][WhiteCardinal] = DoLoadBitmap(hInst, "b", minorSize, "o");
\r
2646 pieceBitmap[2][WhiteCardinal] = DoLoadBitmap(hInst, "b", minorSize, "w");
\r
2647 pieceBitmap[0][WhiteDragon] = DoLoadBitmap(hInst, "r", minorSize, "s");
\r
2648 pieceBitmap[1][WhiteDragon] = DoLoadBitmap(hInst, "r", minorSize, "o");
\r
2649 pieceBitmap[2][WhiteDragon] = DoLoadBitmap(hInst, "r", minorSize, "w");
\r
2650 pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "s");
\r
2651 pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "o");
\r
2652 pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "w");
\r
2656 if(gameInfo.variant == VariantShogi && squareSize == 58)
\r
2657 /* special Shogi support in this size */
\r
2658 { for (i=0; i<=2; i++) { /* replace all bitmaps */
\r
2659 for (piece = WhitePawn;
\r
2660 (int) piece < (int) BlackPawn;
\r
2661 piece = (ChessSquare) ((int) piece + 1)) {
\r
2662 if (pieceBitmap[i][piece] != NULL)
\r
2663 DeleteObject(pieceBitmap[i][piece]);
\r
2666 pieceBitmap[0][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "o");
\r
2667 pieceBitmap[0][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "o");
\r
2668 pieceBitmap[0][WhiteBishop] = DoLoadBitmap(hInst, "sb", squareSize, "o");
\r
2669 pieceBitmap[0][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "o");
\r
2670 pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "o");
\r
2671 pieceBitmap[0][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "o");
\r
2672 pieceBitmap[0][WhiteFerz] = DoLoadBitmap(hInst, "sf", squareSize, "o");
\r
2673 pieceBitmap[0][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "o");
\r
2674 pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "su", squareSize, "o");
\r
2675 pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "sh", squareSize, "o");
\r
2676 pieceBitmap[0][WhiteCardinal] = DoLoadBitmap(hInst, "sa", squareSize, "o");
\r
2677 pieceBitmap[0][WhiteDragon] = DoLoadBitmap(hInst, "sc", squareSize, "o");
\r
2678 pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "sg", squareSize, "o");
\r
2679 pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "ss", squareSize, "o");
\r
2680 pieceBitmap[1][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "o");
\r
2681 pieceBitmap[1][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "o");
\r
2682 pieceBitmap[1][WhiteBishop] = DoLoadBitmap(hInst, "sb", squareSize, "o");
\r
2683 pieceBitmap[1][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "o");
\r
2684 pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "o");
\r
2685 pieceBitmap[1][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "o");
\r
2686 pieceBitmap[1][WhiteFerz] = DoLoadBitmap(hInst, "sf", squareSize, "o");
\r
2687 pieceBitmap[1][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "o");
\r
2688 pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "su", squareSize, "o");
\r
2689 pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "sh", squareSize, "o");
\r
2690 pieceBitmap[1][WhiteCardinal] = DoLoadBitmap(hInst, "sa", squareSize, "o");
\r
2691 pieceBitmap[1][WhiteDragon] = DoLoadBitmap(hInst, "sc", squareSize, "o");
\r
2692 pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "sg", squareSize, "o");
\r
2693 pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "ss", squareSize, "o");
\r
2694 pieceBitmap[2][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "w");
\r
2695 pieceBitmap[2][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "w");
\r
2696 pieceBitmap[2][WhiteBishop] = DoLoadBitmap(hInst, "sr", squareSize, "w");
\r
2697 pieceBitmap[2][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "w");
\r
2698 pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "w");
\r
2699 pieceBitmap[2][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "w");
\r
2700 pieceBitmap[2][WhiteFerz] = DoLoadBitmap(hInst, "sw", squareSize, "w");
\r
2701 pieceBitmap[2][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "w");
\r
2702 pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "sp", squareSize, "w");
\r
2703 pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "sn", squareSize, "w");
\r
2704 pieceBitmap[2][WhiteCardinal] = DoLoadBitmap(hInst, "sr", squareSize, "w");
\r
2705 pieceBitmap[2][WhiteDragon] = DoLoadBitmap(hInst, "sr", squareSize, "w");
\r
2706 pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "sl", squareSize, "w");
\r
2707 pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "sw", squareSize, "w");
\r
2713 PieceBitmap(ChessSquare p, int kind)
\r
2715 if ((int) p >= (int) BlackPawn)
\r
2716 p = (ChessSquare) ((int) p - (int) BlackPawn + (int) WhitePawn);
\r
2718 return pieceBitmap[kind][(int) p];
\r
2721 /***************************************************************/
\r
2723 #define MIN(a,b) (((a) < (b)) ? (a) : (b))
\r
2724 #define MAX(a,b) (((a) > (b)) ? (a) : (b))
\r
2726 #define MIN3(a,b,c) (((a) < (b) && (a) < (c)) ? (a) : (((b) < (a) && (b) < (c)) ? (b) : (c)))
\r
2727 #define MAX3(a,b,c) (((a) > (b) && (a) > (c)) ? (a) : (((b) > (a) && (b) > (c)) ? (b) : (c)))
\r
2731 SquareToPos(int row, int column, int * x, int * y)
\r
2734 *x = boardRect.left + lineGap + ((BOARD_WIDTH-1)-column) * (squareSize + lineGap);
\r
2735 *y = boardRect.top + lineGap + row * (squareSize + lineGap);
\r
2737 *x = boardRect.left + lineGap + column * (squareSize + lineGap);
\r
2738 *y = boardRect.top + lineGap + ((BOARD_HEIGHT-1)-row) * (squareSize + lineGap);
\r
2743 DrawCoordsOnDC(HDC hdc)
\r
2745 static char files[] = "0123456789012345678901221098765432109876543210";
\r
2746 static char ranks[] = "wvutsrqponmlkjihgfedcbaabcdefghijklmnopqrstuvw";
\r
2747 char str[2] = { NULLCHAR, NULLCHAR };
\r
2748 int oldMode, oldAlign, x, y, start, i;
\r
2752 if (!appData.showCoords)
\r
2755 start = flipView ? 1-(ONE!='1') : 45+(ONE!='1')-BOARD_HEIGHT;
\r
2757 oldBrush = SelectObject(hdc, GetStockObject(BLACK_BRUSH));
\r
2758 oldMode = SetBkMode(hdc, (appData.monoMode ? OPAQUE : TRANSPARENT));
\r
2759 oldAlign = GetTextAlign(hdc);
\r
2760 oldFont = SelectObject(hdc, font[boardSize][COORD_FONT]->hf);
\r
2762 y = boardRect.top + lineGap;
\r
2763 x = boardRect.left + lineGap + gameInfo.holdingsWidth*(squareSize + lineGap);
\r
2765 SetTextAlign(hdc, TA_LEFT|TA_TOP);
\r
2766 for (i = 0; i < BOARD_HEIGHT; i++) {
\r
2767 str[0] = files[start + i];
\r
2768 ExtTextOut(hdc, x + 2, y + 1, 0, NULL, str, 1, NULL);
\r
2769 y += squareSize + lineGap;
\r
2772 start = flipView ? 23-(BOARD_RGHT-BOARD_LEFT) : 23;
\r
2774 SetTextAlign(hdc, TA_RIGHT|TA_BOTTOM);
\r
2775 for (i = 0; i < BOARD_RGHT - BOARD_LEFT; i++) {
\r
2776 str[0] = ranks[start + i];
\r
2777 ExtTextOut(hdc, x + squareSize - 2, y - 1, 0, NULL, str, 1, NULL);
\r
2778 x += squareSize + lineGap;
\r
2781 SelectObject(hdc, oldBrush);
\r
2782 SetBkMode(hdc, oldMode);
\r
2783 SetTextAlign(hdc, oldAlign);
\r
2784 SelectObject(hdc, oldFont);
\r
2788 DrawGridOnDC(HDC hdc)
\r
2792 if (lineGap != 0) {
\r
2793 oldPen = SelectObject(hdc, gridPen);
\r
2794 PolyPolyline(hdc, gridEndpoints, gridVertexCounts, BOARD_WIDTH+BOARD_HEIGHT + 2);
\r
2795 SelectObject(hdc, oldPen);
\r
2799 #define HIGHLIGHT_PEN 0
\r
2800 #define PREMOVE_PEN 1
\r
2803 DrawHighlightOnDC(HDC hdc, BOOLEAN on, int x, int y, int pen)
\r
2806 HPEN oldPen, hPen;
\r
2807 if (lineGap == 0) return;
\r
2809 x1 = boardRect.left +
\r
2810 lineGap/2 + ((BOARD_WIDTH-1)-x) * (squareSize + lineGap);
\r
2811 y1 = boardRect.top +
\r
2812 lineGap/2 + y * (squareSize + lineGap);
\r
2814 x1 = boardRect.left +
\r
2815 lineGap/2 + x * (squareSize + lineGap);
\r
2816 y1 = boardRect.top +
\r
2817 lineGap/2 + ((BOARD_HEIGHT-1)-y) * (squareSize + lineGap);
\r
2819 hPen = pen ? premovePen : highlightPen;
\r
2820 oldPen = SelectObject(hdc, on ? hPen : gridPen);
\r
2821 MoveToEx(hdc, x1, y1, NULL);
\r
2822 LineTo(hdc, x1 + squareSize + lineGap, y1);
\r
2823 LineTo(hdc, x1 + squareSize + lineGap, y1 + squareSize + lineGap);
\r
2824 LineTo(hdc, x1, y1 + squareSize + lineGap);
\r
2825 LineTo(hdc, x1, y1);
\r
2826 SelectObject(hdc, oldPen);
\r
2830 DrawHighlightsOnDC(HDC hdc, HighlightInfo *h, int pen)
\r
2833 for (i=0; i<2; i++) {
\r
2834 if (h->sq[i].x >= 0 && h->sq[i].y >= 0)
\r
2835 DrawHighlightOnDC(hdc, TRUE,
\r
2836 h->sq[i].x, h->sq[i].y,
\r
2841 /* Note: sqcolor is used only in monoMode */
\r
2842 /* Note that this code is largely duplicated in woptions.c,
\r
2843 function DrawSampleSquare, so that needs to be updated too */
\r
2845 DrawPieceOnDC(HDC hdc, ChessSquare piece, int color, int sqcolor, int x, int y, HDC tmphdc)
\r
2847 HBITMAP oldBitmap;
\r
2851 if (appData.blindfold) return;
\r
2853 /* [AS] Use font-based pieces if needed */
\r
2854 if( fontBitmapSquareSize >= 0 && (squareSize > 32 || gameInfo.variant >= VariantShogi)) {
\r
2855 /* Create piece bitmaps, or do nothing if piece set is up to date */
\r
2856 CreatePiecesFromFont();
\r
2858 if( fontBitmapSquareSize == squareSize ) {
\r
2859 int index = TranslatePieceToFontPiece(piece);
\r
2861 SelectObject( tmphdc, hPieceMask[ index ] );
\r
2863 if(appData.upsideDown ? color==flipView : (flipView && gameInfo.variant == VariantShogi))
\r
2864 StretchBlt(hdc, x+squareSize, y+squareSize, -squareSize, -squareSize, tmphdc, 0, 0, squareSize, squareSize, SRCAND);
\r
2868 squareSize, squareSize,
\r
2873 SelectObject( tmphdc, hPieceFace[ index ] );
\r
2875 if(appData.upsideDown ? color==flipView : (flipView && gameInfo.variant == VariantShogi))
\r
2876 StretchBlt(hdc, x+squareSize, y+squareSize, -squareSize, -squareSize, tmphdc, 0, 0, squareSize, squareSize, SRCPAINT);
\r
2880 squareSize, squareSize,
\r
2889 if (appData.monoMode) {
\r
2890 SelectObject(tmphdc, PieceBitmap(piece,
\r
2891 color == sqcolor ? OUTLINE_PIECE : SOLID_PIECE));
\r
2892 BitBlt(hdc, x, y, squareSize, squareSize, tmphdc, 0, 0,
\r
2893 sqcolor ? SRCCOPY : NOTSRCCOPY);
\r
2895 tmpSize = squareSize;
\r
2897 ((piece >= (int)WhiteNightrider && piece <= WhiteGrasshopper) ||
\r
2898 (piece >= (int)BlackNightrider && piece <= BlackGrasshopper)) ) {
\r
2899 /* [HGM] no bitmap available for promoted pieces in Crazyhouse */
\r
2900 /* Bitmaps of smaller size are substituted, but we have to align them */
\r
2901 x += (squareSize - minorSize)>>1;
\r
2902 y += squareSize - minorSize - 2;
\r
2903 tmpSize = minorSize;
\r
2905 if (color || appData.allWhite ) {
\r
2906 oldBitmap = SelectObject(tmphdc, PieceBitmap(piece, WHITE_PIECE));
\r
2908 oldBrush = SelectObject(hdc, whitePieceBrush);
\r
2909 else oldBrush = SelectObject(hdc, blackPieceBrush);
\r
2910 if(appData.upsideDown && color==flipView)
\r
2911 StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, 0x00B8074A);
\r
2913 BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, 0x00B8074A);
\r
2914 /* Use black for outline of white pieces */
\r
2915 SelectObject(tmphdc, PieceBitmap(piece, OUTLINE_PIECE));
\r
2916 if(appData.upsideDown && color==flipView)
\r
2917 StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, SRCAND);
\r
2919 BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, SRCAND);
\r
2921 /* Use square color for details of black pieces */
\r
2922 oldBitmap = SelectObject(tmphdc, PieceBitmap(piece, SOLID_PIECE));
\r
2923 oldBrush = SelectObject(hdc, blackPieceBrush);
\r
2924 if(appData.upsideDown && !flipView)
\r
2925 StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, 0x00B8074A);
\r
2927 BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, 0x00B8074A);
\r
2929 SelectObject(hdc, oldBrush);
\r
2930 SelectObject(tmphdc, oldBitmap);
\r
2934 /* [AS] Compute a drawing mode for a square, based on specified settings (see DrawTile) */
\r
2935 int GetBackTextureMode( int algo )
\r
2937 int result = BACK_TEXTURE_MODE_DISABLED;
\r
2941 case BACK_TEXTURE_MODE_PLAIN:
\r
2942 result = 1; /* Always use identity map */
\r
2944 case BACK_TEXTURE_MODE_FULL_RANDOM:
\r
2945 result = 1 + (myrandom() % 3); /* Pick a transformation at random */
\r
2953 [AS] Compute and save texture drawing info, otherwise we may not be able
\r
2954 to handle redraws cleanly (as random numbers would always be different).
\r
2956 VOID RebuildTextureSquareInfo()
\r
2966 ZeroMemory( &backTextureSquareInfo, sizeof(backTextureSquareInfo) );
\r
2968 if( liteBackTexture != NULL ) {
\r
2969 if( GetObject( liteBackTexture, sizeof(bi), &bi ) > 0 ) {
\r
2970 lite_w = bi.bmWidth;
\r
2971 lite_h = bi.bmHeight;
\r
2975 if( darkBackTexture != NULL ) {
\r
2976 if( GetObject( darkBackTexture, sizeof(bi), &bi ) > 0 ) {
\r
2977 dark_w = bi.bmWidth;
\r
2978 dark_h = bi.bmHeight;
\r
2982 for( row=0; row<BOARD_HEIGHT; row++ ) {
\r
2983 for( col=0; col<BOARD_WIDTH; col++ ) {
\r
2984 if( (col + row) & 1 ) {
\r
2986 if( lite_w >= squareSize && lite_h >= squareSize ) {
\r
2987 if( lite_w >= squareSize*BOARD_WIDTH )
\r
2988 backTextureSquareInfo[row][col].x = (2*col+1)*lite_w/(2*BOARD_WIDTH) - squareSize/2; /* [HGM] cut out of center of virtual square */
\r
2990 backTextureSquareInfo[row][col].x = col * (lite_w - squareSize) / (BOARD_WIDTH-1); /* [HGM] divide by size-1 in stead of size! */
\r
2991 if( lite_h >= squareSize*BOARD_HEIGHT )
\r
2992 backTextureSquareInfo[row][col].y = (2*(BOARD_HEIGHT-row)-1)*lite_h/(2*BOARD_HEIGHT) - squareSize/2;
\r
2994 backTextureSquareInfo[row][col].y = (BOARD_HEIGHT-1-row) * (lite_h - squareSize) / (BOARD_HEIGHT-1);
\r
2995 backTextureSquareInfo[row][col].mode = GetBackTextureMode(liteBackTextureMode);
\r
3000 if( dark_w >= squareSize && dark_h >= squareSize ) {
\r
3001 if( dark_w >= squareSize*BOARD_WIDTH )
\r
3002 backTextureSquareInfo[row][col].x = (2*col+1) * dark_w / (2*BOARD_WIDTH) - squareSize/2;
\r
3004 backTextureSquareInfo[row][col].x = col * (dark_w - squareSize) / (BOARD_WIDTH-1);
\r
3005 if( dark_h >= squareSize*BOARD_HEIGHT )
\r
3006 backTextureSquareInfo[row][col].y = (2*(BOARD_HEIGHT-row)-1) * dark_h / (2*BOARD_HEIGHT) - squareSize/2;
\r
3008 backTextureSquareInfo[row][col].y = (BOARD_HEIGHT-1-row) * (dark_h - squareSize) / (BOARD_HEIGHT-1);
\r
3009 backTextureSquareInfo[row][col].mode = GetBackTextureMode(darkBackTextureMode);
\r
3016 /* [AS] Arrow highlighting support */
\r
3018 static double A_WIDTH = 5; /* Width of arrow body */
\r
3020 #define A_HEIGHT_FACTOR 6 /* Length of arrow "point", relative to body width */
\r
3021 #define A_WIDTH_FACTOR 3 /* Width of arrow "point", relative to body width */
\r
3023 static double Sqr( double x )
\r
3028 static int Round( double x )
\r
3030 return (int) (x + 0.5);
\r
3033 /* Draw an arrow between two points using current settings */
\r
3034 VOID DrawArrowBetweenPoints( HDC hdc, int s_x, int s_y, int d_x, int d_y )
\r
3037 double dx, dy, j, k, x, y;
\r
3039 if( d_x == s_x ) {
\r
3040 int h = (d_y > s_y) ? +A_WIDTH*A_HEIGHT_FACTOR : -A_WIDTH*A_HEIGHT_FACTOR;
\r
3042 arrow[0].x = s_x + A_WIDTH + 0.5;
\r
3045 arrow[1].x = s_x + A_WIDTH + 0.5;
\r
3046 arrow[1].y = d_y - h;
\r
3048 arrow[2].x = arrow[1].x + A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;
\r
3049 arrow[2].y = d_y - h;
\r
3054 arrow[5].x = arrow[1].x - 2*A_WIDTH + 0.5;
\r
3055 arrow[5].y = d_y - h;
\r
3057 arrow[4].x = arrow[5].x - A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;
\r
3058 arrow[4].y = d_y - h;
\r
3060 arrow[6].x = arrow[1].x - 2*A_WIDTH + 0.5;
\r
3063 else if( d_y == s_y ) {
\r
3064 int w = (d_x > s_x) ? +A_WIDTH*A_HEIGHT_FACTOR : -A_WIDTH*A_HEIGHT_FACTOR;
\r
3067 arrow[0].y = s_y + A_WIDTH + 0.5;
\r
3069 arrow[1].x = d_x - w;
\r
3070 arrow[1].y = s_y + A_WIDTH + 0.5;
\r
3072 arrow[2].x = d_x - w;
\r
3073 arrow[2].y = arrow[1].y + A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;
\r
3078 arrow[5].x = d_x - w;
\r
3079 arrow[5].y = arrow[1].y - 2*A_WIDTH + 0.5;
\r
3081 arrow[4].x = d_x - w;
\r
3082 arrow[4].y = arrow[5].y - A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;
\r
3085 arrow[6].y = arrow[1].y - 2*A_WIDTH + 0.5;
\r
3088 /* [AS] Needed a lot of paper for this! :-) */
\r
3089 dy = (double) (d_y - s_y) / (double) (d_x - s_x);
\r
3090 dx = (double) (s_x - d_x) / (double) (s_y - d_y);
\r
3092 j = sqrt( Sqr(A_WIDTH) / (1.0 + Sqr(dx)) );
\r
3094 k = sqrt( Sqr(A_WIDTH*A_HEIGHT_FACTOR) / (1.0 + Sqr(dy)) );
\r
3099 arrow[0].x = Round(x - j);
\r
3100 arrow[0].y = Round(y + j*dx);
\r
3102 arrow[1].x = Round(arrow[0].x + 2*j); // [HGM] prevent width to be affected by rounding twice
\r
3103 arrow[1].y = Round(arrow[0].y - 2*j*dx);
\r
3106 x = (double) d_x - k;
\r
3107 y = (double) d_y - k*dy;
\r
3110 x = (double) d_x + k;
\r
3111 y = (double) d_y + k*dy;
\r
3114 x = Round(x); y = Round(y); // [HGM] make sure width of shaft is rounded the same way on both ends
\r
3116 arrow[6].x = Round(x - j);
\r
3117 arrow[6].y = Round(y + j*dx);
\r
3119 arrow[2].x = Round(arrow[6].x + 2*j);
\r
3120 arrow[2].y = Round(arrow[6].y - 2*j*dx);
\r
3122 arrow[3].x = Round(arrow[2].x + j*(A_WIDTH_FACTOR-1));
\r
3123 arrow[3].y = Round(arrow[2].y - j*(A_WIDTH_FACTOR-1)*dx);
\r
3128 arrow[5].x = Round(arrow[6].x - j*(A_WIDTH_FACTOR-1));
\r
3129 arrow[5].y = Round(arrow[6].y + j*(A_WIDTH_FACTOR-1)*dx);
\r
3132 Polygon( hdc, arrow, 7 );
\r
3135 /* [AS] Draw an arrow between two squares */
\r
3136 VOID DrawArrowBetweenSquares( HDC hdc, int s_col, int s_row, int d_col, int d_row )
\r
3138 int s_x, s_y, d_x, d_y;
\r
3145 if( s_col == d_col && s_row == d_row ) {
\r
3149 /* Get source and destination points */
\r
3150 SquareToPos( s_row, s_col, &s_x, &s_y);
\r
3151 SquareToPos( d_row, d_col, &d_x, &d_y);
\r
3154 d_y += squareSize / 2 - squareSize / 4; // [HGM] round towards same centers on all sides!
\r
3156 else if( d_y < s_y ) {
\r
3157 d_y += squareSize / 2 + squareSize / 4;
\r
3160 d_y += squareSize / 2;
\r
3164 d_x += squareSize / 2 - squareSize / 4;
\r
3166 else if( d_x < s_x ) {
\r
3167 d_x += squareSize / 2 + squareSize / 4;
\r
3170 d_x += squareSize / 2;
\r
3173 s_x += squareSize / 2;
\r
3174 s_y += squareSize / 2;
\r
3176 /* Adjust width */
\r
3177 A_WIDTH = squareSize / 14.; //[HGM] make float
\r
3180 stLB.lbStyle = BS_SOLID;
\r
3181 stLB.lbColor = appData.highlightArrowColor;
\r
3184 hpen = CreatePen( PS_SOLID, 2, RGB(0x00,0x00,0x00) );
\r
3185 holdpen = SelectObject( hdc, hpen );
\r
3186 hbrush = CreateBrushIndirect( &stLB );
\r
3187 holdbrush = SelectObject( hdc, hbrush );
\r
3189 DrawArrowBetweenPoints( hdc, s_x, s_y, d_x, d_y );
\r
3191 SelectObject( hdc, holdpen );
\r
3192 SelectObject( hdc, holdbrush );
\r
3193 DeleteObject( hpen );
\r
3194 DeleteObject( hbrush );
\r
3197 BOOL HasHighlightInfo()
\r
3199 BOOL result = FALSE;
\r
3201 if( highlightInfo.sq[0].x >= 0 && highlightInfo.sq[0].y >= 0 &&
\r
3202 highlightInfo.sq[1].x >= 0 && highlightInfo.sq[1].y >= 0 )
\r
3210 BOOL IsDrawArrowEnabled()
\r
3212 BOOL result = FALSE;
\r
3214 if( appData.highlightMoveWithArrow && squareSize >= 32 ) {
\r
3221 VOID DrawArrowHighlight( HDC hdc )
\r
3223 if( IsDrawArrowEnabled() && HasHighlightInfo() ) {
\r
3224 DrawArrowBetweenSquares( hdc,
\r
3225 highlightInfo.sq[0].x, highlightInfo.sq[0].y,
\r
3226 highlightInfo.sq[1].x, highlightInfo.sq[1].y );
\r
3230 HRGN GetArrowHighlightClipRegion( HDC hdc )
\r
3232 HRGN result = NULL;
\r
3234 if( HasHighlightInfo() ) {
\r
3235 int x1, y1, x2, y2;
\r
3236 int sx, sy, dx, dy;
\r
3238 SquareToPos(highlightInfo.sq[0].y, highlightInfo.sq[0].x, &x1, &y1 );
\r
3239 SquareToPos(highlightInfo.sq[1].y, highlightInfo.sq[1].x, &x2, &y2 );
\r
3241 sx = MIN( x1, x2 );
\r
3242 sy = MIN( y1, y2 );
\r
3243 dx = MAX( x1, x2 ) + squareSize;
\r
3244 dy = MAX( y1, y2 ) + squareSize;
\r
3246 result = CreateRectRgn( sx, sy, dx, dy );
\r
3253 Warning: this function modifies the behavior of several other functions.
\r
3255 Basically, Winboard is optimized to avoid drawing the whole board if not strictly
\r
3256 needed. Unfortunately, the decision whether or not to perform a full or partial
\r
3257 repaint is scattered all over the place, which is not good for features such as
\r
3258 "arrow highlighting" that require a full repaint of the board.
\r
3260 So, I've tried to patch the code where I thought it made sense (e.g. after or during
\r
3261 user interaction, when speed is not so important) but especially to avoid errors
\r
3262 in the displayed graphics.
\r
3264 In such patched places, I always try refer to this function so there is a single
\r
3265 place to maintain knowledge.
\r
3267 To restore the original behavior, just return FALSE unconditionally.
\r
3269 BOOL IsFullRepaintPreferrable()
\r
3271 BOOL result = FALSE;
\r
3273 if( (appData.highlightLastMove || appData.highlightDragging) && IsDrawArrowEnabled() ) {
\r
3274 /* Arrow may appear on the board */
\r
3282 This function is called by DrawPosition to know whether a full repaint must
\r
3285 Only DrawPosition may directly call this function, which makes use of
\r
3286 some state information. Other function should call DrawPosition specifying
\r
3287 the repaint flag, and can use IsFullRepaintPreferrable if needed.
\r
3289 BOOL DrawPositionNeedsFullRepaint()
\r
3291 BOOL result = FALSE;
\r
3294 Probably a slightly better policy would be to trigger a full repaint
\r
3295 when animInfo.piece changes state (i.e. empty -> non-empty and viceversa),
\r
3296 but animation is fast enough that it's difficult to notice.
\r
3298 if( animInfo.piece == EmptySquare ) {
\r
3299 if( (appData.highlightLastMove || appData.highlightDragging) && IsDrawArrowEnabled() /*&& HasHighlightInfo()*/ ) {
\r
3308 DrawBoardOnDC(HDC hdc, Board board, HDC tmphdc)
\r
3310 int row, column, x, y, square_color, piece_color;
\r
3311 ChessSquare piece;
\r
3313 HDC texture_hdc = NULL;
\r
3315 /* [AS] Initialize background textures if needed */
\r
3316 if( liteBackTexture != NULL || darkBackTexture != NULL ) {
\r
3317 static int backTextureBoardSize; /* [HGM] boardsize: also new texture if board format changed */
\r
3318 if( backTextureSquareSize != squareSize
\r
3319 || backTextureBoardSize != BOARD_WIDTH+BOARD_FILES*BOARD_HEIGHT) {
\r
3320 backTextureBoardSize = BOARD_WIDTH+BOARD_FILES*BOARD_HEIGHT;
\r
3321 backTextureSquareSize = squareSize;
\r
3322 RebuildTextureSquareInfo();
\r
3325 texture_hdc = CreateCompatibleDC( hdc );
\r
3328 for (row = 0; row < BOARD_HEIGHT; row++) {
\r
3329 for (column = 0; column < BOARD_WIDTH; column++) {
\r
3331 SquareToPos(row, column, &x, &y);
\r
3333 piece = board[row][column];
\r
3335 square_color = ((column + row) % 2) == 1;
\r
3336 if( gameInfo.variant == VariantXiangqi ) {
\r
3337 square_color = !InPalace(row, column);
\r
3338 if(BOARD_HEIGHT&1) { if(row==BOARD_HEIGHT/2) square_color ^= 1; }
\r
3339 else if(row < BOARD_HEIGHT/2) square_color ^= 1;
\r
3341 piece_color = (int) piece < (int) BlackPawn;
\r
3344 /* [HGM] holdings file: light square or black */
\r
3345 if(column == BOARD_LEFT-2) {
\r
3346 if( row > BOARD_HEIGHT - gameInfo.holdingsSize - 1 )
\r
3349 DisplayHoldingsCount(hdc, x, y, 0, 0); /* black out */
\r
3353 if(column == BOARD_RGHT + 1 ) {
\r
3354 if( row < gameInfo.holdingsSize )
\r
3357 DisplayHoldingsCount(hdc, x, y, 0, 0);
\r
3361 if(column == BOARD_LEFT-1 ) /* left align */
\r
3362 DisplayHoldingsCount(hdc, x, y, flipView, (int) board[row][column]);
\r
3363 else if( column == BOARD_RGHT) /* right align */
\r
3364 DisplayHoldingsCount(hdc, x, y, !flipView, (int) board[row][column]);
\r
3366 if (appData.monoMode) {
\r
3367 if (piece == EmptySquare) {
\r
3368 BitBlt(hdc, x, y, squareSize, squareSize, 0, 0, 0,
\r
3369 square_color ? WHITENESS : BLACKNESS);
\r
3371 DrawPieceOnDC(hdc, piece, piece_color, square_color, x, y, tmphdc);
\r
3374 else if( appData.useBitmaps && backTextureSquareInfo[row][column].mode > 0 ) {
\r
3375 /* [AS] Draw the square using a texture bitmap */
\r
3376 HBITMAP hbm = SelectObject( texture_hdc, square_color ? liteBackTexture : darkBackTexture );
\r
3377 int r = row, c = column; // [HGM] do not flip board in flipView
\r
3378 if(flipView) { r = BOARD_HEIGHT-1 - r; c = BOARD_WIDTH-1 - c; }
\r
3381 squareSize, squareSize,
\r
3384 backTextureSquareInfo[r][c].mode,
\r
3385 backTextureSquareInfo[r][c].x,
\r
3386 backTextureSquareInfo[r][c].y );
\r
3388 SelectObject( texture_hdc, hbm );
\r
3390 if (piece != EmptySquare) {
\r
3391 DrawPieceOnDC(hdc, piece, piece_color, -1, x, y, tmphdc);
\r
3395 HBRUSH brush = square_color ? lightSquareBrush : darkSquareBrush;
\r
3397 oldBrush = SelectObject(hdc, brush );
\r
3398 BitBlt(hdc, x, y, squareSize, squareSize, 0, 0, 0, PATCOPY);
\r
3399 SelectObject(hdc, oldBrush);
\r
3400 if (piece != EmptySquare)
\r
3401 DrawPieceOnDC(hdc, piece, piece_color, -1, x, y, tmphdc);
\r
3406 if( texture_hdc != NULL ) {
\r
3407 DeleteDC( texture_hdc );
\r
3411 int saveDiagFlag = 0; FILE *diagFile; // [HGM] diag
\r
3412 void fputDW(FILE *f, int x)
\r
3414 fputc(x & 255, f);
\r
3415 fputc(x>>8 & 255, f);
\r
3416 fputc(x>>16 & 255, f);
\r
3417 fputc(x>>24 & 255, f);
\r
3420 #define MAX_CLIPS 200 /* more than enough */
\r
3423 DrawLogoOnDC(HDC hdc, RECT logoRect, HBITMAP logo)
\r
3425 // HBITMAP bufferBitmap;
\r
3430 int w = 100, h = 50;
\r
3432 if(logo == NULL) {
\r
3433 if(!logoHeight) return;
\r
3434 FillRect( hdc, &logoRect, whitePieceBrush );
\r
3436 // GetClientRect(hwndMain, &Rect);
\r
3437 // bufferBitmap = CreateCompatibleBitmap(hdc, Rect.right-Rect.left+1,
\r
3438 // Rect.bottom-Rect.top+1);
\r
3439 tmphdc = CreateCompatibleDC(hdc);
\r
3440 hbm = SelectObject(tmphdc, logo);
\r
3441 if( GetObject( logo, sizeof(bi), &bi ) > 0 ) {
\r
3445 StretchBlt(hdc, logoRect.left, logoRect.top, logoRect.right - logoRect.left,
\r
3446 logoRect.bottom - logoRect.top, tmphdc, 0, 0, w, h, SRCCOPY);
\r
3447 SelectObject(tmphdc, hbm);
\r
3455 HDC hdc = GetDC(hwndMain);
\r
3456 HBITMAP whiteLogo = (HBITMAP) first.programLogo, blackLogo = (HBITMAP) second.programLogo;
\r
3457 if(appData.autoLogo) {
\r
3459 switch(gameMode) { // pick logos based on game mode
\r
3460 case IcsObserving:
\r
3461 whiteLogo = second.programLogo; // ICS logo
\r
3462 blackLogo = second.programLogo;
\r
3465 case IcsPlayingWhite:
\r
3466 if(!appData.zippyPlay) whiteLogo = userLogo;
\r
3467 blackLogo = second.programLogo; // ICS logo
\r
3469 case IcsPlayingBlack:
\r
3470 whiteLogo = second.programLogo; // ICS logo
\r
3471 blackLogo = appData.zippyPlay ? first.programLogo : userLogo;
\r
3473 case TwoMachinesPlay:
\r
3474 if(first.twoMachinesColor[0] == 'b') {
\r
3475 whiteLogo = second.programLogo;
\r
3476 blackLogo = first.programLogo;
\r
3479 case MachinePlaysWhite:
\r
3480 blackLogo = userLogo;
\r
3482 case MachinePlaysBlack:
\r
3483 whiteLogo = userLogo;
\r
3484 blackLogo = first.programLogo;
\r
3487 DrawLogoOnDC(hdc, leftLogoRect, flipClock ? blackLogo : whiteLogo);
\r
3488 DrawLogoOnDC(hdc, rightLogoRect, flipClock ? whiteLogo : blackLogo);
\r
3489 ReleaseDC(hwndMain, hdc);
\r
3494 UpdateLogos(int display)
\r
3495 { // called after loading new engine(s), in tourney or from menu
\r
3496 LoadLogo(&first, 0, FALSE);
\r
3497 LoadLogo(&second, 1, appData.icsActive);
\r
3498 InitDrawingSizes(-2, 0); // adapt layout of board window to presence/absence of logos
\r
3499 if(display) DisplayLogos();
\r
3502 static HDC hdcSeek;
\r
3504 // [HGM] seekgraph
\r
3505 void DrawSeekAxis( int x, int y, int xTo, int yTo )
\r
3508 HPEN hp = SelectObject( hdcSeek, gridPen );
\r
3509 MoveToEx( hdcSeek, boardRect.left+x, boardRect.top+y, &stPt );
\r
3510 LineTo( hdcSeek, boardRect.left+xTo, boardRect.top+yTo );
\r
3511 SelectObject( hdcSeek, hp );
\r
3514 // front-end wrapper for drawing functions to do rectangles
\r
3515 void DrawSeekBackground( int left, int top, int right, int bottom )
\r
3520 if (hdcSeek == NULL) {
\r
3521 hdcSeek = GetDC(hwndMain);
\r
3522 if (!appData.monoMode) {
\r
3523 SelectPalette(hdcSeek, hPal, FALSE);
\r
3524 RealizePalette(hdcSeek);
\r
3527 hp = SelectObject( hdcSeek, gridPen );
\r
3528 rc.top = boardRect.top+top; rc.left = boardRect.left+left;
\r
3529 rc.bottom = boardRect.top+bottom; rc.right = boardRect.left+right;
\r
3530 FillRect( hdcSeek, &rc, lightSquareBrush );
\r
3531 SelectObject( hdcSeek, hp );
\r
3534 // front-end wrapper for putting text in graph
\r
3535 void DrawSeekText(char *buf, int x, int y)
\r
3538 SetBkMode( hdcSeek, TRANSPARENT );
\r
3539 GetTextExtentPoint32( hdcSeek, buf, strlen(buf), &stSize );
\r
3540 TextOut( hdcSeek, boardRect.left+x-3, boardRect.top+y-stSize.cy/2, buf, strlen(buf) );
\r
3543 void DrawSeekDot(int x, int y, int color)
\r
3545 int square = color & 0x80;
\r
3546 HBRUSH oldBrush = SelectObject(hdcSeek,
\r
3547 color == 0 ? markerBrush : color == 1 ? darkSquareBrush : explodeBrush);
\r
3550 Rectangle(hdcSeek, boardRect.left+x - squareSize/9, boardRect.top+y - squareSize/9,
\r
3551 boardRect.left+x + squareSize/9, boardRect.top+y + squareSize/9);
\r
3553 Ellipse(hdcSeek, boardRect.left+x - squareSize/8, boardRect.top+y - squareSize/8,
\r
3554 boardRect.left+x + squareSize/8, boardRect.top+y + squareSize/8);
\r
3555 SelectObject(hdcSeek, oldBrush);
\r
3559 HDCDrawPosition(HDC hdc, BOOLEAN repaint, Board board)
\r
3561 static Board lastReq[2], lastDrawn[2];
\r
3562 static HighlightInfo lastDrawnHighlight, lastDrawnPremove;
\r
3563 static int lastDrawnFlipView = 0;
\r
3564 static int lastReqValid[2] = {0, 0}, lastDrawnValid[2] = {0, 0};
\r
3565 int releaseDC, x, y, x2, y2, row, column, num_clips = 0, i;
\r
3568 HBITMAP bufferBitmap;
\r
3569 HBITMAP oldBitmap;
\r
3571 HRGN clips[MAX_CLIPS];
\r
3572 ChessSquare dragged_piece = EmptySquare;
\r
3573 int nr = twoBoards*partnerUp;
\r
3575 /* I'm undecided on this - this function figures out whether a full
\r
3576 * repaint is necessary on its own, so there's no real reason to have the
\r
3577 * caller tell it that. I think this can safely be set to FALSE - but
\r
3578 * if we trust the callers not to request full repaints unnessesarily, then
\r
3579 * we could skip some clipping work. In other words, only request a full
\r
3580 * redraw when the majority of pieces have changed positions (ie. flip,
\r
3581 * gamestart and similar) --Hawk
\r
3583 Boolean fullrepaint = repaint;
\r
3585 if(DrawSeekGraph()) return; // [HG} seekgraph: suppress printing board if seek graph up
\r
3587 if( DrawPositionNeedsFullRepaint() ) {
\r
3588 fullrepaint = TRUE;
\r
3591 if (board == NULL) {
\r
3592 if (!lastReqValid[nr]) {
\r
3595 board = lastReq[nr];
\r
3597 CopyBoard(lastReq[nr], board);
\r
3598 lastReqValid[nr] = 1;
\r
3601 if (doingSizing) {
\r
3605 if (IsIconic(hwndMain)) {
\r
3609 if (hdc == NULL) {
\r
3610 hdc = GetDC(hwndMain);
\r
3611 if (!appData.monoMode) {
\r
3612 SelectPalette(hdc, hPal, FALSE);
\r
3613 RealizePalette(hdc);
\r
3617 releaseDC = FALSE;
\r
3620 /* Create some work-DCs */
\r
3621 hdcmem = CreateCompatibleDC(hdc);
\r
3622 tmphdc = CreateCompatibleDC(hdc);
\r
3624 /* If dragging is in progress, we temporarely remove the piece */
\r
3625 /* [HGM] or temporarily decrease count if stacked */
\r
3626 /* !! Moved to before board compare !! */
\r
3627 if (dragInfo.from.x >= 0 && dragInfo.pos.x >= 0) {
\r
3628 dragged_piece = board[dragInfo.from.y][dragInfo.from.x];
\r
3629 if(dragInfo.from.x == BOARD_LEFT-2 ) {
\r
3630 if(--board[dragInfo.from.y][dragInfo.from.x+1] == 0 )
\r
3631 board[dragInfo.from.y][dragInfo.from.x] = EmptySquare;
\r
3633 if(dragInfo.from.x == BOARD_RGHT+1) {
\r
3634 if(--board[dragInfo.from.y][dragInfo.from.x-1] == 0 )
\r
3635 board[dragInfo.from.y][dragInfo.from.x] = EmptySquare;
\r
3637 board[dragInfo.from.y][dragInfo.from.x] = gatingPiece;
\r
3640 /* Figure out which squares need updating by comparing the
\r
3641 * newest board with the last drawn board and checking if
\r
3642 * flipping has changed.
\r
3644 if (!fullrepaint && lastDrawnValid[nr] && (nr == 1 || lastDrawnFlipView == flipView)) {
\r
3645 for (row = 0; row < BOARD_HEIGHT; row++) { /* [HGM] true size, not 8 */
\r
3646 for (column = 0; column < BOARD_WIDTH; column++) {
\r
3647 if (lastDrawn[nr][row][column] != board[row][column]) {
\r
3648 SquareToPos(row, column, &x, &y);
\r
3649 clips[num_clips++] =
\r
3650 CreateRectRgn(x, y, x + squareSize, y + squareSize);
\r
3654 if(nr == 0) { // [HGM] dual: no highlights on second board
\r
3655 for (i=0; i<2; i++) {
\r
3656 if (lastDrawnHighlight.sq[i].x != highlightInfo.sq[i].x ||
\r
3657 lastDrawnHighlight.sq[i].y != highlightInfo.sq[i].y) {
\r
3658 if (lastDrawnHighlight.sq[i].x >= 0 &&
\r
3659 lastDrawnHighlight.sq[i].y >= 0) {
\r
3660 SquareToPos(lastDrawnHighlight.sq[i].y,
\r
3661 lastDrawnHighlight.sq[i].x, &x, &y);
\r
3662 clips[num_clips++] =
\r
3663 CreateRectRgn(x - lineGap, y - lineGap,
\r
3664 x + squareSize + lineGap, y + squareSize + lineGap);
\r
3666 if (highlightInfo.sq[i].x >= 0 && highlightInfo.sq[i].y >= 0) {
\r
3667 SquareToPos(highlightInfo.sq[i].y, highlightInfo.sq[i].x, &x, &y);
\r
3668 clips[num_clips++] =
\r
3669 CreateRectRgn(x - lineGap, y - lineGap,
\r
3670 x + squareSize + lineGap, y + squareSize + lineGap);
\r
3674 for (i=0; i<2; i++) {
\r
3675 if (lastDrawnPremove.sq[i].x != premoveHighlightInfo.sq[i].x ||
\r
3676 lastDrawnPremove.sq[i].y != premoveHighlightInfo.sq[i].y) {
\r
3677 if (lastDrawnPremove.sq[i].x >= 0 &&
\r
3678 lastDrawnPremove.sq[i].y >= 0) {
\r
3679 SquareToPos(lastDrawnPremove.sq[i].y,
\r
3680 lastDrawnPremove.sq[i].x, &x, &y);
\r
3681 clips[num_clips++] =
\r
3682 CreateRectRgn(x - lineGap, y - lineGap,
\r
3683 x + squareSize + lineGap, y + squareSize + lineGap);
\r
3685 if (premoveHighlightInfo.sq[i].x >= 0 &&
\r
3686 premoveHighlightInfo.sq[i].y >= 0) {
\r
3687 SquareToPos(premoveHighlightInfo.sq[i].y,
\r
3688 premoveHighlightInfo.sq[i].x, &x, &y);
\r
3689 clips[num_clips++] =
\r
3690 CreateRectRgn(x - lineGap, y - lineGap,
\r
3691 x + squareSize + lineGap, y + squareSize + lineGap);
\r
3695 } else { // nr == 1
\r
3696 partnerHighlightInfo.sq[0].y = board[EP_STATUS-4];
\r
3697 partnerHighlightInfo.sq[0].x = board[EP_STATUS-3];
\r
3698 partnerHighlightInfo.sq[1].y = board[EP_STATUS-2];
\r
3699 partnerHighlightInfo.sq[1].x = board[EP_STATUS-1];
\r
3700 for (i=0; i<2; i++) {
\r
3701 if (partnerHighlightInfo.sq[i].x >= 0 &&
\r
3702 partnerHighlightInfo.sq[i].y >= 0) {
\r
3703 SquareToPos(partnerHighlightInfo.sq[i].y,
\r
3704 partnerHighlightInfo.sq[i].x, &x, &y);
\r
3705 clips[num_clips++] =
\r
3706 CreateRectRgn(x - lineGap, y - lineGap,
\r
3707 x + squareSize + lineGap, y + squareSize + lineGap);
\r
3709 if (oldPartnerHighlight.sq[i].x >= 0 &&
\r
3710 oldPartnerHighlight.sq[i].y >= 0) {
\r
3711 SquareToPos(oldPartnerHighlight.sq[i].y,
\r
3712 oldPartnerHighlight.sq[i].x, &x, &y);
\r
3713 clips[num_clips++] =
\r
3714 CreateRectRgn(x - lineGap, y - lineGap,
\r
3715 x + squareSize + lineGap, y + squareSize + lineGap);
\r
3720 fullrepaint = TRUE;
\r
3723 /* Create a buffer bitmap - this is the actual bitmap
\r
3724 * being written to. When all the work is done, we can
\r
3725 * copy it to the real DC (the screen). This avoids
\r
3726 * the problems with flickering.
\r
3728 GetClientRect(hwndMain, &Rect);
\r
3729 bufferBitmap = CreateCompatibleBitmap(hdc, Rect.right-Rect.left+1,
\r
3730 Rect.bottom-Rect.top+1);
\r
3731 oldBitmap = SelectObject(hdcmem, bufferBitmap);
\r
3732 if (!appData.monoMode) {
\r
3733 SelectPalette(hdcmem, hPal, FALSE);
\r
3736 /* Create clips for dragging */
\r
3737 if (!fullrepaint) {
\r
3738 if (dragInfo.from.x >= 0) {
\r
3739 SquareToPos(dragInfo.from.y, dragInfo.from.x, &x, &y);
\r
3740 clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);
\r
3742 if (dragInfo.start.x >= 0) {
\r
3743 SquareToPos(dragInfo.start.y, dragInfo.start.x, &x, &y);
\r
3744 clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);
\r
3746 if (dragInfo.pos.x >= 0) {
\r
3747 x = dragInfo.pos.x - squareSize / 2;
\r
3748 y = dragInfo.pos.y - squareSize / 2;
\r
3749 clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);
\r
3751 if (dragInfo.lastpos.x >= 0) {
\r
3752 x = dragInfo.lastpos.x - squareSize / 2;
\r
3753 y = dragInfo.lastpos.y - squareSize / 2;
\r
3754 clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);
\r
3758 /* Are we animating a move?
\r
3760 * - remove the piece from the board (temporarely)
\r
3761 * - calculate the clipping region
\r
3763 if (!fullrepaint) {
\r
3764 if (animInfo.piece != EmptySquare) {
\r
3765 board[animInfo.from.y][animInfo.from.x] = EmptySquare;
\r
3766 x = boardRect.left + animInfo.lastpos.x;
\r
3767 y = boardRect.top + animInfo.lastpos.y;
\r
3768 x2 = boardRect.left + animInfo.pos.x;
\r
3769 y2 = boardRect.top + animInfo.pos.y;
\r
3770 clips[num_clips++] = CreateRectRgn(MIN(x,x2), MIN(y,y2), MAX(x,x2)+squareSize, MAX(y,y2)+squareSize);
\r
3771 /* Slight kludge. The real problem is that after AnimateMove is
\r
3772 done, the position on the screen does not match lastDrawn.
\r
3773 This currently causes trouble only on e.p. captures in
\r
3774 atomic, where the piece moves to an empty square and then
\r
3775 explodes. The old and new positions both had an empty square
\r
3776 at the destination, but animation has drawn a piece there and
\r
3777 we have to remember to erase it. [HGM] moved until after setting lastDrawn */
\r
3778 lastDrawn[0][animInfo.to.y][animInfo.to.x] = animInfo.piece;
\r
3782 /* No clips? Make sure we have fullrepaint set to TRUE */
\r
3783 if (num_clips == 0)
\r
3784 fullrepaint = TRUE;
\r
3786 /* Set clipping on the memory DC */
\r
3787 if (!fullrepaint) {
\r
3788 SelectClipRgn(hdcmem, clips[0]);
\r
3789 for (x = 1; x < num_clips; x++) {
\r
3790 if (ExtSelectClipRgn(hdcmem, clips[x], RGN_OR) == ERROR)
\r
3791 abort(); // this should never ever happen!
\r
3795 /* Do all the drawing to the memory DC */
\r
3796 if(explodeInfo.radius) { // [HGM] atomic
\r
3798 int x, y, r=(explodeInfo.radius * squareSize)/100;
\r
3799 ChessSquare piece = board[explodeInfo.fromY][explodeInfo.fromX];
\r
3800 board[explodeInfo.fromY][explodeInfo.fromX] = EmptySquare; // suppress display of capturer
\r
3801 SquareToPos(explodeInfo.toY, explodeInfo.toX, &x, &y);
\r
3802 x += squareSize/2;
\r
3803 y += squareSize/2;
\r
3804 if(!fullrepaint) {
\r
3805 clips[num_clips] = CreateRectRgn(x-r, y-r, x+r, y+r);
\r
3806 ExtSelectClipRgn(hdcmem, clips[num_clips++], RGN_OR);
\r
3808 DrawGridOnDC(hdcmem);
\r
3809 DrawHighlightsOnDC(hdcmem, &highlightInfo, HIGHLIGHT_PEN);
\r
3810 DrawHighlightsOnDC(hdcmem, &premoveHighlightInfo, PREMOVE_PEN);
\r
3811 DrawBoardOnDC(hdcmem, board, tmphdc);
\r
3812 board[explodeInfo.fromY][explodeInfo.fromX] = piece;
\r
3813 oldBrush = SelectObject(hdcmem, explodeBrush);
\r
3814 Ellipse(hdcmem, x-r, y-r, x+r, y+r);
\r
3815 SelectObject(hdcmem, oldBrush);
\r
3817 DrawGridOnDC(hdcmem);
\r
3818 if(nr == 0) { // [HGM] dual: decide which highlights to draw
\r
3819 DrawHighlightsOnDC(hdcmem, &highlightInfo, HIGHLIGHT_PEN);
\r
3820 DrawHighlightsOnDC(hdcmem, &premoveHighlightInfo, PREMOVE_PEN);
\r
3822 DrawHighlightsOnDC(hdcmem, &partnerHighlightInfo, HIGHLIGHT_PEN);
\r
3823 oldPartnerHighlight = partnerHighlightInfo;
\r
3825 DrawBoardOnDC(hdcmem, board, tmphdc);
\r
3827 if(nr == 0) // [HGM] dual: markers only on left board
\r
3828 for (row = 0; row < BOARD_HEIGHT; row++) {
\r
3829 for (column = 0; column < BOARD_WIDTH; column++) {
\r
3830 if (marker[row][column]) { // marker changes only occur with full repaint!
\r
3831 HBRUSH oldBrush = SelectObject(hdcmem,
\r
3832 marker[row][column] == 2 ? markerBrush : explodeBrush);
\r
3833 SquareToPos(row, column, &x, &y);
\r
3834 Ellipse(hdcmem, x + squareSize/4, y + squareSize/4,
\r
3835 x + 3*squareSize/4, y + 3*squareSize/4);
\r
3836 SelectObject(hdcmem, oldBrush);
\r
3841 if( appData.highlightMoveWithArrow ) {
\r
3842 DrawArrowHighlight(hdcmem);
\r
3845 DrawCoordsOnDC(hdcmem);
\r
3847 CopyBoard(lastDrawn[nr], board); /* [HGM] Moved to here from end of routine, */
\r
3848 /* to make sure lastDrawn contains what is actually drawn */
\r
3850 /* Put the dragged piece back into place and draw it (out of place!) */
\r
3851 if (dragged_piece != EmptySquare) {
\r
3852 /* [HGM] or restack */
\r
3853 if(dragInfo.from.x == BOARD_LEFT-2 )
\r
3854 board[dragInfo.from.y][dragInfo.from.x+1]++;
\r
3856 if(dragInfo.from.x == BOARD_RGHT+1 )
\r
3857 board[dragInfo.from.y][dragInfo.from.x-1]++;
\r
3858 board[dragInfo.from.y][dragInfo.from.x] = dragged_piece;
\r
3859 x = dragInfo.pos.x - squareSize / 2;
\r
3860 y = dragInfo.pos.y - squareSize / 2;
\r
3861 DrawPieceOnDC(hdcmem, dragInfo.piece,
\r
3862 ((int) dragInfo.piece < (int) BlackPawn),
\r
3863 (dragInfo.from.y + dragInfo.from.x) % 2, x, y, tmphdc);
\r
3866 /* Put the animated piece back into place and draw it */
\r
3867 if (animInfo.piece != EmptySquare) {
\r
3868 board[animInfo.from.y][animInfo.from.x] = animInfo.piece;
\r
3869 x = boardRect.left + animInfo.pos.x;
\r
3870 y = boardRect.top + animInfo.pos.y;
\r
3871 DrawPieceOnDC(hdcmem, animInfo.piece,
\r
3872 ((int) animInfo.piece < (int) BlackPawn),
\r
3873 (animInfo.from.y + animInfo.from.x) % 2, x, y, tmphdc);
\r
3876 /* Release the bufferBitmap by selecting in the old bitmap
\r
3877 * and delete the memory DC
\r
3879 SelectObject(hdcmem, oldBitmap);
\r
3882 /* Set clipping on the target DC */
\r
3883 if (!fullrepaint) {
\r
3884 if(nr == 1) for (x = 0; x < num_clips; x++) { // [HGM] dual: translate clips
\r
3886 GetRgnBox(clips[x], &rect);
\r
3887 DeleteObject(clips[x]);
\r
3888 clips[x] = CreateRectRgn(rect.left + wpMain.width/2, rect.top,
\r
3889 rect.right + wpMain.width/2, rect.bottom);
\r
3891 SelectClipRgn(hdc, clips[0]);
\r
3892 for (x = 1; x < num_clips; x++) {
\r
3893 if (ExtSelectClipRgn(hdc, clips[x], RGN_OR) == ERROR)
\r
3894 abort(); // this should never ever happen!
\r
3898 /* Copy the new bitmap onto the screen in one go.
\r
3899 * This way we avoid any flickering
\r
3901 oldBitmap = SelectObject(tmphdc, bufferBitmap);
\r
3902 BitBlt(hdc, boardRect.left + twoBoards*partnerUp*wpMain.width/2, boardRect.top, // [HGM] dual
\r
3903 boardRect.right - boardRect.left,
\r
3904 boardRect.bottom - boardRect.top,
\r
3905 tmphdc, boardRect.left, boardRect.top, SRCCOPY);
\r
3906 if(saveDiagFlag) {
\r
3907 BITMAP b; int i, j=0, m, w, wb, fac=0; char *pData;
\r
3908 BITMAPINFOHEADER bih; int color[16], nrColors=0;
\r
3910 GetObject(bufferBitmap, sizeof(b), &b);
\r
3911 if(pData = malloc(b.bmWidthBytes*b.bmHeight + 10000)) {
\r
3912 bih.biSize = sizeof(BITMAPINFOHEADER);
\r
3913 bih.biWidth = b.bmWidth;
\r
3914 bih.biHeight = b.bmHeight;
\r
3916 bih.biBitCount = b.bmBitsPixel;
\r
3917 bih.biCompression = 0;
\r
3918 bih.biSizeImage = b.bmWidthBytes*b.bmHeight;
\r
3919 bih.biXPelsPerMeter = 0;
\r
3920 bih.biYPelsPerMeter = 0;
\r
3921 bih.biClrUsed = 0;
\r
3922 bih.biClrImportant = 0;
\r
3923 // fprintf(diagFile, "t=%d\nw=%d\nh=%d\nB=%d\nP=%d\nX=%d\n",
\r
3924 // b.bmType, b.bmWidth, b.bmHeight, b.bmWidthBytes, b.bmPlanes, b.bmBitsPixel);
\r
3925 GetDIBits(tmphdc,bufferBitmap,0,b.bmHeight,pData,(BITMAPINFO*)&bih,DIB_RGB_COLORS);
\r
3926 // fprintf(diagFile, "%8x\n", (int) pData);
\r
3928 wb = b.bmWidthBytes;
\r
3930 for(i=0; i<wb*(b.bmHeight - boardRect.top + OUTER_MARGIN)>>2; i++) {
\r
3931 int k = ((int*) pData)[i];
\r
3932 for(j=0; j<nrColors; j++) if(color[j] == k) break;
\r
3933 if(j >= 16) break;
\r
3935 if(j >= nrColors) nrColors = j+1;
\r
3937 if(j<16) { // 16 colors is enough. Compress to 4 bits per pixel
\r
3939 for(i=0; i<b.bmHeight - boardRect.top + OUTER_MARGIN; i++) {
\r
3940 for(w=0; w<(wb>>2); w+=2) {
\r
3941 int k = ((int*) pData)[(wb*i>>2) + w];
\r
3942 for(j=0; j<nrColors; j++) if(color[j] == k) break;
\r
3943 k = ((int*) pData)[(wb*i>>2) + w + 1];
\r
3944 for(m=0; m<nrColors; m++) if(color[m] == k) break;
\r
3945 pData[p++] = m | j<<4;
\r
3947 while(p&3) pData[p++] = 0;
\r
3950 wb = ((wb+31)>>5)<<2;
\r
3952 // write BITMAPFILEHEADER
\r
3953 fprintf(diagFile, "BM");
\r
3954 fputDW(diagFile, wb*(b.bmHeight - boardRect.top + OUTER_MARGIN)+0x36 + (fac?64:0));
\r
3955 fputDW(diagFile, 0);
\r
3956 fputDW(diagFile, 0x36 + (fac?64:0));
\r
3957 // write BITMAPINFOHEADER
\r
3958 fputDW(diagFile, 40);
\r
3959 fputDW(diagFile, b.bmWidth);
\r
3960 fputDW(diagFile, b.bmHeight - boardRect.top + OUTER_MARGIN);
\r
3961 if(fac) fputDW(diagFile, 0x040001); // planes and bits/pixel
\r
3962 else fputDW(diagFile, 0x200001); // planes and bits/pixel
\r
3963 fputDW(diagFile, 0);
\r
3964 fputDW(diagFile, 0);
\r
3965 fputDW(diagFile, 0);
\r
3966 fputDW(diagFile, 0);
\r
3967 fputDW(diagFile, 0);
\r
3968 fputDW(diagFile, 0);
\r
3969 // write color table
\r
3971 for(i=0; i<16; i++) fputDW(diagFile, color[i]);
\r
3972 // write bitmap data
\r
3973 for(i=0; i<wb*(b.bmHeight - boardRect.top + OUTER_MARGIN); i++)
\r
3974 fputc(pData[i], diagFile);
\r
3979 SelectObject(tmphdc, oldBitmap);
\r
3981 /* Massive cleanup */
\r
3982 for (x = 0; x < num_clips; x++)
\r
3983 DeleteObject(clips[x]);
\r
3986 DeleteObject(bufferBitmap);
\r
3989 ReleaseDC(hwndMain, hdc);
\r
3991 if (lastDrawnFlipView != flipView && nr == 0) {
\r
3993 CheckMenuItem(GetMenu(hwndMain),IDM_FlipView, MF_BYCOMMAND|MF_CHECKED);
\r
3995 CheckMenuItem(GetMenu(hwndMain),IDM_FlipView, MF_BYCOMMAND|MF_UNCHECKED);
\r
3998 /* CopyBoard(lastDrawn, board);*/
\r
3999 lastDrawnHighlight = highlightInfo;
\r
4000 lastDrawnPremove = premoveHighlightInfo;
\r
4001 lastDrawnFlipView = flipView;
\r
4002 lastDrawnValid[nr] = 1;
\r
4005 /* [HGM] diag: Save the current board display to the given open file and close the file */
\r
4010 saveDiagFlag = 1; diagFile = f;
\r
4011 HDCDrawPosition(NULL, TRUE, NULL);
\r
4019 /*---------------------------------------------------------------------------*\
\r
4020 | CLIENT PAINT PROCEDURE
\r
4021 | This is the main event-handler for the WM_PAINT message.
\r
4023 \*---------------------------------------------------------------------------*/
\r
4025 PaintProc(HWND hwnd)
\r
4031 if((hdc = BeginPaint(hwnd, &ps))) {
\r
4032 if (IsIconic(hwnd)) {
\r
4033 DrawIcon(hdc, 2, 2, iconCurrent);
\r
4035 if (!appData.monoMode) {
\r
4036 SelectPalette(hdc, hPal, FALSE);
\r
4037 RealizePalette(hdc);
\r
4039 HDCDrawPosition(hdc, 1, NULL);
\r
4040 if(twoBoards) { // [HGM] dual: also redraw other board in other orientation
\r
4041 flipView = !flipView; partnerUp = !partnerUp;
\r
4042 HDCDrawPosition(hdc, 1, NULL);
\r
4043 flipView = !flipView; partnerUp = !partnerUp;
\r
4046 SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);
\r
4047 ExtTextOut(hdc, messageRect.left, messageRect.top,
\r
4048 ETO_CLIPPED|ETO_OPAQUE,
\r
4049 &messageRect, messageText, strlen(messageText), NULL);
\r
4050 SelectObject(hdc, oldFont);
\r
4051 DisplayBothClocks();
\r
4054 EndPaint(hwnd,&ps);
\r
4062 * If the user selects on a border boundary, return -1; if off the board,
\r
4063 * return -2. Otherwise map the event coordinate to the square.
\r
4064 * The offset boardRect.left or boardRect.top must already have been
\r
4065 * subtracted from x.
\r
4067 int EventToSquare(x, limit)
\r
4075 if ((x % (squareSize + lineGap)) >= squareSize)
\r
4077 x /= (squareSize + lineGap);
\r
4089 DropEnable dropEnables[] = {
\r
4090 { 'P', DP_Pawn, N_("Pawn") },
\r
4091 { 'N', DP_Knight, N_("Knight") },
\r
4092 { 'B', DP_Bishop, N_("Bishop") },
\r
4093 { 'R', DP_Rook, N_("Rook") },
\r
4094 { 'Q', DP_Queen, N_("Queen") },
\r
4098 SetupDropMenu(HMENU hmenu)
\r
4100 int i, count, enable;
\r
4102 extern char white_holding[], black_holding[];
\r
4103 char item[MSG_SIZ];
\r
4105 for (i=0; i<sizeof(dropEnables)/sizeof(DropEnable); i++) {
\r
4106 p = strchr(gameMode == IcsPlayingWhite ? white_holding : black_holding,
\r
4107 dropEnables[i].piece);
\r
4109 while (p && *p++ == dropEnables[i].piece) count++;
\r
4110 snprintf(item, MSG_SIZ, "%s %d", T_(dropEnables[i].name), count);
\r
4111 enable = count > 0 || !appData.testLegality
\r
4112 /*!!temp:*/ || (gameInfo.variant == VariantCrazyhouse
\r
4113 && !appData.icsActive);
\r
4114 ModifyMenu(hmenu, dropEnables[i].command,
\r
4115 MF_BYCOMMAND | (enable ? MF_ENABLED : MF_GRAYED) | MF_STRING,
\r
4116 dropEnables[i].command, item);
\r
4120 void DragPieceBegin(int x, int y, Boolean instantly)
\r
4122 dragInfo.lastpos.x = boardRect.left + x;
\r
4123 dragInfo.lastpos.y = boardRect.top + y;
\r
4124 if(instantly) dragInfo.pos = dragInfo.lastpos;
\r
4125 dragInfo.from.x = fromX;
\r
4126 dragInfo.from.y = fromY;
\r
4127 dragInfo.piece = boards[currentMove][fromY][fromX];
\r
4128 dragInfo.start = dragInfo.from;
\r
4129 SetCapture(hwndMain);
\r
4132 void DragPieceEnd(int x, int y)
\r
4135 dragInfo.start.x = dragInfo.start.y = -1;
\r
4136 dragInfo.from = dragInfo.start;
\r
4137 dragInfo.pos = dragInfo.lastpos = dragInfo.start;
\r
4140 void ChangeDragPiece(ChessSquare piece)
\r
4142 dragInfo.piece = piece;
\r
4145 /* Event handler for mouse messages */
\r
4147 MouseEvent(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
\r
4151 static int recursive = 0;
\r
4153 BOOLEAN forceFullRepaint = IsFullRepaintPreferrable(); /* [AS] */
\r
4156 if (message == WM_MBUTTONUP) {
\r
4157 /* Hideous kludge to fool TrackPopupMenu into paying attention
\r
4158 to the middle button: we simulate pressing the left button too!
\r
4160 PostMessage(hwnd, WM_LBUTTONDOWN, wParam, lParam);
\r
4161 PostMessage(hwnd, WM_LBUTTONUP, wParam, lParam);
\r
4167 pt.x = LOWORD(lParam);
\r
4168 pt.y = HIWORD(lParam);
\r
4169 x = EventToSquare(pt.x - boardRect.left, BOARD_WIDTH);
\r
4170 y = EventToSquare(pt.y - boardRect.top, BOARD_HEIGHT);
\r
4171 if (!flipView && y >= 0) {
\r
4172 y = BOARD_HEIGHT - 1 - y;
\r
4174 if (flipView && x >= 0) {
\r
4175 x = BOARD_WIDTH - 1 - x;
\r
4178 shiftKey = GetKeyState(VK_SHIFT) < 0; // [HGM] remember last shift status
\r
4180 switch (message) {
\r
4181 case WM_LBUTTONDOWN:
\r
4182 if (PtInRect((LPRECT) &whiteRect, pt)) {
\r
4183 ClockClick(flipClock); break;
\r
4184 } else if (PtInRect((LPRECT) &blackRect, pt)) {
\r
4185 ClockClick(!flipClock); break;
\r
4187 dragInfo.start.x = dragInfo.start.y = -1;
\r
4188 dragInfo.from = dragInfo.start;
\r
4189 if(fromX == -1 && frozen) { // not sure where this is for
\r
4190 fromX = fromY = -1;
\r
4191 DrawPosition(forceFullRepaint || FALSE, NULL); /* [AS] */
\r
4194 LeftClick(Press, pt.x - boardRect.left, pt.y - boardRect.top);
\r
4195 DrawPosition(TRUE, NULL);
\r
4198 case WM_LBUTTONUP:
\r
4199 LeftClick(Release, pt.x - boardRect.left, pt.y - boardRect.top);
\r
4200 DrawPosition(TRUE, NULL);
\r
4203 case WM_MOUSEMOVE:
\r
4204 if(SeekGraphClick(Press, pt.x - boardRect.left, pt.y - boardRect.top, 1)) break;
\r
4205 if(PromoScroll(pt.x - boardRect.left, pt.y - boardRect.top)) break;
\r
4206 MovePV(pt.x - boardRect.left, pt.y - boardRect.top, boardRect.bottom - boardRect.top);
\r
4207 if ((appData.animateDragging || appData.highlightDragging)
\r
4208 && (wParam & MK_LBUTTON)
\r
4209 && dragInfo.from.x >= 0)
\r
4211 BOOL full_repaint = FALSE;
\r
4213 if (appData.animateDragging) {
\r
4214 dragInfo.pos = pt;
\r
4216 if (appData.highlightDragging) {
\r
4217 SetHighlights(fromX, fromY, x, y);
\r
4218 if( IsDrawArrowEnabled() && (x < 0 || x >= BOARD_WIDTH || y < 0 || y >= BOARD_HEIGHT) ) {
\r
4219 full_repaint = TRUE;
\r
4223 DrawPosition( full_repaint, NULL);
\r
4225 dragInfo.lastpos = dragInfo.pos;
\r
4229 case WM_MOUSEWHEEL: // [DM]
\r
4230 { static int lastDir = 0; // [HGM] build in some hysteresis to avoid spurious events
\r
4231 /* Mouse Wheel is being rolled forward
\r
4232 * Play moves forward
\r
4234 if((short)HIWORD(wParam) < 0 && currentMove < forwardMostMove)
\r
4235 { if(lastDir == 1) ForwardEvent(); else lastDir = 1; } // [HGM] suppress first event in direction
\r
4236 /* Mouse Wheel is being rolled backward
\r
4237 * Play moves backward
\r
4239 if((short)HIWORD(wParam) > 0 && currentMove > backwardMostMove)
\r
4240 { if(lastDir == -1) BackwardEvent(); else lastDir = -1; }
\r
4244 case WM_MBUTTONUP:
\r
4245 case WM_RBUTTONUP:
\r
4247 RightClick(Release, pt.x - boardRect.left, pt.y - boardRect.top, &fromX, &fromY);
\r
4250 case WM_MBUTTONDOWN:
\r
4251 case WM_RBUTTONDOWN:
\r
4254 fromX = fromY = -1;
\r
4255 dragInfo.pos.x = dragInfo.pos.y = -1;
\r
4256 dragInfo.start.x = dragInfo.start.y = -1;
\r
4257 dragInfo.from = dragInfo.start;
\r
4258 dragInfo.lastpos = dragInfo.pos;
\r
4259 if (appData.highlightDragging) {
\r
4260 ClearHighlights();
\r
4263 /* [HGM] right mouse button in clock area edit-game mode ups clock */
\r
4264 if (PtInRect((LPRECT) &whiteRect, pt)) {
\r
4265 if (GetKeyState(VK_SHIFT) < 0) AdjustClock(flipClock, 1);
\r
4266 } else if (PtInRect((LPRECT) &blackRect, pt)) {
\r
4267 if (GetKeyState(VK_SHIFT) < 0) AdjustClock(!flipClock, 1);
\r
4271 DrawPosition(TRUE, NULL);
\r
4273 menuNr = RightClick(Press, pt.x - boardRect.left, pt.y - boardRect.top, &fromX, &fromY);
\r
4276 if (message == WM_MBUTTONDOWN) {
\r
4277 buttonCount = 3; /* even if system didn't think so */
\r
4278 if (wParam & MK_SHIFT)
\r
4279 MenuPopup(hwnd, pt, LoadMenu(hInst, "BlackPieceMenu"), -1);
\r
4281 MenuPopup(hwnd, pt, LoadMenu(hInst, "WhitePieceMenu"), -1);
\r
4282 } else { /* message == WM_RBUTTONDOWN */
\r
4283 /* Just have one menu, on the right button. Windows users don't
\r
4284 think to try the middle one, and sometimes other software steals
\r
4285 it, or it doesn't really exist. */
\r
4286 if(gameInfo.variant != VariantShogi)
\r
4287 MenuPopup(hwnd, pt, LoadMenu(hInst, "PieceMenu"), -1);
\r
4289 MenuPopup(hwnd, pt, LoadMenu(hInst, "ShogiPieceMenu"), -1);
\r
4293 SetCapture(hwndMain);
\r
4296 hmenu = LoadMenu(hInst, "DropPieceMenu");
\r
4297 SetupDropMenu(hmenu);
\r
4298 MenuPopup(hwnd, pt, hmenu, -1);
\r
4308 /* Preprocess messages for buttons in main window */
\r
4310 ButtonProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
\r
4312 int id = GetWindowLongPtr(hwnd, GWLP_ID);
\r
4315 for (i=0; i<N_BUTTONS; i++) {
\r
4316 if (buttonDesc[i].id == id) break;
\r
4318 if (i == N_BUTTONS) return 0;
\r
4319 switch (message) {
\r
4324 dir = (wParam == VK_LEFT) ? -1 : 1;
\r
4325 SetFocus(buttonDesc[(i + dir + N_BUTTONS) % N_BUTTONS].hwnd);
\r
4332 SendMessage(hwndMain, WM_COMMAND, MAKEWPARAM(buttonDesc[i].id, 0), 0);
\r
4335 if (appData.icsActive && (isalpha((char)wParam) || wParam == '0')) {
\r
4336 // [HGM] movenum: only letters or leading zero should go to ICS input
\r
4337 HWND h = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
4338 if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);
\r
4340 SendMessage(h, WM_CHAR, wParam, lParam);
\r
4342 } else if (isalpha((char)wParam) || isdigit((char)wParam)){
\r
4343 TypeInEvent((char)wParam);
\r
4349 return CallWindowProc(buttonDesc[i].wndproc, hwnd, message, wParam, lParam);
\r
4352 /* Process messages for Promotion dialog box */
\r
4354 Promotion(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
4358 switch (message) {
\r
4359 case WM_INITDIALOG: /* message: initialize dialog box */
\r
4360 /* Center the dialog over the application window */
\r
4361 CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));
\r
4362 Translate(hDlg, DLG_PromotionKing);
\r
4363 ShowWindow(GetDlgItem(hDlg, PB_King),
\r
4364 (!appData.testLegality || gameInfo.variant == VariantSuicide ||
\r
4365 gameInfo.variant == VariantSpartan && !WhiteOnMove(currentMove) ||
\r
4366 gameInfo.variant == VariantGiveaway || gameInfo.variant == VariantSuper ) ?
\r
4367 SW_SHOW : SW_HIDE);
\r
4368 /* [HGM] Only allow C & A promotions if these pieces are defined */
\r
4369 ShowWindow(GetDlgItem(hDlg, PB_Archbishop),
\r
4370 ((PieceToChar(WhiteAngel) >= 'A' && WhiteOnMove(currentMove) &&
\r
4371 PieceToChar(WhiteAngel) != '~') ||
\r
4372 (PieceToChar(BlackAngel) >= 'A' && !WhiteOnMove(currentMove) &&
\r
4373 PieceToChar(BlackAngel) != '~') ) ?
\r
4374 SW_SHOW : SW_HIDE);
\r
4375 ShowWindow(GetDlgItem(hDlg, PB_Chancellor),
\r
4376 ((PieceToChar(WhiteMarshall) >= 'A' && WhiteOnMove(currentMove) &&
\r
4377 PieceToChar(WhiteMarshall) != '~') ||
\r
4378 (PieceToChar(BlackMarshall) >= 'A' && !WhiteOnMove(currentMove) &&
\r
4379 PieceToChar(BlackMarshall) != '~') ) ?
\r
4380 SW_SHOW : SW_HIDE);
\r
4381 /* [HGM] Hide B & R button in Shogi, use Q as promote, N as defer */
\r
4382 ShowWindow(GetDlgItem(hDlg, PB_Rook),
\r
4383 gameInfo.variant != VariantShogi ?
\r
4384 SW_SHOW : SW_HIDE);
\r
4385 ShowWindow(GetDlgItem(hDlg, PB_Bishop),
\r
4386 gameInfo.variant != VariantShogi ?
\r
4387 SW_SHOW : SW_HIDE);
\r
4388 if(gameInfo.variant == VariantShogi) {
\r
4389 SetDlgItemText(hDlg, PB_Queen, "YES");
\r
4390 SetDlgItemText(hDlg, PB_Knight, "NO");
\r
4391 SetWindowText(hDlg, "Promote?");
\r
4393 ShowWindow(GetDlgItem(hDlg, IDC_Centaur),
\r
4394 gameInfo.variant == VariantSuper ?
\r
4395 SW_SHOW : SW_HIDE);
\r
4398 case WM_COMMAND: /* message: received a command */
\r
4399 switch (LOWORD(wParam)) {
\r
4401 EndDialog(hDlg, TRUE); /* Exit the dialog */
\r
4402 ClearHighlights();
\r
4403 DrawPosition(FALSE, NULL);
\r
4406 promoChar = gameInfo.variant == VariantSuper ? PieceToChar(BlackSilver) : PieceToChar(BlackKing);
\r
4409 promoChar = gameInfo.variant == VariantShogi ? '+' : ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteQueen : BlackQueen));
\r
4412 promoChar = ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteRook : BlackRook));
\r
4413 if(gameInfo.variant == VariantSpartan && !WhiteOnMove(currentMove)) promoChar = PieceToChar(BlackDragon);
\r
4416 promoChar = ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteBishop : BlackBishop));
\r
4417 if(gameInfo.variant == VariantSpartan && !WhiteOnMove(currentMove)) promoChar = PieceToChar(BlackAlfil);
\r
4419 case PB_Chancellor:
\r
4420 promoChar = ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteMarshall : BlackMarshall));
\r
4422 case PB_Archbishop:
\r
4423 promoChar = ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteAngel : BlackAngel));
\r
4426 promoChar = gameInfo.variant == VariantShogi ? '=' : PieceToChar(WhiteOnMove(currentMove) ? WhiteKnight : BlackKnight);
\r
4431 if(promoChar == '.') return FALSE; // invalid piece chosen
\r
4432 EndDialog(hDlg, TRUE); /* Exit the dialog */
\r
4433 UserMoveEvent(fromX, fromY, toX, toY, promoChar);
\r
4434 fromX = fromY = -1;
\r
4435 if (!appData.highlightLastMove) {
\r
4436 ClearHighlights();
\r
4437 DrawPosition(FALSE, NULL);
\r
4444 /* Pop up promotion dialog */
\r
4446 PromotionPopup(HWND hwnd)
\r
4450 lpProc = MakeProcInstance((FARPROC)Promotion, hInst);
\r
4451 DialogBox(hInst, MAKEINTRESOURCE(DLG_PromotionKing),
\r
4452 hwnd, (DLGPROC)lpProc);
\r
4453 FreeProcInstance(lpProc);
\r
4459 DrawPosition(TRUE, NULL);
\r
4460 PromotionPopup(hwndMain);
\r
4463 /* Toggle ShowThinking */
\r
4465 ToggleShowThinking()
\r
4467 appData.showThinking = !appData.showThinking;
\r
4468 ShowThinkingEvent();
\r
4472 LoadGameDialog(HWND hwnd, char* title)
\r
4476 char fileTitle[MSG_SIZ];
\r
4477 f = OpenFileDialog(hwnd, "rb", "",
\r
4478 appData.oldSaveStyle ? "gam" : "pgn",
\r
4480 title, &number, fileTitle, NULL);
\r
4482 cmailMsgLoaded = FALSE;
\r
4483 if (number == 0) {
\r
4484 int error = GameListBuild(f);
\r
4486 DisplayError(_("Cannot build game list"), error);
\r
4487 } else if (!ListEmpty(&gameList) &&
\r
4488 ((ListGame *) gameList.tailPred)->number > 1) {
\r
4489 GameListPopUp(f, fileTitle);
\r
4492 GameListDestroy();
\r
4495 LoadGame(f, number, fileTitle, FALSE);
\r
4499 int get_term_width()
\r
4504 HFONT hfont, hold_font;
\r
4509 hText = GetDlgItem(hwndConsole, OPT_ConsoleText);
\r
4513 // get the text metrics
\r
4514 hdc = GetDC(hText);
\r
4515 lf = font[boardSize][CONSOLE_FONT]->lf;
\r
4516 if (consoleCF.dwEffects & CFE_BOLD)
\r
4517 lf.lfWeight = FW_BOLD;
\r
4518 if (consoleCF.dwEffects & CFE_ITALIC)
\r
4519 lf.lfItalic = TRUE;
\r
4520 if (consoleCF.dwEffects & CFE_STRIKEOUT)
\r
4521 lf.lfStrikeOut = TRUE;
\r
4522 if (consoleCF.dwEffects & CFE_UNDERLINE)
\r
4523 lf.lfUnderline = TRUE;
\r
4524 hfont = CreateFontIndirect(&lf);
\r
4525 hold_font = SelectObject(hdc, hfont);
\r
4526 GetTextMetrics(hdc, &tm);
\r
4527 SelectObject(hdc, hold_font);
\r
4528 DeleteObject(hfont);
\r
4529 ReleaseDC(hText, hdc);
\r
4531 // get the rectangle
\r
4532 SendMessage(hText, EM_GETRECT, 0, (LPARAM)&rc);
\r
4534 return (rc.right-rc.left) / tm.tmAveCharWidth;
\r
4537 void UpdateICSWidth(HWND hText)
\r
4539 LONG old_width, new_width;
\r
4541 new_width = get_term_width(hText, FALSE);
\r
4542 old_width = GetWindowLongPtr(hText, GWLP_USERDATA);
\r
4543 if (new_width != old_width)
\r
4545 ics_update_width(new_width);
\r
4546 SetWindowLongPtr(hText, GWLP_USERDATA, new_width);
\r
4551 ChangedConsoleFont()
\r
4554 CHARRANGE tmpsel, sel;
\r
4555 MyFont *f = font[boardSize][CONSOLE_FONT];
\r
4556 HWND hText = GetDlgItem(hwndConsole, OPT_ConsoleText);
\r
4557 HWND hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
4560 cfmt.cbSize = sizeof(CHARFORMAT);
\r
4561 cfmt.dwMask = CFM_FACE|CFM_SIZE|CFM_CHARSET;
\r
4562 safeStrCpy(cfmt.szFaceName, font[boardSize][CONSOLE_FONT]->mfp.faceName,
\r
4563 sizeof(cfmt.szFaceName)/sizeof(cfmt.szFaceName[0]) );
\r
4564 /* yHeight is expressed in twips. A twip is 1/20 of a font's point
\r
4565 * size. This was undocumented in the version of MSVC++ that I had
\r
4566 * when I wrote the code, but is apparently documented now.
\r
4568 cfmt.yHeight = (int)(f->mfp.pointSize * 20.0 + 0.5);
\r
4569 cfmt.bCharSet = f->lf.lfCharSet;
\r
4570 cfmt.bPitchAndFamily = f->lf.lfPitchAndFamily;
\r
4571 SendMessage(hText, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &cfmt);
\r
4572 SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &cfmt);
\r
4573 /* Why are the following seemingly needed too? */
\r
4574 SendMessage(hText, EM_SETCHARFORMAT, SCF_DEFAULT, (LPARAM) &cfmt);
\r
4575 SendMessage(hInput, EM_SETCHARFORMAT, SCF_DEFAULT, (LPARAM) &cfmt);
\r
4576 SendMessage(hText, EM_EXGETSEL, 0, (LPARAM)&sel);
\r
4578 tmpsel.cpMax = -1; /*999999?*/
\r
4579 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&tmpsel);
\r
4580 SendMessage(hText, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cfmt);
\r
4581 /* Trying putting this here too. It still seems to tickle a RichEdit
\r
4582 * bug: sometimes RichEdit indents the first line of a paragraph too.
\r
4584 paraf.cbSize = sizeof(paraf);
\r
4585 paraf.dwMask = PFM_OFFSET | PFM_STARTINDENT;
\r
4586 paraf.dxStartIndent = 0;
\r
4587 paraf.dxOffset = WRAP_INDENT;
\r
4588 SendMessage(hText, EM_SETPARAFORMAT, 0, (LPARAM) ¶f);
\r
4589 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
4590 UpdateICSWidth(hText);
\r
4593 /*---------------------------------------------------------------------------*\
\r
4595 * Window Proc for main window
\r
4597 \*---------------------------------------------------------------------------*/
\r
4599 /* Process messages for main window, etc. */
\r
4601 WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
\r
4604 int wmId, wmEvent;
\r
4608 char fileTitle[MSG_SIZ];
\r
4609 char buf[MSG_SIZ];
\r
4610 static SnapData sd;
\r
4611 static int peek=0;
\r
4613 switch (message) {
\r
4615 case WM_PAINT: /* message: repaint portion of window */
\r
4619 case WM_ERASEBKGND:
\r
4620 if (IsIconic(hwnd)) {
\r
4621 /* Cheat; change the message */
\r
4622 return (DefWindowProc(hwnd, WM_ICONERASEBKGND, wParam, lParam));
\r
4624 return (DefWindowProc(hwnd, message, wParam, lParam));
\r
4628 case WM_LBUTTONDOWN:
\r
4629 case WM_MBUTTONDOWN:
\r
4630 case WM_RBUTTONDOWN:
\r
4631 case WM_LBUTTONUP:
\r
4632 case WM_MBUTTONUP:
\r
4633 case WM_RBUTTONUP:
\r
4634 case WM_MOUSEMOVE:
\r
4635 case WM_MOUSEWHEEL:
\r
4636 MouseEvent(hwnd, message, wParam, lParam);
\r
4640 if((char)wParam == '\b') {
\r
4641 ForwardEvent(); peek = 0;
\r
4644 JAWS_KBUP_NAVIGATION
\r
4649 if((char)wParam == '\b') {
\r
4650 if(!peek) BackwardEvent(), peek = 1;
\r
4653 JAWS_KBDOWN_NAVIGATION
\r
4659 JAWS_ALT_INTERCEPT
\r
4661 if (appData.icsActive && ((char)wParam == '\r' || (char)wParam > ' ' && !((char)wParam >= '1' && (char)wParam <= '9'))) {
\r
4662 // [HGM] movenum: for non-zero digits we always do type-in dialog
\r
4663 HWND h = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
4664 if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);
\r
4666 SendMessage(h, message, wParam, lParam);
\r
4667 } else if(lParam != KF_REPEAT) {
\r
4668 if (isalpha((char)wParam) || isdigit((char)wParam)) {
\r
4669 TypeInEvent((char)wParam);
\r
4670 } else if((char)wParam == 003) CopyGameToClipboard();
\r
4671 else if((char)wParam == 026) PasteGameOrFENFromClipboard();
\r
4676 case WM_PALETTECHANGED:
\r
4677 if (hwnd != (HWND)wParam && !appData.monoMode) {
\r
4679 HDC hdc = GetDC(hwndMain);
\r
4680 SelectPalette(hdc, hPal, TRUE);
\r
4681 nnew = RealizePalette(hdc);
\r
4683 paletteChanged = TRUE;
\r
4684 InvalidateRect(hwnd, &boardRect, FALSE);
\r
4686 ReleaseDC(hwnd, hdc);
\r
4690 case WM_QUERYNEWPALETTE:
\r
4691 if (!appData.monoMode /*&& paletteChanged*/) {
\r
4693 HDC hdc = GetDC(hwndMain);
\r
4694 paletteChanged = FALSE;
\r
4695 SelectPalette(hdc, hPal, FALSE);
\r
4696 nnew = RealizePalette(hdc);
\r
4698 InvalidateRect(hwnd, &boardRect, FALSE);
\r
4700 ReleaseDC(hwnd, hdc);
\r
4705 case WM_COMMAND: /* message: command from application menu */
\r
4706 wmId = LOWORD(wParam);
\r
4707 wmEvent = HIWORD(wParam);
\r
4712 SAY("new game enter a move to play against the computer with white");
\r
4715 case IDM_NewGameFRC:
\r
4716 if( NewGameFRC() == 0 ) {
\r
4721 case IDM_NewVariant:
\r
4722 NewVariantPopup(hwnd);
\r
4725 case IDM_LoadGame:
\r
4726 LoadGameDialog(hwnd, _("Load Game from File"));
\r
4729 case IDM_LoadNextGame:
\r
4733 case IDM_LoadPrevGame:
\r
4737 case IDM_ReloadGame:
\r
4741 case IDM_LoadPosition:
\r
4742 if (gameMode == AnalyzeMode || gameMode == AnalyzeFile) {
\r
4743 Reset(FALSE, TRUE);
\r
4746 f = OpenFileDialog(hwnd, "rb", "",
\r
4747 appData.oldSaveStyle ? "pos" : "fen",
\r
4749 _("Load Position from File"), &number, fileTitle, NULL);
\r
4751 LoadPosition(f, number, fileTitle);
\r
4755 case IDM_LoadNextPosition:
\r
4756 ReloadPosition(1);
\r
4759 case IDM_LoadPrevPosition:
\r
4760 ReloadPosition(-1);
\r
4763 case IDM_ReloadPosition:
\r
4764 ReloadPosition(0);
\r
4767 case IDM_SaveGame:
\r
4768 defName = DefaultFileName(appData.oldSaveStyle ? "gam" : "pgn");
\r
4769 f = OpenFileDialog(hwnd, "a", defName,
\r
4770 appData.oldSaveStyle ? "gam" : "pgn",
\r
4772 _("Save Game to File"), NULL, fileTitle, NULL);
\r
4774 SaveGame(f, 0, "");
\r
4778 case IDM_SavePosition:
\r
4779 defName = DefaultFileName(appData.oldSaveStyle ? "pos" : "fen");
\r
4780 f = OpenFileDialog(hwnd, "a", defName,
\r
4781 appData.oldSaveStyle ? "pos" : "fen",
\r
4783 _("Save Position to File"), NULL, fileTitle, NULL);
\r
4785 SavePosition(f, 0, "");
\r
4789 case IDM_SaveDiagram:
\r
4790 defName = "diagram";
\r
4791 f = OpenFileDialog(hwnd, "wb", defName,
\r
4794 _("Save Diagram to File"), NULL, fileTitle, NULL);
\r
4800 case IDM_CopyGame:
\r
4801 CopyGameToClipboard();
\r
4804 case IDM_PasteGame:
\r
4805 PasteGameFromClipboard();
\r
4808 case IDM_CopyGameListToClipboard:
\r
4809 CopyGameListToClipboard();
\r
4812 /* [AS] Autodetect FEN or PGN data */
\r
4813 case IDM_PasteAny:
\r
4814 PasteGameOrFENFromClipboard();
\r
4817 /* [AS] Move history */
\r
4818 case IDM_ShowMoveHistory:
\r
4819 if( MoveHistoryIsUp() ) {
\r
4820 MoveHistoryPopDown();
\r
4823 MoveHistoryPopUp();
\r
4827 /* [AS] Eval graph */
\r
4828 case IDM_ShowEvalGraph:
\r
4829 if( EvalGraphIsUp() ) {
\r
4830 EvalGraphPopDown();
\r
4834 SetFocus(hwndMain);
\r
4838 /* [AS] Engine output */
\r
4839 case IDM_ShowEngineOutput:
\r
4840 if( EngineOutputIsUp() ) {
\r
4841 EngineOutputPopDown();
\r
4844 EngineOutputPopUp();
\r
4848 /* [AS] User adjudication */
\r
4849 case IDM_UserAdjudication_White:
\r
4850 UserAdjudicationEvent( +1 );
\r
4853 case IDM_UserAdjudication_Black:
\r
4854 UserAdjudicationEvent( -1 );
\r
4857 case IDM_UserAdjudication_Draw:
\r
4858 UserAdjudicationEvent( 0 );
\r
4861 /* [AS] Game list options dialog */
\r
4862 case IDM_GameListOptions:
\r
4863 GameListOptions();
\r
4870 case IDM_CopyPosition:
\r
4871 CopyFENToClipboard();
\r
4874 case IDM_PastePosition:
\r
4875 PasteFENFromClipboard();
\r
4878 case IDM_MailMove:
\r
4882 case IDM_ReloadCMailMsg:
\r
4883 Reset(TRUE, TRUE);
\r
4884 ReloadCmailMsgEvent(FALSE);
\r
4887 case IDM_Minimize:
\r
4888 ShowWindow(hwnd, SW_MINIMIZE);
\r
4895 case IDM_MachineWhite:
\r
4896 MachineWhiteEvent();
\r
4898 * refresh the tags dialog only if it's visible
\r
4900 if (gameMode == MachinePlaysWhite && IsWindowVisible(editTagsDialog)) {
\r
4902 tags = PGNTags(&gameInfo);
\r
4903 TagsPopUp(tags, CmailMsg());
\r
4906 SAY("computer starts playing white");
\r
4909 case IDM_MachineBlack:
\r
4910 MachineBlackEvent();
\r
4912 * refresh the tags dialog only if it's visible
\r
4914 if (gameMode == MachinePlaysBlack && IsWindowVisible(editTagsDialog)) {
\r
4916 tags = PGNTags(&gameInfo);
\r
4917 TagsPopUp(tags, CmailMsg());
\r
4920 SAY("computer starts playing black");
\r
4923 case IDM_Match: // [HGM] match: flows into next case, after setting Match Mode and nr of Games
\r
4924 MatchEvent(2); // distinguish from command-line-triggered case (matchMode=1)
\r
4927 case IDM_TwoMachines:
\r
4928 TwoMachinesEvent();
\r
4930 * refresh the tags dialog only if it's visible
\r
4932 if (gameMode == TwoMachinesPlay && IsWindowVisible(editTagsDialog)) {
\r
4934 tags = PGNTags(&gameInfo);
\r
4935 TagsPopUp(tags, CmailMsg());
\r
4938 SAY("computer starts playing both sides");
\r
4941 case IDM_AnalysisMode:
\r
4942 if (!first.analysisSupport) {
\r
4943 snprintf(buf, MSG_SIZ, _("%s does not support analysis"), first.tidy);
\r
4944 DisplayError(buf, 0);
\r
4946 SAY("analyzing current position");
\r
4947 /* [DM] icsEngineAnlyze [HGM] Why is this front-end??? */
\r
4948 if (appData.icsActive) {
\r
4949 if (gameMode != IcsObserving) {
\r
4950 snprintf(buf, MSG_SIZ, "You are not observing a game");
\r
4951 DisplayError(buf, 0);
\r
4952 /* secure check */
\r
4953 if (appData.icsEngineAnalyze) {
\r
4954 if (appData.debugMode)
\r
4955 fprintf(debugFP, "Found unexpected active ICS engine analyze \n");
\r
4956 ExitAnalyzeMode();
\r
4962 /* if enable, user want disable icsEngineAnalyze */
\r
4963 if (appData.icsEngineAnalyze) {
\r
4964 ExitAnalyzeMode();
\r
4968 appData.icsEngineAnalyze = TRUE;
\r
4969 if (appData.debugMode) fprintf(debugFP, "ICS engine analyze starting...\n");
\r
4972 if (!appData.showThinking) ToggleShowThinking();
\r
4973 AnalyzeModeEvent();
\r
4977 case IDM_AnalyzeFile:
\r
4978 if (!first.analysisSupport) {
\r
4979 char buf[MSG_SIZ];
\r
4980 snprintf(buf, MSG_SIZ, _("%s does not support analysis"), first.tidy);
\r
4981 DisplayError(buf, 0);
\r
4983 if (!appData.showThinking) ToggleShowThinking();
\r
4984 AnalyzeFileEvent();
\r
4985 // LoadGameDialog(hwnd, _("Analyze Game from File"));
\r
4986 AnalysisPeriodicEvent(1);
\r
4990 case IDM_IcsClient:
\r
4994 case IDM_EditGame:
\r
4995 case IDM_EditGame2:
\r
5000 case IDM_EditPosition:
\r
5001 case IDM_EditPosition2:
\r
5002 EditPositionEvent();
\r
5003 SAY("enter a FEN string or setup a position on the board using the control R pop up menu");
\r
5006 case IDM_Training:
\r
5010 case IDM_ShowGameList:
\r
5011 ShowGameListProc();
\r
5014 case IDM_EditProgs1:
\r
5015 EditTagsPopUp(firstChessProgramNames, &firstChessProgramNames);
\r
5018 case IDM_LoadProg1:
\r
5019 LoadEnginePopUp(hwndMain, 0);
\r
5022 case IDM_LoadProg2:
\r
5023 LoadEnginePopUp(hwndMain, 1);
\r
5026 case IDM_EditServers:
\r
5027 EditTagsPopUp(icsNames, &icsNames);
\r
5030 case IDM_EditTags:
\r
5035 case IDM_EditBook:
\r
5039 case IDM_EditComment:
\r
5041 if (commentUp && editComment) {
\r
5044 EditCommentEvent();
\r
5064 case IDM_CallFlag:
\r
5084 case IDM_StopObserving:
\r
5085 StopObservingEvent();
\r
5088 case IDM_StopExamining:
\r
5089 StopExaminingEvent();
\r
5093 UploadGameEvent();
\r
5096 case IDM_TypeInMove:
\r
5097 TypeInEvent('\000');
\r
5100 case IDM_TypeInName:
\r
5101 PopUpNameDialog('\000');
\r
5104 case IDM_Backward:
\r
5106 SetFocus(hwndMain);
\r
5113 SetFocus(hwndMain);
\r
5118 SetFocus(hwndMain);
\r
5123 SetFocus(hwndMain);
\r
5126 case OPT_GameListNext: // [HGM] forward these two accelerators to Game List
\r
5127 case OPT_GameListPrev:
\r
5128 if(gameListDialog) SendMessage(gameListDialog, WM_COMMAND, wmId, 0);
\r
5132 RevertEvent(FALSE);
\r
5135 case IDM_Annotate: // [HGM] vari: revert with annotation
\r
5136 RevertEvent(TRUE);
\r
5139 case IDM_TruncateGame:
\r
5140 TruncateGameEvent();
\r
5147 case IDM_RetractMove:
\r
5148 RetractMoveEvent();
\r
5151 case IDM_FlipView:
\r
5152 flipView = !flipView;
\r
5153 DrawPosition(FALSE, NULL);
\r
5156 case IDM_FlipClock:
\r
5157 flipClock = !flipClock;
\r
5158 DisplayBothClocks();
\r
5162 case IDM_MuteSounds:
\r
5163 mute = !mute; // [HGM] mute: keep track of global muting variable
\r
5164 CheckMenuItem(GetMenu(hwndMain),IDM_MuteSounds,
\r
5165 MF_BYCOMMAND|(mute?MF_CHECKED:MF_UNCHECKED));
\r
5168 case IDM_GeneralOptions:
\r
5169 GeneralOptionsPopup(hwnd);
\r
5170 DrawPosition(TRUE, NULL);
\r
5173 case IDM_BoardOptions:
\r
5174 BoardOptionsPopup(hwnd);
\r
5177 case IDM_EnginePlayOptions:
\r
5178 EnginePlayOptionsPopup(hwnd);
\r
5181 case IDM_Engine1Options:
\r
5182 EngineOptionsPopup(hwnd, &first);
\r
5185 case IDM_Engine2Options:
\r
5187 if(WaitForEngine(&second, SettingsMenuIfReady)) break;
\r
5188 EngineOptionsPopup(hwnd, &second);
\r
5191 case IDM_OptionsUCI:
\r
5192 UciOptionsPopup(hwnd);
\r
5196 TourneyPopup(hwnd);
\r
5199 case IDM_IcsOptions:
\r
5200 IcsOptionsPopup(hwnd);
\r
5204 FontsOptionsPopup(hwnd);
\r
5208 SoundOptionsPopup(hwnd);
\r
5211 case IDM_CommPort:
\r
5212 CommPortOptionsPopup(hwnd);
\r
5215 case IDM_LoadOptions:
\r
5216 LoadOptionsPopup(hwnd);
\r
5219 case IDM_SaveOptions:
\r
5220 SaveOptionsPopup(hwnd);
\r
5223 case IDM_TimeControl:
\r
5224 TimeControlOptionsPopup(hwnd);
\r
5227 case IDM_SaveSettings:
\r
5228 SaveSettings(settingsFileName);
\r
5231 case IDM_SaveSettingsOnExit:
\r
5232 saveSettingsOnExit = !saveSettingsOnExit;
\r
5233 (void) CheckMenuItem(GetMenu(hwndMain), IDM_SaveSettingsOnExit,
\r
5234 MF_BYCOMMAND|(saveSettingsOnExit ?
\r
5235 MF_CHECKED : MF_UNCHECKED));
\r
5246 case IDM_AboutGame:
\r
5251 appData.debugMode = !appData.debugMode;
\r
5252 if (appData.debugMode) {
\r
5253 char dir[MSG_SIZ];
\r
5254 GetCurrentDirectory(MSG_SIZ, dir);
\r
5255 SetCurrentDirectory(installDir);
\r
5256 debugFP = fopen(appData.nameOfDebugFile, "w");
\r
5257 SetCurrentDirectory(dir);
\r
5258 setbuf(debugFP, NULL);
\r
5265 case IDM_HELPCONTENTS:
\r
5266 if (!MyHelp (hwnd, "winboard.hlp", HELP_KEY,(DWORD)(LPSTR)"CONTENTS") &&
\r
5267 !HtmlHelp(hwnd, "winboard.chm", 0, 0) ) {
\r
5268 MessageBox (GetFocus(),
\r
5269 _("Unable to activate help"),
\r
5270 szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);
\r
5274 case IDM_HELPSEARCH:
\r
5275 if (!MyHelp (hwnd, "winboard.hlp", HELP_PARTIALKEY, (DWORD)(LPSTR)"") &&
\r
5276 !HtmlHelp(hwnd, "winboard.chm", 0, 0) ) {
\r
5277 MessageBox (GetFocus(),
\r
5278 _("Unable to activate help"),
\r
5279 szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);
\r
5283 case IDM_HELPHELP:
\r
5284 if(!WinHelp(hwnd, (LPSTR)NULL, HELP_HELPONHELP, 0)) {
\r
5285 MessageBox (GetFocus(),
\r
5286 _("Unable to activate help"),
\r
5287 szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);
\r
5292 lpProc = MakeProcInstance((FARPROC)About, hInst);
\r
5294 (gameInfo.event && strcmp(gameInfo.event, "Easter Egg Hunt") == 0) ?
\r
5295 "AboutBox2" : "AboutBox", hwnd, (DLGPROC)lpProc);
\r
5296 FreeProcInstance(lpProc);
\r
5299 case IDM_DirectCommand1:
\r
5300 AskQuestionEvent(_("Direct Command"),
\r
5301 _("Send to chess program:"), "", "1");
\r
5303 case IDM_DirectCommand2:
\r
5304 AskQuestionEvent(_("Direct Command"),
\r
5305 _("Send to second chess program:"), "", "2");
\r
5308 case EP_WhitePawn:
\r
5309 EditPositionMenuEvent(WhitePawn, fromX, fromY);
\r
5310 fromX = fromY = -1;
\r
5313 case EP_WhiteKnight:
\r
5314 EditPositionMenuEvent(WhiteKnight, fromX, fromY);
\r
5315 fromX = fromY = -1;
\r
5318 case EP_WhiteBishop:
\r
5319 EditPositionMenuEvent(WhiteBishop, fromX, fromY);
\r
5320 fromX = fromY = -1;
\r
5323 case EP_WhiteRook:
\r
5324 EditPositionMenuEvent(WhiteRook, fromX, fromY);
\r
5325 fromX = fromY = -1;
\r
5328 case EP_WhiteQueen:
\r
5329 EditPositionMenuEvent(WhiteQueen, fromX, fromY);
\r
5330 fromX = fromY = -1;
\r
5333 case EP_WhiteFerz:
\r
5334 EditPositionMenuEvent(WhiteFerz, fromX, fromY);
\r
5335 fromX = fromY = -1;
\r
5338 case EP_WhiteWazir:
\r
5339 EditPositionMenuEvent(WhiteWazir, fromX, fromY);
\r
5340 fromX = fromY = -1;
\r
5343 case EP_WhiteAlfil:
\r
5344 EditPositionMenuEvent(WhiteAlfil, fromX, fromY);
\r
5345 fromX = fromY = -1;
\r
5348 case EP_WhiteCannon:
\r
5349 EditPositionMenuEvent(WhiteCannon, fromX, fromY);
\r
5350 fromX = fromY = -1;
\r
5353 case EP_WhiteCardinal:
\r
5354 EditPositionMenuEvent(WhiteAngel, fromX, fromY);
\r
5355 fromX = fromY = -1;
\r
5358 case EP_WhiteMarshall:
\r
5359 EditPositionMenuEvent(WhiteMarshall, fromX, fromY);
\r
5360 fromX = fromY = -1;
\r
5363 case EP_WhiteKing:
\r
5364 EditPositionMenuEvent(WhiteKing, fromX, fromY);
\r
5365 fromX = fromY = -1;
\r
5368 case EP_BlackPawn:
\r
5369 EditPositionMenuEvent(BlackPawn, fromX, fromY);
\r
5370 fromX = fromY = -1;
\r
5373 case EP_BlackKnight:
\r
5374 EditPositionMenuEvent(BlackKnight, fromX, fromY);
\r
5375 fromX = fromY = -1;
\r
5378 case EP_BlackBishop:
\r
5379 EditPositionMenuEvent(BlackBishop, fromX, fromY);
\r
5380 fromX = fromY = -1;
\r
5383 case EP_BlackRook:
\r
5384 EditPositionMenuEvent(BlackRook, fromX, fromY);
\r
5385 fromX = fromY = -1;
\r
5388 case EP_BlackQueen:
\r
5389 EditPositionMenuEvent(BlackQueen, fromX, fromY);
\r
5390 fromX = fromY = -1;
\r
5393 case EP_BlackFerz:
\r
5394 EditPositionMenuEvent(BlackFerz, fromX, fromY);
\r
5395 fromX = fromY = -1;
\r
5398 case EP_BlackWazir:
\r
5399 EditPositionMenuEvent(BlackWazir, fromX, fromY);
\r
5400 fromX = fromY = -1;
\r
5403 case EP_BlackAlfil:
\r
5404 EditPositionMenuEvent(BlackAlfil, fromX, fromY);
\r
5405 fromX = fromY = -1;
\r
5408 case EP_BlackCannon:
\r
5409 EditPositionMenuEvent(BlackCannon, fromX, fromY);
\r
5410 fromX = fromY = -1;
\r
5413 case EP_BlackCardinal:
\r
5414 EditPositionMenuEvent(BlackAngel, fromX, fromY);
\r
5415 fromX = fromY = -1;
\r
5418 case EP_BlackMarshall:
\r
5419 EditPositionMenuEvent(BlackMarshall, fromX, fromY);
\r
5420 fromX = fromY = -1;
\r
5423 case EP_BlackKing:
\r
5424 EditPositionMenuEvent(BlackKing, fromX, fromY);
\r
5425 fromX = fromY = -1;
\r
5428 case EP_EmptySquare:
\r
5429 EditPositionMenuEvent(EmptySquare, fromX, fromY);
\r
5430 fromX = fromY = -1;
\r
5433 case EP_ClearBoard:
\r
5434 EditPositionMenuEvent(ClearBoard, fromX, fromY);
\r
5435 fromX = fromY = -1;
\r
5439 EditPositionMenuEvent(WhitePlay, fromX, fromY);
\r
5440 fromX = fromY = -1;
\r
5444 EditPositionMenuEvent(BlackPlay, fromX, fromY);
\r
5445 fromX = fromY = -1;
\r
5449 EditPositionMenuEvent(PromotePiece, fromX, fromY);
\r
5450 fromX = fromY = -1;
\r
5454 EditPositionMenuEvent(DemotePiece, fromX, fromY);
\r
5455 fromX = fromY = -1;
\r
5459 DropMenuEvent(WhitePawn, fromX, fromY);
\r
5460 fromX = fromY = -1;
\r
5464 DropMenuEvent(WhiteKnight, fromX, fromY);
\r
5465 fromX = fromY = -1;
\r
5469 DropMenuEvent(WhiteBishop, fromX, fromY);
\r
5470 fromX = fromY = -1;
\r
5474 DropMenuEvent(WhiteRook, fromX, fromY);
\r
5475 fromX = fromY = -1;
\r
5479 DropMenuEvent(WhiteQueen, fromX, fromY);
\r
5480 fromX = fromY = -1;
\r
5484 barbaric = 0; appData.language = "";
\r
5485 TranslateMenus(0);
\r
5486 CheckMenuItem(GetMenu(hwndMain), lastChecked, MF_BYCOMMAND|MF_UNCHECKED);
\r
5487 CheckMenuItem(GetMenu(hwndMain), IDM_English, MF_BYCOMMAND|MF_CHECKED);
\r
5488 lastChecked = wmId;
\r
5492 if(wmId >= IDM_RecentEngines && wmId < IDM_RecentEngines + appData.recentEngines)
\r
5493 RecentEngineEvent(wmId - IDM_RecentEngines);
\r
5495 if(wmId > IDM_English && wmId < IDM_English+20) {
\r
5496 LoadLanguageFile(languageFile[wmId - IDM_English - 1]);
\r
5497 TranslateMenus(0);
\r
5498 CheckMenuItem(GetMenu(hwndMain), lastChecked, MF_BYCOMMAND|MF_UNCHECKED);
\r
5499 CheckMenuItem(GetMenu(hwndMain), wmId, MF_BYCOMMAND|MF_CHECKED);
\r
5500 lastChecked = wmId;
\r
5503 return (DefWindowProc(hwnd, message, wParam, lParam));
\r
5509 case CLOCK_TIMER_ID:
\r
5510 KillTimer(hwnd, clockTimerEvent); /* Simulate one-shot timer as in X */
\r
5511 clockTimerEvent = 0;
\r
5512 DecrementClocks(); /* call into back end */
\r
5514 case LOAD_GAME_TIMER_ID:
\r
5515 KillTimer(hwnd, loadGameTimerEvent); /* Simulate one-shot timer as in X*/
\r
5516 loadGameTimerEvent = 0;
\r
5517 AutoPlayGameLoop(); /* call into back end */
\r
5519 case ANALYSIS_TIMER_ID:
\r
5520 if ((gameMode == AnalyzeMode || gameMode == AnalyzeFile
\r
5521 || appData.icsEngineAnalyze) && appData.periodicUpdates) {
\r
5522 AnalysisPeriodicEvent(0);
\r
5524 KillTimer(hwnd, analysisTimerEvent);
\r
5525 analysisTimerEvent = 0;
\r
5528 case DELAYED_TIMER_ID:
\r
5529 KillTimer(hwnd, delayedTimerEvent);
\r
5530 delayedTimerEvent = 0;
\r
5531 delayedTimerCallback();
\r
5536 case WM_USER_Input:
\r
5537 InputEvent(hwnd, message, wParam, lParam);
\r
5540 /* [AS] Also move "attached" child windows */
\r
5541 case WM_WINDOWPOSCHANGING:
\r
5543 if( hwnd == hwndMain && appData.useStickyWindows ) {
\r
5544 LPWINDOWPOS lpwp = (LPWINDOWPOS) lParam;
\r
5546 if( ((lpwp->flags & SWP_NOMOVE) == 0) && ((lpwp->flags & SWP_NOSIZE) != 0) ) {
\r
5547 /* Window is moving */
\r
5550 // GetWindowRect( hwnd, &rcMain ); //[HGM] sticky: in XP this returned new position, not old
\r
5551 rcMain.left = wpMain.x; // replace by these 4 lines to reconstruct old rect
\r
5552 rcMain.right = wpMain.x + wpMain.width;
\r
5553 rcMain.top = wpMain.y;
\r
5554 rcMain.bottom = wpMain.y + wpMain.height;
\r
5556 ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, moveHistoryDialog, &wpMoveHistory );
\r
5557 ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, evalGraphDialog, &wpEvalGraph );
\r
5558 ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, engineOutputDialog, &wpEngineOutput );
\r
5559 ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, gameListDialog, &wpGameList );
\r
5560 ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, hwndConsole, &wpConsole );
\r
5561 wpMain.x = lpwp->x;
\r
5562 wpMain.y = lpwp->y;
\r
5567 /* [AS] Snapping */
\r
5568 case WM_ENTERSIZEMOVE:
\r
5569 if(appData.debugMode) { fprintf(debugFP, "size-move\n"); }
\r
5570 if (hwnd == hwndMain) {
\r
5571 doingSizing = TRUE;
\r
5574 return OnEnterSizeMove( &sd, hwnd, wParam, lParam );
\r
5578 if(appData.debugMode) { fprintf(debugFP, "sizing\n"); }
\r
5579 if (hwnd == hwndMain) {
\r
5580 lastSizing = wParam;
\r
5585 if(appData.debugMode) { fprintf(debugFP, "moving\n"); }
\r
5586 return OnMoving( &sd, hwnd, wParam, lParam );
\r
5588 case WM_EXITSIZEMOVE:
\r
5589 if(appData.debugMode) { fprintf(debugFP, "exit size-move, size = %d\n", squareSize); }
\r
5590 if (hwnd == hwndMain) {
\r
5592 doingSizing = FALSE;
\r
5593 InvalidateRect(hwnd, &boardRect, FALSE);
\r
5594 GetClientRect(hwnd, &client);
\r
5595 ResizeBoard(client.right, client.bottom, lastSizing);
\r
5597 if(appData.debugMode) { fprintf(debugFP, "square size = %d\n", squareSize); }
\r
5599 return OnExitSizeMove( &sd, hwnd, wParam, lParam );
\r
5602 case WM_DESTROY: /* message: window being destroyed */
\r
5603 PostQuitMessage(0);
\r
5607 if (hwnd == hwndMain) {
\r
5612 default: /* Passes it on if unprocessed */
\r
5613 return (DefWindowProc(hwnd, message, wParam, lParam));
\r
5618 /*---------------------------------------------------------------------------*\
\r
5620 * Misc utility routines
\r
5622 \*---------------------------------------------------------------------------*/
\r
5625 * Decent random number generator, at least not as bad as Windows
\r
5626 * standard rand, which returns a value in the range 0 to 0x7fff.
\r
5628 unsigned int randstate;
\r
5633 randstate = randstate * 1664525 + 1013904223;
\r
5634 return (int) randstate & 0x7fffffff;
\r
5638 mysrandom(unsigned int seed)
\r
5645 * returns TRUE if user selects a different color, FALSE otherwise
\r
5649 ChangeColor(HWND hwnd, COLORREF *which)
\r
5651 static BOOL firstTime = TRUE;
\r
5652 static DWORD customColors[16];
\r
5654 COLORREF newcolor;
\r
5659 /* Make initial colors in use available as custom colors */
\r
5660 /* Should we put the compiled-in defaults here instead? */
\r
5662 customColors[i++] = lightSquareColor & 0xffffff;
\r
5663 customColors[i++] = darkSquareColor & 0xffffff;
\r
5664 customColors[i++] = whitePieceColor & 0xffffff;
\r
5665 customColors[i++] = blackPieceColor & 0xffffff;
\r
5666 customColors[i++] = highlightSquareColor & 0xffffff;
\r
5667 customColors[i++] = premoveHighlightColor & 0xffffff;
\r
5669 for (ccl = (ColorClass) 0; ccl < NColorClasses && i < 16; ccl++) {
\r
5670 customColors[i++] = textAttribs[ccl].color;
\r
5672 while (i < 16) customColors[i++] = RGB(255, 255, 255);
\r
5673 firstTime = FALSE;
\r
5676 cc.lStructSize = sizeof(cc);
\r
5677 cc.hwndOwner = hwnd;
\r
5678 cc.hInstance = NULL;
\r
5679 cc.rgbResult = (DWORD) (*which & 0xffffff);
\r
5680 cc.lpCustColors = (LPDWORD) customColors;
\r
5681 cc.Flags = CC_RGBINIT|CC_FULLOPEN;
\r
5683 if (!ChooseColor(&cc)) return FALSE;
\r
5685 newcolor = (COLORREF) (0x2000000 | cc.rgbResult);
\r
5686 if (newcolor == *which) return FALSE;
\r
5687 *which = newcolor;
\r
5691 InitDrawingColors();
\r
5692 InvalidateRect(hwnd, &boardRect, FALSE);
\r
5697 MyLoadSound(MySound *ms)
\r
5703 if (ms->data && ms->flag) free(ms->data);
\r
5706 switch (ms->name[0]) {
\r
5712 /* System sound from Control Panel. Don't preload here. */
\r
5716 if (ms->name[1] == NULLCHAR) {
\r
5717 /* "!" alone = silence */
\r
5720 /* Builtin wave resource. Error if not found. */
\r
5721 HANDLE h = FindResource(hInst, ms->name + 1, "WAVE");
\r
5722 if (h == NULL) break;
\r
5723 ms->data = (void *)LoadResource(hInst, h);
\r
5724 ms->flag = 0; // not maloced, so cannot be freed!
\r
5725 if (h == NULL) break;
\r
5730 /* .wav file. Error if not found. */
\r
5731 f = fopen(ms->name, "rb");
\r
5732 if (f == NULL) break;
\r
5733 if (fstat(fileno(f), &st) < 0) break;
\r
5734 ms->data = malloc(st.st_size);
\r
5736 if (fread(ms->data, st.st_size, 1, f) < 1) break;
\r
5742 char buf[MSG_SIZ];
\r
5743 snprintf(buf, MSG_SIZ, _("Error loading sound %s"), ms->name);
\r
5744 DisplayError(buf, GetLastError());
\r
5750 MyPlaySound(MySound *ms)
\r
5752 BOOLEAN ok = FALSE;
\r
5754 if(mute) return TRUE; // [HGM] mute: suppress all sound play when muted
\r
5755 switch (ms->name[0]) {
\r
5757 if(appData.debugMode) fprintf(debugFP, "silence\n");
\r
5762 /* System sound from Control Panel (deprecated feature).
\r
5763 "$" alone or an unset sound name gets default beep (still in use). */
\r
5764 if (ms->name[1]) {
\r
5765 ok = PlaySound(ms->name + 1, NULL, SND_ALIAS|SND_ASYNC);
\r
5767 if (!ok) ok = MessageBeep(MB_OK);
\r
5770 /* Builtin wave resource, or "!" alone for silence */
\r
5771 if (ms->name[1]) {
\r
5772 if (ms->data == NULL) return FALSE;
\r
5773 ok = PlaySound(ms->data, NULL, SND_MEMORY|SND_ASYNC);
\r
5779 /* .wav file. Error if not found. */
\r
5780 if (ms->data == NULL) return FALSE;
\r
5781 ok = PlaySound(ms->data, NULL, SND_MEMORY|SND_ASYNC);
\r
5784 /* Don't print an error: this can happen innocently if the sound driver
\r
5785 is busy; for instance, if another instance of WinBoard is playing
\r
5786 a sound at about the same time. */
\r
5792 OldOpenFileHook(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
5795 OPENFILENAME *ofn;
\r
5796 static UINT *number; /* gross that this is static */
\r
5798 switch (message) {
\r
5799 case WM_INITDIALOG: /* message: initialize dialog box */
\r
5800 /* Center the dialog over the application window */
\r
5801 ofn = (OPENFILENAME *) lParam;
\r
5802 if (ofn->Flags & OFN_ENABLETEMPLATE) {
\r
5803 number = (UINT *) ofn->lCustData;
\r
5804 SendMessage(GetDlgItem(hDlg, edt2), WM_SETTEXT, 0, (LPARAM) "");
\r
5808 CenterWindow (hDlg, GetWindow (hDlg, GW_OWNER));
\r
5809 Translate(hDlg, 1536);
\r
5810 return FALSE; /* Allow for further processing */
\r
5813 if ((LOWORD(wParam) == IDOK) && (number != NULL)) {
\r
5814 *number = GetDlgItemInt(hDlg, OPT_IndexNumberOld, &ok, FALSE);
\r
5816 return FALSE; /* Allow for further processing */
\r
5822 OpenFileHook(HWND hdlg, UINT uiMsg, WPARAM wParam, LPARAM lParam)
\r
5824 static UINT *number;
\r
5825 OPENFILENAME *ofname;
\r
5828 case WM_INITDIALOG:
\r
5829 Translate(hdlg, DLG_IndexNumber);
\r
5830 ofname = (OPENFILENAME *)lParam;
\r
5831 number = (UINT *)(ofname->lCustData);
\r
5834 ofnot = (OFNOTIFY *)lParam;
\r
5835 if (ofnot->hdr.code == CDN_FILEOK) {
\r
5836 *number = GetDlgItemInt(hdlg, OPT_IndexNumber, NULL, FALSE);
\r
5845 OpenFileDialog(HWND hwnd, char *write, char *defName, char *defExt, // [HGM] diag: type of 'write' now string
\r
5846 char *nameFilt, char *dlgTitle, UINT *number,
\r
5847 char fileTitle[MSG_SIZ], char fileName[MSG_SIZ])
\r
5849 OPENFILENAME openFileName;
\r
5850 char buf1[MSG_SIZ];
\r
5853 if (fileName == NULL) fileName = buf1;
\r
5854 if (defName == NULL) {
\r
5855 safeStrCpy(fileName, "*.", 3 );
\r
5856 strcat(fileName, defExt);
\r
5858 safeStrCpy(fileName, defName, MSG_SIZ );
\r
5860 if (fileTitle) safeStrCpy(fileTitle, "", MSG_SIZ );
\r
5861 if (number) *number = 0;
\r
5863 openFileName.lStructSize = sizeof(OPENFILENAME);
\r
5864 openFileName.hwndOwner = hwnd;
\r
5865 openFileName.hInstance = (HANDLE) hInst;
\r
5866 openFileName.lpstrFilter = nameFilt;
\r
5867 openFileName.lpstrCustomFilter = (LPSTR) NULL;
\r
5868 openFileName.nMaxCustFilter = 0L;
\r
5869 openFileName.nFilterIndex = 1L;
\r
5870 openFileName.lpstrFile = fileName;
\r
5871 openFileName.nMaxFile = MSG_SIZ;
\r
5872 openFileName.lpstrFileTitle = fileTitle;
\r
5873 openFileName.nMaxFileTitle = fileTitle ? MSG_SIZ : 0;
\r
5874 openFileName.lpstrInitialDir = NULL;
\r
5875 openFileName.lpstrTitle = dlgTitle;
\r
5876 openFileName.Flags = OFN_PATHMUSTEXIST | OFN_HIDEREADONLY
\r
5877 | (write[0] != 'r' ? 0 : OFN_FILEMUSTEXIST)
\r
5878 | (number ? OFN_ENABLETEMPLATE | OFN_ENABLEHOOK: 0)
\r
5879 | (oldDialog ? 0 : OFN_EXPLORER);
\r
5880 openFileName.nFileOffset = 0;
\r
5881 openFileName.nFileExtension = 0;
\r
5882 openFileName.lpstrDefExt = defExt;
\r
5883 openFileName.lCustData = (LONG) number;
\r
5884 openFileName.lpfnHook = oldDialog ?
\r
5885 (LPOFNHOOKPROC) OldOpenFileHook : (LPOFNHOOKPROC) OpenFileHook;
\r
5886 openFileName.lpTemplateName = (LPSTR)(oldDialog ? 1536 : DLG_IndexNumber);
\r
5888 if (write[0] != 'r' ? GetSaveFileName(&openFileName) :
\r
5889 GetOpenFileName(&openFileName)) {
\r
5890 /* open the file */
\r
5891 f = fopen(openFileName.lpstrFile, write);
\r
5893 MessageBox(hwnd, _("File open failed"), NULL,
\r
5894 MB_OK|MB_ICONEXCLAMATION);
\r
5898 int err = CommDlgExtendedError();
\r
5899 if (err != 0) DisplayError(_("Internal error in file dialog box"), err);
\r
5908 MenuPopup(HWND hwnd, POINT pt, HMENU hmenu, UINT def)
\r
5910 HMENU hmenuTrackPopup; /* floating pop-up menu */
\r
5913 * Get the first pop-up menu in the menu template. This is the
\r
5914 * menu that TrackPopupMenu displays.
\r
5916 hmenuTrackPopup = GetSubMenu(hmenu, 0);
\r
5917 TranslateOneMenu(10, hmenuTrackPopup);
\r
5919 SetMenuDefaultItem(hmenuTrackPopup, def, FALSE);
\r
5922 * TrackPopup uses screen coordinates, so convert the
\r
5923 * coordinates of the mouse click to screen coordinates.
\r
5925 ClientToScreen(hwnd, (LPPOINT) &pt);
\r
5927 /* Draw and track the floating pop-up menu. */
\r
5928 TrackPopupMenu(hmenuTrackPopup, TPM_CENTERALIGN | TPM_RIGHTBUTTON,
\r
5929 pt.x, pt.y, 0, hwnd, NULL);
\r
5931 /* Destroy the menu.*/
\r
5932 DestroyMenu(hmenu);
\r
5937 int sizeX, sizeY, newSizeX, newSizeY;
\r
5939 } ResizeEditPlusButtonsClosure;
\r
5942 ResizeEditPlusButtonsCallback(HWND hChild, LPARAM lparam)
\r
5944 ResizeEditPlusButtonsClosure *cl = (ResizeEditPlusButtonsClosure *)lparam;
\r
5948 if (hChild == cl->hText) return TRUE;
\r
5949 GetWindowRect(hChild, &rect); /* gives screen coords */
\r
5950 pt.x = rect.left + (cl->newSizeX - cl->sizeX)/2;
\r
5951 pt.y = rect.top + cl->newSizeY - cl->sizeY;
\r
5952 ScreenToClient(cl->hDlg, &pt);
\r
5953 cl->hdwp = DeferWindowPos(cl->hdwp, hChild, NULL,
\r
5954 pt.x, pt.y, rect.right - rect.left, rect.bottom - rect.top, SWP_NOZORDER);
\r
5958 /* Resize a dialog that has a (rich) edit field filling most of
\r
5959 the top, with a row of buttons below */
\r
5961 ResizeEditPlusButtons(HWND hDlg, HWND hText, int sizeX, int sizeY, int newSizeX, int newSizeY)
\r
5964 int newTextHeight, newTextWidth;
\r
5965 ResizeEditPlusButtonsClosure cl;
\r
5967 /*if (IsIconic(hDlg)) return;*/
\r
5968 if (newSizeX == sizeX && newSizeY == sizeY) return;
\r
5970 cl.hdwp = BeginDeferWindowPos(8);
\r
5972 GetWindowRect(hText, &rectText); /* gives screen coords */
\r
5973 newTextWidth = rectText.right - rectText.left + newSizeX - sizeX;
\r
5974 newTextHeight = rectText.bottom - rectText.top + newSizeY - sizeY;
\r
5975 if (newTextHeight < 0) {
\r
5976 newSizeY += -newTextHeight;
\r
5977 newTextHeight = 0;
\r
5979 cl.hdwp = DeferWindowPos(cl.hdwp, hText, NULL, 0, 0,
\r
5980 newTextWidth, newTextHeight, SWP_NOZORDER|SWP_NOMOVE);
\r
5986 cl.newSizeX = newSizeX;
\r
5987 cl.newSizeY = newSizeY;
\r
5988 EnumChildWindows(hDlg, ResizeEditPlusButtonsCallback, (LPARAM)&cl);
\r
5990 EndDeferWindowPos(cl.hdwp);
\r
5993 BOOL CenterWindowEx(HWND hwndChild, HWND hwndParent, int mode)
\r
5995 RECT rChild, rParent;
\r
5996 int wChild, hChild, wParent, hParent;
\r
5997 int wScreen, hScreen, xNew, yNew;
\r
6000 /* Get the Height and Width of the child window */
\r
6001 GetWindowRect (hwndChild, &rChild);
\r
6002 wChild = rChild.right - rChild.left;
\r
6003 hChild = rChild.bottom - rChild.top;
\r
6005 /* Get the Height and Width of the parent window */
\r
6006 GetWindowRect (hwndParent, &rParent);
\r
6007 wParent = rParent.right - rParent.left;
\r
6008 hParent = rParent.bottom - rParent.top;
\r
6010 /* Get the display limits */
\r
6011 hdc = GetDC (hwndChild);
\r
6012 wScreen = GetDeviceCaps (hdc, HORZRES);
\r
6013 hScreen = GetDeviceCaps (hdc, VERTRES);
\r
6014 ReleaseDC(hwndChild, hdc);
\r
6016 /* Calculate new X position, then adjust for screen */
\r
6017 xNew = rParent.left + ((wParent - wChild) /2);
\r
6020 } else if ((xNew+wChild) > wScreen) {
\r
6021 xNew = wScreen - wChild;
\r
6024 /* Calculate new Y position, then adjust for screen */
\r
6026 yNew = rParent.top + ((hParent - hChild) /2);
\r
6029 yNew = rParent.top + GetSystemMetrics( SM_CYCAPTION ) * 2 / 3;
\r
6034 } else if ((yNew+hChild) > hScreen) {
\r
6035 yNew = hScreen - hChild;
\r
6038 /* Set it, and return */
\r
6039 return SetWindowPos (hwndChild, NULL,
\r
6040 xNew, yNew, 0, 0, SWP_NOSIZE | SWP_NOZORDER);
\r
6043 /* Center one window over another */
\r
6044 BOOL CenterWindow (HWND hwndChild, HWND hwndParent)
\r
6046 return CenterWindowEx( hwndChild, hwndParent, 0 );
\r
6049 /*---------------------------------------------------------------------------*\
\r
6051 * Startup Dialog functions
\r
6053 \*---------------------------------------------------------------------------*/
\r
6055 InitComboStrings(HANDLE hwndCombo, char **cd)
\r
6057 SendMessage(hwndCombo, CB_RESETCONTENT, 0, 0);
\r
6059 while (*cd != NULL) {
\r
6060 SendMessage(hwndCombo, CB_ADDSTRING, 0, (LPARAM) T_(*cd));
\r
6066 InitComboStringsFromOption(HANDLE hwndCombo, char *str)
\r
6068 char buf1[MAX_ARG_LEN];
\r
6071 if (str[0] == '@') {
\r
6072 FILE* f = fopen(str + 1, "r");
\r
6074 DisplayFatalError(str + 1, errno, 2);
\r
6077 len = fread(buf1, 1, sizeof(buf1)-1, f);
\r
6079 buf1[len] = NULLCHAR;
\r
6083 SendMessage(hwndCombo, CB_RESETCONTENT, 0, 0);
\r
6086 char buf[MSG_SIZ];
\r
6087 char *end = strchr(str, '\n');
\r
6088 if (end == NULL) return;
\r
6089 memcpy(buf, str, end - str);
\r
6090 buf[end - str] = NULLCHAR;
\r
6091 SendMessage(hwndCombo, CB_ADDSTRING, 0, (LPARAM) buf);
\r
6097 SetStartupDialogEnables(HWND hDlg)
\r
6099 EnableWindow(GetDlgItem(hDlg, OPT_ChessEngineName),
\r
6100 IsDlgButtonChecked(hDlg, OPT_ChessEngine) ||
\r
6101 (appData.zippyPlay && IsDlgButtonChecked(hDlg, OPT_ChessServer)));
\r
6102 EnableWindow(GetDlgItem(hDlg, OPT_SecondChessEngineName),
\r
6103 IsDlgButtonChecked(hDlg, OPT_ChessEngine));
\r
6104 EnableWindow(GetDlgItem(hDlg, OPT_ChessServerName),
\r
6105 IsDlgButtonChecked(hDlg, OPT_ChessServer));
\r
6106 EnableWindow(GetDlgItem(hDlg, OPT_AdditionalOptions),
\r
6107 IsDlgButtonChecked(hDlg, OPT_AnyAdditional));
\r
6108 EnableWindow(GetDlgItem(hDlg, IDOK),
\r
6109 IsDlgButtonChecked(hDlg, OPT_ChessEngine) ||
\r
6110 IsDlgButtonChecked(hDlg, OPT_ChessServer) ||
\r
6111 IsDlgButtonChecked(hDlg, OPT_View));
\r
6115 QuoteForFilename(char *filename)
\r
6117 int dquote, space;
\r
6118 dquote = strchr(filename, '"') != NULL;
\r
6119 space = strchr(filename, ' ') != NULL;
\r
6120 if (dquote || space) {
\r
6132 InitEngineBox(HWND hDlg, HWND hwndCombo, char* nthcp, char* nthd, char* nthdir, char *nthnames)
\r
6134 char buf[MSG_SIZ];
\r
6137 InitComboStringsFromOption(hwndCombo, nthnames);
\r
6138 q = QuoteForFilename(nthcp);
\r
6139 snprintf(buf, MSG_SIZ, "%s%s%s", q, nthcp, q);
\r
6140 if (*nthdir != NULLCHAR) {
\r
6141 q = QuoteForFilename(nthdir);
\r
6142 snprintf(buf + strlen(buf), MSG_SIZ, " /%s=%s%s%s", nthd, q, nthdir, q);
\r
6144 if (*nthcp == NULLCHAR) {
\r
6145 SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) 0, (LPARAM) 0);
\r
6146 } else if (SendMessage(hwndCombo, CB_SELECTSTRING, (WPARAM) -1, (LPARAM) buf) == CB_ERR) {
\r
6147 SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) -1, (LPARAM) 0);
\r
6148 SendMessage(hwndCombo, WM_SETTEXT, (WPARAM) 0, (LPARAM) buf);
\r
6153 StartupDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
6155 char buf[MSG_SIZ];
\r
6159 switch (message) {
\r
6160 case WM_INITDIALOG:
\r
6161 /* Center the dialog */
\r
6162 CenterWindow (hDlg, GetDesktopWindow());
\r
6163 Translate(hDlg, DLG_Startup);
\r
6164 /* Initialize the dialog items */
\r
6165 InitEngineBox(hDlg, GetDlgItem(hDlg, OPT_ChessEngineName),
\r
6166 appData.firstChessProgram, "fd", appData.firstDirectory,
\r
6167 firstChessProgramNames);
\r
6168 InitEngineBox(hDlg, GetDlgItem(hDlg, OPT_SecondChessEngineName),
\r
6169 appData.secondChessProgram, singleList ? "fd" : "sd", appData.secondDirectory,
\r
6170 singleList ? firstChessProgramNames : secondChessProgramNames); //[HGM] single: use first list in second combo
\r
6171 hwndCombo = GetDlgItem(hDlg, OPT_ChessServerName);
\r
6172 InitComboStringsFromOption(hwndCombo, icsNames);
\r
6173 snprintf(buf, MSG_SIZ, "%s /icsport=%s", appData.icsHost, appData.icsPort);
\r
6174 if (*appData.icsHelper != NULLCHAR) {
\r
6175 char *q = QuoteForFilename(appData.icsHelper);
\r
6176 sprintf(buf + strlen(buf), " /icshelper=%s%s%s", q, appData.icsHelper, q);
\r
6178 if (*appData.icsHost == NULLCHAR) {
\r
6179 SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) 0, (LPARAM) 0);
\r
6180 /*SendMessage(hwndCombo, CB_SHOWDROPDOWN, (WPARAM) TRUE, (LPARAM) 0); !!too soon */
\r
6181 } else if (SendMessage(hwndCombo, CB_SELECTSTRING, (WPARAM) -1, (LPARAM) buf) == CB_ERR) {
\r
6182 SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) -1, (LPARAM) 0);
\r
6183 SendMessage(hwndCombo, WM_SETTEXT, (WPARAM) 0, (LPARAM) buf);
\r
6186 if (appData.icsActive) {
\r
6187 CheckDlgButton(hDlg, OPT_ChessServer, BST_CHECKED);
\r
6189 else if (appData.noChessProgram) {
\r
6190 CheckDlgButton(hDlg, OPT_View, BST_CHECKED);
\r
6193 CheckDlgButton(hDlg, OPT_ChessEngine, BST_CHECKED);
\r
6196 SetStartupDialogEnables(hDlg);
\r
6200 switch (LOWORD(wParam)) {
\r
6202 if (IsDlgButtonChecked(hDlg, OPT_ChessEngine)) {
\r
6203 safeStrCpy(buf, "/fcp=", sizeof(buf)/sizeof(buf[0]) );
\r
6204 GetDlgItemText(hDlg, OPT_ChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));
\r
6206 comboLine = strdup(p+5); // [HGM] recent: remember complete line of first combobox
\r
6207 ParseArgs(StringGet, &p);
\r
6208 safeStrCpy(buf, singleList ? "/fcp=" : "/scp=", sizeof(buf)/sizeof(buf[0]) );
\r
6209 GetDlgItemText(hDlg, OPT_SecondChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));
\r
6211 SwapEngines(singleList); // temporarily swap first and second, to load a second 'first', ...
\r
6212 ParseArgs(StringGet, &p);
\r
6213 SwapEngines(singleList); // ... and then make it 'second'
\r
6214 appData.noChessProgram = FALSE;
\r
6215 appData.icsActive = FALSE;
\r
6216 } else if (IsDlgButtonChecked(hDlg, OPT_ChessServer)) {
\r
6217 safeStrCpy(buf, "/ics /icshost=", sizeof(buf)/sizeof(buf[0]) );
\r
6218 GetDlgItemText(hDlg, OPT_ChessServerName, buf + strlen(buf), sizeof(buf) - strlen(buf));
\r
6220 ParseArgs(StringGet, &p);
\r
6221 if (appData.zippyPlay) {
\r
6222 safeStrCpy(buf, "/fcp=", sizeof(buf)/sizeof(buf[0]) );
\r
6223 GetDlgItemText(hDlg, OPT_ChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));
\r
6225 ParseArgs(StringGet, &p);
\r
6227 } else if (IsDlgButtonChecked(hDlg, OPT_View)) {
\r
6228 appData.noChessProgram = TRUE;
\r
6229 appData.icsActive = FALSE;
\r
6231 MessageBox(hDlg, _("Choose an option, or cancel to exit"),
\r
6232 _("Option Error"), MB_OK|MB_ICONEXCLAMATION);
\r
6235 if (IsDlgButtonChecked(hDlg, OPT_AnyAdditional)) {
\r
6236 GetDlgItemText(hDlg, OPT_AdditionalOptions, buf, sizeof(buf));
\r
6238 ParseArgs(StringGet, &p);
\r
6240 EndDialog(hDlg, TRUE);
\r
6247 case IDM_HELPCONTENTS:
\r
6248 if (!WinHelp (hDlg, "winboard.hlp", HELP_KEY,(DWORD)(LPSTR)"CONTENTS")) {
\r
6249 MessageBox (GetFocus(),
\r
6250 _("Unable to activate help"),
\r
6251 szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);
\r
6256 SetStartupDialogEnables(hDlg);
\r
6264 /*---------------------------------------------------------------------------*\
\r
6266 * About box dialog functions
\r
6268 \*---------------------------------------------------------------------------*/
\r
6270 /* Process messages for "About" dialog box */
\r
6272 About(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
6274 switch (message) {
\r
6275 case WM_INITDIALOG: /* message: initialize dialog box */
\r
6276 /* Center the dialog over the application window */
\r
6277 CenterWindow (hDlg, GetWindow (hDlg, GW_OWNER));
\r
6278 SetDlgItemText(hDlg, ABOUTBOX_Version, programVersion);
\r
6279 Translate(hDlg, ABOUTBOX);
\r
6283 case WM_COMMAND: /* message: received a command */
\r
6284 if (LOWORD(wParam) == IDOK /* "OK" box selected? */
\r
6285 || LOWORD(wParam) == IDCANCEL) { /* System menu close command? */
\r
6286 EndDialog(hDlg, TRUE); /* Exit the dialog */
\r
6294 /*---------------------------------------------------------------------------*\
\r
6296 * Comment Dialog functions
\r
6298 \*---------------------------------------------------------------------------*/
\r
6301 CommentDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
6303 static HANDLE hwndText = NULL;
\r
6304 int len, newSizeX, newSizeY, flags;
\r
6305 static int sizeX, sizeY;
\r
6310 switch (message) {
\r
6311 case WM_INITDIALOG: /* message: initialize dialog box */
\r
6312 /* Initialize the dialog items */
\r
6313 Translate(hDlg, DLG_EditComment);
\r
6314 hwndText = GetDlgItem(hDlg, OPT_CommentText);
\r
6315 SetDlgItemText(hDlg, OPT_CommentText, commentText);
\r
6316 EnableWindow(GetDlgItem(hDlg, OPT_CancelComment), editComment);
\r
6317 EnableWindow(GetDlgItem(hDlg, OPT_ClearComment), editComment);
\r
6318 EnableWindow(GetDlgItem(hDlg, OPT_EditComment), !editComment);
\r
6319 SendMessage(hwndText, EM_SETREADONLY, !editComment, 0);
\r
6320 SetWindowText(hDlg, commentTitle);
\r
6321 if (editComment) {
\r
6322 SetFocus(hwndText);
\r
6324 SetFocus(GetDlgItem(hDlg, IDOK));
\r
6326 SendMessage(GetDlgItem(hDlg, OPT_CommentText),
\r
6327 WM_SETFONT, (WPARAM)font[boardSize][COMMENT_FONT]->hf,
\r
6328 MAKELPARAM(FALSE, 0));
\r
6329 /* Size and position the dialog */
\r
6330 if (!commentDialog) {
\r
6331 commentDialog = hDlg;
\r
6332 flags = SWP_NOZORDER;
\r
6333 GetClientRect(hDlg, &rect);
\r
6334 sizeX = rect.right;
\r
6335 sizeY = rect.bottom;
\r
6336 if (wpComment.x != CW_USEDEFAULT && wpComment.y != CW_USEDEFAULT &&
\r
6337 wpComment.width != CW_USEDEFAULT && wpComment.height != CW_USEDEFAULT) {
\r
6338 WINDOWPLACEMENT wp;
\r
6339 EnsureOnScreen(&wpComment.x, &wpComment.y, 0, 0);
\r
6340 wp.length = sizeof(WINDOWPLACEMENT);
\r
6342 wp.showCmd = SW_SHOW;
\r
6343 wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;
\r
6344 wp.rcNormalPosition.left = wpComment.x;
\r
6345 wp.rcNormalPosition.right = wpComment.x + wpComment.width;
\r
6346 wp.rcNormalPosition.top = wpComment.y;
\r
6347 wp.rcNormalPosition.bottom = wpComment.y + wpComment.height;
\r
6348 SetWindowPlacement(hDlg, &wp);
\r
6350 GetClientRect(hDlg, &rect);
\r
6351 newSizeX = rect.right;
\r
6352 newSizeY = rect.bottom;
\r
6353 ResizeEditPlusButtons(hDlg, hwndText, sizeX, sizeY,
\r
6354 newSizeX, newSizeY);
\r
6359 SendDlgItemMessage( hDlg, OPT_CommentText, EM_SETEVENTMASK, 0, ENM_MOUSEEVENTS | ENM_KEYEVENTS );
\r
6362 case WM_COMMAND: /* message: received a command */
\r
6363 switch (LOWORD(wParam)) {
\r
6365 if (editComment) {
\r
6367 /* Read changed options from the dialog box */
\r
6368 hwndText = GetDlgItem(hDlg, OPT_CommentText);
\r
6369 len = GetWindowTextLength(hwndText);
\r
6370 str = (char *) malloc(len + 1);
\r
6371 GetWindowText(hwndText, str, len + 1);
\r
6380 ReplaceComment(commentIndex, str);
\r
6387 case OPT_CancelComment:
\r
6391 case OPT_ClearComment:
\r
6392 SetDlgItemText(hDlg, OPT_CommentText, "");
\r
6395 case OPT_EditComment:
\r
6396 EditCommentEvent();
\r
6404 case WM_NOTIFY: // [HGM] vari: cloned from whistory.c
\r
6405 if( wParam == OPT_CommentText ) {
\r
6406 MSGFILTER * lpMF = (MSGFILTER *) lParam;
\r
6408 if( lpMF->msg == WM_RBUTTONDOWN && (lpMF->wParam & (MK_CONTROL | MK_SHIFT)) == 0 ||
\r
6409 lpMF->msg == WM_CHAR && lpMF->wParam == '\022' ) {
\r
6413 pt.x = LOWORD( lpMF->lParam );
\r
6414 pt.y = HIWORD( lpMF->lParam );
\r
6416 if(lpMF->msg == WM_CHAR) {
\r
6418 SendDlgItemMessage( hDlg, OPT_CommentText, EM_EXGETSEL, 0, (LPARAM) &sel );
\r
6419 index = sel.cpMin;
\r
6421 index = SendDlgItemMessage( hDlg, OPT_CommentText, EM_CHARFROMPOS, 0, (LPARAM) &pt );
\r
6423 hwndText = GetDlgItem(hDlg, OPT_CommentText); // cloned from above
\r
6424 len = GetWindowTextLength(hwndText);
\r
6425 str = (char *) malloc(len + 1);
\r
6426 GetWindowText(hwndText, str, len + 1);
\r
6427 ReplaceComment(commentIndex, str);
\r
6428 if(commentIndex != currentMove) ToNrEvent(commentIndex);
\r
6429 LoadVariation( index, str ); // [HGM] also does the actual moving to it, now
\r
6432 /* Zap the message for good: apparently, returning non-zero is not enough */
\r
6433 lpMF->msg = WM_USER;
\r
6441 newSizeX = LOWORD(lParam);
\r
6442 newSizeY = HIWORD(lParam);
\r
6443 ResizeEditPlusButtons(hDlg, hwndText, sizeX, sizeY, newSizeX, newSizeY);
\r
6448 case WM_GETMINMAXINFO:
\r
6449 /* Prevent resizing window too small */
\r
6450 mmi = (MINMAXINFO *) lParam;
\r
6451 mmi->ptMinTrackSize.x = 100;
\r
6452 mmi->ptMinTrackSize.y = 100;
\r
6459 EitherCommentPopUp(int index, char *title, char *str, BOOLEAN edit)
\r
6464 CheckMenuItem(GetMenu(hwndMain), IDM_Comment, edit ? MF_CHECKED : MF_UNCHECKED);
\r
6466 if (str == NULL) str = "";
\r
6467 p = (char *) malloc(2 * strlen(str) + 2);
\r
6470 if (*str == '\n') *q++ = '\r';
\r
6474 if (commentText != NULL) free(commentText);
\r
6476 commentIndex = index;
\r
6477 commentTitle = title;
\r
6479 editComment = edit;
\r
6481 if (commentDialog) {
\r
6482 SendMessage(commentDialog, WM_INITDIALOG, 0, 0);
\r
6483 if (!commentUp) ShowWindow(commentDialog, SW_SHOW);
\r
6485 lpProc = MakeProcInstance((FARPROC)CommentDialog, hInst);
\r
6486 CreateDialog(hInst, MAKEINTRESOURCE(DLG_EditComment),
\r
6487 hwndMain, (DLGPROC)lpProc);
\r
6488 FreeProcInstance(lpProc);
\r
6494 /*---------------------------------------------------------------------------*\
\r
6496 * Type-in move dialog functions
\r
6498 \*---------------------------------------------------------------------------*/
\r
6501 TypeInMoveDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
6503 char move[MSG_SIZ];
\r
6506 switch (message) {
\r
6507 case WM_INITDIALOG:
\r
6508 move[0] = (char) lParam;
\r
6509 move[1] = NULLCHAR;
\r
6510 CenterWindowEx(hDlg, GetWindow(hDlg, GW_OWNER), 1 );
\r
6511 Translate(hDlg, DLG_TypeInMove);
\r
6512 hInput = GetDlgItem(hDlg, OPT_Move);
\r
6513 SetWindowText(hInput, move);
\r
6515 SendMessage(hInput, EM_SETSEL, (WPARAM)9999, (LPARAM)9999);
\r
6519 switch (LOWORD(wParam)) {
\r
6522 shiftKey = GetKeyState(VK_SHIFT) < 0; // [HGM] remember last shift status
\r
6523 GetDlgItemText(hDlg, OPT_Move, move, sizeof(move));
\r
6524 TypeInDoneEvent(move);
\r
6525 EndDialog(hDlg, TRUE);
\r
6528 EndDialog(hDlg, FALSE);
\r
6539 PopUpMoveDialog(char firstchar)
\r
6543 lpProc = MakeProcInstance((FARPROC)TypeInMoveDialog, hInst);
\r
6544 DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_TypeInMove),
\r
6545 hwndMain, (DLGPROC)lpProc, (LPARAM)firstchar);
\r
6546 FreeProcInstance(lpProc);
\r
6549 /*---------------------------------------------------------------------------*\
\r
6551 * Type-in name dialog functions
\r
6553 \*---------------------------------------------------------------------------*/
\r
6556 TypeInNameDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
6558 char move[MSG_SIZ];
\r
6561 switch (message) {
\r
6562 case WM_INITDIALOG:
\r
6563 move[0] = (char) lParam;
\r
6564 move[1] = NULLCHAR;
\r
6565 CenterWindowEx(hDlg, GetWindow(hDlg, GW_OWNER), 1 );
\r
6566 Translate(hDlg, DLG_TypeInName);
\r
6567 hInput = GetDlgItem(hDlg, OPT_Name);
\r
6568 SetWindowText(hInput, move);
\r
6570 SendMessage(hInput, EM_SETSEL, (WPARAM)9999, (LPARAM)9999);
\r
6574 switch (LOWORD(wParam)) {
\r
6576 GetDlgItemText(hDlg, OPT_Name, move, sizeof(move));
\r
6577 appData.userName = strdup(move);
\r
6580 if(gameMode == MachinePlaysWhite || gameMode == MachinePlaysBlack) {
\r
6581 snprintf(move, MSG_SIZ, "%s vs. %s", gameInfo.white, gameInfo.black);
\r
6582 DisplayTitle(move);
\r
6586 EndDialog(hDlg, TRUE);
\r
6589 EndDialog(hDlg, FALSE);
\r
6600 PopUpNameDialog(char firstchar)
\r
6604 lpProc = MakeProcInstance((FARPROC)TypeInNameDialog, hInst);
\r
6605 DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_TypeInName),
\r
6606 hwndMain, (DLGPROC)lpProc, (LPARAM)firstchar);
\r
6607 FreeProcInstance(lpProc);
\r
6610 /*---------------------------------------------------------------------------*\
\r
6614 \*---------------------------------------------------------------------------*/
\r
6616 /* Nonmodal error box */
\r
6617 LRESULT CALLBACK ErrorDialog(HWND hDlg, UINT message,
\r
6618 WPARAM wParam, LPARAM lParam);
\r
6621 ErrorPopUp(char *title, char *content)
\r
6625 BOOLEAN modal = hwndMain == NULL;
\r
6643 strncpy(errorTitle, title, sizeof(errorTitle));
\r
6644 errorTitle[sizeof(errorTitle) - 1] = '\0';
\r
6647 MessageBox(NULL, errorMessage, errorTitle, MB_OK|MB_ICONEXCLAMATION);
\r
6649 lpProc = MakeProcInstance((FARPROC)ErrorDialog, hInst);
\r
6650 CreateDialog(hInst, MAKEINTRESOURCE(DLG_Error),
\r
6651 hwndMain, (DLGPROC)lpProc);
\r
6652 FreeProcInstance(lpProc);
\r
6659 if (!appData.popupMoveErrors && moveErrorMessageUp) DisplayMessage("", "");
\r
6660 if (errorDialog == NULL) return;
\r
6661 DestroyWindow(errorDialog);
\r
6662 errorDialog = NULL;
\r
6663 if(errorExitStatus) ExitEvent(errorExitStatus);
\r
6667 ErrorDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
6672 switch (message) {
\r
6673 case WM_INITDIALOG:
\r
6674 GetWindowRect(hDlg, &rChild);
\r
6677 SetWindowPos(hDlg, NULL, rChild.left,
\r
6678 rChild.top + boardRect.top - (rChild.bottom - rChild.top),
\r
6679 0, 0, SWP_NOZORDER|SWP_NOSIZE);
\r
6683 [AS] It seems that the above code wants to move the dialog up in the "caption
\r
6684 area" of the main window, but it uses the dialog height as an hard-coded constant,
\r
6685 and it doesn't work when you resize the dialog.
\r
6686 For now, just give it a default position.
\r
6688 SetWindowPos(hDlg, NULL, boardRect.left+8, boardRect.top+8, 0, 0, SWP_NOZORDER|SWP_NOSIZE);
\r
6689 Translate(hDlg, DLG_Error);
\r
6691 errorDialog = hDlg;
\r
6692 SetWindowText(hDlg, errorTitle);
\r
6693 hwndText = GetDlgItem(hDlg, OPT_ErrorText);
\r
6694 SetDlgItemText(hDlg, OPT_ErrorText, errorMessage);
\r
6698 switch (LOWORD(wParam)) {
\r
6701 if (errorDialog == hDlg) errorDialog = NULL;
\r
6702 DestroyWindow(hDlg);
\r
6714 HWND gothicDialog = NULL;
\r
6717 GothicDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
6721 int height = GetSystemMetrics(SM_CYCAPTION)+GetSystemMetrics(SM_CYFRAME);
\r
6723 switch (message) {
\r
6724 case WM_INITDIALOG:
\r
6725 GetWindowRect(hDlg, &rChild);
\r
6727 SetWindowPos(hDlg, NULL, wpMain.x, wpMain.y-height, wpMain.width, height,
\r
6731 [AS] It seems that the above code wants to move the dialog up in the "caption
\r
6732 area" of the main window, but it uses the dialog height as an hard-coded constant,
\r
6733 and it doesn't work when you resize the dialog.
\r
6734 For now, just give it a default position.
\r
6736 gothicDialog = hDlg;
\r
6737 SetWindowText(hDlg, errorTitle);
\r
6738 hwndText = GetDlgItem(hDlg, OPT_ErrorText);
\r
6739 SetDlgItemText(hDlg, OPT_ErrorText, errorMessage);
\r
6743 switch (LOWORD(wParam)) {
\r
6746 if (errorDialog == hDlg) errorDialog = NULL;
\r
6747 DestroyWindow(hDlg);
\r
6759 GothicPopUp(char *title, VariantClass variant)
\r
6762 static char *lastTitle;
\r
6764 strncpy(errorTitle, title, sizeof(errorTitle));
\r
6765 errorTitle[sizeof(errorTitle) - 1] = '\0';
\r
6767 if(lastTitle != title && gothicDialog != NULL) {
\r
6768 DestroyWindow(gothicDialog);
\r
6769 gothicDialog = NULL;
\r
6771 if(variant != VariantNormal && gothicDialog == NULL) {
\r
6772 title = lastTitle;
\r
6773 lpProc = MakeProcInstance((FARPROC)GothicDialog, hInst);
\r
6774 CreateDialog(hInst, MAKEINTRESOURCE(DLG_Error),
\r
6775 hwndMain, (DLGPROC)lpProc);
\r
6776 FreeProcInstance(lpProc);
\r
6781 /*---------------------------------------------------------------------------*\
\r
6783 * Ics Interaction console functions
\r
6785 \*---------------------------------------------------------------------------*/
\r
6787 #define HISTORY_SIZE 64
\r
6788 static char *history[HISTORY_SIZE];
\r
6789 int histIn = 0, histP = 0;
\r
6792 SaveInHistory(char *cmd)
\r
6794 if (history[histIn] != NULL) {
\r
6795 free(history[histIn]);
\r
6796 history[histIn] = NULL;
\r
6798 if (*cmd == NULLCHAR) return;
\r
6799 history[histIn] = StrSave(cmd);
\r
6800 histIn = (histIn + 1) % HISTORY_SIZE;
\r
6801 if (history[histIn] != NULL) {
\r
6802 free(history[histIn]);
\r
6803 history[histIn] = NULL;
\r
6809 PrevInHistory(char *cmd)
\r
6812 if (histP == histIn) {
\r
6813 if (history[histIn] != NULL) free(history[histIn]);
\r
6814 history[histIn] = StrSave(cmd);
\r
6816 newhp = (histP - 1 + HISTORY_SIZE) % HISTORY_SIZE;
\r
6817 if (newhp == histIn || history[newhp] == NULL) return NULL;
\r
6819 return history[histP];
\r
6825 if (histP == histIn) return NULL;
\r
6826 histP = (histP + 1) % HISTORY_SIZE;
\r
6827 return history[histP];
\r
6831 LoadIcsTextMenu(IcsTextMenuEntry *e)
\r
6835 hmenu = LoadMenu(hInst, "TextMenu");
\r
6836 h = GetSubMenu(hmenu, 0);
\r
6838 if (strcmp(e->item, "-") == 0) {
\r
6839 AppendMenu(h, MF_SEPARATOR, 0, 0);
\r
6840 } else { // [HGM] re-written a bit to use only one AppendMenu call for both cases (| or no |)
\r
6841 int flags = MF_STRING, j = 0;
\r
6842 if (e->item[0] == '|') {
\r
6843 flags |= MF_MENUBARBREAK;
\r
6846 if(!strcmp(e->command, "none")) flags |= MF_GRAYED; // [HGM] chatclick: provide inactive dummy
\r
6847 AppendMenu(h, flags, IDM_CommandX + i, e->item + j);
\r
6855 WNDPROC consoleTextWindowProc;
\r
6858 CommandX(HWND hwnd, char *command, BOOLEAN getname, BOOLEAN immediate)
\r
6860 char buf[MSG_SIZ], name[MSG_SIZ];
\r
6861 HWND hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
6865 SetWindowText(hInput, command);
\r
6867 SendMessage(hInput, WM_CHAR, '\r', 0);
\r
6869 sel.cpMin = 999999;
\r
6870 sel.cpMax = 999999;
\r
6871 SendMessage(hInput, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
6876 SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);
\r
6877 if (sel.cpMin == sel.cpMax) {
\r
6878 /* Expand to surrounding word */
\r
6881 tr.chrg.cpMax = sel.cpMin;
\r
6882 tr.chrg.cpMin = --sel.cpMin;
\r
6883 if (sel.cpMin < 0) break;
\r
6884 tr.lpstrText = name;
\r
6885 SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr);
\r
6886 } while (isalpha(name[0]) || isdigit(name[0]) || name[0] == '-');
\r
6890 tr.chrg.cpMin = sel.cpMax;
\r
6891 tr.chrg.cpMax = ++sel.cpMax;
\r
6892 tr.lpstrText = name;
\r
6893 if (SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr) < 1) break;
\r
6894 } while (isalpha(name[0]) || isdigit(name[0]) || name[0] == '-');
\r
6897 if (sel.cpMax == sel.cpMin || sel.cpMax - sel.cpMin > MSG_SIZ/2) {
\r
6898 MessageBeep(MB_ICONEXCLAMATION);
\r
6902 tr.lpstrText = name;
\r
6903 SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr);
\r
6905 if (sel.cpMax - sel.cpMin > MSG_SIZ/2) {
\r
6906 MessageBeep(MB_ICONEXCLAMATION);
\r
6909 SendMessage(hwnd, EM_GETSELTEXT, 0, (LPARAM) name);
\r
6912 if(strstr(command, "%s")) snprintf(buf, MSG_SIZ, command, name); else
\r
6913 snprintf(buf, MSG_SIZ, "%s %s", command, name);
\r
6914 SetWindowText(hInput, buf);
\r
6915 SendMessage(hInput, WM_CHAR, '\r', 0);
\r
6917 if(!strcmp(command, "chat")) { ChatPopUp(name); return; }
\r
6918 snprintf(buf, MSG_SIZ, "%s %s ", command, name); /* trailing space */
\r
6919 SetWindowText(hInput, buf);
\r
6920 sel.cpMin = 999999;
\r
6921 sel.cpMax = 999999;
\r
6922 SendMessage(hInput, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
6928 ConsoleTextSubclass(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
\r
6933 switch (message) {
\r
6935 if (!(GetKeyState(VK_CONTROL) & ~1)) break;
\r
6936 if(wParam=='R') return 0;
\r
6939 SendMessage(hwnd, EM_LINESCROLL, 0, -999999);
\r
6942 sel.cpMin = 999999;
\r
6943 sel.cpMax = 999999;
\r
6944 SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
6945 SendMessage(hwnd, EM_SCROLLCARET, 0, 0);
\r
6950 if(wParam != '\022') {
\r
6951 if (wParam == '\t') {
\r
6952 if (GetKeyState(VK_SHIFT) < 0) {
\r
6954 if (IsIconic(hwndMain)) ShowWindow(hwndMain, SW_RESTORE);
\r
6955 if (buttonDesc[0].hwnd) {
\r
6956 SetFocus(buttonDesc[0].hwnd);
\r
6958 SetFocus(hwndMain);
\r
6962 SetFocus(GetDlgItem(hwndConsole, OPT_ConsoleInput));
\r
6965 hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
6966 JAWS_DELETE( SetFocus(hInput); )
\r
6967 SendMessage(hInput, message, wParam, lParam);
\r
6970 } // [HGM] navigate: for Ctrl+R, flow into next case (moved up here) to summon up menu
\r
6972 case WM_RBUTTONDOWN:
\r
6973 if (!(GetKeyState(VK_SHIFT) & ~1)) {
\r
6974 /* Move selection here if it was empty */
\r
6976 pt.x = LOWORD(lParam);
\r
6977 pt.y = HIWORD(lParam);
\r
6978 SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);
\r
6979 if (sel.cpMin == sel.cpMax) {
\r
6980 if(lParam != -1) sel.cpMin = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&pt); /*doc is wrong*/
\r
6981 sel.cpMax = sel.cpMin;
\r
6982 SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
6984 SendMessage(hwnd, EM_HIDESELECTION, FALSE, FALSE);
\r
6985 { // [HGM] chatclick: code moved here from WM_RBUTTONUP case, to have menu appear on down-click
\r
6987 HMENU hmenu = LoadIcsTextMenu(icsTextMenuEntry);
\r
6988 SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);
\r
6989 if (sel.cpMin == sel.cpMax) {
\r
6990 EnableMenuItem(hmenu, IDM_Copy, MF_BYCOMMAND|MF_GRAYED);
\r
6991 EnableMenuItem(hmenu, IDM_QuickPaste, MF_BYCOMMAND|MF_GRAYED);
\r
6993 if (!IsClipboardFormatAvailable(CF_TEXT)) {
\r
6994 EnableMenuItem(hmenu, IDM_Paste, MF_BYCOMMAND|MF_GRAYED);
\r
6996 pt.x = LOWORD(lParam)-30; // [HGM] chatclick: make menu pop up with pointer above upper-right item
\r
6997 pt.y = HIWORD(lParam)-10; // make it appear as if mouse moved there, so it will be selected on up-click
\r
6998 PostMessage(hwnd, WM_MOUSEMOVE, wParam, lParam+5);
\r
6999 MenuPopup(hwnd, pt, hmenu, -1);
\r
7003 case WM_RBUTTONUP:
\r
7004 if (GetKeyState(VK_SHIFT) & ~1) {
\r
7005 SendDlgItemMessage(hwndConsole, OPT_ConsoleText,
\r
7006 WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);
\r
7010 hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
7012 return SendMessage(hInput, message, wParam, lParam);
\r
7013 case WM_MBUTTONDOWN:
\r
7014 return SendMessage(hwnd, WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);
\r
7016 switch (LOWORD(wParam)) {
\r
7017 case IDM_QuickPaste:
\r
7019 SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);
\r
7020 if (sel.cpMin == sel.cpMax) {
\r
7021 MessageBeep(MB_ICONEXCLAMATION);
\r
7024 SendMessage(hwnd, WM_COPY, 0, 0);
\r
7025 hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
7026 SendMessage(hInput, WM_PASTE, 0, 0);
\r
7031 SendMessage(hwnd, WM_CUT, 0, 0);
\r
7034 SendMessage(hwnd, WM_PASTE, 0, 0);
\r
7037 SendMessage(hwnd, WM_COPY, 0, 0);
\r
7041 int i = LOWORD(wParam) - IDM_CommandX;
\r
7042 if (i >= 0 && i < ICS_TEXT_MENU_SIZE &&
\r
7043 icsTextMenuEntry[i].command != NULL) {
\r
7044 CommandX(hwnd, icsTextMenuEntry[i].command,
\r
7045 icsTextMenuEntry[i].getname,
\r
7046 icsTextMenuEntry[i].immediate);
\r
7054 return (*consoleTextWindowProc)(hwnd, message, wParam, lParam);
\r
7057 WNDPROC consoleInputWindowProc;
\r
7060 ConsoleInputSubclass(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
\r
7062 char buf[MSG_SIZ];
\r
7064 static BOOL sendNextChar = FALSE;
\r
7065 static BOOL quoteNextChar = FALSE;
\r
7066 InputSource *is = consoleInputSource;
\r
7070 switch (message) {
\r
7072 if (!appData.localLineEditing || sendNextChar) {
\r
7073 is->buf[0] = (CHAR) wParam;
\r
7075 SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);
\r
7076 sendNextChar = FALSE;
\r
7079 if (quoteNextChar) {
\r
7080 buf[0] = (char) wParam;
\r
7081 buf[1] = NULLCHAR;
\r
7082 SendMessage(hwnd, EM_REPLACESEL, TRUE, (LPARAM) buf);
\r
7083 quoteNextChar = FALSE;
\r
7087 case '\r': /* Enter key */
\r
7088 is->count = GetWindowText(hwnd, is->buf, INPUT_SOURCE_BUF_SIZE-1);
\r
7089 if (consoleEcho) SaveInHistory(is->buf);
\r
7090 is->buf[is->count++] = '\n';
\r
7091 is->buf[is->count] = NULLCHAR;
\r
7092 SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);
\r
7093 if (consoleEcho) {
\r
7094 ConsoleOutput(is->buf, is->count, TRUE);
\r
7095 } else if (appData.localLineEditing) {
\r
7096 ConsoleOutput("\n", 1, TRUE);
\r
7099 case '\033': /* Escape key */
\r
7100 SetWindowText(hwnd, "");
\r
7101 cf.cbSize = sizeof(CHARFORMAT);
\r
7102 cf.dwMask = CFM_COLOR|CFM_BOLD|CFM_ITALIC|CFM_UNDERLINE|CFM_STRIKEOUT;
\r
7103 if (consoleEcho) {
\r
7104 cf.crTextColor = textAttribs[ColorNormal].color;
\r
7106 cf.crTextColor = COLOR_ECHOOFF;
\r
7108 cf.dwEffects = textAttribs[ColorNormal].effects;
\r
7109 SendMessage(hwnd, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf);
\r
7111 case '\t': /* Tab key */
\r
7112 if (GetKeyState(VK_SHIFT) < 0) {
\r
7114 SetFocus(GetDlgItem(hwndConsole, OPT_ConsoleText));
\r
7117 if (IsIconic(hwndMain)) ShowWindow(hwndMain, SW_RESTORE);
\r
7118 if (buttonDesc[0].hwnd) {
\r
7119 SetFocus(buttonDesc[0].hwnd);
\r
7121 SetFocus(hwndMain);
\r
7125 case '\023': /* Ctrl+S */
\r
7126 sendNextChar = TRUE;
\r
7128 case '\021': /* Ctrl+Q */
\r
7129 quoteNextChar = TRUE;
\r
7139 GetWindowText(hwnd, buf, MSG_SIZ);
\r
7140 p = PrevInHistory(buf);
\r
7142 SetWindowText(hwnd, p);
\r
7143 sel.cpMin = 999999;
\r
7144 sel.cpMax = 999999;
\r
7145 SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
7150 p = NextInHistory();
\r
7152 SetWindowText(hwnd, p);
\r
7153 sel.cpMin = 999999;
\r
7154 sel.cpMax = 999999;
\r
7155 SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
7161 if (!(GetKeyState(VK_CONTROL) & ~1)) break;
\r
7165 SendDlgItemMessage(hwndConsole, OPT_ConsoleText, message, wParam, lParam);
\r
7169 case WM_MBUTTONDOWN:
\r
7170 SendDlgItemMessage(hwndConsole, OPT_ConsoleText,
\r
7171 WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);
\r
7173 case WM_RBUTTONUP:
\r
7174 if (GetKeyState(VK_SHIFT) & ~1) {
\r
7175 SendDlgItemMessage(hwndConsole, OPT_ConsoleText,
\r
7176 WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);
\r
7180 hmenu = LoadMenu(hInst, "InputMenu");
\r
7181 SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);
\r
7182 if (sel.cpMin == sel.cpMax) {
\r
7183 EnableMenuItem(hmenu, IDM_Copy, MF_BYCOMMAND|MF_GRAYED);
\r
7184 EnableMenuItem(hmenu, IDM_Cut, MF_BYCOMMAND|MF_GRAYED);
\r
7186 if (!IsClipboardFormatAvailable(CF_TEXT)) {
\r
7187 EnableMenuItem(hmenu, IDM_Paste, MF_BYCOMMAND|MF_GRAYED);
\r
7189 pt.x = LOWORD(lParam);
\r
7190 pt.y = HIWORD(lParam);
\r
7191 MenuPopup(hwnd, pt, hmenu, -1);
\r
7195 switch (LOWORD(wParam)) {
\r
7197 SendMessage(hwnd, EM_UNDO, 0, 0);
\r
7199 case IDM_SelectAll:
\r
7201 sel.cpMax = -1; /*999999?*/
\r
7202 SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
7205 SendMessage(hwnd, WM_CUT, 0, 0);
\r
7208 SendMessage(hwnd, WM_PASTE, 0, 0);
\r
7211 SendMessage(hwnd, WM_COPY, 0, 0);
\r
7216 return (*consoleInputWindowProc)(hwnd, message, wParam, lParam);
\r
7219 #define CO_MAX 100000
\r
7220 #define CO_TRIM 1000
\r
7223 ConsoleWndProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
7225 static SnapData sd;
\r
7226 HWND hText, hInput;
\r
7228 static int sizeX, sizeY;
\r
7229 int newSizeX, newSizeY;
\r
7233 hText = GetDlgItem(hDlg, OPT_ConsoleText);
\r
7234 hInput = GetDlgItem(hDlg, OPT_ConsoleInput);
\r
7236 switch (message) {
\r
7238 if (((NMHDR*)lParam)->code == EN_LINK)
\r
7240 ENLINK *pLink = (ENLINK*)lParam;
\r
7241 if (pLink->msg == WM_LBUTTONUP)
\r
7245 tr.chrg = pLink->chrg;
\r
7246 tr.lpstrText = malloc(1+tr.chrg.cpMax-tr.chrg.cpMin);
\r
7247 SendMessage(hText, EM_GETTEXTRANGE, 0, (LPARAM)&tr);
\r
7248 ShellExecute(NULL, "open", tr.lpstrText, NULL, NULL, SW_SHOW);
\r
7249 free(tr.lpstrText);
\r
7253 case WM_INITDIALOG: /* message: initialize dialog box */
\r
7254 hwndConsole = hDlg;
\r
7256 consoleTextWindowProc = (WNDPROC)
\r
7257 SetWindowLongPtr(hText, GWLP_WNDPROC, (LONG_PTR) ConsoleTextSubclass);
\r
7258 SendMessage(hText, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);
\r
7259 consoleInputWindowProc = (WNDPROC)
\r
7260 SetWindowLongPtr(hInput, GWLP_WNDPROC, (LONG_PTR) ConsoleInputSubclass);
\r
7261 SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);
\r
7262 Colorize(ColorNormal, TRUE);
\r
7263 SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &consoleCF);
\r
7264 ChangedConsoleFont();
\r
7265 GetClientRect(hDlg, &rect);
\r
7266 sizeX = rect.right;
\r
7267 sizeY = rect.bottom;
\r
7268 if (wpConsole.x != CW_USEDEFAULT && wpConsole.y != CW_USEDEFAULT &&
\r
7269 wpConsole.width != CW_USEDEFAULT && wpConsole.height != CW_USEDEFAULT) {
\r
7270 WINDOWPLACEMENT wp;
\r
7271 EnsureOnScreen(&wpConsole.x, &wpConsole.y, 0, 0);
\r
7272 wp.length = sizeof(WINDOWPLACEMENT);
\r
7274 wp.showCmd = SW_SHOW;
\r
7275 wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;
\r
7276 wp.rcNormalPosition.left = wpConsole.x;
\r
7277 wp.rcNormalPosition.right = wpConsole.x + wpConsole.width;
\r
7278 wp.rcNormalPosition.top = wpConsole.y;
\r
7279 wp.rcNormalPosition.bottom = wpConsole.y + wpConsole.height;
\r
7280 SetWindowPlacement(hDlg, &wp);
\r
7283 // [HGM] Chessknight's change 2004-07-13
\r
7284 else { /* Determine Defaults */
\r
7285 WINDOWPLACEMENT wp;
\r
7286 wpConsole.x = wpMain.width + 1;
\r
7287 wpConsole.y = wpMain.y;
\r
7288 wpConsole.width = screenWidth - wpMain.width;
\r
7289 wpConsole.height = wpMain.height;
\r
7290 EnsureOnScreen(&wpConsole.x, &wpConsole.y, 0, 0);
\r
7291 wp.length = sizeof(WINDOWPLACEMENT);
\r
7293 wp.showCmd = SW_SHOW;
\r
7294 wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;
\r
7295 wp.rcNormalPosition.left = wpConsole.x;
\r
7296 wp.rcNormalPosition.right = wpConsole.x + wpConsole.width;
\r
7297 wp.rcNormalPosition.top = wpConsole.y;
\r
7298 wp.rcNormalPosition.bottom = wpConsole.y + wpConsole.height;
\r
7299 SetWindowPlacement(hDlg, &wp);
\r
7302 // Allow hText to highlight URLs and send notifications on them
\r
7303 wMask = (WORD) SendMessage(hText, EM_GETEVENTMASK, 0, 0L);
\r
7304 SendMessage(hText, EM_SETEVENTMASK, 0, wMask | ENM_LINK);
\r
7305 SendMessage(hText, EM_AUTOURLDETECT, TRUE, 0L);
\r
7306 SetWindowLongPtr(hText, GWLP_USERDATA, 79); // initialize the text window's width
\r
7320 if (IsIconic(hDlg)) break;
\r
7321 newSizeX = LOWORD(lParam);
\r
7322 newSizeY = HIWORD(lParam);
\r
7323 if (sizeX != newSizeX || sizeY != newSizeY) {
\r
7324 RECT rectText, rectInput;
\r
7326 int newTextHeight, newTextWidth;
\r
7327 GetWindowRect(hText, &rectText);
\r
7328 newTextWidth = rectText.right - rectText.left + newSizeX - sizeX;
\r
7329 newTextHeight = rectText.bottom - rectText.top + newSizeY - sizeY;
\r
7330 if (newTextHeight < 0) {
\r
7331 newSizeY += -newTextHeight;
\r
7332 newTextHeight = 0;
\r
7334 SetWindowPos(hText, NULL, 0, 0,
\r
7335 newTextWidth, newTextHeight, SWP_NOZORDER|SWP_NOMOVE);
\r
7336 GetWindowRect(hInput, &rectInput); /* gives screen coords */
\r
7337 pt.x = rectInput.left;
\r
7338 pt.y = rectInput.top + newSizeY - sizeY;
\r
7339 ScreenToClient(hDlg, &pt);
\r
7340 SetWindowPos(hInput, NULL,
\r
7341 pt.x, pt.y, /* needs client coords */
\r
7342 rectInput.right - rectInput.left + newSizeX - sizeX,
\r
7343 rectInput.bottom - rectInput.top, SWP_NOZORDER);
\r
7349 case WM_GETMINMAXINFO:
\r
7350 /* Prevent resizing window too small */
\r
7351 mmi = (MINMAXINFO *) lParam;
\r
7352 mmi->ptMinTrackSize.x = 100;
\r
7353 mmi->ptMinTrackSize.y = 100;
\r
7356 /* [AS] Snapping */
\r
7357 case WM_ENTERSIZEMOVE:
\r
7358 return OnEnterSizeMove( &sd, hDlg, wParam, lParam );
\r
7361 return OnSizing( &sd, hDlg, wParam, lParam );
\r
7364 return OnMoving( &sd, hDlg, wParam, lParam );
\r
7366 case WM_EXITSIZEMOVE:
\r
7367 UpdateICSWidth(hText);
\r
7368 return OnExitSizeMove( &sd, hDlg, wParam, lParam );
\r
7371 return DefWindowProc(hDlg, message, wParam, lParam);
\r
7379 if (hwndConsole) return;
\r
7380 hCons = CreateDialog(hInst, szConsoleName, 0, NULL);
\r
7381 SendMessage(hCons, WM_INITDIALOG, 0, 0);
\r
7386 ConsoleOutput(char* data, int length, int forceVisible)
\r
7391 char buf[CO_MAX+1];
\r
7394 static int delayLF = 0;
\r
7395 CHARRANGE savesel, sel;
\r
7397 if (hwndConsole == NULL || length > CO_MAX-100 || length == 0) return;
\r
7405 while (length--) {
\r
7413 } else if (*p == '\007') {
\r
7414 MyPlaySound(&sounds[(int)SoundBell]);
\r
7421 hText = GetDlgItem(hwndConsole, OPT_ConsoleText);
\r
7422 SendMessage(hText, EM_HIDESELECTION, TRUE, FALSE);
\r
7423 /* Save current selection */
\r
7424 SendMessage(hText, EM_EXGETSEL, 0, (LPARAM)&savesel);
\r
7425 exlen = GetWindowTextLength(hText);
\r
7426 /* Find out whether current end of text is visible */
\r
7427 SendMessage(hText, EM_GETRECT, 0, (LPARAM) &rect);
\r
7428 SendMessage(hText, EM_POSFROMCHAR, (WPARAM) &pEnd, exlen);
\r
7429 /* Trim existing text if it's too long */
\r
7430 if (exlen + (q - buf) > CO_MAX) {
\r
7431 trim = (CO_TRIM > (q - buf)) ? CO_TRIM : (q - buf);
\r
7434 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
7435 SendMessage(hText, EM_REPLACESEL, 0, (LPARAM)"");
\r
7437 savesel.cpMin -= trim;
\r
7438 savesel.cpMax -= trim;
\r
7439 if (exlen < 0) exlen = 0;
\r
7440 if (savesel.cpMin < 0) savesel.cpMin = 0;
\r
7441 if (savesel.cpMax < savesel.cpMin) savesel.cpMax = savesel.cpMin;
\r
7443 /* Append the new text */
\r
7444 sel.cpMin = exlen;
\r
7445 sel.cpMax = exlen;
\r
7446 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
7447 SendMessage(hText, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&consoleCF);
\r
7448 SendMessage(hText, EM_REPLACESEL, 0, (LPARAM) buf);
\r
7449 if (forceVisible || exlen == 0 ||
\r
7450 (rect.left <= pEnd.x && pEnd.x < rect.right &&
\r
7451 rect.top <= pEnd.y && pEnd.y < rect.bottom)) {
\r
7452 /* Scroll to make new end of text visible if old end of text
\r
7453 was visible or new text is an echo of user typein */
\r
7454 sel.cpMin = 9999999;
\r
7455 sel.cpMax = 9999999;
\r
7456 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
7457 SendMessage(hText, EM_HIDESELECTION, FALSE, FALSE);
\r
7458 SendMessage(hText, EM_SCROLLCARET, 0, 0);
\r
7459 SendMessage(hText, EM_HIDESELECTION, TRUE, FALSE);
\r
7461 if (savesel.cpMax == exlen || forceVisible) {
\r
7462 /* Move insert point to new end of text if it was at the old
\r
7463 end of text or if the new text is an echo of user typein */
\r
7464 sel.cpMin = 9999999;
\r
7465 sel.cpMax = 9999999;
\r
7466 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
7468 /* Restore previous selection */
\r
7469 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&savesel);
\r
7471 SendMessage(hText, EM_HIDESELECTION, FALSE, FALSE);
\r
7478 DisplayHoldingsCount(HDC hdc, int x, int y, int rightAlign, int copyNumber)
\r
7482 COLORREF oldFg, oldBg;
\r
7486 if(copyNumber > 1)
\r
7487 snprintf(buf, sizeof(buf)/sizeof(buf[0]),"%d", copyNumber); else buf[0] = 0;
\r
7489 oldFg = SetTextColor(hdc, RGB(255, 255, 255)); /* white */
\r
7490 oldBg = SetBkColor(hdc, RGB(0, 0, 0)); /* black */
\r
7491 oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);
\r
7494 rect.right = x + squareSize;
\r
7496 rect.bottom = y + squareSize;
\r
7499 ExtTextOut(hdc, x + MESSAGE_LINE_LEFTMARGIN
\r
7500 + (rightAlign ? (squareSize*2)/3 : 0),
\r
7501 y, ETO_CLIPPED|ETO_OPAQUE,
\r
7502 &rect, str, strlen(str), NULL);
\r
7504 (void) SetTextColor(hdc, oldFg);
\r
7505 (void) SetBkColor(hdc, oldBg);
\r
7506 (void) SelectObject(hdc, oldFont);
\r
7510 DisplayAClock(HDC hdc, int timeRemaining, int highlight,
\r
7511 RECT *rect, char *color, char *flagFell)
\r
7515 COLORREF oldFg, oldBg;
\r
7518 if (twoBoards && partnerUp) return;
\r
7519 if (appData.clockMode) {
\r
7521 snprintf(buf, sizeof(buf)/sizeof(buf[0]), "%c %s %s", color[0], TimeString(timeRemaining), flagFell);
\r
7523 snprintf(buf, sizeof(buf)/sizeof(buf[0]), "%s:%c%s %s", color, (logoHeight>0 ? 0 : ' '), TimeString(timeRemaining), flagFell);
\r
7530 oldFg = SetTextColor(hdc, RGB(255, 255, 255)); /* white */
\r
7531 oldBg = SetBkColor(hdc, RGB(0, 0, 0)); /* black */
\r
7533 oldFg = SetTextColor(hdc, RGB(0, 0, 0)); /* black */
\r
7534 oldBg = SetBkColor(hdc, RGB(255, 255, 255)); /* white */
\r
7536 oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);
\r
7540 ExtTextOut(hdc, rect->left + MESSAGE_LINE_LEFTMARGIN,
\r
7541 rect->top, ETO_CLIPPED|ETO_OPAQUE,
\r
7542 rect, str, strlen(str), NULL);
\r
7543 if(logoHeight > 0 && appData.clockMode) {
\r
7545 str += strlen(color)+2;
\r
7546 r.top = rect->top + logoHeight/2;
\r
7547 r.left = rect->left;
\r
7548 r.right = rect->right;
\r
7549 r.bottom = rect->bottom;
\r
7550 ExtTextOut(hdc, rect->left + MESSAGE_LINE_LEFTMARGIN,
\r
7551 r.top, ETO_CLIPPED|ETO_OPAQUE,
\r
7552 &r, str, strlen(str), NULL);
\r
7554 (void) SetTextColor(hdc, oldFg);
\r
7555 (void) SetBkColor(hdc, oldBg);
\r
7556 (void) SelectObject(hdc, oldFont);
\r
7561 DoReadFile(HANDLE hFile, char *buf, int count, DWORD *outCount,
\r
7567 if( count <= 0 ) {
\r
7568 if (appData.debugMode) {
\r
7569 fprintf( debugFP, "DoReadFile: trying to read past end of buffer, overflow = %d\n", count );
\r
7572 return ERROR_INVALID_USER_BUFFER;
\r
7575 ResetEvent(ovl->hEvent);
\r
7576 ovl->Offset = ovl->OffsetHigh = 0;
\r
7577 ok = ReadFile(hFile, buf, count, outCount, ovl);
\r
7581 err = GetLastError();
\r
7582 if (err == ERROR_IO_PENDING) {
\r
7583 ok = GetOverlappedResult(hFile, ovl, outCount, TRUE);
\r
7587 err = GetLastError();
\r
7594 DoWriteFile(HANDLE hFile, char *buf, int count, DWORD *outCount,
\r
7599 ResetEvent(ovl->hEvent);
\r
7600 ovl->Offset = ovl->OffsetHigh = 0;
\r
7601 ok = WriteFile(hFile, buf, count, outCount, ovl);
\r
7605 err = GetLastError();
\r
7606 if (err == ERROR_IO_PENDING) {
\r
7607 ok = GetOverlappedResult(hFile, ovl, outCount, TRUE);
\r
7611 err = GetLastError();
\r
7617 /* [AS] If input is line by line and a line exceed the buffer size, force an error */
\r
7618 void CheckForInputBufferFull( InputSource * is )
\r
7620 if( is->lineByLine && (is->next - is->buf) >= INPUT_SOURCE_BUF_SIZE ) {
\r
7621 /* Look for end of line */
\r
7622 char * p = is->buf;
\r
7624 while( p < is->next && *p != '\n' ) {
\r
7628 if( p >= is->next ) {
\r
7629 if (appData.debugMode) {
\r
7630 fprintf( debugFP, "Input line exceeded buffer size (source id=%lu)\n", is->id );
\r
7633 is->error = ERROR_BROKEN_PIPE; /* [AS] Just any non-successful code! */
\r
7634 is->count = (DWORD) -1;
\r
7635 is->next = is->buf;
\r
7641 InputThread(LPVOID arg)
\r
7646 is = (InputSource *) arg;
\r
7647 ovl.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
\r
7648 ovl.Internal = ovl.InternalHigh = ovl.Offset = ovl.OffsetHigh = 0;
\r
7649 while (is->hThread != NULL) {
\r
7650 is->error = DoReadFile(is->hFile, is->next,
\r
7651 INPUT_SOURCE_BUF_SIZE - (is->next - is->buf),
\r
7652 &is->count, &ovl);
\r
7653 if (is->error == NO_ERROR) {
\r
7654 is->next += is->count;
\r
7656 if (is->error == ERROR_BROKEN_PIPE) {
\r
7657 /* Correct for MS brain damage. EOF reading a pipe is not an error. */
\r
7660 is->count = (DWORD) -1;
\r
7661 /* [AS] The (is->count <= 0) check below is not useful for unsigned values! */
\r
7666 CheckForInputBufferFull( is );
\r
7668 SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);
\r
7670 if( is->count == ((DWORD) -1) ) break; /* [AS] */
\r
7672 if (is->count <= 0) break; /* Quit on EOF or error */
\r
7675 CloseHandle(ovl.hEvent);
\r
7676 CloseHandle(is->hFile);
\r
7678 if (appData.debugMode) {
\r
7679 fprintf( debugFP, "Input thread terminated (id=%lu, error=%d, count=%ld)\n", is->id, is->error, is->count );
\r
7686 /* Windows 95 beta 2 won't let you do overlapped i/o on a console or pipe */
\r
7688 NonOvlInputThread(LPVOID arg)
\r
7695 is = (InputSource *) arg;
\r
7696 while (is->hThread != NULL) {
\r
7697 is->error = ReadFile(is->hFile, is->next,
\r
7698 INPUT_SOURCE_BUF_SIZE - (is->next - is->buf),
\r
7699 &is->count, NULL) ? NO_ERROR : GetLastError();
\r
7700 if (is->error == NO_ERROR) {
\r
7701 /* Change CRLF to LF */
\r
7702 if (is->next > is->buf) {
\r
7704 i = is->count + 1;
\r
7712 if (prev == '\r' && *p == '\n') {
\r
7724 if (is->error == ERROR_BROKEN_PIPE) {
\r
7725 /* Correct for MS brain damage. EOF reading a pipe is not an error. */
\r
7728 is->count = (DWORD) -1;
\r
7732 CheckForInputBufferFull( is );
\r
7734 SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);
\r
7736 if( is->count == ((DWORD) -1) ) break; /* [AS] */
\r
7738 if (is->count < 0) break; /* Quit on error */
\r
7740 CloseHandle(is->hFile);
\r
7745 SocketInputThread(LPVOID arg)
\r
7749 is = (InputSource *) arg;
\r
7750 while (is->hThread != NULL) {
\r
7751 is->count = recv(is->sock, is->buf, INPUT_SOURCE_BUF_SIZE, 0);
\r
7752 if ((int)is->count == SOCKET_ERROR) {
\r
7753 is->count = (DWORD) -1;
\r
7754 is->error = WSAGetLastError();
\r
7756 is->error = NO_ERROR;
\r
7757 is->next += is->count;
\r
7758 if (is->count == 0 && is->second == is) {
\r
7759 /* End of file on stderr; quit with no message */
\r
7763 SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);
\r
7765 if( is->count == ((DWORD) -1) ) break; /* [AS] */
\r
7767 if (is->count <= 0) break; /* Quit on EOF or error */
\r
7773 InputEvent(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
\r
7777 is = (InputSource *) lParam;
\r
7778 if (is->lineByLine) {
\r
7779 /* Feed in lines one by one */
\r
7780 char *p = is->buf;
\r
7782 while (q < is->next) {
\r
7783 if (*q++ == '\n') {
\r
7784 (is->func)(is, is->closure, p, q - p, NO_ERROR);
\r
7789 /* Move any partial line to the start of the buffer */
\r
7791 while (p < is->next) {
\r
7796 if (is->error != NO_ERROR || is->count == 0) {
\r
7797 /* Notify backend of the error. Note: If there was a partial
\r
7798 line at the end, it is not flushed through. */
\r
7799 (is->func)(is, is->closure, is->buf, is->count, is->error);
\r
7802 /* Feed in the whole chunk of input at once */
\r
7803 (is->func)(is, is->closure, is->buf, is->count, is->error);
\r
7804 is->next = is->buf;
\r
7808 /*---------------------------------------------------------------------------*\
\r
7810 * Menu enables. Used when setting various modes.
\r
7812 \*---------------------------------------------------------------------------*/
\r
7820 GreyRevert(Boolean grey)
\r
7821 { // [HGM] vari: for retracting variations in local mode
\r
7822 HMENU hmenu = GetMenu(hwndMain);
\r
7823 EnableMenuItem(hmenu, IDM_Revert, MF_BYCOMMAND|(grey ? MF_GRAYED : MF_ENABLED));
\r
7824 EnableMenuItem(hmenu, IDM_Annotate, MF_BYCOMMAND|(grey ? MF_GRAYED : MF_ENABLED));
\r
7828 SetMenuEnables(HMENU hmenu, Enables *enab)
\r
7830 while (enab->item > 0) {
\r
7831 (void) EnableMenuItem(hmenu, enab->item, enab->flags);
\r
7836 Enables gnuEnables[] = {
\r
7837 { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },
\r
7838 { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },
\r
7839 { IDM_IcsClient, MF_BYCOMMAND|MF_GRAYED },
\r
7840 { IDM_Accept, MF_BYCOMMAND|MF_GRAYED },
\r
7841 { IDM_Decline, MF_BYCOMMAND|MF_GRAYED },
\r
7842 { IDM_Rematch, MF_BYCOMMAND|MF_GRAYED },
\r
7843 { IDM_Adjourn, MF_BYCOMMAND|MF_GRAYED },
\r
7844 { IDM_StopExamining, MF_BYCOMMAND|MF_GRAYED },
\r
7845 { IDM_StopObserving, MF_BYCOMMAND|MF_GRAYED },
\r
7846 { IDM_Upload, MF_BYCOMMAND|MF_GRAYED },
\r
7847 { IDM_Revert, MF_BYCOMMAND|MF_GRAYED },
\r
7848 { IDM_Annotate, MF_BYCOMMAND|MF_GRAYED },
\r
7849 { IDM_NewChat, MF_BYCOMMAND|MF_GRAYED },
\r
7851 // Needed to switch from ncp to GNU mode on Engine Load
\r
7852 { ACTION_POS, MF_BYPOSITION|MF_ENABLED },
\r
7853 { IDM_MachineWhite, MF_BYCOMMAND|MF_ENABLED },
\r
7854 { IDM_MachineBlack, MF_BYCOMMAND|MF_ENABLED },
\r
7855 { IDM_TwoMachines, MF_BYCOMMAND|MF_ENABLED },
\r
7856 { IDM_Match, MF_BYCOMMAND|MF_ENABLED },
\r
7857 { IDM_AnalysisMode, MF_BYCOMMAND|MF_ENABLED },
\r
7858 { IDM_AnalyzeFile, MF_BYCOMMAND|MF_ENABLED },
\r
7859 { IDM_Engine1Options, MF_BYCOMMAND|MF_ENABLED },
\r
7860 { IDM_Engine2Options, MF_BYCOMMAND|MF_ENABLED },
\r
7861 { IDM_TimeControl, MF_BYCOMMAND|MF_ENABLED },
\r
7862 { IDM_RetractMove, MF_BYCOMMAND|MF_ENABLED },
\r
7863 { IDM_MoveNow, MF_BYCOMMAND|MF_ENABLED },
\r
7864 { IDM_Hint, MF_BYCOMMAND|MF_ENABLED },
\r
7865 { IDM_Book, MF_BYCOMMAND|MF_ENABLED },
\r
7869 Enables icsEnables[] = {
\r
7870 { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },
\r
7871 { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },
\r
7872 { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },
\r
7873 { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },
\r
7874 { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },
\r
7875 { IDM_Match, MF_BYCOMMAND|MF_GRAYED },
\r
7876 { IDM_MachineBoth, MF_BYCOMMAND|MF_GRAYED },
\r
7877 { IDM_AnalysisMode, MF_BYCOMMAND|MF_ENABLED },
\r
7878 { IDM_AnalyzeFile, MF_BYCOMMAND|MF_GRAYED },
\r
7879 { IDM_TimeControl, MF_BYCOMMAND|MF_GRAYED },
\r
7880 { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },
\r
7881 { IDM_Hint, MF_BYCOMMAND|MF_GRAYED },
\r
7882 { IDM_Book, MF_BYCOMMAND|MF_GRAYED },
\r
7883 { IDM_LoadProg1, MF_BYCOMMAND|MF_GRAYED },
\r
7884 { IDM_LoadProg2, MF_BYCOMMAND|MF_GRAYED },
\r
7885 { IDM_IcsOptions, MF_BYCOMMAND|MF_ENABLED },
\r
7886 { IDM_Engine1Options, MF_BYCOMMAND|MF_GRAYED },
\r
7887 { IDM_Engine2Options, MF_BYCOMMAND|MF_GRAYED },
\r
7888 { IDM_Annotate, MF_BYCOMMAND|MF_GRAYED },
\r
7889 { IDM_Tourney, MF_BYCOMMAND|MF_GRAYED },
\r
7894 Enables zippyEnables[] = {
\r
7895 { IDM_MoveNow, MF_BYCOMMAND|MF_ENABLED },
\r
7896 { IDM_Hint, MF_BYCOMMAND|MF_ENABLED },
\r
7897 { IDM_Book, MF_BYCOMMAND|MF_ENABLED },
\r
7898 { IDM_Engine1Options, MF_BYCOMMAND|MF_ENABLED },
\r
7903 Enables ncpEnables[] = {
\r
7904 { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },
\r
7905 { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },
\r
7906 { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },
\r
7907 { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },
\r
7908 { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },
\r
7909 { IDM_Match, MF_BYCOMMAND|MF_GRAYED },
\r
7910 { IDM_AnalysisMode, MF_BYCOMMAND|MF_GRAYED },
\r
7911 { IDM_AnalyzeFile, MF_BYCOMMAND|MF_GRAYED },
\r
7912 { IDM_IcsClient, MF_BYCOMMAND|MF_GRAYED },
\r
7913 { ACTION_POS, MF_BYPOSITION|MF_GRAYED },
\r
7914 { IDM_Revert, MF_BYCOMMAND|MF_GRAYED },
\r
7915 { IDM_Annotate, MF_BYCOMMAND|MF_GRAYED },
\r
7916 { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },
\r
7917 { IDM_RetractMove, MF_BYCOMMAND|MF_GRAYED },
\r
7918 { IDM_TimeControl, MF_BYCOMMAND|MF_GRAYED },
\r
7919 { IDM_Hint, MF_BYCOMMAND|MF_GRAYED },
\r
7920 { IDM_Book, MF_BYCOMMAND|MF_GRAYED },
\r
7921 { IDM_MachineBoth, MF_BYCOMMAND|MF_GRAYED },
\r
7922 { IDM_NewChat, MF_BYCOMMAND|MF_GRAYED },
\r
7923 { IDM_Engine1Options, MF_BYCOMMAND|MF_GRAYED },
\r
7924 { IDM_Engine2Options, MF_BYCOMMAND|MF_GRAYED },
\r
7925 { IDM_Sounds, MF_BYCOMMAND|MF_GRAYED },
\r
7929 Enables trainingOnEnables[] = {
\r
7930 { IDM_EditComment, MF_BYCOMMAND|MF_GRAYED },
\r
7931 { IDM_Comment, MF_BYCOMMAND|MF_GRAYED },
\r
7932 { IDM_Pause, MF_BYCOMMAND|MF_GRAYED },
\r
7933 { IDM_Forward, MF_BYCOMMAND|MF_GRAYED },
\r
7934 { IDM_Backward, MF_BYCOMMAND|MF_GRAYED },
\r
7935 { IDM_ToEnd, MF_BYCOMMAND|MF_GRAYED },
\r
7936 { IDM_ToStart, MF_BYCOMMAND|MF_GRAYED },
\r
7937 { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },
\r
7938 { IDM_TruncateGame, MF_BYCOMMAND|MF_GRAYED },
\r
7942 Enables trainingOffEnables[] = {
\r
7943 { IDM_EditComment, MF_BYCOMMAND|MF_ENABLED },
\r
7944 { IDM_Comment, MF_BYCOMMAND|MF_ENABLED },
\r
7945 { IDM_Pause, MF_BYCOMMAND|MF_ENABLED },
\r
7946 { IDM_Forward, MF_BYCOMMAND|MF_ENABLED },
\r
7947 { IDM_Backward, MF_BYCOMMAND|MF_ENABLED },
\r
7948 { IDM_ToEnd, MF_BYCOMMAND|MF_ENABLED },
\r
7949 { IDM_ToStart, MF_BYCOMMAND|MF_ENABLED },
\r
7950 { IDM_MoveNow, MF_BYCOMMAND|MF_ENABLED },
\r
7951 { IDM_TruncateGame, MF_BYCOMMAND|MF_ENABLED },
\r
7955 /* These modify either ncpEnables or gnuEnables */
\r
7956 Enables cmailEnables[] = {
\r
7957 { IDM_MailMove, MF_BYCOMMAND|MF_ENABLED },
\r
7958 { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_ENABLED },
\r
7959 { ACTION_POS, MF_BYPOSITION|MF_ENABLED },
\r
7960 { IDM_CallFlag, MF_BYCOMMAND|MF_GRAYED },
\r
7961 { IDM_Draw, MF_BYCOMMAND|MF_ENABLED },
\r
7962 { IDM_Adjourn, MF_BYCOMMAND|MF_GRAYED },
\r
7963 { IDM_Abort, MF_BYCOMMAND|MF_GRAYED },
\r
7967 Enables machineThinkingEnables[] = {
\r
7968 { IDM_LoadGame, MF_BYCOMMAND|MF_GRAYED },
\r
7969 { IDM_LoadNextGame, MF_BYCOMMAND|MF_GRAYED },
\r
7970 { IDM_LoadPrevGame, MF_BYCOMMAND|MF_GRAYED },
\r
7971 { IDM_ReloadGame, MF_BYCOMMAND|MF_GRAYED },
\r
7972 { IDM_PasteGame, MF_BYCOMMAND|MF_GRAYED },
\r
7973 { IDM_LoadPosition, MF_BYCOMMAND|MF_GRAYED },
\r
7974 { IDM_LoadNextPosition, MF_BYCOMMAND|MF_GRAYED },
\r
7975 { IDM_LoadPrevPosition, MF_BYCOMMAND|MF_GRAYED },
\r
7976 { IDM_ReloadPosition, MF_BYCOMMAND|MF_GRAYED },
\r
7977 { IDM_PastePosition, MF_BYCOMMAND|MF_GRAYED },
\r
7978 { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },
\r
7979 { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },
\r
7980 { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },
\r
7981 // { IDM_Match, MF_BYCOMMAND|MF_GRAYED },
\r
7982 { IDM_TypeInMove, MF_BYCOMMAND|MF_GRAYED },
\r
7983 { IDM_RetractMove, MF_BYCOMMAND|MF_GRAYED },
\r
7987 Enables userThinkingEnables[] = {
\r
7988 { IDM_LoadGame, MF_BYCOMMAND|MF_ENABLED },
\r
7989 { IDM_LoadNextGame, MF_BYCOMMAND|MF_ENABLED },
\r
7990 { IDM_LoadPrevGame, MF_BYCOMMAND|MF_ENABLED },
\r
7991 { IDM_ReloadGame, MF_BYCOMMAND|MF_ENABLED },
\r
7992 { IDM_PasteGame, MF_BYCOMMAND|MF_ENABLED },
\r
7993 { IDM_LoadPosition, MF_BYCOMMAND|MF_ENABLED },
\r
7994 { IDM_LoadNextPosition, MF_BYCOMMAND|MF_ENABLED },
\r
7995 { IDM_LoadPrevPosition, MF_BYCOMMAND|MF_ENABLED },
\r
7996 { IDM_ReloadPosition, MF_BYCOMMAND|MF_ENABLED },
\r
7997 { IDM_PastePosition, MF_BYCOMMAND|MF_ENABLED },
\r
7998 { IDM_MachineWhite, MF_BYCOMMAND|MF_ENABLED },
\r
7999 { IDM_MachineBlack, MF_BYCOMMAND|MF_ENABLED },
\r
8000 { IDM_TwoMachines, MF_BYCOMMAND|MF_ENABLED },
\r
8001 // { IDM_Match, MF_BYCOMMAND|MF_ENABLED },
\r
8002 { IDM_TypeInMove, MF_BYCOMMAND|MF_ENABLED },
\r
8003 { IDM_RetractMove, MF_BYCOMMAND|MF_ENABLED },
\r
8007 /*---------------------------------------------------------------------------*\
\r
8009 * Front-end interface functions exported by XBoard.
\r
8010 * Functions appear in same order as prototypes in frontend.h.
\r
8012 \*---------------------------------------------------------------------------*/
\r
8014 CheckMark(UINT item, int state)
\r
8016 if(item) CheckMenuItem(GetMenu(hwndMain), item, MF_BYCOMMAND|state);
\r
8022 static UINT prevChecked = 0;
\r
8023 static int prevPausing = 0;
\r
8026 if (pausing != prevPausing) {
\r
8027 prevPausing = pausing;
\r
8028 (void) CheckMenuItem(GetMenu(hwndMain), IDM_Pause,
\r
8029 MF_BYCOMMAND|(pausing ? MF_CHECKED : MF_UNCHECKED));
\r
8030 if (hwndPause) SetWindowText(hwndPause, pausing ? "C" : "P");
\r
8033 switch (gameMode) {
\r
8034 case BeginningOfGame:
\r
8035 if (appData.icsActive)
\r
8036 nowChecked = IDM_IcsClient;
\r
8037 else if (appData.noChessProgram)
\r
8038 nowChecked = IDM_EditGame;
\r
8040 nowChecked = IDM_MachineBlack;
\r
8042 case MachinePlaysBlack:
\r
8043 nowChecked = IDM_MachineBlack;
\r
8045 case MachinePlaysWhite:
\r
8046 nowChecked = IDM_MachineWhite;
\r
8048 case TwoMachinesPlay:
\r
8049 nowChecked = IDM_TwoMachines;
\r
8052 nowChecked = IDM_AnalysisMode;
\r
8055 nowChecked = IDM_AnalyzeFile;
\r
8058 nowChecked = IDM_EditGame;
\r
8060 case PlayFromGameFile:
\r
8061 nowChecked = IDM_LoadGame;
\r
8063 case EditPosition:
\r
8064 nowChecked = IDM_EditPosition;
\r
8067 nowChecked = IDM_Training;
\r
8069 case IcsPlayingWhite:
\r
8070 case IcsPlayingBlack:
\r
8071 case IcsObserving:
\r
8073 nowChecked = IDM_IcsClient;
\r
8080 CheckMark(prevChecked, MF_UNCHECKED);
\r
8081 CheckMark(nowChecked, MF_CHECKED);
\r
8082 CheckMark(IDM_Match, matchMode && matchGame < appData.matchGames ? MF_CHECKED : MF_UNCHECKED);
\r
8084 if (nowChecked == IDM_LoadGame || nowChecked == IDM_Training) {
\r
8085 (void) EnableMenuItem(GetMenu(hwndMain), IDM_Training,
\r
8086 MF_BYCOMMAND|MF_ENABLED);
\r
8088 (void) EnableMenuItem(GetMenu(hwndMain),
\r
8089 IDM_Training, MF_BYCOMMAND|MF_GRAYED);
\r
8092 prevChecked = nowChecked;
\r
8094 /* [DM] icsEngineAnalyze - Do a sceure check too */
\r
8095 if (appData.icsActive) {
\r
8096 if (appData.icsEngineAnalyze) {
\r
8097 CheckMark(IDM_AnalysisMode, MF_CHECKED);
\r
8099 CheckMark(IDM_AnalysisMode, MF_UNCHECKED);
\r
8102 DisplayLogos(); // [HGM] logos: mode change could have altered logos
\r
8108 HMENU hmenu = GetMenu(hwndMain);
\r
8109 SetMenuEnables(hmenu, icsEnables);
\r
8110 EnableMenuItem(GetSubMenu(hmenu, OPTIONS_POS), IDM_IcsOptions,
\r
8111 MF_BYCOMMAND|MF_ENABLED);
\r
8113 if (appData.zippyPlay) {
\r
8114 SetMenuEnables(hmenu, zippyEnables);
\r
8115 if (!appData.noChessProgram) /* [DM] icsEngineAnalyze */
\r
8116 (void) EnableMenuItem(GetMenu(hwndMain), IDM_AnalysisMode,
\r
8117 MF_BYCOMMAND|MF_ENABLED);
\r
8125 SetMenuEnables(GetMenu(hwndMain), gnuEnables);
\r
8131 HMENU hmenu = GetMenu(hwndMain);
\r
8132 SetMenuEnables(hmenu, ncpEnables);
\r
8133 DrawMenuBar(hwndMain);
\r
8139 SetMenuEnables(GetMenu(hwndMain), cmailEnables);
\r
8143 SetTrainingModeOn()
\r
8146 SetMenuEnables(GetMenu(hwndMain), trainingOnEnables);
\r
8147 for (i = 0; i < N_BUTTONS; i++) {
\r
8148 if (buttonDesc[i].hwnd != NULL)
\r
8149 EnableWindow(buttonDesc[i].hwnd, FALSE);
\r
8154 VOID SetTrainingModeOff()
\r
8157 SetMenuEnables(GetMenu(hwndMain), trainingOffEnables);
\r
8158 for (i = 0; i < N_BUTTONS; i++) {
\r
8159 if (buttonDesc[i].hwnd != NULL)
\r
8160 EnableWindow(buttonDesc[i].hwnd, TRUE);
\r
8166 SetUserThinkingEnables()
\r
8168 SetMenuEnables(GetMenu(hwndMain), userThinkingEnables);
\r
8172 SetMachineThinkingEnables()
\r
8174 HMENU hMenu = GetMenu(hwndMain);
\r
8175 int flags = MF_BYCOMMAND|MF_ENABLED;
\r
8177 SetMenuEnables(hMenu, machineThinkingEnables);
\r
8179 if (gameMode == MachinePlaysBlack) {
\r
8180 (void)EnableMenuItem(hMenu, IDM_MachineBlack, flags);
\r
8181 } else if (gameMode == MachinePlaysWhite) {
\r
8182 (void)EnableMenuItem(hMenu, IDM_MachineWhite, flags);
\r
8183 } else if (gameMode == TwoMachinesPlay) {
\r
8184 (void)EnableMenuItem(hMenu, matchMode ? IDM_Match : IDM_TwoMachines, flags); // [HGM] match
\r
8190 DisplayTitle(char *str)
\r
8192 char title[MSG_SIZ], *host;
\r
8193 if (str[0] != NULLCHAR) {
\r
8194 safeStrCpy(title, str, sizeof(title)/sizeof(title[0]) );
\r
8195 } else if (appData.icsActive) {
\r
8196 if (appData.icsCommPort[0] != NULLCHAR)
\r
8199 host = appData.icsHost;
\r
8200 snprintf(title, MSG_SIZ, "%s: %s", szTitle, host);
\r
8201 } else if (appData.noChessProgram) {
\r
8202 safeStrCpy(title, szTitle, sizeof(title)/sizeof(title[0]) );
\r
8204 safeStrCpy(title, szTitle, sizeof(title)/sizeof(title[0]) );
\r
8205 strcat(title, ": ");
\r
8206 strcat(title, first.tidy);
\r
8208 SetWindowText(hwndMain, title);
\r
8213 DisplayMessage(char *str1, char *str2)
\r
8217 int remain = MESSAGE_TEXT_MAX - 1;
\r
8220 moveErrorMessageUp = FALSE; /* turned on later by caller if needed */
\r
8221 messageText[0] = NULLCHAR;
\r
8223 len = strlen(str1);
\r
8224 if (len > remain) len = remain;
\r
8225 strncpy(messageText, str1, len);
\r
8226 messageText[len] = NULLCHAR;
\r
8229 if (*str2 && remain >= 2) {
\r
8231 strcat(messageText, " ");
\r
8234 len = strlen(str2);
\r
8235 if (len > remain) len = remain;
\r
8236 strncat(messageText, str2, len);
\r
8238 messageText[MESSAGE_TEXT_MAX - 1] = NULLCHAR;
\r
8239 safeStrCpy(lastMsg, messageText, MSG_SIZ);
\r
8241 if (hwndMain == NULL || IsIconic(hwndMain)) return;
\r
8245 hdc = GetDC(hwndMain);
\r
8246 oldFont = SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);
\r
8247 ExtTextOut(hdc, messageRect.left, messageRect.top, ETO_CLIPPED|ETO_OPAQUE,
\r
8248 &messageRect, messageText, strlen(messageText), NULL);
\r
8249 (void) SelectObject(hdc, oldFont);
\r
8250 (void) ReleaseDC(hwndMain, hdc);
\r
8254 DisplayError(char *str, int error)
\r
8256 char buf[MSG_SIZ*2], buf2[MSG_SIZ];
\r
8260 safeStrCpy(buf, str, sizeof(buf)/sizeof(buf[0]) );
\r
8262 len = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,
\r
8263 NULL, error, LANG_NEUTRAL,
\r
8264 (LPSTR) buf2, MSG_SIZ, NULL);
\r
8266 snprintf(buf, 2*MSG_SIZ, "%s:\n%s", str, buf2);
\r
8268 ErrorMap *em = errmap;
\r
8269 while (em->err != 0 && em->err != error) em++;
\r
8270 if (em->err != 0) {
\r
8271 snprintf(buf, 2*MSG_SIZ, "%s:\n%s", str, em->msg);
\r
8273 snprintf(buf, 2*MSG_SIZ, "%s:\nError code %d", str, error);
\r
8278 ErrorPopUp(_("Error"), buf);
\r
8283 DisplayMoveError(char *str)
\r
8285 fromX = fromY = -1;
\r
8286 ClearHighlights();
\r
8287 DrawPosition(FALSE, NULL);
\r
8288 if (appData.popupMoveErrors) {
\r
8289 ErrorPopUp(_("Error"), str);
\r
8291 DisplayMessage(str, "");
\r
8292 moveErrorMessageUp = TRUE;
\r
8297 DisplayFatalError(char *str, int error, int exitStatus)
\r
8299 char buf[2*MSG_SIZ], buf2[MSG_SIZ];
\r
8301 char *label = exitStatus ? _("Fatal Error") : _("Exiting");
\r
8304 len = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,
\r
8305 NULL, error, LANG_NEUTRAL,
\r
8306 (LPSTR) buf2, MSG_SIZ, NULL);
\r
8308 snprintf(buf, 2*MSG_SIZ, "%s:\n%s", str, buf2);
\r
8310 ErrorMap *em = errmap;
\r
8311 while (em->err != 0 && em->err != error) em++;
\r
8312 if (em->err != 0) {
\r
8313 snprintf(buf, 2*MSG_SIZ, "%s:\n%s", str, em->msg);
\r
8315 snprintf(buf, 2*MSG_SIZ, "%s:\nError code %d", str, error);
\r
8320 if (appData.debugMode) {
\r
8321 fprintf(debugFP, "%s: %s\n", label, str);
\r
8323 if (appData.popupExitMessage) {
\r
8324 (void) MessageBox(hwndMain, str, label, MB_OK|
\r
8325 (exitStatus ? MB_ICONSTOP : MB_ICONINFORMATION));
\r
8327 ExitEvent(exitStatus);
\r
8332 DisplayInformation(char *str)
\r
8334 (void) MessageBox(hwndMain, str, _("Information"), MB_OK|MB_ICONINFORMATION);
\r
8339 DisplayNote(char *str)
\r
8341 ErrorPopUp(_("Note"), str);
\r
8346 char *title, *question, *replyPrefix;
\r
8351 QuestionDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
8353 static QuestionParams *qp;
\r
8354 char reply[MSG_SIZ];
\r
8357 switch (message) {
\r
8358 case WM_INITDIALOG:
\r
8359 qp = (QuestionParams *) lParam;
\r
8360 CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));
\r
8361 Translate(hDlg, DLG_Question);
\r
8362 SetWindowText(hDlg, qp->title);
\r
8363 SetDlgItemText(hDlg, OPT_QuestionText, qp->question);
\r
8364 SetFocus(GetDlgItem(hDlg, OPT_QuestionInput));
\r
8368 switch (LOWORD(wParam)) {
\r
8370 safeStrCpy(reply, qp->replyPrefix, sizeof(reply)/sizeof(reply[0]) );
\r
8371 if (*reply) strcat(reply, " ");
\r
8372 len = strlen(reply);
\r
8373 GetDlgItemText(hDlg, OPT_QuestionInput, reply + len, sizeof(reply) - len);
\r
8374 strcat(reply, "\n");
\r
8375 OutputToProcess(qp->pr, reply, strlen(reply), &err);
\r
8376 EndDialog(hDlg, TRUE);
\r
8377 if (err) DisplayFatalError(_("Error writing to chess program"), err, 1);
\r
8380 EndDialog(hDlg, FALSE);
\r
8391 AskQuestion(char* title, char *question, char *replyPrefix, ProcRef pr)
\r
8393 QuestionParams qp;
\r
8397 qp.question = question;
\r
8398 qp.replyPrefix = replyPrefix;
\r
8400 lpProc = MakeProcInstance((FARPROC)QuestionDialog, hInst);
\r
8401 DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_Question),
\r
8402 hwndMain, (DLGPROC)lpProc, (LPARAM)&qp);
\r
8403 FreeProcInstance(lpProc);
\r
8406 /* [AS] Pick FRC position */
\r
8407 LRESULT CALLBACK NewGameFRC_Proc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
8409 static int * lpIndexFRC;
\r
8415 case WM_INITDIALOG:
\r
8416 lpIndexFRC = (int *) lParam;
\r
8418 CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));
\r
8419 Translate(hDlg, DLG_NewGameFRC);
\r
8421 SendDlgItemMessage( hDlg, IDC_NFG_Edit, EM_SETLIMITTEXT, sizeof(buf)-1, 0 );
\r
8422 SetDlgItemInt( hDlg, IDC_NFG_Edit, *lpIndexFRC, TRUE );
\r
8423 SendDlgItemMessage( hDlg, IDC_NFG_Edit, EM_SETSEL, 0, -1 );
\r
8424 SetFocus(GetDlgItem(hDlg, IDC_NFG_Edit));
\r
8429 switch( LOWORD(wParam) ) {
\r
8431 *lpIndexFRC = GetDlgItemInt(hDlg, IDC_NFG_Edit, &index_is_ok, TRUE );
\r
8432 EndDialog( hDlg, 0 );
\r
8433 shuffleOpenings = TRUE; /* [HGM] shuffle: switch shuffling on for as long as we stay in current variant */
\r
8436 EndDialog( hDlg, 1 );
\r
8438 case IDC_NFG_Edit:
\r
8439 if( HIWORD(wParam) == EN_CHANGE ) {
\r
8440 GetDlgItemInt(hDlg, IDC_NFG_Edit, &index_is_ok, TRUE );
\r
8442 EnableWindow( GetDlgItem(hDlg, IDOK), index_is_ok );
\r
8445 case IDC_NFG_Random:
\r
8446 snprintf( buf, sizeof(buf)/sizeof(buf[0]), "%d", myrandom() ); /* [HGM] shuffle: no longer limit to 960 */
\r
8447 SetDlgItemText(hDlg, IDC_NFG_Edit, buf );
\r
8460 int index = appData.defaultFrcPosition;
\r
8461 FARPROC lpProc = MakeProcInstance( (FARPROC) NewGameFRC_Proc, hInst );
\r
8463 result = DialogBoxParam( hInst, MAKEINTRESOURCE(DLG_NewGameFRC), hwndMain, (DLGPROC)lpProc, (LPARAM)&index );
\r
8465 if( result == 0 ) {
\r
8466 appData.defaultFrcPosition = index;
\r
8472 /* [AS] Game list options. Refactored by HGM */
\r
8474 HWND gameListOptionsDialog;
\r
8476 // low-level front-end: clear text edit / list widget
\r
8480 SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_RESETCONTENT, 0, 0 );
\r
8483 // low-level front-end: clear text edit / list widget
\r
8485 GLT_DeSelectList()
\r
8487 SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_SETCURSEL, 0, 0 );
\r
8490 // low-level front-end: append line to text edit / list widget
\r
8492 GLT_AddToList( char *name )
\r
8495 SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_ADDSTRING, 0, (LPARAM) name );
\r
8499 // low-level front-end: get line from text edit / list widget
\r
8501 GLT_GetFromList( int index, char *name )
\r
8504 if( SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_GETTEXT, index, (LPARAM) name ) != LB_ERR )
\r
8510 void GLT_MoveSelection( HWND hDlg, int delta )
\r
8512 int idx1 = (int) SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETCURSEL, 0, 0 );
\r
8513 int idx2 = idx1 + delta;
\r
8514 int count = (int) SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETCOUNT, 0, 0 );
\r
8516 if( idx1 >=0 && idx1 < count && idx2 >= 0 && idx2 < count ) {
\r
8519 SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETTEXT, idx1, (LPARAM) buf );
\r
8520 SendDlgItemMessage( hDlg, IDC_GameListTags, LB_DELETESTRING, idx1, 0 );
\r
8521 SendDlgItemMessage( hDlg, IDC_GameListTags, LB_INSERTSTRING, idx2, (LPARAM) buf );
\r
8522 SendDlgItemMessage( hDlg, IDC_GameListTags, LB_SETCURSEL, idx2, 0 );
\r
8526 LRESULT CALLBACK GameListOptions_Proc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
8530 case WM_INITDIALOG:
\r
8531 gameListOptionsDialog = hDlg; // [HGM] pass through global to keep out off back-end
\r
8533 CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));
\r
8534 Translate(hDlg, DLG_GameListOptions);
\r
8536 /* Initialize list */
\r
8537 GLT_TagsToList( lpUserGLT );
\r
8539 SetFocus( GetDlgItem(hDlg, IDC_GameListTags) );
\r
8544 switch( LOWORD(wParam) ) {
\r
8547 EndDialog( hDlg, 0 );
\r
8550 EndDialog( hDlg, 1 );
\r
8553 case IDC_GLT_Default:
\r
8554 GLT_TagsToList( GLT_DEFAULT_TAGS );
\r
8557 case IDC_GLT_Restore:
\r
8558 GLT_TagsToList( appData.gameListTags );
\r
8562 GLT_MoveSelection( hDlg, -1 );
\r
8565 case IDC_GLT_Down:
\r
8566 GLT_MoveSelection( hDlg, +1 );
\r
8576 int GameListOptions()
\r
8579 FARPROC lpProc = MakeProcInstance( (FARPROC) GameListOptions_Proc, hInst );
\r
8581 safeStrCpy( lpUserGLT, appData.gameListTags ,LPUSERGLT_SIZE );
\r
8583 result = DialogBoxParam( hInst, MAKEINTRESOURCE(DLG_GameListOptions), hwndMain, (DLGPROC)lpProc, (LPARAM)lpUserGLT );
\r
8585 if( result == 0 ) {
\r
8586 /* [AS] Memory leak here! */
\r
8587 appData.gameListTags = strdup( lpUserGLT );
\r
8594 DisplayIcsInteractionTitle(char *str)
\r
8596 char consoleTitle[MSG_SIZ];
\r
8598 snprintf(consoleTitle, MSG_SIZ, "%s: %s", szConsoleTitle, str);
\r
8599 SetWindowText(hwndConsole, consoleTitle);
\r
8601 if(appData.chatBoxes) { // [HGM] chat: open chat boxes
\r
8602 char buf[MSG_SIZ], *p = buf, *q;
\r
8603 safeStrCpy(buf, appData.chatBoxes, sizeof(buf)/sizeof(buf[0]) );
\r
8605 q = strchr(p, ';');
\r
8607 if(*p) ChatPopUp(p);
\r
8611 SetActiveWindow(hwndMain);
\r
8615 DrawPosition(int fullRedraw, Board board)
\r
8617 HDCDrawPosition(NULL, (BOOLEAN) fullRedraw, board);
\r
8620 void NotifyFrontendLogin()
\r
8623 UpdateICSWidth(GetDlgItem(hwndConsole, OPT_ConsoleText));
\r
8629 fromX = fromY = -1;
\r
8630 if (dragInfo.pos.x != -1 || dragInfo.pos.y != -1) {
\r
8631 dragInfo.pos.x = dragInfo.pos.y = -1;
\r
8632 dragInfo.pos.x = dragInfo.pos.y = -1;
\r
8633 dragInfo.lastpos = dragInfo.pos;
\r
8634 dragInfo.start.x = dragInfo.start.y = -1;
\r
8635 dragInfo.from = dragInfo.start;
\r
8637 DrawPosition(TRUE, NULL);
\r
8644 CommentPopUp(char *title, char *str)
\r
8646 HWND hwnd = GetActiveWindow();
\r
8647 EitherCommentPopUp(currentMove, title, str, FALSE); // [HGM] vari: fake move index, rather than 0
\r
8649 SetActiveWindow(hwnd);
\r
8653 CommentPopDown(void)
\r
8655 CheckMenuItem(GetMenu(hwndMain), IDM_Comment, MF_UNCHECKED);
\r
8656 if (commentDialog) {
\r
8657 ShowWindow(commentDialog, SW_HIDE);
\r
8659 commentUp = FALSE;
\r
8663 EditCommentPopUp(int index, char *title, char *str)
\r
8665 EitherCommentPopUp(index, title, str, TRUE);
\r
8672 MyPlaySound(&sounds[(int)SoundMove]);
\r
8675 VOID PlayIcsWinSound()
\r
8677 MyPlaySound(&sounds[(int)SoundIcsWin]);
\r
8680 VOID PlayIcsLossSound()
\r
8682 MyPlaySound(&sounds[(int)SoundIcsLoss]);
\r
8685 VOID PlayIcsDrawSound()
\r
8687 MyPlaySound(&sounds[(int)SoundIcsDraw]);
\r
8690 VOID PlayIcsUnfinishedSound()
\r
8692 MyPlaySound(&sounds[(int)SoundIcsUnfinished]);
\r
8698 MyPlaySound(&sounds[(int)SoundAlarm]);
\r
8704 MyPlaySound(&textAttribs[ColorTell].sound);
\r
8712 consoleEcho = TRUE;
\r
8713 hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
8714 SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&consoleCF);
\r
8715 SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);
\r
8724 consoleEcho = FALSE;
\r
8725 hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
8726 /* This works OK: set text and background both to the same color */
\r
8728 cf.crTextColor = COLOR_ECHOOFF;
\r
8729 SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf);
\r
8730 SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, cf.crTextColor);
\r
8733 /* No Raw()...? */
\r
8735 void Colorize(ColorClass cc, int continuation)
\r
8737 currentColorClass = cc;
\r
8738 consoleCF.dwMask = CFM_COLOR|CFM_BOLD|CFM_ITALIC|CFM_UNDERLINE|CFM_STRIKEOUT;
\r
8739 consoleCF.crTextColor = textAttribs[cc].color;
\r
8740 consoleCF.dwEffects = textAttribs[cc].effects;
\r
8741 if (!continuation) MyPlaySound(&textAttribs[cc].sound);
\r
8747 static char buf[MSG_SIZ];
\r
8748 DWORD bufsiz = MSG_SIZ;
\r
8750 if(appData.userName != NULL && appData.userName[0] != 0) {
\r
8751 return appData.userName; /* [HGM] username: prefer name selected by user over his system login */
\r
8753 if (!GetUserName(buf, &bufsiz)) {
\r
8754 /*DisplayError("Error getting user name", GetLastError());*/
\r
8755 safeStrCpy(buf, _("User"), sizeof(buf)/sizeof(buf[0]) );
\r
8763 static char buf[MSG_SIZ];
\r
8764 DWORD bufsiz = MSG_SIZ;
\r
8766 if (!GetComputerName(buf, &bufsiz)) {
\r
8767 /*DisplayError("Error getting host name", GetLastError());*/
\r
8768 safeStrCpy(buf, _("Unknown"), sizeof(buf)/sizeof(buf[0]) );
\r
8775 ClockTimerRunning()
\r
8777 return clockTimerEvent != 0;
\r
8783 if (clockTimerEvent == 0) return FALSE;
\r
8784 KillTimer(hwndMain, clockTimerEvent);
\r
8785 clockTimerEvent = 0;
\r
8790 StartClockTimer(long millisec)
\r
8792 clockTimerEvent = SetTimer(hwndMain, (UINT) CLOCK_TIMER_ID,
\r
8793 (UINT) millisec, NULL);
\r
8797 DisplayWhiteClock(long timeRemaining, int highlight)
\r
8800 char *flag = whiteFlag && gameMode == TwoMachinesPlay ? "(!)" : "";
\r
8802 if(appData.noGUI) return;
\r
8803 hdc = GetDC(hwndMain);
\r
8804 if (!IsIconic(hwndMain)) {
\r
8805 DisplayAClock(hdc, timeRemaining, highlight,
\r
8806 flipClock ? &blackRect : &whiteRect, _("White"), flag);
\r
8808 if (highlight && iconCurrent == iconBlack) {
\r
8809 iconCurrent = iconWhite;
\r
8810 PostMessage(hwndMain, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);
\r
8811 if (IsIconic(hwndMain)) {
\r
8812 DrawIcon(hdc, 2, 2, iconCurrent);
\r
8815 (void) ReleaseDC(hwndMain, hdc);
\r
8817 PostMessage(hwndConsole, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);
\r
8821 DisplayBlackClock(long timeRemaining, int highlight)
\r
8824 char *flag = blackFlag && gameMode == TwoMachinesPlay ? "(!)" : "";
\r
8826 if(appData.noGUI) return;
\r
8827 hdc = GetDC(hwndMain);
\r
8828 if (!IsIconic(hwndMain)) {
\r
8829 DisplayAClock(hdc, timeRemaining, highlight,
\r
8830 flipClock ? &whiteRect : &blackRect, _("Black"), flag);
\r
8832 if (highlight && iconCurrent == iconWhite) {
\r
8833 iconCurrent = iconBlack;
\r
8834 PostMessage(hwndMain, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);
\r
8835 if (IsIconic(hwndMain)) {
\r
8836 DrawIcon(hdc, 2, 2, iconCurrent);
\r
8839 (void) ReleaseDC(hwndMain, hdc);
\r
8841 PostMessage(hwndConsole, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);
\r
8846 LoadGameTimerRunning()
\r
8848 return loadGameTimerEvent != 0;
\r
8852 StopLoadGameTimer()
\r
8854 if (loadGameTimerEvent == 0) return FALSE;
\r
8855 KillTimer(hwndMain, loadGameTimerEvent);
\r
8856 loadGameTimerEvent = 0;
\r
8861 StartLoadGameTimer(long millisec)
\r
8863 loadGameTimerEvent = SetTimer(hwndMain, (UINT) LOAD_GAME_TIMER_ID,
\r
8864 (UINT) millisec, NULL);
\r
8872 char fileTitle[MSG_SIZ];
\r
8874 defName = DefaultFileName(appData.oldSaveStyle ? "gam" : "pgn");
\r
8875 f = OpenFileDialog(hwndMain, "a", defName,
\r
8876 appData.oldSaveStyle ? "gam" : "pgn",
\r
8878 _("Save Game to File"), NULL, fileTitle, NULL);
\r
8880 SaveGame(f, 0, "");
\r
8887 ScheduleDelayedEvent(DelayedEventCallback cb, long millisec)
\r
8889 if (delayedTimerEvent != 0) {
\r
8890 if (appData.debugMode && cb != delayedTimerCallback) { // [HGM] alive: not too much debug
\r
8891 fprintf(debugFP, "ScheduleDelayedEvent: event already scheduled\n");
\r
8893 KillTimer(hwndMain, delayedTimerEvent);
\r
8894 delayedTimerEvent = 0;
\r
8895 if(delayedTimerCallback != cb) // [HGM] alive: do not "flush" same event, just postpone it
\r
8896 delayedTimerCallback();
\r
8898 delayedTimerCallback = cb;
\r
8899 delayedTimerEvent = SetTimer(hwndMain, (UINT) DELAYED_TIMER_ID,
\r
8900 (UINT) millisec, NULL);
\r
8903 DelayedEventCallback
\r
8906 if (delayedTimerEvent) {
\r
8907 return delayedTimerCallback;
\r
8914 CancelDelayedEvent()
\r
8916 if (delayedTimerEvent) {
\r
8917 KillTimer(hwndMain, delayedTimerEvent);
\r
8918 delayedTimerEvent = 0;
\r
8922 DWORD GetWin32Priority(int nice)
\r
8923 { // [HGM] nice: translate Unix nice() value to indows priority class. (Code stolen from Polyglot 1.4w11)
\r
8925 REALTIME_PRIORITY_CLASS 0x00000100
\r
8926 HIGH_PRIORITY_CLASS 0x00000080
\r
8927 ABOVE_NORMAL_PRIORITY_CLASS 0x00008000
\r
8928 NORMAL_PRIORITY_CLASS 0x00000020
\r
8929 BELOW_NORMAL_PRIORITY_CLASS 0x00004000
\r
8930 IDLE_PRIORITY_CLASS 0x00000040
\r
8932 if (nice < -15) return 0x00000080;
\r
8933 if (nice < 0) return 0x00008000;
\r
8934 if (nice == 0) return 0x00000020;
\r
8935 if (nice < 15) return 0x00004000;
\r
8936 return 0x00000040;
\r
8939 void RunCommand(char *cmdLine)
\r
8941 /* Now create the child process. */
\r
8942 STARTUPINFO siStartInfo;
\r
8943 PROCESS_INFORMATION piProcInfo;
\r
8945 siStartInfo.cb = sizeof(STARTUPINFO);
\r
8946 siStartInfo.lpReserved = NULL;
\r
8947 siStartInfo.lpDesktop = NULL;
\r
8948 siStartInfo.lpTitle = NULL;
\r
8949 siStartInfo.dwFlags = STARTF_USESTDHANDLES;
\r
8950 siStartInfo.cbReserved2 = 0;
\r
8951 siStartInfo.lpReserved2 = NULL;
\r
8952 siStartInfo.hStdInput = NULL;
\r
8953 siStartInfo.hStdOutput = NULL;
\r
8954 siStartInfo.hStdError = NULL;
\r
8956 CreateProcess(NULL,
\r
8957 cmdLine, /* command line */
\r
8958 NULL, /* process security attributes */
\r
8959 NULL, /* primary thread security attrs */
\r
8960 TRUE, /* handles are inherited */
\r
8961 DETACHED_PROCESS|CREATE_NEW_PROCESS_GROUP,
\r
8962 NULL, /* use parent's environment */
\r
8964 &siStartInfo, /* STARTUPINFO pointer */
\r
8965 &piProcInfo); /* receives PROCESS_INFORMATION */
\r
8967 CloseHandle(piProcInfo.hThread);
\r
8970 /* Start a child process running the given program.
\r
8971 The process's standard output can be read from "from", and its
\r
8972 standard input can be written to "to".
\r
8973 Exit with fatal error if anything goes wrong.
\r
8974 Returns an opaque pointer that can be used to destroy the process
\r
8978 StartChildProcess(char *cmdLine, char *dir, ProcRef *pr)
\r
8980 #define BUFSIZE 4096
\r
8982 HANDLE hChildStdinRd, hChildStdinWr,
\r
8983 hChildStdoutRd, hChildStdoutWr;
\r
8984 HANDLE hChildStdinWrDup, hChildStdoutRdDup;
\r
8985 SECURITY_ATTRIBUTES saAttr;
\r
8987 PROCESS_INFORMATION piProcInfo;
\r
8988 STARTUPINFO siStartInfo;
\r
8990 char buf[MSG_SIZ];
\r
8993 if (appData.debugMode) {
\r
8994 fprintf(debugFP, "StartChildProcess (dir=\"%s\") %s\n", dir, cmdLine);
\r
8999 /* Set the bInheritHandle flag so pipe handles are inherited. */
\r
9000 saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);
\r
9001 saAttr.bInheritHandle = TRUE;
\r
9002 saAttr.lpSecurityDescriptor = NULL;
\r
9005 * The steps for redirecting child's STDOUT:
\r
9006 * 1. Create anonymous pipe to be STDOUT for child.
\r
9007 * 2. Create a noninheritable duplicate of read handle,
\r
9008 * and close the inheritable read handle.
\r
9011 /* Create a pipe for the child's STDOUT. */
\r
9012 if (! CreatePipe(&hChildStdoutRd, &hChildStdoutWr, &saAttr, 0)) {
\r
9013 return GetLastError();
\r
9016 /* Duplicate the read handle to the pipe, so it is not inherited. */
\r
9017 fSuccess = DuplicateHandle(GetCurrentProcess(), hChildStdoutRd,
\r
9018 GetCurrentProcess(), &hChildStdoutRdDup, 0,
\r
9019 FALSE, /* not inherited */
\r
9020 DUPLICATE_SAME_ACCESS);
\r
9022 return GetLastError();
\r
9024 CloseHandle(hChildStdoutRd);
\r
9027 * The steps for redirecting child's STDIN:
\r
9028 * 1. Create anonymous pipe to be STDIN for child.
\r
9029 * 2. Create a noninheritable duplicate of write handle,
\r
9030 * and close the inheritable write handle.
\r
9033 /* Create a pipe for the child's STDIN. */
\r
9034 if (! CreatePipe(&hChildStdinRd, &hChildStdinWr, &saAttr, 0)) {
\r
9035 return GetLastError();
\r
9038 /* Duplicate the write handle to the pipe, so it is not inherited. */
\r
9039 fSuccess = DuplicateHandle(GetCurrentProcess(), hChildStdinWr,
\r
9040 GetCurrentProcess(), &hChildStdinWrDup, 0,
\r
9041 FALSE, /* not inherited */
\r
9042 DUPLICATE_SAME_ACCESS);
\r
9044 return GetLastError();
\r
9046 CloseHandle(hChildStdinWr);
\r
9048 /* Arrange to (1) look in dir for the child .exe file, and
\r
9049 * (2) have dir be the child's working directory. Interpret
\r
9050 * dir relative to the directory WinBoard loaded from. */
\r
9051 GetCurrentDirectory(MSG_SIZ, buf);
\r
9052 SetCurrentDirectory(installDir);
\r
9053 SetCurrentDirectory(dir);
\r
9055 /* Now create the child process. */
\r
9057 siStartInfo.cb = sizeof(STARTUPINFO);
\r
9058 siStartInfo.lpReserved = NULL;
\r
9059 siStartInfo.lpDesktop = NULL;
\r
9060 siStartInfo.lpTitle = NULL;
\r
9061 siStartInfo.dwFlags = STARTF_USESTDHANDLES;
\r
9062 siStartInfo.cbReserved2 = 0;
\r
9063 siStartInfo.lpReserved2 = NULL;
\r
9064 siStartInfo.hStdInput = hChildStdinRd;
\r
9065 siStartInfo.hStdOutput = hChildStdoutWr;
\r
9066 siStartInfo.hStdError = hChildStdoutWr;
\r
9068 fSuccess = CreateProcess(NULL,
\r
9069 cmdLine, /* command line */
\r
9070 NULL, /* process security attributes */
\r
9071 NULL, /* primary thread security attrs */
\r
9072 TRUE, /* handles are inherited */
\r
9073 DETACHED_PROCESS|CREATE_NEW_PROCESS_GROUP,
\r
9074 NULL, /* use parent's environment */
\r
9076 &siStartInfo, /* STARTUPINFO pointer */
\r
9077 &piProcInfo); /* receives PROCESS_INFORMATION */
\r
9079 err = GetLastError();
\r
9080 SetCurrentDirectory(buf); /* return to prev directory */
\r
9085 if (appData.niceEngines){ // [HGM] nice: adjust engine proc priority
\r
9086 if(appData.debugMode) fprintf(debugFP, "nice engine proc to %d\n", appData.niceEngines);
\r
9087 SetPriorityClass(piProcInfo.hProcess, GetWin32Priority(appData.niceEngines));
\r
9090 /* Close the handles we don't need in the parent */
\r
9091 CloseHandle(piProcInfo.hThread);
\r
9092 CloseHandle(hChildStdinRd);
\r
9093 CloseHandle(hChildStdoutWr);
\r
9095 /* Prepare return value */
\r
9096 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
\r
9097 cp->kind = CPReal;
\r
9098 cp->hProcess = piProcInfo.hProcess;
\r
9099 cp->pid = piProcInfo.dwProcessId;
\r
9100 cp->hFrom = hChildStdoutRdDup;
\r
9101 cp->hTo = hChildStdinWrDup;
\r
9103 *pr = (void *) cp;
\r
9105 /* Klaus Friedel says that this Sleep solves a problem under Windows
\r
9106 2000 where engines sometimes don't see the initial command(s)
\r
9107 from WinBoard and hang. I don't understand how that can happen,
\r
9108 but the Sleep is harmless, so I've put it in. Others have also
\r
9109 reported what may be the same problem, so hopefully this will fix
\r
9110 it for them too. */
\r
9118 DestroyChildProcess(ProcRef pr, int/*boolean*/ signal)
\r
9120 ChildProc *cp; int result;
\r
9122 cp = (ChildProc *) pr;
\r
9123 if (cp == NULL) return;
\r
9125 switch (cp->kind) {
\r
9127 /* TerminateProcess is considered harmful, so... */
\r
9128 CloseHandle(cp->hTo); /* Closing this will give the child an EOF and hopefully kill it */
\r
9129 if (cp->hFrom) CloseHandle(cp->hFrom); /* if NULL, InputThread will close it */
\r
9130 /* The following doesn't work because the chess program
\r
9131 doesn't "have the same console" as WinBoard. Maybe
\r
9132 we could arrange for this even though neither WinBoard
\r
9133 nor the chess program uses a console for stdio? */
\r
9134 /*!!if (signal) GenerateConsoleCtrlEvent(CTRL_BREAK_EVENT, cp->pid);*/
\r
9136 /* [AS] Special termination modes for misbehaving programs... */
\r
9137 if( signal == 9 ) {
\r
9138 result = TerminateProcess( cp->hProcess, 0 );
\r
9140 if ( appData.debugMode) {
\r
9141 fprintf( debugFP, "Terminating process %lu, result=%d\n", cp->pid, result );
\r
9144 else if( signal == 10 ) {
\r
9145 DWORD dw = WaitForSingleObject( cp->hProcess, 3*1000 ); // Wait 3 seconds at most
\r
9147 if( dw != WAIT_OBJECT_0 ) {
\r
9148 result = TerminateProcess( cp->hProcess, 0 );
\r
9150 if ( appData.debugMode) {
\r
9151 fprintf( debugFP, "Process %lu still alive after timeout, killing... result=%d\n", cp->pid, result );
\r
9157 CloseHandle(cp->hProcess);
\r
9161 if (cp->hFrom) CloseHandle(cp->hFrom);
\r
9165 closesocket(cp->sock);
\r
9170 if (signal) send(cp->sock2, "\017", 1, 0); /* 017 = 15 = SIGTERM */
\r
9171 closesocket(cp->sock);
\r
9172 closesocket(cp->sock2);
\r
9180 InterruptChildProcess(ProcRef pr)
\r
9184 cp = (ChildProc *) pr;
\r
9185 if (cp == NULL) return;
\r
9186 switch (cp->kind) {
\r
9188 /* The following doesn't work because the chess program
\r
9189 doesn't "have the same console" as WinBoard. Maybe
\r
9190 we could arrange for this even though neither WinBoard
\r
9191 nor the chess program uses a console for stdio */
\r
9192 /*!!GenerateConsoleCtrlEvent(CTRL_C_EVENT, cp->pid);*/
\r
9197 /* Can't interrupt */
\r
9201 send(cp->sock2, "\002", 1, 0); /* 2 = SIGINT */
\r
9208 OpenTelnet(char *host, char *port, ProcRef *pr)
\r
9210 char cmdLine[MSG_SIZ];
\r
9212 if (port[0] == NULLCHAR) {
\r
9213 snprintf(cmdLine, MSG_SIZ, "%s %s", appData.telnetProgram, host);
\r
9215 snprintf(cmdLine, MSG_SIZ, "%s %s %s", appData.telnetProgram, host, port);
\r
9217 return StartChildProcess(cmdLine, "", pr);
\r
9221 /* Code to open TCP sockets */
\r
9224 OpenTCP(char *host, char *port, ProcRef *pr)
\r
9230 struct sockaddr_in sa, mysa;
\r
9231 struct hostent FAR *hp;
\r
9232 unsigned short uport;
\r
9233 WORD wVersionRequested;
\r
9236 /* Initialize socket DLL */
\r
9237 wVersionRequested = MAKEWORD(1, 1);
\r
9238 err = WSAStartup(wVersionRequested, &wsaData);
\r
9239 if (err != 0) return err;
\r
9242 if ((s = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {
\r
9243 err = WSAGetLastError();
\r
9248 /* Bind local address using (mostly) don't-care values.
\r
9250 memset((char *) &mysa, 0, sizeof(struct sockaddr_in));
\r
9251 mysa.sin_family = AF_INET;
\r
9252 mysa.sin_addr.s_addr = INADDR_ANY;
\r
9253 uport = (unsigned short) 0;
\r
9254 mysa.sin_port = htons(uport);
\r
9255 if (bind(s, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))
\r
9256 == SOCKET_ERROR) {
\r
9257 err = WSAGetLastError();
\r
9262 /* Resolve remote host name */
\r
9263 memset((char *) &sa, 0, sizeof(struct sockaddr_in));
\r
9264 if (!(hp = gethostbyname(host))) {
\r
9265 unsigned int b0, b1, b2, b3;
\r
9267 err = WSAGetLastError();
\r
9269 if (sscanf(host, "%u.%u.%u.%u", &b0, &b1, &b2, &b3) == 4) {
\r
9270 hp = (struct hostent *) calloc(1, sizeof(struct hostent));
\r
9271 hp->h_addrtype = AF_INET;
\r
9273 hp->h_addr_list = (char **) calloc(2, sizeof(char *));
\r
9274 hp->h_addr_list[0] = (char *) malloc(4);
\r
9275 hp->h_addr_list[0][0] = (char) b0;
\r
9276 hp->h_addr_list[0][1] = (char) b1;
\r
9277 hp->h_addr_list[0][2] = (char) b2;
\r
9278 hp->h_addr_list[0][3] = (char) b3;
\r
9284 sa.sin_family = hp->h_addrtype;
\r
9285 uport = (unsigned short) atoi(port);
\r
9286 sa.sin_port = htons(uport);
\r
9287 memcpy((char *) &sa.sin_addr, hp->h_addr, hp->h_length);
\r
9289 /* Make connection */
\r
9290 if (connect(s, (struct sockaddr *) &sa,
\r
9291 sizeof(struct sockaddr_in)) == SOCKET_ERROR) {
\r
9292 err = WSAGetLastError();
\r
9297 /* Prepare return value */
\r
9298 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
\r
9299 cp->kind = CPSock;
\r
9301 *pr = (ProcRef *) cp;
\r
9307 OpenCommPort(char *name, ProcRef *pr)
\r
9312 char fullname[MSG_SIZ];
\r
9314 if (*name != '\\')
\r
9315 snprintf(fullname, MSG_SIZ, "\\\\.\\%s", name);
\r
9317 safeStrCpy(fullname, name, sizeof(fullname)/sizeof(fullname[0]) );
\r
9319 h = CreateFile(name, GENERIC_READ | GENERIC_WRITE,
\r
9320 0, NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL);
\r
9321 if (h == (HANDLE) -1) {
\r
9322 return GetLastError();
\r
9326 if (!SetCommState(h, (LPDCB) &dcb)) return GetLastError();
\r
9328 /* Accumulate characters until a 100ms pause, then parse */
\r
9329 ct.ReadIntervalTimeout = 100;
\r
9330 ct.ReadTotalTimeoutMultiplier = 0;
\r
9331 ct.ReadTotalTimeoutConstant = 0;
\r
9332 ct.WriteTotalTimeoutMultiplier = 0;
\r
9333 ct.WriteTotalTimeoutConstant = 0;
\r
9334 if (!SetCommTimeouts(h, (LPCOMMTIMEOUTS) &ct)) return GetLastError();
\r
9336 /* Prepare return value */
\r
9337 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
\r
9338 cp->kind = CPComm;
\r
9341 *pr = (ProcRef *) cp;
\r
9347 OpenLoopback(ProcRef *pr)
\r
9349 DisplayFatalError(_("Not implemented"), 0, 1);
\r
9355 OpenRcmd(char* host, char* user, char* cmd, ProcRef* pr)
\r
9360 struct sockaddr_in sa, mysa;
\r
9361 struct hostent FAR *hp;
\r
9362 unsigned short uport;
\r
9363 WORD wVersionRequested;
\r
9366 char stderrPortStr[MSG_SIZ];
\r
9368 /* Initialize socket DLL */
\r
9369 wVersionRequested = MAKEWORD(1, 1);
\r
9370 err = WSAStartup(wVersionRequested, &wsaData);
\r
9371 if (err != 0) return err;
\r
9373 /* Resolve remote host name */
\r
9374 memset((char *) &sa, 0, sizeof(struct sockaddr_in));
\r
9375 if (!(hp = gethostbyname(host))) {
\r
9376 unsigned int b0, b1, b2, b3;
\r
9378 err = WSAGetLastError();
\r
9380 if (sscanf(host, "%u.%u.%u.%u", &b0, &b1, &b2, &b3) == 4) {
\r
9381 hp = (struct hostent *) calloc(1, sizeof(struct hostent));
\r
9382 hp->h_addrtype = AF_INET;
\r
9384 hp->h_addr_list = (char **) calloc(2, sizeof(char *));
\r
9385 hp->h_addr_list[0] = (char *) malloc(4);
\r
9386 hp->h_addr_list[0][0] = (char) b0;
\r
9387 hp->h_addr_list[0][1] = (char) b1;
\r
9388 hp->h_addr_list[0][2] = (char) b2;
\r
9389 hp->h_addr_list[0][3] = (char) b3;
\r
9395 sa.sin_family = hp->h_addrtype;
\r
9396 uport = (unsigned short) 514;
\r
9397 sa.sin_port = htons(uport);
\r
9398 memcpy((char *) &sa.sin_addr, hp->h_addr, hp->h_length);
\r
9400 /* Bind local socket to unused "privileged" port address
\r
9402 s = INVALID_SOCKET;
\r
9403 memset((char *) &mysa, 0, sizeof(struct sockaddr_in));
\r
9404 mysa.sin_family = AF_INET;
\r
9405 mysa.sin_addr.s_addr = INADDR_ANY;
\r
9406 for (fromPort = 1023;; fromPort--) {
\r
9407 if (fromPort < 0) {
\r
9409 return WSAEADDRINUSE;
\r
9411 if (s == INVALID_SOCKET) {
\r
9412 if ((s = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {
\r
9413 err = WSAGetLastError();
\r
9418 uport = (unsigned short) fromPort;
\r
9419 mysa.sin_port = htons(uport);
\r
9420 if (bind(s, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))
\r
9421 == SOCKET_ERROR) {
\r
9422 err = WSAGetLastError();
\r
9423 if (err == WSAEADDRINUSE) continue;
\r
9427 if (connect(s, (struct sockaddr *) &sa,
\r
9428 sizeof(struct sockaddr_in)) == SOCKET_ERROR) {
\r
9429 err = WSAGetLastError();
\r
9430 if (err == WSAEADDRINUSE) {
\r
9441 /* Bind stderr local socket to unused "privileged" port address
\r
9443 s2 = INVALID_SOCKET;
\r
9444 memset((char *) &mysa, 0, sizeof(struct sockaddr_in));
\r
9445 mysa.sin_family = AF_INET;
\r
9446 mysa.sin_addr.s_addr = INADDR_ANY;
\r
9447 for (fromPort = 1023;; fromPort--) {
\r
9448 if (fromPort == prevStderrPort) continue; // don't reuse port
\r
9449 if (fromPort < 0) {
\r
9450 (void) closesocket(s);
\r
9452 return WSAEADDRINUSE;
\r
9454 if (s2 == INVALID_SOCKET) {
\r
9455 if ((s2 = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {
\r
9456 err = WSAGetLastError();
\r
9462 uport = (unsigned short) fromPort;
\r
9463 mysa.sin_port = htons(uport);
\r
9464 if (bind(s2, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))
\r
9465 == SOCKET_ERROR) {
\r
9466 err = WSAGetLastError();
\r
9467 if (err == WSAEADDRINUSE) continue;
\r
9468 (void) closesocket(s);
\r
9472 if (listen(s2, 1) == SOCKET_ERROR) {
\r
9473 err = WSAGetLastError();
\r
9474 if (err == WSAEADDRINUSE) {
\r
9476 s2 = INVALID_SOCKET;
\r
9479 (void) closesocket(s);
\r
9480 (void) closesocket(s2);
\r
9486 prevStderrPort = fromPort; // remember port used
\r
9487 snprintf(stderrPortStr, MSG_SIZ, "%d", fromPort);
\r
9489 if (send(s, stderrPortStr, strlen(stderrPortStr) + 1, 0) == SOCKET_ERROR) {
\r
9490 err = WSAGetLastError();
\r
9491 (void) closesocket(s);
\r
9492 (void) closesocket(s2);
\r
9497 if (send(s, UserName(), strlen(UserName()) + 1, 0) == SOCKET_ERROR) {
\r
9498 err = WSAGetLastError();
\r
9499 (void) closesocket(s);
\r
9500 (void) closesocket(s2);
\r
9504 if (*user == NULLCHAR) user = UserName();
\r
9505 if (send(s, user, strlen(user) + 1, 0) == SOCKET_ERROR) {
\r
9506 err = WSAGetLastError();
\r
9507 (void) closesocket(s);
\r
9508 (void) closesocket(s2);
\r
9512 if (send(s, cmd, strlen(cmd) + 1, 0) == SOCKET_ERROR) {
\r
9513 err = WSAGetLastError();
\r
9514 (void) closesocket(s);
\r
9515 (void) closesocket(s2);
\r
9520 if ((s3 = accept(s2, NULL, NULL)) == INVALID_SOCKET) {
\r
9521 err = WSAGetLastError();
\r
9522 (void) closesocket(s);
\r
9523 (void) closesocket(s2);
\r
9527 (void) closesocket(s2); /* Stop listening */
\r
9529 /* Prepare return value */
\r
9530 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
\r
9531 cp->kind = CPRcmd;
\r
9534 *pr = (ProcRef *) cp;
\r
9541 AddInputSource(ProcRef pr, int lineByLine,
\r
9542 InputCallback func, VOIDSTAR closure)
\r
9544 InputSource *is, *is2 = NULL;
\r
9545 ChildProc *cp = (ChildProc *) pr;
\r
9547 is = (InputSource *) calloc(1, sizeof(InputSource));
\r
9548 is->lineByLine = lineByLine;
\r
9550 is->closure = closure;
\r
9551 is->second = NULL;
\r
9552 is->next = is->buf;
\r
9553 if (pr == NoProc) {
\r
9554 is->kind = CPReal;
\r
9555 consoleInputSource = is;
\r
9557 is->kind = cp->kind;
\r
9559 [AS] Try to avoid a race condition if the thread is given control too early:
\r
9560 we create all threads suspended so that the is->hThread variable can be
\r
9561 safely assigned, then let the threads start with ResumeThread.
\r
9563 switch (cp->kind) {
\r
9565 is->hFile = cp->hFrom;
\r
9566 cp->hFrom = NULL; /* now owned by InputThread */
\r
9568 CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) NonOvlInputThread,
\r
9569 (LPVOID) is, CREATE_SUSPENDED, &is->id);
\r
9573 is->hFile = cp->hFrom;
\r
9574 cp->hFrom = NULL; /* now owned by InputThread */
\r
9576 CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) InputThread,
\r
9577 (LPVOID) is, CREATE_SUSPENDED, &is->id);
\r
9581 is->sock = cp->sock;
\r
9583 CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,
\r
9584 (LPVOID) is, CREATE_SUSPENDED, &is->id);
\r
9588 is2 = (InputSource *) calloc(1, sizeof(InputSource));
\r
9590 is->sock = cp->sock;
\r
9592 is2->sock = cp->sock2;
\r
9593 is2->second = is2;
\r
9595 CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,
\r
9596 (LPVOID) is, CREATE_SUSPENDED, &is->id);
\r
9598 CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,
\r
9599 (LPVOID) is2, CREATE_SUSPENDED, &is2->id);
\r
9603 if( is->hThread != NULL ) {
\r
9604 ResumeThread( is->hThread );
\r
9607 if( is2 != NULL && is2->hThread != NULL ) {
\r
9608 ResumeThread( is2->hThread );
\r
9612 return (InputSourceRef) is;
\r
9616 RemoveInputSource(InputSourceRef isr)
\r
9620 is = (InputSource *) isr;
\r
9621 is->hThread = NULL; /* tell thread to stop */
\r
9622 CloseHandle(is->hThread);
\r
9623 if (is->second != NULL) {
\r
9624 is->second->hThread = NULL;
\r
9625 CloseHandle(is->second->hThread);
\r
9629 int no_wrap(char *message, int count)
\r
9631 ConsoleOutput(message, count, FALSE);
\r
9636 OutputToProcess(ProcRef pr, char *message, int count, int *outError)
\r
9639 int outCount = SOCKET_ERROR;
\r
9640 ChildProc *cp = (ChildProc *) pr;
\r
9641 static OVERLAPPED ovl;
\r
9642 static int line = 0;
\r
9646 if (appData.noJoin || !appData.useInternalWrap)
\r
9647 return no_wrap(message, count);
\r
9650 int width = get_term_width();
\r
9651 int len = wrap(NULL, message, count, width, &line);
\r
9652 char *msg = malloc(len);
\r
9656 return no_wrap(message, count);
\r
9659 dbgchk = wrap(msg, message, count, width, &line);
\r
9660 if (dbgchk != len && appData.debugMode)
\r
9661 fprintf(debugFP, "wrap(): dbgchk(%d) != len(%d)\n", dbgchk, len);
\r
9662 ConsoleOutput(msg, len, FALSE);
\r
9669 if (ovl.hEvent == NULL) {
\r
9670 ovl.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
\r
9672 ovl.Internal = ovl.InternalHigh = ovl.Offset = ovl.OffsetHigh = 0;
\r
9674 switch (cp->kind) {
\r
9677 outCount = send(cp->sock, message, count, 0);
\r
9678 if (outCount == SOCKET_ERROR) {
\r
9679 *outError = WSAGetLastError();
\r
9681 *outError = NO_ERROR;
\r
9686 if (WriteFile(((ChildProc *)pr)->hTo, message, count,
\r
9687 &dOutCount, NULL)) {
\r
9688 *outError = NO_ERROR;
\r
9689 outCount = (int) dOutCount;
\r
9691 *outError = GetLastError();
\r
9696 *outError = DoWriteFile(((ChildProc *)pr)->hTo, message, count,
\r
9697 &dOutCount, &ovl);
\r
9698 if (*outError == NO_ERROR) {
\r
9699 outCount = (int) dOutCount;
\r
9709 if(n != 0) Sleep(n);
\r
9713 OutputToProcessDelayed(ProcRef pr, char *message, int count, int *outError,
\r
9716 /* Ignore delay, not implemented for WinBoard */
\r
9717 return OutputToProcess(pr, message, count, outError);
\r
9722 CmailSigHandlerCallBack(InputSourceRef isr, VOIDSTAR closure,
\r
9723 char *buf, int count, int error)
\r
9725 DisplayFatalError(_("Not implemented"), 0, 1);
\r
9728 /* see wgamelist.c for Game List functions */
\r
9729 /* see wedittags.c for Edit Tags functions */
\r
9736 char buf[MSG_SIZ];
\r
9739 if (SearchPath(installDir, appData.icsLogon, NULL, MSG_SIZ, buf, &dummy)) {
\r
9740 f = fopen(buf, "r");
\r
9742 ProcessICSInitScript(f);
\r
9750 StartAnalysisClock()
\r
9752 if (analysisTimerEvent) return;
\r
9753 analysisTimerEvent = SetTimer(hwndMain, (UINT) ANALYSIS_TIMER_ID,
\r
9754 (UINT) 2000, NULL);
\r
9758 SetHighlights(int fromX, int fromY, int toX, int toY)
\r
9760 highlightInfo.sq[0].x = fromX;
\r
9761 highlightInfo.sq[0].y = fromY;
\r
9762 highlightInfo.sq[1].x = toX;
\r
9763 highlightInfo.sq[1].y = toY;
\r
9769 highlightInfo.sq[0].x = highlightInfo.sq[0].y =
\r
9770 highlightInfo.sq[1].x = highlightInfo.sq[1].y = -1;
\r
9774 SetPremoveHighlights(int fromX, int fromY, int toX, int toY)
\r
9776 premoveHighlightInfo.sq[0].x = fromX;
\r
9777 premoveHighlightInfo.sq[0].y = fromY;
\r
9778 premoveHighlightInfo.sq[1].x = toX;
\r
9779 premoveHighlightInfo.sq[1].y = toY;
\r
9783 ClearPremoveHighlights()
\r
9785 premoveHighlightInfo.sq[0].x = premoveHighlightInfo.sq[0].y =
\r
9786 premoveHighlightInfo.sq[1].x = premoveHighlightInfo.sq[1].y = -1;
\r
9790 ShutDownFrontEnd()
\r
9792 if (saveSettingsOnExit) SaveSettings(settingsFileName);
\r
9793 DeleteClipboardTempFiles();
\r
9799 if (IsIconic(hwndMain))
\r
9800 ShowWindow(hwndMain, SW_RESTORE);
\r
9802 SetActiveWindow(hwndMain);
\r
9806 * Prototypes for animation support routines
\r
9808 static void ScreenSquare(int column, int row, POINT * pt);
\r
9809 static void Tween( POINT * start, POINT * mid, POINT * finish, int factor,
\r
9810 POINT frames[], int * nFrames);
\r
9816 AnimateAtomicCapture(Board board, int fromX, int fromY, int toX, int toY)
\r
9817 { // [HGM] atomic: animate blast wave
\r
9820 explodeInfo.fromX = fromX;
\r
9821 explodeInfo.fromY = fromY;
\r
9822 explodeInfo.toX = toX;
\r
9823 explodeInfo.toY = toY;
\r
9824 for(i=1; i<4*kFactor; i++) {
\r
9825 explodeInfo.radius = (i*180)/(4*kFactor-1);
\r
9826 DrawPosition(FALSE, board);
\r
9827 Sleep(appData.animSpeed);
\r
9829 explodeInfo.radius = 0;
\r
9830 DrawPosition(TRUE, board);
\r
9834 AnimateMove(board, fromX, fromY, toX, toY)
\r
9841 ChessSquare piece;
\r
9842 POINT start, finish, mid;
\r
9843 POINT frames[kFactor * 2 + 1];
\r
9846 if (!appData.animate) return;
\r
9847 if (doingSizing) return;
\r
9848 if (fromY < 0 || fromX < 0) return;
\r
9849 piece = board[fromY][fromX];
\r
9850 if (piece >= EmptySquare) return;
\r
9852 ScreenSquare(fromX, fromY, &start);
\r
9853 ScreenSquare(toX, toY, &finish);
\r
9855 /* All moves except knight jumps move in straight line */
\r
9856 if (!(abs(fromX-toX) == 1 && abs(fromY-toY) == 2 || abs(fromX-toX) == 2 && abs(fromY-toY) == 1)) {
\r
9857 mid.x = start.x + (finish.x - start.x) / 2;
\r
9858 mid.y = start.y + (finish.y - start.y) / 2;
\r
9860 /* Knight: make straight movement then diagonal */
\r
9861 if (abs(toY - fromY) < abs(toX - fromX)) {
\r
9862 mid.x = start.x + (finish.x - start.x) / 2;
\r
9866 mid.y = start.y + (finish.y - start.y) / 2;
\r
9870 /* Don't use as many frames for very short moves */
\r
9871 if (abs(toY - fromY) + abs(toX - fromX) <= 2)
\r
9872 Tween(&start, &mid, &finish, kFactor - 1, frames, &nFrames);
\r
9874 Tween(&start, &mid, &finish, kFactor, frames, &nFrames);
\r
9876 animInfo.from.x = fromX;
\r
9877 animInfo.from.y = fromY;
\r
9878 animInfo.to.x = toX;
\r
9879 animInfo.to.y = toY;
\r
9880 animInfo.lastpos = start;
\r
9881 animInfo.piece = piece;
\r
9882 for (n = 0; n < nFrames; n++) {
\r
9883 animInfo.pos = frames[n];
\r
9884 DrawPosition(FALSE, NULL);
\r
9885 animInfo.lastpos = animInfo.pos;
\r
9886 Sleep(appData.animSpeed);
\r
9888 animInfo.pos = finish;
\r
9889 DrawPosition(FALSE, NULL);
\r
9890 animInfo.piece = EmptySquare;
\r
9891 Explode(board, fromX, fromY, toX, toY);
\r
9894 /* Convert board position to corner of screen rect and color */
\r
9897 ScreenSquare(column, row, pt)
\r
9898 int column; int row; POINT * pt;
\r
9901 pt->x = lineGap + ((BOARD_WIDTH-1)-column) * (squareSize + lineGap);
\r
9902 pt->y = lineGap + row * (squareSize + lineGap);
\r
9904 pt->x = lineGap + column * (squareSize + lineGap);
\r
9905 pt->y = lineGap + ((BOARD_HEIGHT-1)-row) * (squareSize + lineGap);
\r
9909 /* Generate a series of frame coords from start->mid->finish.
\r
9910 The movement rate doubles until the half way point is
\r
9911 reached, then halves back down to the final destination,
\r
9912 which gives a nice slow in/out effect. The algorithmn
\r
9913 may seem to generate too many intermediates for short
\r
9914 moves, but remember that the purpose is to attract the
\r
9915 viewers attention to the piece about to be moved and
\r
9916 then to where it ends up. Too few frames would be less
\r
9920 Tween(start, mid, finish, factor, frames, nFrames)
\r
9921 POINT * start; POINT * mid;
\r
9922 POINT * finish; int factor;
\r
9923 POINT frames[]; int * nFrames;
\r
9925 int n, fraction = 1, count = 0;
\r
9927 /* Slow in, stepping 1/16th, then 1/8th, ... */
\r
9928 for (n = 0; n < factor; n++)
\r
9930 for (n = 0; n < factor; n++) {
\r
9931 frames[count].x = start->x + (mid->x - start->x) / fraction;
\r
9932 frames[count].y = start->y + (mid->y - start->y) / fraction;
\r
9934 fraction = fraction / 2;
\r
9938 frames[count] = *mid;
\r
9941 /* Slow out, stepping 1/2, then 1/4, ... */
\r
9943 for (n = 0; n < factor; n++) {
\r
9944 frames[count].x = finish->x - (finish->x - mid->x) / fraction;
\r
9945 frames[count].y = finish->y - (finish->y - mid->y) / fraction;
\r
9947 fraction = fraction * 2;
\r
9953 SettingsPopUp(ChessProgramState *cps)
\r
9954 { // [HGM] wrapper needed because handles must not be passed through back-end
\r
9955 EngineOptionsPopup(savedHwnd, cps);
\r
9958 int flock(int fid, int code)
\r
9960 HANDLE hFile = (HANDLE) _get_osfhandle(fid);
\r
9964 ov.OffsetHigh = 0;
\r
9966 case 1: LockFileEx(hFile, LOCKFILE_EXCLUSIVE_LOCK, 0, 1024, 0, &ov); break; // LOCK_SH
\r
9967 case 2: LockFileEx(hFile, LOCKFILE_EXCLUSIVE_LOCK, 0, 1024, 0, &ov); break; // LOCK_EX
\r
9968 case 3: UnlockFileEx(hFile, 0, 1024, 0, &ov); break; // LOCK_UN
\r
9969 default: return -1;
\r