2 * WinBoard.c -- Windows NT front end to XBoard
\r
4 * Copyright 1991 by Digital Equipment Corporation, Maynard,
\r
7 * Enhancements Copyright 1992-2001, 2002, 2003, 2004, 2005, 2006,
\r
8 * 2007, 2008, 2009, 2010, 2011, 2012 Free Software Foundation, Inc.
\r
10 * Enhancements Copyright 2005 Alessandro Scotti
\r
12 * XBoard borrows its colors and the bitmaps.xchess bitmap set from XChess,
\r
13 * which was written and is copyrighted by Wayne Christopher.
\r
15 * The following terms apply to Digital Equipment Corporation's copyright
\r
16 * interest in XBoard:
\r
17 * ------------------------------------------------------------------------
\r
18 * All Rights Reserved
\r
20 * Permission to use, copy, modify, and distribute this software and its
\r
21 * documentation for any purpose and without fee is hereby granted,
\r
22 * provided that the above copyright notice appear in all copies and that
\r
23 * both that copyright notice and this permission notice appear in
\r
24 * supporting documentation, and that the name of Digital not be
\r
25 * used in advertising or publicity pertaining to distribution of the
\r
26 * software without specific, written prior permission.
\r
28 * DIGITAL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
\r
29 * ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL
\r
30 * DIGITAL BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
\r
31 * ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
\r
32 * WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
\r
33 * ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
\r
35 * ------------------------------------------------------------------------
\r
37 * The following terms apply to the enhanced version of XBoard
\r
38 * distributed by the Free Software Foundation:
\r
39 * ------------------------------------------------------------------------
\r
41 * GNU XBoard is free software: you can redistribute it and/or modify
\r
42 * it under the terms of the GNU General Public License as published by
\r
43 * the Free Software Foundation, either version 3 of the License, or (at
\r
44 * your option) any later version.
\r
46 * GNU XBoard is distributed in the hope that it will be useful, but
\r
47 * WITHOUT ANY WARRANTY; without even the implied warranty of
\r
48 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
\r
49 * General Public License for more details.
\r
51 * You should have received a copy of the GNU General Public License
\r
52 * along with this program. If not, see http://www.gnu.org/licenses/. *
\r
54 *------------------------------------------------------------------------
\r
55 ** See the file ChangeLog for a revision history. */
\r
59 #include <windows.h>
\r
60 #include <winuser.h>
\r
61 #include <winsock.h>
\r
62 #include <commctrl.h>
\r
68 #include <sys/stat.h>
\r
71 #include <commdlg.h>
\r
73 #include <richedit.h>
\r
74 #include <mmsystem.h>
\r
84 #include "frontend.h"
\r
85 #include "backend.h"
\r
86 #include "winboard.h"
\r
88 #include "wclipbrd.h"
\r
89 #include "woptions.h"
\r
90 #include "wsockerr.h"
\r
91 #include "defaults.h"
\r
95 //void InitEngineUCI( const char * iniDir, ChessProgramState * cps );
\r
98 void mysrandom(unsigned int seed);
\r
100 extern int whiteFlag, blackFlag;
\r
101 Boolean flipClock = FALSE;
\r
102 extern HANDLE chatHandle[];
\r
103 extern enum ICS_TYPE ics_type;
\r
105 int MySearchPath P((char *installDir, char *name, char *fullname));
\r
106 int MyGetFullPathName P((char *name, char *fullname));
\r
107 void DisplayHoldingsCount(HDC hdc, int x, int y, int align, int copyNumber);
\r
108 VOID NewVariantPopup(HWND hwnd);
\r
109 int FinishMove P((ChessMove moveType, int fromX, int fromY, int toX, int toY,
\r
110 /*char*/int promoChar));
\r
111 void DisplayMove P((int moveNumber));
\r
112 Boolean ParseFEN P((Board board, int *blackPlaysFirst, char *fen));
\r
113 void ChatPopUp P((char *s));
\r
115 ChessSquare piece;
\r
116 POINT pos; /* window coordinates of current pos */
\r
117 POINT lastpos; /* window coordinates of last pos - used for clipping */
\r
118 POINT from; /* board coordinates of the piece's orig pos */
\r
119 POINT to; /* board coordinates of the piece's new pos */
\r
122 static AnimInfo animInfo = { EmptySquare, {-1,-1}, {-1,-1}, {-1,-1} };
\r
125 POINT start; /* window coordinates of start pos */
\r
126 POINT pos; /* window coordinates of current pos */
\r
127 POINT lastpos; /* window coordinates of last pos - used for clipping */
\r
128 POINT from; /* board coordinates of the piece's orig pos */
\r
132 static DragInfo dragInfo = { {-1,-1}, {-1,-1}, {-1,-1}, {-1,-1}, EmptySquare };
\r
135 POINT sq[2]; /* board coordinates of from, to squares */
\r
138 static HighlightInfo highlightInfo = { {{-1, -1}, {-1, -1}} };
\r
139 static HighlightInfo premoveHighlightInfo = { {{-1, -1}, {-1, -1}} };
\r
140 static HighlightInfo partnerHighlightInfo = { {{-1, -1}, {-1, -1}} };
\r
141 static HighlightInfo oldPartnerHighlight = { {{-1, -1}, {-1, -1}} };
\r
143 typedef struct { // [HGM] atomic
\r
144 int fromX, fromY, toX, toY, radius;
\r
147 static ExplodeInfo explodeInfo;
\r
149 /* Window class names */
\r
150 char szAppName[] = "WinBoard";
\r
151 char szConsoleName[] = "WBConsole";
\r
153 /* Title bar text */
\r
154 char szTitle[] = "WinBoard";
\r
155 char szConsoleTitle[] = "I C S Interaction";
\r
158 char *settingsFileName;
\r
159 Boolean saveSettingsOnExit;
\r
160 char installDir[MSG_SIZ];
\r
161 int errorExitStatus;
\r
163 BoardSize boardSize;
\r
164 Boolean chessProgram;
\r
165 //static int boardX, boardY;
\r
166 int minX, minY; // [HGM] placement: volatile limits on upper-left corner
\r
167 int squareSize, lineGap, minorSize, border;
\r
168 static int winW, winH;
\r
169 static RECT messageRect, whiteRect, blackRect, leftLogoRect, rightLogoRect; // [HGM] logo
\r
170 static int logoHeight = 0;
\r
171 static char messageText[MESSAGE_TEXT_MAX];
\r
172 static int clockTimerEvent = 0;
\r
173 static int loadGameTimerEvent = 0;
\r
174 static int analysisTimerEvent = 0;
\r
175 static DelayedEventCallback delayedTimerCallback;
\r
176 static int delayedTimerEvent = 0;
\r
177 static int buttonCount = 2;
\r
178 char *icsTextMenuString;
\r
180 char *firstChessProgramNames;
\r
181 char *secondChessProgramNames;
\r
183 #define PALETTESIZE 256
\r
185 HINSTANCE hInst; /* current instance */
\r
186 Boolean alwaysOnTop = FALSE;
\r
188 COLORREF lightSquareColor, darkSquareColor, whitePieceColor,
\r
189 blackPieceColor, highlightSquareColor, premoveHighlightColor;
\r
191 ColorClass currentColorClass;
\r
193 static HWND savedHwnd;
\r
194 HWND hCommPort = NULL; /* currently open comm port */
\r
195 static HWND hwndPause; /* pause button */
\r
196 static HBITMAP pieceBitmap[3][(int) BlackPawn]; /* [HGM] nr of bitmaps referred to bP in stead of wK */
\r
197 static HBRUSH lightSquareBrush, darkSquareBrush,
\r
198 blackSquareBrush, /* [HGM] for band between board and holdings */
\r
199 explodeBrush, /* [HGM] atomic */
\r
200 markerBrush, /* [HGM] markers */
\r
201 whitePieceBrush, blackPieceBrush, iconBkgndBrush /*, outlineBrush*/;
\r
202 static POINT gridEndpoints[(BOARD_RANKS + BOARD_FILES + 2) * 2];
\r
203 static DWORD gridVertexCounts[BOARD_RANKS + BOARD_FILES + 2];
\r
204 static HPEN gridPen = NULL;
\r
205 static HPEN highlightPen = NULL;
\r
206 static HPEN premovePen = NULL;
\r
207 static NPLOGPALETTE pLogPal;
\r
208 static BOOL paletteChanged = FALSE;
\r
209 static HICON iconWhite, iconBlack, iconCurrent;
\r
210 static int doingSizing = FALSE;
\r
211 static int lastSizing = 0;
\r
212 static int prevStderrPort;
\r
213 static HBITMAP userLogo;
\r
215 static HBITMAP liteBackTexture = NULL;
\r
216 static HBITMAP darkBackTexture = NULL;
\r
217 static int liteBackTextureMode = BACK_TEXTURE_MODE_PLAIN;
\r
218 static int darkBackTextureMode = BACK_TEXTURE_MODE_PLAIN;
\r
219 static int backTextureSquareSize = 0;
\r
220 static struct { int x; int y; int mode; } backTextureSquareInfo[BOARD_RANKS][BOARD_FILES];
\r
222 #if __GNUC__ && !defined(_winmajor)
\r
223 #define oldDialog 0 /* cygwin doesn't define _winmajor; mingw does */
\r
225 #if defined(_winmajor)
\r
226 #define oldDialog (_winmajor < 4)
\r
228 #define oldDialog 0
\r
232 #define INTERNATIONAL
\r
234 #ifdef INTERNATIONAL
\r
235 # define _(s) T_(s)
\r
241 # define Translate(x, y)
\r
242 # define LoadLanguageFile(s)
\r
245 #ifdef INTERNATIONAL
\r
247 Boolean barbaric; // flag indicating if translation is needed
\r
249 // list of item numbers used in each dialog (used to alter language at run time)
\r
251 #define ABOUTBOX -1 /* not sure why these are needed */
\r
252 #define ABOUTBOX2 -1
\r
254 int dialogItems[][42] = {
\r
255 { ABOUTBOX, IDOK, OPT_MESS, 400 },
\r
256 { DLG_TimeControl, IDC_Babble, OPT_TCUseMoves, OPT_TCUseInc, OPT_TCUseFixed,
\r
257 OPT_TCtext1, OPT_TCtext2, OPT_TCitext1, OPT_TCitext2, OPT_TCftext, GPB_Factors, IDC_Factor1, IDC_Factor2, IDOK, IDCANCEL },
\r
258 { DLG_LoadOptions, OPT_Autostep, OPT_AStext1, OPT_Exact, OPT_Subset, OPT_Struct, OPT_Material, OPT_Range, OPT_Difference,
\r
259 OPT_elo1t, OPT_elo2t, OPT_datet, OPT_Stretch, OPT_Stretcht, OPT_Reversed, OPT_SearchMode, OPT_Mirror, OPT_thresholds, IDOK, IDCANCEL },
\r
260 { DLG_SaveOptions, OPT_Autosave, OPT_AVPrompt, OPT_AVToFile, OPT_AVBrowse,
\r
261 801, OPT_PGN, OPT_Old, OPT_OutOfBookInfo, IDOK, IDCANCEL },
\r
262 { 1536, 1090, IDC_Directories, 1089, 1091, IDOK, IDCANCEL, 1038, IDC_IndexNr, 1037 },
\r
263 { DLG_CommPort, IDOK, IDCANCEL, IDC_Port, IDC_Rate, IDC_Bits, IDC_Parity,
\r
264 IDC_Stop, IDC_Flow, OPT_SerialHelp },
\r
265 { DLG_EditComment, IDOK, OPT_CancelComment, OPT_ClearComment, OPT_EditComment },
\r
266 { DLG_PromotionKing, PB_Chancellor, PB_Archbishop, PB_Queen, PB_Rook,
\r
267 PB_Bishop, PB_Knight, PB_King, IDCANCEL, IDC_Yes, IDC_No, IDC_Centaur },
\r
268 { ABOUTBOX2, IDC_ChessBoard },
\r
269 { DLG_GameList, OPT_GameListLoad, OPT_GameListPrev, OPT_GameListNext,
\r
270 OPT_GameListClose, IDC_GameListDoFilter },
\r
271 { DLG_EditTags, IDOK, OPT_TagsCancel, OPT_EditTags },
\r
272 { DLG_Error, IDOK },
\r
273 { DLG_Colorize, IDOK, IDCANCEL, OPT_ChooseColor, OPT_Bold, OPT_Italic,
\r
274 OPT_Underline, OPT_Strikeout, OPT_Sample },
\r
275 { DLG_Question, IDOK, IDCANCEL, OPT_QuestionText },
\r
276 { DLG_Startup, IDC_Welcome, OPT_ChessEngine, OPT_ChessServer, OPT_View,
\r
277 IDC_SPECIFY_ENG_STATIC, IDC_SPECIFY_SERVER_STATIC, OPT_AnyAdditional,
\r
278 IDOK, IDCANCEL, IDM_HELPCONTENTS },
\r
279 { DLG_IndexNumber, IDC_Index },
\r
280 { DLG_TypeInMove, IDOK, IDCANCEL },
\r
281 { DLG_TypeInName, IDOK, IDCANCEL },
\r
282 { DLG_Sound, IDC_Event, OPT_NoSound, OPT_DefaultBeep, OPT_BuiltInSound,
\r
283 OPT_WavFile, OPT_BrowseSound, OPT_DefaultSounds, IDOK, IDCANCEL, OPT_PlaySound },
\r
284 { DLG_GeneralOptions, IDOK, IDCANCEL, OPT_AlwaysOnTop, OPT_HighlightLastMove,
\r
285 OPT_AlwaysQueen, OPT_PeriodicUpdates, OPT_AnimateDragging, OPT_PonderNextMove,
\r
286 OPT_AnimateMoving, OPT_PopupExitMessage, OPT_AutoFlag, OPT_PopupMoveErrors,
\r
287 OPT_AutoFlipView, OPT_ShowButtonBar, OPT_AutoRaiseBoard, OPT_ShowCoordinates,
\r
288 OPT_Blindfold, OPT_ShowThinking, OPT_HighlightDragging, OPT_TestLegality,
\r
289 OPT_SaveExtPGN, OPT_HideThinkFromHuman, OPT_ExtraInfoInMoveHistory,
\r
290 OPT_HighlightMoveArrow, OPT_AutoLogo ,OPT_SmartMove },
\r
291 { DLG_IcsOptions, IDOK, IDCANCEL, OPT_AutoComment, OPT_AutoKibitz, OPT_AutoObserve,
\r
292 OPT_GetMoveList, OPT_LocalLineEditing, OPT_QuietPlay, OPT_SeekGraph, OPT_AutoRefresh,
\r
293 OPT_BgObserve, OPT_DualBoard, OPT_Premove, OPT_PremoveWhite, OPT_PremoveBlack,
\r
294 OPT_SmartMove, OPT_IcsAlarm, IDC_Sec, OPT_ChooseShoutColor, OPT_ChooseSShoutColor,
\r
295 OPT_ChooseChannel1Color, OPT_ChooseChannelColor, OPT_ChooseKibitzColor,
\r
296 OPT_ChooseTellColor, OPT_ChooseChallengeColor, OPT_ChooseRequestColor,
\r
297 OPT_ChooseSeekColor, OPT_ChooseNormalColor, OPT_ChooseBackgroundColor,
\r
298 OPT_DefaultColors, OPT_DontColorize, IDC_Boxes, GPB_Colors, GPB_Premove,
\r
299 GPB_General, GPB_Alarm, OPT_AutoCreate },
\r
300 { DLG_BoardOptions, IDOK, IDCANCEL, OPT_SizeTiny, OPT_SizeTeeny, OPT_SizeDinky,
\r
301 OPT_SizePetite, OPT_SizeSlim, OPT_SizeSmall, OPT_SizeMediocre, OPT_SizeMiddling,
\r
302 OPT_SizeAverage, OPT_SizeModerate, OPT_SizeMedium, OPT_SizeBulky, OPT_SizeLarge,
\r
303 OPT_SizeBig, OPT_SizeHuge, OPT_SizeGiant, OPT_SizeColossal, OPT_SizeTitanic,
\r
304 OPT_ChooseLightSquareColor, OPT_ChooseDarkSquareColor, OPT_ChooseWhitePieceColor,
\r
305 OPT_ChooseBlackPieceColor, OPT_ChooseHighlightSquareColor, OPT_ChoosePremoveHighlightColor,
\r
306 OPT_Monochrome, OPT_AllWhite, OPT_UpsideDown, OPT_DefaultBoardColors, GPB_Colors,
\r
307 IDC_Light, IDC_Dark, IDC_White, IDC_Black, IDC_High, IDC_PreHigh, GPB_Size, OPT_Bitmaps, OPT_PieceFont, OPT_Grid },
\r
308 { DLG_NewVariant, IDOK, IDCANCEL, OPT_VariantNormal, OPT_VariantFRC, OPT_VariantWildcastle,
\r
309 OPT_VariantNocastle, OPT_VariantLosers, OPT_VariantGiveaway, OPT_VariantSuicide,
\r
310 OPT_Variant3Check, OPT_VariantTwoKings, OPT_VariantAtomic, OPT_VariantCrazyhouse,
\r
311 OPT_VariantBughouse, OPT_VariantTwilight, OPT_VariantShogi, OPT_VariantSuper,
\r
312 OPT_VariantKnightmate, OPT_VariantBerolina, OPT_VariantCylinder, OPT_VariantFairy,
\r
313 OPT_VariantMakruk, OPT_VariantGothic, OPT_VariantCapablanca, OPT_VariantJanus,
\r
314 OPT_VariantCRC, OPT_VariantFalcon, OPT_VariantCourier, OPT_VariantGreat, OPT_VariantSChess,
\r
315 OPT_VariantShatranj, OPT_VariantXiangqi, GPB_Variant, GPB_Board, IDC_Height,
\r
316 IDC_Width, IDC_Hand, IDC_Pieces, IDC_Def },
\r
317 { DLG_Fonts, IDOK, IDCANCEL, OPT_ChooseClockFont, OPT_ChooseMessageFont,
\r
318 OPT_ChooseCoordFont, OPT_ChooseTagFont, OPT_ChooseCommentsFont, OPT_ChooseConsoleFont, OPT_ChooseMoveHistoryFont, OPT_DefaultFonts,
\r
319 OPT_ClockFont, OPT_MessageFont, OPT_CoordFont, OPT_EditTagsFont, OPT_ChoosePieceFont, OPT_MessageFont8,
\r
320 OPT_SampleGameListFont, OPT_ChooseGameListFont, OPT_MessageFont7,
\r
321 OPT_CommentsFont, OPT_MessageFont5, GPB_Current, GPB_All, OPT_MessageFont6 },
\r
322 { DLG_NewGameFRC, IDC_NFG_Label, IDC_NFG_Random, IDOK, IDCANCEL },
\r
323 { DLG_GameListOptions, IDC_GLT, IDC_GLT_Up, IDC_GLT_Down, IDC_GLT_Restore,
\r
324 IDC_GLT_Default, IDOK, IDCANCEL, IDC_GLT_RestoreTo },
\r
325 { DLG_MoveHistory },
\r
326 { DLG_EvalGraph },
\r
327 { DLG_EngineOutput, IDC_EngineLabel1, IDC_Engine1_NPS, IDC_EngineLabel2, IDC_Engine2_NPS },
\r
328 { DLG_Chat, IDC_Partner, IDC_Clear, IDC_Send, },
\r
329 { DLG_EnginePlayOptions, IDC_EpPonder, IDC_EpShowThinking, IDC_EpHideThinkingHuman,
\r
330 IDC_EpPeriodicUpdates, GPB_Adjudications, IDC_Draw, IDC_Moves, IDC_Threshold,
\r
331 IDC_Centi, IDC_TestClaims, IDC_DetectMates, IDC_MaterialDraws, IDC_TrivialDraws,
\r
332 GPB_Apply, IDC_Rule, IDC_Repeats, IDC_ScoreAbs1, IDC_ScoreAbs2, IDOK, IDCANCEL },
\r
333 { DLG_OptionsUCI, IDC_PolyDir, IDC_BrowseForPolyglotDir, IDC_Hash, IDC_Path,
\r
334 IDC_BrowseForEGTB, IDC_Cache, IDC_UseBook, IDC_BrowseForBook, IDC_CPU, IDC_OwnBook1,
\r
335 IDC_OwnBook2, IDC_Depth, IDC_Variation, IDC_DefGames, IDOK, IDCANCEL },
\r
339 static char languageBuf[70000], *foreign[1000], *english[1000], *languageFile[MSG_SIZ];
\r
340 static int lastChecked;
\r
341 static char oldLanguage[MSG_SIZ], *menuText[10][30];
\r
342 extern int tinyLayout;
\r
343 extern char * menuBarText[][10];
\r
346 LoadLanguageFile(char *name)
\r
347 { //load the file with translations, and make a list of the strings to be translated, and their translations
\r
349 int i=0, j=0, n=0, k;
\r
352 if(!name || name[0] == NULLCHAR) return;
\r
353 snprintf(buf, MSG_SIZ, "%s%s", name, strchr(name, '.') ? "" : ".lng"); // auto-append lng extension
\r
354 appData.language = oldLanguage;
\r
355 if(!strcmp(buf, oldLanguage)) { barbaric = 1; return; } // this language already loaded; just switch on
\r
356 if((f = fopen(buf, "r")) == NULL) return;
\r
357 while((k = fgetc(f)) != EOF) {
\r
358 if(i >= sizeof(languageBuf)) { DisplayError("Language file too big", 0); return; }
\r
359 languageBuf[i] = k;
\r
361 if(languageBuf[n] == '"' && languageBuf[i-1] == '"') {
\r
363 if(p = strstr(languageBuf + n + 1, "\" === \"")) {
\r
364 if(p > languageBuf+n+2 && p+8 < languageBuf+i) {
\r
365 if(j >= sizeof(english)) { DisplayError("Too many translated strings", 0); return; }
\r
366 english[j] = languageBuf + n + 1; *p = 0;
\r
367 foreign[j++] = p + 7; languageBuf[i-1] = 0;
\r
368 //if(appData.debugMode) fprintf(debugFP, "translation: replace '%s' by '%s'\n", english[j-1], foreign[j-1]);
\r
373 } else if(i > 0 && languageBuf[i-1] == '\\') {
\r
375 case 'n': k = '\n'; break;
\r
376 case 'r': k = '\r'; break;
\r
377 case 't': k = '\t'; break;
\r
379 languageBuf[--i] = k;
\r
384 barbaric = (j != 0);
\r
385 safeStrCpy(oldLanguage, buf, sizeof(oldLanguage)/sizeof(oldLanguage[0]) );
\r
390 { // return the translation of the given string
\r
391 // efficiency can be improved a lot...
\r
393 static char buf[MSG_SIZ];
\r
394 //if(appData.debugMode) fprintf(debugFP, "T_(%s)\n", s);
\r
395 if(!barbaric) return s;
\r
396 if(!s) return ""; // sanity
\r
397 while(english[i]) {
\r
398 if(!strcmp(s, english[i])) return foreign[i];
\r
399 if(english[i][0] == '%' && strstr(s, english[i]+1) == s) { // allow translation of strings with variable ending
\r
400 snprintf(buf, MSG_SIZ, "%s%s", foreign[i], s + strlen(english[i]+1)); // keep unmatched portion
\r
409 Translate(HWND hDlg, int dialogID)
\r
410 { // translate all text items in the given dialog
\r
412 char buf[MSG_SIZ], *s;
\r
413 if(!barbaric) return;
\r
414 while(dialogItems[i][0] && dialogItems[i][0] != dialogID) i++; // find the dialog description
\r
415 if(dialogItems[i][0] != dialogID) return; // unknown dialog, should not happen
\r
416 GetWindowText( hDlg, buf, MSG_SIZ );
\r
418 if(strcmp(buf, s)) SetWindowText(hDlg, s); // replace by translated string (if different)
\r
419 for(j=1; k=dialogItems[i][j]; j++) { // translate all listed dialog items
\r
420 GetDlgItemText(hDlg, k, buf, MSG_SIZ);
\r
421 if(strlen(buf) == 0) continue;
\r
423 if(strcmp(buf, s)) SetDlgItemText(hDlg, k, s); // replace by translated string (if different)
\r
428 TranslateOneMenu(int i, HMENU subMenu)
\r
431 static MENUITEMINFO info;
\r
433 info.cbSize = sizeof(MENUITEMINFO);
\r
434 info.fMask = MIIM_STATE | MIIM_TYPE;
\r
435 for(j=GetMenuItemCount(subMenu)-1; j>=0; j--){
\r
437 info.dwTypeData = buf;
\r
438 info.cch = sizeof(buf);
\r
439 GetMenuItemInfo(subMenu, j, TRUE, &info);
\r
441 if(menuText[i][j]) safeStrCpy(buf, menuText[i][j], sizeof(buf)/sizeof(buf[0]) );
\r
442 else menuText[i][j] = strdup(buf); // remember original on first change
\r
444 if(buf[0] == NULLCHAR) continue;
\r
445 info.dwTypeData = T_(buf);
\r
446 info.cch = strlen(buf)+1;
\r
447 SetMenuItemInfo(subMenu, j, TRUE, &info);
\r
453 TranslateMenus(int addLanguage)
\r
456 WIN32_FIND_DATA fileData;
\r
458 #define IDM_English 1970
\r
460 HMENU mainMenu = GetMenu(hwndMain);
\r
461 for (i=GetMenuItemCount(mainMenu)-1; i>=0; i--) {
\r
462 HMENU subMenu = GetSubMenu(mainMenu, i);
\r
463 ModifyMenu(mainMenu, i, MF_STRING|MF_BYPOSITION|MF_POPUP|EnableMenuItem(mainMenu, i, MF_BYPOSITION),
\r
464 (UINT) subMenu, T_(menuBarText[tinyLayout][i]));
\r
465 TranslateOneMenu(i, subMenu);
\r
467 DrawMenuBar(hwndMain);
\r
470 if(!addLanguage) return;
\r
471 if((hFind = FindFirstFile("*.LNG", &fileData)) != INVALID_HANDLE_VALUE) {
\r
472 HMENU mainMenu = GetMenu(hwndMain);
\r
473 HMENU subMenu = GetSubMenu(mainMenu, GetMenuItemCount(mainMenu)-1);
\r
474 AppendMenu(subMenu, MF_SEPARATOR, (UINT_PTR) 0, NULL);
\r
475 AppendMenu(subMenu, MF_ENABLED|MF_STRING|(barbaric?MF_UNCHECKED:MF_CHECKED), (UINT_PTR) IDM_English, (LPCTSTR) "English");
\r
476 i = 0; lastChecked = IDM_English;
\r
478 char *p, *q = fileData.cFileName;
\r
479 int checkFlag = MF_UNCHECKED;
\r
480 languageFile[i] = strdup(q);
\r
481 if(barbaric && !strcmp(oldLanguage, q)) {
\r
482 checkFlag = MF_CHECKED;
\r
483 lastChecked = IDM_English + i + 1;
\r
484 CheckMenuItem(mainMenu, IDM_English, MF_BYCOMMAND|MF_UNCHECKED);
\r
486 *q = ToUpper(*q); while(*++q) *q = ToLower(*q);
\r
487 p = strstr(fileData.cFileName, ".lng");
\r
489 AppendMenu(subMenu, MF_ENABLED|MF_STRING|checkFlag, (UINT_PTR) IDM_English + ++i, (LPCTSTR) fileData.cFileName);
\r
490 } while(FindNextFile(hFind, &fileData));
\r
497 #define IDM_RecentEngines 3000
\r
500 RecentEngineMenu (char *s)
\r
502 if(appData.icsActive) return;
\r
503 if(appData.recentEngines > 0 && *s) { // feature is on, and list non-empty
\r
504 HMENU mainMenu = GetMenu(hwndMain);
\r
505 HMENU subMenu = GetSubMenu(mainMenu, 5); // Engine menu
\r
506 int i=IDM_RecentEngines;
\r
507 recentEngines = strdup(appData.recentEngineList); // remember them as they are in menu
\r
508 AppendMenu(subMenu, MF_SEPARATOR, (UINT_PTR) 0, NULL);
\r
510 char *p = strchr(s, '\n');
\r
511 if(p == NULL) return; // malformed!
\r
513 AppendMenu(subMenu, MF_ENABLED|MF_STRING|MF_UNCHECKED, (UINT_PTR) i++, (LPCTSTR) s);
\r
527 int cliWidth, cliHeight;
\r
530 SizeInfo sizeInfo[] =
\r
532 { "tiny", 21, 0, 1, 1, 0, 0 },
\r
533 { "teeny", 25, 1, 1, 1, 0, 0 },
\r
534 { "dinky", 29, 1, 1, 1, 0, 0 },
\r
535 { "petite", 33, 1, 1, 1, 0, 0 },
\r
536 { "slim", 37, 2, 1, 0, 0, 0 },
\r
537 { "small", 40, 2, 1, 0, 0, 0 },
\r
538 { "mediocre", 45, 2, 1, 0, 0, 0 },
\r
539 { "middling", 49, 2, 0, 0, 0, 0 },
\r
540 { "average", 54, 2, 0, 0, 0, 0 },
\r
541 { "moderate", 58, 3, 0, 0, 0, 0 },
\r
542 { "medium", 64, 3, 0, 0, 0, 0 },
\r
543 { "bulky", 72, 3, 0, 0, 0, 0 },
\r
544 { "large", 80, 3, 0, 0, 0, 0 },
\r
545 { "big", 87, 3, 0, 0, 0, 0 },
\r
546 { "huge", 95, 3, 0, 0, 0, 0 },
\r
547 { "giant", 108, 3, 0, 0, 0, 0 },
\r
548 { "colossal", 116, 4, 0, 0, 0, 0 },
\r
549 { "titanic", 129, 4, 0, 0, 0, 0 },
\r
550 { NULL, 0, 0, 0, 0, 0, 0 }
\r
553 #define MF(x) {x, {{0,}, 0. }, {0, }, 0}
\r
554 MyFont fontRec[NUM_SIZES][NUM_FONTS] =
\r
556 { MF(CLOCK_FONT_TINY), MF(MESSAGE_FONT_TINY), MF(COORD_FONT_TINY), MF(CONSOLE_FONT_TINY), MF(COMMENT_FONT_TINY), MF(EDITTAGS_FONT_TINY), MF(MOVEHISTORY_FONT_ALL), MF(GAMELIST_FONT_ALL) },
\r
557 { MF(CLOCK_FONT_TEENY), MF(MESSAGE_FONT_TEENY), MF(COORD_FONT_TEENY), MF(CONSOLE_FONT_TEENY), MF(COMMENT_FONT_TEENY), MF(EDITTAGS_FONT_TEENY), MF(MOVEHISTORY_FONT_ALL), MF(GAMELIST_FONT_ALL) },
\r
558 { MF(CLOCK_FONT_DINKY), MF(MESSAGE_FONT_DINKY), MF(COORD_FONT_DINKY), MF(CONSOLE_FONT_DINKY), MF(COMMENT_FONT_DINKY), MF(EDITTAGS_FONT_DINKY), MF(MOVEHISTORY_FONT_ALL), MF(GAMELIST_FONT_ALL) },
\r
559 { MF(CLOCK_FONT_PETITE), MF(MESSAGE_FONT_PETITE), MF(COORD_FONT_PETITE), MF(CONSOLE_FONT_PETITE), MF(COMMENT_FONT_PETITE), MF(EDITTAGS_FONT_PETITE), MF(MOVEHISTORY_FONT_ALL), MF(GAMELIST_FONT_ALL) },
\r
560 { MF(CLOCK_FONT_SLIM), MF(MESSAGE_FONT_SLIM), MF(COORD_FONT_SLIM), MF(CONSOLE_FONT_SLIM), MF(COMMENT_FONT_SLIM), MF(EDITTAGS_FONT_SLIM), MF(MOVEHISTORY_FONT_ALL), MF(GAMELIST_FONT_ALL) },
\r
561 { MF(CLOCK_FONT_SMALL), MF(MESSAGE_FONT_SMALL), MF(COORD_FONT_SMALL), MF(CONSOLE_FONT_SMALL), MF(COMMENT_FONT_SMALL), MF(EDITTAGS_FONT_SMALL), MF(MOVEHISTORY_FONT_ALL), MF(GAMELIST_FONT_ALL) },
\r
562 { MF(CLOCK_FONT_MEDIOCRE), MF(MESSAGE_FONT_MEDIOCRE), MF(COORD_FONT_MEDIOCRE), MF(CONSOLE_FONT_MEDIOCRE), MF(COMMENT_FONT_MEDIOCRE), MF(EDITTAGS_FONT_MEDIOCRE), MF(MOVEHISTORY_FONT_ALL), MF(GAMELIST_FONT_ALL) },
\r
563 { MF(CLOCK_FONT_MIDDLING), MF(MESSAGE_FONT_MIDDLING), MF(COORD_FONT_MIDDLING), MF(CONSOLE_FONT_MIDDLING), MF(COMMENT_FONT_MIDDLING), MF(EDITTAGS_FONT_MIDDLING), MF(MOVEHISTORY_FONT_ALL), MF(GAMELIST_FONT_ALL) },
\r
564 { MF(CLOCK_FONT_AVERAGE), MF(MESSAGE_FONT_AVERAGE), MF(COORD_FONT_AVERAGE), MF(CONSOLE_FONT_AVERAGE), MF(COMMENT_FONT_AVERAGE), MF(EDITTAGS_FONT_AVERAGE), MF(MOVEHISTORY_FONT_ALL), MF(GAMELIST_FONT_ALL) },
\r
565 { MF(CLOCK_FONT_MODERATE), MF(MESSAGE_FONT_MODERATE), MF(COORD_FONT_MODERATE), MF(CONSOLE_FONT_MODERATE), MF(COMMENT_FONT_MODERATE), MF(EDITTAGS_FONT_MODERATE), MF(MOVEHISTORY_FONT_ALL), MF(GAMELIST_FONT_ALL) },
\r
566 { MF(CLOCK_FONT_MEDIUM), MF(MESSAGE_FONT_MEDIUM), MF(COORD_FONT_MEDIUM), MF(CONSOLE_FONT_MEDIUM), MF(COMMENT_FONT_MEDIUM), MF(EDITTAGS_FONT_MEDIUM), MF(MOVEHISTORY_FONT_ALL), MF(GAMELIST_FONT_ALL) },
\r
567 { MF(CLOCK_FONT_BULKY), MF(MESSAGE_FONT_BULKY), MF(COORD_FONT_BULKY), MF(CONSOLE_FONT_BULKY), MF(COMMENT_FONT_BULKY), MF(EDITTAGS_FONT_BULKY), MF(MOVEHISTORY_FONT_ALL), MF(GAMELIST_FONT_ALL) },
\r
568 { MF(CLOCK_FONT_LARGE), MF(MESSAGE_FONT_LARGE), MF(COORD_FONT_LARGE), MF(CONSOLE_FONT_LARGE), MF(COMMENT_FONT_LARGE), MF(EDITTAGS_FONT_LARGE), MF(MOVEHISTORY_FONT_ALL), MF(GAMELIST_FONT_ALL) },
\r
569 { MF(CLOCK_FONT_BIG), MF(MESSAGE_FONT_BIG), MF(COORD_FONT_BIG), MF(CONSOLE_FONT_BIG), MF(COMMENT_FONT_BIG), MF(EDITTAGS_FONT_BIG), MF(MOVEHISTORY_FONT_ALL), MF(GAMELIST_FONT_ALL) },
\r
570 { MF(CLOCK_FONT_HUGE), MF(MESSAGE_FONT_HUGE), MF(COORD_FONT_HUGE), MF(CONSOLE_FONT_HUGE), MF(COMMENT_FONT_HUGE), MF(EDITTAGS_FONT_HUGE), MF(MOVEHISTORY_FONT_ALL), MF(GAMELIST_FONT_ALL) },
\r
571 { MF(CLOCK_FONT_GIANT), MF(MESSAGE_FONT_GIANT), MF(COORD_FONT_GIANT), MF(CONSOLE_FONT_GIANT), MF(COMMENT_FONT_GIANT), MF(EDITTAGS_FONT_GIANT), MF(MOVEHISTORY_FONT_ALL), MF(GAMELIST_FONT_ALL) },
\r
572 { MF(CLOCK_FONT_COLOSSAL), MF(MESSAGE_FONT_COLOSSAL), MF(COORD_FONT_COLOSSAL), MF(CONSOLE_FONT_COLOSSAL), MF(COMMENT_FONT_COLOSSAL), MF(EDITTAGS_FONT_COLOSSAL), MF(MOVEHISTORY_FONT_ALL), MF (GAMELIST_FONT_ALL) },
\r
573 { MF(CLOCK_FONT_TITANIC), MF(MESSAGE_FONT_TITANIC), MF(COORD_FONT_TITANIC), MF(CONSOLE_FONT_TITANIC), MF(COMMENT_FONT_TITANIC), MF(EDITTAGS_FONT_TITANIC), MF(MOVEHISTORY_FONT_ALL), MF(GAMELIST_FONT_ALL) },
\r
576 MyFont *font[NUM_SIZES][NUM_FONTS];
\r
585 #define BUTTON_WIDTH (tinyLayout ? 16 : 32)
\r
586 #define N_BUTTONS 5
\r
588 MyButtonDesc buttonDesc[N_BUTTONS] =
\r
590 {"<<", IDM_ToStart, NULL, NULL},
\r
591 {"<", IDM_Backward, NULL, NULL},
\r
592 {"P", IDM_Pause, NULL, NULL},
\r
593 {">", IDM_Forward, NULL, NULL},
\r
594 {">>", IDM_ToEnd, NULL, NULL},
\r
597 int tinyLayout = 0, smallLayout = 0;
\r
598 #define MENU_BAR_ITEMS 9
\r
599 char *menuBarText[2][MENU_BAR_ITEMS+1] = {
\r
600 { N_("&File"), N_("&Edit"), N_("&View"), N_("&Mode"), N_("&Action"), N_("E&ngine"), N_("&Options"), N_("&Help"), NULL },
\r
601 { N_("&F"), N_("&E"), N_("&V"), N_("&M"), N_("&A"), N_("&N"), N_("&O"), N_("&H"), NULL },
\r
605 MySound sounds[(int)NSoundClasses];
\r
606 MyTextAttribs textAttribs[(int)NColorClasses];
\r
608 MyColorizeAttribs colorizeAttribs[] = {
\r
609 { (COLORREF)0, 0, N_("Shout Text") },
\r
610 { (COLORREF)0, 0, N_("SShout/CShout") },
\r
611 { (COLORREF)0, 0, N_("Channel 1 Text") },
\r
612 { (COLORREF)0, 0, N_("Channel Text") },
\r
613 { (COLORREF)0, 0, N_("Kibitz Text") },
\r
614 { (COLORREF)0, 0, N_("Tell Text") },
\r
615 { (COLORREF)0, 0, N_("Challenge Text") },
\r
616 { (COLORREF)0, 0, N_("Request Text") },
\r
617 { (COLORREF)0, 0, N_("Seek Text") },
\r
618 { (COLORREF)0, 0, N_("Normal Text") },
\r
619 { (COLORREF)0, 0, N_("None") }
\r
624 static char *commentTitle;
\r
625 static char *commentText;
\r
626 static int commentIndex;
\r
627 static Boolean editComment = FALSE;
\r
630 char errorTitle[MSG_SIZ];
\r
631 char errorMessage[2*MSG_SIZ];
\r
632 HWND errorDialog = NULL;
\r
633 BOOLEAN moveErrorMessageUp = FALSE;
\r
634 BOOLEAN consoleEcho = TRUE;
\r
635 CHARFORMAT consoleCF;
\r
636 COLORREF consoleBackgroundColor;
\r
638 char *programVersion;
\r
644 typedef int CPKind;
\r
653 SOCKET sock2; /* stderr socket for OpenRcmd */
\r
656 #define INPUT_SOURCE_BUF_SIZE 4096
\r
658 typedef struct _InputSource {
\r
665 char buf[INPUT_SOURCE_BUF_SIZE];
\r
669 InputCallback func;
\r
670 struct _InputSource *second; /* for stderr thread on CPRcmd */
\r
674 InputSource *consoleInputSource;
\r
679 VOID ConsoleOutput(char* data, int length, int forceVisible);
\r
680 VOID ConsoleCreate();
\r
682 ConsoleWndProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam);
\r
683 VOID ColorizeTextPopup(HWND hwnd, ColorClass cc);
\r
684 VOID PrintCommSettings(FILE *f, char *name, DCB *dcb);
\r
685 VOID ParseCommSettings(char *arg, DCB *dcb);
\r
687 StartupDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam);
\r
688 VOID APIENTRY MenuPopup(HWND hwnd, POINT pt, HMENU hmenu, UINT def);
\r
689 void ParseIcsTextMenu(char *icsTextMenuString);
\r
690 VOID PopUpNameDialog(char firstchar);
\r
691 VOID UpdateSampleText(HWND hDlg, int id, MyColorizeAttribs *mca);
\r
695 int GameListOptions();
\r
697 int dummy; // [HGM] for obsolete args
\r
699 HWND hwndMain = NULL; /* root window*/
\r
700 HWND hwndConsole = NULL;
\r
701 HWND commentDialog = NULL;
\r
702 HWND moveHistoryDialog = NULL;
\r
703 HWND evalGraphDialog = NULL;
\r
704 HWND engineOutputDialog = NULL;
\r
705 HWND gameListDialog = NULL;
\r
706 HWND editTagsDialog = NULL;
\r
708 int commentUp = FALSE;
\r
710 WindowPlacement wpMain;
\r
711 WindowPlacement wpConsole;
\r
712 WindowPlacement wpComment;
\r
713 WindowPlacement wpMoveHistory;
\r
714 WindowPlacement wpEvalGraph;
\r
715 WindowPlacement wpEngineOutput;
\r
716 WindowPlacement wpGameList;
\r
717 WindowPlacement wpTags;
\r
719 VOID EngineOptionsPopup(); // [HGM] settings
\r
721 VOID GothicPopUp(char *title, VariantClass variant);
\r
723 * Setting "frozen" should disable all user input other than deleting
\r
724 * the window. We do this while engines are initializing themselves.
\r
726 static int frozen = 0;
\r
727 static int oldMenuItemState[MENU_BAR_ITEMS];
\r
733 if (frozen) return;
\r
735 hmenu = GetMenu(hwndMain);
\r
736 for (i=0; i<MENU_BAR_ITEMS; i++) {
\r
737 oldMenuItemState[i] = EnableMenuItem(hmenu, i, MF_BYPOSITION|MF_GRAYED);
\r
739 DrawMenuBar(hwndMain);
\r
742 /* Undo a FreezeUI */
\r
748 if (!frozen) return;
\r
750 hmenu = GetMenu(hwndMain);
\r
751 for (i=0; i<MENU_BAR_ITEMS; i++) {
\r
752 EnableMenuItem(hmenu, i, MF_BYPOSITION|oldMenuItemState[i]);
\r
754 DrawMenuBar(hwndMain);
\r
757 /*static*/ int fromX = -1, fromY = -1, toX, toY; // [HGM] moved upstream, so JAWS can use them
\r
759 /* JAWS preparation patch (WinBoard for the sight impaired). Define required insertions as empty */
\r
765 #define JAWS_ALT_INTERCEPT
\r
766 #define JAWS_KBUP_NAVIGATION
\r
767 #define JAWS_KBDOWN_NAVIGATION
\r
768 #define JAWS_MENU_ITEMS
\r
769 #define JAWS_SILENCE
\r
770 #define JAWS_REPLAY
\r
772 #define JAWS_COPYRIGHT
\r
773 #define JAWS_DELETE(X) X
\r
774 #define SAYMACHINEMOVE()
\r
778 /*---------------------------------------------------------------------------*\
\r
782 \*---------------------------------------------------------------------------*/
\r
785 WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
\r
786 LPSTR lpCmdLine, int nCmdShow)
\r
789 HANDLE hAccelMain, hAccelNoAlt, hAccelNoICS;
\r
790 // INITCOMMONCONTROLSEX ex;
\r
794 LoadLibrary("RICHED32.DLL");
\r
795 consoleCF.cbSize = sizeof(CHARFORMAT);
\r
797 if (!InitApplication(hInstance)) {
\r
800 if (!InitInstance(hInstance, nCmdShow, lpCmdLine)) {
\r
807 // InitCommonControlsEx(&ex);
\r
808 InitCommonControls();
\r
810 hAccelMain = LoadAccelerators (hInstance, szAppName);
\r
811 hAccelNoAlt = LoadAccelerators (hInstance, "NO_ALT");
\r
812 hAccelNoICS = LoadAccelerators( hInstance, "NO_ICS"); /* [AS] No Ctrl-V on ICS!!! */
\r
814 /* Acquire and dispatch messages until a WM_QUIT message is received. */
\r
816 while (GetMessage(&msg, /* message structure */
\r
817 NULL, /* handle of window receiving the message */
\r
818 0, /* lowest message to examine */
\r
819 0)) /* highest message to examine */
\r
822 if(msg.message == WM_CHAR && msg.wParam == '\t') {
\r
823 // [HGM] navigate: switch between all windows with tab
\r
824 HWND e1 = NULL, e2 = NULL, mh = NULL, hInput = NULL, hText = NULL;
\r
825 int i, currentElement = 0;
\r
827 // first determine what element of the chain we come from (if any)
\r
828 if(appData.icsActive) {
\r
829 hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
830 hText = GetDlgItem(hwndConsole, OPT_ConsoleText);
\r
832 if(engineOutputDialog && EngineOutputIsUp()) {
\r
833 e1 = GetDlgItem(engineOutputDialog, IDC_EngineMemo1);
\r
834 e2 = GetDlgItem(engineOutputDialog, IDC_EngineMemo2);
\r
836 if(moveHistoryDialog && MoveHistoryIsUp()) {
\r
837 mh = GetDlgItem(moveHistoryDialog, IDC_MoveHistory);
\r
839 if(msg.hwnd == hwndMain) currentElement = 7 ; else
\r
840 if(msg.hwnd == engineOutputDialog) currentElement = 2; else
\r
841 if(msg.hwnd == e1) currentElement = 2; else
\r
842 if(msg.hwnd == e2) currentElement = 3; else
\r
843 if(msg.hwnd == moveHistoryDialog) currentElement = 4; else
\r
844 if(msg.hwnd == mh) currentElement = 4; else
\r
845 if(msg.hwnd == evalGraphDialog) currentElement = 6; else
\r
846 if(msg.hwnd == hText) currentElement = 5; else
\r
847 if(msg.hwnd == hInput) currentElement = 6; else
\r
848 for (i = 0; i < N_BUTTONS; i++) {
\r
849 if (buttonDesc[i].hwnd == msg.hwnd) { currentElement = 1; break; }
\r
852 // determine where to go to
\r
853 if(currentElement) { HWND h = NULL; int direction = GetKeyState(VK_SHIFT) < 0 ? -1 : 1;
\r
855 currentElement = (currentElement + direction) % 7;
\r
856 switch(currentElement) {
\r
858 h = hwndMain; break; // passing this case always makes the loop exit
\r
860 h = buttonDesc[0].hwnd; break; // could be NULL
\r
862 if(!EngineOutputIsUp()) continue; // skip closed auxiliary windows
\r
865 if(!EngineOutputIsUp()) continue;
\r
868 if(!MoveHistoryIsUp()) continue;
\r
870 // case 6: // input to eval graph does not seem to get here!
\r
871 // if(!EvalGraphIsUp()) continue;
\r
872 // h = evalGraphDialog; break;
\r
874 if(!appData.icsActive) continue;
\r
878 if(!appData.icsActive) continue;
\r
884 if(currentElement > 4 && IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);
\r
885 if(currentElement < 5 && IsIconic(hwndMain)) ShowWindow(hwndMain, SW_RESTORE); // all open together
\r
888 continue; // this message now has been processed
\r
892 if (!(commentDialog && IsDialogMessage(commentDialog, &msg)) &&
\r
893 !(moveHistoryDialog && IsDialogMessage(moveHistoryDialog, &msg)) &&
\r
894 !(evalGraphDialog && IsDialogMessage(evalGraphDialog, &msg)) &&
\r
895 !(engineOutputDialog && IsDialogMessage(engineOutputDialog, &msg)) &&
\r
896 !(editTagsDialog && IsDialogMessage(editTagsDialog, &msg)) &&
\r
897 !(gameListDialog && IsDialogMessage(gameListDialog, &msg)) &&
\r
898 !(errorDialog && IsDialogMessage(errorDialog, &msg)) &&
\r
899 !(!frozen && TranslateAccelerator(hwndMain, hAccelMain, &msg)) && JAWS_ACCEL
\r
900 !(!hwndConsole && TranslateAccelerator(hwndMain, hAccelNoICS, &msg)) &&
\r
901 !(!hwndConsole && TranslateAccelerator(hwndMain, hAccelNoAlt, &msg))) {
\r
902 int done = 0, i; // [HGM] chat: dispatch cat-box messages
\r
903 for(i=0; i<MAX_CHAT; i++)
\r
904 if(chatHandle[i] && IsDialogMessage(chatHandle[i], &msg)) {
\r
907 if(done) continue; // [HGM] chat: end patch
\r
908 TranslateMessage(&msg); /* Translates virtual key codes */
\r
909 DispatchMessage(&msg); /* Dispatches message to window */
\r
914 return (msg.wParam); /* Returns the value from PostQuitMessage */
\r
917 /*---------------------------------------------------------------------------*\
\r
919 * Initialization functions
\r
921 \*---------------------------------------------------------------------------*/
\r
925 { // update user logo if necessary
\r
926 static char oldUserName[MSG_SIZ], dir[MSG_SIZ], *curName;
\r
928 if(appData.autoLogo) {
\r
929 curName = UserName();
\r
930 if(strcmp(curName, oldUserName)) {
\r
931 GetCurrentDirectory(MSG_SIZ, dir);
\r
932 SetCurrentDirectory(installDir);
\r
933 snprintf(oldUserName, MSG_SIZ, "logos\\%s.bmp", curName);
\r
934 userLogo = LoadImage( 0, oldUserName, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );
\r
935 safeStrCpy(oldUserName, curName, sizeof(oldUserName)/sizeof(oldUserName[0]) );
\r
936 if(userLogo == NULL)
\r
937 userLogo = LoadImage( 0, "logos\\dummy.bmp", IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );
\r
938 SetCurrentDirectory(dir); /* return to prev directory */
\r
944 InitApplication(HINSTANCE hInstance)
\r
948 /* Fill in window class structure with parameters that describe the */
\r
951 wc.style = CS_HREDRAW | CS_VREDRAW; /* Class style(s). */
\r
952 wc.lpfnWndProc = (WNDPROC)WndProc; /* Window Procedure */
\r
953 wc.cbClsExtra = 0; /* No per-class extra data. */
\r
954 wc.cbWndExtra = 0; /* No per-window extra data. */
\r
955 wc.hInstance = hInstance; /* Owner of this class */
\r
956 wc.hIcon = LoadIcon(hInstance, "icon_white");
\r
957 wc.hCursor = LoadCursor(NULL, IDC_ARROW); /* Cursor */
\r
958 wc.hbrBackground = (HBRUSH)(COLOR_WINDOW+1); /* Default color */
\r
959 wc.lpszMenuName = szAppName; /* Menu name from .RC */
\r
960 wc.lpszClassName = szAppName; /* Name to register as */
\r
962 /* Register the window class and return success/failure code. */
\r
963 if (!RegisterClass(&wc)) return FALSE;
\r
965 wc.style = CS_HREDRAW | CS_VREDRAW;
\r
966 wc.lpfnWndProc = (WNDPROC)ConsoleWndProc;
\r
968 wc.cbWndExtra = DLGWINDOWEXTRA;
\r
969 wc.hInstance = hInstance;
\r
970 wc.hIcon = LoadIcon(hInstance, "icon_white");
\r
971 wc.hCursor = LoadCursor(NULL, IDC_ARROW);
\r
972 wc.hbrBackground = (HBRUSH)(COLOR_MENU+1);
\r
973 wc.lpszMenuName = NULL;
\r
974 wc.lpszClassName = szConsoleName;
\r
976 if (!RegisterClass(&wc)) return FALSE;
\r
981 /* Set by InitInstance, used by EnsureOnScreen */
\r
982 int screenHeight, screenWidth;
\r
985 EnsureOnScreen(int *x, int *y, int minX, int minY)
\r
987 // int gap = GetSystemMetrics(SM_CYFRAME) + GetSystemMetrics(SM_CYCAPTION);
\r
988 /* Be sure window at (x,y) is not off screen (or even mostly off screen) */
\r
989 if (*x > screenWidth - 32) *x = 0;
\r
990 if (*y > screenHeight - 32) *y = 0;
\r
991 if (*x < minX) *x = minX;
\r
992 if (*y < minY) *y = minY;
\r
996 LoadLogo(ChessProgramState *cps, int n, Boolean ics)
\r
998 char buf[MSG_SIZ], dir[MSG_SIZ];
\r
999 GetCurrentDirectory(MSG_SIZ, dir);
\r
1000 SetCurrentDirectory(installDir);
\r
1001 if( appData.logo[n] && appData.logo[n][0] != NULLCHAR) {
\r
1002 cps->programLogo = LoadImage( 0, appData.logo[n], IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );
\r
1004 if (cps->programLogo == NULL && appData.debugMode) {
\r
1005 fprintf( debugFP, "Unable to load logo bitmap '%s'\n", appData.logo[n] );
\r
1007 } else if(appData.autoLogo) {
\r
1008 if(ics) { // [HGM] logo: in ICS mode second can be used for ICS
\r
1009 char *opponent = "";
\r
1010 if(gameMode == IcsPlayingWhite) opponent = gameInfo.black;
\r
1011 if(gameMode == IcsPlayingBlack) opponent = gameInfo.white;
\r
1012 sprintf(buf, "logos\\%s\\%s.bmp", appData.icsHost, opponent);
\r
1013 if(!*opponent || !(cps->programLogo = LoadImage( 0, buf, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE ))) {
\r
1014 sprintf(buf, "logos\\%s.bmp", appData.icsHost);
\r
1015 cps->programLogo = LoadImage( 0, buf, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );
\r
1018 if(appData.directory[n] && appData.directory[n][0]) {
\r
1019 SetCurrentDirectory(appData.directory[n]);
\r
1020 cps->programLogo = LoadImage( 0, "logo.bmp", IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );
\r
1023 SetCurrentDirectory(dir); /* return to prev directory */
\r
1029 ZeroMemory( &backTextureSquareInfo, sizeof(backTextureSquareInfo) );
\r
1030 backTextureSquareSize = 0; // kludge to force recalculation of texturemode
\r
1032 if( appData.liteBackTextureFile && appData.liteBackTextureFile[0] != NULLCHAR && appData.liteBackTextureFile[0] != '*' ) {
\r
1033 if(liteBackTexture) DeleteObject(liteBackTexture);
\r
1034 liteBackTexture = LoadImage( 0, appData.liteBackTextureFile, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );
\r
1035 liteBackTextureMode = appData.liteBackTextureMode;
\r
1037 if (liteBackTexture == NULL && appData.debugMode) {
\r
1038 fprintf( debugFP, "Unable to load lite texture bitmap '%s'\n", appData.liteBackTextureFile );
\r
1042 if( appData.darkBackTextureFile && appData.darkBackTextureFile[0] != NULLCHAR && appData.darkBackTextureFile[0] != '*' ) {
\r
1043 if(darkBackTexture) DeleteObject(darkBackTexture);
\r
1044 darkBackTexture = LoadImage( 0, appData.darkBackTextureFile, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );
\r
1045 darkBackTextureMode = appData.darkBackTextureMode;
\r
1047 if (darkBackTexture == NULL && appData.debugMode) {
\r
1048 fprintf( debugFP, "Unable to load dark texture bitmap '%s'\n", appData.darkBackTextureFile );
\r
1054 InitInstance(HINSTANCE hInstance, int nCmdShow, LPSTR lpCmdLine)
\r
1056 HWND hwnd; /* Main window handle. */
\r
1058 WINDOWPLACEMENT wp;
\r
1061 hInst = hInstance; /* Store instance handle in our global variable */
\r
1062 programName = szAppName;
\r
1064 if (SearchPath(NULL, "WinBoard.exe", NULL, MSG_SIZ, installDir, &filepart)) {
\r
1065 *filepart = NULLCHAR;
\r
1067 GetCurrentDirectory(MSG_SIZ, installDir);
\r
1069 gameInfo.boardWidth = gameInfo.boardHeight = 8; // [HGM] won't have open window otherwise
\r
1070 screenWidth = screenHeight = 1000; // [HGM] placement: kludge to allow calling EnsureOnScreen from InitAppData
\r
1071 InitAppData(lpCmdLine); /* Get run-time parameters */
\r
1072 /* xboard, and older WinBoards, controlled the move sound with the
\r
1073 appData.ringBellAfterMoves option. In the current WinBoard, we
\r
1074 always turn the option on (so that the backend will call us),
\r
1075 then let the user turn the sound off by setting it to silence if
\r
1076 desired. To accommodate old winboard.ini files saved by old
\r
1077 versions of WinBoard, we also turn off the sound if the option
\r
1078 was initially set to false. [HGM] taken out of InitAppData */
\r
1079 if (!appData.ringBellAfterMoves) {
\r
1080 sounds[(int)SoundMove].name = strdup("");
\r
1081 appData.ringBellAfterMoves = TRUE;
\r
1083 if (appData.debugMode) {
\r
1084 debugFP = fopen(appData.nameOfDebugFile, "w");
\r
1085 setbuf(debugFP, NULL);
\r
1088 LoadLanguageFile(appData.language);
\r
1092 // InitEngineUCI( installDir, &first ); // [HGM] incorporated in InitBackEnd1()
\r
1093 // InitEngineUCI( installDir, &second );
\r
1095 /* Create a main window for this application instance. */
\r
1096 hwnd = CreateWindow(szAppName, szTitle,
\r
1097 (WS_OVERLAPPEDWINDOW & ~WS_MAXIMIZEBOX),
\r
1098 CW_USEDEFAULT, 0, CW_USEDEFAULT, 0,
\r
1099 NULL, NULL, hInstance, NULL);
\r
1102 /* If window could not be created, return "failure" */
\r
1107 /* [HGM] logo: Load logos if specified (must be done before InitDrawingSizes) */
\r
1108 LoadLogo(&first, 0, FALSE);
\r
1109 LoadLogo(&second, 1, appData.icsActive);
\r
1113 iconWhite = LoadIcon(hInstance, "icon_white");
\r
1114 iconBlack = LoadIcon(hInstance, "icon_black");
\r
1115 iconCurrent = iconWhite;
\r
1116 InitDrawingColors();
\r
1117 screenHeight = GetSystemMetrics(SM_CYSCREEN);
\r
1118 screenWidth = GetSystemMetrics(SM_CXSCREEN);
\r
1119 for (ibs = (int) NUM_SIZES - 1; ibs >= 0; ibs--) {
\r
1120 /* Compute window size for each board size, and use the largest
\r
1121 size that fits on this screen as the default. */
\r
1122 InitDrawingSizes((BoardSize)(ibs+1000), 0);
\r
1123 if (boardSize == (BoardSize)-1 &&
\r
1124 winH <= screenHeight
\r
1125 - GetSystemMetrics(SM_CYFRAME) - GetSystemMetrics(SM_CYCAPTION) - 10
\r
1126 && winW <= screenWidth) {
\r
1127 boardSize = (BoardSize)ibs;
\r
1131 InitDrawingSizes(boardSize, 0);
\r
1132 RecentEngineMenu(appData.recentEngineList);
\r
1134 buttonCount = GetSystemMetrics(SM_CMOUSEBUTTONS);
\r
1136 /* [AS] Load textures if specified */
\r
1139 mysrandom( (unsigned) time(NULL) );
\r
1141 /* [AS] Restore layout */
\r
1142 if( wpMoveHistory.visible ) {
\r
1143 MoveHistoryPopUp();
\r
1146 if( wpEvalGraph.visible ) {
\r
1150 if( wpEngineOutput.visible ) {
\r
1151 EngineOutputPopUp();
\r
1154 /* Make the window visible; update its client area; and return "success" */
\r
1155 EnsureOnScreen(&wpMain.x, &wpMain.y, minX, minY);
\r
1156 wp.length = sizeof(WINDOWPLACEMENT);
\r
1158 wp.showCmd = nCmdShow;
\r
1159 wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;
\r
1160 wp.rcNormalPosition.left = wpMain.x;
\r
1161 wp.rcNormalPosition.right = wpMain.x + wpMain.width;
\r
1162 wp.rcNormalPosition.top = wpMain.y;
\r
1163 wp.rcNormalPosition.bottom = wpMain.y + wpMain.height;
\r
1164 SetWindowPlacement(hwndMain, &wp);
\r
1166 InitBackEnd2(); // [HGM] moved until after all windows placed, to save correct position if fatal error on engine start
\r
1168 if(!appData.noGUI) SetWindowPos(hwndMain, alwaysOnTop ? HWND_TOPMOST : HWND_NOTOPMOST,
\r
1169 0, 0, 0, 0, SWP_NOMOVE|SWP_NOSIZE);
\r
1171 if (hwndConsole) {
\r
1173 SetWindowPos(hwndConsole, alwaysOnTop ? HWND_TOPMOST : HWND_NOTOPMOST,
\r
1174 0, 0, 0, 0, SWP_NOMOVE|SWP_NOSIZE);
\r
1176 ShowWindow(hwndConsole, nCmdShow);
\r
1177 SetActiveWindow(hwndConsole);
\r
1179 if(!appData.noGUI) UpdateWindow(hwnd); else ShowWindow(hwnd, SW_MINIMIZE);
\r
1180 if(gameListDialog) SetFocus(gameListDialog); // [HGM] jaws: for if we clicked multi-game game file
\r
1189 HMENU hmenu = GetMenu(hwndMain);
\r
1191 (void) EnableMenuItem(hmenu, IDM_CommPort,
\r
1192 MF_BYCOMMAND|((appData.icsActive &&
\r
1193 *appData.icsCommPort != NULLCHAR) ?
\r
1194 MF_ENABLED : MF_GRAYED));
\r
1195 (void) CheckMenuItem(hmenu, IDM_SaveSettingsOnExit,
\r
1196 MF_BYCOMMAND|(saveSettingsOnExit ?
\r
1197 MF_CHECKED : MF_UNCHECKED));
\r
1200 //---------------------------------------------------------------------------------------------------------
\r
1202 #define ICS_TEXT_MENU_SIZE (IDM_CommandXLast - IDM_CommandX + 1)
\r
1203 #define XBOARD FALSE
\r
1205 #define OPTCHAR "/"
\r
1206 #define SEPCHAR "="
\r
1207 #define TOPLEVEL 0
\r
1211 // front-end part of option handling
\r
1214 LFfromMFP(LOGFONT* lf, MyFontParams *mfp)
\r
1216 HDC hdc = CreateDC("DISPLAY", NULL, NULL, NULL);
\r
1217 lf->lfHeight = -(int)(mfp->pointSize * GetDeviceCaps(hdc, LOGPIXELSY) / 72.0 + 0.5);
\r
1220 lf->lfEscapement = 0;
\r
1221 lf->lfOrientation = 0;
\r
1222 lf->lfWeight = mfp->bold ? FW_BOLD : FW_NORMAL;
\r
1223 lf->lfItalic = mfp->italic;
\r
1224 lf->lfUnderline = mfp->underline;
\r
1225 lf->lfStrikeOut = mfp->strikeout;
\r
1226 lf->lfCharSet = mfp->charset;
\r
1227 lf->lfOutPrecision = OUT_DEFAULT_PRECIS;
\r
1228 lf->lfClipPrecision = CLIP_DEFAULT_PRECIS;
\r
1229 lf->lfQuality = DEFAULT_QUALITY;
\r
1230 lf->lfPitchAndFamily = DEFAULT_PITCH|FF_DONTCARE;
\r
1231 safeStrCpy(lf->lfFaceName, mfp->faceName, sizeof(lf->lfFaceName)/sizeof(lf->lfFaceName[0]) );
\r
1235 CreateFontInMF(MyFont *mf)
\r
1237 LFfromMFP(&mf->lf, &mf->mfp);
\r
1238 if (mf->hf) DeleteObject(mf->hf);
\r
1239 mf->hf = CreateFontIndirect(&mf->lf);
\r
1242 // [HGM] This platform-dependent table provides the location for storing the color info
\r
1244 colorVariable[] = {
\r
1245 &whitePieceColor,
\r
1246 &blackPieceColor,
\r
1247 &lightSquareColor,
\r
1248 &darkSquareColor,
\r
1249 &highlightSquareColor,
\r
1250 &premoveHighlightColor,
\r
1252 &consoleBackgroundColor,
\r
1253 &appData.fontForeColorWhite,
\r
1254 &appData.fontBackColorWhite,
\r
1255 &appData.fontForeColorBlack,
\r
1256 &appData.fontBackColorBlack,
\r
1257 &appData.evalHistColorWhite,
\r
1258 &appData.evalHistColorBlack,
\r
1259 &appData.highlightArrowColor,
\r
1262 /* Command line font name parser. NULL name means do nothing.
\r
1263 Syntax like "Courier New:10.0 bi" or "Arial:10" or "Arial:10b"
\r
1264 For backward compatibility, syntax without the colon is also
\r
1265 accepted, but font names with digits in them won't work in that case.
\r
1268 ParseFontName(char *name, MyFontParams *mfp)
\r
1271 if (name == NULL) return;
\r
1273 q = strchr(p, ':');
\r
1275 if (q - p >= sizeof(mfp->faceName))
\r
1276 ExitArgError(_("Font name too long:"), name, TRUE);
\r
1277 memcpy(mfp->faceName, p, q - p);
\r
1278 mfp->faceName[q - p] = NULLCHAR;
\r
1281 q = mfp->faceName;
\r
1283 while (*p && !isdigit(*p)) {
\r
1285 if (q - mfp->faceName >= sizeof(mfp->faceName))
\r
1286 ExitArgError(_("Font name too long:"), name, TRUE);
\r
1288 while (q > mfp->faceName && q[-1] == ' ') q--;
\r
1291 if (!*p) ExitArgError(_("Font point size missing:"), name, TRUE);
\r
1292 mfp->pointSize = (float) atof(p);
\r
1293 mfp->bold = (strchr(p, 'b') != NULL);
\r
1294 mfp->italic = (strchr(p, 'i') != NULL);
\r
1295 mfp->underline = (strchr(p, 'u') != NULL);
\r
1296 mfp->strikeout = (strchr(p, 's') != NULL);
\r
1297 mfp->charset = DEFAULT_CHARSET;
\r
1298 q = strchr(p, 'c');
\r
1300 mfp->charset = (BYTE) atoi(q+1);
\r
1304 ParseFont(char *name, int number)
\r
1305 { // wrapper to shield back-end from 'font'
\r
1306 ParseFontName(name, &font[boardSize][number]->mfp);
\r
1311 { // in WB we have a 2D array of fonts; this initializes their description
\r
1313 /* Point font array elements to structures and
\r
1314 parse default font names */
\r
1315 for (i=0; i<NUM_FONTS; i++) {
\r
1316 for (j=0; j<NUM_SIZES; j++) {
\r
1317 font[j][i] = &fontRec[j][i];
\r
1318 ParseFontName(font[j][i]->def, &font[j][i]->mfp);
\r
1325 { // here we create the actual fonts from the selected descriptions
\r
1327 for (i=0; i<NUM_FONTS; i++) {
\r
1328 for (j=0; j<NUM_SIZES; j++) {
\r
1329 CreateFontInMF(font[j][i]);
\r
1333 /* Color name parser.
\r
1334 X version accepts X color names, but this one
\r
1335 handles only the #rrggbb form (hex) or rrr,ggg,bbb (decimal) */
\r
1337 ParseColorName(char *name)
\r
1339 int red, green, blue, count;
\r
1340 char buf[MSG_SIZ];
\r
1342 count = sscanf(name, "#%2x%2x%2x", &red, &green, &blue);
\r
1344 count = sscanf(name, "%3d%*[^0-9]%3d%*[^0-9]%3d",
\r
1345 &red, &green, &blue);
\r
1348 snprintf(buf, MSG_SIZ, _("Can't parse color name %s"), name);
\r
1349 DisplayError(buf, 0);
\r
1350 return RGB(0, 0, 0);
\r
1352 return PALETTERGB(red, green, blue);
\r
1356 ParseColor(int n, char *name)
\r
1357 { // for WinBoard the color is an int, which needs to be derived from the string
\r
1358 if(colorVariable[n]) *(int*)colorVariable[n] = ParseColorName(name);
\r
1362 ParseAttribs(COLORREF *color, int *effects, char* argValue)
\r
1364 char *e = argValue;
\r
1368 if (*e == 'b') eff |= CFE_BOLD;
\r
1369 else if (*e == 'i') eff |= CFE_ITALIC;
\r
1370 else if (*e == 'u') eff |= CFE_UNDERLINE;
\r
1371 else if (*e == 's') eff |= CFE_STRIKEOUT;
\r
1372 else if (*e == '#' || isdigit(*e)) break;
\r
1376 *color = ParseColorName(e);
\r
1380 ParseTextAttribs(ColorClass cc, char *s)
\r
1381 { // [HGM] front-end wrapper that does the platform-dependent call
\r
1382 // for XBoard we would set (&appData.colorShout)[cc] = strdup(s);
\r
1383 ParseAttribs(&textAttribs[cc].color, &textAttribs[cc].effects, s);
\r
1387 ParseBoardSize(void *addr, char *name)
\r
1388 { // [HGM] rewritten with return-value ptr to shield back-end from BoardSize
\r
1389 BoardSize bs = SizeTiny;
\r
1390 while (sizeInfo[bs].name != NULL) {
\r
1391 if (StrCaseCmp(name, sizeInfo[bs].name) == 0) {
\r
1392 *(BoardSize *)addr = bs;
\r
1397 ExitArgError(_("Unrecognized board size value"), name, TRUE);
\r
1402 { // [HGM] import name from appData first
\r
1405 for (cc = (ColorClass)0; cc < ColorNormal; cc++) {
\r
1406 textAttribs[cc].sound.name = strdup((&appData.soundShout)[cc]);
\r
1407 textAttribs[cc].sound.data = NULL;
\r
1408 MyLoadSound(&textAttribs[cc].sound);
\r
1410 for (cc = ColorNormal; cc < NColorClasses; cc++) {
\r
1411 textAttribs[cc].sound.name = strdup("");
\r
1412 textAttribs[cc].sound.data = NULL;
\r
1414 for (sc = (SoundClass)0; sc < NSoundClasses; sc++) {
\r
1415 sounds[sc].name = strdup((&appData.soundMove)[sc]);
\r
1416 sounds[sc].data = NULL;
\r
1417 MyLoadSound(&sounds[sc]);
\r
1422 SetCommPortDefaults()
\r
1424 memset(&dcb, 0, sizeof(DCB)); // required by VS 2002 +
\r
1425 dcb.DCBlength = sizeof(DCB);
\r
1426 dcb.BaudRate = 9600;
\r
1427 dcb.fBinary = TRUE;
\r
1428 dcb.fParity = FALSE;
\r
1429 dcb.fOutxCtsFlow = FALSE;
\r
1430 dcb.fOutxDsrFlow = FALSE;
\r
1431 dcb.fDtrControl = DTR_CONTROL_ENABLE;
\r
1432 dcb.fDsrSensitivity = FALSE;
\r
1433 dcb.fTXContinueOnXoff = TRUE;
\r
1434 dcb.fOutX = FALSE;
\r
1436 dcb.fNull = FALSE;
\r
1437 dcb.fRtsControl = RTS_CONTROL_ENABLE;
\r
1438 dcb.fAbortOnError = FALSE;
\r
1440 dcb.Parity = SPACEPARITY;
\r
1441 dcb.StopBits = ONESTOPBIT;
\r
1444 // [HGM] args: these three cases taken out to stay in front-end
\r
1446 SaveFontArg(FILE *f, ArgDescriptor *ad)
\r
1447 { // in WinBoard every board size has its own font, and the "argLoc" identifies the table,
\r
1448 // while the curent board size determines the element. This system should be ported to XBoard.
\r
1449 // What the table contains pointers to, and how to print the font description, remains platform-dependent
\r
1451 for (bs=0; bs<NUM_SIZES; bs++) {
\r
1452 MyFontParams *mfp = &font[bs][(int) ad->argLoc]->mfp;
\r
1453 fprintf(f, "/size=%s ", sizeInfo[bs].name);
\r
1454 fprintf(f, "/%s=\"%s:%g%s%s%s%s%sc%d\"\n",
\r
1455 ad->argName, mfp->faceName, mfp->pointSize,
\r
1456 mfp->bold || mfp->italic || mfp->underline || mfp->strikeout ? " " : "",
\r
1457 mfp->bold ? "b" : "",
\r
1458 mfp->italic ? "i" : "",
\r
1459 mfp->underline ? "u" : "",
\r
1460 mfp->strikeout ? "s" : "",
\r
1461 (int)mfp->charset);
\r
1467 { // [HGM] copy the names from the internal WB variables to appData
\r
1470 for (cc = (ColorClass)0; cc < ColorNormal; cc++)
\r
1471 (&appData.soundShout)[cc] = textAttribs[cc].sound.name;
\r
1472 for (sc = (SoundClass)0; sc < NSoundClasses; sc++)
\r
1473 (&appData.soundMove)[sc] = sounds[sc].name;
\r
1477 SaveAttribsArg(FILE *f, ArgDescriptor *ad)
\r
1478 { // here the "argLoc" defines a table index. It could have contained the 'ta' pointer itself, though
\r
1479 MyTextAttribs* ta = &textAttribs[(ColorClass)ad->argLoc];
\r
1480 fprintf(f, "/%s=\"%s%s%s%s%s#%02lx%02lx%02lx\"\n", ad->argName,
\r
1481 (ta->effects & CFE_BOLD) ? "b" : "",
\r
1482 (ta->effects & CFE_ITALIC) ? "i" : "",
\r
1483 (ta->effects & CFE_UNDERLINE) ? "u" : "",
\r
1484 (ta->effects & CFE_STRIKEOUT) ? "s" : "",
\r
1485 (ta->effects) ? " " : "",
\r
1486 ta->color&0xff, (ta->color >> 8)&0xff, (ta->color >> 16)&0xff);
\r
1490 SaveColor(FILE *f, ArgDescriptor *ad)
\r
1491 { // in WinBoard the color is an int and has to be converted to text. In X it would be a string already?
\r
1492 COLORREF color = *(COLORREF *)colorVariable[(int)ad->argLoc];
\r
1493 fprintf(f, "/%s=#%02lx%02lx%02lx\n", ad->argName,
\r
1494 color&0xff, (color>>8)&0xff, (color>>16)&0xff);
\r
1498 SaveBoardSize(FILE *f, char *name, void *addr)
\r
1499 { // wrapper to shield back-end from BoardSize & sizeInfo
\r
1500 fprintf(f, "/%s=%s\n", name, sizeInfo[*(BoardSize *)addr].name);
\r
1504 ParseCommPortSettings(char *s)
\r
1505 { // wrapper to keep dcb from back-end
\r
1506 ParseCommSettings(s, &dcb);
\r
1511 { // wrapper to shield use of window handles from back-end (make addressible by number?)
\r
1512 GetActualPlacement(hwndMain, &wpMain);
\r
1513 GetActualPlacement(hwndConsole, &wpConsole);
\r
1514 GetActualPlacement(commentDialog, &wpComment);
\r
1515 GetActualPlacement(editTagsDialog, &wpTags);
\r
1516 GetActualPlacement(gameListDialog, &wpGameList);
\r
1517 GetActualPlacement(moveHistoryDialog, &wpMoveHistory);
\r
1518 GetActualPlacement(evalGraphDialog, &wpEvalGraph);
\r
1519 GetActualPlacement(engineOutputDialog, &wpEngineOutput);
\r
1523 PrintCommPortSettings(FILE *f, char *name)
\r
1524 { // wrapper to shield back-end from DCB
\r
1525 PrintCommSettings(f, name, &dcb);
\r
1529 MySearchPath(char *installDir, char *name, char *fullname)
\r
1531 char *dummy, buf[MSG_SIZ], *p = name, *q;
\r
1532 if(name[0]== '%') {
\r
1533 fullname[0] = 0; // [HGM] first expand any environment variables in the given name
\r
1534 while(*p == '%' && (q = strchr(p+1, '%'))) { // [HGM] recognize %*% as environment variable
\r
1535 safeStrCpy(buf, p+1, sizeof(buf)/sizeof(buf[0]) );
\r
1536 *strchr(buf, '%') = 0;
\r
1537 strcat(fullname, getenv(buf));
\r
1538 p = q+1; while(*p == '\\') { strcat(fullname, "\\"); p++; }
\r
1540 strcat(fullname, p); // after environment variables (if any), take the remainder of the given name
\r
1541 if(appData.debugMode) fprintf(debugFP, "name = '%s', expanded name = '%s'\n", name, fullname);
\r
1542 return (int) strlen(fullname);
\r
1544 return (int) SearchPath(installDir, name, NULL, MSG_SIZ, fullname, &dummy);
\r
1548 MyGetFullPathName(char *name, char *fullname)
\r
1551 return (int) GetFullPathName(name, MSG_SIZ, fullname, &dummy);
\r
1556 { // [HGM] args: allows testing if main window is realized from back-end
\r
1557 return hwndMain != NULL;
\r
1561 PopUpStartupDialog()
\r
1565 LoadLanguageFile(appData.language);
\r
1566 lpProc = MakeProcInstance((FARPROC)StartupDialog, hInst);
\r
1567 DialogBox(hInst, MAKEINTRESOURCE(DLG_Startup), NULL, (DLGPROC)lpProc);
\r
1568 FreeProcInstance(lpProc);
\r
1571 /*---------------------------------------------------------------------------*\
\r
1573 * GDI board drawing routines
\r
1575 \*---------------------------------------------------------------------------*/
\r
1577 /* [AS] Draw square using background texture */
\r
1578 static void DrawTile( int dx, int dy, int dw, int dh, HDC dst, HDC src, int mode, int sx, int sy )
\r
1583 return; /* Should never happen! */
\r
1586 SetGraphicsMode( dst, GM_ADVANCED );
\r
1593 /* X reflection */
\r
1598 x.eDx = (FLOAT) dw + dx - 1;
\r
1601 SetWorldTransform( dst, &x );
\r
1604 /* Y reflection */
\r
1610 x.eDy = (FLOAT) dh + dy - 1;
\r
1612 SetWorldTransform( dst, &x );
\r
1620 x.eDx = (FLOAT) dx;
\r
1621 x.eDy = (FLOAT) dy;
\r
1624 SetWorldTransform( dst, &x );
\r
1628 BitBlt( dst, dx, dy, dw, dh, src, sx, sy, SRCCOPY );
\r
1636 SetWorldTransform( dst, &x );
\r
1638 ModifyWorldTransform( dst, 0, MWT_IDENTITY );
\r
1641 /* [AS] [HGM] Make room for more piece types, so all pieces can be different */
\r
1643 PM_WP = (int) WhitePawn,
\r
1644 PM_WN = (int) WhiteKnight,
\r
1645 PM_WB = (int) WhiteBishop,
\r
1646 PM_WR = (int) WhiteRook,
\r
1647 PM_WQ = (int) WhiteQueen,
\r
1648 PM_WF = (int) WhiteFerz,
\r
1649 PM_WW = (int) WhiteWazir,
\r
1650 PM_WE = (int) WhiteAlfil,
\r
1651 PM_WM = (int) WhiteMan,
\r
1652 PM_WO = (int) WhiteCannon,
\r
1653 PM_WU = (int) WhiteUnicorn,
\r
1654 PM_WH = (int) WhiteNightrider,
\r
1655 PM_WA = (int) WhiteAngel,
\r
1656 PM_WC = (int) WhiteMarshall,
\r
1657 PM_WAB = (int) WhiteCardinal,
\r
1658 PM_WD = (int) WhiteDragon,
\r
1659 PM_WL = (int) WhiteLance,
\r
1660 PM_WS = (int) WhiteCobra,
\r
1661 PM_WV = (int) WhiteFalcon,
\r
1662 PM_WSG = (int) WhiteSilver,
\r
1663 PM_WG = (int) WhiteGrasshopper,
\r
1664 PM_WK = (int) WhiteKing,
\r
1665 PM_BP = (int) BlackPawn,
\r
1666 PM_BN = (int) BlackKnight,
\r
1667 PM_BB = (int) BlackBishop,
\r
1668 PM_BR = (int) BlackRook,
\r
1669 PM_BQ = (int) BlackQueen,
\r
1670 PM_BF = (int) BlackFerz,
\r
1671 PM_BW = (int) BlackWazir,
\r
1672 PM_BE = (int) BlackAlfil,
\r
1673 PM_BM = (int) BlackMan,
\r
1674 PM_BO = (int) BlackCannon,
\r
1675 PM_BU = (int) BlackUnicorn,
\r
1676 PM_BH = (int) BlackNightrider,
\r
1677 PM_BA = (int) BlackAngel,
\r
1678 PM_BC = (int) BlackMarshall,
\r
1679 PM_BG = (int) BlackGrasshopper,
\r
1680 PM_BAB = (int) BlackCardinal,
\r
1681 PM_BD = (int) BlackDragon,
\r
1682 PM_BL = (int) BlackLance,
\r
1683 PM_BS = (int) BlackCobra,
\r
1684 PM_BV = (int) BlackFalcon,
\r
1685 PM_BSG = (int) BlackSilver,
\r
1686 PM_BK = (int) BlackKing
\r
1689 static HFONT hPieceFont = NULL;
\r
1690 static HBITMAP hPieceMask[(int) EmptySquare];
\r
1691 static HBITMAP hPieceFace[(int) EmptySquare];
\r
1692 static int fontBitmapSquareSize = 0;
\r
1693 static char pieceToFontChar[(int) EmptySquare] =
\r
1694 { 'p', 'n', 'b', 'r', 'q',
\r
1695 'n', 'b', 'p', 'n', 'b', 'r', 'b', 'r', 'q', 'k',
\r
1696 'k', 'o', 'm', 'v', 't', 'w',
\r
1697 'v', 't', 'o', 'm', 'v', 't', 'v', 't', 'w', 'l',
\r
1700 extern BOOL SetCharTable( char *table, const char * map );
\r
1701 /* [HGM] moved to backend.c */
\r
1703 static void SetPieceBackground( HDC hdc, COLORREF color, int mode )
\r
1706 BYTE r1 = GetRValue( color );
\r
1707 BYTE g1 = GetGValue( color );
\r
1708 BYTE b1 = GetBValue( color );
\r
1714 /* Create a uniform background first */
\r
1715 hbrush = CreateSolidBrush( color );
\r
1716 SetRect( &rc, 0, 0, squareSize, squareSize );
\r
1717 FillRect( hdc, &rc, hbrush );
\r
1718 DeleteObject( hbrush );
\r
1721 /* Vertical gradient, good for pawn, knight and rook, less for queen and king */
\r
1722 int steps = squareSize / 2;
\r
1725 for( i=0; i<steps; i++ ) {
\r
1726 BYTE r = r1 - (r1-r2) * i / steps;
\r
1727 BYTE g = g1 - (g1-g2) * i / steps;
\r
1728 BYTE b = b1 - (b1-b2) * i / steps;
\r
1730 hbrush = CreateSolidBrush( RGB(r,g,b) );
\r
1731 SetRect( &rc, i + squareSize - steps, 0, i + squareSize - steps + 1, squareSize );
\r
1732 FillRect( hdc, &rc, hbrush );
\r
1733 DeleteObject(hbrush);
\r
1736 else if( mode == 2 ) {
\r
1737 /* Diagonal gradient, good more or less for every piece */
\r
1738 POINT triangle[3];
\r
1739 HPEN hpen = SelectObject( hdc, GetStockObject(NULL_PEN) );
\r
1740 HBRUSH hbrush_old;
\r
1741 int steps = squareSize;
\r
1744 triangle[0].x = squareSize - steps;
\r
1745 triangle[0].y = squareSize;
\r
1746 triangle[1].x = squareSize;
\r
1747 triangle[1].y = squareSize;
\r
1748 triangle[2].x = squareSize;
\r
1749 triangle[2].y = squareSize - steps;
\r
1751 for( i=0; i<steps; i++ ) {
\r
1752 BYTE r = r1 - (r1-r2) * i / steps;
\r
1753 BYTE g = g1 - (g1-g2) * i / steps;
\r
1754 BYTE b = b1 - (b1-b2) * i / steps;
\r
1756 hbrush = CreateSolidBrush( RGB(r,g,b) );
\r
1757 hbrush_old = SelectObject( hdc, hbrush );
\r
1758 Polygon( hdc, triangle, 3 );
\r
1759 SelectObject( hdc, hbrush_old );
\r
1760 DeleteObject(hbrush);
\r
1765 SelectObject( hdc, hpen );
\r
1770 [AS] The method I use to create the bitmaps it a bit tricky, but it
\r
1771 seems to work ok. The main problem here is to find the "inside" of a chess
\r
1772 piece: follow the steps as explained below.
\r
1774 static void CreatePieceMaskFromFont( HDC hdc_window, HDC hdc, int index )
\r
1778 COLORREF chroma = RGB(0xFF,0x00,0xFF);
\r
1782 int backColor = whitePieceColor;
\r
1783 int foreColor = blackPieceColor;
\r
1785 if( index < (int)BlackPawn && appData.fontBackColorWhite != appData.fontForeColorWhite ) {
\r
1786 backColor = appData.fontBackColorWhite;
\r
1787 foreColor = appData.fontForeColorWhite;
\r
1789 else if( index >= (int)BlackPawn && appData.fontBackColorBlack != appData.fontForeColorBlack ) {
\r
1790 backColor = appData.fontBackColorBlack;
\r
1791 foreColor = appData.fontForeColorBlack;
\r
1795 hbm = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );
\r
1797 hbm_old = SelectObject( hdc, hbm );
\r
1801 rc.right = squareSize;
\r
1802 rc.bottom = squareSize;
\r
1804 /* Step 1: background is now black */
\r
1805 FillRect( hdc, &rc, GetStockObject(BLACK_BRUSH) );
\r
1807 GetTextExtentPoint32( hdc, &pieceToFontChar[index], 1, &sz );
\r
1809 pt.x = (squareSize - sz.cx) / 2;
\r
1810 pt.y = (squareSize - sz.cy) / 2;
\r
1812 SetBkMode( hdc, TRANSPARENT );
\r
1813 SetTextColor( hdc, chroma );
\r
1814 /* Step 2: the piece has been drawn in purple, there are now black and purple in this bitmap */
\r
1815 TextOut( hdc, pt.x, pt.y, &pieceToFontChar[appData.allWhite && index >= (int)BlackPawn ? index - (int)BlackPawn : index], 1 );
\r
1817 SelectObject( hdc, GetStockObject(WHITE_BRUSH) );
\r
1818 /* Step 3: the area outside the piece is filled with white */
\r
1819 // FloodFill( hdc, 0, 0, chroma );
\r
1820 ExtFloodFill( hdc, 0, 0, 0, FLOODFILLSURFACE );
\r
1821 ExtFloodFill( hdc, 0, squareSize-1, 0, FLOODFILLSURFACE ); // [HGM] fill from all 4 corners, for if piece too big
\r
1822 ExtFloodFill( hdc, squareSize-1, 0, 0, FLOODFILLSURFACE );
\r
1823 ExtFloodFill( hdc, squareSize-1, squareSize-1, 0, FLOODFILLSURFACE );
\r
1824 SelectObject( hdc, GetStockObject(BLACK_BRUSH) );
\r
1826 Step 4: this is the tricky part, the area inside the piece is filled with black,
\r
1827 but if the start point is not inside the piece we're lost!
\r
1828 There should be a better way to do this... if we could create a region or path
\r
1829 from the fill operation we would be fine for example.
\r
1831 // FloodFill( hdc, squareSize / 2, squareSize / 2, RGB(0xFF,0xFF,0xFF) );
\r
1832 ExtFloodFill( hdc, squareSize / 2, squareSize / 2, RGB(0xFF,0xFF,0xFF), FLOODFILLBORDER );
\r
1834 { /* [HGM] shave off edges of mask, in an attempt to correct for the fact that FloodFill does not work correctly under Win XP */
\r
1835 HDC dc2 = CreateCompatibleDC( hdc_window );
\r
1836 HBITMAP bm2 = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );
\r
1838 SelectObject( dc2, bm2 );
\r
1839 BitBlt( dc2, 0, 0, squareSize, squareSize, hdc, 0, 0, SRCCOPY ); // make copy
\r
1840 BitBlt( hdc, 0, 1, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );
\r
1841 BitBlt( hdc, 2, 1, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );
\r
1842 BitBlt( hdc, 1, 0, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );
\r
1843 BitBlt( hdc, 1, 2, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );
\r
1846 DeleteObject( bm2 );
\r
1849 SetTextColor( hdc, 0 );
\r
1851 Step 5: some fonts have "disconnected" areas that are skipped by the fill:
\r
1852 draw the piece again in black for safety.
\r
1854 TextOut( hdc, pt.x, pt.y, &pieceToFontChar[appData.allWhite && index >= (int)BlackPawn ? index - (int)BlackPawn : index], 1 );
\r
1856 SelectObject( hdc, hbm_old );
\r
1858 if( hPieceMask[index] != NULL ) {
\r
1859 DeleteObject( hPieceMask[index] );
\r
1862 hPieceMask[index] = hbm;
\r
1865 hbm = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );
\r
1867 SelectObject( hdc, hbm );
\r
1870 HDC dc1 = CreateCompatibleDC( hdc_window );
\r
1871 HDC dc2 = CreateCompatibleDC( hdc_window );
\r
1872 HBITMAP bm2 = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );
\r
1874 SelectObject( dc1, hPieceMask[index] );
\r
1875 SelectObject( dc2, bm2 );
\r
1876 FillRect( dc2, &rc, GetStockObject(WHITE_BRUSH) );
\r
1877 BitBlt( dc2, 0, 0, squareSize, squareSize, dc1, 0, 0, SRCINVERT );
\r
1880 Now dc2 contains the inverse of the piece mask, i.e. a mask that preserves
\r
1881 the piece background and deletes (makes transparent) the rest.
\r
1882 Thanks to that mask, we are free to paint the background with the greates
\r
1883 freedom, as we'll be able to mask off the unwanted parts when finished.
\r
1884 We use this, to make gradients and give the pieces a "roundish" look.
\r
1886 SetPieceBackground( hdc, backColor, 2 );
\r
1887 BitBlt( hdc, 0, 0, squareSize, squareSize, dc2, 0, 0, SRCAND );
\r
1891 DeleteObject( bm2 );
\r
1894 SetTextColor( hdc, foreColor );
\r
1895 TextOut( hdc, pt.x, pt.y, &pieceToFontChar[appData.allWhite && index >= (int)BlackPawn ? index - (int)BlackPawn : index], 1 );
\r
1897 SelectObject( hdc, hbm_old );
\r
1899 if( hPieceFace[index] != NULL ) {
\r
1900 DeleteObject( hPieceFace[index] );
\r
1903 hPieceFace[index] = hbm;
\r
1906 static int TranslatePieceToFontPiece( int piece )
\r
1936 case BlackMarshall:
\r
1940 case BlackNightrider:
\r
1946 case BlackUnicorn:
\r
1950 case BlackGrasshopper:
\r
1962 case BlackCardinal:
\r
1969 case WhiteMarshall:
\r
1973 case WhiteNightrider:
\r
1979 case WhiteUnicorn:
\r
1983 case WhiteGrasshopper:
\r
1995 case WhiteCardinal:
\r
2004 void CreatePiecesFromFont()
\r
2007 HDC hdc_window = NULL;
\r
2013 if( fontBitmapSquareSize < 0 ) {
\r
2014 /* Something went seriously wrong in the past: do not try to recreate fonts! */
\r
2018 if( !appData.useFont || appData.renderPiecesWithFont == NULL ||
\r
2019 appData.renderPiecesWithFont[0] == NULLCHAR || appData.renderPiecesWithFont[0] == '*' ) {
\r
2020 fontBitmapSquareSize = -1;
\r
2024 if( fontBitmapSquareSize != squareSize ) {
\r
2025 hdc_window = GetDC( hwndMain );
\r
2026 hdc = CreateCompatibleDC( hdc_window );
\r
2028 if( hPieceFont != NULL ) {
\r
2029 DeleteObject( hPieceFont );
\r
2032 for( i=0; i<=(int)BlackKing; i++ ) {
\r
2033 hPieceMask[i] = NULL;
\r
2034 hPieceFace[i] = NULL;
\r
2040 if( appData.fontPieceSize >= 50 && appData.fontPieceSize <= 150 ) {
\r
2041 fontHeight = appData.fontPieceSize;
\r
2044 fontHeight = (fontHeight * squareSize) / 100;
\r
2046 lf.lfHeight = -MulDiv( fontHeight, GetDeviceCaps(hdc, LOGPIXELSY), 72 );
\r
2048 lf.lfEscapement = 0;
\r
2049 lf.lfOrientation = 0;
\r
2050 lf.lfWeight = FW_NORMAL;
\r
2052 lf.lfUnderline = 0;
\r
2053 lf.lfStrikeOut = 0;
\r
2054 lf.lfCharSet = DEFAULT_CHARSET;
\r
2055 lf.lfOutPrecision = OUT_DEFAULT_PRECIS;
\r
2056 lf.lfClipPrecision = CLIP_DEFAULT_PRECIS;
\r
2057 lf.lfQuality = PROOF_QUALITY;
\r
2058 lf.lfPitchAndFamily = DEFAULT_PITCH | FF_DONTCARE;
\r
2059 strncpy( lf.lfFaceName, appData.renderPiecesWithFont, sizeof(lf.lfFaceName) );
\r
2060 lf.lfFaceName[ sizeof(lf.lfFaceName) - 1 ] = '\0';
\r
2062 hPieceFont = CreateFontIndirect( &lf );
\r
2064 if( hPieceFont == NULL ) {
\r
2065 fontBitmapSquareSize = -2;
\r
2068 /* Setup font-to-piece character table */
\r
2069 if( ! SetCharTable(pieceToFontChar, appData.fontToPieceTable) ) {
\r
2070 /* No (or wrong) global settings, try to detect the font */
\r
2071 if( strstr(lf.lfFaceName,"Alpha") != NULL ) {
\r
2073 SetCharTable(pieceToFontChar, "phbrqkojntwl");
\r
2075 else if( strstr(lf.lfFaceName,"DiagramTT") != NULL ) {
\r
2076 /* DiagramTT* family */
\r
2077 SetCharTable(pieceToFontChar, "PNLRQKpnlrqk");
\r
2079 else if( strstr(lf.lfFaceName,"WinboardF") != NULL ) {
\r
2080 /* Fairy symbols */
\r
2081 SetCharTable(pieceToFontChar, "PNBRQFEACWMOHIJGDVSLUKpnbrqfeacwmohijgdvsluk");
\r
2083 else if( strstr(lf.lfFaceName,"GC2004D") != NULL ) {
\r
2084 /* Good Companion (Some characters get warped as literal :-( */
\r
2085 char s[] = "1cmWG0??S??oYI23wgQU";
\r
2086 s[0]=0xB9; s[1]=0xA9; s[6]=0xB1; s[11]=0xBB; s[12]=0xAB; s[17]=0xB3;
\r
2087 SetCharTable(pieceToFontChar, s);
\r
2090 /* Cases, Condal, Leipzig, Lucena, Marroquin, Merida, Usual */
\r
2091 SetCharTable(pieceToFontChar, "pnbrqkomvtwl");
\r
2095 /* Create bitmaps */
\r
2096 hfont_old = SelectObject( hdc, hPieceFont );
\r
2097 for(i=(int)WhitePawn; i<(int)EmptySquare; i++) /* [HGM] made a loop for this */
\r
2098 if(PieceToChar((ChessSquare)i) != '.') /* skip unused pieces */
\r
2099 CreatePieceMaskFromFont( hdc_window, hdc, i );
\r
2101 SelectObject( hdc, hfont_old );
\r
2103 fontBitmapSquareSize = squareSize;
\r
2107 if( hdc != NULL ) {
\r
2111 if( hdc_window != NULL ) {
\r
2112 ReleaseDC( hwndMain, hdc_window );
\r
2117 DoLoadBitmap(HINSTANCE hinst, char *piece, int squareSize, char *suffix)
\r
2119 char name[128], buf[MSG_SIZ];
\r
2121 snprintf(name, sizeof(name)/sizeof(name[0]), "%s%d%s", piece, squareSize, suffix);
\r
2122 if(appData.pieceDirectory[0]) {
\r
2124 snprintf(buf, MSG_SIZ, "%s\\%s.bmp", appData.pieceDirectory, name);
\r
2125 res = LoadImage( 0, buf, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );
\r
2126 if(res) return res;
\r
2128 if (gameInfo.event &&
\r
2129 strcmp(gameInfo.event, "Easter Egg Hunt") == 0 &&
\r
2130 strcmp(name, "k80s") == 0) {
\r
2131 safeStrCpy(name, "tim", sizeof(name)/sizeof(name[0]) );
\r
2133 return LoadBitmap(hinst, name);
\r
2137 /* Insert a color into the program's logical palette
\r
2138 structure. This code assumes the given color is
\r
2139 the result of the RGB or PALETTERGB macro, and it
\r
2140 knows how those macros work (which is documented).
\r
2143 InsertInPalette(COLORREF color)
\r
2145 LPPALETTEENTRY pe = &(pLogPal->palPalEntry[pLogPal->palNumEntries]);
\r
2147 if (pLogPal->palNumEntries++ >= PALETTESIZE) {
\r
2148 DisplayFatalError(_("Too many colors"), 0, 1);
\r
2149 pLogPal->palNumEntries--;
\r
2153 pe->peFlags = (char) 0;
\r
2154 pe->peRed = (char) (0xFF & color);
\r
2155 pe->peGreen = (char) (0xFF & (color >> 8));
\r
2156 pe->peBlue = (char) (0xFF & (color >> 16));
\r
2162 InitDrawingColors()
\r
2164 if (pLogPal == NULL) {
\r
2165 /* Allocate enough memory for a logical palette with
\r
2166 * PALETTESIZE entries and set the size and version fields
\r
2167 * of the logical palette structure.
\r
2169 pLogPal = (NPLOGPALETTE)
\r
2170 LocalAlloc(LMEM_FIXED, (sizeof(LOGPALETTE) +
\r
2171 (sizeof(PALETTEENTRY) * (PALETTESIZE))));
\r
2172 pLogPal->palVersion = 0x300;
\r
2174 pLogPal->palNumEntries = 0;
\r
2176 InsertInPalette(lightSquareColor);
\r
2177 InsertInPalette(darkSquareColor);
\r
2178 InsertInPalette(whitePieceColor);
\r
2179 InsertInPalette(blackPieceColor);
\r
2180 InsertInPalette(highlightSquareColor);
\r
2181 InsertInPalette(premoveHighlightColor);
\r
2183 /* create a logical color palette according the information
\r
2184 * in the LOGPALETTE structure.
\r
2186 hPal = CreatePalette((LPLOGPALETTE) pLogPal);
\r
2188 lightSquareBrush = CreateSolidBrush(lightSquareColor);
\r
2189 blackSquareBrush = CreateSolidBrush(blackPieceColor);
\r
2190 darkSquareBrush = CreateSolidBrush(darkSquareColor);
\r
2191 whitePieceBrush = CreateSolidBrush(whitePieceColor);
\r
2192 blackPieceBrush = CreateSolidBrush(blackPieceColor);
\r
2193 iconBkgndBrush = CreateSolidBrush(GetSysColor(COLOR_BACKGROUND));
\r
2194 explodeBrush = CreateSolidBrush(highlightSquareColor); // [HGM] atomic
\r
2195 markerBrush = CreateSolidBrush(premoveHighlightColor); // [HGM] markers
\r
2196 /* [AS] Force rendering of the font-based pieces */
\r
2197 if( fontBitmapSquareSize > 0 ) {
\r
2198 fontBitmapSquareSize = 0;
\r
2204 BoardWidth(int boardSize, int n)
\r
2205 { /* [HGM] argument n added to allow different width and height */
\r
2206 int lineGap = sizeInfo[boardSize].lineGap;
\r
2208 if( appData.overrideLineGap >= 0 && appData.overrideLineGap <= 5 ) {
\r
2209 lineGap = appData.overrideLineGap;
\r
2212 return (n + 1) * lineGap +
\r
2213 n * sizeInfo[boardSize].squareSize;
\r
2216 /* Respond to board resize by dragging edge */
\r
2218 ResizeBoard(int newSizeX, int newSizeY, int flags)
\r
2220 BoardSize newSize = NUM_SIZES - 1;
\r
2221 static int recurse = 0;
\r
2222 if (IsIconic(hwndMain)) return;
\r
2223 if (recurse > 0) return;
\r
2225 while (newSize > 0) {
\r
2226 InitDrawingSizes(newSize+1000, 0); // [HGM] kludge to update sizeInfo without visible effects
\r
2227 if(newSizeX >= sizeInfo[newSize].cliWidth &&
\r
2228 newSizeY >= sizeInfo[newSize].cliHeight) break;
\r
2231 boardSize = newSize;
\r
2232 InitDrawingSizes(boardSize, flags);
\r
2237 extern Boolean twoBoards, partnerUp; // [HGM] dual
\r
2240 InitDrawingSizes(BoardSize boardSize, int flags)
\r
2242 int i, boardWidth, boardHeight; /* [HGM] height treated separately */
\r
2243 ChessSquare piece;
\r
2244 static int oldBoardSize = -1, oldTinyLayout = 0;
\r
2246 SIZE clockSize, messageSize;
\r
2248 char buf[MSG_SIZ];
\r
2250 HMENU hmenu = GetMenu(hwndMain);
\r
2251 RECT crect, wrect, oldRect;
\r
2253 LOGBRUSH logbrush;
\r
2254 VariantClass v = gameInfo.variant;
\r
2256 int suppressVisibleEffects = 0; // [HGM] kludge to request updating sizeInfo only
\r
2257 if((int)boardSize >= 1000 ) { boardSize -= 1000; suppressVisibleEffects = 1; }
\r
2259 /* [HGM] call with -2 uses old size (for if nr of files, ranks changes) */
\r
2260 if(boardSize == (BoardSize)(-2) ) boardSize = oldBoardSize;
\r
2261 oldBoardSize = boardSize;
\r
2263 if(boardSize != SizeMiddling && boardSize != SizePetite && boardSize != SizeBulky && !appData.useFont)
\r
2264 { // correct board size to one where built-in pieces exist
\r
2265 if((v == VariantCapablanca || v == VariantGothic || v == VariantGrand || v == VariantCapaRandom || v == VariantJanus || v == VariantSuper)
\r
2266 && (boardSize < SizePetite || boardSize > SizeBulky) // Archbishop and Chancellor available in entire middle range
\r
2267 || (v == VariantShogi && boardSize != SizeModerate) // Japanese-style Shogi
\r
2268 || v == VariantKnightmate || v == VariantSChess || v == VariantXiangqi || v == VariantSpartan
\r
2269 || v == VariantShatranj || v == VariantMakruk || v == VariantGreat || v == VariantFairy ) {
\r
2270 if(boardSize < SizeMediocre) boardSize = SizePetite; else
\r
2271 if(boardSize > SizeModerate) boardSize = SizeBulky; else
\r
2272 boardSize = SizeMiddling;
\r
2275 if(!appData.useFont && boardSize == SizePetite && (v == VariantShogi || v == VariantKnightmate)) boardSize = SizeMiddling; // no Unicorn in Petite
\r
2277 oldRect.left = wpMain.x; //[HGM] placement: remember previous window params
\r
2278 oldRect.top = wpMain.y;
\r
2279 oldRect.right = wpMain.x + wpMain.width;
\r
2280 oldRect.bottom = wpMain.y + wpMain.height;
\r
2282 tinyLayout = sizeInfo[boardSize].tinyLayout;
\r
2283 smallLayout = sizeInfo[boardSize].smallLayout;
\r
2284 squareSize = sizeInfo[boardSize].squareSize;
\r
2285 lineGap = sizeInfo[boardSize].lineGap;
\r
2286 minorSize = 0; /* [HGM] Kludge to see if demagnified pieces need to be shifted */
\r
2287 border = appData.useBorder && appData.border[0] ? squareSize/2 : 0;
\r
2289 if( appData.overrideLineGap >= 0 && appData.overrideLineGap <= 5 ) {
\r
2290 lineGap = appData.overrideLineGap;
\r
2293 if (tinyLayout != oldTinyLayout) {
\r
2294 long style = GetWindowLongPtr(hwndMain, GWL_STYLE);
\r
2296 style &= ~WS_SYSMENU;
\r
2297 InsertMenu(hmenu, IDM_Exit, MF_BYCOMMAND, IDM_Minimize,
\r
2298 "&Minimize\tCtrl+F4");
\r
2300 style |= WS_SYSMENU;
\r
2301 RemoveMenu(hmenu, IDM_Minimize, MF_BYCOMMAND);
\r
2303 SetWindowLongPtr(hwndMain, GWL_STYLE, style);
\r
2305 for (i=0; menuBarText[tinyLayout][i]; i++) {
\r
2306 ModifyMenu(hmenu, i, MF_STRING|MF_BYPOSITION|MF_POPUP,
\r
2307 (UINT)GetSubMenu(hmenu, i), T_(menuBarText[tinyLayout][i]));
\r
2309 DrawMenuBar(hwndMain);
\r
2312 boardWidth = BoardWidth(boardSize, BOARD_WIDTH) + 2*border;
\r
2313 boardHeight = BoardWidth(boardSize, BOARD_HEIGHT) + 2*border;
\r
2315 /* Get text area sizes */
\r
2316 hdc = GetDC(hwndMain);
\r
2317 if (appData.clockMode) {
\r
2318 snprintf(buf, MSG_SIZ, _("White: %s"), TimeString(23*60*60*1000L));
\r
2320 snprintf(buf, MSG_SIZ, _("White"));
\r
2322 oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);
\r
2323 GetTextExtentPoint(hdc, buf, strlen(buf), &clockSize);
\r
2324 SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);
\r
2325 str = _("We only care about the height here");
\r
2326 GetTextExtentPoint(hdc, str, strlen(str), &messageSize);
\r
2327 SelectObject(hdc, oldFont);
\r
2328 ReleaseDC(hwndMain, hdc);
\r
2330 /* Compute where everything goes */
\r
2331 if((first.programLogo || second.programLogo) && !tinyLayout) {
\r
2332 /* [HGM] logo: if either logo is on, reserve space for it */
\r
2333 logoHeight = 2*clockSize.cy;
\r
2334 leftLogoRect.left = OUTER_MARGIN;
\r
2335 leftLogoRect.right = leftLogoRect.left + 4*clockSize.cy;
\r
2336 leftLogoRect.top = OUTER_MARGIN;
\r
2337 leftLogoRect.bottom = OUTER_MARGIN + logoHeight;
\r
2339 rightLogoRect.right = OUTER_MARGIN + boardWidth;
\r
2340 rightLogoRect.left = rightLogoRect.right - 4*clockSize.cy;
\r
2341 rightLogoRect.top = OUTER_MARGIN;
\r
2342 rightLogoRect.bottom = OUTER_MARGIN + logoHeight;
\r
2345 whiteRect.left = leftLogoRect.right;
\r
2346 whiteRect.right = OUTER_MARGIN + boardWidth/2 - INNER_MARGIN/2;
\r
2347 whiteRect.top = OUTER_MARGIN;
\r
2348 whiteRect.bottom = whiteRect.top + logoHeight;
\r
2350 blackRect.right = rightLogoRect.left;
\r
2351 blackRect.left = whiteRect.right + INNER_MARGIN;
\r
2352 blackRect.top = whiteRect.top;
\r
2353 blackRect.bottom = whiteRect.bottom;
\r
2355 whiteRect.left = OUTER_MARGIN;
\r
2356 whiteRect.right = whiteRect.left + boardWidth/2 - INNER_MARGIN/2;
\r
2357 whiteRect.top = OUTER_MARGIN;
\r
2358 whiteRect.bottom = whiteRect.top + clockSize.cy;
\r
2360 blackRect.left = whiteRect.right + INNER_MARGIN;
\r
2361 blackRect.right = blackRect.left + boardWidth/2 - 1;
\r
2362 blackRect.top = whiteRect.top;
\r
2363 blackRect.bottom = whiteRect.bottom;
\r
2365 logoHeight = 0; // [HGM] logo: suppress logo after change to tiny layout!
\r
2368 messageRect.left = OUTER_MARGIN + MESSAGE_LINE_LEFTMARGIN;
\r
2369 if (appData.showButtonBar) {
\r
2370 messageRect.right = OUTER_MARGIN + boardWidth // [HGM] logo: expressed independent of clock placement
\r
2371 - N_BUTTONS*BUTTON_WIDTH - MESSAGE_LINE_LEFTMARGIN;
\r
2373 messageRect.right = OUTER_MARGIN + boardWidth;
\r
2375 messageRect.top = whiteRect.bottom + INNER_MARGIN;
\r
2376 messageRect.bottom = messageRect.top + messageSize.cy;
\r
2378 boardRect.left = OUTER_MARGIN;
\r
2379 boardRect.right = boardRect.left + boardWidth;
\r
2380 boardRect.top = messageRect.bottom + INNER_MARGIN;
\r
2381 boardRect.bottom = boardRect.top + boardHeight;
\r
2383 sizeInfo[boardSize].cliWidth = boardRect.right + OUTER_MARGIN;
\r
2384 sizeInfo[boardSize].cliHeight = boardRect.bottom + OUTER_MARGIN;
\r
2385 oldTinyLayout = tinyLayout;
\r
2386 winW = 2 * GetSystemMetrics(SM_CXFRAME) + boardRect.right + OUTER_MARGIN;
\r
2387 winH = 2 * GetSystemMetrics(SM_CYFRAME) + GetSystemMetrics(SM_CYMENU) +
\r
2388 GetSystemMetrics(SM_CYCAPTION) + boardRect.bottom + OUTER_MARGIN;
\r
2389 winW *= 1 + twoBoards;
\r
2390 if(suppressVisibleEffects) return; // [HGM] when called for filling sizeInfo only
\r
2391 wpMain.width = winW; // [HGM] placement: set through temporary which can used by initial sizing choice
\r
2392 wpMain.height = winH; // without disturbing window attachments
\r
2393 GetWindowRect(hwndMain, &wrect);
\r
2394 SetWindowPos(hwndMain, NULL, 0, 0, wpMain.width, wpMain.height,
\r
2395 SWP_NOCOPYBITS|SWP_NOZORDER|SWP_NOMOVE);
\r
2397 // [HGM] placement: let attached windows follow size change.
\r
2398 ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, moveHistoryDialog, &wpMoveHistory );
\r
2399 ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, evalGraphDialog, &wpEvalGraph );
\r
2400 ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, engineOutputDialog, &wpEngineOutput );
\r
2401 ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, gameListDialog, &wpGameList );
\r
2402 ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, hwndConsole, &wpConsole );
\r
2404 /* compensate if menu bar wrapped */
\r
2405 GetClientRect(hwndMain, &crect);
\r
2406 offby = boardRect.bottom + OUTER_MARGIN - crect.bottom;
\r
2407 wpMain.height += offby;
\r
2409 case WMSZ_TOPLEFT:
\r
2410 SetWindowPos(hwndMain, NULL,
\r
2411 wrect.right - wpMain.width, wrect.bottom - wpMain.height,
\r
2412 wpMain.width, wpMain.height, SWP_NOCOPYBITS|SWP_NOZORDER);
\r
2415 case WMSZ_TOPRIGHT:
\r
2417 SetWindowPos(hwndMain, NULL,
\r
2418 wrect.left, wrect.bottom - wpMain.height,
\r
2419 wpMain.width, wpMain.height, SWP_NOCOPYBITS|SWP_NOZORDER);
\r
2422 case WMSZ_BOTTOMLEFT:
\r
2424 SetWindowPos(hwndMain, NULL,
\r
2425 wrect.right - wpMain.width, wrect.top,
\r
2426 wpMain.width, wpMain.height, SWP_NOCOPYBITS|SWP_NOZORDER);
\r
2429 case WMSZ_BOTTOMRIGHT:
\r
2433 SetWindowPos(hwndMain, NULL, 0, 0, wpMain.width, wpMain.height,
\r
2434 SWP_NOCOPYBITS|SWP_NOZORDER|SWP_NOMOVE);
\r
2439 for (i = 0; i < N_BUTTONS; i++) {
\r
2440 if (buttonDesc[i].hwnd != NULL) {
\r
2441 DestroyWindow(buttonDesc[i].hwnd);
\r
2442 buttonDesc[i].hwnd = NULL;
\r
2444 if (appData.showButtonBar) {
\r
2445 buttonDesc[i].hwnd =
\r
2446 CreateWindow("BUTTON", buttonDesc[i].label,
\r
2447 WS_VISIBLE | WS_CHILD | BS_PUSHBUTTON,
\r
2448 boardRect.right - BUTTON_WIDTH*(N_BUTTONS-i),
\r
2449 messageRect.top, BUTTON_WIDTH, messageSize.cy, hwndMain,
\r
2450 (HMENU) buttonDesc[i].id,
\r
2451 (HINSTANCE) GetWindowLongPtr(hwndMain, GWLP_HINSTANCE), NULL);
\r
2453 SendMessage(buttonDesc[i].hwnd, WM_SETFONT,
\r
2454 (WPARAM)font[boardSize][MESSAGE_FONT]->hf,
\r
2455 MAKELPARAM(FALSE, 0));
\r
2457 if (buttonDesc[i].id == IDM_Pause)
\r
2458 hwndPause = buttonDesc[i].hwnd;
\r
2459 buttonDesc[i].wndproc = (WNDPROC)
\r
2460 SetWindowLongPtr(buttonDesc[i].hwnd, GWLP_WNDPROC, (LONG_PTR) ButtonProc);
\r
2463 if (gridPen != NULL) DeleteObject(gridPen);
\r
2464 if (highlightPen != NULL) DeleteObject(highlightPen);
\r
2465 if (premovePen != NULL) DeleteObject(premovePen);
\r
2466 if (lineGap != 0) {
\r
2467 logbrush.lbStyle = BS_SOLID;
\r
2468 logbrush.lbColor = RGB(0, 0, 0); /* grid pen color = black */
\r
2470 ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,
\r
2471 lineGap, &logbrush, 0, NULL);
\r
2472 logbrush.lbColor = highlightSquareColor;
\r
2474 ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,
\r
2475 lineGap, &logbrush, 0, NULL);
\r
2477 logbrush.lbColor = premoveHighlightColor;
\r
2479 ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,
\r
2480 lineGap, &logbrush, 0, NULL);
\r
2482 /* [HGM] Loop had to be split in part for vert. and hor. lines */
\r
2483 for (i = 0; i < BOARD_HEIGHT + 1; i++) {
\r
2484 gridEndpoints[i*2].x = boardRect.left + lineGap / 2 + border;
\r
2485 gridEndpoints[i*2].y = gridEndpoints[i*2 + 1].y =
\r
2486 boardRect.top + lineGap / 2 + (i * (squareSize + lineGap)) + border;
\r
2487 gridEndpoints[i*2 + 1].x = boardRect.left + lineGap / 2 +
\r
2488 BOARD_WIDTH * (squareSize + lineGap) + border;
\r
2489 gridVertexCounts[i*2] = gridVertexCounts[i*2 + 1] = 2;
\r
2491 for (i = 0; i < BOARD_WIDTH + 1; i++) {
\r
2492 gridEndpoints[i*2 + BOARD_HEIGHT*2 + 2].y = boardRect.top + lineGap / 2 + border;
\r
2493 gridEndpoints[i*2 + BOARD_HEIGHT*2 + 2].x =
\r
2494 gridEndpoints[i*2 + 1 + BOARD_HEIGHT*2 + 2].x = boardRect.left +
\r
2495 lineGap / 2 + (i * (squareSize + lineGap)) + border;
\r
2496 gridEndpoints[i*2 + 1 + BOARD_HEIGHT*2 + 2].y =
\r
2497 boardRect.top + BOARD_HEIGHT * (squareSize + lineGap) + border;
\r
2498 gridVertexCounts[i*2] = gridVertexCounts[i*2 + 1] = 2;
\r
2502 /* [HGM] Licensing requirement */
\r
2504 if(gameInfo.variant == VariantGothic) GothicPopUp( GOTHIC, VariantGothic); else
\r
2507 if(gameInfo.variant == VariantFalcon) GothicPopUp( FALCON, VariantFalcon); else
\r
2509 GothicPopUp( "", VariantNormal);
\r
2512 /* if (boardSize == oldBoardSize) return; [HGM] variant might have changed */
\r
2514 /* Load piece bitmaps for this board size */
\r
2515 for (i=0; i<=2; i++) {
\r
2516 for (piece = WhitePawn;
\r
2517 (int) piece < (int) BlackPawn;
\r
2518 piece = (ChessSquare) ((int) piece + 1)) {
\r
2519 if (pieceBitmap[i][piece] != NULL)
\r
2520 DeleteObject(pieceBitmap[i][piece]);
\r
2524 fontBitmapSquareSize = 0; /* [HGM] render: make sure pieces will be recreated, as we might need others now */
\r
2525 // Orthodox Chess pieces
\r
2526 pieceBitmap[0][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "s");
\r
2527 pieceBitmap[0][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "s");
\r
2528 pieceBitmap[0][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "s");
\r
2529 pieceBitmap[0][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "s");
\r
2530 pieceBitmap[0][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "s");
\r
2531 pieceBitmap[1][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "o");
\r
2532 pieceBitmap[1][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "o");
\r
2533 pieceBitmap[1][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "o");
\r
2534 pieceBitmap[1][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "o");
\r
2535 pieceBitmap[1][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "o");
\r
2536 pieceBitmap[2][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "w");
\r
2537 pieceBitmap[2][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "w");
\r
2538 pieceBitmap[2][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "w");
\r
2539 pieceBitmap[2][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "w");
\r
2540 pieceBitmap[2][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "w");
\r
2541 if( gameInfo.variant == VariantShogi && squareSize <= 72 && squareSize >= 33) {
\r
2542 // in Shogi, Hijack the unused Queen for Lance
\r
2543 pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "s");
\r
2544 pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "o");
\r
2545 pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "w");
\r
2547 pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "s");
\r
2548 pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "o");
\r
2549 pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "w");
\r
2552 if(squareSize <= 72 && squareSize >= 33) {
\r
2553 /* A & C are available in most sizes now */
\r
2554 if(squareSize != 49 && squareSize != 72 && squareSize != 33) { // Vortex-like
\r
2555 pieceBitmap[0][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "s");
\r
2556 pieceBitmap[1][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "o");
\r
2557 pieceBitmap[2][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "w");
\r
2558 pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "s");
\r
2559 pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "o");
\r
2560 pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "w");
\r
2561 pieceBitmap[0][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "s");
\r
2562 pieceBitmap[1][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "o");
\r
2563 pieceBitmap[2][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "w");
\r
2564 pieceBitmap[0][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "s");
\r
2565 pieceBitmap[1][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "o");
\r
2566 pieceBitmap[2][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "w");
\r
2567 } else { // Smirf-like
\r
2568 if(gameInfo.variant == VariantSChess) {
\r
2569 pieceBitmap[0][WhiteAngel] = DoLoadBitmap(hInst, "v", squareSize, "s");
\r
2570 pieceBitmap[1][WhiteAngel] = DoLoadBitmap(hInst, "v", squareSize, "o");
\r
2571 pieceBitmap[2][WhiteAngel] = DoLoadBitmap(hInst, "v", squareSize, "w");
\r
2573 pieceBitmap[0][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "s");
\r
2574 pieceBitmap[1][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "o");
\r
2575 pieceBitmap[2][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "w");
\r
2578 if(gameInfo.variant == VariantGothic) { // Vortex-like
\r
2579 pieceBitmap[0][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "s");
\r
2580 pieceBitmap[1][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "o");
\r
2581 pieceBitmap[2][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "w");
\r
2582 } else if(gameInfo.variant == VariantSChess && (squareSize == 49 || squareSize == 72)) {
\r
2583 pieceBitmap[0][WhiteMarshall] = DoLoadBitmap(hInst, "e", squareSize, "s");
\r
2584 pieceBitmap[1][WhiteMarshall] = DoLoadBitmap(hInst, "e", squareSize, "o");
\r
2585 pieceBitmap[2][WhiteMarshall] = DoLoadBitmap(hInst, "e", squareSize, "w");
\r
2586 } else { // WinBoard standard
\r
2587 pieceBitmap[0][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "s");
\r
2588 pieceBitmap[1][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "o");
\r
2589 pieceBitmap[2][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "w");
\r
2594 if(squareSize==72 || squareSize==49 || squareSize==33) { /* experiment with some home-made bitmaps */
\r
2595 pieceBitmap[0][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "s");
\r
2596 pieceBitmap[1][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "o");
\r
2597 pieceBitmap[2][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "w");
\r
2598 pieceBitmap[0][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "s");
\r
2599 pieceBitmap[1][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "o");
\r
2600 pieceBitmap[2][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "w");
\r
2601 pieceBitmap[0][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "s");
\r
2602 pieceBitmap[1][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "o");
\r
2603 pieceBitmap[2][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "w");
\r
2604 pieceBitmap[0][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "s");
\r
2605 pieceBitmap[1][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "o");
\r
2606 pieceBitmap[2][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "w");
\r
2607 pieceBitmap[0][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "s");
\r
2608 pieceBitmap[1][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "o");
\r
2609 pieceBitmap[2][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "w");
\r
2610 pieceBitmap[0][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "s");
\r
2611 pieceBitmap[1][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "o");
\r
2612 pieceBitmap[2][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "w");
\r
2613 pieceBitmap[0][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "s");
\r
2614 pieceBitmap[1][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "o");
\r
2615 pieceBitmap[2][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "w");
\r
2616 pieceBitmap[0][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "s");
\r
2617 pieceBitmap[1][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "o");
\r
2618 pieceBitmap[2][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "w");
\r
2619 pieceBitmap[0][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "s");
\r
2620 pieceBitmap[1][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "o");
\r
2621 pieceBitmap[2][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "w");
\r
2622 pieceBitmap[0][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "s");
\r
2623 pieceBitmap[1][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "o");
\r
2624 pieceBitmap[2][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "w");
\r
2626 if(gameInfo.variant == VariantShogi) { /* promoted Gold represemtations */
\r
2627 pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "wp", squareSize, "s");
\r
2628 pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "wp", squareSize, "o");
\r
2629 pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "w", squareSize, "w");
\r
2630 pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "wn", squareSize, "s");
\r
2631 pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "wn", squareSize, "o");
\r
2632 pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "w", squareSize, "w");
\r
2633 pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "ws", squareSize, "s");
\r
2634 pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "ws", squareSize, "o");
\r
2635 pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "w", squareSize, "w");
\r
2636 pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "wl", squareSize, "s");
\r
2637 pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "wl", squareSize, "o");
\r
2638 pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "w", squareSize, "w");
\r
2640 pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "s");
\r
2641 pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "o");
\r
2642 pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "w");
\r
2643 pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "s");
\r
2644 pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "o");
\r
2645 pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "w");
\r
2646 pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "s");
\r
2647 pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "o");
\r
2648 pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "w");
\r
2649 pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "s");
\r
2650 pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "o");
\r
2651 pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "w");
\r
2654 } else { /* other size, no special bitmaps available. Use smaller symbols */
\r
2655 if((int)boardSize < 2) minorSize = sizeInfo[0].squareSize;
\r
2656 else minorSize = sizeInfo[(int)boardSize - 2].squareSize;
\r
2657 pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "s");
\r
2658 pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "o");
\r
2659 pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "w");
\r
2660 pieceBitmap[0][WhiteCardinal] = DoLoadBitmap(hInst, "b", minorSize, "s");
\r
2661 pieceBitmap[1][WhiteCardinal] = DoLoadBitmap(hInst, "b", minorSize, "o");
\r
2662 pieceBitmap[2][WhiteCardinal] = DoLoadBitmap(hInst, "b", minorSize, "w");
\r
2663 pieceBitmap[0][WhiteDragon] = DoLoadBitmap(hInst, "r", minorSize, "s");
\r
2664 pieceBitmap[1][WhiteDragon] = DoLoadBitmap(hInst, "r", minorSize, "o");
\r
2665 pieceBitmap[2][WhiteDragon] = DoLoadBitmap(hInst, "r", minorSize, "w");
\r
2666 pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "s");
\r
2667 pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "o");
\r
2668 pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "w");
\r
2672 if(gameInfo.variant == VariantShogi && squareSize == 58)
\r
2673 /* special Shogi support in this size */
\r
2674 { for (i=0; i<=2; i++) { /* replace all bitmaps */
\r
2675 for (piece = WhitePawn;
\r
2676 (int) piece < (int) BlackPawn;
\r
2677 piece = (ChessSquare) ((int) piece + 1)) {
\r
2678 if (pieceBitmap[i][piece] != NULL)
\r
2679 DeleteObject(pieceBitmap[i][piece]);
\r
2682 pieceBitmap[0][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "o");
\r
2683 pieceBitmap[0][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "o");
\r
2684 pieceBitmap[0][WhiteBishop] = DoLoadBitmap(hInst, "sb", squareSize, "o");
\r
2685 pieceBitmap[0][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "o");
\r
2686 pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "o");
\r
2687 pieceBitmap[0][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "o");
\r
2688 pieceBitmap[0][WhiteFerz] = DoLoadBitmap(hInst, "sf", squareSize, "o");
\r
2689 pieceBitmap[0][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "o");
\r
2690 pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "su", squareSize, "o");
\r
2691 pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "sh", squareSize, "o");
\r
2692 pieceBitmap[0][WhiteCardinal] = DoLoadBitmap(hInst, "sa", squareSize, "o");
\r
2693 pieceBitmap[0][WhiteDragon] = DoLoadBitmap(hInst, "sc", squareSize, "o");
\r
2694 pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "sg", squareSize, "o");
\r
2695 pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "ss", squareSize, "o");
\r
2696 pieceBitmap[1][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "o");
\r
2697 pieceBitmap[1][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "o");
\r
2698 pieceBitmap[1][WhiteBishop] = DoLoadBitmap(hInst, "sb", squareSize, "o");
\r
2699 pieceBitmap[1][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "o");
\r
2700 pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "o");
\r
2701 pieceBitmap[1][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "o");
\r
2702 pieceBitmap[1][WhiteFerz] = DoLoadBitmap(hInst, "sf", squareSize, "o");
\r
2703 pieceBitmap[1][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "o");
\r
2704 pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "su", squareSize, "o");
\r
2705 pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "sh", squareSize, "o");
\r
2706 pieceBitmap[1][WhiteCardinal] = DoLoadBitmap(hInst, "sa", squareSize, "o");
\r
2707 pieceBitmap[1][WhiteDragon] = DoLoadBitmap(hInst, "sc", squareSize, "o");
\r
2708 pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "sg", squareSize, "o");
\r
2709 pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "ss", squareSize, "o");
\r
2710 pieceBitmap[2][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "w");
\r
2711 pieceBitmap[2][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "w");
\r
2712 pieceBitmap[2][WhiteBishop] = DoLoadBitmap(hInst, "sr", squareSize, "w");
\r
2713 pieceBitmap[2][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "w");
\r
2714 pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "w");
\r
2715 pieceBitmap[2][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "w");
\r
2716 pieceBitmap[2][WhiteFerz] = DoLoadBitmap(hInst, "sw", squareSize, "w");
\r
2717 pieceBitmap[2][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "w");
\r
2718 pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "sp", squareSize, "w");
\r
2719 pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "sn", squareSize, "w");
\r
2720 pieceBitmap[2][WhiteCardinal] = DoLoadBitmap(hInst, "sr", squareSize, "w");
\r
2721 pieceBitmap[2][WhiteDragon] = DoLoadBitmap(hInst, "sr", squareSize, "w");
\r
2722 pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "sl", squareSize, "w");
\r
2723 pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "sw", squareSize, "w");
\r
2729 PieceBitmap(ChessSquare p, int kind)
\r
2731 if ((int) p >= (int) BlackPawn)
\r
2732 p = (ChessSquare) ((int) p - (int) BlackPawn + (int) WhitePawn);
\r
2734 return pieceBitmap[kind][(int) p];
\r
2737 /***************************************************************/
\r
2739 #define MIN(a,b) (((a) < (b)) ? (a) : (b))
\r
2740 #define MAX(a,b) (((a) > (b)) ? (a) : (b))
\r
2742 #define MIN3(a,b,c) (((a) < (b) && (a) < (c)) ? (a) : (((b) < (a) && (b) < (c)) ? (b) : (c)))
\r
2743 #define MAX3(a,b,c) (((a) > (b) && (a) > (c)) ? (a) : (((b) > (a) && (b) > (c)) ? (b) : (c)))
\r
2747 SquareToPos(int row, int column, int * x, int * y)
\r
2750 *x = boardRect.left + lineGap + ((BOARD_WIDTH-1)-column) * (squareSize + lineGap) + border;
\r
2751 *y = boardRect.top + lineGap + row * (squareSize + lineGap) + border;
\r
2753 *x = boardRect.left + lineGap + column * (squareSize + lineGap) + border;
\r
2754 *y = boardRect.top + lineGap + ((BOARD_HEIGHT-1)-row) * (squareSize + lineGap) + border;
\r
2759 DrawCoordsOnDC(HDC hdc)
\r
2761 static char files[] = "0123456789012345678901221098765432109876543210";
\r
2762 static char ranks[] = "wvutsrqponmlkjihgfedcbaabcdefghijklmnopqrstuvw";
\r
2763 char str[2] = { NULLCHAR, NULLCHAR };
\r
2764 int oldMode, oldAlign, x, y, start, i;
\r
2768 if (!appData.showCoords)
\r
2771 start = flipView ? 1-(ONE!='1') : 45+(ONE!='1')-BOARD_HEIGHT;
\r
2773 oldBrush = SelectObject(hdc, GetStockObject(BLACK_BRUSH));
\r
2774 oldMode = SetBkMode(hdc, (appData.monoMode ? OPAQUE : TRANSPARENT));
\r
2775 oldAlign = GetTextAlign(hdc);
\r
2776 oldFont = SelectObject(hdc, font[boardSize][COORD_FONT]->hf);
\r
2778 y = boardRect.top + lineGap;
\r
2779 x = boardRect.left + lineGap + gameInfo.holdingsWidth*(squareSize + lineGap);
\r
2782 SetTextAlign(hdc, TA_RIGHT|TA_TOP);
\r
2783 x += border - lineGap - 4; y += squareSize - 6;
\r
2785 SetTextAlign(hdc, TA_LEFT|TA_TOP);
\r
2786 for (i = 0; i < BOARD_HEIGHT; i++) {
\r
2787 str[0] = files[start + i];
\r
2788 ExtTextOut(hdc, x + 2 - (border ? gameInfo.holdingsWidth * (squareSize + lineGap) : 0), y + 1, 0, NULL, str, 1, NULL);
\r
2789 y += squareSize + lineGap;
\r
2792 start = flipView ? 23-(BOARD_RGHT-BOARD_LEFT) : 23;
\r
2795 SetTextAlign(hdc, TA_LEFT|TA_TOP);
\r
2796 x += -border + 4; y += border - squareSize + 6;
\r
2798 SetTextAlign(hdc, TA_RIGHT|TA_BOTTOM);
\r
2799 for (i = 0; i < BOARD_RGHT - BOARD_LEFT; i++) {
\r
2800 str[0] = ranks[start + i];
\r
2801 ExtTextOut(hdc, x + squareSize - 2, y - 1, 0, NULL, str, 1, NULL);
\r
2802 x += squareSize + lineGap;
\r
2805 SelectObject(hdc, oldBrush);
\r
2806 SetBkMode(hdc, oldMode);
\r
2807 SetTextAlign(hdc, oldAlign);
\r
2808 SelectObject(hdc, oldFont);
\r
2812 DrawGridOnDC(HDC hdc)
\r
2816 if (lineGap != 0) {
\r
2817 oldPen = SelectObject(hdc, gridPen);
\r
2818 PolyPolyline(hdc, gridEndpoints, gridVertexCounts, BOARD_WIDTH+BOARD_HEIGHT + 2);
\r
2819 SelectObject(hdc, oldPen);
\r
2823 #define HIGHLIGHT_PEN 0
\r
2824 #define PREMOVE_PEN 1
\r
2827 DrawHighlightOnDC(HDC hdc, BOOLEAN on, int x, int y, int pen)
\r
2830 HPEN oldPen, hPen;
\r
2831 if (lineGap == 0) return;
\r
2833 x1 = boardRect.left +
\r
2834 lineGap/2 + ((BOARD_WIDTH-1)-x) * (squareSize + lineGap) + border;
\r
2835 y1 = boardRect.top +
\r
2836 lineGap/2 + y * (squareSize + lineGap) + border;
\r
2838 x1 = boardRect.left +
\r
2839 lineGap/2 + x * (squareSize + lineGap) + border;
\r
2840 y1 = boardRect.top +
\r
2841 lineGap/2 + ((BOARD_HEIGHT-1)-y) * (squareSize + lineGap) + border;
\r
2843 hPen = pen ? premovePen : highlightPen;
\r
2844 oldPen = SelectObject(hdc, on ? hPen : gridPen);
\r
2845 MoveToEx(hdc, x1, y1, NULL);
\r
2846 LineTo(hdc, x1 + squareSize + lineGap, y1);
\r
2847 LineTo(hdc, x1 + squareSize + lineGap, y1 + squareSize + lineGap);
\r
2848 LineTo(hdc, x1, y1 + squareSize + lineGap);
\r
2849 LineTo(hdc, x1, y1);
\r
2850 SelectObject(hdc, oldPen);
\r
2854 DrawHighlightsOnDC(HDC hdc, HighlightInfo *h, int pen)
\r
2857 for (i=0; i<2; i++) {
\r
2858 if (h->sq[i].x >= 0 && h->sq[i].y >= 0)
\r
2859 DrawHighlightOnDC(hdc, TRUE,
\r
2860 h->sq[i].x, h->sq[i].y,
\r
2865 /* Note: sqcolor is used only in monoMode */
\r
2866 /* Note that this code is largely duplicated in woptions.c,
\r
2867 function DrawSampleSquare, so that needs to be updated too */
\r
2869 DrawPieceOnDC(HDC hdc, ChessSquare piece, int color, int sqcolor, int x, int y, HDC tmphdc)
\r
2871 HBITMAP oldBitmap;
\r
2875 if (appData.blindfold) return;
\r
2877 /* [AS] Use font-based pieces if needed */
\r
2878 if( fontBitmapSquareSize >= 0 && (squareSize > 32 || gameInfo.variant >= VariantShogi)) {
\r
2879 /* Create piece bitmaps, or do nothing if piece set is up to date */
\r
2880 CreatePiecesFromFont();
\r
2882 if( fontBitmapSquareSize == squareSize ) {
\r
2883 int index = TranslatePieceToFontPiece(piece);
\r
2885 SelectObject( tmphdc, hPieceMask[ index ] );
\r
2887 if(appData.upsideDown ? color==flipView : (flipView && gameInfo.variant == VariantShogi))
\r
2888 StretchBlt(hdc, x+squareSize, y+squareSize, -squareSize, -squareSize, tmphdc, 0, 0, squareSize, squareSize, SRCAND);
\r
2892 squareSize, squareSize,
\r
2897 SelectObject( tmphdc, hPieceFace[ index ] );
\r
2899 if(appData.upsideDown ? color==flipView : (flipView && gameInfo.variant == VariantShogi))
\r
2900 StretchBlt(hdc, x+squareSize, y+squareSize, -squareSize, -squareSize, tmphdc, 0, 0, squareSize, squareSize, SRCPAINT);
\r
2904 squareSize, squareSize,
\r
2913 if (appData.monoMode) {
\r
2914 SelectObject(tmphdc, PieceBitmap(piece,
\r
2915 color == sqcolor ? OUTLINE_PIECE : SOLID_PIECE));
\r
2916 BitBlt(hdc, x, y, squareSize, squareSize, tmphdc, 0, 0,
\r
2917 sqcolor ? SRCCOPY : NOTSRCCOPY);
\r
2919 HBRUSH xBrush = whitePieceBrush;
\r
2920 tmpSize = squareSize;
\r
2921 if(appData.pieceDirectory[0]) xBrush = GetStockObject(WHITE_BRUSH);
\r
2923 ((piece >= (int)WhiteNightrider && piece <= WhiteGrasshopper) ||
\r
2924 (piece >= (int)BlackNightrider && piece <= BlackGrasshopper)) ) {
\r
2925 /* [HGM] no bitmap available for promoted pieces in Crazyhouse */
\r
2926 /* Bitmaps of smaller size are substituted, but we have to align them */
\r
2927 x += (squareSize - minorSize)>>1;
\r
2928 y += squareSize - minorSize - 2;
\r
2929 tmpSize = minorSize;
\r
2931 if (color || appData.allWhite ) {
\r
2932 oldBitmap = SelectObject(tmphdc, PieceBitmap(piece, WHITE_PIECE));
\r
2934 oldBrush = SelectObject(hdc, xBrush);
\r
2935 else oldBrush = SelectObject(hdc, blackPieceBrush);
\r
2936 if(appData.upsideDown && color==flipView)
\r
2937 StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, 0x00B8074A);
\r
2939 BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, 0x00B8074A);
\r
2940 /* Use black for outline of white pieces */
\r
2941 SelectObject(tmphdc, PieceBitmap(piece, OUTLINE_PIECE));
\r
2942 if(appData.upsideDown && color==flipView)
\r
2943 StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, SRCAND);
\r
2945 BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, SRCAND);
\r
2946 } else if(appData.pieceDirectory[0]) {
\r
2947 oldBitmap = SelectObject(tmphdc, PieceBitmap(piece, WHITE_PIECE));
\r
2948 oldBrush = SelectObject(hdc, xBrush);
\r
2949 if(appData.upsideDown && color==flipView)
\r
2950 StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, 0x00B8074A);
\r
2952 BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, 0x00B8074A);
\r
2953 SelectObject(tmphdc, PieceBitmap(piece, SOLID_PIECE));
\r
2954 if(appData.upsideDown && color==flipView)
\r
2955 StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, SRCAND);
\r
2957 BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, SRCAND);
\r
2959 /* Use square color for details of black pieces */
\r
2960 oldBitmap = SelectObject(tmphdc, PieceBitmap(piece, SOLID_PIECE));
\r
2961 oldBrush = SelectObject(hdc, blackPieceBrush);
\r
2962 if(appData.upsideDown && !flipView)
\r
2963 StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, 0x00B8074A);
\r
2965 BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, 0x00B8074A);
\r
2967 SelectObject(hdc, oldBrush);
\r
2968 SelectObject(tmphdc, oldBitmap);
\r
2972 /* [AS] Compute a drawing mode for a square, based on specified settings (see DrawTile) */
\r
2973 int GetBackTextureMode( int algo )
\r
2975 int result = BACK_TEXTURE_MODE_DISABLED;
\r
2979 case BACK_TEXTURE_MODE_PLAIN:
\r
2980 result = 1; /* Always use identity map */
\r
2982 case BACK_TEXTURE_MODE_FULL_RANDOM:
\r
2983 result = 1 + (myrandom() % 3); /* Pick a transformation at random */
\r
2991 [AS] Compute and save texture drawing info, otherwise we may not be able
\r
2992 to handle redraws cleanly (as random numbers would always be different).
\r
2994 VOID RebuildTextureSquareInfo()
\r
3004 ZeroMemory( &backTextureSquareInfo, sizeof(backTextureSquareInfo) );
\r
3006 if( liteBackTexture != NULL ) {
\r
3007 if( GetObject( liteBackTexture, sizeof(bi), &bi ) > 0 ) {
\r
3008 lite_w = bi.bmWidth;
\r
3009 lite_h = bi.bmHeight;
\r
3013 if( darkBackTexture != NULL ) {
\r
3014 if( GetObject( darkBackTexture, sizeof(bi), &bi ) > 0 ) {
\r
3015 dark_w = bi.bmWidth;
\r
3016 dark_h = bi.bmHeight;
\r
3020 for( row=0; row<BOARD_HEIGHT; row++ ) {
\r
3021 for( col=0; col<BOARD_WIDTH; col++ ) {
\r
3022 if( (col + row) & 1 ) {
\r
3024 if( lite_w >= squareSize && lite_h >= squareSize ) {
\r
3025 if( lite_w >= squareSize*BOARD_WIDTH )
\r
3026 backTextureSquareInfo[row][col].x = (2*col+1)*lite_w/(2*BOARD_WIDTH) - squareSize/2; /* [HGM] cut out of center of virtual square */
\r
3028 backTextureSquareInfo[row][col].x = col * (lite_w - squareSize) / (BOARD_WIDTH-1); /* [HGM] divide by size-1 in stead of size! */
\r
3029 if( lite_h >= squareSize*BOARD_HEIGHT )
\r
3030 backTextureSquareInfo[row][col].y = (2*(BOARD_HEIGHT-row)-1)*lite_h/(2*BOARD_HEIGHT) - squareSize/2;
\r
3032 backTextureSquareInfo[row][col].y = (BOARD_HEIGHT-1-row) * (lite_h - squareSize) / (BOARD_HEIGHT-1);
\r
3033 backTextureSquareInfo[row][col].mode = GetBackTextureMode(liteBackTextureMode);
\r
3038 if( dark_w >= squareSize && dark_h >= squareSize ) {
\r
3039 if( dark_w >= squareSize*BOARD_WIDTH )
\r
3040 backTextureSquareInfo[row][col].x = (2*col+1) * dark_w / (2*BOARD_WIDTH) - squareSize/2;
\r
3042 backTextureSquareInfo[row][col].x = col * (dark_w - squareSize) / (BOARD_WIDTH-1);
\r
3043 if( dark_h >= squareSize*BOARD_HEIGHT )
\r
3044 backTextureSquareInfo[row][col].y = (2*(BOARD_HEIGHT-row)-1) * dark_h / (2*BOARD_HEIGHT) - squareSize/2;
\r
3046 backTextureSquareInfo[row][col].y = (BOARD_HEIGHT-1-row) * (dark_h - squareSize) / (BOARD_HEIGHT-1);
\r
3047 backTextureSquareInfo[row][col].mode = GetBackTextureMode(darkBackTextureMode);
\r
3054 /* [AS] Arrow highlighting support */
\r
3056 static double A_WIDTH = 5; /* Width of arrow body */
\r
3058 #define A_HEIGHT_FACTOR 6 /* Length of arrow "point", relative to body width */
\r
3059 #define A_WIDTH_FACTOR 3 /* Width of arrow "point", relative to body width */
\r
3061 static double Sqr( double x )
\r
3066 static int Round( double x )
\r
3068 return (int) (x + 0.5);
\r
3071 /* Draw an arrow between two points using current settings */
\r
3072 VOID DrawArrowBetweenPoints( HDC hdc, int s_x, int s_y, int d_x, int d_y )
\r
3075 double dx, dy, j, k, x, y;
\r
3077 if( d_x == s_x ) {
\r
3078 int h = (d_y > s_y) ? +A_WIDTH*A_HEIGHT_FACTOR : -A_WIDTH*A_HEIGHT_FACTOR;
\r
3080 arrow[0].x = s_x + A_WIDTH + 0.5;
\r
3083 arrow[1].x = s_x + A_WIDTH + 0.5;
\r
3084 arrow[1].y = d_y - h;
\r
3086 arrow[2].x = arrow[1].x + A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;
\r
3087 arrow[2].y = d_y - h;
\r
3092 arrow[5].x = arrow[1].x - 2*A_WIDTH + 0.5;
\r
3093 arrow[5].y = d_y - h;
\r
3095 arrow[4].x = arrow[5].x - A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;
\r
3096 arrow[4].y = d_y - h;
\r
3098 arrow[6].x = arrow[1].x - 2*A_WIDTH + 0.5;
\r
3101 else if( d_y == s_y ) {
\r
3102 int w = (d_x > s_x) ? +A_WIDTH*A_HEIGHT_FACTOR : -A_WIDTH*A_HEIGHT_FACTOR;
\r
3105 arrow[0].y = s_y + A_WIDTH + 0.5;
\r
3107 arrow[1].x = d_x - w;
\r
3108 arrow[1].y = s_y + A_WIDTH + 0.5;
\r
3110 arrow[2].x = d_x - w;
\r
3111 arrow[2].y = arrow[1].y + A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;
\r
3116 arrow[5].x = d_x - w;
\r
3117 arrow[5].y = arrow[1].y - 2*A_WIDTH + 0.5;
\r
3119 arrow[4].x = d_x - w;
\r
3120 arrow[4].y = arrow[5].y - A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;
\r
3123 arrow[6].y = arrow[1].y - 2*A_WIDTH + 0.5;
\r
3126 /* [AS] Needed a lot of paper for this! :-) */
\r
3127 dy = (double) (d_y - s_y) / (double) (d_x - s_x);
\r
3128 dx = (double) (s_x - d_x) / (double) (s_y - d_y);
\r
3130 j = sqrt( Sqr(A_WIDTH) / (1.0 + Sqr(dx)) );
\r
3132 k = sqrt( Sqr(A_WIDTH*A_HEIGHT_FACTOR) / (1.0 + Sqr(dy)) );
\r
3137 arrow[0].x = Round(x - j);
\r
3138 arrow[0].y = Round(y + j*dx);
\r
3140 arrow[1].x = Round(arrow[0].x + 2*j); // [HGM] prevent width to be affected by rounding twice
\r
3141 arrow[1].y = Round(arrow[0].y - 2*j*dx);
\r
3144 x = (double) d_x - k;
\r
3145 y = (double) d_y - k*dy;
\r
3148 x = (double) d_x + k;
\r
3149 y = (double) d_y + k*dy;
\r
3152 x = Round(x); y = Round(y); // [HGM] make sure width of shaft is rounded the same way on both ends
\r
3154 arrow[6].x = Round(x - j);
\r
3155 arrow[6].y = Round(y + j*dx);
\r
3157 arrow[2].x = Round(arrow[6].x + 2*j);
\r
3158 arrow[2].y = Round(arrow[6].y - 2*j*dx);
\r
3160 arrow[3].x = Round(arrow[2].x + j*(A_WIDTH_FACTOR-1));
\r
3161 arrow[3].y = Round(arrow[2].y - j*(A_WIDTH_FACTOR-1)*dx);
\r
3166 arrow[5].x = Round(arrow[6].x - j*(A_WIDTH_FACTOR-1));
\r
3167 arrow[5].y = Round(arrow[6].y + j*(A_WIDTH_FACTOR-1)*dx);
\r
3170 Polygon( hdc, arrow, 7 );
\r
3173 /* [AS] Draw an arrow between two squares */
\r
3174 VOID DrawArrowBetweenSquares( HDC hdc, int s_col, int s_row, int d_col, int d_row )
\r
3176 int s_x, s_y, d_x, d_y;
\r
3183 if( s_col == d_col && s_row == d_row ) {
\r
3187 /* Get source and destination points */
\r
3188 SquareToPos( s_row, s_col, &s_x, &s_y);
\r
3189 SquareToPos( d_row, d_col, &d_x, &d_y);
\r
3192 d_y += squareSize / 2 - squareSize / 4; // [HGM] round towards same centers on all sides!
\r
3194 else if( d_y < s_y ) {
\r
3195 d_y += squareSize / 2 + squareSize / 4;
\r
3198 d_y += squareSize / 2;
\r
3202 d_x += squareSize / 2 - squareSize / 4;
\r
3204 else if( d_x < s_x ) {
\r
3205 d_x += squareSize / 2 + squareSize / 4;
\r
3208 d_x += squareSize / 2;
\r
3211 s_x += squareSize / 2;
\r
3212 s_y += squareSize / 2;
\r
3214 /* Adjust width */
\r
3215 A_WIDTH = squareSize / 14.; //[HGM] make float
\r
3218 stLB.lbStyle = BS_SOLID;
\r
3219 stLB.lbColor = appData.highlightArrowColor;
\r
3222 hpen = CreatePen( PS_SOLID, 2, RGB(0x00,0x00,0x00) );
\r
3223 holdpen = SelectObject( hdc, hpen );
\r
3224 hbrush = CreateBrushIndirect( &stLB );
\r
3225 holdbrush = SelectObject( hdc, hbrush );
\r
3227 DrawArrowBetweenPoints( hdc, s_x, s_y, d_x, d_y );
\r
3229 SelectObject( hdc, holdpen );
\r
3230 SelectObject( hdc, holdbrush );
\r
3231 DeleteObject( hpen );
\r
3232 DeleteObject( hbrush );
\r
3235 BOOL HasHighlightInfo()
\r
3237 BOOL result = FALSE;
\r
3239 if( highlightInfo.sq[0].x >= 0 && highlightInfo.sq[0].y >= 0 &&
\r
3240 highlightInfo.sq[1].x >= 0 && highlightInfo.sq[1].y >= 0 )
\r
3248 BOOL IsDrawArrowEnabled()
\r
3250 BOOL result = FALSE;
\r
3252 if( appData.highlightMoveWithArrow && squareSize >= 32 ) {
\r
3259 VOID DrawArrowHighlight( HDC hdc )
\r
3261 if( IsDrawArrowEnabled() && HasHighlightInfo() ) {
\r
3262 DrawArrowBetweenSquares( hdc,
\r
3263 highlightInfo.sq[0].x, highlightInfo.sq[0].y,
\r
3264 highlightInfo.sq[1].x, highlightInfo.sq[1].y );
\r
3268 HRGN GetArrowHighlightClipRegion( HDC hdc )
\r
3270 HRGN result = NULL;
\r
3272 if( HasHighlightInfo() ) {
\r
3273 int x1, y1, x2, y2;
\r
3274 int sx, sy, dx, dy;
\r
3276 SquareToPos(highlightInfo.sq[0].y, highlightInfo.sq[0].x, &x1, &y1 );
\r
3277 SquareToPos(highlightInfo.sq[1].y, highlightInfo.sq[1].x, &x2, &y2 );
\r
3279 sx = MIN( x1, x2 );
\r
3280 sy = MIN( y1, y2 );
\r
3281 dx = MAX( x1, x2 ) + squareSize;
\r
3282 dy = MAX( y1, y2 ) + squareSize;
\r
3284 result = CreateRectRgn( sx, sy, dx, dy );
\r
3291 Warning: this function modifies the behavior of several other functions.
\r
3293 Basically, Winboard is optimized to avoid drawing the whole board if not strictly
\r
3294 needed. Unfortunately, the decision whether or not to perform a full or partial
\r
3295 repaint is scattered all over the place, which is not good for features such as
\r
3296 "arrow highlighting" that require a full repaint of the board.
\r
3298 So, I've tried to patch the code where I thought it made sense (e.g. after or during
\r
3299 user interaction, when speed is not so important) but especially to avoid errors
\r
3300 in the displayed graphics.
\r
3302 In such patched places, I always try refer to this function so there is a single
\r
3303 place to maintain knowledge.
\r
3305 To restore the original behavior, just return FALSE unconditionally.
\r
3307 BOOL IsFullRepaintPreferrable()
\r
3309 BOOL result = FALSE;
\r
3311 if( (appData.highlightLastMove || appData.highlightDragging) && IsDrawArrowEnabled() ) {
\r
3312 /* Arrow may appear on the board */
\r
3320 This function is called by DrawPosition to know whether a full repaint must
\r
3323 Only DrawPosition may directly call this function, which makes use of
\r
3324 some state information. Other function should call DrawPosition specifying
\r
3325 the repaint flag, and can use IsFullRepaintPreferrable if needed.
\r
3327 BOOL DrawPositionNeedsFullRepaint()
\r
3329 BOOL result = FALSE;
\r
3332 Probably a slightly better policy would be to trigger a full repaint
\r
3333 when animInfo.piece changes state (i.e. empty -> non-empty and viceversa),
\r
3334 but animation is fast enough that it's difficult to notice.
\r
3336 if( animInfo.piece == EmptySquare ) {
\r
3337 if( (appData.highlightLastMove || appData.highlightDragging) && IsDrawArrowEnabled() /*&& HasHighlightInfo()*/ ) {
\r
3345 static HBITMAP borderBitmap;
\r
3348 DrawBackgroundOnDC(HDC hdc)
\r
3354 static char oldBorder[MSG_SIZ];
\r
3355 int w = 600, h = 600, mode;
\r
3357 if(strcmp(appData.border, oldBorder)) { // load new one when old one no longer valid
\r
3358 strncpy(oldBorder, appData.border, MSG_SIZ-1);
\r
3359 borderBitmap = LoadImage( 0, appData.border, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );
\r
3361 if(borderBitmap == NULL) { // loading failed, use white
\r
3362 FillRect( hdc, &boardRect, whitePieceBrush );
\r
3365 tmphdc = CreateCompatibleDC(hdc);
\r
3366 hbm = SelectObject(tmphdc, borderBitmap);
\r
3367 if( GetObject( borderBitmap, sizeof(bi), &bi ) > 0 ) {
\r
3371 mode = SetStretchBltMode(hdc, COLORONCOLOR);
\r
3372 StretchBlt(hdc, boardRect.left, boardRect.top, boardRect.right - boardRect.left,
\r
3373 boardRect.bottom - boardRect.top, tmphdc, 0, 0, w, h, SRCCOPY);
\r
3374 SetStretchBltMode(hdc, mode);
\r
3375 SelectObject(tmphdc, hbm);
\r
3380 DrawBoardOnDC(HDC hdc, Board board, HDC tmphdc)
\r
3382 int row, column, x, y, square_color, piece_color;
\r
3383 ChessSquare piece;
\r
3385 HDC texture_hdc = NULL;
\r
3387 /* [AS] Initialize background textures if needed */
\r
3388 if( liteBackTexture != NULL || darkBackTexture != NULL ) {
\r
3389 static int backTextureBoardSize; /* [HGM] boardsize: also new texture if board format changed */
\r
3390 if( backTextureSquareSize != squareSize
\r
3391 || backTextureBoardSize != BOARD_WIDTH+BOARD_FILES*BOARD_HEIGHT) {
\r
3392 backTextureBoardSize = BOARD_WIDTH+BOARD_FILES*BOARD_HEIGHT;
\r
3393 backTextureSquareSize = squareSize;
\r
3394 RebuildTextureSquareInfo();
\r
3397 texture_hdc = CreateCompatibleDC( hdc );
\r
3400 for (row = 0; row < BOARD_HEIGHT; row++) {
\r
3401 for (column = 0; column < BOARD_WIDTH; column++) {
\r
3403 SquareToPos(row, column, &x, &y);
\r
3405 piece = board[row][column];
\r
3407 square_color = ((column + row) % 2) == 1;
\r
3408 if( gameInfo.variant == VariantXiangqi ) {
\r
3409 square_color = !InPalace(row, column);
\r
3410 if(BOARD_HEIGHT&1) { if(row==BOARD_HEIGHT/2) square_color ^= 1; }
\r
3411 else if(row < BOARD_HEIGHT/2) square_color ^= 1;
\r
3413 piece_color = (int) piece < (int) BlackPawn;
\r
3416 /* [HGM] holdings file: light square or black */
\r
3417 if(column == BOARD_LEFT-2) {
\r
3418 if( row > BOARD_HEIGHT - gameInfo.holdingsSize - 1 )
\r
3421 DisplayHoldingsCount(hdc, x, y, 0, 0); /* black out */
\r
3425 if(column == BOARD_RGHT + 1 ) {
\r
3426 if( row < gameInfo.holdingsSize )
\r
3429 DisplayHoldingsCount(hdc, x, y, 0, 0);
\r
3433 if(column == BOARD_LEFT-1 ) /* left align */
\r
3434 DisplayHoldingsCount(hdc, x, y, flipView, (int) board[row][column]);
\r
3435 else if( column == BOARD_RGHT) /* right align */
\r
3436 DisplayHoldingsCount(hdc, x, y, !flipView, (int) board[row][column]);
\r
3438 if (appData.monoMode) {
\r
3439 if (piece == EmptySquare) {
\r
3440 BitBlt(hdc, x, y, squareSize, squareSize, 0, 0, 0,
\r
3441 square_color ? WHITENESS : BLACKNESS);
\r
3443 DrawPieceOnDC(hdc, piece, piece_color, square_color, x, y, tmphdc);
\r
3446 else if( appData.useBitmaps && backTextureSquareInfo[row][column].mode > 0 ) {
\r
3447 /* [AS] Draw the square using a texture bitmap */
\r
3448 HBITMAP hbm = SelectObject( texture_hdc, square_color ? liteBackTexture : darkBackTexture );
\r
3449 int r = row, c = column; // [HGM] do not flip board in flipView
\r
3450 if(flipView) { r = BOARD_HEIGHT-1 - r; c = BOARD_WIDTH-1 - c; }
\r
3453 squareSize, squareSize,
\r
3456 backTextureSquareInfo[r][c].mode,
\r
3457 backTextureSquareInfo[r][c].x,
\r
3458 backTextureSquareInfo[r][c].y );
\r
3460 SelectObject( texture_hdc, hbm );
\r
3462 if (piece != EmptySquare) {
\r
3463 DrawPieceOnDC(hdc, piece, piece_color, -1, x, y, tmphdc);
\r
3467 HBRUSH brush = square_color ? lightSquareBrush : darkSquareBrush;
\r
3469 oldBrush = SelectObject(hdc, brush );
\r
3470 BitBlt(hdc, x, y, squareSize, squareSize, 0, 0, 0, PATCOPY);
\r
3471 SelectObject(hdc, oldBrush);
\r
3472 if (piece != EmptySquare)
\r
3473 DrawPieceOnDC(hdc, piece, piece_color, -1, x, y, tmphdc);
\r
3478 if( texture_hdc != NULL ) {
\r
3479 DeleteDC( texture_hdc );
\r
3483 int saveDiagFlag = 0; FILE *diagFile; // [HGM] diag
\r
3484 void fputDW(FILE *f, int x)
\r
3486 fputc(x & 255, f);
\r
3487 fputc(x>>8 & 255, f);
\r
3488 fputc(x>>16 & 255, f);
\r
3489 fputc(x>>24 & 255, f);
\r
3492 #define MAX_CLIPS 200 /* more than enough */
\r
3495 DrawLogoOnDC(HDC hdc, RECT logoRect, HBITMAP logo)
\r
3497 // HBITMAP bufferBitmap;
\r
3502 int w = 100, h = 50;
\r
3504 if(logo == NULL) {
\r
3505 if(!logoHeight) return;
\r
3506 FillRect( hdc, &logoRect, whitePieceBrush );
\r
3508 // GetClientRect(hwndMain, &Rect);
\r
3509 // bufferBitmap = CreateCompatibleBitmap(hdc, Rect.right-Rect.left+1,
\r
3510 // Rect.bottom-Rect.top+1);
\r
3511 tmphdc = CreateCompatibleDC(hdc);
\r
3512 hbm = SelectObject(tmphdc, logo);
\r
3513 if( GetObject( logo, sizeof(bi), &bi ) > 0 ) {
\r
3517 StretchBlt(hdc, logoRect.left, logoRect.top, logoRect.right - logoRect.left,
\r
3518 logoRect.bottom - logoRect.top, tmphdc, 0, 0, w, h, SRCCOPY);
\r
3519 SelectObject(tmphdc, hbm);
\r
3527 HDC hdc = GetDC(hwndMain);
\r
3528 HBITMAP whiteLogo = (HBITMAP) first.programLogo, blackLogo = (HBITMAP) second.programLogo;
\r
3529 if(appData.autoLogo) {
\r
3531 switch(gameMode) { // pick logos based on game mode
\r
3532 case IcsObserving:
\r
3533 whiteLogo = second.programLogo; // ICS logo
\r
3534 blackLogo = second.programLogo;
\r
3537 case IcsPlayingWhite:
\r
3538 if(!appData.zippyPlay) whiteLogo = userLogo;
\r
3539 blackLogo = second.programLogo; // ICS logo
\r
3541 case IcsPlayingBlack:
\r
3542 whiteLogo = second.programLogo; // ICS logo
\r
3543 blackLogo = appData.zippyPlay ? first.programLogo : userLogo;
\r
3545 case TwoMachinesPlay:
\r
3546 if(first.twoMachinesColor[0] == 'b') {
\r
3547 whiteLogo = second.programLogo;
\r
3548 blackLogo = first.programLogo;
\r
3551 case MachinePlaysWhite:
\r
3552 blackLogo = userLogo;
\r
3554 case MachinePlaysBlack:
\r
3555 whiteLogo = userLogo;
\r
3556 blackLogo = first.programLogo;
\r
3559 DrawLogoOnDC(hdc, leftLogoRect, flipClock ? blackLogo : whiteLogo);
\r
3560 DrawLogoOnDC(hdc, rightLogoRect, flipClock ? whiteLogo : blackLogo);
\r
3561 ReleaseDC(hwndMain, hdc);
\r
3566 UpdateLogos(int display)
\r
3567 { // called after loading new engine(s), in tourney or from menu
\r
3568 LoadLogo(&first, 0, FALSE);
\r
3569 LoadLogo(&second, 1, appData.icsActive);
\r
3570 InitDrawingSizes(-2, 0); // adapt layout of board window to presence/absence of logos
\r
3571 if(display) DisplayLogos();
\r
3574 static HDC hdcSeek;
\r
3576 // [HGM] seekgraph
\r
3577 void DrawSeekAxis( int x, int y, int xTo, int yTo )
\r
3580 HPEN hp = SelectObject( hdcSeek, gridPen );
\r
3581 MoveToEx( hdcSeek, boardRect.left+x, boardRect.top+y, &stPt );
\r
3582 LineTo( hdcSeek, boardRect.left+xTo, boardRect.top+yTo );
\r
3583 SelectObject( hdcSeek, hp );
\r
3586 // front-end wrapper for drawing functions to do rectangles
\r
3587 void DrawSeekBackground( int left, int top, int right, int bottom )
\r
3592 if (hdcSeek == NULL) {
\r
3593 hdcSeek = GetDC(hwndMain);
\r
3594 if (!appData.monoMode) {
\r
3595 SelectPalette(hdcSeek, hPal, FALSE);
\r
3596 RealizePalette(hdcSeek);
\r
3599 hp = SelectObject( hdcSeek, gridPen );
\r
3600 rc.top = boardRect.top+top; rc.left = boardRect.left+left;
\r
3601 rc.bottom = boardRect.top+bottom; rc.right = boardRect.left+right;
\r
3602 FillRect( hdcSeek, &rc, lightSquareBrush );
\r
3603 SelectObject( hdcSeek, hp );
\r
3606 // front-end wrapper for putting text in graph
\r
3607 void DrawSeekText(char *buf, int x, int y)
\r
3610 SetBkMode( hdcSeek, TRANSPARENT );
\r
3611 GetTextExtentPoint32( hdcSeek, buf, strlen(buf), &stSize );
\r
3612 TextOut( hdcSeek, boardRect.left+x-3, boardRect.top+y-stSize.cy/2, buf, strlen(buf) );
\r
3615 void DrawSeekDot(int x, int y, int color)
\r
3617 int square = color & 0x80;
\r
3618 HBRUSH oldBrush = SelectObject(hdcSeek,
\r
3619 color == 0 ? markerBrush : color == 1 ? darkSquareBrush : explodeBrush);
\r
3622 Rectangle(hdcSeek, boardRect.left+x - squareSize/9, boardRect.top+y - squareSize/9,
\r
3623 boardRect.left+x + squareSize/9, boardRect.top+y + squareSize/9);
\r
3625 Ellipse(hdcSeek, boardRect.left+x - squareSize/8, boardRect.top+y - squareSize/8,
\r
3626 boardRect.left+x + squareSize/8, boardRect.top+y + squareSize/8);
\r
3627 SelectObject(hdcSeek, oldBrush);
\r
3630 void DrawSeekOpen()
\r
3634 void DrawSeekClose()
\r
3639 HDCDrawPosition(HDC hdc, BOOLEAN repaint, Board board)
\r
3641 static Board lastReq[2], lastDrawn[2];
\r
3642 static HighlightInfo lastDrawnHighlight, lastDrawnPremove;
\r
3643 static int lastDrawnFlipView = 0;
\r
3644 static int lastReqValid[2] = {0, 0}, lastDrawnValid[2] = {0, 0};
\r
3645 int releaseDC, x, y, x2, y2, row, column, num_clips = 0, i;
\r
3648 HBITMAP bufferBitmap;
\r
3649 HBITMAP oldBitmap;
\r
3651 HRGN clips[MAX_CLIPS];
\r
3652 ChessSquare dragged_piece = EmptySquare;
\r
3653 int nr = twoBoards*partnerUp;
\r
3655 /* I'm undecided on this - this function figures out whether a full
\r
3656 * repaint is necessary on its own, so there's no real reason to have the
\r
3657 * caller tell it that. I think this can safely be set to FALSE - but
\r
3658 * if we trust the callers not to request full repaints unnessesarily, then
\r
3659 * we could skip some clipping work. In other words, only request a full
\r
3660 * redraw when the majority of pieces have changed positions (ie. flip,
\r
3661 * gamestart and similar) --Hawk
\r
3663 Boolean fullrepaint = repaint;
\r
3665 if(DrawSeekGraph()) return; // [HG} seekgraph: suppress printing board if seek graph up
\r
3667 if( DrawPositionNeedsFullRepaint() ) {
\r
3668 fullrepaint = TRUE;
\r
3671 if (board == NULL) {
\r
3672 if (!lastReqValid[nr]) {
\r
3675 board = lastReq[nr];
\r
3677 CopyBoard(lastReq[nr], board);
\r
3678 lastReqValid[nr] = 1;
\r
3681 if (doingSizing) {
\r
3685 if (IsIconic(hwndMain)) {
\r
3689 if (hdc == NULL) {
\r
3690 hdc = GetDC(hwndMain);
\r
3691 if (!appData.monoMode) {
\r
3692 SelectPalette(hdc, hPal, FALSE);
\r
3693 RealizePalette(hdc);
\r
3697 releaseDC = FALSE;
\r
3700 /* Create some work-DCs */
\r
3701 hdcmem = CreateCompatibleDC(hdc);
\r
3702 tmphdc = CreateCompatibleDC(hdc);
\r
3704 /* If dragging is in progress, we temporarely remove the piece */
\r
3705 /* [HGM] or temporarily decrease count if stacked */
\r
3706 /* !! Moved to before board compare !! */
\r
3707 if (dragInfo.from.x >= 0 && dragInfo.pos.x >= 0) {
\r
3708 dragged_piece = board[dragInfo.from.y][dragInfo.from.x];
\r
3709 if(dragInfo.from.x == BOARD_LEFT-2 ) {
\r
3710 if(--board[dragInfo.from.y][dragInfo.from.x+1] == 0 )
\r
3711 board[dragInfo.from.y][dragInfo.from.x] = EmptySquare;
\r
3713 if(dragInfo.from.x == BOARD_RGHT+1) {
\r
3714 if(--board[dragInfo.from.y][dragInfo.from.x-1] == 0 )
\r
3715 board[dragInfo.from.y][dragInfo.from.x] = EmptySquare;
\r
3717 board[dragInfo.from.y][dragInfo.from.x] = gatingPiece;
\r
3720 /* Figure out which squares need updating by comparing the
\r
3721 * newest board with the last drawn board and checking if
\r
3722 * flipping has changed.
\r
3724 if (!fullrepaint && lastDrawnValid[nr] && (nr == 1 || lastDrawnFlipView == flipView)) {
\r
3725 for (row = 0; row < BOARD_HEIGHT; row++) { /* [HGM] true size, not 8 */
\r
3726 for (column = 0; column < BOARD_WIDTH; column++) {
\r
3727 if (lastDrawn[nr][row][column] != board[row][column]) {
\r
3728 SquareToPos(row, column, &x, &y);
\r
3729 clips[num_clips++] =
\r
3730 CreateRectRgn(x, y, x + squareSize, y + squareSize);
\r
3734 if(nr == 0) { // [HGM] dual: no highlights on second board
\r
3735 for (i=0; i<2; i++) {
\r
3736 if (lastDrawnHighlight.sq[i].x != highlightInfo.sq[i].x ||
\r
3737 lastDrawnHighlight.sq[i].y != highlightInfo.sq[i].y) {
\r
3738 if (lastDrawnHighlight.sq[i].x >= 0 &&
\r
3739 lastDrawnHighlight.sq[i].y >= 0) {
\r
3740 SquareToPos(lastDrawnHighlight.sq[i].y,
\r
3741 lastDrawnHighlight.sq[i].x, &x, &y);
\r
3742 clips[num_clips++] =
\r
3743 CreateRectRgn(x - lineGap, y - lineGap,
\r
3744 x + squareSize + lineGap, y + squareSize + lineGap);
\r
3746 if (highlightInfo.sq[i].x >= 0 && highlightInfo.sq[i].y >= 0) {
\r
3747 SquareToPos(highlightInfo.sq[i].y, highlightInfo.sq[i].x, &x, &y);
\r
3748 clips[num_clips++] =
\r
3749 CreateRectRgn(x - lineGap, y - lineGap,
\r
3750 x + squareSize + lineGap, y + squareSize + lineGap);
\r
3754 for (i=0; i<2; i++) {
\r
3755 if (lastDrawnPremove.sq[i].x != premoveHighlightInfo.sq[i].x ||
\r
3756 lastDrawnPremove.sq[i].y != premoveHighlightInfo.sq[i].y) {
\r
3757 if (lastDrawnPremove.sq[i].x >= 0 &&
\r
3758 lastDrawnPremove.sq[i].y >= 0) {
\r
3759 SquareToPos(lastDrawnPremove.sq[i].y,
\r
3760 lastDrawnPremove.sq[i].x, &x, &y);
\r
3761 clips[num_clips++] =
\r
3762 CreateRectRgn(x - lineGap, y - lineGap,
\r
3763 x + squareSize + lineGap, y + squareSize + lineGap);
\r
3765 if (premoveHighlightInfo.sq[i].x >= 0 &&
\r
3766 premoveHighlightInfo.sq[i].y >= 0) {
\r
3767 SquareToPos(premoveHighlightInfo.sq[i].y,
\r
3768 premoveHighlightInfo.sq[i].x, &x, &y);
\r
3769 clips[num_clips++] =
\r
3770 CreateRectRgn(x - lineGap, y - lineGap,
\r
3771 x + squareSize + lineGap, y + squareSize + lineGap);
\r
3775 } else { // nr == 1
\r
3776 partnerHighlightInfo.sq[0].y = board[EP_STATUS-4];
\r
3777 partnerHighlightInfo.sq[0].x = board[EP_STATUS-3];
\r
3778 partnerHighlightInfo.sq[1].y = board[EP_STATUS-2];
\r
3779 partnerHighlightInfo.sq[1].x = board[EP_STATUS-1];
\r
3780 for (i=0; i<2; i++) {
\r
3781 if (partnerHighlightInfo.sq[i].x >= 0 &&
\r
3782 partnerHighlightInfo.sq[i].y >= 0) {
\r
3783 SquareToPos(partnerHighlightInfo.sq[i].y,
\r
3784 partnerHighlightInfo.sq[i].x, &x, &y);
\r
3785 clips[num_clips++] =
\r
3786 CreateRectRgn(x - lineGap, y - lineGap,
\r
3787 x + squareSize + lineGap, y + squareSize + lineGap);
\r
3789 if (oldPartnerHighlight.sq[i].x >= 0 &&
\r
3790 oldPartnerHighlight.sq[i].y >= 0) {
\r
3791 SquareToPos(oldPartnerHighlight.sq[i].y,
\r
3792 oldPartnerHighlight.sq[i].x, &x, &y);
\r
3793 clips[num_clips++] =
\r
3794 CreateRectRgn(x - lineGap, y - lineGap,
\r
3795 x + squareSize + lineGap, y + squareSize + lineGap);
\r
3800 fullrepaint = TRUE;
\r
3803 /* Create a buffer bitmap - this is the actual bitmap
\r
3804 * being written to. When all the work is done, we can
\r
3805 * copy it to the real DC (the screen). This avoids
\r
3806 * the problems with flickering.
\r
3808 GetClientRect(hwndMain, &Rect);
\r
3809 bufferBitmap = CreateCompatibleBitmap(hdc, Rect.right-Rect.left+1,
\r
3810 Rect.bottom-Rect.top+1);
\r
3811 oldBitmap = SelectObject(hdcmem, bufferBitmap);
\r
3812 if (!appData.monoMode) {
\r
3813 SelectPalette(hdcmem, hPal, FALSE);
\r
3816 /* Create clips for dragging */
\r
3817 if (!fullrepaint) {
\r
3818 if (dragInfo.from.x >= 0) {
\r
3819 SquareToPos(dragInfo.from.y, dragInfo.from.x, &x, &y);
\r
3820 clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);
\r
3822 if (dragInfo.start.x >= 0) {
\r
3823 SquareToPos(dragInfo.start.y, dragInfo.start.x, &x, &y);
\r
3824 clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);
\r
3826 if (dragInfo.pos.x >= 0) {
\r
3827 x = dragInfo.pos.x - squareSize / 2;
\r
3828 y = dragInfo.pos.y - squareSize / 2;
\r
3829 clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);
\r
3831 if (dragInfo.lastpos.x >= 0) {
\r
3832 x = dragInfo.lastpos.x - squareSize / 2;
\r
3833 y = dragInfo.lastpos.y - squareSize / 2;
\r
3834 clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);
\r
3838 /* Are we animating a move?
\r
3840 * - remove the piece from the board (temporarely)
\r
3841 * - calculate the clipping region
\r
3843 if (!fullrepaint) {
\r
3844 if (animInfo.piece != EmptySquare) {
\r
3845 board[animInfo.from.y][animInfo.from.x] = EmptySquare;
\r
3846 x = boardRect.left + animInfo.lastpos.x;
\r
3847 y = boardRect.top + animInfo.lastpos.y;
\r
3848 x2 = boardRect.left + animInfo.pos.x;
\r
3849 y2 = boardRect.top + animInfo.pos.y;
\r
3850 clips[num_clips++] = CreateRectRgn(MIN(x,x2), MIN(y,y2), MAX(x,x2)+squareSize, MAX(y,y2)+squareSize);
\r
3851 /* Slight kludge. The real problem is that after AnimateMove is
\r
3852 done, the position on the screen does not match lastDrawn.
\r
3853 This currently causes trouble only on e.p. captures in
\r
3854 atomic, where the piece moves to an empty square and then
\r
3855 explodes. The old and new positions both had an empty square
\r
3856 at the destination, but animation has drawn a piece there and
\r
3857 we have to remember to erase it. [HGM] moved until after setting lastDrawn */
\r
3858 lastDrawn[0][animInfo.to.y][animInfo.to.x] = animInfo.piece;
\r
3862 /* No clips? Make sure we have fullrepaint set to TRUE */
\r
3863 if (num_clips == 0)
\r
3864 fullrepaint = TRUE;
\r
3866 /* Set clipping on the memory DC */
\r
3867 if (!fullrepaint) {
\r
3868 SelectClipRgn(hdcmem, clips[0]);
\r
3869 for (x = 1; x < num_clips; x++) {
\r
3870 if (ExtSelectClipRgn(hdcmem, clips[x], RGN_OR) == ERROR)
\r
3871 abort(); // this should never ever happen!
\r
3875 /* Do all the drawing to the memory DC */
\r
3876 if(explodeInfo.radius) { // [HGM] atomic
\r
3878 int x, y, r=(explodeInfo.radius * squareSize)/100;
\r
3879 ChessSquare piece = board[explodeInfo.fromY][explodeInfo.fromX];
\r
3880 board[explodeInfo.fromY][explodeInfo.fromX] = EmptySquare; // suppress display of capturer
\r
3881 SquareToPos(explodeInfo.toY, explodeInfo.toX, &x, &y);
\r
3882 x += squareSize/2;
\r
3883 y += squareSize/2;
\r
3884 if(!fullrepaint) {
\r
3885 clips[num_clips] = CreateRectRgn(x-r, y-r, x+r, y+r);
\r
3886 ExtSelectClipRgn(hdcmem, clips[num_clips++], RGN_OR);
\r
3888 DrawGridOnDC(hdcmem);
\r
3889 DrawHighlightsOnDC(hdcmem, &highlightInfo, HIGHLIGHT_PEN);
\r
3890 DrawHighlightsOnDC(hdcmem, &premoveHighlightInfo, PREMOVE_PEN);
\r
3891 DrawBoardOnDC(hdcmem, board, tmphdc);
\r
3892 board[explodeInfo.fromY][explodeInfo.fromX] = piece;
\r
3893 oldBrush = SelectObject(hdcmem, explodeBrush);
\r
3894 Ellipse(hdcmem, x-r, y-r, x+r, y+r);
\r
3895 SelectObject(hdcmem, oldBrush);
\r
3897 if(border) DrawBackgroundOnDC(hdcmem);
\r
3898 DrawGridOnDC(hdcmem);
\r
3899 if(nr == 0) { // [HGM] dual: decide which highlights to draw
\r
3900 DrawHighlightsOnDC(hdcmem, &highlightInfo, HIGHLIGHT_PEN);
\r
3901 DrawHighlightsOnDC(hdcmem, &premoveHighlightInfo, PREMOVE_PEN);
\r
3903 DrawHighlightsOnDC(hdcmem, &partnerHighlightInfo, HIGHLIGHT_PEN);
\r
3904 oldPartnerHighlight = partnerHighlightInfo;
\r
3906 DrawBoardOnDC(hdcmem, board, tmphdc);
\r
3908 if(nr == 0) // [HGM] dual: markers only on left board
\r
3909 for (row = 0; row < BOARD_HEIGHT; row++) {
\r
3910 for (column = 0; column < BOARD_WIDTH; column++) {
\r
3911 if (marker[row][column]) { // marker changes only occur with full repaint!
\r
3912 HBRUSH oldBrush = SelectObject(hdcmem,
\r
3913 marker[row][column] == 2 ? markerBrush : explodeBrush);
\r
3914 SquareToPos(row, column, &x, &y);
\r
3915 Ellipse(hdcmem, x + squareSize/4, y + squareSize/4,
\r
3916 x + 3*squareSize/4, y + 3*squareSize/4);
\r
3917 SelectObject(hdcmem, oldBrush);
\r
3922 if( appData.highlightMoveWithArrow ) {
\r
3923 DrawArrowHighlight(hdcmem);
\r
3926 DrawCoordsOnDC(hdcmem);
\r
3928 CopyBoard(lastDrawn[nr], board); /* [HGM] Moved to here from end of routine, */
\r
3929 /* to make sure lastDrawn contains what is actually drawn */
\r
3931 /* Put the dragged piece back into place and draw it (out of place!) */
\r
3932 if (dragged_piece != EmptySquare) {
\r
3933 /* [HGM] or restack */
\r
3934 if(dragInfo.from.x == BOARD_LEFT-2 )
\r
3935 board[dragInfo.from.y][dragInfo.from.x+1]++;
\r
3937 if(dragInfo.from.x == BOARD_RGHT+1 )
\r
3938 board[dragInfo.from.y][dragInfo.from.x-1]++;
\r
3939 board[dragInfo.from.y][dragInfo.from.x] = dragged_piece;
\r
3940 x = dragInfo.pos.x - squareSize / 2;
\r
3941 y = dragInfo.pos.y - squareSize / 2;
\r
3942 DrawPieceOnDC(hdcmem, dragInfo.piece,
\r
3943 ((int) dragInfo.piece < (int) BlackPawn),
\r
3944 (dragInfo.from.y + dragInfo.from.x) % 2, x, y, tmphdc);
\r
3947 /* Put the animated piece back into place and draw it */
\r
3948 if (animInfo.piece != EmptySquare) {
\r
3949 board[animInfo.from.y][animInfo.from.x] = animInfo.piece;
\r
3950 x = boardRect.left + animInfo.pos.x;
\r
3951 y = boardRect.top + animInfo.pos.y;
\r
3952 DrawPieceOnDC(hdcmem, animInfo.piece,
\r
3953 ((int) animInfo.piece < (int) BlackPawn),
\r
3954 (animInfo.from.y + animInfo.from.x) % 2, x, y, tmphdc);
\r
3957 /* Release the bufferBitmap by selecting in the old bitmap
\r
3958 * and delete the memory DC
\r
3960 SelectObject(hdcmem, oldBitmap);
\r
3963 /* Set clipping on the target DC */
\r
3964 if (!fullrepaint) {
\r
3965 if(nr == 1) for (x = 0; x < num_clips; x++) { // [HGM] dual: translate clips
\r
3967 GetRgnBox(clips[x], &rect);
\r
3968 DeleteObject(clips[x]);
\r
3969 clips[x] = CreateRectRgn(rect.left + wpMain.width/2, rect.top,
\r
3970 rect.right + wpMain.width/2, rect.bottom);
\r
3972 SelectClipRgn(hdc, clips[0]);
\r
3973 for (x = 1; x < num_clips; x++) {
\r
3974 if (ExtSelectClipRgn(hdc, clips[x], RGN_OR) == ERROR)
\r
3975 abort(); // this should never ever happen!
\r
3979 /* Copy the new bitmap onto the screen in one go.
\r
3980 * This way we avoid any flickering
\r
3982 oldBitmap = SelectObject(tmphdc, bufferBitmap);
\r
3983 BitBlt(hdc, boardRect.left + twoBoards*partnerUp*wpMain.width/2, boardRect.top, // [HGM] dual
\r
3984 boardRect.right - boardRect.left,
\r
3985 boardRect.bottom - boardRect.top,
\r
3986 tmphdc, boardRect.left, boardRect.top, SRCCOPY);
\r
3987 if(saveDiagFlag) {
\r
3988 BITMAP b; int i, j=0, m, w, wb, fac=0; char *pData;
\r
3989 BITMAPINFOHEADER bih; int color[16], nrColors=0;
\r
3991 GetObject(bufferBitmap, sizeof(b), &b);
\r
3992 if(pData = malloc(b.bmWidthBytes*b.bmHeight + 10000)) {
\r
3993 bih.biSize = sizeof(BITMAPINFOHEADER);
\r
3994 bih.biWidth = b.bmWidth;
\r
3995 bih.biHeight = b.bmHeight;
\r
3997 bih.biBitCount = b.bmBitsPixel;
\r
3998 bih.biCompression = 0;
\r
3999 bih.biSizeImage = b.bmWidthBytes*b.bmHeight;
\r
4000 bih.biXPelsPerMeter = 0;
\r
4001 bih.biYPelsPerMeter = 0;
\r
4002 bih.biClrUsed = 0;
\r
4003 bih.biClrImportant = 0;
\r
4004 // fprintf(diagFile, "t=%d\nw=%d\nh=%d\nB=%d\nP=%d\nX=%d\n",
\r
4005 // b.bmType, b.bmWidth, b.bmHeight, b.bmWidthBytes, b.bmPlanes, b.bmBitsPixel);
\r
4006 GetDIBits(tmphdc,bufferBitmap,0,b.bmHeight,pData,(BITMAPINFO*)&bih,DIB_RGB_COLORS);
\r
4007 // fprintf(diagFile, "%8x\n", (int) pData);
\r
4009 wb = b.bmWidthBytes;
\r
4011 for(i=0; i<wb*(b.bmHeight - boardRect.top + OUTER_MARGIN)>>2; i++) {
\r
4012 int k = ((int*) pData)[i];
\r
4013 for(j=0; j<nrColors; j++) if(color[j] == k) break;
\r
4014 if(j >= 16) break;
\r
4016 if(j >= nrColors) nrColors = j+1;
\r
4018 if(j<16) { // 16 colors is enough. Compress to 4 bits per pixel
\r
4020 for(i=0; i<b.bmHeight - boardRect.top + OUTER_MARGIN; i++) {
\r
4021 for(w=0; w<(wb>>2); w+=2) {
\r
4022 int k = ((int*) pData)[(wb*i>>2) + w];
\r
4023 for(j=0; j<nrColors; j++) if(color[j] == k) break;
\r
4024 k = ((int*) pData)[(wb*i>>2) + w + 1];
\r
4025 for(m=0; m<nrColors; m++) if(color[m] == k) break;
\r
4026 pData[p++] = m | j<<4;
\r
4028 while(p&3) pData[p++] = 0;
\r
4031 wb = ((wb+31)>>5)<<2;
\r
4033 // write BITMAPFILEHEADER
\r
4034 fprintf(diagFile, "BM");
\r
4035 fputDW(diagFile, wb*(b.bmHeight - boardRect.top + OUTER_MARGIN)+0x36 + (fac?64:0));
\r
4036 fputDW(diagFile, 0);
\r
4037 fputDW(diagFile, 0x36 + (fac?64:0));
\r
4038 // write BITMAPINFOHEADER
\r
4039 fputDW(diagFile, 40);
\r
4040 fputDW(diagFile, b.bmWidth);
\r
4041 fputDW(diagFile, b.bmHeight - boardRect.top + OUTER_MARGIN);
\r
4042 if(fac) fputDW(diagFile, 0x040001); // planes and bits/pixel
\r
4043 else fputDW(diagFile, 0x200001); // planes and bits/pixel
\r
4044 fputDW(diagFile, 0);
\r
4045 fputDW(diagFile, 0);
\r
4046 fputDW(diagFile, 0);
\r
4047 fputDW(diagFile, 0);
\r
4048 fputDW(diagFile, 0);
\r
4049 fputDW(diagFile, 0);
\r
4050 // write color table
\r
4052 for(i=0; i<16; i++) fputDW(diagFile, color[i]);
\r
4053 // write bitmap data
\r
4054 for(i=0; i<wb*(b.bmHeight - boardRect.top + OUTER_MARGIN); i++)
\r
4055 fputc(pData[i], diagFile);
\r
4060 SelectObject(tmphdc, oldBitmap);
\r
4062 /* Massive cleanup */
\r
4063 for (x = 0; x < num_clips; x++)
\r
4064 DeleteObject(clips[x]);
\r
4067 DeleteObject(bufferBitmap);
\r
4070 ReleaseDC(hwndMain, hdc);
\r
4072 if (lastDrawnFlipView != flipView && nr == 0) {
\r
4074 CheckMenuItem(GetMenu(hwndMain),IDM_FlipView, MF_BYCOMMAND|MF_CHECKED);
\r
4076 CheckMenuItem(GetMenu(hwndMain),IDM_FlipView, MF_BYCOMMAND|MF_UNCHECKED);
\r
4079 /* CopyBoard(lastDrawn, board);*/
\r
4080 lastDrawnHighlight = highlightInfo;
\r
4081 lastDrawnPremove = premoveHighlightInfo;
\r
4082 lastDrawnFlipView = flipView;
\r
4083 lastDrawnValid[nr] = 1;
\r
4086 /* [HGM] diag: Save the current board display to the given open file and close the file */
\r
4091 saveDiagFlag = 1; diagFile = f;
\r
4092 HDCDrawPosition(NULL, TRUE, NULL);
\r
4100 /*---------------------------------------------------------------------------*\
\r
4101 | CLIENT PAINT PROCEDURE
\r
4102 | This is the main event-handler for the WM_PAINT message.
\r
4104 \*---------------------------------------------------------------------------*/
\r
4106 PaintProc(HWND hwnd)
\r
4112 if((hdc = BeginPaint(hwnd, &ps))) {
\r
4113 if (IsIconic(hwnd)) {
\r
4114 DrawIcon(hdc, 2, 2, iconCurrent);
\r
4116 if (!appData.monoMode) {
\r
4117 SelectPalette(hdc, hPal, FALSE);
\r
4118 RealizePalette(hdc);
\r
4120 HDCDrawPosition(hdc, 1, NULL);
\r
4121 if(twoBoards) { // [HGM] dual: also redraw other board in other orientation
\r
4122 flipView = !flipView; partnerUp = !partnerUp;
\r
4123 HDCDrawPosition(hdc, 1, NULL);
\r
4124 flipView = !flipView; partnerUp = !partnerUp;
\r
4127 SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);
\r
4128 ExtTextOut(hdc, messageRect.left, messageRect.top,
\r
4129 ETO_CLIPPED|ETO_OPAQUE,
\r
4130 &messageRect, messageText, strlen(messageText), NULL);
\r
4131 SelectObject(hdc, oldFont);
\r
4132 DisplayBothClocks();
\r
4135 EndPaint(hwnd,&ps);
\r
4143 * If the user selects on a border boundary, return -1; if off the board,
\r
4144 * return -2. Otherwise map the event coordinate to the square.
\r
4145 * The offset boardRect.left or boardRect.top must already have been
\r
4146 * subtracted from x.
\r
4148 int EventToSquare(x, limit)
\r
4153 if (x < lineGap + border)
\r
4155 x -= lineGap + border;
\r
4156 if ((x % (squareSize + lineGap)) >= squareSize)
\r
4158 x /= (squareSize + lineGap);
\r
4170 DropEnable dropEnables[] = {
\r
4171 { 'P', DP_Pawn, N_("Pawn") },
\r
4172 { 'N', DP_Knight, N_("Knight") },
\r
4173 { 'B', DP_Bishop, N_("Bishop") },
\r
4174 { 'R', DP_Rook, N_("Rook") },
\r
4175 { 'Q', DP_Queen, N_("Queen") },
\r
4179 SetupDropMenu(HMENU hmenu)
\r
4181 int i, count, enable;
\r
4183 extern char white_holding[], black_holding[];
\r
4184 char item[MSG_SIZ];
\r
4186 for (i=0; i<sizeof(dropEnables)/sizeof(DropEnable); i++) {
\r
4187 p = strchr(gameMode == IcsPlayingWhite ? white_holding : black_holding,
\r
4188 dropEnables[i].piece);
\r
4190 while (p && *p++ == dropEnables[i].piece) count++;
\r
4191 snprintf(item, MSG_SIZ, "%s %d", T_(dropEnables[i].name), count);
\r
4192 enable = count > 0 || !appData.testLegality
\r
4193 /*!!temp:*/ || (gameInfo.variant == VariantCrazyhouse
\r
4194 && !appData.icsActive);
\r
4195 ModifyMenu(hmenu, dropEnables[i].command,
\r
4196 MF_BYCOMMAND | (enable ? MF_ENABLED : MF_GRAYED) | MF_STRING,
\r
4197 dropEnables[i].command, item);
\r
4201 void DragPieceBegin(int x, int y, Boolean instantly)
\r
4203 dragInfo.lastpos.x = boardRect.left + x;
\r
4204 dragInfo.lastpos.y = boardRect.top + y;
\r
4205 if(instantly) dragInfo.pos = dragInfo.lastpos;
\r
4206 dragInfo.from.x = fromX;
\r
4207 dragInfo.from.y = fromY;
\r
4208 dragInfo.piece = boards[currentMove][fromY][fromX];
\r
4209 dragInfo.start = dragInfo.from;
\r
4210 SetCapture(hwndMain);
\r
4213 void DragPieceEnd(int x, int y)
\r
4216 dragInfo.start.x = dragInfo.start.y = -1;
\r
4217 dragInfo.from = dragInfo.start;
\r
4218 dragInfo.pos = dragInfo.lastpos = dragInfo.start;
\r
4221 void ChangeDragPiece(ChessSquare piece)
\r
4223 dragInfo.piece = piece;
\r
4226 /* Event handler for mouse messages */
\r
4228 MouseEvent(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
\r
4232 static int recursive = 0;
\r
4234 BOOLEAN forceFullRepaint = IsFullRepaintPreferrable(); /* [AS] */
\r
4237 if (message == WM_MBUTTONUP) {
\r
4238 /* Hideous kludge to fool TrackPopupMenu into paying attention
\r
4239 to the middle button: we simulate pressing the left button too!
\r
4241 PostMessage(hwnd, WM_LBUTTONDOWN, wParam, lParam);
\r
4242 PostMessage(hwnd, WM_LBUTTONUP, wParam, lParam);
\r
4248 pt.x = LOWORD(lParam);
\r
4249 pt.y = HIWORD(lParam);
\r
4250 x = EventToSquare(pt.x - boardRect.left, BOARD_WIDTH);
\r
4251 y = EventToSquare(pt.y - boardRect.top, BOARD_HEIGHT);
\r
4252 if (!flipView && y >= 0) {
\r
4253 y = BOARD_HEIGHT - 1 - y;
\r
4255 if (flipView && x >= 0) {
\r
4256 x = BOARD_WIDTH - 1 - x;
\r
4259 shiftKey = GetKeyState(VK_SHIFT) < 0; // [HGM] remember last shift status
\r
4261 switch (message) {
\r
4262 case WM_LBUTTONDOWN:
\r
4263 if (PtInRect((LPRECT) &whiteRect, pt)) {
\r
4264 ClockClick(flipClock); break;
\r
4265 } else if (PtInRect((LPRECT) &blackRect, pt)) {
\r
4266 ClockClick(!flipClock); break;
\r
4268 dragInfo.start.x = dragInfo.start.y = -1;
\r
4269 dragInfo.from = dragInfo.start;
\r
4270 if(fromX == -1 && frozen) { // not sure where this is for
\r
4271 fromX = fromY = -1;
\r
4272 DrawPosition(forceFullRepaint || FALSE, NULL); /* [AS] */
\r
4275 LeftClick(Press, pt.x - boardRect.left, pt.y - boardRect.top);
\r
4276 DrawPosition(TRUE, NULL);
\r
4279 case WM_LBUTTONUP:
\r
4280 LeftClick(Release, pt.x - boardRect.left, pt.y - boardRect.top);
\r
4281 DrawPosition(TRUE, NULL);
\r
4284 case WM_MOUSEMOVE:
\r
4285 if(SeekGraphClick(Press, pt.x - boardRect.left, pt.y - boardRect.top, 1)) break;
\r
4286 if(PromoScroll(pt.x - boardRect.left, pt.y - boardRect.top)) break;
\r
4287 MovePV(pt.x - boardRect.left, pt.y - boardRect.top, boardRect.bottom - boardRect.top);
\r
4288 if ((appData.animateDragging || appData.highlightDragging)
\r
4289 && (wParam & MK_LBUTTON)
\r
4290 && dragInfo.from.x >= 0)
\r
4292 BOOL full_repaint = FALSE;
\r
4294 if (appData.animateDragging) {
\r
4295 dragInfo.pos = pt;
\r
4297 if (appData.highlightDragging) {
\r
4298 SetHighlights(fromX, fromY, x, y);
\r
4299 if( IsDrawArrowEnabled() && (x < 0 || x >= BOARD_WIDTH || y < 0 || y >= BOARD_HEIGHT) ) {
\r
4300 full_repaint = TRUE;
\r
4304 DrawPosition( full_repaint, NULL);
\r
4306 dragInfo.lastpos = dragInfo.pos;
\r
4310 case WM_MOUSEWHEEL: // [DM]
\r
4311 { static int lastDir = 0; // [HGM] build in some hysteresis to avoid spurious events
\r
4312 /* Mouse Wheel is being rolled forward
\r
4313 * Play moves forward
\r
4315 if((short)HIWORD(wParam) < 0 && currentMove < forwardMostMove)
\r
4316 { if(lastDir == 1) ForwardEvent(); else lastDir = 1; } // [HGM] suppress first event in direction
\r
4317 /* Mouse Wheel is being rolled backward
\r
4318 * Play moves backward
\r
4320 if((short)HIWORD(wParam) > 0 && currentMove > backwardMostMove)
\r
4321 { if(lastDir == -1) BackwardEvent(); else lastDir = -1; }
\r
4325 case WM_MBUTTONUP:
\r
4326 case WM_RBUTTONUP:
\r
4328 RightClick(Release, pt.x - boardRect.left, pt.y - boardRect.top, &fromX, &fromY);
\r
4331 case WM_MBUTTONDOWN:
\r
4332 case WM_RBUTTONDOWN:
\r
4335 fromX = fromY = -1;
\r
4336 dragInfo.pos.x = dragInfo.pos.y = -1;
\r
4337 dragInfo.start.x = dragInfo.start.y = -1;
\r
4338 dragInfo.from = dragInfo.start;
\r
4339 dragInfo.lastpos = dragInfo.pos;
\r
4340 if (appData.highlightDragging) {
\r
4341 ClearHighlights();
\r
4344 /* [HGM] right mouse button in clock area edit-game mode ups clock */
\r
4345 if (PtInRect((LPRECT) &whiteRect, pt)) {
\r
4346 if (GetKeyState(VK_SHIFT) < 0) AdjustClock(flipClock, 1);
\r
4347 } else if (PtInRect((LPRECT) &blackRect, pt)) {
\r
4348 if (GetKeyState(VK_SHIFT) < 0) AdjustClock(!flipClock, 1);
\r
4352 DrawPosition(TRUE, NULL);
\r
4354 menuNr = RightClick(Press, pt.x - boardRect.left, pt.y - boardRect.top, &fromX, &fromY);
\r
4357 if (message == WM_MBUTTONDOWN) {
\r
4358 buttonCount = 3; /* even if system didn't think so */
\r
4359 if (wParam & MK_SHIFT)
\r
4360 MenuPopup(hwnd, pt, LoadMenu(hInst, "BlackPieceMenu"), -1);
\r
4362 MenuPopup(hwnd, pt, LoadMenu(hInst, "WhitePieceMenu"), -1);
\r
4363 } else { /* message == WM_RBUTTONDOWN */
\r
4364 /* Just have one menu, on the right button. Windows users don't
\r
4365 think to try the middle one, and sometimes other software steals
\r
4366 it, or it doesn't really exist. */
\r
4367 if(gameInfo.variant != VariantShogi)
\r
4368 MenuPopup(hwnd, pt, LoadMenu(hInst, "PieceMenu"), -1);
\r
4370 MenuPopup(hwnd, pt, LoadMenu(hInst, "ShogiPieceMenu"), -1);
\r
4374 SetCapture(hwndMain);
\r
4377 hmenu = LoadMenu(hInst, "DropPieceMenu");
\r
4378 SetupDropMenu(hmenu);
\r
4379 MenuPopup(hwnd, pt, hmenu, -1);
\r
4389 /* Preprocess messages for buttons in main window */
\r
4391 ButtonProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
\r
4393 int id = GetWindowLongPtr(hwnd, GWLP_ID);
\r
4396 for (i=0; i<N_BUTTONS; i++) {
\r
4397 if (buttonDesc[i].id == id) break;
\r
4399 if (i == N_BUTTONS) return 0;
\r
4400 switch (message) {
\r
4405 dir = (wParam == VK_LEFT) ? -1 : 1;
\r
4406 SetFocus(buttonDesc[(i + dir + N_BUTTONS) % N_BUTTONS].hwnd);
\r
4413 SendMessage(hwndMain, WM_COMMAND, MAKEWPARAM(buttonDesc[i].id, 0), 0);
\r
4416 if (appData.icsActive && (isalpha((char)wParam) || wParam == '0')) {
\r
4417 // [HGM] movenum: only letters or leading zero should go to ICS input
\r
4418 HWND h = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
4419 if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);
\r
4421 SendMessage(h, WM_CHAR, wParam, lParam);
\r
4423 } else if (isalpha((char)wParam) || isdigit((char)wParam)){
\r
4424 TypeInEvent((char)wParam);
\r
4430 return CallWindowProc(buttonDesc[i].wndproc, hwnd, message, wParam, lParam);
\r
4433 /* Process messages for Promotion dialog box */
\r
4435 Promotion(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
4439 switch (message) {
\r
4440 case WM_INITDIALOG: /* message: initialize dialog box */
\r
4441 /* Center the dialog over the application window */
\r
4442 CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));
\r
4443 Translate(hDlg, DLG_PromotionKing);
\r
4444 ShowWindow(GetDlgItem(hDlg, PB_King),
\r
4445 (!appData.testLegality || gameInfo.variant == VariantSuicide ||
\r
4446 gameInfo.variant == VariantSpartan && !WhiteOnMove(currentMove) ||
\r
4447 gameInfo.variant == VariantGiveaway || gameInfo.variant == VariantSuper ) ?
\r
4448 SW_SHOW : SW_HIDE);
\r
4449 /* [HGM] Only allow C & A promotions if these pieces are defined */
\r
4450 ShowWindow(GetDlgItem(hDlg, PB_Archbishop),
\r
4451 ((PieceToChar(WhiteAngel) >= 'A' && WhiteOnMove(currentMove) &&
\r
4452 PieceToChar(WhiteAngel) != '~') ||
\r
4453 (PieceToChar(BlackAngel) >= 'A' && !WhiteOnMove(currentMove) &&
\r
4454 PieceToChar(BlackAngel) != '~') ) ?
\r
4455 SW_SHOW : SW_HIDE);
\r
4456 ShowWindow(GetDlgItem(hDlg, PB_Chancellor),
\r
4457 ((PieceToChar(WhiteMarshall) >= 'A' && WhiteOnMove(currentMove) &&
\r
4458 PieceToChar(WhiteMarshall) != '~') ||
\r
4459 (PieceToChar(BlackMarshall) >= 'A' && !WhiteOnMove(currentMove) &&
\r
4460 PieceToChar(BlackMarshall) != '~') ) ?
\r
4461 SW_SHOW : SW_HIDE);
\r
4462 /* [HGM] Hide B & R button in Shogi, use Q as promote, N as defer */
\r
4463 ShowWindow(GetDlgItem(hDlg, PB_Rook),
\r
4464 gameInfo.variant != VariantShogi ?
\r
4465 SW_SHOW : SW_HIDE);
\r
4466 ShowWindow(GetDlgItem(hDlg, PB_Bishop),
\r
4467 gameInfo.variant != VariantShogi ?
\r
4468 SW_SHOW : SW_HIDE);
\r
4469 if(gameInfo.variant == VariantShogi) {
\r
4470 SetDlgItemText(hDlg, PB_Queen, "YES");
\r
4471 SetDlgItemText(hDlg, PB_Knight, "NO");
\r
4472 SetWindowText(hDlg, "Promote?");
\r
4474 ShowWindow(GetDlgItem(hDlg, IDC_Centaur),
\r
4475 gameInfo.variant == VariantSuper ?
\r
4476 SW_SHOW : SW_HIDE);
\r
4479 case WM_COMMAND: /* message: received a command */
\r
4480 switch (LOWORD(wParam)) {
\r
4482 EndDialog(hDlg, TRUE); /* Exit the dialog */
\r
4483 ClearHighlights();
\r
4484 DrawPosition(FALSE, NULL);
\r
4487 promoChar = gameInfo.variant == VariantSuper ? PieceToChar(BlackSilver) : PieceToChar(BlackKing);
\r
4490 promoChar = gameInfo.variant == VariantShogi ? '+' : ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteQueen : BlackQueen));
\r
4493 promoChar = ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteRook : BlackRook));
\r
4494 if(gameInfo.variant == VariantSpartan && !WhiteOnMove(currentMove)) promoChar = PieceToChar(BlackDragon);
\r
4497 promoChar = ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteBishop : BlackBishop));
\r
4498 if(gameInfo.variant == VariantSpartan && !WhiteOnMove(currentMove)) promoChar = PieceToChar(BlackAlfil);
\r
4500 case PB_Chancellor:
\r
4501 promoChar = ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteMarshall : BlackMarshall));
\r
4503 case PB_Archbishop:
\r
4504 promoChar = ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteAngel : BlackAngel));
\r
4507 promoChar = gameInfo.variant == VariantShogi ? '=' : PieceToChar(WhiteOnMove(currentMove) ? WhiteKnight : BlackKnight);
\r
4512 if(promoChar == '.') return FALSE; // invalid piece chosen
\r
4513 EndDialog(hDlg, TRUE); /* Exit the dialog */
\r
4514 UserMoveEvent(fromX, fromY, toX, toY, promoChar);
\r
4515 fromX = fromY = -1;
\r
4516 if (!appData.highlightLastMove) {
\r
4517 ClearHighlights();
\r
4518 DrawPosition(FALSE, NULL);
\r
4525 /* Pop up promotion dialog */
\r
4527 PromotionPopup(HWND hwnd)
\r
4531 lpProc = MakeProcInstance((FARPROC)Promotion, hInst);
\r
4532 DialogBox(hInst, MAKEINTRESOURCE(DLG_PromotionKing),
\r
4533 hwnd, (DLGPROC)lpProc);
\r
4534 FreeProcInstance(lpProc);
\r
4540 DrawPosition(TRUE, NULL);
\r
4541 PromotionPopup(hwndMain);
\r
4545 LoadGameDialog(HWND hwnd, char* title)
\r
4549 char fileTitle[MSG_SIZ];
\r
4550 f = OpenFileDialog(hwnd, "rb", "",
\r
4551 appData.oldSaveStyle ? "gam" : "pgn",
\r
4553 title, &number, fileTitle, NULL);
\r
4555 cmailMsgLoaded = FALSE;
\r
4556 if (number == 0) {
\r
4557 int error = GameListBuild(f);
\r
4559 DisplayError(_("Cannot build game list"), error);
\r
4560 } else if (!ListEmpty(&gameList) &&
\r
4561 ((ListGame *) gameList.tailPred)->number > 1) {
\r
4562 GameListPopUp(f, fileTitle);
\r
4565 GameListDestroy();
\r
4568 LoadGame(f, number, fileTitle, FALSE);
\r
4572 int get_term_width()
\r
4577 HFONT hfont, hold_font;
\r
4582 hText = GetDlgItem(hwndConsole, OPT_ConsoleText);
\r
4586 // get the text metrics
\r
4587 hdc = GetDC(hText);
\r
4588 lf = font[boardSize][CONSOLE_FONT]->lf;
\r
4589 if (consoleCF.dwEffects & CFE_BOLD)
\r
4590 lf.lfWeight = FW_BOLD;
\r
4591 if (consoleCF.dwEffects & CFE_ITALIC)
\r
4592 lf.lfItalic = TRUE;
\r
4593 if (consoleCF.dwEffects & CFE_STRIKEOUT)
\r
4594 lf.lfStrikeOut = TRUE;
\r
4595 if (consoleCF.dwEffects & CFE_UNDERLINE)
\r
4596 lf.lfUnderline = TRUE;
\r
4597 hfont = CreateFontIndirect(&lf);
\r
4598 hold_font = SelectObject(hdc, hfont);
\r
4599 GetTextMetrics(hdc, &tm);
\r
4600 SelectObject(hdc, hold_font);
\r
4601 DeleteObject(hfont);
\r
4602 ReleaseDC(hText, hdc);
\r
4604 // get the rectangle
\r
4605 SendMessage(hText, EM_GETRECT, 0, (LPARAM)&rc);
\r
4607 return (rc.right-rc.left) / tm.tmAveCharWidth;
\r
4610 void UpdateICSWidth(HWND hText)
\r
4612 LONG old_width, new_width;
\r
4614 new_width = get_term_width(hText, FALSE);
\r
4615 old_width = GetWindowLongPtr(hText, GWLP_USERDATA);
\r
4616 if (new_width != old_width)
\r
4618 ics_update_width(new_width);
\r
4619 SetWindowLongPtr(hText, GWLP_USERDATA, new_width);
\r
4624 ChangedConsoleFont()
\r
4627 CHARRANGE tmpsel, sel;
\r
4628 MyFont *f = font[boardSize][CONSOLE_FONT];
\r
4629 HWND hText = GetDlgItem(hwndConsole, OPT_ConsoleText);
\r
4630 HWND hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
4633 cfmt.cbSize = sizeof(CHARFORMAT);
\r
4634 cfmt.dwMask = CFM_FACE|CFM_SIZE|CFM_CHARSET;
\r
4635 safeStrCpy(cfmt.szFaceName, font[boardSize][CONSOLE_FONT]->mfp.faceName,
\r
4636 sizeof(cfmt.szFaceName)/sizeof(cfmt.szFaceName[0]) );
\r
4637 /* yHeight is expressed in twips. A twip is 1/20 of a font's point
\r
4638 * size. This was undocumented in the version of MSVC++ that I had
\r
4639 * when I wrote the code, but is apparently documented now.
\r
4641 cfmt.yHeight = (int)(f->mfp.pointSize * 20.0 + 0.5);
\r
4642 cfmt.bCharSet = f->lf.lfCharSet;
\r
4643 cfmt.bPitchAndFamily = f->lf.lfPitchAndFamily;
\r
4644 SendMessage(hText, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &cfmt);
\r
4645 SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &cfmt);
\r
4646 /* Why are the following seemingly needed too? */
\r
4647 SendMessage(hText, EM_SETCHARFORMAT, SCF_DEFAULT, (LPARAM) &cfmt);
\r
4648 SendMessage(hInput, EM_SETCHARFORMAT, SCF_DEFAULT, (LPARAM) &cfmt);
\r
4649 SendMessage(hText, EM_EXGETSEL, 0, (LPARAM)&sel);
\r
4651 tmpsel.cpMax = -1; /*999999?*/
\r
4652 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&tmpsel);
\r
4653 SendMessage(hText, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cfmt);
\r
4654 /* Trying putting this here too. It still seems to tickle a RichEdit
\r
4655 * bug: sometimes RichEdit indents the first line of a paragraph too.
\r
4657 paraf.cbSize = sizeof(paraf);
\r
4658 paraf.dwMask = PFM_OFFSET | PFM_STARTINDENT;
\r
4659 paraf.dxStartIndent = 0;
\r
4660 paraf.dxOffset = WRAP_INDENT;
\r
4661 SendMessage(hText, EM_SETPARAFORMAT, 0, (LPARAM) ¶f);
\r
4662 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
4663 UpdateICSWidth(hText);
\r
4666 /*---------------------------------------------------------------------------*\
\r
4668 * Window Proc for main window
\r
4670 \*---------------------------------------------------------------------------*/
\r
4672 /* Process messages for main window, etc. */
\r
4674 WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
\r
4677 int wmId, wmEvent;
\r
4681 char fileTitle[MSG_SIZ];
\r
4682 char buf[MSG_SIZ];
\r
4683 static SnapData sd;
\r
4684 static int peek=0;
\r
4686 switch (message) {
\r
4688 case WM_PAINT: /* message: repaint portion of window */
\r
4692 case WM_ERASEBKGND:
\r
4693 if (IsIconic(hwnd)) {
\r
4694 /* Cheat; change the message */
\r
4695 return (DefWindowProc(hwnd, WM_ICONERASEBKGND, wParam, lParam));
\r
4697 return (DefWindowProc(hwnd, message, wParam, lParam));
\r
4701 case WM_LBUTTONDOWN:
\r
4702 case WM_MBUTTONDOWN:
\r
4703 case WM_RBUTTONDOWN:
\r
4704 case WM_LBUTTONUP:
\r
4705 case WM_MBUTTONUP:
\r
4706 case WM_RBUTTONUP:
\r
4707 case WM_MOUSEMOVE:
\r
4708 case WM_MOUSEWHEEL:
\r
4709 MouseEvent(hwnd, message, wParam, lParam);
\r
4713 if((char)wParam == '\b') {
\r
4714 ForwardEvent(); peek = 0;
\r
4717 JAWS_KBUP_NAVIGATION
\r
4722 if((char)wParam == '\b') {
\r
4723 if(!peek) BackwardEvent(), peek = 1;
\r
4726 JAWS_KBDOWN_NAVIGATION
\r
4732 JAWS_ALT_INTERCEPT
\r
4734 if (appData.icsActive && ((char)wParam == '\r' || (char)wParam > ' ' && !((char)wParam >= '1' && (char)wParam <= '9'))) {
\r
4735 // [HGM] movenum: for non-zero digits we always do type-in dialog
\r
4736 HWND h = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
4737 if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);
\r
4739 SendMessage(h, message, wParam, lParam);
\r
4740 } else if(lParam != KF_REPEAT) {
\r
4741 if (isalpha((char)wParam) || isdigit((char)wParam)) {
\r
4742 TypeInEvent((char)wParam);
\r
4743 } else if((char)wParam == 003) CopyGameToClipboard();
\r
4744 else if((char)wParam == 026) PasteGameOrFENFromClipboard();
\r
4749 case WM_PALETTECHANGED:
\r
4750 if (hwnd != (HWND)wParam && !appData.monoMode) {
\r
4752 HDC hdc = GetDC(hwndMain);
\r
4753 SelectPalette(hdc, hPal, TRUE);
\r
4754 nnew = RealizePalette(hdc);
\r
4756 paletteChanged = TRUE;
\r
4757 InvalidateRect(hwnd, &boardRect, FALSE);
\r
4759 ReleaseDC(hwnd, hdc);
\r
4763 case WM_QUERYNEWPALETTE:
\r
4764 if (!appData.monoMode /*&& paletteChanged*/) {
\r
4766 HDC hdc = GetDC(hwndMain);
\r
4767 paletteChanged = FALSE;
\r
4768 SelectPalette(hdc, hPal, FALSE);
\r
4769 nnew = RealizePalette(hdc);
\r
4771 InvalidateRect(hwnd, &boardRect, FALSE);
\r
4773 ReleaseDC(hwnd, hdc);
\r
4778 case WM_COMMAND: /* message: command from application menu */
\r
4779 wmId = LOWORD(wParam);
\r
4780 wmEvent = HIWORD(wParam);
\r
4785 SAY("new game enter a move to play against the computer with white");
\r
4788 case IDM_NewGameFRC:
\r
4789 if( NewGameFRC() == 0 ) {
\r
4794 case IDM_NewVariant:
\r
4795 NewVariantPopup(hwnd);
\r
4798 case IDM_LoadGame:
\r
4799 LoadGameDialog(hwnd, _("Load Game from File"));
\r
4802 case IDM_LoadNextGame:
\r
4806 case IDM_LoadPrevGame:
\r
4810 case IDM_ReloadGame:
\r
4814 case IDM_LoadPosition:
\r
4815 if (gameMode == AnalyzeMode || gameMode == AnalyzeFile) {
\r
4816 Reset(FALSE, TRUE);
\r
4819 f = OpenFileDialog(hwnd, "rb", "",
\r
4820 appData.oldSaveStyle ? "pos" : "fen",
\r
4822 _("Load Position from File"), &number, fileTitle, NULL);
\r
4824 LoadPosition(f, number, fileTitle);
\r
4828 case IDM_LoadNextPosition:
\r
4829 ReloadPosition(1);
\r
4832 case IDM_LoadPrevPosition:
\r
4833 ReloadPosition(-1);
\r
4836 case IDM_ReloadPosition:
\r
4837 ReloadPosition(0);
\r
4840 case IDM_SaveGame:
\r
4841 defName = DefaultFileName(appData.oldSaveStyle ? "gam" : "pgn");
\r
4842 f = OpenFileDialog(hwnd, "a", defName,
\r
4843 appData.oldSaveStyle ? "gam" : "pgn",
\r
4845 _("Save Game to File"), NULL, fileTitle, NULL);
\r
4847 SaveGame(f, 0, "");
\r
4851 case IDM_SavePosition:
\r
4852 defName = DefaultFileName(appData.oldSaveStyle ? "pos" : "fen");
\r
4853 f = OpenFileDialog(hwnd, "a", defName,
\r
4854 appData.oldSaveStyle ? "pos" : "fen",
\r
4856 _("Save Position to File"), NULL, fileTitle, NULL);
\r
4858 SavePosition(f, 0, "");
\r
4862 case IDM_SaveDiagram:
\r
4863 defName = "diagram";
\r
4864 f = OpenFileDialog(hwnd, "wb", defName,
\r
4867 _("Save Diagram to File"), NULL, fileTitle, NULL);
\r
4873 case IDM_CreateBook:
\r
4874 CreateBookEvent();
\r
4877 case IDM_CopyGame:
\r
4878 CopyGameToClipboard();
\r
4881 case IDM_PasteGame:
\r
4882 PasteGameFromClipboard();
\r
4885 case IDM_CopyGameListToClipboard:
\r
4886 CopyGameListToClipboard();
\r
4889 /* [AS] Autodetect FEN or PGN data */
\r
4890 case IDM_PasteAny:
\r
4891 PasteGameOrFENFromClipboard();
\r
4894 /* [AS] Move history */
\r
4895 case IDM_ShowMoveHistory:
\r
4896 if( MoveHistoryIsUp() ) {
\r
4897 MoveHistoryPopDown();
\r
4900 MoveHistoryPopUp();
\r
4904 /* [AS] Eval graph */
\r
4905 case IDM_ShowEvalGraph:
\r
4906 if( EvalGraphIsUp() ) {
\r
4907 EvalGraphPopDown();
\r
4911 SetFocus(hwndMain);
\r
4915 /* [AS] Engine output */
\r
4916 case IDM_ShowEngineOutput:
\r
4917 if( EngineOutputIsUp() ) {
\r
4918 EngineOutputPopDown();
\r
4921 EngineOutputPopUp();
\r
4925 /* [AS] User adjudication */
\r
4926 case IDM_UserAdjudication_White:
\r
4927 UserAdjudicationEvent( +1 );
\r
4930 case IDM_UserAdjudication_Black:
\r
4931 UserAdjudicationEvent( -1 );
\r
4934 case IDM_UserAdjudication_Draw:
\r
4935 UserAdjudicationEvent( 0 );
\r
4938 /* [AS] Game list options dialog */
\r
4939 case IDM_GameListOptions:
\r
4940 GameListOptions();
\r
4947 case IDM_CopyPosition:
\r
4948 CopyFENToClipboard();
\r
4951 case IDM_PastePosition:
\r
4952 PasteFENFromClipboard();
\r
4955 case IDM_MailMove:
\r
4959 case IDM_ReloadCMailMsg:
\r
4960 Reset(TRUE, TRUE);
\r
4961 ReloadCmailMsgEvent(FALSE);
\r
4964 case IDM_Minimize:
\r
4965 ShowWindow(hwnd, SW_MINIMIZE);
\r
4972 case IDM_MachineWhite:
\r
4973 MachineWhiteEvent();
\r
4975 * refresh the tags dialog only if it's visible
\r
4977 if (gameMode == MachinePlaysWhite && IsWindowVisible(editTagsDialog)) {
\r
4979 tags = PGNTags(&gameInfo);
\r
4980 TagsPopUp(tags, CmailMsg());
\r
4983 SAY("computer starts playing white");
\r
4986 case IDM_MachineBlack:
\r
4987 MachineBlackEvent();
\r
4989 * refresh the tags dialog only if it's visible
\r
4991 if (gameMode == MachinePlaysBlack && IsWindowVisible(editTagsDialog)) {
\r
4993 tags = PGNTags(&gameInfo);
\r
4994 TagsPopUp(tags, CmailMsg());
\r
4997 SAY("computer starts playing black");
\r
5000 case IDM_Match: // [HGM] match: flows into next case, after setting Match Mode and nr of Games
\r
5001 MatchEvent(2); // distinguish from command-line-triggered case (matchMode=1)
\r
5004 case IDM_TwoMachines:
\r
5005 TwoMachinesEvent();
\r
5007 * refresh the tags dialog only if it's visible
\r
5009 if (gameMode == TwoMachinesPlay && IsWindowVisible(editTagsDialog)) {
\r
5011 tags = PGNTags(&gameInfo);
\r
5012 TagsPopUp(tags, CmailMsg());
\r
5015 SAY("computer starts playing both sides");
\r
5018 case IDM_AnalysisMode:
\r
5019 if(AnalyzeModeEvent()) {
\r
5020 SAY("analyzing current position");
\r
5024 case IDM_AnalyzeFile:
\r
5025 AnalyzeFileEvent();
\r
5028 case IDM_IcsClient:
\r
5032 case IDM_EditGame:
\r
5033 case IDM_EditGame2:
\r
5038 case IDM_EditPosition:
\r
5039 case IDM_EditPosition2:
\r
5040 EditPositionEvent();
\r
5041 SAY("enter a FEN string or setup a position on the board using the control R pop up menu");
\r
5044 case IDM_Training:
\r
5048 case IDM_ShowGameList:
\r
5049 ShowGameListProc();
\r
5052 case IDM_EditProgs1:
\r
5053 EditTagsPopUp(firstChessProgramNames, &firstChessProgramNames);
\r
5056 case IDM_LoadProg1:
\r
5057 LoadEnginePopUp(hwndMain, 0);
\r
5060 case IDM_LoadProg2:
\r
5061 LoadEnginePopUp(hwndMain, 1);
\r
5064 case IDM_EditServers:
\r
5065 EditTagsPopUp(icsNames, &icsNames);
\r
5068 case IDM_EditTags:
\r
5073 case IDM_EditBook:
\r
5077 case IDM_EditComment:
\r
5079 if (commentUp && editComment) {
\r
5082 EditCommentEvent();
\r
5102 case IDM_CallFlag:
\r
5122 case IDM_StopObserving:
\r
5123 StopObservingEvent();
\r
5126 case IDM_StopExamining:
\r
5127 StopExaminingEvent();
\r
5131 UploadGameEvent();
\r
5134 case IDM_TypeInMove:
\r
5135 TypeInEvent('\000');
\r
5138 case IDM_TypeInName:
\r
5139 PopUpNameDialog('\000');
\r
5142 case IDM_Backward:
\r
5144 SetFocus(hwndMain);
\r
5151 SetFocus(hwndMain);
\r
5156 SetFocus(hwndMain);
\r
5161 SetFocus(hwndMain);
\r
5164 case OPT_GameListNext: // [HGM] forward these two accelerators to Game List
\r
5165 case OPT_GameListPrev:
\r
5166 if(gameListDialog) SendMessage(gameListDialog, WM_COMMAND, wmId, 0);
\r
5170 RevertEvent(FALSE);
\r
5173 case IDM_Annotate: // [HGM] vari: revert with annotation
\r
5174 RevertEvent(TRUE);
\r
5177 case IDM_TruncateGame:
\r
5178 TruncateGameEvent();
\r
5185 case IDM_RetractMove:
\r
5186 RetractMoveEvent();
\r
5189 case IDM_FlipView:
\r
5190 flipView = !flipView;
\r
5191 DrawPosition(FALSE, NULL);
\r
5194 case IDM_FlipClock:
\r
5195 flipClock = !flipClock;
\r
5196 DisplayBothClocks();
\r
5200 case IDM_MuteSounds:
\r
5201 mute = !mute; // [HGM] mute: keep track of global muting variable
\r
5202 CheckMenuItem(GetMenu(hwndMain),IDM_MuteSounds,
\r
5203 MF_BYCOMMAND|(mute?MF_CHECKED:MF_UNCHECKED));
\r
5206 case IDM_GeneralOptions:
\r
5207 GeneralOptionsPopup(hwnd);
\r
5208 DrawPosition(TRUE, NULL);
\r
5211 case IDM_BoardOptions:
\r
5212 BoardOptionsPopup(hwnd);
\r
5215 case IDM_ThemeOptions:
\r
5216 ThemeOptionsPopup(hwnd);
\r
5219 case IDM_EnginePlayOptions:
\r
5220 EnginePlayOptionsPopup(hwnd);
\r
5223 case IDM_Engine1Options:
\r
5224 EngineOptionsPopup(hwnd, &first);
\r
5227 case IDM_Engine2Options:
\r
5229 if(WaitForEngine(&second, SettingsMenuIfReady)) break;
\r
5230 EngineOptionsPopup(hwnd, &second);
\r
5233 case IDM_OptionsUCI:
\r
5234 UciOptionsPopup(hwnd);
\r
5238 TourneyPopup(hwnd);
\r
5241 case IDM_IcsOptions:
\r
5242 IcsOptionsPopup(hwnd);
\r
5246 FontsOptionsPopup(hwnd);
\r
5250 SoundOptionsPopup(hwnd);
\r
5253 case IDM_CommPort:
\r
5254 CommPortOptionsPopup(hwnd);
\r
5257 case IDM_LoadOptions:
\r
5258 LoadOptionsPopup(hwnd);
\r
5261 case IDM_SaveOptions:
\r
5262 SaveOptionsPopup(hwnd);
\r
5265 case IDM_TimeControl:
\r
5266 TimeControlOptionsPopup(hwnd);
\r
5269 case IDM_SaveSettings:
\r
5270 SaveSettings(settingsFileName);
\r
5273 case IDM_SaveSettingsOnExit:
\r
5274 saveSettingsOnExit = !saveSettingsOnExit;
\r
5275 (void) CheckMenuItem(GetMenu(hwndMain), IDM_SaveSettingsOnExit,
\r
5276 MF_BYCOMMAND|(saveSettingsOnExit ?
\r
5277 MF_CHECKED : MF_UNCHECKED));
\r
5288 case IDM_AboutGame:
\r
5293 appData.debugMode = !appData.debugMode;
\r
5294 if (appData.debugMode) {
\r
5295 char dir[MSG_SIZ];
\r
5296 GetCurrentDirectory(MSG_SIZ, dir);
\r
5297 SetCurrentDirectory(installDir);
\r
5298 debugFP = fopen(appData.nameOfDebugFile, "w");
\r
5299 SetCurrentDirectory(dir);
\r
5300 setbuf(debugFP, NULL);
\r
5307 case IDM_HELPCONTENTS:
\r
5308 if (!MyHelp (hwnd, "winboard.hlp", HELP_KEY,(DWORD)(LPSTR)"CONTENTS") &&
\r
5309 !HtmlHelp(hwnd, "winboard.chm", 0, 0) ) {
\r
5310 MessageBox (GetFocus(),
\r
5311 _("Unable to activate help"),
\r
5312 szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);
\r
5316 case IDM_HELPSEARCH:
\r
5317 if (!MyHelp (hwnd, "winboard.hlp", HELP_PARTIALKEY, (DWORD)(LPSTR)"") &&
\r
5318 !HtmlHelp(hwnd, "winboard.chm", 0, 0) ) {
\r
5319 MessageBox (GetFocus(),
\r
5320 _("Unable to activate help"),
\r
5321 szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);
\r
5325 case IDM_HELPHELP:
\r
5326 if(!WinHelp(hwnd, (LPSTR)NULL, HELP_HELPONHELP, 0)) {
\r
5327 MessageBox (GetFocus(),
\r
5328 _("Unable to activate help"),
\r
5329 szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);
\r
5334 lpProc = MakeProcInstance((FARPROC)About, hInst);
\r
5336 (gameInfo.event && strcmp(gameInfo.event, "Easter Egg Hunt") == 0) ?
\r
5337 "AboutBox2" : "AboutBox", hwnd, (DLGPROC)lpProc);
\r
5338 FreeProcInstance(lpProc);
\r
5341 case IDM_DirectCommand1:
\r
5342 AskQuestionEvent(_("Direct Command"),
\r
5343 _("Send to chess program:"), "", "1");
\r
5345 case IDM_DirectCommand2:
\r
5346 AskQuestionEvent(_("Direct Command"),
\r
5347 _("Send to second chess program:"), "", "2");
\r
5350 case EP_WhitePawn:
\r
5351 EditPositionMenuEvent(WhitePawn, fromX, fromY);
\r
5352 fromX = fromY = -1;
\r
5355 case EP_WhiteKnight:
\r
5356 EditPositionMenuEvent(WhiteKnight, fromX, fromY);
\r
5357 fromX = fromY = -1;
\r
5360 case EP_WhiteBishop:
\r
5361 EditPositionMenuEvent(WhiteBishop, fromX, fromY);
\r
5362 fromX = fromY = -1;
\r
5365 case EP_WhiteRook:
\r
5366 EditPositionMenuEvent(WhiteRook, fromX, fromY);
\r
5367 fromX = fromY = -1;
\r
5370 case EP_WhiteQueen:
\r
5371 EditPositionMenuEvent(WhiteQueen, fromX, fromY);
\r
5372 fromX = fromY = -1;
\r
5375 case EP_WhiteFerz:
\r
5376 EditPositionMenuEvent(WhiteFerz, fromX, fromY);
\r
5377 fromX = fromY = -1;
\r
5380 case EP_WhiteWazir:
\r
5381 EditPositionMenuEvent(WhiteWazir, fromX, fromY);
\r
5382 fromX = fromY = -1;
\r
5385 case EP_WhiteAlfil:
\r
5386 EditPositionMenuEvent(WhiteAlfil, fromX, fromY);
\r
5387 fromX = fromY = -1;
\r
5390 case EP_WhiteCannon:
\r
5391 EditPositionMenuEvent(WhiteCannon, fromX, fromY);
\r
5392 fromX = fromY = -1;
\r
5395 case EP_WhiteCardinal:
\r
5396 EditPositionMenuEvent(WhiteAngel, fromX, fromY);
\r
5397 fromX = fromY = -1;
\r
5400 case EP_WhiteMarshall:
\r
5401 EditPositionMenuEvent(WhiteMarshall, fromX, fromY);
\r
5402 fromX = fromY = -1;
\r
5405 case EP_WhiteKing:
\r
5406 EditPositionMenuEvent(WhiteKing, fromX, fromY);
\r
5407 fromX = fromY = -1;
\r
5410 case EP_BlackPawn:
\r
5411 EditPositionMenuEvent(BlackPawn, fromX, fromY);
\r
5412 fromX = fromY = -1;
\r
5415 case EP_BlackKnight:
\r
5416 EditPositionMenuEvent(BlackKnight, fromX, fromY);
\r
5417 fromX = fromY = -1;
\r
5420 case EP_BlackBishop:
\r
5421 EditPositionMenuEvent(BlackBishop, fromX, fromY);
\r
5422 fromX = fromY = -1;
\r
5425 case EP_BlackRook:
\r
5426 EditPositionMenuEvent(BlackRook, fromX, fromY);
\r
5427 fromX = fromY = -1;
\r
5430 case EP_BlackQueen:
\r
5431 EditPositionMenuEvent(BlackQueen, fromX, fromY);
\r
5432 fromX = fromY = -1;
\r
5435 case EP_BlackFerz:
\r
5436 EditPositionMenuEvent(BlackFerz, fromX, fromY);
\r
5437 fromX = fromY = -1;
\r
5440 case EP_BlackWazir:
\r
5441 EditPositionMenuEvent(BlackWazir, fromX, fromY);
\r
5442 fromX = fromY = -1;
\r
5445 case EP_BlackAlfil:
\r
5446 EditPositionMenuEvent(BlackAlfil, fromX, fromY);
\r
5447 fromX = fromY = -1;
\r
5450 case EP_BlackCannon:
\r
5451 EditPositionMenuEvent(BlackCannon, fromX, fromY);
\r
5452 fromX = fromY = -1;
\r
5455 case EP_BlackCardinal:
\r
5456 EditPositionMenuEvent(BlackAngel, fromX, fromY);
\r
5457 fromX = fromY = -1;
\r
5460 case EP_BlackMarshall:
\r
5461 EditPositionMenuEvent(BlackMarshall, fromX, fromY);
\r
5462 fromX = fromY = -1;
\r
5465 case EP_BlackKing:
\r
5466 EditPositionMenuEvent(BlackKing, fromX, fromY);
\r
5467 fromX = fromY = -1;
\r
5470 case EP_EmptySquare:
\r
5471 EditPositionMenuEvent(EmptySquare, fromX, fromY);
\r
5472 fromX = fromY = -1;
\r
5475 case EP_ClearBoard:
\r
5476 EditPositionMenuEvent(ClearBoard, fromX, fromY);
\r
5477 fromX = fromY = -1;
\r
5481 EditPositionMenuEvent(WhitePlay, fromX, fromY);
\r
5482 fromX = fromY = -1;
\r
5486 EditPositionMenuEvent(BlackPlay, fromX, fromY);
\r
5487 fromX = fromY = -1;
\r
5491 EditPositionMenuEvent(PromotePiece, fromX, fromY);
\r
5492 fromX = fromY = -1;
\r
5496 EditPositionMenuEvent(DemotePiece, fromX, fromY);
\r
5497 fromX = fromY = -1;
\r
5501 DropMenuEvent(WhitePawn, fromX, fromY);
\r
5502 fromX = fromY = -1;
\r
5506 DropMenuEvent(WhiteKnight, fromX, fromY);
\r
5507 fromX = fromY = -1;
\r
5511 DropMenuEvent(WhiteBishop, fromX, fromY);
\r
5512 fromX = fromY = -1;
\r
5516 DropMenuEvent(WhiteRook, fromX, fromY);
\r
5517 fromX = fromY = -1;
\r
5521 DropMenuEvent(WhiteQueen, fromX, fromY);
\r
5522 fromX = fromY = -1;
\r
5526 barbaric = 0; appData.language = "";
\r
5527 TranslateMenus(0);
\r
5528 CheckMenuItem(GetMenu(hwndMain), lastChecked, MF_BYCOMMAND|MF_UNCHECKED);
\r
5529 CheckMenuItem(GetMenu(hwndMain), IDM_English, MF_BYCOMMAND|MF_CHECKED);
\r
5530 lastChecked = wmId;
\r
5534 if(wmId >= IDM_RecentEngines && wmId < IDM_RecentEngines + appData.recentEngines)
\r
5535 RecentEngineEvent(wmId - IDM_RecentEngines);
\r
5537 if(wmId > IDM_English && wmId < IDM_English+20) {
\r
5538 LoadLanguageFile(languageFile[wmId - IDM_English - 1]);
\r
5539 TranslateMenus(0);
\r
5540 CheckMenuItem(GetMenu(hwndMain), lastChecked, MF_BYCOMMAND|MF_UNCHECKED);
\r
5541 CheckMenuItem(GetMenu(hwndMain), wmId, MF_BYCOMMAND|MF_CHECKED);
\r
5542 lastChecked = wmId;
\r
5545 return (DefWindowProc(hwnd, message, wParam, lParam));
\r
5551 case CLOCK_TIMER_ID:
\r
5552 KillTimer(hwnd, clockTimerEvent); /* Simulate one-shot timer as in X */
\r
5553 clockTimerEvent = 0;
\r
5554 DecrementClocks(); /* call into back end */
\r
5556 case LOAD_GAME_TIMER_ID:
\r
5557 KillTimer(hwnd, loadGameTimerEvent); /* Simulate one-shot timer as in X*/
\r
5558 loadGameTimerEvent = 0;
\r
5559 AutoPlayGameLoop(); /* call into back end */
\r
5561 case ANALYSIS_TIMER_ID:
\r
5562 if ((gameMode == AnalyzeMode || gameMode == AnalyzeFile
\r
5563 || appData.icsEngineAnalyze) && appData.periodicUpdates) {
\r
5564 AnalysisPeriodicEvent(0);
\r
5566 KillTimer(hwnd, analysisTimerEvent);
\r
5567 analysisTimerEvent = 0;
\r
5570 case DELAYED_TIMER_ID:
\r
5571 KillTimer(hwnd, delayedTimerEvent);
\r
5572 delayedTimerEvent = 0;
\r
5573 delayedTimerCallback();
\r
5578 case WM_USER_Input:
\r
5579 InputEvent(hwnd, message, wParam, lParam);
\r
5582 /* [AS] Also move "attached" child windows */
\r
5583 case WM_WINDOWPOSCHANGING:
\r
5585 if( hwnd == hwndMain && appData.useStickyWindows ) {
\r
5586 LPWINDOWPOS lpwp = (LPWINDOWPOS) lParam;
\r
5588 if( ((lpwp->flags & SWP_NOMOVE) == 0) && ((lpwp->flags & SWP_NOSIZE) != 0) ) {
\r
5589 /* Window is moving */
\r
5592 // GetWindowRect( hwnd, &rcMain ); //[HGM] sticky: in XP this returned new position, not old
\r
5593 rcMain.left = wpMain.x; // replace by these 4 lines to reconstruct old rect
\r
5594 rcMain.right = wpMain.x + wpMain.width;
\r
5595 rcMain.top = wpMain.y;
\r
5596 rcMain.bottom = wpMain.y + wpMain.height;
\r
5598 ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, moveHistoryDialog, &wpMoveHistory );
\r
5599 ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, evalGraphDialog, &wpEvalGraph );
\r
5600 ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, engineOutputDialog, &wpEngineOutput );
\r
5601 ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, gameListDialog, &wpGameList );
\r
5602 ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, hwndConsole, &wpConsole );
\r
5603 wpMain.x = lpwp->x;
\r
5604 wpMain.y = lpwp->y;
\r
5609 /* [AS] Snapping */
\r
5610 case WM_ENTERSIZEMOVE:
\r
5611 if(appData.debugMode) { fprintf(debugFP, "size-move\n"); }
\r
5612 if (hwnd == hwndMain) {
\r
5613 doingSizing = TRUE;
\r
5616 return OnEnterSizeMove( &sd, hwnd, wParam, lParam );
\r
5620 if(appData.debugMode) { fprintf(debugFP, "sizing\n"); }
\r
5621 if (hwnd == hwndMain) {
\r
5622 lastSizing = wParam;
\r
5627 if(appData.debugMode) { fprintf(debugFP, "moving\n"); }
\r
5628 return OnMoving( &sd, hwnd, wParam, lParam );
\r
5630 case WM_EXITSIZEMOVE:
\r
5631 if(appData.debugMode) { fprintf(debugFP, "exit size-move, size = %d\n", squareSize); }
\r
5632 if (hwnd == hwndMain) {
\r
5634 doingSizing = FALSE;
\r
5635 InvalidateRect(hwnd, &boardRect, FALSE);
\r
5636 GetClientRect(hwnd, &client);
\r
5637 ResizeBoard(client.right, client.bottom, lastSizing);
\r
5639 if(appData.debugMode) { fprintf(debugFP, "square size = %d\n", squareSize); }
\r
5641 return OnExitSizeMove( &sd, hwnd, wParam, lParam );
\r
5644 case WM_DESTROY: /* message: window being destroyed */
\r
5645 PostQuitMessage(0);
\r
5649 if (hwnd == hwndMain) {
\r
5654 default: /* Passes it on if unprocessed */
\r
5655 return (DefWindowProc(hwnd, message, wParam, lParam));
\r
5660 /*---------------------------------------------------------------------------*\
\r
5662 * Misc utility routines
\r
5664 \*---------------------------------------------------------------------------*/
\r
5667 * Decent random number generator, at least not as bad as Windows
\r
5668 * standard rand, which returns a value in the range 0 to 0x7fff.
\r
5670 unsigned int randstate;
\r
5675 randstate = randstate * 1664525 + 1013904223;
\r
5676 return (int) randstate & 0x7fffffff;
\r
5680 mysrandom(unsigned int seed)
\r
5687 * returns TRUE if user selects a different color, FALSE otherwise
\r
5691 ChangeColor(HWND hwnd, COLORREF *which)
\r
5693 static BOOL firstTime = TRUE;
\r
5694 static DWORD customColors[16];
\r
5696 COLORREF newcolor;
\r
5701 /* Make initial colors in use available as custom colors */
\r
5702 /* Should we put the compiled-in defaults here instead? */
\r
5704 customColors[i++] = lightSquareColor & 0xffffff;
\r
5705 customColors[i++] = darkSquareColor & 0xffffff;
\r
5706 customColors[i++] = whitePieceColor & 0xffffff;
\r
5707 customColors[i++] = blackPieceColor & 0xffffff;
\r
5708 customColors[i++] = highlightSquareColor & 0xffffff;
\r
5709 customColors[i++] = premoveHighlightColor & 0xffffff;
\r
5711 for (ccl = (ColorClass) 0; ccl < NColorClasses && i < 16; ccl++) {
\r
5712 customColors[i++] = textAttribs[ccl].color;
\r
5714 while (i < 16) customColors[i++] = RGB(255, 255, 255);
\r
5715 firstTime = FALSE;
\r
5718 cc.lStructSize = sizeof(cc);
\r
5719 cc.hwndOwner = hwnd;
\r
5720 cc.hInstance = NULL;
\r
5721 cc.rgbResult = (DWORD) (*which & 0xffffff);
\r
5722 cc.lpCustColors = (LPDWORD) customColors;
\r
5723 cc.Flags = CC_RGBINIT|CC_FULLOPEN;
\r
5725 if (!ChooseColor(&cc)) return FALSE;
\r
5727 newcolor = (COLORREF) (0x2000000 | cc.rgbResult);
\r
5728 if (newcolor == *which) return FALSE;
\r
5729 *which = newcolor;
\r
5733 InitDrawingColors();
\r
5734 InvalidateRect(hwnd, &boardRect, FALSE);
\r
5739 MyLoadSound(MySound *ms)
\r
5745 if (ms->data && ms->flag) free(ms->data);
\r
5748 switch (ms->name[0]) {
\r
5754 /* System sound from Control Panel. Don't preload here. */
\r
5758 if (ms->name[1] == NULLCHAR) {
\r
5759 /* "!" alone = silence */
\r
5762 /* Builtin wave resource. Error if not found. */
\r
5763 HANDLE h = FindResource(hInst, ms->name + 1, "WAVE");
\r
5764 if (h == NULL) break;
\r
5765 ms->data = (void *)LoadResource(hInst, h);
\r
5766 ms->flag = 0; // not maloced, so cannot be freed!
\r
5767 if (h == NULL) break;
\r
5772 /* .wav file. Error if not found. */
\r
5773 f = fopen(ms->name, "rb");
\r
5774 if (f == NULL) break;
\r
5775 if (fstat(fileno(f), &st) < 0) break;
\r
5776 ms->data = malloc(st.st_size);
\r
5778 if (fread(ms->data, st.st_size, 1, f) < 1) break;
\r
5784 char buf[MSG_SIZ];
\r
5785 snprintf(buf, MSG_SIZ, _("Error loading sound %s"), ms->name);
\r
5786 DisplayError(buf, GetLastError());
\r
5792 MyPlaySound(MySound *ms)
\r
5794 BOOLEAN ok = FALSE;
\r
5796 if(mute) return TRUE; // [HGM] mute: suppress all sound play when muted
\r
5797 switch (ms->name[0]) {
\r
5799 if(appData.debugMode) fprintf(debugFP, "silence\n");
\r
5804 /* System sound from Control Panel (deprecated feature).
\r
5805 "$" alone or an unset sound name gets default beep (still in use). */
\r
5806 if (ms->name[1]) {
\r
5807 ok = PlaySound(ms->name + 1, NULL, SND_ALIAS|SND_ASYNC);
\r
5809 if (!ok) ok = MessageBeep(MB_OK);
\r
5812 /* Builtin wave resource, or "!" alone for silence */
\r
5813 if (ms->name[1]) {
\r
5814 if (ms->data == NULL) return FALSE;
\r
5815 ok = PlaySound(ms->data, NULL, SND_MEMORY|SND_ASYNC);
\r
5821 /* .wav file. Error if not found. */
\r
5822 if (ms->data == NULL) return FALSE;
\r
5823 ok = PlaySound(ms->data, NULL, SND_MEMORY|SND_ASYNC);
\r
5826 /* Don't print an error: this can happen innocently if the sound driver
\r
5827 is busy; for instance, if another instance of WinBoard is playing
\r
5828 a sound at about the same time. */
\r
5834 OldOpenFileHook(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
5837 OPENFILENAME *ofn;
\r
5838 static UINT *number; /* gross that this is static */
\r
5840 switch (message) {
\r
5841 case WM_INITDIALOG: /* message: initialize dialog box */
\r
5842 /* Center the dialog over the application window */
\r
5843 ofn = (OPENFILENAME *) lParam;
\r
5844 if (ofn->Flags & OFN_ENABLETEMPLATE) {
\r
5845 number = (UINT *) ofn->lCustData;
\r
5846 SendMessage(GetDlgItem(hDlg, edt2), WM_SETTEXT, 0, (LPARAM) "");
\r
5850 CenterWindow (hDlg, GetWindow (hDlg, GW_OWNER));
\r
5851 Translate(hDlg, 1536);
\r
5852 return FALSE; /* Allow for further processing */
\r
5855 if ((LOWORD(wParam) == IDOK) && (number != NULL)) {
\r
5856 *number = GetDlgItemInt(hDlg, OPT_IndexNumberOld, &ok, FALSE);
\r
5858 return FALSE; /* Allow for further processing */
\r
5864 OpenFileHook(HWND hdlg, UINT uiMsg, WPARAM wParam, LPARAM lParam)
\r
5866 static UINT *number;
\r
5867 OPENFILENAME *ofname;
\r
5870 case WM_INITDIALOG:
\r
5871 Translate(hdlg, DLG_IndexNumber);
\r
5872 ofname = (OPENFILENAME *)lParam;
\r
5873 number = (UINT *)(ofname->lCustData);
\r
5876 ofnot = (OFNOTIFY *)lParam;
\r
5877 if (ofnot->hdr.code == CDN_FILEOK) {
\r
5878 *number = GetDlgItemInt(hdlg, OPT_IndexNumber, NULL, FALSE);
\r
5887 OpenFileDialog(HWND hwnd, char *write, char *defName, char *defExt, // [HGM] diag: type of 'write' now string
\r
5888 char *nameFilt, char *dlgTitle, UINT *number,
\r
5889 char fileTitle[MSG_SIZ], char fileName[MSG_SIZ])
\r
5891 OPENFILENAME openFileName;
\r
5892 char buf1[MSG_SIZ];
\r
5895 if (fileName == NULL) fileName = buf1;
\r
5896 if (defName == NULL) {
\r
5897 safeStrCpy(fileName, "*.", 3 );
\r
5898 strcat(fileName, defExt);
\r
5900 safeStrCpy(fileName, defName, MSG_SIZ );
\r
5902 if (fileTitle) safeStrCpy(fileTitle, "", MSG_SIZ );
\r
5903 if (number) *number = 0;
\r
5905 openFileName.lStructSize = sizeof(OPENFILENAME);
\r
5906 openFileName.hwndOwner = hwnd;
\r
5907 openFileName.hInstance = (HANDLE) hInst;
\r
5908 openFileName.lpstrFilter = nameFilt;
\r
5909 openFileName.lpstrCustomFilter = (LPSTR) NULL;
\r
5910 openFileName.nMaxCustFilter = 0L;
\r
5911 openFileName.nFilterIndex = 1L;
\r
5912 openFileName.lpstrFile = fileName;
\r
5913 openFileName.nMaxFile = MSG_SIZ;
\r
5914 openFileName.lpstrFileTitle = fileTitle;
\r
5915 openFileName.nMaxFileTitle = fileTitle ? MSG_SIZ : 0;
\r
5916 openFileName.lpstrInitialDir = NULL;
\r
5917 openFileName.lpstrTitle = dlgTitle;
\r
5918 openFileName.Flags = OFN_PATHMUSTEXIST | OFN_HIDEREADONLY
\r
5919 | (write[0] != 'r' ? 0 : OFN_FILEMUSTEXIST)
\r
5920 | (number ? OFN_ENABLETEMPLATE | OFN_ENABLEHOOK: 0)
\r
5921 | (oldDialog ? 0 : OFN_EXPLORER);
\r
5922 openFileName.nFileOffset = 0;
\r
5923 openFileName.nFileExtension = 0;
\r
5924 openFileName.lpstrDefExt = defExt;
\r
5925 openFileName.lCustData = (LONG) number;
\r
5926 openFileName.lpfnHook = oldDialog ?
\r
5927 (LPOFNHOOKPROC) OldOpenFileHook : (LPOFNHOOKPROC) OpenFileHook;
\r
5928 openFileName.lpTemplateName = (LPSTR)(oldDialog ? 1536 : DLG_IndexNumber);
\r
5930 if (write[0] != 'r' ? GetSaveFileName(&openFileName) :
\r
5931 GetOpenFileName(&openFileName)) {
\r
5932 /* open the file */
\r
5933 f = fopen(openFileName.lpstrFile, write);
\r
5935 MessageBox(hwnd, _("File open failed"), NULL,
\r
5936 MB_OK|MB_ICONEXCLAMATION);
\r
5940 int err = CommDlgExtendedError();
\r
5941 if (err != 0) DisplayError(_("Internal error in file dialog box"), err);
\r
5950 MenuPopup(HWND hwnd, POINT pt, HMENU hmenu, UINT def)
\r
5952 HMENU hmenuTrackPopup; /* floating pop-up menu */
\r
5955 * Get the first pop-up menu in the menu template. This is the
\r
5956 * menu that TrackPopupMenu displays.
\r
5958 hmenuTrackPopup = GetSubMenu(hmenu, 0);
\r
5959 TranslateOneMenu(10, hmenuTrackPopup);
\r
5961 SetMenuDefaultItem(hmenuTrackPopup, def, FALSE);
\r
5964 * TrackPopup uses screen coordinates, so convert the
\r
5965 * coordinates of the mouse click to screen coordinates.
\r
5967 ClientToScreen(hwnd, (LPPOINT) &pt);
\r
5969 /* Draw and track the floating pop-up menu. */
\r
5970 TrackPopupMenu(hmenuTrackPopup, TPM_CENTERALIGN | TPM_RIGHTBUTTON,
\r
5971 pt.x, pt.y, 0, hwnd, NULL);
\r
5973 /* Destroy the menu.*/
\r
5974 DestroyMenu(hmenu);
\r
5979 int sizeX, sizeY, newSizeX, newSizeY;
\r
5981 } ResizeEditPlusButtonsClosure;
\r
5984 ResizeEditPlusButtonsCallback(HWND hChild, LPARAM lparam)
\r
5986 ResizeEditPlusButtonsClosure *cl = (ResizeEditPlusButtonsClosure *)lparam;
\r
5990 if (hChild == cl->hText) return TRUE;
\r
5991 GetWindowRect(hChild, &rect); /* gives screen coords */
\r
5992 pt.x = rect.left + (cl->newSizeX - cl->sizeX)/2;
\r
5993 pt.y = rect.top + cl->newSizeY - cl->sizeY;
\r
5994 ScreenToClient(cl->hDlg, &pt);
\r
5995 cl->hdwp = DeferWindowPos(cl->hdwp, hChild, NULL,
\r
5996 pt.x, pt.y, rect.right - rect.left, rect.bottom - rect.top, SWP_NOZORDER);
\r
6000 /* Resize a dialog that has a (rich) edit field filling most of
\r
6001 the top, with a row of buttons below */
\r
6003 ResizeEditPlusButtons(HWND hDlg, HWND hText, int sizeX, int sizeY, int newSizeX, int newSizeY)
\r
6006 int newTextHeight, newTextWidth;
\r
6007 ResizeEditPlusButtonsClosure cl;
\r
6009 /*if (IsIconic(hDlg)) return;*/
\r
6010 if (newSizeX == sizeX && newSizeY == sizeY) return;
\r
6012 cl.hdwp = BeginDeferWindowPos(8);
\r
6014 GetWindowRect(hText, &rectText); /* gives screen coords */
\r
6015 newTextWidth = rectText.right - rectText.left + newSizeX - sizeX;
\r
6016 newTextHeight = rectText.bottom - rectText.top + newSizeY - sizeY;
\r
6017 if (newTextHeight < 0) {
\r
6018 newSizeY += -newTextHeight;
\r
6019 newTextHeight = 0;
\r
6021 cl.hdwp = DeferWindowPos(cl.hdwp, hText, NULL, 0, 0,
\r
6022 newTextWidth, newTextHeight, SWP_NOZORDER|SWP_NOMOVE);
\r
6028 cl.newSizeX = newSizeX;
\r
6029 cl.newSizeY = newSizeY;
\r
6030 EnumChildWindows(hDlg, ResizeEditPlusButtonsCallback, (LPARAM)&cl);
\r
6032 EndDeferWindowPos(cl.hdwp);
\r
6035 BOOL CenterWindowEx(HWND hwndChild, HWND hwndParent, int mode)
\r
6037 RECT rChild, rParent;
\r
6038 int wChild, hChild, wParent, hParent;
\r
6039 int wScreen, hScreen, xNew, yNew;
\r
6042 /* Get the Height and Width of the child window */
\r
6043 GetWindowRect (hwndChild, &rChild);
\r
6044 wChild = rChild.right - rChild.left;
\r
6045 hChild = rChild.bottom - rChild.top;
\r
6047 /* Get the Height and Width of the parent window */
\r
6048 GetWindowRect (hwndParent, &rParent);
\r
6049 wParent = rParent.right - rParent.left;
\r
6050 hParent = rParent.bottom - rParent.top;
\r
6052 /* Get the display limits */
\r
6053 hdc = GetDC (hwndChild);
\r
6054 wScreen = GetDeviceCaps (hdc, HORZRES);
\r
6055 hScreen = GetDeviceCaps (hdc, VERTRES);
\r
6056 ReleaseDC(hwndChild, hdc);
\r
6058 /* Calculate new X position, then adjust for screen */
\r
6059 xNew = rParent.left + ((wParent - wChild) /2);
\r
6062 } else if ((xNew+wChild) > wScreen) {
\r
6063 xNew = wScreen - wChild;
\r
6066 /* Calculate new Y position, then adjust for screen */
\r
6068 yNew = rParent.top + ((hParent - hChild) /2);
\r
6071 yNew = rParent.top + GetSystemMetrics( SM_CYCAPTION ) * 2 / 3;
\r
6076 } else if ((yNew+hChild) > hScreen) {
\r
6077 yNew = hScreen - hChild;
\r
6080 /* Set it, and return */
\r
6081 return SetWindowPos (hwndChild, NULL,
\r
6082 xNew, yNew, 0, 0, SWP_NOSIZE | SWP_NOZORDER);
\r
6085 /* Center one window over another */
\r
6086 BOOL CenterWindow (HWND hwndChild, HWND hwndParent)
\r
6088 return CenterWindowEx( hwndChild, hwndParent, 0 );
\r
6091 /*---------------------------------------------------------------------------*\
\r
6093 * Startup Dialog functions
\r
6095 \*---------------------------------------------------------------------------*/
\r
6097 InitComboStrings(HANDLE hwndCombo, char **cd)
\r
6099 SendMessage(hwndCombo, CB_RESETCONTENT, 0, 0);
\r
6101 while (*cd != NULL) {
\r
6102 SendMessage(hwndCombo, CB_ADDSTRING, 0, (LPARAM) T_(*cd));
\r
6108 InitComboStringsFromOption(HANDLE hwndCombo, char *str)
\r
6110 char buf1[MAX_ARG_LEN];
\r
6113 if (str[0] == '@') {
\r
6114 FILE* f = fopen(str + 1, "r");
\r
6116 DisplayFatalError(str + 1, errno, 2);
\r
6119 len = fread(buf1, 1, sizeof(buf1)-1, f);
\r
6121 buf1[len] = NULLCHAR;
\r
6125 SendMessage(hwndCombo, CB_RESETCONTENT, 0, 0);
\r
6128 char buf[MSG_SIZ];
\r
6129 char *end = strchr(str, '\n');
\r
6130 if (end == NULL) return;
\r
6131 memcpy(buf, str, end - str);
\r
6132 buf[end - str] = NULLCHAR;
\r
6133 SendMessage(hwndCombo, CB_ADDSTRING, 0, (LPARAM) buf);
\r
6139 SetStartupDialogEnables(HWND hDlg)
\r
6141 EnableWindow(GetDlgItem(hDlg, OPT_ChessEngineName),
\r
6142 IsDlgButtonChecked(hDlg, OPT_ChessEngine) ||
\r
6143 (appData.zippyPlay && IsDlgButtonChecked(hDlg, OPT_ChessServer)));
\r
6144 EnableWindow(GetDlgItem(hDlg, OPT_SecondChessEngineName),
\r
6145 IsDlgButtonChecked(hDlg, OPT_ChessEngine));
\r
6146 EnableWindow(GetDlgItem(hDlg, OPT_ChessServerName),
\r
6147 IsDlgButtonChecked(hDlg, OPT_ChessServer));
\r
6148 EnableWindow(GetDlgItem(hDlg, OPT_AdditionalOptions),
\r
6149 IsDlgButtonChecked(hDlg, OPT_AnyAdditional));
\r
6150 EnableWindow(GetDlgItem(hDlg, IDOK),
\r
6151 IsDlgButtonChecked(hDlg, OPT_ChessEngine) ||
\r
6152 IsDlgButtonChecked(hDlg, OPT_ChessServer) ||
\r
6153 IsDlgButtonChecked(hDlg, OPT_View));
\r
6157 QuoteForFilename(char *filename)
\r
6159 int dquote, space;
\r
6160 dquote = strchr(filename, '"') != NULL;
\r
6161 space = strchr(filename, ' ') != NULL;
\r
6162 if (dquote || space) {
\r
6174 InitEngineBox(HWND hDlg, HWND hwndCombo, char* nthcp, char* nthd, char* nthdir, char *nthnames)
\r
6176 char buf[MSG_SIZ];
\r
6179 InitComboStringsFromOption(hwndCombo, nthnames);
\r
6180 q = QuoteForFilename(nthcp);
\r
6181 snprintf(buf, MSG_SIZ, "%s%s%s", q, nthcp, q);
\r
6182 if (*nthdir != NULLCHAR) {
\r
6183 q = QuoteForFilename(nthdir);
\r
6184 snprintf(buf + strlen(buf), MSG_SIZ, " /%s=%s%s%s", nthd, q, nthdir, q);
\r
6186 if (*nthcp == NULLCHAR) {
\r
6187 SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) 0, (LPARAM) 0);
\r
6188 } else if (SendMessage(hwndCombo, CB_SELECTSTRING, (WPARAM) -1, (LPARAM) buf) == CB_ERR) {
\r
6189 SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) -1, (LPARAM) 0);
\r
6190 SendMessage(hwndCombo, WM_SETTEXT, (WPARAM) 0, (LPARAM) buf);
\r
6195 StartupDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
6197 char buf[MSG_SIZ];
\r
6201 switch (message) {
\r
6202 case WM_INITDIALOG:
\r
6203 /* Center the dialog */
\r
6204 CenterWindow (hDlg, GetDesktopWindow());
\r
6205 Translate(hDlg, DLG_Startup);
\r
6206 /* Initialize the dialog items */
\r
6207 InitEngineBox(hDlg, GetDlgItem(hDlg, OPT_ChessEngineName),
\r
6208 appData.firstChessProgram, "fd", appData.firstDirectory,
\r
6209 firstChessProgramNames);
\r
6210 InitEngineBox(hDlg, GetDlgItem(hDlg, OPT_SecondChessEngineName),
\r
6211 appData.secondChessProgram, singleList ? "fd" : "sd", appData.secondDirectory,
\r
6212 singleList ? firstChessProgramNames : secondChessProgramNames); //[HGM] single: use first list in second combo
\r
6213 hwndCombo = GetDlgItem(hDlg, OPT_ChessServerName);
\r
6214 InitComboStringsFromOption(hwndCombo, icsNames);
\r
6215 snprintf(buf, MSG_SIZ, "%s /icsport=%s", appData.icsHost, appData.icsPort);
\r
6216 if (*appData.icsHelper != NULLCHAR) {
\r
6217 char *q = QuoteForFilename(appData.icsHelper);
\r
6218 sprintf(buf + strlen(buf), " /icshelper=%s%s%s", q, appData.icsHelper, q);
\r
6220 if (*appData.icsHost == NULLCHAR) {
\r
6221 SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) 0, (LPARAM) 0);
\r
6222 /*SendMessage(hwndCombo, CB_SHOWDROPDOWN, (WPARAM) TRUE, (LPARAM) 0); !!too soon */
\r
6223 } else if (SendMessage(hwndCombo, CB_SELECTSTRING, (WPARAM) -1, (LPARAM) buf) == CB_ERR) {
\r
6224 SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) -1, (LPARAM) 0);
\r
6225 SendMessage(hwndCombo, WM_SETTEXT, (WPARAM) 0, (LPARAM) buf);
\r
6228 if (appData.icsActive) {
\r
6229 CheckDlgButton(hDlg, OPT_ChessServer, BST_CHECKED);
\r
6231 else if (appData.noChessProgram) {
\r
6232 CheckDlgButton(hDlg, OPT_View, BST_CHECKED);
\r
6235 CheckDlgButton(hDlg, OPT_ChessEngine, BST_CHECKED);
\r
6238 SetStartupDialogEnables(hDlg);
\r
6242 switch (LOWORD(wParam)) {
\r
6244 if (IsDlgButtonChecked(hDlg, OPT_ChessEngine)) {
\r
6245 safeStrCpy(buf, "/fcp=", sizeof(buf)/sizeof(buf[0]) );
\r
6246 GetDlgItemText(hDlg, OPT_ChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));
\r
6248 comboLine = strdup(p+5); // [HGM] recent: remember complete line of first combobox
\r
6249 ParseArgs(StringGet, &p);
\r
6250 safeStrCpy(buf, singleList ? "/fcp=" : "/scp=", sizeof(buf)/sizeof(buf[0]) );
\r
6251 GetDlgItemText(hDlg, OPT_SecondChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));
\r
6253 SwapEngines(singleList); // temporarily swap first and second, to load a second 'first', ...
\r
6254 ParseArgs(StringGet, &p);
\r
6255 SwapEngines(singleList); // ... and then make it 'second'
\r
6256 appData.noChessProgram = FALSE;
\r
6257 appData.icsActive = FALSE;
\r
6258 } else if (IsDlgButtonChecked(hDlg, OPT_ChessServer)) {
\r
6259 safeStrCpy(buf, "/ics /icshost=", sizeof(buf)/sizeof(buf[0]) );
\r
6260 GetDlgItemText(hDlg, OPT_ChessServerName, buf + strlen(buf), sizeof(buf) - strlen(buf));
\r
6262 ParseArgs(StringGet, &p);
\r
6263 if (appData.zippyPlay) {
\r
6264 safeStrCpy(buf, "/fcp=", sizeof(buf)/sizeof(buf[0]) );
\r
6265 GetDlgItemText(hDlg, OPT_ChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));
\r
6267 ParseArgs(StringGet, &p);
\r
6269 } else if (IsDlgButtonChecked(hDlg, OPT_View)) {
\r
6270 appData.noChessProgram = TRUE;
\r
6271 appData.icsActive = FALSE;
\r
6273 MessageBox(hDlg, _("Choose an option, or cancel to exit"),
\r
6274 _("Option Error"), MB_OK|MB_ICONEXCLAMATION);
\r
6277 if (IsDlgButtonChecked(hDlg, OPT_AnyAdditional)) {
\r
6278 GetDlgItemText(hDlg, OPT_AdditionalOptions, buf, sizeof(buf));
\r
6280 ParseArgs(StringGet, &p);
\r
6282 EndDialog(hDlg, TRUE);
\r
6289 case IDM_HELPCONTENTS:
\r
6290 if (!WinHelp (hDlg, "winboard.hlp", HELP_KEY,(DWORD)(LPSTR)"CONTENTS")) {
\r
6291 MessageBox (GetFocus(),
\r
6292 _("Unable to activate help"),
\r
6293 szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);
\r
6298 SetStartupDialogEnables(hDlg);
\r
6306 /*---------------------------------------------------------------------------*\
\r
6308 * About box dialog functions
\r
6310 \*---------------------------------------------------------------------------*/
\r
6312 /* Process messages for "About" dialog box */
\r
6314 About(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
6316 switch (message) {
\r
6317 case WM_INITDIALOG: /* message: initialize dialog box */
\r
6318 /* Center the dialog over the application window */
\r
6319 CenterWindow (hDlg, GetWindow (hDlg, GW_OWNER));
\r
6320 SetDlgItemText(hDlg, ABOUTBOX_Version, programVersion);
\r
6321 Translate(hDlg, ABOUTBOX);
\r
6325 case WM_COMMAND: /* message: received a command */
\r
6326 if (LOWORD(wParam) == IDOK /* "OK" box selected? */
\r
6327 || LOWORD(wParam) == IDCANCEL) { /* System menu close command? */
\r
6328 EndDialog(hDlg, TRUE); /* Exit the dialog */
\r
6336 /*---------------------------------------------------------------------------*\
\r
6338 * Comment Dialog functions
\r
6340 \*---------------------------------------------------------------------------*/
\r
6343 CommentDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
6345 static HANDLE hwndText = NULL;
\r
6346 int len, newSizeX, newSizeY, flags;
\r
6347 static int sizeX, sizeY;
\r
6352 switch (message) {
\r
6353 case WM_INITDIALOG: /* message: initialize dialog box */
\r
6354 /* Initialize the dialog items */
\r
6355 Translate(hDlg, DLG_EditComment);
\r
6356 hwndText = GetDlgItem(hDlg, OPT_CommentText);
\r
6357 SetDlgItemText(hDlg, OPT_CommentText, commentText);
\r
6358 EnableWindow(GetDlgItem(hDlg, OPT_CancelComment), editComment);
\r
6359 EnableWindow(GetDlgItem(hDlg, OPT_ClearComment), editComment);
\r
6360 EnableWindow(GetDlgItem(hDlg, OPT_EditComment), !editComment);
\r
6361 SendMessage(hwndText, EM_SETREADONLY, !editComment, 0);
\r
6362 SetWindowText(hDlg, commentTitle);
\r
6363 if (editComment) {
\r
6364 SetFocus(hwndText);
\r
6366 SetFocus(GetDlgItem(hDlg, IDOK));
\r
6368 SendMessage(GetDlgItem(hDlg, OPT_CommentText),
\r
6369 WM_SETFONT, (WPARAM)font[boardSize][COMMENT_FONT]->hf,
\r
6370 MAKELPARAM(FALSE, 0));
\r
6371 /* Size and position the dialog */
\r
6372 if (!commentDialog) {
\r
6373 commentDialog = hDlg;
\r
6374 flags = SWP_NOZORDER;
\r
6375 GetClientRect(hDlg, &rect);
\r
6376 sizeX = rect.right;
\r
6377 sizeY = rect.bottom;
\r
6378 if (wpComment.x != CW_USEDEFAULT && wpComment.y != CW_USEDEFAULT &&
\r
6379 wpComment.width != CW_USEDEFAULT && wpComment.height != CW_USEDEFAULT) {
\r
6380 WINDOWPLACEMENT wp;
\r
6381 EnsureOnScreen(&wpComment.x, &wpComment.y, 0, 0);
\r
6382 wp.length = sizeof(WINDOWPLACEMENT);
\r
6384 wp.showCmd = SW_SHOW;
\r
6385 wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;
\r
6386 wp.rcNormalPosition.left = wpComment.x;
\r
6387 wp.rcNormalPosition.right = wpComment.x + wpComment.width;
\r
6388 wp.rcNormalPosition.top = wpComment.y;
\r
6389 wp.rcNormalPosition.bottom = wpComment.y + wpComment.height;
\r
6390 SetWindowPlacement(hDlg, &wp);
\r
6392 GetClientRect(hDlg, &rect);
\r
6393 newSizeX = rect.right;
\r
6394 newSizeY = rect.bottom;
\r
6395 ResizeEditPlusButtons(hDlg, hwndText, sizeX, sizeY,
\r
6396 newSizeX, newSizeY);
\r
6401 SendDlgItemMessage( hDlg, OPT_CommentText, EM_SETEVENTMASK, 0, ENM_MOUSEEVENTS | ENM_KEYEVENTS );
\r
6404 case WM_COMMAND: /* message: received a command */
\r
6405 switch (LOWORD(wParam)) {
\r
6407 if (editComment) {
\r
6409 /* Read changed options from the dialog box */
\r
6410 hwndText = GetDlgItem(hDlg, OPT_CommentText);
\r
6411 len = GetWindowTextLength(hwndText);
\r
6412 str = (char *) malloc(len + 1);
\r
6413 GetWindowText(hwndText, str, len + 1);
\r
6422 ReplaceComment(commentIndex, str);
\r
6429 case OPT_CancelComment:
\r
6433 case OPT_ClearComment:
\r
6434 SetDlgItemText(hDlg, OPT_CommentText, "");
\r
6437 case OPT_EditComment:
\r
6438 EditCommentEvent();
\r
6446 case WM_NOTIFY: // [HGM] vari: cloned from whistory.c
\r
6447 if( wParam == OPT_CommentText ) {
\r
6448 MSGFILTER * lpMF = (MSGFILTER *) lParam;
\r
6450 if( lpMF->msg == WM_RBUTTONDOWN && (lpMF->wParam & (MK_CONTROL | MK_SHIFT)) == 0 ||
\r
6451 lpMF->msg == WM_CHAR && lpMF->wParam == '\022' ) {
\r
6455 pt.x = LOWORD( lpMF->lParam );
\r
6456 pt.y = HIWORD( lpMF->lParam );
\r
6458 if(lpMF->msg == WM_CHAR) {
\r
6460 SendDlgItemMessage( hDlg, OPT_CommentText, EM_EXGETSEL, 0, (LPARAM) &sel );
\r
6461 index = sel.cpMin;
\r
6463 index = SendDlgItemMessage( hDlg, OPT_CommentText, EM_CHARFROMPOS, 0, (LPARAM) &pt );
\r
6465 hwndText = GetDlgItem(hDlg, OPT_CommentText); // cloned from above
\r
6466 len = GetWindowTextLength(hwndText);
\r
6467 str = (char *) malloc(len + 1);
\r
6468 GetWindowText(hwndText, str, len + 1);
\r
6469 ReplaceComment(commentIndex, str);
\r
6470 if(commentIndex != currentMove) ToNrEvent(commentIndex);
\r
6471 LoadVariation( index, str ); // [HGM] also does the actual moving to it, now
\r
6474 /* Zap the message for good: apparently, returning non-zero is not enough */
\r
6475 lpMF->msg = WM_USER;
\r
6483 newSizeX = LOWORD(lParam);
\r
6484 newSizeY = HIWORD(lParam);
\r
6485 ResizeEditPlusButtons(hDlg, hwndText, sizeX, sizeY, newSizeX, newSizeY);
\r
6490 case WM_GETMINMAXINFO:
\r
6491 /* Prevent resizing window too small */
\r
6492 mmi = (MINMAXINFO *) lParam;
\r
6493 mmi->ptMinTrackSize.x = 100;
\r
6494 mmi->ptMinTrackSize.y = 100;
\r
6501 EitherCommentPopUp(int index, char *title, char *str, BOOLEAN edit)
\r
6506 CheckMenuItem(GetMenu(hwndMain), IDM_Comment, edit ? MF_CHECKED : MF_UNCHECKED);
\r
6508 if (str == NULL) str = "";
\r
6509 p = (char *) malloc(2 * strlen(str) + 2);
\r
6512 if (*str == '\n') *q++ = '\r';
\r
6516 if (commentText != NULL) free(commentText);
\r
6518 commentIndex = index;
\r
6519 commentTitle = title;
\r
6521 editComment = edit;
\r
6523 if (commentDialog) {
\r
6524 SendMessage(commentDialog, WM_INITDIALOG, 0, 0);
\r
6525 if (!commentUp) ShowWindow(commentDialog, SW_SHOW);
\r
6527 lpProc = MakeProcInstance((FARPROC)CommentDialog, hInst);
\r
6528 CreateDialog(hInst, MAKEINTRESOURCE(DLG_EditComment),
\r
6529 hwndMain, (DLGPROC)lpProc);
\r
6530 FreeProcInstance(lpProc);
\r
6536 /*---------------------------------------------------------------------------*\
\r
6538 * Type-in move dialog functions
\r
6540 \*---------------------------------------------------------------------------*/
\r
6543 TypeInMoveDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
6545 char move[MSG_SIZ];
\r
6548 switch (message) {
\r
6549 case WM_INITDIALOG:
\r
6550 move[0] = (char) lParam;
\r
6551 move[1] = NULLCHAR;
\r
6552 CenterWindowEx(hDlg, GetWindow(hDlg, GW_OWNER), 1 );
\r
6553 Translate(hDlg, DLG_TypeInMove);
\r
6554 hInput = GetDlgItem(hDlg, OPT_Move);
\r
6555 SetWindowText(hInput, move);
\r
6557 SendMessage(hInput, EM_SETSEL, (WPARAM)9999, (LPARAM)9999);
\r
6561 switch (LOWORD(wParam)) {
\r
6564 shiftKey = GetKeyState(VK_SHIFT) < 0; // [HGM] remember last shift status
\r
6565 GetDlgItemText(hDlg, OPT_Move, move, sizeof(move));
\r
6566 TypeInDoneEvent(move);
\r
6567 EndDialog(hDlg, TRUE);
\r
6570 EndDialog(hDlg, FALSE);
\r
6581 PopUpMoveDialog(char firstchar)
\r
6585 lpProc = MakeProcInstance((FARPROC)TypeInMoveDialog, hInst);
\r
6586 DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_TypeInMove),
\r
6587 hwndMain, (DLGPROC)lpProc, (LPARAM)firstchar);
\r
6588 FreeProcInstance(lpProc);
\r
6591 /*---------------------------------------------------------------------------*\
\r
6593 * Type-in name dialog functions
\r
6595 \*---------------------------------------------------------------------------*/
\r
6598 TypeInNameDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
6600 char move[MSG_SIZ];
\r
6603 switch (message) {
\r
6604 case WM_INITDIALOG:
\r
6605 move[0] = (char) lParam;
\r
6606 move[1] = NULLCHAR;
\r
6607 CenterWindowEx(hDlg, GetWindow(hDlg, GW_OWNER), 1 );
\r
6608 Translate(hDlg, DLG_TypeInName);
\r
6609 hInput = GetDlgItem(hDlg, OPT_Name);
\r
6610 SetWindowText(hInput, move);
\r
6612 SendMessage(hInput, EM_SETSEL, (WPARAM)9999, (LPARAM)9999);
\r
6616 switch (LOWORD(wParam)) {
\r
6618 GetDlgItemText(hDlg, OPT_Name, move, sizeof(move));
\r
6619 appData.userName = strdup(move);
\r
6622 if(gameMode == MachinePlaysWhite || gameMode == MachinePlaysBlack) {
\r
6623 snprintf(move, MSG_SIZ, "%s vs. %s", gameInfo.white, gameInfo.black);
\r
6624 DisplayTitle(move);
\r
6628 EndDialog(hDlg, TRUE);
\r
6631 EndDialog(hDlg, FALSE);
\r
6642 PopUpNameDialog(char firstchar)
\r
6646 lpProc = MakeProcInstance((FARPROC)TypeInNameDialog, hInst);
\r
6647 DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_TypeInName),
\r
6648 hwndMain, (DLGPROC)lpProc, (LPARAM)firstchar);
\r
6649 FreeProcInstance(lpProc);
\r
6652 /*---------------------------------------------------------------------------*\
\r
6656 \*---------------------------------------------------------------------------*/
\r
6658 /* Nonmodal error box */
\r
6659 LRESULT CALLBACK ErrorDialog(HWND hDlg, UINT message,
\r
6660 WPARAM wParam, LPARAM lParam);
\r
6663 ErrorPopUp(char *title, char *content)
\r
6667 BOOLEAN modal = hwndMain == NULL;
\r
6685 strncpy(errorTitle, title, sizeof(errorTitle));
\r
6686 errorTitle[sizeof(errorTitle) - 1] = '\0';
\r
6689 MessageBox(NULL, errorMessage, errorTitle, MB_OK|MB_ICONEXCLAMATION);
\r
6691 lpProc = MakeProcInstance((FARPROC)ErrorDialog, hInst);
\r
6692 CreateDialog(hInst, MAKEINTRESOURCE(DLG_Error),
\r
6693 hwndMain, (DLGPROC)lpProc);
\r
6694 FreeProcInstance(lpProc);
\r
6701 if (!appData.popupMoveErrors && moveErrorMessageUp) DisplayMessage("", "");
\r
6702 if (errorDialog == NULL) return;
\r
6703 DestroyWindow(errorDialog);
\r
6704 errorDialog = NULL;
\r
6705 if(errorExitStatus) ExitEvent(errorExitStatus);
\r
6709 ErrorDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
6714 switch (message) {
\r
6715 case WM_INITDIALOG:
\r
6716 GetWindowRect(hDlg, &rChild);
\r
6719 SetWindowPos(hDlg, NULL, rChild.left,
\r
6720 rChild.top + boardRect.top - (rChild.bottom - rChild.top),
\r
6721 0, 0, SWP_NOZORDER|SWP_NOSIZE);
\r
6725 [AS] It seems that the above code wants to move the dialog up in the "caption
\r
6726 area" of the main window, but it uses the dialog height as an hard-coded constant,
\r
6727 and it doesn't work when you resize the dialog.
\r
6728 For now, just give it a default position.
\r
6730 SetWindowPos(hDlg, NULL, boardRect.left+8, boardRect.top+8, 0, 0, SWP_NOZORDER|SWP_NOSIZE);
\r
6731 Translate(hDlg, DLG_Error);
\r
6733 errorDialog = hDlg;
\r
6734 SetWindowText(hDlg, errorTitle);
\r
6735 hwndText = GetDlgItem(hDlg, OPT_ErrorText);
\r
6736 SetDlgItemText(hDlg, OPT_ErrorText, errorMessage);
\r
6740 switch (LOWORD(wParam)) {
\r
6743 if (errorDialog == hDlg) errorDialog = NULL;
\r
6744 DestroyWindow(hDlg);
\r
6756 HWND gothicDialog = NULL;
\r
6759 GothicDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
6763 int height = GetSystemMetrics(SM_CYCAPTION)+GetSystemMetrics(SM_CYFRAME);
\r
6765 switch (message) {
\r
6766 case WM_INITDIALOG:
\r
6767 GetWindowRect(hDlg, &rChild);
\r
6769 SetWindowPos(hDlg, NULL, wpMain.x, wpMain.y-height, wpMain.width, height,
\r
6773 [AS] It seems that the above code wants to move the dialog up in the "caption
\r
6774 area" of the main window, but it uses the dialog height as an hard-coded constant,
\r
6775 and it doesn't work when you resize the dialog.
\r
6776 For now, just give it a default position.
\r
6778 gothicDialog = hDlg;
\r
6779 SetWindowText(hDlg, errorTitle);
\r
6780 hwndText = GetDlgItem(hDlg, OPT_ErrorText);
\r
6781 SetDlgItemText(hDlg, OPT_ErrorText, errorMessage);
\r
6785 switch (LOWORD(wParam)) {
\r
6788 if (errorDialog == hDlg) errorDialog = NULL;
\r
6789 DestroyWindow(hDlg);
\r
6801 GothicPopUp(char *title, VariantClass variant)
\r
6804 static char *lastTitle;
\r
6806 strncpy(errorTitle, title, sizeof(errorTitle));
\r
6807 errorTitle[sizeof(errorTitle) - 1] = '\0';
\r
6809 if(lastTitle != title && gothicDialog != NULL) {
\r
6810 DestroyWindow(gothicDialog);
\r
6811 gothicDialog = NULL;
\r
6813 if(variant != VariantNormal && gothicDialog == NULL) {
\r
6814 title = lastTitle;
\r
6815 lpProc = MakeProcInstance((FARPROC)GothicDialog, hInst);
\r
6816 CreateDialog(hInst, MAKEINTRESOURCE(DLG_Error),
\r
6817 hwndMain, (DLGPROC)lpProc);
\r
6818 FreeProcInstance(lpProc);
\r
6823 /*---------------------------------------------------------------------------*\
\r
6825 * Ics Interaction console functions
\r
6827 \*---------------------------------------------------------------------------*/
\r
6829 #define HISTORY_SIZE 64
\r
6830 static char *history[HISTORY_SIZE];
\r
6831 int histIn = 0, histP = 0;
\r
6834 SaveInHistory(char *cmd)
\r
6836 if (history[histIn] != NULL) {
\r
6837 free(history[histIn]);
\r
6838 history[histIn] = NULL;
\r
6840 if (*cmd == NULLCHAR) return;
\r
6841 history[histIn] = StrSave(cmd);
\r
6842 histIn = (histIn + 1) % HISTORY_SIZE;
\r
6843 if (history[histIn] != NULL) {
\r
6844 free(history[histIn]);
\r
6845 history[histIn] = NULL;
\r
6851 PrevInHistory(char *cmd)
\r
6854 if (histP == histIn) {
\r
6855 if (history[histIn] != NULL) free(history[histIn]);
\r
6856 history[histIn] = StrSave(cmd);
\r
6858 newhp = (histP - 1 + HISTORY_SIZE) % HISTORY_SIZE;
\r
6859 if (newhp == histIn || history[newhp] == NULL) return NULL;
\r
6861 return history[histP];
\r
6867 if (histP == histIn) return NULL;
\r
6868 histP = (histP + 1) % HISTORY_SIZE;
\r
6869 return history[histP];
\r
6873 LoadIcsTextMenu(IcsTextMenuEntry *e)
\r
6877 hmenu = LoadMenu(hInst, "TextMenu");
\r
6878 h = GetSubMenu(hmenu, 0);
\r
6880 if (strcmp(e->item, "-") == 0) {
\r
6881 AppendMenu(h, MF_SEPARATOR, 0, 0);
\r
6882 } else { // [HGM] re-written a bit to use only one AppendMenu call for both cases (| or no |)
\r
6883 int flags = MF_STRING, j = 0;
\r
6884 if (e->item[0] == '|') {
\r
6885 flags |= MF_MENUBARBREAK;
\r
6888 if(!strcmp(e->command, "none")) flags |= MF_GRAYED; // [HGM] chatclick: provide inactive dummy
\r
6889 AppendMenu(h, flags, IDM_CommandX + i, e->item + j);
\r
6897 WNDPROC consoleTextWindowProc;
\r
6900 CommandX(HWND hwnd, char *command, BOOLEAN getname, BOOLEAN immediate)
\r
6902 char buf[MSG_SIZ], name[MSG_SIZ];
\r
6903 HWND hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
6907 SetWindowText(hInput, command);
\r
6909 SendMessage(hInput, WM_CHAR, '\r', 0);
\r
6911 sel.cpMin = 999999;
\r
6912 sel.cpMax = 999999;
\r
6913 SendMessage(hInput, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
6918 SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);
\r
6919 if (sel.cpMin == sel.cpMax) {
\r
6920 /* Expand to surrounding word */
\r
6923 tr.chrg.cpMax = sel.cpMin;
\r
6924 tr.chrg.cpMin = --sel.cpMin;
\r
6925 if (sel.cpMin < 0) break;
\r
6926 tr.lpstrText = name;
\r
6927 SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr);
\r
6928 } while (isalpha(name[0]) || isdigit(name[0]) || name[0] == '-');
\r
6932 tr.chrg.cpMin = sel.cpMax;
\r
6933 tr.chrg.cpMax = ++sel.cpMax;
\r
6934 tr.lpstrText = name;
\r
6935 if (SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr) < 1) break;
\r
6936 } while (isalpha(name[0]) || isdigit(name[0]) || name[0] == '-');
\r
6939 if (sel.cpMax == sel.cpMin || sel.cpMax - sel.cpMin > MSG_SIZ/2) {
\r
6940 MessageBeep(MB_ICONEXCLAMATION);
\r
6944 tr.lpstrText = name;
\r
6945 SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr);
\r
6947 if (sel.cpMax - sel.cpMin > MSG_SIZ/2) {
\r
6948 MessageBeep(MB_ICONEXCLAMATION);
\r
6951 SendMessage(hwnd, EM_GETSELTEXT, 0, (LPARAM) name);
\r
6954 if(strstr(command, "%s")) snprintf(buf, MSG_SIZ, command, name); else
\r
6955 snprintf(buf, MSG_SIZ, "%s %s", command, name);
\r
6956 SetWindowText(hInput, buf);
\r
6957 SendMessage(hInput, WM_CHAR, '\r', 0);
\r
6959 if(!strcmp(command, "chat")) { ChatPopUp(name); return; }
\r
6960 snprintf(buf, MSG_SIZ, "%s %s ", command, name); /* trailing space */
\r
6961 SetWindowText(hInput, buf);
\r
6962 sel.cpMin = 999999;
\r
6963 sel.cpMax = 999999;
\r
6964 SendMessage(hInput, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
6970 ConsoleTextSubclass(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
\r
6975 switch (message) {
\r
6977 if (!(GetKeyState(VK_CONTROL) & ~1)) break;
\r
6978 if(wParam=='R') return 0;
\r
6981 SendMessage(hwnd, EM_LINESCROLL, 0, -999999);
\r
6984 sel.cpMin = 999999;
\r
6985 sel.cpMax = 999999;
\r
6986 SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
6987 SendMessage(hwnd, EM_SCROLLCARET, 0, 0);
\r
6992 if(wParam != '\022') {
\r
6993 if (wParam == '\t') {
\r
6994 if (GetKeyState(VK_SHIFT) < 0) {
\r
6996 if (IsIconic(hwndMain)) ShowWindow(hwndMain, SW_RESTORE);
\r
6997 if (buttonDesc[0].hwnd) {
\r
6998 SetFocus(buttonDesc[0].hwnd);
\r
7000 SetFocus(hwndMain);
\r
7004 SetFocus(GetDlgItem(hwndConsole, OPT_ConsoleInput));
\r
7007 hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
7008 JAWS_DELETE( SetFocus(hInput); )
\r
7009 SendMessage(hInput, message, wParam, lParam);
\r
7012 } // [HGM] navigate: for Ctrl+R, flow into next case (moved up here) to summon up menu
\r
7014 case WM_RBUTTONDOWN:
\r
7015 if (!(GetKeyState(VK_SHIFT) & ~1)) {
\r
7016 /* Move selection here if it was empty */
\r
7018 pt.x = LOWORD(lParam);
\r
7019 pt.y = HIWORD(lParam);
\r
7020 SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);
\r
7021 if (sel.cpMin == sel.cpMax) {
\r
7022 if(lParam != -1) sel.cpMin = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&pt); /*doc is wrong*/
\r
7023 sel.cpMax = sel.cpMin;
\r
7024 SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
7026 SendMessage(hwnd, EM_HIDESELECTION, FALSE, FALSE);
\r
7027 { // [HGM] chatclick: code moved here from WM_RBUTTONUP case, to have menu appear on down-click
\r
7029 HMENU hmenu = LoadIcsTextMenu(icsTextMenuEntry);
\r
7030 SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);
\r
7031 if (sel.cpMin == sel.cpMax) {
\r
7032 EnableMenuItem(hmenu, IDM_Copy, MF_BYCOMMAND|MF_GRAYED);
\r
7033 EnableMenuItem(hmenu, IDM_QuickPaste, MF_BYCOMMAND|MF_GRAYED);
\r
7035 if (!IsClipboardFormatAvailable(CF_TEXT)) {
\r
7036 EnableMenuItem(hmenu, IDM_Paste, MF_BYCOMMAND|MF_GRAYED);
\r
7038 pt.x = LOWORD(lParam)-30; // [HGM] chatclick: make menu pop up with pointer above upper-right item
\r
7039 pt.y = HIWORD(lParam)-10; // make it appear as if mouse moved there, so it will be selected on up-click
\r
7040 PostMessage(hwnd, WM_MOUSEMOVE, wParam, lParam+5);
\r
7041 MenuPopup(hwnd, pt, hmenu, -1);
\r
7045 case WM_RBUTTONUP:
\r
7046 if (GetKeyState(VK_SHIFT) & ~1) {
\r
7047 SendDlgItemMessage(hwndConsole, OPT_ConsoleText,
\r
7048 WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);
\r
7052 hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
7054 return SendMessage(hInput, message, wParam, lParam);
\r
7055 case WM_MBUTTONDOWN:
\r
7056 return SendMessage(hwnd, WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);
\r
7058 switch (LOWORD(wParam)) {
\r
7059 case IDM_QuickPaste:
\r
7061 SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);
\r
7062 if (sel.cpMin == sel.cpMax) {
\r
7063 MessageBeep(MB_ICONEXCLAMATION);
\r
7066 SendMessage(hwnd, WM_COPY, 0, 0);
\r
7067 hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
7068 SendMessage(hInput, WM_PASTE, 0, 0);
\r
7073 SendMessage(hwnd, WM_CUT, 0, 0);
\r
7076 SendMessage(hwnd, WM_PASTE, 0, 0);
\r
7079 SendMessage(hwnd, WM_COPY, 0, 0);
\r
7083 int i = LOWORD(wParam) - IDM_CommandX;
\r
7084 if (i >= 0 && i < ICS_TEXT_MENU_SIZE &&
\r
7085 icsTextMenuEntry[i].command != NULL) {
\r
7086 CommandX(hwnd, icsTextMenuEntry[i].command,
\r
7087 icsTextMenuEntry[i].getname,
\r
7088 icsTextMenuEntry[i].immediate);
\r
7096 return (*consoleTextWindowProc)(hwnd, message, wParam, lParam);
\r
7099 WNDPROC consoleInputWindowProc;
\r
7102 ConsoleInputSubclass(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
\r
7104 char buf[MSG_SIZ];
\r
7106 static BOOL sendNextChar = FALSE;
\r
7107 static BOOL quoteNextChar = FALSE;
\r
7108 InputSource *is = consoleInputSource;
\r
7112 switch (message) {
\r
7114 if (!appData.localLineEditing || sendNextChar) {
\r
7115 is->buf[0] = (CHAR) wParam;
\r
7117 SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);
\r
7118 sendNextChar = FALSE;
\r
7121 if (quoteNextChar) {
\r
7122 buf[0] = (char) wParam;
\r
7123 buf[1] = NULLCHAR;
\r
7124 SendMessage(hwnd, EM_REPLACESEL, TRUE, (LPARAM) buf);
\r
7125 quoteNextChar = FALSE;
\r
7129 case '\r': /* Enter key */
\r
7130 is->count = GetWindowText(hwnd, is->buf, INPUT_SOURCE_BUF_SIZE-1);
\r
7131 if (consoleEcho) SaveInHistory(is->buf);
\r
7132 is->buf[is->count++] = '\n';
\r
7133 is->buf[is->count] = NULLCHAR;
\r
7134 SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);
\r
7135 if (consoleEcho) {
\r
7136 ConsoleOutput(is->buf, is->count, TRUE);
\r
7137 } else if (appData.localLineEditing) {
\r
7138 ConsoleOutput("\n", 1, TRUE);
\r
7141 case '\033': /* Escape key */
\r
7142 SetWindowText(hwnd, "");
\r
7143 cf.cbSize = sizeof(CHARFORMAT);
\r
7144 cf.dwMask = CFM_COLOR|CFM_BOLD|CFM_ITALIC|CFM_UNDERLINE|CFM_STRIKEOUT;
\r
7145 if (consoleEcho) {
\r
7146 cf.crTextColor = textAttribs[ColorNormal].color;
\r
7148 cf.crTextColor = COLOR_ECHOOFF;
\r
7150 cf.dwEffects = textAttribs[ColorNormal].effects;
\r
7151 SendMessage(hwnd, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf);
\r
7153 case '\t': /* Tab key */
\r
7154 if (GetKeyState(VK_SHIFT) < 0) {
\r
7156 SetFocus(GetDlgItem(hwndConsole, OPT_ConsoleText));
\r
7159 if (IsIconic(hwndMain)) ShowWindow(hwndMain, SW_RESTORE);
\r
7160 if (buttonDesc[0].hwnd) {
\r
7161 SetFocus(buttonDesc[0].hwnd);
\r
7163 SetFocus(hwndMain);
\r
7167 case '\023': /* Ctrl+S */
\r
7168 sendNextChar = TRUE;
\r
7170 case '\021': /* Ctrl+Q */
\r
7171 quoteNextChar = TRUE;
\r
7181 GetWindowText(hwnd, buf, MSG_SIZ);
\r
7182 p = PrevInHistory(buf);
\r
7184 SetWindowText(hwnd, p);
\r
7185 sel.cpMin = 999999;
\r
7186 sel.cpMax = 999999;
\r
7187 SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
7192 p = NextInHistory();
\r
7194 SetWindowText(hwnd, p);
\r
7195 sel.cpMin = 999999;
\r
7196 sel.cpMax = 999999;
\r
7197 SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
7203 if (!(GetKeyState(VK_CONTROL) & ~1)) break;
\r
7207 SendDlgItemMessage(hwndConsole, OPT_ConsoleText, message, wParam, lParam);
\r
7211 case WM_MBUTTONDOWN:
\r
7212 SendDlgItemMessage(hwndConsole, OPT_ConsoleText,
\r
7213 WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);
\r
7215 case WM_RBUTTONUP:
\r
7216 if (GetKeyState(VK_SHIFT) & ~1) {
\r
7217 SendDlgItemMessage(hwndConsole, OPT_ConsoleText,
\r
7218 WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);
\r
7222 hmenu = LoadMenu(hInst, "InputMenu");
\r
7223 SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);
\r
7224 if (sel.cpMin == sel.cpMax) {
\r
7225 EnableMenuItem(hmenu, IDM_Copy, MF_BYCOMMAND|MF_GRAYED);
\r
7226 EnableMenuItem(hmenu, IDM_Cut, MF_BYCOMMAND|MF_GRAYED);
\r
7228 if (!IsClipboardFormatAvailable(CF_TEXT)) {
\r
7229 EnableMenuItem(hmenu, IDM_Paste, MF_BYCOMMAND|MF_GRAYED);
\r
7231 pt.x = LOWORD(lParam);
\r
7232 pt.y = HIWORD(lParam);
\r
7233 MenuPopup(hwnd, pt, hmenu, -1);
\r
7237 switch (LOWORD(wParam)) {
\r
7239 SendMessage(hwnd, EM_UNDO, 0, 0);
\r
7241 case IDM_SelectAll:
\r
7243 sel.cpMax = -1; /*999999?*/
\r
7244 SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
7247 SendMessage(hwnd, WM_CUT, 0, 0);
\r
7250 SendMessage(hwnd, WM_PASTE, 0, 0);
\r
7253 SendMessage(hwnd, WM_COPY, 0, 0);
\r
7258 return (*consoleInputWindowProc)(hwnd, message, wParam, lParam);
\r
7261 #define CO_MAX 100000
\r
7262 #define CO_TRIM 1000
\r
7265 ConsoleWndProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
7267 static SnapData sd;
\r
7268 HWND hText, hInput;
\r
7270 static int sizeX, sizeY;
\r
7271 int newSizeX, newSizeY;
\r
7275 hText = GetDlgItem(hDlg, OPT_ConsoleText);
\r
7276 hInput = GetDlgItem(hDlg, OPT_ConsoleInput);
\r
7278 switch (message) {
\r
7280 if (((NMHDR*)lParam)->code == EN_LINK)
\r
7282 ENLINK *pLink = (ENLINK*)lParam;
\r
7283 if (pLink->msg == WM_LBUTTONUP)
\r
7287 tr.chrg = pLink->chrg;
\r
7288 tr.lpstrText = malloc(1+tr.chrg.cpMax-tr.chrg.cpMin);
\r
7289 SendMessage(hText, EM_GETTEXTRANGE, 0, (LPARAM)&tr);
\r
7290 ShellExecute(NULL, "open", tr.lpstrText, NULL, NULL, SW_SHOW);
\r
7291 free(tr.lpstrText);
\r
7295 case WM_INITDIALOG: /* message: initialize dialog box */
\r
7296 hwndConsole = hDlg;
\r
7298 consoleTextWindowProc = (WNDPROC)
\r
7299 SetWindowLongPtr(hText, GWLP_WNDPROC, (LONG_PTR) ConsoleTextSubclass);
\r
7300 SendMessage(hText, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);
\r
7301 consoleInputWindowProc = (WNDPROC)
\r
7302 SetWindowLongPtr(hInput, GWLP_WNDPROC, (LONG_PTR) ConsoleInputSubclass);
\r
7303 SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);
\r
7304 Colorize(ColorNormal, TRUE);
\r
7305 SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &consoleCF);
\r
7306 ChangedConsoleFont();
\r
7307 GetClientRect(hDlg, &rect);
\r
7308 sizeX = rect.right;
\r
7309 sizeY = rect.bottom;
\r
7310 if (wpConsole.x != CW_USEDEFAULT && wpConsole.y != CW_USEDEFAULT &&
\r
7311 wpConsole.width != CW_USEDEFAULT && wpConsole.height != CW_USEDEFAULT) {
\r
7312 WINDOWPLACEMENT wp;
\r
7313 EnsureOnScreen(&wpConsole.x, &wpConsole.y, 0, 0);
\r
7314 wp.length = sizeof(WINDOWPLACEMENT);
\r
7316 wp.showCmd = SW_SHOW;
\r
7317 wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;
\r
7318 wp.rcNormalPosition.left = wpConsole.x;
\r
7319 wp.rcNormalPosition.right = wpConsole.x + wpConsole.width;
\r
7320 wp.rcNormalPosition.top = wpConsole.y;
\r
7321 wp.rcNormalPosition.bottom = wpConsole.y + wpConsole.height;
\r
7322 SetWindowPlacement(hDlg, &wp);
\r
7325 // [HGM] Chessknight's change 2004-07-13
\r
7326 else { /* Determine Defaults */
\r
7327 WINDOWPLACEMENT wp;
\r
7328 wpConsole.x = wpMain.width + 1;
\r
7329 wpConsole.y = wpMain.y;
\r
7330 wpConsole.width = screenWidth - wpMain.width;
\r
7331 wpConsole.height = wpMain.height;
\r
7332 EnsureOnScreen(&wpConsole.x, &wpConsole.y, 0, 0);
\r
7333 wp.length = sizeof(WINDOWPLACEMENT);
\r
7335 wp.showCmd = SW_SHOW;
\r
7336 wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;
\r
7337 wp.rcNormalPosition.left = wpConsole.x;
\r
7338 wp.rcNormalPosition.right = wpConsole.x + wpConsole.width;
\r
7339 wp.rcNormalPosition.top = wpConsole.y;
\r
7340 wp.rcNormalPosition.bottom = wpConsole.y + wpConsole.height;
\r
7341 SetWindowPlacement(hDlg, &wp);
\r
7344 // Allow hText to highlight URLs and send notifications on them
\r
7345 wMask = (WORD) SendMessage(hText, EM_GETEVENTMASK, 0, 0L);
\r
7346 SendMessage(hText, EM_SETEVENTMASK, 0, wMask | ENM_LINK);
\r
7347 SendMessage(hText, EM_AUTOURLDETECT, TRUE, 0L);
\r
7348 SetWindowLongPtr(hText, GWLP_USERDATA, 79); // initialize the text window's width
\r
7362 if (IsIconic(hDlg)) break;
\r
7363 newSizeX = LOWORD(lParam);
\r
7364 newSizeY = HIWORD(lParam);
\r
7365 if (sizeX != newSizeX || sizeY != newSizeY) {
\r
7366 RECT rectText, rectInput;
\r
7368 int newTextHeight, newTextWidth;
\r
7369 GetWindowRect(hText, &rectText);
\r
7370 newTextWidth = rectText.right - rectText.left + newSizeX - sizeX;
\r
7371 newTextHeight = rectText.bottom - rectText.top + newSizeY - sizeY;
\r
7372 if (newTextHeight < 0) {
\r
7373 newSizeY += -newTextHeight;
\r
7374 newTextHeight = 0;
\r
7376 SetWindowPos(hText, NULL, 0, 0,
\r
7377 newTextWidth, newTextHeight, SWP_NOZORDER|SWP_NOMOVE);
\r
7378 GetWindowRect(hInput, &rectInput); /* gives screen coords */
\r
7379 pt.x = rectInput.left;
\r
7380 pt.y = rectInput.top + newSizeY - sizeY;
\r
7381 ScreenToClient(hDlg, &pt);
\r
7382 SetWindowPos(hInput, NULL,
\r
7383 pt.x, pt.y, /* needs client coords */
\r
7384 rectInput.right - rectInput.left + newSizeX - sizeX,
\r
7385 rectInput.bottom - rectInput.top, SWP_NOZORDER);
\r
7391 case WM_GETMINMAXINFO:
\r
7392 /* Prevent resizing window too small */
\r
7393 mmi = (MINMAXINFO *) lParam;
\r
7394 mmi->ptMinTrackSize.x = 100;
\r
7395 mmi->ptMinTrackSize.y = 100;
\r
7398 /* [AS] Snapping */
\r
7399 case WM_ENTERSIZEMOVE:
\r
7400 return OnEnterSizeMove( &sd, hDlg, wParam, lParam );
\r
7403 return OnSizing( &sd, hDlg, wParam, lParam );
\r
7406 return OnMoving( &sd, hDlg, wParam, lParam );
\r
7408 case WM_EXITSIZEMOVE:
\r
7409 UpdateICSWidth(hText);
\r
7410 return OnExitSizeMove( &sd, hDlg, wParam, lParam );
\r
7413 return DefWindowProc(hDlg, message, wParam, lParam);
\r
7421 if (hwndConsole) return;
\r
7422 hCons = CreateDialog(hInst, szConsoleName, 0, NULL);
\r
7423 SendMessage(hCons, WM_INITDIALOG, 0, 0);
\r
7428 ConsoleOutput(char* data, int length, int forceVisible)
\r
7433 char buf[CO_MAX+1];
\r
7436 static int delayLF = 0;
\r
7437 CHARRANGE savesel, sel;
\r
7439 if (hwndConsole == NULL || length > CO_MAX-100 || length == 0) return;
\r
7447 while (length--) {
\r
7455 } else if (*p == '\007') {
\r
7456 MyPlaySound(&sounds[(int)SoundBell]);
\r
7463 hText = GetDlgItem(hwndConsole, OPT_ConsoleText);
\r
7464 SendMessage(hText, EM_HIDESELECTION, TRUE, FALSE);
\r
7465 /* Save current selection */
\r
7466 SendMessage(hText, EM_EXGETSEL, 0, (LPARAM)&savesel);
\r
7467 exlen = GetWindowTextLength(hText);
\r
7468 /* Find out whether current end of text is visible */
\r
7469 SendMessage(hText, EM_GETRECT, 0, (LPARAM) &rect);
\r
7470 SendMessage(hText, EM_POSFROMCHAR, (WPARAM) &pEnd, exlen);
\r
7471 /* Trim existing text if it's too long */
\r
7472 if (exlen + (q - buf) > CO_MAX) {
\r
7473 trim = (CO_TRIM > (q - buf)) ? CO_TRIM : (q - buf);
\r
7476 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
7477 SendMessage(hText, EM_REPLACESEL, 0, (LPARAM)"");
\r
7479 savesel.cpMin -= trim;
\r
7480 savesel.cpMax -= trim;
\r
7481 if (exlen < 0) exlen = 0;
\r
7482 if (savesel.cpMin < 0) savesel.cpMin = 0;
\r
7483 if (savesel.cpMax < savesel.cpMin) savesel.cpMax = savesel.cpMin;
\r
7485 /* Append the new text */
\r
7486 sel.cpMin = exlen;
\r
7487 sel.cpMax = exlen;
\r
7488 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
7489 SendMessage(hText, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&consoleCF);
\r
7490 SendMessage(hText, EM_REPLACESEL, 0, (LPARAM) buf);
\r
7491 if (forceVisible || exlen == 0 ||
\r
7492 (rect.left <= pEnd.x && pEnd.x < rect.right &&
\r
7493 rect.top <= pEnd.y && pEnd.y < rect.bottom)) {
\r
7494 /* Scroll to make new end of text visible if old end of text
\r
7495 was visible or new text is an echo of user typein */
\r
7496 sel.cpMin = 9999999;
\r
7497 sel.cpMax = 9999999;
\r
7498 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
7499 SendMessage(hText, EM_HIDESELECTION, FALSE, FALSE);
\r
7500 SendMessage(hText, EM_SCROLLCARET, 0, 0);
\r
7501 SendMessage(hText, EM_HIDESELECTION, TRUE, FALSE);
\r
7503 if (savesel.cpMax == exlen || forceVisible) {
\r
7504 /* Move insert point to new end of text if it was at the old
\r
7505 end of text or if the new text is an echo of user typein */
\r
7506 sel.cpMin = 9999999;
\r
7507 sel.cpMax = 9999999;
\r
7508 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
7510 /* Restore previous selection */
\r
7511 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&savesel);
\r
7513 SendMessage(hText, EM_HIDESELECTION, FALSE, FALSE);
\r
7520 DisplayHoldingsCount(HDC hdc, int x, int y, int rightAlign, int copyNumber)
\r
7524 COLORREF oldFg, oldBg;
\r
7528 if(copyNumber > 1)
\r
7529 snprintf(buf, sizeof(buf)/sizeof(buf[0]),"%d", copyNumber); else buf[0] = 0;
\r
7531 oldFg = SetTextColor(hdc, RGB(255, 255, 255)); /* white */
\r
7532 oldBg = SetBkColor(hdc, RGB(0, 0, 0)); /* black */
\r
7533 oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);
\r
7536 rect.right = x + squareSize;
\r
7538 rect.bottom = y + squareSize;
\r
7541 ExtTextOut(hdc, x + MESSAGE_LINE_LEFTMARGIN
\r
7542 + (rightAlign ? (squareSize*2)/3 : 0),
\r
7543 y, ETO_CLIPPED|ETO_OPAQUE,
\r
7544 &rect, str, strlen(str), NULL);
\r
7546 (void) SetTextColor(hdc, oldFg);
\r
7547 (void) SetBkColor(hdc, oldBg);
\r
7548 (void) SelectObject(hdc, oldFont);
\r
7552 DisplayAClock(HDC hdc, int timeRemaining, int highlight,
\r
7553 RECT *rect, char *color, char *flagFell)
\r
7557 COLORREF oldFg, oldBg;
\r
7560 if (twoBoards && partnerUp) return;
\r
7561 if (appData.clockMode) {
\r
7563 snprintf(buf, sizeof(buf)/sizeof(buf[0]), "%c %s %s", color[0], TimeString(timeRemaining), flagFell);
\r
7565 snprintf(buf, sizeof(buf)/sizeof(buf[0]), "%s:%c%s %s", color, (logoHeight>0 ? 0 : ' '), TimeString(timeRemaining), flagFell);
\r
7572 oldFg = SetTextColor(hdc, RGB(255, 255, 255)); /* white */
\r
7573 oldBg = SetBkColor(hdc, RGB(0, 0, 0)); /* black */
\r
7575 oldFg = SetTextColor(hdc, RGB(0, 0, 0)); /* black */
\r
7576 oldBg = SetBkColor(hdc, RGB(255, 255, 255)); /* white */
\r
7578 oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);
\r
7582 ExtTextOut(hdc, rect->left + MESSAGE_LINE_LEFTMARGIN,
\r
7583 rect->top, ETO_CLIPPED|ETO_OPAQUE,
\r
7584 rect, str, strlen(str), NULL);
\r
7585 if(logoHeight > 0 && appData.clockMode) {
\r
7587 str += strlen(color)+2;
\r
7588 r.top = rect->top + logoHeight/2;
\r
7589 r.left = rect->left;
\r
7590 r.right = rect->right;
\r
7591 r.bottom = rect->bottom;
\r
7592 ExtTextOut(hdc, rect->left + MESSAGE_LINE_LEFTMARGIN,
\r
7593 r.top, ETO_CLIPPED|ETO_OPAQUE,
\r
7594 &r, str, strlen(str), NULL);
\r
7596 (void) SetTextColor(hdc, oldFg);
\r
7597 (void) SetBkColor(hdc, oldBg);
\r
7598 (void) SelectObject(hdc, oldFont);
\r
7603 DoReadFile(HANDLE hFile, char *buf, int count, DWORD *outCount,
\r
7609 if( count <= 0 ) {
\r
7610 if (appData.debugMode) {
\r
7611 fprintf( debugFP, "DoReadFile: trying to read past end of buffer, overflow = %d\n", count );
\r
7614 return ERROR_INVALID_USER_BUFFER;
\r
7617 ResetEvent(ovl->hEvent);
\r
7618 ovl->Offset = ovl->OffsetHigh = 0;
\r
7619 ok = ReadFile(hFile, buf, count, outCount, ovl);
\r
7623 err = GetLastError();
\r
7624 if (err == ERROR_IO_PENDING) {
\r
7625 ok = GetOverlappedResult(hFile, ovl, outCount, TRUE);
\r
7629 err = GetLastError();
\r
7636 DoWriteFile(HANDLE hFile, char *buf, int count, DWORD *outCount,
\r
7641 ResetEvent(ovl->hEvent);
\r
7642 ovl->Offset = ovl->OffsetHigh = 0;
\r
7643 ok = WriteFile(hFile, buf, count, outCount, ovl);
\r
7647 err = GetLastError();
\r
7648 if (err == ERROR_IO_PENDING) {
\r
7649 ok = GetOverlappedResult(hFile, ovl, outCount, TRUE);
\r
7653 err = GetLastError();
\r
7659 /* [AS] If input is line by line and a line exceed the buffer size, force an error */
\r
7660 void CheckForInputBufferFull( InputSource * is )
\r
7662 if( is->lineByLine && (is->next - is->buf) >= INPUT_SOURCE_BUF_SIZE ) {
\r
7663 /* Look for end of line */
\r
7664 char * p = is->buf;
\r
7666 while( p < is->next && *p != '\n' ) {
\r
7670 if( p >= is->next ) {
\r
7671 if (appData.debugMode) {
\r
7672 fprintf( debugFP, "Input line exceeded buffer size (source id=%lu)\n", is->id );
\r
7675 is->error = ERROR_BROKEN_PIPE; /* [AS] Just any non-successful code! */
\r
7676 is->count = (DWORD) -1;
\r
7677 is->next = is->buf;
\r
7683 InputThread(LPVOID arg)
\r
7688 is = (InputSource *) arg;
\r
7689 ovl.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
\r
7690 ovl.Internal = ovl.InternalHigh = ovl.Offset = ovl.OffsetHigh = 0;
\r
7691 while (is->hThread != NULL) {
\r
7692 is->error = DoReadFile(is->hFile, is->next,
\r
7693 INPUT_SOURCE_BUF_SIZE - (is->next - is->buf),
\r
7694 &is->count, &ovl);
\r
7695 if (is->error == NO_ERROR) {
\r
7696 is->next += is->count;
\r
7698 if (is->error == ERROR_BROKEN_PIPE) {
\r
7699 /* Correct for MS brain damage. EOF reading a pipe is not an error. */
\r
7702 is->count = (DWORD) -1;
\r
7703 /* [AS] The (is->count <= 0) check below is not useful for unsigned values! */
\r
7708 CheckForInputBufferFull( is );
\r
7710 SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);
\r
7712 if( is->count == ((DWORD) -1) ) break; /* [AS] */
\r
7714 if (is->count <= 0) break; /* Quit on EOF or error */
\r
7717 CloseHandle(ovl.hEvent);
\r
7718 CloseHandle(is->hFile);
\r
7720 if (appData.debugMode) {
\r
7721 fprintf( debugFP, "Input thread terminated (id=%lu, error=%d, count=%ld)\n", is->id, is->error, is->count );
\r
7728 /* Windows 95 beta 2 won't let you do overlapped i/o on a console or pipe */
\r
7730 NonOvlInputThread(LPVOID arg)
\r
7737 is = (InputSource *) arg;
\r
7738 while (is->hThread != NULL) {
\r
7739 is->error = ReadFile(is->hFile, is->next,
\r
7740 INPUT_SOURCE_BUF_SIZE - (is->next - is->buf),
\r
7741 &is->count, NULL) ? NO_ERROR : GetLastError();
\r
7742 if (is->error == NO_ERROR) {
\r
7743 /* Change CRLF to LF */
\r
7744 if (is->next > is->buf) {
\r
7746 i = is->count + 1;
\r
7754 if (prev == '\r' && *p == '\n') {
\r
7766 if (is->error == ERROR_BROKEN_PIPE) {
\r
7767 /* Correct for MS brain damage. EOF reading a pipe is not an error. */
\r
7770 is->count = (DWORD) -1;
\r
7774 CheckForInputBufferFull( is );
\r
7776 SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);
\r
7778 if( is->count == ((DWORD) -1) ) break; /* [AS] */
\r
7780 if (is->count < 0) break; /* Quit on error */
\r
7782 CloseHandle(is->hFile);
\r
7787 SocketInputThread(LPVOID arg)
\r
7791 is = (InputSource *) arg;
\r
7792 while (is->hThread != NULL) {
\r
7793 is->count = recv(is->sock, is->buf, INPUT_SOURCE_BUF_SIZE, 0);
\r
7794 if ((int)is->count == SOCKET_ERROR) {
\r
7795 is->count = (DWORD) -1;
\r
7796 is->error = WSAGetLastError();
\r
7798 is->error = NO_ERROR;
\r
7799 is->next += is->count;
\r
7800 if (is->count == 0 && is->second == is) {
\r
7801 /* End of file on stderr; quit with no message */
\r
7805 SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);
\r
7807 if( is->count == ((DWORD) -1) ) break; /* [AS] */
\r
7809 if (is->count <= 0) break; /* Quit on EOF or error */
\r
7815 InputEvent(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
\r
7819 is = (InputSource *) lParam;
\r
7820 if (is->lineByLine) {
\r
7821 /* Feed in lines one by one */
\r
7822 char *p = is->buf;
\r
7824 while (q < is->next) {
\r
7825 if (*q++ == '\n') {
\r
7826 (is->func)(is, is->closure, p, q - p, NO_ERROR);
\r
7831 /* Move any partial line to the start of the buffer */
\r
7833 while (p < is->next) {
\r
7838 if (is->error != NO_ERROR || is->count == 0) {
\r
7839 /* Notify backend of the error. Note: If there was a partial
\r
7840 line at the end, it is not flushed through. */
\r
7841 (is->func)(is, is->closure, is->buf, is->count, is->error);
\r
7844 /* Feed in the whole chunk of input at once */
\r
7845 (is->func)(is, is->closure, is->buf, is->count, is->error);
\r
7846 is->next = is->buf;
\r
7850 /*---------------------------------------------------------------------------*\
\r
7852 * Menu enables. Used when setting various modes.
\r
7854 \*---------------------------------------------------------------------------*/
\r
7862 GreyRevert(Boolean grey)
\r
7863 { // [HGM] vari: for retracting variations in local mode
\r
7864 HMENU hmenu = GetMenu(hwndMain);
\r
7865 EnableMenuItem(hmenu, IDM_Revert, MF_BYCOMMAND|(grey ? MF_GRAYED : MF_ENABLED));
\r
7866 EnableMenuItem(hmenu, IDM_Annotate, MF_BYCOMMAND|(grey ? MF_GRAYED : MF_ENABLED));
\r
7870 SetMenuEnables(HMENU hmenu, Enables *enab)
\r
7872 while (enab->item > 0) {
\r
7873 (void) EnableMenuItem(hmenu, enab->item, enab->flags);
\r
7878 Enables gnuEnables[] = {
\r
7879 { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },
\r
7880 { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },
\r
7881 { IDM_IcsClient, MF_BYCOMMAND|MF_GRAYED },
\r
7882 { IDM_Accept, MF_BYCOMMAND|MF_GRAYED },
\r
7883 { IDM_Decline, MF_BYCOMMAND|MF_GRAYED },
\r
7884 { IDM_Rematch, MF_BYCOMMAND|MF_GRAYED },
\r
7885 { IDM_Adjourn, MF_BYCOMMAND|MF_GRAYED },
\r
7886 { IDM_StopExamining, MF_BYCOMMAND|MF_GRAYED },
\r
7887 { IDM_StopObserving, MF_BYCOMMAND|MF_GRAYED },
\r
7888 { IDM_Upload, MF_BYCOMMAND|MF_GRAYED },
\r
7889 { IDM_Revert, MF_BYCOMMAND|MF_GRAYED },
\r
7890 { IDM_Annotate, MF_BYCOMMAND|MF_GRAYED },
\r
7891 { IDM_NewChat, MF_BYCOMMAND|MF_GRAYED },
\r
7893 // Needed to switch from ncp to GNU mode on Engine Load
\r
7894 { ACTION_POS, MF_BYPOSITION|MF_ENABLED },
\r
7895 { IDM_MachineWhite, MF_BYCOMMAND|MF_ENABLED },
\r
7896 { IDM_MachineBlack, MF_BYCOMMAND|MF_ENABLED },
\r
7897 { IDM_TwoMachines, MF_BYCOMMAND|MF_ENABLED },
\r
7898 { IDM_Match, MF_BYCOMMAND|MF_ENABLED },
\r
7899 { IDM_AnalysisMode, MF_BYCOMMAND|MF_ENABLED },
\r
7900 { IDM_AnalyzeFile, MF_BYCOMMAND|MF_ENABLED },
\r
7901 { IDM_Engine1Options, MF_BYCOMMAND|MF_ENABLED },
\r
7902 { IDM_Engine2Options, MF_BYCOMMAND|MF_ENABLED },
\r
7903 { IDM_TimeControl, MF_BYCOMMAND|MF_ENABLED },
\r
7904 { IDM_RetractMove, MF_BYCOMMAND|MF_ENABLED },
\r
7905 { IDM_MoveNow, MF_BYCOMMAND|MF_ENABLED },
\r
7906 { IDM_Hint, MF_BYCOMMAND|MF_ENABLED },
\r
7907 { IDM_Book, MF_BYCOMMAND|MF_ENABLED },
\r
7911 Enables icsEnables[] = {
\r
7912 { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },
\r
7913 { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },
\r
7914 { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },
\r
7915 { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },
\r
7916 { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },
\r
7917 { IDM_Match, MF_BYCOMMAND|MF_GRAYED },
\r
7918 { IDM_MachineBoth, MF_BYCOMMAND|MF_GRAYED },
\r
7919 { IDM_AnalysisMode, MF_BYCOMMAND|MF_ENABLED },
\r
7920 { IDM_AnalyzeFile, MF_BYCOMMAND|MF_GRAYED },
\r
7921 { IDM_TimeControl, MF_BYCOMMAND|MF_GRAYED },
\r
7922 { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },
\r
7923 { IDM_Hint, MF_BYCOMMAND|MF_GRAYED },
\r
7924 { IDM_Book, MF_BYCOMMAND|MF_GRAYED },
\r
7925 { IDM_LoadProg1, MF_BYCOMMAND|MF_GRAYED },
\r
7926 { IDM_LoadProg2, MF_BYCOMMAND|MF_GRAYED },
\r
7927 { IDM_IcsOptions, MF_BYCOMMAND|MF_ENABLED },
\r
7928 { IDM_Engine1Options, MF_BYCOMMAND|MF_GRAYED },
\r
7929 { IDM_Engine2Options, MF_BYCOMMAND|MF_GRAYED },
\r
7930 { IDM_Annotate, MF_BYCOMMAND|MF_GRAYED },
\r
7931 { IDM_Tourney, MF_BYCOMMAND|MF_GRAYED },
\r
7936 Enables zippyEnables[] = {
\r
7937 { IDM_MoveNow, MF_BYCOMMAND|MF_ENABLED },
\r
7938 { IDM_Hint, MF_BYCOMMAND|MF_ENABLED },
\r
7939 { IDM_Book, MF_BYCOMMAND|MF_ENABLED },
\r
7940 { IDM_Engine1Options, MF_BYCOMMAND|MF_ENABLED },
\r
7945 Enables ncpEnables[] = {
\r
7946 { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },
\r
7947 { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },
\r
7948 { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },
\r
7949 { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },
\r
7950 { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },
\r
7951 { IDM_Match, MF_BYCOMMAND|MF_GRAYED },
\r
7952 { IDM_AnalysisMode, MF_BYCOMMAND|MF_GRAYED },
\r
7953 { IDM_AnalyzeFile, MF_BYCOMMAND|MF_GRAYED },
\r
7954 { IDM_IcsClient, MF_BYCOMMAND|MF_GRAYED },
\r
7955 { ACTION_POS, MF_BYPOSITION|MF_GRAYED },
\r
7956 { IDM_Revert, MF_BYCOMMAND|MF_GRAYED },
\r
7957 { IDM_Annotate, MF_BYCOMMAND|MF_GRAYED },
\r
7958 { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },
\r
7959 { IDM_RetractMove, MF_BYCOMMAND|MF_GRAYED },
\r
7960 { IDM_TimeControl, MF_BYCOMMAND|MF_GRAYED },
\r
7961 { IDM_Hint, MF_BYCOMMAND|MF_GRAYED },
\r
7962 { IDM_Book, MF_BYCOMMAND|MF_GRAYED },
\r
7963 { IDM_MachineBoth, MF_BYCOMMAND|MF_GRAYED },
\r
7964 { IDM_NewChat, MF_BYCOMMAND|MF_GRAYED },
\r
7965 { IDM_Engine1Options, MF_BYCOMMAND|MF_GRAYED },
\r
7966 { IDM_Engine2Options, MF_BYCOMMAND|MF_GRAYED },
\r
7967 { IDM_Sounds, MF_BYCOMMAND|MF_GRAYED },
\r
7971 Enables trainingOnEnables[] = {
\r
7972 { IDM_EditComment, MF_BYCOMMAND|MF_GRAYED },
\r
7973 { IDM_Comment, MF_BYCOMMAND|MF_GRAYED },
\r
7974 { IDM_Pause, MF_BYCOMMAND|MF_GRAYED },
\r
7975 { IDM_Forward, MF_BYCOMMAND|MF_GRAYED },
\r
7976 { IDM_Backward, MF_BYCOMMAND|MF_GRAYED },
\r
7977 { IDM_ToEnd, MF_BYCOMMAND|MF_GRAYED },
\r
7978 { IDM_ToStart, MF_BYCOMMAND|MF_GRAYED },
\r
7979 { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },
\r
7980 { IDM_TruncateGame, MF_BYCOMMAND|MF_GRAYED },
\r
7984 Enables trainingOffEnables[] = {
\r
7985 { IDM_EditComment, MF_BYCOMMAND|MF_ENABLED },
\r
7986 { IDM_Comment, MF_BYCOMMAND|MF_ENABLED },
\r
7987 { IDM_Pause, MF_BYCOMMAND|MF_ENABLED },
\r
7988 { IDM_Forward, MF_BYCOMMAND|MF_ENABLED },
\r
7989 { IDM_Backward, MF_BYCOMMAND|MF_ENABLED },
\r
7990 { IDM_ToEnd, MF_BYCOMMAND|MF_ENABLED },
\r
7991 { IDM_ToStart, MF_BYCOMMAND|MF_ENABLED },
\r
7992 { IDM_MoveNow, MF_BYCOMMAND|MF_ENABLED },
\r
7993 { IDM_TruncateGame, MF_BYCOMMAND|MF_ENABLED },
\r
7997 /* These modify either ncpEnables or gnuEnables */
\r
7998 Enables cmailEnables[] = {
\r
7999 { IDM_MailMove, MF_BYCOMMAND|MF_ENABLED },
\r
8000 { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_ENABLED },
\r
8001 { ACTION_POS, MF_BYPOSITION|MF_ENABLED },
\r
8002 { IDM_CallFlag, MF_BYCOMMAND|MF_GRAYED },
\r
8003 { IDM_Draw, MF_BYCOMMAND|MF_ENABLED },
\r
8004 { IDM_Adjourn, MF_BYCOMMAND|MF_GRAYED },
\r
8005 { IDM_Abort, MF_BYCOMMAND|MF_GRAYED },
\r
8009 Enables machineThinkingEnables[] = {
\r
8010 { IDM_LoadGame, MF_BYCOMMAND|MF_GRAYED },
\r
8011 { IDM_LoadNextGame, MF_BYCOMMAND|MF_GRAYED },
\r
8012 { IDM_LoadPrevGame, MF_BYCOMMAND|MF_GRAYED },
\r
8013 { IDM_ReloadGame, MF_BYCOMMAND|MF_GRAYED },
\r
8014 { IDM_PasteGame, MF_BYCOMMAND|MF_GRAYED },
\r
8015 { IDM_LoadPosition, MF_BYCOMMAND|MF_GRAYED },
\r
8016 { IDM_LoadNextPosition, MF_BYCOMMAND|MF_GRAYED },
\r
8017 { IDM_LoadPrevPosition, MF_BYCOMMAND|MF_GRAYED },
\r
8018 { IDM_ReloadPosition, MF_BYCOMMAND|MF_GRAYED },
\r
8019 { IDM_PastePosition, MF_BYCOMMAND|MF_GRAYED },
\r
8020 { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },
\r
8021 { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },
\r
8022 { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },
\r
8023 // { IDM_Match, MF_BYCOMMAND|MF_GRAYED },
\r
8024 { IDM_TypeInMove, MF_BYCOMMAND|MF_GRAYED },
\r
8025 { IDM_RetractMove, MF_BYCOMMAND|MF_GRAYED },
\r
8029 Enables userThinkingEnables[] = {
\r
8030 { IDM_LoadGame, MF_BYCOMMAND|MF_ENABLED },
\r
8031 { IDM_LoadNextGame, MF_BYCOMMAND|MF_ENABLED },
\r
8032 { IDM_LoadPrevGame, MF_BYCOMMAND|MF_ENABLED },
\r
8033 { IDM_ReloadGame, MF_BYCOMMAND|MF_ENABLED },
\r
8034 { IDM_PasteGame, MF_BYCOMMAND|MF_ENABLED },
\r
8035 { IDM_LoadPosition, MF_BYCOMMAND|MF_ENABLED },
\r
8036 { IDM_LoadNextPosition, MF_BYCOMMAND|MF_ENABLED },
\r
8037 { IDM_LoadPrevPosition, MF_BYCOMMAND|MF_ENABLED },
\r
8038 { IDM_ReloadPosition, MF_BYCOMMAND|MF_ENABLED },
\r
8039 { IDM_PastePosition, MF_BYCOMMAND|MF_ENABLED },
\r
8040 { IDM_MachineWhite, MF_BYCOMMAND|MF_ENABLED },
\r
8041 { IDM_MachineBlack, MF_BYCOMMAND|MF_ENABLED },
\r
8042 { IDM_TwoMachines, MF_BYCOMMAND|MF_ENABLED },
\r
8043 // { IDM_Match, MF_BYCOMMAND|MF_ENABLED },
\r
8044 { IDM_TypeInMove, MF_BYCOMMAND|MF_ENABLED },
\r
8045 { IDM_RetractMove, MF_BYCOMMAND|MF_ENABLED },
\r
8049 /*---------------------------------------------------------------------------*\
\r
8051 * Front-end interface functions exported by XBoard.
\r
8052 * Functions appear in same order as prototypes in frontend.h.
\r
8054 \*---------------------------------------------------------------------------*/
\r
8056 CheckMark(UINT item, int state)
\r
8058 if(item) CheckMenuItem(GetMenu(hwndMain), item, MF_BYCOMMAND|state);
\r
8064 static UINT prevChecked = 0;
\r
8065 static int prevPausing = 0;
\r
8068 if (pausing != prevPausing) {
\r
8069 prevPausing = pausing;
\r
8070 (void) CheckMenuItem(GetMenu(hwndMain), IDM_Pause,
\r
8071 MF_BYCOMMAND|(pausing ? MF_CHECKED : MF_UNCHECKED));
\r
8072 if (hwndPause) SetWindowText(hwndPause, pausing ? "C" : "P");
\r
8075 switch (gameMode) {
\r
8076 case BeginningOfGame:
\r
8077 if (appData.icsActive)
\r
8078 nowChecked = IDM_IcsClient;
\r
8079 else if (appData.noChessProgram)
\r
8080 nowChecked = IDM_EditGame;
\r
8082 nowChecked = IDM_MachineBlack;
\r
8084 case MachinePlaysBlack:
\r
8085 nowChecked = IDM_MachineBlack;
\r
8087 case MachinePlaysWhite:
\r
8088 nowChecked = IDM_MachineWhite;
\r
8090 case TwoMachinesPlay:
\r
8091 nowChecked = IDM_TwoMachines;
\r
8094 nowChecked = IDM_AnalysisMode;
\r
8097 nowChecked = IDM_AnalyzeFile;
\r
8100 nowChecked = IDM_EditGame;
\r
8102 case PlayFromGameFile:
\r
8103 nowChecked = IDM_LoadGame;
\r
8105 case EditPosition:
\r
8106 nowChecked = IDM_EditPosition;
\r
8109 nowChecked = IDM_Training;
\r
8111 case IcsPlayingWhite:
\r
8112 case IcsPlayingBlack:
\r
8113 case IcsObserving:
\r
8115 nowChecked = IDM_IcsClient;
\r
8122 CheckMark(prevChecked, MF_UNCHECKED);
\r
8123 CheckMark(nowChecked, MF_CHECKED);
\r
8124 CheckMark(IDM_Match, matchMode && matchGame < appData.matchGames ? MF_CHECKED : MF_UNCHECKED);
\r
8126 if (nowChecked == IDM_LoadGame || nowChecked == IDM_Training) {
\r
8127 (void) EnableMenuItem(GetMenu(hwndMain), IDM_Training,
\r
8128 MF_BYCOMMAND|MF_ENABLED);
\r
8130 (void) EnableMenuItem(GetMenu(hwndMain),
\r
8131 IDM_Training, MF_BYCOMMAND|MF_GRAYED);
\r
8134 prevChecked = nowChecked;
\r
8136 /* [DM] icsEngineAnalyze - Do a sceure check too */
\r
8137 if (appData.icsActive) {
\r
8138 if (appData.icsEngineAnalyze) {
\r
8139 CheckMark(IDM_AnalysisMode, MF_CHECKED);
\r
8141 CheckMark(IDM_AnalysisMode, MF_UNCHECKED);
\r
8144 DisplayLogos(); // [HGM] logos: mode change could have altered logos
\r
8150 HMENU hmenu = GetMenu(hwndMain);
\r
8151 SetMenuEnables(hmenu, icsEnables);
\r
8152 EnableMenuItem(GetSubMenu(hmenu, OPTIONS_POS), IDM_IcsOptions,
\r
8153 MF_BYCOMMAND|MF_ENABLED);
\r
8155 if (appData.zippyPlay) {
\r
8156 SetMenuEnables(hmenu, zippyEnables);
\r
8157 if (!appData.noChessProgram) /* [DM] icsEngineAnalyze */
\r
8158 (void) EnableMenuItem(GetMenu(hwndMain), IDM_AnalysisMode,
\r
8159 MF_BYCOMMAND|MF_ENABLED);
\r
8167 SetMenuEnables(GetMenu(hwndMain), gnuEnables);
\r
8173 HMENU hmenu = GetMenu(hwndMain);
\r
8174 SetMenuEnables(hmenu, ncpEnables);
\r
8175 DrawMenuBar(hwndMain);
\r
8181 SetMenuEnables(GetMenu(hwndMain), cmailEnables);
\r
8185 SetTrainingModeOn()
\r
8188 SetMenuEnables(GetMenu(hwndMain), trainingOnEnables);
\r
8189 for (i = 0; i < N_BUTTONS; i++) {
\r
8190 if (buttonDesc[i].hwnd != NULL)
\r
8191 EnableWindow(buttonDesc[i].hwnd, FALSE);
\r
8196 VOID SetTrainingModeOff()
\r
8199 SetMenuEnables(GetMenu(hwndMain), trainingOffEnables);
\r
8200 for (i = 0; i < N_BUTTONS; i++) {
\r
8201 if (buttonDesc[i].hwnd != NULL)
\r
8202 EnableWindow(buttonDesc[i].hwnd, TRUE);
\r
8208 SetUserThinkingEnables()
\r
8210 SetMenuEnables(GetMenu(hwndMain), userThinkingEnables);
\r
8214 SetMachineThinkingEnables()
\r
8216 HMENU hMenu = GetMenu(hwndMain);
\r
8217 int flags = MF_BYCOMMAND|MF_ENABLED;
\r
8219 SetMenuEnables(hMenu, machineThinkingEnables);
\r
8221 if (gameMode == MachinePlaysBlack) {
\r
8222 (void)EnableMenuItem(hMenu, IDM_MachineBlack, flags);
\r
8223 } else if (gameMode == MachinePlaysWhite) {
\r
8224 (void)EnableMenuItem(hMenu, IDM_MachineWhite, flags);
\r
8225 } else if (gameMode == TwoMachinesPlay) {
\r
8226 (void)EnableMenuItem(hMenu, matchMode ? IDM_Match : IDM_TwoMachines, flags); // [HGM] match
\r
8232 DisplayTitle(char *str)
\r
8234 char title[MSG_SIZ], *host;
\r
8235 if (str[0] != NULLCHAR) {
\r
8236 safeStrCpy(title, str, sizeof(title)/sizeof(title[0]) );
\r
8237 } else if (appData.icsActive) {
\r
8238 if (appData.icsCommPort[0] != NULLCHAR)
\r
8241 host = appData.icsHost;
\r
8242 snprintf(title, MSG_SIZ, "%s: %s", szTitle, host);
\r
8243 } else if (appData.noChessProgram) {
\r
8244 safeStrCpy(title, szTitle, sizeof(title)/sizeof(title[0]) );
\r
8246 safeStrCpy(title, szTitle, sizeof(title)/sizeof(title[0]) );
\r
8247 strcat(title, ": ");
\r
8248 strcat(title, first.tidy);
\r
8250 SetWindowText(hwndMain, title);
\r
8255 DisplayMessage(char *str1, char *str2)
\r
8259 int remain = MESSAGE_TEXT_MAX - 1;
\r
8262 moveErrorMessageUp = FALSE; /* turned on later by caller if needed */
\r
8263 messageText[0] = NULLCHAR;
\r
8265 len = strlen(str1);
\r
8266 if (len > remain) len = remain;
\r
8267 strncpy(messageText, str1, len);
\r
8268 messageText[len] = NULLCHAR;
\r
8271 if (*str2 && remain >= 2) {
\r
8273 strcat(messageText, " ");
\r
8276 len = strlen(str2);
\r
8277 if (len > remain) len = remain;
\r
8278 strncat(messageText, str2, len);
\r
8280 messageText[MESSAGE_TEXT_MAX - 1] = NULLCHAR;
\r
8281 safeStrCpy(lastMsg, messageText, MSG_SIZ);
\r
8283 if (hwndMain == NULL || IsIconic(hwndMain)) return;
\r
8287 hdc = GetDC(hwndMain);
\r
8288 oldFont = SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);
\r
8289 ExtTextOut(hdc, messageRect.left, messageRect.top, ETO_CLIPPED|ETO_OPAQUE,
\r
8290 &messageRect, messageText, strlen(messageText), NULL);
\r
8291 (void) SelectObject(hdc, oldFont);
\r
8292 (void) ReleaseDC(hwndMain, hdc);
\r
8296 DisplayError(char *str, int error)
\r
8298 char buf[MSG_SIZ*2], buf2[MSG_SIZ];
\r
8302 safeStrCpy(buf, str, sizeof(buf)/sizeof(buf[0]) );
\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 ErrorPopUp(_("Error"), buf);
\r
8325 DisplayMoveError(char *str)
\r
8327 fromX = fromY = -1;
\r
8328 ClearHighlights();
\r
8329 DrawPosition(FALSE, NULL);
\r
8330 if (appData.popupMoveErrors) {
\r
8331 ErrorPopUp(_("Error"), str);
\r
8333 DisplayMessage(str, "");
\r
8334 moveErrorMessageUp = TRUE;
\r
8339 DisplayFatalError(char *str, int error, int exitStatus)
\r
8341 char buf[2*MSG_SIZ], buf2[MSG_SIZ];
\r
8343 char *label = exitStatus ? _("Fatal Error") : _("Exiting");
\r
8346 len = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,
\r
8347 NULL, error, LANG_NEUTRAL,
\r
8348 (LPSTR) buf2, MSG_SIZ, NULL);
\r
8350 snprintf(buf, 2*MSG_SIZ, "%s:\n%s", str, buf2);
\r
8352 ErrorMap *em = errmap;
\r
8353 while (em->err != 0 && em->err != error) em++;
\r
8354 if (em->err != 0) {
\r
8355 snprintf(buf, 2*MSG_SIZ, "%s:\n%s", str, em->msg);
\r
8357 snprintf(buf, 2*MSG_SIZ, "%s:\nError code %d", str, error);
\r
8362 if (appData.debugMode) {
\r
8363 fprintf(debugFP, "%s: %s\n", label, str);
\r
8365 if (appData.popupExitMessage) {
\r
8366 (void) MessageBox(hwndMain, str, label, MB_OK|
\r
8367 (exitStatus ? MB_ICONSTOP : MB_ICONINFORMATION));
\r
8369 ExitEvent(exitStatus);
\r
8374 DisplayInformation(char *str)
\r
8376 (void) MessageBox(hwndMain, str, _("Information"), MB_OK|MB_ICONINFORMATION);
\r
8381 DisplayNote(char *str)
\r
8383 ErrorPopUp(_("Note"), str);
\r
8388 char *title, *question, *replyPrefix;
\r
8393 QuestionDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
8395 static QuestionParams *qp;
\r
8396 char reply[MSG_SIZ];
\r
8399 switch (message) {
\r
8400 case WM_INITDIALOG:
\r
8401 qp = (QuestionParams *) lParam;
\r
8402 CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));
\r
8403 Translate(hDlg, DLG_Question);
\r
8404 SetWindowText(hDlg, qp->title);
\r
8405 SetDlgItemText(hDlg, OPT_QuestionText, qp->question);
\r
8406 SetFocus(GetDlgItem(hDlg, OPT_QuestionInput));
\r
8410 switch (LOWORD(wParam)) {
\r
8412 safeStrCpy(reply, qp->replyPrefix, sizeof(reply)/sizeof(reply[0]) );
\r
8413 if (*reply) strcat(reply, " ");
\r
8414 len = strlen(reply);
\r
8415 GetDlgItemText(hDlg, OPT_QuestionInput, reply + len, sizeof(reply) - len);
\r
8416 strcat(reply, "\n");
\r
8417 OutputToProcess(qp->pr, reply, strlen(reply), &err);
\r
8418 EndDialog(hDlg, TRUE);
\r
8419 if (err) DisplayFatalError(_("Error writing to chess program"), err, 1);
\r
8422 EndDialog(hDlg, FALSE);
\r
8433 AskQuestion(char* title, char *question, char *replyPrefix, ProcRef pr)
\r
8435 QuestionParams qp;
\r
8439 qp.question = question;
\r
8440 qp.replyPrefix = replyPrefix;
\r
8442 lpProc = MakeProcInstance((FARPROC)QuestionDialog, hInst);
\r
8443 DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_Question),
\r
8444 hwndMain, (DLGPROC)lpProc, (LPARAM)&qp);
\r
8445 FreeProcInstance(lpProc);
\r
8448 /* [AS] Pick FRC position */
\r
8449 LRESULT CALLBACK NewGameFRC_Proc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
8451 static int * lpIndexFRC;
\r
8457 case WM_INITDIALOG:
\r
8458 lpIndexFRC = (int *) lParam;
\r
8460 CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));
\r
8461 Translate(hDlg, DLG_NewGameFRC);
\r
8463 SendDlgItemMessage( hDlg, IDC_NFG_Edit, EM_SETLIMITTEXT, sizeof(buf)-1, 0 );
\r
8464 SetDlgItemInt( hDlg, IDC_NFG_Edit, *lpIndexFRC, TRUE );
\r
8465 SendDlgItemMessage( hDlg, IDC_NFG_Edit, EM_SETSEL, 0, -1 );
\r
8466 SetFocus(GetDlgItem(hDlg, IDC_NFG_Edit));
\r
8471 switch( LOWORD(wParam) ) {
\r
8473 *lpIndexFRC = GetDlgItemInt(hDlg, IDC_NFG_Edit, &index_is_ok, TRUE );
\r
8474 EndDialog( hDlg, 0 );
\r
8475 shuffleOpenings = TRUE; /* [HGM] shuffle: switch shuffling on for as long as we stay in current variant */
\r
8478 EndDialog( hDlg, 1 );
\r
8480 case IDC_NFG_Edit:
\r
8481 if( HIWORD(wParam) == EN_CHANGE ) {
\r
8482 GetDlgItemInt(hDlg, IDC_NFG_Edit, &index_is_ok, TRUE );
\r
8484 EnableWindow( GetDlgItem(hDlg, IDOK), index_is_ok );
\r
8487 case IDC_NFG_Random:
\r
8488 snprintf( buf, sizeof(buf)/sizeof(buf[0]), "%d", myrandom() ); /* [HGM] shuffle: no longer limit to 960 */
\r
8489 SetDlgItemText(hDlg, IDC_NFG_Edit, buf );
\r
8502 int index = appData.defaultFrcPosition;
\r
8503 FARPROC lpProc = MakeProcInstance( (FARPROC) NewGameFRC_Proc, hInst );
\r
8505 result = DialogBoxParam( hInst, MAKEINTRESOURCE(DLG_NewGameFRC), hwndMain, (DLGPROC)lpProc, (LPARAM)&index );
\r
8507 if( result == 0 ) {
\r
8508 appData.defaultFrcPosition = index;
\r
8514 /* [AS] Game list options. Refactored by HGM */
\r
8516 HWND gameListOptionsDialog;
\r
8518 // low-level front-end: clear text edit / list widget
\r
8522 SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_RESETCONTENT, 0, 0 );
\r
8525 // low-level front-end: clear text edit / list widget
\r
8527 GLT_DeSelectList()
\r
8529 SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_SETCURSEL, 0, 0 );
\r
8532 // low-level front-end: append line to text edit / list widget
\r
8534 GLT_AddToList( char *name )
\r
8537 SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_ADDSTRING, 0, (LPARAM) name );
\r
8541 // low-level front-end: get line from text edit / list widget
\r
8543 GLT_GetFromList( int index, char *name )
\r
8546 if( SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_GETTEXT, index, (LPARAM) name ) != LB_ERR )
\r
8552 void GLT_MoveSelection( HWND hDlg, int delta )
\r
8554 int idx1 = (int) SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETCURSEL, 0, 0 );
\r
8555 int idx2 = idx1 + delta;
\r
8556 int count = (int) SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETCOUNT, 0, 0 );
\r
8558 if( idx1 >=0 && idx1 < count && idx2 >= 0 && idx2 < count ) {
\r
8561 SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETTEXT, idx1, (LPARAM) buf );
\r
8562 SendDlgItemMessage( hDlg, IDC_GameListTags, LB_DELETESTRING, idx1, 0 );
\r
8563 SendDlgItemMessage( hDlg, IDC_GameListTags, LB_INSERTSTRING, idx2, (LPARAM) buf );
\r
8564 SendDlgItemMessage( hDlg, IDC_GameListTags, LB_SETCURSEL, idx2, 0 );
\r
8568 LRESULT CALLBACK GameListOptions_Proc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
8572 case WM_INITDIALOG:
\r
8573 gameListOptionsDialog = hDlg; // [HGM] pass through global to keep out off back-end
\r
8575 CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));
\r
8576 Translate(hDlg, DLG_GameListOptions);
\r
8578 /* Initialize list */
\r
8579 GLT_TagsToList( lpUserGLT );
\r
8581 SetFocus( GetDlgItem(hDlg, IDC_GameListTags) );
\r
8586 switch( LOWORD(wParam) ) {
\r
8589 EndDialog( hDlg, 0 );
\r
8592 EndDialog( hDlg, 1 );
\r
8595 case IDC_GLT_Default:
\r
8596 GLT_TagsToList( GLT_DEFAULT_TAGS );
\r
8599 case IDC_GLT_Restore:
\r
8600 GLT_TagsToList( appData.gameListTags );
\r
8604 GLT_MoveSelection( hDlg, -1 );
\r
8607 case IDC_GLT_Down:
\r
8608 GLT_MoveSelection( hDlg, +1 );
\r
8618 int GameListOptions()
\r
8621 FARPROC lpProc = MakeProcInstance( (FARPROC) GameListOptions_Proc, hInst );
\r
8623 safeStrCpy( lpUserGLT, appData.gameListTags ,LPUSERGLT_SIZE );
\r
8625 result = DialogBoxParam( hInst, MAKEINTRESOURCE(DLG_GameListOptions), hwndMain, (DLGPROC)lpProc, (LPARAM)lpUserGLT );
\r
8627 if( result == 0 ) {
\r
8628 /* [AS] Memory leak here! */
\r
8629 appData.gameListTags = strdup( lpUserGLT );
\r
8636 DisplayIcsInteractionTitle(char *str)
\r
8638 char consoleTitle[MSG_SIZ];
\r
8640 snprintf(consoleTitle, MSG_SIZ, "%s: %s", szConsoleTitle, str);
\r
8641 SetWindowText(hwndConsole, consoleTitle);
\r
8643 if(appData.chatBoxes) { // [HGM] chat: open chat boxes
\r
8644 char buf[MSG_SIZ], *p = buf, *q;
\r
8645 safeStrCpy(buf, appData.chatBoxes, sizeof(buf)/sizeof(buf[0]) );
\r
8647 q = strchr(p, ';');
\r
8649 if(*p) ChatPopUp(p);
\r
8653 SetActiveWindow(hwndMain);
\r
8657 DrawPosition(int fullRedraw, Board board)
\r
8659 HDCDrawPosition(NULL, (BOOLEAN) fullRedraw, board);
\r
8662 void NotifyFrontendLogin()
\r
8665 UpdateICSWidth(GetDlgItem(hwndConsole, OPT_ConsoleText));
\r
8671 fromX = fromY = -1;
\r
8672 if (dragInfo.pos.x != -1 || dragInfo.pos.y != -1) {
\r
8673 dragInfo.pos.x = dragInfo.pos.y = -1;
\r
8674 dragInfo.pos.x = dragInfo.pos.y = -1;
\r
8675 dragInfo.lastpos = dragInfo.pos;
\r
8676 dragInfo.start.x = dragInfo.start.y = -1;
\r
8677 dragInfo.from = dragInfo.start;
\r
8679 DrawPosition(TRUE, NULL);
\r
8686 CommentPopUp(char *title, char *str)
\r
8688 HWND hwnd = GetActiveWindow();
\r
8689 EitherCommentPopUp(currentMove, title, str, FALSE); // [HGM] vari: fake move index, rather than 0
\r
8691 SetActiveWindow(hwnd);
\r
8695 CommentPopDown(void)
\r
8697 CheckMenuItem(GetMenu(hwndMain), IDM_Comment, MF_UNCHECKED);
\r
8698 if (commentDialog) {
\r
8699 ShowWindow(commentDialog, SW_HIDE);
\r
8701 commentUp = FALSE;
\r
8705 EditCommentPopUp(int index, char *title, char *str)
\r
8707 EitherCommentPopUp(index, title, str, TRUE);
\r
8714 MyPlaySound(&sounds[(int)SoundMove]);
\r
8717 VOID PlayIcsWinSound()
\r
8719 MyPlaySound(&sounds[(int)SoundIcsWin]);
\r
8722 VOID PlayIcsLossSound()
\r
8724 MyPlaySound(&sounds[(int)SoundIcsLoss]);
\r
8727 VOID PlayIcsDrawSound()
\r
8729 MyPlaySound(&sounds[(int)SoundIcsDraw]);
\r
8732 VOID PlayIcsUnfinishedSound()
\r
8734 MyPlaySound(&sounds[(int)SoundIcsUnfinished]);
\r
8740 MyPlaySound(&sounds[(int)SoundAlarm]);
\r
8746 MyPlaySound(&textAttribs[ColorTell].sound);
\r
8754 consoleEcho = TRUE;
\r
8755 hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
8756 SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&consoleCF);
\r
8757 SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);
\r
8766 consoleEcho = FALSE;
\r
8767 hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
8768 /* This works OK: set text and background both to the same color */
\r
8770 cf.crTextColor = COLOR_ECHOOFF;
\r
8771 SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf);
\r
8772 SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, cf.crTextColor);
\r
8775 /* No Raw()...? */
\r
8777 void Colorize(ColorClass cc, int continuation)
\r
8779 currentColorClass = cc;
\r
8780 consoleCF.dwMask = CFM_COLOR|CFM_BOLD|CFM_ITALIC|CFM_UNDERLINE|CFM_STRIKEOUT;
\r
8781 consoleCF.crTextColor = textAttribs[cc].color;
\r
8782 consoleCF.dwEffects = textAttribs[cc].effects;
\r
8783 if (!continuation) MyPlaySound(&textAttribs[cc].sound);
\r
8789 static char buf[MSG_SIZ];
\r
8790 DWORD bufsiz = MSG_SIZ;
\r
8792 if(appData.userName != NULL && appData.userName[0] != 0) {
\r
8793 return appData.userName; /* [HGM] username: prefer name selected by user over his system login */
\r
8795 if (!GetUserName(buf, &bufsiz)) {
\r
8796 /*DisplayError("Error getting user name", GetLastError());*/
\r
8797 safeStrCpy(buf, _("User"), sizeof(buf)/sizeof(buf[0]) );
\r
8805 static char buf[MSG_SIZ];
\r
8806 DWORD bufsiz = MSG_SIZ;
\r
8808 if (!GetComputerName(buf, &bufsiz)) {
\r
8809 /*DisplayError("Error getting host name", GetLastError());*/
\r
8810 safeStrCpy(buf, _("Unknown"), sizeof(buf)/sizeof(buf[0]) );
\r
8817 ClockTimerRunning()
\r
8819 return clockTimerEvent != 0;
\r
8825 if (clockTimerEvent == 0) return FALSE;
\r
8826 KillTimer(hwndMain, clockTimerEvent);
\r
8827 clockTimerEvent = 0;
\r
8832 StartClockTimer(long millisec)
\r
8834 clockTimerEvent = SetTimer(hwndMain, (UINT) CLOCK_TIMER_ID,
\r
8835 (UINT) millisec, NULL);
\r
8839 DisplayWhiteClock(long timeRemaining, int highlight)
\r
8842 char *flag = whiteFlag && gameMode == TwoMachinesPlay ? "(!)" : "";
\r
8844 if(appData.noGUI) return;
\r
8845 hdc = GetDC(hwndMain);
\r
8846 if (!IsIconic(hwndMain)) {
\r
8847 DisplayAClock(hdc, timeRemaining, highlight,
\r
8848 flipClock ? &blackRect : &whiteRect, _("White"), flag);
\r
8850 if (highlight && iconCurrent == iconBlack) {
\r
8851 iconCurrent = iconWhite;
\r
8852 PostMessage(hwndMain, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);
\r
8853 if (IsIconic(hwndMain)) {
\r
8854 DrawIcon(hdc, 2, 2, iconCurrent);
\r
8857 (void) ReleaseDC(hwndMain, hdc);
\r
8859 PostMessage(hwndConsole, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);
\r
8863 DisplayBlackClock(long timeRemaining, int highlight)
\r
8866 char *flag = blackFlag && gameMode == TwoMachinesPlay ? "(!)" : "";
\r
8868 if(appData.noGUI) return;
\r
8869 hdc = GetDC(hwndMain);
\r
8870 if (!IsIconic(hwndMain)) {
\r
8871 DisplayAClock(hdc, timeRemaining, highlight,
\r
8872 flipClock ? &whiteRect : &blackRect, _("Black"), flag);
\r
8874 if (highlight && iconCurrent == iconWhite) {
\r
8875 iconCurrent = iconBlack;
\r
8876 PostMessage(hwndMain, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);
\r
8877 if (IsIconic(hwndMain)) {
\r
8878 DrawIcon(hdc, 2, 2, iconCurrent);
\r
8881 (void) ReleaseDC(hwndMain, hdc);
\r
8883 PostMessage(hwndConsole, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);
\r
8888 LoadGameTimerRunning()
\r
8890 return loadGameTimerEvent != 0;
\r
8894 StopLoadGameTimer()
\r
8896 if (loadGameTimerEvent == 0) return FALSE;
\r
8897 KillTimer(hwndMain, loadGameTimerEvent);
\r
8898 loadGameTimerEvent = 0;
\r
8903 StartLoadGameTimer(long millisec)
\r
8905 loadGameTimerEvent = SetTimer(hwndMain, (UINT) LOAD_GAME_TIMER_ID,
\r
8906 (UINT) millisec, NULL);
\r
8914 char fileTitle[MSG_SIZ];
\r
8916 defName = DefaultFileName(appData.oldSaveStyle ? "gam" : "pgn");
\r
8917 f = OpenFileDialog(hwndMain, "a", defName,
\r
8918 appData.oldSaveStyle ? "gam" : "pgn",
\r
8920 _("Save Game to File"), NULL, fileTitle, NULL);
\r
8922 SaveGame(f, 0, "");
\r
8929 ScheduleDelayedEvent(DelayedEventCallback cb, long millisec)
\r
8931 if (delayedTimerEvent != 0) {
\r
8932 if (appData.debugMode && cb != delayedTimerCallback) { // [HGM] alive: not too much debug
\r
8933 fprintf(debugFP, "ScheduleDelayedEvent: event already scheduled\n");
\r
8935 KillTimer(hwndMain, delayedTimerEvent);
\r
8936 delayedTimerEvent = 0;
\r
8937 if(delayedTimerCallback != cb) // [HGM] alive: do not "flush" same event, just postpone it
\r
8938 delayedTimerCallback();
\r
8940 delayedTimerCallback = cb;
\r
8941 delayedTimerEvent = SetTimer(hwndMain, (UINT) DELAYED_TIMER_ID,
\r
8942 (UINT) millisec, NULL);
\r
8945 DelayedEventCallback
\r
8948 if (delayedTimerEvent) {
\r
8949 return delayedTimerCallback;
\r
8956 CancelDelayedEvent()
\r
8958 if (delayedTimerEvent) {
\r
8959 KillTimer(hwndMain, delayedTimerEvent);
\r
8960 delayedTimerEvent = 0;
\r
8964 DWORD GetWin32Priority(int nice)
\r
8965 { // [HGM] nice: translate Unix nice() value to indows priority class. (Code stolen from Polyglot 1.4w11)
\r
8967 REALTIME_PRIORITY_CLASS 0x00000100
\r
8968 HIGH_PRIORITY_CLASS 0x00000080
\r
8969 ABOVE_NORMAL_PRIORITY_CLASS 0x00008000
\r
8970 NORMAL_PRIORITY_CLASS 0x00000020
\r
8971 BELOW_NORMAL_PRIORITY_CLASS 0x00004000
\r
8972 IDLE_PRIORITY_CLASS 0x00000040
\r
8974 if (nice < -15) return 0x00000080;
\r
8975 if (nice < 0) return 0x00008000;
\r
8976 if (nice == 0) return 0x00000020;
\r
8977 if (nice < 15) return 0x00004000;
\r
8978 return 0x00000040;
\r
8981 void RunCommand(char *cmdLine)
\r
8983 /* Now create the child process. */
\r
8984 STARTUPINFO siStartInfo;
\r
8985 PROCESS_INFORMATION piProcInfo;
\r
8987 siStartInfo.cb = sizeof(STARTUPINFO);
\r
8988 siStartInfo.lpReserved = NULL;
\r
8989 siStartInfo.lpDesktop = NULL;
\r
8990 siStartInfo.lpTitle = NULL;
\r
8991 siStartInfo.dwFlags = STARTF_USESTDHANDLES;
\r
8992 siStartInfo.cbReserved2 = 0;
\r
8993 siStartInfo.lpReserved2 = NULL;
\r
8994 siStartInfo.hStdInput = NULL;
\r
8995 siStartInfo.hStdOutput = NULL;
\r
8996 siStartInfo.hStdError = NULL;
\r
8998 CreateProcess(NULL,
\r
8999 cmdLine, /* command line */
\r
9000 NULL, /* process security attributes */
\r
9001 NULL, /* primary thread security attrs */
\r
9002 TRUE, /* handles are inherited */
\r
9003 DETACHED_PROCESS|CREATE_NEW_PROCESS_GROUP,
\r
9004 NULL, /* use parent's environment */
\r
9006 &siStartInfo, /* STARTUPINFO pointer */
\r
9007 &piProcInfo); /* receives PROCESS_INFORMATION */
\r
9009 CloseHandle(piProcInfo.hThread);
\r
9012 /* Start a child process running the given program.
\r
9013 The process's standard output can be read from "from", and its
\r
9014 standard input can be written to "to".
\r
9015 Exit with fatal error if anything goes wrong.
\r
9016 Returns an opaque pointer that can be used to destroy the process
\r
9020 StartChildProcess(char *cmdLine, char *dir, ProcRef *pr)
\r
9022 #define BUFSIZE 4096
\r
9024 HANDLE hChildStdinRd, hChildStdinWr,
\r
9025 hChildStdoutRd, hChildStdoutWr;
\r
9026 HANDLE hChildStdinWrDup, hChildStdoutRdDup;
\r
9027 SECURITY_ATTRIBUTES saAttr;
\r
9029 PROCESS_INFORMATION piProcInfo;
\r
9030 STARTUPINFO siStartInfo;
\r
9032 char buf[MSG_SIZ];
\r
9035 if (appData.debugMode) {
\r
9036 fprintf(debugFP, "StartChildProcess (dir=\"%s\") %s\n", dir, cmdLine);
\r
9041 /* Set the bInheritHandle flag so pipe handles are inherited. */
\r
9042 saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);
\r
9043 saAttr.bInheritHandle = TRUE;
\r
9044 saAttr.lpSecurityDescriptor = NULL;
\r
9047 * The steps for redirecting child's STDOUT:
\r
9048 * 1. Create anonymous pipe to be STDOUT for child.
\r
9049 * 2. Create a noninheritable duplicate of read handle,
\r
9050 * and close the inheritable read handle.
\r
9053 /* Create a pipe for the child's STDOUT. */
\r
9054 if (! CreatePipe(&hChildStdoutRd, &hChildStdoutWr, &saAttr, 0)) {
\r
9055 return GetLastError();
\r
9058 /* Duplicate the read handle to the pipe, so it is not inherited. */
\r
9059 fSuccess = DuplicateHandle(GetCurrentProcess(), hChildStdoutRd,
\r
9060 GetCurrentProcess(), &hChildStdoutRdDup, 0,
\r
9061 FALSE, /* not inherited */
\r
9062 DUPLICATE_SAME_ACCESS);
\r
9064 return GetLastError();
\r
9066 CloseHandle(hChildStdoutRd);
\r
9069 * The steps for redirecting child's STDIN:
\r
9070 * 1. Create anonymous pipe to be STDIN for child.
\r
9071 * 2. Create a noninheritable duplicate of write handle,
\r
9072 * and close the inheritable write handle.
\r
9075 /* Create a pipe for the child's STDIN. */
\r
9076 if (! CreatePipe(&hChildStdinRd, &hChildStdinWr, &saAttr, 0)) {
\r
9077 return GetLastError();
\r
9080 /* Duplicate the write handle to the pipe, so it is not inherited. */
\r
9081 fSuccess = DuplicateHandle(GetCurrentProcess(), hChildStdinWr,
\r
9082 GetCurrentProcess(), &hChildStdinWrDup, 0,
\r
9083 FALSE, /* not inherited */
\r
9084 DUPLICATE_SAME_ACCESS);
\r
9086 return GetLastError();
\r
9088 CloseHandle(hChildStdinWr);
\r
9090 /* Arrange to (1) look in dir for the child .exe file, and
\r
9091 * (2) have dir be the child's working directory. Interpret
\r
9092 * dir relative to the directory WinBoard loaded from. */
\r
9093 GetCurrentDirectory(MSG_SIZ, buf);
\r
9094 SetCurrentDirectory(installDir);
\r
9095 SetCurrentDirectory(dir);
\r
9097 /* Now create the child process. */
\r
9099 siStartInfo.cb = sizeof(STARTUPINFO);
\r
9100 siStartInfo.lpReserved = NULL;
\r
9101 siStartInfo.lpDesktop = NULL;
\r
9102 siStartInfo.lpTitle = NULL;
\r
9103 siStartInfo.dwFlags = STARTF_USESTDHANDLES;
\r
9104 siStartInfo.cbReserved2 = 0;
\r
9105 siStartInfo.lpReserved2 = NULL;
\r
9106 siStartInfo.hStdInput = hChildStdinRd;
\r
9107 siStartInfo.hStdOutput = hChildStdoutWr;
\r
9108 siStartInfo.hStdError = hChildStdoutWr;
\r
9110 fSuccess = CreateProcess(NULL,
\r
9111 cmdLine, /* command line */
\r
9112 NULL, /* process security attributes */
\r
9113 NULL, /* primary thread security attrs */
\r
9114 TRUE, /* handles are inherited */
\r
9115 DETACHED_PROCESS|CREATE_NEW_PROCESS_GROUP,
\r
9116 NULL, /* use parent's environment */
\r
9118 &siStartInfo, /* STARTUPINFO pointer */
\r
9119 &piProcInfo); /* receives PROCESS_INFORMATION */
\r
9121 err = GetLastError();
\r
9122 SetCurrentDirectory(buf); /* return to prev directory */
\r
9127 if (appData.niceEngines){ // [HGM] nice: adjust engine proc priority
\r
9128 if(appData.debugMode) fprintf(debugFP, "nice engine proc to %d\n", appData.niceEngines);
\r
9129 SetPriorityClass(piProcInfo.hProcess, GetWin32Priority(appData.niceEngines));
\r
9132 /* Close the handles we don't need in the parent */
\r
9133 CloseHandle(piProcInfo.hThread);
\r
9134 CloseHandle(hChildStdinRd);
\r
9135 CloseHandle(hChildStdoutWr);
\r
9137 /* Prepare return value */
\r
9138 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
\r
9139 cp->kind = CPReal;
\r
9140 cp->hProcess = piProcInfo.hProcess;
\r
9141 cp->pid = piProcInfo.dwProcessId;
\r
9142 cp->hFrom = hChildStdoutRdDup;
\r
9143 cp->hTo = hChildStdinWrDup;
\r
9145 *pr = (void *) cp;
\r
9147 /* Klaus Friedel says that this Sleep solves a problem under Windows
\r
9148 2000 where engines sometimes don't see the initial command(s)
\r
9149 from WinBoard and hang. I don't understand how that can happen,
\r
9150 but the Sleep is harmless, so I've put it in. Others have also
\r
9151 reported what may be the same problem, so hopefully this will fix
\r
9152 it for them too. */
\r
9160 DestroyChildProcess(ProcRef pr, int/*boolean*/ signal)
\r
9162 ChildProc *cp; int result;
\r
9164 cp = (ChildProc *) pr;
\r
9165 if (cp == NULL) return;
\r
9167 switch (cp->kind) {
\r
9169 /* TerminateProcess is considered harmful, so... */
\r
9170 CloseHandle(cp->hTo); /* Closing this will give the child an EOF and hopefully kill it */
\r
9171 if (cp->hFrom) CloseHandle(cp->hFrom); /* if NULL, InputThread will close it */
\r
9172 /* The following doesn't work because the chess program
\r
9173 doesn't "have the same console" as WinBoard. Maybe
\r
9174 we could arrange for this even though neither WinBoard
\r
9175 nor the chess program uses a console for stdio? */
\r
9176 /*!!if (signal) GenerateConsoleCtrlEvent(CTRL_BREAK_EVENT, cp->pid);*/
\r
9178 /* [AS] Special termination modes for misbehaving programs... */
\r
9179 if( signal == 9 ) {
\r
9180 result = TerminateProcess( cp->hProcess, 0 );
\r
9182 if ( appData.debugMode) {
\r
9183 fprintf( debugFP, "Terminating process %lu, result=%d\n", cp->pid, result );
\r
9186 else if( signal == 10 ) {
\r
9187 DWORD dw = WaitForSingleObject( cp->hProcess, 3*1000 ); // Wait 3 seconds at most
\r
9189 if( dw != WAIT_OBJECT_0 ) {
\r
9190 result = TerminateProcess( cp->hProcess, 0 );
\r
9192 if ( appData.debugMode) {
\r
9193 fprintf( debugFP, "Process %lu still alive after timeout, killing... result=%d\n", cp->pid, result );
\r
9199 CloseHandle(cp->hProcess);
\r
9203 if (cp->hFrom) CloseHandle(cp->hFrom);
\r
9207 closesocket(cp->sock);
\r
9212 if (signal) send(cp->sock2, "\017", 1, 0); /* 017 = 15 = SIGTERM */
\r
9213 closesocket(cp->sock);
\r
9214 closesocket(cp->sock2);
\r
9222 InterruptChildProcess(ProcRef pr)
\r
9226 cp = (ChildProc *) pr;
\r
9227 if (cp == NULL) return;
\r
9228 switch (cp->kind) {
\r
9230 /* The following doesn't work because the chess program
\r
9231 doesn't "have the same console" as WinBoard. Maybe
\r
9232 we could arrange for this even though neither WinBoard
\r
9233 nor the chess program uses a console for stdio */
\r
9234 /*!!GenerateConsoleCtrlEvent(CTRL_C_EVENT, cp->pid);*/
\r
9239 /* Can't interrupt */
\r
9243 send(cp->sock2, "\002", 1, 0); /* 2 = SIGINT */
\r
9250 OpenTelnet(char *host, char *port, ProcRef *pr)
\r
9252 char cmdLine[MSG_SIZ];
\r
9254 if (port[0] == NULLCHAR) {
\r
9255 snprintf(cmdLine, MSG_SIZ, "%s %s", appData.telnetProgram, host);
\r
9257 snprintf(cmdLine, MSG_SIZ, "%s %s %s", appData.telnetProgram, host, port);
\r
9259 return StartChildProcess(cmdLine, "", pr);
\r
9263 /* Code to open TCP sockets */
\r
9266 OpenTCP(char *host, char *port, ProcRef *pr)
\r
9272 struct sockaddr_in sa, mysa;
\r
9273 struct hostent FAR *hp;
\r
9274 unsigned short uport;
\r
9275 WORD wVersionRequested;
\r
9278 /* Initialize socket DLL */
\r
9279 wVersionRequested = MAKEWORD(1, 1);
\r
9280 err = WSAStartup(wVersionRequested, &wsaData);
\r
9281 if (err != 0) return err;
\r
9284 if ((s = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {
\r
9285 err = WSAGetLastError();
\r
9290 /* Bind local address using (mostly) don't-care values.
\r
9292 memset((char *) &mysa, 0, sizeof(struct sockaddr_in));
\r
9293 mysa.sin_family = AF_INET;
\r
9294 mysa.sin_addr.s_addr = INADDR_ANY;
\r
9295 uport = (unsigned short) 0;
\r
9296 mysa.sin_port = htons(uport);
\r
9297 if (bind(s, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))
\r
9298 == SOCKET_ERROR) {
\r
9299 err = WSAGetLastError();
\r
9304 /* Resolve remote host name */
\r
9305 memset((char *) &sa, 0, sizeof(struct sockaddr_in));
\r
9306 if (!(hp = gethostbyname(host))) {
\r
9307 unsigned int b0, b1, b2, b3;
\r
9309 err = WSAGetLastError();
\r
9311 if (sscanf(host, "%u.%u.%u.%u", &b0, &b1, &b2, &b3) == 4) {
\r
9312 hp = (struct hostent *) calloc(1, sizeof(struct hostent));
\r
9313 hp->h_addrtype = AF_INET;
\r
9315 hp->h_addr_list = (char **) calloc(2, sizeof(char *));
\r
9316 hp->h_addr_list[0] = (char *) malloc(4);
\r
9317 hp->h_addr_list[0][0] = (char) b0;
\r
9318 hp->h_addr_list[0][1] = (char) b1;
\r
9319 hp->h_addr_list[0][2] = (char) b2;
\r
9320 hp->h_addr_list[0][3] = (char) b3;
\r
9326 sa.sin_family = hp->h_addrtype;
\r
9327 uport = (unsigned short) atoi(port);
\r
9328 sa.sin_port = htons(uport);
\r
9329 memcpy((char *) &sa.sin_addr, hp->h_addr, hp->h_length);
\r
9331 /* Make connection */
\r
9332 if (connect(s, (struct sockaddr *) &sa,
\r
9333 sizeof(struct sockaddr_in)) == SOCKET_ERROR) {
\r
9334 err = WSAGetLastError();
\r
9339 /* Prepare return value */
\r
9340 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
\r
9341 cp->kind = CPSock;
\r
9343 *pr = (ProcRef *) cp;
\r
9349 OpenCommPort(char *name, ProcRef *pr)
\r
9354 char fullname[MSG_SIZ];
\r
9356 if (*name != '\\')
\r
9357 snprintf(fullname, MSG_SIZ, "\\\\.\\%s", name);
\r
9359 safeStrCpy(fullname, name, sizeof(fullname)/sizeof(fullname[0]) );
\r
9361 h = CreateFile(name, GENERIC_READ | GENERIC_WRITE,
\r
9362 0, NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL);
\r
9363 if (h == (HANDLE) -1) {
\r
9364 return GetLastError();
\r
9368 if (!SetCommState(h, (LPDCB) &dcb)) return GetLastError();
\r
9370 /* Accumulate characters until a 100ms pause, then parse */
\r
9371 ct.ReadIntervalTimeout = 100;
\r
9372 ct.ReadTotalTimeoutMultiplier = 0;
\r
9373 ct.ReadTotalTimeoutConstant = 0;
\r
9374 ct.WriteTotalTimeoutMultiplier = 0;
\r
9375 ct.WriteTotalTimeoutConstant = 0;
\r
9376 if (!SetCommTimeouts(h, (LPCOMMTIMEOUTS) &ct)) return GetLastError();
\r
9378 /* Prepare return value */
\r
9379 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
\r
9380 cp->kind = CPComm;
\r
9383 *pr = (ProcRef *) cp;
\r
9389 OpenLoopback(ProcRef *pr)
\r
9391 DisplayFatalError(_("Not implemented"), 0, 1);
\r
9397 OpenRcmd(char* host, char* user, char* cmd, ProcRef* pr)
\r
9402 struct sockaddr_in sa, mysa;
\r
9403 struct hostent FAR *hp;
\r
9404 unsigned short uport;
\r
9405 WORD wVersionRequested;
\r
9408 char stderrPortStr[MSG_SIZ];
\r
9410 /* Initialize socket DLL */
\r
9411 wVersionRequested = MAKEWORD(1, 1);
\r
9412 err = WSAStartup(wVersionRequested, &wsaData);
\r
9413 if (err != 0) return err;
\r
9415 /* Resolve remote host name */
\r
9416 memset((char *) &sa, 0, sizeof(struct sockaddr_in));
\r
9417 if (!(hp = gethostbyname(host))) {
\r
9418 unsigned int b0, b1, b2, b3;
\r
9420 err = WSAGetLastError();
\r
9422 if (sscanf(host, "%u.%u.%u.%u", &b0, &b1, &b2, &b3) == 4) {
\r
9423 hp = (struct hostent *) calloc(1, sizeof(struct hostent));
\r
9424 hp->h_addrtype = AF_INET;
\r
9426 hp->h_addr_list = (char **) calloc(2, sizeof(char *));
\r
9427 hp->h_addr_list[0] = (char *) malloc(4);
\r
9428 hp->h_addr_list[0][0] = (char) b0;
\r
9429 hp->h_addr_list[0][1] = (char) b1;
\r
9430 hp->h_addr_list[0][2] = (char) b2;
\r
9431 hp->h_addr_list[0][3] = (char) b3;
\r
9437 sa.sin_family = hp->h_addrtype;
\r
9438 uport = (unsigned short) 514;
\r
9439 sa.sin_port = htons(uport);
\r
9440 memcpy((char *) &sa.sin_addr, hp->h_addr, hp->h_length);
\r
9442 /* Bind local socket to unused "privileged" port address
\r
9444 s = INVALID_SOCKET;
\r
9445 memset((char *) &mysa, 0, sizeof(struct sockaddr_in));
\r
9446 mysa.sin_family = AF_INET;
\r
9447 mysa.sin_addr.s_addr = INADDR_ANY;
\r
9448 for (fromPort = 1023;; fromPort--) {
\r
9449 if (fromPort < 0) {
\r
9451 return WSAEADDRINUSE;
\r
9453 if (s == INVALID_SOCKET) {
\r
9454 if ((s = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {
\r
9455 err = WSAGetLastError();
\r
9460 uport = (unsigned short) fromPort;
\r
9461 mysa.sin_port = htons(uport);
\r
9462 if (bind(s, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))
\r
9463 == SOCKET_ERROR) {
\r
9464 err = WSAGetLastError();
\r
9465 if (err == WSAEADDRINUSE) continue;
\r
9469 if (connect(s, (struct sockaddr *) &sa,
\r
9470 sizeof(struct sockaddr_in)) == SOCKET_ERROR) {
\r
9471 err = WSAGetLastError();
\r
9472 if (err == WSAEADDRINUSE) {
\r
9483 /* Bind stderr local socket to unused "privileged" port address
\r
9485 s2 = INVALID_SOCKET;
\r
9486 memset((char *) &mysa, 0, sizeof(struct sockaddr_in));
\r
9487 mysa.sin_family = AF_INET;
\r
9488 mysa.sin_addr.s_addr = INADDR_ANY;
\r
9489 for (fromPort = 1023;; fromPort--) {
\r
9490 if (fromPort == prevStderrPort) continue; // don't reuse port
\r
9491 if (fromPort < 0) {
\r
9492 (void) closesocket(s);
\r
9494 return WSAEADDRINUSE;
\r
9496 if (s2 == INVALID_SOCKET) {
\r
9497 if ((s2 = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {
\r
9498 err = WSAGetLastError();
\r
9504 uport = (unsigned short) fromPort;
\r
9505 mysa.sin_port = htons(uport);
\r
9506 if (bind(s2, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))
\r
9507 == SOCKET_ERROR) {
\r
9508 err = WSAGetLastError();
\r
9509 if (err == WSAEADDRINUSE) continue;
\r
9510 (void) closesocket(s);
\r
9514 if (listen(s2, 1) == SOCKET_ERROR) {
\r
9515 err = WSAGetLastError();
\r
9516 if (err == WSAEADDRINUSE) {
\r
9518 s2 = INVALID_SOCKET;
\r
9521 (void) closesocket(s);
\r
9522 (void) closesocket(s2);
\r
9528 prevStderrPort = fromPort; // remember port used
\r
9529 snprintf(stderrPortStr, MSG_SIZ, "%d", fromPort);
\r
9531 if (send(s, stderrPortStr, strlen(stderrPortStr) + 1, 0) == SOCKET_ERROR) {
\r
9532 err = WSAGetLastError();
\r
9533 (void) closesocket(s);
\r
9534 (void) closesocket(s2);
\r
9539 if (send(s, UserName(), strlen(UserName()) + 1, 0) == SOCKET_ERROR) {
\r
9540 err = WSAGetLastError();
\r
9541 (void) closesocket(s);
\r
9542 (void) closesocket(s2);
\r
9546 if (*user == NULLCHAR) user = UserName();
\r
9547 if (send(s, user, strlen(user) + 1, 0) == SOCKET_ERROR) {
\r
9548 err = WSAGetLastError();
\r
9549 (void) closesocket(s);
\r
9550 (void) closesocket(s2);
\r
9554 if (send(s, cmd, strlen(cmd) + 1, 0) == SOCKET_ERROR) {
\r
9555 err = WSAGetLastError();
\r
9556 (void) closesocket(s);
\r
9557 (void) closesocket(s2);
\r
9562 if ((s3 = accept(s2, NULL, NULL)) == INVALID_SOCKET) {
\r
9563 err = WSAGetLastError();
\r
9564 (void) closesocket(s);
\r
9565 (void) closesocket(s2);
\r
9569 (void) closesocket(s2); /* Stop listening */
\r
9571 /* Prepare return value */
\r
9572 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
\r
9573 cp->kind = CPRcmd;
\r
9576 *pr = (ProcRef *) cp;
\r
9583 AddInputSource(ProcRef pr, int lineByLine,
\r
9584 InputCallback func, VOIDSTAR closure)
\r
9586 InputSource *is, *is2 = NULL;
\r
9587 ChildProc *cp = (ChildProc *) pr;
\r
9589 is = (InputSource *) calloc(1, sizeof(InputSource));
\r
9590 is->lineByLine = lineByLine;
\r
9592 is->closure = closure;
\r
9593 is->second = NULL;
\r
9594 is->next = is->buf;
\r
9595 if (pr == NoProc) {
\r
9596 is->kind = CPReal;
\r
9597 consoleInputSource = is;
\r
9599 is->kind = cp->kind;
\r
9601 [AS] Try to avoid a race condition if the thread is given control too early:
\r
9602 we create all threads suspended so that the is->hThread variable can be
\r
9603 safely assigned, then let the threads start with ResumeThread.
\r
9605 switch (cp->kind) {
\r
9607 is->hFile = cp->hFrom;
\r
9608 cp->hFrom = NULL; /* now owned by InputThread */
\r
9610 CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) NonOvlInputThread,
\r
9611 (LPVOID) is, CREATE_SUSPENDED, &is->id);
\r
9615 is->hFile = cp->hFrom;
\r
9616 cp->hFrom = NULL; /* now owned by InputThread */
\r
9618 CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) InputThread,
\r
9619 (LPVOID) is, CREATE_SUSPENDED, &is->id);
\r
9623 is->sock = cp->sock;
\r
9625 CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,
\r
9626 (LPVOID) is, CREATE_SUSPENDED, &is->id);
\r
9630 is2 = (InputSource *) calloc(1, sizeof(InputSource));
\r
9632 is->sock = cp->sock;
\r
9634 is2->sock = cp->sock2;
\r
9635 is2->second = is2;
\r
9637 CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,
\r
9638 (LPVOID) is, CREATE_SUSPENDED, &is->id);
\r
9640 CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,
\r
9641 (LPVOID) is2, CREATE_SUSPENDED, &is2->id);
\r
9645 if( is->hThread != NULL ) {
\r
9646 ResumeThread( is->hThread );
\r
9649 if( is2 != NULL && is2->hThread != NULL ) {
\r
9650 ResumeThread( is2->hThread );
\r
9654 return (InputSourceRef) is;
\r
9658 RemoveInputSource(InputSourceRef isr)
\r
9662 is = (InputSource *) isr;
\r
9663 is->hThread = NULL; /* tell thread to stop */
\r
9664 CloseHandle(is->hThread);
\r
9665 if (is->second != NULL) {
\r
9666 is->second->hThread = NULL;
\r
9667 CloseHandle(is->second->hThread);
\r
9671 int no_wrap(char *message, int count)
\r
9673 ConsoleOutput(message, count, FALSE);
\r
9678 OutputToProcess(ProcRef pr, char *message, int count, int *outError)
\r
9681 int outCount = SOCKET_ERROR;
\r
9682 ChildProc *cp = (ChildProc *) pr;
\r
9683 static OVERLAPPED ovl;
\r
9684 static int line = 0;
\r
9688 if (appData.noJoin || !appData.useInternalWrap)
\r
9689 return no_wrap(message, count);
\r
9692 int width = get_term_width();
\r
9693 int len = wrap(NULL, message, count, width, &line);
\r
9694 char *msg = malloc(len);
\r
9698 return no_wrap(message, count);
\r
9701 dbgchk = wrap(msg, message, count, width, &line);
\r
9702 if (dbgchk != len && appData.debugMode)
\r
9703 fprintf(debugFP, "wrap(): dbgchk(%d) != len(%d)\n", dbgchk, len);
\r
9704 ConsoleOutput(msg, len, FALSE);
\r
9711 if (ovl.hEvent == NULL) {
\r
9712 ovl.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
\r
9714 ovl.Internal = ovl.InternalHigh = ovl.Offset = ovl.OffsetHigh = 0;
\r
9716 switch (cp->kind) {
\r
9719 outCount = send(cp->sock, message, count, 0);
\r
9720 if (outCount == SOCKET_ERROR) {
\r
9721 *outError = WSAGetLastError();
\r
9723 *outError = NO_ERROR;
\r
9728 if (WriteFile(((ChildProc *)pr)->hTo, message, count,
\r
9729 &dOutCount, NULL)) {
\r
9730 *outError = NO_ERROR;
\r
9731 outCount = (int) dOutCount;
\r
9733 *outError = GetLastError();
\r
9738 *outError = DoWriteFile(((ChildProc *)pr)->hTo, message, count,
\r
9739 &dOutCount, &ovl);
\r
9740 if (*outError == NO_ERROR) {
\r
9741 outCount = (int) dOutCount;
\r
9751 if(n != 0) Sleep(n);
\r
9755 OutputToProcessDelayed(ProcRef pr, char *message, int count, int *outError,
\r
9758 /* Ignore delay, not implemented for WinBoard */
\r
9759 return OutputToProcess(pr, message, count, outError);
\r
9764 CmailSigHandlerCallBack(InputSourceRef isr, VOIDSTAR closure,
\r
9765 char *buf, int count, int error)
\r
9767 DisplayFatalError(_("Not implemented"), 0, 1);
\r
9770 /* see wgamelist.c for Game List functions */
\r
9771 /* see wedittags.c for Edit Tags functions */
\r
9778 char buf[MSG_SIZ];
\r
9781 if (SearchPath(installDir, appData.icsLogon, NULL, MSG_SIZ, buf, &dummy)) {
\r
9782 f = fopen(buf, "r");
\r
9784 ProcessICSInitScript(f);
\r
9794 StartAnalysisClock()
\r
9796 if (analysisTimerEvent) return;
\r
9797 analysisTimerEvent = SetTimer(hwndMain, (UINT) ANALYSIS_TIMER_ID,
\r
9798 (UINT) 2000, NULL);
\r
9802 SetHighlights(int fromX, int fromY, int toX, int toY)
\r
9804 highlightInfo.sq[0].x = fromX;
\r
9805 highlightInfo.sq[0].y = fromY;
\r
9806 highlightInfo.sq[1].x = toX;
\r
9807 highlightInfo.sq[1].y = toY;
\r
9813 highlightInfo.sq[0].x = highlightInfo.sq[0].y =
\r
9814 highlightInfo.sq[1].x = highlightInfo.sq[1].y = -1;
\r
9818 SetPremoveHighlights(int fromX, int fromY, int toX, int toY)
\r
9820 premoveHighlightInfo.sq[0].x = fromX;
\r
9821 premoveHighlightInfo.sq[0].y = fromY;
\r
9822 premoveHighlightInfo.sq[1].x = toX;
\r
9823 premoveHighlightInfo.sq[1].y = toY;
\r
9827 ClearPremoveHighlights()
\r
9829 premoveHighlightInfo.sq[0].x = premoveHighlightInfo.sq[0].y =
\r
9830 premoveHighlightInfo.sq[1].x = premoveHighlightInfo.sq[1].y = -1;
\r
9834 ShutDownFrontEnd()
\r
9836 if (saveSettingsOnExit) SaveSettings(settingsFileName);
\r
9837 DeleteClipboardTempFiles();
\r
9843 if (IsIconic(hwndMain))
\r
9844 ShowWindow(hwndMain, SW_RESTORE);
\r
9846 SetActiveWindow(hwndMain);
\r
9850 * Prototypes for animation support routines
\r
9852 static void ScreenSquare(int column, int row, POINT * pt);
\r
9853 static void Tween( POINT * start, POINT * mid, POINT * finish, int factor,
\r
9854 POINT frames[], int * nFrames);
\r
9860 AnimateAtomicCapture(Board board, int fromX, int fromY, int toX, int toY)
\r
9861 { // [HGM] atomic: animate blast wave
\r
9864 explodeInfo.fromX = fromX;
\r
9865 explodeInfo.fromY = fromY;
\r
9866 explodeInfo.toX = toX;
\r
9867 explodeInfo.toY = toY;
\r
9868 for(i=1; i<4*kFactor; i++) {
\r
9869 explodeInfo.radius = (i*180)/(4*kFactor-1);
\r
9870 DrawPosition(FALSE, board);
\r
9871 Sleep(appData.animSpeed);
\r
9873 explodeInfo.radius = 0;
\r
9874 DrawPosition(TRUE, board);
\r
9878 AnimateMove(board, fromX, fromY, toX, toY)
\r
9885 ChessSquare piece;
\r
9886 POINT start, finish, mid;
\r
9887 POINT frames[kFactor * 2 + 1];
\r
9890 if (!appData.animate) return;
\r
9891 if (doingSizing) return;
\r
9892 if (fromY < 0 || fromX < 0) return;
\r
9893 piece = board[fromY][fromX];
\r
9894 if (piece >= EmptySquare) return;
\r
9896 ScreenSquare(fromX, fromY, &start);
\r
9897 ScreenSquare(toX, toY, &finish);
\r
9899 /* All moves except knight jumps move in straight line */
\r
9900 if (!(abs(fromX-toX) == 1 && abs(fromY-toY) == 2 || abs(fromX-toX) == 2 && abs(fromY-toY) == 1)) {
\r
9901 mid.x = start.x + (finish.x - start.x) / 2;
\r
9902 mid.y = start.y + (finish.y - start.y) / 2;
\r
9904 /* Knight: make straight movement then diagonal */
\r
9905 if (abs(toY - fromY) < abs(toX - fromX)) {
\r
9906 mid.x = start.x + (finish.x - start.x) / 2;
\r
9910 mid.y = start.y + (finish.y - start.y) / 2;
\r
9914 /* Don't use as many frames for very short moves */
\r
9915 if (abs(toY - fromY) + abs(toX - fromX) <= 2)
\r
9916 Tween(&start, &mid, &finish, kFactor - 1, frames, &nFrames);
\r
9918 Tween(&start, &mid, &finish, kFactor, frames, &nFrames);
\r
9920 animInfo.from.x = fromX;
\r
9921 animInfo.from.y = fromY;
\r
9922 animInfo.to.x = toX;
\r
9923 animInfo.to.y = toY;
\r
9924 animInfo.lastpos = start;
\r
9925 animInfo.piece = piece;
\r
9926 for (n = 0; n < nFrames; n++) {
\r
9927 animInfo.pos = frames[n];
\r
9928 DrawPosition(FALSE, NULL);
\r
9929 animInfo.lastpos = animInfo.pos;
\r
9930 Sleep(appData.animSpeed);
\r
9932 animInfo.pos = finish;
\r
9933 DrawPosition(FALSE, NULL);
\r
9934 animInfo.piece = EmptySquare;
\r
9935 Explode(board, fromX, fromY, toX, toY);
\r
9938 /* Convert board position to corner of screen rect and color */
\r
9941 ScreenSquare(column, row, pt)
\r
9942 int column; int row; POINT * pt;
\r
9945 pt->x = lineGap + ((BOARD_WIDTH-1)-column) * (squareSize + lineGap) + border;
\r
9946 pt->y = lineGap + row * (squareSize + lineGap) + border;
\r
9948 pt->x = lineGap + column * (squareSize + lineGap) + border;
\r
9949 pt->y = lineGap + ((BOARD_HEIGHT-1)-row) * (squareSize + lineGap) + border;
\r
9953 /* Generate a series of frame coords from start->mid->finish.
\r
9954 The movement rate doubles until the half way point is
\r
9955 reached, then halves back down to the final destination,
\r
9956 which gives a nice slow in/out effect. The algorithmn
\r
9957 may seem to generate too many intermediates for short
\r
9958 moves, but remember that the purpose is to attract the
\r
9959 viewers attention to the piece about to be moved and
\r
9960 then to where it ends up. Too few frames would be less
\r
9964 Tween(start, mid, finish, factor, frames, nFrames)
\r
9965 POINT * start; POINT * mid;
\r
9966 POINT * finish; int factor;
\r
9967 POINT frames[]; int * nFrames;
\r
9969 int n, fraction = 1, count = 0;
\r
9971 /* Slow in, stepping 1/16th, then 1/8th, ... */
\r
9972 for (n = 0; n < factor; n++)
\r
9974 for (n = 0; n < factor; n++) {
\r
9975 frames[count].x = start->x + (mid->x - start->x) / fraction;
\r
9976 frames[count].y = start->y + (mid->y - start->y) / fraction;
\r
9978 fraction = fraction / 2;
\r
9982 frames[count] = *mid;
\r
9985 /* Slow out, stepping 1/2, then 1/4, ... */
\r
9987 for (n = 0; n < factor; n++) {
\r
9988 frames[count].x = finish->x - (finish->x - mid->x) / fraction;
\r
9989 frames[count].y = finish->y - (finish->y - mid->y) / fraction;
\r
9991 fraction = fraction * 2;
\r
9997 SettingsPopUp(ChessProgramState *cps)
\r
9998 { // [HGM] wrapper needed because handles must not be passed through back-end
\r
9999 EngineOptionsPopup(savedHwnd, cps);
\r
10002 int flock(int fid, int code)
\r
10004 HANDLE hFile = (HANDLE) _get_osfhandle(fid);
\r
10006 ov.hEvent = NULL;
\r
10008 ov.OffsetHigh = 0;
\r
10010 case 1: LockFileEx(hFile, LOCKFILE_EXCLUSIVE_LOCK, 0, 1024, 0, &ov); break; // LOCK_SH
\r
10011 case 2: LockFileEx(hFile, LOCKFILE_EXCLUSIVE_LOCK, 0, 1024, 0, &ov); break; // LOCK_EX
\r
10012 case 3: UnlockFileEx(hFile, 0, 1024, 0, &ov); break; // LOCK_UN
\r
10013 default: return -1;
\r
10022 static char col[8][20];
\r
10023 COLORREF color = *(COLORREF *) colorVariable[n];
\r
10025 snprintf(col[i], 20, "#%02lx%02lx%02lx", color&0xff, (color>>8)&0xff, (color>>16)&0xff);
\r
10030 ActivateTheme (int new)
\r
10031 { // Redo initialization of features depending on options that can occur in themes
\r
10033 if(new) InitDrawingColors();
\r
10034 fontBitmapSquareSize = 0; // request creation of new font pieces
\r
10035 InitDrawingSizes(-2, 0);
\r
10036 InvalidateRect(hwndMain, NULL, TRUE);
\r