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
2076 case WhiteCardinal:
\r
2085 void CreatePiecesFromFont()
\r
2088 HDC hdc_window = NULL;
\r
2094 if( fontBitmapSquareSize < 0 ) {
\r
2095 /* Something went seriously wrong in the past: do not try to recreate fonts! */
\r
2099 if( !appData.useFont || appData.renderPiecesWithFont == NULL ||
\r
2100 appData.renderPiecesWithFont[0] == NULLCHAR || appData.renderPiecesWithFont[0] == '*' ) {
\r
2101 fontBitmapSquareSize = -1;
\r
2105 if( fontBitmapSquareSize != squareSize ) {
\r
2106 hdc_window = GetDC( hwndMain );
\r
2107 hdc = CreateCompatibleDC( hdc_window );
\r
2109 if( hPieceFont != NULL ) {
\r
2110 DeleteObject( hPieceFont );
\r
2113 for( i=0; i<=(int)BlackKing; i++ ) {
\r
2114 hPieceMask[i] = NULL;
\r
2115 hPieceFace[i] = NULL;
\r
2121 if( appData.fontPieceSize >= 50 && appData.fontPieceSize <= 150 ) {
\r
2122 fontHeight = appData.fontPieceSize;
\r
2125 fontHeight = (fontHeight * squareSize) / 100;
\r
2127 lf.lfHeight = -MulDiv( fontHeight, GetDeviceCaps(hdc, LOGPIXELSY), 72 );
\r
2129 lf.lfEscapement = 0;
\r
2130 lf.lfOrientation = 0;
\r
2131 lf.lfWeight = FW_NORMAL;
\r
2133 lf.lfUnderline = 0;
\r
2134 lf.lfStrikeOut = 0;
\r
2135 lf.lfCharSet = DEFAULT_CHARSET;
\r
2136 lf.lfOutPrecision = OUT_DEFAULT_PRECIS;
\r
2137 lf.lfClipPrecision = CLIP_DEFAULT_PRECIS;
\r
2138 lf.lfQuality = PROOF_QUALITY;
\r
2139 lf.lfPitchAndFamily = DEFAULT_PITCH | FF_DONTCARE;
\r
2140 strncpy( lf.lfFaceName, appData.renderPiecesWithFont, sizeof(lf.lfFaceName) );
\r
2141 lf.lfFaceName[ sizeof(lf.lfFaceName) - 1 ] = '\0';
\r
2143 hPieceFont = CreateFontIndirect( &lf );
\r
2145 if( hPieceFont == NULL ) {
\r
2146 fontBitmapSquareSize = -2;
\r
2149 /* Setup font-to-piece character table */
\r
2150 if( ! SetCharTable(pieceToFontChar, appData.fontToPieceTable) ) {
\r
2151 /* No (or wrong) global settings, try to detect the font */
\r
2152 if( strstr(lf.lfFaceName,"Alpha") != NULL ) {
\r
2154 SetCharTable(pieceToFontChar, "phbrqkojntwl");
\r
2156 else if( strstr(lf.lfFaceName,"DiagramTT") != NULL ) {
\r
2157 /* DiagramTT* family */
\r
2158 SetCharTable(pieceToFontChar, "PNLRQKpnlrqk");
\r
2160 else if( strstr(lf.lfFaceName,"WinboardF") != NULL ) {
\r
2161 /* Fairy symbols */
\r
2162 SetCharTable(pieceToFontChar, "PNBRQFEACWMOHIJGDVSLUKpnbrqfeacwmohijgdvsluk");
\r
2164 else if( strstr(lf.lfFaceName,"GC2004D") != NULL ) {
\r
2165 /* Good Companion (Some characters get warped as literal :-( */
\r
2166 char s[] = "1cmWG0??S??oYI23wgQU";
\r
2167 s[0]=0xB9; s[1]=0xA9; s[6]=0xB1; s[11]=0xBB; s[12]=0xAB; s[17]=0xB3;
\r
2168 SetCharTable(pieceToFontChar, s);
\r
2171 /* Cases, Condal, Leipzig, Lucena, Marroquin, Merida, Usual */
\r
2172 SetCharTable(pieceToFontChar, "pnbrqkomvtwl");
\r
2176 /* Create bitmaps */
\r
2177 hfont_old = SelectObject( hdc, hPieceFont );
\r
2178 for(i=(int)WhitePawn; i<(int)EmptySquare; i++) /* [HGM] made a loop for this */
\r
2179 if(PieceToChar((ChessSquare)i) != '.') /* skip unused pieces */
\r
2180 CreatePieceMaskFromFont( hdc_window, hdc, i );
\r
2182 SelectObject( hdc, hfont_old );
\r
2184 fontBitmapSquareSize = squareSize;
\r
2188 if( hdc != NULL ) {
\r
2192 if( hdc_window != NULL ) {
\r
2193 ReleaseDC( hwndMain, hdc_window );
\r
2198 DoLoadBitmap(HINSTANCE hinst, char *piece, int squareSize, char *suffix)
\r
2200 char name[128], buf[MSG_SIZ], *ids = "pnbrqfeicwmohajgdvlsukaacvdklnwpwnwlwswolfgnuzebracameltowersword", *p;
\r
2202 snprintf(name, sizeof(name)/sizeof(name[0]), "%s%d%s", piece, squareSize, suffix);
\r
2203 if(appData.pieceDirectory[0]) {
\r
2205 snprintf(buf, MSG_SIZ, "%s\\%s.bmp", appData.pieceDirectory, name);
\r
2206 res = LoadImage( 0, buf, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );
\r
2207 if(res) return res;
\r
2208 p = strstr(ids, piece);
\r
2209 if(p) { // if we could reconstruct canonical piece number, try the pieceNNN_ format before falling back on built-ins
\r
2212 case 21: n = WhiteKing; break;
\r
2213 case 22: n = WhiteAngel; break;
\r
2214 case 24: n = WhiteSilver; break;
\r
2215 case 26: n = WhiteDragon; break;
\r
2216 case 28: n = WhiteLion; break;
\r
2217 case 30: n = WhiteTokin; break;
\r
2218 case 32: n = WhitePKnight; break;
\r
2219 case 34: n = WhitePLance; break;
\r
2220 case 36: n = WhitePSilver; break;
\r
2221 case 38: n = WhiteWolf; break;
\r
2222 case 42: n = WhiteGnu; break;
\r
2223 case 45: n = WhiteZebra; break;
\r
2224 case 50: n = WhiteCamel; break;
\r
2225 case 55: n = WhiteTower; break;
\r
2226 case 60: n = WhiteSword; break;
\r
2228 snprintf(buf, MSG_SIZ, "%s\\piece%d_%d%s.bmp", appData.pieceDirectory, n, squareSize, suffix);
\r
2229 res = LoadImage( 0, buf, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );
\r
2230 if(res) return res;
\r
2233 if (gameInfo.event &&
\r
2234 strcmp(gameInfo.event, "Easter Egg Hunt") == 0 &&
\r
2235 strcmp(name, "k80s") == 0) {
\r
2236 safeStrCpy(name, "tim", sizeof(name)/sizeof(name[0]) );
\r
2238 return LoadBitmap(hinst, name);
\r
2242 /* Insert a color into the program's logical palette
\r
2243 structure. This code assumes the given color is
\r
2244 the result of the RGB or PALETTERGB macro, and it
\r
2245 knows how those macros work (which is documented).
\r
2248 InsertInPalette(COLORREF color)
\r
2250 LPPALETTEENTRY pe = &(pLogPal->palPalEntry[pLogPal->palNumEntries]);
\r
2252 if (pLogPal->palNumEntries++ >= PALETTESIZE) {
\r
2253 DisplayFatalError(_("Too many colors"), 0, 1);
\r
2254 pLogPal->palNumEntries--;
\r
2258 pe->peFlags = (char) 0;
\r
2259 pe->peRed = (char) (0xFF & color);
\r
2260 pe->peGreen = (char) (0xFF & (color >> 8));
\r
2261 pe->peBlue = (char) (0xFF & (color >> 16));
\r
2267 InitDrawingColors()
\r
2270 if (pLogPal == NULL) {
\r
2271 /* Allocate enough memory for a logical palette with
\r
2272 * PALETTESIZE entries and set the size and version fields
\r
2273 * of the logical palette structure.
\r
2275 pLogPal = (NPLOGPALETTE)
\r
2276 LocalAlloc(LMEM_FIXED, (sizeof(LOGPALETTE) +
\r
2277 (sizeof(PALETTEENTRY) * (PALETTESIZE))));
\r
2278 pLogPal->palVersion = 0x300;
\r
2280 pLogPal->palNumEntries = 0;
\r
2282 InsertInPalette(lightSquareColor);
\r
2283 InsertInPalette(darkSquareColor);
\r
2284 InsertInPalette(whitePieceColor);
\r
2285 InsertInPalette(blackPieceColor);
\r
2286 InsertInPalette(highlightSquareColor);
\r
2287 InsertInPalette(premoveHighlightColor);
\r
2289 /* create a logical color palette according the information
\r
2290 * in the LOGPALETTE structure.
\r
2292 hPal = CreatePalette((LPLOGPALETTE) pLogPal);
\r
2294 lightSquareBrush = CreateSolidBrush(lightSquareColor);
\r
2295 blackSquareBrush = CreateSolidBrush(blackPieceColor);
\r
2296 darkSquareBrush = CreateSolidBrush(darkSquareColor);
\r
2297 whitePieceBrush = CreateSolidBrush(whitePieceColor);
\r
2298 blackPieceBrush = CreateSolidBrush(blackPieceColor);
\r
2299 iconBkgndBrush = CreateSolidBrush(GetSysColor(COLOR_BACKGROUND));
\r
2300 explodeBrush = CreateSolidBrush(highlightSquareColor); // [HGM] atomic
\r
2301 for(i=0; i<8;i++) markerBrush[i] = CreateSolidBrush(markerColor[i]); // [HGM] markers
\r
2303 /* [AS] Force rendering of the font-based pieces */
\r
2304 if( fontBitmapSquareSize > 0 ) {
\r
2305 fontBitmapSquareSize = 0;
\r
2311 BoardWidth(int boardSize, int n)
\r
2312 { /* [HGM] argument n added to allow different width and height */
\r
2313 int lineGap = sizeInfo[boardSize].lineGap;
\r
2315 if( appData.overrideLineGap >= 0 && appData.overrideLineGap <= 5 ) {
\r
2316 lineGap = appData.overrideLineGap;
\r
2319 return (n + 1) * lineGap +
\r
2320 n * sizeInfo[boardSize].squareSize;
\r
2323 /* Respond to board resize by dragging edge */
\r
2325 ResizeBoard(int newSizeX, int newSizeY, int flags)
\r
2327 BoardSize newSize = NUM_SIZES - 1;
\r
2328 static int recurse = 0;
\r
2329 if (IsIconic(hwndMain)) return;
\r
2330 if (recurse > 0) return;
\r
2332 while (newSize > 0) {
\r
2333 InitDrawingSizes(newSize+1000, 0); // [HGM] kludge to update sizeInfo without visible effects
\r
2334 if(newSizeX >= sizeInfo[newSize].cliWidth &&
\r
2335 newSizeY >= sizeInfo[newSize].cliHeight) break;
\r
2338 boardSize = newSize;
\r
2339 InitDrawingSizes(boardSize, flags);
\r
2344 extern Boolean twoBoards, partnerUp; // [HGM] dual
\r
2347 InitDrawingSizes(BoardSize boardSize, int flags)
\r
2349 int i, boardWidth, boardHeight; /* [HGM] height treated separately */
\r
2350 ChessSquare piece;
\r
2351 static int oldBoardSize = -1, oldTinyLayout = 0;
\r
2353 SIZE clockSize, messageSize;
\r
2355 char buf[MSG_SIZ];
\r
2357 HMENU hmenu = GetMenu(hwndMain);
\r
2358 RECT crect, wrect, oldRect;
\r
2360 LOGBRUSH logbrush;
\r
2361 VariantClass v = gameInfo.variant;
\r
2363 int suppressVisibleEffects = 0; // [HGM] kludge to request updating sizeInfo only
\r
2364 if((int)boardSize >= 1000 ) { boardSize -= 1000; suppressVisibleEffects = 1; }
\r
2366 /* [HGM] call with -2 uses old size (for if nr of files, ranks changes) */
\r
2367 if(boardSize == (BoardSize)(-2) ) boardSize = oldBoardSize;
\r
2368 if(boardSize == -1) return; // no size defined yet; abort (to allow early call of InitPosition)
\r
2369 oldBoardSize = boardSize;
\r
2371 if(boardSize != SizeMiddling && boardSize != SizePetite && boardSize != SizeBulky && !appData.useFont)
\r
2372 { // correct board size to one where built-in pieces exist
\r
2373 if((v == VariantCapablanca || v == VariantGothic || v == VariantGrand || v == VariantCapaRandom || v == VariantJanus || v == VariantSuper)
\r
2374 && (boardSize < SizePetite || boardSize > SizeBulky) // Archbishop and Chancellor available in entire middle range
\r
2376 || (v == VariantShogi && boardSize != SizeModerate) // Japanese-style Shogi
\r
2377 || v == VariantKnightmate || v == VariantSChess || v == VariantXiangqi || v == VariantSpartan
\r
2378 || v == VariantShatranj || v == VariantMakruk || v == VariantGreat || v == VariantFairy || v == VariantLion ) {
\r
2379 if(boardSize < SizeMediocre) boardSize = SizePetite; else
\r
2380 if(boardSize > SizeModerate) boardSize = SizeBulky; else
\r
2381 boardSize = SizeMiddling;
\r
2384 if(!appData.useFont && boardSize == SizePetite && (v == VariantKnightmate)) boardSize = SizeMiddling; // no Unicorn in Petite
\r
2386 oldRect.left = wpMain.x; //[HGM] placement: remember previous window params
\r
2387 oldRect.top = wpMain.y;
\r
2388 oldRect.right = wpMain.x + wpMain.width;
\r
2389 oldRect.bottom = wpMain.y + wpMain.height;
\r
2391 tinyLayout = sizeInfo[boardSize].tinyLayout;
\r
2392 smallLayout = sizeInfo[boardSize].smallLayout;
\r
2393 squareSize = sizeInfo[boardSize].squareSize;
\r
2394 lineGap = sizeInfo[boardSize].lineGap;
\r
2395 minorSize = 0; /* [HGM] Kludge to see if demagnified pieces need to be shifted */
\r
2396 border = appData.useBorder && appData.border[0] ? squareSize/2 : 0;
\r
2398 // [HGM] decide on tininess based on total board width rather than square size
\r
2399 tinyLayout = squareSize * (BOARD_WIDTH);
\r
2400 tinyLayout = tinyLayout < 35*8 ? 2 : tinyLayout < 43*8 ? 1 : 0;
\r
2402 if( appData.overrideLineGap >= 0 && appData.overrideLineGap <= 5 ) {
\r
2403 lineGap = appData.overrideLineGap;
\r
2406 if (tinyLayout != oldTinyLayout) {
\r
2407 long style = GetWindowLongPtr(hwndMain, GWL_STYLE);
\r
2408 if (tinyLayout == 2) {
\r
2409 style &= ~WS_SYSMENU;
\r
2410 InsertMenu(hmenu, IDM_Exit, MF_BYCOMMAND, IDM_Minimize,
\r
2411 "&Minimize\tCtrl+F4");
\r
2413 style |= WS_SYSMENU;
\r
2414 RemoveMenu(hmenu, IDM_Minimize, MF_BYCOMMAND);
\r
2416 SetWindowLongPtr(hwndMain, GWL_STYLE, style);
\r
2418 for (i=0; menuBarText[tinyLayout][i]; i++) {
\r
2419 ModifyMenu(hmenu, i, MF_STRING|MF_BYPOSITION|MF_POPUP,
\r
2420 (UINT)GetSubMenu(hmenu, i), T_(menuBarText[tinyLayout][i]));
\r
2422 DrawMenuBar(hwndMain);
\r
2425 boardWidth = BoardWidth(boardSize, BOARD_WIDTH) + 2*border;
\r
2426 boardHeight = BoardWidth(boardSize, BOARD_HEIGHT) + 2*border;
\r
2428 /* Get text area sizes */
\r
2429 hdc = GetDC(hwndMain);
\r
2430 if (appData.clockMode) {
\r
2431 snprintf(buf, MSG_SIZ, _("White: %s"), TimeString(23*60*60*1000L));
\r
2433 snprintf(buf, MSG_SIZ, _("White"));
\r
2435 oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);
\r
2436 GetTextExtentPoint(hdc, buf, strlen(buf), &clockSize);
\r
2437 SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);
\r
2438 str = _("We only care about the height here");
\r
2439 GetTextExtentPoint(hdc, str, strlen(str), &messageSize);
\r
2440 SelectObject(hdc, oldFont);
\r
2441 ReleaseDC(hwndMain, hdc);
\r
2443 /* Compute where everything goes */
\r
2444 if((first.programLogo || second.programLogo) && tinyLayout != 2) {
\r
2445 /* [HGM] logo: if either logo is on, reserve space for it */
\r
2446 logoHeight = 2*clockSize.cy;
\r
2447 leftLogoRect.left = OUTER_MARGIN;
\r
2448 leftLogoRect.right = leftLogoRect.left + 4*clockSize.cy;
\r
2449 leftLogoRect.top = OUTER_MARGIN;
\r
2450 leftLogoRect.bottom = OUTER_MARGIN + logoHeight;
\r
2452 rightLogoRect.right = OUTER_MARGIN + boardWidth;
\r
2453 rightLogoRect.left = rightLogoRect.right - 4*clockSize.cy;
\r
2454 rightLogoRect.top = OUTER_MARGIN;
\r
2455 rightLogoRect.bottom = OUTER_MARGIN + logoHeight;
\r
2458 whiteRect.left = leftLogoRect.right;
\r
2459 whiteRect.right = OUTER_MARGIN + boardWidth/2 - INNER_MARGIN/2;
\r
2460 whiteRect.top = OUTER_MARGIN;
\r
2461 whiteRect.bottom = whiteRect.top + logoHeight;
\r
2463 blackRect.right = rightLogoRect.left;
\r
2464 blackRect.left = whiteRect.right + INNER_MARGIN;
\r
2465 blackRect.top = whiteRect.top;
\r
2466 blackRect.bottom = whiteRect.bottom;
\r
2468 whiteRect.left = OUTER_MARGIN;
\r
2469 whiteRect.right = whiteRect.left + boardWidth/2 - INNER_MARGIN/2;
\r
2470 whiteRect.top = OUTER_MARGIN;
\r
2471 whiteRect.bottom = whiteRect.top + clockSize.cy;
\r
2473 blackRect.left = whiteRect.right + INNER_MARGIN;
\r
2474 blackRect.right = blackRect.left + boardWidth/2 - 1;
\r
2475 blackRect.top = whiteRect.top;
\r
2476 blackRect.bottom = whiteRect.bottom;
\r
2478 logoHeight = 0; // [HGM] logo: suppress logo after change to tiny layout!
\r
2481 messageRect.left = OUTER_MARGIN + MESSAGE_LINE_LEFTMARGIN;
\r
2482 if (appData.showButtonBar) {
\r
2483 messageRect.right = OUTER_MARGIN + boardWidth // [HGM] logo: expressed independent of clock placement
\r
2484 - N_BUTTONS*BUTTON_WIDTH - MESSAGE_LINE_LEFTMARGIN;
\r
2486 messageRect.right = OUTER_MARGIN + boardWidth;
\r
2488 messageRect.top = whiteRect.bottom + INNER_MARGIN;
\r
2489 messageRect.bottom = messageRect.top + messageSize.cy;
\r
2491 boardRect.left = OUTER_MARGIN;
\r
2492 boardRect.right = boardRect.left + boardWidth;
\r
2493 boardRect.top = messageRect.bottom + INNER_MARGIN;
\r
2494 boardRect.bottom = boardRect.top + boardHeight;
\r
2496 sizeInfo[boardSize].cliWidth = boardRect.right + OUTER_MARGIN;
\r
2497 sizeInfo[boardSize].cliHeight = boardRect.bottom + OUTER_MARGIN;
\r
2498 oldTinyLayout = tinyLayout;
\r
2499 winW = 2 * GetSystemMetrics(SM_CXFRAME) + boardRect.right + OUTER_MARGIN;
\r
2500 winH = 2 * GetSystemMetrics(SM_CYFRAME) + GetSystemMetrics(SM_CYMENU) +
\r
2501 GetSystemMetrics(SM_CYCAPTION) + boardRect.bottom + OUTER_MARGIN;
\r
2502 winW *= 1 + twoBoards;
\r
2503 if(suppressVisibleEffects) return; // [HGM] when called for filling sizeInfo only
\r
2504 wpMain.width = winW; // [HGM] placement: set through temporary which can used by initial sizing choice
\r
2505 wpMain.height = winH; // without disturbing window attachments
\r
2506 GetWindowRect(hwndMain, &wrect);
\r
2507 SetWindowPos(hwndMain, NULL, 0, 0, wpMain.width, wpMain.height,
\r
2508 SWP_NOCOPYBITS|SWP_NOZORDER|SWP_NOMOVE);
\r
2510 // [HGM] placement: let attached windows follow size change.
\r
2511 ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, moveHistoryDialog, &wpMoveHistory );
\r
2512 ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, evalGraphDialog, &wpEvalGraph );
\r
2513 ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, engineOutputDialog, &wpEngineOutput );
\r
2514 ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, gameListDialog, &wpGameList );
\r
2515 ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, hwndConsole, &wpConsole );
\r
2517 /* compensate if menu bar wrapped */
\r
2518 GetClientRect(hwndMain, &crect);
\r
2519 offby = boardRect.bottom + OUTER_MARGIN - crect.bottom;
\r
2520 wpMain.height += offby;
\r
2522 case WMSZ_TOPLEFT:
\r
2523 SetWindowPos(hwndMain, NULL,
\r
2524 wrect.right - wpMain.width, wrect.bottom - wpMain.height,
\r
2525 wpMain.width, wpMain.height, SWP_NOCOPYBITS|SWP_NOZORDER);
\r
2528 case WMSZ_TOPRIGHT:
\r
2530 SetWindowPos(hwndMain, NULL,
\r
2531 wrect.left, wrect.bottom - wpMain.height,
\r
2532 wpMain.width, wpMain.height, SWP_NOCOPYBITS|SWP_NOZORDER);
\r
2535 case WMSZ_BOTTOMLEFT:
\r
2537 SetWindowPos(hwndMain, NULL,
\r
2538 wrect.right - wpMain.width, wrect.top,
\r
2539 wpMain.width, wpMain.height, SWP_NOCOPYBITS|SWP_NOZORDER);
\r
2542 case WMSZ_BOTTOMRIGHT:
\r
2546 SetWindowPos(hwndMain, NULL, 0, 0, wpMain.width, wpMain.height,
\r
2547 SWP_NOCOPYBITS|SWP_NOZORDER|SWP_NOMOVE);
\r
2552 for (i = 0; i < N_BUTTONS; i++) {
\r
2553 if (buttonDesc[i].hwnd != NULL) {
\r
2554 DestroyWindow(buttonDesc[i].hwnd);
\r
2555 buttonDesc[i].hwnd = NULL;
\r
2557 if (appData.showButtonBar) {
\r
2558 buttonDesc[i].hwnd =
\r
2559 CreateWindow("BUTTON", buttonDesc[i].label,
\r
2560 WS_VISIBLE | WS_CHILD | BS_PUSHBUTTON,
\r
2561 boardRect.right - BUTTON_WIDTH*(N_BUTTONS-i),
\r
2562 messageRect.top, BUTTON_WIDTH, messageSize.cy, hwndMain,
\r
2563 (HMENU) buttonDesc[i].id,
\r
2564 (HINSTANCE) GetWindowLongPtr(hwndMain, GWLP_HINSTANCE), NULL);
\r
2565 if (tinyLayout == 2) {
\r
2566 SendMessage(buttonDesc[i].hwnd, WM_SETFONT,
\r
2567 (WPARAM)font[boardSize][MESSAGE_FONT]->hf,
\r
2568 MAKELPARAM(FALSE, 0));
\r
2570 if (buttonDesc[i].id == IDM_Pause)
\r
2571 hwndPause = buttonDesc[i].hwnd;
\r
2572 buttonDesc[i].wndproc = (WNDPROC)
\r
2573 SetWindowLongPtr(buttonDesc[i].hwnd, GWLP_WNDPROC, (LONG_PTR) ButtonProc);
\r
2576 if (gridPen != NULL) DeleteObject(gridPen);
\r
2577 if (highlightPen != NULL) DeleteObject(highlightPen);
\r
2578 if (premovePen != NULL) DeleteObject(premovePen);
\r
2579 if (lineGap != 0) {
\r
2580 logbrush.lbStyle = BS_SOLID;
\r
2581 logbrush.lbColor = RGB(0, 0, 0); /* grid pen color = black */
\r
2583 ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,
\r
2584 lineGap, &logbrush, 0, NULL);
\r
2585 logbrush.lbColor = highlightSquareColor;
\r
2587 ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,
\r
2588 lineGap, &logbrush, 0, NULL);
\r
2590 logbrush.lbColor = premoveHighlightColor;
\r
2592 ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,
\r
2593 lineGap, &logbrush, 0, NULL);
\r
2595 /* [HGM] Loop had to be split in part for vert. and hor. lines */
\r
2596 for (i = 0; i < BOARD_HEIGHT + 1; i++) {
\r
2597 gridEndpoints[i*2].x = boardRect.left + lineGap / 2 + border;
\r
2598 gridEndpoints[i*2].y = gridEndpoints[i*2 + 1].y =
\r
2599 boardRect.top + lineGap / 2 + (i * (squareSize + lineGap)) + border;
\r
2600 gridEndpoints[i*2 + 1].x = boardRect.left + lineGap / 2 +
\r
2601 BOARD_WIDTH * (squareSize + lineGap) + border;
\r
2602 gridVertexCounts[i*2] = gridVertexCounts[i*2 + 1] = 2;
\r
2604 for (i = 0; i < BOARD_WIDTH + 1; i++) {
\r
2605 gridEndpoints[i*2 + BOARD_HEIGHT*2 + 2].y = boardRect.top + lineGap / 2 + border;
\r
2606 gridEndpoints[i*2 + BOARD_HEIGHT*2 + 2].x =
\r
2607 gridEndpoints[i*2 + 1 + BOARD_HEIGHT*2 + 2].x = boardRect.left +
\r
2608 lineGap / 2 + (i * (squareSize + lineGap)) + border;
\r
2609 gridEndpoints[i*2 + 1 + BOARD_HEIGHT*2 + 2].y =
\r
2610 boardRect.top + BOARD_HEIGHT * (squareSize + lineGap) + border;
\r
2611 gridVertexCounts[i*2] = gridVertexCounts[i*2 + 1] = 2;
\r
2615 /* [HGM] Licensing requirement */
\r
2617 if(gameInfo.variant == VariantGothic) GothicPopUp( GOTHIC, VariantGothic); else
\r
2620 if(gameInfo.variant == VariantFalcon) GothicPopUp( FALCON, VariantFalcon); else
\r
2622 GothicPopUp( "", VariantNormal);
\r
2625 /* if (boardSize == oldBoardSize) return; [HGM] variant might have changed */
\r
2627 /* Load piece bitmaps for this board size */
\r
2628 for (i=0; i<=2; i++) {
\r
2629 for (piece = WhitePawn;
\r
2630 (int) piece < (int) BlackPawn;
\r
2631 piece = (ChessSquare) ((int) piece + 1)) {
\r
2632 if (pieceBitmap[i][piece] != NULL)
\r
2633 DeleteObject(pieceBitmap[i][piece]);
\r
2634 pieceBitmap[i][piece] = NULL;
\r
2638 fontBitmapSquareSize = 0; /* [HGM] render: make sure pieces will be recreated, as we might need others now */
\r
2640 // Orthodox Chess pieces
\r
2641 pieceBitmap[0][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "s");
\r
2642 pieceBitmap[0][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "s");
\r
2643 pieceBitmap[0][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "s");
\r
2644 pieceBitmap[0][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "s");
\r
2645 pieceBitmap[0][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "s");
\r
2646 pieceBitmap[1][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "o");
\r
2647 pieceBitmap[1][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "o");
\r
2648 pieceBitmap[1][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "o");
\r
2649 pieceBitmap[1][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "o");
\r
2650 pieceBitmap[1][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "o");
\r
2651 pieceBitmap[2][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "w");
\r
2652 pieceBitmap[2][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "w");
\r
2653 pieceBitmap[2][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "w");
\r
2654 pieceBitmap[2][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "w");
\r
2655 pieceBitmap[2][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "w");
\r
2656 if( gameInfo.variant == VariantShogi && squareSize <= 72 && squareSize >= 33) {
\r
2657 // in Shogi, Hijack the unused Queen for Lance
\r
2658 pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "s");
\r
2659 pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "o");
\r
2660 pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "w");
\r
2662 pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "s");
\r
2663 pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "o");
\r
2664 pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "w");
\r
2667 if(squareSize <= 72 && squareSize >= 33) {
\r
2668 /* A & C are available in most sizes now */
\r
2669 if(squareSize != 49 && squareSize != 72 && squareSize != 33) { // Vortex-like
\r
2670 pieceBitmap[0][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "s");
\r
2671 pieceBitmap[1][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "o");
\r
2672 pieceBitmap[2][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "w");
\r
2673 pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "s");
\r
2674 pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "o");
\r
2675 pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "w");
\r
2676 pieceBitmap[0][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "s");
\r
2677 pieceBitmap[1][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "o");
\r
2678 pieceBitmap[2][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "w");
\r
2679 pieceBitmap[0][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "s");
\r
2680 pieceBitmap[1][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "o");
\r
2681 pieceBitmap[2][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "w");
\r
2682 } else { // Smirf-like
\r
2683 if(gameInfo.variant == VariantSChess) {
\r
2684 pieceBitmap[0][WhiteAngel] = DoLoadBitmap(hInst, "v", squareSize, "s");
\r
2685 pieceBitmap[1][WhiteAngel] = DoLoadBitmap(hInst, "v", squareSize, "o");
\r
2686 pieceBitmap[2][WhiteAngel] = DoLoadBitmap(hInst, "v", squareSize, "w");
\r
2688 pieceBitmap[0][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "s");
\r
2689 pieceBitmap[1][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "o");
\r
2690 pieceBitmap[2][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "w");
\r
2693 if(gameInfo.variant == VariantGothic) { // Vortex-like
\r
2694 pieceBitmap[0][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "s");
\r
2695 pieceBitmap[1][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "o");
\r
2696 pieceBitmap[2][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "w");
\r
2697 } else if(gameInfo.variant == VariantSChess && (squareSize == 49 || squareSize == 72)) {
\r
2698 pieceBitmap[0][WhiteMarshall] = DoLoadBitmap(hInst, "e", squareSize, "s");
\r
2699 pieceBitmap[1][WhiteMarshall] = DoLoadBitmap(hInst, "e", squareSize, "o");
\r
2700 pieceBitmap[2][WhiteMarshall] = DoLoadBitmap(hInst, "e", squareSize, "w");
\r
2701 } else { // WinBoard standard
\r
2702 pieceBitmap[0][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "s");
\r
2703 pieceBitmap[1][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "o");
\r
2704 pieceBitmap[2][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "w");
\r
2709 if(squareSize==72 || squareSize==49 || squareSize==33) { /* experiment with some home-made bitmaps */
\r
2710 pieceBitmap[0][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "s");
\r
2711 pieceBitmap[1][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "o");
\r
2712 pieceBitmap[2][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "w");
\r
2713 pieceBitmap[0][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "s");
\r
2714 pieceBitmap[1][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "o");
\r
2715 pieceBitmap[2][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "w");
\r
2716 pieceBitmap[0][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "s");
\r
2717 pieceBitmap[1][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "o");
\r
2718 pieceBitmap[2][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "w");
\r
2719 pieceBitmap[0][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "s");
\r
2720 pieceBitmap[1][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "o");
\r
2721 pieceBitmap[2][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "w");
\r
2722 pieceBitmap[0][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "s");
\r
2723 pieceBitmap[1][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "o");
\r
2724 pieceBitmap[2][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "w");
\r
2725 pieceBitmap[0][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "s");
\r
2726 pieceBitmap[1][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "o");
\r
2727 pieceBitmap[2][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "w");
\r
2728 pieceBitmap[0][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "s");
\r
2729 pieceBitmap[1][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "o");
\r
2730 pieceBitmap[2][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "w");
\r
2731 pieceBitmap[0][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "s");
\r
2732 pieceBitmap[1][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "o");
\r
2733 pieceBitmap[2][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "w");
\r
2734 pieceBitmap[0][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "s");
\r
2735 pieceBitmap[1][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "o");
\r
2736 pieceBitmap[2][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "w");
\r
2737 pieceBitmap[0][WhiteAmazon] = DoLoadBitmap(hInst, "l", squareSize, "s");
\r
2738 pieceBitmap[1][WhiteAmazon] = DoLoadBitmap(hInst, "l", squareSize, "o");
\r
2739 pieceBitmap[2][WhiteAmazon] = DoLoadBitmap(hInst, "l", squareSize, "w");
\r
2740 pieceBitmap[0][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "s");
\r
2741 pieceBitmap[1][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "o");
\r
2742 pieceBitmap[2][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "w");
\r
2743 pieceBitmap[0][WhiteLion] = DoLoadBitmap(hInst, "ln", squareSize, "s");
\r
2744 pieceBitmap[1][WhiteLion] = DoLoadBitmap(hInst, "ln", squareSize, "o");
\r
2745 pieceBitmap[2][WhiteLion] = DoLoadBitmap(hInst, "ln", squareSize, "w");
\r
2746 pieceBitmap[0][WhiteCub] = DoLoadBitmap(hInst, "ln", squareSize, "s");
\r
2747 pieceBitmap[1][WhiteCub] = DoLoadBitmap(hInst, "ln", squareSize, "o");
\r
2748 pieceBitmap[2][WhiteCub] = DoLoadBitmap(hInst, "ln", squareSize, "w");
\r
2749 pieceBitmap[0][WhiteWolf] = DoLoadBitmap(hInst, "wolf", squareSize, "s");
\r
2750 pieceBitmap[1][WhiteWolf] = DoLoadBitmap(hInst, "wolf", squareSize, "o");
\r
2751 pieceBitmap[2][WhiteWolf] = DoLoadBitmap(hInst, "wolf", squareSize, "w");
\r
2752 pieceBitmap[0][WhiteCamel] = DoLoadBitmap(hInst, "camel", squareSize, "s");
\r
2753 pieceBitmap[1][WhiteCamel] = DoLoadBitmap(hInst, "camel", squareSize, "o");
\r
2754 pieceBitmap[2][WhiteCamel] = DoLoadBitmap(hInst, "camel", squareSize, "w");
\r
2755 pieceBitmap[0][WhiteZebra] = DoLoadBitmap(hInst, "zebra", squareSize, "s");
\r
2756 pieceBitmap[1][WhiteZebra] = DoLoadBitmap(hInst, "zebra", squareSize, "o");
\r
2757 pieceBitmap[2][WhiteZebra] = DoLoadBitmap(hInst, "n", squareSize, "w");
\r
2758 pieceBitmap[0][WhiteTower] = DoLoadBitmap(hInst, "tower", squareSize, "s");
\r
2759 pieceBitmap[1][WhiteTower] = DoLoadBitmap(hInst, "tower", squareSize, "o");
\r
2760 pieceBitmap[2][WhiteTower] = DoLoadBitmap(hInst, "tower", squareSize, "w");
\r
2761 pieceBitmap[0][WhiteSword] = DoLoadBitmap(hInst, "sword", squareSize, "s");
\r
2762 pieceBitmap[1][WhiteSword] = DoLoadBitmap(hInst, "sword", squareSize, "o");
\r
2763 pieceBitmap[2][WhiteSword] = DoLoadBitmap(hInst, "sword", squareSize, "w");
\r
2764 pieceBitmap[0][WhiteGnu] = DoLoadBitmap(hInst, "gnu", squareSize, "s");
\r
2765 pieceBitmap[1][WhiteGnu] = DoLoadBitmap(hInst, "gnu", squareSize, "o");
\r
2766 pieceBitmap[2][WhiteGnu] = DoLoadBitmap(hInst, "gnu", squareSize, "w");
\r
2768 if(gameInfo.variant == VariantShogi && BOARD_HEIGHT != 7) { /* promoted Gold representations (but not in Tori!)*/
\r
2769 pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "wp", squareSize, "s");
\r
2770 pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "wp", squareSize, "o");
\r
2771 pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "w", squareSize, "w");
\r
2772 pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "wn", squareSize, "s");
\r
2773 pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "wn", squareSize, "o");
\r
2774 pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "w", squareSize, "w");
\r
2775 pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "ws", squareSize, "s");
\r
2776 pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "ws", squareSize, "o");
\r
2777 pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "w", squareSize, "w");
\r
2778 pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "wl", squareSize, "s");
\r
2779 pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "wl", squareSize, "o");
\r
2780 pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "w", squareSize, "w");
\r
2782 pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "s");
\r
2783 pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "o");
\r
2784 pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "w");
\r
2785 pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "s");
\r
2786 pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "o");
\r
2787 pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "w");
\r
2788 pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "s");
\r
2789 pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "o");
\r
2790 pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "w");
\r
2791 pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "s");
\r
2792 pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "o");
\r
2793 pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "w");
\r
2796 } else { /* other size, no special bitmaps available. Use smaller symbols */
\r
2797 if((int)boardSize < 2) minorSize = sizeInfo[0].squareSize;
\r
2798 else minorSize = sizeInfo[(int)boardSize - 2].squareSize;
\r
2799 pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "s");
\r
2800 pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "o");
\r
2801 pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "w");
\r
2802 pieceBitmap[0][WhiteCardinal] = DoLoadBitmap(hInst, "b", minorSize, "s");
\r
2803 pieceBitmap[1][WhiteCardinal] = DoLoadBitmap(hInst, "b", minorSize, "o");
\r
2804 pieceBitmap[2][WhiteCardinal] = DoLoadBitmap(hInst, "b", minorSize, "w");
\r
2805 pieceBitmap[0][WhiteDragon] = DoLoadBitmap(hInst, "r", minorSize, "s");
\r
2806 pieceBitmap[1][WhiteDragon] = DoLoadBitmap(hInst, "r", minorSize, "o");
\r
2807 pieceBitmap[2][WhiteDragon] = DoLoadBitmap(hInst, "r", minorSize, "w");
\r
2808 pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "s");
\r
2809 pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "o");
\r
2810 pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "w");
\r
2814 if(gameInfo.variant == VariantShogi && squareSize == 58)
\r
2815 /* special Shogi support in this size */
\r
2816 { for (i=0; i<=2; i++) { /* replace all bitmaps */
\r
2817 for (piece = WhitePawn;
\r
2818 (int) piece < (int) BlackPawn;
\r
2819 piece = (ChessSquare) ((int) piece + 1)) {
\r
2820 if (pieceBitmap[i][piece] != NULL)
\r
2821 DeleteObject(pieceBitmap[i][piece]);
\r
2824 pieceBitmap[0][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "o");
\r
2825 pieceBitmap[0][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "o");
\r
2826 pieceBitmap[0][WhiteBishop] = DoLoadBitmap(hInst, "sb", squareSize, "o");
\r
2827 pieceBitmap[0][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "o");
\r
2828 pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "o");
\r
2829 pieceBitmap[0][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "o");
\r
2830 pieceBitmap[0][WhiteFerz] = DoLoadBitmap(hInst, "sf", squareSize, "o");
\r
2831 pieceBitmap[0][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "o");
\r
2832 pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "su", squareSize, "o");
\r
2833 pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "sh", squareSize, "o");
\r
2834 pieceBitmap[0][WhiteCardinal] = DoLoadBitmap(hInst, "sa", squareSize, "o");
\r
2835 pieceBitmap[0][WhiteDragon] = DoLoadBitmap(hInst, "sc", squareSize, "o");
\r
2836 pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "sg", squareSize, "o");
\r
2837 pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "ss", squareSize, "o");
\r
2838 pieceBitmap[1][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "o");
\r
2839 pieceBitmap[1][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "o");
\r
2840 pieceBitmap[1][WhiteBishop] = DoLoadBitmap(hInst, "sb", squareSize, "o");
\r
2841 pieceBitmap[1][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "o");
\r
2842 pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "o");
\r
2843 pieceBitmap[1][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "o");
\r
2844 pieceBitmap[1][WhiteFerz] = DoLoadBitmap(hInst, "sf", squareSize, "o");
\r
2845 pieceBitmap[1][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "o");
\r
2846 pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "su", squareSize, "o");
\r
2847 pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "sh", squareSize, "o");
\r
2848 pieceBitmap[1][WhiteCardinal] = DoLoadBitmap(hInst, "sa", squareSize, "o");
\r
2849 pieceBitmap[1][WhiteDragon] = DoLoadBitmap(hInst, "sc", squareSize, "o");
\r
2850 pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "sg", squareSize, "o");
\r
2851 pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "ss", squareSize, "o");
\r
2852 pieceBitmap[2][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "w");
\r
2853 pieceBitmap[2][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "w");
\r
2854 pieceBitmap[2][WhiteBishop] = DoLoadBitmap(hInst, "sr", squareSize, "w");
\r
2855 pieceBitmap[2][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "w");
\r
2856 pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "w");
\r
2857 pieceBitmap[2][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "w");
\r
2858 pieceBitmap[2][WhiteFerz] = DoLoadBitmap(hInst, "sw", squareSize, "w");
\r
2859 pieceBitmap[2][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "w");
\r
2860 pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "sp", squareSize, "w");
\r
2861 pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "sn", squareSize, "w");
\r
2862 pieceBitmap[2][WhiteCardinal] = DoLoadBitmap(hInst, "sr", squareSize, "w");
\r
2863 pieceBitmap[2][WhiteDragon] = DoLoadBitmap(hInst, "sr", squareSize, "w");
\r
2864 pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "sl", squareSize, "w");
\r
2865 pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "sw", squareSize, "w");
\r
2869 if(appData.pieceDirectory[0]) for(i=WhitePawn; i<BlackPawn; i++) { // try for all missing pieces with new naming convention
\r
2870 char buf[MSG_SIZ];
\r
2871 if(pieceBitmap[0][i]) continue;
\r
2872 snprintf(buf, MSG_SIZ, "piece%d_", i);
\r
2873 pieceBitmap[0][i] = DoLoadBitmap(hInst, buf, squareSize, "s");
\r
2874 pieceBitmap[1][i] = DoLoadBitmap(hInst, buf, squareSize, "o");
\r
2875 pieceBitmap[2][i] = DoLoadBitmap(hInst, buf, squareSize, "w");
\r
2880 PieceBitmap(ChessSquare p, int kind)
\r
2882 if ((int) p >= (int) BlackPawn)
\r
2883 p = (ChessSquare) ((int) p - (int) BlackPawn + (int) WhitePawn);
\r
2885 return pieceBitmap[kind][(int) p];
\r
2888 /***************************************************************/
\r
2890 #define MIN(a,b) (((a) < (b)) ? (a) : (b))
\r
2891 #define MAX(a,b) (((a) > (b)) ? (a) : (b))
\r
2893 #define MIN3(a,b,c) (((a) < (b) && (a) < (c)) ? (a) : (((b) < (a) && (b) < (c)) ? (b) : (c)))
\r
2894 #define MAX3(a,b,c) (((a) > (b) && (a) > (c)) ? (a) : (((b) > (a) && (b) > (c)) ? (b) : (c)))
\r
2898 SquareToPos(int row, int column, int * x, int * y)
\r
2901 *x = boardRect.left + lineGap + ((BOARD_WIDTH-1)-column) * (squareSize + lineGap) + border;
\r
2902 *y = boardRect.top + lineGap + row * (squareSize + lineGap) + border;
\r
2904 *x = boardRect.left + lineGap + column * (squareSize + lineGap) + border;
\r
2905 *y = boardRect.top + lineGap + ((BOARD_HEIGHT-1)-row) * (squareSize + lineGap) + border;
\r
2910 DrawCoordsOnDC(HDC hdc)
\r
2912 static char files[] = "0123456789012345678901221098765432109876543210";
\r
2913 static char ranks[] = "wvutsrqponmlkjihgfedcbaabcdefghijklmnopqrstuvw";
\r
2914 char str[2] = { NULLCHAR, NULLCHAR };
\r
2915 int oldMode, oldAlign, x, y, start, i;
\r
2919 if (!appData.showCoords)
\r
2922 start = flipView ? 1-(ONE!='1') : 45+(ONE!='1')-BOARD_HEIGHT;
\r
2924 oldBrush = SelectObject(hdc, GetStockObject(BLACK_BRUSH));
\r
2925 oldMode = SetBkMode(hdc, (appData.monoMode ? OPAQUE : TRANSPARENT));
\r
2926 oldAlign = GetTextAlign(hdc);
\r
2927 oldFont = SelectObject(hdc, font[boardSize][COORD_FONT]->hf);
\r
2929 y = boardRect.top + lineGap;
\r
2930 x = boardRect.left + lineGap + gameInfo.holdingsWidth*(squareSize + lineGap);
\r
2933 SetTextAlign(hdc, TA_RIGHT|TA_TOP);
\r
2934 x += border - lineGap - 4; y += squareSize - 6;
\r
2936 SetTextAlign(hdc, TA_LEFT|TA_TOP);
\r
2937 for (i = 0; i < BOARD_HEIGHT; i++) {
\r
2938 str[0] = files[start + i];
\r
2939 ExtTextOut(hdc, x + 2 - (border ? gameInfo.holdingsWidth * (squareSize + lineGap) : 0), y + 1, 0, NULL, str, 1, NULL);
\r
2940 y += squareSize + lineGap;
\r
2943 start = flipView ? 23-(BOARD_RGHT-BOARD_LEFT) : 23;
\r
2946 SetTextAlign(hdc, TA_LEFT|TA_TOP);
\r
2947 x += -border + 4; y += border - squareSize + 6;
\r
2949 SetTextAlign(hdc, TA_RIGHT|TA_BOTTOM);
\r
2950 for (i = 0; i < BOARD_RGHT - BOARD_LEFT; i++) {
\r
2951 str[0] = ranks[start + i];
\r
2952 ExtTextOut(hdc, x + squareSize - 2, y - 1, 0, NULL, str, 1, NULL);
\r
2953 x += squareSize + lineGap;
\r
2956 SelectObject(hdc, oldBrush);
\r
2957 SetBkMode(hdc, oldMode);
\r
2958 SetTextAlign(hdc, oldAlign);
\r
2959 SelectObject(hdc, oldFont);
\r
2963 DrawGridOnDC(HDC hdc)
\r
2967 if (lineGap != 0) {
\r
2968 oldPen = SelectObject(hdc, gridPen);
\r
2969 PolyPolyline(hdc, gridEndpoints, gridVertexCounts, BOARD_WIDTH+BOARD_HEIGHT + 2);
\r
2970 SelectObject(hdc, oldPen);
\r
2974 #define HIGHLIGHT_PEN 0
\r
2975 #define PREMOVE_PEN 1
\r
2978 DrawHighlightOnDC(HDC hdc, BOOLEAN on, int x, int y, int pen)
\r
2981 HPEN oldPen, hPen;
\r
2982 if (lineGap == 0) return;
\r
2984 x1 = boardRect.left +
\r
2985 lineGap/2 + ((BOARD_WIDTH-1)-x) * (squareSize + lineGap) + border;
\r
2986 y1 = boardRect.top +
\r
2987 lineGap/2 + y * (squareSize + lineGap) + border;
\r
2989 x1 = boardRect.left +
\r
2990 lineGap/2 + x * (squareSize + lineGap) + border;
\r
2991 y1 = boardRect.top +
\r
2992 lineGap/2 + ((BOARD_HEIGHT-1)-y) * (squareSize + lineGap) + border;
\r
2994 hPen = pen ? premovePen : highlightPen;
\r
2995 oldPen = SelectObject(hdc, on ? hPen : gridPen);
\r
2996 MoveToEx(hdc, x1, y1, NULL);
\r
2997 LineTo(hdc, x1 + squareSize + lineGap, y1);
\r
2998 LineTo(hdc, x1 + squareSize + lineGap, y1 + squareSize + lineGap);
\r
2999 LineTo(hdc, x1, y1 + squareSize + lineGap);
\r
3000 LineTo(hdc, x1, y1);
\r
3001 SelectObject(hdc, oldPen);
\r
3005 DrawHighlightsOnDC(HDC hdc, HighlightInfo *h, int pen)
\r
3008 for (i=0; i<2; i++) {
\r
3009 if (h->sq[i].x >= 0 && h->sq[i].y >= 0)
\r
3010 DrawHighlightOnDC(hdc, TRUE,
\r
3011 h->sq[i].x, h->sq[i].y,
\r
3016 /* Note: sqcolor is used only in monoMode */
\r
3017 /* Note that this code is largely duplicated in woptions.c,
\r
3018 function DrawSampleSquare, so that needs to be updated too */
\r
3020 DrawPieceOnDC(HDC hdc, ChessSquare piece, int color, int sqcolor, int x, int y, HDC tmphdc)
\r
3022 HBITMAP oldBitmap;
\r
3023 HBRUSH oldBrush = NULL;
\r
3026 if (appData.blindfold) return;
\r
3028 /* [AS] Use font-based pieces if needed */
\r
3029 if( fontBitmapSquareSize >= 0 && (squareSize > 32 || gameInfo.variant >= VariantShogi)) {
\r
3030 /* Create piece bitmaps, or do nothing if piece set is up to date */
\r
3031 CreatePiecesFromFont();
\r
3033 if( fontBitmapSquareSize == squareSize ) {
\r
3034 int index = TranslatePieceToFontPiece(piece);
\r
3036 SelectObject( tmphdc, hPieceMask[ index ] );
\r
3038 if(appData.upsideDown ? color==flipView : (flipView && gameInfo.variant == VariantShogi))
\r
3039 StretchBlt(hdc, x+squareSize, y+squareSize, -squareSize, -squareSize, tmphdc, 0, 0, squareSize, squareSize, SRCAND);
\r
3043 squareSize, squareSize,
\r
3048 SelectObject( tmphdc, hPieceFace[ index ] );
\r
3050 if(appData.upsideDown ? color==flipView : (flipView && gameInfo.variant == VariantShogi))
\r
3051 StretchBlt(hdc, x+squareSize, y+squareSize, -squareSize, -squareSize, tmphdc, 0, 0, squareSize, squareSize, SRCPAINT);
\r
3055 squareSize, squareSize,
\r
3064 if (appData.monoMode) {
\r
3065 SelectObject(tmphdc, PieceBitmap(piece,
\r
3066 color == sqcolor ? OUTLINE_PIECE : SOLID_PIECE));
\r
3067 BitBlt(hdc, x, y, squareSize, squareSize, tmphdc, 0, 0,
\r
3068 sqcolor ? SRCCOPY : NOTSRCCOPY);
\r
3070 HBRUSH xBrush = whitePieceBrush;
\r
3071 tmpSize = squareSize;
\r
3072 if(appData.pieceDirectory[0]) xBrush = GetStockObject(WHITE_BRUSH);
\r
3074 ((piece >= (int)WhiteNightrider && piece <= WhiteGrasshopper) ||
\r
3075 (piece >= (int)BlackNightrider && piece <= BlackGrasshopper)) ) {
\r
3076 /* [HGM] no bitmap available for promoted pieces in Crazyhouse */
\r
3077 /* Bitmaps of smaller size are substituted, but we have to align them */
\r
3078 x += (squareSize - minorSize)>>1;
\r
3079 y += squareSize - minorSize - 2;
\r
3080 tmpSize = minorSize;
\r
3082 #if WINVER >= 0x0500
\r
3083 HBITMAP pbm = PieceBitmap(piece, color ? OUTLINE_PIECE : SOLID_PIECE);
\r
3085 GetObject(pbm, sizeof(BITMAP), &b);
\r
3086 if(b.bmBitsPixel == 32) { // for now this is a kludge to indicate bitmaps with alpha channel
\r
3088 bf.BlendOp = AC_SRC_OVER;
\r
3089 bf.BlendFlags = 0;
\r
3090 bf.SourceConstantAlpha = 0xFF;
\r
3091 bf.AlphaFormat = AC_SRC_ALPHA;
\r
3092 oldBitmap = SelectObject(tmphdc, pbm);
\r
3093 AlphaBlend(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, bf);
\r
3096 if (color || appData.allWhite ) {
\r
3097 oldBitmap = SelectObject(tmphdc, PieceBitmap(piece, WHITE_PIECE));
\r
3099 oldBrush = SelectObject(hdc, xBrush);
\r
3100 else oldBrush = SelectObject(hdc, blackPieceBrush);
\r
3101 if(appData.upsideDown && color==flipView)
\r
3102 StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, 0x00B8074A);
\r
3104 BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, 0x00B8074A);
\r
3105 /* Use black for outline of white pieces */
\r
3106 SelectObject(tmphdc, PieceBitmap(piece, OUTLINE_PIECE));
\r
3107 if(appData.upsideDown && color==flipView)
\r
3108 StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, SRCAND);
\r
3110 BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, SRCAND);
\r
3111 } else if(appData.pieceDirectory[0]) {
\r
3112 oldBitmap = SelectObject(tmphdc, PieceBitmap(piece, WHITE_PIECE));
\r
3113 oldBrush = SelectObject(hdc, xBrush);
\r
3114 if(appData.upsideDown && color==flipView)
\r
3115 StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, 0x00B8074A);
\r
3117 BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, 0x00B8074A);
\r
3118 SelectObject(tmphdc, PieceBitmap(piece, SOLID_PIECE));
\r
3119 if(appData.upsideDown && color==flipView)
\r
3120 StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, SRCAND);
\r
3122 BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, SRCAND);
\r
3124 /* Use square color for details of black pieces */
\r
3125 oldBitmap = SelectObject(tmphdc, PieceBitmap(piece, SOLID_PIECE));
\r
3126 oldBrush = SelectObject(hdc, blackPieceBrush);
\r
3127 if(appData.upsideDown && !flipView)
\r
3128 StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, 0x00B8074A);
\r
3130 BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, 0x00B8074A);
\r
3132 if(oldBrush) SelectObject(hdc, oldBrush);
\r
3133 SelectObject(tmphdc, oldBitmap);
\r
3137 /* [AS] Compute a drawing mode for a square, based on specified settings (see DrawTile) */
\r
3138 int GetBackTextureMode( int algo )
\r
3140 int result = BACK_TEXTURE_MODE_DISABLED;
\r
3144 case BACK_TEXTURE_MODE_PLAIN:
\r
3145 result = 1; /* Always use identity map */
\r
3147 case BACK_TEXTURE_MODE_FULL_RANDOM:
\r
3148 result = 1 + (myrandom() % 3); /* Pick a transformation at random */
\r
3156 [AS] Compute and save texture drawing info, otherwise we may not be able
\r
3157 to handle redraws cleanly (as random numbers would always be different).
\r
3159 VOID RebuildTextureSquareInfo()
\r
3169 ZeroMemory( &backTextureSquareInfo, sizeof(backTextureSquareInfo) );
\r
3171 if( liteBackTexture != NULL ) {
\r
3172 if( GetObject( liteBackTexture, sizeof(bi), &bi ) > 0 ) {
\r
3173 lite_w = bi.bmWidth;
\r
3174 lite_h = bi.bmHeight;
\r
3178 if( darkBackTexture != NULL ) {
\r
3179 if( GetObject( darkBackTexture, sizeof(bi), &bi ) > 0 ) {
\r
3180 dark_w = bi.bmWidth;
\r
3181 dark_h = bi.bmHeight;
\r
3185 for( row=0; row<BOARD_HEIGHT; row++ ) {
\r
3186 for( col=0; col<BOARD_WIDTH; col++ ) {
\r
3187 if( (col + row) & 1 ) {
\r
3189 if( lite_w >= squareSize && lite_h >= squareSize ) {
\r
3190 if( lite_w >= squareSize*BOARD_WIDTH )
\r
3191 backTextureSquareInfo[row][col].x = (2*col+1)*lite_w/(2*BOARD_WIDTH) - squareSize/2; /* [HGM] cut out of center of virtual square */
\r
3193 backTextureSquareInfo[row][col].x = col * (lite_w - squareSize) / (BOARD_WIDTH-1); /* [HGM] divide by size-1 in stead of size! */
\r
3194 if( lite_h >= squareSize*BOARD_HEIGHT )
\r
3195 backTextureSquareInfo[row][col].y = (2*(BOARD_HEIGHT-row)-1)*lite_h/(2*BOARD_HEIGHT) - squareSize/2;
\r
3197 backTextureSquareInfo[row][col].y = (BOARD_HEIGHT-1-row) * (lite_h - squareSize) / (BOARD_HEIGHT-1);
\r
3198 backTextureSquareInfo[row][col].mode = GetBackTextureMode(liteBackTextureMode);
\r
3203 if( dark_w >= squareSize && dark_h >= squareSize ) {
\r
3204 if( dark_w >= squareSize*BOARD_WIDTH )
\r
3205 backTextureSquareInfo[row][col].x = (2*col+1) * dark_w / (2*BOARD_WIDTH) - squareSize/2;
\r
3207 backTextureSquareInfo[row][col].x = col * (dark_w - squareSize) / (BOARD_WIDTH-1);
\r
3208 if( dark_h >= squareSize*BOARD_HEIGHT )
\r
3209 backTextureSquareInfo[row][col].y = (2*(BOARD_HEIGHT-row)-1) * dark_h / (2*BOARD_HEIGHT) - squareSize/2;
\r
3211 backTextureSquareInfo[row][col].y = (BOARD_HEIGHT-1-row) * (dark_h - squareSize) / (BOARD_HEIGHT-1);
\r
3212 backTextureSquareInfo[row][col].mode = GetBackTextureMode(darkBackTextureMode);
\r
3219 /* [AS] Arrow highlighting support */
\r
3221 static double A_WIDTH = 5; /* Width of arrow body */
\r
3223 #define A_HEIGHT_FACTOR 6 /* Length of arrow "point", relative to body width */
\r
3224 #define A_WIDTH_FACTOR 3 /* Width of arrow "point", relative to body width */
\r
3226 static double Sqr( double x )
\r
3231 static int Round( double x )
\r
3233 return (int) (x + 0.5);
\r
3236 /* Draw an arrow between two points using current settings */
\r
3237 VOID DrawArrowBetweenPoints( HDC hdc, int s_x, int s_y, int d_x, int d_y )
\r
3240 double dx, dy, j, k, x, y;
\r
3242 if( d_x == s_x ) {
\r
3243 int h = (d_y > s_y) ? +A_WIDTH*A_HEIGHT_FACTOR : -A_WIDTH*A_HEIGHT_FACTOR;
\r
3245 arrow[0].x = s_x + A_WIDTH + 0.5;
\r
3248 arrow[1].x = s_x + A_WIDTH + 0.5;
\r
3249 arrow[1].y = d_y - h;
\r
3251 arrow[2].x = arrow[1].x + A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;
\r
3252 arrow[2].y = d_y - h;
\r
3257 arrow[5].x = arrow[1].x - 2*A_WIDTH + 0.5;
\r
3258 arrow[5].y = d_y - h;
\r
3260 arrow[4].x = arrow[5].x - A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;
\r
3261 arrow[4].y = d_y - h;
\r
3263 arrow[6].x = arrow[1].x - 2*A_WIDTH + 0.5;
\r
3266 else if( d_y == s_y ) {
\r
3267 int w = (d_x > s_x) ? +A_WIDTH*A_HEIGHT_FACTOR : -A_WIDTH*A_HEIGHT_FACTOR;
\r
3270 arrow[0].y = s_y + A_WIDTH + 0.5;
\r
3272 arrow[1].x = d_x - w;
\r
3273 arrow[1].y = s_y + A_WIDTH + 0.5;
\r
3275 arrow[2].x = d_x - w;
\r
3276 arrow[2].y = arrow[1].y + A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;
\r
3281 arrow[5].x = d_x - w;
\r
3282 arrow[5].y = arrow[1].y - 2*A_WIDTH + 0.5;
\r
3284 arrow[4].x = d_x - w;
\r
3285 arrow[4].y = arrow[5].y - A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;
\r
3288 arrow[6].y = arrow[1].y - 2*A_WIDTH + 0.5;
\r
3291 /* [AS] Needed a lot of paper for this! :-) */
\r
3292 dy = (double) (d_y - s_y) / (double) (d_x - s_x);
\r
3293 dx = (double) (s_x - d_x) / (double) (s_y - d_y);
\r
3295 j = sqrt( Sqr(A_WIDTH) / (1.0 + Sqr(dx)) );
\r
3297 k = sqrt( Sqr(A_WIDTH*A_HEIGHT_FACTOR) / (1.0 + Sqr(dy)) );
\r
3302 arrow[0].x = Round(x - j);
\r
3303 arrow[0].y = Round(y + j*dx);
\r
3305 arrow[1].x = Round(arrow[0].x + 2*j); // [HGM] prevent width to be affected by rounding twice
\r
3306 arrow[1].y = Round(arrow[0].y - 2*j*dx);
\r
3309 x = (double) d_x - k;
\r
3310 y = (double) d_y - k*dy;
\r
3313 x = (double) d_x + k;
\r
3314 y = (double) d_y + k*dy;
\r
3317 x = Round(x); y = Round(y); // [HGM] make sure width of shaft is rounded the same way on both ends
\r
3319 arrow[6].x = Round(x - j);
\r
3320 arrow[6].y = Round(y + j*dx);
\r
3322 arrow[2].x = Round(arrow[6].x + 2*j);
\r
3323 arrow[2].y = Round(arrow[6].y - 2*j*dx);
\r
3325 arrow[3].x = Round(arrow[2].x + j*(A_WIDTH_FACTOR-1));
\r
3326 arrow[3].y = Round(arrow[2].y - j*(A_WIDTH_FACTOR-1)*dx);
\r
3331 arrow[5].x = Round(arrow[6].x - j*(A_WIDTH_FACTOR-1));
\r
3332 arrow[5].y = Round(arrow[6].y + j*(A_WIDTH_FACTOR-1)*dx);
\r
3335 Polygon( hdc, arrow, 7 );
\r
3338 /* [AS] Draw an arrow between two squares */
\r
3339 VOID DrawArrowBetweenSquares( HDC hdc, int s_col, int s_row, int d_col, int d_row )
\r
3341 int s_x, s_y, d_x, d_y;
\r
3348 if( s_col == d_col && s_row == d_row ) {
\r
3352 /* Get source and destination points */
\r
3353 SquareToPos( s_row, s_col, &s_x, &s_y);
\r
3354 SquareToPos( d_row, d_col, &d_x, &d_y);
\r
3357 d_y += squareSize / 2 - squareSize / 4; // [HGM] round towards same centers on all sides!
\r
3359 else if( d_y < s_y ) {
\r
3360 d_y += squareSize / 2 + squareSize / 4;
\r
3363 d_y += squareSize / 2;
\r
3367 d_x += squareSize / 2 - squareSize / 4;
\r
3369 else if( d_x < s_x ) {
\r
3370 d_x += squareSize / 2 + squareSize / 4;
\r
3373 d_x += squareSize / 2;
\r
3376 s_x += squareSize / 2;
\r
3377 s_y += squareSize / 2;
\r
3379 /* Adjust width */
\r
3380 A_WIDTH = squareSize / 14.; //[HGM] make float
\r
3383 stLB.lbStyle = BS_SOLID;
\r
3384 stLB.lbColor = appData.highlightArrowColor;
\r
3387 hpen = CreatePen( PS_SOLID, 2, RGB(0x00,0x00,0x00) );
\r
3388 holdpen = SelectObject( hdc, hpen );
\r
3389 hbrush = CreateBrushIndirect( &stLB );
\r
3390 holdbrush = SelectObject( hdc, hbrush );
\r
3392 DrawArrowBetweenPoints( hdc, s_x, s_y, d_x, d_y );
\r
3394 SelectObject( hdc, holdpen );
\r
3395 SelectObject( hdc, holdbrush );
\r
3396 DeleteObject( hpen );
\r
3397 DeleteObject( hbrush );
\r
3400 BOOL HasHighlightInfo()
\r
3402 BOOL result = FALSE;
\r
3404 if( highlightInfo.sq[0].x >= 0 && highlightInfo.sq[0].y >= 0 &&
\r
3405 highlightInfo.sq[1].x >= 0 && highlightInfo.sq[1].y >= 0 )
\r
3416 BOOL IsDrawArrowEnabled()
\r
3418 BOOL result = FALSE;
\r
3420 if( appData.highlightMoveWithArrow && squareSize >= 32 ) {
\r
3427 VOID DrawArrowHighlight( HDC hdc )
\r
3429 if( IsDrawArrowEnabled() && HasHighlightInfo() ) {
\r
3430 DrawArrowBetweenSquares( hdc,
\r
3431 highlightInfo.sq[0].x, highlightInfo.sq[0].y,
\r
3432 highlightInfo.sq[1].x, highlightInfo.sq[1].y );
\r
3436 HRGN GetArrowHighlightClipRegion( HDC hdc )
\r
3438 HRGN result = NULL;
\r
3440 if( HasHighlightInfo() ) {
\r
3441 int x1, y1, x2, y2;
\r
3442 int sx, sy, dx, dy;
\r
3444 SquareToPos(highlightInfo.sq[0].y, highlightInfo.sq[0].x, &x1, &y1 );
\r
3445 SquareToPos(highlightInfo.sq[1].y, highlightInfo.sq[1].x, &x2, &y2 );
\r
3447 sx = MIN( x1, x2 );
\r
3448 sy = MIN( y1, y2 );
\r
3449 dx = MAX( x1, x2 ) + squareSize;
\r
3450 dy = MAX( y1, y2 ) + squareSize;
\r
3452 result = CreateRectRgn( sx, sy, dx, dy );
\r
3459 Warning: this function modifies the behavior of several other functions.
\r
3461 Basically, Winboard is optimized to avoid drawing the whole board if not strictly
\r
3462 needed. Unfortunately, the decision whether or not to perform a full or partial
\r
3463 repaint is scattered all over the place, which is not good for features such as
\r
3464 "arrow highlighting" that require a full repaint of the board.
\r
3466 So, I've tried to patch the code where I thought it made sense (e.g. after or during
\r
3467 user interaction, when speed is not so important) but especially to avoid errors
\r
3468 in the displayed graphics.
\r
3470 In such patched places, I always try refer to this function so there is a single
\r
3471 place to maintain knowledge.
\r
3473 To restore the original behavior, just return FALSE unconditionally.
\r
3475 BOOL IsFullRepaintPreferrable()
\r
3477 BOOL result = FALSE;
\r
3479 if( (appData.highlightLastMove || appData.highlightDragging) && IsDrawArrowEnabled() ) {
\r
3480 /* Arrow may appear on the board */
\r
3488 This function is called by DrawPosition to know whether a full repaint must
\r
3491 Only DrawPosition may directly call this function, which makes use of
\r
3492 some state information. Other function should call DrawPosition specifying
\r
3493 the repaint flag, and can use IsFullRepaintPreferrable if needed.
\r
3495 BOOL DrawPositionNeedsFullRepaint()
\r
3497 BOOL result = FALSE;
\r
3500 Probably a slightly better policy would be to trigger a full repaint
\r
3501 when animInfo.piece changes state (i.e. empty -> non-empty and viceversa),
\r
3502 but animation is fast enough that it's difficult to notice.
\r
3504 if( animInfo.piece == EmptySquare ) {
\r
3505 if( (appData.highlightLastMove || appData.highlightDragging) && IsDrawArrowEnabled() /*&& HasHighlightInfo()*/ ) {
\r
3513 static HBITMAP borderBitmap;
\r
3516 DrawBackgroundOnDC(HDC hdc)
\r
3522 static char oldBorder[MSG_SIZ];
\r
3523 int w = 600, h = 600, mode;
\r
3525 if(strcmp(appData.border, oldBorder)) { // load new one when old one no longer valid
\r
3526 strncpy(oldBorder, appData.border, MSG_SIZ-1);
\r
3527 borderBitmap = LoadImage( 0, appData.border, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );
\r
3529 if(borderBitmap == NULL) { // loading failed, use white
\r
3530 FillRect( hdc, &boardRect, whitePieceBrush );
\r
3533 tmphdc = CreateCompatibleDC(hdc);
\r
3534 hbm = SelectObject(tmphdc, borderBitmap);
\r
3535 if( GetObject( borderBitmap, sizeof(bi), &bi ) > 0 ) {
\r
3539 mode = SetStretchBltMode(hdc, COLORONCOLOR);
\r
3540 StretchBlt(hdc, boardRect.left, boardRect.top, boardRect.right - boardRect.left,
\r
3541 boardRect.bottom - boardRect.top, tmphdc, 0, 0, w, h, SRCCOPY);
\r
3542 SetStretchBltMode(hdc, mode);
\r
3543 SelectObject(tmphdc, hbm);
\r
3548 DrawBoardOnDC(HDC hdc, Board board, HDC tmphdc)
\r
3550 int row, column, x, y, square_color, piece_color;
\r
3551 ChessSquare piece;
\r
3553 HDC texture_hdc = NULL;
\r
3555 /* [AS] Initialize background textures if needed */
\r
3556 if( liteBackTexture != NULL || darkBackTexture != NULL ) {
\r
3557 static int backTextureBoardSize; /* [HGM] boardsize: also new texture if board format changed */
\r
3558 if( backTextureSquareSize != squareSize
\r
3559 || backTextureBoardSize != BOARD_WIDTH+BOARD_FILES*BOARD_HEIGHT) {
\r
3560 backTextureBoardSize = BOARD_WIDTH+BOARD_FILES*BOARD_HEIGHT;
\r
3561 backTextureSquareSize = squareSize;
\r
3562 RebuildTextureSquareInfo();
\r
3565 texture_hdc = CreateCompatibleDC( hdc );
\r
3568 for (row = 0; row < BOARD_HEIGHT; row++) {
\r
3569 for (column = 0; column < BOARD_WIDTH; column++) {
\r
3571 SquareToPos(row, column, &x, &y);
\r
3573 piece = board[row][column];
\r
3575 square_color = ((column + row) % 2) == 1;
\r
3576 if( gameInfo.variant == VariantXiangqi ) {
\r
3577 square_color = !InPalace(row, column);
\r
3578 if(BOARD_HEIGHT&1) { if(row==BOARD_HEIGHT/2) square_color ^= 1; }
\r
3579 else if(row < BOARD_HEIGHT/2) square_color ^= 1;
\r
3581 piece_color = (int) piece < (int) BlackPawn;
\r
3584 /* [HGM] holdings file: light square or black */
\r
3585 if(column == BOARD_LEFT-2) {
\r
3586 if( row > BOARD_HEIGHT - gameInfo.holdingsSize - 1 )
\r
3589 DisplayHoldingsCount(hdc, x, y, 0, 0); /* black out */
\r
3593 if(column == BOARD_RGHT + 1 ) {
\r
3594 if( row < gameInfo.holdingsSize )
\r
3597 DisplayHoldingsCount(hdc, x, y, 0, 0);
\r
3601 if(column == BOARD_LEFT-1 ) /* left align */
\r
3602 DisplayHoldingsCount(hdc, x, y, flipView, (int) board[row][column]);
\r
3603 else if( column == BOARD_RGHT) /* right align */
\r
3604 DisplayHoldingsCount(hdc, x, y, !flipView, (int) board[row][column]);
\r
3605 else if( piece == DarkSquare) DisplayHoldingsCount(hdc, x, y, 0, 0);
\r
3607 if (appData.monoMode) {
\r
3608 if (piece == EmptySquare) {
\r
3609 BitBlt(hdc, x, y, squareSize, squareSize, 0, 0, 0,
\r
3610 square_color ? WHITENESS : BLACKNESS);
\r
3612 DrawPieceOnDC(hdc, piece, piece_color, square_color, x, y, tmphdc);
\r
3615 else if( appData.useBitmaps && backTextureSquareInfo[row][column].mode > 0 ) {
\r
3616 /* [AS] Draw the square using a texture bitmap */
\r
3617 HBITMAP hbm = SelectObject( texture_hdc, square_color ? liteBackTexture : darkBackTexture );
\r
3618 int r = row, c = column; // [HGM] do not flip board in flipView
\r
3619 if(flipView) { r = BOARD_HEIGHT-1 - r; c = BOARD_WIDTH-1 - c; }
\r
3622 squareSize, squareSize,
\r
3625 backTextureSquareInfo[r][c].mode,
\r
3626 backTextureSquareInfo[r][c].x,
\r
3627 backTextureSquareInfo[r][c].y );
\r
3629 SelectObject( texture_hdc, hbm );
\r
3631 if (piece != EmptySquare) {
\r
3632 DrawPieceOnDC(hdc, piece, piece_color, -1, x, y, tmphdc);
\r
3636 HBRUSH brush = square_color ? lightSquareBrush : darkSquareBrush;
\r
3638 oldBrush = SelectObject(hdc, brush );
\r
3639 BitBlt(hdc, x, y, squareSize, squareSize, 0, 0, 0, PATCOPY);
\r
3640 SelectObject(hdc, oldBrush);
\r
3641 if (piece != EmptySquare)
\r
3642 DrawPieceOnDC(hdc, piece, piece_color, -1, x, y, tmphdc);
\r
3647 if( texture_hdc != NULL ) {
\r
3648 DeleteDC( texture_hdc );
\r
3652 int saveDiagFlag = 0; FILE *diagFile; // [HGM] diag
\r
3653 void fputDW(FILE *f, int x)
\r
3655 fputc(x & 255, f);
\r
3656 fputc(x>>8 & 255, f);
\r
3657 fputc(x>>16 & 255, f);
\r
3658 fputc(x>>24 & 255, f);
\r
3661 #define MAX_CLIPS 200 /* more than enough */
\r
3664 DrawLogoOnDC(HDC hdc, RECT logoRect, HBITMAP logo)
\r
3666 // HBITMAP bufferBitmap;
\r
3671 int w = 100, h = 50;
\r
3673 if(logo == NULL) {
\r
3674 if(!logoHeight) return;
\r
3675 FillRect( hdc, &logoRect, whitePieceBrush );
\r
3677 // GetClientRect(hwndMain, &Rect);
\r
3678 // bufferBitmap = CreateCompatibleBitmap(hdc, Rect.right-Rect.left+1,
\r
3679 // Rect.bottom-Rect.top+1);
\r
3680 tmphdc = CreateCompatibleDC(hdc);
\r
3681 hbm = SelectObject(tmphdc, logo);
\r
3682 if( GetObject( logo, sizeof(bi), &bi ) > 0 ) {
\r
3686 StretchBlt(hdc, logoRect.left, logoRect.top, logoRect.right - logoRect.left,
\r
3687 logoRect.bottom - logoRect.top, tmphdc, 0, 0, w, h, SRCCOPY);
\r
3688 SelectObject(tmphdc, hbm);
\r
3696 HDC hdc = GetDC(hwndMain);
\r
3697 HBITMAP whiteLogo = (HBITMAP) first.programLogo, blackLogo = (HBITMAP) second.programLogo;
\r
3698 if(appData.autoLogo) {
\r
3700 switch(gameMode) { // pick logos based on game mode
\r
3701 case IcsObserving:
\r
3702 whiteLogo = second.programLogo; // ICS logo
\r
3703 blackLogo = second.programLogo;
\r
3706 case IcsPlayingWhite:
\r
3707 if(!appData.zippyPlay) whiteLogo = userLogo;
\r
3708 blackLogo = second.programLogo; // ICS logo
\r
3710 case IcsPlayingBlack:
\r
3711 whiteLogo = second.programLogo; // ICS logo
\r
3712 blackLogo = appData.zippyPlay ? first.programLogo : userLogo;
\r
3714 case TwoMachinesPlay:
\r
3715 if(first.twoMachinesColor[0] == 'b') {
\r
3716 whiteLogo = second.programLogo;
\r
3717 blackLogo = first.programLogo;
\r
3720 case MachinePlaysWhite:
\r
3721 blackLogo = userLogo;
\r
3723 case MachinePlaysBlack:
\r
3724 whiteLogo = userLogo;
\r
3725 blackLogo = first.programLogo;
\r
3728 DrawLogoOnDC(hdc, leftLogoRect, flipClock ? blackLogo : whiteLogo);
\r
3729 DrawLogoOnDC(hdc, rightLogoRect, flipClock ? whiteLogo : blackLogo);
\r
3730 ReleaseDC(hwndMain, hdc);
\r
3735 UpdateLogos(int display)
\r
3736 { // called after loading new engine(s), in tourney or from menu
\r
3737 LoadLogo(&first, 0, FALSE);
\r
3738 LoadLogo(&second, 1, appData.icsActive);
\r
3739 InitDrawingSizes(-2, 0); // adapt layout of board window to presence/absence of logos
\r
3740 if(display) DisplayLogos();
\r
3743 static HDC hdcSeek;
\r
3745 // [HGM] seekgraph
\r
3746 void DrawSeekAxis( int x, int y, int xTo, int yTo )
\r
3749 HPEN hp = SelectObject( hdcSeek, gridPen );
\r
3750 MoveToEx( hdcSeek, boardRect.left+x, boardRect.top+y, &stPt );
\r
3751 LineTo( hdcSeek, boardRect.left+xTo, boardRect.top+yTo );
\r
3752 SelectObject( hdcSeek, hp );
\r
3755 // front-end wrapper for drawing functions to do rectangles
\r
3756 void DrawSeekBackground( int left, int top, int right, int bottom )
\r
3761 if (hdcSeek == NULL) {
\r
3762 hdcSeek = GetDC(hwndMain);
\r
3763 if (!appData.monoMode) {
\r
3764 SelectPalette(hdcSeek, hPal, FALSE);
\r
3765 RealizePalette(hdcSeek);
\r
3768 hp = SelectObject( hdcSeek, gridPen );
\r
3769 rc.top = boardRect.top+top; rc.left = boardRect.left+left;
\r
3770 rc.bottom = boardRect.top+bottom; rc.right = boardRect.left+right;
\r
3771 FillRect( hdcSeek, &rc, lightSquareBrush );
\r
3772 SelectObject( hdcSeek, hp );
\r
3775 // front-end wrapper for putting text in graph
\r
3776 void DrawSeekText(char *buf, int x, int y)
\r
3779 SetBkMode( hdcSeek, TRANSPARENT );
\r
3780 GetTextExtentPoint32( hdcSeek, buf, strlen(buf), &stSize );
\r
3781 TextOut( hdcSeek, boardRect.left+x-3, boardRect.top+y-stSize.cy/2, buf, strlen(buf) );
\r
3784 void DrawSeekDot(int x, int y, int color)
\r
3786 int square = color & 0x80;
\r
3787 HBRUSH oldBrush = SelectObject(hdcSeek,
\r
3788 color == 0 ? markerBrush[1] : color == 1 ? darkSquareBrush : explodeBrush);
\r
3791 Rectangle(hdcSeek, boardRect.left+x - squareSize/9, boardRect.top+y - squareSize/9,
\r
3792 boardRect.left+x + squareSize/9, boardRect.top+y + squareSize/9);
\r
3794 Ellipse(hdcSeek, boardRect.left+x - squareSize/8, boardRect.top+y - squareSize/8,
\r
3795 boardRect.left+x + squareSize/8, boardRect.top+y + squareSize/8);
\r
3796 SelectObject(hdcSeek, oldBrush);
\r
3799 void DrawSeekOpen()
\r
3803 void DrawSeekClose()
\r
3812 HDCDrawPosition(HDC hdc, BOOLEAN repaint, Board board)
\r
3814 static Board lastReq[2], lastDrawn[2];
\r
3815 static HighlightInfo lastDrawnHighlight, lastDrawnPremove;
\r
3816 static int lastDrawnFlipView = 0;
\r
3817 static int lastReqValid[2] = {0, 0}, lastDrawnValid[2] = {0, 0};
\r
3818 int releaseDC, x, y, x2, y2, row, column, num_clips = 0, i;
\r
3821 HBITMAP bufferBitmap;
\r
3822 HBITMAP oldBitmap;
\r
3824 HRGN clips[MAX_CLIPS];
\r
3825 ChessSquare dragged_piece = EmptySquare;
\r
3826 int nr = twoBoards*partnerUp;
\r
3828 /* I'm undecided on this - this function figures out whether a full
\r
3829 * repaint is necessary on its own, so there's no real reason to have the
\r
3830 * caller tell it that. I think this can safely be set to FALSE - but
\r
3831 * if we trust the callers not to request full repaints unnessesarily, then
\r
3832 * we could skip some clipping work. In other words, only request a full
\r
3833 * redraw when the majority of pieces have changed positions (ie. flip,
\r
3834 * gamestart and similar) --Hawk
\r
3836 Boolean fullrepaint = repaint;
\r
3838 if(DrawSeekGraph()) return; // [HG} seekgraph: suppress printing board if seek graph up
\r
3840 if( DrawPositionNeedsFullRepaint() ) {
\r
3841 fullrepaint = TRUE;
\r
3844 if (board == NULL) {
\r
3845 if (!lastReqValid[nr]) {
\r
3848 board = lastReq[nr];
\r
3850 CopyBoard(lastReq[nr], board);
\r
3851 lastReqValid[nr] = 1;
\r
3854 if (doingSizing) {
\r
3858 if (IsIconic(hwndMain)) {
\r
3862 if (hdc == NULL) {
\r
3863 hdc = GetDC(hwndMain);
\r
3864 if (!appData.monoMode) {
\r
3865 SelectPalette(hdc, hPal, FALSE);
\r
3866 RealizePalette(hdc);
\r
3870 releaseDC = FALSE;
\r
3873 /* Create some work-DCs */
\r
3874 hdcmem = CreateCompatibleDC(hdc);
\r
3875 tmphdc = CreateCompatibleDC(hdc);
\r
3877 /* If dragging is in progress, we temporarely remove the piece */
\r
3878 /* [HGM] or temporarily decrease count if stacked */
\r
3879 /* !! Moved to before board compare !! */
\r
3880 if (dragInfo.from.x >= 0 && dragInfo.pos.x >= 0) {
\r
3881 dragged_piece = board[dragInfo.from.y][dragInfo.from.x];
\r
3882 if(dragInfo.from.x == BOARD_LEFT-2 ) {
\r
3883 if(--board[dragInfo.from.y][dragInfo.from.x+1] == 0 )
\r
3884 board[dragInfo.from.y][dragInfo.from.x] = EmptySquare;
\r
3886 if(dragInfo.from.x == BOARD_RGHT+1) {
\r
3887 if(--board[dragInfo.from.y][dragInfo.from.x-1] == 0 )
\r
3888 board[dragInfo.from.y][dragInfo.from.x] = EmptySquare;
\r
3890 board[dragInfo.from.y][dragInfo.from.x] = gatingPiece;
\r
3893 /* Figure out which squares need updating by comparing the
\r
3894 * newest board with the last drawn board and checking if
\r
3895 * flipping has changed.
\r
3897 if (!fullrepaint && lastDrawnValid[nr] && (nr == 1 || lastDrawnFlipView == flipView)) {
\r
3898 for (row = 0; row < BOARD_HEIGHT; row++) { /* [HGM] true size, not 8 */
\r
3899 for (column = 0; column < BOARD_WIDTH; column++) {
\r
3900 if (lastDrawn[nr][row][column] != board[row][column]) {
\r
3901 SquareToPos(row, column, &x, &y);
\r
3902 clips[num_clips++] =
\r
3903 CreateRectRgn(x, y, x + squareSize, y + squareSize);
\r
3907 if(nr == 0) { // [HGM] dual: no highlights on second board
\r
3908 for (i=0; i<2; i++) {
\r
3909 if (lastDrawnHighlight.sq[i].x != highlightInfo.sq[i].x ||
\r
3910 lastDrawnHighlight.sq[i].y != highlightInfo.sq[i].y) {
\r
3911 if (lastDrawnHighlight.sq[i].x >= 0 &&
\r
3912 lastDrawnHighlight.sq[i].y >= 0) {
\r
3913 SquareToPos(lastDrawnHighlight.sq[i].y,
\r
3914 lastDrawnHighlight.sq[i].x, &x, &y);
\r
3915 clips[num_clips++] =
\r
3916 CreateRectRgn(x - lineGap, y - lineGap,
\r
3917 x + squareSize + lineGap, y + squareSize + lineGap);
\r
3919 if (highlightInfo.sq[i].x >= 0 && highlightInfo.sq[i].y >= 0) {
\r
3920 SquareToPos(highlightInfo.sq[i].y, highlightInfo.sq[i].x, &x, &y);
\r
3921 clips[num_clips++] =
\r
3922 CreateRectRgn(x - lineGap, y - lineGap,
\r
3923 x + squareSize + lineGap, y + squareSize + lineGap);
\r
3927 for (i=0; i<2; i++) {
\r
3928 if (lastDrawnPremove.sq[i].x != premoveHighlightInfo.sq[i].x ||
\r
3929 lastDrawnPremove.sq[i].y != premoveHighlightInfo.sq[i].y) {
\r
3930 if (lastDrawnPremove.sq[i].x >= 0 &&
\r
3931 lastDrawnPremove.sq[i].y >= 0) {
\r
3932 SquareToPos(lastDrawnPremove.sq[i].y,
\r
3933 lastDrawnPremove.sq[i].x, &x, &y);
\r
3934 clips[num_clips++] =
\r
3935 CreateRectRgn(x - lineGap, y - lineGap,
\r
3936 x + squareSize + lineGap, y + squareSize + lineGap);
\r
3938 if (premoveHighlightInfo.sq[i].x >= 0 &&
\r
3939 premoveHighlightInfo.sq[i].y >= 0) {
\r
3940 SquareToPos(premoveHighlightInfo.sq[i].y,
\r
3941 premoveHighlightInfo.sq[i].x, &x, &y);
\r
3942 clips[num_clips++] =
\r
3943 CreateRectRgn(x - lineGap, y - lineGap,
\r
3944 x + squareSize + lineGap, y + squareSize + lineGap);
\r
3948 } else { // nr == 1
\r
3949 partnerHighlightInfo.sq[0].y = board[EP_STATUS-4];
\r
3950 partnerHighlightInfo.sq[0].x = board[EP_STATUS-3];
\r
3951 partnerHighlightInfo.sq[1].y = board[EP_STATUS-2];
\r
3952 partnerHighlightInfo.sq[1].x = board[EP_STATUS-1];
\r
3953 for (i=0; i<2; i++) {
\r
3954 if (partnerHighlightInfo.sq[i].x >= 0 &&
\r
3955 partnerHighlightInfo.sq[i].y >= 0) {
\r
3956 SquareToPos(partnerHighlightInfo.sq[i].y,
\r
3957 partnerHighlightInfo.sq[i].x, &x, &y);
\r
3958 clips[num_clips++] =
\r
3959 CreateRectRgn(x - lineGap, y - lineGap,
\r
3960 x + squareSize + lineGap, y + squareSize + lineGap);
\r
3962 if (oldPartnerHighlight.sq[i].x >= 0 &&
\r
3963 oldPartnerHighlight.sq[i].y >= 0) {
\r
3964 SquareToPos(oldPartnerHighlight.sq[i].y,
\r
3965 oldPartnerHighlight.sq[i].x, &x, &y);
\r
3966 clips[num_clips++] =
\r
3967 CreateRectRgn(x - lineGap, y - lineGap,
\r
3968 x + squareSize + lineGap, y + squareSize + lineGap);
\r
3973 fullrepaint = TRUE;
\r
3976 /* Create a buffer bitmap - this is the actual bitmap
\r
3977 * being written to. When all the work is done, we can
\r
3978 * copy it to the real DC (the screen). This avoids
\r
3979 * the problems with flickering.
\r
3981 GetClientRect(hwndMain, &Rect);
\r
3982 bufferBitmap = CreateCompatibleBitmap(hdc, Rect.right-Rect.left+1,
\r
3983 Rect.bottom-Rect.top+1);
\r
3984 oldBitmap = SelectObject(hdcmem, bufferBitmap);
\r
3985 if (!appData.monoMode) {
\r
3986 SelectPalette(hdcmem, hPal, FALSE);
\r
3989 /* Create clips for dragging */
\r
3990 if (!fullrepaint) {
\r
3991 if (dragInfo.from.x >= 0) {
\r
3992 SquareToPos(dragInfo.from.y, dragInfo.from.x, &x, &y);
\r
3993 clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);
\r
3995 if (dragInfo.start.x >= 0) {
\r
3996 SquareToPos(dragInfo.start.y, dragInfo.start.x, &x, &y);
\r
3997 clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);
\r
3999 if (dragInfo.pos.x >= 0) {
\r
4000 x = dragInfo.pos.x - squareSize / 2;
\r
4001 y = dragInfo.pos.y - squareSize / 2;
\r
4002 clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);
\r
4004 if (dragInfo.lastpos.x >= 0) {
\r
4005 x = dragInfo.lastpos.x - squareSize / 2;
\r
4006 y = dragInfo.lastpos.y - squareSize / 2;
\r
4007 clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);
\r
4011 /* Are we animating a move?
\r
4013 * - remove the piece from the board (temporarely)
\r
4014 * - calculate the clipping region
\r
4016 if (!fullrepaint) {
\r
4017 if (animInfo.piece != EmptySquare) {
\r
4018 board[animInfo.from.y][animInfo.from.x] = EmptySquare;
\r
4019 x = boardRect.left + animInfo.lastpos.x;
\r
4020 y = boardRect.top + animInfo.lastpos.y;
\r
4021 x2 = boardRect.left + animInfo.pos.x;
\r
4022 y2 = boardRect.top + animInfo.pos.y;
\r
4023 clips[num_clips++] = CreateRectRgn(MIN(x,x2), MIN(y,y2), MAX(x,x2)+squareSize, MAX(y,y2)+squareSize);
\r
4024 /* Slight kludge. The real problem is that after AnimateMove is
\r
4025 done, the position on the screen does not match lastDrawn.
\r
4026 This currently causes trouble only on e.p. captures in
\r
4027 atomic, where the piece moves to an empty square and then
\r
4028 explodes. The old and new positions both had an empty square
\r
4029 at the destination, but animation has drawn a piece there and
\r
4030 we have to remember to erase it. [HGM] moved until after setting lastDrawn */
\r
4032 lastDrawn[0][animInfo.to.y][animInfo.to.x] = animInfo.piece;
\r
4036 /* No clips? Make sure we have fullrepaint set to TRUE */
\r
4037 if (num_clips == 0)
\r
4038 fullrepaint = TRUE;
\r
4040 /* Set clipping on the memory DC */
\r
4041 if (!fullrepaint) {
\r
4042 SelectClipRgn(hdcmem, clips[0]);
\r
4043 for (x = 1; x < num_clips; x++) {
\r
4044 if (ExtSelectClipRgn(hdcmem, clips[x], RGN_OR) == ERROR)
\r
4045 abort(); // this should never ever happen!
\r
4049 /* Do all the drawing to the memory DC */
\r
4050 if(explodeInfo.radius) { // [HGM] atomic
\r
4052 int x, y, r=(explodeInfo.radius * squareSize)/100;
\r
4053 ChessSquare piece = board[explodeInfo.fromY][explodeInfo.fromX];
\r
4054 board[explodeInfo.fromY][explodeInfo.fromX] = EmptySquare; // suppress display of capturer
\r
4055 SquareToPos(explodeInfo.toY, explodeInfo.toX, &x, &y);
\r
4056 x += squareSize/2;
\r
4057 y += squareSize/2;
\r
4058 if(!fullrepaint) {
\r
4059 clips[num_clips] = CreateRectRgn(x-r, y-r, x+r, y+r);
\r
4060 ExtSelectClipRgn(hdcmem, clips[num_clips++], RGN_OR);
\r
4062 DrawGridOnDC(hdcmem);
\r
4063 DrawHighlightsOnDC(hdcmem, &highlightInfo, HIGHLIGHT_PEN);
\r
4064 DrawHighlightsOnDC(hdcmem, &premoveHighlightInfo, PREMOVE_PEN);
\r
4065 DrawBoardOnDC(hdcmem, board, tmphdc);
\r
4066 board[explodeInfo.fromY][explodeInfo.fromX] = piece;
\r
4067 oldBrush = SelectObject(hdcmem, explodeBrush);
\r
4068 Ellipse(hdcmem, x-r, y-r, x+r, y+r);
\r
4069 SelectObject(hdcmem, oldBrush);
\r
4071 if(border) DrawBackgroundOnDC(hdcmem);
\r
4072 DrawGridOnDC(hdcmem);
\r
4073 if(nr == 0) { // [HGM] dual: decide which highlights to draw
\r
4074 DrawHighlightsOnDC(hdcmem, &highlightInfo, HIGHLIGHT_PEN);
\r
4075 DrawHighlightsOnDC(hdcmem, &premoveHighlightInfo, PREMOVE_PEN);
\r
4077 DrawHighlightsOnDC(hdcmem, &partnerHighlightInfo, HIGHLIGHT_PEN);
\r
4078 oldPartnerHighlight = partnerHighlightInfo;
\r
4080 DrawBoardOnDC(hdcmem, board, tmphdc);
\r
4082 if(nr == 0) // [HGM] dual: markers only on left board
\r
4083 for (row = 0; row < BOARD_HEIGHT; row++) {
\r
4084 for (column = 0; column < BOARD_WIDTH; column++) {
\r
4085 if (marker[row][column]) { // marker changes only occur with full repaint!
\r
4086 HBRUSH oldBrush = SelectObject(hdcmem, markerBrush[marker[row][column]-1]);
\r
4087 SquareToPos(row, column, &x, &y);
\r
4088 Ellipse(hdcmem, x + squareSize/4, y + squareSize/4,
\r
4089 x + 3*squareSize/4, y + 3*squareSize/4);
\r
4090 SelectObject(hdcmem, oldBrush);
\r
4095 if( appData.highlightMoveWithArrow ) {
\r
4097 DrawArrowHighlight(hdcmem);
\r
4100 DrawCoordsOnDC(hdcmem);
\r
4102 CopyBoard(lastDrawn[nr], board); /* [HGM] Moved to here from end of routine, */
\r
4103 /* to make sure lastDrawn contains what is actually drawn */
\r
4105 /* Put the dragged piece back into place and draw it (out of place!) */
\r
4106 if (dragged_piece != EmptySquare) {
\r
4107 /* [HGM] or restack */
\r
4108 if(dragInfo.from.x == BOARD_LEFT-2 )
\r
4109 board[dragInfo.from.y][dragInfo.from.x+1]++;
\r
4111 if(dragInfo.from.x == BOARD_RGHT+1 )
\r
4112 board[dragInfo.from.y][dragInfo.from.x-1]++;
\r
4114 board[dragInfo.from.y][dragInfo.from.x] = dragged_piece;
\r
4115 x = dragInfo.pos.x - squareSize / 2;
\r
4116 y = dragInfo.pos.y - squareSize / 2;
\r
4117 DrawPieceOnDC(hdcmem, dragInfo.piece,
\r
4118 ((int) dragInfo.piece < (int) BlackPawn),
\r
4119 (dragInfo.from.y + dragInfo.from.x) % 2, x, y, tmphdc);
\r
4122 /* Put the animated piece back into place and draw it */
\r
4123 if (animInfo.piece != EmptySquare) {
\r
4124 board[animInfo.from.y][animInfo.from.x] = animInfo.piece;
\r
4125 x = boardRect.left + animInfo.pos.x;
\r
4126 y = boardRect.top + animInfo.pos.y;
\r
4127 DrawPieceOnDC(hdcmem, animInfo.piece,
\r
4128 ((int) animInfo.piece < (int) BlackPawn),
\r
4129 (animInfo.from.y + animInfo.from.x) % 2, x, y, tmphdc);
\r
4132 /* Release the bufferBitmap by selecting in the old bitmap
\r
4133 * and delete the memory DC
\r
4135 SelectObject(hdcmem, oldBitmap);
\r
4138 /* Set clipping on the target DC */
\r
4139 if (!fullrepaint) {
\r
4140 if(nr == 1) for (x = 0; x < num_clips; x++) { // [HGM] dual: translate clips
\r
4142 GetRgnBox(clips[x], &rect);
\r
4143 DeleteObject(clips[x]);
\r
4144 clips[x] = CreateRectRgn(rect.left + wpMain.width/2, rect.top,
\r
4145 rect.right + wpMain.width/2, rect.bottom);
\r
4147 SelectClipRgn(hdc, clips[0]);
\r
4148 for (x = 1; x < num_clips; x++) {
\r
4149 if (ExtSelectClipRgn(hdc, clips[x], RGN_OR) == ERROR)
\r
4150 abort(); // this should never ever happen!
\r
4154 /* Copy the new bitmap onto the screen in one go.
\r
4155 * This way we avoid any flickering
\r
4157 oldBitmap = SelectObject(tmphdc, bufferBitmap);
\r
4158 BitBlt(hdc, boardRect.left + twoBoards*partnerUp*wpMain.width/2, boardRect.top, // [HGM] dual
\r
4159 boardRect.right - boardRect.left,
\r
4160 boardRect.bottom - boardRect.top,
\r
4161 tmphdc, boardRect.left, boardRect.top, SRCCOPY);
\r
4162 if(saveDiagFlag) {
\r
4163 BITMAP b; int i, j=0, m, w, wb, fac=0; char *pData;
\r
4164 BITMAPINFOHEADER bih; int color[16], nrColors=0;
\r
4165 HBITMAP src = bufferBitmap, obmp; HDC tmp = CreateCompatibleDC(hdc);
\r
4167 bufferBitmap = CreateCompatibleBitmap(hdc, boardRect.right-boardRect.left, Rect.bottom-Rect.top-2*OUTER_MARGIN);
\r
4168 obmp = SelectObject(tmp, bufferBitmap);
\r
4169 BitBlt(tmp, 0, 0, boardRect.right - boardRect.left, Rect.bottom - Rect.top - 2*OUTER_MARGIN,
\r
4170 tmphdc, boardRect.left, OUTER_MARGIN, SRCCOPY);
\r
4171 GetObject(bufferBitmap, sizeof(b), &b);
\r
4172 if(pData = malloc(b.bmWidthBytes*b.bmHeight + 10000)) {
\r
4173 bih.biSize = sizeof(BITMAPINFOHEADER);
\r
4174 bih.biWidth = b.bmWidth;
\r
4175 bih.biHeight = b.bmHeight;
\r
4177 bih.biBitCount = b.bmBitsPixel;
\r
4178 bih.biCompression = 0;
\r
4179 bih.biSizeImage = b.bmWidthBytes*b.bmHeight;
\r
4180 bih.biXPelsPerMeter = 0;
\r
4181 bih.biYPelsPerMeter = 0;
\r
4182 bih.biClrUsed = 0;
\r
4183 bih.biClrImportant = 0;
\r
4184 // fprintf(diagFile, "t=%d\nw=%d\nh=%d\nB=%d\nP=%d\nX=%d\n",
\r
4185 // b.bmType, b.bmWidth, b.bmHeight, b.bmWidthBytes, b.bmPlanes, b.bmBitsPixel);
\r
4186 GetDIBits(tmphdc,bufferBitmap,0,b.bmHeight,pData,(BITMAPINFO*)&bih,DIB_RGB_COLORS);
\r
4187 // fprintf(diagFile, "%8x\n", (int) pData);
\r
4189 wb = b.bmWidthBytes;
\r
4191 for(i=0; i<wb*(b.bmHeight - boardRect.top + OUTER_MARGIN)>>2; i++) {
\r
4192 int k = ((int*) pData)[i];
\r
4193 for(j=0; j<nrColors; j++) if(color[j] == k) break;
\r
4194 if(j >= 16) break;
\r
4196 if(j >= nrColors) nrColors = j+1;
\r
4198 if(j<16) { // 16 colors is enough. Compress to 4 bits per pixel
\r
4200 for(i=0; i<b.bmHeight - boardRect.top + OUTER_MARGIN; i++) {
\r
4201 for(w=0; w<(wb>>2); w+=2) {
\r
4202 int k = ((int*) pData)[(wb*i>>2) + w];
\r
4203 for(j=0; j<nrColors; j++) if(color[j] == k) break;
\r
4204 k = ((int*) pData)[(wb*i>>2) + w + 1];
\r
4205 for(m=0; m<nrColors; m++) if(color[m] == k) break;
\r
4206 pData[p++] = m | j<<4;
\r
4208 while(p&3) pData[p++] = 0;
\r
4211 wb = ((wb+31)>>5)<<2;
\r
4213 // write BITMAPFILEHEADER
\r
4214 fprintf(diagFile, "BM");
\r
4215 fputDW(diagFile, wb*(b.bmHeight - boardRect.top + OUTER_MARGIN)+0x36 + (fac?64:0));
\r
4216 fputDW(diagFile, 0);
\r
4217 fputDW(diagFile, 0x36 + (fac?64:0));
\r
4218 // write BITMAPINFOHEADER
\r
4219 fputDW(diagFile, 40);
\r
4220 fputDW(diagFile, b.bmWidth);
\r
4221 fputDW(diagFile, b.bmHeight - boardRect.top + OUTER_MARGIN);
\r
4222 if(fac) fputDW(diagFile, 0x040001); // planes and bits/pixel
\r
4223 else fputDW(diagFile, 0x200001); // planes and bits/pixel
\r
4224 fputDW(diagFile, 0);
\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 // write color table
\r
4232 for(i=0; i<16; i++) fputDW(diagFile, color[i]);
\r
4233 // write bitmap data
\r
4235 for(i=0; i<wb*(b.bmHeight - boardRect.top + OUTER_MARGIN); i++)
\r
4236 fputc(pData[i], diagFile);
\r
4239 DeleteObject(bufferBitmap); bufferBitmap = src;
\r
4240 SelectObject(tmp, obmp);
\r
4244 SelectObject(tmphdc, oldBitmap);
\r
4246 /* Massive cleanup */
\r
4247 for (x = 0; x < num_clips; x++)
\r
4248 DeleteObject(clips[x]);
\r
4251 DeleteObject(bufferBitmap);
\r
4254 ReleaseDC(hwndMain, hdc);
\r
4256 if (lastDrawnFlipView != flipView && nr == 0) {
\r
4258 CheckMenuItem(GetMenu(hwndMain),IDM_FlipView, MF_BYCOMMAND|MF_CHECKED);
\r
4260 CheckMenuItem(GetMenu(hwndMain),IDM_FlipView, MF_BYCOMMAND|MF_UNCHECKED);
\r
4263 /* CopyBoard(lastDrawn, board);*/
\r
4264 lastDrawnHighlight = highlightInfo;
\r
4265 lastDrawnPremove = premoveHighlightInfo;
\r
4266 lastDrawnFlipView = flipView;
\r
4267 lastDrawnValid[nr] = 1;
\r
4270 /* [HGM] diag: Save the current board display to the given open file and close the file */
\r
4275 saveDiagFlag = 1; diagFile = f;
\r
4276 HDCDrawPosition(NULL, TRUE, NULL);
\r
4284 /*---------------------------------------------------------------------------*\
\r
4285 | CLIENT PAINT PROCEDURE
\r
4286 | This is the main event-handler for the WM_PAINT message.
\r
4288 \*---------------------------------------------------------------------------*/
\r
4290 PaintProc(HWND hwnd)
\r
4296 if((hdc = BeginPaint(hwnd, &ps))) {
\r
4297 if (IsIconic(hwnd)) {
\r
4298 DrawIcon(hdc, 2, 2, iconCurrent);
\r
4300 if (!appData.monoMode) {
\r
4301 SelectPalette(hdc, hPal, FALSE);
\r
4302 RealizePalette(hdc);
\r
4304 HDCDrawPosition(hdc, 1, NULL);
\r
4305 if(twoBoards) { // [HGM] dual: also redraw other board in other orientation
\r
4306 flipView = !flipView; partnerUp = !partnerUp;
\r
4307 HDCDrawPosition(hdc, 1, NULL);
\r
4308 flipView = !flipView; partnerUp = !partnerUp;
\r
4311 SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);
\r
4312 ExtTextOut(hdc, messageRect.left, messageRect.top,
\r
4313 ETO_CLIPPED|ETO_OPAQUE,
\r
4314 &messageRect, messageText, strlen(messageText), NULL);
\r
4315 SelectObject(hdc, oldFont);
\r
4316 DisplayBothClocks();
\r
4319 EndPaint(hwnd,&ps);
\r
4327 * If the user selects on a border boundary, return -1; if off the board,
\r
4328 * return -2. Otherwise map the event coordinate to the square.
\r
4329 * The offset boardRect.left or boardRect.top must already have been
\r
4330 * subtracted from x.
\r
4332 int EventToSquare(x, limit)
\r
4337 if (x < lineGap + border)
\r
4339 x -= lineGap + border;
\r
4340 if ((x % (squareSize + lineGap)) >= squareSize)
\r
4342 x /= (squareSize + lineGap);
\r
4354 DropEnable dropEnables[] = {
\r
4355 { 'P', DP_Pawn, N_("Pawn") },
\r
4356 { 'N', DP_Knight, N_("Knight") },
\r
4357 { 'B', DP_Bishop, N_("Bishop") },
\r
4358 { 'R', DP_Rook, N_("Rook") },
\r
4359 { 'Q', DP_Queen, N_("Queen") },
\r
4363 SetupDropMenu(HMENU hmenu)
\r
4365 int i, count, enable;
\r
4367 extern char white_holding[], black_holding[];
\r
4368 char item[MSG_SIZ];
\r
4370 for (i=0; i<sizeof(dropEnables)/sizeof(DropEnable); i++) {
\r
4371 p = strchr(gameMode == IcsPlayingWhite ? white_holding : black_holding,
\r
4372 dropEnables[i].piece);
\r
4374 while (p && *p++ == dropEnables[i].piece) count++;
\r
4375 snprintf(item, MSG_SIZ, "%s %d", T_(dropEnables[i].name), count);
\r
4376 enable = count > 0 || !appData.testLegality
\r
4377 /*!!temp:*/ || (gameInfo.variant == VariantCrazyhouse
\r
4378 && !appData.icsActive);
\r
4379 ModifyMenu(hmenu, dropEnables[i].command,
\r
4380 MF_BYCOMMAND | (enable ? MF_ENABLED : MF_GRAYED) | MF_STRING,
\r
4381 dropEnables[i].command, item);
\r
4385 void DragPieceBegin(int x, int y, Boolean instantly)
\r
4387 dragInfo.lastpos.x = boardRect.left + x;
\r
4388 dragInfo.lastpos.y = boardRect.top + y;
\r
4389 if(instantly) dragInfo.pos = dragInfo.lastpos;
\r
4390 dragInfo.from.x = fromX;
\r
4391 dragInfo.from.y = fromY;
\r
4392 dragInfo.piece = boards[currentMove][fromY][fromX];
\r
4393 dragInfo.start = dragInfo.from;
\r
4394 SetCapture(hwndMain);
\r
4397 void DragPieceEnd(int x, int y)
\r
4400 dragInfo.start.x = dragInfo.start.y = -1;
\r
4401 dragInfo.from = dragInfo.start;
\r
4402 dragInfo.pos = dragInfo.lastpos = dragInfo.start;
\r
4405 void ChangeDragPiece(ChessSquare piece)
\r
4407 dragInfo.piece = piece;
\r
4410 /* Event handler for mouse messages */
\r
4412 MouseEvent(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
\r
4416 static int recursive = 0;
\r
4418 BOOLEAN forceFullRepaint = IsFullRepaintPreferrable(); /* [AS] */
\r
4421 if (message == WM_MBUTTONUP) {
\r
4422 /* Hideous kludge to fool TrackPopupMenu into paying attention
\r
4423 to the middle button: we simulate pressing the left button too!
\r
4425 PostMessage(hwnd, WM_LBUTTONDOWN, wParam, lParam);
\r
4426 PostMessage(hwnd, WM_LBUTTONUP, wParam, lParam);
\r
4432 pt.x = LOWORD(lParam);
\r
4433 pt.y = HIWORD(lParam);
\r
4434 x = EventToSquare(pt.x - boardRect.left, BOARD_WIDTH);
\r
4435 y = EventToSquare(pt.y - boardRect.top, BOARD_HEIGHT);
\r
4436 if (!flipView && y >= 0) {
\r
4437 y = BOARD_HEIGHT - 1 - y;
\r
4439 if (flipView && x >= 0) {
\r
4440 x = BOARD_WIDTH - 1 - x;
\r
4443 shiftKey = GetKeyState(VK_SHIFT) < 0; // [HGM] remember last shift status
\r
4444 controlKey = GetKeyState(VK_CONTROL) < 0; // [HGM] remember last shift status
\r
4446 switch (message) {
\r
4447 case WM_LBUTTONDOWN:
\r
4448 if (PtInRect((LPRECT) &whiteRect, pt)) {
\r
4449 ClockClick(flipClock); break;
\r
4450 } else if (PtInRect((LPRECT) &blackRect, pt)) {
\r
4451 ClockClick(!flipClock); break;
\r
4453 if(dragging) { // [HGM] lion: don't destroy dragging info if we are already dragging
\r
4454 dragInfo.start.x = dragInfo.start.y = -1;
\r
4455 dragInfo.from = dragInfo.start;
\r
4457 if(fromX == -1 && frozen) { // not sure where this is for
\r
4458 fromX = fromY = -1;
\r
4459 DrawPosition(forceFullRepaint || FALSE, NULL); /* [AS] */
\r
4462 LeftClick(Press, pt.x - boardRect.left, pt.y - boardRect.top);
\r
4463 DrawPosition(TRUE, NULL);
\r
4466 case WM_LBUTTONUP:
\r
4467 LeftClick(Release, pt.x - boardRect.left, pt.y - boardRect.top);
\r
4468 DrawPosition(TRUE, NULL);
\r
4471 case WM_MOUSEMOVE:
\r
4472 if(SeekGraphClick(Press, pt.x - boardRect.left, pt.y - boardRect.top, 1)) break;
\r
4473 if(PromoScroll(pt.x - boardRect.left, pt.y - boardRect.top)) break;
\r
4474 MovePV(pt.x - boardRect.left, pt.y - boardRect.top, boardRect.bottom - boardRect.top);
\r
4475 if ((appData.animateDragging || appData.highlightDragging)
\r
4476 && (wParam & MK_LBUTTON || dragging == 2)
\r
4477 && dragInfo.from.x >= 0)
\r
4479 BOOL full_repaint = FALSE;
\r
4481 if (appData.animateDragging) {
\r
4482 dragInfo.pos = pt;
\r
4484 if (appData.highlightDragging) {
\r
4485 HoverEvent(highlightInfo.sq[1].x, highlightInfo.sq[1].y, x, y);
\r
4486 if( IsDrawArrowEnabled() && (x < 0 || x >= BOARD_WIDTH || y < 0 || y >= BOARD_HEIGHT) ) {
\r
4487 full_repaint = TRUE;
\r
4491 DrawPosition( full_repaint, NULL);
\r
4493 dragInfo.lastpos = dragInfo.pos;
\r
4497 case WM_MOUSEWHEEL: // [DM]
\r
4498 { static int lastDir = 0; // [HGM] build in some hysteresis to avoid spurious events
\r
4499 /* Mouse Wheel is being rolled forward
\r
4500 * Play moves forward
\r
4502 if((short)HIWORD(wParam) < 0 && currentMove < forwardMostMove)
\r
4503 { if(lastDir == 1) ForwardEvent(); else lastDir = 1; } // [HGM] suppress first event in direction
\r
4504 /* Mouse Wheel is being rolled backward
\r
4505 * Play moves backward
\r
4507 if((short)HIWORD(wParam) > 0 && currentMove > backwardMostMove)
\r
4508 { if(lastDir == -1) BackwardEvent(); else lastDir = -1; }
\r
4512 case WM_MBUTTONUP:
\r
4513 case WM_RBUTTONUP:
\r
4515 RightClick(Release, pt.x - boardRect.left, pt.y - boardRect.top, &fromX, &fromY);
\r
4518 case WM_MBUTTONDOWN:
\r
4519 case WM_RBUTTONDOWN:
\r
4522 fromX = fromY = -1;
\r
4523 dragInfo.pos.x = dragInfo.pos.y = -1;
\r
4524 dragInfo.start.x = dragInfo.start.y = -1;
\r
4525 dragInfo.from = dragInfo.start;
\r
4526 dragInfo.lastpos = dragInfo.pos;
\r
4527 if (appData.highlightDragging) {
\r
4528 ClearHighlights();
\r
4531 /* [HGM] right mouse button in clock area edit-game mode ups clock */
\r
4532 if (PtInRect((LPRECT) &whiteRect, pt)) {
\r
4533 if (GetKeyState(VK_SHIFT) < 0) AdjustClock(flipClock, 1);
\r
4534 } else if (PtInRect((LPRECT) &blackRect, pt)) {
\r
4535 if (GetKeyState(VK_SHIFT) < 0) AdjustClock(!flipClock, 1);
\r
4539 DrawPosition(TRUE, NULL);
\r
4541 menuNr = RightClick(Press, pt.x - boardRect.left, pt.y - boardRect.top, &fromX, &fromY);
\r
4544 if (message == WM_MBUTTONDOWN) {
\r
4545 buttonCount = 3; /* even if system didn't think so */
\r
4546 if (wParam & MK_SHIFT)
\r
4547 MenuPopup(hwnd, pt, LoadMenu(hInst, "BlackPieceMenu"), -1);
\r
4549 MenuPopup(hwnd, pt, LoadMenu(hInst, "WhitePieceMenu"), -1);
\r
4550 } else { /* message == WM_RBUTTONDOWN */
\r
4551 /* Just have one menu, on the right button. Windows users don't
\r
4552 think to try the middle one, and sometimes other software steals
\r
4553 it, or it doesn't really exist. */
\r
4554 if(gameInfo.variant != VariantShogi)
\r
4555 MenuPopup(hwnd, pt, LoadMenu(hInst, "PieceMenu"), -1);
\r
4557 MenuPopup(hwnd, pt, LoadMenu(hInst, "ShogiPieceMenu"), -1);
\r
4561 SetCapture(hwndMain);
\r
4564 hmenu = LoadMenu(hInst, "DropPieceMenu");
\r
4565 SetupDropMenu(hmenu);
\r
4566 MenuPopup(hwnd, pt, hmenu, -1);
\r
4576 /* Preprocess messages for buttons in main window */
\r
4578 ButtonProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
\r
4580 int id = GetWindowLongPtr(hwnd, GWLP_ID);
\r
4583 for (i=0; i<N_BUTTONS; i++) {
\r
4584 if (buttonDesc[i].id == id) break;
\r
4586 if (i == N_BUTTONS) return 0;
\r
4587 switch (message) {
\r
4592 dir = (wParam == VK_LEFT) ? -1 : 1;
\r
4593 SetFocus(buttonDesc[(i + dir + N_BUTTONS) % N_BUTTONS].hwnd);
\r
4600 SendMessage(hwndMain, WM_COMMAND, MAKEWPARAM(buttonDesc[i].id, 0), 0);
\r
4603 if (appData.icsActive && (isalpha((char)wParam) || wParam == '0')) {
\r
4604 // [HGM] movenum: only letters or leading zero should go to ICS input
\r
4605 HWND h = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
4606 if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);
\r
4608 SendMessage(h, WM_CHAR, wParam, lParam);
\r
4610 } else if (isalpha((char)wParam) || isdigit((char)wParam)){
\r
4611 TypeInEvent((char)wParam);
\r
4617 return CallWindowProc(buttonDesc[i].wndproc, hwnd, message, wParam, lParam);
\r
4620 static int promoStyle;
\r
4622 /* Process messages for Promotion dialog box */
\r
4624 Promotion(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
4629 switch (message) {
\r
4631 case WM_INITDIALOG: /* message: initialize dialog box */
\r
4632 /* Center the dialog over the application window */
\r
4633 CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));
\r
4634 Translate(hDlg, DLG_PromotionKing);
\r
4635 ShowWindow(GetDlgItem(hDlg, PB_King),
\r
4636 (!appData.testLegality || gameInfo.variant == VariantSuicide ||
\r
4637 gameInfo.variant == VariantSpartan && !WhiteOnMove(currentMove) ||
\r
4638 gameInfo.variant == VariantGiveaway || gameInfo.variant == VariantSuper ) ?
\r
4639 SW_SHOW : SW_HIDE);
\r
4640 /* [HGM] Only allow C & A promotions if these pieces are defined */
\r
4641 ShowWindow(GetDlgItem(hDlg, PB_Archbishop),
\r
4642 ((PieceToChar(WhiteAngel) >= 'A' && WhiteOnMove(currentMove) &&
\r
4643 PieceToChar(WhiteAngel) != '~') ||
\r
4644 (PieceToChar(BlackAngel) >= 'A' && !WhiteOnMove(currentMove) &&
\r
4645 PieceToChar(BlackAngel) != '~') ) ?
\r
4646 SW_SHOW : SW_HIDE);
\r
4647 ShowWindow(GetDlgItem(hDlg, PB_Chancellor),
\r
4648 ((PieceToChar(WhiteMarshall) >= 'A' && WhiteOnMove(currentMove) &&
\r
4649 PieceToChar(WhiteMarshall) != '~') ||
\r
4650 (PieceToChar(BlackMarshall) >= 'A' && !WhiteOnMove(currentMove) &&
\r
4651 PieceToChar(BlackMarshall) != '~') ) ?
\r
4652 SW_SHOW : SW_HIDE);
\r
4653 /* [HGM] Hide B & R button in Shogi, use Q as promote, N as defer */
\r
4654 ShowWindow(GetDlgItem(hDlg, PB_Rook), !promoStyle ? SW_SHOW : SW_HIDE);
\r
4655 ShowWindow(GetDlgItem(hDlg, PB_Bishop), !promoStyle ? SW_SHOW : SW_HIDE);
\r
4657 SetDlgItemText(hDlg, PB_Queen, "YES");
\r
4658 SetDlgItemText(hDlg, PB_Knight, "NO");
\r
4659 SetWindowText(hDlg, "Promote?");
\r
4661 ShowWindow(GetDlgItem(hDlg, IDC_Centaur),
\r
4662 gameInfo.variant == VariantSuper ?
\r
4663 SW_SHOW : SW_HIDE);
\r
4666 case WM_COMMAND: /* message: received a command */
\r
4667 switch (LOWORD(wParam)) {
\r
4669 EndDialog(hDlg, TRUE); /* Exit the dialog */
\r
4670 ClearHighlights();
\r
4671 DrawPosition(FALSE, NULL);
\r
4674 promoChar = gameInfo.variant == VariantSuper ? PieceToChar(BlackSilver) : PieceToChar(BlackKing);
\r
4677 promoChar = promoStyle ? '+' : ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteQueen : BlackQueen));
\r
4680 promoChar = ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteRook : BlackRook));
\r
4681 if(gameInfo.variant == VariantSpartan && !WhiteOnMove(currentMove)) promoChar = PieceToChar(BlackDragon);
\r
4684 promoChar = ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteBishop : BlackBishop));
\r
4685 if(gameInfo.variant == VariantSpartan && !WhiteOnMove(currentMove)) promoChar = PieceToChar(BlackAlfil);
\r
4687 case PB_Chancellor:
\r
4688 promoChar = ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteMarshall : BlackMarshall));
\r
4690 case PB_Archbishop:
\r
4691 promoChar = ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteAngel : BlackAngel));
\r
4694 promoChar = gameInfo.variant == VariantShogi ? '=' : promoStyle ? NULLCHAR :
\r
4695 ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteKnight : BlackKnight));
\r
4700 if(promoChar == '.') return FALSE; // invalid piece chosen
\r
4701 EndDialog(hDlg, TRUE); /* Exit the dialog */
\r
4702 UserMoveEvent(fromX, fromY, toX, toY, promoChar);
\r
4703 fromX = fromY = -1;
\r
4704 if (!appData.highlightLastMove) {
\r
4705 ClearHighlights();
\r
4706 DrawPosition(FALSE, NULL);
\r
4713 /* Pop up promotion dialog */
\r
4715 PromotionPopup(HWND hwnd)
\r
4719 lpProc = MakeProcInstance((FARPROC)Promotion, hInst);
\r
4720 DialogBox(hInst, MAKEINTRESOURCE(DLG_PromotionKing),
\r
4721 hwnd, (DLGPROC)lpProc);
\r
4722 FreeProcInstance(lpProc);
\r
4726 PromotionPopUp(char choice)
\r
4728 promoStyle = (choice == '+' || IS_SHOGI(gameInfo.variant));
\r
4729 DrawPosition(TRUE, NULL);
\r
4730 PromotionPopup(hwndMain);
\r
4734 LoadGameDialog(HWND hwnd, char* title)
\r
4738 char fileTitle[MSG_SIZ];
\r
4739 f = OpenFileDialog(hwnd, "rb", "",
\r
4740 appData.oldSaveStyle ? "gam" : "pgn",
\r
4742 title, &number, fileTitle, NULL);
\r
4744 cmailMsgLoaded = FALSE;
\r
4745 if (number == 0) {
\r
4746 int error = GameListBuild(f);
\r
4748 DisplayError(_("Cannot build game list"), error);
\r
4749 } else if (!ListEmpty(&gameList) &&
\r
4750 ((ListGame *) gameList.tailPred)->number > 1) {
\r
4751 GameListPopUp(f, fileTitle);
\r
4754 GameListDestroy();
\r
4757 LoadGame(f, number, fileTitle, FALSE);
\r
4761 int get_term_width()
\r
4766 HFONT hfont, hold_font;
\r
4771 hText = GetDlgItem(hwndConsole, OPT_ConsoleText);
\r
4775 // get the text metrics
\r
4776 hdc = GetDC(hText);
\r
4777 lf = font[boardSize][CONSOLE_FONT]->lf;
\r
4778 if (consoleCF.dwEffects & CFE_BOLD)
\r
4779 lf.lfWeight = FW_BOLD;
\r
4780 if (consoleCF.dwEffects & CFE_ITALIC)
\r
4781 lf.lfItalic = TRUE;
\r
4782 if (consoleCF.dwEffects & CFE_STRIKEOUT)
\r
4783 lf.lfStrikeOut = TRUE;
\r
4784 if (consoleCF.dwEffects & CFE_UNDERLINE)
\r
4785 lf.lfUnderline = TRUE;
\r
4786 hfont = CreateFontIndirect(&lf);
\r
4787 hold_font = SelectObject(hdc, hfont);
\r
4788 GetTextMetrics(hdc, &tm);
\r
4789 SelectObject(hdc, hold_font);
\r
4790 DeleteObject(hfont);
\r
4791 ReleaseDC(hText, hdc);
\r
4793 // get the rectangle
\r
4794 SendMessage(hText, EM_GETRECT, 0, (LPARAM)&rc);
\r
4796 return (rc.right-rc.left) / tm.tmAveCharWidth;
\r
4799 void UpdateICSWidth(HWND hText)
\r
4801 LONG old_width, new_width;
\r
4803 new_width = get_term_width(hText, FALSE);
\r
4804 old_width = GetWindowLongPtr(hText, GWLP_USERDATA);
\r
4805 if (new_width != old_width)
\r
4807 ics_update_width(new_width);
\r
4808 SetWindowLongPtr(hText, GWLP_USERDATA, new_width);
\r
4813 ChangedConsoleFont()
\r
4816 CHARRANGE tmpsel, sel;
\r
4817 MyFont *f = font[boardSize][CONSOLE_FONT];
\r
4818 HWND hText = GetDlgItem(hwndConsole, OPT_ConsoleText);
\r
4819 HWND hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
4822 cfmt.cbSize = sizeof(CHARFORMAT);
\r
4823 cfmt.dwMask = CFM_FACE|CFM_SIZE|CFM_CHARSET;
\r
4824 safeStrCpy(cfmt.szFaceName, font[boardSize][CONSOLE_FONT]->mfp.faceName,
\r
4825 sizeof(cfmt.szFaceName)/sizeof(cfmt.szFaceName[0]) );
\r
4826 /* yHeight is expressed in twips. A twip is 1/20 of a font's point
\r
4827 * size. This was undocumented in the version of MSVC++ that I had
\r
4828 * when I wrote the code, but is apparently documented now.
\r
4830 cfmt.yHeight = (int)(f->mfp.pointSize * 20.0 + 0.5);
\r
4831 cfmt.bCharSet = f->lf.lfCharSet;
\r
4832 cfmt.bPitchAndFamily = f->lf.lfPitchAndFamily;
\r
4833 SendMessage(hText, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &cfmt);
\r
4834 SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &cfmt);
\r
4835 /* Why are the following seemingly needed too? */
\r
4836 SendMessage(hText, EM_SETCHARFORMAT, SCF_DEFAULT, (LPARAM) &cfmt);
\r
4837 SendMessage(hInput, EM_SETCHARFORMAT, SCF_DEFAULT, (LPARAM) &cfmt);
\r
4838 SendMessage(hText, EM_EXGETSEL, 0, (LPARAM)&sel);
\r
4840 tmpsel.cpMax = -1; /*999999?*/
\r
4841 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&tmpsel);
\r
4842 SendMessage(hText, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cfmt);
\r
4843 /* Trying putting this here too. It still seems to tickle a RichEdit
\r
4844 * bug: sometimes RichEdit indents the first line of a paragraph too.
\r
4846 paraf.cbSize = sizeof(paraf);
\r
4847 paraf.dwMask = PFM_OFFSET | PFM_STARTINDENT;
\r
4848 paraf.dxStartIndent = 0;
\r
4849 paraf.dxOffset = WRAP_INDENT;
\r
4850 SendMessage(hText, EM_SETPARAFORMAT, 0, (LPARAM) ¶f);
\r
4851 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
4852 UpdateICSWidth(hText);
\r
4855 /*---------------------------------------------------------------------------*\
\r
4857 * Window Proc for main window
\r
4859 \*---------------------------------------------------------------------------*/
\r
4861 /* Process messages for main window, etc. */
\r
4863 WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
\r
4870 char fileTitle[MSG_SIZ];
\r
4871 static SnapData sd;
\r
4872 static int peek=0;
\r
4874 switch (message) {
\r
4876 case WM_PAINT: /* message: repaint portion of window */
\r
4880 case WM_ERASEBKGND:
\r
4881 if (IsIconic(hwnd)) {
\r
4882 /* Cheat; change the message */
\r
4883 return (DefWindowProc(hwnd, WM_ICONERASEBKGND, wParam, lParam));
\r
4885 return (DefWindowProc(hwnd, message, wParam, lParam));
\r
4889 case WM_LBUTTONDOWN:
\r
4890 case WM_MBUTTONDOWN:
\r
4891 case WM_RBUTTONDOWN:
\r
4892 case WM_LBUTTONUP:
\r
4893 case WM_MBUTTONUP:
\r
4894 case WM_RBUTTONUP:
\r
4895 case WM_MOUSEMOVE:
\r
4896 case WM_MOUSEWHEEL:
\r
4897 MouseEvent(hwnd, message, wParam, lParam);
\r
4901 if((char)wParam == '\b') {
\r
4902 ForwardEvent(); peek = 0;
\r
4905 JAWS_KBUP_NAVIGATION
\r
4910 if((char)wParam == '\b') {
\r
4911 if(!peek) BackwardEvent(), peek = 1;
\r
4914 JAWS_KBDOWN_NAVIGATION
\r
4920 JAWS_ALT_INTERCEPT
\r
4922 if (appData.icsActive && ((char)wParam == '\r' || (char)wParam > ' ' && !((char)wParam >= '1' && (char)wParam <= '9'))) {
\r
4923 // [HGM] movenum: for non-zero digits we always do type-in dialog
\r
4924 HWND h = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
4925 if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);
\r
4927 SendMessage(h, message, wParam, lParam);
\r
4928 } else if(lParam != KF_REPEAT) {
\r
4929 if (isalpha((char)wParam) || isdigit((char)wParam)) {
\r
4930 TypeInEvent((char)wParam);
\r
4931 } else if((char)wParam == 003) CopyGameToClipboard();
\r
4932 else if((char)wParam == 026) PasteGameOrFENFromClipboard();
\r
4937 case WM_PALETTECHANGED:
\r
4938 if (hwnd != (HWND)wParam && !appData.monoMode) {
\r
4940 HDC hdc = GetDC(hwndMain);
\r
4941 SelectPalette(hdc, hPal, TRUE);
\r
4942 nnew = RealizePalette(hdc);
\r
4944 paletteChanged = TRUE;
\r
4946 InvalidateRect(hwnd, &boardRect, FALSE);
\r
4948 ReleaseDC(hwnd, hdc);
\r
4952 case WM_QUERYNEWPALETTE:
\r
4953 if (!appData.monoMode /*&& paletteChanged*/) {
\r
4955 HDC hdc = GetDC(hwndMain);
\r
4956 paletteChanged = FALSE;
\r
4957 SelectPalette(hdc, hPal, FALSE);
\r
4958 nnew = RealizePalette(hdc);
\r
4960 InvalidateRect(hwnd, &boardRect, FALSE);
\r
4962 ReleaseDC(hwnd, hdc);
\r
4967 case WM_COMMAND: /* message: command from application menu */
\r
4968 wmId = LOWORD(wParam);
\r
4973 SAY("new game enter a move to play against the computer with white");
\r
4976 case IDM_NewGameFRC:
\r
4977 if( NewGameFRC() == 0 ) {
\r
4982 case IDM_NewVariant:
\r
4983 NewVariantPopup(hwnd);
\r
4986 case IDM_LoadGame:
\r
4987 LoadGameDialog(hwnd, _("Load Game from File"));
\r
4990 case IDM_LoadNextGame:
\r
4994 case IDM_LoadPrevGame:
\r
4998 case IDM_ReloadGame:
\r
5002 case IDM_LoadPosition:
\r
5003 if (gameMode == AnalyzeMode || gameMode == AnalyzeFile) {
\r
5004 Reset(FALSE, TRUE);
\r
5007 f = OpenFileDialog(hwnd, "rb", "",
\r
5008 appData.oldSaveStyle ? "pos" : "fen",
\r
5010 _("Load Position from File"), &number, fileTitle, NULL);
\r
5012 LoadPosition(f, number, fileTitle);
\r
5016 case IDM_LoadNextPosition:
\r
5017 ReloadPosition(1);
\r
5020 case IDM_LoadPrevPosition:
\r
5021 ReloadPosition(-1);
\r
5024 case IDM_ReloadPosition:
\r
5025 ReloadPosition(0);
\r
5028 case IDM_SaveGame:
\r
5029 defName = DefaultFileName(appData.oldSaveStyle ? "gam" : "pgn");
\r
5030 f = OpenFileDialog(hwnd, "a", defName,
\r
5031 appData.oldSaveStyle ? "gam" : "pgn",
\r
5033 _("Save Game to File"), NULL, fileTitle, NULL);
\r
5035 SaveGame(f, 0, "");
\r
5039 case IDM_SavePosition:
\r
5040 defName = DefaultFileName(appData.oldSaveStyle ? "pos" : "fen");
\r
5041 f = OpenFileDialog(hwnd, "a", defName,
\r
5042 appData.oldSaveStyle ? "pos" : "fen",
\r
5044 _("Save Position to File"), NULL, fileTitle, NULL);
\r
5046 SavePosition(f, 0, "");
\r
5050 case IDM_SaveDiagram:
\r
5051 defName = "diagram";
\r
5052 f = OpenFileDialog(hwnd, "wb", defName,
\r
5055 _("Save Diagram to File"), NULL, fileTitle, NULL);
\r
5061 case IDM_SaveSelected:
\r
5062 f = OpenFileDialog(hwnd, "a", "",
\r
5065 _("Save Game to File"), NULL, fileTitle, NULL);
\r
5067 SaveSelected(f, 0, "");
\r
5071 case IDM_CreateBook:
\r
5072 CreateBookEvent();
\r
5075 case IDM_CopyGame:
\r
5076 CopyGameToClipboard();
\r
5079 case IDM_PasteGame:
\r
5080 PasteGameFromClipboard();
\r
5083 case IDM_CopyGameListToClipboard:
\r
5084 CopyGameListToClipboard();
\r
5087 /* [AS] Autodetect FEN or PGN data */
\r
5088 case IDM_PasteAny:
\r
5089 PasteGameOrFENFromClipboard();
\r
5092 /* [AS] Move history */
\r
5093 case IDM_ShowMoveHistory:
\r
5094 if( MoveHistoryIsUp() ) {
\r
5095 MoveHistoryPopDown();
\r
5098 MoveHistoryPopUp();
\r
5102 /* [AS] Eval graph */
\r
5103 case IDM_ShowEvalGraph:
\r
5104 if( EvalGraphIsUp() ) {
\r
5105 EvalGraphPopDown();
\r
5109 SetFocus(hwndMain);
\r
5113 /* [AS] Engine output */
\r
5114 case IDM_ShowEngineOutput:
\r
5115 if( EngineOutputIsUp() ) {
\r
5116 EngineOutputPopDown();
\r
5119 EngineOutputPopUp();
\r
5123 /* [AS] User adjudication */
\r
5124 case IDM_UserAdjudication_White:
\r
5125 UserAdjudicationEvent( +1 );
\r
5128 case IDM_UserAdjudication_Black:
\r
5129 UserAdjudicationEvent( -1 );
\r
5132 case IDM_UserAdjudication_Draw:
\r
5133 UserAdjudicationEvent( 0 );
\r
5136 /* [AS] Game list options dialog */
\r
5137 case IDM_GameListOptions:
\r
5138 GameListOptions();
\r
5145 case IDM_CopyPosition:
\r
5146 CopyFENToClipboard();
\r
5149 case IDM_PastePosition:
\r
5150 PasteFENFromClipboard();
\r
5153 case IDM_MailMove:
\r
5157 case IDM_ReloadCMailMsg:
\r
5158 Reset(TRUE, TRUE);
\r
5159 ReloadCmailMsgEvent(FALSE);
\r
5162 case IDM_Minimize:
\r
5163 ShowWindow(hwnd, SW_MINIMIZE);
\r
5170 case IDM_MachineWhite:
\r
5171 MachineWhiteEvent();
\r
5173 * refresh the tags dialog only if it's visible
\r
5175 if (gameMode == MachinePlaysWhite && IsWindowVisible(editTagsDialog)) {
\r
5177 tags = PGNTags(&gameInfo);
\r
5178 TagsPopUp(tags, CmailMsg());
\r
5181 SAY("computer starts playing white");
\r
5184 case IDM_MachineBlack:
\r
5185 MachineBlackEvent();
\r
5187 * refresh the tags dialog only if it's visible
\r
5189 if (gameMode == MachinePlaysBlack && IsWindowVisible(editTagsDialog)) {
\r
5191 tags = PGNTags(&gameInfo);
\r
5192 TagsPopUp(tags, CmailMsg());
\r
5195 SAY("computer starts playing black");
\r
5198 case IDM_Match: // [HGM] match: flows into next case, after setting Match Mode and nr of Games
\r
5199 if(matchMode) EnableMenuItem(GetMenu(hwndMain), IDM_Match, MF_BYCOMMAND|MF_GRAYED);
\r
5200 MatchEvent(2); // distinguish from command-line-triggered case (matchMode=1)
\r
5203 case IDM_TwoMachines:
\r
5204 TwoMachinesEvent();
\r
5207 * refresh the tags dialog only if it's visible
\r
5209 if (gameMode == TwoMachinesPlay && IsWindowVisible(editTagsDialog)) {
\r
5211 tags = PGNTags(&gameInfo);
\r
5212 TagsPopUp(tags, CmailMsg());
\r
5215 SAY("computer starts playing both sides");
\r
5218 case IDM_AnalysisMode:
\r
5219 if(AnalyzeModeEvent()) {
\r
5220 SAY("analyzing current position");
\r
5224 case IDM_AnalyzeFile:
\r
5225 AnalyzeFileEvent();
\r
5228 case IDM_IcsClient:
\r
5232 case IDM_EditGame:
\r
5233 case IDM_EditGame2:
\r
5238 case IDM_EditPosition:
\r
5239 case IDM_EditPosition2:
\r
5240 EditPositionEvent();
\r
5241 SAY("enter a FEN string or setup a position on the board using the control R pop up menu");
\r
5244 case IDM_Training:
\r
5248 case IDM_ShowGameList:
\r
5249 ShowGameListProc();
\r
5252 case IDM_EditProgs1:
\r
5253 EditTagsPopUp(firstChessProgramNames, &firstChessProgramNames);
\r
5256 case IDM_LoadProg1:
\r
5257 LoadEnginePopUp(hwndMain, 0);
\r
5260 case IDM_LoadProg2:
\r
5261 LoadEnginePopUp(hwndMain, 1);
\r
5264 case IDM_EditServers:
\r
5265 EditTagsPopUp(icsNames, &icsNames);
\r
5268 case IDM_EditTags:
\r
5273 case IDM_EditBook:
\r
5277 case IDM_EditComment:
\r
5279 if (commentUp && editComment) {
\r
5282 EditCommentEvent();
\r
5303 case IDM_CallFlag:
\r
5323 case IDM_StopObserving:
\r
5324 StopObservingEvent();
\r
5327 case IDM_StopExamining:
\r
5328 StopExaminingEvent();
\r
5332 UploadGameEvent();
\r
5335 case IDM_TypeInMove:
\r
5336 TypeInEvent('\000');
\r
5339 case IDM_TypeInName:
\r
5340 PopUpNameDialog('\000');
\r
5343 case IDM_Backward:
\r
5345 SetFocus(hwndMain);
\r
5352 SetFocus(hwndMain);
\r
5357 SetFocus(hwndMain);
\r
5362 SetFocus(hwndMain);
\r
5365 case OPT_GameListNext: // [HGM] forward these two accelerators to Game List
\r
5366 case OPT_GameListPrev:
\r
5367 if(gameListDialog) SendMessage(gameListDialog, WM_COMMAND, wmId, 0);
\r
5371 RevertEvent(FALSE);
\r
5374 case IDM_Annotate: // [HGM] vari: revert with annotation
\r
5375 RevertEvent(TRUE);
\r
5378 case IDM_TruncateGame:
\r
5379 TruncateGameEvent();
\r
5386 case IDM_RetractMove:
\r
5387 RetractMoveEvent();
\r
5390 case IDM_FlipView:
\r
5391 flipView = !flipView;
\r
5392 DrawPosition(FALSE, NULL);
\r
5395 case IDM_FlipClock:
\r
5396 flipClock = !flipClock;
\r
5397 DisplayBothClocks();
\r
5401 case IDM_MuteSounds:
\r
5402 mute = !mute; // [HGM] mute: keep track of global muting variable
\r
5403 CheckMenuItem(GetMenu(hwndMain),IDM_MuteSounds,
\r
5404 MF_BYCOMMAND|(mute?MF_CHECKED:MF_UNCHECKED));
\r
5407 case IDM_GeneralOptions:
\r
5408 GeneralOptionsPopup(hwnd);
\r
5409 DrawPosition(TRUE, NULL);
\r
5412 case IDM_BoardOptions:
\r
5413 BoardOptionsPopup(hwnd);
\r
5416 case IDM_ThemeOptions:
\r
5417 ThemeOptionsPopup(hwnd);
\r
5420 case IDM_EnginePlayOptions:
\r
5421 EnginePlayOptionsPopup(hwnd);
\r
5424 case IDM_Engine1Options:
\r
5425 EngineOptionsPopup(hwnd, &first);
\r
5428 case IDM_Engine2Options:
\r
5430 if(WaitForEngine(&second, SettingsMenuIfReady)) break;
\r
5431 EngineOptionsPopup(hwnd, &second);
\r
5434 case IDM_OptionsUCI:
\r
5435 UciOptionsPopup(hwnd);
\r
5439 TourneyPopup(hwnd);
\r
5442 case IDM_IcsOptions:
\r
5443 IcsOptionsPopup(hwnd);
\r
5447 FontsOptionsPopup(hwnd);
\r
5451 SoundOptionsPopup(hwnd);
\r
5454 case IDM_CommPort:
\r
5455 CommPortOptionsPopup(hwnd);
\r
5458 case IDM_LoadOptions:
\r
5459 LoadOptionsPopup(hwnd);
\r
5462 case IDM_SaveOptions:
\r
5463 SaveOptionsPopup(hwnd);
\r
5466 case IDM_TimeControl:
\r
5467 TimeControlOptionsPopup(hwnd);
\r
5470 case IDM_SaveSettings:
\r
5471 SaveSettings(settingsFileName);
\r
5474 case IDM_SaveSettingsOnExit:
\r
5475 saveSettingsOnExit = !saveSettingsOnExit;
\r
5476 (void) CheckMenuItem(GetMenu(hwndMain), IDM_SaveSettingsOnExit,
\r
5477 MF_BYCOMMAND|(saveSettingsOnExit ?
\r
5478 MF_CHECKED : MF_UNCHECKED));
\r
5489 case IDM_AboutGame:
\r
5494 appData.debugMode = !appData.debugMode;
\r
5495 if (appData.debugMode) {
\r
5496 char dir[MSG_SIZ];
\r
5497 GetCurrentDirectory(MSG_SIZ, dir);
\r
5498 SetCurrentDirectory(installDir);
\r
5499 debugFP = fopen(appData.nameOfDebugFile, "w");
\r
5500 SetCurrentDirectory(dir);
\r
5501 setbuf(debugFP, NULL);
\r
5508 case IDM_HELPCONTENTS:
\r
5509 if (!MyHelp (hwnd, "winboard.hlp", HELP_KEY,(DWORD)(LPSTR)"CONTENTS") &&
\r
5510 !HtmlHelp(hwnd, "winboard.chm", 0, 0) ) {
\r
5511 MessageBox (GetFocus(),
\r
5512 _("Unable to activate help"),
\r
5513 szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);
\r
5517 case IDM_HELPSEARCH:
\r
5518 if (!MyHelp (hwnd, "winboard.hlp", HELP_PARTIALKEY, (DWORD)(LPSTR)"") &&
\r
5519 !HtmlHelp(hwnd, "winboard.chm", 0, 0) ) {
\r
5520 MessageBox (GetFocus(),
\r
5521 _("Unable to activate help"),
\r
5522 szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);
\r
5526 case IDM_HELPHELP:
\r
5527 if(!WinHelp(hwnd, (LPSTR)NULL, HELP_HELPONHELP, 0)) {
\r
5528 MessageBox (GetFocus(),
\r
5529 _("Unable to activate help"),
\r
5530 szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);
\r
5535 lpProc = MakeProcInstance((FARPROC)About, hInst);
\r
5537 (gameInfo.event && strcmp(gameInfo.event, "Easter Egg Hunt") == 0) ?
\r
5538 "AboutBox2" : "AboutBox", hwnd, (DLGPROC)lpProc);
\r
5539 FreeProcInstance(lpProc);
\r
5542 case IDM_DirectCommand1:
\r
5543 AskQuestionEvent(_("Direct Command"),
\r
5544 _("Send to chess program:"), "", "1");
\r
5546 case IDM_DirectCommand2:
\r
5547 AskQuestionEvent(_("Direct Command"),
\r
5548 _("Send to second chess program:"), "", "2");
\r
5551 case EP_WhitePawn:
\r
5552 EditPositionMenuEvent(WhitePawn, fromX, fromY);
\r
5553 fromX = fromY = -1;
\r
5556 case EP_WhiteKnight:
\r
5557 EditPositionMenuEvent(WhiteKnight, fromX, fromY);
\r
5558 fromX = fromY = -1;
\r
5561 case EP_WhiteBishop:
\r
5562 EditPositionMenuEvent(WhiteBishop, fromX, fromY);
\r
5563 fromX = fromY = -1;
\r
5566 case EP_WhiteRook:
\r
5567 EditPositionMenuEvent(WhiteRook, fromX, fromY);
\r
5568 fromX = fromY = -1;
\r
5571 case EP_WhiteQueen:
\r
5572 EditPositionMenuEvent(WhiteQueen, fromX, fromY);
\r
5573 fromX = fromY = -1;
\r
5576 case EP_WhiteFerz:
\r
5577 EditPositionMenuEvent(WhiteFerz, fromX, fromY);
\r
5578 fromX = fromY = -1;
\r
5581 case EP_WhiteWazir:
\r
5582 EditPositionMenuEvent(WhiteWazir, fromX, fromY);
\r
5583 fromX = fromY = -1;
\r
5586 case EP_WhiteAlfil:
\r
5587 EditPositionMenuEvent(WhiteAlfil, fromX, fromY);
\r
5588 fromX = fromY = -1;
\r
5591 case EP_WhiteCannon:
\r
5592 EditPositionMenuEvent(WhiteCannon, fromX, fromY);
\r
5593 fromX = fromY = -1;
\r
5596 case EP_WhiteCardinal:
\r
5597 EditPositionMenuEvent(WhiteAngel, fromX, fromY);
\r
5598 fromX = fromY = -1;
\r
5601 case EP_WhiteMarshall:
\r
5602 EditPositionMenuEvent(WhiteMarshall, fromX, fromY);
\r
5603 fromX = fromY = -1;
\r
5606 case EP_WhiteKing:
\r
5607 EditPositionMenuEvent(WhiteKing, fromX, fromY);
\r
5608 fromX = fromY = -1;
\r
5611 case EP_BlackPawn:
\r
5612 EditPositionMenuEvent(BlackPawn, fromX, fromY);
\r
5613 fromX = fromY = -1;
\r
5616 case EP_BlackKnight:
\r
5617 EditPositionMenuEvent(BlackKnight, fromX, fromY);
\r
5618 fromX = fromY = -1;
\r
5621 case EP_BlackBishop:
\r
5622 EditPositionMenuEvent(BlackBishop, fromX, fromY);
\r
5623 fromX = fromY = -1;
\r
5626 case EP_BlackRook:
\r
5627 EditPositionMenuEvent(BlackRook, fromX, fromY);
\r
5628 fromX = fromY = -1;
\r
5631 case EP_BlackQueen:
\r
5632 EditPositionMenuEvent(BlackQueen, fromX, fromY);
\r
5633 fromX = fromY = -1;
\r
5636 case EP_BlackFerz:
\r
5637 EditPositionMenuEvent(BlackFerz, fromX, fromY);
\r
5638 fromX = fromY = -1;
\r
5641 case EP_BlackWazir:
\r
5642 EditPositionMenuEvent(BlackWazir, fromX, fromY);
\r
5643 fromX = fromY = -1;
\r
5646 case EP_BlackAlfil:
\r
5647 EditPositionMenuEvent(BlackAlfil, fromX, fromY);
\r
5648 fromX = fromY = -1;
\r
5651 case EP_BlackCannon:
\r
5652 EditPositionMenuEvent(BlackCannon, fromX, fromY);
\r
5653 fromX = fromY = -1;
\r
5656 case EP_BlackCardinal:
\r
5657 EditPositionMenuEvent(BlackAngel, fromX, fromY);
\r
5658 fromX = fromY = -1;
\r
5661 case EP_BlackMarshall:
\r
5662 EditPositionMenuEvent(BlackMarshall, fromX, fromY);
\r
5663 fromX = fromY = -1;
\r
5666 case EP_BlackKing:
\r
5667 EditPositionMenuEvent(BlackKing, fromX, fromY);
\r
5668 fromX = fromY = -1;
\r
5671 case EP_EmptySquare:
\r
5672 EditPositionMenuEvent(EmptySquare, fromX, fromY);
\r
5673 fromX = fromY = -1;
\r
5676 case EP_ClearBoard:
\r
5677 EditPositionMenuEvent(ClearBoard, fromX, fromY);
\r
5678 fromX = fromY = -1;
\r
5682 EditPositionMenuEvent(WhitePlay, fromX, fromY);
\r
5683 fromX = fromY = -1;
\r
5687 EditPositionMenuEvent(BlackPlay, fromX, fromY);
\r
5688 fromX = fromY = -1;
\r
5692 EditPositionMenuEvent(PromotePiece, fromX, fromY);
\r
5693 fromX = fromY = -1;
\r
5697 EditPositionMenuEvent(DemotePiece, fromX, fromY);
\r
5698 fromX = fromY = -1;
\r
5702 DropMenuEvent(WhitePawn, fromX, fromY);
\r
5703 fromX = fromY = -1;
\r
5707 DropMenuEvent(WhiteKnight, fromX, fromY);
\r
5708 fromX = fromY = -1;
\r
5712 DropMenuEvent(WhiteBishop, fromX, fromY);
\r
5713 fromX = fromY = -1;
\r
5717 DropMenuEvent(WhiteRook, fromX, fromY);
\r
5718 fromX = fromY = -1;
\r
5722 DropMenuEvent(WhiteQueen, fromX, fromY);
\r
5723 fromX = fromY = -1;
\r
5727 barbaric = 0; appData.language = "";
\r
5728 TranslateMenus(0);
\r
5729 CheckMenuItem(GetMenu(hwndMain), lastChecked, MF_BYCOMMAND|MF_UNCHECKED);
\r
5730 CheckMenuItem(GetMenu(hwndMain), IDM_English, MF_BYCOMMAND|MF_CHECKED);
\r
5731 lastChecked = wmId;
\r
5735 if(wmId >= IDM_RecentEngines && wmId < IDM_RecentEngines + appData.recentEngines)
\r
5736 RecentEngineEvent(wmId - IDM_RecentEngines);
\r
5738 if(wmId > IDM_English && wmId < IDM_English+20) {
\r
5739 LoadLanguageFile(languageFile[wmId - IDM_English - 1]);
\r
5740 TranslateMenus(0);
\r
5741 CheckMenuItem(GetMenu(hwndMain), lastChecked, MF_BYCOMMAND|MF_UNCHECKED);
\r
5742 CheckMenuItem(GetMenu(hwndMain), wmId, MF_BYCOMMAND|MF_CHECKED);
\r
5743 lastChecked = wmId;
\r
5746 return (DefWindowProc(hwnd, message, wParam, lParam));
\r
5752 case CLOCK_TIMER_ID:
\r
5753 KillTimer(hwnd, clockTimerEvent); /* Simulate one-shot timer as in X */
\r
5754 clockTimerEvent = 0;
\r
5755 DecrementClocks(); /* call into back end */
\r
5757 case LOAD_GAME_TIMER_ID:
\r
5758 KillTimer(hwnd, loadGameTimerEvent); /* Simulate one-shot timer as in X*/
\r
5759 loadGameTimerEvent = 0;
\r
5760 AutoPlayGameLoop(); /* call into back end */
\r
5762 case ANALYSIS_TIMER_ID:
\r
5763 if ((gameMode == AnalyzeMode || gameMode == AnalyzeFile
\r
5764 || appData.icsEngineAnalyze) && appData.periodicUpdates) {
\r
5765 AnalysisPeriodicEvent(0);
\r
5767 KillTimer(hwnd, analysisTimerEvent);
\r
5768 analysisTimerEvent = 0;
\r
5771 case DELAYED_TIMER_ID:
\r
5772 KillTimer(hwnd, delayedTimerEvent);
\r
5773 delayedTimerEvent = 0;
\r
5774 delayedTimerCallback();
\r
5779 case WM_USER_Input:
\r
5780 InputEvent(hwnd, message, wParam, lParam);
\r
5783 /* [AS] Also move "attached" child windows */
\r
5784 case WM_WINDOWPOSCHANGING:
\r
5786 if( hwnd == hwndMain && appData.useStickyWindows ) {
\r
5787 LPWINDOWPOS lpwp = (LPWINDOWPOS) lParam;
\r
5789 if( ((lpwp->flags & SWP_NOMOVE) == 0) /*&& ((lpwp->flags & SWP_NOSIZE) != 0)*/ ) { // [HGM] in Win8 size always accompanies move?
\r
5790 /* Window is moving */
\r
5793 // GetWindowRect( hwnd, &rcMain ); //[HGM] sticky: in XP this returned new position, not old
\r
5794 rcMain.left = wpMain.x; // replace by these 4 lines to reconstruct old rect
\r
5795 rcMain.right = wpMain.x + wpMain.width;
\r
5796 rcMain.top = wpMain.y;
\r
5797 rcMain.bottom = wpMain.y + wpMain.height;
\r
5799 ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, moveHistoryDialog, &wpMoveHistory );
\r
5800 ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, evalGraphDialog, &wpEvalGraph );
\r
5801 ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, engineOutputDialog, &wpEngineOutput );
\r
5802 ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, gameListDialog, &wpGameList );
\r
5803 ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, hwndConsole, &wpConsole );
\r
5804 wpMain.x = lpwp->x;
\r
5805 wpMain.y = lpwp->y;
\r
5811 /* [AS] Snapping */
\r
5812 case WM_ENTERSIZEMOVE:
\r
5813 if(appData.debugMode) { fprintf(debugFP, "size-move\n"); }
\r
5814 if (hwnd == hwndMain) {
\r
5815 doingSizing = TRUE;
\r
5818 return OnEnterSizeMove( &sd, hwnd, wParam, lParam );
\r
5822 if(appData.debugMode) { fprintf(debugFP, "sizing\n"); }
\r
5823 if (hwnd == hwndMain) {
\r
5824 lastSizing = wParam;
\r
5829 if(appData.debugMode) { fprintf(debugFP, "moving\n"); }
\r
5830 return OnMoving( &sd, hwnd, wParam, lParam );
\r
5832 case WM_EXITSIZEMOVE:
\r
5833 if(appData.debugMode) { fprintf(debugFP, "exit size-move, size = %d\n", squareSize); }
\r
5834 if (hwnd == hwndMain) {
\r
5836 doingSizing = FALSE;
\r
5837 InvalidateRect(hwnd, &boardRect, FALSE);
\r
5838 GetClientRect(hwnd, &client);
\r
5839 ResizeBoard(client.right, client.bottom, lastSizing);
\r
5841 if(appData.debugMode) { fprintf(debugFP, "square size = %d\n", squareSize); }
\r
5843 return OnExitSizeMove( &sd, hwnd, wParam, lParam );
\r
5846 case WM_DESTROY: /* message: window being destroyed */
\r
5847 PostQuitMessage(0);
\r
5851 if (hwnd == hwndMain) {
\r
5856 default: /* Passes it on if unprocessed */
\r
5857 return (DefWindowProc(hwnd, message, wParam, lParam));
\r
5864 /*---------------------------------------------------------------------------*\
\r
5866 * Misc utility routines
\r
5868 \*---------------------------------------------------------------------------*/
\r
5871 * Decent random number generator, at least not as bad as Windows
\r
5872 * standard rand, which returns a value in the range 0 to 0x7fff.
\r
5874 unsigned int randstate;
\r
5879 randstate = randstate * 1664525 + 1013904223;
\r
5880 return (int) randstate & 0x7fffffff;
\r
5884 mysrandom(unsigned int seed)
\r
5891 * returns TRUE if user selects a different color, FALSE otherwise
\r
5895 ChangeColor(HWND hwnd, COLORREF *which)
\r
5897 static BOOL firstTime = TRUE;
\r
5898 static DWORD customColors[16];
\r
5900 COLORREF newcolor;
\r
5905 /* Make initial colors in use available as custom colors */
\r
5906 /* Should we put the compiled-in defaults here instead? */
\r
5908 customColors[i++] = lightSquareColor & 0xffffff;
\r
5909 customColors[i++] = darkSquareColor & 0xffffff;
\r
5910 customColors[i++] = whitePieceColor & 0xffffff;
\r
5911 customColors[i++] = blackPieceColor & 0xffffff;
\r
5912 customColors[i++] = highlightSquareColor & 0xffffff;
\r
5913 customColors[i++] = premoveHighlightColor & 0xffffff;
\r
5915 for (ccl = (ColorClass) 0; ccl < NColorClasses && i < 16; ccl++) {
\r
5916 customColors[i++] = textAttribs[ccl].color;
\r
5918 while (i < 16) customColors[i++] = RGB(255, 255, 255);
\r
5919 firstTime = FALSE;
\r
5922 cc.lStructSize = sizeof(cc);
\r
5923 cc.hwndOwner = hwnd;
\r
5924 cc.hInstance = NULL;
\r
5925 cc.rgbResult = (DWORD) (*which & 0xffffff);
\r
5926 cc.lpCustColors = (LPDWORD) customColors;
\r
5927 cc.Flags = CC_RGBINIT|CC_FULLOPEN;
\r
5929 if (!ChooseColor(&cc)) return FALSE;
\r
5931 newcolor = (COLORREF) (0x2000000 | cc.rgbResult);
\r
5932 if (newcolor == *which) return FALSE;
\r
5933 *which = newcolor;
\r
5937 InitDrawingColors();
\r
5938 InvalidateRect(hwnd, &boardRect, FALSE);
\r
5943 MyLoadSound(MySound *ms)
\r
5949 if (ms->data && ms->flag) free(ms->data);
\r
5952 switch (ms->name[0]) {
\r
5958 /* System sound from Control Panel. Don't preload here. */
\r
5962 if (ms->name[1] == NULLCHAR) {
\r
5963 /* "!" alone = silence */
\r
5966 /* Builtin wave resource. Error if not found. */
\r
5967 HANDLE h = FindResource(hInst, ms->name + 1, "WAVE");
\r
5968 if (h == NULL) break;
\r
5969 ms->data = (void *)LoadResource(hInst, h);
\r
5970 ms->flag = 0; // not maloced, so cannot be freed!
\r
5971 if (h == NULL) break;
\r
5976 /* .wav file. Error if not found. */
\r
5977 f = fopen(ms->name, "rb");
\r
5978 if (f == NULL) break;
\r
5979 if (fstat(fileno(f), &st) < 0) break;
\r
5980 ms->data = malloc(st.st_size);
\r
5982 if (fread(ms->data, st.st_size, 1, f) < 1) break;
\r
5988 char buf[MSG_SIZ];
\r
5989 snprintf(buf, MSG_SIZ, _("Error loading sound %s"), ms->name);
\r
5990 DisplayError(buf, GetLastError());
\r
5996 MyPlaySound(MySound *ms)
\r
5998 BOOLEAN ok = FALSE;
\r
6000 if(mute) return TRUE; // [HGM] mute: suppress all sound play when muted
\r
6001 switch (ms->name[0]) {
\r
6003 if(appData.debugMode) fprintf(debugFP, "silence\n");
\r
6008 /* System sound from Control Panel (deprecated feature).
\r
6009 "$" alone or an unset sound name gets default beep (still in use). */
\r
6010 if (ms->name[1]) {
\r
6011 ok = PlaySound(ms->name + 1, NULL, SND_ALIAS|SND_ASYNC);
\r
6013 if (!ok) ok = MessageBeep(MB_OK);
\r
6016 /* Builtin wave resource, or "!" alone for silence */
\r
6017 if (ms->name[1]) {
\r
6018 if (ms->data == NULL) return FALSE;
\r
6019 ok = PlaySound(ms->data, NULL, SND_MEMORY|SND_ASYNC);
\r
6025 /* .wav file. Error if not found. */
\r
6026 if (ms->data == NULL) return FALSE;
\r
6027 ok = PlaySound(ms->data, NULL, SND_MEMORY|SND_ASYNC);
\r
6030 /* Don't print an error: this can happen innocently if the sound driver
\r
6031 is busy; for instance, if another instance of WinBoard is playing
\r
6032 a sound at about the same time. */
\r
6038 OldOpenFileHook(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
6041 OPENFILENAME *ofn;
\r
6042 static UINT *number; /* gross that this is static */
\r
6044 switch (message) {
\r
6045 case WM_INITDIALOG: /* message: initialize dialog box */
\r
6046 /* Center the dialog over the application window */
\r
6047 ofn = (OPENFILENAME *) lParam;
\r
6048 if (ofn->Flags & OFN_ENABLETEMPLATE) {
\r
6049 number = (UINT *) ofn->lCustData;
\r
6050 SendMessage(GetDlgItem(hDlg, edt2), WM_SETTEXT, 0, (LPARAM) "");
\r
6054 CenterWindow (hDlg, GetWindow (hDlg, GW_OWNER));
\r
6055 Translate(hDlg, 1536);
\r
6056 return FALSE; /* Allow for further processing */
\r
6059 if ((LOWORD(wParam) == IDOK) && (number != NULL)) {
\r
6060 *number = GetDlgItemInt(hDlg, OPT_IndexNumberOld, &ok, FALSE);
\r
6062 return FALSE; /* Allow for further processing */
\r
6068 OpenFileHook(HWND hdlg, UINT uiMsg, WPARAM wParam, LPARAM lParam)
\r
6070 static UINT *number;
\r
6071 OPENFILENAME *ofname;
\r
6074 case WM_INITDIALOG:
\r
6075 Translate(hdlg, DLG_IndexNumber);
\r
6076 ofname = (OPENFILENAME *)lParam;
\r
6077 number = (UINT *)(ofname->lCustData);
\r
6080 ofnot = (OFNOTIFY *)lParam;
\r
6081 if (ofnot->hdr.code == CDN_FILEOK) {
\r
6082 *number = GetDlgItemInt(hdlg, OPT_IndexNumber, NULL, FALSE);
\r
6091 OpenFileDialog(HWND hwnd, char *write, char *defName, char *defExt, // [HGM] diag: type of 'write' now string
\r
6092 char *nameFilt, char *dlgTitle, UINT *number,
\r
6093 char fileTitle[MSG_SIZ], char fileName[MSG_SIZ])
\r
6095 OPENFILENAME openFileName;
\r
6096 char buf1[MSG_SIZ];
\r
6099 if (fileName == NULL) fileName = buf1;
\r
6100 if (defName == NULL) {
\r
6101 safeStrCpy(fileName, "*.", 3 );
\r
6102 strcat(fileName, defExt);
\r
6104 safeStrCpy(fileName, defName, MSG_SIZ );
\r
6106 if (fileTitle) safeStrCpy(fileTitle, "", MSG_SIZ );
\r
6107 if (number) *number = 0;
\r
6109 openFileName.lStructSize = sizeof(OPENFILENAME);
\r
6110 openFileName.hwndOwner = hwnd;
\r
6111 openFileName.hInstance = (HANDLE) hInst;
\r
6112 openFileName.lpstrFilter = nameFilt;
\r
6113 openFileName.lpstrCustomFilter = (LPSTR) NULL;
\r
6114 openFileName.nMaxCustFilter = 0L;
\r
6115 openFileName.nFilterIndex = 1L;
\r
6116 openFileName.lpstrFile = fileName;
\r
6117 openFileName.nMaxFile = MSG_SIZ;
\r
6118 openFileName.lpstrFileTitle = fileTitle;
\r
6119 openFileName.nMaxFileTitle = fileTitle ? MSG_SIZ : 0;
\r
6120 openFileName.lpstrInitialDir = NULL;
\r
6121 openFileName.lpstrTitle = dlgTitle;
\r
6122 openFileName.Flags = OFN_PATHMUSTEXIST | OFN_HIDEREADONLY
\r
6123 | (write[0] != 'r' ? 0 : OFN_FILEMUSTEXIST)
\r
6124 | (number ? OFN_ENABLETEMPLATE | OFN_ENABLEHOOK: 0)
\r
6125 | (oldDialog ? 0 : OFN_EXPLORER);
\r
6126 openFileName.nFileOffset = 0;
\r
6127 openFileName.nFileExtension = 0;
\r
6128 openFileName.lpstrDefExt = defExt;
\r
6129 openFileName.lCustData = (LONG) number;
\r
6130 openFileName.lpfnHook = oldDialog ?
\r
6131 (LPOFNHOOKPROC) OldOpenFileHook : (LPOFNHOOKPROC) OpenFileHook;
\r
6132 openFileName.lpTemplateName = (LPSTR)(oldDialog ? 1536 : DLG_IndexNumber);
\r
6134 if (write[0] != 'r' ? GetSaveFileName(&openFileName) :
\r
6135 GetOpenFileName(&openFileName)) {
\r
6136 /* open the file */
\r
6137 f = fopen(openFileName.lpstrFile, write);
\r
6139 MessageBox(hwnd, _("File open failed"), NULL,
\r
6140 MB_OK|MB_ICONEXCLAMATION);
\r
6144 int err = CommDlgExtendedError();
\r
6145 if (err != 0) DisplayError(_("Internal error in file dialog box"), err);
\r
6154 MenuPopup(HWND hwnd, POINT pt, HMENU hmenu, UINT def)
\r
6156 HMENU hmenuTrackPopup; /* floating pop-up menu */
\r
6159 * Get the first pop-up menu in the menu template. This is the
\r
6160 * menu that TrackPopupMenu displays.
\r
6162 hmenuTrackPopup = GetSubMenu(hmenu, 0);
\r
6163 TranslateOneMenu(10, hmenuTrackPopup);
\r
6165 SetMenuDefaultItem(hmenuTrackPopup, def, FALSE);
\r
6168 * TrackPopup uses screen coordinates, so convert the
\r
6169 * coordinates of the mouse click to screen coordinates.
\r
6171 ClientToScreen(hwnd, (LPPOINT) &pt);
\r
6173 /* Draw and track the floating pop-up menu. */
\r
6174 TrackPopupMenu(hmenuTrackPopup, TPM_CENTERALIGN | TPM_RIGHTBUTTON,
\r
6175 pt.x, pt.y, 0, hwnd, NULL);
\r
6177 /* Destroy the menu.*/
\r
6178 DestroyMenu(hmenu);
\r
6183 int sizeX, sizeY, newSizeX, newSizeY;
\r
6185 } ResizeEditPlusButtonsClosure;
\r
6188 ResizeEditPlusButtonsCallback(HWND hChild, LPARAM lparam)
\r
6190 ResizeEditPlusButtonsClosure *cl = (ResizeEditPlusButtonsClosure *)lparam;
\r
6194 if (hChild == cl->hText) return TRUE;
\r
6195 GetWindowRect(hChild, &rect); /* gives screen coords */
\r
6196 pt.x = rect.left + (cl->newSizeX - cl->sizeX)/2;
\r
6197 pt.y = rect.top + cl->newSizeY - cl->sizeY;
\r
6198 ScreenToClient(cl->hDlg, &pt);
\r
6199 cl->hdwp = DeferWindowPos(cl->hdwp, hChild, NULL,
\r
6200 pt.x, pt.y, rect.right - rect.left, rect.bottom - rect.top, SWP_NOZORDER);
\r
6204 /* Resize a dialog that has a (rich) edit field filling most of
\r
6205 the top, with a row of buttons below */
\r
6207 ResizeEditPlusButtons(HWND hDlg, HWND hText, int sizeX, int sizeY, int newSizeX, int newSizeY)
\r
6210 int newTextHeight, newTextWidth;
\r
6211 ResizeEditPlusButtonsClosure cl;
\r
6213 /*if (IsIconic(hDlg)) return;*/
\r
6214 if (newSizeX == sizeX && newSizeY == sizeY) return;
\r
6216 cl.hdwp = BeginDeferWindowPos(8);
\r
6218 GetWindowRect(hText, &rectText); /* gives screen coords */
\r
6219 newTextWidth = rectText.right - rectText.left + newSizeX - sizeX;
\r
6220 newTextHeight = rectText.bottom - rectText.top + newSizeY - sizeY;
\r
6221 if (newTextHeight < 0) {
\r
6222 newSizeY += -newTextHeight;
\r
6223 newTextHeight = 0;
\r
6225 cl.hdwp = DeferWindowPos(cl.hdwp, hText, NULL, 0, 0,
\r
6226 newTextWidth, newTextHeight, SWP_NOZORDER|SWP_NOMOVE);
\r
6232 cl.newSizeX = newSizeX;
\r
6233 cl.newSizeY = newSizeY;
\r
6234 EnumChildWindows(hDlg, ResizeEditPlusButtonsCallback, (LPARAM)&cl);
\r
6236 EndDeferWindowPos(cl.hdwp);
\r
6239 BOOL CenterWindowEx(HWND hwndChild, HWND hwndParent, int mode)
\r
6241 RECT rChild, rParent;
\r
6242 int wChild, hChild, wParent, hParent;
\r
6243 int wScreen, hScreen, xNew, yNew;
\r
6246 /* Get the Height and Width of the child window */
\r
6247 GetWindowRect (hwndChild, &rChild);
\r
6248 wChild = rChild.right - rChild.left;
\r
6249 hChild = rChild.bottom - rChild.top;
\r
6251 /* Get the Height and Width of the parent window */
\r
6252 GetWindowRect (hwndParent, &rParent);
\r
6253 wParent = rParent.right - rParent.left;
\r
6254 hParent = rParent.bottom - rParent.top;
\r
6256 /* Get the display limits */
\r
6257 hdc = GetDC (hwndChild);
\r
6258 wScreen = GetDeviceCaps (hdc, HORZRES);
\r
6259 hScreen = GetDeviceCaps (hdc, VERTRES);
\r
6260 ReleaseDC(hwndChild, hdc);
\r
6262 /* Calculate new X position, then adjust for screen */
\r
6263 xNew = rParent.left + ((wParent - wChild) /2);
\r
6266 } else if ((xNew+wChild) > wScreen) {
\r
6267 xNew = wScreen - wChild;
\r
6270 /* Calculate new Y position, then adjust for screen */
\r
6272 yNew = rParent.top + ((hParent - hChild) /2);
\r
6275 yNew = rParent.top + GetSystemMetrics( SM_CYCAPTION ) * 2 / 3;
\r
6280 } else if ((yNew+hChild) > hScreen) {
\r
6281 yNew = hScreen - hChild;
\r
6284 /* Set it, and return */
\r
6285 return SetWindowPos (hwndChild, NULL,
\r
6286 xNew, yNew, 0, 0, SWP_NOSIZE | SWP_NOZORDER);
\r
6289 /* Center one window over another */
\r
6290 BOOL CenterWindow (HWND hwndChild, HWND hwndParent)
\r
6292 return CenterWindowEx( hwndChild, hwndParent, 0 );
\r
6295 /*---------------------------------------------------------------------------*\
\r
6297 * Startup Dialog functions
\r
6299 \*---------------------------------------------------------------------------*/
\r
6301 InitComboStrings(HANDLE hwndCombo, char **cd)
\r
6303 SendMessage(hwndCombo, CB_RESETCONTENT, 0, 0);
\r
6305 while (*cd != NULL) {
\r
6306 SendMessage(hwndCombo, CB_ADDSTRING, 0, (LPARAM) T_(*cd));
\r
6312 InitComboStringsFromOption(HANDLE hwndCombo, char *str)
\r
6314 char buf1[MAX_ARG_LEN];
\r
6317 if (str[0] == '@') {
\r
6318 FILE* f = fopen(str + 1, "r");
\r
6320 DisplayFatalError(str + 1, errno, 2);
\r
6323 len = fread(buf1, 1, sizeof(buf1)-1, f);
\r
6325 buf1[len] = NULLCHAR;
\r
6331 SendMessage(hwndCombo, CB_RESETCONTENT, 0, 0);
\r
6334 char buf[MSG_SIZ];
\r
6335 char *end = strchr(str, '\n');
\r
6336 if (end == NULL) return;
\r
6337 memcpy(buf, str, end - str);
\r
6338 buf[end - str] = NULLCHAR;
\r
6339 SendMessage(hwndCombo, CB_ADDSTRING, 0, (LPARAM) buf);
\r
6345 SetStartupDialogEnables(HWND hDlg)
\r
6347 EnableWindow(GetDlgItem(hDlg, OPT_ChessEngineName),
\r
6348 IsDlgButtonChecked(hDlg, OPT_ChessEngine) ||
\r
6349 (appData.zippyPlay && IsDlgButtonChecked(hDlg, OPT_ChessServer)));
\r
6350 EnableWindow(GetDlgItem(hDlg, OPT_SecondChessEngineName),
\r
6351 IsDlgButtonChecked(hDlg, OPT_ChessEngine));
\r
6352 EnableWindow(GetDlgItem(hDlg, OPT_ChessServerName),
\r
6353 IsDlgButtonChecked(hDlg, OPT_ChessServer));
\r
6354 EnableWindow(GetDlgItem(hDlg, OPT_AdditionalOptions),
\r
6355 IsDlgButtonChecked(hDlg, OPT_AnyAdditional));
\r
6356 EnableWindow(GetDlgItem(hDlg, IDOK),
\r
6357 IsDlgButtonChecked(hDlg, OPT_ChessEngine) ||
\r
6358 IsDlgButtonChecked(hDlg, OPT_ChessServer) ||
\r
6359 IsDlgButtonChecked(hDlg, OPT_View));
\r
6363 QuoteForFilename(char *filename)
\r
6365 int dquote, space;
\r
6366 dquote = strchr(filename, '"') != NULL;
\r
6367 space = strchr(filename, ' ') != NULL;
\r
6368 if (dquote || space) {
\r
6380 InitEngineBox(HWND hDlg, HWND hwndCombo, char* nthcp, char* nthd, char* nthdir, char *nthnames)
\r
6382 char buf[MSG_SIZ];
\r
6385 InitComboStringsFromOption(hwndCombo, nthnames);
\r
6386 q = QuoteForFilename(nthcp);
\r
6387 snprintf(buf, MSG_SIZ, "%s%s%s", q, nthcp, q);
\r
6388 if (*nthdir != NULLCHAR) {
\r
6389 q = QuoteForFilename(nthdir);
\r
6390 snprintf(buf + strlen(buf), MSG_SIZ, " /%s=%s%s%s", nthd, q, nthdir, q);
\r
6392 if (*nthcp == NULLCHAR) {
\r
6393 SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) 0, (LPARAM) 0);
\r
6394 } else if (SendMessage(hwndCombo, CB_SELECTSTRING, (WPARAM) -1, (LPARAM) buf) == CB_ERR) {
\r
6395 SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) -1, (LPARAM) 0);
\r
6396 SendMessage(hwndCombo, WM_SETTEXT, (WPARAM) 0, (LPARAM) buf);
\r
6401 StartupDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
6403 char buf[MSG_SIZ];
\r
6407 switch (message) {
\r
6408 case WM_INITDIALOG:
\r
6409 /* Center the dialog */
\r
6410 CenterWindow (hDlg, GetDesktopWindow());
\r
6411 Translate(hDlg, DLG_Startup);
\r
6412 /* Initialize the dialog items */
\r
6413 InitEngineBox(hDlg, GetDlgItem(hDlg, OPT_ChessEngineName),
\r
6414 appData.firstChessProgram, "fd", appData.firstDirectory,
\r
6415 firstChessProgramNames);
\r
6416 InitEngineBox(hDlg, GetDlgItem(hDlg, OPT_SecondChessEngineName),
\r
6417 appData.secondChessProgram, singleList ? "fd" : "sd", appData.secondDirectory,
\r
6418 singleList ? firstChessProgramNames : secondChessProgramNames); //[HGM] single: use first list in second combo
\r
6419 hwndCombo = GetDlgItem(hDlg, OPT_ChessServerName);
\r
6420 InitComboStringsFromOption(hwndCombo, icsNames);
\r
6421 snprintf(buf, MSG_SIZ, "%s /icsport=%s", appData.icsHost, appData.icsPort);
\r
6422 if (*appData.icsHelper != NULLCHAR) {
\r
6423 char *q = QuoteForFilename(appData.icsHelper);
\r
6424 sprintf(buf + strlen(buf), " /icshelper=%s%s%s", q, appData.icsHelper, q);
\r
6426 if (*appData.icsHost == NULLCHAR) {
\r
6427 SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) 0, (LPARAM) 0);
\r
6428 /*SendMessage(hwndCombo, CB_SHOWDROPDOWN, (WPARAM) TRUE, (LPARAM) 0); !!too soon */
\r
6429 } else if (SendMessage(hwndCombo, CB_SELECTSTRING, (WPARAM) -1, (LPARAM) buf) == CB_ERR) {
\r
6430 SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) -1, (LPARAM) 0);
\r
6431 SendMessage(hwndCombo, WM_SETTEXT, (WPARAM) 0, (LPARAM) buf);
\r
6434 if (appData.icsActive) {
\r
6435 CheckDlgButton(hDlg, OPT_ChessServer, BST_CHECKED);
\r
6437 else if (appData.noChessProgram) {
\r
6438 CheckDlgButton(hDlg, OPT_View, BST_CHECKED);
\r
6441 CheckDlgButton(hDlg, OPT_ChessEngine, BST_CHECKED);
\r
6444 SetStartupDialogEnables(hDlg);
\r
6448 switch (LOWORD(wParam)) {
\r
6450 if (IsDlgButtonChecked(hDlg, OPT_ChessEngine)) {
\r
6451 safeStrCpy(buf, "/fcp=", sizeof(buf)/sizeof(buf[0]) );
\r
6452 GetDlgItemText(hDlg, OPT_ChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));
\r
6454 comboLine = strdup(p+5); // [HGM] recent: remember complete line of first combobox
\r
6455 ParseArgs(StringGet, &p);
\r
6456 safeStrCpy(buf, singleList ? "/fcp=" : "/scp=", sizeof(buf)/sizeof(buf[0]) );
\r
6457 GetDlgItemText(hDlg, OPT_SecondChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));
\r
6459 SwapEngines(singleList); // temporarily swap first and second, to load a second 'first', ...
\r
6460 ParseArgs(StringGet, &p);
\r
6461 SwapEngines(singleList); // ... and then make it 'second'
\r
6463 appData.noChessProgram = FALSE;
\r
6464 appData.icsActive = FALSE;
\r
6465 } else if (IsDlgButtonChecked(hDlg, OPT_ChessServer)) {
\r
6466 safeStrCpy(buf, "/ics /icshost=", sizeof(buf)/sizeof(buf[0]) );
\r
6467 GetDlgItemText(hDlg, OPT_ChessServerName, buf + strlen(buf), sizeof(buf) - strlen(buf));
\r
6469 ParseArgs(StringGet, &p);
\r
6470 if (appData.zippyPlay) {
\r
6471 safeStrCpy(buf, "/fcp=", sizeof(buf)/sizeof(buf[0]) );
\r
6472 GetDlgItemText(hDlg, OPT_ChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));
\r
6474 ParseArgs(StringGet, &p);
\r
6476 } else if (IsDlgButtonChecked(hDlg, OPT_View)) {
\r
6477 appData.noChessProgram = TRUE;
\r
6478 appData.icsActive = FALSE;
\r
6480 MessageBox(hDlg, _("Choose an option, or cancel to exit"),
\r
6481 _("Option Error"), MB_OK|MB_ICONEXCLAMATION);
\r
6484 if (IsDlgButtonChecked(hDlg, OPT_AnyAdditional)) {
\r
6485 GetDlgItemText(hDlg, OPT_AdditionalOptions, buf, sizeof(buf));
\r
6487 ParseArgs(StringGet, &p);
\r
6489 EndDialog(hDlg, TRUE);
\r
6496 case IDM_HELPCONTENTS:
\r
6497 if (!WinHelp (hDlg, "winboard.hlp", HELP_KEY,(DWORD)(LPSTR)"CONTENTS")) {
\r
6498 MessageBox (GetFocus(),
\r
6499 _("Unable to activate help"),
\r
6500 szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);
\r
6505 SetStartupDialogEnables(hDlg);
\r
6513 /*---------------------------------------------------------------------------*\
\r
6515 * About box dialog functions
\r
6517 \*---------------------------------------------------------------------------*/
\r
6519 /* Process messages for "About" dialog box */
\r
6521 About(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
6523 switch (message) {
\r
6524 case WM_INITDIALOG: /* message: initialize dialog box */
\r
6525 /* Center the dialog over the application window */
\r
6526 CenterWindow (hDlg, GetWindow (hDlg, GW_OWNER));
\r
6527 SetDlgItemText(hDlg, ABOUTBOX_Version, programVersion);
\r
6528 Translate(hDlg, ABOUTBOX);
\r
6532 case WM_COMMAND: /* message: received a command */
\r
6533 if (LOWORD(wParam) == IDOK /* "OK" box selected? */
\r
6534 || LOWORD(wParam) == IDCANCEL) { /* System menu close command? */
\r
6535 EndDialog(hDlg, TRUE); /* Exit the dialog */
\r
6543 /*---------------------------------------------------------------------------*\
\r
6545 * Comment Dialog functions
\r
6547 \*---------------------------------------------------------------------------*/
\r
6550 CommentDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
6552 static HANDLE hwndText = NULL;
\r
6553 int len, newSizeX, newSizeY;
\r
6554 static int sizeX, sizeY;
\r
6559 switch (message) {
\r
6560 case WM_INITDIALOG: /* message: initialize dialog box */
\r
6561 /* Initialize the dialog items */
\r
6562 Translate(hDlg, DLG_EditComment);
\r
6563 hwndText = GetDlgItem(hDlg, OPT_CommentText);
\r
6564 SetDlgItemText(hDlg, OPT_CommentText, commentText);
\r
6565 EnableWindow(GetDlgItem(hDlg, OPT_CancelComment), editComment);
\r
6566 EnableWindow(GetDlgItem(hDlg, OPT_ClearComment), editComment);
\r
6567 EnableWindow(GetDlgItem(hDlg, OPT_EditComment), !editComment);
\r
6568 SendMessage(hwndText, EM_SETREADONLY, !editComment, 0);
\r
6569 SetWindowText(hDlg, commentTitle);
\r
6570 if (editComment) {
\r
6571 SetFocus(hwndText);
\r
6573 SetFocus(GetDlgItem(hDlg, IDOK));
\r
6575 SendMessage(GetDlgItem(hDlg, OPT_CommentText),
\r
6576 WM_SETFONT, (WPARAM)font[boardSize][COMMENT_FONT]->hf,
\r
6577 MAKELPARAM(FALSE, 0));
\r
6578 /* Size and position the dialog */
\r
6579 if (!commentDialog) {
\r
6580 commentDialog = hDlg;
\r
6581 GetClientRect(hDlg, &rect);
\r
6582 sizeX = rect.right;
\r
6583 sizeY = rect.bottom;
\r
6584 if (wpComment.x != CW_USEDEFAULT && wpComment.y != CW_USEDEFAULT &&
\r
6585 wpComment.width != CW_USEDEFAULT && wpComment.height != CW_USEDEFAULT) {
\r
6586 WINDOWPLACEMENT wp;
\r
6587 EnsureOnScreen(&wpComment.x, &wpComment.y, 0, 0);
\r
6588 wp.length = sizeof(WINDOWPLACEMENT);
\r
6590 wp.showCmd = SW_SHOW;
\r
6591 wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;
\r
6592 wp.rcNormalPosition.left = wpComment.x;
\r
6593 wp.rcNormalPosition.right = wpComment.x + wpComment.width;
\r
6594 wp.rcNormalPosition.top = wpComment.y;
\r
6595 wp.rcNormalPosition.bottom = wpComment.y + wpComment.height;
\r
6596 SetWindowPlacement(hDlg, &wp);
\r
6598 GetClientRect(hDlg, &rect);
\r
6599 newSizeX = rect.right;
\r
6600 newSizeY = rect.bottom;
\r
6601 ResizeEditPlusButtons(hDlg, hwndText, sizeX, sizeY,
\r
6602 newSizeX, newSizeY);
\r
6607 SendDlgItemMessage( hDlg, OPT_CommentText, EM_SETEVENTMASK, 0, ENM_MOUSEEVENTS | ENM_KEYEVENTS );
\r
6610 case WM_COMMAND: /* message: received a command */
\r
6611 switch (LOWORD(wParam)) {
\r
6613 if (editComment) {
\r
6615 /* Read changed options from the dialog box */
\r
6616 hwndText = GetDlgItem(hDlg, OPT_CommentText);
\r
6617 len = GetWindowTextLength(hwndText);
\r
6618 str = (char *) malloc(len + 1);
\r
6619 GetWindowText(hwndText, str, len + 1);
\r
6628 ReplaceComment(commentIndex, str);
\r
6635 case OPT_CancelComment:
\r
6639 case OPT_ClearComment:
\r
6640 SetDlgItemText(hDlg, OPT_CommentText, "");
\r
6643 case OPT_EditComment:
\r
6644 EditCommentEvent();
\r
6652 case WM_NOTIFY: // [HGM] vari: cloned from whistory.c
\r
6653 if( wParam == OPT_CommentText ) {
\r
6654 MSGFILTER * lpMF = (MSGFILTER *) lParam;
\r
6656 if( lpMF->msg == WM_RBUTTONDOWN && (lpMF->wParam & (MK_CONTROL | MK_SHIFT)) == 0 ||
\r
6657 lpMF->msg == WM_CHAR && lpMF->wParam == '\022' ) {
\r
6661 pt.x = LOWORD( lpMF->lParam );
\r
6662 pt.y = HIWORD( lpMF->lParam );
\r
6664 if(lpMF->msg == WM_CHAR) {
\r
6666 SendDlgItemMessage( hDlg, OPT_CommentText, EM_EXGETSEL, 0, (LPARAM) &sel );
\r
6667 index = sel.cpMin;
\r
6669 index = SendDlgItemMessage( hDlg, OPT_CommentText, EM_CHARFROMPOS, 0, (LPARAM) &pt );
\r
6671 hwndText = GetDlgItem(hDlg, OPT_CommentText); // cloned from above
\r
6672 len = GetWindowTextLength(hwndText);
\r
6673 str = (char *) malloc(len + 1);
\r
6674 GetWindowText(hwndText, str, len + 1);
\r
6675 ReplaceComment(commentIndex, str);
\r
6676 if(commentIndex != currentMove) ToNrEvent(commentIndex);
\r
6677 LoadVariation( index, str ); // [HGM] also does the actual moving to it, now
\r
6680 /* Zap the message for good: apparently, returning non-zero is not enough */
\r
6681 lpMF->msg = WM_USER;
\r
6689 newSizeX = LOWORD(lParam);
\r
6690 newSizeY = HIWORD(lParam);
\r
6691 ResizeEditPlusButtons(hDlg, hwndText, sizeX, sizeY, newSizeX, newSizeY);
\r
6696 case WM_GETMINMAXINFO:
\r
6697 /* Prevent resizing window too small */
\r
6698 mmi = (MINMAXINFO *) lParam;
\r
6699 mmi->ptMinTrackSize.x = 100;
\r
6700 mmi->ptMinTrackSize.y = 100;
\r
6707 EitherCommentPopUp(int index, char *title, char *str, BOOLEAN edit)
\r
6712 CheckMenuItem(GetMenu(hwndMain), IDM_Comment, edit ? MF_CHECKED : MF_UNCHECKED);
\r
6714 if (str == NULL) str = "";
\r
6715 p = (char *) malloc(2 * strlen(str) + 2);
\r
6718 if (*str == '\n') *q++ = '\r';
\r
6722 if (commentText != NULL) free(commentText);
\r
6724 commentIndex = index;
\r
6725 commentTitle = title;
\r
6727 editComment = edit;
\r
6729 if (commentDialog) {
\r
6730 SendMessage(commentDialog, WM_INITDIALOG, 0, 0);
\r
6731 if (!commentUp) ShowWindow(commentDialog, SW_SHOW);
\r
6733 lpProc = MakeProcInstance((FARPROC)CommentDialog, hInst);
\r
6734 CreateDialog(hInst, MAKEINTRESOURCE(DLG_EditComment),
\r
6735 hwndMain, (DLGPROC)lpProc);
\r
6736 FreeProcInstance(lpProc);
\r
6742 /*---------------------------------------------------------------------------*\
\r
6744 * Type-in move dialog functions
\r
6746 \*---------------------------------------------------------------------------*/
\r
6749 TypeInMoveDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
6751 char move[MSG_SIZ];
\r
6754 switch (message) {
\r
6755 case WM_INITDIALOG:
\r
6756 move[0] = (char) lParam;
\r
6757 move[1] = NULLCHAR;
\r
6758 CenterWindowEx(hDlg, GetWindow(hDlg, GW_OWNER), 1 );
\r
6759 Translate(hDlg, DLG_TypeInMove);
\r
6760 hInput = GetDlgItem(hDlg, OPT_Move);
\r
6761 SetWindowText(hInput, move);
\r
6763 SendMessage(hInput, EM_SETSEL, (WPARAM)9999, (LPARAM)9999);
\r
6767 switch (LOWORD(wParam)) {
\r
6770 shiftKey = GetKeyState(VK_SHIFT) < 0; // [HGM] remember last shift status
\r
6771 GetDlgItemText(hDlg, OPT_Move, move, sizeof(move));
\r
6772 TypeInDoneEvent(move);
\r
6773 EndDialog(hDlg, TRUE);
\r
6776 EndDialog(hDlg, FALSE);
\r
6787 PopUpMoveDialog(char firstchar)
\r
6791 lpProc = MakeProcInstance((FARPROC)TypeInMoveDialog, hInst);
\r
6792 DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_TypeInMove),
\r
6793 hwndMain, (DLGPROC)lpProc, (LPARAM)firstchar);
\r
6794 FreeProcInstance(lpProc);
\r
6797 /*---------------------------------------------------------------------------*\
\r
6799 * Type-in name dialog functions
\r
6801 \*---------------------------------------------------------------------------*/
\r
6804 TypeInNameDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
6806 char move[MSG_SIZ];
\r
6809 switch (message) {
\r
6810 case WM_INITDIALOG:
\r
6811 move[0] = (char) lParam;
\r
6812 move[1] = NULLCHAR;
\r
6813 CenterWindowEx(hDlg, GetWindow(hDlg, GW_OWNER), 1 );
\r
6814 Translate(hDlg, DLG_TypeInName);
\r
6815 hInput = GetDlgItem(hDlg, OPT_Name);
\r
6816 SetWindowText(hInput, move);
\r
6818 SendMessage(hInput, EM_SETSEL, (WPARAM)9999, (LPARAM)9999);
\r
6822 switch (LOWORD(wParam)) {
\r
6824 GetDlgItemText(hDlg, OPT_Name, move, sizeof(move));
\r
6825 appData.userName = strdup(move);
\r
6826 SetUserLogo(); DisplayLogos();
\r
6828 if(gameMode == MachinePlaysWhite || gameMode == MachinePlaysBlack) {
\r
6829 snprintf(move, MSG_SIZ, "%s vs. %s", gameInfo.white, gameInfo.black);
\r
6830 DisplayTitle(move);
\r
6834 EndDialog(hDlg, TRUE);
\r
6837 EndDialog(hDlg, FALSE);
\r
6848 PopUpNameDialog(char firstchar)
\r
6852 lpProc = MakeProcInstance((FARPROC)TypeInNameDialog, hInst);
\r
6853 DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_TypeInName),
\r
6854 hwndMain, (DLGPROC)lpProc, (LPARAM)firstchar);
\r
6855 FreeProcInstance(lpProc);
\r
6858 /*---------------------------------------------------------------------------*\
\r
6862 \*---------------------------------------------------------------------------*/
\r
6864 /* Nonmodal error box */
\r
6865 LRESULT CALLBACK ErrorDialog(HWND hDlg, UINT message,
\r
6866 WPARAM wParam, LPARAM lParam);
\r
6869 ErrorPopUp(char *title, char *content)
\r
6873 BOOLEAN modal = hwndMain == NULL;
\r
6891 strncpy(errorTitle, title, sizeof(errorTitle));
\r
6892 errorTitle[sizeof(errorTitle) - 1] = '\0';
\r
6895 MessageBox(NULL, errorMessage, errorTitle, MB_OK|MB_ICONEXCLAMATION);
\r
6897 lpProc = MakeProcInstance((FARPROC)ErrorDialog, hInst);
\r
6898 CreateDialog(hInst, MAKEINTRESOURCE(DLG_Error),
\r
6899 hwndMain, (DLGPROC)lpProc);
\r
6900 FreeProcInstance(lpProc);
\r
6907 if (!appData.popupMoveErrors && moveErrorMessageUp) DisplayMessage("", "");
\r
6908 if (errorDialog == NULL) return;
\r
6909 DestroyWindow(errorDialog);
\r
6910 errorDialog = NULL;
\r
6911 if(errorExitStatus) ExitEvent(errorExitStatus);
\r
6915 ErrorDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
6919 switch (message) {
\r
6920 case WM_INITDIALOG:
\r
6921 GetWindowRect(hDlg, &rChild);
\r
6924 SetWindowPos(hDlg, NULL, rChild.left,
\r
6925 rChild.top + boardRect.top - (rChild.bottom - rChild.top),
\r
6926 0, 0, SWP_NOZORDER|SWP_NOSIZE);
\r
6930 [AS] It seems that the above code wants to move the dialog up in the "caption
\r
6931 area" of the main window, but it uses the dialog height as an hard-coded constant,
\r
6932 and it doesn't work when you resize the dialog.
\r
6933 For now, just give it a default position.
\r
6935 SetWindowPos(hDlg, NULL, boardRect.left+8, boardRect.top+8, 0, 0, SWP_NOZORDER|SWP_NOSIZE);
\r
6936 Translate(hDlg, DLG_Error);
\r
6938 errorDialog = hDlg;
\r
6939 SetWindowText(hDlg, errorTitle);
\r
6940 SetDlgItemText(hDlg, OPT_ErrorText, errorMessage);
\r
6944 switch (LOWORD(wParam)) {
\r
6947 if (errorDialog == hDlg) errorDialog = NULL;
\r
6948 DestroyWindow(hDlg);
\r
6960 HWND gothicDialog = NULL;
\r
6963 GothicDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
6966 int height = GetSystemMetrics(SM_CYCAPTION)+GetSystemMetrics(SM_CYFRAME);
\r
6968 switch (message) {
\r
6969 case WM_INITDIALOG:
\r
6970 GetWindowRect(hDlg, &rChild);
\r
6972 SetWindowPos(hDlg, NULL, wpMain.x, wpMain.y-height, wpMain.width, height,
\r
6976 [AS] It seems that the above code wants to move the dialog up in the "caption
\r
6977 area" of the main window, but it uses the dialog height as an hard-coded constant,
\r
6978 and it doesn't work when you resize the dialog.
\r
6979 For now, just give it a default position.
\r
6981 gothicDialog = hDlg;
\r
6982 SetWindowText(hDlg, errorTitle);
\r
6983 SetDlgItemText(hDlg, OPT_ErrorText, errorMessage);
\r
6987 switch (LOWORD(wParam)) {
\r
6990 if (errorDialog == hDlg) errorDialog = NULL;
\r
6991 DestroyWindow(hDlg);
\r
7003 GothicPopUp(char *title, VariantClass variant)
\r
7006 static char *lastTitle;
\r
7008 strncpy(errorTitle, title, sizeof(errorTitle));
\r
7009 errorTitle[sizeof(errorTitle) - 1] = '\0';
\r
7011 if(lastTitle != title && gothicDialog != NULL) {
\r
7012 DestroyWindow(gothicDialog);
\r
7013 gothicDialog = NULL;
\r
7015 if(variant != VariantNormal && gothicDialog == NULL) {
\r
7016 title = lastTitle;
\r
7017 lpProc = MakeProcInstance((FARPROC)GothicDialog, hInst);
\r
7018 CreateDialog(hInst, MAKEINTRESOURCE(DLG_Error),
\r
7019 hwndMain, (DLGPROC)lpProc);
\r
7020 FreeProcInstance(lpProc);
\r
7025 /*---------------------------------------------------------------------------*\
\r
7027 * Ics Interaction console functions
\r
7029 \*---------------------------------------------------------------------------*/
\r
7031 #define HISTORY_SIZE 64
\r
7032 static char *history[HISTORY_SIZE];
\r
7033 int histIn = 0, histP = 0;
\r
7037 SaveInHistory(char *cmd)
\r
7039 if (history[histIn] != NULL) {
\r
7040 free(history[histIn]);
\r
7041 history[histIn] = NULL;
\r
7043 if (*cmd == NULLCHAR) return;
\r
7044 history[histIn] = StrSave(cmd);
\r
7045 histIn = (histIn + 1) % HISTORY_SIZE;
\r
7046 if (history[histIn] != NULL) {
\r
7047 free(history[histIn]);
\r
7049 history[histIn] = NULL;
\r
7055 PrevInHistory(char *cmd)
\r
7058 if (histP == histIn) {
\r
7059 if (history[histIn] != NULL) free(history[histIn]);
\r
7060 history[histIn] = StrSave(cmd);
\r
7062 newhp = (histP - 1 + HISTORY_SIZE) % HISTORY_SIZE;
\r
7063 if (newhp == histIn || history[newhp] == NULL) return NULL;
\r
7065 return history[histP];
\r
7071 if (histP == histIn) return NULL;
\r
7072 histP = (histP + 1) % HISTORY_SIZE;
\r
7073 return history[histP];
\r
7077 LoadIcsTextMenu(IcsTextMenuEntry *e)
\r
7081 hmenu = LoadMenu(hInst, "TextMenu");
\r
7082 h = GetSubMenu(hmenu, 0);
\r
7084 if (strcmp(e->item, "-") == 0) {
\r
7085 AppendMenu(h, MF_SEPARATOR, 0, 0);
\r
7086 } else { // [HGM] re-written a bit to use only one AppendMenu call for both cases (| or no |)
\r
7087 int flags = MF_STRING, j = 0;
\r
7088 if (e->item[0] == '|') {
\r
7089 flags |= MF_MENUBARBREAK;
\r
7092 if(!strcmp(e->command, "none")) flags |= MF_GRAYED; // [HGM] chatclick: provide inactive dummy
\r
7093 AppendMenu(h, flags, IDM_CommandX + i, e->item + j);
\r
7101 WNDPROC consoleTextWindowProc;
\r
7104 CommandX(HWND hwnd, char *command, BOOLEAN getname, BOOLEAN immediate)
\r
7106 char buf[MSG_SIZ], name[MSG_SIZ];
\r
7107 HWND hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
7111 SetWindowText(hInput, command);
\r
7113 SendMessage(hInput, WM_CHAR, '\r', 0);
\r
7115 sel.cpMin = 999999;
\r
7116 sel.cpMax = 999999;
\r
7117 SendMessage(hInput, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
7122 SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);
\r
7123 if (sel.cpMin == sel.cpMax) {
\r
7124 /* Expand to surrounding word */
\r
7127 tr.chrg.cpMax = sel.cpMin;
\r
7128 tr.chrg.cpMin = --sel.cpMin;
\r
7129 if (sel.cpMin < 0) break;
\r
7130 tr.lpstrText = name;
\r
7131 SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr);
\r
7132 } while (isalpha(name[0]) || isdigit(name[0]) || name[0] == '-');
\r
7136 tr.chrg.cpMin = sel.cpMax;
\r
7137 tr.chrg.cpMax = ++sel.cpMax;
\r
7138 tr.lpstrText = name;
\r
7139 if (SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr) < 1) break;
\r
7140 } while (isalpha(name[0]) || isdigit(name[0]) || name[0] == '-');
\r
7143 if (sel.cpMax == sel.cpMin || sel.cpMax - sel.cpMin > MSG_SIZ/2) {
\r
7144 MessageBeep(MB_ICONEXCLAMATION);
\r
7148 tr.lpstrText = name;
\r
7149 SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr);
\r
7151 if (sel.cpMax - sel.cpMin > MSG_SIZ/2) {
\r
7152 MessageBeep(MB_ICONEXCLAMATION);
\r
7155 SendMessage(hwnd, EM_GETSELTEXT, 0, (LPARAM) name);
\r
7158 if(strstr(command, "%s")) snprintf(buf, MSG_SIZ, command, name); else
\r
7159 snprintf(buf, MSG_SIZ, "%s %s", command, name);
\r
7160 SetWindowText(hInput, buf);
\r
7161 SendMessage(hInput, WM_CHAR, '\r', 0);
\r
7163 if(!strcmp(command, "chat")) { ChatPopUp(name); return; }
\r
7164 snprintf(buf, MSG_SIZ, "%s %s ", command, name); /* trailing space */
\r
7165 SetWindowText(hInput, buf);
\r
7166 sel.cpMin = 999999;
\r
7167 sel.cpMax = 999999;
\r
7168 SendMessage(hInput, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
7174 ConsoleTextSubclass(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
\r
7179 switch (message) {
\r
7181 if (!(GetKeyState(VK_CONTROL) & ~1)) break;
\r
7182 if(wParam=='R') return 0;
\r
7185 SendMessage(hwnd, EM_LINESCROLL, 0, -999999);
\r
7188 sel.cpMin = 999999;
\r
7189 sel.cpMax = 999999;
\r
7190 SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
7191 SendMessage(hwnd, EM_SCROLLCARET, 0, 0);
\r
7196 if(wParam != '\022') {
\r
7197 if (wParam == '\t') {
\r
7198 if (GetKeyState(VK_SHIFT) < 0) {
\r
7200 if (IsIconic(hwndMain)) ShowWindow(hwndMain, SW_RESTORE);
\r
7201 if (buttonDesc[0].hwnd) {
\r
7202 SetFocus(buttonDesc[0].hwnd);
\r
7204 SetFocus(hwndMain);
\r
7208 SetFocus(GetDlgItem(hwndConsole, OPT_ConsoleInput));
\r
7211 hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
7212 JAWS_DELETE( SetFocus(hInput); )
\r
7213 SendMessage(hInput, message, wParam, lParam);
\r
7216 } // [HGM] navigate: for Ctrl+R, flow into next case (moved up here) to summon up menu
\r
7218 case WM_RBUTTONDOWN:
\r
7219 if (!(GetKeyState(VK_SHIFT) & ~1)) {
\r
7220 /* Move selection here if it was empty */
\r
7222 pt.x = LOWORD(lParam);
\r
7223 pt.y = HIWORD(lParam);
\r
7224 SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);
\r
7225 if (sel.cpMin == sel.cpMax) {
\r
7226 if(lParam != -1) sel.cpMin = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&pt); /*doc is wrong*/
\r
7227 sel.cpMax = sel.cpMin;
\r
7228 SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
7230 SendMessage(hwnd, EM_HIDESELECTION, FALSE, FALSE);
\r
7231 { // [HGM] chatclick: code moved here from WM_RBUTTONUP case, to have menu appear on down-click
\r
7233 HMENU hmenu = LoadIcsTextMenu(icsTextMenuEntry);
\r
7234 SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);
\r
7235 if (sel.cpMin == sel.cpMax) {
\r
7236 EnableMenuItem(hmenu, IDM_Copy, MF_BYCOMMAND|MF_GRAYED);
\r
7237 EnableMenuItem(hmenu, IDM_QuickPaste, MF_BYCOMMAND|MF_GRAYED);
\r
7239 if (!IsClipboardFormatAvailable(CF_TEXT)) {
\r
7240 EnableMenuItem(hmenu, IDM_Paste, MF_BYCOMMAND|MF_GRAYED);
\r
7242 pt.x = LOWORD(lParam)-30; // [HGM] chatclick: make menu pop up with pointer above upper-right item
\r
7243 pt.y = HIWORD(lParam)-10; // make it appear as if mouse moved there, so it will be selected on up-click
\r
7244 PostMessage(hwnd, WM_MOUSEMOVE, wParam, lParam+5);
\r
7245 MenuPopup(hwnd, pt, hmenu, -1);
\r
7249 case WM_RBUTTONUP:
\r
7250 if (GetKeyState(VK_SHIFT) & ~1) {
\r
7251 SendDlgItemMessage(hwndConsole, OPT_ConsoleText,
\r
7252 WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);
\r
7256 hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
7258 return SendMessage(hInput, message, wParam, lParam);
\r
7259 case WM_MBUTTONDOWN:
\r
7260 return SendMessage(hwnd, WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);
\r
7262 switch (LOWORD(wParam)) {
\r
7263 case IDM_QuickPaste:
\r
7265 SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);
\r
7266 if (sel.cpMin == sel.cpMax) {
\r
7267 MessageBeep(MB_ICONEXCLAMATION);
\r
7270 SendMessage(hwnd, WM_COPY, 0, 0);
\r
7271 hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
7272 SendMessage(hInput, WM_PASTE, 0, 0);
\r
7277 SendMessage(hwnd, WM_CUT, 0, 0);
\r
7280 SendMessage(hwnd, WM_PASTE, 0, 0);
\r
7283 SendMessage(hwnd, WM_COPY, 0, 0);
\r
7287 int i = LOWORD(wParam) - IDM_CommandX;
\r
7288 if (i >= 0 && i < ICS_TEXT_MENU_SIZE &&
\r
7289 icsTextMenuEntry[i].command != NULL) {
\r
7290 CommandX(hwnd, icsTextMenuEntry[i].command,
\r
7291 icsTextMenuEntry[i].getname,
\r
7292 icsTextMenuEntry[i].immediate);
\r
7300 return (*consoleTextWindowProc)(hwnd, message, wParam, lParam);
\r
7303 WNDPROC consoleInputWindowProc;
\r
7306 ConsoleInputSubclass(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
\r
7308 char buf[MSG_SIZ];
\r
7310 static BOOL sendNextChar = FALSE;
\r
7311 static BOOL quoteNextChar = FALSE;
\r
7312 InputSource *is = consoleInputSource;
\r
7316 switch (message) {
\r
7318 if (!appData.localLineEditing || sendNextChar) {
\r
7319 is->buf[0] = (CHAR) wParam;
\r
7321 SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);
\r
7322 sendNextChar = FALSE;
\r
7325 if (quoteNextChar) {
\r
7326 buf[0] = (char) wParam;
\r
7327 buf[1] = NULLCHAR;
\r
7328 SendMessage(hwnd, EM_REPLACESEL, TRUE, (LPARAM) buf);
\r
7329 quoteNextChar = FALSE;
\r
7333 case '\r': /* Enter key */
\r
7334 is->count = GetWindowText(hwnd, is->buf, INPUT_SOURCE_BUF_SIZE-1);
\r
7335 if (consoleEcho) SaveInHistory(is->buf);
\r
7336 is->buf[is->count++] = '\n';
\r
7337 is->buf[is->count] = NULLCHAR;
\r
7338 SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);
\r
7339 if (consoleEcho) {
\r
7340 ConsoleOutput(is->buf, is->count, TRUE);
\r
7341 } else if (appData.localLineEditing) {
\r
7342 ConsoleOutput("\n", 1, TRUE);
\r
7345 case '\033': /* Escape key */
\r
7346 SetWindowText(hwnd, "");
\r
7347 cf.cbSize = sizeof(CHARFORMAT);
\r
7348 cf.dwMask = CFM_COLOR|CFM_BOLD|CFM_ITALIC|CFM_UNDERLINE|CFM_STRIKEOUT;
\r
7349 if (consoleEcho) {
\r
7350 cf.crTextColor = textAttribs[ColorNormal].color;
\r
7352 cf.crTextColor = COLOR_ECHOOFF;
\r
7354 cf.dwEffects = textAttribs[ColorNormal].effects;
\r
7355 SendMessage(hwnd, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf);
\r
7357 case '\t': /* Tab key */
\r
7358 if (GetKeyState(VK_SHIFT) < 0) {
\r
7360 SetFocus(GetDlgItem(hwndConsole, OPT_ConsoleText));
\r
7363 if (IsIconic(hwndMain)) ShowWindow(hwndMain, SW_RESTORE);
\r
7364 if (buttonDesc[0].hwnd) {
\r
7365 SetFocus(buttonDesc[0].hwnd);
\r
7367 SetFocus(hwndMain);
\r
7371 case '\023': /* Ctrl+S */
\r
7372 sendNextChar = TRUE;
\r
7374 case '\021': /* Ctrl+Q */
\r
7375 quoteNextChar = TRUE;
\r
7385 GetWindowText(hwnd, buf, MSG_SIZ);
\r
7386 p = PrevInHistory(buf);
\r
7388 SetWindowText(hwnd, p);
\r
7389 sel.cpMin = 999999;
\r
7390 sel.cpMax = 999999;
\r
7391 SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
7396 p = NextInHistory();
\r
7398 SetWindowText(hwnd, p);
\r
7399 sel.cpMin = 999999;
\r
7400 sel.cpMax = 999999;
\r
7401 SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
7407 if (!(GetKeyState(VK_CONTROL) & ~1)) break;
\r
7411 SendDlgItemMessage(hwndConsole, OPT_ConsoleText, message, wParam, lParam);
\r
7415 case WM_MBUTTONDOWN:
\r
7416 SendDlgItemMessage(hwndConsole, OPT_ConsoleText,
\r
7417 WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);
\r
7419 case WM_RBUTTONUP:
\r
7420 if (GetKeyState(VK_SHIFT) & ~1) {
\r
7421 SendDlgItemMessage(hwndConsole, OPT_ConsoleText,
\r
7422 WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);
\r
7426 hmenu = LoadMenu(hInst, "InputMenu");
\r
7427 SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);
\r
7428 if (sel.cpMin == sel.cpMax) {
\r
7429 EnableMenuItem(hmenu, IDM_Copy, MF_BYCOMMAND|MF_GRAYED);
\r
7430 EnableMenuItem(hmenu, IDM_Cut, MF_BYCOMMAND|MF_GRAYED);
\r
7432 if (!IsClipboardFormatAvailable(CF_TEXT)) {
\r
7433 EnableMenuItem(hmenu, IDM_Paste, MF_BYCOMMAND|MF_GRAYED);
\r
7435 pt.x = LOWORD(lParam);
\r
7436 pt.y = HIWORD(lParam);
\r
7437 MenuPopup(hwnd, pt, hmenu, -1);
\r
7441 switch (LOWORD(wParam)) {
\r
7443 SendMessage(hwnd, EM_UNDO, 0, 0);
\r
7445 case IDM_SelectAll:
\r
7447 sel.cpMax = -1; /*999999?*/
\r
7448 SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
7451 SendMessage(hwnd, WM_CUT, 0, 0);
\r
7454 SendMessage(hwnd, WM_PASTE, 0, 0);
\r
7457 SendMessage(hwnd, WM_COPY, 0, 0);
\r
7462 return (*consoleInputWindowProc)(hwnd, message, wParam, lParam);
\r
7465 #define CO_MAX 100000
\r
7466 #define CO_TRIM 1000
\r
7469 ConsoleWndProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
7471 static SnapData sd;
\r
7472 HWND hText, hInput;
\r
7474 static int sizeX, sizeY;
\r
7475 int newSizeX, newSizeY;
\r
7479 hText = GetDlgItem(hDlg, OPT_ConsoleText);
\r
7480 hInput = GetDlgItem(hDlg, OPT_ConsoleInput);
\r
7482 switch (message) {
\r
7484 if (((NMHDR*)lParam)->code == EN_LINK)
\r
7486 ENLINK *pLink = (ENLINK*)lParam;
\r
7487 if (pLink->msg == WM_LBUTTONUP)
\r
7491 tr.chrg = pLink->chrg;
\r
7492 tr.lpstrText = malloc(1+tr.chrg.cpMax-tr.chrg.cpMin);
\r
7493 SendMessage(hText, EM_GETTEXTRANGE, 0, (LPARAM)&tr);
\r
7494 ShellExecute(NULL, "open", tr.lpstrText, NULL, NULL, SW_SHOW);
\r
7495 free(tr.lpstrText);
\r
7499 case WM_INITDIALOG: /* message: initialize dialog box */
\r
7500 hwndConsole = hDlg;
\r
7502 consoleTextWindowProc = (WNDPROC)
\r
7503 SetWindowLongPtr(hText, GWLP_WNDPROC, (LONG_PTR) ConsoleTextSubclass);
\r
7504 SendMessage(hText, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);
\r
7505 consoleInputWindowProc = (WNDPROC)
\r
7506 SetWindowLongPtr(hInput, GWLP_WNDPROC, (LONG_PTR) ConsoleInputSubclass);
\r
7507 SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);
\r
7508 Colorize(ColorNormal, TRUE);
\r
7509 SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &consoleCF);
\r
7510 ChangedConsoleFont();
\r
7511 GetClientRect(hDlg, &rect);
\r
7512 sizeX = rect.right;
\r
7513 sizeY = rect.bottom;
\r
7514 if (wpConsole.x != CW_USEDEFAULT && wpConsole.y != CW_USEDEFAULT &&
\r
7515 wpConsole.width != CW_USEDEFAULT && wpConsole.height != CW_USEDEFAULT) {
\r
7516 WINDOWPLACEMENT wp;
\r
7517 EnsureOnScreen(&wpConsole.x, &wpConsole.y, 0, 0);
\r
7518 wp.length = sizeof(WINDOWPLACEMENT);
\r
7520 wp.showCmd = SW_SHOW;
\r
7521 wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;
\r
7522 wp.rcNormalPosition.left = wpConsole.x;
\r
7523 wp.rcNormalPosition.right = wpConsole.x + wpConsole.width;
\r
7524 wp.rcNormalPosition.top = wpConsole.y;
\r
7525 wp.rcNormalPosition.bottom = wpConsole.y + wpConsole.height;
\r
7526 SetWindowPlacement(hDlg, &wp);
\r
7529 // [HGM] Chessknight's change 2004-07-13
\r
7530 else { /* Determine Defaults */
\r
7531 WINDOWPLACEMENT wp;
\r
7532 wpConsole.x = wpMain.width + 1;
\r
7533 wpConsole.y = wpMain.y;
\r
7534 wpConsole.width = screenWidth - wpMain.width;
\r
7535 wpConsole.height = wpMain.height;
\r
7536 EnsureOnScreen(&wpConsole.x, &wpConsole.y, 0, 0);
\r
7537 wp.length = sizeof(WINDOWPLACEMENT);
\r
7539 wp.showCmd = SW_SHOW;
\r
7540 wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;
\r
7541 wp.rcNormalPosition.left = wpConsole.x;
\r
7542 wp.rcNormalPosition.right = wpConsole.x + wpConsole.width;
\r
7543 wp.rcNormalPosition.top = wpConsole.y;
\r
7544 wp.rcNormalPosition.bottom = wpConsole.y + wpConsole.height;
\r
7545 SetWindowPlacement(hDlg, &wp);
\r
7548 // Allow hText to highlight URLs and send notifications on them
\r
7549 wMask = (WORD) SendMessage(hText, EM_GETEVENTMASK, 0, 0L);
\r
7550 SendMessage(hText, EM_SETEVENTMASK, 0, wMask | ENM_LINK);
\r
7551 SendMessage(hText, EM_AUTOURLDETECT, TRUE, 0L);
\r
7552 SetWindowLongPtr(hText, GWLP_USERDATA, 79); // initialize the text window's width
\r
7566 if (IsIconic(hDlg)) break;
\r
7567 newSizeX = LOWORD(lParam);
\r
7568 newSizeY = HIWORD(lParam);
\r
7569 if (sizeX != newSizeX || sizeY != newSizeY) {
\r
7570 RECT rectText, rectInput;
\r
7572 int newTextHeight, newTextWidth;
\r
7573 GetWindowRect(hText, &rectText);
\r
7574 newTextWidth = rectText.right - rectText.left + newSizeX - sizeX;
\r
7575 newTextHeight = rectText.bottom - rectText.top + newSizeY - sizeY;
\r
7576 if (newTextHeight < 0) {
\r
7577 newSizeY += -newTextHeight;
\r
7578 newTextHeight = 0;
\r
7580 SetWindowPos(hText, NULL, 0, 0,
\r
7581 newTextWidth, newTextHeight, SWP_NOZORDER|SWP_NOMOVE);
\r
7582 GetWindowRect(hInput, &rectInput); /* gives screen coords */
\r
7583 pt.x = rectInput.left;
\r
7584 pt.y = rectInput.top + newSizeY - sizeY;
\r
7585 ScreenToClient(hDlg, &pt);
\r
7586 SetWindowPos(hInput, NULL,
\r
7587 pt.x, pt.y, /* needs client coords */
\r
7588 rectInput.right - rectInput.left + newSizeX - sizeX,
\r
7589 rectInput.bottom - rectInput.top, SWP_NOZORDER);
\r
7595 case WM_GETMINMAXINFO:
\r
7596 /* Prevent resizing window too small */
\r
7597 mmi = (MINMAXINFO *) lParam;
\r
7598 mmi->ptMinTrackSize.x = 100;
\r
7599 mmi->ptMinTrackSize.y = 100;
\r
7602 /* [AS] Snapping */
\r
7603 case WM_ENTERSIZEMOVE:
\r
7604 return OnEnterSizeMove( &sd, hDlg, wParam, lParam );
\r
7607 return OnSizing( &sd, hDlg, wParam, lParam );
\r
7610 return OnMoving( &sd, hDlg, wParam, lParam );
\r
7612 case WM_EXITSIZEMOVE:
\r
7613 UpdateICSWidth(hText);
\r
7614 return OnExitSizeMove( &sd, hDlg, wParam, lParam );
\r
7617 return DefWindowProc(hDlg, message, wParam, lParam);
\r
7625 if (hwndConsole) return;
\r
7626 hCons = CreateDialog(hInst, szConsoleName, 0, NULL);
\r
7627 SendMessage(hCons, WM_INITDIALOG, 0, 0);
\r
7632 ConsoleOutput(char* data, int length, int forceVisible)
\r
7637 char buf[CO_MAX+1];
\r
7640 static int delayLF = 0;
\r
7641 CHARRANGE savesel, sel;
\r
7643 if (hwndConsole == NULL || length > CO_MAX-100 || length == 0) return;
\r
7651 while (length--) {
\r
7659 } else if (*p == '\007') {
\r
7660 MyPlaySound(&sounds[(int)SoundBell]);
\r
7667 hText = GetDlgItem(hwndConsole, OPT_ConsoleText);
\r
7668 SendMessage(hText, EM_HIDESELECTION, TRUE, FALSE);
\r
7669 /* Save current selection */
\r
7670 SendMessage(hText, EM_EXGETSEL, 0, (LPARAM)&savesel);
\r
7671 exlen = GetWindowTextLength(hText);
\r
7672 /* Find out whether current end of text is visible */
\r
7673 SendMessage(hText, EM_GETRECT, 0, (LPARAM) &rect);
\r
7674 SendMessage(hText, EM_POSFROMCHAR, (WPARAM) &pEnd, exlen);
\r
7675 /* Trim existing text if it's too long */
\r
7676 if (exlen + (q - buf) > CO_MAX) {
\r
7677 trim = (CO_TRIM > (q - buf)) ? CO_TRIM : (q - buf);
\r
7680 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
7681 SendMessage(hText, EM_REPLACESEL, 0, (LPARAM)"");
\r
7683 savesel.cpMin -= trim;
\r
7684 savesel.cpMax -= trim;
\r
7685 if (exlen < 0) exlen = 0;
\r
7686 if (savesel.cpMin < 0) savesel.cpMin = 0;
\r
7687 if (savesel.cpMax < savesel.cpMin) savesel.cpMax = savesel.cpMin;
\r
7689 /* Append the new text */
\r
7690 sel.cpMin = exlen;
\r
7691 sel.cpMax = exlen;
\r
7692 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
7693 SendMessage(hText, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&consoleCF);
\r
7694 SendMessage(hText, EM_REPLACESEL, 0, (LPARAM) buf);
\r
7695 if (forceVisible || exlen == 0 ||
\r
7696 (rect.left <= pEnd.x && pEnd.x < rect.right &&
\r
7697 rect.top <= pEnd.y && pEnd.y < rect.bottom)) {
\r
7698 /* Scroll to make new end of text visible if old end of text
\r
7699 was visible or new text is an echo of user typein */
\r
7700 sel.cpMin = 9999999;
\r
7701 sel.cpMax = 9999999;
\r
7702 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
7703 SendMessage(hText, EM_HIDESELECTION, FALSE, FALSE);
\r
7704 SendMessage(hText, EM_SCROLLCARET, 0, 0);
\r
7705 SendMessage(hText, EM_HIDESELECTION, TRUE, FALSE);
\r
7707 if (savesel.cpMax == exlen || forceVisible) {
\r
7708 /* Move insert point to new end of text if it was at the old
\r
7709 end of text or if the new text is an echo of user typein */
\r
7710 sel.cpMin = 9999999;
\r
7711 sel.cpMax = 9999999;
\r
7712 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
7714 /* Restore previous selection */
\r
7715 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&savesel);
\r
7717 SendMessage(hText, EM_HIDESELECTION, FALSE, FALSE);
\r
7724 DisplayHoldingsCount(HDC hdc, int x, int y, int rightAlign, int copyNumber)
\r
7728 COLORREF oldFg, oldBg;
\r
7732 if(copyNumber > 1)
\r
7733 snprintf(buf, sizeof(buf)/sizeof(buf[0]),"%d", copyNumber); else buf[0] = 0;
\r
7735 oldFg = SetTextColor(hdc, RGB(255, 255, 255)); /* white */
\r
7736 oldBg = SetBkColor(hdc, RGB(0, 0, 0)); /* black */
\r
7737 oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);
\r
7740 rect.right = x + squareSize;
\r
7742 rect.bottom = y + squareSize;
\r
7745 ExtTextOut(hdc, x + MESSAGE_LINE_LEFTMARGIN
\r
7746 + (rightAlign ? (squareSize*2)/3 : 0),
\r
7747 y, ETO_CLIPPED|ETO_OPAQUE,
\r
7748 &rect, str, strlen(str), NULL);
\r
7750 (void) SetTextColor(hdc, oldFg);
\r
7751 (void) SetBkColor(hdc, oldBg);
\r
7752 (void) SelectObject(hdc, oldFont);
\r
7756 DisplayAClock(HDC hdc, int timeRemaining, int highlight,
\r
7757 RECT *rect, char *color, char *flagFell)
\r
7761 COLORREF oldFg, oldBg;
\r
7764 if (twoBoards && partnerUp) return;
\r
7765 if (appData.clockMode) {
\r
7766 if (tinyLayout == 2)
\r
7767 snprintf(buf, sizeof(buf)/sizeof(buf[0]), "%c %s %s", color[0], TimeString(timeRemaining), flagFell);
\r
7769 snprintf(buf, sizeof(buf)/sizeof(buf[0]), "%s:%c%s %s", color, (logoHeight>0 ? 0 : ' '), TimeString(timeRemaining), flagFell);
\r
7776 oldFg = SetTextColor(hdc, RGB(255, 255, 255)); /* white */
\r
7777 oldBg = SetBkColor(hdc, RGB(0, 0, 0)); /* black */
\r
7779 oldFg = SetTextColor(hdc, RGB(0, 0, 0)); /* black */
\r
7780 oldBg = SetBkColor(hdc, RGB(255, 255, 255)); /* white */
\r
7783 oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);
\r
7787 ExtTextOut(hdc, rect->left + MESSAGE_LINE_LEFTMARGIN,
\r
7788 rect->top, ETO_CLIPPED|ETO_OPAQUE,
\r
7789 rect, str, strlen(str), NULL);
\r
7790 if(logoHeight > 0 && appData.clockMode) {
\r
7792 str += strlen(color)+2;
\r
7793 r.top = rect->top + logoHeight/2;
\r
7794 r.left = rect->left;
\r
7795 r.right = rect->right;
\r
7796 r.bottom = rect->bottom;
\r
7797 ExtTextOut(hdc, rect->left + MESSAGE_LINE_LEFTMARGIN,
\r
7798 r.top, ETO_CLIPPED|ETO_OPAQUE,
\r
7799 &r, str, strlen(str), NULL);
\r
7801 (void) SetTextColor(hdc, oldFg);
\r
7802 (void) SetBkColor(hdc, oldBg);
\r
7803 (void) SelectObject(hdc, oldFont);
\r
7808 DoReadFile(HANDLE hFile, char *buf, int count, DWORD *outCount,
\r
7814 if( count <= 0 ) {
\r
7815 if (appData.debugMode) {
\r
7816 fprintf( debugFP, "DoReadFile: trying to read past end of buffer, overflow = %d\n", count );
\r
7819 return ERROR_INVALID_USER_BUFFER;
\r
7822 ResetEvent(ovl->hEvent);
\r
7823 ovl->Offset = ovl->OffsetHigh = 0;
\r
7824 ok = ReadFile(hFile, buf, count, outCount, ovl);
\r
7828 err = GetLastError();
\r
7829 if (err == ERROR_IO_PENDING) {
\r
7830 ok = GetOverlappedResult(hFile, ovl, outCount, TRUE);
\r
7834 err = GetLastError();
\r
7841 DoWriteFile(HANDLE hFile, char *buf, int count, DWORD *outCount,
\r
7846 ResetEvent(ovl->hEvent);
\r
7847 ovl->Offset = ovl->OffsetHigh = 0;
\r
7848 ok = WriteFile(hFile, buf, count, outCount, ovl);
\r
7852 err = GetLastError();
\r
7853 if (err == ERROR_IO_PENDING) {
\r
7854 ok = GetOverlappedResult(hFile, ovl, outCount, TRUE);
\r
7858 err = GetLastError();
\r
7865 /* [AS] If input is line by line and a line exceed the buffer size, force an error */
\r
7866 void CheckForInputBufferFull( InputSource * is )
\r
7868 if( is->lineByLine && (is->next - is->buf) >= INPUT_SOURCE_BUF_SIZE ) {
\r
7869 /* Look for end of line */
\r
7870 char * p = is->buf;
\r
7872 while( p < is->next && *p != '\n' ) {
\r
7876 if( p >= is->next ) {
\r
7877 if (appData.debugMode) {
\r
7878 fprintf( debugFP, "Input line exceeded buffer size (source id=%lu)\n", is->id );
\r
7881 is->error = ERROR_BROKEN_PIPE; /* [AS] Just any non-successful code! */
\r
7882 is->count = (DWORD) -1;
\r
7883 is->next = is->buf;
\r
7889 InputThread(LPVOID arg)
\r
7894 is = (InputSource *) arg;
\r
7895 ovl.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
\r
7896 ovl.Internal = ovl.InternalHigh = ovl.Offset = ovl.OffsetHigh = 0;
\r
7897 while (is->hThread != NULL) {
\r
7898 is->error = DoReadFile(is->hFile, is->next,
\r
7899 INPUT_SOURCE_BUF_SIZE - (is->next - is->buf),
\r
7900 &is->count, &ovl);
\r
7901 if (is->error == NO_ERROR) {
\r
7902 is->next += is->count;
\r
7904 if (is->error == ERROR_BROKEN_PIPE) {
\r
7905 /* Correct for MS brain damage. EOF reading a pipe is not an error. */
\r
7908 is->count = (DWORD) -1;
\r
7909 /* [AS] The (is->count <= 0) check below is not useful for unsigned values! */
\r
7914 CheckForInputBufferFull( is );
\r
7916 SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);
\r
7918 if( is->count == ((DWORD) -1) ) break; /* [AS] */
\r
7920 if (is->count <= 0) break; /* Quit on EOF or error */
\r
7923 CloseHandle(ovl.hEvent);
\r
7924 CloseHandle(is->hFile);
\r
7926 if (appData.debugMode) {
\r
7927 fprintf( debugFP, "Input thread terminated (id=%lu, error=%d, count=%ld)\n", is->id, is->error, is->count );
\r
7934 /* Windows 95 beta 2 won't let you do overlapped i/o on a console or pipe */
\r
7936 NonOvlInputThread(LPVOID arg)
\r
7943 is = (InputSource *) arg;
\r
7944 while (is->hThread != NULL) {
\r
7945 is->error = ReadFile(is->hFile, is->next,
\r
7946 INPUT_SOURCE_BUF_SIZE - (is->next - is->buf),
\r
7947 &is->count, NULL) ? NO_ERROR : GetLastError();
\r
7948 if (is->error == NO_ERROR) {
\r
7949 /* Change CRLF to LF */
\r
7950 if (is->next > is->buf) {
\r
7952 i = is->count + 1;
\r
7960 if (prev == '\r' && *p == '\n') {
\r
7972 if (is->error == ERROR_BROKEN_PIPE) {
\r
7973 /* Correct for MS brain damage. EOF reading a pipe is not an error. */
\r
7976 is->count = (DWORD) -1;
\r
7980 CheckForInputBufferFull( is );
\r
7982 SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);
\r
7984 if( is->count == ((DWORD) -1) ) break; /* [AS] */
\r
7986 if (is->count < 0) break; /* Quit on error */
\r
7988 CloseHandle(is->hFile);
\r
7993 SocketInputThread(LPVOID arg)
\r
7997 is = (InputSource *) arg;
\r
7998 while (is->hThread != NULL) {
\r
7999 is->count = recv(is->sock, is->buf, INPUT_SOURCE_BUF_SIZE, 0);
\r
8000 if ((int)is->count == SOCKET_ERROR) {
\r
8001 is->count = (DWORD) -1;
\r
8002 is->error = WSAGetLastError();
\r
8004 is->error = NO_ERROR;
\r
8005 is->next += is->count;
\r
8006 if (is->count == 0 && is->second == is) {
\r
8007 /* End of file on stderr; quit with no message */
\r
8011 SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);
\r
8013 if( is->count == ((DWORD) -1) ) break; /* [AS] */
\r
8015 if (is->count <= 0) break; /* Quit on EOF or error */
\r
8021 InputEvent(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
\r
8025 is = (InputSource *) lParam;
\r
8026 if (is->lineByLine) {
\r
8027 /* Feed in lines one by one */
\r
8028 char *p = is->buf;
\r
8030 while (q < is->next) {
\r
8031 if (*q++ == '\n') {
\r
8032 (is->func)(is, is->closure, p, q - p, NO_ERROR);
\r
8037 /* Move any partial line to the start of the buffer */
\r
8039 while (p < is->next) {
\r
8044 if (is->error != NO_ERROR || is->count == 0) {
\r
8045 /* Notify backend of the error. Note: If there was a partial
\r
8046 line at the end, it is not flushed through. */
\r
8047 (is->func)(is, is->closure, is->buf, is->count, is->error);
\r
8050 /* Feed in the whole chunk of input at once */
\r
8051 (is->func)(is, is->closure, is->buf, is->count, is->error);
\r
8052 is->next = is->buf;
\r
8056 /*---------------------------------------------------------------------------*\
\r
8058 * Menu enables. Used when setting various modes.
\r
8060 \*---------------------------------------------------------------------------*/
\r
8068 GreyRevert(Boolean grey)
\r
8069 { // [HGM] vari: for retracting variations in local mode
\r
8070 HMENU hmenu = GetMenu(hwndMain);
\r
8071 EnableMenuItem(hmenu, IDM_Revert, MF_BYCOMMAND|(grey ? MF_GRAYED : MF_ENABLED));
\r
8072 EnableMenuItem(hmenu, IDM_Annotate, MF_BYCOMMAND|(grey ? MF_GRAYED : MF_ENABLED));
\r
8076 SetMenuEnables(HMENU hmenu, Enables *enab)
\r
8078 while (enab->item > 0) {
\r
8079 (void) EnableMenuItem(hmenu, enab->item, enab->flags);
\r
8084 Enables gnuEnables[] = {
\r
8085 { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },
\r
8086 { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },
\r
8087 { IDM_IcsClient, MF_BYCOMMAND|MF_GRAYED },
\r
8088 { IDM_Accept, MF_BYCOMMAND|MF_GRAYED },
\r
8089 { IDM_Decline, MF_BYCOMMAND|MF_GRAYED },
\r
8090 { IDM_Rematch, MF_BYCOMMAND|MF_GRAYED },
\r
8091 { IDM_Adjourn, MF_BYCOMMAND|MF_GRAYED },
\r
8092 { IDM_StopExamining, MF_BYCOMMAND|MF_GRAYED },
\r
8093 { IDM_StopObserving, MF_BYCOMMAND|MF_GRAYED },
\r
8094 { IDM_Upload, MF_BYCOMMAND|MF_GRAYED },
\r
8095 { IDM_Revert, MF_BYCOMMAND|MF_GRAYED },
\r
8096 { IDM_Annotate, MF_BYCOMMAND|MF_GRAYED },
\r
8097 { IDM_NewChat, MF_BYCOMMAND|MF_GRAYED },
\r
8100 // Needed to switch from ncp to GNU mode on Engine Load
\r
8101 { ACTION_POS, MF_BYPOSITION|MF_ENABLED },
\r
8102 { IDM_MachineWhite, MF_BYCOMMAND|MF_ENABLED },
\r
8103 { IDM_MachineBlack, MF_BYCOMMAND|MF_ENABLED },
\r
8104 { IDM_TwoMachines, MF_BYCOMMAND|MF_ENABLED },
\r
8105 { IDM_Match, MF_BYCOMMAND|MF_ENABLED },
\r
8106 { IDM_AnalysisMode, MF_BYCOMMAND|MF_ENABLED },
\r
8107 { IDM_AnalyzeFile, MF_BYCOMMAND|MF_ENABLED },
\r
8108 { IDM_Engine1Options, MF_BYCOMMAND|MF_ENABLED },
\r
8109 { IDM_Engine2Options, MF_BYCOMMAND|MF_ENABLED },
\r
8110 { IDM_TimeControl, MF_BYCOMMAND|MF_ENABLED },
\r
8111 { IDM_RetractMove, MF_BYCOMMAND|MF_ENABLED },
\r
8112 { IDM_MoveNow, MF_BYCOMMAND|MF_ENABLED },
\r
8113 { IDM_Hint, MF_BYCOMMAND|MF_ENABLED },
\r
8114 { IDM_Book, MF_BYCOMMAND|MF_ENABLED },
\r
8118 Enables icsEnables[] = {
\r
8119 { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },
\r
8120 { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },
\r
8121 { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },
\r
8122 { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },
\r
8123 { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },
\r
8124 { IDM_Match, MF_BYCOMMAND|MF_GRAYED },
\r
8125 { IDM_MachineBoth, MF_BYCOMMAND|MF_GRAYED },
\r
8126 { IDM_AnalysisMode, MF_BYCOMMAND|MF_ENABLED },
\r
8127 { IDM_AnalyzeFile, MF_BYCOMMAND|MF_GRAYED },
\r
8128 { IDM_TimeControl, MF_BYCOMMAND|MF_GRAYED },
\r
8129 { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },
\r
8130 { IDM_Hint, MF_BYCOMMAND|MF_GRAYED },
\r
8131 { IDM_Book, MF_BYCOMMAND|MF_GRAYED },
\r
8132 { IDM_LoadProg1, MF_BYCOMMAND|MF_GRAYED },
\r
8133 { IDM_LoadProg2, MF_BYCOMMAND|MF_GRAYED },
\r
8134 { IDM_IcsOptions, MF_BYCOMMAND|MF_ENABLED },
\r
8135 { IDM_Engine1Options, MF_BYCOMMAND|MF_GRAYED },
\r
8136 { IDM_Engine2Options, MF_BYCOMMAND|MF_GRAYED },
\r
8137 { IDM_Annotate, MF_BYCOMMAND|MF_GRAYED },
\r
8138 { IDM_Tourney, MF_BYCOMMAND|MF_GRAYED },
\r
8143 Enables zippyEnables[] = {
\r
8144 { IDM_MoveNow, MF_BYCOMMAND|MF_ENABLED },
\r
8145 { IDM_Hint, MF_BYCOMMAND|MF_ENABLED },
\r
8146 { IDM_Book, MF_BYCOMMAND|MF_ENABLED },
\r
8147 { IDM_Engine1Options, MF_BYCOMMAND|MF_ENABLED },
\r
8152 Enables ncpEnables[] = {
\r
8153 { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },
\r
8154 { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },
\r
8155 { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },
\r
8156 { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },
\r
8157 { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },
\r
8158 { IDM_Match, MF_BYCOMMAND|MF_GRAYED },
\r
8159 { IDM_AnalysisMode, MF_BYCOMMAND|MF_GRAYED },
\r
8160 { IDM_AnalyzeFile, MF_BYCOMMAND|MF_GRAYED },
\r
8161 { IDM_IcsClient, MF_BYCOMMAND|MF_GRAYED },
\r
8162 { ACTION_POS, MF_BYPOSITION|MF_GRAYED },
\r
8163 { IDM_Revert, MF_BYCOMMAND|MF_GRAYED },
\r
8164 { IDM_Annotate, MF_BYCOMMAND|MF_GRAYED },
\r
8165 { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },
\r
8166 { IDM_RetractMove, MF_BYCOMMAND|MF_GRAYED },
\r
8167 { IDM_TimeControl, MF_BYCOMMAND|MF_GRAYED },
\r
8168 { IDM_Hint, MF_BYCOMMAND|MF_GRAYED },
\r
8169 { IDM_Book, MF_BYCOMMAND|MF_GRAYED },
\r
8170 { IDM_MachineBoth, MF_BYCOMMAND|MF_GRAYED },
\r
8171 { IDM_NewChat, MF_BYCOMMAND|MF_GRAYED },
\r
8172 { IDM_Engine1Options, MF_BYCOMMAND|MF_GRAYED },
\r
8173 { IDM_Engine2Options, MF_BYCOMMAND|MF_GRAYED },
\r
8174 { IDM_Sounds, MF_BYCOMMAND|MF_GRAYED },
\r
8178 Enables trainingOnEnables[] = {
\r
8179 { IDM_EditComment, MF_BYCOMMAND|MF_GRAYED },
\r
8180 { IDM_Comment, MF_BYCOMMAND|MF_GRAYED },
\r
8181 { IDM_Pause, MF_BYCOMMAND|MF_GRAYED },
\r
8182 { IDM_Forward, MF_BYCOMMAND|MF_GRAYED },
\r
8183 { IDM_Backward, MF_BYCOMMAND|MF_GRAYED },
\r
8184 { IDM_ToEnd, MF_BYCOMMAND|MF_GRAYED },
\r
8185 { IDM_ToStart, MF_BYCOMMAND|MF_GRAYED },
\r
8186 { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },
\r
8187 { IDM_TruncateGame, MF_BYCOMMAND|MF_GRAYED },
\r
8191 Enables trainingOffEnables[] = {
\r
8192 { IDM_EditComment, MF_BYCOMMAND|MF_ENABLED },
\r
8193 { IDM_Comment, MF_BYCOMMAND|MF_ENABLED },
\r
8194 { IDM_Pause, MF_BYCOMMAND|MF_ENABLED },
\r
8195 { IDM_Forward, MF_BYCOMMAND|MF_ENABLED },
\r
8196 { IDM_Backward, MF_BYCOMMAND|MF_ENABLED },
\r
8197 { IDM_ToEnd, MF_BYCOMMAND|MF_ENABLED },
\r
8198 { IDM_ToStart, MF_BYCOMMAND|MF_ENABLED },
\r
8199 { IDM_MoveNow, MF_BYCOMMAND|MF_ENABLED },
\r
8200 { IDM_TruncateGame, MF_BYCOMMAND|MF_ENABLED },
\r
8204 /* These modify either ncpEnables or gnuEnables */
\r
8205 Enables cmailEnables[] = {
\r
8206 { IDM_MailMove, MF_BYCOMMAND|MF_ENABLED },
\r
8207 { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_ENABLED },
\r
8208 { ACTION_POS, MF_BYPOSITION|MF_ENABLED },
\r
8209 { IDM_CallFlag, MF_BYCOMMAND|MF_GRAYED },
\r
8210 { IDM_Draw, MF_BYCOMMAND|MF_ENABLED },
\r
8211 { IDM_Adjourn, MF_BYCOMMAND|MF_GRAYED },
\r
8212 { IDM_Abort, MF_BYCOMMAND|MF_GRAYED },
\r
8216 Enables machineThinkingEnables[] = {
\r
8217 { IDM_LoadGame, MF_BYCOMMAND|MF_GRAYED },
\r
8218 { IDM_LoadNextGame, MF_BYCOMMAND|MF_GRAYED },
\r
8219 { IDM_LoadPrevGame, MF_BYCOMMAND|MF_GRAYED },
\r
8220 { IDM_ReloadGame, MF_BYCOMMAND|MF_GRAYED },
\r
8221 { IDM_PasteGame, MF_BYCOMMAND|MF_GRAYED },
\r
8222 { IDM_LoadPosition, MF_BYCOMMAND|MF_GRAYED },
\r
8223 { IDM_LoadNextPosition, MF_BYCOMMAND|MF_GRAYED },
\r
8224 { IDM_LoadPrevPosition, MF_BYCOMMAND|MF_GRAYED },
\r
8225 { IDM_ReloadPosition, MF_BYCOMMAND|MF_GRAYED },
\r
8226 { IDM_PastePosition, MF_BYCOMMAND|MF_GRAYED },
\r
8227 { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },
\r
8228 { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },
\r
8229 { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },
\r
8230 // { IDM_Match, MF_BYCOMMAND|MF_GRAYED },
\r
8231 { IDM_TypeInMove, MF_BYCOMMAND|MF_GRAYED },
\r
8232 { IDM_RetractMove, MF_BYCOMMAND|MF_GRAYED },
\r
8236 Enables userThinkingEnables[] = {
\r
8237 { IDM_LoadGame, MF_BYCOMMAND|MF_ENABLED },
\r
8238 { IDM_LoadNextGame, MF_BYCOMMAND|MF_ENABLED },
\r
8239 { IDM_LoadPrevGame, MF_BYCOMMAND|MF_ENABLED },
\r
8240 { IDM_ReloadGame, MF_BYCOMMAND|MF_ENABLED },
\r
8241 { IDM_PasteGame, MF_BYCOMMAND|MF_ENABLED },
\r
8242 { IDM_LoadPosition, MF_BYCOMMAND|MF_ENABLED },
\r
8243 { IDM_LoadNextPosition, MF_BYCOMMAND|MF_ENABLED },
\r
8244 { IDM_LoadPrevPosition, MF_BYCOMMAND|MF_ENABLED },
\r
8245 { IDM_ReloadPosition, MF_BYCOMMAND|MF_ENABLED },
\r
8246 { IDM_PastePosition, MF_BYCOMMAND|MF_ENABLED },
\r
8247 { IDM_MachineWhite, MF_BYCOMMAND|MF_ENABLED },
\r
8248 { IDM_MachineBlack, MF_BYCOMMAND|MF_ENABLED },
\r
8249 { IDM_TwoMachines, MF_BYCOMMAND|MF_ENABLED },
\r
8250 // { IDM_Match, MF_BYCOMMAND|MF_ENABLED },
\r
8251 { IDM_TypeInMove, MF_BYCOMMAND|MF_ENABLED },
\r
8252 { IDM_RetractMove, MF_BYCOMMAND|MF_ENABLED },
\r
8256 /*---------------------------------------------------------------------------*\
\r
8258 * Front-end interface functions exported by XBoard.
\r
8259 * Functions appear in same order as prototypes in frontend.h.
\r
8261 \*---------------------------------------------------------------------------*/
\r
8263 CheckMark(UINT item, int state)
\r
8265 if(item) CheckMenuItem(GetMenu(hwndMain), item, MF_BYCOMMAND|state);
\r
8271 static UINT prevChecked = 0;
\r
8272 static int prevPausing = 0;
\r
8275 if (pausing != prevPausing) {
\r
8276 prevPausing = pausing;
\r
8277 (void) CheckMenuItem(GetMenu(hwndMain), IDM_Pause,
\r
8278 MF_BYCOMMAND|(pausing ? MF_CHECKED : MF_UNCHECKED));
\r
8279 if (hwndPause) SetWindowText(hwndPause, pausing ? "C" : "P");
\r
8282 switch (gameMode) {
\r
8283 case BeginningOfGame:
\r
8284 if (appData.icsActive)
\r
8285 nowChecked = IDM_IcsClient;
\r
8286 else if (appData.noChessProgram)
\r
8287 nowChecked = IDM_EditGame;
\r
8289 nowChecked = IDM_MachineBlack;
\r
8291 case MachinePlaysBlack:
\r
8292 nowChecked = IDM_MachineBlack;
\r
8294 case MachinePlaysWhite:
\r
8295 nowChecked = IDM_MachineWhite;
\r
8297 case TwoMachinesPlay:
\r
8298 nowChecked = IDM_TwoMachines;
\r
8301 nowChecked = IDM_AnalysisMode;
\r
8304 nowChecked = IDM_AnalyzeFile;
\r
8307 nowChecked = IDM_EditGame;
\r
8309 case PlayFromGameFile:
\r
8310 nowChecked = IDM_LoadGame;
\r
8312 case EditPosition:
\r
8313 nowChecked = IDM_EditPosition;
\r
8316 nowChecked = IDM_Training;
\r
8318 case IcsPlayingWhite:
\r
8319 case IcsPlayingBlack:
\r
8320 case IcsObserving:
\r
8322 nowChecked = IDM_IcsClient;
\r
8329 if(prevChecked == IDM_TwoMachines) // [HGM] 'Machine Match' might have gotten disabled when stopping match
\r
8330 EnableMenuItem(GetMenu(hwndMain), IDM_Match, MF_BYCOMMAND|MF_ENABLED);
\r
8331 CheckMark(prevChecked, MF_UNCHECKED);
\r
8332 CheckMark(nowChecked, MF_CHECKED);
\r
8333 CheckMark(IDM_Match, matchMode && matchGame < appData.matchGames ? MF_CHECKED : MF_UNCHECKED);
\r
8335 if (nowChecked == IDM_LoadGame || nowChecked == IDM_Training) {
\r
8336 (void) EnableMenuItem(GetMenu(hwndMain), IDM_Training,
\r
8337 MF_BYCOMMAND|MF_ENABLED);
\r
8339 (void) EnableMenuItem(GetMenu(hwndMain),
\r
8340 IDM_Training, MF_BYCOMMAND|MF_GRAYED);
\r
8343 prevChecked = nowChecked;
\r
8345 /* [DM] icsEngineAnalyze - Do a sceure check too */
\r
8346 if (appData.icsActive) {
\r
8347 if (appData.icsEngineAnalyze) {
\r
8348 CheckMark(IDM_AnalysisMode, MF_CHECKED);
\r
8350 CheckMark(IDM_AnalysisMode, MF_UNCHECKED);
\r
8353 DisplayLogos(); // [HGM] logos: mode change could have altered logos
\r
8359 HMENU hmenu = GetMenu(hwndMain);
\r
8360 SetMenuEnables(hmenu, icsEnables);
\r
8361 EnableMenuItem(GetSubMenu(hmenu, OPTIONS_POS), IDM_IcsOptions,
\r
8362 MF_BYCOMMAND|MF_ENABLED);
\r
8364 if (appData.zippyPlay) {
\r
8365 SetMenuEnables(hmenu, zippyEnables);
\r
8366 if (!appData.noChessProgram) /* [DM] icsEngineAnalyze */
\r
8367 (void) EnableMenuItem(GetMenu(hwndMain), IDM_AnalysisMode,
\r
8368 MF_BYCOMMAND|MF_ENABLED);
\r
8376 SetMenuEnables(GetMenu(hwndMain), gnuEnables);
\r
8382 HMENU hmenu = GetMenu(hwndMain);
\r
8383 SetMenuEnables(hmenu, ncpEnables);
\r
8384 DrawMenuBar(hwndMain);
\r
8390 SetMenuEnables(GetMenu(hwndMain), cmailEnables);
\r
8394 SetTrainingModeOn()
\r
8397 SetMenuEnables(GetMenu(hwndMain), trainingOnEnables);
\r
8398 for (i = 0; i < N_BUTTONS; i++) {
\r
8399 if (buttonDesc[i].hwnd != NULL)
\r
8400 EnableWindow(buttonDesc[i].hwnd, FALSE);
\r
8405 VOID SetTrainingModeOff()
\r
8408 SetMenuEnables(GetMenu(hwndMain), trainingOffEnables);
\r
8409 for (i = 0; i < N_BUTTONS; i++) {
\r
8410 if (buttonDesc[i].hwnd != NULL)
\r
8411 EnableWindow(buttonDesc[i].hwnd, TRUE);
\r
8417 SetUserThinkingEnables()
\r
8419 SetMenuEnables(GetMenu(hwndMain), userThinkingEnables);
\r
8423 SetMachineThinkingEnables()
\r
8425 HMENU hMenu = GetMenu(hwndMain);
\r
8426 int flags = MF_BYCOMMAND|MF_ENABLED;
\r
8428 SetMenuEnables(hMenu, machineThinkingEnables);
\r
8430 if (gameMode == MachinePlaysBlack) {
\r
8431 (void)EnableMenuItem(hMenu, IDM_MachineBlack, flags);
\r
8432 } else if (gameMode == MachinePlaysWhite) {
\r
8433 (void)EnableMenuItem(hMenu, IDM_MachineWhite, flags);
\r
8434 } else if (gameMode == TwoMachinesPlay) {
\r
8435 (void)EnableMenuItem(hMenu, matchMode ? IDM_Match : IDM_TwoMachines, flags); // [HGM] match
\r
8441 DisplayTitle(char *str)
\r
8443 char title[MSG_SIZ], *host;
\r
8444 if (str[0] != NULLCHAR) {
\r
8445 safeStrCpy(title, str, sizeof(title)/sizeof(title[0]) );
\r
8446 } else if (appData.icsActive) {
\r
8447 if (appData.icsCommPort[0] != NULLCHAR)
\r
8450 host = appData.icsHost;
\r
8451 snprintf(title, MSG_SIZ, "%s: %s", szTitle, host);
\r
8452 } else if (appData.noChessProgram) {
\r
8453 safeStrCpy(title, szTitle, sizeof(title)/sizeof(title[0]) );
\r
8455 safeStrCpy(title, szTitle, sizeof(title)/sizeof(title[0]) );
\r
8456 strcat(title, ": ");
\r
8457 strcat(title, first.tidy);
\r
8459 SetWindowText(hwndMain, title);
\r
8464 DisplayMessage(char *str1, char *str2)
\r
8468 int remain = MESSAGE_TEXT_MAX - 1;
\r
8471 moveErrorMessageUp = FALSE; /* turned on later by caller if needed */
\r
8472 messageText[0] = NULLCHAR;
\r
8474 len = strlen(str1);
\r
8475 if (len > remain) len = remain;
\r
8476 strncpy(messageText, str1, len);
\r
8477 messageText[len] = NULLCHAR;
\r
8480 if (*str2 && remain >= 2) {
\r
8482 strcat(messageText, " ");
\r
8485 len = strlen(str2);
\r
8486 if (len > remain) len = remain;
\r
8487 strncat(messageText, str2, len);
\r
8489 messageText[MESSAGE_TEXT_MAX - 1] = NULLCHAR;
\r
8490 safeStrCpy(lastMsg, messageText, MSG_SIZ);
\r
8492 if (hwndMain == NULL || IsIconic(hwndMain)) return;
\r
8496 hdc = GetDC(hwndMain);
\r
8497 oldFont = SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);
\r
8498 ExtTextOut(hdc, messageRect.left, messageRect.top, ETO_CLIPPED|ETO_OPAQUE,
\r
8499 &messageRect, messageText, strlen(messageText), NULL);
\r
8500 (void) SelectObject(hdc, oldFont);
\r
8501 (void) ReleaseDC(hwndMain, hdc);
\r
8505 DisplayError(char *str, int error)
\r
8507 char buf[MSG_SIZ*2], buf2[MSG_SIZ];
\r
8511 safeStrCpy(buf, str, sizeof(buf)/sizeof(buf[0]) );
\r
8513 len = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,
\r
8514 NULL, error, LANG_NEUTRAL,
\r
8515 (LPSTR) buf2, MSG_SIZ, NULL);
\r
8517 snprintf(buf, 2*MSG_SIZ, "%s:\n%s", str, buf2);
\r
8519 ErrorMap *em = errmap;
\r
8520 while (em->err != 0 && em->err != error) em++;
\r
8521 if (em->err != 0) {
\r
8522 snprintf(buf, 2*MSG_SIZ, "%s:\n%s", str, em->msg);
\r
8524 snprintf(buf, 2*MSG_SIZ, "%s:\nError code %d", str, error);
\r
8529 ErrorPopUp(_("Error"), buf);
\r
8534 DisplayMoveError(char *str)
\r
8536 fromX = fromY = -1;
\r
8537 ClearHighlights();
\r
8538 DrawPosition(FALSE, NULL);
\r
8539 if (appData.popupMoveErrors) {
\r
8540 ErrorPopUp(_("Error"), str);
\r
8542 DisplayMessage(str, "");
\r
8543 moveErrorMessageUp = TRUE;
\r
8548 DisplayFatalError(char *str, int error, int exitStatus)
\r
8550 char buf[2*MSG_SIZ], buf2[MSG_SIZ];
\r
8552 char *label = exitStatus ? _("Fatal Error") : _("Exiting");
\r
8555 len = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,
\r
8556 NULL, error, LANG_NEUTRAL,
\r
8557 (LPSTR) buf2, MSG_SIZ, NULL);
\r
8559 snprintf(buf, 2*MSG_SIZ, "%s:\n%s", str, buf2);
\r
8561 ErrorMap *em = errmap;
\r
8562 while (em->err != 0 && em->err != error) em++;
\r
8563 if (em->err != 0) {
\r
8564 snprintf(buf, 2*MSG_SIZ, "%s:\n%s", str, em->msg);
\r
8566 snprintf(buf, 2*MSG_SIZ, "%s:\nError code %d", str, error);
\r
8571 if (appData.debugMode) {
\r
8572 fprintf(debugFP, "%s: %s\n", label, str);
\r
8574 if (appData.popupExitMessage) {
\r
8575 if(appData.icsActive) SendToICS("logout\n"); // [HGM] make sure no new games will be started!
\r
8576 (void) MessageBox(hwndMain, str, label, MB_OK|
\r
8577 (exitStatus ? MB_ICONSTOP : MB_ICONINFORMATION));
\r
8579 ExitEvent(exitStatus);
\r
8584 DisplayInformation(char *str)
\r
8586 (void) MessageBox(hwndMain, str, _("Information"), MB_OK|MB_ICONINFORMATION);
\r
8596 DisplayNote(char *str)
\r
8598 ErrorPopUp(_("Note"), str);
\r
8603 char *title, *question, *replyPrefix;
\r
8608 QuestionDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
8610 static QuestionParams *qp;
\r
8611 char reply[MSG_SIZ];
\r
8614 switch (message) {
\r
8615 case WM_INITDIALOG:
\r
8616 qp = (QuestionParams *) lParam;
\r
8617 CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));
\r
8618 Translate(hDlg, DLG_Question);
\r
8619 SetWindowText(hDlg, qp->title);
\r
8620 SetDlgItemText(hDlg, OPT_QuestionText, qp->question);
\r
8621 SetFocus(GetDlgItem(hDlg, OPT_QuestionInput));
\r
8625 switch (LOWORD(wParam)) {
\r
8627 safeStrCpy(reply, qp->replyPrefix, sizeof(reply)/sizeof(reply[0]) );
\r
8628 if (*reply) strcat(reply, " ");
\r
8629 len = strlen(reply);
\r
8630 GetDlgItemText(hDlg, OPT_QuestionInput, reply + len, sizeof(reply) - len);
\r
8631 strcat(reply, "\n");
\r
8632 OutputToProcess(qp->pr, reply, strlen(reply), &err);
\r
8633 EndDialog(hDlg, TRUE);
\r
8634 if (err) DisplayFatalError(_("Error writing to chess program"), err, 1);
\r
8637 EndDialog(hDlg, FALSE);
\r
8648 AskQuestion(char* title, char *question, char *replyPrefix, ProcRef pr)
\r
8650 QuestionParams qp;
\r
8654 qp.question = question;
\r
8655 qp.replyPrefix = replyPrefix;
\r
8657 lpProc = MakeProcInstance((FARPROC)QuestionDialog, hInst);
\r
8658 DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_Question),
\r
8659 hwndMain, (DLGPROC)lpProc, (LPARAM)&qp);
\r
8660 FreeProcInstance(lpProc);
\r
8663 /* [AS] Pick FRC position */
\r
8664 LRESULT CALLBACK NewGameFRC_Proc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
8666 static int * lpIndexFRC;
\r
8672 case WM_INITDIALOG:
\r
8673 lpIndexFRC = (int *) lParam;
\r
8675 CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));
\r
8676 Translate(hDlg, DLG_NewGameFRC);
\r
8678 SendDlgItemMessage( hDlg, IDC_NFG_Edit, EM_SETLIMITTEXT, sizeof(buf)-1, 0 );
\r
8679 SetDlgItemInt( hDlg, IDC_NFG_Edit, *lpIndexFRC, TRUE );
\r
8680 SendDlgItemMessage( hDlg, IDC_NFG_Edit, EM_SETSEL, 0, -1 );
\r
8681 SetFocus(GetDlgItem(hDlg, IDC_NFG_Edit));
\r
8686 switch( LOWORD(wParam) ) {
\r
8688 *lpIndexFRC = GetDlgItemInt(hDlg, IDC_NFG_Edit, &index_is_ok, TRUE );
\r
8689 EndDialog( hDlg, 0 );
\r
8690 shuffleOpenings = TRUE; /* [HGM] shuffle: switch shuffling on for as long as we stay in current variant */
\r
8693 EndDialog( hDlg, 1 );
\r
8695 case IDC_NFG_Edit:
\r
8696 if( HIWORD(wParam) == EN_CHANGE ) {
\r
8697 GetDlgItemInt(hDlg, IDC_NFG_Edit, &index_is_ok, TRUE );
\r
8699 EnableWindow( GetDlgItem(hDlg, IDOK), index_is_ok );
\r
8702 case IDC_NFG_Random:
\r
8703 snprintf( buf, sizeof(buf)/sizeof(buf[0]), "%d", myrandom() ); /* [HGM] shuffle: no longer limit to 960 */
\r
8704 SetDlgItemText(hDlg, IDC_NFG_Edit, buf );
\r
8717 int index = appData.defaultFrcPosition;
\r
8718 FARPROC lpProc = MakeProcInstance( (FARPROC) NewGameFRC_Proc, hInst );
\r
8720 result = DialogBoxParam( hInst, MAKEINTRESOURCE(DLG_NewGameFRC), hwndMain, (DLGPROC)lpProc, (LPARAM)&index );
\r
8722 if( result == 0 ) {
\r
8723 appData.defaultFrcPosition = index;
\r
8729 /* [AS] Game list options. Refactored by HGM */
\r
8731 HWND gameListOptionsDialog;
\r
8733 // low-level front-end: clear text edit / list widget
\r
8738 SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_RESETCONTENT, 0, 0 );
\r
8741 // low-level front-end: clear text edit / list widget
\r
8743 GLT_DeSelectList()
\r
8745 SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_SETCURSEL, 0, 0 );
\r
8748 // low-level front-end: append line to text edit / list widget
\r
8750 GLT_AddToList( char *name )
\r
8753 SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_ADDSTRING, 0, (LPARAM) name );
\r
8757 // low-level front-end: get line from text edit / list widget
\r
8759 GLT_GetFromList( int index, char *name )
\r
8762 if( SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_GETTEXT, index, (LPARAM) name ) != LB_ERR )
\r
8768 void GLT_MoveSelection( HWND hDlg, int delta )
\r
8770 int idx1 = (int) SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETCURSEL, 0, 0 );
\r
8771 int idx2 = idx1 + delta;
\r
8772 int count = (int) SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETCOUNT, 0, 0 );
\r
8774 if( idx1 >=0 && idx1 < count && idx2 >= 0 && idx2 < count ) {
\r
8777 SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETTEXT, idx1, (LPARAM) buf );
\r
8778 SendDlgItemMessage( hDlg, IDC_GameListTags, LB_DELETESTRING, idx1, 0 );
\r
8779 SendDlgItemMessage( hDlg, IDC_GameListTags, LB_INSERTSTRING, idx2, (LPARAM) buf );
\r
8780 SendDlgItemMessage( hDlg, IDC_GameListTags, LB_SETCURSEL, idx2, 0 );
\r
8784 LRESULT CALLBACK GameListOptions_Proc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
8788 case WM_INITDIALOG:
\r
8789 gameListOptionsDialog = hDlg; // [HGM] pass through global to keep out off back-end
\r
8791 CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));
\r
8792 Translate(hDlg, DLG_GameListOptions);
\r
8794 /* Initialize list */
\r
8795 GLT_TagsToList( lpUserGLT );
\r
8797 SetFocus( GetDlgItem(hDlg, IDC_GameListTags) );
\r
8802 switch( LOWORD(wParam) ) {
\r
8805 EndDialog( hDlg, 0 );
\r
8808 EndDialog( hDlg, 1 );
\r
8811 case IDC_GLT_Default:
\r
8812 GLT_TagsToList( GLT_DEFAULT_TAGS );
\r
8815 case IDC_GLT_Restore:
\r
8816 GLT_TagsToList( appData.gameListTags );
\r
8820 GLT_MoveSelection( hDlg, -1 );
\r
8823 case IDC_GLT_Down:
\r
8824 GLT_MoveSelection( hDlg, +1 );
\r
8834 int GameListOptions()
\r
8837 FARPROC lpProc = MakeProcInstance( (FARPROC) GameListOptions_Proc, hInst );
\r
8839 safeStrCpy( lpUserGLT, appData.gameListTags ,LPUSERGLT_SIZE );
\r
8841 result = DialogBoxParam( hInst, MAKEINTRESOURCE(DLG_GameListOptions), hwndMain, (DLGPROC)lpProc, (LPARAM)lpUserGLT );
\r
8843 if( result == 0 ) {
\r
8844 char *oldTags = appData.gameListTags;
\r
8845 /* [AS] Memory leak here! */
\r
8846 appData.gameListTags = strdup( lpUserGLT );
\r
8847 if(strcmp(oldTags, appData.gameListTags)) // [HGM] redo Game List when we changed something
\r
8848 GameListToListBox(NULL, TRUE, ".", NULL, FALSE, FALSE); // "." as filter is kludge to select all
\r
8855 DisplayIcsInteractionTitle(char *str)
\r
8857 char consoleTitle[MSG_SIZ];
\r
8859 snprintf(consoleTitle, MSG_SIZ, "%s: %s", szConsoleTitle, str);
\r
8860 SetWindowText(hwndConsole, consoleTitle);
\r
8862 if(appData.chatBoxes) { // [HGM] chat: open chat boxes
\r
8863 char buf[MSG_SIZ], *p = buf, *q;
\r
8864 safeStrCpy(buf, appData.chatBoxes, sizeof(buf)/sizeof(buf[0]) );
\r
8866 q = strchr(p, ';');
\r
8868 if(*p) ChatPopUp(p);
\r
8872 SetActiveWindow(hwndMain);
\r
8876 DrawPosition(int fullRedraw, Board board)
\r
8878 HDCDrawPosition(NULL, (BOOLEAN) fullRedraw, board);
\r
8881 void NotifyFrontendLogin()
\r
8884 UpdateICSWidth(GetDlgItem(hwndConsole, OPT_ConsoleText));
\r
8890 fromX = fromY = -1;
\r
8891 if (dragInfo.pos.x != -1 || dragInfo.pos.y != -1) {
\r
8892 dragInfo.pos.x = dragInfo.pos.y = -1;
\r
8893 dragInfo.pos.x = dragInfo.pos.y = -1;
\r
8894 dragInfo.lastpos = dragInfo.pos;
\r
8895 dragInfo.start.x = dragInfo.start.y = -1;
\r
8896 dragInfo.from = dragInfo.start;
\r
8898 DrawPosition(TRUE, NULL);
\r
8905 CommentPopUp(char *title, char *str)
\r
8907 HWND hwnd = GetActiveWindow();
\r
8908 EitherCommentPopUp(currentMove, title, str, FALSE); // [HGM] vari: fake move index, rather than 0
\r
8910 SetActiveWindow(hwnd);
\r
8914 CommentPopDown(void)
\r
8916 CheckMenuItem(GetMenu(hwndMain), IDM_Comment, MF_UNCHECKED);
\r
8917 if (commentDialog) {
\r
8918 ShowWindow(commentDialog, SW_HIDE);
\r
8920 commentUp = FALSE;
\r
8924 EditCommentPopUp(int index, char *title, char *str)
\r
8926 EitherCommentPopUp(index, title, str, TRUE);
\r
8933 MyPlaySound(&sounds[(int)SoundRoar]);
\r
8940 MyPlaySound(&sounds[(int)SoundMove]);
\r
8943 VOID PlayIcsWinSound()
\r
8945 MyPlaySound(&sounds[(int)SoundIcsWin]);
\r
8948 VOID PlayIcsLossSound()
\r
8950 MyPlaySound(&sounds[(int)SoundIcsLoss]);
\r
8953 VOID PlayIcsDrawSound()
\r
8955 MyPlaySound(&sounds[(int)SoundIcsDraw]);
\r
8958 VOID PlayIcsUnfinishedSound()
\r
8960 MyPlaySound(&sounds[(int)SoundIcsUnfinished]);
\r
8966 MyPlaySound(&sounds[(int)SoundAlarm]);
\r
8972 MyPlaySound(&textAttribs[ColorTell].sound);
\r
8980 consoleEcho = TRUE;
\r
8981 hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
8982 SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&consoleCF);
\r
8983 SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);
\r
8992 consoleEcho = FALSE;
\r
8993 hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
8994 /* This works OK: set text and background both to the same color */
\r
8996 cf.crTextColor = COLOR_ECHOOFF;
\r
8997 SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf);
\r
8998 SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, cf.crTextColor);
\r
9001 /* No Raw()...? */
\r
9003 void Colorize(ColorClass cc, int continuation)
\r
9005 currentColorClass = cc;
\r
9006 consoleCF.dwMask = CFM_COLOR|CFM_BOLD|CFM_ITALIC|CFM_UNDERLINE|CFM_STRIKEOUT;
\r
9007 consoleCF.crTextColor = textAttribs[cc].color;
\r
9008 consoleCF.dwEffects = textAttribs[cc].effects;
\r
9009 if (!continuation) MyPlaySound(&textAttribs[cc].sound);
\r
9015 static char buf[MSG_SIZ];
\r
9016 DWORD bufsiz = MSG_SIZ;
\r
9018 if(appData.userName != NULL && appData.userName[0] != 0) {
\r
9019 return appData.userName; /* [HGM] username: prefer name selected by user over his system login */
\r
9021 if (!GetUserName(buf, &bufsiz)) {
\r
9022 /*DisplayError("Error getting user name", GetLastError());*/
\r
9023 safeStrCpy(buf, _("User"), sizeof(buf)/sizeof(buf[0]) );
\r
9031 static char buf[MSG_SIZ];
\r
9032 DWORD bufsiz = MSG_SIZ;
\r
9034 if (!GetComputerName(buf, &bufsiz)) {
\r
9035 /*DisplayError("Error getting host name", GetLastError());*/
\r
9036 safeStrCpy(buf, _("Unknown"), sizeof(buf)/sizeof(buf[0]) );
\r
9043 ClockTimerRunning()
\r
9045 return clockTimerEvent != 0;
\r
9051 if (clockTimerEvent == 0) return FALSE;
\r
9052 KillTimer(hwndMain, clockTimerEvent);
\r
9053 clockTimerEvent = 0;
\r
9058 StartClockTimer(long millisec)
\r
9060 clockTimerEvent = SetTimer(hwndMain, (UINT) CLOCK_TIMER_ID,
\r
9061 (UINT) millisec, NULL);
\r
9065 DisplayWhiteClock(long timeRemaining, int highlight)
\r
9068 char *flag = whiteFlag && gameMode == TwoMachinesPlay ? "(!)" : "";
\r
9070 if(appData.noGUI) return;
\r
9071 hdc = GetDC(hwndMain);
\r
9072 if (!IsIconic(hwndMain)) {
\r
9073 DisplayAClock(hdc, timeRemaining, highlight,
\r
9074 flipClock ? &blackRect : &whiteRect, _("White"), flag);
\r
9076 if (highlight && iconCurrent == iconBlack) {
\r
9077 iconCurrent = iconWhite;
\r
9078 PostMessage(hwndMain, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);
\r
9079 if (IsIconic(hwndMain)) {
\r
9080 DrawIcon(hdc, 2, 2, iconCurrent);
\r
9083 (void) ReleaseDC(hwndMain, hdc);
\r
9085 PostMessage(hwndConsole, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);
\r
9089 DisplayBlackClock(long timeRemaining, int highlight)
\r
9092 char *flag = blackFlag && gameMode == TwoMachinesPlay ? "(!)" : "";
\r
9095 if(appData.noGUI) return;
\r
9096 hdc = GetDC(hwndMain);
\r
9097 if (!IsIconic(hwndMain)) {
\r
9098 DisplayAClock(hdc, timeRemaining, highlight,
\r
9099 flipClock ? &whiteRect : &blackRect, _("Black"), flag);
\r
9101 if (highlight && iconCurrent == iconWhite) {
\r
9102 iconCurrent = iconBlack;
\r
9103 PostMessage(hwndMain, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);
\r
9104 if (IsIconic(hwndMain)) {
\r
9105 DrawIcon(hdc, 2, 2, iconCurrent);
\r
9108 (void) ReleaseDC(hwndMain, hdc);
\r
9110 PostMessage(hwndConsole, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);
\r
9115 LoadGameTimerRunning()
\r
9117 return loadGameTimerEvent != 0;
\r
9121 StopLoadGameTimer()
\r
9123 if (loadGameTimerEvent == 0) return FALSE;
\r
9124 KillTimer(hwndMain, loadGameTimerEvent);
\r
9125 loadGameTimerEvent = 0;
\r
9130 StartLoadGameTimer(long millisec)
\r
9132 loadGameTimerEvent = SetTimer(hwndMain, (UINT) LOAD_GAME_TIMER_ID,
\r
9133 (UINT) millisec, NULL);
\r
9141 char fileTitle[MSG_SIZ];
\r
9143 defName = DefaultFileName(appData.oldSaveStyle ? "gam" : "pgn");
\r
9144 f = OpenFileDialog(hwndMain, "a", defName,
\r
9145 appData.oldSaveStyle ? "gam" : "pgn",
\r
9147 _("Save Game to File"), NULL, fileTitle, NULL);
\r
9149 SaveGame(f, 0, "");
\r
9156 ScheduleDelayedEvent(DelayedEventCallback cb, long millisec)
\r
9158 if (delayedTimerEvent != 0) {
\r
9159 if (appData.debugMode && cb != delayedTimerCallback) { // [HGM] alive: not too much debug
\r
9160 fprintf(debugFP, "ScheduleDelayedEvent: event already scheduled\n");
\r
9162 KillTimer(hwndMain, delayedTimerEvent);
\r
9163 delayedTimerEvent = 0;
\r
9164 if(delayedTimerCallback != cb) // [HGM] alive: do not "flush" same event, just postpone it
\r
9165 delayedTimerCallback();
\r
9167 delayedTimerCallback = cb;
\r
9168 delayedTimerEvent = SetTimer(hwndMain, (UINT) DELAYED_TIMER_ID,
\r
9169 (UINT) millisec, NULL);
\r
9172 DelayedEventCallback
\r
9175 if (delayedTimerEvent) {
\r
9176 return delayedTimerCallback;
\r
9183 CancelDelayedEvent()
\r
9185 if (delayedTimerEvent) {
\r
9186 KillTimer(hwndMain, delayedTimerEvent);
\r
9187 delayedTimerEvent = 0;
\r
9191 DWORD GetWin32Priority(int nice)
\r
9192 { // [HGM] nice: translate Unix nice() value to indows priority class. (Code stolen from Polyglot 1.4w11)
\r
9194 REALTIME_PRIORITY_CLASS 0x00000100
\r
9195 HIGH_PRIORITY_CLASS 0x00000080
\r
9196 ABOVE_NORMAL_PRIORITY_CLASS 0x00008000
\r
9197 NORMAL_PRIORITY_CLASS 0x00000020
\r
9198 BELOW_NORMAL_PRIORITY_CLASS 0x00004000
\r
9199 IDLE_PRIORITY_CLASS 0x00000040
\r
9201 if (nice < -15) return 0x00000080;
\r
9202 if (nice < 0) return 0x00008000;
\r
9204 if (nice == 0) return 0x00000020;
\r
9205 if (nice < 15) return 0x00004000;
\r
9206 return 0x00000040;
\r
9209 void RunCommand(char *cmdLine)
\r
9211 /* Now create the child process. */
\r
9212 STARTUPINFO siStartInfo;
\r
9213 PROCESS_INFORMATION piProcInfo;
\r
9215 siStartInfo.cb = sizeof(STARTUPINFO);
\r
9216 siStartInfo.lpReserved = NULL;
\r
9217 siStartInfo.lpDesktop = NULL;
\r
9218 siStartInfo.lpTitle = NULL;
\r
9219 siStartInfo.dwFlags = STARTF_USESTDHANDLES;
\r
9220 siStartInfo.cbReserved2 = 0;
\r
9221 siStartInfo.lpReserved2 = NULL;
\r
9222 siStartInfo.hStdInput = NULL;
\r
9223 siStartInfo.hStdOutput = NULL;
\r
9224 siStartInfo.hStdError = NULL;
\r
9226 CreateProcess(NULL,
\r
9227 cmdLine, /* command line */
\r
9228 NULL, /* process security attributes */
\r
9229 NULL, /* primary thread security attrs */
\r
9230 TRUE, /* handles are inherited */
\r
9231 DETACHED_PROCESS|CREATE_NEW_PROCESS_GROUP,
\r
9232 NULL, /* use parent's environment */
\r
9234 &siStartInfo, /* STARTUPINFO pointer */
\r
9235 &piProcInfo); /* receives PROCESS_INFORMATION */
\r
9237 CloseHandle(piProcInfo.hThread);
\r
9240 /* Start a child process running the given program.
\r
9241 The process's standard output can be read from "from", and its
\r
9242 standard input can be written to "to".
\r
9243 Exit with fatal error if anything goes wrong.
\r
9244 Returns an opaque pointer that can be used to destroy the process
\r
9248 StartChildProcess(char *cmdLine, char *dir, ProcRef *pr)
\r
9250 #define BUFSIZE 4096
\r
9252 HANDLE hChildStdinRd, hChildStdinWr,
\r
9253 hChildStdoutRd, hChildStdoutWr;
\r
9254 HANDLE hChildStdinWrDup, hChildStdoutRdDup;
\r
9255 SECURITY_ATTRIBUTES saAttr;
\r
9257 PROCESS_INFORMATION piProcInfo;
\r
9258 STARTUPINFO siStartInfo;
\r
9260 char buf[MSG_SIZ];
\r
9263 if (appData.debugMode) {
\r
9264 fprintf(debugFP, "StartChildProcess (dir=\"%s\") %s\n", dir, cmdLine);
\r
9269 /* Set the bInheritHandle flag so pipe handles are inherited. */
\r
9270 saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);
\r
9271 saAttr.bInheritHandle = TRUE;
\r
9272 saAttr.lpSecurityDescriptor = NULL;
\r
9275 * The steps for redirecting child's STDOUT:
\r
9276 * 1. Create anonymous pipe to be STDOUT for child.
\r
9277 * 2. Create a noninheritable duplicate of read handle,
\r
9278 * and close the inheritable read handle.
\r
9281 /* Create a pipe for the child's STDOUT. */
\r
9282 if (! CreatePipe(&hChildStdoutRd, &hChildStdoutWr, &saAttr, 0)) {
\r
9283 return GetLastError();
\r
9286 /* Duplicate the read handle to the pipe, so it is not inherited. */
\r
9287 fSuccess = DuplicateHandle(GetCurrentProcess(), hChildStdoutRd,
\r
9288 GetCurrentProcess(), &hChildStdoutRdDup, 0,
\r
9289 FALSE, /* not inherited */
\r
9290 DUPLICATE_SAME_ACCESS);
\r
9292 return GetLastError();
\r
9294 CloseHandle(hChildStdoutRd);
\r
9297 * The steps for redirecting child's STDIN:
\r
9298 * 1. Create anonymous pipe to be STDIN for child.
\r
9299 * 2. Create a noninheritable duplicate of write handle,
\r
9300 * and close the inheritable write handle.
\r
9303 /* Create a pipe for the child's STDIN. */
\r
9304 if (! CreatePipe(&hChildStdinRd, &hChildStdinWr, &saAttr, 0)) {
\r
9305 return GetLastError();
\r
9308 /* Duplicate the write handle to the pipe, so it is not inherited. */
\r
9309 fSuccess = DuplicateHandle(GetCurrentProcess(), hChildStdinWr,
\r
9310 GetCurrentProcess(), &hChildStdinWrDup, 0,
\r
9311 FALSE, /* not inherited */
\r
9312 DUPLICATE_SAME_ACCESS);
\r
9314 return GetLastError();
\r
9316 CloseHandle(hChildStdinWr);
\r
9318 /* Arrange to (1) look in dir for the child .exe file, and
\r
9319 * (2) have dir be the child's working directory. Interpret
\r
9320 * dir relative to the directory WinBoard loaded from. */
\r
9321 GetCurrentDirectory(MSG_SIZ, buf);
\r
9322 SetCurrentDirectory(installDir);
\r
9323 SetCurrentDirectory(dir);
\r
9325 /* Now create the child process. */
\r
9327 siStartInfo.cb = sizeof(STARTUPINFO);
\r
9328 siStartInfo.lpReserved = NULL;
\r
9329 siStartInfo.lpDesktop = NULL;
\r
9330 siStartInfo.lpTitle = NULL;
\r
9331 siStartInfo.dwFlags = STARTF_USESTDHANDLES;
\r
9332 siStartInfo.cbReserved2 = 0;
\r
9333 siStartInfo.lpReserved2 = NULL;
\r
9334 siStartInfo.hStdInput = hChildStdinRd;
\r
9335 siStartInfo.hStdOutput = hChildStdoutWr;
\r
9336 siStartInfo.hStdError = hChildStdoutWr;
\r
9338 fSuccess = CreateProcess(NULL,
\r
9339 cmdLine, /* command line */
\r
9340 NULL, /* process security attributes */
\r
9341 NULL, /* primary thread security attrs */
\r
9342 TRUE, /* handles are inherited */
\r
9343 DETACHED_PROCESS|CREATE_NEW_PROCESS_GROUP,
\r
9344 NULL, /* use parent's environment */
\r
9346 &siStartInfo, /* STARTUPINFO pointer */
\r
9347 &piProcInfo); /* receives PROCESS_INFORMATION */
\r
9349 err = GetLastError();
\r
9350 SetCurrentDirectory(buf); /* return to prev directory */
\r
9355 if (appData.niceEngines){ // [HGM] nice: adjust engine proc priority
\r
9356 if(appData.debugMode) fprintf(debugFP, "nice engine proc to %d\n", appData.niceEngines);
\r
9357 SetPriorityClass(piProcInfo.hProcess, GetWin32Priority(appData.niceEngines));
\r
9360 /* Close the handles we don't need in the parent */
\r
9361 CloseHandle(piProcInfo.hThread);
\r
9362 CloseHandle(hChildStdinRd);
\r
9363 CloseHandle(hChildStdoutWr);
\r
9365 /* Prepare return value */
\r
9366 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
\r
9367 cp->kind = CPReal;
\r
9368 cp->hProcess = piProcInfo.hProcess;
\r
9369 cp->pid = piProcInfo.dwProcessId;
\r
9370 cp->hFrom = hChildStdoutRdDup;
\r
9371 cp->hTo = hChildStdinWrDup;
\r
9373 *pr = (void *) cp;
\r
9375 /* Klaus Friedel says that this Sleep solves a problem under Windows
\r
9376 2000 where engines sometimes don't see the initial command(s)
\r
9377 from WinBoard and hang. I don't understand how that can happen,
\r
9378 but the Sleep is harmless, so I've put it in. Others have also
\r
9379 reported what may be the same problem, so hopefully this will fix
\r
9380 it for them too. */
\r
9388 DestroyChildProcess(ProcRef pr, int/*boolean*/ signal)
\r
9390 ChildProc *cp; int result;
\r
9392 cp = (ChildProc *) pr;
\r
9393 if (cp == NULL) return;
\r
9395 switch (cp->kind) {
\r
9397 /* TerminateProcess is considered harmful, so... */
\r
9398 CloseHandle(cp->hTo); /* Closing this will give the child an EOF and hopefully kill it */
\r
9399 if (cp->hFrom) CloseHandle(cp->hFrom); /* if NULL, InputThread will close it */
\r
9400 /* The following doesn't work because the chess program
\r
9401 doesn't "have the same console" as WinBoard. Maybe
\r
9402 we could arrange for this even though neither WinBoard
\r
9403 nor the chess program uses a console for stdio? */
\r
9404 /*!!if (signal) GenerateConsoleCtrlEvent(CTRL_BREAK_EVENT, cp->pid);*/
\r
9406 /* [AS] Special termination modes for misbehaving programs... */
\r
9407 if( signal & 8 ) {
\r
9408 result = TerminateProcess( cp->hProcess, 0 );
\r
9410 if ( appData.debugMode) {
\r
9411 fprintf( debugFP, "Terminating process %lu, result=%d\n", cp->pid, result );
\r
9414 else if( signal & 4 ) {
\r
9415 DWORD dw = WaitForSingleObject( cp->hProcess, appData.delayAfterQuit*1000 + 50 ); // Wait 3 seconds at most
\r
9417 if( dw != WAIT_OBJECT_0 ) {
\r
9418 result = TerminateProcess( cp->hProcess, 0 );
\r
9420 if ( appData.debugMode) {
\r
9421 fprintf( debugFP, "Process %lu still alive after timeout, killing... result=%d\n", cp->pid, result );
\r
9427 CloseHandle(cp->hProcess);
\r
9431 if (cp->hFrom) CloseHandle(cp->hFrom);
\r
9435 closesocket(cp->sock);
\r
9440 if (signal) send(cp->sock2, "\017", 1, 0); /* 017 = 15 = SIGTERM */
\r
9441 closesocket(cp->sock);
\r
9442 closesocket(cp->sock2);
\r
9450 InterruptChildProcess(ProcRef pr)
\r
9454 cp = (ChildProc *) pr;
\r
9455 if (cp == NULL) return;
\r
9456 switch (cp->kind) {
\r
9458 /* The following doesn't work because the chess program
\r
9459 doesn't "have the same console" as WinBoard. Maybe
\r
9460 we could arrange for this even though neither WinBoard
\r
9461 nor the chess program uses a console for stdio */
\r
9462 /*!!GenerateConsoleCtrlEvent(CTRL_C_EVENT, cp->pid);*/
\r
9467 /* Can't interrupt */
\r
9471 send(cp->sock2, "\002", 1, 0); /* 2 = SIGINT */
\r
9478 OpenTelnet(char *host, char *port, ProcRef *pr)
\r
9480 char cmdLine[MSG_SIZ];
\r
9482 if (port[0] == NULLCHAR) {
\r
9483 snprintf(cmdLine, MSG_SIZ, "%s %s", appData.telnetProgram, host);
\r
9485 snprintf(cmdLine, MSG_SIZ, "%s %s %s", appData.telnetProgram, host, port);
\r
9487 return StartChildProcess(cmdLine, "", pr);
\r
9491 /* Code to open TCP sockets */
\r
9494 OpenTCP(char *host, char *port, ProcRef *pr)
\r
9500 struct sockaddr_in sa, mysa;
\r
9501 struct hostent FAR *hp;
\r
9502 unsigned short uport;
\r
9503 WORD wVersionRequested;
\r
9506 /* Initialize socket DLL */
\r
9507 wVersionRequested = MAKEWORD(1, 1);
\r
9508 err = WSAStartup(wVersionRequested, &wsaData);
\r
9509 if (err != 0) return err;
\r
9512 if ((s = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {
\r
9513 err = WSAGetLastError();
\r
9518 /* Bind local address using (mostly) don't-care values.
\r
9520 memset((char *) &mysa, 0, sizeof(struct sockaddr_in));
\r
9521 mysa.sin_family = AF_INET;
\r
9522 mysa.sin_addr.s_addr = INADDR_ANY;
\r
9523 uport = (unsigned short) 0;
\r
9524 mysa.sin_port = htons(uport);
\r
9525 if (bind(s, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))
\r
9526 == SOCKET_ERROR) {
\r
9527 err = WSAGetLastError();
\r
9532 /* Resolve remote host name */
\r
9533 memset((char *) &sa, 0, sizeof(struct sockaddr_in));
\r
9534 if (!(hp = gethostbyname(host))) {
\r
9535 unsigned int b0, b1, b2, b3;
\r
9537 err = WSAGetLastError();
\r
9539 if (sscanf(host, "%u.%u.%u.%u", &b0, &b1, &b2, &b3) == 4) {
\r
9540 hp = (struct hostent *) calloc(1, sizeof(struct hostent));
\r
9541 hp->h_addrtype = AF_INET;
\r
9543 hp->h_addr_list = (char **) calloc(2, sizeof(char *));
\r
9544 hp->h_addr_list[0] = (char *) malloc(4);
\r
9545 hp->h_addr_list[0][0] = (char) b0;
\r
9546 hp->h_addr_list[0][1] = (char) b1;
\r
9547 hp->h_addr_list[0][2] = (char) b2;
\r
9548 hp->h_addr_list[0][3] = (char) b3;
\r
9554 sa.sin_family = hp->h_addrtype;
\r
9555 uport = (unsigned short) atoi(port);
\r
9556 sa.sin_port = htons(uport);
\r
9557 memcpy((char *) &sa.sin_addr, hp->h_addr, hp->h_length);
\r
9559 /* Make connection */
\r
9560 if (connect(s, (struct sockaddr *) &sa,
\r
9561 sizeof(struct sockaddr_in)) == SOCKET_ERROR) {
\r
9562 err = WSAGetLastError();
\r
9567 /* Prepare return value */
\r
9568 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
\r
9569 cp->kind = CPSock;
\r
9571 *pr = (ProcRef *) cp;
\r
9577 OpenCommPort(char *name, ProcRef *pr)
\r
9582 char fullname[MSG_SIZ];
\r
9584 if (*name != '\\')
\r
9585 snprintf(fullname, MSG_SIZ, "\\\\.\\%s", name);
\r
9587 safeStrCpy(fullname, name, sizeof(fullname)/sizeof(fullname[0]) );
\r
9589 h = CreateFile(name, GENERIC_READ | GENERIC_WRITE,
\r
9590 0, NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL);
\r
9591 if (h == (HANDLE) -1) {
\r
9592 return GetLastError();
\r
9596 if (!SetCommState(h, (LPDCB) &dcb)) return GetLastError();
\r
9598 /* Accumulate characters until a 100ms pause, then parse */
\r
9599 ct.ReadIntervalTimeout = 100;
\r
9600 ct.ReadTotalTimeoutMultiplier = 0;
\r
9601 ct.ReadTotalTimeoutConstant = 0;
\r
9602 ct.WriteTotalTimeoutMultiplier = 0;
\r
9603 ct.WriteTotalTimeoutConstant = 0;
\r
9604 if (!SetCommTimeouts(h, (LPCOMMTIMEOUTS) &ct)) return GetLastError();
\r
9606 /* Prepare return value */
\r
9607 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
\r
9608 cp->kind = CPComm;
\r
9611 *pr = (ProcRef *) cp;
\r
9617 OpenLoopback(ProcRef *pr)
\r
9619 DisplayFatalError(_("Not implemented"), 0, 1);
\r
9625 OpenRcmd(char* host, char* user, char* cmd, ProcRef* pr)
\r
9630 struct sockaddr_in sa, mysa;
\r
9631 struct hostent FAR *hp;
\r
9632 unsigned short uport;
\r
9633 WORD wVersionRequested;
\r
9636 char stderrPortStr[MSG_SIZ];
\r
9638 /* Initialize socket DLL */
\r
9639 wVersionRequested = MAKEWORD(1, 1);
\r
9640 err = WSAStartup(wVersionRequested, &wsaData);
\r
9641 if (err != 0) return err;
\r
9643 /* Resolve remote host name */
\r
9644 memset((char *) &sa, 0, sizeof(struct sockaddr_in));
\r
9645 if (!(hp = gethostbyname(host))) {
\r
9646 unsigned int b0, b1, b2, b3;
\r
9648 err = WSAGetLastError();
\r
9650 if (sscanf(host, "%u.%u.%u.%u", &b0, &b1, &b2, &b3) == 4) {
\r
9651 hp = (struct hostent *) calloc(1, sizeof(struct hostent));
\r
9652 hp->h_addrtype = AF_INET;
\r
9654 hp->h_addr_list = (char **) calloc(2, sizeof(char *));
\r
9655 hp->h_addr_list[0] = (char *) malloc(4);
\r
9656 hp->h_addr_list[0][0] = (char) b0;
\r
9657 hp->h_addr_list[0][1] = (char) b1;
\r
9658 hp->h_addr_list[0][2] = (char) b2;
\r
9659 hp->h_addr_list[0][3] = (char) b3;
\r
9665 sa.sin_family = hp->h_addrtype;
\r
9666 uport = (unsigned short) 514;
\r
9667 sa.sin_port = htons(uport);
\r
9668 memcpy((char *) &sa.sin_addr, hp->h_addr, hp->h_length);
\r
9670 /* Bind local socket to unused "privileged" port address
\r
9672 s = INVALID_SOCKET;
\r
9673 memset((char *) &mysa, 0, sizeof(struct sockaddr_in));
\r
9674 mysa.sin_family = AF_INET;
\r
9675 mysa.sin_addr.s_addr = INADDR_ANY;
\r
9676 for (fromPort = 1023;; fromPort--) {
\r
9677 if (fromPort < 0) {
\r
9679 return WSAEADDRINUSE;
\r
9681 if (s == INVALID_SOCKET) {
\r
9682 if ((s = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {
\r
9683 err = WSAGetLastError();
\r
9688 uport = (unsigned short) fromPort;
\r
9689 mysa.sin_port = htons(uport);
\r
9690 if (bind(s, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))
\r
9691 == SOCKET_ERROR) {
\r
9692 err = WSAGetLastError();
\r
9693 if (err == WSAEADDRINUSE) continue;
\r
9697 if (connect(s, (struct sockaddr *) &sa,
\r
9698 sizeof(struct sockaddr_in)) == SOCKET_ERROR) {
\r
9699 err = WSAGetLastError();
\r
9700 if (err == WSAEADDRINUSE) {
\r
9711 /* Bind stderr local socket to unused "privileged" port address
\r
9713 s2 = INVALID_SOCKET;
\r
9714 memset((char *) &mysa, 0, sizeof(struct sockaddr_in));
\r
9715 mysa.sin_family = AF_INET;
\r
9716 mysa.sin_addr.s_addr = INADDR_ANY;
\r
9717 for (fromPort = 1023;; fromPort--) {
\r
9718 if (fromPort == prevStderrPort) continue; // don't reuse port
\r
9719 if (fromPort < 0) {
\r
9720 (void) closesocket(s);
\r
9722 return WSAEADDRINUSE;
\r
9724 if (s2 == INVALID_SOCKET) {
\r
9725 if ((s2 = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {
\r
9726 err = WSAGetLastError();
\r
9732 uport = (unsigned short) fromPort;
\r
9733 mysa.sin_port = htons(uport);
\r
9734 if (bind(s2, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))
\r
9735 == SOCKET_ERROR) {
\r
9736 err = WSAGetLastError();
\r
9737 if (err == WSAEADDRINUSE) continue;
\r
9738 (void) closesocket(s);
\r
9742 if (listen(s2, 1) == SOCKET_ERROR) {
\r
9743 err = WSAGetLastError();
\r
9744 if (err == WSAEADDRINUSE) {
\r
9746 s2 = INVALID_SOCKET;
\r
9749 (void) closesocket(s);
\r
9750 (void) closesocket(s2);
\r
9756 prevStderrPort = fromPort; // remember port used
\r
9757 snprintf(stderrPortStr, MSG_SIZ, "%d", fromPort);
\r
9759 if (send(s, stderrPortStr, strlen(stderrPortStr) + 1, 0) == SOCKET_ERROR) {
\r
9760 err = WSAGetLastError();
\r
9761 (void) closesocket(s);
\r
9762 (void) closesocket(s2);
\r
9767 if (send(s, UserName(), strlen(UserName()) + 1, 0) == SOCKET_ERROR) {
\r
9768 err = WSAGetLastError();
\r
9769 (void) closesocket(s);
\r
9770 (void) closesocket(s2);
\r
9774 if (*user == NULLCHAR) user = UserName();
\r
9775 if (send(s, user, strlen(user) + 1, 0) == SOCKET_ERROR) {
\r
9776 err = WSAGetLastError();
\r
9777 (void) closesocket(s);
\r
9778 (void) closesocket(s2);
\r
9782 if (send(s, cmd, strlen(cmd) + 1, 0) == SOCKET_ERROR) {
\r
9783 err = WSAGetLastError();
\r
9784 (void) closesocket(s);
\r
9785 (void) closesocket(s2);
\r
9790 if ((s3 = accept(s2, NULL, NULL)) == INVALID_SOCKET) {
\r
9791 err = WSAGetLastError();
\r
9792 (void) closesocket(s);
\r
9793 (void) closesocket(s2);
\r
9797 (void) closesocket(s2); /* Stop listening */
\r
9799 /* Prepare return value */
\r
9800 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
\r
9801 cp->kind = CPRcmd;
\r
9804 *pr = (ProcRef *) cp;
\r
9811 AddInputSource(ProcRef pr, int lineByLine,
\r
9812 InputCallback func, VOIDSTAR closure)
\r
9814 InputSource *is, *is2 = NULL;
\r
9815 ChildProc *cp = (ChildProc *) pr;
\r
9817 is = (InputSource *) calloc(1, sizeof(InputSource));
\r
9818 is->lineByLine = lineByLine;
\r
9820 is->closure = closure;
\r
9821 is->second = NULL;
\r
9822 is->next = is->buf;
\r
9823 if (pr == NoProc) {
\r
9824 is->kind = CPReal;
\r
9825 consoleInputSource = is;
\r
9827 is->kind = cp->kind;
\r
9829 [AS] Try to avoid a race condition if the thread is given control too early:
\r
9830 we create all threads suspended so that the is->hThread variable can be
\r
9831 safely assigned, then let the threads start with ResumeThread.
\r
9833 switch (cp->kind) {
\r
9835 is->hFile = cp->hFrom;
\r
9836 cp->hFrom = NULL; /* now owned by InputThread */
\r
9838 CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) NonOvlInputThread,
\r
9839 (LPVOID) is, CREATE_SUSPENDED, &is->id);
\r
9843 is->hFile = cp->hFrom;
\r
9844 cp->hFrom = NULL; /* now owned by InputThread */
\r
9846 CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) InputThread,
\r
9847 (LPVOID) is, CREATE_SUSPENDED, &is->id);
\r
9851 is->sock = cp->sock;
\r
9853 CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,
\r
9854 (LPVOID) is, CREATE_SUSPENDED, &is->id);
\r
9858 is2 = (InputSource *) calloc(1, sizeof(InputSource));
\r
9860 is->sock = cp->sock;
\r
9862 is2->sock = cp->sock2;
\r
9863 is2->second = is2;
\r
9865 CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,
\r
9866 (LPVOID) is, CREATE_SUSPENDED, &is->id);
\r
9868 CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,
\r
9869 (LPVOID) is2, CREATE_SUSPENDED, &is2->id);
\r
9873 if( is->hThread != NULL ) {
\r
9874 ResumeThread( is->hThread );
\r
9877 if( is2 != NULL && is2->hThread != NULL ) {
\r
9878 ResumeThread( is2->hThread );
\r
9882 return (InputSourceRef) is;
\r
9886 RemoveInputSource(InputSourceRef isr)
\r
9890 is = (InputSource *) isr;
\r
9891 is->hThread = NULL; /* tell thread to stop */
\r
9892 CloseHandle(is->hThread);
\r
9893 if (is->second != NULL) {
\r
9894 is->second->hThread = NULL;
\r
9895 CloseHandle(is->second->hThread);
\r
9899 int no_wrap(char *message, int count)
\r
9901 ConsoleOutput(message, count, FALSE);
\r
9906 OutputToProcess(ProcRef pr, char *message, int count, int *outError)
\r
9909 int outCount = SOCKET_ERROR;
\r
9910 ChildProc *cp = (ChildProc *) pr;
\r
9911 static OVERLAPPED ovl;
\r
9913 static int line = 0;
\r
9917 if (appData.noJoin || !appData.useInternalWrap)
\r
9918 return no_wrap(message, count);
\r
9921 int width = get_term_width();
\r
9922 int len = wrap(NULL, message, count, width, &line);
\r
9923 char *msg = malloc(len);
\r
9927 return no_wrap(message, count);
\r
9930 dbgchk = wrap(msg, message, count, width, &line);
\r
9931 if (dbgchk != len && appData.debugMode)
\r
9932 fprintf(debugFP, "wrap(): dbgchk(%d) != len(%d)\n", dbgchk, len);
\r
9933 ConsoleOutput(msg, len, FALSE);
\r
9940 if (ovl.hEvent == NULL) {
\r
9941 ovl.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
\r
9943 ovl.Internal = ovl.InternalHigh = ovl.Offset = ovl.OffsetHigh = 0;
\r
9945 switch (cp->kind) {
\r
9948 outCount = send(cp->sock, message, count, 0);
\r
9949 if (outCount == SOCKET_ERROR) {
\r
9950 *outError = WSAGetLastError();
\r
9952 *outError = NO_ERROR;
\r
9957 if (WriteFile(((ChildProc *)pr)->hTo, message, count,
\r
9958 &dOutCount, NULL)) {
\r
9959 *outError = NO_ERROR;
\r
9960 outCount = (int) dOutCount;
\r
9962 *outError = GetLastError();
\r
9967 *outError = DoWriteFile(((ChildProc *)pr)->hTo, message, count,
\r
9968 &dOutCount, &ovl);
\r
9969 if (*outError == NO_ERROR) {
\r
9970 outCount = (int) dOutCount;
\r
9980 if(n != 0) Sleep(n);
\r
9984 OutputToProcessDelayed(ProcRef pr, char *message, int count, int *outError,
\r
9987 /* Ignore delay, not implemented for WinBoard */
\r
9988 return OutputToProcess(pr, message, count, outError);
\r
9993 CmailSigHandlerCallBack(InputSourceRef isr, VOIDSTAR closure,
\r
9994 char *buf, int count, int error)
\r
9996 DisplayFatalError(_("Not implemented"), 0, 1);
\r
9999 /* see wgamelist.c for Game List functions */
\r
10000 /* see wedittags.c for Edit Tags functions */
\r
10007 char buf[MSG_SIZ];
\r
10010 if (SearchPath(installDir, appData.icsLogon, NULL, MSG_SIZ, buf, &dummy)) {
\r
10011 f = fopen(buf, "r");
\r
10013 ProcessICSInitScript(f);
\r
10023 StartAnalysisClock()
\r
10025 if (analysisTimerEvent) return;
\r
10026 analysisTimerEvent = SetTimer(hwndMain, (UINT) ANALYSIS_TIMER_ID,
\r
10027 (UINT) 2000, NULL);
\r
10031 SetHighlights(int fromX, int fromY, int toX, int toY)
\r
10033 highlightInfo.sq[0].x = fromX;
\r
10034 highlightInfo.sq[0].y = fromY;
\r
10035 highlightInfo.sq[1].x = toX;
\r
10036 highlightInfo.sq[1].y = toY;
\r
10040 ClearHighlights()
\r
10042 highlightInfo.sq[0].x = highlightInfo.sq[0].y =
\r
10043 highlightInfo.sq[1].x = highlightInfo.sq[1].y = -1;
\r
10047 SetPremoveHighlights(int fromX, int fromY, int toX, int toY)
\r
10049 premoveHighlightInfo.sq[0].x = fromX;
\r
10050 premoveHighlightInfo.sq[0].y = fromY;
\r
10051 premoveHighlightInfo.sq[1].x = toX;
\r
10052 premoveHighlightInfo.sq[1].y = toY;
\r
10056 ClearPremoveHighlights()
\r
10058 premoveHighlightInfo.sq[0].x = premoveHighlightInfo.sq[0].y =
\r
10059 premoveHighlightInfo.sq[1].x = premoveHighlightInfo.sq[1].y = -1;
\r
10063 ShutDownFrontEnd()
\r
10065 if (saveSettingsOnExit) SaveSettings(settingsFileName);
\r
10066 DeleteClipboardTempFiles();
\r
10072 if (IsIconic(hwndMain))
\r
10073 ShowWindow(hwndMain, SW_RESTORE);
\r
10075 SetActiveWindow(hwndMain);
\r
10079 * Prototypes for animation support routines
\r
10081 static void ScreenSquare(int column, int row, POINT * pt);
\r
10082 static void Tween( POINT * start, POINT * mid, POINT * finish, int factor,
\r
10083 POINT frames[], int * nFrames);
\r
10086 #define kFactor 4
\r
10089 AnimateAtomicCapture(Board board, int fromX, int fromY, int toX, int toY)
\r
10090 { // [HGM] atomic: animate blast wave
\r
10093 explodeInfo.fromX = fromX;
\r
10094 explodeInfo.fromY = fromY;
\r
10095 explodeInfo.toX = toX;
\r
10096 explodeInfo.toY = toY;
\r
10097 for(i=1; i<4*kFactor; i++) {
\r
10098 explodeInfo.radius = (i*180)/(4*kFactor-1);
\r
10099 DrawPosition(FALSE, board);
\r
10100 Sleep(appData.animSpeed);
\r
10102 explodeInfo.radius = 0;
\r
10103 DrawPosition(TRUE, board);
\r
10107 AnimateMove(board, fromX, fromY, toX, toY)
\r
10114 ChessSquare piece, victim = EmptySquare, victim2 = EmptySquare;
\r
10115 int x = toX, y = toY, x2 = kill2X;
\r
10116 POINT start, finish, mid;
\r
10117 POINT frames[kFactor * 2 + 1];
\r
10120 if(killX >= 0 && IS_LION(board[fromY][fromX])) Roar();
\r
10122 if (!appData.animate) return;
\r
10123 if (doingSizing) return;
\r
10124 if (fromY < 0 || fromX < 0) return;
\r
10125 piece = board[fromY][fromX];
\r
10126 if (piece >= EmptySquare) return;
\r
10128 if(x2 >= 0) toX = kill2X, toY = kill2Y, victim = board[killY][killX], victim2 = board[kill2Y][kill2X]; else
\r
10129 if(killX >= 0) toX = killX, toY = killY, victim = board[killY][killX]; // [HGM] lion: first to kill square
\r
10131 animInfo.from.x = fromX;
\r
10132 animInfo.from.y = fromY;
\r
10136 ScreenSquare(fromX, fromY, &start);
\r
10137 ScreenSquare(toX, toY, &finish);
\r
10139 /* All moves except knight jumps move in straight line */
\r
10140 if (!(abs(fromX-toX) == 1 && abs(fromY-toY) == 2 || abs(fromX-toX) == 2 && abs(fromY-toY) == 1)) {
\r
10141 mid.x = start.x + (finish.x - start.x) / 2;
\r
10142 mid.y = start.y + (finish.y - start.y) / 2;
\r
10144 /* Knight: make straight movement then diagonal */
\r
10145 if (abs(toY - fromY) < abs(toX - fromX)) {
\r
10146 mid.x = start.x + (finish.x - start.x) / 2;
\r
10150 mid.y = start.y + (finish.y - start.y) / 2;
\r
10154 /* Don't use as many frames for very short moves */
\r
10155 if (abs(toY - fromY) + abs(toX - fromX) <= 2)
\r
10156 Tween(&start, &mid, &finish, kFactor - 1, frames, &nFrames);
\r
10158 Tween(&start, &mid, &finish, kFactor, frames, &nFrames);
\r
10160 animInfo.to.x = toX;
\r
10161 animInfo.to.y = toY;
\r
10162 animInfo.lastpos = start;
\r
10163 animInfo.piece = piece;
\r
10164 for (n = 0; n < nFrames; n++) {
\r
10165 animInfo.pos = frames[n];
\r
10166 DrawPosition(FALSE, board);
\r
10167 animInfo.lastpos = animInfo.pos;
\r
10168 Sleep(appData.animSpeed);
\r
10170 animInfo.pos = finish;
\r
10171 DrawPosition(FALSE, board);
\r
10173 if(toX == x2 && toY == kill2Y) {
\r
10174 fromX = toX; fromY = toY; toX = killX; toY = killY; x2 = -1;
\r
10175 board[kill2Y][kill2X] = EmptySquare; goto again;
\r
10177 if(toX != x || toY != y) {
\r
10178 fromX = toX; fromY = toY; toX = x; toY = y;
\r
10179 board[killY][killX] = EmptySquare; goto again;
\r
10182 if(victim2 != EmptySquare) board[kill2Y][kill2X] = victim2;
\r
10183 if(victim != EmptySquare) board[killY][killX] = victim;
\r
10185 animInfo.piece = EmptySquare;
\r
10186 Explode(board, fromX, fromY, toX, toY);
\r
10189 /* Convert board position to corner of screen rect and color */
\r
10192 ScreenSquare(column, row, pt)
\r
10193 int column; int row; POINT * pt;
\r
10196 pt->x = lineGap + ((BOARD_WIDTH-1)-column) * (squareSize + lineGap) + border;
\r
10197 pt->y = lineGap + row * (squareSize + lineGap) + border;
\r
10199 pt->x = lineGap + column * (squareSize + lineGap) + border;
\r
10200 pt->y = lineGap + ((BOARD_HEIGHT-1)-row) * (squareSize + lineGap) + border;
\r
10204 /* Generate a series of frame coords from start->mid->finish.
\r
10205 The movement rate doubles until the half way point is
\r
10206 reached, then halves back down to the final destination,
\r
10207 which gives a nice slow in/out effect. The algorithmn
\r
10208 may seem to generate too many intermediates for short
\r
10209 moves, but remember that the purpose is to attract the
\r
10210 viewers attention to the piece about to be moved and
\r
10211 then to where it ends up. Too few frames would be less
\r
10215 Tween(start, mid, finish, factor, frames, nFrames)
\r
10216 POINT * start; POINT * mid;
\r
10217 POINT * finish; int factor;
\r
10218 POINT frames[]; int * nFrames;
\r
10220 int n, fraction = 1, count = 0;
\r
10222 /* Slow in, stepping 1/16th, then 1/8th, ... */
\r
10223 for (n = 0; n < factor; n++)
\r
10225 for (n = 0; n < factor; n++) {
\r
10226 frames[count].x = start->x + (mid->x - start->x) / fraction;
\r
10227 frames[count].y = start->y + (mid->y - start->y) / fraction;
\r
10229 fraction = fraction / 2;
\r
10233 frames[count] = *mid;
\r
10236 /* Slow out, stepping 1/2, then 1/4, ... */
\r
10238 for (n = 0; n < factor; n++) {
\r
10239 frames[count].x = finish->x - (finish->x - mid->x) / fraction;
\r
10240 frames[count].y = finish->y - (finish->y - mid->y) / fraction;
\r
10242 fraction = fraction * 2;
\r
10244 *nFrames = count;
\r
10248 SettingsPopUp(ChessProgramState *cps)
\r
10249 { // [HGM] wrapper needed because handles must not be passed through back-end
\r
10250 EngineOptionsPopup(savedHwnd, cps);
\r
10253 int flock(int fid, int code)
\r
10255 HANDLE hFile = (HANDLE) _get_osfhandle(fid);
\r
10257 ov.hEvent = NULL;
\r
10259 ov.OffsetHigh = 0;
\r
10261 case 1: LockFileEx(hFile, LOCKFILE_EXCLUSIVE_LOCK, 0, 1024, 0, &ov); break; // LOCK_SH
\r
10263 case 2: LockFileEx(hFile, LOCKFILE_EXCLUSIVE_LOCK, 0, 1024, 0, &ov); break; // LOCK_EX
\r
10264 case 3: UnlockFileEx(hFile, 0, 1024, 0, &ov); break; // LOCK_UN
\r
10265 default: return -1;
\r
10274 static char col[8][20];
\r
10275 COLORREF color = *(COLORREF *) colorVariable[n];
\r
10277 snprintf(col[i], 20, "#%02lx%02lx%02lx", color&0xff, (color>>8)&0xff, (color>>16)&0xff);
\r
10282 ActivateTheme (int new)
\r
10283 { // Redo initialization of features depending on options that can occur in themes
\r
10285 if(new) InitDrawingColors();
\r
10286 fontBitmapSquareSize = 0; // request creation of new font pieces
\r
10287 InitDrawingSizes(boardSize, 0);
\r
10288 InvalidateRect(hwndMain, NULL, TRUE);
\r