2 * WinBoard.c -- Windows NT front end to XBoard
\r
4 * Copyright 1991 by Digital Equipment Corporation, Maynard,
\r
7 * Enhancements Copyright 1992-2001, 2002, 2003, 2004, 2005, 2006,
\r
8 * 2007, 2008, 2009, 2010, 2011, 2012, 2013 Free Software Foundation, Inc.
\r
10 * Enhancements Copyright 2005 Alessandro Scotti
\r
12 * XBoard borrows its colors and the bitmaps.xchess bitmap set from XChess,
\r
13 * which was written and is copyrighted by Wayne Christopher.
\r
15 * The following terms apply to Digital Equipment Corporation's copyright
\r
16 * interest in XBoard:
\r
17 * ------------------------------------------------------------------------
\r
18 * All Rights Reserved
\r
20 * Permission to use, copy, modify, and distribute this software and its
\r
21 * documentation for any purpose and without fee is hereby granted,
\r
22 * provided that the above copyright notice appear in all copies and that
\r
23 * both that copyright notice and this permission notice appear in
\r
24 * supporting documentation, and that the name of Digital not be
\r
25 * used in advertising or publicity pertaining to distribution of the
\r
26 * software without specific, written prior permission.
\r
28 * DIGITAL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
\r
29 * ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL
\r
30 * DIGITAL BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
\r
31 * ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
\r
32 * WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
\r
33 * ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
\r
35 * ------------------------------------------------------------------------
\r
37 * The following terms apply to the enhanced version of XBoard
\r
38 * distributed by the Free Software Foundation:
\r
39 * ------------------------------------------------------------------------
\r
41 * GNU XBoard is free software: you can redistribute it and/or modify
\r
42 * it under the terms of the GNU General Public License as published by
\r
43 * the Free Software Foundation, either version 3 of the License, or (at
\r
44 * your option) any later version.
\r
46 * GNU XBoard is distributed in the hope that it will be useful, but
\r
47 * WITHOUT ANY WARRANTY; without even the implied warranty of
\r
48 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
\r
49 * General Public License for more details.
\r
51 * You should have received a copy of the GNU General Public License
\r
52 * along with this program. If not, see http://www.gnu.org/licenses/. *
\r
54 *------------------------------------------------------------------------
\r
55 ** See the file ChangeLog for a revision history. */
\r
59 #include <windows.h>
\r
60 #include <winuser.h>
\r
61 #include <winsock.h>
\r
62 #include <commctrl.h>
\r
68 #include <sys/stat.h>
\r
71 #include <commdlg.h>
\r
73 #include <richedit.h>
\r
74 #include <mmsystem.h>
\r
84 #include "frontend.h"
\r
85 #include "backend.h"
\r
86 #include "winboard.h"
\r
88 #include "wclipbrd.h"
\r
89 #include "woptions.h"
\r
90 #include "wsockerr.h"
\r
91 #include "defaults.h"
\r
95 //void InitEngineUCI( const char * iniDir, ChessProgramState * cps );
\r
98 void mysrandom(unsigned int seed);
\r
100 extern int whiteFlag, blackFlag;
\r
101 Boolean flipClock = FALSE;
\r
102 extern HANDLE chatHandle[];
\r
103 extern enum ICS_TYPE ics_type;
\r
105 int MySearchPath P((char *installDir, char *name, char *fullname));
\r
106 int MyGetFullPathName P((char *name, char *fullname));
\r
107 void DisplayHoldingsCount(HDC hdc, int x, int y, int align, int copyNumber);
\r
108 VOID NewVariantPopup(HWND hwnd);
\r
109 int FinishMove P((ChessMove moveType, int fromX, int fromY, int toX, int toY,
\r
110 /*char*/int promoChar));
\r
111 void DisplayMove P((int moveNumber));
\r
112 Boolean ParseFEN P((Board board, int *blackPlaysFirst, char *fen));
\r
113 void ChatPopUp P((char *s));
\r
115 ChessSquare piece;
\r
116 POINT pos; /* window coordinates of current pos */
\r
117 POINT lastpos; /* window coordinates of last pos - used for clipping */
\r
118 POINT from; /* board coordinates of the piece's orig pos */
\r
119 POINT to; /* board coordinates of the piece's new pos */
\r
122 static AnimInfo animInfo = { EmptySquare, {-1,-1}, {-1,-1}, {-1,-1} };
\r
125 POINT start; /* window coordinates of start pos */
\r
126 POINT pos; /* window coordinates of current pos */
\r
127 POINT lastpos; /* window coordinates of last pos - used for clipping */
\r
128 POINT from; /* board coordinates of the piece's orig pos */
\r
132 static DragInfo dragInfo = { {-1,-1}, {-1,-1}, {-1,-1}, {-1,-1}, EmptySquare };
\r
135 POINT sq[2]; /* board coordinates of from, to squares */
\r
138 static HighlightInfo highlightInfo = { {{-1, -1}, {-1, -1}} };
\r
139 static HighlightInfo premoveHighlightInfo = { {{-1, -1}, {-1, -1}} };
\r
140 static HighlightInfo partnerHighlightInfo = { {{-1, -1}, {-1, -1}} };
\r
141 static HighlightInfo oldPartnerHighlight = { {{-1, -1}, {-1, -1}} };
\r
143 typedef struct { // [HGM] atomic
\r
144 int fromX, fromY, toX, toY, radius;
\r
147 static ExplodeInfo explodeInfo;
\r
149 /* Window class names */
\r
150 char szAppName[] = "WinBoard";
\r
151 char szConsoleName[] = "WBConsole";
\r
153 /* Title bar text */
\r
154 char szTitle[] = "WinBoard";
\r
155 char szConsoleTitle[] = "I C S Interaction";
\r
158 char *settingsFileName;
\r
159 Boolean saveSettingsOnExit;
\r
160 char installDir[MSG_SIZ];
\r
161 int errorExitStatus;
\r
163 BoardSize boardSize;
\r
164 Boolean chessProgram;
\r
165 //static int boardX, boardY;
\r
166 int minX, minY; // [HGM] placement: volatile limits on upper-left corner
\r
167 int squareSize, lineGap, minorSize, border;
\r
168 static int winW, winH;
\r
169 static RECT messageRect, whiteRect, blackRect, leftLogoRect, rightLogoRect; // [HGM] logo
\r
170 static int logoHeight = 0;
\r
171 static char messageText[MESSAGE_TEXT_MAX];
\r
172 static int clockTimerEvent = 0;
\r
173 static int loadGameTimerEvent = 0;
\r
174 static int analysisTimerEvent = 0;
\r
175 static DelayedEventCallback delayedTimerCallback;
\r
176 static int delayedTimerEvent = 0;
\r
177 static int buttonCount = 2;
\r
178 char *icsTextMenuString;
\r
180 char *firstChessProgramNames;
\r
181 char *secondChessProgramNames;
\r
183 #define PALETTESIZE 256
\r
185 HINSTANCE hInst; /* current instance */
\r
186 Boolean alwaysOnTop = FALSE;
\r
188 COLORREF lightSquareColor, darkSquareColor, whitePieceColor,
\r
189 blackPieceColor, highlightSquareColor, premoveHighlightColor;
\r
191 ColorClass currentColorClass;
\r
193 static HWND savedHwnd;
\r
194 HWND hCommPort = NULL; /* currently open comm port */
\r
195 static HWND hwndPause; /* pause button */
\r
196 static HBITMAP pieceBitmap[3][(int) BlackPawn]; /* [HGM] nr of bitmaps referred to bP in stead of wK */
\r
197 static HBRUSH lightSquareBrush, darkSquareBrush,
\r
198 blackSquareBrush, /* [HGM] for band between board and holdings */
\r
199 explodeBrush, /* [HGM] atomic */
\r
200 markerBrush, /* [HGM] markers */
\r
201 whitePieceBrush, blackPieceBrush, iconBkgndBrush /*, outlineBrush*/;
\r
202 static POINT gridEndpoints[(BOARD_RANKS + BOARD_FILES + 2) * 2];
\r
203 static DWORD gridVertexCounts[BOARD_RANKS + BOARD_FILES + 2];
\r
204 static HPEN gridPen = NULL;
\r
205 static HPEN highlightPen = NULL;
\r
206 static HPEN premovePen = NULL;
\r
207 static NPLOGPALETTE pLogPal;
\r
208 static BOOL paletteChanged = FALSE;
\r
209 static HICON iconWhite, iconBlack, iconCurrent;
\r
210 static int doingSizing = FALSE;
\r
211 static int lastSizing = 0;
\r
212 static int prevStderrPort;
\r
213 static HBITMAP userLogo;
\r
215 static HBITMAP liteBackTexture = NULL;
\r
216 static HBITMAP darkBackTexture = NULL;
\r
217 static int liteBackTextureMode = BACK_TEXTURE_MODE_PLAIN;
\r
218 static int darkBackTextureMode = BACK_TEXTURE_MODE_PLAIN;
\r
219 static int backTextureSquareSize = 0;
\r
220 static struct { int x; int y; int mode; } backTextureSquareInfo[BOARD_RANKS][BOARD_FILES];
\r
222 #if __GNUC__ && !defined(_winmajor)
\r
223 #define oldDialog 0 /* cygwin doesn't define _winmajor; mingw does */
\r
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
1066 SetCurrentDirectory(installDir);
\r
1068 GetCurrentDirectory(MSG_SIZ, installDir);
\r
1070 gameInfo.boardWidth = gameInfo.boardHeight = 8; // [HGM] won't have open window otherwise
\r
1071 screenWidth = screenHeight = 1000; // [HGM] placement: kludge to allow calling EnsureOnScreen from InitAppData
\r
1072 InitAppData(lpCmdLine); /* Get run-time parameters */
\r
1073 /* xboard, and older WinBoards, controlled the move sound with the
\r
1074 appData.ringBellAfterMoves option. In the current WinBoard, we
\r
1075 always turn the option on (so that the backend will call us),
\r
1076 then let the user turn the sound off by setting it to silence if
\r
1077 desired. To accommodate old winboard.ini files saved by old
\r
1078 versions of WinBoard, we also turn off the sound if the option
\r
1079 was initially set to false. [HGM] taken out of InitAppData */
\r
1080 if (!appData.ringBellAfterMoves) {
\r
1081 sounds[(int)SoundMove].name = strdup("");
\r
1082 appData.ringBellAfterMoves = TRUE;
\r
1084 if (appData.debugMode) {
\r
1085 debugFP = fopen(appData.nameOfDebugFile, "w");
\r
1086 setbuf(debugFP, NULL);
\r
1089 LoadLanguageFile(appData.language);
\r
1093 // InitEngineUCI( installDir, &first ); // [HGM] incorporated in InitBackEnd1()
\r
1094 // InitEngineUCI( installDir, &second );
\r
1096 /* Create a main window for this application instance. */
\r
1097 hwnd = CreateWindow(szAppName, szTitle,
\r
1098 (WS_OVERLAPPEDWINDOW & ~WS_MAXIMIZEBOX),
\r
1099 CW_USEDEFAULT, 0, CW_USEDEFAULT, 0,
\r
1100 NULL, NULL, hInstance, NULL);
\r
1103 /* If window could not be created, return "failure" */
\r
1108 /* [HGM] logo: Load logos if specified (must be done before InitDrawingSizes) */
\r
1109 LoadLogo(&first, 0, FALSE);
\r
1110 LoadLogo(&second, 1, appData.icsActive);
\r
1114 iconWhite = LoadIcon(hInstance, "icon_white");
\r
1115 iconBlack = LoadIcon(hInstance, "icon_black");
\r
1116 iconCurrent = iconWhite;
\r
1117 InitDrawingColors();
\r
1118 screenHeight = GetSystemMetrics(SM_CYSCREEN);
\r
1119 screenWidth = GetSystemMetrics(SM_CXSCREEN);
\r
1120 for (ibs = (int) NUM_SIZES - 1; ibs >= 0; ibs--) {
\r
1121 /* Compute window size for each board size, and use the largest
\r
1122 size that fits on this screen as the default. */
\r
1123 InitDrawingSizes((BoardSize)(ibs+1000), 0);
\r
1124 if (boardSize == (BoardSize)-1 &&
\r
1125 winH <= screenHeight
\r
1126 - GetSystemMetrics(SM_CYFRAME) - GetSystemMetrics(SM_CYCAPTION) - 10
\r
1127 && winW <= screenWidth) {
\r
1128 boardSize = (BoardSize)ibs;
\r
1132 InitDrawingSizes(boardSize, 0);
\r
1133 RecentEngineMenu(appData.recentEngineList);
\r
1135 buttonCount = GetSystemMetrics(SM_CMOUSEBUTTONS);
\r
1137 /* [AS] Load textures if specified */
\r
1140 mysrandom( (unsigned) time(NULL) );
\r
1142 /* [AS] Restore layout */
\r
1143 if( wpMoveHistory.visible ) {
\r
1144 MoveHistoryPopUp();
\r
1147 if( wpEvalGraph.visible ) {
\r
1151 if( wpEngineOutput.visible ) {
\r
1152 EngineOutputPopUp();
\r
1155 /* Make the window visible; update its client area; and return "success" */
\r
1156 EnsureOnScreen(&wpMain.x, &wpMain.y, minX, minY);
\r
1157 wp.length = sizeof(WINDOWPLACEMENT);
\r
1159 wp.showCmd = nCmdShow;
\r
1160 wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;
\r
1161 wp.rcNormalPosition.left = wpMain.x;
\r
1162 wp.rcNormalPosition.right = wpMain.x + wpMain.width;
\r
1163 wp.rcNormalPosition.top = wpMain.y;
\r
1164 wp.rcNormalPosition.bottom = wpMain.y + wpMain.height;
\r
1165 SetWindowPlacement(hwndMain, &wp);
\r
1167 InitBackEnd2(); // [HGM] moved until after all windows placed, to save correct position if fatal error on engine start
\r
1169 if(!appData.noGUI) SetWindowPos(hwndMain, alwaysOnTop ? HWND_TOPMOST : HWND_NOTOPMOST,
\r
1170 0, 0, 0, 0, SWP_NOMOVE|SWP_NOSIZE);
\r
1172 if (hwndConsole) {
\r
1174 SetWindowPos(hwndConsole, alwaysOnTop ? HWND_TOPMOST : HWND_NOTOPMOST,
\r
1175 0, 0, 0, 0, SWP_NOMOVE|SWP_NOSIZE);
\r
1177 ShowWindow(hwndConsole, nCmdShow);
\r
1178 SetActiveWindow(hwndConsole);
\r
1180 if(!appData.noGUI) UpdateWindow(hwnd); else ShowWindow(hwnd, SW_MINIMIZE);
\r
1181 if(gameListDialog) SetFocus(gameListDialog); // [HGM] jaws: for if we clicked multi-game game file
\r
1190 HMENU hmenu = GetMenu(hwndMain);
\r
1192 (void) EnableMenuItem(hmenu, IDM_CommPort,
\r
1193 MF_BYCOMMAND|((appData.icsActive &&
\r
1194 *appData.icsCommPort != NULLCHAR) ?
\r
1195 MF_ENABLED : MF_GRAYED));
\r
1196 (void) CheckMenuItem(hmenu, IDM_SaveSettingsOnExit,
\r
1197 MF_BYCOMMAND|(saveSettingsOnExit ?
\r
1198 MF_CHECKED : MF_UNCHECKED));
\r
1201 //---------------------------------------------------------------------------------------------------------
\r
1203 #define ICS_TEXT_MENU_SIZE (IDM_CommandXLast - IDM_CommandX + 1)
\r
1204 #define XBOARD FALSE
\r
1206 #define OPTCHAR "/"
\r
1207 #define SEPCHAR "="
\r
1208 #define TOPLEVEL 0
\r
1212 // front-end part of option handling
\r
1215 LFfromMFP(LOGFONT* lf, MyFontParams *mfp)
\r
1217 HDC hdc = CreateDC("DISPLAY", NULL, NULL, NULL);
\r
1218 lf->lfHeight = -(int)(mfp->pointSize * GetDeviceCaps(hdc, LOGPIXELSY) / 72.0 + 0.5);
\r
1221 lf->lfEscapement = 0;
\r
1222 lf->lfOrientation = 0;
\r
1223 lf->lfWeight = mfp->bold ? FW_BOLD : FW_NORMAL;
\r
1224 lf->lfItalic = mfp->italic;
\r
1225 lf->lfUnderline = mfp->underline;
\r
1226 lf->lfStrikeOut = mfp->strikeout;
\r
1227 lf->lfCharSet = mfp->charset;
\r
1228 lf->lfOutPrecision = OUT_DEFAULT_PRECIS;
\r
1229 lf->lfClipPrecision = CLIP_DEFAULT_PRECIS;
\r
1230 lf->lfQuality = DEFAULT_QUALITY;
\r
1231 lf->lfPitchAndFamily = DEFAULT_PITCH|FF_DONTCARE;
\r
1232 safeStrCpy(lf->lfFaceName, mfp->faceName, sizeof(lf->lfFaceName)/sizeof(lf->lfFaceName[0]) );
\r
1236 CreateFontInMF(MyFont *mf)
\r
1238 LFfromMFP(&mf->lf, &mf->mfp);
\r
1239 if (mf->hf) DeleteObject(mf->hf);
\r
1240 mf->hf = CreateFontIndirect(&mf->lf);
\r
1243 // [HGM] This platform-dependent table provides the location for storing the color info
\r
1245 colorVariable[] = {
\r
1246 &whitePieceColor,
\r
1247 &blackPieceColor,
\r
1248 &lightSquareColor,
\r
1249 &darkSquareColor,
\r
1250 &highlightSquareColor,
\r
1251 &premoveHighlightColor,
\r
1253 &consoleBackgroundColor,
\r
1254 &appData.fontForeColorWhite,
\r
1255 &appData.fontBackColorWhite,
\r
1256 &appData.fontForeColorBlack,
\r
1257 &appData.fontBackColorBlack,
\r
1258 &appData.evalHistColorWhite,
\r
1259 &appData.evalHistColorBlack,
\r
1260 &appData.highlightArrowColor,
\r
1263 /* Command line font name parser. NULL name means do nothing.
\r
1264 Syntax like "Courier New:10.0 bi" or "Arial:10" or "Arial:10b"
\r
1265 For backward compatibility, syntax without the colon is also
\r
1266 accepted, but font names with digits in them won't work in that case.
\r
1269 ParseFontName(char *name, MyFontParams *mfp)
\r
1272 if (name == NULL) return;
\r
1274 q = strchr(p, ':');
\r
1276 if (q - p >= sizeof(mfp->faceName))
\r
1277 ExitArgError(_("Font name too long:"), name, TRUE);
\r
1278 memcpy(mfp->faceName, p, q - p);
\r
1279 mfp->faceName[q - p] = NULLCHAR;
\r
1282 q = mfp->faceName;
\r
1284 while (*p && !isdigit(*p)) {
\r
1286 if (q - mfp->faceName >= sizeof(mfp->faceName))
\r
1287 ExitArgError(_("Font name too long:"), name, TRUE);
\r
1289 while (q > mfp->faceName && q[-1] == ' ') q--;
\r
1292 if (!*p) ExitArgError(_("Font point size missing:"), name, TRUE);
\r
1293 mfp->pointSize = (float) atof(p);
\r
1294 mfp->bold = (strchr(p, 'b') != NULL);
\r
1295 mfp->italic = (strchr(p, 'i') != NULL);
\r
1296 mfp->underline = (strchr(p, 'u') != NULL);
\r
1297 mfp->strikeout = (strchr(p, 's') != NULL);
\r
1298 mfp->charset = DEFAULT_CHARSET;
\r
1299 q = strchr(p, 'c');
\r
1301 mfp->charset = (BYTE) atoi(q+1);
\r
1305 ParseFont(char *name, int number)
\r
1306 { // wrapper to shield back-end from 'font'
\r
1307 ParseFontName(name, &font[boardSize][number]->mfp);
\r
1312 { // in WB we have a 2D array of fonts; this initializes their description
\r
1314 /* Point font array elements to structures and
\r
1315 parse default font names */
\r
1316 for (i=0; i<NUM_FONTS; i++) {
\r
1317 for (j=0; j<NUM_SIZES; j++) {
\r
1318 font[j][i] = &fontRec[j][i];
\r
1319 ParseFontName(font[j][i]->def, &font[j][i]->mfp);
\r
1326 { // here we create the actual fonts from the selected descriptions
\r
1328 for (i=0; i<NUM_FONTS; i++) {
\r
1329 for (j=0; j<NUM_SIZES; j++) {
\r
1330 CreateFontInMF(font[j][i]);
\r
1334 /* Color name parser.
\r
1335 X version accepts X color names, but this one
\r
1336 handles only the #rrggbb form (hex) or rrr,ggg,bbb (decimal) */
\r
1338 ParseColorName(char *name)
\r
1340 int red, green, blue, count;
\r
1341 char buf[MSG_SIZ];
\r
1343 count = sscanf(name, "#%2x%2x%2x", &red, &green, &blue);
\r
1345 count = sscanf(name, "%3d%*[^0-9]%3d%*[^0-9]%3d",
\r
1346 &red, &green, &blue);
\r
1349 snprintf(buf, MSG_SIZ, _("Can't parse color name %s"), name);
\r
1350 DisplayError(buf, 0);
\r
1351 return RGB(0, 0, 0);
\r
1353 return PALETTERGB(red, green, blue);
\r
1357 ParseColor(int n, char *name)
\r
1358 { // for WinBoard the color is an int, which needs to be derived from the string
\r
1359 if(colorVariable[n]) *(int*)colorVariable[n] = ParseColorName(name);
\r
1363 ParseAttribs(COLORREF *color, int *effects, char* argValue)
\r
1365 char *e = argValue;
\r
1369 if (*e == 'b') eff |= CFE_BOLD;
\r
1370 else if (*e == 'i') eff |= CFE_ITALIC;
\r
1371 else if (*e == 'u') eff |= CFE_UNDERLINE;
\r
1372 else if (*e == 's') eff |= CFE_STRIKEOUT;
\r
1373 else if (*e == '#' || isdigit(*e)) break;
\r
1377 *color = ParseColorName(e);
\r
1381 ParseTextAttribs(ColorClass cc, char *s)
\r
1382 { // [HGM] front-end wrapper that does the platform-dependent call
\r
1383 // for XBoard we would set (&appData.colorShout)[cc] = strdup(s);
\r
1384 ParseAttribs(&textAttribs[cc].color, &textAttribs[cc].effects, s);
\r
1388 ParseBoardSize(void *addr, char *name)
\r
1389 { // [HGM] rewritten with return-value ptr to shield back-end from BoardSize
\r
1390 BoardSize bs = SizeTiny;
\r
1391 while (sizeInfo[bs].name != NULL) {
\r
1392 if (StrCaseCmp(name, sizeInfo[bs].name) == 0) {
\r
1393 *(BoardSize *)addr = bs;
\r
1398 ExitArgError(_("Unrecognized board size value"), name, TRUE);
\r
1403 { // [HGM] import name from appData first
\r
1406 for (cc = (ColorClass)0; cc < ColorNormal; cc++) {
\r
1407 textAttribs[cc].sound.name = strdup((&appData.soundShout)[cc]);
\r
1408 textAttribs[cc].sound.data = NULL;
\r
1409 MyLoadSound(&textAttribs[cc].sound);
\r
1411 for (cc = ColorNormal; cc < NColorClasses; cc++) {
\r
1412 textAttribs[cc].sound.name = strdup("");
\r
1413 textAttribs[cc].sound.data = NULL;
\r
1415 for (sc = (SoundClass)0; sc < NSoundClasses; sc++) {
\r
1416 sounds[sc].name = strdup((&appData.soundMove)[sc]);
\r
1417 sounds[sc].data = NULL;
\r
1418 MyLoadSound(&sounds[sc]);
\r
1423 SetCommPortDefaults()
\r
1425 memset(&dcb, 0, sizeof(DCB)); // required by VS 2002 +
\r
1426 dcb.DCBlength = sizeof(DCB);
\r
1427 dcb.BaudRate = 9600;
\r
1428 dcb.fBinary = TRUE;
\r
1429 dcb.fParity = FALSE;
\r
1430 dcb.fOutxCtsFlow = FALSE;
\r
1431 dcb.fOutxDsrFlow = FALSE;
\r
1432 dcb.fDtrControl = DTR_CONTROL_ENABLE;
\r
1433 dcb.fDsrSensitivity = FALSE;
\r
1434 dcb.fTXContinueOnXoff = TRUE;
\r
1435 dcb.fOutX = FALSE;
\r
1437 dcb.fNull = FALSE;
\r
1438 dcb.fRtsControl = RTS_CONTROL_ENABLE;
\r
1439 dcb.fAbortOnError = FALSE;
\r
1441 dcb.Parity = SPACEPARITY;
\r
1442 dcb.StopBits = ONESTOPBIT;
\r
1445 // [HGM] args: these three cases taken out to stay in front-end
\r
1447 SaveFontArg(FILE *f, ArgDescriptor *ad)
\r
1448 { // in WinBoard every board size has its own font, and the "argLoc" identifies the table,
\r
1449 // while the curent board size determines the element. This system should be ported to XBoard.
\r
1450 // What the table contains pointers to, and how to print the font description, remains platform-dependent
\r
1452 for (bs=0; bs<NUM_SIZES; bs++) {
\r
1453 MyFontParams *mfp = &font[bs][(int) ad->argLoc]->mfp;
\r
1454 fprintf(f, "/size=%s ", sizeInfo[bs].name);
\r
1455 fprintf(f, "/%s=\"%s:%g%s%s%s%s%sc%d\"\n",
\r
1456 ad->argName, mfp->faceName, mfp->pointSize,
\r
1457 mfp->bold || mfp->italic || mfp->underline || mfp->strikeout ? " " : "",
\r
1458 mfp->bold ? "b" : "",
\r
1459 mfp->italic ? "i" : "",
\r
1460 mfp->underline ? "u" : "",
\r
1461 mfp->strikeout ? "s" : "",
\r
1462 (int)mfp->charset);
\r
1468 { // [HGM] copy the names from the internal WB variables to appData
\r
1471 for (cc = (ColorClass)0; cc < ColorNormal; cc++)
\r
1472 (&appData.soundShout)[cc] = textAttribs[cc].sound.name;
\r
1473 for (sc = (SoundClass)0; sc < NSoundClasses; sc++)
\r
1474 (&appData.soundMove)[sc] = sounds[sc].name;
\r
1478 SaveAttribsArg(FILE *f, ArgDescriptor *ad)
\r
1479 { // here the "argLoc" defines a table index. It could have contained the 'ta' pointer itself, though
\r
1480 MyTextAttribs* ta = &textAttribs[(ColorClass)ad->argLoc];
\r
1481 fprintf(f, "/%s=\"%s%s%s%s%s#%02lx%02lx%02lx\"\n", ad->argName,
\r
1482 (ta->effects & CFE_BOLD) ? "b" : "",
\r
1483 (ta->effects & CFE_ITALIC) ? "i" : "",
\r
1484 (ta->effects & CFE_UNDERLINE) ? "u" : "",
\r
1485 (ta->effects & CFE_STRIKEOUT) ? "s" : "",
\r
1486 (ta->effects) ? " " : "",
\r
1487 ta->color&0xff, (ta->color >> 8)&0xff, (ta->color >> 16)&0xff);
\r
1491 SaveColor(FILE *f, ArgDescriptor *ad)
\r
1492 { // in WinBoard the color is an int and has to be converted to text. In X it would be a string already?
\r
1493 COLORREF color = *(COLORREF *)colorVariable[(int)ad->argLoc];
\r
1494 fprintf(f, "/%s=#%02lx%02lx%02lx\n", ad->argName,
\r
1495 color&0xff, (color>>8)&0xff, (color>>16)&0xff);
\r
1499 SaveBoardSize(FILE *f, char *name, void *addr)
\r
1500 { // wrapper to shield back-end from BoardSize & sizeInfo
\r
1501 fprintf(f, "/%s=%s\n", name, sizeInfo[*(BoardSize *)addr].name);
\r
1505 ParseCommPortSettings(char *s)
\r
1506 { // wrapper to keep dcb from back-end
\r
1507 ParseCommSettings(s, &dcb);
\r
1512 { // wrapper to shield use of window handles from back-end (make addressible by number?)
\r
1513 GetActualPlacement(hwndMain, &wpMain);
\r
1514 GetActualPlacement(hwndConsole, &wpConsole);
\r
1515 GetActualPlacement(commentDialog, &wpComment);
\r
1516 GetActualPlacement(editTagsDialog, &wpTags);
\r
1517 GetActualPlacement(gameListDialog, &wpGameList);
\r
1518 GetActualPlacement(moveHistoryDialog, &wpMoveHistory);
\r
1519 GetActualPlacement(evalGraphDialog, &wpEvalGraph);
\r
1520 GetActualPlacement(engineOutputDialog, &wpEngineOutput);
\r
1524 PrintCommPortSettings(FILE *f, char *name)
\r
1525 { // wrapper to shield back-end from DCB
\r
1526 PrintCommSettings(f, name, &dcb);
\r
1530 MySearchPath(char *installDir, char *name, char *fullname)
\r
1532 char *dummy, buf[MSG_SIZ], *p = name, *q;
\r
1533 if(name[0]== '%') {
\r
1534 fullname[0] = 0; // [HGM] first expand any environment variables in the given name
\r
1535 while(*p == '%' && (q = strchr(p+1, '%'))) { // [HGM] recognize %*% as environment variable
\r
1536 safeStrCpy(buf, p+1, sizeof(buf)/sizeof(buf[0]) );
\r
1537 *strchr(buf, '%') = 0;
\r
1538 strcat(fullname, getenv(buf));
\r
1539 p = q+1; while(*p == '\\') { strcat(fullname, "\\"); p++; }
\r
1541 strcat(fullname, p); // after environment variables (if any), take the remainder of the given name
\r
1542 if(appData.debugMode) fprintf(debugFP, "name = '%s', expanded name = '%s'\n", name, fullname);
\r
1543 return (int) strlen(fullname);
\r
1545 return (int) SearchPath(installDir, name, NULL, MSG_SIZ, fullname, &dummy);
\r
1549 MyGetFullPathName(char *name, char *fullname)
\r
1552 return (int) GetFullPathName(name, MSG_SIZ, fullname, &dummy);
\r
1557 { // [HGM] args: allows testing if main window is realized from back-end
\r
1558 return hwndMain != NULL;
\r
1562 PopUpStartupDialog()
\r
1566 LoadLanguageFile(appData.language);
\r
1567 lpProc = MakeProcInstance((FARPROC)StartupDialog, hInst);
\r
1568 DialogBox(hInst, MAKEINTRESOURCE(DLG_Startup), NULL, (DLGPROC)lpProc);
\r
1569 FreeProcInstance(lpProc);
\r
1572 /*---------------------------------------------------------------------------*\
\r
1574 * GDI board drawing routines
\r
1576 \*---------------------------------------------------------------------------*/
\r
1578 /* [AS] Draw square using background texture */
\r
1579 static void DrawTile( int dx, int dy, int dw, int dh, HDC dst, HDC src, int mode, int sx, int sy )
\r
1584 return; /* Should never happen! */
\r
1587 SetGraphicsMode( dst, GM_ADVANCED );
\r
1594 /* X reflection */
\r
1599 x.eDx = (FLOAT) dw + dx - 1;
\r
1602 SetWorldTransform( dst, &x );
\r
1605 /* Y reflection */
\r
1611 x.eDy = (FLOAT) dh + dy - 1;
\r
1613 SetWorldTransform( dst, &x );
\r
1621 x.eDx = (FLOAT) dx;
\r
1622 x.eDy = (FLOAT) dy;
\r
1625 SetWorldTransform( dst, &x );
\r
1629 BitBlt( dst, dx, dy, dw, dh, src, sx, sy, SRCCOPY );
\r
1637 SetWorldTransform( dst, &x );
\r
1639 ModifyWorldTransform( dst, 0, MWT_IDENTITY );
\r
1642 /* [AS] [HGM] Make room for more piece types, so all pieces can be different */
\r
1644 PM_WP = (int) WhitePawn,
\r
1645 PM_WN = (int) WhiteKnight,
\r
1646 PM_WB = (int) WhiteBishop,
\r
1647 PM_WR = (int) WhiteRook,
\r
1648 PM_WQ = (int) WhiteQueen,
\r
1649 PM_WF = (int) WhiteFerz,
\r
1650 PM_WW = (int) WhiteWazir,
\r
1651 PM_WE = (int) WhiteAlfil,
\r
1652 PM_WM = (int) WhiteMan,
\r
1653 PM_WO = (int) WhiteCannon,
\r
1654 PM_WU = (int) WhiteUnicorn,
\r
1655 PM_WH = (int) WhiteNightrider,
\r
1656 PM_WA = (int) WhiteAngel,
\r
1657 PM_WC = (int) WhiteMarshall,
\r
1658 PM_WAB = (int) WhiteCardinal,
\r
1659 PM_WD = (int) WhiteDragon,
\r
1660 PM_WL = (int) WhiteLance,
\r
1661 PM_WS = (int) WhiteCobra,
\r
1662 PM_WV = (int) WhiteFalcon,
\r
1663 PM_WSG = (int) WhiteSilver,
\r
1664 PM_WG = (int) WhiteGrasshopper,
\r
1665 PM_WK = (int) WhiteKing,
\r
1666 PM_BP = (int) BlackPawn,
\r
1667 PM_BN = (int) BlackKnight,
\r
1668 PM_BB = (int) BlackBishop,
\r
1669 PM_BR = (int) BlackRook,
\r
1670 PM_BQ = (int) BlackQueen,
\r
1671 PM_BF = (int) BlackFerz,
\r
1672 PM_BW = (int) BlackWazir,
\r
1673 PM_BE = (int) BlackAlfil,
\r
1674 PM_BM = (int) BlackMan,
\r
1675 PM_BO = (int) BlackCannon,
\r
1676 PM_BU = (int) BlackUnicorn,
\r
1677 PM_BH = (int) BlackNightrider,
\r
1678 PM_BA = (int) BlackAngel,
\r
1679 PM_BC = (int) BlackMarshall,
\r
1680 PM_BG = (int) BlackGrasshopper,
\r
1681 PM_BAB = (int) BlackCardinal,
\r
1682 PM_BD = (int) BlackDragon,
\r
1683 PM_BL = (int) BlackLance,
\r
1684 PM_BS = (int) BlackCobra,
\r
1685 PM_BV = (int) BlackFalcon,
\r
1686 PM_BSG = (int) BlackSilver,
\r
1687 PM_BK = (int) BlackKing
\r
1690 static HFONT hPieceFont = NULL;
\r
1691 static HBITMAP hPieceMask[(int) EmptySquare];
\r
1692 static HBITMAP hPieceFace[(int) EmptySquare];
\r
1693 static int fontBitmapSquareSize = 0;
\r
1694 static char pieceToFontChar[(int) EmptySquare] =
\r
1695 { 'p', 'n', 'b', 'r', 'q',
\r
1696 'n', 'b', 'p', 'n', 'b', 'r', 'b', 'r', 'q', 'k',
\r
1697 'k', 'o', 'm', 'v', 't', 'w',
\r
1698 'v', 't', 'o', 'm', 'v', 't', 'v', 't', 'w', 'l',
\r
1701 extern BOOL SetCharTable( char *table, const char * map );
\r
1702 /* [HGM] moved to backend.c */
\r
1704 static void SetPieceBackground( HDC hdc, COLORREF color, int mode )
\r
1707 BYTE r1 = GetRValue( color );
\r
1708 BYTE g1 = GetGValue( color );
\r
1709 BYTE b1 = GetBValue( color );
\r
1715 /* Create a uniform background first */
\r
1716 hbrush = CreateSolidBrush( color );
\r
1717 SetRect( &rc, 0, 0, squareSize, squareSize );
\r
1718 FillRect( hdc, &rc, hbrush );
\r
1719 DeleteObject( hbrush );
\r
1722 /* Vertical gradient, good for pawn, knight and rook, less for queen and king */
\r
1723 int steps = squareSize / 2;
\r
1726 for( i=0; i<steps; i++ ) {
\r
1727 BYTE r = r1 - (r1-r2) * i / steps;
\r
1728 BYTE g = g1 - (g1-g2) * i / steps;
\r
1729 BYTE b = b1 - (b1-b2) * i / steps;
\r
1731 hbrush = CreateSolidBrush( RGB(r,g,b) );
\r
1732 SetRect( &rc, i + squareSize - steps, 0, i + squareSize - steps + 1, squareSize );
\r
1733 FillRect( hdc, &rc, hbrush );
\r
1734 DeleteObject(hbrush);
\r
1737 else if( mode == 2 ) {
\r
1738 /* Diagonal gradient, good more or less for every piece */
\r
1739 POINT triangle[3];
\r
1740 HPEN hpen = SelectObject( hdc, GetStockObject(NULL_PEN) );
\r
1741 HBRUSH hbrush_old;
\r
1742 int steps = squareSize;
\r
1745 triangle[0].x = squareSize - steps;
\r
1746 triangle[0].y = squareSize;
\r
1747 triangle[1].x = squareSize;
\r
1748 triangle[1].y = squareSize;
\r
1749 triangle[2].x = squareSize;
\r
1750 triangle[2].y = squareSize - steps;
\r
1752 for( i=0; i<steps; i++ ) {
\r
1753 BYTE r = r1 - (r1-r2) * i / steps;
\r
1754 BYTE g = g1 - (g1-g2) * i / steps;
\r
1755 BYTE b = b1 - (b1-b2) * i / steps;
\r
1757 hbrush = CreateSolidBrush( RGB(r,g,b) );
\r
1758 hbrush_old = SelectObject( hdc, hbrush );
\r
1759 Polygon( hdc, triangle, 3 );
\r
1760 SelectObject( hdc, hbrush_old );
\r
1761 DeleteObject(hbrush);
\r
1766 SelectObject( hdc, hpen );
\r
1771 [AS] The method I use to create the bitmaps it a bit tricky, but it
\r
1772 seems to work ok. The main problem here is to find the "inside" of a chess
\r
1773 piece: follow the steps as explained below.
\r
1775 static void CreatePieceMaskFromFont( HDC hdc_window, HDC hdc, int index )
\r
1779 COLORREF chroma = RGB(0xFF,0x00,0xFF);
\r
1783 int backColor = whitePieceColor;
\r
1784 int foreColor = blackPieceColor;
\r
1786 if( index < (int)BlackPawn && appData.fontBackColorWhite != appData.fontForeColorWhite ) {
\r
1787 backColor = appData.fontBackColorWhite;
\r
1788 foreColor = appData.fontForeColorWhite;
\r
1790 else if( index >= (int)BlackPawn && appData.fontBackColorBlack != appData.fontForeColorBlack ) {
\r
1791 backColor = appData.fontBackColorBlack;
\r
1792 foreColor = appData.fontForeColorBlack;
\r
1796 hbm = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );
\r
1798 hbm_old = SelectObject( hdc, hbm );
\r
1802 rc.right = squareSize;
\r
1803 rc.bottom = squareSize;
\r
1805 /* Step 1: background is now black */
\r
1806 FillRect( hdc, &rc, GetStockObject(BLACK_BRUSH) );
\r
1808 GetTextExtentPoint32( hdc, &pieceToFontChar[index], 1, &sz );
\r
1810 pt.x = (squareSize - sz.cx) / 2;
\r
1811 pt.y = (squareSize - sz.cy) / 2;
\r
1813 SetBkMode( hdc, TRANSPARENT );
\r
1814 SetTextColor( hdc, chroma );
\r
1815 /* Step 2: the piece has been drawn in purple, there are now black and purple in this bitmap */
\r
1816 TextOut( hdc, pt.x, pt.y, &pieceToFontChar[appData.allWhite && index >= (int)BlackPawn ? index - (int)BlackPawn : index], 1 );
\r
1818 SelectObject( hdc, GetStockObject(WHITE_BRUSH) );
\r
1819 /* Step 3: the area outside the piece is filled with white */
\r
1820 // FloodFill( hdc, 0, 0, chroma );
\r
1821 ExtFloodFill( hdc, 0, 0, 0, FLOODFILLSURFACE );
\r
1822 ExtFloodFill( hdc, 0, squareSize-1, 0, FLOODFILLSURFACE ); // [HGM] fill from all 4 corners, for if piece too big
\r
1823 ExtFloodFill( hdc, squareSize-1, 0, 0, FLOODFILLSURFACE );
\r
1824 ExtFloodFill( hdc, squareSize-1, squareSize-1, 0, FLOODFILLSURFACE );
\r
1825 SelectObject( hdc, GetStockObject(BLACK_BRUSH) );
\r
1827 Step 4: this is the tricky part, the area inside the piece is filled with black,
\r
1828 but if the start point is not inside the piece we're lost!
\r
1829 There should be a better way to do this... if we could create a region or path
\r
1830 from the fill operation we would be fine for example.
\r
1832 // FloodFill( hdc, squareSize / 2, squareSize / 2, RGB(0xFF,0xFF,0xFF) );
\r
1833 ExtFloodFill( hdc, squareSize / 2, squareSize / 2, RGB(0xFF,0xFF,0xFF), FLOODFILLBORDER );
\r
1835 { /* [HGM] shave off edges of mask, in an attempt to correct for the fact that FloodFill does not work correctly under Win XP */
\r
1836 HDC dc2 = CreateCompatibleDC( hdc_window );
\r
1837 HBITMAP bm2 = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );
\r
1839 SelectObject( dc2, bm2 );
\r
1840 BitBlt( dc2, 0, 0, squareSize, squareSize, hdc, 0, 0, SRCCOPY ); // make copy
\r
1841 BitBlt( hdc, 0, 1, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );
\r
1842 BitBlt( hdc, 2, 1, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );
\r
1843 BitBlt( hdc, 1, 0, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );
\r
1844 BitBlt( hdc, 1, 2, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );
\r
1847 DeleteObject( bm2 );
\r
1850 SetTextColor( hdc, 0 );
\r
1852 Step 5: some fonts have "disconnected" areas that are skipped by the fill:
\r
1853 draw the piece again in black for safety.
\r
1855 TextOut( hdc, pt.x, pt.y, &pieceToFontChar[appData.allWhite && index >= (int)BlackPawn ? index - (int)BlackPawn : index], 1 );
\r
1857 SelectObject( hdc, hbm_old );
\r
1859 if( hPieceMask[index] != NULL ) {
\r
1860 DeleteObject( hPieceMask[index] );
\r
1863 hPieceMask[index] = hbm;
\r
1866 hbm = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );
\r
1868 SelectObject( hdc, hbm );
\r
1871 HDC dc1 = CreateCompatibleDC( hdc_window );
\r
1872 HDC dc2 = CreateCompatibleDC( hdc_window );
\r
1873 HBITMAP bm2 = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );
\r
1875 SelectObject( dc1, hPieceMask[index] );
\r
1876 SelectObject( dc2, bm2 );
\r
1877 FillRect( dc2, &rc, GetStockObject(WHITE_BRUSH) );
\r
1878 BitBlt( dc2, 0, 0, squareSize, squareSize, dc1, 0, 0, SRCINVERT );
\r
1881 Now dc2 contains the inverse of the piece mask, i.e. a mask that preserves
\r
1882 the piece background and deletes (makes transparent) the rest.
\r
1883 Thanks to that mask, we are free to paint the background with the greates
\r
1884 freedom, as we'll be able to mask off the unwanted parts when finished.
\r
1885 We use this, to make gradients and give the pieces a "roundish" look.
\r
1887 SetPieceBackground( hdc, backColor, 2 );
\r
1888 BitBlt( hdc, 0, 0, squareSize, squareSize, dc2, 0, 0, SRCAND );
\r
1892 DeleteObject( bm2 );
\r
1895 SetTextColor( hdc, foreColor );
\r
1896 TextOut( hdc, pt.x, pt.y, &pieceToFontChar[appData.allWhite && index >= (int)BlackPawn ? index - (int)BlackPawn : index], 1 );
\r
1898 SelectObject( hdc, hbm_old );
\r
1900 if( hPieceFace[index] != NULL ) {
\r
1901 DeleteObject( hPieceFace[index] );
\r
1904 hPieceFace[index] = hbm;
\r
1907 static int TranslatePieceToFontPiece( int piece )
\r
1937 case BlackMarshall:
\r
1941 case BlackNightrider:
\r
1947 case BlackUnicorn:
\r
1951 case BlackGrasshopper:
\r
1963 case BlackCardinal:
\r
1970 case WhiteMarshall:
\r
1974 case WhiteNightrider:
\r
1980 case WhiteUnicorn:
\r
1984 case WhiteGrasshopper:
\r
1996 case WhiteCardinal:
\r
2005 void CreatePiecesFromFont()
\r
2008 HDC hdc_window = NULL;
\r
2014 if( fontBitmapSquareSize < 0 ) {
\r
2015 /* Something went seriously wrong in the past: do not try to recreate fonts! */
\r
2019 if( !appData.useFont || appData.renderPiecesWithFont == NULL ||
\r
2020 appData.renderPiecesWithFont[0] == NULLCHAR || appData.renderPiecesWithFont[0] == '*' ) {
\r
2021 fontBitmapSquareSize = -1;
\r
2025 if( fontBitmapSquareSize != squareSize ) {
\r
2026 hdc_window = GetDC( hwndMain );
\r
2027 hdc = CreateCompatibleDC( hdc_window );
\r
2029 if( hPieceFont != NULL ) {
\r
2030 DeleteObject( hPieceFont );
\r
2033 for( i=0; i<=(int)BlackKing; i++ ) {
\r
2034 hPieceMask[i] = NULL;
\r
2035 hPieceFace[i] = NULL;
\r
2041 if( appData.fontPieceSize >= 50 && appData.fontPieceSize <= 150 ) {
\r
2042 fontHeight = appData.fontPieceSize;
\r
2045 fontHeight = (fontHeight * squareSize) / 100;
\r
2047 lf.lfHeight = -MulDiv( fontHeight, GetDeviceCaps(hdc, LOGPIXELSY), 72 );
\r
2049 lf.lfEscapement = 0;
\r
2050 lf.lfOrientation = 0;
\r
2051 lf.lfWeight = FW_NORMAL;
\r
2053 lf.lfUnderline = 0;
\r
2054 lf.lfStrikeOut = 0;
\r
2055 lf.lfCharSet = DEFAULT_CHARSET;
\r
2056 lf.lfOutPrecision = OUT_DEFAULT_PRECIS;
\r
2057 lf.lfClipPrecision = CLIP_DEFAULT_PRECIS;
\r
2058 lf.lfQuality = PROOF_QUALITY;
\r
2059 lf.lfPitchAndFamily = DEFAULT_PITCH | FF_DONTCARE;
\r
2060 strncpy( lf.lfFaceName, appData.renderPiecesWithFont, sizeof(lf.lfFaceName) );
\r
2061 lf.lfFaceName[ sizeof(lf.lfFaceName) - 1 ] = '\0';
\r
2063 hPieceFont = CreateFontIndirect( &lf );
\r
2065 if( hPieceFont == NULL ) {
\r
2066 fontBitmapSquareSize = -2;
\r
2069 /* Setup font-to-piece character table */
\r
2070 if( ! SetCharTable(pieceToFontChar, appData.fontToPieceTable) ) {
\r
2071 /* No (or wrong) global settings, try to detect the font */
\r
2072 if( strstr(lf.lfFaceName,"Alpha") != NULL ) {
\r
2074 SetCharTable(pieceToFontChar, "phbrqkojntwl");
\r
2076 else if( strstr(lf.lfFaceName,"DiagramTT") != NULL ) {
\r
2077 /* DiagramTT* family */
\r
2078 SetCharTable(pieceToFontChar, "PNLRQKpnlrqk");
\r
2080 else if( strstr(lf.lfFaceName,"WinboardF") != NULL ) {
\r
2081 /* Fairy symbols */
\r
2082 SetCharTable(pieceToFontChar, "PNBRQFEACWMOHIJGDVSLUKpnbrqfeacwmohijgdvsluk");
\r
2084 else if( strstr(lf.lfFaceName,"GC2004D") != NULL ) {
\r
2085 /* Good Companion (Some characters get warped as literal :-( */
\r
2086 char s[] = "1cmWG0??S??oYI23wgQU";
\r
2087 s[0]=0xB9; s[1]=0xA9; s[6]=0xB1; s[11]=0xBB; s[12]=0xAB; s[17]=0xB3;
\r
2088 SetCharTable(pieceToFontChar, s);
\r
2091 /* Cases, Condal, Leipzig, Lucena, Marroquin, Merida, Usual */
\r
2092 SetCharTable(pieceToFontChar, "pnbrqkomvtwl");
\r
2096 /* Create bitmaps */
\r
2097 hfont_old = SelectObject( hdc, hPieceFont );
\r
2098 for(i=(int)WhitePawn; i<(int)EmptySquare; i++) /* [HGM] made a loop for this */
\r
2099 if(PieceToChar((ChessSquare)i) != '.') /* skip unused pieces */
\r
2100 CreatePieceMaskFromFont( hdc_window, hdc, i );
\r
2102 SelectObject( hdc, hfont_old );
\r
2104 fontBitmapSquareSize = squareSize;
\r
2108 if( hdc != NULL ) {
\r
2112 if( hdc_window != NULL ) {
\r
2113 ReleaseDC( hwndMain, hdc_window );
\r
2118 DoLoadBitmap(HINSTANCE hinst, char *piece, int squareSize, char *suffix)
\r
2120 char name[128], buf[MSG_SIZ];
\r
2122 snprintf(name, sizeof(name)/sizeof(name[0]), "%s%d%s", piece, squareSize, suffix);
\r
2123 if(appData.pieceDirectory[0]) {
\r
2125 snprintf(buf, MSG_SIZ, "%s\\%s.bmp", appData.pieceDirectory, name);
\r
2126 res = LoadImage( 0, buf, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );
\r
2127 if(res) return res;
\r
2129 if (gameInfo.event &&
\r
2130 strcmp(gameInfo.event, "Easter Egg Hunt") == 0 &&
\r
2131 strcmp(name, "k80s") == 0) {
\r
2132 safeStrCpy(name, "tim", sizeof(name)/sizeof(name[0]) );
\r
2134 return LoadBitmap(hinst, name);
\r
2138 /* Insert a color into the program's logical palette
\r
2139 structure. This code assumes the given color is
\r
2140 the result of the RGB or PALETTERGB macro, and it
\r
2141 knows how those macros work (which is documented).
\r
2144 InsertInPalette(COLORREF color)
\r
2146 LPPALETTEENTRY pe = &(pLogPal->palPalEntry[pLogPal->palNumEntries]);
\r
2148 if (pLogPal->palNumEntries++ >= PALETTESIZE) {
\r
2149 DisplayFatalError(_("Too many colors"), 0, 1);
\r
2150 pLogPal->palNumEntries--;
\r
2154 pe->peFlags = (char) 0;
\r
2155 pe->peRed = (char) (0xFF & color);
\r
2156 pe->peGreen = (char) (0xFF & (color >> 8));
\r
2157 pe->peBlue = (char) (0xFF & (color >> 16));
\r
2163 InitDrawingColors()
\r
2165 if (pLogPal == NULL) {
\r
2166 /* Allocate enough memory for a logical palette with
\r
2167 * PALETTESIZE entries and set the size and version fields
\r
2168 * of the logical palette structure.
\r
2170 pLogPal = (NPLOGPALETTE)
\r
2171 LocalAlloc(LMEM_FIXED, (sizeof(LOGPALETTE) +
\r
2172 (sizeof(PALETTEENTRY) * (PALETTESIZE))));
\r
2173 pLogPal->palVersion = 0x300;
\r
2175 pLogPal->palNumEntries = 0;
\r
2177 InsertInPalette(lightSquareColor);
\r
2178 InsertInPalette(darkSquareColor);
\r
2179 InsertInPalette(whitePieceColor);
\r
2180 InsertInPalette(blackPieceColor);
\r
2181 InsertInPalette(highlightSquareColor);
\r
2182 InsertInPalette(premoveHighlightColor);
\r
2184 /* create a logical color palette according the information
\r
2185 * in the LOGPALETTE structure.
\r
2187 hPal = CreatePalette((LPLOGPALETTE) pLogPal);
\r
2189 lightSquareBrush = CreateSolidBrush(lightSquareColor);
\r
2190 blackSquareBrush = CreateSolidBrush(blackPieceColor);
\r
2191 darkSquareBrush = CreateSolidBrush(darkSquareColor);
\r
2192 whitePieceBrush = CreateSolidBrush(whitePieceColor);
\r
2193 blackPieceBrush = CreateSolidBrush(blackPieceColor);
\r
2194 iconBkgndBrush = CreateSolidBrush(GetSysColor(COLOR_BACKGROUND));
\r
2195 explodeBrush = CreateSolidBrush(highlightSquareColor); // [HGM] atomic
\r
2196 markerBrush = CreateSolidBrush(premoveHighlightColor); // [HGM] markers
\r
2197 /* [AS] Force rendering of the font-based pieces */
\r
2198 if( fontBitmapSquareSize > 0 ) {
\r
2199 fontBitmapSquareSize = 0;
\r
2205 BoardWidth(int boardSize, int n)
\r
2206 { /* [HGM] argument n added to allow different width and height */
\r
2207 int lineGap = sizeInfo[boardSize].lineGap;
\r
2209 if( appData.overrideLineGap >= 0 && appData.overrideLineGap <= 5 ) {
\r
2210 lineGap = appData.overrideLineGap;
\r
2213 return (n + 1) * lineGap +
\r
2214 n * sizeInfo[boardSize].squareSize;
\r
2217 /* Respond to board resize by dragging edge */
\r
2219 ResizeBoard(int newSizeX, int newSizeY, int flags)
\r
2221 BoardSize newSize = NUM_SIZES - 1;
\r
2222 static int recurse = 0;
\r
2223 if (IsIconic(hwndMain)) return;
\r
2224 if (recurse > 0) return;
\r
2226 while (newSize > 0) {
\r
2227 InitDrawingSizes(newSize+1000, 0); // [HGM] kludge to update sizeInfo without visible effects
\r
2228 if(newSizeX >= sizeInfo[newSize].cliWidth &&
\r
2229 newSizeY >= sizeInfo[newSize].cliHeight) break;
\r
2232 boardSize = newSize;
\r
2233 InitDrawingSizes(boardSize, flags);
\r
2238 extern Boolean twoBoards, partnerUp; // [HGM] dual
\r
2241 InitDrawingSizes(BoardSize boardSize, int flags)
\r
2243 int i, boardWidth, boardHeight; /* [HGM] height treated separately */
\r
2244 ChessSquare piece;
\r
2245 static int oldBoardSize = -1, oldTinyLayout = 0;
\r
2247 SIZE clockSize, messageSize;
\r
2249 char buf[MSG_SIZ];
\r
2251 HMENU hmenu = GetMenu(hwndMain);
\r
2252 RECT crect, wrect, oldRect;
\r
2254 LOGBRUSH logbrush;
\r
2255 VariantClass v = gameInfo.variant;
\r
2257 int suppressVisibleEffects = 0; // [HGM] kludge to request updating sizeInfo only
\r
2258 if((int)boardSize >= 1000 ) { boardSize -= 1000; suppressVisibleEffects = 1; }
\r
2260 /* [HGM] call with -2 uses old size (for if nr of files, ranks changes) */
\r
2261 if(boardSize == (BoardSize)(-2) ) boardSize = oldBoardSize;
\r
2262 oldBoardSize = boardSize;
\r
2264 if(boardSize != SizeMiddling && boardSize != SizePetite && boardSize != SizeBulky && !appData.useFont)
\r
2265 { // correct board size to one where built-in pieces exist
\r
2266 if((v == VariantCapablanca || v == VariantGothic || v == VariantGrand || v == VariantCapaRandom || v == VariantJanus || v == VariantSuper)
\r
2267 && (boardSize < SizePetite || boardSize > SizeBulky) // Archbishop and Chancellor available in entire middle range
\r
2268 || (v == VariantShogi && boardSize != SizeModerate) // Japanese-style Shogi
\r
2269 || v == VariantKnightmate || v == VariantSChess || v == VariantXiangqi || v == VariantSpartan
\r
2270 || v == VariantShatranj || v == VariantMakruk || v == VariantGreat || v == VariantFairy ) {
\r
2271 if(boardSize < SizeMediocre) boardSize = SizePetite; else
\r
2272 if(boardSize > SizeModerate) boardSize = SizeBulky; else
\r
2273 boardSize = SizeMiddling;
\r
2276 if(!appData.useFont && boardSize == SizePetite && (v == VariantShogi || v == VariantKnightmate)) boardSize = SizeMiddling; // no Unicorn in Petite
\r
2278 oldRect.left = wpMain.x; //[HGM] placement: remember previous window params
\r
2279 oldRect.top = wpMain.y;
\r
2280 oldRect.right = wpMain.x + wpMain.width;
\r
2281 oldRect.bottom = wpMain.y + wpMain.height;
\r
2283 tinyLayout = sizeInfo[boardSize].tinyLayout;
\r
2284 smallLayout = sizeInfo[boardSize].smallLayout;
\r
2285 squareSize = sizeInfo[boardSize].squareSize;
\r
2286 lineGap = sizeInfo[boardSize].lineGap;
\r
2287 minorSize = 0; /* [HGM] Kludge to see if demagnified pieces need to be shifted */
\r
2288 border = appData.useBorder && appData.border[0] ? squareSize/2 : 0;
\r
2290 if( appData.overrideLineGap >= 0 && appData.overrideLineGap <= 5 ) {
\r
2291 lineGap = appData.overrideLineGap;
\r
2294 if (tinyLayout != oldTinyLayout) {
\r
2295 long style = GetWindowLongPtr(hwndMain, GWL_STYLE);
\r
2297 style &= ~WS_SYSMENU;
\r
2298 InsertMenu(hmenu, IDM_Exit, MF_BYCOMMAND, IDM_Minimize,
\r
2299 "&Minimize\tCtrl+F4");
\r
2301 style |= WS_SYSMENU;
\r
2302 RemoveMenu(hmenu, IDM_Minimize, MF_BYCOMMAND);
\r
2304 SetWindowLongPtr(hwndMain, GWL_STYLE, style);
\r
2306 for (i=0; menuBarText[tinyLayout][i]; i++) {
\r
2307 ModifyMenu(hmenu, i, MF_STRING|MF_BYPOSITION|MF_POPUP,
\r
2308 (UINT)GetSubMenu(hmenu, i), T_(menuBarText[tinyLayout][i]));
\r
2310 DrawMenuBar(hwndMain);
\r
2313 boardWidth = BoardWidth(boardSize, BOARD_WIDTH) + 2*border;
\r
2314 boardHeight = BoardWidth(boardSize, BOARD_HEIGHT) + 2*border;
\r
2316 /* Get text area sizes */
\r
2317 hdc = GetDC(hwndMain);
\r
2318 if (appData.clockMode) {
\r
2319 snprintf(buf, MSG_SIZ, _("White: %s"), TimeString(23*60*60*1000L));
\r
2321 snprintf(buf, MSG_SIZ, _("White"));
\r
2323 oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);
\r
2324 GetTextExtentPoint(hdc, buf, strlen(buf), &clockSize);
\r
2325 SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);
\r
2326 str = _("We only care about the height here");
\r
2327 GetTextExtentPoint(hdc, str, strlen(str), &messageSize);
\r
2328 SelectObject(hdc, oldFont);
\r
2329 ReleaseDC(hwndMain, hdc);
\r
2331 /* Compute where everything goes */
\r
2332 if((first.programLogo || second.programLogo) && !tinyLayout) {
\r
2333 /* [HGM] logo: if either logo is on, reserve space for it */
\r
2334 logoHeight = 2*clockSize.cy;
\r
2335 leftLogoRect.left = OUTER_MARGIN;
\r
2336 leftLogoRect.right = leftLogoRect.left + 4*clockSize.cy;
\r
2337 leftLogoRect.top = OUTER_MARGIN;
\r
2338 leftLogoRect.bottom = OUTER_MARGIN + logoHeight;
\r
2340 rightLogoRect.right = OUTER_MARGIN + boardWidth;
\r
2341 rightLogoRect.left = rightLogoRect.right - 4*clockSize.cy;
\r
2342 rightLogoRect.top = OUTER_MARGIN;
\r
2343 rightLogoRect.bottom = OUTER_MARGIN + logoHeight;
\r
2346 whiteRect.left = leftLogoRect.right;
\r
2347 whiteRect.right = OUTER_MARGIN + boardWidth/2 - INNER_MARGIN/2;
\r
2348 whiteRect.top = OUTER_MARGIN;
\r
2349 whiteRect.bottom = whiteRect.top + logoHeight;
\r
2351 blackRect.right = rightLogoRect.left;
\r
2352 blackRect.left = whiteRect.right + INNER_MARGIN;
\r
2353 blackRect.top = whiteRect.top;
\r
2354 blackRect.bottom = whiteRect.bottom;
\r
2356 whiteRect.left = OUTER_MARGIN;
\r
2357 whiteRect.right = whiteRect.left + boardWidth/2 - INNER_MARGIN/2;
\r
2358 whiteRect.top = OUTER_MARGIN;
\r
2359 whiteRect.bottom = whiteRect.top + clockSize.cy;
\r
2361 blackRect.left = whiteRect.right + INNER_MARGIN;
\r
2362 blackRect.right = blackRect.left + boardWidth/2 - 1;
\r
2363 blackRect.top = whiteRect.top;
\r
2364 blackRect.bottom = whiteRect.bottom;
\r
2366 logoHeight = 0; // [HGM] logo: suppress logo after change to tiny layout!
\r
2369 messageRect.left = OUTER_MARGIN + MESSAGE_LINE_LEFTMARGIN;
\r
2370 if (appData.showButtonBar) {
\r
2371 messageRect.right = OUTER_MARGIN + boardWidth // [HGM] logo: expressed independent of clock placement
\r
2372 - N_BUTTONS*BUTTON_WIDTH - MESSAGE_LINE_LEFTMARGIN;
\r
2374 messageRect.right = OUTER_MARGIN + boardWidth;
\r
2376 messageRect.top = whiteRect.bottom + INNER_MARGIN;
\r
2377 messageRect.bottom = messageRect.top + messageSize.cy;
\r
2379 boardRect.left = OUTER_MARGIN;
\r
2380 boardRect.right = boardRect.left + boardWidth;
\r
2381 boardRect.top = messageRect.bottom + INNER_MARGIN;
\r
2382 boardRect.bottom = boardRect.top + boardHeight;
\r
2384 sizeInfo[boardSize].cliWidth = boardRect.right + OUTER_MARGIN;
\r
2385 sizeInfo[boardSize].cliHeight = boardRect.bottom + OUTER_MARGIN;
\r
2386 oldTinyLayout = tinyLayout;
\r
2387 winW = 2 * GetSystemMetrics(SM_CXFRAME) + boardRect.right + OUTER_MARGIN;
\r
2388 winH = 2 * GetSystemMetrics(SM_CYFRAME) + GetSystemMetrics(SM_CYMENU) +
\r
2389 GetSystemMetrics(SM_CYCAPTION) + boardRect.bottom + OUTER_MARGIN;
\r
2390 winW *= 1 + twoBoards;
\r
2391 if(suppressVisibleEffects) return; // [HGM] when called for filling sizeInfo only
\r
2392 wpMain.width = winW; // [HGM] placement: set through temporary which can used by initial sizing choice
\r
2393 wpMain.height = winH; // without disturbing window attachments
\r
2394 GetWindowRect(hwndMain, &wrect);
\r
2395 SetWindowPos(hwndMain, NULL, 0, 0, wpMain.width, wpMain.height,
\r
2396 SWP_NOCOPYBITS|SWP_NOZORDER|SWP_NOMOVE);
\r
2398 // [HGM] placement: let attached windows follow size change.
\r
2399 ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, moveHistoryDialog, &wpMoveHistory );
\r
2400 ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, evalGraphDialog, &wpEvalGraph );
\r
2401 ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, engineOutputDialog, &wpEngineOutput );
\r
2402 ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, gameListDialog, &wpGameList );
\r
2403 ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, hwndConsole, &wpConsole );
\r
2405 /* compensate if menu bar wrapped */
\r
2406 GetClientRect(hwndMain, &crect);
\r
2407 offby = boardRect.bottom + OUTER_MARGIN - crect.bottom;
\r
2408 wpMain.height += offby;
\r
2410 case WMSZ_TOPLEFT:
\r
2411 SetWindowPos(hwndMain, NULL,
\r
2412 wrect.right - wpMain.width, wrect.bottom - wpMain.height,
\r
2413 wpMain.width, wpMain.height, SWP_NOCOPYBITS|SWP_NOZORDER);
\r
2416 case WMSZ_TOPRIGHT:
\r
2418 SetWindowPos(hwndMain, NULL,
\r
2419 wrect.left, wrect.bottom - wpMain.height,
\r
2420 wpMain.width, wpMain.height, SWP_NOCOPYBITS|SWP_NOZORDER);
\r
2423 case WMSZ_BOTTOMLEFT:
\r
2425 SetWindowPos(hwndMain, NULL,
\r
2426 wrect.right - wpMain.width, wrect.top,
\r
2427 wpMain.width, wpMain.height, SWP_NOCOPYBITS|SWP_NOZORDER);
\r
2430 case WMSZ_BOTTOMRIGHT:
\r
2434 SetWindowPos(hwndMain, NULL, 0, 0, wpMain.width, wpMain.height,
\r
2435 SWP_NOCOPYBITS|SWP_NOZORDER|SWP_NOMOVE);
\r
2440 for (i = 0; i < N_BUTTONS; i++) {
\r
2441 if (buttonDesc[i].hwnd != NULL) {
\r
2442 DestroyWindow(buttonDesc[i].hwnd);
\r
2443 buttonDesc[i].hwnd = NULL;
\r
2445 if (appData.showButtonBar) {
\r
2446 buttonDesc[i].hwnd =
\r
2447 CreateWindow("BUTTON", buttonDesc[i].label,
\r
2448 WS_VISIBLE | WS_CHILD | BS_PUSHBUTTON,
\r
2449 boardRect.right - BUTTON_WIDTH*(N_BUTTONS-i),
\r
2450 messageRect.top, BUTTON_WIDTH, messageSize.cy, hwndMain,
\r
2451 (HMENU) buttonDesc[i].id,
\r
2452 (HINSTANCE) GetWindowLongPtr(hwndMain, GWLP_HINSTANCE), NULL);
\r
2454 SendMessage(buttonDesc[i].hwnd, WM_SETFONT,
\r
2455 (WPARAM)font[boardSize][MESSAGE_FONT]->hf,
\r
2456 MAKELPARAM(FALSE, 0));
\r
2458 if (buttonDesc[i].id == IDM_Pause)
\r
2459 hwndPause = buttonDesc[i].hwnd;
\r
2460 buttonDesc[i].wndproc = (WNDPROC)
\r
2461 SetWindowLongPtr(buttonDesc[i].hwnd, GWLP_WNDPROC, (LONG_PTR) ButtonProc);
\r
2464 if (gridPen != NULL) DeleteObject(gridPen);
\r
2465 if (highlightPen != NULL) DeleteObject(highlightPen);
\r
2466 if (premovePen != NULL) DeleteObject(premovePen);
\r
2467 if (lineGap != 0) {
\r
2468 logbrush.lbStyle = BS_SOLID;
\r
2469 logbrush.lbColor = RGB(0, 0, 0); /* grid pen color = black */
\r
2471 ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,
\r
2472 lineGap, &logbrush, 0, NULL);
\r
2473 logbrush.lbColor = highlightSquareColor;
\r
2475 ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,
\r
2476 lineGap, &logbrush, 0, NULL);
\r
2478 logbrush.lbColor = premoveHighlightColor;
\r
2480 ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,
\r
2481 lineGap, &logbrush, 0, NULL);
\r
2483 /* [HGM] Loop had to be split in part for vert. and hor. lines */
\r
2484 for (i = 0; i < BOARD_HEIGHT + 1; i++) {
\r
2485 gridEndpoints[i*2].x = boardRect.left + lineGap / 2 + border;
\r
2486 gridEndpoints[i*2].y = gridEndpoints[i*2 + 1].y =
\r
2487 boardRect.top + lineGap / 2 + (i * (squareSize + lineGap)) + border;
\r
2488 gridEndpoints[i*2 + 1].x = boardRect.left + lineGap / 2 +
\r
2489 BOARD_WIDTH * (squareSize + lineGap) + border;
\r
2490 gridVertexCounts[i*2] = gridVertexCounts[i*2 + 1] = 2;
\r
2492 for (i = 0; i < BOARD_WIDTH + 1; i++) {
\r
2493 gridEndpoints[i*2 + BOARD_HEIGHT*2 + 2].y = boardRect.top + lineGap / 2 + border;
\r
2494 gridEndpoints[i*2 + BOARD_HEIGHT*2 + 2].x =
\r
2495 gridEndpoints[i*2 + 1 + BOARD_HEIGHT*2 + 2].x = boardRect.left +
\r
2496 lineGap / 2 + (i * (squareSize + lineGap)) + border;
\r
2497 gridEndpoints[i*2 + 1 + BOARD_HEIGHT*2 + 2].y =
\r
2498 boardRect.top + BOARD_HEIGHT * (squareSize + lineGap) + border;
\r
2499 gridVertexCounts[i*2] = gridVertexCounts[i*2 + 1] = 2;
\r
2503 /* [HGM] Licensing requirement */
\r
2505 if(gameInfo.variant == VariantGothic) GothicPopUp( GOTHIC, VariantGothic); else
\r
2508 if(gameInfo.variant == VariantFalcon) GothicPopUp( FALCON, VariantFalcon); else
\r
2510 GothicPopUp( "", VariantNormal);
\r
2513 /* if (boardSize == oldBoardSize) return; [HGM] variant might have changed */
\r
2515 /* Load piece bitmaps for this board size */
\r
2516 for (i=0; i<=2; i++) {
\r
2517 for (piece = WhitePawn;
\r
2518 (int) piece < (int) BlackPawn;
\r
2519 piece = (ChessSquare) ((int) piece + 1)) {
\r
2520 if (pieceBitmap[i][piece] != NULL)
\r
2521 DeleteObject(pieceBitmap[i][piece]);
\r
2525 fontBitmapSquareSize = 0; /* [HGM] render: make sure pieces will be recreated, as we might need others now */
\r
2526 // Orthodox Chess pieces
\r
2527 pieceBitmap[0][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "s");
\r
2528 pieceBitmap[0][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "s");
\r
2529 pieceBitmap[0][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "s");
\r
2530 pieceBitmap[0][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "s");
\r
2531 pieceBitmap[0][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "s");
\r
2532 pieceBitmap[1][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "o");
\r
2533 pieceBitmap[1][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "o");
\r
2534 pieceBitmap[1][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "o");
\r
2535 pieceBitmap[1][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "o");
\r
2536 pieceBitmap[1][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "o");
\r
2537 pieceBitmap[2][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "w");
\r
2538 pieceBitmap[2][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "w");
\r
2539 pieceBitmap[2][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "w");
\r
2540 pieceBitmap[2][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "w");
\r
2541 pieceBitmap[2][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "w");
\r
2542 if( gameInfo.variant == VariantShogi && squareSize <= 72 && squareSize >= 33) {
\r
2543 // in Shogi, Hijack the unused Queen for Lance
\r
2544 pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "s");
\r
2545 pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "o");
\r
2546 pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "w");
\r
2548 pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "s");
\r
2549 pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "o");
\r
2550 pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "w");
\r
2553 if(squareSize <= 72 && squareSize >= 33) {
\r
2554 /* A & C are available in most sizes now */
\r
2555 if(squareSize != 49 && squareSize != 72 && squareSize != 33) { // Vortex-like
\r
2556 pieceBitmap[0][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "s");
\r
2557 pieceBitmap[1][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "o");
\r
2558 pieceBitmap[2][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "w");
\r
2559 pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "s");
\r
2560 pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "o");
\r
2561 pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "w");
\r
2562 pieceBitmap[0][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "s");
\r
2563 pieceBitmap[1][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "o");
\r
2564 pieceBitmap[2][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "w");
\r
2565 pieceBitmap[0][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "s");
\r
2566 pieceBitmap[1][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "o");
\r
2567 pieceBitmap[2][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "w");
\r
2568 } else { // Smirf-like
\r
2569 if(gameInfo.variant == VariantSChess) {
\r
2570 pieceBitmap[0][WhiteAngel] = DoLoadBitmap(hInst, "v", squareSize, "s");
\r
2571 pieceBitmap[1][WhiteAngel] = DoLoadBitmap(hInst, "v", squareSize, "o");
\r
2572 pieceBitmap[2][WhiteAngel] = DoLoadBitmap(hInst, "v", squareSize, "w");
\r
2574 pieceBitmap[0][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "s");
\r
2575 pieceBitmap[1][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "o");
\r
2576 pieceBitmap[2][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "w");
\r
2579 if(gameInfo.variant == VariantGothic) { // Vortex-like
\r
2580 pieceBitmap[0][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "s");
\r
2581 pieceBitmap[1][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "o");
\r
2582 pieceBitmap[2][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "w");
\r
2583 } else if(gameInfo.variant == VariantSChess && (squareSize == 49 || squareSize == 72)) {
\r
2584 pieceBitmap[0][WhiteMarshall] = DoLoadBitmap(hInst, "e", squareSize, "s");
\r
2585 pieceBitmap[1][WhiteMarshall] = DoLoadBitmap(hInst, "e", squareSize, "o");
\r
2586 pieceBitmap[2][WhiteMarshall] = DoLoadBitmap(hInst, "e", squareSize, "w");
\r
2587 } else { // WinBoard standard
\r
2588 pieceBitmap[0][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "s");
\r
2589 pieceBitmap[1][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "o");
\r
2590 pieceBitmap[2][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "w");
\r
2595 if(squareSize==72 || squareSize==49 || squareSize==33) { /* experiment with some home-made bitmaps */
\r
2596 pieceBitmap[0][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "s");
\r
2597 pieceBitmap[1][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "o");
\r
2598 pieceBitmap[2][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "w");
\r
2599 pieceBitmap[0][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "s");
\r
2600 pieceBitmap[1][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "o");
\r
2601 pieceBitmap[2][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "w");
\r
2602 pieceBitmap[0][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "s");
\r
2603 pieceBitmap[1][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "o");
\r
2604 pieceBitmap[2][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "w");
\r
2605 pieceBitmap[0][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "s");
\r
2606 pieceBitmap[1][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "o");
\r
2607 pieceBitmap[2][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "w");
\r
2608 pieceBitmap[0][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "s");
\r
2609 pieceBitmap[1][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "o");
\r
2610 pieceBitmap[2][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "w");
\r
2611 pieceBitmap[0][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "s");
\r
2612 pieceBitmap[1][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "o");
\r
2613 pieceBitmap[2][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "w");
\r
2614 pieceBitmap[0][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "s");
\r
2615 pieceBitmap[1][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "o");
\r
2616 pieceBitmap[2][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "w");
\r
2617 pieceBitmap[0][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "s");
\r
2618 pieceBitmap[1][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "o");
\r
2619 pieceBitmap[2][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "w");
\r
2620 pieceBitmap[0][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "s");
\r
2621 pieceBitmap[1][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "o");
\r
2622 pieceBitmap[2][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "w");
\r
2623 pieceBitmap[0][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "s");
\r
2624 pieceBitmap[1][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "o");
\r
2625 pieceBitmap[2][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "w");
\r
2627 if(gameInfo.variant == VariantShogi) { /* promoted Gold represemtations */
\r
2628 pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "wp", squareSize, "s");
\r
2629 pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "wp", squareSize, "o");
\r
2630 pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "w", squareSize, "w");
\r
2631 pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "wn", squareSize, "s");
\r
2632 pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "wn", squareSize, "o");
\r
2633 pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "w", squareSize, "w");
\r
2634 pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "ws", squareSize, "s");
\r
2635 pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "ws", squareSize, "o");
\r
2636 pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "w", squareSize, "w");
\r
2637 pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "wl", squareSize, "s");
\r
2638 pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "wl", squareSize, "o");
\r
2639 pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "w", squareSize, "w");
\r
2641 pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "s");
\r
2642 pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "o");
\r
2643 pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "w");
\r
2644 pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "s");
\r
2645 pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "o");
\r
2646 pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "w");
\r
2647 pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "s");
\r
2648 pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "o");
\r
2649 pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "w");
\r
2650 pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "s");
\r
2651 pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "o");
\r
2652 pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "w");
\r
2655 } else { /* other size, no special bitmaps available. Use smaller symbols */
\r
2656 if((int)boardSize < 2) minorSize = sizeInfo[0].squareSize;
\r
2657 else minorSize = sizeInfo[(int)boardSize - 2].squareSize;
\r
2658 pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "s");
\r
2659 pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "o");
\r
2660 pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "w");
\r
2661 pieceBitmap[0][WhiteCardinal] = DoLoadBitmap(hInst, "b", minorSize, "s");
\r
2662 pieceBitmap[1][WhiteCardinal] = DoLoadBitmap(hInst, "b", minorSize, "o");
\r
2663 pieceBitmap[2][WhiteCardinal] = DoLoadBitmap(hInst, "b", minorSize, "w");
\r
2664 pieceBitmap[0][WhiteDragon] = DoLoadBitmap(hInst, "r", minorSize, "s");
\r
2665 pieceBitmap[1][WhiteDragon] = DoLoadBitmap(hInst, "r", minorSize, "o");
\r
2666 pieceBitmap[2][WhiteDragon] = DoLoadBitmap(hInst, "r", minorSize, "w");
\r
2667 pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "s");
\r
2668 pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "o");
\r
2669 pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "w");
\r
2673 if(gameInfo.variant == VariantShogi && squareSize == 58)
\r
2674 /* special Shogi support in this size */
\r
2675 { for (i=0; i<=2; i++) { /* replace all bitmaps */
\r
2676 for (piece = WhitePawn;
\r
2677 (int) piece < (int) BlackPawn;
\r
2678 piece = (ChessSquare) ((int) piece + 1)) {
\r
2679 if (pieceBitmap[i][piece] != NULL)
\r
2680 DeleteObject(pieceBitmap[i][piece]);
\r
2683 pieceBitmap[0][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "o");
\r
2684 pieceBitmap[0][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "o");
\r
2685 pieceBitmap[0][WhiteBishop] = DoLoadBitmap(hInst, "sb", squareSize, "o");
\r
2686 pieceBitmap[0][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "o");
\r
2687 pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "o");
\r
2688 pieceBitmap[0][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "o");
\r
2689 pieceBitmap[0][WhiteFerz] = DoLoadBitmap(hInst, "sf", squareSize, "o");
\r
2690 pieceBitmap[0][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "o");
\r
2691 pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "su", squareSize, "o");
\r
2692 pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "sh", squareSize, "o");
\r
2693 pieceBitmap[0][WhiteCardinal] = DoLoadBitmap(hInst, "sa", squareSize, "o");
\r
2694 pieceBitmap[0][WhiteDragon] = DoLoadBitmap(hInst, "sc", squareSize, "o");
\r
2695 pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "sg", squareSize, "o");
\r
2696 pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "ss", squareSize, "o");
\r
2697 pieceBitmap[1][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "o");
\r
2698 pieceBitmap[1][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "o");
\r
2699 pieceBitmap[1][WhiteBishop] = DoLoadBitmap(hInst, "sb", squareSize, "o");
\r
2700 pieceBitmap[1][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "o");
\r
2701 pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "o");
\r
2702 pieceBitmap[1][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "o");
\r
2703 pieceBitmap[1][WhiteFerz] = DoLoadBitmap(hInst, "sf", squareSize, "o");
\r
2704 pieceBitmap[1][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "o");
\r
2705 pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "su", squareSize, "o");
\r
2706 pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "sh", squareSize, "o");
\r
2707 pieceBitmap[1][WhiteCardinal] = DoLoadBitmap(hInst, "sa", squareSize, "o");
\r
2708 pieceBitmap[1][WhiteDragon] = DoLoadBitmap(hInst, "sc", squareSize, "o");
\r
2709 pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "sg", squareSize, "o");
\r
2710 pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "ss", squareSize, "o");
\r
2711 pieceBitmap[2][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "w");
\r
2712 pieceBitmap[2][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "w");
\r
2713 pieceBitmap[2][WhiteBishop] = DoLoadBitmap(hInst, "sr", squareSize, "w");
\r
2714 pieceBitmap[2][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "w");
\r
2715 pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "w");
\r
2716 pieceBitmap[2][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "w");
\r
2717 pieceBitmap[2][WhiteFerz] = DoLoadBitmap(hInst, "sw", squareSize, "w");
\r
2718 pieceBitmap[2][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "w");
\r
2719 pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "sp", squareSize, "w");
\r
2720 pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "sn", squareSize, "w");
\r
2721 pieceBitmap[2][WhiteCardinal] = DoLoadBitmap(hInst, "sr", squareSize, "w");
\r
2722 pieceBitmap[2][WhiteDragon] = DoLoadBitmap(hInst, "sr", squareSize, "w");
\r
2723 pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "sl", squareSize, "w");
\r
2724 pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "sw", squareSize, "w");
\r
2730 PieceBitmap(ChessSquare p, int kind)
\r
2732 if ((int) p >= (int) BlackPawn)
\r
2733 p = (ChessSquare) ((int) p - (int) BlackPawn + (int) WhitePawn);
\r
2735 return pieceBitmap[kind][(int) p];
\r
2738 /***************************************************************/
\r
2740 #define MIN(a,b) (((a) < (b)) ? (a) : (b))
\r
2741 #define MAX(a,b) (((a) > (b)) ? (a) : (b))
\r
2743 #define MIN3(a,b,c) (((a) < (b) && (a) < (c)) ? (a) : (((b) < (a) && (b) < (c)) ? (b) : (c)))
\r
2744 #define MAX3(a,b,c) (((a) > (b) && (a) > (c)) ? (a) : (((b) > (a) && (b) > (c)) ? (b) : (c)))
\r
2748 SquareToPos(int row, int column, int * x, int * y)
\r
2751 *x = boardRect.left + lineGap + ((BOARD_WIDTH-1)-column) * (squareSize + lineGap) + border;
\r
2752 *y = boardRect.top + lineGap + row * (squareSize + lineGap) + border;
\r
2754 *x = boardRect.left + lineGap + column * (squareSize + lineGap) + border;
\r
2755 *y = boardRect.top + lineGap + ((BOARD_HEIGHT-1)-row) * (squareSize + lineGap) + border;
\r
2760 DrawCoordsOnDC(HDC hdc)
\r
2762 static char files[] = "0123456789012345678901221098765432109876543210";
\r
2763 static char ranks[] = "wvutsrqponmlkjihgfedcbaabcdefghijklmnopqrstuvw";
\r
2764 char str[2] = { NULLCHAR, NULLCHAR };
\r
2765 int oldMode, oldAlign, x, y, start, i;
\r
2769 if (!appData.showCoords)
\r
2772 start = flipView ? 1-(ONE!='1') : 45+(ONE!='1')-BOARD_HEIGHT;
\r
2774 oldBrush = SelectObject(hdc, GetStockObject(BLACK_BRUSH));
\r
2775 oldMode = SetBkMode(hdc, (appData.monoMode ? OPAQUE : TRANSPARENT));
\r
2776 oldAlign = GetTextAlign(hdc);
\r
2777 oldFont = SelectObject(hdc, font[boardSize][COORD_FONT]->hf);
\r
2779 y = boardRect.top + lineGap;
\r
2780 x = boardRect.left + lineGap + gameInfo.holdingsWidth*(squareSize + lineGap);
\r
2783 SetTextAlign(hdc, TA_RIGHT|TA_TOP);
\r
2784 x += border - lineGap - 4; y += squareSize - 6;
\r
2786 SetTextAlign(hdc, TA_LEFT|TA_TOP);
\r
2787 for (i = 0; i < BOARD_HEIGHT; i++) {
\r
2788 str[0] = files[start + i];
\r
2789 ExtTextOut(hdc, x + 2 - (border ? gameInfo.holdingsWidth * (squareSize + lineGap) : 0), y + 1, 0, NULL, str, 1, NULL);
\r
2790 y += squareSize + lineGap;
\r
2793 start = flipView ? 23-(BOARD_RGHT-BOARD_LEFT) : 23;
\r
2796 SetTextAlign(hdc, TA_LEFT|TA_TOP);
\r
2797 x += -border + 4; y += border - squareSize + 6;
\r
2799 SetTextAlign(hdc, TA_RIGHT|TA_BOTTOM);
\r
2800 for (i = 0; i < BOARD_RGHT - BOARD_LEFT; i++) {
\r
2801 str[0] = ranks[start + i];
\r
2802 ExtTextOut(hdc, x + squareSize - 2, y - 1, 0, NULL, str, 1, NULL);
\r
2803 x += squareSize + lineGap;
\r
2806 SelectObject(hdc, oldBrush);
\r
2807 SetBkMode(hdc, oldMode);
\r
2808 SetTextAlign(hdc, oldAlign);
\r
2809 SelectObject(hdc, oldFont);
\r
2813 DrawGridOnDC(HDC hdc)
\r
2817 if (lineGap != 0) {
\r
2818 oldPen = SelectObject(hdc, gridPen);
\r
2819 PolyPolyline(hdc, gridEndpoints, gridVertexCounts, BOARD_WIDTH+BOARD_HEIGHT + 2);
\r
2820 SelectObject(hdc, oldPen);
\r
2824 #define HIGHLIGHT_PEN 0
\r
2825 #define PREMOVE_PEN 1
\r
2828 DrawHighlightOnDC(HDC hdc, BOOLEAN on, int x, int y, int pen)
\r
2831 HPEN oldPen, hPen;
\r
2832 if (lineGap == 0) return;
\r
2834 x1 = boardRect.left +
\r
2835 lineGap/2 + ((BOARD_WIDTH-1)-x) * (squareSize + lineGap) + border;
\r
2836 y1 = boardRect.top +
\r
2837 lineGap/2 + y * (squareSize + lineGap) + border;
\r
2839 x1 = boardRect.left +
\r
2840 lineGap/2 + x * (squareSize + lineGap) + border;
\r
2841 y1 = boardRect.top +
\r
2842 lineGap/2 + ((BOARD_HEIGHT-1)-y) * (squareSize + lineGap) + border;
\r
2844 hPen = pen ? premovePen : highlightPen;
\r
2845 oldPen = SelectObject(hdc, on ? hPen : gridPen);
\r
2846 MoveToEx(hdc, x1, y1, NULL);
\r
2847 LineTo(hdc, x1 + squareSize + lineGap, y1);
\r
2848 LineTo(hdc, x1 + squareSize + lineGap, y1 + squareSize + lineGap);
\r
2849 LineTo(hdc, x1, y1 + squareSize + lineGap);
\r
2850 LineTo(hdc, x1, y1);
\r
2851 SelectObject(hdc, oldPen);
\r
2855 DrawHighlightsOnDC(HDC hdc, HighlightInfo *h, int pen)
\r
2858 for (i=0; i<2; i++) {
\r
2859 if (h->sq[i].x >= 0 && h->sq[i].y >= 0)
\r
2860 DrawHighlightOnDC(hdc, TRUE,
\r
2861 h->sq[i].x, h->sq[i].y,
\r
2866 /* Note: sqcolor is used only in monoMode */
\r
2867 /* Note that this code is largely duplicated in woptions.c,
\r
2868 function DrawSampleSquare, so that needs to be updated too */
\r
2870 DrawPieceOnDC(HDC hdc, ChessSquare piece, int color, int sqcolor, int x, int y, HDC tmphdc)
\r
2872 HBITMAP oldBitmap;
\r
2876 if (appData.blindfold) return;
\r
2878 /* [AS] Use font-based pieces if needed */
\r
2879 if( fontBitmapSquareSize >= 0 && (squareSize > 32 || gameInfo.variant >= VariantShogi)) {
\r
2880 /* Create piece bitmaps, or do nothing if piece set is up to date */
\r
2881 CreatePiecesFromFont();
\r
2883 if( fontBitmapSquareSize == squareSize ) {
\r
2884 int index = TranslatePieceToFontPiece(piece);
\r
2886 SelectObject( tmphdc, hPieceMask[ index ] );
\r
2888 if(appData.upsideDown ? color==flipView : (flipView && gameInfo.variant == VariantShogi))
\r
2889 StretchBlt(hdc, x+squareSize, y+squareSize, -squareSize, -squareSize, tmphdc, 0, 0, squareSize, squareSize, SRCAND);
\r
2893 squareSize, squareSize,
\r
2898 SelectObject( tmphdc, hPieceFace[ index ] );
\r
2900 if(appData.upsideDown ? color==flipView : (flipView && gameInfo.variant == VariantShogi))
\r
2901 StretchBlt(hdc, x+squareSize, y+squareSize, -squareSize, -squareSize, tmphdc, 0, 0, squareSize, squareSize, SRCPAINT);
\r
2905 squareSize, squareSize,
\r
2914 if (appData.monoMode) {
\r
2915 SelectObject(tmphdc, PieceBitmap(piece,
\r
2916 color == sqcolor ? OUTLINE_PIECE : SOLID_PIECE));
\r
2917 BitBlt(hdc, x, y, squareSize, squareSize, tmphdc, 0, 0,
\r
2918 sqcolor ? SRCCOPY : NOTSRCCOPY);
\r
2920 HBRUSH xBrush = whitePieceBrush;
\r
2921 tmpSize = squareSize;
\r
2922 if(appData.pieceDirectory[0]) xBrush = GetStockObject(WHITE_BRUSH);
\r
2924 ((piece >= (int)WhiteNightrider && piece <= WhiteGrasshopper) ||
\r
2925 (piece >= (int)BlackNightrider && piece <= BlackGrasshopper)) ) {
\r
2926 /* [HGM] no bitmap available for promoted pieces in Crazyhouse */
\r
2927 /* Bitmaps of smaller size are substituted, but we have to align them */
\r
2928 x += (squareSize - minorSize)>>1;
\r
2929 y += squareSize - minorSize - 2;
\r
2930 tmpSize = minorSize;
\r
2932 if (color || appData.allWhite ) {
\r
2933 oldBitmap = SelectObject(tmphdc, PieceBitmap(piece, WHITE_PIECE));
\r
2935 oldBrush = SelectObject(hdc, xBrush);
\r
2936 else oldBrush = SelectObject(hdc, blackPieceBrush);
\r
2937 if(appData.upsideDown && color==flipView)
\r
2938 StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, 0x00B8074A);
\r
2940 BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, 0x00B8074A);
\r
2941 /* Use black for outline of white pieces */
\r
2942 SelectObject(tmphdc, PieceBitmap(piece, OUTLINE_PIECE));
\r
2943 if(appData.upsideDown && color==flipView)
\r
2944 StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, SRCAND);
\r
2946 BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, SRCAND);
\r
2947 } else if(appData.pieceDirectory[0]) {
\r
2948 oldBitmap = SelectObject(tmphdc, PieceBitmap(piece, WHITE_PIECE));
\r
2949 oldBrush = SelectObject(hdc, xBrush);
\r
2950 if(appData.upsideDown && color==flipView)
\r
2951 StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, 0x00B8074A);
\r
2953 BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, 0x00B8074A);
\r
2954 SelectObject(tmphdc, PieceBitmap(piece, SOLID_PIECE));
\r
2955 if(appData.upsideDown && color==flipView)
\r
2956 StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, SRCAND);
\r
2958 BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, SRCAND);
\r
2960 /* Use square color for details of black pieces */
\r
2961 oldBitmap = SelectObject(tmphdc, PieceBitmap(piece, SOLID_PIECE));
\r
2962 oldBrush = SelectObject(hdc, blackPieceBrush);
\r
2963 if(appData.upsideDown && !flipView)
\r
2964 StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, 0x00B8074A);
\r
2966 BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, 0x00B8074A);
\r
2968 SelectObject(hdc, oldBrush);
\r
2969 SelectObject(tmphdc, oldBitmap);
\r
2973 /* [AS] Compute a drawing mode for a square, based on specified settings (see DrawTile) */
\r
2974 int GetBackTextureMode( int algo )
\r
2976 int result = BACK_TEXTURE_MODE_DISABLED;
\r
2980 case BACK_TEXTURE_MODE_PLAIN:
\r
2981 result = 1; /* Always use identity map */
\r
2983 case BACK_TEXTURE_MODE_FULL_RANDOM:
\r
2984 result = 1 + (myrandom() % 3); /* Pick a transformation at random */
\r
2992 [AS] Compute and save texture drawing info, otherwise we may not be able
\r
2993 to handle redraws cleanly (as random numbers would always be different).
\r
2995 VOID RebuildTextureSquareInfo()
\r
3005 ZeroMemory( &backTextureSquareInfo, sizeof(backTextureSquareInfo) );
\r
3007 if( liteBackTexture != NULL ) {
\r
3008 if( GetObject( liteBackTexture, sizeof(bi), &bi ) > 0 ) {
\r
3009 lite_w = bi.bmWidth;
\r
3010 lite_h = bi.bmHeight;
\r
3014 if( darkBackTexture != NULL ) {
\r
3015 if( GetObject( darkBackTexture, sizeof(bi), &bi ) > 0 ) {
\r
3016 dark_w = bi.bmWidth;
\r
3017 dark_h = bi.bmHeight;
\r
3021 for( row=0; row<BOARD_HEIGHT; row++ ) {
\r
3022 for( col=0; col<BOARD_WIDTH; col++ ) {
\r
3023 if( (col + row) & 1 ) {
\r
3025 if( lite_w >= squareSize && lite_h >= squareSize ) {
\r
3026 if( lite_w >= squareSize*BOARD_WIDTH )
\r
3027 backTextureSquareInfo[row][col].x = (2*col+1)*lite_w/(2*BOARD_WIDTH) - squareSize/2; /* [HGM] cut out of center of virtual square */
\r
3029 backTextureSquareInfo[row][col].x = col * (lite_w - squareSize) / (BOARD_WIDTH-1); /* [HGM] divide by size-1 in stead of size! */
\r
3030 if( lite_h >= squareSize*BOARD_HEIGHT )
\r
3031 backTextureSquareInfo[row][col].y = (2*(BOARD_HEIGHT-row)-1)*lite_h/(2*BOARD_HEIGHT) - squareSize/2;
\r
3033 backTextureSquareInfo[row][col].y = (BOARD_HEIGHT-1-row) * (lite_h - squareSize) / (BOARD_HEIGHT-1);
\r
3034 backTextureSquareInfo[row][col].mode = GetBackTextureMode(liteBackTextureMode);
\r
3039 if( dark_w >= squareSize && dark_h >= squareSize ) {
\r
3040 if( dark_w >= squareSize*BOARD_WIDTH )
\r
3041 backTextureSquareInfo[row][col].x = (2*col+1) * dark_w / (2*BOARD_WIDTH) - squareSize/2;
\r
3043 backTextureSquareInfo[row][col].x = col * (dark_w - squareSize) / (BOARD_WIDTH-1);
\r
3044 if( dark_h >= squareSize*BOARD_HEIGHT )
\r
3045 backTextureSquareInfo[row][col].y = (2*(BOARD_HEIGHT-row)-1) * dark_h / (2*BOARD_HEIGHT) - squareSize/2;
\r
3047 backTextureSquareInfo[row][col].y = (BOARD_HEIGHT-1-row) * (dark_h - squareSize) / (BOARD_HEIGHT-1);
\r
3048 backTextureSquareInfo[row][col].mode = GetBackTextureMode(darkBackTextureMode);
\r
3055 /* [AS] Arrow highlighting support */
\r
3057 static double A_WIDTH = 5; /* Width of arrow body */
\r
3059 #define A_HEIGHT_FACTOR 6 /* Length of arrow "point", relative to body width */
\r
3060 #define A_WIDTH_FACTOR 3 /* Width of arrow "point", relative to body width */
\r
3062 static double Sqr( double x )
\r
3067 static int Round( double x )
\r
3069 return (int) (x + 0.5);
\r
3072 /* Draw an arrow between two points using current settings */
\r
3073 VOID DrawArrowBetweenPoints( HDC hdc, int s_x, int s_y, int d_x, int d_y )
\r
3076 double dx, dy, j, k, x, y;
\r
3078 if( d_x == s_x ) {
\r
3079 int h = (d_y > s_y) ? +A_WIDTH*A_HEIGHT_FACTOR : -A_WIDTH*A_HEIGHT_FACTOR;
\r
3081 arrow[0].x = s_x + A_WIDTH + 0.5;
\r
3084 arrow[1].x = s_x + A_WIDTH + 0.5;
\r
3085 arrow[1].y = d_y - h;
\r
3087 arrow[2].x = arrow[1].x + A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;
\r
3088 arrow[2].y = d_y - h;
\r
3093 arrow[5].x = arrow[1].x - 2*A_WIDTH + 0.5;
\r
3094 arrow[5].y = d_y - h;
\r
3096 arrow[4].x = arrow[5].x - A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;
\r
3097 arrow[4].y = d_y - h;
\r
3099 arrow[6].x = arrow[1].x - 2*A_WIDTH + 0.5;
\r
3102 else if( d_y == s_y ) {
\r
3103 int w = (d_x > s_x) ? +A_WIDTH*A_HEIGHT_FACTOR : -A_WIDTH*A_HEIGHT_FACTOR;
\r
3106 arrow[0].y = s_y + A_WIDTH + 0.5;
\r
3108 arrow[1].x = d_x - w;
\r
3109 arrow[1].y = s_y + A_WIDTH + 0.5;
\r
3111 arrow[2].x = d_x - w;
\r
3112 arrow[2].y = arrow[1].y + A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;
\r
3117 arrow[5].x = d_x - w;
\r
3118 arrow[5].y = arrow[1].y - 2*A_WIDTH + 0.5;
\r
3120 arrow[4].x = d_x - w;
\r
3121 arrow[4].y = arrow[5].y - A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;
\r
3124 arrow[6].y = arrow[1].y - 2*A_WIDTH + 0.5;
\r
3127 /* [AS] Needed a lot of paper for this! :-) */
\r
3128 dy = (double) (d_y - s_y) / (double) (d_x - s_x);
\r
3129 dx = (double) (s_x - d_x) / (double) (s_y - d_y);
\r
3131 j = sqrt( Sqr(A_WIDTH) / (1.0 + Sqr(dx)) );
\r
3133 k = sqrt( Sqr(A_WIDTH*A_HEIGHT_FACTOR) / (1.0 + Sqr(dy)) );
\r
3138 arrow[0].x = Round(x - j);
\r
3139 arrow[0].y = Round(y + j*dx);
\r
3141 arrow[1].x = Round(arrow[0].x + 2*j); // [HGM] prevent width to be affected by rounding twice
\r
3142 arrow[1].y = Round(arrow[0].y - 2*j*dx);
\r
3145 x = (double) d_x - k;
\r
3146 y = (double) d_y - k*dy;
\r
3149 x = (double) d_x + k;
\r
3150 y = (double) d_y + k*dy;
\r
3153 x = Round(x); y = Round(y); // [HGM] make sure width of shaft is rounded the same way on both ends
\r
3155 arrow[6].x = Round(x - j);
\r
3156 arrow[6].y = Round(y + j*dx);
\r
3158 arrow[2].x = Round(arrow[6].x + 2*j);
\r
3159 arrow[2].y = Round(arrow[6].y - 2*j*dx);
\r
3161 arrow[3].x = Round(arrow[2].x + j*(A_WIDTH_FACTOR-1));
\r
3162 arrow[3].y = Round(arrow[2].y - j*(A_WIDTH_FACTOR-1)*dx);
\r
3167 arrow[5].x = Round(arrow[6].x - j*(A_WIDTH_FACTOR-1));
\r
3168 arrow[5].y = Round(arrow[6].y + j*(A_WIDTH_FACTOR-1)*dx);
\r
3171 Polygon( hdc, arrow, 7 );
\r
3174 /* [AS] Draw an arrow between two squares */
\r
3175 VOID DrawArrowBetweenSquares( HDC hdc, int s_col, int s_row, int d_col, int d_row )
\r
3177 int s_x, s_y, d_x, d_y;
\r
3184 if( s_col == d_col && s_row == d_row ) {
\r
3188 /* Get source and destination points */
\r
3189 SquareToPos( s_row, s_col, &s_x, &s_y);
\r
3190 SquareToPos( d_row, d_col, &d_x, &d_y);
\r
3193 d_y += squareSize / 2 - squareSize / 4; // [HGM] round towards same centers on all sides!
\r
3195 else if( d_y < s_y ) {
\r
3196 d_y += squareSize / 2 + squareSize / 4;
\r
3199 d_y += squareSize / 2;
\r
3203 d_x += squareSize / 2 - squareSize / 4;
\r
3205 else if( d_x < s_x ) {
\r
3206 d_x += squareSize / 2 + squareSize / 4;
\r
3209 d_x += squareSize / 2;
\r
3212 s_x += squareSize / 2;
\r
3213 s_y += squareSize / 2;
\r
3215 /* Adjust width */
\r
3216 A_WIDTH = squareSize / 14.; //[HGM] make float
\r
3219 stLB.lbStyle = BS_SOLID;
\r
3220 stLB.lbColor = appData.highlightArrowColor;
\r
3223 hpen = CreatePen( PS_SOLID, 2, RGB(0x00,0x00,0x00) );
\r
3224 holdpen = SelectObject( hdc, hpen );
\r
3225 hbrush = CreateBrushIndirect( &stLB );
\r
3226 holdbrush = SelectObject( hdc, hbrush );
\r
3228 DrawArrowBetweenPoints( hdc, s_x, s_y, d_x, d_y );
\r
3230 SelectObject( hdc, holdpen );
\r
3231 SelectObject( hdc, holdbrush );
\r
3232 DeleteObject( hpen );
\r
3233 DeleteObject( hbrush );
\r
3236 BOOL HasHighlightInfo()
\r
3238 BOOL result = FALSE;
\r
3240 if( highlightInfo.sq[0].x >= 0 && highlightInfo.sq[0].y >= 0 &&
\r
3241 highlightInfo.sq[1].x >= 0 && highlightInfo.sq[1].y >= 0 )
\r
3249 BOOL IsDrawArrowEnabled()
\r
3251 BOOL result = FALSE;
\r
3253 if( appData.highlightMoveWithArrow && squareSize >= 32 ) {
\r
3260 VOID DrawArrowHighlight( HDC hdc )
\r
3262 if( IsDrawArrowEnabled() && HasHighlightInfo() ) {
\r
3263 DrawArrowBetweenSquares( hdc,
\r
3264 highlightInfo.sq[0].x, highlightInfo.sq[0].y,
\r
3265 highlightInfo.sq[1].x, highlightInfo.sq[1].y );
\r
3269 HRGN GetArrowHighlightClipRegion( HDC hdc )
\r
3271 HRGN result = NULL;
\r
3273 if( HasHighlightInfo() ) {
\r
3274 int x1, y1, x2, y2;
\r
3275 int sx, sy, dx, dy;
\r
3277 SquareToPos(highlightInfo.sq[0].y, highlightInfo.sq[0].x, &x1, &y1 );
\r
3278 SquareToPos(highlightInfo.sq[1].y, highlightInfo.sq[1].x, &x2, &y2 );
\r
3280 sx = MIN( x1, x2 );
\r
3281 sy = MIN( y1, y2 );
\r
3282 dx = MAX( x1, x2 ) + squareSize;
\r
3283 dy = MAX( y1, y2 ) + squareSize;
\r
3285 result = CreateRectRgn( sx, sy, dx, dy );
\r
3292 Warning: this function modifies the behavior of several other functions.
\r
3294 Basically, Winboard is optimized to avoid drawing the whole board if not strictly
\r
3295 needed. Unfortunately, the decision whether or not to perform a full or partial
\r
3296 repaint is scattered all over the place, which is not good for features such as
\r
3297 "arrow highlighting" that require a full repaint of the board.
\r
3299 So, I've tried to patch the code where I thought it made sense (e.g. after or during
\r
3300 user interaction, when speed is not so important) but especially to avoid errors
\r
3301 in the displayed graphics.
\r
3303 In such patched places, I always try refer to this function so there is a single
\r
3304 place to maintain knowledge.
\r
3306 To restore the original behavior, just return FALSE unconditionally.
\r
3308 BOOL IsFullRepaintPreferrable()
\r
3310 BOOL result = FALSE;
\r
3312 if( (appData.highlightLastMove || appData.highlightDragging) && IsDrawArrowEnabled() ) {
\r
3313 /* Arrow may appear on the board */
\r
3321 This function is called by DrawPosition to know whether a full repaint must
\r
3324 Only DrawPosition may directly call this function, which makes use of
\r
3325 some state information. Other function should call DrawPosition specifying
\r
3326 the repaint flag, and can use IsFullRepaintPreferrable if needed.
\r
3328 BOOL DrawPositionNeedsFullRepaint()
\r
3330 BOOL result = FALSE;
\r
3333 Probably a slightly better policy would be to trigger a full repaint
\r
3334 when animInfo.piece changes state (i.e. empty -> non-empty and viceversa),
\r
3335 but animation is fast enough that it's difficult to notice.
\r
3337 if( animInfo.piece == EmptySquare ) {
\r
3338 if( (appData.highlightLastMove || appData.highlightDragging) && IsDrawArrowEnabled() /*&& HasHighlightInfo()*/ ) {
\r
3346 static HBITMAP borderBitmap;
\r
3349 DrawBackgroundOnDC(HDC hdc)
\r
3355 static char oldBorder[MSG_SIZ];
\r
3356 int w = 600, h = 600, mode;
\r
3358 if(strcmp(appData.border, oldBorder)) { // load new one when old one no longer valid
\r
3359 strncpy(oldBorder, appData.border, MSG_SIZ-1);
\r
3360 borderBitmap = LoadImage( 0, appData.border, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );
\r
3362 if(borderBitmap == NULL) { // loading failed, use white
\r
3363 FillRect( hdc, &boardRect, whitePieceBrush );
\r
3366 tmphdc = CreateCompatibleDC(hdc);
\r
3367 hbm = SelectObject(tmphdc, borderBitmap);
\r
3368 if( GetObject( borderBitmap, sizeof(bi), &bi ) > 0 ) {
\r
3372 mode = SetStretchBltMode(hdc, COLORONCOLOR);
\r
3373 StretchBlt(hdc, boardRect.left, boardRect.top, boardRect.right - boardRect.left,
\r
3374 boardRect.bottom - boardRect.top, tmphdc, 0, 0, w, h, SRCCOPY);
\r
3375 SetStretchBltMode(hdc, mode);
\r
3376 SelectObject(tmphdc, hbm);
\r
3381 DrawBoardOnDC(HDC hdc, Board board, HDC tmphdc)
\r
3383 int row, column, x, y, square_color, piece_color;
\r
3384 ChessSquare piece;
\r
3386 HDC texture_hdc = NULL;
\r
3388 /* [AS] Initialize background textures if needed */
\r
3389 if( liteBackTexture != NULL || darkBackTexture != NULL ) {
\r
3390 static int backTextureBoardSize; /* [HGM] boardsize: also new texture if board format changed */
\r
3391 if( backTextureSquareSize != squareSize
\r
3392 || backTextureBoardSize != BOARD_WIDTH+BOARD_FILES*BOARD_HEIGHT) {
\r
3393 backTextureBoardSize = BOARD_WIDTH+BOARD_FILES*BOARD_HEIGHT;
\r
3394 backTextureSquareSize = squareSize;
\r
3395 RebuildTextureSquareInfo();
\r
3398 texture_hdc = CreateCompatibleDC( hdc );
\r
3401 for (row = 0; row < BOARD_HEIGHT; row++) {
\r
3402 for (column = 0; column < BOARD_WIDTH; column++) {
\r
3404 SquareToPos(row, column, &x, &y);
\r
3406 piece = board[row][column];
\r
3408 square_color = ((column + row) % 2) == 1;
\r
3409 if( gameInfo.variant == VariantXiangqi ) {
\r
3410 square_color = !InPalace(row, column);
\r
3411 if(BOARD_HEIGHT&1) { if(row==BOARD_HEIGHT/2) square_color ^= 1; }
\r
3412 else if(row < BOARD_HEIGHT/2) square_color ^= 1;
\r
3414 piece_color = (int) piece < (int) BlackPawn;
\r
3417 /* [HGM] holdings file: light square or black */
\r
3418 if(column == BOARD_LEFT-2) {
\r
3419 if( row > BOARD_HEIGHT - gameInfo.holdingsSize - 1 )
\r
3422 DisplayHoldingsCount(hdc, x, y, 0, 0); /* black out */
\r
3426 if(column == BOARD_RGHT + 1 ) {
\r
3427 if( row < gameInfo.holdingsSize )
\r
3430 DisplayHoldingsCount(hdc, x, y, 0, 0);
\r
3434 if(column == BOARD_LEFT-1 ) /* left align */
\r
3435 DisplayHoldingsCount(hdc, x, y, flipView, (int) board[row][column]);
\r
3436 else if( column == BOARD_RGHT) /* right align */
\r
3437 DisplayHoldingsCount(hdc, x, y, !flipView, (int) board[row][column]);
\r
3439 if (appData.monoMode) {
\r
3440 if (piece == EmptySquare) {
\r
3441 BitBlt(hdc, x, y, squareSize, squareSize, 0, 0, 0,
\r
3442 square_color ? WHITENESS : BLACKNESS);
\r
3444 DrawPieceOnDC(hdc, piece, piece_color, square_color, x, y, tmphdc);
\r
3447 else if( appData.useBitmaps && backTextureSquareInfo[row][column].mode > 0 ) {
\r
3448 /* [AS] Draw the square using a texture bitmap */
\r
3449 HBITMAP hbm = SelectObject( texture_hdc, square_color ? liteBackTexture : darkBackTexture );
\r
3450 int r = row, c = column; // [HGM] do not flip board in flipView
\r
3451 if(flipView) { r = BOARD_HEIGHT-1 - r; c = BOARD_WIDTH-1 - c; }
\r
3454 squareSize, squareSize,
\r
3457 backTextureSquareInfo[r][c].mode,
\r
3458 backTextureSquareInfo[r][c].x,
\r
3459 backTextureSquareInfo[r][c].y );
\r
3461 SelectObject( texture_hdc, hbm );
\r
3463 if (piece != EmptySquare) {
\r
3464 DrawPieceOnDC(hdc, piece, piece_color, -1, x, y, tmphdc);
\r
3468 HBRUSH brush = square_color ? lightSquareBrush : darkSquareBrush;
\r
3470 oldBrush = SelectObject(hdc, brush );
\r
3471 BitBlt(hdc, x, y, squareSize, squareSize, 0, 0, 0, PATCOPY);
\r
3472 SelectObject(hdc, oldBrush);
\r
3473 if (piece != EmptySquare)
\r
3474 DrawPieceOnDC(hdc, piece, piece_color, -1, x, y, tmphdc);
\r
3479 if( texture_hdc != NULL ) {
\r
3480 DeleteDC( texture_hdc );
\r
3484 int saveDiagFlag = 0; FILE *diagFile; // [HGM] diag
\r
3485 void fputDW(FILE *f, int x)
\r
3487 fputc(x & 255, f);
\r
3488 fputc(x>>8 & 255, f);
\r
3489 fputc(x>>16 & 255, f);
\r
3490 fputc(x>>24 & 255, f);
\r
3493 #define MAX_CLIPS 200 /* more than enough */
\r
3496 DrawLogoOnDC(HDC hdc, RECT logoRect, HBITMAP logo)
\r
3498 // HBITMAP bufferBitmap;
\r
3503 int w = 100, h = 50;
\r
3505 if(logo == NULL) {
\r
3506 if(!logoHeight) return;
\r
3507 FillRect( hdc, &logoRect, whitePieceBrush );
\r
3509 // GetClientRect(hwndMain, &Rect);
\r
3510 // bufferBitmap = CreateCompatibleBitmap(hdc, Rect.right-Rect.left+1,
\r
3511 // Rect.bottom-Rect.top+1);
\r
3512 tmphdc = CreateCompatibleDC(hdc);
\r
3513 hbm = SelectObject(tmphdc, logo);
\r
3514 if( GetObject( logo, sizeof(bi), &bi ) > 0 ) {
\r
3518 StretchBlt(hdc, logoRect.left, logoRect.top, logoRect.right - logoRect.left,
\r
3519 logoRect.bottom - logoRect.top, tmphdc, 0, 0, w, h, SRCCOPY);
\r
3520 SelectObject(tmphdc, hbm);
\r
3528 HDC hdc = GetDC(hwndMain);
\r
3529 HBITMAP whiteLogo = (HBITMAP) first.programLogo, blackLogo = (HBITMAP) second.programLogo;
\r
3530 if(appData.autoLogo) {
\r
3532 switch(gameMode) { // pick logos based on game mode
\r
3533 case IcsObserving:
\r
3534 whiteLogo = second.programLogo; // ICS logo
\r
3535 blackLogo = second.programLogo;
\r
3538 case IcsPlayingWhite:
\r
3539 if(!appData.zippyPlay) whiteLogo = userLogo;
\r
3540 blackLogo = second.programLogo; // ICS logo
\r
3542 case IcsPlayingBlack:
\r
3543 whiteLogo = second.programLogo; // ICS logo
\r
3544 blackLogo = appData.zippyPlay ? first.programLogo : userLogo;
\r
3546 case TwoMachinesPlay:
\r
3547 if(first.twoMachinesColor[0] == 'b') {
\r
3548 whiteLogo = second.programLogo;
\r
3549 blackLogo = first.programLogo;
\r
3552 case MachinePlaysWhite:
\r
3553 blackLogo = userLogo;
\r
3555 case MachinePlaysBlack:
\r
3556 whiteLogo = userLogo;
\r
3557 blackLogo = first.programLogo;
\r
3560 DrawLogoOnDC(hdc, leftLogoRect, flipClock ? blackLogo : whiteLogo);
\r
3561 DrawLogoOnDC(hdc, rightLogoRect, flipClock ? whiteLogo : blackLogo);
\r
3562 ReleaseDC(hwndMain, hdc);
\r
3567 UpdateLogos(int display)
\r
3568 { // called after loading new engine(s), in tourney or from menu
\r
3569 LoadLogo(&first, 0, FALSE);
\r
3570 LoadLogo(&second, 1, appData.icsActive);
\r
3571 InitDrawingSizes(-2, 0); // adapt layout of board window to presence/absence of logos
\r
3572 if(display) DisplayLogos();
\r
3575 static HDC hdcSeek;
\r
3577 // [HGM] seekgraph
\r
3578 void DrawSeekAxis( int x, int y, int xTo, int yTo )
\r
3581 HPEN hp = SelectObject( hdcSeek, gridPen );
\r
3582 MoveToEx( hdcSeek, boardRect.left+x, boardRect.top+y, &stPt );
\r
3583 LineTo( hdcSeek, boardRect.left+xTo, boardRect.top+yTo );
\r
3584 SelectObject( hdcSeek, hp );
\r
3587 // front-end wrapper for drawing functions to do rectangles
\r
3588 void DrawSeekBackground( int left, int top, int right, int bottom )
\r
3593 if (hdcSeek == NULL) {
\r
3594 hdcSeek = GetDC(hwndMain);
\r
3595 if (!appData.monoMode) {
\r
3596 SelectPalette(hdcSeek, hPal, FALSE);
\r
3597 RealizePalette(hdcSeek);
\r
3600 hp = SelectObject( hdcSeek, gridPen );
\r
3601 rc.top = boardRect.top+top; rc.left = boardRect.left+left;
\r
3602 rc.bottom = boardRect.top+bottom; rc.right = boardRect.left+right;
\r
3603 FillRect( hdcSeek, &rc, lightSquareBrush );
\r
3604 SelectObject( hdcSeek, hp );
\r
3607 // front-end wrapper for putting text in graph
\r
3608 void DrawSeekText(char *buf, int x, int y)
\r
3611 SetBkMode( hdcSeek, TRANSPARENT );
\r
3612 GetTextExtentPoint32( hdcSeek, buf, strlen(buf), &stSize );
\r
3613 TextOut( hdcSeek, boardRect.left+x-3, boardRect.top+y-stSize.cy/2, buf, strlen(buf) );
\r
3616 void DrawSeekDot(int x, int y, int color)
\r
3618 int square = color & 0x80;
\r
3619 HBRUSH oldBrush = SelectObject(hdcSeek,
\r
3620 color == 0 ? markerBrush : color == 1 ? darkSquareBrush : explodeBrush);
\r
3623 Rectangle(hdcSeek, boardRect.left+x - squareSize/9, boardRect.top+y - squareSize/9,
\r
3624 boardRect.left+x + squareSize/9, boardRect.top+y + squareSize/9);
\r
3626 Ellipse(hdcSeek, boardRect.left+x - squareSize/8, boardRect.top+y - squareSize/8,
\r
3627 boardRect.left+x + squareSize/8, boardRect.top+y + squareSize/8);
\r
3628 SelectObject(hdcSeek, oldBrush);
\r
3631 void DrawSeekOpen()
\r
3635 void DrawSeekClose()
\r
3640 HDCDrawPosition(HDC hdc, BOOLEAN repaint, Board board)
\r
3642 static Board lastReq[2], lastDrawn[2];
\r
3643 static HighlightInfo lastDrawnHighlight, lastDrawnPremove;
\r
3644 static int lastDrawnFlipView = 0;
\r
3645 static int lastReqValid[2] = {0, 0}, lastDrawnValid[2] = {0, 0};
\r
3646 int releaseDC, x, y, x2, y2, row, column, num_clips = 0, i;
\r
3649 HBITMAP bufferBitmap;
\r
3650 HBITMAP oldBitmap;
\r
3652 HRGN clips[MAX_CLIPS];
\r
3653 ChessSquare dragged_piece = EmptySquare;
\r
3654 int nr = twoBoards*partnerUp;
\r
3656 /* I'm undecided on this - this function figures out whether a full
\r
3657 * repaint is necessary on its own, so there's no real reason to have the
\r
3658 * caller tell it that. I think this can safely be set to FALSE - but
\r
3659 * if we trust the callers not to request full repaints unnessesarily, then
\r
3660 * we could skip some clipping work. In other words, only request a full
\r
3661 * redraw when the majority of pieces have changed positions (ie. flip,
\r
3662 * gamestart and similar) --Hawk
\r
3664 Boolean fullrepaint = repaint;
\r
3666 if(DrawSeekGraph()) return; // [HG} seekgraph: suppress printing board if seek graph up
\r
3668 if( DrawPositionNeedsFullRepaint() ) {
\r
3669 fullrepaint = TRUE;
\r
3672 if (board == NULL) {
\r
3673 if (!lastReqValid[nr]) {
\r
3676 board = lastReq[nr];
\r
3678 CopyBoard(lastReq[nr], board);
\r
3679 lastReqValid[nr] = 1;
\r
3682 if (doingSizing) {
\r
3686 if (IsIconic(hwndMain)) {
\r
3690 if (hdc == NULL) {
\r
3691 hdc = GetDC(hwndMain);
\r
3692 if (!appData.monoMode) {
\r
3693 SelectPalette(hdc, hPal, FALSE);
\r
3694 RealizePalette(hdc);
\r
3698 releaseDC = FALSE;
\r
3701 /* Create some work-DCs */
\r
3702 hdcmem = CreateCompatibleDC(hdc);
\r
3703 tmphdc = CreateCompatibleDC(hdc);
\r
3705 /* If dragging is in progress, we temporarely remove the piece */
\r
3706 /* [HGM] or temporarily decrease count if stacked */
\r
3707 /* !! Moved to before board compare !! */
\r
3708 if (dragInfo.from.x >= 0 && dragInfo.pos.x >= 0) {
\r
3709 dragged_piece = board[dragInfo.from.y][dragInfo.from.x];
\r
3710 if(dragInfo.from.x == BOARD_LEFT-2 ) {
\r
3711 if(--board[dragInfo.from.y][dragInfo.from.x+1] == 0 )
\r
3712 board[dragInfo.from.y][dragInfo.from.x] = EmptySquare;
\r
3714 if(dragInfo.from.x == BOARD_RGHT+1) {
\r
3715 if(--board[dragInfo.from.y][dragInfo.from.x-1] == 0 )
\r
3716 board[dragInfo.from.y][dragInfo.from.x] = EmptySquare;
\r
3718 board[dragInfo.from.y][dragInfo.from.x] = gatingPiece;
\r
3721 /* Figure out which squares need updating by comparing the
\r
3722 * newest board with the last drawn board and checking if
\r
3723 * flipping has changed.
\r
3725 if (!fullrepaint && lastDrawnValid[nr] && (nr == 1 || lastDrawnFlipView == flipView)) {
\r
3726 for (row = 0; row < BOARD_HEIGHT; row++) { /* [HGM] true size, not 8 */
\r
3727 for (column = 0; column < BOARD_WIDTH; column++) {
\r
3728 if (lastDrawn[nr][row][column] != board[row][column]) {
\r
3729 SquareToPos(row, column, &x, &y);
\r
3730 clips[num_clips++] =
\r
3731 CreateRectRgn(x, y, x + squareSize, y + squareSize);
\r
3735 if(nr == 0) { // [HGM] dual: no highlights on second board
\r
3736 for (i=0; i<2; i++) {
\r
3737 if (lastDrawnHighlight.sq[i].x != highlightInfo.sq[i].x ||
\r
3738 lastDrawnHighlight.sq[i].y != highlightInfo.sq[i].y) {
\r
3739 if (lastDrawnHighlight.sq[i].x >= 0 &&
\r
3740 lastDrawnHighlight.sq[i].y >= 0) {
\r
3741 SquareToPos(lastDrawnHighlight.sq[i].y,
\r
3742 lastDrawnHighlight.sq[i].x, &x, &y);
\r
3743 clips[num_clips++] =
\r
3744 CreateRectRgn(x - lineGap, y - lineGap,
\r
3745 x + squareSize + lineGap, y + squareSize + lineGap);
\r
3747 if (highlightInfo.sq[i].x >= 0 && highlightInfo.sq[i].y >= 0) {
\r
3748 SquareToPos(highlightInfo.sq[i].y, highlightInfo.sq[i].x, &x, &y);
\r
3749 clips[num_clips++] =
\r
3750 CreateRectRgn(x - lineGap, y - lineGap,
\r
3751 x + squareSize + lineGap, y + squareSize + lineGap);
\r
3755 for (i=0; i<2; i++) {
\r
3756 if (lastDrawnPremove.sq[i].x != premoveHighlightInfo.sq[i].x ||
\r
3757 lastDrawnPremove.sq[i].y != premoveHighlightInfo.sq[i].y) {
\r
3758 if (lastDrawnPremove.sq[i].x >= 0 &&
\r
3759 lastDrawnPremove.sq[i].y >= 0) {
\r
3760 SquareToPos(lastDrawnPremove.sq[i].y,
\r
3761 lastDrawnPremove.sq[i].x, &x, &y);
\r
3762 clips[num_clips++] =
\r
3763 CreateRectRgn(x - lineGap, y - lineGap,
\r
3764 x + squareSize + lineGap, y + squareSize + lineGap);
\r
3766 if (premoveHighlightInfo.sq[i].x >= 0 &&
\r
3767 premoveHighlightInfo.sq[i].y >= 0) {
\r
3768 SquareToPos(premoveHighlightInfo.sq[i].y,
\r
3769 premoveHighlightInfo.sq[i].x, &x, &y);
\r
3770 clips[num_clips++] =
\r
3771 CreateRectRgn(x - lineGap, y - lineGap,
\r
3772 x + squareSize + lineGap, y + squareSize + lineGap);
\r
3776 } else { // nr == 1
\r
3777 partnerHighlightInfo.sq[0].y = board[EP_STATUS-4];
\r
3778 partnerHighlightInfo.sq[0].x = board[EP_STATUS-3];
\r
3779 partnerHighlightInfo.sq[1].y = board[EP_STATUS-2];
\r
3780 partnerHighlightInfo.sq[1].x = board[EP_STATUS-1];
\r
3781 for (i=0; i<2; i++) {
\r
3782 if (partnerHighlightInfo.sq[i].x >= 0 &&
\r
3783 partnerHighlightInfo.sq[i].y >= 0) {
\r
3784 SquareToPos(partnerHighlightInfo.sq[i].y,
\r
3785 partnerHighlightInfo.sq[i].x, &x, &y);
\r
3786 clips[num_clips++] =
\r
3787 CreateRectRgn(x - lineGap, y - lineGap,
\r
3788 x + squareSize + lineGap, y + squareSize + lineGap);
\r
3790 if (oldPartnerHighlight.sq[i].x >= 0 &&
\r
3791 oldPartnerHighlight.sq[i].y >= 0) {
\r
3792 SquareToPos(oldPartnerHighlight.sq[i].y,
\r
3793 oldPartnerHighlight.sq[i].x, &x, &y);
\r
3794 clips[num_clips++] =
\r
3795 CreateRectRgn(x - lineGap, y - lineGap,
\r
3796 x + squareSize + lineGap, y + squareSize + lineGap);
\r
3801 fullrepaint = TRUE;
\r
3804 /* Create a buffer bitmap - this is the actual bitmap
\r
3805 * being written to. When all the work is done, we can
\r
3806 * copy it to the real DC (the screen). This avoids
\r
3807 * the problems with flickering.
\r
3809 GetClientRect(hwndMain, &Rect);
\r
3810 bufferBitmap = CreateCompatibleBitmap(hdc, Rect.right-Rect.left+1,
\r
3811 Rect.bottom-Rect.top+1);
\r
3812 oldBitmap = SelectObject(hdcmem, bufferBitmap);
\r
3813 if (!appData.monoMode) {
\r
3814 SelectPalette(hdcmem, hPal, FALSE);
\r
3817 /* Create clips for dragging */
\r
3818 if (!fullrepaint) {
\r
3819 if (dragInfo.from.x >= 0) {
\r
3820 SquareToPos(dragInfo.from.y, dragInfo.from.x, &x, &y);
\r
3821 clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);
\r
3823 if (dragInfo.start.x >= 0) {
\r
3824 SquareToPos(dragInfo.start.y, dragInfo.start.x, &x, &y);
\r
3825 clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);
\r
3827 if (dragInfo.pos.x >= 0) {
\r
3828 x = dragInfo.pos.x - squareSize / 2;
\r
3829 y = dragInfo.pos.y - squareSize / 2;
\r
3830 clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);
\r
3832 if (dragInfo.lastpos.x >= 0) {
\r
3833 x = dragInfo.lastpos.x - squareSize / 2;
\r
3834 y = dragInfo.lastpos.y - squareSize / 2;
\r
3835 clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);
\r
3839 /* Are we animating a move?
\r
3841 * - remove the piece from the board (temporarely)
\r
3842 * - calculate the clipping region
\r
3844 if (!fullrepaint) {
\r
3845 if (animInfo.piece != EmptySquare) {
\r
3846 board[animInfo.from.y][animInfo.from.x] = EmptySquare;
\r
3847 x = boardRect.left + animInfo.lastpos.x;
\r
3848 y = boardRect.top + animInfo.lastpos.y;
\r
3849 x2 = boardRect.left + animInfo.pos.x;
\r
3850 y2 = boardRect.top + animInfo.pos.y;
\r
3851 clips[num_clips++] = CreateRectRgn(MIN(x,x2), MIN(y,y2), MAX(x,x2)+squareSize, MAX(y,y2)+squareSize);
\r
3852 /* Slight kludge. The real problem is that after AnimateMove is
\r
3853 done, the position on the screen does not match lastDrawn.
\r
3854 This currently causes trouble only on e.p. captures in
\r
3855 atomic, where the piece moves to an empty square and then
\r
3856 explodes. The old and new positions both had an empty square
\r
3857 at the destination, but animation has drawn a piece there and
\r
3858 we have to remember to erase it. [HGM] moved until after setting lastDrawn */
\r
3859 lastDrawn[0][animInfo.to.y][animInfo.to.x] = animInfo.piece;
\r
3863 /* No clips? Make sure we have fullrepaint set to TRUE */
\r
3864 if (num_clips == 0)
\r
3865 fullrepaint = TRUE;
\r
3867 /* Set clipping on the memory DC */
\r
3868 if (!fullrepaint) {
\r
3869 SelectClipRgn(hdcmem, clips[0]);
\r
3870 for (x = 1; x < num_clips; x++) {
\r
3871 if (ExtSelectClipRgn(hdcmem, clips[x], RGN_OR) == ERROR)
\r
3872 abort(); // this should never ever happen!
\r
3876 /* Do all the drawing to the memory DC */
\r
3877 if(explodeInfo.radius) { // [HGM] atomic
\r
3879 int x, y, r=(explodeInfo.radius * squareSize)/100;
\r
3880 ChessSquare piece = board[explodeInfo.fromY][explodeInfo.fromX];
\r
3881 board[explodeInfo.fromY][explodeInfo.fromX] = EmptySquare; // suppress display of capturer
\r
3882 SquareToPos(explodeInfo.toY, explodeInfo.toX, &x, &y);
\r
3883 x += squareSize/2;
\r
3884 y += squareSize/2;
\r
3885 if(!fullrepaint) {
\r
3886 clips[num_clips] = CreateRectRgn(x-r, y-r, x+r, y+r);
\r
3887 ExtSelectClipRgn(hdcmem, clips[num_clips++], RGN_OR);
\r
3889 DrawGridOnDC(hdcmem);
\r
3890 DrawHighlightsOnDC(hdcmem, &highlightInfo, HIGHLIGHT_PEN);
\r
3891 DrawHighlightsOnDC(hdcmem, &premoveHighlightInfo, PREMOVE_PEN);
\r
3892 DrawBoardOnDC(hdcmem, board, tmphdc);
\r
3893 board[explodeInfo.fromY][explodeInfo.fromX] = piece;
\r
3894 oldBrush = SelectObject(hdcmem, explodeBrush);
\r
3895 Ellipse(hdcmem, x-r, y-r, x+r, y+r);
\r
3896 SelectObject(hdcmem, oldBrush);
\r
3898 if(border) DrawBackgroundOnDC(hdcmem);
\r
3899 DrawGridOnDC(hdcmem);
\r
3900 if(nr == 0) { // [HGM] dual: decide which highlights to draw
\r
3901 DrawHighlightsOnDC(hdcmem, &highlightInfo, HIGHLIGHT_PEN);
\r
3902 DrawHighlightsOnDC(hdcmem, &premoveHighlightInfo, PREMOVE_PEN);
\r
3904 DrawHighlightsOnDC(hdcmem, &partnerHighlightInfo, HIGHLIGHT_PEN);
\r
3905 oldPartnerHighlight = partnerHighlightInfo;
\r
3907 DrawBoardOnDC(hdcmem, board, tmphdc);
\r
3909 if(nr == 0) // [HGM] dual: markers only on left board
\r
3910 for (row = 0; row < BOARD_HEIGHT; row++) {
\r
3911 for (column = 0; column < BOARD_WIDTH; column++) {
\r
3912 if (marker[row][column]) { // marker changes only occur with full repaint!
\r
3913 HBRUSH oldBrush = SelectObject(hdcmem,
\r
3914 marker[row][column] == 2 ? markerBrush : explodeBrush);
\r
3915 SquareToPos(row, column, &x, &y);
\r
3916 Ellipse(hdcmem, x + squareSize/4, y + squareSize/4,
\r
3917 x + 3*squareSize/4, y + 3*squareSize/4);
\r
3918 SelectObject(hdcmem, oldBrush);
\r
3923 if( appData.highlightMoveWithArrow ) {
\r
3924 DrawArrowHighlight(hdcmem);
\r
3927 DrawCoordsOnDC(hdcmem);
\r
3929 CopyBoard(lastDrawn[nr], board); /* [HGM] Moved to here from end of routine, */
\r
3930 /* to make sure lastDrawn contains what is actually drawn */
\r
3932 /* Put the dragged piece back into place and draw it (out of place!) */
\r
3933 if (dragged_piece != EmptySquare) {
\r
3934 /* [HGM] or restack */
\r
3935 if(dragInfo.from.x == BOARD_LEFT-2 )
\r
3936 board[dragInfo.from.y][dragInfo.from.x+1]++;
\r
3938 if(dragInfo.from.x == BOARD_RGHT+1 )
\r
3939 board[dragInfo.from.y][dragInfo.from.x-1]++;
\r
3940 board[dragInfo.from.y][dragInfo.from.x] = dragged_piece;
\r
3941 x = dragInfo.pos.x - squareSize / 2;
\r
3942 y = dragInfo.pos.y - squareSize / 2;
\r
3943 DrawPieceOnDC(hdcmem, dragInfo.piece,
\r
3944 ((int) dragInfo.piece < (int) BlackPawn),
\r
3945 (dragInfo.from.y + dragInfo.from.x) % 2, x, y, tmphdc);
\r
3948 /* Put the animated piece back into place and draw it */
\r
3949 if (animInfo.piece != EmptySquare) {
\r
3950 board[animInfo.from.y][animInfo.from.x] = animInfo.piece;
\r
3951 x = boardRect.left + animInfo.pos.x;
\r
3952 y = boardRect.top + animInfo.pos.y;
\r
3953 DrawPieceOnDC(hdcmem, animInfo.piece,
\r
3954 ((int) animInfo.piece < (int) BlackPawn),
\r
3955 (animInfo.from.y + animInfo.from.x) % 2, x, y, tmphdc);
\r
3958 /* Release the bufferBitmap by selecting in the old bitmap
\r
3959 * and delete the memory DC
\r
3961 SelectObject(hdcmem, oldBitmap);
\r
3964 /* Set clipping on the target DC */
\r
3965 if (!fullrepaint) {
\r
3966 if(nr == 1) for (x = 0; x < num_clips; x++) { // [HGM] dual: translate clips
\r
3968 GetRgnBox(clips[x], &rect);
\r
3969 DeleteObject(clips[x]);
\r
3970 clips[x] = CreateRectRgn(rect.left + wpMain.width/2, rect.top,
\r
3971 rect.right + wpMain.width/2, rect.bottom);
\r
3973 SelectClipRgn(hdc, clips[0]);
\r
3974 for (x = 1; x < num_clips; x++) {
\r
3975 if (ExtSelectClipRgn(hdc, clips[x], RGN_OR) == ERROR)
\r
3976 abort(); // this should never ever happen!
\r
3980 /* Copy the new bitmap onto the screen in one go.
\r
3981 * This way we avoid any flickering
\r
3983 oldBitmap = SelectObject(tmphdc, bufferBitmap);
\r
3984 BitBlt(hdc, boardRect.left + twoBoards*partnerUp*wpMain.width/2, boardRect.top, // [HGM] dual
\r
3985 boardRect.right - boardRect.left,
\r
3986 boardRect.bottom - boardRect.top,
\r
3987 tmphdc, boardRect.left, boardRect.top, SRCCOPY);
\r
3988 if(saveDiagFlag) {
\r
3989 BITMAP b; int i, j=0, m, w, wb, fac=0; char *pData;
\r
3990 BITMAPINFOHEADER bih; int color[16], nrColors=0;
\r
3992 GetObject(bufferBitmap, sizeof(b), &b);
\r
3993 if(pData = malloc(b.bmWidthBytes*b.bmHeight + 10000)) {
\r
3994 bih.biSize = sizeof(BITMAPINFOHEADER);
\r
3995 bih.biWidth = b.bmWidth;
\r
3996 bih.biHeight = b.bmHeight;
\r
3998 bih.biBitCount = b.bmBitsPixel;
\r
3999 bih.biCompression = 0;
\r
4000 bih.biSizeImage = b.bmWidthBytes*b.bmHeight;
\r
4001 bih.biXPelsPerMeter = 0;
\r
4002 bih.biYPelsPerMeter = 0;
\r
4003 bih.biClrUsed = 0;
\r
4004 bih.biClrImportant = 0;
\r
4005 // fprintf(diagFile, "t=%d\nw=%d\nh=%d\nB=%d\nP=%d\nX=%d\n",
\r
4006 // b.bmType, b.bmWidth, b.bmHeight, b.bmWidthBytes, b.bmPlanes, b.bmBitsPixel);
\r
4007 GetDIBits(tmphdc,bufferBitmap,0,b.bmHeight,pData,(BITMAPINFO*)&bih,DIB_RGB_COLORS);
\r
4008 // fprintf(diagFile, "%8x\n", (int) pData);
\r
4010 wb = b.bmWidthBytes;
\r
4012 for(i=0; i<wb*(b.bmHeight - boardRect.top + OUTER_MARGIN)>>2; i++) {
\r
4013 int k = ((int*) pData)[i];
\r
4014 for(j=0; j<nrColors; j++) if(color[j] == k) break;
\r
4015 if(j >= 16) break;
\r
4017 if(j >= nrColors) nrColors = j+1;
\r
4019 if(j<16) { // 16 colors is enough. Compress to 4 bits per pixel
\r
4021 for(i=0; i<b.bmHeight - boardRect.top + OUTER_MARGIN; i++) {
\r
4022 for(w=0; w<(wb>>2); w+=2) {
\r
4023 int k = ((int*) pData)[(wb*i>>2) + w];
\r
4024 for(j=0; j<nrColors; j++) if(color[j] == k) break;
\r
4025 k = ((int*) pData)[(wb*i>>2) + w + 1];
\r
4026 for(m=0; m<nrColors; m++) if(color[m] == k) break;
\r
4027 pData[p++] = m | j<<4;
\r
4029 while(p&3) pData[p++] = 0;
\r
4032 wb = ((wb+31)>>5)<<2;
\r
4034 // write BITMAPFILEHEADER
\r
4035 fprintf(diagFile, "BM");
\r
4036 fputDW(diagFile, wb*(b.bmHeight - boardRect.top + OUTER_MARGIN)+0x36 + (fac?64:0));
\r
4037 fputDW(diagFile, 0);
\r
4038 fputDW(diagFile, 0x36 + (fac?64:0));
\r
4039 // write BITMAPINFOHEADER
\r
4040 fputDW(diagFile, 40);
\r
4041 fputDW(diagFile, b.bmWidth);
\r
4042 fputDW(diagFile, b.bmHeight - boardRect.top + OUTER_MARGIN);
\r
4043 if(fac) fputDW(diagFile, 0x040001); // planes and bits/pixel
\r
4044 else fputDW(diagFile, 0x200001); // planes and bits/pixel
\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 fputDW(diagFile, 0);
\r
4051 // write color table
\r
4053 for(i=0; i<16; i++) fputDW(diagFile, color[i]);
\r
4054 // write bitmap data
\r
4055 for(i=0; i<wb*(b.bmHeight - boardRect.top + OUTER_MARGIN); i++)
\r
4056 fputc(pData[i], diagFile);
\r
4061 SelectObject(tmphdc, oldBitmap);
\r
4063 /* Massive cleanup */
\r
4064 for (x = 0; x < num_clips; x++)
\r
4065 DeleteObject(clips[x]);
\r
4068 DeleteObject(bufferBitmap);
\r
4071 ReleaseDC(hwndMain, hdc);
\r
4073 if (lastDrawnFlipView != flipView && nr == 0) {
\r
4075 CheckMenuItem(GetMenu(hwndMain),IDM_FlipView, MF_BYCOMMAND|MF_CHECKED);
\r
4077 CheckMenuItem(GetMenu(hwndMain),IDM_FlipView, MF_BYCOMMAND|MF_UNCHECKED);
\r
4080 /* CopyBoard(lastDrawn, board);*/
\r
4081 lastDrawnHighlight = highlightInfo;
\r
4082 lastDrawnPremove = premoveHighlightInfo;
\r
4083 lastDrawnFlipView = flipView;
\r
4084 lastDrawnValid[nr] = 1;
\r
4087 /* [HGM] diag: Save the current board display to the given open file and close the file */
\r
4092 saveDiagFlag = 1; diagFile = f;
\r
4093 HDCDrawPosition(NULL, TRUE, NULL);
\r
4101 /*---------------------------------------------------------------------------*\
\r
4102 | CLIENT PAINT PROCEDURE
\r
4103 | This is the main event-handler for the WM_PAINT message.
\r
4105 \*---------------------------------------------------------------------------*/
\r
4107 PaintProc(HWND hwnd)
\r
4113 if((hdc = BeginPaint(hwnd, &ps))) {
\r
4114 if (IsIconic(hwnd)) {
\r
4115 DrawIcon(hdc, 2, 2, iconCurrent);
\r
4117 if (!appData.monoMode) {
\r
4118 SelectPalette(hdc, hPal, FALSE);
\r
4119 RealizePalette(hdc);
\r
4121 HDCDrawPosition(hdc, 1, NULL);
\r
4122 if(twoBoards) { // [HGM] dual: also redraw other board in other orientation
\r
4123 flipView = !flipView; partnerUp = !partnerUp;
\r
4124 HDCDrawPosition(hdc, 1, NULL);
\r
4125 flipView = !flipView; partnerUp = !partnerUp;
\r
4128 SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);
\r
4129 ExtTextOut(hdc, messageRect.left, messageRect.top,
\r
4130 ETO_CLIPPED|ETO_OPAQUE,
\r
4131 &messageRect, messageText, strlen(messageText), NULL);
\r
4132 SelectObject(hdc, oldFont);
\r
4133 DisplayBothClocks();
\r
4136 EndPaint(hwnd,&ps);
\r
4144 * If the user selects on a border boundary, return -1; if off the board,
\r
4145 * return -2. Otherwise map the event coordinate to the square.
\r
4146 * The offset boardRect.left or boardRect.top must already have been
\r
4147 * subtracted from x.
\r
4149 int EventToSquare(x, limit)
\r
4154 if (x < lineGap + border)
\r
4156 x -= lineGap + border;
\r
4157 if ((x % (squareSize + lineGap)) >= squareSize)
\r
4159 x /= (squareSize + lineGap);
\r
4171 DropEnable dropEnables[] = {
\r
4172 { 'P', DP_Pawn, N_("Pawn") },
\r
4173 { 'N', DP_Knight, N_("Knight") },
\r
4174 { 'B', DP_Bishop, N_("Bishop") },
\r
4175 { 'R', DP_Rook, N_("Rook") },
\r
4176 { 'Q', DP_Queen, N_("Queen") },
\r
4180 SetupDropMenu(HMENU hmenu)
\r
4182 int i, count, enable;
\r
4184 extern char white_holding[], black_holding[];
\r
4185 char item[MSG_SIZ];
\r
4187 for (i=0; i<sizeof(dropEnables)/sizeof(DropEnable); i++) {
\r
4188 p = strchr(gameMode == IcsPlayingWhite ? white_holding : black_holding,
\r
4189 dropEnables[i].piece);
\r
4191 while (p && *p++ == dropEnables[i].piece) count++;
\r
4192 snprintf(item, MSG_SIZ, "%s %d", T_(dropEnables[i].name), count);
\r
4193 enable = count > 0 || !appData.testLegality
\r
4194 /*!!temp:*/ || (gameInfo.variant == VariantCrazyhouse
\r
4195 && !appData.icsActive);
\r
4196 ModifyMenu(hmenu, dropEnables[i].command,
\r
4197 MF_BYCOMMAND | (enable ? MF_ENABLED : MF_GRAYED) | MF_STRING,
\r
4198 dropEnables[i].command, item);
\r
4202 void DragPieceBegin(int x, int y, Boolean instantly)
\r
4204 dragInfo.lastpos.x = boardRect.left + x;
\r
4205 dragInfo.lastpos.y = boardRect.top + y;
\r
4206 if(instantly) dragInfo.pos = dragInfo.lastpos;
\r
4207 dragInfo.from.x = fromX;
\r
4208 dragInfo.from.y = fromY;
\r
4209 dragInfo.piece = boards[currentMove][fromY][fromX];
\r
4210 dragInfo.start = dragInfo.from;
\r
4211 SetCapture(hwndMain);
\r
4214 void DragPieceEnd(int x, int y)
\r
4217 dragInfo.start.x = dragInfo.start.y = -1;
\r
4218 dragInfo.from = dragInfo.start;
\r
4219 dragInfo.pos = dragInfo.lastpos = dragInfo.start;
\r
4222 void ChangeDragPiece(ChessSquare piece)
\r
4224 dragInfo.piece = piece;
\r
4227 /* Event handler for mouse messages */
\r
4229 MouseEvent(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
\r
4233 static int recursive = 0;
\r
4235 BOOLEAN forceFullRepaint = IsFullRepaintPreferrable(); /* [AS] */
\r
4238 if (message == WM_MBUTTONUP) {
\r
4239 /* Hideous kludge to fool TrackPopupMenu into paying attention
\r
4240 to the middle button: we simulate pressing the left button too!
\r
4242 PostMessage(hwnd, WM_LBUTTONDOWN, wParam, lParam);
\r
4243 PostMessage(hwnd, WM_LBUTTONUP, wParam, lParam);
\r
4249 pt.x = LOWORD(lParam);
\r
4250 pt.y = HIWORD(lParam);
\r
4251 x = EventToSquare(pt.x - boardRect.left, BOARD_WIDTH);
\r
4252 y = EventToSquare(pt.y - boardRect.top, BOARD_HEIGHT);
\r
4253 if (!flipView && y >= 0) {
\r
4254 y = BOARD_HEIGHT - 1 - y;
\r
4256 if (flipView && x >= 0) {
\r
4257 x = BOARD_WIDTH - 1 - x;
\r
4260 shiftKey = GetKeyState(VK_SHIFT) < 0; // [HGM] remember last shift status
\r
4261 controlKey = GetKeyState(VK_CONTROL) < 0; // [HGM] remember last shift status
\r
4263 switch (message) {
\r
4264 case WM_LBUTTONDOWN:
\r
4265 if (PtInRect((LPRECT) &whiteRect, pt)) {
\r
4266 ClockClick(flipClock); break;
\r
4267 } else if (PtInRect((LPRECT) &blackRect, pt)) {
\r
4268 ClockClick(!flipClock); break;
\r
4270 dragInfo.start.x = dragInfo.start.y = -1;
\r
4271 dragInfo.from = dragInfo.start;
\r
4272 if(fromX == -1 && frozen) { // not sure where this is for
\r
4273 fromX = fromY = -1;
\r
4274 DrawPosition(forceFullRepaint || FALSE, NULL); /* [AS] */
\r
4277 LeftClick(Press, pt.x - boardRect.left, pt.y - boardRect.top);
\r
4278 DrawPosition(TRUE, NULL);
\r
4281 case WM_LBUTTONUP:
\r
4282 LeftClick(Release, pt.x - boardRect.left, pt.y - boardRect.top);
\r
4283 DrawPosition(TRUE, NULL);
\r
4286 case WM_MOUSEMOVE:
\r
4287 if(SeekGraphClick(Press, pt.x - boardRect.left, pt.y - boardRect.top, 1)) break;
\r
4288 if(PromoScroll(pt.x - boardRect.left, pt.y - boardRect.top)) break;
\r
4289 MovePV(pt.x - boardRect.left, pt.y - boardRect.top, boardRect.bottom - boardRect.top);
\r
4290 if ((appData.animateDragging || appData.highlightDragging)
\r
4291 && (wParam & MK_LBUTTON)
\r
4292 && dragInfo.from.x >= 0)
\r
4294 BOOL full_repaint = FALSE;
\r
4296 if (appData.animateDragging) {
\r
4297 dragInfo.pos = pt;
\r
4299 if (appData.highlightDragging) {
\r
4300 SetHighlights(fromX, fromY, x, y);
\r
4301 if( IsDrawArrowEnabled() && (x < 0 || x >= BOARD_WIDTH || y < 0 || y >= BOARD_HEIGHT) ) {
\r
4302 full_repaint = TRUE;
\r
4306 DrawPosition( full_repaint, NULL);
\r
4308 dragInfo.lastpos = dragInfo.pos;
\r
4312 case WM_MOUSEWHEEL: // [DM]
\r
4313 { static int lastDir = 0; // [HGM] build in some hysteresis to avoid spurious events
\r
4314 /* Mouse Wheel is being rolled forward
\r
4315 * Play moves forward
\r
4317 if((short)HIWORD(wParam) < 0 && currentMove < forwardMostMove)
\r
4318 { if(lastDir == 1) ForwardEvent(); else lastDir = 1; } // [HGM] suppress first event in direction
\r
4319 /* Mouse Wheel is being rolled backward
\r
4320 * Play moves backward
\r
4322 if((short)HIWORD(wParam) > 0 && currentMove > backwardMostMove)
\r
4323 { if(lastDir == -1) BackwardEvent(); else lastDir = -1; }
\r
4327 case WM_MBUTTONUP:
\r
4328 case WM_RBUTTONUP:
\r
4330 RightClick(Release, pt.x - boardRect.left, pt.y - boardRect.top, &fromX, &fromY);
\r
4333 case WM_MBUTTONDOWN:
\r
4334 case WM_RBUTTONDOWN:
\r
4337 fromX = fromY = -1;
\r
4338 dragInfo.pos.x = dragInfo.pos.y = -1;
\r
4339 dragInfo.start.x = dragInfo.start.y = -1;
\r
4340 dragInfo.from = dragInfo.start;
\r
4341 dragInfo.lastpos = dragInfo.pos;
\r
4342 if (appData.highlightDragging) {
\r
4343 ClearHighlights();
\r
4346 /* [HGM] right mouse button in clock area edit-game mode ups clock */
\r
4347 if (PtInRect((LPRECT) &whiteRect, pt)) {
\r
4348 if (GetKeyState(VK_SHIFT) < 0) AdjustClock(flipClock, 1);
\r
4349 } else if (PtInRect((LPRECT) &blackRect, pt)) {
\r
4350 if (GetKeyState(VK_SHIFT) < 0) AdjustClock(!flipClock, 1);
\r
4354 DrawPosition(TRUE, NULL);
\r
4356 menuNr = RightClick(Press, pt.x - boardRect.left, pt.y - boardRect.top, &fromX, &fromY);
\r
4359 if (message == WM_MBUTTONDOWN) {
\r
4360 buttonCount = 3; /* even if system didn't think so */
\r
4361 if (wParam & MK_SHIFT)
\r
4362 MenuPopup(hwnd, pt, LoadMenu(hInst, "BlackPieceMenu"), -1);
\r
4364 MenuPopup(hwnd, pt, LoadMenu(hInst, "WhitePieceMenu"), -1);
\r
4365 } else { /* message == WM_RBUTTONDOWN */
\r
4366 /* Just have one menu, on the right button. Windows users don't
\r
4367 think to try the middle one, and sometimes other software steals
\r
4368 it, or it doesn't really exist. */
\r
4369 if(gameInfo.variant != VariantShogi)
\r
4370 MenuPopup(hwnd, pt, LoadMenu(hInst, "PieceMenu"), -1);
\r
4372 MenuPopup(hwnd, pt, LoadMenu(hInst, "ShogiPieceMenu"), -1);
\r
4376 SetCapture(hwndMain);
\r
4379 hmenu = LoadMenu(hInst, "DropPieceMenu");
\r
4380 SetupDropMenu(hmenu);
\r
4381 MenuPopup(hwnd, pt, hmenu, -1);
\r
4391 /* Preprocess messages for buttons in main window */
\r
4393 ButtonProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
\r
4395 int id = GetWindowLongPtr(hwnd, GWLP_ID);
\r
4398 for (i=0; i<N_BUTTONS; i++) {
\r
4399 if (buttonDesc[i].id == id) break;
\r
4401 if (i == N_BUTTONS) return 0;
\r
4402 switch (message) {
\r
4407 dir = (wParam == VK_LEFT) ? -1 : 1;
\r
4408 SetFocus(buttonDesc[(i + dir + N_BUTTONS) % N_BUTTONS].hwnd);
\r
4415 SendMessage(hwndMain, WM_COMMAND, MAKEWPARAM(buttonDesc[i].id, 0), 0);
\r
4418 if (appData.icsActive && (isalpha((char)wParam) || wParam == '0')) {
\r
4419 // [HGM] movenum: only letters or leading zero should go to ICS input
\r
4420 HWND h = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
4421 if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);
\r
4423 SendMessage(h, WM_CHAR, wParam, lParam);
\r
4425 } else if (isalpha((char)wParam) || isdigit((char)wParam)){
\r
4426 TypeInEvent((char)wParam);
\r
4432 return CallWindowProc(buttonDesc[i].wndproc, hwnd, message, wParam, lParam);
\r
4435 /* Process messages for Promotion dialog box */
\r
4437 Promotion(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
4441 switch (message) {
\r
4442 case WM_INITDIALOG: /* message: initialize dialog box */
\r
4443 /* Center the dialog over the application window */
\r
4444 CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));
\r
4445 Translate(hDlg, DLG_PromotionKing);
\r
4446 ShowWindow(GetDlgItem(hDlg, PB_King),
\r
4447 (!appData.testLegality || gameInfo.variant == VariantSuicide ||
\r
4448 gameInfo.variant == VariantSpartan && !WhiteOnMove(currentMove) ||
\r
4449 gameInfo.variant == VariantGiveaway || gameInfo.variant == VariantSuper ) ?
\r
4450 SW_SHOW : SW_HIDE);
\r
4451 /* [HGM] Only allow C & A promotions if these pieces are defined */
\r
4452 ShowWindow(GetDlgItem(hDlg, PB_Archbishop),
\r
4453 ((PieceToChar(WhiteAngel) >= 'A' && WhiteOnMove(currentMove) &&
\r
4454 PieceToChar(WhiteAngel) != '~') ||
\r
4455 (PieceToChar(BlackAngel) >= 'A' && !WhiteOnMove(currentMove) &&
\r
4456 PieceToChar(BlackAngel) != '~') ) ?
\r
4457 SW_SHOW : SW_HIDE);
\r
4458 ShowWindow(GetDlgItem(hDlg, PB_Chancellor),
\r
4459 ((PieceToChar(WhiteMarshall) >= 'A' && WhiteOnMove(currentMove) &&
\r
4460 PieceToChar(WhiteMarshall) != '~') ||
\r
4461 (PieceToChar(BlackMarshall) >= 'A' && !WhiteOnMove(currentMove) &&
\r
4462 PieceToChar(BlackMarshall) != '~') ) ?
\r
4463 SW_SHOW : SW_HIDE);
\r
4464 /* [HGM] Hide B & R button in Shogi, use Q as promote, N as defer */
\r
4465 ShowWindow(GetDlgItem(hDlg, PB_Rook),
\r
4466 gameInfo.variant != VariantShogi ?
\r
4467 SW_SHOW : SW_HIDE);
\r
4468 ShowWindow(GetDlgItem(hDlg, PB_Bishop),
\r
4469 gameInfo.variant != VariantShogi ?
\r
4470 SW_SHOW : SW_HIDE);
\r
4471 if(gameInfo.variant == VariantShogi) {
\r
4472 SetDlgItemText(hDlg, PB_Queen, "YES");
\r
4473 SetDlgItemText(hDlg, PB_Knight, "NO");
\r
4474 SetWindowText(hDlg, "Promote?");
\r
4476 ShowWindow(GetDlgItem(hDlg, IDC_Centaur),
\r
4477 gameInfo.variant == VariantSuper ?
\r
4478 SW_SHOW : SW_HIDE);
\r
4481 case WM_COMMAND: /* message: received a command */
\r
4482 switch (LOWORD(wParam)) {
\r
4484 EndDialog(hDlg, TRUE); /* Exit the dialog */
\r
4485 ClearHighlights();
\r
4486 DrawPosition(FALSE, NULL);
\r
4489 promoChar = gameInfo.variant == VariantSuper ? PieceToChar(BlackSilver) : PieceToChar(BlackKing);
\r
4492 promoChar = gameInfo.variant == VariantShogi ? '+' : ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteQueen : BlackQueen));
\r
4495 promoChar = ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteRook : BlackRook));
\r
4496 if(gameInfo.variant == VariantSpartan && !WhiteOnMove(currentMove)) promoChar = PieceToChar(BlackDragon);
\r
4499 promoChar = ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteBishop : BlackBishop));
\r
4500 if(gameInfo.variant == VariantSpartan && !WhiteOnMove(currentMove)) promoChar = PieceToChar(BlackAlfil);
\r
4502 case PB_Chancellor:
\r
4503 promoChar = ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteMarshall : BlackMarshall));
\r
4505 case PB_Archbishop:
\r
4506 promoChar = ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteAngel : BlackAngel));
\r
4509 promoChar = gameInfo.variant == VariantShogi ? '=' : PieceToChar(WhiteOnMove(currentMove) ? WhiteKnight : BlackKnight);
\r
4514 if(promoChar == '.') return FALSE; // invalid piece chosen
\r
4515 EndDialog(hDlg, TRUE); /* Exit the dialog */
\r
4516 UserMoveEvent(fromX, fromY, toX, toY, promoChar);
\r
4517 fromX = fromY = -1;
\r
4518 if (!appData.highlightLastMove) {
\r
4519 ClearHighlights();
\r
4520 DrawPosition(FALSE, NULL);
\r
4527 /* Pop up promotion dialog */
\r
4529 PromotionPopup(HWND hwnd)
\r
4533 lpProc = MakeProcInstance((FARPROC)Promotion, hInst);
\r
4534 DialogBox(hInst, MAKEINTRESOURCE(DLG_PromotionKing),
\r
4535 hwnd, (DLGPROC)lpProc);
\r
4536 FreeProcInstance(lpProc);
\r
4542 DrawPosition(TRUE, NULL);
\r
4543 PromotionPopup(hwndMain);
\r
4547 LoadGameDialog(HWND hwnd, char* title)
\r
4551 char fileTitle[MSG_SIZ];
\r
4552 f = OpenFileDialog(hwnd, "rb", "",
\r
4553 appData.oldSaveStyle ? "gam" : "pgn",
\r
4555 title, &number, fileTitle, NULL);
\r
4557 cmailMsgLoaded = FALSE;
\r
4558 if (number == 0) {
\r
4559 int error = GameListBuild(f);
\r
4561 DisplayError(_("Cannot build game list"), error);
\r
4562 } else if (!ListEmpty(&gameList) &&
\r
4563 ((ListGame *) gameList.tailPred)->number > 1) {
\r
4564 GameListPopUp(f, fileTitle);
\r
4567 GameListDestroy();
\r
4570 LoadGame(f, number, fileTitle, FALSE);
\r
4574 int get_term_width()
\r
4579 HFONT hfont, hold_font;
\r
4584 hText = GetDlgItem(hwndConsole, OPT_ConsoleText);
\r
4588 // get the text metrics
\r
4589 hdc = GetDC(hText);
\r
4590 lf = font[boardSize][CONSOLE_FONT]->lf;
\r
4591 if (consoleCF.dwEffects & CFE_BOLD)
\r
4592 lf.lfWeight = FW_BOLD;
\r
4593 if (consoleCF.dwEffects & CFE_ITALIC)
\r
4594 lf.lfItalic = TRUE;
\r
4595 if (consoleCF.dwEffects & CFE_STRIKEOUT)
\r
4596 lf.lfStrikeOut = TRUE;
\r
4597 if (consoleCF.dwEffects & CFE_UNDERLINE)
\r
4598 lf.lfUnderline = TRUE;
\r
4599 hfont = CreateFontIndirect(&lf);
\r
4600 hold_font = SelectObject(hdc, hfont);
\r
4601 GetTextMetrics(hdc, &tm);
\r
4602 SelectObject(hdc, hold_font);
\r
4603 DeleteObject(hfont);
\r
4604 ReleaseDC(hText, hdc);
\r
4606 // get the rectangle
\r
4607 SendMessage(hText, EM_GETRECT, 0, (LPARAM)&rc);
\r
4609 return (rc.right-rc.left) / tm.tmAveCharWidth;
\r
4612 void UpdateICSWidth(HWND hText)
\r
4614 LONG old_width, new_width;
\r
4616 new_width = get_term_width(hText, FALSE);
\r
4617 old_width = GetWindowLongPtr(hText, GWLP_USERDATA);
\r
4618 if (new_width != old_width)
\r
4620 ics_update_width(new_width);
\r
4621 SetWindowLongPtr(hText, GWLP_USERDATA, new_width);
\r
4626 ChangedConsoleFont()
\r
4629 CHARRANGE tmpsel, sel;
\r
4630 MyFont *f = font[boardSize][CONSOLE_FONT];
\r
4631 HWND hText = GetDlgItem(hwndConsole, OPT_ConsoleText);
\r
4632 HWND hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
4635 cfmt.cbSize = sizeof(CHARFORMAT);
\r
4636 cfmt.dwMask = CFM_FACE|CFM_SIZE|CFM_CHARSET;
\r
4637 safeStrCpy(cfmt.szFaceName, font[boardSize][CONSOLE_FONT]->mfp.faceName,
\r
4638 sizeof(cfmt.szFaceName)/sizeof(cfmt.szFaceName[0]) );
\r
4639 /* yHeight is expressed in twips. A twip is 1/20 of a font's point
\r
4640 * size. This was undocumented in the version of MSVC++ that I had
\r
4641 * when I wrote the code, but is apparently documented now.
\r
4643 cfmt.yHeight = (int)(f->mfp.pointSize * 20.0 + 0.5);
\r
4644 cfmt.bCharSet = f->lf.lfCharSet;
\r
4645 cfmt.bPitchAndFamily = f->lf.lfPitchAndFamily;
\r
4646 SendMessage(hText, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &cfmt);
\r
4647 SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &cfmt);
\r
4648 /* Why are the following seemingly needed too? */
\r
4649 SendMessage(hText, EM_SETCHARFORMAT, SCF_DEFAULT, (LPARAM) &cfmt);
\r
4650 SendMessage(hInput, EM_SETCHARFORMAT, SCF_DEFAULT, (LPARAM) &cfmt);
\r
4651 SendMessage(hText, EM_EXGETSEL, 0, (LPARAM)&sel);
\r
4653 tmpsel.cpMax = -1; /*999999?*/
\r
4654 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&tmpsel);
\r
4655 SendMessage(hText, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cfmt);
\r
4656 /* Trying putting this here too. It still seems to tickle a RichEdit
\r
4657 * bug: sometimes RichEdit indents the first line of a paragraph too.
\r
4659 paraf.cbSize = sizeof(paraf);
\r
4660 paraf.dwMask = PFM_OFFSET | PFM_STARTINDENT;
\r
4661 paraf.dxStartIndent = 0;
\r
4662 paraf.dxOffset = WRAP_INDENT;
\r
4663 SendMessage(hText, EM_SETPARAFORMAT, 0, (LPARAM) ¶f);
\r
4664 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
4665 UpdateICSWidth(hText);
\r
4668 /*---------------------------------------------------------------------------*\
\r
4670 * Window Proc for main window
\r
4672 \*---------------------------------------------------------------------------*/
\r
4674 /* Process messages for main window, etc. */
\r
4676 WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
\r
4679 int wmId, wmEvent;
\r
4683 char fileTitle[MSG_SIZ];
\r
4684 static SnapData sd;
\r
4685 static int peek=0;
\r
4687 switch (message) {
\r
4689 case WM_PAINT: /* message: repaint portion of window */
\r
4693 case WM_ERASEBKGND:
\r
4694 if (IsIconic(hwnd)) {
\r
4695 /* Cheat; change the message */
\r
4696 return (DefWindowProc(hwnd, WM_ICONERASEBKGND, wParam, lParam));
\r
4698 return (DefWindowProc(hwnd, message, wParam, lParam));
\r
4702 case WM_LBUTTONDOWN:
\r
4703 case WM_MBUTTONDOWN:
\r
4704 case WM_RBUTTONDOWN:
\r
4705 case WM_LBUTTONUP:
\r
4706 case WM_MBUTTONUP:
\r
4707 case WM_RBUTTONUP:
\r
4708 case WM_MOUSEMOVE:
\r
4709 case WM_MOUSEWHEEL:
\r
4710 MouseEvent(hwnd, message, wParam, lParam);
\r
4714 if((char)wParam == '\b') {
\r
4715 ForwardEvent(); peek = 0;
\r
4718 JAWS_KBUP_NAVIGATION
\r
4723 if((char)wParam == '\b') {
\r
4724 if(!peek) BackwardEvent(), peek = 1;
\r
4727 JAWS_KBDOWN_NAVIGATION
\r
4733 JAWS_ALT_INTERCEPT
\r
4735 if (appData.icsActive && ((char)wParam == '\r' || (char)wParam > ' ' && !((char)wParam >= '1' && (char)wParam <= '9'))) {
\r
4736 // [HGM] movenum: for non-zero digits we always do type-in dialog
\r
4737 HWND h = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
4738 if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);
\r
4740 SendMessage(h, message, wParam, lParam);
\r
4741 } else if(lParam != KF_REPEAT) {
\r
4742 if (isalpha((char)wParam) || isdigit((char)wParam)) {
\r
4743 TypeInEvent((char)wParam);
\r
4744 } else if((char)wParam == 003) CopyGameToClipboard();
\r
4745 else if((char)wParam == 026) PasteGameOrFENFromClipboard();
\r
4750 case WM_PALETTECHANGED:
\r
4751 if (hwnd != (HWND)wParam && !appData.monoMode) {
\r
4753 HDC hdc = GetDC(hwndMain);
\r
4754 SelectPalette(hdc, hPal, TRUE);
\r
4755 nnew = RealizePalette(hdc);
\r
4757 paletteChanged = TRUE;
\r
4758 InvalidateRect(hwnd, &boardRect, FALSE);
\r
4760 ReleaseDC(hwnd, hdc);
\r
4764 case WM_QUERYNEWPALETTE:
\r
4765 if (!appData.monoMode /*&& paletteChanged*/) {
\r
4767 HDC hdc = GetDC(hwndMain);
\r
4768 paletteChanged = FALSE;
\r
4769 SelectPalette(hdc, hPal, FALSE);
\r
4770 nnew = RealizePalette(hdc);
\r
4772 InvalidateRect(hwnd, &boardRect, FALSE);
\r
4774 ReleaseDC(hwnd, hdc);
\r
4779 case WM_COMMAND: /* message: command from application menu */
\r
4780 wmId = LOWORD(wParam);
\r
4781 wmEvent = HIWORD(wParam);
\r
4786 SAY("new game enter a move to play against the computer with white");
\r
4789 case IDM_NewGameFRC:
\r
4790 if( NewGameFRC() == 0 ) {
\r
4795 case IDM_NewVariant:
\r
4796 NewVariantPopup(hwnd);
\r
4799 case IDM_LoadGame:
\r
4800 LoadGameDialog(hwnd, _("Load Game from File"));
\r
4803 case IDM_LoadNextGame:
\r
4807 case IDM_LoadPrevGame:
\r
4811 case IDM_ReloadGame:
\r
4815 case IDM_LoadPosition:
\r
4816 if (gameMode == AnalyzeMode || gameMode == AnalyzeFile) {
\r
4817 Reset(FALSE, TRUE);
\r
4820 f = OpenFileDialog(hwnd, "rb", "",
\r
4821 appData.oldSaveStyle ? "pos" : "fen",
\r
4823 _("Load Position from File"), &number, fileTitle, NULL);
\r
4825 LoadPosition(f, number, fileTitle);
\r
4829 case IDM_LoadNextPosition:
\r
4830 ReloadPosition(1);
\r
4833 case IDM_LoadPrevPosition:
\r
4834 ReloadPosition(-1);
\r
4837 case IDM_ReloadPosition:
\r
4838 ReloadPosition(0);
\r
4841 case IDM_SaveGame:
\r
4842 defName = DefaultFileName(appData.oldSaveStyle ? "gam" : "pgn");
\r
4843 f = OpenFileDialog(hwnd, "a", defName,
\r
4844 appData.oldSaveStyle ? "gam" : "pgn",
\r
4846 _("Save Game to File"), NULL, fileTitle, NULL);
\r
4848 SaveGame(f, 0, "");
\r
4852 case IDM_SavePosition:
\r
4853 defName = DefaultFileName(appData.oldSaveStyle ? "pos" : "fen");
\r
4854 f = OpenFileDialog(hwnd, "a", defName,
\r
4855 appData.oldSaveStyle ? "pos" : "fen",
\r
4857 _("Save Position to File"), NULL, fileTitle, NULL);
\r
4859 SavePosition(f, 0, "");
\r
4863 case IDM_SaveDiagram:
\r
4864 defName = "diagram";
\r
4865 f = OpenFileDialog(hwnd, "wb", defName,
\r
4868 _("Save Diagram to File"), NULL, fileTitle, NULL);
\r
4874 case IDM_CreateBook:
\r
4875 CreateBookEvent();
\r
4878 case IDM_CopyGame:
\r
4879 CopyGameToClipboard();
\r
4882 case IDM_PasteGame:
\r
4883 PasteGameFromClipboard();
\r
4886 case IDM_CopyGameListToClipboard:
\r
4887 CopyGameListToClipboard();
\r
4890 /* [AS] Autodetect FEN or PGN data */
\r
4891 case IDM_PasteAny:
\r
4892 PasteGameOrFENFromClipboard();
\r
4895 /* [AS] Move history */
\r
4896 case IDM_ShowMoveHistory:
\r
4897 if( MoveHistoryIsUp() ) {
\r
4898 MoveHistoryPopDown();
\r
4901 MoveHistoryPopUp();
\r
4905 /* [AS] Eval graph */
\r
4906 case IDM_ShowEvalGraph:
\r
4907 if( EvalGraphIsUp() ) {
\r
4908 EvalGraphPopDown();
\r
4912 SetFocus(hwndMain);
\r
4916 /* [AS] Engine output */
\r
4917 case IDM_ShowEngineOutput:
\r
4918 if( EngineOutputIsUp() ) {
\r
4919 EngineOutputPopDown();
\r
4922 EngineOutputPopUp();
\r
4926 /* [AS] User adjudication */
\r
4927 case IDM_UserAdjudication_White:
\r
4928 UserAdjudicationEvent( +1 );
\r
4931 case IDM_UserAdjudication_Black:
\r
4932 UserAdjudicationEvent( -1 );
\r
4935 case IDM_UserAdjudication_Draw:
\r
4936 UserAdjudicationEvent( 0 );
\r
4939 /* [AS] Game list options dialog */
\r
4940 case IDM_GameListOptions:
\r
4941 GameListOptions();
\r
4948 case IDM_CopyPosition:
\r
4949 CopyFENToClipboard();
\r
4952 case IDM_PastePosition:
\r
4953 PasteFENFromClipboard();
\r
4956 case IDM_MailMove:
\r
4960 case IDM_ReloadCMailMsg:
\r
4961 Reset(TRUE, TRUE);
\r
4962 ReloadCmailMsgEvent(FALSE);
\r
4965 case IDM_Minimize:
\r
4966 ShowWindow(hwnd, SW_MINIMIZE);
\r
4973 case IDM_MachineWhite:
\r
4974 MachineWhiteEvent();
\r
4976 * refresh the tags dialog only if it's visible
\r
4978 if (gameMode == MachinePlaysWhite && IsWindowVisible(editTagsDialog)) {
\r
4980 tags = PGNTags(&gameInfo);
\r
4981 TagsPopUp(tags, CmailMsg());
\r
4984 SAY("computer starts playing white");
\r
4987 case IDM_MachineBlack:
\r
4988 MachineBlackEvent();
\r
4990 * refresh the tags dialog only if it's visible
\r
4992 if (gameMode == MachinePlaysBlack && IsWindowVisible(editTagsDialog)) {
\r
4994 tags = PGNTags(&gameInfo);
\r
4995 TagsPopUp(tags, CmailMsg());
\r
4998 SAY("computer starts playing black");
\r
5001 case IDM_Match: // [HGM] match: flows into next case, after setting Match Mode and nr of Games
\r
5002 MatchEvent(2); // distinguish from command-line-triggered case (matchMode=1)
\r
5005 case IDM_TwoMachines:
\r
5006 TwoMachinesEvent();
\r
5008 * refresh the tags dialog only if it's visible
\r
5010 if (gameMode == TwoMachinesPlay && IsWindowVisible(editTagsDialog)) {
\r
5012 tags = PGNTags(&gameInfo);
\r
5013 TagsPopUp(tags, CmailMsg());
\r
5016 SAY("computer starts playing both sides");
\r
5019 case IDM_AnalysisMode:
\r
5020 if(AnalyzeModeEvent()) {
\r
5021 SAY("analyzing current position");
\r
5025 case IDM_AnalyzeFile:
\r
5026 AnalyzeFileEvent();
\r
5029 case IDM_IcsClient:
\r
5033 case IDM_EditGame:
\r
5034 case IDM_EditGame2:
\r
5039 case IDM_EditPosition:
\r
5040 case IDM_EditPosition2:
\r
5041 EditPositionEvent();
\r
5042 SAY("enter a FEN string or setup a position on the board using the control R pop up menu");
\r
5045 case IDM_Training:
\r
5049 case IDM_ShowGameList:
\r
5050 ShowGameListProc();
\r
5053 case IDM_EditProgs1:
\r
5054 EditTagsPopUp(firstChessProgramNames, &firstChessProgramNames);
\r
5057 case IDM_LoadProg1:
\r
5058 LoadEnginePopUp(hwndMain, 0);
\r
5061 case IDM_LoadProg2:
\r
5062 LoadEnginePopUp(hwndMain, 1);
\r
5065 case IDM_EditServers:
\r
5066 EditTagsPopUp(icsNames, &icsNames);
\r
5069 case IDM_EditTags:
\r
5074 case IDM_EditBook:
\r
5078 case IDM_EditComment:
\r
5080 if (commentUp && editComment) {
\r
5083 EditCommentEvent();
\r
5103 case IDM_CallFlag:
\r
5123 case IDM_StopObserving:
\r
5124 StopObservingEvent();
\r
5127 case IDM_StopExamining:
\r
5128 StopExaminingEvent();
\r
5132 UploadGameEvent();
\r
5135 case IDM_TypeInMove:
\r
5136 TypeInEvent('\000');
\r
5139 case IDM_TypeInName:
\r
5140 PopUpNameDialog('\000');
\r
5143 case IDM_Backward:
\r
5145 SetFocus(hwndMain);
\r
5152 SetFocus(hwndMain);
\r
5157 SetFocus(hwndMain);
\r
5162 SetFocus(hwndMain);
\r
5165 case OPT_GameListNext: // [HGM] forward these two accelerators to Game List
\r
5166 case OPT_GameListPrev:
\r
5167 if(gameListDialog) SendMessage(gameListDialog, WM_COMMAND, wmId, 0);
\r
5171 RevertEvent(FALSE);
\r
5174 case IDM_Annotate: // [HGM] vari: revert with annotation
\r
5175 RevertEvent(TRUE);
\r
5178 case IDM_TruncateGame:
\r
5179 TruncateGameEvent();
\r
5186 case IDM_RetractMove:
\r
5187 RetractMoveEvent();
\r
5190 case IDM_FlipView:
\r
5191 flipView = !flipView;
\r
5192 DrawPosition(FALSE, NULL);
\r
5195 case IDM_FlipClock:
\r
5196 flipClock = !flipClock;
\r
5197 DisplayBothClocks();
\r
5201 case IDM_MuteSounds:
\r
5202 mute = !mute; // [HGM] mute: keep track of global muting variable
\r
5203 CheckMenuItem(GetMenu(hwndMain),IDM_MuteSounds,
\r
5204 MF_BYCOMMAND|(mute?MF_CHECKED:MF_UNCHECKED));
\r
5207 case IDM_GeneralOptions:
\r
5208 GeneralOptionsPopup(hwnd);
\r
5209 DrawPosition(TRUE, NULL);
\r
5212 case IDM_BoardOptions:
\r
5213 BoardOptionsPopup(hwnd);
\r
5216 case IDM_ThemeOptions:
\r
5217 ThemeOptionsPopup(hwnd);
\r
5220 case IDM_EnginePlayOptions:
\r
5221 EnginePlayOptionsPopup(hwnd);
\r
5224 case IDM_Engine1Options:
\r
5225 EngineOptionsPopup(hwnd, &first);
\r
5228 case IDM_Engine2Options:
\r
5230 if(WaitForEngine(&second, SettingsMenuIfReady)) break;
\r
5231 EngineOptionsPopup(hwnd, &second);
\r
5234 case IDM_OptionsUCI:
\r
5235 UciOptionsPopup(hwnd);
\r
5239 TourneyPopup(hwnd);
\r
5242 case IDM_IcsOptions:
\r
5243 IcsOptionsPopup(hwnd);
\r
5247 FontsOptionsPopup(hwnd);
\r
5251 SoundOptionsPopup(hwnd);
\r
5254 case IDM_CommPort:
\r
5255 CommPortOptionsPopup(hwnd);
\r
5258 case IDM_LoadOptions:
\r
5259 LoadOptionsPopup(hwnd);
\r
5262 case IDM_SaveOptions:
\r
5263 SaveOptionsPopup(hwnd);
\r
5266 case IDM_TimeControl:
\r
5267 TimeControlOptionsPopup(hwnd);
\r
5270 case IDM_SaveSettings:
\r
5271 SaveSettings(settingsFileName);
\r
5274 case IDM_SaveSettingsOnExit:
\r
5275 saveSettingsOnExit = !saveSettingsOnExit;
\r
5276 (void) CheckMenuItem(GetMenu(hwndMain), IDM_SaveSettingsOnExit,
\r
5277 MF_BYCOMMAND|(saveSettingsOnExit ?
\r
5278 MF_CHECKED : MF_UNCHECKED));
\r
5289 case IDM_AboutGame:
\r
5294 appData.debugMode = !appData.debugMode;
\r
5295 if (appData.debugMode) {
\r
5296 char dir[MSG_SIZ];
\r
5297 GetCurrentDirectory(MSG_SIZ, dir);
\r
5298 SetCurrentDirectory(installDir);
\r
5299 debugFP = fopen(appData.nameOfDebugFile, "w");
\r
5300 SetCurrentDirectory(dir);
\r
5301 setbuf(debugFP, NULL);
\r
5308 case IDM_HELPCONTENTS:
\r
5309 if (!MyHelp (hwnd, "winboard.hlp", HELP_KEY,(DWORD)(LPSTR)"CONTENTS") &&
\r
5310 !HtmlHelp(hwnd, "winboard.chm", 0, 0) ) {
\r
5311 MessageBox (GetFocus(),
\r
5312 _("Unable to activate help"),
\r
5313 szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);
\r
5317 case IDM_HELPSEARCH:
\r
5318 if (!MyHelp (hwnd, "winboard.hlp", HELP_PARTIALKEY, (DWORD)(LPSTR)"") &&
\r
5319 !HtmlHelp(hwnd, "winboard.chm", 0, 0) ) {
\r
5320 MessageBox (GetFocus(),
\r
5321 _("Unable to activate help"),
\r
5322 szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);
\r
5326 case IDM_HELPHELP:
\r
5327 if(!WinHelp(hwnd, (LPSTR)NULL, HELP_HELPONHELP, 0)) {
\r
5328 MessageBox (GetFocus(),
\r
5329 _("Unable to activate help"),
\r
5330 szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);
\r
5335 lpProc = MakeProcInstance((FARPROC)About, hInst);
\r
5337 (gameInfo.event && strcmp(gameInfo.event, "Easter Egg Hunt") == 0) ?
\r
5338 "AboutBox2" : "AboutBox", hwnd, (DLGPROC)lpProc);
\r
5339 FreeProcInstance(lpProc);
\r
5342 case IDM_DirectCommand1:
\r
5343 AskQuestionEvent(_("Direct Command"),
\r
5344 _("Send to chess program:"), "", "1");
\r
5346 case IDM_DirectCommand2:
\r
5347 AskQuestionEvent(_("Direct Command"),
\r
5348 _("Send to second chess program:"), "", "2");
\r
5351 case EP_WhitePawn:
\r
5352 EditPositionMenuEvent(WhitePawn, fromX, fromY);
\r
5353 fromX = fromY = -1;
\r
5356 case EP_WhiteKnight:
\r
5357 EditPositionMenuEvent(WhiteKnight, fromX, fromY);
\r
5358 fromX = fromY = -1;
\r
5361 case EP_WhiteBishop:
\r
5362 EditPositionMenuEvent(WhiteBishop, fromX, fromY);
\r
5363 fromX = fromY = -1;
\r
5366 case EP_WhiteRook:
\r
5367 EditPositionMenuEvent(WhiteRook, fromX, fromY);
\r
5368 fromX = fromY = -1;
\r
5371 case EP_WhiteQueen:
\r
5372 EditPositionMenuEvent(WhiteQueen, fromX, fromY);
\r
5373 fromX = fromY = -1;
\r
5376 case EP_WhiteFerz:
\r
5377 EditPositionMenuEvent(WhiteFerz, fromX, fromY);
\r
5378 fromX = fromY = -1;
\r
5381 case EP_WhiteWazir:
\r
5382 EditPositionMenuEvent(WhiteWazir, fromX, fromY);
\r
5383 fromX = fromY = -1;
\r
5386 case EP_WhiteAlfil:
\r
5387 EditPositionMenuEvent(WhiteAlfil, fromX, fromY);
\r
5388 fromX = fromY = -1;
\r
5391 case EP_WhiteCannon:
\r
5392 EditPositionMenuEvent(WhiteCannon, fromX, fromY);
\r
5393 fromX = fromY = -1;
\r
5396 case EP_WhiteCardinal:
\r
5397 EditPositionMenuEvent(WhiteAngel, fromX, fromY);
\r
5398 fromX = fromY = -1;
\r
5401 case EP_WhiteMarshall:
\r
5402 EditPositionMenuEvent(WhiteMarshall, fromX, fromY);
\r
5403 fromX = fromY = -1;
\r
5406 case EP_WhiteKing:
\r
5407 EditPositionMenuEvent(WhiteKing, fromX, fromY);
\r
5408 fromX = fromY = -1;
\r
5411 case EP_BlackPawn:
\r
5412 EditPositionMenuEvent(BlackPawn, fromX, fromY);
\r
5413 fromX = fromY = -1;
\r
5416 case EP_BlackKnight:
\r
5417 EditPositionMenuEvent(BlackKnight, fromX, fromY);
\r
5418 fromX = fromY = -1;
\r
5421 case EP_BlackBishop:
\r
5422 EditPositionMenuEvent(BlackBishop, fromX, fromY);
\r
5423 fromX = fromY = -1;
\r
5426 case EP_BlackRook:
\r
5427 EditPositionMenuEvent(BlackRook, fromX, fromY);
\r
5428 fromX = fromY = -1;
\r
5431 case EP_BlackQueen:
\r
5432 EditPositionMenuEvent(BlackQueen, fromX, fromY);
\r
5433 fromX = fromY = -1;
\r
5436 case EP_BlackFerz:
\r
5437 EditPositionMenuEvent(BlackFerz, fromX, fromY);
\r
5438 fromX = fromY = -1;
\r
5441 case EP_BlackWazir:
\r
5442 EditPositionMenuEvent(BlackWazir, fromX, fromY);
\r
5443 fromX = fromY = -1;
\r
5446 case EP_BlackAlfil:
\r
5447 EditPositionMenuEvent(BlackAlfil, fromX, fromY);
\r
5448 fromX = fromY = -1;
\r
5451 case EP_BlackCannon:
\r
5452 EditPositionMenuEvent(BlackCannon, fromX, fromY);
\r
5453 fromX = fromY = -1;
\r
5456 case EP_BlackCardinal:
\r
5457 EditPositionMenuEvent(BlackAngel, fromX, fromY);
\r
5458 fromX = fromY = -1;
\r
5461 case EP_BlackMarshall:
\r
5462 EditPositionMenuEvent(BlackMarshall, fromX, fromY);
\r
5463 fromX = fromY = -1;
\r
5466 case EP_BlackKing:
\r
5467 EditPositionMenuEvent(BlackKing, fromX, fromY);
\r
5468 fromX = fromY = -1;
\r
5471 case EP_EmptySquare:
\r
5472 EditPositionMenuEvent(EmptySquare, fromX, fromY);
\r
5473 fromX = fromY = -1;
\r
5476 case EP_ClearBoard:
\r
5477 EditPositionMenuEvent(ClearBoard, fromX, fromY);
\r
5478 fromX = fromY = -1;
\r
5482 EditPositionMenuEvent(WhitePlay, fromX, fromY);
\r
5483 fromX = fromY = -1;
\r
5487 EditPositionMenuEvent(BlackPlay, fromX, fromY);
\r
5488 fromX = fromY = -1;
\r
5492 EditPositionMenuEvent(PromotePiece, fromX, fromY);
\r
5493 fromX = fromY = -1;
\r
5497 EditPositionMenuEvent(DemotePiece, fromX, fromY);
\r
5498 fromX = fromY = -1;
\r
5502 DropMenuEvent(WhitePawn, fromX, fromY);
\r
5503 fromX = fromY = -1;
\r
5507 DropMenuEvent(WhiteKnight, fromX, fromY);
\r
5508 fromX = fromY = -1;
\r
5512 DropMenuEvent(WhiteBishop, fromX, fromY);
\r
5513 fromX = fromY = -1;
\r
5517 DropMenuEvent(WhiteRook, fromX, fromY);
\r
5518 fromX = fromY = -1;
\r
5522 DropMenuEvent(WhiteQueen, fromX, fromY);
\r
5523 fromX = fromY = -1;
\r
5527 barbaric = 0; appData.language = "";
\r
5528 TranslateMenus(0);
\r
5529 CheckMenuItem(GetMenu(hwndMain), lastChecked, MF_BYCOMMAND|MF_UNCHECKED);
\r
5530 CheckMenuItem(GetMenu(hwndMain), IDM_English, MF_BYCOMMAND|MF_CHECKED);
\r
5531 lastChecked = wmId;
\r
5535 if(wmId >= IDM_RecentEngines && wmId < IDM_RecentEngines + appData.recentEngines)
\r
5536 RecentEngineEvent(wmId - IDM_RecentEngines);
\r
5538 if(wmId > IDM_English && wmId < IDM_English+20) {
\r
5539 LoadLanguageFile(languageFile[wmId - IDM_English - 1]);
\r
5540 TranslateMenus(0);
\r
5541 CheckMenuItem(GetMenu(hwndMain), lastChecked, MF_BYCOMMAND|MF_UNCHECKED);
\r
5542 CheckMenuItem(GetMenu(hwndMain), wmId, MF_BYCOMMAND|MF_CHECKED);
\r
5543 lastChecked = wmId;
\r
5546 return (DefWindowProc(hwnd, message, wParam, lParam));
\r
5552 case CLOCK_TIMER_ID:
\r
5553 KillTimer(hwnd, clockTimerEvent); /* Simulate one-shot timer as in X */
\r
5554 clockTimerEvent = 0;
\r
5555 DecrementClocks(); /* call into back end */
\r
5557 case LOAD_GAME_TIMER_ID:
\r
5558 KillTimer(hwnd, loadGameTimerEvent); /* Simulate one-shot timer as in X*/
\r
5559 loadGameTimerEvent = 0;
\r
5560 AutoPlayGameLoop(); /* call into back end */
\r
5562 case ANALYSIS_TIMER_ID:
\r
5563 if ((gameMode == AnalyzeMode || gameMode == AnalyzeFile
\r
5564 || appData.icsEngineAnalyze) && appData.periodicUpdates) {
\r
5565 AnalysisPeriodicEvent(0);
\r
5567 KillTimer(hwnd, analysisTimerEvent);
\r
5568 analysisTimerEvent = 0;
\r
5571 case DELAYED_TIMER_ID:
\r
5572 KillTimer(hwnd, delayedTimerEvent);
\r
5573 delayedTimerEvent = 0;
\r
5574 delayedTimerCallback();
\r
5579 case WM_USER_Input:
\r
5580 InputEvent(hwnd, message, wParam, lParam);
\r
5583 /* [AS] Also move "attached" child windows */
\r
5584 case WM_WINDOWPOSCHANGING:
\r
5586 if( hwnd == hwndMain && appData.useStickyWindows ) {
\r
5587 LPWINDOWPOS lpwp = (LPWINDOWPOS) lParam;
\r
5589 if( ((lpwp->flags & SWP_NOMOVE) == 0) && ((lpwp->flags & SWP_NOSIZE) != 0) ) {
\r
5590 /* Window is moving */
\r
5593 // GetWindowRect( hwnd, &rcMain ); //[HGM] sticky: in XP this returned new position, not old
\r
5594 rcMain.left = wpMain.x; // replace by these 4 lines to reconstruct old rect
\r
5595 rcMain.right = wpMain.x + wpMain.width;
\r
5596 rcMain.top = wpMain.y;
\r
5597 rcMain.bottom = wpMain.y + wpMain.height;
\r
5599 ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, moveHistoryDialog, &wpMoveHistory );
\r
5600 ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, evalGraphDialog, &wpEvalGraph );
\r
5601 ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, engineOutputDialog, &wpEngineOutput );
\r
5602 ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, gameListDialog, &wpGameList );
\r
5603 ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, hwndConsole, &wpConsole );
\r
5604 wpMain.x = lpwp->x;
\r
5605 wpMain.y = lpwp->y;
\r
5610 /* [AS] Snapping */
\r
5611 case WM_ENTERSIZEMOVE:
\r
5612 if(appData.debugMode) { fprintf(debugFP, "size-move\n"); }
\r
5613 if (hwnd == hwndMain) {
\r
5614 doingSizing = TRUE;
\r
5617 return OnEnterSizeMove( &sd, hwnd, wParam, lParam );
\r
5621 if(appData.debugMode) { fprintf(debugFP, "sizing\n"); }
\r
5622 if (hwnd == hwndMain) {
\r
5623 lastSizing = wParam;
\r
5628 if(appData.debugMode) { fprintf(debugFP, "moving\n"); }
\r
5629 return OnMoving( &sd, hwnd, wParam, lParam );
\r
5631 case WM_EXITSIZEMOVE:
\r
5632 if(appData.debugMode) { fprintf(debugFP, "exit size-move, size = %d\n", squareSize); }
\r
5633 if (hwnd == hwndMain) {
\r
5635 doingSizing = FALSE;
\r
5636 InvalidateRect(hwnd, &boardRect, FALSE);
\r
5637 GetClientRect(hwnd, &client);
\r
5638 ResizeBoard(client.right, client.bottom, lastSizing);
\r
5640 if(appData.debugMode) { fprintf(debugFP, "square size = %d\n", squareSize); }
\r
5642 return OnExitSizeMove( &sd, hwnd, wParam, lParam );
\r
5645 case WM_DESTROY: /* message: window being destroyed */
\r
5646 PostQuitMessage(0);
\r
5650 if (hwnd == hwndMain) {
\r
5655 default: /* Passes it on if unprocessed */
\r
5656 return (DefWindowProc(hwnd, message, wParam, lParam));
\r
5661 /*---------------------------------------------------------------------------*\
\r
5663 * Misc utility routines
\r
5665 \*---------------------------------------------------------------------------*/
\r
5668 * Decent random number generator, at least not as bad as Windows
\r
5669 * standard rand, which returns a value in the range 0 to 0x7fff.
\r
5671 unsigned int randstate;
\r
5676 randstate = randstate * 1664525 + 1013904223;
\r
5677 return (int) randstate & 0x7fffffff;
\r
5681 mysrandom(unsigned int seed)
\r
5688 * returns TRUE if user selects a different color, FALSE otherwise
\r
5692 ChangeColor(HWND hwnd, COLORREF *which)
\r
5694 static BOOL firstTime = TRUE;
\r
5695 static DWORD customColors[16];
\r
5697 COLORREF newcolor;
\r
5702 /* Make initial colors in use available as custom colors */
\r
5703 /* Should we put the compiled-in defaults here instead? */
\r
5705 customColors[i++] = lightSquareColor & 0xffffff;
\r
5706 customColors[i++] = darkSquareColor & 0xffffff;
\r
5707 customColors[i++] = whitePieceColor & 0xffffff;
\r
5708 customColors[i++] = blackPieceColor & 0xffffff;
\r
5709 customColors[i++] = highlightSquareColor & 0xffffff;
\r
5710 customColors[i++] = premoveHighlightColor & 0xffffff;
\r
5712 for (ccl = (ColorClass) 0; ccl < NColorClasses && i < 16; ccl++) {
\r
5713 customColors[i++] = textAttribs[ccl].color;
\r
5715 while (i < 16) customColors[i++] = RGB(255, 255, 255);
\r
5716 firstTime = FALSE;
\r
5719 cc.lStructSize = sizeof(cc);
\r
5720 cc.hwndOwner = hwnd;
\r
5721 cc.hInstance = NULL;
\r
5722 cc.rgbResult = (DWORD) (*which & 0xffffff);
\r
5723 cc.lpCustColors = (LPDWORD) customColors;
\r
5724 cc.Flags = CC_RGBINIT|CC_FULLOPEN;
\r
5726 if (!ChooseColor(&cc)) return FALSE;
\r
5728 newcolor = (COLORREF) (0x2000000 | cc.rgbResult);
\r
5729 if (newcolor == *which) return FALSE;
\r
5730 *which = newcolor;
\r
5734 InitDrawingColors();
\r
5735 InvalidateRect(hwnd, &boardRect, FALSE);
\r
5740 MyLoadSound(MySound *ms)
\r
5746 if (ms->data && ms->flag) free(ms->data);
\r
5749 switch (ms->name[0]) {
\r
5755 /* System sound from Control Panel. Don't preload here. */
\r
5759 if (ms->name[1] == NULLCHAR) {
\r
5760 /* "!" alone = silence */
\r
5763 /* Builtin wave resource. Error if not found. */
\r
5764 HANDLE h = FindResource(hInst, ms->name + 1, "WAVE");
\r
5765 if (h == NULL) break;
\r
5766 ms->data = (void *)LoadResource(hInst, h);
\r
5767 ms->flag = 0; // not maloced, so cannot be freed!
\r
5768 if (h == NULL) break;
\r
5773 /* .wav file. Error if not found. */
\r
5774 f = fopen(ms->name, "rb");
\r
5775 if (f == NULL) break;
\r
5776 if (fstat(fileno(f), &st) < 0) break;
\r
5777 ms->data = malloc(st.st_size);
\r
5779 if (fread(ms->data, st.st_size, 1, f) < 1) break;
\r
5785 char buf[MSG_SIZ];
\r
5786 snprintf(buf, MSG_SIZ, _("Error loading sound %s"), ms->name);
\r
5787 DisplayError(buf, GetLastError());
\r
5793 MyPlaySound(MySound *ms)
\r
5795 BOOLEAN ok = FALSE;
\r
5797 if(mute) return TRUE; // [HGM] mute: suppress all sound play when muted
\r
5798 switch (ms->name[0]) {
\r
5800 if(appData.debugMode) fprintf(debugFP, "silence\n");
\r
5805 /* System sound from Control Panel (deprecated feature).
\r
5806 "$" alone or an unset sound name gets default beep (still in use). */
\r
5807 if (ms->name[1]) {
\r
5808 ok = PlaySound(ms->name + 1, NULL, SND_ALIAS|SND_ASYNC);
\r
5810 if (!ok) ok = MessageBeep(MB_OK);
\r
5813 /* Builtin wave resource, or "!" alone for silence */
\r
5814 if (ms->name[1]) {
\r
5815 if (ms->data == NULL) return FALSE;
\r
5816 ok = PlaySound(ms->data, NULL, SND_MEMORY|SND_ASYNC);
\r
5822 /* .wav file. Error if not found. */
\r
5823 if (ms->data == NULL) return FALSE;
\r
5824 ok = PlaySound(ms->data, NULL, SND_MEMORY|SND_ASYNC);
\r
5827 /* Don't print an error: this can happen innocently if the sound driver
\r
5828 is busy; for instance, if another instance of WinBoard is playing
\r
5829 a sound at about the same time. */
\r
5835 OldOpenFileHook(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
5838 OPENFILENAME *ofn;
\r
5839 static UINT *number; /* gross that this is static */
\r
5841 switch (message) {
\r
5842 case WM_INITDIALOG: /* message: initialize dialog box */
\r
5843 /* Center the dialog over the application window */
\r
5844 ofn = (OPENFILENAME *) lParam;
\r
5845 if (ofn->Flags & OFN_ENABLETEMPLATE) {
\r
5846 number = (UINT *) ofn->lCustData;
\r
5847 SendMessage(GetDlgItem(hDlg, edt2), WM_SETTEXT, 0, (LPARAM) "");
\r
5851 CenterWindow (hDlg, GetWindow (hDlg, GW_OWNER));
\r
5852 Translate(hDlg, 1536);
\r
5853 return FALSE; /* Allow for further processing */
\r
5856 if ((LOWORD(wParam) == IDOK) && (number != NULL)) {
\r
5857 *number = GetDlgItemInt(hDlg, OPT_IndexNumberOld, &ok, FALSE);
\r
5859 return FALSE; /* Allow for further processing */
\r
5865 OpenFileHook(HWND hdlg, UINT uiMsg, WPARAM wParam, LPARAM lParam)
\r
5867 static UINT *number;
\r
5868 OPENFILENAME *ofname;
\r
5871 case WM_INITDIALOG:
\r
5872 Translate(hdlg, DLG_IndexNumber);
\r
5873 ofname = (OPENFILENAME *)lParam;
\r
5874 number = (UINT *)(ofname->lCustData);
\r
5877 ofnot = (OFNOTIFY *)lParam;
\r
5878 if (ofnot->hdr.code == CDN_FILEOK) {
\r
5879 *number = GetDlgItemInt(hdlg, OPT_IndexNumber, NULL, FALSE);
\r
5888 OpenFileDialog(HWND hwnd, char *write, char *defName, char *defExt, // [HGM] diag: type of 'write' now string
\r
5889 char *nameFilt, char *dlgTitle, UINT *number,
\r
5890 char fileTitle[MSG_SIZ], char fileName[MSG_SIZ])
\r
5892 OPENFILENAME openFileName;
\r
5893 char buf1[MSG_SIZ];
\r
5896 if (fileName == NULL) fileName = buf1;
\r
5897 if (defName == NULL) {
\r
5898 safeStrCpy(fileName, "*.", 3 );
\r
5899 strcat(fileName, defExt);
\r
5901 safeStrCpy(fileName, defName, MSG_SIZ );
\r
5903 if (fileTitle) safeStrCpy(fileTitle, "", MSG_SIZ );
\r
5904 if (number) *number = 0;
\r
5906 openFileName.lStructSize = sizeof(OPENFILENAME);
\r
5907 openFileName.hwndOwner = hwnd;
\r
5908 openFileName.hInstance = (HANDLE) hInst;
\r
5909 openFileName.lpstrFilter = nameFilt;
\r
5910 openFileName.lpstrCustomFilter = (LPSTR) NULL;
\r
5911 openFileName.nMaxCustFilter = 0L;
\r
5912 openFileName.nFilterIndex = 1L;
\r
5913 openFileName.lpstrFile = fileName;
\r
5914 openFileName.nMaxFile = MSG_SIZ;
\r
5915 openFileName.lpstrFileTitle = fileTitle;
\r
5916 openFileName.nMaxFileTitle = fileTitle ? MSG_SIZ : 0;
\r
5917 openFileName.lpstrInitialDir = NULL;
\r
5918 openFileName.lpstrTitle = dlgTitle;
\r
5919 openFileName.Flags = OFN_PATHMUSTEXIST | OFN_HIDEREADONLY
\r
5920 | (write[0] != 'r' ? 0 : OFN_FILEMUSTEXIST)
\r
5921 | (number ? OFN_ENABLETEMPLATE | OFN_ENABLEHOOK: 0)
\r
5922 | (oldDialog ? 0 : OFN_EXPLORER);
\r
5923 openFileName.nFileOffset = 0;
\r
5924 openFileName.nFileExtension = 0;
\r
5925 openFileName.lpstrDefExt = defExt;
\r
5926 openFileName.lCustData = (LONG) number;
\r
5927 openFileName.lpfnHook = oldDialog ?
\r
5928 (LPOFNHOOKPROC) OldOpenFileHook : (LPOFNHOOKPROC) OpenFileHook;
\r
5929 openFileName.lpTemplateName = (LPSTR)(oldDialog ? 1536 : DLG_IndexNumber);
\r
5931 if (write[0] != 'r' ? GetSaveFileName(&openFileName) :
\r
5932 GetOpenFileName(&openFileName)) {
\r
5933 /* open the file */
\r
5934 f = fopen(openFileName.lpstrFile, write);
\r
5936 MessageBox(hwnd, _("File open failed"), NULL,
\r
5937 MB_OK|MB_ICONEXCLAMATION);
\r
5941 int err = CommDlgExtendedError();
\r
5942 if (err != 0) DisplayError(_("Internal error in file dialog box"), err);
\r
5951 MenuPopup(HWND hwnd, POINT pt, HMENU hmenu, UINT def)
\r
5953 HMENU hmenuTrackPopup; /* floating pop-up menu */
\r
5956 * Get the first pop-up menu in the menu template. This is the
\r
5957 * menu that TrackPopupMenu displays.
\r
5959 hmenuTrackPopup = GetSubMenu(hmenu, 0);
\r
5960 TranslateOneMenu(10, hmenuTrackPopup);
\r
5962 SetMenuDefaultItem(hmenuTrackPopup, def, FALSE);
\r
5965 * TrackPopup uses screen coordinates, so convert the
\r
5966 * coordinates of the mouse click to screen coordinates.
\r
5968 ClientToScreen(hwnd, (LPPOINT) &pt);
\r
5970 /* Draw and track the floating pop-up menu. */
\r
5971 TrackPopupMenu(hmenuTrackPopup, TPM_CENTERALIGN | TPM_RIGHTBUTTON,
\r
5972 pt.x, pt.y, 0, hwnd, NULL);
\r
5974 /* Destroy the menu.*/
\r
5975 DestroyMenu(hmenu);
\r
5980 int sizeX, sizeY, newSizeX, newSizeY;
\r
5982 } ResizeEditPlusButtonsClosure;
\r
5985 ResizeEditPlusButtonsCallback(HWND hChild, LPARAM lparam)
\r
5987 ResizeEditPlusButtonsClosure *cl = (ResizeEditPlusButtonsClosure *)lparam;
\r
5991 if (hChild == cl->hText) return TRUE;
\r
5992 GetWindowRect(hChild, &rect); /* gives screen coords */
\r
5993 pt.x = rect.left + (cl->newSizeX - cl->sizeX)/2;
\r
5994 pt.y = rect.top + cl->newSizeY - cl->sizeY;
\r
5995 ScreenToClient(cl->hDlg, &pt);
\r
5996 cl->hdwp = DeferWindowPos(cl->hdwp, hChild, NULL,
\r
5997 pt.x, pt.y, rect.right - rect.left, rect.bottom - rect.top, SWP_NOZORDER);
\r
6001 /* Resize a dialog that has a (rich) edit field filling most of
\r
6002 the top, with a row of buttons below */
\r
6004 ResizeEditPlusButtons(HWND hDlg, HWND hText, int sizeX, int sizeY, int newSizeX, int newSizeY)
\r
6007 int newTextHeight, newTextWidth;
\r
6008 ResizeEditPlusButtonsClosure cl;
\r
6010 /*if (IsIconic(hDlg)) return;*/
\r
6011 if (newSizeX == sizeX && newSizeY == sizeY) return;
\r
6013 cl.hdwp = BeginDeferWindowPos(8);
\r
6015 GetWindowRect(hText, &rectText); /* gives screen coords */
\r
6016 newTextWidth = rectText.right - rectText.left + newSizeX - sizeX;
\r
6017 newTextHeight = rectText.bottom - rectText.top + newSizeY - sizeY;
\r
6018 if (newTextHeight < 0) {
\r
6019 newSizeY += -newTextHeight;
\r
6020 newTextHeight = 0;
\r
6022 cl.hdwp = DeferWindowPos(cl.hdwp, hText, NULL, 0, 0,
\r
6023 newTextWidth, newTextHeight, SWP_NOZORDER|SWP_NOMOVE);
\r
6029 cl.newSizeX = newSizeX;
\r
6030 cl.newSizeY = newSizeY;
\r
6031 EnumChildWindows(hDlg, ResizeEditPlusButtonsCallback, (LPARAM)&cl);
\r
6033 EndDeferWindowPos(cl.hdwp);
\r
6036 BOOL CenterWindowEx(HWND hwndChild, HWND hwndParent, int mode)
\r
6038 RECT rChild, rParent;
\r
6039 int wChild, hChild, wParent, hParent;
\r
6040 int wScreen, hScreen, xNew, yNew;
\r
6043 /* Get the Height and Width of the child window */
\r
6044 GetWindowRect (hwndChild, &rChild);
\r
6045 wChild = rChild.right - rChild.left;
\r
6046 hChild = rChild.bottom - rChild.top;
\r
6048 /* Get the Height and Width of the parent window */
\r
6049 GetWindowRect (hwndParent, &rParent);
\r
6050 wParent = rParent.right - rParent.left;
\r
6051 hParent = rParent.bottom - rParent.top;
\r
6053 /* Get the display limits */
\r
6054 hdc = GetDC (hwndChild);
\r
6055 wScreen = GetDeviceCaps (hdc, HORZRES);
\r
6056 hScreen = GetDeviceCaps (hdc, VERTRES);
\r
6057 ReleaseDC(hwndChild, hdc);
\r
6059 /* Calculate new X position, then adjust for screen */
\r
6060 xNew = rParent.left + ((wParent - wChild) /2);
\r
6063 } else if ((xNew+wChild) > wScreen) {
\r
6064 xNew = wScreen - wChild;
\r
6067 /* Calculate new Y position, then adjust for screen */
\r
6069 yNew = rParent.top + ((hParent - hChild) /2);
\r
6072 yNew = rParent.top + GetSystemMetrics( SM_CYCAPTION ) * 2 / 3;
\r
6077 } else if ((yNew+hChild) > hScreen) {
\r
6078 yNew = hScreen - hChild;
\r
6081 /* Set it, and return */
\r
6082 return SetWindowPos (hwndChild, NULL,
\r
6083 xNew, yNew, 0, 0, SWP_NOSIZE | SWP_NOZORDER);
\r
6086 /* Center one window over another */
\r
6087 BOOL CenterWindow (HWND hwndChild, HWND hwndParent)
\r
6089 return CenterWindowEx( hwndChild, hwndParent, 0 );
\r
6092 /*---------------------------------------------------------------------------*\
\r
6094 * Startup Dialog functions
\r
6096 \*---------------------------------------------------------------------------*/
\r
6098 InitComboStrings(HANDLE hwndCombo, char **cd)
\r
6100 SendMessage(hwndCombo, CB_RESETCONTENT, 0, 0);
\r
6102 while (*cd != NULL) {
\r
6103 SendMessage(hwndCombo, CB_ADDSTRING, 0, (LPARAM) T_(*cd));
\r
6109 InitComboStringsFromOption(HANDLE hwndCombo, char *str)
\r
6111 char buf1[MAX_ARG_LEN];
\r
6114 if (str[0] == '@') {
\r
6115 FILE* f = fopen(str + 1, "r");
\r
6117 DisplayFatalError(str + 1, errno, 2);
\r
6120 len = fread(buf1, 1, sizeof(buf1)-1, f);
\r
6122 buf1[len] = NULLCHAR;
\r
6126 SendMessage(hwndCombo, CB_RESETCONTENT, 0, 0);
\r
6129 char buf[MSG_SIZ];
\r
6130 char *end = strchr(str, '\n');
\r
6131 if (end == NULL) return;
\r
6132 memcpy(buf, str, end - str);
\r
6133 buf[end - str] = NULLCHAR;
\r
6134 SendMessage(hwndCombo, CB_ADDSTRING, 0, (LPARAM) buf);
\r
6140 SetStartupDialogEnables(HWND hDlg)
\r
6142 EnableWindow(GetDlgItem(hDlg, OPT_ChessEngineName),
\r
6143 IsDlgButtonChecked(hDlg, OPT_ChessEngine) ||
\r
6144 (appData.zippyPlay && IsDlgButtonChecked(hDlg, OPT_ChessServer)));
\r
6145 EnableWindow(GetDlgItem(hDlg, OPT_SecondChessEngineName),
\r
6146 IsDlgButtonChecked(hDlg, OPT_ChessEngine));
\r
6147 EnableWindow(GetDlgItem(hDlg, OPT_ChessServerName),
\r
6148 IsDlgButtonChecked(hDlg, OPT_ChessServer));
\r
6149 EnableWindow(GetDlgItem(hDlg, OPT_AdditionalOptions),
\r
6150 IsDlgButtonChecked(hDlg, OPT_AnyAdditional));
\r
6151 EnableWindow(GetDlgItem(hDlg, IDOK),
\r
6152 IsDlgButtonChecked(hDlg, OPT_ChessEngine) ||
\r
6153 IsDlgButtonChecked(hDlg, OPT_ChessServer) ||
\r
6154 IsDlgButtonChecked(hDlg, OPT_View));
\r
6158 QuoteForFilename(char *filename)
\r
6160 int dquote, space;
\r
6161 dquote = strchr(filename, '"') != NULL;
\r
6162 space = strchr(filename, ' ') != NULL;
\r
6163 if (dquote || space) {
\r
6175 InitEngineBox(HWND hDlg, HWND hwndCombo, char* nthcp, char* nthd, char* nthdir, char *nthnames)
\r
6177 char buf[MSG_SIZ];
\r
6180 InitComboStringsFromOption(hwndCombo, nthnames);
\r
6181 q = QuoteForFilename(nthcp);
\r
6182 snprintf(buf, MSG_SIZ, "%s%s%s", q, nthcp, q);
\r
6183 if (*nthdir != NULLCHAR) {
\r
6184 q = QuoteForFilename(nthdir);
\r
6185 snprintf(buf + strlen(buf), MSG_SIZ, " /%s=%s%s%s", nthd, q, nthdir, q);
\r
6187 if (*nthcp == NULLCHAR) {
\r
6188 SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) 0, (LPARAM) 0);
\r
6189 } else if (SendMessage(hwndCombo, CB_SELECTSTRING, (WPARAM) -1, (LPARAM) buf) == CB_ERR) {
\r
6190 SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) -1, (LPARAM) 0);
\r
6191 SendMessage(hwndCombo, WM_SETTEXT, (WPARAM) 0, (LPARAM) buf);
\r
6196 StartupDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
6198 char buf[MSG_SIZ];
\r
6202 switch (message) {
\r
6203 case WM_INITDIALOG:
\r
6204 /* Center the dialog */
\r
6205 CenterWindow (hDlg, GetDesktopWindow());
\r
6206 Translate(hDlg, DLG_Startup);
\r
6207 /* Initialize the dialog items */
\r
6208 InitEngineBox(hDlg, GetDlgItem(hDlg, OPT_ChessEngineName),
\r
6209 appData.firstChessProgram, "fd", appData.firstDirectory,
\r
6210 firstChessProgramNames);
\r
6211 InitEngineBox(hDlg, GetDlgItem(hDlg, OPT_SecondChessEngineName),
\r
6212 appData.secondChessProgram, singleList ? "fd" : "sd", appData.secondDirectory,
\r
6213 singleList ? firstChessProgramNames : secondChessProgramNames); //[HGM] single: use first list in second combo
\r
6214 hwndCombo = GetDlgItem(hDlg, OPT_ChessServerName);
\r
6215 InitComboStringsFromOption(hwndCombo, icsNames);
\r
6216 snprintf(buf, MSG_SIZ, "%s /icsport=%s", appData.icsHost, appData.icsPort);
\r
6217 if (*appData.icsHelper != NULLCHAR) {
\r
6218 char *q = QuoteForFilename(appData.icsHelper);
\r
6219 sprintf(buf + strlen(buf), " /icshelper=%s%s%s", q, appData.icsHelper, q);
\r
6221 if (*appData.icsHost == NULLCHAR) {
\r
6222 SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) 0, (LPARAM) 0);
\r
6223 /*SendMessage(hwndCombo, CB_SHOWDROPDOWN, (WPARAM) TRUE, (LPARAM) 0); !!too soon */
\r
6224 } else if (SendMessage(hwndCombo, CB_SELECTSTRING, (WPARAM) -1, (LPARAM) buf) == CB_ERR) {
\r
6225 SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) -1, (LPARAM) 0);
\r
6226 SendMessage(hwndCombo, WM_SETTEXT, (WPARAM) 0, (LPARAM) buf);
\r
6229 if (appData.icsActive) {
\r
6230 CheckDlgButton(hDlg, OPT_ChessServer, BST_CHECKED);
\r
6232 else if (appData.noChessProgram) {
\r
6233 CheckDlgButton(hDlg, OPT_View, BST_CHECKED);
\r
6236 CheckDlgButton(hDlg, OPT_ChessEngine, BST_CHECKED);
\r
6239 SetStartupDialogEnables(hDlg);
\r
6243 switch (LOWORD(wParam)) {
\r
6245 if (IsDlgButtonChecked(hDlg, OPT_ChessEngine)) {
\r
6246 safeStrCpy(buf, "/fcp=", sizeof(buf)/sizeof(buf[0]) );
\r
6247 GetDlgItemText(hDlg, OPT_ChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));
\r
6249 comboLine = strdup(p+5); // [HGM] recent: remember complete line of first combobox
\r
6250 ParseArgs(StringGet, &p);
\r
6251 safeStrCpy(buf, singleList ? "/fcp=" : "/scp=", sizeof(buf)/sizeof(buf[0]) );
\r
6252 GetDlgItemText(hDlg, OPT_SecondChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));
\r
6254 SwapEngines(singleList); // temporarily swap first and second, to load a second 'first', ...
\r
6255 ParseArgs(StringGet, &p);
\r
6256 SwapEngines(singleList); // ... and then make it 'second'
\r
6257 appData.noChessProgram = FALSE;
\r
6258 appData.icsActive = FALSE;
\r
6259 } else if (IsDlgButtonChecked(hDlg, OPT_ChessServer)) {
\r
6260 safeStrCpy(buf, "/ics /icshost=", sizeof(buf)/sizeof(buf[0]) );
\r
6261 GetDlgItemText(hDlg, OPT_ChessServerName, buf + strlen(buf), sizeof(buf) - strlen(buf));
\r
6263 ParseArgs(StringGet, &p);
\r
6264 if (appData.zippyPlay) {
\r
6265 safeStrCpy(buf, "/fcp=", sizeof(buf)/sizeof(buf[0]) );
\r
6266 GetDlgItemText(hDlg, OPT_ChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));
\r
6268 ParseArgs(StringGet, &p);
\r
6270 } else if (IsDlgButtonChecked(hDlg, OPT_View)) {
\r
6271 appData.noChessProgram = TRUE;
\r
6272 appData.icsActive = FALSE;
\r
6274 MessageBox(hDlg, _("Choose an option, or cancel to exit"),
\r
6275 _("Option Error"), MB_OK|MB_ICONEXCLAMATION);
\r
6278 if (IsDlgButtonChecked(hDlg, OPT_AnyAdditional)) {
\r
6279 GetDlgItemText(hDlg, OPT_AdditionalOptions, buf, sizeof(buf));
\r
6281 ParseArgs(StringGet, &p);
\r
6283 EndDialog(hDlg, TRUE);
\r
6290 case IDM_HELPCONTENTS:
\r
6291 if (!WinHelp (hDlg, "winboard.hlp", HELP_KEY,(DWORD)(LPSTR)"CONTENTS")) {
\r
6292 MessageBox (GetFocus(),
\r
6293 _("Unable to activate help"),
\r
6294 szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);
\r
6299 SetStartupDialogEnables(hDlg);
\r
6307 /*---------------------------------------------------------------------------*\
\r
6309 * About box dialog functions
\r
6311 \*---------------------------------------------------------------------------*/
\r
6313 /* Process messages for "About" dialog box */
\r
6315 About(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
6317 switch (message) {
\r
6318 case WM_INITDIALOG: /* message: initialize dialog box */
\r
6319 /* Center the dialog over the application window */
\r
6320 CenterWindow (hDlg, GetWindow (hDlg, GW_OWNER));
\r
6321 SetDlgItemText(hDlg, ABOUTBOX_Version, programVersion);
\r
6322 Translate(hDlg, ABOUTBOX);
\r
6326 case WM_COMMAND: /* message: received a command */
\r
6327 if (LOWORD(wParam) == IDOK /* "OK" box selected? */
\r
6328 || LOWORD(wParam) == IDCANCEL) { /* System menu close command? */
\r
6329 EndDialog(hDlg, TRUE); /* Exit the dialog */
\r
6337 /*---------------------------------------------------------------------------*\
\r
6339 * Comment Dialog functions
\r
6341 \*---------------------------------------------------------------------------*/
\r
6344 CommentDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
6346 static HANDLE hwndText = NULL;
\r
6347 int len, newSizeX, newSizeY, flags;
\r
6348 static int sizeX, sizeY;
\r
6353 switch (message) {
\r
6354 case WM_INITDIALOG: /* message: initialize dialog box */
\r
6355 /* Initialize the dialog items */
\r
6356 Translate(hDlg, DLG_EditComment);
\r
6357 hwndText = GetDlgItem(hDlg, OPT_CommentText);
\r
6358 SetDlgItemText(hDlg, OPT_CommentText, commentText);
\r
6359 EnableWindow(GetDlgItem(hDlg, OPT_CancelComment), editComment);
\r
6360 EnableWindow(GetDlgItem(hDlg, OPT_ClearComment), editComment);
\r
6361 EnableWindow(GetDlgItem(hDlg, OPT_EditComment), !editComment);
\r
6362 SendMessage(hwndText, EM_SETREADONLY, !editComment, 0);
\r
6363 SetWindowText(hDlg, commentTitle);
\r
6364 if (editComment) {
\r
6365 SetFocus(hwndText);
\r
6367 SetFocus(GetDlgItem(hDlg, IDOK));
\r
6369 SendMessage(GetDlgItem(hDlg, OPT_CommentText),
\r
6370 WM_SETFONT, (WPARAM)font[boardSize][COMMENT_FONT]->hf,
\r
6371 MAKELPARAM(FALSE, 0));
\r
6372 /* Size and position the dialog */
\r
6373 if (!commentDialog) {
\r
6374 commentDialog = hDlg;
\r
6375 flags = SWP_NOZORDER;
\r
6376 GetClientRect(hDlg, &rect);
\r
6377 sizeX = rect.right;
\r
6378 sizeY = rect.bottom;
\r
6379 if (wpComment.x != CW_USEDEFAULT && wpComment.y != CW_USEDEFAULT &&
\r
6380 wpComment.width != CW_USEDEFAULT && wpComment.height != CW_USEDEFAULT) {
\r
6381 WINDOWPLACEMENT wp;
\r
6382 EnsureOnScreen(&wpComment.x, &wpComment.y, 0, 0);
\r
6383 wp.length = sizeof(WINDOWPLACEMENT);
\r
6385 wp.showCmd = SW_SHOW;
\r
6386 wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;
\r
6387 wp.rcNormalPosition.left = wpComment.x;
\r
6388 wp.rcNormalPosition.right = wpComment.x + wpComment.width;
\r
6389 wp.rcNormalPosition.top = wpComment.y;
\r
6390 wp.rcNormalPosition.bottom = wpComment.y + wpComment.height;
\r
6391 SetWindowPlacement(hDlg, &wp);
\r
6393 GetClientRect(hDlg, &rect);
\r
6394 newSizeX = rect.right;
\r
6395 newSizeY = rect.bottom;
\r
6396 ResizeEditPlusButtons(hDlg, hwndText, sizeX, sizeY,
\r
6397 newSizeX, newSizeY);
\r
6402 SendDlgItemMessage( hDlg, OPT_CommentText, EM_SETEVENTMASK, 0, ENM_MOUSEEVENTS | ENM_KEYEVENTS );
\r
6405 case WM_COMMAND: /* message: received a command */
\r
6406 switch (LOWORD(wParam)) {
\r
6408 if (editComment) {
\r
6410 /* Read changed options from the dialog box */
\r
6411 hwndText = GetDlgItem(hDlg, OPT_CommentText);
\r
6412 len = GetWindowTextLength(hwndText);
\r
6413 str = (char *) malloc(len + 1);
\r
6414 GetWindowText(hwndText, str, len + 1);
\r
6423 ReplaceComment(commentIndex, str);
\r
6430 case OPT_CancelComment:
\r
6434 case OPT_ClearComment:
\r
6435 SetDlgItemText(hDlg, OPT_CommentText, "");
\r
6438 case OPT_EditComment:
\r
6439 EditCommentEvent();
\r
6447 case WM_NOTIFY: // [HGM] vari: cloned from whistory.c
\r
6448 if( wParam == OPT_CommentText ) {
\r
6449 MSGFILTER * lpMF = (MSGFILTER *) lParam;
\r
6451 if( lpMF->msg == WM_RBUTTONDOWN && (lpMF->wParam & (MK_CONTROL | MK_SHIFT)) == 0 ||
\r
6452 lpMF->msg == WM_CHAR && lpMF->wParam == '\022' ) {
\r
6456 pt.x = LOWORD( lpMF->lParam );
\r
6457 pt.y = HIWORD( lpMF->lParam );
\r
6459 if(lpMF->msg == WM_CHAR) {
\r
6461 SendDlgItemMessage( hDlg, OPT_CommentText, EM_EXGETSEL, 0, (LPARAM) &sel );
\r
6462 index = sel.cpMin;
\r
6464 index = SendDlgItemMessage( hDlg, OPT_CommentText, EM_CHARFROMPOS, 0, (LPARAM) &pt );
\r
6466 hwndText = GetDlgItem(hDlg, OPT_CommentText); // cloned from above
\r
6467 len = GetWindowTextLength(hwndText);
\r
6468 str = (char *) malloc(len + 1);
\r
6469 GetWindowText(hwndText, str, len + 1);
\r
6470 ReplaceComment(commentIndex, str);
\r
6471 if(commentIndex != currentMove) ToNrEvent(commentIndex);
\r
6472 LoadVariation( index, str ); // [HGM] also does the actual moving to it, now
\r
6475 /* Zap the message for good: apparently, returning non-zero is not enough */
\r
6476 lpMF->msg = WM_USER;
\r
6484 newSizeX = LOWORD(lParam);
\r
6485 newSizeY = HIWORD(lParam);
\r
6486 ResizeEditPlusButtons(hDlg, hwndText, sizeX, sizeY, newSizeX, newSizeY);
\r
6491 case WM_GETMINMAXINFO:
\r
6492 /* Prevent resizing window too small */
\r
6493 mmi = (MINMAXINFO *) lParam;
\r
6494 mmi->ptMinTrackSize.x = 100;
\r
6495 mmi->ptMinTrackSize.y = 100;
\r
6502 EitherCommentPopUp(int index, char *title, char *str, BOOLEAN edit)
\r
6507 CheckMenuItem(GetMenu(hwndMain), IDM_Comment, edit ? MF_CHECKED : MF_UNCHECKED);
\r
6509 if (str == NULL) str = "";
\r
6510 p = (char *) malloc(2 * strlen(str) + 2);
\r
6513 if (*str == '\n') *q++ = '\r';
\r
6517 if (commentText != NULL) free(commentText);
\r
6519 commentIndex = index;
\r
6520 commentTitle = title;
\r
6522 editComment = edit;
\r
6524 if (commentDialog) {
\r
6525 SendMessage(commentDialog, WM_INITDIALOG, 0, 0);
\r
6526 if (!commentUp) ShowWindow(commentDialog, SW_SHOW);
\r
6528 lpProc = MakeProcInstance((FARPROC)CommentDialog, hInst);
\r
6529 CreateDialog(hInst, MAKEINTRESOURCE(DLG_EditComment),
\r
6530 hwndMain, (DLGPROC)lpProc);
\r
6531 FreeProcInstance(lpProc);
\r
6537 /*---------------------------------------------------------------------------*\
\r
6539 * Type-in move dialog functions
\r
6541 \*---------------------------------------------------------------------------*/
\r
6544 TypeInMoveDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
6546 char move[MSG_SIZ];
\r
6549 switch (message) {
\r
6550 case WM_INITDIALOG:
\r
6551 move[0] = (char) lParam;
\r
6552 move[1] = NULLCHAR;
\r
6553 CenterWindowEx(hDlg, GetWindow(hDlg, GW_OWNER), 1 );
\r
6554 Translate(hDlg, DLG_TypeInMove);
\r
6555 hInput = GetDlgItem(hDlg, OPT_Move);
\r
6556 SetWindowText(hInput, move);
\r
6558 SendMessage(hInput, EM_SETSEL, (WPARAM)9999, (LPARAM)9999);
\r
6562 switch (LOWORD(wParam)) {
\r
6565 shiftKey = GetKeyState(VK_SHIFT) < 0; // [HGM] remember last shift status
\r
6566 GetDlgItemText(hDlg, OPT_Move, move, sizeof(move));
\r
6567 TypeInDoneEvent(move);
\r
6568 EndDialog(hDlg, TRUE);
\r
6571 EndDialog(hDlg, FALSE);
\r
6582 PopUpMoveDialog(char firstchar)
\r
6586 lpProc = MakeProcInstance((FARPROC)TypeInMoveDialog, hInst);
\r
6587 DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_TypeInMove),
\r
6588 hwndMain, (DLGPROC)lpProc, (LPARAM)firstchar);
\r
6589 FreeProcInstance(lpProc);
\r
6592 /*---------------------------------------------------------------------------*\
\r
6594 * Type-in name dialog functions
\r
6596 \*---------------------------------------------------------------------------*/
\r
6599 TypeInNameDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
6601 char move[MSG_SIZ];
\r
6604 switch (message) {
\r
6605 case WM_INITDIALOG:
\r
6606 move[0] = (char) lParam;
\r
6607 move[1] = NULLCHAR;
\r
6608 CenterWindowEx(hDlg, GetWindow(hDlg, GW_OWNER), 1 );
\r
6609 Translate(hDlg, DLG_TypeInName);
\r
6610 hInput = GetDlgItem(hDlg, OPT_Name);
\r
6611 SetWindowText(hInput, move);
\r
6613 SendMessage(hInput, EM_SETSEL, (WPARAM)9999, (LPARAM)9999);
\r
6617 switch (LOWORD(wParam)) {
\r
6619 GetDlgItemText(hDlg, OPT_Name, move, sizeof(move));
\r
6620 appData.userName = strdup(move);
\r
6623 if(gameMode == MachinePlaysWhite || gameMode == MachinePlaysBlack) {
\r
6624 snprintf(move, MSG_SIZ, "%s vs. %s", gameInfo.white, gameInfo.black);
\r
6625 DisplayTitle(move);
\r
6629 EndDialog(hDlg, TRUE);
\r
6632 EndDialog(hDlg, FALSE);
\r
6643 PopUpNameDialog(char firstchar)
\r
6647 lpProc = MakeProcInstance((FARPROC)TypeInNameDialog, hInst);
\r
6648 DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_TypeInName),
\r
6649 hwndMain, (DLGPROC)lpProc, (LPARAM)firstchar);
\r
6650 FreeProcInstance(lpProc);
\r
6653 /*---------------------------------------------------------------------------*\
\r
6657 \*---------------------------------------------------------------------------*/
\r
6659 /* Nonmodal error box */
\r
6660 LRESULT CALLBACK ErrorDialog(HWND hDlg, UINT message,
\r
6661 WPARAM wParam, LPARAM lParam);
\r
6664 ErrorPopUp(char *title, char *content)
\r
6668 BOOLEAN modal = hwndMain == NULL;
\r
6686 strncpy(errorTitle, title, sizeof(errorTitle));
\r
6687 errorTitle[sizeof(errorTitle) - 1] = '\0';
\r
6690 MessageBox(NULL, errorMessage, errorTitle, MB_OK|MB_ICONEXCLAMATION);
\r
6692 lpProc = MakeProcInstance((FARPROC)ErrorDialog, hInst);
\r
6693 CreateDialog(hInst, MAKEINTRESOURCE(DLG_Error),
\r
6694 hwndMain, (DLGPROC)lpProc);
\r
6695 FreeProcInstance(lpProc);
\r
6702 if (!appData.popupMoveErrors && moveErrorMessageUp) DisplayMessage("", "");
\r
6703 if (errorDialog == NULL) return;
\r
6704 DestroyWindow(errorDialog);
\r
6705 errorDialog = NULL;
\r
6706 if(errorExitStatus) ExitEvent(errorExitStatus);
\r
6710 ErrorDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
6715 switch (message) {
\r
6716 case WM_INITDIALOG:
\r
6717 GetWindowRect(hDlg, &rChild);
\r
6720 SetWindowPos(hDlg, NULL, rChild.left,
\r
6721 rChild.top + boardRect.top - (rChild.bottom - rChild.top),
\r
6722 0, 0, SWP_NOZORDER|SWP_NOSIZE);
\r
6726 [AS] It seems that the above code wants to move the dialog up in the "caption
\r
6727 area" of the main window, but it uses the dialog height as an hard-coded constant,
\r
6728 and it doesn't work when you resize the dialog.
\r
6729 For now, just give it a default position.
\r
6731 SetWindowPos(hDlg, NULL, boardRect.left+8, boardRect.top+8, 0, 0, SWP_NOZORDER|SWP_NOSIZE);
\r
6732 Translate(hDlg, DLG_Error);
\r
6734 errorDialog = hDlg;
\r
6735 SetWindowText(hDlg, errorTitle);
\r
6736 hwndText = GetDlgItem(hDlg, OPT_ErrorText);
\r
6737 SetDlgItemText(hDlg, OPT_ErrorText, errorMessage);
\r
6741 switch (LOWORD(wParam)) {
\r
6744 if (errorDialog == hDlg) errorDialog = NULL;
\r
6745 DestroyWindow(hDlg);
\r
6757 HWND gothicDialog = NULL;
\r
6760 GothicDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
6764 int height = GetSystemMetrics(SM_CYCAPTION)+GetSystemMetrics(SM_CYFRAME);
\r
6766 switch (message) {
\r
6767 case WM_INITDIALOG:
\r
6768 GetWindowRect(hDlg, &rChild);
\r
6770 SetWindowPos(hDlg, NULL, wpMain.x, wpMain.y-height, wpMain.width, height,
\r
6774 [AS] It seems that the above code wants to move the dialog up in the "caption
\r
6775 area" of the main window, but it uses the dialog height as an hard-coded constant,
\r
6776 and it doesn't work when you resize the dialog.
\r
6777 For now, just give it a default position.
\r
6779 gothicDialog = hDlg;
\r
6780 SetWindowText(hDlg, errorTitle);
\r
6781 hwndText = GetDlgItem(hDlg, OPT_ErrorText);
\r
6782 SetDlgItemText(hDlg, OPT_ErrorText, errorMessage);
\r
6786 switch (LOWORD(wParam)) {
\r
6789 if (errorDialog == hDlg) errorDialog = NULL;
\r
6790 DestroyWindow(hDlg);
\r
6802 GothicPopUp(char *title, VariantClass variant)
\r
6805 static char *lastTitle;
\r
6807 strncpy(errorTitle, title, sizeof(errorTitle));
\r
6808 errorTitle[sizeof(errorTitle) - 1] = '\0';
\r
6810 if(lastTitle != title && gothicDialog != NULL) {
\r
6811 DestroyWindow(gothicDialog);
\r
6812 gothicDialog = NULL;
\r
6814 if(variant != VariantNormal && gothicDialog == NULL) {
\r
6815 title = lastTitle;
\r
6816 lpProc = MakeProcInstance((FARPROC)GothicDialog, hInst);
\r
6817 CreateDialog(hInst, MAKEINTRESOURCE(DLG_Error),
\r
6818 hwndMain, (DLGPROC)lpProc);
\r
6819 FreeProcInstance(lpProc);
\r
6824 /*---------------------------------------------------------------------------*\
\r
6826 * Ics Interaction console functions
\r
6828 \*---------------------------------------------------------------------------*/
\r
6830 #define HISTORY_SIZE 64
\r
6831 static char *history[HISTORY_SIZE];
\r
6832 int histIn = 0, histP = 0;
\r
6835 SaveInHistory(char *cmd)
\r
6837 if (history[histIn] != NULL) {
\r
6838 free(history[histIn]);
\r
6839 history[histIn] = NULL;
\r
6841 if (*cmd == NULLCHAR) return;
\r
6842 history[histIn] = StrSave(cmd);
\r
6843 histIn = (histIn + 1) % HISTORY_SIZE;
\r
6844 if (history[histIn] != NULL) {
\r
6845 free(history[histIn]);
\r
6846 history[histIn] = NULL;
\r
6852 PrevInHistory(char *cmd)
\r
6855 if (histP == histIn) {
\r
6856 if (history[histIn] != NULL) free(history[histIn]);
\r
6857 history[histIn] = StrSave(cmd);
\r
6859 newhp = (histP - 1 + HISTORY_SIZE) % HISTORY_SIZE;
\r
6860 if (newhp == histIn || history[newhp] == NULL) return NULL;
\r
6862 return history[histP];
\r
6868 if (histP == histIn) return NULL;
\r
6869 histP = (histP + 1) % HISTORY_SIZE;
\r
6870 return history[histP];
\r
6874 LoadIcsTextMenu(IcsTextMenuEntry *e)
\r
6878 hmenu = LoadMenu(hInst, "TextMenu");
\r
6879 h = GetSubMenu(hmenu, 0);
\r
6881 if (strcmp(e->item, "-") == 0) {
\r
6882 AppendMenu(h, MF_SEPARATOR, 0, 0);
\r
6883 } else { // [HGM] re-written a bit to use only one AppendMenu call for both cases (| or no |)
\r
6884 int flags = MF_STRING, j = 0;
\r
6885 if (e->item[0] == '|') {
\r
6886 flags |= MF_MENUBARBREAK;
\r
6889 if(!strcmp(e->command, "none")) flags |= MF_GRAYED; // [HGM] chatclick: provide inactive dummy
\r
6890 AppendMenu(h, flags, IDM_CommandX + i, e->item + j);
\r
6898 WNDPROC consoleTextWindowProc;
\r
6901 CommandX(HWND hwnd, char *command, BOOLEAN getname, BOOLEAN immediate)
\r
6903 char buf[MSG_SIZ], name[MSG_SIZ];
\r
6904 HWND hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
6908 SetWindowText(hInput, command);
\r
6910 SendMessage(hInput, WM_CHAR, '\r', 0);
\r
6912 sel.cpMin = 999999;
\r
6913 sel.cpMax = 999999;
\r
6914 SendMessage(hInput, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
6919 SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);
\r
6920 if (sel.cpMin == sel.cpMax) {
\r
6921 /* Expand to surrounding word */
\r
6924 tr.chrg.cpMax = sel.cpMin;
\r
6925 tr.chrg.cpMin = --sel.cpMin;
\r
6926 if (sel.cpMin < 0) break;
\r
6927 tr.lpstrText = name;
\r
6928 SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr);
\r
6929 } while (isalpha(name[0]) || isdigit(name[0]) || name[0] == '-');
\r
6933 tr.chrg.cpMin = sel.cpMax;
\r
6934 tr.chrg.cpMax = ++sel.cpMax;
\r
6935 tr.lpstrText = name;
\r
6936 if (SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr) < 1) break;
\r
6937 } while (isalpha(name[0]) || isdigit(name[0]) || name[0] == '-');
\r
6940 if (sel.cpMax == sel.cpMin || sel.cpMax - sel.cpMin > MSG_SIZ/2) {
\r
6941 MessageBeep(MB_ICONEXCLAMATION);
\r
6945 tr.lpstrText = name;
\r
6946 SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr);
\r
6948 if (sel.cpMax - sel.cpMin > MSG_SIZ/2) {
\r
6949 MessageBeep(MB_ICONEXCLAMATION);
\r
6952 SendMessage(hwnd, EM_GETSELTEXT, 0, (LPARAM) name);
\r
6955 if(strstr(command, "%s")) snprintf(buf, MSG_SIZ, command, name); else
\r
6956 snprintf(buf, MSG_SIZ, "%s %s", command, name);
\r
6957 SetWindowText(hInput, buf);
\r
6958 SendMessage(hInput, WM_CHAR, '\r', 0);
\r
6960 if(!strcmp(command, "chat")) { ChatPopUp(name); return; }
\r
6961 snprintf(buf, MSG_SIZ, "%s %s ", command, name); /* trailing space */
\r
6962 SetWindowText(hInput, buf);
\r
6963 sel.cpMin = 999999;
\r
6964 sel.cpMax = 999999;
\r
6965 SendMessage(hInput, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
6971 ConsoleTextSubclass(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
\r
6976 switch (message) {
\r
6978 if (!(GetKeyState(VK_CONTROL) & ~1)) break;
\r
6979 if(wParam=='R') return 0;
\r
6982 SendMessage(hwnd, EM_LINESCROLL, 0, -999999);
\r
6985 sel.cpMin = 999999;
\r
6986 sel.cpMax = 999999;
\r
6987 SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
6988 SendMessage(hwnd, EM_SCROLLCARET, 0, 0);
\r
6993 if(wParam != '\022') {
\r
6994 if (wParam == '\t') {
\r
6995 if (GetKeyState(VK_SHIFT) < 0) {
\r
6997 if (IsIconic(hwndMain)) ShowWindow(hwndMain, SW_RESTORE);
\r
6998 if (buttonDesc[0].hwnd) {
\r
6999 SetFocus(buttonDesc[0].hwnd);
\r
7001 SetFocus(hwndMain);
\r
7005 SetFocus(GetDlgItem(hwndConsole, OPT_ConsoleInput));
\r
7008 hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
7009 JAWS_DELETE( SetFocus(hInput); )
\r
7010 SendMessage(hInput, message, wParam, lParam);
\r
7013 } // [HGM] navigate: for Ctrl+R, flow into next case (moved up here) to summon up menu
\r
7015 case WM_RBUTTONDOWN:
\r
7016 if (!(GetKeyState(VK_SHIFT) & ~1)) {
\r
7017 /* Move selection here if it was empty */
\r
7019 pt.x = LOWORD(lParam);
\r
7020 pt.y = HIWORD(lParam);
\r
7021 SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);
\r
7022 if (sel.cpMin == sel.cpMax) {
\r
7023 if(lParam != -1) sel.cpMin = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&pt); /*doc is wrong*/
\r
7024 sel.cpMax = sel.cpMin;
\r
7025 SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
7027 SendMessage(hwnd, EM_HIDESELECTION, FALSE, FALSE);
\r
7028 { // [HGM] chatclick: code moved here from WM_RBUTTONUP case, to have menu appear on down-click
\r
7030 HMENU hmenu = LoadIcsTextMenu(icsTextMenuEntry);
\r
7031 SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);
\r
7032 if (sel.cpMin == sel.cpMax) {
\r
7033 EnableMenuItem(hmenu, IDM_Copy, MF_BYCOMMAND|MF_GRAYED);
\r
7034 EnableMenuItem(hmenu, IDM_QuickPaste, MF_BYCOMMAND|MF_GRAYED);
\r
7036 if (!IsClipboardFormatAvailable(CF_TEXT)) {
\r
7037 EnableMenuItem(hmenu, IDM_Paste, MF_BYCOMMAND|MF_GRAYED);
\r
7039 pt.x = LOWORD(lParam)-30; // [HGM] chatclick: make menu pop up with pointer above upper-right item
\r
7040 pt.y = HIWORD(lParam)-10; // make it appear as if mouse moved there, so it will be selected on up-click
\r
7041 PostMessage(hwnd, WM_MOUSEMOVE, wParam, lParam+5);
\r
7042 MenuPopup(hwnd, pt, hmenu, -1);
\r
7046 case WM_RBUTTONUP:
\r
7047 if (GetKeyState(VK_SHIFT) & ~1) {
\r
7048 SendDlgItemMessage(hwndConsole, OPT_ConsoleText,
\r
7049 WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);
\r
7053 hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
7055 return SendMessage(hInput, message, wParam, lParam);
\r
7056 case WM_MBUTTONDOWN:
\r
7057 return SendMessage(hwnd, WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);
\r
7059 switch (LOWORD(wParam)) {
\r
7060 case IDM_QuickPaste:
\r
7062 SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);
\r
7063 if (sel.cpMin == sel.cpMax) {
\r
7064 MessageBeep(MB_ICONEXCLAMATION);
\r
7067 SendMessage(hwnd, WM_COPY, 0, 0);
\r
7068 hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
7069 SendMessage(hInput, WM_PASTE, 0, 0);
\r
7074 SendMessage(hwnd, WM_CUT, 0, 0);
\r
7077 SendMessage(hwnd, WM_PASTE, 0, 0);
\r
7080 SendMessage(hwnd, WM_COPY, 0, 0);
\r
7084 int i = LOWORD(wParam) - IDM_CommandX;
\r
7085 if (i >= 0 && i < ICS_TEXT_MENU_SIZE &&
\r
7086 icsTextMenuEntry[i].command != NULL) {
\r
7087 CommandX(hwnd, icsTextMenuEntry[i].command,
\r
7088 icsTextMenuEntry[i].getname,
\r
7089 icsTextMenuEntry[i].immediate);
\r
7097 return (*consoleTextWindowProc)(hwnd, message, wParam, lParam);
\r
7100 WNDPROC consoleInputWindowProc;
\r
7103 ConsoleInputSubclass(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
\r
7105 char buf[MSG_SIZ];
\r
7107 static BOOL sendNextChar = FALSE;
\r
7108 static BOOL quoteNextChar = FALSE;
\r
7109 InputSource *is = consoleInputSource;
\r
7113 switch (message) {
\r
7115 if (!appData.localLineEditing || sendNextChar) {
\r
7116 is->buf[0] = (CHAR) wParam;
\r
7118 SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);
\r
7119 sendNextChar = FALSE;
\r
7122 if (quoteNextChar) {
\r
7123 buf[0] = (char) wParam;
\r
7124 buf[1] = NULLCHAR;
\r
7125 SendMessage(hwnd, EM_REPLACESEL, TRUE, (LPARAM) buf);
\r
7126 quoteNextChar = FALSE;
\r
7130 case '\r': /* Enter key */
\r
7131 is->count = GetWindowText(hwnd, is->buf, INPUT_SOURCE_BUF_SIZE-1);
\r
7132 if (consoleEcho) SaveInHistory(is->buf);
\r
7133 is->buf[is->count++] = '\n';
\r
7134 is->buf[is->count] = NULLCHAR;
\r
7135 SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);
\r
7136 if (consoleEcho) {
\r
7137 ConsoleOutput(is->buf, is->count, TRUE);
\r
7138 } else if (appData.localLineEditing) {
\r
7139 ConsoleOutput("\n", 1, TRUE);
\r
7142 case '\033': /* Escape key */
\r
7143 SetWindowText(hwnd, "");
\r
7144 cf.cbSize = sizeof(CHARFORMAT);
\r
7145 cf.dwMask = CFM_COLOR|CFM_BOLD|CFM_ITALIC|CFM_UNDERLINE|CFM_STRIKEOUT;
\r
7146 if (consoleEcho) {
\r
7147 cf.crTextColor = textAttribs[ColorNormal].color;
\r
7149 cf.crTextColor = COLOR_ECHOOFF;
\r
7151 cf.dwEffects = textAttribs[ColorNormal].effects;
\r
7152 SendMessage(hwnd, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf);
\r
7154 case '\t': /* Tab key */
\r
7155 if (GetKeyState(VK_SHIFT) < 0) {
\r
7157 SetFocus(GetDlgItem(hwndConsole, OPT_ConsoleText));
\r
7160 if (IsIconic(hwndMain)) ShowWindow(hwndMain, SW_RESTORE);
\r
7161 if (buttonDesc[0].hwnd) {
\r
7162 SetFocus(buttonDesc[0].hwnd);
\r
7164 SetFocus(hwndMain);
\r
7168 case '\023': /* Ctrl+S */
\r
7169 sendNextChar = TRUE;
\r
7171 case '\021': /* Ctrl+Q */
\r
7172 quoteNextChar = TRUE;
\r
7182 GetWindowText(hwnd, buf, MSG_SIZ);
\r
7183 p = PrevInHistory(buf);
\r
7185 SetWindowText(hwnd, p);
\r
7186 sel.cpMin = 999999;
\r
7187 sel.cpMax = 999999;
\r
7188 SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
7193 p = NextInHistory();
\r
7195 SetWindowText(hwnd, p);
\r
7196 sel.cpMin = 999999;
\r
7197 sel.cpMax = 999999;
\r
7198 SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
7204 if (!(GetKeyState(VK_CONTROL) & ~1)) break;
\r
7208 SendDlgItemMessage(hwndConsole, OPT_ConsoleText, message, wParam, lParam);
\r
7212 case WM_MBUTTONDOWN:
\r
7213 SendDlgItemMessage(hwndConsole, OPT_ConsoleText,
\r
7214 WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);
\r
7216 case WM_RBUTTONUP:
\r
7217 if (GetKeyState(VK_SHIFT) & ~1) {
\r
7218 SendDlgItemMessage(hwndConsole, OPT_ConsoleText,
\r
7219 WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);
\r
7223 hmenu = LoadMenu(hInst, "InputMenu");
\r
7224 SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);
\r
7225 if (sel.cpMin == sel.cpMax) {
\r
7226 EnableMenuItem(hmenu, IDM_Copy, MF_BYCOMMAND|MF_GRAYED);
\r
7227 EnableMenuItem(hmenu, IDM_Cut, MF_BYCOMMAND|MF_GRAYED);
\r
7229 if (!IsClipboardFormatAvailable(CF_TEXT)) {
\r
7230 EnableMenuItem(hmenu, IDM_Paste, MF_BYCOMMAND|MF_GRAYED);
\r
7232 pt.x = LOWORD(lParam);
\r
7233 pt.y = HIWORD(lParam);
\r
7234 MenuPopup(hwnd, pt, hmenu, -1);
\r
7238 switch (LOWORD(wParam)) {
\r
7240 SendMessage(hwnd, EM_UNDO, 0, 0);
\r
7242 case IDM_SelectAll:
\r
7244 sel.cpMax = -1; /*999999?*/
\r
7245 SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
7248 SendMessage(hwnd, WM_CUT, 0, 0);
\r
7251 SendMessage(hwnd, WM_PASTE, 0, 0);
\r
7254 SendMessage(hwnd, WM_COPY, 0, 0);
\r
7259 return (*consoleInputWindowProc)(hwnd, message, wParam, lParam);
\r
7262 #define CO_MAX 100000
\r
7263 #define CO_TRIM 1000
\r
7266 ConsoleWndProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
7268 static SnapData sd;
\r
7269 HWND hText, hInput;
\r
7271 static int sizeX, sizeY;
\r
7272 int newSizeX, newSizeY;
\r
7276 hText = GetDlgItem(hDlg, OPT_ConsoleText);
\r
7277 hInput = GetDlgItem(hDlg, OPT_ConsoleInput);
\r
7279 switch (message) {
\r
7281 if (((NMHDR*)lParam)->code == EN_LINK)
\r
7283 ENLINK *pLink = (ENLINK*)lParam;
\r
7284 if (pLink->msg == WM_LBUTTONUP)
\r
7288 tr.chrg = pLink->chrg;
\r
7289 tr.lpstrText = malloc(1+tr.chrg.cpMax-tr.chrg.cpMin);
\r
7290 SendMessage(hText, EM_GETTEXTRANGE, 0, (LPARAM)&tr);
\r
7291 ShellExecute(NULL, "open", tr.lpstrText, NULL, NULL, SW_SHOW);
\r
7292 free(tr.lpstrText);
\r
7296 case WM_INITDIALOG: /* message: initialize dialog box */
\r
7297 hwndConsole = hDlg;
\r
7299 consoleTextWindowProc = (WNDPROC)
\r
7300 SetWindowLongPtr(hText, GWLP_WNDPROC, (LONG_PTR) ConsoleTextSubclass);
\r
7301 SendMessage(hText, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);
\r
7302 consoleInputWindowProc = (WNDPROC)
\r
7303 SetWindowLongPtr(hInput, GWLP_WNDPROC, (LONG_PTR) ConsoleInputSubclass);
\r
7304 SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);
\r
7305 Colorize(ColorNormal, TRUE);
\r
7306 SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &consoleCF);
\r
7307 ChangedConsoleFont();
\r
7308 GetClientRect(hDlg, &rect);
\r
7309 sizeX = rect.right;
\r
7310 sizeY = rect.bottom;
\r
7311 if (wpConsole.x != CW_USEDEFAULT && wpConsole.y != CW_USEDEFAULT &&
\r
7312 wpConsole.width != CW_USEDEFAULT && wpConsole.height != CW_USEDEFAULT) {
\r
7313 WINDOWPLACEMENT wp;
\r
7314 EnsureOnScreen(&wpConsole.x, &wpConsole.y, 0, 0);
\r
7315 wp.length = sizeof(WINDOWPLACEMENT);
\r
7317 wp.showCmd = SW_SHOW;
\r
7318 wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;
\r
7319 wp.rcNormalPosition.left = wpConsole.x;
\r
7320 wp.rcNormalPosition.right = wpConsole.x + wpConsole.width;
\r
7321 wp.rcNormalPosition.top = wpConsole.y;
\r
7322 wp.rcNormalPosition.bottom = wpConsole.y + wpConsole.height;
\r
7323 SetWindowPlacement(hDlg, &wp);
\r
7326 // [HGM] Chessknight's change 2004-07-13
\r
7327 else { /* Determine Defaults */
\r
7328 WINDOWPLACEMENT wp;
\r
7329 wpConsole.x = wpMain.width + 1;
\r
7330 wpConsole.y = wpMain.y;
\r
7331 wpConsole.width = screenWidth - wpMain.width;
\r
7332 wpConsole.height = wpMain.height;
\r
7333 EnsureOnScreen(&wpConsole.x, &wpConsole.y, 0, 0);
\r
7334 wp.length = sizeof(WINDOWPLACEMENT);
\r
7336 wp.showCmd = SW_SHOW;
\r
7337 wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;
\r
7338 wp.rcNormalPosition.left = wpConsole.x;
\r
7339 wp.rcNormalPosition.right = wpConsole.x + wpConsole.width;
\r
7340 wp.rcNormalPosition.top = wpConsole.y;
\r
7341 wp.rcNormalPosition.bottom = wpConsole.y + wpConsole.height;
\r
7342 SetWindowPlacement(hDlg, &wp);
\r
7345 // Allow hText to highlight URLs and send notifications on them
\r
7346 wMask = (WORD) SendMessage(hText, EM_GETEVENTMASK, 0, 0L);
\r
7347 SendMessage(hText, EM_SETEVENTMASK, 0, wMask | ENM_LINK);
\r
7348 SendMessage(hText, EM_AUTOURLDETECT, TRUE, 0L);
\r
7349 SetWindowLongPtr(hText, GWLP_USERDATA, 79); // initialize the text window's width
\r
7363 if (IsIconic(hDlg)) break;
\r
7364 newSizeX = LOWORD(lParam);
\r
7365 newSizeY = HIWORD(lParam);
\r
7366 if (sizeX != newSizeX || sizeY != newSizeY) {
\r
7367 RECT rectText, rectInput;
\r
7369 int newTextHeight, newTextWidth;
\r
7370 GetWindowRect(hText, &rectText);
\r
7371 newTextWidth = rectText.right - rectText.left + newSizeX - sizeX;
\r
7372 newTextHeight = rectText.bottom - rectText.top + newSizeY - sizeY;
\r
7373 if (newTextHeight < 0) {
\r
7374 newSizeY += -newTextHeight;
\r
7375 newTextHeight = 0;
\r
7377 SetWindowPos(hText, NULL, 0, 0,
\r
7378 newTextWidth, newTextHeight, SWP_NOZORDER|SWP_NOMOVE);
\r
7379 GetWindowRect(hInput, &rectInput); /* gives screen coords */
\r
7380 pt.x = rectInput.left;
\r
7381 pt.y = rectInput.top + newSizeY - sizeY;
\r
7382 ScreenToClient(hDlg, &pt);
\r
7383 SetWindowPos(hInput, NULL,
\r
7384 pt.x, pt.y, /* needs client coords */
\r
7385 rectInput.right - rectInput.left + newSizeX - sizeX,
\r
7386 rectInput.bottom - rectInput.top, SWP_NOZORDER);
\r
7392 case WM_GETMINMAXINFO:
\r
7393 /* Prevent resizing window too small */
\r
7394 mmi = (MINMAXINFO *) lParam;
\r
7395 mmi->ptMinTrackSize.x = 100;
\r
7396 mmi->ptMinTrackSize.y = 100;
\r
7399 /* [AS] Snapping */
\r
7400 case WM_ENTERSIZEMOVE:
\r
7401 return OnEnterSizeMove( &sd, hDlg, wParam, lParam );
\r
7404 return OnSizing( &sd, hDlg, wParam, lParam );
\r
7407 return OnMoving( &sd, hDlg, wParam, lParam );
\r
7409 case WM_EXITSIZEMOVE:
\r
7410 UpdateICSWidth(hText);
\r
7411 return OnExitSizeMove( &sd, hDlg, wParam, lParam );
\r
7414 return DefWindowProc(hDlg, message, wParam, lParam);
\r
7422 if (hwndConsole) return;
\r
7423 hCons = CreateDialog(hInst, szConsoleName, 0, NULL);
\r
7424 SendMessage(hCons, WM_INITDIALOG, 0, 0);
\r
7429 ConsoleOutput(char* data, int length, int forceVisible)
\r
7434 char buf[CO_MAX+1];
\r
7437 static int delayLF = 0;
\r
7438 CHARRANGE savesel, sel;
\r
7440 if (hwndConsole == NULL || length > CO_MAX-100 || length == 0) return;
\r
7448 while (length--) {
\r
7456 } else if (*p == '\007') {
\r
7457 MyPlaySound(&sounds[(int)SoundBell]);
\r
7464 hText = GetDlgItem(hwndConsole, OPT_ConsoleText);
\r
7465 SendMessage(hText, EM_HIDESELECTION, TRUE, FALSE);
\r
7466 /* Save current selection */
\r
7467 SendMessage(hText, EM_EXGETSEL, 0, (LPARAM)&savesel);
\r
7468 exlen = GetWindowTextLength(hText);
\r
7469 /* Find out whether current end of text is visible */
\r
7470 SendMessage(hText, EM_GETRECT, 0, (LPARAM) &rect);
\r
7471 SendMessage(hText, EM_POSFROMCHAR, (WPARAM) &pEnd, exlen);
\r
7472 /* Trim existing text if it's too long */
\r
7473 if (exlen + (q - buf) > CO_MAX) {
\r
7474 trim = (CO_TRIM > (q - buf)) ? CO_TRIM : (q - buf);
\r
7477 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
7478 SendMessage(hText, EM_REPLACESEL, 0, (LPARAM)"");
\r
7480 savesel.cpMin -= trim;
\r
7481 savesel.cpMax -= trim;
\r
7482 if (exlen < 0) exlen = 0;
\r
7483 if (savesel.cpMin < 0) savesel.cpMin = 0;
\r
7484 if (savesel.cpMax < savesel.cpMin) savesel.cpMax = savesel.cpMin;
\r
7486 /* Append the new text */
\r
7487 sel.cpMin = exlen;
\r
7488 sel.cpMax = exlen;
\r
7489 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
7490 SendMessage(hText, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&consoleCF);
\r
7491 SendMessage(hText, EM_REPLACESEL, 0, (LPARAM) buf);
\r
7492 if (forceVisible || exlen == 0 ||
\r
7493 (rect.left <= pEnd.x && pEnd.x < rect.right &&
\r
7494 rect.top <= pEnd.y && pEnd.y < rect.bottom)) {
\r
7495 /* Scroll to make new end of text visible if old end of text
\r
7496 was visible or new text is an echo of user typein */
\r
7497 sel.cpMin = 9999999;
\r
7498 sel.cpMax = 9999999;
\r
7499 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
7500 SendMessage(hText, EM_HIDESELECTION, FALSE, FALSE);
\r
7501 SendMessage(hText, EM_SCROLLCARET, 0, 0);
\r
7502 SendMessage(hText, EM_HIDESELECTION, TRUE, FALSE);
\r
7504 if (savesel.cpMax == exlen || forceVisible) {
\r
7505 /* Move insert point to new end of text if it was at the old
\r
7506 end of text or if the new text is an echo of user typein */
\r
7507 sel.cpMin = 9999999;
\r
7508 sel.cpMax = 9999999;
\r
7509 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
7511 /* Restore previous selection */
\r
7512 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&savesel);
\r
7514 SendMessage(hText, EM_HIDESELECTION, FALSE, FALSE);
\r
7521 DisplayHoldingsCount(HDC hdc, int x, int y, int rightAlign, int copyNumber)
\r
7525 COLORREF oldFg, oldBg;
\r
7529 if(copyNumber > 1)
\r
7530 snprintf(buf, sizeof(buf)/sizeof(buf[0]),"%d", copyNumber); else buf[0] = 0;
\r
7532 oldFg = SetTextColor(hdc, RGB(255, 255, 255)); /* white */
\r
7533 oldBg = SetBkColor(hdc, RGB(0, 0, 0)); /* black */
\r
7534 oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);
\r
7537 rect.right = x + squareSize;
\r
7539 rect.bottom = y + squareSize;
\r
7542 ExtTextOut(hdc, x + MESSAGE_LINE_LEFTMARGIN
\r
7543 + (rightAlign ? (squareSize*2)/3 : 0),
\r
7544 y, ETO_CLIPPED|ETO_OPAQUE,
\r
7545 &rect, str, strlen(str), NULL);
\r
7547 (void) SetTextColor(hdc, oldFg);
\r
7548 (void) SetBkColor(hdc, oldBg);
\r
7549 (void) SelectObject(hdc, oldFont);
\r
7553 DisplayAClock(HDC hdc, int timeRemaining, int highlight,
\r
7554 RECT *rect, char *color, char *flagFell)
\r
7558 COLORREF oldFg, oldBg;
\r
7561 if (twoBoards && partnerUp) return;
\r
7562 if (appData.clockMode) {
\r
7564 snprintf(buf, sizeof(buf)/sizeof(buf[0]), "%c %s %s", color[0], TimeString(timeRemaining), flagFell);
\r
7566 snprintf(buf, sizeof(buf)/sizeof(buf[0]), "%s:%c%s %s", color, (logoHeight>0 ? 0 : ' '), TimeString(timeRemaining), flagFell);
\r
7573 oldFg = SetTextColor(hdc, RGB(255, 255, 255)); /* white */
\r
7574 oldBg = SetBkColor(hdc, RGB(0, 0, 0)); /* black */
\r
7576 oldFg = SetTextColor(hdc, RGB(0, 0, 0)); /* black */
\r
7577 oldBg = SetBkColor(hdc, RGB(255, 255, 255)); /* white */
\r
7579 oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);
\r
7583 ExtTextOut(hdc, rect->left + MESSAGE_LINE_LEFTMARGIN,
\r
7584 rect->top, ETO_CLIPPED|ETO_OPAQUE,
\r
7585 rect, str, strlen(str), NULL);
\r
7586 if(logoHeight > 0 && appData.clockMode) {
\r
7588 str += strlen(color)+2;
\r
7589 r.top = rect->top + logoHeight/2;
\r
7590 r.left = rect->left;
\r
7591 r.right = rect->right;
\r
7592 r.bottom = rect->bottom;
\r
7593 ExtTextOut(hdc, rect->left + MESSAGE_LINE_LEFTMARGIN,
\r
7594 r.top, ETO_CLIPPED|ETO_OPAQUE,
\r
7595 &r, str, strlen(str), NULL);
\r
7597 (void) SetTextColor(hdc, oldFg);
\r
7598 (void) SetBkColor(hdc, oldBg);
\r
7599 (void) SelectObject(hdc, oldFont);
\r
7604 DoReadFile(HANDLE hFile, char *buf, int count, DWORD *outCount,
\r
7610 if( count <= 0 ) {
\r
7611 if (appData.debugMode) {
\r
7612 fprintf( debugFP, "DoReadFile: trying to read past end of buffer, overflow = %d\n", count );
\r
7615 return ERROR_INVALID_USER_BUFFER;
\r
7618 ResetEvent(ovl->hEvent);
\r
7619 ovl->Offset = ovl->OffsetHigh = 0;
\r
7620 ok = ReadFile(hFile, buf, count, outCount, ovl);
\r
7624 err = GetLastError();
\r
7625 if (err == ERROR_IO_PENDING) {
\r
7626 ok = GetOverlappedResult(hFile, ovl, outCount, TRUE);
\r
7630 err = GetLastError();
\r
7637 DoWriteFile(HANDLE hFile, char *buf, int count, DWORD *outCount,
\r
7642 ResetEvent(ovl->hEvent);
\r
7643 ovl->Offset = ovl->OffsetHigh = 0;
\r
7644 ok = WriteFile(hFile, buf, count, outCount, ovl);
\r
7648 err = GetLastError();
\r
7649 if (err == ERROR_IO_PENDING) {
\r
7650 ok = GetOverlappedResult(hFile, ovl, outCount, TRUE);
\r
7654 err = GetLastError();
\r
7660 /* [AS] If input is line by line and a line exceed the buffer size, force an error */
\r
7661 void CheckForInputBufferFull( InputSource * is )
\r
7663 if( is->lineByLine && (is->next - is->buf) >= INPUT_SOURCE_BUF_SIZE ) {
\r
7664 /* Look for end of line */
\r
7665 char * p = is->buf;
\r
7667 while( p < is->next && *p != '\n' ) {
\r
7671 if( p >= is->next ) {
\r
7672 if (appData.debugMode) {
\r
7673 fprintf( debugFP, "Input line exceeded buffer size (source id=%lu)\n", is->id );
\r
7676 is->error = ERROR_BROKEN_PIPE; /* [AS] Just any non-successful code! */
\r
7677 is->count = (DWORD) -1;
\r
7678 is->next = is->buf;
\r
7684 InputThread(LPVOID arg)
\r
7689 is = (InputSource *) arg;
\r
7690 ovl.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
\r
7691 ovl.Internal = ovl.InternalHigh = ovl.Offset = ovl.OffsetHigh = 0;
\r
7692 while (is->hThread != NULL) {
\r
7693 is->error = DoReadFile(is->hFile, is->next,
\r
7694 INPUT_SOURCE_BUF_SIZE - (is->next - is->buf),
\r
7695 &is->count, &ovl);
\r
7696 if (is->error == NO_ERROR) {
\r
7697 is->next += is->count;
\r
7699 if (is->error == ERROR_BROKEN_PIPE) {
\r
7700 /* Correct for MS brain damage. EOF reading a pipe is not an error. */
\r
7703 is->count = (DWORD) -1;
\r
7704 /* [AS] The (is->count <= 0) check below is not useful for unsigned values! */
\r
7709 CheckForInputBufferFull( is );
\r
7711 SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);
\r
7713 if( is->count == ((DWORD) -1) ) break; /* [AS] */
\r
7715 if (is->count <= 0) break; /* Quit on EOF or error */
\r
7718 CloseHandle(ovl.hEvent);
\r
7719 CloseHandle(is->hFile);
\r
7721 if (appData.debugMode) {
\r
7722 fprintf( debugFP, "Input thread terminated (id=%lu, error=%d, count=%ld)\n", is->id, is->error, is->count );
\r
7729 /* Windows 95 beta 2 won't let you do overlapped i/o on a console or pipe */
\r
7731 NonOvlInputThread(LPVOID arg)
\r
7738 is = (InputSource *) arg;
\r
7739 while (is->hThread != NULL) {
\r
7740 is->error = ReadFile(is->hFile, is->next,
\r
7741 INPUT_SOURCE_BUF_SIZE - (is->next - is->buf),
\r
7742 &is->count, NULL) ? NO_ERROR : GetLastError();
\r
7743 if (is->error == NO_ERROR) {
\r
7744 /* Change CRLF to LF */
\r
7745 if (is->next > is->buf) {
\r
7747 i = is->count + 1;
\r
7755 if (prev == '\r' && *p == '\n') {
\r
7767 if (is->error == ERROR_BROKEN_PIPE) {
\r
7768 /* Correct for MS brain damage. EOF reading a pipe is not an error. */
\r
7771 is->count = (DWORD) -1;
\r
7775 CheckForInputBufferFull( is );
\r
7777 SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);
\r
7779 if( is->count == ((DWORD) -1) ) break; /* [AS] */
\r
7781 if (is->count < 0) break; /* Quit on error */
\r
7783 CloseHandle(is->hFile);
\r
7788 SocketInputThread(LPVOID arg)
\r
7792 is = (InputSource *) arg;
\r
7793 while (is->hThread != NULL) {
\r
7794 is->count = recv(is->sock, is->buf, INPUT_SOURCE_BUF_SIZE, 0);
\r
7795 if ((int)is->count == SOCKET_ERROR) {
\r
7796 is->count = (DWORD) -1;
\r
7797 is->error = WSAGetLastError();
\r
7799 is->error = NO_ERROR;
\r
7800 is->next += is->count;
\r
7801 if (is->count == 0 && is->second == is) {
\r
7802 /* End of file on stderr; quit with no message */
\r
7806 SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);
\r
7808 if( is->count == ((DWORD) -1) ) break; /* [AS] */
\r
7810 if (is->count <= 0) break; /* Quit on EOF or error */
\r
7816 InputEvent(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
\r
7820 is = (InputSource *) lParam;
\r
7821 if (is->lineByLine) {
\r
7822 /* Feed in lines one by one */
\r
7823 char *p = is->buf;
\r
7825 while (q < is->next) {
\r
7826 if (*q++ == '\n') {
\r
7827 (is->func)(is, is->closure, p, q - p, NO_ERROR);
\r
7832 /* Move any partial line to the start of the buffer */
\r
7834 while (p < is->next) {
\r
7839 if (is->error != NO_ERROR || is->count == 0) {
\r
7840 /* Notify backend of the error. Note: If there was a partial
\r
7841 line at the end, it is not flushed through. */
\r
7842 (is->func)(is, is->closure, is->buf, is->count, is->error);
\r
7845 /* Feed in the whole chunk of input at once */
\r
7846 (is->func)(is, is->closure, is->buf, is->count, is->error);
\r
7847 is->next = is->buf;
\r
7851 /*---------------------------------------------------------------------------*\
\r
7853 * Menu enables. Used when setting various modes.
\r
7855 \*---------------------------------------------------------------------------*/
\r
7863 GreyRevert(Boolean grey)
\r
7864 { // [HGM] vari: for retracting variations in local mode
\r
7865 HMENU hmenu = GetMenu(hwndMain);
\r
7866 EnableMenuItem(hmenu, IDM_Revert, MF_BYCOMMAND|(grey ? MF_GRAYED : MF_ENABLED));
\r
7867 EnableMenuItem(hmenu, IDM_Annotate, MF_BYCOMMAND|(grey ? MF_GRAYED : MF_ENABLED));
\r
7871 SetMenuEnables(HMENU hmenu, Enables *enab)
\r
7873 while (enab->item > 0) {
\r
7874 (void) EnableMenuItem(hmenu, enab->item, enab->flags);
\r
7879 Enables gnuEnables[] = {
\r
7880 { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },
\r
7881 { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },
\r
7882 { IDM_IcsClient, MF_BYCOMMAND|MF_GRAYED },
\r
7883 { IDM_Accept, MF_BYCOMMAND|MF_GRAYED },
\r
7884 { IDM_Decline, MF_BYCOMMAND|MF_GRAYED },
\r
7885 { IDM_Rematch, MF_BYCOMMAND|MF_GRAYED },
\r
7886 { IDM_Adjourn, MF_BYCOMMAND|MF_GRAYED },
\r
7887 { IDM_StopExamining, MF_BYCOMMAND|MF_GRAYED },
\r
7888 { IDM_StopObserving, MF_BYCOMMAND|MF_GRAYED },
\r
7889 { IDM_Upload, MF_BYCOMMAND|MF_GRAYED },
\r
7890 { IDM_Revert, MF_BYCOMMAND|MF_GRAYED },
\r
7891 { IDM_Annotate, MF_BYCOMMAND|MF_GRAYED },
\r
7892 { IDM_NewChat, MF_BYCOMMAND|MF_GRAYED },
\r
7894 // Needed to switch from ncp to GNU mode on Engine Load
\r
7895 { ACTION_POS, MF_BYPOSITION|MF_ENABLED },
\r
7896 { IDM_MachineWhite, MF_BYCOMMAND|MF_ENABLED },
\r
7897 { IDM_MachineBlack, MF_BYCOMMAND|MF_ENABLED },
\r
7898 { IDM_TwoMachines, MF_BYCOMMAND|MF_ENABLED },
\r
7899 { IDM_Match, MF_BYCOMMAND|MF_ENABLED },
\r
7900 { IDM_AnalysisMode, MF_BYCOMMAND|MF_ENABLED },
\r
7901 { IDM_AnalyzeFile, MF_BYCOMMAND|MF_ENABLED },
\r
7902 { IDM_Engine1Options, MF_BYCOMMAND|MF_ENABLED },
\r
7903 { IDM_Engine2Options, MF_BYCOMMAND|MF_ENABLED },
\r
7904 { IDM_TimeControl, MF_BYCOMMAND|MF_ENABLED },
\r
7905 { IDM_RetractMove, MF_BYCOMMAND|MF_ENABLED },
\r
7906 { IDM_MoveNow, MF_BYCOMMAND|MF_ENABLED },
\r
7907 { IDM_Hint, MF_BYCOMMAND|MF_ENABLED },
\r
7908 { IDM_Book, MF_BYCOMMAND|MF_ENABLED },
\r
7912 Enables icsEnables[] = {
\r
7913 { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },
\r
7914 { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },
\r
7915 { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },
\r
7916 { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },
\r
7917 { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },
\r
7918 { IDM_Match, MF_BYCOMMAND|MF_GRAYED },
\r
7919 { IDM_MachineBoth, MF_BYCOMMAND|MF_GRAYED },
\r
7920 { IDM_AnalysisMode, MF_BYCOMMAND|MF_ENABLED },
\r
7921 { IDM_AnalyzeFile, MF_BYCOMMAND|MF_GRAYED },
\r
7922 { IDM_TimeControl, MF_BYCOMMAND|MF_GRAYED },
\r
7923 { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },
\r
7924 { IDM_Hint, MF_BYCOMMAND|MF_GRAYED },
\r
7925 { IDM_Book, MF_BYCOMMAND|MF_GRAYED },
\r
7926 { IDM_LoadProg1, MF_BYCOMMAND|MF_GRAYED },
\r
7927 { IDM_LoadProg2, MF_BYCOMMAND|MF_GRAYED },
\r
7928 { IDM_IcsOptions, MF_BYCOMMAND|MF_ENABLED },
\r
7929 { IDM_Engine1Options, MF_BYCOMMAND|MF_GRAYED },
\r
7930 { IDM_Engine2Options, MF_BYCOMMAND|MF_GRAYED },
\r
7931 { IDM_Annotate, MF_BYCOMMAND|MF_GRAYED },
\r
7932 { IDM_Tourney, MF_BYCOMMAND|MF_GRAYED },
\r
7937 Enables zippyEnables[] = {
\r
7938 { IDM_MoveNow, MF_BYCOMMAND|MF_ENABLED },
\r
7939 { IDM_Hint, MF_BYCOMMAND|MF_ENABLED },
\r
7940 { IDM_Book, MF_BYCOMMAND|MF_ENABLED },
\r
7941 { IDM_Engine1Options, MF_BYCOMMAND|MF_ENABLED },
\r
7946 Enables ncpEnables[] = {
\r
7947 { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },
\r
7948 { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },
\r
7949 { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },
\r
7950 { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },
\r
7951 { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },
\r
7952 { IDM_Match, MF_BYCOMMAND|MF_GRAYED },
\r
7953 { IDM_AnalysisMode, MF_BYCOMMAND|MF_GRAYED },
\r
7954 { IDM_AnalyzeFile, MF_BYCOMMAND|MF_GRAYED },
\r
7955 { IDM_IcsClient, MF_BYCOMMAND|MF_GRAYED },
\r
7956 { ACTION_POS, MF_BYPOSITION|MF_GRAYED },
\r
7957 { IDM_Revert, MF_BYCOMMAND|MF_GRAYED },
\r
7958 { IDM_Annotate, MF_BYCOMMAND|MF_GRAYED },
\r
7959 { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },
\r
7960 { IDM_RetractMove, MF_BYCOMMAND|MF_GRAYED },
\r
7961 { IDM_TimeControl, MF_BYCOMMAND|MF_GRAYED },
\r
7962 { IDM_Hint, MF_BYCOMMAND|MF_GRAYED },
\r
7963 { IDM_Book, MF_BYCOMMAND|MF_GRAYED },
\r
7964 { IDM_MachineBoth, MF_BYCOMMAND|MF_GRAYED },
\r
7965 { IDM_NewChat, MF_BYCOMMAND|MF_GRAYED },
\r
7966 { IDM_Engine1Options, MF_BYCOMMAND|MF_GRAYED },
\r
7967 { IDM_Engine2Options, MF_BYCOMMAND|MF_GRAYED },
\r
7968 { IDM_Sounds, MF_BYCOMMAND|MF_GRAYED },
\r
7972 Enables trainingOnEnables[] = {
\r
7973 { IDM_EditComment, MF_BYCOMMAND|MF_GRAYED },
\r
7974 { IDM_Comment, MF_BYCOMMAND|MF_GRAYED },
\r
7975 { IDM_Pause, MF_BYCOMMAND|MF_GRAYED },
\r
7976 { IDM_Forward, MF_BYCOMMAND|MF_GRAYED },
\r
7977 { IDM_Backward, MF_BYCOMMAND|MF_GRAYED },
\r
7978 { IDM_ToEnd, MF_BYCOMMAND|MF_GRAYED },
\r
7979 { IDM_ToStart, MF_BYCOMMAND|MF_GRAYED },
\r
7980 { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },
\r
7981 { IDM_TruncateGame, MF_BYCOMMAND|MF_GRAYED },
\r
7985 Enables trainingOffEnables[] = {
\r
7986 { IDM_EditComment, MF_BYCOMMAND|MF_ENABLED },
\r
7987 { IDM_Comment, MF_BYCOMMAND|MF_ENABLED },
\r
7988 { IDM_Pause, MF_BYCOMMAND|MF_ENABLED },
\r
7989 { IDM_Forward, MF_BYCOMMAND|MF_ENABLED },
\r
7990 { IDM_Backward, MF_BYCOMMAND|MF_ENABLED },
\r
7991 { IDM_ToEnd, MF_BYCOMMAND|MF_ENABLED },
\r
7992 { IDM_ToStart, MF_BYCOMMAND|MF_ENABLED },
\r
7993 { IDM_MoveNow, MF_BYCOMMAND|MF_ENABLED },
\r
7994 { IDM_TruncateGame, MF_BYCOMMAND|MF_ENABLED },
\r
7998 /* These modify either ncpEnables or gnuEnables */
\r
7999 Enables cmailEnables[] = {
\r
8000 { IDM_MailMove, MF_BYCOMMAND|MF_ENABLED },
\r
8001 { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_ENABLED },
\r
8002 { ACTION_POS, MF_BYPOSITION|MF_ENABLED },
\r
8003 { IDM_CallFlag, MF_BYCOMMAND|MF_GRAYED },
\r
8004 { IDM_Draw, MF_BYCOMMAND|MF_ENABLED },
\r
8005 { IDM_Adjourn, MF_BYCOMMAND|MF_GRAYED },
\r
8006 { IDM_Abort, MF_BYCOMMAND|MF_GRAYED },
\r
8010 Enables machineThinkingEnables[] = {
\r
8011 { IDM_LoadGame, MF_BYCOMMAND|MF_GRAYED },
\r
8012 { IDM_LoadNextGame, MF_BYCOMMAND|MF_GRAYED },
\r
8013 { IDM_LoadPrevGame, MF_BYCOMMAND|MF_GRAYED },
\r
8014 { IDM_ReloadGame, MF_BYCOMMAND|MF_GRAYED },
\r
8015 { IDM_PasteGame, MF_BYCOMMAND|MF_GRAYED },
\r
8016 { IDM_LoadPosition, MF_BYCOMMAND|MF_GRAYED },
\r
8017 { IDM_LoadNextPosition, MF_BYCOMMAND|MF_GRAYED },
\r
8018 { IDM_LoadPrevPosition, MF_BYCOMMAND|MF_GRAYED },
\r
8019 { IDM_ReloadPosition, MF_BYCOMMAND|MF_GRAYED },
\r
8020 { IDM_PastePosition, MF_BYCOMMAND|MF_GRAYED },
\r
8021 { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },
\r
8022 { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },
\r
8023 { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },
\r
8024 // { IDM_Match, MF_BYCOMMAND|MF_GRAYED },
\r
8025 { IDM_TypeInMove, MF_BYCOMMAND|MF_GRAYED },
\r
8026 { IDM_RetractMove, MF_BYCOMMAND|MF_GRAYED },
\r
8030 Enables userThinkingEnables[] = {
\r
8031 { IDM_LoadGame, MF_BYCOMMAND|MF_ENABLED },
\r
8032 { IDM_LoadNextGame, MF_BYCOMMAND|MF_ENABLED },
\r
8033 { IDM_LoadPrevGame, MF_BYCOMMAND|MF_ENABLED },
\r
8034 { IDM_ReloadGame, MF_BYCOMMAND|MF_ENABLED },
\r
8035 { IDM_PasteGame, MF_BYCOMMAND|MF_ENABLED },
\r
8036 { IDM_LoadPosition, MF_BYCOMMAND|MF_ENABLED },
\r
8037 { IDM_LoadNextPosition, MF_BYCOMMAND|MF_ENABLED },
\r
8038 { IDM_LoadPrevPosition, MF_BYCOMMAND|MF_ENABLED },
\r
8039 { IDM_ReloadPosition, MF_BYCOMMAND|MF_ENABLED },
\r
8040 { IDM_PastePosition, MF_BYCOMMAND|MF_ENABLED },
\r
8041 { IDM_MachineWhite, MF_BYCOMMAND|MF_ENABLED },
\r
8042 { IDM_MachineBlack, MF_BYCOMMAND|MF_ENABLED },
\r
8043 { IDM_TwoMachines, MF_BYCOMMAND|MF_ENABLED },
\r
8044 // { IDM_Match, MF_BYCOMMAND|MF_ENABLED },
\r
8045 { IDM_TypeInMove, MF_BYCOMMAND|MF_ENABLED },
\r
8046 { IDM_RetractMove, MF_BYCOMMAND|MF_ENABLED },
\r
8050 /*---------------------------------------------------------------------------*\
\r
8052 * Front-end interface functions exported by XBoard.
\r
8053 * Functions appear in same order as prototypes in frontend.h.
\r
8055 \*---------------------------------------------------------------------------*/
\r
8057 CheckMark(UINT item, int state)
\r
8059 if(item) CheckMenuItem(GetMenu(hwndMain), item, MF_BYCOMMAND|state);
\r
8065 static UINT prevChecked = 0;
\r
8066 static int prevPausing = 0;
\r
8069 if (pausing != prevPausing) {
\r
8070 prevPausing = pausing;
\r
8071 (void) CheckMenuItem(GetMenu(hwndMain), IDM_Pause,
\r
8072 MF_BYCOMMAND|(pausing ? MF_CHECKED : MF_UNCHECKED));
\r
8073 if (hwndPause) SetWindowText(hwndPause, pausing ? "C" : "P");
\r
8076 switch (gameMode) {
\r
8077 case BeginningOfGame:
\r
8078 if (appData.icsActive)
\r
8079 nowChecked = IDM_IcsClient;
\r
8080 else if (appData.noChessProgram)
\r
8081 nowChecked = IDM_EditGame;
\r
8083 nowChecked = IDM_MachineBlack;
\r
8085 case MachinePlaysBlack:
\r
8086 nowChecked = IDM_MachineBlack;
\r
8088 case MachinePlaysWhite:
\r
8089 nowChecked = IDM_MachineWhite;
\r
8091 case TwoMachinesPlay:
\r
8092 nowChecked = IDM_TwoMachines;
\r
8095 nowChecked = IDM_AnalysisMode;
\r
8098 nowChecked = IDM_AnalyzeFile;
\r
8101 nowChecked = IDM_EditGame;
\r
8103 case PlayFromGameFile:
\r
8104 nowChecked = IDM_LoadGame;
\r
8106 case EditPosition:
\r
8107 nowChecked = IDM_EditPosition;
\r
8110 nowChecked = IDM_Training;
\r
8112 case IcsPlayingWhite:
\r
8113 case IcsPlayingBlack:
\r
8114 case IcsObserving:
\r
8116 nowChecked = IDM_IcsClient;
\r
8123 CheckMark(prevChecked, MF_UNCHECKED);
\r
8124 CheckMark(nowChecked, MF_CHECKED);
\r
8125 CheckMark(IDM_Match, matchMode && matchGame < appData.matchGames ? MF_CHECKED : MF_UNCHECKED);
\r
8127 if (nowChecked == IDM_LoadGame || nowChecked == IDM_Training) {
\r
8128 (void) EnableMenuItem(GetMenu(hwndMain), IDM_Training,
\r
8129 MF_BYCOMMAND|MF_ENABLED);
\r
8131 (void) EnableMenuItem(GetMenu(hwndMain),
\r
8132 IDM_Training, MF_BYCOMMAND|MF_GRAYED);
\r
8135 prevChecked = nowChecked;
\r
8137 /* [DM] icsEngineAnalyze - Do a sceure check too */
\r
8138 if (appData.icsActive) {
\r
8139 if (appData.icsEngineAnalyze) {
\r
8140 CheckMark(IDM_AnalysisMode, MF_CHECKED);
\r
8142 CheckMark(IDM_AnalysisMode, MF_UNCHECKED);
\r
8145 DisplayLogos(); // [HGM] logos: mode change could have altered logos
\r
8151 HMENU hmenu = GetMenu(hwndMain);
\r
8152 SetMenuEnables(hmenu, icsEnables);
\r
8153 EnableMenuItem(GetSubMenu(hmenu, OPTIONS_POS), IDM_IcsOptions,
\r
8154 MF_BYCOMMAND|MF_ENABLED);
\r
8156 if (appData.zippyPlay) {
\r
8157 SetMenuEnables(hmenu, zippyEnables);
\r
8158 if (!appData.noChessProgram) /* [DM] icsEngineAnalyze */
\r
8159 (void) EnableMenuItem(GetMenu(hwndMain), IDM_AnalysisMode,
\r
8160 MF_BYCOMMAND|MF_ENABLED);
\r
8168 SetMenuEnables(GetMenu(hwndMain), gnuEnables);
\r
8174 HMENU hmenu = GetMenu(hwndMain);
\r
8175 SetMenuEnables(hmenu, ncpEnables);
\r
8176 DrawMenuBar(hwndMain);
\r
8182 SetMenuEnables(GetMenu(hwndMain), cmailEnables);
\r
8186 SetTrainingModeOn()
\r
8189 SetMenuEnables(GetMenu(hwndMain), trainingOnEnables);
\r
8190 for (i = 0; i < N_BUTTONS; i++) {
\r
8191 if (buttonDesc[i].hwnd != NULL)
\r
8192 EnableWindow(buttonDesc[i].hwnd, FALSE);
\r
8197 VOID SetTrainingModeOff()
\r
8200 SetMenuEnables(GetMenu(hwndMain), trainingOffEnables);
\r
8201 for (i = 0; i < N_BUTTONS; i++) {
\r
8202 if (buttonDesc[i].hwnd != NULL)
\r
8203 EnableWindow(buttonDesc[i].hwnd, TRUE);
\r
8209 SetUserThinkingEnables()
\r
8211 SetMenuEnables(GetMenu(hwndMain), userThinkingEnables);
\r
8215 SetMachineThinkingEnables()
\r
8217 HMENU hMenu = GetMenu(hwndMain);
\r
8218 int flags = MF_BYCOMMAND|MF_ENABLED;
\r
8220 SetMenuEnables(hMenu, machineThinkingEnables);
\r
8222 if (gameMode == MachinePlaysBlack) {
\r
8223 (void)EnableMenuItem(hMenu, IDM_MachineBlack, flags);
\r
8224 } else if (gameMode == MachinePlaysWhite) {
\r
8225 (void)EnableMenuItem(hMenu, IDM_MachineWhite, flags);
\r
8226 } else if (gameMode == TwoMachinesPlay) {
\r
8227 (void)EnableMenuItem(hMenu, matchMode ? IDM_Match : IDM_TwoMachines, flags); // [HGM] match
\r
8233 DisplayTitle(char *str)
\r
8235 char title[MSG_SIZ], *host;
\r
8236 if (str[0] != NULLCHAR) {
\r
8237 safeStrCpy(title, str, sizeof(title)/sizeof(title[0]) );
\r
8238 } else if (appData.icsActive) {
\r
8239 if (appData.icsCommPort[0] != NULLCHAR)
\r
8242 host = appData.icsHost;
\r
8243 snprintf(title, MSG_SIZ, "%s: %s", szTitle, host);
\r
8244 } else if (appData.noChessProgram) {
\r
8245 safeStrCpy(title, szTitle, sizeof(title)/sizeof(title[0]) );
\r
8247 safeStrCpy(title, szTitle, sizeof(title)/sizeof(title[0]) );
\r
8248 strcat(title, ": ");
\r
8249 strcat(title, first.tidy);
\r
8251 SetWindowText(hwndMain, title);
\r
8256 DisplayMessage(char *str1, char *str2)
\r
8260 int remain = MESSAGE_TEXT_MAX - 1;
\r
8263 moveErrorMessageUp = FALSE; /* turned on later by caller if needed */
\r
8264 messageText[0] = NULLCHAR;
\r
8266 len = strlen(str1);
\r
8267 if (len > remain) len = remain;
\r
8268 strncpy(messageText, str1, len);
\r
8269 messageText[len] = NULLCHAR;
\r
8272 if (*str2 && remain >= 2) {
\r
8274 strcat(messageText, " ");
\r
8277 len = strlen(str2);
\r
8278 if (len > remain) len = remain;
\r
8279 strncat(messageText, str2, len);
\r
8281 messageText[MESSAGE_TEXT_MAX - 1] = NULLCHAR;
\r
8282 safeStrCpy(lastMsg, messageText, MSG_SIZ);
\r
8284 if (hwndMain == NULL || IsIconic(hwndMain)) return;
\r
8288 hdc = GetDC(hwndMain);
\r
8289 oldFont = SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);
\r
8290 ExtTextOut(hdc, messageRect.left, messageRect.top, ETO_CLIPPED|ETO_OPAQUE,
\r
8291 &messageRect, messageText, strlen(messageText), NULL);
\r
8292 (void) SelectObject(hdc, oldFont);
\r
8293 (void) ReleaseDC(hwndMain, hdc);
\r
8297 DisplayError(char *str, int error)
\r
8299 char buf[MSG_SIZ*2], buf2[MSG_SIZ];
\r
8303 safeStrCpy(buf, str, sizeof(buf)/sizeof(buf[0]) );
\r
8305 len = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,
\r
8306 NULL, error, LANG_NEUTRAL,
\r
8307 (LPSTR) buf2, MSG_SIZ, NULL);
\r
8309 snprintf(buf, 2*MSG_SIZ, "%s:\n%s", str, buf2);
\r
8311 ErrorMap *em = errmap;
\r
8312 while (em->err != 0 && em->err != error) em++;
\r
8313 if (em->err != 0) {
\r
8314 snprintf(buf, 2*MSG_SIZ, "%s:\n%s", str, em->msg);
\r
8316 snprintf(buf, 2*MSG_SIZ, "%s:\nError code %d", str, error);
\r
8321 ErrorPopUp(_("Error"), buf);
\r
8326 DisplayMoveError(char *str)
\r
8328 fromX = fromY = -1;
\r
8329 ClearHighlights();
\r
8330 DrawPosition(FALSE, NULL);
\r
8331 if (appData.popupMoveErrors) {
\r
8332 ErrorPopUp(_("Error"), str);
\r
8334 DisplayMessage(str, "");
\r
8335 moveErrorMessageUp = TRUE;
\r
8340 DisplayFatalError(char *str, int error, int exitStatus)
\r
8342 char buf[2*MSG_SIZ], buf2[MSG_SIZ];
\r
8344 char *label = exitStatus ? _("Fatal Error") : _("Exiting");
\r
8347 len = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,
\r
8348 NULL, error, LANG_NEUTRAL,
\r
8349 (LPSTR) buf2, MSG_SIZ, NULL);
\r
8351 snprintf(buf, 2*MSG_SIZ, "%s:\n%s", str, buf2);
\r
8353 ErrorMap *em = errmap;
\r
8354 while (em->err != 0 && em->err != error) em++;
\r
8355 if (em->err != 0) {
\r
8356 snprintf(buf, 2*MSG_SIZ, "%s:\n%s", str, em->msg);
\r
8358 snprintf(buf, 2*MSG_SIZ, "%s:\nError code %d", str, error);
\r
8363 if (appData.debugMode) {
\r
8364 fprintf(debugFP, "%s: %s\n", label, str);
\r
8366 if (appData.popupExitMessage) {
\r
8367 (void) MessageBox(hwndMain, str, label, MB_OK|
\r
8368 (exitStatus ? MB_ICONSTOP : MB_ICONINFORMATION));
\r
8370 ExitEvent(exitStatus);
\r
8375 DisplayInformation(char *str)
\r
8377 (void) MessageBox(hwndMain, str, _("Information"), MB_OK|MB_ICONINFORMATION);
\r
8382 DisplayNote(char *str)
\r
8384 ErrorPopUp(_("Note"), str);
\r
8389 char *title, *question, *replyPrefix;
\r
8394 QuestionDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
8396 static QuestionParams *qp;
\r
8397 char reply[MSG_SIZ];
\r
8400 switch (message) {
\r
8401 case WM_INITDIALOG:
\r
8402 qp = (QuestionParams *) lParam;
\r
8403 CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));
\r
8404 Translate(hDlg, DLG_Question);
\r
8405 SetWindowText(hDlg, qp->title);
\r
8406 SetDlgItemText(hDlg, OPT_QuestionText, qp->question);
\r
8407 SetFocus(GetDlgItem(hDlg, OPT_QuestionInput));
\r
8411 switch (LOWORD(wParam)) {
\r
8413 safeStrCpy(reply, qp->replyPrefix, sizeof(reply)/sizeof(reply[0]) );
\r
8414 if (*reply) strcat(reply, " ");
\r
8415 len = strlen(reply);
\r
8416 GetDlgItemText(hDlg, OPT_QuestionInput, reply + len, sizeof(reply) - len);
\r
8417 strcat(reply, "\n");
\r
8418 OutputToProcess(qp->pr, reply, strlen(reply), &err);
\r
8419 EndDialog(hDlg, TRUE);
\r
8420 if (err) DisplayFatalError(_("Error writing to chess program"), err, 1);
\r
8423 EndDialog(hDlg, FALSE);
\r
8434 AskQuestion(char* title, char *question, char *replyPrefix, ProcRef pr)
\r
8436 QuestionParams qp;
\r
8440 qp.question = question;
\r
8441 qp.replyPrefix = replyPrefix;
\r
8443 lpProc = MakeProcInstance((FARPROC)QuestionDialog, hInst);
\r
8444 DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_Question),
\r
8445 hwndMain, (DLGPROC)lpProc, (LPARAM)&qp);
\r
8446 FreeProcInstance(lpProc);
\r
8449 /* [AS] Pick FRC position */
\r
8450 LRESULT CALLBACK NewGameFRC_Proc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
8452 static int * lpIndexFRC;
\r
8458 case WM_INITDIALOG:
\r
8459 lpIndexFRC = (int *) lParam;
\r
8461 CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));
\r
8462 Translate(hDlg, DLG_NewGameFRC);
\r
8464 SendDlgItemMessage( hDlg, IDC_NFG_Edit, EM_SETLIMITTEXT, sizeof(buf)-1, 0 );
\r
8465 SetDlgItemInt( hDlg, IDC_NFG_Edit, *lpIndexFRC, TRUE );
\r
8466 SendDlgItemMessage( hDlg, IDC_NFG_Edit, EM_SETSEL, 0, -1 );
\r
8467 SetFocus(GetDlgItem(hDlg, IDC_NFG_Edit));
\r
8472 switch( LOWORD(wParam) ) {
\r
8474 *lpIndexFRC = GetDlgItemInt(hDlg, IDC_NFG_Edit, &index_is_ok, TRUE );
\r
8475 EndDialog( hDlg, 0 );
\r
8476 shuffleOpenings = TRUE; /* [HGM] shuffle: switch shuffling on for as long as we stay in current variant */
\r
8479 EndDialog( hDlg, 1 );
\r
8481 case IDC_NFG_Edit:
\r
8482 if( HIWORD(wParam) == EN_CHANGE ) {
\r
8483 GetDlgItemInt(hDlg, IDC_NFG_Edit, &index_is_ok, TRUE );
\r
8485 EnableWindow( GetDlgItem(hDlg, IDOK), index_is_ok );
\r
8488 case IDC_NFG_Random:
\r
8489 snprintf( buf, sizeof(buf)/sizeof(buf[0]), "%d", myrandom() ); /* [HGM] shuffle: no longer limit to 960 */
\r
8490 SetDlgItemText(hDlg, IDC_NFG_Edit, buf );
\r
8503 int index = appData.defaultFrcPosition;
\r
8504 FARPROC lpProc = MakeProcInstance( (FARPROC) NewGameFRC_Proc, hInst );
\r
8506 result = DialogBoxParam( hInst, MAKEINTRESOURCE(DLG_NewGameFRC), hwndMain, (DLGPROC)lpProc, (LPARAM)&index );
\r
8508 if( result == 0 ) {
\r
8509 appData.defaultFrcPosition = index;
\r
8515 /* [AS] Game list options. Refactored by HGM */
\r
8517 HWND gameListOptionsDialog;
\r
8519 // low-level front-end: clear text edit / list widget
\r
8523 SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_RESETCONTENT, 0, 0 );
\r
8526 // low-level front-end: clear text edit / list widget
\r
8528 GLT_DeSelectList()
\r
8530 SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_SETCURSEL, 0, 0 );
\r
8533 // low-level front-end: append line to text edit / list widget
\r
8535 GLT_AddToList( char *name )
\r
8538 SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_ADDSTRING, 0, (LPARAM) name );
\r
8542 // low-level front-end: get line from text edit / list widget
\r
8544 GLT_GetFromList( int index, char *name )
\r
8547 if( SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_GETTEXT, index, (LPARAM) name ) != LB_ERR )
\r
8553 void GLT_MoveSelection( HWND hDlg, int delta )
\r
8555 int idx1 = (int) SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETCURSEL, 0, 0 );
\r
8556 int idx2 = idx1 + delta;
\r
8557 int count = (int) SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETCOUNT, 0, 0 );
\r
8559 if( idx1 >=0 && idx1 < count && idx2 >= 0 && idx2 < count ) {
\r
8562 SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETTEXT, idx1, (LPARAM) buf );
\r
8563 SendDlgItemMessage( hDlg, IDC_GameListTags, LB_DELETESTRING, idx1, 0 );
\r
8564 SendDlgItemMessage( hDlg, IDC_GameListTags, LB_INSERTSTRING, idx2, (LPARAM) buf );
\r
8565 SendDlgItemMessage( hDlg, IDC_GameListTags, LB_SETCURSEL, idx2, 0 );
\r
8569 LRESULT CALLBACK GameListOptions_Proc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
8573 case WM_INITDIALOG:
\r
8574 gameListOptionsDialog = hDlg; // [HGM] pass through global to keep out off back-end
\r
8576 CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));
\r
8577 Translate(hDlg, DLG_GameListOptions);
\r
8579 /* Initialize list */
\r
8580 GLT_TagsToList( lpUserGLT );
\r
8582 SetFocus( GetDlgItem(hDlg, IDC_GameListTags) );
\r
8587 switch( LOWORD(wParam) ) {
\r
8590 EndDialog( hDlg, 0 );
\r
8593 EndDialog( hDlg, 1 );
\r
8596 case IDC_GLT_Default:
\r
8597 GLT_TagsToList( GLT_DEFAULT_TAGS );
\r
8600 case IDC_GLT_Restore:
\r
8601 GLT_TagsToList( appData.gameListTags );
\r
8605 GLT_MoveSelection( hDlg, -1 );
\r
8608 case IDC_GLT_Down:
\r
8609 GLT_MoveSelection( hDlg, +1 );
\r
8619 int GameListOptions()
\r
8622 FARPROC lpProc = MakeProcInstance( (FARPROC) GameListOptions_Proc, hInst );
\r
8624 safeStrCpy( lpUserGLT, appData.gameListTags ,LPUSERGLT_SIZE );
\r
8626 result = DialogBoxParam( hInst, MAKEINTRESOURCE(DLG_GameListOptions), hwndMain, (DLGPROC)lpProc, (LPARAM)lpUserGLT );
\r
8628 if( result == 0 ) {
\r
8629 /* [AS] Memory leak here! */
\r
8630 appData.gameListTags = strdup( lpUserGLT );
\r
8637 DisplayIcsInteractionTitle(char *str)
\r
8639 char consoleTitle[MSG_SIZ];
\r
8641 snprintf(consoleTitle, MSG_SIZ, "%s: %s", szConsoleTitle, str);
\r
8642 SetWindowText(hwndConsole, consoleTitle);
\r
8644 if(appData.chatBoxes) { // [HGM] chat: open chat boxes
\r
8645 char buf[MSG_SIZ], *p = buf, *q;
\r
8646 safeStrCpy(buf, appData.chatBoxes, sizeof(buf)/sizeof(buf[0]) );
\r
8648 q = strchr(p, ';');
\r
8650 if(*p) ChatPopUp(p);
\r
8654 SetActiveWindow(hwndMain);
\r
8658 DrawPosition(int fullRedraw, Board board)
\r
8660 HDCDrawPosition(NULL, (BOOLEAN) fullRedraw, board);
\r
8663 void NotifyFrontendLogin()
\r
8666 UpdateICSWidth(GetDlgItem(hwndConsole, OPT_ConsoleText));
\r
8672 fromX = fromY = -1;
\r
8673 if (dragInfo.pos.x != -1 || dragInfo.pos.y != -1) {
\r
8674 dragInfo.pos.x = dragInfo.pos.y = -1;
\r
8675 dragInfo.pos.x = dragInfo.pos.y = -1;
\r
8676 dragInfo.lastpos = dragInfo.pos;
\r
8677 dragInfo.start.x = dragInfo.start.y = -1;
\r
8678 dragInfo.from = dragInfo.start;
\r
8680 DrawPosition(TRUE, NULL);
\r
8687 CommentPopUp(char *title, char *str)
\r
8689 HWND hwnd = GetActiveWindow();
\r
8690 EitherCommentPopUp(currentMove, title, str, FALSE); // [HGM] vari: fake move index, rather than 0
\r
8692 SetActiveWindow(hwnd);
\r
8696 CommentPopDown(void)
\r
8698 CheckMenuItem(GetMenu(hwndMain), IDM_Comment, MF_UNCHECKED);
\r
8699 if (commentDialog) {
\r
8700 ShowWindow(commentDialog, SW_HIDE);
\r
8702 commentUp = FALSE;
\r
8706 EditCommentPopUp(int index, char *title, char *str)
\r
8708 EitherCommentPopUp(index, title, str, TRUE);
\r
8715 MyPlaySound(&sounds[(int)SoundMove]);
\r
8718 VOID PlayIcsWinSound()
\r
8720 MyPlaySound(&sounds[(int)SoundIcsWin]);
\r
8723 VOID PlayIcsLossSound()
\r
8725 MyPlaySound(&sounds[(int)SoundIcsLoss]);
\r
8728 VOID PlayIcsDrawSound()
\r
8730 MyPlaySound(&sounds[(int)SoundIcsDraw]);
\r
8733 VOID PlayIcsUnfinishedSound()
\r
8735 MyPlaySound(&sounds[(int)SoundIcsUnfinished]);
\r
8741 MyPlaySound(&sounds[(int)SoundAlarm]);
\r
8747 MyPlaySound(&textAttribs[ColorTell].sound);
\r
8755 consoleEcho = TRUE;
\r
8756 hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
8757 SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&consoleCF);
\r
8758 SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);
\r
8767 consoleEcho = FALSE;
\r
8768 hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
8769 /* This works OK: set text and background both to the same color */
\r
8771 cf.crTextColor = COLOR_ECHOOFF;
\r
8772 SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf);
\r
8773 SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, cf.crTextColor);
\r
8776 /* No Raw()...? */
\r
8778 void Colorize(ColorClass cc, int continuation)
\r
8780 currentColorClass = cc;
\r
8781 consoleCF.dwMask = CFM_COLOR|CFM_BOLD|CFM_ITALIC|CFM_UNDERLINE|CFM_STRIKEOUT;
\r
8782 consoleCF.crTextColor = textAttribs[cc].color;
\r
8783 consoleCF.dwEffects = textAttribs[cc].effects;
\r
8784 if (!continuation) MyPlaySound(&textAttribs[cc].sound);
\r
8790 static char buf[MSG_SIZ];
\r
8791 DWORD bufsiz = MSG_SIZ;
\r
8793 if(appData.userName != NULL && appData.userName[0] != 0) {
\r
8794 return appData.userName; /* [HGM] username: prefer name selected by user over his system login */
\r
8796 if (!GetUserName(buf, &bufsiz)) {
\r
8797 /*DisplayError("Error getting user name", GetLastError());*/
\r
8798 safeStrCpy(buf, _("User"), sizeof(buf)/sizeof(buf[0]) );
\r
8806 static char buf[MSG_SIZ];
\r
8807 DWORD bufsiz = MSG_SIZ;
\r
8809 if (!GetComputerName(buf, &bufsiz)) {
\r
8810 /*DisplayError("Error getting host name", GetLastError());*/
\r
8811 safeStrCpy(buf, _("Unknown"), sizeof(buf)/sizeof(buf[0]) );
\r
8818 ClockTimerRunning()
\r
8820 return clockTimerEvent != 0;
\r
8826 if (clockTimerEvent == 0) return FALSE;
\r
8827 KillTimer(hwndMain, clockTimerEvent);
\r
8828 clockTimerEvent = 0;
\r
8833 StartClockTimer(long millisec)
\r
8835 clockTimerEvent = SetTimer(hwndMain, (UINT) CLOCK_TIMER_ID,
\r
8836 (UINT) millisec, NULL);
\r
8840 DisplayWhiteClock(long timeRemaining, int highlight)
\r
8843 char *flag = whiteFlag && gameMode == TwoMachinesPlay ? "(!)" : "";
\r
8845 if(appData.noGUI) return;
\r
8846 hdc = GetDC(hwndMain);
\r
8847 if (!IsIconic(hwndMain)) {
\r
8848 DisplayAClock(hdc, timeRemaining, highlight,
\r
8849 flipClock ? &blackRect : &whiteRect, _("White"), flag);
\r
8851 if (highlight && iconCurrent == iconBlack) {
\r
8852 iconCurrent = iconWhite;
\r
8853 PostMessage(hwndMain, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);
\r
8854 if (IsIconic(hwndMain)) {
\r
8855 DrawIcon(hdc, 2, 2, iconCurrent);
\r
8858 (void) ReleaseDC(hwndMain, hdc);
\r
8860 PostMessage(hwndConsole, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);
\r
8864 DisplayBlackClock(long timeRemaining, int highlight)
\r
8867 char *flag = blackFlag && gameMode == TwoMachinesPlay ? "(!)" : "";
\r
8869 if(appData.noGUI) return;
\r
8870 hdc = GetDC(hwndMain);
\r
8871 if (!IsIconic(hwndMain)) {
\r
8872 DisplayAClock(hdc, timeRemaining, highlight,
\r
8873 flipClock ? &whiteRect : &blackRect, _("Black"), flag);
\r
8875 if (highlight && iconCurrent == iconWhite) {
\r
8876 iconCurrent = iconBlack;
\r
8877 PostMessage(hwndMain, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);
\r
8878 if (IsIconic(hwndMain)) {
\r
8879 DrawIcon(hdc, 2, 2, iconCurrent);
\r
8882 (void) ReleaseDC(hwndMain, hdc);
\r
8884 PostMessage(hwndConsole, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);
\r
8889 LoadGameTimerRunning()
\r
8891 return loadGameTimerEvent != 0;
\r
8895 StopLoadGameTimer()
\r
8897 if (loadGameTimerEvent == 0) return FALSE;
\r
8898 KillTimer(hwndMain, loadGameTimerEvent);
\r
8899 loadGameTimerEvent = 0;
\r
8904 StartLoadGameTimer(long millisec)
\r
8906 loadGameTimerEvent = SetTimer(hwndMain, (UINT) LOAD_GAME_TIMER_ID,
\r
8907 (UINT) millisec, NULL);
\r
8915 char fileTitle[MSG_SIZ];
\r
8917 defName = DefaultFileName(appData.oldSaveStyle ? "gam" : "pgn");
\r
8918 f = OpenFileDialog(hwndMain, "a", defName,
\r
8919 appData.oldSaveStyle ? "gam" : "pgn",
\r
8921 _("Save Game to File"), NULL, fileTitle, NULL);
\r
8923 SaveGame(f, 0, "");
\r
8930 ScheduleDelayedEvent(DelayedEventCallback cb, long millisec)
\r
8932 if (delayedTimerEvent != 0) {
\r
8933 if (appData.debugMode && cb != delayedTimerCallback) { // [HGM] alive: not too much debug
\r
8934 fprintf(debugFP, "ScheduleDelayedEvent: event already scheduled\n");
\r
8936 KillTimer(hwndMain, delayedTimerEvent);
\r
8937 delayedTimerEvent = 0;
\r
8938 if(delayedTimerCallback != cb) // [HGM] alive: do not "flush" same event, just postpone it
\r
8939 delayedTimerCallback();
\r
8941 delayedTimerCallback = cb;
\r
8942 delayedTimerEvent = SetTimer(hwndMain, (UINT) DELAYED_TIMER_ID,
\r
8943 (UINT) millisec, NULL);
\r
8946 DelayedEventCallback
\r
8949 if (delayedTimerEvent) {
\r
8950 return delayedTimerCallback;
\r
8957 CancelDelayedEvent()
\r
8959 if (delayedTimerEvent) {
\r
8960 KillTimer(hwndMain, delayedTimerEvent);
\r
8961 delayedTimerEvent = 0;
\r
8965 DWORD GetWin32Priority(int nice)
\r
8966 { // [HGM] nice: translate Unix nice() value to indows priority class. (Code stolen from Polyglot 1.4w11)
\r
8968 REALTIME_PRIORITY_CLASS 0x00000100
\r
8969 HIGH_PRIORITY_CLASS 0x00000080
\r
8970 ABOVE_NORMAL_PRIORITY_CLASS 0x00008000
\r
8971 NORMAL_PRIORITY_CLASS 0x00000020
\r
8972 BELOW_NORMAL_PRIORITY_CLASS 0x00004000
\r
8973 IDLE_PRIORITY_CLASS 0x00000040
\r
8975 if (nice < -15) return 0x00000080;
\r
8976 if (nice < 0) return 0x00008000;
\r
8977 if (nice == 0) return 0x00000020;
\r
8978 if (nice < 15) return 0x00004000;
\r
8979 return 0x00000040;
\r
8982 void RunCommand(char *cmdLine)
\r
8984 /* Now create the child process. */
\r
8985 STARTUPINFO siStartInfo;
\r
8986 PROCESS_INFORMATION piProcInfo;
\r
8988 siStartInfo.cb = sizeof(STARTUPINFO);
\r
8989 siStartInfo.lpReserved = NULL;
\r
8990 siStartInfo.lpDesktop = NULL;
\r
8991 siStartInfo.lpTitle = NULL;
\r
8992 siStartInfo.dwFlags = STARTF_USESTDHANDLES;
\r
8993 siStartInfo.cbReserved2 = 0;
\r
8994 siStartInfo.lpReserved2 = NULL;
\r
8995 siStartInfo.hStdInput = NULL;
\r
8996 siStartInfo.hStdOutput = NULL;
\r
8997 siStartInfo.hStdError = NULL;
\r
8999 CreateProcess(NULL,
\r
9000 cmdLine, /* command line */
\r
9001 NULL, /* process security attributes */
\r
9002 NULL, /* primary thread security attrs */
\r
9003 TRUE, /* handles are inherited */
\r
9004 DETACHED_PROCESS|CREATE_NEW_PROCESS_GROUP,
\r
9005 NULL, /* use parent's environment */
\r
9007 &siStartInfo, /* STARTUPINFO pointer */
\r
9008 &piProcInfo); /* receives PROCESS_INFORMATION */
\r
9010 CloseHandle(piProcInfo.hThread);
\r
9013 /* Start a child process running the given program.
\r
9014 The process's standard output can be read from "from", and its
\r
9015 standard input can be written to "to".
\r
9016 Exit with fatal error if anything goes wrong.
\r
9017 Returns an opaque pointer that can be used to destroy the process
\r
9021 StartChildProcess(char *cmdLine, char *dir, ProcRef *pr)
\r
9023 #define BUFSIZE 4096
\r
9025 HANDLE hChildStdinRd, hChildStdinWr,
\r
9026 hChildStdoutRd, hChildStdoutWr;
\r
9027 HANDLE hChildStdinWrDup, hChildStdoutRdDup;
\r
9028 SECURITY_ATTRIBUTES saAttr;
\r
9030 PROCESS_INFORMATION piProcInfo;
\r
9031 STARTUPINFO siStartInfo;
\r
9033 char buf[MSG_SIZ];
\r
9036 if (appData.debugMode) {
\r
9037 fprintf(debugFP, "StartChildProcess (dir=\"%s\") %s\n", dir, cmdLine);
\r
9042 /* Set the bInheritHandle flag so pipe handles are inherited. */
\r
9043 saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);
\r
9044 saAttr.bInheritHandle = TRUE;
\r
9045 saAttr.lpSecurityDescriptor = NULL;
\r
9048 * The steps for redirecting child's STDOUT:
\r
9049 * 1. Create anonymous pipe to be STDOUT for child.
\r
9050 * 2. Create a noninheritable duplicate of read handle,
\r
9051 * and close the inheritable read handle.
\r
9054 /* Create a pipe for the child's STDOUT. */
\r
9055 if (! CreatePipe(&hChildStdoutRd, &hChildStdoutWr, &saAttr, 0)) {
\r
9056 return GetLastError();
\r
9059 /* Duplicate the read handle to the pipe, so it is not inherited. */
\r
9060 fSuccess = DuplicateHandle(GetCurrentProcess(), hChildStdoutRd,
\r
9061 GetCurrentProcess(), &hChildStdoutRdDup, 0,
\r
9062 FALSE, /* not inherited */
\r
9063 DUPLICATE_SAME_ACCESS);
\r
9065 return GetLastError();
\r
9067 CloseHandle(hChildStdoutRd);
\r
9070 * The steps for redirecting child's STDIN:
\r
9071 * 1. Create anonymous pipe to be STDIN for child.
\r
9072 * 2. Create a noninheritable duplicate of write handle,
\r
9073 * and close the inheritable write handle.
\r
9076 /* Create a pipe for the child's STDIN. */
\r
9077 if (! CreatePipe(&hChildStdinRd, &hChildStdinWr, &saAttr, 0)) {
\r
9078 return GetLastError();
\r
9081 /* Duplicate the write handle to the pipe, so it is not inherited. */
\r
9082 fSuccess = DuplicateHandle(GetCurrentProcess(), hChildStdinWr,
\r
9083 GetCurrentProcess(), &hChildStdinWrDup, 0,
\r
9084 FALSE, /* not inherited */
\r
9085 DUPLICATE_SAME_ACCESS);
\r
9087 return GetLastError();
\r
9089 CloseHandle(hChildStdinWr);
\r
9091 /* Arrange to (1) look in dir for the child .exe file, and
\r
9092 * (2) have dir be the child's working directory. Interpret
\r
9093 * dir relative to the directory WinBoard loaded from. */
\r
9094 GetCurrentDirectory(MSG_SIZ, buf);
\r
9095 SetCurrentDirectory(installDir);
\r
9096 SetCurrentDirectory(dir);
\r
9098 /* Now create the child process. */
\r
9100 siStartInfo.cb = sizeof(STARTUPINFO);
\r
9101 siStartInfo.lpReserved = NULL;
\r
9102 siStartInfo.lpDesktop = NULL;
\r
9103 siStartInfo.lpTitle = NULL;
\r
9104 siStartInfo.dwFlags = STARTF_USESTDHANDLES;
\r
9105 siStartInfo.cbReserved2 = 0;
\r
9106 siStartInfo.lpReserved2 = NULL;
\r
9107 siStartInfo.hStdInput = hChildStdinRd;
\r
9108 siStartInfo.hStdOutput = hChildStdoutWr;
\r
9109 siStartInfo.hStdError = hChildStdoutWr;
\r
9111 fSuccess = CreateProcess(NULL,
\r
9112 cmdLine, /* command line */
\r
9113 NULL, /* process security attributes */
\r
9114 NULL, /* primary thread security attrs */
\r
9115 TRUE, /* handles are inherited */
\r
9116 DETACHED_PROCESS|CREATE_NEW_PROCESS_GROUP,
\r
9117 NULL, /* use parent's environment */
\r
9119 &siStartInfo, /* STARTUPINFO pointer */
\r
9120 &piProcInfo); /* receives PROCESS_INFORMATION */
\r
9122 err = GetLastError();
\r
9123 SetCurrentDirectory(buf); /* return to prev directory */
\r
9128 if (appData.niceEngines){ // [HGM] nice: adjust engine proc priority
\r
9129 if(appData.debugMode) fprintf(debugFP, "nice engine proc to %d\n", appData.niceEngines);
\r
9130 SetPriorityClass(piProcInfo.hProcess, GetWin32Priority(appData.niceEngines));
\r
9133 /* Close the handles we don't need in the parent */
\r
9134 CloseHandle(piProcInfo.hThread);
\r
9135 CloseHandle(hChildStdinRd);
\r
9136 CloseHandle(hChildStdoutWr);
\r
9138 /* Prepare return value */
\r
9139 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
\r
9140 cp->kind = CPReal;
\r
9141 cp->hProcess = piProcInfo.hProcess;
\r
9142 cp->pid = piProcInfo.dwProcessId;
\r
9143 cp->hFrom = hChildStdoutRdDup;
\r
9144 cp->hTo = hChildStdinWrDup;
\r
9146 *pr = (void *) cp;
\r
9148 /* Klaus Friedel says that this Sleep solves a problem under Windows
\r
9149 2000 where engines sometimes don't see the initial command(s)
\r
9150 from WinBoard and hang. I don't understand how that can happen,
\r
9151 but the Sleep is harmless, so I've put it in. Others have also
\r
9152 reported what may be the same problem, so hopefully this will fix
\r
9153 it for them too. */
\r
9161 DestroyChildProcess(ProcRef pr, int/*boolean*/ signal)
\r
9163 ChildProc *cp; int result;
\r
9165 cp = (ChildProc *) pr;
\r
9166 if (cp == NULL) return;
\r
9168 switch (cp->kind) {
\r
9170 /* TerminateProcess is considered harmful, so... */
\r
9171 CloseHandle(cp->hTo); /* Closing this will give the child an EOF and hopefully kill it */
\r
9172 if (cp->hFrom) CloseHandle(cp->hFrom); /* if NULL, InputThread will close it */
\r
9173 /* The following doesn't work because the chess program
\r
9174 doesn't "have the same console" as WinBoard. Maybe
\r
9175 we could arrange for this even though neither WinBoard
\r
9176 nor the chess program uses a console for stdio? */
\r
9177 /*!!if (signal) GenerateConsoleCtrlEvent(CTRL_BREAK_EVENT, cp->pid);*/
\r
9179 /* [AS] Special termination modes for misbehaving programs... */
\r
9180 if( signal == 9 ) {
\r
9181 result = TerminateProcess( cp->hProcess, 0 );
\r
9183 if ( appData.debugMode) {
\r
9184 fprintf( debugFP, "Terminating process %lu, result=%d\n", cp->pid, result );
\r
9187 else if( signal == 10 ) {
\r
9188 DWORD dw = WaitForSingleObject( cp->hProcess, 3*1000 ); // Wait 3 seconds at most
\r
9190 if( dw != WAIT_OBJECT_0 ) {
\r
9191 result = TerminateProcess( cp->hProcess, 0 );
\r
9193 if ( appData.debugMode) {
\r
9194 fprintf( debugFP, "Process %lu still alive after timeout, killing... result=%d\n", cp->pid, result );
\r
9200 CloseHandle(cp->hProcess);
\r
9204 if (cp->hFrom) CloseHandle(cp->hFrom);
\r
9208 closesocket(cp->sock);
\r
9213 if (signal) send(cp->sock2, "\017", 1, 0); /* 017 = 15 = SIGTERM */
\r
9214 closesocket(cp->sock);
\r
9215 closesocket(cp->sock2);
\r
9223 InterruptChildProcess(ProcRef pr)
\r
9227 cp = (ChildProc *) pr;
\r
9228 if (cp == NULL) return;
\r
9229 switch (cp->kind) {
\r
9231 /* The following doesn't work because the chess program
\r
9232 doesn't "have the same console" as WinBoard. Maybe
\r
9233 we could arrange for this even though neither WinBoard
\r
9234 nor the chess program uses a console for stdio */
\r
9235 /*!!GenerateConsoleCtrlEvent(CTRL_C_EVENT, cp->pid);*/
\r
9240 /* Can't interrupt */
\r
9244 send(cp->sock2, "\002", 1, 0); /* 2 = SIGINT */
\r
9251 OpenTelnet(char *host, char *port, ProcRef *pr)
\r
9253 char cmdLine[MSG_SIZ];
\r
9255 if (port[0] == NULLCHAR) {
\r
9256 snprintf(cmdLine, MSG_SIZ, "%s %s", appData.telnetProgram, host);
\r
9258 snprintf(cmdLine, MSG_SIZ, "%s %s %s", appData.telnetProgram, host, port);
\r
9260 return StartChildProcess(cmdLine, "", pr);
\r
9264 /* Code to open TCP sockets */
\r
9267 OpenTCP(char *host, char *port, ProcRef *pr)
\r
9273 struct sockaddr_in sa, mysa;
\r
9274 struct hostent FAR *hp;
\r
9275 unsigned short uport;
\r
9276 WORD wVersionRequested;
\r
9279 /* Initialize socket DLL */
\r
9280 wVersionRequested = MAKEWORD(1, 1);
\r
9281 err = WSAStartup(wVersionRequested, &wsaData);
\r
9282 if (err != 0) return err;
\r
9285 if ((s = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {
\r
9286 err = WSAGetLastError();
\r
9291 /* Bind local address using (mostly) don't-care values.
\r
9293 memset((char *) &mysa, 0, sizeof(struct sockaddr_in));
\r
9294 mysa.sin_family = AF_INET;
\r
9295 mysa.sin_addr.s_addr = INADDR_ANY;
\r
9296 uport = (unsigned short) 0;
\r
9297 mysa.sin_port = htons(uport);
\r
9298 if (bind(s, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))
\r
9299 == SOCKET_ERROR) {
\r
9300 err = WSAGetLastError();
\r
9305 /* Resolve remote host name */
\r
9306 memset((char *) &sa, 0, sizeof(struct sockaddr_in));
\r
9307 if (!(hp = gethostbyname(host))) {
\r
9308 unsigned int b0, b1, b2, b3;
\r
9310 err = WSAGetLastError();
\r
9312 if (sscanf(host, "%u.%u.%u.%u", &b0, &b1, &b2, &b3) == 4) {
\r
9313 hp = (struct hostent *) calloc(1, sizeof(struct hostent));
\r
9314 hp->h_addrtype = AF_INET;
\r
9316 hp->h_addr_list = (char **) calloc(2, sizeof(char *));
\r
9317 hp->h_addr_list[0] = (char *) malloc(4);
\r
9318 hp->h_addr_list[0][0] = (char) b0;
\r
9319 hp->h_addr_list[0][1] = (char) b1;
\r
9320 hp->h_addr_list[0][2] = (char) b2;
\r
9321 hp->h_addr_list[0][3] = (char) b3;
\r
9327 sa.sin_family = hp->h_addrtype;
\r
9328 uport = (unsigned short) atoi(port);
\r
9329 sa.sin_port = htons(uport);
\r
9330 memcpy((char *) &sa.sin_addr, hp->h_addr, hp->h_length);
\r
9332 /* Make connection */
\r
9333 if (connect(s, (struct sockaddr *) &sa,
\r
9334 sizeof(struct sockaddr_in)) == SOCKET_ERROR) {
\r
9335 err = WSAGetLastError();
\r
9340 /* Prepare return value */
\r
9341 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
\r
9342 cp->kind = CPSock;
\r
9344 *pr = (ProcRef *) cp;
\r
9350 OpenCommPort(char *name, ProcRef *pr)
\r
9355 char fullname[MSG_SIZ];
\r
9357 if (*name != '\\')
\r
9358 snprintf(fullname, MSG_SIZ, "\\\\.\\%s", name);
\r
9360 safeStrCpy(fullname, name, sizeof(fullname)/sizeof(fullname[0]) );
\r
9362 h = CreateFile(name, GENERIC_READ | GENERIC_WRITE,
\r
9363 0, NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL);
\r
9364 if (h == (HANDLE) -1) {
\r
9365 return GetLastError();
\r
9369 if (!SetCommState(h, (LPDCB) &dcb)) return GetLastError();
\r
9371 /* Accumulate characters until a 100ms pause, then parse */
\r
9372 ct.ReadIntervalTimeout = 100;
\r
9373 ct.ReadTotalTimeoutMultiplier = 0;
\r
9374 ct.ReadTotalTimeoutConstant = 0;
\r
9375 ct.WriteTotalTimeoutMultiplier = 0;
\r
9376 ct.WriteTotalTimeoutConstant = 0;
\r
9377 if (!SetCommTimeouts(h, (LPCOMMTIMEOUTS) &ct)) return GetLastError();
\r
9379 /* Prepare return value */
\r
9380 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
\r
9381 cp->kind = CPComm;
\r
9384 *pr = (ProcRef *) cp;
\r
9390 OpenLoopback(ProcRef *pr)
\r
9392 DisplayFatalError(_("Not implemented"), 0, 1);
\r
9398 OpenRcmd(char* host, char* user, char* cmd, ProcRef* pr)
\r
9403 struct sockaddr_in sa, mysa;
\r
9404 struct hostent FAR *hp;
\r
9405 unsigned short uport;
\r
9406 WORD wVersionRequested;
\r
9409 char stderrPortStr[MSG_SIZ];
\r
9411 /* Initialize socket DLL */
\r
9412 wVersionRequested = MAKEWORD(1, 1);
\r
9413 err = WSAStartup(wVersionRequested, &wsaData);
\r
9414 if (err != 0) return err;
\r
9416 /* Resolve remote host name */
\r
9417 memset((char *) &sa, 0, sizeof(struct sockaddr_in));
\r
9418 if (!(hp = gethostbyname(host))) {
\r
9419 unsigned int b0, b1, b2, b3;
\r
9421 err = WSAGetLastError();
\r
9423 if (sscanf(host, "%u.%u.%u.%u", &b0, &b1, &b2, &b3) == 4) {
\r
9424 hp = (struct hostent *) calloc(1, sizeof(struct hostent));
\r
9425 hp->h_addrtype = AF_INET;
\r
9427 hp->h_addr_list = (char **) calloc(2, sizeof(char *));
\r
9428 hp->h_addr_list[0] = (char *) malloc(4);
\r
9429 hp->h_addr_list[0][0] = (char) b0;
\r
9430 hp->h_addr_list[0][1] = (char) b1;
\r
9431 hp->h_addr_list[0][2] = (char) b2;
\r
9432 hp->h_addr_list[0][3] = (char) b3;
\r
9438 sa.sin_family = hp->h_addrtype;
\r
9439 uport = (unsigned short) 514;
\r
9440 sa.sin_port = htons(uport);
\r
9441 memcpy((char *) &sa.sin_addr, hp->h_addr, hp->h_length);
\r
9443 /* Bind local socket to unused "privileged" port address
\r
9445 s = INVALID_SOCKET;
\r
9446 memset((char *) &mysa, 0, sizeof(struct sockaddr_in));
\r
9447 mysa.sin_family = AF_INET;
\r
9448 mysa.sin_addr.s_addr = INADDR_ANY;
\r
9449 for (fromPort = 1023;; fromPort--) {
\r
9450 if (fromPort < 0) {
\r
9452 return WSAEADDRINUSE;
\r
9454 if (s == INVALID_SOCKET) {
\r
9455 if ((s = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {
\r
9456 err = WSAGetLastError();
\r
9461 uport = (unsigned short) fromPort;
\r
9462 mysa.sin_port = htons(uport);
\r
9463 if (bind(s, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))
\r
9464 == SOCKET_ERROR) {
\r
9465 err = WSAGetLastError();
\r
9466 if (err == WSAEADDRINUSE) continue;
\r
9470 if (connect(s, (struct sockaddr *) &sa,
\r
9471 sizeof(struct sockaddr_in)) == SOCKET_ERROR) {
\r
9472 err = WSAGetLastError();
\r
9473 if (err == WSAEADDRINUSE) {
\r
9484 /* Bind stderr local socket to unused "privileged" port address
\r
9486 s2 = INVALID_SOCKET;
\r
9487 memset((char *) &mysa, 0, sizeof(struct sockaddr_in));
\r
9488 mysa.sin_family = AF_INET;
\r
9489 mysa.sin_addr.s_addr = INADDR_ANY;
\r
9490 for (fromPort = 1023;; fromPort--) {
\r
9491 if (fromPort == prevStderrPort) continue; // don't reuse port
\r
9492 if (fromPort < 0) {
\r
9493 (void) closesocket(s);
\r
9495 return WSAEADDRINUSE;
\r
9497 if (s2 == INVALID_SOCKET) {
\r
9498 if ((s2 = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {
\r
9499 err = WSAGetLastError();
\r
9505 uport = (unsigned short) fromPort;
\r
9506 mysa.sin_port = htons(uport);
\r
9507 if (bind(s2, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))
\r
9508 == SOCKET_ERROR) {
\r
9509 err = WSAGetLastError();
\r
9510 if (err == WSAEADDRINUSE) continue;
\r
9511 (void) closesocket(s);
\r
9515 if (listen(s2, 1) == SOCKET_ERROR) {
\r
9516 err = WSAGetLastError();
\r
9517 if (err == WSAEADDRINUSE) {
\r
9519 s2 = INVALID_SOCKET;
\r
9522 (void) closesocket(s);
\r
9523 (void) closesocket(s2);
\r
9529 prevStderrPort = fromPort; // remember port used
\r
9530 snprintf(stderrPortStr, MSG_SIZ, "%d", fromPort);
\r
9532 if (send(s, stderrPortStr, strlen(stderrPortStr) + 1, 0) == SOCKET_ERROR) {
\r
9533 err = WSAGetLastError();
\r
9534 (void) closesocket(s);
\r
9535 (void) closesocket(s2);
\r
9540 if (send(s, UserName(), strlen(UserName()) + 1, 0) == SOCKET_ERROR) {
\r
9541 err = WSAGetLastError();
\r
9542 (void) closesocket(s);
\r
9543 (void) closesocket(s2);
\r
9547 if (*user == NULLCHAR) user = UserName();
\r
9548 if (send(s, user, strlen(user) + 1, 0) == SOCKET_ERROR) {
\r
9549 err = WSAGetLastError();
\r
9550 (void) closesocket(s);
\r
9551 (void) closesocket(s2);
\r
9555 if (send(s, cmd, strlen(cmd) + 1, 0) == SOCKET_ERROR) {
\r
9556 err = WSAGetLastError();
\r
9557 (void) closesocket(s);
\r
9558 (void) closesocket(s2);
\r
9563 if ((s3 = accept(s2, NULL, NULL)) == INVALID_SOCKET) {
\r
9564 err = WSAGetLastError();
\r
9565 (void) closesocket(s);
\r
9566 (void) closesocket(s2);
\r
9570 (void) closesocket(s2); /* Stop listening */
\r
9572 /* Prepare return value */
\r
9573 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
\r
9574 cp->kind = CPRcmd;
\r
9577 *pr = (ProcRef *) cp;
\r
9584 AddInputSource(ProcRef pr, int lineByLine,
\r
9585 InputCallback func, VOIDSTAR closure)
\r
9587 InputSource *is, *is2 = NULL;
\r
9588 ChildProc *cp = (ChildProc *) pr;
\r
9590 is = (InputSource *) calloc(1, sizeof(InputSource));
\r
9591 is->lineByLine = lineByLine;
\r
9593 is->closure = closure;
\r
9594 is->second = NULL;
\r
9595 is->next = is->buf;
\r
9596 if (pr == NoProc) {
\r
9597 is->kind = CPReal;
\r
9598 consoleInputSource = is;
\r
9600 is->kind = cp->kind;
\r
9602 [AS] Try to avoid a race condition if the thread is given control too early:
\r
9603 we create all threads suspended so that the is->hThread variable can be
\r
9604 safely assigned, then let the threads start with ResumeThread.
\r
9606 switch (cp->kind) {
\r
9608 is->hFile = cp->hFrom;
\r
9609 cp->hFrom = NULL; /* now owned by InputThread */
\r
9611 CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) NonOvlInputThread,
\r
9612 (LPVOID) is, CREATE_SUSPENDED, &is->id);
\r
9616 is->hFile = cp->hFrom;
\r
9617 cp->hFrom = NULL; /* now owned by InputThread */
\r
9619 CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) InputThread,
\r
9620 (LPVOID) is, CREATE_SUSPENDED, &is->id);
\r
9624 is->sock = cp->sock;
\r
9626 CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,
\r
9627 (LPVOID) is, CREATE_SUSPENDED, &is->id);
\r
9631 is2 = (InputSource *) calloc(1, sizeof(InputSource));
\r
9633 is->sock = cp->sock;
\r
9635 is2->sock = cp->sock2;
\r
9636 is2->second = is2;
\r
9638 CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,
\r
9639 (LPVOID) is, CREATE_SUSPENDED, &is->id);
\r
9641 CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,
\r
9642 (LPVOID) is2, CREATE_SUSPENDED, &is2->id);
\r
9646 if( is->hThread != NULL ) {
\r
9647 ResumeThread( is->hThread );
\r
9650 if( is2 != NULL && is2->hThread != NULL ) {
\r
9651 ResumeThread( is2->hThread );
\r
9655 return (InputSourceRef) is;
\r
9659 RemoveInputSource(InputSourceRef isr)
\r
9663 is = (InputSource *) isr;
\r
9664 is->hThread = NULL; /* tell thread to stop */
\r
9665 CloseHandle(is->hThread);
\r
9666 if (is->second != NULL) {
\r
9667 is->second->hThread = NULL;
\r
9668 CloseHandle(is->second->hThread);
\r
9672 int no_wrap(char *message, int count)
\r
9674 ConsoleOutput(message, count, FALSE);
\r
9679 OutputToProcess(ProcRef pr, char *message, int count, int *outError)
\r
9682 int outCount = SOCKET_ERROR;
\r
9683 ChildProc *cp = (ChildProc *) pr;
\r
9684 static OVERLAPPED ovl;
\r
9685 static int line = 0;
\r
9689 if (appData.noJoin || !appData.useInternalWrap)
\r
9690 return no_wrap(message, count);
\r
9693 int width = get_term_width();
\r
9694 int len = wrap(NULL, message, count, width, &line);
\r
9695 char *msg = malloc(len);
\r
9699 return no_wrap(message, count);
\r
9702 dbgchk = wrap(msg, message, count, width, &line);
\r
9703 if (dbgchk != len && appData.debugMode)
\r
9704 fprintf(debugFP, "wrap(): dbgchk(%d) != len(%d)\n", dbgchk, len);
\r
9705 ConsoleOutput(msg, len, FALSE);
\r
9712 if (ovl.hEvent == NULL) {
\r
9713 ovl.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
\r
9715 ovl.Internal = ovl.InternalHigh = ovl.Offset = ovl.OffsetHigh = 0;
\r
9717 switch (cp->kind) {
\r
9720 outCount = send(cp->sock, message, count, 0);
\r
9721 if (outCount == SOCKET_ERROR) {
\r
9722 *outError = WSAGetLastError();
\r
9724 *outError = NO_ERROR;
\r
9729 if (WriteFile(((ChildProc *)pr)->hTo, message, count,
\r
9730 &dOutCount, NULL)) {
\r
9731 *outError = NO_ERROR;
\r
9732 outCount = (int) dOutCount;
\r
9734 *outError = GetLastError();
\r
9739 *outError = DoWriteFile(((ChildProc *)pr)->hTo, message, count,
\r
9740 &dOutCount, &ovl);
\r
9741 if (*outError == NO_ERROR) {
\r
9742 outCount = (int) dOutCount;
\r
9752 if(n != 0) Sleep(n);
\r
9756 OutputToProcessDelayed(ProcRef pr, char *message, int count, int *outError,
\r
9759 /* Ignore delay, not implemented for WinBoard */
\r
9760 return OutputToProcess(pr, message, count, outError);
\r
9765 CmailSigHandlerCallBack(InputSourceRef isr, VOIDSTAR closure,
\r
9766 char *buf, int count, int error)
\r
9768 DisplayFatalError(_("Not implemented"), 0, 1);
\r
9771 /* see wgamelist.c for Game List functions */
\r
9772 /* see wedittags.c for Edit Tags functions */
\r
9779 char buf[MSG_SIZ];
\r
9782 if (SearchPath(installDir, appData.icsLogon, NULL, MSG_SIZ, buf, &dummy)) {
\r
9783 f = fopen(buf, "r");
\r
9785 ProcessICSInitScript(f);
\r
9795 StartAnalysisClock()
\r
9797 if (analysisTimerEvent) return;
\r
9798 analysisTimerEvent = SetTimer(hwndMain, (UINT) ANALYSIS_TIMER_ID,
\r
9799 (UINT) 2000, NULL);
\r
9803 SetHighlights(int fromX, int fromY, int toX, int toY)
\r
9805 highlightInfo.sq[0].x = fromX;
\r
9806 highlightInfo.sq[0].y = fromY;
\r
9807 highlightInfo.sq[1].x = toX;
\r
9808 highlightInfo.sq[1].y = toY;
\r
9814 highlightInfo.sq[0].x = highlightInfo.sq[0].y =
\r
9815 highlightInfo.sq[1].x = highlightInfo.sq[1].y = -1;
\r
9819 SetPremoveHighlights(int fromX, int fromY, int toX, int toY)
\r
9821 premoveHighlightInfo.sq[0].x = fromX;
\r
9822 premoveHighlightInfo.sq[0].y = fromY;
\r
9823 premoveHighlightInfo.sq[1].x = toX;
\r
9824 premoveHighlightInfo.sq[1].y = toY;
\r
9828 ClearPremoveHighlights()
\r
9830 premoveHighlightInfo.sq[0].x = premoveHighlightInfo.sq[0].y =
\r
9831 premoveHighlightInfo.sq[1].x = premoveHighlightInfo.sq[1].y = -1;
\r
9835 ShutDownFrontEnd()
\r
9837 if (saveSettingsOnExit) SaveSettings(settingsFileName);
\r
9838 DeleteClipboardTempFiles();
\r
9844 if (IsIconic(hwndMain))
\r
9845 ShowWindow(hwndMain, SW_RESTORE);
\r
9847 SetActiveWindow(hwndMain);
\r
9851 * Prototypes for animation support routines
\r
9853 static void ScreenSquare(int column, int row, POINT * pt);
\r
9854 static void Tween( POINT * start, POINT * mid, POINT * finish, int factor,
\r
9855 POINT frames[], int * nFrames);
\r
9861 AnimateAtomicCapture(Board board, int fromX, int fromY, int toX, int toY)
\r
9862 { // [HGM] atomic: animate blast wave
\r
9865 explodeInfo.fromX = fromX;
\r
9866 explodeInfo.fromY = fromY;
\r
9867 explodeInfo.toX = toX;
\r
9868 explodeInfo.toY = toY;
\r
9869 for(i=1; i<4*kFactor; i++) {
\r
9870 explodeInfo.radius = (i*180)/(4*kFactor-1);
\r
9871 DrawPosition(FALSE, board);
\r
9872 Sleep(appData.animSpeed);
\r
9874 explodeInfo.radius = 0;
\r
9875 DrawPosition(TRUE, board);
\r
9879 AnimateMove(board, fromX, fromY, toX, toY)
\r
9886 ChessSquare piece;
\r
9887 POINT start, finish, mid;
\r
9888 POINT frames[kFactor * 2 + 1];
\r
9891 if (!appData.animate) return;
\r
9892 if (doingSizing) return;
\r
9893 if (fromY < 0 || fromX < 0) return;
\r
9894 piece = board[fromY][fromX];
\r
9895 if (piece >= EmptySquare) return;
\r
9897 ScreenSquare(fromX, fromY, &start);
\r
9898 ScreenSquare(toX, toY, &finish);
\r
9900 /* All moves except knight jumps move in straight line */
\r
9901 if (!(abs(fromX-toX) == 1 && abs(fromY-toY) == 2 || abs(fromX-toX) == 2 && abs(fromY-toY) == 1)) {
\r
9902 mid.x = start.x + (finish.x - start.x) / 2;
\r
9903 mid.y = start.y + (finish.y - start.y) / 2;
\r
9905 /* Knight: make straight movement then diagonal */
\r
9906 if (abs(toY - fromY) < abs(toX - fromX)) {
\r
9907 mid.x = start.x + (finish.x - start.x) / 2;
\r
9911 mid.y = start.y + (finish.y - start.y) / 2;
\r
9915 /* Don't use as many frames for very short moves */
\r
9916 if (abs(toY - fromY) + abs(toX - fromX) <= 2)
\r
9917 Tween(&start, &mid, &finish, kFactor - 1, frames, &nFrames);
\r
9919 Tween(&start, &mid, &finish, kFactor, frames, &nFrames);
\r
9921 animInfo.from.x = fromX;
\r
9922 animInfo.from.y = fromY;
\r
9923 animInfo.to.x = toX;
\r
9924 animInfo.to.y = toY;
\r
9925 animInfo.lastpos = start;
\r
9926 animInfo.piece = piece;
\r
9927 for (n = 0; n < nFrames; n++) {
\r
9928 animInfo.pos = frames[n];
\r
9929 DrawPosition(FALSE, NULL);
\r
9930 animInfo.lastpos = animInfo.pos;
\r
9931 Sleep(appData.animSpeed);
\r
9933 animInfo.pos = finish;
\r
9934 DrawPosition(FALSE, NULL);
\r
9935 animInfo.piece = EmptySquare;
\r
9936 Explode(board, fromX, fromY, toX, toY);
\r
9939 /* Convert board position to corner of screen rect and color */
\r
9942 ScreenSquare(column, row, pt)
\r
9943 int column; int row; POINT * pt;
\r
9946 pt->x = lineGap + ((BOARD_WIDTH-1)-column) * (squareSize + lineGap) + border;
\r
9947 pt->y = lineGap + row * (squareSize + lineGap) + border;
\r
9949 pt->x = lineGap + column * (squareSize + lineGap) + border;
\r
9950 pt->y = lineGap + ((BOARD_HEIGHT-1)-row) * (squareSize + lineGap) + border;
\r
9954 /* Generate a series of frame coords from start->mid->finish.
\r
9955 The movement rate doubles until the half way point is
\r
9956 reached, then halves back down to the final destination,
\r
9957 which gives a nice slow in/out effect. The algorithmn
\r
9958 may seem to generate too many intermediates for short
\r
9959 moves, but remember that the purpose is to attract the
\r
9960 viewers attention to the piece about to be moved and
\r
9961 then to where it ends up. Too few frames would be less
\r
9965 Tween(start, mid, finish, factor, frames, nFrames)
\r
9966 POINT * start; POINT * mid;
\r
9967 POINT * finish; int factor;
\r
9968 POINT frames[]; int * nFrames;
\r
9970 int n, fraction = 1, count = 0;
\r
9972 /* Slow in, stepping 1/16th, then 1/8th, ... */
\r
9973 for (n = 0; n < factor; n++)
\r
9975 for (n = 0; n < factor; n++) {
\r
9976 frames[count].x = start->x + (mid->x - start->x) / fraction;
\r
9977 frames[count].y = start->y + (mid->y - start->y) / fraction;
\r
9979 fraction = fraction / 2;
\r
9983 frames[count] = *mid;
\r
9986 /* Slow out, stepping 1/2, then 1/4, ... */
\r
9988 for (n = 0; n < factor; n++) {
\r
9989 frames[count].x = finish->x - (finish->x - mid->x) / fraction;
\r
9990 frames[count].y = finish->y - (finish->y - mid->y) / fraction;
\r
9992 fraction = fraction * 2;
\r
9998 SettingsPopUp(ChessProgramState *cps)
\r
9999 { // [HGM] wrapper needed because handles must not be passed through back-end
\r
10000 EngineOptionsPopup(savedHwnd, cps);
\r
10003 int flock(int fid, int code)
\r
10005 HANDLE hFile = (HANDLE) _get_osfhandle(fid);
\r
10007 ov.hEvent = NULL;
\r
10009 ov.OffsetHigh = 0;
\r
10011 case 1: LockFileEx(hFile, LOCKFILE_EXCLUSIVE_LOCK, 0, 1024, 0, &ov); break; // LOCK_SH
\r
10012 case 2: LockFileEx(hFile, LOCKFILE_EXCLUSIVE_LOCK, 0, 1024, 0, &ov); break; // LOCK_EX
\r
10013 case 3: UnlockFileEx(hFile, 0, 1024, 0, &ov); break; // LOCK_UN
\r
10014 default: return -1;
\r
10023 static char col[8][20];
\r
10024 COLORREF color = *(COLORREF *) colorVariable[n];
\r
10026 snprintf(col[i], 20, "#%02lx%02lx%02lx", color&0xff, (color>>8)&0xff, (color>>16)&0xff);
\r
10031 ActivateTheme (int new)
\r
10032 { // Redo initialization of features depending on options that can occur in themes
\r
10034 if(new) InitDrawingColors();
\r
10035 fontBitmapSquareSize = 0; // request creation of new font pieces
\r
10036 InitDrawingSizes(-2, 0);
\r
10037 InvalidateRect(hwndMain, NULL, TRUE);
\r