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, 2014, 2015, 2016 Free
\r
9 * Software Foundation, Inc.
\r
11 * Enhancements Copyright 2005 Alessandro Scotti
\r
13 * XBoard borrows its colors and the bitmaps.xchess bitmap set from XChess,
\r
14 * which was written and is copyrighted by Wayne Christopher.
\r
16 * The following terms apply to Digital Equipment Corporation's copyright
\r
17 * interest in XBoard:
\r
18 * ------------------------------------------------------------------------
\r
19 * All Rights Reserved
\r
21 * Permission to use, copy, modify, and distribute this software and its
\r
22 * documentation for any purpose and without fee is hereby granted,
\r
23 * provided that the above copyright notice appear in all copies and that
\r
24 * both that copyright notice and this permission notice appear in
\r
25 * supporting documentation, and that the name of Digital not be
\r
26 * used in advertising or publicity pertaining to distribution of the
\r
27 * software without specific, written prior permission.
\r
29 * DIGITAL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
\r
30 * ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL
\r
31 * DIGITAL BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
\r
32 * ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
\r
33 * WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
\r
34 * ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
\r
36 * ------------------------------------------------------------------------
\r
38 * The following terms apply to the enhanced version of XBoard
\r
39 * distributed by the Free Software Foundation:
\r
40 * ------------------------------------------------------------------------
\r
42 * GNU XBoard is free software: you can redistribute it and/or modify
\r
43 * it under the terms of the GNU General Public License as published by
\r
44 * the Free Software Foundation, either version 3 of the License, or (at
\r
45 * your option) any later version.
\r
47 * GNU XBoard is distributed in the hope that it will be useful, but
\r
48 * WITHOUT ANY WARRANTY; without even the implied warranty of
\r
49 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
\r
50 * General Public License for more details.
\r
52 * You should have received a copy of the GNU General Public License
\r
53 * along with this program. If not, see http://www.gnu.org/licenses/. *
\r
55 *------------------------------------------------------------------------
\r
56 ** See the file ChangeLog for a revision history. */
\r
59 #define WINVER 0x0500
\r
64 #include <windows.h>
\r
65 #include <winuser.h>
\r
66 #include <winsock.h>
\r
67 #include <commctrl.h>
\r
73 #include <sys/stat.h>
\r
76 #include <commdlg.h>
\r
78 #include <richedit.h>
\r
79 #include <mmsystem.h>
\r
89 #include "frontend.h"
\r
90 #include "backend.h"
\r
91 #include "winboard.h"
\r
93 #include "wclipbrd.h"
\r
94 #include "woptions.h"
\r
95 #include "wsockerr.h"
\r
96 #include "defaults.h"
\r
101 #define DATADIR "~~"
\r
103 //void InitEngineUCI( const char * iniDir, ChessProgramState * cps );
\r
105 int myrandom(void);
\r
106 void mysrandom(unsigned int seed);
\r
108 extern int whiteFlag, blackFlag;
\r
109 Boolean flipClock = FALSE;
\r
110 extern HANDLE chatHandle[];
\r
111 extern enum ICS_TYPE ics_type;
\r
113 int MySearchPath P((char *installDir, char *name, char *fullname));
\r
114 int MyGetFullPathName P((char *name, char *fullname));
\r
115 void DisplayHoldingsCount(HDC hdc, int x, int y, int align, int copyNumber);
\r
116 VOID NewVariantPopup(HWND hwnd);
\r
117 int FinishMove P((ChessMove moveType, int fromX, int fromY, int toX, int toY,
\r
118 /*char*/int promoChar));
\r
119 void DisplayMove P((int moveNumber));
\r
120 void ChatPopUp P((char *s));
\r
122 ChessSquare piece;
\r
123 POINT pos; /* window coordinates of current pos */
\r
124 POINT lastpos; /* window coordinates of last pos - used for clipping */
\r
125 POINT from; /* board coordinates of the piece's orig pos */
\r
126 POINT to; /* board coordinates of the piece's new pos */
\r
129 static AnimInfo animInfo = { EmptySquare, {-1,-1}, {-1,-1}, {-1,-1} };
\r
132 POINT start; /* window coordinates of start pos */
\r
133 POINT pos; /* window coordinates of current pos */
\r
134 POINT lastpos; /* window coordinates of last pos - used for clipping */
\r
135 POINT from; /* board coordinates of the piece's orig pos */
\r
139 static DragInfo dragInfo = { {-1,-1}, {-1,-1}, {-1,-1}, {-1,-1}, EmptySquare };
\r
142 POINT sq[2]; /* board coordinates of from, to squares */
\r
145 static HighlightInfo highlightInfo = { {{-1, -1}, {-1, -1}} };
\r
146 static HighlightInfo premoveHighlightInfo = { {{-1, -1}, {-1, -1}} };
\r
147 static HighlightInfo partnerHighlightInfo = { {{-1, -1}, {-1, -1}} };
\r
148 static HighlightInfo oldPartnerHighlight = { {{-1, -1}, {-1, -1}} };
\r
150 typedef struct { // [HGM] atomic
\r
151 int fromX, fromY, toX, toY, radius;
\r
154 static ExplodeInfo explodeInfo;
\r
156 /* Window class names */
\r
157 char szAppName[] = "WinBoard";
\r
158 char szConsoleName[] = "WBConsole";
\r
160 /* Title bar text */
\r
161 char szTitle[] = "WinBoard";
\r
162 char szConsoleTitle[] = "I C S Interaction";
\r
165 char *settingsFileName;
\r
166 Boolean saveSettingsOnExit;
\r
167 char installDir[MSG_SIZ];
\r
168 int errorExitStatus;
\r
170 BoardSize boardSize;
\r
171 Boolean chessProgram;
\r
172 //static int boardX, boardY;
\r
173 int minX, minY; // [HGM] placement: volatile limits on upper-left corner
\r
174 int squareSize, lineGap, minorSize;
\r
175 static int winW, winH;
\r
176 static RECT messageRect, whiteRect, blackRect, leftLogoRect, rightLogoRect; // [HGM] logo
\r
177 static int logoHeight = 0;
\r
178 static char messageText[MESSAGE_TEXT_MAX];
\r
179 static int clockTimerEvent = 0;
\r
180 static int loadGameTimerEvent = 0;
\r
181 static int analysisTimerEvent = 0;
\r
182 static DelayedEventCallback delayedTimerCallback;
\r
183 static int delayedTimerEvent = 0;
\r
184 static int buttonCount = 2;
\r
185 char *icsTextMenuString;
\r
187 char *firstChessProgramNames;
\r
188 char *secondChessProgramNames;
\r
190 #define PALETTESIZE 256
\r
192 HINSTANCE hInst; /* current instance */
\r
193 Boolean alwaysOnTop = FALSE;
\r
195 COLORREF lightSquareColor, darkSquareColor, whitePieceColor,
\r
196 blackPieceColor, highlightSquareColor, premoveHighlightColor;
\r
197 COLORREF markerColor[8] = { 0x00FFFF, 0x0000FF, 0x00FF00, 0xFF0000, 0xFFFF00, 0xFF00FF, 0xFFFFFF, 0x000000 };
\r
199 ColorClass currentColorClass;
\r
201 static HWND savedHwnd;
\r
202 HWND hCommPort = NULL; /* currently open comm port */
\r
203 static HWND hwndPause; /* pause button */
\r
204 static HBITMAP pieceBitmap[3][(int) BlackPawn]; /* [HGM] nr of bitmaps referred to bP in stead of wK */
\r
205 static HBRUSH lightSquareBrush, darkSquareBrush,
\r
206 blackSquareBrush, /* [HGM] for band between board and holdings */
\r
207 explodeBrush, /* [HGM] atomic */
\r
208 markerBrush[8], /* [HGM] markers */
\r
209 whitePieceBrush, blackPieceBrush, iconBkgndBrush /*, outlineBrush*/;
\r
210 static POINT gridEndpoints[(BOARD_RANKS + BOARD_FILES + 2) * 2];
\r
211 static DWORD gridVertexCounts[BOARD_RANKS + BOARD_FILES + 2];
\r
212 static HPEN gridPen = NULL;
\r
213 static HPEN highlightPen = NULL;
\r
214 static HPEN premovePen = NULL;
\r
215 static NPLOGPALETTE pLogPal;
\r
216 static BOOL paletteChanged = FALSE;
\r
217 static HICON iconWhite, iconBlack, iconCurrent;
\r
218 static int doingSizing = FALSE;
\r
219 static int lastSizing = 0;
\r
220 static int prevStderrPort;
\r
221 static HBITMAP userLogo;
\r
223 static HBITMAP liteBackTexture = NULL;
\r
224 static HBITMAP darkBackTexture = NULL;
\r
225 static int liteBackTextureMode = BACK_TEXTURE_MODE_PLAIN;
\r
226 static int darkBackTextureMode = BACK_TEXTURE_MODE_PLAIN;
\r
227 static int backTextureSquareSize = 0;
\r
228 static struct { int x; int y; int mode; } backTextureSquareInfo[BOARD_RANKS][BOARD_FILES];
\r
230 #if __GNUC__ && !defined(_winmajor)
\r
231 #define oldDialog 0 /* cygwin doesn't define _winmajor; mingw does */
\r
234 #if defined(_winmajor)
\r
235 #define oldDialog (_winmajor < 4)
\r
237 #define oldDialog 0
\r
241 #define INTERNATIONAL
\r
243 #ifdef INTERNATIONAL
\r
244 # define _(s) T_(s)
\r
250 # define Translate(x, y)
\r
251 # define LoadLanguageFile(s)
\r
254 #ifdef INTERNATIONAL
\r
256 Boolean barbaric; // flag indicating if translation is needed
\r
258 // list of item numbers used in each dialog (used to alter language at run time)
\r
260 #define ABOUTBOX -1 /* not sure why these are needed */
\r
261 #define ABOUTBOX2 -1
\r
263 int dialogItems[][42] = {
\r
264 { ABOUTBOX, IDOK, OPT_MESS, 400 },
\r
265 { DLG_TimeControl, IDC_Babble, OPT_TCUseMoves, OPT_TCUseInc, OPT_TCUseFixed,
\r
266 OPT_TCtext1, OPT_TCtext2, OPT_TCitext1, OPT_TCitext2, OPT_TCftext, GPB_Factors, IDC_Factor1, IDC_Factor2, IDOK, IDCANCEL },
\r
267 { DLG_LoadOptions, OPT_Autostep, OPT_AStext1, OPT_Exact, OPT_Subset, OPT_Struct, OPT_Material, OPT_Range, OPT_Difference,
\r
268 OPT_elo1t, OPT_elo2t, OPT_datet, OPT_Stretch, OPT_Stretcht, OPT_Reversed, OPT_SearchMode, OPT_Mirror, OPT_thresholds,
\r
269 OPT_Ranget, IDOK, IDCANCEL },
\r
270 { DLG_SaveOptions, OPT_Autosave, OPT_AVPrompt, OPT_AVToFile, OPT_AVBrowse,
\r
271 801, OPT_PGN, OPT_Old, OPT_OutOfBookInfo, IDOK, IDCANCEL },
\r
272 { 1536, 1090, IDC_Directories, 1089, 1091, IDOK, IDCANCEL, 1038, IDC_IndexNr, 1037 },
\r
273 { DLG_CommPort, IDOK, IDCANCEL, IDC_Port, IDC_Rate, IDC_Bits, IDC_Parity,
\r
274 IDC_Stop, IDC_Flow, OPT_SerialHelp },
\r
275 { DLG_EditComment, IDOK, OPT_CancelComment, OPT_ClearComment, OPT_EditComment },
\r
276 { DLG_PromotionKing, PB_Chancellor, PB_Archbishop, PB_Queen, PB_Rook,
\r
277 PB_Bishop, PB_Knight, PB_King, IDCANCEL, IDC_Yes, IDC_No, IDC_Centaur },
\r
278 { ABOUTBOX2, IDC_ChessBoard },
\r
279 { DLG_GameList, OPT_GameListLoad, OPT_GameListPrev, OPT_GameListNext,
\r
280 OPT_GameListClose, IDC_GameListDoFilter },
\r
281 { DLG_EditTags, IDOK, OPT_TagsCancel, OPT_EditTags },
\r
282 { DLG_Error, IDOK },
\r
283 { DLG_Colorize, IDOK, IDCANCEL, OPT_ChooseColor, OPT_Bold, OPT_Italic,
\r
284 OPT_Underline, OPT_Strikeout, OPT_Sample },
\r
285 { DLG_Question, IDOK, IDCANCEL, OPT_QuestionText },
\r
286 { DLG_Startup, IDC_Welcome, OPT_ChessEngine, OPT_ChessServer, OPT_View,
\r
287 IDC_SPECIFY_ENG_STATIC, IDC_SPECIFY_SERVER_STATIC, OPT_AnyAdditional,
\r
288 IDOK, IDCANCEL, IDM_HELPCONTENTS },
\r
289 { DLG_IndexNumber, IDC_Index },
\r
290 { DLG_TypeInMove, IDOK, IDCANCEL },
\r
291 { DLG_TypeInName, IDOK, IDCANCEL },
\r
292 { DLG_Sound, IDC_Event, OPT_NoSound, OPT_DefaultBeep, OPT_BuiltInSound,
\r
293 OPT_WavFile, OPT_BrowseSound, OPT_DefaultSounds, IDOK, IDCANCEL, OPT_PlaySound },
\r
294 { DLG_GeneralOptions, IDOK, IDCANCEL, OPT_AlwaysOnTop, OPT_HighlightLastMove,
\r
295 OPT_AlwaysQueen, OPT_PeriodicUpdates, OPT_AnimateDragging, OPT_PonderNextMove,
\r
296 OPT_AnimateMoving, OPT_PopupExitMessage, OPT_AutoFlag, OPT_PopupMoveErrors,
\r
297 OPT_AutoFlipView, OPT_ShowButtonBar, OPT_AutoRaiseBoard, OPT_ShowCoordinates,
\r
298 OPT_Blindfold, OPT_ShowThinking, OPT_HighlightDragging, OPT_TestLegality,
\r
299 OPT_SaveExtPGN, OPT_HideThinkFromHuman, OPT_ExtraInfoInMoveHistory,
\r
300 OPT_HighlightMoveArrow, OPT_AutoLogo ,OPT_SmartMove },
\r
301 { DLG_IcsOptions, IDOK, IDCANCEL, OPT_AutoComment, OPT_AutoKibitz, OPT_AutoObserve,
\r
302 OPT_GetMoveList, OPT_LocalLineEditing, OPT_QuietPlay, OPT_SeekGraph, OPT_AutoRefresh,
\r
303 OPT_BgObserve, OPT_DualBoard, OPT_Premove, OPT_PremoveWhite, OPT_PremoveBlack,
\r
304 OPT_SmartMove, OPT_IcsAlarm, IDC_Sec, OPT_ChooseShoutColor, OPT_ChooseSShoutColor,
\r
305 OPT_ChooseChannel1Color, OPT_ChooseChannelColor, OPT_ChooseKibitzColor,
\r
306 OPT_ChooseTellColor, OPT_ChooseChallengeColor, OPT_ChooseRequestColor,
\r
307 OPT_ChooseSeekColor, OPT_ChooseNormalColor, OPT_ChooseBackgroundColor,
\r
308 OPT_DefaultColors, OPT_DontColorize, IDC_Boxes, GPB_Colors, GPB_Premove,
\r
309 GPB_General, GPB_Alarm, OPT_AutoCreate },
\r
310 { DLG_BoardOptions, IDOK, IDCANCEL, OPT_SizeTiny, OPT_SizeTeeny, OPT_SizeDinky,
\r
311 OPT_SizePetite, OPT_SizeSlim, OPT_SizeSmall, OPT_SizeMediocre, OPT_SizeMiddling,
\r
312 OPT_SizeAverage, OPT_SizeModerate, OPT_SizeMedium, OPT_SizeBulky, OPT_SizeLarge,
\r
313 OPT_SizeBig, OPT_SizeHuge, OPT_SizeGiant, OPT_SizeColossal, OPT_SizeTitanic,
\r
314 OPT_ChooseLightSquareColor, OPT_ChooseDarkSquareColor, OPT_ChooseWhitePieceColor,
\r
315 OPT_ChooseBlackPieceColor, OPT_ChooseHighlightSquareColor, OPT_ChoosePremoveHighlightColor,
\r
316 OPT_Monochrome, OPT_AllWhite, OPT_UpsideDown, OPT_DefaultBoardColors, GPB_Colors,
\r
317 IDC_Light, IDC_Dark, IDC_White, IDC_Black, IDC_High, IDC_PreHigh, GPB_Size, OPT_Bitmaps, OPT_PieceFont, OPT_Grid },
\r
318 { DLG_NewVariant, IDOK, IDCANCEL, OPT_VariantNormal, OPT_VariantFRC, OPT_VariantWildcastle,
\r
319 OPT_VariantNocastle, OPT_VariantLosers, OPT_VariantGiveaway, OPT_VariantSuicide,
\r
320 OPT_Variant3Check, OPT_VariantTwoKings, OPT_VariantAtomic, OPT_VariantCrazyhouse,
\r
321 OPT_VariantBughouse, OPT_VariantTwilight, OPT_VariantShogi, OPT_VariantSuper,
\r
322 OPT_VariantKnightmate, OPT_VariantBerolina, OPT_VariantCylinder, OPT_VariantFairy,
\r
323 OPT_VariantMakruk, OPT_VariantGothic, OPT_VariantCapablanca, OPT_VariantJanus,
\r
324 OPT_VariantCRC, OPT_VariantFalcon, OPT_VariantCourier, OPT_VariantGreat, OPT_VariantSChess,
\r
325 OPT_VariantShatranj, OPT_VariantXiangqi, GPB_Variant, GPB_Board, IDC_Height,
\r
326 IDC_Width, IDC_Hand, IDC_Pieces, IDC_Def },
\r
327 { DLG_Fonts, IDOK, IDCANCEL, OPT_ChooseClockFont, OPT_ChooseMessageFont,
\r
328 OPT_ChooseCoordFont, OPT_ChooseTagFont, OPT_ChooseCommentsFont, OPT_ChooseConsoleFont, OPT_ChooseMoveHistoryFont, OPT_DefaultFonts,
\r
329 OPT_ClockFont, OPT_MessageFont, OPT_CoordFont, OPT_EditTagsFont, OPT_ChoosePieceFont, OPT_MessageFont8,
\r
330 OPT_SampleGameListFont, OPT_ChooseGameListFont, OPT_MessageFont7,
\r
331 OPT_CommentsFont, OPT_MessageFont5, GPB_Current, GPB_All, OPT_MessageFont6 },
\r
332 { DLG_NewGameFRC, IDC_NFG_Label, IDC_NFG_Random, IDOK, IDCANCEL },
\r
333 { DLG_GameListOptions, IDC_GLT, IDC_GLT_Up, IDC_GLT_Down, IDC_GLT_Restore,
\r
334 IDC_GLT_Default, IDOK, IDCANCEL, IDC_GLT_RestoreTo },
\r
335 { DLG_MoveHistory },
\r
336 { DLG_EvalGraph },
\r
337 { DLG_EngineOutput, IDC_EngineLabel1, IDC_Engine1_NPS, IDC_EngineLabel2, IDC_Engine2_NPS },
\r
338 { DLG_Chat, IDC_Partner, IDC_Clear, IDC_Send, },
\r
339 { DLG_EnginePlayOptions, IDC_EpPonder, IDC_EpShowThinking, IDC_EpHideThinkingHuman,
\r
340 IDC_EpPeriodicUpdates, GPB_Adjudications, IDC_Draw, IDC_Moves, IDC_Threshold,
\r
341 IDC_Centi, IDC_TestClaims, IDC_DetectMates, IDC_MaterialDraws, IDC_TrivialDraws,
\r
342 GPB_Apply, IDC_Rule, IDC_Repeats, IDC_ScoreAbs1, IDC_ScoreAbs2, IDOK, IDCANCEL },
\r
343 { DLG_OptionsUCI, IDC_PolyDir, IDC_BrowseForPolyglotDir, IDC_Hash, IDC_Path,
\r
344 IDC_BrowseForEGTB, IDC_Cache, IDC_UseBook, IDC_BrowseForBook, IDC_CPU, IDC_OwnBook1,
\r
345 IDC_OwnBook2, IDC_Depth, IDC_Variation, IDC_DefGames, IDOK, IDCANCEL },
\r
349 static char languageBuf[70000], *foreign[1000], *english[1000], *languageFile[MSG_SIZ];
\r
350 static int lastChecked;
\r
351 static char oldLanguage[MSG_SIZ], *menuText[10][30];
\r
352 extern int tinyLayout;
\r
353 extern char * menuBarText[][10];
\r
356 LoadLanguageFile(char *name)
\r
357 { //load the file with translations, and make a list of the strings to be translated, and their translations
\r
359 int i=0, j=0, n=0, k;
\r
362 if(!name || name[0] == NULLCHAR) return;
\r
363 snprintf(buf, MSG_SIZ, "%s%s", name, strchr(name, '.') ? "" : ".lng"); // auto-append lng extension
\r
364 appData.language = oldLanguage;
\r
365 if(!strcmp(buf, oldLanguage)) { barbaric = 1; return; } // this language already loaded; just switch on
\r
366 if((f = fopen(buf, "r")) == NULL) return;
\r
367 while((k = fgetc(f)) != EOF) {
\r
368 if(i >= sizeof(languageBuf)) { DisplayError("Language file too big", 0); return; }
\r
369 languageBuf[i] = k;
\r
371 if(languageBuf[n] == '"' && languageBuf[i-1] == '"') {
\r
373 if(p = strstr(languageBuf + n + 1, "\" === \"")) {
\r
374 if(p > languageBuf+n+2 && p+8 < languageBuf+i) {
\r
375 if(j >= sizeof(english)) { DisplayError("Too many translated strings", 0); return; }
\r
376 english[j] = languageBuf + n + 1; *p = 0;
\r
377 foreign[j++] = p + 7; languageBuf[i-1] = 0;
\r
378 //if(appData.debugMode) fprintf(debugFP, "translation: replace '%s' by '%s'\n", english[j-1], foreign[j-1]);
\r
383 } else if(i > 0 && languageBuf[i-1] == '\\') {
\r
385 case 'n': k = '\n'; break;
\r
386 case 'r': k = '\r'; break;
\r
387 case 't': k = '\t'; break;
\r
389 languageBuf[--i] = k;
\r
394 barbaric = (j != 0);
\r
395 safeStrCpy(oldLanguage, buf, sizeof(oldLanguage)/sizeof(oldLanguage[0]) );
\r
400 { // return the translation of the given string
\r
401 // efficiency can be improved a lot...
\r
403 static char buf[MSG_SIZ];
\r
404 //if(appData.debugMode) fprintf(debugFP, "T_(%s)\n", s);
\r
405 if(!barbaric) return s;
\r
406 if(!s) return ""; // sanity
\r
407 while(english[i]) {
\r
408 if(!strcmp(s, english[i])) return foreign[i];
\r
409 if(english[i][0] == '%' && strstr(s, english[i]+1) == s) { // allow translation of strings with variable ending
\r
410 snprintf(buf, MSG_SIZ, "%s%s", foreign[i], s + strlen(english[i]+1)); // keep unmatched portion
\r
419 Translate(HWND hDlg, int dialogID)
\r
420 { // translate all text items in the given dialog
\r
422 char buf[MSG_SIZ], *s;
\r
423 if(!barbaric) return;
\r
424 while(dialogItems[i][0] && dialogItems[i][0] != dialogID) i++; // find the dialog description
\r
425 if(dialogItems[i][0] != dialogID) return; // unknown dialog, should not happen
\r
426 GetWindowText( hDlg, buf, MSG_SIZ );
\r
428 if(strcmp(buf, s)) SetWindowText(hDlg, s); // replace by translated string (if different)
\r
429 for(j=1; k=dialogItems[i][j]; j++) { // translate all listed dialog items
\r
430 GetDlgItemText(hDlg, k, buf, MSG_SIZ);
\r
431 if(strlen(buf) == 0) continue;
\r
433 if(strcmp(buf, s)) SetDlgItemText(hDlg, k, s); // replace by translated string (if different)
\r
438 TranslateOneMenu(int i, HMENU subMenu)
\r
441 static MENUITEMINFO info;
\r
443 info.cbSize = sizeof(MENUITEMINFO);
\r
444 info.fMask = MIIM_STATE | MIIM_TYPE;
\r
445 for(j=GetMenuItemCount(subMenu)-1; j>=0; j--){
\r
447 info.dwTypeData = buf;
\r
448 info.cch = sizeof(buf);
\r
449 GetMenuItemInfo(subMenu, j, TRUE, &info);
\r
451 if(menuText[i][j]) safeStrCpy(buf, menuText[i][j], sizeof(buf)/sizeof(buf[0]) );
\r
452 else menuText[i][j] = strdup(buf); // remember original on first change
\r
454 if(buf[0] == NULLCHAR) continue;
\r
455 info.dwTypeData = T_(buf);
\r
456 info.cch = strlen(buf)+1;
\r
457 SetMenuItemInfo(subMenu, j, TRUE, &info);
\r
463 TranslateMenus(int addLanguage)
\r
466 WIN32_FIND_DATA fileData;
\r
468 #define IDM_English 1970
\r
470 HMENU mainMenu = GetMenu(hwndMain);
\r
471 for (i=GetMenuItemCount(mainMenu)-1; i>=0; i--) {
\r
472 HMENU subMenu = GetSubMenu(mainMenu, i);
\r
473 ModifyMenu(mainMenu, i, MF_STRING|MF_BYPOSITION|MF_POPUP|EnableMenuItem(mainMenu, i, MF_BYPOSITION),
\r
474 (UINT) subMenu, T_(menuBarText[tinyLayout][i]));
\r
475 TranslateOneMenu(i, subMenu);
\r
477 DrawMenuBar(hwndMain);
\r
480 if(!addLanguage) return;
\r
481 if((hFind = FindFirstFile("*.LNG", &fileData)) != INVALID_HANDLE_VALUE) {
\r
482 HMENU mainMenu = GetMenu(hwndMain);
\r
483 HMENU subMenu = GetSubMenu(mainMenu, GetMenuItemCount(mainMenu)-1);
\r
484 AppendMenu(subMenu, MF_SEPARATOR, (UINT_PTR) 0, NULL);
\r
485 AppendMenu(subMenu, MF_ENABLED|MF_STRING|(barbaric?MF_UNCHECKED:MF_CHECKED), (UINT_PTR) IDM_English, (LPCTSTR) "English");
\r
486 i = 0; lastChecked = IDM_English;
\r
488 char *p, *q = fileData.cFileName;
\r
489 int checkFlag = MF_UNCHECKED;
\r
490 languageFile[i] = strdup(q);
\r
491 if(barbaric && !strcmp(oldLanguage, q)) {
\r
492 checkFlag = MF_CHECKED;
\r
493 lastChecked = IDM_English + i + 1;
\r
494 CheckMenuItem(mainMenu, IDM_English, MF_BYCOMMAND|MF_UNCHECKED);
\r
496 *q = ToUpper(*q); while(*++q) *q = ToLower(*q);
\r
497 p = strstr(fileData.cFileName, ".lng");
\r
499 AppendMenu(subMenu, MF_ENABLED|MF_STRING|checkFlag, (UINT_PTR) IDM_English + ++i, (LPCTSTR) fileData.cFileName);
\r
500 } while(FindNextFile(hFind, &fileData));
\r
507 #define IDM_RecentEngines 3000
\r
510 RecentEngineMenu (char *s)
\r
512 if(appData.icsActive) return;
\r
513 if(appData.recentEngines > 0 && *s) { // feature is on, and list non-empty
\r
514 HMENU mainMenu = GetMenu(hwndMain);
\r
515 HMENU subMenu = GetSubMenu(mainMenu, 5); // Engine menu
\r
516 int i=IDM_RecentEngines;
\r
517 recentEngines = strdup(appData.recentEngineList); // remember them as they are in menu
\r
518 AppendMenu(subMenu, MF_SEPARATOR, (UINT_PTR) 0, NULL);
\r
520 char *p = strchr(s, '\n');
\r
521 if(p == NULL) return; // malformed!
\r
523 AppendMenu(subMenu, MF_ENABLED|MF_STRING|MF_UNCHECKED, (UINT_PTR) i++, (LPCTSTR) s);
\r
537 int cliWidth, cliHeight;
\r
540 SizeInfo sizeInfo[] =
\r
542 { "tiny", 21, 0, 1, 2, 0, 0 },
\r
543 { "teeny", 25, 1, 1, 2, 0, 0 },
\r
544 { "dinky", 29, 1, 1, 2, 0, 0 },
\r
545 { "petite", 33, 1, 1, 2, 0, 0 },
\r
546 { "slim", 37, 2, 1, 1, 0, 0 },
\r
547 { "small", 40, 2, 1, 1, 0, 0 },
\r
548 { "mediocre", 45, 2, 1, 0, 0, 0 },
\r
549 { "middling", 49, 2, 0, 0, 0, 0 },
\r
550 { "average", 54, 2, 0, 0, 0, 0 },
\r
551 { "moderate", 58, 3, 0, 0, 0, 0 },
\r
552 { "medium", 64, 3, 0, 0, 0, 0 },
\r
553 { "bulky", 72, 3, 0, 0, 0, 0 },
\r
554 { "large", 80, 3, 0, 0, 0, 0 },
\r
555 { "big", 87, 3, 0, 0, 0, 0 },
\r
556 { "huge", 95, 3, 0, 0, 0, 0 },
\r
557 { "giant", 108, 3, 0, 0, 0, 0 },
\r
558 { "colossal", 116, 4, 0, 0, 0, 0 },
\r
559 { "titanic", 129, 4, 0, 0, 0, 0 },
\r
560 { NULL, 0, 0, 0, 0, 0, 0 }
\r
563 #define MF(x) {x, {{0,}, 0. }, {0, }, 0}
\r
564 MyFont fontRec[NUM_SIZES][NUM_FONTS] =
\r
566 { 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
567 { 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
568 { 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
569 { 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
570 { 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
571 { 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
572 { 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
573 { 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
574 { 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
575 { 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
576 { 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
577 { 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
578 { 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
579 { 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
580 { 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
581 { 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
582 { 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
583 { 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
586 MyFont *font[NUM_SIZES][NUM_FONTS];
\r
595 #define BUTTON_WIDTH (tinyLayout == 2 ? 16 : 32)
\r
596 #define N_BUTTONS 5
\r
598 MyButtonDesc buttonDesc[N_BUTTONS] =
\r
600 {"<<", IDM_ToStart, NULL, NULL},
\r
601 {"<", IDM_Backward, NULL, NULL},
\r
602 {"P", IDM_Pause, NULL, NULL},
\r
603 {">", IDM_Forward, NULL, NULL},
\r
604 {">>", IDM_ToEnd, NULL, NULL},
\r
607 int tinyLayout = 0, smallLayout = 0;
\r
608 #define MENU_BAR_ITEMS 9
\r
609 char *menuBarText[3][MENU_BAR_ITEMS+1] = {
\r
610 { N_("&File"), N_("&Edit"), N_("&View"), N_("&Mode"), N_("&Action"), N_("E&ngine"), N_("&Options"), N_("&Help"), NULL },
\r
611 { N_("&Fil"), N_("&Ed"), N_("&Vw"), N_("&Mod"), N_("&Act"), N_("E&ng"), N_("&Opt"), N_("&Hlp"), NULL },
\r
612 { N_("&F"), N_("&E"), N_("&V"), N_("&M"), N_("&A"), N_("&N"), N_("&O"), N_("&H"), NULL },
\r
616 MySound sounds[(int)NSoundClasses];
\r
617 MyTextAttribs textAttribs[(int)NColorClasses];
\r
619 MyColorizeAttribs colorizeAttribs[] = {
\r
620 { (COLORREF)0, 0, N_("Shout Text") },
\r
621 { (COLORREF)0, 0, N_("SShout/CShout") },
\r
622 { (COLORREF)0, 0, N_("Channel 1 Text") },
\r
623 { (COLORREF)0, 0, N_("Channel Text") },
\r
624 { (COLORREF)0, 0, N_("Kibitz Text") },
\r
625 { (COLORREF)0, 0, N_("Tell Text") },
\r
626 { (COLORREF)0, 0, N_("Challenge Text") },
\r
627 { (COLORREF)0, 0, N_("Request Text") },
\r
628 { (COLORREF)0, 0, N_("Seek Text") },
\r
629 { (COLORREF)0, 0, N_("Normal Text") },
\r
630 { (COLORREF)0, 0, N_("None") }
\r
635 static char *commentTitle;
\r
636 static char *commentText;
\r
637 static int commentIndex;
\r
638 static Boolean editComment = FALSE;
\r
641 char errorTitle[MSG_SIZ];
\r
642 char errorMessage[2*MSG_SIZ];
\r
643 HWND errorDialog = NULL;
\r
644 BOOLEAN moveErrorMessageUp = FALSE;
\r
645 BOOLEAN consoleEcho = TRUE;
\r
646 CHARFORMAT consoleCF;
\r
647 COLORREF consoleBackgroundColor;
\r
649 char *programVersion;
\r
655 typedef int CPKind;
\r
664 SOCKET sock2; /* stderr socket for OpenRcmd */
\r
667 #define INPUT_SOURCE_BUF_SIZE 4096
\r
669 typedef struct _InputSource {
\r
676 char buf[INPUT_SOURCE_BUF_SIZE];
\r
680 InputCallback func;
\r
681 struct _InputSource *second; /* for stderr thread on CPRcmd */
\r
685 InputSource *consoleInputSource;
\r
690 VOID ConsoleOutput(char* data, int length, int forceVisible);
\r
691 VOID ConsoleCreate();
\r
693 ConsoleWndProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam);
\r
694 VOID ColorizeTextPopup(HWND hwnd, ColorClass cc);
\r
695 VOID PrintCommSettings(FILE *f, char *name, DCB *dcb);
\r
696 VOID ParseCommSettings(char *arg, DCB *dcb);
\r
698 StartupDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam);
\r
699 VOID APIENTRY MenuPopup(HWND hwnd, POINT pt, HMENU hmenu, UINT def);
\r
700 void ParseIcsTextMenu(char *icsTextMenuString);
\r
701 VOID PopUpNameDialog(char firstchar);
\r
702 VOID UpdateSampleText(HWND hDlg, int id, MyColorizeAttribs *mca);
\r
706 int GameListOptions();
\r
708 int dummy; // [HGM] for obsolete args
\r
710 HWND hwndMain = NULL; /* root window*/
\r
711 HWND hwndConsole = NULL;
\r
712 HWND commentDialog = NULL;
\r
713 HWND moveHistoryDialog = NULL;
\r
714 HWND evalGraphDialog = NULL;
\r
715 HWND engineOutputDialog = NULL;
\r
716 HWND gameListDialog = NULL;
\r
717 HWND editTagsDialog = NULL;
\r
719 int commentUp = FALSE;
\r
721 WindowPlacement wpMain;
\r
722 WindowPlacement wpConsole;
\r
723 WindowPlacement wpComment;
\r
724 WindowPlacement wpMoveHistory;
\r
725 WindowPlacement wpEvalGraph;
\r
726 WindowPlacement wpEngineOutput;
\r
727 WindowPlacement wpGameList;
\r
728 WindowPlacement wpTags;
\r
730 VOID EngineOptionsPopup(); // [HGM] settings
\r
732 VOID GothicPopUp(char *title, VariantClass variant);
\r
734 * Setting "frozen" should disable all user input other than deleting
\r
735 * the window. We do this while engines are initializing themselves.
\r
737 static int frozen = 0;
\r
738 static int oldMenuItemState[MENU_BAR_ITEMS];
\r
744 if (frozen) return;
\r
746 hmenu = GetMenu(hwndMain);
\r
747 for (i=0; i<MENU_BAR_ITEMS; i++) {
\r
748 oldMenuItemState[i] = EnableMenuItem(hmenu, i, MF_BYPOSITION|MF_GRAYED);
\r
750 DrawMenuBar(hwndMain);
\r
753 /* Undo a FreezeUI */
\r
759 if (!frozen) return;
\r
761 hmenu = GetMenu(hwndMain);
\r
762 for (i=0; i<MENU_BAR_ITEMS; i++) {
\r
763 EnableMenuItem(hmenu, i, MF_BYPOSITION|oldMenuItemState[i]);
\r
765 DrawMenuBar(hwndMain);
\r
768 /*static*/ int fromX = -1, fromY = -1, toX, toY; // [HGM] moved upstream, so JAWS can use them
\r
770 /* JAWS preparation patch (WinBoard for the sight impaired). Define required insertions as empty */
\r
776 #define JAWS_ALT_INTERCEPT
\r
777 #define JAWS_KBUP_NAVIGATION
\r
778 #define JAWS_KBDOWN_NAVIGATION
\r
779 #define JAWS_MENU_ITEMS
\r
780 #define JAWS_SILENCE
\r
781 #define JAWS_REPLAY
\r
783 #define JAWS_COPYRIGHT
\r
784 #define JAWS_DELETE(X) X
\r
785 #define SAYMACHINEMOVE()
\r
789 /*---------------------------------------------------------------------------*\
\r
793 \*---------------------------------------------------------------------------*/
\r
795 static void HandleMessage P((MSG *message));
\r
796 static HANDLE hAccelMain, hAccelNoAlt, hAccelNoICS;
\r
799 WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
\r
800 LPSTR lpCmdLine, int nCmdShow)
\r
803 // INITCOMMONCONTROLSEX ex;
\r
807 LoadLibrary("RICHED32.DLL");
\r
808 consoleCF.cbSize = sizeof(CHARFORMAT);
\r
810 if (!InitApplication(hInstance)) {
\r
813 if (!InitInstance(hInstance, nCmdShow, lpCmdLine)) {
\r
820 // InitCommonControlsEx(&ex);
\r
821 InitCommonControls();
\r
823 hAccelMain = LoadAccelerators (hInstance, szAppName);
\r
824 hAccelNoAlt = LoadAccelerators (hInstance, "NO_ALT");
\r
825 hAccelNoICS = LoadAccelerators( hInstance, "NO_ICS"); /* [AS] No Ctrl-V on ICS!!! */
\r
827 /* Acquire and dispatch messages until a WM_QUIT message is received. */
\r
829 while (GetMessage(&msg, /* message structure */
\r
830 NULL, /* handle of window receiving the message */
\r
831 0, /* lowest message to examine */
\r
832 0)) /* highest message to examine */
\r
834 HandleMessage(&msg);
\r
838 return (msg.wParam); /* Returns the value from PostQuitMessage */
\r
842 HandleMessage (MSG *message)
\r
844 MSG msg = *message;
\r
846 if(msg.message == WM_CHAR && msg.wParam == '\t') {
\r
847 // [HGM] navigate: switch between all windows with tab
\r
848 HWND e1 = NULL, e2 = NULL, mh = NULL, hInput = NULL, hText = NULL;
\r
849 int i, currentElement = 0;
\r
851 // first determine what element of the chain we come from (if any)
\r
852 if(appData.icsActive) {
\r
853 hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
854 hText = GetDlgItem(hwndConsole, OPT_ConsoleText);
\r
856 if(engineOutputDialog && EngineOutputIsUp()) {
\r
857 e1 = GetDlgItem(engineOutputDialog, IDC_EngineMemo1);
\r
858 e2 = GetDlgItem(engineOutputDialog, IDC_EngineMemo2);
\r
860 if(moveHistoryDialog && MoveHistoryIsUp()) {
\r
861 mh = GetDlgItem(moveHistoryDialog, IDC_MoveHistory);
\r
863 if(msg.hwnd == hwndMain) currentElement = 7 ; else
\r
864 if(msg.hwnd == engineOutputDialog) currentElement = 2; else
\r
865 if(msg.hwnd == e1) currentElement = 2; else
\r
866 if(msg.hwnd == e2) currentElement = 3; else
\r
867 if(msg.hwnd == moveHistoryDialog) currentElement = 4; else
\r
868 if(msg.hwnd == mh) currentElement = 4; else
\r
869 if(msg.hwnd == evalGraphDialog) currentElement = 6; else
\r
870 if(msg.hwnd == hText) currentElement = 5; else
\r
871 if(msg.hwnd == hInput) currentElement = 6; else
\r
872 for (i = 0; i < N_BUTTONS; i++) {
\r
873 if (buttonDesc[i].hwnd == msg.hwnd) { currentElement = 1; break; }
\r
876 // determine where to go to
\r
877 if(currentElement) { HWND h = NULL; int direction = GetKeyState(VK_SHIFT) < 0 ? -1 : 1;
\r
879 currentElement = (currentElement + direction) % 7;
\r
880 switch(currentElement) {
\r
882 h = hwndMain; break; // passing this case always makes the loop exit
\r
884 h = buttonDesc[0].hwnd; break; // could be NULL
\r
886 if(!EngineOutputIsUp()) continue; // skip closed auxiliary windows
\r
889 if(!EngineOutputIsUp()) continue;
\r
892 if(!MoveHistoryIsUp()) continue;
\r
894 // case 6: // input to eval graph does not seem to get here!
\r
895 // if(!EvalGraphIsUp()) continue;
\r
896 // h = evalGraphDialog; break;
\r
898 if(!appData.icsActive) continue;
\r
902 if(!appData.icsActive) continue;
\r
908 if(currentElement > 4 && IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);
\r
909 if(currentElement < 5 && IsIconic(hwndMain)) ShowWindow(hwndMain, SW_RESTORE); // all open together
\r
912 return; // this message now has been processed
\r
916 if (!(commentDialog && IsDialogMessage(commentDialog, &msg)) &&
\r
917 !(moveHistoryDialog && IsDialogMessage(moveHistoryDialog, &msg)) &&
\r
918 !(evalGraphDialog && IsDialogMessage(evalGraphDialog, &msg)) &&
\r
919 !(engineOutputDialog && IsDialogMessage(engineOutputDialog, &msg)) &&
\r
920 !(editTagsDialog && IsDialogMessage(editTagsDialog, &msg)) &&
\r
921 !(gameListDialog && IsDialogMessage(gameListDialog, &msg)) &&
\r
922 !(errorDialog && IsDialogMessage(errorDialog, &msg)) &&
\r
923 !(!frozen && TranslateAccelerator(hwndMain, hAccelMain, &msg)) && JAWS_ACCEL
\r
924 !(!hwndConsole && TranslateAccelerator(hwndMain, hAccelNoICS, &msg)) &&
\r
925 !(!hwndConsole && TranslateAccelerator(hwndMain, hAccelNoAlt, &msg))) {
\r
926 int done = 0, i; // [HGM] chat: dispatch cat-box messages
\r
927 for(i=0; i<MAX_CHAT; i++)
\r
928 if(chatHandle[i] && IsDialogMessage(chatHandle[i], &msg)) {
\r
931 if(done) return; // [HGM] chat: end patch
\r
932 TranslateMessage(&msg); /* Translates virtual key codes */
\r
933 DispatchMessage(&msg); /* Dispatches message to window */
\r
939 { /* Dispatch pending messages */
\r
941 while (PeekMessage(&msg, /* message structure */
\r
942 NULL, /* handle of window receiving the message */
\r
943 0, /* lowest message to examine */
\r
944 0, /* highest message to examine */
\r
947 HandleMessage(&msg);
\r
951 /*---------------------------------------------------------------------------*\
\r
953 * Initialization functions
\r
955 \*---------------------------------------------------------------------------*/
\r
959 { // update user logo if necessary
\r
960 static char oldUserName[MSG_SIZ], dir[MSG_SIZ], *curName;
\r
962 if(appData.autoLogo) {
\r
963 curName = UserName();
\r
964 if(strcmp(curName, oldUserName)) {
\r
965 GetCurrentDirectory(MSG_SIZ, dir);
\r
966 SetCurrentDirectory(installDir);
\r
967 snprintf(oldUserName, MSG_SIZ, "logos\\%s.bmp", curName);
\r
968 userLogo = LoadImage( 0, oldUserName, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );
\r
969 safeStrCpy(oldUserName, curName, sizeof(oldUserName)/sizeof(oldUserName[0]) );
\r
970 if(userLogo == NULL)
\r
971 userLogo = LoadImage( 0, "logos\\dummy.bmp", IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );
\r
972 SetCurrentDirectory(dir); /* return to prev directory */
\r
978 InitApplication(HINSTANCE hInstance)
\r
982 /* Fill in window class structure with parameters that describe the */
\r
985 wc.style = CS_HREDRAW | CS_VREDRAW; /* Class style(s). */
\r
986 wc.lpfnWndProc = (WNDPROC)WndProc; /* Window Procedure */
\r
987 wc.cbClsExtra = 0; /* No per-class extra data. */
\r
988 wc.cbWndExtra = 0; /* No per-window extra data. */
\r
989 wc.hInstance = hInstance; /* Owner of this class */
\r
990 wc.hIcon = LoadIcon(hInstance, "icon_white");
\r
991 wc.hCursor = LoadCursor(NULL, IDC_ARROW); /* Cursor */
\r
992 wc.hbrBackground = (HBRUSH)(COLOR_WINDOW+1); /* Default color */
\r
993 wc.lpszMenuName = szAppName; /* Menu name from .RC */
\r
994 wc.lpszClassName = szAppName; /* Name to register as */
\r
996 /* Register the window class and return success/failure code. */
\r
997 if (!RegisterClass(&wc)) return FALSE;
\r
999 wc.style = CS_HREDRAW | CS_VREDRAW;
\r
1000 wc.lpfnWndProc = (WNDPROC)ConsoleWndProc;
\r
1001 wc.cbClsExtra = 0;
\r
1002 wc.cbWndExtra = DLGWINDOWEXTRA;
\r
1003 wc.hInstance = hInstance;
\r
1004 wc.hIcon = LoadIcon(hInstance, "icon_white");
\r
1005 wc.hCursor = LoadCursor(NULL, IDC_ARROW);
\r
1006 wc.hbrBackground = (HBRUSH)(COLOR_MENU+1);
\r
1007 wc.lpszMenuName = NULL;
\r
1008 wc.lpszClassName = szConsoleName;
\r
1010 if (!RegisterClass(&wc)) return FALSE;
\r
1015 /* Set by InitInstance, used by EnsureOnScreen */
\r
1016 int screenHeight, screenWidth;
\r
1017 RECT screenGeometry;
\r
1020 EnsureOnScreen(int *x, int *y, int minX, int minY)
\r
1022 // int gap = GetSystemMetrics(SM_CYFRAME) + GetSystemMetrics(SM_CYCAPTION);
\r
1023 /* Be sure window at (x,y) is not off screen (or even mostly off screen) */
\r
1024 if (*x > screenGeometry.right - 32) *x = screenGeometry.left;
\r
1025 if (*y > screenGeometry.bottom - 32) *y = screenGeometry.top;
\r
1026 if (*x < screenGeometry.left + minX) *x = screenGeometry.left + minX;
\r
1027 if (*y < screenGeometry.top + minY) *y = screenGeometry.top + minY;
\r
1031 LoadLogo(ChessProgramState *cps, int n, Boolean ics)
\r
1033 char buf[MSG_SIZ], dir[MSG_SIZ];
\r
1034 GetCurrentDirectory(MSG_SIZ, dir);
\r
1035 SetCurrentDirectory(installDir);
\r
1036 if( appData.logo[n] && appData.logo[n][0] != NULLCHAR) {
\r
1037 cps->programLogo = LoadImage( 0, appData.logo[n], IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );
\r
1039 if (cps->programLogo == NULL && appData.debugMode) {
\r
1040 fprintf( debugFP, "Unable to load logo bitmap '%s'\n", appData.logo[n] );
\r
1042 } else if(appData.autoLogo) {
\r
1043 if(ics) { // [HGM] logo: in ICS mode second can be used for ICS
\r
1044 char *opponent = "";
\r
1045 if(gameMode == IcsPlayingWhite) opponent = gameInfo.black;
\r
1046 if(gameMode == IcsPlayingBlack) opponent = gameInfo.white;
\r
1047 sprintf(buf, "logos\\%s\\%s.bmp", appData.icsHost, opponent);
\r
1048 if(!*opponent || !(cps->programLogo = LoadImage( 0, buf, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE ))) {
\r
1049 sprintf(buf, "logos\\%s.bmp", appData.icsHost);
\r
1050 cps->programLogo = LoadImage( 0, buf, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );
\r
1053 if(appData.directory[n] && appData.directory[n][0]) {
\r
1054 SetCurrentDirectory(appData.directory[n]);
\r
1055 cps->programLogo = LoadImage( 0, "logo.bmp", IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );
\r
1058 SetCurrentDirectory(dir); /* return to prev directory */
\r
1064 ZeroMemory( &backTextureSquareInfo, sizeof(backTextureSquareInfo) );
\r
1065 backTextureSquareSize = 0; // kludge to force recalculation of texturemode
\r
1067 if( appData.liteBackTextureFile && appData.liteBackTextureFile[0] != NULLCHAR && appData.liteBackTextureFile[0] != '*' ) {
\r
1068 if(liteBackTexture) DeleteObject(liteBackTexture);
\r
1069 liteBackTexture = LoadImage( 0, appData.liteBackTextureFile, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );
\r
1070 liteBackTextureMode = appData.liteBackTextureMode;
\r
1072 if (liteBackTexture == NULL && appData.debugMode) {
\r
1073 fprintf( debugFP, "Unable to load lite texture bitmap '%s'\n", appData.liteBackTextureFile );
\r
1077 if( appData.darkBackTextureFile && appData.darkBackTextureFile[0] != NULLCHAR && appData.darkBackTextureFile[0] != '*' ) {
\r
1078 if(darkBackTexture) DeleteObject(darkBackTexture);
\r
1079 darkBackTexture = LoadImage( 0, appData.darkBackTextureFile, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );
\r
1080 darkBackTextureMode = appData.darkBackTextureMode;
\r
1082 if (darkBackTexture == NULL && appData.debugMode) {
\r
1083 fprintf( debugFP, "Unable to load dark texture bitmap '%s'\n", appData.darkBackTextureFile );
\r
1088 #ifndef SM_CXVIRTUALSCREEN
\r
1089 #define SM_CXVIRTUALSCREEN 78
\r
1091 #ifndef SM_CYVIRTUALSCREEN
\r
1092 #define SM_CYVIRTUALSCREEN 79
\r
1094 #ifndef SM_XVIRTUALSCREEN
\r
1095 #define SM_XVIRTUALSCREEN 76
\r
1097 #ifndef SM_YVIRTUALSCREEN
\r
1098 #define SM_YVIRTUALSCREEN 77
\r
1104 screenHeight = GetSystemMetrics(SM_CYVIRTUALSCREEN);
\r
1105 if( !screenHeight ) screenHeight = GetSystemMetrics(SM_CYSCREEN);
\r
1106 screenWidth = GetSystemMetrics(SM_CXVIRTUALSCREEN);
\r
1107 if( !screenWidth ) screenWidth = GetSystemMetrics(SM_CXSCREEN);
\r
1108 screenGeometry.left = GetSystemMetrics(SM_XVIRTUALSCREEN);
\r
1109 screenGeometry.top = GetSystemMetrics(SM_YVIRTUALSCREEN);
\r
1110 screenGeometry.right = screenGeometry.left + screenWidth;
\r
1111 screenGeometry.bottom = screenGeometry.top + screenHeight;
\r
1114 ChessProgramState broadcast;
\r
1117 InitInstance(HINSTANCE hInstance, int nCmdShow, LPSTR lpCmdLine)
\r
1119 HWND hwnd; /* Main window handle. */
\r
1121 WINDOWPLACEMENT wp;
\r
1124 hInst = hInstance; /* Store instance handle in our global variable */
\r
1125 programName = szAppName;
\r
1127 if (SearchPath(NULL, "WinBoard.exe", NULL, MSG_SIZ, installDir, &filepart)) {
\r
1128 *filepart = NULLCHAR;
\r
1129 SetCurrentDirectory(installDir);
\r
1131 GetCurrentDirectory(MSG_SIZ, installDir);
\r
1133 gameInfo.boardWidth = gameInfo.boardHeight = 8; // [HGM] won't have open window otherwise
\r
1135 InitAppData(lpCmdLine); /* Get run-time parameters */
\r
1136 /* xboard, and older WinBoards, controlled the move sound with the
\r
1137 appData.ringBellAfterMoves option. In the current WinBoard, we
\r
1138 always turn the option on (so that the backend will call us),
\r
1139 then let the user turn the sound off by setting it to silence if
\r
1140 desired. To accommodate old winboard.ini files saved by old
\r
1141 versions of WinBoard, we also turn off the sound if the option
\r
1142 was initially set to false. [HGM] taken out of InitAppData */
\r
1143 if (!appData.ringBellAfterMoves) {
\r
1144 sounds[(int)SoundMove].name = strdup("");
\r
1145 appData.ringBellAfterMoves = TRUE;
\r
1147 if (appData.debugMode) {
\r
1148 char *c = appData.nameOfDebugFile;
\r
1149 if(strstr(c, "///") == c) {
\r
1150 broadcast.which = "broadcaster";
\r
1151 broadcast.pr = NoProc;
\r
1152 broadcast.isr = NULL;
\r
1153 broadcast.program = c + 3;
\r
1154 broadcast.dir = ".";
\r
1155 broadcast.host = "localhost";
\r
1156 StartChessProgram(&broadcast);
\r
1157 debugFP = (FILE*) _fdopen(_open_osfhandle((long)(((ChildProc*)(broadcast.pr))->hTo), _O_WRONLY), "w");
\r
1159 debugFP = fopen(c, "w");
\r
1160 setbuf(debugFP, NULL);
\r
1163 LoadLanguageFile(appData.language);
\r
1167 // InitEngineUCI( installDir, &first ); // [HGM] incorporated in InitBackEnd1()
\r
1168 // InitEngineUCI( installDir, &second );
\r
1170 /* Create a main window for this application instance. */
\r
1171 hwnd = CreateWindow(szAppName, szTitle,
\r
1172 (WS_OVERLAPPEDWINDOW & ~WS_MAXIMIZEBOX),
\r
1173 CW_USEDEFAULT, 0, CW_USEDEFAULT, 0,
\r
1174 NULL, NULL, hInstance, NULL);
\r
1177 /* If window could not be created, return "failure" */
\r
1182 /* [HGM] logo: Load logos if specified (must be done before InitDrawingSizes) */
\r
1183 LoadLogo(&first, 0, FALSE);
\r
1184 LoadLogo(&second, 1, appData.icsActive);
\r
1188 iconWhite = LoadIcon(hInstance, "icon_white");
\r
1189 iconBlack = LoadIcon(hInstance, "icon_black");
\r
1190 iconCurrent = iconWhite;
\r
1191 InitDrawingColors();
\r
1193 InitPosition(0); // to set nr of ranks and files, which might be non-default through command-line args
\r
1194 for (ibs = (int) NUM_SIZES - 1; ibs >= 0; ibs--) {
\r
1195 /* Compute window size for each board size, and use the largest
\r
1196 size that fits on this screen as the default. */
\r
1197 InitDrawingSizes((BoardSize)(ibs+1000), 0);
\r
1198 if (boardSize == (BoardSize)-1 &&
\r
1199 winH <= screenHeight
\r
1200 - GetSystemMetrics(SM_CYFRAME) - GetSystemMetrics(SM_CYCAPTION) - 10
\r
1201 && winW <= screenWidth) {
\r
1202 boardSize = (BoardSize)ibs;
\r
1206 InitDrawingSizes(boardSize, 0);
\r
1207 RecentEngineMenu(appData.recentEngineList);
\r
1209 buttonCount = GetSystemMetrics(SM_CMOUSEBUTTONS);
\r
1211 /* [AS] Load textures if specified */
\r
1214 mysrandom( (unsigned) time(NULL) );
\r
1216 /* [AS] Restore layout */
\r
1217 if( wpMoveHistory.visible ) {
\r
1218 MoveHistoryPopUp();
\r
1221 if( wpEvalGraph.visible ) {
\r
1225 if( wpEngineOutput.visible ) {
\r
1226 EngineOutputPopUp();
\r
1229 /* Make the window visible; update its client area; and return "success" */
\r
1230 EnsureOnScreen(&wpMain.x, &wpMain.y, minX, minY);
\r
1231 wp.length = sizeof(WINDOWPLACEMENT);
\r
1233 wp.showCmd = nCmdShow;
\r
1234 wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;
\r
1235 wp.rcNormalPosition.left = wpMain.x;
\r
1236 wp.rcNormalPosition.right = wpMain.x + wpMain.width;
\r
1237 wp.rcNormalPosition.top = wpMain.y;
\r
1238 wp.rcNormalPosition.bottom = wpMain.y + wpMain.height;
\r
1239 SetWindowPlacement(hwndMain, &wp);
\r
1241 InitBackEnd2(); // [HGM] moved until after all windows placed, to save correct position if fatal error on engine start
\r
1243 if(!appData.noGUI) SetWindowPos(hwndMain, alwaysOnTop ? HWND_TOPMOST : HWND_NOTOPMOST,
\r
1244 0, 0, 0, 0, SWP_NOMOVE|SWP_NOSIZE);
\r
1246 if (hwndConsole) {
\r
1248 SetWindowPos(hwndConsole, alwaysOnTop ? HWND_TOPMOST : HWND_NOTOPMOST,
\r
1249 0, 0, 0, 0, SWP_NOMOVE|SWP_NOSIZE);
\r
1251 ShowWindow(hwndConsole, nCmdShow);
\r
1252 SetActiveWindow(hwndConsole);
\r
1254 if(!appData.noGUI) UpdateWindow(hwnd); else ShowWindow(hwnd, SW_MINIMIZE);
\r
1255 if(gameListDialog) SetFocus(gameListDialog); // [HGM] jaws: for if we clicked multi-game game file
\r
1264 HMENU hmenu = GetMenu(hwndMain);
\r
1266 (void) EnableMenuItem(hmenu, IDM_CommPort,
\r
1267 MF_BYCOMMAND|((appData.icsActive &&
\r
1268 *appData.icsCommPort != NULLCHAR) ?
\r
1269 MF_ENABLED : MF_GRAYED));
\r
1270 (void) CheckMenuItem(hmenu, IDM_SaveSettingsOnExit,
\r
1271 MF_BYCOMMAND|(saveSettingsOnExit ?
\r
1272 MF_CHECKED : MF_UNCHECKED));
\r
1273 EnableMenuItem(hmenu, IDM_SaveSelected, MF_GRAYED);
\r
1276 //---------------------------------------------------------------------------------------------------------
\r
1278 #define ICS_TEXT_MENU_SIZE (IDM_CommandXLast - IDM_CommandX + 1)
\r
1279 #define XBOARD FALSE
\r
1281 #define OPTCHAR "/"
\r
1282 #define SEPCHAR "="
\r
1283 #define TOPLEVEL 0
\r
1287 // front-end part of option handling
\r
1290 LFfromMFP(LOGFONT* lf, MyFontParams *mfp)
\r
1292 HDC hdc = CreateDC("DISPLAY", NULL, NULL, NULL);
\r
1293 lf->lfHeight = -(int)(mfp->pointSize * GetDeviceCaps(hdc, LOGPIXELSY) / 72.0 + 0.5);
\r
1296 lf->lfEscapement = 0;
\r
1297 lf->lfOrientation = 0;
\r
1298 lf->lfWeight = mfp->bold ? FW_BOLD : FW_NORMAL;
\r
1299 lf->lfItalic = mfp->italic;
\r
1300 lf->lfUnderline = mfp->underline;
\r
1301 lf->lfStrikeOut = mfp->strikeout;
\r
1302 lf->lfCharSet = mfp->charset;
\r
1303 lf->lfOutPrecision = OUT_DEFAULT_PRECIS;
\r
1307 lf->lfClipPrecision = CLIP_DEFAULT_PRECIS;
\r
1308 lf->lfQuality = DEFAULT_QUALITY;
\r
1309 lf->lfPitchAndFamily = DEFAULT_PITCH|FF_DONTCARE;
\r
1310 safeStrCpy(lf->lfFaceName, mfp->faceName, sizeof(lf->lfFaceName)/sizeof(lf->lfFaceName[0]) );
\r
1314 CreateFontInMF(MyFont *mf)
\r
1316 LFfromMFP(&mf->lf, &mf->mfp);
\r
1317 if (mf->hf) DeleteObject(mf->hf);
\r
1318 mf->hf = CreateFontIndirect(&mf->lf);
\r
1321 // [HGM] This platform-dependent table provides the location for storing the color info
\r
1323 colorVariable[] = {
\r
1324 &whitePieceColor,
\r
1325 &blackPieceColor,
\r
1326 &lightSquareColor,
\r
1327 &darkSquareColor,
\r
1328 &highlightSquareColor,
\r
1329 &premoveHighlightColor,
\r
1331 &consoleBackgroundColor,
\r
1332 &appData.fontForeColorWhite,
\r
1333 &appData.fontBackColorWhite,
\r
1334 &appData.fontForeColorBlack,
\r
1335 &appData.fontBackColorBlack,
\r
1336 &appData.evalHistColorWhite,
\r
1337 &appData.evalHistColorBlack,
\r
1338 &appData.highlightArrowColor,
\r
1341 /* Command line font name parser. NULL name means do nothing.
\r
1342 Syntax like "Courier New:10.0 bi" or "Arial:10" or "Arial:10b"
\r
1343 For backward compatibility, syntax without the colon is also
\r
1344 accepted, but font names with digits in them won't work in that case.
\r
1347 ParseFontName(char *name, MyFontParams *mfp)
\r
1350 if (name == NULL) return;
\r
1352 q = strchr(p, ':');
\r
1354 if (q - p >= sizeof(mfp->faceName))
\r
1355 ExitArgError(_("Font name too long:"), name, TRUE);
\r
1356 memcpy(mfp->faceName, p, q - p);
\r
1357 mfp->faceName[q - p] = NULLCHAR;
\r
1360 q = mfp->faceName;
\r
1362 while (*p && !isdigit(*p)) {
\r
1364 if (q - mfp->faceName >= sizeof(mfp->faceName))
\r
1365 ExitArgError(_("Font name too long:"), name, TRUE);
\r
1367 while (q > mfp->faceName && q[-1] == ' ') q--;
\r
1370 if (!*p) ExitArgError(_("Font point size missing:"), name, TRUE);
\r
1371 mfp->pointSize = (float) atof(p);
\r
1372 mfp->bold = (strchr(p, 'b') != NULL);
\r
1373 mfp->italic = (strchr(p, 'i') != NULL);
\r
1374 mfp->underline = (strchr(p, 'u') != NULL);
\r
1375 mfp->strikeout = (strchr(p, 's') != NULL);
\r
1376 mfp->charset = DEFAULT_CHARSET;
\r
1377 q = strchr(p, 'c');
\r
1379 mfp->charset = (BYTE) atoi(q+1);
\r
1383 ParseFont(char *name, int number)
\r
1384 { // wrapper to shield back-end from 'font'
\r
1385 ParseFontName(name, &font[boardSize][number]->mfp);
\r
1390 { // in WB we have a 2D array of fonts; this initializes their description
\r
1392 /* Point font array elements to structures and
\r
1393 parse default font names */
\r
1394 for (i=0; i<NUM_FONTS; i++) {
\r
1395 for (j=0; j<NUM_SIZES; j++) {
\r
1396 font[j][i] = &fontRec[j][i];
\r
1397 ParseFontName(font[j][i]->def, &font[j][i]->mfp);
\r
1404 { // here we create the actual fonts from the selected descriptions
\r
1406 for (i=0; i<NUM_FONTS; i++) {
\r
1407 for (j=0; j<NUM_SIZES; j++) {
\r
1408 CreateFontInMF(font[j][i]);
\r
1412 /* Color name parser.
\r
1413 X version accepts X color names, but this one
\r
1414 handles only the #rrggbb form (hex) or rrr,ggg,bbb (decimal) */
\r
1416 ParseColorName(char *name)
\r
1418 int red, green, blue, count;
\r
1419 char buf[MSG_SIZ];
\r
1421 count = sscanf(name, "#%2x%2x%2x", &red, &green, &blue);
\r
1423 count = sscanf(name, "%3d%*[^0-9]%3d%*[^0-9]%3d",
\r
1424 &red, &green, &blue);
\r
1427 snprintf(buf, MSG_SIZ, _("Can't parse color name %s"), name);
\r
1428 DisplayError(buf, 0);
\r
1429 return RGB(0, 0, 0);
\r
1431 return PALETTERGB(red, green, blue);
\r
1435 ParseColor(int n, char *name)
\r
1436 { // for WinBoard the color is an int, which needs to be derived from the string
\r
1437 if(colorVariable[n]) *(int*)colorVariable[n] = ParseColorName(name);
\r
1441 ParseAttribs(COLORREF *color, int *effects, char* argValue)
\r
1443 char *e = argValue;
\r
1447 if (*e == 'b') eff |= CFE_BOLD;
\r
1448 else if (*e == 'i') eff |= CFE_ITALIC;
\r
1449 else if (*e == 'u') eff |= CFE_UNDERLINE;
\r
1450 else if (*e == 's') eff |= CFE_STRIKEOUT;
\r
1451 else if (*e == '#' || isdigit(*e)) break;
\r
1455 *color = ParseColorName(e);
\r
1459 ParseTextAttribs(ColorClass cc, char *s)
\r
1460 { // [HGM] front-end wrapper that does the platform-dependent call
\r
1461 // for XBoard we would set (&appData.colorShout)[cc] = strdup(s);
\r
1462 ParseAttribs(&textAttribs[cc].color, &textAttribs[cc].effects, s);
\r
1466 ParseBoardSize(void *addr, char *name)
\r
1467 { // [HGM] rewritten with return-value ptr to shield back-end from BoardSize
\r
1468 BoardSize bs = SizeTiny;
\r
1469 while (sizeInfo[bs].name != NULL) {
\r
1470 if (StrCaseCmp(name, sizeInfo[bs].name) == 0) {
\r
1471 *(BoardSize *)addr = bs;
\r
1476 ExitArgError(_("Unrecognized board size value"), name, TRUE);
\r
1481 { // [HGM] import name from appData first
\r
1484 for (cc = (ColorClass)0; cc < ColorNormal; cc++) {
\r
1485 textAttribs[cc].sound.name = strdup((&appData.soundShout)[cc]);
\r
1486 textAttribs[cc].sound.data = NULL;
\r
1487 MyLoadSound(&textAttribs[cc].sound);
\r
1489 for (cc = ColorNormal; cc < NColorClasses; cc++) {
\r
1490 textAttribs[cc].sound.name = strdup("");
\r
1491 textAttribs[cc].sound.data = NULL;
\r
1493 for (sc = (SoundClass)0; sc < NSoundClasses; sc++) {
\r
1494 sounds[sc].name = strdup((&appData.soundMove)[sc]);
\r
1495 sounds[sc].data = NULL;
\r
1496 MyLoadSound(&sounds[sc]);
\r
1501 SetCommPortDefaults()
\r
1503 memset(&dcb, 0, sizeof(DCB)); // required by VS 2002 +
\r
1504 dcb.DCBlength = sizeof(DCB);
\r
1505 dcb.BaudRate = 9600;
\r
1506 dcb.fBinary = TRUE;
\r
1507 dcb.fParity = FALSE;
\r
1508 dcb.fOutxCtsFlow = FALSE;
\r
1509 dcb.fOutxDsrFlow = FALSE;
\r
1510 dcb.fDtrControl = DTR_CONTROL_ENABLE;
\r
1511 dcb.fDsrSensitivity = FALSE;
\r
1512 dcb.fTXContinueOnXoff = TRUE;
\r
1513 dcb.fOutX = FALSE;
\r
1515 dcb.fNull = FALSE;
\r
1516 dcb.fRtsControl = RTS_CONTROL_ENABLE;
\r
1517 dcb.fAbortOnError = FALSE;
\r
1519 dcb.Parity = SPACEPARITY;
\r
1520 dcb.StopBits = ONESTOPBIT;
\r
1523 // [HGM] args: these three cases taken out to stay in front-end
\r
1525 SaveFontArg(FILE *f, ArgDescriptor *ad)
\r
1526 { // in WinBoard every board size has its own font, and the "argLoc" identifies the table,
\r
1527 // while the curent board size determines the element. This system should be ported to XBoard.
\r
1528 // What the table contains pointers to, and how to print the font description, remains platform-dependent
\r
1530 for (bs=0; bs<NUM_SIZES; bs++) {
\r
1531 MyFontParams *mfp = &font[bs][(int) ad->argLoc]->mfp;
\r
1532 fprintf(f, "/size=%s ", sizeInfo[bs].name);
\r
1533 fprintf(f, "/%s=\"%s:%g%s%s%s%s%sc%d\"\n",
\r
1534 ad->argName, mfp->faceName, mfp->pointSize,
\r
1535 mfp->bold || mfp->italic || mfp->underline || mfp->strikeout ? " " : "",
\r
1536 mfp->bold ? "b" : "",
\r
1537 mfp->italic ? "i" : "",
\r
1538 mfp->underline ? "u" : "",
\r
1539 mfp->strikeout ? "s" : "",
\r
1540 (int)mfp->charset);
\r
1546 { // [HGM] copy the names from the internal WB variables to appData
\r
1549 for (cc = (ColorClass)0; cc < ColorNormal; cc++)
\r
1550 (&appData.soundShout)[cc] = textAttribs[cc].sound.name;
\r
1551 for (sc = (SoundClass)0; sc < NSoundClasses; sc++)
\r
1552 (&appData.soundMove)[sc] = sounds[sc].name;
\r
1556 SaveAttribsArg(FILE *f, ArgDescriptor *ad)
\r
1557 { // here the "argLoc" defines a table index. It could have contained the 'ta' pointer itself, though
\r
1558 MyTextAttribs* ta = &textAttribs[(ColorClass)ad->argLoc];
\r
1559 fprintf(f, "/%s=\"%s%s%s%s%s#%02lx%02lx%02lx\"\n", ad->argName,
\r
1560 (ta->effects & CFE_BOLD) ? "b" : "",
\r
1561 (ta->effects & CFE_ITALIC) ? "i" : "",
\r
1562 (ta->effects & CFE_UNDERLINE) ? "u" : "",
\r
1563 (ta->effects & CFE_STRIKEOUT) ? "s" : "",
\r
1564 (ta->effects) ? " " : "",
\r
1565 ta->color&0xff, (ta->color >> 8)&0xff, (ta->color >> 16)&0xff);
\r
1569 SaveColor(FILE *f, ArgDescriptor *ad)
\r
1570 { // in WinBoard the color is an int and has to be converted to text. In X it would be a string already?
\r
1571 COLORREF color = *(COLORREF *)colorVariable[(int)ad->argLoc];
\r
1572 fprintf(f, "/%s=#%02lx%02lx%02lx\n", ad->argName,
\r
1573 color&0xff, (color>>8)&0xff, (color>>16)&0xff);
\r
1577 SaveBoardSize(FILE *f, char *name, void *addr)
\r
1578 { // wrapper to shield back-end from BoardSize & sizeInfo
\r
1579 fprintf(f, "/%s=%s\n", name, sizeInfo[*(BoardSize *)addr].name);
\r
1583 ParseCommPortSettings(char *s)
\r
1584 { // wrapper to keep dcb from back-end
\r
1585 ParseCommSettings(s, &dcb);
\r
1590 { // wrapper to shield use of window handles from back-end (make addressible by number?)
\r
1591 GetActualPlacement(hwndMain, &wpMain);
\r
1592 GetActualPlacement(hwndConsole, &wpConsole);
\r
1593 GetActualPlacement(commentDialog, &wpComment);
\r
1594 GetActualPlacement(editTagsDialog, &wpTags);
\r
1595 GetActualPlacement(gameListDialog, &wpGameList);
\r
1596 GetActualPlacement(moveHistoryDialog, &wpMoveHistory);
\r
1597 GetActualPlacement(evalGraphDialog, &wpEvalGraph);
\r
1598 GetActualPlacement(engineOutputDialog, &wpEngineOutput);
\r
1602 PrintCommPortSettings(FILE *f, char *name)
\r
1603 { // wrapper to shield back-end from DCB
\r
1604 PrintCommSettings(f, name, &dcb);
\r
1608 MySearchPath(char *installDir, char *name, char *fullname)
\r
1610 char *dummy, buf[MSG_SIZ], *p = name, *q;
\r
1611 if(name[0]== '%') {
\r
1612 fullname[0] = 0; // [HGM] first expand any environment variables in the given name
\r
1613 while(*p == '%' && (q = strchr(p+1, '%'))) { // [HGM] recognize %*% as environment variable
\r
1614 safeStrCpy(buf, p+1, sizeof(buf)/sizeof(buf[0]) );
\r
1615 *strchr(buf, '%') = 0;
\r
1616 strcat(fullname, getenv(buf));
\r
1617 p = q+1; while(*p == '\\') { strcat(fullname, "\\"); p++; }
\r
1619 strcat(fullname, p); // after environment variables (if any), take the remainder of the given name
\r
1620 if(appData.debugMode) fprintf(debugFP, "name = '%s', expanded name = '%s'\n", name, fullname);
\r
1621 return (int) strlen(fullname);
\r
1623 return (int) SearchPath(installDir, name, NULL, MSG_SIZ, fullname, &dummy);
\r
1627 MyGetFullPathName(char *name, char *fullname)
\r
1630 return (int) GetFullPathName(name, MSG_SIZ, fullname, &dummy);
\r
1635 { // [HGM] args: allows testing if main window is realized from back-end
\r
1636 return hwndMain != NULL;
\r
1640 PopUpStartupDialog()
\r
1644 LoadLanguageFile(appData.language);
\r
1645 lpProc = MakeProcInstance((FARPROC)StartupDialog, hInst);
\r
1646 DialogBox(hInst, MAKEINTRESOURCE(DLG_Startup), NULL, (DLGPROC)lpProc);
\r
1647 FreeProcInstance(lpProc);
\r
1650 /*---------------------------------------------------------------------------*\
\r
1652 * GDI board drawing routines
\r
1654 \*---------------------------------------------------------------------------*/
\r
1656 /* [AS] Draw square using background texture */
\r
1657 static void DrawTile( int dx, int dy, int dw, int dh, HDC dst, HDC src, int mode, int sx, int sy )
\r
1662 return; /* Should never happen! */
\r
1665 SetGraphicsMode( dst, GM_ADVANCED );
\r
1672 /* X reflection */
\r
1677 x.eDx = (FLOAT) dw + dx - 1;
\r
1680 SetWorldTransform( dst, &x );
\r
1683 /* Y reflection */
\r
1689 x.eDy = (FLOAT) dh + dy - 1;
\r
1691 SetWorldTransform( dst, &x );
\r
1699 x.eDx = (FLOAT) dx;
\r
1700 x.eDy = (FLOAT) dy;
\r
1703 SetWorldTransform( dst, &x );
\r
1707 BitBlt( dst, dx, dy, dw, dh, src, sx, sy, SRCCOPY );
\r
1715 SetWorldTransform( dst, &x );
\r
1717 ModifyWorldTransform( dst, 0, MWT_IDENTITY );
\r
1720 /* [AS] [HGM] Make room for more piece types, so all pieces can be different */
\r
1722 PM_WP = (int) WhitePawn,
\r
1723 PM_WN = (int) WhiteKnight,
\r
1724 PM_WB = (int) WhiteBishop,
\r
1725 PM_WR = (int) WhiteRook,
\r
1726 PM_WQ = (int) WhiteQueen,
\r
1727 PM_WF = (int) WhiteFerz,
\r
1728 PM_WW = (int) WhiteWazir,
\r
1729 PM_WE = (int) WhiteAlfil,
\r
1730 PM_WM = (int) WhiteMan,
\r
1731 PM_WO = (int) WhiteCannon,
\r
1732 PM_WU = (int) WhiteUnicorn,
\r
1733 PM_WH = (int) WhiteNightrider,
\r
1734 PM_WA = (int) WhiteAngel,
\r
1735 PM_WC = (int) WhiteMarshall,
\r
1736 PM_WAB = (int) WhiteCardinal,
\r
1737 PM_WD = (int) WhiteDragon,
\r
1738 PM_WL = (int) WhiteLance,
\r
1739 PM_WS = (int) WhiteCobra,
\r
1740 PM_WV = (int) WhiteFalcon,
\r
1741 PM_WSG = (int) WhiteSilver,
\r
1742 PM_WG = (int) WhiteGrasshopper,
\r
1743 PM_WK = (int) WhiteKing,
\r
1744 PM_BP = (int) BlackPawn,
\r
1745 PM_BN = (int) BlackKnight,
\r
1746 PM_BB = (int) BlackBishop,
\r
1747 PM_BR = (int) BlackRook,
\r
1748 PM_BQ = (int) BlackQueen,
\r
1749 PM_BF = (int) BlackFerz,
\r
1750 PM_BW = (int) BlackWazir,
\r
1751 PM_BE = (int) BlackAlfil,
\r
1752 PM_BM = (int) BlackMan,
\r
1753 PM_BO = (int) BlackCannon,
\r
1754 PM_BU = (int) BlackUnicorn,
\r
1755 PM_BH = (int) BlackNightrider,
\r
1756 PM_BA = (int) BlackAngel,
\r
1757 PM_BC = (int) BlackMarshall,
\r
1758 PM_BG = (int) BlackGrasshopper,
\r
1759 PM_BAB = (int) BlackCardinal,
\r
1760 PM_BD = (int) BlackDragon,
\r
1761 PM_BL = (int) BlackLance,
\r
1762 PM_BS = (int) BlackCobra,
\r
1763 PM_BV = (int) BlackFalcon,
\r
1764 PM_BSG = (int) BlackSilver,
\r
1765 PM_BK = (int) BlackKing
\r
1768 static HFONT hPieceFont = NULL;
\r
1769 static HBITMAP hPieceMask[(int) EmptySquare];
\r
1770 static HBITMAP hPieceFace[(int) EmptySquare];
\r
1771 static int fontBitmapSquareSize = 0;
\r
1772 static char pieceToFontChar[(int) EmptySquare] =
\r
1773 { 'p', 'n', 'b', 'r', 'q',
\r
1774 'n', 'b', 'p', 'n', 'b', 'r', 'b', 'r', 'q', 'k',
\r
1775 'k', 'o', 'm', 'v', 't', 'w',
\r
1776 'v', 't', 'o', 'm', 'v', 't', 'v', 't', 'w', 'l',
\r
1779 extern BOOL SetCharTable( char *table, const char * map );
\r
1780 /* [HGM] moved to backend.c */
\r
1782 static void SetPieceBackground( HDC hdc, COLORREF color, int mode )
\r
1785 BYTE r1 = GetRValue( color );
\r
1786 BYTE g1 = GetGValue( color );
\r
1787 BYTE b1 = GetBValue( color );
\r
1793 /* Create a uniform background first */
\r
1794 hbrush = CreateSolidBrush( color );
\r
1795 SetRect( &rc, 0, 0, squareSize, squareSize );
\r
1796 FillRect( hdc, &rc, hbrush );
\r
1797 DeleteObject( hbrush );
\r
1800 /* Vertical gradient, good for pawn, knight and rook, less for queen and king */
\r
1801 int steps = squareSize / 2;
\r
1804 for( i=0; i<steps; i++ ) {
\r
1805 BYTE r = r1 - (r1-r2) * i / steps;
\r
1806 BYTE g = g1 - (g1-g2) * i / steps;
\r
1807 BYTE b = b1 - (b1-b2) * i / steps;
\r
1809 hbrush = CreateSolidBrush( RGB(r,g,b) );
\r
1810 SetRect( &rc, i + squareSize - steps, 0, i + squareSize - steps + 1, squareSize );
\r
1811 FillRect( hdc, &rc, hbrush );
\r
1812 DeleteObject(hbrush);
\r
1815 else if( mode == 2 ) {
\r
1816 /* Diagonal gradient, good more or less for every piece */
\r
1817 POINT triangle[3];
\r
1818 HPEN hpen = SelectObject( hdc, GetStockObject(NULL_PEN) );
\r
1819 HBRUSH hbrush_old;
\r
1820 int steps = squareSize;
\r
1823 triangle[0].x = squareSize - steps;
\r
1824 triangle[0].y = squareSize;
\r
1825 triangle[1].x = squareSize;
\r
1826 triangle[1].y = squareSize;
\r
1827 triangle[2].x = squareSize;
\r
1828 triangle[2].y = squareSize - steps;
\r
1830 for( i=0; i<steps; i++ ) {
\r
1831 BYTE r = r1 - (r1-r2) * i / steps;
\r
1832 BYTE g = g1 - (g1-g2) * i / steps;
\r
1833 BYTE b = b1 - (b1-b2) * i / steps;
\r
1835 hbrush = CreateSolidBrush( RGB(r,g,b) );
\r
1836 hbrush_old = SelectObject( hdc, hbrush );
\r
1837 Polygon( hdc, triangle, 3 );
\r
1838 SelectObject( hdc, hbrush_old );
\r
1839 DeleteObject(hbrush);
\r
1844 SelectObject( hdc, hpen );
\r
1849 [AS] The method I use to create the bitmaps it a bit tricky, but it
\r
1850 seems to work ok. The main problem here is to find the "inside" of a chess
\r
1851 piece: follow the steps as explained below.
\r
1853 static void CreatePieceMaskFromFont( HDC hdc_window, HDC hdc, int index )
\r
1857 COLORREF chroma = RGB(0xFF,0x00,0xFF);
\r
1863 int backColor = whitePieceColor;
\r
1864 int foreColor = blackPieceColor;
\r
1866 if( index < (int)BlackPawn && appData.fontBackColorWhite != appData.fontForeColorWhite ) {
\r
1867 backColor = appData.fontBackColorWhite;
\r
1868 foreColor = appData.fontForeColorWhite;
\r
1870 else if( index >= (int)BlackPawn && appData.fontBackColorBlack != appData.fontForeColorBlack ) {
\r
1871 backColor = appData.fontBackColorBlack;
\r
1872 foreColor = appData.fontForeColorBlack;
\r
1876 hbm = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );
\r
1878 hbm_old = SelectObject( hdc, hbm );
\r
1882 rc.right = squareSize;
\r
1883 rc.bottom = squareSize;
\r
1885 /* Step 1: background is now black */
\r
1886 FillRect( hdc, &rc, GetStockObject(BLACK_BRUSH) );
\r
1888 GetTextExtentPoint32( hdc, &pieceToFontChar[index], 1, &sz );
\r
1890 pt.x = (squareSize - sz.cx) / 2;
\r
1891 pt.y = (squareSize - sz.cy) / 2;
\r
1893 SetBkMode( hdc, TRANSPARENT );
\r
1894 SetTextColor( hdc, chroma );
\r
1895 /* Step 2: the piece has been drawn in purple, there are now black and purple in this bitmap */
\r
1896 TextOut( hdc, pt.x, pt.y, &pieceToFontChar[appData.allWhite && index >= (int)BlackPawn ? index - (int)BlackPawn : index], 1 );
\r
1898 SelectObject( hdc, GetStockObject(WHITE_BRUSH) );
\r
1899 /* Step 3: the area outside the piece is filled with white */
\r
1900 // FloodFill( hdc, 0, 0, chroma );
\r
1901 ExtFloodFill( hdc, 0, 0, 0, FLOODFILLSURFACE );
\r
1902 ExtFloodFill( hdc, 0, squareSize-1, 0, FLOODFILLSURFACE ); // [HGM] fill from all 4 corners, for if piece too big
\r
1903 ExtFloodFill( hdc, squareSize-1, 0, 0, FLOODFILLSURFACE );
\r
1904 ExtFloodFill( hdc, squareSize-1, squareSize-1, 0, FLOODFILLSURFACE );
\r
1905 SelectObject( hdc, GetStockObject(BLACK_BRUSH) );
\r
1907 Step 4: this is the tricky part, the area inside the piece is filled with black,
\r
1908 but if the start point is not inside the piece we're lost!
\r
1909 There should be a better way to do this... if we could create a region or path
\r
1910 from the fill operation we would be fine for example.
\r
1912 // FloodFill( hdc, squareSize / 2, squareSize / 2, RGB(0xFF,0xFF,0xFF) );
\r
1913 ExtFloodFill( hdc, squareSize / 2, squareSize / 2, RGB(0xFF,0xFF,0xFF), FLOODFILLBORDER );
\r
1915 { /* [HGM] shave off edges of mask, in an attempt to correct for the fact that FloodFill does not work correctly under Win XP */
\r
1916 HDC dc2 = CreateCompatibleDC( hdc_window );
\r
1917 HBITMAP bm2 = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );
\r
1919 SelectObject( dc2, bm2 );
\r
1920 BitBlt( dc2, 0, 0, squareSize, squareSize, hdc, 0, 0, SRCCOPY ); // make copy
\r
1921 BitBlt( hdc, 0, 1, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );
\r
1922 BitBlt( hdc, 2, 1, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );
\r
1923 BitBlt( hdc, 1, 0, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );
\r
1924 BitBlt( hdc, 1, 2, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );
\r
1927 DeleteObject( bm2 );
\r
1930 SetTextColor( hdc, 0 );
\r
1932 Step 5: some fonts have "disconnected" areas that are skipped by the fill:
\r
1933 draw the piece again in black for safety.
\r
1935 TextOut( hdc, pt.x, pt.y, &pieceToFontChar[appData.allWhite && index >= (int)BlackPawn ? index - (int)BlackPawn : index], 1 );
\r
1937 SelectObject( hdc, hbm_old );
\r
1939 if( hPieceMask[index] != NULL ) {
\r
1940 DeleteObject( hPieceMask[index] );
\r
1943 hPieceMask[index] = hbm;
\r
1946 hbm = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );
\r
1948 SelectObject( hdc, hbm );
\r
1951 HDC dc1 = CreateCompatibleDC( hdc_window );
\r
1952 HDC dc2 = CreateCompatibleDC( hdc_window );
\r
1953 HBITMAP bm2 = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );
\r
1955 SelectObject( dc1, hPieceMask[index] );
\r
1956 SelectObject( dc2, bm2 );
\r
1957 FillRect( dc2, &rc, GetStockObject(WHITE_BRUSH) );
\r
1958 BitBlt( dc2, 0, 0, squareSize, squareSize, dc1, 0, 0, SRCINVERT );
\r
1961 Now dc2 contains the inverse of the piece mask, i.e. a mask that preserves
\r
1962 the piece background and deletes (makes transparent) the rest.
\r
1963 Thanks to that mask, we are free to paint the background with the greates
\r
1964 freedom, as we'll be able to mask off the unwanted parts when finished.
\r
1965 We use this, to make gradients and give the pieces a "roundish" look.
\r
1967 SetPieceBackground( hdc, backColor, 2 );
\r
1968 BitBlt( hdc, 0, 0, squareSize, squareSize, dc2, 0, 0, SRCAND );
\r
1972 DeleteObject( bm2 );
\r
1975 SetTextColor( hdc, foreColor );
\r
1976 TextOut( hdc, pt.x, pt.y, &pieceToFontChar[appData.allWhite && index >= (int)BlackPawn ? index - (int)BlackPawn : index], 1 );
\r
1978 SelectObject( hdc, hbm_old );
\r
1980 if( hPieceFace[index] != NULL ) {
\r
1981 DeleteObject( hPieceFace[index] );
\r
1984 hPieceFace[index] = hbm;
\r
1987 static int TranslatePieceToFontPiece( int piece )
\r
2017 case BlackMarshall:
\r
2021 case BlackNightrider:
\r
2027 case BlackUnicorn:
\r
2031 case BlackGrasshopper:
\r
2043 case BlackCardinal:
\r
2050 case WhiteMarshall:
\r
2054 case WhiteNightrider:
\r
2060 case WhiteUnicorn:
\r
2064 case WhiteGrasshopper:
\r
2077 case WhiteCardinal:
\r
2086 void CreatePiecesFromFont()
\r
2089 HDC hdc_window = NULL;
\r
2095 if( fontBitmapSquareSize < 0 ) {
\r
2096 /* Something went seriously wrong in the past: do not try to recreate fonts! */
\r
2100 if( !appData.useFont || appData.renderPiecesWithFont == NULL ||
\r
2101 appData.renderPiecesWithFont[0] == NULLCHAR || appData.renderPiecesWithFont[0] == '*' ) {
\r
2102 fontBitmapSquareSize = -1;
\r
2106 if( fontBitmapSquareSize != squareSize ) {
\r
2107 hdc_window = GetDC( hwndMain );
\r
2108 hdc = CreateCompatibleDC( hdc_window );
\r
2110 if( hPieceFont != NULL ) {
\r
2111 DeleteObject( hPieceFont );
\r
2114 for( i=0; i<=(int)BlackKing; i++ ) {
\r
2115 hPieceMask[i] = NULL;
\r
2116 hPieceFace[i] = NULL;
\r
2122 if( appData.fontPieceSize >= 50 && appData.fontPieceSize <= 150 ) {
\r
2123 fontHeight = appData.fontPieceSize;
\r
2126 fontHeight = (fontHeight * squareSize) / 100;
\r
2128 lf.lfHeight = -MulDiv( fontHeight, GetDeviceCaps(hdc, LOGPIXELSY), 72 );
\r
2130 lf.lfEscapement = 0;
\r
2131 lf.lfOrientation = 0;
\r
2132 lf.lfWeight = FW_NORMAL;
\r
2134 lf.lfUnderline = 0;
\r
2135 lf.lfStrikeOut = 0;
\r
2136 lf.lfCharSet = DEFAULT_CHARSET;
\r
2137 lf.lfOutPrecision = OUT_DEFAULT_PRECIS;
\r
2138 lf.lfClipPrecision = CLIP_DEFAULT_PRECIS;
\r
2139 lf.lfQuality = PROOF_QUALITY;
\r
2140 lf.lfPitchAndFamily = DEFAULT_PITCH | FF_DONTCARE;
\r
2141 strncpy( lf.lfFaceName, appData.renderPiecesWithFont, sizeof(lf.lfFaceName) );
\r
2142 lf.lfFaceName[ sizeof(lf.lfFaceName) - 1 ] = '\0';
\r
2144 hPieceFont = CreateFontIndirect( &lf );
\r
2146 if( hPieceFont == NULL ) {
\r
2147 fontBitmapSquareSize = -2;
\r
2150 /* Setup font-to-piece character table */
\r
2151 if( ! SetCharTable(pieceToFontChar, appData.fontToPieceTable) ) {
\r
2152 /* No (or wrong) global settings, try to detect the font */
\r
2153 if( strstr(lf.lfFaceName,"Alpha") != NULL ) {
\r
2155 SetCharTable(pieceToFontChar, "phbrqkojntwl");
\r
2157 else if( strstr(lf.lfFaceName,"DiagramTT") != NULL ) {
\r
2158 /* DiagramTT* family */
\r
2159 SetCharTable(pieceToFontChar, "PNLRQKpnlrqk");
\r
2161 else if( strstr(lf.lfFaceName,"WinboardF") != NULL ) {
\r
2162 /* Fairy symbols */
\r
2163 SetCharTable(pieceToFontChar, "PNBRQFEACWMOHIJGDVSLUKpnbrqfeacwmohijgdvsluk");
\r
2165 else if( strstr(lf.lfFaceName,"GC2004D") != NULL ) {
\r
2166 /* Good Companion (Some characters get warped as literal :-( */
\r
2167 char s[] = "1cmWG0??S??oYI23wgQU";
\r
2168 s[0]=0xB9; s[1]=0xA9; s[6]=0xB1; s[11]=0xBB; s[12]=0xAB; s[17]=0xB3;
\r
2169 SetCharTable(pieceToFontChar, s);
\r
2172 /* Cases, Condal, Leipzig, Lucena, Marroquin, Merida, Usual */
\r
2173 SetCharTable(pieceToFontChar, "pnbrqkomvtwl");
\r
2177 /* Create bitmaps */
\r
2178 hfont_old = SelectObject( hdc, hPieceFont );
\r
2179 for(i=(int)WhitePawn; i<(int)EmptySquare; i++) /* [HGM] made a loop for this */
\r
2180 if(PieceToChar((ChessSquare)i) != '.') /* skip unused pieces */
\r
2181 CreatePieceMaskFromFont( hdc_window, hdc, i );
\r
2183 SelectObject( hdc, hfont_old );
\r
2185 fontBitmapSquareSize = squareSize;
\r
2189 if( hdc != NULL ) {
\r
2193 if( hdc_window != NULL ) {
\r
2194 ReleaseDC( hwndMain, hdc_window );
\r
2199 DoLoadBitmap(HINSTANCE hinst, char *piece, int squareSize, char *suffix)
\r
2201 char name[128], buf[MSG_SIZ], *ids = "pnbrqfeicwmohajgdvlsukaacvdklnwpwnwlwswolfgnuzebracameltowersword", *p;
\r
2203 snprintf(name, sizeof(name)/sizeof(name[0]), "%s%d%s", piece, squareSize, suffix);
\r
2204 if(appData.pieceDirectory[0]) {
\r
2206 snprintf(buf, MSG_SIZ, "%s\\%s.bmp", appData.pieceDirectory, name);
\r
2207 res = LoadImage( 0, buf, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );
\r
2208 if(res) return res;
\r
2209 p = strstr(ids, piece);
\r
2210 if(p) { // if we could reconstruct canonical piece number, try the pieceNNN_ format before falling back on built-ins
\r
2213 case 21: n = WhiteKing; break;
\r
2214 case 22: n = WhiteAngel; break;
\r
2215 case 24: n = WhiteSilver; break;
\r
2216 case 26: n = WhiteDragon; break;
\r
2217 case 28: n = WhiteLion; break;
\r
2218 case 30: n = WhiteTokin; break;
\r
2219 case 32: n = WhitePKnight; break;
\r
2220 case 34: n = WhitePLance; break;
\r
2221 case 36: n = WhitePSilver; break;
\r
2222 case 38: n = WhiteWolf; break;
\r
2223 case 42: n = WhiteGnu; break;
\r
2224 case 45: n = WhiteZebra; break;
\r
2225 case 50: n = WhiteCamel; break;
\r
2226 case 55: n = WhiteTower; break;
\r
2227 case 60: n = WhiteSword; break;
\r
2229 snprintf(buf, MSG_SIZ, "%s\\piece%d_%d%s.bmp", appData.pieceDirectory, n, squareSize, suffix);
\r
2230 res = LoadImage( 0, buf, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );
\r
2231 if(res) return res;
\r
2234 if (gameInfo.event &&
\r
2235 strcmp(gameInfo.event, "Easter Egg Hunt") == 0 &&
\r
2236 strcmp(name, "k80s") == 0) {
\r
2237 safeStrCpy(name, "tim", sizeof(name)/sizeof(name[0]) );
\r
2239 return LoadBitmap(hinst, name);
\r
2243 /* Insert a color into the program's logical palette
\r
2244 structure. This code assumes the given color is
\r
2245 the result of the RGB or PALETTERGB macro, and it
\r
2246 knows how those macros work (which is documented).
\r
2249 InsertInPalette(COLORREF color)
\r
2251 LPPALETTEENTRY pe = &(pLogPal->palPalEntry[pLogPal->palNumEntries]);
\r
2253 if (pLogPal->palNumEntries++ >= PALETTESIZE) {
\r
2254 DisplayFatalError(_("Too many colors"), 0, 1);
\r
2255 pLogPal->palNumEntries--;
\r
2259 pe->peFlags = (char) 0;
\r
2260 pe->peRed = (char) (0xFF & color);
\r
2261 pe->peGreen = (char) (0xFF & (color >> 8));
\r
2262 pe->peBlue = (char) (0xFF & (color >> 16));
\r
2268 InitDrawingColors()
\r
2271 if (pLogPal == NULL) {
\r
2272 /* Allocate enough memory for a logical palette with
\r
2273 * PALETTESIZE entries and set the size and version fields
\r
2274 * of the logical palette structure.
\r
2276 pLogPal = (NPLOGPALETTE)
\r
2277 LocalAlloc(LMEM_FIXED, (sizeof(LOGPALETTE) +
\r
2278 (sizeof(PALETTEENTRY) * (PALETTESIZE))));
\r
2279 pLogPal->palVersion = 0x300;
\r
2281 pLogPal->palNumEntries = 0;
\r
2283 InsertInPalette(lightSquareColor);
\r
2284 InsertInPalette(darkSquareColor);
\r
2285 InsertInPalette(whitePieceColor);
\r
2286 InsertInPalette(blackPieceColor);
\r
2287 InsertInPalette(highlightSquareColor);
\r
2288 InsertInPalette(premoveHighlightColor);
\r
2290 /* create a logical color palette according the information
\r
2291 * in the LOGPALETTE structure.
\r
2293 hPal = CreatePalette((LPLOGPALETTE) pLogPal);
\r
2295 lightSquareBrush = CreateSolidBrush(lightSquareColor);
\r
2296 blackSquareBrush = CreateSolidBrush(blackPieceColor);
\r
2297 darkSquareBrush = CreateSolidBrush(darkSquareColor);
\r
2298 whitePieceBrush = CreateSolidBrush(whitePieceColor);
\r
2299 blackPieceBrush = CreateSolidBrush(blackPieceColor);
\r
2300 iconBkgndBrush = CreateSolidBrush(GetSysColor(COLOR_BACKGROUND));
\r
2301 explodeBrush = CreateSolidBrush(highlightSquareColor); // [HGM] atomic
\r
2302 for(i=0; i<8;i++) markerBrush[i] = CreateSolidBrush(markerColor[i]); // [HGM] markers
\r
2304 /* [AS] Force rendering of the font-based pieces */
\r
2305 if( fontBitmapSquareSize > 0 ) {
\r
2306 fontBitmapSquareSize = 0;
\r
2312 BoardWidth(int boardSize, int n)
\r
2313 { /* [HGM] argument n added to allow different width and height */
\r
2314 int lineGap = sizeInfo[boardSize].lineGap;
\r
2316 if( appData.overrideLineGap >= 0 && appData.overrideLineGap <= 5 ) {
\r
2317 lineGap = appData.overrideLineGap;
\r
2320 return (n + 1) * lineGap +
\r
2321 n * sizeInfo[boardSize].squareSize;
\r
2324 /* Respond to board resize by dragging edge */
\r
2326 ResizeBoard(int newSizeX, int newSizeY, int flags)
\r
2328 BoardSize newSize = NUM_SIZES - 1;
\r
2329 static int recurse = 0;
\r
2330 if (IsIconic(hwndMain)) return;
\r
2331 if (recurse > 0) return;
\r
2333 while (newSize > 0) {
\r
2334 InitDrawingSizes(newSize+1000, 0); // [HGM] kludge to update sizeInfo without visible effects
\r
2335 if(newSizeX >= sizeInfo[newSize].cliWidth &&
\r
2336 newSizeY >= sizeInfo[newSize].cliHeight) break;
\r
2339 boardSize = newSize;
\r
2340 InitDrawingSizes(boardSize, flags);
\r
2345 extern Boolean twoBoards, partnerUp; // [HGM] dual
\r
2348 InitDrawingSizes(BoardSize boardSize, int flags)
\r
2350 int i, boardWidth, boardHeight; /* [HGM] height treated separately */
\r
2351 ChessSquare piece;
\r
2352 static int oldBoardSize = -1, oldTinyLayout = 0;
\r
2354 SIZE clockSize, messageSize;
\r
2356 char buf[MSG_SIZ];
\r
2358 HMENU hmenu = GetMenu(hwndMain);
\r
2359 RECT crect, wrect, oldRect;
\r
2361 LOGBRUSH logbrush;
\r
2362 VariantClass v = gameInfo.variant;
\r
2364 int suppressVisibleEffects = 0; // [HGM] kludge to request updating sizeInfo only
\r
2365 if((int)boardSize >= 1000 ) { boardSize -= 1000; suppressVisibleEffects = 1; }
\r
2367 /* [HGM] call with -2 uses old size (for if nr of files, ranks changes) */
\r
2368 if(boardSize == (BoardSize)(-2) ) boardSize = oldBoardSize;
\r
2369 if(boardSize == -1) return; // no size defined yet; abort (to allow early call of InitPosition)
\r
2370 oldBoardSize = boardSize;
\r
2372 if(boardSize != SizeMiddling && boardSize != SizePetite && boardSize != SizeBulky && !appData.useFont)
\r
2373 { // correct board size to one where built-in pieces exist
\r
2374 if((v == VariantCapablanca || v == VariantGothic || v == VariantGrand || v == VariantCapaRandom || v == VariantJanus || v == VariantSuper)
\r
2375 && (boardSize < SizePetite || boardSize > SizeBulky) // Archbishop and Chancellor available in entire middle range
\r
2377 || (v == VariantShogi && boardSize != SizeModerate) // Japanese-style Shogi
\r
2378 || v == VariantKnightmate || v == VariantSChess || v == VariantXiangqi || v == VariantSpartan
\r
2379 || v == VariantShatranj || v == VariantMakruk || v == VariantGreat || v == VariantFairy || v == VariantLion ) {
\r
2380 if(boardSize < SizeMediocre) boardSize = SizePetite; else
\r
2381 if(boardSize > SizeModerate) boardSize = SizeBulky; else
\r
2382 boardSize = SizeMiddling;
\r
2385 if(!appData.useFont && boardSize == SizePetite && (v == VariantKnightmate)) boardSize = SizeMiddling; // no Unicorn in Petite
\r
2387 oldRect.left = wpMain.x; //[HGM] placement: remember previous window params
\r
2388 oldRect.top = wpMain.y;
\r
2389 oldRect.right = wpMain.x + wpMain.width;
\r
2390 oldRect.bottom = wpMain.y + wpMain.height;
\r
2392 tinyLayout = sizeInfo[boardSize].tinyLayout;
\r
2393 smallLayout = sizeInfo[boardSize].smallLayout;
\r
2394 squareSize = sizeInfo[boardSize].squareSize;
\r
2395 lineGap = sizeInfo[boardSize].lineGap;
\r
2396 minorSize = 0; /* [HGM] Kludge to see if demagnified pieces need to be shifted */
\r
2397 border = appData.useBorder && appData.border[0] ? squareSize/2 : 0;
\r
2399 // [HGM] decide on tininess based on total board width rather than square size
\r
2400 tinyLayout = squareSize * (BOARD_WIDTH);
\r
2401 tinyLayout = tinyLayout < 35*8 ? 2 : tinyLayout < 43*8 ? 1 : 0;
\r
2403 if( appData.overrideLineGap >= 0 && appData.overrideLineGap <= 5 ) {
\r
2404 lineGap = appData.overrideLineGap;
\r
2407 if (tinyLayout != oldTinyLayout) {
\r
2408 long style = GetWindowLongPtr(hwndMain, GWL_STYLE);
\r
2409 if (tinyLayout == 2) {
\r
2410 style &= ~WS_SYSMENU;
\r
2411 InsertMenu(hmenu, IDM_Exit, MF_BYCOMMAND, IDM_Minimize,
\r
2412 "&Minimize\tCtrl+F4");
\r
2414 style |= WS_SYSMENU;
\r
2415 RemoveMenu(hmenu, IDM_Minimize, MF_BYCOMMAND);
\r
2417 SetWindowLongPtr(hwndMain, GWL_STYLE, style);
\r
2419 for (i=0; menuBarText[tinyLayout][i]; i++) {
\r
2420 ModifyMenu(hmenu, i, MF_STRING|MF_BYPOSITION|MF_POPUP,
\r
2421 (UINT)GetSubMenu(hmenu, i), T_(menuBarText[tinyLayout][i]));
\r
2423 DrawMenuBar(hwndMain);
\r
2426 boardWidth = BoardWidth(boardSize, BOARD_WIDTH) + 2*border;
\r
2427 boardHeight = BoardWidth(boardSize, BOARD_HEIGHT) + 2*border;
\r
2429 /* Get text area sizes */
\r
2430 hdc = GetDC(hwndMain);
\r
2431 if (appData.clockMode) {
\r
2432 snprintf(buf, MSG_SIZ, _("White: %s"), TimeString(23*60*60*1000L));
\r
2434 snprintf(buf, MSG_SIZ, _("White"));
\r
2436 oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);
\r
2437 GetTextExtentPoint(hdc, buf, strlen(buf), &clockSize);
\r
2438 SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);
\r
2439 str = _("We only care about the height here");
\r
2440 GetTextExtentPoint(hdc, str, strlen(str), &messageSize);
\r
2441 SelectObject(hdc, oldFont);
\r
2442 ReleaseDC(hwndMain, hdc);
\r
2444 /* Compute where everything goes */
\r
2445 if((first.programLogo || second.programLogo) && tinyLayout != 2) {
\r
2446 /* [HGM] logo: if either logo is on, reserve space for it */
\r
2447 logoHeight = 2*clockSize.cy;
\r
2448 leftLogoRect.left = OUTER_MARGIN;
\r
2449 leftLogoRect.right = leftLogoRect.left + 4*clockSize.cy;
\r
2450 leftLogoRect.top = OUTER_MARGIN;
\r
2451 leftLogoRect.bottom = OUTER_MARGIN + logoHeight;
\r
2453 rightLogoRect.right = OUTER_MARGIN + boardWidth;
\r
2454 rightLogoRect.left = rightLogoRect.right - 4*clockSize.cy;
\r
2455 rightLogoRect.top = OUTER_MARGIN;
\r
2456 rightLogoRect.bottom = OUTER_MARGIN + logoHeight;
\r
2459 whiteRect.left = leftLogoRect.right;
\r
2460 whiteRect.right = OUTER_MARGIN + boardWidth/2 - INNER_MARGIN/2;
\r
2461 whiteRect.top = OUTER_MARGIN;
\r
2462 whiteRect.bottom = whiteRect.top + logoHeight;
\r
2464 blackRect.right = rightLogoRect.left;
\r
2465 blackRect.left = whiteRect.right + INNER_MARGIN;
\r
2466 blackRect.top = whiteRect.top;
\r
2467 blackRect.bottom = whiteRect.bottom;
\r
2469 whiteRect.left = OUTER_MARGIN;
\r
2470 whiteRect.right = whiteRect.left + boardWidth/2 - INNER_MARGIN/2;
\r
2471 whiteRect.top = OUTER_MARGIN;
\r
2472 whiteRect.bottom = whiteRect.top + clockSize.cy;
\r
2474 blackRect.left = whiteRect.right + INNER_MARGIN;
\r
2475 blackRect.right = blackRect.left + boardWidth/2 - 1;
\r
2476 blackRect.top = whiteRect.top;
\r
2477 blackRect.bottom = whiteRect.bottom;
\r
2479 logoHeight = 0; // [HGM] logo: suppress logo after change to tiny layout!
\r
2482 messageRect.left = OUTER_MARGIN + MESSAGE_LINE_LEFTMARGIN;
\r
2483 if (appData.showButtonBar) {
\r
2484 messageRect.right = OUTER_MARGIN + boardWidth // [HGM] logo: expressed independent of clock placement
\r
2485 - N_BUTTONS*BUTTON_WIDTH - MESSAGE_LINE_LEFTMARGIN;
\r
2487 messageRect.right = OUTER_MARGIN + boardWidth;
\r
2489 messageRect.top = whiteRect.bottom + INNER_MARGIN;
\r
2490 messageRect.bottom = messageRect.top + messageSize.cy;
\r
2492 boardRect.left = OUTER_MARGIN;
\r
2493 boardRect.right = boardRect.left + boardWidth;
\r
2494 boardRect.top = messageRect.bottom + INNER_MARGIN;
\r
2495 boardRect.bottom = boardRect.top + boardHeight;
\r
2497 sizeInfo[boardSize].cliWidth = boardRect.right + OUTER_MARGIN;
\r
2498 sizeInfo[boardSize].cliHeight = boardRect.bottom + OUTER_MARGIN;
\r
2499 oldTinyLayout = tinyLayout;
\r
2500 winW = 2 * GetSystemMetrics(SM_CXFRAME) + boardRect.right + OUTER_MARGIN;
\r
2501 winH = 2 * GetSystemMetrics(SM_CYFRAME) + GetSystemMetrics(SM_CYMENU) +
\r
2502 GetSystemMetrics(SM_CYCAPTION) + boardRect.bottom + OUTER_MARGIN;
\r
2503 winW *= 1 + twoBoards;
\r
2504 if(suppressVisibleEffects) return; // [HGM] when called for filling sizeInfo only
\r
2505 wpMain.width = winW; // [HGM] placement: set through temporary which can used by initial sizing choice
\r
2506 wpMain.height = winH; // without disturbing window attachments
\r
2507 GetWindowRect(hwndMain, &wrect);
\r
2508 SetWindowPos(hwndMain, NULL, 0, 0, wpMain.width, wpMain.height,
\r
2509 SWP_NOCOPYBITS|SWP_NOZORDER|SWP_NOMOVE);
\r
2511 // [HGM] placement: let attached windows follow size change.
\r
2512 ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, moveHistoryDialog, &wpMoveHistory );
\r
2513 ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, evalGraphDialog, &wpEvalGraph );
\r
2514 ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, engineOutputDialog, &wpEngineOutput );
\r
2515 ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, gameListDialog, &wpGameList );
\r
2516 ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, hwndConsole, &wpConsole );
\r
2518 /* compensate if menu bar wrapped */
\r
2519 GetClientRect(hwndMain, &crect);
\r
2520 offby = boardRect.bottom + OUTER_MARGIN - crect.bottom;
\r
2521 wpMain.height += offby;
\r
2523 case WMSZ_TOPLEFT:
\r
2524 SetWindowPos(hwndMain, NULL,
\r
2525 wrect.right - wpMain.width, wrect.bottom - wpMain.height,
\r
2526 wpMain.width, wpMain.height, SWP_NOCOPYBITS|SWP_NOZORDER);
\r
2529 case WMSZ_TOPRIGHT:
\r
2531 SetWindowPos(hwndMain, NULL,
\r
2532 wrect.left, wrect.bottom - wpMain.height,
\r
2533 wpMain.width, wpMain.height, SWP_NOCOPYBITS|SWP_NOZORDER);
\r
2536 case WMSZ_BOTTOMLEFT:
\r
2538 SetWindowPos(hwndMain, NULL,
\r
2539 wrect.right - wpMain.width, wrect.top,
\r
2540 wpMain.width, wpMain.height, SWP_NOCOPYBITS|SWP_NOZORDER);
\r
2543 case WMSZ_BOTTOMRIGHT:
\r
2547 SetWindowPos(hwndMain, NULL, 0, 0, wpMain.width, wpMain.height,
\r
2548 SWP_NOCOPYBITS|SWP_NOZORDER|SWP_NOMOVE);
\r
2553 for (i = 0; i < N_BUTTONS; i++) {
\r
2554 if (buttonDesc[i].hwnd != NULL) {
\r
2555 DestroyWindow(buttonDesc[i].hwnd);
\r
2556 buttonDesc[i].hwnd = NULL;
\r
2558 if (appData.showButtonBar) {
\r
2559 buttonDesc[i].hwnd =
\r
2560 CreateWindow("BUTTON", buttonDesc[i].label,
\r
2561 WS_VISIBLE | WS_CHILD | BS_PUSHBUTTON,
\r
2562 boardRect.right - BUTTON_WIDTH*(N_BUTTONS-i),
\r
2563 messageRect.top, BUTTON_WIDTH, messageSize.cy, hwndMain,
\r
2564 (HMENU) buttonDesc[i].id,
\r
2565 (HINSTANCE) GetWindowLongPtr(hwndMain, GWLP_HINSTANCE), NULL);
\r
2566 if (tinyLayout == 2) {
\r
2567 SendMessage(buttonDesc[i].hwnd, WM_SETFONT,
\r
2568 (WPARAM)font[boardSize][MESSAGE_FONT]->hf,
\r
2569 MAKELPARAM(FALSE, 0));
\r
2571 if (buttonDesc[i].id == IDM_Pause)
\r
2572 hwndPause = buttonDesc[i].hwnd;
\r
2573 buttonDesc[i].wndproc = (WNDPROC)
\r
2574 SetWindowLongPtr(buttonDesc[i].hwnd, GWLP_WNDPROC, (LONG_PTR) ButtonProc);
\r
2577 if (gridPen != NULL) DeleteObject(gridPen);
\r
2578 if (highlightPen != NULL) DeleteObject(highlightPen);
\r
2579 if (premovePen != NULL) DeleteObject(premovePen);
\r
2580 if (lineGap != 0) {
\r
2581 logbrush.lbStyle = BS_SOLID;
\r
2582 logbrush.lbColor = RGB(0, 0, 0); /* grid pen color = black */
\r
2584 ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,
\r
2585 lineGap, &logbrush, 0, NULL);
\r
2586 logbrush.lbColor = highlightSquareColor;
\r
2588 ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,
\r
2589 lineGap, &logbrush, 0, NULL);
\r
2591 logbrush.lbColor = premoveHighlightColor;
\r
2593 ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,
\r
2594 lineGap, &logbrush, 0, NULL);
\r
2596 /* [HGM] Loop had to be split in part for vert. and hor. lines */
\r
2597 for (i = 0; i < BOARD_HEIGHT + 1; i++) {
\r
2598 gridEndpoints[i*2].x = boardRect.left + lineGap / 2 + border;
\r
2599 gridEndpoints[i*2].y = gridEndpoints[i*2 + 1].y =
\r
2600 boardRect.top + lineGap / 2 + (i * (squareSize + lineGap)) + border;
\r
2601 gridEndpoints[i*2 + 1].x = boardRect.left + lineGap / 2 +
\r
2602 BOARD_WIDTH * (squareSize + lineGap) + border;
\r
2603 gridVertexCounts[i*2] = gridVertexCounts[i*2 + 1] = 2;
\r
2605 for (i = 0; i < BOARD_WIDTH + 1; i++) {
\r
2606 gridEndpoints[i*2 + BOARD_HEIGHT*2 + 2].y = boardRect.top + lineGap / 2 + border;
\r
2607 gridEndpoints[i*2 + BOARD_HEIGHT*2 + 2].x =
\r
2608 gridEndpoints[i*2 + 1 + BOARD_HEIGHT*2 + 2].x = boardRect.left +
\r
2609 lineGap / 2 + (i * (squareSize + lineGap)) + border;
\r
2610 gridEndpoints[i*2 + 1 + BOARD_HEIGHT*2 + 2].y =
\r
2611 boardRect.top + BOARD_HEIGHT * (squareSize + lineGap) + border;
\r
2612 gridVertexCounts[i*2] = gridVertexCounts[i*2 + 1] = 2;
\r
2616 /* [HGM] Licensing requirement */
\r
2618 if(gameInfo.variant == VariantGothic) GothicPopUp( GOTHIC, VariantGothic); else
\r
2621 if(gameInfo.variant == VariantFalcon) GothicPopUp( FALCON, VariantFalcon); else
\r
2623 GothicPopUp( "", VariantNormal);
\r
2626 /* if (boardSize == oldBoardSize) return; [HGM] variant might have changed */
\r
2628 /* Load piece bitmaps for this board size */
\r
2629 for (i=0; i<=2; i++) {
\r
2630 for (piece = WhitePawn;
\r
2631 (int) piece < (int) BlackPawn;
\r
2632 piece = (ChessSquare) ((int) piece + 1)) {
\r
2633 if (pieceBitmap[i][piece] != NULL)
\r
2634 DeleteObject(pieceBitmap[i][piece]);
\r
2635 pieceBitmap[i][piece] = NULL;
\r
2639 fontBitmapSquareSize = 0; /* [HGM] render: make sure pieces will be recreated, as we might need others now */
\r
2641 // Orthodox Chess pieces
\r
2642 pieceBitmap[0][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "s");
\r
2643 pieceBitmap[0][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "s");
\r
2644 pieceBitmap[0][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "s");
\r
2645 pieceBitmap[0][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "s");
\r
2646 pieceBitmap[0][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "s");
\r
2647 pieceBitmap[1][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "o");
\r
2648 pieceBitmap[1][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "o");
\r
2649 pieceBitmap[1][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "o");
\r
2650 pieceBitmap[1][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "o");
\r
2651 pieceBitmap[1][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "o");
\r
2652 pieceBitmap[2][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "w");
\r
2653 pieceBitmap[2][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "w");
\r
2654 pieceBitmap[2][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "w");
\r
2655 pieceBitmap[2][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "w");
\r
2656 pieceBitmap[2][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "w");
\r
2657 if( gameInfo.variant == VariantShogi && squareSize <= 72 && squareSize >= 33) {
\r
2658 // in Shogi, Hijack the unused Queen for Lance
\r
2659 pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "s");
\r
2660 pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "o");
\r
2661 pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "w");
\r
2663 pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "s");
\r
2664 pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "o");
\r
2665 pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "w");
\r
2668 if(squareSize <= 72 && squareSize >= 33) {
\r
2669 /* A & C are available in most sizes now */
\r
2670 if(squareSize != 49 && squareSize != 72 && squareSize != 33) { // Vortex-like
\r
2671 pieceBitmap[0][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "s");
\r
2672 pieceBitmap[1][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "o");
\r
2673 pieceBitmap[2][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "w");
\r
2674 pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "s");
\r
2675 pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "o");
\r
2676 pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "w");
\r
2677 pieceBitmap[0][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "s");
\r
2678 pieceBitmap[1][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "o");
\r
2679 pieceBitmap[2][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "w");
\r
2680 pieceBitmap[0][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "s");
\r
2681 pieceBitmap[1][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "o");
\r
2682 pieceBitmap[2][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "w");
\r
2683 } else { // Smirf-like
\r
2684 if(gameInfo.variant == VariantSChess) {
\r
2685 pieceBitmap[0][WhiteAngel] = DoLoadBitmap(hInst, "v", squareSize, "s");
\r
2686 pieceBitmap[1][WhiteAngel] = DoLoadBitmap(hInst, "v", squareSize, "o");
\r
2687 pieceBitmap[2][WhiteAngel] = DoLoadBitmap(hInst, "v", squareSize, "w");
\r
2689 pieceBitmap[0][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "s");
\r
2690 pieceBitmap[1][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "o");
\r
2691 pieceBitmap[2][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "w");
\r
2694 if(gameInfo.variant == VariantGothic) { // Vortex-like
\r
2695 pieceBitmap[0][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "s");
\r
2696 pieceBitmap[1][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "o");
\r
2697 pieceBitmap[2][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "w");
\r
2698 } else if(gameInfo.variant == VariantSChess && (squareSize == 49 || squareSize == 72)) {
\r
2699 pieceBitmap[0][WhiteMarshall] = DoLoadBitmap(hInst, "e", squareSize, "s");
\r
2700 pieceBitmap[1][WhiteMarshall] = DoLoadBitmap(hInst, "e", squareSize, "o");
\r
2701 pieceBitmap[2][WhiteMarshall] = DoLoadBitmap(hInst, "e", squareSize, "w");
\r
2702 } else { // WinBoard standard
\r
2703 pieceBitmap[0][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "s");
\r
2704 pieceBitmap[1][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "o");
\r
2705 pieceBitmap[2][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "w");
\r
2710 if(squareSize==72 || squareSize==49 || squareSize==33) { /* experiment with some home-made bitmaps */
\r
2711 pieceBitmap[0][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "s");
\r
2712 pieceBitmap[1][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "o");
\r
2713 pieceBitmap[2][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "w");
\r
2714 pieceBitmap[0][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "s");
\r
2715 pieceBitmap[1][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "o");
\r
2716 pieceBitmap[2][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "w");
\r
2717 pieceBitmap[0][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "s");
\r
2718 pieceBitmap[1][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "o");
\r
2719 pieceBitmap[2][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "w");
\r
2720 pieceBitmap[0][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "s");
\r
2721 pieceBitmap[1][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "o");
\r
2722 pieceBitmap[2][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "w");
\r
2723 pieceBitmap[0][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "s");
\r
2724 pieceBitmap[1][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "o");
\r
2725 pieceBitmap[2][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "w");
\r
2726 pieceBitmap[0][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "s");
\r
2727 pieceBitmap[1][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "o");
\r
2728 pieceBitmap[2][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "w");
\r
2729 pieceBitmap[0][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "s");
\r
2730 pieceBitmap[1][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "o");
\r
2731 pieceBitmap[2][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "w");
\r
2732 pieceBitmap[0][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "s");
\r
2733 pieceBitmap[1][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "o");
\r
2734 pieceBitmap[2][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "w");
\r
2735 pieceBitmap[0][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "s");
\r
2736 pieceBitmap[1][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "o");
\r
2737 pieceBitmap[2][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "w");
\r
2738 pieceBitmap[0][WhiteAmazon] = DoLoadBitmap(hInst, "l", squareSize, "s");
\r
2739 pieceBitmap[1][WhiteAmazon] = DoLoadBitmap(hInst, "l", squareSize, "o");
\r
2740 pieceBitmap[2][WhiteAmazon] = DoLoadBitmap(hInst, "l", squareSize, "w");
\r
2741 pieceBitmap[0][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "s");
\r
2742 pieceBitmap[1][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "o");
\r
2743 pieceBitmap[2][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "w");
\r
2744 pieceBitmap[0][WhiteLion] = DoLoadBitmap(hInst, "ln", squareSize, "s");
\r
2745 pieceBitmap[1][WhiteLion] = DoLoadBitmap(hInst, "ln", squareSize, "o");
\r
2746 pieceBitmap[2][WhiteLion] = DoLoadBitmap(hInst, "ln", squareSize, "w");
\r
2747 pieceBitmap[0][WhiteCub] = DoLoadBitmap(hInst, "ln", squareSize, "s");
\r
2748 pieceBitmap[1][WhiteCub] = DoLoadBitmap(hInst, "ln", squareSize, "o");
\r
2749 pieceBitmap[2][WhiteCub] = DoLoadBitmap(hInst, "ln", squareSize, "w");
\r
2750 pieceBitmap[0][WhiteWolf] = DoLoadBitmap(hInst, "wolf", squareSize, "s");
\r
2751 pieceBitmap[1][WhiteWolf] = DoLoadBitmap(hInst, "wolf", squareSize, "o");
\r
2752 pieceBitmap[2][WhiteWolf] = DoLoadBitmap(hInst, "wolf", squareSize, "w");
\r
2753 pieceBitmap[0][WhiteCamel] = DoLoadBitmap(hInst, "camel", squareSize, "s");
\r
2754 pieceBitmap[1][WhiteCamel] = DoLoadBitmap(hInst, "camel", squareSize, "o");
\r
2755 pieceBitmap[2][WhiteCamel] = DoLoadBitmap(hInst, "camel", squareSize, "w");
\r
2756 pieceBitmap[0][WhiteZebra] = DoLoadBitmap(hInst, "zebra", squareSize, "s");
\r
2757 pieceBitmap[1][WhiteZebra] = DoLoadBitmap(hInst, "zebra", squareSize, "o");
\r
2758 pieceBitmap[2][WhiteZebra] = DoLoadBitmap(hInst, "n", squareSize, "w");
\r
2759 pieceBitmap[0][WhiteTower] = DoLoadBitmap(hInst, "tower", squareSize, "s");
\r
2760 pieceBitmap[1][WhiteTower] = DoLoadBitmap(hInst, "tower", squareSize, "o");
\r
2761 pieceBitmap[2][WhiteTower] = DoLoadBitmap(hInst, "tower", squareSize, "w");
\r
2762 pieceBitmap[0][WhiteSword] = DoLoadBitmap(hInst, "sword", squareSize, "s");
\r
2763 pieceBitmap[1][WhiteSword] = DoLoadBitmap(hInst, "sword", squareSize, "o");
\r
2764 pieceBitmap[2][WhiteSword] = DoLoadBitmap(hInst, "sword", squareSize, "w");
\r
2765 pieceBitmap[0][WhiteGnu] = DoLoadBitmap(hInst, "gnu", squareSize, "s");
\r
2766 pieceBitmap[1][WhiteGnu] = DoLoadBitmap(hInst, "gnu", squareSize, "o");
\r
2767 pieceBitmap[2][WhiteGnu] = DoLoadBitmap(hInst, "gnu", squareSize, "w");
\r
2769 if(gameInfo.variant == VariantShogi && BOARD_HEIGHT != 7) { /* promoted Gold representations (but not in Tori!)*/
\r
2770 pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "wp", squareSize, "s");
\r
2771 pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "wp", squareSize, "o");
\r
2772 pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "w", squareSize, "w");
\r
2773 pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "wn", squareSize, "s");
\r
2774 pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "wn", squareSize, "o");
\r
2775 pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "w", squareSize, "w");
\r
2776 pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "ws", squareSize, "s");
\r
2777 pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "ws", squareSize, "o");
\r
2778 pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "w", squareSize, "w");
\r
2779 pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "wl", squareSize, "s");
\r
2780 pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "wl", squareSize, "o");
\r
2781 pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "w", squareSize, "w");
\r
2783 pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "s");
\r
2784 pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "o");
\r
2785 pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "w");
\r
2786 pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "s");
\r
2787 pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "o");
\r
2788 pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "w");
\r
2789 pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "s");
\r
2790 pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "o");
\r
2791 pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "w");
\r
2792 pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "s");
\r
2793 pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "o");
\r
2794 pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "w");
\r
2797 } else { /* other size, no special bitmaps available. Use smaller symbols */
\r
2798 if((int)boardSize < 2) minorSize = sizeInfo[0].squareSize;
\r
2799 else minorSize = sizeInfo[(int)boardSize - 2].squareSize;
\r
2800 pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "s");
\r
2801 pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "o");
\r
2802 pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "w");
\r
2803 pieceBitmap[0][WhiteCardinal] = DoLoadBitmap(hInst, "b", minorSize, "s");
\r
2804 pieceBitmap[1][WhiteCardinal] = DoLoadBitmap(hInst, "b", minorSize, "o");
\r
2805 pieceBitmap[2][WhiteCardinal] = DoLoadBitmap(hInst, "b", minorSize, "w");
\r
2806 pieceBitmap[0][WhiteDragon] = DoLoadBitmap(hInst, "r", minorSize, "s");
\r
2807 pieceBitmap[1][WhiteDragon] = DoLoadBitmap(hInst, "r", minorSize, "o");
\r
2808 pieceBitmap[2][WhiteDragon] = DoLoadBitmap(hInst, "r", minorSize, "w");
\r
2809 pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "s");
\r
2810 pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "o");
\r
2811 pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "w");
\r
2815 if(gameInfo.variant == VariantShogi && squareSize == 58)
\r
2816 /* special Shogi support in this size */
\r
2817 { for (i=0; i<=2; i++) { /* replace all bitmaps */
\r
2818 for (piece = WhitePawn;
\r
2819 (int) piece < (int) BlackPawn;
\r
2820 piece = (ChessSquare) ((int) piece + 1)) {
\r
2821 if (pieceBitmap[i][piece] != NULL)
\r
2822 DeleteObject(pieceBitmap[i][piece]);
\r
2825 pieceBitmap[0][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "o");
\r
2826 pieceBitmap[0][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "o");
\r
2827 pieceBitmap[0][WhiteBishop] = DoLoadBitmap(hInst, "sb", squareSize, "o");
\r
2828 pieceBitmap[0][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "o");
\r
2829 pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "o");
\r
2830 pieceBitmap[0][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "o");
\r
2831 pieceBitmap[0][WhiteFerz] = DoLoadBitmap(hInst, "sf", squareSize, "o");
\r
2832 pieceBitmap[0][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "o");
\r
2833 pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "su", squareSize, "o");
\r
2834 pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "sh", squareSize, "o");
\r
2835 pieceBitmap[0][WhiteCardinal] = DoLoadBitmap(hInst, "sa", squareSize, "o");
\r
2836 pieceBitmap[0][WhiteDragon] = DoLoadBitmap(hInst, "sc", squareSize, "o");
\r
2837 pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "sg", squareSize, "o");
\r
2838 pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "ss", squareSize, "o");
\r
2839 pieceBitmap[1][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "o");
\r
2840 pieceBitmap[1][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "o");
\r
2841 pieceBitmap[1][WhiteBishop] = DoLoadBitmap(hInst, "sb", squareSize, "o");
\r
2842 pieceBitmap[1][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "o");
\r
2843 pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "o");
\r
2844 pieceBitmap[1][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "o");
\r
2845 pieceBitmap[1][WhiteFerz] = DoLoadBitmap(hInst, "sf", squareSize, "o");
\r
2846 pieceBitmap[1][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "o");
\r
2847 pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "su", squareSize, "o");
\r
2848 pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "sh", squareSize, "o");
\r
2849 pieceBitmap[1][WhiteCardinal] = DoLoadBitmap(hInst, "sa", squareSize, "o");
\r
2850 pieceBitmap[1][WhiteDragon] = DoLoadBitmap(hInst, "sc", squareSize, "o");
\r
2851 pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "sg", squareSize, "o");
\r
2852 pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "ss", squareSize, "o");
\r
2853 pieceBitmap[2][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "w");
\r
2854 pieceBitmap[2][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "w");
\r
2855 pieceBitmap[2][WhiteBishop] = DoLoadBitmap(hInst, "sr", squareSize, "w");
\r
2856 pieceBitmap[2][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "w");
\r
2857 pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "w");
\r
2858 pieceBitmap[2][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "w");
\r
2859 pieceBitmap[2][WhiteFerz] = DoLoadBitmap(hInst, "sw", squareSize, "w");
\r
2860 pieceBitmap[2][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "w");
\r
2861 pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "sp", squareSize, "w");
\r
2862 pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "sn", squareSize, "w");
\r
2863 pieceBitmap[2][WhiteCardinal] = DoLoadBitmap(hInst, "sr", squareSize, "w");
\r
2864 pieceBitmap[2][WhiteDragon] = DoLoadBitmap(hInst, "sr", squareSize, "w");
\r
2865 pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "sl", squareSize, "w");
\r
2866 pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "sw", squareSize, "w");
\r
2870 if(appData.pieceDirectory[0]) for(i=WhitePawn; i<BlackPawn; i++) { // try for all missing pieces with new naming convention
\r
2871 char buf[MSG_SIZ];
\r
2872 if(pieceBitmap[0][i]) continue;
\r
2873 snprintf(buf, MSG_SIZ, "piece%d_", i);
\r
2874 pieceBitmap[0][i] = DoLoadBitmap(hInst, buf, squareSize, "s");
\r
2875 pieceBitmap[1][i] = DoLoadBitmap(hInst, buf, squareSize, "o");
\r
2876 pieceBitmap[2][i] = DoLoadBitmap(hInst, buf, squareSize, "w");
\r
2881 PieceBitmap(ChessSquare p, int kind)
\r
2883 if ((int) p >= (int) BlackPawn)
\r
2884 p = (ChessSquare) ((int) p - (int) BlackPawn + (int) WhitePawn);
\r
2886 return pieceBitmap[kind][(int) p];
\r
2889 /***************************************************************/
\r
2891 #define MIN(a,b) (((a) < (b)) ? (a) : (b))
\r
2892 #define MAX(a,b) (((a) > (b)) ? (a) : (b))
\r
2894 #define MIN3(a,b,c) (((a) < (b) && (a) < (c)) ? (a) : (((b) < (a) && (b) < (c)) ? (b) : (c)))
\r
2895 #define MAX3(a,b,c) (((a) > (b) && (a) > (c)) ? (a) : (((b) > (a) && (b) > (c)) ? (b) : (c)))
\r
2899 SquareToPos(int row, int column, int * x, int * y)
\r
2902 *x = boardRect.left + lineGap + ((BOARD_WIDTH-1)-column) * (squareSize + lineGap) + border;
\r
2903 *y = boardRect.top + lineGap + row * (squareSize + lineGap) + border;
\r
2905 *x = boardRect.left + lineGap + column * (squareSize + lineGap) + border;
\r
2906 *y = boardRect.top + lineGap + ((BOARD_HEIGHT-1)-row) * (squareSize + lineGap) + border;
\r
2911 DrawCoordsOnDC(HDC hdc)
\r
2913 static char files[] = "0123456789012345678901221098765432109876543210";
\r
2914 static char ranks[] = "wvutsrqponmlkjihgfedcbaabcdefghijklmnopqrstuvw";
\r
2915 char str[2] = { NULLCHAR, NULLCHAR };
\r
2916 int oldMode, oldAlign, x, y, start, i;
\r
2920 if (!appData.showCoords)
\r
2923 start = flipView ? 1-(ONE!='1') : 45+(ONE!='1')-BOARD_HEIGHT;
\r
2925 oldBrush = SelectObject(hdc, GetStockObject(BLACK_BRUSH));
\r
2926 oldMode = SetBkMode(hdc, (appData.monoMode ? OPAQUE : TRANSPARENT));
\r
2927 oldAlign = GetTextAlign(hdc);
\r
2928 oldFont = SelectObject(hdc, font[boardSize][COORD_FONT]->hf);
\r
2930 y = boardRect.top + lineGap;
\r
2931 x = boardRect.left + lineGap + gameInfo.holdingsWidth*(squareSize + lineGap);
\r
2934 SetTextAlign(hdc, TA_RIGHT|TA_TOP);
\r
2935 x += border - lineGap - 4; y += squareSize - 6;
\r
2937 SetTextAlign(hdc, TA_LEFT|TA_TOP);
\r
2938 for (i = 0; i < BOARD_HEIGHT; i++) {
\r
2939 str[0] = files[start + i];
\r
2940 ExtTextOut(hdc, x + 2 - (border ? gameInfo.holdingsWidth * (squareSize + lineGap) : 0), y + 1, 0, NULL, str, 1, NULL);
\r
2941 y += squareSize + lineGap;
\r
2944 start = flipView ? 23-(BOARD_RGHT-BOARD_LEFT) : 23;
\r
2947 SetTextAlign(hdc, TA_LEFT|TA_TOP);
\r
2948 x += -border + 4; y += border - squareSize + 6;
\r
2950 SetTextAlign(hdc, TA_RIGHT|TA_BOTTOM);
\r
2951 for (i = 0; i < BOARD_RGHT - BOARD_LEFT; i++) {
\r
2952 str[0] = ranks[start + i];
\r
2953 ExtTextOut(hdc, x + squareSize - 2, y - 1, 0, NULL, str, 1, NULL);
\r
2954 x += squareSize + lineGap;
\r
2957 SelectObject(hdc, oldBrush);
\r
2958 SetBkMode(hdc, oldMode);
\r
2959 SetTextAlign(hdc, oldAlign);
\r
2960 SelectObject(hdc, oldFont);
\r
2964 DrawGridOnDC(HDC hdc)
\r
2968 if (lineGap != 0) {
\r
2969 oldPen = SelectObject(hdc, gridPen);
\r
2970 PolyPolyline(hdc, gridEndpoints, gridVertexCounts, BOARD_WIDTH+BOARD_HEIGHT + 2);
\r
2971 SelectObject(hdc, oldPen);
\r
2975 #define HIGHLIGHT_PEN 0
\r
2976 #define PREMOVE_PEN 1
\r
2979 DrawHighlightOnDC(HDC hdc, BOOLEAN on, int x, int y, int pen)
\r
2982 HPEN oldPen, hPen;
\r
2983 if (lineGap == 0) return;
\r
2985 x1 = boardRect.left +
\r
2986 lineGap/2 + ((BOARD_WIDTH-1)-x) * (squareSize + lineGap) + border;
\r
2987 y1 = boardRect.top +
\r
2988 lineGap/2 + y * (squareSize + lineGap) + border;
\r
2990 x1 = boardRect.left +
\r
2991 lineGap/2 + x * (squareSize + lineGap) + border;
\r
2992 y1 = boardRect.top +
\r
2993 lineGap/2 + ((BOARD_HEIGHT-1)-y) * (squareSize + lineGap) + border;
\r
2995 hPen = pen ? premovePen : highlightPen;
\r
2996 oldPen = SelectObject(hdc, on ? hPen : gridPen);
\r
2997 MoveToEx(hdc, x1, y1, NULL);
\r
2998 LineTo(hdc, x1 + squareSize + lineGap, y1);
\r
2999 LineTo(hdc, x1 + squareSize + lineGap, y1 + squareSize + lineGap);
\r
3000 LineTo(hdc, x1, y1 + squareSize + lineGap);
\r
3001 LineTo(hdc, x1, y1);
\r
3002 SelectObject(hdc, oldPen);
\r
3006 DrawHighlightsOnDC(HDC hdc, HighlightInfo *h, int pen)
\r
3009 for (i=0; i<2; i++) {
\r
3010 if (h->sq[i].x >= 0 && h->sq[i].y >= 0)
\r
3011 DrawHighlightOnDC(hdc, TRUE,
\r
3012 h->sq[i].x, h->sq[i].y,
\r
3017 /* Note: sqcolor is used only in monoMode */
\r
3018 /* Note that this code is largely duplicated in woptions.c,
\r
3019 function DrawSampleSquare, so that needs to be updated too */
\r
3021 DrawPieceOnDC(HDC hdc, ChessSquare piece, int color, int sqcolor, int x, int y, HDC tmphdc)
\r
3023 HBITMAP oldBitmap;
\r
3024 HBRUSH oldBrush = NULL;
\r
3027 if (appData.blindfold) return;
\r
3029 /* [AS] Use font-based pieces if needed */
\r
3030 if( fontBitmapSquareSize >= 0 && (squareSize > 32 || gameInfo.variant >= VariantShogi)) {
\r
3031 /* Create piece bitmaps, or do nothing if piece set is up to date */
\r
3032 CreatePiecesFromFont();
\r
3034 if( fontBitmapSquareSize == squareSize ) {
\r
3035 int index = TranslatePieceToFontPiece(piece);
\r
3037 SelectObject( tmphdc, hPieceMask[ index ] );
\r
3039 if(appData.upsideDown ? color==flipView : (flipView && gameInfo.variant == VariantShogi))
\r
3040 StretchBlt(hdc, x+squareSize, y+squareSize, -squareSize, -squareSize, tmphdc, 0, 0, squareSize, squareSize, SRCAND);
\r
3044 squareSize, squareSize,
\r
3049 SelectObject( tmphdc, hPieceFace[ index ] );
\r
3051 if(appData.upsideDown ? color==flipView : (flipView && gameInfo.variant == VariantShogi))
\r
3052 StretchBlt(hdc, x+squareSize, y+squareSize, -squareSize, -squareSize, tmphdc, 0, 0, squareSize, squareSize, SRCPAINT);
\r
3056 squareSize, squareSize,
\r
3065 if (appData.monoMode) {
\r
3066 SelectObject(tmphdc, PieceBitmap(piece,
\r
3067 color == sqcolor ? OUTLINE_PIECE : SOLID_PIECE));
\r
3068 BitBlt(hdc, x, y, squareSize, squareSize, tmphdc, 0, 0,
\r
3069 sqcolor ? SRCCOPY : NOTSRCCOPY);
\r
3071 HBRUSH xBrush = whitePieceBrush;
\r
3072 tmpSize = squareSize;
\r
3073 if(appData.pieceDirectory[0]) xBrush = GetStockObject(WHITE_BRUSH);
\r
3075 ((piece >= (int)WhiteNightrider && piece <= WhiteGrasshopper) ||
\r
3076 (piece >= (int)BlackNightrider && piece <= BlackGrasshopper)) ) {
\r
3077 /* [HGM] no bitmap available for promoted pieces in Crazyhouse */
\r
3078 /* Bitmaps of smaller size are substituted, but we have to align them */
\r
3079 x += (squareSize - minorSize)>>1;
\r
3080 y += squareSize - minorSize - 2;
\r
3081 tmpSize = minorSize;
\r
3083 #if WINVER >= 0x0500
\r
3084 HBITMAP pbm = PieceBitmap(piece, color ? OUTLINE_PIECE : SOLID_PIECE);
\r
3086 GetObject(pbm, sizeof(BITMAP), &b);
\r
3087 if(b.bmBitsPixel == 32) { // for now this is a kludge to indicate bitmaps with alpha channel
\r
3089 bf.BlendOp = AC_SRC_OVER;
\r
3090 bf.BlendFlags = 0;
\r
3091 bf.SourceConstantAlpha = 0xFF;
\r
3092 bf.AlphaFormat = AC_SRC_ALPHA;
\r
3093 oldBitmap = SelectObject(tmphdc, pbm);
\r
3094 AlphaBlend(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, bf);
\r
3097 if (color || appData.allWhite ) {
\r
3098 oldBitmap = SelectObject(tmphdc, PieceBitmap(piece, WHITE_PIECE));
\r
3100 oldBrush = SelectObject(hdc, xBrush);
\r
3101 else oldBrush = SelectObject(hdc, blackPieceBrush);
\r
3102 if(appData.upsideDown && color==flipView)
\r
3103 StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, 0x00B8074A);
\r
3105 BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, 0x00B8074A);
\r
3106 /* Use black for outline of white pieces */
\r
3107 SelectObject(tmphdc, PieceBitmap(piece, OUTLINE_PIECE));
\r
3108 if(appData.upsideDown && color==flipView)
\r
3109 StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, SRCAND);
\r
3111 BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, SRCAND);
\r
3112 } else if(appData.pieceDirectory[0]) {
\r
3113 oldBitmap = SelectObject(tmphdc, PieceBitmap(piece, WHITE_PIECE));
\r
3114 oldBrush = SelectObject(hdc, xBrush);
\r
3115 if(appData.upsideDown && color==flipView)
\r
3116 StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, 0x00B8074A);
\r
3118 BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, 0x00B8074A);
\r
3119 SelectObject(tmphdc, PieceBitmap(piece, SOLID_PIECE));
\r
3120 if(appData.upsideDown && color==flipView)
\r
3121 StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, SRCAND);
\r
3123 BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, SRCAND);
\r
3125 /* Use square color for details of black pieces */
\r
3126 oldBitmap = SelectObject(tmphdc, PieceBitmap(piece, SOLID_PIECE));
\r
3127 oldBrush = SelectObject(hdc, blackPieceBrush);
\r
3128 if(appData.upsideDown && !flipView)
\r
3129 StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, 0x00B8074A);
\r
3131 BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, 0x00B8074A);
\r
3133 if(oldBrush) SelectObject(hdc, oldBrush);
\r
3134 SelectObject(tmphdc, oldBitmap);
\r
3138 /* [AS] Compute a drawing mode for a square, based on specified settings (see DrawTile) */
\r
3139 int GetBackTextureMode( int algo )
\r
3141 int result = BACK_TEXTURE_MODE_DISABLED;
\r
3145 case BACK_TEXTURE_MODE_PLAIN:
\r
3146 result = 1; /* Always use identity map */
\r
3148 case BACK_TEXTURE_MODE_FULL_RANDOM:
\r
3149 result = 1 + (myrandom() % 3); /* Pick a transformation at random */
\r
3157 [AS] Compute and save texture drawing info, otherwise we may not be able
\r
3158 to handle redraws cleanly (as random numbers would always be different).
\r
3160 VOID RebuildTextureSquareInfo()
\r
3170 ZeroMemory( &backTextureSquareInfo, sizeof(backTextureSquareInfo) );
\r
3172 if( liteBackTexture != NULL ) {
\r
3173 if( GetObject( liteBackTexture, sizeof(bi), &bi ) > 0 ) {
\r
3174 lite_w = bi.bmWidth;
\r
3175 lite_h = bi.bmHeight;
\r
3179 if( darkBackTexture != NULL ) {
\r
3180 if( GetObject( darkBackTexture, sizeof(bi), &bi ) > 0 ) {
\r
3181 dark_w = bi.bmWidth;
\r
3182 dark_h = bi.bmHeight;
\r
3186 for( row=0; row<BOARD_HEIGHT; row++ ) {
\r
3187 for( col=0; col<BOARD_WIDTH; col++ ) {
\r
3188 if( (col + row) & 1 ) {
\r
3190 if( lite_w >= squareSize && lite_h >= squareSize ) {
\r
3191 if( lite_w >= squareSize*BOARD_WIDTH )
\r
3192 backTextureSquareInfo[row][col].x = (2*col+1)*lite_w/(2*BOARD_WIDTH) - squareSize/2; /* [HGM] cut out of center of virtual square */
\r
3194 backTextureSquareInfo[row][col].x = col * (lite_w - squareSize) / (BOARD_WIDTH-1); /* [HGM] divide by size-1 in stead of size! */
\r
3195 if( lite_h >= squareSize*BOARD_HEIGHT )
\r
3196 backTextureSquareInfo[row][col].y = (2*(BOARD_HEIGHT-row)-1)*lite_h/(2*BOARD_HEIGHT) - squareSize/2;
\r
3198 backTextureSquareInfo[row][col].y = (BOARD_HEIGHT-1-row) * (lite_h - squareSize) / (BOARD_HEIGHT-1);
\r
3199 backTextureSquareInfo[row][col].mode = GetBackTextureMode(liteBackTextureMode);
\r
3204 if( dark_w >= squareSize && dark_h >= squareSize ) {
\r
3205 if( dark_w >= squareSize*BOARD_WIDTH )
\r
3206 backTextureSquareInfo[row][col].x = (2*col+1) * dark_w / (2*BOARD_WIDTH) - squareSize/2;
\r
3208 backTextureSquareInfo[row][col].x = col * (dark_w - squareSize) / (BOARD_WIDTH-1);
\r
3209 if( dark_h >= squareSize*BOARD_HEIGHT )
\r
3210 backTextureSquareInfo[row][col].y = (2*(BOARD_HEIGHT-row)-1) * dark_h / (2*BOARD_HEIGHT) - squareSize/2;
\r
3212 backTextureSquareInfo[row][col].y = (BOARD_HEIGHT-1-row) * (dark_h - squareSize) / (BOARD_HEIGHT-1);
\r
3213 backTextureSquareInfo[row][col].mode = GetBackTextureMode(darkBackTextureMode);
\r
3220 /* [AS] Arrow highlighting support */
\r
3222 static double A_WIDTH = 5; /* Width of arrow body */
\r
3224 #define A_HEIGHT_FACTOR 6 /* Length of arrow "point", relative to body width */
\r
3225 #define A_WIDTH_FACTOR 3 /* Width of arrow "point", relative to body width */
\r
3227 static double Sqr( double x )
\r
3232 static int Round( double x )
\r
3234 return (int) (x + 0.5);
\r
3237 /* Draw an arrow between two points using current settings */
\r
3238 VOID DrawArrowBetweenPoints( HDC hdc, int s_x, int s_y, int d_x, int d_y )
\r
3241 double dx, dy, j, k, x, y;
\r
3243 if( d_x == s_x ) {
\r
3244 int h = (d_y > s_y) ? +A_WIDTH*A_HEIGHT_FACTOR : -A_WIDTH*A_HEIGHT_FACTOR;
\r
3246 arrow[0].x = s_x + A_WIDTH + 0.5;
\r
3249 arrow[1].x = s_x + A_WIDTH + 0.5;
\r
3250 arrow[1].y = d_y - h;
\r
3252 arrow[2].x = arrow[1].x + A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;
\r
3253 arrow[2].y = d_y - h;
\r
3258 arrow[5].x = arrow[1].x - 2*A_WIDTH + 0.5;
\r
3259 arrow[5].y = d_y - h;
\r
3261 arrow[4].x = arrow[5].x - A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;
\r
3262 arrow[4].y = d_y - h;
\r
3264 arrow[6].x = arrow[1].x - 2*A_WIDTH + 0.5;
\r
3267 else if( d_y == s_y ) {
\r
3268 int w = (d_x > s_x) ? +A_WIDTH*A_HEIGHT_FACTOR : -A_WIDTH*A_HEIGHT_FACTOR;
\r
3271 arrow[0].y = s_y + A_WIDTH + 0.5;
\r
3273 arrow[1].x = d_x - w;
\r
3274 arrow[1].y = s_y + A_WIDTH + 0.5;
\r
3276 arrow[2].x = d_x - w;
\r
3277 arrow[2].y = arrow[1].y + A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;
\r
3282 arrow[5].x = d_x - w;
\r
3283 arrow[5].y = arrow[1].y - 2*A_WIDTH + 0.5;
\r
3285 arrow[4].x = d_x - w;
\r
3286 arrow[4].y = arrow[5].y - A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;
\r
3289 arrow[6].y = arrow[1].y - 2*A_WIDTH + 0.5;
\r
3292 /* [AS] Needed a lot of paper for this! :-) */
\r
3293 dy = (double) (d_y - s_y) / (double) (d_x - s_x);
\r
3294 dx = (double) (s_x - d_x) / (double) (s_y - d_y);
\r
3296 j = sqrt( Sqr(A_WIDTH) / (1.0 + Sqr(dx)) );
\r
3298 k = sqrt( Sqr(A_WIDTH*A_HEIGHT_FACTOR) / (1.0 + Sqr(dy)) );
\r
3303 arrow[0].x = Round(x - j);
\r
3304 arrow[0].y = Round(y + j*dx);
\r
3306 arrow[1].x = Round(arrow[0].x + 2*j); // [HGM] prevent width to be affected by rounding twice
\r
3307 arrow[1].y = Round(arrow[0].y - 2*j*dx);
\r
3310 x = (double) d_x - k;
\r
3311 y = (double) d_y - k*dy;
\r
3314 x = (double) d_x + k;
\r
3315 y = (double) d_y + k*dy;
\r
3318 x = Round(x); y = Round(y); // [HGM] make sure width of shaft is rounded the same way on both ends
\r
3320 arrow[6].x = Round(x - j);
\r
3321 arrow[6].y = Round(y + j*dx);
\r
3323 arrow[2].x = Round(arrow[6].x + 2*j);
\r
3324 arrow[2].y = Round(arrow[6].y - 2*j*dx);
\r
3326 arrow[3].x = Round(arrow[2].x + j*(A_WIDTH_FACTOR-1));
\r
3327 arrow[3].y = Round(arrow[2].y - j*(A_WIDTH_FACTOR-1)*dx);
\r
3332 arrow[5].x = Round(arrow[6].x - j*(A_WIDTH_FACTOR-1));
\r
3333 arrow[5].y = Round(arrow[6].y + j*(A_WIDTH_FACTOR-1)*dx);
\r
3336 Polygon( hdc, arrow, 7 );
\r
3339 /* [AS] Draw an arrow between two squares */
\r
3340 VOID DrawArrowBetweenSquares( HDC hdc, int s_col, int s_row, int d_col, int d_row )
\r
3342 int s_x, s_y, d_x, d_y;
\r
3349 if( s_col == d_col && s_row == d_row ) {
\r
3353 /* Get source and destination points */
\r
3354 SquareToPos( s_row, s_col, &s_x, &s_y);
\r
3355 SquareToPos( d_row, d_col, &d_x, &d_y);
\r
3358 d_y += squareSize / 2 - squareSize / 4; // [HGM] round towards same centers on all sides!
\r
3360 else if( d_y < s_y ) {
\r
3361 d_y += squareSize / 2 + squareSize / 4;
\r
3364 d_y += squareSize / 2;
\r
3368 d_x += squareSize / 2 - squareSize / 4;
\r
3370 else if( d_x < s_x ) {
\r
3371 d_x += squareSize / 2 + squareSize / 4;
\r
3374 d_x += squareSize / 2;
\r
3377 s_x += squareSize / 2;
\r
3378 s_y += squareSize / 2;
\r
3380 /* Adjust width */
\r
3381 A_WIDTH = squareSize / 14.; //[HGM] make float
\r
3384 stLB.lbStyle = BS_SOLID;
\r
3385 stLB.lbColor = appData.highlightArrowColor;
\r
3388 hpen = CreatePen( PS_SOLID, 2, RGB(0x00,0x00,0x00) );
\r
3389 holdpen = SelectObject( hdc, hpen );
\r
3390 hbrush = CreateBrushIndirect( &stLB );
\r
3391 holdbrush = SelectObject( hdc, hbrush );
\r
3393 DrawArrowBetweenPoints( hdc, s_x, s_y, d_x, d_y );
\r
3395 SelectObject( hdc, holdpen );
\r
3396 SelectObject( hdc, holdbrush );
\r
3397 DeleteObject( hpen );
\r
3398 DeleteObject( hbrush );
\r
3401 BOOL HasHighlightInfo()
\r
3403 BOOL result = FALSE;
\r
3405 if( highlightInfo.sq[0].x >= 0 && highlightInfo.sq[0].y >= 0 &&
\r
3406 highlightInfo.sq[1].x >= 0 && highlightInfo.sq[1].y >= 0 )
\r
3417 BOOL IsDrawArrowEnabled()
\r
3419 BOOL result = FALSE;
\r
3421 if( appData.highlightMoveWithArrow && squareSize >= 32 ) {
\r
3428 VOID DrawArrowHighlight( HDC hdc )
\r
3430 if( IsDrawArrowEnabled() && HasHighlightInfo() ) {
\r
3431 DrawArrowBetweenSquares( hdc,
\r
3432 highlightInfo.sq[0].x, highlightInfo.sq[0].y,
\r
3433 highlightInfo.sq[1].x, highlightInfo.sq[1].y );
\r
3437 HRGN GetArrowHighlightClipRegion( HDC hdc )
\r
3439 HRGN result = NULL;
\r
3441 if( HasHighlightInfo() ) {
\r
3442 int x1, y1, x2, y2;
\r
3443 int sx, sy, dx, dy;
\r
3445 SquareToPos(highlightInfo.sq[0].y, highlightInfo.sq[0].x, &x1, &y1 );
\r
3446 SquareToPos(highlightInfo.sq[1].y, highlightInfo.sq[1].x, &x2, &y2 );
\r
3448 sx = MIN( x1, x2 );
\r
3449 sy = MIN( y1, y2 );
\r
3450 dx = MAX( x1, x2 ) + squareSize;
\r
3451 dy = MAX( y1, y2 ) + squareSize;
\r
3453 result = CreateRectRgn( sx, sy, dx, dy );
\r
3460 Warning: this function modifies the behavior of several other functions.
\r
3462 Basically, Winboard is optimized to avoid drawing the whole board if not strictly
\r
3463 needed. Unfortunately, the decision whether or not to perform a full or partial
\r
3464 repaint is scattered all over the place, which is not good for features such as
\r
3465 "arrow highlighting" that require a full repaint of the board.
\r
3467 So, I've tried to patch the code where I thought it made sense (e.g. after or during
\r
3468 user interaction, when speed is not so important) but especially to avoid errors
\r
3469 in the displayed graphics.
\r
3471 In such patched places, I always try refer to this function so there is a single
\r
3472 place to maintain knowledge.
\r
3474 To restore the original behavior, just return FALSE unconditionally.
\r
3476 BOOL IsFullRepaintPreferrable()
\r
3478 BOOL result = FALSE;
\r
3480 if( (appData.highlightLastMove || appData.highlightDragging) && IsDrawArrowEnabled() ) {
\r
3481 /* Arrow may appear on the board */
\r
3489 This function is called by DrawPosition to know whether a full repaint must
\r
3492 Only DrawPosition may directly call this function, which makes use of
\r
3493 some state information. Other function should call DrawPosition specifying
\r
3494 the repaint flag, and can use IsFullRepaintPreferrable if needed.
\r
3496 BOOL DrawPositionNeedsFullRepaint()
\r
3498 BOOL result = FALSE;
\r
3501 Probably a slightly better policy would be to trigger a full repaint
\r
3502 when animInfo.piece changes state (i.e. empty -> non-empty and viceversa),
\r
3503 but animation is fast enough that it's difficult to notice.
\r
3505 if( animInfo.piece == EmptySquare ) {
\r
3506 if( (appData.highlightLastMove || appData.highlightDragging) && IsDrawArrowEnabled() /*&& HasHighlightInfo()*/ ) {
\r
3514 static HBITMAP borderBitmap;
\r
3517 DrawBackgroundOnDC(HDC hdc)
\r
3523 static char oldBorder[MSG_SIZ];
\r
3524 int w = 600, h = 600, mode;
\r
3526 if(strcmp(appData.border, oldBorder)) { // load new one when old one no longer valid
\r
3527 strncpy(oldBorder, appData.border, MSG_SIZ-1);
\r
3528 borderBitmap = LoadImage( 0, appData.border, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );
\r
3530 if(borderBitmap == NULL) { // loading failed, use white
\r
3531 FillRect( hdc, &boardRect, whitePieceBrush );
\r
3534 tmphdc = CreateCompatibleDC(hdc);
\r
3535 hbm = SelectObject(tmphdc, borderBitmap);
\r
3536 if( GetObject( borderBitmap, sizeof(bi), &bi ) > 0 ) {
\r
3540 mode = SetStretchBltMode(hdc, COLORONCOLOR);
\r
3541 StretchBlt(hdc, boardRect.left, boardRect.top, boardRect.right - boardRect.left,
\r
3542 boardRect.bottom - boardRect.top, tmphdc, 0, 0, w, h, SRCCOPY);
\r
3543 SetStretchBltMode(hdc, mode);
\r
3544 SelectObject(tmphdc, hbm);
\r
3549 DrawBoardOnDC(HDC hdc, Board board, HDC tmphdc)
\r
3551 int row, column, x, y, square_color, piece_color;
\r
3552 ChessSquare piece;
\r
3554 HDC texture_hdc = NULL;
\r
3556 /* [AS] Initialize background textures if needed */
\r
3557 if( liteBackTexture != NULL || darkBackTexture != NULL ) {
\r
3558 static int backTextureBoardSize; /* [HGM] boardsize: also new texture if board format changed */
\r
3559 if( backTextureSquareSize != squareSize
\r
3560 || backTextureBoardSize != BOARD_WIDTH+BOARD_FILES*BOARD_HEIGHT) {
\r
3561 backTextureBoardSize = BOARD_WIDTH+BOARD_FILES*BOARD_HEIGHT;
\r
3562 backTextureSquareSize = squareSize;
\r
3563 RebuildTextureSquareInfo();
\r
3566 texture_hdc = CreateCompatibleDC( hdc );
\r
3569 for (row = 0; row < BOARD_HEIGHT; row++) {
\r
3570 for (column = 0; column < BOARD_WIDTH; column++) {
\r
3572 SquareToPos(row, column, &x, &y);
\r
3574 piece = board[row][column];
\r
3576 square_color = ((column + row) % 2) == 1;
\r
3577 if( gameInfo.variant == VariantXiangqi ) {
\r
3578 square_color = !InPalace(row, column);
\r
3579 if(BOARD_HEIGHT&1) { if(row==BOARD_HEIGHT/2) square_color ^= 1; }
\r
3580 else if(row < BOARD_HEIGHT/2) square_color ^= 1;
\r
3582 piece_color = (int) piece < (int) BlackPawn;
\r
3585 /* [HGM] holdings file: light square or black */
\r
3586 if(column == BOARD_LEFT-2) {
\r
3587 if( row > BOARD_HEIGHT - gameInfo.holdingsSize - 1 )
\r
3590 DisplayHoldingsCount(hdc, x, y, 0, 0); /* black out */
\r
3594 if(column == BOARD_RGHT + 1 ) {
\r
3595 if( row < gameInfo.holdingsSize )
\r
3598 DisplayHoldingsCount(hdc, x, y, 0, 0);
\r
3602 if(column == BOARD_LEFT-1 ) /* left align */
\r
3603 DisplayHoldingsCount(hdc, x, y, flipView, (int) board[row][column]);
\r
3604 else if( column == BOARD_RGHT) /* right align */
\r
3605 DisplayHoldingsCount(hdc, x, y, !flipView, (int) board[row][column]);
\r
3606 else if( piece == DarkSquare) DisplayHoldingsCount(hdc, x, y, 0, 0);
\r
3608 if (appData.monoMode) {
\r
3609 if (piece == EmptySquare) {
\r
3610 BitBlt(hdc, x, y, squareSize, squareSize, 0, 0, 0,
\r
3611 square_color ? WHITENESS : BLACKNESS);
\r
3613 DrawPieceOnDC(hdc, piece, piece_color, square_color, x, y, tmphdc);
\r
3616 else if( appData.useBitmaps && backTextureSquareInfo[row][column].mode > 0 ) {
\r
3617 /* [AS] Draw the square using a texture bitmap */
\r
3618 HBITMAP hbm = SelectObject( texture_hdc, square_color ? liteBackTexture : darkBackTexture );
\r
3619 int r = row, c = column; // [HGM] do not flip board in flipView
\r
3620 if(flipView) { r = BOARD_HEIGHT-1 - r; c = BOARD_WIDTH-1 - c; }
\r
3623 squareSize, squareSize,
\r
3626 backTextureSquareInfo[r][c].mode,
\r
3627 backTextureSquareInfo[r][c].x,
\r
3628 backTextureSquareInfo[r][c].y );
\r
3630 SelectObject( texture_hdc, hbm );
\r
3632 if (piece != EmptySquare) {
\r
3633 DrawPieceOnDC(hdc, piece, piece_color, -1, x, y, tmphdc);
\r
3637 HBRUSH brush = square_color ? lightSquareBrush : darkSquareBrush;
\r
3639 oldBrush = SelectObject(hdc, brush );
\r
3640 BitBlt(hdc, x, y, squareSize, squareSize, 0, 0, 0, PATCOPY);
\r
3641 SelectObject(hdc, oldBrush);
\r
3642 if (piece != EmptySquare)
\r
3643 DrawPieceOnDC(hdc, piece, piece_color, -1, x, y, tmphdc);
\r
3648 if( texture_hdc != NULL ) {
\r
3649 DeleteDC( texture_hdc );
\r
3653 int saveDiagFlag = 0; FILE *diagFile; // [HGM] diag
\r
3654 void fputDW(FILE *f, int x)
\r
3656 fputc(x & 255, f);
\r
3657 fputc(x>>8 & 255, f);
\r
3658 fputc(x>>16 & 255, f);
\r
3659 fputc(x>>24 & 255, f);
\r
3662 #define MAX_CLIPS 200 /* more than enough */
\r
3665 DrawLogoOnDC(HDC hdc, RECT logoRect, HBITMAP logo)
\r
3667 // HBITMAP bufferBitmap;
\r
3672 int w = 100, h = 50;
\r
3674 if(logo == NULL) {
\r
3675 if(!logoHeight) return;
\r
3676 FillRect( hdc, &logoRect, whitePieceBrush );
\r
3678 // GetClientRect(hwndMain, &Rect);
\r
3679 // bufferBitmap = CreateCompatibleBitmap(hdc, Rect.right-Rect.left+1,
\r
3680 // Rect.bottom-Rect.top+1);
\r
3681 tmphdc = CreateCompatibleDC(hdc);
\r
3682 hbm = SelectObject(tmphdc, logo);
\r
3683 if( GetObject( logo, sizeof(bi), &bi ) > 0 ) {
\r
3687 StretchBlt(hdc, logoRect.left, logoRect.top, logoRect.right - logoRect.left,
\r
3688 logoRect.bottom - logoRect.top, tmphdc, 0, 0, w, h, SRCCOPY);
\r
3689 SelectObject(tmphdc, hbm);
\r
3697 HDC hdc = GetDC(hwndMain);
\r
3698 HBITMAP whiteLogo = (HBITMAP) first.programLogo, blackLogo = (HBITMAP) second.programLogo;
\r
3699 if(appData.autoLogo) {
\r
3701 switch(gameMode) { // pick logos based on game mode
\r
3702 case IcsObserving:
\r
3703 whiteLogo = second.programLogo; // ICS logo
\r
3704 blackLogo = second.programLogo;
\r
3707 case IcsPlayingWhite:
\r
3708 if(!appData.zippyPlay) whiteLogo = userLogo;
\r
3709 blackLogo = second.programLogo; // ICS logo
\r
3711 case IcsPlayingBlack:
\r
3712 whiteLogo = second.programLogo; // ICS logo
\r
3713 blackLogo = appData.zippyPlay ? first.programLogo : userLogo;
\r
3715 case TwoMachinesPlay:
\r
3716 if(first.twoMachinesColor[0] == 'b') {
\r
3717 whiteLogo = second.programLogo;
\r
3718 blackLogo = first.programLogo;
\r
3721 case MachinePlaysWhite:
\r
3722 blackLogo = userLogo;
\r
3724 case MachinePlaysBlack:
\r
3725 whiteLogo = userLogo;
\r
3726 blackLogo = first.programLogo;
\r
3729 DrawLogoOnDC(hdc, leftLogoRect, flipClock ? blackLogo : whiteLogo);
\r
3730 DrawLogoOnDC(hdc, rightLogoRect, flipClock ? whiteLogo : blackLogo);
\r
3731 ReleaseDC(hwndMain, hdc);
\r
3736 UpdateLogos(int display)
\r
3737 { // called after loading new engine(s), in tourney or from menu
\r
3738 LoadLogo(&first, 0, FALSE);
\r
3739 LoadLogo(&second, 1, appData.icsActive);
\r
3740 InitDrawingSizes(-2, 0); // adapt layout of board window to presence/absence of logos
\r
3741 if(display) DisplayLogos();
\r
3744 static HDC hdcSeek;
\r
3746 // [HGM] seekgraph
\r
3747 void DrawSeekAxis( int x, int y, int xTo, int yTo )
\r
3750 HPEN hp = SelectObject( hdcSeek, gridPen );
\r
3751 MoveToEx( hdcSeek, boardRect.left+x, boardRect.top+y, &stPt );
\r
3752 LineTo( hdcSeek, boardRect.left+xTo, boardRect.top+yTo );
\r
3753 SelectObject( hdcSeek, hp );
\r
3756 // front-end wrapper for drawing functions to do rectangles
\r
3757 void DrawSeekBackground( int left, int top, int right, int bottom )
\r
3762 if (hdcSeek == NULL) {
\r
3763 hdcSeek = GetDC(hwndMain);
\r
3764 if (!appData.monoMode) {
\r
3765 SelectPalette(hdcSeek, hPal, FALSE);
\r
3766 RealizePalette(hdcSeek);
\r
3769 hp = SelectObject( hdcSeek, gridPen );
\r
3770 rc.top = boardRect.top+top; rc.left = boardRect.left+left;
\r
3771 rc.bottom = boardRect.top+bottom; rc.right = boardRect.left+right;
\r
3772 FillRect( hdcSeek, &rc, lightSquareBrush );
\r
3773 SelectObject( hdcSeek, hp );
\r
3776 // front-end wrapper for putting text in graph
\r
3777 void DrawSeekText(char *buf, int x, int y)
\r
3780 SetBkMode( hdcSeek, TRANSPARENT );
\r
3781 GetTextExtentPoint32( hdcSeek, buf, strlen(buf), &stSize );
\r
3782 TextOut( hdcSeek, boardRect.left+x-3, boardRect.top+y-stSize.cy/2, buf, strlen(buf) );
\r
3785 void DrawSeekDot(int x, int y, int color)
\r
3787 int square = color & 0x80;
\r
3788 HBRUSH oldBrush = SelectObject(hdcSeek,
\r
3789 color == 0 ? markerBrush[1] : color == 1 ? darkSquareBrush : explodeBrush);
\r
3792 Rectangle(hdcSeek, boardRect.left+x - squareSize/9, boardRect.top+y - squareSize/9,
\r
3793 boardRect.left+x + squareSize/9, boardRect.top+y + squareSize/9);
\r
3795 Ellipse(hdcSeek, boardRect.left+x - squareSize/8, boardRect.top+y - squareSize/8,
\r
3796 boardRect.left+x + squareSize/8, boardRect.top+y + squareSize/8);
\r
3797 SelectObject(hdcSeek, oldBrush);
\r
3800 void DrawSeekOpen()
\r
3804 void DrawSeekClose()
\r
3813 HDCDrawPosition(HDC hdc, BOOLEAN repaint, Board board)
\r
3815 static Board lastReq[2], lastDrawn[2];
\r
3816 static HighlightInfo lastDrawnHighlight, lastDrawnPremove;
\r
3817 static int lastDrawnFlipView = 0;
\r
3818 static int lastReqValid[2] = {0, 0}, lastDrawnValid[2] = {0, 0};
\r
3819 int releaseDC, x, y, x2, y2, row, column, num_clips = 0, i;
\r
3822 HBITMAP bufferBitmap;
\r
3823 HBITMAP oldBitmap;
\r
3825 HRGN clips[MAX_CLIPS];
\r
3826 ChessSquare dragged_piece = EmptySquare;
\r
3827 int nr = twoBoards*partnerUp;
\r
3829 /* I'm undecided on this - this function figures out whether a full
\r
3830 * repaint is necessary on its own, so there's no real reason to have the
\r
3831 * caller tell it that. I think this can safely be set to FALSE - but
\r
3832 * if we trust the callers not to request full repaints unnessesarily, then
\r
3833 * we could skip some clipping work. In other words, only request a full
\r
3834 * redraw when the majority of pieces have changed positions (ie. flip,
\r
3835 * gamestart and similar) --Hawk
\r
3837 Boolean fullrepaint = repaint;
\r
3839 if(DrawSeekGraph()) return; // [HG} seekgraph: suppress printing board if seek graph up
\r
3841 if( DrawPositionNeedsFullRepaint() ) {
\r
3842 fullrepaint = TRUE;
\r
3845 if (board == NULL) {
\r
3846 if (!lastReqValid[nr]) {
\r
3849 board = lastReq[nr];
\r
3851 CopyBoard(lastReq[nr], board);
\r
3852 lastReqValid[nr] = 1;
\r
3855 if (doingSizing) {
\r
3859 if (IsIconic(hwndMain)) {
\r
3863 if (hdc == NULL) {
\r
3864 hdc = GetDC(hwndMain);
\r
3865 if (!appData.monoMode) {
\r
3866 SelectPalette(hdc, hPal, FALSE);
\r
3867 RealizePalette(hdc);
\r
3871 releaseDC = FALSE;
\r
3874 /* Create some work-DCs */
\r
3875 hdcmem = CreateCompatibleDC(hdc);
\r
3876 tmphdc = CreateCompatibleDC(hdc);
\r
3878 /* If dragging is in progress, we temporarely remove the piece */
\r
3879 /* [HGM] or temporarily decrease count if stacked */
\r
3880 /* !! Moved to before board compare !! */
\r
3881 if (dragInfo.from.x >= 0 && dragInfo.pos.x >= 0) {
\r
3882 dragged_piece = board[dragInfo.from.y][dragInfo.from.x];
\r
3883 if(dragInfo.from.x == BOARD_LEFT-2 ) {
\r
3884 if(--board[dragInfo.from.y][dragInfo.from.x+1] == 0 )
\r
3885 board[dragInfo.from.y][dragInfo.from.x] = EmptySquare;
\r
3887 if(dragInfo.from.x == BOARD_RGHT+1) {
\r
3888 if(--board[dragInfo.from.y][dragInfo.from.x-1] == 0 )
\r
3889 board[dragInfo.from.y][dragInfo.from.x] = EmptySquare;
\r
3891 board[dragInfo.from.y][dragInfo.from.x] = gatingPiece;
\r
3894 /* Figure out which squares need updating by comparing the
\r
3895 * newest board with the last drawn board and checking if
\r
3896 * flipping has changed.
\r
3898 if (!fullrepaint && lastDrawnValid[nr] && (nr == 1 || lastDrawnFlipView == flipView)) {
\r
3899 for (row = 0; row < BOARD_HEIGHT; row++) { /* [HGM] true size, not 8 */
\r
3900 for (column = 0; column < BOARD_WIDTH; column++) {
\r
3901 if (lastDrawn[nr][row][column] != board[row][column]) {
\r
3902 SquareToPos(row, column, &x, &y);
\r
3903 clips[num_clips++] =
\r
3904 CreateRectRgn(x, y, x + squareSize, y + squareSize);
\r
3908 if(nr == 0) { // [HGM] dual: no highlights on second board
\r
3909 for (i=0; i<2; i++) {
\r
3910 if (lastDrawnHighlight.sq[i].x != highlightInfo.sq[i].x ||
\r
3911 lastDrawnHighlight.sq[i].y != highlightInfo.sq[i].y) {
\r
3912 if (lastDrawnHighlight.sq[i].x >= 0 &&
\r
3913 lastDrawnHighlight.sq[i].y >= 0) {
\r
3914 SquareToPos(lastDrawnHighlight.sq[i].y,
\r
3915 lastDrawnHighlight.sq[i].x, &x, &y);
\r
3916 clips[num_clips++] =
\r
3917 CreateRectRgn(x - lineGap, y - lineGap,
\r
3918 x + squareSize + lineGap, y + squareSize + lineGap);
\r
3920 if (highlightInfo.sq[i].x >= 0 && highlightInfo.sq[i].y >= 0) {
\r
3921 SquareToPos(highlightInfo.sq[i].y, highlightInfo.sq[i].x, &x, &y);
\r
3922 clips[num_clips++] =
\r
3923 CreateRectRgn(x - lineGap, y - lineGap,
\r
3924 x + squareSize + lineGap, y + squareSize + lineGap);
\r
3928 for (i=0; i<2; i++) {
\r
3929 if (lastDrawnPremove.sq[i].x != premoveHighlightInfo.sq[i].x ||
\r
3930 lastDrawnPremove.sq[i].y != premoveHighlightInfo.sq[i].y) {
\r
3931 if (lastDrawnPremove.sq[i].x >= 0 &&
\r
3932 lastDrawnPremove.sq[i].y >= 0) {
\r
3933 SquareToPos(lastDrawnPremove.sq[i].y,
\r
3934 lastDrawnPremove.sq[i].x, &x, &y);
\r
3935 clips[num_clips++] =
\r
3936 CreateRectRgn(x - lineGap, y - lineGap,
\r
3937 x + squareSize + lineGap, y + squareSize + lineGap);
\r
3939 if (premoveHighlightInfo.sq[i].x >= 0 &&
\r
3940 premoveHighlightInfo.sq[i].y >= 0) {
\r
3941 SquareToPos(premoveHighlightInfo.sq[i].y,
\r
3942 premoveHighlightInfo.sq[i].x, &x, &y);
\r
3943 clips[num_clips++] =
\r
3944 CreateRectRgn(x - lineGap, y - lineGap,
\r
3945 x + squareSize + lineGap, y + squareSize + lineGap);
\r
3949 } else { // nr == 1
\r
3950 partnerHighlightInfo.sq[0].y = board[EP_STATUS-4];
\r
3951 partnerHighlightInfo.sq[0].x = board[EP_STATUS-3];
\r
3952 partnerHighlightInfo.sq[1].y = board[EP_STATUS-2];
\r
3953 partnerHighlightInfo.sq[1].x = board[EP_STATUS-1];
\r
3954 for (i=0; i<2; i++) {
\r
3955 if (partnerHighlightInfo.sq[i].x >= 0 &&
\r
3956 partnerHighlightInfo.sq[i].y >= 0) {
\r
3957 SquareToPos(partnerHighlightInfo.sq[i].y,
\r
3958 partnerHighlightInfo.sq[i].x, &x, &y);
\r
3959 clips[num_clips++] =
\r
3960 CreateRectRgn(x - lineGap, y - lineGap,
\r
3961 x + squareSize + lineGap, y + squareSize + lineGap);
\r
3963 if (oldPartnerHighlight.sq[i].x >= 0 &&
\r
3964 oldPartnerHighlight.sq[i].y >= 0) {
\r
3965 SquareToPos(oldPartnerHighlight.sq[i].y,
\r
3966 oldPartnerHighlight.sq[i].x, &x, &y);
\r
3967 clips[num_clips++] =
\r
3968 CreateRectRgn(x - lineGap, y - lineGap,
\r
3969 x + squareSize + lineGap, y + squareSize + lineGap);
\r
3974 fullrepaint = TRUE;
\r
3977 /* Create a buffer bitmap - this is the actual bitmap
\r
3978 * being written to. When all the work is done, we can
\r
3979 * copy it to the real DC (the screen). This avoids
\r
3980 * the problems with flickering.
\r
3982 GetClientRect(hwndMain, &Rect);
\r
3983 bufferBitmap = CreateCompatibleBitmap(hdc, Rect.right-Rect.left+1,
\r
3984 Rect.bottom-Rect.top+1);
\r
3985 oldBitmap = SelectObject(hdcmem, bufferBitmap);
\r
3986 if (!appData.monoMode) {
\r
3987 SelectPalette(hdcmem, hPal, FALSE);
\r
3990 /* Create clips for dragging */
\r
3991 if (!fullrepaint) {
\r
3992 if (dragInfo.from.x >= 0) {
\r
3993 SquareToPos(dragInfo.from.y, dragInfo.from.x, &x, &y);
\r
3994 clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);
\r
3996 if (dragInfo.start.x >= 0) {
\r
3997 SquareToPos(dragInfo.start.y, dragInfo.start.x, &x, &y);
\r
3998 clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);
\r
4000 if (dragInfo.pos.x >= 0) {
\r
4001 x = dragInfo.pos.x - squareSize / 2;
\r
4002 y = dragInfo.pos.y - squareSize / 2;
\r
4003 clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);
\r
4005 if (dragInfo.lastpos.x >= 0) {
\r
4006 x = dragInfo.lastpos.x - squareSize / 2;
\r
4007 y = dragInfo.lastpos.y - squareSize / 2;
\r
4008 clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);
\r
4012 /* Are we animating a move?
\r
4014 * - remove the piece from the board (temporarely)
\r
4015 * - calculate the clipping region
\r
4017 if (!fullrepaint) {
\r
4018 if (animInfo.piece != EmptySquare) {
\r
4019 board[animInfo.from.y][animInfo.from.x] = EmptySquare;
\r
4020 x = boardRect.left + animInfo.lastpos.x;
\r
4021 y = boardRect.top + animInfo.lastpos.y;
\r
4022 x2 = boardRect.left + animInfo.pos.x;
\r
4023 y2 = boardRect.top + animInfo.pos.y;
\r
4024 clips[num_clips++] = CreateRectRgn(MIN(x,x2), MIN(y,y2), MAX(x,x2)+squareSize, MAX(y,y2)+squareSize);
\r
4025 /* Slight kludge. The real problem is that after AnimateMove is
\r
4026 done, the position on the screen does not match lastDrawn.
\r
4027 This currently causes trouble only on e.p. captures in
\r
4028 atomic, where the piece moves to an empty square and then
\r
4029 explodes. The old and new positions both had an empty square
\r
4030 at the destination, but animation has drawn a piece there and
\r
4031 we have to remember to erase it. [HGM] moved until after setting lastDrawn */
\r
4033 lastDrawn[0][animInfo.to.y][animInfo.to.x] = animInfo.piece;
\r
4037 /* No clips? Make sure we have fullrepaint set to TRUE */
\r
4038 if (num_clips == 0)
\r
4039 fullrepaint = TRUE;
\r
4041 /* Set clipping on the memory DC */
\r
4042 if (!fullrepaint) {
\r
4043 SelectClipRgn(hdcmem, clips[0]);
\r
4044 for (x = 1; x < num_clips; x++) {
\r
4045 if (ExtSelectClipRgn(hdcmem, clips[x], RGN_OR) == ERROR)
\r
4046 abort(); // this should never ever happen!
\r
4050 /* Do all the drawing to the memory DC */
\r
4051 if(explodeInfo.radius) { // [HGM] atomic
\r
4053 int x, y, r=(explodeInfo.radius * squareSize)/100;
\r
4054 ChessSquare piece = board[explodeInfo.fromY][explodeInfo.fromX];
\r
4055 board[explodeInfo.fromY][explodeInfo.fromX] = EmptySquare; // suppress display of capturer
\r
4056 SquareToPos(explodeInfo.toY, explodeInfo.toX, &x, &y);
\r
4057 x += squareSize/2;
\r
4058 y += squareSize/2;
\r
4059 if(!fullrepaint) {
\r
4060 clips[num_clips] = CreateRectRgn(x-r, y-r, x+r, y+r);
\r
4061 ExtSelectClipRgn(hdcmem, clips[num_clips++], RGN_OR);
\r
4063 DrawGridOnDC(hdcmem);
\r
4064 DrawHighlightsOnDC(hdcmem, &highlightInfo, HIGHLIGHT_PEN);
\r
4065 DrawHighlightsOnDC(hdcmem, &premoveHighlightInfo, PREMOVE_PEN);
\r
4066 DrawBoardOnDC(hdcmem, board, tmphdc);
\r
4067 board[explodeInfo.fromY][explodeInfo.fromX] = piece;
\r
4068 oldBrush = SelectObject(hdcmem, explodeBrush);
\r
4069 Ellipse(hdcmem, x-r, y-r, x+r, y+r);
\r
4070 SelectObject(hdcmem, oldBrush);
\r
4072 if(border) DrawBackgroundOnDC(hdcmem);
\r
4073 DrawGridOnDC(hdcmem);
\r
4074 if(nr == 0) { // [HGM] dual: decide which highlights to draw
\r
4075 DrawHighlightsOnDC(hdcmem, &highlightInfo, HIGHLIGHT_PEN);
\r
4076 DrawHighlightsOnDC(hdcmem, &premoveHighlightInfo, PREMOVE_PEN);
\r
4078 DrawHighlightsOnDC(hdcmem, &partnerHighlightInfo, HIGHLIGHT_PEN);
\r
4079 oldPartnerHighlight = partnerHighlightInfo;
\r
4081 DrawBoardOnDC(hdcmem, board, tmphdc);
\r
4083 if(nr == 0) // [HGM] dual: markers only on left board
\r
4084 for (row = 0; row < BOARD_HEIGHT; row++) {
\r
4085 for (column = 0; column < BOARD_WIDTH; column++) {
\r
4086 if (marker[row][column]) { // marker changes only occur with full repaint!
\r
4087 HBRUSH oldBrush = SelectObject(hdcmem, markerBrush[marker[row][column]-1]);
\r
4088 SquareToPos(row, column, &x, &y);
\r
4089 Ellipse(hdcmem, x + squareSize/4, y + squareSize/4,
\r
4090 x + 3*squareSize/4, y + 3*squareSize/4);
\r
4091 SelectObject(hdcmem, oldBrush);
\r
4096 if( appData.highlightMoveWithArrow ) {
\r
4098 DrawArrowHighlight(hdcmem);
\r
4101 DrawCoordsOnDC(hdcmem);
\r
4103 CopyBoard(lastDrawn[nr], board); /* [HGM] Moved to here from end of routine, */
\r
4104 /* to make sure lastDrawn contains what is actually drawn */
\r
4106 /* Put the dragged piece back into place and draw it (out of place!) */
\r
4107 if (dragged_piece != EmptySquare) {
\r
4108 /* [HGM] or restack */
\r
4109 if(dragInfo.from.x == BOARD_LEFT-2 )
\r
4110 board[dragInfo.from.y][dragInfo.from.x+1]++;
\r
4112 if(dragInfo.from.x == BOARD_RGHT+1 )
\r
4113 board[dragInfo.from.y][dragInfo.from.x-1]++;
\r
4115 board[dragInfo.from.y][dragInfo.from.x] = dragged_piece;
\r
4116 x = dragInfo.pos.x - squareSize / 2;
\r
4117 y = dragInfo.pos.y - squareSize / 2;
\r
4118 DrawPieceOnDC(hdcmem, dragInfo.piece,
\r
4119 ((int) dragInfo.piece < (int) BlackPawn),
\r
4120 (dragInfo.from.y + dragInfo.from.x) % 2, x, y, tmphdc);
\r
4123 /* Put the animated piece back into place and draw it */
\r
4124 if (animInfo.piece != EmptySquare) {
\r
4125 board[animInfo.from.y][animInfo.from.x] = animInfo.piece;
\r
4126 x = boardRect.left + animInfo.pos.x;
\r
4127 y = boardRect.top + animInfo.pos.y;
\r
4128 DrawPieceOnDC(hdcmem, animInfo.piece,
\r
4129 ((int) animInfo.piece < (int) BlackPawn),
\r
4130 (animInfo.from.y + animInfo.from.x) % 2, x, y, tmphdc);
\r
4133 /* Release the bufferBitmap by selecting in the old bitmap
\r
4134 * and delete the memory DC
\r
4136 SelectObject(hdcmem, oldBitmap);
\r
4139 /* Set clipping on the target DC */
\r
4140 if (!fullrepaint) {
\r
4141 if(nr == 1) for (x = 0; x < num_clips; x++) { // [HGM] dual: translate clips
\r
4143 GetRgnBox(clips[x], &rect);
\r
4144 DeleteObject(clips[x]);
\r
4145 clips[x] = CreateRectRgn(rect.left + wpMain.width/2, rect.top,
\r
4146 rect.right + wpMain.width/2, rect.bottom);
\r
4148 SelectClipRgn(hdc, clips[0]);
\r
4149 for (x = 1; x < num_clips; x++) {
\r
4150 if (ExtSelectClipRgn(hdc, clips[x], RGN_OR) == ERROR)
\r
4151 abort(); // this should never ever happen!
\r
4155 /* Copy the new bitmap onto the screen in one go.
\r
4156 * This way we avoid any flickering
\r
4158 oldBitmap = SelectObject(tmphdc, bufferBitmap);
\r
4159 BitBlt(hdc, boardRect.left + twoBoards*partnerUp*wpMain.width/2, boardRect.top, // [HGM] dual
\r
4160 boardRect.right - boardRect.left,
\r
4161 boardRect.bottom - boardRect.top,
\r
4162 tmphdc, boardRect.left, boardRect.top, SRCCOPY);
\r
4163 if(saveDiagFlag) {
\r
4164 BITMAP b; int i, j=0, m, w, wb, fac=0; char *pData;
\r
4165 BITMAPINFOHEADER bih; int color[16], nrColors=0;
\r
4166 HBITMAP src = bufferBitmap, obmp; HDC tmp = CreateCompatibleDC(hdc);
\r
4168 bufferBitmap = CreateCompatibleBitmap(hdc, boardRect.right-boardRect.left, Rect.bottom-Rect.top-2*OUTER_MARGIN);
\r
4169 obmp = SelectObject(tmp, bufferBitmap);
\r
4170 BitBlt(tmp, 0, 0, boardRect.right - boardRect.left, Rect.bottom - Rect.top - 2*OUTER_MARGIN,
\r
4171 tmphdc, boardRect.left, OUTER_MARGIN, SRCCOPY);
\r
4172 GetObject(bufferBitmap, sizeof(b), &b);
\r
4173 if(pData = malloc(b.bmWidthBytes*b.bmHeight + 10000)) {
\r
4174 bih.biSize = sizeof(BITMAPINFOHEADER);
\r
4175 bih.biWidth = b.bmWidth;
\r
4176 bih.biHeight = b.bmHeight;
\r
4178 bih.biBitCount = b.bmBitsPixel;
\r
4179 bih.biCompression = 0;
\r
4180 bih.biSizeImage = b.bmWidthBytes*b.bmHeight;
\r
4181 bih.biXPelsPerMeter = 0;
\r
4182 bih.biYPelsPerMeter = 0;
\r
4183 bih.biClrUsed = 0;
\r
4184 bih.biClrImportant = 0;
\r
4185 // fprintf(diagFile, "t=%d\nw=%d\nh=%d\nB=%d\nP=%d\nX=%d\n",
\r
4186 // b.bmType, b.bmWidth, b.bmHeight, b.bmWidthBytes, b.bmPlanes, b.bmBitsPixel);
\r
4187 GetDIBits(tmphdc,bufferBitmap,0,b.bmHeight,pData,(BITMAPINFO*)&bih,DIB_RGB_COLORS);
\r
4188 // fprintf(diagFile, "%8x\n", (int) pData);
\r
4190 wb = b.bmWidthBytes;
\r
4192 for(i=0; i<wb*(b.bmHeight - boardRect.top + OUTER_MARGIN)>>2; i++) {
\r
4193 int k = ((int*) pData)[i];
\r
4194 for(j=0; j<nrColors; j++) if(color[j] == k) break;
\r
4195 if(j >= 16) break;
\r
4197 if(j >= nrColors) nrColors = j+1;
\r
4199 if(j<16) { // 16 colors is enough. Compress to 4 bits per pixel
\r
4201 for(i=0; i<b.bmHeight - boardRect.top + OUTER_MARGIN; i++) {
\r
4202 for(w=0; w<(wb>>2); w+=2) {
\r
4203 int k = ((int*) pData)[(wb*i>>2) + w];
\r
4204 for(j=0; j<nrColors; j++) if(color[j] == k) break;
\r
4205 k = ((int*) pData)[(wb*i>>2) + w + 1];
\r
4206 for(m=0; m<nrColors; m++) if(color[m] == k) break;
\r
4207 pData[p++] = m | j<<4;
\r
4209 while(p&3) pData[p++] = 0;
\r
4212 wb = ((wb+31)>>5)<<2;
\r
4214 // write BITMAPFILEHEADER
\r
4215 fprintf(diagFile, "BM");
\r
4216 fputDW(diagFile, wb*(b.bmHeight - boardRect.top + OUTER_MARGIN)+0x36 + (fac?64:0));
\r
4217 fputDW(diagFile, 0);
\r
4218 fputDW(diagFile, 0x36 + (fac?64:0));
\r
4219 // write BITMAPINFOHEADER
\r
4220 fputDW(diagFile, 40);
\r
4221 fputDW(diagFile, b.bmWidth);
\r
4222 fputDW(diagFile, b.bmHeight - boardRect.top + OUTER_MARGIN);
\r
4223 if(fac) fputDW(diagFile, 0x040001); // planes and bits/pixel
\r
4224 else fputDW(diagFile, 0x200001); // planes and bits/pixel
\r
4225 fputDW(diagFile, 0);
\r
4226 fputDW(diagFile, 0);
\r
4227 fputDW(diagFile, 0);
\r
4228 fputDW(diagFile, 0);
\r
4229 fputDW(diagFile, 0);
\r
4230 fputDW(diagFile, 0);
\r
4231 // write color table
\r
4233 for(i=0; i<16; i++) fputDW(diagFile, color[i]);
\r
4234 // write bitmap data
\r
4236 for(i=0; i<wb*(b.bmHeight - boardRect.top + OUTER_MARGIN); i++)
\r
4237 fputc(pData[i], diagFile);
\r
4240 DeleteObject(bufferBitmap); bufferBitmap = src;
\r
4241 SelectObject(tmp, obmp);
\r
4245 SelectObject(tmphdc, oldBitmap);
\r
4247 /* Massive cleanup */
\r
4248 for (x = 0; x < num_clips; x++)
\r
4249 DeleteObject(clips[x]);
\r
4252 DeleteObject(bufferBitmap);
\r
4255 ReleaseDC(hwndMain, hdc);
\r
4257 if (lastDrawnFlipView != flipView && nr == 0) {
\r
4259 CheckMenuItem(GetMenu(hwndMain),IDM_FlipView, MF_BYCOMMAND|MF_CHECKED);
\r
4261 CheckMenuItem(GetMenu(hwndMain),IDM_FlipView, MF_BYCOMMAND|MF_UNCHECKED);
\r
4264 /* CopyBoard(lastDrawn, board);*/
\r
4265 lastDrawnHighlight = highlightInfo;
\r
4266 lastDrawnPremove = premoveHighlightInfo;
\r
4267 lastDrawnFlipView = flipView;
\r
4268 lastDrawnValid[nr] = 1;
\r
4271 /* [HGM] diag: Save the current board display to the given open file and close the file */
\r
4276 saveDiagFlag = 1; diagFile = f;
\r
4277 HDCDrawPosition(NULL, TRUE, NULL);
\r
4285 /*---------------------------------------------------------------------------*\
\r
4286 | CLIENT PAINT PROCEDURE
\r
4287 | This is the main event-handler for the WM_PAINT message.
\r
4289 \*---------------------------------------------------------------------------*/
\r
4291 PaintProc(HWND hwnd)
\r
4297 if((hdc = BeginPaint(hwnd, &ps))) {
\r
4298 if (IsIconic(hwnd)) {
\r
4299 DrawIcon(hdc, 2, 2, iconCurrent);
\r
4301 if (!appData.monoMode) {
\r
4302 SelectPalette(hdc, hPal, FALSE);
\r
4303 RealizePalette(hdc);
\r
4305 HDCDrawPosition(hdc, 1, NULL);
\r
4306 if(twoBoards) { // [HGM] dual: also redraw other board in other orientation
\r
4307 flipView = !flipView; partnerUp = !partnerUp;
\r
4308 HDCDrawPosition(hdc, 1, NULL);
\r
4309 flipView = !flipView; partnerUp = !partnerUp;
\r
4312 SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);
\r
4313 ExtTextOut(hdc, messageRect.left, messageRect.top,
\r
4314 ETO_CLIPPED|ETO_OPAQUE,
\r
4315 &messageRect, messageText, strlen(messageText), NULL);
\r
4316 SelectObject(hdc, oldFont);
\r
4317 DisplayBothClocks();
\r
4320 EndPaint(hwnd,&ps);
\r
4328 * If the user selects on a border boundary, return -1; if off the board,
\r
4329 * return -2. Otherwise map the event coordinate to the square.
\r
4330 * The offset boardRect.left or boardRect.top must already have been
\r
4331 * subtracted from x.
\r
4333 int EventToSquare(x, limit)
\r
4338 if (x < lineGap + border)
\r
4340 x -= lineGap + border;
\r
4341 if ((x % (squareSize + lineGap)) >= squareSize)
\r
4343 x /= (squareSize + lineGap);
\r
4355 DropEnable dropEnables[] = {
\r
4356 { 'P', DP_Pawn, N_("Pawn") },
\r
4357 { 'N', DP_Knight, N_("Knight") },
\r
4358 { 'B', DP_Bishop, N_("Bishop") },
\r
4359 { 'R', DP_Rook, N_("Rook") },
\r
4360 { 'Q', DP_Queen, N_("Queen") },
\r
4364 SetupDropMenu(HMENU hmenu)
\r
4366 int i, count, enable;
\r
4368 extern char white_holding[], black_holding[];
\r
4369 char item[MSG_SIZ];
\r
4371 for (i=0; i<sizeof(dropEnables)/sizeof(DropEnable); i++) {
\r
4372 p = strchr(gameMode == IcsPlayingWhite ? white_holding : black_holding,
\r
4373 dropEnables[i].piece);
\r
4375 while (p && *p++ == dropEnables[i].piece) count++;
\r
4376 snprintf(item, MSG_SIZ, "%s %d", T_(dropEnables[i].name), count);
\r
4377 enable = count > 0 || !appData.testLegality
\r
4378 /*!!temp:*/ || (gameInfo.variant == VariantCrazyhouse
\r
4379 && !appData.icsActive);
\r
4380 ModifyMenu(hmenu, dropEnables[i].command,
\r
4381 MF_BYCOMMAND | (enable ? MF_ENABLED : MF_GRAYED) | MF_STRING,
\r
4382 dropEnables[i].command, item);
\r
4386 void DragPieceBegin(int x, int y, Boolean instantly)
\r
4388 dragInfo.lastpos.x = boardRect.left + x;
\r
4389 dragInfo.lastpos.y = boardRect.top + y;
\r
4390 if(instantly) dragInfo.pos = dragInfo.lastpos;
\r
4391 dragInfo.from.x = fromX;
\r
4392 dragInfo.from.y = fromY;
\r
4393 dragInfo.piece = boards[currentMove][fromY][fromX];
\r
4394 dragInfo.start = dragInfo.from;
\r
4395 SetCapture(hwndMain);
\r
4398 void DragPieceEnd(int x, int y)
\r
4401 dragInfo.start.x = dragInfo.start.y = -1;
\r
4402 dragInfo.from = dragInfo.start;
\r
4403 dragInfo.pos = dragInfo.lastpos = dragInfo.start;
\r
4406 void ChangeDragPiece(ChessSquare piece)
\r
4408 dragInfo.piece = piece;
\r
4411 /* Event handler for mouse messages */
\r
4413 MouseEvent(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
\r
4417 static int recursive = 0;
\r
4419 BOOLEAN forceFullRepaint = IsFullRepaintPreferrable(); /* [AS] */
\r
4422 if (message == WM_MBUTTONUP) {
\r
4423 /* Hideous kludge to fool TrackPopupMenu into paying attention
\r
4424 to the middle button: we simulate pressing the left button too!
\r
4426 PostMessage(hwnd, WM_LBUTTONDOWN, wParam, lParam);
\r
4427 PostMessage(hwnd, WM_LBUTTONUP, wParam, lParam);
\r
4433 pt.x = LOWORD(lParam);
\r
4434 pt.y = HIWORD(lParam);
\r
4435 x = EventToSquare(pt.x - boardRect.left, BOARD_WIDTH);
\r
4436 y = EventToSquare(pt.y - boardRect.top, BOARD_HEIGHT);
\r
4437 if (!flipView && y >= 0) {
\r
4438 y = BOARD_HEIGHT - 1 - y;
\r
4440 if (flipView && x >= 0) {
\r
4441 x = BOARD_WIDTH - 1 - x;
\r
4444 shiftKey = GetKeyState(VK_SHIFT) < 0; // [HGM] remember last shift status
\r
4445 controlKey = GetKeyState(VK_CONTROL) < 0; // [HGM] remember last shift status
\r
4447 switch (message) {
\r
4448 case WM_LBUTTONDOWN:
\r
4449 if (PtInRect((LPRECT) &whiteRect, pt)) {
\r
4450 ClockClick(flipClock); break;
\r
4451 } else if (PtInRect((LPRECT) &blackRect, pt)) {
\r
4452 ClockClick(!flipClock); break;
\r
4454 if(dragging) { // [HGM] lion: don't destroy dragging info if we are already dragging
\r
4455 dragInfo.start.x = dragInfo.start.y = -1;
\r
4456 dragInfo.from = dragInfo.start;
\r
4458 if(fromX == -1 && frozen) { // not sure where this is for
\r
4459 fromX = fromY = -1;
\r
4460 DrawPosition(forceFullRepaint || FALSE, NULL); /* [AS] */
\r
4463 LeftClick(Press, pt.x - boardRect.left, pt.y - boardRect.top);
\r
4464 DrawPosition(TRUE, NULL);
\r
4467 case WM_LBUTTONUP:
\r
4468 LeftClick(Release, pt.x - boardRect.left, pt.y - boardRect.top);
\r
4469 DrawPosition(TRUE, NULL);
\r
4472 case WM_MOUSEMOVE:
\r
4473 if(SeekGraphClick(Press, pt.x - boardRect.left, pt.y - boardRect.top, 1)) break;
\r
4474 if(PromoScroll(pt.x - boardRect.left, pt.y - boardRect.top)) break;
\r
4475 MovePV(pt.x - boardRect.left, pt.y - boardRect.top, boardRect.bottom - boardRect.top);
\r
4476 if ((appData.animateDragging || appData.highlightDragging)
\r
4477 && (wParam & MK_LBUTTON || dragging == 2)
\r
4478 && dragInfo.from.x >= 0)
\r
4480 BOOL full_repaint = FALSE;
\r
4482 if (appData.animateDragging) {
\r
4483 dragInfo.pos = pt;
\r
4485 if (appData.highlightDragging) {
\r
4486 HoverEvent(highlightInfo.sq[1].x, highlightInfo.sq[1].y, x, y);
\r
4487 if( IsDrawArrowEnabled() && (x < 0 || x >= BOARD_WIDTH || y < 0 || y >= BOARD_HEIGHT) ) {
\r
4488 full_repaint = TRUE;
\r
4492 DrawPosition( full_repaint, NULL);
\r
4494 dragInfo.lastpos = dragInfo.pos;
\r
4498 case WM_MOUSEWHEEL: // [DM]
\r
4499 { static int lastDir = 0; // [HGM] build in some hysteresis to avoid spurious events
\r
4500 /* Mouse Wheel is being rolled forward
\r
4501 * Play moves forward
\r
4503 if((short)HIWORD(wParam) < 0 && currentMove < forwardMostMove)
\r
4504 { if(lastDir == 1) ForwardEvent(); else lastDir = 1; } // [HGM] suppress first event in direction
\r
4505 /* Mouse Wheel is being rolled backward
\r
4506 * Play moves backward
\r
4508 if((short)HIWORD(wParam) > 0 && currentMove > backwardMostMove)
\r
4509 { if(lastDir == -1) BackwardEvent(); else lastDir = -1; }
\r
4513 case WM_MBUTTONUP:
\r
4514 case WM_RBUTTONUP:
\r
4516 RightClick(Release, pt.x - boardRect.left, pt.y - boardRect.top, &fromX, &fromY);
\r
4519 case WM_MBUTTONDOWN:
\r
4520 case WM_RBUTTONDOWN:
\r
4523 fromX = fromY = -1;
\r
4524 dragInfo.pos.x = dragInfo.pos.y = -1;
\r
4525 dragInfo.start.x = dragInfo.start.y = -1;
\r
4526 dragInfo.from = dragInfo.start;
\r
4527 dragInfo.lastpos = dragInfo.pos;
\r
4528 if (appData.highlightDragging) {
\r
4529 ClearHighlights();
\r
4532 /* [HGM] right mouse button in clock area edit-game mode ups clock */
\r
4533 if (PtInRect((LPRECT) &whiteRect, pt)) {
\r
4534 if (GetKeyState(VK_SHIFT) < 0) AdjustClock(flipClock, 1);
\r
4535 } else if (PtInRect((LPRECT) &blackRect, pt)) {
\r
4536 if (GetKeyState(VK_SHIFT) < 0) AdjustClock(!flipClock, 1);
\r
4540 DrawPosition(TRUE, NULL);
\r
4542 menuNr = RightClick(Press, pt.x - boardRect.left, pt.y - boardRect.top, &fromX, &fromY);
\r
4545 if (message == WM_MBUTTONDOWN) {
\r
4546 buttonCount = 3; /* even if system didn't think so */
\r
4547 if (wParam & MK_SHIFT)
\r
4548 MenuPopup(hwnd, pt, LoadMenu(hInst, "BlackPieceMenu"), -1);
\r
4550 MenuPopup(hwnd, pt, LoadMenu(hInst, "WhitePieceMenu"), -1);
\r
4551 } else { /* message == WM_RBUTTONDOWN */
\r
4552 /* Just have one menu, on the right button. Windows users don't
\r
4553 think to try the middle one, and sometimes other software steals
\r
4554 it, or it doesn't really exist. */
\r
4555 if(gameInfo.variant != VariantShogi)
\r
4556 MenuPopup(hwnd, pt, LoadMenu(hInst, "PieceMenu"), -1);
\r
4558 MenuPopup(hwnd, pt, LoadMenu(hInst, "ShogiPieceMenu"), -1);
\r
4562 SetCapture(hwndMain);
\r
4565 hmenu = LoadMenu(hInst, "DropPieceMenu");
\r
4566 SetupDropMenu(hmenu);
\r
4567 MenuPopup(hwnd, pt, hmenu, -1);
\r
4577 /* Preprocess messages for buttons in main window */
\r
4579 ButtonProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
\r
4581 int id = GetWindowLongPtr(hwnd, GWLP_ID);
\r
4584 for (i=0; i<N_BUTTONS; i++) {
\r
4585 if (buttonDesc[i].id == id) break;
\r
4587 if (i == N_BUTTONS) return 0;
\r
4588 switch (message) {
\r
4593 dir = (wParam == VK_LEFT) ? -1 : 1;
\r
4594 SetFocus(buttonDesc[(i + dir + N_BUTTONS) % N_BUTTONS].hwnd);
\r
4601 SendMessage(hwndMain, WM_COMMAND, MAKEWPARAM(buttonDesc[i].id, 0), 0);
\r
4604 if (appData.icsActive && (isalpha((char)wParam) || wParam == '0')) {
\r
4605 // [HGM] movenum: only letters or leading zero should go to ICS input
\r
4606 HWND h = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
4607 if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);
\r
4609 SendMessage(h, WM_CHAR, wParam, lParam);
\r
4611 } else if (isalpha((char)wParam) || isdigit((char)wParam)){
\r
4612 TypeInEvent((char)wParam);
\r
4618 return CallWindowProc(buttonDesc[i].wndproc, hwnd, message, wParam, lParam);
\r
4621 static int promoStyle;
\r
4623 /* Process messages for Promotion dialog box */
\r
4625 Promotion(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
4630 switch (message) {
\r
4632 case WM_INITDIALOG: /* message: initialize dialog box */
\r
4633 /* Center the dialog over the application window */
\r
4634 CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));
\r
4635 Translate(hDlg, DLG_PromotionKing);
\r
4636 ShowWindow(GetDlgItem(hDlg, PB_King),
\r
4637 (!appData.testLegality || gameInfo.variant == VariantSuicide ||
\r
4638 gameInfo.variant == VariantSpartan && !WhiteOnMove(currentMove) ||
\r
4639 gameInfo.variant == VariantGiveaway || gameInfo.variant == VariantSuper ) ?
\r
4640 SW_SHOW : SW_HIDE);
\r
4641 /* [HGM] Only allow C & A promotions if these pieces are defined */
\r
4642 ShowWindow(GetDlgItem(hDlg, PB_Archbishop),
\r
4643 ((PieceToChar(WhiteAngel) >= 'A' && WhiteOnMove(currentMove) &&
\r
4644 PieceToChar(WhiteAngel) != '~') ||
\r
4645 (PieceToChar(BlackAngel) >= 'A' && !WhiteOnMove(currentMove) &&
\r
4646 PieceToChar(BlackAngel) != '~') ) ?
\r
4647 SW_SHOW : SW_HIDE);
\r
4648 ShowWindow(GetDlgItem(hDlg, PB_Chancellor),
\r
4649 ((PieceToChar(WhiteMarshall) >= 'A' && WhiteOnMove(currentMove) &&
\r
4650 PieceToChar(WhiteMarshall) != '~') ||
\r
4651 (PieceToChar(BlackMarshall) >= 'A' && !WhiteOnMove(currentMove) &&
\r
4652 PieceToChar(BlackMarshall) != '~') ) ?
\r
4653 SW_SHOW : SW_HIDE);
\r
4654 /* [HGM] Hide B & R button in Shogi, use Q as promote, N as defer */
\r
4655 ShowWindow(GetDlgItem(hDlg, PB_Rook), !promoStyle ? SW_SHOW : SW_HIDE);
\r
4656 ShowWindow(GetDlgItem(hDlg, PB_Bishop), !promoStyle ? SW_SHOW : SW_HIDE);
\r
4658 SetDlgItemText(hDlg, PB_Queen, "YES");
\r
4659 SetDlgItemText(hDlg, PB_Knight, "NO");
\r
4660 SetWindowText(hDlg, "Promote?");
\r
4662 ShowWindow(GetDlgItem(hDlg, IDC_Centaur),
\r
4663 gameInfo.variant == VariantSuper ?
\r
4664 SW_SHOW : SW_HIDE);
\r
4667 case WM_COMMAND: /* message: received a command */
\r
4668 switch (LOWORD(wParam)) {
\r
4670 EndDialog(hDlg, TRUE); /* Exit the dialog */
\r
4671 ClearHighlights();
\r
4672 DrawPosition(FALSE, NULL);
\r
4675 promoChar = gameInfo.variant == VariantSuper ? PieceToChar(BlackSilver) : PieceToChar(BlackKing);
\r
4678 promoChar = promoStyle ? '+' : ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteQueen : BlackQueen));
\r
4681 promoChar = ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteRook : BlackRook));
\r
4682 if(gameInfo.variant == VariantSpartan && !WhiteOnMove(currentMove)) promoChar = PieceToChar(BlackDragon);
\r
4685 promoChar = ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteBishop : BlackBishop));
\r
4686 if(gameInfo.variant == VariantSpartan && !WhiteOnMove(currentMove)) promoChar = PieceToChar(BlackAlfil);
\r
4688 case PB_Chancellor:
\r
4689 promoChar = ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteMarshall : BlackMarshall));
\r
4691 case PB_Archbishop:
\r
4692 promoChar = ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteAngel : BlackAngel));
\r
4695 promoChar = gameInfo.variant == VariantShogi ? '=' : promoStyle ? NULLCHAR :
\r
4696 ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteKnight : BlackKnight));
\r
4701 if(promoChar == '.') return FALSE; // invalid piece chosen
\r
4702 EndDialog(hDlg, TRUE); /* Exit the dialog */
\r
4703 UserMoveEvent(fromX, fromY, toX, toY, promoChar);
\r
4704 fromX = fromY = -1;
\r
4705 if (!appData.highlightLastMove) {
\r
4706 ClearHighlights();
\r
4707 DrawPosition(FALSE, NULL);
\r
4714 /* Pop up promotion dialog */
\r
4716 PromotionPopup(HWND hwnd)
\r
4720 lpProc = MakeProcInstance((FARPROC)Promotion, hInst);
\r
4721 DialogBox(hInst, MAKEINTRESOURCE(DLG_PromotionKing),
\r
4722 hwnd, (DLGPROC)lpProc);
\r
4723 FreeProcInstance(lpProc);
\r
4727 PromotionPopUp(char choice)
\r
4729 promoStyle = (choice == '+' || IS_SHOGI(gameInfo.variant));
\r
4730 DrawPosition(TRUE, NULL);
\r
4731 PromotionPopup(hwndMain);
\r
4735 LoadGameDialog(HWND hwnd, char* title)
\r
4739 char fileTitle[MSG_SIZ];
\r
4740 f = OpenFileDialog(hwnd, "rb", "",
\r
4741 appData.oldSaveStyle ? "gam" : "pgn",
\r
4743 title, &number, fileTitle, NULL);
\r
4745 cmailMsgLoaded = FALSE;
\r
4746 if (number == 0) {
\r
4747 int error = GameListBuild(f);
\r
4749 DisplayError(_("Cannot build game list"), error);
\r
4750 } else if (!ListEmpty(&gameList) &&
\r
4751 ((ListGame *) gameList.tailPred)->number > 1) {
\r
4752 GameListPopUp(f, fileTitle);
\r
4755 GameListDestroy();
\r
4758 LoadGame(f, number, fileTitle, FALSE);
\r
4762 int get_term_width()
\r
4767 HFONT hfont, hold_font;
\r
4772 hText = GetDlgItem(hwndConsole, OPT_ConsoleText);
\r
4776 // get the text metrics
\r
4777 hdc = GetDC(hText);
\r
4778 lf = font[boardSize][CONSOLE_FONT]->lf;
\r
4779 if (consoleCF.dwEffects & CFE_BOLD)
\r
4780 lf.lfWeight = FW_BOLD;
\r
4781 if (consoleCF.dwEffects & CFE_ITALIC)
\r
4782 lf.lfItalic = TRUE;
\r
4783 if (consoleCF.dwEffects & CFE_STRIKEOUT)
\r
4784 lf.lfStrikeOut = TRUE;
\r
4785 if (consoleCF.dwEffects & CFE_UNDERLINE)
\r
4786 lf.lfUnderline = TRUE;
\r
4787 hfont = CreateFontIndirect(&lf);
\r
4788 hold_font = SelectObject(hdc, hfont);
\r
4789 GetTextMetrics(hdc, &tm);
\r
4790 SelectObject(hdc, hold_font);
\r
4791 DeleteObject(hfont);
\r
4792 ReleaseDC(hText, hdc);
\r
4794 // get the rectangle
\r
4795 SendMessage(hText, EM_GETRECT, 0, (LPARAM)&rc);
\r
4797 return (rc.right-rc.left) / tm.tmAveCharWidth;
\r
4800 void UpdateICSWidth(HWND hText)
\r
4802 LONG old_width, new_width;
\r
4804 new_width = get_term_width(hText, FALSE);
\r
4805 old_width = GetWindowLongPtr(hText, GWLP_USERDATA);
\r
4806 if (new_width != old_width)
\r
4808 ics_update_width(new_width);
\r
4809 SetWindowLongPtr(hText, GWLP_USERDATA, new_width);
\r
4814 ChangedConsoleFont()
\r
4817 CHARRANGE tmpsel, sel;
\r
4818 MyFont *f = font[boardSize][CONSOLE_FONT];
\r
4819 HWND hText = GetDlgItem(hwndConsole, OPT_ConsoleText);
\r
4820 HWND hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
4823 cfmt.cbSize = sizeof(CHARFORMAT);
\r
4824 cfmt.dwMask = CFM_FACE|CFM_SIZE|CFM_CHARSET;
\r
4825 safeStrCpy(cfmt.szFaceName, font[boardSize][CONSOLE_FONT]->mfp.faceName,
\r
4826 sizeof(cfmt.szFaceName)/sizeof(cfmt.szFaceName[0]) );
\r
4827 /* yHeight is expressed in twips. A twip is 1/20 of a font's point
\r
4828 * size. This was undocumented in the version of MSVC++ that I had
\r
4829 * when I wrote the code, but is apparently documented now.
\r
4831 cfmt.yHeight = (int)(f->mfp.pointSize * 20.0 + 0.5);
\r
4832 cfmt.bCharSet = f->lf.lfCharSet;
\r
4833 cfmt.bPitchAndFamily = f->lf.lfPitchAndFamily;
\r
4834 SendMessage(hText, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &cfmt);
\r
4835 SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &cfmt);
\r
4836 /* Why are the following seemingly needed too? */
\r
4837 SendMessage(hText, EM_SETCHARFORMAT, SCF_DEFAULT, (LPARAM) &cfmt);
\r
4838 SendMessage(hInput, EM_SETCHARFORMAT, SCF_DEFAULT, (LPARAM) &cfmt);
\r
4839 SendMessage(hText, EM_EXGETSEL, 0, (LPARAM)&sel);
\r
4841 tmpsel.cpMax = -1; /*999999?*/
\r
4842 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&tmpsel);
\r
4843 SendMessage(hText, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cfmt);
\r
4844 /* Trying putting this here too. It still seems to tickle a RichEdit
\r
4845 * bug: sometimes RichEdit indents the first line of a paragraph too.
\r
4847 paraf.cbSize = sizeof(paraf);
\r
4848 paraf.dwMask = PFM_OFFSET | PFM_STARTINDENT;
\r
4849 paraf.dxStartIndent = 0;
\r
4850 paraf.dxOffset = WRAP_INDENT;
\r
4851 SendMessage(hText, EM_SETPARAFORMAT, 0, (LPARAM) ¶f);
\r
4852 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
4853 UpdateICSWidth(hText);
\r
4856 /*---------------------------------------------------------------------------*\
\r
4858 * Window Proc for main window
\r
4860 \*---------------------------------------------------------------------------*/
\r
4862 /* Process messages for main window, etc. */
\r
4864 WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
\r
4871 char fileTitle[MSG_SIZ];
\r
4872 static SnapData sd;
\r
4873 static int peek=0;
\r
4875 switch (message) {
\r
4877 case WM_PAINT: /* message: repaint portion of window */
\r
4881 case WM_ERASEBKGND:
\r
4882 if (IsIconic(hwnd)) {
\r
4883 /* Cheat; change the message */
\r
4884 return (DefWindowProc(hwnd, WM_ICONERASEBKGND, wParam, lParam));
\r
4886 return (DefWindowProc(hwnd, message, wParam, lParam));
\r
4890 case WM_LBUTTONDOWN:
\r
4891 case WM_MBUTTONDOWN:
\r
4892 case WM_RBUTTONDOWN:
\r
4893 case WM_LBUTTONUP:
\r
4894 case WM_MBUTTONUP:
\r
4895 case WM_RBUTTONUP:
\r
4896 case WM_MOUSEMOVE:
\r
4897 case WM_MOUSEWHEEL:
\r
4898 MouseEvent(hwnd, message, wParam, lParam);
\r
4902 if((char)wParam == '\b') {
\r
4903 ForwardEvent(); peek = 0;
\r
4906 JAWS_KBUP_NAVIGATION
\r
4911 if((char)wParam == '\b') {
\r
4912 if(!peek) BackwardEvent(), peek = 1;
\r
4915 JAWS_KBDOWN_NAVIGATION
\r
4921 JAWS_ALT_INTERCEPT
\r
4923 if (appData.icsActive && ((char)wParam == '\r' || (char)wParam > ' ' && !((char)wParam >= '1' && (char)wParam <= '9'))) {
\r
4924 // [HGM] movenum: for non-zero digits we always do type-in dialog
\r
4925 HWND h = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
4926 if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);
\r
4928 SendMessage(h, message, wParam, lParam);
\r
4929 } else if(lParam != KF_REPEAT) {
\r
4930 if (isalpha((char)wParam) || isdigit((char)wParam)) {
\r
4931 TypeInEvent((char)wParam);
\r
4932 } else if((char)wParam == 003) CopyGameToClipboard();
\r
4933 else if((char)wParam == 026) PasteGameOrFENFromClipboard();
\r
4938 case WM_PALETTECHANGED:
\r
4939 if (hwnd != (HWND)wParam && !appData.monoMode) {
\r
4941 HDC hdc = GetDC(hwndMain);
\r
4942 SelectPalette(hdc, hPal, TRUE);
\r
4943 nnew = RealizePalette(hdc);
\r
4945 paletteChanged = TRUE;
\r
4947 InvalidateRect(hwnd, &boardRect, FALSE);
\r
4949 ReleaseDC(hwnd, hdc);
\r
4953 case WM_QUERYNEWPALETTE:
\r
4954 if (!appData.monoMode /*&& paletteChanged*/) {
\r
4956 HDC hdc = GetDC(hwndMain);
\r
4957 paletteChanged = FALSE;
\r
4958 SelectPalette(hdc, hPal, FALSE);
\r
4959 nnew = RealizePalette(hdc);
\r
4961 InvalidateRect(hwnd, &boardRect, FALSE);
\r
4963 ReleaseDC(hwnd, hdc);
\r
4968 case WM_COMMAND: /* message: command from application menu */
\r
4969 wmId = LOWORD(wParam);
\r
4974 SAY("new game enter a move to play against the computer with white");
\r
4977 case IDM_NewGameFRC:
\r
4978 if( NewGameFRC() == 0 ) {
\r
4983 case IDM_NewVariant:
\r
4984 NewVariantPopup(hwnd);
\r
4987 case IDM_LoadGame:
\r
4988 LoadGameDialog(hwnd, _("Load Game from File"));
\r
4991 case IDM_LoadNextGame:
\r
4995 case IDM_LoadPrevGame:
\r
4999 case IDM_ReloadGame:
\r
5003 case IDM_LoadPosition:
\r
5004 if (gameMode == AnalyzeMode || gameMode == AnalyzeFile) {
\r
5005 Reset(FALSE, TRUE);
\r
5008 f = OpenFileDialog(hwnd, "rb", "",
\r
5009 appData.oldSaveStyle ? "pos" : "fen",
\r
5011 _("Load Position from File"), &number, fileTitle, NULL);
\r
5013 LoadPosition(f, number, fileTitle);
\r
5017 case IDM_LoadNextPosition:
\r
5018 ReloadPosition(1);
\r
5021 case IDM_LoadPrevPosition:
\r
5022 ReloadPosition(-1);
\r
5025 case IDM_ReloadPosition:
\r
5026 ReloadPosition(0);
\r
5029 case IDM_SaveGame:
\r
5030 defName = DefaultFileName(appData.oldSaveStyle ? "gam" : "pgn");
\r
5031 f = OpenFileDialog(hwnd, "a", defName,
\r
5032 appData.oldSaveStyle ? "gam" : "pgn",
\r
5034 _("Save Game to File"), NULL, fileTitle, NULL);
\r
5036 SaveGame(f, 0, "");
\r
5040 case IDM_SavePosition:
\r
5041 defName = DefaultFileName(appData.oldSaveStyle ? "pos" : "fen");
\r
5042 f = OpenFileDialog(hwnd, "a", defName,
\r
5043 appData.oldSaveStyle ? "pos" : "fen",
\r
5045 _("Save Position to File"), NULL, fileTitle, NULL);
\r
5047 SavePosition(f, 0, "");
\r
5051 case IDM_SaveDiagram:
\r
5052 defName = "diagram";
\r
5053 f = OpenFileDialog(hwnd, "wb", defName,
\r
5056 _("Save Diagram to File"), NULL, fileTitle, NULL);
\r
5062 case IDM_SaveSelected:
\r
5063 f = OpenFileDialog(hwnd, "a", "",
\r
5066 _("Save Game to File"), NULL, fileTitle, NULL);
\r
5068 SaveSelected(f, 0, "");
\r
5072 case IDM_CreateBook:
\r
5073 CreateBookEvent();
\r
5076 case IDM_CopyGame:
\r
5077 CopyGameToClipboard();
\r
5080 case IDM_PasteGame:
\r
5081 PasteGameFromClipboard();
\r
5084 case IDM_CopyGameListToClipboard:
\r
5085 CopyGameListToClipboard();
\r
5088 /* [AS] Autodetect FEN or PGN data */
\r
5089 case IDM_PasteAny:
\r
5090 PasteGameOrFENFromClipboard();
\r
5093 /* [AS] Move history */
\r
5094 case IDM_ShowMoveHistory:
\r
5095 if( MoveHistoryIsUp() ) {
\r
5096 MoveHistoryPopDown();
\r
5099 MoveHistoryPopUp();
\r
5103 /* [AS] Eval graph */
\r
5104 case IDM_ShowEvalGraph:
\r
5105 if( EvalGraphIsUp() ) {
\r
5106 EvalGraphPopDown();
\r
5110 SetFocus(hwndMain);
\r
5114 /* [AS] Engine output */
\r
5115 case IDM_ShowEngineOutput:
\r
5116 if( EngineOutputIsUp() ) {
\r
5117 EngineOutputPopDown();
\r
5120 EngineOutputPopUp();
\r
5124 /* [AS] User adjudication */
\r
5125 case IDM_UserAdjudication_White:
\r
5126 UserAdjudicationEvent( +1 );
\r
5129 case IDM_UserAdjudication_Black:
\r
5130 UserAdjudicationEvent( -1 );
\r
5133 case IDM_UserAdjudication_Draw:
\r
5134 UserAdjudicationEvent( 0 );
\r
5137 /* [AS] Game list options dialog */
\r
5138 case IDM_GameListOptions:
\r
5139 GameListOptions();
\r
5146 case IDM_CopyPosition:
\r
5147 CopyFENToClipboard();
\r
5150 case IDM_PastePosition:
\r
5151 PasteFENFromClipboard();
\r
5154 case IDM_MailMove:
\r
5158 case IDM_ReloadCMailMsg:
\r
5159 Reset(TRUE, TRUE);
\r
5160 ReloadCmailMsgEvent(FALSE);
\r
5163 case IDM_Minimize:
\r
5164 ShowWindow(hwnd, SW_MINIMIZE);
\r
5171 case IDM_MachineWhite:
\r
5172 MachineWhiteEvent();
\r
5174 * refresh the tags dialog only if it's visible
\r
5176 if (gameMode == MachinePlaysWhite && IsWindowVisible(editTagsDialog)) {
\r
5178 tags = PGNTags(&gameInfo);
\r
5179 TagsPopUp(tags, CmailMsg());
\r
5182 SAY("computer starts playing white");
\r
5185 case IDM_MachineBlack:
\r
5186 MachineBlackEvent();
\r
5188 * refresh the tags dialog only if it's visible
\r
5190 if (gameMode == MachinePlaysBlack && IsWindowVisible(editTagsDialog)) {
\r
5192 tags = PGNTags(&gameInfo);
\r
5193 TagsPopUp(tags, CmailMsg());
\r
5196 SAY("computer starts playing black");
\r
5199 case IDM_Match: // [HGM] match: flows into next case, after setting Match Mode and nr of Games
\r
5200 if(matchMode) EnableMenuItem(GetMenu(hwndMain), IDM_Match, MF_BYCOMMAND|MF_GRAYED);
\r
5201 MatchEvent(2); // distinguish from command-line-triggered case (matchMode=1)
\r
5204 case IDM_TwoMachines:
\r
5205 TwoMachinesEvent();
\r
5208 * refresh the tags dialog only if it's visible
\r
5210 if (gameMode == TwoMachinesPlay && IsWindowVisible(editTagsDialog)) {
\r
5212 tags = PGNTags(&gameInfo);
\r
5213 TagsPopUp(tags, CmailMsg());
\r
5216 SAY("computer starts playing both sides");
\r
5219 case IDM_AnalysisMode:
\r
5220 if(AnalyzeModeEvent()) {
\r
5221 SAY("analyzing current position");
\r
5225 case IDM_AnalyzeFile:
\r
5226 AnalyzeFileEvent();
\r
5229 case IDM_IcsClient:
\r
5233 case IDM_EditGame:
\r
5234 case IDM_EditGame2:
\r
5239 case IDM_EditPosition:
\r
5240 case IDM_EditPosition2:
\r
5241 EditPositionEvent();
\r
5242 SAY("enter a FEN string or setup a position on the board using the control R pop up menu");
\r
5245 case IDM_Training:
\r
5249 case IDM_ShowGameList:
\r
5250 ShowGameListProc();
\r
5253 case IDM_EditProgs1:
\r
5254 EditTagsPopUp(firstChessProgramNames, &firstChessProgramNames);
\r
5257 case IDM_LoadProg1:
\r
5258 LoadEnginePopUp(hwndMain, 0);
\r
5261 case IDM_LoadProg2:
\r
5262 LoadEnginePopUp(hwndMain, 1);
\r
5265 case IDM_EditServers:
\r
5266 EditTagsPopUp(icsNames, &icsNames);
\r
5269 case IDM_EditTags:
\r
5274 case IDM_EditBook:
\r
5278 case IDM_EditComment:
\r
5280 if (commentUp && editComment) {
\r
5283 EditCommentEvent();
\r
5304 case IDM_CallFlag:
\r
5324 case IDM_StopObserving:
\r
5325 StopObservingEvent();
\r
5328 case IDM_StopExamining:
\r
5329 StopExaminingEvent();
\r
5333 UploadGameEvent();
\r
5336 case IDM_TypeInMove:
\r
5337 TypeInEvent('\000');
\r
5340 case IDM_TypeInName:
\r
5341 PopUpNameDialog('\000');
\r
5344 case IDM_Backward:
\r
5346 SetFocus(hwndMain);
\r
5353 SetFocus(hwndMain);
\r
5359 SetFocus(hwndMain);
\r
5364 SetFocus(hwndMain);
\r
5367 case OPT_GameListNext: // [HGM] forward these two accelerators to Game List
\r
5368 case OPT_GameListPrev:
\r
5369 if(gameListDialog) SendMessage(gameListDialog, WM_COMMAND, wmId, 0);
\r
5373 RevertEvent(FALSE);
\r
5376 case IDM_Annotate: // [HGM] vari: revert with annotation
\r
5377 RevertEvent(TRUE);
\r
5380 case IDM_TruncateGame:
\r
5381 TruncateGameEvent();
\r
5388 case IDM_RetractMove:
\r
5389 RetractMoveEvent();
\r
5392 case IDM_FlipView:
\r
5393 flipView = !flipView;
\r
5394 DrawPosition(FALSE, NULL);
\r
5397 case IDM_FlipClock:
\r
5398 flipClock = !flipClock;
\r
5399 DisplayBothClocks();
\r
5403 case IDM_MuteSounds:
\r
5404 mute = !mute; // [HGM] mute: keep track of global muting variable
\r
5405 CheckMenuItem(GetMenu(hwndMain),IDM_MuteSounds,
\r
5406 MF_BYCOMMAND|(mute?MF_CHECKED:MF_UNCHECKED));
\r
5409 case IDM_GeneralOptions:
\r
5410 GeneralOptionsPopup(hwnd);
\r
5411 DrawPosition(TRUE, NULL);
\r
5414 case IDM_BoardOptions:
\r
5415 BoardOptionsPopup(hwnd);
\r
5418 case IDM_ThemeOptions:
\r
5419 ThemeOptionsPopup(hwnd);
\r
5422 case IDM_EnginePlayOptions:
\r
5423 EnginePlayOptionsPopup(hwnd);
\r
5426 case IDM_Engine1Options:
\r
5427 EngineOptionsPopup(hwnd, &first);
\r
5430 case IDM_Engine2Options:
\r
5432 if(WaitForEngine(&second, SettingsMenuIfReady)) break;
\r
5433 EngineOptionsPopup(hwnd, &second);
\r
5436 case IDM_OptionsUCI:
\r
5437 UciOptionsPopup(hwnd);
\r
5441 TourneyPopup(hwnd);
\r
5444 case IDM_IcsOptions:
\r
5445 IcsOptionsPopup(hwnd);
\r
5449 FontsOptionsPopup(hwnd);
\r
5453 SoundOptionsPopup(hwnd);
\r
5456 case IDM_CommPort:
\r
5457 CommPortOptionsPopup(hwnd);
\r
5460 case IDM_LoadOptions:
\r
5461 LoadOptionsPopup(hwnd);
\r
5464 case IDM_SaveOptions:
\r
5465 SaveOptionsPopup(hwnd);
\r
5468 case IDM_TimeControl:
\r
5469 TimeControlOptionsPopup(hwnd);
\r
5472 case IDM_SaveSettings:
\r
5473 SaveSettings(settingsFileName);
\r
5476 case IDM_SaveSettingsOnExit:
\r
5477 saveSettingsOnExit = !saveSettingsOnExit;
\r
5478 (void) CheckMenuItem(GetMenu(hwndMain), IDM_SaveSettingsOnExit,
\r
5479 MF_BYCOMMAND|(saveSettingsOnExit ?
\r
5480 MF_CHECKED : MF_UNCHECKED));
\r
5491 case IDM_AboutGame:
\r
5496 appData.debugMode = !appData.debugMode;
\r
5497 if (appData.debugMode) {
\r
5498 char dir[MSG_SIZ];
\r
5499 GetCurrentDirectory(MSG_SIZ, dir);
\r
5500 SetCurrentDirectory(installDir);
\r
5501 debugFP = fopen(appData.nameOfDebugFile, "w");
\r
5502 SetCurrentDirectory(dir);
\r
5503 setbuf(debugFP, NULL);
\r
5510 case IDM_HELPCONTENTS:
\r
5511 if (!MyHelp (hwnd, "winboard.hlp", HELP_KEY,(DWORD)(LPSTR)"CONTENTS") &&
\r
5512 !HtmlHelp(hwnd, "winboard.chm", 0, 0) ) {
\r
5513 MessageBox (GetFocus(),
\r
5514 _("Unable to activate help"),
\r
5515 szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);
\r
5519 case IDM_HELPSEARCH:
\r
5520 if (!MyHelp (hwnd, "winboard.hlp", HELP_PARTIALKEY, (DWORD)(LPSTR)"") &&
\r
5521 !HtmlHelp(hwnd, "winboard.chm", 0, 0) ) {
\r
5522 MessageBox (GetFocus(),
\r
5523 _("Unable to activate help"),
\r
5524 szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);
\r
5528 case IDM_HELPHELP:
\r
5529 if(!WinHelp(hwnd, (LPSTR)NULL, HELP_HELPONHELP, 0)) {
\r
5530 MessageBox (GetFocus(),
\r
5531 _("Unable to activate help"),
\r
5532 szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);
\r
5537 lpProc = MakeProcInstance((FARPROC)About, hInst);
\r
5539 (gameInfo.event && strcmp(gameInfo.event, "Easter Egg Hunt") == 0) ?
\r
5540 "AboutBox2" : "AboutBox", hwnd, (DLGPROC)lpProc);
\r
5541 FreeProcInstance(lpProc);
\r
5544 case IDM_DirectCommand1:
\r
5545 AskQuestionEvent(_("Direct Command"),
\r
5546 _("Send to chess program:"), "", "1");
\r
5548 case IDM_DirectCommand2:
\r
5549 AskQuestionEvent(_("Direct Command"),
\r
5550 _("Send to second chess program:"), "", "2");
\r
5553 case EP_WhitePawn:
\r
5554 EditPositionMenuEvent(WhitePawn, fromX, fromY);
\r
5555 fromX = fromY = -1;
\r
5558 case EP_WhiteKnight:
\r
5559 EditPositionMenuEvent(WhiteKnight, fromX, fromY);
\r
5560 fromX = fromY = -1;
\r
5563 case EP_WhiteBishop:
\r
5564 EditPositionMenuEvent(WhiteBishop, fromX, fromY);
\r
5565 fromX = fromY = -1;
\r
5568 case EP_WhiteRook:
\r
5569 EditPositionMenuEvent(WhiteRook, fromX, fromY);
\r
5570 fromX = fromY = -1;
\r
5573 case EP_WhiteQueen:
\r
5574 EditPositionMenuEvent(WhiteQueen, fromX, fromY);
\r
5575 fromX = fromY = -1;
\r
5578 case EP_WhiteFerz:
\r
5579 EditPositionMenuEvent(WhiteFerz, fromX, fromY);
\r
5580 fromX = fromY = -1;
\r
5583 case EP_WhiteWazir:
\r
5584 EditPositionMenuEvent(WhiteWazir, fromX, fromY);
\r
5585 fromX = fromY = -1;
\r
5588 case EP_WhiteAlfil:
\r
5589 EditPositionMenuEvent(WhiteAlfil, fromX, fromY);
\r
5590 fromX = fromY = -1;
\r
5593 case EP_WhiteCannon:
\r
5594 EditPositionMenuEvent(WhiteCannon, fromX, fromY);
\r
5595 fromX = fromY = -1;
\r
5598 case EP_WhiteCardinal:
\r
5599 EditPositionMenuEvent(WhiteAngel, fromX, fromY);
\r
5600 fromX = fromY = -1;
\r
5603 case EP_WhiteMarshall:
\r
5604 EditPositionMenuEvent(WhiteMarshall, fromX, fromY);
\r
5605 fromX = fromY = -1;
\r
5608 case EP_WhiteKing:
\r
5609 EditPositionMenuEvent(WhiteKing, fromX, fromY);
\r
5610 fromX = fromY = -1;
\r
5613 case EP_BlackPawn:
\r
5614 EditPositionMenuEvent(BlackPawn, fromX, fromY);
\r
5615 fromX = fromY = -1;
\r
5618 case EP_BlackKnight:
\r
5619 EditPositionMenuEvent(BlackKnight, fromX, fromY);
\r
5620 fromX = fromY = -1;
\r
5623 case EP_BlackBishop:
\r
5624 EditPositionMenuEvent(BlackBishop, fromX, fromY);
\r
5625 fromX = fromY = -1;
\r
5628 case EP_BlackRook:
\r
5629 EditPositionMenuEvent(BlackRook, fromX, fromY);
\r
5630 fromX = fromY = -1;
\r
5633 case EP_BlackQueen:
\r
5634 EditPositionMenuEvent(BlackQueen, fromX, fromY);
\r
5635 fromX = fromY = -1;
\r
5638 case EP_BlackFerz:
\r
5639 EditPositionMenuEvent(BlackFerz, fromX, fromY);
\r
5640 fromX = fromY = -1;
\r
5643 case EP_BlackWazir:
\r
5644 EditPositionMenuEvent(BlackWazir, fromX, fromY);
\r
5645 fromX = fromY = -1;
\r
5648 case EP_BlackAlfil:
\r
5649 EditPositionMenuEvent(BlackAlfil, fromX, fromY);
\r
5650 fromX = fromY = -1;
\r
5653 case EP_BlackCannon:
\r
5654 EditPositionMenuEvent(BlackCannon, fromX, fromY);
\r
5655 fromX = fromY = -1;
\r
5658 case EP_BlackCardinal:
\r
5659 EditPositionMenuEvent(BlackAngel, fromX, fromY);
\r
5660 fromX = fromY = -1;
\r
5663 case EP_BlackMarshall:
\r
5664 EditPositionMenuEvent(BlackMarshall, fromX, fromY);
\r
5665 fromX = fromY = -1;
\r
5668 case EP_BlackKing:
\r
5669 EditPositionMenuEvent(BlackKing, fromX, fromY);
\r
5670 fromX = fromY = -1;
\r
5673 case EP_EmptySquare:
\r
5674 EditPositionMenuEvent(EmptySquare, fromX, fromY);
\r
5675 fromX = fromY = -1;
\r
5678 case EP_ClearBoard:
\r
5679 EditPositionMenuEvent(ClearBoard, fromX, fromY);
\r
5680 fromX = fromY = -1;
\r
5684 EditPositionMenuEvent(WhitePlay, fromX, fromY);
\r
5685 fromX = fromY = -1;
\r
5689 EditPositionMenuEvent(BlackPlay, fromX, fromY);
\r
5690 fromX = fromY = -1;
\r
5694 EditPositionMenuEvent(PromotePiece, fromX, fromY);
\r
5695 fromX = fromY = -1;
\r
5699 EditPositionMenuEvent(DemotePiece, fromX, fromY);
\r
5700 fromX = fromY = -1;
\r
5704 DropMenuEvent(WhitePawn, fromX, fromY);
\r
5705 fromX = fromY = -1;
\r
5709 DropMenuEvent(WhiteKnight, fromX, fromY);
\r
5710 fromX = fromY = -1;
\r
5714 DropMenuEvent(WhiteBishop, fromX, fromY);
\r
5715 fromX = fromY = -1;
\r
5719 DropMenuEvent(WhiteRook, fromX, fromY);
\r
5720 fromX = fromY = -1;
\r
5724 DropMenuEvent(WhiteQueen, fromX, fromY);
\r
5725 fromX = fromY = -1;
\r
5729 barbaric = 0; appData.language = "";
\r
5730 TranslateMenus(0);
\r
5731 CheckMenuItem(GetMenu(hwndMain), lastChecked, MF_BYCOMMAND|MF_UNCHECKED);
\r
5732 CheckMenuItem(GetMenu(hwndMain), IDM_English, MF_BYCOMMAND|MF_CHECKED);
\r
5733 lastChecked = wmId;
\r
5737 if(wmId >= IDM_RecentEngines && wmId < IDM_RecentEngines + appData.recentEngines)
\r
5738 RecentEngineEvent(wmId - IDM_RecentEngines);
\r
5740 if(wmId > IDM_English && wmId < IDM_English+20) {
\r
5741 LoadLanguageFile(languageFile[wmId - IDM_English - 1]);
\r
5742 TranslateMenus(0);
\r
5743 CheckMenuItem(GetMenu(hwndMain), lastChecked, MF_BYCOMMAND|MF_UNCHECKED);
\r
5744 CheckMenuItem(GetMenu(hwndMain), wmId, MF_BYCOMMAND|MF_CHECKED);
\r
5745 lastChecked = wmId;
\r
5748 return (DefWindowProc(hwnd, message, wParam, lParam));
\r
5754 case CLOCK_TIMER_ID:
\r
5755 KillTimer(hwnd, clockTimerEvent); /* Simulate one-shot timer as in X */
\r
5756 clockTimerEvent = 0;
\r
5757 DecrementClocks(); /* call into back end */
\r
5759 case LOAD_GAME_TIMER_ID:
\r
5760 KillTimer(hwnd, loadGameTimerEvent); /* Simulate one-shot timer as in X*/
\r
5761 loadGameTimerEvent = 0;
\r
5762 AutoPlayGameLoop(); /* call into back end */
\r
5764 case ANALYSIS_TIMER_ID:
\r
5765 if ((gameMode == AnalyzeMode || gameMode == AnalyzeFile
\r
5766 || appData.icsEngineAnalyze) && appData.periodicUpdates) {
\r
5767 AnalysisPeriodicEvent(0);
\r
5769 KillTimer(hwnd, analysisTimerEvent);
\r
5770 analysisTimerEvent = 0;
\r
5773 case DELAYED_TIMER_ID:
\r
5774 KillTimer(hwnd, delayedTimerEvent);
\r
5775 delayedTimerEvent = 0;
\r
5776 delayedTimerCallback();
\r
5781 case WM_USER_Input:
\r
5782 InputEvent(hwnd, message, wParam, lParam);
\r
5785 /* [AS] Also move "attached" child windows */
\r
5786 case WM_WINDOWPOSCHANGING:
\r
5788 if( hwnd == hwndMain && appData.useStickyWindows ) {
\r
5789 LPWINDOWPOS lpwp = (LPWINDOWPOS) lParam;
\r
5791 if( ((lpwp->flags & SWP_NOMOVE) == 0) /*&& ((lpwp->flags & SWP_NOSIZE) != 0)*/ ) { // [HGM] in Win8 size always accompanies move?
\r
5792 /* Window is moving */
\r
5795 // GetWindowRect( hwnd, &rcMain ); //[HGM] sticky: in XP this returned new position, not old
\r
5796 rcMain.left = wpMain.x; // replace by these 4 lines to reconstruct old rect
\r
5797 rcMain.right = wpMain.x + wpMain.width;
\r
5798 rcMain.top = wpMain.y;
\r
5799 rcMain.bottom = wpMain.y + wpMain.height;
\r
5801 ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, moveHistoryDialog, &wpMoveHistory );
\r
5802 ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, evalGraphDialog, &wpEvalGraph );
\r
5803 ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, engineOutputDialog, &wpEngineOutput );
\r
5804 ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, gameListDialog, &wpGameList );
\r
5805 ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, hwndConsole, &wpConsole );
\r
5806 wpMain.x = lpwp->x;
\r
5807 wpMain.y = lpwp->y;
\r
5813 /* [AS] Snapping */
\r
5814 case WM_ENTERSIZEMOVE:
\r
5815 if(appData.debugMode) { fprintf(debugFP, "size-move\n"); }
\r
5816 if (hwnd == hwndMain) {
\r
5817 doingSizing = TRUE;
\r
5820 return OnEnterSizeMove( &sd, hwnd, wParam, lParam );
\r
5824 if(appData.debugMode) { fprintf(debugFP, "sizing\n"); }
\r
5825 if (hwnd == hwndMain) {
\r
5826 lastSizing = wParam;
\r
5831 if(appData.debugMode) { fprintf(debugFP, "moving\n"); }
\r
5832 return OnMoving( &sd, hwnd, wParam, lParam );
\r
5834 case WM_EXITSIZEMOVE:
\r
5835 if(appData.debugMode) { fprintf(debugFP, "exit size-move, size = %d\n", squareSize); }
\r
5836 if (hwnd == hwndMain) {
\r
5838 doingSizing = FALSE;
\r
5839 InvalidateRect(hwnd, &boardRect, FALSE);
\r
5840 GetClientRect(hwnd, &client);
\r
5841 ResizeBoard(client.right, client.bottom, lastSizing);
\r
5843 if(appData.debugMode) { fprintf(debugFP, "square size = %d\n", squareSize); }
\r
5845 return OnExitSizeMove( &sd, hwnd, wParam, lParam );
\r
5848 case WM_DESTROY: /* message: window being destroyed */
\r
5849 PostQuitMessage(0);
\r
5853 if (hwnd == hwndMain) {
\r
5858 default: /* Passes it on if unprocessed */
\r
5859 return (DefWindowProc(hwnd, message, wParam, lParam));
\r
5866 /*---------------------------------------------------------------------------*\
\r
5868 * Misc utility routines
\r
5870 \*---------------------------------------------------------------------------*/
\r
5873 * Decent random number generator, at least not as bad as Windows
\r
5874 * standard rand, which returns a value in the range 0 to 0x7fff.
\r
5876 unsigned int randstate;
\r
5881 randstate = randstate * 1664525 + 1013904223;
\r
5882 return (int) randstate & 0x7fffffff;
\r
5886 mysrandom(unsigned int seed)
\r
5893 * returns TRUE if user selects a different color, FALSE otherwise
\r
5897 ChangeColor(HWND hwnd, COLORREF *which)
\r
5899 static BOOL firstTime = TRUE;
\r
5900 static DWORD customColors[16];
\r
5902 COLORREF newcolor;
\r
5907 /* Make initial colors in use available as custom colors */
\r
5908 /* Should we put the compiled-in defaults here instead? */
\r
5910 customColors[i++] = lightSquareColor & 0xffffff;
\r
5911 customColors[i++] = darkSquareColor & 0xffffff;
\r
5912 customColors[i++] = whitePieceColor & 0xffffff;
\r
5913 customColors[i++] = blackPieceColor & 0xffffff;
\r
5914 customColors[i++] = highlightSquareColor & 0xffffff;
\r
5915 customColors[i++] = premoveHighlightColor & 0xffffff;
\r
5917 for (ccl = (ColorClass) 0; ccl < NColorClasses && i < 16; ccl++) {
\r
5918 customColors[i++] = textAttribs[ccl].color;
\r
5920 while (i < 16) customColors[i++] = RGB(255, 255, 255);
\r
5921 firstTime = FALSE;
\r
5924 cc.lStructSize = sizeof(cc);
\r
5925 cc.hwndOwner = hwnd;
\r
5926 cc.hInstance = NULL;
\r
5927 cc.rgbResult = (DWORD) (*which & 0xffffff);
\r
5928 cc.lpCustColors = (LPDWORD) customColors;
\r
5929 cc.Flags = CC_RGBINIT|CC_FULLOPEN;
\r
5931 if (!ChooseColor(&cc)) return FALSE;
\r
5933 newcolor = (COLORREF) (0x2000000 | cc.rgbResult);
\r
5934 if (newcolor == *which) return FALSE;
\r
5935 *which = newcolor;
\r
5939 InitDrawingColors();
\r
5940 InvalidateRect(hwnd, &boardRect, FALSE);
\r
5945 MyLoadSound(MySound *ms)
\r
5951 if (ms->data && ms->flag) free(ms->data);
\r
5954 switch (ms->name[0]) {
\r
5960 /* System sound from Control Panel. Don't preload here. */
\r
5964 if (ms->name[1] == NULLCHAR) {
\r
5965 /* "!" alone = silence */
\r
5968 /* Builtin wave resource. Error if not found. */
\r
5969 HANDLE h = FindResource(hInst, ms->name + 1, "WAVE");
\r
5970 if (h == NULL) break;
\r
5971 ms->data = (void *)LoadResource(hInst, h);
\r
5972 ms->flag = 0; // not maloced, so cannot be freed!
\r
5973 if (h == NULL) break;
\r
5978 /* .wav file. Error if not found. */
\r
5979 f = fopen(ms->name, "rb");
\r
5980 if (f == NULL) break;
\r
5981 if (fstat(fileno(f), &st) < 0) break;
\r
5982 ms->data = malloc(st.st_size);
\r
5984 if (fread(ms->data, st.st_size, 1, f) < 1) break;
\r
5990 char buf[MSG_SIZ];
\r
5991 snprintf(buf, MSG_SIZ, _("Error loading sound %s"), ms->name);
\r
5992 DisplayError(buf, GetLastError());
\r
5998 MyPlaySound(MySound *ms)
\r
6000 BOOLEAN ok = FALSE;
\r
6002 if(mute) return TRUE; // [HGM] mute: suppress all sound play when muted
\r
6003 switch (ms->name[0]) {
\r
6005 if(appData.debugMode) fprintf(debugFP, "silence\n");
\r
6010 /* System sound from Control Panel (deprecated feature).
\r
6011 "$" alone or an unset sound name gets default beep (still in use). */
\r
6012 if (ms->name[1]) {
\r
6013 ok = PlaySound(ms->name + 1, NULL, SND_ALIAS|SND_ASYNC);
\r
6015 if (!ok) ok = MessageBeep(MB_OK);
\r
6018 /* Builtin wave resource, or "!" alone for silence */
\r
6019 if (ms->name[1]) {
\r
6020 if (ms->data == NULL) return FALSE;
\r
6021 ok = PlaySound(ms->data, NULL, SND_MEMORY|SND_ASYNC);
\r
6027 /* .wav file. Error if not found. */
\r
6028 if (ms->data == NULL) return FALSE;
\r
6029 ok = PlaySound(ms->data, NULL, SND_MEMORY|SND_ASYNC);
\r
6032 /* Don't print an error: this can happen innocently if the sound driver
\r
6033 is busy; for instance, if another instance of WinBoard is playing
\r
6034 a sound at about the same time. */
\r
6040 OldOpenFileHook(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
6043 OPENFILENAME *ofn;
\r
6044 static UINT *number; /* gross that this is static */
\r
6046 switch (message) {
\r
6047 case WM_INITDIALOG: /* message: initialize dialog box */
\r
6048 /* Center the dialog over the application window */
\r
6049 ofn = (OPENFILENAME *) lParam;
\r
6050 if (ofn->Flags & OFN_ENABLETEMPLATE) {
\r
6051 number = (UINT *) ofn->lCustData;
\r
6052 SendMessage(GetDlgItem(hDlg, edt2), WM_SETTEXT, 0, (LPARAM) "");
\r
6056 CenterWindow (hDlg, GetWindow (hDlg, GW_OWNER));
\r
6057 Translate(hDlg, 1536);
\r
6058 return FALSE; /* Allow for further processing */
\r
6061 if ((LOWORD(wParam) == IDOK) && (number != NULL)) {
\r
6062 *number = GetDlgItemInt(hDlg, OPT_IndexNumberOld, &ok, FALSE);
\r
6064 return FALSE; /* Allow for further processing */
\r
6070 OpenFileHook(HWND hdlg, UINT uiMsg, WPARAM wParam, LPARAM lParam)
\r
6072 static UINT *number;
\r
6073 OPENFILENAME *ofname;
\r
6076 case WM_INITDIALOG:
\r
6077 Translate(hdlg, DLG_IndexNumber);
\r
6078 ofname = (OPENFILENAME *)lParam;
\r
6079 number = (UINT *)(ofname->lCustData);
\r
6082 ofnot = (OFNOTIFY *)lParam;
\r
6083 if (ofnot->hdr.code == CDN_FILEOK) {
\r
6084 *number = GetDlgItemInt(hdlg, OPT_IndexNumber, NULL, FALSE);
\r
6093 OpenFileDialog(HWND hwnd, char *write, char *defName, char *defExt, // [HGM] diag: type of 'write' now string
\r
6094 char *nameFilt, char *dlgTitle, UINT *number,
\r
6095 char fileTitle[MSG_SIZ], char fileName[MSG_SIZ])
\r
6097 OPENFILENAME openFileName;
\r
6098 char buf1[MSG_SIZ];
\r
6101 if (fileName == NULL) fileName = buf1;
\r
6102 if (defName == NULL) {
\r
6103 safeStrCpy(fileName, "*.", 3 );
\r
6104 strcat(fileName, defExt);
\r
6106 safeStrCpy(fileName, defName, MSG_SIZ );
\r
6108 if (fileTitle) safeStrCpy(fileTitle, "", MSG_SIZ );
\r
6109 if (number) *number = 0;
\r
6111 openFileName.lStructSize = sizeof(OPENFILENAME);
\r
6112 openFileName.hwndOwner = hwnd;
\r
6113 openFileName.hInstance = (HANDLE) hInst;
\r
6114 openFileName.lpstrFilter = nameFilt;
\r
6115 openFileName.lpstrCustomFilter = (LPSTR) NULL;
\r
6116 openFileName.nMaxCustFilter = 0L;
\r
6117 openFileName.nFilterIndex = 1L;
\r
6118 openFileName.lpstrFile = fileName;
\r
6119 openFileName.nMaxFile = MSG_SIZ;
\r
6120 openFileName.lpstrFileTitle = fileTitle;
\r
6121 openFileName.nMaxFileTitle = fileTitle ? MSG_SIZ : 0;
\r
6122 openFileName.lpstrInitialDir = NULL;
\r
6123 openFileName.lpstrTitle = dlgTitle;
\r
6124 openFileName.Flags = OFN_PATHMUSTEXIST | OFN_HIDEREADONLY
\r
6125 | (write[0] != 'r' ? 0 : OFN_FILEMUSTEXIST)
\r
6126 | (number ? OFN_ENABLETEMPLATE | OFN_ENABLEHOOK: 0)
\r
6127 | (oldDialog ? 0 : OFN_EXPLORER);
\r
6128 openFileName.nFileOffset = 0;
\r
6129 openFileName.nFileExtension = 0;
\r
6130 openFileName.lpstrDefExt = defExt;
\r
6131 openFileName.lCustData = (LONG) number;
\r
6132 openFileName.lpfnHook = oldDialog ?
\r
6133 (LPOFNHOOKPROC) OldOpenFileHook : (LPOFNHOOKPROC) OpenFileHook;
\r
6134 openFileName.lpTemplateName = (LPSTR)(oldDialog ? 1536 : DLG_IndexNumber);
\r
6136 if (write[0] != 'r' ? GetSaveFileName(&openFileName) :
\r
6137 GetOpenFileName(&openFileName)) {
\r
6138 /* open the file */
\r
6139 f = fopen(openFileName.lpstrFile, write);
\r
6141 MessageBox(hwnd, _("File open failed"), NULL,
\r
6142 MB_OK|MB_ICONEXCLAMATION);
\r
6146 int err = CommDlgExtendedError();
\r
6147 if (err != 0) DisplayError(_("Internal error in file dialog box"), err);
\r
6156 MenuPopup(HWND hwnd, POINT pt, HMENU hmenu, UINT def)
\r
6158 HMENU hmenuTrackPopup; /* floating pop-up menu */
\r
6161 * Get the first pop-up menu in the menu template. This is the
\r
6162 * menu that TrackPopupMenu displays.
\r
6164 hmenuTrackPopup = GetSubMenu(hmenu, 0);
\r
6165 TranslateOneMenu(10, hmenuTrackPopup);
\r
6167 SetMenuDefaultItem(hmenuTrackPopup, def, FALSE);
\r
6170 * TrackPopup uses screen coordinates, so convert the
\r
6171 * coordinates of the mouse click to screen coordinates.
\r
6173 ClientToScreen(hwnd, (LPPOINT) &pt);
\r
6175 /* Draw and track the floating pop-up menu. */
\r
6176 TrackPopupMenu(hmenuTrackPopup, TPM_CENTERALIGN | TPM_RIGHTBUTTON,
\r
6177 pt.x, pt.y, 0, hwnd, NULL);
\r
6179 /* Destroy the menu.*/
\r
6180 DestroyMenu(hmenu);
\r
6185 int sizeX, sizeY, newSizeX, newSizeY;
\r
6187 } ResizeEditPlusButtonsClosure;
\r
6190 ResizeEditPlusButtonsCallback(HWND hChild, LPARAM lparam)
\r
6192 ResizeEditPlusButtonsClosure *cl = (ResizeEditPlusButtonsClosure *)lparam;
\r
6196 if (hChild == cl->hText) return TRUE;
\r
6197 GetWindowRect(hChild, &rect); /* gives screen coords */
\r
6198 pt.x = rect.left + (cl->newSizeX - cl->sizeX)/2;
\r
6199 pt.y = rect.top + cl->newSizeY - cl->sizeY;
\r
6200 ScreenToClient(cl->hDlg, &pt);
\r
6201 cl->hdwp = DeferWindowPos(cl->hdwp, hChild, NULL,
\r
6202 pt.x, pt.y, rect.right - rect.left, rect.bottom - rect.top, SWP_NOZORDER);
\r
6206 /* Resize a dialog that has a (rich) edit field filling most of
\r
6207 the top, with a row of buttons below */
\r
6209 ResizeEditPlusButtons(HWND hDlg, HWND hText, int sizeX, int sizeY, int newSizeX, int newSizeY)
\r
6212 int newTextHeight, newTextWidth;
\r
6213 ResizeEditPlusButtonsClosure cl;
\r
6215 /*if (IsIconic(hDlg)) return;*/
\r
6216 if (newSizeX == sizeX && newSizeY == sizeY) return;
\r
6218 cl.hdwp = BeginDeferWindowPos(8);
\r
6220 GetWindowRect(hText, &rectText); /* gives screen coords */
\r
6221 newTextWidth = rectText.right - rectText.left + newSizeX - sizeX;
\r
6222 newTextHeight = rectText.bottom - rectText.top + newSizeY - sizeY;
\r
6223 if (newTextHeight < 0) {
\r
6224 newSizeY += -newTextHeight;
\r
6225 newTextHeight = 0;
\r
6227 cl.hdwp = DeferWindowPos(cl.hdwp, hText, NULL, 0, 0,
\r
6228 newTextWidth, newTextHeight, SWP_NOZORDER|SWP_NOMOVE);
\r
6234 cl.newSizeX = newSizeX;
\r
6235 cl.newSizeY = newSizeY;
\r
6236 EnumChildWindows(hDlg, ResizeEditPlusButtonsCallback, (LPARAM)&cl);
\r
6238 EndDeferWindowPos(cl.hdwp);
\r
6241 BOOL CenterWindowEx(HWND hwndChild, HWND hwndParent, int mode)
\r
6243 RECT rChild, rParent;
\r
6244 int wChild, hChild, wParent, hParent;
\r
6245 int wScreen, hScreen, xNew, yNew;
\r
6248 /* Get the Height and Width of the child window */
\r
6249 GetWindowRect (hwndChild, &rChild);
\r
6250 wChild = rChild.right - rChild.left;
\r
6251 hChild = rChild.bottom - rChild.top;
\r
6253 /* Get the Height and Width of the parent window */
\r
6254 GetWindowRect (hwndParent, &rParent);
\r
6255 wParent = rParent.right - rParent.left;
\r
6256 hParent = rParent.bottom - rParent.top;
\r
6258 /* Get the display limits */
\r
6259 hdc = GetDC (hwndChild);
\r
6260 wScreen = GetDeviceCaps (hdc, HORZRES);
\r
6261 hScreen = GetDeviceCaps (hdc, VERTRES);
\r
6262 ReleaseDC(hwndChild, hdc);
\r
6264 /* Calculate new X position, then adjust for screen */
\r
6265 xNew = rParent.left + ((wParent - wChild) /2);
\r
6268 } else if ((xNew+wChild) > wScreen) {
\r
6269 xNew = wScreen - wChild;
\r
6272 /* Calculate new Y position, then adjust for screen */
\r
6274 yNew = rParent.top + ((hParent - hChild) /2);
\r
6277 yNew = rParent.top + GetSystemMetrics( SM_CYCAPTION ) * 2 / 3;
\r
6282 } else if ((yNew+hChild) > hScreen) {
\r
6283 yNew = hScreen - hChild;
\r
6286 /* Set it, and return */
\r
6287 return SetWindowPos (hwndChild, NULL,
\r
6288 xNew, yNew, 0, 0, SWP_NOSIZE | SWP_NOZORDER);
\r
6291 /* Center one window over another */
\r
6292 BOOL CenterWindow (HWND hwndChild, HWND hwndParent)
\r
6294 return CenterWindowEx( hwndChild, hwndParent, 0 );
\r
6297 /*---------------------------------------------------------------------------*\
\r
6299 * Startup Dialog functions
\r
6301 \*---------------------------------------------------------------------------*/
\r
6303 InitComboStrings(HANDLE hwndCombo, char **cd)
\r
6305 SendMessage(hwndCombo, CB_RESETCONTENT, 0, 0);
\r
6307 while (*cd != NULL) {
\r
6308 SendMessage(hwndCombo, CB_ADDSTRING, 0, (LPARAM) T_(*cd));
\r
6314 InitComboStringsFromOption(HANDLE hwndCombo, char *str)
\r
6316 char buf1[MAX_ARG_LEN];
\r
6319 if (str[0] == '@') {
\r
6320 FILE* f = fopen(str + 1, "r");
\r
6322 DisplayFatalError(str + 1, errno, 2);
\r
6325 len = fread(buf1, 1, sizeof(buf1)-1, f);
\r
6327 buf1[len] = NULLCHAR;
\r
6333 SendMessage(hwndCombo, CB_RESETCONTENT, 0, 0);
\r
6336 char buf[MSG_SIZ];
\r
6337 char *end = strchr(str, '\n');
\r
6338 if (end == NULL) return;
\r
6339 memcpy(buf, str, end - str);
\r
6340 buf[end - str] = NULLCHAR;
\r
6341 SendMessage(hwndCombo, CB_ADDSTRING, 0, (LPARAM) buf);
\r
6347 SetStartupDialogEnables(HWND hDlg)
\r
6349 EnableWindow(GetDlgItem(hDlg, OPT_ChessEngineName),
\r
6350 IsDlgButtonChecked(hDlg, OPT_ChessEngine) ||
\r
6351 (appData.zippyPlay && IsDlgButtonChecked(hDlg, OPT_ChessServer)));
\r
6352 EnableWindow(GetDlgItem(hDlg, OPT_SecondChessEngineName),
\r
6353 IsDlgButtonChecked(hDlg, OPT_ChessEngine));
\r
6354 EnableWindow(GetDlgItem(hDlg, OPT_ChessServerName),
\r
6355 IsDlgButtonChecked(hDlg, OPT_ChessServer));
\r
6356 EnableWindow(GetDlgItem(hDlg, OPT_AdditionalOptions),
\r
6357 IsDlgButtonChecked(hDlg, OPT_AnyAdditional));
\r
6358 EnableWindow(GetDlgItem(hDlg, IDOK),
\r
6359 IsDlgButtonChecked(hDlg, OPT_ChessEngine) ||
\r
6360 IsDlgButtonChecked(hDlg, OPT_ChessServer) ||
\r
6361 IsDlgButtonChecked(hDlg, OPT_View));
\r
6365 QuoteForFilename(char *filename)
\r
6367 int dquote, space;
\r
6368 dquote = strchr(filename, '"') != NULL;
\r
6369 space = strchr(filename, ' ') != NULL;
\r
6370 if (dquote || space) {
\r
6382 InitEngineBox(HWND hDlg, HWND hwndCombo, char* nthcp, char* nthd, char* nthdir, char *nthnames)
\r
6384 char buf[MSG_SIZ];
\r
6387 InitComboStringsFromOption(hwndCombo, nthnames);
\r
6388 q = QuoteForFilename(nthcp);
\r
6389 snprintf(buf, MSG_SIZ, "%s%s%s", q, nthcp, q);
\r
6390 if (*nthdir != NULLCHAR) {
\r
6391 q = QuoteForFilename(nthdir);
\r
6392 snprintf(buf + strlen(buf), MSG_SIZ, " /%s=%s%s%s", nthd, q, nthdir, q);
\r
6394 if (*nthcp == NULLCHAR) {
\r
6395 SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) 0, (LPARAM) 0);
\r
6396 } else if (SendMessage(hwndCombo, CB_SELECTSTRING, (WPARAM) -1, (LPARAM) buf) == CB_ERR) {
\r
6397 SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) -1, (LPARAM) 0);
\r
6398 SendMessage(hwndCombo, WM_SETTEXT, (WPARAM) 0, (LPARAM) buf);
\r
6403 StartupDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
6405 char buf[MSG_SIZ];
\r
6409 switch (message) {
\r
6410 case WM_INITDIALOG:
\r
6411 /* Center the dialog */
\r
6412 CenterWindow (hDlg, GetDesktopWindow());
\r
6413 Translate(hDlg, DLG_Startup);
\r
6414 /* Initialize the dialog items */
\r
6415 InitEngineBox(hDlg, GetDlgItem(hDlg, OPT_ChessEngineName),
\r
6416 appData.firstChessProgram, "fd", appData.firstDirectory,
\r
6417 firstChessProgramNames);
\r
6418 InitEngineBox(hDlg, GetDlgItem(hDlg, OPT_SecondChessEngineName),
\r
6419 appData.secondChessProgram, singleList ? "fd" : "sd", appData.secondDirectory,
\r
6420 singleList ? firstChessProgramNames : secondChessProgramNames); //[HGM] single: use first list in second combo
\r
6421 hwndCombo = GetDlgItem(hDlg, OPT_ChessServerName);
\r
6422 InitComboStringsFromOption(hwndCombo, icsNames);
\r
6423 snprintf(buf, MSG_SIZ, "%s /icsport=%s", appData.icsHost, appData.icsPort);
\r
6424 if (*appData.icsHelper != NULLCHAR) {
\r
6425 char *q = QuoteForFilename(appData.icsHelper);
\r
6426 sprintf(buf + strlen(buf), " /icshelper=%s%s%s", q, appData.icsHelper, q);
\r
6428 if (*appData.icsHost == NULLCHAR) {
\r
6429 SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) 0, (LPARAM) 0);
\r
6430 /*SendMessage(hwndCombo, CB_SHOWDROPDOWN, (WPARAM) TRUE, (LPARAM) 0); !!too soon */
\r
6431 } else if (SendMessage(hwndCombo, CB_SELECTSTRING, (WPARAM) -1, (LPARAM) buf) == CB_ERR) {
\r
6432 SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) -1, (LPARAM) 0);
\r
6433 SendMessage(hwndCombo, WM_SETTEXT, (WPARAM) 0, (LPARAM) buf);
\r
6436 if (appData.icsActive) {
\r
6437 CheckDlgButton(hDlg, OPT_ChessServer, BST_CHECKED);
\r
6439 else if (appData.noChessProgram) {
\r
6440 CheckDlgButton(hDlg, OPT_View, BST_CHECKED);
\r
6443 CheckDlgButton(hDlg, OPT_ChessEngine, BST_CHECKED);
\r
6446 SetStartupDialogEnables(hDlg);
\r
6450 switch (LOWORD(wParam)) {
\r
6452 if (IsDlgButtonChecked(hDlg, OPT_ChessEngine)) {
\r
6453 safeStrCpy(buf, "/fcp=", sizeof(buf)/sizeof(buf[0]) );
\r
6454 GetDlgItemText(hDlg, OPT_ChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));
\r
6456 currentEngine[0] = strdup(p+5); // [HGM] recent: remember complete line of first combobox
\r
6457 ParseArgs(StringGet, &p);
\r
6458 safeStrCpy(buf, singleList ? "/fcp=" : "/scp=", sizeof(buf)/sizeof(buf[0]) );
\r
6459 GetDlgItemText(hDlg, OPT_SecondChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));
\r
6461 currentEngine[1] = strdup(p+5); // [HGM] also remember engine line of 2nd for saving its settings
\r
6462 SwapEngines(singleList); // temporarily swap first and second, to load a second 'first', ...
\r
6463 ParseArgs(StringGet, &p);
\r
6464 SwapEngines(singleList); // ... and then make it 'second'
\r
6466 appData.noChessProgram = FALSE;
\r
6467 appData.icsActive = FALSE;
\r
6468 } else if (IsDlgButtonChecked(hDlg, OPT_ChessServer)) {
\r
6469 safeStrCpy(buf, "/ics /icshost=", sizeof(buf)/sizeof(buf[0]) );
\r
6470 GetDlgItemText(hDlg, OPT_ChessServerName, buf + strlen(buf), sizeof(buf) - strlen(buf));
\r
6472 ParseArgs(StringGet, &p);
\r
6473 if (appData.zippyPlay) {
\r
6474 safeStrCpy(buf, "/fcp=", sizeof(buf)/sizeof(buf[0]) );
\r
6475 GetDlgItemText(hDlg, OPT_ChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));
\r
6477 ParseArgs(StringGet, &p);
\r
6479 } else if (IsDlgButtonChecked(hDlg, OPT_View)) {
\r
6480 appData.noChessProgram = TRUE;
\r
6481 appData.icsActive = FALSE;
\r
6483 MessageBox(hDlg, _("Choose an option, or cancel to exit"),
\r
6484 _("Option Error"), MB_OK|MB_ICONEXCLAMATION);
\r
6487 if (IsDlgButtonChecked(hDlg, OPT_AnyAdditional)) {
\r
6488 GetDlgItemText(hDlg, OPT_AdditionalOptions, buf, sizeof(buf));
\r
6490 ParseArgs(StringGet, &p);
\r
6492 EndDialog(hDlg, TRUE);
\r
6499 case IDM_HELPCONTENTS:
\r
6500 if (!WinHelp (hDlg, "winboard.hlp", HELP_KEY,(DWORD)(LPSTR)"CONTENTS")) {
\r
6501 MessageBox (GetFocus(),
\r
6502 _("Unable to activate help"),
\r
6503 szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);
\r
6508 SetStartupDialogEnables(hDlg);
\r
6516 /*---------------------------------------------------------------------------*\
\r
6518 * About box dialog functions
\r
6520 \*---------------------------------------------------------------------------*/
\r
6522 /* Process messages for "About" dialog box */
\r
6524 About(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
6526 switch (message) {
\r
6527 case WM_INITDIALOG: /* message: initialize dialog box */
\r
6528 /* Center the dialog over the application window */
\r
6529 CenterWindow (hDlg, GetWindow (hDlg, GW_OWNER));
\r
6530 SetDlgItemText(hDlg, ABOUTBOX_Version, programVersion);
\r
6531 Translate(hDlg, ABOUTBOX);
\r
6535 case WM_COMMAND: /* message: received a command */
\r
6536 if (LOWORD(wParam) == IDOK /* "OK" box selected? */
\r
6537 || LOWORD(wParam) == IDCANCEL) { /* System menu close command? */
\r
6538 EndDialog(hDlg, TRUE); /* Exit the dialog */
\r
6546 /*---------------------------------------------------------------------------*\
\r
6548 * Comment Dialog functions
\r
6550 \*---------------------------------------------------------------------------*/
\r
6553 CommentDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
6555 static HANDLE hwndText = NULL;
\r
6556 int len, newSizeX, newSizeY;
\r
6557 static int sizeX, sizeY;
\r
6562 switch (message) {
\r
6563 case WM_INITDIALOG: /* message: initialize dialog box */
\r
6564 /* Initialize the dialog items */
\r
6565 Translate(hDlg, DLG_EditComment);
\r
6566 hwndText = GetDlgItem(hDlg, OPT_CommentText);
\r
6567 SetDlgItemText(hDlg, OPT_CommentText, commentText);
\r
6568 EnableWindow(GetDlgItem(hDlg, OPT_CancelComment), editComment);
\r
6569 EnableWindow(GetDlgItem(hDlg, OPT_ClearComment), editComment);
\r
6570 EnableWindow(GetDlgItem(hDlg, OPT_EditComment), !editComment);
\r
6571 SendMessage(hwndText, EM_SETREADONLY, !editComment, 0);
\r
6572 SetWindowText(hDlg, commentTitle);
\r
6573 if (editComment) {
\r
6574 SetFocus(hwndText);
\r
6576 SetFocus(GetDlgItem(hDlg, IDOK));
\r
6578 SendMessage(GetDlgItem(hDlg, OPT_CommentText),
\r
6579 WM_SETFONT, (WPARAM)font[boardSize][COMMENT_FONT]->hf,
\r
6580 MAKELPARAM(FALSE, 0));
\r
6581 /* Size and position the dialog */
\r
6582 if (!commentDialog) {
\r
6583 commentDialog = hDlg;
\r
6584 GetClientRect(hDlg, &rect);
\r
6585 sizeX = rect.right;
\r
6586 sizeY = rect.bottom;
\r
6587 if (wpComment.x != CW_USEDEFAULT && wpComment.y != CW_USEDEFAULT &&
\r
6588 wpComment.width != CW_USEDEFAULT && wpComment.height != CW_USEDEFAULT) {
\r
6589 WINDOWPLACEMENT wp;
\r
6590 EnsureOnScreen(&wpComment.x, &wpComment.y, 0, 0);
\r
6591 wp.length = sizeof(WINDOWPLACEMENT);
\r
6593 wp.showCmd = SW_SHOW;
\r
6594 wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;
\r
6595 wp.rcNormalPosition.left = wpComment.x;
\r
6596 wp.rcNormalPosition.right = wpComment.x + wpComment.width;
\r
6597 wp.rcNormalPosition.top = wpComment.y;
\r
6598 wp.rcNormalPosition.bottom = wpComment.y + wpComment.height;
\r
6599 SetWindowPlacement(hDlg, &wp);
\r
6601 GetClientRect(hDlg, &rect);
\r
6602 newSizeX = rect.right;
\r
6603 newSizeY = rect.bottom;
\r
6604 ResizeEditPlusButtons(hDlg, hwndText, sizeX, sizeY,
\r
6605 newSizeX, newSizeY);
\r
6610 SendDlgItemMessage( hDlg, OPT_CommentText, EM_SETEVENTMASK, 0, ENM_MOUSEEVENTS | ENM_KEYEVENTS );
\r
6613 case WM_COMMAND: /* message: received a command */
\r
6614 switch (LOWORD(wParam)) {
\r
6616 if (editComment) {
\r
6618 /* Read changed options from the dialog box */
\r
6619 hwndText = GetDlgItem(hDlg, OPT_CommentText);
\r
6620 len = GetWindowTextLength(hwndText);
\r
6621 str = (char *) malloc(len + 1);
\r
6622 GetWindowText(hwndText, str, len + 1);
\r
6631 ReplaceComment(commentIndex, str);
\r
6638 case OPT_CancelComment:
\r
6642 case OPT_ClearComment:
\r
6643 SetDlgItemText(hDlg, OPT_CommentText, "");
\r
6646 case OPT_EditComment:
\r
6647 EditCommentEvent();
\r
6655 case WM_NOTIFY: // [HGM] vari: cloned from whistory.c
\r
6656 if( wParam == OPT_CommentText ) {
\r
6657 MSGFILTER * lpMF = (MSGFILTER *) lParam;
\r
6659 if( lpMF->msg == WM_RBUTTONDOWN && (lpMF->wParam & (MK_CONTROL | MK_SHIFT)) == 0 ||
\r
6660 lpMF->msg == WM_CHAR && lpMF->wParam == '\022' ) {
\r
6664 pt.x = LOWORD( lpMF->lParam );
\r
6665 pt.y = HIWORD( lpMF->lParam );
\r
6667 if(lpMF->msg == WM_CHAR) {
\r
6669 SendDlgItemMessage( hDlg, OPT_CommentText, EM_EXGETSEL, 0, (LPARAM) &sel );
\r
6670 index = sel.cpMin;
\r
6672 index = SendDlgItemMessage( hDlg, OPT_CommentText, EM_CHARFROMPOS, 0, (LPARAM) &pt );
\r
6674 hwndText = GetDlgItem(hDlg, OPT_CommentText); // cloned from above
\r
6675 len = GetWindowTextLength(hwndText);
\r
6676 str = (char *) malloc(len + 1);
\r
6677 GetWindowText(hwndText, str, len + 1);
\r
6678 ReplaceComment(commentIndex, str);
\r
6679 if(commentIndex != currentMove) ToNrEvent(commentIndex);
\r
6680 LoadVariation( index, str ); // [HGM] also does the actual moving to it, now
\r
6683 /* Zap the message for good: apparently, returning non-zero is not enough */
\r
6684 lpMF->msg = WM_USER;
\r
6692 newSizeX = LOWORD(lParam);
\r
6693 newSizeY = HIWORD(lParam);
\r
6694 ResizeEditPlusButtons(hDlg, hwndText, sizeX, sizeY, newSizeX, newSizeY);
\r
6699 case WM_GETMINMAXINFO:
\r
6700 /* Prevent resizing window too small */
\r
6701 mmi = (MINMAXINFO *) lParam;
\r
6702 mmi->ptMinTrackSize.x = 100;
\r
6703 mmi->ptMinTrackSize.y = 100;
\r
6710 EitherCommentPopUp(int index, char *title, char *str, BOOLEAN edit)
\r
6715 CheckMenuItem(GetMenu(hwndMain), IDM_Comment, edit ? MF_CHECKED : MF_UNCHECKED);
\r
6717 if (str == NULL) str = "";
\r
6718 p = (char *) malloc(2 * strlen(str) + 2);
\r
6721 if (*str == '\n') *q++ = '\r';
\r
6725 if (commentText != NULL) free(commentText);
\r
6727 commentIndex = index;
\r
6728 commentTitle = title;
\r
6730 editComment = edit;
\r
6732 if (commentDialog) {
\r
6733 SendMessage(commentDialog, WM_INITDIALOG, 0, 0);
\r
6734 if (!commentUp) ShowWindow(commentDialog, SW_SHOW);
\r
6736 lpProc = MakeProcInstance((FARPROC)CommentDialog, hInst);
\r
6737 CreateDialog(hInst, MAKEINTRESOURCE(DLG_EditComment),
\r
6738 hwndMain, (DLGPROC)lpProc);
\r
6739 FreeProcInstance(lpProc);
\r
6745 /*---------------------------------------------------------------------------*\
\r
6747 * Type-in move dialog functions
\r
6749 \*---------------------------------------------------------------------------*/
\r
6752 TypeInMoveDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
6754 char move[MSG_SIZ];
\r
6757 switch (message) {
\r
6758 case WM_INITDIALOG:
\r
6759 move[0] = (char) lParam;
\r
6760 move[1] = NULLCHAR;
\r
6761 CenterWindowEx(hDlg, GetWindow(hDlg, GW_OWNER), 1 );
\r
6762 Translate(hDlg, DLG_TypeInMove);
\r
6763 hInput = GetDlgItem(hDlg, OPT_Move);
\r
6764 SetWindowText(hInput, move);
\r
6766 SendMessage(hInput, EM_SETSEL, (WPARAM)9999, (LPARAM)9999);
\r
6770 switch (LOWORD(wParam)) {
\r
6773 shiftKey = GetKeyState(VK_SHIFT) < 0; // [HGM] remember last shift status
\r
6774 GetDlgItemText(hDlg, OPT_Move, move, sizeof(move));
\r
6775 TypeInDoneEvent(move);
\r
6776 EndDialog(hDlg, TRUE);
\r
6779 EndDialog(hDlg, FALSE);
\r
6790 PopUpMoveDialog(char firstchar)
\r
6794 lpProc = MakeProcInstance((FARPROC)TypeInMoveDialog, hInst);
\r
6795 DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_TypeInMove),
\r
6796 hwndMain, (DLGPROC)lpProc, (LPARAM)firstchar);
\r
6797 FreeProcInstance(lpProc);
\r
6800 /*---------------------------------------------------------------------------*\
\r
6802 * Type-in name dialog functions
\r
6804 \*---------------------------------------------------------------------------*/
\r
6807 TypeInNameDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
6809 char move[MSG_SIZ];
\r
6812 switch (message) {
\r
6813 case WM_INITDIALOG:
\r
6814 move[0] = (char) lParam;
\r
6815 move[1] = NULLCHAR;
\r
6816 CenterWindowEx(hDlg, GetWindow(hDlg, GW_OWNER), 1 );
\r
6817 Translate(hDlg, DLG_TypeInName);
\r
6818 hInput = GetDlgItem(hDlg, OPT_Name);
\r
6819 SetWindowText(hInput, move);
\r
6821 SendMessage(hInput, EM_SETSEL, (WPARAM)9999, (LPARAM)9999);
\r
6825 switch (LOWORD(wParam)) {
\r
6827 GetDlgItemText(hDlg, OPT_Name, move, sizeof(move));
\r
6828 appData.userName = strdup(move);
\r
6829 SetUserLogo(); DisplayLogos();
\r
6831 if(gameMode == MachinePlaysWhite || gameMode == MachinePlaysBlack) {
\r
6832 snprintf(move, MSG_SIZ, "%s vs. %s", gameInfo.white, gameInfo.black);
\r
6833 DisplayTitle(move);
\r
6837 EndDialog(hDlg, TRUE);
\r
6840 EndDialog(hDlg, FALSE);
\r
6851 PopUpNameDialog(char firstchar)
\r
6855 lpProc = MakeProcInstance((FARPROC)TypeInNameDialog, hInst);
\r
6856 DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_TypeInName),
\r
6857 hwndMain, (DLGPROC)lpProc, (LPARAM)firstchar);
\r
6858 FreeProcInstance(lpProc);
\r
6861 /*---------------------------------------------------------------------------*\
\r
6865 \*---------------------------------------------------------------------------*/
\r
6867 /* Nonmodal error box */
\r
6868 LRESULT CALLBACK ErrorDialog(HWND hDlg, UINT message,
\r
6869 WPARAM wParam, LPARAM lParam);
\r
6872 ErrorPopUp(char *title, char *content)
\r
6876 BOOLEAN modal = hwndMain == NULL;
\r
6894 strncpy(errorTitle, title, sizeof(errorTitle));
\r
6895 errorTitle[sizeof(errorTitle) - 1] = '\0';
\r
6898 MessageBox(NULL, errorMessage, errorTitle, MB_OK|MB_ICONEXCLAMATION);
\r
6900 lpProc = MakeProcInstance((FARPROC)ErrorDialog, hInst);
\r
6901 CreateDialog(hInst, MAKEINTRESOURCE(DLG_Error),
\r
6902 hwndMain, (DLGPROC)lpProc);
\r
6903 FreeProcInstance(lpProc);
\r
6910 if (!appData.popupMoveErrors && moveErrorMessageUp) DisplayMessage("", "");
\r
6911 if (errorDialog == NULL) return;
\r
6912 DestroyWindow(errorDialog);
\r
6913 errorDialog = NULL;
\r
6914 if(errorExitStatus) ExitEvent(errorExitStatus);
\r
6918 ErrorDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
6922 switch (message) {
\r
6923 case WM_INITDIALOG:
\r
6924 GetWindowRect(hDlg, &rChild);
\r
6927 SetWindowPos(hDlg, NULL, rChild.left,
\r
6928 rChild.top + boardRect.top - (rChild.bottom - rChild.top),
\r
6929 0, 0, SWP_NOZORDER|SWP_NOSIZE);
\r
6933 [AS] It seems that the above code wants to move the dialog up in the "caption
\r
6934 area" of the main window, but it uses the dialog height as an hard-coded constant,
\r
6935 and it doesn't work when you resize the dialog.
\r
6936 For now, just give it a default position.
\r
6938 SetWindowPos(hDlg, NULL, boardRect.left+8, boardRect.top+8, 0, 0, SWP_NOZORDER|SWP_NOSIZE);
\r
6939 Translate(hDlg, DLG_Error);
\r
6941 errorDialog = hDlg;
\r
6942 SetWindowText(hDlg, errorTitle);
\r
6943 SetDlgItemText(hDlg, OPT_ErrorText, errorMessage);
\r
6947 switch (LOWORD(wParam)) {
\r
6950 if (errorDialog == hDlg) errorDialog = NULL;
\r
6951 DestroyWindow(hDlg);
\r
6963 HWND gothicDialog = NULL;
\r
6966 GothicDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
6969 int height = GetSystemMetrics(SM_CYCAPTION)+GetSystemMetrics(SM_CYFRAME);
\r
6971 switch (message) {
\r
6972 case WM_INITDIALOG:
\r
6973 GetWindowRect(hDlg, &rChild);
\r
6975 SetWindowPos(hDlg, NULL, wpMain.x, wpMain.y-height, wpMain.width, height,
\r
6979 [AS] It seems that the above code wants to move the dialog up in the "caption
\r
6980 area" of the main window, but it uses the dialog height as an hard-coded constant,
\r
6981 and it doesn't work when you resize the dialog.
\r
6982 For now, just give it a default position.
\r
6984 gothicDialog = hDlg;
\r
6985 SetWindowText(hDlg, errorTitle);
\r
6986 SetDlgItemText(hDlg, OPT_ErrorText, errorMessage);
\r
6990 switch (LOWORD(wParam)) {
\r
6993 if (errorDialog == hDlg) errorDialog = NULL;
\r
6994 DestroyWindow(hDlg);
\r
7006 GothicPopUp(char *title, VariantClass variant)
\r
7009 static char *lastTitle;
\r
7011 strncpy(errorTitle, title, sizeof(errorTitle));
\r
7012 errorTitle[sizeof(errorTitle) - 1] = '\0';
\r
7014 if(lastTitle != title && gothicDialog != NULL) {
\r
7015 DestroyWindow(gothicDialog);
\r
7016 gothicDialog = NULL;
\r
7018 if(variant != VariantNormal && gothicDialog == NULL) {
\r
7019 title = lastTitle;
\r
7020 lpProc = MakeProcInstance((FARPROC)GothicDialog, hInst);
\r
7021 CreateDialog(hInst, MAKEINTRESOURCE(DLG_Error),
\r
7022 hwndMain, (DLGPROC)lpProc);
\r
7023 FreeProcInstance(lpProc);
\r
7028 /*---------------------------------------------------------------------------*\
\r
7030 * Ics Interaction console functions
\r
7032 \*---------------------------------------------------------------------------*/
\r
7034 #define HISTORY_SIZE 64
\r
7035 static char *history[HISTORY_SIZE];
\r
7036 int histIn = 0, histP = 0;
\r
7040 SaveInHistory(char *cmd)
\r
7042 if (history[histIn] != NULL) {
\r
7043 free(history[histIn]);
\r
7044 history[histIn] = NULL;
\r
7046 if (*cmd == NULLCHAR) return;
\r
7047 history[histIn] = StrSave(cmd);
\r
7048 histIn = (histIn + 1) % HISTORY_SIZE;
\r
7049 if (history[histIn] != NULL) {
\r
7050 free(history[histIn]);
\r
7052 history[histIn] = NULL;
\r
7058 PrevInHistory(char *cmd)
\r
7061 if (histP == histIn) {
\r
7062 if (history[histIn] != NULL) free(history[histIn]);
\r
7063 history[histIn] = StrSave(cmd);
\r
7065 newhp = (histP - 1 + HISTORY_SIZE) % HISTORY_SIZE;
\r
7066 if (newhp == histIn || history[newhp] == NULL) return NULL;
\r
7068 return history[histP];
\r
7074 if (histP == histIn) return NULL;
\r
7075 histP = (histP + 1) % HISTORY_SIZE;
\r
7076 return history[histP];
\r
7080 LoadIcsTextMenu(IcsTextMenuEntry *e)
\r
7084 hmenu = LoadMenu(hInst, "TextMenu");
\r
7085 h = GetSubMenu(hmenu, 0);
\r
7087 if (strcmp(e->item, "-") == 0) {
\r
7088 AppendMenu(h, MF_SEPARATOR, 0, 0);
\r
7089 } else { // [HGM] re-written a bit to use only one AppendMenu call for both cases (| or no |)
\r
7090 int flags = MF_STRING, j = 0;
\r
7091 if (e->item[0] == '|') {
\r
7092 flags |= MF_MENUBARBREAK;
\r
7095 if(!strcmp(e->command, "none")) flags |= MF_GRAYED; // [HGM] chatclick: provide inactive dummy
\r
7096 AppendMenu(h, flags, IDM_CommandX + i, e->item + j);
\r
7104 WNDPROC consoleTextWindowProc;
\r
7107 CommandX(HWND hwnd, char *command, BOOLEAN getname, BOOLEAN immediate)
\r
7109 char buf[MSG_SIZ], name[MSG_SIZ];
\r
7110 HWND hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
7114 SetWindowText(hInput, command);
\r
7116 SendMessage(hInput, WM_CHAR, '\r', 0);
\r
7118 sel.cpMin = 999999;
\r
7119 sel.cpMax = 999999;
\r
7120 SendMessage(hInput, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
7125 SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);
\r
7126 if (sel.cpMin == sel.cpMax) {
\r
7127 /* Expand to surrounding word */
\r
7130 tr.chrg.cpMax = sel.cpMin;
\r
7131 tr.chrg.cpMin = --sel.cpMin;
\r
7132 if (sel.cpMin < 0) break;
\r
7133 tr.lpstrText = name;
\r
7134 SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr);
\r
7135 } while (isalpha(name[0]) || isdigit(name[0]) || name[0] == '-');
\r
7139 tr.chrg.cpMin = sel.cpMax;
\r
7140 tr.chrg.cpMax = ++sel.cpMax;
\r
7141 tr.lpstrText = name;
\r
7142 if (SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr) < 1) break;
\r
7143 } while (isalpha(name[0]) || isdigit(name[0]) || name[0] == '-');
\r
7146 if (sel.cpMax == sel.cpMin || sel.cpMax - sel.cpMin > MSG_SIZ/2) {
\r
7147 MessageBeep(MB_ICONEXCLAMATION);
\r
7151 tr.lpstrText = name;
\r
7152 SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr);
\r
7154 if (sel.cpMax - sel.cpMin > MSG_SIZ/2) {
\r
7155 MessageBeep(MB_ICONEXCLAMATION);
\r
7158 SendMessage(hwnd, EM_GETSELTEXT, 0, (LPARAM) name);
\r
7161 if(strstr(command, "%s")) snprintf(buf, MSG_SIZ, command, name); else
\r
7162 snprintf(buf, MSG_SIZ, "%s %s", command, name);
\r
7163 SetWindowText(hInput, buf);
\r
7164 SendMessage(hInput, WM_CHAR, '\r', 0);
\r
7166 if(!strcmp(command, "chat")) { ChatPopUp(name); return; }
\r
7167 snprintf(buf, MSG_SIZ, "%s %s ", command, name); /* trailing space */
\r
7168 SetWindowText(hInput, buf);
\r
7169 sel.cpMin = 999999;
\r
7170 sel.cpMax = 999999;
\r
7171 SendMessage(hInput, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
7177 ConsoleTextSubclass(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
\r
7182 switch (message) {
\r
7184 if (!(GetKeyState(VK_CONTROL) & ~1)) break;
\r
7185 if(wParam=='R') return 0;
\r
7188 SendMessage(hwnd, EM_LINESCROLL, 0, -999999);
\r
7191 sel.cpMin = 999999;
\r
7192 sel.cpMax = 999999;
\r
7193 SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
7194 SendMessage(hwnd, EM_SCROLLCARET, 0, 0);
\r
7199 if(wParam != '\022') {
\r
7200 if (wParam == '\t') {
\r
7201 if (GetKeyState(VK_SHIFT) < 0) {
\r
7203 if (IsIconic(hwndMain)) ShowWindow(hwndMain, SW_RESTORE);
\r
7204 if (buttonDesc[0].hwnd) {
\r
7205 SetFocus(buttonDesc[0].hwnd);
\r
7207 SetFocus(hwndMain);
\r
7211 SetFocus(GetDlgItem(hwndConsole, OPT_ConsoleInput));
\r
7214 hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
7215 JAWS_DELETE( SetFocus(hInput); )
\r
7216 SendMessage(hInput, message, wParam, lParam);
\r
7219 } // [HGM] navigate: for Ctrl+R, flow into next case (moved up here) to summon up menu
\r
7221 case WM_RBUTTONDOWN:
\r
7222 if (!(GetKeyState(VK_SHIFT) & ~1)) {
\r
7223 /* Move selection here if it was empty */
\r
7225 pt.x = LOWORD(lParam);
\r
7226 pt.y = HIWORD(lParam);
\r
7227 SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);
\r
7228 if (sel.cpMin == sel.cpMax) {
\r
7229 if(lParam != -1) sel.cpMin = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&pt); /*doc is wrong*/
\r
7230 sel.cpMax = sel.cpMin;
\r
7231 SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
7233 SendMessage(hwnd, EM_HIDESELECTION, FALSE, FALSE);
\r
7234 { // [HGM] chatclick: code moved here from WM_RBUTTONUP case, to have menu appear on down-click
\r
7236 HMENU hmenu = LoadIcsTextMenu(icsTextMenuEntry);
\r
7237 SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);
\r
7238 if (sel.cpMin == sel.cpMax) {
\r
7239 EnableMenuItem(hmenu, IDM_Copy, MF_BYCOMMAND|MF_GRAYED);
\r
7240 EnableMenuItem(hmenu, IDM_QuickPaste, MF_BYCOMMAND|MF_GRAYED);
\r
7242 if (!IsClipboardFormatAvailable(CF_TEXT)) {
\r
7243 EnableMenuItem(hmenu, IDM_Paste, MF_BYCOMMAND|MF_GRAYED);
\r
7245 pt.x = LOWORD(lParam)-30; // [HGM] chatclick: make menu pop up with pointer above upper-right item
\r
7246 pt.y = HIWORD(lParam)-10; // make it appear as if mouse moved there, so it will be selected on up-click
\r
7247 PostMessage(hwnd, WM_MOUSEMOVE, wParam, lParam+5);
\r
7248 MenuPopup(hwnd, pt, hmenu, -1);
\r
7252 case WM_RBUTTONUP:
\r
7253 if (GetKeyState(VK_SHIFT) & ~1) {
\r
7254 SendDlgItemMessage(hwndConsole, OPT_ConsoleText,
\r
7255 WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);
\r
7259 hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
7261 return SendMessage(hInput, message, wParam, lParam);
\r
7262 case WM_MBUTTONDOWN:
\r
7263 return SendMessage(hwnd, WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);
\r
7265 switch (LOWORD(wParam)) {
\r
7266 case IDM_QuickPaste:
\r
7268 SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);
\r
7269 if (sel.cpMin == sel.cpMax) {
\r
7270 MessageBeep(MB_ICONEXCLAMATION);
\r
7273 SendMessage(hwnd, WM_COPY, 0, 0);
\r
7274 hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
7275 SendMessage(hInput, WM_PASTE, 0, 0);
\r
7280 SendMessage(hwnd, WM_CUT, 0, 0);
\r
7283 SendMessage(hwnd, WM_PASTE, 0, 0);
\r
7286 SendMessage(hwnd, WM_COPY, 0, 0);
\r
7290 int i = LOWORD(wParam) - IDM_CommandX;
\r
7291 if (i >= 0 && i < ICS_TEXT_MENU_SIZE &&
\r
7292 icsTextMenuEntry[i].command != NULL) {
\r
7293 CommandX(hwnd, icsTextMenuEntry[i].command,
\r
7294 icsTextMenuEntry[i].getname,
\r
7295 icsTextMenuEntry[i].immediate);
\r
7303 return (*consoleTextWindowProc)(hwnd, message, wParam, lParam);
\r
7306 WNDPROC consoleInputWindowProc;
\r
7309 ConsoleInputSubclass(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
\r
7311 char buf[MSG_SIZ];
\r
7313 static BOOL sendNextChar = FALSE;
\r
7314 static BOOL quoteNextChar = FALSE;
\r
7315 InputSource *is = consoleInputSource;
\r
7319 switch (message) {
\r
7321 if (!appData.localLineEditing || sendNextChar) {
\r
7322 is->buf[0] = (CHAR) wParam;
\r
7324 SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);
\r
7325 sendNextChar = FALSE;
\r
7328 if (quoteNextChar) {
\r
7329 buf[0] = (char) wParam;
\r
7330 buf[1] = NULLCHAR;
\r
7331 SendMessage(hwnd, EM_REPLACESEL, TRUE, (LPARAM) buf);
\r
7332 quoteNextChar = FALSE;
\r
7336 case '\r': /* Enter key */
\r
7337 is->count = GetWindowText(hwnd, is->buf, INPUT_SOURCE_BUF_SIZE-1);
\r
7338 if (consoleEcho) SaveInHistory(is->buf);
\r
7339 is->buf[is->count++] = '\n';
\r
7340 is->buf[is->count] = NULLCHAR;
\r
7341 SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);
\r
7342 if (consoleEcho) {
\r
7343 ConsoleOutput(is->buf, is->count, TRUE);
\r
7344 } else if (appData.localLineEditing) {
\r
7345 ConsoleOutput("\n", 1, TRUE);
\r
7348 case '\033': /* Escape key */
\r
7349 SetWindowText(hwnd, "");
\r
7350 cf.cbSize = sizeof(CHARFORMAT);
\r
7351 cf.dwMask = CFM_COLOR|CFM_BOLD|CFM_ITALIC|CFM_UNDERLINE|CFM_STRIKEOUT;
\r
7352 if (consoleEcho) {
\r
7353 cf.crTextColor = textAttribs[ColorNormal].color;
\r
7355 cf.crTextColor = COLOR_ECHOOFF;
\r
7357 cf.dwEffects = textAttribs[ColorNormal].effects;
\r
7358 SendMessage(hwnd, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf);
\r
7360 case '\t': /* Tab key */
\r
7361 if (GetKeyState(VK_SHIFT) < 0) {
\r
7363 SetFocus(GetDlgItem(hwndConsole, OPT_ConsoleText));
\r
7366 if (IsIconic(hwndMain)) ShowWindow(hwndMain, SW_RESTORE);
\r
7367 if (buttonDesc[0].hwnd) {
\r
7368 SetFocus(buttonDesc[0].hwnd);
\r
7370 SetFocus(hwndMain);
\r
7374 case '\023': /* Ctrl+S */
\r
7375 sendNextChar = TRUE;
\r
7377 case '\021': /* Ctrl+Q */
\r
7378 quoteNextChar = TRUE;
\r
7388 GetWindowText(hwnd, buf, MSG_SIZ);
\r
7389 p = PrevInHistory(buf);
\r
7391 SetWindowText(hwnd, p);
\r
7392 sel.cpMin = 999999;
\r
7393 sel.cpMax = 999999;
\r
7394 SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
7399 p = NextInHistory();
\r
7401 SetWindowText(hwnd, p);
\r
7402 sel.cpMin = 999999;
\r
7403 sel.cpMax = 999999;
\r
7404 SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
7410 if (!(GetKeyState(VK_CONTROL) & ~1)) break;
\r
7414 SendDlgItemMessage(hwndConsole, OPT_ConsoleText, message, wParam, lParam);
\r
7418 case WM_MBUTTONDOWN:
\r
7419 SendDlgItemMessage(hwndConsole, OPT_ConsoleText,
\r
7420 WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);
\r
7422 case WM_RBUTTONUP:
\r
7423 if (GetKeyState(VK_SHIFT) & ~1) {
\r
7424 SendDlgItemMessage(hwndConsole, OPT_ConsoleText,
\r
7425 WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);
\r
7429 hmenu = LoadMenu(hInst, "InputMenu");
\r
7430 SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);
\r
7431 if (sel.cpMin == sel.cpMax) {
\r
7432 EnableMenuItem(hmenu, IDM_Copy, MF_BYCOMMAND|MF_GRAYED);
\r
7433 EnableMenuItem(hmenu, IDM_Cut, MF_BYCOMMAND|MF_GRAYED);
\r
7435 if (!IsClipboardFormatAvailable(CF_TEXT)) {
\r
7436 EnableMenuItem(hmenu, IDM_Paste, MF_BYCOMMAND|MF_GRAYED);
\r
7438 pt.x = LOWORD(lParam);
\r
7439 pt.y = HIWORD(lParam);
\r
7440 MenuPopup(hwnd, pt, hmenu, -1);
\r
7444 switch (LOWORD(wParam)) {
\r
7446 SendMessage(hwnd, EM_UNDO, 0, 0);
\r
7448 case IDM_SelectAll:
\r
7450 sel.cpMax = -1; /*999999?*/
\r
7451 SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
7454 SendMessage(hwnd, WM_CUT, 0, 0);
\r
7457 SendMessage(hwnd, WM_PASTE, 0, 0);
\r
7460 SendMessage(hwnd, WM_COPY, 0, 0);
\r
7465 return (*consoleInputWindowProc)(hwnd, message, wParam, lParam);
\r
7468 #define CO_MAX 100000
\r
7469 #define CO_TRIM 1000
\r
7472 ConsoleWndProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
7474 static SnapData sd;
\r
7475 HWND hText, hInput;
\r
7477 static int sizeX, sizeY;
\r
7478 int newSizeX, newSizeY;
\r
7482 hText = GetDlgItem(hDlg, OPT_ConsoleText);
\r
7483 hInput = GetDlgItem(hDlg, OPT_ConsoleInput);
\r
7485 switch (message) {
\r
7487 if (((NMHDR*)lParam)->code == EN_LINK)
\r
7489 ENLINK *pLink = (ENLINK*)lParam;
\r
7490 if (pLink->msg == WM_LBUTTONUP)
\r
7494 tr.chrg = pLink->chrg;
\r
7495 tr.lpstrText = malloc(1+tr.chrg.cpMax-tr.chrg.cpMin);
\r
7496 SendMessage(hText, EM_GETTEXTRANGE, 0, (LPARAM)&tr);
\r
7497 ShellExecute(NULL, "open", tr.lpstrText, NULL, NULL, SW_SHOW);
\r
7498 free(tr.lpstrText);
\r
7502 case WM_INITDIALOG: /* message: initialize dialog box */
\r
7503 hwndConsole = hDlg;
\r
7505 consoleTextWindowProc = (WNDPROC)
\r
7506 SetWindowLongPtr(hText, GWLP_WNDPROC, (LONG_PTR) ConsoleTextSubclass);
\r
7507 SendMessage(hText, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);
\r
7508 consoleInputWindowProc = (WNDPROC)
\r
7509 SetWindowLongPtr(hInput, GWLP_WNDPROC, (LONG_PTR) ConsoleInputSubclass);
\r
7510 SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);
\r
7511 Colorize(ColorNormal, TRUE);
\r
7512 SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &consoleCF);
\r
7513 ChangedConsoleFont();
\r
7514 GetClientRect(hDlg, &rect);
\r
7515 sizeX = rect.right;
\r
7516 sizeY = rect.bottom;
\r
7517 if (wpConsole.x != CW_USEDEFAULT && wpConsole.y != CW_USEDEFAULT &&
\r
7518 wpConsole.width != CW_USEDEFAULT && wpConsole.height != CW_USEDEFAULT) {
\r
7519 WINDOWPLACEMENT wp;
\r
7520 EnsureOnScreen(&wpConsole.x, &wpConsole.y, 0, 0);
\r
7521 wp.length = sizeof(WINDOWPLACEMENT);
\r
7523 wp.showCmd = SW_SHOW;
\r
7524 wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;
\r
7525 wp.rcNormalPosition.left = wpConsole.x;
\r
7526 wp.rcNormalPosition.right = wpConsole.x + wpConsole.width;
\r
7527 wp.rcNormalPosition.top = wpConsole.y;
\r
7528 wp.rcNormalPosition.bottom = wpConsole.y + wpConsole.height;
\r
7529 SetWindowPlacement(hDlg, &wp);
\r
7532 // [HGM] Chessknight's change 2004-07-13
\r
7533 else { /* Determine Defaults */
\r
7534 WINDOWPLACEMENT wp;
\r
7535 wpConsole.x = wpMain.width + 1;
\r
7536 wpConsole.y = wpMain.y;
\r
7537 wpConsole.width = screenWidth - wpMain.width;
\r
7538 wpConsole.height = wpMain.height;
\r
7539 EnsureOnScreen(&wpConsole.x, &wpConsole.y, 0, 0);
\r
7540 wp.length = sizeof(WINDOWPLACEMENT);
\r
7542 wp.showCmd = SW_SHOW;
\r
7543 wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;
\r
7544 wp.rcNormalPosition.left = wpConsole.x;
\r
7545 wp.rcNormalPosition.right = wpConsole.x + wpConsole.width;
\r
7546 wp.rcNormalPosition.top = wpConsole.y;
\r
7547 wp.rcNormalPosition.bottom = wpConsole.y + wpConsole.height;
\r
7548 SetWindowPlacement(hDlg, &wp);
\r
7551 // Allow hText to highlight URLs and send notifications on them
\r
7552 wMask = (WORD) SendMessage(hText, EM_GETEVENTMASK, 0, 0L);
\r
7553 SendMessage(hText, EM_SETEVENTMASK, 0, wMask | ENM_LINK);
\r
7554 SendMessage(hText, EM_AUTOURLDETECT, TRUE, 0L);
\r
7555 SetWindowLongPtr(hText, GWLP_USERDATA, 79); // initialize the text window's width
\r
7569 if (IsIconic(hDlg)) break;
\r
7570 newSizeX = LOWORD(lParam);
\r
7571 newSizeY = HIWORD(lParam);
\r
7572 if (sizeX != newSizeX || sizeY != newSizeY) {
\r
7573 RECT rectText, rectInput;
\r
7575 int newTextHeight, newTextWidth;
\r
7576 GetWindowRect(hText, &rectText);
\r
7577 newTextWidth = rectText.right - rectText.left + newSizeX - sizeX;
\r
7578 newTextHeight = rectText.bottom - rectText.top + newSizeY - sizeY;
\r
7579 if (newTextHeight < 0) {
\r
7580 newSizeY += -newTextHeight;
\r
7581 newTextHeight = 0;
\r
7583 SetWindowPos(hText, NULL, 0, 0,
\r
7584 newTextWidth, newTextHeight, SWP_NOZORDER|SWP_NOMOVE);
\r
7585 GetWindowRect(hInput, &rectInput); /* gives screen coords */
\r
7586 pt.x = rectInput.left;
\r
7587 pt.y = rectInput.top + newSizeY - sizeY;
\r
7588 ScreenToClient(hDlg, &pt);
\r
7589 SetWindowPos(hInput, NULL,
\r
7590 pt.x, pt.y, /* needs client coords */
\r
7591 rectInput.right - rectInput.left + newSizeX - sizeX,
\r
7592 rectInput.bottom - rectInput.top, SWP_NOZORDER);
\r
7598 case WM_GETMINMAXINFO:
\r
7599 /* Prevent resizing window too small */
\r
7600 mmi = (MINMAXINFO *) lParam;
\r
7601 mmi->ptMinTrackSize.x = 100;
\r
7602 mmi->ptMinTrackSize.y = 100;
\r
7605 /* [AS] Snapping */
\r
7606 case WM_ENTERSIZEMOVE:
\r
7607 return OnEnterSizeMove( &sd, hDlg, wParam, lParam );
\r
7610 return OnSizing( &sd, hDlg, wParam, lParam );
\r
7613 return OnMoving( &sd, hDlg, wParam, lParam );
\r
7615 case WM_EXITSIZEMOVE:
\r
7616 UpdateICSWidth(hText);
\r
7617 return OnExitSizeMove( &sd, hDlg, wParam, lParam );
\r
7620 return DefWindowProc(hDlg, message, wParam, lParam);
\r
7628 if (hwndConsole) return;
\r
7629 hCons = CreateDialog(hInst, szConsoleName, 0, NULL);
\r
7630 SendMessage(hCons, WM_INITDIALOG, 0, 0);
\r
7635 ConsoleOutput(char* data, int length, int forceVisible)
\r
7640 char buf[CO_MAX+1];
\r
7643 static int delayLF = 0;
\r
7644 CHARRANGE savesel, sel;
\r
7646 if (hwndConsole == NULL || length > CO_MAX-100 || length == 0) return;
\r
7654 while (length--) {
\r
7662 } else if (*p == '\007') {
\r
7663 MyPlaySound(&sounds[(int)SoundBell]);
\r
7670 hText = GetDlgItem(hwndConsole, OPT_ConsoleText);
\r
7671 SendMessage(hText, EM_HIDESELECTION, TRUE, FALSE);
\r
7672 /* Save current selection */
\r
7673 SendMessage(hText, EM_EXGETSEL, 0, (LPARAM)&savesel);
\r
7674 exlen = GetWindowTextLength(hText);
\r
7675 /* Find out whether current end of text is visible */
\r
7676 SendMessage(hText, EM_GETRECT, 0, (LPARAM) &rect);
\r
7677 SendMessage(hText, EM_POSFROMCHAR, (WPARAM) &pEnd, exlen);
\r
7678 /* Trim existing text if it's too long */
\r
7679 if (exlen + (q - buf) > CO_MAX) {
\r
7680 trim = (CO_TRIM > (q - buf)) ? CO_TRIM : (q - buf);
\r
7683 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
7684 SendMessage(hText, EM_REPLACESEL, 0, (LPARAM)"");
\r
7686 savesel.cpMin -= trim;
\r
7687 savesel.cpMax -= trim;
\r
7688 if (exlen < 0) exlen = 0;
\r
7689 if (savesel.cpMin < 0) savesel.cpMin = 0;
\r
7690 if (savesel.cpMax < savesel.cpMin) savesel.cpMax = savesel.cpMin;
\r
7692 /* Append the new text */
\r
7693 sel.cpMin = exlen;
\r
7694 sel.cpMax = exlen;
\r
7695 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
7696 SendMessage(hText, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&consoleCF);
\r
7697 SendMessage(hText, EM_REPLACESEL, 0, (LPARAM) buf);
\r
7698 if (forceVisible || exlen == 0 ||
\r
7699 (rect.left <= pEnd.x && pEnd.x < rect.right &&
\r
7700 rect.top <= pEnd.y && pEnd.y < rect.bottom)) {
\r
7701 /* Scroll to make new end of text visible if old end of text
\r
7702 was visible or new text is an echo of user typein */
\r
7703 sel.cpMin = 9999999;
\r
7704 sel.cpMax = 9999999;
\r
7705 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
7706 SendMessage(hText, EM_HIDESELECTION, FALSE, FALSE);
\r
7707 SendMessage(hText, EM_SCROLLCARET, 0, 0);
\r
7708 SendMessage(hText, EM_HIDESELECTION, TRUE, FALSE);
\r
7710 if (savesel.cpMax == exlen || forceVisible) {
\r
7711 /* Move insert point to new end of text if it was at the old
\r
7712 end of text or if the new text is an echo of user typein */
\r
7713 sel.cpMin = 9999999;
\r
7714 sel.cpMax = 9999999;
\r
7715 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
7717 /* Restore previous selection */
\r
7718 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&savesel);
\r
7720 SendMessage(hText, EM_HIDESELECTION, FALSE, FALSE);
\r
7727 DisplayHoldingsCount(HDC hdc, int x, int y, int rightAlign, int copyNumber)
\r
7731 COLORREF oldFg, oldBg;
\r
7735 if(copyNumber > 1)
\r
7736 snprintf(buf, sizeof(buf)/sizeof(buf[0]),"%d", copyNumber); else buf[0] = 0;
\r
7738 oldFg = SetTextColor(hdc, RGB(255, 255, 255)); /* white */
\r
7739 oldBg = SetBkColor(hdc, RGB(0, 0, 0)); /* black */
\r
7740 oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);
\r
7743 rect.right = x + squareSize;
\r
7745 rect.bottom = y + squareSize;
\r
7748 ExtTextOut(hdc, x + MESSAGE_LINE_LEFTMARGIN
\r
7749 + (rightAlign ? (squareSize*2)/3 : 0),
\r
7750 y, ETO_CLIPPED|ETO_OPAQUE,
\r
7751 &rect, str, strlen(str), NULL);
\r
7753 (void) SetTextColor(hdc, oldFg);
\r
7754 (void) SetBkColor(hdc, oldBg);
\r
7755 (void) SelectObject(hdc, oldFont);
\r
7759 DisplayAClock(HDC hdc, int timeRemaining, int highlight,
\r
7760 RECT *rect, char *color, char *flagFell)
\r
7764 COLORREF oldFg, oldBg;
\r
7767 if (twoBoards && partnerUp) return;
\r
7768 if (appData.clockMode) {
\r
7769 if (tinyLayout == 2)
\r
7770 snprintf(buf, sizeof(buf)/sizeof(buf[0]), "%c %s %s", color[0], TimeString(timeRemaining), flagFell);
\r
7772 snprintf(buf, sizeof(buf)/sizeof(buf[0]), "%s:%c%s %s", color, (logoHeight>0 ? 0 : ' '), TimeString(timeRemaining), flagFell);
\r
7779 oldFg = SetTextColor(hdc, RGB(255, 255, 255)); /* white */
\r
7780 oldBg = SetBkColor(hdc, RGB(0, 0, 0)); /* black */
\r
7782 oldFg = SetTextColor(hdc, RGB(0, 0, 0)); /* black */
\r
7783 oldBg = SetBkColor(hdc, RGB(255, 255, 255)); /* white */
\r
7786 oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);
\r
7790 ExtTextOut(hdc, rect->left + MESSAGE_LINE_LEFTMARGIN,
\r
7791 rect->top, ETO_CLIPPED|ETO_OPAQUE,
\r
7792 rect, str, strlen(str), NULL);
\r
7793 if(logoHeight > 0 && appData.clockMode) {
\r
7795 str += strlen(color)+2;
\r
7796 r.top = rect->top + logoHeight/2;
\r
7797 r.left = rect->left;
\r
7798 r.right = rect->right;
\r
7799 r.bottom = rect->bottom;
\r
7800 ExtTextOut(hdc, rect->left + MESSAGE_LINE_LEFTMARGIN,
\r
7801 r.top, ETO_CLIPPED|ETO_OPAQUE,
\r
7802 &r, str, strlen(str), NULL);
\r
7804 (void) SetTextColor(hdc, oldFg);
\r
7805 (void) SetBkColor(hdc, oldBg);
\r
7806 (void) SelectObject(hdc, oldFont);
\r
7811 DoReadFile(HANDLE hFile, char *buf, int count, DWORD *outCount,
\r
7817 if( count <= 0 ) {
\r
7818 if (appData.debugMode) {
\r
7819 fprintf( debugFP, "DoReadFile: trying to read past end of buffer, overflow = %d\n", count );
\r
7822 return ERROR_INVALID_USER_BUFFER;
\r
7825 ResetEvent(ovl->hEvent);
\r
7826 ovl->Offset = ovl->OffsetHigh = 0;
\r
7827 ok = ReadFile(hFile, buf, count, outCount, ovl);
\r
7831 err = GetLastError();
\r
7832 if (err == ERROR_IO_PENDING) {
\r
7833 ok = GetOverlappedResult(hFile, ovl, outCount, TRUE);
\r
7837 err = GetLastError();
\r
7844 DoWriteFile(HANDLE hFile, char *buf, int count, DWORD *outCount,
\r
7849 ResetEvent(ovl->hEvent);
\r
7850 ovl->Offset = ovl->OffsetHigh = 0;
\r
7851 ok = WriteFile(hFile, buf, count, outCount, ovl);
\r
7855 err = GetLastError();
\r
7856 if (err == ERROR_IO_PENDING) {
\r
7857 ok = GetOverlappedResult(hFile, ovl, outCount, TRUE);
\r
7861 err = GetLastError();
\r
7868 /* [AS] If input is line by line and a line exceed the buffer size, force an error */
\r
7869 void CheckForInputBufferFull( InputSource * is )
\r
7871 if( is->lineByLine && (is->next - is->buf) >= INPUT_SOURCE_BUF_SIZE ) {
\r
7872 /* Look for end of line */
\r
7873 char * p = is->buf;
\r
7875 while( p < is->next && *p != '\n' ) {
\r
7879 if( p >= is->next ) {
\r
7880 if (appData.debugMode) {
\r
7881 fprintf( debugFP, "Input line exceeded buffer size (source id=%lu)\n", is->id );
\r
7884 is->error = ERROR_BROKEN_PIPE; /* [AS] Just any non-successful code! */
\r
7885 is->count = (DWORD) -1;
\r
7886 is->next = is->buf;
\r
7892 InputThread(LPVOID arg)
\r
7897 is = (InputSource *) arg;
\r
7898 ovl.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
\r
7899 ovl.Internal = ovl.InternalHigh = ovl.Offset = ovl.OffsetHigh = 0;
\r
7900 while (is->hThread != NULL) {
\r
7901 is->error = DoReadFile(is->hFile, is->next,
\r
7902 INPUT_SOURCE_BUF_SIZE - (is->next - is->buf),
\r
7903 &is->count, &ovl);
\r
7904 if (is->error == NO_ERROR) {
\r
7905 is->next += is->count;
\r
7907 if (is->error == ERROR_BROKEN_PIPE) {
\r
7908 /* Correct for MS brain damage. EOF reading a pipe is not an error. */
\r
7911 is->count = (DWORD) -1;
\r
7912 /* [AS] The (is->count <= 0) check below is not useful for unsigned values! */
\r
7917 CheckForInputBufferFull( is );
\r
7919 SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);
\r
7921 if( is->count == ((DWORD) -1) ) break; /* [AS] */
\r
7923 if (is->count <= 0) break; /* Quit on EOF or error */
\r
7926 CloseHandle(ovl.hEvent);
\r
7927 CloseHandle(is->hFile);
\r
7929 if (appData.debugMode) {
\r
7930 fprintf( debugFP, "Input thread terminated (id=%lu, error=%d, count=%ld)\n", is->id, is->error, is->count );
\r
7937 /* Windows 95 beta 2 won't let you do overlapped i/o on a console or pipe */
\r
7939 NonOvlInputThread(LPVOID arg)
\r
7946 is = (InputSource *) arg;
\r
7947 while (is->hThread != NULL) {
\r
7948 is->error = ReadFile(is->hFile, is->next,
\r
7949 INPUT_SOURCE_BUF_SIZE - (is->next - is->buf),
\r
7950 &is->count, NULL) ? NO_ERROR : GetLastError();
\r
7951 if (is->error == NO_ERROR) {
\r
7952 /* Change CRLF to LF */
\r
7953 if (is->next > is->buf) {
\r
7955 i = is->count + 1;
\r
7963 if (prev == '\r' && *p == '\n') {
\r
7975 if (is->error == ERROR_BROKEN_PIPE) {
\r
7976 /* Correct for MS brain damage. EOF reading a pipe is not an error. */
\r
7979 is->count = (DWORD) -1;
\r
7983 CheckForInputBufferFull( is );
\r
7985 SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);
\r
7987 if( is->count == ((DWORD) -1) ) break; /* [AS] */
\r
7989 if (is->count < 0) break; /* Quit on error */
\r
7991 CloseHandle(is->hFile);
\r
7996 SocketInputThread(LPVOID arg)
\r
8000 is = (InputSource *) arg;
\r
8001 while (is->hThread != NULL) {
\r
8002 is->count = recv(is->sock, is->buf, INPUT_SOURCE_BUF_SIZE, 0);
\r
8003 if ((int)is->count == SOCKET_ERROR) {
\r
8004 is->count = (DWORD) -1;
\r
8005 is->error = WSAGetLastError();
\r
8007 is->error = NO_ERROR;
\r
8008 is->next += is->count;
\r
8009 if (is->count == 0 && is->second == is) {
\r
8010 /* End of file on stderr; quit with no message */
\r
8014 SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);
\r
8016 if( is->count == ((DWORD) -1) ) break; /* [AS] */
\r
8018 if (is->count <= 0) break; /* Quit on EOF or error */
\r
8024 InputEvent(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
\r
8028 is = (InputSource *) lParam;
\r
8029 if (is->lineByLine) {
\r
8030 /* Feed in lines one by one */
\r
8031 char *p = is->buf;
\r
8033 while (q < is->next) {
\r
8034 if (*q++ == '\n') {
\r
8035 (is->func)(is, is->closure, p, q - p, NO_ERROR);
\r
8040 /* Move any partial line to the start of the buffer */
\r
8042 while (p < is->next) {
\r
8047 if (is->error != NO_ERROR || is->count == 0) {
\r
8048 /* Notify backend of the error. Note: If there was a partial
\r
8049 line at the end, it is not flushed through. */
\r
8050 (is->func)(is, is->closure, is->buf, is->count, is->error);
\r
8053 /* Feed in the whole chunk of input at once */
\r
8054 (is->func)(is, is->closure, is->buf, is->count, is->error);
\r
8055 is->next = is->buf;
\r
8059 /*---------------------------------------------------------------------------*\
\r
8061 * Menu enables. Used when setting various modes.
\r
8063 \*---------------------------------------------------------------------------*/
\r
8071 GreyRevert(Boolean grey)
\r
8072 { // [HGM] vari: for retracting variations in local mode
\r
8073 HMENU hmenu = GetMenu(hwndMain);
\r
8074 EnableMenuItem(hmenu, IDM_Revert, MF_BYCOMMAND|(grey ? MF_GRAYED : MF_ENABLED));
\r
8075 EnableMenuItem(hmenu, IDM_Annotate, MF_BYCOMMAND|(grey ? MF_GRAYED : MF_ENABLED));
\r
8079 SetMenuEnables(HMENU hmenu, Enables *enab)
\r
8081 while (enab->item > 0) {
\r
8082 (void) EnableMenuItem(hmenu, enab->item, enab->flags);
\r
8087 Enables gnuEnables[] = {
\r
8088 { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },
\r
8089 { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },
\r
8090 { IDM_IcsClient, MF_BYCOMMAND|MF_GRAYED },
\r
8091 { IDM_Accept, MF_BYCOMMAND|MF_GRAYED },
\r
8092 { IDM_Decline, MF_BYCOMMAND|MF_GRAYED },
\r
8093 { IDM_Rematch, MF_BYCOMMAND|MF_GRAYED },
\r
8094 { IDM_Adjourn, MF_BYCOMMAND|MF_GRAYED },
\r
8095 { IDM_StopExamining, MF_BYCOMMAND|MF_GRAYED },
\r
8096 { IDM_StopObserving, MF_BYCOMMAND|MF_GRAYED },
\r
8097 { IDM_Upload, MF_BYCOMMAND|MF_GRAYED },
\r
8098 { IDM_Revert, MF_BYCOMMAND|MF_GRAYED },
\r
8099 { IDM_Annotate, MF_BYCOMMAND|MF_GRAYED },
\r
8100 { IDM_NewChat, MF_BYCOMMAND|MF_GRAYED },
\r
8103 // Needed to switch from ncp to GNU mode on Engine Load
\r
8104 { ACTION_POS, MF_BYPOSITION|MF_ENABLED },
\r
8105 { IDM_MachineWhite, MF_BYCOMMAND|MF_ENABLED },
\r
8106 { IDM_MachineBlack, MF_BYCOMMAND|MF_ENABLED },
\r
8107 { IDM_TwoMachines, MF_BYCOMMAND|MF_ENABLED },
\r
8108 { IDM_Match, MF_BYCOMMAND|MF_ENABLED },
\r
8109 { IDM_AnalysisMode, MF_BYCOMMAND|MF_ENABLED },
\r
8110 { IDM_AnalyzeFile, MF_BYCOMMAND|MF_ENABLED },
\r
8111 { IDM_Engine1Options, MF_BYCOMMAND|MF_ENABLED },
\r
8112 { IDM_Engine2Options, MF_BYCOMMAND|MF_ENABLED },
\r
8113 { IDM_TimeControl, MF_BYCOMMAND|MF_ENABLED },
\r
8114 { IDM_RetractMove, MF_BYCOMMAND|MF_ENABLED },
\r
8115 { IDM_MoveNow, MF_BYCOMMAND|MF_ENABLED },
\r
8116 { IDM_Hint, MF_BYCOMMAND|MF_ENABLED },
\r
8117 { IDM_Book, MF_BYCOMMAND|MF_ENABLED },
\r
8121 Enables icsEnables[] = {
\r
8122 { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },
\r
8123 { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },
\r
8124 { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },
\r
8125 { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },
\r
8126 { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },
\r
8127 { IDM_Match, MF_BYCOMMAND|MF_GRAYED },
\r
8128 { IDM_MachineBoth, MF_BYCOMMAND|MF_GRAYED },
\r
8129 { IDM_AnalysisMode, MF_BYCOMMAND|MF_ENABLED },
\r
8130 { IDM_AnalyzeFile, MF_BYCOMMAND|MF_GRAYED },
\r
8131 { IDM_TimeControl, MF_BYCOMMAND|MF_GRAYED },
\r
8132 { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },
\r
8133 { IDM_Hint, MF_BYCOMMAND|MF_GRAYED },
\r
8134 { IDM_Book, MF_BYCOMMAND|MF_GRAYED },
\r
8135 { IDM_LoadProg1, MF_BYCOMMAND|MF_GRAYED },
\r
8136 { IDM_LoadProg2, MF_BYCOMMAND|MF_GRAYED },
\r
8137 { IDM_IcsOptions, MF_BYCOMMAND|MF_ENABLED },
\r
8138 { IDM_Engine1Options, MF_BYCOMMAND|MF_GRAYED },
\r
8139 { IDM_Engine2Options, MF_BYCOMMAND|MF_GRAYED },
\r
8140 { IDM_Annotate, MF_BYCOMMAND|MF_GRAYED },
\r
8141 { IDM_Tourney, MF_BYCOMMAND|MF_GRAYED },
\r
8146 Enables zippyEnables[] = {
\r
8147 { IDM_MoveNow, MF_BYCOMMAND|MF_ENABLED },
\r
8148 { IDM_Hint, MF_BYCOMMAND|MF_ENABLED },
\r
8149 { IDM_Book, MF_BYCOMMAND|MF_ENABLED },
\r
8150 { IDM_Engine1Options, MF_BYCOMMAND|MF_ENABLED },
\r
8155 Enables ncpEnables[] = {
\r
8156 { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },
\r
8157 { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },
\r
8158 { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },
\r
8159 { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },
\r
8160 { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },
\r
8161 { IDM_Match, MF_BYCOMMAND|MF_GRAYED },
\r
8162 { IDM_AnalysisMode, MF_BYCOMMAND|MF_GRAYED },
\r
8163 { IDM_AnalyzeFile, MF_BYCOMMAND|MF_GRAYED },
\r
8164 { IDM_IcsClient, MF_BYCOMMAND|MF_GRAYED },
\r
8165 { ACTION_POS, MF_BYPOSITION|MF_GRAYED },
\r
8166 { IDM_Revert, MF_BYCOMMAND|MF_GRAYED },
\r
8167 { IDM_Annotate, MF_BYCOMMAND|MF_GRAYED },
\r
8168 { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },
\r
8169 { IDM_RetractMove, MF_BYCOMMAND|MF_GRAYED },
\r
8170 { IDM_TimeControl, MF_BYCOMMAND|MF_GRAYED },
\r
8171 { IDM_Hint, MF_BYCOMMAND|MF_GRAYED },
\r
8172 { IDM_Book, MF_BYCOMMAND|MF_GRAYED },
\r
8173 { IDM_MachineBoth, MF_BYCOMMAND|MF_GRAYED },
\r
8174 { IDM_NewChat, MF_BYCOMMAND|MF_GRAYED },
\r
8175 { IDM_Engine1Options, MF_BYCOMMAND|MF_GRAYED },
\r
8176 { IDM_Engine2Options, MF_BYCOMMAND|MF_GRAYED },
\r
8177 { IDM_Sounds, MF_BYCOMMAND|MF_GRAYED },
\r
8181 Enables trainingOnEnables[] = {
\r
8182 { IDM_EditComment, MF_BYCOMMAND|MF_GRAYED },
\r
8183 { IDM_Comment, MF_BYCOMMAND|MF_GRAYED },
\r
8184 { IDM_Pause, MF_BYCOMMAND|MF_GRAYED },
\r
8185 { IDM_Forward, MF_BYCOMMAND|MF_GRAYED },
\r
8186 { IDM_Backward, MF_BYCOMMAND|MF_GRAYED },
\r
8187 { IDM_ToEnd, MF_BYCOMMAND|MF_GRAYED },
\r
8188 { IDM_ToStart, MF_BYCOMMAND|MF_GRAYED },
\r
8189 { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },
\r
8190 { IDM_TruncateGame, MF_BYCOMMAND|MF_GRAYED },
\r
8194 Enables trainingOffEnables[] = {
\r
8195 { IDM_EditComment, MF_BYCOMMAND|MF_ENABLED },
\r
8196 { IDM_Comment, MF_BYCOMMAND|MF_ENABLED },
\r
8197 { IDM_Pause, MF_BYCOMMAND|MF_ENABLED },
\r
8198 { IDM_Forward, MF_BYCOMMAND|MF_ENABLED },
\r
8199 { IDM_Backward, MF_BYCOMMAND|MF_ENABLED },
\r
8200 { IDM_ToEnd, MF_BYCOMMAND|MF_ENABLED },
\r
8201 { IDM_ToStart, MF_BYCOMMAND|MF_ENABLED },
\r
8202 { IDM_MoveNow, MF_BYCOMMAND|MF_ENABLED },
\r
8203 { IDM_TruncateGame, MF_BYCOMMAND|MF_ENABLED },
\r
8207 /* These modify either ncpEnables or gnuEnables */
\r
8208 Enables cmailEnables[] = {
\r
8209 { IDM_MailMove, MF_BYCOMMAND|MF_ENABLED },
\r
8210 { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_ENABLED },
\r
8211 { ACTION_POS, MF_BYPOSITION|MF_ENABLED },
\r
8212 { IDM_CallFlag, MF_BYCOMMAND|MF_GRAYED },
\r
8213 { IDM_Draw, MF_BYCOMMAND|MF_ENABLED },
\r
8214 { IDM_Adjourn, MF_BYCOMMAND|MF_GRAYED },
\r
8215 { IDM_Abort, MF_BYCOMMAND|MF_GRAYED },
\r
8219 Enables machineThinkingEnables[] = {
\r
8220 { IDM_LoadGame, MF_BYCOMMAND|MF_GRAYED },
\r
8221 { IDM_LoadNextGame, MF_BYCOMMAND|MF_GRAYED },
\r
8222 { IDM_LoadPrevGame, MF_BYCOMMAND|MF_GRAYED },
\r
8223 { IDM_ReloadGame, MF_BYCOMMAND|MF_GRAYED },
\r
8224 { IDM_PasteGame, MF_BYCOMMAND|MF_GRAYED },
\r
8225 { IDM_LoadPosition, MF_BYCOMMAND|MF_GRAYED },
\r
8226 { IDM_LoadNextPosition, MF_BYCOMMAND|MF_GRAYED },
\r
8227 { IDM_LoadPrevPosition, MF_BYCOMMAND|MF_GRAYED },
\r
8228 { IDM_ReloadPosition, MF_BYCOMMAND|MF_GRAYED },
\r
8229 { IDM_PastePosition, MF_BYCOMMAND|MF_GRAYED },
\r
8230 { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },
\r
8231 { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },
\r
8232 { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },
\r
8233 // { IDM_Match, MF_BYCOMMAND|MF_GRAYED },
\r
8234 { IDM_TypeInMove, MF_BYCOMMAND|MF_GRAYED },
\r
8235 { IDM_RetractMove, MF_BYCOMMAND|MF_GRAYED },
\r
8239 Enables userThinkingEnables[] = {
\r
8240 { IDM_LoadGame, MF_BYCOMMAND|MF_ENABLED },
\r
8241 { IDM_LoadNextGame, MF_BYCOMMAND|MF_ENABLED },
\r
8242 { IDM_LoadPrevGame, MF_BYCOMMAND|MF_ENABLED },
\r
8243 { IDM_ReloadGame, MF_BYCOMMAND|MF_ENABLED },
\r
8244 { IDM_PasteGame, MF_BYCOMMAND|MF_ENABLED },
\r
8245 { IDM_LoadPosition, MF_BYCOMMAND|MF_ENABLED },
\r
8246 { IDM_LoadNextPosition, MF_BYCOMMAND|MF_ENABLED },
\r
8247 { IDM_LoadPrevPosition, MF_BYCOMMAND|MF_ENABLED },
\r
8248 { IDM_ReloadPosition, MF_BYCOMMAND|MF_ENABLED },
\r
8249 { IDM_PastePosition, MF_BYCOMMAND|MF_ENABLED },
\r
8250 { IDM_MachineWhite, MF_BYCOMMAND|MF_ENABLED },
\r
8251 { IDM_MachineBlack, MF_BYCOMMAND|MF_ENABLED },
\r
8252 { IDM_TwoMachines, MF_BYCOMMAND|MF_ENABLED },
\r
8253 // { IDM_Match, MF_BYCOMMAND|MF_ENABLED },
\r
8254 { IDM_TypeInMove, MF_BYCOMMAND|MF_ENABLED },
\r
8255 { IDM_RetractMove, MF_BYCOMMAND|MF_ENABLED },
\r
8259 /*---------------------------------------------------------------------------*\
\r
8261 * Front-end interface functions exported by XBoard.
\r
8262 * Functions appear in same order as prototypes in frontend.h.
\r
8264 \*---------------------------------------------------------------------------*/
\r
8266 CheckMark(UINT item, int state)
\r
8268 if(item) CheckMenuItem(GetMenu(hwndMain), item, MF_BYCOMMAND|state);
\r
8274 static UINT prevChecked = 0;
\r
8275 static int prevPausing = 0;
\r
8278 if (pausing != prevPausing) {
\r
8279 prevPausing = pausing;
\r
8280 (void) CheckMenuItem(GetMenu(hwndMain), IDM_Pause,
\r
8281 MF_BYCOMMAND|(pausing ? MF_CHECKED : MF_UNCHECKED));
\r
8282 if (hwndPause) SetWindowText(hwndPause, pausing ? "C" : "P");
\r
8285 switch (gameMode) {
\r
8286 case BeginningOfGame:
\r
8287 if (appData.icsActive)
\r
8288 nowChecked = IDM_IcsClient;
\r
8289 else if (appData.noChessProgram)
\r
8290 nowChecked = IDM_EditGame;
\r
8292 nowChecked = IDM_MachineBlack;
\r
8294 case MachinePlaysBlack:
\r
8295 nowChecked = IDM_MachineBlack;
\r
8297 case MachinePlaysWhite:
\r
8298 nowChecked = IDM_MachineWhite;
\r
8300 case TwoMachinesPlay:
\r
8301 nowChecked = IDM_TwoMachines;
\r
8304 nowChecked = IDM_AnalysisMode;
\r
8307 nowChecked = IDM_AnalyzeFile;
\r
8310 nowChecked = IDM_EditGame;
\r
8312 case PlayFromGameFile:
\r
8313 nowChecked = IDM_LoadGame;
\r
8315 case EditPosition:
\r
8316 nowChecked = IDM_EditPosition;
\r
8319 nowChecked = IDM_Training;
\r
8321 case IcsPlayingWhite:
\r
8322 case IcsPlayingBlack:
\r
8323 case IcsObserving:
\r
8325 nowChecked = IDM_IcsClient;
\r
8332 if(prevChecked == IDM_TwoMachines) // [HGM] 'Machine Match' might have gotten disabled when stopping match
\r
8333 EnableMenuItem(GetMenu(hwndMain), IDM_Match, MF_BYCOMMAND|MF_ENABLED);
\r
8334 CheckMark(prevChecked, MF_UNCHECKED);
\r
8335 CheckMark(nowChecked, MF_CHECKED);
\r
8336 CheckMark(IDM_Match, matchMode && matchGame < appData.matchGames ? MF_CHECKED : MF_UNCHECKED);
\r
8338 if (nowChecked == IDM_LoadGame || nowChecked == IDM_Training) {
\r
8339 (void) EnableMenuItem(GetMenu(hwndMain), IDM_Training,
\r
8340 MF_BYCOMMAND|MF_ENABLED);
\r
8342 (void) EnableMenuItem(GetMenu(hwndMain),
\r
8343 IDM_Training, MF_BYCOMMAND|MF_GRAYED);
\r
8346 prevChecked = nowChecked;
\r
8348 /* [DM] icsEngineAnalyze - Do a sceure check too */
\r
8349 if (appData.icsActive) {
\r
8350 if (appData.icsEngineAnalyze) {
\r
8351 CheckMark(IDM_AnalysisMode, MF_CHECKED);
\r
8353 CheckMark(IDM_AnalysisMode, MF_UNCHECKED);
\r
8356 DisplayLogos(); // [HGM] logos: mode change could have altered logos
\r
8362 HMENU hmenu = GetMenu(hwndMain);
\r
8363 SetMenuEnables(hmenu, icsEnables);
\r
8364 EnableMenuItem(GetSubMenu(hmenu, OPTIONS_POS), IDM_IcsOptions,
\r
8365 MF_BYCOMMAND|MF_ENABLED);
\r
8367 if (appData.zippyPlay) {
\r
8368 SetMenuEnables(hmenu, zippyEnables);
\r
8369 if (!appData.noChessProgram) /* [DM] icsEngineAnalyze */
\r
8370 (void) EnableMenuItem(GetMenu(hwndMain), IDM_AnalysisMode,
\r
8371 MF_BYCOMMAND|MF_ENABLED);
\r
8379 SetMenuEnables(GetMenu(hwndMain), gnuEnables);
\r
8385 HMENU hmenu = GetMenu(hwndMain);
\r
8386 SetMenuEnables(hmenu, ncpEnables);
\r
8387 DrawMenuBar(hwndMain);
\r
8393 SetMenuEnables(GetMenu(hwndMain), cmailEnables);
\r
8397 SetTrainingModeOn()
\r
8400 SetMenuEnables(GetMenu(hwndMain), trainingOnEnables);
\r
8401 for (i = 0; i < N_BUTTONS; i++) {
\r
8402 if (buttonDesc[i].hwnd != NULL)
\r
8403 EnableWindow(buttonDesc[i].hwnd, FALSE);
\r
8408 VOID SetTrainingModeOff()
\r
8411 SetMenuEnables(GetMenu(hwndMain), trainingOffEnables);
\r
8412 for (i = 0; i < N_BUTTONS; i++) {
\r
8413 if (buttonDesc[i].hwnd != NULL)
\r
8414 EnableWindow(buttonDesc[i].hwnd, TRUE);
\r
8420 SetUserThinkingEnables()
\r
8422 SetMenuEnables(GetMenu(hwndMain), userThinkingEnables);
\r
8426 SetMachineThinkingEnables()
\r
8428 HMENU hMenu = GetMenu(hwndMain);
\r
8429 int flags = MF_BYCOMMAND|MF_ENABLED;
\r
8431 SetMenuEnables(hMenu, machineThinkingEnables);
\r
8433 if (gameMode == MachinePlaysBlack) {
\r
8434 (void)EnableMenuItem(hMenu, IDM_MachineBlack, flags);
\r
8435 } else if (gameMode == MachinePlaysWhite) {
\r
8436 (void)EnableMenuItem(hMenu, IDM_MachineWhite, flags);
\r
8437 } else if (gameMode == TwoMachinesPlay) {
\r
8438 (void)EnableMenuItem(hMenu, matchMode ? IDM_Match : IDM_TwoMachines, flags); // [HGM] match
\r
8444 DisplayTitle(char *str)
\r
8446 char title[MSG_SIZ], *host;
\r
8447 if (str[0] != NULLCHAR) {
\r
8448 safeStrCpy(title, str, sizeof(title)/sizeof(title[0]) );
\r
8449 } else if (appData.icsActive) {
\r
8450 if (appData.icsCommPort[0] != NULLCHAR)
\r
8453 host = appData.icsHost;
\r
8454 snprintf(title, MSG_SIZ, "%s: %s", szTitle, host);
\r
8455 } else if (appData.noChessProgram) {
\r
8456 safeStrCpy(title, szTitle, sizeof(title)/sizeof(title[0]) );
\r
8458 safeStrCpy(title, szTitle, sizeof(title)/sizeof(title[0]) );
\r
8459 strcat(title, ": ");
\r
8460 strcat(title, first.tidy);
\r
8462 SetWindowText(hwndMain, title);
\r
8467 DisplayMessage(char *str1, char *str2)
\r
8471 int remain = MESSAGE_TEXT_MAX - 1;
\r
8474 moveErrorMessageUp = FALSE; /* turned on later by caller if needed */
\r
8475 messageText[0] = NULLCHAR;
\r
8477 len = strlen(str1);
\r
8478 if (len > remain) len = remain;
\r
8479 strncpy(messageText, str1, len);
\r
8480 messageText[len] = NULLCHAR;
\r
8483 if (*str2 && remain >= 2) {
\r
8485 strcat(messageText, " ");
\r
8488 len = strlen(str2);
\r
8489 if (len > remain) len = remain;
\r
8490 strncat(messageText, str2, len);
\r
8492 messageText[MESSAGE_TEXT_MAX - 1] = NULLCHAR;
\r
8493 safeStrCpy(lastMsg, messageText, MSG_SIZ);
\r
8495 if (hwndMain == NULL || IsIconic(hwndMain)) return;
\r
8499 hdc = GetDC(hwndMain);
\r
8500 oldFont = SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);
\r
8501 ExtTextOut(hdc, messageRect.left, messageRect.top, ETO_CLIPPED|ETO_OPAQUE,
\r
8502 &messageRect, messageText, strlen(messageText), NULL);
\r
8503 (void) SelectObject(hdc, oldFont);
\r
8504 (void) ReleaseDC(hwndMain, hdc);
\r
8508 DisplayError(char *str, int error)
\r
8510 char buf[MSG_SIZ*2], buf2[MSG_SIZ];
\r
8514 safeStrCpy(buf, str, sizeof(buf)/sizeof(buf[0]) );
\r
8516 len = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,
\r
8517 NULL, error, LANG_NEUTRAL,
\r
8518 (LPSTR) buf2, MSG_SIZ, NULL);
\r
8520 snprintf(buf, 2*MSG_SIZ, "%s:\n%s", str, buf2);
\r
8522 ErrorMap *em = errmap;
\r
8523 while (em->err != 0 && em->err != error) em++;
\r
8524 if (em->err != 0) {
\r
8525 snprintf(buf, 2*MSG_SIZ, "%s:\n%s", str, em->msg);
\r
8527 snprintf(buf, 2*MSG_SIZ, "%s:\nError code %d", str, error);
\r
8532 ErrorPopUp(_("Error"), buf);
\r
8537 DisplayMoveError(char *str)
\r
8539 fromX = fromY = -1;
\r
8540 ClearHighlights();
\r
8541 DrawPosition(FALSE, NULL);
\r
8542 if (appData.popupMoveErrors) {
\r
8543 ErrorPopUp(_("Error"), str);
\r
8545 DisplayMessage(str, "");
\r
8546 moveErrorMessageUp = TRUE;
\r
8551 DisplayFatalError(char *str, int error, int exitStatus)
\r
8553 char buf[2*MSG_SIZ], buf2[MSG_SIZ];
\r
8555 char *label = exitStatus ? _("Fatal Error") : _("Exiting");
\r
8558 len = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,
\r
8559 NULL, error, LANG_NEUTRAL,
\r
8560 (LPSTR) buf2, MSG_SIZ, NULL);
\r
8562 snprintf(buf, 2*MSG_SIZ, "%s:\n%s", str, buf2);
\r
8564 ErrorMap *em = errmap;
\r
8565 while (em->err != 0 && em->err != error) em++;
\r
8566 if (em->err != 0) {
\r
8567 snprintf(buf, 2*MSG_SIZ, "%s:\n%s", str, em->msg);
\r
8569 snprintf(buf, 2*MSG_SIZ, "%s:\nError code %d", str, error);
\r
8574 if (appData.debugMode) {
\r
8575 fprintf(debugFP, "%s: %s\n", label, str);
\r
8577 if (appData.popupExitMessage) {
\r
8578 if(appData.icsActive) SendToICS("logout\n"); // [HGM] make sure no new games will be started!
\r
8579 (void) MessageBox(hwndMain, str, label, MB_OK|
\r
8580 (exitStatus ? MB_ICONSTOP : MB_ICONINFORMATION));
\r
8582 ExitEvent(exitStatus);
\r
8587 DisplayInformation(char *str)
\r
8589 (void) MessageBox(hwndMain, str, _("Information"), MB_OK|MB_ICONINFORMATION);
\r
8599 DisplayNote(char *str)
\r
8601 ErrorPopUp(_("Note"), str);
\r
8606 char *title, *question, *replyPrefix;
\r
8611 QuestionDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
8613 static QuestionParams *qp;
\r
8614 char reply[MSG_SIZ];
\r
8617 switch (message) {
\r
8618 case WM_INITDIALOG:
\r
8619 qp = (QuestionParams *) lParam;
\r
8620 CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));
\r
8621 Translate(hDlg, DLG_Question);
\r
8622 SetWindowText(hDlg, qp->title);
\r
8623 SetDlgItemText(hDlg, OPT_QuestionText, qp->question);
\r
8624 SetFocus(GetDlgItem(hDlg, OPT_QuestionInput));
\r
8628 switch (LOWORD(wParam)) {
\r
8630 safeStrCpy(reply, qp->replyPrefix, sizeof(reply)/sizeof(reply[0]) );
\r
8631 if (*reply) strcat(reply, " ");
\r
8632 len = strlen(reply);
\r
8633 GetDlgItemText(hDlg, OPT_QuestionInput, reply + len, sizeof(reply) - len);
\r
8634 strcat(reply, "\n");
\r
8635 OutputToProcess(qp->pr, reply, strlen(reply), &err);
\r
8636 EndDialog(hDlg, TRUE);
\r
8637 if (err) DisplayFatalError(_("Error writing to chess program"), err, 1);
\r
8640 EndDialog(hDlg, FALSE);
\r
8651 AskQuestion(char* title, char *question, char *replyPrefix, ProcRef pr)
\r
8653 QuestionParams qp;
\r
8657 qp.question = question;
\r
8658 qp.replyPrefix = replyPrefix;
\r
8660 lpProc = MakeProcInstance((FARPROC)QuestionDialog, hInst);
\r
8661 DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_Question),
\r
8662 hwndMain, (DLGPROC)lpProc, (LPARAM)&qp);
\r
8663 FreeProcInstance(lpProc);
\r
8666 /* [AS] Pick FRC position */
\r
8667 LRESULT CALLBACK NewGameFRC_Proc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
8669 static int * lpIndexFRC;
\r
8675 case WM_INITDIALOG:
\r
8676 lpIndexFRC = (int *) lParam;
\r
8678 CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));
\r
8679 Translate(hDlg, DLG_NewGameFRC);
\r
8681 SendDlgItemMessage( hDlg, IDC_NFG_Edit, EM_SETLIMITTEXT, sizeof(buf)-1, 0 );
\r
8682 SetDlgItemInt( hDlg, IDC_NFG_Edit, *lpIndexFRC, TRUE );
\r
8683 SendDlgItemMessage( hDlg, IDC_NFG_Edit, EM_SETSEL, 0, -1 );
\r
8684 SetFocus(GetDlgItem(hDlg, IDC_NFG_Edit));
\r
8689 switch( LOWORD(wParam) ) {
\r
8691 *lpIndexFRC = GetDlgItemInt(hDlg, IDC_NFG_Edit, &index_is_ok, TRUE );
\r
8692 EndDialog( hDlg, 0 );
\r
8693 shuffleOpenings = TRUE; /* [HGM] shuffle: switch shuffling on for as long as we stay in current variant */
\r
8696 EndDialog( hDlg, 1 );
\r
8698 case IDC_NFG_Edit:
\r
8699 if( HIWORD(wParam) == EN_CHANGE ) {
\r
8700 GetDlgItemInt(hDlg, IDC_NFG_Edit, &index_is_ok, TRUE );
\r
8702 EnableWindow( GetDlgItem(hDlg, IDOK), index_is_ok );
\r
8705 case IDC_NFG_Random:
\r
8706 snprintf( buf, sizeof(buf)/sizeof(buf[0]), "%d", myrandom() ); /* [HGM] shuffle: no longer limit to 960 */
\r
8707 SetDlgItemText(hDlg, IDC_NFG_Edit, buf );
\r
8720 int index = appData.defaultFrcPosition;
\r
8721 FARPROC lpProc = MakeProcInstance( (FARPROC) NewGameFRC_Proc, hInst );
\r
8723 result = DialogBoxParam( hInst, MAKEINTRESOURCE(DLG_NewGameFRC), hwndMain, (DLGPROC)lpProc, (LPARAM)&index );
\r
8725 if( result == 0 ) {
\r
8726 appData.defaultFrcPosition = index;
\r
8732 /* [AS] Game list options. Refactored by HGM */
\r
8734 HWND gameListOptionsDialog;
\r
8736 // low-level front-end: clear text edit / list widget
\r
8741 SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_RESETCONTENT, 0, 0 );
\r
8744 // low-level front-end: clear text edit / list widget
\r
8746 GLT_DeSelectList()
\r
8748 SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_SETCURSEL, 0, 0 );
\r
8751 // low-level front-end: append line to text edit / list widget
\r
8753 GLT_AddToList( char *name )
\r
8756 SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_ADDSTRING, 0, (LPARAM) name );
\r
8760 // low-level front-end: get line from text edit / list widget
\r
8762 GLT_GetFromList( int index, char *name )
\r
8765 if( SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_GETTEXT, index, (LPARAM) name ) != LB_ERR )
\r
8771 void GLT_MoveSelection( HWND hDlg, int delta )
\r
8773 int idx1 = (int) SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETCURSEL, 0, 0 );
\r
8774 int idx2 = idx1 + delta;
\r
8775 int count = (int) SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETCOUNT, 0, 0 );
\r
8777 if( idx1 >=0 && idx1 < count && idx2 >= 0 && idx2 < count ) {
\r
8780 SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETTEXT, idx1, (LPARAM) buf );
\r
8781 SendDlgItemMessage( hDlg, IDC_GameListTags, LB_DELETESTRING, idx1, 0 );
\r
8782 SendDlgItemMessage( hDlg, IDC_GameListTags, LB_INSERTSTRING, idx2, (LPARAM) buf );
\r
8783 SendDlgItemMessage( hDlg, IDC_GameListTags, LB_SETCURSEL, idx2, 0 );
\r
8787 LRESULT CALLBACK GameListOptions_Proc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
8791 case WM_INITDIALOG:
\r
8792 gameListOptionsDialog = hDlg; // [HGM] pass through global to keep out off back-end
\r
8794 CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));
\r
8795 Translate(hDlg, DLG_GameListOptions);
\r
8797 /* Initialize list */
\r
8798 GLT_TagsToList( lpUserGLT );
\r
8800 SetFocus( GetDlgItem(hDlg, IDC_GameListTags) );
\r
8805 switch( LOWORD(wParam) ) {
\r
8808 EndDialog( hDlg, 0 );
\r
8811 EndDialog( hDlg, 1 );
\r
8814 case IDC_GLT_Default:
\r
8815 GLT_TagsToList( GLT_DEFAULT_TAGS );
\r
8818 case IDC_GLT_Restore:
\r
8819 GLT_TagsToList( appData.gameListTags );
\r
8823 GLT_MoveSelection( hDlg, -1 );
\r
8826 case IDC_GLT_Down:
\r
8827 GLT_MoveSelection( hDlg, +1 );
\r
8837 int GameListOptions()
\r
8840 FARPROC lpProc = MakeProcInstance( (FARPROC) GameListOptions_Proc, hInst );
\r
8842 safeStrCpy( lpUserGLT, appData.gameListTags ,LPUSERGLT_SIZE );
\r
8844 result = DialogBoxParam( hInst, MAKEINTRESOURCE(DLG_GameListOptions), hwndMain, (DLGPROC)lpProc, (LPARAM)lpUserGLT );
\r
8846 if( result == 0 ) {
\r
8847 char *oldTags = appData.gameListTags;
\r
8848 /* [AS] Memory leak here! */
\r
8849 appData.gameListTags = strdup( lpUserGLT );
\r
8850 if(strcmp(oldTags, appData.gameListTags)) // [HGM] redo Game List when we changed something
\r
8851 GameListToListBox(NULL, TRUE, ".", NULL, FALSE, FALSE); // "." as filter is kludge to select all
\r
8858 DisplayIcsInteractionTitle(char *str)
\r
8860 char consoleTitle[MSG_SIZ];
\r
8862 snprintf(consoleTitle, MSG_SIZ, "%s: %s", szConsoleTitle, str);
\r
8863 SetWindowText(hwndConsole, consoleTitle);
\r
8865 if(appData.chatBoxes) { // [HGM] chat: open chat boxes
\r
8866 char buf[MSG_SIZ], *p = buf, *q;
\r
8867 safeStrCpy(buf, appData.chatBoxes, sizeof(buf)/sizeof(buf[0]) );
\r
8869 q = strchr(p, ';');
\r
8871 if(*p) ChatPopUp(p);
\r
8875 SetActiveWindow(hwndMain);
\r
8879 DrawPositionX(int fullRedraw, Board board)
\r
8881 HDCDrawPosition(NULL, (BOOLEAN) fullRedraw, board);
\r
8884 void NotifyFrontendLogin()
\r
8887 UpdateICSWidth(GetDlgItem(hwndConsole, OPT_ConsoleText));
\r
8893 fromX = fromY = -1;
\r
8894 if (dragInfo.pos.x != -1 || dragInfo.pos.y != -1) {
\r
8895 dragInfo.pos.x = dragInfo.pos.y = -1;
\r
8896 dragInfo.pos.x = dragInfo.pos.y = -1;
\r
8897 dragInfo.lastpos = dragInfo.pos;
\r
8898 dragInfo.start.x = dragInfo.start.y = -1;
\r
8899 dragInfo.from = dragInfo.start;
\r
8901 DrawPosition(TRUE, NULL);
\r
8908 CommentPopUp(char *title, char *str)
\r
8910 HWND hwnd = GetActiveWindow();
\r
8911 EitherCommentPopUp(currentMove, title, str, FALSE); // [HGM] vari: fake move index, rather than 0
\r
8913 SetActiveWindow(hwnd);
\r
8917 CommentPopDown(void)
\r
8919 CheckMenuItem(GetMenu(hwndMain), IDM_Comment, MF_UNCHECKED);
\r
8920 if (commentDialog) {
\r
8921 ShowWindow(commentDialog, SW_HIDE);
\r
8923 commentUp = FALSE;
\r
8927 EditCommentPopUp(int index, char *title, char *str)
\r
8929 EitherCommentPopUp(index, title, str, TRUE);
\r
8936 MyPlaySound(&sounds[(int)SoundRoar]);
\r
8943 MyPlaySound(&sounds[(int)SoundMove]);
\r
8946 VOID PlayIcsWinSound()
\r
8948 MyPlaySound(&sounds[(int)SoundIcsWin]);
\r
8951 VOID PlayIcsLossSound()
\r
8953 MyPlaySound(&sounds[(int)SoundIcsLoss]);
\r
8956 VOID PlayIcsDrawSound()
\r
8958 MyPlaySound(&sounds[(int)SoundIcsDraw]);
\r
8961 VOID PlayIcsUnfinishedSound()
\r
8963 MyPlaySound(&sounds[(int)SoundIcsUnfinished]);
\r
8969 MyPlaySound(&sounds[(int)SoundAlarm]);
\r
8975 MyPlaySound(&textAttribs[ColorTell].sound);
\r
8983 consoleEcho = TRUE;
\r
8984 hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
8985 SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&consoleCF);
\r
8986 SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);
\r
8995 consoleEcho = FALSE;
\r
8996 hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
8997 /* This works OK: set text and background both to the same color */
\r
8999 cf.crTextColor = COLOR_ECHOOFF;
\r
9000 SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf);
\r
9001 SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, cf.crTextColor);
\r
9004 /* No Raw()...? */
\r
9006 void Colorize(ColorClass cc, int continuation)
\r
9008 currentColorClass = cc;
\r
9009 consoleCF.dwMask = CFM_COLOR|CFM_BOLD|CFM_ITALIC|CFM_UNDERLINE|CFM_STRIKEOUT;
\r
9010 consoleCF.crTextColor = textAttribs[cc].color;
\r
9011 consoleCF.dwEffects = textAttribs[cc].effects;
\r
9012 if (!continuation) MyPlaySound(&textAttribs[cc].sound);
\r
9018 static char buf[MSG_SIZ];
\r
9019 DWORD bufsiz = MSG_SIZ;
\r
9021 if(appData.userName != NULL && appData.userName[0] != 0) {
\r
9022 return appData.userName; /* [HGM] username: prefer name selected by user over his system login */
\r
9024 if (!GetUserName(buf, &bufsiz)) {
\r
9025 /*DisplayError("Error getting user name", GetLastError());*/
\r
9026 safeStrCpy(buf, _("User"), sizeof(buf)/sizeof(buf[0]) );
\r
9034 static char buf[MSG_SIZ];
\r
9035 DWORD bufsiz = MSG_SIZ;
\r
9037 if (!GetComputerName(buf, &bufsiz)) {
\r
9038 /*DisplayError("Error getting host name", GetLastError());*/
\r
9039 safeStrCpy(buf, _("Unknown"), sizeof(buf)/sizeof(buf[0]) );
\r
9046 ClockTimerRunning()
\r
9048 return clockTimerEvent != 0;
\r
9054 if (clockTimerEvent == 0) return FALSE;
\r
9055 KillTimer(hwndMain, clockTimerEvent);
\r
9056 clockTimerEvent = 0;
\r
9061 StartClockTimer(long millisec)
\r
9063 clockTimerEvent = SetTimer(hwndMain, (UINT) CLOCK_TIMER_ID,
\r
9064 (UINT) millisec, NULL);
\r
9068 DisplayWhiteClock(long timeRemaining, int highlight)
\r
9071 char *flag = whiteFlag && gameMode == TwoMachinesPlay ? "(!)" : "";
\r
9073 if(appData.noGUI) return;
\r
9074 hdc = GetDC(hwndMain);
\r
9075 if (!IsIconic(hwndMain)) {
\r
9076 DisplayAClock(hdc, timeRemaining, highlight,
\r
9077 flipClock ? &blackRect : &whiteRect, _("White"), flag);
\r
9079 if (highlight && iconCurrent == iconBlack) {
\r
9080 iconCurrent = iconWhite;
\r
9081 PostMessage(hwndMain, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);
\r
9082 if (IsIconic(hwndMain)) {
\r
9083 DrawIcon(hdc, 2, 2, iconCurrent);
\r
9086 (void) ReleaseDC(hwndMain, hdc);
\r
9088 PostMessage(hwndConsole, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);
\r
9092 DisplayBlackClock(long timeRemaining, int highlight)
\r
9095 char *flag = blackFlag && gameMode == TwoMachinesPlay ? "(!)" : "";
\r
9098 if(appData.noGUI) return;
\r
9099 hdc = GetDC(hwndMain);
\r
9100 if (!IsIconic(hwndMain)) {
\r
9101 DisplayAClock(hdc, timeRemaining, highlight,
\r
9102 flipClock ? &whiteRect : &blackRect, _("Black"), flag);
\r
9104 if (highlight && iconCurrent == iconWhite) {
\r
9105 iconCurrent = iconBlack;
\r
9106 PostMessage(hwndMain, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);
\r
9107 if (IsIconic(hwndMain)) {
\r
9108 DrawIcon(hdc, 2, 2, iconCurrent);
\r
9111 (void) ReleaseDC(hwndMain, hdc);
\r
9113 PostMessage(hwndConsole, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);
\r
9118 LoadGameTimerRunning()
\r
9120 return loadGameTimerEvent != 0;
\r
9124 StopLoadGameTimer()
\r
9126 if (loadGameTimerEvent == 0) return FALSE;
\r
9127 KillTimer(hwndMain, loadGameTimerEvent);
\r
9128 loadGameTimerEvent = 0;
\r
9133 StartLoadGameTimer(long millisec)
\r
9135 loadGameTimerEvent = SetTimer(hwndMain, (UINT) LOAD_GAME_TIMER_ID,
\r
9136 (UINT) millisec, NULL);
\r
9144 char fileTitle[MSG_SIZ];
\r
9146 defName = DefaultFileName(appData.oldSaveStyle ? "gam" : "pgn");
\r
9147 f = OpenFileDialog(hwndMain, "a", defName,
\r
9148 appData.oldSaveStyle ? "gam" : "pgn",
\r
9150 _("Save Game to File"), NULL, fileTitle, NULL);
\r
9152 SaveGame(f, 0, "");
\r
9159 ScheduleDelayedEvent(DelayedEventCallback cb, long millisec)
\r
9161 if (delayedTimerEvent != 0) {
\r
9162 if (appData.debugMode && cb != delayedTimerCallback) { // [HGM] alive: not too much debug
\r
9163 fprintf(debugFP, "ScheduleDelayedEvent: event already scheduled\n");
\r
9165 KillTimer(hwndMain, delayedTimerEvent);
\r
9166 delayedTimerEvent = 0;
\r
9167 if(delayedTimerCallback != cb) // [HGM] alive: do not "flush" same event, just postpone it
\r
9168 delayedTimerCallback();
\r
9170 delayedTimerCallback = cb;
\r
9171 delayedTimerEvent = SetTimer(hwndMain, (UINT) DELAYED_TIMER_ID,
\r
9172 (UINT) millisec, NULL);
\r
9175 DelayedEventCallback
\r
9178 if (delayedTimerEvent) {
\r
9179 return delayedTimerCallback;
\r
9186 CancelDelayedEvent()
\r
9188 if (delayedTimerEvent) {
\r
9189 KillTimer(hwndMain, delayedTimerEvent);
\r
9190 delayedTimerEvent = 0;
\r
9194 DWORD GetWin32Priority(int nice)
\r
9195 { // [HGM] nice: translate Unix nice() value to indows priority class. (Code stolen from Polyglot 1.4w11)
\r
9197 REALTIME_PRIORITY_CLASS 0x00000100
\r
9198 HIGH_PRIORITY_CLASS 0x00000080
\r
9199 ABOVE_NORMAL_PRIORITY_CLASS 0x00008000
\r
9200 NORMAL_PRIORITY_CLASS 0x00000020
\r
9201 BELOW_NORMAL_PRIORITY_CLASS 0x00004000
\r
9202 IDLE_PRIORITY_CLASS 0x00000040
\r
9204 if (nice < -15) return 0x00000080;
\r
9205 if (nice < 0) return 0x00008000;
\r
9207 if (nice == 0) return 0x00000020;
\r
9208 if (nice < 15) return 0x00004000;
\r
9209 return 0x00000040;
\r
9212 void RunCommand(char *cmdLine)
\r
9214 /* Now create the child process. */
\r
9215 STARTUPINFO siStartInfo;
\r
9216 PROCESS_INFORMATION piProcInfo;
\r
9218 siStartInfo.cb = sizeof(STARTUPINFO);
\r
9219 siStartInfo.lpReserved = NULL;
\r
9220 siStartInfo.lpDesktop = NULL;
\r
9221 siStartInfo.lpTitle = NULL;
\r
9222 siStartInfo.dwFlags = STARTF_USESTDHANDLES;
\r
9223 siStartInfo.cbReserved2 = 0;
\r
9224 siStartInfo.lpReserved2 = NULL;
\r
9225 siStartInfo.hStdInput = NULL;
\r
9226 siStartInfo.hStdOutput = NULL;
\r
9227 siStartInfo.hStdError = NULL;
\r
9229 CreateProcess(NULL,
\r
9230 cmdLine, /* command line */
\r
9231 NULL, /* process security attributes */
\r
9232 NULL, /* primary thread security attrs */
\r
9233 TRUE, /* handles are inherited */
\r
9234 DETACHED_PROCESS|CREATE_NEW_PROCESS_GROUP,
\r
9235 NULL, /* use parent's environment */
\r
9237 &siStartInfo, /* STARTUPINFO pointer */
\r
9238 &piProcInfo); /* receives PROCESS_INFORMATION */
\r
9240 CloseHandle(piProcInfo.hThread);
\r
9243 /* Start a child process running the given program.
\r
9244 The process's standard output can be read from "from", and its
\r
9245 standard input can be written to "to".
\r
9246 Exit with fatal error if anything goes wrong.
\r
9247 Returns an opaque pointer that can be used to destroy the process
\r
9251 StartChildProcess(char *cmdLine, char *dir, ProcRef *pr)
\r
9253 #define BUFSIZE 4096
\r
9255 HANDLE hChildStdinRd, hChildStdinWr,
\r
9256 hChildStdoutRd, hChildStdoutWr;
\r
9257 HANDLE hChildStdinWrDup, hChildStdoutRdDup;
\r
9258 SECURITY_ATTRIBUTES saAttr;
\r
9260 PROCESS_INFORMATION piProcInfo;
\r
9261 STARTUPINFO siStartInfo;
\r
9263 char buf[MSG_SIZ];
\r
9266 if (appData.debugMode) {
\r
9267 fprintf(debugFP, "StartChildProcess (dir=\"%s\") %s\n", dir, cmdLine);
\r
9272 /* Set the bInheritHandle flag so pipe handles are inherited. */
\r
9273 saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);
\r
9274 saAttr.bInheritHandle = TRUE;
\r
9275 saAttr.lpSecurityDescriptor = NULL;
\r
9278 * The steps for redirecting child's STDOUT:
\r
9279 * 1. Create anonymous pipe to be STDOUT for child.
\r
9280 * 2. Create a noninheritable duplicate of read handle,
\r
9281 * and close the inheritable read handle.
\r
9284 /* Create a pipe for the child's STDOUT. */
\r
9285 if (! CreatePipe(&hChildStdoutRd, &hChildStdoutWr, &saAttr, 0)) {
\r
9286 return GetLastError();
\r
9289 /* Duplicate the read handle to the pipe, so it is not inherited. */
\r
9290 fSuccess = DuplicateHandle(GetCurrentProcess(), hChildStdoutRd,
\r
9291 GetCurrentProcess(), &hChildStdoutRdDup, 0,
\r
9292 FALSE, /* not inherited */
\r
9293 DUPLICATE_SAME_ACCESS);
\r
9295 return GetLastError();
\r
9297 CloseHandle(hChildStdoutRd);
\r
9300 * The steps for redirecting child's STDIN:
\r
9301 * 1. Create anonymous pipe to be STDIN for child.
\r
9302 * 2. Create a noninheritable duplicate of write handle,
\r
9303 * and close the inheritable write handle.
\r
9306 /* Create a pipe for the child's STDIN. */
\r
9307 if (! CreatePipe(&hChildStdinRd, &hChildStdinWr, &saAttr, 0)) {
\r
9308 return GetLastError();
\r
9311 /* Duplicate the write handle to the pipe, so it is not inherited. */
\r
9312 fSuccess = DuplicateHandle(GetCurrentProcess(), hChildStdinWr,
\r
9313 GetCurrentProcess(), &hChildStdinWrDup, 0,
\r
9314 FALSE, /* not inherited */
\r
9315 DUPLICATE_SAME_ACCESS);
\r
9317 return GetLastError();
\r
9319 CloseHandle(hChildStdinWr);
\r
9321 /* Arrange to (1) look in dir for the child .exe file, and
\r
9322 * (2) have dir be the child's working directory. Interpret
\r
9323 * dir relative to the directory WinBoard loaded from. */
\r
9324 GetCurrentDirectory(MSG_SIZ, buf);
\r
9325 SetCurrentDirectory(installDir);
\r
9326 SetCurrentDirectory(dir);
\r
9328 /* Now create the child process. */
\r
9330 siStartInfo.cb = sizeof(STARTUPINFO);
\r
9331 siStartInfo.lpReserved = NULL;
\r
9332 siStartInfo.lpDesktop = NULL;
\r
9333 siStartInfo.lpTitle = NULL;
\r
9334 siStartInfo.dwFlags = STARTF_USESTDHANDLES;
\r
9335 siStartInfo.cbReserved2 = 0;
\r
9336 siStartInfo.lpReserved2 = NULL;
\r
9337 siStartInfo.hStdInput = hChildStdinRd;
\r
9338 siStartInfo.hStdOutput = hChildStdoutWr;
\r
9339 siStartInfo.hStdError = hChildStdoutWr;
\r
9341 fSuccess = CreateProcess(NULL,
\r
9342 cmdLine, /* command line */
\r
9343 NULL, /* process security attributes */
\r
9344 NULL, /* primary thread security attrs */
\r
9345 TRUE, /* handles are inherited */
\r
9346 DETACHED_PROCESS|CREATE_NEW_PROCESS_GROUP,
\r
9347 NULL, /* use parent's environment */
\r
9349 &siStartInfo, /* STARTUPINFO pointer */
\r
9350 &piProcInfo); /* receives PROCESS_INFORMATION */
\r
9352 err = GetLastError();
\r
9353 SetCurrentDirectory(buf); /* return to prev directory */
\r
9358 if (appData.niceEngines){ // [HGM] nice: adjust engine proc priority
\r
9359 if(appData.debugMode) fprintf(debugFP, "nice engine proc to %d\n", appData.niceEngines);
\r
9360 SetPriorityClass(piProcInfo.hProcess, GetWin32Priority(appData.niceEngines));
\r
9363 /* Close the handles we don't need in the parent */
\r
9364 CloseHandle(piProcInfo.hThread);
\r
9365 CloseHandle(hChildStdinRd);
\r
9366 CloseHandle(hChildStdoutWr);
\r
9368 /* Prepare return value */
\r
9369 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
\r
9370 cp->kind = CPReal;
\r
9371 cp->hProcess = piProcInfo.hProcess;
\r
9372 cp->pid = piProcInfo.dwProcessId;
\r
9373 cp->hFrom = hChildStdoutRdDup;
\r
9374 cp->hTo = hChildStdinWrDup;
\r
9376 *pr = (void *) cp;
\r
9378 /* Klaus Friedel says that this Sleep solves a problem under Windows
\r
9379 2000 where engines sometimes don't see the initial command(s)
\r
9380 from WinBoard and hang. I don't understand how that can happen,
\r
9381 but the Sleep is harmless, so I've put it in. Others have also
\r
9382 reported what may be the same problem, so hopefully this will fix
\r
9383 it for them too. */
\r
9391 DestroyChildProcess(ProcRef pr, int/*boolean*/ signal)
\r
9393 ChildProc *cp; int result;
\r
9395 cp = (ChildProc *) pr;
\r
9396 if (cp == NULL) return;
\r
9398 switch (cp->kind) {
\r
9400 /* TerminateProcess is considered harmful, so... */
\r
9401 CloseHandle(cp->hTo); /* Closing this will give the child an EOF and hopefully kill it */
\r
9402 if (cp->hFrom) CloseHandle(cp->hFrom); /* if NULL, InputThread will close it */
\r
9403 /* The following doesn't work because the chess program
\r
9404 doesn't "have the same console" as WinBoard. Maybe
\r
9405 we could arrange for this even though neither WinBoard
\r
9406 nor the chess program uses a console for stdio? */
\r
9407 /*!!if (signal) GenerateConsoleCtrlEvent(CTRL_BREAK_EVENT, cp->pid);*/
\r
9409 /* [AS] Special termination modes for misbehaving programs... */
\r
9410 if( signal & 8 ) {
\r
9411 result = TerminateProcess( cp->hProcess, 0 );
\r
9413 if ( appData.debugMode) {
\r
9414 fprintf( debugFP, "Terminating process %lu, result=%d\n", cp->pid, result );
\r
9417 else if( signal & 4 ) {
\r
9418 DWORD dw = WaitForSingleObject( cp->hProcess, appData.delayAfterQuit*1000 + 50 ); // Wait 3 seconds at most
\r
9420 if( dw != WAIT_OBJECT_0 ) {
\r
9421 result = TerminateProcess( cp->hProcess, 0 );
\r
9423 if ( appData.debugMode) {
\r
9424 fprintf( debugFP, "Process %lu still alive after timeout, killing... result=%d\n", cp->pid, result );
\r
9430 CloseHandle(cp->hProcess);
\r
9434 if (cp->hFrom) CloseHandle(cp->hFrom);
\r
9438 closesocket(cp->sock);
\r
9443 if (signal) send(cp->sock2, "\017", 1, 0); /* 017 = 15 = SIGTERM */
\r
9444 closesocket(cp->sock);
\r
9445 closesocket(cp->sock2);
\r
9453 InterruptChildProcess(ProcRef pr)
\r
9457 cp = (ChildProc *) pr;
\r
9458 if (cp == NULL) return;
\r
9459 switch (cp->kind) {
\r
9461 /* The following doesn't work because the chess program
\r
9462 doesn't "have the same console" as WinBoard. Maybe
\r
9463 we could arrange for this even though neither WinBoard
\r
9464 nor the chess program uses a console for stdio */
\r
9465 /*!!GenerateConsoleCtrlEvent(CTRL_C_EVENT, cp->pid);*/
\r
9470 /* Can't interrupt */
\r
9474 send(cp->sock2, "\002", 1, 0); /* 2 = SIGINT */
\r
9481 OpenTelnet(char *host, char *port, ProcRef *pr)
\r
9483 char cmdLine[MSG_SIZ];
\r
9485 if (port[0] == NULLCHAR) {
\r
9486 snprintf(cmdLine, MSG_SIZ, "%s %s", appData.telnetProgram, host);
\r
9488 snprintf(cmdLine, MSG_SIZ, "%s %s %s", appData.telnetProgram, host, port);
\r
9490 return StartChildProcess(cmdLine, "", pr);
\r
9494 /* Code to open TCP sockets */
\r
9497 OpenTCP(char *host, char *port, ProcRef *pr)
\r
9503 struct sockaddr_in sa, mysa;
\r
9504 struct hostent FAR *hp;
\r
9505 unsigned short uport;
\r
9506 WORD wVersionRequested;
\r
9509 /* Initialize socket DLL */
\r
9510 wVersionRequested = MAKEWORD(1, 1);
\r
9511 err = WSAStartup(wVersionRequested, &wsaData);
\r
9512 if (err != 0) return err;
\r
9515 if ((s = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {
\r
9516 err = WSAGetLastError();
\r
9521 /* Bind local address using (mostly) don't-care values.
\r
9523 memset((char *) &mysa, 0, sizeof(struct sockaddr_in));
\r
9524 mysa.sin_family = AF_INET;
\r
9525 mysa.sin_addr.s_addr = INADDR_ANY;
\r
9526 uport = (unsigned short) 0;
\r
9527 mysa.sin_port = htons(uport);
\r
9528 if (bind(s, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))
\r
9529 == SOCKET_ERROR) {
\r
9530 err = WSAGetLastError();
\r
9535 /* Resolve remote host name */
\r
9536 memset((char *) &sa, 0, sizeof(struct sockaddr_in));
\r
9537 if (!(hp = gethostbyname(host))) {
\r
9538 unsigned int b0, b1, b2, b3;
\r
9540 err = WSAGetLastError();
\r
9542 if (sscanf(host, "%u.%u.%u.%u", &b0, &b1, &b2, &b3) == 4) {
\r
9543 hp = (struct hostent *) calloc(1, sizeof(struct hostent));
\r
9544 hp->h_addrtype = AF_INET;
\r
9546 hp->h_addr_list = (char **) calloc(2, sizeof(char *));
\r
9547 hp->h_addr_list[0] = (char *) malloc(4);
\r
9548 hp->h_addr_list[0][0] = (char) b0;
\r
9549 hp->h_addr_list[0][1] = (char) b1;
\r
9550 hp->h_addr_list[0][2] = (char) b2;
\r
9551 hp->h_addr_list[0][3] = (char) b3;
\r
9557 sa.sin_family = hp->h_addrtype;
\r
9558 uport = (unsigned short) atoi(port);
\r
9559 sa.sin_port = htons(uport);
\r
9560 memcpy((char *) &sa.sin_addr, hp->h_addr, hp->h_length);
\r
9562 /* Make connection */
\r
9563 if (connect(s, (struct sockaddr *) &sa,
\r
9564 sizeof(struct sockaddr_in)) == SOCKET_ERROR) {
\r
9565 err = WSAGetLastError();
\r
9570 /* Prepare return value */
\r
9571 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
\r
9572 cp->kind = CPSock;
\r
9574 *pr = (ProcRef *) cp;
\r
9580 OpenCommPort(char *name, ProcRef *pr)
\r
9585 char fullname[MSG_SIZ];
\r
9587 if (*name != '\\')
\r
9588 snprintf(fullname, MSG_SIZ, "\\\\.\\%s", name);
\r
9590 safeStrCpy(fullname, name, sizeof(fullname)/sizeof(fullname[0]) );
\r
9592 h = CreateFile(name, GENERIC_READ | GENERIC_WRITE,
\r
9593 0, NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL);
\r
9594 if (h == (HANDLE) -1) {
\r
9595 return GetLastError();
\r
9599 if (!SetCommState(h, (LPDCB) &dcb)) return GetLastError();
\r
9601 /* Accumulate characters until a 100ms pause, then parse */
\r
9602 ct.ReadIntervalTimeout = 100;
\r
9603 ct.ReadTotalTimeoutMultiplier = 0;
\r
9604 ct.ReadTotalTimeoutConstant = 0;
\r
9605 ct.WriteTotalTimeoutMultiplier = 0;
\r
9606 ct.WriteTotalTimeoutConstant = 0;
\r
9607 if (!SetCommTimeouts(h, (LPCOMMTIMEOUTS) &ct)) return GetLastError();
\r
9609 /* Prepare return value */
\r
9610 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
\r
9611 cp->kind = CPComm;
\r
9614 *pr = (ProcRef *) cp;
\r
9620 OpenLoopback(ProcRef *pr)
\r
9622 DisplayFatalError(_("Not implemented"), 0, 1);
\r
9628 OpenRcmd(char* host, char* user, char* cmd, ProcRef* pr)
\r
9633 struct sockaddr_in sa, mysa;
\r
9634 struct hostent FAR *hp;
\r
9635 unsigned short uport;
\r
9636 WORD wVersionRequested;
\r
9639 char stderrPortStr[MSG_SIZ];
\r
9641 /* Initialize socket DLL */
\r
9642 wVersionRequested = MAKEWORD(1, 1);
\r
9643 err = WSAStartup(wVersionRequested, &wsaData);
\r
9644 if (err != 0) return err;
\r
9646 /* Resolve remote host name */
\r
9647 memset((char *) &sa, 0, sizeof(struct sockaddr_in));
\r
9648 if (!(hp = gethostbyname(host))) {
\r
9649 unsigned int b0, b1, b2, b3;
\r
9651 err = WSAGetLastError();
\r
9653 if (sscanf(host, "%u.%u.%u.%u", &b0, &b1, &b2, &b3) == 4) {
\r
9654 hp = (struct hostent *) calloc(1, sizeof(struct hostent));
\r
9655 hp->h_addrtype = AF_INET;
\r
9657 hp->h_addr_list = (char **) calloc(2, sizeof(char *));
\r
9658 hp->h_addr_list[0] = (char *) malloc(4);
\r
9659 hp->h_addr_list[0][0] = (char) b0;
\r
9660 hp->h_addr_list[0][1] = (char) b1;
\r
9661 hp->h_addr_list[0][2] = (char) b2;
\r
9662 hp->h_addr_list[0][3] = (char) b3;
\r
9668 sa.sin_family = hp->h_addrtype;
\r
9669 uport = (unsigned short) 514;
\r
9670 sa.sin_port = htons(uport);
\r
9671 memcpy((char *) &sa.sin_addr, hp->h_addr, hp->h_length);
\r
9673 /* Bind local socket to unused "privileged" port address
\r
9675 s = INVALID_SOCKET;
\r
9676 memset((char *) &mysa, 0, sizeof(struct sockaddr_in));
\r
9677 mysa.sin_family = AF_INET;
\r
9678 mysa.sin_addr.s_addr = INADDR_ANY;
\r
9679 for (fromPort = 1023;; fromPort--) {
\r
9680 if (fromPort < 0) {
\r
9682 return WSAEADDRINUSE;
\r
9684 if (s == INVALID_SOCKET) {
\r
9686 if ((s = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {
\r
9687 err = WSAGetLastError();
\r
9692 uport = (unsigned short) fromPort;
\r
9693 mysa.sin_port = htons(uport);
\r
9694 if (bind(s, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))
\r
9695 == SOCKET_ERROR) {
\r
9696 err = WSAGetLastError();
\r
9697 if (err == WSAEADDRINUSE) continue;
\r
9701 if (connect(s, (struct sockaddr *) &sa,
\r
9702 sizeof(struct sockaddr_in)) == SOCKET_ERROR) {
\r
9703 err = WSAGetLastError();
\r
9704 if (err == WSAEADDRINUSE) {
\r
9715 /* Bind stderr local socket to unused "privileged" port address
\r
9717 s2 = INVALID_SOCKET;
\r
9718 memset((char *) &mysa, 0, sizeof(struct sockaddr_in));
\r
9719 mysa.sin_family = AF_INET;
\r
9720 mysa.sin_addr.s_addr = INADDR_ANY;
\r
9721 for (fromPort = 1023;; fromPort--) {
\r
9722 if (fromPort == prevStderrPort) continue; // don't reuse port
\r
9723 if (fromPort < 0) {
\r
9724 (void) closesocket(s);
\r
9726 return WSAEADDRINUSE;
\r
9728 if (s2 == INVALID_SOCKET) {
\r
9729 if ((s2 = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {
\r
9730 err = WSAGetLastError();
\r
9736 uport = (unsigned short) fromPort;
\r
9737 mysa.sin_port = htons(uport);
\r
9738 if (bind(s2, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))
\r
9739 == SOCKET_ERROR) {
\r
9740 err = WSAGetLastError();
\r
9741 if (err == WSAEADDRINUSE) continue;
\r
9742 (void) closesocket(s);
\r
9746 if (listen(s2, 1) == SOCKET_ERROR) {
\r
9747 err = WSAGetLastError();
\r
9748 if (err == WSAEADDRINUSE) {
\r
9750 s2 = INVALID_SOCKET;
\r
9753 (void) closesocket(s);
\r
9754 (void) closesocket(s2);
\r
9760 prevStderrPort = fromPort; // remember port used
\r
9761 snprintf(stderrPortStr, MSG_SIZ, "%d", fromPort);
\r
9763 if (send(s, stderrPortStr, strlen(stderrPortStr) + 1, 0) == SOCKET_ERROR) {
\r
9764 err = WSAGetLastError();
\r
9765 (void) closesocket(s);
\r
9766 (void) closesocket(s2);
\r
9771 if (send(s, UserName(), strlen(UserName()) + 1, 0) == SOCKET_ERROR) {
\r
9772 err = WSAGetLastError();
\r
9773 (void) closesocket(s);
\r
9774 (void) closesocket(s2);
\r
9778 if (*user == NULLCHAR) user = UserName();
\r
9779 if (send(s, user, strlen(user) + 1, 0) == SOCKET_ERROR) {
\r
9780 err = WSAGetLastError();
\r
9781 (void) closesocket(s);
\r
9782 (void) closesocket(s2);
\r
9786 if (send(s, cmd, strlen(cmd) + 1, 0) == SOCKET_ERROR) {
\r
9787 err = WSAGetLastError();
\r
9788 (void) closesocket(s);
\r
9789 (void) closesocket(s2);
\r
9794 if ((s3 = accept(s2, NULL, NULL)) == INVALID_SOCKET) {
\r
9795 err = WSAGetLastError();
\r
9796 (void) closesocket(s);
\r
9797 (void) closesocket(s2);
\r
9801 (void) closesocket(s2); /* Stop listening */
\r
9803 /* Prepare return value */
\r
9804 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
\r
9805 cp->kind = CPRcmd;
\r
9808 *pr = (ProcRef *) cp;
\r
9815 AddInputSource(ProcRef pr, int lineByLine,
\r
9816 InputCallback func, VOIDSTAR closure)
\r
9818 InputSource *is, *is2 = NULL;
\r
9819 ChildProc *cp = (ChildProc *) pr;
\r
9821 is = (InputSource *) calloc(1, sizeof(InputSource));
\r
9822 is->lineByLine = lineByLine;
\r
9824 is->closure = closure;
\r
9825 is->second = NULL;
\r
9826 is->next = is->buf;
\r
9827 if (pr == NoProc) {
\r
9828 is->kind = CPReal;
\r
9829 consoleInputSource = is;
\r
9831 is->kind = cp->kind;
\r
9833 [AS] Try to avoid a race condition if the thread is given control too early:
\r
9834 we create all threads suspended so that the is->hThread variable can be
\r
9835 safely assigned, then let the threads start with ResumeThread.
\r
9837 switch (cp->kind) {
\r
9839 is->hFile = cp->hFrom;
\r
9840 cp->hFrom = NULL; /* now owned by InputThread */
\r
9842 CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) NonOvlInputThread,
\r
9843 (LPVOID) is, CREATE_SUSPENDED, &is->id);
\r
9847 is->hFile = cp->hFrom;
\r
9848 cp->hFrom = NULL; /* now owned by InputThread */
\r
9850 CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) InputThread,
\r
9851 (LPVOID) is, CREATE_SUSPENDED, &is->id);
\r
9855 is->sock = cp->sock;
\r
9857 CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,
\r
9858 (LPVOID) is, CREATE_SUSPENDED, &is->id);
\r
9862 is2 = (InputSource *) calloc(1, sizeof(InputSource));
\r
9864 is->sock = cp->sock;
\r
9866 is2->sock = cp->sock2;
\r
9867 is2->second = is2;
\r
9869 CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,
\r
9870 (LPVOID) is, CREATE_SUSPENDED, &is->id);
\r
9872 CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,
\r
9873 (LPVOID) is2, CREATE_SUSPENDED, &is2->id);
\r
9877 if( is->hThread != NULL ) {
\r
9878 ResumeThread( is->hThread );
\r
9881 if( is2 != NULL && is2->hThread != NULL ) {
\r
9882 ResumeThread( is2->hThread );
\r
9886 return (InputSourceRef) is;
\r
9890 RemoveInputSource(InputSourceRef isr)
\r
9894 is = (InputSource *) isr;
\r
9895 is->hThread = NULL; /* tell thread to stop */
\r
9896 CloseHandle(is->hThread);
\r
9897 if (is->second != NULL) {
\r
9898 is->second->hThread = NULL;
\r
9899 CloseHandle(is->second->hThread);
\r
9903 int no_wrap(char *message, int count)
\r
9905 ConsoleOutput(message, count, FALSE);
\r
9910 OutputToProcess(ProcRef pr, char *message, int count, int *outError)
\r
9913 int outCount = SOCKET_ERROR;
\r
9914 ChildProc *cp = (ChildProc *) pr;
\r
9915 static OVERLAPPED ovl;
\r
9917 static int line = 0;
\r
9921 if (appData.noJoin || !appData.useInternalWrap)
\r
9922 return no_wrap(message, count);
\r
9925 int width = get_term_width();
\r
9926 int len = wrap(NULL, message, count, width, &line);
\r
9927 char *msg = malloc(len);
\r
9931 return no_wrap(message, count);
\r
9934 dbgchk = wrap(msg, message, count, width, &line);
\r
9935 if (dbgchk != len && appData.debugMode)
\r
9936 fprintf(debugFP, "wrap(): dbgchk(%d) != len(%d)\n", dbgchk, len);
\r
9937 ConsoleOutput(msg, len, FALSE);
\r
9944 if (ovl.hEvent == NULL) {
\r
9945 ovl.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
\r
9947 ovl.Internal = ovl.InternalHigh = ovl.Offset = ovl.OffsetHigh = 0;
\r
9949 switch (cp->kind) {
\r
9952 outCount = send(cp->sock, message, count, 0);
\r
9953 if (outCount == SOCKET_ERROR) {
\r
9954 *outError = WSAGetLastError();
\r
9956 *outError = NO_ERROR;
\r
9961 if (WriteFile(((ChildProc *)pr)->hTo, message, count,
\r
9962 &dOutCount, NULL)) {
\r
9963 *outError = NO_ERROR;
\r
9964 outCount = (int) dOutCount;
\r
9966 *outError = GetLastError();
\r
9971 *outError = DoWriteFile(((ChildProc *)pr)->hTo, message, count,
\r
9972 &dOutCount, &ovl);
\r
9973 if (*outError == NO_ERROR) {
\r
9974 outCount = (int) dOutCount;
\r
9984 if(n != 0) Sleep(n);
\r
9988 OutputToProcessDelayed(ProcRef pr, char *message, int count, int *outError,
\r
9991 /* Ignore delay, not implemented for WinBoard */
\r
9992 return OutputToProcess(pr, message, count, outError);
\r
9997 CmailSigHandlerCallBack(InputSourceRef isr, VOIDSTAR closure,
\r
9998 char *buf, int count, int error)
\r
10000 DisplayFatalError(_("Not implemented"), 0, 1);
\r
10003 /* see wgamelist.c for Game List functions */
\r
10004 /* see wedittags.c for Edit Tags functions */
\r
10011 char buf[MSG_SIZ];
\r
10014 if (SearchPath(installDir, appData.icsLogon, NULL, MSG_SIZ, buf, &dummy)) {
\r
10015 f = fopen(buf, "r");
\r
10017 ProcessICSInitScript(f);
\r
10027 StartAnalysisClock()
\r
10029 if (analysisTimerEvent) return;
\r
10030 analysisTimerEvent = SetTimer(hwndMain, (UINT) ANALYSIS_TIMER_ID,
\r
10031 (UINT) 2000, NULL);
\r
10035 SetHighlights(int fromX, int fromY, int toX, int toY)
\r
10037 highlightInfo.sq[0].x = fromX;
\r
10038 highlightInfo.sq[0].y = fromY;
\r
10039 highlightInfo.sq[1].x = toX;
\r
10040 highlightInfo.sq[1].y = toY;
\r
10044 ClearHighlights()
\r
10046 highlightInfo.sq[0].x = highlightInfo.sq[0].y =
\r
10047 highlightInfo.sq[1].x = highlightInfo.sq[1].y = -1;
\r
10051 SetPremoveHighlights(int fromX, int fromY, int toX, int toY)
\r
10053 premoveHighlightInfo.sq[0].x = fromX;
\r
10054 premoveHighlightInfo.sq[0].y = fromY;
\r
10055 premoveHighlightInfo.sq[1].x = toX;
\r
10056 premoveHighlightInfo.sq[1].y = toY;
\r
10060 ClearPremoveHighlights()
\r
10062 premoveHighlightInfo.sq[0].x = premoveHighlightInfo.sq[0].y =
\r
10063 premoveHighlightInfo.sq[1].x = premoveHighlightInfo.sq[1].y = -1;
\r
10067 ShutDownFrontEnd()
\r
10069 if (saveSettingsOnExit) SaveSettings(settingsFileName);
\r
10070 DeleteClipboardTempFiles();
\r
10076 if (IsIconic(hwndMain))
\r
10077 ShowWindow(hwndMain, SW_RESTORE);
\r
10079 SetActiveWindow(hwndMain);
\r
10083 * Prototypes for animation support routines
\r
10085 static void ScreenSquare(int column, int row, POINT * pt);
\r
10086 static void Tween( POINT * start, POINT * mid, POINT * finish, int factor,
\r
10087 POINT frames[], int * nFrames);
\r
10090 #define kFactor 4
\r
10093 AnimateAtomicCapture(Board board, int fromX, int fromY, int toX, int toY)
\r
10094 { // [HGM] atomic: animate blast wave
\r
10097 explodeInfo.fromX = fromX;
\r
10098 explodeInfo.fromY = fromY;
\r
10099 explodeInfo.toX = toX;
\r
10100 explodeInfo.toY = toY;
\r
10101 for(i=1; i<4*kFactor; i++) {
\r
10102 explodeInfo.radius = (i*180)/(4*kFactor-1);
\r
10103 DrawPosition(FALSE, board);
\r
10104 Sleep(appData.animSpeed);
\r
10106 explodeInfo.radius = 0;
\r
10107 DrawPosition(TRUE, board);
\r
10111 AnimateMove(board, fromX, fromY, toX, toY)
\r
10118 ChessSquare piece, victim = EmptySquare, victim2 = EmptySquare;
\r
10119 int x = toX, y = toY, x2 = kill2X;
\r
10120 POINT start, finish, mid;
\r
10121 POINT frames[kFactor * 2 + 1];
\r
10124 if(killX >= 0 && IS_LION(board[fromY][fromX])) Roar();
\r
10126 if (!appData.animate) return;
\r
10127 if (doingSizing) return;
\r
10128 if (fromY < 0 || fromX < 0) return;
\r
10129 piece = board[fromY][fromX];
\r
10130 if (piece >= EmptySquare) return;
\r
10132 if(x2 >= 0) toX = kill2X, toY = kill2Y, victim = board[killY][killX], victim2 = board[kill2Y][kill2X]; else
\r
10133 if(killX >= 0) toX = killX, toY = killY, victim = board[killY][killX]; // [HGM] lion: first to kill square
\r
10135 animInfo.from.x = fromX;
\r
10136 animInfo.from.y = fromY;
\r
10140 ScreenSquare(fromX, fromY, &start);
\r
10141 ScreenSquare(toX, toY, &finish);
\r
10143 /* All moves except knight jumps move in straight line */
\r
10144 if (!(abs(fromX-toX) == 1 && abs(fromY-toY) == 2 || abs(fromX-toX) == 2 && abs(fromY-toY) == 1)) {
\r
10145 mid.x = start.x + (finish.x - start.x) / 2;
\r
10146 mid.y = start.y + (finish.y - start.y) / 2;
\r
10148 /* Knight: make straight movement then diagonal */
\r
10149 if (abs(toY - fromY) < abs(toX - fromX)) {
\r
10150 mid.x = start.x + (finish.x - start.x) / 2;
\r
10154 mid.y = start.y + (finish.y - start.y) / 2;
\r
10158 /* Don't use as many frames for very short moves */
\r
10159 if (abs(toY - fromY) + abs(toX - fromX) <= 2)
\r
10160 Tween(&start, &mid, &finish, kFactor - 1, frames, &nFrames);
\r
10162 Tween(&start, &mid, &finish, kFactor, frames, &nFrames);
\r
10164 animInfo.to.x = toX;
\r
10165 animInfo.to.y = toY;
\r
10166 animInfo.lastpos = start;
\r
10167 animInfo.piece = piece;
\r
10168 for (n = 0; n < nFrames; n++) {
\r
10169 animInfo.pos = frames[n];
\r
10170 DrawPosition(FALSE, board);
\r
10171 animInfo.lastpos = animInfo.pos;
\r
10172 Sleep(appData.animSpeed);
\r
10174 animInfo.pos = finish;
\r
10175 DrawPosition(FALSE, board);
\r
10177 if(toX == x2 && toY == kill2Y) {
\r
10178 fromX = toX; fromY = toY; toX = killX; toY = killY; x2 = -1;
\r
10179 board[kill2Y][kill2X] = EmptySquare; goto again;
\r
10181 if(toX != x || toY != y) {
\r
10182 fromX = toX; fromY = toY; toX = x; toY = y;
\r
10183 board[killY][killX] = EmptySquare; goto again;
\r
10186 if(victim2 != EmptySquare) board[kill2Y][kill2X] = victim2;
\r
10187 if(victim != EmptySquare) board[killY][killX] = victim;
\r
10189 animInfo.piece = EmptySquare;
\r
10190 Explode(board, fromX, fromY, toX, toY);
\r
10193 /* Convert board position to corner of screen rect and color */
\r
10196 ScreenSquare(column, row, pt)
\r
10197 int column; int row; POINT * pt;
\r
10200 pt->x = lineGap + ((BOARD_WIDTH-1)-column) * (squareSize + lineGap) + border;
\r
10201 pt->y = lineGap + row * (squareSize + lineGap) + border;
\r
10203 pt->x = lineGap + column * (squareSize + lineGap) + border;
\r
10204 pt->y = lineGap + ((BOARD_HEIGHT-1)-row) * (squareSize + lineGap) + border;
\r
10208 /* Generate a series of frame coords from start->mid->finish.
\r
10209 The movement rate doubles until the half way point is
\r
10210 reached, then halves back down to the final destination,
\r
10211 which gives a nice slow in/out effect. The algorithmn
\r
10212 may seem to generate too many intermediates for short
\r
10213 moves, but remember that the purpose is to attract the
\r
10214 viewers attention to the piece about to be moved and
\r
10215 then to where it ends up. Too few frames would be less
\r
10219 Tween(start, mid, finish, factor, frames, nFrames)
\r
10220 POINT * start; POINT * mid;
\r
10221 POINT * finish; int factor;
\r
10222 POINT frames[]; int * nFrames;
\r
10224 int n, fraction = 1, count = 0;
\r
10226 /* Slow in, stepping 1/16th, then 1/8th, ... */
\r
10227 for (n = 0; n < factor; n++)
\r
10229 for (n = 0; n < factor; n++) {
\r
10230 frames[count].x = start->x + (mid->x - start->x) / fraction;
\r
10231 frames[count].y = start->y + (mid->y - start->y) / fraction;
\r
10233 fraction = fraction / 2;
\r
10237 frames[count] = *mid;
\r
10240 /* Slow out, stepping 1/2, then 1/4, ... */
\r
10242 for (n = 0; n < factor; n++) {
\r
10243 frames[count].x = finish->x - (finish->x - mid->x) / fraction;
\r
10244 frames[count].y = finish->y - (finish->y - mid->y) / fraction;
\r
10246 fraction = fraction * 2;
\r
10248 *nFrames = count;
\r
10252 SettingsPopUp(ChessProgramState *cps)
\r
10253 { // [HGM] wrapper needed because handles must not be passed through back-end
\r
10254 EngineOptionsPopup(savedHwnd, cps);
\r
10257 int flock(int fid, int code)
\r
10259 HANDLE hFile = (HANDLE) _get_osfhandle(fid);
\r
10261 ov.hEvent = NULL;
\r
10263 ov.OffsetHigh = 0;
\r
10265 case 1: LockFileEx(hFile, LOCKFILE_EXCLUSIVE_LOCK, 0, 1024, 0, &ov); break; // LOCK_SH
\r
10267 case 2: LockFileEx(hFile, LOCKFILE_EXCLUSIVE_LOCK, 0, 1024, 0, &ov); break; // LOCK_EX
\r
10268 case 3: UnlockFileEx(hFile, 0, 1024, 0, &ov); break; // LOCK_UN
\r
10269 default: return -1;
\r
10278 static char col[8][20];
\r
10279 COLORREF color = *(COLORREF *) colorVariable[n];
\r
10281 snprintf(col[i], 20, "#%02lx%02lx%02lx", color&0xff, (color>>8)&0xff, (color>>16)&0xff);
\r
10286 ActivateTheme (int new)
\r
10287 { // Redo initialization of features depending on options that can occur in themes
\r
10289 if(new) InitDrawingColors();
\r
10290 fontBitmapSquareSize = 0; // request creation of new font pieces
\r
10291 InitDrawingSizes(boardSize, 0);
\r
10292 InvalidateRect(hwndMain, NULL, TRUE);
\r