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