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][25];
\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 sprintf(buf, "%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 strcpy(oldLanguage, buf);
\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
433 if(menuText[i][j]) strcpy(buf, menuText[i][j]); else {
\r
434 GetMenuString(subMenu, j, buf, MSG_SIZ, MF_BYPOSITION);
\r
435 menuText[i][j] = strdup(buf); // remember original on first change
\r
437 if(buf[0] == NULLCHAR) continue;
\r
438 //fprintf(debugFP, "menu(%d,%d) = %s (%08x, %08x) %d\n", i, j, buf, mainMenu, subMenu, k);
\r
439 ModifyMenu(subMenu, j, MF_STRING|MF_BYPOSITION
\r
440 |CheckMenuItem(subMenu, j, MF_BYPOSITION)
\r
441 |EnableMenuItem(subMenu, j, MF_BYPOSITION), k, T_(buf));
\r
444 DrawMenuBar(hwndMain);
\r
447 if(!addLanguage) return;
\r
448 if((hFind = FindFirstFile("*.LNG", &fileData)) != INVALID_HANDLE_VALUE) {
\r
449 HMENU mainMenu = GetMenu(hwndMain);
\r
450 HMENU subMenu = GetSubMenu(mainMenu, GetMenuItemCount(mainMenu)-1);
\r
451 AppendMenu(subMenu, MF_SEPARATOR, (UINT_PTR) 0, NULL);
\r
452 AppendMenu(subMenu, MF_ENABLED|MF_STRING|(barbaric?MF_UNCHECKED:MF_CHECKED), (UINT_PTR) IDM_English, (LPCTSTR) "English");
\r
453 i = 0; lastChecked = IDM_English;
\r
455 char *p, *q = fileData.cFileName;
\r
456 int checkFlag = MF_UNCHECKED;
\r
457 languageFile[i] = strdup(q);
\r
458 if(barbaric && !strcmp(oldLanguage, q)) {
\r
459 checkFlag = MF_CHECKED;
\r
460 lastChecked = IDM_English + i + 1;
\r
461 CheckMenuItem(mainMenu, IDM_English, MF_BYCOMMAND|MF_UNCHECKED);
\r
463 *q = ToUpper(*q); while(*++q) *q = ToLower(*q);
\r
464 p = strstr(fileData.cFileName, ".lng");
\r
466 AppendMenu(subMenu, MF_ENABLED|MF_STRING|checkFlag, (UINT_PTR) IDM_English + ++i, (LPCTSTR) fileData.cFileName);
\r
467 } while(FindNextFile(hFind, &fileData));
\r
480 int cliWidth, cliHeight;
\r
483 SizeInfo sizeInfo[] =
\r
485 { "tiny", 21, 0, 1, 1, 0, 0 },
\r
486 { "teeny", 25, 1, 1, 1, 0, 0 },
\r
487 { "dinky", 29, 1, 1, 1, 0, 0 },
\r
488 { "petite", 33, 1, 1, 1, 0, 0 },
\r
489 { "slim", 37, 2, 1, 0, 0, 0 },
\r
490 { "small", 40, 2, 1, 0, 0, 0 },
\r
491 { "mediocre", 45, 2, 1, 0, 0, 0 },
\r
492 { "middling", 49, 2, 0, 0, 0, 0 },
\r
493 { "average", 54, 2, 0, 0, 0, 0 },
\r
494 { "moderate", 58, 3, 0, 0, 0, 0 },
\r
495 { "medium", 64, 3, 0, 0, 0, 0 },
\r
496 { "bulky", 72, 3, 0, 0, 0, 0 },
\r
497 { "large", 80, 3, 0, 0, 0, 0 },
\r
498 { "big", 87, 3, 0, 0, 0, 0 },
\r
499 { "huge", 95, 3, 0, 0, 0, 0 },
\r
500 { "giant", 108, 3, 0, 0, 0, 0 },
\r
501 { "colossal", 116, 4, 0, 0, 0, 0 },
\r
502 { "titanic", 129, 4, 0, 0, 0, 0 },
\r
503 { NULL, 0, 0, 0, 0, 0, 0 }
\r
506 #define MF(x) {x, {{0,}, 0. }, {0, }, 0}
\r
507 MyFont fontRec[NUM_SIZES][NUM_FONTS] =
\r
509 { 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
510 { 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
511 { 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
512 { 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
513 { 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
514 { 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
515 { 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
516 { 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
517 { 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
518 { 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
519 { 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
520 { 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
521 { 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
522 { 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
523 { 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
524 { 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
525 { 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
526 { 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
529 MyFont *font[NUM_SIZES][NUM_FONTS];
\r
538 #define BUTTON_WIDTH (tinyLayout ? 16 : 32)
\r
539 #define N_BUTTONS 5
\r
541 MyButtonDesc buttonDesc[N_BUTTONS] =
\r
543 {"<<", IDM_ToStart, NULL, NULL},
\r
544 {"<", IDM_Backward, NULL, NULL},
\r
545 {"P", IDM_Pause, NULL, NULL},
\r
546 {">", IDM_Forward, NULL, NULL},
\r
547 {">>", IDM_ToEnd, NULL, NULL},
\r
550 int tinyLayout = 0, smallLayout = 0;
\r
551 #define MENU_BAR_ITEMS 7
\r
552 char *menuBarText[2][MENU_BAR_ITEMS+1] = {
\r
553 { N_("&File"), N_("&Mode"), N_("&Action"), N_("&Step"), N_("&Options"), N_("&Help"), NULL },
\r
554 { N_("&F"), N_("&M"), N_("&A"), N_("&S"), N_("&O"), N_("&H"), NULL },
\r
558 MySound sounds[(int)NSoundClasses];
\r
559 MyTextAttribs textAttribs[(int)NColorClasses];
\r
561 MyColorizeAttribs colorizeAttribs[] = {
\r
562 { (COLORREF)0, 0, N_("Shout Text") },
\r
563 { (COLORREF)0, 0, N_("SShout/CShout") },
\r
564 { (COLORREF)0, 0, N_("Channel 1 Text") },
\r
565 { (COLORREF)0, 0, N_("Channel Text") },
\r
566 { (COLORREF)0, 0, N_("Kibitz Text") },
\r
567 { (COLORREF)0, 0, N_("Tell Text") },
\r
568 { (COLORREF)0, 0, N_("Challenge Text") },
\r
569 { (COLORREF)0, 0, N_("Request Text") },
\r
570 { (COLORREF)0, 0, N_("Seek Text") },
\r
571 { (COLORREF)0, 0, N_("Normal Text") },
\r
572 { (COLORREF)0, 0, N_("None") }
\r
577 static char *commentTitle;
\r
578 static char *commentText;
\r
579 static int commentIndex;
\r
580 static Boolean editComment = FALSE;
\r
583 char errorTitle[MSG_SIZ];
\r
584 char errorMessage[2*MSG_SIZ];
\r
585 HWND errorDialog = NULL;
\r
586 BOOLEAN moveErrorMessageUp = FALSE;
\r
587 BOOLEAN consoleEcho = TRUE;
\r
588 CHARFORMAT consoleCF;
\r
589 COLORREF consoleBackgroundColor;
\r
591 char *programVersion;
\r
597 typedef int CPKind;
\r
606 SOCKET sock2; /* stderr socket for OpenRcmd */
\r
609 #define INPUT_SOURCE_BUF_SIZE 4096
\r
611 typedef struct _InputSource {
\r
618 char buf[INPUT_SOURCE_BUF_SIZE];
\r
622 InputCallback func;
\r
623 struct _InputSource *second; /* for stderr thread on CPRcmd */
\r
627 InputSource *consoleInputSource;
\r
632 VOID ConsoleOutput(char* data, int length, int forceVisible);
\r
633 VOID ConsoleCreate();
\r
635 ConsoleWndProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam);
\r
636 VOID ColorizeTextPopup(HWND hwnd, ColorClass cc);
\r
637 VOID PrintCommSettings(FILE *f, char *name, DCB *dcb);
\r
638 VOID ParseCommSettings(char *arg, DCB *dcb);
\r
640 StartupDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam);
\r
641 VOID APIENTRY MenuPopup(HWND hwnd, POINT pt, HMENU hmenu, UINT def);
\r
642 void ParseIcsTextMenu(char *icsTextMenuString);
\r
643 VOID PopUpMoveDialog(char firstchar);
\r
644 VOID PopUpNameDialog(char firstchar);
\r
645 VOID UpdateSampleText(HWND hDlg, int id, MyColorizeAttribs *mca);
\r
649 int GameListOptions();
\r
651 int dummy; // [HGM] for obsolete args
\r
653 HWND hwndMain = NULL; /* root window*/
\r
654 HWND hwndConsole = NULL;
\r
655 HWND commentDialog = NULL;
\r
656 HWND moveHistoryDialog = NULL;
\r
657 HWND evalGraphDialog = NULL;
\r
658 HWND engineOutputDialog = NULL;
\r
659 HWND gameListDialog = NULL;
\r
660 HWND editTagsDialog = NULL;
\r
662 int commentUp = FALSE;
\r
664 WindowPlacement wpMain;
\r
665 WindowPlacement wpConsole;
\r
666 WindowPlacement wpComment;
\r
667 WindowPlacement wpMoveHistory;
\r
668 WindowPlacement wpEvalGraph;
\r
669 WindowPlacement wpEngineOutput;
\r
670 WindowPlacement wpGameList;
\r
671 WindowPlacement wpTags;
\r
673 VOID EngineOptionsPopup(); // [HGM] settings
\r
675 VOID GothicPopUp(char *title, VariantClass variant);
\r
677 * Setting "frozen" should disable all user input other than deleting
\r
678 * the window. We do this while engines are initializing themselves.
\r
680 static int frozen = 0;
\r
681 static int oldMenuItemState[MENU_BAR_ITEMS];
\r
687 if (frozen) return;
\r
689 hmenu = GetMenu(hwndMain);
\r
690 for (i=0; i<MENU_BAR_ITEMS; i++) {
\r
691 oldMenuItemState[i] = EnableMenuItem(hmenu, i, MF_BYPOSITION|MF_GRAYED);
\r
693 DrawMenuBar(hwndMain);
\r
696 /* Undo a FreezeUI */
\r
702 if (!frozen) return;
\r
704 hmenu = GetMenu(hwndMain);
\r
705 for (i=0; i<MENU_BAR_ITEMS; i++) {
\r
706 EnableMenuItem(hmenu, i, MF_BYPOSITION|oldMenuItemState[i]);
\r
708 DrawMenuBar(hwndMain);
\r
711 /*static*/ int fromX = -1, fromY = -1, toX, toY; // [HGM] moved upstream, so JAWS can use them
\r
713 /* JAWS preparation patch (WinBoard for the sight impaired). Define required insertions as empty */
\r
719 #define JAWS_ALT_INTERCEPT
\r
720 #define JAWS_KB_NAVIGATION
\r
721 #define JAWS_MENU_ITEMS
\r
722 #define JAWS_SILENCE
\r
723 #define JAWS_REPLAY
\r
725 #define JAWS_COPYRIGHT
\r
726 #define JAWS_DELETE(X) X
\r
727 #define SAYMACHINEMOVE()
\r
731 /*---------------------------------------------------------------------------*\
\r
735 \*---------------------------------------------------------------------------*/
\r
738 WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
\r
739 LPSTR lpCmdLine, int nCmdShow)
\r
742 HANDLE hAccelMain, hAccelNoAlt, hAccelNoICS;
\r
743 // INITCOMMONCONTROLSEX ex;
\r
747 LoadLibrary("RICHED32.DLL");
\r
748 consoleCF.cbSize = sizeof(CHARFORMAT);
\r
750 if (!InitApplication(hInstance)) {
\r
753 if (!InitInstance(hInstance, nCmdShow, lpCmdLine)) {
\r
759 // InitCommonControlsEx(&ex);
\r
760 InitCommonControls();
\r
762 hAccelMain = LoadAccelerators (hInstance, szAppName);
\r
763 hAccelNoAlt = LoadAccelerators (hInstance, "NO_ALT");
\r
764 hAccelNoICS = LoadAccelerators( hInstance, "NO_ICS"); /* [AS] No Ctrl-V on ICS!!! */
\r
766 /* Acquire and dispatch messages until a WM_QUIT message is received. */
\r
768 while (GetMessage(&msg, /* message structure */
\r
769 NULL, /* handle of window receiving the message */
\r
770 0, /* lowest message to examine */
\r
771 0)) /* highest message to examine */
\r
774 if(msg.message == WM_CHAR && msg.wParam == '\t') {
\r
775 // [HGM] navigate: switch between all windows with tab
\r
776 HWND e1 = NULL, e2 = NULL, mh = NULL, hInput = NULL, hText = NULL;
\r
777 int i, currentElement = 0;
\r
779 // first determine what element of the chain we come from (if any)
\r
780 if(appData.icsActive) {
\r
781 hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
782 hText = GetDlgItem(hwndConsole, OPT_ConsoleText);
\r
784 if(engineOutputDialog && EngineOutputIsUp()) {
\r
785 e1 = GetDlgItem(engineOutputDialog, IDC_EngineMemo1);
\r
786 e2 = GetDlgItem(engineOutputDialog, IDC_EngineMemo2);
\r
788 if(moveHistoryDialog && MoveHistoryIsUp()) {
\r
789 mh = GetDlgItem(moveHistoryDialog, IDC_MoveHistory);
\r
791 if(msg.hwnd == hwndMain) currentElement = 7 ; else
\r
792 if(msg.hwnd == engineOutputDialog) currentElement = 2; else
\r
793 if(msg.hwnd == e1) currentElement = 2; else
\r
794 if(msg.hwnd == e2) currentElement = 3; else
\r
795 if(msg.hwnd == moveHistoryDialog) currentElement = 4; else
\r
796 if(msg.hwnd == mh) currentElement = 4; else
\r
797 if(msg.hwnd == evalGraphDialog) currentElement = 6; else
\r
798 if(msg.hwnd == hText) currentElement = 5; else
\r
799 if(msg.hwnd == hInput) currentElement = 6; else
\r
800 for (i = 0; i < N_BUTTONS; i++) {
\r
801 if (buttonDesc[i].hwnd == msg.hwnd) { currentElement = 1; break; }
\r
804 // determine where to go to
\r
805 if(currentElement) { HWND h = NULL; int direction = GetKeyState(VK_SHIFT) < 0 ? -1 : 1;
\r
807 currentElement = (currentElement + direction) % 7;
\r
808 switch(currentElement) {
\r
810 h = hwndMain; break; // passing this case always makes the loop exit
\r
812 h = buttonDesc[0].hwnd; break; // could be NULL
\r
814 if(!EngineOutputIsUp()) continue; // skip closed auxiliary windows
\r
817 if(!EngineOutputIsUp()) continue;
\r
820 if(!MoveHistoryIsUp()) continue;
\r
822 // case 6: // input to eval graph does not seem to get here!
\r
823 // if(!EvalGraphIsUp()) continue;
\r
824 // h = evalGraphDialog; break;
\r
826 if(!appData.icsActive) continue;
\r
830 if(!appData.icsActive) continue;
\r
836 if(currentElement > 4 && IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);
\r
837 if(currentElement < 5 && IsIconic(hwndMain)) ShowWindow(hwndMain, SW_RESTORE); // all open together
\r
840 continue; // this message now has been processed
\r
844 if (!(commentDialog && IsDialogMessage(commentDialog, &msg)) &&
\r
845 !(moveHistoryDialog && IsDialogMessage(moveHistoryDialog, &msg)) &&
\r
846 !(evalGraphDialog && IsDialogMessage(evalGraphDialog, &msg)) &&
\r
847 !(engineOutputDialog && IsDialogMessage(engineOutputDialog, &msg)) &&
\r
848 !(editTagsDialog && IsDialogMessage(editTagsDialog, &msg)) &&
\r
849 !(gameListDialog && IsDialogMessage(gameListDialog, &msg)) &&
\r
850 !(errorDialog && IsDialogMessage(errorDialog, &msg)) &&
\r
851 !(!frozen && TranslateAccelerator(hwndMain, hAccelMain, &msg)) && JAWS_ACCEL
\r
852 !(!hwndConsole && TranslateAccelerator(hwndMain, hAccelNoICS, &msg)) &&
\r
853 !(!hwndConsole && TranslateAccelerator(hwndMain, hAccelNoAlt, &msg))) {
\r
854 int done = 0, i; // [HGM] chat: dispatch cat-box messages
\r
855 for(i=0; i<MAX_CHAT; i++)
\r
856 if(chatHandle[i] && IsDialogMessage(chatHandle[i], &msg)) {
\r
859 if(done) continue; // [HGM] chat: end patch
\r
860 TranslateMessage(&msg); /* Translates virtual key codes */
\r
861 DispatchMessage(&msg); /* Dispatches message to window */
\r
866 return (msg.wParam); /* Returns the value from PostQuitMessage */
\r
869 /*---------------------------------------------------------------------------*\
\r
871 * Initialization functions
\r
873 \*---------------------------------------------------------------------------*/
\r
877 { // update user logo if necessary
\r
878 static char oldUserName[MSG_SIZ], *curName;
\r
880 if(appData.autoLogo) {
\r
881 curName = UserName();
\r
882 if(strcmp(curName, oldUserName)) {
\r
883 sprintf(oldUserName, "logos\\%s.bmp", curName);
\r
884 userLogo = LoadImage( 0, oldUserName, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );
\r
885 strcpy(oldUserName, curName);
\r
891 InitApplication(HINSTANCE hInstance)
\r
895 /* Fill in window class structure with parameters that describe the */
\r
898 wc.style = CS_HREDRAW | CS_VREDRAW; /* Class style(s). */
\r
899 wc.lpfnWndProc = (WNDPROC)WndProc; /* Window Procedure */
\r
900 wc.cbClsExtra = 0; /* No per-class extra data. */
\r
901 wc.cbWndExtra = 0; /* No per-window extra data. */
\r
902 wc.hInstance = hInstance; /* Owner of this class */
\r
903 wc.hIcon = LoadIcon(hInstance, "icon_white");
\r
904 wc.hCursor = LoadCursor(NULL, IDC_ARROW); /* Cursor */
\r
905 wc.hbrBackground = (HBRUSH)(COLOR_WINDOW+1); /* Default color */
\r
906 wc.lpszMenuName = szAppName; /* Menu name from .RC */
\r
907 wc.lpszClassName = szAppName; /* Name to register as */
\r
909 /* Register the window class and return success/failure code. */
\r
910 if (!RegisterClass(&wc)) return FALSE;
\r
912 wc.style = CS_HREDRAW | CS_VREDRAW;
\r
913 wc.lpfnWndProc = (WNDPROC)ConsoleWndProc;
\r
915 wc.cbWndExtra = DLGWINDOWEXTRA;
\r
916 wc.hInstance = hInstance;
\r
917 wc.hIcon = LoadIcon(hInstance, "icon_white");
\r
918 wc.hCursor = LoadCursor(NULL, IDC_ARROW);
\r
919 wc.hbrBackground = (HBRUSH)(COLOR_MENU+1);
\r
920 wc.lpszMenuName = NULL;
\r
921 wc.lpszClassName = szConsoleName;
\r
923 if (!RegisterClass(&wc)) return FALSE;
\r
928 /* Set by InitInstance, used by EnsureOnScreen */
\r
929 int screenHeight, screenWidth;
\r
932 EnsureOnScreen(int *x, int *y, int minX, int minY)
\r
934 // int gap = GetSystemMetrics(SM_CYFRAME) + GetSystemMetrics(SM_CYCAPTION);
\r
935 /* Be sure window at (x,y) is not off screen (or even mostly off screen) */
\r
936 if (*x > screenWidth - 32) *x = 0;
\r
937 if (*y > screenHeight - 32) *y = 0;
\r
938 if (*x < minX) *x = minX;
\r
939 if (*y < minY) *y = minY;
\r
943 InitInstance(HINSTANCE hInstance, int nCmdShow, LPSTR lpCmdLine)
\r
945 HWND hwnd; /* Main window handle. */
\r
947 WINDOWPLACEMENT wp;
\r
950 hInst = hInstance; /* Store instance handle in our global variable */
\r
951 programName = szAppName;
\r
953 if (SearchPath(NULL, "WinBoard.exe", NULL, MSG_SIZ, installDir, &filepart)) {
\r
954 *filepart = NULLCHAR;
\r
956 GetCurrentDirectory(MSG_SIZ, installDir);
\r
958 gameInfo.boardWidth = gameInfo.boardHeight = 8; // [HGM] won't have open window otherwise
\r
959 screenWidth = screenHeight = 1000; // [HGM] placement: kludge to allow calling EnsureOnScreen from InitAppData
\r
960 InitAppData(lpCmdLine); /* Get run-time parameters */
\r
961 /* xboard, and older WinBoards, controlled the move sound with the
\r
962 appData.ringBellAfterMoves option. In the current WinBoard, we
\r
963 always turn the option on (so that the backend will call us),
\r
964 then let the user turn the sound off by setting it to silence if
\r
965 desired. To accommodate old winboard.ini files saved by old
\r
966 versions of WinBoard, we also turn off the sound if the option
\r
967 was initially set to false. [HGM] taken out of InitAppData */
\r
968 if (!appData.ringBellAfterMoves) {
\r
969 sounds[(int)SoundMove].name = strdup("");
\r
970 appData.ringBellAfterMoves = TRUE;
\r
972 if (appData.debugMode) {
\r
973 debugFP = fopen(appData.nameOfDebugFile, "w");
\r
974 setbuf(debugFP, NULL);
\r
977 LoadLanguageFile(appData.language);
\r
981 // InitEngineUCI( installDir, &first ); // [HGM] incorporated in InitBackEnd1()
\r
982 // InitEngineUCI( installDir, &second );
\r
984 /* Create a main window for this application instance. */
\r
985 hwnd = CreateWindow(szAppName, szTitle,
\r
986 (WS_OVERLAPPEDWINDOW & ~WS_MAXIMIZEBOX),
\r
987 CW_USEDEFAULT, 0, CW_USEDEFAULT, 0,
\r
988 NULL, NULL, hInstance, NULL);
\r
991 /* If window could not be created, return "failure" */
\r
996 /* [HGM] logo: Load logos if specified (must be done before InitDrawingSizes) */
\r
997 if( appData.firstLogo && appData.firstLogo[0] != NULLCHAR) {
\r
998 first.programLogo = LoadImage( 0, appData.firstLogo, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );
\r
1000 if (first.programLogo == NULL && appData.debugMode) {
\r
1001 fprintf( debugFP, "Unable to load logo bitmap '%s'\n", appData.firstLogo );
\r
1003 } else if(appData.autoLogo) {
\r
1004 if(appData.firstDirectory && appData.firstDirectory[0]) {
\r
1005 char buf[MSG_SIZ];
\r
1006 sprintf(buf, "%s/logo.bmp", appData.firstDirectory);
\r
1007 first.programLogo = LoadImage( 0, buf, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );
\r
1011 if( appData.secondLogo && appData.secondLogo[0] != NULLCHAR) {
\r
1012 second.programLogo = LoadImage( 0, appData.secondLogo, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );
\r
1014 if (second.programLogo == NULL && appData.debugMode) {
\r
1015 fprintf( debugFP, "Unable to load logo bitmap '%s'\n", appData.secondLogo );
\r
1017 } else if(appData.autoLogo) {
\r
1018 char buf[MSG_SIZ];
\r
1019 if(appData.icsActive) { // [HGM] logo: in ICS mode second can be used for ICS
\r
1020 sprintf(buf, "logos\\%s.bmp", appData.icsHost);
\r
1021 second.programLogo = LoadImage( 0, buf, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );
\r
1023 if(appData.secondDirectory && appData.secondDirectory[0]) {
\r
1024 sprintf(buf, "%s\\logo.bmp", appData.secondDirectory);
\r
1025 second.programLogo = LoadImage( 0, buf, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );
\r
1031 iconWhite = LoadIcon(hInstance, "icon_white");
\r
1032 iconBlack = LoadIcon(hInstance, "icon_black");
\r
1033 iconCurrent = iconWhite;
\r
1034 InitDrawingColors();
\r
1035 screenHeight = GetSystemMetrics(SM_CYSCREEN);
\r
1036 screenWidth = GetSystemMetrics(SM_CXSCREEN);
\r
1037 for (ibs = (int) NUM_SIZES - 1; ibs >= 0; ibs--) {
\r
1038 /* Compute window size for each board size, and use the largest
\r
1039 size that fits on this screen as the default. */
\r
1040 InitDrawingSizes((BoardSize)(ibs+1000), 0);
\r
1041 if (boardSize == (BoardSize)-1 &&
\r
1042 winH <= screenHeight
\r
1043 - GetSystemMetrics(SM_CYFRAME) - GetSystemMetrics(SM_CYCAPTION) - 10
\r
1044 && winW <= screenWidth) {
\r
1045 boardSize = (BoardSize)ibs;
\r
1049 InitDrawingSizes(boardSize, 0);
\r
1050 TranslateMenus(1);
\r
1052 buttonCount = GetSystemMetrics(SM_CMOUSEBUTTONS);
\r
1054 /* [AS] Load textures if specified */
\r
1055 ZeroMemory( &backTextureSquareInfo, sizeof(backTextureSquareInfo) );
\r
1057 if( appData.liteBackTextureFile && appData.liteBackTextureFile[0] != NULLCHAR && appData.liteBackTextureFile[0] != '*' ) {
\r
1058 liteBackTexture = LoadImage( 0, appData.liteBackTextureFile, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );
\r
1059 liteBackTextureMode = appData.liteBackTextureMode;
\r
1061 if (liteBackTexture == NULL && appData.debugMode) {
\r
1062 fprintf( debugFP, "Unable to load lite texture bitmap '%s'\n", appData.liteBackTextureFile );
\r
1066 if( appData.darkBackTextureFile && appData.darkBackTextureFile[0] != NULLCHAR && appData.darkBackTextureFile[0] != '*' ) {
\r
1067 darkBackTexture = LoadImage( 0, appData.darkBackTextureFile, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );
\r
1068 darkBackTextureMode = appData.darkBackTextureMode;
\r
1070 if (darkBackTexture == NULL && appData.debugMode) {
\r
1071 fprintf( debugFP, "Unable to load dark texture bitmap '%s'\n", appData.darkBackTextureFile );
\r
1075 mysrandom( (unsigned) time(NULL) );
\r
1077 /* [AS] Restore layout */
\r
1078 if( wpMoveHistory.visible ) {
\r
1079 MoveHistoryPopUp();
\r
1082 if( wpEvalGraph.visible ) {
\r
1086 if( wpEngineOutput.visible ) {
\r
1087 EngineOutputPopUp();
\r
1090 /* Make the window visible; update its client area; and return "success" */
\r
1091 EnsureOnScreen(&wpMain.x, &wpMain.y, minX, minY);
\r
1092 wp.length = sizeof(WINDOWPLACEMENT);
\r
1094 wp.showCmd = nCmdShow;
\r
1095 wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;
\r
1096 wp.rcNormalPosition.left = wpMain.x;
\r
1097 wp.rcNormalPosition.right = wpMain.x + wpMain.width;
\r
1098 wp.rcNormalPosition.top = wpMain.y;
\r
1099 wp.rcNormalPosition.bottom = wpMain.y + wpMain.height;
\r
1100 SetWindowPlacement(hwndMain, &wp);
\r
1102 InitBackEnd2(); // [HGM] moved until after all windows placed, to save correct position if fatal error on engine start
\r
1104 if(!appData.noGUI) SetWindowPos(hwndMain, alwaysOnTop ? HWND_TOPMOST : HWND_NOTOPMOST,
\r
1105 0, 0, 0, 0, SWP_NOMOVE|SWP_NOSIZE);
\r
1107 if (hwndConsole) {
\r
1109 SetWindowPos(hwndConsole, alwaysOnTop ? HWND_TOPMOST : HWND_NOTOPMOST,
\r
1110 0, 0, 0, 0, SWP_NOMOVE|SWP_NOSIZE);
\r
1112 ShowWindow(hwndConsole, nCmdShow);
\r
1113 if(appData.chatBoxes) { // [HGM] chat: open chat boxes
\r
1114 char buf[MSG_SIZ], *p = buf, *q;
\r
1115 strcpy(buf, appData.chatBoxes);
\r
1117 q = strchr(p, ';');
\r
1119 if(*p) ChatPopUp(p);
\r
1122 SetActiveWindow(hwndConsole);
\r
1124 if(!appData.noGUI) UpdateWindow(hwnd); else ShowWindow(hwnd, SW_MINIMIZE);
\r
1125 if(gameListDialog) SetFocus(gameListDialog); // [HGM] jaws: for if we clicked multi-game game file
\r
1134 HMENU hmenu = GetMenu(hwndMain);
\r
1136 (void) EnableMenuItem(hmenu, IDM_CommPort,
\r
1137 MF_BYCOMMAND|((appData.icsActive &&
\r
1138 *appData.icsCommPort != NULLCHAR) ?
\r
1139 MF_ENABLED : MF_GRAYED));
\r
1140 (void) CheckMenuItem(hmenu, IDM_SaveSettingsOnExit,
\r
1141 MF_BYCOMMAND|(saveSettingsOnExit ?
\r
1142 MF_CHECKED : MF_UNCHECKED));
\r
1145 //---------------------------------------------------------------------------------------------------------
\r
1147 #define ICS_TEXT_MENU_SIZE (IDM_CommandXLast - IDM_CommandX + 1)
\r
1148 #define XBOARD FALSE
\r
1150 #define OPTCHAR "/"
\r
1151 #define SEPCHAR "="
\r
1155 // front-end part of option handling
\r
1158 LFfromMFP(LOGFONT* lf, MyFontParams *mfp)
\r
1160 HDC hdc = CreateDC("DISPLAY", NULL, NULL, NULL);
\r
1161 lf->lfHeight = -(int)(mfp->pointSize * GetDeviceCaps(hdc, LOGPIXELSY) / 72.0 + 0.5);
\r
1164 lf->lfEscapement = 0;
\r
1165 lf->lfOrientation = 0;
\r
1166 lf->lfWeight = mfp->bold ? FW_BOLD : FW_NORMAL;
\r
1167 lf->lfItalic = mfp->italic;
\r
1168 lf->lfUnderline = mfp->underline;
\r
1169 lf->lfStrikeOut = mfp->strikeout;
\r
1170 lf->lfCharSet = mfp->charset;
\r
1171 lf->lfOutPrecision = OUT_DEFAULT_PRECIS;
\r
1172 lf->lfClipPrecision = CLIP_DEFAULT_PRECIS;
\r
1173 lf->lfQuality = DEFAULT_QUALITY;
\r
1174 lf->lfPitchAndFamily = DEFAULT_PITCH|FF_DONTCARE;
\r
1175 strcpy(lf->lfFaceName, mfp->faceName);
\r
1179 CreateFontInMF(MyFont *mf)
\r
1181 LFfromMFP(&mf->lf, &mf->mfp);
\r
1182 if (mf->hf) DeleteObject(mf->hf);
\r
1183 mf->hf = CreateFontIndirect(&mf->lf);
\r
1186 // [HGM] This platform-dependent table provides the location for storing the color info
\r
1188 colorVariable[] = {
\r
1189 &whitePieceColor,
\r
1190 &blackPieceColor,
\r
1191 &lightSquareColor,
\r
1192 &darkSquareColor,
\r
1193 &highlightSquareColor,
\r
1194 &premoveHighlightColor,
\r
1196 &consoleBackgroundColor,
\r
1197 &appData.fontForeColorWhite,
\r
1198 &appData.fontBackColorWhite,
\r
1199 &appData.fontForeColorBlack,
\r
1200 &appData.fontBackColorBlack,
\r
1201 &appData.evalHistColorWhite,
\r
1202 &appData.evalHistColorBlack,
\r
1203 &appData.highlightArrowColor,
\r
1206 /* Command line font name parser. NULL name means do nothing.
\r
1207 Syntax like "Courier New:10.0 bi" or "Arial:10" or "Arial:10b"
\r
1208 For backward compatibility, syntax without the colon is also
\r
1209 accepted, but font names with digits in them won't work in that case.
\r
1212 ParseFontName(char *name, MyFontParams *mfp)
\r
1215 if (name == NULL) return;
\r
1217 q = strchr(p, ':');
\r
1219 if (q - p >= sizeof(mfp->faceName))
\r
1220 ExitArgError(_("Font name too long:"), name);
\r
1221 memcpy(mfp->faceName, p, q - p);
\r
1222 mfp->faceName[q - p] = NULLCHAR;
\r
1225 q = mfp->faceName;
\r
1226 while (*p && !isdigit(*p)) {
\r
1228 if (q - mfp->faceName >= sizeof(mfp->faceName))
\r
1229 ExitArgError(_("Font name too long:"), name);
\r
1231 while (q > mfp->faceName && q[-1] == ' ') q--;
\r
1234 if (!*p) ExitArgError(_("Font point size missing:"), name);
\r
1235 mfp->pointSize = (float) atof(p);
\r
1236 mfp->bold = (strchr(p, 'b') != NULL);
\r
1237 mfp->italic = (strchr(p, 'i') != NULL);
\r
1238 mfp->underline = (strchr(p, 'u') != NULL);
\r
1239 mfp->strikeout = (strchr(p, 's') != NULL);
\r
1240 mfp->charset = DEFAULT_CHARSET;
\r
1241 q = strchr(p, 'c');
\r
1243 mfp->charset = (BYTE) atoi(q+1);
\r
1247 ParseFont(char *name, int number)
\r
1248 { // wrapper to shield back-end from 'font'
\r
1249 ParseFontName(name, &font[boardSize][number]->mfp);
\r
1254 { // in WB we have a 2D array of fonts; this initializes their description
\r
1256 /* Point font array elements to structures and
\r
1257 parse default font names */
\r
1258 for (i=0; i<NUM_FONTS; i++) {
\r
1259 for (j=0; j<NUM_SIZES; j++) {
\r
1260 font[j][i] = &fontRec[j][i];
\r
1261 ParseFontName(font[j][i]->def, &font[j][i]->mfp);
\r
1268 { // here we create the actual fonts from the selected descriptions
\r
1270 for (i=0; i<NUM_FONTS; i++) {
\r
1271 for (j=0; j<NUM_SIZES; j++) {
\r
1272 CreateFontInMF(font[j][i]);
\r
1276 /* Color name parser.
\r
1277 X version accepts X color names, but this one
\r
1278 handles only the #rrggbb form (hex) or rrr,ggg,bbb (decimal) */
\r
1280 ParseColorName(char *name)
\r
1282 int red, green, blue, count;
\r
1283 char buf[MSG_SIZ];
\r
1285 count = sscanf(name, "#%2x%2x%2x", &red, &green, &blue);
\r
1287 count = sscanf(name, "%3d%*[^0-9]%3d%*[^0-9]%3d",
\r
1288 &red, &green, &blue);
\r
1291 sprintf(buf, _("Can't parse color name %s"), name);
\r
1292 DisplayError(buf, 0);
\r
1293 return RGB(0, 0, 0);
\r
1295 return PALETTERGB(red, green, blue);
\r
1299 ParseColor(int n, char *name)
\r
1300 { // for WinBoard the color is an int, which needs to be derived from the string
\r
1301 if(colorVariable[n]) *(int*)colorVariable[n] = ParseColorName(name);
\r
1305 ParseAttribs(COLORREF *color, int *effects, char* argValue)
\r
1307 char *e = argValue;
\r
1311 if (*e == 'b') eff |= CFE_BOLD;
\r
1312 else if (*e == 'i') eff |= CFE_ITALIC;
\r
1313 else if (*e == 'u') eff |= CFE_UNDERLINE;
\r
1314 else if (*e == 's') eff |= CFE_STRIKEOUT;
\r
1315 else if (*e == '#' || isdigit(*e)) break;
\r
1319 *color = ParseColorName(e);
\r
1323 ParseTextAttribs(ColorClass cc, char *s)
\r
1324 { // [HGM] front-end wrapper that does the platform-dependent call
\r
1325 // for XBoard we would set (&appData.colorShout)[cc] = strdup(s);
\r
1326 ParseAttribs(&textAttribs[cc].color, &textAttribs[cc].effects, s);
\r
1330 ParseBoardSize(void *addr, char *name)
\r
1331 { // [HGM] rewritten with return-value ptr to shield back-end from BoardSize
\r
1332 BoardSize bs = SizeTiny;
\r
1333 while (sizeInfo[bs].name != NULL) {
\r
1334 if (StrCaseCmp(name, sizeInfo[bs].name) == 0) {
\r
1335 *(BoardSize *)addr = bs;
\r
1340 ExitArgError(_("Unrecognized board size value"), name);
\r
1345 { // [HGM] import name from appData first
\r
1348 for (cc = (ColorClass)0; cc < ColorNormal; cc++) {
\r
1349 textAttribs[cc].sound.name = strdup((&appData.soundShout)[cc]);
\r
1350 textAttribs[cc].sound.data = NULL;
\r
1351 MyLoadSound(&textAttribs[cc].sound);
\r
1353 for (cc = ColorNormal; cc < NColorClasses; cc++) {
\r
1354 textAttribs[cc].sound.name = strdup("");
\r
1355 textAttribs[cc].sound.data = NULL;
\r
1357 for (sc = (SoundClass)0; sc < NSoundClasses; sc++) {
\r
1358 sounds[sc].name = strdup((&appData.soundMove)[sc]);
\r
1359 sounds[sc].data = NULL;
\r
1360 MyLoadSound(&sounds[sc]);
\r
1365 SetCommPortDefaults()
\r
1367 memset(&dcb, 0, sizeof(DCB)); // required by VS 2002 +
\r
1368 dcb.DCBlength = sizeof(DCB);
\r
1369 dcb.BaudRate = 9600;
\r
1370 dcb.fBinary = TRUE;
\r
1371 dcb.fParity = FALSE;
\r
1372 dcb.fOutxCtsFlow = FALSE;
\r
1373 dcb.fOutxDsrFlow = FALSE;
\r
1374 dcb.fDtrControl = DTR_CONTROL_ENABLE;
\r
1375 dcb.fDsrSensitivity = FALSE;
\r
1376 dcb.fTXContinueOnXoff = TRUE;
\r
1377 dcb.fOutX = FALSE;
\r
1379 dcb.fNull = FALSE;
\r
1380 dcb.fRtsControl = RTS_CONTROL_ENABLE;
\r
1381 dcb.fAbortOnError = FALSE;
\r
1383 dcb.Parity = SPACEPARITY;
\r
1384 dcb.StopBits = ONESTOPBIT;
\r
1387 // [HGM] args: these three cases taken out to stay in front-end
\r
1389 SaveFontArg(FILE *f, ArgDescriptor *ad)
\r
1390 { // in WinBoard every board size has its own font, and the "argLoc" identifies the table,
\r
1391 // while the curent board size determines the element. This system should be ported to XBoard.
\r
1392 // What the table contains pointers to, and how to print the font description, remains platform-dependent
\r
1394 for (bs=0; bs<NUM_SIZES; bs++) {
\r
1395 MyFontParams *mfp = &font[bs][(int) ad->argLoc]->mfp;
\r
1396 fprintf(f, "/size=%s ", sizeInfo[bs].name);
\r
1397 fprintf(f, "/%s=\"%s:%g%s%s%s%s%sc%d\"\n",
\r
1398 ad->argName, mfp->faceName, mfp->pointSize,
\r
1399 mfp->bold || mfp->italic || mfp->underline || mfp->strikeout ? " " : "",
\r
1400 mfp->bold ? "b" : "",
\r
1401 mfp->italic ? "i" : "",
\r
1402 mfp->underline ? "u" : "",
\r
1403 mfp->strikeout ? "s" : "",
\r
1404 (int)mfp->charset);
\r
1410 { // [HGM] copy the names from the internal WB variables to appData
\r
1413 for (cc = (ColorClass)0; cc < ColorNormal; cc++)
\r
1414 (&appData.soundShout)[cc] = textAttribs[cc].sound.name;
\r
1415 for (sc = (SoundClass)0; sc < NSoundClasses; sc++)
\r
1416 (&appData.soundMove)[sc] = sounds[sc].name;
\r
1420 SaveAttribsArg(FILE *f, ArgDescriptor *ad)
\r
1421 { // here the "argLoc" defines a table index. It could have contained the 'ta' pointer itself, though
\r
1422 MyTextAttribs* ta = &textAttribs[(ColorClass)ad->argLoc];
\r
1423 fprintf(f, "/%s=\"%s%s%s%s%s#%02lx%02lx%02lx\"\n", ad->argName,
\r
1424 (ta->effects & CFE_BOLD) ? "b" : "",
\r
1425 (ta->effects & CFE_ITALIC) ? "i" : "",
\r
1426 (ta->effects & CFE_UNDERLINE) ? "u" : "",
\r
1427 (ta->effects & CFE_STRIKEOUT) ? "s" : "",
\r
1428 (ta->effects) ? " " : "",
\r
1429 ta->color&0xff, (ta->color >> 8)&0xff, (ta->color >> 16)&0xff);
\r
1433 SaveColor(FILE *f, ArgDescriptor *ad)
\r
1434 { // in WinBoard the color is an int and has to be converted to text. In X it would be a string already?
\r
1435 COLORREF color = *(COLORREF *)colorVariable[(int)ad->argLoc];
\r
1436 fprintf(f, "/%s=#%02lx%02lx%02lx\n", ad->argName,
\r
1437 color&0xff, (color>>8)&0xff, (color>>16)&0xff);
\r
1441 SaveBoardSize(FILE *f, char *name, void *addr)
\r
1442 { // wrapper to shield back-end from BoardSize & sizeInfo
\r
1443 fprintf(f, "/%s=%s\n", name, sizeInfo[*(BoardSize *)addr].name);
\r
1447 ParseCommPortSettings(char *s)
\r
1448 { // wrapper to keep dcb from back-end
\r
1449 ParseCommSettings(s, &dcb);
\r
1454 { // wrapper to shield use of window handles from back-end (make addressible by number?)
\r
1455 GetActualPlacement(hwndMain, &wpMain);
\r
1456 GetActualPlacement(hwndConsole, &wpConsole);
\r
1457 GetActualPlacement(commentDialog, &wpComment);
\r
1458 GetActualPlacement(editTagsDialog, &wpTags);
\r
1459 GetActualPlacement(gameListDialog, &wpGameList);
\r
1460 GetActualPlacement(moveHistoryDialog, &wpMoveHistory);
\r
1461 GetActualPlacement(evalGraphDialog, &wpEvalGraph);
\r
1462 GetActualPlacement(engineOutputDialog, &wpEngineOutput);
\r
1466 PrintCommPortSettings(FILE *f, char *name)
\r
1467 { // wrapper to shield back-end from DCB
\r
1468 PrintCommSettings(f, name, &dcb);
\r
1472 MySearchPath(char *installDir, char *name, char *fullname)
\r
1474 char *dummy, buf[MSG_SIZ], *p = name, *q;
\r
1475 if(name[0]== '%') {
\r
1476 fullname[0] = 0; // [HGM] first expand any environment variables in the given name
\r
1477 while(*p == '%' && (q = strchr(p+1, '%'))) { // [HGM] recognize %*% as environment variable
\r
1479 *strchr(buf, '%') = 0;
\r
1480 strcat(fullname, getenv(buf));
\r
1481 p = q+1; while(*p == '\\') { strcat(fullname, "\\"); p++; }
\r
1483 strcat(fullname, p); // after environment variables (if any), take the remainder of the given name
\r
1484 if(appData.debugMode) fprintf(debugFP, "name = '%s', expanded name = '%s'\n", name, fullname);
\r
1485 return (int) strlen(fullname);
\r
1487 return (int) SearchPath(installDir, name, NULL, MSG_SIZ, fullname, &dummy);
\r
1491 MyGetFullPathName(char *name, char *fullname)
\r
1494 return (int) GetFullPathName(name, MSG_SIZ, fullname, &dummy);
\r
1499 { // [HGM] args: allows testing if main window is realized from back-end
\r
1500 return hwndMain != NULL;
\r
1504 PopUpStartupDialog()
\r
1508 LoadLanguageFile(appData.language);
\r
1509 lpProc = MakeProcInstance((FARPROC)StartupDialog, hInst);
\r
1510 DialogBox(hInst, MAKEINTRESOURCE(DLG_Startup), NULL, (DLGPROC)lpProc);
\r
1511 FreeProcInstance(lpProc);
\r
1514 /*---------------------------------------------------------------------------*\
\r
1516 * GDI board drawing routines
\r
1518 \*---------------------------------------------------------------------------*/
\r
1520 /* [AS] Draw square using background texture */
\r
1521 static void DrawTile( int dx, int dy, int dw, int dh, HDC dst, HDC src, int mode, int sx, int sy )
\r
1526 return; /* Should never happen! */
\r
1529 SetGraphicsMode( dst, GM_ADVANCED );
\r
1536 /* X reflection */
\r
1541 x.eDx = (FLOAT) dw + dx - 1;
\r
1544 SetWorldTransform( dst, &x );
\r
1547 /* Y reflection */
\r
1553 x.eDy = (FLOAT) dh + dy - 1;
\r
1555 SetWorldTransform( dst, &x );
\r
1563 x.eDx = (FLOAT) dx;
\r
1564 x.eDy = (FLOAT) dy;
\r
1567 SetWorldTransform( dst, &x );
\r
1571 BitBlt( dst, dx, dy, dw, dh, src, sx, sy, SRCCOPY );
\r
1579 SetWorldTransform( dst, &x );
\r
1581 ModifyWorldTransform( dst, 0, MWT_IDENTITY );
\r
1584 /* [AS] [HGM] Make room for more piece types, so all pieces can be different */
\r
1586 PM_WP = (int) WhitePawn,
\r
1587 PM_WN = (int) WhiteKnight,
\r
1588 PM_WB = (int) WhiteBishop,
\r
1589 PM_WR = (int) WhiteRook,
\r
1590 PM_WQ = (int) WhiteQueen,
\r
1591 PM_WF = (int) WhiteFerz,
\r
1592 PM_WW = (int) WhiteWazir,
\r
1593 PM_WE = (int) WhiteAlfil,
\r
1594 PM_WM = (int) WhiteMan,
\r
1595 PM_WO = (int) WhiteCannon,
\r
1596 PM_WU = (int) WhiteUnicorn,
\r
1597 PM_WH = (int) WhiteNightrider,
\r
1598 PM_WA = (int) WhiteAngel,
\r
1599 PM_WC = (int) WhiteMarshall,
\r
1600 PM_WAB = (int) WhiteCardinal,
\r
1601 PM_WD = (int) WhiteDragon,
\r
1602 PM_WL = (int) WhiteLance,
\r
1603 PM_WS = (int) WhiteCobra,
\r
1604 PM_WV = (int) WhiteFalcon,
\r
1605 PM_WSG = (int) WhiteSilver,
\r
1606 PM_WG = (int) WhiteGrasshopper,
\r
1607 PM_WK = (int) WhiteKing,
\r
1608 PM_BP = (int) BlackPawn,
\r
1609 PM_BN = (int) BlackKnight,
\r
1610 PM_BB = (int) BlackBishop,
\r
1611 PM_BR = (int) BlackRook,
\r
1612 PM_BQ = (int) BlackQueen,
\r
1613 PM_BF = (int) BlackFerz,
\r
1614 PM_BW = (int) BlackWazir,
\r
1615 PM_BE = (int) BlackAlfil,
\r
1616 PM_BM = (int) BlackMan,
\r
1617 PM_BO = (int) BlackCannon,
\r
1618 PM_BU = (int) BlackUnicorn,
\r
1619 PM_BH = (int) BlackNightrider,
\r
1620 PM_BA = (int) BlackAngel,
\r
1621 PM_BC = (int) BlackMarshall,
\r
1622 PM_BG = (int) BlackGrasshopper,
\r
1623 PM_BAB = (int) BlackCardinal,
\r
1624 PM_BD = (int) BlackDragon,
\r
1625 PM_BL = (int) BlackLance,
\r
1626 PM_BS = (int) BlackCobra,
\r
1627 PM_BV = (int) BlackFalcon,
\r
1628 PM_BSG = (int) BlackSilver,
\r
1629 PM_BK = (int) BlackKing
\r
1632 static HFONT hPieceFont = NULL;
\r
1633 static HBITMAP hPieceMask[(int) EmptySquare];
\r
1634 static HBITMAP hPieceFace[(int) EmptySquare];
\r
1635 static int fontBitmapSquareSize = 0;
\r
1636 static char pieceToFontChar[(int) EmptySquare] =
\r
1637 { 'p', 'n', 'b', 'r', 'q',
\r
1638 'n', 'b', 'p', 'n', 'b', 'r', 'b', 'r', 'q', 'k',
\r
1639 'k', 'o', 'm', 'v', 't', 'w',
\r
1640 'v', 't', 'o', 'm', 'v', 't', 'v', 't', 'w', 'l',
\r
1643 extern BOOL SetCharTable( char *table, const char * map );
\r
1644 /* [HGM] moved to backend.c */
\r
1646 static void SetPieceBackground( HDC hdc, COLORREF color, int mode )
\r
1649 BYTE r1 = GetRValue( color );
\r
1650 BYTE g1 = GetGValue( color );
\r
1651 BYTE b1 = GetBValue( color );
\r
1657 /* Create a uniform background first */
\r
1658 hbrush = CreateSolidBrush( color );
\r
1659 SetRect( &rc, 0, 0, squareSize, squareSize );
\r
1660 FillRect( hdc, &rc, hbrush );
\r
1661 DeleteObject( hbrush );
\r
1664 /* Vertical gradient, good for pawn, knight and rook, less for queen and king */
\r
1665 int steps = squareSize / 2;
\r
1668 for( i=0; i<steps; i++ ) {
\r
1669 BYTE r = r1 - (r1-r2) * i / steps;
\r
1670 BYTE g = g1 - (g1-g2) * i / steps;
\r
1671 BYTE b = b1 - (b1-b2) * i / steps;
\r
1673 hbrush = CreateSolidBrush( RGB(r,g,b) );
\r
1674 SetRect( &rc, i + squareSize - steps, 0, i + squareSize - steps + 1, squareSize );
\r
1675 FillRect( hdc, &rc, hbrush );
\r
1676 DeleteObject(hbrush);
\r
1679 else if( mode == 2 ) {
\r
1680 /* Diagonal gradient, good more or less for every piece */
\r
1681 POINT triangle[3];
\r
1682 HPEN hpen = SelectObject( hdc, GetStockObject(NULL_PEN) );
\r
1683 HBRUSH hbrush_old;
\r
1684 int steps = squareSize;
\r
1687 triangle[0].x = squareSize - steps;
\r
1688 triangle[0].y = squareSize;
\r
1689 triangle[1].x = squareSize;
\r
1690 triangle[1].y = squareSize;
\r
1691 triangle[2].x = squareSize;
\r
1692 triangle[2].y = squareSize - steps;
\r
1694 for( i=0; i<steps; i++ ) {
\r
1695 BYTE r = r1 - (r1-r2) * i / steps;
\r
1696 BYTE g = g1 - (g1-g2) * i / steps;
\r
1697 BYTE b = b1 - (b1-b2) * i / steps;
\r
1699 hbrush = CreateSolidBrush( RGB(r,g,b) );
\r
1700 hbrush_old = SelectObject( hdc, hbrush );
\r
1701 Polygon( hdc, triangle, 3 );
\r
1702 SelectObject( hdc, hbrush_old );
\r
1703 DeleteObject(hbrush);
\r
1708 SelectObject( hdc, hpen );
\r
1713 [AS] The method I use to create the bitmaps it a bit tricky, but it
\r
1714 seems to work ok. The main problem here is to find the "inside" of a chess
\r
1715 piece: follow the steps as explained below.
\r
1717 static void CreatePieceMaskFromFont( HDC hdc_window, HDC hdc, int index )
\r
1721 COLORREF chroma = RGB(0xFF,0x00,0xFF);
\r
1725 int backColor = whitePieceColor;
\r
1726 int foreColor = blackPieceColor;
\r
1728 if( index < (int)BlackPawn && appData.fontBackColorWhite != appData.fontForeColorWhite ) {
\r
1729 backColor = appData.fontBackColorWhite;
\r
1730 foreColor = appData.fontForeColorWhite;
\r
1732 else if( index >= (int)BlackPawn && appData.fontBackColorBlack != appData.fontForeColorBlack ) {
\r
1733 backColor = appData.fontBackColorBlack;
\r
1734 foreColor = appData.fontForeColorBlack;
\r
1738 hbm = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );
\r
1740 hbm_old = SelectObject( hdc, hbm );
\r
1744 rc.right = squareSize;
\r
1745 rc.bottom = squareSize;
\r
1747 /* Step 1: background is now black */
\r
1748 FillRect( hdc, &rc, GetStockObject(BLACK_BRUSH) );
\r
1750 GetTextExtentPoint32( hdc, &pieceToFontChar[index], 1, &sz );
\r
1752 pt.x = (squareSize - sz.cx) / 2;
\r
1753 pt.y = (squareSize - sz.cy) / 2;
\r
1755 SetBkMode( hdc, TRANSPARENT );
\r
1756 SetTextColor( hdc, chroma );
\r
1757 /* Step 2: the piece has been drawn in purple, there are now black and purple in this bitmap */
\r
1758 TextOut( hdc, pt.x, pt.y, &pieceToFontChar[appData.allWhite && index >= (int)BlackPawn ? index - (int)BlackPawn : index], 1 );
\r
1760 SelectObject( hdc, GetStockObject(WHITE_BRUSH) );
\r
1761 /* Step 3: the area outside the piece is filled with white */
\r
1762 // FloodFill( hdc, 0, 0, chroma );
\r
1763 ExtFloodFill( hdc, 0, 0, 0, FLOODFILLSURFACE );
\r
1764 ExtFloodFill( hdc, 0, squareSize-1, 0, FLOODFILLSURFACE ); // [HGM] fill from all 4 corners, for if piece too big
\r
1765 ExtFloodFill( hdc, squareSize-1, 0, 0, FLOODFILLSURFACE );
\r
1766 ExtFloodFill( hdc, squareSize-1, squareSize-1, 0, FLOODFILLSURFACE );
\r
1767 SelectObject( hdc, GetStockObject(BLACK_BRUSH) );
\r
1769 Step 4: this is the tricky part, the area inside the piece is filled with black,
\r
1770 but if the start point is not inside the piece we're lost!
\r
1771 There should be a better way to do this... if we could create a region or path
\r
1772 from the fill operation we would be fine for example.
\r
1774 // FloodFill( hdc, squareSize / 2, squareSize / 2, RGB(0xFF,0xFF,0xFF) );
\r
1775 ExtFloodFill( hdc, squareSize / 2, squareSize / 2, RGB(0xFF,0xFF,0xFF), FLOODFILLBORDER );
\r
1777 { /* [HGM] shave off edges of mask, in an attempt to correct for the fact that FloodFill does not work correctly under Win XP */
\r
1778 HDC dc2 = CreateCompatibleDC( hdc_window );
\r
1779 HBITMAP bm2 = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );
\r
1781 SelectObject( dc2, bm2 );
\r
1782 BitBlt( dc2, 0, 0, squareSize, squareSize, hdc, 0, 0, SRCCOPY ); // make copy
\r
1783 BitBlt( hdc, 0, 1, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );
\r
1784 BitBlt( hdc, 2, 1, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );
\r
1785 BitBlt( hdc, 1, 0, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );
\r
1786 BitBlt( hdc, 1, 2, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );
\r
1789 DeleteObject( bm2 );
\r
1792 SetTextColor( hdc, 0 );
\r
1794 Step 5: some fonts have "disconnected" areas that are skipped by the fill:
\r
1795 draw the piece again in black for safety.
\r
1797 TextOut( hdc, pt.x, pt.y, &pieceToFontChar[appData.allWhite && index >= (int)BlackPawn ? index - (int)BlackPawn : index], 1 );
\r
1799 SelectObject( hdc, hbm_old );
\r
1801 if( hPieceMask[index] != NULL ) {
\r
1802 DeleteObject( hPieceMask[index] );
\r
1805 hPieceMask[index] = hbm;
\r
1808 hbm = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );
\r
1810 SelectObject( hdc, hbm );
\r
1813 HDC dc1 = CreateCompatibleDC( hdc_window );
\r
1814 HDC dc2 = CreateCompatibleDC( hdc_window );
\r
1815 HBITMAP bm2 = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );
\r
1817 SelectObject( dc1, hPieceMask[index] );
\r
1818 SelectObject( dc2, bm2 );
\r
1819 FillRect( dc2, &rc, GetStockObject(WHITE_BRUSH) );
\r
1820 BitBlt( dc2, 0, 0, squareSize, squareSize, dc1, 0, 0, SRCINVERT );
\r
1823 Now dc2 contains the inverse of the piece mask, i.e. a mask that preserves
\r
1824 the piece background and deletes (makes transparent) the rest.
\r
1825 Thanks to that mask, we are free to paint the background with the greates
\r
1826 freedom, as we'll be able to mask off the unwanted parts when finished.
\r
1827 We use this, to make gradients and give the pieces a "roundish" look.
\r
1829 SetPieceBackground( hdc, backColor, 2 );
\r
1830 BitBlt( hdc, 0, 0, squareSize, squareSize, dc2, 0, 0, SRCAND );
\r
1834 DeleteObject( bm2 );
\r
1837 SetTextColor( hdc, foreColor );
\r
1838 TextOut( hdc, pt.x, pt.y, &pieceToFontChar[appData.allWhite && index >= (int)BlackPawn ? index - (int)BlackPawn : index], 1 );
\r
1840 SelectObject( hdc, hbm_old );
\r
1842 if( hPieceFace[index] != NULL ) {
\r
1843 DeleteObject( hPieceFace[index] );
\r
1846 hPieceFace[index] = hbm;
\r
1849 static int TranslatePieceToFontPiece( int piece )
\r
1879 case BlackMarshall:
\r
1883 case BlackNightrider:
\r
1889 case BlackUnicorn:
\r
1893 case BlackGrasshopper:
\r
1905 case BlackCardinal:
\r
1912 case WhiteMarshall:
\r
1916 case WhiteNightrider:
\r
1922 case WhiteUnicorn:
\r
1926 case WhiteGrasshopper:
\r
1938 case WhiteCardinal:
\r
1947 void CreatePiecesFromFont()
\r
1950 HDC hdc_window = NULL;
\r
1956 if( fontBitmapSquareSize < 0 ) {
\r
1957 /* Something went seriously wrong in the past: do not try to recreate fonts! */
\r
1961 if( appData.renderPiecesWithFont == NULL || appData.renderPiecesWithFont[0] == NULLCHAR || appData.renderPiecesWithFont[0] == '*' ) {
\r
1962 fontBitmapSquareSize = -1;
\r
1966 if( fontBitmapSquareSize != squareSize ) {
\r
1967 hdc_window = GetDC( hwndMain );
\r
1968 hdc = CreateCompatibleDC( hdc_window );
\r
1970 if( hPieceFont != NULL ) {
\r
1971 DeleteObject( hPieceFont );
\r
1974 for( i=0; i<=(int)BlackKing; i++ ) {
\r
1975 hPieceMask[i] = NULL;
\r
1976 hPieceFace[i] = NULL;
\r
1982 if( appData.fontPieceSize >= 50 && appData.fontPieceSize <= 150 ) {
\r
1983 fontHeight = appData.fontPieceSize;
\r
1986 fontHeight = (fontHeight * squareSize) / 100;
\r
1988 lf.lfHeight = -MulDiv( fontHeight, GetDeviceCaps(hdc, LOGPIXELSY), 72 );
\r
1990 lf.lfEscapement = 0;
\r
1991 lf.lfOrientation = 0;
\r
1992 lf.lfWeight = FW_NORMAL;
\r
1994 lf.lfUnderline = 0;
\r
1995 lf.lfStrikeOut = 0;
\r
1996 lf.lfCharSet = DEFAULT_CHARSET;
\r
1997 lf.lfOutPrecision = OUT_DEFAULT_PRECIS;
\r
1998 lf.lfClipPrecision = CLIP_DEFAULT_PRECIS;
\r
1999 lf.lfQuality = PROOF_QUALITY;
\r
2000 lf.lfPitchAndFamily = DEFAULT_PITCH | FF_DONTCARE;
\r
2001 strncpy( lf.lfFaceName, appData.renderPiecesWithFont, sizeof(lf.lfFaceName) );
\r
2002 lf.lfFaceName[ sizeof(lf.lfFaceName) - 1 ] = '\0';
\r
2004 hPieceFont = CreateFontIndirect( &lf );
\r
2006 if( hPieceFont == NULL ) {
\r
2007 fontBitmapSquareSize = -2;
\r
2010 /* Setup font-to-piece character table */
\r
2011 if( ! SetCharTable(pieceToFontChar, appData.fontToPieceTable) ) {
\r
2012 /* No (or wrong) global settings, try to detect the font */
\r
2013 if( strstr(lf.lfFaceName,"Alpha") != NULL ) {
\r
2015 SetCharTable(pieceToFontChar, "phbrqkojntwl");
\r
2017 else if( strstr(lf.lfFaceName,"DiagramTT") != NULL ) {
\r
2018 /* DiagramTT* family */
\r
2019 SetCharTable(pieceToFontChar, "PNLRQKpnlrqk");
\r
2021 else if( strstr(lf.lfFaceName,"WinboardF") != NULL ) {
\r
2022 /* Fairy symbols */
\r
2023 SetCharTable(pieceToFontChar, "PNBRQFEACWMOHIJGDVSLUKpnbrqfeacwmohijgdvsluk");
\r
2025 else if( strstr(lf.lfFaceName,"GC2004D") != NULL ) {
\r
2026 /* Good Companion (Some characters get warped as literal :-( */
\r
2027 char s[] = "1cmWG0??S??oYI23wgQU";
\r
2028 s[0]=0xB9; s[1]=0xA9; s[6]=0xB1; s[11]=0xBB; s[12]=0xAB; s[17]=0xB3;
\r
2029 SetCharTable(pieceToFontChar, s);
\r
2032 /* Cases, Condal, Leipzig, Lucena, Marroquin, Merida, Usual */
\r
2033 SetCharTable(pieceToFontChar, "pnbrqkomvtwl");
\r
2037 /* Create bitmaps */
\r
2038 hfont_old = SelectObject( hdc, hPieceFont );
\r
2039 for(i=(int)WhitePawn; i<(int)EmptySquare; i++) /* [HGM] made a loop for this */
\r
2040 if(PieceToChar((ChessSquare)i) != '.') /* skip unused pieces */
\r
2041 CreatePieceMaskFromFont( hdc_window, hdc, i );
\r
2043 SelectObject( hdc, hfont_old );
\r
2045 fontBitmapSquareSize = squareSize;
\r
2049 if( hdc != NULL ) {
\r
2053 if( hdc_window != NULL ) {
\r
2054 ReleaseDC( hwndMain, hdc_window );
\r
2059 DoLoadBitmap(HINSTANCE hinst, char *piece, int squareSize, char *suffix)
\r
2063 sprintf(name, "%s%d%s", piece, squareSize, suffix);
\r
2064 if (gameInfo.event &&
\r
2065 strcmp(gameInfo.event, "Easter Egg Hunt") == 0 &&
\r
2066 strcmp(name, "k80s") == 0) {
\r
2067 strcpy(name, "tim");
\r
2069 return LoadBitmap(hinst, name);
\r
2073 /* Insert a color into the program's logical palette
\r
2074 structure. This code assumes the given color is
\r
2075 the result of the RGB or PALETTERGB macro, and it
\r
2076 knows how those macros work (which is documented).
\r
2079 InsertInPalette(COLORREF color)
\r
2081 LPPALETTEENTRY pe = &(pLogPal->palPalEntry[pLogPal->palNumEntries]);
\r
2083 if (pLogPal->palNumEntries++ >= PALETTESIZE) {
\r
2084 DisplayFatalError(_("Too many colors"), 0, 1);
\r
2085 pLogPal->palNumEntries--;
\r
2089 pe->peFlags = (char) 0;
\r
2090 pe->peRed = (char) (0xFF & color);
\r
2091 pe->peGreen = (char) (0xFF & (color >> 8));
\r
2092 pe->peBlue = (char) (0xFF & (color >> 16));
\r
2098 InitDrawingColors()
\r
2100 if (pLogPal == NULL) {
\r
2101 /* Allocate enough memory for a logical palette with
\r
2102 * PALETTESIZE entries and set the size and version fields
\r
2103 * of the logical palette structure.
\r
2105 pLogPal = (NPLOGPALETTE)
\r
2106 LocalAlloc(LMEM_FIXED, (sizeof(LOGPALETTE) +
\r
2107 (sizeof(PALETTEENTRY) * (PALETTESIZE))));
\r
2108 pLogPal->palVersion = 0x300;
\r
2110 pLogPal->palNumEntries = 0;
\r
2112 InsertInPalette(lightSquareColor);
\r
2113 InsertInPalette(darkSquareColor);
\r
2114 InsertInPalette(whitePieceColor);
\r
2115 InsertInPalette(blackPieceColor);
\r
2116 InsertInPalette(highlightSquareColor);
\r
2117 InsertInPalette(premoveHighlightColor);
\r
2119 /* create a logical color palette according the information
\r
2120 * in the LOGPALETTE structure.
\r
2122 hPal = CreatePalette((LPLOGPALETTE) pLogPal);
\r
2124 lightSquareBrush = CreateSolidBrush(lightSquareColor);
\r
2125 blackSquareBrush = CreateSolidBrush(blackPieceColor);
\r
2126 darkSquareBrush = CreateSolidBrush(darkSquareColor);
\r
2127 whitePieceBrush = CreateSolidBrush(whitePieceColor);
\r
2128 blackPieceBrush = CreateSolidBrush(blackPieceColor);
\r
2129 iconBkgndBrush = CreateSolidBrush(GetSysColor(COLOR_BACKGROUND));
\r
2130 explodeBrush = CreateSolidBrush(highlightSquareColor); // [HGM] atomic
\r
2131 markerBrush = CreateSolidBrush(premoveHighlightColor); // [HGM] markers
\r
2132 /* [AS] Force rendering of the font-based pieces */
\r
2133 if( fontBitmapSquareSize > 0 ) {
\r
2134 fontBitmapSquareSize = 0;
\r
2140 BoardWidth(int boardSize, int n)
\r
2141 { /* [HGM] argument n added to allow different width and height */
\r
2142 int lineGap = sizeInfo[boardSize].lineGap;
\r
2144 if( appData.overrideLineGap >= 0 && appData.overrideLineGap <= 5 ) {
\r
2145 lineGap = appData.overrideLineGap;
\r
2148 return (n + 1) * lineGap +
\r
2149 n * sizeInfo[boardSize].squareSize;
\r
2152 /* Respond to board resize by dragging edge */
\r
2154 ResizeBoard(int newSizeX, int newSizeY, int flags)
\r
2156 BoardSize newSize = NUM_SIZES - 1;
\r
2157 static int recurse = 0;
\r
2158 if (IsIconic(hwndMain)) return;
\r
2159 if (recurse > 0) return;
\r
2161 while (newSize > 0) {
\r
2162 InitDrawingSizes(newSize+1000, 0); // [HGM] kludge to update sizeInfo without visible effects
\r
2163 if(newSizeX >= sizeInfo[newSize].cliWidth &&
\r
2164 newSizeY >= sizeInfo[newSize].cliHeight) break;
\r
2167 boardSize = newSize;
\r
2168 InitDrawingSizes(boardSize, flags);
\r
2173 extern Boolean twoBoards, partnerUp; // [HGM] dual
\r
2176 InitDrawingSizes(BoardSize boardSize, int flags)
\r
2178 int i, boardWidth, boardHeight; /* [HGM] height treated separately */
\r
2179 ChessSquare piece;
\r
2180 static int oldBoardSize = -1, oldTinyLayout = 0;
\r
2182 SIZE clockSize, messageSize;
\r
2184 char buf[MSG_SIZ];
\r
2186 HMENU hmenu = GetMenu(hwndMain);
\r
2187 RECT crect, wrect, oldRect;
\r
2189 LOGBRUSH logbrush;
\r
2191 int suppressVisibleEffects = 0; // [HGM] kludge to request updating sizeInfo only
\r
2192 if((int)boardSize >= 1000 ) { boardSize -= 1000; suppressVisibleEffects = 1; }
\r
2194 /* [HGM] call with -2 uses old size (for if nr of files, ranks changes) */
\r
2195 if(boardSize == (BoardSize)(-2) ) boardSize = oldBoardSize;
\r
2197 oldRect.left = wpMain.x; //[HGM] placement: remember previous window params
\r
2198 oldRect.top = wpMain.y;
\r
2199 oldRect.right = wpMain.x + wpMain.width;
\r
2200 oldRect.bottom = wpMain.y + wpMain.height;
\r
2202 tinyLayout = sizeInfo[boardSize].tinyLayout;
\r
2203 smallLayout = sizeInfo[boardSize].smallLayout;
\r
2204 squareSize = sizeInfo[boardSize].squareSize;
\r
2205 lineGap = sizeInfo[boardSize].lineGap;
\r
2206 minorSize = 0; /* [HGM] Kludge to see if demagnified pieces need to be shifted */
\r
2208 if( appData.overrideLineGap >= 0 && appData.overrideLineGap <= 5 ) {
\r
2209 lineGap = appData.overrideLineGap;
\r
2212 if (tinyLayout != oldTinyLayout) {
\r
2213 long style = GetWindowLong(hwndMain, GWL_STYLE);
\r
2215 style &= ~WS_SYSMENU;
\r
2216 InsertMenu(hmenu, IDM_Exit, MF_BYCOMMAND, IDM_Minimize,
\r
2217 "&Minimize\tCtrl+F4");
\r
2219 style |= WS_SYSMENU;
\r
2220 RemoveMenu(hmenu, IDM_Minimize, MF_BYCOMMAND);
\r
2222 SetWindowLong(hwndMain, GWL_STYLE, style);
\r
2224 for (i=0; menuBarText[tinyLayout][i]; i++) {
\r
2225 ModifyMenu(hmenu, i, MF_STRING|MF_BYPOSITION|MF_POPUP,
\r
2226 (UINT)GetSubMenu(hmenu, i), T_(menuBarText[tinyLayout][i]));
\r
2228 DrawMenuBar(hwndMain);
\r
2231 boardWidth = BoardWidth(boardSize, BOARD_WIDTH);
\r
2232 boardHeight = BoardWidth(boardSize, BOARD_HEIGHT);
\r
2234 /* Get text area sizes */
\r
2235 hdc = GetDC(hwndMain);
\r
2236 if (appData.clockMode) {
\r
2237 sprintf(buf, _("White: %s"), TimeString(23*60*60*1000L));
\r
2239 sprintf(buf, _("White"));
\r
2241 oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);
\r
2242 GetTextExtentPoint(hdc, buf, strlen(buf), &clockSize);
\r
2243 SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);
\r
2244 str = _("We only care about the height here");
\r
2245 GetTextExtentPoint(hdc, str, strlen(str), &messageSize);
\r
2246 SelectObject(hdc, oldFont);
\r
2247 ReleaseDC(hwndMain, hdc);
\r
2249 /* Compute where everything goes */
\r
2250 if((first.programLogo || second.programLogo) && !tinyLayout) {
\r
2251 /* [HGM] logo: if either logo is on, reserve space for it */
\r
2252 logoHeight = 2*clockSize.cy;
\r
2253 leftLogoRect.left = OUTER_MARGIN;
\r
2254 leftLogoRect.right = leftLogoRect.left + 4*clockSize.cy;
\r
2255 leftLogoRect.top = OUTER_MARGIN;
\r
2256 leftLogoRect.bottom = OUTER_MARGIN + logoHeight;
\r
2258 rightLogoRect.right = OUTER_MARGIN + boardWidth;
\r
2259 rightLogoRect.left = rightLogoRect.right - 4*clockSize.cy;
\r
2260 rightLogoRect.top = OUTER_MARGIN;
\r
2261 rightLogoRect.bottom = OUTER_MARGIN + logoHeight;
\r
2264 whiteRect.left = leftLogoRect.right;
\r
2265 whiteRect.right = OUTER_MARGIN + boardWidth/2 - INNER_MARGIN/2;
\r
2266 whiteRect.top = OUTER_MARGIN;
\r
2267 whiteRect.bottom = whiteRect.top + logoHeight;
\r
2269 blackRect.right = rightLogoRect.left;
\r
2270 blackRect.left = whiteRect.right + INNER_MARGIN;
\r
2271 blackRect.top = whiteRect.top;
\r
2272 blackRect.bottom = whiteRect.bottom;
\r
2274 whiteRect.left = OUTER_MARGIN;
\r
2275 whiteRect.right = whiteRect.left + boardWidth/2 - INNER_MARGIN/2;
\r
2276 whiteRect.top = OUTER_MARGIN;
\r
2277 whiteRect.bottom = whiteRect.top + clockSize.cy;
\r
2279 blackRect.left = whiteRect.right + INNER_MARGIN;
\r
2280 blackRect.right = blackRect.left + boardWidth/2 - 1;
\r
2281 blackRect.top = whiteRect.top;
\r
2282 blackRect.bottom = whiteRect.bottom;
\r
2284 logoHeight = 0; // [HGM] logo: suppress logo after change to tiny layout!
\r
2287 messageRect.left = OUTER_MARGIN + MESSAGE_LINE_LEFTMARGIN;
\r
2288 if (appData.showButtonBar) {
\r
2289 messageRect.right = OUTER_MARGIN + boardWidth // [HGM] logo: expressed independent of clock placement
\r
2290 - N_BUTTONS*BUTTON_WIDTH - MESSAGE_LINE_LEFTMARGIN;
\r
2292 messageRect.right = OUTER_MARGIN + boardWidth;
\r
2294 messageRect.top = whiteRect.bottom + INNER_MARGIN;
\r
2295 messageRect.bottom = messageRect.top + messageSize.cy;
\r
2297 boardRect.left = OUTER_MARGIN;
\r
2298 boardRect.right = boardRect.left + boardWidth;
\r
2299 boardRect.top = messageRect.bottom + INNER_MARGIN;
\r
2300 boardRect.bottom = boardRect.top + boardHeight;
\r
2302 sizeInfo[boardSize].cliWidth = boardRect.right + OUTER_MARGIN;
\r
2303 sizeInfo[boardSize].cliHeight = boardRect.bottom + OUTER_MARGIN;
\r
2304 oldBoardSize = boardSize;
\r
2305 oldTinyLayout = tinyLayout;
\r
2306 winW = 2 * GetSystemMetrics(SM_CXFRAME) + boardRect.right + OUTER_MARGIN;
\r
2307 winH = 2 * GetSystemMetrics(SM_CYFRAME) + GetSystemMetrics(SM_CYMENU) +
\r
2308 GetSystemMetrics(SM_CYCAPTION) + boardRect.bottom + OUTER_MARGIN;
\r
2309 winW *= 1 + twoBoards;
\r
2310 if(suppressVisibleEffects) return; // [HGM] when called for filling sizeInfo only
\r
2311 wpMain.width = winW; // [HGM] placement: set through temporary which can used by initial sizing choice
\r
2312 wpMain.height = winH; // without disturbing window attachments
\r
2313 GetWindowRect(hwndMain, &wrect);
\r
2314 SetWindowPos(hwndMain, NULL, 0, 0, wpMain.width, wpMain.height,
\r
2315 SWP_NOCOPYBITS|SWP_NOZORDER|SWP_NOMOVE);
\r
2317 // [HGM] placement: let attached windows follow size change.
\r
2318 ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, moveHistoryDialog, &wpMoveHistory );
\r
2319 ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, evalGraphDialog, &wpEvalGraph );
\r
2320 ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, engineOutputDialog, &wpEngineOutput );
\r
2321 ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, gameListDialog, &wpGameList );
\r
2322 ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, hwndConsole, &wpConsole );
\r
2324 /* compensate if menu bar wrapped */
\r
2325 GetClientRect(hwndMain, &crect);
\r
2326 offby = boardRect.bottom + OUTER_MARGIN - crect.bottom;
\r
2327 wpMain.height += offby;
\r
2329 case WMSZ_TOPLEFT:
\r
2330 SetWindowPos(hwndMain, NULL,
\r
2331 wrect.right - wpMain.width, wrect.bottom - wpMain.height,
\r
2332 wpMain.width, wpMain.height, SWP_NOCOPYBITS|SWP_NOZORDER);
\r
2335 case WMSZ_TOPRIGHT:
\r
2337 SetWindowPos(hwndMain, NULL,
\r
2338 wrect.left, wrect.bottom - wpMain.height,
\r
2339 wpMain.width, wpMain.height, SWP_NOCOPYBITS|SWP_NOZORDER);
\r
2342 case WMSZ_BOTTOMLEFT:
\r
2344 SetWindowPos(hwndMain, NULL,
\r
2345 wrect.right - wpMain.width, wrect.top,
\r
2346 wpMain.width, wpMain.height, SWP_NOCOPYBITS|SWP_NOZORDER);
\r
2349 case WMSZ_BOTTOMRIGHT:
\r
2353 SetWindowPos(hwndMain, NULL, 0, 0, wpMain.width, wpMain.height,
\r
2354 SWP_NOCOPYBITS|SWP_NOZORDER|SWP_NOMOVE);
\r
2359 for (i = 0; i < N_BUTTONS; i++) {
\r
2360 if (buttonDesc[i].hwnd != NULL) {
\r
2361 DestroyWindow(buttonDesc[i].hwnd);
\r
2362 buttonDesc[i].hwnd = NULL;
\r
2364 if (appData.showButtonBar) {
\r
2365 buttonDesc[i].hwnd =
\r
2366 CreateWindow("BUTTON", buttonDesc[i].label,
\r
2367 WS_VISIBLE | WS_CHILD | BS_PUSHBUTTON,
\r
2368 boardRect.right - BUTTON_WIDTH*(N_BUTTONS-i),
\r
2369 messageRect.top, BUTTON_WIDTH, messageSize.cy, hwndMain,
\r
2370 (HMENU) buttonDesc[i].id,
\r
2371 (HINSTANCE) GetWindowLong(hwndMain, GWL_HINSTANCE), NULL);
\r
2373 SendMessage(buttonDesc[i].hwnd, WM_SETFONT,
\r
2374 (WPARAM)font[boardSize][MESSAGE_FONT]->hf,
\r
2375 MAKELPARAM(FALSE, 0));
\r
2377 if (buttonDesc[i].id == IDM_Pause)
\r
2378 hwndPause = buttonDesc[i].hwnd;
\r
2379 buttonDesc[i].wndproc = (WNDPROC)
\r
2380 SetWindowLong(buttonDesc[i].hwnd, GWL_WNDPROC, (LONG) ButtonProc);
\r
2383 if (gridPen != NULL) DeleteObject(gridPen);
\r
2384 if (highlightPen != NULL) DeleteObject(highlightPen);
\r
2385 if (premovePen != NULL) DeleteObject(premovePen);
\r
2386 if (lineGap != 0) {
\r
2387 logbrush.lbStyle = BS_SOLID;
\r
2388 logbrush.lbColor = RGB(0, 0, 0); /* grid pen color = black */
\r
2390 ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,
\r
2391 lineGap, &logbrush, 0, NULL);
\r
2392 logbrush.lbColor = highlightSquareColor;
\r
2394 ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,
\r
2395 lineGap, &logbrush, 0, NULL);
\r
2397 logbrush.lbColor = premoveHighlightColor;
\r
2399 ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,
\r
2400 lineGap, &logbrush, 0, NULL);
\r
2402 /* [HGM] Loop had to be split in part for vert. and hor. lines */
\r
2403 for (i = 0; i < BOARD_HEIGHT + 1; i++) {
\r
2404 gridEndpoints[i*2].x = boardRect.left + lineGap / 2;
\r
2405 gridEndpoints[i*2].y = gridEndpoints[i*2 + 1].y =
\r
2406 boardRect.top + lineGap / 2 + (i * (squareSize + lineGap));
\r
2407 gridEndpoints[i*2 + 1].x = boardRect.left + lineGap / 2 +
\r
2408 BOARD_WIDTH * (squareSize + lineGap);
\r
2409 gridVertexCounts[i*2] = gridVertexCounts[i*2 + 1] = 2;
\r
2411 for (i = 0; i < BOARD_WIDTH + 1; i++) {
\r
2412 gridEndpoints[i*2 + BOARD_HEIGHT*2 + 2].y = boardRect.top + lineGap / 2;
\r
2413 gridEndpoints[i*2 + BOARD_HEIGHT*2 + 2].x =
\r
2414 gridEndpoints[i*2 + 1 + BOARD_HEIGHT*2 + 2].x = boardRect.left +
\r
2415 lineGap / 2 + (i * (squareSize + lineGap));
\r
2416 gridEndpoints[i*2 + 1 + BOARD_HEIGHT*2 + 2].y =
\r
2417 boardRect.top + BOARD_HEIGHT * (squareSize + lineGap);
\r
2418 gridVertexCounts[i*2] = gridVertexCounts[i*2 + 1] = 2;
\r
2422 /* [HGM] Licensing requirement */
\r
2424 if(gameInfo.variant == VariantGothic) GothicPopUp( GOTHIC, VariantGothic); else
\r
2427 if(gameInfo.variant == VariantFalcon) GothicPopUp( FALCON, VariantFalcon); else
\r
2429 GothicPopUp( "", VariantNormal);
\r
2432 /* if (boardSize == oldBoardSize) return; [HGM] variant might have changed */
\r
2434 /* Load piece bitmaps for this board size */
\r
2435 for (i=0; i<=2; i++) {
\r
2436 for (piece = WhitePawn;
\r
2437 (int) piece < (int) BlackPawn;
\r
2438 piece = (ChessSquare) ((int) piece + 1)) {
\r
2439 if (pieceBitmap[i][piece] != NULL)
\r
2440 DeleteObject(pieceBitmap[i][piece]);
\r
2444 fontBitmapSquareSize = 0; /* [HGM] render: make sure pieces will be recreated, as we might need others now */
\r
2445 // Orthodox Chess pieces
\r
2446 pieceBitmap[0][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "s");
\r
2447 pieceBitmap[0][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "s");
\r
2448 pieceBitmap[0][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "s");
\r
2449 pieceBitmap[0][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "s");
\r
2450 pieceBitmap[0][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "s");
\r
2451 pieceBitmap[1][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "o");
\r
2452 pieceBitmap[1][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "o");
\r
2453 pieceBitmap[1][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "o");
\r
2454 pieceBitmap[1][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "o");
\r
2455 pieceBitmap[1][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "o");
\r
2456 pieceBitmap[2][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "w");
\r
2457 pieceBitmap[2][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "w");
\r
2458 pieceBitmap[2][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "w");
\r
2459 pieceBitmap[2][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "w");
\r
2460 pieceBitmap[2][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "w");
\r
2461 if( !strcmp(appData.variant, "shogi") && (squareSize==72 || squareSize==49)) {
\r
2462 // in Shogi, Hijack the unused Queen for Lance
\r
2463 pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "s");
\r
2464 pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "o");
\r
2465 pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "w");
\r
2467 pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "s");
\r
2468 pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "o");
\r
2469 pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "w");
\r
2472 if(squareSize <= 72 && squareSize >= 33) {
\r
2473 /* A & C are available in most sizes now */
\r
2474 if(squareSize != 49 && squareSize != 72 && squareSize != 33) { // Vortex-like
\r
2475 pieceBitmap[0][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "s");
\r
2476 pieceBitmap[1][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "o");
\r
2477 pieceBitmap[2][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "w");
\r
2478 pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "s");
\r
2479 pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "o");
\r
2480 pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "w");
\r
2481 pieceBitmap[0][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "s");
\r
2482 pieceBitmap[1][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "o");
\r
2483 pieceBitmap[2][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "w");
\r
2484 pieceBitmap[0][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "s");
\r
2485 pieceBitmap[1][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "o");
\r
2486 pieceBitmap[2][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "w");
\r
2487 } else { // Smirf-like
\r
2488 pieceBitmap[0][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "s");
\r
2489 pieceBitmap[1][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "o");
\r
2490 pieceBitmap[2][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "w");
\r
2492 if(gameInfo.variant == VariantGothic) { // Vortex-like
\r
2493 pieceBitmap[0][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "s");
\r
2494 pieceBitmap[1][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "o");
\r
2495 pieceBitmap[2][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "w");
\r
2496 } else { // WinBoard standard
\r
2497 pieceBitmap[0][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "s");
\r
2498 pieceBitmap[1][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "o");
\r
2499 pieceBitmap[2][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "w");
\r
2504 if(squareSize==72 || squareSize==49 || squareSize==33) { /* experiment with some home-made bitmaps */
\r
2505 pieceBitmap[0][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "s");
\r
2506 pieceBitmap[1][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "o");
\r
2507 pieceBitmap[2][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "w");
\r
2508 pieceBitmap[0][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "s");
\r
2509 pieceBitmap[1][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "o");
\r
2510 pieceBitmap[2][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "w");
\r
2511 pieceBitmap[0][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "s");
\r
2512 pieceBitmap[1][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "o");
\r
2513 pieceBitmap[2][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "w");
\r
2514 pieceBitmap[0][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "s");
\r
2515 pieceBitmap[1][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "o");
\r
2516 pieceBitmap[2][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "w");
\r
2517 pieceBitmap[0][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "s");
\r
2518 pieceBitmap[1][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "o");
\r
2519 pieceBitmap[2][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "w");
\r
2520 pieceBitmap[0][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "s");
\r
2521 pieceBitmap[1][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "o");
\r
2522 pieceBitmap[2][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "w");
\r
2523 pieceBitmap[0][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "s");
\r
2524 pieceBitmap[1][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "o");
\r
2525 pieceBitmap[2][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "w");
\r
2526 pieceBitmap[0][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "s");
\r
2527 pieceBitmap[1][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "o");
\r
2528 pieceBitmap[2][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "w");
\r
2529 pieceBitmap[0][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "s");
\r
2530 pieceBitmap[1][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "o");
\r
2531 pieceBitmap[2][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "w");
\r
2532 pieceBitmap[0][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "s");
\r
2533 pieceBitmap[1][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "o");
\r
2534 pieceBitmap[2][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "w");
\r
2536 if(gameInfo.variant == VariantShogi) { /* promoted Gold represemtations */
\r
2537 pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "wp", squareSize, "s");
\r
2538 pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "wp", squareSize, "o");
\r
2539 pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "w", squareSize, "w");
\r
2540 pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "wn", squareSize, "s");
\r
2541 pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "wn", squareSize, "o");
\r
2542 pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "w", squareSize, "w");
\r
2543 pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "ws", squareSize, "s");
\r
2544 pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "ws", squareSize, "o");
\r
2545 pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "w", squareSize, "w");
\r
2546 pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "wl", squareSize, "s");
\r
2547 pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "wl", squareSize, "o");
\r
2548 pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "w", squareSize, "w");
\r
2550 pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "s");
\r
2551 pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "o");
\r
2552 pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "w");
\r
2553 pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "s");
\r
2554 pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "o");
\r
2555 pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "w");
\r
2556 pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "s");
\r
2557 pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "o");
\r
2558 pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "w");
\r
2559 pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "s");
\r
2560 pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "o");
\r
2561 pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "w");
\r
2564 } else { /* other size, no special bitmaps available. Use smaller symbols */
\r
2565 if((int)boardSize < 2) minorSize = sizeInfo[0].squareSize;
\r
2566 else minorSize = sizeInfo[(int)boardSize - 2].squareSize;
\r
2567 pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "s");
\r
2568 pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "o");
\r
2569 pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "w");
\r
2570 pieceBitmap[0][WhiteCardinal] = DoLoadBitmap(hInst, "b", minorSize, "s");
\r
2571 pieceBitmap[1][WhiteCardinal] = DoLoadBitmap(hInst, "b", minorSize, "o");
\r
2572 pieceBitmap[2][WhiteCardinal] = DoLoadBitmap(hInst, "b", minorSize, "w");
\r
2573 pieceBitmap[0][WhiteDragon] = DoLoadBitmap(hInst, "r", minorSize, "s");
\r
2574 pieceBitmap[1][WhiteDragon] = DoLoadBitmap(hInst, "r", minorSize, "o");
\r
2575 pieceBitmap[2][WhiteDragon] = DoLoadBitmap(hInst, "r", minorSize, "w");
\r
2576 pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "s");
\r
2577 pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "o");
\r
2578 pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "w");
\r
2582 if(gameInfo.variant == VariantShogi && squareSize == 58)
\r
2583 /* special Shogi support in this size */
\r
2584 { for (i=0; i<=2; i++) { /* replace all bitmaps */
\r
2585 for (piece = WhitePawn;
\r
2586 (int) piece < (int) BlackPawn;
\r
2587 piece = (ChessSquare) ((int) piece + 1)) {
\r
2588 if (pieceBitmap[i][piece] != NULL)
\r
2589 DeleteObject(pieceBitmap[i][piece]);
\r
2592 pieceBitmap[0][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "o");
\r
2593 pieceBitmap[0][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "o");
\r
2594 pieceBitmap[0][WhiteBishop] = DoLoadBitmap(hInst, "sb", squareSize, "o");
\r
2595 pieceBitmap[0][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "o");
\r
2596 pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "o");
\r
2597 pieceBitmap[0][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "o");
\r
2598 pieceBitmap[0][WhiteFerz] = DoLoadBitmap(hInst, "sf", squareSize, "o");
\r
2599 pieceBitmap[0][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "o");
\r
2600 pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "su", squareSize, "o");
\r
2601 pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "sh", squareSize, "o");
\r
2602 pieceBitmap[0][WhiteCardinal] = DoLoadBitmap(hInst, "sa", squareSize, "o");
\r
2603 pieceBitmap[0][WhiteDragon] = DoLoadBitmap(hInst, "sc", squareSize, "o");
\r
2604 pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "sg", squareSize, "o");
\r
2605 pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "ss", squareSize, "o");
\r
2606 pieceBitmap[1][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "o");
\r
2607 pieceBitmap[1][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "o");
\r
2608 pieceBitmap[1][WhiteBishop] = DoLoadBitmap(hInst, "sb", squareSize, "o");
\r
2609 pieceBitmap[1][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "o");
\r
2610 pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "o");
\r
2611 pieceBitmap[1][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "o");
\r
2612 pieceBitmap[1][WhiteFerz] = DoLoadBitmap(hInst, "sf", squareSize, "o");
\r
2613 pieceBitmap[1][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "o");
\r
2614 pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "su", squareSize, "o");
\r
2615 pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "sh", squareSize, "o");
\r
2616 pieceBitmap[1][WhiteCardinal] = DoLoadBitmap(hInst, "sa", squareSize, "o");
\r
2617 pieceBitmap[1][WhiteDragon] = DoLoadBitmap(hInst, "sc", squareSize, "o");
\r
2618 pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "sg", squareSize, "o");
\r
2619 pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "ss", squareSize, "o");
\r
2620 pieceBitmap[2][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "w");
\r
2621 pieceBitmap[2][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "w");
\r
2622 pieceBitmap[2][WhiteBishop] = DoLoadBitmap(hInst, "sr", squareSize, "w");
\r
2623 pieceBitmap[2][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "w");
\r
2624 pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "w");
\r
2625 pieceBitmap[2][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "w");
\r
2626 pieceBitmap[2][WhiteFerz] = DoLoadBitmap(hInst, "sw", squareSize, "w");
\r
2627 pieceBitmap[2][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "w");
\r
2628 pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "sp", squareSize, "w");
\r
2629 pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "sn", squareSize, "w");
\r
2630 pieceBitmap[2][WhiteCardinal] = DoLoadBitmap(hInst, "sr", squareSize, "w");
\r
2631 pieceBitmap[2][WhiteDragon] = DoLoadBitmap(hInst, "sr", squareSize, "w");
\r
2632 pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "sl", squareSize, "w");
\r
2633 pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "sw", squareSize, "w");
\r
2639 PieceBitmap(ChessSquare p, int kind)
\r
2641 if ((int) p >= (int) BlackPawn)
\r
2642 p = (ChessSquare) ((int) p - (int) BlackPawn + (int) WhitePawn);
\r
2644 return pieceBitmap[kind][(int) p];
\r
2647 /***************************************************************/
\r
2649 #define MIN(a,b) (((a) < (b)) ? (a) : (b))
\r
2650 #define MAX(a,b) (((a) > (b)) ? (a) : (b))
\r
2652 #define MIN3(a,b,c) (((a) < (b) && (a) < (c)) ? (a) : (((b) < (a) && (b) < (c)) ? (b) : (c)))
\r
2653 #define MAX3(a,b,c) (((a) > (b) && (a) > (c)) ? (a) : (((b) > (a) && (b) > (c)) ? (b) : (c)))
\r
2657 SquareToPos(int row, int column, int * x, int * y)
\r
2660 *x = boardRect.left + lineGap + ((BOARD_WIDTH-1)-column) * (squareSize + lineGap);
\r
2661 *y = boardRect.top + lineGap + row * (squareSize + lineGap);
\r
2663 *x = boardRect.left + lineGap + column * (squareSize + lineGap);
\r
2664 *y = boardRect.top + lineGap + ((BOARD_HEIGHT-1)-row) * (squareSize + lineGap);
\r
2669 DrawCoordsOnDC(HDC hdc)
\r
2671 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
2672 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
2673 char str[2] = { NULLCHAR, NULLCHAR };
\r
2674 int oldMode, oldAlign, x, y, start, i;
\r
2678 if (!appData.showCoords)
\r
2681 start = flipView ? 1-(ONE!='1') : 23+(ONE!='1')-BOARD_HEIGHT;
\r
2683 oldBrush = SelectObject(hdc, GetStockObject(BLACK_BRUSH));
\r
2684 oldMode = SetBkMode(hdc, (appData.monoMode ? OPAQUE : TRANSPARENT));
\r
2685 oldAlign = GetTextAlign(hdc);
\r
2686 oldFont = SelectObject(hdc, font[boardSize][COORD_FONT]->hf);
\r
2688 y = boardRect.top + lineGap;
\r
2689 x = boardRect.left + lineGap + gameInfo.holdingsWidth*(squareSize + lineGap);
\r
2691 SetTextAlign(hdc, TA_LEFT|TA_TOP);
\r
2692 for (i = 0; i < BOARD_HEIGHT; i++) {
\r
2693 str[0] = files[start + i];
\r
2694 ExtTextOut(hdc, x + 2, y + 1, 0, NULL, str, 1, NULL);
\r
2695 y += squareSize + lineGap;
\r
2698 start = flipView ? 12-(BOARD_RGHT-BOARD_LEFT) : 12;
\r
2700 SetTextAlign(hdc, TA_RIGHT|TA_BOTTOM);
\r
2701 for (i = 0; i < BOARD_RGHT - BOARD_LEFT; i++) {
\r
2702 str[0] = ranks[start + i];
\r
2703 ExtTextOut(hdc, x + squareSize - 2, y - 1, 0, NULL, str, 1, NULL);
\r
2704 x += squareSize + lineGap;
\r
2707 SelectObject(hdc, oldBrush);
\r
2708 SetBkMode(hdc, oldMode);
\r
2709 SetTextAlign(hdc, oldAlign);
\r
2710 SelectObject(hdc, oldFont);
\r
2714 DrawGridOnDC(HDC hdc)
\r
2718 if (lineGap != 0) {
\r
2719 oldPen = SelectObject(hdc, gridPen);
\r
2720 PolyPolyline(hdc, gridEndpoints, gridVertexCounts, BOARD_WIDTH+BOARD_HEIGHT + 2);
\r
2721 SelectObject(hdc, oldPen);
\r
2725 #define HIGHLIGHT_PEN 0
\r
2726 #define PREMOVE_PEN 1
\r
2729 DrawHighlightOnDC(HDC hdc, BOOLEAN on, int x, int y, int pen)
\r
2732 HPEN oldPen, hPen;
\r
2733 if (lineGap == 0) return;
\r
2735 x1 = boardRect.left +
\r
2736 lineGap/2 + ((BOARD_WIDTH-1)-x) * (squareSize + lineGap);
\r
2737 y1 = boardRect.top +
\r
2738 lineGap/2 + y * (squareSize + lineGap);
\r
2740 x1 = boardRect.left +
\r
2741 lineGap/2 + x * (squareSize + lineGap);
\r
2742 y1 = boardRect.top +
\r
2743 lineGap/2 + ((BOARD_HEIGHT-1)-y) * (squareSize + lineGap);
\r
2745 hPen = pen ? premovePen : highlightPen;
\r
2746 oldPen = SelectObject(hdc, on ? hPen : gridPen);
\r
2747 MoveToEx(hdc, x1, y1, NULL);
\r
2748 LineTo(hdc, x1 + squareSize + lineGap, y1);
\r
2749 LineTo(hdc, x1 + squareSize + lineGap, y1 + squareSize + lineGap);
\r
2750 LineTo(hdc, x1, y1 + squareSize + lineGap);
\r
2751 LineTo(hdc, x1, y1);
\r
2752 SelectObject(hdc, oldPen);
\r
2756 DrawHighlightsOnDC(HDC hdc, HighlightInfo *h, int pen)
\r
2759 for (i=0; i<2; i++) {
\r
2760 if (h->sq[i].x >= 0 && h->sq[i].y >= 0)
\r
2761 DrawHighlightOnDC(hdc, TRUE,
\r
2762 h->sq[i].x, h->sq[i].y,
\r
2767 /* Note: sqcolor is used only in monoMode */
\r
2768 /* Note that this code is largely duplicated in woptions.c,
\r
2769 function DrawSampleSquare, so that needs to be updated too */
\r
2771 DrawPieceOnDC(HDC hdc, ChessSquare piece, int color, int sqcolor, int x, int y, HDC tmphdc)
\r
2773 HBITMAP oldBitmap;
\r
2777 if (appData.blindfold) return;
\r
2779 /* [AS] Use font-based pieces if needed */
\r
2780 if( fontBitmapSquareSize >= 0 && (squareSize > 32 || gameInfo.variant >= VariantShogi)) {
\r
2781 /* Create piece bitmaps, or do nothing if piece set is up to date */
\r
2782 CreatePiecesFromFont();
\r
2784 if( fontBitmapSquareSize == squareSize ) {
\r
2785 int index = TranslatePieceToFontPiece(piece);
\r
2787 SelectObject( tmphdc, hPieceMask[ index ] );
\r
2789 if(appData.upsideDown ? color==flipView : (flipView && gameInfo.variant == VariantShogi))
\r
2790 StretchBlt(hdc, x+squareSize, y+squareSize, -squareSize, -squareSize, tmphdc, 0, 0, squareSize, squareSize, SRCAND);
\r
2794 squareSize, squareSize,
\r
2799 SelectObject( tmphdc, hPieceFace[ index ] );
\r
2801 if(appData.upsideDown ? color==flipView : (flipView && gameInfo.variant == VariantShogi))
\r
2802 StretchBlt(hdc, x+squareSize, y+squareSize, -squareSize, -squareSize, tmphdc, 0, 0, squareSize, squareSize, SRCPAINT);
\r
2806 squareSize, squareSize,
\r
2815 if (appData.monoMode) {
\r
2816 SelectObject(tmphdc, PieceBitmap(piece,
\r
2817 color == sqcolor ? OUTLINE_PIECE : SOLID_PIECE));
\r
2818 BitBlt(hdc, x, y, squareSize, squareSize, tmphdc, 0, 0,
\r
2819 sqcolor ? SRCCOPY : NOTSRCCOPY);
\r
2821 tmpSize = squareSize;
\r
2823 ((piece >= (int)WhiteNightrider && piece <= WhiteGrasshopper) ||
\r
2824 (piece >= (int)BlackNightrider && piece <= BlackGrasshopper)) ) {
\r
2825 /* [HGM] no bitmap available for promoted pieces in Crazyhouse */
\r
2826 /* Bitmaps of smaller size are substituted, but we have to align them */
\r
2827 x += (squareSize - minorSize)>>1;
\r
2828 y += squareSize - minorSize - 2;
\r
2829 tmpSize = minorSize;
\r
2831 if (color || appData.allWhite ) {
\r
2832 oldBitmap = SelectObject(tmphdc, PieceBitmap(piece, WHITE_PIECE));
\r
2834 oldBrush = SelectObject(hdc, whitePieceBrush);
\r
2835 else oldBrush = SelectObject(hdc, blackPieceBrush);
\r
2836 if(appData.upsideDown && color==flipView)
\r
2837 StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, 0x00B8074A);
\r
2839 BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, 0x00B8074A);
\r
2840 /* Use black for outline of white pieces */
\r
2841 SelectObject(tmphdc, PieceBitmap(piece, OUTLINE_PIECE));
\r
2842 if(appData.upsideDown && color==flipView)
\r
2843 StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, SRCAND);
\r
2845 BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, SRCAND);
\r
2847 /* Use square color for details of black pieces */
\r
2848 oldBitmap = SelectObject(tmphdc, PieceBitmap(piece, SOLID_PIECE));
\r
2849 oldBrush = SelectObject(hdc, blackPieceBrush);
\r
2850 if(appData.upsideDown && !flipView)
\r
2851 StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, 0x00B8074A);
\r
2853 BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, 0x00B8074A);
\r
2855 SelectObject(hdc, oldBrush);
\r
2856 SelectObject(tmphdc, oldBitmap);
\r
2860 /* [AS] Compute a drawing mode for a square, based on specified settings (see DrawTile) */
\r
2861 int GetBackTextureMode( int algo )
\r
2863 int result = BACK_TEXTURE_MODE_DISABLED;
\r
2867 case BACK_TEXTURE_MODE_PLAIN:
\r
2868 result = 1; /* Always use identity map */
\r
2870 case BACK_TEXTURE_MODE_FULL_RANDOM:
\r
2871 result = 1 + (myrandom() % 3); /* Pick a transformation at random */
\r
2879 [AS] Compute and save texture drawing info, otherwise we may not be able
\r
2880 to handle redraws cleanly (as random numbers would always be different).
\r
2882 VOID RebuildTextureSquareInfo()
\r
2892 ZeroMemory( &backTextureSquareInfo, sizeof(backTextureSquareInfo) );
\r
2894 if( liteBackTexture != NULL ) {
\r
2895 if( GetObject( liteBackTexture, sizeof(bi), &bi ) > 0 ) {
\r
2896 lite_w = bi.bmWidth;
\r
2897 lite_h = bi.bmHeight;
\r
2901 if( darkBackTexture != NULL ) {
\r
2902 if( GetObject( darkBackTexture, sizeof(bi), &bi ) > 0 ) {
\r
2903 dark_w = bi.bmWidth;
\r
2904 dark_h = bi.bmHeight;
\r
2908 for( row=0; row<BOARD_HEIGHT; row++ ) {
\r
2909 for( col=0; col<BOARD_WIDTH; col++ ) {
\r
2910 if( (col + row) & 1 ) {
\r
2912 if( lite_w >= squareSize && lite_h >= squareSize ) {
\r
2913 if( lite_w >= squareSize*BOARD_WIDTH )
\r
2914 backTextureSquareInfo[row][col].x = (2*col+1)*lite_w/(2*BOARD_WIDTH) - squareSize/2; /* [HGM] cut out of center of virtual square */
\r
2916 backTextureSquareInfo[row][col].x = col * (lite_w - squareSize) / (BOARD_WIDTH-1); /* [HGM] divide by size-1 in stead of size! */
\r
2917 if( lite_h >= squareSize*BOARD_HEIGHT )
\r
2918 backTextureSquareInfo[row][col].y = (2*(BOARD_HEIGHT-row)-1)*lite_h/(2*BOARD_HEIGHT) - squareSize/2;
\r
2920 backTextureSquareInfo[row][col].y = (BOARD_HEIGHT-1-row) * (lite_h - squareSize) / (BOARD_HEIGHT-1);
\r
2921 backTextureSquareInfo[row][col].mode = GetBackTextureMode(liteBackTextureMode);
\r
2926 if( dark_w >= squareSize && dark_h >= squareSize ) {
\r
2927 if( dark_w >= squareSize*BOARD_WIDTH )
\r
2928 backTextureSquareInfo[row][col].x = (2*col+1) * dark_w / (2*BOARD_WIDTH) - squareSize/2;
\r
2930 backTextureSquareInfo[row][col].x = col * (dark_w - squareSize) / (BOARD_WIDTH-1);
\r
2931 if( dark_h >= squareSize*BOARD_HEIGHT )
\r
2932 backTextureSquareInfo[row][col].y = (2*(BOARD_HEIGHT-row)-1) * dark_h / (2*BOARD_HEIGHT) - squareSize/2;
\r
2934 backTextureSquareInfo[row][col].y = (BOARD_HEIGHT-1-row) * (dark_h - squareSize) / (BOARD_HEIGHT-1);
\r
2935 backTextureSquareInfo[row][col].mode = GetBackTextureMode(darkBackTextureMode);
\r
2942 /* [AS] Arrow highlighting support */
\r
2944 static int A_WIDTH = 5; /* Width of arrow body */
\r
2946 #define A_HEIGHT_FACTOR 6 /* Length of arrow "point", relative to body width */
\r
2947 #define A_WIDTH_FACTOR 3 /* Width of arrow "point", relative to body width */
\r
2949 static double Sqr( double x )
\r
2954 static int Round( double x )
\r
2956 return (int) (x + 0.5);
\r
2959 /* Draw an arrow between two points using current settings */
\r
2960 VOID DrawArrowBetweenPoints( HDC hdc, int s_x, int s_y, int d_x, int d_y )
\r
2963 double dx, dy, j, k, x, y;
\r
2965 if( d_x == s_x ) {
\r
2966 int h = (d_y > s_y) ? +A_WIDTH*A_HEIGHT_FACTOR : -A_WIDTH*A_HEIGHT_FACTOR;
\r
2968 arrow[0].x = s_x + A_WIDTH;
\r
2971 arrow[1].x = s_x + A_WIDTH;
\r
2972 arrow[1].y = d_y - h;
\r
2974 arrow[2].x = s_x + A_WIDTH*A_WIDTH_FACTOR;
\r
2975 arrow[2].y = d_y - h;
\r
2980 arrow[4].x = s_x - A_WIDTH*A_WIDTH_FACTOR;
\r
2981 arrow[4].y = d_y - h;
\r
2983 arrow[5].x = s_x - A_WIDTH;
\r
2984 arrow[5].y = d_y - h;
\r
2986 arrow[6].x = s_x - A_WIDTH;
\r
2989 else if( d_y == s_y ) {
\r
2990 int w = (d_x > s_x) ? +A_WIDTH*A_HEIGHT_FACTOR : -A_WIDTH*A_HEIGHT_FACTOR;
\r
2993 arrow[0].y = s_y + A_WIDTH;
\r
2995 arrow[1].x = d_x - w;
\r
2996 arrow[1].y = s_y + A_WIDTH;
\r
2998 arrow[2].x = d_x - w;
\r
2999 arrow[2].y = s_y + A_WIDTH*A_WIDTH_FACTOR;
\r
3004 arrow[4].x = d_x - w;
\r
3005 arrow[4].y = s_y - A_WIDTH*A_WIDTH_FACTOR;
\r
3007 arrow[5].x = d_x - w;
\r
3008 arrow[5].y = s_y - A_WIDTH;
\r
3011 arrow[6].y = s_y - A_WIDTH;
\r
3014 /* [AS] Needed a lot of paper for this! :-) */
\r
3015 dy = (double) (d_y - s_y) / (double) (d_x - s_x);
\r
3016 dx = (double) (s_x - d_x) / (double) (s_y - d_y);
\r
3018 j = sqrt( Sqr(A_WIDTH) / (1.0 + Sqr(dx)) );
\r
3020 k = sqrt( Sqr(A_WIDTH*A_HEIGHT_FACTOR) / (1.0 + Sqr(dy)) );
\r
3025 arrow[0].x = Round(x - j);
\r
3026 arrow[0].y = Round(y + j*dx);
\r
3028 arrow[1].x = Round(x + j);
\r
3029 arrow[1].y = Round(y - j*dx);
\r
3032 x = (double) d_x - k;
\r
3033 y = (double) d_y - k*dy;
\r
3036 x = (double) d_x + k;
\r
3037 y = (double) d_y + k*dy;
\r
3040 arrow[2].x = Round(x + j);
\r
3041 arrow[2].y = Round(y - j*dx);
\r
3043 arrow[3].x = Round(x + j*A_WIDTH_FACTOR);
\r
3044 arrow[3].y = Round(y - j*A_WIDTH_FACTOR*dx);
\r
3049 arrow[5].x = Round(x - j*A_WIDTH_FACTOR);
\r
3050 arrow[5].y = Round(y + j*A_WIDTH_FACTOR*dx);
\r
3052 arrow[6].x = Round(x - j);
\r
3053 arrow[6].y = Round(y + j*dx);
\r
3056 Polygon( hdc, arrow, 7 );
\r
3059 /* [AS] Draw an arrow between two squares */
\r
3060 VOID DrawArrowBetweenSquares( HDC hdc, int s_col, int s_row, int d_col, int d_row )
\r
3062 int s_x, s_y, d_x, d_y;
\r
3069 if( s_col == d_col && s_row == d_row ) {
\r
3073 /* Get source and destination points */
\r
3074 SquareToPos( s_row, s_col, &s_x, &s_y);
\r
3075 SquareToPos( d_row, d_col, &d_x, &d_y);
\r
3078 d_y += squareSize / 4;
\r
3080 else if( d_y < s_y ) {
\r
3081 d_y += 3 * squareSize / 4;
\r
3084 d_y += squareSize / 2;
\r
3088 d_x += squareSize / 4;
\r
3090 else if( d_x < s_x ) {
\r
3091 d_x += 3 * squareSize / 4;
\r
3094 d_x += squareSize / 2;
\r
3097 s_x += squareSize / 2;
\r
3098 s_y += squareSize / 2;
\r
3100 /* Adjust width */
\r
3101 A_WIDTH = squareSize / 14;
\r
3104 stLB.lbStyle = BS_SOLID;
\r
3105 stLB.lbColor = appData.highlightArrowColor;
\r
3108 hpen = CreatePen( PS_SOLID, 2, RGB(0x00,0x00,0x00) );
\r
3109 holdpen = SelectObject( hdc, hpen );
\r
3110 hbrush = CreateBrushIndirect( &stLB );
\r
3111 holdbrush = SelectObject( hdc, hbrush );
\r
3113 DrawArrowBetweenPoints( hdc, s_x, s_y, d_x, d_y );
\r
3115 SelectObject( hdc, holdpen );
\r
3116 SelectObject( hdc, holdbrush );
\r
3117 DeleteObject( hpen );
\r
3118 DeleteObject( hbrush );
\r
3121 BOOL HasHighlightInfo()
\r
3123 BOOL result = FALSE;
\r
3125 if( highlightInfo.sq[0].x >= 0 && highlightInfo.sq[0].y >= 0 &&
\r
3126 highlightInfo.sq[1].x >= 0 && highlightInfo.sq[1].y >= 0 )
\r
3134 BOOL IsDrawArrowEnabled()
\r
3136 BOOL result = FALSE;
\r
3138 if( appData.highlightMoveWithArrow && squareSize >= 32 ) {
\r
3145 VOID DrawArrowHighlight( HDC hdc )
\r
3147 if( IsDrawArrowEnabled() && HasHighlightInfo() ) {
\r
3148 DrawArrowBetweenSquares( hdc,
\r
3149 highlightInfo.sq[0].x, highlightInfo.sq[0].y,
\r
3150 highlightInfo.sq[1].x, highlightInfo.sq[1].y );
\r
3154 HRGN GetArrowHighlightClipRegion( HDC hdc )
\r
3156 HRGN result = NULL;
\r
3158 if( HasHighlightInfo() ) {
\r
3159 int x1, y1, x2, y2;
\r
3160 int sx, sy, dx, dy;
\r
3162 SquareToPos(highlightInfo.sq[0].y, highlightInfo.sq[0].x, &x1, &y1 );
\r
3163 SquareToPos(highlightInfo.sq[1].y, highlightInfo.sq[1].x, &x2, &y2 );
\r
3165 sx = MIN( x1, x2 );
\r
3166 sy = MIN( y1, y2 );
\r
3167 dx = MAX( x1, x2 ) + squareSize;
\r
3168 dy = MAX( y1, y2 ) + squareSize;
\r
3170 result = CreateRectRgn( sx, sy, dx, dy );
\r
3177 Warning: this function modifies the behavior of several other functions.
\r
3179 Basically, Winboard is optimized to avoid drawing the whole board if not strictly
\r
3180 needed. Unfortunately, the decision whether or not to perform a full or partial
\r
3181 repaint is scattered all over the place, which is not good for features such as
\r
3182 "arrow highlighting" that require a full repaint of the board.
\r
3184 So, I've tried to patch the code where I thought it made sense (e.g. after or during
\r
3185 user interaction, when speed is not so important) but especially to avoid errors
\r
3186 in the displayed graphics.
\r
3188 In such patched places, I always try refer to this function so there is a single
\r
3189 place to maintain knowledge.
\r
3191 To restore the original behavior, just return FALSE unconditionally.
\r
3193 BOOL IsFullRepaintPreferrable()
\r
3195 BOOL result = FALSE;
\r
3197 if( (appData.highlightLastMove || appData.highlightDragging) && IsDrawArrowEnabled() ) {
\r
3198 /* Arrow may appear on the board */
\r
3206 This function is called by DrawPosition to know whether a full repaint must
\r
3209 Only DrawPosition may directly call this function, which makes use of
\r
3210 some state information. Other function should call DrawPosition specifying
\r
3211 the repaint flag, and can use IsFullRepaintPreferrable if needed.
\r
3213 BOOL DrawPositionNeedsFullRepaint()
\r
3215 BOOL result = FALSE;
\r
3218 Probably a slightly better policy would be to trigger a full repaint
\r
3219 when animInfo.piece changes state (i.e. empty -> non-empty and viceversa),
\r
3220 but animation is fast enough that it's difficult to notice.
\r
3222 if( animInfo.piece == EmptySquare ) {
\r
3223 if( (appData.highlightLastMove || appData.highlightDragging) && IsDrawArrowEnabled() /*&& HasHighlightInfo()*/ ) {
\r
3232 DrawBoardOnDC(HDC hdc, Board board, HDC tmphdc)
\r
3234 int row, column, x, y, square_color, piece_color;
\r
3235 ChessSquare piece;
\r
3237 HDC texture_hdc = NULL;
\r
3239 /* [AS] Initialize background textures if needed */
\r
3240 if( liteBackTexture != NULL || darkBackTexture != NULL ) {
\r
3241 static int backTextureBoardSize; /* [HGM] boardsize: also new texture if board format changed */
\r
3242 if( backTextureSquareSize != squareSize
\r
3243 || backTextureBoardSize != BOARD_WIDTH+BOARD_FILES*BOARD_HEIGHT) {
\r
3244 backTextureBoardSize = BOARD_WIDTH+BOARD_FILES*BOARD_HEIGHT;
\r
3245 backTextureSquareSize = squareSize;
\r
3246 RebuildTextureSquareInfo();
\r
3249 texture_hdc = CreateCompatibleDC( hdc );
\r
3252 for (row = 0; row < BOARD_HEIGHT; row++) {
\r
3253 for (column = 0; column < BOARD_WIDTH; column++) {
\r
3255 SquareToPos(row, column, &x, &y);
\r
3257 piece = board[row][column];
\r
3259 square_color = ((column + row) % 2) == 1;
\r
3260 if( gameInfo.variant == VariantXiangqi ) {
\r
3261 square_color = !InPalace(row, column);
\r
3262 if(BOARD_HEIGHT&1) { if(row==BOARD_HEIGHT/2) square_color ^= 1; }
\r
3263 else if(row < BOARD_HEIGHT/2) square_color ^= 1;
\r
3265 piece_color = (int) piece < (int) BlackPawn;
\r
3268 /* [HGM] holdings file: light square or black */
\r
3269 if(column == BOARD_LEFT-2) {
\r
3270 if( row > BOARD_HEIGHT - gameInfo.holdingsSize - 1 )
\r
3273 DisplayHoldingsCount(hdc, x, y, 0, 0); /* black out */
\r
3277 if(column == BOARD_RGHT + 1 ) {
\r
3278 if( row < gameInfo.holdingsSize )
\r
3281 DisplayHoldingsCount(hdc, x, y, 0, 0);
\r
3285 if(column == BOARD_LEFT-1 ) /* left align */
\r
3286 DisplayHoldingsCount(hdc, x, y, flipView, (int) board[row][column]);
\r
3287 else if( column == BOARD_RGHT) /* right align */
\r
3288 DisplayHoldingsCount(hdc, x, y, !flipView, (int) board[row][column]);
\r
3290 if (appData.monoMode) {
\r
3291 if (piece == EmptySquare) {
\r
3292 BitBlt(hdc, x, y, squareSize, squareSize, 0, 0, 0,
\r
3293 square_color ? WHITENESS : BLACKNESS);
\r
3295 DrawPieceOnDC(hdc, piece, piece_color, square_color, x, y, tmphdc);
\r
3298 else if( backTextureSquareInfo[row][column].mode > 0 ) {
\r
3299 /* [AS] Draw the square using a texture bitmap */
\r
3300 HBITMAP hbm = SelectObject( texture_hdc, square_color ? liteBackTexture : darkBackTexture );
\r
3301 int r = row, c = column; // [HGM] do not flip board in flipView
\r
3302 if(flipView) { r = BOARD_HEIGHT-1 - r; c = BOARD_WIDTH-1 - c; }
\r
3305 squareSize, squareSize,
\r
3308 backTextureSquareInfo[r][c].mode,
\r
3309 backTextureSquareInfo[r][c].x,
\r
3310 backTextureSquareInfo[r][c].y );
\r
3312 SelectObject( texture_hdc, hbm );
\r
3314 if (piece != EmptySquare) {
\r
3315 DrawPieceOnDC(hdc, piece, piece_color, -1, x, y, tmphdc);
\r
3319 HBRUSH brush = square_color ? lightSquareBrush : darkSquareBrush;
\r
3321 oldBrush = SelectObject(hdc, brush );
\r
3322 BitBlt(hdc, x, y, squareSize, squareSize, 0, 0, 0, PATCOPY);
\r
3323 SelectObject(hdc, oldBrush);
\r
3324 if (piece != EmptySquare)
\r
3325 DrawPieceOnDC(hdc, piece, piece_color, -1, x, y, tmphdc);
\r
3330 if( texture_hdc != NULL ) {
\r
3331 DeleteDC( texture_hdc );
\r
3335 int saveDiagFlag = 0; FILE *diagFile; // [HGM] diag
\r
3336 void fputDW(FILE *f, int x)
\r
3338 fputc(x & 255, f);
\r
3339 fputc(x>>8 & 255, f);
\r
3340 fputc(x>>16 & 255, f);
\r
3341 fputc(x>>24 & 255, f);
\r
3344 #define MAX_CLIPS 200 /* more than enough */
\r
3347 DrawLogoOnDC(HDC hdc, RECT logoRect, HBITMAP logo)
\r
3349 // HBITMAP bufferBitmap;
\r
3354 int w = 100, h = 50;
\r
3356 if(logo == NULL) return;
\r
3357 // GetClientRect(hwndMain, &Rect);
\r
3358 // bufferBitmap = CreateCompatibleBitmap(hdc, Rect.right-Rect.left+1,
\r
3359 // Rect.bottom-Rect.top+1);
\r
3360 tmphdc = CreateCompatibleDC(hdc);
\r
3361 hbm = SelectObject(tmphdc, logo);
\r
3362 if( GetObject( logo, sizeof(bi), &bi ) > 0 ) {
\r
3366 StretchBlt(hdc, logoRect.left, logoRect.top, logoRect.right - logoRect.left,
\r
3367 logoRect.bottom - logoRect.top, tmphdc, 0, 0, w, h, SRCCOPY);
\r
3368 SelectObject(tmphdc, hbm);
\r
3372 static HDC hdcSeek;
\r
3374 // [HGM] seekgraph
\r
3375 void DrawSeekAxis( int x, int y, int xTo, int yTo )
\r
3378 HPEN hp = SelectObject( hdcSeek, gridPen );
\r
3379 MoveToEx( hdcSeek, boardRect.left+x, boardRect.top+y, &stPt );
\r
3380 LineTo( hdcSeek, boardRect.left+xTo, boardRect.top+yTo );
\r
3381 SelectObject( hdcSeek, hp );
\r
3384 // front-end wrapper for drawing functions to do rectangles
\r
3385 void DrawSeekBackground( int left, int top, int right, int bottom )
\r
3390 if (hdcSeek == NULL) {
\r
3391 hdcSeek = GetDC(hwndMain);
\r
3392 if (!appData.monoMode) {
\r
3393 SelectPalette(hdcSeek, hPal, FALSE);
\r
3394 RealizePalette(hdcSeek);
\r
3397 hp = SelectObject( hdcSeek, gridPen );
\r
3398 rc.top = boardRect.top+top; rc.left = boardRect.left+left;
\r
3399 rc.bottom = boardRect.top+bottom; rc.right = boardRect.left+right;
\r
3400 FillRect( hdcSeek, &rc, lightSquareBrush );
\r
3401 SelectObject( hdcSeek, hp );
\r
3404 // front-end wrapper for putting text in graph
\r
3405 void DrawSeekText(char *buf, int x, int y)
\r
3408 SetBkMode( hdcSeek, TRANSPARENT );
\r
3409 GetTextExtentPoint32( hdcSeek, buf, strlen(buf), &stSize );
\r
3410 TextOut( hdcSeek, boardRect.left+x-3, boardRect.top+y-stSize.cy/2, buf, strlen(buf) );
\r
3413 void DrawSeekDot(int x, int y, int color)
\r
3415 int square = color & 0x80;
\r
3416 HBRUSH oldBrush = SelectObject(hdcSeek,
\r
3417 color == 0 ? markerBrush : color == 1 ? darkSquareBrush : explodeBrush);
\r
3420 Rectangle(hdcSeek, boardRect.left+x - squareSize/9, boardRect.top+y - squareSize/9,
\r
3421 boardRect.left+x + squareSize/9, boardRect.top+y + squareSize/9);
\r
3423 Ellipse(hdcSeek, boardRect.left+x - squareSize/8, boardRect.top+y - squareSize/8,
\r
3424 boardRect.left+x + squareSize/8, boardRect.top+y + squareSize/8);
\r
3425 SelectObject(hdcSeek, oldBrush);
\r
3429 HDCDrawPosition(HDC hdc, BOOLEAN repaint, Board board)
\r
3431 static Board lastReq[2], lastDrawn[2];
\r
3432 static HighlightInfo lastDrawnHighlight, lastDrawnPremove;
\r
3433 static int lastDrawnFlipView = 0;
\r
3434 static int lastReqValid[2] = {0, 0}, lastDrawnValid[2] = {0, 0};
\r
3435 int releaseDC, x, y, x2, y2, row, column, num_clips = 0, i;
\r
3438 HBITMAP bufferBitmap;
\r
3439 HBITMAP oldBitmap;
\r
3441 HRGN clips[MAX_CLIPS];
\r
3442 ChessSquare dragged_piece = EmptySquare;
\r
3443 int nr = twoBoards*partnerUp;
\r
3445 /* I'm undecided on this - this function figures out whether a full
\r
3446 * repaint is necessary on its own, so there's no real reason to have the
\r
3447 * caller tell it that. I think this can safely be set to FALSE - but
\r
3448 * if we trust the callers not to request full repaints unnessesarily, then
\r
3449 * we could skip some clipping work. In other words, only request a full
\r
3450 * redraw when the majority of pieces have changed positions (ie. flip,
\r
3451 * gamestart and similar) --Hawk
\r
3453 Boolean fullrepaint = repaint;
\r
3455 if(DrawSeekGraph()) return; // [HG} seekgraph: suppress printing board if seek graph up
\r
3457 if( DrawPositionNeedsFullRepaint() ) {
\r
3458 fullrepaint = TRUE;
\r
3461 if (board == NULL) {
\r
3462 if (!lastReqValid[nr]) {
\r
3465 board = lastReq[nr];
\r
3467 CopyBoard(lastReq[nr], board);
\r
3468 lastReqValid[nr] = 1;
\r
3471 if (doingSizing) {
\r
3475 if (IsIconic(hwndMain)) {
\r
3479 if (hdc == NULL) {
\r
3480 hdc = GetDC(hwndMain);
\r
3481 if (!appData.monoMode) {
\r
3482 SelectPalette(hdc, hPal, FALSE);
\r
3483 RealizePalette(hdc);
\r
3487 releaseDC = FALSE;
\r
3490 /* Create some work-DCs */
\r
3491 hdcmem = CreateCompatibleDC(hdc);
\r
3492 tmphdc = CreateCompatibleDC(hdc);
\r
3494 /* If dragging is in progress, we temporarely remove the piece */
\r
3495 /* [HGM] or temporarily decrease count if stacked */
\r
3496 /* !! Moved to before board compare !! */
\r
3497 if (dragInfo.from.x >= 0 && dragInfo.pos.x >= 0) {
\r
3498 dragged_piece = board[dragInfo.from.y][dragInfo.from.x];
\r
3499 if(dragInfo.from.x == BOARD_LEFT-2 ) {
\r
3500 if(--board[dragInfo.from.y][dragInfo.from.x+1] == 0 )
\r
3501 board[dragInfo.from.y][dragInfo.from.x] = EmptySquare;
\r
3503 if(dragInfo.from.x == BOARD_RGHT+1) {
\r
3504 if(--board[dragInfo.from.y][dragInfo.from.x-1] == 0 )
\r
3505 board[dragInfo.from.y][dragInfo.from.x] = EmptySquare;
\r
3507 board[dragInfo.from.y][dragInfo.from.x] = EmptySquare;
\r
3510 /* Figure out which squares need updating by comparing the
\r
3511 * newest board with the last drawn board and checking if
\r
3512 * flipping has changed.
\r
3514 if (!fullrepaint && lastDrawnValid[nr] && (nr == 1 || lastDrawnFlipView == flipView)) {
\r
3515 for (row = 0; row < BOARD_HEIGHT; row++) { /* [HGM] true size, not 8 */
\r
3516 for (column = 0; column < BOARD_WIDTH; column++) {
\r
3517 if (lastDrawn[nr][row][column] != board[row][column]) {
\r
3518 SquareToPos(row, column, &x, &y);
\r
3519 clips[num_clips++] =
\r
3520 CreateRectRgn(x, y, x + squareSize, y + squareSize);
\r
3524 if(nr == 0) { // [HGM] dual: no highlights on second board
\r
3525 for (i=0; i<2; i++) {
\r
3526 if (lastDrawnHighlight.sq[i].x != highlightInfo.sq[i].x ||
\r
3527 lastDrawnHighlight.sq[i].y != highlightInfo.sq[i].y) {
\r
3528 if (lastDrawnHighlight.sq[i].x >= 0 &&
\r
3529 lastDrawnHighlight.sq[i].y >= 0) {
\r
3530 SquareToPos(lastDrawnHighlight.sq[i].y,
\r
3531 lastDrawnHighlight.sq[i].x, &x, &y);
\r
3532 clips[num_clips++] =
\r
3533 CreateRectRgn(x - lineGap, y - lineGap,
\r
3534 x + squareSize + lineGap, y + squareSize + lineGap);
\r
3536 if (highlightInfo.sq[i].x >= 0 && highlightInfo.sq[i].y >= 0) {
\r
3537 SquareToPos(highlightInfo.sq[i].y, highlightInfo.sq[i].x, &x, &y);
\r
3538 clips[num_clips++] =
\r
3539 CreateRectRgn(x - lineGap, y - lineGap,
\r
3540 x + squareSize + lineGap, y + squareSize + lineGap);
\r
3544 for (i=0; i<2; i++) {
\r
3545 if (lastDrawnPremove.sq[i].x != premoveHighlightInfo.sq[i].x ||
\r
3546 lastDrawnPremove.sq[i].y != premoveHighlightInfo.sq[i].y) {
\r
3547 if (lastDrawnPremove.sq[i].x >= 0 &&
\r
3548 lastDrawnPremove.sq[i].y >= 0) {
\r
3549 SquareToPos(lastDrawnPremove.sq[i].y,
\r
3550 lastDrawnPremove.sq[i].x, &x, &y);
\r
3551 clips[num_clips++] =
\r
3552 CreateRectRgn(x - lineGap, y - lineGap,
\r
3553 x + squareSize + lineGap, y + squareSize + lineGap);
\r
3555 if (premoveHighlightInfo.sq[i].x >= 0 &&
\r
3556 premoveHighlightInfo.sq[i].y >= 0) {
\r
3557 SquareToPos(premoveHighlightInfo.sq[i].y,
\r
3558 premoveHighlightInfo.sq[i].x, &x, &y);
\r
3559 clips[num_clips++] =
\r
3560 CreateRectRgn(x - lineGap, y - lineGap,
\r
3561 x + squareSize + lineGap, y + squareSize + lineGap);
\r
3565 } else { // nr == 1
\r
3566 partnerHighlightInfo.sq[0].y = board[EP_STATUS-4];
\r
3567 partnerHighlightInfo.sq[0].x = board[EP_STATUS-3];
\r
3568 partnerHighlightInfo.sq[1].y = board[EP_STATUS-2];
\r
3569 partnerHighlightInfo.sq[1].x = board[EP_STATUS-1];
\r
3570 for (i=0; i<2; i++) {
\r
3571 if (partnerHighlightInfo.sq[i].x >= 0 &&
\r
3572 partnerHighlightInfo.sq[i].y >= 0) {
\r
3573 SquareToPos(partnerHighlightInfo.sq[i].y,
\r
3574 partnerHighlightInfo.sq[i].x, &x, &y);
\r
3575 clips[num_clips++] =
\r
3576 CreateRectRgn(x - lineGap, y - lineGap,
\r
3577 x + squareSize + lineGap, y + squareSize + lineGap);
\r
3579 if (oldPartnerHighlight.sq[i].x >= 0 &&
\r
3580 oldPartnerHighlight.sq[i].y >= 0) {
\r
3581 SquareToPos(oldPartnerHighlight.sq[i].y,
\r
3582 oldPartnerHighlight.sq[i].x, &x, &y);
\r
3583 clips[num_clips++] =
\r
3584 CreateRectRgn(x - lineGap, y - lineGap,
\r
3585 x + squareSize + lineGap, y + squareSize + lineGap);
\r
3590 fullrepaint = TRUE;
\r
3593 /* Create a buffer bitmap - this is the actual bitmap
\r
3594 * being written to. When all the work is done, we can
\r
3595 * copy it to the real DC (the screen). This avoids
\r
3596 * the problems with flickering.
\r
3598 GetClientRect(hwndMain, &Rect);
\r
3599 bufferBitmap = CreateCompatibleBitmap(hdc, Rect.right-Rect.left+1,
\r
3600 Rect.bottom-Rect.top+1);
\r
3601 oldBitmap = SelectObject(hdcmem, bufferBitmap);
\r
3602 if (!appData.monoMode) {
\r
3603 SelectPalette(hdcmem, hPal, FALSE);
\r
3606 /* Create clips for dragging */
\r
3607 if (!fullrepaint) {
\r
3608 if (dragInfo.from.x >= 0) {
\r
3609 SquareToPos(dragInfo.from.y, dragInfo.from.x, &x, &y);
\r
3610 clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);
\r
3612 if (dragInfo.start.x >= 0) {
\r
3613 SquareToPos(dragInfo.start.y, dragInfo.start.x, &x, &y);
\r
3614 clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);
\r
3616 if (dragInfo.pos.x >= 0) {
\r
3617 x = dragInfo.pos.x - squareSize / 2;
\r
3618 y = dragInfo.pos.y - squareSize / 2;
\r
3619 clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);
\r
3621 if (dragInfo.lastpos.x >= 0) {
\r
3622 x = dragInfo.lastpos.x - squareSize / 2;
\r
3623 y = dragInfo.lastpos.y - squareSize / 2;
\r
3624 clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);
\r
3628 /* Are we animating a move?
\r
3630 * - remove the piece from the board (temporarely)
\r
3631 * - calculate the clipping region
\r
3633 if (!fullrepaint) {
\r
3634 if (animInfo.piece != EmptySquare) {
\r
3635 board[animInfo.from.y][animInfo.from.x] = EmptySquare;
\r
3636 x = boardRect.left + animInfo.lastpos.x;
\r
3637 y = boardRect.top + animInfo.lastpos.y;
\r
3638 x2 = boardRect.left + animInfo.pos.x;
\r
3639 y2 = boardRect.top + animInfo.pos.y;
\r
3640 clips[num_clips++] = CreateRectRgn(MIN(x,x2), MIN(y,y2), MAX(x,x2)+squareSize, MAX(y,y2)+squareSize);
\r
3641 /* Slight kludge. The real problem is that after AnimateMove is
\r
3642 done, the position on the screen does not match lastDrawn.
\r
3643 This currently causes trouble only on e.p. captures in
\r
3644 atomic, where the piece moves to an empty square and then
\r
3645 explodes. The old and new positions both had an empty square
\r
3646 at the destination, but animation has drawn a piece there and
\r
3647 we have to remember to erase it. [HGM] moved until after setting lastDrawn */
\r
3648 lastDrawn[0][animInfo.to.y][animInfo.to.x] = animInfo.piece;
\r
3652 /* No clips? Make sure we have fullrepaint set to TRUE */
\r
3653 if (num_clips == 0)
\r
3654 fullrepaint = TRUE;
\r
3656 /* Set clipping on the memory DC */
\r
3657 if (!fullrepaint) {
\r
3658 SelectClipRgn(hdcmem, clips[0]);
\r
3659 for (x = 1; x < num_clips; x++) {
\r
3660 if (ExtSelectClipRgn(hdcmem, clips[x], RGN_OR) == ERROR)
\r
3661 abort(); // this should never ever happen!
\r
3665 /* Do all the drawing to the memory DC */
\r
3666 if(explodeInfo.radius) { // [HGM] atomic
\r
3668 int x, y, r=(explodeInfo.radius * squareSize)/100;
\r
3669 board[explodeInfo.fromY][explodeInfo.fromX] = EmptySquare; // suppress display of capturer
\r
3670 SquareToPos(explodeInfo.toY, explodeInfo.toX, &x, &y);
\r
3671 x += squareSize/2;
\r
3672 y += squareSize/2;
\r
3673 if(!fullrepaint) {
\r
3674 clips[num_clips] = CreateRectRgn(x-r, y-r, x+r, y+r);
\r
3675 ExtSelectClipRgn(hdcmem, clips[num_clips++], RGN_OR);
\r
3677 DrawGridOnDC(hdcmem);
\r
3678 DrawHighlightsOnDC(hdcmem, &highlightInfo, HIGHLIGHT_PEN);
\r
3679 DrawHighlightsOnDC(hdcmem, &premoveHighlightInfo, PREMOVE_PEN);
\r
3680 DrawBoardOnDC(hdcmem, board, tmphdc);
\r
3681 oldBrush = SelectObject(hdcmem, explodeBrush);
\r
3682 Ellipse(hdcmem, x-r, y-r, x+r, y+r);
\r
3683 SelectObject(hdcmem, oldBrush);
\r
3685 DrawGridOnDC(hdcmem);
\r
3686 if(nr == 0) { // [HGM] dual: decide which highlights to draw
\r
3687 DrawHighlightsOnDC(hdcmem, &highlightInfo, HIGHLIGHT_PEN);
\r
3688 DrawHighlightsOnDC(hdcmem, &premoveHighlightInfo, PREMOVE_PEN);
\r
3690 DrawHighlightsOnDC(hdcmem, &partnerHighlightInfo, HIGHLIGHT_PEN);
\r
3691 oldPartnerHighlight = partnerHighlightInfo;
\r
3693 DrawBoardOnDC(hdcmem, board, tmphdc);
\r
3695 if(nr == 0) // [HGM] dual: markers only on left board
\r
3696 for (row = 0; row < BOARD_HEIGHT; row++) {
\r
3697 for (column = 0; column < BOARD_WIDTH; column++) {
\r
3698 if (marker[row][column]) { // marker changes only occur with full repaint!
\r
3699 HBRUSH oldBrush = SelectObject(hdcmem,
\r
3700 marker[row][column] == 2 ? markerBrush : explodeBrush);
\r
3701 SquareToPos(row, column, &x, &y);
\r
3702 Ellipse(hdcmem, x + squareSize/4, y + squareSize/4,
\r
3703 x + 3*squareSize/4, y + 3*squareSize/4);
\r
3704 SelectObject(hdcmem, oldBrush);
\r
3709 HBITMAP whiteLogo = (HBITMAP) first.programLogo, blackLogo = (HBITMAP) second.programLogo;
\r
3710 if(appData.autoLogo) {
\r
3712 switch(gameMode) { // pick logos based on game mode
\r
3713 case IcsObserving:
\r
3714 whiteLogo = second.programLogo; // ICS logo
\r
3715 blackLogo = second.programLogo;
\r
3718 case IcsPlayingWhite:
\r
3719 if(!appData.zippyPlay) whiteLogo = userLogo;
\r
3720 blackLogo = second.programLogo; // ICS logo
\r
3722 case IcsPlayingBlack:
\r
3723 whiteLogo = second.programLogo; // ICS logo
\r
3724 blackLogo = appData.zippyPlay ? first.programLogo : userLogo;
\r
3726 case TwoMachinesPlay:
\r
3727 if(first.twoMachinesColor[0] == 'b') {
\r
3728 whiteLogo = second.programLogo;
\r
3729 blackLogo = first.programLogo;
\r
3732 case MachinePlaysWhite:
\r
3733 blackLogo = userLogo;
\r
3735 case MachinePlaysBlack:
\r
3736 whiteLogo = userLogo;
\r
3737 blackLogo = first.programLogo;
\r
3740 DrawLogoOnDC(hdc, leftLogoRect, flipClock ? blackLogo : whiteLogo);
\r
3741 DrawLogoOnDC(hdc, rightLogoRect, flipClock ? whiteLogo : blackLogo);
\r
3744 if( appData.highlightMoveWithArrow ) {
\r
3745 DrawArrowHighlight(hdcmem);
\r
3748 DrawCoordsOnDC(hdcmem);
\r
3750 CopyBoard(lastDrawn[nr], board); /* [HGM] Moved to here from end of routine, */
\r
3751 /* to make sure lastDrawn contains what is actually drawn */
\r
3753 /* Put the dragged piece back into place and draw it (out of place!) */
\r
3754 if (dragged_piece != EmptySquare) {
\r
3755 /* [HGM] or restack */
\r
3756 if(dragInfo.from.x == BOARD_LEFT-2 )
\r
3757 board[dragInfo.from.y][dragInfo.from.x+1]++;
\r
3759 if(dragInfo.from.x == BOARD_RGHT+1 )
\r
3760 board[dragInfo.from.y][dragInfo.from.x-1]++;
\r
3761 board[dragInfo.from.y][dragInfo.from.x] = dragged_piece;
\r
3762 x = dragInfo.pos.x - squareSize / 2;
\r
3763 y = dragInfo.pos.y - squareSize / 2;
\r
3764 DrawPieceOnDC(hdcmem, dragged_piece,
\r
3765 ((int) dragged_piece < (int) BlackPawn),
\r
3766 (dragInfo.from.y + dragInfo.from.x) % 2, x, y, tmphdc);
\r
3769 /* Put the animated piece back into place and draw it */
\r
3770 if (animInfo.piece != EmptySquare) {
\r
3771 board[animInfo.from.y][animInfo.from.x] = animInfo.piece;
\r
3772 x = boardRect.left + animInfo.pos.x;
\r
3773 y = boardRect.top + animInfo.pos.y;
\r
3774 DrawPieceOnDC(hdcmem, animInfo.piece,
\r
3775 ((int) animInfo.piece < (int) BlackPawn),
\r
3776 (animInfo.from.y + animInfo.from.x) % 2, x, y, tmphdc);
\r
3779 /* Release the bufferBitmap by selecting in the old bitmap
\r
3780 * and delete the memory DC
\r
3782 SelectObject(hdcmem, oldBitmap);
\r
3785 /* Set clipping on the target DC */
\r
3786 if (!fullrepaint) {
\r
3787 if(nr == 1) for (x = 0; x < num_clips; x++) { // [HGM] dual: translate clips
\r
3789 GetRgnBox(clips[x], &rect);
\r
3790 DeleteObject(clips[x]);
\r
3791 clips[x] = CreateRectRgn(rect.left + wpMain.width/2, rect.top,
\r
3792 rect.right + wpMain.width/2, rect.bottom);
\r
3794 SelectClipRgn(hdc, clips[0]);
\r
3795 for (x = 1; x < num_clips; x++) {
\r
3796 if (ExtSelectClipRgn(hdc, clips[x], RGN_OR) == ERROR)
\r
3797 abort(); // this should never ever happen!
\r
3801 /* Copy the new bitmap onto the screen in one go.
\r
3802 * This way we avoid any flickering
\r
3804 oldBitmap = SelectObject(tmphdc, bufferBitmap);
\r
3805 BitBlt(hdc, boardRect.left + twoBoards*partnerUp*wpMain.width/2, boardRect.top, // [HGM] dual
\r
3806 boardRect.right - boardRect.left,
\r
3807 boardRect.bottom - boardRect.top,
\r
3808 tmphdc, boardRect.left, boardRect.top, SRCCOPY);
\r
3809 if(saveDiagFlag) {
\r
3810 BITMAP b; int i, j=0, m, w, wb, fac=0; char pData[1000000];
\r
3811 BITMAPINFOHEADER bih; int color[16], nrColors=0;
\r
3813 GetObject(bufferBitmap, sizeof(b), &b);
\r
3814 if(b.bmWidthBytes*b.bmHeight <= 990000) {
\r
3815 bih.biSize = sizeof(BITMAPINFOHEADER);
\r
3816 bih.biWidth = b.bmWidth;
\r
3817 bih.biHeight = b.bmHeight;
\r
3819 bih.biBitCount = b.bmBitsPixel;
\r
3820 bih.biCompression = 0;
\r
3821 bih.biSizeImage = b.bmWidthBytes*b.bmHeight;
\r
3822 bih.biXPelsPerMeter = 0;
\r
3823 bih.biYPelsPerMeter = 0;
\r
3824 bih.biClrUsed = 0;
\r
3825 bih.biClrImportant = 0;
\r
3826 // fprintf(diagFile, "t=%d\nw=%d\nh=%d\nB=%d\nP=%d\nX=%d\n",
\r
3827 // b.bmType, b.bmWidth, b.bmHeight, b.bmWidthBytes, b.bmPlanes, b.bmBitsPixel);
\r
3828 GetDIBits(tmphdc,bufferBitmap,0,b.bmHeight,pData,(BITMAPINFO*)&bih,DIB_RGB_COLORS);
\r
3829 // fprintf(diagFile, "%8x\n", (int) pData);
\r
3831 wb = b.bmWidthBytes;
\r
3833 for(i=0; i<wb*(b.bmHeight - boardRect.top + OUTER_MARGIN)>>2; i++) {
\r
3834 int k = ((int*) pData)[i];
\r
3835 for(j=0; j<nrColors; j++) if(color[j] == k) break;
\r
3836 if(j >= 16) break;
\r
3838 if(j >= nrColors) nrColors = j+1;
\r
3840 if(j<16) { // 16 colors is enough. Compress to 4 bits per pixel
\r
3842 for(i=0; i<b.bmHeight - boardRect.top + OUTER_MARGIN; i++) {
\r
3843 for(w=0; w<(wb>>2); w+=2) {
\r
3844 int k = ((int*) pData)[(wb*i>>2) + w];
\r
3845 for(j=0; j<nrColors; j++) if(color[j] == k) break;
\r
3846 k = ((int*) pData)[(wb*i>>2) + w + 1];
\r
3847 for(m=0; m<nrColors; m++) if(color[m] == k) break;
\r
3848 pData[p++] = m | j<<4;
\r
3850 while(p&3) pData[p++] = 0;
\r
3853 wb = ((wb+31)>>5)<<2;
\r
3855 // write BITMAPFILEHEADER
\r
3856 fprintf(diagFile, "BM");
\r
3857 fputDW(diagFile, wb*(b.bmHeight - boardRect.top + OUTER_MARGIN)+0x36 + (fac?64:0));
\r
3858 fputDW(diagFile, 0);
\r
3859 fputDW(diagFile, 0x36 + (fac?64:0));
\r
3860 // write BITMAPINFOHEADER
\r
3861 fputDW(diagFile, 40);
\r
3862 fputDW(diagFile, b.bmWidth);
\r
3863 fputDW(diagFile, b.bmHeight - boardRect.top + OUTER_MARGIN);
\r
3864 if(fac) fputDW(diagFile, 0x040001); // planes and bits/pixel
\r
3865 else fputDW(diagFile, 0x200001); // planes and bits/pixel
\r
3866 fputDW(diagFile, 0);
\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 // write color table
\r
3874 for(i=0; i<16; i++) fputDW(diagFile, color[i]);
\r
3875 // write bitmap data
\r
3876 for(i=0; i<wb*(b.bmHeight - boardRect.top + OUTER_MARGIN); i++)
\r
3877 fputc(pData[i], diagFile);
\r
3881 SelectObject(tmphdc, oldBitmap);
\r
3883 /* Massive cleanup */
\r
3884 for (x = 0; x < num_clips; x++)
\r
3885 DeleteObject(clips[x]);
\r
3888 DeleteObject(bufferBitmap);
\r
3891 ReleaseDC(hwndMain, hdc);
\r
3893 if (lastDrawnFlipView != flipView && nr == 0) {
\r
3895 CheckMenuItem(GetMenu(hwndMain),IDM_FlipView, MF_BYCOMMAND|MF_CHECKED);
\r
3897 CheckMenuItem(GetMenu(hwndMain),IDM_FlipView, MF_BYCOMMAND|MF_UNCHECKED);
\r
3900 /* CopyBoard(lastDrawn, board);*/
\r
3901 lastDrawnHighlight = highlightInfo;
\r
3902 lastDrawnPremove = premoveHighlightInfo;
\r
3903 lastDrawnFlipView = flipView;
\r
3904 lastDrawnValid[nr] = 1;
\r
3907 /* [HGM] diag: Save the current board display to the given open file and close the file */
\r
3912 saveDiagFlag = 1; diagFile = f;
\r
3913 HDCDrawPosition(NULL, TRUE, NULL);
\r
3917 // if(f != NULL) fprintf(f, "Sorry, but this feature is still in preparation\n");
\r
3924 /*---------------------------------------------------------------------------*\
\r
3925 | CLIENT PAINT PROCEDURE
\r
3926 | This is the main event-handler for the WM_PAINT message.
\r
3928 \*---------------------------------------------------------------------------*/
\r
3930 PaintProc(HWND hwnd)
\r
3936 if((hdc = BeginPaint(hwnd, &ps))) {
\r
3937 if (IsIconic(hwnd)) {
\r
3938 DrawIcon(hdc, 2, 2, iconCurrent);
\r
3940 if (!appData.monoMode) {
\r
3941 SelectPalette(hdc, hPal, FALSE);
\r
3942 RealizePalette(hdc);
\r
3944 HDCDrawPosition(hdc, 1, NULL);
\r
3945 if(twoBoards) { // [HGM] dual: also redraw other board in other orientation
\r
3946 flipView = !flipView; partnerUp = !partnerUp;
\r
3947 HDCDrawPosition(hdc, 1, NULL);
\r
3948 flipView = !flipView; partnerUp = !partnerUp;
\r
3951 SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);
\r
3952 ExtTextOut(hdc, messageRect.left, messageRect.top,
\r
3953 ETO_CLIPPED|ETO_OPAQUE,
\r
3954 &messageRect, messageText, strlen(messageText), NULL);
\r
3955 SelectObject(hdc, oldFont);
\r
3956 DisplayBothClocks();
\r
3958 EndPaint(hwnd,&ps);
\r
3966 * If the user selects on a border boundary, return -1; if off the board,
\r
3967 * return -2. Otherwise map the event coordinate to the square.
\r
3968 * The offset boardRect.left or boardRect.top must already have been
\r
3969 * subtracted from x.
\r
3971 int EventToSquare(x, limit)
\r
3979 if ((x % (squareSize + lineGap)) >= squareSize)
\r
3981 x /= (squareSize + lineGap);
\r
3993 DropEnable dropEnables[] = {
\r
3994 { 'P', DP_Pawn, N_("Pawn") },
\r
3995 { 'N', DP_Knight, N_("Knight") },
\r
3996 { 'B', DP_Bishop, N_("Bishop") },
\r
3997 { 'R', DP_Rook, N_("Rook") },
\r
3998 { 'Q', DP_Queen, N_("Queen") },
\r
4002 SetupDropMenu(HMENU hmenu)
\r
4004 int i, count, enable;
\r
4006 extern char white_holding[], black_holding[];
\r
4007 char item[MSG_SIZ];
\r
4009 for (i=0; i<sizeof(dropEnables)/sizeof(DropEnable); i++) {
\r
4010 p = strchr(gameMode == IcsPlayingWhite ? white_holding : black_holding,
\r
4011 dropEnables[i].piece);
\r
4013 while (p && *p++ == dropEnables[i].piece) count++;
\r
4014 sprintf(item, "%s %d", T_(dropEnables[i].name), count);
\r
4015 enable = count > 0 || !appData.testLegality
\r
4016 /*!!temp:*/ || (gameInfo.variant == VariantCrazyhouse
\r
4017 && !appData.icsActive);
\r
4018 ModifyMenu(hmenu, dropEnables[i].command,
\r
4019 MF_BYCOMMAND | (enable ? MF_ENABLED : MF_GRAYED) | MF_STRING,
\r
4020 dropEnables[i].command, item);
\r
4024 void DragPieceBegin(int x, int y)
\r
4026 dragInfo.lastpos.x = boardRect.left + x;
\r
4027 dragInfo.lastpos.y = boardRect.top + y;
\r
4028 dragInfo.from.x = fromX;
\r
4029 dragInfo.from.y = fromY;
\r
4030 dragInfo.start = dragInfo.from;
\r
4031 SetCapture(hwndMain);
\r
4034 void DragPieceEnd(int x, int y)
\r
4037 dragInfo.start.x = dragInfo.start.y = -1;
\r
4038 dragInfo.from = dragInfo.start;
\r
4039 dragInfo.pos = dragInfo.lastpos = dragInfo.start;
\r
4042 /* Event handler for mouse messages */
\r
4044 MouseEvent(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
\r
4048 static int recursive = 0;
\r
4050 BOOLEAN forceFullRepaint = IsFullRepaintPreferrable(); /* [AS] */
\r
4053 if (message == WM_MBUTTONUP) {
\r
4054 /* Hideous kludge to fool TrackPopupMenu into paying attention
\r
4055 to the middle button: we simulate pressing the left button too!
\r
4057 PostMessage(hwnd, WM_LBUTTONDOWN, wParam, lParam);
\r
4058 PostMessage(hwnd, WM_LBUTTONUP, wParam, lParam);
\r
4064 pt.x = LOWORD(lParam);
\r
4065 pt.y = HIWORD(lParam);
\r
4066 x = EventToSquare(pt.x - boardRect.left, BOARD_WIDTH);
\r
4067 y = EventToSquare(pt.y - boardRect.top, BOARD_HEIGHT);
\r
4068 if (!flipView && y >= 0) {
\r
4069 y = BOARD_HEIGHT - 1 - y;
\r
4071 if (flipView && x >= 0) {
\r
4072 x = BOARD_WIDTH - 1 - x;
\r
4075 switch (message) {
\r
4076 case WM_LBUTTONDOWN:
\r
4077 if (PtInRect((LPRECT) &whiteRect, pt)) {
\r
4078 if (gameMode == EditPosition) {
\r
4079 SetWhiteToPlayEvent();
\r
4080 } else if (gameMode == EditGame || GetKeyState(VK_SHIFT) < 0) {
\r
4081 AdjustClock(flipClock, -1);
\r
4082 } else if (gameMode == IcsPlayingBlack ||
\r
4083 gameMode == MachinePlaysWhite) {
\r
4086 } else if (PtInRect((LPRECT) &blackRect, pt)) {
\r
4087 if (gameMode == EditPosition) {
\r
4088 SetBlackToPlayEvent();
\r
4089 } else if (gameMode == EditGame || GetKeyState(VK_SHIFT) < 0) {
\r
4090 AdjustClock(!flipClock, -1);
\r
4091 } else if (gameMode == IcsPlayingWhite ||
\r
4092 gameMode == MachinePlaysBlack) {
\r
4096 dragInfo.start.x = dragInfo.start.y = -1;
\r
4097 dragInfo.from = dragInfo.start;
\r
4098 if(fromX == -1 && frozen) { // not sure where this is for
\r
4099 fromX = fromY = -1;
\r
4100 DrawPosition(forceFullRepaint || FALSE, NULL); /* [AS] */
\r
4103 LeftClick(Press, pt.x - boardRect.left, pt.y - boardRect.top);
\r
4104 DrawPosition(TRUE, NULL);
\r
4107 case WM_LBUTTONUP:
\r
4108 LeftClick(Release, pt.x - boardRect.left, pt.y - boardRect.top);
\r
4109 DrawPosition(TRUE, NULL);
\r
4112 case WM_MOUSEMOVE:
\r
4113 if(SeekGraphClick(Press, pt.x - boardRect.left, pt.y - boardRect.top, 1)) break;
\r
4114 MovePV(pt.x - boardRect.left, pt.y - boardRect.top, boardRect.bottom - boardRect.top);
\r
4115 if ((appData.animateDragging || appData.highlightDragging)
\r
4116 && (wParam & MK_LBUTTON)
\r
4117 && dragInfo.from.x >= 0)
\r
4119 BOOL full_repaint = FALSE;
\r
4121 if (appData.animateDragging) {
\r
4122 dragInfo.pos = pt;
\r
4124 if (appData.highlightDragging) {
\r
4125 SetHighlights(fromX, fromY, x, y);
\r
4126 if( IsDrawArrowEnabled() && (x < 0 || x >= BOARD_WIDTH || y < 0 || y >= BOARD_HEIGHT) ) {
\r
4127 full_repaint = TRUE;
\r
4131 DrawPosition( full_repaint, NULL);
\r
4133 dragInfo.lastpos = dragInfo.pos;
\r
4137 case WM_MOUSEWHEEL: // [DM]
\r
4138 { static int lastDir = 0; // [HGM] build in some hysteresis to avoid spurious events
\r
4139 /* Mouse Wheel is being rolled forward
\r
4140 * Play moves forward
\r
4142 if((short)HIWORD(wParam) < 0 && currentMove < forwardMostMove)
\r
4143 { if(lastDir == 1) ForwardEvent(); else lastDir = 1; } // [HGM] suppress first event in direction
\r
4144 /* Mouse Wheel is being rolled backward
\r
4145 * Play moves backward
\r
4147 if((short)HIWORD(wParam) > 0 && currentMove > backwardMostMove)
\r
4148 { if(lastDir == -1) BackwardEvent(); else lastDir = -1; }
\r
4152 case WM_MBUTTONUP:
\r
4153 case WM_RBUTTONUP:
\r
4155 RightClick(Release, pt.x - boardRect.left, pt.y - boardRect.top, &fromX, &fromY);
\r
4158 case WM_MBUTTONDOWN:
\r
4159 case WM_RBUTTONDOWN:
\r
4162 fromX = fromY = -1;
\r
4163 dragInfo.pos.x = dragInfo.pos.y = -1;
\r
4164 dragInfo.start.x = dragInfo.start.y = -1;
\r
4165 dragInfo.from = dragInfo.start;
\r
4166 dragInfo.lastpos = dragInfo.pos;
\r
4167 if (appData.highlightDragging) {
\r
4168 ClearHighlights();
\r
4171 /* [HGM] right mouse button in clock area edit-game mode ups clock */
\r
4172 if (PtInRect((LPRECT) &whiteRect, pt)) {
\r
4173 if (gameMode == EditGame || GetKeyState(VK_SHIFT) < 0) AdjustClock(flipClock, 1);
\r
4174 } else if (PtInRect((LPRECT) &blackRect, pt)) {
\r
4175 if (gameMode == EditGame || GetKeyState(VK_SHIFT) < 0) AdjustClock(!flipClock, 1);
\r
4179 DrawPosition(TRUE, NULL);
\r
4181 menuNr = RightClick(Press, pt.x - boardRect.left, pt.y - boardRect.top, &fromX, &fromY);
\r
4184 if (message == WM_MBUTTONDOWN) {
\r
4185 buttonCount = 3; /* even if system didn't think so */
\r
4186 if (wParam & MK_SHIFT)
\r
4187 MenuPopup(hwnd, pt, LoadMenu(hInst, "BlackPieceMenu"), -1);
\r
4189 MenuPopup(hwnd, pt, LoadMenu(hInst, "WhitePieceMenu"), -1);
\r
4190 } else { /* message == WM_RBUTTONDOWN */
\r
4191 /* Just have one menu, on the right button. Windows users don't
\r
4192 think to try the middle one, and sometimes other software steals
\r
4193 it, or it doesn't really exist. */
\r
4194 if(gameInfo.variant != VariantShogi)
\r
4195 MenuPopup(hwnd, pt, LoadMenu(hInst, "PieceMenu"), -1);
\r
4197 MenuPopup(hwnd, pt, LoadMenu(hInst, "ShogiPieceMenu"), -1);
\r
4201 SetCapture(hwndMain);
4204 hmenu = LoadMenu(hInst, "DropPieceMenu");
\r
4205 SetupDropMenu(hmenu);
\r
4206 MenuPopup(hwnd, pt, hmenu, -1);
\r
4216 /* Preprocess messages for buttons in main window */
\r
4218 ButtonProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
\r
4220 int id = GetWindowLong(hwnd, GWL_ID);
\r
4223 for (i=0; i<N_BUTTONS; i++) {
\r
4224 if (buttonDesc[i].id == id) break;
\r
4226 if (i == N_BUTTONS) return 0;
\r
4227 switch (message) {
\r
4232 dir = (wParam == VK_LEFT) ? -1 : 1;
\r
4233 SetFocus(buttonDesc[(i + dir + N_BUTTONS) % N_BUTTONS].hwnd);
\r
4240 SendMessage(hwndMain, WM_COMMAND, MAKEWPARAM(buttonDesc[i].id, 0), 0);
\r
4243 if (appData.icsActive && (isalpha((char)wParam) || wParam == '0')) {
\r
4244 // [HGM] movenum: only letters or leading zero should go to ICS input
\r
4245 HWND h = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
4246 if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);
\r
4248 SendMessage(h, WM_CHAR, wParam, lParam);
\r
4250 } else if (isalpha((char)wParam) || isdigit((char)wParam)){
\r
4251 PopUpMoveDialog((char)wParam);
\r
4257 return CallWindowProc(buttonDesc[i].wndproc, hwnd, message, wParam, lParam);
\r
4260 /* Process messages for Promotion dialog box */
\r
4262 Promotion(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
4266 switch (message) {
\r
4267 case WM_INITDIALOG: /* message: initialize dialog box */
\r
4268 /* Center the dialog over the application window */
\r
4269 CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));
\r
4270 Translate(hDlg, DLG_PromotionKing);
\r
4271 ShowWindow(GetDlgItem(hDlg, PB_King),
\r
4272 (!appData.testLegality || gameInfo.variant == VariantSuicide ||
\r
4273 gameInfo.variant == VariantGiveaway || gameInfo.variant == VariantSuper ) ?
\r
4274 SW_SHOW : SW_HIDE);
\r
4275 /* [HGM] Only allow C & A promotions if these pieces are defined */
\r
4276 ShowWindow(GetDlgItem(hDlg, PB_Archbishop),
\r
4277 ((PieceToChar(WhiteAngel) >= 'A' &&
\r
4278 PieceToChar(WhiteAngel) != '~') ||
\r
4279 (PieceToChar(BlackAngel) >= 'A' &&
\r
4280 PieceToChar(BlackAngel) != '~') ) ?
\r
4281 SW_SHOW : SW_HIDE);
\r
4282 ShowWindow(GetDlgItem(hDlg, PB_Chancellor),
\r
4283 ((PieceToChar(WhiteMarshall) >= 'A' &&
\r
4284 PieceToChar(WhiteMarshall) != '~') ||
\r
4285 (PieceToChar(BlackMarshall) >= 'A' &&
\r
4286 PieceToChar(BlackMarshall) != '~') ) ?
\r
4287 SW_SHOW : SW_HIDE);
\r
4288 /* [HGM] Hide B & R button in Shogi, use Q as promote, N as defer */
\r
4289 ShowWindow(GetDlgItem(hDlg, PB_Rook),
\r
4290 gameInfo.variant != VariantShogi ?
\r
4291 SW_SHOW : SW_HIDE);
\r
4292 ShowWindow(GetDlgItem(hDlg, PB_Bishop),
\r
4293 gameInfo.variant != VariantShogi ?
\r
4294 SW_SHOW : SW_HIDE);
\r
4295 ShowWindow(GetDlgItem(hDlg, IDC_Yes),
\r
4296 gameInfo.variant == VariantShogi ?
\r
4297 SW_SHOW : SW_HIDE);
\r
4298 ShowWindow(GetDlgItem(hDlg, IDC_No),
\r
4299 gameInfo.variant == VariantShogi ?
\r
4300 SW_SHOW : SW_HIDE);
\r
4301 ShowWindow(GetDlgItem(hDlg, IDC_Centaur),
\r
4302 gameInfo.variant == VariantSuper ?
\r
4303 SW_SHOW : SW_HIDE);
\r
4306 case WM_COMMAND: /* message: received a command */
\r
4307 switch (LOWORD(wParam)) {
\r
4309 EndDialog(hDlg, TRUE); /* Exit the dialog */
\r
4310 ClearHighlights();
\r
4311 DrawPosition(FALSE, NULL);
\r
4314 promoChar = gameInfo.variant == VariantSuper ? PieceToChar(BlackSilver) : PieceToChar(BlackKing);
\r
4317 promoChar = gameInfo.variant == VariantShogi ? '+' : PieceToChar(BlackQueen);
\r
4320 promoChar = PieceToChar(BlackRook);
\r
4323 promoChar = PieceToChar(BlackBishop);
\r
4325 case PB_Chancellor:
\r
4326 promoChar = PieceToChar(BlackMarshall);
\r
4328 case PB_Archbishop:
\r
4329 promoChar = PieceToChar(BlackAngel);
\r
4332 promoChar = gameInfo.variant == VariantShogi ? '=' : PieceToChar(BlackKnight);
\r
4337 EndDialog(hDlg, TRUE); /* Exit the dialog */
\r
4338 /* [HGM] <popupFix> Call FinishMove rather than UserMoveEvent, as we
\r
4339 only show the popup when we are already sure the move is valid or
\r
4340 legal. We pass a faulty move type, but the kludge is that FinishMove
\r
4341 will figure out it is a promotion from the promoChar. */
\r
4342 UserMoveEvent(fromX, fromY, toX, toY, promoChar);
\r
4343 fromX = fromY = -1;
\r
4344 if (!appData.highlightLastMove) {
\r
4345 ClearHighlights();
\r
4346 DrawPosition(FALSE, NULL);
\r
4353 /* Pop up promotion dialog */
\r
4355 PromotionPopup(HWND hwnd)
\r
4359 lpProc = MakeProcInstance((FARPROC)Promotion, hInst);
\r
4360 DialogBox(hInst, MAKEINTRESOURCE(DLG_PromotionKing),
\r
4361 hwnd, (DLGPROC)lpProc);
\r
4362 FreeProcInstance(lpProc);
\r
4368 DrawPosition(TRUE, NULL);
\r
4369 PromotionPopup(hwndMain);
\r
4372 /* Toggle ShowThinking */
\r
4374 ToggleShowThinking()
\r
4376 appData.showThinking = !appData.showThinking;
\r
4377 ShowThinkingEvent();
\r
4381 LoadGameDialog(HWND hwnd, char* title)
\r
4385 char fileTitle[MSG_SIZ];
\r
4386 f = OpenFileDialog(hwnd, "rb", "",
\r
4387 appData.oldSaveStyle ? "gam" : "pgn",
\r
4389 title, &number, fileTitle, NULL);
\r
4391 cmailMsgLoaded = FALSE;
\r
4392 if (number == 0) {
\r
4393 int error = GameListBuild(f);
\r
4395 DisplayError(_("Cannot build game list"), error);
\r
4396 } else if (!ListEmpty(&gameList) &&
\r
4397 ((ListGame *) gameList.tailPred)->number > 1) {
\r
4398 GameListPopUp(f, fileTitle);
\r
4401 GameListDestroy();
\r
4404 LoadGame(f, number, fileTitle, FALSE);
\r
4408 int get_term_width()
\r
4413 HFONT hfont, hold_font;
\r
4418 hText = GetDlgItem(hwndConsole, OPT_ConsoleText);
\r
4422 // get the text metrics
\r
4423 hdc = GetDC(hText);
\r
4424 lf = font[boardSize][CONSOLE_FONT]->lf;
\r
4425 if (consoleCF.dwEffects & CFE_BOLD)
\r
4426 lf.lfWeight = FW_BOLD;
\r
4427 if (consoleCF.dwEffects & CFE_ITALIC)
\r
4428 lf.lfItalic = TRUE;
\r
4429 if (consoleCF.dwEffects & CFE_STRIKEOUT)
\r
4430 lf.lfStrikeOut = TRUE;
\r
4431 if (consoleCF.dwEffects & CFE_UNDERLINE)
\r
4432 lf.lfUnderline = TRUE;
\r
4433 hfont = CreateFontIndirect(&lf);
\r
4434 hold_font = SelectObject(hdc, hfont);
\r
4435 GetTextMetrics(hdc, &tm);
\r
4436 SelectObject(hdc, hold_font);
\r
4437 DeleteObject(hfont);
\r
4438 ReleaseDC(hText, hdc);
\r
4440 // get the rectangle
\r
4441 SendMessage(hText, EM_GETRECT, 0, (LPARAM)&rc);
\r
4443 return (rc.right-rc.left) / tm.tmAveCharWidth;
\r
4446 void UpdateICSWidth(HWND hText)
\r
4448 LONG old_width, new_width;
\r
4450 new_width = get_term_width(hText, FALSE);
\r
4451 old_width = GetWindowLong(hText, GWL_USERDATA);
\r
4452 if (new_width != old_width)
\r
4454 ics_update_width(new_width);
\r
4455 SetWindowLong(hText, GWL_USERDATA, new_width);
\r
4460 ChangedConsoleFont()
\r
4463 CHARRANGE tmpsel, sel;
\r
4464 MyFont *f = font[boardSize][CONSOLE_FONT];
\r
4465 HWND hText = GetDlgItem(hwndConsole, OPT_ConsoleText);
\r
4466 HWND hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
4469 cfmt.cbSize = sizeof(CHARFORMAT);
\r
4470 cfmt.dwMask = CFM_FACE|CFM_SIZE|CFM_CHARSET;
\r
4471 strcpy(cfmt.szFaceName, font[boardSize][CONSOLE_FONT]->mfp.faceName);
\r
4472 /* yHeight is expressed in twips. A twip is 1/20 of a font's point
\r
4473 * size. This was undocumented in the version of MSVC++ that I had
\r
4474 * when I wrote the code, but is apparently documented now.
\r
4476 cfmt.yHeight = (int)(f->mfp.pointSize * 20.0 + 0.5);
\r
4477 cfmt.bCharSet = f->lf.lfCharSet;
\r
4478 cfmt.bPitchAndFamily = f->lf.lfPitchAndFamily;
\r
4479 SendMessage(hText, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &cfmt);
\r
4480 SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &cfmt);
\r
4481 /* Why are the following seemingly needed too? */
\r
4482 SendMessage(hText, EM_SETCHARFORMAT, SCF_DEFAULT, (LPARAM) &cfmt);
\r
4483 SendMessage(hInput, EM_SETCHARFORMAT, SCF_DEFAULT, (LPARAM) &cfmt);
\r
4484 SendMessage(hText, EM_EXGETSEL, 0, (LPARAM)&sel);
\r
4486 tmpsel.cpMax = -1; /*999999?*/
\r
4487 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&tmpsel);
\r
4488 SendMessage(hText, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cfmt);
\r
4489 /* Trying putting this here too. It still seems to tickle a RichEdit
\r
4490 * bug: sometimes RichEdit indents the first line of a paragraph too.
\r
4492 paraf.cbSize = sizeof(paraf);
\r
4493 paraf.dwMask = PFM_OFFSET | PFM_STARTINDENT;
\r
4494 paraf.dxStartIndent = 0;
\r
4495 paraf.dxOffset = WRAP_INDENT;
\r
4496 SendMessage(hText, EM_SETPARAFORMAT, 0, (LPARAM) ¶f);
\r
4497 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
4498 UpdateICSWidth(hText);
\r
4501 /*---------------------------------------------------------------------------*\
\r
4503 * Window Proc for main window
\r
4505 \*---------------------------------------------------------------------------*/
\r
4507 /* Process messages for main window, etc. */
\r
4509 WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
\r
4512 int wmId, wmEvent;
\r
4516 char fileTitle[MSG_SIZ];
\r
4517 char buf[MSG_SIZ];
\r
4518 static SnapData sd;
\r
4520 switch (message) {
\r
4522 case WM_PAINT: /* message: repaint portion of window */
\r
4526 case WM_ERASEBKGND:
\r
4527 if (IsIconic(hwnd)) {
\r
4528 /* Cheat; change the message */
\r
4529 return (DefWindowProc(hwnd, WM_ICONERASEBKGND, wParam, lParam));
\r
4531 return (DefWindowProc(hwnd, message, wParam, lParam));
\r
4535 case WM_LBUTTONDOWN:
\r
4536 case WM_MBUTTONDOWN:
\r
4537 case WM_RBUTTONDOWN:
\r
4538 case WM_LBUTTONUP:
\r
4539 case WM_MBUTTONUP:
\r
4540 case WM_RBUTTONUP:
\r
4541 case WM_MOUSEMOVE:
\r
4542 case WM_MOUSEWHEEL:
\r
4543 MouseEvent(hwnd, message, wParam, lParam);
\r
4546 JAWS_KB_NAVIGATION
\r
4550 JAWS_ALT_INTERCEPT
\r
4552 if (appData.icsActive && ((char)wParam == '\r' || (char)wParam > ' ' && !((char)wParam >= '1' && (char)wParam <= '9'))) {
\r
4553 // [HGM] movenum: for non-zero digits we always do type-in dialog
\r
4554 HWND h = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
4555 if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);
\r
4557 SendMessage(h, message, wParam, lParam);
\r
4558 } else if(lParam != KF_REPEAT) {
\r
4559 if (isalpha((char)wParam) || isdigit((char)wParam)) {
\r
4560 PopUpMoveDialog((char)wParam);
\r
4561 } else if((char)wParam == 003) CopyGameToClipboard();
\r
4562 else if((char)wParam == 026) PasteGameOrFENFromClipboard();
\r
4567 case WM_PALETTECHANGED:
\r
4568 if (hwnd != (HWND)wParam && !appData.monoMode) {
\r
4570 HDC hdc = GetDC(hwndMain);
\r
4571 SelectPalette(hdc, hPal, TRUE);
\r
4572 nnew = RealizePalette(hdc);
\r
4574 paletteChanged = TRUE;
\r
4575 InvalidateRect(hwnd, &boardRect, FALSE);
\r
4577 ReleaseDC(hwnd, hdc);
\r
4581 case WM_QUERYNEWPALETTE:
\r
4582 if (!appData.monoMode /*&& paletteChanged*/) {
\r
4584 HDC hdc = GetDC(hwndMain);
\r
4585 paletteChanged = FALSE;
\r
4586 SelectPalette(hdc, hPal, FALSE);
\r
4587 nnew = RealizePalette(hdc);
\r
4589 InvalidateRect(hwnd, &boardRect, FALSE);
\r
4591 ReleaseDC(hwnd, hdc);
\r
4596 case WM_COMMAND: /* message: command from application menu */
\r
4597 wmId = LOWORD(wParam);
\r
4598 wmEvent = HIWORD(wParam);
\r
4603 SAY("new game enter a move to play against the computer with white");
\r
4606 case IDM_NewGameFRC:
\r
4607 if( NewGameFRC() == 0 ) {
\r
4612 case IDM_NewVariant:
\r
4613 NewVariantPopup(hwnd);
\r
4616 case IDM_LoadGame:
\r
4617 LoadGameDialog(hwnd, _("Load Game from File"));
\r
4620 case IDM_LoadNextGame:
\r
4624 case IDM_LoadPrevGame:
\r
4628 case IDM_ReloadGame:
\r
4632 case IDM_LoadPosition:
\r
4633 if (gameMode == AnalyzeMode || gameMode == AnalyzeFile) {
\r
4634 Reset(FALSE, TRUE);
\r
4637 f = OpenFileDialog(hwnd, "rb", "",
\r
4638 appData.oldSaveStyle ? "pos" : "fen",
\r
4640 _("Load Position from File"), &number, fileTitle, NULL);
\r
4642 LoadPosition(f, number, fileTitle);
\r
4646 case IDM_LoadNextPosition:
\r
4647 ReloadPosition(1);
\r
4650 case IDM_LoadPrevPosition:
\r
4651 ReloadPosition(-1);
\r
4654 case IDM_ReloadPosition:
\r
4655 ReloadPosition(0);
\r
4658 case IDM_SaveGame:
\r
4659 defName = DefaultFileName(appData.oldSaveStyle ? "gam" : "pgn");
\r
4660 f = OpenFileDialog(hwnd, "a", defName,
\r
4661 appData.oldSaveStyle ? "gam" : "pgn",
\r
4663 _("Save Game to File"), NULL, fileTitle, NULL);
\r
4665 SaveGame(f, 0, "");
\r
4669 case IDM_SavePosition:
\r
4670 defName = DefaultFileName(appData.oldSaveStyle ? "pos" : "fen");
\r
4671 f = OpenFileDialog(hwnd, "a", defName,
\r
4672 appData.oldSaveStyle ? "pos" : "fen",
\r
4674 _("Save Position to File"), NULL, fileTitle, NULL);
\r
4676 SavePosition(f, 0, "");
\r
4680 case IDM_SaveDiagram:
\r
4681 defName = "diagram";
\r
4682 f = OpenFileDialog(hwnd, "wb", defName,
\r
4685 "Save Diagram to File", NULL, fileTitle, NULL);
\r
4691 case IDM_CopyGame:
\r
4692 CopyGameToClipboard();
\r
4695 case IDM_PasteGame:
\r
4696 PasteGameFromClipboard();
\r
4699 case IDM_CopyGameListToClipboard:
\r
4700 CopyGameListToClipboard();
\r
4703 /* [AS] Autodetect FEN or PGN data */
\r
4704 case IDM_PasteAny:
\r
4705 PasteGameOrFENFromClipboard();
\r
4708 /* [AS] Move history */
\r
4709 case IDM_ShowMoveHistory:
\r
4710 if( MoveHistoryIsUp() ) {
\r
4711 MoveHistoryPopDown();
\r
4714 MoveHistoryPopUp();
\r
4718 /* [AS] Eval graph */
\r
4719 case IDM_ShowEvalGraph:
\r
4720 if( EvalGraphIsUp() ) {
\r
4721 EvalGraphPopDown();
\r
4725 SetFocus(hwndMain);
\r
4729 /* [AS] Engine output */
\r
4730 case IDM_ShowEngineOutput:
\r
4731 if( EngineOutputIsUp() ) {
\r
4732 EngineOutputPopDown();
\r
4735 EngineOutputPopUp();
\r
4739 /* [AS] User adjudication */
\r
4740 case IDM_UserAdjudication_White:
\r
4741 UserAdjudicationEvent( +1 );
\r
4744 case IDM_UserAdjudication_Black:
\r
4745 UserAdjudicationEvent( -1 );
\r
4748 case IDM_UserAdjudication_Draw:
\r
4749 UserAdjudicationEvent( 0 );
\r
4752 /* [AS] Game list options dialog */
\r
4753 case IDM_GameListOptions:
\r
4754 GameListOptions();
\r
4761 case IDM_CopyPosition:
\r
4762 CopyFENToClipboard();
\r
4765 case IDM_PastePosition:
\r
4766 PasteFENFromClipboard();
\r
4769 case IDM_MailMove:
\r
4773 case IDM_ReloadCMailMsg:
\r
4774 Reset(TRUE, TRUE);
\r
4775 ReloadCmailMsgEvent(FALSE);
\r
4778 case IDM_Minimize:
\r
4779 ShowWindow(hwnd, SW_MINIMIZE);
\r
4786 case IDM_MachineWhite:
\r
4787 MachineWhiteEvent();
\r
4789 * refresh the tags dialog only if it's visible
\r
4791 if (gameMode == MachinePlaysWhite && IsWindowVisible(editTagsDialog)) {
\r
4793 tags = PGNTags(&gameInfo);
\r
4794 TagsPopUp(tags, CmailMsg());
\r
4797 SAY("computer starts playing white");
\r
4800 case IDM_MachineBlack:
\r
4801 MachineBlackEvent();
\r
4803 * refresh the tags dialog only if it's visible
\r
4805 if (gameMode == MachinePlaysBlack && IsWindowVisible(editTagsDialog)) {
\r
4807 tags = PGNTags(&gameInfo);
\r
4808 TagsPopUp(tags, CmailMsg());
\r
4811 SAY("computer starts playing black");
\r
4814 case IDM_Match: // [HGM] match: flows into next case, after setting Match Mode and nr of Games
\r
4815 if(gameMode != BeginningOfGame) break; // allow menu item to remain enabled for better mode highligting
\r
4816 matchMode = 2;// distinguish from command-line-triggered case (matchMode=1)
\r
4817 appData.matchGames = appData.defaultMatchGames;
\r
4820 case IDM_TwoMachines:
\r
4821 TwoMachinesEvent();
\r
4823 * refresh the tags dialog only if it's visible
\r
4825 if (gameMode == TwoMachinesPlay && IsWindowVisible(editTagsDialog)) {
\r
4827 tags = PGNTags(&gameInfo);
\r
4828 TagsPopUp(tags, CmailMsg());
\r
4831 SAY("computer starts playing both sides");
\r
4834 case IDM_AnalysisMode:
\r
4835 if (!first.analysisSupport) {
\r
4836 sprintf(buf, _("%s does not support analysis"), first.tidy);
\r
4837 DisplayError(buf, 0);
\r
4839 SAY("analyzing current position");
\r
4840 /* [DM] icsEngineAnlyze [HGM] Why is this front-end??? */
\r
4841 if (appData.icsActive) {
\r
4842 if (gameMode != IcsObserving) {
\r
4843 sprintf(buf, "You are not observing a game");
\r
4844 DisplayError(buf, 0);
\r
4845 /* secure check */
\r
4846 if (appData.icsEngineAnalyze) {
\r
4847 if (appData.debugMode)
\r
4848 fprintf(debugFP, "Found unexpected active ICS engine analyze \n");
\r
4849 ExitAnalyzeMode();
\r
4855 /* if enable, user want disable icsEngineAnalyze */
\r
4856 if (appData.icsEngineAnalyze) {
\r
4857 ExitAnalyzeMode();
\r
4861 appData.icsEngineAnalyze = TRUE;
\r
4862 if (appData.debugMode) fprintf(debugFP, "ICS engine analyze starting...\n");
\r
4865 if (!appData.showThinking) ToggleShowThinking();
\r
4866 AnalyzeModeEvent();
\r
4870 case IDM_AnalyzeFile:
\r
4871 if (!first.analysisSupport) {
\r
4872 char buf[MSG_SIZ];
\r
4873 sprintf(buf, _("%s does not support analysis"), first.tidy);
\r
4874 DisplayError(buf, 0);
\r
4876 if (!appData.showThinking) ToggleShowThinking();
\r
4877 AnalyzeFileEvent();
\r
4878 LoadGameDialog(hwnd, _("Analyze Game from File"));
\r
4879 AnalysisPeriodicEvent(1);
\r
4883 case IDM_IcsClient:
\r
4887 case IDM_EditGame:
\r
4892 case IDM_EditPosition:
\r
4893 EditPositionEvent();
\r
4894 SAY("enter a FEN string or setup a position on the board using the control R pop up menu");
\r
4897 case IDM_Training:
\r
4901 case IDM_ShowGameList:
\r
4902 ShowGameListProc();
\r
4905 case IDM_EditTags:
\r
4909 case IDM_EditComment:
\r
4910 if (commentUp && editComment) {
\r
4913 EditCommentEvent();
\r
4933 case IDM_CallFlag:
\r
4953 case IDM_StopObserving:
\r
4954 StopObservingEvent();
\r
4957 case IDM_StopExamining:
\r
4958 StopExaminingEvent();
\r
4962 UploadGameEvent();
\r
4965 case IDM_TypeInMove:
\r
4966 PopUpMoveDialog('\000');
\r
4969 case IDM_TypeInName:
\r
4970 PopUpNameDialog('\000');
\r
4973 case IDM_Backward:
\r
4975 SetFocus(hwndMain);
\r
4982 SetFocus(hwndMain);
\r
4987 SetFocus(hwndMain);
\r
4992 SetFocus(hwndMain);
\r
4996 RevertEvent(FALSE);
\r
4999 case IDM_Annotate: // [HGM] vari: revert with annotation
\r
5000 RevertEvent(TRUE);
\r
5003 case IDM_TruncateGame:
\r
5004 TruncateGameEvent();
\r
5011 case IDM_RetractMove:
\r
5012 RetractMoveEvent();
\r
5015 case IDM_FlipView:
\r
5016 flipView = !flipView;
\r
5017 DrawPosition(FALSE, NULL);
\r
5020 case IDM_FlipClock:
\r
5021 flipClock = !flipClock;
\r
5022 DisplayBothClocks();
\r
5023 DrawPosition(FALSE, NULL);
\r
5026 case IDM_MuteSounds:
\r
5027 mute = !mute; // [HGM] mute: keep track of global muting variable
\r
5028 CheckMenuItem(GetMenu(hwndMain),IDM_MuteSounds,
\r
5029 MF_BYCOMMAND|(mute?MF_CHECKED:MF_UNCHECKED));
\r
5032 case IDM_GeneralOptions:
\r
5033 GeneralOptionsPopup(hwnd);
\r
5034 DrawPosition(TRUE, NULL);
\r
5037 case IDM_BoardOptions:
\r
5038 BoardOptionsPopup(hwnd);
\r
5041 case IDM_EnginePlayOptions:
\r
5042 EnginePlayOptionsPopup(hwnd);
\r
5045 case IDM_Engine1Options:
\r
5046 EngineOptionsPopup(hwnd, &first);
\r
5049 case IDM_Engine2Options:
\r
5050 EngineOptionsPopup(hwnd, &second);
\r
5053 case IDM_OptionsUCI:
\r
5054 UciOptionsPopup(hwnd);
\r
5057 case IDM_IcsOptions:
\r
5058 IcsOptionsPopup(hwnd);
\r
5062 FontsOptionsPopup(hwnd);
\r
5066 SoundOptionsPopup(hwnd);
\r
5069 case IDM_CommPort:
\r
5070 CommPortOptionsPopup(hwnd);
\r
5073 case IDM_LoadOptions:
\r
5074 LoadOptionsPopup(hwnd);
\r
5077 case IDM_SaveOptions:
\r
5078 SaveOptionsPopup(hwnd);
\r
5081 case IDM_TimeControl:
\r
5082 TimeControlOptionsPopup(hwnd);
\r
5085 case IDM_SaveSettings:
\r
5086 SaveSettings(settingsFileName);
\r
5089 case IDM_SaveSettingsOnExit:
\r
5090 saveSettingsOnExit = !saveSettingsOnExit;
\r
5091 (void) CheckMenuItem(GetMenu(hwndMain), IDM_SaveSettingsOnExit,
\r
5092 MF_BYCOMMAND|(saveSettingsOnExit ?
\r
5093 MF_CHECKED : MF_UNCHECKED));
\r
5104 case IDM_AboutGame:
\r
5109 appData.debugMode = !appData.debugMode;
\r
5110 if (appData.debugMode) {
\r
5111 char dir[MSG_SIZ];
\r
5112 GetCurrentDirectory(MSG_SIZ, dir);
\r
5113 SetCurrentDirectory(installDir);
\r
5114 debugFP = fopen(appData.nameOfDebugFile, "w");
\r
5115 SetCurrentDirectory(dir);
\r
5116 setbuf(debugFP, NULL);
\r
5123 case IDM_HELPCONTENTS:
\r
5124 if (!MyHelp (hwnd, "winboard.hlp", HELP_KEY,(DWORD)(LPSTR)"CONTENTS") &&
\r
5125 !HtmlHelp(hwnd, "winboard.chm", 0, 0) ) {
\r
5126 MessageBox (GetFocus(),
\r
5127 _("Unable to activate help"),
\r
5128 szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);
\r
5132 case IDM_HELPSEARCH:
\r
5133 if (!MyHelp (hwnd, "winboard.hlp", HELP_PARTIALKEY, (DWORD)(LPSTR)"") &&
\r
5134 !HtmlHelp(hwnd, "winboard.chm", 0, 0) ) {
\r
5135 MessageBox (GetFocus(),
\r
5136 _("Unable to activate help"),
\r
5137 szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);
\r
5141 case IDM_HELPHELP:
\r
5142 if(!WinHelp(hwnd, (LPSTR)NULL, HELP_HELPONHELP, 0)) {
\r
5143 MessageBox (GetFocus(),
\r
5144 _("Unable to activate help"),
\r
5145 szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);
\r
5150 lpProc = MakeProcInstance((FARPROC)About, hInst);
\r
5152 (gameInfo.event && strcmp(gameInfo.event, "Easter Egg Hunt") == 0) ?
\r
5153 "AboutBox2" : "AboutBox", hwnd, (DLGPROC)lpProc);
\r
5154 FreeProcInstance(lpProc);
\r
5157 case IDM_DirectCommand1:
\r
5158 AskQuestionEvent(_("Direct Command"),
\r
5159 _("Send to chess program:"), "", "1");
\r
5161 case IDM_DirectCommand2:
\r
5162 AskQuestionEvent(_("Direct Command"),
\r
5163 _("Send to second chess program:"), "", "2");
\r
5166 case EP_WhitePawn:
\r
5167 EditPositionMenuEvent(WhitePawn, fromX, fromY);
\r
5168 fromX = fromY = -1;
\r
5171 case EP_WhiteKnight:
\r
5172 EditPositionMenuEvent(WhiteKnight, fromX, fromY);
\r
5173 fromX = fromY = -1;
\r
5176 case EP_WhiteBishop:
\r
5177 EditPositionMenuEvent(WhiteBishop, fromX, fromY);
\r
5178 fromX = fromY = -1;
\r
5181 case EP_WhiteRook:
\r
5182 EditPositionMenuEvent(WhiteRook, fromX, fromY);
\r
5183 fromX = fromY = -1;
\r
5186 case EP_WhiteQueen:
\r
5187 EditPositionMenuEvent(WhiteQueen, fromX, fromY);
\r
5188 fromX = fromY = -1;
\r
5191 case EP_WhiteFerz:
\r
5192 EditPositionMenuEvent(WhiteFerz, fromX, fromY);
\r
5193 fromX = fromY = -1;
\r
5196 case EP_WhiteWazir:
\r
5197 EditPositionMenuEvent(WhiteWazir, fromX, fromY);
\r
5198 fromX = fromY = -1;
\r
5201 case EP_WhiteAlfil:
\r
5202 EditPositionMenuEvent(WhiteAlfil, fromX, fromY);
\r
5203 fromX = fromY = -1;
\r
5206 case EP_WhiteCannon:
\r
5207 EditPositionMenuEvent(WhiteCannon, fromX, fromY);
\r
5208 fromX = fromY = -1;
\r
5211 case EP_WhiteCardinal:
\r
5212 EditPositionMenuEvent(WhiteAngel, fromX, fromY);
\r
5213 fromX = fromY = -1;
\r
5216 case EP_WhiteMarshall:
\r
5217 EditPositionMenuEvent(WhiteMarshall, fromX, fromY);
\r
5218 fromX = fromY = -1;
\r
5221 case EP_WhiteKing:
\r
5222 EditPositionMenuEvent(WhiteKing, fromX, fromY);
\r
5223 fromX = fromY = -1;
\r
5226 case EP_BlackPawn:
\r
5227 EditPositionMenuEvent(BlackPawn, fromX, fromY);
\r
5228 fromX = fromY = -1;
\r
5231 case EP_BlackKnight:
\r
5232 EditPositionMenuEvent(BlackKnight, fromX, fromY);
\r
5233 fromX = fromY = -1;
\r
5236 case EP_BlackBishop:
\r
5237 EditPositionMenuEvent(BlackBishop, fromX, fromY);
\r
5238 fromX = fromY = -1;
\r
5241 case EP_BlackRook:
\r
5242 EditPositionMenuEvent(BlackRook, fromX, fromY);
\r
5243 fromX = fromY = -1;
\r
5246 case EP_BlackQueen:
\r
5247 EditPositionMenuEvent(BlackQueen, fromX, fromY);
\r
5248 fromX = fromY = -1;
\r
5251 case EP_BlackFerz:
\r
5252 EditPositionMenuEvent(BlackFerz, fromX, fromY);
\r
5253 fromX = fromY = -1;
\r
5256 case EP_BlackWazir:
\r
5257 EditPositionMenuEvent(BlackWazir, fromX, fromY);
\r
5258 fromX = fromY = -1;
\r
5261 case EP_BlackAlfil:
\r
5262 EditPositionMenuEvent(BlackAlfil, fromX, fromY);
\r
5263 fromX = fromY = -1;
\r
5266 case EP_BlackCannon:
\r
5267 EditPositionMenuEvent(BlackCannon, fromX, fromY);
\r
5268 fromX = fromY = -1;
\r
5271 case EP_BlackCardinal:
\r
5272 EditPositionMenuEvent(BlackAngel, fromX, fromY);
\r
5273 fromX = fromY = -1;
\r
5276 case EP_BlackMarshall:
\r
5277 EditPositionMenuEvent(BlackMarshall, fromX, fromY);
\r
5278 fromX = fromY = -1;
\r
5281 case EP_BlackKing:
\r
5282 EditPositionMenuEvent(BlackKing, fromX, fromY);
\r
5283 fromX = fromY = -1;
\r
5286 case EP_EmptySquare:
\r
5287 EditPositionMenuEvent(EmptySquare, fromX, fromY);
\r
5288 fromX = fromY = -1;
\r
5291 case EP_ClearBoard:
\r
5292 EditPositionMenuEvent(ClearBoard, fromX, fromY);
\r
5293 fromX = fromY = -1;
\r
5297 EditPositionMenuEvent(WhitePlay, fromX, fromY);
\r
5298 fromX = fromY = -1;
\r
5302 EditPositionMenuEvent(BlackPlay, fromX, fromY);
\r
5303 fromX = fromY = -1;
\r
5307 EditPositionMenuEvent(PromotePiece, fromX, fromY);
\r
5308 fromX = fromY = -1;
\r
5312 EditPositionMenuEvent(DemotePiece, fromX, fromY);
\r
5313 fromX = fromY = -1;
\r
5317 DropMenuEvent(WhitePawn, fromX, fromY);
\r
5318 fromX = fromY = -1;
\r
5322 DropMenuEvent(WhiteKnight, fromX, fromY);
\r
5323 fromX = fromY = -1;
\r
5327 DropMenuEvent(WhiteBishop, fromX, fromY);
\r
5328 fromX = fromY = -1;
\r
5332 DropMenuEvent(WhiteRook, fromX, fromY);
\r
5333 fromX = fromY = -1;
\r
5337 DropMenuEvent(WhiteQueen, fromX, fromY);
\r
5338 fromX = fromY = -1;
\r
5343 TranslateMenus(0);
\r
5344 CheckMenuItem(GetMenu(hwndMain), lastChecked, MF_BYCOMMAND|MF_UNCHECKED);
\r
5345 CheckMenuItem(GetMenu(hwndMain), IDM_English, MF_BYCOMMAND|MF_CHECKED);
\r
5346 lastChecked = wmId;
\r
5350 if(wmId > IDM_English && wmId < IDM_English+5) {
\r
5351 LoadLanguageFile(languageFile[wmId - IDM_English - 1]);
\r
5352 TranslateMenus(0);
\r
5353 CheckMenuItem(GetMenu(hwndMain), lastChecked, MF_BYCOMMAND|MF_UNCHECKED);
\r
5354 CheckMenuItem(GetMenu(hwndMain), wmId, MF_BYCOMMAND|MF_CHECKED);
\r
5355 lastChecked = wmId;
\r
5358 return (DefWindowProc(hwnd, message, wParam, lParam));
\r
5364 case CLOCK_TIMER_ID:
\r
5365 KillTimer(hwnd, clockTimerEvent); /* Simulate one-shot timer as in X */
\r
5366 clockTimerEvent = 0;
\r
5367 DecrementClocks(); /* call into back end */
\r
5369 case LOAD_GAME_TIMER_ID:
\r
5370 KillTimer(hwnd, loadGameTimerEvent); /* Simulate one-shot timer as in X*/
\r
5371 loadGameTimerEvent = 0;
\r
5372 AutoPlayGameLoop(); /* call into back end */
\r
5374 case ANALYSIS_TIMER_ID:
\r
5375 if ((gameMode == AnalyzeMode || gameMode == AnalyzeFile
\r
5376 || appData.icsEngineAnalyze) && appData.periodicUpdates) {
\r
5377 AnalysisPeriodicEvent(0);
\r
5379 KillTimer(hwnd, analysisTimerEvent);
\r
5380 analysisTimerEvent = 0;
\r
5383 case DELAYED_TIMER_ID:
\r
5384 KillTimer(hwnd, delayedTimerEvent);
\r
5385 delayedTimerEvent = 0;
\r
5386 delayedTimerCallback();
\r
5391 case WM_USER_Input:
\r
5392 InputEvent(hwnd, message, wParam, lParam);
\r
5395 /* [AS] Also move "attached" child windows */
\r
5396 case WM_WINDOWPOSCHANGING:
\r
5398 if( hwnd == hwndMain && appData.useStickyWindows ) {
\r
5399 LPWINDOWPOS lpwp = (LPWINDOWPOS) lParam;
\r
5401 if( ((lpwp->flags & SWP_NOMOVE) == 0) && ((lpwp->flags & SWP_NOSIZE) != 0) ) {
\r
5402 /* Window is moving */
\r
5405 // GetWindowRect( hwnd, &rcMain ); //[HGM] sticky: in XP this returned new position, not old
\r
5406 rcMain.left = wpMain.x; // replace by these 4 lines to reconstruct old rect
\r
5407 rcMain.right = wpMain.x + wpMain.width;
\r
5408 rcMain.top = wpMain.y;
\r
5409 rcMain.bottom = wpMain.y + wpMain.height;
\r
5411 ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, moveHistoryDialog, &wpMoveHistory );
\r
5412 ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, evalGraphDialog, &wpEvalGraph );
\r
5413 ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, engineOutputDialog, &wpEngineOutput );
\r
5414 ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, gameListDialog, &wpGameList );
\r
5415 ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, hwndConsole, &wpConsole );
\r
5416 wpMain.x = lpwp->x;
\r
5417 wpMain.y = lpwp->y;
\r
5422 /* [AS] Snapping */
\r
5423 case WM_ENTERSIZEMOVE:
\r
5424 if(appData.debugMode) { fprintf(debugFP, "size-move\n"); }
\r
5425 if (hwnd == hwndMain) {
\r
5426 doingSizing = TRUE;
\r
5429 return OnEnterSizeMove( &sd, hwnd, wParam, lParam );
\r
5433 if(appData.debugMode) { fprintf(debugFP, "sizing\n"); }
\r
5434 if (hwnd == hwndMain) {
\r
5435 lastSizing = wParam;
\r
5440 if(appData.debugMode) { fprintf(debugFP, "moving\n"); }
\r
5441 return OnMoving( &sd, hwnd, wParam, lParam );
\r
5443 case WM_EXITSIZEMOVE:
\r
5444 if(appData.debugMode) { fprintf(debugFP, "exit size-move, size = %d\n", squareSize); }
\r
5445 if (hwnd == hwndMain) {
\r
5447 doingSizing = FALSE;
\r
5448 InvalidateRect(hwnd, &boardRect, FALSE);
\r
5449 GetClientRect(hwnd, &client);
\r
5450 ResizeBoard(client.right, client.bottom, lastSizing);
\r
5452 if(appData.debugMode) { fprintf(debugFP, "square size = %d\n", squareSize); }
\r
5454 return OnExitSizeMove( &sd, hwnd, wParam, lParam );
\r
5457 case WM_DESTROY: /* message: window being destroyed */
\r
5458 PostQuitMessage(0);
\r
5462 if (hwnd == hwndMain) {
\r
5467 default: /* Passes it on if unprocessed */
\r
5468 return (DefWindowProc(hwnd, message, wParam, lParam));
\r
5473 /*---------------------------------------------------------------------------*\
\r
5475 * Misc utility routines
\r
5477 \*---------------------------------------------------------------------------*/
\r
5480 * Decent random number generator, at least not as bad as Windows
\r
5481 * standard rand, which returns a value in the range 0 to 0x7fff.
\r
5483 unsigned int randstate;
\r
5488 randstate = randstate * 1664525 + 1013904223;
\r
5489 return (int) randstate & 0x7fffffff;
\r
5493 mysrandom(unsigned int seed)
\r
5500 * returns TRUE if user selects a different color, FALSE otherwise
\r
5504 ChangeColor(HWND hwnd, COLORREF *which)
\r
5506 static BOOL firstTime = TRUE;
\r
5507 static DWORD customColors[16];
\r
5509 COLORREF newcolor;
\r
5514 /* Make initial colors in use available as custom colors */
\r
5515 /* Should we put the compiled-in defaults here instead? */
\r
5517 customColors[i++] = lightSquareColor & 0xffffff;
\r
5518 customColors[i++] = darkSquareColor & 0xffffff;
\r
5519 customColors[i++] = whitePieceColor & 0xffffff;
\r
5520 customColors[i++] = blackPieceColor & 0xffffff;
\r
5521 customColors[i++] = highlightSquareColor & 0xffffff;
\r
5522 customColors[i++] = premoveHighlightColor & 0xffffff;
\r
5524 for (ccl = (ColorClass) 0; ccl < NColorClasses && i < 16; ccl++) {
\r
5525 customColors[i++] = textAttribs[ccl].color;
\r
5527 while (i < 16) customColors[i++] = RGB(255, 255, 255);
\r
5528 firstTime = FALSE;
\r
5531 cc.lStructSize = sizeof(cc);
\r
5532 cc.hwndOwner = hwnd;
\r
5533 cc.hInstance = NULL;
\r
5534 cc.rgbResult = (DWORD) (*which & 0xffffff);
\r
5535 cc.lpCustColors = (LPDWORD) customColors;
\r
5536 cc.Flags = CC_RGBINIT|CC_FULLOPEN;
\r
5538 if (!ChooseColor(&cc)) return FALSE;
\r
5540 newcolor = (COLORREF) (0x2000000 | cc.rgbResult);
\r
5541 if (newcolor == *which) return FALSE;
\r
5542 *which = newcolor;
\r
5546 InitDrawingColors();
\r
5547 InvalidateRect(hwnd, &boardRect, FALSE);
\r
5552 MyLoadSound(MySound *ms)
\r
5558 if (ms->data) free(ms->data);
\r
5561 switch (ms->name[0]) {
\r
5567 /* System sound from Control Panel. Don't preload here. */
\r
5571 if (ms->name[1] == NULLCHAR) {
\r
5572 /* "!" alone = silence */
\r
5575 /* Builtin wave resource. Error if not found. */
\r
5576 HANDLE h = FindResource(hInst, ms->name + 1, "WAVE");
\r
5577 if (h == NULL) break;
\r
5578 ms->data = (void *)LoadResource(hInst, h);
\r
5579 if (h == NULL) break;
\r
5584 /* .wav file. Error if not found. */
\r
5585 f = fopen(ms->name, "rb");
\r
5586 if (f == NULL) break;
\r
5587 if (fstat(fileno(f), &st) < 0) break;
\r
5588 ms->data = malloc(st.st_size);
\r
5589 if (fread(ms->data, st.st_size, 1, f) < 1) break;
\r
5595 char buf[MSG_SIZ];
\r
5596 sprintf(buf, _("Error loading sound %s"), ms->name);
\r
5597 DisplayError(buf, GetLastError());
\r
5603 MyPlaySound(MySound *ms)
\r
5605 BOOLEAN ok = FALSE;
\r
5607 if(mute) return TRUE; // [HGM] mute: suppress all sound play when muted
\r
5608 switch (ms->name[0]) {
\r
5610 if(appData.debugMode) fprintf(debugFP, "silence\n");
\r
5615 /* System sound from Control Panel (deprecated feature).
\r
5616 "$" alone or an unset sound name gets default beep (still in use). */
\r
5617 if (ms->name[1]) {
\r
5618 ok = PlaySound(ms->name + 1, NULL, SND_ALIAS|SND_ASYNC);
\r
5620 if (!ok) ok = MessageBeep(MB_OK);
\r
5623 /* Builtin wave resource, or "!" alone for silence */
\r
5624 if (ms->name[1]) {
\r
5625 if (ms->data == NULL) return FALSE;
\r
5626 ok = PlaySound(ms->data, NULL, SND_MEMORY|SND_ASYNC);
\r
5632 /* .wav file. Error if not found. */
\r
5633 if (ms->data == NULL) return FALSE;
\r
5634 ok = PlaySound(ms->data, NULL, SND_MEMORY|SND_ASYNC);
\r
5637 /* Don't print an error: this can happen innocently if the sound driver
\r
5638 is busy; for instance, if another instance of WinBoard is playing
\r
5639 a sound at about the same time. */
\r
5645 OldOpenFileHook(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
5648 OPENFILENAME *ofn;
\r
5649 static UINT *number; /* gross that this is static */
\r
5651 switch (message) {
\r
5652 case WM_INITDIALOG: /* message: initialize dialog box */
\r
5653 /* Center the dialog over the application window */
\r
5654 ofn = (OPENFILENAME *) lParam;
\r
5655 if (ofn->Flags & OFN_ENABLETEMPLATE) {
\r
5656 number = (UINT *) ofn->lCustData;
\r
5657 SendMessage(GetDlgItem(hDlg, edt2), WM_SETTEXT, 0, (LPARAM) "");
\r
5661 CenterWindow (hDlg, GetWindow (hDlg, GW_OWNER));
\r
5662 Translate(hDlg, 1536);
\r
5663 return FALSE; /* Allow for further processing */
\r
5666 if ((LOWORD(wParam) == IDOK) && (number != NULL)) {
\r
5667 *number = GetDlgItemInt(hDlg, OPT_IndexNumberOld, &ok, FALSE);
\r
5669 return FALSE; /* Allow for further processing */
\r
5675 OpenFileHook(HWND hdlg, UINT uiMsg, WPARAM wParam, LPARAM lParam)
\r
5677 static UINT *number;
\r
5678 OPENFILENAME *ofname;
\r
5681 case WM_INITDIALOG:
\r
5682 Translate(hdlg, DLG_IndexNumber);
\r
5683 ofname = (OPENFILENAME *)lParam;
\r
5684 number = (UINT *)(ofname->lCustData);
\r
5687 ofnot = (OFNOTIFY *)lParam;
\r
5688 if (ofnot->hdr.code == CDN_FILEOK) {
\r
5689 *number = GetDlgItemInt(hdlg, OPT_IndexNumber, NULL, FALSE);
\r
5698 OpenFileDialog(HWND hwnd, char *write, char *defName, char *defExt, // [HGM] diag: type of 'write' now string
\r
5699 char *nameFilt, char *dlgTitle, UINT *number,
\r
5700 char fileTitle[MSG_SIZ], char fileName[MSG_SIZ])
\r
5702 OPENFILENAME openFileName;
\r
5703 char buf1[MSG_SIZ];
\r
5706 if (fileName == NULL) fileName = buf1;
\r
5707 if (defName == NULL) {
\r
5708 strcpy(fileName, "*.");
\r
5709 strcat(fileName, defExt);
\r
5711 strcpy(fileName, defName);
\r
5713 if (fileTitle) strcpy(fileTitle, "");
\r
5714 if (number) *number = 0;
\r
5716 openFileName.lStructSize = sizeof(OPENFILENAME);
\r
5717 openFileName.hwndOwner = hwnd;
\r
5718 openFileName.hInstance = (HANDLE) hInst;
\r
5719 openFileName.lpstrFilter = nameFilt;
\r
5720 openFileName.lpstrCustomFilter = (LPSTR) NULL;
\r
5721 openFileName.nMaxCustFilter = 0L;
\r
5722 openFileName.nFilterIndex = 1L;
\r
5723 openFileName.lpstrFile = fileName;
\r
5724 openFileName.nMaxFile = MSG_SIZ;
\r
5725 openFileName.lpstrFileTitle = fileTitle;
\r
5726 openFileName.nMaxFileTitle = fileTitle ? MSG_SIZ : 0;
\r
5727 openFileName.lpstrInitialDir = NULL;
\r
5728 openFileName.lpstrTitle = dlgTitle;
\r
5729 openFileName.Flags = OFN_PATHMUSTEXIST | OFN_HIDEREADONLY
\r
5730 | (write[0] != 'r' ? 0 : OFN_FILEMUSTEXIST)
\r
5731 | (number ? OFN_ENABLETEMPLATE | OFN_ENABLEHOOK: 0)
\r
5732 | (oldDialog ? 0 : OFN_EXPLORER);
\r
5733 openFileName.nFileOffset = 0;
\r
5734 openFileName.nFileExtension = 0;
\r
5735 openFileName.lpstrDefExt = defExt;
\r
5736 openFileName.lCustData = (LONG) number;
\r
5737 openFileName.lpfnHook = oldDialog ?
\r
5738 (LPOFNHOOKPROC) OldOpenFileHook : (LPOFNHOOKPROC) OpenFileHook;
\r
5739 openFileName.lpTemplateName = (LPSTR)(oldDialog ? 1536 : DLG_IndexNumber);
\r
5741 if (write[0] != 'r' ? GetSaveFileName(&openFileName) :
\r
5742 GetOpenFileName(&openFileName)) {
\r
5743 /* open the file */
\r
5744 f = fopen(openFileName.lpstrFile, write);
\r
5746 MessageBox(hwnd, _("File open failed"), NULL,
\r
5747 MB_OK|MB_ICONEXCLAMATION);
\r
5751 int err = CommDlgExtendedError();
\r
5752 if (err != 0) DisplayError(_("Internal error in file dialog box"), err);
\r
5761 MenuPopup(HWND hwnd, POINT pt, HMENU hmenu, UINT def)
\r
5763 HMENU hmenuTrackPopup; /* floating pop-up menu */
\r
5766 * Get the first pop-up menu in the menu template. This is the
\r
5767 * menu that TrackPopupMenu displays.
\r
5769 hmenuTrackPopup = GetSubMenu(hmenu, 0);
\r
5771 SetMenuDefaultItem(hmenuTrackPopup, def, FALSE);
\r
5774 * TrackPopup uses screen coordinates, so convert the
\r
5775 * coordinates of the mouse click to screen coordinates.
\r
5777 ClientToScreen(hwnd, (LPPOINT) &pt);
\r
5779 /* Draw and track the floating pop-up menu. */
\r
5780 TrackPopupMenu(hmenuTrackPopup, TPM_CENTERALIGN | TPM_RIGHTBUTTON,
\r
5781 pt.x, pt.y, 0, hwnd, NULL);
\r
5783 /* Destroy the menu.*/
\r
5784 DestroyMenu(hmenu);
\r
5789 int sizeX, sizeY, newSizeX, newSizeY;
\r
5791 } ResizeEditPlusButtonsClosure;
\r
5794 ResizeEditPlusButtonsCallback(HWND hChild, LPARAM lparam)
\r
5796 ResizeEditPlusButtonsClosure *cl = (ResizeEditPlusButtonsClosure *)lparam;
\r
5800 if (hChild == cl->hText) return TRUE;
\r
5801 GetWindowRect(hChild, &rect); /* gives screen coords */
\r
5802 pt.x = rect.left + (cl->newSizeX - cl->sizeX)/2;
\r
5803 pt.y = rect.top + cl->newSizeY - cl->sizeY;
\r
5804 ScreenToClient(cl->hDlg, &pt);
\r
5805 cl->hdwp = DeferWindowPos(cl->hdwp, hChild, NULL,
\r
5806 pt.x, pt.y, rect.right - rect.left, rect.bottom - rect.top, SWP_NOZORDER);
\r
5810 /* Resize a dialog that has a (rich) edit field filling most of
\r
5811 the top, with a row of buttons below */
\r
5813 ResizeEditPlusButtons(HWND hDlg, HWND hText, int sizeX, int sizeY, int newSizeX, int newSizeY)
\r
5816 int newTextHeight, newTextWidth;
\r
5817 ResizeEditPlusButtonsClosure cl;
\r
5819 /*if (IsIconic(hDlg)) return;*/
\r
5820 if (newSizeX == sizeX && newSizeY == sizeY) return;
\r
5822 cl.hdwp = BeginDeferWindowPos(8);
\r
5824 GetWindowRect(hText, &rectText); /* gives screen coords */
\r
5825 newTextWidth = rectText.right - rectText.left + newSizeX - sizeX;
\r
5826 newTextHeight = rectText.bottom - rectText.top + newSizeY - sizeY;
\r
5827 if (newTextHeight < 0) {
\r
5828 newSizeY += -newTextHeight;
\r
5829 newTextHeight = 0;
\r
5831 cl.hdwp = DeferWindowPos(cl.hdwp, hText, NULL, 0, 0,
\r
5832 newTextWidth, newTextHeight, SWP_NOZORDER|SWP_NOMOVE);
\r
5838 cl.newSizeX = newSizeX;
\r
5839 cl.newSizeY = newSizeY;
\r
5840 EnumChildWindows(hDlg, ResizeEditPlusButtonsCallback, (LPARAM)&cl);
\r
5842 EndDeferWindowPos(cl.hdwp);
\r
5845 BOOL CenterWindowEx(HWND hwndChild, HWND hwndParent, int mode)
\r
5847 RECT rChild, rParent;
\r
5848 int wChild, hChild, wParent, hParent;
\r
5849 int wScreen, hScreen, xNew, yNew;
\r
5852 /* Get the Height and Width of the child window */
\r
5853 GetWindowRect (hwndChild, &rChild);
\r
5854 wChild = rChild.right - rChild.left;
\r
5855 hChild = rChild.bottom - rChild.top;
\r
5857 /* Get the Height and Width of the parent window */
\r
5858 GetWindowRect (hwndParent, &rParent);
\r
5859 wParent = rParent.right - rParent.left;
\r
5860 hParent = rParent.bottom - rParent.top;
\r
5862 /* Get the display limits */
\r
5863 hdc = GetDC (hwndChild);
\r
5864 wScreen = GetDeviceCaps (hdc, HORZRES);
\r
5865 hScreen = GetDeviceCaps (hdc, VERTRES);
\r
5866 ReleaseDC(hwndChild, hdc);
\r
5868 /* Calculate new X position, then adjust for screen */
\r
5869 xNew = rParent.left + ((wParent - wChild) /2);
\r
5872 } else if ((xNew+wChild) > wScreen) {
\r
5873 xNew = wScreen - wChild;
\r
5876 /* Calculate new Y position, then adjust for screen */
\r
5878 yNew = rParent.top + ((hParent - hChild) /2);
\r
5881 yNew = rParent.top + GetSystemMetrics( SM_CYCAPTION ) * 2 / 3;
\r
5886 } else if ((yNew+hChild) > hScreen) {
\r
5887 yNew = hScreen - hChild;
\r
5890 /* Set it, and return */
\r
5891 return SetWindowPos (hwndChild, NULL,
\r
5892 xNew, yNew, 0, 0, SWP_NOSIZE | SWP_NOZORDER);
\r
5895 /* Center one window over another */
\r
5896 BOOL CenterWindow (HWND hwndChild, HWND hwndParent)
\r
5898 return CenterWindowEx( hwndChild, hwndParent, 0 );
\r
5901 /*---------------------------------------------------------------------------*\
\r
5903 * Startup Dialog functions
\r
5905 \*---------------------------------------------------------------------------*/
\r
5907 InitComboStrings(HANDLE hwndCombo, char **cd)
\r
5909 SendMessage(hwndCombo, CB_RESETCONTENT, 0, 0);
\r
5911 while (*cd != NULL) {
\r
5912 SendMessage(hwndCombo, CB_ADDSTRING, 0, (LPARAM) T_(*cd));
\r
5918 InitComboStringsFromOption(HANDLE hwndCombo, char *str)
\r
5920 char buf1[MAX_ARG_LEN];
\r
5923 if (str[0] == '@') {
\r
5924 FILE* f = fopen(str + 1, "r");
\r
5926 DisplayFatalError(str + 1, errno, 2);
\r
5929 len = fread(buf1, 1, sizeof(buf1)-1, f);
\r
5931 buf1[len] = NULLCHAR;
\r
5935 SendMessage(hwndCombo, CB_RESETCONTENT, 0, 0);
\r
5938 char buf[MSG_SIZ];
\r
5939 char *end = strchr(str, '\n');
\r
5940 if (end == NULL) return;
\r
5941 memcpy(buf, str, end - str);
\r
5942 buf[end - str] = NULLCHAR;
\r
5943 SendMessage(hwndCombo, CB_ADDSTRING, 0, (LPARAM) buf);
\r
5949 SetStartupDialogEnables(HWND hDlg)
\r
5951 EnableWindow(GetDlgItem(hDlg, OPT_ChessEngineName),
\r
5952 IsDlgButtonChecked(hDlg, OPT_ChessEngine) ||
\r
5953 (appData.zippyPlay && IsDlgButtonChecked(hDlg, OPT_ChessServer)));
\r
5954 EnableWindow(GetDlgItem(hDlg, OPT_SecondChessEngineName),
\r
5955 IsDlgButtonChecked(hDlg, OPT_ChessEngine));
\r
5956 EnableWindow(GetDlgItem(hDlg, OPT_ChessServerName),
\r
5957 IsDlgButtonChecked(hDlg, OPT_ChessServer));
\r
5958 EnableWindow(GetDlgItem(hDlg, OPT_AdditionalOptions),
\r
5959 IsDlgButtonChecked(hDlg, OPT_AnyAdditional));
\r
5960 EnableWindow(GetDlgItem(hDlg, IDOK),
\r
5961 IsDlgButtonChecked(hDlg, OPT_ChessEngine) ||
\r
5962 IsDlgButtonChecked(hDlg, OPT_ChessServer) ||
\r
5963 IsDlgButtonChecked(hDlg, OPT_View));
\r
5967 QuoteForFilename(char *filename)
\r
5969 int dquote, space;
\r
5970 dquote = strchr(filename, '"') != NULL;
\r
5971 space = strchr(filename, ' ') != NULL;
\r
5972 if (dquote || space) {
\r
5984 InitEngineBox(HWND hDlg, HWND hwndCombo, char* nthcp, char* nthd, char* nthdir, char *nthnames)
\r
5986 char buf[MSG_SIZ];
\r
5989 InitComboStringsFromOption(hwndCombo, nthnames);
\r
5990 q = QuoteForFilename(nthcp);
\r
5991 sprintf(buf, "%s%s%s", q, nthcp, q);
\r
5992 if (*nthdir != NULLCHAR) {
\r
5993 q = QuoteForFilename(nthdir);
\r
5994 sprintf(buf + strlen(buf), " /%s=%s%s%s", nthd, q, nthdir, q);
\r
5996 if (*nthcp == NULLCHAR) {
\r
5997 SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) 0, (LPARAM) 0);
\r
5998 } else if (SendMessage(hwndCombo, CB_SELECTSTRING, (WPARAM) -1, (LPARAM) buf) == CB_ERR) {
\r
5999 SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) -1, (LPARAM) 0);
\r
6000 SendMessage(hwndCombo, WM_SETTEXT, (WPARAM) 0, (LPARAM) buf);
\r
6005 StartupDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
6007 char buf[MSG_SIZ];
\r
6011 switch (message) {
\r
6012 case WM_INITDIALOG:
\r
6013 /* Center the dialog */
\r
6014 CenterWindow (hDlg, GetDesktopWindow());
\r
6015 Translate(hDlg, DLG_Startup);
\r
6016 /* Initialize the dialog items */
\r
6017 InitEngineBox(hDlg, GetDlgItem(hDlg, OPT_ChessEngineName),
\r
6018 appData.firstChessProgram, "fd", appData.firstDirectory,
\r
6019 firstChessProgramNames);
\r
6020 InitEngineBox(hDlg, GetDlgItem(hDlg, OPT_SecondChessEngineName),
\r
6021 appData.secondChessProgram, "sd", appData.secondDirectory,
\r
6022 secondChessProgramNames);
\r
6023 hwndCombo = GetDlgItem(hDlg, OPT_ChessServerName);
\r
6024 InitComboStringsFromOption(hwndCombo, icsNames);
\r
6025 sprintf(buf, "%s /icsport=%s", appData.icsHost, appData.icsPort);
\r
6026 if (*appData.icsHelper != NULLCHAR) {
\r
6027 char *q = QuoteForFilename(appData.icsHelper);
\r
6028 sprintf(buf + strlen(buf), " /icshelper=%s%s%s", q, appData.icsHelper, q);
\r
6030 if (*appData.icsHost == NULLCHAR) {
\r
6031 SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) 0, (LPARAM) 0);
\r
6032 /*SendMessage(hwndCombo, CB_SHOWDROPDOWN, (WPARAM) TRUE, (LPARAM) 0); !!too soon */
\r
6033 } else if (SendMessage(hwndCombo, CB_SELECTSTRING, (WPARAM) -1, (LPARAM) buf) == CB_ERR) {
\r
6034 SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) -1, (LPARAM) 0);
\r
6035 SendMessage(hwndCombo, WM_SETTEXT, (WPARAM) 0, (LPARAM) buf);
\r
6038 if (appData.icsActive) {
\r
6039 CheckDlgButton(hDlg, OPT_ChessServer, BST_CHECKED);
\r
6041 else if (appData.noChessProgram) {
\r
6042 CheckDlgButton(hDlg, OPT_View, BST_CHECKED);
\r
6045 CheckDlgButton(hDlg, OPT_ChessEngine, BST_CHECKED);
\r
6048 SetStartupDialogEnables(hDlg);
\r
6052 switch (LOWORD(wParam)) {
\r
6054 if (IsDlgButtonChecked(hDlg, OPT_ChessEngine)) {
\r
6055 strcpy(buf, "/fcp=");
\r
6056 GetDlgItemText(hDlg, OPT_ChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));
\r
6058 ParseArgs(StringGet, &p);
\r
6059 strcpy(buf, "/scp=");
\r
6060 GetDlgItemText(hDlg, OPT_SecondChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));
\r
6062 ParseArgs(StringGet, &p);
\r
6063 appData.noChessProgram = FALSE;
\r
6064 appData.icsActive = FALSE;
\r
6065 } else if (IsDlgButtonChecked(hDlg, OPT_ChessServer)) {
\r
6066 strcpy(buf, "/ics /icshost=");
\r
6067 GetDlgItemText(hDlg, OPT_ChessServerName, buf + strlen(buf), sizeof(buf) - strlen(buf));
\r
6069 ParseArgs(StringGet, &p);
\r
6070 if (appData.zippyPlay) {
\r
6071 strcpy(buf, "/fcp=");
\r
6072 GetDlgItemText(hDlg, OPT_ChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));
\r
6074 ParseArgs(StringGet, &p);
\r
6076 } else if (IsDlgButtonChecked(hDlg, OPT_View)) {
\r
6077 appData.noChessProgram = TRUE;
\r
6078 appData.icsActive = FALSE;
\r
6080 MessageBox(hDlg, _("Choose an option, or cancel to exit"),
\r
6081 _("Option Error"), MB_OK|MB_ICONEXCLAMATION);
\r
6084 if (IsDlgButtonChecked(hDlg, OPT_AnyAdditional)) {
\r
6085 GetDlgItemText(hDlg, OPT_AdditionalOptions, buf, sizeof(buf));
\r
6087 ParseArgs(StringGet, &p);
\r
6089 EndDialog(hDlg, TRUE);
\r
6096 case IDM_HELPCONTENTS:
\r
6097 if (!WinHelp (hDlg, "winboard.hlp", HELP_KEY,(DWORD)(LPSTR)"CONTENTS")) {
\r
6098 MessageBox (GetFocus(),
\r
6099 _("Unable to activate help"),
\r
6100 szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);
\r
6105 SetStartupDialogEnables(hDlg);
\r
6113 /*---------------------------------------------------------------------------*\
\r
6115 * About box dialog functions
\r
6117 \*---------------------------------------------------------------------------*/
\r
6119 /* Process messages for "About" dialog box */
\r
6121 About(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
6123 switch (message) {
\r
6124 case WM_INITDIALOG: /* message: initialize dialog box */
\r
6125 /* Center the dialog over the application window */
\r
6126 CenterWindow (hDlg, GetWindow (hDlg, GW_OWNER));
\r
6127 SetDlgItemText(hDlg, ABOUTBOX_Version, programVersion);
\r
6128 Translate(hDlg, ABOUTBOX);
\r
6132 case WM_COMMAND: /* message: received a command */
\r
6133 if (LOWORD(wParam) == IDOK /* "OK" box selected? */
\r
6134 || LOWORD(wParam) == IDCANCEL) { /* System menu close command? */
\r
6135 EndDialog(hDlg, TRUE); /* Exit the dialog */
\r
6143 /*---------------------------------------------------------------------------*\
\r
6145 * Comment Dialog functions
\r
6147 \*---------------------------------------------------------------------------*/
\r
6150 CommentDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
6152 static HANDLE hwndText = NULL;
\r
6153 int len, newSizeX, newSizeY, flags;
\r
6154 static int sizeX, sizeY;
\r
6159 switch (message) {
\r
6160 case WM_INITDIALOG: /* message: initialize dialog box */
\r
6161 /* Initialize the dialog items */
\r
6162 Translate(hDlg, DLG_EditComment);
\r
6163 hwndText = GetDlgItem(hDlg, OPT_CommentText);
\r
6164 SetDlgItemText(hDlg, OPT_CommentText, commentText);
\r
6165 EnableWindow(GetDlgItem(hDlg, OPT_CancelComment), editComment);
\r
6166 EnableWindow(GetDlgItem(hDlg, OPT_ClearComment), editComment);
\r
6167 EnableWindow(GetDlgItem(hDlg, OPT_EditComment), !editComment);
\r
6168 SendMessage(hwndText, EM_SETREADONLY, !editComment, 0);
\r
6169 SetWindowText(hDlg, commentTitle);
\r
6170 if (editComment) {
\r
6171 SetFocus(hwndText);
\r
6173 SetFocus(GetDlgItem(hDlg, IDOK));
\r
6175 SendMessage(GetDlgItem(hDlg, OPT_CommentText),
\r
6176 WM_SETFONT, (WPARAM)font[boardSize][COMMENT_FONT]->hf,
\r
6177 MAKELPARAM(FALSE, 0));
\r
6178 /* Size and position the dialog */
\r
6179 if (!commentDialog) {
\r
6180 commentDialog = hDlg;
\r
6181 flags = SWP_NOZORDER;
\r
6182 GetClientRect(hDlg, &rect);
\r
6183 sizeX = rect.right;
\r
6184 sizeY = rect.bottom;
\r
6185 if (wpComment.x != CW_USEDEFAULT && wpComment.y != CW_USEDEFAULT &&
\r
6186 wpComment.width != CW_USEDEFAULT && wpComment.height != CW_USEDEFAULT) {
\r
6187 WINDOWPLACEMENT wp;
\r
6188 EnsureOnScreen(&wpComment.x, &wpComment.y, 0, 0);
\r
6189 wp.length = sizeof(WINDOWPLACEMENT);
\r
6191 wp.showCmd = SW_SHOW;
\r
6192 wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;
\r
6193 wp.rcNormalPosition.left = wpComment.x;
\r
6194 wp.rcNormalPosition.right = wpComment.x + wpComment.width;
\r
6195 wp.rcNormalPosition.top = wpComment.y;
\r
6196 wp.rcNormalPosition.bottom = wpComment.y + wpComment.height;
\r
6197 SetWindowPlacement(hDlg, &wp);
\r
6199 GetClientRect(hDlg, &rect);
\r
6200 newSizeX = rect.right;
\r
6201 newSizeY = rect.bottom;
\r
6202 ResizeEditPlusButtons(hDlg, hwndText, sizeX, sizeY,
\r
6203 newSizeX, newSizeY);
\r
6208 SendDlgItemMessage( hDlg, OPT_CommentText, EM_SETEVENTMASK, 0, ENM_MOUSEEVENTS );
\r
6211 case WM_COMMAND: /* message: received a command */
\r
6212 switch (LOWORD(wParam)) {
\r
6214 if (editComment) {
\r
6216 /* Read changed options from the dialog box */
\r
6217 hwndText = GetDlgItem(hDlg, OPT_CommentText);
\r
6218 len = GetWindowTextLength(hwndText);
\r
6219 str = (char *) malloc(len + 1);
\r
6220 GetWindowText(hwndText, str, len + 1);
\r
6229 ReplaceComment(commentIndex, str);
\r
6236 case OPT_CancelComment:
\r
6240 case OPT_ClearComment:
\r
6241 SetDlgItemText(hDlg, OPT_CommentText, "");
\r
6244 case OPT_EditComment:
\r
6245 EditCommentEvent();
\r
6253 case WM_NOTIFY: // [HGM] vari: cloned from whistory.c
\r
6254 if( wParam == OPT_CommentText ) {
\r
6255 MSGFILTER * lpMF = (MSGFILTER *) lParam;
\r
6257 if( lpMF->msg == WM_RBUTTONDOWN && (lpMF->wParam & (MK_CONTROL | MK_SHIFT)) == 0 ) {
\r
6261 pt.x = LOWORD( lpMF->lParam );
\r
6262 pt.y = HIWORD( lpMF->lParam );
\r
6264 index = SendDlgItemMessage( hDlg, OPT_CommentText, EM_CHARFROMPOS, 0, (LPARAM) &pt );
\r
6266 hwndText = GetDlgItem(hDlg, OPT_CommentText); // cloned from above
\r
6267 len = GetWindowTextLength(hwndText);
\r
6268 str = (char *) malloc(len + 1);
\r
6269 GetWindowText(hwndText, str, len + 1);
\r
6270 ReplaceComment(commentIndex, str);
\r
6271 if(commentIndex != currentMove) ToNrEvent(commentIndex);
\r
6272 LoadVariation( index, str ); // [HGM] also does the actual moving to it, now
\r
6275 /* Zap the message for good: apparently, returning non-zero is not enough */
\r
6276 lpMF->msg = WM_USER;
\r
6284 newSizeX = LOWORD(lParam);
\r
6285 newSizeY = HIWORD(lParam);
\r
6286 ResizeEditPlusButtons(hDlg, hwndText, sizeX, sizeY, newSizeX, newSizeY);
\r
6291 case WM_GETMINMAXINFO:
\r
6292 /* Prevent resizing window too small */
\r
6293 mmi = (MINMAXINFO *) lParam;
\r
6294 mmi->ptMinTrackSize.x = 100;
\r
6295 mmi->ptMinTrackSize.y = 100;
\r
6302 EitherCommentPopUp(int index, char *title, char *str, BOOLEAN edit)
\r
6307 CheckMenuItem(GetMenu(hwndMain), IDM_EditComment, edit ? MF_CHECKED : MF_UNCHECKED);
\r
6309 if (str == NULL) str = "";
\r
6310 p = (char *) malloc(2 * strlen(str) + 2);
\r
6313 if (*str == '\n') *q++ = '\r';
\r
6317 if (commentText != NULL) free(commentText);
\r
6319 commentIndex = index;
\r
6320 commentTitle = title;
\r
6322 editComment = edit;
\r
6324 if (commentDialog) {
\r
6325 SendMessage(commentDialog, WM_INITDIALOG, 0, 0);
\r
6326 if (!commentUp) ShowWindow(commentDialog, SW_SHOW);
\r
6328 lpProc = MakeProcInstance((FARPROC)CommentDialog, hInst);
\r
6329 CreateDialog(hInst, MAKEINTRESOURCE(DLG_EditComment),
\r
6330 hwndMain, (DLGPROC)lpProc);
\r
6331 FreeProcInstance(lpProc);
\r
6337 /*---------------------------------------------------------------------------*\
\r
6339 * Type-in move dialog functions
\r
6341 \*---------------------------------------------------------------------------*/
\r
6344 TypeInMoveDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
6346 char move[MSG_SIZ];
\r
6348 ChessMove moveType;
\r
6349 int fromX, fromY, toX, toY;
\r
6352 switch (message) {
\r
6353 case WM_INITDIALOG:
\r
6354 move[0] = (char) lParam;
\r
6355 move[1] = NULLCHAR;
\r
6356 CenterWindowEx(hDlg, GetWindow(hDlg, GW_OWNER), 1 );
\r
6357 Translate(hDlg, DLG_TypeInMove);
\r
6358 hInput = GetDlgItem(hDlg, OPT_Move);
\r
6359 SetWindowText(hInput, move);
\r
6361 SendMessage(hInput, EM_SETSEL, (WPARAM)9999, (LPARAM)9999);
\r
6365 switch (LOWORD(wParam)) {
\r
6367 GetDlgItemText(hDlg, OPT_Move, move, sizeof(move));
\r
6368 { int n; Board board;
\r
6370 if(gameMode == EditPosition && ParseFEN(board, &n, move) ) {
\r
6371 EditPositionPasteFEN(move);
\r
6372 EndDialog(hDlg, TRUE);
\r
6375 // [HGM] movenum: allow move number to be typed in any mode
\r
6376 if(sscanf(move, "%d", &n) == 1 && n != 0 ) {
\r
6378 EndDialog(hDlg, TRUE);
\r
6382 if (gameMode != EditGame && currentMove != forwardMostMove &&
\r
6383 gameMode != Training) {
\r
6384 DisplayMoveError(_("Displayed move is not current"));
\r
6386 // GetDlgItemText(hDlg, OPT_Move, move, sizeof(move)); // moved upstream
\r
6387 int ok = ParseOneMove(move, gameMode == EditPosition ? blackPlaysFirst : currentMove,
\r
6388 &moveType, &fromX, &fromY, &toX, &toY, &promoChar);
\r
6389 if(!ok && move[0] >= 'a') { move[0] += 'A' - 'a'; ok = 2; } // [HGM] try also capitalized
\r
6390 if (ok==1 || ok && ParseOneMove(move, gameMode == EditPosition ? blackPlaysFirst : currentMove,
\r
6391 &moveType, &fromX, &fromY, &toX, &toY, &promoChar)) {
\r
6392 if (gameMode != Training)
\r
6393 forwardMostMove = currentMove;
\r
6394 UserMoveEvent(fromX, fromY, toX, toY, promoChar);
\r
6396 DisplayMoveError(_("Could not parse move"));
\r
6399 EndDialog(hDlg, TRUE);
\r
6402 EndDialog(hDlg, FALSE);
\r
6413 PopUpMoveDialog(char firstchar)
\r
6417 if ((gameMode == BeginningOfGame && !appData.icsActive) ||
\r
6418 gameMode == MachinePlaysWhite || gameMode == MachinePlaysBlack ||
\r
6419 gameMode == AnalyzeMode || gameMode == EditGame ||
\r
6420 gameMode == EditPosition || gameMode == IcsExamining ||
\r
6421 gameMode == IcsPlayingWhite || gameMode == IcsPlayingBlack ||
\r
6422 isdigit(firstchar) && // [HGM] movenum: allow typing in of move nr in 'passive' modes
\r
6423 ( gameMode == AnalyzeFile || gameMode == PlayFromGameFile ||
\r
6424 gameMode == IcsObserving || gameMode == TwoMachinesPlay ) ||
\r
6425 gameMode == Training) {
\r
6426 lpProc = MakeProcInstance((FARPROC)TypeInMoveDialog, hInst);
\r
6427 DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_TypeInMove),
\r
6428 hwndMain, (DLGPROC)lpProc, (LPARAM)firstchar);
\r
6429 FreeProcInstance(lpProc);
\r
6433 /*---------------------------------------------------------------------------*\
\r
6435 * Type-in name dialog functions
\r
6437 \*---------------------------------------------------------------------------*/
\r
6440 TypeInNameDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
6442 char move[MSG_SIZ];
\r
6445 switch (message) {
\r
6446 case WM_INITDIALOG:
\r
6447 move[0] = (char) lParam;
\r
6448 move[1] = NULLCHAR;
\r
6449 CenterWindowEx(hDlg, GetWindow(hDlg, GW_OWNER), 1 );
\r
6450 Translate(hDlg, DLG_TypeInName);
\r
6451 hInput = GetDlgItem(hDlg, OPT_Name);
\r
6452 SetWindowText(hInput, move);
\r
6454 SendMessage(hInput, EM_SETSEL, (WPARAM)9999, (LPARAM)9999);
\r
6458 switch (LOWORD(wParam)) {
\r
6460 GetDlgItemText(hDlg, OPT_Name, move, sizeof(move));
\r
6461 appData.userName = strdup(move);
\r
6464 if(gameMode == MachinePlaysWhite || gameMode == MachinePlaysBlack) {
\r
6465 sprintf(move, "%s vs. %s", gameInfo.white, gameInfo.black);
\r
6466 DisplayTitle(move);
\r
6470 EndDialog(hDlg, TRUE);
\r
6473 EndDialog(hDlg, FALSE);
\r
6484 PopUpNameDialog(char firstchar)
\r
6488 lpProc = MakeProcInstance((FARPROC)TypeInNameDialog, hInst);
\r
6489 DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_TypeInName),
\r
6490 hwndMain, (DLGPROC)lpProc, (LPARAM)firstchar);
\r
6491 FreeProcInstance(lpProc);
\r
6494 /*---------------------------------------------------------------------------*\
\r
6498 \*---------------------------------------------------------------------------*/
\r
6500 /* Nonmodal error box */
\r
6501 LRESULT CALLBACK ErrorDialog(HWND hDlg, UINT message,
\r
6502 WPARAM wParam, LPARAM lParam);
\r
6505 ErrorPopUp(char *title, char *content)
\r
6509 BOOLEAN modal = hwndMain == NULL;
\r
6527 strncpy(errorTitle, title, sizeof(errorTitle));
\r
6528 errorTitle[sizeof(errorTitle) - 1] = '\0';
\r
6531 MessageBox(NULL, errorMessage, errorTitle, MB_OK|MB_ICONEXCLAMATION);
\r
6533 lpProc = MakeProcInstance((FARPROC)ErrorDialog, hInst);
\r
6534 CreateDialog(hInst, MAKEINTRESOURCE(DLG_Error),
\r
6535 hwndMain, (DLGPROC)lpProc);
\r
6536 FreeProcInstance(lpProc);
\r
6543 if (!appData.popupMoveErrors && moveErrorMessageUp) DisplayMessage("", "");
\r
6544 if (errorDialog == NULL) return;
\r
6545 DestroyWindow(errorDialog);
\r
6546 errorDialog = NULL;
\r
6547 if(errorExitStatus) ExitEvent(errorExitStatus);
\r
6551 ErrorDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
6556 switch (message) {
\r
6557 case WM_INITDIALOG:
\r
6558 GetWindowRect(hDlg, &rChild);
\r
6561 SetWindowPos(hDlg, NULL, rChild.left,
\r
6562 rChild.top + boardRect.top - (rChild.bottom - rChild.top),
\r
6563 0, 0, SWP_NOZORDER|SWP_NOSIZE);
\r
6567 [AS] It seems that the above code wants to move the dialog up in the "caption
\r
6568 area" of the main window, but it uses the dialog height as an hard-coded constant,
\r
6569 and it doesn't work when you resize the dialog.
\r
6570 For now, just give it a default position.
\r
6572 SetWindowPos(hDlg, NULL, boardRect.left+8, boardRect.top+8, 0, 0, SWP_NOZORDER|SWP_NOSIZE);
\r
6573 Translate(hDlg, DLG_Error);
\r
6575 errorDialog = hDlg;
\r
6576 SetWindowText(hDlg, errorTitle);
\r
6577 hwndText = GetDlgItem(hDlg, OPT_ErrorText);
\r
6578 SetDlgItemText(hDlg, OPT_ErrorText, errorMessage);
\r
6582 switch (LOWORD(wParam)) {
\r
6585 if (errorDialog == hDlg) errorDialog = NULL;
\r
6586 DestroyWindow(hDlg);
\r
6598 HWND gothicDialog = NULL;
\r
6601 GothicDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
6605 int height = GetSystemMetrics(SM_CYCAPTION)+GetSystemMetrics(SM_CYFRAME);
\r
6607 switch (message) {
\r
6608 case WM_INITDIALOG:
\r
6609 GetWindowRect(hDlg, &rChild);
\r
6611 SetWindowPos(hDlg, NULL, wpMain.x, wpMain.y-height, wpMain.width, height,
\r
6615 [AS] It seems that the above code wants to move the dialog up in the "caption
\r
6616 area" of the main window, but it uses the dialog height as an hard-coded constant,
\r
6617 and it doesn't work when you resize the dialog.
\r
6618 For now, just give it a default position.
\r
6620 gothicDialog = hDlg;
\r
6621 SetWindowText(hDlg, errorTitle);
\r
6622 hwndText = GetDlgItem(hDlg, OPT_ErrorText);
\r
6623 SetDlgItemText(hDlg, OPT_ErrorText, errorMessage);
\r
6627 switch (LOWORD(wParam)) {
\r
6630 if (errorDialog == hDlg) errorDialog = NULL;
\r
6631 DestroyWindow(hDlg);
\r
6643 GothicPopUp(char *title, VariantClass variant)
\r
6646 static char *lastTitle;
\r
6648 strncpy(errorTitle, title, sizeof(errorTitle));
\r
6649 errorTitle[sizeof(errorTitle) - 1] = '\0';
\r
6651 if(lastTitle != title && gothicDialog != NULL) {
\r
6652 DestroyWindow(gothicDialog);
\r
6653 gothicDialog = NULL;
\r
6655 if(variant != VariantNormal && gothicDialog == NULL) {
\r
6656 title = lastTitle;
\r
6657 lpProc = MakeProcInstance((FARPROC)GothicDialog, hInst);
\r
6658 CreateDialog(hInst, MAKEINTRESOURCE(DLG_Error),
\r
6659 hwndMain, (DLGPROC)lpProc);
\r
6660 FreeProcInstance(lpProc);
\r
6665 /*---------------------------------------------------------------------------*\
\r
6667 * Ics Interaction console functions
\r
6669 \*---------------------------------------------------------------------------*/
\r
6671 #define HISTORY_SIZE 64
\r
6672 static char *history[HISTORY_SIZE];
\r
6673 int histIn = 0, histP = 0;
\r
6676 SaveInHistory(char *cmd)
\r
6678 if (history[histIn] != NULL) {
\r
6679 free(history[histIn]);
\r
6680 history[histIn] = NULL;
\r
6682 if (*cmd == NULLCHAR) return;
\r
6683 history[histIn] = StrSave(cmd);
\r
6684 histIn = (histIn + 1) % HISTORY_SIZE;
\r
6685 if (history[histIn] != NULL) {
\r
6686 free(history[histIn]);
\r
6687 history[histIn] = NULL;
\r
6693 PrevInHistory(char *cmd)
\r
6696 if (histP == histIn) {
\r
6697 if (history[histIn] != NULL) free(history[histIn]);
\r
6698 history[histIn] = StrSave(cmd);
\r
6700 newhp = (histP - 1 + HISTORY_SIZE) % HISTORY_SIZE;
\r
6701 if (newhp == histIn || history[newhp] == NULL) return NULL;
\r
6703 return history[histP];
\r
6709 if (histP == histIn) return NULL;
\r
6710 histP = (histP + 1) % HISTORY_SIZE;
\r
6711 return history[histP];
\r
6715 LoadIcsTextMenu(IcsTextMenuEntry *e)
\r
6719 hmenu = LoadMenu(hInst, "TextMenu");
\r
6720 h = GetSubMenu(hmenu, 0);
\r
6722 if (strcmp(e->item, "-") == 0) {
\r
6723 AppendMenu(h, MF_SEPARATOR, 0, 0);
\r
6724 } else { // [HGM] re-written a bit to use only one AppendMenu call for both cases (| or no |)
\r
6725 int flags = MF_STRING, j = 0;
\r
6726 if (e->item[0] == '|') {
\r
6727 flags |= MF_MENUBARBREAK;
\r
6730 if(!strcmp(e->command, "none")) flags |= MF_GRAYED; // [HGM] chatclick: provide inactive dummy
\r
6731 AppendMenu(h, flags, IDM_CommandX + i, e->item + j);
\r
6739 WNDPROC consoleTextWindowProc;
\r
6742 CommandX(HWND hwnd, char *command, BOOLEAN getname, BOOLEAN immediate)
\r
6744 char buf[MSG_SIZ], name[MSG_SIZ];
\r
6745 HWND hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
6749 SetWindowText(hInput, command);
\r
6751 SendMessage(hInput, WM_CHAR, '\r', 0);
\r
6753 sel.cpMin = 999999;
\r
6754 sel.cpMax = 999999;
\r
6755 SendMessage(hInput, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
6760 SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);
\r
6761 if (sel.cpMin == sel.cpMax) {
\r
6762 /* Expand to surrounding word */
\r
6765 tr.chrg.cpMax = sel.cpMin;
\r
6766 tr.chrg.cpMin = --sel.cpMin;
\r
6767 if (sel.cpMin < 0) break;
\r
6768 tr.lpstrText = name;
\r
6769 SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr);
\r
6770 } while (isalpha(name[0]) || isdigit(name[0]) || name[0] == '-');
\r
6774 tr.chrg.cpMin = sel.cpMax;
\r
6775 tr.chrg.cpMax = ++sel.cpMax;
\r
6776 tr.lpstrText = name;
\r
6777 if (SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr) < 1) break;
\r
6778 } while (isalpha(name[0]) || isdigit(name[0]) || name[0] == '-');
\r
6781 if (sel.cpMax == sel.cpMin || sel.cpMax - sel.cpMin > MSG_SIZ/2) {
\r
6782 MessageBeep(MB_ICONEXCLAMATION);
\r
6786 tr.lpstrText = name;
\r
6787 SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr);
\r
6789 if (sel.cpMax - sel.cpMin > MSG_SIZ/2) {
\r
6790 MessageBeep(MB_ICONEXCLAMATION);
\r
6793 SendMessage(hwnd, EM_GETSELTEXT, 0, (LPARAM) name);
\r
6796 sprintf(buf, "%s %s", command, name);
\r
6797 SetWindowText(hInput, buf);
\r
6798 SendMessage(hInput, WM_CHAR, '\r', 0);
\r
6800 if(!strcmp(command, "chat")) { ChatPopUp(name); return; }
\r
6801 sprintf(buf, "%s %s ", command, name); /* trailing space */
\r
6802 SetWindowText(hInput, buf);
\r
6803 sel.cpMin = 999999;
\r
6804 sel.cpMax = 999999;
\r
6805 SendMessage(hInput, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
6811 ConsoleTextSubclass(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
\r
6816 switch (message) {
\r
6818 if (!(GetKeyState(VK_CONTROL) & ~1)) break;
\r
6821 SendMessage(hwnd, EM_LINESCROLL, 0, -999999);
\r
6824 sel.cpMin = 999999;
\r
6825 sel.cpMax = 999999;
\r
6826 SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
6827 SendMessage(hwnd, EM_SCROLLCARET, 0, 0);
\r
6832 if(wParam != '\022') {
\r
6833 if (wParam == '\t') {
\r
6834 if (GetKeyState(VK_SHIFT) < 0) {
\r
6836 if (IsIconic(hwndMain)) ShowWindow(hwndMain, SW_RESTORE);
\r
6837 if (buttonDesc[0].hwnd) {
\r
6838 SetFocus(buttonDesc[0].hwnd);
\r
6840 SetFocus(hwndMain);
\r
6844 SetFocus(GetDlgItem(hwndConsole, OPT_ConsoleInput));
\r
6847 hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
6848 JAWS_DELETE( SetFocus(hInput); )
\r
6849 SendMessage(hInput, message, wParam, lParam);
\r
6852 } // [HGM] navigate: for Ctrl+R, flow into nex case (moved up here) to summon up menu
\r
6853 case WM_RBUTTONDOWN:
\r
6854 if (!(GetKeyState(VK_SHIFT) & ~1)) {
\r
6855 /* Move selection here if it was empty */
\r
6857 pt.x = LOWORD(lParam);
\r
6858 pt.y = HIWORD(lParam);
\r
6859 SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);
\r
6860 if (sel.cpMin == sel.cpMax) {
\r
6861 sel.cpMin = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&pt); /*doc is wrong*/
\r
6862 sel.cpMax = sel.cpMin;
\r
6863 SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
6865 SendMessage(hwnd, EM_HIDESELECTION, FALSE, FALSE);
\r
6866 { // [HGM] chatclick: code moved here from WM_RBUTTONUP case, to have menu appear on down-click
\r
6868 HMENU hmenu = LoadIcsTextMenu(icsTextMenuEntry);
\r
6869 SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);
\r
6870 if (sel.cpMin == sel.cpMax) {
\r
6871 EnableMenuItem(hmenu, IDM_Copy, MF_BYCOMMAND|MF_GRAYED);
\r
6872 EnableMenuItem(hmenu, IDM_QuickPaste, MF_BYCOMMAND|MF_GRAYED);
\r
6874 if (!IsClipboardFormatAvailable(CF_TEXT)) {
\r
6875 EnableMenuItem(hmenu, IDM_Paste, MF_BYCOMMAND|MF_GRAYED);
\r
6877 pt.x = LOWORD(lParam)-30; // [HGM] chatclick: make menu pop up with pointer above upper-right item
\r
6878 pt.y = HIWORD(lParam)-10; // make it appear as if mouse moved there, so it will be selected on up-click
\r
6879 PostMessage(hwnd, WM_MOUSEMOVE, wParam, lParam+5);
\r
6880 MenuPopup(hwnd, pt, hmenu, -1);
\r
6884 case WM_RBUTTONUP:
\r
6885 if (GetKeyState(VK_SHIFT) & ~1) {
\r
6886 SendDlgItemMessage(hwndConsole, OPT_ConsoleText,
\r
6887 WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);
\r
6891 hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
6893 return SendMessage(hInput, message, wParam, lParam);
\r
6894 case WM_MBUTTONDOWN:
\r
6895 return SendMessage(hwnd, WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);
\r
6897 switch (LOWORD(wParam)) {
\r
6898 case IDM_QuickPaste:
\r
6900 SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);
\r
6901 if (sel.cpMin == sel.cpMax) {
\r
6902 MessageBeep(MB_ICONEXCLAMATION);
\r
6905 SendMessage(hwnd, WM_COPY, 0, 0);
\r
6906 hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
6907 SendMessage(hInput, WM_PASTE, 0, 0);
\r
6912 SendMessage(hwnd, WM_CUT, 0, 0);
\r
6915 SendMessage(hwnd, WM_PASTE, 0, 0);
\r
6918 SendMessage(hwnd, WM_COPY, 0, 0);
\r
6922 int i = LOWORD(wParam) - IDM_CommandX;
\r
6923 if (i >= 0 && i < ICS_TEXT_MENU_SIZE &&
\r
6924 icsTextMenuEntry[i].command != NULL) {
\r
6925 CommandX(hwnd, icsTextMenuEntry[i].command,
\r
6926 icsTextMenuEntry[i].getname,
\r
6927 icsTextMenuEntry[i].immediate);
\r
6935 return (*consoleTextWindowProc)(hwnd, message, wParam, lParam);
\r
6938 WNDPROC consoleInputWindowProc;
\r
6941 ConsoleInputSubclass(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
\r
6943 char buf[MSG_SIZ];
\r
6945 static BOOL sendNextChar = FALSE;
\r
6946 static BOOL quoteNextChar = FALSE;
\r
6947 InputSource *is = consoleInputSource;
\r
6951 switch (message) {
\r
6953 if (!appData.localLineEditing || sendNextChar) {
\r
6954 is->buf[0] = (CHAR) wParam;
\r
6956 SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);
\r
6957 sendNextChar = FALSE;
\r
6960 if (quoteNextChar) {
\r
6961 buf[0] = (char) wParam;
\r
6962 buf[1] = NULLCHAR;
\r
6963 SendMessage(hwnd, EM_REPLACESEL, TRUE, (LPARAM) buf);
\r
6964 quoteNextChar = FALSE;
\r
6968 case '\r': /* Enter key */
\r
6969 is->count = GetWindowText(hwnd, is->buf, INPUT_SOURCE_BUF_SIZE-1);
\r
6970 if (consoleEcho) SaveInHistory(is->buf);
\r
6971 is->buf[is->count++] = '\n';
\r
6972 is->buf[is->count] = NULLCHAR;
\r
6973 SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);
\r
6974 if (consoleEcho) {
\r
6975 ConsoleOutput(is->buf, is->count, TRUE);
\r
6976 } else if (appData.localLineEditing) {
\r
6977 ConsoleOutput("\n", 1, TRUE);
\r
6980 case '\033': /* Escape key */
\r
6981 SetWindowText(hwnd, "");
\r
6982 cf.cbSize = sizeof(CHARFORMAT);
\r
6983 cf.dwMask = CFM_COLOR|CFM_BOLD|CFM_ITALIC|CFM_UNDERLINE|CFM_STRIKEOUT;
\r
6984 if (consoleEcho) {
\r
6985 cf.crTextColor = textAttribs[ColorNormal].color;
\r
6987 cf.crTextColor = COLOR_ECHOOFF;
\r
6989 cf.dwEffects = textAttribs[ColorNormal].effects;
\r
6990 SendMessage(hwnd, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf);
\r
6992 case '\t': /* Tab key */
\r
6993 if (GetKeyState(VK_SHIFT) < 0) {
\r
6995 SetFocus(GetDlgItem(hwndConsole, OPT_ConsoleText));
\r
6998 if (IsIconic(hwndMain)) ShowWindow(hwndMain, SW_RESTORE);
\r
6999 if (buttonDesc[0].hwnd) {
\r
7000 SetFocus(buttonDesc[0].hwnd);
\r
7002 SetFocus(hwndMain);
\r
7006 case '\023': /* Ctrl+S */
\r
7007 sendNextChar = TRUE;
\r
7009 case '\021': /* Ctrl+Q */
\r
7010 quoteNextChar = TRUE;
\r
7020 GetWindowText(hwnd, buf, MSG_SIZ);
\r
7021 p = PrevInHistory(buf);
\r
7023 SetWindowText(hwnd, p);
\r
7024 sel.cpMin = 999999;
\r
7025 sel.cpMax = 999999;
\r
7026 SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
7031 p = NextInHistory();
\r
7033 SetWindowText(hwnd, p);
\r
7034 sel.cpMin = 999999;
\r
7035 sel.cpMax = 999999;
\r
7036 SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
7042 if (!(GetKeyState(VK_CONTROL) & ~1)) break;
\r
7046 SendDlgItemMessage(hwndConsole, OPT_ConsoleText, message, wParam, lParam);
\r
7050 case WM_MBUTTONDOWN:
\r
7051 SendDlgItemMessage(hwndConsole, OPT_ConsoleText,
\r
7052 WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);
\r
7054 case WM_RBUTTONUP:
\r
7055 if (GetKeyState(VK_SHIFT) & ~1) {
\r
7056 SendDlgItemMessage(hwndConsole, OPT_ConsoleText,
\r
7057 WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);
\r
7061 hmenu = LoadMenu(hInst, "InputMenu");
\r
7062 SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);
\r
7063 if (sel.cpMin == sel.cpMax) {
\r
7064 EnableMenuItem(hmenu, IDM_Copy, MF_BYCOMMAND|MF_GRAYED);
\r
7065 EnableMenuItem(hmenu, IDM_Cut, MF_BYCOMMAND|MF_GRAYED);
\r
7067 if (!IsClipboardFormatAvailable(CF_TEXT)) {
\r
7068 EnableMenuItem(hmenu, IDM_Paste, MF_BYCOMMAND|MF_GRAYED);
\r
7070 pt.x = LOWORD(lParam);
\r
7071 pt.y = HIWORD(lParam);
\r
7072 MenuPopup(hwnd, pt, hmenu, -1);
\r
7076 switch (LOWORD(wParam)) {
\r
7078 SendMessage(hwnd, EM_UNDO, 0, 0);
\r
7080 case IDM_SelectAll:
\r
7082 sel.cpMax = -1; /*999999?*/
\r
7083 SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
7086 SendMessage(hwnd, WM_CUT, 0, 0);
\r
7089 SendMessage(hwnd, WM_PASTE, 0, 0);
\r
7092 SendMessage(hwnd, WM_COPY, 0, 0);
\r
7097 return (*consoleInputWindowProc)(hwnd, message, wParam, lParam);
\r
7100 #define CO_MAX 100000
\r
7101 #define CO_TRIM 1000
\r
7104 ConsoleWndProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
7106 static SnapData sd;
\r
7107 HWND hText, hInput;
\r
7109 static int sizeX, sizeY;
\r
7110 int newSizeX, newSizeY;
\r
7114 hText = GetDlgItem(hDlg, OPT_ConsoleText);
\r
7115 hInput = GetDlgItem(hDlg, OPT_ConsoleInput);
\r
7117 switch (message) {
\r
7119 if (((NMHDR*)lParam)->code == EN_LINK)
\r
7121 ENLINK *pLink = (ENLINK*)lParam;
\r
7122 if (pLink->msg == WM_LBUTTONUP)
\r
7126 tr.chrg = pLink->chrg;
\r
7127 tr.lpstrText = malloc(1+tr.chrg.cpMax-tr.chrg.cpMin);
\r
7128 SendMessage(hText, EM_GETTEXTRANGE, 0, (LPARAM)&tr);
\r
7129 ShellExecute(NULL, "open", tr.lpstrText, NULL, NULL, SW_SHOW);
\r
7130 free(tr.lpstrText);
\r
7134 case WM_INITDIALOG: /* message: initialize dialog box */
\r
7135 hwndConsole = hDlg;
\r
7137 consoleTextWindowProc = (WNDPROC)
\r
7138 SetWindowLong(hText, GWL_WNDPROC, (LONG) ConsoleTextSubclass);
\r
7139 SendMessage(hText, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);
\r
7140 consoleInputWindowProc = (WNDPROC)
\r
7141 SetWindowLong(hInput, GWL_WNDPROC, (LONG) ConsoleInputSubclass);
\r
7142 SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);
\r
7143 Colorize(ColorNormal, TRUE);
\r
7144 SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &consoleCF);
\r
7145 ChangedConsoleFont();
\r
7146 GetClientRect(hDlg, &rect);
\r
7147 sizeX = rect.right;
\r
7148 sizeY = rect.bottom;
\r
7149 if (wpConsole.x != CW_USEDEFAULT && wpConsole.y != CW_USEDEFAULT &&
\r
7150 wpConsole.width != CW_USEDEFAULT && wpConsole.height != CW_USEDEFAULT) {
\r
7151 WINDOWPLACEMENT wp;
\r
7152 EnsureOnScreen(&wpConsole.x, &wpConsole.y, 0, 0);
\r
7153 wp.length = sizeof(WINDOWPLACEMENT);
\r
7155 wp.showCmd = SW_SHOW;
\r
7156 wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;
\r
7157 wp.rcNormalPosition.left = wpConsole.x;
\r
7158 wp.rcNormalPosition.right = wpConsole.x + wpConsole.width;
\r
7159 wp.rcNormalPosition.top = wpConsole.y;
\r
7160 wp.rcNormalPosition.bottom = wpConsole.y + wpConsole.height;
\r
7161 SetWindowPlacement(hDlg, &wp);
\r
7164 // [HGM] Chessknight's change 2004-07-13
\r
7165 else { /* Determine Defaults */
\r
7166 WINDOWPLACEMENT wp;
\r
7167 wpConsole.x = wpMain.width + 1;
\r
7168 wpConsole.y = wpMain.y;
\r
7169 wpConsole.width = screenWidth - wpMain.width;
\r
7170 wpConsole.height = wpMain.height;
\r
7171 EnsureOnScreen(&wpConsole.x, &wpConsole.y, 0, 0);
\r
7172 wp.length = sizeof(WINDOWPLACEMENT);
\r
7174 wp.showCmd = SW_SHOW;
\r
7175 wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;
\r
7176 wp.rcNormalPosition.left = wpConsole.x;
\r
7177 wp.rcNormalPosition.right = wpConsole.x + wpConsole.width;
\r
7178 wp.rcNormalPosition.top = wpConsole.y;
\r
7179 wp.rcNormalPosition.bottom = wpConsole.y + wpConsole.height;
\r
7180 SetWindowPlacement(hDlg, &wp);
\r
7183 // Allow hText to highlight URLs and send notifications on them
\r
7184 wMask = (WORD) SendMessage(hText, EM_GETEVENTMASK, 0, 0L);
\r
7185 SendMessage(hText, EM_SETEVENTMASK, 0, wMask | ENM_LINK);
\r
7186 SendMessage(hText, EM_AUTOURLDETECT, TRUE, 0L);
\r
7187 SetWindowLong(hText, GWL_USERDATA, 79); // initialize the text window's width
\r
7201 if (IsIconic(hDlg)) break;
\r
7202 newSizeX = LOWORD(lParam);
\r
7203 newSizeY = HIWORD(lParam);
\r
7204 if (sizeX != newSizeX || sizeY != newSizeY) {
\r
7205 RECT rectText, rectInput;
\r
7207 int newTextHeight, newTextWidth;
\r
7208 GetWindowRect(hText, &rectText);
\r
7209 newTextWidth = rectText.right - rectText.left + newSizeX - sizeX;
\r
7210 newTextHeight = rectText.bottom - rectText.top + newSizeY - sizeY;
\r
7211 if (newTextHeight < 0) {
\r
7212 newSizeY += -newTextHeight;
\r
7213 newTextHeight = 0;
\r
7215 SetWindowPos(hText, NULL, 0, 0,
\r
7216 newTextWidth, newTextHeight, SWP_NOZORDER|SWP_NOMOVE);
\r
7217 GetWindowRect(hInput, &rectInput); /* gives screen coords */
\r
7218 pt.x = rectInput.left;
\r
7219 pt.y = rectInput.top + newSizeY - sizeY;
\r
7220 ScreenToClient(hDlg, &pt);
\r
7221 SetWindowPos(hInput, NULL,
\r
7222 pt.x, pt.y, /* needs client coords */
\r
7223 rectInput.right - rectInput.left + newSizeX - sizeX,
\r
7224 rectInput.bottom - rectInput.top, SWP_NOZORDER);
\r
7230 case WM_GETMINMAXINFO:
\r
7231 /* Prevent resizing window too small */
\r
7232 mmi = (MINMAXINFO *) lParam;
\r
7233 mmi->ptMinTrackSize.x = 100;
\r
7234 mmi->ptMinTrackSize.y = 100;
\r
7237 /* [AS] Snapping */
\r
7238 case WM_ENTERSIZEMOVE:
\r
7239 return OnEnterSizeMove( &sd, hDlg, wParam, lParam );
\r
7242 return OnSizing( &sd, hDlg, wParam, lParam );
\r
7245 return OnMoving( &sd, hDlg, wParam, lParam );
\r
7247 case WM_EXITSIZEMOVE:
\r
7248 UpdateICSWidth(hText);
\r
7249 return OnExitSizeMove( &sd, hDlg, wParam, lParam );
\r
7252 return DefWindowProc(hDlg, message, wParam, lParam);
\r
7260 if (hwndConsole) return;
\r
7261 hCons = CreateDialog(hInst, szConsoleName, 0, NULL);
\r
7262 SendMessage(hCons, WM_INITDIALOG, 0, 0);
\r
7267 ConsoleOutput(char* data, int length, int forceVisible)
\r
7272 char buf[CO_MAX+1];
\r
7275 static int delayLF = 0;
\r
7276 CHARRANGE savesel, sel;
\r
7278 if (hwndConsole == NULL || length > CO_MAX-100 || length == 0) return;
\r
7286 while (length--) {
\r
7294 } else if (*p == '\007') {
\r
7295 MyPlaySound(&sounds[(int)SoundBell]);
\r
7302 hText = GetDlgItem(hwndConsole, OPT_ConsoleText);
\r
7303 SendMessage(hText, EM_HIDESELECTION, TRUE, FALSE);
\r
7304 /* Save current selection */
\r
7305 SendMessage(hText, EM_EXGETSEL, 0, (LPARAM)&savesel);
\r
7306 exlen = GetWindowTextLength(hText);
\r
7307 /* Find out whether current end of text is visible */
\r
7308 SendMessage(hText, EM_GETRECT, 0, (LPARAM) &rect);
\r
7309 SendMessage(hText, EM_POSFROMCHAR, (WPARAM) &pEnd, exlen);
\r
7310 /* Trim existing text if it's too long */
\r
7311 if (exlen + (q - buf) > CO_MAX) {
\r
7312 trim = (CO_TRIM > (q - buf)) ? CO_TRIM : (q - buf);
\r
7315 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
7316 SendMessage(hText, EM_REPLACESEL, 0, (LPARAM)"");
\r
7318 savesel.cpMin -= trim;
\r
7319 savesel.cpMax -= trim;
\r
7320 if (exlen < 0) exlen = 0;
\r
7321 if (savesel.cpMin < 0) savesel.cpMin = 0;
\r
7322 if (savesel.cpMax < savesel.cpMin) savesel.cpMax = savesel.cpMin;
\r
7324 /* Append the new text */
\r
7325 sel.cpMin = exlen;
\r
7326 sel.cpMax = exlen;
\r
7327 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
7328 SendMessage(hText, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&consoleCF);
\r
7329 SendMessage(hText, EM_REPLACESEL, 0, (LPARAM) buf);
\r
7330 if (forceVisible || exlen == 0 ||
\r
7331 (rect.left <= pEnd.x && pEnd.x < rect.right &&
\r
7332 rect.top <= pEnd.y && pEnd.y < rect.bottom)) {
\r
7333 /* Scroll to make new end of text visible if old end of text
\r
7334 was visible or new text is an echo of user typein */
\r
7335 sel.cpMin = 9999999;
\r
7336 sel.cpMax = 9999999;
\r
7337 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
7338 SendMessage(hText, EM_HIDESELECTION, FALSE, FALSE);
\r
7339 SendMessage(hText, EM_SCROLLCARET, 0, 0);
\r
7340 SendMessage(hText, EM_HIDESELECTION, TRUE, FALSE);
\r
7342 if (savesel.cpMax == exlen || forceVisible) {
\r
7343 /* Move insert point to new end of text if it was at the old
\r
7344 end of text or if the new text is an echo of user typein */
\r
7345 sel.cpMin = 9999999;
\r
7346 sel.cpMax = 9999999;
\r
7347 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
7349 /* Restore previous selection */
\r
7350 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&savesel);
\r
7352 SendMessage(hText, EM_HIDESELECTION, FALSE, FALSE);
\r
7359 DisplayHoldingsCount(HDC hdc, int x, int y, int rightAlign, int copyNumber)
\r
7363 COLORREF oldFg, oldBg;
\r
7367 if(copyNumber > 1) sprintf(buf, "%d", copyNumber); else buf[0] = 0;
\r
7369 oldFg = SetTextColor(hdc, RGB(255, 255, 255)); /* white */
\r
7370 oldBg = SetBkColor(hdc, RGB(0, 0, 0)); /* black */
\r
7371 oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);
\r
7374 rect.right = x + squareSize;
\r
7376 rect.bottom = y + squareSize;
\r
7379 ExtTextOut(hdc, x + MESSAGE_LINE_LEFTMARGIN
\r
7380 + (rightAlign ? (squareSize*2)/3 : 0),
\r
7381 y, ETO_CLIPPED|ETO_OPAQUE,
\r
7382 &rect, str, strlen(str), NULL);
\r
7384 (void) SetTextColor(hdc, oldFg);
\r
7385 (void) SetBkColor(hdc, oldBg);
\r
7386 (void) SelectObject(hdc, oldFont);
\r
7390 DisplayAClock(HDC hdc, int timeRemaining, int highlight,
\r
7391 RECT *rect, char *color, char *flagFell)
\r
7395 COLORREF oldFg, oldBg;
\r
7398 if (appData.clockMode) {
\r
7400 sprintf(buf, "%c %s %s", color[0], TimeString(timeRemaining), flagFell);
\r
7402 sprintf(buf, "%s:%c%s %s", color, (logoHeight>0 ? 0 : ' '), TimeString(timeRemaining), flagFell);
\r
7409 oldFg = SetTextColor(hdc, RGB(255, 255, 255)); /* white */
\r
7410 oldBg = SetBkColor(hdc, RGB(0, 0, 0)); /* black */
\r
7412 oldFg = SetTextColor(hdc, RGB(0, 0, 0)); /* black */
\r
7413 oldBg = SetBkColor(hdc, RGB(255, 255, 255)); /* white */
\r
7415 oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);
\r
7419 ExtTextOut(hdc, rect->left + MESSAGE_LINE_LEFTMARGIN,
\r
7420 rect->top, ETO_CLIPPED|ETO_OPAQUE,
\r
7421 rect, str, strlen(str), NULL);
\r
7422 if(logoHeight > 0 && appData.clockMode) {
\r
7424 sprintf(buf, "%s %s", buf+7, flagFell);
\r
7425 r.top = rect->top + logoHeight/2;
\r
7426 r.left = rect->left;
\r
7427 r.right = rect->right;
\r
7428 r.bottom = rect->bottom;
\r
7429 ExtTextOut(hdc, rect->left + MESSAGE_LINE_LEFTMARGIN,
\r
7430 r.top, ETO_CLIPPED|ETO_OPAQUE,
\r
7431 &r, str, strlen(str), NULL);
\r
7433 (void) SetTextColor(hdc, oldFg);
\r
7434 (void) SetBkColor(hdc, oldBg);
\r
7435 (void) SelectObject(hdc, oldFont);
\r
7440 DoReadFile(HANDLE hFile, char *buf, int count, DWORD *outCount,
\r
7446 if( count <= 0 ) {
\r
7447 if (appData.debugMode) {
\r
7448 fprintf( debugFP, "DoReadFile: trying to read past end of buffer, overflow = %d\n", count );
\r
7451 return ERROR_INVALID_USER_BUFFER;
\r
7454 ResetEvent(ovl->hEvent);
\r
7455 ovl->Offset = ovl->OffsetHigh = 0;
\r
7456 ok = ReadFile(hFile, buf, count, outCount, ovl);
\r
7460 err = GetLastError();
\r
7461 if (err == ERROR_IO_PENDING) {
\r
7462 ok = GetOverlappedResult(hFile, ovl, outCount, TRUE);
\r
7466 err = GetLastError();
\r
7473 DoWriteFile(HANDLE hFile, char *buf, int count, DWORD *outCount,
\r
7478 ResetEvent(ovl->hEvent);
\r
7479 ovl->Offset = ovl->OffsetHigh = 0;
\r
7480 ok = WriteFile(hFile, buf, count, outCount, ovl);
\r
7484 err = GetLastError();
\r
7485 if (err == ERROR_IO_PENDING) {
\r
7486 ok = GetOverlappedResult(hFile, ovl, outCount, TRUE);
\r
7490 err = GetLastError();
\r
7496 /* [AS] If input is line by line and a line exceed the buffer size, force an error */
\r
7497 void CheckForInputBufferFull( InputSource * is )
\r
7499 if( is->lineByLine && (is->next - is->buf) >= INPUT_SOURCE_BUF_SIZE ) {
\r
7500 /* Look for end of line */
\r
7501 char * p = is->buf;
\r
7503 while( p < is->next && *p != '\n' ) {
\r
7507 if( p >= is->next ) {
\r
7508 if (appData.debugMode) {
\r
7509 fprintf( debugFP, "Input line exceeded buffer size (source id=%lu)\n", is->id );
\r
7512 is->error = ERROR_BROKEN_PIPE; /* [AS] Just any non-successful code! */
\r
7513 is->count = (DWORD) -1;
\r
7514 is->next = is->buf;
\r
7520 InputThread(LPVOID arg)
\r
7525 is = (InputSource *) arg;
\r
7526 ovl.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
\r
7527 ovl.Internal = ovl.InternalHigh = ovl.Offset = ovl.OffsetHigh = 0;
\r
7528 while (is->hThread != NULL) {
\r
7529 is->error = DoReadFile(is->hFile, is->next,
\r
7530 INPUT_SOURCE_BUF_SIZE - (is->next - is->buf),
\r
7531 &is->count, &ovl);
\r
7532 if (is->error == NO_ERROR) {
\r
7533 is->next += is->count;
\r
7535 if (is->error == ERROR_BROKEN_PIPE) {
\r
7536 /* Correct for MS brain damage. EOF reading a pipe is not an error. */
\r
7539 is->count = (DWORD) -1;
\r
7540 /* [AS] The (is->count <= 0) check below is not useful for unsigned values! */
\r
7545 CheckForInputBufferFull( is );
\r
7547 SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);
\r
7549 if( is->count == ((DWORD) -1) ) break; /* [AS] */
\r
7551 if (is->count <= 0) break; /* Quit on EOF or error */
\r
7554 CloseHandle(ovl.hEvent);
\r
7555 CloseHandle(is->hFile);
\r
7557 if (appData.debugMode) {
\r
7558 fprintf( debugFP, "Input thread terminated (id=%lu, error=%d, count=%ld)\n", is->id, is->error, is->count );
\r
7565 /* Windows 95 beta 2 won't let you do overlapped i/o on a console or pipe */
\r
7567 NonOvlInputThread(LPVOID arg)
\r
7574 is = (InputSource *) arg;
\r
7575 while (is->hThread != NULL) {
\r
7576 is->error = ReadFile(is->hFile, is->next,
\r
7577 INPUT_SOURCE_BUF_SIZE - (is->next - is->buf),
\r
7578 &is->count, NULL) ? NO_ERROR : GetLastError();
\r
7579 if (is->error == NO_ERROR) {
\r
7580 /* Change CRLF to LF */
\r
7581 if (is->next > is->buf) {
\r
7583 i = is->count + 1;
\r
7591 if (prev == '\r' && *p == '\n') {
\r
7603 if (is->error == ERROR_BROKEN_PIPE) {
\r
7604 /* Correct for MS brain damage. EOF reading a pipe is not an error. */
\r
7607 is->count = (DWORD) -1;
\r
7611 CheckForInputBufferFull( is );
\r
7613 SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);
\r
7615 if( is->count == ((DWORD) -1) ) break; /* [AS] */
\r
7617 if (is->count < 0) break; /* Quit on error */
\r
7619 CloseHandle(is->hFile);
\r
7624 SocketInputThread(LPVOID arg)
\r
7628 is = (InputSource *) arg;
\r
7629 while (is->hThread != NULL) {
\r
7630 is->count = recv(is->sock, is->buf, INPUT_SOURCE_BUF_SIZE, 0);
\r
7631 if ((int)is->count == SOCKET_ERROR) {
\r
7632 is->count = (DWORD) -1;
\r
7633 is->error = WSAGetLastError();
\r
7635 is->error = NO_ERROR;
\r
7636 is->next += is->count;
\r
7637 if (is->count == 0 && is->second == is) {
\r
7638 /* End of file on stderr; quit with no message */
\r
7642 SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);
\r
7644 if( is->count == ((DWORD) -1) ) break; /* [AS] */
\r
7646 if (is->count <= 0) break; /* Quit on EOF or error */
\r
7652 InputEvent(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
\r
7656 is = (InputSource *) lParam;
\r
7657 if (is->lineByLine) {
\r
7658 /* Feed in lines one by one */
\r
7659 char *p = is->buf;
\r
7661 while (q < is->next) {
\r
7662 if (*q++ == '\n') {
\r
7663 (is->func)(is, is->closure, p, q - p, NO_ERROR);
\r
7668 /* Move any partial line to the start of the buffer */
\r
7670 while (p < is->next) {
\r
7675 if (is->error != NO_ERROR || is->count == 0) {
\r
7676 /* Notify backend of the error. Note: If there was a partial
\r
7677 line at the end, it is not flushed through. */
\r
7678 (is->func)(is, is->closure, is->buf, is->count, is->error);
\r
7681 /* Feed in the whole chunk of input at once */
\r
7682 (is->func)(is, is->closure, is->buf, is->count, is->error);
\r
7683 is->next = is->buf;
\r
7687 /*---------------------------------------------------------------------------*\
\r
7689 * Menu enables. Used when setting various modes.
\r
7691 \*---------------------------------------------------------------------------*/
\r
7699 GreyRevert(Boolean grey)
\r
7700 { // [HGM] vari: for retracting variations in local mode
\r
7701 HMENU hmenu = GetMenu(hwndMain);
\r
7702 EnableMenuItem(hmenu, IDM_Revert, MF_BYCOMMAND|(grey ? MF_GRAYED : MF_ENABLED));
\r
7703 EnableMenuItem(hmenu, IDM_Annotate, MF_BYCOMMAND|(grey ? MF_GRAYED : MF_ENABLED));
\r
7707 SetMenuEnables(HMENU hmenu, Enables *enab)
\r
7709 while (enab->item > 0) {
\r
7710 (void) EnableMenuItem(hmenu, enab->item, enab->flags);
\r
7715 Enables gnuEnables[] = {
\r
7716 { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },
\r
7717 { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },
\r
7718 { IDM_IcsClient, MF_BYCOMMAND|MF_GRAYED },
\r
7719 { IDM_Accept, MF_BYCOMMAND|MF_GRAYED },
\r
7720 { IDM_Decline, MF_BYCOMMAND|MF_GRAYED },
\r
7721 { IDM_Rematch, MF_BYCOMMAND|MF_GRAYED },
\r
7722 { IDM_Adjourn, MF_BYCOMMAND|MF_GRAYED },
\r
7723 { IDM_StopExamining, MF_BYCOMMAND|MF_GRAYED },
\r
7724 { IDM_StopObserving, MF_BYCOMMAND|MF_GRAYED },
\r
7725 { IDM_Upload, MF_BYCOMMAND|MF_GRAYED },
\r
7726 { IDM_Revert, MF_BYCOMMAND|MF_GRAYED },
\r
7727 { IDM_Annotate, MF_BYCOMMAND|MF_GRAYED },
\r
7728 { IDM_NewChat, MF_BYCOMMAND|MF_GRAYED },
\r
7732 Enables icsEnables[] = {
\r
7733 { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },
\r
7734 { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },
\r
7735 { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },
\r
7736 { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },
\r
7737 { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },
\r
7738 { IDM_Match, MF_BYCOMMAND|MF_GRAYED },
\r
7739 { IDM_MachineBoth, MF_BYCOMMAND|MF_GRAYED },
\r
7740 { IDM_AnalysisMode, MF_BYCOMMAND|MF_ENABLED },
\r
7741 { IDM_AnalyzeFile, MF_BYCOMMAND|MF_GRAYED },
\r
7742 { IDM_TimeControl, MF_BYCOMMAND|MF_GRAYED },
\r
7743 { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },
\r
7744 { IDM_Hint, MF_BYCOMMAND|MF_GRAYED },
\r
7745 { IDM_Book, MF_BYCOMMAND|MF_GRAYED },
\r
7746 { IDM_IcsOptions, MF_BYCOMMAND|MF_ENABLED },
\r
7747 { IDM_Engine1Options, MF_BYCOMMAND|MF_GRAYED },
\r
7748 { IDM_Engine2Options, MF_BYCOMMAND|MF_GRAYED },
\r
7749 { IDM_Annotate, MF_BYCOMMAND|MF_GRAYED },
\r
7754 Enables zippyEnables[] = {
\r
7755 { IDM_MoveNow, MF_BYCOMMAND|MF_ENABLED },
\r
7756 { IDM_Hint, MF_BYCOMMAND|MF_ENABLED },
\r
7757 { IDM_Book, MF_BYCOMMAND|MF_ENABLED },
\r
7758 { IDM_Engine1Options, MF_BYCOMMAND|MF_ENABLED },
\r
7763 Enables ncpEnables[] = {
\r
7764 { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },
\r
7765 { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },
\r
7766 { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },
\r
7767 { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },
\r
7768 { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },
\r
7769 { IDM_Match, MF_BYCOMMAND|MF_GRAYED },
\r
7770 { IDM_AnalysisMode, MF_BYCOMMAND|MF_GRAYED },
\r
7771 { IDM_AnalyzeFile, MF_BYCOMMAND|MF_GRAYED },
\r
7772 { IDM_IcsClient, MF_BYCOMMAND|MF_GRAYED },
\r
7773 { ACTION_POS, MF_BYPOSITION|MF_GRAYED },
\r
7774 { IDM_Revert, MF_BYCOMMAND|MF_GRAYED },
\r
7775 { IDM_Annotate, MF_BYCOMMAND|MF_GRAYED },
\r
7776 { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },
\r
7777 { IDM_RetractMove, MF_BYCOMMAND|MF_GRAYED },
\r
7778 { IDM_TimeControl, MF_BYCOMMAND|MF_GRAYED },
\r
7779 { IDM_Hint, MF_BYCOMMAND|MF_GRAYED },
\r
7780 { IDM_Book, MF_BYCOMMAND|MF_GRAYED },
\r
7781 { IDM_MachineBoth, MF_BYCOMMAND|MF_GRAYED },
\r
7782 { IDM_NewChat, MF_BYCOMMAND|MF_GRAYED },
\r
7783 { IDM_Engine1Options, MF_BYCOMMAND|MF_GRAYED },
\r
7784 { IDM_Engine2Options, MF_BYCOMMAND|MF_GRAYED },
\r
7788 Enables trainingOnEnables[] = {
\r
7789 { IDM_EditComment, MF_BYCOMMAND|MF_GRAYED },
\r
7790 { IDM_Pause, MF_BYCOMMAND|MF_GRAYED },
\r
7791 { IDM_Forward, MF_BYCOMMAND|MF_GRAYED },
\r
7792 { IDM_Backward, MF_BYCOMMAND|MF_GRAYED },
\r
7793 { IDM_ToEnd, MF_BYCOMMAND|MF_GRAYED },
\r
7794 { IDM_ToStart, MF_BYCOMMAND|MF_GRAYED },
\r
7795 { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },
\r
7796 { IDM_TruncateGame, MF_BYCOMMAND|MF_GRAYED },
\r
7800 Enables trainingOffEnables[] = {
\r
7801 { IDM_EditComment, MF_BYCOMMAND|MF_ENABLED },
\r
7802 { IDM_Pause, MF_BYCOMMAND|MF_ENABLED },
\r
7803 { IDM_Forward, MF_BYCOMMAND|MF_ENABLED },
\r
7804 { IDM_Backward, MF_BYCOMMAND|MF_ENABLED },
\r
7805 { IDM_ToEnd, MF_BYCOMMAND|MF_ENABLED },
\r
7806 { IDM_ToStart, MF_BYCOMMAND|MF_ENABLED },
\r
7807 { IDM_MoveNow, MF_BYCOMMAND|MF_ENABLED },
\r
7808 { IDM_TruncateGame, MF_BYCOMMAND|MF_ENABLED },
\r
7812 /* These modify either ncpEnables or gnuEnables */
\r
7813 Enables cmailEnables[] = {
\r
7814 { IDM_MailMove, MF_BYCOMMAND|MF_ENABLED },
\r
7815 { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_ENABLED },
\r
7816 { ACTION_POS, MF_BYPOSITION|MF_ENABLED },
\r
7817 { IDM_CallFlag, MF_BYCOMMAND|MF_GRAYED },
\r
7818 { IDM_Draw, MF_BYCOMMAND|MF_ENABLED },
\r
7819 { IDM_Adjourn, MF_BYCOMMAND|MF_GRAYED },
\r
7820 { IDM_Abort, MF_BYCOMMAND|MF_GRAYED },
\r
7824 Enables machineThinkingEnables[] = {
\r
7825 { IDM_LoadGame, MF_BYCOMMAND|MF_GRAYED },
\r
7826 { IDM_LoadNextGame, MF_BYCOMMAND|MF_GRAYED },
\r
7827 { IDM_LoadPrevGame, MF_BYCOMMAND|MF_GRAYED },
\r
7828 { IDM_ReloadGame, MF_BYCOMMAND|MF_GRAYED },
\r
7829 { IDM_PasteGame, MF_BYCOMMAND|MF_GRAYED },
\r
7830 { IDM_LoadPosition, MF_BYCOMMAND|MF_GRAYED },
\r
7831 { IDM_LoadNextPosition, MF_BYCOMMAND|MF_GRAYED },
\r
7832 { IDM_LoadPrevPosition, MF_BYCOMMAND|MF_GRAYED },
\r
7833 { IDM_ReloadPosition, MF_BYCOMMAND|MF_GRAYED },
\r
7834 { IDM_PastePosition, MF_BYCOMMAND|MF_GRAYED },
\r
7835 { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },
\r
7836 { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },
\r
7837 { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },
\r
7838 { IDM_Match, MF_BYCOMMAND|MF_GRAYED },
\r
7839 { IDM_TypeInMove, MF_BYCOMMAND|MF_GRAYED },
\r
7840 { IDM_RetractMove, MF_BYCOMMAND|MF_GRAYED },
\r
7844 Enables userThinkingEnables[] = {
\r
7845 { IDM_LoadGame, MF_BYCOMMAND|MF_ENABLED },
\r
7846 { IDM_LoadNextGame, MF_BYCOMMAND|MF_ENABLED },
\r
7847 { IDM_LoadPrevGame, MF_BYCOMMAND|MF_ENABLED },
\r
7848 { IDM_ReloadGame, MF_BYCOMMAND|MF_ENABLED },
\r
7849 { IDM_PasteGame, MF_BYCOMMAND|MF_ENABLED },
\r
7850 { IDM_LoadPosition, MF_BYCOMMAND|MF_ENABLED },
\r
7851 { IDM_LoadNextPosition, MF_BYCOMMAND|MF_ENABLED },
\r
7852 { IDM_LoadPrevPosition, MF_BYCOMMAND|MF_ENABLED },
\r
7853 { IDM_ReloadPosition, MF_BYCOMMAND|MF_ENABLED },
\r
7854 { IDM_PastePosition, MF_BYCOMMAND|MF_ENABLED },
\r
7855 { IDM_MachineWhite, MF_BYCOMMAND|MF_ENABLED },
\r
7856 { IDM_MachineBlack, MF_BYCOMMAND|MF_ENABLED },
\r
7857 { IDM_TwoMachines, MF_BYCOMMAND|MF_ENABLED },
\r
7858 { IDM_Match, MF_BYCOMMAND|MF_ENABLED },
\r
7859 { IDM_TypeInMove, MF_BYCOMMAND|MF_ENABLED },
\r
7860 { IDM_RetractMove, MF_BYCOMMAND|MF_ENABLED },
\r
7864 /*---------------------------------------------------------------------------*\
\r
7866 * Front-end interface functions exported by XBoard.
\r
7867 * Functions appear in same order as prototypes in frontend.h.
\r
7869 \*---------------------------------------------------------------------------*/
\r
7873 static UINT prevChecked = 0;
\r
7874 static int prevPausing = 0;
\r
7877 if (pausing != prevPausing) {
\r
7878 prevPausing = pausing;
\r
7879 (void) CheckMenuItem(GetMenu(hwndMain), IDM_Pause,
\r
7880 MF_BYCOMMAND|(pausing ? MF_CHECKED : MF_UNCHECKED));
\r
7881 if (hwndPause) SetWindowText(hwndPause, pausing ? "C" : "P");
\r
7884 switch (gameMode) {
\r
7885 case BeginningOfGame:
\r
7886 if (appData.icsActive)
\r
7887 nowChecked = IDM_IcsClient;
\r
7888 else if (appData.noChessProgram)
\r
7889 nowChecked = IDM_EditGame;
\r
7891 nowChecked = IDM_MachineBlack;
\r
7893 case MachinePlaysBlack:
\r
7894 nowChecked = IDM_MachineBlack;
\r
7896 case MachinePlaysWhite:
\r
7897 nowChecked = IDM_MachineWhite;
\r
7899 case TwoMachinesPlay:
\r
7900 nowChecked = matchMode ? IDM_Match : IDM_TwoMachines; // [HGM] match
\r
7903 nowChecked = IDM_AnalysisMode;
\r
7906 nowChecked = IDM_AnalyzeFile;
\r
7909 nowChecked = IDM_EditGame;
\r
7911 case PlayFromGameFile:
\r
7912 nowChecked = IDM_LoadGame;
\r
7914 case EditPosition:
\r
7915 nowChecked = IDM_EditPosition;
\r
7918 nowChecked = IDM_Training;
\r
7920 case IcsPlayingWhite:
\r
7921 case IcsPlayingBlack:
\r
7922 case IcsObserving:
\r
7924 nowChecked = IDM_IcsClient;
\r
7931 if (prevChecked != 0)
\r
7932 (void) CheckMenuItem(GetMenu(hwndMain),
\r
7933 prevChecked, MF_BYCOMMAND|MF_UNCHECKED);
\r
7934 if (nowChecked != 0)
\r
7935 (void) CheckMenuItem(GetMenu(hwndMain),
\r
7936 nowChecked, MF_BYCOMMAND|MF_CHECKED);
\r
7938 if (nowChecked == IDM_LoadGame || nowChecked == IDM_Training) {
\r
7939 (void) EnableMenuItem(GetMenu(hwndMain), IDM_Training,
\r
7940 MF_BYCOMMAND|MF_ENABLED);
\r
7942 (void) EnableMenuItem(GetMenu(hwndMain),
\r
7943 IDM_Training, MF_BYCOMMAND|MF_GRAYED);
\r
7946 prevChecked = nowChecked;
\r
7948 /* [DM] icsEngineAnalyze - Do a sceure check too */
\r
7949 if (appData.icsActive) {
\r
7950 if (appData.icsEngineAnalyze) {
\r
7951 (void) CheckMenuItem(GetMenu(hwndMain), IDM_AnalysisMode,
\r
7952 MF_BYCOMMAND|MF_CHECKED);
\r
7954 (void) CheckMenuItem(GetMenu(hwndMain), IDM_AnalysisMode,
\r
7955 MF_BYCOMMAND|MF_UNCHECKED);
\r
7963 HMENU hmenu = GetMenu(hwndMain);
\r
7964 SetMenuEnables(hmenu, icsEnables);
\r
7965 EnableMenuItem(GetSubMenu(hmenu, OPTIONS_POS), ICS_POS,
\r
7966 MF_BYPOSITION|MF_ENABLED);
\r
7968 if (appData.zippyPlay) {
\r
7969 SetMenuEnables(hmenu, zippyEnables);
\r
7970 if (!appData.noChessProgram) /* [DM] icsEngineAnalyze */
\r
7971 (void) EnableMenuItem(GetMenu(hwndMain), IDM_AnalysisMode,
\r
7972 MF_BYCOMMAND|MF_ENABLED);
\r
7980 SetMenuEnables(GetMenu(hwndMain), gnuEnables);
\r
7986 HMENU hmenu = GetMenu(hwndMain);
\r
7987 SetMenuEnables(hmenu, ncpEnables);
\r
7988 EnableMenuItem(GetSubMenu(hmenu, OPTIONS_POS), SOUNDS_POS,
\r
7989 MF_BYPOSITION|MF_GRAYED);
\r
7990 DrawMenuBar(hwndMain);
\r
7996 SetMenuEnables(GetMenu(hwndMain), cmailEnables);
\r
8000 SetTrainingModeOn()
\r
8003 SetMenuEnables(GetMenu(hwndMain), trainingOnEnables);
\r
8004 for (i = 0; i < N_BUTTONS; i++) {
\r
8005 if (buttonDesc[i].hwnd != NULL)
\r
8006 EnableWindow(buttonDesc[i].hwnd, FALSE);
\r
8011 VOID SetTrainingModeOff()
\r
8014 SetMenuEnables(GetMenu(hwndMain), trainingOffEnables);
\r
8015 for (i = 0; i < N_BUTTONS; i++) {
\r
8016 if (buttonDesc[i].hwnd != NULL)
\r
8017 EnableWindow(buttonDesc[i].hwnd, TRUE);
\r
8023 SetUserThinkingEnables()
\r
8025 SetMenuEnables(GetMenu(hwndMain), userThinkingEnables);
\r
8029 SetMachineThinkingEnables()
\r
8031 HMENU hMenu = GetMenu(hwndMain);
\r
8032 int flags = MF_BYCOMMAND|MF_ENABLED;
\r
8034 SetMenuEnables(hMenu, machineThinkingEnables);
\r
8036 if (gameMode == MachinePlaysBlack) {
\r
8037 (void)EnableMenuItem(hMenu, IDM_MachineBlack, flags);
\r
8038 } else if (gameMode == MachinePlaysWhite) {
\r
8039 (void)EnableMenuItem(hMenu, IDM_MachineWhite, flags);
\r
8040 } else if (gameMode == TwoMachinesPlay) {
\r
8041 (void)EnableMenuItem(hMenu, matchMode ? IDM_Match : IDM_TwoMachines, flags); // [HGM] match
\r
8047 DisplayTitle(char *str)
\r
8049 char title[MSG_SIZ], *host;
\r
8050 if (str[0] != NULLCHAR) {
\r
8051 strcpy(title, str);
\r
8052 } else if (appData.icsActive) {
\r
8053 if (appData.icsCommPort[0] != NULLCHAR)
\r
8056 host = appData.icsHost;
\r
8057 sprintf(title, "%s: %s", szTitle, host);
\r
8058 } else if (appData.noChessProgram) {
\r
8059 strcpy(title, szTitle);
\r
8061 strcpy(title, szTitle);
\r
8062 strcat(title, ": ");
\r
8063 strcat(title, first.tidy);
\r
8065 SetWindowText(hwndMain, title);
\r
8070 DisplayMessage(char *str1, char *str2)
\r
8074 int remain = MESSAGE_TEXT_MAX - 1;
\r
8077 moveErrorMessageUp = FALSE; /* turned on later by caller if needed */
\r
8078 messageText[0] = NULLCHAR;
\r
8080 len = strlen(str1);
\r
8081 if (len > remain) len = remain;
\r
8082 strncpy(messageText, str1, len);
\r
8083 messageText[len] = NULLCHAR;
\r
8086 if (*str2 && remain >= 2) {
\r
8088 strcat(messageText, " ");
\r
8091 len = strlen(str2);
\r
8092 if (len > remain) len = remain;
\r
8093 strncat(messageText, str2, len);
\r
8095 messageText[MESSAGE_TEXT_MAX - 1] = NULLCHAR;
\r
8097 if (hwndMain == NULL || IsIconic(hwndMain)) return;
\r
8101 hdc = GetDC(hwndMain);
\r
8102 oldFont = SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);
\r
8103 ExtTextOut(hdc, messageRect.left, messageRect.top, ETO_CLIPPED|ETO_OPAQUE,
\r
8104 &messageRect, messageText, strlen(messageText), NULL);
\r
8105 (void) SelectObject(hdc, oldFont);
\r
8106 (void) ReleaseDC(hwndMain, hdc);
\r
8110 DisplayError(char *str, int error)
\r
8112 char buf[MSG_SIZ*2], buf2[MSG_SIZ];
\r
8118 len = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,
\r
8119 NULL, error, LANG_NEUTRAL,
\r
8120 (LPSTR) buf2, MSG_SIZ, NULL);
\r
8122 sprintf(buf, "%s:\n%s", str, buf2);
\r
8124 ErrorMap *em = errmap;
\r
8125 while (em->err != 0 && em->err != error) em++;
\r
8126 if (em->err != 0) {
\r
8127 sprintf(buf, "%s:\n%s", str, em->msg);
\r
8129 sprintf(buf, "%s:\nError code %d", str, error);
\r
8134 ErrorPopUp(_("Error"), buf);
\r
8139 DisplayMoveError(char *str)
\r
8141 fromX = fromY = -1;
\r
8142 ClearHighlights();
\r
8143 DrawPosition(FALSE, NULL);
\r
8144 if (appData.popupMoveErrors) {
\r
8145 ErrorPopUp(_("Error"), str);
\r
8147 DisplayMessage(str, "");
\r
8148 moveErrorMessageUp = TRUE;
\r
8153 DisplayFatalError(char *str, int error, int exitStatus)
\r
8155 char buf[2*MSG_SIZ], buf2[MSG_SIZ];
\r
8157 char *label = exitStatus ? _("Fatal Error") : _("Exiting");
\r
8160 len = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,
\r
8161 NULL, error, LANG_NEUTRAL,
\r
8162 (LPSTR) buf2, MSG_SIZ, NULL);
\r
8164 sprintf(buf, "%s:\n%s", str, buf2);
\r
8166 ErrorMap *em = errmap;
\r
8167 while (em->err != 0 && em->err != error) em++;
\r
8168 if (em->err != 0) {
\r
8169 sprintf(buf, "%s:\n%s", str, em->msg);
\r
8171 sprintf(buf, "%s:\nError code %d", str, error);
\r
8176 if (appData.debugMode) {
\r
8177 fprintf(debugFP, "%s: %s\n", label, str);
\r
8179 if (appData.popupExitMessage) {
\r
8180 (void) MessageBox(hwndMain, str, label, MB_OK|
\r
8181 (exitStatus ? MB_ICONSTOP : MB_ICONINFORMATION));
\r
8183 ExitEvent(exitStatus);
\r
8188 DisplayInformation(char *str)
\r
8190 (void) MessageBox(hwndMain, str, _("Information"), MB_OK|MB_ICONINFORMATION);
\r
8195 DisplayNote(char *str)
\r
8197 ErrorPopUp(_("Note"), str);
\r
8202 char *title, *question, *replyPrefix;
\r
8207 QuestionDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
8209 static QuestionParams *qp;
\r
8210 char reply[MSG_SIZ];
\r
8213 switch (message) {
\r
8214 case WM_INITDIALOG:
\r
8215 qp = (QuestionParams *) lParam;
\r
8216 CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));
\r
8217 Translate(hDlg, DLG_Question);
\r
8218 SetWindowText(hDlg, qp->title);
\r
8219 SetDlgItemText(hDlg, OPT_QuestionText, qp->question);
\r
8220 SetFocus(GetDlgItem(hDlg, OPT_QuestionInput));
\r
8224 switch (LOWORD(wParam)) {
\r
8226 strcpy(reply, qp->replyPrefix);
\r
8227 if (*reply) strcat(reply, " ");
\r
8228 len = strlen(reply);
\r
8229 GetDlgItemText(hDlg, OPT_QuestionInput, reply + len, sizeof(reply) - len);
\r
8230 strcat(reply, "\n");
\r
8231 OutputToProcess(qp->pr, reply, strlen(reply), &err);
\r
8232 EndDialog(hDlg, TRUE);
\r
8233 if (err) DisplayFatalError(_("Error writing to chess program"), err, 1);
\r
8236 EndDialog(hDlg, FALSE);
\r
8247 AskQuestion(char* title, char *question, char *replyPrefix, ProcRef pr)
\r
8249 QuestionParams qp;
\r
8253 qp.question = question;
\r
8254 qp.replyPrefix = replyPrefix;
\r
8256 lpProc = MakeProcInstance((FARPROC)QuestionDialog, hInst);
\r
8257 DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_Question),
\r
8258 hwndMain, (DLGPROC)lpProc, (LPARAM)&qp);
\r
8259 FreeProcInstance(lpProc);
\r
8262 /* [AS] Pick FRC position */
\r
8263 LRESULT CALLBACK NewGameFRC_Proc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
8265 static int * lpIndexFRC;
\r
8271 case WM_INITDIALOG:
\r
8272 lpIndexFRC = (int *) lParam;
\r
8274 CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));
\r
8275 Translate(hDlg, DLG_NewGameFRC);
\r
8277 SendDlgItemMessage( hDlg, IDC_NFG_Edit, EM_SETLIMITTEXT, sizeof(buf)-1, 0 );
\r
8278 SetDlgItemInt( hDlg, IDC_NFG_Edit, *lpIndexFRC, TRUE );
\r
8279 SendDlgItemMessage( hDlg, IDC_NFG_Edit, EM_SETSEL, 0, -1 );
\r
8280 SetFocus(GetDlgItem(hDlg, IDC_NFG_Edit));
\r
8285 switch( LOWORD(wParam) ) {
\r
8287 *lpIndexFRC = GetDlgItemInt(hDlg, IDC_NFG_Edit, &index_is_ok, TRUE );
\r
8288 EndDialog( hDlg, 0 );
\r
8289 shuffleOpenings = TRUE; /* [HGM] shuffle: switch shuffling on for as long as we stay in current variant */
\r
8292 EndDialog( hDlg, 1 );
\r
8294 case IDC_NFG_Edit:
\r
8295 if( HIWORD(wParam) == EN_CHANGE ) {
\r
8296 GetDlgItemInt(hDlg, IDC_NFG_Edit, &index_is_ok, TRUE );
\r
8298 EnableWindow( GetDlgItem(hDlg, IDOK), index_is_ok );
\r
8301 case IDC_NFG_Random:
\r
8302 sprintf( buf, "%d", myrandom() ); /* [HGM] shuffle: no longer limit to 960 */
\r
8303 SetDlgItemText(hDlg, IDC_NFG_Edit, buf );
\r
8316 int index = appData.defaultFrcPosition;
\r
8317 FARPROC lpProc = MakeProcInstance( (FARPROC) NewGameFRC_Proc, hInst );
\r
8319 result = DialogBoxParam( hInst, MAKEINTRESOURCE(DLG_NewGameFRC), hwndMain, (DLGPROC)lpProc, (LPARAM)&index );
\r
8321 if( result == 0 ) {
\r
8322 appData.defaultFrcPosition = index;
\r
8328 /* [AS] Game list options. Refactored by HGM */
\r
8330 HWND gameListOptionsDialog;
\r
8332 // low-level front-end: clear text edit / list widget
\r
8336 SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_RESETCONTENT, 0, 0 );
\r
8339 // low-level front-end: clear text edit / list widget
\r
8341 GLT_DeSelectList()
\r
8343 SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_SETCURSEL, 0, 0 );
\r
8346 // low-level front-end: append line to text edit / list widget
\r
8348 GLT_AddToList( char *name )
\r
8351 SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_ADDSTRING, 0, (LPARAM) name );
\r
8355 // low-level front-end: get line from text edit / list widget
\r
8357 GLT_GetFromList( int index, char *name )
\r
8360 if( SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_GETTEXT, index, (LPARAM) name ) != LB_ERR )
\r
8366 void GLT_MoveSelection( HWND hDlg, int delta )
\r
8368 int idx1 = (int) SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETCURSEL, 0, 0 );
\r
8369 int idx2 = idx1 + delta;
\r
8370 int count = (int) SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETCOUNT, 0, 0 );
\r
8372 if( idx1 >=0 && idx1 < count && idx2 >= 0 && idx2 < count ) {
\r
8375 SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETTEXT, idx1, (LPARAM) buf );
\r
8376 SendDlgItemMessage( hDlg, IDC_GameListTags, LB_DELETESTRING, idx1, 0 );
\r
8377 SendDlgItemMessage( hDlg, IDC_GameListTags, LB_INSERTSTRING, idx2, (LPARAM) buf );
\r
8378 SendDlgItemMessage( hDlg, IDC_GameListTags, LB_SETCURSEL, idx2, 0 );
\r
8382 LRESULT CALLBACK GameListOptions_Proc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
8386 case WM_INITDIALOG:
\r
8387 gameListOptionsDialog = hDlg; // [HGM] pass through global to keep out off back-end
\r
8389 CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));
\r
8390 Translate(hDlg, DLG_GameListOptions);
\r
8392 /* Initialize list */
\r
8393 GLT_TagsToList( lpUserGLT );
\r
8395 SetFocus( GetDlgItem(hDlg, IDC_GameListTags) );
\r
8400 switch( LOWORD(wParam) ) {
\r
8403 EndDialog( hDlg, 0 );
\r
8406 EndDialog( hDlg, 1 );
\r
8409 case IDC_GLT_Default:
\r
8410 GLT_TagsToList( GLT_DEFAULT_TAGS );
\r
8413 case IDC_GLT_Restore:
\r
8414 GLT_TagsToList( appData.gameListTags );
\r
8418 GLT_MoveSelection( hDlg, -1 );
\r
8421 case IDC_GLT_Down:
\r
8422 GLT_MoveSelection( hDlg, +1 );
\r
8432 int GameListOptions()
\r
8435 FARPROC lpProc = MakeProcInstance( (FARPROC) GameListOptions_Proc, hInst );
\r
8437 strcpy( lpUserGLT, appData.gameListTags );
\r
8439 result = DialogBoxParam( hInst, MAKEINTRESOURCE(DLG_GameListOptions), hwndMain, (DLGPROC)lpProc, (LPARAM)lpUserGLT );
\r
8441 if( result == 0 ) {
\r
8442 /* [AS] Memory leak here! */
\r
8443 appData.gameListTags = strdup( lpUserGLT );
\r
8450 DisplayIcsInteractionTitle(char *str)
\r
8452 char consoleTitle[MSG_SIZ];
\r
8454 sprintf(consoleTitle, "%s: %s", szConsoleTitle, str);
\r
8455 SetWindowText(hwndConsole, consoleTitle);
\r
8459 DrawPosition(int fullRedraw, Board board)
\r
8461 HDCDrawPosition(NULL, (BOOLEAN) fullRedraw, board);
\r
8464 void NotifyFrontendLogin()
\r
8467 UpdateICSWidth(GetDlgItem(hwndConsole, OPT_ConsoleText));
\r
8473 fromX = fromY = -1;
\r
8474 if (dragInfo.pos.x != -1 || dragInfo.pos.y != -1) {
\r
8475 dragInfo.pos.x = dragInfo.pos.y = -1;
\r
8476 dragInfo.pos.x = dragInfo.pos.y = -1;
\r
8477 dragInfo.lastpos = dragInfo.pos;
\r
8478 dragInfo.start.x = dragInfo.start.y = -1;
\r
8479 dragInfo.from = dragInfo.start;
\r
8481 DrawPosition(TRUE, NULL);
\r
8488 CommentPopUp(char *title, char *str)
\r
8490 HWND hwnd = GetActiveWindow();
\r
8491 EitherCommentPopUp(currentMove, title, str, FALSE); // [HGM] vari: fake move index, rather than 0
\r
8493 SetActiveWindow(hwnd);
\r
8497 CommentPopDown(void)
\r
8499 CheckMenuItem(GetMenu(hwndMain), IDM_EditComment, MF_UNCHECKED);
\r
8500 if (commentDialog) {
\r
8501 ShowWindow(commentDialog, SW_HIDE);
\r
8503 commentUp = FALSE;
\r
8507 EditCommentPopUp(int index, char *title, char *str)
\r
8509 EitherCommentPopUp(index, title, str, TRUE);
\r
8516 MyPlaySound(&sounds[(int)SoundMove]);
\r
8519 VOID PlayIcsWinSound()
\r
8521 MyPlaySound(&sounds[(int)SoundIcsWin]);
\r
8524 VOID PlayIcsLossSound()
\r
8526 MyPlaySound(&sounds[(int)SoundIcsLoss]);
\r
8529 VOID PlayIcsDrawSound()
\r
8531 MyPlaySound(&sounds[(int)SoundIcsDraw]);
\r
8534 VOID PlayIcsUnfinishedSound()
\r
8536 MyPlaySound(&sounds[(int)SoundIcsUnfinished]);
\r
8542 MyPlaySound(&sounds[(int)SoundAlarm]);
\r
8550 consoleEcho = TRUE;
\r
8551 hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
8552 SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&consoleCF);
\r
8553 SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);
\r
8562 consoleEcho = FALSE;
\r
8563 hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
8564 /* This works OK: set text and background both to the same color */
\r
8566 cf.crTextColor = COLOR_ECHOOFF;
\r
8567 SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf);
\r
8568 SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, cf.crTextColor);
\r
8571 /* No Raw()...? */
\r
8573 void Colorize(ColorClass cc, int continuation)
\r
8575 currentColorClass = cc;
\r
8576 consoleCF.dwMask = CFM_COLOR|CFM_BOLD|CFM_ITALIC|CFM_UNDERLINE|CFM_STRIKEOUT;
\r
8577 consoleCF.crTextColor = textAttribs[cc].color;
\r
8578 consoleCF.dwEffects = textAttribs[cc].effects;
\r
8579 if (!continuation) MyPlaySound(&textAttribs[cc].sound);
\r
8585 static char buf[MSG_SIZ];
\r
8586 DWORD bufsiz = MSG_SIZ;
\r
8588 if(appData.userName != NULL && appData.userName[0] != 0) {
\r
8589 return appData.userName; /* [HGM] username: prefer name selected by user over his system login */
\r
8591 if (!GetUserName(buf, &bufsiz)) {
\r
8592 /*DisplayError("Error getting user name", GetLastError());*/
\r
8593 strcpy(buf, _("User"));
\r
8601 static char buf[MSG_SIZ];
\r
8602 DWORD bufsiz = MSG_SIZ;
\r
8604 if (!GetComputerName(buf, &bufsiz)) {
\r
8605 /*DisplayError("Error getting host name", GetLastError());*/
\r
8606 strcpy(buf, _("Unknown"));
\r
8613 ClockTimerRunning()
\r
8615 return clockTimerEvent != 0;
\r
8621 if (clockTimerEvent == 0) return FALSE;
\r
8622 KillTimer(hwndMain, clockTimerEvent);
\r
8623 clockTimerEvent = 0;
\r
8628 StartClockTimer(long millisec)
\r
8630 clockTimerEvent = SetTimer(hwndMain, (UINT) CLOCK_TIMER_ID,
\r
8631 (UINT) millisec, NULL);
\r
8635 DisplayWhiteClock(long timeRemaining, int highlight)
\r
8638 char *flag = whiteFlag && gameMode == TwoMachinesPlay ? "(!)" : "";
\r
8640 if(appData.noGUI) return;
\r
8641 hdc = GetDC(hwndMain);
\r
8642 if (!IsIconic(hwndMain)) {
\r
8643 DisplayAClock(hdc, timeRemaining, highlight,
\r
8644 flipClock ? &blackRect : &whiteRect, _("White"), flag);
\r
8646 if (highlight && iconCurrent == iconBlack) {
\r
8647 iconCurrent = iconWhite;
\r
8648 PostMessage(hwndMain, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);
\r
8649 if (IsIconic(hwndMain)) {
\r
8650 DrawIcon(hdc, 2, 2, iconCurrent);
\r
8653 (void) ReleaseDC(hwndMain, hdc);
\r
8655 PostMessage(hwndConsole, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);
\r
8659 DisplayBlackClock(long timeRemaining, int highlight)
\r
8662 char *flag = blackFlag && gameMode == TwoMachinesPlay ? "(!)" : "";
\r
8664 if(appData.noGUI) return;
\r
8665 hdc = GetDC(hwndMain);
\r
8666 if (!IsIconic(hwndMain)) {
\r
8667 DisplayAClock(hdc, timeRemaining, highlight,
\r
8668 flipClock ? &whiteRect : &blackRect, _("Black"), flag);
\r
8670 if (highlight && iconCurrent == iconWhite) {
\r
8671 iconCurrent = iconBlack;
\r
8672 PostMessage(hwndMain, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);
\r
8673 if (IsIconic(hwndMain)) {
\r
8674 DrawIcon(hdc, 2, 2, iconCurrent);
\r
8677 (void) ReleaseDC(hwndMain, hdc);
\r
8679 PostMessage(hwndConsole, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);
\r
8684 LoadGameTimerRunning()
\r
8686 return loadGameTimerEvent != 0;
\r
8690 StopLoadGameTimer()
\r
8692 if (loadGameTimerEvent == 0) return FALSE;
\r
8693 KillTimer(hwndMain, loadGameTimerEvent);
\r
8694 loadGameTimerEvent = 0;
\r
8699 StartLoadGameTimer(long millisec)
\r
8701 loadGameTimerEvent = SetTimer(hwndMain, (UINT) LOAD_GAME_TIMER_ID,
\r
8702 (UINT) millisec, NULL);
\r
8710 char fileTitle[MSG_SIZ];
\r
8712 defName = DefaultFileName(appData.oldSaveStyle ? "gam" : "pgn");
\r
8713 f = OpenFileDialog(hwndMain, "a", defName,
\r
8714 appData.oldSaveStyle ? "gam" : "pgn",
\r
8716 _("Save Game to File"), NULL, fileTitle, NULL);
\r
8718 SaveGame(f, 0, "");
\r
8725 ScheduleDelayedEvent(DelayedEventCallback cb, long millisec)
\r
8727 if (delayedTimerEvent != 0) {
\r
8728 if (appData.debugMode && cb != delayedTimerCallback) { // [HGM] alive: not too much debug
\r
8729 fprintf(debugFP, "ScheduleDelayedEvent: event already scheduled\n");
\r
8731 KillTimer(hwndMain, delayedTimerEvent);
\r
8732 delayedTimerEvent = 0;
\r
8733 if(delayedTimerCallback != cb) // [HGM] alive: do not "flush" same event, just postpone it
\r
8734 delayedTimerCallback();
\r
8736 delayedTimerCallback = cb;
\r
8737 delayedTimerEvent = SetTimer(hwndMain, (UINT) DELAYED_TIMER_ID,
\r
8738 (UINT) millisec, NULL);
\r
8741 DelayedEventCallback
\r
8744 if (delayedTimerEvent) {
\r
8745 return delayedTimerCallback;
\r
8752 CancelDelayedEvent()
\r
8754 if (delayedTimerEvent) {
\r
8755 KillTimer(hwndMain, delayedTimerEvent);
\r
8756 delayedTimerEvent = 0;
\r
8760 DWORD GetWin32Priority(int nice)
\r
8761 { // [HGM] nice: translate Unix nice() value to indows priority class. (Code stolen from Polyglot 1.4w11)
\r
8763 REALTIME_PRIORITY_CLASS 0x00000100
\r
8764 HIGH_PRIORITY_CLASS 0x00000080
\r
8765 ABOVE_NORMAL_PRIORITY_CLASS 0x00008000
\r
8766 NORMAL_PRIORITY_CLASS 0x00000020
\r
8767 BELOW_NORMAL_PRIORITY_CLASS 0x00004000
\r
8768 IDLE_PRIORITY_CLASS 0x00000040
\r
8770 if (nice < -15) return 0x00000080;
\r
8771 if (nice < 0) return 0x00008000;
\r
8772 if (nice == 0) return 0x00000020;
\r
8773 if (nice < 15) return 0x00004000;
\r
8774 return 0x00000040;
\r
8777 /* Start a child process running the given program.
\r
8778 The process's standard output can be read from "from", and its
\r
8779 standard input can be written to "to".
\r
8780 Exit with fatal error if anything goes wrong.
\r
8781 Returns an opaque pointer that can be used to destroy the process
\r
8785 StartChildProcess(char *cmdLine, char *dir, ProcRef *pr)
\r
8787 #define BUFSIZE 4096
\r
8789 HANDLE hChildStdinRd, hChildStdinWr,
\r
8790 hChildStdoutRd, hChildStdoutWr;
\r
8791 HANDLE hChildStdinWrDup, hChildStdoutRdDup;
\r
8792 SECURITY_ATTRIBUTES saAttr;
\r
8794 PROCESS_INFORMATION piProcInfo;
\r
8795 STARTUPINFO siStartInfo;
\r
8797 char buf[MSG_SIZ];
\r
8800 if (appData.debugMode) {
\r
8801 fprintf(debugFP, "StartChildProcess (dir=\"%s\") %s\n", dir, cmdLine);
\r
8806 /* Set the bInheritHandle flag so pipe handles are inherited. */
\r
8807 saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);
\r
8808 saAttr.bInheritHandle = TRUE;
\r
8809 saAttr.lpSecurityDescriptor = NULL;
\r
8812 * The steps for redirecting child's STDOUT:
\r
8813 * 1. Create anonymous pipe to be STDOUT for child.
\r
8814 * 2. Create a noninheritable duplicate of read handle,
\r
8815 * and close the inheritable read handle.
\r
8818 /* Create a pipe for the child's STDOUT. */
\r
8819 if (! CreatePipe(&hChildStdoutRd, &hChildStdoutWr, &saAttr, 0)) {
\r
8820 return GetLastError();
\r
8823 /* Duplicate the read handle to the pipe, so it is not inherited. */
\r
8824 fSuccess = DuplicateHandle(GetCurrentProcess(), hChildStdoutRd,
\r
8825 GetCurrentProcess(), &hChildStdoutRdDup, 0,
\r
8826 FALSE, /* not inherited */
\r
8827 DUPLICATE_SAME_ACCESS);
\r
8829 return GetLastError();
\r
8831 CloseHandle(hChildStdoutRd);
\r
8834 * The steps for redirecting child's STDIN:
\r
8835 * 1. Create anonymous pipe to be STDIN for child.
\r
8836 * 2. Create a noninheritable duplicate of write handle,
\r
8837 * and close the inheritable write handle.
\r
8840 /* Create a pipe for the child's STDIN. */
\r
8841 if (! CreatePipe(&hChildStdinRd, &hChildStdinWr, &saAttr, 0)) {
\r
8842 return GetLastError();
\r
8845 /* Duplicate the write handle to the pipe, so it is not inherited. */
\r
8846 fSuccess = DuplicateHandle(GetCurrentProcess(), hChildStdinWr,
\r
8847 GetCurrentProcess(), &hChildStdinWrDup, 0,
\r
8848 FALSE, /* not inherited */
\r
8849 DUPLICATE_SAME_ACCESS);
\r
8851 return GetLastError();
\r
8853 CloseHandle(hChildStdinWr);
\r
8855 /* Arrange to (1) look in dir for the child .exe file, and
\r
8856 * (2) have dir be the child's working directory. Interpret
\r
8857 * dir relative to the directory WinBoard loaded from. */
\r
8858 GetCurrentDirectory(MSG_SIZ, buf);
\r
8859 SetCurrentDirectory(installDir);
\r
8860 SetCurrentDirectory(dir);
\r
8862 /* Now create the child process. */
\r
8864 siStartInfo.cb = sizeof(STARTUPINFO);
\r
8865 siStartInfo.lpReserved = NULL;
\r
8866 siStartInfo.lpDesktop = NULL;
\r
8867 siStartInfo.lpTitle = NULL;
\r
8868 siStartInfo.dwFlags = STARTF_USESTDHANDLES;
\r
8869 siStartInfo.cbReserved2 = 0;
\r
8870 siStartInfo.lpReserved2 = NULL;
\r
8871 siStartInfo.hStdInput = hChildStdinRd;
\r
8872 siStartInfo.hStdOutput = hChildStdoutWr;
\r
8873 siStartInfo.hStdError = hChildStdoutWr;
\r
8875 fSuccess = CreateProcess(NULL,
\r
8876 cmdLine, /* command line */
\r
8877 NULL, /* process security attributes */
\r
8878 NULL, /* primary thread security attrs */
\r
8879 TRUE, /* handles are inherited */
\r
8880 DETACHED_PROCESS|CREATE_NEW_PROCESS_GROUP,
\r
8881 NULL, /* use parent's environment */
\r
8883 &siStartInfo, /* STARTUPINFO pointer */
\r
8884 &piProcInfo); /* receives PROCESS_INFORMATION */
\r
8886 err = GetLastError();
\r
8887 SetCurrentDirectory(buf); /* return to prev directory */
\r
8892 if (appData.niceEngines){ // [HGM] nice: adjust engine proc priority
\r
8893 if(appData.debugMode) fprintf(debugFP, "nice engine proc to %d\n", appData.niceEngines);
\r
8894 SetPriorityClass(piProcInfo.hProcess, GetWin32Priority(appData.niceEngines));
\r
8897 /* Close the handles we don't need in the parent */
\r
8898 CloseHandle(piProcInfo.hThread);
\r
8899 CloseHandle(hChildStdinRd);
\r
8900 CloseHandle(hChildStdoutWr);
\r
8902 /* Prepare return value */
\r
8903 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
\r
8904 cp->kind = CPReal;
\r
8905 cp->hProcess = piProcInfo.hProcess;
\r
8906 cp->pid = piProcInfo.dwProcessId;
\r
8907 cp->hFrom = hChildStdoutRdDup;
\r
8908 cp->hTo = hChildStdinWrDup;
\r
8910 *pr = (void *) cp;
\r
8912 /* Klaus Friedel says that this Sleep solves a problem under Windows
\r
8913 2000 where engines sometimes don't see the initial command(s)
\r
8914 from WinBoard and hang. I don't understand how that can happen,
\r
8915 but the Sleep is harmless, so I've put it in. Others have also
\r
8916 reported what may be the same problem, so hopefully this will fix
\r
8917 it for them too. */
\r
8925 DestroyChildProcess(ProcRef pr, int/*boolean*/ signal)
\r
8927 ChildProc *cp; int result;
\r
8929 cp = (ChildProc *) pr;
\r
8930 if (cp == NULL) return;
\r
8932 switch (cp->kind) {
\r
8934 /* TerminateProcess is considered harmful, so... */
\r
8935 CloseHandle(cp->hTo); /* Closing this will give the child an EOF and hopefully kill it */
\r
8936 if (cp->hFrom) CloseHandle(cp->hFrom); /* if NULL, InputThread will close it */
\r
8937 /* The following doesn't work because the chess program
\r
8938 doesn't "have the same console" as WinBoard. Maybe
\r
8939 we could arrange for this even though neither WinBoard
\r
8940 nor the chess program uses a console for stdio? */
\r
8941 /*!!if (signal) GenerateConsoleCtrlEvent(CTRL_BREAK_EVENT, cp->pid);*/
\r
8943 /* [AS] Special termination modes for misbehaving programs... */
\r
8944 if( signal == 9 ) {
\r
8945 result = TerminateProcess( cp->hProcess, 0 );
\r
8947 if ( appData.debugMode) {
\r
8948 fprintf( debugFP, "Terminating process %lu, result=%d\n", cp->pid, result );
\r
8951 else if( signal == 10 ) {
\r
8952 DWORD dw = WaitForSingleObject( cp->hProcess, 3*1000 ); // Wait 3 seconds at most
\r
8954 if( dw != WAIT_OBJECT_0 ) {
\r
8955 result = TerminateProcess( cp->hProcess, 0 );
\r
8957 if ( appData.debugMode) {
\r
8958 fprintf( debugFP, "Process %lu still alive after timeout, killing... result=%d\n", cp->pid, result );
\r
8964 CloseHandle(cp->hProcess);
\r
8968 if (cp->hFrom) CloseHandle(cp->hFrom);
\r
8972 closesocket(cp->sock);
\r
8977 if (signal) send(cp->sock2, "\017", 1, 0); /* 017 = 15 = SIGTERM */
\r
8978 closesocket(cp->sock);
\r
8979 closesocket(cp->sock2);
\r
8987 InterruptChildProcess(ProcRef pr)
\r
8991 cp = (ChildProc *) pr;
\r
8992 if (cp == NULL) return;
\r
8993 switch (cp->kind) {
\r
8995 /* The following doesn't work because the chess program
\r
8996 doesn't "have the same console" as WinBoard. Maybe
\r
8997 we could arrange for this even though neither WinBoard
\r
8998 nor the chess program uses a console for stdio */
\r
8999 /*!!GenerateConsoleCtrlEvent(CTRL_C_EVENT, cp->pid);*/
\r
9004 /* Can't interrupt */
\r
9008 send(cp->sock2, "\002", 1, 0); /* 2 = SIGINT */
\r
9015 OpenTelnet(char *host, char *port, ProcRef *pr)
\r
9017 char cmdLine[MSG_SIZ];
\r
9019 if (port[0] == NULLCHAR) {
\r
9020 sprintf(cmdLine, "%s %s", appData.telnetProgram, host);
\r
9022 sprintf(cmdLine, "%s %s %s", appData.telnetProgram, host, port);
\r
9024 return StartChildProcess(cmdLine, "", pr);
\r
9028 /* Code to open TCP sockets */
\r
9031 OpenTCP(char *host, char *port, ProcRef *pr)
\r
9036 struct sockaddr_in sa, mysa;
\r
9037 struct hostent FAR *hp;
\r
9038 unsigned short uport;
\r
9039 WORD wVersionRequested;
\r
9042 /* Initialize socket DLL */
\r
9043 wVersionRequested = MAKEWORD(1, 1);
\r
9044 err = WSAStartup(wVersionRequested, &wsaData);
\r
9045 if (err != 0) return err;
\r
9048 if ((s = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {
\r
9049 err = WSAGetLastError();
\r
9054 /* Bind local address using (mostly) don't-care values.
\r
9056 memset((char *) &mysa, 0, sizeof(struct sockaddr_in));
\r
9057 mysa.sin_family = AF_INET;
\r
9058 mysa.sin_addr.s_addr = INADDR_ANY;
\r
9059 uport = (unsigned short) 0;
\r
9060 mysa.sin_port = htons(uport);
\r
9061 if (bind(s, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))
\r
9062 == SOCKET_ERROR) {
\r
9063 err = WSAGetLastError();
\r
9068 /* Resolve remote host name */
\r
9069 memset((char *) &sa, 0, sizeof(struct sockaddr_in));
\r
9070 if (!(hp = gethostbyname(host))) {
\r
9071 unsigned int b0, b1, b2, b3;
\r
9073 err = WSAGetLastError();
\r
9075 if (sscanf(host, "%u.%u.%u.%u", &b0, &b1, &b2, &b3) == 4) {
\r
9076 hp = (struct hostent *) calloc(1, sizeof(struct hostent));
\r
9077 hp->h_addrtype = AF_INET;
\r
9079 hp->h_addr_list = (char **) calloc(2, sizeof(char *));
\r
9080 hp->h_addr_list[0] = (char *) malloc(4);
\r
9081 hp->h_addr_list[0][0] = (char) b0;
\r
9082 hp->h_addr_list[0][1] = (char) b1;
\r
9083 hp->h_addr_list[0][2] = (char) b2;
\r
9084 hp->h_addr_list[0][3] = (char) b3;
\r
9090 sa.sin_family = hp->h_addrtype;
\r
9091 uport = (unsigned short) atoi(port);
\r
9092 sa.sin_port = htons(uport);
\r
9093 memcpy((char *) &sa.sin_addr, hp->h_addr, hp->h_length);
\r
9095 /* Make connection */
\r
9096 if (connect(s, (struct sockaddr *) &sa,
\r
9097 sizeof(struct sockaddr_in)) == SOCKET_ERROR) {
\r
9098 err = WSAGetLastError();
\r
9103 /* Prepare return value */
\r
9104 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
\r
9105 cp->kind = CPSock;
\r
9107 *pr = (ProcRef *) cp;
\r
9113 OpenCommPort(char *name, ProcRef *pr)
\r
9118 char fullname[MSG_SIZ];
\r
9120 if (*name != '\\')
\r
9121 sprintf(fullname, "\\\\.\\%s", name);
\r
9123 strcpy(fullname, name);
\r
9125 h = CreateFile(name, GENERIC_READ | GENERIC_WRITE,
\r
9126 0, NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL);
\r
9127 if (h == (HANDLE) -1) {
\r
9128 return GetLastError();
\r
9132 if (!SetCommState(h, (LPDCB) &dcb)) return GetLastError();
\r
9134 /* Accumulate characters until a 100ms pause, then parse */
\r
9135 ct.ReadIntervalTimeout = 100;
\r
9136 ct.ReadTotalTimeoutMultiplier = 0;
\r
9137 ct.ReadTotalTimeoutConstant = 0;
\r
9138 ct.WriteTotalTimeoutMultiplier = 0;
\r
9139 ct.WriteTotalTimeoutConstant = 0;
\r
9140 if (!SetCommTimeouts(h, (LPCOMMTIMEOUTS) &ct)) return GetLastError();
\r
9142 /* Prepare return value */
\r
9143 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
\r
9144 cp->kind = CPComm;
\r
9147 *pr = (ProcRef *) cp;
\r
9153 OpenLoopback(ProcRef *pr)
\r
9155 DisplayFatalError(_("Not implemented"), 0, 1);
\r
9161 OpenRcmd(char* host, char* user, char* cmd, ProcRef* pr)
\r
9166 struct sockaddr_in sa, mysa;
\r
9167 struct hostent FAR *hp;
\r
9168 unsigned short uport;
\r
9169 WORD wVersionRequested;
\r
9172 char stderrPortStr[MSG_SIZ];
\r
9174 /* Initialize socket DLL */
\r
9175 wVersionRequested = MAKEWORD(1, 1);
\r
9176 err = WSAStartup(wVersionRequested, &wsaData);
\r
9177 if (err != 0) return err;
\r
9179 /* Resolve remote host name */
\r
9180 memset((char *) &sa, 0, sizeof(struct sockaddr_in));
\r
9181 if (!(hp = gethostbyname(host))) {
\r
9182 unsigned int b0, b1, b2, b3;
\r
9184 err = WSAGetLastError();
\r
9186 if (sscanf(host, "%u.%u.%u.%u", &b0, &b1, &b2, &b3) == 4) {
\r
9187 hp = (struct hostent *) calloc(1, sizeof(struct hostent));
\r
9188 hp->h_addrtype = AF_INET;
\r
9190 hp->h_addr_list = (char **) calloc(2, sizeof(char *));
\r
9191 hp->h_addr_list[0] = (char *) malloc(4);
\r
9192 hp->h_addr_list[0][0] = (char) b0;
\r
9193 hp->h_addr_list[0][1] = (char) b1;
\r
9194 hp->h_addr_list[0][2] = (char) b2;
\r
9195 hp->h_addr_list[0][3] = (char) b3;
\r
9201 sa.sin_family = hp->h_addrtype;
\r
9202 uport = (unsigned short) 514;
\r
9203 sa.sin_port = htons(uport);
\r
9204 memcpy((char *) &sa.sin_addr, hp->h_addr, hp->h_length);
\r
9206 /* Bind local socket to unused "privileged" port address
\r
9208 s = INVALID_SOCKET;
\r
9209 memset((char *) &mysa, 0, sizeof(struct sockaddr_in));
\r
9210 mysa.sin_family = AF_INET;
\r
9211 mysa.sin_addr.s_addr = INADDR_ANY;
\r
9212 for (fromPort = 1023;; fromPort--) {
\r
9213 if (fromPort < 0) {
\r
9215 return WSAEADDRINUSE;
\r
9217 if (s == INVALID_SOCKET) {
\r
9218 if ((s = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {
\r
9219 err = WSAGetLastError();
\r
9224 uport = (unsigned short) fromPort;
\r
9225 mysa.sin_port = htons(uport);
\r
9226 if (bind(s, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))
\r
9227 == SOCKET_ERROR) {
\r
9228 err = WSAGetLastError();
\r
9229 if (err == WSAEADDRINUSE) continue;
\r
9233 if (connect(s, (struct sockaddr *) &sa,
\r
9234 sizeof(struct sockaddr_in)) == SOCKET_ERROR) {
\r
9235 err = WSAGetLastError();
\r
9236 if (err == WSAEADDRINUSE) {
\r
9247 /* Bind stderr local socket to unused "privileged" port address
\r
9249 s2 = INVALID_SOCKET;
\r
9250 memset((char *) &mysa, 0, sizeof(struct sockaddr_in));
\r
9251 mysa.sin_family = AF_INET;
\r
9252 mysa.sin_addr.s_addr = INADDR_ANY;
\r
9253 for (fromPort = 1023;; fromPort--) {
\r
9254 if (fromPort == prevStderrPort) continue; // don't reuse port
\r
9255 if (fromPort < 0) {
\r
9256 (void) closesocket(s);
\r
9258 return WSAEADDRINUSE;
\r
9260 if (s2 == INVALID_SOCKET) {
\r
9261 if ((s2 = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {
\r
9262 err = WSAGetLastError();
\r
9268 uport = (unsigned short) fromPort;
\r
9269 mysa.sin_port = htons(uport);
\r
9270 if (bind(s2, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))
\r
9271 == SOCKET_ERROR) {
\r
9272 err = WSAGetLastError();
\r
9273 if (err == WSAEADDRINUSE) continue;
\r
9274 (void) closesocket(s);
\r
9278 if (listen(s2, 1) == SOCKET_ERROR) {
\r
9279 err = WSAGetLastError();
\r
9280 if (err == WSAEADDRINUSE) {
\r
9282 s2 = INVALID_SOCKET;
\r
9285 (void) closesocket(s);
\r
9286 (void) closesocket(s2);
\r
9292 prevStderrPort = fromPort; // remember port used
\r
9293 sprintf(stderrPortStr, "%d", fromPort);
\r
9295 if (send(s, stderrPortStr, strlen(stderrPortStr) + 1, 0) == SOCKET_ERROR) {
\r
9296 err = WSAGetLastError();
\r
9297 (void) closesocket(s);
\r
9298 (void) closesocket(s2);
\r
9303 if (send(s, UserName(), strlen(UserName()) + 1, 0) == SOCKET_ERROR) {
\r
9304 err = WSAGetLastError();
\r
9305 (void) closesocket(s);
\r
9306 (void) closesocket(s2);
\r
9310 if (*user == NULLCHAR) user = UserName();
\r
9311 if (send(s, user, strlen(user) + 1, 0) == SOCKET_ERROR) {
\r
9312 err = WSAGetLastError();
\r
9313 (void) closesocket(s);
\r
9314 (void) closesocket(s2);
\r
9318 if (send(s, cmd, strlen(cmd) + 1, 0) == SOCKET_ERROR) {
\r
9319 err = WSAGetLastError();
\r
9320 (void) closesocket(s);
\r
9321 (void) closesocket(s2);
\r
9326 if ((s3 = accept(s2, NULL, NULL)) == INVALID_SOCKET) {
\r
9327 err = WSAGetLastError();
\r
9328 (void) closesocket(s);
\r
9329 (void) closesocket(s2);
\r
9333 (void) closesocket(s2); /* Stop listening */
\r
9335 /* Prepare return value */
\r
9336 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
\r
9337 cp->kind = CPRcmd;
\r
9340 *pr = (ProcRef *) cp;
\r
9347 AddInputSource(ProcRef pr, int lineByLine,
\r
9348 InputCallback func, VOIDSTAR closure)
\r
9350 InputSource *is, *is2 = NULL;
\r
9351 ChildProc *cp = (ChildProc *) pr;
\r
9353 is = (InputSource *) calloc(1, sizeof(InputSource));
\r
9354 is->lineByLine = lineByLine;
\r
9356 is->closure = closure;
\r
9357 is->second = NULL;
\r
9358 is->next = is->buf;
\r
9359 if (pr == NoProc) {
\r
9360 is->kind = CPReal;
\r
9361 consoleInputSource = is;
\r
9363 is->kind = cp->kind;
\r
9365 [AS] Try to avoid a race condition if the thread is given control too early:
\r
9366 we create all threads suspended so that the is->hThread variable can be
\r
9367 safely assigned, then let the threads start with ResumeThread.
\r
9369 switch (cp->kind) {
\r
9371 is->hFile = cp->hFrom;
\r
9372 cp->hFrom = NULL; /* now owned by InputThread */
\r
9374 CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) NonOvlInputThread,
\r
9375 (LPVOID) is, CREATE_SUSPENDED, &is->id);
\r
9379 is->hFile = cp->hFrom;
\r
9380 cp->hFrom = NULL; /* now owned by InputThread */
\r
9382 CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) InputThread,
\r
9383 (LPVOID) is, CREATE_SUSPENDED, &is->id);
\r
9387 is->sock = cp->sock;
\r
9389 CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,
\r
9390 (LPVOID) is, CREATE_SUSPENDED, &is->id);
\r
9394 is2 = (InputSource *) calloc(1, sizeof(InputSource));
\r
9396 is->sock = cp->sock;
\r
9398 is2->sock = cp->sock2;
\r
9399 is2->second = is2;
\r
9401 CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,
\r
9402 (LPVOID) is, CREATE_SUSPENDED, &is->id);
\r
9404 CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,
\r
9405 (LPVOID) is2, CREATE_SUSPENDED, &is2->id);
\r
9409 if( is->hThread != NULL ) {
\r
9410 ResumeThread( is->hThread );
\r
9413 if( is2 != NULL && is2->hThread != NULL ) {
\r
9414 ResumeThread( is2->hThread );
\r
9418 return (InputSourceRef) is;
\r
9422 RemoveInputSource(InputSourceRef isr)
\r
9426 is = (InputSource *) isr;
\r
9427 is->hThread = NULL; /* tell thread to stop */
\r
9428 CloseHandle(is->hThread);
\r
9429 if (is->second != NULL) {
\r
9430 is->second->hThread = NULL;
\r
9431 CloseHandle(is->second->hThread);
\r
9435 int no_wrap(char *message, int count)
\r
9437 ConsoleOutput(message, count, FALSE);
\r
9442 OutputToProcess(ProcRef pr, char *message, int count, int *outError)
\r
9445 int outCount = SOCKET_ERROR;
\r
9446 ChildProc *cp = (ChildProc *) pr;
\r
9447 static OVERLAPPED ovl;
\r
9448 static int line = 0;
\r
9452 if (appData.noJoin || !appData.useInternalWrap)
\r
9453 return no_wrap(message, count);
\r
9456 int width = get_term_width();
\r
9457 int len = wrap(NULL, message, count, width, &line);
\r
9458 char *msg = malloc(len);
\r
9462 return no_wrap(message, count);
\r
9465 dbgchk = wrap(msg, message, count, width, &line);
\r
9466 if (dbgchk != len && appData.debugMode)
\r
9467 fprintf(debugFP, "wrap(): dbgchk(%d) != len(%d)\n", dbgchk, len);
\r
9468 ConsoleOutput(msg, len, FALSE);
\r
9475 if (ovl.hEvent == NULL) {
\r
9476 ovl.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
\r
9478 ovl.Internal = ovl.InternalHigh = ovl.Offset = ovl.OffsetHigh = 0;
\r
9480 switch (cp->kind) {
\r
9483 outCount = send(cp->sock, message, count, 0);
\r
9484 if (outCount == SOCKET_ERROR) {
\r
9485 *outError = WSAGetLastError();
\r
9487 *outError = NO_ERROR;
\r
9492 if (WriteFile(((ChildProc *)pr)->hTo, message, count,
\r
9493 &dOutCount, NULL)) {
\r
9494 *outError = NO_ERROR;
\r
9495 outCount = (int) dOutCount;
\r
9497 *outError = GetLastError();
\r
9502 *outError = DoWriteFile(((ChildProc *)pr)->hTo, message, count,
\r
9503 &dOutCount, &ovl);
\r
9504 if (*outError == NO_ERROR) {
\r
9505 outCount = (int) dOutCount;
\r
9513 OutputToProcessDelayed(ProcRef pr, char *message, int count, int *outError,
\r
9516 /* Ignore delay, not implemented for WinBoard */
\r
9517 return OutputToProcess(pr, message, count, outError);
\r
9522 CmailSigHandlerCallBack(InputSourceRef isr, VOIDSTAR closure,
\r
9523 char *buf, int count, int error)
\r
9525 DisplayFatalError(_("Not implemented"), 0, 1);
\r
9528 /* see wgamelist.c for Game List functions */
\r
9529 /* see wedittags.c for Edit Tags functions */
\r
9536 char buf[MSG_SIZ];
\r
9539 if (SearchPath(installDir, appData.icsLogon, NULL, MSG_SIZ, buf, &dummy)) {
\r
9540 f = fopen(buf, "r");
\r
9542 ProcessICSInitScript(f);
\r
9550 StartAnalysisClock()
\r
9552 if (analysisTimerEvent) return;
\r
9553 analysisTimerEvent = SetTimer(hwndMain, (UINT) ANALYSIS_TIMER_ID,
\r
9554 (UINT) 2000, NULL);
\r
9558 SetHighlights(int fromX, int fromY, int toX, int toY)
\r
9560 highlightInfo.sq[0].x = fromX;
\r
9561 highlightInfo.sq[0].y = fromY;
\r
9562 highlightInfo.sq[1].x = toX;
\r
9563 highlightInfo.sq[1].y = toY;
\r
9569 highlightInfo.sq[0].x = highlightInfo.sq[0].y =
\r
9570 highlightInfo.sq[1].x = highlightInfo.sq[1].y = -1;
\r
9574 SetPremoveHighlights(int fromX, int fromY, int toX, int toY)
\r
9576 premoveHighlightInfo.sq[0].x = fromX;
\r
9577 premoveHighlightInfo.sq[0].y = fromY;
\r
9578 premoveHighlightInfo.sq[1].x = toX;
\r
9579 premoveHighlightInfo.sq[1].y = toY;
\r
9583 ClearPremoveHighlights()
\r
9585 premoveHighlightInfo.sq[0].x = premoveHighlightInfo.sq[0].y =
\r
9586 premoveHighlightInfo.sq[1].x = premoveHighlightInfo.sq[1].y = -1;
\r
9590 ShutDownFrontEnd()
\r
9592 if (saveSettingsOnExit) SaveSettings(settingsFileName);
\r
9593 DeleteClipboardTempFiles();
\r
9599 if (IsIconic(hwndMain))
\r
9600 ShowWindow(hwndMain, SW_RESTORE);
\r
9602 SetActiveWindow(hwndMain);
\r
9606 * Prototypes for animation support routines
\r
9608 static void ScreenSquare(int column, int row, POINT * pt);
\r
9609 static void Tween( POINT * start, POINT * mid, POINT * finish, int factor,
\r
9610 POINT frames[], int * nFrames);
\r
9614 AnimateAtomicCapture(int fromX, int fromY, int toX, int toY, int nFrames)
\r
9615 { // [HGM] atomic: animate blast wave
\r
9617 if(appData.debugMode) fprintf(debugFP, "exploding (%d,%d)\n", toX, toY);
\r
9618 explodeInfo.fromX = fromX;
\r
9619 explodeInfo.fromY = fromY;
\r
9620 explodeInfo.toX = toX;
\r
9621 explodeInfo.toY = toY;
\r
9622 for(i=1; i<nFrames; i++) {
\r
9623 explodeInfo.radius = (i*180)/(nFrames-1);
\r
9624 DrawPosition(FALSE, NULL);
\r
9625 Sleep(appData.animSpeed);
\r
9627 explodeInfo.radius = 0;
\r
9628 DrawPosition(TRUE, NULL);
\r
9634 AnimateMove(board, fromX, fromY, toX, toY)
\r
9641 ChessSquare piece;
\r
9642 POINT start, finish, mid;
\r
9643 POINT frames[kFactor * 2 + 1];
\r
9646 if (!appData.animate) return;
\r
9647 if (doingSizing) return;
\r
9648 if (fromY < 0 || fromX < 0) return;
\r
9649 piece = board[fromY][fromX];
\r
9650 if (piece >= EmptySquare) return;
\r
9652 ScreenSquare(fromX, fromY, &start);
\r
9653 ScreenSquare(toX, toY, &finish);
\r
9655 /* All pieces except knights move in straight line */
\r
9656 if (piece != WhiteKnight && piece != BlackKnight) {
\r
9657 mid.x = start.x + (finish.x - start.x) / 2;
\r
9658 mid.y = start.y + (finish.y - start.y) / 2;
\r
9660 /* Knight: make diagonal movement then straight */
\r
9661 if (abs(toY - fromY) < abs(toX - fromX)) {
\r
9662 mid.x = start.x + (finish.x - start.x) / 2;
\r
9666 mid.y = start.y + (finish.y - start.y) / 2;
\r
9670 /* Don't use as many frames for very short moves */
\r
9671 if (abs(toY - fromY) + abs(toX - fromX) <= 2)
\r
9672 Tween(&start, &mid, &finish, kFactor - 1, frames, &nFrames);
\r
9674 Tween(&start, &mid, &finish, kFactor, frames, &nFrames);
\r
9676 animInfo.from.x = fromX;
\r
9677 animInfo.from.y = fromY;
\r
9678 animInfo.to.x = toX;
\r
9679 animInfo.to.y = toY;
\r
9680 animInfo.lastpos = start;
\r
9681 animInfo.piece = piece;
\r
9682 for (n = 0; n < nFrames; n++) {
\r
9683 animInfo.pos = frames[n];
\r
9684 DrawPosition(FALSE, NULL);
\r
9685 animInfo.lastpos = animInfo.pos;
\r
9686 Sleep(appData.animSpeed);
\r
9688 animInfo.pos = finish;
\r
9689 DrawPosition(FALSE, NULL);
\r
9690 animInfo.piece = EmptySquare;
\r
9691 if(gameInfo.variant == VariantAtomic &&
\r
9692 (board[toY][toX] != EmptySquare || fromX != toX && (piece == WhitePawn || piece == BlackPawn) ) )
\r
9693 AnimateAtomicCapture(fromX, fromY, toX, toY, 2*nFrames);
\r
9696 /* Convert board position to corner of screen rect and color */
\r
9699 ScreenSquare(column, row, pt)
\r
9700 int column; int row; POINT * pt;
\r
9703 pt->x = lineGap + ((BOARD_WIDTH-1)-column) * (squareSize + lineGap);
\r
9704 pt->y = lineGap + row * (squareSize + lineGap);
\r
9706 pt->x = lineGap + column * (squareSize + lineGap);
\r
9707 pt->y = lineGap + ((BOARD_HEIGHT-1)-row) * (squareSize + lineGap);
\r
9711 /* Generate a series of frame coords from start->mid->finish.
\r
9712 The movement rate doubles until the half way point is
\r
9713 reached, then halves back down to the final destination,
\r
9714 which gives a nice slow in/out effect. The algorithmn
\r
9715 may seem to generate too many intermediates for short
\r
9716 moves, but remember that the purpose is to attract the
\r
9717 viewers attention to the piece about to be moved and
\r
9718 then to where it ends up. Too few frames would be less
\r
9722 Tween(start, mid, finish, factor, frames, nFrames)
\r
9723 POINT * start; POINT * mid;
\r
9724 POINT * finish; int factor;
\r
9725 POINT frames[]; int * nFrames;
\r
9727 int n, fraction = 1, count = 0;
\r
9729 /* Slow in, stepping 1/16th, then 1/8th, ... */
\r
9730 for (n = 0; n < factor; n++)
\r
9732 for (n = 0; n < factor; n++) {
\r
9733 frames[count].x = start->x + (mid->x - start->x) / fraction;
\r
9734 frames[count].y = start->y + (mid->y - start->y) / fraction;
\r
9736 fraction = fraction / 2;
\r
9740 frames[count] = *mid;
\r
9743 /* Slow out, stepping 1/2, then 1/4, ... */
\r
9745 for (n = 0; n < factor; n++) {
\r
9746 frames[count].x = finish->x - (finish->x - mid->x) / fraction;
\r
9747 frames[count].y = finish->y - (finish->y - mid->y) / fraction;
\r
9749 fraction = fraction * 2;
\r
9755 HistorySet( char movelist[][2*MOVE_LEN], int first, int last, int current )
\r
9757 MoveHistorySet( movelist, first, last, current, pvInfoList );
\r
9759 EvalGraphSet( first, last, current, pvInfoList );
\r