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==49)) {
\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 switch (message) {
\r
4077 case WM_LBUTTONDOWN:
\r
4078 if (PtInRect((LPRECT) &whiteRect, pt)) {
\r
4079 if (gameMode == EditPosition) {
\r
4080 SetWhiteToPlayEvent();
\r
4081 } else if (gameMode == EditGame || GetKeyState(VK_SHIFT) < 0) {
\r
4082 AdjustClock(flipClock, -1);
\r
4083 } else if (gameMode == IcsPlayingBlack ||
\r
4084 gameMode == MachinePlaysWhite) {
\r
4087 } else if (PtInRect((LPRECT) &blackRect, pt)) {
\r
4088 if (gameMode == EditPosition) {
\r
4089 SetBlackToPlayEvent();
\r
4090 } else if (gameMode == EditGame || GetKeyState(VK_SHIFT) < 0) {
\r
4091 AdjustClock(!flipClock, -1);
\r
4092 } else if (gameMode == IcsPlayingWhite ||
\r
4093 gameMode == MachinePlaysBlack) {
\r
4097 dragInfo.start.x = dragInfo.start.y = -1;
\r
4098 dragInfo.from = dragInfo.start;
\r
4099 if(fromX == -1 && frozen) { // not sure where this is for
\r
4100 fromX = fromY = -1;
\r
4101 DrawPosition(forceFullRepaint || FALSE, NULL); /* [AS] */
\r
4104 LeftClick(Press, pt.x - boardRect.left, pt.y - boardRect.top);
\r
4105 DrawPosition(TRUE, NULL);
\r
4108 case WM_LBUTTONUP:
\r
4109 LeftClick(Release, pt.x - boardRect.left, pt.y - boardRect.top);
\r
4110 DrawPosition(TRUE, NULL);
\r
4113 case WM_MOUSEMOVE:
\r
4114 if(SeekGraphClick(Press, pt.x - boardRect.left, pt.y - boardRect.top, 1)) break;
\r
4115 MovePV(pt.x - boardRect.left, pt.y - boardRect.top, boardRect.bottom - boardRect.top);
\r
4116 if ((appData.animateDragging || appData.highlightDragging)
\r
4117 && (wParam & MK_LBUTTON)
\r
4118 && dragInfo.from.x >= 0)
\r
4120 BOOL full_repaint = FALSE;
\r
4122 if (appData.animateDragging) {
\r
4123 dragInfo.pos = pt;
\r
4125 if (appData.highlightDragging) {
\r
4126 SetHighlights(fromX, fromY, x, y);
\r
4127 if( IsDrawArrowEnabled() && (x < 0 || x >= BOARD_WIDTH || y < 0 || y >= BOARD_HEIGHT) ) {
\r
4128 full_repaint = TRUE;
\r
4132 DrawPosition( full_repaint, NULL);
\r
4134 dragInfo.lastpos = dragInfo.pos;
\r
4138 case WM_MOUSEWHEEL: // [DM]
\r
4139 { static int lastDir = 0; // [HGM] build in some hysteresis to avoid spurious events
\r
4140 /* Mouse Wheel is being rolled forward
\r
4141 * Play moves forward
\r
4143 if((short)HIWORD(wParam) < 0 && currentMove < forwardMostMove)
\r
4144 { if(lastDir == 1) ForwardEvent(); else lastDir = 1; } // [HGM] suppress first event in direction
\r
4145 /* Mouse Wheel is being rolled backward
\r
4146 * Play moves backward
\r
4148 if((short)HIWORD(wParam) > 0 && currentMove > backwardMostMove)
\r
4149 { if(lastDir == -1) BackwardEvent(); else lastDir = -1; }
\r
4153 case WM_MBUTTONUP:
\r
4154 case WM_RBUTTONUP:
\r
4156 RightClick(Release, pt.x - boardRect.left, pt.y - boardRect.top, &fromX, &fromY);
\r
4159 case WM_MBUTTONDOWN:
\r
4160 case WM_RBUTTONDOWN:
\r
4163 fromX = fromY = -1;
\r
4164 dragInfo.pos.x = dragInfo.pos.y = -1;
\r
4165 dragInfo.start.x = dragInfo.start.y = -1;
\r
4166 dragInfo.from = dragInfo.start;
\r
4167 dragInfo.lastpos = dragInfo.pos;
\r
4168 if (appData.highlightDragging) {
\r
4169 ClearHighlights();
\r
4172 /* [HGM] right mouse button in clock area edit-game mode ups clock */
\r
4173 if (PtInRect((LPRECT) &whiteRect, pt)) {
\r
4174 if (gameMode == EditGame || GetKeyState(VK_SHIFT) < 0) AdjustClock(flipClock, 1);
\r
4175 } else if (PtInRect((LPRECT) &blackRect, pt)) {
\r
4176 if (gameMode == EditGame || GetKeyState(VK_SHIFT) < 0) AdjustClock(!flipClock, 1);
\r
4180 DrawPosition(TRUE, NULL);
\r
4182 menuNr = RightClick(Press, pt.x - boardRect.left, pt.y - boardRect.top, &fromX, &fromY);
\r
4185 if (message == WM_MBUTTONDOWN) {
\r
4186 buttonCount = 3; /* even if system didn't think so */
\r
4187 if (wParam & MK_SHIFT)
\r
4188 MenuPopup(hwnd, pt, LoadMenu(hInst, "BlackPieceMenu"), -1);
\r
4190 MenuPopup(hwnd, pt, LoadMenu(hInst, "WhitePieceMenu"), -1);
\r
4191 } else { /* message == WM_RBUTTONDOWN */
\r
4192 /* Just have one menu, on the right button. Windows users don't
\r
4193 think to try the middle one, and sometimes other software steals
\r
4194 it, or it doesn't really exist. */
\r
4195 if(gameInfo.variant != VariantShogi)
\r
4196 MenuPopup(hwnd, pt, LoadMenu(hInst, "PieceMenu"), -1);
\r
4198 MenuPopup(hwnd, pt, LoadMenu(hInst, "ShogiPieceMenu"), -1);
\r
4202 SetCapture(hwndMain);
4205 hmenu = LoadMenu(hInst, "DropPieceMenu");
\r
4206 SetupDropMenu(hmenu);
\r
4207 MenuPopup(hwnd, pt, hmenu, -1);
\r
4217 /* Preprocess messages for buttons in main window */
\r
4219 ButtonProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
\r
4221 int id = GetWindowLong(hwnd, GWL_ID);
\r
4224 for (i=0; i<N_BUTTONS; i++) {
\r
4225 if (buttonDesc[i].id == id) break;
\r
4227 if (i == N_BUTTONS) return 0;
\r
4228 switch (message) {
\r
4233 dir = (wParam == VK_LEFT) ? -1 : 1;
\r
4234 SetFocus(buttonDesc[(i + dir + N_BUTTONS) % N_BUTTONS].hwnd);
\r
4241 SendMessage(hwndMain, WM_COMMAND, MAKEWPARAM(buttonDesc[i].id, 0), 0);
\r
4244 if (appData.icsActive && (isalpha((char)wParam) || wParam == '0')) {
\r
4245 // [HGM] movenum: only letters or leading zero should go to ICS input
\r
4246 HWND h = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
4247 if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);
\r
4249 SendMessage(h, WM_CHAR, wParam, lParam);
\r
4251 } else if (isalpha((char)wParam) || isdigit((char)wParam)){
\r
4252 PopUpMoveDialog((char)wParam);
\r
4258 return CallWindowProc(buttonDesc[i].wndproc, hwnd, message, wParam, lParam);
\r
4261 /* Process messages for Promotion dialog box */
\r
4263 Promotion(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
4267 switch (message) {
\r
4268 case WM_INITDIALOG: /* message: initialize dialog box */
\r
4269 /* Center the dialog over the application window */
\r
4270 CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));
\r
4271 Translate(hDlg, DLG_PromotionKing);
\r
4272 ShowWindow(GetDlgItem(hDlg, PB_King),
\r
4273 (!appData.testLegality || gameInfo.variant == VariantSuicide ||
\r
4274 gameInfo.variant == VariantGiveaway || gameInfo.variant == VariantSuper ) ?
\r
4275 SW_SHOW : SW_HIDE);
\r
4276 /* [HGM] Only allow C & A promotions if these pieces are defined */
\r
4277 ShowWindow(GetDlgItem(hDlg, PB_Archbishop),
\r
4278 ((PieceToChar(WhiteAngel) >= 'A' &&
\r
4279 PieceToChar(WhiteAngel) != '~') ||
\r
4280 (PieceToChar(BlackAngel) >= 'A' &&
\r
4281 PieceToChar(BlackAngel) != '~') ) ?
\r
4282 SW_SHOW : SW_HIDE);
\r
4283 ShowWindow(GetDlgItem(hDlg, PB_Chancellor),
\r
4284 ((PieceToChar(WhiteMarshall) >= 'A' &&
\r
4285 PieceToChar(WhiteMarshall) != '~') ||
\r
4286 (PieceToChar(BlackMarshall) >= 'A' &&
\r
4287 PieceToChar(BlackMarshall) != '~') ) ?
\r
4288 SW_SHOW : SW_HIDE);
\r
4289 /* [HGM] Hide B & R button in Shogi, use Q as promote, N as defer */
\r
4290 ShowWindow(GetDlgItem(hDlg, PB_Rook),
\r
4291 gameInfo.variant != VariantShogi ?
\r
4292 SW_SHOW : SW_HIDE);
\r
4293 ShowWindow(GetDlgItem(hDlg, PB_Bishop),
\r
4294 gameInfo.variant != VariantShogi ?
\r
4295 SW_SHOW : SW_HIDE);
\r
4296 ShowWindow(GetDlgItem(hDlg, IDC_Yes),
\r
4297 gameInfo.variant == VariantShogi ?
\r
4298 SW_SHOW : SW_HIDE);
\r
4299 ShowWindow(GetDlgItem(hDlg, IDC_No),
\r
4300 gameInfo.variant == VariantShogi ?
\r
4301 SW_SHOW : SW_HIDE);
\r
4302 ShowWindow(GetDlgItem(hDlg, IDC_Centaur),
\r
4303 gameInfo.variant == VariantSuper ?
\r
4304 SW_SHOW : SW_HIDE);
\r
4307 case WM_COMMAND: /* message: received a command */
\r
4308 switch (LOWORD(wParam)) {
\r
4310 EndDialog(hDlg, TRUE); /* Exit the dialog */
\r
4311 ClearHighlights();
\r
4312 DrawPosition(FALSE, NULL);
\r
4315 promoChar = gameInfo.variant == VariantSuper ? PieceToChar(BlackSilver) : PieceToChar(BlackKing);
\r
4318 promoChar = gameInfo.variant == VariantShogi ? '+' : PieceToChar(BlackQueen);
\r
4321 promoChar = PieceToChar(BlackRook);
\r
4324 promoChar = PieceToChar(BlackBishop);
\r
4326 case PB_Chancellor:
\r
4327 promoChar = PieceToChar(BlackMarshall);
\r
4329 case PB_Archbishop:
\r
4330 promoChar = PieceToChar(BlackAngel);
\r
4333 promoChar = gameInfo.variant == VariantShogi ? '=' : PieceToChar(BlackKnight);
\r
4338 EndDialog(hDlg, TRUE); /* Exit the dialog */
\r
4339 /* [HGM] <popupFix> Call FinishMove rather than UserMoveEvent, as we
\r
4340 only show the popup when we are already sure the move is valid or
\r
4341 legal. We pass a faulty move type, but the kludge is that FinishMove
\r
4342 will figure out it is a promotion from the promoChar. */
\r
4343 UserMoveEvent(fromX, fromY, toX, toY, promoChar);
\r
4344 fromX = fromY = -1;
\r
4345 if (!appData.highlightLastMove) {
\r
4346 ClearHighlights();
\r
4347 DrawPosition(FALSE, NULL);
\r
4354 /* Pop up promotion dialog */
\r
4356 PromotionPopup(HWND hwnd)
\r
4360 lpProc = MakeProcInstance((FARPROC)Promotion, hInst);
\r
4361 DialogBox(hInst, MAKEINTRESOURCE(DLG_PromotionKing),
\r
4362 hwnd, (DLGPROC)lpProc);
\r
4363 FreeProcInstance(lpProc);
\r
4369 DrawPosition(TRUE, NULL);
\r
4370 PromotionPopup(hwndMain);
\r
4373 /* Toggle ShowThinking */
\r
4375 ToggleShowThinking()
\r
4377 appData.showThinking = !appData.showThinking;
\r
4378 ShowThinkingEvent();
\r
4382 LoadGameDialog(HWND hwnd, char* title)
\r
4386 char fileTitle[MSG_SIZ];
\r
4387 f = OpenFileDialog(hwnd, "rb", "",
\r
4388 appData.oldSaveStyle ? "gam" : "pgn",
\r
4390 title, &number, fileTitle, NULL);
\r
4392 cmailMsgLoaded = FALSE;
\r
4393 if (number == 0) {
\r
4394 int error = GameListBuild(f);
\r
4396 DisplayError(_("Cannot build game list"), error);
\r
4397 } else if (!ListEmpty(&gameList) &&
\r
4398 ((ListGame *) gameList.tailPred)->number > 1) {
\r
4399 GameListPopUp(f, fileTitle);
\r
4402 GameListDestroy();
\r
4405 LoadGame(f, number, fileTitle, FALSE);
\r
4409 int get_term_width()
\r
4414 HFONT hfont, hold_font;
\r
4419 hText = GetDlgItem(hwndConsole, OPT_ConsoleText);
\r
4423 // get the text metrics
\r
4424 hdc = GetDC(hText);
\r
4425 lf = font[boardSize][CONSOLE_FONT]->lf;
\r
4426 if (consoleCF.dwEffects & CFE_BOLD)
\r
4427 lf.lfWeight = FW_BOLD;
\r
4428 if (consoleCF.dwEffects & CFE_ITALIC)
\r
4429 lf.lfItalic = TRUE;
\r
4430 if (consoleCF.dwEffects & CFE_STRIKEOUT)
\r
4431 lf.lfStrikeOut = TRUE;
\r
4432 if (consoleCF.dwEffects & CFE_UNDERLINE)
\r
4433 lf.lfUnderline = TRUE;
\r
4434 hfont = CreateFontIndirect(&lf);
\r
4435 hold_font = SelectObject(hdc, hfont);
\r
4436 GetTextMetrics(hdc, &tm);
\r
4437 SelectObject(hdc, hold_font);
\r
4438 DeleteObject(hfont);
\r
4439 ReleaseDC(hText, hdc);
\r
4441 // get the rectangle
\r
4442 SendMessage(hText, EM_GETRECT, 0, (LPARAM)&rc);
\r
4444 return (rc.right-rc.left) / tm.tmAveCharWidth;
\r
4447 void UpdateICSWidth(HWND hText)
\r
4449 LONG old_width, new_width;
\r
4451 new_width = get_term_width(hText, FALSE);
\r
4452 old_width = GetWindowLong(hText, GWL_USERDATA);
\r
4453 if (new_width != old_width)
\r
4455 ics_update_width(new_width);
\r
4456 SetWindowLong(hText, GWL_USERDATA, new_width);
\r
4461 ChangedConsoleFont()
\r
4464 CHARRANGE tmpsel, sel;
\r
4465 MyFont *f = font[boardSize][CONSOLE_FONT];
\r
4466 HWND hText = GetDlgItem(hwndConsole, OPT_ConsoleText);
\r
4467 HWND hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
4470 cfmt.cbSize = sizeof(CHARFORMAT);
\r
4471 cfmt.dwMask = CFM_FACE|CFM_SIZE|CFM_CHARSET;
\r
4472 safeStrCpy(cfmt.szFaceName, font[boardSize][CONSOLE_FONT]->mfp.faceName,
\r
4473 sizeof(cfmt.szFaceName)/sizeof(cfmt.szFaceName[0]) );
\r
4474 /* yHeight is expressed in twips. A twip is 1/20 of a font's point
\r
4475 * size. This was undocumented in the version of MSVC++ that I had
\r
4476 * when I wrote the code, but is apparently documented now.
\r
4478 cfmt.yHeight = (int)(f->mfp.pointSize * 20.0 + 0.5);
\r
4479 cfmt.bCharSet = f->lf.lfCharSet;
\r
4480 cfmt.bPitchAndFamily = f->lf.lfPitchAndFamily;
\r
4481 SendMessage(hText, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &cfmt);
\r
4482 SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &cfmt);
\r
4483 /* Why are the following seemingly needed too? */
\r
4484 SendMessage(hText, EM_SETCHARFORMAT, SCF_DEFAULT, (LPARAM) &cfmt);
\r
4485 SendMessage(hInput, EM_SETCHARFORMAT, SCF_DEFAULT, (LPARAM) &cfmt);
\r
4486 SendMessage(hText, EM_EXGETSEL, 0, (LPARAM)&sel);
\r
4488 tmpsel.cpMax = -1; /*999999?*/
\r
4489 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&tmpsel);
\r
4490 SendMessage(hText, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cfmt);
\r
4491 /* Trying putting this here too. It still seems to tickle a RichEdit
\r
4492 * bug: sometimes RichEdit indents the first line of a paragraph too.
\r
4494 paraf.cbSize = sizeof(paraf);
\r
4495 paraf.dwMask = PFM_OFFSET | PFM_STARTINDENT;
\r
4496 paraf.dxStartIndent = 0;
\r
4497 paraf.dxOffset = WRAP_INDENT;
\r
4498 SendMessage(hText, EM_SETPARAFORMAT, 0, (LPARAM) ¶f);
\r
4499 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
4500 UpdateICSWidth(hText);
\r
4503 /*---------------------------------------------------------------------------*\
\r
4505 * Window Proc for main window
\r
4507 \*---------------------------------------------------------------------------*/
\r
4509 /* Process messages for main window, etc. */
\r
4511 WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
\r
4514 int wmId, wmEvent;
\r
4518 char fileTitle[MSG_SIZ];
\r
4519 char buf[MSG_SIZ];
\r
4520 static SnapData sd;
\r
4522 switch (message) {
\r
4524 case WM_PAINT: /* message: repaint portion of window */
\r
4528 case WM_ERASEBKGND:
\r
4529 if (IsIconic(hwnd)) {
\r
4530 /* Cheat; change the message */
\r
4531 return (DefWindowProc(hwnd, WM_ICONERASEBKGND, wParam, lParam));
\r
4533 return (DefWindowProc(hwnd, message, wParam, lParam));
\r
4537 case WM_LBUTTONDOWN:
\r
4538 case WM_MBUTTONDOWN:
\r
4539 case WM_RBUTTONDOWN:
\r
4540 case WM_LBUTTONUP:
\r
4541 case WM_MBUTTONUP:
\r
4542 case WM_RBUTTONUP:
\r
4543 case WM_MOUSEMOVE:
\r
4544 case WM_MOUSEWHEEL:
\r
4545 MouseEvent(hwnd, message, wParam, lParam);
\r
4548 JAWS_KB_NAVIGATION
\r
4552 JAWS_ALT_INTERCEPT
\r
4554 if (appData.icsActive && ((char)wParam == '\r' || (char)wParam > ' ' && !((char)wParam >= '1' && (char)wParam <= '9'))) {
\r
4555 // [HGM] movenum: for non-zero digits we always do type-in dialog
\r
4556 HWND h = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
4557 if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);
\r
4559 SendMessage(h, message, wParam, lParam);
\r
4560 } else if(lParam != KF_REPEAT) {
\r
4561 if (isalpha((char)wParam) || isdigit((char)wParam)) {
\r
4562 PopUpMoveDialog((char)wParam);
\r
4563 } else if((char)wParam == 003) CopyGameToClipboard();
\r
4564 else if((char)wParam == 026) PasteGameOrFENFromClipboard();
\r
4569 case WM_PALETTECHANGED:
\r
4570 if (hwnd != (HWND)wParam && !appData.monoMode) {
\r
4572 HDC hdc = GetDC(hwndMain);
\r
4573 SelectPalette(hdc, hPal, TRUE);
\r
4574 nnew = RealizePalette(hdc);
\r
4576 paletteChanged = TRUE;
\r
4577 InvalidateRect(hwnd, &boardRect, FALSE);
\r
4579 ReleaseDC(hwnd, hdc);
\r
4583 case WM_QUERYNEWPALETTE:
\r
4584 if (!appData.monoMode /*&& paletteChanged*/) {
\r
4586 HDC hdc = GetDC(hwndMain);
\r
4587 paletteChanged = FALSE;
\r
4588 SelectPalette(hdc, hPal, FALSE);
\r
4589 nnew = RealizePalette(hdc);
\r
4591 InvalidateRect(hwnd, &boardRect, FALSE);
\r
4593 ReleaseDC(hwnd, hdc);
\r
4598 case WM_COMMAND: /* message: command from application menu */
\r
4599 wmId = LOWORD(wParam);
\r
4600 wmEvent = HIWORD(wParam);
\r
4605 SAY("new game enter a move to play against the computer with white");
\r
4608 case IDM_NewGameFRC:
\r
4609 if( NewGameFRC() == 0 ) {
\r
4614 case IDM_NewVariant:
\r
4615 NewVariantPopup(hwnd);
\r
4618 case IDM_LoadGame:
\r
4619 LoadGameDialog(hwnd, _("Load Game from File"));
\r
4622 case IDM_LoadNextGame:
\r
4626 case IDM_LoadPrevGame:
\r
4630 case IDM_ReloadGame:
\r
4634 case IDM_LoadPosition:
\r
4635 if (gameMode == AnalyzeMode || gameMode == AnalyzeFile) {
\r
4636 Reset(FALSE, TRUE);
\r
4639 f = OpenFileDialog(hwnd, "rb", "",
\r
4640 appData.oldSaveStyle ? "pos" : "fen",
\r
4642 _("Load Position from File"), &number, fileTitle, NULL);
\r
4644 LoadPosition(f, number, fileTitle);
\r
4648 case IDM_LoadNextPosition:
\r
4649 ReloadPosition(1);
\r
4652 case IDM_LoadPrevPosition:
\r
4653 ReloadPosition(-1);
\r
4656 case IDM_ReloadPosition:
\r
4657 ReloadPosition(0);
\r
4660 case IDM_SaveGame:
\r
4661 defName = DefaultFileName(appData.oldSaveStyle ? "gam" : "pgn");
\r
4662 f = OpenFileDialog(hwnd, "a", defName,
\r
4663 appData.oldSaveStyle ? "gam" : "pgn",
\r
4665 _("Save Game to File"), NULL, fileTitle, NULL);
\r
4667 SaveGame(f, 0, "");
\r
4671 case IDM_SavePosition:
\r
4672 defName = DefaultFileName(appData.oldSaveStyle ? "pos" : "fen");
\r
4673 f = OpenFileDialog(hwnd, "a", defName,
\r
4674 appData.oldSaveStyle ? "pos" : "fen",
\r
4676 _("Save Position to File"), NULL, fileTitle, NULL);
\r
4678 SavePosition(f, 0, "");
\r
4682 case IDM_SaveDiagram:
\r
4683 defName = "diagram";
\r
4684 f = OpenFileDialog(hwnd, "wb", defName,
\r
4687 "Save Diagram to File", NULL, fileTitle, NULL);
\r
4693 case IDM_CopyGame:
\r
4694 CopyGameToClipboard();
\r
4697 case IDM_PasteGame:
\r
4698 PasteGameFromClipboard();
\r
4701 case IDM_CopyGameListToClipboard:
\r
4702 CopyGameListToClipboard();
\r
4705 /* [AS] Autodetect FEN or PGN data */
\r
4706 case IDM_PasteAny:
\r
4707 PasteGameOrFENFromClipboard();
\r
4710 /* [AS] Move history */
\r
4711 case IDM_ShowMoveHistory:
\r
4712 if( MoveHistoryIsUp() ) {
\r
4713 MoveHistoryPopDown();
\r
4716 MoveHistoryPopUp();
\r
4720 /* [AS] Eval graph */
\r
4721 case IDM_ShowEvalGraph:
\r
4722 if( EvalGraphIsUp() ) {
\r
4723 EvalGraphPopDown();
\r
4727 SetFocus(hwndMain);
\r
4731 /* [AS] Engine output */
\r
4732 case IDM_ShowEngineOutput:
\r
4733 if( EngineOutputIsUp() ) {
\r
4734 EngineOutputPopDown();
\r
4737 EngineOutputPopUp();
\r
4741 /* [AS] User adjudication */
\r
4742 case IDM_UserAdjudication_White:
\r
4743 UserAdjudicationEvent( +1 );
\r
4746 case IDM_UserAdjudication_Black:
\r
4747 UserAdjudicationEvent( -1 );
\r
4750 case IDM_UserAdjudication_Draw:
\r
4751 UserAdjudicationEvent( 0 );
\r
4754 /* [AS] Game list options dialog */
\r
4755 case IDM_GameListOptions:
\r
4756 GameListOptions();
\r
4763 case IDM_CopyPosition:
\r
4764 CopyFENToClipboard();
\r
4767 case IDM_PastePosition:
\r
4768 PasteFENFromClipboard();
\r
4771 case IDM_MailMove:
\r
4775 case IDM_ReloadCMailMsg:
\r
4776 Reset(TRUE, TRUE);
\r
4777 ReloadCmailMsgEvent(FALSE);
\r
4780 case IDM_Minimize:
\r
4781 ShowWindow(hwnd, SW_MINIMIZE);
\r
4788 case IDM_MachineWhite:
\r
4789 MachineWhiteEvent();
\r
4791 * refresh the tags dialog only if it's visible
\r
4793 if (gameMode == MachinePlaysWhite && IsWindowVisible(editTagsDialog)) {
\r
4795 tags = PGNTags(&gameInfo);
\r
4796 TagsPopUp(tags, CmailMsg());
\r
4799 SAY("computer starts playing white");
\r
4802 case IDM_MachineBlack:
\r
4803 MachineBlackEvent();
\r
4805 * refresh the tags dialog only if it's visible
\r
4807 if (gameMode == MachinePlaysBlack && IsWindowVisible(editTagsDialog)) {
\r
4809 tags = PGNTags(&gameInfo);
\r
4810 TagsPopUp(tags, CmailMsg());
\r
4813 SAY("computer starts playing black");
\r
4816 case IDM_Match: // [HGM] match: flows into next case, after setting Match Mode and nr of Games
\r
4817 if(gameMode != BeginningOfGame) break; // allow menu item to remain enabled for better mode highligting
\r
4818 matchMode = 2;// distinguish from command-line-triggered case (matchMode=1)
\r
4819 appData.matchGames = appData.defaultMatchGames;
\r
4822 case IDM_TwoMachines:
\r
4823 TwoMachinesEvent();
\r
4825 * refresh the tags dialog only if it's visible
\r
4827 if (gameMode == TwoMachinesPlay && IsWindowVisible(editTagsDialog)) {
\r
4829 tags = PGNTags(&gameInfo);
\r
4830 TagsPopUp(tags, CmailMsg());
\r
4833 SAY("computer starts playing both sides");
\r
4836 case IDM_AnalysisMode:
\r
4837 if (!first.analysisSupport) {
\r
4838 snprintf(buf, MSG_SIZ, _("%s does not support analysis"), first.tidy);
\r
4839 DisplayError(buf, 0);
\r
4841 SAY("analyzing current position");
\r
4842 /* [DM] icsEngineAnlyze [HGM] Why is this front-end??? */
\r
4843 if (appData.icsActive) {
\r
4844 if (gameMode != IcsObserving) {
\r
4845 snprintf(buf, MSG_SIZ, "You are not observing a game");
\r
4846 DisplayError(buf, 0);
\r
4847 /* secure check */
\r
4848 if (appData.icsEngineAnalyze) {
\r
4849 if (appData.debugMode)
\r
4850 fprintf(debugFP, "Found unexpected active ICS engine analyze \n");
\r
4851 ExitAnalyzeMode();
\r
4857 /* if enable, user want disable icsEngineAnalyze */
\r
4858 if (appData.icsEngineAnalyze) {
\r
4859 ExitAnalyzeMode();
\r
4863 appData.icsEngineAnalyze = TRUE;
\r
4864 if (appData.debugMode) fprintf(debugFP, "ICS engine analyze starting...\n");
\r
4867 if (!appData.showThinking) ToggleShowThinking();
\r
4868 AnalyzeModeEvent();
\r
4872 case IDM_AnalyzeFile:
\r
4873 if (!first.analysisSupport) {
\r
4874 char buf[MSG_SIZ];
\r
4875 snprintf(buf, MSG_SIZ, _("%s does not support analysis"), first.tidy);
\r
4876 DisplayError(buf, 0);
\r
4878 if (!appData.showThinking) ToggleShowThinking();
\r
4879 AnalyzeFileEvent();
\r
4880 LoadGameDialog(hwnd, _("Analyze Game from File"));
\r
4881 AnalysisPeriodicEvent(1);
\r
4885 case IDM_IcsClient:
\r
4889 case IDM_EditGame:
\r
4894 case IDM_EditPosition:
\r
4895 EditPositionEvent();
\r
4896 SAY("enter a FEN string or setup a position on the board using the control R pop up menu");
\r
4899 case IDM_Training:
\r
4903 case IDM_ShowGameList:
\r
4904 ShowGameListProc();
\r
4907 case IDM_EditTags:
\r
4911 case IDM_EditComment:
\r
4912 if (commentUp && editComment) {
\r
4915 EditCommentEvent();
\r
4935 case IDM_CallFlag:
\r
4955 case IDM_StopObserving:
\r
4956 StopObservingEvent();
\r
4959 case IDM_StopExamining:
\r
4960 StopExaminingEvent();
\r
4964 UploadGameEvent();
\r
4967 case IDM_TypeInMove:
\r
4968 PopUpMoveDialog('\000');
\r
4971 case IDM_TypeInName:
\r
4972 PopUpNameDialog('\000');
\r
4975 case IDM_Backward:
\r
4977 SetFocus(hwndMain);
\r
4984 SetFocus(hwndMain);
\r
4989 SetFocus(hwndMain);
\r
4994 SetFocus(hwndMain);
\r
4998 RevertEvent(FALSE);
\r
5001 case IDM_Annotate: // [HGM] vari: revert with annotation
\r
5002 RevertEvent(TRUE);
\r
5005 case IDM_TruncateGame:
\r
5006 TruncateGameEvent();
\r
5013 case IDM_RetractMove:
\r
5014 RetractMoveEvent();
\r
5017 case IDM_FlipView:
\r
5018 flipView = !flipView;
\r
5019 DrawPosition(FALSE, NULL);
\r
5022 case IDM_FlipClock:
\r
5023 flipClock = !flipClock;
\r
5024 DisplayBothClocks();
\r
5025 DrawPosition(FALSE, NULL);
\r
5028 case IDM_MuteSounds:
\r
5029 mute = !mute; // [HGM] mute: keep track of global muting variable
\r
5030 CheckMenuItem(GetMenu(hwndMain),IDM_MuteSounds,
\r
5031 MF_BYCOMMAND|(mute?MF_CHECKED:MF_UNCHECKED));
\r
5034 case IDM_GeneralOptions:
\r
5035 GeneralOptionsPopup(hwnd);
\r
5036 DrawPosition(TRUE, NULL);
\r
5039 case IDM_BoardOptions:
\r
5040 BoardOptionsPopup(hwnd);
\r
5043 case IDM_EnginePlayOptions:
\r
5044 EnginePlayOptionsPopup(hwnd);
\r
5047 case IDM_Engine1Options:
\r
5048 EngineOptionsPopup(hwnd, &first);
\r
5051 case IDM_Engine2Options:
\r
5052 EngineOptionsPopup(hwnd, &second);
\r
5055 case IDM_OptionsUCI:
\r
5056 UciOptionsPopup(hwnd);
\r
5059 case IDM_IcsOptions:
\r
5060 IcsOptionsPopup(hwnd);
\r
5064 FontsOptionsPopup(hwnd);
\r
5068 SoundOptionsPopup(hwnd);
\r
5071 case IDM_CommPort:
\r
5072 CommPortOptionsPopup(hwnd);
\r
5075 case IDM_LoadOptions:
\r
5076 LoadOptionsPopup(hwnd);
\r
5079 case IDM_SaveOptions:
\r
5080 SaveOptionsPopup(hwnd);
\r
5083 case IDM_TimeControl:
\r
5084 TimeControlOptionsPopup(hwnd);
\r
5087 case IDM_SaveSettings:
\r
5088 SaveSettings(settingsFileName);
\r
5091 case IDM_SaveSettingsOnExit:
\r
5092 saveSettingsOnExit = !saveSettingsOnExit;
\r
5093 (void) CheckMenuItem(GetMenu(hwndMain), IDM_SaveSettingsOnExit,
\r
5094 MF_BYCOMMAND|(saveSettingsOnExit ?
\r
5095 MF_CHECKED : MF_UNCHECKED));
\r
5106 case IDM_AboutGame:
\r
5111 appData.debugMode = !appData.debugMode;
\r
5112 if (appData.debugMode) {
\r
5113 char dir[MSG_SIZ];
\r
5114 GetCurrentDirectory(MSG_SIZ, dir);
\r
5115 SetCurrentDirectory(installDir);
\r
5116 debugFP = fopen(appData.nameOfDebugFile, "w");
\r
5117 SetCurrentDirectory(dir);
\r
5118 setbuf(debugFP, NULL);
\r
5125 case IDM_HELPCONTENTS:
\r
5126 if (!MyHelp (hwnd, "winboard.hlp", HELP_KEY,(DWORD)(LPSTR)"CONTENTS") &&
\r
5127 !HtmlHelp(hwnd, "winboard.chm", 0, 0) ) {
\r
5128 MessageBox (GetFocus(),
\r
5129 _("Unable to activate help"),
\r
5130 szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);
\r
5134 case IDM_HELPSEARCH:
\r
5135 if (!MyHelp (hwnd, "winboard.hlp", HELP_PARTIALKEY, (DWORD)(LPSTR)"") &&
\r
5136 !HtmlHelp(hwnd, "winboard.chm", 0, 0) ) {
\r
5137 MessageBox (GetFocus(),
\r
5138 _("Unable to activate help"),
\r
5139 szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);
\r
5143 case IDM_HELPHELP:
\r
5144 if(!WinHelp(hwnd, (LPSTR)NULL, HELP_HELPONHELP, 0)) {
\r
5145 MessageBox (GetFocus(),
\r
5146 _("Unable to activate help"),
\r
5147 szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);
\r
5152 lpProc = MakeProcInstance((FARPROC)About, hInst);
\r
5154 (gameInfo.event && strcmp(gameInfo.event, "Easter Egg Hunt") == 0) ?
\r
5155 "AboutBox2" : "AboutBox", hwnd, (DLGPROC)lpProc);
\r
5156 FreeProcInstance(lpProc);
\r
5159 case IDM_DirectCommand1:
\r
5160 AskQuestionEvent(_("Direct Command"),
\r
5161 _("Send to chess program:"), "", "1");
\r
5163 case IDM_DirectCommand2:
\r
5164 AskQuestionEvent(_("Direct Command"),
\r
5165 _("Send to second chess program:"), "", "2");
\r
5168 case EP_WhitePawn:
\r
5169 EditPositionMenuEvent(WhitePawn, fromX, fromY);
\r
5170 fromX = fromY = -1;
\r
5173 case EP_WhiteKnight:
\r
5174 EditPositionMenuEvent(WhiteKnight, fromX, fromY);
\r
5175 fromX = fromY = -1;
\r
5178 case EP_WhiteBishop:
\r
5179 EditPositionMenuEvent(WhiteBishop, fromX, fromY);
\r
5180 fromX = fromY = -1;
\r
5183 case EP_WhiteRook:
\r
5184 EditPositionMenuEvent(WhiteRook, fromX, fromY);
\r
5185 fromX = fromY = -1;
\r
5188 case EP_WhiteQueen:
\r
5189 EditPositionMenuEvent(WhiteQueen, fromX, fromY);
\r
5190 fromX = fromY = -1;
\r
5193 case EP_WhiteFerz:
\r
5194 EditPositionMenuEvent(WhiteFerz, fromX, fromY);
\r
5195 fromX = fromY = -1;
\r
5198 case EP_WhiteWazir:
\r
5199 EditPositionMenuEvent(WhiteWazir, fromX, fromY);
\r
5200 fromX = fromY = -1;
\r
5203 case EP_WhiteAlfil:
\r
5204 EditPositionMenuEvent(WhiteAlfil, fromX, fromY);
\r
5205 fromX = fromY = -1;
\r
5208 case EP_WhiteCannon:
\r
5209 EditPositionMenuEvent(WhiteCannon, fromX, fromY);
\r
5210 fromX = fromY = -1;
\r
5213 case EP_WhiteCardinal:
\r
5214 EditPositionMenuEvent(WhiteAngel, fromX, fromY);
\r
5215 fromX = fromY = -1;
\r
5218 case EP_WhiteMarshall:
\r
5219 EditPositionMenuEvent(WhiteMarshall, fromX, fromY);
\r
5220 fromX = fromY = -1;
\r
5223 case EP_WhiteKing:
\r
5224 EditPositionMenuEvent(WhiteKing, fromX, fromY);
\r
5225 fromX = fromY = -1;
\r
5228 case EP_BlackPawn:
\r
5229 EditPositionMenuEvent(BlackPawn, fromX, fromY);
\r
5230 fromX = fromY = -1;
\r
5233 case EP_BlackKnight:
\r
5234 EditPositionMenuEvent(BlackKnight, fromX, fromY);
\r
5235 fromX = fromY = -1;
\r
5238 case EP_BlackBishop:
\r
5239 EditPositionMenuEvent(BlackBishop, fromX, fromY);
\r
5240 fromX = fromY = -1;
\r
5243 case EP_BlackRook:
\r
5244 EditPositionMenuEvent(BlackRook, fromX, fromY);
\r
5245 fromX = fromY = -1;
\r
5248 case EP_BlackQueen:
\r
5249 EditPositionMenuEvent(BlackQueen, fromX, fromY);
\r
5250 fromX = fromY = -1;
\r
5253 case EP_BlackFerz:
\r
5254 EditPositionMenuEvent(BlackFerz, fromX, fromY);
\r
5255 fromX = fromY = -1;
\r
5258 case EP_BlackWazir:
\r
5259 EditPositionMenuEvent(BlackWazir, fromX, fromY);
\r
5260 fromX = fromY = -1;
\r
5263 case EP_BlackAlfil:
\r
5264 EditPositionMenuEvent(BlackAlfil, fromX, fromY);
\r
5265 fromX = fromY = -1;
\r
5268 case EP_BlackCannon:
\r
5269 EditPositionMenuEvent(BlackCannon, fromX, fromY);
\r
5270 fromX = fromY = -1;
\r
5273 case EP_BlackCardinal:
\r
5274 EditPositionMenuEvent(BlackAngel, fromX, fromY);
\r
5275 fromX = fromY = -1;
\r
5278 case EP_BlackMarshall:
\r
5279 EditPositionMenuEvent(BlackMarshall, fromX, fromY);
\r
5280 fromX = fromY = -1;
\r
5283 case EP_BlackKing:
\r
5284 EditPositionMenuEvent(BlackKing, fromX, fromY);
\r
5285 fromX = fromY = -1;
\r
5288 case EP_EmptySquare:
\r
5289 EditPositionMenuEvent(EmptySquare, fromX, fromY);
\r
5290 fromX = fromY = -1;
\r
5293 case EP_ClearBoard:
\r
5294 EditPositionMenuEvent(ClearBoard, fromX, fromY);
\r
5295 fromX = fromY = -1;
\r
5299 EditPositionMenuEvent(WhitePlay, fromX, fromY);
\r
5300 fromX = fromY = -1;
\r
5304 EditPositionMenuEvent(BlackPlay, fromX, fromY);
\r
5305 fromX = fromY = -1;
\r
5309 EditPositionMenuEvent(PromotePiece, fromX, fromY);
\r
5310 fromX = fromY = -1;
\r
5314 EditPositionMenuEvent(DemotePiece, fromX, fromY);
\r
5315 fromX = fromY = -1;
\r
5319 DropMenuEvent(WhitePawn, fromX, fromY);
\r
5320 fromX = fromY = -1;
\r
5324 DropMenuEvent(WhiteKnight, fromX, fromY);
\r
5325 fromX = fromY = -1;
\r
5329 DropMenuEvent(WhiteBishop, fromX, fromY);
\r
5330 fromX = fromY = -1;
\r
5334 DropMenuEvent(WhiteRook, fromX, fromY);
\r
5335 fromX = fromY = -1;
\r
5339 DropMenuEvent(WhiteQueen, fromX, fromY);
\r
5340 fromX = fromY = -1;
\r
5345 TranslateMenus(0);
\r
5346 CheckMenuItem(GetMenu(hwndMain), lastChecked, MF_BYCOMMAND|MF_UNCHECKED);
\r
5347 CheckMenuItem(GetMenu(hwndMain), IDM_English, MF_BYCOMMAND|MF_CHECKED);
\r
5348 lastChecked = wmId;
\r
5352 if(wmId > IDM_English && wmId < IDM_English+5) {
\r
5353 LoadLanguageFile(languageFile[wmId - IDM_English - 1]);
\r
5354 TranslateMenus(0);
\r
5355 CheckMenuItem(GetMenu(hwndMain), lastChecked, MF_BYCOMMAND|MF_UNCHECKED);
\r
5356 CheckMenuItem(GetMenu(hwndMain), wmId, MF_BYCOMMAND|MF_CHECKED);
\r
5357 lastChecked = wmId;
\r
5360 return (DefWindowProc(hwnd, message, wParam, lParam));
\r
5366 case CLOCK_TIMER_ID:
\r
5367 KillTimer(hwnd, clockTimerEvent); /* Simulate one-shot timer as in X */
\r
5368 clockTimerEvent = 0;
\r
5369 DecrementClocks(); /* call into back end */
\r
5371 case LOAD_GAME_TIMER_ID:
\r
5372 KillTimer(hwnd, loadGameTimerEvent); /* Simulate one-shot timer as in X*/
\r
5373 loadGameTimerEvent = 0;
\r
5374 AutoPlayGameLoop(); /* call into back end */
\r
5376 case ANALYSIS_TIMER_ID:
\r
5377 if ((gameMode == AnalyzeMode || gameMode == AnalyzeFile
\r
5378 || appData.icsEngineAnalyze) && appData.periodicUpdates) {
\r
5379 AnalysisPeriodicEvent(0);
\r
5381 KillTimer(hwnd, analysisTimerEvent);
\r
5382 analysisTimerEvent = 0;
\r
5385 case DELAYED_TIMER_ID:
\r
5386 KillTimer(hwnd, delayedTimerEvent);
\r
5387 delayedTimerEvent = 0;
\r
5388 delayedTimerCallback();
\r
5393 case WM_USER_Input:
\r
5394 InputEvent(hwnd, message, wParam, lParam);
\r
5397 /* [AS] Also move "attached" child windows */
\r
5398 case WM_WINDOWPOSCHANGING:
\r
5400 if( hwnd == hwndMain && appData.useStickyWindows ) {
\r
5401 LPWINDOWPOS lpwp = (LPWINDOWPOS) lParam;
\r
5403 if( ((lpwp->flags & SWP_NOMOVE) == 0) && ((lpwp->flags & SWP_NOSIZE) != 0) ) {
\r
5404 /* Window is moving */
\r
5407 // GetWindowRect( hwnd, &rcMain ); //[HGM] sticky: in XP this returned new position, not old
\r
5408 rcMain.left = wpMain.x; // replace by these 4 lines to reconstruct old rect
\r
5409 rcMain.right = wpMain.x + wpMain.width;
\r
5410 rcMain.top = wpMain.y;
\r
5411 rcMain.bottom = wpMain.y + wpMain.height;
\r
5413 ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, moveHistoryDialog, &wpMoveHistory );
\r
5414 ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, evalGraphDialog, &wpEvalGraph );
\r
5415 ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, engineOutputDialog, &wpEngineOutput );
\r
5416 ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, gameListDialog, &wpGameList );
\r
5417 ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, hwndConsole, &wpConsole );
\r
5418 wpMain.x = lpwp->x;
\r
5419 wpMain.y = lpwp->y;
\r
5424 /* [AS] Snapping */
\r
5425 case WM_ENTERSIZEMOVE:
\r
5426 if(appData.debugMode) { fprintf(debugFP, "size-move\n"); }
\r
5427 if (hwnd == hwndMain) {
\r
5428 doingSizing = TRUE;
\r
5431 return OnEnterSizeMove( &sd, hwnd, wParam, lParam );
\r
5435 if(appData.debugMode) { fprintf(debugFP, "sizing\n"); }
\r
5436 if (hwnd == hwndMain) {
\r
5437 lastSizing = wParam;
\r
5442 if(appData.debugMode) { fprintf(debugFP, "moving\n"); }
\r
5443 return OnMoving( &sd, hwnd, wParam, lParam );
\r
5445 case WM_EXITSIZEMOVE:
\r
5446 if(appData.debugMode) { fprintf(debugFP, "exit size-move, size = %d\n", squareSize); }
\r
5447 if (hwnd == hwndMain) {
\r
5449 doingSizing = FALSE;
\r
5450 InvalidateRect(hwnd, &boardRect, FALSE);
\r
5451 GetClientRect(hwnd, &client);
\r
5452 ResizeBoard(client.right, client.bottom, lastSizing);
\r
5454 if(appData.debugMode) { fprintf(debugFP, "square size = %d\n", squareSize); }
\r
5456 return OnExitSizeMove( &sd, hwnd, wParam, lParam );
\r
5459 case WM_DESTROY: /* message: window being destroyed */
\r
5460 PostQuitMessage(0);
\r
5464 if (hwnd == hwndMain) {
\r
5469 default: /* Passes it on if unprocessed */
\r
5470 return (DefWindowProc(hwnd, message, wParam, lParam));
\r
5475 /*---------------------------------------------------------------------------*\
\r
5477 * Misc utility routines
\r
5479 \*---------------------------------------------------------------------------*/
\r
5482 * Decent random number generator, at least not as bad as Windows
\r
5483 * standard rand, which returns a value in the range 0 to 0x7fff.
\r
5485 unsigned int randstate;
\r
5490 randstate = randstate * 1664525 + 1013904223;
\r
5491 return (int) randstate & 0x7fffffff;
\r
5495 mysrandom(unsigned int seed)
\r
5502 * returns TRUE if user selects a different color, FALSE otherwise
\r
5506 ChangeColor(HWND hwnd, COLORREF *which)
\r
5508 static BOOL firstTime = TRUE;
\r
5509 static DWORD customColors[16];
\r
5511 COLORREF newcolor;
\r
5516 /* Make initial colors in use available as custom colors */
\r
5517 /* Should we put the compiled-in defaults here instead? */
\r
5519 customColors[i++] = lightSquareColor & 0xffffff;
\r
5520 customColors[i++] = darkSquareColor & 0xffffff;
\r
5521 customColors[i++] = whitePieceColor & 0xffffff;
\r
5522 customColors[i++] = blackPieceColor & 0xffffff;
\r
5523 customColors[i++] = highlightSquareColor & 0xffffff;
\r
5524 customColors[i++] = premoveHighlightColor & 0xffffff;
\r
5526 for (ccl = (ColorClass) 0; ccl < NColorClasses && i < 16; ccl++) {
\r
5527 customColors[i++] = textAttribs[ccl].color;
\r
5529 while (i < 16) customColors[i++] = RGB(255, 255, 255);
\r
5530 firstTime = FALSE;
\r
5533 cc.lStructSize = sizeof(cc);
\r
5534 cc.hwndOwner = hwnd;
\r
5535 cc.hInstance = NULL;
\r
5536 cc.rgbResult = (DWORD) (*which & 0xffffff);
\r
5537 cc.lpCustColors = (LPDWORD) customColors;
\r
5538 cc.Flags = CC_RGBINIT|CC_FULLOPEN;
\r
5540 if (!ChooseColor(&cc)) return FALSE;
\r
5542 newcolor = (COLORREF) (0x2000000 | cc.rgbResult);
\r
5543 if (newcolor == *which) return FALSE;
\r
5544 *which = newcolor;
\r
5548 InitDrawingColors();
\r
5549 InvalidateRect(hwnd, &boardRect, FALSE);
\r
5554 MyLoadSound(MySound *ms)
\r
5560 if (ms->data) free(ms->data);
\r
5563 switch (ms->name[0]) {
\r
5569 /* System sound from Control Panel. Don't preload here. */
\r
5573 if (ms->name[1] == NULLCHAR) {
\r
5574 /* "!" alone = silence */
\r
5577 /* Builtin wave resource. Error if not found. */
\r
5578 HANDLE h = FindResource(hInst, ms->name + 1, "WAVE");
\r
5579 if (h == NULL) break;
\r
5580 ms->data = (void *)LoadResource(hInst, h);
\r
5581 if (h == NULL) break;
\r
5586 /* .wav file. Error if not found. */
\r
5587 f = fopen(ms->name, "rb");
\r
5588 if (f == NULL) break;
\r
5589 if (fstat(fileno(f), &st) < 0) break;
\r
5590 ms->data = malloc(st.st_size);
\r
5591 if (fread(ms->data, st.st_size, 1, f) < 1) break;
\r
5597 char buf[MSG_SIZ];
\r
5598 snprintf(buf, MSG_SIZ, _("Error loading sound %s"), ms->name);
\r
5599 DisplayError(buf, GetLastError());
\r
5605 MyPlaySound(MySound *ms)
\r
5607 BOOLEAN ok = FALSE;
\r
5609 if(mute) return TRUE; // [HGM] mute: suppress all sound play when muted
\r
5610 switch (ms->name[0]) {
\r
5612 if(appData.debugMode) fprintf(debugFP, "silence\n");
\r
5617 /* System sound from Control Panel (deprecated feature).
\r
5618 "$" alone or an unset sound name gets default beep (still in use). */
\r
5619 if (ms->name[1]) {
\r
5620 ok = PlaySound(ms->name + 1, NULL, SND_ALIAS|SND_ASYNC);
\r
5622 if (!ok) ok = MessageBeep(MB_OK);
\r
5625 /* Builtin wave resource, or "!" alone for silence */
\r
5626 if (ms->name[1]) {
\r
5627 if (ms->data == NULL) return FALSE;
\r
5628 ok = PlaySound(ms->data, NULL, SND_MEMORY|SND_ASYNC);
\r
5634 /* .wav file. Error if not found. */
\r
5635 if (ms->data == NULL) return FALSE;
\r
5636 ok = PlaySound(ms->data, NULL, SND_MEMORY|SND_ASYNC);
\r
5639 /* Don't print an error: this can happen innocently if the sound driver
\r
5640 is busy; for instance, if another instance of WinBoard is playing
\r
5641 a sound at about the same time. */
\r
5647 OldOpenFileHook(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
5650 OPENFILENAME *ofn;
\r
5651 static UINT *number; /* gross that this is static */
\r
5653 switch (message) {
\r
5654 case WM_INITDIALOG: /* message: initialize dialog box */
\r
5655 /* Center the dialog over the application window */
\r
5656 ofn = (OPENFILENAME *) lParam;
\r
5657 if (ofn->Flags & OFN_ENABLETEMPLATE) {
\r
5658 number = (UINT *) ofn->lCustData;
\r
5659 SendMessage(GetDlgItem(hDlg, edt2), WM_SETTEXT, 0, (LPARAM) "");
\r
5663 CenterWindow (hDlg, GetWindow (hDlg, GW_OWNER));
\r
5664 Translate(hDlg, 1536);
\r
5665 return FALSE; /* Allow for further processing */
\r
5668 if ((LOWORD(wParam) == IDOK) && (number != NULL)) {
\r
5669 *number = GetDlgItemInt(hDlg, OPT_IndexNumberOld, &ok, FALSE);
\r
5671 return FALSE; /* Allow for further processing */
\r
5677 OpenFileHook(HWND hdlg, UINT uiMsg, WPARAM wParam, LPARAM lParam)
\r
5679 static UINT *number;
\r
5680 OPENFILENAME *ofname;
\r
5683 case WM_INITDIALOG:
\r
5684 Translate(hdlg, DLG_IndexNumber);
\r
5685 ofname = (OPENFILENAME *)lParam;
\r
5686 number = (UINT *)(ofname->lCustData);
\r
5689 ofnot = (OFNOTIFY *)lParam;
\r
5690 if (ofnot->hdr.code == CDN_FILEOK) {
\r
5691 *number = GetDlgItemInt(hdlg, OPT_IndexNumber, NULL, FALSE);
\r
5700 OpenFileDialog(HWND hwnd, char *write, char *defName, char *defExt, // [HGM] diag: type of 'write' now string
\r
5701 char *nameFilt, char *dlgTitle, UINT *number,
\r
5702 char fileTitle[MSG_SIZ], char fileName[MSG_SIZ])
\r
5704 OPENFILENAME openFileName;
\r
5705 char buf1[MSG_SIZ];
\r
5708 if (fileName == NULL) fileName = buf1;
\r
5709 if (defName == NULL) {
\r
5710 safeStrCpy(fileName, "*.", sizeof(fileName)/sizeof(fileName[0]) );
\r
5711 strcat(fileName, defExt);
\r
5713 safeStrCpy(fileName, defName, sizeof(fileName)/sizeof(fileName[0]) );
\r
5715 if (fileTitle) safeStrCpy(fileTitle, "", sizeof(fileTitle)/sizeof(fileTitle[0]) );
\r
5716 if (number) *number = 0;
\r
5718 openFileName.lStructSize = sizeof(OPENFILENAME);
\r
5719 openFileName.hwndOwner = hwnd;
\r
5720 openFileName.hInstance = (HANDLE) hInst;
\r
5721 openFileName.lpstrFilter = nameFilt;
\r
5722 openFileName.lpstrCustomFilter = (LPSTR) NULL;
\r
5723 openFileName.nMaxCustFilter = 0L;
\r
5724 openFileName.nFilterIndex = 1L;
\r
5725 openFileName.lpstrFile = fileName;
\r
5726 openFileName.nMaxFile = MSG_SIZ;
\r
5727 openFileName.lpstrFileTitle = fileTitle;
\r
5728 openFileName.nMaxFileTitle = fileTitle ? MSG_SIZ : 0;
\r
5729 openFileName.lpstrInitialDir = NULL;
\r
5730 openFileName.lpstrTitle = dlgTitle;
\r
5731 openFileName.Flags = OFN_PATHMUSTEXIST | OFN_HIDEREADONLY
\r
5732 | (write[0] != 'r' ? 0 : OFN_FILEMUSTEXIST)
\r
5733 | (number ? OFN_ENABLETEMPLATE | OFN_ENABLEHOOK: 0)
\r
5734 | (oldDialog ? 0 : OFN_EXPLORER);
\r
5735 openFileName.nFileOffset = 0;
\r
5736 openFileName.nFileExtension = 0;
\r
5737 openFileName.lpstrDefExt = defExt;
\r
5738 openFileName.lCustData = (LONG) number;
\r
5739 openFileName.lpfnHook = oldDialog ?
\r
5740 (LPOFNHOOKPROC) OldOpenFileHook : (LPOFNHOOKPROC) OpenFileHook;
\r
5741 openFileName.lpTemplateName = (LPSTR)(oldDialog ? 1536 : DLG_IndexNumber);
\r
5743 if (write[0] != 'r' ? GetSaveFileName(&openFileName) :
\r
5744 GetOpenFileName(&openFileName)) {
\r
5745 /* open the file */
\r
5746 f = fopen(openFileName.lpstrFile, write);
\r
5748 MessageBox(hwnd, _("File open failed"), NULL,
\r
5749 MB_OK|MB_ICONEXCLAMATION);
\r
5753 int err = CommDlgExtendedError();
\r
5754 if (err != 0) DisplayError(_("Internal error in file dialog box"), err);
\r
5763 MenuPopup(HWND hwnd, POINT pt, HMENU hmenu, UINT def)
\r
5765 HMENU hmenuTrackPopup; /* floating pop-up menu */
\r
5768 * Get the first pop-up menu in the menu template. This is the
\r
5769 * menu that TrackPopupMenu displays.
\r
5771 hmenuTrackPopup = GetSubMenu(hmenu, 0);
\r
5773 SetMenuDefaultItem(hmenuTrackPopup, def, FALSE);
\r
5776 * TrackPopup uses screen coordinates, so convert the
\r
5777 * coordinates of the mouse click to screen coordinates.
\r
5779 ClientToScreen(hwnd, (LPPOINT) &pt);
\r
5781 /* Draw and track the floating pop-up menu. */
\r
5782 TrackPopupMenu(hmenuTrackPopup, TPM_CENTERALIGN | TPM_RIGHTBUTTON,
\r
5783 pt.x, pt.y, 0, hwnd, NULL);
\r
5785 /* Destroy the menu.*/
\r
5786 DestroyMenu(hmenu);
\r
5791 int sizeX, sizeY, newSizeX, newSizeY;
\r
5793 } ResizeEditPlusButtonsClosure;
\r
5796 ResizeEditPlusButtonsCallback(HWND hChild, LPARAM lparam)
\r
5798 ResizeEditPlusButtonsClosure *cl = (ResizeEditPlusButtonsClosure *)lparam;
\r
5802 if (hChild == cl->hText) return TRUE;
\r
5803 GetWindowRect(hChild, &rect); /* gives screen coords */
\r
5804 pt.x = rect.left + (cl->newSizeX - cl->sizeX)/2;
\r
5805 pt.y = rect.top + cl->newSizeY - cl->sizeY;
\r
5806 ScreenToClient(cl->hDlg, &pt);
\r
5807 cl->hdwp = DeferWindowPos(cl->hdwp, hChild, NULL,
\r
5808 pt.x, pt.y, rect.right - rect.left, rect.bottom - rect.top, SWP_NOZORDER);
\r
5812 /* Resize a dialog that has a (rich) edit field filling most of
\r
5813 the top, with a row of buttons below */
\r
5815 ResizeEditPlusButtons(HWND hDlg, HWND hText, int sizeX, int sizeY, int newSizeX, int newSizeY)
\r
5818 int newTextHeight, newTextWidth;
\r
5819 ResizeEditPlusButtonsClosure cl;
\r
5821 /*if (IsIconic(hDlg)) return;*/
\r
5822 if (newSizeX == sizeX && newSizeY == sizeY) return;
\r
5824 cl.hdwp = BeginDeferWindowPos(8);
\r
5826 GetWindowRect(hText, &rectText); /* gives screen coords */
\r
5827 newTextWidth = rectText.right - rectText.left + newSizeX - sizeX;
\r
5828 newTextHeight = rectText.bottom - rectText.top + newSizeY - sizeY;
\r
5829 if (newTextHeight < 0) {
\r
5830 newSizeY += -newTextHeight;
\r
5831 newTextHeight = 0;
\r
5833 cl.hdwp = DeferWindowPos(cl.hdwp, hText, NULL, 0, 0,
\r
5834 newTextWidth, newTextHeight, SWP_NOZORDER|SWP_NOMOVE);
\r
5840 cl.newSizeX = newSizeX;
\r
5841 cl.newSizeY = newSizeY;
\r
5842 EnumChildWindows(hDlg, ResizeEditPlusButtonsCallback, (LPARAM)&cl);
\r
5844 EndDeferWindowPos(cl.hdwp);
\r
5847 BOOL CenterWindowEx(HWND hwndChild, HWND hwndParent, int mode)
\r
5849 RECT rChild, rParent;
\r
5850 int wChild, hChild, wParent, hParent;
\r
5851 int wScreen, hScreen, xNew, yNew;
\r
5854 /* Get the Height and Width of the child window */
\r
5855 GetWindowRect (hwndChild, &rChild);
\r
5856 wChild = rChild.right - rChild.left;
\r
5857 hChild = rChild.bottom - rChild.top;
\r
5859 /* Get the Height and Width of the parent window */
\r
5860 GetWindowRect (hwndParent, &rParent);
\r
5861 wParent = rParent.right - rParent.left;
\r
5862 hParent = rParent.bottom - rParent.top;
\r
5864 /* Get the display limits */
\r
5865 hdc = GetDC (hwndChild);
\r
5866 wScreen = GetDeviceCaps (hdc, HORZRES);
\r
5867 hScreen = GetDeviceCaps (hdc, VERTRES);
\r
5868 ReleaseDC(hwndChild, hdc);
\r
5870 /* Calculate new X position, then adjust for screen */
\r
5871 xNew = rParent.left + ((wParent - wChild) /2);
\r
5874 } else if ((xNew+wChild) > wScreen) {
\r
5875 xNew = wScreen - wChild;
\r
5878 /* Calculate new Y position, then adjust for screen */
\r
5880 yNew = rParent.top + ((hParent - hChild) /2);
\r
5883 yNew = rParent.top + GetSystemMetrics( SM_CYCAPTION ) * 2 / 3;
\r
5888 } else if ((yNew+hChild) > hScreen) {
\r
5889 yNew = hScreen - hChild;
\r
5892 /* Set it, and return */
\r
5893 return SetWindowPos (hwndChild, NULL,
\r
5894 xNew, yNew, 0, 0, SWP_NOSIZE | SWP_NOZORDER);
\r
5897 /* Center one window over another */
\r
5898 BOOL CenterWindow (HWND hwndChild, HWND hwndParent)
\r
5900 return CenterWindowEx( hwndChild, hwndParent, 0 );
\r
5903 /*---------------------------------------------------------------------------*\
\r
5905 * Startup Dialog functions
\r
5907 \*---------------------------------------------------------------------------*/
\r
5909 InitComboStrings(HANDLE hwndCombo, char **cd)
\r
5911 SendMessage(hwndCombo, CB_RESETCONTENT, 0, 0);
\r
5913 while (*cd != NULL) {
\r
5914 SendMessage(hwndCombo, CB_ADDSTRING, 0, (LPARAM) T_(*cd));
\r
5920 InitComboStringsFromOption(HANDLE hwndCombo, char *str)
\r
5922 char buf1[MAX_ARG_LEN];
\r
5925 if (str[0] == '@') {
\r
5926 FILE* f = fopen(str + 1, "r");
\r
5928 DisplayFatalError(str + 1, errno, 2);
\r
5931 len = fread(buf1, 1, sizeof(buf1)-1, f);
\r
5933 buf1[len] = NULLCHAR;
\r
5937 SendMessage(hwndCombo, CB_RESETCONTENT, 0, 0);
\r
5940 char buf[MSG_SIZ];
\r
5941 char *end = strchr(str, '\n');
\r
5942 if (end == NULL) return;
\r
5943 memcpy(buf, str, end - str);
\r
5944 buf[end - str] = NULLCHAR;
\r
5945 SendMessage(hwndCombo, CB_ADDSTRING, 0, (LPARAM) buf);
\r
5951 SetStartupDialogEnables(HWND hDlg)
\r
5953 EnableWindow(GetDlgItem(hDlg, OPT_ChessEngineName),
\r
5954 IsDlgButtonChecked(hDlg, OPT_ChessEngine) ||
\r
5955 (appData.zippyPlay && IsDlgButtonChecked(hDlg, OPT_ChessServer)));
\r
5956 EnableWindow(GetDlgItem(hDlg, OPT_SecondChessEngineName),
\r
5957 IsDlgButtonChecked(hDlg, OPT_ChessEngine));
\r
5958 EnableWindow(GetDlgItem(hDlg, OPT_ChessServerName),
\r
5959 IsDlgButtonChecked(hDlg, OPT_ChessServer));
\r
5960 EnableWindow(GetDlgItem(hDlg, OPT_AdditionalOptions),
\r
5961 IsDlgButtonChecked(hDlg, OPT_AnyAdditional));
\r
5962 EnableWindow(GetDlgItem(hDlg, IDOK),
\r
5963 IsDlgButtonChecked(hDlg, OPT_ChessEngine) ||
\r
5964 IsDlgButtonChecked(hDlg, OPT_ChessServer) ||
\r
5965 IsDlgButtonChecked(hDlg, OPT_View));
\r
5969 QuoteForFilename(char *filename)
\r
5971 int dquote, space;
\r
5972 dquote = strchr(filename, '"') != NULL;
\r
5973 space = strchr(filename, ' ') != NULL;
\r
5974 if (dquote || space) {
\r
5986 InitEngineBox(HWND hDlg, HWND hwndCombo, char* nthcp, char* nthd, char* nthdir, char *nthnames)
\r
5988 char buf[MSG_SIZ];
\r
5991 InitComboStringsFromOption(hwndCombo, nthnames);
\r
5992 q = QuoteForFilename(nthcp);
\r
5993 snprintf(buf, MSG_SIZ, "%s%s%s", q, nthcp, q);
\r
5994 if (*nthdir != NULLCHAR) {
\r
5995 q = QuoteForFilename(nthdir);
\r
5996 snprintf(buf + strlen(buf), MSG_SIZ, " /%s=%s%s%s", nthd, q, nthdir, q);
\r
5998 if (*nthcp == NULLCHAR) {
\r
5999 SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) 0, (LPARAM) 0);
\r
6000 } else if (SendMessage(hwndCombo, CB_SELECTSTRING, (WPARAM) -1, (LPARAM) buf) == CB_ERR) {
\r
6001 SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) -1, (LPARAM) 0);
\r
6002 SendMessage(hwndCombo, WM_SETTEXT, (WPARAM) 0, (LPARAM) buf);
\r
6007 StartupDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
6009 char buf[MSG_SIZ];
\r
6013 switch (message) {
\r
6014 case WM_INITDIALOG:
\r
6015 /* Center the dialog */
\r
6016 CenterWindow (hDlg, GetDesktopWindow());
\r
6017 Translate(hDlg, DLG_Startup);
\r
6018 /* Initialize the dialog items */
\r
6019 InitEngineBox(hDlg, GetDlgItem(hDlg, OPT_ChessEngineName),
\r
6020 appData.firstChessProgram, "fd", appData.firstDirectory,
\r
6021 firstChessProgramNames);
\r
6022 InitEngineBox(hDlg, GetDlgItem(hDlg, OPT_SecondChessEngineName),
\r
6023 appData.secondChessProgram, "sd", appData.secondDirectory,
\r
6024 secondChessProgramNames);
\r
6025 hwndCombo = GetDlgItem(hDlg, OPT_ChessServerName);
\r
6026 InitComboStringsFromOption(hwndCombo, icsNames);
\r
6027 snprintf(buf, MSG_SIZ, "%s /icsport=%s", appData.icsHost, appData.icsPort);
\r
6028 if (*appData.icsHelper != NULLCHAR) {
\r
6029 char *q = QuoteForFilename(appData.icsHelper);
\r
6030 sprintf(buf + strlen(buf), " /icshelper=%s%s%s", q, appData.icsHelper, q);
\r
6032 if (*appData.icsHost == NULLCHAR) {
\r
6033 SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) 0, (LPARAM) 0);
\r
6034 /*SendMessage(hwndCombo, CB_SHOWDROPDOWN, (WPARAM) TRUE, (LPARAM) 0); !!too soon */
\r
6035 } else if (SendMessage(hwndCombo, CB_SELECTSTRING, (WPARAM) -1, (LPARAM) buf) == CB_ERR) {
\r
6036 SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) -1, (LPARAM) 0);
\r
6037 SendMessage(hwndCombo, WM_SETTEXT, (WPARAM) 0, (LPARAM) buf);
\r
6040 if (appData.icsActive) {
\r
6041 CheckDlgButton(hDlg, OPT_ChessServer, BST_CHECKED);
\r
6043 else if (appData.noChessProgram) {
\r
6044 CheckDlgButton(hDlg, OPT_View, BST_CHECKED);
\r
6047 CheckDlgButton(hDlg, OPT_ChessEngine, BST_CHECKED);
\r
6050 SetStartupDialogEnables(hDlg);
\r
6054 switch (LOWORD(wParam)) {
\r
6056 if (IsDlgButtonChecked(hDlg, OPT_ChessEngine)) {
\r
6057 safeStrCpy(buf, "/fcp=", sizeof(buf)/sizeof(buf[0]) );
\r
6058 GetDlgItemText(hDlg, OPT_ChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));
\r
6060 ParseArgs(StringGet, &p);
\r
6061 safeStrCpy(buf, "/scp=", sizeof(buf)/sizeof(buf[0]) );
\r
6062 GetDlgItemText(hDlg, OPT_SecondChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));
\r
6064 ParseArgs(StringGet, &p);
\r
6065 appData.noChessProgram = FALSE;
\r
6066 appData.icsActive = FALSE;
\r
6067 } else if (IsDlgButtonChecked(hDlg, OPT_ChessServer)) {
\r
6068 safeStrCpy(buf, "/ics /icshost=", sizeof(buf)/sizeof(buf[0]) );
\r
6069 GetDlgItemText(hDlg, OPT_ChessServerName, buf + strlen(buf), sizeof(buf) - strlen(buf));
\r
6071 ParseArgs(StringGet, &p);
\r
6072 if (appData.zippyPlay) {
\r
6073 safeStrCpy(buf, "/fcp=", sizeof(buf)/sizeof(buf[0]) );
\r
6074 GetDlgItemText(hDlg, OPT_ChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));
\r
6076 ParseArgs(StringGet, &p);
\r
6078 } else if (IsDlgButtonChecked(hDlg, OPT_View)) {
\r
6079 appData.noChessProgram = TRUE;
\r
6080 appData.icsActive = FALSE;
\r
6082 MessageBox(hDlg, _("Choose an option, or cancel to exit"),
\r
6083 _("Option Error"), MB_OK|MB_ICONEXCLAMATION);
\r
6086 if (IsDlgButtonChecked(hDlg, OPT_AnyAdditional)) {
\r
6087 GetDlgItemText(hDlg, OPT_AdditionalOptions, buf, sizeof(buf));
\r
6089 ParseArgs(StringGet, &p);
\r
6091 EndDialog(hDlg, TRUE);
\r
6098 case IDM_HELPCONTENTS:
\r
6099 if (!WinHelp (hDlg, "winboard.hlp", HELP_KEY,(DWORD)(LPSTR)"CONTENTS")) {
\r
6100 MessageBox (GetFocus(),
\r
6101 _("Unable to activate help"),
\r
6102 szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);
\r
6107 SetStartupDialogEnables(hDlg);
\r
6115 /*---------------------------------------------------------------------------*\
\r
6117 * About box dialog functions
\r
6119 \*---------------------------------------------------------------------------*/
\r
6121 /* Process messages for "About" dialog box */
\r
6123 About(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
6125 switch (message) {
\r
6126 case WM_INITDIALOG: /* message: initialize dialog box */
\r
6127 /* Center the dialog over the application window */
\r
6128 CenterWindow (hDlg, GetWindow (hDlg, GW_OWNER));
\r
6129 SetDlgItemText(hDlg, ABOUTBOX_Version, programVersion);
\r
6130 Translate(hDlg, ABOUTBOX);
\r
6134 case WM_COMMAND: /* message: received a command */
\r
6135 if (LOWORD(wParam) == IDOK /* "OK" box selected? */
\r
6136 || LOWORD(wParam) == IDCANCEL) { /* System menu close command? */
\r
6137 EndDialog(hDlg, TRUE); /* Exit the dialog */
\r
6145 /*---------------------------------------------------------------------------*\
\r
6147 * Comment Dialog functions
\r
6149 \*---------------------------------------------------------------------------*/
\r
6152 CommentDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
6154 static HANDLE hwndText = NULL;
\r
6155 int len, newSizeX, newSizeY, flags;
\r
6156 static int sizeX, sizeY;
\r
6161 switch (message) {
\r
6162 case WM_INITDIALOG: /* message: initialize dialog box */
\r
6163 /* Initialize the dialog items */
\r
6164 Translate(hDlg, DLG_EditComment);
\r
6165 hwndText = GetDlgItem(hDlg, OPT_CommentText);
\r
6166 SetDlgItemText(hDlg, OPT_CommentText, commentText);
\r
6167 EnableWindow(GetDlgItem(hDlg, OPT_CancelComment), editComment);
\r
6168 EnableWindow(GetDlgItem(hDlg, OPT_ClearComment), editComment);
\r
6169 EnableWindow(GetDlgItem(hDlg, OPT_EditComment), !editComment);
\r
6170 SendMessage(hwndText, EM_SETREADONLY, !editComment, 0);
\r
6171 SetWindowText(hDlg, commentTitle);
\r
6172 if (editComment) {
\r
6173 SetFocus(hwndText);
\r
6175 SetFocus(GetDlgItem(hDlg, IDOK));
\r
6177 SendMessage(GetDlgItem(hDlg, OPT_CommentText),
\r
6178 WM_SETFONT, (WPARAM)font[boardSize][COMMENT_FONT]->hf,
\r
6179 MAKELPARAM(FALSE, 0));
\r
6180 /* Size and position the dialog */
\r
6181 if (!commentDialog) {
\r
6182 commentDialog = hDlg;
\r
6183 flags = SWP_NOZORDER;
\r
6184 GetClientRect(hDlg, &rect);
\r
6185 sizeX = rect.right;
\r
6186 sizeY = rect.bottom;
\r
6187 if (wpComment.x != CW_USEDEFAULT && wpComment.y != CW_USEDEFAULT &&
\r
6188 wpComment.width != CW_USEDEFAULT && wpComment.height != CW_USEDEFAULT) {
\r
6189 WINDOWPLACEMENT wp;
\r
6190 EnsureOnScreen(&wpComment.x, &wpComment.y, 0, 0);
\r
6191 wp.length = sizeof(WINDOWPLACEMENT);
\r
6193 wp.showCmd = SW_SHOW;
\r
6194 wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;
\r
6195 wp.rcNormalPosition.left = wpComment.x;
\r
6196 wp.rcNormalPosition.right = wpComment.x + wpComment.width;
\r
6197 wp.rcNormalPosition.top = wpComment.y;
\r
6198 wp.rcNormalPosition.bottom = wpComment.y + wpComment.height;
\r
6199 SetWindowPlacement(hDlg, &wp);
\r
6201 GetClientRect(hDlg, &rect);
\r
6202 newSizeX = rect.right;
\r
6203 newSizeY = rect.bottom;
\r
6204 ResizeEditPlusButtons(hDlg, hwndText, sizeX, sizeY,
\r
6205 newSizeX, newSizeY);
\r
6210 SendDlgItemMessage( hDlg, OPT_CommentText, EM_SETEVENTMASK, 0, ENM_MOUSEEVENTS );
\r
6213 case WM_COMMAND: /* message: received a command */
\r
6214 switch (LOWORD(wParam)) {
\r
6216 if (editComment) {
\r
6218 /* Read changed options from the dialog box */
\r
6219 hwndText = GetDlgItem(hDlg, OPT_CommentText);
\r
6220 len = GetWindowTextLength(hwndText);
\r
6221 str = (char *) malloc(len + 1);
\r
6222 GetWindowText(hwndText, str, len + 1);
\r
6231 ReplaceComment(commentIndex, str);
\r
6238 case OPT_CancelComment:
\r
6242 case OPT_ClearComment:
\r
6243 SetDlgItemText(hDlg, OPT_CommentText, "");
\r
6246 case OPT_EditComment:
\r
6247 EditCommentEvent();
\r
6255 case WM_NOTIFY: // [HGM] vari: cloned from whistory.c
\r
6256 if( wParam == OPT_CommentText ) {
\r
6257 MSGFILTER * lpMF = (MSGFILTER *) lParam;
\r
6259 if( lpMF->msg == WM_RBUTTONDOWN && (lpMF->wParam & (MK_CONTROL | MK_SHIFT)) == 0 ) {
\r
6263 pt.x = LOWORD( lpMF->lParam );
\r
6264 pt.y = HIWORD( lpMF->lParam );
\r
6266 index = SendDlgItemMessage( hDlg, OPT_CommentText, EM_CHARFROMPOS, 0, (LPARAM) &pt );
\r
6268 hwndText = GetDlgItem(hDlg, OPT_CommentText); // cloned from above
\r
6269 len = GetWindowTextLength(hwndText);
\r
6270 str = (char *) malloc(len + 1);
\r
6271 GetWindowText(hwndText, str, len + 1);
\r
6272 ReplaceComment(commentIndex, str);
\r
6273 if(commentIndex != currentMove) ToNrEvent(commentIndex);
\r
6274 LoadVariation( index, str ); // [HGM] also does the actual moving to it, now
\r
6277 /* Zap the message for good: apparently, returning non-zero is not enough */
\r
6278 lpMF->msg = WM_USER;
\r
6286 newSizeX = LOWORD(lParam);
\r
6287 newSizeY = HIWORD(lParam);
\r
6288 ResizeEditPlusButtons(hDlg, hwndText, sizeX, sizeY, newSizeX, newSizeY);
\r
6293 case WM_GETMINMAXINFO:
\r
6294 /* Prevent resizing window too small */
\r
6295 mmi = (MINMAXINFO *) lParam;
\r
6296 mmi->ptMinTrackSize.x = 100;
\r
6297 mmi->ptMinTrackSize.y = 100;
\r
6304 EitherCommentPopUp(int index, char *title, char *str, BOOLEAN edit)
\r
6309 CheckMenuItem(GetMenu(hwndMain), IDM_EditComment, edit ? MF_CHECKED : MF_UNCHECKED);
\r
6311 if (str == NULL) str = "";
\r
6312 p = (char *) malloc(2 * strlen(str) + 2);
\r
6315 if (*str == '\n') *q++ = '\r';
\r
6319 if (commentText != NULL) free(commentText);
\r
6321 commentIndex = index;
\r
6322 commentTitle = title;
\r
6324 editComment = edit;
\r
6326 if (commentDialog) {
\r
6327 SendMessage(commentDialog, WM_INITDIALOG, 0, 0);
\r
6328 if (!commentUp) ShowWindow(commentDialog, SW_SHOW);
\r
6330 lpProc = MakeProcInstance((FARPROC)CommentDialog, hInst);
\r
6331 CreateDialog(hInst, MAKEINTRESOURCE(DLG_EditComment),
\r
6332 hwndMain, (DLGPROC)lpProc);
\r
6333 FreeProcInstance(lpProc);
\r
6339 /*---------------------------------------------------------------------------*\
\r
6341 * Type-in move dialog functions
\r
6343 \*---------------------------------------------------------------------------*/
\r
6346 TypeInMoveDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
6348 char move[MSG_SIZ];
\r
6350 ChessMove moveType;
\r
6351 int fromX, fromY, toX, toY;
\r
6354 switch (message) {
\r
6355 case WM_INITDIALOG:
\r
6356 move[0] = (char) lParam;
\r
6357 move[1] = NULLCHAR;
\r
6358 CenterWindowEx(hDlg, GetWindow(hDlg, GW_OWNER), 1 );
\r
6359 Translate(hDlg, DLG_TypeInMove);
\r
6360 hInput = GetDlgItem(hDlg, OPT_Move);
\r
6361 SetWindowText(hInput, move);
\r
6363 SendMessage(hInput, EM_SETSEL, (WPARAM)9999, (LPARAM)9999);
\r
6367 switch (LOWORD(wParam)) {
\r
6369 GetDlgItemText(hDlg, OPT_Move, move, sizeof(move));
\r
6370 { int n; Board board;
\r
6372 if(gameMode == EditPosition && ParseFEN(board, &n, move) ) {
\r
6373 EditPositionPasteFEN(move);
\r
6374 EndDialog(hDlg, TRUE);
\r
6377 // [HGM] movenum: allow move number to be typed in any mode
\r
6378 if(sscanf(move, "%d", &n) == 1 && n != 0 ) {
\r
6380 EndDialog(hDlg, TRUE);
\r
6384 if (gameMode != EditGame && currentMove != forwardMostMove &&
\r
6385 gameMode != Training) {
\r
6386 DisplayMoveError(_("Displayed move is not current"));
\r
6388 // GetDlgItemText(hDlg, OPT_Move, move, sizeof(move)); // moved upstream
\r
6389 int ok = ParseOneMove(move, gameMode == EditPosition ? blackPlaysFirst : currentMove,
\r
6390 &moveType, &fromX, &fromY, &toX, &toY, &promoChar);
\r
6391 if(!ok && move[0] >= 'a') { move[0] += 'A' - 'a'; ok = 2; } // [HGM] try also capitalized
\r
6392 if (ok==1 || ok && ParseOneMove(move, gameMode == EditPosition ? blackPlaysFirst : currentMove,
\r
6393 &moveType, &fromX, &fromY, &toX, &toY, &promoChar)) {
\r
6394 if (gameMode != Training)
\r
6395 forwardMostMove = currentMove;
\r
6396 UserMoveEvent(fromX, fromY, toX, toY, promoChar);
\r
6398 DisplayMoveError(_("Could not parse move"));
\r
6401 EndDialog(hDlg, TRUE);
\r
6404 EndDialog(hDlg, FALSE);
\r
6415 PopUpMoveDialog(char firstchar)
\r
6419 if ((gameMode == BeginningOfGame && !appData.icsActive) ||
\r
6420 gameMode == MachinePlaysWhite || gameMode == MachinePlaysBlack ||
\r
6421 gameMode == AnalyzeMode || gameMode == EditGame ||
\r
6422 gameMode == EditPosition || gameMode == IcsExamining ||
\r
6423 gameMode == IcsPlayingWhite || gameMode == IcsPlayingBlack ||
\r
6424 isdigit(firstchar) && // [HGM] movenum: allow typing in of move nr in 'passive' modes
\r
6425 ( gameMode == AnalyzeFile || gameMode == PlayFromGameFile ||
\r
6426 gameMode == IcsObserving || gameMode == TwoMachinesPlay ) ||
\r
6427 gameMode == Training) {
\r
6428 lpProc = MakeProcInstance((FARPROC)TypeInMoveDialog, hInst);
\r
6429 DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_TypeInMove),
\r
6430 hwndMain, (DLGPROC)lpProc, (LPARAM)firstchar);
\r
6431 FreeProcInstance(lpProc);
\r
6435 /*---------------------------------------------------------------------------*\
\r
6437 * Type-in name dialog functions
\r
6439 \*---------------------------------------------------------------------------*/
\r
6442 TypeInNameDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
6444 char move[MSG_SIZ];
\r
6447 switch (message) {
\r
6448 case WM_INITDIALOG:
\r
6449 move[0] = (char) lParam;
\r
6450 move[1] = NULLCHAR;
\r
6451 CenterWindowEx(hDlg, GetWindow(hDlg, GW_OWNER), 1 );
\r
6452 Translate(hDlg, DLG_TypeInName);
\r
6453 hInput = GetDlgItem(hDlg, OPT_Name);
\r
6454 SetWindowText(hInput, move);
\r
6456 SendMessage(hInput, EM_SETSEL, (WPARAM)9999, (LPARAM)9999);
\r
6460 switch (LOWORD(wParam)) {
\r
6462 GetDlgItemText(hDlg, OPT_Name, move, sizeof(move));
\r
6463 appData.userName = strdup(move);
\r
6466 if(gameMode == MachinePlaysWhite || gameMode == MachinePlaysBlack) {
\r
6467 snprintf(move, MSG_SIZ, "%s vs. %s", gameInfo.white, gameInfo.black);
\r
6468 DisplayTitle(move);
\r
6472 EndDialog(hDlg, TRUE);
\r
6475 EndDialog(hDlg, FALSE);
\r
6486 PopUpNameDialog(char firstchar)
\r
6490 lpProc = MakeProcInstance((FARPROC)TypeInNameDialog, hInst);
\r
6491 DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_TypeInName),
\r
6492 hwndMain, (DLGPROC)lpProc, (LPARAM)firstchar);
\r
6493 FreeProcInstance(lpProc);
\r
6496 /*---------------------------------------------------------------------------*\
\r
6500 \*---------------------------------------------------------------------------*/
\r
6502 /* Nonmodal error box */
\r
6503 LRESULT CALLBACK ErrorDialog(HWND hDlg, UINT message,
\r
6504 WPARAM wParam, LPARAM lParam);
\r
6507 ErrorPopUp(char *title, char *content)
\r
6511 BOOLEAN modal = hwndMain == NULL;
\r
6529 strncpy(errorTitle, title, sizeof(errorTitle));
\r
6530 errorTitle[sizeof(errorTitle) - 1] = '\0';
\r
6533 MessageBox(NULL, errorMessage, errorTitle, MB_OK|MB_ICONEXCLAMATION);
\r
6535 lpProc = MakeProcInstance((FARPROC)ErrorDialog, hInst);
\r
6536 CreateDialog(hInst, MAKEINTRESOURCE(DLG_Error),
\r
6537 hwndMain, (DLGPROC)lpProc);
\r
6538 FreeProcInstance(lpProc);
\r
6545 if (!appData.popupMoveErrors && moveErrorMessageUp) DisplayMessage("", "");
\r
6546 if (errorDialog == NULL) return;
\r
6547 DestroyWindow(errorDialog);
\r
6548 errorDialog = NULL;
\r
6549 if(errorExitStatus) ExitEvent(errorExitStatus);
\r
6553 ErrorDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
6558 switch (message) {
\r
6559 case WM_INITDIALOG:
\r
6560 GetWindowRect(hDlg, &rChild);
\r
6563 SetWindowPos(hDlg, NULL, rChild.left,
\r
6564 rChild.top + boardRect.top - (rChild.bottom - rChild.top),
\r
6565 0, 0, SWP_NOZORDER|SWP_NOSIZE);
\r
6569 [AS] It seems that the above code wants to move the dialog up in the "caption
\r
6570 area" of the main window, but it uses the dialog height as an hard-coded constant,
\r
6571 and it doesn't work when you resize the dialog.
\r
6572 For now, just give it a default position.
\r
6574 SetWindowPos(hDlg, NULL, boardRect.left+8, boardRect.top+8, 0, 0, SWP_NOZORDER|SWP_NOSIZE);
\r
6575 Translate(hDlg, DLG_Error);
\r
6577 errorDialog = hDlg;
\r
6578 SetWindowText(hDlg, errorTitle);
\r
6579 hwndText = GetDlgItem(hDlg, OPT_ErrorText);
\r
6580 SetDlgItemText(hDlg, OPT_ErrorText, errorMessage);
\r
6584 switch (LOWORD(wParam)) {
\r
6587 if (errorDialog == hDlg) errorDialog = NULL;
\r
6588 DestroyWindow(hDlg);
\r
6600 HWND gothicDialog = NULL;
\r
6603 GothicDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
6607 int height = GetSystemMetrics(SM_CYCAPTION)+GetSystemMetrics(SM_CYFRAME);
\r
6609 switch (message) {
\r
6610 case WM_INITDIALOG:
\r
6611 GetWindowRect(hDlg, &rChild);
\r
6613 SetWindowPos(hDlg, NULL, wpMain.x, wpMain.y-height, wpMain.width, height,
\r
6617 [AS] It seems that the above code wants to move the dialog up in the "caption
\r
6618 area" of the main window, but it uses the dialog height as an hard-coded constant,
\r
6619 and it doesn't work when you resize the dialog.
\r
6620 For now, just give it a default position.
\r
6622 gothicDialog = hDlg;
\r
6623 SetWindowText(hDlg, errorTitle);
\r
6624 hwndText = GetDlgItem(hDlg, OPT_ErrorText);
\r
6625 SetDlgItemText(hDlg, OPT_ErrorText, errorMessage);
\r
6629 switch (LOWORD(wParam)) {
\r
6632 if (errorDialog == hDlg) errorDialog = NULL;
\r
6633 DestroyWindow(hDlg);
\r
6645 GothicPopUp(char *title, VariantClass variant)
\r
6648 static char *lastTitle;
\r
6650 strncpy(errorTitle, title, sizeof(errorTitle));
\r
6651 errorTitle[sizeof(errorTitle) - 1] = '\0';
\r
6653 if(lastTitle != title && gothicDialog != NULL) {
\r
6654 DestroyWindow(gothicDialog);
\r
6655 gothicDialog = NULL;
\r
6657 if(variant != VariantNormal && gothicDialog == NULL) {
\r
6658 title = lastTitle;
\r
6659 lpProc = MakeProcInstance((FARPROC)GothicDialog, hInst);
\r
6660 CreateDialog(hInst, MAKEINTRESOURCE(DLG_Error),
\r
6661 hwndMain, (DLGPROC)lpProc);
\r
6662 FreeProcInstance(lpProc);
\r
6667 /*---------------------------------------------------------------------------*\
\r
6669 * Ics Interaction console functions
\r
6671 \*---------------------------------------------------------------------------*/
\r
6673 #define HISTORY_SIZE 64
\r
6674 static char *history[HISTORY_SIZE];
\r
6675 int histIn = 0, histP = 0;
\r
6678 SaveInHistory(char *cmd)
\r
6680 if (history[histIn] != NULL) {
\r
6681 free(history[histIn]);
\r
6682 history[histIn] = NULL;
\r
6684 if (*cmd == NULLCHAR) return;
\r
6685 history[histIn] = StrSave(cmd);
\r
6686 histIn = (histIn + 1) % HISTORY_SIZE;
\r
6687 if (history[histIn] != NULL) {
\r
6688 free(history[histIn]);
\r
6689 history[histIn] = NULL;
\r
6695 PrevInHistory(char *cmd)
\r
6698 if (histP == histIn) {
\r
6699 if (history[histIn] != NULL) free(history[histIn]);
\r
6700 history[histIn] = StrSave(cmd);
\r
6702 newhp = (histP - 1 + HISTORY_SIZE) % HISTORY_SIZE;
\r
6703 if (newhp == histIn || history[newhp] == NULL) return NULL;
\r
6705 return history[histP];
\r
6711 if (histP == histIn) return NULL;
\r
6712 histP = (histP + 1) % HISTORY_SIZE;
\r
6713 return history[histP];
\r
6717 LoadIcsTextMenu(IcsTextMenuEntry *e)
\r
6721 hmenu = LoadMenu(hInst, "TextMenu");
\r
6722 h = GetSubMenu(hmenu, 0);
\r
6724 if (strcmp(e->item, "-") == 0) {
\r
6725 AppendMenu(h, MF_SEPARATOR, 0, 0);
\r
6726 } else { // [HGM] re-written a bit to use only one AppendMenu call for both cases (| or no |)
\r
6727 int flags = MF_STRING, j = 0;
\r
6728 if (e->item[0] == '|') {
\r
6729 flags |= MF_MENUBARBREAK;
\r
6732 if(!strcmp(e->command, "none")) flags |= MF_GRAYED; // [HGM] chatclick: provide inactive dummy
\r
6733 AppendMenu(h, flags, IDM_CommandX + i, e->item + j);
\r
6741 WNDPROC consoleTextWindowProc;
\r
6744 CommandX(HWND hwnd, char *command, BOOLEAN getname, BOOLEAN immediate)
\r
6746 char buf[MSG_SIZ], name[MSG_SIZ];
\r
6747 HWND hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
6751 SetWindowText(hInput, command);
\r
6753 SendMessage(hInput, WM_CHAR, '\r', 0);
\r
6755 sel.cpMin = 999999;
\r
6756 sel.cpMax = 999999;
\r
6757 SendMessage(hInput, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
6762 SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);
\r
6763 if (sel.cpMin == sel.cpMax) {
\r
6764 /* Expand to surrounding word */
\r
6767 tr.chrg.cpMax = sel.cpMin;
\r
6768 tr.chrg.cpMin = --sel.cpMin;
\r
6769 if (sel.cpMin < 0) break;
\r
6770 tr.lpstrText = name;
\r
6771 SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr);
\r
6772 } while (isalpha(name[0]) || isdigit(name[0]) || name[0] == '-');
\r
6776 tr.chrg.cpMin = sel.cpMax;
\r
6777 tr.chrg.cpMax = ++sel.cpMax;
\r
6778 tr.lpstrText = name;
\r
6779 if (SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr) < 1) break;
\r
6780 } while (isalpha(name[0]) || isdigit(name[0]) || name[0] == '-');
\r
6783 if (sel.cpMax == sel.cpMin || sel.cpMax - sel.cpMin > MSG_SIZ/2) {
\r
6784 MessageBeep(MB_ICONEXCLAMATION);
\r
6788 tr.lpstrText = name;
\r
6789 SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr);
\r
6791 if (sel.cpMax - sel.cpMin > MSG_SIZ/2) {
\r
6792 MessageBeep(MB_ICONEXCLAMATION);
\r
6795 SendMessage(hwnd, EM_GETSELTEXT, 0, (LPARAM) name);
\r
6798 snprintf(buf, MSG_SIZ, "%s %s", command, name);
\r
6799 SetWindowText(hInput, buf);
\r
6800 SendMessage(hInput, WM_CHAR, '\r', 0);
\r
6802 if(!strcmp(command, "chat")) { ChatPopUp(name); return; }
\r
6803 snprintf(buf, MSG_SIZ, "%s %s ", command, name); /* trailing space */
\r
6804 SetWindowText(hInput, buf);
\r
6805 sel.cpMin = 999999;
\r
6806 sel.cpMax = 999999;
\r
6807 SendMessage(hInput, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
6813 ConsoleTextSubclass(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
\r
6818 switch (message) {
\r
6820 if (!(GetKeyState(VK_CONTROL) & ~1)) break;
\r
6823 SendMessage(hwnd, EM_LINESCROLL, 0, -999999);
\r
6826 sel.cpMin = 999999;
\r
6827 sel.cpMax = 999999;
\r
6828 SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
6829 SendMessage(hwnd, EM_SCROLLCARET, 0, 0);
\r
6834 if(wParam != '\022') {
\r
6835 if (wParam == '\t') {
\r
6836 if (GetKeyState(VK_SHIFT) < 0) {
\r
6838 if (IsIconic(hwndMain)) ShowWindow(hwndMain, SW_RESTORE);
\r
6839 if (buttonDesc[0].hwnd) {
\r
6840 SetFocus(buttonDesc[0].hwnd);
\r
6842 SetFocus(hwndMain);
\r
6846 SetFocus(GetDlgItem(hwndConsole, OPT_ConsoleInput));
\r
6849 hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
6850 JAWS_DELETE( SetFocus(hInput); )
\r
6851 SendMessage(hInput, message, wParam, lParam);
\r
6854 } // [HGM] navigate: for Ctrl+R, flow into nex case (moved up here) to summon up menu
\r
6855 case WM_RBUTTONDOWN:
\r
6856 if (!(GetKeyState(VK_SHIFT) & ~1)) {
\r
6857 /* Move selection here if it was empty */
\r
6859 pt.x = LOWORD(lParam);
\r
6860 pt.y = HIWORD(lParam);
\r
6861 SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);
\r
6862 if (sel.cpMin == sel.cpMax) {
\r
6863 sel.cpMin = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&pt); /*doc is wrong*/
\r
6864 sel.cpMax = sel.cpMin;
\r
6865 SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
6867 SendMessage(hwnd, EM_HIDESELECTION, FALSE, FALSE);
\r
6868 { // [HGM] chatclick: code moved here from WM_RBUTTONUP case, to have menu appear on down-click
\r
6870 HMENU hmenu = LoadIcsTextMenu(icsTextMenuEntry);
\r
6871 SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);
\r
6872 if (sel.cpMin == sel.cpMax) {
\r
6873 EnableMenuItem(hmenu, IDM_Copy, MF_BYCOMMAND|MF_GRAYED);
\r
6874 EnableMenuItem(hmenu, IDM_QuickPaste, MF_BYCOMMAND|MF_GRAYED);
\r
6876 if (!IsClipboardFormatAvailable(CF_TEXT)) {
\r
6877 EnableMenuItem(hmenu, IDM_Paste, MF_BYCOMMAND|MF_GRAYED);
\r
6879 pt.x = LOWORD(lParam)-30; // [HGM] chatclick: make menu pop up with pointer above upper-right item
\r
6880 pt.y = HIWORD(lParam)-10; // make it appear as if mouse moved there, so it will be selected on up-click
\r
6881 PostMessage(hwnd, WM_MOUSEMOVE, wParam, lParam+5);
\r
6882 MenuPopup(hwnd, pt, hmenu, -1);
\r
6886 case WM_RBUTTONUP:
\r
6887 if (GetKeyState(VK_SHIFT) & ~1) {
\r
6888 SendDlgItemMessage(hwndConsole, OPT_ConsoleText,
\r
6889 WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);
\r
6893 hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
6895 return SendMessage(hInput, message, wParam, lParam);
\r
6896 case WM_MBUTTONDOWN:
\r
6897 return SendMessage(hwnd, WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);
\r
6899 switch (LOWORD(wParam)) {
\r
6900 case IDM_QuickPaste:
\r
6902 SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);
\r
6903 if (sel.cpMin == sel.cpMax) {
\r
6904 MessageBeep(MB_ICONEXCLAMATION);
\r
6907 SendMessage(hwnd, WM_COPY, 0, 0);
\r
6908 hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
6909 SendMessage(hInput, WM_PASTE, 0, 0);
\r
6914 SendMessage(hwnd, WM_CUT, 0, 0);
\r
6917 SendMessage(hwnd, WM_PASTE, 0, 0);
\r
6920 SendMessage(hwnd, WM_COPY, 0, 0);
\r
6924 int i = LOWORD(wParam) - IDM_CommandX;
\r
6925 if (i >= 0 && i < ICS_TEXT_MENU_SIZE &&
\r
6926 icsTextMenuEntry[i].command != NULL) {
\r
6927 CommandX(hwnd, icsTextMenuEntry[i].command,
\r
6928 icsTextMenuEntry[i].getname,
\r
6929 icsTextMenuEntry[i].immediate);
\r
6937 return (*consoleTextWindowProc)(hwnd, message, wParam, lParam);
\r
6940 WNDPROC consoleInputWindowProc;
\r
6943 ConsoleInputSubclass(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
\r
6945 char buf[MSG_SIZ];
\r
6947 static BOOL sendNextChar = FALSE;
\r
6948 static BOOL quoteNextChar = FALSE;
\r
6949 InputSource *is = consoleInputSource;
\r
6953 switch (message) {
\r
6955 if (!appData.localLineEditing || sendNextChar) {
\r
6956 is->buf[0] = (CHAR) wParam;
\r
6958 SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);
\r
6959 sendNextChar = FALSE;
\r
6962 if (quoteNextChar) {
\r
6963 buf[0] = (char) wParam;
\r
6964 buf[1] = NULLCHAR;
\r
6965 SendMessage(hwnd, EM_REPLACESEL, TRUE, (LPARAM) buf);
\r
6966 quoteNextChar = FALSE;
\r
6970 case '\r': /* Enter key */
\r
6971 is->count = GetWindowText(hwnd, is->buf, INPUT_SOURCE_BUF_SIZE-1);
\r
6972 if (consoleEcho) SaveInHistory(is->buf);
\r
6973 is->buf[is->count++] = '\n';
\r
6974 is->buf[is->count] = NULLCHAR;
\r
6975 SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);
\r
6976 if (consoleEcho) {
\r
6977 ConsoleOutput(is->buf, is->count, TRUE);
\r
6978 } else if (appData.localLineEditing) {
\r
6979 ConsoleOutput("\n", 1, TRUE);
\r
6982 case '\033': /* Escape key */
\r
6983 SetWindowText(hwnd, "");
\r
6984 cf.cbSize = sizeof(CHARFORMAT);
\r
6985 cf.dwMask = CFM_COLOR|CFM_BOLD|CFM_ITALIC|CFM_UNDERLINE|CFM_STRIKEOUT;
\r
6986 if (consoleEcho) {
\r
6987 cf.crTextColor = textAttribs[ColorNormal].color;
\r
6989 cf.crTextColor = COLOR_ECHOOFF;
\r
6991 cf.dwEffects = textAttribs[ColorNormal].effects;
\r
6992 SendMessage(hwnd, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf);
\r
6994 case '\t': /* Tab key */
\r
6995 if (GetKeyState(VK_SHIFT) < 0) {
\r
6997 SetFocus(GetDlgItem(hwndConsole, OPT_ConsoleText));
\r
7000 if (IsIconic(hwndMain)) ShowWindow(hwndMain, SW_RESTORE);
\r
7001 if (buttonDesc[0].hwnd) {
\r
7002 SetFocus(buttonDesc[0].hwnd);
\r
7004 SetFocus(hwndMain);
\r
7008 case '\023': /* Ctrl+S */
\r
7009 sendNextChar = TRUE;
\r
7011 case '\021': /* Ctrl+Q */
\r
7012 quoteNextChar = TRUE;
\r
7022 GetWindowText(hwnd, buf, MSG_SIZ);
\r
7023 p = PrevInHistory(buf);
\r
7025 SetWindowText(hwnd, p);
\r
7026 sel.cpMin = 999999;
\r
7027 sel.cpMax = 999999;
\r
7028 SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
7033 p = NextInHistory();
\r
7035 SetWindowText(hwnd, p);
\r
7036 sel.cpMin = 999999;
\r
7037 sel.cpMax = 999999;
\r
7038 SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
7044 if (!(GetKeyState(VK_CONTROL) & ~1)) break;
\r
7048 SendDlgItemMessage(hwndConsole, OPT_ConsoleText, message, wParam, lParam);
\r
7052 case WM_MBUTTONDOWN:
\r
7053 SendDlgItemMessage(hwndConsole, OPT_ConsoleText,
\r
7054 WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);
\r
7056 case WM_RBUTTONUP:
\r
7057 if (GetKeyState(VK_SHIFT) & ~1) {
\r
7058 SendDlgItemMessage(hwndConsole, OPT_ConsoleText,
\r
7059 WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);
\r
7063 hmenu = LoadMenu(hInst, "InputMenu");
\r
7064 SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);
\r
7065 if (sel.cpMin == sel.cpMax) {
\r
7066 EnableMenuItem(hmenu, IDM_Copy, MF_BYCOMMAND|MF_GRAYED);
\r
7067 EnableMenuItem(hmenu, IDM_Cut, MF_BYCOMMAND|MF_GRAYED);
\r
7069 if (!IsClipboardFormatAvailable(CF_TEXT)) {
\r
7070 EnableMenuItem(hmenu, IDM_Paste, MF_BYCOMMAND|MF_GRAYED);
\r
7072 pt.x = LOWORD(lParam);
\r
7073 pt.y = HIWORD(lParam);
\r
7074 MenuPopup(hwnd, pt, hmenu, -1);
\r
7078 switch (LOWORD(wParam)) {
\r
7080 SendMessage(hwnd, EM_UNDO, 0, 0);
\r
7082 case IDM_SelectAll:
\r
7084 sel.cpMax = -1; /*999999?*/
\r
7085 SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
7088 SendMessage(hwnd, WM_CUT, 0, 0);
\r
7091 SendMessage(hwnd, WM_PASTE, 0, 0);
\r
7094 SendMessage(hwnd, WM_COPY, 0, 0);
\r
7099 return (*consoleInputWindowProc)(hwnd, message, wParam, lParam);
\r
7102 #define CO_MAX 100000
\r
7103 #define CO_TRIM 1000
\r
7106 ConsoleWndProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
7108 static SnapData sd;
\r
7109 HWND hText, hInput;
\r
7111 static int sizeX, sizeY;
\r
7112 int newSizeX, newSizeY;
\r
7116 hText = GetDlgItem(hDlg, OPT_ConsoleText);
\r
7117 hInput = GetDlgItem(hDlg, OPT_ConsoleInput);
\r
7119 switch (message) {
\r
7121 if (((NMHDR*)lParam)->code == EN_LINK)
\r
7123 ENLINK *pLink = (ENLINK*)lParam;
\r
7124 if (pLink->msg == WM_LBUTTONUP)
\r
7128 tr.chrg = pLink->chrg;
\r
7129 tr.lpstrText = malloc(1+tr.chrg.cpMax-tr.chrg.cpMin);
\r
7130 SendMessage(hText, EM_GETTEXTRANGE, 0, (LPARAM)&tr);
\r
7131 ShellExecute(NULL, "open", tr.lpstrText, NULL, NULL, SW_SHOW);
\r
7132 free(tr.lpstrText);
\r
7136 case WM_INITDIALOG: /* message: initialize dialog box */
\r
7137 hwndConsole = hDlg;
\r
7139 consoleTextWindowProc = (WNDPROC)
\r
7140 SetWindowLong(hText, GWL_WNDPROC, (LONG) ConsoleTextSubclass);
\r
7141 SendMessage(hText, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);
\r
7142 consoleInputWindowProc = (WNDPROC)
\r
7143 SetWindowLong(hInput, GWL_WNDPROC, (LONG) ConsoleInputSubclass);
\r
7144 SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);
\r
7145 Colorize(ColorNormal, TRUE);
\r
7146 SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &consoleCF);
\r
7147 ChangedConsoleFont();
\r
7148 GetClientRect(hDlg, &rect);
\r
7149 sizeX = rect.right;
\r
7150 sizeY = rect.bottom;
\r
7151 if (wpConsole.x != CW_USEDEFAULT && wpConsole.y != CW_USEDEFAULT &&
\r
7152 wpConsole.width != CW_USEDEFAULT && wpConsole.height != CW_USEDEFAULT) {
\r
7153 WINDOWPLACEMENT wp;
\r
7154 EnsureOnScreen(&wpConsole.x, &wpConsole.y, 0, 0);
\r
7155 wp.length = sizeof(WINDOWPLACEMENT);
\r
7157 wp.showCmd = SW_SHOW;
\r
7158 wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;
\r
7159 wp.rcNormalPosition.left = wpConsole.x;
\r
7160 wp.rcNormalPosition.right = wpConsole.x + wpConsole.width;
\r
7161 wp.rcNormalPosition.top = wpConsole.y;
\r
7162 wp.rcNormalPosition.bottom = wpConsole.y + wpConsole.height;
\r
7163 SetWindowPlacement(hDlg, &wp);
\r
7166 // [HGM] Chessknight's change 2004-07-13
\r
7167 else { /* Determine Defaults */
\r
7168 WINDOWPLACEMENT wp;
\r
7169 wpConsole.x = wpMain.width + 1;
\r
7170 wpConsole.y = wpMain.y;
\r
7171 wpConsole.width = screenWidth - wpMain.width;
\r
7172 wpConsole.height = wpMain.height;
\r
7173 EnsureOnScreen(&wpConsole.x, &wpConsole.y, 0, 0);
\r
7174 wp.length = sizeof(WINDOWPLACEMENT);
\r
7176 wp.showCmd = SW_SHOW;
\r
7177 wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;
\r
7178 wp.rcNormalPosition.left = wpConsole.x;
\r
7179 wp.rcNormalPosition.right = wpConsole.x + wpConsole.width;
\r
7180 wp.rcNormalPosition.top = wpConsole.y;
\r
7181 wp.rcNormalPosition.bottom = wpConsole.y + wpConsole.height;
\r
7182 SetWindowPlacement(hDlg, &wp);
\r
7185 // Allow hText to highlight URLs and send notifications on them
\r
7186 wMask = (WORD) SendMessage(hText, EM_GETEVENTMASK, 0, 0L);
\r
7187 SendMessage(hText, EM_SETEVENTMASK, 0, wMask | ENM_LINK);
\r
7188 SendMessage(hText, EM_AUTOURLDETECT, TRUE, 0L);
\r
7189 SetWindowLong(hText, GWL_USERDATA, 79); // initialize the text window's width
\r
7203 if (IsIconic(hDlg)) break;
\r
7204 newSizeX = LOWORD(lParam);
\r
7205 newSizeY = HIWORD(lParam);
\r
7206 if (sizeX != newSizeX || sizeY != newSizeY) {
\r
7207 RECT rectText, rectInput;
\r
7209 int newTextHeight, newTextWidth;
\r
7210 GetWindowRect(hText, &rectText);
\r
7211 newTextWidth = rectText.right - rectText.left + newSizeX - sizeX;
\r
7212 newTextHeight = rectText.bottom - rectText.top + newSizeY - sizeY;
\r
7213 if (newTextHeight < 0) {
\r
7214 newSizeY += -newTextHeight;
\r
7215 newTextHeight = 0;
\r
7217 SetWindowPos(hText, NULL, 0, 0,
\r
7218 newTextWidth, newTextHeight, SWP_NOZORDER|SWP_NOMOVE);
\r
7219 GetWindowRect(hInput, &rectInput); /* gives screen coords */
\r
7220 pt.x = rectInput.left;
\r
7221 pt.y = rectInput.top + newSizeY - sizeY;
\r
7222 ScreenToClient(hDlg, &pt);
\r
7223 SetWindowPos(hInput, NULL,
\r
7224 pt.x, pt.y, /* needs client coords */
\r
7225 rectInput.right - rectInput.left + newSizeX - sizeX,
\r
7226 rectInput.bottom - rectInput.top, SWP_NOZORDER);
\r
7232 case WM_GETMINMAXINFO:
\r
7233 /* Prevent resizing window too small */
\r
7234 mmi = (MINMAXINFO *) lParam;
\r
7235 mmi->ptMinTrackSize.x = 100;
\r
7236 mmi->ptMinTrackSize.y = 100;
\r
7239 /* [AS] Snapping */
\r
7240 case WM_ENTERSIZEMOVE:
\r
7241 return OnEnterSizeMove( &sd, hDlg, wParam, lParam );
\r
7244 return OnSizing( &sd, hDlg, wParam, lParam );
\r
7247 return OnMoving( &sd, hDlg, wParam, lParam );
\r
7249 case WM_EXITSIZEMOVE:
\r
7250 UpdateICSWidth(hText);
\r
7251 return OnExitSizeMove( &sd, hDlg, wParam, lParam );
\r
7254 return DefWindowProc(hDlg, message, wParam, lParam);
\r
7262 if (hwndConsole) return;
\r
7263 hCons = CreateDialog(hInst, szConsoleName, 0, NULL);
\r
7264 SendMessage(hCons, WM_INITDIALOG, 0, 0);
\r
7269 ConsoleOutput(char* data, int length, int forceVisible)
\r
7274 char buf[CO_MAX+1];
\r
7277 static int delayLF = 0;
\r
7278 CHARRANGE savesel, sel;
\r
7280 if (hwndConsole == NULL || length > CO_MAX-100 || length == 0) return;
\r
7288 while (length--) {
\r
7296 } else if (*p == '\007') {
\r
7297 MyPlaySound(&sounds[(int)SoundBell]);
\r
7304 hText = GetDlgItem(hwndConsole, OPT_ConsoleText);
\r
7305 SendMessage(hText, EM_HIDESELECTION, TRUE, FALSE);
\r
7306 /* Save current selection */
\r
7307 SendMessage(hText, EM_EXGETSEL, 0, (LPARAM)&savesel);
\r
7308 exlen = GetWindowTextLength(hText);
\r
7309 /* Find out whether current end of text is visible */
\r
7310 SendMessage(hText, EM_GETRECT, 0, (LPARAM) &rect);
\r
7311 SendMessage(hText, EM_POSFROMCHAR, (WPARAM) &pEnd, exlen);
\r
7312 /* Trim existing text if it's too long */
\r
7313 if (exlen + (q - buf) > CO_MAX) {
\r
7314 trim = (CO_TRIM > (q - buf)) ? CO_TRIM : (q - buf);
\r
7317 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
7318 SendMessage(hText, EM_REPLACESEL, 0, (LPARAM)"");
\r
7320 savesel.cpMin -= trim;
\r
7321 savesel.cpMax -= trim;
\r
7322 if (exlen < 0) exlen = 0;
\r
7323 if (savesel.cpMin < 0) savesel.cpMin = 0;
\r
7324 if (savesel.cpMax < savesel.cpMin) savesel.cpMax = savesel.cpMin;
\r
7326 /* Append the new text */
\r
7327 sel.cpMin = exlen;
\r
7328 sel.cpMax = exlen;
\r
7329 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
7330 SendMessage(hText, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&consoleCF);
\r
7331 SendMessage(hText, EM_REPLACESEL, 0, (LPARAM) buf);
\r
7332 if (forceVisible || exlen == 0 ||
\r
7333 (rect.left <= pEnd.x && pEnd.x < rect.right &&
\r
7334 rect.top <= pEnd.y && pEnd.y < rect.bottom)) {
\r
7335 /* Scroll to make new end of text visible if old end of text
\r
7336 was visible or new text is an echo of user typein */
\r
7337 sel.cpMin = 9999999;
\r
7338 sel.cpMax = 9999999;
\r
7339 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
7340 SendMessage(hText, EM_HIDESELECTION, FALSE, FALSE);
\r
7341 SendMessage(hText, EM_SCROLLCARET, 0, 0);
\r
7342 SendMessage(hText, EM_HIDESELECTION, TRUE, FALSE);
\r
7344 if (savesel.cpMax == exlen || forceVisible) {
\r
7345 /* Move insert point to new end of text if it was at the old
\r
7346 end of text or if the new text is an echo of user typein */
\r
7347 sel.cpMin = 9999999;
\r
7348 sel.cpMax = 9999999;
\r
7349 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
7351 /* Restore previous selection */
\r
7352 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&savesel);
\r
7354 SendMessage(hText, EM_HIDESELECTION, FALSE, FALSE);
\r
7361 DisplayHoldingsCount(HDC hdc, int x, int y, int rightAlign, int copyNumber)
\r
7365 COLORREF oldFg, oldBg;
\r
7370 snprintf(buf, sizeof(buf)/sizeof(buf[0]),"%d", copyNumber); else buf[0] = 0;
\r
7372 oldFg = SetTextColor(hdc, RGB(255, 255, 255)); /* white */
\r
7373 oldBg = SetBkColor(hdc, RGB(0, 0, 0)); /* black */
\r
7374 oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);
\r
7377 rect.right = x + squareSize;
\r
7379 rect.bottom = y + squareSize;
\r
7382 ExtTextOut(hdc, x + MESSAGE_LINE_LEFTMARGIN
\r
7383 + (rightAlign ? (squareSize*2)/3 : 0),
\r
7384 y, ETO_CLIPPED|ETO_OPAQUE,
\r
7385 &rect, str, strlen(str), NULL);
\r
7387 (void) SetTextColor(hdc, oldFg);
\r
7388 (void) SetBkColor(hdc, oldBg);
\r
7389 (void) SelectObject(hdc, oldFont);
\r
7393 DisplayAClock(HDC hdc, int timeRemaining, int highlight,
\r
7394 RECT *rect, char *color, char *flagFell)
\r
7398 COLORREF oldFg, oldBg;
\r
7401 if (appData.clockMode) {
\r
7403 snprintf(buf, sizeof(buf)/sizeof(buf[0]), "%c %s %s", color[0], TimeString(timeRemaining), flagFell);
\r
7405 snprintf(buf, sizeof(buf)/sizeof(buf[0]), "%s:%c%s %s", color, (logoHeight>0 ? 0 : ' '), TimeString(timeRemaining), flagFell);
\r
7412 oldFg = SetTextColor(hdc, RGB(255, 255, 255)); /* white */
\r
7413 oldBg = SetBkColor(hdc, RGB(0, 0, 0)); /* black */
\r
7415 oldFg = SetTextColor(hdc, RGB(0, 0, 0)); /* black */
\r
7416 oldBg = SetBkColor(hdc, RGB(255, 255, 255)); /* white */
\r
7418 oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);
\r
7422 ExtTextOut(hdc, rect->left + MESSAGE_LINE_LEFTMARGIN,
\r
7423 rect->top, ETO_CLIPPED|ETO_OPAQUE,
\r
7424 rect, str, strlen(str), NULL);
\r
7425 if(logoHeight > 0 && appData.clockMode) {
\r
7427 snprintf(buf, sizeof(buf)/sizeof(buf[0]), "%s %s", buf+7, flagFell);
\r
7428 r.top = rect->top + logoHeight/2;
\r
7429 r.left = rect->left;
\r
7430 r.right = rect->right;
\r
7431 r.bottom = rect->bottom;
\r
7432 ExtTextOut(hdc, rect->left + MESSAGE_LINE_LEFTMARGIN,
\r
7433 r.top, ETO_CLIPPED|ETO_OPAQUE,
\r
7434 &r, str, strlen(str), NULL);
\r
7436 (void) SetTextColor(hdc, oldFg);
\r
7437 (void) SetBkColor(hdc, oldBg);
\r
7438 (void) SelectObject(hdc, oldFont);
\r
7443 DoReadFile(HANDLE hFile, char *buf, int count, DWORD *outCount,
\r
7449 if( count <= 0 ) {
\r
7450 if (appData.debugMode) {
\r
7451 fprintf( debugFP, "DoReadFile: trying to read past end of buffer, overflow = %d\n", count );
\r
7454 return ERROR_INVALID_USER_BUFFER;
\r
7457 ResetEvent(ovl->hEvent);
\r
7458 ovl->Offset = ovl->OffsetHigh = 0;
\r
7459 ok = ReadFile(hFile, buf, count, outCount, ovl);
\r
7463 err = GetLastError();
\r
7464 if (err == ERROR_IO_PENDING) {
\r
7465 ok = GetOverlappedResult(hFile, ovl, outCount, TRUE);
\r
7469 err = GetLastError();
\r
7476 DoWriteFile(HANDLE hFile, char *buf, int count, DWORD *outCount,
\r
7481 ResetEvent(ovl->hEvent);
\r
7482 ovl->Offset = ovl->OffsetHigh = 0;
\r
7483 ok = WriteFile(hFile, buf, count, outCount, ovl);
\r
7487 err = GetLastError();
\r
7488 if (err == ERROR_IO_PENDING) {
\r
7489 ok = GetOverlappedResult(hFile, ovl, outCount, TRUE);
\r
7493 err = GetLastError();
\r
7499 /* [AS] If input is line by line and a line exceed the buffer size, force an error */
\r
7500 void CheckForInputBufferFull( InputSource * is )
\r
7502 if( is->lineByLine && (is->next - is->buf) >= INPUT_SOURCE_BUF_SIZE ) {
\r
7503 /* Look for end of line */
\r
7504 char * p = is->buf;
\r
7506 while( p < is->next && *p != '\n' ) {
\r
7510 if( p >= is->next ) {
\r
7511 if (appData.debugMode) {
\r
7512 fprintf( debugFP, "Input line exceeded buffer size (source id=%lu)\n", is->id );
\r
7515 is->error = ERROR_BROKEN_PIPE; /* [AS] Just any non-successful code! */
\r
7516 is->count = (DWORD) -1;
\r
7517 is->next = is->buf;
\r
7523 InputThread(LPVOID arg)
\r
7528 is = (InputSource *) arg;
\r
7529 ovl.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
\r
7530 ovl.Internal = ovl.InternalHigh = ovl.Offset = ovl.OffsetHigh = 0;
\r
7531 while (is->hThread != NULL) {
\r
7532 is->error = DoReadFile(is->hFile, is->next,
\r
7533 INPUT_SOURCE_BUF_SIZE - (is->next - is->buf),
\r
7534 &is->count, &ovl);
\r
7535 if (is->error == NO_ERROR) {
\r
7536 is->next += is->count;
\r
7538 if (is->error == ERROR_BROKEN_PIPE) {
\r
7539 /* Correct for MS brain damage. EOF reading a pipe is not an error. */
\r
7542 is->count = (DWORD) -1;
\r
7543 /* [AS] The (is->count <= 0) check below is not useful for unsigned values! */
\r
7548 CheckForInputBufferFull( is );
\r
7550 SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);
\r
7552 if( is->count == ((DWORD) -1) ) break; /* [AS] */
\r
7554 if (is->count <= 0) break; /* Quit on EOF or error */
\r
7557 CloseHandle(ovl.hEvent);
\r
7558 CloseHandle(is->hFile);
\r
7560 if (appData.debugMode) {
\r
7561 fprintf( debugFP, "Input thread terminated (id=%lu, error=%d, count=%ld)\n", is->id, is->error, is->count );
\r
7568 /* Windows 95 beta 2 won't let you do overlapped i/o on a console or pipe */
\r
7570 NonOvlInputThread(LPVOID arg)
\r
7577 is = (InputSource *) arg;
\r
7578 while (is->hThread != NULL) {
\r
7579 is->error = ReadFile(is->hFile, is->next,
\r
7580 INPUT_SOURCE_BUF_SIZE - (is->next - is->buf),
\r
7581 &is->count, NULL) ? NO_ERROR : GetLastError();
\r
7582 if (is->error == NO_ERROR) {
\r
7583 /* Change CRLF to LF */
\r
7584 if (is->next > is->buf) {
\r
7586 i = is->count + 1;
\r
7594 if (prev == '\r' && *p == '\n') {
\r
7606 if (is->error == ERROR_BROKEN_PIPE) {
\r
7607 /* Correct for MS brain damage. EOF reading a pipe is not an error. */
\r
7610 is->count = (DWORD) -1;
\r
7614 CheckForInputBufferFull( is );
\r
7616 SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);
\r
7618 if( is->count == ((DWORD) -1) ) break; /* [AS] */
\r
7620 if (is->count < 0) break; /* Quit on error */
\r
7622 CloseHandle(is->hFile);
\r
7627 SocketInputThread(LPVOID arg)
\r
7631 is = (InputSource *) arg;
\r
7632 while (is->hThread != NULL) {
\r
7633 is->count = recv(is->sock, is->buf, INPUT_SOURCE_BUF_SIZE, 0);
\r
7634 if ((int)is->count == SOCKET_ERROR) {
\r
7635 is->count = (DWORD) -1;
\r
7636 is->error = WSAGetLastError();
\r
7638 is->error = NO_ERROR;
\r
7639 is->next += is->count;
\r
7640 if (is->count == 0 && is->second == is) {
\r
7641 /* End of file on stderr; quit with no message */
\r
7645 SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);
\r
7647 if( is->count == ((DWORD) -1) ) break; /* [AS] */
\r
7649 if (is->count <= 0) break; /* Quit on EOF or error */
\r
7655 InputEvent(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
\r
7659 is = (InputSource *) lParam;
\r
7660 if (is->lineByLine) {
\r
7661 /* Feed in lines one by one */
\r
7662 char *p = is->buf;
\r
7664 while (q < is->next) {
\r
7665 if (*q++ == '\n') {
\r
7666 (is->func)(is, is->closure, p, q - p, NO_ERROR);
\r
7671 /* Move any partial line to the start of the buffer */
\r
7673 while (p < is->next) {
\r
7678 if (is->error != NO_ERROR || is->count == 0) {
\r
7679 /* Notify backend of the error. Note: If there was a partial
\r
7680 line at the end, it is not flushed through. */
\r
7681 (is->func)(is, is->closure, is->buf, is->count, is->error);
\r
7684 /* Feed in the whole chunk of input at once */
\r
7685 (is->func)(is, is->closure, is->buf, is->count, is->error);
\r
7686 is->next = is->buf;
\r
7690 /*---------------------------------------------------------------------------*\
\r
7692 * Menu enables. Used when setting various modes.
\r
7694 \*---------------------------------------------------------------------------*/
\r
7702 GreyRevert(Boolean grey)
\r
7703 { // [HGM] vari: for retracting variations in local mode
\r
7704 HMENU hmenu = GetMenu(hwndMain);
\r
7705 EnableMenuItem(hmenu, IDM_Revert, MF_BYCOMMAND|(grey ? MF_GRAYED : MF_ENABLED));
\r
7706 EnableMenuItem(hmenu, IDM_Annotate, MF_BYCOMMAND|(grey ? MF_GRAYED : MF_ENABLED));
\r
7710 SetMenuEnables(HMENU hmenu, Enables *enab)
\r
7712 while (enab->item > 0) {
\r
7713 (void) EnableMenuItem(hmenu, enab->item, enab->flags);
\r
7718 Enables gnuEnables[] = {
\r
7719 { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },
\r
7720 { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },
\r
7721 { IDM_IcsClient, MF_BYCOMMAND|MF_GRAYED },
\r
7722 { IDM_Accept, MF_BYCOMMAND|MF_GRAYED },
\r
7723 { IDM_Decline, MF_BYCOMMAND|MF_GRAYED },
\r
7724 { IDM_Rematch, MF_BYCOMMAND|MF_GRAYED },
\r
7725 { IDM_Adjourn, MF_BYCOMMAND|MF_GRAYED },
\r
7726 { IDM_StopExamining, MF_BYCOMMAND|MF_GRAYED },
\r
7727 { IDM_StopObserving, MF_BYCOMMAND|MF_GRAYED },
\r
7728 { IDM_Upload, MF_BYCOMMAND|MF_GRAYED },
\r
7729 { IDM_Revert, MF_BYCOMMAND|MF_GRAYED },
\r
7730 { IDM_Annotate, MF_BYCOMMAND|MF_GRAYED },
\r
7731 { IDM_NewChat, MF_BYCOMMAND|MF_GRAYED },
\r
7735 Enables icsEnables[] = {
\r
7736 { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },
\r
7737 { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },
\r
7738 { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },
\r
7739 { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },
\r
7740 { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },
\r
7741 { IDM_Match, MF_BYCOMMAND|MF_GRAYED },
\r
7742 { IDM_MachineBoth, MF_BYCOMMAND|MF_GRAYED },
\r
7743 { IDM_AnalysisMode, MF_BYCOMMAND|MF_ENABLED },
\r
7744 { IDM_AnalyzeFile, MF_BYCOMMAND|MF_GRAYED },
\r
7745 { IDM_TimeControl, MF_BYCOMMAND|MF_GRAYED },
\r
7746 { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },
\r
7747 { IDM_Hint, MF_BYCOMMAND|MF_GRAYED },
\r
7748 { IDM_Book, MF_BYCOMMAND|MF_GRAYED },
\r
7749 { IDM_IcsOptions, MF_BYCOMMAND|MF_ENABLED },
\r
7750 { IDM_Engine1Options, MF_BYCOMMAND|MF_GRAYED },
\r
7751 { IDM_Engine2Options, MF_BYCOMMAND|MF_GRAYED },
\r
7752 { IDM_Annotate, MF_BYCOMMAND|MF_GRAYED },
\r
7757 Enables zippyEnables[] = {
\r
7758 { IDM_MoveNow, MF_BYCOMMAND|MF_ENABLED },
\r
7759 { IDM_Hint, MF_BYCOMMAND|MF_ENABLED },
\r
7760 { IDM_Book, MF_BYCOMMAND|MF_ENABLED },
\r
7761 { IDM_Engine1Options, MF_BYCOMMAND|MF_ENABLED },
\r
7766 Enables ncpEnables[] = {
\r
7767 { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },
\r
7768 { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },
\r
7769 { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },
\r
7770 { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },
\r
7771 { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },
\r
7772 { IDM_Match, MF_BYCOMMAND|MF_GRAYED },
\r
7773 { IDM_AnalysisMode, MF_BYCOMMAND|MF_GRAYED },
\r
7774 { IDM_AnalyzeFile, MF_BYCOMMAND|MF_GRAYED },
\r
7775 { IDM_IcsClient, MF_BYCOMMAND|MF_GRAYED },
\r
7776 { ACTION_POS, MF_BYPOSITION|MF_GRAYED },
\r
7777 { IDM_Revert, MF_BYCOMMAND|MF_GRAYED },
\r
7778 { IDM_Annotate, MF_BYCOMMAND|MF_GRAYED },
\r
7779 { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },
\r
7780 { IDM_RetractMove, MF_BYCOMMAND|MF_GRAYED },
\r
7781 { IDM_TimeControl, MF_BYCOMMAND|MF_GRAYED },
\r
7782 { IDM_Hint, MF_BYCOMMAND|MF_GRAYED },
\r
7783 { IDM_Book, MF_BYCOMMAND|MF_GRAYED },
\r
7784 { IDM_MachineBoth, MF_BYCOMMAND|MF_GRAYED },
\r
7785 { IDM_NewChat, MF_BYCOMMAND|MF_GRAYED },
\r
7786 { IDM_Engine1Options, MF_BYCOMMAND|MF_GRAYED },
\r
7787 { IDM_Engine2Options, MF_BYCOMMAND|MF_GRAYED },
\r
7791 Enables trainingOnEnables[] = {
\r
7792 { IDM_EditComment, MF_BYCOMMAND|MF_GRAYED },
\r
7793 { IDM_Pause, MF_BYCOMMAND|MF_GRAYED },
\r
7794 { IDM_Forward, MF_BYCOMMAND|MF_GRAYED },
\r
7795 { IDM_Backward, MF_BYCOMMAND|MF_GRAYED },
\r
7796 { IDM_ToEnd, MF_BYCOMMAND|MF_GRAYED },
\r
7797 { IDM_ToStart, MF_BYCOMMAND|MF_GRAYED },
\r
7798 { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },
\r
7799 { IDM_TruncateGame, MF_BYCOMMAND|MF_GRAYED },
\r
7803 Enables trainingOffEnables[] = {
\r
7804 { IDM_EditComment, MF_BYCOMMAND|MF_ENABLED },
\r
7805 { IDM_Pause, MF_BYCOMMAND|MF_ENABLED },
\r
7806 { IDM_Forward, MF_BYCOMMAND|MF_ENABLED },
\r
7807 { IDM_Backward, MF_BYCOMMAND|MF_ENABLED },
\r
7808 { IDM_ToEnd, MF_BYCOMMAND|MF_ENABLED },
\r
7809 { IDM_ToStart, MF_BYCOMMAND|MF_ENABLED },
\r
7810 { IDM_MoveNow, MF_BYCOMMAND|MF_ENABLED },
\r
7811 { IDM_TruncateGame, MF_BYCOMMAND|MF_ENABLED },
\r
7815 /* These modify either ncpEnables or gnuEnables */
\r
7816 Enables cmailEnables[] = {
\r
7817 { IDM_MailMove, MF_BYCOMMAND|MF_ENABLED },
\r
7818 { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_ENABLED },
\r
7819 { ACTION_POS, MF_BYPOSITION|MF_ENABLED },
\r
7820 { IDM_CallFlag, MF_BYCOMMAND|MF_GRAYED },
\r
7821 { IDM_Draw, MF_BYCOMMAND|MF_ENABLED },
\r
7822 { IDM_Adjourn, MF_BYCOMMAND|MF_GRAYED },
\r
7823 { IDM_Abort, MF_BYCOMMAND|MF_GRAYED },
\r
7827 Enables machineThinkingEnables[] = {
\r
7828 { IDM_LoadGame, MF_BYCOMMAND|MF_GRAYED },
\r
7829 { IDM_LoadNextGame, MF_BYCOMMAND|MF_GRAYED },
\r
7830 { IDM_LoadPrevGame, MF_BYCOMMAND|MF_GRAYED },
\r
7831 { IDM_ReloadGame, MF_BYCOMMAND|MF_GRAYED },
\r
7832 { IDM_PasteGame, MF_BYCOMMAND|MF_GRAYED },
\r
7833 { IDM_LoadPosition, MF_BYCOMMAND|MF_GRAYED },
\r
7834 { IDM_LoadNextPosition, MF_BYCOMMAND|MF_GRAYED },
\r
7835 { IDM_LoadPrevPosition, MF_BYCOMMAND|MF_GRAYED },
\r
7836 { IDM_ReloadPosition, MF_BYCOMMAND|MF_GRAYED },
\r
7837 { IDM_PastePosition, MF_BYCOMMAND|MF_GRAYED },
\r
7838 { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },
\r
7839 { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },
\r
7840 { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },
\r
7841 { IDM_Match, MF_BYCOMMAND|MF_GRAYED },
\r
7842 { IDM_TypeInMove, MF_BYCOMMAND|MF_GRAYED },
\r
7843 { IDM_RetractMove, MF_BYCOMMAND|MF_GRAYED },
\r
7847 Enables userThinkingEnables[] = {
\r
7848 { IDM_LoadGame, MF_BYCOMMAND|MF_ENABLED },
\r
7849 { IDM_LoadNextGame, MF_BYCOMMAND|MF_ENABLED },
\r
7850 { IDM_LoadPrevGame, MF_BYCOMMAND|MF_ENABLED },
\r
7851 { IDM_ReloadGame, MF_BYCOMMAND|MF_ENABLED },
\r
7852 { IDM_PasteGame, MF_BYCOMMAND|MF_ENABLED },
\r
7853 { IDM_LoadPosition, MF_BYCOMMAND|MF_ENABLED },
\r
7854 { IDM_LoadNextPosition, MF_BYCOMMAND|MF_ENABLED },
\r
7855 { IDM_LoadPrevPosition, MF_BYCOMMAND|MF_ENABLED },
\r
7856 { IDM_ReloadPosition, MF_BYCOMMAND|MF_ENABLED },
\r
7857 { IDM_PastePosition, MF_BYCOMMAND|MF_ENABLED },
\r
7858 { IDM_MachineWhite, MF_BYCOMMAND|MF_ENABLED },
\r
7859 { IDM_MachineBlack, MF_BYCOMMAND|MF_ENABLED },
\r
7860 { IDM_TwoMachines, MF_BYCOMMAND|MF_ENABLED },
\r
7861 { IDM_Match, MF_BYCOMMAND|MF_ENABLED },
\r
7862 { IDM_TypeInMove, MF_BYCOMMAND|MF_ENABLED },
\r
7863 { IDM_RetractMove, MF_BYCOMMAND|MF_ENABLED },
\r
7867 /*---------------------------------------------------------------------------*\
\r
7869 * Front-end interface functions exported by XBoard.
\r
7870 * Functions appear in same order as prototypes in frontend.h.
\r
7872 \*---------------------------------------------------------------------------*/
\r
7876 static UINT prevChecked = 0;
\r
7877 static int prevPausing = 0;
\r
7880 if (pausing != prevPausing) {
\r
7881 prevPausing = pausing;
\r
7882 (void) CheckMenuItem(GetMenu(hwndMain), IDM_Pause,
\r
7883 MF_BYCOMMAND|(pausing ? MF_CHECKED : MF_UNCHECKED));
\r
7884 if (hwndPause) SetWindowText(hwndPause, pausing ? "C" : "P");
\r
7887 switch (gameMode) {
\r
7888 case BeginningOfGame:
\r
7889 if (appData.icsActive)
\r
7890 nowChecked = IDM_IcsClient;
\r
7891 else if (appData.noChessProgram)
\r
7892 nowChecked = IDM_EditGame;
\r
7894 nowChecked = IDM_MachineBlack;
\r
7896 case MachinePlaysBlack:
\r
7897 nowChecked = IDM_MachineBlack;
\r
7899 case MachinePlaysWhite:
\r
7900 nowChecked = IDM_MachineWhite;
\r
7902 case TwoMachinesPlay:
\r
7903 nowChecked = matchMode ? IDM_Match : IDM_TwoMachines; // [HGM] match
\r
7906 nowChecked = IDM_AnalysisMode;
\r
7909 nowChecked = IDM_AnalyzeFile;
\r
7912 nowChecked = IDM_EditGame;
\r
7914 case PlayFromGameFile:
\r
7915 nowChecked = IDM_LoadGame;
\r
7917 case EditPosition:
\r
7918 nowChecked = IDM_EditPosition;
\r
7921 nowChecked = IDM_Training;
\r
7923 case IcsPlayingWhite:
\r
7924 case IcsPlayingBlack:
\r
7925 case IcsObserving:
\r
7927 nowChecked = IDM_IcsClient;
\r
7934 if (prevChecked != 0)
\r
7935 (void) CheckMenuItem(GetMenu(hwndMain),
\r
7936 prevChecked, MF_BYCOMMAND|MF_UNCHECKED);
\r
7937 if (nowChecked != 0)
\r
7938 (void) CheckMenuItem(GetMenu(hwndMain),
\r
7939 nowChecked, MF_BYCOMMAND|MF_CHECKED);
\r
7941 if (nowChecked == IDM_LoadGame || nowChecked == IDM_Training) {
\r
7942 (void) EnableMenuItem(GetMenu(hwndMain), IDM_Training,
\r
7943 MF_BYCOMMAND|MF_ENABLED);
\r
7945 (void) EnableMenuItem(GetMenu(hwndMain),
\r
7946 IDM_Training, MF_BYCOMMAND|MF_GRAYED);
\r
7949 prevChecked = nowChecked;
\r
7951 /* [DM] icsEngineAnalyze - Do a sceure check too */
\r
7952 if (appData.icsActive) {
\r
7953 if (appData.icsEngineAnalyze) {
\r
7954 (void) CheckMenuItem(GetMenu(hwndMain), IDM_AnalysisMode,
\r
7955 MF_BYCOMMAND|MF_CHECKED);
\r
7957 (void) CheckMenuItem(GetMenu(hwndMain), IDM_AnalysisMode,
\r
7958 MF_BYCOMMAND|MF_UNCHECKED);
\r
7966 HMENU hmenu = GetMenu(hwndMain);
\r
7967 SetMenuEnables(hmenu, icsEnables);
\r
7968 EnableMenuItem(GetSubMenu(hmenu, OPTIONS_POS), ICS_POS,
\r
7969 MF_BYPOSITION|MF_ENABLED);
\r
7971 if (appData.zippyPlay) {
\r
7972 SetMenuEnables(hmenu, zippyEnables);
\r
7973 if (!appData.noChessProgram) /* [DM] icsEngineAnalyze */
\r
7974 (void) EnableMenuItem(GetMenu(hwndMain), IDM_AnalysisMode,
\r
7975 MF_BYCOMMAND|MF_ENABLED);
\r
7983 SetMenuEnables(GetMenu(hwndMain), gnuEnables);
\r
7989 HMENU hmenu = GetMenu(hwndMain);
\r
7990 SetMenuEnables(hmenu, ncpEnables);
\r
7991 EnableMenuItem(GetSubMenu(hmenu, OPTIONS_POS), SOUNDS_POS,
\r
7992 MF_BYPOSITION|MF_GRAYED);
\r
7993 DrawMenuBar(hwndMain);
\r
7999 SetMenuEnables(GetMenu(hwndMain), cmailEnables);
\r
8003 SetTrainingModeOn()
\r
8006 SetMenuEnables(GetMenu(hwndMain), trainingOnEnables);
\r
8007 for (i = 0; i < N_BUTTONS; i++) {
\r
8008 if (buttonDesc[i].hwnd != NULL)
\r
8009 EnableWindow(buttonDesc[i].hwnd, FALSE);
\r
8014 VOID SetTrainingModeOff()
\r
8017 SetMenuEnables(GetMenu(hwndMain), trainingOffEnables);
\r
8018 for (i = 0; i < N_BUTTONS; i++) {
\r
8019 if (buttonDesc[i].hwnd != NULL)
\r
8020 EnableWindow(buttonDesc[i].hwnd, TRUE);
\r
8026 SetUserThinkingEnables()
\r
8028 SetMenuEnables(GetMenu(hwndMain), userThinkingEnables);
\r
8032 SetMachineThinkingEnables()
\r
8034 HMENU hMenu = GetMenu(hwndMain);
\r
8035 int flags = MF_BYCOMMAND|MF_ENABLED;
\r
8037 SetMenuEnables(hMenu, machineThinkingEnables);
\r
8039 if (gameMode == MachinePlaysBlack) {
\r
8040 (void)EnableMenuItem(hMenu, IDM_MachineBlack, flags);
\r
8041 } else if (gameMode == MachinePlaysWhite) {
\r
8042 (void)EnableMenuItem(hMenu, IDM_MachineWhite, flags);
\r
8043 } else if (gameMode == TwoMachinesPlay) {
\r
8044 (void)EnableMenuItem(hMenu, matchMode ? IDM_Match : IDM_TwoMachines, flags); // [HGM] match
\r
8050 DisplayTitle(char *str)
\r
8052 char title[MSG_SIZ], *host;
\r
8053 if (str[0] != NULLCHAR) {
\r
8054 safeStrCpy(title, str, sizeof(title)/sizeof(title[0]) );
\r
8055 } else if (appData.icsActive) {
\r
8056 if (appData.icsCommPort[0] != NULLCHAR)
\r
8059 host = appData.icsHost;
\r
8060 snprintf(title, MSG_SIZ, "%s: %s", szTitle, host);
\r
8061 } else if (appData.noChessProgram) {
\r
8062 safeStrCpy(title, szTitle, sizeof(title)/sizeof(title[0]) );
\r
8064 safeStrCpy(title, szTitle, sizeof(title)/sizeof(title[0]) );
\r
8065 strcat(title, ": ");
\r
8066 strcat(title, first.tidy);
\r
8068 SetWindowText(hwndMain, title);
\r
8073 DisplayMessage(char *str1, char *str2)
\r
8077 int remain = MESSAGE_TEXT_MAX - 1;
\r
8080 moveErrorMessageUp = FALSE; /* turned on later by caller if needed */
\r
8081 messageText[0] = NULLCHAR;
\r
8083 len = strlen(str1);
\r
8084 if (len > remain) len = remain;
\r
8085 strncpy(messageText, str1, len);
\r
8086 messageText[len] = NULLCHAR;
\r
8089 if (*str2 && remain >= 2) {
\r
8091 strcat(messageText, " ");
\r
8094 len = strlen(str2);
\r
8095 if (len > remain) len = remain;
\r
8096 strncat(messageText, str2, len);
\r
8098 messageText[MESSAGE_TEXT_MAX - 1] = NULLCHAR;
\r
8100 if (hwndMain == NULL || IsIconic(hwndMain)) return;
\r
8104 hdc = GetDC(hwndMain);
\r
8105 oldFont = SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);
\r
8106 ExtTextOut(hdc, messageRect.left, messageRect.top, ETO_CLIPPED|ETO_OPAQUE,
\r
8107 &messageRect, messageText, strlen(messageText), NULL);
\r
8108 (void) SelectObject(hdc, oldFont);
\r
8109 (void) ReleaseDC(hwndMain, hdc);
\r
8113 DisplayError(char *str, int error)
\r
8115 char buf[MSG_SIZ*2], buf2[MSG_SIZ];
\r
8119 safeStrCpy(buf, str, sizeof(buf)/sizeof(buf[0]) );
\r
8121 len = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,
\r
8122 NULL, error, LANG_NEUTRAL,
\r
8123 (LPSTR) buf2, MSG_SIZ, NULL);
\r
8125 snprintf(buf, 2*MSG_SIZ, "%s:\n%s", str, buf2);
\r
8127 ErrorMap *em = errmap;
\r
8128 while (em->err != 0 && em->err != error) em++;
\r
8129 if (em->err != 0) {
\r
8130 snprintf(buf, 2*MSG_SIZ, "%s:\n%s", str, em->msg);
\r
8132 snprintf(buf, 2*MSG_SIZ, "%s:\nError code %d", str, error);
\r
8137 ErrorPopUp(_("Error"), buf);
\r
8142 DisplayMoveError(char *str)
\r
8144 fromX = fromY = -1;
\r
8145 ClearHighlights();
\r
8146 DrawPosition(FALSE, NULL);
\r
8147 if (appData.popupMoveErrors) {
\r
8148 ErrorPopUp(_("Error"), str);
\r
8150 DisplayMessage(str, "");
\r
8151 moveErrorMessageUp = TRUE;
\r
8156 DisplayFatalError(char *str, int error, int exitStatus)
\r
8158 char buf[2*MSG_SIZ], buf2[MSG_SIZ];
\r
8160 char *label = exitStatus ? _("Fatal Error") : _("Exiting");
\r
8163 len = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,
\r
8164 NULL, error, LANG_NEUTRAL,
\r
8165 (LPSTR) buf2, MSG_SIZ, NULL);
\r
8167 snprintf(buf, 2*MSG_SIZ, "%s:\n%s", str, buf2);
\r
8169 ErrorMap *em = errmap;
\r
8170 while (em->err != 0 && em->err != error) em++;
\r
8171 if (em->err != 0) {
\r
8172 snprintf(buf, 2*MSG_SIZ, "%s:\n%s", str, em->msg);
\r
8174 snprintf(buf, 2*MSG_SIZ, "%s:\nError code %d", str, error);
\r
8179 if (appData.debugMode) {
\r
8180 fprintf(debugFP, "%s: %s\n", label, str);
\r
8182 if (appData.popupExitMessage) {
\r
8183 (void) MessageBox(hwndMain, str, label, MB_OK|
\r
8184 (exitStatus ? MB_ICONSTOP : MB_ICONINFORMATION));
\r
8186 ExitEvent(exitStatus);
\r
8191 DisplayInformation(char *str)
\r
8193 (void) MessageBox(hwndMain, str, _("Information"), MB_OK|MB_ICONINFORMATION);
\r
8198 DisplayNote(char *str)
\r
8200 ErrorPopUp(_("Note"), str);
\r
8205 char *title, *question, *replyPrefix;
\r
8210 QuestionDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
8212 static QuestionParams *qp;
\r
8213 char reply[MSG_SIZ];
\r
8216 switch (message) {
\r
8217 case WM_INITDIALOG:
\r
8218 qp = (QuestionParams *) lParam;
\r
8219 CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));
\r
8220 Translate(hDlg, DLG_Question);
\r
8221 SetWindowText(hDlg, qp->title);
\r
8222 SetDlgItemText(hDlg, OPT_QuestionText, qp->question);
\r
8223 SetFocus(GetDlgItem(hDlg, OPT_QuestionInput));
\r
8227 switch (LOWORD(wParam)) {
\r
8229 safeStrCpy(reply, qp->replyPrefix, sizeof(reply)/sizeof(reply[0]) );
\r
8230 if (*reply) strcat(reply, " ");
\r
8231 len = strlen(reply);
\r
8232 GetDlgItemText(hDlg, OPT_QuestionInput, reply + len, sizeof(reply) - len);
\r
8233 strcat(reply, "\n");
\r
8234 OutputToProcess(qp->pr, reply, strlen(reply), &err);
\r
8235 EndDialog(hDlg, TRUE);
\r
8236 if (err) DisplayFatalError(_("Error writing to chess program"), err, 1);
\r
8239 EndDialog(hDlg, FALSE);
\r
8250 AskQuestion(char* title, char *question, char *replyPrefix, ProcRef pr)
\r
8252 QuestionParams qp;
\r
8256 qp.question = question;
\r
8257 qp.replyPrefix = replyPrefix;
\r
8259 lpProc = MakeProcInstance((FARPROC)QuestionDialog, hInst);
\r
8260 DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_Question),
\r
8261 hwndMain, (DLGPROC)lpProc, (LPARAM)&qp);
\r
8262 FreeProcInstance(lpProc);
\r
8265 /* [AS] Pick FRC position */
\r
8266 LRESULT CALLBACK NewGameFRC_Proc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
8268 static int * lpIndexFRC;
\r
8274 case WM_INITDIALOG:
\r
8275 lpIndexFRC = (int *) lParam;
\r
8277 CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));
\r
8278 Translate(hDlg, DLG_NewGameFRC);
\r
8280 SendDlgItemMessage( hDlg, IDC_NFG_Edit, EM_SETLIMITTEXT, sizeof(buf)-1, 0 );
\r
8281 SetDlgItemInt( hDlg, IDC_NFG_Edit, *lpIndexFRC, TRUE );
\r
8282 SendDlgItemMessage( hDlg, IDC_NFG_Edit, EM_SETSEL, 0, -1 );
\r
8283 SetFocus(GetDlgItem(hDlg, IDC_NFG_Edit));
\r
8288 switch( LOWORD(wParam) ) {
\r
8290 *lpIndexFRC = GetDlgItemInt(hDlg, IDC_NFG_Edit, &index_is_ok, TRUE );
\r
8291 EndDialog( hDlg, 0 );
\r
8292 shuffleOpenings = TRUE; /* [HGM] shuffle: switch shuffling on for as long as we stay in current variant */
\r
8295 EndDialog( hDlg, 1 );
\r
8297 case IDC_NFG_Edit:
\r
8298 if( HIWORD(wParam) == EN_CHANGE ) {
\r
8299 GetDlgItemInt(hDlg, IDC_NFG_Edit, &index_is_ok, TRUE );
\r
8301 EnableWindow( GetDlgItem(hDlg, IDOK), index_is_ok );
\r
8304 case IDC_NFG_Random:
\r
8305 snprintf( buf, sizeof(buf)/sizeof(buf[0]), "%d", myrandom() ); /* [HGM] shuffle: no longer limit to 960 */
\r
8306 SetDlgItemText(hDlg, IDC_NFG_Edit, buf );
\r
8319 int index = appData.defaultFrcPosition;
\r
8320 FARPROC lpProc = MakeProcInstance( (FARPROC) NewGameFRC_Proc, hInst );
\r
8322 result = DialogBoxParam( hInst, MAKEINTRESOURCE(DLG_NewGameFRC), hwndMain, (DLGPROC)lpProc, (LPARAM)&index );
\r
8324 if( result == 0 ) {
\r
8325 appData.defaultFrcPosition = index;
\r
8331 /* [AS] Game list options. Refactored by HGM */
\r
8333 HWND gameListOptionsDialog;
\r
8335 // low-level front-end: clear text edit / list widget
\r
8339 SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_RESETCONTENT, 0, 0 );
\r
8342 // low-level front-end: clear text edit / list widget
\r
8344 GLT_DeSelectList()
\r
8346 SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_SETCURSEL, 0, 0 );
\r
8349 // low-level front-end: append line to text edit / list widget
\r
8351 GLT_AddToList( char *name )
\r
8354 SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_ADDSTRING, 0, (LPARAM) name );
\r
8358 // low-level front-end: get line from text edit / list widget
\r
8360 GLT_GetFromList( int index, char *name )
\r
8363 if( SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_GETTEXT, index, (LPARAM) name ) != LB_ERR )
\r
8369 void GLT_MoveSelection( HWND hDlg, int delta )
\r
8371 int idx1 = (int) SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETCURSEL, 0, 0 );
\r
8372 int idx2 = idx1 + delta;
\r
8373 int count = (int) SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETCOUNT, 0, 0 );
\r
8375 if( idx1 >=0 && idx1 < count && idx2 >= 0 && idx2 < count ) {
\r
8378 SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETTEXT, idx1, (LPARAM) buf );
\r
8379 SendDlgItemMessage( hDlg, IDC_GameListTags, LB_DELETESTRING, idx1, 0 );
\r
8380 SendDlgItemMessage( hDlg, IDC_GameListTags, LB_INSERTSTRING, idx2, (LPARAM) buf );
\r
8381 SendDlgItemMessage( hDlg, IDC_GameListTags, LB_SETCURSEL, idx2, 0 );
\r
8385 LRESULT CALLBACK GameListOptions_Proc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
8389 case WM_INITDIALOG:
\r
8390 gameListOptionsDialog = hDlg; // [HGM] pass through global to keep out off back-end
\r
8392 CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));
\r
8393 Translate(hDlg, DLG_GameListOptions);
\r
8395 /* Initialize list */
\r
8396 GLT_TagsToList( lpUserGLT );
\r
8398 SetFocus( GetDlgItem(hDlg, IDC_GameListTags) );
\r
8403 switch( LOWORD(wParam) ) {
\r
8406 EndDialog( hDlg, 0 );
\r
8409 EndDialog( hDlg, 1 );
\r
8412 case IDC_GLT_Default:
\r
8413 GLT_TagsToList( GLT_DEFAULT_TAGS );
\r
8416 case IDC_GLT_Restore:
\r
8417 GLT_TagsToList( appData.gameListTags );
\r
8421 GLT_MoveSelection( hDlg, -1 );
\r
8424 case IDC_GLT_Down:
\r
8425 GLT_MoveSelection( hDlg, +1 );
\r
8435 int GameListOptions()
\r
8438 FARPROC lpProc = MakeProcInstance( (FARPROC) GameListOptions_Proc, hInst );
\r
8440 safeStrCpy( lpUserGLT, appData.gameListTags ,LPUSERGLT_SIZE );
\r
8442 result = DialogBoxParam( hInst, MAKEINTRESOURCE(DLG_GameListOptions), hwndMain, (DLGPROC)lpProc, (LPARAM)lpUserGLT );
\r
8444 if( result == 0 ) {
\r
8445 /* [AS] Memory leak here! */
\r
8446 appData.gameListTags = strdup( lpUserGLT );
\r
8453 DisplayIcsInteractionTitle(char *str)
\r
8455 char consoleTitle[MSG_SIZ];
\r
8457 snprintf(consoleTitle, MSG_SIZ, "%s: %s", szConsoleTitle, str);
\r
8458 SetWindowText(hwndConsole, consoleTitle);
\r
8462 DrawPosition(int fullRedraw, Board board)
\r
8464 HDCDrawPosition(NULL, (BOOLEAN) fullRedraw, board);
\r
8467 void NotifyFrontendLogin()
\r
8470 UpdateICSWidth(GetDlgItem(hwndConsole, OPT_ConsoleText));
\r
8476 fromX = fromY = -1;
\r
8477 if (dragInfo.pos.x != -1 || dragInfo.pos.y != -1) {
\r
8478 dragInfo.pos.x = dragInfo.pos.y = -1;
\r
8479 dragInfo.pos.x = dragInfo.pos.y = -1;
\r
8480 dragInfo.lastpos = dragInfo.pos;
\r
8481 dragInfo.start.x = dragInfo.start.y = -1;
\r
8482 dragInfo.from = dragInfo.start;
\r
8484 DrawPosition(TRUE, NULL);
\r
8491 CommentPopUp(char *title, char *str)
\r
8493 HWND hwnd = GetActiveWindow();
\r
8494 EitherCommentPopUp(currentMove, title, str, FALSE); // [HGM] vari: fake move index, rather than 0
\r
8496 SetActiveWindow(hwnd);
\r
8500 CommentPopDown(void)
\r
8502 CheckMenuItem(GetMenu(hwndMain), IDM_EditComment, MF_UNCHECKED);
\r
8503 if (commentDialog) {
\r
8504 ShowWindow(commentDialog, SW_HIDE);
\r
8506 commentUp = FALSE;
\r
8510 EditCommentPopUp(int index, char *title, char *str)
\r
8512 EitherCommentPopUp(index, title, str, TRUE);
\r
8519 MyPlaySound(&sounds[(int)SoundMove]);
\r
8522 VOID PlayIcsWinSound()
\r
8524 MyPlaySound(&sounds[(int)SoundIcsWin]);
\r
8527 VOID PlayIcsLossSound()
\r
8529 MyPlaySound(&sounds[(int)SoundIcsLoss]);
\r
8532 VOID PlayIcsDrawSound()
\r
8534 MyPlaySound(&sounds[(int)SoundIcsDraw]);
\r
8537 VOID PlayIcsUnfinishedSound()
\r
8539 MyPlaySound(&sounds[(int)SoundIcsUnfinished]);
\r
8545 MyPlaySound(&sounds[(int)SoundAlarm]);
\r
8553 consoleEcho = TRUE;
\r
8554 hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
8555 SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&consoleCF);
\r
8556 SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);
\r
8565 consoleEcho = FALSE;
\r
8566 hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
8567 /* This works OK: set text and background both to the same color */
\r
8569 cf.crTextColor = COLOR_ECHOOFF;
\r
8570 SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf);
\r
8571 SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, cf.crTextColor);
\r
8574 /* No Raw()...? */
\r
8576 void Colorize(ColorClass cc, int continuation)
\r
8578 currentColorClass = cc;
\r
8579 consoleCF.dwMask = CFM_COLOR|CFM_BOLD|CFM_ITALIC|CFM_UNDERLINE|CFM_STRIKEOUT;
\r
8580 consoleCF.crTextColor = textAttribs[cc].color;
\r
8581 consoleCF.dwEffects = textAttribs[cc].effects;
\r
8582 if (!continuation) MyPlaySound(&textAttribs[cc].sound);
\r
8588 static char buf[MSG_SIZ];
\r
8589 DWORD bufsiz = MSG_SIZ;
\r
8591 if(appData.userName != NULL && appData.userName[0] != 0) {
\r
8592 return appData.userName; /* [HGM] username: prefer name selected by user over his system login */
\r
8594 if (!GetUserName(buf, &bufsiz)) {
\r
8595 /*DisplayError("Error getting user name", GetLastError());*/
\r
8596 safeStrCpy(buf, _("User"), sizeof(buf)/sizeof(buf[0]) );
\r
8604 static char buf[MSG_SIZ];
\r
8605 DWORD bufsiz = MSG_SIZ;
\r
8607 if (!GetComputerName(buf, &bufsiz)) {
\r
8608 /*DisplayError("Error getting host name", GetLastError());*/
\r
8609 safeStrCpy(buf, _("Unknown"), sizeof(buf)/sizeof(buf[0]) );
\r
8616 ClockTimerRunning()
\r
8618 return clockTimerEvent != 0;
\r
8624 if (clockTimerEvent == 0) return FALSE;
\r
8625 KillTimer(hwndMain, clockTimerEvent);
\r
8626 clockTimerEvent = 0;
\r
8631 StartClockTimer(long millisec)
\r
8633 clockTimerEvent = SetTimer(hwndMain, (UINT) CLOCK_TIMER_ID,
\r
8634 (UINT) millisec, NULL);
\r
8638 DisplayWhiteClock(long timeRemaining, int highlight)
\r
8641 char *flag = whiteFlag && gameMode == TwoMachinesPlay ? "(!)" : "";
\r
8643 if(appData.noGUI) return;
\r
8644 hdc = GetDC(hwndMain);
\r
8645 if (!IsIconic(hwndMain)) {
\r
8646 DisplayAClock(hdc, timeRemaining, highlight,
\r
8647 flipClock ? &blackRect : &whiteRect, _("White"), flag);
\r
8649 if (highlight && iconCurrent == iconBlack) {
\r
8650 iconCurrent = iconWhite;
\r
8651 PostMessage(hwndMain, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);
\r
8652 if (IsIconic(hwndMain)) {
\r
8653 DrawIcon(hdc, 2, 2, iconCurrent);
\r
8656 (void) ReleaseDC(hwndMain, hdc);
\r
8658 PostMessage(hwndConsole, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);
\r
8662 DisplayBlackClock(long timeRemaining, int highlight)
\r
8665 char *flag = blackFlag && gameMode == TwoMachinesPlay ? "(!)" : "";
\r
8667 if(appData.noGUI) return;
\r
8668 hdc = GetDC(hwndMain);
\r
8669 if (!IsIconic(hwndMain)) {
\r
8670 DisplayAClock(hdc, timeRemaining, highlight,
\r
8671 flipClock ? &whiteRect : &blackRect, _("Black"), flag);
\r
8673 if (highlight && iconCurrent == iconWhite) {
\r
8674 iconCurrent = iconBlack;
\r
8675 PostMessage(hwndMain, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);
\r
8676 if (IsIconic(hwndMain)) {
\r
8677 DrawIcon(hdc, 2, 2, iconCurrent);
\r
8680 (void) ReleaseDC(hwndMain, hdc);
\r
8682 PostMessage(hwndConsole, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);
\r
8687 LoadGameTimerRunning()
\r
8689 return loadGameTimerEvent != 0;
\r
8693 StopLoadGameTimer()
\r
8695 if (loadGameTimerEvent == 0) return FALSE;
\r
8696 KillTimer(hwndMain, loadGameTimerEvent);
\r
8697 loadGameTimerEvent = 0;
\r
8702 StartLoadGameTimer(long millisec)
\r
8704 loadGameTimerEvent = SetTimer(hwndMain, (UINT) LOAD_GAME_TIMER_ID,
\r
8705 (UINT) millisec, NULL);
\r
8713 char fileTitle[MSG_SIZ];
\r
8715 defName = DefaultFileName(appData.oldSaveStyle ? "gam" : "pgn");
\r
8716 f = OpenFileDialog(hwndMain, "a", defName,
\r
8717 appData.oldSaveStyle ? "gam" : "pgn",
\r
8719 _("Save Game to File"), NULL, fileTitle, NULL);
\r
8721 SaveGame(f, 0, "");
\r
8728 ScheduleDelayedEvent(DelayedEventCallback cb, long millisec)
\r
8730 if (delayedTimerEvent != 0) {
\r
8731 if (appData.debugMode && cb != delayedTimerCallback) { // [HGM] alive: not too much debug
\r
8732 fprintf(debugFP, "ScheduleDelayedEvent: event already scheduled\n");
\r
8734 KillTimer(hwndMain, delayedTimerEvent);
\r
8735 delayedTimerEvent = 0;
\r
8736 if(delayedTimerCallback != cb) // [HGM] alive: do not "flush" same event, just postpone it
\r
8737 delayedTimerCallback();
\r
8739 delayedTimerCallback = cb;
\r
8740 delayedTimerEvent = SetTimer(hwndMain, (UINT) DELAYED_TIMER_ID,
\r
8741 (UINT) millisec, NULL);
\r
8744 DelayedEventCallback
\r
8747 if (delayedTimerEvent) {
\r
8748 return delayedTimerCallback;
\r
8755 CancelDelayedEvent()
\r
8757 if (delayedTimerEvent) {
\r
8758 KillTimer(hwndMain, delayedTimerEvent);
\r
8759 delayedTimerEvent = 0;
\r
8763 DWORD GetWin32Priority(int nice)
\r
8764 { // [HGM] nice: translate Unix nice() value to indows priority class. (Code stolen from Polyglot 1.4w11)
\r
8766 REALTIME_PRIORITY_CLASS 0x00000100
\r
8767 HIGH_PRIORITY_CLASS 0x00000080
\r
8768 ABOVE_NORMAL_PRIORITY_CLASS 0x00008000
\r
8769 NORMAL_PRIORITY_CLASS 0x00000020
\r
8770 BELOW_NORMAL_PRIORITY_CLASS 0x00004000
\r
8771 IDLE_PRIORITY_CLASS 0x00000040
\r
8773 if (nice < -15) return 0x00000080;
\r
8774 if (nice < 0) return 0x00008000;
\r
8775 if (nice == 0) return 0x00000020;
\r
8776 if (nice < 15) return 0x00004000;
\r
8777 return 0x00000040;
\r
8780 /* Start a child process running the given program.
\r
8781 The process's standard output can be read from "from", and its
\r
8782 standard input can be written to "to".
\r
8783 Exit with fatal error if anything goes wrong.
\r
8784 Returns an opaque pointer that can be used to destroy the process
\r
8788 StartChildProcess(char *cmdLine, char *dir, ProcRef *pr)
\r
8790 #define BUFSIZE 4096
\r
8792 HANDLE hChildStdinRd, hChildStdinWr,
\r
8793 hChildStdoutRd, hChildStdoutWr;
\r
8794 HANDLE hChildStdinWrDup, hChildStdoutRdDup;
\r
8795 SECURITY_ATTRIBUTES saAttr;
\r
8797 PROCESS_INFORMATION piProcInfo;
\r
8798 STARTUPINFO siStartInfo;
\r
8800 char buf[MSG_SIZ];
\r
8803 if (appData.debugMode) {
\r
8804 fprintf(debugFP, "StartChildProcess (dir=\"%s\") %s\n", dir, cmdLine);
\r
8809 /* Set the bInheritHandle flag so pipe handles are inherited. */
\r
8810 saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);
\r
8811 saAttr.bInheritHandle = TRUE;
\r
8812 saAttr.lpSecurityDescriptor = NULL;
\r
8815 * The steps for redirecting child's STDOUT:
\r
8816 * 1. Create anonymous pipe to be STDOUT for child.
\r
8817 * 2. Create a noninheritable duplicate of read handle,
\r
8818 * and close the inheritable read handle.
\r
8821 /* Create a pipe for the child's STDOUT. */
\r
8822 if (! CreatePipe(&hChildStdoutRd, &hChildStdoutWr, &saAttr, 0)) {
\r
8823 return GetLastError();
\r
8826 /* Duplicate the read handle to the pipe, so it is not inherited. */
\r
8827 fSuccess = DuplicateHandle(GetCurrentProcess(), hChildStdoutRd,
\r
8828 GetCurrentProcess(), &hChildStdoutRdDup, 0,
\r
8829 FALSE, /* not inherited */
\r
8830 DUPLICATE_SAME_ACCESS);
\r
8832 return GetLastError();
\r
8834 CloseHandle(hChildStdoutRd);
\r
8837 * The steps for redirecting child's STDIN:
\r
8838 * 1. Create anonymous pipe to be STDIN for child.
\r
8839 * 2. Create a noninheritable duplicate of write handle,
\r
8840 * and close the inheritable write handle.
\r
8843 /* Create a pipe for the child's STDIN. */
\r
8844 if (! CreatePipe(&hChildStdinRd, &hChildStdinWr, &saAttr, 0)) {
\r
8845 return GetLastError();
\r
8848 /* Duplicate the write handle to the pipe, so it is not inherited. */
\r
8849 fSuccess = DuplicateHandle(GetCurrentProcess(), hChildStdinWr,
\r
8850 GetCurrentProcess(), &hChildStdinWrDup, 0,
\r
8851 FALSE, /* not inherited */
\r
8852 DUPLICATE_SAME_ACCESS);
\r
8854 return GetLastError();
\r
8856 CloseHandle(hChildStdinWr);
\r
8858 /* Arrange to (1) look in dir for the child .exe file, and
\r
8859 * (2) have dir be the child's working directory. Interpret
\r
8860 * dir relative to the directory WinBoard loaded from. */
\r
8861 GetCurrentDirectory(MSG_SIZ, buf);
\r
8862 SetCurrentDirectory(installDir);
\r
8863 SetCurrentDirectory(dir);
\r
8865 /* Now create the child process. */
\r
8867 siStartInfo.cb = sizeof(STARTUPINFO);
\r
8868 siStartInfo.lpReserved = NULL;
\r
8869 siStartInfo.lpDesktop = NULL;
\r
8870 siStartInfo.lpTitle = NULL;
\r
8871 siStartInfo.dwFlags = STARTF_USESTDHANDLES;
\r
8872 siStartInfo.cbReserved2 = 0;
\r
8873 siStartInfo.lpReserved2 = NULL;
\r
8874 siStartInfo.hStdInput = hChildStdinRd;
\r
8875 siStartInfo.hStdOutput = hChildStdoutWr;
\r
8876 siStartInfo.hStdError = hChildStdoutWr;
\r
8878 fSuccess = CreateProcess(NULL,
\r
8879 cmdLine, /* command line */
\r
8880 NULL, /* process security attributes */
\r
8881 NULL, /* primary thread security attrs */
\r
8882 TRUE, /* handles are inherited */
\r
8883 DETACHED_PROCESS|CREATE_NEW_PROCESS_GROUP,
\r
8884 NULL, /* use parent's environment */
\r
8886 &siStartInfo, /* STARTUPINFO pointer */
\r
8887 &piProcInfo); /* receives PROCESS_INFORMATION */
\r
8889 err = GetLastError();
\r
8890 SetCurrentDirectory(buf); /* return to prev directory */
\r
8895 if (appData.niceEngines){ // [HGM] nice: adjust engine proc priority
\r
8896 if(appData.debugMode) fprintf(debugFP, "nice engine proc to %d\n", appData.niceEngines);
\r
8897 SetPriorityClass(piProcInfo.hProcess, GetWin32Priority(appData.niceEngines));
\r
8900 /* Close the handles we don't need in the parent */
\r
8901 CloseHandle(piProcInfo.hThread);
\r
8902 CloseHandle(hChildStdinRd);
\r
8903 CloseHandle(hChildStdoutWr);
\r
8905 /* Prepare return value */
\r
8906 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
\r
8907 cp->kind = CPReal;
\r
8908 cp->hProcess = piProcInfo.hProcess;
\r
8909 cp->pid = piProcInfo.dwProcessId;
\r
8910 cp->hFrom = hChildStdoutRdDup;
\r
8911 cp->hTo = hChildStdinWrDup;
\r
8913 *pr = (void *) cp;
\r
8915 /* Klaus Friedel says that this Sleep solves a problem under Windows
\r
8916 2000 where engines sometimes don't see the initial command(s)
\r
8917 from WinBoard and hang. I don't understand how that can happen,
\r
8918 but the Sleep is harmless, so I've put it in. Others have also
\r
8919 reported what may be the same problem, so hopefully this will fix
\r
8920 it for them too. */
\r
8928 DestroyChildProcess(ProcRef pr, int/*boolean*/ signal)
\r
8930 ChildProc *cp; int result;
\r
8932 cp = (ChildProc *) pr;
\r
8933 if (cp == NULL) return;
\r
8935 switch (cp->kind) {
\r
8937 /* TerminateProcess is considered harmful, so... */
\r
8938 CloseHandle(cp->hTo); /* Closing this will give the child an EOF and hopefully kill it */
\r
8939 if (cp->hFrom) CloseHandle(cp->hFrom); /* if NULL, InputThread will close it */
\r
8940 /* The following doesn't work because the chess program
\r
8941 doesn't "have the same console" as WinBoard. Maybe
\r
8942 we could arrange for this even though neither WinBoard
\r
8943 nor the chess program uses a console for stdio? */
\r
8944 /*!!if (signal) GenerateConsoleCtrlEvent(CTRL_BREAK_EVENT, cp->pid);*/
\r
8946 /* [AS] Special termination modes for misbehaving programs... */
\r
8947 if( signal == 9 ) {
\r
8948 result = TerminateProcess( cp->hProcess, 0 );
\r
8950 if ( appData.debugMode) {
\r
8951 fprintf( debugFP, "Terminating process %lu, result=%d\n", cp->pid, result );
\r
8954 else if( signal == 10 ) {
\r
8955 DWORD dw = WaitForSingleObject( cp->hProcess, 3*1000 ); // Wait 3 seconds at most
\r
8957 if( dw != WAIT_OBJECT_0 ) {
\r
8958 result = TerminateProcess( cp->hProcess, 0 );
\r
8960 if ( appData.debugMode) {
\r
8961 fprintf( debugFP, "Process %lu still alive after timeout, killing... result=%d\n", cp->pid, result );
\r
8967 CloseHandle(cp->hProcess);
\r
8971 if (cp->hFrom) CloseHandle(cp->hFrom);
\r
8975 closesocket(cp->sock);
\r
8980 if (signal) send(cp->sock2, "\017", 1, 0); /* 017 = 15 = SIGTERM */
\r
8981 closesocket(cp->sock);
\r
8982 closesocket(cp->sock2);
\r
8990 InterruptChildProcess(ProcRef pr)
\r
8994 cp = (ChildProc *) pr;
\r
8995 if (cp == NULL) return;
\r
8996 switch (cp->kind) {
\r
8998 /* The following doesn't work because the chess program
\r
8999 doesn't "have the same console" as WinBoard. Maybe
\r
9000 we could arrange for this even though neither WinBoard
\r
9001 nor the chess program uses a console for stdio */
\r
9002 /*!!GenerateConsoleCtrlEvent(CTRL_C_EVENT, cp->pid);*/
\r
9007 /* Can't interrupt */
\r
9011 send(cp->sock2, "\002", 1, 0); /* 2 = SIGINT */
\r
9018 OpenTelnet(char *host, char *port, ProcRef *pr)
\r
9020 char cmdLine[MSG_SIZ];
\r
9022 if (port[0] == NULLCHAR) {
\r
9023 snprintf(cmdLine, MSG_SIZ, "%s %s", appData.telnetProgram, host);
\r
9025 snprintf(cmdLine, MSG_SIZ, "%s %s %s", appData.telnetProgram, host, port);
\r
9027 return StartChildProcess(cmdLine, "", pr);
\r
9031 /* Code to open TCP sockets */
\r
9034 OpenTCP(char *host, char *port, ProcRef *pr)
\r
9039 struct sockaddr_in sa, mysa;
\r
9040 struct hostent FAR *hp;
\r
9041 unsigned short uport;
\r
9042 WORD wVersionRequested;
\r
9045 /* Initialize socket DLL */
\r
9046 wVersionRequested = MAKEWORD(1, 1);
\r
9047 err = WSAStartup(wVersionRequested, &wsaData);
\r
9048 if (err != 0) return err;
\r
9051 if ((s = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {
\r
9052 err = WSAGetLastError();
\r
9057 /* Bind local address using (mostly) don't-care values.
\r
9059 memset((char *) &mysa, 0, sizeof(struct sockaddr_in));
\r
9060 mysa.sin_family = AF_INET;
\r
9061 mysa.sin_addr.s_addr = INADDR_ANY;
\r
9062 uport = (unsigned short) 0;
\r
9063 mysa.sin_port = htons(uport);
\r
9064 if (bind(s, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))
\r
9065 == SOCKET_ERROR) {
\r
9066 err = WSAGetLastError();
\r
9071 /* Resolve remote host name */
\r
9072 memset((char *) &sa, 0, sizeof(struct sockaddr_in));
\r
9073 if (!(hp = gethostbyname(host))) {
\r
9074 unsigned int b0, b1, b2, b3;
\r
9076 err = WSAGetLastError();
\r
9078 if (sscanf(host, "%u.%u.%u.%u", &b0, &b1, &b2, &b3) == 4) {
\r
9079 hp = (struct hostent *) calloc(1, sizeof(struct hostent));
\r
9080 hp->h_addrtype = AF_INET;
\r
9082 hp->h_addr_list = (char **) calloc(2, sizeof(char *));
\r
9083 hp->h_addr_list[0] = (char *) malloc(4);
\r
9084 hp->h_addr_list[0][0] = (char) b0;
\r
9085 hp->h_addr_list[0][1] = (char) b1;
\r
9086 hp->h_addr_list[0][2] = (char) b2;
\r
9087 hp->h_addr_list[0][3] = (char) b3;
\r
9093 sa.sin_family = hp->h_addrtype;
\r
9094 uport = (unsigned short) atoi(port);
\r
9095 sa.sin_port = htons(uport);
\r
9096 memcpy((char *) &sa.sin_addr, hp->h_addr, hp->h_length);
\r
9098 /* Make connection */
\r
9099 if (connect(s, (struct sockaddr *) &sa,
\r
9100 sizeof(struct sockaddr_in)) == SOCKET_ERROR) {
\r
9101 err = WSAGetLastError();
\r
9106 /* Prepare return value */
\r
9107 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
\r
9108 cp->kind = CPSock;
\r
9110 *pr = (ProcRef *) cp;
\r
9116 OpenCommPort(char *name, ProcRef *pr)
\r
9121 char fullname[MSG_SIZ];
\r
9123 if (*name != '\\')
\r
9124 snprintf(fullname, MSG_SIZ, "\\\\.\\%s", name);
\r
9126 safeStrCpy(fullname, name, sizeof(fullname)/sizeof(fullname[0]) );
\r
9128 h = CreateFile(name, GENERIC_READ | GENERIC_WRITE,
\r
9129 0, NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL);
\r
9130 if (h == (HANDLE) -1) {
\r
9131 return GetLastError();
\r
9135 if (!SetCommState(h, (LPDCB) &dcb)) return GetLastError();
\r
9137 /* Accumulate characters until a 100ms pause, then parse */
\r
9138 ct.ReadIntervalTimeout = 100;
\r
9139 ct.ReadTotalTimeoutMultiplier = 0;
\r
9140 ct.ReadTotalTimeoutConstant = 0;
\r
9141 ct.WriteTotalTimeoutMultiplier = 0;
\r
9142 ct.WriteTotalTimeoutConstant = 0;
\r
9143 if (!SetCommTimeouts(h, (LPCOMMTIMEOUTS) &ct)) return GetLastError();
\r
9145 /* Prepare return value */
\r
9146 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
\r
9147 cp->kind = CPComm;
\r
9150 *pr = (ProcRef *) cp;
\r
9156 OpenLoopback(ProcRef *pr)
\r
9158 DisplayFatalError(_("Not implemented"), 0, 1);
\r
9164 OpenRcmd(char* host, char* user, char* cmd, ProcRef* pr)
\r
9169 struct sockaddr_in sa, mysa;
\r
9170 struct hostent FAR *hp;
\r
9171 unsigned short uport;
\r
9172 WORD wVersionRequested;
\r
9175 char stderrPortStr[MSG_SIZ];
\r
9177 /* Initialize socket DLL */
\r
9178 wVersionRequested = MAKEWORD(1, 1);
\r
9179 err = WSAStartup(wVersionRequested, &wsaData);
\r
9180 if (err != 0) return err;
\r
9182 /* Resolve remote host name */
\r
9183 memset((char *) &sa, 0, sizeof(struct sockaddr_in));
\r
9184 if (!(hp = gethostbyname(host))) {
\r
9185 unsigned int b0, b1, b2, b3;
\r
9187 err = WSAGetLastError();
\r
9189 if (sscanf(host, "%u.%u.%u.%u", &b0, &b1, &b2, &b3) == 4) {
\r
9190 hp = (struct hostent *) calloc(1, sizeof(struct hostent));
\r
9191 hp->h_addrtype = AF_INET;
\r
9193 hp->h_addr_list = (char **) calloc(2, sizeof(char *));
\r
9194 hp->h_addr_list[0] = (char *) malloc(4);
\r
9195 hp->h_addr_list[0][0] = (char) b0;
\r
9196 hp->h_addr_list[0][1] = (char) b1;
\r
9197 hp->h_addr_list[0][2] = (char) b2;
\r
9198 hp->h_addr_list[0][3] = (char) b3;
\r
9204 sa.sin_family = hp->h_addrtype;
\r
9205 uport = (unsigned short) 514;
\r
9206 sa.sin_port = htons(uport);
\r
9207 memcpy((char *) &sa.sin_addr, hp->h_addr, hp->h_length);
\r
9209 /* Bind local socket to unused "privileged" port address
\r
9211 s = INVALID_SOCKET;
\r
9212 memset((char *) &mysa, 0, sizeof(struct sockaddr_in));
\r
9213 mysa.sin_family = AF_INET;
\r
9214 mysa.sin_addr.s_addr = INADDR_ANY;
\r
9215 for (fromPort = 1023;; fromPort--) {
\r
9216 if (fromPort < 0) {
\r
9218 return WSAEADDRINUSE;
\r
9220 if (s == INVALID_SOCKET) {
\r
9221 if ((s = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {
\r
9222 err = WSAGetLastError();
\r
9227 uport = (unsigned short) fromPort;
\r
9228 mysa.sin_port = htons(uport);
\r
9229 if (bind(s, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))
\r
9230 == SOCKET_ERROR) {
\r
9231 err = WSAGetLastError();
\r
9232 if (err == WSAEADDRINUSE) continue;
\r
9236 if (connect(s, (struct sockaddr *) &sa,
\r
9237 sizeof(struct sockaddr_in)) == SOCKET_ERROR) {
\r
9238 err = WSAGetLastError();
\r
9239 if (err == WSAEADDRINUSE) {
\r
9250 /* Bind stderr local socket to unused "privileged" port address
\r
9252 s2 = INVALID_SOCKET;
\r
9253 memset((char *) &mysa, 0, sizeof(struct sockaddr_in));
\r
9254 mysa.sin_family = AF_INET;
\r
9255 mysa.sin_addr.s_addr = INADDR_ANY;
\r
9256 for (fromPort = 1023;; fromPort--) {
\r
9257 if (fromPort == prevStderrPort) continue; // don't reuse port
\r
9258 if (fromPort < 0) {
\r
9259 (void) closesocket(s);
\r
9261 return WSAEADDRINUSE;
\r
9263 if (s2 == INVALID_SOCKET) {
\r
9264 if ((s2 = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {
\r
9265 err = WSAGetLastError();
\r
9271 uport = (unsigned short) fromPort;
\r
9272 mysa.sin_port = htons(uport);
\r
9273 if (bind(s2, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))
\r
9274 == SOCKET_ERROR) {
\r
9275 err = WSAGetLastError();
\r
9276 if (err == WSAEADDRINUSE) continue;
\r
9277 (void) closesocket(s);
\r
9281 if (listen(s2, 1) == SOCKET_ERROR) {
\r
9282 err = WSAGetLastError();
\r
9283 if (err == WSAEADDRINUSE) {
\r
9285 s2 = INVALID_SOCKET;
\r
9288 (void) closesocket(s);
\r
9289 (void) closesocket(s2);
\r
9295 prevStderrPort = fromPort; // remember port used
\r
9296 snprintf(stderrPortStr, MSG_SIZ, "%d", fromPort);
\r
9298 if (send(s, stderrPortStr, strlen(stderrPortStr) + 1, 0) == SOCKET_ERROR) {
\r
9299 err = WSAGetLastError();
\r
9300 (void) closesocket(s);
\r
9301 (void) closesocket(s2);
\r
9306 if (send(s, UserName(), strlen(UserName()) + 1, 0) == SOCKET_ERROR) {
\r
9307 err = WSAGetLastError();
\r
9308 (void) closesocket(s);
\r
9309 (void) closesocket(s2);
\r
9313 if (*user == NULLCHAR) user = UserName();
\r
9314 if (send(s, user, strlen(user) + 1, 0) == SOCKET_ERROR) {
\r
9315 err = WSAGetLastError();
\r
9316 (void) closesocket(s);
\r
9317 (void) closesocket(s2);
\r
9321 if (send(s, cmd, strlen(cmd) + 1, 0) == SOCKET_ERROR) {
\r
9322 err = WSAGetLastError();
\r
9323 (void) closesocket(s);
\r
9324 (void) closesocket(s2);
\r
9329 if ((s3 = accept(s2, NULL, NULL)) == INVALID_SOCKET) {
\r
9330 err = WSAGetLastError();
\r
9331 (void) closesocket(s);
\r
9332 (void) closesocket(s2);
\r
9336 (void) closesocket(s2); /* Stop listening */
\r
9338 /* Prepare return value */
\r
9339 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
\r
9340 cp->kind = CPRcmd;
\r
9343 *pr = (ProcRef *) cp;
\r
9350 AddInputSource(ProcRef pr, int lineByLine,
\r
9351 InputCallback func, VOIDSTAR closure)
\r
9353 InputSource *is, *is2 = NULL;
\r
9354 ChildProc *cp = (ChildProc *) pr;
\r
9356 is = (InputSource *) calloc(1, sizeof(InputSource));
\r
9357 is->lineByLine = lineByLine;
\r
9359 is->closure = closure;
\r
9360 is->second = NULL;
\r
9361 is->next = is->buf;
\r
9362 if (pr == NoProc) {
\r
9363 is->kind = CPReal;
\r
9364 consoleInputSource = is;
\r
9366 is->kind = cp->kind;
\r
9368 [AS] Try to avoid a race condition if the thread is given control too early:
\r
9369 we create all threads suspended so that the is->hThread variable can be
\r
9370 safely assigned, then let the threads start with ResumeThread.
\r
9372 switch (cp->kind) {
\r
9374 is->hFile = cp->hFrom;
\r
9375 cp->hFrom = NULL; /* now owned by InputThread */
\r
9377 CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) NonOvlInputThread,
\r
9378 (LPVOID) is, CREATE_SUSPENDED, &is->id);
\r
9382 is->hFile = cp->hFrom;
\r
9383 cp->hFrom = NULL; /* now owned by InputThread */
\r
9385 CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) InputThread,
\r
9386 (LPVOID) is, CREATE_SUSPENDED, &is->id);
\r
9390 is->sock = cp->sock;
\r
9392 CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,
\r
9393 (LPVOID) is, CREATE_SUSPENDED, &is->id);
\r
9397 is2 = (InputSource *) calloc(1, sizeof(InputSource));
\r
9399 is->sock = cp->sock;
\r
9401 is2->sock = cp->sock2;
\r
9402 is2->second = is2;
\r
9404 CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,
\r
9405 (LPVOID) is, CREATE_SUSPENDED, &is->id);
\r
9407 CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,
\r
9408 (LPVOID) is2, CREATE_SUSPENDED, &is2->id);
\r
9412 if( is->hThread != NULL ) {
\r
9413 ResumeThread( is->hThread );
\r
9416 if( is2 != NULL && is2->hThread != NULL ) {
\r
9417 ResumeThread( is2->hThread );
\r
9421 return (InputSourceRef) is;
\r
9425 RemoveInputSource(InputSourceRef isr)
\r
9429 is = (InputSource *) isr;
\r
9430 is->hThread = NULL; /* tell thread to stop */
\r
9431 CloseHandle(is->hThread);
\r
9432 if (is->second != NULL) {
\r
9433 is->second->hThread = NULL;
\r
9434 CloseHandle(is->second->hThread);
\r
9438 int no_wrap(char *message, int count)
\r
9440 ConsoleOutput(message, count, FALSE);
\r
9445 OutputToProcess(ProcRef pr, char *message, int count, int *outError)
\r
9448 int outCount = SOCKET_ERROR;
\r
9449 ChildProc *cp = (ChildProc *) pr;
\r
9450 static OVERLAPPED ovl;
\r
9451 static int line = 0;
\r
9455 if (appData.noJoin || !appData.useInternalWrap)
\r
9456 return no_wrap(message, count);
\r
9459 int width = get_term_width();
\r
9460 int len = wrap(NULL, message, count, width, &line);
\r
9461 char *msg = malloc(len);
\r
9465 return no_wrap(message, count);
\r
9468 dbgchk = wrap(msg, message, count, width, &line);
\r
9469 if (dbgchk != len && appData.debugMode)
\r
9470 fprintf(debugFP, "wrap(): dbgchk(%d) != len(%d)\n", dbgchk, len);
\r
9471 ConsoleOutput(msg, len, FALSE);
\r
9478 if (ovl.hEvent == NULL) {
\r
9479 ovl.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
\r
9481 ovl.Internal = ovl.InternalHigh = ovl.Offset = ovl.OffsetHigh = 0;
\r
9483 switch (cp->kind) {
\r
9486 outCount = send(cp->sock, message, count, 0);
\r
9487 if (outCount == SOCKET_ERROR) {
\r
9488 *outError = WSAGetLastError();
\r
9490 *outError = NO_ERROR;
\r
9495 if (WriteFile(((ChildProc *)pr)->hTo, message, count,
\r
9496 &dOutCount, NULL)) {
\r
9497 *outError = NO_ERROR;
\r
9498 outCount = (int) dOutCount;
\r
9500 *outError = GetLastError();
\r
9505 *outError = DoWriteFile(((ChildProc *)pr)->hTo, message, count,
\r
9506 &dOutCount, &ovl);
\r
9507 if (*outError == NO_ERROR) {
\r
9508 outCount = (int) dOutCount;
\r
9516 OutputToProcessDelayed(ProcRef pr, char *message, int count, int *outError,
\r
9519 /* Ignore delay, not implemented for WinBoard */
\r
9520 return OutputToProcess(pr, message, count, outError);
\r
9525 CmailSigHandlerCallBack(InputSourceRef isr, VOIDSTAR closure,
\r
9526 char *buf, int count, int error)
\r
9528 DisplayFatalError(_("Not implemented"), 0, 1);
\r
9531 /* see wgamelist.c for Game List functions */
\r
9532 /* see wedittags.c for Edit Tags functions */
\r
9539 char buf[MSG_SIZ];
\r
9542 if (SearchPath(installDir, appData.icsLogon, NULL, MSG_SIZ, buf, &dummy)) {
\r
9543 f = fopen(buf, "r");
\r
9545 ProcessICSInitScript(f);
\r
9553 StartAnalysisClock()
\r
9555 if (analysisTimerEvent) return;
\r
9556 analysisTimerEvent = SetTimer(hwndMain, (UINT) ANALYSIS_TIMER_ID,
\r
9557 (UINT) 2000, NULL);
\r
9561 SetHighlights(int fromX, int fromY, int toX, int toY)
\r
9563 highlightInfo.sq[0].x = fromX;
\r
9564 highlightInfo.sq[0].y = fromY;
\r
9565 highlightInfo.sq[1].x = toX;
\r
9566 highlightInfo.sq[1].y = toY;
\r
9572 highlightInfo.sq[0].x = highlightInfo.sq[0].y =
\r
9573 highlightInfo.sq[1].x = highlightInfo.sq[1].y = -1;
\r
9577 SetPremoveHighlights(int fromX, int fromY, int toX, int toY)
\r
9579 premoveHighlightInfo.sq[0].x = fromX;
\r
9580 premoveHighlightInfo.sq[0].y = fromY;
\r
9581 premoveHighlightInfo.sq[1].x = toX;
\r
9582 premoveHighlightInfo.sq[1].y = toY;
\r
9586 ClearPremoveHighlights()
\r
9588 premoveHighlightInfo.sq[0].x = premoveHighlightInfo.sq[0].y =
\r
9589 premoveHighlightInfo.sq[1].x = premoveHighlightInfo.sq[1].y = -1;
\r
9593 ShutDownFrontEnd()
\r
9595 if (saveSettingsOnExit) SaveSettings(settingsFileName);
\r
9596 DeleteClipboardTempFiles();
\r
9602 if (IsIconic(hwndMain))
\r
9603 ShowWindow(hwndMain, SW_RESTORE);
\r
9605 SetActiveWindow(hwndMain);
\r
9609 * Prototypes for animation support routines
\r
9611 static void ScreenSquare(int column, int row, POINT * pt);
\r
9612 static void Tween( POINT * start, POINT * mid, POINT * finish, int factor,
\r
9613 POINT frames[], int * nFrames);
\r
9617 AnimateAtomicCapture(int fromX, int fromY, int toX, int toY, int nFrames)
\r
9618 { // [HGM] atomic: animate blast wave
\r
9620 if(appData.debugMode) fprintf(debugFP, "exploding (%d,%d)\n", toX, toY);
\r
9621 explodeInfo.fromX = fromX;
\r
9622 explodeInfo.fromY = fromY;
\r
9623 explodeInfo.toX = toX;
\r
9624 explodeInfo.toY = toY;
\r
9625 for(i=1; i<nFrames; i++) {
\r
9626 explodeInfo.radius = (i*180)/(nFrames-1);
\r
9627 DrawPosition(FALSE, NULL);
\r
9628 Sleep(appData.animSpeed);
\r
9630 explodeInfo.radius = 0;
\r
9631 DrawPosition(TRUE, NULL);
\r
9637 AnimateMove(board, fromX, fromY, toX, toY)
\r
9644 ChessSquare piece;
\r
9645 POINT start, finish, mid;
\r
9646 POINT frames[kFactor * 2 + 1];
\r
9649 if (!appData.animate) return;
\r
9650 if (doingSizing) return;
\r
9651 if (fromY < 0 || fromX < 0) return;
\r
9652 piece = board[fromY][fromX];
\r
9653 if (piece >= EmptySquare) return;
\r
9655 ScreenSquare(fromX, fromY, &start);
\r
9656 ScreenSquare(toX, toY, &finish);
\r
9658 /* All pieces except knights move in straight line */
\r
9659 if (piece != WhiteKnight && piece != BlackKnight) {
\r
9660 mid.x = start.x + (finish.x - start.x) / 2;
\r
9661 mid.y = start.y + (finish.y - start.y) / 2;
\r
9663 /* Knight: make diagonal movement then straight */
\r
9664 if (abs(toY - fromY) < abs(toX - fromX)) {
\r
9665 mid.x = start.x + (finish.x - start.x) / 2;
\r
9669 mid.y = start.y + (finish.y - start.y) / 2;
\r
9673 /* Don't use as many frames for very short moves */
\r
9674 if (abs(toY - fromY) + abs(toX - fromX) <= 2)
\r
9675 Tween(&start, &mid, &finish, kFactor - 1, frames, &nFrames);
\r
9677 Tween(&start, &mid, &finish, kFactor, frames, &nFrames);
\r
9679 animInfo.from.x = fromX;
\r
9680 animInfo.from.y = fromY;
\r
9681 animInfo.to.x = toX;
\r
9682 animInfo.to.y = toY;
\r
9683 animInfo.lastpos = start;
\r
9684 animInfo.piece = piece;
\r
9685 for (n = 0; n < nFrames; n++) {
\r
9686 animInfo.pos = frames[n];
\r
9687 DrawPosition(FALSE, NULL);
\r
9688 animInfo.lastpos = animInfo.pos;
\r
9689 Sleep(appData.animSpeed);
\r
9691 animInfo.pos = finish;
\r
9692 DrawPosition(FALSE, NULL);
\r
9693 animInfo.piece = EmptySquare;
\r
9694 if(gameInfo.variant == VariantAtomic &&
\r
9695 (board[toY][toX] != EmptySquare || fromX != toX && (piece == WhitePawn || piece == BlackPawn) ) )
\r
9696 AnimateAtomicCapture(fromX, fromY, toX, toY, 2*nFrames);
\r
9699 /* Convert board position to corner of screen rect and color */
\r
9702 ScreenSquare(column, row, pt)
\r
9703 int column; int row; POINT * pt;
\r
9706 pt->x = lineGap + ((BOARD_WIDTH-1)-column) * (squareSize + lineGap);
\r
9707 pt->y = lineGap + row * (squareSize + lineGap);
\r
9709 pt->x = lineGap + column * (squareSize + lineGap);
\r
9710 pt->y = lineGap + ((BOARD_HEIGHT-1)-row) * (squareSize + lineGap);
\r
9714 /* Generate a series of frame coords from start->mid->finish.
\r
9715 The movement rate doubles until the half way point is
\r
9716 reached, then halves back down to the final destination,
\r
9717 which gives a nice slow in/out effect. The algorithmn
\r
9718 may seem to generate too many intermediates for short
\r
9719 moves, but remember that the purpose is to attract the
\r
9720 viewers attention to the piece about to be moved and
\r
9721 then to where it ends up. Too few frames would be less
\r
9725 Tween(start, mid, finish, factor, frames, nFrames)
\r
9726 POINT * start; POINT * mid;
\r
9727 POINT * finish; int factor;
\r
9728 POINT frames[]; int * nFrames;
\r
9730 int n, fraction = 1, count = 0;
\r
9732 /* Slow in, stepping 1/16th, then 1/8th, ... */
\r
9733 for (n = 0; n < factor; n++)
\r
9735 for (n = 0; n < factor; n++) {
\r
9736 frames[count].x = start->x + (mid->x - start->x) / fraction;
\r
9737 frames[count].y = start->y + (mid->y - start->y) / fraction;
\r
9739 fraction = fraction / 2;
\r
9743 frames[count] = *mid;
\r
9746 /* Slow out, stepping 1/2, then 1/4, ... */
\r
9748 for (n = 0; n < factor; n++) {
\r
9749 frames[count].x = finish->x - (finish->x - mid->x) / fraction;
\r
9750 frames[count].y = finish->y - (finish->y - mid->y) / fraction;
\r
9752 fraction = fraction * 2;
\r
9758 HistorySet( char movelist[][2*MOVE_LEN], int first, int last, int current )
\r
9760 MoveHistorySet( movelist, first, last, current, pvInfoList );
\r
9762 EvalGraphSet( first, last, current, pvInfoList );
\r