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 currentEngine[0] = 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 currentEngine[1] = strdup(p+5); // [HGM] also remember engine line of 2nd for saving its settings
\r
6460 SwapEngines(singleList); // temporarily swap first and second, to load a second 'first', ...
\r
6461 ParseArgs(StringGet, &p);
\r
6462 SwapEngines(singleList); // ... and then make it 'second'
\r
6464 appData.noChessProgram = FALSE;
\r
6465 appData.icsActive = FALSE;
\r
6466 } else if (IsDlgButtonChecked(hDlg, OPT_ChessServer)) {
\r
6467 safeStrCpy(buf, "/ics /icshost=", sizeof(buf)/sizeof(buf[0]) );
\r
6468 GetDlgItemText(hDlg, OPT_ChessServerName, buf + strlen(buf), sizeof(buf) - strlen(buf));
\r
6470 ParseArgs(StringGet, &p);
\r
6471 if (appData.zippyPlay) {
\r
6472 safeStrCpy(buf, "/fcp=", sizeof(buf)/sizeof(buf[0]) );
\r
6473 GetDlgItemText(hDlg, OPT_ChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));
\r
6475 ParseArgs(StringGet, &p);
\r
6477 } else if (IsDlgButtonChecked(hDlg, OPT_View)) {
\r
6478 appData.noChessProgram = TRUE;
\r
6479 appData.icsActive = FALSE;
\r
6481 MessageBox(hDlg, _("Choose an option, or cancel to exit"),
\r
6482 _("Option Error"), MB_OK|MB_ICONEXCLAMATION);
\r
6485 if (IsDlgButtonChecked(hDlg, OPT_AnyAdditional)) {
\r
6486 GetDlgItemText(hDlg, OPT_AdditionalOptions, buf, sizeof(buf));
\r
6488 ParseArgs(StringGet, &p);
\r
6490 EndDialog(hDlg, TRUE);
\r
6497 case IDM_HELPCONTENTS:
\r
6498 if (!WinHelp (hDlg, "winboard.hlp", HELP_KEY,(DWORD)(LPSTR)"CONTENTS")) {
\r
6499 MessageBox (GetFocus(),
\r
6500 _("Unable to activate help"),
\r
6501 szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);
\r
6506 SetStartupDialogEnables(hDlg);
\r
6514 /*---------------------------------------------------------------------------*\
\r
6516 * About box dialog functions
\r
6518 \*---------------------------------------------------------------------------*/
\r
6520 /* Process messages for "About" dialog box */
\r
6522 About(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
6524 switch (message) {
\r
6525 case WM_INITDIALOG: /* message: initialize dialog box */
\r
6526 /* Center the dialog over the application window */
\r
6527 CenterWindow (hDlg, GetWindow (hDlg, GW_OWNER));
\r
6528 SetDlgItemText(hDlg, ABOUTBOX_Version, programVersion);
\r
6529 Translate(hDlg, ABOUTBOX);
\r
6533 case WM_COMMAND: /* message: received a command */
\r
6534 if (LOWORD(wParam) == IDOK /* "OK" box selected? */
\r
6535 || LOWORD(wParam) == IDCANCEL) { /* System menu close command? */
\r
6536 EndDialog(hDlg, TRUE); /* Exit the dialog */
\r
6544 /*---------------------------------------------------------------------------*\
\r
6546 * Comment Dialog functions
\r
6548 \*---------------------------------------------------------------------------*/
\r
6551 CommentDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
6553 static HANDLE hwndText = NULL;
\r
6554 int len, newSizeX, newSizeY;
\r
6555 static int sizeX, sizeY;
\r
6560 switch (message) {
\r
6561 case WM_INITDIALOG: /* message: initialize dialog box */
\r
6562 /* Initialize the dialog items */
\r
6563 Translate(hDlg, DLG_EditComment);
\r
6564 hwndText = GetDlgItem(hDlg, OPT_CommentText);
\r
6565 SetDlgItemText(hDlg, OPT_CommentText, commentText);
\r
6566 EnableWindow(GetDlgItem(hDlg, OPT_CancelComment), editComment);
\r
6567 EnableWindow(GetDlgItem(hDlg, OPT_ClearComment), editComment);
\r
6568 EnableWindow(GetDlgItem(hDlg, OPT_EditComment), !editComment);
\r
6569 SendMessage(hwndText, EM_SETREADONLY, !editComment, 0);
\r
6570 SetWindowText(hDlg, commentTitle);
\r
6571 if (editComment) {
\r
6572 SetFocus(hwndText);
\r
6574 SetFocus(GetDlgItem(hDlg, IDOK));
\r
6576 SendMessage(GetDlgItem(hDlg, OPT_CommentText),
\r
6577 WM_SETFONT, (WPARAM)font[boardSize][COMMENT_FONT]->hf,
\r
6578 MAKELPARAM(FALSE, 0));
\r
6579 /* Size and position the dialog */
\r
6580 if (!commentDialog) {
\r
6581 commentDialog = hDlg;
\r
6582 GetClientRect(hDlg, &rect);
\r
6583 sizeX = rect.right;
\r
6584 sizeY = rect.bottom;
\r
6585 if (wpComment.x != CW_USEDEFAULT && wpComment.y != CW_USEDEFAULT &&
\r
6586 wpComment.width != CW_USEDEFAULT && wpComment.height != CW_USEDEFAULT) {
\r
6587 WINDOWPLACEMENT wp;
\r
6588 EnsureOnScreen(&wpComment.x, &wpComment.y, 0, 0);
\r
6589 wp.length = sizeof(WINDOWPLACEMENT);
\r
6591 wp.showCmd = SW_SHOW;
\r
6592 wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;
\r
6593 wp.rcNormalPosition.left = wpComment.x;
\r
6594 wp.rcNormalPosition.right = wpComment.x + wpComment.width;
\r
6595 wp.rcNormalPosition.top = wpComment.y;
\r
6596 wp.rcNormalPosition.bottom = wpComment.y + wpComment.height;
\r
6597 SetWindowPlacement(hDlg, &wp);
\r
6599 GetClientRect(hDlg, &rect);
\r
6600 newSizeX = rect.right;
\r
6601 newSizeY = rect.bottom;
\r
6602 ResizeEditPlusButtons(hDlg, hwndText, sizeX, sizeY,
\r
6603 newSizeX, newSizeY);
\r
6608 SendDlgItemMessage( hDlg, OPT_CommentText, EM_SETEVENTMASK, 0, ENM_MOUSEEVENTS | ENM_KEYEVENTS );
\r
6611 case WM_COMMAND: /* message: received a command */
\r
6612 switch (LOWORD(wParam)) {
\r
6614 if (editComment) {
\r
6616 /* Read changed options from the dialog box */
\r
6617 hwndText = GetDlgItem(hDlg, OPT_CommentText);
\r
6618 len = GetWindowTextLength(hwndText);
\r
6619 str = (char *) malloc(len + 1);
\r
6620 GetWindowText(hwndText, str, len + 1);
\r
6629 ReplaceComment(commentIndex, str);
\r
6636 case OPT_CancelComment:
\r
6640 case OPT_ClearComment:
\r
6641 SetDlgItemText(hDlg, OPT_CommentText, "");
\r
6644 case OPT_EditComment:
\r
6645 EditCommentEvent();
\r
6653 case WM_NOTIFY: // [HGM] vari: cloned from whistory.c
\r
6654 if( wParam == OPT_CommentText ) {
\r
6655 MSGFILTER * lpMF = (MSGFILTER *) lParam;
\r
6657 if( lpMF->msg == WM_RBUTTONDOWN && (lpMF->wParam & (MK_CONTROL | MK_SHIFT)) == 0 ||
\r
6658 lpMF->msg == WM_CHAR && lpMF->wParam == '\022' ) {
\r
6662 pt.x = LOWORD( lpMF->lParam );
\r
6663 pt.y = HIWORD( lpMF->lParam );
\r
6665 if(lpMF->msg == WM_CHAR) {
\r
6667 SendDlgItemMessage( hDlg, OPT_CommentText, EM_EXGETSEL, 0, (LPARAM) &sel );
\r
6668 index = sel.cpMin;
\r
6670 index = SendDlgItemMessage( hDlg, OPT_CommentText, EM_CHARFROMPOS, 0, (LPARAM) &pt );
\r
6672 hwndText = GetDlgItem(hDlg, OPT_CommentText); // cloned from above
\r
6673 len = GetWindowTextLength(hwndText);
\r
6674 str = (char *) malloc(len + 1);
\r
6675 GetWindowText(hwndText, str, len + 1);
\r
6676 ReplaceComment(commentIndex, str);
\r
6677 if(commentIndex != currentMove) ToNrEvent(commentIndex);
\r
6678 LoadVariation( index, str ); // [HGM] also does the actual moving to it, now
\r
6681 /* Zap the message for good: apparently, returning non-zero is not enough */
\r
6682 lpMF->msg = WM_USER;
\r
6690 newSizeX = LOWORD(lParam);
\r
6691 newSizeY = HIWORD(lParam);
\r
6692 ResizeEditPlusButtons(hDlg, hwndText, sizeX, sizeY, newSizeX, newSizeY);
\r
6697 case WM_GETMINMAXINFO:
\r
6698 /* Prevent resizing window too small */
\r
6699 mmi = (MINMAXINFO *) lParam;
\r
6700 mmi->ptMinTrackSize.x = 100;
\r
6701 mmi->ptMinTrackSize.y = 100;
\r
6708 EitherCommentPopUp(int index, char *title, char *str, BOOLEAN edit)
\r
6713 CheckMenuItem(GetMenu(hwndMain), IDM_Comment, edit ? MF_CHECKED : MF_UNCHECKED);
\r
6715 if (str == NULL) str = "";
\r
6716 p = (char *) malloc(2 * strlen(str) + 2);
\r
6719 if (*str == '\n') *q++ = '\r';
\r
6723 if (commentText != NULL) free(commentText);
\r
6725 commentIndex = index;
\r
6726 commentTitle = title;
\r
6728 editComment = edit;
\r
6730 if (commentDialog) {
\r
6731 SendMessage(commentDialog, WM_INITDIALOG, 0, 0);
\r
6732 if (!commentUp) ShowWindow(commentDialog, SW_SHOW);
\r
6734 lpProc = MakeProcInstance((FARPROC)CommentDialog, hInst);
\r
6735 CreateDialog(hInst, MAKEINTRESOURCE(DLG_EditComment),
\r
6736 hwndMain, (DLGPROC)lpProc);
\r
6737 FreeProcInstance(lpProc);
\r
6743 /*---------------------------------------------------------------------------*\
\r
6745 * Type-in move dialog functions
\r
6747 \*---------------------------------------------------------------------------*/
\r
6750 TypeInMoveDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
6752 char move[MSG_SIZ];
\r
6755 switch (message) {
\r
6756 case WM_INITDIALOG:
\r
6757 move[0] = (char) lParam;
\r
6758 move[1] = NULLCHAR;
\r
6759 CenterWindowEx(hDlg, GetWindow(hDlg, GW_OWNER), 1 );
\r
6760 Translate(hDlg, DLG_TypeInMove);
\r
6761 hInput = GetDlgItem(hDlg, OPT_Move);
\r
6762 SetWindowText(hInput, move);
\r
6764 SendMessage(hInput, EM_SETSEL, (WPARAM)9999, (LPARAM)9999);
\r
6768 switch (LOWORD(wParam)) {
\r
6771 shiftKey = GetKeyState(VK_SHIFT) < 0; // [HGM] remember last shift status
\r
6772 GetDlgItemText(hDlg, OPT_Move, move, sizeof(move));
\r
6773 TypeInDoneEvent(move);
\r
6774 EndDialog(hDlg, TRUE);
\r
6777 EndDialog(hDlg, FALSE);
\r
6788 PopUpMoveDialog(char firstchar)
\r
6792 lpProc = MakeProcInstance((FARPROC)TypeInMoveDialog, hInst);
\r
6793 DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_TypeInMove),
\r
6794 hwndMain, (DLGPROC)lpProc, (LPARAM)firstchar);
\r
6795 FreeProcInstance(lpProc);
\r
6798 /*---------------------------------------------------------------------------*\
\r
6800 * Type-in name dialog functions
\r
6802 \*---------------------------------------------------------------------------*/
\r
6805 TypeInNameDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
6807 char move[MSG_SIZ];
\r
6810 switch (message) {
\r
6811 case WM_INITDIALOG:
\r
6812 move[0] = (char) lParam;
\r
6813 move[1] = NULLCHAR;
\r
6814 CenterWindowEx(hDlg, GetWindow(hDlg, GW_OWNER), 1 );
\r
6815 Translate(hDlg, DLG_TypeInName);
\r
6816 hInput = GetDlgItem(hDlg, OPT_Name);
\r
6817 SetWindowText(hInput, move);
\r
6819 SendMessage(hInput, EM_SETSEL, (WPARAM)9999, (LPARAM)9999);
\r
6823 switch (LOWORD(wParam)) {
\r
6825 GetDlgItemText(hDlg, OPT_Name, move, sizeof(move));
\r
6826 appData.userName = strdup(move);
\r
6827 SetUserLogo(); DisplayLogos();
\r
6829 if(gameMode == MachinePlaysWhite || gameMode == MachinePlaysBlack) {
\r
6830 snprintf(move, MSG_SIZ, "%s vs. %s", gameInfo.white, gameInfo.black);
\r
6831 DisplayTitle(move);
\r
6835 EndDialog(hDlg, TRUE);
\r
6838 EndDialog(hDlg, FALSE);
\r
6849 PopUpNameDialog(char firstchar)
\r
6853 lpProc = MakeProcInstance((FARPROC)TypeInNameDialog, hInst);
\r
6854 DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_TypeInName),
\r
6855 hwndMain, (DLGPROC)lpProc, (LPARAM)firstchar);
\r
6856 FreeProcInstance(lpProc);
\r
6859 /*---------------------------------------------------------------------------*\
\r
6863 \*---------------------------------------------------------------------------*/
\r
6865 /* Nonmodal error box */
\r
6866 LRESULT CALLBACK ErrorDialog(HWND hDlg, UINT message,
\r
6867 WPARAM wParam, LPARAM lParam);
\r
6870 ErrorPopUp(char *title, char *content)
\r
6874 BOOLEAN modal = hwndMain == NULL;
\r
6892 strncpy(errorTitle, title, sizeof(errorTitle));
\r
6893 errorTitle[sizeof(errorTitle) - 1] = '\0';
\r
6896 MessageBox(NULL, errorMessage, errorTitle, MB_OK|MB_ICONEXCLAMATION);
\r
6898 lpProc = MakeProcInstance((FARPROC)ErrorDialog, hInst);
\r
6899 CreateDialog(hInst, MAKEINTRESOURCE(DLG_Error),
\r
6900 hwndMain, (DLGPROC)lpProc);
\r
6901 FreeProcInstance(lpProc);
\r
6908 if (!appData.popupMoveErrors && moveErrorMessageUp) DisplayMessage("", "");
\r
6909 if (errorDialog == NULL) return;
\r
6910 DestroyWindow(errorDialog);
\r
6911 errorDialog = NULL;
\r
6912 if(errorExitStatus) ExitEvent(errorExitStatus);
\r
6916 ErrorDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
6920 switch (message) {
\r
6921 case WM_INITDIALOG:
\r
6922 GetWindowRect(hDlg, &rChild);
\r
6925 SetWindowPos(hDlg, NULL, rChild.left,
\r
6926 rChild.top + boardRect.top - (rChild.bottom - rChild.top),
\r
6927 0, 0, SWP_NOZORDER|SWP_NOSIZE);
\r
6931 [AS] It seems that the above code wants to move the dialog up in the "caption
\r
6932 area" of the main window, but it uses the dialog height as an hard-coded constant,
\r
6933 and it doesn't work when you resize the dialog.
\r
6934 For now, just give it a default position.
\r
6936 SetWindowPos(hDlg, NULL, boardRect.left+8, boardRect.top+8, 0, 0, SWP_NOZORDER|SWP_NOSIZE);
\r
6937 Translate(hDlg, DLG_Error);
\r
6939 errorDialog = hDlg;
\r
6940 SetWindowText(hDlg, errorTitle);
\r
6941 SetDlgItemText(hDlg, OPT_ErrorText, errorMessage);
\r
6945 switch (LOWORD(wParam)) {
\r
6948 if (errorDialog == hDlg) errorDialog = NULL;
\r
6949 DestroyWindow(hDlg);
\r
6961 HWND gothicDialog = NULL;
\r
6964 GothicDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
6967 int height = GetSystemMetrics(SM_CYCAPTION)+GetSystemMetrics(SM_CYFRAME);
\r
6969 switch (message) {
\r
6970 case WM_INITDIALOG:
\r
6971 GetWindowRect(hDlg, &rChild);
\r
6973 SetWindowPos(hDlg, NULL, wpMain.x, wpMain.y-height, wpMain.width, height,
\r
6977 [AS] It seems that the above code wants to move the dialog up in the "caption
\r
6978 area" of the main window, but it uses the dialog height as an hard-coded constant,
\r
6979 and it doesn't work when you resize the dialog.
\r
6980 For now, just give it a default position.
\r
6982 gothicDialog = hDlg;
\r
6983 SetWindowText(hDlg, errorTitle);
\r
6984 SetDlgItemText(hDlg, OPT_ErrorText, errorMessage);
\r
6988 switch (LOWORD(wParam)) {
\r
6991 if (errorDialog == hDlg) errorDialog = NULL;
\r
6992 DestroyWindow(hDlg);
\r
7004 GothicPopUp(char *title, VariantClass variant)
\r
7007 static char *lastTitle;
\r
7009 strncpy(errorTitle, title, sizeof(errorTitle));
\r
7010 errorTitle[sizeof(errorTitle) - 1] = '\0';
\r
7012 if(lastTitle != title && gothicDialog != NULL) {
\r
7013 DestroyWindow(gothicDialog);
\r
7014 gothicDialog = NULL;
\r
7016 if(variant != VariantNormal && gothicDialog == NULL) {
\r
7017 title = lastTitle;
\r
7018 lpProc = MakeProcInstance((FARPROC)GothicDialog, hInst);
\r
7019 CreateDialog(hInst, MAKEINTRESOURCE(DLG_Error),
\r
7020 hwndMain, (DLGPROC)lpProc);
\r
7021 FreeProcInstance(lpProc);
\r
7026 /*---------------------------------------------------------------------------*\
\r
7028 * Ics Interaction console functions
\r
7030 \*---------------------------------------------------------------------------*/
\r
7032 #define HISTORY_SIZE 64
\r
7033 static char *history[HISTORY_SIZE];
\r
7034 int histIn = 0, histP = 0;
\r
7038 SaveInHistory(char *cmd)
\r
7040 if (history[histIn] != NULL) {
\r
7041 free(history[histIn]);
\r
7042 history[histIn] = NULL;
\r
7044 if (*cmd == NULLCHAR) return;
\r
7045 history[histIn] = StrSave(cmd);
\r
7046 histIn = (histIn + 1) % HISTORY_SIZE;
\r
7047 if (history[histIn] != NULL) {
\r
7048 free(history[histIn]);
\r
7050 history[histIn] = NULL;
\r
7056 PrevInHistory(char *cmd)
\r
7059 if (histP == histIn) {
\r
7060 if (history[histIn] != NULL) free(history[histIn]);
\r
7061 history[histIn] = StrSave(cmd);
\r
7063 newhp = (histP - 1 + HISTORY_SIZE) % HISTORY_SIZE;
\r
7064 if (newhp == histIn || history[newhp] == NULL) return NULL;
\r
7066 return history[histP];
\r
7072 if (histP == histIn) return NULL;
\r
7073 histP = (histP + 1) % HISTORY_SIZE;
\r
7074 return history[histP];
\r
7078 LoadIcsTextMenu(IcsTextMenuEntry *e)
\r
7082 hmenu = LoadMenu(hInst, "TextMenu");
\r
7083 h = GetSubMenu(hmenu, 0);
\r
7085 if (strcmp(e->item, "-") == 0) {
\r
7086 AppendMenu(h, MF_SEPARATOR, 0, 0);
\r
7087 } else { // [HGM] re-written a bit to use only one AppendMenu call for both cases (| or no |)
\r
7088 int flags = MF_STRING, j = 0;
\r
7089 if (e->item[0] == '|') {
\r
7090 flags |= MF_MENUBARBREAK;
\r
7093 if(!strcmp(e->command, "none")) flags |= MF_GRAYED; // [HGM] chatclick: provide inactive dummy
\r
7094 AppendMenu(h, flags, IDM_CommandX + i, e->item + j);
\r
7102 WNDPROC consoleTextWindowProc;
\r
7105 CommandX(HWND hwnd, char *command, BOOLEAN getname, BOOLEAN immediate)
\r
7107 char buf[MSG_SIZ], name[MSG_SIZ];
\r
7108 HWND hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
7112 SetWindowText(hInput, command);
\r
7114 SendMessage(hInput, WM_CHAR, '\r', 0);
\r
7116 sel.cpMin = 999999;
\r
7117 sel.cpMax = 999999;
\r
7118 SendMessage(hInput, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
7123 SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);
\r
7124 if (sel.cpMin == sel.cpMax) {
\r
7125 /* Expand to surrounding word */
\r
7128 tr.chrg.cpMax = sel.cpMin;
\r
7129 tr.chrg.cpMin = --sel.cpMin;
\r
7130 if (sel.cpMin < 0) break;
\r
7131 tr.lpstrText = name;
\r
7132 SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr);
\r
7133 } while (isalpha(name[0]) || isdigit(name[0]) || name[0] == '-');
\r
7137 tr.chrg.cpMin = sel.cpMax;
\r
7138 tr.chrg.cpMax = ++sel.cpMax;
\r
7139 tr.lpstrText = name;
\r
7140 if (SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr) < 1) break;
\r
7141 } while (isalpha(name[0]) || isdigit(name[0]) || name[0] == '-');
\r
7144 if (sel.cpMax == sel.cpMin || sel.cpMax - sel.cpMin > MSG_SIZ/2) {
\r
7145 MessageBeep(MB_ICONEXCLAMATION);
\r
7149 tr.lpstrText = name;
\r
7150 SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr);
\r
7152 if (sel.cpMax - sel.cpMin > MSG_SIZ/2) {
\r
7153 MessageBeep(MB_ICONEXCLAMATION);
\r
7156 SendMessage(hwnd, EM_GETSELTEXT, 0, (LPARAM) name);
\r
7159 if(strstr(command, "%s")) snprintf(buf, MSG_SIZ, command, name); else
\r
7160 snprintf(buf, MSG_SIZ, "%s %s", command, name);
\r
7161 SetWindowText(hInput, buf);
\r
7162 SendMessage(hInput, WM_CHAR, '\r', 0);
\r
7164 if(!strcmp(command, "chat")) { ChatPopUp(name); return; }
\r
7165 snprintf(buf, MSG_SIZ, "%s %s ", command, name); /* trailing space */
\r
7166 SetWindowText(hInput, buf);
\r
7167 sel.cpMin = 999999;
\r
7168 sel.cpMax = 999999;
\r
7169 SendMessage(hInput, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
7175 ConsoleTextSubclass(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
\r
7180 switch (message) {
\r
7182 if (!(GetKeyState(VK_CONTROL) & ~1)) break;
\r
7183 if(wParam=='R') return 0;
\r
7186 SendMessage(hwnd, EM_LINESCROLL, 0, -999999);
\r
7189 sel.cpMin = 999999;
\r
7190 sel.cpMax = 999999;
\r
7191 SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
7192 SendMessage(hwnd, EM_SCROLLCARET, 0, 0);
\r
7197 if(wParam != '\022') {
\r
7198 if (wParam == '\t') {
\r
7199 if (GetKeyState(VK_SHIFT) < 0) {
\r
7201 if (IsIconic(hwndMain)) ShowWindow(hwndMain, SW_RESTORE);
\r
7202 if (buttonDesc[0].hwnd) {
\r
7203 SetFocus(buttonDesc[0].hwnd);
\r
7205 SetFocus(hwndMain);
\r
7209 SetFocus(GetDlgItem(hwndConsole, OPT_ConsoleInput));
\r
7212 hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
7213 JAWS_DELETE( SetFocus(hInput); )
\r
7214 SendMessage(hInput, message, wParam, lParam);
\r
7217 } // [HGM] navigate: for Ctrl+R, flow into next case (moved up here) to summon up menu
\r
7219 case WM_RBUTTONDOWN:
\r
7220 if (!(GetKeyState(VK_SHIFT) & ~1)) {
\r
7221 /* Move selection here if it was empty */
\r
7223 pt.x = LOWORD(lParam);
\r
7224 pt.y = HIWORD(lParam);
\r
7225 SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);
\r
7226 if (sel.cpMin == sel.cpMax) {
\r
7227 if(lParam != -1) sel.cpMin = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&pt); /*doc is wrong*/
\r
7228 sel.cpMax = sel.cpMin;
\r
7229 SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
7231 SendMessage(hwnd, EM_HIDESELECTION, FALSE, FALSE);
\r
7232 { // [HGM] chatclick: code moved here from WM_RBUTTONUP case, to have menu appear on down-click
\r
7234 HMENU hmenu = LoadIcsTextMenu(icsTextMenuEntry);
\r
7235 SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);
\r
7236 if (sel.cpMin == sel.cpMax) {
\r
7237 EnableMenuItem(hmenu, IDM_Copy, MF_BYCOMMAND|MF_GRAYED);
\r
7238 EnableMenuItem(hmenu, IDM_QuickPaste, MF_BYCOMMAND|MF_GRAYED);
\r
7240 if (!IsClipboardFormatAvailable(CF_TEXT)) {
\r
7241 EnableMenuItem(hmenu, IDM_Paste, MF_BYCOMMAND|MF_GRAYED);
\r
7243 pt.x = LOWORD(lParam)-30; // [HGM] chatclick: make menu pop up with pointer above upper-right item
\r
7244 pt.y = HIWORD(lParam)-10; // make it appear as if mouse moved there, so it will be selected on up-click
\r
7245 PostMessage(hwnd, WM_MOUSEMOVE, wParam, lParam+5);
\r
7246 MenuPopup(hwnd, pt, hmenu, -1);
\r
7250 case WM_RBUTTONUP:
\r
7251 if (GetKeyState(VK_SHIFT) & ~1) {
\r
7252 SendDlgItemMessage(hwndConsole, OPT_ConsoleText,
\r
7253 WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);
\r
7257 hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
7259 return SendMessage(hInput, message, wParam, lParam);
\r
7260 case WM_MBUTTONDOWN:
\r
7261 return SendMessage(hwnd, WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);
\r
7263 switch (LOWORD(wParam)) {
\r
7264 case IDM_QuickPaste:
\r
7266 SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);
\r
7267 if (sel.cpMin == sel.cpMax) {
\r
7268 MessageBeep(MB_ICONEXCLAMATION);
\r
7271 SendMessage(hwnd, WM_COPY, 0, 0);
\r
7272 hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
7273 SendMessage(hInput, WM_PASTE, 0, 0);
\r
7278 SendMessage(hwnd, WM_CUT, 0, 0);
\r
7281 SendMessage(hwnd, WM_PASTE, 0, 0);
\r
7284 SendMessage(hwnd, WM_COPY, 0, 0);
\r
7288 int i = LOWORD(wParam) - IDM_CommandX;
\r
7289 if (i >= 0 && i < ICS_TEXT_MENU_SIZE &&
\r
7290 icsTextMenuEntry[i].command != NULL) {
\r
7291 CommandX(hwnd, icsTextMenuEntry[i].command,
\r
7292 icsTextMenuEntry[i].getname,
\r
7293 icsTextMenuEntry[i].immediate);
\r
7301 return (*consoleTextWindowProc)(hwnd, message, wParam, lParam);
\r
7304 WNDPROC consoleInputWindowProc;
\r
7307 ConsoleInputSubclass(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
\r
7309 char buf[MSG_SIZ];
\r
7311 static BOOL sendNextChar = FALSE;
\r
7312 static BOOL quoteNextChar = FALSE;
\r
7313 InputSource *is = consoleInputSource;
\r
7317 switch (message) {
\r
7319 if (!appData.localLineEditing || sendNextChar) {
\r
7320 is->buf[0] = (CHAR) wParam;
\r
7322 SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);
\r
7323 sendNextChar = FALSE;
\r
7326 if (quoteNextChar) {
\r
7327 buf[0] = (char) wParam;
\r
7328 buf[1] = NULLCHAR;
\r
7329 SendMessage(hwnd, EM_REPLACESEL, TRUE, (LPARAM) buf);
\r
7330 quoteNextChar = FALSE;
\r
7334 case '\r': /* Enter key */
\r
7335 is->count = GetWindowText(hwnd, is->buf, INPUT_SOURCE_BUF_SIZE-1);
\r
7336 if (consoleEcho) SaveInHistory(is->buf);
\r
7337 is->buf[is->count++] = '\n';
\r
7338 is->buf[is->count] = NULLCHAR;
\r
7339 SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);
\r
7340 if (consoleEcho) {
\r
7341 ConsoleOutput(is->buf, is->count, TRUE);
\r
7342 } else if (appData.localLineEditing) {
\r
7343 ConsoleOutput("\n", 1, TRUE);
\r
7346 case '\033': /* Escape key */
\r
7347 SetWindowText(hwnd, "");
\r
7348 cf.cbSize = sizeof(CHARFORMAT);
\r
7349 cf.dwMask = CFM_COLOR|CFM_BOLD|CFM_ITALIC|CFM_UNDERLINE|CFM_STRIKEOUT;
\r
7350 if (consoleEcho) {
\r
7351 cf.crTextColor = textAttribs[ColorNormal].color;
\r
7353 cf.crTextColor = COLOR_ECHOOFF;
\r
7355 cf.dwEffects = textAttribs[ColorNormal].effects;
\r
7356 SendMessage(hwnd, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf);
\r
7358 case '\t': /* Tab key */
\r
7359 if (GetKeyState(VK_SHIFT) < 0) {
\r
7361 SetFocus(GetDlgItem(hwndConsole, OPT_ConsoleText));
\r
7364 if (IsIconic(hwndMain)) ShowWindow(hwndMain, SW_RESTORE);
\r
7365 if (buttonDesc[0].hwnd) {
\r
7366 SetFocus(buttonDesc[0].hwnd);
\r
7368 SetFocus(hwndMain);
\r
7372 case '\023': /* Ctrl+S */
\r
7373 sendNextChar = TRUE;
\r
7375 case '\021': /* Ctrl+Q */
\r
7376 quoteNextChar = TRUE;
\r
7386 GetWindowText(hwnd, buf, MSG_SIZ);
\r
7387 p = PrevInHistory(buf);
\r
7389 SetWindowText(hwnd, p);
\r
7390 sel.cpMin = 999999;
\r
7391 sel.cpMax = 999999;
\r
7392 SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
7397 p = NextInHistory();
\r
7399 SetWindowText(hwnd, p);
\r
7400 sel.cpMin = 999999;
\r
7401 sel.cpMax = 999999;
\r
7402 SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
7408 if (!(GetKeyState(VK_CONTROL) & ~1)) break;
\r
7412 SendDlgItemMessage(hwndConsole, OPT_ConsoleText, message, wParam, lParam);
\r
7416 case WM_MBUTTONDOWN:
\r
7417 SendDlgItemMessage(hwndConsole, OPT_ConsoleText,
\r
7418 WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);
\r
7420 case WM_RBUTTONUP:
\r
7421 if (GetKeyState(VK_SHIFT) & ~1) {
\r
7422 SendDlgItemMessage(hwndConsole, OPT_ConsoleText,
\r
7423 WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);
\r
7427 hmenu = LoadMenu(hInst, "InputMenu");
\r
7428 SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);
\r
7429 if (sel.cpMin == sel.cpMax) {
\r
7430 EnableMenuItem(hmenu, IDM_Copy, MF_BYCOMMAND|MF_GRAYED);
\r
7431 EnableMenuItem(hmenu, IDM_Cut, MF_BYCOMMAND|MF_GRAYED);
\r
7433 if (!IsClipboardFormatAvailable(CF_TEXT)) {
\r
7434 EnableMenuItem(hmenu, IDM_Paste, MF_BYCOMMAND|MF_GRAYED);
\r
7436 pt.x = LOWORD(lParam);
\r
7437 pt.y = HIWORD(lParam);
\r
7438 MenuPopup(hwnd, pt, hmenu, -1);
\r
7442 switch (LOWORD(wParam)) {
\r
7444 SendMessage(hwnd, EM_UNDO, 0, 0);
\r
7446 case IDM_SelectAll:
\r
7448 sel.cpMax = -1; /*999999?*/
\r
7449 SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
7452 SendMessage(hwnd, WM_CUT, 0, 0);
\r
7455 SendMessage(hwnd, WM_PASTE, 0, 0);
\r
7458 SendMessage(hwnd, WM_COPY, 0, 0);
\r
7463 return (*consoleInputWindowProc)(hwnd, message, wParam, lParam);
\r
7466 #define CO_MAX 100000
\r
7467 #define CO_TRIM 1000
\r
7470 ConsoleWndProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
7472 static SnapData sd;
\r
7473 HWND hText, hInput;
\r
7475 static int sizeX, sizeY;
\r
7476 int newSizeX, newSizeY;
\r
7480 hText = GetDlgItem(hDlg, OPT_ConsoleText);
\r
7481 hInput = GetDlgItem(hDlg, OPT_ConsoleInput);
\r
7483 switch (message) {
\r
7485 if (((NMHDR*)lParam)->code == EN_LINK)
\r
7487 ENLINK *pLink = (ENLINK*)lParam;
\r
7488 if (pLink->msg == WM_LBUTTONUP)
\r
7492 tr.chrg = pLink->chrg;
\r
7493 tr.lpstrText = malloc(1+tr.chrg.cpMax-tr.chrg.cpMin);
\r
7494 SendMessage(hText, EM_GETTEXTRANGE, 0, (LPARAM)&tr);
\r
7495 ShellExecute(NULL, "open", tr.lpstrText, NULL, NULL, SW_SHOW);
\r
7496 free(tr.lpstrText);
\r
7500 case WM_INITDIALOG: /* message: initialize dialog box */
\r
7501 hwndConsole = hDlg;
\r
7503 consoleTextWindowProc = (WNDPROC)
\r
7504 SetWindowLongPtr(hText, GWLP_WNDPROC, (LONG_PTR) ConsoleTextSubclass);
\r
7505 SendMessage(hText, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);
\r
7506 consoleInputWindowProc = (WNDPROC)
\r
7507 SetWindowLongPtr(hInput, GWLP_WNDPROC, (LONG_PTR) ConsoleInputSubclass);
\r
7508 SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);
\r
7509 Colorize(ColorNormal, TRUE);
\r
7510 SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &consoleCF);
\r
7511 ChangedConsoleFont();
\r
7512 GetClientRect(hDlg, &rect);
\r
7513 sizeX = rect.right;
\r
7514 sizeY = rect.bottom;
\r
7515 if (wpConsole.x != CW_USEDEFAULT && wpConsole.y != CW_USEDEFAULT &&
\r
7516 wpConsole.width != CW_USEDEFAULT && wpConsole.height != CW_USEDEFAULT) {
\r
7517 WINDOWPLACEMENT wp;
\r
7518 EnsureOnScreen(&wpConsole.x, &wpConsole.y, 0, 0);
\r
7519 wp.length = sizeof(WINDOWPLACEMENT);
\r
7521 wp.showCmd = SW_SHOW;
\r
7522 wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;
\r
7523 wp.rcNormalPosition.left = wpConsole.x;
\r
7524 wp.rcNormalPosition.right = wpConsole.x + wpConsole.width;
\r
7525 wp.rcNormalPosition.top = wpConsole.y;
\r
7526 wp.rcNormalPosition.bottom = wpConsole.y + wpConsole.height;
\r
7527 SetWindowPlacement(hDlg, &wp);
\r
7530 // [HGM] Chessknight's change 2004-07-13
\r
7531 else { /* Determine Defaults */
\r
7532 WINDOWPLACEMENT wp;
\r
7533 wpConsole.x = wpMain.width + 1;
\r
7534 wpConsole.y = wpMain.y;
\r
7535 wpConsole.width = screenWidth - wpMain.width;
\r
7536 wpConsole.height = wpMain.height;
\r
7537 EnsureOnScreen(&wpConsole.x, &wpConsole.y, 0, 0);
\r
7538 wp.length = sizeof(WINDOWPLACEMENT);
\r
7540 wp.showCmd = SW_SHOW;
\r
7541 wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;
\r
7542 wp.rcNormalPosition.left = wpConsole.x;
\r
7543 wp.rcNormalPosition.right = wpConsole.x + wpConsole.width;
\r
7544 wp.rcNormalPosition.top = wpConsole.y;
\r
7545 wp.rcNormalPosition.bottom = wpConsole.y + wpConsole.height;
\r
7546 SetWindowPlacement(hDlg, &wp);
\r
7549 // Allow hText to highlight URLs and send notifications on them
\r
7550 wMask = (WORD) SendMessage(hText, EM_GETEVENTMASK, 0, 0L);
\r
7551 SendMessage(hText, EM_SETEVENTMASK, 0, wMask | ENM_LINK);
\r
7552 SendMessage(hText, EM_AUTOURLDETECT, TRUE, 0L);
\r
7553 SetWindowLongPtr(hText, GWLP_USERDATA, 79); // initialize the text window's width
\r
7567 if (IsIconic(hDlg)) break;
\r
7568 newSizeX = LOWORD(lParam);
\r
7569 newSizeY = HIWORD(lParam);
\r
7570 if (sizeX != newSizeX || sizeY != newSizeY) {
\r
7571 RECT rectText, rectInput;
\r
7573 int newTextHeight, newTextWidth;
\r
7574 GetWindowRect(hText, &rectText);
\r
7575 newTextWidth = rectText.right - rectText.left + newSizeX - sizeX;
\r
7576 newTextHeight = rectText.bottom - rectText.top + newSizeY - sizeY;
\r
7577 if (newTextHeight < 0) {
\r
7578 newSizeY += -newTextHeight;
\r
7579 newTextHeight = 0;
\r
7581 SetWindowPos(hText, NULL, 0, 0,
\r
7582 newTextWidth, newTextHeight, SWP_NOZORDER|SWP_NOMOVE);
\r
7583 GetWindowRect(hInput, &rectInput); /* gives screen coords */
\r
7584 pt.x = rectInput.left;
\r
7585 pt.y = rectInput.top + newSizeY - sizeY;
\r
7586 ScreenToClient(hDlg, &pt);
\r
7587 SetWindowPos(hInput, NULL,
\r
7588 pt.x, pt.y, /* needs client coords */
\r
7589 rectInput.right - rectInput.left + newSizeX - sizeX,
\r
7590 rectInput.bottom - rectInput.top, SWP_NOZORDER);
\r
7596 case WM_GETMINMAXINFO:
\r
7597 /* Prevent resizing window too small */
\r
7598 mmi = (MINMAXINFO *) lParam;
\r
7599 mmi->ptMinTrackSize.x = 100;
\r
7600 mmi->ptMinTrackSize.y = 100;
\r
7603 /* [AS] Snapping */
\r
7604 case WM_ENTERSIZEMOVE:
\r
7605 return OnEnterSizeMove( &sd, hDlg, wParam, lParam );
\r
7608 return OnSizing( &sd, hDlg, wParam, lParam );
\r
7611 return OnMoving( &sd, hDlg, wParam, lParam );
\r
7613 case WM_EXITSIZEMOVE:
\r
7614 UpdateICSWidth(hText);
\r
7615 return OnExitSizeMove( &sd, hDlg, wParam, lParam );
\r
7618 return DefWindowProc(hDlg, message, wParam, lParam);
\r
7626 if (hwndConsole) return;
\r
7627 hCons = CreateDialog(hInst, szConsoleName, 0, NULL);
\r
7628 SendMessage(hCons, WM_INITDIALOG, 0, 0);
\r
7633 ConsoleOutput(char* data, int length, int forceVisible)
\r
7638 char buf[CO_MAX+1];
\r
7641 static int delayLF = 0;
\r
7642 CHARRANGE savesel, sel;
\r
7644 if (hwndConsole == NULL || length > CO_MAX-100 || length == 0) return;
\r
7652 while (length--) {
\r
7660 } else if (*p == '\007') {
\r
7661 MyPlaySound(&sounds[(int)SoundBell]);
\r
7668 hText = GetDlgItem(hwndConsole, OPT_ConsoleText);
\r
7669 SendMessage(hText, EM_HIDESELECTION, TRUE, FALSE);
\r
7670 /* Save current selection */
\r
7671 SendMessage(hText, EM_EXGETSEL, 0, (LPARAM)&savesel);
\r
7672 exlen = GetWindowTextLength(hText);
\r
7673 /* Find out whether current end of text is visible */
\r
7674 SendMessage(hText, EM_GETRECT, 0, (LPARAM) &rect);
\r
7675 SendMessage(hText, EM_POSFROMCHAR, (WPARAM) &pEnd, exlen);
\r
7676 /* Trim existing text if it's too long */
\r
7677 if (exlen + (q - buf) > CO_MAX) {
\r
7678 trim = (CO_TRIM > (q - buf)) ? CO_TRIM : (q - buf);
\r
7681 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
7682 SendMessage(hText, EM_REPLACESEL, 0, (LPARAM)"");
\r
7684 savesel.cpMin -= trim;
\r
7685 savesel.cpMax -= trim;
\r
7686 if (exlen < 0) exlen = 0;
\r
7687 if (savesel.cpMin < 0) savesel.cpMin = 0;
\r
7688 if (savesel.cpMax < savesel.cpMin) savesel.cpMax = savesel.cpMin;
\r
7690 /* Append the new text */
\r
7691 sel.cpMin = exlen;
\r
7692 sel.cpMax = exlen;
\r
7693 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
7694 SendMessage(hText, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&consoleCF);
\r
7695 SendMessage(hText, EM_REPLACESEL, 0, (LPARAM) buf);
\r
7696 if (forceVisible || exlen == 0 ||
\r
7697 (rect.left <= pEnd.x && pEnd.x < rect.right &&
\r
7698 rect.top <= pEnd.y && pEnd.y < rect.bottom)) {
\r
7699 /* Scroll to make new end of text visible if old end of text
\r
7700 was visible or new text is an echo of user typein */
\r
7701 sel.cpMin = 9999999;
\r
7702 sel.cpMax = 9999999;
\r
7703 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
7704 SendMessage(hText, EM_HIDESELECTION, FALSE, FALSE);
\r
7705 SendMessage(hText, EM_SCROLLCARET, 0, 0);
\r
7706 SendMessage(hText, EM_HIDESELECTION, TRUE, FALSE);
\r
7708 if (savesel.cpMax == exlen || forceVisible) {
\r
7709 /* Move insert point to new end of text if it was at the old
\r
7710 end of text or if the new text is an echo of user typein */
\r
7711 sel.cpMin = 9999999;
\r
7712 sel.cpMax = 9999999;
\r
7713 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
7715 /* Restore previous selection */
\r
7716 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&savesel);
\r
7718 SendMessage(hText, EM_HIDESELECTION, FALSE, FALSE);
\r
7725 DisplayHoldingsCount(HDC hdc, int x, int y, int rightAlign, int copyNumber)
\r
7729 COLORREF oldFg, oldBg;
\r
7733 if(copyNumber > 1)
\r
7734 snprintf(buf, sizeof(buf)/sizeof(buf[0]),"%d", copyNumber); else buf[0] = 0;
\r
7736 oldFg = SetTextColor(hdc, RGB(255, 255, 255)); /* white */
\r
7737 oldBg = SetBkColor(hdc, RGB(0, 0, 0)); /* black */
\r
7738 oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);
\r
7741 rect.right = x + squareSize;
\r
7743 rect.bottom = y + squareSize;
\r
7746 ExtTextOut(hdc, x + MESSAGE_LINE_LEFTMARGIN
\r
7747 + (rightAlign ? (squareSize*2)/3 : 0),
\r
7748 y, ETO_CLIPPED|ETO_OPAQUE,
\r
7749 &rect, str, strlen(str), NULL);
\r
7751 (void) SetTextColor(hdc, oldFg);
\r
7752 (void) SetBkColor(hdc, oldBg);
\r
7753 (void) SelectObject(hdc, oldFont);
\r
7757 DisplayAClock(HDC hdc, int timeRemaining, int highlight,
\r
7758 RECT *rect, char *color, char *flagFell)
\r
7762 COLORREF oldFg, oldBg;
\r
7765 if (twoBoards && partnerUp) return;
\r
7766 if (appData.clockMode) {
\r
7767 if (tinyLayout == 2)
\r
7768 snprintf(buf, sizeof(buf)/sizeof(buf[0]), "%c %s %s", color[0], TimeString(timeRemaining), flagFell);
\r
7770 snprintf(buf, sizeof(buf)/sizeof(buf[0]), "%s:%c%s %s", color, (logoHeight>0 ? 0 : ' '), TimeString(timeRemaining), flagFell);
\r
7777 oldFg = SetTextColor(hdc, RGB(255, 255, 255)); /* white */
\r
7778 oldBg = SetBkColor(hdc, RGB(0, 0, 0)); /* black */
\r
7780 oldFg = SetTextColor(hdc, RGB(0, 0, 0)); /* black */
\r
7781 oldBg = SetBkColor(hdc, RGB(255, 255, 255)); /* white */
\r
7784 oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);
\r
7788 ExtTextOut(hdc, rect->left + MESSAGE_LINE_LEFTMARGIN,
\r
7789 rect->top, ETO_CLIPPED|ETO_OPAQUE,
\r
7790 rect, str, strlen(str), NULL);
\r
7791 if(logoHeight > 0 && appData.clockMode) {
\r
7793 str += strlen(color)+2;
\r
7794 r.top = rect->top + logoHeight/2;
\r
7795 r.left = rect->left;
\r
7796 r.right = rect->right;
\r
7797 r.bottom = rect->bottom;
\r
7798 ExtTextOut(hdc, rect->left + MESSAGE_LINE_LEFTMARGIN,
\r
7799 r.top, ETO_CLIPPED|ETO_OPAQUE,
\r
7800 &r, str, strlen(str), NULL);
\r
7802 (void) SetTextColor(hdc, oldFg);
\r
7803 (void) SetBkColor(hdc, oldBg);
\r
7804 (void) SelectObject(hdc, oldFont);
\r
7809 DoReadFile(HANDLE hFile, char *buf, int count, DWORD *outCount,
\r
7815 if( count <= 0 ) {
\r
7816 if (appData.debugMode) {
\r
7817 fprintf( debugFP, "DoReadFile: trying to read past end of buffer, overflow = %d\n", count );
\r
7820 return ERROR_INVALID_USER_BUFFER;
\r
7823 ResetEvent(ovl->hEvent);
\r
7824 ovl->Offset = ovl->OffsetHigh = 0;
\r
7825 ok = ReadFile(hFile, buf, count, outCount, ovl);
\r
7829 err = GetLastError();
\r
7830 if (err == ERROR_IO_PENDING) {
\r
7831 ok = GetOverlappedResult(hFile, ovl, outCount, TRUE);
\r
7835 err = GetLastError();
\r
7842 DoWriteFile(HANDLE hFile, char *buf, int count, DWORD *outCount,
\r
7847 ResetEvent(ovl->hEvent);
\r
7848 ovl->Offset = ovl->OffsetHigh = 0;
\r
7849 ok = WriteFile(hFile, buf, count, outCount, ovl);
\r
7853 err = GetLastError();
\r
7854 if (err == ERROR_IO_PENDING) {
\r
7855 ok = GetOverlappedResult(hFile, ovl, outCount, TRUE);
\r
7859 err = GetLastError();
\r
7866 /* [AS] If input is line by line and a line exceed the buffer size, force an error */
\r
7867 void CheckForInputBufferFull( InputSource * is )
\r
7869 if( is->lineByLine && (is->next - is->buf) >= INPUT_SOURCE_BUF_SIZE ) {
\r
7870 /* Look for end of line */
\r
7871 char * p = is->buf;
\r
7873 while( p < is->next && *p != '\n' ) {
\r
7877 if( p >= is->next ) {
\r
7878 if (appData.debugMode) {
\r
7879 fprintf( debugFP, "Input line exceeded buffer size (source id=%lu)\n", is->id );
\r
7882 is->error = ERROR_BROKEN_PIPE; /* [AS] Just any non-successful code! */
\r
7883 is->count = (DWORD) -1;
\r
7884 is->next = is->buf;
\r
7890 InputThread(LPVOID arg)
\r
7895 is = (InputSource *) arg;
\r
7896 ovl.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
\r
7897 ovl.Internal = ovl.InternalHigh = ovl.Offset = ovl.OffsetHigh = 0;
\r
7898 while (is->hThread != NULL) {
\r
7899 is->error = DoReadFile(is->hFile, is->next,
\r
7900 INPUT_SOURCE_BUF_SIZE - (is->next - is->buf),
\r
7901 &is->count, &ovl);
\r
7902 if (is->error == NO_ERROR) {
\r
7903 is->next += is->count;
\r
7905 if (is->error == ERROR_BROKEN_PIPE) {
\r
7906 /* Correct for MS brain damage. EOF reading a pipe is not an error. */
\r
7909 is->count = (DWORD) -1;
\r
7910 /* [AS] The (is->count <= 0) check below is not useful for unsigned values! */
\r
7915 CheckForInputBufferFull( is );
\r
7917 SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);
\r
7919 if( is->count == ((DWORD) -1) ) break; /* [AS] */
\r
7921 if (is->count <= 0) break; /* Quit on EOF or error */
\r
7924 CloseHandle(ovl.hEvent);
\r
7925 CloseHandle(is->hFile);
\r
7927 if (appData.debugMode) {
\r
7928 fprintf( debugFP, "Input thread terminated (id=%lu, error=%d, count=%ld)\n", is->id, is->error, is->count );
\r
7935 /* Windows 95 beta 2 won't let you do overlapped i/o on a console or pipe */
\r
7937 NonOvlInputThread(LPVOID arg)
\r
7944 is = (InputSource *) arg;
\r
7945 while (is->hThread != NULL) {
\r
7946 is->error = ReadFile(is->hFile, is->next,
\r
7947 INPUT_SOURCE_BUF_SIZE - (is->next - is->buf),
\r
7948 &is->count, NULL) ? NO_ERROR : GetLastError();
\r
7949 if (is->error == NO_ERROR) {
\r
7950 /* Change CRLF to LF */
\r
7951 if (is->next > is->buf) {
\r
7953 i = is->count + 1;
\r
7961 if (prev == '\r' && *p == '\n') {
\r
7973 if (is->error == ERROR_BROKEN_PIPE) {
\r
7974 /* Correct for MS brain damage. EOF reading a pipe is not an error. */
\r
7977 is->count = (DWORD) -1;
\r
7981 CheckForInputBufferFull( is );
\r
7983 SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);
\r
7985 if( is->count == ((DWORD) -1) ) break; /* [AS] */
\r
7987 if (is->count < 0) break; /* Quit on error */
\r
7989 CloseHandle(is->hFile);
\r
7994 SocketInputThread(LPVOID arg)
\r
7998 is = (InputSource *) arg;
\r
7999 while (is->hThread != NULL) {
\r
8000 is->count = recv(is->sock, is->buf, INPUT_SOURCE_BUF_SIZE, 0);
\r
8001 if ((int)is->count == SOCKET_ERROR) {
\r
8002 is->count = (DWORD) -1;
\r
8003 is->error = WSAGetLastError();
\r
8005 is->error = NO_ERROR;
\r
8006 is->next += is->count;
\r
8007 if (is->count == 0 && is->second == is) {
\r
8008 /* End of file on stderr; quit with no message */
\r
8012 SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);
\r
8014 if( is->count == ((DWORD) -1) ) break; /* [AS] */
\r
8016 if (is->count <= 0) break; /* Quit on EOF or error */
\r
8022 InputEvent(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
\r
8026 is = (InputSource *) lParam;
\r
8027 if (is->lineByLine) {
\r
8028 /* Feed in lines one by one */
\r
8029 char *p = is->buf;
\r
8031 while (q < is->next) {
\r
8032 if (*q++ == '\n') {
\r
8033 (is->func)(is, is->closure, p, q - p, NO_ERROR);
\r
8038 /* Move any partial line to the start of the buffer */
\r
8040 while (p < is->next) {
\r
8045 if (is->error != NO_ERROR || is->count == 0) {
\r
8046 /* Notify backend of the error. Note: If there was a partial
\r
8047 line at the end, it is not flushed through. */
\r
8048 (is->func)(is, is->closure, is->buf, is->count, is->error);
\r
8051 /* Feed in the whole chunk of input at once */
\r
8052 (is->func)(is, is->closure, is->buf, is->count, is->error);
\r
8053 is->next = is->buf;
\r
8057 /*---------------------------------------------------------------------------*\
\r
8059 * Menu enables. Used when setting various modes.
\r
8061 \*---------------------------------------------------------------------------*/
\r
8069 GreyRevert(Boolean grey)
\r
8070 { // [HGM] vari: for retracting variations in local mode
\r
8071 HMENU hmenu = GetMenu(hwndMain);
\r
8072 EnableMenuItem(hmenu, IDM_Revert, MF_BYCOMMAND|(grey ? MF_GRAYED : MF_ENABLED));
\r
8073 EnableMenuItem(hmenu, IDM_Annotate, MF_BYCOMMAND|(grey ? MF_GRAYED : MF_ENABLED));
\r
8077 SetMenuEnables(HMENU hmenu, Enables *enab)
\r
8079 while (enab->item > 0) {
\r
8080 (void) EnableMenuItem(hmenu, enab->item, enab->flags);
\r
8085 Enables gnuEnables[] = {
\r
8086 { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },
\r
8087 { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },
\r
8088 { IDM_IcsClient, MF_BYCOMMAND|MF_GRAYED },
\r
8089 { IDM_Accept, MF_BYCOMMAND|MF_GRAYED },
\r
8090 { IDM_Decline, MF_BYCOMMAND|MF_GRAYED },
\r
8091 { IDM_Rematch, MF_BYCOMMAND|MF_GRAYED },
\r
8092 { IDM_Adjourn, MF_BYCOMMAND|MF_GRAYED },
\r
8093 { IDM_StopExamining, MF_BYCOMMAND|MF_GRAYED },
\r
8094 { IDM_StopObserving, MF_BYCOMMAND|MF_GRAYED },
\r
8095 { IDM_Upload, MF_BYCOMMAND|MF_GRAYED },
\r
8096 { IDM_Revert, MF_BYCOMMAND|MF_GRAYED },
\r
8097 { IDM_Annotate, MF_BYCOMMAND|MF_GRAYED },
\r
8098 { IDM_NewChat, MF_BYCOMMAND|MF_GRAYED },
\r
8101 // Needed to switch from ncp to GNU mode on Engine Load
\r
8102 { ACTION_POS, MF_BYPOSITION|MF_ENABLED },
\r
8103 { IDM_MachineWhite, MF_BYCOMMAND|MF_ENABLED },
\r
8104 { IDM_MachineBlack, MF_BYCOMMAND|MF_ENABLED },
\r
8105 { IDM_TwoMachines, MF_BYCOMMAND|MF_ENABLED },
\r
8106 { IDM_Match, MF_BYCOMMAND|MF_ENABLED },
\r
8107 { IDM_AnalysisMode, MF_BYCOMMAND|MF_ENABLED },
\r
8108 { IDM_AnalyzeFile, MF_BYCOMMAND|MF_ENABLED },
\r
8109 { IDM_Engine1Options, MF_BYCOMMAND|MF_ENABLED },
\r
8110 { IDM_Engine2Options, MF_BYCOMMAND|MF_ENABLED },
\r
8111 { IDM_TimeControl, MF_BYCOMMAND|MF_ENABLED },
\r
8112 { IDM_RetractMove, MF_BYCOMMAND|MF_ENABLED },
\r
8113 { IDM_MoveNow, MF_BYCOMMAND|MF_ENABLED },
\r
8114 { IDM_Hint, MF_BYCOMMAND|MF_ENABLED },
\r
8115 { IDM_Book, MF_BYCOMMAND|MF_ENABLED },
\r
8119 Enables icsEnables[] = {
\r
8120 { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },
\r
8121 { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },
\r
8122 { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },
\r
8123 { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },
\r
8124 { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },
\r
8125 { IDM_Match, MF_BYCOMMAND|MF_GRAYED },
\r
8126 { IDM_MachineBoth, MF_BYCOMMAND|MF_GRAYED },
\r
8127 { IDM_AnalysisMode, MF_BYCOMMAND|MF_ENABLED },
\r
8128 { IDM_AnalyzeFile, MF_BYCOMMAND|MF_GRAYED },
\r
8129 { IDM_TimeControl, MF_BYCOMMAND|MF_GRAYED },
\r
8130 { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },
\r
8131 { IDM_Hint, MF_BYCOMMAND|MF_GRAYED },
\r
8132 { IDM_Book, MF_BYCOMMAND|MF_GRAYED },
\r
8133 { IDM_LoadProg1, MF_BYCOMMAND|MF_GRAYED },
\r
8134 { IDM_LoadProg2, MF_BYCOMMAND|MF_GRAYED },
\r
8135 { IDM_IcsOptions, MF_BYCOMMAND|MF_ENABLED },
\r
8136 { IDM_Engine1Options, MF_BYCOMMAND|MF_GRAYED },
\r
8137 { IDM_Engine2Options, MF_BYCOMMAND|MF_GRAYED },
\r
8138 { IDM_Annotate, MF_BYCOMMAND|MF_GRAYED },
\r
8139 { IDM_Tourney, MF_BYCOMMAND|MF_GRAYED },
\r
8144 Enables zippyEnables[] = {
\r
8145 { IDM_MoveNow, MF_BYCOMMAND|MF_ENABLED },
\r
8146 { IDM_Hint, MF_BYCOMMAND|MF_ENABLED },
\r
8147 { IDM_Book, MF_BYCOMMAND|MF_ENABLED },
\r
8148 { IDM_Engine1Options, MF_BYCOMMAND|MF_ENABLED },
\r
8153 Enables ncpEnables[] = {
\r
8154 { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },
\r
8155 { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },
\r
8156 { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },
\r
8157 { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },
\r
8158 { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },
\r
8159 { IDM_Match, MF_BYCOMMAND|MF_GRAYED },
\r
8160 { IDM_AnalysisMode, MF_BYCOMMAND|MF_GRAYED },
\r
8161 { IDM_AnalyzeFile, MF_BYCOMMAND|MF_GRAYED },
\r
8162 { IDM_IcsClient, MF_BYCOMMAND|MF_GRAYED },
\r
8163 { ACTION_POS, MF_BYPOSITION|MF_GRAYED },
\r
8164 { IDM_Revert, MF_BYCOMMAND|MF_GRAYED },
\r
8165 { IDM_Annotate, MF_BYCOMMAND|MF_GRAYED },
\r
8166 { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },
\r
8167 { IDM_RetractMove, MF_BYCOMMAND|MF_GRAYED },
\r
8168 { IDM_TimeControl, MF_BYCOMMAND|MF_GRAYED },
\r
8169 { IDM_Hint, MF_BYCOMMAND|MF_GRAYED },
\r
8170 { IDM_Book, MF_BYCOMMAND|MF_GRAYED },
\r
8171 { IDM_MachineBoth, MF_BYCOMMAND|MF_GRAYED },
\r
8172 { IDM_NewChat, MF_BYCOMMAND|MF_GRAYED },
\r
8173 { IDM_Engine1Options, MF_BYCOMMAND|MF_GRAYED },
\r
8174 { IDM_Engine2Options, MF_BYCOMMAND|MF_GRAYED },
\r
8175 { IDM_Sounds, MF_BYCOMMAND|MF_GRAYED },
\r
8179 Enables trainingOnEnables[] = {
\r
8180 { IDM_EditComment, MF_BYCOMMAND|MF_GRAYED },
\r
8181 { IDM_Comment, MF_BYCOMMAND|MF_GRAYED },
\r
8182 { IDM_Pause, MF_BYCOMMAND|MF_GRAYED },
\r
8183 { IDM_Forward, MF_BYCOMMAND|MF_GRAYED },
\r
8184 { IDM_Backward, MF_BYCOMMAND|MF_GRAYED },
\r
8185 { IDM_ToEnd, MF_BYCOMMAND|MF_GRAYED },
\r
8186 { IDM_ToStart, MF_BYCOMMAND|MF_GRAYED },
\r
8187 { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },
\r
8188 { IDM_TruncateGame, MF_BYCOMMAND|MF_GRAYED },
\r
8192 Enables trainingOffEnables[] = {
\r
8193 { IDM_EditComment, MF_BYCOMMAND|MF_ENABLED },
\r
8194 { IDM_Comment, MF_BYCOMMAND|MF_ENABLED },
\r
8195 { IDM_Pause, MF_BYCOMMAND|MF_ENABLED },
\r
8196 { IDM_Forward, MF_BYCOMMAND|MF_ENABLED },
\r
8197 { IDM_Backward, MF_BYCOMMAND|MF_ENABLED },
\r
8198 { IDM_ToEnd, MF_BYCOMMAND|MF_ENABLED },
\r
8199 { IDM_ToStart, MF_BYCOMMAND|MF_ENABLED },
\r
8200 { IDM_MoveNow, MF_BYCOMMAND|MF_ENABLED },
\r
8201 { IDM_TruncateGame, MF_BYCOMMAND|MF_ENABLED },
\r
8205 /* These modify either ncpEnables or gnuEnables */
\r
8206 Enables cmailEnables[] = {
\r
8207 { IDM_MailMove, MF_BYCOMMAND|MF_ENABLED },
\r
8208 { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_ENABLED },
\r
8209 { ACTION_POS, MF_BYPOSITION|MF_ENABLED },
\r
8210 { IDM_CallFlag, MF_BYCOMMAND|MF_GRAYED },
\r
8211 { IDM_Draw, MF_BYCOMMAND|MF_ENABLED },
\r
8212 { IDM_Adjourn, MF_BYCOMMAND|MF_GRAYED },
\r
8213 { IDM_Abort, MF_BYCOMMAND|MF_GRAYED },
\r
8217 Enables machineThinkingEnables[] = {
\r
8218 { IDM_LoadGame, MF_BYCOMMAND|MF_GRAYED },
\r
8219 { IDM_LoadNextGame, MF_BYCOMMAND|MF_GRAYED },
\r
8220 { IDM_LoadPrevGame, MF_BYCOMMAND|MF_GRAYED },
\r
8221 { IDM_ReloadGame, MF_BYCOMMAND|MF_GRAYED },
\r
8222 { IDM_PasteGame, MF_BYCOMMAND|MF_GRAYED },
\r
8223 { IDM_LoadPosition, MF_BYCOMMAND|MF_GRAYED },
\r
8224 { IDM_LoadNextPosition, MF_BYCOMMAND|MF_GRAYED },
\r
8225 { IDM_LoadPrevPosition, MF_BYCOMMAND|MF_GRAYED },
\r
8226 { IDM_ReloadPosition, MF_BYCOMMAND|MF_GRAYED },
\r
8227 { IDM_PastePosition, MF_BYCOMMAND|MF_GRAYED },
\r
8228 { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },
\r
8229 { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },
\r
8230 { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },
\r
8231 // { IDM_Match, MF_BYCOMMAND|MF_GRAYED },
\r
8232 { IDM_TypeInMove, MF_BYCOMMAND|MF_GRAYED },
\r
8233 { IDM_RetractMove, MF_BYCOMMAND|MF_GRAYED },
\r
8237 Enables userThinkingEnables[] = {
\r
8238 { IDM_LoadGame, MF_BYCOMMAND|MF_ENABLED },
\r
8239 { IDM_LoadNextGame, MF_BYCOMMAND|MF_ENABLED },
\r
8240 { IDM_LoadPrevGame, MF_BYCOMMAND|MF_ENABLED },
\r
8241 { IDM_ReloadGame, MF_BYCOMMAND|MF_ENABLED },
\r
8242 { IDM_PasteGame, MF_BYCOMMAND|MF_ENABLED },
\r
8243 { IDM_LoadPosition, MF_BYCOMMAND|MF_ENABLED },
\r
8244 { IDM_LoadNextPosition, MF_BYCOMMAND|MF_ENABLED },
\r
8245 { IDM_LoadPrevPosition, MF_BYCOMMAND|MF_ENABLED },
\r
8246 { IDM_ReloadPosition, MF_BYCOMMAND|MF_ENABLED },
\r
8247 { IDM_PastePosition, MF_BYCOMMAND|MF_ENABLED },
\r
8248 { IDM_MachineWhite, MF_BYCOMMAND|MF_ENABLED },
\r
8249 { IDM_MachineBlack, MF_BYCOMMAND|MF_ENABLED },
\r
8250 { IDM_TwoMachines, MF_BYCOMMAND|MF_ENABLED },
\r
8251 // { IDM_Match, MF_BYCOMMAND|MF_ENABLED },
\r
8252 { IDM_TypeInMove, MF_BYCOMMAND|MF_ENABLED },
\r
8253 { IDM_RetractMove, MF_BYCOMMAND|MF_ENABLED },
\r
8257 /*---------------------------------------------------------------------------*\
\r
8259 * Front-end interface functions exported by XBoard.
\r
8260 * Functions appear in same order as prototypes in frontend.h.
\r
8262 \*---------------------------------------------------------------------------*/
\r
8264 CheckMark(UINT item, int state)
\r
8266 if(item) CheckMenuItem(GetMenu(hwndMain), item, MF_BYCOMMAND|state);
\r
8272 static UINT prevChecked = 0;
\r
8273 static int prevPausing = 0;
\r
8276 if (pausing != prevPausing) {
\r
8277 prevPausing = pausing;
\r
8278 (void) CheckMenuItem(GetMenu(hwndMain), IDM_Pause,
\r
8279 MF_BYCOMMAND|(pausing ? MF_CHECKED : MF_UNCHECKED));
\r
8280 if (hwndPause) SetWindowText(hwndPause, pausing ? "C" : "P");
\r
8283 switch (gameMode) {
\r
8284 case BeginningOfGame:
\r
8285 if (appData.icsActive)
\r
8286 nowChecked = IDM_IcsClient;
\r
8287 else if (appData.noChessProgram)
\r
8288 nowChecked = IDM_EditGame;
\r
8290 nowChecked = IDM_MachineBlack;
\r
8292 case MachinePlaysBlack:
\r
8293 nowChecked = IDM_MachineBlack;
\r
8295 case MachinePlaysWhite:
\r
8296 nowChecked = IDM_MachineWhite;
\r
8298 case TwoMachinesPlay:
\r
8299 nowChecked = IDM_TwoMachines;
\r
8302 nowChecked = IDM_AnalysisMode;
\r
8305 nowChecked = IDM_AnalyzeFile;
\r
8308 nowChecked = IDM_EditGame;
\r
8310 case PlayFromGameFile:
\r
8311 nowChecked = IDM_LoadGame;
\r
8313 case EditPosition:
\r
8314 nowChecked = IDM_EditPosition;
\r
8317 nowChecked = IDM_Training;
\r
8319 case IcsPlayingWhite:
\r
8320 case IcsPlayingBlack:
\r
8321 case IcsObserving:
\r
8323 nowChecked = IDM_IcsClient;
\r
8330 if(prevChecked == IDM_TwoMachines) // [HGM] 'Machine Match' might have gotten disabled when stopping match
\r
8331 EnableMenuItem(GetMenu(hwndMain), IDM_Match, MF_BYCOMMAND|MF_ENABLED);
\r
8332 CheckMark(prevChecked, MF_UNCHECKED);
\r
8333 CheckMark(nowChecked, MF_CHECKED);
\r
8334 CheckMark(IDM_Match, matchMode && matchGame < appData.matchGames ? MF_CHECKED : MF_UNCHECKED);
\r
8336 if (nowChecked == IDM_LoadGame || nowChecked == IDM_Training) {
\r
8337 (void) EnableMenuItem(GetMenu(hwndMain), IDM_Training,
\r
8338 MF_BYCOMMAND|MF_ENABLED);
\r
8340 (void) EnableMenuItem(GetMenu(hwndMain),
\r
8341 IDM_Training, MF_BYCOMMAND|MF_GRAYED);
\r
8344 prevChecked = nowChecked;
\r
8346 /* [DM] icsEngineAnalyze - Do a sceure check too */
\r
8347 if (appData.icsActive) {
\r
8348 if (appData.icsEngineAnalyze) {
\r
8349 CheckMark(IDM_AnalysisMode, MF_CHECKED);
\r
8351 CheckMark(IDM_AnalysisMode, MF_UNCHECKED);
\r
8354 DisplayLogos(); // [HGM] logos: mode change could have altered logos
\r
8360 HMENU hmenu = GetMenu(hwndMain);
\r
8361 SetMenuEnables(hmenu, icsEnables);
\r
8362 EnableMenuItem(GetSubMenu(hmenu, OPTIONS_POS), IDM_IcsOptions,
\r
8363 MF_BYCOMMAND|MF_ENABLED);
\r
8365 if (appData.zippyPlay) {
\r
8366 SetMenuEnables(hmenu, zippyEnables);
\r
8367 if (!appData.noChessProgram) /* [DM] icsEngineAnalyze */
\r
8368 (void) EnableMenuItem(GetMenu(hwndMain), IDM_AnalysisMode,
\r
8369 MF_BYCOMMAND|MF_ENABLED);
\r
8377 SetMenuEnables(GetMenu(hwndMain), gnuEnables);
\r
8383 HMENU hmenu = GetMenu(hwndMain);
\r
8384 SetMenuEnables(hmenu, ncpEnables);
\r
8385 DrawMenuBar(hwndMain);
\r
8391 SetMenuEnables(GetMenu(hwndMain), cmailEnables);
\r
8395 SetTrainingModeOn()
\r
8398 SetMenuEnables(GetMenu(hwndMain), trainingOnEnables);
\r
8399 for (i = 0; i < N_BUTTONS; i++) {
\r
8400 if (buttonDesc[i].hwnd != NULL)
\r
8401 EnableWindow(buttonDesc[i].hwnd, FALSE);
\r
8406 VOID SetTrainingModeOff()
\r
8409 SetMenuEnables(GetMenu(hwndMain), trainingOffEnables);
\r
8410 for (i = 0; i < N_BUTTONS; i++) {
\r
8411 if (buttonDesc[i].hwnd != NULL)
\r
8412 EnableWindow(buttonDesc[i].hwnd, TRUE);
\r
8418 SetUserThinkingEnables()
\r
8420 SetMenuEnables(GetMenu(hwndMain), userThinkingEnables);
\r
8424 SetMachineThinkingEnables()
\r
8426 HMENU hMenu = GetMenu(hwndMain);
\r
8427 int flags = MF_BYCOMMAND|MF_ENABLED;
\r
8429 SetMenuEnables(hMenu, machineThinkingEnables);
\r
8431 if (gameMode == MachinePlaysBlack) {
\r
8432 (void)EnableMenuItem(hMenu, IDM_MachineBlack, flags);
\r
8433 } else if (gameMode == MachinePlaysWhite) {
\r
8434 (void)EnableMenuItem(hMenu, IDM_MachineWhite, flags);
\r
8435 } else if (gameMode == TwoMachinesPlay) {
\r
8436 (void)EnableMenuItem(hMenu, matchMode ? IDM_Match : IDM_TwoMachines, flags); // [HGM] match
\r
8442 DisplayTitle(char *str)
\r
8444 char title[MSG_SIZ], *host;
\r
8445 if (str[0] != NULLCHAR) {
\r
8446 safeStrCpy(title, str, sizeof(title)/sizeof(title[0]) );
\r
8447 } else if (appData.icsActive) {
\r
8448 if (appData.icsCommPort[0] != NULLCHAR)
\r
8451 host = appData.icsHost;
\r
8452 snprintf(title, MSG_SIZ, "%s: %s", szTitle, host);
\r
8453 } else if (appData.noChessProgram) {
\r
8454 safeStrCpy(title, szTitle, sizeof(title)/sizeof(title[0]) );
\r
8456 safeStrCpy(title, szTitle, sizeof(title)/sizeof(title[0]) );
\r
8457 strcat(title, ": ");
\r
8458 strcat(title, first.tidy);
\r
8460 SetWindowText(hwndMain, title);
\r
8465 DisplayMessage(char *str1, char *str2)
\r
8469 int remain = MESSAGE_TEXT_MAX - 1;
\r
8472 moveErrorMessageUp = FALSE; /* turned on later by caller if needed */
\r
8473 messageText[0] = NULLCHAR;
\r
8475 len = strlen(str1);
\r
8476 if (len > remain) len = remain;
\r
8477 strncpy(messageText, str1, len);
\r
8478 messageText[len] = NULLCHAR;
\r
8481 if (*str2 && remain >= 2) {
\r
8483 strcat(messageText, " ");
\r
8486 len = strlen(str2);
\r
8487 if (len > remain) len = remain;
\r
8488 strncat(messageText, str2, len);
\r
8490 messageText[MESSAGE_TEXT_MAX - 1] = NULLCHAR;
\r
8491 safeStrCpy(lastMsg, messageText, MSG_SIZ);
\r
8493 if (hwndMain == NULL || IsIconic(hwndMain)) return;
\r
8497 hdc = GetDC(hwndMain);
\r
8498 oldFont = SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);
\r
8499 ExtTextOut(hdc, messageRect.left, messageRect.top, ETO_CLIPPED|ETO_OPAQUE,
\r
8500 &messageRect, messageText, strlen(messageText), NULL);
\r
8501 (void) SelectObject(hdc, oldFont);
\r
8502 (void) ReleaseDC(hwndMain, hdc);
\r
8506 DisplayError(char *str, int error)
\r
8508 char buf[MSG_SIZ*2], buf2[MSG_SIZ];
\r
8512 safeStrCpy(buf, str, sizeof(buf)/sizeof(buf[0]) );
\r
8514 len = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,
\r
8515 NULL, error, LANG_NEUTRAL,
\r
8516 (LPSTR) buf2, MSG_SIZ, NULL);
\r
8518 snprintf(buf, 2*MSG_SIZ, "%s:\n%s", str, buf2);
\r
8520 ErrorMap *em = errmap;
\r
8521 while (em->err != 0 && em->err != error) em++;
\r
8522 if (em->err != 0) {
\r
8523 snprintf(buf, 2*MSG_SIZ, "%s:\n%s", str, em->msg);
\r
8525 snprintf(buf, 2*MSG_SIZ, "%s:\nError code %d", str, error);
\r
8530 ErrorPopUp(_("Error"), buf);
\r
8535 DisplayMoveError(char *str)
\r
8537 fromX = fromY = -1;
\r
8538 ClearHighlights();
\r
8539 DrawPosition(FALSE, NULL);
\r
8540 if (appData.popupMoveErrors) {
\r
8541 ErrorPopUp(_("Error"), str);
\r
8543 DisplayMessage(str, "");
\r
8544 moveErrorMessageUp = TRUE;
\r
8549 DisplayFatalError(char *str, int error, int exitStatus)
\r
8551 char buf[2*MSG_SIZ], buf2[MSG_SIZ];
\r
8553 char *label = exitStatus ? _("Fatal Error") : _("Exiting");
\r
8556 len = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,
\r
8557 NULL, error, LANG_NEUTRAL,
\r
8558 (LPSTR) buf2, MSG_SIZ, NULL);
\r
8560 snprintf(buf, 2*MSG_SIZ, "%s:\n%s", str, buf2);
\r
8562 ErrorMap *em = errmap;
\r
8563 while (em->err != 0 && em->err != error) em++;
\r
8564 if (em->err != 0) {
\r
8565 snprintf(buf, 2*MSG_SIZ, "%s:\n%s", str, em->msg);
\r
8567 snprintf(buf, 2*MSG_SIZ, "%s:\nError code %d", str, error);
\r
8572 if (appData.debugMode) {
\r
8573 fprintf(debugFP, "%s: %s\n", label, str);
\r
8575 if (appData.popupExitMessage) {
\r
8576 if(appData.icsActive) SendToICS("logout\n"); // [HGM] make sure no new games will be started!
\r
8577 (void) MessageBox(hwndMain, str, label, MB_OK|
\r
8578 (exitStatus ? MB_ICONSTOP : MB_ICONINFORMATION));
\r
8580 ExitEvent(exitStatus);
\r
8585 DisplayInformation(char *str)
\r
8587 (void) MessageBox(hwndMain, str, _("Information"), MB_OK|MB_ICONINFORMATION);
\r
8597 DisplayNote(char *str)
\r
8599 ErrorPopUp(_("Note"), str);
\r
8604 char *title, *question, *replyPrefix;
\r
8609 QuestionDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
8611 static QuestionParams *qp;
\r
8612 char reply[MSG_SIZ];
\r
8615 switch (message) {
\r
8616 case WM_INITDIALOG:
\r
8617 qp = (QuestionParams *) lParam;
\r
8618 CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));
\r
8619 Translate(hDlg, DLG_Question);
\r
8620 SetWindowText(hDlg, qp->title);
\r
8621 SetDlgItemText(hDlg, OPT_QuestionText, qp->question);
\r
8622 SetFocus(GetDlgItem(hDlg, OPT_QuestionInput));
\r
8626 switch (LOWORD(wParam)) {
\r
8628 safeStrCpy(reply, qp->replyPrefix, sizeof(reply)/sizeof(reply[0]) );
\r
8629 if (*reply) strcat(reply, " ");
\r
8630 len = strlen(reply);
\r
8631 GetDlgItemText(hDlg, OPT_QuestionInput, reply + len, sizeof(reply) - len);
\r
8632 strcat(reply, "\n");
\r
8633 OutputToProcess(qp->pr, reply, strlen(reply), &err);
\r
8634 EndDialog(hDlg, TRUE);
\r
8635 if (err) DisplayFatalError(_("Error writing to chess program"), err, 1);
\r
8638 EndDialog(hDlg, FALSE);
\r
8649 AskQuestion(char* title, char *question, char *replyPrefix, ProcRef pr)
\r
8651 QuestionParams qp;
\r
8655 qp.question = question;
\r
8656 qp.replyPrefix = replyPrefix;
\r
8658 lpProc = MakeProcInstance((FARPROC)QuestionDialog, hInst);
\r
8659 DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_Question),
\r
8660 hwndMain, (DLGPROC)lpProc, (LPARAM)&qp);
\r
8661 FreeProcInstance(lpProc);
\r
8664 /* [AS] Pick FRC position */
\r
8665 LRESULT CALLBACK NewGameFRC_Proc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
8667 static int * lpIndexFRC;
\r
8673 case WM_INITDIALOG:
\r
8674 lpIndexFRC = (int *) lParam;
\r
8676 CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));
\r
8677 Translate(hDlg, DLG_NewGameFRC);
\r
8679 SendDlgItemMessage( hDlg, IDC_NFG_Edit, EM_SETLIMITTEXT, sizeof(buf)-1, 0 );
\r
8680 SetDlgItemInt( hDlg, IDC_NFG_Edit, *lpIndexFRC, TRUE );
\r
8681 SendDlgItemMessage( hDlg, IDC_NFG_Edit, EM_SETSEL, 0, -1 );
\r
8682 SetFocus(GetDlgItem(hDlg, IDC_NFG_Edit));
\r
8687 switch( LOWORD(wParam) ) {
\r
8689 *lpIndexFRC = GetDlgItemInt(hDlg, IDC_NFG_Edit, &index_is_ok, TRUE );
\r
8690 EndDialog( hDlg, 0 );
\r
8691 shuffleOpenings = TRUE; /* [HGM] shuffle: switch shuffling on for as long as we stay in current variant */
\r
8694 EndDialog( hDlg, 1 );
\r
8696 case IDC_NFG_Edit:
\r
8697 if( HIWORD(wParam) == EN_CHANGE ) {
\r
8698 GetDlgItemInt(hDlg, IDC_NFG_Edit, &index_is_ok, TRUE );
\r
8700 EnableWindow( GetDlgItem(hDlg, IDOK), index_is_ok );
\r
8703 case IDC_NFG_Random:
\r
8704 snprintf( buf, sizeof(buf)/sizeof(buf[0]), "%d", myrandom() ); /* [HGM] shuffle: no longer limit to 960 */
\r
8705 SetDlgItemText(hDlg, IDC_NFG_Edit, buf );
\r
8718 int index = appData.defaultFrcPosition;
\r
8719 FARPROC lpProc = MakeProcInstance( (FARPROC) NewGameFRC_Proc, hInst );
\r
8721 result = DialogBoxParam( hInst, MAKEINTRESOURCE(DLG_NewGameFRC), hwndMain, (DLGPROC)lpProc, (LPARAM)&index );
\r
8723 if( result == 0 ) {
\r
8724 appData.defaultFrcPosition = index;
\r
8730 /* [AS] Game list options. Refactored by HGM */
\r
8732 HWND gameListOptionsDialog;
\r
8734 // low-level front-end: clear text edit / list widget
\r
8739 SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_RESETCONTENT, 0, 0 );
\r
8742 // low-level front-end: clear text edit / list widget
\r
8744 GLT_DeSelectList()
\r
8746 SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_SETCURSEL, 0, 0 );
\r
8749 // low-level front-end: append line to text edit / list widget
\r
8751 GLT_AddToList( char *name )
\r
8754 SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_ADDSTRING, 0, (LPARAM) name );
\r
8758 // low-level front-end: get line from text edit / list widget
\r
8760 GLT_GetFromList( int index, char *name )
\r
8763 if( SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_GETTEXT, index, (LPARAM) name ) != LB_ERR )
\r
8769 void GLT_MoveSelection( HWND hDlg, int delta )
\r
8771 int idx1 = (int) SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETCURSEL, 0, 0 );
\r
8772 int idx2 = idx1 + delta;
\r
8773 int count = (int) SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETCOUNT, 0, 0 );
\r
8775 if( idx1 >=0 && idx1 < count && idx2 >= 0 && idx2 < count ) {
\r
8778 SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETTEXT, idx1, (LPARAM) buf );
\r
8779 SendDlgItemMessage( hDlg, IDC_GameListTags, LB_DELETESTRING, idx1, 0 );
\r
8780 SendDlgItemMessage( hDlg, IDC_GameListTags, LB_INSERTSTRING, idx2, (LPARAM) buf );
\r
8781 SendDlgItemMessage( hDlg, IDC_GameListTags, LB_SETCURSEL, idx2, 0 );
\r
8785 LRESULT CALLBACK GameListOptions_Proc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
8789 case WM_INITDIALOG:
\r
8790 gameListOptionsDialog = hDlg; // [HGM] pass through global to keep out off back-end
\r
8792 CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));
\r
8793 Translate(hDlg, DLG_GameListOptions);
\r
8795 /* Initialize list */
\r
8796 GLT_TagsToList( lpUserGLT );
\r
8798 SetFocus( GetDlgItem(hDlg, IDC_GameListTags) );
\r
8803 switch( LOWORD(wParam) ) {
\r
8806 EndDialog( hDlg, 0 );
\r
8809 EndDialog( hDlg, 1 );
\r
8812 case IDC_GLT_Default:
\r
8813 GLT_TagsToList( GLT_DEFAULT_TAGS );
\r
8816 case IDC_GLT_Restore:
\r
8817 GLT_TagsToList( appData.gameListTags );
\r
8821 GLT_MoveSelection( hDlg, -1 );
\r
8824 case IDC_GLT_Down:
\r
8825 GLT_MoveSelection( hDlg, +1 );
\r
8835 int GameListOptions()
\r
8838 FARPROC lpProc = MakeProcInstance( (FARPROC) GameListOptions_Proc, hInst );
\r
8840 safeStrCpy( lpUserGLT, appData.gameListTags ,LPUSERGLT_SIZE );
\r
8842 result = DialogBoxParam( hInst, MAKEINTRESOURCE(DLG_GameListOptions), hwndMain, (DLGPROC)lpProc, (LPARAM)lpUserGLT );
\r
8844 if( result == 0 ) {
\r
8845 char *oldTags = appData.gameListTags;
\r
8846 /* [AS] Memory leak here! */
\r
8847 appData.gameListTags = strdup( lpUserGLT );
\r
8848 if(strcmp(oldTags, appData.gameListTags)) // [HGM] redo Game List when we changed something
\r
8849 GameListToListBox(NULL, TRUE, ".", NULL, FALSE, FALSE); // "." as filter is kludge to select all
\r
8856 DisplayIcsInteractionTitle(char *str)
\r
8858 char consoleTitle[MSG_SIZ];
\r
8860 snprintf(consoleTitle, MSG_SIZ, "%s: %s", szConsoleTitle, str);
\r
8861 SetWindowText(hwndConsole, consoleTitle);
\r
8863 if(appData.chatBoxes) { // [HGM] chat: open chat boxes
\r
8864 char buf[MSG_SIZ], *p = buf, *q;
\r
8865 safeStrCpy(buf, appData.chatBoxes, sizeof(buf)/sizeof(buf[0]) );
\r
8867 q = strchr(p, ';');
\r
8869 if(*p) ChatPopUp(p);
\r
8873 SetActiveWindow(hwndMain);
\r
8877 DrawPosition(int fullRedraw, Board board)
\r
8879 HDCDrawPosition(NULL, (BOOLEAN) fullRedraw, board);
\r
8882 void NotifyFrontendLogin()
\r
8885 UpdateICSWidth(GetDlgItem(hwndConsole, OPT_ConsoleText));
\r
8891 fromX = fromY = -1;
\r
8892 if (dragInfo.pos.x != -1 || dragInfo.pos.y != -1) {
\r
8893 dragInfo.pos.x = dragInfo.pos.y = -1;
\r
8894 dragInfo.pos.x = dragInfo.pos.y = -1;
\r
8895 dragInfo.lastpos = dragInfo.pos;
\r
8896 dragInfo.start.x = dragInfo.start.y = -1;
\r
8897 dragInfo.from = dragInfo.start;
\r
8899 DrawPosition(TRUE, NULL);
\r
8906 CommentPopUp(char *title, char *str)
\r
8908 HWND hwnd = GetActiveWindow();
\r
8909 EitherCommentPopUp(currentMove, title, str, FALSE); // [HGM] vari: fake move index, rather than 0
\r
8911 SetActiveWindow(hwnd);
\r
8915 CommentPopDown(void)
\r
8917 CheckMenuItem(GetMenu(hwndMain), IDM_Comment, MF_UNCHECKED);
\r
8918 if (commentDialog) {
\r
8919 ShowWindow(commentDialog, SW_HIDE);
\r
8921 commentUp = FALSE;
\r
8925 EditCommentPopUp(int index, char *title, char *str)
\r
8927 EitherCommentPopUp(index, title, str, TRUE);
\r
8934 MyPlaySound(&sounds[(int)SoundRoar]);
\r
8941 MyPlaySound(&sounds[(int)SoundMove]);
\r
8944 VOID PlayIcsWinSound()
\r
8946 MyPlaySound(&sounds[(int)SoundIcsWin]);
\r
8949 VOID PlayIcsLossSound()
\r
8951 MyPlaySound(&sounds[(int)SoundIcsLoss]);
\r
8954 VOID PlayIcsDrawSound()
\r
8956 MyPlaySound(&sounds[(int)SoundIcsDraw]);
\r
8959 VOID PlayIcsUnfinishedSound()
\r
8961 MyPlaySound(&sounds[(int)SoundIcsUnfinished]);
\r
8967 MyPlaySound(&sounds[(int)SoundAlarm]);
\r
8973 MyPlaySound(&textAttribs[ColorTell].sound);
\r
8981 consoleEcho = TRUE;
\r
8982 hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
8983 SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&consoleCF);
\r
8984 SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);
\r
8993 consoleEcho = FALSE;
\r
8994 hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
8995 /* This works OK: set text and background both to the same color */
\r
8997 cf.crTextColor = COLOR_ECHOOFF;
\r
8998 SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf);
\r
8999 SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, cf.crTextColor);
\r
9002 /* No Raw()...? */
\r
9004 void Colorize(ColorClass cc, int continuation)
\r
9006 currentColorClass = cc;
\r
9007 consoleCF.dwMask = CFM_COLOR|CFM_BOLD|CFM_ITALIC|CFM_UNDERLINE|CFM_STRIKEOUT;
\r
9008 consoleCF.crTextColor = textAttribs[cc].color;
\r
9009 consoleCF.dwEffects = textAttribs[cc].effects;
\r
9010 if (!continuation) MyPlaySound(&textAttribs[cc].sound);
\r
9016 static char buf[MSG_SIZ];
\r
9017 DWORD bufsiz = MSG_SIZ;
\r
9019 if(appData.userName != NULL && appData.userName[0] != 0) {
\r
9020 return appData.userName; /* [HGM] username: prefer name selected by user over his system login */
\r
9022 if (!GetUserName(buf, &bufsiz)) {
\r
9023 /*DisplayError("Error getting user name", GetLastError());*/
\r
9024 safeStrCpy(buf, _("User"), sizeof(buf)/sizeof(buf[0]) );
\r
9032 static char buf[MSG_SIZ];
\r
9033 DWORD bufsiz = MSG_SIZ;
\r
9035 if (!GetComputerName(buf, &bufsiz)) {
\r
9036 /*DisplayError("Error getting host name", GetLastError());*/
\r
9037 safeStrCpy(buf, _("Unknown"), sizeof(buf)/sizeof(buf[0]) );
\r
9044 ClockTimerRunning()
\r
9046 return clockTimerEvent != 0;
\r
9052 if (clockTimerEvent == 0) return FALSE;
\r
9053 KillTimer(hwndMain, clockTimerEvent);
\r
9054 clockTimerEvent = 0;
\r
9059 StartClockTimer(long millisec)
\r
9061 clockTimerEvent = SetTimer(hwndMain, (UINT) CLOCK_TIMER_ID,
\r
9062 (UINT) millisec, NULL);
\r
9066 DisplayWhiteClock(long timeRemaining, int highlight)
\r
9069 char *flag = whiteFlag && gameMode == TwoMachinesPlay ? "(!)" : "";
\r
9071 if(appData.noGUI) return;
\r
9072 hdc = GetDC(hwndMain);
\r
9073 if (!IsIconic(hwndMain)) {
\r
9074 DisplayAClock(hdc, timeRemaining, highlight,
\r
9075 flipClock ? &blackRect : &whiteRect, _("White"), flag);
\r
9077 if (highlight && iconCurrent == iconBlack) {
\r
9078 iconCurrent = iconWhite;
\r
9079 PostMessage(hwndMain, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);
\r
9080 if (IsIconic(hwndMain)) {
\r
9081 DrawIcon(hdc, 2, 2, iconCurrent);
\r
9084 (void) ReleaseDC(hwndMain, hdc);
\r
9086 PostMessage(hwndConsole, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);
\r
9090 DisplayBlackClock(long timeRemaining, int highlight)
\r
9093 char *flag = blackFlag && gameMode == TwoMachinesPlay ? "(!)" : "";
\r
9096 if(appData.noGUI) return;
\r
9097 hdc = GetDC(hwndMain);
\r
9098 if (!IsIconic(hwndMain)) {
\r
9099 DisplayAClock(hdc, timeRemaining, highlight,
\r
9100 flipClock ? &whiteRect : &blackRect, _("Black"), flag);
\r
9102 if (highlight && iconCurrent == iconWhite) {
\r
9103 iconCurrent = iconBlack;
\r
9104 PostMessage(hwndMain, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);
\r
9105 if (IsIconic(hwndMain)) {
\r
9106 DrawIcon(hdc, 2, 2, iconCurrent);
\r
9109 (void) ReleaseDC(hwndMain, hdc);
\r
9111 PostMessage(hwndConsole, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);
\r
9116 LoadGameTimerRunning()
\r
9118 return loadGameTimerEvent != 0;
\r
9122 StopLoadGameTimer()
\r
9124 if (loadGameTimerEvent == 0) return FALSE;
\r
9125 KillTimer(hwndMain, loadGameTimerEvent);
\r
9126 loadGameTimerEvent = 0;
\r
9131 StartLoadGameTimer(long millisec)
\r
9133 loadGameTimerEvent = SetTimer(hwndMain, (UINT) LOAD_GAME_TIMER_ID,
\r
9134 (UINT) millisec, NULL);
\r
9142 char fileTitle[MSG_SIZ];
\r
9144 defName = DefaultFileName(appData.oldSaveStyle ? "gam" : "pgn");
\r
9145 f = OpenFileDialog(hwndMain, "a", defName,
\r
9146 appData.oldSaveStyle ? "gam" : "pgn",
\r
9148 _("Save Game to File"), NULL, fileTitle, NULL);
\r
9150 SaveGame(f, 0, "");
\r
9157 ScheduleDelayedEvent(DelayedEventCallback cb, long millisec)
\r
9159 if (delayedTimerEvent != 0) {
\r
9160 if (appData.debugMode && cb != delayedTimerCallback) { // [HGM] alive: not too much debug
\r
9161 fprintf(debugFP, "ScheduleDelayedEvent: event already scheduled\n");
\r
9163 KillTimer(hwndMain, delayedTimerEvent);
\r
9164 delayedTimerEvent = 0;
\r
9165 if(delayedTimerCallback != cb) // [HGM] alive: do not "flush" same event, just postpone it
\r
9166 delayedTimerCallback();
\r
9168 delayedTimerCallback = cb;
\r
9169 delayedTimerEvent = SetTimer(hwndMain, (UINT) DELAYED_TIMER_ID,
\r
9170 (UINT) millisec, NULL);
\r
9173 DelayedEventCallback
\r
9176 if (delayedTimerEvent) {
\r
9177 return delayedTimerCallback;
\r
9184 CancelDelayedEvent()
\r
9186 if (delayedTimerEvent) {
\r
9187 KillTimer(hwndMain, delayedTimerEvent);
\r
9188 delayedTimerEvent = 0;
\r
9192 DWORD GetWin32Priority(int nice)
\r
9193 { // [HGM] nice: translate Unix nice() value to indows priority class. (Code stolen from Polyglot 1.4w11)
\r
9195 REALTIME_PRIORITY_CLASS 0x00000100
\r
9196 HIGH_PRIORITY_CLASS 0x00000080
\r
9197 ABOVE_NORMAL_PRIORITY_CLASS 0x00008000
\r
9198 NORMAL_PRIORITY_CLASS 0x00000020
\r
9199 BELOW_NORMAL_PRIORITY_CLASS 0x00004000
\r
9200 IDLE_PRIORITY_CLASS 0x00000040
\r
9202 if (nice < -15) return 0x00000080;
\r
9203 if (nice < 0) return 0x00008000;
\r
9205 if (nice == 0) return 0x00000020;
\r
9206 if (nice < 15) return 0x00004000;
\r
9207 return 0x00000040;
\r
9210 void RunCommand(char *cmdLine)
\r
9212 /* Now create the child process. */
\r
9213 STARTUPINFO siStartInfo;
\r
9214 PROCESS_INFORMATION piProcInfo;
\r
9216 siStartInfo.cb = sizeof(STARTUPINFO);
\r
9217 siStartInfo.lpReserved = NULL;
\r
9218 siStartInfo.lpDesktop = NULL;
\r
9219 siStartInfo.lpTitle = NULL;
\r
9220 siStartInfo.dwFlags = STARTF_USESTDHANDLES;
\r
9221 siStartInfo.cbReserved2 = 0;
\r
9222 siStartInfo.lpReserved2 = NULL;
\r
9223 siStartInfo.hStdInput = NULL;
\r
9224 siStartInfo.hStdOutput = NULL;
\r
9225 siStartInfo.hStdError = NULL;
\r
9227 CreateProcess(NULL,
\r
9228 cmdLine, /* command line */
\r
9229 NULL, /* process security attributes */
\r
9230 NULL, /* primary thread security attrs */
\r
9231 TRUE, /* handles are inherited */
\r
9232 DETACHED_PROCESS|CREATE_NEW_PROCESS_GROUP,
\r
9233 NULL, /* use parent's environment */
\r
9235 &siStartInfo, /* STARTUPINFO pointer */
\r
9236 &piProcInfo); /* receives PROCESS_INFORMATION */
\r
9238 CloseHandle(piProcInfo.hThread);
\r
9241 /* Start a child process running the given program.
\r
9242 The process's standard output can be read from "from", and its
\r
9243 standard input can be written to "to".
\r
9244 Exit with fatal error if anything goes wrong.
\r
9245 Returns an opaque pointer that can be used to destroy the process
\r
9249 StartChildProcess(char *cmdLine, char *dir, ProcRef *pr)
\r
9251 #define BUFSIZE 4096
\r
9253 HANDLE hChildStdinRd, hChildStdinWr,
\r
9254 hChildStdoutRd, hChildStdoutWr;
\r
9255 HANDLE hChildStdinWrDup, hChildStdoutRdDup;
\r
9256 SECURITY_ATTRIBUTES saAttr;
\r
9258 PROCESS_INFORMATION piProcInfo;
\r
9259 STARTUPINFO siStartInfo;
\r
9261 char buf[MSG_SIZ];
\r
9264 if (appData.debugMode) {
\r
9265 fprintf(debugFP, "StartChildProcess (dir=\"%s\") %s\n", dir, cmdLine);
\r
9270 /* Set the bInheritHandle flag so pipe handles are inherited. */
\r
9271 saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);
\r
9272 saAttr.bInheritHandle = TRUE;
\r
9273 saAttr.lpSecurityDescriptor = NULL;
\r
9276 * The steps for redirecting child's STDOUT:
\r
9277 * 1. Create anonymous pipe to be STDOUT for child.
\r
9278 * 2. Create a noninheritable duplicate of read handle,
\r
9279 * and close the inheritable read handle.
\r
9282 /* Create a pipe for the child's STDOUT. */
\r
9283 if (! CreatePipe(&hChildStdoutRd, &hChildStdoutWr, &saAttr, 0)) {
\r
9284 return GetLastError();
\r
9287 /* Duplicate the read handle to the pipe, so it is not inherited. */
\r
9288 fSuccess = DuplicateHandle(GetCurrentProcess(), hChildStdoutRd,
\r
9289 GetCurrentProcess(), &hChildStdoutRdDup, 0,
\r
9290 FALSE, /* not inherited */
\r
9291 DUPLICATE_SAME_ACCESS);
\r
9293 return GetLastError();
\r
9295 CloseHandle(hChildStdoutRd);
\r
9298 * The steps for redirecting child's STDIN:
\r
9299 * 1. Create anonymous pipe to be STDIN for child.
\r
9300 * 2. Create a noninheritable duplicate of write handle,
\r
9301 * and close the inheritable write handle.
\r
9304 /* Create a pipe for the child's STDIN. */
\r
9305 if (! CreatePipe(&hChildStdinRd, &hChildStdinWr, &saAttr, 0)) {
\r
9306 return GetLastError();
\r
9309 /* Duplicate the write handle to the pipe, so it is not inherited. */
\r
9310 fSuccess = DuplicateHandle(GetCurrentProcess(), hChildStdinWr,
\r
9311 GetCurrentProcess(), &hChildStdinWrDup, 0,
\r
9312 FALSE, /* not inherited */
\r
9313 DUPLICATE_SAME_ACCESS);
\r
9315 return GetLastError();
\r
9317 CloseHandle(hChildStdinWr);
\r
9319 /* Arrange to (1) look in dir for the child .exe file, and
\r
9320 * (2) have dir be the child's working directory. Interpret
\r
9321 * dir relative to the directory WinBoard loaded from. */
\r
9322 GetCurrentDirectory(MSG_SIZ, buf);
\r
9323 SetCurrentDirectory(installDir);
\r
9324 SetCurrentDirectory(dir);
\r
9326 /* Now create the child process. */
\r
9328 siStartInfo.cb = sizeof(STARTUPINFO);
\r
9329 siStartInfo.lpReserved = NULL;
\r
9330 siStartInfo.lpDesktop = NULL;
\r
9331 siStartInfo.lpTitle = NULL;
\r
9332 siStartInfo.dwFlags = STARTF_USESTDHANDLES;
\r
9333 siStartInfo.cbReserved2 = 0;
\r
9334 siStartInfo.lpReserved2 = NULL;
\r
9335 siStartInfo.hStdInput = hChildStdinRd;
\r
9336 siStartInfo.hStdOutput = hChildStdoutWr;
\r
9337 siStartInfo.hStdError = hChildStdoutWr;
\r
9339 fSuccess = CreateProcess(NULL,
\r
9340 cmdLine, /* command line */
\r
9341 NULL, /* process security attributes */
\r
9342 NULL, /* primary thread security attrs */
\r
9343 TRUE, /* handles are inherited */
\r
9344 DETACHED_PROCESS|CREATE_NEW_PROCESS_GROUP,
\r
9345 NULL, /* use parent's environment */
\r
9347 &siStartInfo, /* STARTUPINFO pointer */
\r
9348 &piProcInfo); /* receives PROCESS_INFORMATION */
\r
9350 err = GetLastError();
\r
9351 SetCurrentDirectory(buf); /* return to prev directory */
\r
9356 if (appData.niceEngines){ // [HGM] nice: adjust engine proc priority
\r
9357 if(appData.debugMode) fprintf(debugFP, "nice engine proc to %d\n", appData.niceEngines);
\r
9358 SetPriorityClass(piProcInfo.hProcess, GetWin32Priority(appData.niceEngines));
\r
9361 /* Close the handles we don't need in the parent */
\r
9362 CloseHandle(piProcInfo.hThread);
\r
9363 CloseHandle(hChildStdinRd);
\r
9364 CloseHandle(hChildStdoutWr);
\r
9366 /* Prepare return value */
\r
9367 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
\r
9368 cp->kind = CPReal;
\r
9369 cp->hProcess = piProcInfo.hProcess;
\r
9370 cp->pid = piProcInfo.dwProcessId;
\r
9371 cp->hFrom = hChildStdoutRdDup;
\r
9372 cp->hTo = hChildStdinWrDup;
\r
9374 *pr = (void *) cp;
\r
9376 /* Klaus Friedel says that this Sleep solves a problem under Windows
\r
9377 2000 where engines sometimes don't see the initial command(s)
\r
9378 from WinBoard and hang. I don't understand how that can happen,
\r
9379 but the Sleep is harmless, so I've put it in. Others have also
\r
9380 reported what may be the same problem, so hopefully this will fix
\r
9381 it for them too. */
\r
9389 DestroyChildProcess(ProcRef pr, int/*boolean*/ signal)
\r
9391 ChildProc *cp; int result;
\r
9393 cp = (ChildProc *) pr;
\r
9394 if (cp == NULL) return;
\r
9396 switch (cp->kind) {
\r
9398 /* TerminateProcess is considered harmful, so... */
\r
9399 CloseHandle(cp->hTo); /* Closing this will give the child an EOF and hopefully kill it */
\r
9400 if (cp->hFrom) CloseHandle(cp->hFrom); /* if NULL, InputThread will close it */
\r
9401 /* The following doesn't work because the chess program
\r
9402 doesn't "have the same console" as WinBoard. Maybe
\r
9403 we could arrange for this even though neither WinBoard
\r
9404 nor the chess program uses a console for stdio? */
\r
9405 /*!!if (signal) GenerateConsoleCtrlEvent(CTRL_BREAK_EVENT, cp->pid);*/
\r
9407 /* [AS] Special termination modes for misbehaving programs... */
\r
9408 if( signal & 8 ) {
\r
9409 result = TerminateProcess( cp->hProcess, 0 );
\r
9411 if ( appData.debugMode) {
\r
9412 fprintf( debugFP, "Terminating process %lu, result=%d\n", cp->pid, result );
\r
9415 else if( signal & 4 ) {
\r
9416 DWORD dw = WaitForSingleObject( cp->hProcess, appData.delayAfterQuit*1000 + 50 ); // Wait 3 seconds at most
\r
9418 if( dw != WAIT_OBJECT_0 ) {
\r
9419 result = TerminateProcess( cp->hProcess, 0 );
\r
9421 if ( appData.debugMode) {
\r
9422 fprintf( debugFP, "Process %lu still alive after timeout, killing... result=%d\n", cp->pid, result );
\r
9428 CloseHandle(cp->hProcess);
\r
9432 if (cp->hFrom) CloseHandle(cp->hFrom);
\r
9436 closesocket(cp->sock);
\r
9441 if (signal) send(cp->sock2, "\017", 1, 0); /* 017 = 15 = SIGTERM */
\r
9442 closesocket(cp->sock);
\r
9443 closesocket(cp->sock2);
\r
9451 InterruptChildProcess(ProcRef pr)
\r
9455 cp = (ChildProc *) pr;
\r
9456 if (cp == NULL) return;
\r
9457 switch (cp->kind) {
\r
9459 /* The following doesn't work because the chess program
\r
9460 doesn't "have the same console" as WinBoard. Maybe
\r
9461 we could arrange for this even though neither WinBoard
\r
9462 nor the chess program uses a console for stdio */
\r
9463 /*!!GenerateConsoleCtrlEvent(CTRL_C_EVENT, cp->pid);*/
\r
9468 /* Can't interrupt */
\r
9472 send(cp->sock2, "\002", 1, 0); /* 2 = SIGINT */
\r
9479 OpenTelnet(char *host, char *port, ProcRef *pr)
\r
9481 char cmdLine[MSG_SIZ];
\r
9483 if (port[0] == NULLCHAR) {
\r
9484 snprintf(cmdLine, MSG_SIZ, "%s %s", appData.telnetProgram, host);
\r
9486 snprintf(cmdLine, MSG_SIZ, "%s %s %s", appData.telnetProgram, host, port);
\r
9488 return StartChildProcess(cmdLine, "", pr);
\r
9492 /* Code to open TCP sockets */
\r
9495 OpenTCP(char *host, char *port, ProcRef *pr)
\r
9501 struct sockaddr_in sa, mysa;
\r
9502 struct hostent FAR *hp;
\r
9503 unsigned short uport;
\r
9504 WORD wVersionRequested;
\r
9507 /* Initialize socket DLL */
\r
9508 wVersionRequested = MAKEWORD(1, 1);
\r
9509 err = WSAStartup(wVersionRequested, &wsaData);
\r
9510 if (err != 0) return err;
\r
9513 if ((s = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {
\r
9514 err = WSAGetLastError();
\r
9519 /* Bind local address using (mostly) don't-care values.
\r
9521 memset((char *) &mysa, 0, sizeof(struct sockaddr_in));
\r
9522 mysa.sin_family = AF_INET;
\r
9523 mysa.sin_addr.s_addr = INADDR_ANY;
\r
9524 uport = (unsigned short) 0;
\r
9525 mysa.sin_port = htons(uport);
\r
9526 if (bind(s, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))
\r
9527 == SOCKET_ERROR) {
\r
9528 err = WSAGetLastError();
\r
9533 /* Resolve remote host name */
\r
9534 memset((char *) &sa, 0, sizeof(struct sockaddr_in));
\r
9535 if (!(hp = gethostbyname(host))) {
\r
9536 unsigned int b0, b1, b2, b3;
\r
9538 err = WSAGetLastError();
\r
9540 if (sscanf(host, "%u.%u.%u.%u", &b0, &b1, &b2, &b3) == 4) {
\r
9541 hp = (struct hostent *) calloc(1, sizeof(struct hostent));
\r
9542 hp->h_addrtype = AF_INET;
\r
9544 hp->h_addr_list = (char **) calloc(2, sizeof(char *));
\r
9545 hp->h_addr_list[0] = (char *) malloc(4);
\r
9546 hp->h_addr_list[0][0] = (char) b0;
\r
9547 hp->h_addr_list[0][1] = (char) b1;
\r
9548 hp->h_addr_list[0][2] = (char) b2;
\r
9549 hp->h_addr_list[0][3] = (char) b3;
\r
9555 sa.sin_family = hp->h_addrtype;
\r
9556 uport = (unsigned short) atoi(port);
\r
9557 sa.sin_port = htons(uport);
\r
9558 memcpy((char *) &sa.sin_addr, hp->h_addr, hp->h_length);
\r
9560 /* Make connection */
\r
9561 if (connect(s, (struct sockaddr *) &sa,
\r
9562 sizeof(struct sockaddr_in)) == SOCKET_ERROR) {
\r
9563 err = WSAGetLastError();
\r
9568 /* Prepare return value */
\r
9569 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
\r
9570 cp->kind = CPSock;
\r
9572 *pr = (ProcRef *) cp;
\r
9578 OpenCommPort(char *name, ProcRef *pr)
\r
9583 char fullname[MSG_SIZ];
\r
9585 if (*name != '\\')
\r
9586 snprintf(fullname, MSG_SIZ, "\\\\.\\%s", name);
\r
9588 safeStrCpy(fullname, name, sizeof(fullname)/sizeof(fullname[0]) );
\r
9590 h = CreateFile(name, GENERIC_READ | GENERIC_WRITE,
\r
9591 0, NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL);
\r
9592 if (h == (HANDLE) -1) {
\r
9593 return GetLastError();
\r
9597 if (!SetCommState(h, (LPDCB) &dcb)) return GetLastError();
\r
9599 /* Accumulate characters until a 100ms pause, then parse */
\r
9600 ct.ReadIntervalTimeout = 100;
\r
9601 ct.ReadTotalTimeoutMultiplier = 0;
\r
9602 ct.ReadTotalTimeoutConstant = 0;
\r
9603 ct.WriteTotalTimeoutMultiplier = 0;
\r
9604 ct.WriteTotalTimeoutConstant = 0;
\r
9605 if (!SetCommTimeouts(h, (LPCOMMTIMEOUTS) &ct)) return GetLastError();
\r
9607 /* Prepare return value */
\r
9608 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
\r
9609 cp->kind = CPComm;
\r
9612 *pr = (ProcRef *) cp;
\r
9618 OpenLoopback(ProcRef *pr)
\r
9620 DisplayFatalError(_("Not implemented"), 0, 1);
\r
9626 OpenRcmd(char* host, char* user, char* cmd, ProcRef* pr)
\r
9631 struct sockaddr_in sa, mysa;
\r
9632 struct hostent FAR *hp;
\r
9633 unsigned short uport;
\r
9634 WORD wVersionRequested;
\r
9637 char stderrPortStr[MSG_SIZ];
\r
9639 /* Initialize socket DLL */
\r
9640 wVersionRequested = MAKEWORD(1, 1);
\r
9641 err = WSAStartup(wVersionRequested, &wsaData);
\r
9642 if (err != 0) return err;
\r
9644 /* Resolve remote host name */
\r
9645 memset((char *) &sa, 0, sizeof(struct sockaddr_in));
\r
9646 if (!(hp = gethostbyname(host))) {
\r
9647 unsigned int b0, b1, b2, b3;
\r
9649 err = WSAGetLastError();
\r
9651 if (sscanf(host, "%u.%u.%u.%u", &b0, &b1, &b2, &b3) == 4) {
\r
9652 hp = (struct hostent *) calloc(1, sizeof(struct hostent));
\r
9653 hp->h_addrtype = AF_INET;
\r
9655 hp->h_addr_list = (char **) calloc(2, sizeof(char *));
\r
9656 hp->h_addr_list[0] = (char *) malloc(4);
\r
9657 hp->h_addr_list[0][0] = (char) b0;
\r
9658 hp->h_addr_list[0][1] = (char) b1;
\r
9659 hp->h_addr_list[0][2] = (char) b2;
\r
9660 hp->h_addr_list[0][3] = (char) b3;
\r
9666 sa.sin_family = hp->h_addrtype;
\r
9667 uport = (unsigned short) 514;
\r
9668 sa.sin_port = htons(uport);
\r
9669 memcpy((char *) &sa.sin_addr, hp->h_addr, hp->h_length);
\r
9671 /* Bind local socket to unused "privileged" port address
\r
9673 s = INVALID_SOCKET;
\r
9674 memset((char *) &mysa, 0, sizeof(struct sockaddr_in));
\r
9675 mysa.sin_family = AF_INET;
\r
9676 mysa.sin_addr.s_addr = INADDR_ANY;
\r
9677 for (fromPort = 1023;; fromPort--) {
\r
9678 if (fromPort < 0) {
\r
9680 return WSAEADDRINUSE;
\r
9682 if (s == INVALID_SOCKET) {
\r
9683 if ((s = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {
\r
9684 err = WSAGetLastError();
\r
9689 uport = (unsigned short) fromPort;
\r
9690 mysa.sin_port = htons(uport);
\r
9691 if (bind(s, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))
\r
9692 == SOCKET_ERROR) {
\r
9693 err = WSAGetLastError();
\r
9694 if (err == WSAEADDRINUSE) continue;
\r
9698 if (connect(s, (struct sockaddr *) &sa,
\r
9699 sizeof(struct sockaddr_in)) == SOCKET_ERROR) {
\r
9700 err = WSAGetLastError();
\r
9701 if (err == WSAEADDRINUSE) {
\r
9712 /* Bind stderr local socket to unused "privileged" port address
\r
9714 s2 = INVALID_SOCKET;
\r
9715 memset((char *) &mysa, 0, sizeof(struct sockaddr_in));
\r
9716 mysa.sin_family = AF_INET;
\r
9717 mysa.sin_addr.s_addr = INADDR_ANY;
\r
9718 for (fromPort = 1023;; fromPort--) {
\r
9719 if (fromPort == prevStderrPort) continue; // don't reuse port
\r
9720 if (fromPort < 0) {
\r
9721 (void) closesocket(s);
\r
9723 return WSAEADDRINUSE;
\r
9725 if (s2 == INVALID_SOCKET) {
\r
9726 if ((s2 = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {
\r
9727 err = WSAGetLastError();
\r
9733 uport = (unsigned short) fromPort;
\r
9734 mysa.sin_port = htons(uport);
\r
9735 if (bind(s2, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))
\r
9736 == SOCKET_ERROR) {
\r
9737 err = WSAGetLastError();
\r
9738 if (err == WSAEADDRINUSE) continue;
\r
9739 (void) closesocket(s);
\r
9743 if (listen(s2, 1) == SOCKET_ERROR) {
\r
9744 err = WSAGetLastError();
\r
9745 if (err == WSAEADDRINUSE) {
\r
9747 s2 = INVALID_SOCKET;
\r
9750 (void) closesocket(s);
\r
9751 (void) closesocket(s2);
\r
9757 prevStderrPort = fromPort; // remember port used
\r
9758 snprintf(stderrPortStr, MSG_SIZ, "%d", fromPort);
\r
9760 if (send(s, stderrPortStr, strlen(stderrPortStr) + 1, 0) == SOCKET_ERROR) {
\r
9761 err = WSAGetLastError();
\r
9762 (void) closesocket(s);
\r
9763 (void) closesocket(s2);
\r
9768 if (send(s, UserName(), strlen(UserName()) + 1, 0) == SOCKET_ERROR) {
\r
9769 err = WSAGetLastError();
\r
9770 (void) closesocket(s);
\r
9771 (void) closesocket(s2);
\r
9775 if (*user == NULLCHAR) user = UserName();
\r
9776 if (send(s, user, strlen(user) + 1, 0) == SOCKET_ERROR) {
\r
9777 err = WSAGetLastError();
\r
9778 (void) closesocket(s);
\r
9779 (void) closesocket(s2);
\r
9783 if (send(s, cmd, strlen(cmd) + 1, 0) == SOCKET_ERROR) {
\r
9784 err = WSAGetLastError();
\r
9785 (void) closesocket(s);
\r
9786 (void) closesocket(s2);
\r
9791 if ((s3 = accept(s2, NULL, NULL)) == INVALID_SOCKET) {
\r
9792 err = WSAGetLastError();
\r
9793 (void) closesocket(s);
\r
9794 (void) closesocket(s2);
\r
9798 (void) closesocket(s2); /* Stop listening */
\r
9800 /* Prepare return value */
\r
9801 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
\r
9802 cp->kind = CPRcmd;
\r
9805 *pr = (ProcRef *) cp;
\r
9812 AddInputSource(ProcRef pr, int lineByLine,
\r
9813 InputCallback func, VOIDSTAR closure)
\r
9815 InputSource *is, *is2 = NULL;
\r
9816 ChildProc *cp = (ChildProc *) pr;
\r
9818 is = (InputSource *) calloc(1, sizeof(InputSource));
\r
9819 is->lineByLine = lineByLine;
\r
9821 is->closure = closure;
\r
9822 is->second = NULL;
\r
9823 is->next = is->buf;
\r
9824 if (pr == NoProc) {
\r
9825 is->kind = CPReal;
\r
9826 consoleInputSource = is;
\r
9828 is->kind = cp->kind;
\r
9830 [AS] Try to avoid a race condition if the thread is given control too early:
\r
9831 we create all threads suspended so that the is->hThread variable can be
\r
9832 safely assigned, then let the threads start with ResumeThread.
\r
9834 switch (cp->kind) {
\r
9836 is->hFile = cp->hFrom;
\r
9837 cp->hFrom = NULL; /* now owned by InputThread */
\r
9839 CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) NonOvlInputThread,
\r
9840 (LPVOID) is, CREATE_SUSPENDED, &is->id);
\r
9844 is->hFile = cp->hFrom;
\r
9845 cp->hFrom = NULL; /* now owned by InputThread */
\r
9847 CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) InputThread,
\r
9848 (LPVOID) is, CREATE_SUSPENDED, &is->id);
\r
9852 is->sock = cp->sock;
\r
9854 CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,
\r
9855 (LPVOID) is, CREATE_SUSPENDED, &is->id);
\r
9859 is2 = (InputSource *) calloc(1, sizeof(InputSource));
\r
9861 is->sock = cp->sock;
\r
9863 is2->sock = cp->sock2;
\r
9864 is2->second = is2;
\r
9866 CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,
\r
9867 (LPVOID) is, CREATE_SUSPENDED, &is->id);
\r
9869 CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,
\r
9870 (LPVOID) is2, CREATE_SUSPENDED, &is2->id);
\r
9874 if( is->hThread != NULL ) {
\r
9875 ResumeThread( is->hThread );
\r
9878 if( is2 != NULL && is2->hThread != NULL ) {
\r
9879 ResumeThread( is2->hThread );
\r
9883 return (InputSourceRef) is;
\r
9887 RemoveInputSource(InputSourceRef isr)
\r
9891 is = (InputSource *) isr;
\r
9892 is->hThread = NULL; /* tell thread to stop */
\r
9893 CloseHandle(is->hThread);
\r
9894 if (is->second != NULL) {
\r
9895 is->second->hThread = NULL;
\r
9896 CloseHandle(is->second->hThread);
\r
9900 int no_wrap(char *message, int count)
\r
9902 ConsoleOutput(message, count, FALSE);
\r
9907 OutputToProcess(ProcRef pr, char *message, int count, int *outError)
\r
9910 int outCount = SOCKET_ERROR;
\r
9911 ChildProc *cp = (ChildProc *) pr;
\r
9912 static OVERLAPPED ovl;
\r
9914 static int line = 0;
\r
9918 if (appData.noJoin || !appData.useInternalWrap)
\r
9919 return no_wrap(message, count);
\r
9922 int width = get_term_width();
\r
9923 int len = wrap(NULL, message, count, width, &line);
\r
9924 char *msg = malloc(len);
\r
9928 return no_wrap(message, count);
\r
9931 dbgchk = wrap(msg, message, count, width, &line);
\r
9932 if (dbgchk != len && appData.debugMode)
\r
9933 fprintf(debugFP, "wrap(): dbgchk(%d) != len(%d)\n", dbgchk, len);
\r
9934 ConsoleOutput(msg, len, FALSE);
\r
9941 if (ovl.hEvent == NULL) {
\r
9942 ovl.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
\r
9944 ovl.Internal = ovl.InternalHigh = ovl.Offset = ovl.OffsetHigh = 0;
\r
9946 switch (cp->kind) {
\r
9949 outCount = send(cp->sock, message, count, 0);
\r
9950 if (outCount == SOCKET_ERROR) {
\r
9951 *outError = WSAGetLastError();
\r
9953 *outError = NO_ERROR;
\r
9958 if (WriteFile(((ChildProc *)pr)->hTo, message, count,
\r
9959 &dOutCount, NULL)) {
\r
9960 *outError = NO_ERROR;
\r
9961 outCount = (int) dOutCount;
\r
9963 *outError = GetLastError();
\r
9968 *outError = DoWriteFile(((ChildProc *)pr)->hTo, message, count,
\r
9969 &dOutCount, &ovl);
\r
9970 if (*outError == NO_ERROR) {
\r
9971 outCount = (int) dOutCount;
\r
9981 if(n != 0) Sleep(n);
\r
9985 OutputToProcessDelayed(ProcRef pr, char *message, int count, int *outError,
\r
9988 /* Ignore delay, not implemented for WinBoard */
\r
9989 return OutputToProcess(pr, message, count, outError);
\r
9994 CmailSigHandlerCallBack(InputSourceRef isr, VOIDSTAR closure,
\r
9995 char *buf, int count, int error)
\r
9997 DisplayFatalError(_("Not implemented"), 0, 1);
\r
10000 /* see wgamelist.c for Game List functions */
\r
10001 /* see wedittags.c for Edit Tags functions */
\r
10008 char buf[MSG_SIZ];
\r
10011 if (SearchPath(installDir, appData.icsLogon, NULL, MSG_SIZ, buf, &dummy)) {
\r
10012 f = fopen(buf, "r");
\r
10014 ProcessICSInitScript(f);
\r
10024 StartAnalysisClock()
\r
10026 if (analysisTimerEvent) return;
\r
10027 analysisTimerEvent = SetTimer(hwndMain, (UINT) ANALYSIS_TIMER_ID,
\r
10028 (UINT) 2000, NULL);
\r
10032 SetHighlights(int fromX, int fromY, int toX, int toY)
\r
10034 highlightInfo.sq[0].x = fromX;
\r
10035 highlightInfo.sq[0].y = fromY;
\r
10036 highlightInfo.sq[1].x = toX;
\r
10037 highlightInfo.sq[1].y = toY;
\r
10041 ClearHighlights()
\r
10043 highlightInfo.sq[0].x = highlightInfo.sq[0].y =
\r
10044 highlightInfo.sq[1].x = highlightInfo.sq[1].y = -1;
\r
10048 SetPremoveHighlights(int fromX, int fromY, int toX, int toY)
\r
10050 premoveHighlightInfo.sq[0].x = fromX;
\r
10051 premoveHighlightInfo.sq[0].y = fromY;
\r
10052 premoveHighlightInfo.sq[1].x = toX;
\r
10053 premoveHighlightInfo.sq[1].y = toY;
\r
10057 ClearPremoveHighlights()
\r
10059 premoveHighlightInfo.sq[0].x = premoveHighlightInfo.sq[0].y =
\r
10060 premoveHighlightInfo.sq[1].x = premoveHighlightInfo.sq[1].y = -1;
\r
10064 ShutDownFrontEnd()
\r
10066 if (saveSettingsOnExit) SaveSettings(settingsFileName);
\r
10067 DeleteClipboardTempFiles();
\r
10073 if (IsIconic(hwndMain))
\r
10074 ShowWindow(hwndMain, SW_RESTORE);
\r
10076 SetActiveWindow(hwndMain);
\r
10080 * Prototypes for animation support routines
\r
10082 static void ScreenSquare(int column, int row, POINT * pt);
\r
10083 static void Tween( POINT * start, POINT * mid, POINT * finish, int factor,
\r
10084 POINT frames[], int * nFrames);
\r
10087 #define kFactor 4
\r
10090 AnimateAtomicCapture(Board board, int fromX, int fromY, int toX, int toY)
\r
10091 { // [HGM] atomic: animate blast wave
\r
10094 explodeInfo.fromX = fromX;
\r
10095 explodeInfo.fromY = fromY;
\r
10096 explodeInfo.toX = toX;
\r
10097 explodeInfo.toY = toY;
\r
10098 for(i=1; i<4*kFactor; i++) {
\r
10099 explodeInfo.radius = (i*180)/(4*kFactor-1);
\r
10100 DrawPosition(FALSE, board);
\r
10101 Sleep(appData.animSpeed);
\r
10103 explodeInfo.radius = 0;
\r
10104 DrawPosition(TRUE, board);
\r
10108 AnimateMove(board, fromX, fromY, toX, toY)
\r
10115 ChessSquare piece, victim = EmptySquare, victim2 = EmptySquare;
\r
10116 int x = toX, y = toY, x2 = kill2X;
\r
10117 POINT start, finish, mid;
\r
10118 POINT frames[kFactor * 2 + 1];
\r
10121 if(killX >= 0 && IS_LION(board[fromY][fromX])) Roar();
\r
10123 if (!appData.animate) return;
\r
10124 if (doingSizing) return;
\r
10125 if (fromY < 0 || fromX < 0) return;
\r
10126 piece = board[fromY][fromX];
\r
10127 if (piece >= EmptySquare) return;
\r
10129 if(x2 >= 0) toX = kill2X, toY = kill2Y, victim = board[killY][killX], victim2 = board[kill2Y][kill2X]; else
\r
10130 if(killX >= 0) toX = killX, toY = killY, victim = board[killY][killX]; // [HGM] lion: first to kill square
\r
10132 animInfo.from.x = fromX;
\r
10133 animInfo.from.y = fromY;
\r
10137 ScreenSquare(fromX, fromY, &start);
\r
10138 ScreenSquare(toX, toY, &finish);
\r
10140 /* All moves except knight jumps move in straight line */
\r
10141 if (!(abs(fromX-toX) == 1 && abs(fromY-toY) == 2 || abs(fromX-toX) == 2 && abs(fromY-toY) == 1)) {
\r
10142 mid.x = start.x + (finish.x - start.x) / 2;
\r
10143 mid.y = start.y + (finish.y - start.y) / 2;
\r
10145 /* Knight: make straight movement then diagonal */
\r
10146 if (abs(toY - fromY) < abs(toX - fromX)) {
\r
10147 mid.x = start.x + (finish.x - start.x) / 2;
\r
10151 mid.y = start.y + (finish.y - start.y) / 2;
\r
10155 /* Don't use as many frames for very short moves */
\r
10156 if (abs(toY - fromY) + abs(toX - fromX) <= 2)
\r
10157 Tween(&start, &mid, &finish, kFactor - 1, frames, &nFrames);
\r
10159 Tween(&start, &mid, &finish, kFactor, frames, &nFrames);
\r
10161 animInfo.to.x = toX;
\r
10162 animInfo.to.y = toY;
\r
10163 animInfo.lastpos = start;
\r
10164 animInfo.piece = piece;
\r
10165 for (n = 0; n < nFrames; n++) {
\r
10166 animInfo.pos = frames[n];
\r
10167 DrawPosition(FALSE, board);
\r
10168 animInfo.lastpos = animInfo.pos;
\r
10169 Sleep(appData.animSpeed);
\r
10171 animInfo.pos = finish;
\r
10172 DrawPosition(FALSE, board);
\r
10174 if(toX == x2 && toY == kill2Y) {
\r
10175 fromX = toX; fromY = toY; toX = killX; toY = killY; x2 = -1;
\r
10176 board[kill2Y][kill2X] = EmptySquare; goto again;
\r
10178 if(toX != x || toY != y) {
\r
10179 fromX = toX; fromY = toY; toX = x; toY = y;
\r
10180 board[killY][killX] = EmptySquare; goto again;
\r
10183 if(victim2 != EmptySquare) board[kill2Y][kill2X] = victim2;
\r
10184 if(victim != EmptySquare) board[killY][killX] = victim;
\r
10186 animInfo.piece = EmptySquare;
\r
10187 Explode(board, fromX, fromY, toX, toY);
\r
10190 /* Convert board position to corner of screen rect and color */
\r
10193 ScreenSquare(column, row, pt)
\r
10194 int column; int row; POINT * pt;
\r
10197 pt->x = lineGap + ((BOARD_WIDTH-1)-column) * (squareSize + lineGap) + border;
\r
10198 pt->y = lineGap + row * (squareSize + lineGap) + border;
\r
10200 pt->x = lineGap + column * (squareSize + lineGap) + border;
\r
10201 pt->y = lineGap + ((BOARD_HEIGHT-1)-row) * (squareSize + lineGap) + border;
\r
10205 /* Generate a series of frame coords from start->mid->finish.
\r
10206 The movement rate doubles until the half way point is
\r
10207 reached, then halves back down to the final destination,
\r
10208 which gives a nice slow in/out effect. The algorithmn
\r
10209 may seem to generate too many intermediates for short
\r
10210 moves, but remember that the purpose is to attract the
\r
10211 viewers attention to the piece about to be moved and
\r
10212 then to where it ends up. Too few frames would be less
\r
10216 Tween(start, mid, finish, factor, frames, nFrames)
\r
10217 POINT * start; POINT * mid;
\r
10218 POINT * finish; int factor;
\r
10219 POINT frames[]; int * nFrames;
\r
10221 int n, fraction = 1, count = 0;
\r
10223 /* Slow in, stepping 1/16th, then 1/8th, ... */
\r
10224 for (n = 0; n < factor; n++)
\r
10226 for (n = 0; n < factor; n++) {
\r
10227 frames[count].x = start->x + (mid->x - start->x) / fraction;
\r
10228 frames[count].y = start->y + (mid->y - start->y) / fraction;
\r
10230 fraction = fraction / 2;
\r
10234 frames[count] = *mid;
\r
10237 /* Slow out, stepping 1/2, then 1/4, ... */
\r
10239 for (n = 0; n < factor; n++) {
\r
10240 frames[count].x = finish->x - (finish->x - mid->x) / fraction;
\r
10241 frames[count].y = finish->y - (finish->y - mid->y) / fraction;
\r
10243 fraction = fraction * 2;
\r
10245 *nFrames = count;
\r
10249 SettingsPopUp(ChessProgramState *cps)
\r
10250 { // [HGM] wrapper needed because handles must not be passed through back-end
\r
10251 EngineOptionsPopup(savedHwnd, cps);
\r
10254 int flock(int fid, int code)
\r
10256 HANDLE hFile = (HANDLE) _get_osfhandle(fid);
\r
10258 ov.hEvent = NULL;
\r
10260 ov.OffsetHigh = 0;
\r
10262 case 1: LockFileEx(hFile, LOCKFILE_EXCLUSIVE_LOCK, 0, 1024, 0, &ov); break; // LOCK_SH
\r
10264 case 2: LockFileEx(hFile, LOCKFILE_EXCLUSIVE_LOCK, 0, 1024, 0, &ov); break; // LOCK_EX
\r
10265 case 3: UnlockFileEx(hFile, 0, 1024, 0, &ov); break; // LOCK_UN
\r
10266 default: return -1;
\r
10275 static char col[8][20];
\r
10276 COLORREF color = *(COLORREF *) colorVariable[n];
\r
10278 snprintf(col[i], 20, "#%02lx%02lx%02lx", color&0xff, (color>>8)&0xff, (color>>16)&0xff);
\r
10283 ActivateTheme (int new)
\r
10284 { // Redo initialization of features depending on options that can occur in themes
\r
10286 if(new) InitDrawingColors();
\r
10287 fontBitmapSquareSize = 0; // request creation of new font pieces
\r
10288 InitDrawingSizes(boardSize, 0);
\r
10289 InvalidateRect(hwndMain, NULL, TRUE);
\r