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 Free Software Foundation, Inc.
\r
10 * Enhancements Copyright 2005 Alessandro Scotti
\r
12 * XBoard borrows its colors and the bitmaps.xchess bitmap set from XChess,
\r
13 * which was written and is copyrighted by Wayne Christopher.
\r
15 * The following terms apply to Digital Equipment Corporation's copyright
\r
16 * interest in XBoard:
\r
17 * ------------------------------------------------------------------------
\r
18 * All Rights Reserved
\r
20 * Permission to use, copy, modify, and distribute this software and its
\r
21 * documentation for any purpose and without fee is hereby granted,
\r
22 * provided that the above copyright notice appear in all copies and that
\r
23 * both that copyright notice and this permission notice appear in
\r
24 * supporting documentation, and that the name of Digital not be
\r
25 * used in advertising or publicity pertaining to distribution of the
\r
26 * software without specific, written prior permission.
\r
28 * DIGITAL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
\r
29 * ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL
\r
30 * DIGITAL BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
\r
31 * ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
\r
32 * WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
\r
33 * ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
\r
35 * ------------------------------------------------------------------------
\r
37 * The following terms apply to the enhanced version of XBoard
\r
38 * distributed by the Free Software Foundation:
\r
39 * ------------------------------------------------------------------------
\r
41 * GNU XBoard is free software: you can redistribute it and/or modify
\r
42 * it under the terms of the GNU General Public License as published by
\r
43 * the Free Software Foundation, either version 3 of the License, or (at
\r
44 * your option) any later version.
\r
46 * GNU XBoard is distributed in the hope that it will be useful, but
\r
47 * WITHOUT ANY WARRANTY; without even the implied warranty of
\r
48 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
\r
49 * General Public License for more details.
\r
51 * You should have received a copy of the GNU General Public License
\r
52 * along with this program. If not, see http://www.gnu.org/licenses/. *
\r
54 *------------------------------------------------------------------------
\r
55 ** See the file ChangeLog for a revision history. */
\r
59 #include <windows.h>
\r
60 #include <winuser.h>
\r
61 #include <winsock.h>
\r
62 #include <commctrl.h>
\r
68 #include <sys/stat.h>
\r
71 #include <commdlg.h>
\r
73 #include <richedit.h>
\r
74 #include <mmsystem.h>
\r
83 #include "frontend.h"
\r
84 #include "backend.h"
\r
85 #include "winboard.h"
\r
87 #include "wclipbrd.h"
\r
88 #include "woptions.h"
\r
89 #include "wsockerr.h"
\r
90 #include "defaults.h"
\r
94 //void InitEngineUCI( const char * iniDir, ChessProgramState * cps );
\r
97 void mysrandom(unsigned int seed);
\r
99 extern int whiteFlag, blackFlag;
\r
100 Boolean flipClock = FALSE;
\r
101 extern HANDLE chatHandle[];
\r
102 extern int ics_type;
\r
104 void DisplayHoldingsCount(HDC hdc, int x, int y, int align, int copyNumber);
\r
105 VOID NewVariantPopup(HWND hwnd);
\r
106 int FinishMove P((ChessMove moveType, int fromX, int fromY, int toX, int toY,
\r
107 /*char*/int promoChar));
\r
108 void AnimateAtomicCapture(int fromX, int fromY, int toX, int toY, int nFrames);
\r
109 void DisplayMove P((int moveNumber));
\r
110 Boolean ParseFEN P((Board board, int *blackPlaysFirst, char *fen));
\r
111 void ChatPopUp P((char *s));
\r
113 ChessSquare piece;
\r
114 POINT pos; /* window coordinates of current pos */
\r
115 POINT lastpos; /* window coordinates of last pos - used for clipping */
\r
116 POINT from; /* board coordinates of the piece's orig pos */
\r
117 POINT to; /* board coordinates of the piece's new pos */
\r
120 static AnimInfo animInfo = { EmptySquare, {-1,-1}, {-1,-1}, {-1,-1} };
\r
123 POINT start; /* window coordinates of start pos */
\r
124 POINT pos; /* window coordinates of current pos */
\r
125 POINT lastpos; /* window coordinates of last pos - used for clipping */
\r
126 POINT from; /* board coordinates of the piece's orig pos */
\r
129 static DragInfo dragInfo = { {-1,-1}, {-1,-1}, {-1,-1}, {-1,-1} };
\r
132 POINT sq[2]; /* board coordinates of from, to squares */
\r
135 static HighlightInfo highlightInfo = { {{-1, -1}, {-1, -1}} };
\r
136 static HighlightInfo premoveHighlightInfo = { {{-1, -1}, {-1, -1}} };
\r
137 static HighlightInfo partnerHighlightInfo = { {{-1, -1}, {-1, -1}} };
\r
138 static HighlightInfo oldPartnerHighlight = { {{-1, -1}, {-1, -1}} };
\r
140 typedef struct { // [HGM] atomic
\r
141 int fromX, fromY, toX, toY, radius;
\r
144 static ExplodeInfo explodeInfo;
\r
146 /* Window class names */
\r
147 char szAppName[] = "WinBoard";
\r
148 char szConsoleName[] = "WBConsole";
\r
150 /* Title bar text */
\r
151 char szTitle[] = "WinBoard";
\r
152 char szConsoleTitle[] = "I C S Interaction";
\r
155 char *settingsFileName;
\r
156 Boolean saveSettingsOnExit;
\r
157 char installDir[MSG_SIZ];
\r
158 int errorExitStatus;
\r
160 BoardSize boardSize;
\r
161 Boolean chessProgram;
\r
162 //static int boardX, boardY;
\r
163 int minX, minY; // [HGM] placement: volatile limits on upper-left corner
\r
164 int squareSize, lineGap, minorSize;
\r
165 static int winW, winH;
\r
166 static RECT messageRect, whiteRect, blackRect, leftLogoRect, rightLogoRect; // [HGM] logo
\r
167 static int logoHeight = 0;
\r
168 static char messageText[MESSAGE_TEXT_MAX];
\r
169 static int clockTimerEvent = 0;
\r
170 static int loadGameTimerEvent = 0;
\r
171 static int analysisTimerEvent = 0;
\r
172 static DelayedEventCallback delayedTimerCallback;
\r
173 static int delayedTimerEvent = 0;
\r
174 static int buttonCount = 2;
\r
175 char *icsTextMenuString;
\r
177 char *firstChessProgramNames;
\r
178 char *secondChessProgramNames;
\r
180 #define PALETTESIZE 256
\r
182 HINSTANCE hInst; /* current instance */
\r
183 Boolean alwaysOnTop = FALSE;
\r
185 COLORREF lightSquareColor, darkSquareColor, whitePieceColor,
\r
186 blackPieceColor, highlightSquareColor, premoveHighlightColor;
\r
188 ColorClass currentColorClass;
\r
190 HWND hCommPort = NULL; /* currently open comm port */
\r
191 static HWND hwndPause; /* pause button */
\r
192 static HBITMAP pieceBitmap[3][(int) BlackPawn]; /* [HGM] nr of bitmaps referred to bP in stead of wK */
\r
193 static HBRUSH lightSquareBrush, darkSquareBrush,
\r
194 blackSquareBrush, /* [HGM] for band between board and holdings */
\r
195 explodeBrush, /* [HGM] atomic */
\r
196 markerBrush, /* [HGM] markers */
\r
197 whitePieceBrush, blackPieceBrush, iconBkgndBrush /*, outlineBrush*/;
\r
198 static POINT gridEndpoints[(BOARD_RANKS + BOARD_FILES + 2) * 2];
\r
199 static DWORD gridVertexCounts[BOARD_RANKS + BOARD_FILES + 2];
\r
200 static HPEN gridPen = NULL;
\r
201 static HPEN highlightPen = NULL;
\r
202 static HPEN premovePen = NULL;
\r
203 static NPLOGPALETTE pLogPal;
\r
204 static BOOL paletteChanged = FALSE;
\r
205 static HICON iconWhite, iconBlack, iconCurrent;
\r
206 static int doingSizing = FALSE;
\r
207 static int lastSizing = 0;
\r
208 static int prevStderrPort;
\r
209 static HBITMAP userLogo;
\r
211 static HBITMAP liteBackTexture = NULL;
\r
212 static HBITMAP darkBackTexture = NULL;
\r
213 static int liteBackTextureMode = BACK_TEXTURE_MODE_PLAIN;
\r
214 static int darkBackTextureMode = BACK_TEXTURE_MODE_PLAIN;
\r
215 static int backTextureSquareSize = 0;
\r
216 static struct { int x; int y; int mode; } backTextureSquareInfo[BOARD_RANKS][BOARD_FILES];
\r
218 #if __GNUC__ && !defined(_winmajor)
\r
219 #define oldDialog 0 /* cygwin doesn't define _winmajor; mingw does */
\r
221 #if defined(_winmajor)
\r
222 #define oldDialog (_winmajor < 4)
\r
224 #define oldDialog 0
\r
228 #define INTERNATIONAL
\r
230 #ifdef INTERNATIONAL
\r
231 # define _(s) T_(s)
\r
237 # define Translate(x, y)
\r
238 # define LoadLanguageFile(s)
\r
241 #ifdef INTERNATIONAL
\r
243 Boolean barbaric; // flag indicating if translation is needed
\r
245 // list of item numbers used in each dialog (used to alter language at run time)
\r
247 #define ABOUTBOX -1 /* not sure why these are needed */
\r
248 #define ABOUTBOX2 -1
\r
250 int dialogItems[][40] = {
\r
251 { ABOUTBOX, IDOK, 400 },
\r
252 { DLG_TimeControl, IDC_Babble, OPT_TCUseMoves, OPT_TCUseInc, OPT_TCUseFixed,
\r
253 OPT_TCtext1, OPT_TCtext2, OPT_TCitext1, OPT_TCitext2, OPT_TCftext, GPB_Factors, IDC_Factor1, IDC_Factor2, IDOK, IDCANCEL },
\r
254 { DLG_LoadOptions, OPT_Autostep, OPT_AStext1, IDOK, IDCANCEL },
\r
255 { DLG_SaveOptions, OPT_Autosave, OPT_AVPrompt, OPT_AVToFile, OPT_AVBrowse,
\r
256 801, OPT_PGN, OPT_Old, OPT_OutOfBookInfo, IDOK, IDCANCEL },
\r
257 { 1536, 1090, IDC_Directories, 1089, 1091, IDOK, IDCANCEL, 1038, IDC_IndexNr, 1037 },
\r
258 { DLG_CommPort, IDOK, IDCANCEL, IDC_Port, IDC_Rate, IDC_Bits, IDC_Parity,
\r
259 IDC_Stop, IDC_Flow, OPT_SerialHelp },
\r
260 { DLG_EditComment, IDOK, OPT_CancelComment, OPT_ClearComment, OPT_EditComment },
\r
261 { DLG_PromotionKing, PB_Chancellor, PB_Archbishop, PB_Queen, PB_Rook,
\r
262 PB_Bishop, PB_Knight, PB_King, IDCANCEL, IDC_Yes, IDC_No, IDC_Centaur },
\r
263 { ABOUTBOX2, IDC_ChessBoard },
\r
264 { DLG_GameList, OPT_GameListLoad, OPT_GameListPrev, OPT_GameListNext,
\r
265 OPT_GameListClose, IDC_GameListDoFilter },
\r
266 { DLG_EditTags, IDOK, OPT_TagsCancel, OPT_EditTags },
\r
267 { DLG_Error, IDOK },
\r
268 { DLG_Colorize, IDOK, IDCANCEL, OPT_ChooseColor, OPT_Bold, OPT_Italic,
\r
269 OPT_Underline, OPT_Strikeout, OPT_Sample },
\r
270 { DLG_Question, IDOK, IDCANCEL, OPT_QuestionText },
\r
271 { DLG_Startup, IDC_Welcome, OPT_ChessEngine, OPT_ChessServer, OPT_View,
\r
272 IDC_SPECIFY_ENG_STATIC, IDC_SPECIFY_SERVER_STATIC, OPT_AnyAdditional,
\r
273 IDOK, IDCANCEL, IDM_HELPCONTENTS },
\r
274 { DLG_IndexNumber, IDC_Index },
\r
275 { DLG_TypeInMove, IDOK, IDCANCEL },
\r
276 { DLG_TypeInName, IDOK, IDCANCEL },
\r
277 { DLG_Sound, IDC_Event, OPT_NoSound, OPT_DefaultBeep, OPT_BuiltInSound,
\r
278 OPT_WavFile, OPT_BrowseSound, OPT_DefaultSounds, IDOK, IDCANCEL, OPT_PlaySound },
\r
279 { DLG_GeneralOptions, IDOK, IDCANCEL, OPT_AlwaysOnTop, OPT_HighlightLastMove,
\r
280 OPT_AlwaysQueen, OPT_PeriodicUpdates, OPT_AnimateDragging, OPT_PonderNextMove,
\r
281 OPT_AnimateMoving, OPT_PopupExitMessage, OPT_AutoFlag, OPT_PopupMoveErrors,
\r
282 OPT_AutoFlipView, OPT_ShowButtonBar, OPT_AutoRaiseBoard, OPT_ShowCoordinates,
\r
283 OPT_Blindfold, OPT_ShowThinking, OPT_HighlightDragging, OPT_TestLegality,
\r
284 OPT_SaveExtPGN, OPT_HideThinkFromHuman, OPT_ExtraInfoInMoveHistory,
\r
285 OPT_HighlightMoveArrow, OPT_AutoLogo },
\r
286 { DLG_IcsOptions, IDOK, IDCANCEL, OPT_AutoComment, OPT_AutoKibitz, OPT_AutoObserve,
\r
287 OPT_GetMoveList, OPT_LocalLineEditing, OPT_QuietPlay, OPT_SeekGraph, OPT_AutoRefresh,
\r
288 OPT_BgObserve, OPT_DualBoard, OPT_Premove, OPT_PremoveWhite, OPT_PremoveBlack,
\r
289 OPT_SmartMove, OPT_IcsAlarm, IDC_Sec, OPT_ChooseShoutColor, OPT_ChooseSShoutColor,
\r
290 OPT_ChooseChannel1Color, OPT_ChooseChannelColor, OPT_ChooseKibitzColor,
\r
291 OPT_ChooseTellColor, OPT_ChooseChallengeColor, OPT_ChooseRequestColor,
\r
292 OPT_ChooseSeekColor, OPT_ChooseNormalColor, OPT_ChooseBackgroundColor,
\r
293 OPT_DefaultColors, OPT_DontColorize, IDC_Boxes, GPB_Colors, GPB_Premove,
\r
294 GPB_General, GPB_Alarm },
\r
295 { DLG_BoardOptions, IDOK, IDCANCEL, OPT_SizeTiny, OPT_SizeTeeny, OPT_SizeDinky,
\r
296 OPT_SizePetite, OPT_SizeSlim, OPT_SizeSmall, OPT_SizeMediocre, OPT_SizeMiddling,
\r
297 OPT_SizeAverage, OPT_SizeModerate, OPT_SizeMedium, OPT_SizeBulky, OPT_SizeLarge,
\r
298 OPT_SizeBig, OPT_SizeHuge, OPT_SizeGiant, OPT_SizeColossal, OPT_SizeTitanic,
\r
299 OPT_ChooseLightSquareColor, OPT_ChooseDarkSquareColor, OPT_ChooseWhitePieceColor,
\r
300 OPT_ChooseBlackPieceColor, OPT_ChooseHighlightSquareColor, OPT_ChoosePremoveHighlightColor,
\r
301 OPT_Monochrome, OPT_AllWhite, OPT_UpsideDown, OPT_DefaultBoardColors, GPB_Colors,
\r
302 IDC_Light, IDC_Dark, IDC_White, IDC_Black, IDC_High, IDC_PreHigh, GPB_Size },
\r
303 { DLG_NewVariant, IDOK, IDCANCEL, OPT_VariantNormal, OPT_VariantFRC, OPT_VariantWildcastle,
\r
304 OPT_VariantNocastle, OPT_VariantLosers, OPT_VariantGiveaway, OPT_VariantSuicide,
\r
305 OPT_Variant3Check, OPT_VariantTwoKings, OPT_VariantAtomic, OPT_VariantCrazyhouse,
\r
306 OPT_VariantBughouse, OPT_VariantTwilight, OPT_VariantShogi, OPT_VariantSuper,
\r
307 OPT_VariantKnightmate, OPT_VariantBerolina, OPT_VariantCylinder, OPT_VariantFairy,
\r
308 OPT_VariantMakruk, OPT_VariantGothic, OPT_VariantCapablanca, OPT_VariantJanus,
\r
309 OPT_VariantCRC, OPT_VariantFalcon, OPT_VariantCourier, OPT_VariantGreat,
\r
310 OPT_VariantShatranj, OPT_VariantXiangqi, GPB_Variant, GPB_Board, IDC_Height,
\r
311 IDC_Width, IDC_Hand, IDC_Pieces, IDC_Def },
\r
312 { DLG_Fonts, IDOK, IDCANCEL, OPT_ChooseClockFont, OPT_ChooseMessageFont,
\r
313 OPT_ChooseCoordFont, OPT_ChooseTagFont, OPT_ChooseCommentsFont, OPT_ChooseConsoleFont, OPT_ChooseMoveHistoryFont, OPT_DefaultFonts,
\r
314 OPT_ClockFont, OPT_MessageFont, OPT_CoordFont, OPT_EditTagsFont,
\r
315 OPT_CommentsFont, OPT_MessageFont5, GPB_Current, GPB_All, OPT_MessageFont6 },
\r
316 { DLG_NewGameFRC, IDC_NFG_Label, IDC_NFG_Random, IDOK, IDCANCEL },
\r
317 { DLG_GameListOptions, IDC_GLT, IDC_GLT_Up, IDC_GLT_Down, IDC_GLT_Restore,
\r
318 IDC_GLT_Default, IDOK, IDCANCEL, IDC_GLT_RestoreTo },
\r
319 { DLG_MoveHistory },
\r
320 { DLG_EvalGraph },
\r
321 { DLG_EngineOutput, IDC_EngineLabel1, IDC_Engine1_NPS, IDC_EngineLabel2, IDC_Engine2_NPS },
\r
322 { DLG_Chat, IDC_Partner, IDC_Clear, IDC_Send, },
\r
323 { DLG_EnginePlayOptions, IDC_EpPonder, IDC_EpShowThinking, IDC_EpHideThinkingHuman,
\r
324 IDC_EpPeriodicUpdates, GPB_Adjudications, IDC_Draw, IDC_Moves, IDC_Threshold,
\r
325 IDC_Centi, IDC_TestClaims, IDC_DetectMates, IDC_MaterialDraws, IDC_TrivialDraws,
\r
326 GPB_Apply, IDC_Rule, IDC_Repeats, IDC_ScoreAbs1, IDC_ScoreAbs2, IDOK, IDCANCEL },
\r
327 { DLG_OptionsUCI, IDC_PolyDir, IDC_BrowseForPolyglotDir, IDC_Hash, IDC_Path,
\r
328 IDC_BrowseForEGTB, IDC_Cache, IDC_UseBook, IDC_BrowseForBook, IDC_CPU, IDC_OwnBook1,
\r
329 IDC_OwnBook2, IDC_Depth, IDC_Variation, IDC_DefGames, IDOK, IDCANCEL },
\r
333 static char languageBuf[50000], *foreign[1000], *english[1000], *languageFile[MSG_SIZ];
\r
334 static int lastChecked;
\r
335 static char oldLanguage[MSG_SIZ], *menuText[10][30];
\r
336 extern int tinyLayout;
\r
337 extern char * menuBarText[][8];
\r
340 LoadLanguageFile(char *name)
\r
341 { //load the file with translations, and make a list of the strings to be translated, and their translations
\r
343 int i=0, j=0, n=0, k;
\r
346 if(!name || name[0] == NULLCHAR) return;
\r
347 snprintf(buf, MSG_SIZ, "%s%s", name, strchr(name, '.') ? "" : ".lng"); // auto-append lng extension
\r
348 if(!strcmp(buf, oldLanguage)) { barbaric = 1; return; } // this language already loaded; just switch on
\r
349 if((f = fopen(buf, "r")) == NULL) return;
\r
350 while((k = fgetc(f)) != EOF) {
\r
351 if(i >= sizeof(languageBuf)) { DisplayError("Language file too big", 0); return; }
\r
352 languageBuf[i] = k;
\r
354 if(languageBuf[n] == '"' && languageBuf[i-1] == '"') {
\r
356 if(p = strstr(languageBuf + n + 1, "\" === \"")) {
\r
357 if(p > languageBuf+n+2 && p+8 < languageBuf+i) {
\r
358 if(j >= sizeof(english)) { DisplayError("Too many translated strings", 0); return; }
\r
359 english[j] = languageBuf + n + 1; *p = 0;
\r
360 foreign[j++] = p + 7; languageBuf[i-1] = 0;
\r
361 //if(appData.debugMode) fprintf(debugFP, "translation: replace '%s' by '%s'\n", english[j-1], foreign[j-1]);
\r
366 } else if(i > 0 && languageBuf[i-1] == '\\') {
\r
368 case 'n': k = '\n'; break;
\r
369 case 'r': k = '\r'; break;
\r
370 case 't': k = '\t'; break;
\r
372 languageBuf[--i] = k;
\r
377 barbaric = (j != 0);
\r
378 safeStrCpy(oldLanguage, buf, sizeof(oldLanguage)/sizeof(oldLanguage[0]) );
\r
383 { // return the translation of the given string
\r
384 // efficiency can be improved a lot...
\r
386 //if(appData.debugMode) fprintf(debugFP, "T_(%s)\n", s);
\r
387 if(!barbaric) return s;
\r
388 if(!s) return ""; // sanity
\r
389 while(english[i]) {
\r
390 if(!strcmp(s, english[i])) return foreign[i];
\r
397 Translate(HWND hDlg, int dialogID)
\r
398 { // translate all text items in the given dialog
\r
400 char buf[MSG_SIZ], *s;
\r
401 //if(appData.debugMode) fprintf(debugFP, "Translate(%d)\n", dialogID);
\r
402 if(!barbaric) return;
\r
403 while(dialogItems[i][0] && dialogItems[i][0] != dialogID) i++; // find the dialog description
\r
404 if(dialogItems[i][0] != dialogID) return; // unknown dialog, should not happen
\r
405 GetWindowText( hDlg, buf, MSG_SIZ );
\r
407 //if(appData.debugMode) fprintf(debugFP, "WindowText '%s' -> '%s'\n", buf, s);
\r
408 if(strcmp(buf, s)) SetWindowText(hDlg, s); // replace by translated string (if different)
\r
409 for(j=1; k=dialogItems[i][j]; j++) { // translate all listed dialog items
\r
410 GetDlgItemText(hDlg, k, buf, MSG_SIZ);
\r
411 if(strlen(buf) == 0) continue;
\r
413 if(strcmp(buf, s)) SetDlgItemText(hDlg, k, s); // replace by translated string (if different)
\r
418 TranslateMenus(int addLanguage)
\r
421 WIN32_FIND_DATA fileData;
\r
423 #define IDM_English 1895
\r
425 HMENU mainMenu = GetMenu(hwndMain);
\r
426 for (i=GetMenuItemCount(mainMenu)-1; i>=0; i--) {
\r
427 HMENU subMenu = GetSubMenu(mainMenu, i);
\r
428 ModifyMenu(mainMenu, i, MF_STRING|MF_BYPOSITION|MF_POPUP|EnableMenuItem(mainMenu, i, MF_BYPOSITION),
\r
429 (UINT) subMenu, T_(menuBarText[tinyLayout][i]));
\r
430 for(j=GetMenuItemCount(subMenu)-1; j>=0; j--){
\r
432 UINT k = GetMenuItemID(subMenu, j);
\r
434 safeStrCpy(buf, menuText[i][j], sizeof(buf)/sizeof(buf[0]) ); else {
\r
435 GetMenuString(subMenu, j, buf, MSG_SIZ, MF_BYPOSITION);
\r
436 menuText[i][j] = strdup(buf); // remember original on first change
\r
438 if(buf[0] == NULLCHAR) continue;
\r
439 //fprintf(debugFP, "menu(%d,%d) = %s (%08x, %08x) %d\n", i, j, buf, mainMenu, subMenu, k);
\r
440 ModifyMenu(subMenu, j, MF_STRING|MF_BYPOSITION
\r
441 |CheckMenuItem(subMenu, j, MF_BYPOSITION)
\r
442 |EnableMenuItem(subMenu, j, MF_BYPOSITION), k, T_(buf));
\r
445 DrawMenuBar(hwndMain);
\r
448 if(!addLanguage) return;
\r
449 if((hFind = FindFirstFile("*.LNG", &fileData)) != INVALID_HANDLE_VALUE) {
\r
450 HMENU mainMenu = GetMenu(hwndMain);
\r
451 HMENU subMenu = GetSubMenu(mainMenu, GetMenuItemCount(mainMenu)-1);
\r
452 AppendMenu(subMenu, MF_SEPARATOR, (UINT_PTR) 0, NULL);
\r
453 AppendMenu(subMenu, MF_ENABLED|MF_STRING|(barbaric?MF_UNCHECKED:MF_CHECKED), (UINT_PTR) IDM_English, (LPCTSTR) "English");
\r
454 i = 0; lastChecked = IDM_English;
\r
456 char *p, *q = fileData.cFileName;
\r
457 int checkFlag = MF_UNCHECKED;
\r
458 languageFile[i] = strdup(q);
\r
459 if(barbaric && !strcmp(oldLanguage, q)) {
\r
460 checkFlag = MF_CHECKED;
\r
461 lastChecked = IDM_English + i + 1;
\r
462 CheckMenuItem(mainMenu, IDM_English, MF_BYCOMMAND|MF_UNCHECKED);
\r
464 *q = ToUpper(*q); while(*++q) *q = ToLower(*q);
\r
465 p = strstr(fileData.cFileName, ".lng");
\r
467 AppendMenu(subMenu, MF_ENABLED|MF_STRING|checkFlag, (UINT_PTR) IDM_English + ++i, (LPCTSTR) fileData.cFileName);
\r
468 } while(FindNextFile(hFind, &fileData));
\r
481 int cliWidth, cliHeight;
\r
484 SizeInfo sizeInfo[] =
\r
486 { "tiny", 21, 0, 1, 1, 0, 0 },
\r
487 { "teeny", 25, 1, 1, 1, 0, 0 },
\r
488 { "dinky", 29, 1, 1, 1, 0, 0 },
\r
489 { "petite", 33, 1, 1, 1, 0, 0 },
\r
490 { "slim", 37, 2, 1, 0, 0, 0 },
\r
491 { "small", 40, 2, 1, 0, 0, 0 },
\r
492 { "mediocre", 45, 2, 1, 0, 0, 0 },
\r
493 { "middling", 49, 2, 0, 0, 0, 0 },
\r
494 { "average", 54, 2, 0, 0, 0, 0 },
\r
495 { "moderate", 58, 3, 0, 0, 0, 0 },
\r
496 { "medium", 64, 3, 0, 0, 0, 0 },
\r
497 { "bulky", 72, 3, 0, 0, 0, 0 },
\r
498 { "large", 80, 3, 0, 0, 0, 0 },
\r
499 { "big", 87, 3, 0, 0, 0, 0 },
\r
500 { "huge", 95, 3, 0, 0, 0, 0 },
\r
501 { "giant", 108, 3, 0, 0, 0, 0 },
\r
502 { "colossal", 116, 4, 0, 0, 0, 0 },
\r
503 { "titanic", 129, 4, 0, 0, 0, 0 },
\r
504 { NULL, 0, 0, 0, 0, 0, 0 }
\r
507 #define MF(x) {x, {{0,}, 0. }, {0, }, 0}
\r
508 MyFont fontRec[NUM_SIZES][NUM_FONTS] =
\r
510 { 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) },
\r
511 { 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) },
\r
512 { 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) },
\r
513 { 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) },
\r
514 { 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) },
\r
515 { 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) },
\r
516 { 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) },
\r
517 { 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) },
\r
518 { 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) },
\r
519 { 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) },
\r
520 { 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) },
\r
521 { 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) },
\r
522 { 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) },
\r
523 { 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) },
\r
524 { 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) },
\r
525 { 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) },
\r
526 { 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) },
\r
527 { 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) },
\r
530 MyFont *font[NUM_SIZES][NUM_FONTS];
\r
539 #define BUTTON_WIDTH (tinyLayout ? 16 : 32)
\r
540 #define N_BUTTONS 5
\r
542 MyButtonDesc buttonDesc[N_BUTTONS] =
\r
544 {"<<", IDM_ToStart, NULL, NULL},
\r
545 {"<", IDM_Backward, NULL, NULL},
\r
546 {"P", IDM_Pause, NULL, NULL},
\r
547 {">", IDM_Forward, NULL, NULL},
\r
548 {">>", IDM_ToEnd, NULL, NULL},
\r
551 int tinyLayout = 0, smallLayout = 0;
\r
552 #define MENU_BAR_ITEMS 7
\r
553 char *menuBarText[2][MENU_BAR_ITEMS+1] = {
\r
554 { N_("&File"), N_("&Mode"), N_("&Action"), N_("&Step"), N_("&Options"), N_("&Help"), NULL },
\r
555 { N_("&F"), N_("&M"), N_("&A"), N_("&S"), N_("&O"), N_("&H"), NULL },
\r
559 MySound sounds[(int)NSoundClasses];
\r
560 MyTextAttribs textAttribs[(int)NColorClasses];
\r
562 MyColorizeAttribs colorizeAttribs[] = {
\r
563 { (COLORREF)0, 0, N_("Shout Text") },
\r
564 { (COLORREF)0, 0, N_("SShout/CShout") },
\r
565 { (COLORREF)0, 0, N_("Channel 1 Text") },
\r
566 { (COLORREF)0, 0, N_("Channel Text") },
\r
567 { (COLORREF)0, 0, N_("Kibitz Text") },
\r
568 { (COLORREF)0, 0, N_("Tell Text") },
\r
569 { (COLORREF)0, 0, N_("Challenge Text") },
\r
570 { (COLORREF)0, 0, N_("Request Text") },
\r
571 { (COLORREF)0, 0, N_("Seek Text") },
\r
572 { (COLORREF)0, 0, N_("Normal Text") },
\r
573 { (COLORREF)0, 0, N_("None") }
\r
578 static char *commentTitle;
\r
579 static char *commentText;
\r
580 static int commentIndex;
\r
581 static Boolean editComment = FALSE;
\r
584 char errorTitle[MSG_SIZ];
\r
585 char errorMessage[2*MSG_SIZ];
\r
586 HWND errorDialog = NULL;
\r
587 BOOLEAN moveErrorMessageUp = FALSE;
\r
588 BOOLEAN consoleEcho = TRUE;
\r
589 CHARFORMAT consoleCF;
\r
590 COLORREF consoleBackgroundColor;
\r
592 char *programVersion;
\r
598 typedef int CPKind;
\r
607 SOCKET sock2; /* stderr socket for OpenRcmd */
\r
610 #define INPUT_SOURCE_BUF_SIZE 4096
\r
612 typedef struct _InputSource {
\r
619 char buf[INPUT_SOURCE_BUF_SIZE];
\r
623 InputCallback func;
\r
624 struct _InputSource *second; /* for stderr thread on CPRcmd */
\r
628 InputSource *consoleInputSource;
\r
633 VOID ConsoleOutput(char* data, int length, int forceVisible);
\r
634 VOID ConsoleCreate();
\r
636 ConsoleWndProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam);
\r
637 VOID ColorizeTextPopup(HWND hwnd, ColorClass cc);
\r
638 VOID PrintCommSettings(FILE *f, char *name, DCB *dcb);
\r
639 VOID ParseCommSettings(char *arg, DCB *dcb);
\r
641 StartupDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam);
\r
642 VOID APIENTRY MenuPopup(HWND hwnd, POINT pt, HMENU hmenu, UINT def);
\r
643 void ParseIcsTextMenu(char *icsTextMenuString);
\r
644 VOID PopUpMoveDialog(char firstchar);
\r
645 VOID PopUpNameDialog(char firstchar);
\r
646 VOID UpdateSampleText(HWND hDlg, int id, MyColorizeAttribs *mca);
\r
650 int GameListOptions();
\r
652 int dummy; // [HGM] for obsolete args
\r
654 HWND hwndMain = NULL; /* root window*/
\r
655 HWND hwndConsole = NULL;
\r
656 HWND commentDialog = NULL;
\r
657 HWND moveHistoryDialog = NULL;
\r
658 HWND evalGraphDialog = NULL;
\r
659 HWND engineOutputDialog = NULL;
\r
660 HWND gameListDialog = NULL;
\r
661 HWND editTagsDialog = NULL;
\r
663 int commentUp = FALSE;
\r
665 WindowPlacement wpMain;
\r
666 WindowPlacement wpConsole;
\r
667 WindowPlacement wpComment;
\r
668 WindowPlacement wpMoveHistory;
\r
669 WindowPlacement wpEvalGraph;
\r
670 WindowPlacement wpEngineOutput;
\r
671 WindowPlacement wpGameList;
\r
672 WindowPlacement wpTags;
\r
674 VOID EngineOptionsPopup(); // [HGM] settings
\r
676 VOID GothicPopUp(char *title, VariantClass variant);
\r
678 * Setting "frozen" should disable all user input other than deleting
\r
679 * the window. We do this while engines are initializing themselves.
\r
681 static int frozen = 0;
\r
682 static int oldMenuItemState[MENU_BAR_ITEMS];
\r
688 if (frozen) return;
\r
690 hmenu = GetMenu(hwndMain);
\r
691 for (i=0; i<MENU_BAR_ITEMS; i++) {
\r
692 oldMenuItemState[i] = EnableMenuItem(hmenu, i, MF_BYPOSITION|MF_GRAYED);
\r
694 DrawMenuBar(hwndMain);
\r
697 /* Undo a FreezeUI */
\r
703 if (!frozen) return;
\r
705 hmenu = GetMenu(hwndMain);
\r
706 for (i=0; i<MENU_BAR_ITEMS; i++) {
\r
707 EnableMenuItem(hmenu, i, MF_BYPOSITION|oldMenuItemState[i]);
\r
709 DrawMenuBar(hwndMain);
\r
712 /*static*/ int fromX = -1, fromY = -1, toX, toY; // [HGM] moved upstream, so JAWS can use them
\r
714 /* JAWS preparation patch (WinBoard for the sight impaired). Define required insertions as empty */
\r
720 #define JAWS_ALT_INTERCEPT
\r
721 #define JAWS_KB_NAVIGATION
\r
722 #define JAWS_MENU_ITEMS
\r
723 #define JAWS_SILENCE
\r
724 #define JAWS_REPLAY
\r
726 #define JAWS_COPYRIGHT
\r
727 #define JAWS_DELETE(X) X
\r
728 #define SAYMACHINEMOVE()
\r
732 /*---------------------------------------------------------------------------*\
\r
736 \*---------------------------------------------------------------------------*/
\r
739 WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
\r
740 LPSTR lpCmdLine, int nCmdShow)
\r
743 HANDLE hAccelMain, hAccelNoAlt, hAccelNoICS;
\r
744 // INITCOMMONCONTROLSEX ex;
\r
748 LoadLibrary("RICHED32.DLL");
\r
749 consoleCF.cbSize = sizeof(CHARFORMAT);
\r
751 if (!InitApplication(hInstance)) {
\r
754 if (!InitInstance(hInstance, nCmdShow, lpCmdLine)) {
\r
760 // InitCommonControlsEx(&ex);
\r
761 InitCommonControls();
\r
763 hAccelMain = LoadAccelerators (hInstance, szAppName);
\r
764 hAccelNoAlt = LoadAccelerators (hInstance, "NO_ALT");
\r
765 hAccelNoICS = LoadAccelerators( hInstance, "NO_ICS"); /* [AS] No Ctrl-V on ICS!!! */
\r
767 /* Acquire and dispatch messages until a WM_QUIT message is received. */
\r
769 while (GetMessage(&msg, /* message structure */
\r
770 NULL, /* handle of window receiving the message */
\r
771 0, /* lowest message to examine */
\r
772 0)) /* highest message to examine */
\r
775 if(msg.message == WM_CHAR && msg.wParam == '\t') {
\r
776 // [HGM] navigate: switch between all windows with tab
\r
777 HWND e1 = NULL, e2 = NULL, mh = NULL, hInput = NULL, hText = NULL;
\r
778 int i, currentElement = 0;
\r
780 // first determine what element of the chain we come from (if any)
\r
781 if(appData.icsActive) {
\r
782 hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
783 hText = GetDlgItem(hwndConsole, OPT_ConsoleText);
\r
785 if(engineOutputDialog && EngineOutputIsUp()) {
\r
786 e1 = GetDlgItem(engineOutputDialog, IDC_EngineMemo1);
\r
787 e2 = GetDlgItem(engineOutputDialog, IDC_EngineMemo2);
\r
789 if(moveHistoryDialog && MoveHistoryIsUp()) {
\r
790 mh = GetDlgItem(moveHistoryDialog, IDC_MoveHistory);
\r
792 if(msg.hwnd == hwndMain) currentElement = 7 ; else
\r
793 if(msg.hwnd == engineOutputDialog) currentElement = 2; else
\r
794 if(msg.hwnd == e1) currentElement = 2; else
\r
795 if(msg.hwnd == e2) currentElement = 3; else
\r
796 if(msg.hwnd == moveHistoryDialog) currentElement = 4; else
\r
797 if(msg.hwnd == mh) currentElement = 4; else
\r
798 if(msg.hwnd == evalGraphDialog) currentElement = 6; else
\r
799 if(msg.hwnd == hText) currentElement = 5; else
\r
800 if(msg.hwnd == hInput) currentElement = 6; else
\r
801 for (i = 0; i < N_BUTTONS; i++) {
\r
802 if (buttonDesc[i].hwnd == msg.hwnd) { currentElement = 1; break; }
\r
805 // determine where to go to
\r
806 if(currentElement) { HWND h = NULL; int direction = GetKeyState(VK_SHIFT) < 0 ? -1 : 1;
\r
808 currentElement = (currentElement + direction) % 7;
\r
809 switch(currentElement) {
\r
811 h = hwndMain; break; // passing this case always makes the loop exit
\r
813 h = buttonDesc[0].hwnd; break; // could be NULL
\r
815 if(!EngineOutputIsUp()) continue; // skip closed auxiliary windows
\r
818 if(!EngineOutputIsUp()) continue;
\r
821 if(!MoveHistoryIsUp()) continue;
\r
823 // case 6: // input to eval graph does not seem to get here!
\r
824 // if(!EvalGraphIsUp()) continue;
\r
825 // h = evalGraphDialog; break;
\r
827 if(!appData.icsActive) continue;
\r
831 if(!appData.icsActive) continue;
\r
837 if(currentElement > 4 && IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);
\r
838 if(currentElement < 5 && IsIconic(hwndMain)) ShowWindow(hwndMain, SW_RESTORE); // all open together
\r
841 continue; // this message now has been processed
\r
845 if (!(commentDialog && IsDialogMessage(commentDialog, &msg)) &&
\r
846 !(moveHistoryDialog && IsDialogMessage(moveHistoryDialog, &msg)) &&
\r
847 !(evalGraphDialog && IsDialogMessage(evalGraphDialog, &msg)) &&
\r
848 !(engineOutputDialog && IsDialogMessage(engineOutputDialog, &msg)) &&
\r
849 !(editTagsDialog && IsDialogMessage(editTagsDialog, &msg)) &&
\r
850 !(gameListDialog && IsDialogMessage(gameListDialog, &msg)) &&
\r
851 !(errorDialog && IsDialogMessage(errorDialog, &msg)) &&
\r
852 !(!frozen && TranslateAccelerator(hwndMain, hAccelMain, &msg)) && JAWS_ACCEL
\r
853 !(!hwndConsole && TranslateAccelerator(hwndMain, hAccelNoICS, &msg)) &&
\r
854 !(!hwndConsole && TranslateAccelerator(hwndMain, hAccelNoAlt, &msg))) {
\r
855 int done = 0, i; // [HGM] chat: dispatch cat-box messages
\r
856 for(i=0; i<MAX_CHAT; i++)
\r
857 if(chatHandle[i] && IsDialogMessage(chatHandle[i], &msg)) {
\r
860 if(done) continue; // [HGM] chat: end patch
\r
861 TranslateMessage(&msg); /* Translates virtual key codes */
\r
862 DispatchMessage(&msg); /* Dispatches message to window */
\r
867 return (msg.wParam); /* Returns the value from PostQuitMessage */
\r
870 /*---------------------------------------------------------------------------*\
\r
872 * Initialization functions
\r
874 \*---------------------------------------------------------------------------*/
\r
878 { // update user logo if necessary
\r
879 static char oldUserName[MSG_SIZ], *curName;
\r
881 if(appData.autoLogo) {
\r
882 curName = UserName();
\r
883 if(strcmp(curName, oldUserName)) {
\r
884 snprintf(oldUserName, MSG_SIZ, "logos\\%s.bmp", curName);
\r
885 userLogo = LoadImage( 0, oldUserName, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );
\r
886 safeStrCpy(oldUserName, curName, sizeof(oldUserName)/sizeof(oldUserName[0]) );
\r
892 InitApplication(HINSTANCE hInstance)
\r
896 /* Fill in window class structure with parameters that describe the */
\r
899 wc.style = CS_HREDRAW | CS_VREDRAW; /* Class style(s). */
\r
900 wc.lpfnWndProc = (WNDPROC)WndProc; /* Window Procedure */
\r
901 wc.cbClsExtra = 0; /* No per-class extra data. */
\r
902 wc.cbWndExtra = 0; /* No per-window extra data. */
\r
903 wc.hInstance = hInstance; /* Owner of this class */
\r
904 wc.hIcon = LoadIcon(hInstance, "icon_white");
\r
905 wc.hCursor = LoadCursor(NULL, IDC_ARROW); /* Cursor */
\r
906 wc.hbrBackground = (HBRUSH)(COLOR_WINDOW+1); /* Default color */
\r
907 wc.lpszMenuName = szAppName; /* Menu name from .RC */
\r
908 wc.lpszClassName = szAppName; /* Name to register as */
\r
910 /* Register the window class and return success/failure code. */
\r
911 if (!RegisterClass(&wc)) return FALSE;
\r
913 wc.style = CS_HREDRAW | CS_VREDRAW;
\r
914 wc.lpfnWndProc = (WNDPROC)ConsoleWndProc;
\r
916 wc.cbWndExtra = DLGWINDOWEXTRA;
\r
917 wc.hInstance = hInstance;
\r
918 wc.hIcon = LoadIcon(hInstance, "icon_white");
\r
919 wc.hCursor = LoadCursor(NULL, IDC_ARROW);
\r
920 wc.hbrBackground = (HBRUSH)(COLOR_MENU+1);
\r
921 wc.lpszMenuName = NULL;
\r
922 wc.lpszClassName = szConsoleName;
\r
924 if (!RegisterClass(&wc)) return FALSE;
\r
929 /* Set by InitInstance, used by EnsureOnScreen */
\r
930 int screenHeight, screenWidth;
\r
933 EnsureOnScreen(int *x, int *y, int minX, int minY)
\r
935 // int gap = GetSystemMetrics(SM_CYFRAME) + GetSystemMetrics(SM_CYCAPTION);
\r
936 /* Be sure window at (x,y) is not off screen (or even mostly off screen) */
\r
937 if (*x > screenWidth - 32) *x = 0;
\r
938 if (*y > screenHeight - 32) *y = 0;
\r
939 if (*x < minX) *x = minX;
\r
940 if (*y < minY) *y = minY;
\r
944 InitInstance(HINSTANCE hInstance, int nCmdShow, LPSTR lpCmdLine)
\r
946 HWND hwnd; /* Main window handle. */
\r
948 WINDOWPLACEMENT wp;
\r
951 hInst = hInstance; /* Store instance handle in our global variable */
\r
952 programName = szAppName;
\r
954 if (SearchPath(NULL, "WinBoard.exe", NULL, MSG_SIZ, installDir, &filepart)) {
\r
955 *filepart = NULLCHAR;
\r
957 GetCurrentDirectory(MSG_SIZ, installDir);
\r
959 gameInfo.boardWidth = gameInfo.boardHeight = 8; // [HGM] won't have open window otherwise
\r
960 screenWidth = screenHeight = 1000; // [HGM] placement: kludge to allow calling EnsureOnScreen from InitAppData
\r
961 InitAppData(lpCmdLine); /* Get run-time parameters */
\r
962 /* xboard, and older WinBoards, controlled the move sound with the
\r
963 appData.ringBellAfterMoves option. In the current WinBoard, we
\r
964 always turn the option on (so that the backend will call us),
\r
965 then let the user turn the sound off by setting it to silence if
\r
966 desired. To accommodate old winboard.ini files saved by old
\r
967 versions of WinBoard, we also turn off the sound if the option
\r
968 was initially set to false. [HGM] taken out of InitAppData */
\r
969 if (!appData.ringBellAfterMoves) {
\r
970 sounds[(int)SoundMove].name = strdup("");
\r
971 appData.ringBellAfterMoves = TRUE;
\r
973 if (appData.debugMode) {
\r
974 debugFP = fopen(appData.nameOfDebugFile, "w");
\r
975 setbuf(debugFP, NULL);
\r
978 LoadLanguageFile(appData.language);
\r
982 // InitEngineUCI( installDir, &first ); // [HGM] incorporated in InitBackEnd1()
\r
983 // InitEngineUCI( installDir, &second );
\r
985 /* Create a main window for this application instance. */
\r
986 hwnd = CreateWindow(szAppName, szTitle,
\r
987 (WS_OVERLAPPEDWINDOW & ~WS_MAXIMIZEBOX),
\r
988 CW_USEDEFAULT, 0, CW_USEDEFAULT, 0,
\r
989 NULL, NULL, hInstance, NULL);
\r
992 /* If window could not be created, return "failure" */
\r
997 /* [HGM] logo: Load logos if specified (must be done before InitDrawingSizes) */
\r
998 if( appData.firstLogo && appData.firstLogo[0] != NULLCHAR) {
\r
999 first.programLogo = LoadImage( 0, appData.firstLogo, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );
\r
1001 if (first.programLogo == NULL && appData.debugMode) {
\r
1002 fprintf( debugFP, "Unable to load logo bitmap '%s'\n", appData.firstLogo );
\r
1004 } else if(appData.autoLogo) {
\r
1005 if(appData.firstDirectory && appData.firstDirectory[0]) {
\r
1006 char buf[MSG_SIZ];
\r
1007 snprintf(buf, MSG_SIZ, "%s/logo.bmp", appData.firstDirectory);
\r
1008 first.programLogo = LoadImage( 0, buf, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );
\r
1012 if( appData.secondLogo && appData.secondLogo[0] != NULLCHAR) {
\r
1013 second.programLogo = LoadImage( 0, appData.secondLogo, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );
\r
1015 if (second.programLogo == NULL && appData.debugMode) {
\r
1016 fprintf( debugFP, "Unable to load logo bitmap '%s'\n", appData.secondLogo );
\r
1018 } else if(appData.autoLogo) {
\r
1019 char buf[MSG_SIZ];
\r
1020 if(appData.icsActive) { // [HGM] logo: in ICS mode second can be used for ICS
\r
1021 snprintf(buf, MSG_SIZ, "logos\\%s.bmp", appData.icsHost);
\r
1022 second.programLogo = LoadImage( 0, buf, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );
\r
1024 if(appData.secondDirectory && appData.secondDirectory[0]) {
\r
1025 snprintf(buf, MSG_SIZ, "%s\\logo.bmp", appData.secondDirectory);
\r
1026 second.programLogo = LoadImage( 0, buf, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );
\r
1032 iconWhite = LoadIcon(hInstance, "icon_white");
\r
1033 iconBlack = LoadIcon(hInstance, "icon_black");
\r
1034 iconCurrent = iconWhite;
\r
1035 InitDrawingColors();
\r
1036 screenHeight = GetSystemMetrics(SM_CYSCREEN);
\r
1037 screenWidth = GetSystemMetrics(SM_CXSCREEN);
\r
1038 for (ibs = (int) NUM_SIZES - 1; ibs >= 0; ibs--) {
\r
1039 /* Compute window size for each board size, and use the largest
\r
1040 size that fits on this screen as the default. */
\r
1041 InitDrawingSizes((BoardSize)(ibs+1000), 0);
\r
1042 if (boardSize == (BoardSize)-1 &&
\r
1043 winH <= screenHeight
\r
1044 - GetSystemMetrics(SM_CYFRAME) - GetSystemMetrics(SM_CYCAPTION) - 10
\r
1045 && winW <= screenWidth) {
\r
1046 boardSize = (BoardSize)ibs;
\r
1050 InitDrawingSizes(boardSize, 0);
\r
1051 TranslateMenus(1);
\r
1053 buttonCount = GetSystemMetrics(SM_CMOUSEBUTTONS);
\r
1055 /* [AS] Load textures if specified */
\r
1056 ZeroMemory( &backTextureSquareInfo, sizeof(backTextureSquareInfo) );
\r
1058 if( appData.liteBackTextureFile && appData.liteBackTextureFile[0] != NULLCHAR && appData.liteBackTextureFile[0] != '*' ) {
\r
1059 liteBackTexture = LoadImage( 0, appData.liteBackTextureFile, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );
\r
1060 liteBackTextureMode = appData.liteBackTextureMode;
\r
1062 if (liteBackTexture == NULL && appData.debugMode) {
\r
1063 fprintf( debugFP, "Unable to load lite texture bitmap '%s'\n", appData.liteBackTextureFile );
\r
1067 if( appData.darkBackTextureFile && appData.darkBackTextureFile[0] != NULLCHAR && appData.darkBackTextureFile[0] != '*' ) {
\r
1068 darkBackTexture = LoadImage( 0, appData.darkBackTextureFile, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );
\r
1069 darkBackTextureMode = appData.darkBackTextureMode;
\r
1071 if (darkBackTexture == NULL && appData.debugMode) {
\r
1072 fprintf( debugFP, "Unable to load dark texture bitmap '%s'\n", appData.darkBackTextureFile );
\r
1076 mysrandom( (unsigned) time(NULL) );
\r
1078 /* [AS] Restore layout */
\r
1079 if( wpMoveHistory.visible ) {
\r
1080 MoveHistoryPopUp();
\r
1083 if( wpEvalGraph.visible ) {
\r
1087 if( wpEngineOutput.visible ) {
\r
1088 EngineOutputPopUp();
\r
1091 /* Make the window visible; update its client area; and return "success" */
\r
1092 EnsureOnScreen(&wpMain.x, &wpMain.y, minX, minY);
\r
1093 wp.length = sizeof(WINDOWPLACEMENT);
\r
1095 wp.showCmd = nCmdShow;
\r
1096 wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;
\r
1097 wp.rcNormalPosition.left = wpMain.x;
\r
1098 wp.rcNormalPosition.right = wpMain.x + wpMain.width;
\r
1099 wp.rcNormalPosition.top = wpMain.y;
\r
1100 wp.rcNormalPosition.bottom = wpMain.y + wpMain.height;
\r
1101 SetWindowPlacement(hwndMain, &wp);
\r
1103 InitBackEnd2(); // [HGM] moved until after all windows placed, to save correct position if fatal error on engine start
\r
1105 if(!appData.noGUI) SetWindowPos(hwndMain, alwaysOnTop ? HWND_TOPMOST : HWND_NOTOPMOST,
\r
1106 0, 0, 0, 0, SWP_NOMOVE|SWP_NOSIZE);
\r
1108 if (hwndConsole) {
\r
1110 SetWindowPos(hwndConsole, alwaysOnTop ? HWND_TOPMOST : HWND_NOTOPMOST,
\r
1111 0, 0, 0, 0, SWP_NOMOVE|SWP_NOSIZE);
\r
1113 ShowWindow(hwndConsole, nCmdShow);
\r
1114 if(appData.chatBoxes) { // [HGM] chat: open chat boxes
\r
1115 char buf[MSG_SIZ], *p = buf, *q;
\r
1116 safeStrCpy(buf, appData.chatBoxes, sizeof(buf)/sizeof(buf[0]) );
\r
1118 q = strchr(p, ';');
\r
1120 if(*p) ChatPopUp(p);
\r
1123 SetActiveWindow(hwndConsole);
\r
1125 if(!appData.noGUI) UpdateWindow(hwnd); else ShowWindow(hwnd, SW_MINIMIZE);
\r
1126 if(gameListDialog) SetFocus(gameListDialog); // [HGM] jaws: for if we clicked multi-game game file
\r
1135 HMENU hmenu = GetMenu(hwndMain);
\r
1137 (void) EnableMenuItem(hmenu, IDM_CommPort,
\r
1138 MF_BYCOMMAND|((appData.icsActive &&
\r
1139 *appData.icsCommPort != NULLCHAR) ?
\r
1140 MF_ENABLED : MF_GRAYED));
\r
1141 (void) CheckMenuItem(hmenu, IDM_SaveSettingsOnExit,
\r
1142 MF_BYCOMMAND|(saveSettingsOnExit ?
\r
1143 MF_CHECKED : MF_UNCHECKED));
\r
1146 //---------------------------------------------------------------------------------------------------------
\r
1148 #define ICS_TEXT_MENU_SIZE (IDM_CommandXLast - IDM_CommandX + 1)
\r
1149 #define XBOARD FALSE
\r
1151 #define OPTCHAR "/"
\r
1152 #define SEPCHAR "="
\r
1156 // front-end part of option handling
\r
1159 LFfromMFP(LOGFONT* lf, MyFontParams *mfp)
\r
1161 HDC hdc = CreateDC("DISPLAY", NULL, NULL, NULL);
\r
1162 lf->lfHeight = -(int)(mfp->pointSize * GetDeviceCaps(hdc, LOGPIXELSY) / 72.0 + 0.5);
\r
1165 lf->lfEscapement = 0;
\r
1166 lf->lfOrientation = 0;
\r
1167 lf->lfWeight = mfp->bold ? FW_BOLD : FW_NORMAL;
\r
1168 lf->lfItalic = mfp->italic;
\r
1169 lf->lfUnderline = mfp->underline;
\r
1170 lf->lfStrikeOut = mfp->strikeout;
\r
1171 lf->lfCharSet = mfp->charset;
\r
1172 lf->lfOutPrecision = OUT_DEFAULT_PRECIS;
\r
1173 lf->lfClipPrecision = CLIP_DEFAULT_PRECIS;
\r
1174 lf->lfQuality = DEFAULT_QUALITY;
\r
1175 lf->lfPitchAndFamily = DEFAULT_PITCH|FF_DONTCARE;
\r
1176 safeStrCpy(lf->lfFaceName, mfp->faceName, sizeof(lf->lfFaceName)/sizeof(lf->lfFaceName[0]) );
\r
1180 CreateFontInMF(MyFont *mf)
\r
1182 LFfromMFP(&mf->lf, &mf->mfp);
\r
1183 if (mf->hf) DeleteObject(mf->hf);
\r
1184 mf->hf = CreateFontIndirect(&mf->lf);
\r
1187 // [HGM] This platform-dependent table provides the location for storing the color info
\r
1189 colorVariable[] = {
\r
1190 &whitePieceColor,
\r
1191 &blackPieceColor,
\r
1192 &lightSquareColor,
\r
1193 &darkSquareColor,
\r
1194 &highlightSquareColor,
\r
1195 &premoveHighlightColor,
\r
1197 &consoleBackgroundColor,
\r
1198 &appData.fontForeColorWhite,
\r
1199 &appData.fontBackColorWhite,
\r
1200 &appData.fontForeColorBlack,
\r
1201 &appData.fontBackColorBlack,
\r
1202 &appData.evalHistColorWhite,
\r
1203 &appData.evalHistColorBlack,
\r
1204 &appData.highlightArrowColor,
\r
1207 /* Command line font name parser. NULL name means do nothing.
\r
1208 Syntax like "Courier New:10.0 bi" or "Arial:10" or "Arial:10b"
\r
1209 For backward compatibility, syntax without the colon is also
\r
1210 accepted, but font names with digits in them won't work in that case.
\r
1213 ParseFontName(char *name, MyFontParams *mfp)
\r
1216 if (name == NULL) return;
\r
1218 q = strchr(p, ':');
\r
1220 if (q - p >= sizeof(mfp->faceName))
\r
1221 ExitArgError(_("Font name too long:"), name);
\r
1222 memcpy(mfp->faceName, p, q - p);
\r
1223 mfp->faceName[q - p] = NULLCHAR;
\r
1226 q = mfp->faceName;
\r
1227 while (*p && !isdigit(*p)) {
\r
1229 if (q - mfp->faceName >= sizeof(mfp->faceName))
\r
1230 ExitArgError(_("Font name too long:"), name);
\r
1232 while (q > mfp->faceName && q[-1] == ' ') q--;
\r
1235 if (!*p) ExitArgError(_("Font point size missing:"), name);
\r
1236 mfp->pointSize = (float) atof(p);
\r
1237 mfp->bold = (strchr(p, 'b') != NULL);
\r
1238 mfp->italic = (strchr(p, 'i') != NULL);
\r
1239 mfp->underline = (strchr(p, 'u') != NULL);
\r
1240 mfp->strikeout = (strchr(p, 's') != NULL);
\r
1241 mfp->charset = DEFAULT_CHARSET;
\r
1242 q = strchr(p, 'c');
\r
1244 mfp->charset = (BYTE) atoi(q+1);
\r
1248 ParseFont(char *name, int number)
\r
1249 { // wrapper to shield back-end from 'font'
\r
1250 ParseFontName(name, &font[boardSize][number]->mfp);
\r
1255 { // in WB we have a 2D array of fonts; this initializes their description
\r
1257 /* Point font array elements to structures and
\r
1258 parse default font names */
\r
1259 for (i=0; i<NUM_FONTS; i++) {
\r
1260 for (j=0; j<NUM_SIZES; j++) {
\r
1261 font[j][i] = &fontRec[j][i];
\r
1262 ParseFontName(font[j][i]->def, &font[j][i]->mfp);
\r
1269 { // here we create the actual fonts from the selected descriptions
\r
1271 for (i=0; i<NUM_FONTS; i++) {
\r
1272 for (j=0; j<NUM_SIZES; j++) {
\r
1273 CreateFontInMF(font[j][i]);
\r
1277 /* Color name parser.
\r
1278 X version accepts X color names, but this one
\r
1279 handles only the #rrggbb form (hex) or rrr,ggg,bbb (decimal) */
\r
1281 ParseColorName(char *name)
\r
1283 int red, green, blue, count;
\r
1284 char buf[MSG_SIZ];
\r
1286 count = sscanf(name, "#%2x%2x%2x", &red, &green, &blue);
\r
1288 count = sscanf(name, "%3d%*[^0-9]%3d%*[^0-9]%3d",
\r
1289 &red, &green, &blue);
\r
1292 snprintf(buf, MSG_SIZ, _("Can't parse color name %s"), name);
\r
1293 DisplayError(buf, 0);
\r
1294 return RGB(0, 0, 0);
\r
1296 return PALETTERGB(red, green, blue);
\r
1300 ParseColor(int n, char *name)
\r
1301 { // for WinBoard the color is an int, which needs to be derived from the string
\r
1302 if(colorVariable[n]) *(int*)colorVariable[n] = ParseColorName(name);
\r
1306 ParseAttribs(COLORREF *color, int *effects, char* argValue)
\r
1308 char *e = argValue;
\r
1312 if (*e == 'b') eff |= CFE_BOLD;
\r
1313 else if (*e == 'i') eff |= CFE_ITALIC;
\r
1314 else if (*e == 'u') eff |= CFE_UNDERLINE;
\r
1315 else if (*e == 's') eff |= CFE_STRIKEOUT;
\r
1316 else if (*e == '#' || isdigit(*e)) break;
\r
1320 *color = ParseColorName(e);
\r
1324 ParseTextAttribs(ColorClass cc, char *s)
\r
1325 { // [HGM] front-end wrapper that does the platform-dependent call
\r
1326 // for XBoard we would set (&appData.colorShout)[cc] = strdup(s);
\r
1327 ParseAttribs(&textAttribs[cc].color, &textAttribs[cc].effects, s);
\r
1331 ParseBoardSize(void *addr, char *name)
\r
1332 { // [HGM] rewritten with return-value ptr to shield back-end from BoardSize
\r
1333 BoardSize bs = SizeTiny;
\r
1334 while (sizeInfo[bs].name != NULL) {
\r
1335 if (StrCaseCmp(name, sizeInfo[bs].name) == 0) {
\r
1336 *(BoardSize *)addr = bs;
\r
1341 ExitArgError(_("Unrecognized board size value"), name);
\r
1346 { // [HGM] import name from appData first
\r
1349 for (cc = (ColorClass)0; cc < ColorNormal; cc++) {
\r
1350 textAttribs[cc].sound.name = strdup((&appData.soundShout)[cc]);
\r
1351 textAttribs[cc].sound.data = NULL;
\r
1352 MyLoadSound(&textAttribs[cc].sound);
\r
1354 for (cc = ColorNormal; cc < NColorClasses; cc++) {
\r
1355 textAttribs[cc].sound.name = strdup("");
\r
1356 textAttribs[cc].sound.data = NULL;
\r
1358 for (sc = (SoundClass)0; sc < NSoundClasses; sc++) {
\r
1359 sounds[sc].name = strdup((&appData.soundMove)[sc]);
\r
1360 sounds[sc].data = NULL;
\r
1361 MyLoadSound(&sounds[sc]);
\r
1366 SetCommPortDefaults()
\r
1368 memset(&dcb, 0, sizeof(DCB)); // required by VS 2002 +
\r
1369 dcb.DCBlength = sizeof(DCB);
\r
1370 dcb.BaudRate = 9600;
\r
1371 dcb.fBinary = TRUE;
\r
1372 dcb.fParity = FALSE;
\r
1373 dcb.fOutxCtsFlow = FALSE;
\r
1374 dcb.fOutxDsrFlow = FALSE;
\r
1375 dcb.fDtrControl = DTR_CONTROL_ENABLE;
\r
1376 dcb.fDsrSensitivity = FALSE;
\r
1377 dcb.fTXContinueOnXoff = TRUE;
\r
1378 dcb.fOutX = FALSE;
\r
1380 dcb.fNull = FALSE;
\r
1381 dcb.fRtsControl = RTS_CONTROL_ENABLE;
\r
1382 dcb.fAbortOnError = FALSE;
\r
1384 dcb.Parity = SPACEPARITY;
\r
1385 dcb.StopBits = ONESTOPBIT;
\r
1388 // [HGM] args: these three cases taken out to stay in front-end
\r
1390 SaveFontArg(FILE *f, ArgDescriptor *ad)
\r
1391 { // in WinBoard every board size has its own font, and the "argLoc" identifies the table,
\r
1392 // while the curent board size determines the element. This system should be ported to XBoard.
\r
1393 // What the table contains pointers to, and how to print the font description, remains platform-dependent
\r
1395 for (bs=0; bs<NUM_SIZES; bs++) {
\r
1396 MyFontParams *mfp = &font[bs][(int) ad->argLoc]->mfp;
\r
1397 fprintf(f, "/size=%s ", sizeInfo[bs].name);
\r
1398 fprintf(f, "/%s=\"%s:%g%s%s%s%s%sc%d\"\n",
\r
1399 ad->argName, mfp->faceName, mfp->pointSize,
\r
1400 mfp->bold || mfp->italic || mfp->underline || mfp->strikeout ? " " : "",
\r
1401 mfp->bold ? "b" : "",
\r
1402 mfp->italic ? "i" : "",
\r
1403 mfp->underline ? "u" : "",
\r
1404 mfp->strikeout ? "s" : "",
\r
1405 (int)mfp->charset);
\r
1411 { // [HGM] copy the names from the internal WB variables to appData
\r
1414 for (cc = (ColorClass)0; cc < ColorNormal; cc++)
\r
1415 (&appData.soundShout)[cc] = textAttribs[cc].sound.name;
\r
1416 for (sc = (SoundClass)0; sc < NSoundClasses; sc++)
\r
1417 (&appData.soundMove)[sc] = sounds[sc].name;
\r
1421 SaveAttribsArg(FILE *f, ArgDescriptor *ad)
\r
1422 { // here the "argLoc" defines a table index. It could have contained the 'ta' pointer itself, though
\r
1423 MyTextAttribs* ta = &textAttribs[(ColorClass)ad->argLoc];
\r
1424 fprintf(f, "/%s=\"%s%s%s%s%s#%02lx%02lx%02lx\"\n", ad->argName,
\r
1425 (ta->effects & CFE_BOLD) ? "b" : "",
\r
1426 (ta->effects & CFE_ITALIC) ? "i" : "",
\r
1427 (ta->effects & CFE_UNDERLINE) ? "u" : "",
\r
1428 (ta->effects & CFE_STRIKEOUT) ? "s" : "",
\r
1429 (ta->effects) ? " " : "",
\r
1430 ta->color&0xff, (ta->color >> 8)&0xff, (ta->color >> 16)&0xff);
\r
1434 SaveColor(FILE *f, ArgDescriptor *ad)
\r
1435 { // in WinBoard the color is an int and has to be converted to text. In X it would be a string already?
\r
1436 COLORREF color = *(COLORREF *)colorVariable[(int)ad->argLoc];
\r
1437 fprintf(f, "/%s=#%02lx%02lx%02lx\n", ad->argName,
\r
1438 color&0xff, (color>>8)&0xff, (color>>16)&0xff);
\r
1442 SaveBoardSize(FILE *f, char *name, void *addr)
\r
1443 { // wrapper to shield back-end from BoardSize & sizeInfo
\r
1444 fprintf(f, "/%s=%s\n", name, sizeInfo[*(BoardSize *)addr].name);
\r
1448 ParseCommPortSettings(char *s)
\r
1449 { // wrapper to keep dcb from back-end
\r
1450 ParseCommSettings(s, &dcb);
\r
1455 { // wrapper to shield use of window handles from back-end (make addressible by number?)
\r
1456 GetActualPlacement(hwndMain, &wpMain);
\r
1457 GetActualPlacement(hwndConsole, &wpConsole);
\r
1458 GetActualPlacement(commentDialog, &wpComment);
\r
1459 GetActualPlacement(editTagsDialog, &wpTags);
\r
1460 GetActualPlacement(gameListDialog, &wpGameList);
\r
1461 GetActualPlacement(moveHistoryDialog, &wpMoveHistory);
\r
1462 GetActualPlacement(evalGraphDialog, &wpEvalGraph);
\r
1463 GetActualPlacement(engineOutputDialog, &wpEngineOutput);
\r
1467 PrintCommPortSettings(FILE *f, char *name)
\r
1468 { // wrapper to shield back-end from DCB
\r
1469 PrintCommSettings(f, name, &dcb);
\r
1473 MySearchPath(char *installDir, char *name, char *fullname)
\r
1475 char *dummy, buf[MSG_SIZ], *p = name, *q;
\r
1476 if(name[0]== '%') {
\r
1477 fullname[0] = 0; // [HGM] first expand any environment variables in the given name
\r
1478 while(*p == '%' && (q = strchr(p+1, '%'))) { // [HGM] recognize %*% as environment variable
\r
1479 safeStrCpy(buf, p+1, sizeof(buf)/sizeof(buf[0]) );
\r
1480 *strchr(buf, '%') = 0;
\r
1481 strcat(fullname, getenv(buf));
\r
1482 p = q+1; while(*p == '\\') { strcat(fullname, "\\"); p++; }
\r
1484 strcat(fullname, p); // after environment variables (if any), take the remainder of the given name
\r
1485 if(appData.debugMode) fprintf(debugFP, "name = '%s', expanded name = '%s'\n", name, fullname);
\r
1486 return (int) strlen(fullname);
\r
1488 return (int) SearchPath(installDir, name, NULL, MSG_SIZ, fullname, &dummy);
\r
1492 MyGetFullPathName(char *name, char *fullname)
\r
1495 return (int) GetFullPathName(name, MSG_SIZ, fullname, &dummy);
\r
1500 { // [HGM] args: allows testing if main window is realized from back-end
\r
1501 return hwndMain != NULL;
\r
1505 PopUpStartupDialog()
\r
1509 LoadLanguageFile(appData.language);
\r
1510 lpProc = MakeProcInstance((FARPROC)StartupDialog, hInst);
\r
1511 DialogBox(hInst, MAKEINTRESOURCE(DLG_Startup), NULL, (DLGPROC)lpProc);
\r
1512 FreeProcInstance(lpProc);
\r
1515 /*---------------------------------------------------------------------------*\
\r
1517 * GDI board drawing routines
\r
1519 \*---------------------------------------------------------------------------*/
\r
1521 /* [AS] Draw square using background texture */
\r
1522 static void DrawTile( int dx, int dy, int dw, int dh, HDC dst, HDC src, int mode, int sx, int sy )
\r
1527 return; /* Should never happen! */
\r
1530 SetGraphicsMode( dst, GM_ADVANCED );
\r
1537 /* X reflection */
\r
1542 x.eDx = (FLOAT) dw + dx - 1;
\r
1545 SetWorldTransform( dst, &x );
\r
1548 /* Y reflection */
\r
1554 x.eDy = (FLOAT) dh + dy - 1;
\r
1556 SetWorldTransform( dst, &x );
\r
1564 x.eDx = (FLOAT) dx;
\r
1565 x.eDy = (FLOAT) dy;
\r
1568 SetWorldTransform( dst, &x );
\r
1572 BitBlt( dst, dx, dy, dw, dh, src, sx, sy, SRCCOPY );
\r
1580 SetWorldTransform( dst, &x );
\r
1582 ModifyWorldTransform( dst, 0, MWT_IDENTITY );
\r
1585 /* [AS] [HGM] Make room for more piece types, so all pieces can be different */
\r
1587 PM_WP = (int) WhitePawn,
\r
1588 PM_WN = (int) WhiteKnight,
\r
1589 PM_WB = (int) WhiteBishop,
\r
1590 PM_WR = (int) WhiteRook,
\r
1591 PM_WQ = (int) WhiteQueen,
\r
1592 PM_WF = (int) WhiteFerz,
\r
1593 PM_WW = (int) WhiteWazir,
\r
1594 PM_WE = (int) WhiteAlfil,
\r
1595 PM_WM = (int) WhiteMan,
\r
1596 PM_WO = (int) WhiteCannon,
\r
1597 PM_WU = (int) WhiteUnicorn,
\r
1598 PM_WH = (int) WhiteNightrider,
\r
1599 PM_WA = (int) WhiteAngel,
\r
1600 PM_WC = (int) WhiteMarshall,
\r
1601 PM_WAB = (int) WhiteCardinal,
\r
1602 PM_WD = (int) WhiteDragon,
\r
1603 PM_WL = (int) WhiteLance,
\r
1604 PM_WS = (int) WhiteCobra,
\r
1605 PM_WV = (int) WhiteFalcon,
\r
1606 PM_WSG = (int) WhiteSilver,
\r
1607 PM_WG = (int) WhiteGrasshopper,
\r
1608 PM_WK = (int) WhiteKing,
\r
1609 PM_BP = (int) BlackPawn,
\r
1610 PM_BN = (int) BlackKnight,
\r
1611 PM_BB = (int) BlackBishop,
\r
1612 PM_BR = (int) BlackRook,
\r
1613 PM_BQ = (int) BlackQueen,
\r
1614 PM_BF = (int) BlackFerz,
\r
1615 PM_BW = (int) BlackWazir,
\r
1616 PM_BE = (int) BlackAlfil,
\r
1617 PM_BM = (int) BlackMan,
\r
1618 PM_BO = (int) BlackCannon,
\r
1619 PM_BU = (int) BlackUnicorn,
\r
1620 PM_BH = (int) BlackNightrider,
\r
1621 PM_BA = (int) BlackAngel,
\r
1622 PM_BC = (int) BlackMarshall,
\r
1623 PM_BG = (int) BlackGrasshopper,
\r
1624 PM_BAB = (int) BlackCardinal,
\r
1625 PM_BD = (int) BlackDragon,
\r
1626 PM_BL = (int) BlackLance,
\r
1627 PM_BS = (int) BlackCobra,
\r
1628 PM_BV = (int) BlackFalcon,
\r
1629 PM_BSG = (int) BlackSilver,
\r
1630 PM_BK = (int) BlackKing
\r
1633 static HFONT hPieceFont = NULL;
\r
1634 static HBITMAP hPieceMask[(int) EmptySquare];
\r
1635 static HBITMAP hPieceFace[(int) EmptySquare];
\r
1636 static int fontBitmapSquareSize = 0;
\r
1637 static char pieceToFontChar[(int) EmptySquare] =
\r
1638 { 'p', 'n', 'b', 'r', 'q',
\r
1639 'n', 'b', 'p', 'n', 'b', 'r', 'b', 'r', 'q', 'k',
\r
1640 'k', 'o', 'm', 'v', 't', 'w',
\r
1641 'v', 't', 'o', 'm', 'v', 't', 'v', 't', 'w', 'l',
\r
1644 extern BOOL SetCharTable( char *table, const char * map );
\r
1645 /* [HGM] moved to backend.c */
\r
1647 static void SetPieceBackground( HDC hdc, COLORREF color, int mode )
\r
1650 BYTE r1 = GetRValue( color );
\r
1651 BYTE g1 = GetGValue( color );
\r
1652 BYTE b1 = GetBValue( color );
\r
1658 /* Create a uniform background first */
\r
1659 hbrush = CreateSolidBrush( color );
\r
1660 SetRect( &rc, 0, 0, squareSize, squareSize );
\r
1661 FillRect( hdc, &rc, hbrush );
\r
1662 DeleteObject( hbrush );
\r
1665 /* Vertical gradient, good for pawn, knight and rook, less for queen and king */
\r
1666 int steps = squareSize / 2;
\r
1669 for( i=0; i<steps; i++ ) {
\r
1670 BYTE r = r1 - (r1-r2) * i / steps;
\r
1671 BYTE g = g1 - (g1-g2) * i / steps;
\r
1672 BYTE b = b1 - (b1-b2) * i / steps;
\r
1674 hbrush = CreateSolidBrush( RGB(r,g,b) );
\r
1675 SetRect( &rc, i + squareSize - steps, 0, i + squareSize - steps + 1, squareSize );
\r
1676 FillRect( hdc, &rc, hbrush );
\r
1677 DeleteObject(hbrush);
\r
1680 else if( mode == 2 ) {
\r
1681 /* Diagonal gradient, good more or less for every piece */
\r
1682 POINT triangle[3];
\r
1683 HPEN hpen = SelectObject( hdc, GetStockObject(NULL_PEN) );
\r
1684 HBRUSH hbrush_old;
\r
1685 int steps = squareSize;
\r
1688 triangle[0].x = squareSize - steps;
\r
1689 triangle[0].y = squareSize;
\r
1690 triangle[1].x = squareSize;
\r
1691 triangle[1].y = squareSize;
\r
1692 triangle[2].x = squareSize;
\r
1693 triangle[2].y = squareSize - steps;
\r
1695 for( i=0; i<steps; i++ ) {
\r
1696 BYTE r = r1 - (r1-r2) * i / steps;
\r
1697 BYTE g = g1 - (g1-g2) * i / steps;
\r
1698 BYTE b = b1 - (b1-b2) * i / steps;
\r
1700 hbrush = CreateSolidBrush( RGB(r,g,b) );
\r
1701 hbrush_old = SelectObject( hdc, hbrush );
\r
1702 Polygon( hdc, triangle, 3 );
\r
1703 SelectObject( hdc, hbrush_old );
\r
1704 DeleteObject(hbrush);
\r
1709 SelectObject( hdc, hpen );
\r
1714 [AS] The method I use to create the bitmaps it a bit tricky, but it
\r
1715 seems to work ok. The main problem here is to find the "inside" of a chess
\r
1716 piece: follow the steps as explained below.
\r
1718 static void CreatePieceMaskFromFont( HDC hdc_window, HDC hdc, int index )
\r
1722 COLORREF chroma = RGB(0xFF,0x00,0xFF);
\r
1726 int backColor = whitePieceColor;
\r
1727 int foreColor = blackPieceColor;
\r
1729 if( index < (int)BlackPawn && appData.fontBackColorWhite != appData.fontForeColorWhite ) {
\r
1730 backColor = appData.fontBackColorWhite;
\r
1731 foreColor = appData.fontForeColorWhite;
\r
1733 else if( index >= (int)BlackPawn && appData.fontBackColorBlack != appData.fontForeColorBlack ) {
\r
1734 backColor = appData.fontBackColorBlack;
\r
1735 foreColor = appData.fontForeColorBlack;
\r
1739 hbm = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );
\r
1741 hbm_old = SelectObject( hdc, hbm );
\r
1745 rc.right = squareSize;
\r
1746 rc.bottom = squareSize;
\r
1748 /* Step 1: background is now black */
\r
1749 FillRect( hdc, &rc, GetStockObject(BLACK_BRUSH) );
\r
1751 GetTextExtentPoint32( hdc, &pieceToFontChar[index], 1, &sz );
\r
1753 pt.x = (squareSize - sz.cx) / 2;
\r
1754 pt.y = (squareSize - sz.cy) / 2;
\r
1756 SetBkMode( hdc, TRANSPARENT );
\r
1757 SetTextColor( hdc, chroma );
\r
1758 /* Step 2: the piece has been drawn in purple, there are now black and purple in this bitmap */
\r
1759 TextOut( hdc, pt.x, pt.y, &pieceToFontChar[appData.allWhite && index >= (int)BlackPawn ? index - (int)BlackPawn : index], 1 );
\r
1761 SelectObject( hdc, GetStockObject(WHITE_BRUSH) );
\r
1762 /* Step 3: the area outside the piece is filled with white */
\r
1763 // FloodFill( hdc, 0, 0, chroma );
\r
1764 ExtFloodFill( hdc, 0, 0, 0, FLOODFILLSURFACE );
\r
1765 ExtFloodFill( hdc, 0, squareSize-1, 0, FLOODFILLSURFACE ); // [HGM] fill from all 4 corners, for if piece too big
\r
1766 ExtFloodFill( hdc, squareSize-1, 0, 0, FLOODFILLSURFACE );
\r
1767 ExtFloodFill( hdc, squareSize-1, squareSize-1, 0, FLOODFILLSURFACE );
\r
1768 SelectObject( hdc, GetStockObject(BLACK_BRUSH) );
\r
1770 Step 4: this is the tricky part, the area inside the piece is filled with black,
\r
1771 but if the start point is not inside the piece we're lost!
\r
1772 There should be a better way to do this... if we could create a region or path
\r
1773 from the fill operation we would be fine for example.
\r
1775 // FloodFill( hdc, squareSize / 2, squareSize / 2, RGB(0xFF,0xFF,0xFF) );
\r
1776 ExtFloodFill( hdc, squareSize / 2, squareSize / 2, RGB(0xFF,0xFF,0xFF), FLOODFILLBORDER );
\r
1778 { /* [HGM] shave off edges of mask, in an attempt to correct for the fact that FloodFill does not work correctly under Win XP */
\r
1779 HDC dc2 = CreateCompatibleDC( hdc_window );
\r
1780 HBITMAP bm2 = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );
\r
1782 SelectObject( dc2, bm2 );
\r
1783 BitBlt( dc2, 0, 0, squareSize, squareSize, hdc, 0, 0, SRCCOPY ); // make copy
\r
1784 BitBlt( hdc, 0, 1, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );
\r
1785 BitBlt( hdc, 2, 1, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );
\r
1786 BitBlt( hdc, 1, 0, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );
\r
1787 BitBlt( hdc, 1, 2, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );
\r
1790 DeleteObject( bm2 );
\r
1793 SetTextColor( hdc, 0 );
\r
1795 Step 5: some fonts have "disconnected" areas that are skipped by the fill:
\r
1796 draw the piece again in black for safety.
\r
1798 TextOut( hdc, pt.x, pt.y, &pieceToFontChar[appData.allWhite && index >= (int)BlackPawn ? index - (int)BlackPawn : index], 1 );
\r
1800 SelectObject( hdc, hbm_old );
\r
1802 if( hPieceMask[index] != NULL ) {
\r
1803 DeleteObject( hPieceMask[index] );
\r
1806 hPieceMask[index] = hbm;
\r
1809 hbm = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );
\r
1811 SelectObject( hdc, hbm );
\r
1814 HDC dc1 = CreateCompatibleDC( hdc_window );
\r
1815 HDC dc2 = CreateCompatibleDC( hdc_window );
\r
1816 HBITMAP bm2 = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );
\r
1818 SelectObject( dc1, hPieceMask[index] );
\r
1819 SelectObject( dc2, bm2 );
\r
1820 FillRect( dc2, &rc, GetStockObject(WHITE_BRUSH) );
\r
1821 BitBlt( dc2, 0, 0, squareSize, squareSize, dc1, 0, 0, SRCINVERT );
\r
1824 Now dc2 contains the inverse of the piece mask, i.e. a mask that preserves
\r
1825 the piece background and deletes (makes transparent) the rest.
\r
1826 Thanks to that mask, we are free to paint the background with the greates
\r
1827 freedom, as we'll be able to mask off the unwanted parts when finished.
\r
1828 We use this, to make gradients and give the pieces a "roundish" look.
\r
1830 SetPieceBackground( hdc, backColor, 2 );
\r
1831 BitBlt( hdc, 0, 0, squareSize, squareSize, dc2, 0, 0, SRCAND );
\r
1835 DeleteObject( bm2 );
\r
1838 SetTextColor( hdc, foreColor );
\r
1839 TextOut( hdc, pt.x, pt.y, &pieceToFontChar[appData.allWhite && index >= (int)BlackPawn ? index - (int)BlackPawn : index], 1 );
\r
1841 SelectObject( hdc, hbm_old );
\r
1843 if( hPieceFace[index] != NULL ) {
\r
1844 DeleteObject( hPieceFace[index] );
\r
1847 hPieceFace[index] = hbm;
\r
1850 static int TranslatePieceToFontPiece( int piece )
\r
1880 case BlackMarshall:
\r
1884 case BlackNightrider:
\r
1890 case BlackUnicorn:
\r
1894 case BlackGrasshopper:
\r
1906 case BlackCardinal:
\r
1913 case WhiteMarshall:
\r
1917 case WhiteNightrider:
\r
1923 case WhiteUnicorn:
\r
1927 case WhiteGrasshopper:
\r
1939 case WhiteCardinal:
\r
1948 void CreatePiecesFromFont()
\r
1951 HDC hdc_window = NULL;
\r
1957 if( fontBitmapSquareSize < 0 ) {
\r
1958 /* Something went seriously wrong in the past: do not try to recreate fonts! */
\r
1962 if( appData.renderPiecesWithFont == NULL || appData.renderPiecesWithFont[0] == NULLCHAR || appData.renderPiecesWithFont[0] == '*' ) {
\r
1963 fontBitmapSquareSize = -1;
\r
1967 if( fontBitmapSquareSize != squareSize ) {
\r
1968 hdc_window = GetDC( hwndMain );
\r
1969 hdc = CreateCompatibleDC( hdc_window );
\r
1971 if( hPieceFont != NULL ) {
\r
1972 DeleteObject( hPieceFont );
\r
1975 for( i=0; i<=(int)BlackKing; i++ ) {
\r
1976 hPieceMask[i] = NULL;
\r
1977 hPieceFace[i] = NULL;
\r
1983 if( appData.fontPieceSize >= 50 && appData.fontPieceSize <= 150 ) {
\r
1984 fontHeight = appData.fontPieceSize;
\r
1987 fontHeight = (fontHeight * squareSize) / 100;
\r
1989 lf.lfHeight = -MulDiv( fontHeight, GetDeviceCaps(hdc, LOGPIXELSY), 72 );
\r
1991 lf.lfEscapement = 0;
\r
1992 lf.lfOrientation = 0;
\r
1993 lf.lfWeight = FW_NORMAL;
\r
1995 lf.lfUnderline = 0;
\r
1996 lf.lfStrikeOut = 0;
\r
1997 lf.lfCharSet = DEFAULT_CHARSET;
\r
1998 lf.lfOutPrecision = OUT_DEFAULT_PRECIS;
\r
1999 lf.lfClipPrecision = CLIP_DEFAULT_PRECIS;
\r
2000 lf.lfQuality = PROOF_QUALITY;
\r
2001 lf.lfPitchAndFamily = DEFAULT_PITCH | FF_DONTCARE;
\r
2002 strncpy( lf.lfFaceName, appData.renderPiecesWithFont, sizeof(lf.lfFaceName) );
\r
2003 lf.lfFaceName[ sizeof(lf.lfFaceName) - 1 ] = '\0';
\r
2005 hPieceFont = CreateFontIndirect( &lf );
\r
2007 if( hPieceFont == NULL ) {
\r
2008 fontBitmapSquareSize = -2;
\r
2011 /* Setup font-to-piece character table */
\r
2012 if( ! SetCharTable(pieceToFontChar, appData.fontToPieceTable) ) {
\r
2013 /* No (or wrong) global settings, try to detect the font */
\r
2014 if( strstr(lf.lfFaceName,"Alpha") != NULL ) {
\r
2016 SetCharTable(pieceToFontChar, "phbrqkojntwl");
\r
2018 else if( strstr(lf.lfFaceName,"DiagramTT") != NULL ) {
\r
2019 /* DiagramTT* family */
\r
2020 SetCharTable(pieceToFontChar, "PNLRQKpnlrqk");
\r
2022 else if( strstr(lf.lfFaceName,"WinboardF") != NULL ) {
\r
2023 /* Fairy symbols */
\r
2024 SetCharTable(pieceToFontChar, "PNBRQFEACWMOHIJGDVSLUKpnbrqfeacwmohijgdvsluk");
\r
2026 else if( strstr(lf.lfFaceName,"GC2004D") != NULL ) {
\r
2027 /* Good Companion (Some characters get warped as literal :-( */
\r
2028 char s[] = "1cmWG0??S??oYI23wgQU";
\r
2029 s[0]=0xB9; s[1]=0xA9; s[6]=0xB1; s[11]=0xBB; s[12]=0xAB; s[17]=0xB3;
\r
2030 SetCharTable(pieceToFontChar, s);
\r
2033 /* Cases, Condal, Leipzig, Lucena, Marroquin, Merida, Usual */
\r
2034 SetCharTable(pieceToFontChar, "pnbrqkomvtwl");
\r
2038 /* Create bitmaps */
\r
2039 hfont_old = SelectObject( hdc, hPieceFont );
\r
2040 for(i=(int)WhitePawn; i<(int)EmptySquare; i++) /* [HGM] made a loop for this */
\r
2041 if(PieceToChar((ChessSquare)i) != '.') /* skip unused pieces */
\r
2042 CreatePieceMaskFromFont( hdc_window, hdc, i );
\r
2044 SelectObject( hdc, hfont_old );
\r
2046 fontBitmapSquareSize = squareSize;
\r
2050 if( hdc != NULL ) {
\r
2054 if( hdc_window != NULL ) {
\r
2055 ReleaseDC( hwndMain, hdc_window );
\r
2060 DoLoadBitmap(HINSTANCE hinst, char *piece, int squareSize, char *suffix)
\r
2064 snprintf(name, sizeof(name)/sizeof(name[0]), "%s%d%s", piece, squareSize, suffix);
\r
2065 if (gameInfo.event &&
\r
2066 strcmp(gameInfo.event, "Easter Egg Hunt") == 0 &&
\r
2067 strcmp(name, "k80s") == 0) {
\r
2068 safeStrCpy(name, "tim", sizeof(name)/sizeof(name[0]) );
\r
2070 return LoadBitmap(hinst, name);
\r
2074 /* Insert a color into the program's logical palette
\r
2075 structure. This code assumes the given color is
\r
2076 the result of the RGB or PALETTERGB macro, and it
\r
2077 knows how those macros work (which is documented).
\r
2080 InsertInPalette(COLORREF color)
\r
2082 LPPALETTEENTRY pe = &(pLogPal->palPalEntry[pLogPal->palNumEntries]);
\r
2084 if (pLogPal->palNumEntries++ >= PALETTESIZE) {
\r
2085 DisplayFatalError(_("Too many colors"), 0, 1);
\r
2086 pLogPal->palNumEntries--;
\r
2090 pe->peFlags = (char) 0;
\r
2091 pe->peRed = (char) (0xFF & color);
\r
2092 pe->peGreen = (char) (0xFF & (color >> 8));
\r
2093 pe->peBlue = (char) (0xFF & (color >> 16));
\r
2099 InitDrawingColors()
\r
2101 if (pLogPal == NULL) {
\r
2102 /* Allocate enough memory for a logical palette with
\r
2103 * PALETTESIZE entries and set the size and version fields
\r
2104 * of the logical palette structure.
\r
2106 pLogPal = (NPLOGPALETTE)
\r
2107 LocalAlloc(LMEM_FIXED, (sizeof(LOGPALETTE) +
\r
2108 (sizeof(PALETTEENTRY) * (PALETTESIZE))));
\r
2109 pLogPal->palVersion = 0x300;
\r
2111 pLogPal->palNumEntries = 0;
\r
2113 InsertInPalette(lightSquareColor);
\r
2114 InsertInPalette(darkSquareColor);
\r
2115 InsertInPalette(whitePieceColor);
\r
2116 InsertInPalette(blackPieceColor);
\r
2117 InsertInPalette(highlightSquareColor);
\r
2118 InsertInPalette(premoveHighlightColor);
\r
2120 /* create a logical color palette according the information
\r
2121 * in the LOGPALETTE structure.
\r
2123 hPal = CreatePalette((LPLOGPALETTE) pLogPal);
\r
2125 lightSquareBrush = CreateSolidBrush(lightSquareColor);
\r
2126 blackSquareBrush = CreateSolidBrush(blackPieceColor);
\r
2127 darkSquareBrush = CreateSolidBrush(darkSquareColor);
\r
2128 whitePieceBrush = CreateSolidBrush(whitePieceColor);
\r
2129 blackPieceBrush = CreateSolidBrush(blackPieceColor);
\r
2130 iconBkgndBrush = CreateSolidBrush(GetSysColor(COLOR_BACKGROUND));
\r
2131 explodeBrush = CreateSolidBrush(highlightSquareColor); // [HGM] atomic
\r
2132 markerBrush = CreateSolidBrush(premoveHighlightColor); // [HGM] markers
\r
2133 /* [AS] Force rendering of the font-based pieces */
\r
2134 if( fontBitmapSquareSize > 0 ) {
\r
2135 fontBitmapSquareSize = 0;
\r
2141 BoardWidth(int boardSize, int n)
\r
2142 { /* [HGM] argument n added to allow different width and height */
\r
2143 int lineGap = sizeInfo[boardSize].lineGap;
\r
2145 if( appData.overrideLineGap >= 0 && appData.overrideLineGap <= 5 ) {
\r
2146 lineGap = appData.overrideLineGap;
\r
2149 return (n + 1) * lineGap +
\r
2150 n * sizeInfo[boardSize].squareSize;
\r
2153 /* Respond to board resize by dragging edge */
\r
2155 ResizeBoard(int newSizeX, int newSizeY, int flags)
\r
2157 BoardSize newSize = NUM_SIZES - 1;
\r
2158 static int recurse = 0;
\r
2159 if (IsIconic(hwndMain)) return;
\r
2160 if (recurse > 0) return;
\r
2162 while (newSize > 0) {
\r
2163 InitDrawingSizes(newSize+1000, 0); // [HGM] kludge to update sizeInfo without visible effects
\r
2164 if(newSizeX >= sizeInfo[newSize].cliWidth &&
\r
2165 newSizeY >= sizeInfo[newSize].cliHeight) break;
\r
2168 boardSize = newSize;
\r
2169 InitDrawingSizes(boardSize, flags);
\r
2174 extern Boolean twoBoards, partnerUp; // [HGM] dual
\r
2177 InitDrawingSizes(BoardSize boardSize, int flags)
\r
2179 int i, boardWidth, boardHeight; /* [HGM] height treated separately */
\r
2180 ChessSquare piece;
\r
2181 static int oldBoardSize = -1, oldTinyLayout = 0;
\r
2183 SIZE clockSize, messageSize;
\r
2185 char buf[MSG_SIZ];
\r
2187 HMENU hmenu = GetMenu(hwndMain);
\r
2188 RECT crect, wrect, oldRect;
\r
2190 LOGBRUSH logbrush;
\r
2192 int suppressVisibleEffects = 0; // [HGM] kludge to request updating sizeInfo only
\r
2193 if((int)boardSize >= 1000 ) { boardSize -= 1000; suppressVisibleEffects = 1; }
\r
2195 /* [HGM] call with -2 uses old size (for if nr of files, ranks changes) */
\r
2196 if(boardSize == (BoardSize)(-2) ) boardSize = oldBoardSize;
\r
2198 oldRect.left = wpMain.x; //[HGM] placement: remember previous window params
\r
2199 oldRect.top = wpMain.y;
\r
2200 oldRect.right = wpMain.x + wpMain.width;
\r
2201 oldRect.bottom = wpMain.y + wpMain.height;
\r
2203 tinyLayout = sizeInfo[boardSize].tinyLayout;
\r
2204 smallLayout = sizeInfo[boardSize].smallLayout;
\r
2205 squareSize = sizeInfo[boardSize].squareSize;
\r
2206 lineGap = sizeInfo[boardSize].lineGap;
\r
2207 minorSize = 0; /* [HGM] Kludge to see if demagnified pieces need to be shifted */
\r
2209 if( appData.overrideLineGap >= 0 && appData.overrideLineGap <= 5 ) {
\r
2210 lineGap = appData.overrideLineGap;
\r
2213 if (tinyLayout != oldTinyLayout) {
\r
2214 long style = GetWindowLong(hwndMain, GWL_STYLE);
\r
2216 style &= ~WS_SYSMENU;
\r
2217 InsertMenu(hmenu, IDM_Exit, MF_BYCOMMAND, IDM_Minimize,
\r
2218 "&Minimize\tCtrl+F4");
\r
2220 style |= WS_SYSMENU;
\r
2221 RemoveMenu(hmenu, IDM_Minimize, MF_BYCOMMAND);
\r
2223 SetWindowLong(hwndMain, GWL_STYLE, style);
\r
2225 for (i=0; menuBarText[tinyLayout][i]; i++) {
\r
2226 ModifyMenu(hmenu, i, MF_STRING|MF_BYPOSITION|MF_POPUP,
\r
2227 (UINT)GetSubMenu(hmenu, i), T_(menuBarText[tinyLayout][i]));
\r
2229 DrawMenuBar(hwndMain);
\r
2232 boardWidth = BoardWidth(boardSize, BOARD_WIDTH);
\r
2233 boardHeight = BoardWidth(boardSize, BOARD_HEIGHT);
\r
2235 /* Get text area sizes */
\r
2236 hdc = GetDC(hwndMain);
\r
2237 if (appData.clockMode) {
\r
2238 snprintf(buf, MSG_SIZ, _("White: %s"), TimeString(23*60*60*1000L));
\r
2240 snprintf(buf, MSG_SIZ, _("White"));
\r
2242 oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);
\r
2243 GetTextExtentPoint(hdc, buf, strlen(buf), &clockSize);
\r
2244 SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);
\r
2245 str = _("We only care about the height here");
\r
2246 GetTextExtentPoint(hdc, str, strlen(str), &messageSize);
\r
2247 SelectObject(hdc, oldFont);
\r
2248 ReleaseDC(hwndMain, hdc);
\r
2250 /* Compute where everything goes */
\r
2251 if((first.programLogo || second.programLogo) && !tinyLayout) {
\r
2252 /* [HGM] logo: if either logo is on, reserve space for it */
\r
2253 logoHeight = 2*clockSize.cy;
\r
2254 leftLogoRect.left = OUTER_MARGIN;
\r
2255 leftLogoRect.right = leftLogoRect.left + 4*clockSize.cy;
\r
2256 leftLogoRect.top = OUTER_MARGIN;
\r
2257 leftLogoRect.bottom = OUTER_MARGIN + logoHeight;
\r
2259 rightLogoRect.right = OUTER_MARGIN + boardWidth;
\r
2260 rightLogoRect.left = rightLogoRect.right - 4*clockSize.cy;
\r
2261 rightLogoRect.top = OUTER_MARGIN;
\r
2262 rightLogoRect.bottom = OUTER_MARGIN + logoHeight;
\r
2265 whiteRect.left = leftLogoRect.right;
\r
2266 whiteRect.right = OUTER_MARGIN + boardWidth/2 - INNER_MARGIN/2;
\r
2267 whiteRect.top = OUTER_MARGIN;
\r
2268 whiteRect.bottom = whiteRect.top + logoHeight;
\r
2270 blackRect.right = rightLogoRect.left;
\r
2271 blackRect.left = whiteRect.right + INNER_MARGIN;
\r
2272 blackRect.top = whiteRect.top;
\r
2273 blackRect.bottom = whiteRect.bottom;
\r
2275 whiteRect.left = OUTER_MARGIN;
\r
2276 whiteRect.right = whiteRect.left + boardWidth/2 - INNER_MARGIN/2;
\r
2277 whiteRect.top = OUTER_MARGIN;
\r
2278 whiteRect.bottom = whiteRect.top + clockSize.cy;
\r
2280 blackRect.left = whiteRect.right + INNER_MARGIN;
\r
2281 blackRect.right = blackRect.left + boardWidth/2 - 1;
\r
2282 blackRect.top = whiteRect.top;
\r
2283 blackRect.bottom = whiteRect.bottom;
\r
2285 logoHeight = 0; // [HGM] logo: suppress logo after change to tiny layout!
\r
2288 messageRect.left = OUTER_MARGIN + MESSAGE_LINE_LEFTMARGIN;
\r
2289 if (appData.showButtonBar) {
\r
2290 messageRect.right = OUTER_MARGIN + boardWidth // [HGM] logo: expressed independent of clock placement
\r
2291 - N_BUTTONS*BUTTON_WIDTH - MESSAGE_LINE_LEFTMARGIN;
\r
2293 messageRect.right = OUTER_MARGIN + boardWidth;
\r
2295 messageRect.top = whiteRect.bottom + INNER_MARGIN;
\r
2296 messageRect.bottom = messageRect.top + messageSize.cy;
\r
2298 boardRect.left = OUTER_MARGIN;
\r
2299 boardRect.right = boardRect.left + boardWidth;
\r
2300 boardRect.top = messageRect.bottom + INNER_MARGIN;
\r
2301 boardRect.bottom = boardRect.top + boardHeight;
\r
2303 sizeInfo[boardSize].cliWidth = boardRect.right + OUTER_MARGIN;
\r
2304 sizeInfo[boardSize].cliHeight = boardRect.bottom + OUTER_MARGIN;
\r
2305 oldBoardSize = boardSize;
\r
2306 oldTinyLayout = tinyLayout;
\r
2307 winW = 2 * GetSystemMetrics(SM_CXFRAME) + boardRect.right + OUTER_MARGIN;
\r
2308 winH = 2 * GetSystemMetrics(SM_CYFRAME) + GetSystemMetrics(SM_CYMENU) +
\r
2309 GetSystemMetrics(SM_CYCAPTION) + boardRect.bottom + OUTER_MARGIN;
\r
2310 winW *= 1 + twoBoards;
\r
2311 if(suppressVisibleEffects) return; // [HGM] when called for filling sizeInfo only
\r
2312 wpMain.width = winW; // [HGM] placement: set through temporary which can used by initial sizing choice
\r
2313 wpMain.height = winH; // without disturbing window attachments
\r
2314 GetWindowRect(hwndMain, &wrect);
\r
2315 SetWindowPos(hwndMain, NULL, 0, 0, wpMain.width, wpMain.height,
\r
2316 SWP_NOCOPYBITS|SWP_NOZORDER|SWP_NOMOVE);
\r
2318 // [HGM] placement: let attached windows follow size change.
\r
2319 ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, moveHistoryDialog, &wpMoveHistory );
\r
2320 ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, evalGraphDialog, &wpEvalGraph );
\r
2321 ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, engineOutputDialog, &wpEngineOutput );
\r
2322 ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, gameListDialog, &wpGameList );
\r
2323 ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, hwndConsole, &wpConsole );
\r
2325 /* compensate if menu bar wrapped */
\r
2326 GetClientRect(hwndMain, &crect);
\r
2327 offby = boardRect.bottom + OUTER_MARGIN - crect.bottom;
\r
2328 wpMain.height += offby;
\r
2330 case WMSZ_TOPLEFT:
\r
2331 SetWindowPos(hwndMain, NULL,
\r
2332 wrect.right - wpMain.width, wrect.bottom - wpMain.height,
\r
2333 wpMain.width, wpMain.height, SWP_NOCOPYBITS|SWP_NOZORDER);
\r
2336 case WMSZ_TOPRIGHT:
\r
2338 SetWindowPos(hwndMain, NULL,
\r
2339 wrect.left, wrect.bottom - wpMain.height,
\r
2340 wpMain.width, wpMain.height, SWP_NOCOPYBITS|SWP_NOZORDER);
\r
2343 case WMSZ_BOTTOMLEFT:
\r
2345 SetWindowPos(hwndMain, NULL,
\r
2346 wrect.right - wpMain.width, wrect.top,
\r
2347 wpMain.width, wpMain.height, SWP_NOCOPYBITS|SWP_NOZORDER);
\r
2350 case WMSZ_BOTTOMRIGHT:
\r
2354 SetWindowPos(hwndMain, NULL, 0, 0, wpMain.width, wpMain.height,
\r
2355 SWP_NOCOPYBITS|SWP_NOZORDER|SWP_NOMOVE);
\r
2360 for (i = 0; i < N_BUTTONS; i++) {
\r
2361 if (buttonDesc[i].hwnd != NULL) {
\r
2362 DestroyWindow(buttonDesc[i].hwnd);
\r
2363 buttonDesc[i].hwnd = NULL;
\r
2365 if (appData.showButtonBar) {
\r
2366 buttonDesc[i].hwnd =
\r
2367 CreateWindow("BUTTON", buttonDesc[i].label,
\r
2368 WS_VISIBLE | WS_CHILD | BS_PUSHBUTTON,
\r
2369 boardRect.right - BUTTON_WIDTH*(N_BUTTONS-i),
\r
2370 messageRect.top, BUTTON_WIDTH, messageSize.cy, hwndMain,
\r
2371 (HMENU) buttonDesc[i].id,
\r
2372 (HINSTANCE) GetWindowLong(hwndMain, GWL_HINSTANCE), NULL);
\r
2374 SendMessage(buttonDesc[i].hwnd, WM_SETFONT,
\r
2375 (WPARAM)font[boardSize][MESSAGE_FONT]->hf,
\r
2376 MAKELPARAM(FALSE, 0));
\r
2378 if (buttonDesc[i].id == IDM_Pause)
\r
2379 hwndPause = buttonDesc[i].hwnd;
\r
2380 buttonDesc[i].wndproc = (WNDPROC)
\r
2381 SetWindowLong(buttonDesc[i].hwnd, GWL_WNDPROC, (LONG) ButtonProc);
\r
2384 if (gridPen != NULL) DeleteObject(gridPen);
\r
2385 if (highlightPen != NULL) DeleteObject(highlightPen);
\r
2386 if (premovePen != NULL) DeleteObject(premovePen);
\r
2387 if (lineGap != 0) {
\r
2388 logbrush.lbStyle = BS_SOLID;
\r
2389 logbrush.lbColor = RGB(0, 0, 0); /* grid pen color = black */
\r
2391 ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,
\r
2392 lineGap, &logbrush, 0, NULL);
\r
2393 logbrush.lbColor = highlightSquareColor;
\r
2395 ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,
\r
2396 lineGap, &logbrush, 0, NULL);
\r
2398 logbrush.lbColor = premoveHighlightColor;
\r
2400 ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,
\r
2401 lineGap, &logbrush, 0, NULL);
\r
2403 /* [HGM] Loop had to be split in part for vert. and hor. lines */
\r
2404 for (i = 0; i < BOARD_HEIGHT + 1; i++) {
\r
2405 gridEndpoints[i*2].x = boardRect.left + lineGap / 2;
\r
2406 gridEndpoints[i*2].y = gridEndpoints[i*2 + 1].y =
\r
2407 boardRect.top + lineGap / 2 + (i * (squareSize + lineGap));
\r
2408 gridEndpoints[i*2 + 1].x = boardRect.left + lineGap / 2 +
\r
2409 BOARD_WIDTH * (squareSize + lineGap);
\r
2410 gridVertexCounts[i*2] = gridVertexCounts[i*2 + 1] = 2;
\r
2412 for (i = 0; i < BOARD_WIDTH + 1; i++) {
\r
2413 gridEndpoints[i*2 + BOARD_HEIGHT*2 + 2].y = boardRect.top + lineGap / 2;
\r
2414 gridEndpoints[i*2 + BOARD_HEIGHT*2 + 2].x =
\r
2415 gridEndpoints[i*2 + 1 + BOARD_HEIGHT*2 + 2].x = boardRect.left +
\r
2416 lineGap / 2 + (i * (squareSize + lineGap));
\r
2417 gridEndpoints[i*2 + 1 + BOARD_HEIGHT*2 + 2].y =
\r
2418 boardRect.top + BOARD_HEIGHT * (squareSize + lineGap);
\r
2419 gridVertexCounts[i*2] = gridVertexCounts[i*2 + 1] = 2;
\r
2423 /* [HGM] Licensing requirement */
\r
2425 if(gameInfo.variant == VariantGothic) GothicPopUp( GOTHIC, VariantGothic); else
\r
2428 if(gameInfo.variant == VariantFalcon) GothicPopUp( FALCON, VariantFalcon); else
\r
2430 GothicPopUp( "", VariantNormal);
\r
2433 /* if (boardSize == oldBoardSize) return; [HGM] variant might have changed */
\r
2435 /* Load piece bitmaps for this board size */
\r
2436 for (i=0; i<=2; i++) {
\r
2437 for (piece = WhitePawn;
\r
2438 (int) piece < (int) BlackPawn;
\r
2439 piece = (ChessSquare) ((int) piece + 1)) {
\r
2440 if (pieceBitmap[i][piece] != NULL)
\r
2441 DeleteObject(pieceBitmap[i][piece]);
\r
2445 fontBitmapSquareSize = 0; /* [HGM] render: make sure pieces will be recreated, as we might need others now */
\r
2446 // Orthodox Chess pieces
\r
2447 pieceBitmap[0][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "s");
\r
2448 pieceBitmap[0][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "s");
\r
2449 pieceBitmap[0][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "s");
\r
2450 pieceBitmap[0][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "s");
\r
2451 pieceBitmap[0][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "s");
\r
2452 pieceBitmap[1][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "o");
\r
2453 pieceBitmap[1][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "o");
\r
2454 pieceBitmap[1][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "o");
\r
2455 pieceBitmap[1][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "o");
\r
2456 pieceBitmap[1][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "o");
\r
2457 pieceBitmap[2][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "w");
\r
2458 pieceBitmap[2][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "w");
\r
2459 pieceBitmap[2][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "w");
\r
2460 pieceBitmap[2][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "w");
\r
2461 pieceBitmap[2][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "w");
\r
2462 if( gameInfo.variant == VariantShogi && squareSize <= 72 && squareSize >= 33) {
\r
2463 // in Shogi, Hijack the unused Queen for Lance
\r
2464 pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "s");
\r
2465 pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "o");
\r
2466 pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "w");
\r
2468 pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "s");
\r
2469 pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "o");
\r
2470 pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "w");
\r
2473 if(squareSize <= 72 && squareSize >= 33) {
\r
2474 /* A & C are available in most sizes now */
\r
2475 if(squareSize != 49 && squareSize != 72 && squareSize != 33) { // Vortex-like
\r
2476 pieceBitmap[0][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "s");
\r
2477 pieceBitmap[1][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "o");
\r
2478 pieceBitmap[2][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "w");
\r
2479 pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "s");
\r
2480 pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "o");
\r
2481 pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "w");
\r
2482 pieceBitmap[0][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "s");
\r
2483 pieceBitmap[1][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "o");
\r
2484 pieceBitmap[2][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "w");
\r
2485 pieceBitmap[0][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "s");
\r
2486 pieceBitmap[1][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "o");
\r
2487 pieceBitmap[2][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "w");
\r
2488 } else { // Smirf-like
\r
2489 pieceBitmap[0][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "s");
\r
2490 pieceBitmap[1][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "o");
\r
2491 pieceBitmap[2][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "w");
\r
2493 if(gameInfo.variant == VariantGothic) { // Vortex-like
\r
2494 pieceBitmap[0][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "s");
\r
2495 pieceBitmap[1][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "o");
\r
2496 pieceBitmap[2][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "w");
\r
2497 } else { // WinBoard standard
\r
2498 pieceBitmap[0][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "s");
\r
2499 pieceBitmap[1][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "o");
\r
2500 pieceBitmap[2][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "w");
\r
2505 if(squareSize==72 || squareSize==49 || squareSize==33) { /* experiment with some home-made bitmaps */
\r
2506 pieceBitmap[0][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "s");
\r
2507 pieceBitmap[1][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "o");
\r
2508 pieceBitmap[2][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "w");
\r
2509 pieceBitmap[0][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "s");
\r
2510 pieceBitmap[1][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "o");
\r
2511 pieceBitmap[2][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "w");
\r
2512 pieceBitmap[0][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "s");
\r
2513 pieceBitmap[1][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "o");
\r
2514 pieceBitmap[2][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "w");
\r
2515 pieceBitmap[0][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "s");
\r
2516 pieceBitmap[1][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "o");
\r
2517 pieceBitmap[2][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "w");
\r
2518 pieceBitmap[0][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "s");
\r
2519 pieceBitmap[1][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "o");
\r
2520 pieceBitmap[2][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "w");
\r
2521 pieceBitmap[0][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "s");
\r
2522 pieceBitmap[1][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "o");
\r
2523 pieceBitmap[2][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "w");
\r
2524 pieceBitmap[0][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "s");
\r
2525 pieceBitmap[1][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "o");
\r
2526 pieceBitmap[2][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "w");
\r
2527 pieceBitmap[0][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "s");
\r
2528 pieceBitmap[1][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "o");
\r
2529 pieceBitmap[2][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "w");
\r
2530 pieceBitmap[0][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "s");
\r
2531 pieceBitmap[1][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "o");
\r
2532 pieceBitmap[2][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "w");
\r
2533 pieceBitmap[0][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "s");
\r
2534 pieceBitmap[1][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "o");
\r
2535 pieceBitmap[2][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "w");
\r
2537 if(gameInfo.variant == VariantShogi) { /* promoted Gold represemtations */
\r
2538 pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "wp", squareSize, "s");
\r
2539 pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "wp", squareSize, "o");
\r
2540 pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "w", squareSize, "w");
\r
2541 pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "wn", squareSize, "s");
\r
2542 pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "wn", squareSize, "o");
\r
2543 pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "w", squareSize, "w");
\r
2544 pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "ws", squareSize, "s");
\r
2545 pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "ws", squareSize, "o");
\r
2546 pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "w", squareSize, "w");
\r
2547 pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "wl", squareSize, "s");
\r
2548 pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "wl", squareSize, "o");
\r
2549 pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "w", squareSize, "w");
\r
2551 pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "s");
\r
2552 pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "o");
\r
2553 pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "w");
\r
2554 pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "s");
\r
2555 pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "o");
\r
2556 pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "w");
\r
2557 pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "s");
\r
2558 pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "o");
\r
2559 pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "w");
\r
2560 pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "s");
\r
2561 pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "o");
\r
2562 pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "w");
\r
2565 } else { /* other size, no special bitmaps available. Use smaller symbols */
\r
2566 if((int)boardSize < 2) minorSize = sizeInfo[0].squareSize;
\r
2567 else minorSize = sizeInfo[(int)boardSize - 2].squareSize;
\r
2568 pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "s");
\r
2569 pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "o");
\r
2570 pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "w");
\r
2571 pieceBitmap[0][WhiteCardinal] = DoLoadBitmap(hInst, "b", minorSize, "s");
\r
2572 pieceBitmap[1][WhiteCardinal] = DoLoadBitmap(hInst, "b", minorSize, "o");
\r
2573 pieceBitmap[2][WhiteCardinal] = DoLoadBitmap(hInst, "b", minorSize, "w");
\r
2574 pieceBitmap[0][WhiteDragon] = DoLoadBitmap(hInst, "r", minorSize, "s");
\r
2575 pieceBitmap[1][WhiteDragon] = DoLoadBitmap(hInst, "r", minorSize, "o");
\r
2576 pieceBitmap[2][WhiteDragon] = DoLoadBitmap(hInst, "r", minorSize, "w");
\r
2577 pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "s");
\r
2578 pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "o");
\r
2579 pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "w");
\r
2583 if(gameInfo.variant == VariantShogi && squareSize == 58)
\r
2584 /* special Shogi support in this size */
\r
2585 { for (i=0; i<=2; i++) { /* replace all bitmaps */
\r
2586 for (piece = WhitePawn;
\r
2587 (int) piece < (int) BlackPawn;
\r
2588 piece = (ChessSquare) ((int) piece + 1)) {
\r
2589 if (pieceBitmap[i][piece] != NULL)
\r
2590 DeleteObject(pieceBitmap[i][piece]);
\r
2593 pieceBitmap[0][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "o");
\r
2594 pieceBitmap[0][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "o");
\r
2595 pieceBitmap[0][WhiteBishop] = DoLoadBitmap(hInst, "sb", squareSize, "o");
\r
2596 pieceBitmap[0][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "o");
\r
2597 pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "o");
\r
2598 pieceBitmap[0][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "o");
\r
2599 pieceBitmap[0][WhiteFerz] = DoLoadBitmap(hInst, "sf", squareSize, "o");
\r
2600 pieceBitmap[0][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "o");
\r
2601 pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "su", squareSize, "o");
\r
2602 pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "sh", squareSize, "o");
\r
2603 pieceBitmap[0][WhiteCardinal] = DoLoadBitmap(hInst, "sa", squareSize, "o");
\r
2604 pieceBitmap[0][WhiteDragon] = DoLoadBitmap(hInst, "sc", squareSize, "o");
\r
2605 pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "sg", squareSize, "o");
\r
2606 pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "ss", squareSize, "o");
\r
2607 pieceBitmap[1][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "o");
\r
2608 pieceBitmap[1][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "o");
\r
2609 pieceBitmap[1][WhiteBishop] = DoLoadBitmap(hInst, "sb", squareSize, "o");
\r
2610 pieceBitmap[1][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "o");
\r
2611 pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "o");
\r
2612 pieceBitmap[1][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "o");
\r
2613 pieceBitmap[1][WhiteFerz] = DoLoadBitmap(hInst, "sf", squareSize, "o");
\r
2614 pieceBitmap[1][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "o");
\r
2615 pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "su", squareSize, "o");
\r
2616 pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "sh", squareSize, "o");
\r
2617 pieceBitmap[1][WhiteCardinal] = DoLoadBitmap(hInst, "sa", squareSize, "o");
\r
2618 pieceBitmap[1][WhiteDragon] = DoLoadBitmap(hInst, "sc", squareSize, "o");
\r
2619 pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "sg", squareSize, "o");
\r
2620 pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "ss", squareSize, "o");
\r
2621 pieceBitmap[2][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "w");
\r
2622 pieceBitmap[2][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "w");
\r
2623 pieceBitmap[2][WhiteBishop] = DoLoadBitmap(hInst, "sr", squareSize, "w");
\r
2624 pieceBitmap[2][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "w");
\r
2625 pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "w");
\r
2626 pieceBitmap[2][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "w");
\r
2627 pieceBitmap[2][WhiteFerz] = DoLoadBitmap(hInst, "sw", squareSize, "w");
\r
2628 pieceBitmap[2][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "w");
\r
2629 pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "sp", squareSize, "w");
\r
2630 pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "sn", squareSize, "w");
\r
2631 pieceBitmap[2][WhiteCardinal] = DoLoadBitmap(hInst, "sr", squareSize, "w");
\r
2632 pieceBitmap[2][WhiteDragon] = DoLoadBitmap(hInst, "sr", squareSize, "w");
\r
2633 pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "sl", squareSize, "w");
\r
2634 pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "sw", squareSize, "w");
\r
2640 PieceBitmap(ChessSquare p, int kind)
\r
2642 if ((int) p >= (int) BlackPawn)
\r
2643 p = (ChessSquare) ((int) p - (int) BlackPawn + (int) WhitePawn);
\r
2645 return pieceBitmap[kind][(int) p];
\r
2648 /***************************************************************/
\r
2650 #define MIN(a,b) (((a) < (b)) ? (a) : (b))
\r
2651 #define MAX(a,b) (((a) > (b)) ? (a) : (b))
\r
2653 #define MIN3(a,b,c) (((a) < (b) && (a) < (c)) ? (a) : (((b) < (a) && (b) < (c)) ? (b) : (c)))
\r
2654 #define MAX3(a,b,c) (((a) > (b) && (a) > (c)) ? (a) : (((b) > (a) && (b) > (c)) ? (b) : (c)))
\r
2658 SquareToPos(int row, int column, int * x, int * y)
\r
2661 *x = boardRect.left + lineGap + ((BOARD_WIDTH-1)-column) * (squareSize + lineGap);
\r
2662 *y = boardRect.top + lineGap + row * (squareSize + lineGap);
\r
2664 *x = boardRect.left + lineGap + column * (squareSize + lineGap);
\r
2665 *y = boardRect.top + lineGap + ((BOARD_HEIGHT-1)-row) * (squareSize + lineGap);
\r
2670 DrawCoordsOnDC(HDC hdc)
\r
2672 static char files[24] = {'0', '1','2','3','4','5','6','7','8','9','0','1','1','0','9','8','7','6','5','4','3','2','1','0'};
\r
2673 static char ranks[24] = {'l', 'k','j','i','h','g','f','e','d','c','b','a','a','b','c','d','e','f','g','h','i','j','k','l'};
\r
2674 char str[2] = { NULLCHAR, NULLCHAR };
\r
2675 int oldMode, oldAlign, x, y, start, i;
\r
2679 if (!appData.showCoords)
\r
2682 start = flipView ? 1-(ONE!='1') : 23+(ONE!='1')-BOARD_HEIGHT;
\r
2684 oldBrush = SelectObject(hdc, GetStockObject(BLACK_BRUSH));
\r
2685 oldMode = SetBkMode(hdc, (appData.monoMode ? OPAQUE : TRANSPARENT));
\r
2686 oldAlign = GetTextAlign(hdc);
\r
2687 oldFont = SelectObject(hdc, font[boardSize][COORD_FONT]->hf);
\r
2689 y = boardRect.top + lineGap;
\r
2690 x = boardRect.left + lineGap + gameInfo.holdingsWidth*(squareSize + lineGap);
\r
2692 SetTextAlign(hdc, TA_LEFT|TA_TOP);
\r
2693 for (i = 0; i < BOARD_HEIGHT; i++) {
\r
2694 str[0] = files[start + i];
\r
2695 ExtTextOut(hdc, x + 2, y + 1, 0, NULL, str, 1, NULL);
\r
2696 y += squareSize + lineGap;
\r
2699 start = flipView ? 12-(BOARD_RGHT-BOARD_LEFT) : 12;
\r
2701 SetTextAlign(hdc, TA_RIGHT|TA_BOTTOM);
\r
2702 for (i = 0; i < BOARD_RGHT - BOARD_LEFT; i++) {
\r
2703 str[0] = ranks[start + i];
\r
2704 ExtTextOut(hdc, x + squareSize - 2, y - 1, 0, NULL, str, 1, NULL);
\r
2705 x += squareSize + lineGap;
\r
2708 SelectObject(hdc, oldBrush);
\r
2709 SetBkMode(hdc, oldMode);
\r
2710 SetTextAlign(hdc, oldAlign);
\r
2711 SelectObject(hdc, oldFont);
\r
2715 DrawGridOnDC(HDC hdc)
\r
2719 if (lineGap != 0) {
\r
2720 oldPen = SelectObject(hdc, gridPen);
\r
2721 PolyPolyline(hdc, gridEndpoints, gridVertexCounts, BOARD_WIDTH+BOARD_HEIGHT + 2);
\r
2722 SelectObject(hdc, oldPen);
\r
2726 #define HIGHLIGHT_PEN 0
\r
2727 #define PREMOVE_PEN 1
\r
2730 DrawHighlightOnDC(HDC hdc, BOOLEAN on, int x, int y, int pen)
\r
2733 HPEN oldPen, hPen;
\r
2734 if (lineGap == 0) return;
\r
2736 x1 = boardRect.left +
\r
2737 lineGap/2 + ((BOARD_WIDTH-1)-x) * (squareSize + lineGap);
\r
2738 y1 = boardRect.top +
\r
2739 lineGap/2 + y * (squareSize + lineGap);
\r
2741 x1 = boardRect.left +
\r
2742 lineGap/2 + x * (squareSize + lineGap);
\r
2743 y1 = boardRect.top +
\r
2744 lineGap/2 + ((BOARD_HEIGHT-1)-y) * (squareSize + lineGap);
\r
2746 hPen = pen ? premovePen : highlightPen;
\r
2747 oldPen = SelectObject(hdc, on ? hPen : gridPen);
\r
2748 MoveToEx(hdc, x1, y1, NULL);
\r
2749 LineTo(hdc, x1 + squareSize + lineGap, y1);
\r
2750 LineTo(hdc, x1 + squareSize + lineGap, y1 + squareSize + lineGap);
\r
2751 LineTo(hdc, x1, y1 + squareSize + lineGap);
\r
2752 LineTo(hdc, x1, y1);
\r
2753 SelectObject(hdc, oldPen);
\r
2757 DrawHighlightsOnDC(HDC hdc, HighlightInfo *h, int pen)
\r
2760 for (i=0; i<2; i++) {
\r
2761 if (h->sq[i].x >= 0 && h->sq[i].y >= 0)
\r
2762 DrawHighlightOnDC(hdc, TRUE,
\r
2763 h->sq[i].x, h->sq[i].y,
\r
2768 /* Note: sqcolor is used only in monoMode */
\r
2769 /* Note that this code is largely duplicated in woptions.c,
\r
2770 function DrawSampleSquare, so that needs to be updated too */
\r
2772 DrawPieceOnDC(HDC hdc, ChessSquare piece, int color, int sqcolor, int x, int y, HDC tmphdc)
\r
2774 HBITMAP oldBitmap;
\r
2778 if (appData.blindfold) return;
\r
2780 /* [AS] Use font-based pieces if needed */
\r
2781 if( fontBitmapSquareSize >= 0 && (squareSize > 32 || gameInfo.variant >= VariantShogi)) {
\r
2782 /* Create piece bitmaps, or do nothing if piece set is up to date */
\r
2783 CreatePiecesFromFont();
\r
2785 if( fontBitmapSquareSize == squareSize ) {
\r
2786 int index = TranslatePieceToFontPiece(piece);
\r
2788 SelectObject( tmphdc, hPieceMask[ index ] );
\r
2790 if(appData.upsideDown ? color==flipView : (flipView && gameInfo.variant == VariantShogi))
\r
2791 StretchBlt(hdc, x+squareSize, y+squareSize, -squareSize, -squareSize, tmphdc, 0, 0, squareSize, squareSize, SRCAND);
\r
2795 squareSize, squareSize,
\r
2800 SelectObject( tmphdc, hPieceFace[ index ] );
\r
2802 if(appData.upsideDown ? color==flipView : (flipView && gameInfo.variant == VariantShogi))
\r
2803 StretchBlt(hdc, x+squareSize, y+squareSize, -squareSize, -squareSize, tmphdc, 0, 0, squareSize, squareSize, SRCPAINT);
\r
2807 squareSize, squareSize,
\r
2816 if (appData.monoMode) {
\r
2817 SelectObject(tmphdc, PieceBitmap(piece,
\r
2818 color == sqcolor ? OUTLINE_PIECE : SOLID_PIECE));
\r
2819 BitBlt(hdc, x, y, squareSize, squareSize, tmphdc, 0, 0,
\r
2820 sqcolor ? SRCCOPY : NOTSRCCOPY);
\r
2822 tmpSize = squareSize;
\r
2824 ((piece >= (int)WhiteNightrider && piece <= WhiteGrasshopper) ||
\r
2825 (piece >= (int)BlackNightrider && piece <= BlackGrasshopper)) ) {
\r
2826 /* [HGM] no bitmap available for promoted pieces in Crazyhouse */
\r
2827 /* Bitmaps of smaller size are substituted, but we have to align them */
\r
2828 x += (squareSize - minorSize)>>1;
\r
2829 y += squareSize - minorSize - 2;
\r
2830 tmpSize = minorSize;
\r
2832 if (color || appData.allWhite ) {
\r
2833 oldBitmap = SelectObject(tmphdc, PieceBitmap(piece, WHITE_PIECE));
\r
2835 oldBrush = SelectObject(hdc, whitePieceBrush);
\r
2836 else oldBrush = SelectObject(hdc, blackPieceBrush);
\r
2837 if(appData.upsideDown && color==flipView)
\r
2838 StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, 0x00B8074A);
\r
2840 BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, 0x00B8074A);
\r
2841 /* Use black for outline of white pieces */
\r
2842 SelectObject(tmphdc, PieceBitmap(piece, OUTLINE_PIECE));
\r
2843 if(appData.upsideDown && color==flipView)
\r
2844 StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, SRCAND);
\r
2846 BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, SRCAND);
\r
2848 /* Use square color for details of black pieces */
\r
2849 oldBitmap = SelectObject(tmphdc, PieceBitmap(piece, SOLID_PIECE));
\r
2850 oldBrush = SelectObject(hdc, blackPieceBrush);
\r
2851 if(appData.upsideDown && !flipView)
\r
2852 StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, 0x00B8074A);
\r
2854 BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, 0x00B8074A);
\r
2856 SelectObject(hdc, oldBrush);
\r
2857 SelectObject(tmphdc, oldBitmap);
\r
2861 /* [AS] Compute a drawing mode for a square, based on specified settings (see DrawTile) */
\r
2862 int GetBackTextureMode( int algo )
\r
2864 int result = BACK_TEXTURE_MODE_DISABLED;
\r
2868 case BACK_TEXTURE_MODE_PLAIN:
\r
2869 result = 1; /* Always use identity map */
\r
2871 case BACK_TEXTURE_MODE_FULL_RANDOM:
\r
2872 result = 1 + (myrandom() % 3); /* Pick a transformation at random */
\r
2880 [AS] Compute and save texture drawing info, otherwise we may not be able
\r
2881 to handle redraws cleanly (as random numbers would always be different).
\r
2883 VOID RebuildTextureSquareInfo()
\r
2893 ZeroMemory( &backTextureSquareInfo, sizeof(backTextureSquareInfo) );
\r
2895 if( liteBackTexture != NULL ) {
\r
2896 if( GetObject( liteBackTexture, sizeof(bi), &bi ) > 0 ) {
\r
2897 lite_w = bi.bmWidth;
\r
2898 lite_h = bi.bmHeight;
\r
2902 if( darkBackTexture != NULL ) {
\r
2903 if( GetObject( darkBackTexture, sizeof(bi), &bi ) > 0 ) {
\r
2904 dark_w = bi.bmWidth;
\r
2905 dark_h = bi.bmHeight;
\r
2909 for( row=0; row<BOARD_HEIGHT; row++ ) {
\r
2910 for( col=0; col<BOARD_WIDTH; col++ ) {
\r
2911 if( (col + row) & 1 ) {
\r
2913 if( lite_w >= squareSize && lite_h >= squareSize ) {
\r
2914 if( lite_w >= squareSize*BOARD_WIDTH )
\r
2915 backTextureSquareInfo[row][col].x = (2*col+1)*lite_w/(2*BOARD_WIDTH) - squareSize/2; /* [HGM] cut out of center of virtual square */
\r
2917 backTextureSquareInfo[row][col].x = col * (lite_w - squareSize) / (BOARD_WIDTH-1); /* [HGM] divide by size-1 in stead of size! */
\r
2918 if( lite_h >= squareSize*BOARD_HEIGHT )
\r
2919 backTextureSquareInfo[row][col].y = (2*(BOARD_HEIGHT-row)-1)*lite_h/(2*BOARD_HEIGHT) - squareSize/2;
\r
2921 backTextureSquareInfo[row][col].y = (BOARD_HEIGHT-1-row) * (lite_h - squareSize) / (BOARD_HEIGHT-1);
\r
2922 backTextureSquareInfo[row][col].mode = GetBackTextureMode(liteBackTextureMode);
\r
2927 if( dark_w >= squareSize && dark_h >= squareSize ) {
\r
2928 if( dark_w >= squareSize*BOARD_WIDTH )
\r
2929 backTextureSquareInfo[row][col].x = (2*col+1) * dark_w / (2*BOARD_WIDTH) - squareSize/2;
\r
2931 backTextureSquareInfo[row][col].x = col * (dark_w - squareSize) / (BOARD_WIDTH-1);
\r
2932 if( dark_h >= squareSize*BOARD_HEIGHT )
\r
2933 backTextureSquareInfo[row][col].y = (2*(BOARD_HEIGHT-row)-1) * dark_h / (2*BOARD_HEIGHT) - squareSize/2;
\r
2935 backTextureSquareInfo[row][col].y = (BOARD_HEIGHT-1-row) * (dark_h - squareSize) / (BOARD_HEIGHT-1);
\r
2936 backTextureSquareInfo[row][col].mode = GetBackTextureMode(darkBackTextureMode);
\r
2943 /* [AS] Arrow highlighting support */
\r
2945 static int A_WIDTH = 5; /* Width of arrow body */
\r
2947 #define A_HEIGHT_FACTOR 6 /* Length of arrow "point", relative to body width */
\r
2948 #define A_WIDTH_FACTOR 3 /* Width of arrow "point", relative to body width */
\r
2950 static double Sqr( double x )
\r
2955 static int Round( double x )
\r
2957 return (int) (x + 0.5);
\r
2960 /* Draw an arrow between two points using current settings */
\r
2961 VOID DrawArrowBetweenPoints( HDC hdc, int s_x, int s_y, int d_x, int d_y )
\r
2964 double dx, dy, j, k, x, y;
\r
2966 if( d_x == s_x ) {
\r
2967 int h = (d_y > s_y) ? +A_WIDTH*A_HEIGHT_FACTOR : -A_WIDTH*A_HEIGHT_FACTOR;
\r
2969 arrow[0].x = s_x + A_WIDTH;
\r
2972 arrow[1].x = s_x + A_WIDTH;
\r
2973 arrow[1].y = d_y - h;
\r
2975 arrow[2].x = s_x + A_WIDTH*A_WIDTH_FACTOR;
\r
2976 arrow[2].y = d_y - h;
\r
2981 arrow[4].x = s_x - A_WIDTH*A_WIDTH_FACTOR;
\r
2982 arrow[4].y = d_y - h;
\r
2984 arrow[5].x = s_x - A_WIDTH;
\r
2985 arrow[5].y = d_y - h;
\r
2987 arrow[6].x = s_x - A_WIDTH;
\r
2990 else if( d_y == s_y ) {
\r
2991 int w = (d_x > s_x) ? +A_WIDTH*A_HEIGHT_FACTOR : -A_WIDTH*A_HEIGHT_FACTOR;
\r
2994 arrow[0].y = s_y + A_WIDTH;
\r
2996 arrow[1].x = d_x - w;
\r
2997 arrow[1].y = s_y + A_WIDTH;
\r
2999 arrow[2].x = d_x - w;
\r
3000 arrow[2].y = s_y + A_WIDTH*A_WIDTH_FACTOR;
\r
3005 arrow[4].x = d_x - w;
\r
3006 arrow[4].y = s_y - A_WIDTH*A_WIDTH_FACTOR;
\r
3008 arrow[5].x = d_x - w;
\r
3009 arrow[5].y = s_y - A_WIDTH;
\r
3012 arrow[6].y = s_y - A_WIDTH;
\r
3015 /* [AS] Needed a lot of paper for this! :-) */
\r
3016 dy = (double) (d_y - s_y) / (double) (d_x - s_x);
\r
3017 dx = (double) (s_x - d_x) / (double) (s_y - d_y);
\r
3019 j = sqrt( Sqr(A_WIDTH) / (1.0 + Sqr(dx)) );
\r
3021 k = sqrt( Sqr(A_WIDTH*A_HEIGHT_FACTOR) / (1.0 + Sqr(dy)) );
\r
3026 arrow[0].x = Round(x - j);
\r
3027 arrow[0].y = Round(y + j*dx);
\r
3029 arrow[1].x = Round(x + j);
\r
3030 arrow[1].y = Round(y - j*dx);
\r
3033 x = (double) d_x - k;
\r
3034 y = (double) d_y - k*dy;
\r
3037 x = (double) d_x + k;
\r
3038 y = (double) d_y + k*dy;
\r
3041 arrow[2].x = Round(x + j);
\r
3042 arrow[2].y = Round(y - j*dx);
\r
3044 arrow[3].x = Round(x + j*A_WIDTH_FACTOR);
\r
3045 arrow[3].y = Round(y - j*A_WIDTH_FACTOR*dx);
\r
3050 arrow[5].x = Round(x - j*A_WIDTH_FACTOR);
\r
3051 arrow[5].y = Round(y + j*A_WIDTH_FACTOR*dx);
\r
3053 arrow[6].x = Round(x - j);
\r
3054 arrow[6].y = Round(y + j*dx);
\r
3057 Polygon( hdc, arrow, 7 );
\r
3060 /* [AS] Draw an arrow between two squares */
\r
3061 VOID DrawArrowBetweenSquares( HDC hdc, int s_col, int s_row, int d_col, int d_row )
\r
3063 int s_x, s_y, d_x, d_y;
\r
3070 if( s_col == d_col && s_row == d_row ) {
\r
3074 /* Get source and destination points */
\r
3075 SquareToPos( s_row, s_col, &s_x, &s_y);
\r
3076 SquareToPos( d_row, d_col, &d_x, &d_y);
\r
3079 d_y += squareSize / 4;
\r
3081 else if( d_y < s_y ) {
\r
3082 d_y += 3 * squareSize / 4;
\r
3085 d_y += squareSize / 2;
\r
3089 d_x += squareSize / 4;
\r
3091 else if( d_x < s_x ) {
\r
3092 d_x += 3 * squareSize / 4;
\r
3095 d_x += squareSize / 2;
\r
3098 s_x += squareSize / 2;
\r
3099 s_y += squareSize / 2;
\r
3101 /* Adjust width */
\r
3102 A_WIDTH = squareSize / 14;
\r
3105 stLB.lbStyle = BS_SOLID;
\r
3106 stLB.lbColor = appData.highlightArrowColor;
\r
3109 hpen = CreatePen( PS_SOLID, 2, RGB(0x00,0x00,0x00) );
\r
3110 holdpen = SelectObject( hdc, hpen );
\r
3111 hbrush = CreateBrushIndirect( &stLB );
\r
3112 holdbrush = SelectObject( hdc, hbrush );
\r
3114 DrawArrowBetweenPoints( hdc, s_x, s_y, d_x, d_y );
\r
3116 SelectObject( hdc, holdpen );
\r
3117 SelectObject( hdc, holdbrush );
\r
3118 DeleteObject( hpen );
\r
3119 DeleteObject( hbrush );
\r
3122 BOOL HasHighlightInfo()
\r
3124 BOOL result = FALSE;
\r
3126 if( highlightInfo.sq[0].x >= 0 && highlightInfo.sq[0].y >= 0 &&
\r
3127 highlightInfo.sq[1].x >= 0 && highlightInfo.sq[1].y >= 0 )
\r
3135 BOOL IsDrawArrowEnabled()
\r
3137 BOOL result = FALSE;
\r
3139 if( appData.highlightMoveWithArrow && squareSize >= 32 ) {
\r
3146 VOID DrawArrowHighlight( HDC hdc )
\r
3148 if( IsDrawArrowEnabled() && HasHighlightInfo() ) {
\r
3149 DrawArrowBetweenSquares( hdc,
\r
3150 highlightInfo.sq[0].x, highlightInfo.sq[0].y,
\r
3151 highlightInfo.sq[1].x, highlightInfo.sq[1].y );
\r
3155 HRGN GetArrowHighlightClipRegion( HDC hdc )
\r
3157 HRGN result = NULL;
\r
3159 if( HasHighlightInfo() ) {
\r
3160 int x1, y1, x2, y2;
\r
3161 int sx, sy, dx, dy;
\r
3163 SquareToPos(highlightInfo.sq[0].y, highlightInfo.sq[0].x, &x1, &y1 );
\r
3164 SquareToPos(highlightInfo.sq[1].y, highlightInfo.sq[1].x, &x2, &y2 );
\r
3166 sx = MIN( x1, x2 );
\r
3167 sy = MIN( y1, y2 );
\r
3168 dx = MAX( x1, x2 ) + squareSize;
\r
3169 dy = MAX( y1, y2 ) + squareSize;
\r
3171 result = CreateRectRgn( sx, sy, dx, dy );
\r
3178 Warning: this function modifies the behavior of several other functions.
\r
3180 Basically, Winboard is optimized to avoid drawing the whole board if not strictly
\r
3181 needed. Unfortunately, the decision whether or not to perform a full or partial
\r
3182 repaint is scattered all over the place, which is not good for features such as
\r
3183 "arrow highlighting" that require a full repaint of the board.
\r
3185 So, I've tried to patch the code where I thought it made sense (e.g. after or during
\r
3186 user interaction, when speed is not so important) but especially to avoid errors
\r
3187 in the displayed graphics.
\r
3189 In such patched places, I always try refer to this function so there is a single
\r
3190 place to maintain knowledge.
\r
3192 To restore the original behavior, just return FALSE unconditionally.
\r
3194 BOOL IsFullRepaintPreferrable()
\r
3196 BOOL result = FALSE;
\r
3198 if( (appData.highlightLastMove || appData.highlightDragging) && IsDrawArrowEnabled() ) {
\r
3199 /* Arrow may appear on the board */
\r
3207 This function is called by DrawPosition to know whether a full repaint must
\r
3210 Only DrawPosition may directly call this function, which makes use of
\r
3211 some state information. Other function should call DrawPosition specifying
\r
3212 the repaint flag, and can use IsFullRepaintPreferrable if needed.
\r
3214 BOOL DrawPositionNeedsFullRepaint()
\r
3216 BOOL result = FALSE;
\r
3219 Probably a slightly better policy would be to trigger a full repaint
\r
3220 when animInfo.piece changes state (i.e. empty -> non-empty and viceversa),
\r
3221 but animation is fast enough that it's difficult to notice.
\r
3223 if( animInfo.piece == EmptySquare ) {
\r
3224 if( (appData.highlightLastMove || appData.highlightDragging) && IsDrawArrowEnabled() /*&& HasHighlightInfo()*/ ) {
\r
3233 DrawBoardOnDC(HDC hdc, Board board, HDC tmphdc)
\r
3235 int row, column, x, y, square_color, piece_color;
\r
3236 ChessSquare piece;
\r
3238 HDC texture_hdc = NULL;
\r
3240 /* [AS] Initialize background textures if needed */
\r
3241 if( liteBackTexture != NULL || darkBackTexture != NULL ) {
\r
3242 static int backTextureBoardSize; /* [HGM] boardsize: also new texture if board format changed */
\r
3243 if( backTextureSquareSize != squareSize
\r
3244 || backTextureBoardSize != BOARD_WIDTH+BOARD_FILES*BOARD_HEIGHT) {
\r
3245 backTextureBoardSize = BOARD_WIDTH+BOARD_FILES*BOARD_HEIGHT;
\r
3246 backTextureSquareSize = squareSize;
\r
3247 RebuildTextureSquareInfo();
\r
3250 texture_hdc = CreateCompatibleDC( hdc );
\r
3253 for (row = 0; row < BOARD_HEIGHT; row++) {
\r
3254 for (column = 0; column < BOARD_WIDTH; column++) {
\r
3256 SquareToPos(row, column, &x, &y);
\r
3258 piece = board[row][column];
\r
3260 square_color = ((column + row) % 2) == 1;
\r
3261 if( gameInfo.variant == VariantXiangqi ) {
\r
3262 square_color = !InPalace(row, column);
\r
3263 if(BOARD_HEIGHT&1) { if(row==BOARD_HEIGHT/2) square_color ^= 1; }
\r
3264 else if(row < BOARD_HEIGHT/2) square_color ^= 1;
\r
3266 piece_color = (int) piece < (int) BlackPawn;
\r
3269 /* [HGM] holdings file: light square or black */
\r
3270 if(column == BOARD_LEFT-2) {
\r
3271 if( row > BOARD_HEIGHT - gameInfo.holdingsSize - 1 )
\r
3274 DisplayHoldingsCount(hdc, x, y, 0, 0); /* black out */
\r
3278 if(column == BOARD_RGHT + 1 ) {
\r
3279 if( row < gameInfo.holdingsSize )
\r
3282 DisplayHoldingsCount(hdc, x, y, 0, 0);
\r
3286 if(column == BOARD_LEFT-1 ) /* left align */
\r
3287 DisplayHoldingsCount(hdc, x, y, flipView, (int) board[row][column]);
\r
3288 else if( column == BOARD_RGHT) /* right align */
\r
3289 DisplayHoldingsCount(hdc, x, y, !flipView, (int) board[row][column]);
\r
3291 if (appData.monoMode) {
\r
3292 if (piece == EmptySquare) {
\r
3293 BitBlt(hdc, x, y, squareSize, squareSize, 0, 0, 0,
\r
3294 square_color ? WHITENESS : BLACKNESS);
\r
3296 DrawPieceOnDC(hdc, piece, piece_color, square_color, x, y, tmphdc);
\r
3299 else if( backTextureSquareInfo[row][column].mode > 0 ) {
\r
3300 /* [AS] Draw the square using a texture bitmap */
\r
3301 HBITMAP hbm = SelectObject( texture_hdc, square_color ? liteBackTexture : darkBackTexture );
\r
3302 int r = row, c = column; // [HGM] do not flip board in flipView
\r
3303 if(flipView) { r = BOARD_HEIGHT-1 - r; c = BOARD_WIDTH-1 - c; }
\r
3306 squareSize, squareSize,
\r
3309 backTextureSquareInfo[r][c].mode,
\r
3310 backTextureSquareInfo[r][c].x,
\r
3311 backTextureSquareInfo[r][c].y );
\r
3313 SelectObject( texture_hdc, hbm );
\r
3315 if (piece != EmptySquare) {
\r
3316 DrawPieceOnDC(hdc, piece, piece_color, -1, x, y, tmphdc);
\r
3320 HBRUSH brush = square_color ? lightSquareBrush : darkSquareBrush;
\r
3322 oldBrush = SelectObject(hdc, brush );
\r
3323 BitBlt(hdc, x, y, squareSize, squareSize, 0, 0, 0, PATCOPY);
\r
3324 SelectObject(hdc, oldBrush);
\r
3325 if (piece != EmptySquare)
\r
3326 DrawPieceOnDC(hdc, piece, piece_color, -1, x, y, tmphdc);
\r
3331 if( texture_hdc != NULL ) {
\r
3332 DeleteDC( texture_hdc );
\r
3336 int saveDiagFlag = 0; FILE *diagFile; // [HGM] diag
\r
3337 void fputDW(FILE *f, int x)
\r
3339 fputc(x & 255, f);
\r
3340 fputc(x>>8 & 255, f);
\r
3341 fputc(x>>16 & 255, f);
\r
3342 fputc(x>>24 & 255, f);
\r
3345 #define MAX_CLIPS 200 /* more than enough */
\r
3348 DrawLogoOnDC(HDC hdc, RECT logoRect, HBITMAP logo)
\r
3350 // HBITMAP bufferBitmap;
\r
3355 int w = 100, h = 50;
\r
3357 if(logo == NULL) return;
\r
3358 // GetClientRect(hwndMain, &Rect);
\r
3359 // bufferBitmap = CreateCompatibleBitmap(hdc, Rect.right-Rect.left+1,
\r
3360 // Rect.bottom-Rect.top+1);
\r
3361 tmphdc = CreateCompatibleDC(hdc);
\r
3362 hbm = SelectObject(tmphdc, logo);
\r
3363 if( GetObject( logo, sizeof(bi), &bi ) > 0 ) {
\r
3367 StretchBlt(hdc, logoRect.left, logoRect.top, logoRect.right - logoRect.left,
\r
3368 logoRect.bottom - logoRect.top, tmphdc, 0, 0, w, h, SRCCOPY);
\r
3369 SelectObject(tmphdc, hbm);
\r
3373 static HDC hdcSeek;
\r
3375 // [HGM] seekgraph
\r
3376 void DrawSeekAxis( int x, int y, int xTo, int yTo )
\r
3379 HPEN hp = SelectObject( hdcSeek, gridPen );
\r
3380 MoveToEx( hdcSeek, boardRect.left+x, boardRect.top+y, &stPt );
\r
3381 LineTo( hdcSeek, boardRect.left+xTo, boardRect.top+yTo );
\r
3382 SelectObject( hdcSeek, hp );
\r
3385 // front-end wrapper for drawing functions to do rectangles
\r
3386 void DrawSeekBackground( int left, int top, int right, int bottom )
\r
3391 if (hdcSeek == NULL) {
\r
3392 hdcSeek = GetDC(hwndMain);
\r
3393 if (!appData.monoMode) {
\r
3394 SelectPalette(hdcSeek, hPal, FALSE);
\r
3395 RealizePalette(hdcSeek);
\r
3398 hp = SelectObject( hdcSeek, gridPen );
\r
3399 rc.top = boardRect.top+top; rc.left = boardRect.left+left;
\r
3400 rc.bottom = boardRect.top+bottom; rc.right = boardRect.left+right;
\r
3401 FillRect( hdcSeek, &rc, lightSquareBrush );
\r
3402 SelectObject( hdcSeek, hp );
\r
3405 // front-end wrapper for putting text in graph
\r
3406 void DrawSeekText(char *buf, int x, int y)
\r
3409 SetBkMode( hdcSeek, TRANSPARENT );
\r
3410 GetTextExtentPoint32( hdcSeek, buf, strlen(buf), &stSize );
\r
3411 TextOut( hdcSeek, boardRect.left+x-3, boardRect.top+y-stSize.cy/2, buf, strlen(buf) );
\r
3414 void DrawSeekDot(int x, int y, int color)
\r
3416 int square = color & 0x80;
\r
3417 HBRUSH oldBrush = SelectObject(hdcSeek,
\r
3418 color == 0 ? markerBrush : color == 1 ? darkSquareBrush : explodeBrush);
\r
3421 Rectangle(hdcSeek, boardRect.left+x - squareSize/9, boardRect.top+y - squareSize/9,
\r
3422 boardRect.left+x + squareSize/9, boardRect.top+y + squareSize/9);
\r
3424 Ellipse(hdcSeek, boardRect.left+x - squareSize/8, boardRect.top+y - squareSize/8,
\r
3425 boardRect.left+x + squareSize/8, boardRect.top+y + squareSize/8);
\r
3426 SelectObject(hdcSeek, oldBrush);
\r
3430 HDCDrawPosition(HDC hdc, BOOLEAN repaint, Board board)
\r
3432 static Board lastReq[2], lastDrawn[2];
\r
3433 static HighlightInfo lastDrawnHighlight, lastDrawnPremove;
\r
3434 static int lastDrawnFlipView = 0;
\r
3435 static int lastReqValid[2] = {0, 0}, lastDrawnValid[2] = {0, 0};
\r
3436 int releaseDC, x, y, x2, y2, row, column, num_clips = 0, i;
\r
3439 HBITMAP bufferBitmap;
\r
3440 HBITMAP oldBitmap;
\r
3442 HRGN clips[MAX_CLIPS];
\r
3443 ChessSquare dragged_piece = EmptySquare;
\r
3444 int nr = twoBoards*partnerUp;
\r
3446 /* I'm undecided on this - this function figures out whether a full
\r
3447 * repaint is necessary on its own, so there's no real reason to have the
\r
3448 * caller tell it that. I think this can safely be set to FALSE - but
\r
3449 * if we trust the callers not to request full repaints unnessesarily, then
\r
3450 * we could skip some clipping work. In other words, only request a full
\r
3451 * redraw when the majority of pieces have changed positions (ie. flip,
\r
3452 * gamestart and similar) --Hawk
\r
3454 Boolean fullrepaint = repaint;
\r
3456 if(DrawSeekGraph()) return; // [HG} seekgraph: suppress printing board if seek graph up
\r
3458 if( DrawPositionNeedsFullRepaint() ) {
\r
3459 fullrepaint = TRUE;
\r
3462 if (board == NULL) {
\r
3463 if (!lastReqValid[nr]) {
\r
3466 board = lastReq[nr];
\r
3468 CopyBoard(lastReq[nr], board);
\r
3469 lastReqValid[nr] = 1;
\r
3472 if (doingSizing) {
\r
3476 if (IsIconic(hwndMain)) {
\r
3480 if (hdc == NULL) {
\r
3481 hdc = GetDC(hwndMain);
\r
3482 if (!appData.monoMode) {
\r
3483 SelectPalette(hdc, hPal, FALSE);
\r
3484 RealizePalette(hdc);
\r
3488 releaseDC = FALSE;
\r
3491 /* Create some work-DCs */
\r
3492 hdcmem = CreateCompatibleDC(hdc);
\r
3493 tmphdc = CreateCompatibleDC(hdc);
\r
3495 /* If dragging is in progress, we temporarely remove the piece */
\r
3496 /* [HGM] or temporarily decrease count if stacked */
\r
3497 /* !! Moved to before board compare !! */
\r
3498 if (dragInfo.from.x >= 0 && dragInfo.pos.x >= 0) {
\r
3499 dragged_piece = board[dragInfo.from.y][dragInfo.from.x];
\r
3500 if(dragInfo.from.x == BOARD_LEFT-2 ) {
\r
3501 if(--board[dragInfo.from.y][dragInfo.from.x+1] == 0 )
\r
3502 board[dragInfo.from.y][dragInfo.from.x] = EmptySquare;
\r
3504 if(dragInfo.from.x == BOARD_RGHT+1) {
\r
3505 if(--board[dragInfo.from.y][dragInfo.from.x-1] == 0 )
\r
3506 board[dragInfo.from.y][dragInfo.from.x] = EmptySquare;
\r
3508 board[dragInfo.from.y][dragInfo.from.x] = EmptySquare;
\r
3511 /* Figure out which squares need updating by comparing the
\r
3512 * newest board with the last drawn board and checking if
\r
3513 * flipping has changed.
\r
3515 if (!fullrepaint && lastDrawnValid[nr] && (nr == 1 || lastDrawnFlipView == flipView)) {
\r
3516 for (row = 0; row < BOARD_HEIGHT; row++) { /* [HGM] true size, not 8 */
\r
3517 for (column = 0; column < BOARD_WIDTH; column++) {
\r
3518 if (lastDrawn[nr][row][column] != board[row][column]) {
\r
3519 SquareToPos(row, column, &x, &y);
\r
3520 clips[num_clips++] =
\r
3521 CreateRectRgn(x, y, x + squareSize, y + squareSize);
\r
3525 if(nr == 0) { // [HGM] dual: no highlights on second board
\r
3526 for (i=0; i<2; i++) {
\r
3527 if (lastDrawnHighlight.sq[i].x != highlightInfo.sq[i].x ||
\r
3528 lastDrawnHighlight.sq[i].y != highlightInfo.sq[i].y) {
\r
3529 if (lastDrawnHighlight.sq[i].x >= 0 &&
\r
3530 lastDrawnHighlight.sq[i].y >= 0) {
\r
3531 SquareToPos(lastDrawnHighlight.sq[i].y,
\r
3532 lastDrawnHighlight.sq[i].x, &x, &y);
\r
3533 clips[num_clips++] =
\r
3534 CreateRectRgn(x - lineGap, y - lineGap,
\r
3535 x + squareSize + lineGap, y + squareSize + lineGap);
\r
3537 if (highlightInfo.sq[i].x >= 0 && highlightInfo.sq[i].y >= 0) {
\r
3538 SquareToPos(highlightInfo.sq[i].y, highlightInfo.sq[i].x, &x, &y);
\r
3539 clips[num_clips++] =
\r
3540 CreateRectRgn(x - lineGap, y - lineGap,
\r
3541 x + squareSize + lineGap, y + squareSize + lineGap);
\r
3545 for (i=0; i<2; i++) {
\r
3546 if (lastDrawnPremove.sq[i].x != premoveHighlightInfo.sq[i].x ||
\r
3547 lastDrawnPremove.sq[i].y != premoveHighlightInfo.sq[i].y) {
\r
3548 if (lastDrawnPremove.sq[i].x >= 0 &&
\r
3549 lastDrawnPremove.sq[i].y >= 0) {
\r
3550 SquareToPos(lastDrawnPremove.sq[i].y,
\r
3551 lastDrawnPremove.sq[i].x, &x, &y);
\r
3552 clips[num_clips++] =
\r
3553 CreateRectRgn(x - lineGap, y - lineGap,
\r
3554 x + squareSize + lineGap, y + squareSize + lineGap);
\r
3556 if (premoveHighlightInfo.sq[i].x >= 0 &&
\r
3557 premoveHighlightInfo.sq[i].y >= 0) {
\r
3558 SquareToPos(premoveHighlightInfo.sq[i].y,
\r
3559 premoveHighlightInfo.sq[i].x, &x, &y);
\r
3560 clips[num_clips++] =
\r
3561 CreateRectRgn(x - lineGap, y - lineGap,
\r
3562 x + squareSize + lineGap, y + squareSize + lineGap);
\r
3566 } else { // nr == 1
\r
3567 partnerHighlightInfo.sq[0].y = board[EP_STATUS-4];
\r
3568 partnerHighlightInfo.sq[0].x = board[EP_STATUS-3];
\r
3569 partnerHighlightInfo.sq[1].y = board[EP_STATUS-2];
\r
3570 partnerHighlightInfo.sq[1].x = board[EP_STATUS-1];
\r
3571 for (i=0; i<2; i++) {
\r
3572 if (partnerHighlightInfo.sq[i].x >= 0 &&
\r
3573 partnerHighlightInfo.sq[i].y >= 0) {
\r
3574 SquareToPos(partnerHighlightInfo.sq[i].y,
\r
3575 partnerHighlightInfo.sq[i].x, &x, &y);
\r
3576 clips[num_clips++] =
\r
3577 CreateRectRgn(x - lineGap, y - lineGap,
\r
3578 x + squareSize + lineGap, y + squareSize + lineGap);
\r
3580 if (oldPartnerHighlight.sq[i].x >= 0 &&
\r
3581 oldPartnerHighlight.sq[i].y >= 0) {
\r
3582 SquareToPos(oldPartnerHighlight.sq[i].y,
\r
3583 oldPartnerHighlight.sq[i].x, &x, &y);
\r
3584 clips[num_clips++] =
\r
3585 CreateRectRgn(x - lineGap, y - lineGap,
\r
3586 x + squareSize + lineGap, y + squareSize + lineGap);
\r
3591 fullrepaint = TRUE;
\r
3594 /* Create a buffer bitmap - this is the actual bitmap
\r
3595 * being written to. When all the work is done, we can
\r
3596 * copy it to the real DC (the screen). This avoids
\r
3597 * the problems with flickering.
\r
3599 GetClientRect(hwndMain, &Rect);
\r
3600 bufferBitmap = CreateCompatibleBitmap(hdc, Rect.right-Rect.left+1,
\r
3601 Rect.bottom-Rect.top+1);
\r
3602 oldBitmap = SelectObject(hdcmem, bufferBitmap);
\r
3603 if (!appData.monoMode) {
\r
3604 SelectPalette(hdcmem, hPal, FALSE);
\r
3607 /* Create clips for dragging */
\r
3608 if (!fullrepaint) {
\r
3609 if (dragInfo.from.x >= 0) {
\r
3610 SquareToPos(dragInfo.from.y, dragInfo.from.x, &x, &y);
\r
3611 clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);
\r
3613 if (dragInfo.start.x >= 0) {
\r
3614 SquareToPos(dragInfo.start.y, dragInfo.start.x, &x, &y);
\r
3615 clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);
\r
3617 if (dragInfo.pos.x >= 0) {
\r
3618 x = dragInfo.pos.x - squareSize / 2;
\r
3619 y = dragInfo.pos.y - squareSize / 2;
\r
3620 clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);
\r
3622 if (dragInfo.lastpos.x >= 0) {
\r
3623 x = dragInfo.lastpos.x - squareSize / 2;
\r
3624 y = dragInfo.lastpos.y - squareSize / 2;
\r
3625 clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);
\r
3629 /* Are we animating a move?
\r
3631 * - remove the piece from the board (temporarely)
\r
3632 * - calculate the clipping region
\r
3634 if (!fullrepaint) {
\r
3635 if (animInfo.piece != EmptySquare) {
\r
3636 board[animInfo.from.y][animInfo.from.x] = EmptySquare;
\r
3637 x = boardRect.left + animInfo.lastpos.x;
\r
3638 y = boardRect.top + animInfo.lastpos.y;
\r
3639 x2 = boardRect.left + animInfo.pos.x;
\r
3640 y2 = boardRect.top + animInfo.pos.y;
\r
3641 clips[num_clips++] = CreateRectRgn(MIN(x,x2), MIN(y,y2), MAX(x,x2)+squareSize, MAX(y,y2)+squareSize);
\r
3642 /* Slight kludge. The real problem is that after AnimateMove is
\r
3643 done, the position on the screen does not match lastDrawn.
\r
3644 This currently causes trouble only on e.p. captures in
\r
3645 atomic, where the piece moves to an empty square and then
\r
3646 explodes. The old and new positions both had an empty square
\r
3647 at the destination, but animation has drawn a piece there and
\r
3648 we have to remember to erase it. [HGM] moved until after setting lastDrawn */
\r
3649 lastDrawn[0][animInfo.to.y][animInfo.to.x] = animInfo.piece;
\r
3653 /* No clips? Make sure we have fullrepaint set to TRUE */
\r
3654 if (num_clips == 0)
\r
3655 fullrepaint = TRUE;
\r
3657 /* Set clipping on the memory DC */
\r
3658 if (!fullrepaint) {
\r
3659 SelectClipRgn(hdcmem, clips[0]);
\r
3660 for (x = 1; x < num_clips; x++) {
\r
3661 if (ExtSelectClipRgn(hdcmem, clips[x], RGN_OR) == ERROR)
\r
3662 abort(); // this should never ever happen!
\r
3666 /* Do all the drawing to the memory DC */
\r
3667 if(explodeInfo.radius) { // [HGM] atomic
\r
3669 int x, y, r=(explodeInfo.radius * squareSize)/100;
\r
3670 board[explodeInfo.fromY][explodeInfo.fromX] = EmptySquare; // suppress display of capturer
\r
3671 SquareToPos(explodeInfo.toY, explodeInfo.toX, &x, &y);
\r
3672 x += squareSize/2;
\r
3673 y += squareSize/2;
\r
3674 if(!fullrepaint) {
\r
3675 clips[num_clips] = CreateRectRgn(x-r, y-r, x+r, y+r);
\r
3676 ExtSelectClipRgn(hdcmem, clips[num_clips++], RGN_OR);
\r
3678 DrawGridOnDC(hdcmem);
\r
3679 DrawHighlightsOnDC(hdcmem, &highlightInfo, HIGHLIGHT_PEN);
\r
3680 DrawHighlightsOnDC(hdcmem, &premoveHighlightInfo, PREMOVE_PEN);
\r
3681 DrawBoardOnDC(hdcmem, board, tmphdc);
\r
3682 oldBrush = SelectObject(hdcmem, explodeBrush);
\r
3683 Ellipse(hdcmem, x-r, y-r, x+r, y+r);
\r
3684 SelectObject(hdcmem, oldBrush);
\r
3686 DrawGridOnDC(hdcmem);
\r
3687 if(nr == 0) { // [HGM] dual: decide which highlights to draw
\r
3688 DrawHighlightsOnDC(hdcmem, &highlightInfo, HIGHLIGHT_PEN);
\r
3689 DrawHighlightsOnDC(hdcmem, &premoveHighlightInfo, PREMOVE_PEN);
\r
3691 DrawHighlightsOnDC(hdcmem, &partnerHighlightInfo, HIGHLIGHT_PEN);
\r
3692 oldPartnerHighlight = partnerHighlightInfo;
\r
3694 DrawBoardOnDC(hdcmem, board, tmphdc);
\r
3696 if(nr == 0) // [HGM] dual: markers only on left board
\r
3697 for (row = 0; row < BOARD_HEIGHT; row++) {
\r
3698 for (column = 0; column < BOARD_WIDTH; column++) {
\r
3699 if (marker[row][column]) { // marker changes only occur with full repaint!
\r
3700 HBRUSH oldBrush = SelectObject(hdcmem,
\r
3701 marker[row][column] == 2 ? markerBrush : explodeBrush);
\r
3702 SquareToPos(row, column, &x, &y);
\r
3703 Ellipse(hdcmem, x + squareSize/4, y + squareSize/4,
\r
3704 x + 3*squareSize/4, y + 3*squareSize/4);
\r
3705 SelectObject(hdcmem, oldBrush);
\r
3710 HBITMAP whiteLogo = (HBITMAP) first.programLogo, blackLogo = (HBITMAP) second.programLogo;
\r
3711 if(appData.autoLogo) {
\r
3713 switch(gameMode) { // pick logos based on game mode
\r
3714 case IcsObserving:
\r
3715 whiteLogo = second.programLogo; // ICS logo
\r
3716 blackLogo = second.programLogo;
\r
3719 case IcsPlayingWhite:
\r
3720 if(!appData.zippyPlay) whiteLogo = userLogo;
\r
3721 blackLogo = second.programLogo; // ICS logo
\r
3723 case IcsPlayingBlack:
\r
3724 whiteLogo = second.programLogo; // ICS logo
\r
3725 blackLogo = appData.zippyPlay ? first.programLogo : userLogo;
\r
3727 case TwoMachinesPlay:
\r
3728 if(first.twoMachinesColor[0] == 'b') {
\r
3729 whiteLogo = second.programLogo;
\r
3730 blackLogo = first.programLogo;
\r
3733 case MachinePlaysWhite:
\r
3734 blackLogo = userLogo;
\r
3736 case MachinePlaysBlack:
\r
3737 whiteLogo = userLogo;
\r
3738 blackLogo = first.programLogo;
\r
3741 DrawLogoOnDC(hdc, leftLogoRect, flipClock ? blackLogo : whiteLogo);
\r
3742 DrawLogoOnDC(hdc, rightLogoRect, flipClock ? whiteLogo : blackLogo);
\r
3745 if( appData.highlightMoveWithArrow ) {
\r
3746 DrawArrowHighlight(hdcmem);
\r
3749 DrawCoordsOnDC(hdcmem);
\r
3751 CopyBoard(lastDrawn[nr], board); /* [HGM] Moved to here from end of routine, */
\r
3752 /* to make sure lastDrawn contains what is actually drawn */
\r
3754 /* Put the dragged piece back into place and draw it (out of place!) */
\r
3755 if (dragged_piece != EmptySquare) {
\r
3756 /* [HGM] or restack */
\r
3757 if(dragInfo.from.x == BOARD_LEFT-2 )
\r
3758 board[dragInfo.from.y][dragInfo.from.x+1]++;
\r
3760 if(dragInfo.from.x == BOARD_RGHT+1 )
\r
3761 board[dragInfo.from.y][dragInfo.from.x-1]++;
\r
3762 board[dragInfo.from.y][dragInfo.from.x] = dragged_piece;
\r
3763 x = dragInfo.pos.x - squareSize / 2;
\r
3764 y = dragInfo.pos.y - squareSize / 2;
\r
3765 DrawPieceOnDC(hdcmem, dragged_piece,
\r
3766 ((int) dragged_piece < (int) BlackPawn),
\r
3767 (dragInfo.from.y + dragInfo.from.x) % 2, x, y, tmphdc);
\r
3770 /* Put the animated piece back into place and draw it */
\r
3771 if (animInfo.piece != EmptySquare) {
\r
3772 board[animInfo.from.y][animInfo.from.x] = animInfo.piece;
\r
3773 x = boardRect.left + animInfo.pos.x;
\r
3774 y = boardRect.top + animInfo.pos.y;
\r
3775 DrawPieceOnDC(hdcmem, animInfo.piece,
\r
3776 ((int) animInfo.piece < (int) BlackPawn),
\r
3777 (animInfo.from.y + animInfo.from.x) % 2, x, y, tmphdc);
\r
3780 /* Release the bufferBitmap by selecting in the old bitmap
\r
3781 * and delete the memory DC
\r
3783 SelectObject(hdcmem, oldBitmap);
\r
3786 /* Set clipping on the target DC */
\r
3787 if (!fullrepaint) {
\r
3788 if(nr == 1) for (x = 0; x < num_clips; x++) { // [HGM] dual: translate clips
\r
3790 GetRgnBox(clips[x], &rect);
\r
3791 DeleteObject(clips[x]);
\r
3792 clips[x] = CreateRectRgn(rect.left + wpMain.width/2, rect.top,
\r
3793 rect.right + wpMain.width/2, rect.bottom);
\r
3795 SelectClipRgn(hdc, clips[0]);
\r
3796 for (x = 1; x < num_clips; x++) {
\r
3797 if (ExtSelectClipRgn(hdc, clips[x], RGN_OR) == ERROR)
\r
3798 abort(); // this should never ever happen!
\r
3802 /* Copy the new bitmap onto the screen in one go.
\r
3803 * This way we avoid any flickering
\r
3805 oldBitmap = SelectObject(tmphdc, bufferBitmap);
\r
3806 BitBlt(hdc, boardRect.left + twoBoards*partnerUp*wpMain.width/2, boardRect.top, // [HGM] dual
\r
3807 boardRect.right - boardRect.left,
\r
3808 boardRect.bottom - boardRect.top,
\r
3809 tmphdc, boardRect.left, boardRect.top, SRCCOPY);
\r
3810 if(saveDiagFlag) {
\r
3811 BITMAP b; int i, j=0, m, w, wb, fac=0; char pData[1000000];
\r
3812 BITMAPINFOHEADER bih; int color[16], nrColors=0;
\r
3814 GetObject(bufferBitmap, sizeof(b), &b);
\r
3815 if(b.bmWidthBytes*b.bmHeight <= 990000) {
\r
3816 bih.biSize = sizeof(BITMAPINFOHEADER);
\r
3817 bih.biWidth = b.bmWidth;
\r
3818 bih.biHeight = b.bmHeight;
\r
3820 bih.biBitCount = b.bmBitsPixel;
\r
3821 bih.biCompression = 0;
\r
3822 bih.biSizeImage = b.bmWidthBytes*b.bmHeight;
\r
3823 bih.biXPelsPerMeter = 0;
\r
3824 bih.biYPelsPerMeter = 0;
\r
3825 bih.biClrUsed = 0;
\r
3826 bih.biClrImportant = 0;
\r
3827 // fprintf(diagFile, "t=%d\nw=%d\nh=%d\nB=%d\nP=%d\nX=%d\n",
\r
3828 // b.bmType, b.bmWidth, b.bmHeight, b.bmWidthBytes, b.bmPlanes, b.bmBitsPixel);
\r
3829 GetDIBits(tmphdc,bufferBitmap,0,b.bmHeight,pData,(BITMAPINFO*)&bih,DIB_RGB_COLORS);
\r
3830 // fprintf(diagFile, "%8x\n", (int) pData);
\r
3832 wb = b.bmWidthBytes;
\r
3834 for(i=0; i<wb*(b.bmHeight - boardRect.top + OUTER_MARGIN)>>2; i++) {
\r
3835 int k = ((int*) pData)[i];
\r
3836 for(j=0; j<nrColors; j++) if(color[j] == k) break;
\r
3837 if(j >= 16) break;
\r
3839 if(j >= nrColors) nrColors = j+1;
\r
3841 if(j<16) { // 16 colors is enough. Compress to 4 bits per pixel
\r
3843 for(i=0; i<b.bmHeight - boardRect.top + OUTER_MARGIN; i++) {
\r
3844 for(w=0; w<(wb>>2); w+=2) {
\r
3845 int k = ((int*) pData)[(wb*i>>2) + w];
\r
3846 for(j=0; j<nrColors; j++) if(color[j] == k) break;
\r
3847 k = ((int*) pData)[(wb*i>>2) + w + 1];
\r
3848 for(m=0; m<nrColors; m++) if(color[m] == k) break;
\r
3849 pData[p++] = m | j<<4;
\r
3851 while(p&3) pData[p++] = 0;
\r
3854 wb = ((wb+31)>>5)<<2;
\r
3856 // write BITMAPFILEHEADER
\r
3857 fprintf(diagFile, "BM");
\r
3858 fputDW(diagFile, wb*(b.bmHeight - boardRect.top + OUTER_MARGIN)+0x36 + (fac?64:0));
\r
3859 fputDW(diagFile, 0);
\r
3860 fputDW(diagFile, 0x36 + (fac?64:0));
\r
3861 // write BITMAPINFOHEADER
\r
3862 fputDW(diagFile, 40);
\r
3863 fputDW(diagFile, b.bmWidth);
\r
3864 fputDW(diagFile, b.bmHeight - boardRect.top + OUTER_MARGIN);
\r
3865 if(fac) fputDW(diagFile, 0x040001); // planes and bits/pixel
\r
3866 else fputDW(diagFile, 0x200001); // planes and bits/pixel
\r
3867 fputDW(diagFile, 0);
\r
3868 fputDW(diagFile, 0);
\r
3869 fputDW(diagFile, 0);
\r
3870 fputDW(diagFile, 0);
\r
3871 fputDW(diagFile, 0);
\r
3872 fputDW(diagFile, 0);
\r
3873 // write color table
\r
3875 for(i=0; i<16; i++) fputDW(diagFile, color[i]);
\r
3876 // write bitmap data
\r
3877 for(i=0; i<wb*(b.bmHeight - boardRect.top + OUTER_MARGIN); i++)
\r
3878 fputc(pData[i], diagFile);
\r
3882 SelectObject(tmphdc, oldBitmap);
\r
3884 /* Massive cleanup */
\r
3885 for (x = 0; x < num_clips; x++)
\r
3886 DeleteObject(clips[x]);
\r
3889 DeleteObject(bufferBitmap);
\r
3892 ReleaseDC(hwndMain, hdc);
\r
3894 if (lastDrawnFlipView != flipView && nr == 0) {
\r
3896 CheckMenuItem(GetMenu(hwndMain),IDM_FlipView, MF_BYCOMMAND|MF_CHECKED);
\r
3898 CheckMenuItem(GetMenu(hwndMain),IDM_FlipView, MF_BYCOMMAND|MF_UNCHECKED);
\r
3901 /* CopyBoard(lastDrawn, board);*/
\r
3902 lastDrawnHighlight = highlightInfo;
\r
3903 lastDrawnPremove = premoveHighlightInfo;
\r
3904 lastDrawnFlipView = flipView;
\r
3905 lastDrawnValid[nr] = 1;
\r
3908 /* [HGM] diag: Save the current board display to the given open file and close the file */
\r
3913 saveDiagFlag = 1; diagFile = f;
\r
3914 HDCDrawPosition(NULL, TRUE, NULL);
\r
3918 // if(f != NULL) fprintf(f, "Sorry, but this feature is still in preparation\n");
\r
3925 /*---------------------------------------------------------------------------*\
\r
3926 | CLIENT PAINT PROCEDURE
\r
3927 | This is the main event-handler for the WM_PAINT message.
\r
3929 \*---------------------------------------------------------------------------*/
\r
3931 PaintProc(HWND hwnd)
\r
3937 if((hdc = BeginPaint(hwnd, &ps))) {
\r
3938 if (IsIconic(hwnd)) {
\r
3939 DrawIcon(hdc, 2, 2, iconCurrent);
\r
3941 if (!appData.monoMode) {
\r
3942 SelectPalette(hdc, hPal, FALSE);
\r
3943 RealizePalette(hdc);
\r
3945 HDCDrawPosition(hdc, 1, NULL);
\r
3946 if(twoBoards) { // [HGM] dual: also redraw other board in other orientation
\r
3947 flipView = !flipView; partnerUp = !partnerUp;
\r
3948 HDCDrawPosition(hdc, 1, NULL);
\r
3949 flipView = !flipView; partnerUp = !partnerUp;
\r
3952 SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);
\r
3953 ExtTextOut(hdc, messageRect.left, messageRect.top,
\r
3954 ETO_CLIPPED|ETO_OPAQUE,
\r
3955 &messageRect, messageText, strlen(messageText), NULL);
\r
3956 SelectObject(hdc, oldFont);
\r
3957 DisplayBothClocks();
\r
3959 EndPaint(hwnd,&ps);
\r
3967 * If the user selects on a border boundary, return -1; if off the board,
\r
3968 * return -2. Otherwise map the event coordinate to the square.
\r
3969 * The offset boardRect.left or boardRect.top must already have been
\r
3970 * subtracted from x.
\r
3972 int EventToSquare(x, limit)
\r
3980 if ((x % (squareSize + lineGap)) >= squareSize)
\r
3982 x /= (squareSize + lineGap);
\r
3994 DropEnable dropEnables[] = {
\r
3995 { 'P', DP_Pawn, N_("Pawn") },
\r
3996 { 'N', DP_Knight, N_("Knight") },
\r
3997 { 'B', DP_Bishop, N_("Bishop") },
\r
3998 { 'R', DP_Rook, N_("Rook") },
\r
3999 { 'Q', DP_Queen, N_("Queen") },
\r
4003 SetupDropMenu(HMENU hmenu)
\r
4005 int i, count, enable;
\r
4007 extern char white_holding[], black_holding[];
\r
4008 char item[MSG_SIZ];
\r
4010 for (i=0; i<sizeof(dropEnables)/sizeof(DropEnable); i++) {
\r
4011 p = strchr(gameMode == IcsPlayingWhite ? white_holding : black_holding,
\r
4012 dropEnables[i].piece);
\r
4014 while (p && *p++ == dropEnables[i].piece) count++;
\r
4015 snprintf(item, MSG_SIZ, "%s %d", T_(dropEnables[i].name), count);
\r
4016 enable = count > 0 || !appData.testLegality
\r
4017 /*!!temp:*/ || (gameInfo.variant == VariantCrazyhouse
\r
4018 && !appData.icsActive);
\r
4019 ModifyMenu(hmenu, dropEnables[i].command,
\r
4020 MF_BYCOMMAND | (enable ? MF_ENABLED : MF_GRAYED) | MF_STRING,
\r
4021 dropEnables[i].command, item);
\r
4025 void DragPieceBegin(int x, int y)
\r
4027 dragInfo.lastpos.x = boardRect.left + x;
\r
4028 dragInfo.lastpos.y = boardRect.top + y;
\r
4029 dragInfo.from.x = fromX;
\r
4030 dragInfo.from.y = fromY;
\r
4031 dragInfo.start = dragInfo.from;
\r
4032 SetCapture(hwndMain);
\r
4035 void DragPieceEnd(int x, int y)
\r
4038 dragInfo.start.x = dragInfo.start.y = -1;
\r
4039 dragInfo.from = dragInfo.start;
\r
4040 dragInfo.pos = dragInfo.lastpos = dragInfo.start;
\r
4043 /* Event handler for mouse messages */
\r
4045 MouseEvent(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
\r
4049 static int recursive = 0;
\r
4051 BOOLEAN forceFullRepaint = IsFullRepaintPreferrable(); /* [AS] */
\r
4054 if (message == WM_MBUTTONUP) {
\r
4055 /* Hideous kludge to fool TrackPopupMenu into paying attention
\r
4056 to the middle button: we simulate pressing the left button too!
\r
4058 PostMessage(hwnd, WM_LBUTTONDOWN, wParam, lParam);
\r
4059 PostMessage(hwnd, WM_LBUTTONUP, wParam, lParam);
\r
4065 pt.x = LOWORD(lParam);
\r
4066 pt.y = HIWORD(lParam);
\r
4067 x = EventToSquare(pt.x - boardRect.left, BOARD_WIDTH);
\r
4068 y = EventToSquare(pt.y - boardRect.top, BOARD_HEIGHT);
\r
4069 if (!flipView && y >= 0) {
\r
4070 y = BOARD_HEIGHT - 1 - y;
\r
4072 if (flipView && x >= 0) {
\r
4073 x = BOARD_WIDTH - 1 - x;
\r
4076 shiftKey = GetKeyState(VK_SHIFT) < 0; // [HGM] remember last shift status
\r
4078 switch (message) {
\r
4079 case WM_LBUTTONDOWN:
\r
4080 if (PtInRect((LPRECT) &whiteRect, pt)) {
\r
4081 if (gameMode == EditPosition) {
\r
4082 SetWhiteToPlayEvent();
\r
4083 } else if (gameMode == EditGame || GetKeyState(VK_SHIFT) < 0) {
\r
4084 AdjustClock(flipClock, -1);
\r
4085 } else if (gameMode == IcsPlayingBlack ||
\r
4086 gameMode == MachinePlaysWhite) {
\r
4089 } else if (PtInRect((LPRECT) &blackRect, pt)) {
\r
4090 if (gameMode == EditPosition) {
\r
4091 SetBlackToPlayEvent();
\r
4092 } else if (gameMode == EditGame || GetKeyState(VK_SHIFT) < 0) {
\r
4093 AdjustClock(!flipClock, -1);
\r
4094 } else if (gameMode == IcsPlayingWhite ||
\r
4095 gameMode == MachinePlaysBlack) {
\r
4099 dragInfo.start.x = dragInfo.start.y = -1;
\r
4100 dragInfo.from = dragInfo.start;
\r
4101 if(fromX == -1 && frozen) { // not sure where this is for
\r
4102 fromX = fromY = -1;
\r
4103 DrawPosition(forceFullRepaint || FALSE, NULL); /* [AS] */
\r
4106 LeftClick(Press, pt.x - boardRect.left, pt.y - boardRect.top);
\r
4107 DrawPosition(TRUE, NULL);
\r
4110 case WM_LBUTTONUP:
\r
4111 LeftClick(Release, pt.x - boardRect.left, pt.y - boardRect.top);
\r
4112 DrawPosition(TRUE, NULL);
\r
4115 case WM_MOUSEMOVE:
\r
4116 if(SeekGraphClick(Press, pt.x - boardRect.left, pt.y - boardRect.top, 1)) break;
\r
4117 MovePV(pt.x - boardRect.left, pt.y - boardRect.top, boardRect.bottom - boardRect.top);
\r
4118 if ((appData.animateDragging || appData.highlightDragging)
\r
4119 && (wParam & MK_LBUTTON)
\r
4120 && dragInfo.from.x >= 0)
\r
4122 BOOL full_repaint = FALSE;
\r
4124 if (appData.animateDragging) {
\r
4125 dragInfo.pos = pt;
\r
4127 if (appData.highlightDragging) {
\r
4128 SetHighlights(fromX, fromY, x, y);
\r
4129 if( IsDrawArrowEnabled() && (x < 0 || x >= BOARD_WIDTH || y < 0 || y >= BOARD_HEIGHT) ) {
\r
4130 full_repaint = TRUE;
\r
4134 DrawPosition( full_repaint, NULL);
\r
4136 dragInfo.lastpos = dragInfo.pos;
\r
4140 case WM_MOUSEWHEEL: // [DM]
\r
4141 { static int lastDir = 0; // [HGM] build in some hysteresis to avoid spurious events
\r
4142 /* Mouse Wheel is being rolled forward
\r
4143 * Play moves forward
\r
4145 if((short)HIWORD(wParam) < 0 && currentMove < forwardMostMove)
\r
4146 { if(lastDir == 1) ForwardEvent(); else lastDir = 1; } // [HGM] suppress first event in direction
\r
4147 /* Mouse Wheel is being rolled backward
\r
4148 * Play moves backward
\r
4150 if((short)HIWORD(wParam) > 0 && currentMove > backwardMostMove)
\r
4151 { if(lastDir == -1) BackwardEvent(); else lastDir = -1; }
\r
4155 case WM_MBUTTONUP:
\r
4156 case WM_RBUTTONUP:
\r
4158 RightClick(Release, pt.x - boardRect.left, pt.y - boardRect.top, &fromX, &fromY);
\r
4161 case WM_MBUTTONDOWN:
\r
4162 case WM_RBUTTONDOWN:
\r
4165 fromX = fromY = -1;
\r
4166 dragInfo.pos.x = dragInfo.pos.y = -1;
\r
4167 dragInfo.start.x = dragInfo.start.y = -1;
\r
4168 dragInfo.from = dragInfo.start;
\r
4169 dragInfo.lastpos = dragInfo.pos;
\r
4170 if (appData.highlightDragging) {
\r
4171 ClearHighlights();
\r
4174 /* [HGM] right mouse button in clock area edit-game mode ups clock */
\r
4175 if (PtInRect((LPRECT) &whiteRect, pt)) {
\r
4176 if (gameMode == EditGame || GetKeyState(VK_SHIFT) < 0) AdjustClock(flipClock, 1);
\r
4177 } else if (PtInRect((LPRECT) &blackRect, pt)) {
\r
4178 if (gameMode == EditGame || GetKeyState(VK_SHIFT) < 0) AdjustClock(!flipClock, 1);
\r
4182 DrawPosition(TRUE, NULL);
\r
4184 menuNr = RightClick(Press, pt.x - boardRect.left, pt.y - boardRect.top, &fromX, &fromY);
\r
4187 if (message == WM_MBUTTONDOWN) {
\r
4188 buttonCount = 3; /* even if system didn't think so */
\r
4189 if (wParam & MK_SHIFT)
\r
4190 MenuPopup(hwnd, pt, LoadMenu(hInst, "BlackPieceMenu"), -1);
\r
4192 MenuPopup(hwnd, pt, LoadMenu(hInst, "WhitePieceMenu"), -1);
\r
4193 } else { /* message == WM_RBUTTONDOWN */
\r
4194 /* Just have one menu, on the right button. Windows users don't
\r
4195 think to try the middle one, and sometimes other software steals
\r
4196 it, or it doesn't really exist. */
\r
4197 if(gameInfo.variant != VariantShogi)
\r
4198 MenuPopup(hwnd, pt, LoadMenu(hInst, "PieceMenu"), -1);
\r
4200 MenuPopup(hwnd, pt, LoadMenu(hInst, "ShogiPieceMenu"), -1);
\r
4204 SetCapture(hwndMain);
4207 hmenu = LoadMenu(hInst, "DropPieceMenu");
\r
4208 SetupDropMenu(hmenu);
\r
4209 MenuPopup(hwnd, pt, hmenu, -1);
\r
4219 /* Preprocess messages for buttons in main window */
\r
4221 ButtonProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
\r
4223 int id = GetWindowLong(hwnd, GWL_ID);
\r
4226 for (i=0; i<N_BUTTONS; i++) {
\r
4227 if (buttonDesc[i].id == id) break;
\r
4229 if (i == N_BUTTONS) return 0;
\r
4230 switch (message) {
\r
4235 dir = (wParam == VK_LEFT) ? -1 : 1;
\r
4236 SetFocus(buttonDesc[(i + dir + N_BUTTONS) % N_BUTTONS].hwnd);
\r
4243 SendMessage(hwndMain, WM_COMMAND, MAKEWPARAM(buttonDesc[i].id, 0), 0);
\r
4246 if (appData.icsActive && (isalpha((char)wParam) || wParam == '0')) {
\r
4247 // [HGM] movenum: only letters or leading zero should go to ICS input
\r
4248 HWND h = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
4249 if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);
\r
4251 SendMessage(h, WM_CHAR, wParam, lParam);
\r
4253 } else if (isalpha((char)wParam) || isdigit((char)wParam)){
\r
4254 PopUpMoveDialog((char)wParam);
\r
4260 return CallWindowProc(buttonDesc[i].wndproc, hwnd, message, wParam, lParam);
\r
4263 /* Process messages for Promotion dialog box */
\r
4265 Promotion(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
4269 switch (message) {
\r
4270 case WM_INITDIALOG: /* message: initialize dialog box */
\r
4271 /* Center the dialog over the application window */
\r
4272 CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));
\r
4273 Translate(hDlg, DLG_PromotionKing);
\r
4274 ShowWindow(GetDlgItem(hDlg, PB_King),
\r
4275 (!appData.testLegality || gameInfo.variant == VariantSuicide ||
\r
4276 gameInfo.variant == VariantGiveaway || gameInfo.variant == VariantSuper ) ?
\r
4277 SW_SHOW : SW_HIDE);
\r
4278 /* [HGM] Only allow C & A promotions if these pieces are defined */
\r
4279 ShowWindow(GetDlgItem(hDlg, PB_Archbishop),
\r
4280 ((PieceToChar(WhiteAngel) >= 'A' &&
\r
4281 PieceToChar(WhiteAngel) != '~') ||
\r
4282 (PieceToChar(BlackAngel) >= 'A' &&
\r
4283 PieceToChar(BlackAngel) != '~') ) ?
\r
4284 SW_SHOW : SW_HIDE);
\r
4285 ShowWindow(GetDlgItem(hDlg, PB_Chancellor),
\r
4286 ((PieceToChar(WhiteMarshall) >= 'A' &&
\r
4287 PieceToChar(WhiteMarshall) != '~') ||
\r
4288 (PieceToChar(BlackMarshall) >= 'A' &&
\r
4289 PieceToChar(BlackMarshall) != '~') ) ?
\r
4290 SW_SHOW : SW_HIDE);
\r
4291 /* [HGM] Hide B & R button in Shogi, use Q as promote, N as defer */
\r
4292 ShowWindow(GetDlgItem(hDlg, PB_Rook),
\r
4293 gameInfo.variant != VariantShogi ?
\r
4294 SW_SHOW : SW_HIDE);
\r
4295 ShowWindow(GetDlgItem(hDlg, PB_Bishop),
\r
4296 gameInfo.variant != VariantShogi ?
\r
4297 SW_SHOW : SW_HIDE);
\r
4298 ShowWindow(GetDlgItem(hDlg, IDC_Yes),
\r
4299 gameInfo.variant == VariantShogi ?
\r
4300 SW_SHOW : SW_HIDE);
\r
4301 ShowWindow(GetDlgItem(hDlg, IDC_No),
\r
4302 gameInfo.variant == VariantShogi ?
\r
4303 SW_SHOW : SW_HIDE);
\r
4304 ShowWindow(GetDlgItem(hDlg, IDC_Centaur),
\r
4305 gameInfo.variant == VariantSuper ?
\r
4306 SW_SHOW : SW_HIDE);
\r
4309 case WM_COMMAND: /* message: received a command */
\r
4310 switch (LOWORD(wParam)) {
\r
4312 EndDialog(hDlg, TRUE); /* Exit the dialog */
\r
4313 ClearHighlights();
\r
4314 DrawPosition(FALSE, NULL);
\r
4317 promoChar = gameInfo.variant == VariantSuper ? PieceToChar(BlackSilver) : PieceToChar(BlackKing);
\r
4320 promoChar = gameInfo.variant == VariantShogi ? '+' : PieceToChar(BlackQueen);
\r
4323 promoChar = PieceToChar(BlackRook);
\r
4326 promoChar = PieceToChar(BlackBishop);
\r
4328 case PB_Chancellor:
\r
4329 promoChar = PieceToChar(BlackMarshall);
\r
4331 case PB_Archbishop:
\r
4332 promoChar = PieceToChar(BlackAngel);
\r
4335 promoChar = gameInfo.variant == VariantShogi ? '=' : PieceToChar(BlackKnight);
\r
4340 EndDialog(hDlg, TRUE); /* Exit the dialog */
\r
4341 /* [HGM] <popupFix> Call FinishMove rather than UserMoveEvent, as we
\r
4342 only show the popup when we are already sure the move is valid or
\r
4343 legal. We pass a faulty move type, but the kludge is that FinishMove
\r
4344 will figure out it is a promotion from the promoChar. */
\r
4345 UserMoveEvent(fromX, fromY, toX, toY, promoChar);
\r
4346 fromX = fromY = -1;
\r
4347 if (!appData.highlightLastMove) {
\r
4348 ClearHighlights();
\r
4349 DrawPosition(FALSE, NULL);
\r
4356 /* Pop up promotion dialog */
\r
4358 PromotionPopup(HWND hwnd)
\r
4362 lpProc = MakeProcInstance((FARPROC)Promotion, hInst);
\r
4363 DialogBox(hInst, MAKEINTRESOURCE(DLG_PromotionKing),
\r
4364 hwnd, (DLGPROC)lpProc);
\r
4365 FreeProcInstance(lpProc);
\r
4371 DrawPosition(TRUE, NULL);
\r
4372 PromotionPopup(hwndMain);
\r
4375 /* Toggle ShowThinking */
\r
4377 ToggleShowThinking()
\r
4379 appData.showThinking = !appData.showThinking;
\r
4380 ShowThinkingEvent();
\r
4384 LoadGameDialog(HWND hwnd, char* title)
\r
4388 char fileTitle[MSG_SIZ];
\r
4389 f = OpenFileDialog(hwnd, "rb", "",
\r
4390 appData.oldSaveStyle ? "gam" : "pgn",
\r
4392 title, &number, fileTitle, NULL);
\r
4394 cmailMsgLoaded = FALSE;
\r
4395 if (number == 0) {
\r
4396 int error = GameListBuild(f);
\r
4398 DisplayError(_("Cannot build game list"), error);
\r
4399 } else if (!ListEmpty(&gameList) &&
\r
4400 ((ListGame *) gameList.tailPred)->number > 1) {
\r
4401 GameListPopUp(f, fileTitle);
\r
4404 GameListDestroy();
\r
4407 LoadGame(f, number, fileTitle, FALSE);
\r
4411 int get_term_width()
\r
4416 HFONT hfont, hold_font;
\r
4421 hText = GetDlgItem(hwndConsole, OPT_ConsoleText);
\r
4425 // get the text metrics
\r
4426 hdc = GetDC(hText);
\r
4427 lf = font[boardSize][CONSOLE_FONT]->lf;
\r
4428 if (consoleCF.dwEffects & CFE_BOLD)
\r
4429 lf.lfWeight = FW_BOLD;
\r
4430 if (consoleCF.dwEffects & CFE_ITALIC)
\r
4431 lf.lfItalic = TRUE;
\r
4432 if (consoleCF.dwEffects & CFE_STRIKEOUT)
\r
4433 lf.lfStrikeOut = TRUE;
\r
4434 if (consoleCF.dwEffects & CFE_UNDERLINE)
\r
4435 lf.lfUnderline = TRUE;
\r
4436 hfont = CreateFontIndirect(&lf);
\r
4437 hold_font = SelectObject(hdc, hfont);
\r
4438 GetTextMetrics(hdc, &tm);
\r
4439 SelectObject(hdc, hold_font);
\r
4440 DeleteObject(hfont);
\r
4441 ReleaseDC(hText, hdc);
\r
4443 // get the rectangle
\r
4444 SendMessage(hText, EM_GETRECT, 0, (LPARAM)&rc);
\r
4446 return (rc.right-rc.left) / tm.tmAveCharWidth;
\r
4449 void UpdateICSWidth(HWND hText)
\r
4451 LONG old_width, new_width;
\r
4453 new_width = get_term_width(hText, FALSE);
\r
4454 old_width = GetWindowLong(hText, GWL_USERDATA);
\r
4455 if (new_width != old_width)
\r
4457 ics_update_width(new_width);
\r
4458 SetWindowLong(hText, GWL_USERDATA, new_width);
\r
4463 ChangedConsoleFont()
\r
4466 CHARRANGE tmpsel, sel;
\r
4467 MyFont *f = font[boardSize][CONSOLE_FONT];
\r
4468 HWND hText = GetDlgItem(hwndConsole, OPT_ConsoleText);
\r
4469 HWND hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
4472 cfmt.cbSize = sizeof(CHARFORMAT);
\r
4473 cfmt.dwMask = CFM_FACE|CFM_SIZE|CFM_CHARSET;
\r
4474 safeStrCpy(cfmt.szFaceName, font[boardSize][CONSOLE_FONT]->mfp.faceName,
\r
4475 sizeof(cfmt.szFaceName)/sizeof(cfmt.szFaceName[0]) );
\r
4476 /* yHeight is expressed in twips. A twip is 1/20 of a font's point
\r
4477 * size. This was undocumented in the version of MSVC++ that I had
\r
4478 * when I wrote the code, but is apparently documented now.
\r
4480 cfmt.yHeight = (int)(f->mfp.pointSize * 20.0 + 0.5);
\r
4481 cfmt.bCharSet = f->lf.lfCharSet;
\r
4482 cfmt.bPitchAndFamily = f->lf.lfPitchAndFamily;
\r
4483 SendMessage(hText, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &cfmt);
\r
4484 SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &cfmt);
\r
4485 /* Why are the following seemingly needed too? */
\r
4486 SendMessage(hText, EM_SETCHARFORMAT, SCF_DEFAULT, (LPARAM) &cfmt);
\r
4487 SendMessage(hInput, EM_SETCHARFORMAT, SCF_DEFAULT, (LPARAM) &cfmt);
\r
4488 SendMessage(hText, EM_EXGETSEL, 0, (LPARAM)&sel);
\r
4490 tmpsel.cpMax = -1; /*999999?*/
\r
4491 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&tmpsel);
\r
4492 SendMessage(hText, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cfmt);
\r
4493 /* Trying putting this here too. It still seems to tickle a RichEdit
\r
4494 * bug: sometimes RichEdit indents the first line of a paragraph too.
\r
4496 paraf.cbSize = sizeof(paraf);
\r
4497 paraf.dwMask = PFM_OFFSET | PFM_STARTINDENT;
\r
4498 paraf.dxStartIndent = 0;
\r
4499 paraf.dxOffset = WRAP_INDENT;
\r
4500 SendMessage(hText, EM_SETPARAFORMAT, 0, (LPARAM) ¶f);
\r
4501 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
4502 UpdateICSWidth(hText);
\r
4505 /*---------------------------------------------------------------------------*\
\r
4507 * Window Proc for main window
\r
4509 \*---------------------------------------------------------------------------*/
\r
4511 /* Process messages for main window, etc. */
\r
4513 WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
\r
4516 int wmId, wmEvent;
\r
4520 char fileTitle[MSG_SIZ];
\r
4521 char buf[MSG_SIZ];
\r
4522 static SnapData sd;
\r
4524 switch (message) {
\r
4526 case WM_PAINT: /* message: repaint portion of window */
\r
4530 case WM_ERASEBKGND:
\r
4531 if (IsIconic(hwnd)) {
\r
4532 /* Cheat; change the message */
\r
4533 return (DefWindowProc(hwnd, WM_ICONERASEBKGND, wParam, lParam));
\r
4535 return (DefWindowProc(hwnd, message, wParam, lParam));
\r
4539 case WM_LBUTTONDOWN:
\r
4540 case WM_MBUTTONDOWN:
\r
4541 case WM_RBUTTONDOWN:
\r
4542 case WM_LBUTTONUP:
\r
4543 case WM_MBUTTONUP:
\r
4544 case WM_RBUTTONUP:
\r
4545 case WM_MOUSEMOVE:
\r
4546 case WM_MOUSEWHEEL:
\r
4547 MouseEvent(hwnd, message, wParam, lParam);
\r
4550 JAWS_KB_NAVIGATION
\r
4554 JAWS_ALT_INTERCEPT
\r
4556 if (appData.icsActive && ((char)wParam == '\r' || (char)wParam > ' ' && !((char)wParam >= '1' && (char)wParam <= '9'))) {
\r
4557 // [HGM] movenum: for non-zero digits we always do type-in dialog
\r
4558 HWND h = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
4559 if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);
\r
4561 SendMessage(h, message, wParam, lParam);
\r
4562 } else if(lParam != KF_REPEAT) {
\r
4563 if (isalpha((char)wParam) || isdigit((char)wParam)) {
\r
4564 PopUpMoveDialog((char)wParam);
\r
4565 } else if((char)wParam == 003) CopyGameToClipboard();
\r
4566 else if((char)wParam == 026) PasteGameOrFENFromClipboard();
\r
4571 case WM_PALETTECHANGED:
\r
4572 if (hwnd != (HWND)wParam && !appData.monoMode) {
\r
4574 HDC hdc = GetDC(hwndMain);
\r
4575 SelectPalette(hdc, hPal, TRUE);
\r
4576 nnew = RealizePalette(hdc);
\r
4578 paletteChanged = TRUE;
\r
4579 InvalidateRect(hwnd, &boardRect, FALSE);
\r
4581 ReleaseDC(hwnd, hdc);
\r
4585 case WM_QUERYNEWPALETTE:
\r
4586 if (!appData.monoMode /*&& paletteChanged*/) {
\r
4588 HDC hdc = GetDC(hwndMain);
\r
4589 paletteChanged = FALSE;
\r
4590 SelectPalette(hdc, hPal, FALSE);
\r
4591 nnew = RealizePalette(hdc);
\r
4593 InvalidateRect(hwnd, &boardRect, FALSE);
\r
4595 ReleaseDC(hwnd, hdc);
\r
4600 case WM_COMMAND: /* message: command from application menu */
\r
4601 wmId = LOWORD(wParam);
\r
4602 wmEvent = HIWORD(wParam);
\r
4607 SAY("new game enter a move to play against the computer with white");
\r
4610 case IDM_NewGameFRC:
\r
4611 if( NewGameFRC() == 0 ) {
\r
4616 case IDM_NewVariant:
\r
4617 NewVariantPopup(hwnd);
\r
4620 case IDM_LoadGame:
\r
4621 LoadGameDialog(hwnd, _("Load Game from File"));
\r
4624 case IDM_LoadNextGame:
\r
4628 case IDM_LoadPrevGame:
\r
4632 case IDM_ReloadGame:
\r
4636 case IDM_LoadPosition:
\r
4637 if (gameMode == AnalyzeMode || gameMode == AnalyzeFile) {
\r
4638 Reset(FALSE, TRUE);
\r
4641 f = OpenFileDialog(hwnd, "rb", "",
\r
4642 appData.oldSaveStyle ? "pos" : "fen",
\r
4644 _("Load Position from File"), &number, fileTitle, NULL);
\r
4646 LoadPosition(f, number, fileTitle);
\r
4650 case IDM_LoadNextPosition:
\r
4651 ReloadPosition(1);
\r
4654 case IDM_LoadPrevPosition:
\r
4655 ReloadPosition(-1);
\r
4658 case IDM_ReloadPosition:
\r
4659 ReloadPosition(0);
\r
4662 case IDM_SaveGame:
\r
4663 defName = DefaultFileName(appData.oldSaveStyle ? "gam" : "pgn");
\r
4664 f = OpenFileDialog(hwnd, "a", defName,
\r
4665 appData.oldSaveStyle ? "gam" : "pgn",
\r
4667 _("Save Game to File"), NULL, fileTitle, NULL);
\r
4669 SaveGame(f, 0, "");
\r
4673 case IDM_SavePosition:
\r
4674 defName = DefaultFileName(appData.oldSaveStyle ? "pos" : "fen");
\r
4675 f = OpenFileDialog(hwnd, "a", defName,
\r
4676 appData.oldSaveStyle ? "pos" : "fen",
\r
4678 _("Save Position to File"), NULL, fileTitle, NULL);
\r
4680 SavePosition(f, 0, "");
\r
4684 case IDM_SaveDiagram:
\r
4685 defName = "diagram";
\r
4686 f = OpenFileDialog(hwnd, "wb", defName,
\r
4689 "Save Diagram to File", NULL, fileTitle, NULL);
\r
4695 case IDM_CopyGame:
\r
4696 CopyGameToClipboard();
\r
4699 case IDM_PasteGame:
\r
4700 PasteGameFromClipboard();
\r
4703 case IDM_CopyGameListToClipboard:
\r
4704 CopyGameListToClipboard();
\r
4707 /* [AS] Autodetect FEN or PGN data */
\r
4708 case IDM_PasteAny:
\r
4709 PasteGameOrFENFromClipboard();
\r
4712 /* [AS] Move history */
\r
4713 case IDM_ShowMoveHistory:
\r
4714 if( MoveHistoryIsUp() ) {
\r
4715 MoveHistoryPopDown();
\r
4718 MoveHistoryPopUp();
\r
4722 /* [AS] Eval graph */
\r
4723 case IDM_ShowEvalGraph:
\r
4724 if( EvalGraphIsUp() ) {
\r
4725 EvalGraphPopDown();
\r
4729 SetFocus(hwndMain);
\r
4733 /* [AS] Engine output */
\r
4734 case IDM_ShowEngineOutput:
\r
4735 if( EngineOutputIsUp() ) {
\r
4736 EngineOutputPopDown();
\r
4739 EngineOutputPopUp();
\r
4743 /* [AS] User adjudication */
\r
4744 case IDM_UserAdjudication_White:
\r
4745 UserAdjudicationEvent( +1 );
\r
4748 case IDM_UserAdjudication_Black:
\r
4749 UserAdjudicationEvent( -1 );
\r
4752 case IDM_UserAdjudication_Draw:
\r
4753 UserAdjudicationEvent( 0 );
\r
4756 /* [AS] Game list options dialog */
\r
4757 case IDM_GameListOptions:
\r
4758 GameListOptions();
\r
4765 case IDM_CopyPosition:
\r
4766 CopyFENToClipboard();
\r
4769 case IDM_PastePosition:
\r
4770 PasteFENFromClipboard();
\r
4773 case IDM_MailMove:
\r
4777 case IDM_ReloadCMailMsg:
\r
4778 Reset(TRUE, TRUE);
\r
4779 ReloadCmailMsgEvent(FALSE);
\r
4782 case IDM_Minimize:
\r
4783 ShowWindow(hwnd, SW_MINIMIZE);
\r
4790 case IDM_MachineWhite:
\r
4791 MachineWhiteEvent();
\r
4793 * refresh the tags dialog only if it's visible
\r
4795 if (gameMode == MachinePlaysWhite && IsWindowVisible(editTagsDialog)) {
\r
4797 tags = PGNTags(&gameInfo);
\r
4798 TagsPopUp(tags, CmailMsg());
\r
4801 SAY("computer starts playing white");
\r
4804 case IDM_MachineBlack:
\r
4805 MachineBlackEvent();
\r
4807 * refresh the tags dialog only if it's visible
\r
4809 if (gameMode == MachinePlaysBlack && IsWindowVisible(editTagsDialog)) {
\r
4811 tags = PGNTags(&gameInfo);
\r
4812 TagsPopUp(tags, CmailMsg());
\r
4815 SAY("computer starts playing black");
\r
4818 case IDM_Match: // [HGM] match: flows into next case, after setting Match Mode and nr of Games
\r
4819 if(gameMode != BeginningOfGame) break; // allow menu item to remain enabled for better mode highligting
\r
4820 matchMode = 2;// distinguish from command-line-triggered case (matchMode=1)
\r
4821 appData.matchGames = appData.defaultMatchGames;
\r
4824 case IDM_TwoMachines:
\r
4825 TwoMachinesEvent();
\r
4827 * refresh the tags dialog only if it's visible
\r
4829 if (gameMode == TwoMachinesPlay && IsWindowVisible(editTagsDialog)) {
\r
4831 tags = PGNTags(&gameInfo);
\r
4832 TagsPopUp(tags, CmailMsg());
\r
4835 SAY("computer starts playing both sides");
\r
4838 case IDM_AnalysisMode:
\r
4839 if (!first.analysisSupport) {
\r
4840 snprintf(buf, MSG_SIZ, _("%s does not support analysis"), first.tidy);
\r
4841 DisplayError(buf, 0);
\r
4843 SAY("analyzing current position");
\r
4844 /* [DM] icsEngineAnlyze [HGM] Why is this front-end??? */
\r
4845 if (appData.icsActive) {
\r
4846 if (gameMode != IcsObserving) {
\r
4847 snprintf(buf, MSG_SIZ, "You are not observing a game");
\r
4848 DisplayError(buf, 0);
\r
4849 /* secure check */
\r
4850 if (appData.icsEngineAnalyze) {
\r
4851 if (appData.debugMode)
\r
4852 fprintf(debugFP, "Found unexpected active ICS engine analyze \n");
\r
4853 ExitAnalyzeMode();
\r
4859 /* if enable, user want disable icsEngineAnalyze */
\r
4860 if (appData.icsEngineAnalyze) {
\r
4861 ExitAnalyzeMode();
\r
4865 appData.icsEngineAnalyze = TRUE;
\r
4866 if (appData.debugMode) fprintf(debugFP, "ICS engine analyze starting...\n");
\r
4869 if (!appData.showThinking) ToggleShowThinking();
\r
4870 AnalyzeModeEvent();
\r
4874 case IDM_AnalyzeFile:
\r
4875 if (!first.analysisSupport) {
\r
4876 char buf[MSG_SIZ];
\r
4877 snprintf(buf, MSG_SIZ, _("%s does not support analysis"), first.tidy);
\r
4878 DisplayError(buf, 0);
\r
4880 if (!appData.showThinking) ToggleShowThinking();
\r
4881 AnalyzeFileEvent();
\r
4882 LoadGameDialog(hwnd, _("Analyze Game from File"));
\r
4883 AnalysisPeriodicEvent(1);
\r
4887 case IDM_IcsClient:
\r
4891 case IDM_EditGame:
\r
4896 case IDM_EditPosition:
\r
4897 EditPositionEvent();
\r
4898 SAY("enter a FEN string or setup a position on the board using the control R pop up menu");
\r
4901 case IDM_Training:
\r
4905 case IDM_ShowGameList:
\r
4906 ShowGameListProc();
\r
4909 case IDM_EditTags:
\r
4913 case IDM_EditComment:
\r
4914 if (commentUp && editComment) {
\r
4917 EditCommentEvent();
\r
4937 case IDM_CallFlag:
\r
4957 case IDM_StopObserving:
\r
4958 StopObservingEvent();
\r
4961 case IDM_StopExamining:
\r
4962 StopExaminingEvent();
\r
4966 UploadGameEvent();
\r
4969 case IDM_TypeInMove:
\r
4970 PopUpMoveDialog('\000');
\r
4973 case IDM_TypeInName:
\r
4974 PopUpNameDialog('\000');
\r
4977 case IDM_Backward:
\r
4979 SetFocus(hwndMain);
\r
4986 SetFocus(hwndMain);
\r
4991 SetFocus(hwndMain);
\r
4996 SetFocus(hwndMain);
\r
5000 RevertEvent(FALSE);
\r
5003 case IDM_Annotate: // [HGM] vari: revert with annotation
\r
5004 RevertEvent(TRUE);
\r
5007 case IDM_TruncateGame:
\r
5008 TruncateGameEvent();
\r
5015 case IDM_RetractMove:
\r
5016 RetractMoveEvent();
\r
5019 case IDM_FlipView:
\r
5020 flipView = !flipView;
\r
5021 DrawPosition(FALSE, NULL);
\r
5024 case IDM_FlipClock:
\r
5025 flipClock = !flipClock;
\r
5026 DisplayBothClocks();
\r
5027 DrawPosition(FALSE, NULL);
\r
5030 case IDM_MuteSounds:
\r
5031 mute = !mute; // [HGM] mute: keep track of global muting variable
\r
5032 CheckMenuItem(GetMenu(hwndMain),IDM_MuteSounds,
\r
5033 MF_BYCOMMAND|(mute?MF_CHECKED:MF_UNCHECKED));
\r
5036 case IDM_GeneralOptions:
\r
5037 GeneralOptionsPopup(hwnd);
\r
5038 DrawPosition(TRUE, NULL);
\r
5041 case IDM_BoardOptions:
\r
5042 BoardOptionsPopup(hwnd);
\r
5045 case IDM_EnginePlayOptions:
\r
5046 EnginePlayOptionsPopup(hwnd);
\r
5049 case IDM_Engine1Options:
\r
5050 EngineOptionsPopup(hwnd, &first);
\r
5053 case IDM_Engine2Options:
\r
5054 EngineOptionsPopup(hwnd, &second);
\r
5057 case IDM_OptionsUCI:
\r
5058 UciOptionsPopup(hwnd);
\r
5061 case IDM_IcsOptions:
\r
5062 IcsOptionsPopup(hwnd);
\r
5066 FontsOptionsPopup(hwnd);
\r
5070 SoundOptionsPopup(hwnd);
\r
5073 case IDM_CommPort:
\r
5074 CommPortOptionsPopup(hwnd);
\r
5077 case IDM_LoadOptions:
\r
5078 LoadOptionsPopup(hwnd);
\r
5081 case IDM_SaveOptions:
\r
5082 SaveOptionsPopup(hwnd);
\r
5085 case IDM_TimeControl:
\r
5086 TimeControlOptionsPopup(hwnd);
\r
5089 case IDM_SaveSettings:
\r
5090 SaveSettings(settingsFileName);
\r
5093 case IDM_SaveSettingsOnExit:
\r
5094 saveSettingsOnExit = !saveSettingsOnExit;
\r
5095 (void) CheckMenuItem(GetMenu(hwndMain), IDM_SaveSettingsOnExit,
\r
5096 MF_BYCOMMAND|(saveSettingsOnExit ?
\r
5097 MF_CHECKED : MF_UNCHECKED));
\r
5108 case IDM_AboutGame:
\r
5113 appData.debugMode = !appData.debugMode;
\r
5114 if (appData.debugMode) {
\r
5115 char dir[MSG_SIZ];
\r
5116 GetCurrentDirectory(MSG_SIZ, dir);
\r
5117 SetCurrentDirectory(installDir);
\r
5118 debugFP = fopen(appData.nameOfDebugFile, "w");
\r
5119 SetCurrentDirectory(dir);
\r
5120 setbuf(debugFP, NULL);
\r
5127 case IDM_HELPCONTENTS:
\r
5128 if (!MyHelp (hwnd, "winboard.hlp", HELP_KEY,(DWORD)(LPSTR)"CONTENTS") &&
\r
5129 !HtmlHelp(hwnd, "winboard.chm", 0, 0) ) {
\r
5130 MessageBox (GetFocus(),
\r
5131 _("Unable to activate help"),
\r
5132 szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);
\r
5136 case IDM_HELPSEARCH:
\r
5137 if (!MyHelp (hwnd, "winboard.hlp", HELP_PARTIALKEY, (DWORD)(LPSTR)"") &&
\r
5138 !HtmlHelp(hwnd, "winboard.chm", 0, 0) ) {
\r
5139 MessageBox (GetFocus(),
\r
5140 _("Unable to activate help"),
\r
5141 szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);
\r
5145 case IDM_HELPHELP:
\r
5146 if(!WinHelp(hwnd, (LPSTR)NULL, HELP_HELPONHELP, 0)) {
\r
5147 MessageBox (GetFocus(),
\r
5148 _("Unable to activate help"),
\r
5149 szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);
\r
5154 lpProc = MakeProcInstance((FARPROC)About, hInst);
\r
5156 (gameInfo.event && strcmp(gameInfo.event, "Easter Egg Hunt") == 0) ?
\r
5157 "AboutBox2" : "AboutBox", hwnd, (DLGPROC)lpProc);
\r
5158 FreeProcInstance(lpProc);
\r
5161 case IDM_DirectCommand1:
\r
5162 AskQuestionEvent(_("Direct Command"),
\r
5163 _("Send to chess program:"), "", "1");
\r
5165 case IDM_DirectCommand2:
\r
5166 AskQuestionEvent(_("Direct Command"),
\r
5167 _("Send to second chess program:"), "", "2");
\r
5170 case EP_WhitePawn:
\r
5171 EditPositionMenuEvent(WhitePawn, fromX, fromY);
\r
5172 fromX = fromY = -1;
\r
5175 case EP_WhiteKnight:
\r
5176 EditPositionMenuEvent(WhiteKnight, fromX, fromY);
\r
5177 fromX = fromY = -1;
\r
5180 case EP_WhiteBishop:
\r
5181 EditPositionMenuEvent(WhiteBishop, fromX, fromY);
\r
5182 fromX = fromY = -1;
\r
5185 case EP_WhiteRook:
\r
5186 EditPositionMenuEvent(WhiteRook, fromX, fromY);
\r
5187 fromX = fromY = -1;
\r
5190 case EP_WhiteQueen:
\r
5191 EditPositionMenuEvent(WhiteQueen, fromX, fromY);
\r
5192 fromX = fromY = -1;
\r
5195 case EP_WhiteFerz:
\r
5196 EditPositionMenuEvent(WhiteFerz, fromX, fromY);
\r
5197 fromX = fromY = -1;
\r
5200 case EP_WhiteWazir:
\r
5201 EditPositionMenuEvent(WhiteWazir, fromX, fromY);
\r
5202 fromX = fromY = -1;
\r
5205 case EP_WhiteAlfil:
\r
5206 EditPositionMenuEvent(WhiteAlfil, fromX, fromY);
\r
5207 fromX = fromY = -1;
\r
5210 case EP_WhiteCannon:
\r
5211 EditPositionMenuEvent(WhiteCannon, fromX, fromY);
\r
5212 fromX = fromY = -1;
\r
5215 case EP_WhiteCardinal:
\r
5216 EditPositionMenuEvent(WhiteAngel, fromX, fromY);
\r
5217 fromX = fromY = -1;
\r
5220 case EP_WhiteMarshall:
\r
5221 EditPositionMenuEvent(WhiteMarshall, fromX, fromY);
\r
5222 fromX = fromY = -1;
\r
5225 case EP_WhiteKing:
\r
5226 EditPositionMenuEvent(WhiteKing, fromX, fromY);
\r
5227 fromX = fromY = -1;
\r
5230 case EP_BlackPawn:
\r
5231 EditPositionMenuEvent(BlackPawn, fromX, fromY);
\r
5232 fromX = fromY = -1;
\r
5235 case EP_BlackKnight:
\r
5236 EditPositionMenuEvent(BlackKnight, fromX, fromY);
\r
5237 fromX = fromY = -1;
\r
5240 case EP_BlackBishop:
\r
5241 EditPositionMenuEvent(BlackBishop, fromX, fromY);
\r
5242 fromX = fromY = -1;
\r
5245 case EP_BlackRook:
\r
5246 EditPositionMenuEvent(BlackRook, fromX, fromY);
\r
5247 fromX = fromY = -1;
\r
5250 case EP_BlackQueen:
\r
5251 EditPositionMenuEvent(BlackQueen, fromX, fromY);
\r
5252 fromX = fromY = -1;
\r
5255 case EP_BlackFerz:
\r
5256 EditPositionMenuEvent(BlackFerz, fromX, fromY);
\r
5257 fromX = fromY = -1;
\r
5260 case EP_BlackWazir:
\r
5261 EditPositionMenuEvent(BlackWazir, fromX, fromY);
\r
5262 fromX = fromY = -1;
\r
5265 case EP_BlackAlfil:
\r
5266 EditPositionMenuEvent(BlackAlfil, fromX, fromY);
\r
5267 fromX = fromY = -1;
\r
5270 case EP_BlackCannon:
\r
5271 EditPositionMenuEvent(BlackCannon, fromX, fromY);
\r
5272 fromX = fromY = -1;
\r
5275 case EP_BlackCardinal:
\r
5276 EditPositionMenuEvent(BlackAngel, fromX, fromY);
\r
5277 fromX = fromY = -1;
\r
5280 case EP_BlackMarshall:
\r
5281 EditPositionMenuEvent(BlackMarshall, fromX, fromY);
\r
5282 fromX = fromY = -1;
\r
5285 case EP_BlackKing:
\r
5286 EditPositionMenuEvent(BlackKing, fromX, fromY);
\r
5287 fromX = fromY = -1;
\r
5290 case EP_EmptySquare:
\r
5291 EditPositionMenuEvent(EmptySquare, fromX, fromY);
\r
5292 fromX = fromY = -1;
\r
5295 case EP_ClearBoard:
\r
5296 EditPositionMenuEvent(ClearBoard, fromX, fromY);
\r
5297 fromX = fromY = -1;
\r
5301 EditPositionMenuEvent(WhitePlay, fromX, fromY);
\r
5302 fromX = fromY = -1;
\r
5306 EditPositionMenuEvent(BlackPlay, fromX, fromY);
\r
5307 fromX = fromY = -1;
\r
5311 EditPositionMenuEvent(PromotePiece, fromX, fromY);
\r
5312 fromX = fromY = -1;
\r
5316 EditPositionMenuEvent(DemotePiece, fromX, fromY);
\r
5317 fromX = fromY = -1;
\r
5321 DropMenuEvent(WhitePawn, fromX, fromY);
\r
5322 fromX = fromY = -1;
\r
5326 DropMenuEvent(WhiteKnight, fromX, fromY);
\r
5327 fromX = fromY = -1;
\r
5331 DropMenuEvent(WhiteBishop, fromX, fromY);
\r
5332 fromX = fromY = -1;
\r
5336 DropMenuEvent(WhiteRook, fromX, fromY);
\r
5337 fromX = fromY = -1;
\r
5341 DropMenuEvent(WhiteQueen, fromX, fromY);
\r
5342 fromX = fromY = -1;
\r
5347 TranslateMenus(0);
\r
5348 CheckMenuItem(GetMenu(hwndMain), lastChecked, MF_BYCOMMAND|MF_UNCHECKED);
\r
5349 CheckMenuItem(GetMenu(hwndMain), IDM_English, MF_BYCOMMAND|MF_CHECKED);
\r
5350 lastChecked = wmId;
\r
5354 if(wmId > IDM_English && wmId < IDM_English+5) {
\r
5355 LoadLanguageFile(languageFile[wmId - IDM_English - 1]);
\r
5356 TranslateMenus(0);
\r
5357 CheckMenuItem(GetMenu(hwndMain), lastChecked, MF_BYCOMMAND|MF_UNCHECKED);
\r
5358 CheckMenuItem(GetMenu(hwndMain), wmId, MF_BYCOMMAND|MF_CHECKED);
\r
5359 lastChecked = wmId;
\r
5362 return (DefWindowProc(hwnd, message, wParam, lParam));
\r
5368 case CLOCK_TIMER_ID:
\r
5369 KillTimer(hwnd, clockTimerEvent); /* Simulate one-shot timer as in X */
\r
5370 clockTimerEvent = 0;
\r
5371 DecrementClocks(); /* call into back end */
\r
5373 case LOAD_GAME_TIMER_ID:
\r
5374 KillTimer(hwnd, loadGameTimerEvent); /* Simulate one-shot timer as in X*/
\r
5375 loadGameTimerEvent = 0;
\r
5376 AutoPlayGameLoop(); /* call into back end */
\r
5378 case ANALYSIS_TIMER_ID:
\r
5379 if ((gameMode == AnalyzeMode || gameMode == AnalyzeFile
\r
5380 || appData.icsEngineAnalyze) && appData.periodicUpdates) {
\r
5381 AnalysisPeriodicEvent(0);
\r
5383 KillTimer(hwnd, analysisTimerEvent);
\r
5384 analysisTimerEvent = 0;
\r
5387 case DELAYED_TIMER_ID:
\r
5388 KillTimer(hwnd, delayedTimerEvent);
\r
5389 delayedTimerEvent = 0;
\r
5390 delayedTimerCallback();
\r
5395 case WM_USER_Input:
\r
5396 InputEvent(hwnd, message, wParam, lParam);
\r
5399 /* [AS] Also move "attached" child windows */
\r
5400 case WM_WINDOWPOSCHANGING:
\r
5402 if( hwnd == hwndMain && appData.useStickyWindows ) {
\r
5403 LPWINDOWPOS lpwp = (LPWINDOWPOS) lParam;
\r
5405 if( ((lpwp->flags & SWP_NOMOVE) == 0) && ((lpwp->flags & SWP_NOSIZE) != 0) ) {
\r
5406 /* Window is moving */
\r
5409 // GetWindowRect( hwnd, &rcMain ); //[HGM] sticky: in XP this returned new position, not old
\r
5410 rcMain.left = wpMain.x; // replace by these 4 lines to reconstruct old rect
\r
5411 rcMain.right = wpMain.x + wpMain.width;
\r
5412 rcMain.top = wpMain.y;
\r
5413 rcMain.bottom = wpMain.y + wpMain.height;
\r
5415 ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, moveHistoryDialog, &wpMoveHistory );
\r
5416 ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, evalGraphDialog, &wpEvalGraph );
\r
5417 ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, engineOutputDialog, &wpEngineOutput );
\r
5418 ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, gameListDialog, &wpGameList );
\r
5419 ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, hwndConsole, &wpConsole );
\r
5420 wpMain.x = lpwp->x;
\r
5421 wpMain.y = lpwp->y;
\r
5426 /* [AS] Snapping */
\r
5427 case WM_ENTERSIZEMOVE:
\r
5428 if(appData.debugMode) { fprintf(debugFP, "size-move\n"); }
\r
5429 if (hwnd == hwndMain) {
\r
5430 doingSizing = TRUE;
\r
5433 return OnEnterSizeMove( &sd, hwnd, wParam, lParam );
\r
5437 if(appData.debugMode) { fprintf(debugFP, "sizing\n"); }
\r
5438 if (hwnd == hwndMain) {
\r
5439 lastSizing = wParam;
\r
5444 if(appData.debugMode) { fprintf(debugFP, "moving\n"); }
\r
5445 return OnMoving( &sd, hwnd, wParam, lParam );
\r
5447 case WM_EXITSIZEMOVE:
\r
5448 if(appData.debugMode) { fprintf(debugFP, "exit size-move, size = %d\n", squareSize); }
\r
5449 if (hwnd == hwndMain) {
\r
5451 doingSizing = FALSE;
\r
5452 InvalidateRect(hwnd, &boardRect, FALSE);
\r
5453 GetClientRect(hwnd, &client);
\r
5454 ResizeBoard(client.right, client.bottom, lastSizing);
\r
5456 if(appData.debugMode) { fprintf(debugFP, "square size = %d\n", squareSize); }
\r
5458 return OnExitSizeMove( &sd, hwnd, wParam, lParam );
\r
5461 case WM_DESTROY: /* message: window being destroyed */
\r
5462 PostQuitMessage(0);
\r
5466 if (hwnd == hwndMain) {
\r
5471 default: /* Passes it on if unprocessed */
\r
5472 return (DefWindowProc(hwnd, message, wParam, lParam));
\r
5477 /*---------------------------------------------------------------------------*\
\r
5479 * Misc utility routines
\r
5481 \*---------------------------------------------------------------------------*/
\r
5484 * Decent random number generator, at least not as bad as Windows
\r
5485 * standard rand, which returns a value in the range 0 to 0x7fff.
\r
5487 unsigned int randstate;
\r
5492 randstate = randstate * 1664525 + 1013904223;
\r
5493 return (int) randstate & 0x7fffffff;
\r
5497 mysrandom(unsigned int seed)
\r
5504 * returns TRUE if user selects a different color, FALSE otherwise
\r
5508 ChangeColor(HWND hwnd, COLORREF *which)
\r
5510 static BOOL firstTime = TRUE;
\r
5511 static DWORD customColors[16];
\r
5513 COLORREF newcolor;
\r
5518 /* Make initial colors in use available as custom colors */
\r
5519 /* Should we put the compiled-in defaults here instead? */
\r
5521 customColors[i++] = lightSquareColor & 0xffffff;
\r
5522 customColors[i++] = darkSquareColor & 0xffffff;
\r
5523 customColors[i++] = whitePieceColor & 0xffffff;
\r
5524 customColors[i++] = blackPieceColor & 0xffffff;
\r
5525 customColors[i++] = highlightSquareColor & 0xffffff;
\r
5526 customColors[i++] = premoveHighlightColor & 0xffffff;
\r
5528 for (ccl = (ColorClass) 0; ccl < NColorClasses && i < 16; ccl++) {
\r
5529 customColors[i++] = textAttribs[ccl].color;
\r
5531 while (i < 16) customColors[i++] = RGB(255, 255, 255);
\r
5532 firstTime = FALSE;
\r
5535 cc.lStructSize = sizeof(cc);
\r
5536 cc.hwndOwner = hwnd;
\r
5537 cc.hInstance = NULL;
\r
5538 cc.rgbResult = (DWORD) (*which & 0xffffff);
\r
5539 cc.lpCustColors = (LPDWORD) customColors;
\r
5540 cc.Flags = CC_RGBINIT|CC_FULLOPEN;
\r
5542 if (!ChooseColor(&cc)) return FALSE;
\r
5544 newcolor = (COLORREF) (0x2000000 | cc.rgbResult);
\r
5545 if (newcolor == *which) return FALSE;
\r
5546 *which = newcolor;
\r
5550 InitDrawingColors();
\r
5551 InvalidateRect(hwnd, &boardRect, FALSE);
\r
5556 MyLoadSound(MySound *ms)
\r
5562 if (ms->data) free(ms->data);
\r
5565 switch (ms->name[0]) {
\r
5571 /* System sound from Control Panel. Don't preload here. */
\r
5575 if (ms->name[1] == NULLCHAR) {
\r
5576 /* "!" alone = silence */
\r
5579 /* Builtin wave resource. Error if not found. */
\r
5580 HANDLE h = FindResource(hInst, ms->name + 1, "WAVE");
\r
5581 if (h == NULL) break;
\r
5582 ms->data = (void *)LoadResource(hInst, h);
\r
5583 if (h == NULL) break;
\r
5588 /* .wav file. Error if not found. */
\r
5589 f = fopen(ms->name, "rb");
\r
5590 if (f == NULL) break;
\r
5591 if (fstat(fileno(f), &st) < 0) break;
\r
5592 ms->data = malloc(st.st_size);
\r
5593 if (fread(ms->data, st.st_size, 1, f) < 1) break;
\r
5599 char buf[MSG_SIZ];
\r
5600 snprintf(buf, MSG_SIZ, _("Error loading sound %s"), ms->name);
\r
5601 DisplayError(buf, GetLastError());
\r
5607 MyPlaySound(MySound *ms)
\r
5609 BOOLEAN ok = FALSE;
\r
5611 if(mute) return TRUE; // [HGM] mute: suppress all sound play when muted
\r
5612 switch (ms->name[0]) {
\r
5614 if(appData.debugMode) fprintf(debugFP, "silence\n");
\r
5619 /* System sound from Control Panel (deprecated feature).
\r
5620 "$" alone or an unset sound name gets default beep (still in use). */
\r
5621 if (ms->name[1]) {
\r
5622 ok = PlaySound(ms->name + 1, NULL, SND_ALIAS|SND_ASYNC);
\r
5624 if (!ok) ok = MessageBeep(MB_OK);
\r
5627 /* Builtin wave resource, or "!" alone for silence */
\r
5628 if (ms->name[1]) {
\r
5629 if (ms->data == NULL) return FALSE;
\r
5630 ok = PlaySound(ms->data, NULL, SND_MEMORY|SND_ASYNC);
\r
5636 /* .wav file. Error if not found. */
\r
5637 if (ms->data == NULL) return FALSE;
\r
5638 ok = PlaySound(ms->data, NULL, SND_MEMORY|SND_ASYNC);
\r
5641 /* Don't print an error: this can happen innocently if the sound driver
\r
5642 is busy; for instance, if another instance of WinBoard is playing
\r
5643 a sound at about the same time. */
\r
5649 OldOpenFileHook(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
5652 OPENFILENAME *ofn;
\r
5653 static UINT *number; /* gross that this is static */
\r
5655 switch (message) {
\r
5656 case WM_INITDIALOG: /* message: initialize dialog box */
\r
5657 /* Center the dialog over the application window */
\r
5658 ofn = (OPENFILENAME *) lParam;
\r
5659 if (ofn->Flags & OFN_ENABLETEMPLATE) {
\r
5660 number = (UINT *) ofn->lCustData;
\r
5661 SendMessage(GetDlgItem(hDlg, edt2), WM_SETTEXT, 0, (LPARAM) "");
\r
5665 CenterWindow (hDlg, GetWindow (hDlg, GW_OWNER));
\r
5666 Translate(hDlg, 1536);
\r
5667 return FALSE; /* Allow for further processing */
\r
5670 if ((LOWORD(wParam) == IDOK) && (number != NULL)) {
\r
5671 *number = GetDlgItemInt(hDlg, OPT_IndexNumberOld, &ok, FALSE);
\r
5673 return FALSE; /* Allow for further processing */
\r
5679 OpenFileHook(HWND hdlg, UINT uiMsg, WPARAM wParam, LPARAM lParam)
\r
5681 static UINT *number;
\r
5682 OPENFILENAME *ofname;
\r
5685 case WM_INITDIALOG:
\r
5686 Translate(hdlg, DLG_IndexNumber);
\r
5687 ofname = (OPENFILENAME *)lParam;
\r
5688 number = (UINT *)(ofname->lCustData);
\r
5691 ofnot = (OFNOTIFY *)lParam;
\r
5692 if (ofnot->hdr.code == CDN_FILEOK) {
\r
5693 *number = GetDlgItemInt(hdlg, OPT_IndexNumber, NULL, FALSE);
\r
5702 OpenFileDialog(HWND hwnd, char *write, char *defName, char *defExt, // [HGM] diag: type of 'write' now string
\r
5703 char *nameFilt, char *dlgTitle, UINT *number,
\r
5704 char fileTitle[MSG_SIZ], char fileName[MSG_SIZ])
\r
5706 OPENFILENAME openFileName;
\r
5707 char buf1[MSG_SIZ];
\r
5710 if (fileName == NULL) fileName = buf1;
\r
5711 if (defName == NULL) {
\r
5712 safeStrCpy(fileName, "*.", sizeof(fileName)/sizeof(fileName[0]) );
\r
5713 strcat(fileName, defExt);
\r
5715 safeStrCpy(fileName, defName, sizeof(fileName)/sizeof(fileName[0]) );
\r
5717 if (fileTitle) safeStrCpy(fileTitle, "", sizeof(fileTitle)/sizeof(fileTitle[0]) );
\r
5718 if (number) *number = 0;
\r
5720 openFileName.lStructSize = sizeof(OPENFILENAME);
\r
5721 openFileName.hwndOwner = hwnd;
\r
5722 openFileName.hInstance = (HANDLE) hInst;
\r
5723 openFileName.lpstrFilter = nameFilt;
\r
5724 openFileName.lpstrCustomFilter = (LPSTR) NULL;
\r
5725 openFileName.nMaxCustFilter = 0L;
\r
5726 openFileName.nFilterIndex = 1L;
\r
5727 openFileName.lpstrFile = fileName;
\r
5728 openFileName.nMaxFile = MSG_SIZ;
\r
5729 openFileName.lpstrFileTitle = fileTitle;
\r
5730 openFileName.nMaxFileTitle = fileTitle ? MSG_SIZ : 0;
\r
5731 openFileName.lpstrInitialDir = NULL;
\r
5732 openFileName.lpstrTitle = dlgTitle;
\r
5733 openFileName.Flags = OFN_PATHMUSTEXIST | OFN_HIDEREADONLY
\r
5734 | (write[0] != 'r' ? 0 : OFN_FILEMUSTEXIST)
\r
5735 | (number ? OFN_ENABLETEMPLATE | OFN_ENABLEHOOK: 0)
\r
5736 | (oldDialog ? 0 : OFN_EXPLORER);
\r
5737 openFileName.nFileOffset = 0;
\r
5738 openFileName.nFileExtension = 0;
\r
5739 openFileName.lpstrDefExt = defExt;
\r
5740 openFileName.lCustData = (LONG) number;
\r
5741 openFileName.lpfnHook = oldDialog ?
\r
5742 (LPOFNHOOKPROC) OldOpenFileHook : (LPOFNHOOKPROC) OpenFileHook;
\r
5743 openFileName.lpTemplateName = (LPSTR)(oldDialog ? 1536 : DLG_IndexNumber);
\r
5745 if (write[0] != 'r' ? GetSaveFileName(&openFileName) :
\r
5746 GetOpenFileName(&openFileName)) {
\r
5747 /* open the file */
\r
5748 f = fopen(openFileName.lpstrFile, write);
\r
5750 MessageBox(hwnd, _("File open failed"), NULL,
\r
5751 MB_OK|MB_ICONEXCLAMATION);
\r
5755 int err = CommDlgExtendedError();
\r
5756 if (err != 0) DisplayError(_("Internal error in file dialog box"), err);
\r
5765 MenuPopup(HWND hwnd, POINT pt, HMENU hmenu, UINT def)
\r
5767 HMENU hmenuTrackPopup; /* floating pop-up menu */
\r
5770 * Get the first pop-up menu in the menu template. This is the
\r
5771 * menu that TrackPopupMenu displays.
\r
5773 hmenuTrackPopup = GetSubMenu(hmenu, 0);
\r
5775 SetMenuDefaultItem(hmenuTrackPopup, def, FALSE);
\r
5778 * TrackPopup uses screen coordinates, so convert the
\r
5779 * coordinates of the mouse click to screen coordinates.
\r
5781 ClientToScreen(hwnd, (LPPOINT) &pt);
\r
5783 /* Draw and track the floating pop-up menu. */
\r
5784 TrackPopupMenu(hmenuTrackPopup, TPM_CENTERALIGN | TPM_RIGHTBUTTON,
\r
5785 pt.x, pt.y, 0, hwnd, NULL);
\r
5787 /* Destroy the menu.*/
\r
5788 DestroyMenu(hmenu);
\r
5793 int sizeX, sizeY, newSizeX, newSizeY;
\r
5795 } ResizeEditPlusButtonsClosure;
\r
5798 ResizeEditPlusButtonsCallback(HWND hChild, LPARAM lparam)
\r
5800 ResizeEditPlusButtonsClosure *cl = (ResizeEditPlusButtonsClosure *)lparam;
\r
5804 if (hChild == cl->hText) return TRUE;
\r
5805 GetWindowRect(hChild, &rect); /* gives screen coords */
\r
5806 pt.x = rect.left + (cl->newSizeX - cl->sizeX)/2;
\r
5807 pt.y = rect.top + cl->newSizeY - cl->sizeY;
\r
5808 ScreenToClient(cl->hDlg, &pt);
\r
5809 cl->hdwp = DeferWindowPos(cl->hdwp, hChild, NULL,
\r
5810 pt.x, pt.y, rect.right - rect.left, rect.bottom - rect.top, SWP_NOZORDER);
\r
5814 /* Resize a dialog that has a (rich) edit field filling most of
\r
5815 the top, with a row of buttons below */
\r
5817 ResizeEditPlusButtons(HWND hDlg, HWND hText, int sizeX, int sizeY, int newSizeX, int newSizeY)
\r
5820 int newTextHeight, newTextWidth;
\r
5821 ResizeEditPlusButtonsClosure cl;
\r
5823 /*if (IsIconic(hDlg)) return;*/
\r
5824 if (newSizeX == sizeX && newSizeY == sizeY) return;
\r
5826 cl.hdwp = BeginDeferWindowPos(8);
\r
5828 GetWindowRect(hText, &rectText); /* gives screen coords */
\r
5829 newTextWidth = rectText.right - rectText.left + newSizeX - sizeX;
\r
5830 newTextHeight = rectText.bottom - rectText.top + newSizeY - sizeY;
\r
5831 if (newTextHeight < 0) {
\r
5832 newSizeY += -newTextHeight;
\r
5833 newTextHeight = 0;
\r
5835 cl.hdwp = DeferWindowPos(cl.hdwp, hText, NULL, 0, 0,
\r
5836 newTextWidth, newTextHeight, SWP_NOZORDER|SWP_NOMOVE);
\r
5842 cl.newSizeX = newSizeX;
\r
5843 cl.newSizeY = newSizeY;
\r
5844 EnumChildWindows(hDlg, ResizeEditPlusButtonsCallback, (LPARAM)&cl);
\r
5846 EndDeferWindowPos(cl.hdwp);
\r
5849 BOOL CenterWindowEx(HWND hwndChild, HWND hwndParent, int mode)
\r
5851 RECT rChild, rParent;
\r
5852 int wChild, hChild, wParent, hParent;
\r
5853 int wScreen, hScreen, xNew, yNew;
\r
5856 /* Get the Height and Width of the child window */
\r
5857 GetWindowRect (hwndChild, &rChild);
\r
5858 wChild = rChild.right - rChild.left;
\r
5859 hChild = rChild.bottom - rChild.top;
\r
5861 /* Get the Height and Width of the parent window */
\r
5862 GetWindowRect (hwndParent, &rParent);
\r
5863 wParent = rParent.right - rParent.left;
\r
5864 hParent = rParent.bottom - rParent.top;
\r
5866 /* Get the display limits */
\r
5867 hdc = GetDC (hwndChild);
\r
5868 wScreen = GetDeviceCaps (hdc, HORZRES);
\r
5869 hScreen = GetDeviceCaps (hdc, VERTRES);
\r
5870 ReleaseDC(hwndChild, hdc);
\r
5872 /* Calculate new X position, then adjust for screen */
\r
5873 xNew = rParent.left + ((wParent - wChild) /2);
\r
5876 } else if ((xNew+wChild) > wScreen) {
\r
5877 xNew = wScreen - wChild;
\r
5880 /* Calculate new Y position, then adjust for screen */
\r
5882 yNew = rParent.top + ((hParent - hChild) /2);
\r
5885 yNew = rParent.top + GetSystemMetrics( SM_CYCAPTION ) * 2 / 3;
\r
5890 } else if ((yNew+hChild) > hScreen) {
\r
5891 yNew = hScreen - hChild;
\r
5894 /* Set it, and return */
\r
5895 return SetWindowPos (hwndChild, NULL,
\r
5896 xNew, yNew, 0, 0, SWP_NOSIZE | SWP_NOZORDER);
\r
5899 /* Center one window over another */
\r
5900 BOOL CenterWindow (HWND hwndChild, HWND hwndParent)
\r
5902 return CenterWindowEx( hwndChild, hwndParent, 0 );
\r
5905 /*---------------------------------------------------------------------------*\
\r
5907 * Startup Dialog functions
\r
5909 \*---------------------------------------------------------------------------*/
\r
5911 InitComboStrings(HANDLE hwndCombo, char **cd)
\r
5913 SendMessage(hwndCombo, CB_RESETCONTENT, 0, 0);
\r
5915 while (*cd != NULL) {
\r
5916 SendMessage(hwndCombo, CB_ADDSTRING, 0, (LPARAM) T_(*cd));
\r
5922 InitComboStringsFromOption(HANDLE hwndCombo, char *str)
\r
5924 char buf1[MAX_ARG_LEN];
\r
5927 if (str[0] == '@') {
\r
5928 FILE* f = fopen(str + 1, "r");
\r
5930 DisplayFatalError(str + 1, errno, 2);
\r
5933 len = fread(buf1, 1, sizeof(buf1)-1, f);
\r
5935 buf1[len] = NULLCHAR;
\r
5939 SendMessage(hwndCombo, CB_RESETCONTENT, 0, 0);
\r
5942 char buf[MSG_SIZ];
\r
5943 char *end = strchr(str, '\n');
\r
5944 if (end == NULL) return;
\r
5945 memcpy(buf, str, end - str);
\r
5946 buf[end - str] = NULLCHAR;
\r
5947 SendMessage(hwndCombo, CB_ADDSTRING, 0, (LPARAM) buf);
\r
5953 SetStartupDialogEnables(HWND hDlg)
\r
5955 EnableWindow(GetDlgItem(hDlg, OPT_ChessEngineName),
\r
5956 IsDlgButtonChecked(hDlg, OPT_ChessEngine) ||
\r
5957 (appData.zippyPlay && IsDlgButtonChecked(hDlg, OPT_ChessServer)));
\r
5958 EnableWindow(GetDlgItem(hDlg, OPT_SecondChessEngineName),
\r
5959 IsDlgButtonChecked(hDlg, OPT_ChessEngine));
\r
5960 EnableWindow(GetDlgItem(hDlg, OPT_ChessServerName),
\r
5961 IsDlgButtonChecked(hDlg, OPT_ChessServer));
\r
5962 EnableWindow(GetDlgItem(hDlg, OPT_AdditionalOptions),
\r
5963 IsDlgButtonChecked(hDlg, OPT_AnyAdditional));
\r
5964 EnableWindow(GetDlgItem(hDlg, IDOK),
\r
5965 IsDlgButtonChecked(hDlg, OPT_ChessEngine) ||
\r
5966 IsDlgButtonChecked(hDlg, OPT_ChessServer) ||
\r
5967 IsDlgButtonChecked(hDlg, OPT_View));
\r
5971 QuoteForFilename(char *filename)
\r
5973 int dquote, space;
\r
5974 dquote = strchr(filename, '"') != NULL;
\r
5975 space = strchr(filename, ' ') != NULL;
\r
5976 if (dquote || space) {
\r
5988 InitEngineBox(HWND hDlg, HWND hwndCombo, char* nthcp, char* nthd, char* nthdir, char *nthnames)
\r
5990 char buf[MSG_SIZ];
\r
5993 InitComboStringsFromOption(hwndCombo, nthnames);
\r
5994 q = QuoteForFilename(nthcp);
\r
5995 snprintf(buf, MSG_SIZ, "%s%s%s", q, nthcp, q);
\r
5996 if (*nthdir != NULLCHAR) {
\r
5997 q = QuoteForFilename(nthdir);
\r
5998 snprintf(buf + strlen(buf), MSG_SIZ, " /%s=%s%s%s", nthd, q, nthdir, q);
\r
6000 if (*nthcp == NULLCHAR) {
\r
6001 SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) 0, (LPARAM) 0);
\r
6002 } else if (SendMessage(hwndCombo, CB_SELECTSTRING, (WPARAM) -1, (LPARAM) buf) == CB_ERR) {
\r
6003 SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) -1, (LPARAM) 0);
\r
6004 SendMessage(hwndCombo, WM_SETTEXT, (WPARAM) 0, (LPARAM) buf);
\r
6009 StartupDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
6011 char buf[MSG_SIZ];
\r
6015 switch (message) {
\r
6016 case WM_INITDIALOG:
\r
6017 /* Center the dialog */
\r
6018 CenterWindow (hDlg, GetDesktopWindow());
\r
6019 Translate(hDlg, DLG_Startup);
\r
6020 /* Initialize the dialog items */
\r
6021 InitEngineBox(hDlg, GetDlgItem(hDlg, OPT_ChessEngineName),
\r
6022 appData.firstChessProgram, "fd", appData.firstDirectory,
\r
6023 firstChessProgramNames);
\r
6024 InitEngineBox(hDlg, GetDlgItem(hDlg, OPT_SecondChessEngineName),
\r
6025 appData.secondChessProgram, "sd", appData.secondDirectory,
\r
6026 secondChessProgramNames);
\r
6027 hwndCombo = GetDlgItem(hDlg, OPT_ChessServerName);
\r
6028 InitComboStringsFromOption(hwndCombo, icsNames);
\r
6029 snprintf(buf, MSG_SIZ, "%s /icsport=%s", appData.icsHost, appData.icsPort);
\r
6030 if (*appData.icsHelper != NULLCHAR) {
\r
6031 char *q = QuoteForFilename(appData.icsHelper);
\r
6032 sprintf(buf + strlen(buf), " /icshelper=%s%s%s", q, appData.icsHelper, q);
\r
6034 if (*appData.icsHost == NULLCHAR) {
\r
6035 SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) 0, (LPARAM) 0);
\r
6036 /*SendMessage(hwndCombo, CB_SHOWDROPDOWN, (WPARAM) TRUE, (LPARAM) 0); !!too soon */
\r
6037 } else if (SendMessage(hwndCombo, CB_SELECTSTRING, (WPARAM) -1, (LPARAM) buf) == CB_ERR) {
\r
6038 SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) -1, (LPARAM) 0);
\r
6039 SendMessage(hwndCombo, WM_SETTEXT, (WPARAM) 0, (LPARAM) buf);
\r
6042 if (appData.icsActive) {
\r
6043 CheckDlgButton(hDlg, OPT_ChessServer, BST_CHECKED);
\r
6045 else if (appData.noChessProgram) {
\r
6046 CheckDlgButton(hDlg, OPT_View, BST_CHECKED);
\r
6049 CheckDlgButton(hDlg, OPT_ChessEngine, BST_CHECKED);
\r
6052 SetStartupDialogEnables(hDlg);
\r
6056 switch (LOWORD(wParam)) {
\r
6058 if (IsDlgButtonChecked(hDlg, OPT_ChessEngine)) {
\r
6059 safeStrCpy(buf, "/fcp=", sizeof(buf)/sizeof(buf[0]) );
\r
6060 GetDlgItemText(hDlg, OPT_ChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));
\r
6062 ParseArgs(StringGet, &p);
\r
6063 safeStrCpy(buf, "/scp=", sizeof(buf)/sizeof(buf[0]) );
\r
6064 GetDlgItemText(hDlg, OPT_SecondChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));
\r
6066 ParseArgs(StringGet, &p);
\r
6067 appData.noChessProgram = FALSE;
\r
6068 appData.icsActive = FALSE;
\r
6069 } else if (IsDlgButtonChecked(hDlg, OPT_ChessServer)) {
\r
6070 safeStrCpy(buf, "/ics /icshost=", sizeof(buf)/sizeof(buf[0]) );
\r
6071 GetDlgItemText(hDlg, OPT_ChessServerName, buf + strlen(buf), sizeof(buf) - strlen(buf));
\r
6073 ParseArgs(StringGet, &p);
\r
6074 if (appData.zippyPlay) {
\r
6075 safeStrCpy(buf, "/fcp=", sizeof(buf)/sizeof(buf[0]) );
\r
6076 GetDlgItemText(hDlg, OPT_ChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));
\r
6078 ParseArgs(StringGet, &p);
\r
6080 } else if (IsDlgButtonChecked(hDlg, OPT_View)) {
\r
6081 appData.noChessProgram = TRUE;
\r
6082 appData.icsActive = FALSE;
\r
6084 MessageBox(hDlg, _("Choose an option, or cancel to exit"),
\r
6085 _("Option Error"), MB_OK|MB_ICONEXCLAMATION);
\r
6088 if (IsDlgButtonChecked(hDlg, OPT_AnyAdditional)) {
\r
6089 GetDlgItemText(hDlg, OPT_AdditionalOptions, buf, sizeof(buf));
\r
6091 ParseArgs(StringGet, &p);
\r
6093 EndDialog(hDlg, TRUE);
\r
6100 case IDM_HELPCONTENTS:
\r
6101 if (!WinHelp (hDlg, "winboard.hlp", HELP_KEY,(DWORD)(LPSTR)"CONTENTS")) {
\r
6102 MessageBox (GetFocus(),
\r
6103 _("Unable to activate help"),
\r
6104 szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);
\r
6109 SetStartupDialogEnables(hDlg);
\r
6117 /*---------------------------------------------------------------------------*\
\r
6119 * About box dialog functions
\r
6121 \*---------------------------------------------------------------------------*/
\r
6123 /* Process messages for "About" dialog box */
\r
6125 About(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
6127 switch (message) {
\r
6128 case WM_INITDIALOG: /* message: initialize dialog box */
\r
6129 /* Center the dialog over the application window */
\r
6130 CenterWindow (hDlg, GetWindow (hDlg, GW_OWNER));
\r
6131 SetDlgItemText(hDlg, ABOUTBOX_Version, programVersion);
\r
6132 Translate(hDlg, ABOUTBOX);
\r
6136 case WM_COMMAND: /* message: received a command */
\r
6137 if (LOWORD(wParam) == IDOK /* "OK" box selected? */
\r
6138 || LOWORD(wParam) == IDCANCEL) { /* System menu close command? */
\r
6139 EndDialog(hDlg, TRUE); /* Exit the dialog */
\r
6147 /*---------------------------------------------------------------------------*\
\r
6149 * Comment Dialog functions
\r
6151 \*---------------------------------------------------------------------------*/
\r
6154 CommentDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
6156 static HANDLE hwndText = NULL;
\r
6157 int len, newSizeX, newSizeY, flags;
\r
6158 static int sizeX, sizeY;
\r
6163 switch (message) {
\r
6164 case WM_INITDIALOG: /* message: initialize dialog box */
\r
6165 /* Initialize the dialog items */
\r
6166 Translate(hDlg, DLG_EditComment);
\r
6167 hwndText = GetDlgItem(hDlg, OPT_CommentText);
\r
6168 SetDlgItemText(hDlg, OPT_CommentText, commentText);
\r
6169 EnableWindow(GetDlgItem(hDlg, OPT_CancelComment), editComment);
\r
6170 EnableWindow(GetDlgItem(hDlg, OPT_ClearComment), editComment);
\r
6171 EnableWindow(GetDlgItem(hDlg, OPT_EditComment), !editComment);
\r
6172 SendMessage(hwndText, EM_SETREADONLY, !editComment, 0);
\r
6173 SetWindowText(hDlg, commentTitle);
\r
6174 if (editComment) {
\r
6175 SetFocus(hwndText);
\r
6177 SetFocus(GetDlgItem(hDlg, IDOK));
\r
6179 SendMessage(GetDlgItem(hDlg, OPT_CommentText),
\r
6180 WM_SETFONT, (WPARAM)font[boardSize][COMMENT_FONT]->hf,
\r
6181 MAKELPARAM(FALSE, 0));
\r
6182 /* Size and position the dialog */
\r
6183 if (!commentDialog) {
\r
6184 commentDialog = hDlg;
\r
6185 flags = SWP_NOZORDER;
\r
6186 GetClientRect(hDlg, &rect);
\r
6187 sizeX = rect.right;
\r
6188 sizeY = rect.bottom;
\r
6189 if (wpComment.x != CW_USEDEFAULT && wpComment.y != CW_USEDEFAULT &&
\r
6190 wpComment.width != CW_USEDEFAULT && wpComment.height != CW_USEDEFAULT) {
\r
6191 WINDOWPLACEMENT wp;
\r
6192 EnsureOnScreen(&wpComment.x, &wpComment.y, 0, 0);
\r
6193 wp.length = sizeof(WINDOWPLACEMENT);
\r
6195 wp.showCmd = SW_SHOW;
\r
6196 wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;
\r
6197 wp.rcNormalPosition.left = wpComment.x;
\r
6198 wp.rcNormalPosition.right = wpComment.x + wpComment.width;
\r
6199 wp.rcNormalPosition.top = wpComment.y;
\r
6200 wp.rcNormalPosition.bottom = wpComment.y + wpComment.height;
\r
6201 SetWindowPlacement(hDlg, &wp);
\r
6203 GetClientRect(hDlg, &rect);
\r
6204 newSizeX = rect.right;
\r
6205 newSizeY = rect.bottom;
\r
6206 ResizeEditPlusButtons(hDlg, hwndText, sizeX, sizeY,
\r
6207 newSizeX, newSizeY);
\r
6212 SendDlgItemMessage( hDlg, OPT_CommentText, EM_SETEVENTMASK, 0, ENM_MOUSEEVENTS );
\r
6215 case WM_COMMAND: /* message: received a command */
\r
6216 switch (LOWORD(wParam)) {
\r
6218 if (editComment) {
\r
6220 /* Read changed options from the dialog box */
\r
6221 hwndText = GetDlgItem(hDlg, OPT_CommentText);
\r
6222 len = GetWindowTextLength(hwndText);
\r
6223 str = (char *) malloc(len + 1);
\r
6224 GetWindowText(hwndText, str, len + 1);
\r
6233 ReplaceComment(commentIndex, str);
\r
6240 case OPT_CancelComment:
\r
6244 case OPT_ClearComment:
\r
6245 SetDlgItemText(hDlg, OPT_CommentText, "");
\r
6248 case OPT_EditComment:
\r
6249 EditCommentEvent();
\r
6257 case WM_NOTIFY: // [HGM] vari: cloned from whistory.c
\r
6258 if( wParam == OPT_CommentText ) {
\r
6259 MSGFILTER * lpMF = (MSGFILTER *) lParam;
\r
6261 if( lpMF->msg == WM_RBUTTONDOWN && (lpMF->wParam & (MK_CONTROL | MK_SHIFT)) == 0 ) {
\r
6265 pt.x = LOWORD( lpMF->lParam );
\r
6266 pt.y = HIWORD( lpMF->lParam );
\r
6268 index = SendDlgItemMessage( hDlg, OPT_CommentText, EM_CHARFROMPOS, 0, (LPARAM) &pt );
\r
6270 hwndText = GetDlgItem(hDlg, OPT_CommentText); // cloned from above
\r
6271 len = GetWindowTextLength(hwndText);
\r
6272 str = (char *) malloc(len + 1);
\r
6273 GetWindowText(hwndText, str, len + 1);
\r
6274 ReplaceComment(commentIndex, str);
\r
6275 if(commentIndex != currentMove) ToNrEvent(commentIndex);
\r
6276 LoadVariation( index, str ); // [HGM] also does the actual moving to it, now
\r
6279 /* Zap the message for good: apparently, returning non-zero is not enough */
\r
6280 lpMF->msg = WM_USER;
\r
6288 newSizeX = LOWORD(lParam);
\r
6289 newSizeY = HIWORD(lParam);
\r
6290 ResizeEditPlusButtons(hDlg, hwndText, sizeX, sizeY, newSizeX, newSizeY);
\r
6295 case WM_GETMINMAXINFO:
\r
6296 /* Prevent resizing window too small */
\r
6297 mmi = (MINMAXINFO *) lParam;
\r
6298 mmi->ptMinTrackSize.x = 100;
\r
6299 mmi->ptMinTrackSize.y = 100;
\r
6306 EitherCommentPopUp(int index, char *title, char *str, BOOLEAN edit)
\r
6311 CheckMenuItem(GetMenu(hwndMain), IDM_EditComment, edit ? MF_CHECKED : MF_UNCHECKED);
\r
6313 if (str == NULL) str = "";
\r
6314 p = (char *) malloc(2 * strlen(str) + 2);
\r
6317 if (*str == '\n') *q++ = '\r';
\r
6321 if (commentText != NULL) free(commentText);
\r
6323 commentIndex = index;
\r
6324 commentTitle = title;
\r
6326 editComment = edit;
\r
6328 if (commentDialog) {
\r
6329 SendMessage(commentDialog, WM_INITDIALOG, 0, 0);
\r
6330 if (!commentUp) ShowWindow(commentDialog, SW_SHOW);
\r
6332 lpProc = MakeProcInstance((FARPROC)CommentDialog, hInst);
\r
6333 CreateDialog(hInst, MAKEINTRESOURCE(DLG_EditComment),
\r
6334 hwndMain, (DLGPROC)lpProc);
\r
6335 FreeProcInstance(lpProc);
\r
6341 /*---------------------------------------------------------------------------*\
\r
6343 * Type-in move dialog functions
\r
6345 \*---------------------------------------------------------------------------*/
\r
6348 TypeInMoveDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
6350 char move[MSG_SIZ];
\r
6352 ChessMove moveType;
\r
6353 int fromX, fromY, toX, toY;
\r
6356 switch (message) {
\r
6357 case WM_INITDIALOG:
\r
6358 move[0] = (char) lParam;
\r
6359 move[1] = NULLCHAR;
\r
6360 CenterWindowEx(hDlg, GetWindow(hDlg, GW_OWNER), 1 );
\r
6361 Translate(hDlg, DLG_TypeInMove);
\r
6362 hInput = GetDlgItem(hDlg, OPT_Move);
\r
6363 SetWindowText(hInput, move);
\r
6365 SendMessage(hInput, EM_SETSEL, (WPARAM)9999, (LPARAM)9999);
\r
6369 switch (LOWORD(wParam)) {
\r
6371 shiftKey = GetKeyState(VK_SHIFT) < 0; // [HGM] remember last shift status
\r
6372 GetDlgItemText(hDlg, OPT_Move, move, sizeof(move));
\r
6373 { int n; Board board;
\r
6375 if(gameMode == EditPosition && ParseFEN(board, &n, move) ) {
\r
6376 EditPositionPasteFEN(move);
\r
6377 EndDialog(hDlg, TRUE);
\r
6380 // [HGM] movenum: allow move number to be typed in any mode
\r
6381 if(sscanf(move, "%d", &n) == 1 && n != 0 ) {
\r
6383 EndDialog(hDlg, TRUE);
\r
6387 if (gameMode != EditGame && currentMove != forwardMostMove &&
\r
6388 gameMode != Training) {
\r
6389 DisplayMoveError(_("Displayed move is not current"));
\r
6391 // GetDlgItemText(hDlg, OPT_Move, move, sizeof(move)); // moved upstream
\r
6392 int ok = ParseOneMove(move, gameMode == EditPosition ? blackPlaysFirst : currentMove,
\r
6393 &moveType, &fromX, &fromY, &toX, &toY, &promoChar);
\r
6394 if(!ok && move[0] >= 'a') { move[0] += 'A' - 'a'; ok = 2; } // [HGM] try also capitalized
\r
6395 if (ok==1 || ok && ParseOneMove(move, gameMode == EditPosition ? blackPlaysFirst : currentMove,
\r
6396 &moveType, &fromX, &fromY, &toX, &toY, &promoChar)) {
\r
6397 if (gameMode != Training)
\r
6398 forwardMostMove = currentMove;
\r
6399 UserMoveEvent(fromX, fromY, toX, toY, promoChar);
\r
6401 DisplayMoveError(_("Could not parse move"));
\r
6404 EndDialog(hDlg, TRUE);
\r
6407 EndDialog(hDlg, FALSE);
\r
6418 PopUpMoveDialog(char firstchar)
\r
6422 if ((gameMode == BeginningOfGame && !appData.icsActive) ||
\r
6423 gameMode == MachinePlaysWhite || gameMode == MachinePlaysBlack ||
\r
6424 gameMode == AnalyzeMode || gameMode == EditGame ||
\r
6425 gameMode == EditPosition || gameMode == IcsExamining ||
\r
6426 gameMode == IcsPlayingWhite || gameMode == IcsPlayingBlack ||
\r
6427 isdigit(firstchar) && // [HGM] movenum: allow typing in of move nr in 'passive' modes
\r
6428 ( gameMode == AnalyzeFile || gameMode == PlayFromGameFile ||
\r
6429 gameMode == IcsObserving || gameMode == TwoMachinesPlay ) ||
\r
6430 gameMode == Training) {
\r
6431 lpProc = MakeProcInstance((FARPROC)TypeInMoveDialog, hInst);
\r
6432 DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_TypeInMove),
\r
6433 hwndMain, (DLGPROC)lpProc, (LPARAM)firstchar);
\r
6434 FreeProcInstance(lpProc);
\r
6438 /*---------------------------------------------------------------------------*\
\r
6440 * Type-in name dialog functions
\r
6442 \*---------------------------------------------------------------------------*/
\r
6445 TypeInNameDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
6447 char move[MSG_SIZ];
\r
6450 switch (message) {
\r
6451 case WM_INITDIALOG:
\r
6452 move[0] = (char) lParam;
\r
6453 move[1] = NULLCHAR;
\r
6454 CenterWindowEx(hDlg, GetWindow(hDlg, GW_OWNER), 1 );
\r
6455 Translate(hDlg, DLG_TypeInName);
\r
6456 hInput = GetDlgItem(hDlg, OPT_Name);
\r
6457 SetWindowText(hInput, move);
\r
6459 SendMessage(hInput, EM_SETSEL, (WPARAM)9999, (LPARAM)9999);
\r
6463 switch (LOWORD(wParam)) {
\r
6465 GetDlgItemText(hDlg, OPT_Name, move, sizeof(move));
\r
6466 appData.userName = strdup(move);
\r
6469 if(gameMode == MachinePlaysWhite || gameMode == MachinePlaysBlack) {
\r
6470 snprintf(move, MSG_SIZ, "%s vs. %s", gameInfo.white, gameInfo.black);
\r
6471 DisplayTitle(move);
\r
6475 EndDialog(hDlg, TRUE);
\r
6478 EndDialog(hDlg, FALSE);
\r
6489 PopUpNameDialog(char firstchar)
\r
6493 lpProc = MakeProcInstance((FARPROC)TypeInNameDialog, hInst);
\r
6494 DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_TypeInName),
\r
6495 hwndMain, (DLGPROC)lpProc, (LPARAM)firstchar);
\r
6496 FreeProcInstance(lpProc);
\r
6499 /*---------------------------------------------------------------------------*\
\r
6503 \*---------------------------------------------------------------------------*/
\r
6505 /* Nonmodal error box */
\r
6506 LRESULT CALLBACK ErrorDialog(HWND hDlg, UINT message,
\r
6507 WPARAM wParam, LPARAM lParam);
\r
6510 ErrorPopUp(char *title, char *content)
\r
6514 BOOLEAN modal = hwndMain == NULL;
\r
6532 strncpy(errorTitle, title, sizeof(errorTitle));
\r
6533 errorTitle[sizeof(errorTitle) - 1] = '\0';
\r
6536 MessageBox(NULL, errorMessage, errorTitle, MB_OK|MB_ICONEXCLAMATION);
\r
6538 lpProc = MakeProcInstance((FARPROC)ErrorDialog, hInst);
\r
6539 CreateDialog(hInst, MAKEINTRESOURCE(DLG_Error),
\r
6540 hwndMain, (DLGPROC)lpProc);
\r
6541 FreeProcInstance(lpProc);
\r
6548 if (!appData.popupMoveErrors && moveErrorMessageUp) DisplayMessage("", "");
\r
6549 if (errorDialog == NULL) return;
\r
6550 DestroyWindow(errorDialog);
\r
6551 errorDialog = NULL;
\r
6552 if(errorExitStatus) ExitEvent(errorExitStatus);
\r
6556 ErrorDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
6561 switch (message) {
\r
6562 case WM_INITDIALOG:
\r
6563 GetWindowRect(hDlg, &rChild);
\r
6566 SetWindowPos(hDlg, NULL, rChild.left,
\r
6567 rChild.top + boardRect.top - (rChild.bottom - rChild.top),
\r
6568 0, 0, SWP_NOZORDER|SWP_NOSIZE);
\r
6572 [AS] It seems that the above code wants to move the dialog up in the "caption
\r
6573 area" of the main window, but it uses the dialog height as an hard-coded constant,
\r
6574 and it doesn't work when you resize the dialog.
\r
6575 For now, just give it a default position.
\r
6577 SetWindowPos(hDlg, NULL, boardRect.left+8, boardRect.top+8, 0, 0, SWP_NOZORDER|SWP_NOSIZE);
\r
6578 Translate(hDlg, DLG_Error);
\r
6580 errorDialog = hDlg;
\r
6581 SetWindowText(hDlg, errorTitle);
\r
6582 hwndText = GetDlgItem(hDlg, OPT_ErrorText);
\r
6583 SetDlgItemText(hDlg, OPT_ErrorText, errorMessage);
\r
6587 switch (LOWORD(wParam)) {
\r
6590 if (errorDialog == hDlg) errorDialog = NULL;
\r
6591 DestroyWindow(hDlg);
\r
6603 HWND gothicDialog = NULL;
\r
6606 GothicDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
6610 int height = GetSystemMetrics(SM_CYCAPTION)+GetSystemMetrics(SM_CYFRAME);
\r
6612 switch (message) {
\r
6613 case WM_INITDIALOG:
\r
6614 GetWindowRect(hDlg, &rChild);
\r
6616 SetWindowPos(hDlg, NULL, wpMain.x, wpMain.y-height, wpMain.width, height,
\r
6620 [AS] It seems that the above code wants to move the dialog up in the "caption
\r
6621 area" of the main window, but it uses the dialog height as an hard-coded constant,
\r
6622 and it doesn't work when you resize the dialog.
\r
6623 For now, just give it a default position.
\r
6625 gothicDialog = hDlg;
\r
6626 SetWindowText(hDlg, errorTitle);
\r
6627 hwndText = GetDlgItem(hDlg, OPT_ErrorText);
\r
6628 SetDlgItemText(hDlg, OPT_ErrorText, errorMessage);
\r
6632 switch (LOWORD(wParam)) {
\r
6635 if (errorDialog == hDlg) errorDialog = NULL;
\r
6636 DestroyWindow(hDlg);
\r
6648 GothicPopUp(char *title, VariantClass variant)
\r
6651 static char *lastTitle;
\r
6653 strncpy(errorTitle, title, sizeof(errorTitle));
\r
6654 errorTitle[sizeof(errorTitle) - 1] = '\0';
\r
6656 if(lastTitle != title && gothicDialog != NULL) {
\r
6657 DestroyWindow(gothicDialog);
\r
6658 gothicDialog = NULL;
\r
6660 if(variant != VariantNormal && gothicDialog == NULL) {
\r
6661 title = lastTitle;
\r
6662 lpProc = MakeProcInstance((FARPROC)GothicDialog, hInst);
\r
6663 CreateDialog(hInst, MAKEINTRESOURCE(DLG_Error),
\r
6664 hwndMain, (DLGPROC)lpProc);
\r
6665 FreeProcInstance(lpProc);
\r
6670 /*---------------------------------------------------------------------------*\
\r
6672 * Ics Interaction console functions
\r
6674 \*---------------------------------------------------------------------------*/
\r
6676 #define HISTORY_SIZE 64
\r
6677 static char *history[HISTORY_SIZE];
\r
6678 int histIn = 0, histP = 0;
\r
6681 SaveInHistory(char *cmd)
\r
6683 if (history[histIn] != NULL) {
\r
6684 free(history[histIn]);
\r
6685 history[histIn] = NULL;
\r
6687 if (*cmd == NULLCHAR) return;
\r
6688 history[histIn] = StrSave(cmd);
\r
6689 histIn = (histIn + 1) % HISTORY_SIZE;
\r
6690 if (history[histIn] != NULL) {
\r
6691 free(history[histIn]);
\r
6692 history[histIn] = NULL;
\r
6698 PrevInHistory(char *cmd)
\r
6701 if (histP == histIn) {
\r
6702 if (history[histIn] != NULL) free(history[histIn]);
\r
6703 history[histIn] = StrSave(cmd);
\r
6705 newhp = (histP - 1 + HISTORY_SIZE) % HISTORY_SIZE;
\r
6706 if (newhp == histIn || history[newhp] == NULL) return NULL;
\r
6708 return history[histP];
\r
6714 if (histP == histIn) return NULL;
\r
6715 histP = (histP + 1) % HISTORY_SIZE;
\r
6716 return history[histP];
\r
6720 LoadIcsTextMenu(IcsTextMenuEntry *e)
\r
6724 hmenu = LoadMenu(hInst, "TextMenu");
\r
6725 h = GetSubMenu(hmenu, 0);
\r
6727 if (strcmp(e->item, "-") == 0) {
\r
6728 AppendMenu(h, MF_SEPARATOR, 0, 0);
\r
6729 } else { // [HGM] re-written a bit to use only one AppendMenu call for both cases (| or no |)
\r
6730 int flags = MF_STRING, j = 0;
\r
6731 if (e->item[0] == '|') {
\r
6732 flags |= MF_MENUBARBREAK;
\r
6735 if(!strcmp(e->command, "none")) flags |= MF_GRAYED; // [HGM] chatclick: provide inactive dummy
\r
6736 AppendMenu(h, flags, IDM_CommandX + i, e->item + j);
\r
6744 WNDPROC consoleTextWindowProc;
\r
6747 CommandX(HWND hwnd, char *command, BOOLEAN getname, BOOLEAN immediate)
\r
6749 char buf[MSG_SIZ], name[MSG_SIZ];
\r
6750 HWND hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
6754 SetWindowText(hInput, command);
\r
6756 SendMessage(hInput, WM_CHAR, '\r', 0);
\r
6758 sel.cpMin = 999999;
\r
6759 sel.cpMax = 999999;
\r
6760 SendMessage(hInput, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
6765 SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);
\r
6766 if (sel.cpMin == sel.cpMax) {
\r
6767 /* Expand to surrounding word */
\r
6770 tr.chrg.cpMax = sel.cpMin;
\r
6771 tr.chrg.cpMin = --sel.cpMin;
\r
6772 if (sel.cpMin < 0) break;
\r
6773 tr.lpstrText = name;
\r
6774 SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr);
\r
6775 } while (isalpha(name[0]) || isdigit(name[0]) || name[0] == '-');
\r
6779 tr.chrg.cpMin = sel.cpMax;
\r
6780 tr.chrg.cpMax = ++sel.cpMax;
\r
6781 tr.lpstrText = name;
\r
6782 if (SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr) < 1) break;
\r
6783 } while (isalpha(name[0]) || isdigit(name[0]) || name[0] == '-');
\r
6786 if (sel.cpMax == sel.cpMin || sel.cpMax - sel.cpMin > MSG_SIZ/2) {
\r
6787 MessageBeep(MB_ICONEXCLAMATION);
\r
6791 tr.lpstrText = name;
\r
6792 SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr);
\r
6794 if (sel.cpMax - sel.cpMin > MSG_SIZ/2) {
\r
6795 MessageBeep(MB_ICONEXCLAMATION);
\r
6798 SendMessage(hwnd, EM_GETSELTEXT, 0, (LPARAM) name);
\r
6801 snprintf(buf, MSG_SIZ, "%s %s", command, name);
\r
6802 SetWindowText(hInput, buf);
\r
6803 SendMessage(hInput, WM_CHAR, '\r', 0);
\r
6805 if(!strcmp(command, "chat")) { ChatPopUp(name); return; }
\r
6806 snprintf(buf, MSG_SIZ, "%s %s ", command, name); /* trailing space */
\r
6807 SetWindowText(hInput, buf);
\r
6808 sel.cpMin = 999999;
\r
6809 sel.cpMax = 999999;
\r
6810 SendMessage(hInput, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
6816 ConsoleTextSubclass(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
\r
6821 switch (message) {
\r
6823 if (!(GetKeyState(VK_CONTROL) & ~1)) break;
\r
6826 SendMessage(hwnd, EM_LINESCROLL, 0, -999999);
\r
6829 sel.cpMin = 999999;
\r
6830 sel.cpMax = 999999;
\r
6831 SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
6832 SendMessage(hwnd, EM_SCROLLCARET, 0, 0);
\r
6837 if(wParam != '\022') {
\r
6838 if (wParam == '\t') {
\r
6839 if (GetKeyState(VK_SHIFT) < 0) {
\r
6841 if (IsIconic(hwndMain)) ShowWindow(hwndMain, SW_RESTORE);
\r
6842 if (buttonDesc[0].hwnd) {
\r
6843 SetFocus(buttonDesc[0].hwnd);
\r
6845 SetFocus(hwndMain);
\r
6849 SetFocus(GetDlgItem(hwndConsole, OPT_ConsoleInput));
\r
6852 hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
6853 JAWS_DELETE( SetFocus(hInput); )
\r
6854 SendMessage(hInput, message, wParam, lParam);
\r
6857 } // [HGM] navigate: for Ctrl+R, flow into nex case (moved up here) to summon up menu
\r
6858 case WM_RBUTTONDOWN:
\r
6859 if (!(GetKeyState(VK_SHIFT) & ~1)) {
\r
6860 /* Move selection here if it was empty */
\r
6862 pt.x = LOWORD(lParam);
\r
6863 pt.y = HIWORD(lParam);
\r
6864 SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);
\r
6865 if (sel.cpMin == sel.cpMax) {
\r
6866 sel.cpMin = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&pt); /*doc is wrong*/
\r
6867 sel.cpMax = sel.cpMin;
\r
6868 SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
6870 SendMessage(hwnd, EM_HIDESELECTION, FALSE, FALSE);
\r
6871 { // [HGM] chatclick: code moved here from WM_RBUTTONUP case, to have menu appear on down-click
\r
6873 HMENU hmenu = LoadIcsTextMenu(icsTextMenuEntry);
\r
6874 SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);
\r
6875 if (sel.cpMin == sel.cpMax) {
\r
6876 EnableMenuItem(hmenu, IDM_Copy, MF_BYCOMMAND|MF_GRAYED);
\r
6877 EnableMenuItem(hmenu, IDM_QuickPaste, MF_BYCOMMAND|MF_GRAYED);
\r
6879 if (!IsClipboardFormatAvailable(CF_TEXT)) {
\r
6880 EnableMenuItem(hmenu, IDM_Paste, MF_BYCOMMAND|MF_GRAYED);
\r
6882 pt.x = LOWORD(lParam)-30; // [HGM] chatclick: make menu pop up with pointer above upper-right item
\r
6883 pt.y = HIWORD(lParam)-10; // make it appear as if mouse moved there, so it will be selected on up-click
\r
6884 PostMessage(hwnd, WM_MOUSEMOVE, wParam, lParam+5);
\r
6885 MenuPopup(hwnd, pt, hmenu, -1);
\r
6889 case WM_RBUTTONUP:
\r
6890 if (GetKeyState(VK_SHIFT) & ~1) {
\r
6891 SendDlgItemMessage(hwndConsole, OPT_ConsoleText,
\r
6892 WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);
\r
6896 hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
6898 return SendMessage(hInput, message, wParam, lParam);
\r
6899 case WM_MBUTTONDOWN:
\r
6900 return SendMessage(hwnd, WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);
\r
6902 switch (LOWORD(wParam)) {
\r
6903 case IDM_QuickPaste:
\r
6905 SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);
\r
6906 if (sel.cpMin == sel.cpMax) {
\r
6907 MessageBeep(MB_ICONEXCLAMATION);
\r
6910 SendMessage(hwnd, WM_COPY, 0, 0);
\r
6911 hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
6912 SendMessage(hInput, WM_PASTE, 0, 0);
\r
6917 SendMessage(hwnd, WM_CUT, 0, 0);
\r
6920 SendMessage(hwnd, WM_PASTE, 0, 0);
\r
6923 SendMessage(hwnd, WM_COPY, 0, 0);
\r
6927 int i = LOWORD(wParam) - IDM_CommandX;
\r
6928 if (i >= 0 && i < ICS_TEXT_MENU_SIZE &&
\r
6929 icsTextMenuEntry[i].command != NULL) {
\r
6930 CommandX(hwnd, icsTextMenuEntry[i].command,
\r
6931 icsTextMenuEntry[i].getname,
\r
6932 icsTextMenuEntry[i].immediate);
\r
6940 return (*consoleTextWindowProc)(hwnd, message, wParam, lParam);
\r
6943 WNDPROC consoleInputWindowProc;
\r
6946 ConsoleInputSubclass(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
\r
6948 char buf[MSG_SIZ];
\r
6950 static BOOL sendNextChar = FALSE;
\r
6951 static BOOL quoteNextChar = FALSE;
\r
6952 InputSource *is = consoleInputSource;
\r
6956 switch (message) {
\r
6958 if (!appData.localLineEditing || sendNextChar) {
\r
6959 is->buf[0] = (CHAR) wParam;
\r
6961 SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);
\r
6962 sendNextChar = FALSE;
\r
6965 if (quoteNextChar) {
\r
6966 buf[0] = (char) wParam;
\r
6967 buf[1] = NULLCHAR;
\r
6968 SendMessage(hwnd, EM_REPLACESEL, TRUE, (LPARAM) buf);
\r
6969 quoteNextChar = FALSE;
\r
6973 case '\r': /* Enter key */
\r
6974 is->count = GetWindowText(hwnd, is->buf, INPUT_SOURCE_BUF_SIZE-1);
\r
6975 if (consoleEcho) SaveInHistory(is->buf);
\r
6976 is->buf[is->count++] = '\n';
\r
6977 is->buf[is->count] = NULLCHAR;
\r
6978 SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);
\r
6979 if (consoleEcho) {
\r
6980 ConsoleOutput(is->buf, is->count, TRUE);
\r
6981 } else if (appData.localLineEditing) {
\r
6982 ConsoleOutput("\n", 1, TRUE);
\r
6985 case '\033': /* Escape key */
\r
6986 SetWindowText(hwnd, "");
\r
6987 cf.cbSize = sizeof(CHARFORMAT);
\r
6988 cf.dwMask = CFM_COLOR|CFM_BOLD|CFM_ITALIC|CFM_UNDERLINE|CFM_STRIKEOUT;
\r
6989 if (consoleEcho) {
\r
6990 cf.crTextColor = textAttribs[ColorNormal].color;
\r
6992 cf.crTextColor = COLOR_ECHOOFF;
\r
6994 cf.dwEffects = textAttribs[ColorNormal].effects;
\r
6995 SendMessage(hwnd, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf);
\r
6997 case '\t': /* Tab key */
\r
6998 if (GetKeyState(VK_SHIFT) < 0) {
\r
7000 SetFocus(GetDlgItem(hwndConsole, OPT_ConsoleText));
\r
7003 if (IsIconic(hwndMain)) ShowWindow(hwndMain, SW_RESTORE);
\r
7004 if (buttonDesc[0].hwnd) {
\r
7005 SetFocus(buttonDesc[0].hwnd);
\r
7007 SetFocus(hwndMain);
\r
7011 case '\023': /* Ctrl+S */
\r
7012 sendNextChar = TRUE;
\r
7014 case '\021': /* Ctrl+Q */
\r
7015 quoteNextChar = TRUE;
\r
7025 GetWindowText(hwnd, buf, MSG_SIZ);
\r
7026 p = PrevInHistory(buf);
\r
7028 SetWindowText(hwnd, p);
\r
7029 sel.cpMin = 999999;
\r
7030 sel.cpMax = 999999;
\r
7031 SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
7036 p = NextInHistory();
\r
7038 SetWindowText(hwnd, p);
\r
7039 sel.cpMin = 999999;
\r
7040 sel.cpMax = 999999;
\r
7041 SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
7047 if (!(GetKeyState(VK_CONTROL) & ~1)) break;
\r
7051 SendDlgItemMessage(hwndConsole, OPT_ConsoleText, message, wParam, lParam);
\r
7055 case WM_MBUTTONDOWN:
\r
7056 SendDlgItemMessage(hwndConsole, OPT_ConsoleText,
\r
7057 WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);
\r
7059 case WM_RBUTTONUP:
\r
7060 if (GetKeyState(VK_SHIFT) & ~1) {
\r
7061 SendDlgItemMessage(hwndConsole, OPT_ConsoleText,
\r
7062 WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);
\r
7066 hmenu = LoadMenu(hInst, "InputMenu");
\r
7067 SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);
\r
7068 if (sel.cpMin == sel.cpMax) {
\r
7069 EnableMenuItem(hmenu, IDM_Copy, MF_BYCOMMAND|MF_GRAYED);
\r
7070 EnableMenuItem(hmenu, IDM_Cut, MF_BYCOMMAND|MF_GRAYED);
\r
7072 if (!IsClipboardFormatAvailable(CF_TEXT)) {
\r
7073 EnableMenuItem(hmenu, IDM_Paste, MF_BYCOMMAND|MF_GRAYED);
\r
7075 pt.x = LOWORD(lParam);
\r
7076 pt.y = HIWORD(lParam);
\r
7077 MenuPopup(hwnd, pt, hmenu, -1);
\r
7081 switch (LOWORD(wParam)) {
\r
7083 SendMessage(hwnd, EM_UNDO, 0, 0);
\r
7085 case IDM_SelectAll:
\r
7087 sel.cpMax = -1; /*999999?*/
\r
7088 SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
7091 SendMessage(hwnd, WM_CUT, 0, 0);
\r
7094 SendMessage(hwnd, WM_PASTE, 0, 0);
\r
7097 SendMessage(hwnd, WM_COPY, 0, 0);
\r
7102 return (*consoleInputWindowProc)(hwnd, message, wParam, lParam);
\r
7105 #define CO_MAX 100000
\r
7106 #define CO_TRIM 1000
\r
7109 ConsoleWndProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
7111 static SnapData sd;
\r
7112 HWND hText, hInput;
\r
7114 static int sizeX, sizeY;
\r
7115 int newSizeX, newSizeY;
\r
7119 hText = GetDlgItem(hDlg, OPT_ConsoleText);
\r
7120 hInput = GetDlgItem(hDlg, OPT_ConsoleInput);
\r
7122 switch (message) {
\r
7124 if (((NMHDR*)lParam)->code == EN_LINK)
\r
7126 ENLINK *pLink = (ENLINK*)lParam;
\r
7127 if (pLink->msg == WM_LBUTTONUP)
\r
7131 tr.chrg = pLink->chrg;
\r
7132 tr.lpstrText = malloc(1+tr.chrg.cpMax-tr.chrg.cpMin);
\r
7133 SendMessage(hText, EM_GETTEXTRANGE, 0, (LPARAM)&tr);
\r
7134 ShellExecute(NULL, "open", tr.lpstrText, NULL, NULL, SW_SHOW);
\r
7135 free(tr.lpstrText);
\r
7139 case WM_INITDIALOG: /* message: initialize dialog box */
\r
7140 hwndConsole = hDlg;
\r
7142 consoleTextWindowProc = (WNDPROC)
\r
7143 SetWindowLong(hText, GWL_WNDPROC, (LONG) ConsoleTextSubclass);
\r
7144 SendMessage(hText, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);
\r
7145 consoleInputWindowProc = (WNDPROC)
\r
7146 SetWindowLong(hInput, GWL_WNDPROC, (LONG) ConsoleInputSubclass);
\r
7147 SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);
\r
7148 Colorize(ColorNormal, TRUE);
\r
7149 SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &consoleCF);
\r
7150 ChangedConsoleFont();
\r
7151 GetClientRect(hDlg, &rect);
\r
7152 sizeX = rect.right;
\r
7153 sizeY = rect.bottom;
\r
7154 if (wpConsole.x != CW_USEDEFAULT && wpConsole.y != CW_USEDEFAULT &&
\r
7155 wpConsole.width != CW_USEDEFAULT && wpConsole.height != CW_USEDEFAULT) {
\r
7156 WINDOWPLACEMENT wp;
\r
7157 EnsureOnScreen(&wpConsole.x, &wpConsole.y, 0, 0);
\r
7158 wp.length = sizeof(WINDOWPLACEMENT);
\r
7160 wp.showCmd = SW_SHOW;
\r
7161 wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;
\r
7162 wp.rcNormalPosition.left = wpConsole.x;
\r
7163 wp.rcNormalPosition.right = wpConsole.x + wpConsole.width;
\r
7164 wp.rcNormalPosition.top = wpConsole.y;
\r
7165 wp.rcNormalPosition.bottom = wpConsole.y + wpConsole.height;
\r
7166 SetWindowPlacement(hDlg, &wp);
\r
7169 // [HGM] Chessknight's change 2004-07-13
\r
7170 else { /* Determine Defaults */
\r
7171 WINDOWPLACEMENT wp;
\r
7172 wpConsole.x = wpMain.width + 1;
\r
7173 wpConsole.y = wpMain.y;
\r
7174 wpConsole.width = screenWidth - wpMain.width;
\r
7175 wpConsole.height = wpMain.height;
\r
7176 EnsureOnScreen(&wpConsole.x, &wpConsole.y, 0, 0);
\r
7177 wp.length = sizeof(WINDOWPLACEMENT);
\r
7179 wp.showCmd = SW_SHOW;
\r
7180 wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;
\r
7181 wp.rcNormalPosition.left = wpConsole.x;
\r
7182 wp.rcNormalPosition.right = wpConsole.x + wpConsole.width;
\r
7183 wp.rcNormalPosition.top = wpConsole.y;
\r
7184 wp.rcNormalPosition.bottom = wpConsole.y + wpConsole.height;
\r
7185 SetWindowPlacement(hDlg, &wp);
\r
7188 // Allow hText to highlight URLs and send notifications on them
\r
7189 wMask = (WORD) SendMessage(hText, EM_GETEVENTMASK, 0, 0L);
\r
7190 SendMessage(hText, EM_SETEVENTMASK, 0, wMask | ENM_LINK);
\r
7191 SendMessage(hText, EM_AUTOURLDETECT, TRUE, 0L);
\r
7192 SetWindowLong(hText, GWL_USERDATA, 79); // initialize the text window's width
\r
7206 if (IsIconic(hDlg)) break;
\r
7207 newSizeX = LOWORD(lParam);
\r
7208 newSizeY = HIWORD(lParam);
\r
7209 if (sizeX != newSizeX || sizeY != newSizeY) {
\r
7210 RECT rectText, rectInput;
\r
7212 int newTextHeight, newTextWidth;
\r
7213 GetWindowRect(hText, &rectText);
\r
7214 newTextWidth = rectText.right - rectText.left + newSizeX - sizeX;
\r
7215 newTextHeight = rectText.bottom - rectText.top + newSizeY - sizeY;
\r
7216 if (newTextHeight < 0) {
\r
7217 newSizeY += -newTextHeight;
\r
7218 newTextHeight = 0;
\r
7220 SetWindowPos(hText, NULL, 0, 0,
\r
7221 newTextWidth, newTextHeight, SWP_NOZORDER|SWP_NOMOVE);
\r
7222 GetWindowRect(hInput, &rectInput); /* gives screen coords */
\r
7223 pt.x = rectInput.left;
\r
7224 pt.y = rectInput.top + newSizeY - sizeY;
\r
7225 ScreenToClient(hDlg, &pt);
\r
7226 SetWindowPos(hInput, NULL,
\r
7227 pt.x, pt.y, /* needs client coords */
\r
7228 rectInput.right - rectInput.left + newSizeX - sizeX,
\r
7229 rectInput.bottom - rectInput.top, SWP_NOZORDER);
\r
7235 case WM_GETMINMAXINFO:
\r
7236 /* Prevent resizing window too small */
\r
7237 mmi = (MINMAXINFO *) lParam;
\r
7238 mmi->ptMinTrackSize.x = 100;
\r
7239 mmi->ptMinTrackSize.y = 100;
\r
7242 /* [AS] Snapping */
\r
7243 case WM_ENTERSIZEMOVE:
\r
7244 return OnEnterSizeMove( &sd, hDlg, wParam, lParam );
\r
7247 return OnSizing( &sd, hDlg, wParam, lParam );
\r
7250 return OnMoving( &sd, hDlg, wParam, lParam );
\r
7252 case WM_EXITSIZEMOVE:
\r
7253 UpdateICSWidth(hText);
\r
7254 return OnExitSizeMove( &sd, hDlg, wParam, lParam );
\r
7257 return DefWindowProc(hDlg, message, wParam, lParam);
\r
7265 if (hwndConsole) return;
\r
7266 hCons = CreateDialog(hInst, szConsoleName, 0, NULL);
\r
7267 SendMessage(hCons, WM_INITDIALOG, 0, 0);
\r
7272 ConsoleOutput(char* data, int length, int forceVisible)
\r
7277 char buf[CO_MAX+1];
\r
7280 static int delayLF = 0;
\r
7281 CHARRANGE savesel, sel;
\r
7283 if (hwndConsole == NULL || length > CO_MAX-100 || length == 0) return;
\r
7291 while (length--) {
\r
7299 } else if (*p == '\007') {
\r
7300 MyPlaySound(&sounds[(int)SoundBell]);
\r
7307 hText = GetDlgItem(hwndConsole, OPT_ConsoleText);
\r
7308 SendMessage(hText, EM_HIDESELECTION, TRUE, FALSE);
\r
7309 /* Save current selection */
\r
7310 SendMessage(hText, EM_EXGETSEL, 0, (LPARAM)&savesel);
\r
7311 exlen = GetWindowTextLength(hText);
\r
7312 /* Find out whether current end of text is visible */
\r
7313 SendMessage(hText, EM_GETRECT, 0, (LPARAM) &rect);
\r
7314 SendMessage(hText, EM_POSFROMCHAR, (WPARAM) &pEnd, exlen);
\r
7315 /* Trim existing text if it's too long */
\r
7316 if (exlen + (q - buf) > CO_MAX) {
\r
7317 trim = (CO_TRIM > (q - buf)) ? CO_TRIM : (q - buf);
\r
7320 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
7321 SendMessage(hText, EM_REPLACESEL, 0, (LPARAM)"");
\r
7323 savesel.cpMin -= trim;
\r
7324 savesel.cpMax -= trim;
\r
7325 if (exlen < 0) exlen = 0;
\r
7326 if (savesel.cpMin < 0) savesel.cpMin = 0;
\r
7327 if (savesel.cpMax < savesel.cpMin) savesel.cpMax = savesel.cpMin;
\r
7329 /* Append the new text */
\r
7330 sel.cpMin = exlen;
\r
7331 sel.cpMax = exlen;
\r
7332 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
7333 SendMessage(hText, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&consoleCF);
\r
7334 SendMessage(hText, EM_REPLACESEL, 0, (LPARAM) buf);
\r
7335 if (forceVisible || exlen == 0 ||
\r
7336 (rect.left <= pEnd.x && pEnd.x < rect.right &&
\r
7337 rect.top <= pEnd.y && pEnd.y < rect.bottom)) {
\r
7338 /* Scroll to make new end of text visible if old end of text
\r
7339 was visible or new text is an echo of user typein */
\r
7340 sel.cpMin = 9999999;
\r
7341 sel.cpMax = 9999999;
\r
7342 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
7343 SendMessage(hText, EM_HIDESELECTION, FALSE, FALSE);
\r
7344 SendMessage(hText, EM_SCROLLCARET, 0, 0);
\r
7345 SendMessage(hText, EM_HIDESELECTION, TRUE, FALSE);
\r
7347 if (savesel.cpMax == exlen || forceVisible) {
\r
7348 /* Move insert point to new end of text if it was at the old
\r
7349 end of text or if the new text is an echo of user typein */
\r
7350 sel.cpMin = 9999999;
\r
7351 sel.cpMax = 9999999;
\r
7352 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
7354 /* Restore previous selection */
\r
7355 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&savesel);
\r
7357 SendMessage(hText, EM_HIDESELECTION, FALSE, FALSE);
\r
7364 DisplayHoldingsCount(HDC hdc, int x, int y, int rightAlign, int copyNumber)
\r
7368 COLORREF oldFg, oldBg;
\r
7373 snprintf(buf, sizeof(buf)/sizeof(buf[0]),"%d", copyNumber); else buf[0] = 0;
\r
7375 oldFg = SetTextColor(hdc, RGB(255, 255, 255)); /* white */
\r
7376 oldBg = SetBkColor(hdc, RGB(0, 0, 0)); /* black */
\r
7377 oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);
\r
7380 rect.right = x + squareSize;
\r
7382 rect.bottom = y + squareSize;
\r
7385 ExtTextOut(hdc, x + MESSAGE_LINE_LEFTMARGIN
\r
7386 + (rightAlign ? (squareSize*2)/3 : 0),
\r
7387 y, ETO_CLIPPED|ETO_OPAQUE,
\r
7388 &rect, str, strlen(str), NULL);
\r
7390 (void) SetTextColor(hdc, oldFg);
\r
7391 (void) SetBkColor(hdc, oldBg);
\r
7392 (void) SelectObject(hdc, oldFont);
\r
7396 DisplayAClock(HDC hdc, int timeRemaining, int highlight,
\r
7397 RECT *rect, char *color, char *flagFell)
\r
7401 COLORREF oldFg, oldBg;
\r
7404 if (appData.clockMode) {
\r
7406 snprintf(buf, sizeof(buf)/sizeof(buf[0]), "%c %s %s", color[0], TimeString(timeRemaining), flagFell);
\r
7408 snprintf(buf, sizeof(buf)/sizeof(buf[0]), "%s:%c%s %s", color, (logoHeight>0 ? 0 : ' '), TimeString(timeRemaining), flagFell);
\r
7415 oldFg = SetTextColor(hdc, RGB(255, 255, 255)); /* white */
\r
7416 oldBg = SetBkColor(hdc, RGB(0, 0, 0)); /* black */
\r
7418 oldFg = SetTextColor(hdc, RGB(0, 0, 0)); /* black */
\r
7419 oldBg = SetBkColor(hdc, RGB(255, 255, 255)); /* white */
\r
7421 oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);
\r
7425 ExtTextOut(hdc, rect->left + MESSAGE_LINE_LEFTMARGIN,
\r
7426 rect->top, ETO_CLIPPED|ETO_OPAQUE,
\r
7427 rect, str, strlen(str), NULL);
\r
7428 if(logoHeight > 0 && appData.clockMode) {
\r
7430 snprintf(buf, sizeof(buf)/sizeof(buf[0]), "%s %s", buf+7, flagFell);
\r
7431 r.top = rect->top + logoHeight/2;
\r
7432 r.left = rect->left;
\r
7433 r.right = rect->right;
\r
7434 r.bottom = rect->bottom;
\r
7435 ExtTextOut(hdc, rect->left + MESSAGE_LINE_LEFTMARGIN,
\r
7436 r.top, ETO_CLIPPED|ETO_OPAQUE,
\r
7437 &r, str, strlen(str), NULL);
\r
7439 (void) SetTextColor(hdc, oldFg);
\r
7440 (void) SetBkColor(hdc, oldBg);
\r
7441 (void) SelectObject(hdc, oldFont);
\r
7446 DoReadFile(HANDLE hFile, char *buf, int count, DWORD *outCount,
\r
7452 if( count <= 0 ) {
\r
7453 if (appData.debugMode) {
\r
7454 fprintf( debugFP, "DoReadFile: trying to read past end of buffer, overflow = %d\n", count );
\r
7457 return ERROR_INVALID_USER_BUFFER;
\r
7460 ResetEvent(ovl->hEvent);
\r
7461 ovl->Offset = ovl->OffsetHigh = 0;
\r
7462 ok = ReadFile(hFile, buf, count, outCount, ovl);
\r
7466 err = GetLastError();
\r
7467 if (err == ERROR_IO_PENDING) {
\r
7468 ok = GetOverlappedResult(hFile, ovl, outCount, TRUE);
\r
7472 err = GetLastError();
\r
7479 DoWriteFile(HANDLE hFile, char *buf, int count, DWORD *outCount,
\r
7484 ResetEvent(ovl->hEvent);
\r
7485 ovl->Offset = ovl->OffsetHigh = 0;
\r
7486 ok = WriteFile(hFile, buf, count, outCount, ovl);
\r
7490 err = GetLastError();
\r
7491 if (err == ERROR_IO_PENDING) {
\r
7492 ok = GetOverlappedResult(hFile, ovl, outCount, TRUE);
\r
7496 err = GetLastError();
\r
7502 /* [AS] If input is line by line and a line exceed the buffer size, force an error */
\r
7503 void CheckForInputBufferFull( InputSource * is )
\r
7505 if( is->lineByLine && (is->next - is->buf) >= INPUT_SOURCE_BUF_SIZE ) {
\r
7506 /* Look for end of line */
\r
7507 char * p = is->buf;
\r
7509 while( p < is->next && *p != '\n' ) {
\r
7513 if( p >= is->next ) {
\r
7514 if (appData.debugMode) {
\r
7515 fprintf( debugFP, "Input line exceeded buffer size (source id=%lu)\n", is->id );
\r
7518 is->error = ERROR_BROKEN_PIPE; /* [AS] Just any non-successful code! */
\r
7519 is->count = (DWORD) -1;
\r
7520 is->next = is->buf;
\r
7526 InputThread(LPVOID arg)
\r
7531 is = (InputSource *) arg;
\r
7532 ovl.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
\r
7533 ovl.Internal = ovl.InternalHigh = ovl.Offset = ovl.OffsetHigh = 0;
\r
7534 while (is->hThread != NULL) {
\r
7535 is->error = DoReadFile(is->hFile, is->next,
\r
7536 INPUT_SOURCE_BUF_SIZE - (is->next - is->buf),
\r
7537 &is->count, &ovl);
\r
7538 if (is->error == NO_ERROR) {
\r
7539 is->next += is->count;
\r
7541 if (is->error == ERROR_BROKEN_PIPE) {
\r
7542 /* Correct for MS brain damage. EOF reading a pipe is not an error. */
\r
7545 is->count = (DWORD) -1;
\r
7546 /* [AS] The (is->count <= 0) check below is not useful for unsigned values! */
\r
7551 CheckForInputBufferFull( is );
\r
7553 SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);
\r
7555 if( is->count == ((DWORD) -1) ) break; /* [AS] */
\r
7557 if (is->count <= 0) break; /* Quit on EOF or error */
\r
7560 CloseHandle(ovl.hEvent);
\r
7561 CloseHandle(is->hFile);
\r
7563 if (appData.debugMode) {
\r
7564 fprintf( debugFP, "Input thread terminated (id=%lu, error=%d, count=%ld)\n", is->id, is->error, is->count );
\r
7571 /* Windows 95 beta 2 won't let you do overlapped i/o on a console or pipe */
\r
7573 NonOvlInputThread(LPVOID arg)
\r
7580 is = (InputSource *) arg;
\r
7581 while (is->hThread != NULL) {
\r
7582 is->error = ReadFile(is->hFile, is->next,
\r
7583 INPUT_SOURCE_BUF_SIZE - (is->next - is->buf),
\r
7584 &is->count, NULL) ? NO_ERROR : GetLastError();
\r
7585 if (is->error == NO_ERROR) {
\r
7586 /* Change CRLF to LF */
\r
7587 if (is->next > is->buf) {
\r
7589 i = is->count + 1;
\r
7597 if (prev == '\r' && *p == '\n') {
\r
7609 if (is->error == ERROR_BROKEN_PIPE) {
\r
7610 /* Correct for MS brain damage. EOF reading a pipe is not an error. */
\r
7613 is->count = (DWORD) -1;
\r
7617 CheckForInputBufferFull( is );
\r
7619 SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);
\r
7621 if( is->count == ((DWORD) -1) ) break; /* [AS] */
\r
7623 if (is->count < 0) break; /* Quit on error */
\r
7625 CloseHandle(is->hFile);
\r
7630 SocketInputThread(LPVOID arg)
\r
7634 is = (InputSource *) arg;
\r
7635 while (is->hThread != NULL) {
\r
7636 is->count = recv(is->sock, is->buf, INPUT_SOURCE_BUF_SIZE, 0);
\r
7637 if ((int)is->count == SOCKET_ERROR) {
\r
7638 is->count = (DWORD) -1;
\r
7639 is->error = WSAGetLastError();
\r
7641 is->error = NO_ERROR;
\r
7642 is->next += is->count;
\r
7643 if (is->count == 0 && is->second == is) {
\r
7644 /* End of file on stderr; quit with no message */
\r
7648 SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);
\r
7650 if( is->count == ((DWORD) -1) ) break; /* [AS] */
\r
7652 if (is->count <= 0) break; /* Quit on EOF or error */
\r
7658 InputEvent(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
\r
7662 is = (InputSource *) lParam;
\r
7663 if (is->lineByLine) {
\r
7664 /* Feed in lines one by one */
\r
7665 char *p = is->buf;
\r
7667 while (q < is->next) {
\r
7668 if (*q++ == '\n') {
\r
7669 (is->func)(is, is->closure, p, q - p, NO_ERROR);
\r
7674 /* Move any partial line to the start of the buffer */
\r
7676 while (p < is->next) {
\r
7681 if (is->error != NO_ERROR || is->count == 0) {
\r
7682 /* Notify backend of the error. Note: If there was a partial
\r
7683 line at the end, it is not flushed through. */
\r
7684 (is->func)(is, is->closure, is->buf, is->count, is->error);
\r
7687 /* Feed in the whole chunk of input at once */
\r
7688 (is->func)(is, is->closure, is->buf, is->count, is->error);
\r
7689 is->next = is->buf;
\r
7693 /*---------------------------------------------------------------------------*\
\r
7695 * Menu enables. Used when setting various modes.
\r
7697 \*---------------------------------------------------------------------------*/
\r
7705 GreyRevert(Boolean grey)
\r
7706 { // [HGM] vari: for retracting variations in local mode
\r
7707 HMENU hmenu = GetMenu(hwndMain);
\r
7708 EnableMenuItem(hmenu, IDM_Revert, MF_BYCOMMAND|(grey ? MF_GRAYED : MF_ENABLED));
\r
7709 EnableMenuItem(hmenu, IDM_Annotate, MF_BYCOMMAND|(grey ? MF_GRAYED : MF_ENABLED));
\r
7713 SetMenuEnables(HMENU hmenu, Enables *enab)
\r
7715 while (enab->item > 0) {
\r
7716 (void) EnableMenuItem(hmenu, enab->item, enab->flags);
\r
7721 Enables gnuEnables[] = {
\r
7722 { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },
\r
7723 { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },
\r
7724 { IDM_IcsClient, MF_BYCOMMAND|MF_GRAYED },
\r
7725 { IDM_Accept, MF_BYCOMMAND|MF_GRAYED },
\r
7726 { IDM_Decline, MF_BYCOMMAND|MF_GRAYED },
\r
7727 { IDM_Rematch, MF_BYCOMMAND|MF_GRAYED },
\r
7728 { IDM_Adjourn, MF_BYCOMMAND|MF_GRAYED },
\r
7729 { IDM_StopExamining, MF_BYCOMMAND|MF_GRAYED },
\r
7730 { IDM_StopObserving, MF_BYCOMMAND|MF_GRAYED },
\r
7731 { IDM_Upload, MF_BYCOMMAND|MF_GRAYED },
\r
7732 { IDM_Revert, MF_BYCOMMAND|MF_GRAYED },
\r
7733 { IDM_Annotate, MF_BYCOMMAND|MF_GRAYED },
\r
7734 { IDM_NewChat, MF_BYCOMMAND|MF_GRAYED },
\r
7738 Enables icsEnables[] = {
\r
7739 { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },
\r
7740 { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },
\r
7741 { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },
\r
7742 { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },
\r
7743 { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },
\r
7744 { IDM_Match, MF_BYCOMMAND|MF_GRAYED },
\r
7745 { IDM_MachineBoth, MF_BYCOMMAND|MF_GRAYED },
\r
7746 { IDM_AnalysisMode, MF_BYCOMMAND|MF_ENABLED },
\r
7747 { IDM_AnalyzeFile, MF_BYCOMMAND|MF_GRAYED },
\r
7748 { IDM_TimeControl, MF_BYCOMMAND|MF_GRAYED },
\r
7749 { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },
\r
7750 { IDM_Hint, MF_BYCOMMAND|MF_GRAYED },
\r
7751 { IDM_Book, MF_BYCOMMAND|MF_GRAYED },
\r
7752 { IDM_IcsOptions, MF_BYCOMMAND|MF_ENABLED },
\r
7753 { IDM_Engine1Options, MF_BYCOMMAND|MF_GRAYED },
\r
7754 { IDM_Engine2Options, MF_BYCOMMAND|MF_GRAYED },
\r
7755 { IDM_Annotate, MF_BYCOMMAND|MF_GRAYED },
\r
7760 Enables zippyEnables[] = {
\r
7761 { IDM_MoveNow, MF_BYCOMMAND|MF_ENABLED },
\r
7762 { IDM_Hint, MF_BYCOMMAND|MF_ENABLED },
\r
7763 { IDM_Book, MF_BYCOMMAND|MF_ENABLED },
\r
7764 { IDM_Engine1Options, MF_BYCOMMAND|MF_ENABLED },
\r
7769 Enables ncpEnables[] = {
\r
7770 { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },
\r
7771 { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },
\r
7772 { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },
\r
7773 { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },
\r
7774 { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },
\r
7775 { IDM_Match, MF_BYCOMMAND|MF_GRAYED },
\r
7776 { IDM_AnalysisMode, MF_BYCOMMAND|MF_GRAYED },
\r
7777 { IDM_AnalyzeFile, MF_BYCOMMAND|MF_GRAYED },
\r
7778 { IDM_IcsClient, MF_BYCOMMAND|MF_GRAYED },
\r
7779 { ACTION_POS, MF_BYPOSITION|MF_GRAYED },
\r
7780 { IDM_Revert, MF_BYCOMMAND|MF_GRAYED },
\r
7781 { IDM_Annotate, MF_BYCOMMAND|MF_GRAYED },
\r
7782 { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },
\r
7783 { IDM_RetractMove, MF_BYCOMMAND|MF_GRAYED },
\r
7784 { IDM_TimeControl, MF_BYCOMMAND|MF_GRAYED },
\r
7785 { IDM_Hint, MF_BYCOMMAND|MF_GRAYED },
\r
7786 { IDM_Book, MF_BYCOMMAND|MF_GRAYED },
\r
7787 { IDM_MachineBoth, MF_BYCOMMAND|MF_GRAYED },
\r
7788 { IDM_NewChat, MF_BYCOMMAND|MF_GRAYED },
\r
7789 { IDM_Engine1Options, MF_BYCOMMAND|MF_GRAYED },
\r
7790 { IDM_Engine2Options, MF_BYCOMMAND|MF_GRAYED },
\r
7794 Enables trainingOnEnables[] = {
\r
7795 { IDM_EditComment, MF_BYCOMMAND|MF_GRAYED },
\r
7796 { IDM_Pause, MF_BYCOMMAND|MF_GRAYED },
\r
7797 { IDM_Forward, MF_BYCOMMAND|MF_GRAYED },
\r
7798 { IDM_Backward, MF_BYCOMMAND|MF_GRAYED },
\r
7799 { IDM_ToEnd, MF_BYCOMMAND|MF_GRAYED },
\r
7800 { IDM_ToStart, MF_BYCOMMAND|MF_GRAYED },
\r
7801 { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },
\r
7802 { IDM_TruncateGame, MF_BYCOMMAND|MF_GRAYED },
\r
7806 Enables trainingOffEnables[] = {
\r
7807 { IDM_EditComment, MF_BYCOMMAND|MF_ENABLED },
\r
7808 { IDM_Pause, MF_BYCOMMAND|MF_ENABLED },
\r
7809 { IDM_Forward, MF_BYCOMMAND|MF_ENABLED },
\r
7810 { IDM_Backward, MF_BYCOMMAND|MF_ENABLED },
\r
7811 { IDM_ToEnd, MF_BYCOMMAND|MF_ENABLED },
\r
7812 { IDM_ToStart, MF_BYCOMMAND|MF_ENABLED },
\r
7813 { IDM_MoveNow, MF_BYCOMMAND|MF_ENABLED },
\r
7814 { IDM_TruncateGame, MF_BYCOMMAND|MF_ENABLED },
\r
7818 /* These modify either ncpEnables or gnuEnables */
\r
7819 Enables cmailEnables[] = {
\r
7820 { IDM_MailMove, MF_BYCOMMAND|MF_ENABLED },
\r
7821 { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_ENABLED },
\r
7822 { ACTION_POS, MF_BYPOSITION|MF_ENABLED },
\r
7823 { IDM_CallFlag, MF_BYCOMMAND|MF_GRAYED },
\r
7824 { IDM_Draw, MF_BYCOMMAND|MF_ENABLED },
\r
7825 { IDM_Adjourn, MF_BYCOMMAND|MF_GRAYED },
\r
7826 { IDM_Abort, MF_BYCOMMAND|MF_GRAYED },
\r
7830 Enables machineThinkingEnables[] = {
\r
7831 { IDM_LoadGame, MF_BYCOMMAND|MF_GRAYED },
\r
7832 { IDM_LoadNextGame, MF_BYCOMMAND|MF_GRAYED },
\r
7833 { IDM_LoadPrevGame, MF_BYCOMMAND|MF_GRAYED },
\r
7834 { IDM_ReloadGame, MF_BYCOMMAND|MF_GRAYED },
\r
7835 { IDM_PasteGame, MF_BYCOMMAND|MF_GRAYED },
\r
7836 { IDM_LoadPosition, MF_BYCOMMAND|MF_GRAYED },
\r
7837 { IDM_LoadNextPosition, MF_BYCOMMAND|MF_GRAYED },
\r
7838 { IDM_LoadPrevPosition, MF_BYCOMMAND|MF_GRAYED },
\r
7839 { IDM_ReloadPosition, MF_BYCOMMAND|MF_GRAYED },
\r
7840 { IDM_PastePosition, MF_BYCOMMAND|MF_GRAYED },
\r
7841 { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },
\r
7842 { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },
\r
7843 { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },
\r
7844 { IDM_Match, MF_BYCOMMAND|MF_GRAYED },
\r
7845 { IDM_TypeInMove, MF_BYCOMMAND|MF_GRAYED },
\r
7846 { IDM_RetractMove, MF_BYCOMMAND|MF_GRAYED },
\r
7850 Enables userThinkingEnables[] = {
\r
7851 { IDM_LoadGame, MF_BYCOMMAND|MF_ENABLED },
\r
7852 { IDM_LoadNextGame, MF_BYCOMMAND|MF_ENABLED },
\r
7853 { IDM_LoadPrevGame, MF_BYCOMMAND|MF_ENABLED },
\r
7854 { IDM_ReloadGame, MF_BYCOMMAND|MF_ENABLED },
\r
7855 { IDM_PasteGame, MF_BYCOMMAND|MF_ENABLED },
\r
7856 { IDM_LoadPosition, MF_BYCOMMAND|MF_ENABLED },
\r
7857 { IDM_LoadNextPosition, MF_BYCOMMAND|MF_ENABLED },
\r
7858 { IDM_LoadPrevPosition, MF_BYCOMMAND|MF_ENABLED },
\r
7859 { IDM_ReloadPosition, MF_BYCOMMAND|MF_ENABLED },
\r
7860 { IDM_PastePosition, MF_BYCOMMAND|MF_ENABLED },
\r
7861 { IDM_MachineWhite, MF_BYCOMMAND|MF_ENABLED },
\r
7862 { IDM_MachineBlack, MF_BYCOMMAND|MF_ENABLED },
\r
7863 { IDM_TwoMachines, MF_BYCOMMAND|MF_ENABLED },
\r
7864 { IDM_Match, MF_BYCOMMAND|MF_ENABLED },
\r
7865 { IDM_TypeInMove, MF_BYCOMMAND|MF_ENABLED },
\r
7866 { IDM_RetractMove, MF_BYCOMMAND|MF_ENABLED },
\r
7870 /*---------------------------------------------------------------------------*\
\r
7872 * Front-end interface functions exported by XBoard.
\r
7873 * Functions appear in same order as prototypes in frontend.h.
\r
7875 \*---------------------------------------------------------------------------*/
\r
7879 static UINT prevChecked = 0;
\r
7880 static int prevPausing = 0;
\r
7883 if (pausing != prevPausing) {
\r
7884 prevPausing = pausing;
\r
7885 (void) CheckMenuItem(GetMenu(hwndMain), IDM_Pause,
\r
7886 MF_BYCOMMAND|(pausing ? MF_CHECKED : MF_UNCHECKED));
\r
7887 if (hwndPause) SetWindowText(hwndPause, pausing ? "C" : "P");
\r
7890 switch (gameMode) {
\r
7891 case BeginningOfGame:
\r
7892 if (appData.icsActive)
\r
7893 nowChecked = IDM_IcsClient;
\r
7894 else if (appData.noChessProgram)
\r
7895 nowChecked = IDM_EditGame;
\r
7897 nowChecked = IDM_MachineBlack;
\r
7899 case MachinePlaysBlack:
\r
7900 nowChecked = IDM_MachineBlack;
\r
7902 case MachinePlaysWhite:
\r
7903 nowChecked = IDM_MachineWhite;
\r
7905 case TwoMachinesPlay:
\r
7906 nowChecked = matchMode ? IDM_Match : IDM_TwoMachines; // [HGM] match
\r
7909 nowChecked = IDM_AnalysisMode;
\r
7912 nowChecked = IDM_AnalyzeFile;
\r
7915 nowChecked = IDM_EditGame;
\r
7917 case PlayFromGameFile:
\r
7918 nowChecked = IDM_LoadGame;
\r
7920 case EditPosition:
\r
7921 nowChecked = IDM_EditPosition;
\r
7924 nowChecked = IDM_Training;
\r
7926 case IcsPlayingWhite:
\r
7927 case IcsPlayingBlack:
\r
7928 case IcsObserving:
\r
7930 nowChecked = IDM_IcsClient;
\r
7937 if (prevChecked != 0)
\r
7938 (void) CheckMenuItem(GetMenu(hwndMain),
\r
7939 prevChecked, MF_BYCOMMAND|MF_UNCHECKED);
\r
7940 if (nowChecked != 0)
\r
7941 (void) CheckMenuItem(GetMenu(hwndMain),
\r
7942 nowChecked, MF_BYCOMMAND|MF_CHECKED);
\r
7944 if (nowChecked == IDM_LoadGame || nowChecked == IDM_Training) {
\r
7945 (void) EnableMenuItem(GetMenu(hwndMain), IDM_Training,
\r
7946 MF_BYCOMMAND|MF_ENABLED);
\r
7948 (void) EnableMenuItem(GetMenu(hwndMain),
\r
7949 IDM_Training, MF_BYCOMMAND|MF_GRAYED);
\r
7952 prevChecked = nowChecked;
\r
7954 /* [DM] icsEngineAnalyze - Do a sceure check too */
\r
7955 if (appData.icsActive) {
\r
7956 if (appData.icsEngineAnalyze) {
\r
7957 (void) CheckMenuItem(GetMenu(hwndMain), IDM_AnalysisMode,
\r
7958 MF_BYCOMMAND|MF_CHECKED);
\r
7960 (void) CheckMenuItem(GetMenu(hwndMain), IDM_AnalysisMode,
\r
7961 MF_BYCOMMAND|MF_UNCHECKED);
\r
7969 HMENU hmenu = GetMenu(hwndMain);
\r
7970 SetMenuEnables(hmenu, icsEnables);
\r
7971 EnableMenuItem(GetSubMenu(hmenu, OPTIONS_POS), ICS_POS,
\r
7972 MF_BYPOSITION|MF_ENABLED);
\r
7974 if (appData.zippyPlay) {
\r
7975 SetMenuEnables(hmenu, zippyEnables);
\r
7976 if (!appData.noChessProgram) /* [DM] icsEngineAnalyze */
\r
7977 (void) EnableMenuItem(GetMenu(hwndMain), IDM_AnalysisMode,
\r
7978 MF_BYCOMMAND|MF_ENABLED);
\r
7986 SetMenuEnables(GetMenu(hwndMain), gnuEnables);
\r
7992 HMENU hmenu = GetMenu(hwndMain);
\r
7993 SetMenuEnables(hmenu, ncpEnables);
\r
7994 EnableMenuItem(GetSubMenu(hmenu, OPTIONS_POS), SOUNDS_POS,
\r
7995 MF_BYPOSITION|MF_GRAYED);
\r
7996 DrawMenuBar(hwndMain);
\r
8002 SetMenuEnables(GetMenu(hwndMain), cmailEnables);
\r
8006 SetTrainingModeOn()
\r
8009 SetMenuEnables(GetMenu(hwndMain), trainingOnEnables);
\r
8010 for (i = 0; i < N_BUTTONS; i++) {
\r
8011 if (buttonDesc[i].hwnd != NULL)
\r
8012 EnableWindow(buttonDesc[i].hwnd, FALSE);
\r
8017 VOID SetTrainingModeOff()
\r
8020 SetMenuEnables(GetMenu(hwndMain), trainingOffEnables);
\r
8021 for (i = 0; i < N_BUTTONS; i++) {
\r
8022 if (buttonDesc[i].hwnd != NULL)
\r
8023 EnableWindow(buttonDesc[i].hwnd, TRUE);
\r
8029 SetUserThinkingEnables()
\r
8031 SetMenuEnables(GetMenu(hwndMain), userThinkingEnables);
\r
8035 SetMachineThinkingEnables()
\r
8037 HMENU hMenu = GetMenu(hwndMain);
\r
8038 int flags = MF_BYCOMMAND|MF_ENABLED;
\r
8040 SetMenuEnables(hMenu, machineThinkingEnables);
\r
8042 if (gameMode == MachinePlaysBlack) {
\r
8043 (void)EnableMenuItem(hMenu, IDM_MachineBlack, flags);
\r
8044 } else if (gameMode == MachinePlaysWhite) {
\r
8045 (void)EnableMenuItem(hMenu, IDM_MachineWhite, flags);
\r
8046 } else if (gameMode == TwoMachinesPlay) {
\r
8047 (void)EnableMenuItem(hMenu, matchMode ? IDM_Match : IDM_TwoMachines, flags); // [HGM] match
\r
8053 DisplayTitle(char *str)
\r
8055 char title[MSG_SIZ], *host;
\r
8056 if (str[0] != NULLCHAR) {
\r
8057 safeStrCpy(title, str, sizeof(title)/sizeof(title[0]) );
\r
8058 } else if (appData.icsActive) {
\r
8059 if (appData.icsCommPort[0] != NULLCHAR)
\r
8062 host = appData.icsHost;
\r
8063 snprintf(title, MSG_SIZ, "%s: %s", szTitle, host);
\r
8064 } else if (appData.noChessProgram) {
\r
8065 safeStrCpy(title, szTitle, sizeof(title)/sizeof(title[0]) );
\r
8067 safeStrCpy(title, szTitle, sizeof(title)/sizeof(title[0]) );
\r
8068 strcat(title, ": ");
\r
8069 strcat(title, first.tidy);
\r
8071 SetWindowText(hwndMain, title);
\r
8076 DisplayMessage(char *str1, char *str2)
\r
8080 int remain = MESSAGE_TEXT_MAX - 1;
\r
8083 moveErrorMessageUp = FALSE; /* turned on later by caller if needed */
\r
8084 messageText[0] = NULLCHAR;
\r
8086 len = strlen(str1);
\r
8087 if (len > remain) len = remain;
\r
8088 strncpy(messageText, str1, len);
\r
8089 messageText[len] = NULLCHAR;
\r
8092 if (*str2 && remain >= 2) {
\r
8094 strcat(messageText, " ");
\r
8097 len = strlen(str2);
\r
8098 if (len > remain) len = remain;
\r
8099 strncat(messageText, str2, len);
\r
8101 messageText[MESSAGE_TEXT_MAX - 1] = NULLCHAR;
\r
8103 if (hwndMain == NULL || IsIconic(hwndMain)) return;
\r
8107 hdc = GetDC(hwndMain);
\r
8108 oldFont = SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);
\r
8109 ExtTextOut(hdc, messageRect.left, messageRect.top, ETO_CLIPPED|ETO_OPAQUE,
\r
8110 &messageRect, messageText, strlen(messageText), NULL);
\r
8111 (void) SelectObject(hdc, oldFont);
\r
8112 (void) ReleaseDC(hwndMain, hdc);
\r
8116 DisplayError(char *str, int error)
\r
8118 char buf[MSG_SIZ*2], buf2[MSG_SIZ];
\r
8122 safeStrCpy(buf, str, sizeof(buf)/sizeof(buf[0]) );
\r
8124 len = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,
\r
8125 NULL, error, LANG_NEUTRAL,
\r
8126 (LPSTR) buf2, MSG_SIZ, NULL);
\r
8128 snprintf(buf, 2*MSG_SIZ, "%s:\n%s", str, buf2);
\r
8130 ErrorMap *em = errmap;
\r
8131 while (em->err != 0 && em->err != error) em++;
\r
8132 if (em->err != 0) {
\r
8133 snprintf(buf, 2*MSG_SIZ, "%s:\n%s", str, em->msg);
\r
8135 snprintf(buf, 2*MSG_SIZ, "%s:\nError code %d", str, error);
\r
8140 ErrorPopUp(_("Error"), buf);
\r
8145 DisplayMoveError(char *str)
\r
8147 fromX = fromY = -1;
\r
8148 ClearHighlights();
\r
8149 DrawPosition(FALSE, NULL);
\r
8150 if (appData.popupMoveErrors) {
\r
8151 ErrorPopUp(_("Error"), str);
\r
8153 DisplayMessage(str, "");
\r
8154 moveErrorMessageUp = TRUE;
\r
8159 DisplayFatalError(char *str, int error, int exitStatus)
\r
8161 char buf[2*MSG_SIZ], buf2[MSG_SIZ];
\r
8163 char *label = exitStatus ? _("Fatal Error") : _("Exiting");
\r
8166 len = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,
\r
8167 NULL, error, LANG_NEUTRAL,
\r
8168 (LPSTR) buf2, MSG_SIZ, NULL);
\r
8170 snprintf(buf, 2*MSG_SIZ, "%s:\n%s", str, buf2);
\r
8172 ErrorMap *em = errmap;
\r
8173 while (em->err != 0 && em->err != error) em++;
\r
8174 if (em->err != 0) {
\r
8175 snprintf(buf, 2*MSG_SIZ, "%s:\n%s", str, em->msg);
\r
8177 snprintf(buf, 2*MSG_SIZ, "%s:\nError code %d", str, error);
\r
8182 if (appData.debugMode) {
\r
8183 fprintf(debugFP, "%s: %s\n", label, str);
\r
8185 if (appData.popupExitMessage) {
\r
8186 (void) MessageBox(hwndMain, str, label, MB_OK|
\r
8187 (exitStatus ? MB_ICONSTOP : MB_ICONINFORMATION));
\r
8189 ExitEvent(exitStatus);
\r
8194 DisplayInformation(char *str)
\r
8196 (void) MessageBox(hwndMain, str, _("Information"), MB_OK|MB_ICONINFORMATION);
\r
8201 DisplayNote(char *str)
\r
8203 ErrorPopUp(_("Note"), str);
\r
8208 char *title, *question, *replyPrefix;
\r
8213 QuestionDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
8215 static QuestionParams *qp;
\r
8216 char reply[MSG_SIZ];
\r
8219 switch (message) {
\r
8220 case WM_INITDIALOG:
\r
8221 qp = (QuestionParams *) lParam;
\r
8222 CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));
\r
8223 Translate(hDlg, DLG_Question);
\r
8224 SetWindowText(hDlg, qp->title);
\r
8225 SetDlgItemText(hDlg, OPT_QuestionText, qp->question);
\r
8226 SetFocus(GetDlgItem(hDlg, OPT_QuestionInput));
\r
8230 switch (LOWORD(wParam)) {
\r
8232 safeStrCpy(reply, qp->replyPrefix, sizeof(reply)/sizeof(reply[0]) );
\r
8233 if (*reply) strcat(reply, " ");
\r
8234 len = strlen(reply);
\r
8235 GetDlgItemText(hDlg, OPT_QuestionInput, reply + len, sizeof(reply) - len);
\r
8236 strcat(reply, "\n");
\r
8237 OutputToProcess(qp->pr, reply, strlen(reply), &err);
\r
8238 EndDialog(hDlg, TRUE);
\r
8239 if (err) DisplayFatalError(_("Error writing to chess program"), err, 1);
\r
8242 EndDialog(hDlg, FALSE);
\r
8253 AskQuestion(char* title, char *question, char *replyPrefix, ProcRef pr)
\r
8255 QuestionParams qp;
\r
8259 qp.question = question;
\r
8260 qp.replyPrefix = replyPrefix;
\r
8262 lpProc = MakeProcInstance((FARPROC)QuestionDialog, hInst);
\r
8263 DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_Question),
\r
8264 hwndMain, (DLGPROC)lpProc, (LPARAM)&qp);
\r
8265 FreeProcInstance(lpProc);
\r
8268 /* [AS] Pick FRC position */
\r
8269 LRESULT CALLBACK NewGameFRC_Proc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
8271 static int * lpIndexFRC;
\r
8277 case WM_INITDIALOG:
\r
8278 lpIndexFRC = (int *) lParam;
\r
8280 CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));
\r
8281 Translate(hDlg, DLG_NewGameFRC);
\r
8283 SendDlgItemMessage( hDlg, IDC_NFG_Edit, EM_SETLIMITTEXT, sizeof(buf)-1, 0 );
\r
8284 SetDlgItemInt( hDlg, IDC_NFG_Edit, *lpIndexFRC, TRUE );
\r
8285 SendDlgItemMessage( hDlg, IDC_NFG_Edit, EM_SETSEL, 0, -1 );
\r
8286 SetFocus(GetDlgItem(hDlg, IDC_NFG_Edit));
\r
8291 switch( LOWORD(wParam) ) {
\r
8293 *lpIndexFRC = GetDlgItemInt(hDlg, IDC_NFG_Edit, &index_is_ok, TRUE );
\r
8294 EndDialog( hDlg, 0 );
\r
8295 shuffleOpenings = TRUE; /* [HGM] shuffle: switch shuffling on for as long as we stay in current variant */
\r
8298 EndDialog( hDlg, 1 );
\r
8300 case IDC_NFG_Edit:
\r
8301 if( HIWORD(wParam) == EN_CHANGE ) {
\r
8302 GetDlgItemInt(hDlg, IDC_NFG_Edit, &index_is_ok, TRUE );
\r
8304 EnableWindow( GetDlgItem(hDlg, IDOK), index_is_ok );
\r
8307 case IDC_NFG_Random:
\r
8308 snprintf( buf, sizeof(buf)/sizeof(buf[0]), "%d", myrandom() ); /* [HGM] shuffle: no longer limit to 960 */
\r
8309 SetDlgItemText(hDlg, IDC_NFG_Edit, buf );
\r
8322 int index = appData.defaultFrcPosition;
\r
8323 FARPROC lpProc = MakeProcInstance( (FARPROC) NewGameFRC_Proc, hInst );
\r
8325 result = DialogBoxParam( hInst, MAKEINTRESOURCE(DLG_NewGameFRC), hwndMain, (DLGPROC)lpProc, (LPARAM)&index );
\r
8327 if( result == 0 ) {
\r
8328 appData.defaultFrcPosition = index;
\r
8334 /* [AS] Game list options. Refactored by HGM */
\r
8336 HWND gameListOptionsDialog;
\r
8338 // low-level front-end: clear text edit / list widget
\r
8342 SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_RESETCONTENT, 0, 0 );
\r
8345 // low-level front-end: clear text edit / list widget
\r
8347 GLT_DeSelectList()
\r
8349 SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_SETCURSEL, 0, 0 );
\r
8352 // low-level front-end: append line to text edit / list widget
\r
8354 GLT_AddToList( char *name )
\r
8357 SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_ADDSTRING, 0, (LPARAM) name );
\r
8361 // low-level front-end: get line from text edit / list widget
\r
8363 GLT_GetFromList( int index, char *name )
\r
8366 if( SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_GETTEXT, index, (LPARAM) name ) != LB_ERR )
\r
8372 void GLT_MoveSelection( HWND hDlg, int delta )
\r
8374 int idx1 = (int) SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETCURSEL, 0, 0 );
\r
8375 int idx2 = idx1 + delta;
\r
8376 int count = (int) SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETCOUNT, 0, 0 );
\r
8378 if( idx1 >=0 && idx1 < count && idx2 >= 0 && idx2 < count ) {
\r
8381 SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETTEXT, idx1, (LPARAM) buf );
\r
8382 SendDlgItemMessage( hDlg, IDC_GameListTags, LB_DELETESTRING, idx1, 0 );
\r
8383 SendDlgItemMessage( hDlg, IDC_GameListTags, LB_INSERTSTRING, idx2, (LPARAM) buf );
\r
8384 SendDlgItemMessage( hDlg, IDC_GameListTags, LB_SETCURSEL, idx2, 0 );
\r
8388 LRESULT CALLBACK GameListOptions_Proc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
8392 case WM_INITDIALOG:
\r
8393 gameListOptionsDialog = hDlg; // [HGM] pass through global to keep out off back-end
\r
8395 CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));
\r
8396 Translate(hDlg, DLG_GameListOptions);
\r
8398 /* Initialize list */
\r
8399 GLT_TagsToList( lpUserGLT );
\r
8401 SetFocus( GetDlgItem(hDlg, IDC_GameListTags) );
\r
8406 switch( LOWORD(wParam) ) {
\r
8409 EndDialog( hDlg, 0 );
\r
8412 EndDialog( hDlg, 1 );
\r
8415 case IDC_GLT_Default:
\r
8416 GLT_TagsToList( GLT_DEFAULT_TAGS );
\r
8419 case IDC_GLT_Restore:
\r
8420 GLT_TagsToList( appData.gameListTags );
\r
8424 GLT_MoveSelection( hDlg, -1 );
\r
8427 case IDC_GLT_Down:
\r
8428 GLT_MoveSelection( hDlg, +1 );
\r
8438 int GameListOptions()
\r
8441 FARPROC lpProc = MakeProcInstance( (FARPROC) GameListOptions_Proc, hInst );
\r
8443 safeStrCpy( lpUserGLT, appData.gameListTags ,LPUSERGLT_SIZE );
\r
8445 result = DialogBoxParam( hInst, MAKEINTRESOURCE(DLG_GameListOptions), hwndMain, (DLGPROC)lpProc, (LPARAM)lpUserGLT );
\r
8447 if( result == 0 ) {
\r
8448 /* [AS] Memory leak here! */
\r
8449 appData.gameListTags = strdup( lpUserGLT );
\r
8456 DisplayIcsInteractionTitle(char *str)
\r
8458 char consoleTitle[MSG_SIZ];
\r
8460 snprintf(consoleTitle, MSG_SIZ, "%s: %s", szConsoleTitle, str);
\r
8461 SetWindowText(hwndConsole, consoleTitle);
\r
8465 DrawPosition(int fullRedraw, Board board)
\r
8467 HDCDrawPosition(NULL, (BOOLEAN) fullRedraw, board);
\r
8470 void NotifyFrontendLogin()
\r
8473 UpdateICSWidth(GetDlgItem(hwndConsole, OPT_ConsoleText));
\r
8479 fromX = fromY = -1;
\r
8480 if (dragInfo.pos.x != -1 || dragInfo.pos.y != -1) {
\r
8481 dragInfo.pos.x = dragInfo.pos.y = -1;
\r
8482 dragInfo.pos.x = dragInfo.pos.y = -1;
\r
8483 dragInfo.lastpos = dragInfo.pos;
\r
8484 dragInfo.start.x = dragInfo.start.y = -1;
\r
8485 dragInfo.from = dragInfo.start;
\r
8487 DrawPosition(TRUE, NULL);
\r
8494 CommentPopUp(char *title, char *str)
\r
8496 HWND hwnd = GetActiveWindow();
\r
8497 EitherCommentPopUp(currentMove, title, str, FALSE); // [HGM] vari: fake move index, rather than 0
\r
8499 SetActiveWindow(hwnd);
\r
8503 CommentPopDown(void)
\r
8505 CheckMenuItem(GetMenu(hwndMain), IDM_EditComment, MF_UNCHECKED);
\r
8506 if (commentDialog) {
\r
8507 ShowWindow(commentDialog, SW_HIDE);
\r
8509 commentUp = FALSE;
\r
8513 EditCommentPopUp(int index, char *title, char *str)
\r
8515 EitherCommentPopUp(index, title, str, TRUE);
\r
8522 MyPlaySound(&sounds[(int)SoundMove]);
\r
8525 VOID PlayIcsWinSound()
\r
8527 MyPlaySound(&sounds[(int)SoundIcsWin]);
\r
8530 VOID PlayIcsLossSound()
\r
8532 MyPlaySound(&sounds[(int)SoundIcsLoss]);
\r
8535 VOID PlayIcsDrawSound()
\r
8537 MyPlaySound(&sounds[(int)SoundIcsDraw]);
\r
8540 VOID PlayIcsUnfinishedSound()
\r
8542 MyPlaySound(&sounds[(int)SoundIcsUnfinished]);
\r
8548 MyPlaySound(&sounds[(int)SoundAlarm]);
\r
8556 consoleEcho = TRUE;
\r
8557 hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
8558 SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&consoleCF);
\r
8559 SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);
\r
8568 consoleEcho = FALSE;
\r
8569 hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
8570 /* This works OK: set text and background both to the same color */
\r
8572 cf.crTextColor = COLOR_ECHOOFF;
\r
8573 SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf);
\r
8574 SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, cf.crTextColor);
\r
8577 /* No Raw()...? */
\r
8579 void Colorize(ColorClass cc, int continuation)
\r
8581 currentColorClass = cc;
\r
8582 consoleCF.dwMask = CFM_COLOR|CFM_BOLD|CFM_ITALIC|CFM_UNDERLINE|CFM_STRIKEOUT;
\r
8583 consoleCF.crTextColor = textAttribs[cc].color;
\r
8584 consoleCF.dwEffects = textAttribs[cc].effects;
\r
8585 if (!continuation) MyPlaySound(&textAttribs[cc].sound);
\r
8591 static char buf[MSG_SIZ];
\r
8592 DWORD bufsiz = MSG_SIZ;
\r
8594 if(appData.userName != NULL && appData.userName[0] != 0) {
\r
8595 return appData.userName; /* [HGM] username: prefer name selected by user over his system login */
\r
8597 if (!GetUserName(buf, &bufsiz)) {
\r
8598 /*DisplayError("Error getting user name", GetLastError());*/
\r
8599 safeStrCpy(buf, _("User"), sizeof(buf)/sizeof(buf[0]) );
\r
8607 static char buf[MSG_SIZ];
\r
8608 DWORD bufsiz = MSG_SIZ;
\r
8610 if (!GetComputerName(buf, &bufsiz)) {
\r
8611 /*DisplayError("Error getting host name", GetLastError());*/
\r
8612 safeStrCpy(buf, _("Unknown"), sizeof(buf)/sizeof(buf[0]) );
\r
8619 ClockTimerRunning()
\r
8621 return clockTimerEvent != 0;
\r
8627 if (clockTimerEvent == 0) return FALSE;
\r
8628 KillTimer(hwndMain, clockTimerEvent);
\r
8629 clockTimerEvent = 0;
\r
8634 StartClockTimer(long millisec)
\r
8636 clockTimerEvent = SetTimer(hwndMain, (UINT) CLOCK_TIMER_ID,
\r
8637 (UINT) millisec, NULL);
\r
8641 DisplayWhiteClock(long timeRemaining, int highlight)
\r
8644 char *flag = whiteFlag && gameMode == TwoMachinesPlay ? "(!)" : "";
\r
8646 if(appData.noGUI) return;
\r
8647 hdc = GetDC(hwndMain);
\r
8648 if (!IsIconic(hwndMain)) {
\r
8649 DisplayAClock(hdc, timeRemaining, highlight,
\r
8650 flipClock ? &blackRect : &whiteRect, _("White"), flag);
\r
8652 if (highlight && iconCurrent == iconBlack) {
\r
8653 iconCurrent = iconWhite;
\r
8654 PostMessage(hwndMain, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);
\r
8655 if (IsIconic(hwndMain)) {
\r
8656 DrawIcon(hdc, 2, 2, iconCurrent);
\r
8659 (void) ReleaseDC(hwndMain, hdc);
\r
8661 PostMessage(hwndConsole, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);
\r
8665 DisplayBlackClock(long timeRemaining, int highlight)
\r
8668 char *flag = blackFlag && gameMode == TwoMachinesPlay ? "(!)" : "";
\r
8670 if(appData.noGUI) return;
\r
8671 hdc = GetDC(hwndMain);
\r
8672 if (!IsIconic(hwndMain)) {
\r
8673 DisplayAClock(hdc, timeRemaining, highlight,
\r
8674 flipClock ? &whiteRect : &blackRect, _("Black"), flag);
\r
8676 if (highlight && iconCurrent == iconWhite) {
\r
8677 iconCurrent = iconBlack;
\r
8678 PostMessage(hwndMain, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);
\r
8679 if (IsIconic(hwndMain)) {
\r
8680 DrawIcon(hdc, 2, 2, iconCurrent);
\r
8683 (void) ReleaseDC(hwndMain, hdc);
\r
8685 PostMessage(hwndConsole, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);
\r
8690 LoadGameTimerRunning()
\r
8692 return loadGameTimerEvent != 0;
\r
8696 StopLoadGameTimer()
\r
8698 if (loadGameTimerEvent == 0) return FALSE;
\r
8699 KillTimer(hwndMain, loadGameTimerEvent);
\r
8700 loadGameTimerEvent = 0;
\r
8705 StartLoadGameTimer(long millisec)
\r
8707 loadGameTimerEvent = SetTimer(hwndMain, (UINT) LOAD_GAME_TIMER_ID,
\r
8708 (UINT) millisec, NULL);
\r
8716 char fileTitle[MSG_SIZ];
\r
8718 defName = DefaultFileName(appData.oldSaveStyle ? "gam" : "pgn");
\r
8719 f = OpenFileDialog(hwndMain, "a", defName,
\r
8720 appData.oldSaveStyle ? "gam" : "pgn",
\r
8722 _("Save Game to File"), NULL, fileTitle, NULL);
\r
8724 SaveGame(f, 0, "");
\r
8731 ScheduleDelayedEvent(DelayedEventCallback cb, long millisec)
\r
8733 if (delayedTimerEvent != 0) {
\r
8734 if (appData.debugMode && cb != delayedTimerCallback) { // [HGM] alive: not too much debug
\r
8735 fprintf(debugFP, "ScheduleDelayedEvent: event already scheduled\n");
\r
8737 KillTimer(hwndMain, delayedTimerEvent);
\r
8738 delayedTimerEvent = 0;
\r
8739 if(delayedTimerCallback != cb) // [HGM] alive: do not "flush" same event, just postpone it
\r
8740 delayedTimerCallback();
\r
8742 delayedTimerCallback = cb;
\r
8743 delayedTimerEvent = SetTimer(hwndMain, (UINT) DELAYED_TIMER_ID,
\r
8744 (UINT) millisec, NULL);
\r
8747 DelayedEventCallback
\r
8750 if (delayedTimerEvent) {
\r
8751 return delayedTimerCallback;
\r
8758 CancelDelayedEvent()
\r
8760 if (delayedTimerEvent) {
\r
8761 KillTimer(hwndMain, delayedTimerEvent);
\r
8762 delayedTimerEvent = 0;
\r
8766 DWORD GetWin32Priority(int nice)
\r
8767 { // [HGM] nice: translate Unix nice() value to indows priority class. (Code stolen from Polyglot 1.4w11)
\r
8769 REALTIME_PRIORITY_CLASS 0x00000100
\r
8770 HIGH_PRIORITY_CLASS 0x00000080
\r
8771 ABOVE_NORMAL_PRIORITY_CLASS 0x00008000
\r
8772 NORMAL_PRIORITY_CLASS 0x00000020
\r
8773 BELOW_NORMAL_PRIORITY_CLASS 0x00004000
\r
8774 IDLE_PRIORITY_CLASS 0x00000040
\r
8776 if (nice < -15) return 0x00000080;
\r
8777 if (nice < 0) return 0x00008000;
\r
8778 if (nice == 0) return 0x00000020;
\r
8779 if (nice < 15) return 0x00004000;
\r
8780 return 0x00000040;
\r
8783 /* Start a child process running the given program.
\r
8784 The process's standard output can be read from "from", and its
\r
8785 standard input can be written to "to".
\r
8786 Exit with fatal error if anything goes wrong.
\r
8787 Returns an opaque pointer that can be used to destroy the process
\r
8791 StartChildProcess(char *cmdLine, char *dir, ProcRef *pr)
\r
8793 #define BUFSIZE 4096
\r
8795 HANDLE hChildStdinRd, hChildStdinWr,
\r
8796 hChildStdoutRd, hChildStdoutWr;
\r
8797 HANDLE hChildStdinWrDup, hChildStdoutRdDup;
\r
8798 SECURITY_ATTRIBUTES saAttr;
\r
8800 PROCESS_INFORMATION piProcInfo;
\r
8801 STARTUPINFO siStartInfo;
\r
8803 char buf[MSG_SIZ];
\r
8806 if (appData.debugMode) {
\r
8807 fprintf(debugFP, "StartChildProcess (dir=\"%s\") %s\n", dir, cmdLine);
\r
8812 /* Set the bInheritHandle flag so pipe handles are inherited. */
\r
8813 saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);
\r
8814 saAttr.bInheritHandle = TRUE;
\r
8815 saAttr.lpSecurityDescriptor = NULL;
\r
8818 * The steps for redirecting child's STDOUT:
\r
8819 * 1. Create anonymous pipe to be STDOUT for child.
\r
8820 * 2. Create a noninheritable duplicate of read handle,
\r
8821 * and close the inheritable read handle.
\r
8824 /* Create a pipe for the child's STDOUT. */
\r
8825 if (! CreatePipe(&hChildStdoutRd, &hChildStdoutWr, &saAttr, 0)) {
\r
8826 return GetLastError();
\r
8829 /* Duplicate the read handle to the pipe, so it is not inherited. */
\r
8830 fSuccess = DuplicateHandle(GetCurrentProcess(), hChildStdoutRd,
\r
8831 GetCurrentProcess(), &hChildStdoutRdDup, 0,
\r
8832 FALSE, /* not inherited */
\r
8833 DUPLICATE_SAME_ACCESS);
\r
8835 return GetLastError();
\r
8837 CloseHandle(hChildStdoutRd);
\r
8840 * The steps for redirecting child's STDIN:
\r
8841 * 1. Create anonymous pipe to be STDIN for child.
\r
8842 * 2. Create a noninheritable duplicate of write handle,
\r
8843 * and close the inheritable write handle.
\r
8846 /* Create a pipe for the child's STDIN. */
\r
8847 if (! CreatePipe(&hChildStdinRd, &hChildStdinWr, &saAttr, 0)) {
\r
8848 return GetLastError();
\r
8851 /* Duplicate the write handle to the pipe, so it is not inherited. */
\r
8852 fSuccess = DuplicateHandle(GetCurrentProcess(), hChildStdinWr,
\r
8853 GetCurrentProcess(), &hChildStdinWrDup, 0,
\r
8854 FALSE, /* not inherited */
\r
8855 DUPLICATE_SAME_ACCESS);
\r
8857 return GetLastError();
\r
8859 CloseHandle(hChildStdinWr);
\r
8861 /* Arrange to (1) look in dir for the child .exe file, and
\r
8862 * (2) have dir be the child's working directory. Interpret
\r
8863 * dir relative to the directory WinBoard loaded from. */
\r
8864 GetCurrentDirectory(MSG_SIZ, buf);
\r
8865 SetCurrentDirectory(installDir);
\r
8866 SetCurrentDirectory(dir);
\r
8868 /* Now create the child process. */
\r
8870 siStartInfo.cb = sizeof(STARTUPINFO);
\r
8871 siStartInfo.lpReserved = NULL;
\r
8872 siStartInfo.lpDesktop = NULL;
\r
8873 siStartInfo.lpTitle = NULL;
\r
8874 siStartInfo.dwFlags = STARTF_USESTDHANDLES;
\r
8875 siStartInfo.cbReserved2 = 0;
\r
8876 siStartInfo.lpReserved2 = NULL;
\r
8877 siStartInfo.hStdInput = hChildStdinRd;
\r
8878 siStartInfo.hStdOutput = hChildStdoutWr;
\r
8879 siStartInfo.hStdError = hChildStdoutWr;
\r
8881 fSuccess = CreateProcess(NULL,
\r
8882 cmdLine, /* command line */
\r
8883 NULL, /* process security attributes */
\r
8884 NULL, /* primary thread security attrs */
\r
8885 TRUE, /* handles are inherited */
\r
8886 DETACHED_PROCESS|CREATE_NEW_PROCESS_GROUP,
\r
8887 NULL, /* use parent's environment */
\r
8889 &siStartInfo, /* STARTUPINFO pointer */
\r
8890 &piProcInfo); /* receives PROCESS_INFORMATION */
\r
8892 err = GetLastError();
\r
8893 SetCurrentDirectory(buf); /* return to prev directory */
\r
8898 if (appData.niceEngines){ // [HGM] nice: adjust engine proc priority
\r
8899 if(appData.debugMode) fprintf(debugFP, "nice engine proc to %d\n", appData.niceEngines);
\r
8900 SetPriorityClass(piProcInfo.hProcess, GetWin32Priority(appData.niceEngines));
\r
8903 /* Close the handles we don't need in the parent */
\r
8904 CloseHandle(piProcInfo.hThread);
\r
8905 CloseHandle(hChildStdinRd);
\r
8906 CloseHandle(hChildStdoutWr);
\r
8908 /* Prepare return value */
\r
8909 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
\r
8910 cp->kind = CPReal;
\r
8911 cp->hProcess = piProcInfo.hProcess;
\r
8912 cp->pid = piProcInfo.dwProcessId;
\r
8913 cp->hFrom = hChildStdoutRdDup;
\r
8914 cp->hTo = hChildStdinWrDup;
\r
8916 *pr = (void *) cp;
\r
8918 /* Klaus Friedel says that this Sleep solves a problem under Windows
\r
8919 2000 where engines sometimes don't see the initial command(s)
\r
8920 from WinBoard and hang. I don't understand how that can happen,
\r
8921 but the Sleep is harmless, so I've put it in. Others have also
\r
8922 reported what may be the same problem, so hopefully this will fix
\r
8923 it for them too. */
\r
8931 DestroyChildProcess(ProcRef pr, int/*boolean*/ signal)
\r
8933 ChildProc *cp; int result;
\r
8935 cp = (ChildProc *) pr;
\r
8936 if (cp == NULL) return;
\r
8938 switch (cp->kind) {
\r
8940 /* TerminateProcess is considered harmful, so... */
\r
8941 CloseHandle(cp->hTo); /* Closing this will give the child an EOF and hopefully kill it */
\r
8942 if (cp->hFrom) CloseHandle(cp->hFrom); /* if NULL, InputThread will close it */
\r
8943 /* The following doesn't work because the chess program
\r
8944 doesn't "have the same console" as WinBoard. Maybe
\r
8945 we could arrange for this even though neither WinBoard
\r
8946 nor the chess program uses a console for stdio? */
\r
8947 /*!!if (signal) GenerateConsoleCtrlEvent(CTRL_BREAK_EVENT, cp->pid);*/
\r
8949 /* [AS] Special termination modes for misbehaving programs... */
\r
8950 if( signal == 9 ) {
\r
8951 result = TerminateProcess( cp->hProcess, 0 );
\r
8953 if ( appData.debugMode) {
\r
8954 fprintf( debugFP, "Terminating process %lu, result=%d\n", cp->pid, result );
\r
8957 else if( signal == 10 ) {
\r
8958 DWORD dw = WaitForSingleObject( cp->hProcess, 3*1000 ); // Wait 3 seconds at most
\r
8960 if( dw != WAIT_OBJECT_0 ) {
\r
8961 result = TerminateProcess( cp->hProcess, 0 );
\r
8963 if ( appData.debugMode) {
\r
8964 fprintf( debugFP, "Process %lu still alive after timeout, killing... result=%d\n", cp->pid, result );
\r
8970 CloseHandle(cp->hProcess);
\r
8974 if (cp->hFrom) CloseHandle(cp->hFrom);
\r
8978 closesocket(cp->sock);
\r
8983 if (signal) send(cp->sock2, "\017", 1, 0); /* 017 = 15 = SIGTERM */
\r
8984 closesocket(cp->sock);
\r
8985 closesocket(cp->sock2);
\r
8993 InterruptChildProcess(ProcRef pr)
\r
8997 cp = (ChildProc *) pr;
\r
8998 if (cp == NULL) return;
\r
8999 switch (cp->kind) {
\r
9001 /* The following doesn't work because the chess program
\r
9002 doesn't "have the same console" as WinBoard. Maybe
\r
9003 we could arrange for this even though neither WinBoard
\r
9004 nor the chess program uses a console for stdio */
\r
9005 /*!!GenerateConsoleCtrlEvent(CTRL_C_EVENT, cp->pid);*/
\r
9010 /* Can't interrupt */
\r
9014 send(cp->sock2, "\002", 1, 0); /* 2 = SIGINT */
\r
9021 OpenTelnet(char *host, char *port, ProcRef *pr)
\r
9023 char cmdLine[MSG_SIZ];
\r
9025 if (port[0] == NULLCHAR) {
\r
9026 snprintf(cmdLine, MSG_SIZ, "%s %s", appData.telnetProgram, host);
\r
9028 snprintf(cmdLine, MSG_SIZ, "%s %s %s", appData.telnetProgram, host, port);
\r
9030 return StartChildProcess(cmdLine, "", pr);
\r
9034 /* Code to open TCP sockets */
\r
9037 OpenTCP(char *host, char *port, ProcRef *pr)
\r
9042 struct sockaddr_in sa, mysa;
\r
9043 struct hostent FAR *hp;
\r
9044 unsigned short uport;
\r
9045 WORD wVersionRequested;
\r
9048 /* Initialize socket DLL */
\r
9049 wVersionRequested = MAKEWORD(1, 1);
\r
9050 err = WSAStartup(wVersionRequested, &wsaData);
\r
9051 if (err != 0) return err;
\r
9054 if ((s = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {
\r
9055 err = WSAGetLastError();
\r
9060 /* Bind local address using (mostly) don't-care values.
\r
9062 memset((char *) &mysa, 0, sizeof(struct sockaddr_in));
\r
9063 mysa.sin_family = AF_INET;
\r
9064 mysa.sin_addr.s_addr = INADDR_ANY;
\r
9065 uport = (unsigned short) 0;
\r
9066 mysa.sin_port = htons(uport);
\r
9067 if (bind(s, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))
\r
9068 == SOCKET_ERROR) {
\r
9069 err = WSAGetLastError();
\r
9074 /* Resolve remote host name */
\r
9075 memset((char *) &sa, 0, sizeof(struct sockaddr_in));
\r
9076 if (!(hp = gethostbyname(host))) {
\r
9077 unsigned int b0, b1, b2, b3;
\r
9079 err = WSAGetLastError();
\r
9081 if (sscanf(host, "%u.%u.%u.%u", &b0, &b1, &b2, &b3) == 4) {
\r
9082 hp = (struct hostent *) calloc(1, sizeof(struct hostent));
\r
9083 hp->h_addrtype = AF_INET;
\r
9085 hp->h_addr_list = (char **) calloc(2, sizeof(char *));
\r
9086 hp->h_addr_list[0] = (char *) malloc(4);
\r
9087 hp->h_addr_list[0][0] = (char) b0;
\r
9088 hp->h_addr_list[0][1] = (char) b1;
\r
9089 hp->h_addr_list[0][2] = (char) b2;
\r
9090 hp->h_addr_list[0][3] = (char) b3;
\r
9096 sa.sin_family = hp->h_addrtype;
\r
9097 uport = (unsigned short) atoi(port);
\r
9098 sa.sin_port = htons(uport);
\r
9099 memcpy((char *) &sa.sin_addr, hp->h_addr, hp->h_length);
\r
9101 /* Make connection */
\r
9102 if (connect(s, (struct sockaddr *) &sa,
\r
9103 sizeof(struct sockaddr_in)) == SOCKET_ERROR) {
\r
9104 err = WSAGetLastError();
\r
9109 /* Prepare return value */
\r
9110 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
\r
9111 cp->kind = CPSock;
\r
9113 *pr = (ProcRef *) cp;
\r
9119 OpenCommPort(char *name, ProcRef *pr)
\r
9124 char fullname[MSG_SIZ];
\r
9126 if (*name != '\\')
\r
9127 snprintf(fullname, MSG_SIZ, "\\\\.\\%s", name);
\r
9129 safeStrCpy(fullname, name, sizeof(fullname)/sizeof(fullname[0]) );
\r
9131 h = CreateFile(name, GENERIC_READ | GENERIC_WRITE,
\r
9132 0, NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL);
\r
9133 if (h == (HANDLE) -1) {
\r
9134 return GetLastError();
\r
9138 if (!SetCommState(h, (LPDCB) &dcb)) return GetLastError();
\r
9140 /* Accumulate characters until a 100ms pause, then parse */
\r
9141 ct.ReadIntervalTimeout = 100;
\r
9142 ct.ReadTotalTimeoutMultiplier = 0;
\r
9143 ct.ReadTotalTimeoutConstant = 0;
\r
9144 ct.WriteTotalTimeoutMultiplier = 0;
\r
9145 ct.WriteTotalTimeoutConstant = 0;
\r
9146 if (!SetCommTimeouts(h, (LPCOMMTIMEOUTS) &ct)) return GetLastError();
\r
9148 /* Prepare return value */
\r
9149 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
\r
9150 cp->kind = CPComm;
\r
9153 *pr = (ProcRef *) cp;
\r
9159 OpenLoopback(ProcRef *pr)
\r
9161 DisplayFatalError(_("Not implemented"), 0, 1);
\r
9167 OpenRcmd(char* host, char* user, char* cmd, ProcRef* pr)
\r
9172 struct sockaddr_in sa, mysa;
\r
9173 struct hostent FAR *hp;
\r
9174 unsigned short uport;
\r
9175 WORD wVersionRequested;
\r
9178 char stderrPortStr[MSG_SIZ];
\r
9180 /* Initialize socket DLL */
\r
9181 wVersionRequested = MAKEWORD(1, 1);
\r
9182 err = WSAStartup(wVersionRequested, &wsaData);
\r
9183 if (err != 0) return err;
\r
9185 /* Resolve remote host name */
\r
9186 memset((char *) &sa, 0, sizeof(struct sockaddr_in));
\r
9187 if (!(hp = gethostbyname(host))) {
\r
9188 unsigned int b0, b1, b2, b3;
\r
9190 err = WSAGetLastError();
\r
9192 if (sscanf(host, "%u.%u.%u.%u", &b0, &b1, &b2, &b3) == 4) {
\r
9193 hp = (struct hostent *) calloc(1, sizeof(struct hostent));
\r
9194 hp->h_addrtype = AF_INET;
\r
9196 hp->h_addr_list = (char **) calloc(2, sizeof(char *));
\r
9197 hp->h_addr_list[0] = (char *) malloc(4);
\r
9198 hp->h_addr_list[0][0] = (char) b0;
\r
9199 hp->h_addr_list[0][1] = (char) b1;
\r
9200 hp->h_addr_list[0][2] = (char) b2;
\r
9201 hp->h_addr_list[0][3] = (char) b3;
\r
9207 sa.sin_family = hp->h_addrtype;
\r
9208 uport = (unsigned short) 514;
\r
9209 sa.sin_port = htons(uport);
\r
9210 memcpy((char *) &sa.sin_addr, hp->h_addr, hp->h_length);
\r
9212 /* Bind local socket to unused "privileged" port address
\r
9214 s = INVALID_SOCKET;
\r
9215 memset((char *) &mysa, 0, sizeof(struct sockaddr_in));
\r
9216 mysa.sin_family = AF_INET;
\r
9217 mysa.sin_addr.s_addr = INADDR_ANY;
\r
9218 for (fromPort = 1023;; fromPort--) {
\r
9219 if (fromPort < 0) {
\r
9221 return WSAEADDRINUSE;
\r
9223 if (s == INVALID_SOCKET) {
\r
9224 if ((s = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {
\r
9225 err = WSAGetLastError();
\r
9230 uport = (unsigned short) fromPort;
\r
9231 mysa.sin_port = htons(uport);
\r
9232 if (bind(s, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))
\r
9233 == SOCKET_ERROR) {
\r
9234 err = WSAGetLastError();
\r
9235 if (err == WSAEADDRINUSE) continue;
\r
9239 if (connect(s, (struct sockaddr *) &sa,
\r
9240 sizeof(struct sockaddr_in)) == SOCKET_ERROR) {
\r
9241 err = WSAGetLastError();
\r
9242 if (err == WSAEADDRINUSE) {
\r
9253 /* Bind stderr local socket to unused "privileged" port address
\r
9255 s2 = INVALID_SOCKET;
\r
9256 memset((char *) &mysa, 0, sizeof(struct sockaddr_in));
\r
9257 mysa.sin_family = AF_INET;
\r
9258 mysa.sin_addr.s_addr = INADDR_ANY;
\r
9259 for (fromPort = 1023;; fromPort--) {
\r
9260 if (fromPort == prevStderrPort) continue; // don't reuse port
\r
9261 if (fromPort < 0) {
\r
9262 (void) closesocket(s);
\r
9264 return WSAEADDRINUSE;
\r
9266 if (s2 == INVALID_SOCKET) {
\r
9267 if ((s2 = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {
\r
9268 err = WSAGetLastError();
\r
9274 uport = (unsigned short) fromPort;
\r
9275 mysa.sin_port = htons(uport);
\r
9276 if (bind(s2, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))
\r
9277 == SOCKET_ERROR) {
\r
9278 err = WSAGetLastError();
\r
9279 if (err == WSAEADDRINUSE) continue;
\r
9280 (void) closesocket(s);
\r
9284 if (listen(s2, 1) == SOCKET_ERROR) {
\r
9285 err = WSAGetLastError();
\r
9286 if (err == WSAEADDRINUSE) {
\r
9288 s2 = INVALID_SOCKET;
\r
9291 (void) closesocket(s);
\r
9292 (void) closesocket(s2);
\r
9298 prevStderrPort = fromPort; // remember port used
\r
9299 snprintf(stderrPortStr, MSG_SIZ, "%d", fromPort);
\r
9301 if (send(s, stderrPortStr, strlen(stderrPortStr) + 1, 0) == SOCKET_ERROR) {
\r
9302 err = WSAGetLastError();
\r
9303 (void) closesocket(s);
\r
9304 (void) closesocket(s2);
\r
9309 if (send(s, UserName(), strlen(UserName()) + 1, 0) == SOCKET_ERROR) {
\r
9310 err = WSAGetLastError();
\r
9311 (void) closesocket(s);
\r
9312 (void) closesocket(s2);
\r
9316 if (*user == NULLCHAR) user = UserName();
\r
9317 if (send(s, user, strlen(user) + 1, 0) == SOCKET_ERROR) {
\r
9318 err = WSAGetLastError();
\r
9319 (void) closesocket(s);
\r
9320 (void) closesocket(s2);
\r
9324 if (send(s, cmd, strlen(cmd) + 1, 0) == SOCKET_ERROR) {
\r
9325 err = WSAGetLastError();
\r
9326 (void) closesocket(s);
\r
9327 (void) closesocket(s2);
\r
9332 if ((s3 = accept(s2, NULL, NULL)) == INVALID_SOCKET) {
\r
9333 err = WSAGetLastError();
\r
9334 (void) closesocket(s);
\r
9335 (void) closesocket(s2);
\r
9339 (void) closesocket(s2); /* Stop listening */
\r
9341 /* Prepare return value */
\r
9342 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
\r
9343 cp->kind = CPRcmd;
\r
9346 *pr = (ProcRef *) cp;
\r
9353 AddInputSource(ProcRef pr, int lineByLine,
\r
9354 InputCallback func, VOIDSTAR closure)
\r
9356 InputSource *is, *is2 = NULL;
\r
9357 ChildProc *cp = (ChildProc *) pr;
\r
9359 is = (InputSource *) calloc(1, sizeof(InputSource));
\r
9360 is->lineByLine = lineByLine;
\r
9362 is->closure = closure;
\r
9363 is->second = NULL;
\r
9364 is->next = is->buf;
\r
9365 if (pr == NoProc) {
\r
9366 is->kind = CPReal;
\r
9367 consoleInputSource = is;
\r
9369 is->kind = cp->kind;
\r
9371 [AS] Try to avoid a race condition if the thread is given control too early:
\r
9372 we create all threads suspended so that the is->hThread variable can be
\r
9373 safely assigned, then let the threads start with ResumeThread.
\r
9375 switch (cp->kind) {
\r
9377 is->hFile = cp->hFrom;
\r
9378 cp->hFrom = NULL; /* now owned by InputThread */
\r
9380 CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) NonOvlInputThread,
\r
9381 (LPVOID) is, CREATE_SUSPENDED, &is->id);
\r
9385 is->hFile = cp->hFrom;
\r
9386 cp->hFrom = NULL; /* now owned by InputThread */
\r
9388 CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) InputThread,
\r
9389 (LPVOID) is, CREATE_SUSPENDED, &is->id);
\r
9393 is->sock = cp->sock;
\r
9395 CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,
\r
9396 (LPVOID) is, CREATE_SUSPENDED, &is->id);
\r
9400 is2 = (InputSource *) calloc(1, sizeof(InputSource));
\r
9402 is->sock = cp->sock;
\r
9404 is2->sock = cp->sock2;
\r
9405 is2->second = is2;
\r
9407 CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,
\r
9408 (LPVOID) is, CREATE_SUSPENDED, &is->id);
\r
9410 CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,
\r
9411 (LPVOID) is2, CREATE_SUSPENDED, &is2->id);
\r
9415 if( is->hThread != NULL ) {
\r
9416 ResumeThread( is->hThread );
\r
9419 if( is2 != NULL && is2->hThread != NULL ) {
\r
9420 ResumeThread( is2->hThread );
\r
9424 return (InputSourceRef) is;
\r
9428 RemoveInputSource(InputSourceRef isr)
\r
9432 is = (InputSource *) isr;
\r
9433 is->hThread = NULL; /* tell thread to stop */
\r
9434 CloseHandle(is->hThread);
\r
9435 if (is->second != NULL) {
\r
9436 is->second->hThread = NULL;
\r
9437 CloseHandle(is->second->hThread);
\r
9441 int no_wrap(char *message, int count)
\r
9443 ConsoleOutput(message, count, FALSE);
\r
9448 OutputToProcess(ProcRef pr, char *message, int count, int *outError)
\r
9451 int outCount = SOCKET_ERROR;
\r
9452 ChildProc *cp = (ChildProc *) pr;
\r
9453 static OVERLAPPED ovl;
\r
9454 static int line = 0;
\r
9458 if (appData.noJoin || !appData.useInternalWrap)
\r
9459 return no_wrap(message, count);
\r
9462 int width = get_term_width();
\r
9463 int len = wrap(NULL, message, count, width, &line);
\r
9464 char *msg = malloc(len);
\r
9468 return no_wrap(message, count);
\r
9471 dbgchk = wrap(msg, message, count, width, &line);
\r
9472 if (dbgchk != len && appData.debugMode)
\r
9473 fprintf(debugFP, "wrap(): dbgchk(%d) != len(%d)\n", dbgchk, len);
\r
9474 ConsoleOutput(msg, len, FALSE);
\r
9481 if (ovl.hEvent == NULL) {
\r
9482 ovl.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
\r
9484 ovl.Internal = ovl.InternalHigh = ovl.Offset = ovl.OffsetHigh = 0;
\r
9486 switch (cp->kind) {
\r
9489 outCount = send(cp->sock, message, count, 0);
\r
9490 if (outCount == SOCKET_ERROR) {
\r
9491 *outError = WSAGetLastError();
\r
9493 *outError = NO_ERROR;
\r
9498 if (WriteFile(((ChildProc *)pr)->hTo, message, count,
\r
9499 &dOutCount, NULL)) {
\r
9500 *outError = NO_ERROR;
\r
9501 outCount = (int) dOutCount;
\r
9503 *outError = GetLastError();
\r
9508 *outError = DoWriteFile(((ChildProc *)pr)->hTo, message, count,
\r
9509 &dOutCount, &ovl);
\r
9510 if (*outError == NO_ERROR) {
\r
9511 outCount = (int) dOutCount;
\r
9519 OutputToProcessDelayed(ProcRef pr, char *message, int count, int *outError,
\r
9522 /* Ignore delay, not implemented for WinBoard */
\r
9523 return OutputToProcess(pr, message, count, outError);
\r
9528 CmailSigHandlerCallBack(InputSourceRef isr, VOIDSTAR closure,
\r
9529 char *buf, int count, int error)
\r
9531 DisplayFatalError(_("Not implemented"), 0, 1);
\r
9534 /* see wgamelist.c for Game List functions */
\r
9535 /* see wedittags.c for Edit Tags functions */
\r
9542 char buf[MSG_SIZ];
\r
9545 if (SearchPath(installDir, appData.icsLogon, NULL, MSG_SIZ, buf, &dummy)) {
\r
9546 f = fopen(buf, "r");
\r
9548 ProcessICSInitScript(f);
\r
9556 StartAnalysisClock()
\r
9558 if (analysisTimerEvent) return;
\r
9559 analysisTimerEvent = SetTimer(hwndMain, (UINT) ANALYSIS_TIMER_ID,
\r
9560 (UINT) 2000, NULL);
\r
9564 SetHighlights(int fromX, int fromY, int toX, int toY)
\r
9566 highlightInfo.sq[0].x = fromX;
\r
9567 highlightInfo.sq[0].y = fromY;
\r
9568 highlightInfo.sq[1].x = toX;
\r
9569 highlightInfo.sq[1].y = toY;
\r
9575 highlightInfo.sq[0].x = highlightInfo.sq[0].y =
\r
9576 highlightInfo.sq[1].x = highlightInfo.sq[1].y = -1;
\r
9580 SetPremoveHighlights(int fromX, int fromY, int toX, int toY)
\r
9582 premoveHighlightInfo.sq[0].x = fromX;
\r
9583 premoveHighlightInfo.sq[0].y = fromY;
\r
9584 premoveHighlightInfo.sq[1].x = toX;
\r
9585 premoveHighlightInfo.sq[1].y = toY;
\r
9589 ClearPremoveHighlights()
\r
9591 premoveHighlightInfo.sq[0].x = premoveHighlightInfo.sq[0].y =
\r
9592 premoveHighlightInfo.sq[1].x = premoveHighlightInfo.sq[1].y = -1;
\r
9596 ShutDownFrontEnd()
\r
9598 if (saveSettingsOnExit) SaveSettings(settingsFileName);
\r
9599 DeleteClipboardTempFiles();
\r
9605 if (IsIconic(hwndMain))
\r
9606 ShowWindow(hwndMain, SW_RESTORE);
\r
9608 SetActiveWindow(hwndMain);
\r
9612 * Prototypes for animation support routines
\r
9614 static void ScreenSquare(int column, int row, POINT * pt);
\r
9615 static void Tween( POINT * start, POINT * mid, POINT * finish, int factor,
\r
9616 POINT frames[], int * nFrames);
\r
9620 AnimateAtomicCapture(int fromX, int fromY, int toX, int toY, int nFrames)
\r
9621 { // [HGM] atomic: animate blast wave
\r
9623 if(appData.debugMode) fprintf(debugFP, "exploding (%d,%d)\n", toX, toY);
\r
9624 explodeInfo.fromX = fromX;
\r
9625 explodeInfo.fromY = fromY;
\r
9626 explodeInfo.toX = toX;
\r
9627 explodeInfo.toY = toY;
\r
9628 for(i=1; i<nFrames; i++) {
\r
9629 explodeInfo.radius = (i*180)/(nFrames-1);
\r
9630 DrawPosition(FALSE, NULL);
\r
9631 Sleep(appData.animSpeed);
\r
9633 explodeInfo.radius = 0;
\r
9634 DrawPosition(TRUE, NULL);
\r
9640 AnimateMove(board, fromX, fromY, toX, toY)
\r
9647 ChessSquare piece;
\r
9648 POINT start, finish, mid;
\r
9649 POINT frames[kFactor * 2 + 1];
\r
9652 if (!appData.animate) return;
\r
9653 if (doingSizing) return;
\r
9654 if (fromY < 0 || fromX < 0) return;
\r
9655 piece = board[fromY][fromX];
\r
9656 if (piece >= EmptySquare) return;
\r
9658 ScreenSquare(fromX, fromY, &start);
\r
9659 ScreenSquare(toX, toY, &finish);
\r
9661 /* All pieces except knights move in straight line */
\r
9662 if (piece != WhiteKnight && piece != BlackKnight) {
\r
9663 mid.x = start.x + (finish.x - start.x) / 2;
\r
9664 mid.y = start.y + (finish.y - start.y) / 2;
\r
9666 /* Knight: make diagonal movement then straight */
\r
9667 if (abs(toY - fromY) < abs(toX - fromX)) {
\r
9668 mid.x = start.x + (finish.x - start.x) / 2;
\r
9672 mid.y = start.y + (finish.y - start.y) / 2;
\r
9676 /* Don't use as many frames for very short moves */
\r
9677 if (abs(toY - fromY) + abs(toX - fromX) <= 2)
\r
9678 Tween(&start, &mid, &finish, kFactor - 1, frames, &nFrames);
\r
9680 Tween(&start, &mid, &finish, kFactor, frames, &nFrames);
\r
9682 animInfo.from.x = fromX;
\r
9683 animInfo.from.y = fromY;
\r
9684 animInfo.to.x = toX;
\r
9685 animInfo.to.y = toY;
\r
9686 animInfo.lastpos = start;
\r
9687 animInfo.piece = piece;
\r
9688 for (n = 0; n < nFrames; n++) {
\r
9689 animInfo.pos = frames[n];
\r
9690 DrawPosition(FALSE, NULL);
\r
9691 animInfo.lastpos = animInfo.pos;
\r
9692 Sleep(appData.animSpeed);
\r
9694 animInfo.pos = finish;
\r
9695 DrawPosition(FALSE, NULL);
\r
9696 animInfo.piece = EmptySquare;
\r
9697 if(gameInfo.variant == VariantAtomic &&
\r
9698 (board[toY][toX] != EmptySquare || fromX != toX && (piece == WhitePawn || piece == BlackPawn) ) )
\r
9699 AnimateAtomicCapture(fromX, fromY, toX, toY, 2*nFrames);
\r
9702 /* Convert board position to corner of screen rect and color */
\r
9705 ScreenSquare(column, row, pt)
\r
9706 int column; int row; POINT * pt;
\r
9709 pt->x = lineGap + ((BOARD_WIDTH-1)-column) * (squareSize + lineGap);
\r
9710 pt->y = lineGap + row * (squareSize + lineGap);
\r
9712 pt->x = lineGap + column * (squareSize + lineGap);
\r
9713 pt->y = lineGap + ((BOARD_HEIGHT-1)-row) * (squareSize + lineGap);
\r
9717 /* Generate a series of frame coords from start->mid->finish.
\r
9718 The movement rate doubles until the half way point is
\r
9719 reached, then halves back down to the final destination,
\r
9720 which gives a nice slow in/out effect. The algorithmn
\r
9721 may seem to generate too many intermediates for short
\r
9722 moves, but remember that the purpose is to attract the
\r
9723 viewers attention to the piece about to be moved and
\r
9724 then to where it ends up. Too few frames would be less
\r
9728 Tween(start, mid, finish, factor, frames, nFrames)
\r
9729 POINT * start; POINT * mid;
\r
9730 POINT * finish; int factor;
\r
9731 POINT frames[]; int * nFrames;
\r
9733 int n, fraction = 1, count = 0;
\r
9735 /* Slow in, stepping 1/16th, then 1/8th, ... */
\r
9736 for (n = 0; n < factor; n++)
\r
9738 for (n = 0; n < factor; n++) {
\r
9739 frames[count].x = start->x + (mid->x - start->x) / fraction;
\r
9740 frames[count].y = start->y + (mid->y - start->y) / fraction;
\r
9742 fraction = fraction / 2;
\r
9746 frames[count] = *mid;
\r
9749 /* Slow out, stepping 1/2, then 1/4, ... */
\r
9751 for (n = 0; n < factor; n++) {
\r
9752 frames[count].x = finish->x - (finish->x - mid->x) / fraction;
\r
9753 frames[count].y = finish->y - (finish->y - mid->y) / fraction;
\r
9755 fraction = fraction * 2;
\r
9761 HistorySet( char movelist[][2*MOVE_LEN], int first, int last, int current )
\r
9763 MoveHistorySet( movelist, first, last, current, pvInfoList );
\r
9765 EvalGraphSet( first, last, current, pvInfoList );
\r