2 * WinBoard.c -- Windows NT front end to XBoard
\r
4 * Copyright 1991 by Digital Equipment Corporation, Maynard,
\r
7 * Enhancements Copyright 1992-2001, 2002, 2003, 2004, 2005, 2006,
\r
8 * 2007, 2008, 2009, 2010, 2011 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
84 #include "frontend.h"
\r
85 #include "backend.h"
\r
86 #include "winboard.h"
\r
88 #include "wclipbrd.h"
\r
89 #include "woptions.h"
\r
90 #include "wsockerr.h"
\r
91 #include "defaults.h"
\r
95 //void InitEngineUCI( const char * iniDir, ChessProgramState * cps );
\r
98 void mysrandom(unsigned int seed);
\r
100 extern int whiteFlag, blackFlag;
\r
101 Boolean flipClock = FALSE;
\r
102 extern HANDLE chatHandle[];
\r
103 extern int ics_type;
\r
105 void DisplayHoldingsCount(HDC hdc, int x, int y, int align, int copyNumber);
\r
106 VOID NewVariantPopup(HWND hwnd);
\r
107 int FinishMove P((ChessMove moveType, int fromX, int fromY, int toX, int toY,
\r
108 /*char*/int promoChar));
\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
130 static DragInfo dragInfo = { {-1,-1}, {-1,-1}, {-1,-1}, {-1,-1}, EmptySquare };
\r
133 POINT sq[2]; /* board coordinates of from, to squares */
\r
136 static HighlightInfo highlightInfo = { {{-1, -1}, {-1, -1}} };
\r
137 static HighlightInfo premoveHighlightInfo = { {{-1, -1}, {-1, -1}} };
\r
138 static HighlightInfo partnerHighlightInfo = { {{-1, -1}, {-1, -1}} };
\r
139 static HighlightInfo oldPartnerHighlight = { {{-1, -1}, {-1, -1}} };
\r
141 typedef struct { // [HGM] atomic
\r
142 int fromX, fromY, toX, toY, radius;
\r
145 static ExplodeInfo explodeInfo;
\r
147 /* Window class names */
\r
148 char szAppName[] = "WinBoard";
\r
149 char szConsoleName[] = "WBConsole";
\r
151 /* Title bar text */
\r
152 char szTitle[] = "WinBoard";
\r
153 char szConsoleTitle[] = "I C S Interaction";
\r
156 char *settingsFileName;
\r
157 Boolean saveSettingsOnExit;
\r
158 char installDir[MSG_SIZ];
\r
159 int errorExitStatus;
\r
161 BoardSize boardSize;
\r
162 Boolean chessProgram;
\r
163 //static int boardX, boardY;
\r
164 int minX, minY; // [HGM] placement: volatile limits on upper-left corner
\r
165 int squareSize, lineGap, minorSize;
\r
166 static int winW, winH;
\r
167 static RECT messageRect, whiteRect, blackRect, leftLogoRect, rightLogoRect; // [HGM] logo
\r
168 static int logoHeight = 0;
\r
169 static char messageText[MESSAGE_TEXT_MAX];
\r
170 static int clockTimerEvent = 0;
\r
171 static int loadGameTimerEvent = 0;
\r
172 static int analysisTimerEvent = 0;
\r
173 static DelayedEventCallback delayedTimerCallback;
\r
174 static int delayedTimerEvent = 0;
\r
175 static int buttonCount = 2;
\r
176 char *icsTextMenuString;
\r
178 char *firstChessProgramNames;
\r
179 char *secondChessProgramNames;
\r
181 #define PALETTESIZE 256
\r
183 HINSTANCE hInst; /* current instance */
\r
184 Boolean alwaysOnTop = FALSE;
\r
186 COLORREF lightSquareColor, darkSquareColor, whitePieceColor,
\r
187 blackPieceColor, highlightSquareColor, premoveHighlightColor;
\r
189 ColorClass currentColorClass;
\r
191 static HWND savedHwnd;
\r
192 HWND hCommPort = NULL; /* currently open comm port */
\r
193 static HWND hwndPause; /* pause button */
\r
194 static HBITMAP pieceBitmap[3][(int) BlackPawn]; /* [HGM] nr of bitmaps referred to bP in stead of wK */
\r
195 static HBRUSH lightSquareBrush, darkSquareBrush,
\r
196 blackSquareBrush, /* [HGM] for band between board and holdings */
\r
197 explodeBrush, /* [HGM] atomic */
\r
198 markerBrush, /* [HGM] markers */
\r
199 whitePieceBrush, blackPieceBrush, iconBkgndBrush /*, outlineBrush*/;
\r
200 static POINT gridEndpoints[(BOARD_RANKS + BOARD_FILES + 2) * 2];
\r
201 static DWORD gridVertexCounts[BOARD_RANKS + BOARD_FILES + 2];
\r
202 static HPEN gridPen = NULL;
\r
203 static HPEN highlightPen = NULL;
\r
204 static HPEN premovePen = NULL;
\r
205 static NPLOGPALETTE pLogPal;
\r
206 static BOOL paletteChanged = FALSE;
\r
207 static HICON iconWhite, iconBlack, iconCurrent;
\r
208 static int doingSizing = FALSE;
\r
209 static int lastSizing = 0;
\r
210 static int prevStderrPort;
\r
211 static HBITMAP userLogo;
\r
213 static HBITMAP liteBackTexture = NULL;
\r
214 static HBITMAP darkBackTexture = NULL;
\r
215 static int liteBackTextureMode = BACK_TEXTURE_MODE_PLAIN;
\r
216 static int darkBackTextureMode = BACK_TEXTURE_MODE_PLAIN;
\r
217 static int backTextureSquareSize = 0;
\r
218 static struct { int x; int y; int mode; } backTextureSquareInfo[BOARD_RANKS][BOARD_FILES];
\r
220 #if __GNUC__ && !defined(_winmajor)
\r
221 #define oldDialog 0 /* cygwin doesn't define _winmajor; mingw does */
\r
223 #if defined(_winmajor)
\r
224 #define oldDialog (_winmajor < 4)
\r
226 #define oldDialog 0
\r
230 #define INTERNATIONAL
\r
232 #ifdef INTERNATIONAL
\r
233 # define _(s) T_(s)
\r
239 # define Translate(x, y)
\r
240 # define LoadLanguageFile(s)
\r
243 #ifdef INTERNATIONAL
\r
245 Boolean barbaric; // flag indicating if translation is needed
\r
247 // list of item numbers used in each dialog (used to alter language at run time)
\r
249 #define ABOUTBOX -1 /* not sure why these are needed */
\r
250 #define ABOUTBOX2 -1
\r
252 int dialogItems[][41 ] = {
\r
253 { ABOUTBOX, IDOK, OPT_MESS, 400 },
\r
254 { DLG_TimeControl, IDC_Babble, OPT_TCUseMoves, OPT_TCUseInc, OPT_TCUseFixed,
\r
255 OPT_TCtext1, OPT_TCtext2, OPT_TCitext1, OPT_TCitext2, OPT_TCftext, GPB_Factors, IDC_Factor1, IDC_Factor2, IDOK, IDCANCEL },
\r
256 { DLG_LoadOptions, OPT_Autostep, OPT_AStext1, IDOK, IDCANCEL },
\r
257 { DLG_SaveOptions, OPT_Autosave, OPT_AVPrompt, OPT_AVToFile, OPT_AVBrowse,
\r
258 801, OPT_PGN, OPT_Old, OPT_OutOfBookInfo, IDOK, IDCANCEL },
\r
259 { 1536, 1090, IDC_Directories, 1089, 1091, IDOK, IDCANCEL, 1038, IDC_IndexNr, 1037 },
\r
260 { DLG_CommPort, IDOK, IDCANCEL, IDC_Port, IDC_Rate, IDC_Bits, IDC_Parity,
\r
261 IDC_Stop, IDC_Flow, OPT_SerialHelp },
\r
262 { DLG_EditComment, IDOK, OPT_CancelComment, OPT_ClearComment, OPT_EditComment },
\r
263 { DLG_PromotionKing, PB_Chancellor, PB_Archbishop, PB_Queen, PB_Rook,
\r
264 PB_Bishop, PB_Knight, PB_King, IDCANCEL, IDC_Yes, IDC_No, IDC_Centaur },
\r
265 { ABOUTBOX2, IDC_ChessBoard },
\r
266 { DLG_GameList, OPT_GameListLoad, OPT_GameListPrev, OPT_GameListNext,
\r
267 OPT_GameListClose, IDC_GameListDoFilter },
\r
268 { DLG_EditTags, IDOK, OPT_TagsCancel, OPT_EditTags },
\r
269 { DLG_Error, IDOK },
\r
270 { DLG_Colorize, IDOK, IDCANCEL, OPT_ChooseColor, OPT_Bold, OPT_Italic,
\r
271 OPT_Underline, OPT_Strikeout, OPT_Sample },
\r
272 { DLG_Question, IDOK, IDCANCEL, OPT_QuestionText },
\r
273 { DLG_Startup, IDC_Welcome, OPT_ChessEngine, OPT_ChessServer, OPT_View,
\r
274 IDC_SPECIFY_ENG_STATIC, IDC_SPECIFY_SERVER_STATIC, OPT_AnyAdditional,
\r
275 IDOK, IDCANCEL, IDM_HELPCONTENTS },
\r
276 { DLG_IndexNumber, IDC_Index },
\r
277 { DLG_TypeInMove, IDOK, IDCANCEL },
\r
278 { DLG_TypeInName, IDOK, IDCANCEL },
\r
279 { DLG_Sound, IDC_Event, OPT_NoSound, OPT_DefaultBeep, OPT_BuiltInSound,
\r
280 OPT_WavFile, OPT_BrowseSound, OPT_DefaultSounds, IDOK, IDCANCEL, OPT_PlaySound },
\r
281 { DLG_GeneralOptions, IDOK, IDCANCEL, OPT_AlwaysOnTop, OPT_HighlightLastMove,
\r
282 OPT_AlwaysQueen, OPT_PeriodicUpdates, OPT_AnimateDragging, OPT_PonderNextMove,
\r
283 OPT_AnimateMoving, OPT_PopupExitMessage, OPT_AutoFlag, OPT_PopupMoveErrors,
\r
284 OPT_AutoFlipView, OPT_ShowButtonBar, OPT_AutoRaiseBoard, OPT_ShowCoordinates,
\r
285 OPT_Blindfold, OPT_ShowThinking, OPT_HighlightDragging, OPT_TestLegality,
\r
286 OPT_SaveExtPGN, OPT_HideThinkFromHuman, OPT_ExtraInfoInMoveHistory,
\r
287 OPT_HighlightMoveArrow, OPT_AutoLogo ,OPT_SmartMove },
\r
288 { DLG_IcsOptions, IDOK, IDCANCEL, OPT_AutoComment, OPT_AutoKibitz, OPT_AutoObserve,
\r
289 OPT_GetMoveList, OPT_LocalLineEditing, OPT_QuietPlay, OPT_SeekGraph, OPT_AutoRefresh,
\r
290 OPT_BgObserve, OPT_DualBoard, OPT_Premove, OPT_PremoveWhite, OPT_PremoveBlack,
\r
291 OPT_SmartMove, OPT_IcsAlarm, IDC_Sec, OPT_ChooseShoutColor, OPT_ChooseSShoutColor,
\r
292 OPT_ChooseChannel1Color, OPT_ChooseChannelColor, OPT_ChooseKibitzColor,
\r
293 OPT_ChooseTellColor, OPT_ChooseChallengeColor, OPT_ChooseRequestColor,
\r
294 OPT_ChooseSeekColor, OPT_ChooseNormalColor, OPT_ChooseBackgroundColor,
\r
295 OPT_DefaultColors, OPT_DontColorize, IDC_Boxes, GPB_Colors, GPB_Premove,
\r
296 GPB_General, GPB_Alarm },
\r
297 { DLG_BoardOptions, IDOK, IDCANCEL, OPT_SizeTiny, OPT_SizeTeeny, OPT_SizeDinky,
\r
298 OPT_SizePetite, OPT_SizeSlim, OPT_SizeSmall, OPT_SizeMediocre, OPT_SizeMiddling,
\r
299 OPT_SizeAverage, OPT_SizeModerate, OPT_SizeMedium, OPT_SizeBulky, OPT_SizeLarge,
\r
300 OPT_SizeBig, OPT_SizeHuge, OPT_SizeGiant, OPT_SizeColossal, OPT_SizeTitanic,
\r
301 OPT_ChooseLightSquareColor, OPT_ChooseDarkSquareColor, OPT_ChooseWhitePieceColor,
\r
302 OPT_ChooseBlackPieceColor, OPT_ChooseHighlightSquareColor, OPT_ChoosePremoveHighlightColor,
\r
303 OPT_Monochrome, OPT_AllWhite, OPT_UpsideDown, OPT_DefaultBoardColors, GPB_Colors,
\r
304 IDC_Light, IDC_Dark, IDC_White, IDC_Black, IDC_High, IDC_PreHigh, GPB_Size, OPT_Bitmaps, OPT_PieceFont },
\r
305 { DLG_NewVariant, IDOK, IDCANCEL, OPT_VariantNormal, OPT_VariantFRC, OPT_VariantWildcastle,
\r
306 OPT_VariantNocastle, OPT_VariantLosers, OPT_VariantGiveaway, OPT_VariantSuicide,
\r
307 OPT_Variant3Check, OPT_VariantTwoKings, OPT_VariantAtomic, OPT_VariantCrazyhouse,
\r
308 OPT_VariantBughouse, OPT_VariantTwilight, OPT_VariantShogi, OPT_VariantSuper,
\r
309 OPT_VariantKnightmate, OPT_VariantBerolina, OPT_VariantCylinder, OPT_VariantFairy,
\r
310 OPT_VariantMakruk, OPT_VariantGothic, OPT_VariantCapablanca, OPT_VariantJanus,
\r
311 OPT_VariantCRC, OPT_VariantFalcon, OPT_VariantCourier, OPT_VariantGreat, OPT_VariantSChess,
\r
312 OPT_VariantShatranj, OPT_VariantXiangqi, GPB_Variant, GPB_Board, IDC_Height,
\r
313 IDC_Width, IDC_Hand, IDC_Pieces, IDC_Def },
\r
314 { DLG_Fonts, IDOK, IDCANCEL, OPT_ChooseClockFont, OPT_ChooseMessageFont,
\r
315 OPT_ChooseCoordFont, OPT_ChooseTagFont, OPT_ChooseCommentsFont, OPT_ChooseConsoleFont, OPT_ChooseMoveHistoryFont, OPT_DefaultFonts,
\r
316 OPT_ClockFont, OPT_MessageFont, OPT_CoordFont, OPT_EditTagsFont, OPT_ChoosePieceFont, OPT_MessageFont8,
\r
317 OPT_SampleGameListFont, OPT_ChooseGameListFont, OPT_MessageFont7,
\r
318 OPT_CommentsFont, OPT_MessageFont5, GPB_Current, GPB_All, OPT_MessageFont6 },
\r
319 { DLG_NewGameFRC, IDC_NFG_Label, IDC_NFG_Random, IDOK, IDCANCEL },
\r
320 { DLG_GameListOptions, IDC_GLT, IDC_GLT_Up, IDC_GLT_Down, IDC_GLT_Restore,
\r
321 IDC_GLT_Default, IDOK, IDCANCEL, IDC_GLT_RestoreTo },
\r
322 { DLG_MoveHistory },
\r
323 { DLG_EvalGraph },
\r
324 { DLG_EngineOutput, IDC_EngineLabel1, IDC_Engine1_NPS, IDC_EngineLabel2, IDC_Engine2_NPS },
\r
325 { DLG_Chat, IDC_Partner, IDC_Clear, IDC_Send, },
\r
326 { DLG_EnginePlayOptions, IDC_EpPonder, IDC_EpShowThinking, IDC_EpHideThinkingHuman,
\r
327 IDC_EpPeriodicUpdates, GPB_Adjudications, IDC_Draw, IDC_Moves, IDC_Threshold,
\r
328 IDC_Centi, IDC_TestClaims, IDC_DetectMates, IDC_MaterialDraws, IDC_TrivialDraws,
\r
329 GPB_Apply, IDC_Rule, IDC_Repeats, IDC_ScoreAbs1, IDC_ScoreAbs2, IDOK, IDCANCEL },
\r
330 { DLG_OptionsUCI, IDC_PolyDir, IDC_BrowseForPolyglotDir, IDC_Hash, IDC_Path,
\r
331 IDC_BrowseForEGTB, IDC_Cache, IDC_UseBook, IDC_BrowseForBook, IDC_CPU, IDC_OwnBook1,
\r
332 IDC_OwnBook2, IDC_Depth, IDC_Variation, IDC_DefGames, IDOK, IDCANCEL },
\r
336 static char languageBuf[50000], *foreign[1000], *english[1000], *languageFile[MSG_SIZ];
\r
337 static int lastChecked;
\r
338 static char oldLanguage[MSG_SIZ], *menuText[10][30];
\r
339 extern int tinyLayout;
\r
340 extern char * menuBarText[][10];
\r
343 LoadLanguageFile(char *name)
\r
344 { //load the file with translations, and make a list of the strings to be translated, and their translations
\r
346 int i=0, j=0, n=0, k;
\r
349 if(!name || name[0] == NULLCHAR) return;
\r
350 snprintf(buf, MSG_SIZ, "%s%s", name, strchr(name, '.') ? "" : ".lng"); // auto-append lng extension
\r
351 appData.language = oldLanguage;
\r
352 if(!strcmp(buf, oldLanguage)) { barbaric = 1; return; } // this language already loaded; just switch on
\r
353 if((f = fopen(buf, "r")) == NULL) return;
\r
354 while((k = fgetc(f)) != EOF) {
\r
355 if(i >= sizeof(languageBuf)) { DisplayError("Language file too big", 0); return; }
\r
356 languageBuf[i] = k;
\r
358 if(languageBuf[n] == '"' && languageBuf[i-1] == '"') {
\r
360 if(p = strstr(languageBuf + n + 1, "\" === \"")) {
\r
361 if(p > languageBuf+n+2 && p+8 < languageBuf+i) {
\r
362 if(j >= sizeof(english)) { DisplayError("Too many translated strings", 0); return; }
\r
363 english[j] = languageBuf + n + 1; *p = 0;
\r
364 foreign[j++] = p + 7; languageBuf[i-1] = 0;
\r
365 //if(appData.debugMode) fprintf(debugFP, "translation: replace '%s' by '%s'\n", english[j-1], foreign[j-1]);
\r
370 } else if(i > 0 && languageBuf[i-1] == '\\') {
\r
372 case 'n': k = '\n'; break;
\r
373 case 'r': k = '\r'; break;
\r
374 case 't': k = '\t'; break;
\r
376 languageBuf[--i] = k;
\r
381 barbaric = (j != 0);
\r
382 safeStrCpy(oldLanguage, buf, sizeof(oldLanguage)/sizeof(oldLanguage[0]) );
\r
387 { // return the translation of the given string
\r
388 // efficiency can be improved a lot...
\r
390 //if(appData.debugMode) fprintf(debugFP, "T_(%s)\n", s);
\r
391 if(!barbaric) return s;
\r
392 if(!s) return ""; // sanity
\r
393 while(english[i]) {
\r
394 if(!strcmp(s, english[i])) return foreign[i];
\r
401 Translate(HWND hDlg, int dialogID)
\r
402 { // translate all text items in the given dialog
\r
404 char buf[MSG_SIZ], *s;
\r
405 if(!barbaric) return;
\r
406 while(dialogItems[i][0] && dialogItems[i][0] != dialogID) i++; // find the dialog description
\r
407 if(dialogItems[i][0] != dialogID) return; // unknown dialog, should not happen
\r
408 GetWindowText( hDlg, buf, MSG_SIZ );
\r
410 if(strcmp(buf, s)) SetWindowText(hDlg, s); // replace by translated string (if different)
\r
411 for(j=1; k=dialogItems[i][j]; j++) { // translate all listed dialog items
\r
412 GetDlgItemText(hDlg, k, buf, MSG_SIZ);
\r
413 if(strlen(buf) == 0) continue;
\r
415 if(strcmp(buf, s)) SetDlgItemText(hDlg, k, s); // replace by translated string (if different)
\r
420 TranslateOneMenu(int i, HMENU subMenu)
\r
423 static MENUITEMINFO info;
\r
425 info.cbSize = sizeof(MENUITEMINFO);
\r
426 info.fMask = MIIM_STATE | MIIM_TYPE;
\r
427 for(j=GetMenuItemCount(subMenu)-1; j>=0; j--){
\r
429 info.dwTypeData = buf;
\r
430 info.cch = sizeof(buf);
\r
431 GetMenuItemInfo(subMenu, j, TRUE, &info);
\r
433 if(menuText[i][j]) safeStrCpy(buf, menuText[i][j], sizeof(buf)/sizeof(buf[0]) );
\r
434 else menuText[i][j] = strdup(buf); // remember original on first change
\r
436 if(buf[0] == NULLCHAR) continue;
\r
437 info.dwTypeData = T_(buf);
\r
438 info.cch = strlen(buf)+1;
\r
439 SetMenuItemInfo(subMenu, j, TRUE, &info);
\r
445 TranslateMenus(int addLanguage)
\r
448 WIN32_FIND_DATA fileData;
\r
450 #define IDM_English 1970
\r
452 HMENU mainMenu = GetMenu(hwndMain);
\r
453 for (i=GetMenuItemCount(mainMenu)-1; i>=0; i--) {
\r
454 HMENU subMenu = GetSubMenu(mainMenu, i);
\r
455 ModifyMenu(mainMenu, i, MF_STRING|MF_BYPOSITION|MF_POPUP|EnableMenuItem(mainMenu, i, MF_BYPOSITION),
\r
456 (UINT) subMenu, T_(menuBarText[tinyLayout][i]));
\r
457 TranslateOneMenu(i, subMenu);
\r
459 DrawMenuBar(hwndMain);
\r
462 if(!addLanguage) return;
\r
463 if((hFind = FindFirstFile("*.LNG", &fileData)) != INVALID_HANDLE_VALUE) {
\r
464 HMENU mainMenu = GetMenu(hwndMain);
\r
465 HMENU subMenu = GetSubMenu(mainMenu, GetMenuItemCount(mainMenu)-1);
\r
466 AppendMenu(subMenu, MF_SEPARATOR, (UINT_PTR) 0, NULL);
\r
467 AppendMenu(subMenu, MF_ENABLED|MF_STRING|(barbaric?MF_UNCHECKED:MF_CHECKED), (UINT_PTR) IDM_English, (LPCTSTR) "English");
\r
468 i = 0; lastChecked = IDM_English;
\r
470 char *p, *q = fileData.cFileName;
\r
471 int checkFlag = MF_UNCHECKED;
\r
472 languageFile[i] = strdup(q);
\r
473 if(barbaric && !strcmp(oldLanguage, q)) {
\r
474 checkFlag = MF_CHECKED;
\r
475 lastChecked = IDM_English + i + 1;
\r
476 CheckMenuItem(mainMenu, IDM_English, MF_BYCOMMAND|MF_UNCHECKED);
\r
478 *q = ToUpper(*q); while(*++q) *q = ToLower(*q);
\r
479 p = strstr(fileData.cFileName, ".lng");
\r
481 AppendMenu(subMenu, MF_ENABLED|MF_STRING|checkFlag, (UINT_PTR) IDM_English + ++i, (LPCTSTR) fileData.cFileName);
\r
482 } while(FindNextFile(hFind, &fileData));
\r
495 int cliWidth, cliHeight;
\r
498 SizeInfo sizeInfo[] =
\r
500 { "tiny", 21, 0, 1, 1, 0, 0 },
\r
501 { "teeny", 25, 1, 1, 1, 0, 0 },
\r
502 { "dinky", 29, 1, 1, 1, 0, 0 },
\r
503 { "petite", 33, 1, 1, 1, 0, 0 },
\r
504 { "slim", 37, 2, 1, 0, 0, 0 },
\r
505 { "small", 40, 2, 1, 0, 0, 0 },
\r
506 { "mediocre", 45, 2, 1, 0, 0, 0 },
\r
507 { "middling", 49, 2, 0, 0, 0, 0 },
\r
508 { "average", 54, 2, 0, 0, 0, 0 },
\r
509 { "moderate", 58, 3, 0, 0, 0, 0 },
\r
510 { "medium", 64, 3, 0, 0, 0, 0 },
\r
511 { "bulky", 72, 3, 0, 0, 0, 0 },
\r
512 { "large", 80, 3, 0, 0, 0, 0 },
\r
513 { "big", 87, 3, 0, 0, 0, 0 },
\r
514 { "huge", 95, 3, 0, 0, 0, 0 },
\r
515 { "giant", 108, 3, 0, 0, 0, 0 },
\r
516 { "colossal", 116, 4, 0, 0, 0, 0 },
\r
517 { "titanic", 129, 4, 0, 0, 0, 0 },
\r
518 { NULL, 0, 0, 0, 0, 0, 0 }
\r
521 #define MF(x) {x, {{0,}, 0. }, {0, }, 0}
\r
522 MyFont fontRec[NUM_SIZES][NUM_FONTS] =
\r
524 { MF(CLOCK_FONT_TINY), MF(MESSAGE_FONT_TINY), MF(COORD_FONT_TINY), MF(CONSOLE_FONT_TINY), MF(COMMENT_FONT_TINY), MF(EDITTAGS_FONT_TINY), MF(MOVEHISTORY_FONT_ALL), MF(GAMELIST_FONT_ALL) },
\r
525 { MF(CLOCK_FONT_TEENY), MF(MESSAGE_FONT_TEENY), MF(COORD_FONT_TEENY), MF(CONSOLE_FONT_TEENY), MF(COMMENT_FONT_TEENY), MF(EDITTAGS_FONT_TEENY), MF(MOVEHISTORY_FONT_ALL), MF(GAMELIST_FONT_ALL) },
\r
526 { MF(CLOCK_FONT_DINKY), MF(MESSAGE_FONT_DINKY), MF(COORD_FONT_DINKY), MF(CONSOLE_FONT_DINKY), MF(COMMENT_FONT_DINKY), MF(EDITTAGS_FONT_DINKY), MF(MOVEHISTORY_FONT_ALL), MF(GAMELIST_FONT_ALL) },
\r
527 { MF(CLOCK_FONT_PETITE), MF(MESSAGE_FONT_PETITE), MF(COORD_FONT_PETITE), MF(CONSOLE_FONT_PETITE), MF(COMMENT_FONT_PETITE), MF(EDITTAGS_FONT_PETITE), MF(MOVEHISTORY_FONT_ALL), MF(GAMELIST_FONT_ALL) },
\r
528 { MF(CLOCK_FONT_SLIM), MF(MESSAGE_FONT_SLIM), MF(COORD_FONT_SLIM), MF(CONSOLE_FONT_SLIM), MF(COMMENT_FONT_SLIM), MF(EDITTAGS_FONT_SLIM), MF(MOVEHISTORY_FONT_ALL), MF(GAMELIST_FONT_ALL) },
\r
529 { MF(CLOCK_FONT_SMALL), MF(MESSAGE_FONT_SMALL), MF(COORD_FONT_SMALL), MF(CONSOLE_FONT_SMALL), MF(COMMENT_FONT_SMALL), MF(EDITTAGS_FONT_SMALL), MF(MOVEHISTORY_FONT_ALL), MF(GAMELIST_FONT_ALL) },
\r
530 { MF(CLOCK_FONT_MEDIOCRE), MF(MESSAGE_FONT_MEDIOCRE), MF(COORD_FONT_MEDIOCRE), MF(CONSOLE_FONT_MEDIOCRE), MF(COMMENT_FONT_MEDIOCRE), MF(EDITTAGS_FONT_MEDIOCRE), MF(MOVEHISTORY_FONT_ALL), MF(GAMELIST_FONT_ALL) },
\r
531 { MF(CLOCK_FONT_MIDDLING), MF(MESSAGE_FONT_MIDDLING), MF(COORD_FONT_MIDDLING), MF(CONSOLE_FONT_MIDDLING), MF(COMMENT_FONT_MIDDLING), MF(EDITTAGS_FONT_MIDDLING), MF(MOVEHISTORY_FONT_ALL), MF(GAMELIST_FONT_ALL) },
\r
532 { MF(CLOCK_FONT_AVERAGE), MF(MESSAGE_FONT_AVERAGE), MF(COORD_FONT_AVERAGE), MF(CONSOLE_FONT_AVERAGE), MF(COMMENT_FONT_AVERAGE), MF(EDITTAGS_FONT_AVERAGE), MF(MOVEHISTORY_FONT_ALL), MF(GAMELIST_FONT_ALL) },
\r
533 { MF(CLOCK_FONT_MODERATE), MF(MESSAGE_FONT_MODERATE), MF(COORD_FONT_MODERATE), MF(CONSOLE_FONT_MODERATE), MF(COMMENT_FONT_MODERATE), MF(EDITTAGS_FONT_MODERATE), MF(MOVEHISTORY_FONT_ALL), MF(GAMELIST_FONT_ALL) },
\r
534 { MF(CLOCK_FONT_MEDIUM), MF(MESSAGE_FONT_MEDIUM), MF(COORD_FONT_MEDIUM), MF(CONSOLE_FONT_MEDIUM), MF(COMMENT_FONT_MEDIUM), MF(EDITTAGS_FONT_MEDIUM), MF(MOVEHISTORY_FONT_ALL), MF(GAMELIST_FONT_ALL) },
\r
535 { MF(CLOCK_FONT_BULKY), MF(MESSAGE_FONT_BULKY), MF(COORD_FONT_BULKY), MF(CONSOLE_FONT_BULKY), MF(COMMENT_FONT_BULKY), MF(EDITTAGS_FONT_BULKY), MF(MOVEHISTORY_FONT_ALL), MF(GAMELIST_FONT_ALL) },
\r
536 { MF(CLOCK_FONT_LARGE), MF(MESSAGE_FONT_LARGE), MF(COORD_FONT_LARGE), MF(CONSOLE_FONT_LARGE), MF(COMMENT_FONT_LARGE), MF(EDITTAGS_FONT_LARGE), MF(MOVEHISTORY_FONT_ALL), MF(GAMELIST_FONT_ALL) },
\r
537 { MF(CLOCK_FONT_BIG), MF(MESSAGE_FONT_BIG), MF(COORD_FONT_BIG), MF(CONSOLE_FONT_BIG), MF(COMMENT_FONT_BIG), MF(EDITTAGS_FONT_BIG), MF(MOVEHISTORY_FONT_ALL), MF(GAMELIST_FONT_ALL) },
\r
538 { MF(CLOCK_FONT_HUGE), MF(MESSAGE_FONT_HUGE), MF(COORD_FONT_HUGE), MF(CONSOLE_FONT_HUGE), MF(COMMENT_FONT_HUGE), MF(EDITTAGS_FONT_HUGE), MF(MOVEHISTORY_FONT_ALL), MF(GAMELIST_FONT_ALL) },
\r
539 { MF(CLOCK_FONT_GIANT), MF(MESSAGE_FONT_GIANT), MF(COORD_FONT_GIANT), MF(CONSOLE_FONT_GIANT), MF(COMMENT_FONT_GIANT), MF(EDITTAGS_FONT_GIANT), MF(MOVEHISTORY_FONT_ALL), MF(GAMELIST_FONT_ALL) },
\r
540 { MF(CLOCK_FONT_COLOSSAL), MF(MESSAGE_FONT_COLOSSAL), MF(COORD_FONT_COLOSSAL), MF(CONSOLE_FONT_COLOSSAL), MF(COMMENT_FONT_COLOSSAL), MF(EDITTAGS_FONT_COLOSSAL), MF(MOVEHISTORY_FONT_ALL), MF (GAMELIST_FONT_ALL) },
\r
541 { MF(CLOCK_FONT_TITANIC), MF(MESSAGE_FONT_TITANIC), MF(COORD_FONT_TITANIC), MF(CONSOLE_FONT_TITANIC), MF(COMMENT_FONT_TITANIC), MF(EDITTAGS_FONT_TITANIC), MF(MOVEHISTORY_FONT_ALL), MF(GAMELIST_FONT_ALL) },
\r
544 MyFont *font[NUM_SIZES][NUM_FONTS];
\r
553 #define BUTTON_WIDTH (tinyLayout ? 16 : 32)
\r
554 #define N_BUTTONS 5
\r
556 MyButtonDesc buttonDesc[N_BUTTONS] =
\r
558 {"<<", IDM_ToStart, NULL, NULL},
\r
559 {"<", IDM_Backward, NULL, NULL},
\r
560 {"P", IDM_Pause, NULL, NULL},
\r
561 {">", IDM_Forward, NULL, NULL},
\r
562 {">>", IDM_ToEnd, NULL, NULL},
\r
565 int tinyLayout = 0, smallLayout = 0;
\r
566 #define MENU_BAR_ITEMS 9
\r
567 char *menuBarText[2][MENU_BAR_ITEMS+1] = {
\r
568 { N_("&File"), N_("&Edit"), N_("&View"), N_("&Mode"), N_("&Action"), N_("E&ngine"), N_("&Options"), N_("&Help"), NULL },
\r
569 { N_("&F"), N_("&E"), N_("&V"), N_("&M"), N_("&A"), N_("&N"), N_("&O"), N_("&H"), NULL },
\r
573 MySound sounds[(int)NSoundClasses];
\r
574 MyTextAttribs textAttribs[(int)NColorClasses];
\r
576 MyColorizeAttribs colorizeAttribs[] = {
\r
577 { (COLORREF)0, 0, N_("Shout Text") },
\r
578 { (COLORREF)0, 0, N_("SShout/CShout") },
\r
579 { (COLORREF)0, 0, N_("Channel 1 Text") },
\r
580 { (COLORREF)0, 0, N_("Channel Text") },
\r
581 { (COLORREF)0, 0, N_("Kibitz Text") },
\r
582 { (COLORREF)0, 0, N_("Tell Text") },
\r
583 { (COLORREF)0, 0, N_("Challenge Text") },
\r
584 { (COLORREF)0, 0, N_("Request Text") },
\r
585 { (COLORREF)0, 0, N_("Seek Text") },
\r
586 { (COLORREF)0, 0, N_("Normal Text") },
\r
587 { (COLORREF)0, 0, N_("None") }
\r
592 static char *commentTitle;
\r
593 static char *commentText;
\r
594 static int commentIndex;
\r
595 static Boolean editComment = FALSE;
\r
598 char errorTitle[MSG_SIZ];
\r
599 char errorMessage[2*MSG_SIZ];
\r
600 HWND errorDialog = NULL;
\r
601 BOOLEAN moveErrorMessageUp = FALSE;
\r
602 BOOLEAN consoleEcho = TRUE;
\r
603 CHARFORMAT consoleCF;
\r
604 COLORREF consoleBackgroundColor;
\r
606 char *programVersion;
\r
612 typedef int CPKind;
\r
621 SOCKET sock2; /* stderr socket for OpenRcmd */
\r
624 #define INPUT_SOURCE_BUF_SIZE 4096
\r
626 typedef struct _InputSource {
\r
633 char buf[INPUT_SOURCE_BUF_SIZE];
\r
637 InputCallback func;
\r
638 struct _InputSource *second; /* for stderr thread on CPRcmd */
\r
642 InputSource *consoleInputSource;
\r
647 VOID ConsoleOutput(char* data, int length, int forceVisible);
\r
648 VOID ConsoleCreate();
\r
650 ConsoleWndProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam);
\r
651 VOID ColorizeTextPopup(HWND hwnd, ColorClass cc);
\r
652 VOID PrintCommSettings(FILE *f, char *name, DCB *dcb);
\r
653 VOID ParseCommSettings(char *arg, DCB *dcb);
\r
655 StartupDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam);
\r
656 VOID APIENTRY MenuPopup(HWND hwnd, POINT pt, HMENU hmenu, UINT def);
\r
657 void ParseIcsTextMenu(char *icsTextMenuString);
\r
658 VOID PopUpNameDialog(char firstchar);
\r
659 VOID UpdateSampleText(HWND hDlg, int id, MyColorizeAttribs *mca);
\r
663 int GameListOptions();
\r
665 int dummy; // [HGM] for obsolete args
\r
667 HWND hwndMain = NULL; /* root window*/
\r
668 HWND hwndConsole = NULL;
\r
669 HWND commentDialog = NULL;
\r
670 HWND moveHistoryDialog = NULL;
\r
671 HWND evalGraphDialog = NULL;
\r
672 HWND engineOutputDialog = NULL;
\r
673 HWND gameListDialog = NULL;
\r
674 HWND editTagsDialog = NULL;
\r
676 int commentUp = FALSE;
\r
678 WindowPlacement wpMain;
\r
679 WindowPlacement wpConsole;
\r
680 WindowPlacement wpComment;
\r
681 WindowPlacement wpMoveHistory;
\r
682 WindowPlacement wpEvalGraph;
\r
683 WindowPlacement wpEngineOutput;
\r
684 WindowPlacement wpGameList;
\r
685 WindowPlacement wpTags;
\r
687 VOID EngineOptionsPopup(); // [HGM] settings
\r
689 VOID GothicPopUp(char *title, VariantClass variant);
\r
691 * Setting "frozen" should disable all user input other than deleting
\r
692 * the window. We do this while engines are initializing themselves.
\r
694 static int frozen = 0;
\r
695 static int oldMenuItemState[MENU_BAR_ITEMS];
\r
701 if (frozen) return;
\r
703 hmenu = GetMenu(hwndMain);
\r
704 for (i=0; i<MENU_BAR_ITEMS; i++) {
\r
705 oldMenuItemState[i] = EnableMenuItem(hmenu, i, MF_BYPOSITION|MF_GRAYED);
\r
707 DrawMenuBar(hwndMain);
\r
710 /* Undo a FreezeUI */
\r
716 if (!frozen) return;
\r
718 hmenu = GetMenu(hwndMain);
\r
719 for (i=0; i<MENU_BAR_ITEMS; i++) {
\r
720 EnableMenuItem(hmenu, i, MF_BYPOSITION|oldMenuItemState[i]);
\r
722 DrawMenuBar(hwndMain);
\r
725 /*static*/ int fromX = -1, fromY = -1, toX, toY; // [HGM] moved upstream, so JAWS can use them
\r
727 /* JAWS preparation patch (WinBoard for the sight impaired). Define required insertions as empty */
\r
733 #define JAWS_ALT_INTERCEPT
\r
734 #define JAWS_KB_NAVIGATION
\r
735 #define JAWS_MENU_ITEMS
\r
736 #define JAWS_SILENCE
\r
737 #define JAWS_REPLAY
\r
739 #define JAWS_COPYRIGHT
\r
740 #define JAWS_DELETE(X) X
\r
741 #define SAYMACHINEMOVE()
\r
745 /*---------------------------------------------------------------------------*\
\r
749 \*---------------------------------------------------------------------------*/
\r
752 WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
\r
753 LPSTR lpCmdLine, int nCmdShow)
\r
756 HANDLE hAccelMain, hAccelNoAlt, hAccelNoICS;
\r
757 // INITCOMMONCONTROLSEX ex;
\r
761 LoadLibrary("RICHED32.DLL");
\r
762 consoleCF.cbSize = sizeof(CHARFORMAT);
\r
764 if (!InitApplication(hInstance)) {
\r
767 if (!InitInstance(hInstance, nCmdShow, lpCmdLine)) {
\r
774 // InitCommonControlsEx(&ex);
\r
775 InitCommonControls();
\r
777 hAccelMain = LoadAccelerators (hInstance, szAppName);
\r
778 hAccelNoAlt = LoadAccelerators (hInstance, "NO_ALT");
\r
779 hAccelNoICS = LoadAccelerators( hInstance, "NO_ICS"); /* [AS] No Ctrl-V on ICS!!! */
\r
781 /* Acquire and dispatch messages until a WM_QUIT message is received. */
\r
783 while (GetMessage(&msg, /* message structure */
\r
784 NULL, /* handle of window receiving the message */
\r
785 0, /* lowest message to examine */
\r
786 0)) /* highest message to examine */
\r
789 if(msg.message == WM_CHAR && msg.wParam == '\t') {
\r
790 // [HGM] navigate: switch between all windows with tab
\r
791 HWND e1 = NULL, e2 = NULL, mh = NULL, hInput = NULL, hText = NULL;
\r
792 int i, currentElement = 0;
\r
794 // first determine what element of the chain we come from (if any)
\r
795 if(appData.icsActive) {
\r
796 hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
797 hText = GetDlgItem(hwndConsole, OPT_ConsoleText);
\r
799 if(engineOutputDialog && EngineOutputIsUp()) {
\r
800 e1 = GetDlgItem(engineOutputDialog, IDC_EngineMemo1);
\r
801 e2 = GetDlgItem(engineOutputDialog, IDC_EngineMemo2);
\r
803 if(moveHistoryDialog && MoveHistoryIsUp()) {
\r
804 mh = GetDlgItem(moveHistoryDialog, IDC_MoveHistory);
\r
806 if(msg.hwnd == hwndMain) currentElement = 7 ; else
\r
807 if(msg.hwnd == engineOutputDialog) currentElement = 2; else
\r
808 if(msg.hwnd == e1) currentElement = 2; else
\r
809 if(msg.hwnd == e2) currentElement = 3; else
\r
810 if(msg.hwnd == moveHistoryDialog) currentElement = 4; else
\r
811 if(msg.hwnd == mh) currentElement = 4; else
\r
812 if(msg.hwnd == evalGraphDialog) currentElement = 6; else
\r
813 if(msg.hwnd == hText) currentElement = 5; else
\r
814 if(msg.hwnd == hInput) currentElement = 6; else
\r
815 for (i = 0; i < N_BUTTONS; i++) {
\r
816 if (buttonDesc[i].hwnd == msg.hwnd) { currentElement = 1; break; }
\r
819 // determine where to go to
\r
820 if(currentElement) { HWND h = NULL; int direction = GetKeyState(VK_SHIFT) < 0 ? -1 : 1;
\r
822 currentElement = (currentElement + direction) % 7;
\r
823 switch(currentElement) {
\r
825 h = hwndMain; break; // passing this case always makes the loop exit
\r
827 h = buttonDesc[0].hwnd; break; // could be NULL
\r
829 if(!EngineOutputIsUp()) continue; // skip closed auxiliary windows
\r
832 if(!EngineOutputIsUp()) continue;
\r
835 if(!MoveHistoryIsUp()) continue;
\r
837 // case 6: // input to eval graph does not seem to get here!
\r
838 // if(!EvalGraphIsUp()) continue;
\r
839 // h = evalGraphDialog; break;
\r
841 if(!appData.icsActive) continue;
\r
845 if(!appData.icsActive) continue;
\r
851 if(currentElement > 4 && IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);
\r
852 if(currentElement < 5 && IsIconic(hwndMain)) ShowWindow(hwndMain, SW_RESTORE); // all open together
\r
855 continue; // this message now has been processed
\r
859 if (!(commentDialog && IsDialogMessage(commentDialog, &msg)) &&
\r
860 !(moveHistoryDialog && IsDialogMessage(moveHistoryDialog, &msg)) &&
\r
861 !(evalGraphDialog && IsDialogMessage(evalGraphDialog, &msg)) &&
\r
862 !(engineOutputDialog && IsDialogMessage(engineOutputDialog, &msg)) &&
\r
863 !(editTagsDialog && IsDialogMessage(editTagsDialog, &msg)) &&
\r
864 !(gameListDialog && IsDialogMessage(gameListDialog, &msg)) &&
\r
865 !(errorDialog && IsDialogMessage(errorDialog, &msg)) &&
\r
866 !(!frozen && TranslateAccelerator(hwndMain, hAccelMain, &msg)) && JAWS_ACCEL
\r
867 !(!hwndConsole && TranslateAccelerator(hwndMain, hAccelNoICS, &msg)) &&
\r
868 !(!hwndConsole && TranslateAccelerator(hwndMain, hAccelNoAlt, &msg))) {
\r
869 int done = 0, i; // [HGM] chat: dispatch cat-box messages
\r
870 for(i=0; i<MAX_CHAT; i++)
\r
871 if(chatHandle[i] && IsDialogMessage(chatHandle[i], &msg)) {
\r
874 if(done) continue; // [HGM] chat: end patch
\r
875 TranslateMessage(&msg); /* Translates virtual key codes */
\r
876 DispatchMessage(&msg); /* Dispatches message to window */
\r
881 return (msg.wParam); /* Returns the value from PostQuitMessage */
\r
884 /*---------------------------------------------------------------------------*\
\r
886 * Initialization functions
\r
888 \*---------------------------------------------------------------------------*/
\r
892 { // update user logo if necessary
\r
893 static char oldUserName[MSG_SIZ], dir[MSG_SIZ], *curName;
\r
895 if(appData.autoLogo) {
\r
896 curName = UserName();
\r
897 if(strcmp(curName, oldUserName)) {
\r
898 GetCurrentDirectory(MSG_SIZ, dir);
\r
899 SetCurrentDirectory(installDir);
\r
900 snprintf(oldUserName, MSG_SIZ, "logos\\%s.bmp", curName);
\r
901 userLogo = LoadImage( 0, oldUserName, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );
\r
902 safeStrCpy(oldUserName, curName, sizeof(oldUserName)/sizeof(oldUserName[0]) );
\r
903 if(userLogo == NULL)
\r
904 userLogo = LoadImage( 0, "logos\\dummy.bmp", IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );
\r
905 SetCurrentDirectory(dir); /* return to prev directory */
\r
911 InitApplication(HINSTANCE hInstance)
\r
915 /* Fill in window class structure with parameters that describe the */
\r
918 wc.style = CS_HREDRAW | CS_VREDRAW; /* Class style(s). */
\r
919 wc.lpfnWndProc = (WNDPROC)WndProc; /* Window Procedure */
\r
920 wc.cbClsExtra = 0; /* No per-class extra data. */
\r
921 wc.cbWndExtra = 0; /* No per-window extra data. */
\r
922 wc.hInstance = hInstance; /* Owner of this class */
\r
923 wc.hIcon = LoadIcon(hInstance, "icon_white");
\r
924 wc.hCursor = LoadCursor(NULL, IDC_ARROW); /* Cursor */
\r
925 wc.hbrBackground = (HBRUSH)(COLOR_WINDOW+1); /* Default color */
\r
926 wc.lpszMenuName = szAppName; /* Menu name from .RC */
\r
927 wc.lpszClassName = szAppName; /* Name to register as */
\r
929 /* Register the window class and return success/failure code. */
\r
930 if (!RegisterClass(&wc)) return FALSE;
\r
932 wc.style = CS_HREDRAW | CS_VREDRAW;
\r
933 wc.lpfnWndProc = (WNDPROC)ConsoleWndProc;
\r
935 wc.cbWndExtra = DLGWINDOWEXTRA;
\r
936 wc.hInstance = hInstance;
\r
937 wc.hIcon = LoadIcon(hInstance, "icon_white");
\r
938 wc.hCursor = LoadCursor(NULL, IDC_ARROW);
\r
939 wc.hbrBackground = (HBRUSH)(COLOR_MENU+1);
\r
940 wc.lpszMenuName = NULL;
\r
941 wc.lpszClassName = szConsoleName;
\r
943 if (!RegisterClass(&wc)) return FALSE;
\r
948 /* Set by InitInstance, used by EnsureOnScreen */
\r
949 int screenHeight, screenWidth;
\r
952 EnsureOnScreen(int *x, int *y, int minX, int minY)
\r
954 // int gap = GetSystemMetrics(SM_CYFRAME) + GetSystemMetrics(SM_CYCAPTION);
\r
955 /* Be sure window at (x,y) is not off screen (or even mostly off screen) */
\r
956 if (*x > screenWidth - 32) *x = 0;
\r
957 if (*y > screenHeight - 32) *y = 0;
\r
958 if (*x < minX) *x = minX;
\r
959 if (*y < minY) *y = minY;
\r
963 LoadLogo(ChessProgramState *cps, int n, Boolean ics)
\r
965 char buf[MSG_SIZ], dir[MSG_SIZ];
\r
966 GetCurrentDirectory(MSG_SIZ, dir);
\r
967 SetCurrentDirectory(installDir);
\r
968 if( appData.logo[n] && appData.logo[n][0] != NULLCHAR) {
\r
969 cps->programLogo = LoadImage( 0, appData.logo[n], IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );
\r
971 if (cps->programLogo == NULL && appData.debugMode) {
\r
972 fprintf( debugFP, "Unable to load logo bitmap '%s'\n", appData.logo[n] );
\r
974 } else if(appData.autoLogo) {
\r
975 if(ics) { // [HGM] logo: in ICS mode second can be used for ICS
\r
976 sprintf(buf, "logos\\%s.bmp", appData.icsHost);
\r
977 cps->programLogo = LoadImage( 0, buf, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );
\r
979 if(appData.directory[n] && appData.directory[n][0]) {
\r
980 SetCurrentDirectory(appData.directory[n]);
\r
981 cps->programLogo = LoadImage( 0, "logo.bmp", IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );
\r
984 SetCurrentDirectory(dir); /* return to prev directory */
\r
990 ZeroMemory( &backTextureSquareInfo, sizeof(backTextureSquareInfo) );
\r
991 backTextureSquareSize = 0; // kludge to force recalculation of texturemode
\r
993 if( appData.liteBackTextureFile && appData.liteBackTextureFile[0] != NULLCHAR && appData.liteBackTextureFile[0] != '*' ) {
\r
994 liteBackTexture = LoadImage( 0, appData.liteBackTextureFile, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );
\r
995 liteBackTextureMode = appData.liteBackTextureMode;
\r
997 if (liteBackTexture == NULL && appData.debugMode) {
\r
998 fprintf( debugFP, "Unable to load lite texture bitmap '%s'\n", appData.liteBackTextureFile );
\r
1002 if( appData.darkBackTextureFile && appData.darkBackTextureFile[0] != NULLCHAR && appData.darkBackTextureFile[0] != '*' ) {
\r
1003 darkBackTexture = LoadImage( 0, appData.darkBackTextureFile, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );
\r
1004 darkBackTextureMode = appData.darkBackTextureMode;
\r
1006 if (darkBackTexture == NULL && appData.debugMode) {
\r
1007 fprintf( debugFP, "Unable to load dark texture bitmap '%s'\n", appData.darkBackTextureFile );
\r
1013 InitInstance(HINSTANCE hInstance, int nCmdShow, LPSTR lpCmdLine)
\r
1015 HWND hwnd; /* Main window handle. */
\r
1017 WINDOWPLACEMENT wp;
\r
1020 hInst = hInstance; /* Store instance handle in our global variable */
\r
1021 programName = szAppName;
\r
1023 if (SearchPath(NULL, "WinBoard.exe", NULL, MSG_SIZ, installDir, &filepart)) {
\r
1024 *filepart = NULLCHAR;
\r
1026 GetCurrentDirectory(MSG_SIZ, installDir);
\r
1028 gameInfo.boardWidth = gameInfo.boardHeight = 8; // [HGM] won't have open window otherwise
\r
1029 screenWidth = screenHeight = 1000; // [HGM] placement: kludge to allow calling EnsureOnScreen from InitAppData
\r
1030 InitAppData(lpCmdLine); /* Get run-time parameters */
\r
1031 /* xboard, and older WinBoards, controlled the move sound with the
\r
1032 appData.ringBellAfterMoves option. In the current WinBoard, we
\r
1033 always turn the option on (so that the backend will call us),
\r
1034 then let the user turn the sound off by setting it to silence if
\r
1035 desired. To accommodate old winboard.ini files saved by old
\r
1036 versions of WinBoard, we also turn off the sound if the option
\r
1037 was initially set to false. [HGM] taken out of InitAppData */
\r
1038 if (!appData.ringBellAfterMoves) {
\r
1039 sounds[(int)SoundMove].name = strdup("");
\r
1040 appData.ringBellAfterMoves = TRUE;
\r
1042 if (appData.debugMode) {
\r
1043 debugFP = fopen(appData.nameOfDebugFile, "w");
\r
1044 setbuf(debugFP, NULL);
\r
1047 LoadLanguageFile(appData.language);
\r
1051 // InitEngineUCI( installDir, &first ); // [HGM] incorporated in InitBackEnd1()
\r
1052 // InitEngineUCI( installDir, &second );
\r
1054 /* Create a main window for this application instance. */
\r
1055 hwnd = CreateWindow(szAppName, szTitle,
\r
1056 (WS_OVERLAPPEDWINDOW & ~WS_MAXIMIZEBOX),
\r
1057 CW_USEDEFAULT, 0, CW_USEDEFAULT, 0,
\r
1058 NULL, NULL, hInstance, NULL);
\r
1061 /* If window could not be created, return "failure" */
\r
1066 /* [HGM] logo: Load logos if specified (must be done before InitDrawingSizes) */
\r
1067 LoadLogo(&first, 0, FALSE);
\r
1068 LoadLogo(&second, 1, appData.icsActive);
\r
1072 iconWhite = LoadIcon(hInstance, "icon_white");
\r
1073 iconBlack = LoadIcon(hInstance, "icon_black");
\r
1074 iconCurrent = iconWhite;
\r
1075 InitDrawingColors();
\r
1076 screenHeight = GetSystemMetrics(SM_CYSCREEN);
\r
1077 screenWidth = GetSystemMetrics(SM_CXSCREEN);
\r
1078 for (ibs = (int) NUM_SIZES - 1; ibs >= 0; ibs--) {
\r
1079 /* Compute window size for each board size, and use the largest
\r
1080 size that fits on this screen as the default. */
\r
1081 InitDrawingSizes((BoardSize)(ibs+1000), 0);
\r
1082 if (boardSize == (BoardSize)-1 &&
\r
1083 winH <= screenHeight
\r
1084 - GetSystemMetrics(SM_CYFRAME) - GetSystemMetrics(SM_CYCAPTION) - 10
\r
1085 && winW <= screenWidth) {
\r
1086 boardSize = (BoardSize)ibs;
\r
1090 InitDrawingSizes(boardSize, 0);
\r
1092 buttonCount = GetSystemMetrics(SM_CMOUSEBUTTONS);
\r
1094 /* [AS] Load textures if specified */
\r
1097 mysrandom( (unsigned) time(NULL) );
\r
1099 /* [AS] Restore layout */
\r
1100 if( wpMoveHistory.visible ) {
\r
1101 MoveHistoryPopUp();
\r
1104 if( wpEvalGraph.visible ) {
\r
1108 if( wpEngineOutput.visible ) {
\r
1109 EngineOutputPopUp();
\r
1112 /* Make the window visible; update its client area; and return "success" */
\r
1113 EnsureOnScreen(&wpMain.x, &wpMain.y, minX, minY);
\r
1114 wp.length = sizeof(WINDOWPLACEMENT);
\r
1116 wp.showCmd = nCmdShow;
\r
1117 wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;
\r
1118 wp.rcNormalPosition.left = wpMain.x;
\r
1119 wp.rcNormalPosition.right = wpMain.x + wpMain.width;
\r
1120 wp.rcNormalPosition.top = wpMain.y;
\r
1121 wp.rcNormalPosition.bottom = wpMain.y + wpMain.height;
\r
1122 SetWindowPlacement(hwndMain, &wp);
\r
1124 InitBackEnd2(); // [HGM] moved until after all windows placed, to save correct position if fatal error on engine start
\r
1126 if(!appData.noGUI) SetWindowPos(hwndMain, alwaysOnTop ? HWND_TOPMOST : HWND_NOTOPMOST,
\r
1127 0, 0, 0, 0, SWP_NOMOVE|SWP_NOSIZE);
\r
1129 if (hwndConsole) {
\r
1131 SetWindowPos(hwndConsole, alwaysOnTop ? HWND_TOPMOST : HWND_NOTOPMOST,
\r
1132 0, 0, 0, 0, SWP_NOMOVE|SWP_NOSIZE);
\r
1134 ShowWindow(hwndConsole, nCmdShow);
\r
1135 if(appData.chatBoxes) { // [HGM] chat: open chat boxes
\r
1136 char buf[MSG_SIZ], *p = buf, *q;
\r
1137 safeStrCpy(buf, appData.chatBoxes, sizeof(buf)/sizeof(buf[0]) );
\r
1139 q = strchr(p, ';');
\r
1141 if(*p) ChatPopUp(p);
\r
1144 SetActiveWindow(hwndConsole);
\r
1146 if(!appData.noGUI) UpdateWindow(hwnd); else ShowWindow(hwnd, SW_MINIMIZE);
\r
1147 if(gameListDialog) SetFocus(gameListDialog); // [HGM] jaws: for if we clicked multi-game game file
\r
1156 HMENU hmenu = GetMenu(hwndMain);
\r
1158 (void) EnableMenuItem(hmenu, IDM_CommPort,
\r
1159 MF_BYCOMMAND|((appData.icsActive &&
\r
1160 *appData.icsCommPort != NULLCHAR) ?
\r
1161 MF_ENABLED : MF_GRAYED));
\r
1162 (void) CheckMenuItem(hmenu, IDM_SaveSettingsOnExit,
\r
1163 MF_BYCOMMAND|(saveSettingsOnExit ?
\r
1164 MF_CHECKED : MF_UNCHECKED));
\r
1167 //---------------------------------------------------------------------------------------------------------
\r
1169 #define ICS_TEXT_MENU_SIZE (IDM_CommandXLast - IDM_CommandX + 1)
\r
1170 #define XBOARD FALSE
\r
1172 #define OPTCHAR "/"
\r
1173 #define SEPCHAR "="
\r
1177 // front-end part of option handling
\r
1180 LFfromMFP(LOGFONT* lf, MyFontParams *mfp)
\r
1182 HDC hdc = CreateDC("DISPLAY", NULL, NULL, NULL);
\r
1183 lf->lfHeight = -(int)(mfp->pointSize * GetDeviceCaps(hdc, LOGPIXELSY) / 72.0 + 0.5);
\r
1186 lf->lfEscapement = 0;
\r
1187 lf->lfOrientation = 0;
\r
1188 lf->lfWeight = mfp->bold ? FW_BOLD : FW_NORMAL;
\r
1189 lf->lfItalic = mfp->italic;
\r
1190 lf->lfUnderline = mfp->underline;
\r
1191 lf->lfStrikeOut = mfp->strikeout;
\r
1192 lf->lfCharSet = mfp->charset;
\r
1193 lf->lfOutPrecision = OUT_DEFAULT_PRECIS;
\r
1194 lf->lfClipPrecision = CLIP_DEFAULT_PRECIS;
\r
1195 lf->lfQuality = DEFAULT_QUALITY;
\r
1196 lf->lfPitchAndFamily = DEFAULT_PITCH|FF_DONTCARE;
\r
1197 safeStrCpy(lf->lfFaceName, mfp->faceName, sizeof(lf->lfFaceName)/sizeof(lf->lfFaceName[0]) );
\r
1201 CreateFontInMF(MyFont *mf)
\r
1203 LFfromMFP(&mf->lf, &mf->mfp);
\r
1204 if (mf->hf) DeleteObject(mf->hf);
\r
1205 mf->hf = CreateFontIndirect(&mf->lf);
\r
1208 // [HGM] This platform-dependent table provides the location for storing the color info
\r
1210 colorVariable[] = {
\r
1211 &whitePieceColor,
\r
1212 &blackPieceColor,
\r
1213 &lightSquareColor,
\r
1214 &darkSquareColor,
\r
1215 &highlightSquareColor,
\r
1216 &premoveHighlightColor,
\r
1218 &consoleBackgroundColor,
\r
1219 &appData.fontForeColorWhite,
\r
1220 &appData.fontBackColorWhite,
\r
1221 &appData.fontForeColorBlack,
\r
1222 &appData.fontBackColorBlack,
\r
1223 &appData.evalHistColorWhite,
\r
1224 &appData.evalHistColorBlack,
\r
1225 &appData.highlightArrowColor,
\r
1228 /* Command line font name parser. NULL name means do nothing.
\r
1229 Syntax like "Courier New:10.0 bi" or "Arial:10" or "Arial:10b"
\r
1230 For backward compatibility, syntax without the colon is also
\r
1231 accepted, but font names with digits in them won't work in that case.
\r
1234 ParseFontName(char *name, MyFontParams *mfp)
\r
1237 if (name == NULL) return;
\r
1239 q = strchr(p, ':');
\r
1241 if (q - p >= sizeof(mfp->faceName))
\r
1242 ExitArgError(_("Font name too long:"), name, TRUE);
\r
1243 memcpy(mfp->faceName, p, q - p);
\r
1244 mfp->faceName[q - p] = NULLCHAR;
\r
1247 q = mfp->faceName;
\r
1248 while (*p && !isdigit(*p)) {
\r
1250 if (q - mfp->faceName >= sizeof(mfp->faceName))
\r
1251 ExitArgError(_("Font name too long:"), name, TRUE);
\r
1253 while (q > mfp->faceName && q[-1] == ' ') q--;
\r
1256 if (!*p) ExitArgError(_("Font point size missing:"), name, TRUE);
\r
1257 mfp->pointSize = (float) atof(p);
\r
1258 mfp->bold = (strchr(p, 'b') != NULL);
\r
1259 mfp->italic = (strchr(p, 'i') != NULL);
\r
1260 mfp->underline = (strchr(p, 'u') != NULL);
\r
1261 mfp->strikeout = (strchr(p, 's') != NULL);
\r
1262 mfp->charset = DEFAULT_CHARSET;
\r
1263 q = strchr(p, 'c');
\r
1265 mfp->charset = (BYTE) atoi(q+1);
\r
1269 ParseFont(char *name, int number)
\r
1270 { // wrapper to shield back-end from 'font'
\r
1271 ParseFontName(name, &font[boardSize][number]->mfp);
\r
1276 { // in WB we have a 2D array of fonts; this initializes their description
\r
1278 /* Point font array elements to structures and
\r
1279 parse default font names */
\r
1280 for (i=0; i<NUM_FONTS; i++) {
\r
1281 for (j=0; j<NUM_SIZES; j++) {
\r
1282 font[j][i] = &fontRec[j][i];
\r
1283 ParseFontName(font[j][i]->def, &font[j][i]->mfp);
\r
1290 { // here we create the actual fonts from the selected descriptions
\r
1292 for (i=0; i<NUM_FONTS; i++) {
\r
1293 for (j=0; j<NUM_SIZES; j++) {
\r
1294 CreateFontInMF(font[j][i]);
\r
1298 /* Color name parser.
\r
1299 X version accepts X color names, but this one
\r
1300 handles only the #rrggbb form (hex) or rrr,ggg,bbb (decimal) */
\r
1302 ParseColorName(char *name)
\r
1304 int red, green, blue, count;
\r
1305 char buf[MSG_SIZ];
\r
1307 count = sscanf(name, "#%2x%2x%2x", &red, &green, &blue);
\r
1309 count = sscanf(name, "%3d%*[^0-9]%3d%*[^0-9]%3d",
\r
1310 &red, &green, &blue);
\r
1313 snprintf(buf, MSG_SIZ, _("Can't parse color name %s"), name);
\r
1314 DisplayError(buf, 0);
\r
1315 return RGB(0, 0, 0);
\r
1317 return PALETTERGB(red, green, blue);
\r
1321 ParseColor(int n, char *name)
\r
1322 { // for WinBoard the color is an int, which needs to be derived from the string
\r
1323 if(colorVariable[n]) *(int*)colorVariable[n] = ParseColorName(name);
\r
1327 ParseAttribs(COLORREF *color, int *effects, char* argValue)
\r
1329 char *e = argValue;
\r
1333 if (*e == 'b') eff |= CFE_BOLD;
\r
1334 else if (*e == 'i') eff |= CFE_ITALIC;
\r
1335 else if (*e == 'u') eff |= CFE_UNDERLINE;
\r
1336 else if (*e == 's') eff |= CFE_STRIKEOUT;
\r
1337 else if (*e == '#' || isdigit(*e)) break;
\r
1341 *color = ParseColorName(e);
\r
1345 ParseTextAttribs(ColorClass cc, char *s)
\r
1346 { // [HGM] front-end wrapper that does the platform-dependent call
\r
1347 // for XBoard we would set (&appData.colorShout)[cc] = strdup(s);
\r
1348 ParseAttribs(&textAttribs[cc].color, &textAttribs[cc].effects, s);
\r
1352 ParseBoardSize(void *addr, char *name)
\r
1353 { // [HGM] rewritten with return-value ptr to shield back-end from BoardSize
\r
1354 BoardSize bs = SizeTiny;
\r
1355 while (sizeInfo[bs].name != NULL) {
\r
1356 if (StrCaseCmp(name, sizeInfo[bs].name) == 0) {
\r
1357 *(BoardSize *)addr = bs;
\r
1362 ExitArgError(_("Unrecognized board size value"), name, TRUE);
\r
1367 { // [HGM] import name from appData first
\r
1370 for (cc = (ColorClass)0; cc < ColorNormal; cc++) {
\r
1371 textAttribs[cc].sound.name = strdup((&appData.soundShout)[cc]);
\r
1372 textAttribs[cc].sound.data = NULL;
\r
1373 MyLoadSound(&textAttribs[cc].sound);
\r
1375 for (cc = ColorNormal; cc < NColorClasses; cc++) {
\r
1376 textAttribs[cc].sound.name = strdup("");
\r
1377 textAttribs[cc].sound.data = NULL;
\r
1379 for (sc = (SoundClass)0; sc < NSoundClasses; sc++) {
\r
1380 sounds[sc].name = strdup((&appData.soundMove)[sc]);
\r
1381 sounds[sc].data = NULL;
\r
1382 MyLoadSound(&sounds[sc]);
\r
1387 SetCommPortDefaults()
\r
1389 memset(&dcb, 0, sizeof(DCB)); // required by VS 2002 +
\r
1390 dcb.DCBlength = sizeof(DCB);
\r
1391 dcb.BaudRate = 9600;
\r
1392 dcb.fBinary = TRUE;
\r
1393 dcb.fParity = FALSE;
\r
1394 dcb.fOutxCtsFlow = FALSE;
\r
1395 dcb.fOutxDsrFlow = FALSE;
\r
1396 dcb.fDtrControl = DTR_CONTROL_ENABLE;
\r
1397 dcb.fDsrSensitivity = FALSE;
\r
1398 dcb.fTXContinueOnXoff = TRUE;
\r
1399 dcb.fOutX = FALSE;
\r
1401 dcb.fNull = FALSE;
\r
1402 dcb.fRtsControl = RTS_CONTROL_ENABLE;
\r
1403 dcb.fAbortOnError = FALSE;
\r
1405 dcb.Parity = SPACEPARITY;
\r
1406 dcb.StopBits = ONESTOPBIT;
\r
1409 // [HGM] args: these three cases taken out to stay in front-end
\r
1411 SaveFontArg(FILE *f, ArgDescriptor *ad)
\r
1412 { // in WinBoard every board size has its own font, and the "argLoc" identifies the table,
\r
1413 // while the curent board size determines the element. This system should be ported to XBoard.
\r
1414 // What the table contains pointers to, and how to print the font description, remains platform-dependent
\r
1416 for (bs=0; bs<NUM_SIZES; bs++) {
\r
1417 MyFontParams *mfp = &font[bs][(int) ad->argLoc]->mfp;
\r
1418 fprintf(f, "/size=%s ", sizeInfo[bs].name);
\r
1419 fprintf(f, "/%s=\"%s:%g%s%s%s%s%sc%d\"\n",
\r
1420 ad->argName, mfp->faceName, mfp->pointSize,
\r
1421 mfp->bold || mfp->italic || mfp->underline || mfp->strikeout ? " " : "",
\r
1422 mfp->bold ? "b" : "",
\r
1423 mfp->italic ? "i" : "",
\r
1424 mfp->underline ? "u" : "",
\r
1425 mfp->strikeout ? "s" : "",
\r
1426 (int)mfp->charset);
\r
1432 { // [HGM] copy the names from the internal WB variables to appData
\r
1435 for (cc = (ColorClass)0; cc < ColorNormal; cc++)
\r
1436 (&appData.soundShout)[cc] = textAttribs[cc].sound.name;
\r
1437 for (sc = (SoundClass)0; sc < NSoundClasses; sc++)
\r
1438 (&appData.soundMove)[sc] = sounds[sc].name;
\r
1442 SaveAttribsArg(FILE *f, ArgDescriptor *ad)
\r
1443 { // here the "argLoc" defines a table index. It could have contained the 'ta' pointer itself, though
\r
1444 MyTextAttribs* ta = &textAttribs[(ColorClass)ad->argLoc];
\r
1445 fprintf(f, "/%s=\"%s%s%s%s%s#%02lx%02lx%02lx\"\n", ad->argName,
\r
1446 (ta->effects & CFE_BOLD) ? "b" : "",
\r
1447 (ta->effects & CFE_ITALIC) ? "i" : "",
\r
1448 (ta->effects & CFE_UNDERLINE) ? "u" : "",
\r
1449 (ta->effects & CFE_STRIKEOUT) ? "s" : "",
\r
1450 (ta->effects) ? " " : "",
\r
1451 ta->color&0xff, (ta->color >> 8)&0xff, (ta->color >> 16)&0xff);
\r
1455 SaveColor(FILE *f, ArgDescriptor *ad)
\r
1456 { // in WinBoard the color is an int and has to be converted to text. In X it would be a string already?
\r
1457 COLORREF color = *(COLORREF *)colorVariable[(int)ad->argLoc];
\r
1458 fprintf(f, "/%s=#%02lx%02lx%02lx\n", ad->argName,
\r
1459 color&0xff, (color>>8)&0xff, (color>>16)&0xff);
\r
1463 SaveBoardSize(FILE *f, char *name, void *addr)
\r
1464 { // wrapper to shield back-end from BoardSize & sizeInfo
\r
1465 fprintf(f, "/%s=%s\n", name, sizeInfo[*(BoardSize *)addr].name);
\r
1469 ParseCommPortSettings(char *s)
\r
1470 { // wrapper to keep dcb from back-end
\r
1471 ParseCommSettings(s, &dcb);
\r
1476 { // wrapper to shield use of window handles from back-end (make addressible by number?)
\r
1477 GetActualPlacement(hwndMain, &wpMain);
\r
1478 GetActualPlacement(hwndConsole, &wpConsole);
\r
1479 GetActualPlacement(commentDialog, &wpComment);
\r
1480 GetActualPlacement(editTagsDialog, &wpTags);
\r
1481 GetActualPlacement(gameListDialog, &wpGameList);
\r
1482 GetActualPlacement(moveHistoryDialog, &wpMoveHistory);
\r
1483 GetActualPlacement(evalGraphDialog, &wpEvalGraph);
\r
1484 GetActualPlacement(engineOutputDialog, &wpEngineOutput);
\r
1488 PrintCommPortSettings(FILE *f, char *name)
\r
1489 { // wrapper to shield back-end from DCB
\r
1490 PrintCommSettings(f, name, &dcb);
\r
1494 MySearchPath(char *installDir, char *name, char *fullname)
\r
1496 char *dummy, buf[MSG_SIZ], *p = name, *q;
\r
1497 if(name[0]== '%') {
\r
1498 fullname[0] = 0; // [HGM] first expand any environment variables in the given name
\r
1499 while(*p == '%' && (q = strchr(p+1, '%'))) { // [HGM] recognize %*% as environment variable
\r
1500 safeStrCpy(buf, p+1, sizeof(buf)/sizeof(buf[0]) );
\r
1501 *strchr(buf, '%') = 0;
\r
1502 strcat(fullname, getenv(buf));
\r
1503 p = q+1; while(*p == '\\') { strcat(fullname, "\\"); p++; }
\r
1505 strcat(fullname, p); // after environment variables (if any), take the remainder of the given name
\r
1506 if(appData.debugMode) fprintf(debugFP, "name = '%s', expanded name = '%s'\n", name, fullname);
\r
1507 return (int) strlen(fullname);
\r
1509 return (int) SearchPath(installDir, name, NULL, MSG_SIZ, fullname, &dummy);
\r
1513 MyGetFullPathName(char *name, char *fullname)
\r
1516 return (int) GetFullPathName(name, MSG_SIZ, fullname, &dummy);
\r
1521 { // [HGM] args: allows testing if main window is realized from back-end
\r
1522 return hwndMain != NULL;
\r
1526 PopUpStartupDialog()
\r
1530 LoadLanguageFile(appData.language);
\r
1531 lpProc = MakeProcInstance((FARPROC)StartupDialog, hInst);
\r
1532 DialogBox(hInst, MAKEINTRESOURCE(DLG_Startup), NULL, (DLGPROC)lpProc);
\r
1533 FreeProcInstance(lpProc);
\r
1536 /*---------------------------------------------------------------------------*\
\r
1538 * GDI board drawing routines
\r
1540 \*---------------------------------------------------------------------------*/
\r
1542 /* [AS] Draw square using background texture */
\r
1543 static void DrawTile( int dx, int dy, int dw, int dh, HDC dst, HDC src, int mode, int sx, int sy )
\r
1548 return; /* Should never happen! */
\r
1551 SetGraphicsMode( dst, GM_ADVANCED );
\r
1558 /* X reflection */
\r
1563 x.eDx = (FLOAT) dw + dx - 1;
\r
1566 SetWorldTransform( dst, &x );
\r
1569 /* Y reflection */
\r
1575 x.eDy = (FLOAT) dh + dy - 1;
\r
1577 SetWorldTransform( dst, &x );
\r
1585 x.eDx = (FLOAT) dx;
\r
1586 x.eDy = (FLOAT) dy;
\r
1589 SetWorldTransform( dst, &x );
\r
1593 BitBlt( dst, dx, dy, dw, dh, src, sx, sy, SRCCOPY );
\r
1601 SetWorldTransform( dst, &x );
\r
1603 ModifyWorldTransform( dst, 0, MWT_IDENTITY );
\r
1606 /* [AS] [HGM] Make room for more piece types, so all pieces can be different */
\r
1608 PM_WP = (int) WhitePawn,
\r
1609 PM_WN = (int) WhiteKnight,
\r
1610 PM_WB = (int) WhiteBishop,
\r
1611 PM_WR = (int) WhiteRook,
\r
1612 PM_WQ = (int) WhiteQueen,
\r
1613 PM_WF = (int) WhiteFerz,
\r
1614 PM_WW = (int) WhiteWazir,
\r
1615 PM_WE = (int) WhiteAlfil,
\r
1616 PM_WM = (int) WhiteMan,
\r
1617 PM_WO = (int) WhiteCannon,
\r
1618 PM_WU = (int) WhiteUnicorn,
\r
1619 PM_WH = (int) WhiteNightrider,
\r
1620 PM_WA = (int) WhiteAngel,
\r
1621 PM_WC = (int) WhiteMarshall,
\r
1622 PM_WAB = (int) WhiteCardinal,
\r
1623 PM_WD = (int) WhiteDragon,
\r
1624 PM_WL = (int) WhiteLance,
\r
1625 PM_WS = (int) WhiteCobra,
\r
1626 PM_WV = (int) WhiteFalcon,
\r
1627 PM_WSG = (int) WhiteSilver,
\r
1628 PM_WG = (int) WhiteGrasshopper,
\r
1629 PM_WK = (int) WhiteKing,
\r
1630 PM_BP = (int) BlackPawn,
\r
1631 PM_BN = (int) BlackKnight,
\r
1632 PM_BB = (int) BlackBishop,
\r
1633 PM_BR = (int) BlackRook,
\r
1634 PM_BQ = (int) BlackQueen,
\r
1635 PM_BF = (int) BlackFerz,
\r
1636 PM_BW = (int) BlackWazir,
\r
1637 PM_BE = (int) BlackAlfil,
\r
1638 PM_BM = (int) BlackMan,
\r
1639 PM_BO = (int) BlackCannon,
\r
1640 PM_BU = (int) BlackUnicorn,
\r
1641 PM_BH = (int) BlackNightrider,
\r
1642 PM_BA = (int) BlackAngel,
\r
1643 PM_BC = (int) BlackMarshall,
\r
1644 PM_BG = (int) BlackGrasshopper,
\r
1645 PM_BAB = (int) BlackCardinal,
\r
1646 PM_BD = (int) BlackDragon,
\r
1647 PM_BL = (int) BlackLance,
\r
1648 PM_BS = (int) BlackCobra,
\r
1649 PM_BV = (int) BlackFalcon,
\r
1650 PM_BSG = (int) BlackSilver,
\r
1651 PM_BK = (int) BlackKing
\r
1654 static HFONT hPieceFont = NULL;
\r
1655 static HBITMAP hPieceMask[(int) EmptySquare];
\r
1656 static HBITMAP hPieceFace[(int) EmptySquare];
\r
1657 static int fontBitmapSquareSize = 0;
\r
1658 static char pieceToFontChar[(int) EmptySquare] =
\r
1659 { 'p', 'n', 'b', 'r', 'q',
\r
1660 'n', 'b', 'p', 'n', 'b', 'r', 'b', 'r', 'q', 'k',
\r
1661 'k', 'o', 'm', 'v', 't', 'w',
\r
1662 'v', 't', 'o', 'm', 'v', 't', 'v', 't', 'w', 'l',
\r
1665 extern BOOL SetCharTable( char *table, const char * map );
\r
1666 /* [HGM] moved to backend.c */
\r
1668 static void SetPieceBackground( HDC hdc, COLORREF color, int mode )
\r
1671 BYTE r1 = GetRValue( color );
\r
1672 BYTE g1 = GetGValue( color );
\r
1673 BYTE b1 = GetBValue( color );
\r
1679 /* Create a uniform background first */
\r
1680 hbrush = CreateSolidBrush( color );
\r
1681 SetRect( &rc, 0, 0, squareSize, squareSize );
\r
1682 FillRect( hdc, &rc, hbrush );
\r
1683 DeleteObject( hbrush );
\r
1686 /* Vertical gradient, good for pawn, knight and rook, less for queen and king */
\r
1687 int steps = squareSize / 2;
\r
1690 for( i=0; i<steps; i++ ) {
\r
1691 BYTE r = r1 - (r1-r2) * i / steps;
\r
1692 BYTE g = g1 - (g1-g2) * i / steps;
\r
1693 BYTE b = b1 - (b1-b2) * i / steps;
\r
1695 hbrush = CreateSolidBrush( RGB(r,g,b) );
\r
1696 SetRect( &rc, i + squareSize - steps, 0, i + squareSize - steps + 1, squareSize );
\r
1697 FillRect( hdc, &rc, hbrush );
\r
1698 DeleteObject(hbrush);
\r
1701 else if( mode == 2 ) {
\r
1702 /* Diagonal gradient, good more or less for every piece */
\r
1703 POINT triangle[3];
\r
1704 HPEN hpen = SelectObject( hdc, GetStockObject(NULL_PEN) );
\r
1705 HBRUSH hbrush_old;
\r
1706 int steps = squareSize;
\r
1709 triangle[0].x = squareSize - steps;
\r
1710 triangle[0].y = squareSize;
\r
1711 triangle[1].x = squareSize;
\r
1712 triangle[1].y = squareSize;
\r
1713 triangle[2].x = squareSize;
\r
1714 triangle[2].y = squareSize - steps;
\r
1716 for( i=0; i<steps; i++ ) {
\r
1717 BYTE r = r1 - (r1-r2) * i / steps;
\r
1718 BYTE g = g1 - (g1-g2) * i / steps;
\r
1719 BYTE b = b1 - (b1-b2) * i / steps;
\r
1721 hbrush = CreateSolidBrush( RGB(r,g,b) );
\r
1722 hbrush_old = SelectObject( hdc, hbrush );
\r
1723 Polygon( hdc, triangle, 3 );
\r
1724 SelectObject( hdc, hbrush_old );
\r
1725 DeleteObject(hbrush);
\r
1730 SelectObject( hdc, hpen );
\r
1735 [AS] The method I use to create the bitmaps it a bit tricky, but it
\r
1736 seems to work ok. The main problem here is to find the "inside" of a chess
\r
1737 piece: follow the steps as explained below.
\r
1739 static void CreatePieceMaskFromFont( HDC hdc_window, HDC hdc, int index )
\r
1743 COLORREF chroma = RGB(0xFF,0x00,0xFF);
\r
1747 int backColor = whitePieceColor;
\r
1748 int foreColor = blackPieceColor;
\r
1750 if( index < (int)BlackPawn && appData.fontBackColorWhite != appData.fontForeColorWhite ) {
\r
1751 backColor = appData.fontBackColorWhite;
\r
1752 foreColor = appData.fontForeColorWhite;
\r
1754 else if( index >= (int)BlackPawn && appData.fontBackColorBlack != appData.fontForeColorBlack ) {
\r
1755 backColor = appData.fontBackColorBlack;
\r
1756 foreColor = appData.fontForeColorBlack;
\r
1760 hbm = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );
\r
1762 hbm_old = SelectObject( hdc, hbm );
\r
1766 rc.right = squareSize;
\r
1767 rc.bottom = squareSize;
\r
1769 /* Step 1: background is now black */
\r
1770 FillRect( hdc, &rc, GetStockObject(BLACK_BRUSH) );
\r
1772 GetTextExtentPoint32( hdc, &pieceToFontChar[index], 1, &sz );
\r
1774 pt.x = (squareSize - sz.cx) / 2;
\r
1775 pt.y = (squareSize - sz.cy) / 2;
\r
1777 SetBkMode( hdc, TRANSPARENT );
\r
1778 SetTextColor( hdc, chroma );
\r
1779 /* Step 2: the piece has been drawn in purple, there are now black and purple in this bitmap */
\r
1780 TextOut( hdc, pt.x, pt.y, &pieceToFontChar[appData.allWhite && index >= (int)BlackPawn ? index - (int)BlackPawn : index], 1 );
\r
1782 SelectObject( hdc, GetStockObject(WHITE_BRUSH) );
\r
1783 /* Step 3: the area outside the piece is filled with white */
\r
1784 // FloodFill( hdc, 0, 0, chroma );
\r
1785 ExtFloodFill( hdc, 0, 0, 0, FLOODFILLSURFACE );
\r
1786 ExtFloodFill( hdc, 0, squareSize-1, 0, FLOODFILLSURFACE ); // [HGM] fill from all 4 corners, for if piece too big
\r
1787 ExtFloodFill( hdc, squareSize-1, 0, 0, FLOODFILLSURFACE );
\r
1788 ExtFloodFill( hdc, squareSize-1, squareSize-1, 0, FLOODFILLSURFACE );
\r
1789 SelectObject( hdc, GetStockObject(BLACK_BRUSH) );
\r
1791 Step 4: this is the tricky part, the area inside the piece is filled with black,
\r
1792 but if the start point is not inside the piece we're lost!
\r
1793 There should be a better way to do this... if we could create a region or path
\r
1794 from the fill operation we would be fine for example.
\r
1796 // FloodFill( hdc, squareSize / 2, squareSize / 2, RGB(0xFF,0xFF,0xFF) );
\r
1797 ExtFloodFill( hdc, squareSize / 2, squareSize / 2, RGB(0xFF,0xFF,0xFF), FLOODFILLBORDER );
\r
1799 { /* [HGM] shave off edges of mask, in an attempt to correct for the fact that FloodFill does not work correctly under Win XP */
\r
1800 HDC dc2 = CreateCompatibleDC( hdc_window );
\r
1801 HBITMAP bm2 = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );
\r
1803 SelectObject( dc2, bm2 );
\r
1804 BitBlt( dc2, 0, 0, squareSize, squareSize, hdc, 0, 0, SRCCOPY ); // make copy
\r
1805 BitBlt( hdc, 0, 1, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );
\r
1806 BitBlt( hdc, 2, 1, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );
\r
1807 BitBlt( hdc, 1, 0, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );
\r
1808 BitBlt( hdc, 1, 2, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );
\r
1811 DeleteObject( bm2 );
\r
1814 SetTextColor( hdc, 0 );
\r
1816 Step 5: some fonts have "disconnected" areas that are skipped by the fill:
\r
1817 draw the piece again in black for safety.
\r
1819 TextOut( hdc, pt.x, pt.y, &pieceToFontChar[appData.allWhite && index >= (int)BlackPawn ? index - (int)BlackPawn : index], 1 );
\r
1821 SelectObject( hdc, hbm_old );
\r
1823 if( hPieceMask[index] != NULL ) {
\r
1824 DeleteObject( hPieceMask[index] );
\r
1827 hPieceMask[index] = hbm;
\r
1830 hbm = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );
\r
1832 SelectObject( hdc, hbm );
\r
1835 HDC dc1 = CreateCompatibleDC( hdc_window );
\r
1836 HDC dc2 = CreateCompatibleDC( hdc_window );
\r
1837 HBITMAP bm2 = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );
\r
1839 SelectObject( dc1, hPieceMask[index] );
\r
1840 SelectObject( dc2, bm2 );
\r
1841 FillRect( dc2, &rc, GetStockObject(WHITE_BRUSH) );
\r
1842 BitBlt( dc2, 0, 0, squareSize, squareSize, dc1, 0, 0, SRCINVERT );
\r
1845 Now dc2 contains the inverse of the piece mask, i.e. a mask that preserves
\r
1846 the piece background and deletes (makes transparent) the rest.
\r
1847 Thanks to that mask, we are free to paint the background with the greates
\r
1848 freedom, as we'll be able to mask off the unwanted parts when finished.
\r
1849 We use this, to make gradients and give the pieces a "roundish" look.
\r
1851 SetPieceBackground( hdc, backColor, 2 );
\r
1852 BitBlt( hdc, 0, 0, squareSize, squareSize, dc2, 0, 0, SRCAND );
\r
1856 DeleteObject( bm2 );
\r
1859 SetTextColor( hdc, foreColor );
\r
1860 TextOut( hdc, pt.x, pt.y, &pieceToFontChar[appData.allWhite && index >= (int)BlackPawn ? index - (int)BlackPawn : index], 1 );
\r
1862 SelectObject( hdc, hbm_old );
\r
1864 if( hPieceFace[index] != NULL ) {
\r
1865 DeleteObject( hPieceFace[index] );
\r
1868 hPieceFace[index] = hbm;
\r
1871 static int TranslatePieceToFontPiece( int piece )
\r
1901 case BlackMarshall:
\r
1905 case BlackNightrider:
\r
1911 case BlackUnicorn:
\r
1915 case BlackGrasshopper:
\r
1927 case BlackCardinal:
\r
1934 case WhiteMarshall:
\r
1938 case WhiteNightrider:
\r
1944 case WhiteUnicorn:
\r
1948 case WhiteGrasshopper:
\r
1960 case WhiteCardinal:
\r
1969 void CreatePiecesFromFont()
\r
1972 HDC hdc_window = NULL;
\r
1978 if( fontBitmapSquareSize < 0 ) {
\r
1979 /* Something went seriously wrong in the past: do not try to recreate fonts! */
\r
1983 if( !appData.useFont || appData.renderPiecesWithFont == NULL ||
\r
1984 appData.renderPiecesWithFont[0] == NULLCHAR || appData.renderPiecesWithFont[0] == '*' ) {
\r
1985 fontBitmapSquareSize = -1;
\r
1989 if( fontBitmapSquareSize != squareSize ) {
\r
1990 hdc_window = GetDC( hwndMain );
\r
1991 hdc = CreateCompatibleDC( hdc_window );
\r
1993 if( hPieceFont != NULL ) {
\r
1994 DeleteObject( hPieceFont );
\r
1997 for( i=0; i<=(int)BlackKing; i++ ) {
\r
1998 hPieceMask[i] = NULL;
\r
1999 hPieceFace[i] = NULL;
\r
2005 if( appData.fontPieceSize >= 50 && appData.fontPieceSize <= 150 ) {
\r
2006 fontHeight = appData.fontPieceSize;
\r
2009 fontHeight = (fontHeight * squareSize) / 100;
\r
2011 lf.lfHeight = -MulDiv( fontHeight, GetDeviceCaps(hdc, LOGPIXELSY), 72 );
\r
2013 lf.lfEscapement = 0;
\r
2014 lf.lfOrientation = 0;
\r
2015 lf.lfWeight = FW_NORMAL;
\r
2017 lf.lfUnderline = 0;
\r
2018 lf.lfStrikeOut = 0;
\r
2019 lf.lfCharSet = DEFAULT_CHARSET;
\r
2020 lf.lfOutPrecision = OUT_DEFAULT_PRECIS;
\r
2021 lf.lfClipPrecision = CLIP_DEFAULT_PRECIS;
\r
2022 lf.lfQuality = PROOF_QUALITY;
\r
2023 lf.lfPitchAndFamily = DEFAULT_PITCH | FF_DONTCARE;
\r
2024 strncpy( lf.lfFaceName, appData.renderPiecesWithFont, sizeof(lf.lfFaceName) );
\r
2025 lf.lfFaceName[ sizeof(lf.lfFaceName) - 1 ] = '\0';
\r
2027 hPieceFont = CreateFontIndirect( &lf );
\r
2029 if( hPieceFont == NULL ) {
\r
2030 fontBitmapSquareSize = -2;
\r
2033 /* Setup font-to-piece character table */
\r
2034 if( ! SetCharTable(pieceToFontChar, appData.fontToPieceTable) ) {
\r
2035 /* No (or wrong) global settings, try to detect the font */
\r
2036 if( strstr(lf.lfFaceName,"Alpha") != NULL ) {
\r
2038 SetCharTable(pieceToFontChar, "phbrqkojntwl");
\r
2040 else if( strstr(lf.lfFaceName,"DiagramTT") != NULL ) {
\r
2041 /* DiagramTT* family */
\r
2042 SetCharTable(pieceToFontChar, "PNLRQKpnlrqk");
\r
2044 else if( strstr(lf.lfFaceName,"WinboardF") != NULL ) {
\r
2045 /* Fairy symbols */
\r
2046 SetCharTable(pieceToFontChar, "PNBRQFEACWMOHIJGDVSLUKpnbrqfeacwmohijgdvsluk");
\r
2048 else if( strstr(lf.lfFaceName,"GC2004D") != NULL ) {
\r
2049 /* Good Companion (Some characters get warped as literal :-( */
\r
2050 char s[] = "1cmWG0??S??oYI23wgQU";
\r
2051 s[0]=0xB9; s[1]=0xA9; s[6]=0xB1; s[11]=0xBB; s[12]=0xAB; s[17]=0xB3;
\r
2052 SetCharTable(pieceToFontChar, s);
\r
2055 /* Cases, Condal, Leipzig, Lucena, Marroquin, Merida, Usual */
\r
2056 SetCharTable(pieceToFontChar, "pnbrqkomvtwl");
\r
2060 /* Create bitmaps */
\r
2061 hfont_old = SelectObject( hdc, hPieceFont );
\r
2062 for(i=(int)WhitePawn; i<(int)EmptySquare; i++) /* [HGM] made a loop for this */
\r
2063 if(PieceToChar((ChessSquare)i) != '.') /* skip unused pieces */
\r
2064 CreatePieceMaskFromFont( hdc_window, hdc, i );
\r
2066 SelectObject( hdc, hfont_old );
\r
2068 fontBitmapSquareSize = squareSize;
\r
2072 if( hdc != NULL ) {
\r
2076 if( hdc_window != NULL ) {
\r
2077 ReleaseDC( hwndMain, hdc_window );
\r
2082 DoLoadBitmap(HINSTANCE hinst, char *piece, int squareSize, char *suffix)
\r
2086 snprintf(name, sizeof(name)/sizeof(name[0]), "%s%d%s", piece, squareSize, suffix);
\r
2087 if (gameInfo.event &&
\r
2088 strcmp(gameInfo.event, "Easter Egg Hunt") == 0 &&
\r
2089 strcmp(name, "k80s") == 0) {
\r
2090 safeStrCpy(name, "tim", sizeof(name)/sizeof(name[0]) );
\r
2092 return LoadBitmap(hinst, name);
\r
2096 /* Insert a color into the program's logical palette
\r
2097 structure. This code assumes the given color is
\r
2098 the result of the RGB or PALETTERGB macro, and it
\r
2099 knows how those macros work (which is documented).
\r
2102 InsertInPalette(COLORREF color)
\r
2104 LPPALETTEENTRY pe = &(pLogPal->palPalEntry[pLogPal->palNumEntries]);
\r
2106 if (pLogPal->palNumEntries++ >= PALETTESIZE) {
\r
2107 DisplayFatalError(_("Too many colors"), 0, 1);
\r
2108 pLogPal->palNumEntries--;
\r
2112 pe->peFlags = (char) 0;
\r
2113 pe->peRed = (char) (0xFF & color);
\r
2114 pe->peGreen = (char) (0xFF & (color >> 8));
\r
2115 pe->peBlue = (char) (0xFF & (color >> 16));
\r
2121 InitDrawingColors()
\r
2123 if (pLogPal == NULL) {
\r
2124 /* Allocate enough memory for a logical palette with
\r
2125 * PALETTESIZE entries and set the size and version fields
\r
2126 * of the logical palette structure.
\r
2128 pLogPal = (NPLOGPALETTE)
\r
2129 LocalAlloc(LMEM_FIXED, (sizeof(LOGPALETTE) +
\r
2130 (sizeof(PALETTEENTRY) * (PALETTESIZE))));
\r
2131 pLogPal->palVersion = 0x300;
\r
2133 pLogPal->palNumEntries = 0;
\r
2135 InsertInPalette(lightSquareColor);
\r
2136 InsertInPalette(darkSquareColor);
\r
2137 InsertInPalette(whitePieceColor);
\r
2138 InsertInPalette(blackPieceColor);
\r
2139 InsertInPalette(highlightSquareColor);
\r
2140 InsertInPalette(premoveHighlightColor);
\r
2142 /* create a logical color palette according the information
\r
2143 * in the LOGPALETTE structure.
\r
2145 hPal = CreatePalette((LPLOGPALETTE) pLogPal);
\r
2147 lightSquareBrush = CreateSolidBrush(lightSquareColor);
\r
2148 blackSquareBrush = CreateSolidBrush(blackPieceColor);
\r
2149 darkSquareBrush = CreateSolidBrush(darkSquareColor);
\r
2150 whitePieceBrush = CreateSolidBrush(whitePieceColor);
\r
2151 blackPieceBrush = CreateSolidBrush(blackPieceColor);
\r
2152 iconBkgndBrush = CreateSolidBrush(GetSysColor(COLOR_BACKGROUND));
\r
2153 explodeBrush = CreateSolidBrush(highlightSquareColor); // [HGM] atomic
\r
2154 markerBrush = CreateSolidBrush(premoveHighlightColor); // [HGM] markers
\r
2155 /* [AS] Force rendering of the font-based pieces */
\r
2156 if( fontBitmapSquareSize > 0 ) {
\r
2157 fontBitmapSquareSize = 0;
\r
2163 BoardWidth(int boardSize, int n)
\r
2164 { /* [HGM] argument n added to allow different width and height */
\r
2165 int lineGap = sizeInfo[boardSize].lineGap;
\r
2167 if( appData.overrideLineGap >= 0 && appData.overrideLineGap <= 5 ) {
\r
2168 lineGap = appData.overrideLineGap;
\r
2171 return (n + 1) * lineGap +
\r
2172 n * sizeInfo[boardSize].squareSize;
\r
2175 /* Respond to board resize by dragging edge */
\r
2177 ResizeBoard(int newSizeX, int newSizeY, int flags)
\r
2179 BoardSize newSize = NUM_SIZES - 1;
\r
2180 static int recurse = 0;
\r
2181 if (IsIconic(hwndMain)) return;
\r
2182 if (recurse > 0) return;
\r
2184 while (newSize > 0) {
\r
2185 InitDrawingSizes(newSize+1000, 0); // [HGM] kludge to update sizeInfo without visible effects
\r
2186 if(newSizeX >= sizeInfo[newSize].cliWidth &&
\r
2187 newSizeY >= sizeInfo[newSize].cliHeight) break;
\r
2190 boardSize = newSize;
\r
2191 InitDrawingSizes(boardSize, flags);
\r
2196 extern Boolean twoBoards, partnerUp; // [HGM] dual
\r
2199 InitDrawingSizes(BoardSize boardSize, int flags)
\r
2201 int i, boardWidth, boardHeight; /* [HGM] height treated separately */
\r
2202 ChessSquare piece;
\r
2203 static int oldBoardSize = -1, oldTinyLayout = 0;
\r
2205 SIZE clockSize, messageSize;
\r
2207 char buf[MSG_SIZ];
\r
2209 HMENU hmenu = GetMenu(hwndMain);
\r
2210 RECT crect, wrect, oldRect;
\r
2212 LOGBRUSH logbrush;
\r
2214 int suppressVisibleEffects = 0; // [HGM] kludge to request updating sizeInfo only
\r
2215 if((int)boardSize >= 1000 ) { boardSize -= 1000; suppressVisibleEffects = 1; }
\r
2217 /* [HGM] call with -2 uses old size (for if nr of files, ranks changes) */
\r
2218 if(boardSize == (BoardSize)(-2) ) boardSize = oldBoardSize;
\r
2220 oldRect.left = wpMain.x; //[HGM] placement: remember previous window params
\r
2221 oldRect.top = wpMain.y;
\r
2222 oldRect.right = wpMain.x + wpMain.width;
\r
2223 oldRect.bottom = wpMain.y + wpMain.height;
\r
2225 tinyLayout = sizeInfo[boardSize].tinyLayout;
\r
2226 smallLayout = sizeInfo[boardSize].smallLayout;
\r
2227 squareSize = sizeInfo[boardSize].squareSize;
\r
2228 lineGap = sizeInfo[boardSize].lineGap;
\r
2229 minorSize = 0; /* [HGM] Kludge to see if demagnified pieces need to be shifted */
\r
2231 if( appData.overrideLineGap >= 0 && appData.overrideLineGap <= 5 ) {
\r
2232 lineGap = appData.overrideLineGap;
\r
2235 if (tinyLayout != oldTinyLayout) {
\r
2236 long style = GetWindowLongPtr(hwndMain, GWL_STYLE);
\r
2238 style &= ~WS_SYSMENU;
\r
2239 InsertMenu(hmenu, IDM_Exit, MF_BYCOMMAND, IDM_Minimize,
\r
2240 "&Minimize\tCtrl+F4");
\r
2242 style |= WS_SYSMENU;
\r
2243 RemoveMenu(hmenu, IDM_Minimize, MF_BYCOMMAND);
\r
2245 SetWindowLongPtr(hwndMain, GWL_STYLE, style);
\r
2247 for (i=0; menuBarText[tinyLayout][i]; i++) {
\r
2248 ModifyMenu(hmenu, i, MF_STRING|MF_BYPOSITION|MF_POPUP,
\r
2249 (UINT)GetSubMenu(hmenu, i), T_(menuBarText[tinyLayout][i]));
\r
2251 DrawMenuBar(hwndMain);
\r
2254 boardWidth = BoardWidth(boardSize, BOARD_WIDTH);
\r
2255 boardHeight = BoardWidth(boardSize, BOARD_HEIGHT);
\r
2257 /* Get text area sizes */
\r
2258 hdc = GetDC(hwndMain);
\r
2259 if (appData.clockMode) {
\r
2260 snprintf(buf, MSG_SIZ, _("White: %s"), TimeString(23*60*60*1000L));
\r
2262 snprintf(buf, MSG_SIZ, _("White"));
\r
2264 oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);
\r
2265 GetTextExtentPoint(hdc, buf, strlen(buf), &clockSize);
\r
2266 SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);
\r
2267 str = _("We only care about the height here");
\r
2268 GetTextExtentPoint(hdc, str, strlen(str), &messageSize);
\r
2269 SelectObject(hdc, oldFont);
\r
2270 ReleaseDC(hwndMain, hdc);
\r
2272 /* Compute where everything goes */
\r
2273 if((first.programLogo || second.programLogo) && !tinyLayout) {
\r
2274 /* [HGM] logo: if either logo is on, reserve space for it */
\r
2275 logoHeight = 2*clockSize.cy;
\r
2276 leftLogoRect.left = OUTER_MARGIN;
\r
2277 leftLogoRect.right = leftLogoRect.left + 4*clockSize.cy;
\r
2278 leftLogoRect.top = OUTER_MARGIN;
\r
2279 leftLogoRect.bottom = OUTER_MARGIN + logoHeight;
\r
2281 rightLogoRect.right = OUTER_MARGIN + boardWidth;
\r
2282 rightLogoRect.left = rightLogoRect.right - 4*clockSize.cy;
\r
2283 rightLogoRect.top = OUTER_MARGIN;
\r
2284 rightLogoRect.bottom = OUTER_MARGIN + logoHeight;
\r
2287 whiteRect.left = leftLogoRect.right;
\r
2288 whiteRect.right = OUTER_MARGIN + boardWidth/2 - INNER_MARGIN/2;
\r
2289 whiteRect.top = OUTER_MARGIN;
\r
2290 whiteRect.bottom = whiteRect.top + logoHeight;
\r
2292 blackRect.right = rightLogoRect.left;
\r
2293 blackRect.left = whiteRect.right + INNER_MARGIN;
\r
2294 blackRect.top = whiteRect.top;
\r
2295 blackRect.bottom = whiteRect.bottom;
\r
2297 whiteRect.left = OUTER_MARGIN;
\r
2298 whiteRect.right = whiteRect.left + boardWidth/2 - INNER_MARGIN/2;
\r
2299 whiteRect.top = OUTER_MARGIN;
\r
2300 whiteRect.bottom = whiteRect.top + clockSize.cy;
\r
2302 blackRect.left = whiteRect.right + INNER_MARGIN;
\r
2303 blackRect.right = blackRect.left + boardWidth/2 - 1;
\r
2304 blackRect.top = whiteRect.top;
\r
2305 blackRect.bottom = whiteRect.bottom;
\r
2307 logoHeight = 0; // [HGM] logo: suppress logo after change to tiny layout!
\r
2310 messageRect.left = OUTER_MARGIN + MESSAGE_LINE_LEFTMARGIN;
\r
2311 if (appData.showButtonBar) {
\r
2312 messageRect.right = OUTER_MARGIN + boardWidth // [HGM] logo: expressed independent of clock placement
\r
2313 - N_BUTTONS*BUTTON_WIDTH - MESSAGE_LINE_LEFTMARGIN;
\r
2315 messageRect.right = OUTER_MARGIN + boardWidth;
\r
2317 messageRect.top = whiteRect.bottom + INNER_MARGIN;
\r
2318 messageRect.bottom = messageRect.top + messageSize.cy;
\r
2320 boardRect.left = OUTER_MARGIN;
\r
2321 boardRect.right = boardRect.left + boardWidth;
\r
2322 boardRect.top = messageRect.bottom + INNER_MARGIN;
\r
2323 boardRect.bottom = boardRect.top + boardHeight;
\r
2325 sizeInfo[boardSize].cliWidth = boardRect.right + OUTER_MARGIN;
\r
2326 sizeInfo[boardSize].cliHeight = boardRect.bottom + OUTER_MARGIN;
\r
2327 oldBoardSize = boardSize;
\r
2328 oldTinyLayout = tinyLayout;
\r
2329 winW = 2 * GetSystemMetrics(SM_CXFRAME) + boardRect.right + OUTER_MARGIN;
\r
2330 winH = 2 * GetSystemMetrics(SM_CYFRAME) + GetSystemMetrics(SM_CYMENU) +
\r
2331 GetSystemMetrics(SM_CYCAPTION) + boardRect.bottom + OUTER_MARGIN;
\r
2332 winW *= 1 + twoBoards;
\r
2333 if(suppressVisibleEffects) return; // [HGM] when called for filling sizeInfo only
\r
2334 wpMain.width = winW; // [HGM] placement: set through temporary which can used by initial sizing choice
\r
2335 wpMain.height = winH; // without disturbing window attachments
\r
2336 GetWindowRect(hwndMain, &wrect);
\r
2337 SetWindowPos(hwndMain, NULL, 0, 0, wpMain.width, wpMain.height,
\r
2338 SWP_NOCOPYBITS|SWP_NOZORDER|SWP_NOMOVE);
\r
2340 // [HGM] placement: let attached windows follow size change.
\r
2341 ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, moveHistoryDialog, &wpMoveHistory );
\r
2342 ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, evalGraphDialog, &wpEvalGraph );
\r
2343 ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, engineOutputDialog, &wpEngineOutput );
\r
2344 ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, gameListDialog, &wpGameList );
\r
2345 ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, hwndConsole, &wpConsole );
\r
2347 /* compensate if menu bar wrapped */
\r
2348 GetClientRect(hwndMain, &crect);
\r
2349 offby = boardRect.bottom + OUTER_MARGIN - crect.bottom;
\r
2350 wpMain.height += offby;
\r
2352 case WMSZ_TOPLEFT:
\r
2353 SetWindowPos(hwndMain, NULL,
\r
2354 wrect.right - wpMain.width, wrect.bottom - wpMain.height,
\r
2355 wpMain.width, wpMain.height, SWP_NOCOPYBITS|SWP_NOZORDER);
\r
2358 case WMSZ_TOPRIGHT:
\r
2360 SetWindowPos(hwndMain, NULL,
\r
2361 wrect.left, wrect.bottom - wpMain.height,
\r
2362 wpMain.width, wpMain.height, SWP_NOCOPYBITS|SWP_NOZORDER);
\r
2365 case WMSZ_BOTTOMLEFT:
\r
2367 SetWindowPos(hwndMain, NULL,
\r
2368 wrect.right - wpMain.width, wrect.top,
\r
2369 wpMain.width, wpMain.height, SWP_NOCOPYBITS|SWP_NOZORDER);
\r
2372 case WMSZ_BOTTOMRIGHT:
\r
2376 SetWindowPos(hwndMain, NULL, 0, 0, wpMain.width, wpMain.height,
\r
2377 SWP_NOCOPYBITS|SWP_NOZORDER|SWP_NOMOVE);
\r
2382 for (i = 0; i < N_BUTTONS; i++) {
\r
2383 if (buttonDesc[i].hwnd != NULL) {
\r
2384 DestroyWindow(buttonDesc[i].hwnd);
\r
2385 buttonDesc[i].hwnd = NULL;
\r
2387 if (appData.showButtonBar) {
\r
2388 buttonDesc[i].hwnd =
\r
2389 CreateWindow("BUTTON", buttonDesc[i].label,
\r
2390 WS_VISIBLE | WS_CHILD | BS_PUSHBUTTON,
\r
2391 boardRect.right - BUTTON_WIDTH*(N_BUTTONS-i),
\r
2392 messageRect.top, BUTTON_WIDTH, messageSize.cy, hwndMain,
\r
2393 (HMENU) buttonDesc[i].id,
\r
2394 (HINSTANCE) GetWindowLongPtr(hwndMain, GWLP_HINSTANCE), NULL);
\r
2396 SendMessage(buttonDesc[i].hwnd, WM_SETFONT,
\r
2397 (WPARAM)font[boardSize][MESSAGE_FONT]->hf,
\r
2398 MAKELPARAM(FALSE, 0));
\r
2400 if (buttonDesc[i].id == IDM_Pause)
\r
2401 hwndPause = buttonDesc[i].hwnd;
\r
2402 buttonDesc[i].wndproc = (WNDPROC)
\r
2403 SetWindowLongPtr(buttonDesc[i].hwnd, GWLP_WNDPROC, (LONG_PTR) ButtonProc);
\r
2406 if (gridPen != NULL) DeleteObject(gridPen);
\r
2407 if (highlightPen != NULL) DeleteObject(highlightPen);
\r
2408 if (premovePen != NULL) DeleteObject(premovePen);
\r
2409 if (lineGap != 0) {
\r
2410 logbrush.lbStyle = BS_SOLID;
\r
2411 logbrush.lbColor = RGB(0, 0, 0); /* grid pen color = black */
\r
2413 ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,
\r
2414 lineGap, &logbrush, 0, NULL);
\r
2415 logbrush.lbColor = highlightSquareColor;
\r
2417 ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,
\r
2418 lineGap, &logbrush, 0, NULL);
\r
2420 logbrush.lbColor = premoveHighlightColor;
\r
2422 ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,
\r
2423 lineGap, &logbrush, 0, NULL);
\r
2425 /* [HGM] Loop had to be split in part for vert. and hor. lines */
\r
2426 for (i = 0; i < BOARD_HEIGHT + 1; i++) {
\r
2427 gridEndpoints[i*2].x = boardRect.left + lineGap / 2;
\r
2428 gridEndpoints[i*2].y = gridEndpoints[i*2 + 1].y =
\r
2429 boardRect.top + lineGap / 2 + (i * (squareSize + lineGap));
\r
2430 gridEndpoints[i*2 + 1].x = boardRect.left + lineGap / 2 +
\r
2431 BOARD_WIDTH * (squareSize + lineGap);
\r
2432 gridVertexCounts[i*2] = gridVertexCounts[i*2 + 1] = 2;
\r
2434 for (i = 0; i < BOARD_WIDTH + 1; i++) {
\r
2435 gridEndpoints[i*2 + BOARD_HEIGHT*2 + 2].y = boardRect.top + lineGap / 2;
\r
2436 gridEndpoints[i*2 + BOARD_HEIGHT*2 + 2].x =
\r
2437 gridEndpoints[i*2 + 1 + BOARD_HEIGHT*2 + 2].x = boardRect.left +
\r
2438 lineGap / 2 + (i * (squareSize + lineGap));
\r
2439 gridEndpoints[i*2 + 1 + BOARD_HEIGHT*2 + 2].y =
\r
2440 boardRect.top + BOARD_HEIGHT * (squareSize + lineGap);
\r
2441 gridVertexCounts[i*2] = gridVertexCounts[i*2 + 1] = 2;
\r
2445 /* [HGM] Licensing requirement */
\r
2447 if(gameInfo.variant == VariantGothic) GothicPopUp( GOTHIC, VariantGothic); else
\r
2450 if(gameInfo.variant == VariantFalcon) GothicPopUp( FALCON, VariantFalcon); else
\r
2452 GothicPopUp( "", VariantNormal);
\r
2455 /* if (boardSize == oldBoardSize) return; [HGM] variant might have changed */
\r
2457 /* Load piece bitmaps for this board size */
\r
2458 for (i=0; i<=2; i++) {
\r
2459 for (piece = WhitePawn;
\r
2460 (int) piece < (int) BlackPawn;
\r
2461 piece = (ChessSquare) ((int) piece + 1)) {
\r
2462 if (pieceBitmap[i][piece] != NULL)
\r
2463 DeleteObject(pieceBitmap[i][piece]);
\r
2467 fontBitmapSquareSize = 0; /* [HGM] render: make sure pieces will be recreated, as we might need others now */
\r
2468 // Orthodox Chess pieces
\r
2469 pieceBitmap[0][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "s");
\r
2470 pieceBitmap[0][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "s");
\r
2471 pieceBitmap[0][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "s");
\r
2472 pieceBitmap[0][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "s");
\r
2473 pieceBitmap[0][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "s");
\r
2474 pieceBitmap[1][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "o");
\r
2475 pieceBitmap[1][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "o");
\r
2476 pieceBitmap[1][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "o");
\r
2477 pieceBitmap[1][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "o");
\r
2478 pieceBitmap[1][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "o");
\r
2479 pieceBitmap[2][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "w");
\r
2480 pieceBitmap[2][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "w");
\r
2481 pieceBitmap[2][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "w");
\r
2482 pieceBitmap[2][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "w");
\r
2483 pieceBitmap[2][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "w");
\r
2484 if( gameInfo.variant == VariantShogi && squareSize <= 72 && squareSize >= 33) {
\r
2485 // in Shogi, Hijack the unused Queen for Lance
\r
2486 pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "s");
\r
2487 pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "o");
\r
2488 pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "w");
\r
2490 pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "s");
\r
2491 pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "o");
\r
2492 pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "w");
\r
2495 if(squareSize <= 72 && squareSize >= 33) {
\r
2496 /* A & C are available in most sizes now */
\r
2497 if(squareSize != 49 && squareSize != 72 && squareSize != 33) { // Vortex-like
\r
2498 pieceBitmap[0][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "s");
\r
2499 pieceBitmap[1][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "o");
\r
2500 pieceBitmap[2][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "w");
\r
2501 pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "s");
\r
2502 pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "o");
\r
2503 pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "w");
\r
2504 pieceBitmap[0][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "s");
\r
2505 pieceBitmap[1][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "o");
\r
2506 pieceBitmap[2][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "w");
\r
2507 pieceBitmap[0][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "s");
\r
2508 pieceBitmap[1][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "o");
\r
2509 pieceBitmap[2][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "w");
\r
2510 } else { // Smirf-like
\r
2511 if(gameInfo.variant == VariantSChess) {
\r
2512 pieceBitmap[0][WhiteAngel] = DoLoadBitmap(hInst, "v", squareSize, "s");
\r
2513 pieceBitmap[1][WhiteAngel] = DoLoadBitmap(hInst, "v", squareSize, "o");
\r
2514 pieceBitmap[2][WhiteAngel] = DoLoadBitmap(hInst, "v", squareSize, "w");
\r
2516 pieceBitmap[0][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "s");
\r
2517 pieceBitmap[1][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "o");
\r
2518 pieceBitmap[2][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "w");
\r
2521 if(gameInfo.variant == VariantGothic) { // Vortex-like
\r
2522 pieceBitmap[0][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "s");
\r
2523 pieceBitmap[1][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "o");
\r
2524 pieceBitmap[2][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "w");
\r
2525 } else if(gameInfo.variant == VariantSChess && (squareSize == 49 || squareSize == 72)) {
\r
2526 pieceBitmap[0][WhiteMarshall] = DoLoadBitmap(hInst, "e", squareSize, "s");
\r
2527 pieceBitmap[1][WhiteMarshall] = DoLoadBitmap(hInst, "e", squareSize, "o");
\r
2528 pieceBitmap[2][WhiteMarshall] = DoLoadBitmap(hInst, "e", squareSize, "w");
\r
2529 } else { // WinBoard standard
\r
2530 pieceBitmap[0][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "s");
\r
2531 pieceBitmap[1][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "o");
\r
2532 pieceBitmap[2][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "w");
\r
2537 if(squareSize==72 || squareSize==49 || squareSize==33) { /* experiment with some home-made bitmaps */
\r
2538 pieceBitmap[0][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "s");
\r
2539 pieceBitmap[1][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "o");
\r
2540 pieceBitmap[2][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "w");
\r
2541 pieceBitmap[0][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "s");
\r
2542 pieceBitmap[1][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "o");
\r
2543 pieceBitmap[2][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "w");
\r
2544 pieceBitmap[0][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "s");
\r
2545 pieceBitmap[1][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "o");
\r
2546 pieceBitmap[2][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "w");
\r
2547 pieceBitmap[0][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "s");
\r
2548 pieceBitmap[1][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "o");
\r
2549 pieceBitmap[2][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "w");
\r
2550 pieceBitmap[0][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "s");
\r
2551 pieceBitmap[1][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "o");
\r
2552 pieceBitmap[2][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "w");
\r
2553 pieceBitmap[0][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "s");
\r
2554 pieceBitmap[1][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "o");
\r
2555 pieceBitmap[2][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "w");
\r
2556 pieceBitmap[0][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "s");
\r
2557 pieceBitmap[1][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "o");
\r
2558 pieceBitmap[2][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "w");
\r
2559 pieceBitmap[0][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "s");
\r
2560 pieceBitmap[1][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "o");
\r
2561 pieceBitmap[2][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "w");
\r
2562 pieceBitmap[0][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "s");
\r
2563 pieceBitmap[1][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "o");
\r
2564 pieceBitmap[2][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "w");
\r
2565 pieceBitmap[0][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "s");
\r
2566 pieceBitmap[1][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "o");
\r
2567 pieceBitmap[2][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "w");
\r
2569 if(gameInfo.variant == VariantShogi) { /* promoted Gold represemtations */
\r
2570 pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "wp", squareSize, "s");
\r
2571 pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "wp", squareSize, "o");
\r
2572 pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "w", squareSize, "w");
\r
2573 pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "wn", squareSize, "s");
\r
2574 pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "wn", squareSize, "o");
\r
2575 pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "w", squareSize, "w");
\r
2576 pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "ws", squareSize, "s");
\r
2577 pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "ws", squareSize, "o");
\r
2578 pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "w", squareSize, "w");
\r
2579 pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "wl", squareSize, "s");
\r
2580 pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "wl", squareSize, "o");
\r
2581 pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "w", squareSize, "w");
\r
2583 pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "s");
\r
2584 pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "o");
\r
2585 pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "w");
\r
2586 pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "s");
\r
2587 pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "o");
\r
2588 pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "w");
\r
2589 pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "s");
\r
2590 pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "o");
\r
2591 pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "w");
\r
2592 pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "s");
\r
2593 pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "o");
\r
2594 pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "w");
\r
2597 } else { /* other size, no special bitmaps available. Use smaller symbols */
\r
2598 if((int)boardSize < 2) minorSize = sizeInfo[0].squareSize;
\r
2599 else minorSize = sizeInfo[(int)boardSize - 2].squareSize;
\r
2600 pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "s");
\r
2601 pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "o");
\r
2602 pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "w");
\r
2603 pieceBitmap[0][WhiteCardinal] = DoLoadBitmap(hInst, "b", minorSize, "s");
\r
2604 pieceBitmap[1][WhiteCardinal] = DoLoadBitmap(hInst, "b", minorSize, "o");
\r
2605 pieceBitmap[2][WhiteCardinal] = DoLoadBitmap(hInst, "b", minorSize, "w");
\r
2606 pieceBitmap[0][WhiteDragon] = DoLoadBitmap(hInst, "r", minorSize, "s");
\r
2607 pieceBitmap[1][WhiteDragon] = DoLoadBitmap(hInst, "r", minorSize, "o");
\r
2608 pieceBitmap[2][WhiteDragon] = DoLoadBitmap(hInst, "r", minorSize, "w");
\r
2609 pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "s");
\r
2610 pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "o");
\r
2611 pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "w");
\r
2615 if(gameInfo.variant == VariantShogi && squareSize == 58)
\r
2616 /* special Shogi support in this size */
\r
2617 { for (i=0; i<=2; i++) { /* replace all bitmaps */
\r
2618 for (piece = WhitePawn;
\r
2619 (int) piece < (int) BlackPawn;
\r
2620 piece = (ChessSquare) ((int) piece + 1)) {
\r
2621 if (pieceBitmap[i][piece] != NULL)
\r
2622 DeleteObject(pieceBitmap[i][piece]);
\r
2625 pieceBitmap[0][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "o");
\r
2626 pieceBitmap[0][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "o");
\r
2627 pieceBitmap[0][WhiteBishop] = DoLoadBitmap(hInst, "sb", squareSize, "o");
\r
2628 pieceBitmap[0][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "o");
\r
2629 pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "o");
\r
2630 pieceBitmap[0][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "o");
\r
2631 pieceBitmap[0][WhiteFerz] = DoLoadBitmap(hInst, "sf", squareSize, "o");
\r
2632 pieceBitmap[0][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "o");
\r
2633 pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "su", squareSize, "o");
\r
2634 pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "sh", squareSize, "o");
\r
2635 pieceBitmap[0][WhiteCardinal] = DoLoadBitmap(hInst, "sa", squareSize, "o");
\r
2636 pieceBitmap[0][WhiteDragon] = DoLoadBitmap(hInst, "sc", squareSize, "o");
\r
2637 pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "sg", squareSize, "o");
\r
2638 pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "ss", squareSize, "o");
\r
2639 pieceBitmap[1][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "o");
\r
2640 pieceBitmap[1][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "o");
\r
2641 pieceBitmap[1][WhiteBishop] = DoLoadBitmap(hInst, "sb", squareSize, "o");
\r
2642 pieceBitmap[1][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "o");
\r
2643 pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "o");
\r
2644 pieceBitmap[1][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "o");
\r
2645 pieceBitmap[1][WhiteFerz] = DoLoadBitmap(hInst, "sf", squareSize, "o");
\r
2646 pieceBitmap[1][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "o");
\r
2647 pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "su", squareSize, "o");
\r
2648 pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "sh", squareSize, "o");
\r
2649 pieceBitmap[1][WhiteCardinal] = DoLoadBitmap(hInst, "sa", squareSize, "o");
\r
2650 pieceBitmap[1][WhiteDragon] = DoLoadBitmap(hInst, "sc", squareSize, "o");
\r
2651 pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "sg", squareSize, "o");
\r
2652 pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "ss", squareSize, "o");
\r
2653 pieceBitmap[2][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "w");
\r
2654 pieceBitmap[2][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "w");
\r
2655 pieceBitmap[2][WhiteBishop] = DoLoadBitmap(hInst, "sr", squareSize, "w");
\r
2656 pieceBitmap[2][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "w");
\r
2657 pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "w");
\r
2658 pieceBitmap[2][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "w");
\r
2659 pieceBitmap[2][WhiteFerz] = DoLoadBitmap(hInst, "sw", squareSize, "w");
\r
2660 pieceBitmap[2][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "w");
\r
2661 pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "sp", squareSize, "w");
\r
2662 pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "sn", squareSize, "w");
\r
2663 pieceBitmap[2][WhiteCardinal] = DoLoadBitmap(hInst, "sr", squareSize, "w");
\r
2664 pieceBitmap[2][WhiteDragon] = DoLoadBitmap(hInst, "sr", squareSize, "w");
\r
2665 pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "sl", squareSize, "w");
\r
2666 pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "sw", squareSize, "w");
\r
2672 PieceBitmap(ChessSquare p, int kind)
\r
2674 if ((int) p >= (int) BlackPawn)
\r
2675 p = (ChessSquare) ((int) p - (int) BlackPawn + (int) WhitePawn);
\r
2677 return pieceBitmap[kind][(int) p];
\r
2680 /***************************************************************/
\r
2682 #define MIN(a,b) (((a) < (b)) ? (a) : (b))
\r
2683 #define MAX(a,b) (((a) > (b)) ? (a) : (b))
\r
2685 #define MIN3(a,b,c) (((a) < (b) && (a) < (c)) ? (a) : (((b) < (a) && (b) < (c)) ? (b) : (c)))
\r
2686 #define MAX3(a,b,c) (((a) > (b) && (a) > (c)) ? (a) : (((b) > (a) && (b) > (c)) ? (b) : (c)))
\r
2690 SquareToPos(int row, int column, int * x, int * y)
\r
2693 *x = boardRect.left + lineGap + ((BOARD_WIDTH-1)-column) * (squareSize + lineGap);
\r
2694 *y = boardRect.top + lineGap + row * (squareSize + lineGap);
\r
2696 *x = boardRect.left + lineGap + column * (squareSize + lineGap);
\r
2697 *y = boardRect.top + lineGap + ((BOARD_HEIGHT-1)-row) * (squareSize + lineGap);
\r
2702 DrawCoordsOnDC(HDC hdc)
\r
2704 static char files[] = "0123456789012345678901221098765432109876543210";
\r
2705 static char ranks[] = "wvutsrqponmlkjihgfedcbaabcdefghijklmnopqrstuvw";
\r
2706 char str[2] = { NULLCHAR, NULLCHAR };
\r
2707 int oldMode, oldAlign, x, y, start, i;
\r
2711 if (!appData.showCoords)
\r
2714 start = flipView ? 1-(ONE!='1') : 45+(ONE!='1')-BOARD_HEIGHT;
\r
2716 oldBrush = SelectObject(hdc, GetStockObject(BLACK_BRUSH));
\r
2717 oldMode = SetBkMode(hdc, (appData.monoMode ? OPAQUE : TRANSPARENT));
\r
2718 oldAlign = GetTextAlign(hdc);
\r
2719 oldFont = SelectObject(hdc, font[boardSize][COORD_FONT]->hf);
\r
2721 y = boardRect.top + lineGap;
\r
2722 x = boardRect.left + lineGap + gameInfo.holdingsWidth*(squareSize + lineGap);
\r
2724 SetTextAlign(hdc, TA_LEFT|TA_TOP);
\r
2725 for (i = 0; i < BOARD_HEIGHT; i++) {
\r
2726 str[0] = files[start + i];
\r
2727 ExtTextOut(hdc, x + 2, y + 1, 0, NULL, str, 1, NULL);
\r
2728 y += squareSize + lineGap;
\r
2731 start = flipView ? 23-(BOARD_RGHT-BOARD_LEFT) : 23;
\r
2733 SetTextAlign(hdc, TA_RIGHT|TA_BOTTOM);
\r
2734 for (i = 0; i < BOARD_RGHT - BOARD_LEFT; i++) {
\r
2735 str[0] = ranks[start + i];
\r
2736 ExtTextOut(hdc, x + squareSize - 2, y - 1, 0, NULL, str, 1, NULL);
\r
2737 x += squareSize + lineGap;
\r
2740 SelectObject(hdc, oldBrush);
\r
2741 SetBkMode(hdc, oldMode);
\r
2742 SetTextAlign(hdc, oldAlign);
\r
2743 SelectObject(hdc, oldFont);
\r
2747 DrawGridOnDC(HDC hdc)
\r
2751 if (lineGap != 0) {
\r
2752 oldPen = SelectObject(hdc, gridPen);
\r
2753 PolyPolyline(hdc, gridEndpoints, gridVertexCounts, BOARD_WIDTH+BOARD_HEIGHT + 2);
\r
2754 SelectObject(hdc, oldPen);
\r
2758 #define HIGHLIGHT_PEN 0
\r
2759 #define PREMOVE_PEN 1
\r
2762 DrawHighlightOnDC(HDC hdc, BOOLEAN on, int x, int y, int pen)
\r
2765 HPEN oldPen, hPen;
\r
2766 if (lineGap == 0) return;
\r
2768 x1 = boardRect.left +
\r
2769 lineGap/2 + ((BOARD_WIDTH-1)-x) * (squareSize + lineGap);
\r
2770 y1 = boardRect.top +
\r
2771 lineGap/2 + y * (squareSize + lineGap);
\r
2773 x1 = boardRect.left +
\r
2774 lineGap/2 + x * (squareSize + lineGap);
\r
2775 y1 = boardRect.top +
\r
2776 lineGap/2 + ((BOARD_HEIGHT-1)-y) * (squareSize + lineGap);
\r
2778 hPen = pen ? premovePen : highlightPen;
\r
2779 oldPen = SelectObject(hdc, on ? hPen : gridPen);
\r
2780 MoveToEx(hdc, x1, y1, NULL);
\r
2781 LineTo(hdc, x1 + squareSize + lineGap, y1);
\r
2782 LineTo(hdc, x1 + squareSize + lineGap, y1 + squareSize + lineGap);
\r
2783 LineTo(hdc, x1, y1 + squareSize + lineGap);
\r
2784 LineTo(hdc, x1, y1);
\r
2785 SelectObject(hdc, oldPen);
\r
2789 DrawHighlightsOnDC(HDC hdc, HighlightInfo *h, int pen)
\r
2792 for (i=0; i<2; i++) {
\r
2793 if (h->sq[i].x >= 0 && h->sq[i].y >= 0)
\r
2794 DrawHighlightOnDC(hdc, TRUE,
\r
2795 h->sq[i].x, h->sq[i].y,
\r
2800 /* Note: sqcolor is used only in monoMode */
\r
2801 /* Note that this code is largely duplicated in woptions.c,
\r
2802 function DrawSampleSquare, so that needs to be updated too */
\r
2804 DrawPieceOnDC(HDC hdc, ChessSquare piece, int color, int sqcolor, int x, int y, HDC tmphdc)
\r
2806 HBITMAP oldBitmap;
\r
2810 if (appData.blindfold) return;
\r
2812 /* [AS] Use font-based pieces if needed */
\r
2813 if( fontBitmapSquareSize >= 0 && (squareSize > 32 || gameInfo.variant >= VariantShogi)) {
\r
2814 /* Create piece bitmaps, or do nothing if piece set is up to date */
\r
2815 CreatePiecesFromFont();
\r
2817 if( fontBitmapSquareSize == squareSize ) {
\r
2818 int index = TranslatePieceToFontPiece(piece);
\r
2820 SelectObject( tmphdc, hPieceMask[ index ] );
\r
2822 if(appData.upsideDown ? color==flipView : (flipView && gameInfo.variant == VariantShogi))
\r
2823 StretchBlt(hdc, x+squareSize, y+squareSize, -squareSize, -squareSize, tmphdc, 0, 0, squareSize, squareSize, SRCAND);
\r
2827 squareSize, squareSize,
\r
2832 SelectObject( tmphdc, hPieceFace[ index ] );
\r
2834 if(appData.upsideDown ? color==flipView : (flipView && gameInfo.variant == VariantShogi))
\r
2835 StretchBlt(hdc, x+squareSize, y+squareSize, -squareSize, -squareSize, tmphdc, 0, 0, squareSize, squareSize, SRCPAINT);
\r
2839 squareSize, squareSize,
\r
2848 if (appData.monoMode) {
\r
2849 SelectObject(tmphdc, PieceBitmap(piece,
\r
2850 color == sqcolor ? OUTLINE_PIECE : SOLID_PIECE));
\r
2851 BitBlt(hdc, x, y, squareSize, squareSize, tmphdc, 0, 0,
\r
2852 sqcolor ? SRCCOPY : NOTSRCCOPY);
\r
2854 tmpSize = squareSize;
\r
2856 ((piece >= (int)WhiteNightrider && piece <= WhiteGrasshopper) ||
\r
2857 (piece >= (int)BlackNightrider && piece <= BlackGrasshopper)) ) {
\r
2858 /* [HGM] no bitmap available for promoted pieces in Crazyhouse */
\r
2859 /* Bitmaps of smaller size are substituted, but we have to align them */
\r
2860 x += (squareSize - minorSize)>>1;
\r
2861 y += squareSize - minorSize - 2;
\r
2862 tmpSize = minorSize;
\r
2864 if (color || appData.allWhite ) {
\r
2865 oldBitmap = SelectObject(tmphdc, PieceBitmap(piece, WHITE_PIECE));
\r
2867 oldBrush = SelectObject(hdc, whitePieceBrush);
\r
2868 else oldBrush = SelectObject(hdc, blackPieceBrush);
\r
2869 if(appData.upsideDown && color==flipView)
\r
2870 StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, 0x00B8074A);
\r
2872 BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, 0x00B8074A);
\r
2873 /* Use black for outline of white pieces */
\r
2874 SelectObject(tmphdc, PieceBitmap(piece, OUTLINE_PIECE));
\r
2875 if(appData.upsideDown && color==flipView)
\r
2876 StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, SRCAND);
\r
2878 BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, SRCAND);
\r
2880 /* Use square color for details of black pieces */
\r
2881 oldBitmap = SelectObject(tmphdc, PieceBitmap(piece, SOLID_PIECE));
\r
2882 oldBrush = SelectObject(hdc, blackPieceBrush);
\r
2883 if(appData.upsideDown && !flipView)
\r
2884 StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, 0x00B8074A);
\r
2886 BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, 0x00B8074A);
\r
2888 SelectObject(hdc, oldBrush);
\r
2889 SelectObject(tmphdc, oldBitmap);
\r
2893 /* [AS] Compute a drawing mode for a square, based on specified settings (see DrawTile) */
\r
2894 int GetBackTextureMode( int algo )
\r
2896 int result = BACK_TEXTURE_MODE_DISABLED;
\r
2900 case BACK_TEXTURE_MODE_PLAIN:
\r
2901 result = 1; /* Always use identity map */
\r
2903 case BACK_TEXTURE_MODE_FULL_RANDOM:
\r
2904 result = 1 + (myrandom() % 3); /* Pick a transformation at random */
\r
2912 [AS] Compute and save texture drawing info, otherwise we may not be able
\r
2913 to handle redraws cleanly (as random numbers would always be different).
\r
2915 VOID RebuildTextureSquareInfo()
\r
2925 ZeroMemory( &backTextureSquareInfo, sizeof(backTextureSquareInfo) );
\r
2927 if( liteBackTexture != NULL ) {
\r
2928 if( GetObject( liteBackTexture, sizeof(bi), &bi ) > 0 ) {
\r
2929 lite_w = bi.bmWidth;
\r
2930 lite_h = bi.bmHeight;
\r
2934 if( darkBackTexture != NULL ) {
\r
2935 if( GetObject( darkBackTexture, sizeof(bi), &bi ) > 0 ) {
\r
2936 dark_w = bi.bmWidth;
\r
2937 dark_h = bi.bmHeight;
\r
2941 for( row=0; row<BOARD_HEIGHT; row++ ) {
\r
2942 for( col=0; col<BOARD_WIDTH; col++ ) {
\r
2943 if( (col + row) & 1 ) {
\r
2945 if( lite_w >= squareSize && lite_h >= squareSize ) {
\r
2946 if( lite_w >= squareSize*BOARD_WIDTH )
\r
2947 backTextureSquareInfo[row][col].x = (2*col+1)*lite_w/(2*BOARD_WIDTH) - squareSize/2; /* [HGM] cut out of center of virtual square */
\r
2949 backTextureSquareInfo[row][col].x = col * (lite_w - squareSize) / (BOARD_WIDTH-1); /* [HGM] divide by size-1 in stead of size! */
\r
2950 if( lite_h >= squareSize*BOARD_HEIGHT )
\r
2951 backTextureSquareInfo[row][col].y = (2*(BOARD_HEIGHT-row)-1)*lite_h/(2*BOARD_HEIGHT) - squareSize/2;
\r
2953 backTextureSquareInfo[row][col].y = (BOARD_HEIGHT-1-row) * (lite_h - squareSize) / (BOARD_HEIGHT-1);
\r
2954 backTextureSquareInfo[row][col].mode = GetBackTextureMode(liteBackTextureMode);
\r
2959 if( dark_w >= squareSize && dark_h >= squareSize ) {
\r
2960 if( dark_w >= squareSize*BOARD_WIDTH )
\r
2961 backTextureSquareInfo[row][col].x = (2*col+1) * dark_w / (2*BOARD_WIDTH) - squareSize/2;
\r
2963 backTextureSquareInfo[row][col].x = col * (dark_w - squareSize) / (BOARD_WIDTH-1);
\r
2964 if( dark_h >= squareSize*BOARD_HEIGHT )
\r
2965 backTextureSquareInfo[row][col].y = (2*(BOARD_HEIGHT-row)-1) * dark_h / (2*BOARD_HEIGHT) - squareSize/2;
\r
2967 backTextureSquareInfo[row][col].y = (BOARD_HEIGHT-1-row) * (dark_h - squareSize) / (BOARD_HEIGHT-1);
\r
2968 backTextureSquareInfo[row][col].mode = GetBackTextureMode(darkBackTextureMode);
\r
2975 /* [AS] Arrow highlighting support */
\r
2977 static double A_WIDTH = 5; /* Width of arrow body */
\r
2979 #define A_HEIGHT_FACTOR 6 /* Length of arrow "point", relative to body width */
\r
2980 #define A_WIDTH_FACTOR 3 /* Width of arrow "point", relative to body width */
\r
2982 static double Sqr( double x )
\r
2987 static int Round( double x )
\r
2989 return (int) (x + 0.5);
\r
2992 /* Draw an arrow between two points using current settings */
\r
2993 VOID DrawArrowBetweenPoints( HDC hdc, int s_x, int s_y, int d_x, int d_y )
\r
2996 double dx, dy, j, k, x, y;
\r
2998 if( d_x == s_x ) {
\r
2999 int h = (d_y > s_y) ? +A_WIDTH*A_HEIGHT_FACTOR : -A_WIDTH*A_HEIGHT_FACTOR;
\r
3001 arrow[0].x = s_x + A_WIDTH + 0.5;
\r
3004 arrow[1].x = s_x + A_WIDTH + 0.5;
\r
3005 arrow[1].y = d_y - h;
\r
3007 arrow[2].x = arrow[1].x + A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;
\r
3008 arrow[2].y = d_y - h;
\r
3013 arrow[5].x = arrow[1].x - 2*A_WIDTH + 0.5;
\r
3014 arrow[5].y = d_y - h;
\r
3016 arrow[4].x = arrow[5].x - A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;
\r
3017 arrow[4].y = d_y - h;
\r
3019 arrow[6].x = arrow[1].x - 2*A_WIDTH + 0.5;
\r
3022 else if( d_y == s_y ) {
\r
3023 int w = (d_x > s_x) ? +A_WIDTH*A_HEIGHT_FACTOR : -A_WIDTH*A_HEIGHT_FACTOR;
\r
3026 arrow[0].y = s_y + A_WIDTH + 0.5;
\r
3028 arrow[1].x = d_x - w;
\r
3029 arrow[1].y = s_y + A_WIDTH + 0.5;
\r
3031 arrow[2].x = d_x - w;
\r
3032 arrow[2].y = arrow[1].y + A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;
\r
3037 arrow[5].x = d_x - w;
\r
3038 arrow[5].y = arrow[1].y - 2*A_WIDTH + 0.5;
\r
3040 arrow[4].x = d_x - w;
\r
3041 arrow[4].y = arrow[5].y - A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;
\r
3044 arrow[6].y = arrow[1].y - 2*A_WIDTH + 0.5;
\r
3047 /* [AS] Needed a lot of paper for this! :-) */
\r
3048 dy = (double) (d_y - s_y) / (double) (d_x - s_x);
\r
3049 dx = (double) (s_x - d_x) / (double) (s_y - d_y);
\r
3051 j = sqrt( Sqr(A_WIDTH) / (1.0 + Sqr(dx)) );
\r
3053 k = sqrt( Sqr(A_WIDTH*A_HEIGHT_FACTOR) / (1.0 + Sqr(dy)) );
\r
3058 arrow[0].x = Round(x - j);
\r
3059 arrow[0].y = Round(y + j*dx);
\r
3061 arrow[1].x = Round(arrow[0].x + 2*j); // [HGM] prevent width to be affected by rounding twice
\r
3062 arrow[1].y = Round(arrow[0].y - 2*j*dx);
\r
3065 x = (double) d_x - k;
\r
3066 y = (double) d_y - k*dy;
\r
3069 x = (double) d_x + k;
\r
3070 y = (double) d_y + k*dy;
\r
3073 x = Round(x); y = Round(y); // [HGM] make sure width of shaft is rounded the same way on both ends
\r
3075 arrow[6].x = Round(x - j);
\r
3076 arrow[6].y = Round(y + j*dx);
\r
3078 arrow[2].x = Round(arrow[6].x + 2*j);
\r
3079 arrow[2].y = Round(arrow[6].y - 2*j*dx);
\r
3081 arrow[3].x = Round(arrow[2].x + j*(A_WIDTH_FACTOR-1));
\r
3082 arrow[3].y = Round(arrow[2].y - j*(A_WIDTH_FACTOR-1)*dx);
\r
3087 arrow[5].x = Round(arrow[6].x - j*(A_WIDTH_FACTOR-1));
\r
3088 arrow[5].y = Round(arrow[6].y + j*(A_WIDTH_FACTOR-1)*dx);
\r
3091 Polygon( hdc, arrow, 7 );
\r
3094 /* [AS] Draw an arrow between two squares */
\r
3095 VOID DrawArrowBetweenSquares( HDC hdc, int s_col, int s_row, int d_col, int d_row )
\r
3097 int s_x, s_y, d_x, d_y;
\r
3104 if( s_col == d_col && s_row == d_row ) {
\r
3108 /* Get source and destination points */
\r
3109 SquareToPos( s_row, s_col, &s_x, &s_y);
\r
3110 SquareToPos( d_row, d_col, &d_x, &d_y);
\r
3113 d_y += squareSize / 2 - squareSize / 4; // [HGM] round towards same centers on all sides!
\r
3115 else if( d_y < s_y ) {
\r
3116 d_y += squareSize / 2 + squareSize / 4;
\r
3119 d_y += squareSize / 2;
\r
3123 d_x += squareSize / 2 - squareSize / 4;
\r
3125 else if( d_x < s_x ) {
\r
3126 d_x += squareSize / 2 + squareSize / 4;
\r
3129 d_x += squareSize / 2;
\r
3132 s_x += squareSize / 2;
\r
3133 s_y += squareSize / 2;
\r
3135 /* Adjust width */
\r
3136 A_WIDTH = squareSize / 14.; //[HGM] make float
\r
3139 stLB.lbStyle = BS_SOLID;
\r
3140 stLB.lbColor = appData.highlightArrowColor;
\r
3143 hpen = CreatePen( PS_SOLID, 2, RGB(0x00,0x00,0x00) );
\r
3144 holdpen = SelectObject( hdc, hpen );
\r
3145 hbrush = CreateBrushIndirect( &stLB );
\r
3146 holdbrush = SelectObject( hdc, hbrush );
\r
3148 DrawArrowBetweenPoints( hdc, s_x, s_y, d_x, d_y );
\r
3150 SelectObject( hdc, holdpen );
\r
3151 SelectObject( hdc, holdbrush );
\r
3152 DeleteObject( hpen );
\r
3153 DeleteObject( hbrush );
\r
3156 BOOL HasHighlightInfo()
\r
3158 BOOL result = FALSE;
\r
3160 if( highlightInfo.sq[0].x >= 0 && highlightInfo.sq[0].y >= 0 &&
\r
3161 highlightInfo.sq[1].x >= 0 && highlightInfo.sq[1].y >= 0 )
\r
3169 BOOL IsDrawArrowEnabled()
\r
3171 BOOL result = FALSE;
\r
3173 if( appData.highlightMoveWithArrow && squareSize >= 32 ) {
\r
3180 VOID DrawArrowHighlight( HDC hdc )
\r
3182 if( IsDrawArrowEnabled() && HasHighlightInfo() ) {
\r
3183 DrawArrowBetweenSquares( hdc,
\r
3184 highlightInfo.sq[0].x, highlightInfo.sq[0].y,
\r
3185 highlightInfo.sq[1].x, highlightInfo.sq[1].y );
\r
3189 HRGN GetArrowHighlightClipRegion( HDC hdc )
\r
3191 HRGN result = NULL;
\r
3193 if( HasHighlightInfo() ) {
\r
3194 int x1, y1, x2, y2;
\r
3195 int sx, sy, dx, dy;
\r
3197 SquareToPos(highlightInfo.sq[0].y, highlightInfo.sq[0].x, &x1, &y1 );
\r
3198 SquareToPos(highlightInfo.sq[1].y, highlightInfo.sq[1].x, &x2, &y2 );
\r
3200 sx = MIN( x1, x2 );
\r
3201 sy = MIN( y1, y2 );
\r
3202 dx = MAX( x1, x2 ) + squareSize;
\r
3203 dy = MAX( y1, y2 ) + squareSize;
\r
3205 result = CreateRectRgn( sx, sy, dx, dy );
\r
3212 Warning: this function modifies the behavior of several other functions.
\r
3214 Basically, Winboard is optimized to avoid drawing the whole board if not strictly
\r
3215 needed. Unfortunately, the decision whether or not to perform a full or partial
\r
3216 repaint is scattered all over the place, which is not good for features such as
\r
3217 "arrow highlighting" that require a full repaint of the board.
\r
3219 So, I've tried to patch the code where I thought it made sense (e.g. after or during
\r
3220 user interaction, when speed is not so important) but especially to avoid errors
\r
3221 in the displayed graphics.
\r
3223 In such patched places, I always try refer to this function so there is a single
\r
3224 place to maintain knowledge.
\r
3226 To restore the original behavior, just return FALSE unconditionally.
\r
3228 BOOL IsFullRepaintPreferrable()
\r
3230 BOOL result = FALSE;
\r
3232 if( (appData.highlightLastMove || appData.highlightDragging) && IsDrawArrowEnabled() ) {
\r
3233 /* Arrow may appear on the board */
\r
3241 This function is called by DrawPosition to know whether a full repaint must
\r
3244 Only DrawPosition may directly call this function, which makes use of
\r
3245 some state information. Other function should call DrawPosition specifying
\r
3246 the repaint flag, and can use IsFullRepaintPreferrable if needed.
\r
3248 BOOL DrawPositionNeedsFullRepaint()
\r
3250 BOOL result = FALSE;
\r
3253 Probably a slightly better policy would be to trigger a full repaint
\r
3254 when animInfo.piece changes state (i.e. empty -> non-empty and viceversa),
\r
3255 but animation is fast enough that it's difficult to notice.
\r
3257 if( animInfo.piece == EmptySquare ) {
\r
3258 if( (appData.highlightLastMove || appData.highlightDragging) && IsDrawArrowEnabled() /*&& HasHighlightInfo()*/ ) {
\r
3267 DrawBoardOnDC(HDC hdc, Board board, HDC tmphdc)
\r
3269 int row, column, x, y, square_color, piece_color;
\r
3270 ChessSquare piece;
\r
3272 HDC texture_hdc = NULL;
\r
3274 /* [AS] Initialize background textures if needed */
\r
3275 if( liteBackTexture != NULL || darkBackTexture != NULL ) {
\r
3276 static int backTextureBoardSize; /* [HGM] boardsize: also new texture if board format changed */
\r
3277 if( backTextureSquareSize != squareSize
\r
3278 || backTextureBoardSize != BOARD_WIDTH+BOARD_FILES*BOARD_HEIGHT) {
\r
3279 backTextureBoardSize = BOARD_WIDTH+BOARD_FILES*BOARD_HEIGHT;
\r
3280 backTextureSquareSize = squareSize;
\r
3281 RebuildTextureSquareInfo();
\r
3284 texture_hdc = CreateCompatibleDC( hdc );
\r
3287 for (row = 0; row < BOARD_HEIGHT; row++) {
\r
3288 for (column = 0; column < BOARD_WIDTH; column++) {
\r
3290 SquareToPos(row, column, &x, &y);
\r
3292 piece = board[row][column];
\r
3294 square_color = ((column + row) % 2) == 1;
\r
3295 if( gameInfo.variant == VariantXiangqi ) {
\r
3296 square_color = !InPalace(row, column);
\r
3297 if(BOARD_HEIGHT&1) { if(row==BOARD_HEIGHT/2) square_color ^= 1; }
\r
3298 else if(row < BOARD_HEIGHT/2) square_color ^= 1;
\r
3300 piece_color = (int) piece < (int) BlackPawn;
\r
3303 /* [HGM] holdings file: light square or black */
\r
3304 if(column == BOARD_LEFT-2) {
\r
3305 if( row > BOARD_HEIGHT - gameInfo.holdingsSize - 1 )
\r
3308 DisplayHoldingsCount(hdc, x, y, 0, 0); /* black out */
\r
3312 if(column == BOARD_RGHT + 1 ) {
\r
3313 if( row < gameInfo.holdingsSize )
\r
3316 DisplayHoldingsCount(hdc, x, y, 0, 0);
\r
3320 if(column == BOARD_LEFT-1 ) /* left align */
\r
3321 DisplayHoldingsCount(hdc, x, y, flipView, (int) board[row][column]);
\r
3322 else if( column == BOARD_RGHT) /* right align */
\r
3323 DisplayHoldingsCount(hdc, x, y, !flipView, (int) board[row][column]);
\r
3325 if (appData.monoMode) {
\r
3326 if (piece == EmptySquare) {
\r
3327 BitBlt(hdc, x, y, squareSize, squareSize, 0, 0, 0,
\r
3328 square_color ? WHITENESS : BLACKNESS);
\r
3330 DrawPieceOnDC(hdc, piece, piece_color, square_color, x, y, tmphdc);
\r
3333 else if( appData.useBitmaps && backTextureSquareInfo[row][column].mode > 0 ) {
\r
3334 /* [AS] Draw the square using a texture bitmap */
\r
3335 HBITMAP hbm = SelectObject( texture_hdc, square_color ? liteBackTexture : darkBackTexture );
\r
3336 int r = row, c = column; // [HGM] do not flip board in flipView
\r
3337 if(flipView) { r = BOARD_HEIGHT-1 - r; c = BOARD_WIDTH-1 - c; }
\r
3340 squareSize, squareSize,
\r
3343 backTextureSquareInfo[r][c].mode,
\r
3344 backTextureSquareInfo[r][c].x,
\r
3345 backTextureSquareInfo[r][c].y );
\r
3347 SelectObject( texture_hdc, hbm );
\r
3349 if (piece != EmptySquare) {
\r
3350 DrawPieceOnDC(hdc, piece, piece_color, -1, x, y, tmphdc);
\r
3354 HBRUSH brush = square_color ? lightSquareBrush : darkSquareBrush;
\r
3356 oldBrush = SelectObject(hdc, brush );
\r
3357 BitBlt(hdc, x, y, squareSize, squareSize, 0, 0, 0, PATCOPY);
\r
3358 SelectObject(hdc, oldBrush);
\r
3359 if (piece != EmptySquare)
\r
3360 DrawPieceOnDC(hdc, piece, piece_color, -1, x, y, tmphdc);
\r
3365 if( texture_hdc != NULL ) {
\r
3366 DeleteDC( texture_hdc );
\r
3370 int saveDiagFlag = 0; FILE *diagFile; // [HGM] diag
\r
3371 void fputDW(FILE *f, int x)
\r
3373 fputc(x & 255, f);
\r
3374 fputc(x>>8 & 255, f);
\r
3375 fputc(x>>16 & 255, f);
\r
3376 fputc(x>>24 & 255, f);
\r
3379 #define MAX_CLIPS 200 /* more than enough */
\r
3382 DrawLogoOnDC(HDC hdc, RECT logoRect, HBITMAP logo)
\r
3384 // HBITMAP bufferBitmap;
\r
3389 int w = 100, h = 50;
\r
3391 if(logo == NULL) {
\r
3392 if(!logoHeight) return;
\r
3393 FillRect( hdc, &logoRect, whitePieceBrush );
\r
3395 // GetClientRect(hwndMain, &Rect);
\r
3396 // bufferBitmap = CreateCompatibleBitmap(hdc, Rect.right-Rect.left+1,
\r
3397 // Rect.bottom-Rect.top+1);
\r
3398 tmphdc = CreateCompatibleDC(hdc);
\r
3399 hbm = SelectObject(tmphdc, logo);
\r
3400 if( GetObject( logo, sizeof(bi), &bi ) > 0 ) {
\r
3404 StretchBlt(hdc, logoRect.left, logoRect.top, logoRect.right - logoRect.left,
\r
3405 logoRect.bottom - logoRect.top, tmphdc, 0, 0, w, h, SRCCOPY);
\r
3406 SelectObject(tmphdc, hbm);
\r
3414 HDC hdc = GetDC(hwndMain);
\r
3415 HBITMAP whiteLogo = (HBITMAP) first.programLogo, blackLogo = (HBITMAP) second.programLogo;
\r
3416 if(appData.autoLogo) {
\r
3418 switch(gameMode) { // pick logos based on game mode
\r
3419 case IcsObserving:
\r
3420 whiteLogo = second.programLogo; // ICS logo
\r
3421 blackLogo = second.programLogo;
\r
3424 case IcsPlayingWhite:
\r
3425 if(!appData.zippyPlay) whiteLogo = userLogo;
\r
3426 blackLogo = second.programLogo; // ICS logo
\r
3428 case IcsPlayingBlack:
\r
3429 whiteLogo = second.programLogo; // ICS logo
\r
3430 blackLogo = appData.zippyPlay ? first.programLogo : userLogo;
\r
3432 case TwoMachinesPlay:
\r
3433 if(first.twoMachinesColor[0] == 'b') {
\r
3434 whiteLogo = second.programLogo;
\r
3435 blackLogo = first.programLogo;
\r
3438 case MachinePlaysWhite:
\r
3439 blackLogo = userLogo;
\r
3441 case MachinePlaysBlack:
\r
3442 whiteLogo = userLogo;
\r
3443 blackLogo = first.programLogo;
\r
3446 DrawLogoOnDC(hdc, leftLogoRect, flipClock ? blackLogo : whiteLogo);
\r
3447 DrawLogoOnDC(hdc, rightLogoRect, flipClock ? whiteLogo : blackLogo);
\r
3448 ReleaseDC(hwndMain, hdc);
\r
3453 UpdateLogos(int display)
\r
3454 { // called after loading new engine(s), in tourney or from menu
\r
3455 LoadLogo(&first, 0, FALSE);
\r
3456 LoadLogo(&second, 1, appData.icsActive);
\r
3457 InitDrawingSizes(-2, 0); // adapt layout of board window to presence/absence of logos
\r
3458 if(display) DisplayLogos();
\r
3461 static HDC hdcSeek;
\r
3463 // [HGM] seekgraph
\r
3464 void DrawSeekAxis( int x, int y, int xTo, int yTo )
\r
3467 HPEN hp = SelectObject( hdcSeek, gridPen );
\r
3468 MoveToEx( hdcSeek, boardRect.left+x, boardRect.top+y, &stPt );
\r
3469 LineTo( hdcSeek, boardRect.left+xTo, boardRect.top+yTo );
\r
3470 SelectObject( hdcSeek, hp );
\r
3473 // front-end wrapper for drawing functions to do rectangles
\r
3474 void DrawSeekBackground( int left, int top, int right, int bottom )
\r
3479 if (hdcSeek == NULL) {
\r
3480 hdcSeek = GetDC(hwndMain);
\r
3481 if (!appData.monoMode) {
\r
3482 SelectPalette(hdcSeek, hPal, FALSE);
\r
3483 RealizePalette(hdcSeek);
\r
3486 hp = SelectObject( hdcSeek, gridPen );
\r
3487 rc.top = boardRect.top+top; rc.left = boardRect.left+left;
\r
3488 rc.bottom = boardRect.top+bottom; rc.right = boardRect.left+right;
\r
3489 FillRect( hdcSeek, &rc, lightSquareBrush );
\r
3490 SelectObject( hdcSeek, hp );
\r
3493 // front-end wrapper for putting text in graph
\r
3494 void DrawSeekText(char *buf, int x, int y)
\r
3497 SetBkMode( hdcSeek, TRANSPARENT );
\r
3498 GetTextExtentPoint32( hdcSeek, buf, strlen(buf), &stSize );
\r
3499 TextOut( hdcSeek, boardRect.left+x-3, boardRect.top+y-stSize.cy/2, buf, strlen(buf) );
\r
3502 void DrawSeekDot(int x, int y, int color)
\r
3504 int square = color & 0x80;
\r
3505 HBRUSH oldBrush = SelectObject(hdcSeek,
\r
3506 color == 0 ? markerBrush : color == 1 ? darkSquareBrush : explodeBrush);
\r
3509 Rectangle(hdcSeek, boardRect.left+x - squareSize/9, boardRect.top+y - squareSize/9,
\r
3510 boardRect.left+x + squareSize/9, boardRect.top+y + squareSize/9);
\r
3512 Ellipse(hdcSeek, boardRect.left+x - squareSize/8, boardRect.top+y - squareSize/8,
\r
3513 boardRect.left+x + squareSize/8, boardRect.top+y + squareSize/8);
\r
3514 SelectObject(hdcSeek, oldBrush);
\r
3518 HDCDrawPosition(HDC hdc, BOOLEAN repaint, Board board)
\r
3520 static Board lastReq[2], lastDrawn[2];
\r
3521 static HighlightInfo lastDrawnHighlight, lastDrawnPremove;
\r
3522 static int lastDrawnFlipView = 0;
\r
3523 static int lastReqValid[2] = {0, 0}, lastDrawnValid[2] = {0, 0};
\r
3524 int releaseDC, x, y, x2, y2, row, column, num_clips = 0, i;
\r
3527 HBITMAP bufferBitmap;
\r
3528 HBITMAP oldBitmap;
\r
3530 HRGN clips[MAX_CLIPS];
\r
3531 ChessSquare dragged_piece = EmptySquare;
\r
3532 int nr = twoBoards*partnerUp;
\r
3534 /* I'm undecided on this - this function figures out whether a full
\r
3535 * repaint is necessary on its own, so there's no real reason to have the
\r
3536 * caller tell it that. I think this can safely be set to FALSE - but
\r
3537 * if we trust the callers not to request full repaints unnessesarily, then
\r
3538 * we could skip some clipping work. In other words, only request a full
\r
3539 * redraw when the majority of pieces have changed positions (ie. flip,
\r
3540 * gamestart and similar) --Hawk
\r
3542 Boolean fullrepaint = repaint;
\r
3544 if(DrawSeekGraph()) return; // [HG} seekgraph: suppress printing board if seek graph up
\r
3546 if( DrawPositionNeedsFullRepaint() ) {
\r
3547 fullrepaint = TRUE;
\r
3550 if (board == NULL) {
\r
3551 if (!lastReqValid[nr]) {
\r
3554 board = lastReq[nr];
\r
3556 CopyBoard(lastReq[nr], board);
\r
3557 lastReqValid[nr] = 1;
\r
3560 if (doingSizing) {
\r
3564 if (IsIconic(hwndMain)) {
\r
3568 if (hdc == NULL) {
\r
3569 hdc = GetDC(hwndMain);
\r
3570 if (!appData.monoMode) {
\r
3571 SelectPalette(hdc, hPal, FALSE);
\r
3572 RealizePalette(hdc);
\r
3576 releaseDC = FALSE;
\r
3579 /* Create some work-DCs */
\r
3580 hdcmem = CreateCompatibleDC(hdc);
\r
3581 tmphdc = CreateCompatibleDC(hdc);
\r
3583 /* If dragging is in progress, we temporarely remove the piece */
\r
3584 /* [HGM] or temporarily decrease count if stacked */
\r
3585 /* !! Moved to before board compare !! */
\r
3586 if (dragInfo.from.x >= 0 && dragInfo.pos.x >= 0) {
\r
3587 dragged_piece = board[dragInfo.from.y][dragInfo.from.x];
\r
3588 if(dragInfo.from.x == BOARD_LEFT-2 ) {
\r
3589 if(--board[dragInfo.from.y][dragInfo.from.x+1] == 0 )
\r
3590 board[dragInfo.from.y][dragInfo.from.x] = EmptySquare;
\r
3592 if(dragInfo.from.x == BOARD_RGHT+1) {
\r
3593 if(--board[dragInfo.from.y][dragInfo.from.x-1] == 0 )
\r
3594 board[dragInfo.from.y][dragInfo.from.x] = EmptySquare;
\r
3596 board[dragInfo.from.y][dragInfo.from.x] = gatingPiece;
\r
3599 /* Figure out which squares need updating by comparing the
\r
3600 * newest board with the last drawn board and checking if
\r
3601 * flipping has changed.
\r
3603 if (!fullrepaint && lastDrawnValid[nr] && (nr == 1 || lastDrawnFlipView == flipView)) {
\r
3604 for (row = 0; row < BOARD_HEIGHT; row++) { /* [HGM] true size, not 8 */
\r
3605 for (column = 0; column < BOARD_WIDTH; column++) {
\r
3606 if (lastDrawn[nr][row][column] != board[row][column]) {
\r
3607 SquareToPos(row, column, &x, &y);
\r
3608 clips[num_clips++] =
\r
3609 CreateRectRgn(x, y, x + squareSize, y + squareSize);
\r
3613 if(nr == 0) { // [HGM] dual: no highlights on second board
\r
3614 for (i=0; i<2; i++) {
\r
3615 if (lastDrawnHighlight.sq[i].x != highlightInfo.sq[i].x ||
\r
3616 lastDrawnHighlight.sq[i].y != highlightInfo.sq[i].y) {
\r
3617 if (lastDrawnHighlight.sq[i].x >= 0 &&
\r
3618 lastDrawnHighlight.sq[i].y >= 0) {
\r
3619 SquareToPos(lastDrawnHighlight.sq[i].y,
\r
3620 lastDrawnHighlight.sq[i].x, &x, &y);
\r
3621 clips[num_clips++] =
\r
3622 CreateRectRgn(x - lineGap, y - lineGap,
\r
3623 x + squareSize + lineGap, y + squareSize + lineGap);
\r
3625 if (highlightInfo.sq[i].x >= 0 && highlightInfo.sq[i].y >= 0) {
\r
3626 SquareToPos(highlightInfo.sq[i].y, highlightInfo.sq[i].x, &x, &y);
\r
3627 clips[num_clips++] =
\r
3628 CreateRectRgn(x - lineGap, y - lineGap,
\r
3629 x + squareSize + lineGap, y + squareSize + lineGap);
\r
3633 for (i=0; i<2; i++) {
\r
3634 if (lastDrawnPremove.sq[i].x != premoveHighlightInfo.sq[i].x ||
\r
3635 lastDrawnPremove.sq[i].y != premoveHighlightInfo.sq[i].y) {
\r
3636 if (lastDrawnPremove.sq[i].x >= 0 &&
\r
3637 lastDrawnPremove.sq[i].y >= 0) {
\r
3638 SquareToPos(lastDrawnPremove.sq[i].y,
\r
3639 lastDrawnPremove.sq[i].x, &x, &y);
\r
3640 clips[num_clips++] =
\r
3641 CreateRectRgn(x - lineGap, y - lineGap,
\r
3642 x + squareSize + lineGap, y + squareSize + lineGap);
\r
3644 if (premoveHighlightInfo.sq[i].x >= 0 &&
\r
3645 premoveHighlightInfo.sq[i].y >= 0) {
\r
3646 SquareToPos(premoveHighlightInfo.sq[i].y,
\r
3647 premoveHighlightInfo.sq[i].x, &x, &y);
\r
3648 clips[num_clips++] =
\r
3649 CreateRectRgn(x - lineGap, y - lineGap,
\r
3650 x + squareSize + lineGap, y + squareSize + lineGap);
\r
3654 } else { // nr == 1
\r
3655 partnerHighlightInfo.sq[0].y = board[EP_STATUS-4];
\r
3656 partnerHighlightInfo.sq[0].x = board[EP_STATUS-3];
\r
3657 partnerHighlightInfo.sq[1].y = board[EP_STATUS-2];
\r
3658 partnerHighlightInfo.sq[1].x = board[EP_STATUS-1];
\r
3659 for (i=0; i<2; i++) {
\r
3660 if (partnerHighlightInfo.sq[i].x >= 0 &&
\r
3661 partnerHighlightInfo.sq[i].y >= 0) {
\r
3662 SquareToPos(partnerHighlightInfo.sq[i].y,
\r
3663 partnerHighlightInfo.sq[i].x, &x, &y);
\r
3664 clips[num_clips++] =
\r
3665 CreateRectRgn(x - lineGap, y - lineGap,
\r
3666 x + squareSize + lineGap, y + squareSize + lineGap);
\r
3668 if (oldPartnerHighlight.sq[i].x >= 0 &&
\r
3669 oldPartnerHighlight.sq[i].y >= 0) {
\r
3670 SquareToPos(oldPartnerHighlight.sq[i].y,
\r
3671 oldPartnerHighlight.sq[i].x, &x, &y);
\r
3672 clips[num_clips++] =
\r
3673 CreateRectRgn(x - lineGap, y - lineGap,
\r
3674 x + squareSize + lineGap, y + squareSize + lineGap);
\r
3679 fullrepaint = TRUE;
\r
3682 /* Create a buffer bitmap - this is the actual bitmap
\r
3683 * being written to. When all the work is done, we can
\r
3684 * copy it to the real DC (the screen). This avoids
\r
3685 * the problems with flickering.
\r
3687 GetClientRect(hwndMain, &Rect);
\r
3688 bufferBitmap = CreateCompatibleBitmap(hdc, Rect.right-Rect.left+1,
\r
3689 Rect.bottom-Rect.top+1);
\r
3690 oldBitmap = SelectObject(hdcmem, bufferBitmap);
\r
3691 if (!appData.monoMode) {
\r
3692 SelectPalette(hdcmem, hPal, FALSE);
\r
3695 /* Create clips for dragging */
\r
3696 if (!fullrepaint) {
\r
3697 if (dragInfo.from.x >= 0) {
\r
3698 SquareToPos(dragInfo.from.y, dragInfo.from.x, &x, &y);
\r
3699 clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);
\r
3701 if (dragInfo.start.x >= 0) {
\r
3702 SquareToPos(dragInfo.start.y, dragInfo.start.x, &x, &y);
\r
3703 clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);
\r
3705 if (dragInfo.pos.x >= 0) {
\r
3706 x = dragInfo.pos.x - squareSize / 2;
\r
3707 y = dragInfo.pos.y - squareSize / 2;
\r
3708 clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);
\r
3710 if (dragInfo.lastpos.x >= 0) {
\r
3711 x = dragInfo.lastpos.x - squareSize / 2;
\r
3712 y = dragInfo.lastpos.y - squareSize / 2;
\r
3713 clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);
\r
3717 /* Are we animating a move?
\r
3719 * - remove the piece from the board (temporarely)
\r
3720 * - calculate the clipping region
\r
3722 if (!fullrepaint) {
\r
3723 if (animInfo.piece != EmptySquare) {
\r
3724 board[animInfo.from.y][animInfo.from.x] = EmptySquare;
\r
3725 x = boardRect.left + animInfo.lastpos.x;
\r
3726 y = boardRect.top + animInfo.lastpos.y;
\r
3727 x2 = boardRect.left + animInfo.pos.x;
\r
3728 y2 = boardRect.top + animInfo.pos.y;
\r
3729 clips[num_clips++] = CreateRectRgn(MIN(x,x2), MIN(y,y2), MAX(x,x2)+squareSize, MAX(y,y2)+squareSize);
\r
3730 /* Slight kludge. The real problem is that after AnimateMove is
\r
3731 done, the position on the screen does not match lastDrawn.
\r
3732 This currently causes trouble only on e.p. captures in
\r
3733 atomic, where the piece moves to an empty square and then
\r
3734 explodes. The old and new positions both had an empty square
\r
3735 at the destination, but animation has drawn a piece there and
\r
3736 we have to remember to erase it. [HGM] moved until after setting lastDrawn */
\r
3737 lastDrawn[0][animInfo.to.y][animInfo.to.x] = animInfo.piece;
\r
3741 /* No clips? Make sure we have fullrepaint set to TRUE */
\r
3742 if (num_clips == 0)
\r
3743 fullrepaint = TRUE;
\r
3745 /* Set clipping on the memory DC */
\r
3746 if (!fullrepaint) {
\r
3747 SelectClipRgn(hdcmem, clips[0]);
\r
3748 for (x = 1; x < num_clips; x++) {
\r
3749 if (ExtSelectClipRgn(hdcmem, clips[x], RGN_OR) == ERROR)
\r
3750 abort(); // this should never ever happen!
\r
3754 /* Do all the drawing to the memory DC */
\r
3755 if(explodeInfo.radius) { // [HGM] atomic
\r
3757 int x, y, r=(explodeInfo.radius * squareSize)/100;
\r
3758 ChessSquare piece = board[explodeInfo.fromY][explodeInfo.fromX];
\r
3759 board[explodeInfo.fromY][explodeInfo.fromX] = EmptySquare; // suppress display of capturer
\r
3760 SquareToPos(explodeInfo.toY, explodeInfo.toX, &x, &y);
\r
3761 x += squareSize/2;
\r
3762 y += squareSize/2;
\r
3763 if(!fullrepaint) {
\r
3764 clips[num_clips] = CreateRectRgn(x-r, y-r, x+r, y+r);
\r
3765 ExtSelectClipRgn(hdcmem, clips[num_clips++], RGN_OR);
\r
3767 DrawGridOnDC(hdcmem);
\r
3768 DrawHighlightsOnDC(hdcmem, &highlightInfo, HIGHLIGHT_PEN);
\r
3769 DrawHighlightsOnDC(hdcmem, &premoveHighlightInfo, PREMOVE_PEN);
\r
3770 DrawBoardOnDC(hdcmem, board, tmphdc);
\r
3771 board[explodeInfo.fromY][explodeInfo.fromX] = piece;
\r
3772 oldBrush = SelectObject(hdcmem, explodeBrush);
\r
3773 Ellipse(hdcmem, x-r, y-r, x+r, y+r);
\r
3774 SelectObject(hdcmem, oldBrush);
\r
3776 DrawGridOnDC(hdcmem);
\r
3777 if(nr == 0) { // [HGM] dual: decide which highlights to draw
\r
3778 DrawHighlightsOnDC(hdcmem, &highlightInfo, HIGHLIGHT_PEN);
\r
3779 DrawHighlightsOnDC(hdcmem, &premoveHighlightInfo, PREMOVE_PEN);
\r
3781 DrawHighlightsOnDC(hdcmem, &partnerHighlightInfo, HIGHLIGHT_PEN);
\r
3782 oldPartnerHighlight = partnerHighlightInfo;
\r
3784 DrawBoardOnDC(hdcmem, board, tmphdc);
\r
3786 if(nr == 0) // [HGM] dual: markers only on left board
\r
3787 for (row = 0; row < BOARD_HEIGHT; row++) {
\r
3788 for (column = 0; column < BOARD_WIDTH; column++) {
\r
3789 if (marker[row][column]) { // marker changes only occur with full repaint!
\r
3790 HBRUSH oldBrush = SelectObject(hdcmem,
\r
3791 marker[row][column] == 2 ? markerBrush : explodeBrush);
\r
3792 SquareToPos(row, column, &x, &y);
\r
3793 Ellipse(hdcmem, x + squareSize/4, y + squareSize/4,
\r
3794 x + 3*squareSize/4, y + 3*squareSize/4);
\r
3795 SelectObject(hdcmem, oldBrush);
\r
3800 if( appData.highlightMoveWithArrow ) {
\r
3801 DrawArrowHighlight(hdcmem);
\r
3804 DrawCoordsOnDC(hdcmem);
\r
3806 CopyBoard(lastDrawn[nr], board); /* [HGM] Moved to here from end of routine, */
\r
3807 /* to make sure lastDrawn contains what is actually drawn */
\r
3809 /* Put the dragged piece back into place and draw it (out of place!) */
\r
3810 if (dragged_piece != EmptySquare) {
\r
3811 /* [HGM] or restack */
\r
3812 if(dragInfo.from.x == BOARD_LEFT-2 )
\r
3813 board[dragInfo.from.y][dragInfo.from.x+1]++;
\r
3815 if(dragInfo.from.x == BOARD_RGHT+1 )
\r
3816 board[dragInfo.from.y][dragInfo.from.x-1]++;
\r
3817 board[dragInfo.from.y][dragInfo.from.x] = dragged_piece;
\r
3818 x = dragInfo.pos.x - squareSize / 2;
\r
3819 y = dragInfo.pos.y - squareSize / 2;
\r
3820 DrawPieceOnDC(hdcmem, dragInfo.piece,
\r
3821 ((int) dragInfo.piece < (int) BlackPawn),
\r
3822 (dragInfo.from.y + dragInfo.from.x) % 2, x, y, tmphdc);
\r
3825 /* Put the animated piece back into place and draw it */
\r
3826 if (animInfo.piece != EmptySquare) {
\r
3827 board[animInfo.from.y][animInfo.from.x] = animInfo.piece;
\r
3828 x = boardRect.left + animInfo.pos.x;
\r
3829 y = boardRect.top + animInfo.pos.y;
\r
3830 DrawPieceOnDC(hdcmem, animInfo.piece,
\r
3831 ((int) animInfo.piece < (int) BlackPawn),
\r
3832 (animInfo.from.y + animInfo.from.x) % 2, x, y, tmphdc);
\r
3835 /* Release the bufferBitmap by selecting in the old bitmap
\r
3836 * and delete the memory DC
\r
3838 SelectObject(hdcmem, oldBitmap);
\r
3841 /* Set clipping on the target DC */
\r
3842 if (!fullrepaint) {
\r
3843 if(nr == 1) for (x = 0; x < num_clips; x++) { // [HGM] dual: translate clips
\r
3845 GetRgnBox(clips[x], &rect);
\r
3846 DeleteObject(clips[x]);
\r
3847 clips[x] = CreateRectRgn(rect.left + wpMain.width/2, rect.top,
\r
3848 rect.right + wpMain.width/2, rect.bottom);
\r
3850 SelectClipRgn(hdc, clips[0]);
\r
3851 for (x = 1; x < num_clips; x++) {
\r
3852 if (ExtSelectClipRgn(hdc, clips[x], RGN_OR) == ERROR)
\r
3853 abort(); // this should never ever happen!
\r
3857 /* Copy the new bitmap onto the screen in one go.
\r
3858 * This way we avoid any flickering
\r
3860 oldBitmap = SelectObject(tmphdc, bufferBitmap);
\r
3861 BitBlt(hdc, boardRect.left + twoBoards*partnerUp*wpMain.width/2, boardRect.top, // [HGM] dual
\r
3862 boardRect.right - boardRect.left,
\r
3863 boardRect.bottom - boardRect.top,
\r
3864 tmphdc, boardRect.left, boardRect.top, SRCCOPY);
\r
3865 if(saveDiagFlag) {
\r
3866 BITMAP b; int i, j=0, m, w, wb, fac=0; char *pData;
\r
3867 BITMAPINFOHEADER bih; int color[16], nrColors=0;
\r
3869 GetObject(bufferBitmap, sizeof(b), &b);
\r
3870 if(pData = malloc(b.bmWidthBytes*b.bmHeight + 10000)) {
\r
3871 bih.biSize = sizeof(BITMAPINFOHEADER);
\r
3872 bih.biWidth = b.bmWidth;
\r
3873 bih.biHeight = b.bmHeight;
\r
3875 bih.biBitCount = b.bmBitsPixel;
\r
3876 bih.biCompression = 0;
\r
3877 bih.biSizeImage = b.bmWidthBytes*b.bmHeight;
\r
3878 bih.biXPelsPerMeter = 0;
\r
3879 bih.biYPelsPerMeter = 0;
\r
3880 bih.biClrUsed = 0;
\r
3881 bih.biClrImportant = 0;
\r
3882 // fprintf(diagFile, "t=%d\nw=%d\nh=%d\nB=%d\nP=%d\nX=%d\n",
\r
3883 // b.bmType, b.bmWidth, b.bmHeight, b.bmWidthBytes, b.bmPlanes, b.bmBitsPixel);
\r
3884 GetDIBits(tmphdc,bufferBitmap,0,b.bmHeight,pData,(BITMAPINFO*)&bih,DIB_RGB_COLORS);
\r
3885 // fprintf(diagFile, "%8x\n", (int) pData);
\r
3887 wb = b.bmWidthBytes;
\r
3889 for(i=0; i<wb*(b.bmHeight - boardRect.top + OUTER_MARGIN)>>2; i++) {
\r
3890 int k = ((int*) pData)[i];
\r
3891 for(j=0; j<nrColors; j++) if(color[j] == k) break;
\r
3892 if(j >= 16) break;
\r
3894 if(j >= nrColors) nrColors = j+1;
\r
3896 if(j<16) { // 16 colors is enough. Compress to 4 bits per pixel
\r
3898 for(i=0; i<b.bmHeight - boardRect.top + OUTER_MARGIN; i++) {
\r
3899 for(w=0; w<(wb>>2); w+=2) {
\r
3900 int k = ((int*) pData)[(wb*i>>2) + w];
\r
3901 for(j=0; j<nrColors; j++) if(color[j] == k) break;
\r
3902 k = ((int*) pData)[(wb*i>>2) + w + 1];
\r
3903 for(m=0; m<nrColors; m++) if(color[m] == k) break;
\r
3904 pData[p++] = m | j<<4;
\r
3906 while(p&3) pData[p++] = 0;
\r
3909 wb = ((wb+31)>>5)<<2;
\r
3911 // write BITMAPFILEHEADER
\r
3912 fprintf(diagFile, "BM");
\r
3913 fputDW(diagFile, wb*(b.bmHeight - boardRect.top + OUTER_MARGIN)+0x36 + (fac?64:0));
\r
3914 fputDW(diagFile, 0);
\r
3915 fputDW(diagFile, 0x36 + (fac?64:0));
\r
3916 // write BITMAPINFOHEADER
\r
3917 fputDW(diagFile, 40);
\r
3918 fputDW(diagFile, b.bmWidth);
\r
3919 fputDW(diagFile, b.bmHeight - boardRect.top + OUTER_MARGIN);
\r
3920 if(fac) fputDW(diagFile, 0x040001); // planes and bits/pixel
\r
3921 else fputDW(diagFile, 0x200001); // planes and bits/pixel
\r
3922 fputDW(diagFile, 0);
\r
3923 fputDW(diagFile, 0);
\r
3924 fputDW(diagFile, 0);
\r
3925 fputDW(diagFile, 0);
\r
3926 fputDW(diagFile, 0);
\r
3927 fputDW(diagFile, 0);
\r
3928 // write color table
\r
3930 for(i=0; i<16; i++) fputDW(diagFile, color[i]);
\r
3931 // write bitmap data
\r
3932 for(i=0; i<wb*(b.bmHeight - boardRect.top + OUTER_MARGIN); i++)
\r
3933 fputc(pData[i], diagFile);
\r
3938 SelectObject(tmphdc, oldBitmap);
\r
3940 /* Massive cleanup */
\r
3941 for (x = 0; x < num_clips; x++)
\r
3942 DeleteObject(clips[x]);
\r
3945 DeleteObject(bufferBitmap);
\r
3948 ReleaseDC(hwndMain, hdc);
\r
3950 if (lastDrawnFlipView != flipView && nr == 0) {
\r
3952 CheckMenuItem(GetMenu(hwndMain),IDM_FlipView, MF_BYCOMMAND|MF_CHECKED);
\r
3954 CheckMenuItem(GetMenu(hwndMain),IDM_FlipView, MF_BYCOMMAND|MF_UNCHECKED);
\r
3957 /* CopyBoard(lastDrawn, board);*/
\r
3958 lastDrawnHighlight = highlightInfo;
\r
3959 lastDrawnPremove = premoveHighlightInfo;
\r
3960 lastDrawnFlipView = flipView;
\r
3961 lastDrawnValid[nr] = 1;
\r
3964 /* [HGM] diag: Save the current board display to the given open file and close the file */
\r
3969 saveDiagFlag = 1; diagFile = f;
\r
3970 HDCDrawPosition(NULL, TRUE, NULL);
\r
3978 /*---------------------------------------------------------------------------*\
\r
3979 | CLIENT PAINT PROCEDURE
\r
3980 | This is the main event-handler for the WM_PAINT message.
\r
3982 \*---------------------------------------------------------------------------*/
\r
3984 PaintProc(HWND hwnd)
\r
3990 if((hdc = BeginPaint(hwnd, &ps))) {
\r
3991 if (IsIconic(hwnd)) {
\r
3992 DrawIcon(hdc, 2, 2, iconCurrent);
\r
3994 if (!appData.monoMode) {
\r
3995 SelectPalette(hdc, hPal, FALSE);
\r
3996 RealizePalette(hdc);
\r
3998 HDCDrawPosition(hdc, 1, NULL);
\r
3999 if(twoBoards) { // [HGM] dual: also redraw other board in other orientation
\r
4000 flipView = !flipView; partnerUp = !partnerUp;
\r
4001 HDCDrawPosition(hdc, 1, NULL);
\r
4002 flipView = !flipView; partnerUp = !partnerUp;
\r
4005 SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);
\r
4006 ExtTextOut(hdc, messageRect.left, messageRect.top,
\r
4007 ETO_CLIPPED|ETO_OPAQUE,
\r
4008 &messageRect, messageText, strlen(messageText), NULL);
\r
4009 SelectObject(hdc, oldFont);
\r
4010 DisplayBothClocks();
\r
4013 EndPaint(hwnd,&ps);
\r
4021 * If the user selects on a border boundary, return -1; if off the board,
\r
4022 * return -2. Otherwise map the event coordinate to the square.
\r
4023 * The offset boardRect.left or boardRect.top must already have been
\r
4024 * subtracted from x.
\r
4026 int EventToSquare(x, limit)
\r
4034 if ((x % (squareSize + lineGap)) >= squareSize)
\r
4036 x /= (squareSize + lineGap);
\r
4048 DropEnable dropEnables[] = {
\r
4049 { 'P', DP_Pawn, N_("Pawn") },
\r
4050 { 'N', DP_Knight, N_("Knight") },
\r
4051 { 'B', DP_Bishop, N_("Bishop") },
\r
4052 { 'R', DP_Rook, N_("Rook") },
\r
4053 { 'Q', DP_Queen, N_("Queen") },
\r
4057 SetupDropMenu(HMENU hmenu)
\r
4059 int i, count, enable;
\r
4061 extern char white_holding[], black_holding[];
\r
4062 char item[MSG_SIZ];
\r
4064 for (i=0; i<sizeof(dropEnables)/sizeof(DropEnable); i++) {
\r
4065 p = strchr(gameMode == IcsPlayingWhite ? white_holding : black_holding,
\r
4066 dropEnables[i].piece);
\r
4068 while (p && *p++ == dropEnables[i].piece) count++;
\r
4069 snprintf(item, MSG_SIZ, "%s %d", T_(dropEnables[i].name), count);
\r
4070 enable = count > 0 || !appData.testLegality
\r
4071 /*!!temp:*/ || (gameInfo.variant == VariantCrazyhouse
\r
4072 && !appData.icsActive);
\r
4073 ModifyMenu(hmenu, dropEnables[i].command,
\r
4074 MF_BYCOMMAND | (enable ? MF_ENABLED : MF_GRAYED) | MF_STRING,
\r
4075 dropEnables[i].command, item);
\r
4079 void DragPieceBegin(int x, int y, Boolean instantly)
\r
4081 dragInfo.lastpos.x = boardRect.left + x;
\r
4082 dragInfo.lastpos.y = boardRect.top + y;
\r
4083 if(instantly) dragInfo.pos = dragInfo.lastpos;
\r
4084 dragInfo.from.x = fromX;
\r
4085 dragInfo.from.y = fromY;
\r
4086 dragInfo.piece = boards[currentMove][fromY][fromX];
\r
4087 dragInfo.start = dragInfo.from;
\r
4088 SetCapture(hwndMain);
\r
4091 void DragPieceEnd(int x, int y)
\r
4094 dragInfo.start.x = dragInfo.start.y = -1;
\r
4095 dragInfo.from = dragInfo.start;
\r
4096 dragInfo.pos = dragInfo.lastpos = dragInfo.start;
\r
4099 void ChangeDragPiece(ChessSquare piece)
\r
4101 dragInfo.piece = piece;
\r
4104 /* Event handler for mouse messages */
\r
4106 MouseEvent(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
\r
4110 static int recursive = 0;
\r
4112 BOOLEAN forceFullRepaint = IsFullRepaintPreferrable(); /* [AS] */
\r
4115 if (message == WM_MBUTTONUP) {
\r
4116 /* Hideous kludge to fool TrackPopupMenu into paying attention
\r
4117 to the middle button: we simulate pressing the left button too!
\r
4119 PostMessage(hwnd, WM_LBUTTONDOWN, wParam, lParam);
\r
4120 PostMessage(hwnd, WM_LBUTTONUP, wParam, lParam);
\r
4126 pt.x = LOWORD(lParam);
\r
4127 pt.y = HIWORD(lParam);
\r
4128 x = EventToSquare(pt.x - boardRect.left, BOARD_WIDTH);
\r
4129 y = EventToSquare(pt.y - boardRect.top, BOARD_HEIGHT);
\r
4130 if (!flipView && y >= 0) {
\r
4131 y = BOARD_HEIGHT - 1 - y;
\r
4133 if (flipView && x >= 0) {
\r
4134 x = BOARD_WIDTH - 1 - x;
\r
4137 shiftKey = GetKeyState(VK_SHIFT) < 0; // [HGM] remember last shift status
\r
4139 switch (message) {
\r
4140 case WM_LBUTTONDOWN:
\r
4141 if (PtInRect((LPRECT) &whiteRect, pt)) {
\r
4142 ClockClick(flipClock);
\r
4143 } else if (PtInRect((LPRECT) &blackRect, pt)) {
\r
4144 ClockClick(!flipClock);
\r
4146 dragInfo.start.x = dragInfo.start.y = -1;
\r
4147 dragInfo.from = dragInfo.start;
\r
4148 if(fromX == -1 && frozen) { // not sure where this is for
\r
4149 fromX = fromY = -1;
\r
4150 DrawPosition(forceFullRepaint || FALSE, NULL); /* [AS] */
\r
4153 LeftClick(Press, pt.x - boardRect.left, pt.y - boardRect.top);
\r
4154 DrawPosition(TRUE, NULL);
\r
4157 case WM_LBUTTONUP:
\r
4158 LeftClick(Release, pt.x - boardRect.left, pt.y - boardRect.top);
\r
4159 DrawPosition(TRUE, NULL);
\r
4162 case WM_MOUSEMOVE:
\r
4163 if(SeekGraphClick(Press, pt.x - boardRect.left, pt.y - boardRect.top, 1)) break;
\r
4164 if(PromoScroll(pt.x - boardRect.left, pt.y - boardRect.top)) break;
\r
4165 MovePV(pt.x - boardRect.left, pt.y - boardRect.top, boardRect.bottom - boardRect.top);
\r
4166 if ((appData.animateDragging || appData.highlightDragging)
\r
4167 && (wParam & MK_LBUTTON)
\r
4168 && dragInfo.from.x >= 0)
\r
4170 BOOL full_repaint = FALSE;
\r
4172 if (appData.animateDragging) {
\r
4173 dragInfo.pos = pt;
\r
4175 if (appData.highlightDragging) {
\r
4176 SetHighlights(fromX, fromY, x, y);
\r
4177 if( IsDrawArrowEnabled() && (x < 0 || x >= BOARD_WIDTH || y < 0 || y >= BOARD_HEIGHT) ) {
\r
4178 full_repaint = TRUE;
\r
4182 DrawPosition( full_repaint, NULL);
\r
4184 dragInfo.lastpos = dragInfo.pos;
\r
4188 case WM_MOUSEWHEEL: // [DM]
\r
4189 { static int lastDir = 0; // [HGM] build in some hysteresis to avoid spurious events
\r
4190 /* Mouse Wheel is being rolled forward
\r
4191 * Play moves forward
\r
4193 if((short)HIWORD(wParam) < 0 && currentMove < forwardMostMove)
\r
4194 { if(lastDir == 1) ForwardEvent(); else lastDir = 1; } // [HGM] suppress first event in direction
\r
4195 /* Mouse Wheel is being rolled backward
\r
4196 * Play moves backward
\r
4198 if((short)HIWORD(wParam) > 0 && currentMove > backwardMostMove)
\r
4199 { if(lastDir == -1) BackwardEvent(); else lastDir = -1; }
\r
4203 case WM_MBUTTONUP:
\r
4204 case WM_RBUTTONUP:
\r
4206 RightClick(Release, pt.x - boardRect.left, pt.y - boardRect.top, &fromX, &fromY);
\r
4209 case WM_MBUTTONDOWN:
\r
4210 case WM_RBUTTONDOWN:
\r
4213 fromX = fromY = -1;
\r
4214 dragInfo.pos.x = dragInfo.pos.y = -1;
\r
4215 dragInfo.start.x = dragInfo.start.y = -1;
\r
4216 dragInfo.from = dragInfo.start;
\r
4217 dragInfo.lastpos = dragInfo.pos;
\r
4218 if (appData.highlightDragging) {
\r
4219 ClearHighlights();
\r
4222 /* [HGM] right mouse button in clock area edit-game mode ups clock */
\r
4223 if (PtInRect((LPRECT) &whiteRect, pt)) {
\r
4224 if (gameMode == EditGame || GetKeyState(VK_SHIFT) < 0) AdjustClock(flipClock, 1);
\r
4225 } else if (PtInRect((LPRECT) &blackRect, pt)) {
\r
4226 if (gameMode == EditGame || GetKeyState(VK_SHIFT) < 0) AdjustClock(!flipClock, 1);
\r
4230 DrawPosition(TRUE, NULL);
\r
4232 menuNr = RightClick(Press, pt.x - boardRect.left, pt.y - boardRect.top, &fromX, &fromY);
\r
4235 if (message == WM_MBUTTONDOWN) {
\r
4236 buttonCount = 3; /* even if system didn't think so */
\r
4237 if (wParam & MK_SHIFT)
\r
4238 MenuPopup(hwnd, pt, LoadMenu(hInst, "BlackPieceMenu"), -1);
\r
4240 MenuPopup(hwnd, pt, LoadMenu(hInst, "WhitePieceMenu"), -1);
\r
4241 } else { /* message == WM_RBUTTONDOWN */
\r
4242 /* Just have one menu, on the right button. Windows users don't
\r
4243 think to try the middle one, and sometimes other software steals
\r
4244 it, or it doesn't really exist. */
\r
4245 if(gameInfo.variant != VariantShogi)
\r
4246 MenuPopup(hwnd, pt, LoadMenu(hInst, "PieceMenu"), -1);
\r
4248 MenuPopup(hwnd, pt, LoadMenu(hInst, "ShogiPieceMenu"), -1);
\r
4252 SetCapture(hwndMain);
4255 hmenu = LoadMenu(hInst, "DropPieceMenu");
\r
4256 SetupDropMenu(hmenu);
\r
4257 MenuPopup(hwnd, pt, hmenu, -1);
\r
4267 /* Preprocess messages for buttons in main window */
\r
4269 ButtonProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
\r
4271 int id = GetWindowLongPtr(hwnd, GWLP_ID);
\r
4274 for (i=0; i<N_BUTTONS; i++) {
\r
4275 if (buttonDesc[i].id == id) break;
\r
4277 if (i == N_BUTTONS) return 0;
\r
4278 switch (message) {
\r
4283 dir = (wParam == VK_LEFT) ? -1 : 1;
\r
4284 SetFocus(buttonDesc[(i + dir + N_BUTTONS) % N_BUTTONS].hwnd);
\r
4291 SendMessage(hwndMain, WM_COMMAND, MAKEWPARAM(buttonDesc[i].id, 0), 0);
\r
4294 if (appData.icsActive && (isalpha((char)wParam) || wParam == '0')) {
\r
4295 // [HGM] movenum: only letters or leading zero should go to ICS input
\r
4296 HWND h = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
4297 if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);
\r
4299 SendMessage(h, WM_CHAR, wParam, lParam);
\r
4301 } else if (isalpha((char)wParam) || isdigit((char)wParam)){
\r
4302 TypeInEvent((char)wParam);
\r
4308 return CallWindowProc(buttonDesc[i].wndproc, hwnd, message, wParam, lParam);
\r
4311 /* Process messages for Promotion dialog box */
\r
4313 Promotion(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
4317 switch (message) {
\r
4318 case WM_INITDIALOG: /* message: initialize dialog box */
\r
4319 /* Center the dialog over the application window */
\r
4320 CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));
\r
4321 Translate(hDlg, DLG_PromotionKing);
\r
4322 ShowWindow(GetDlgItem(hDlg, PB_King),
\r
4323 (!appData.testLegality || gameInfo.variant == VariantSuicide ||
\r
4324 gameInfo.variant == VariantSpartan && !WhiteOnMove(currentMove) ||
\r
4325 gameInfo.variant == VariantGiveaway || gameInfo.variant == VariantSuper ) ?
\r
4326 SW_SHOW : SW_HIDE);
\r
4327 /* [HGM] Only allow C & A promotions if these pieces are defined */
\r
4328 ShowWindow(GetDlgItem(hDlg, PB_Archbishop),
\r
4329 ((PieceToChar(WhiteAngel) >= 'A' && WhiteOnMove(currentMove) &&
\r
4330 PieceToChar(WhiteAngel) != '~') ||
\r
4331 (PieceToChar(BlackAngel) >= 'A' && !WhiteOnMove(currentMove) &&
\r
4332 PieceToChar(BlackAngel) != '~') ) ?
\r
4333 SW_SHOW : SW_HIDE);
\r
4334 ShowWindow(GetDlgItem(hDlg, PB_Chancellor),
\r
4335 ((PieceToChar(WhiteMarshall) >= 'A' && WhiteOnMove(currentMove) &&
\r
4336 PieceToChar(WhiteMarshall) != '~') ||
\r
4337 (PieceToChar(BlackMarshall) >= 'A' && !WhiteOnMove(currentMove) &&
\r
4338 PieceToChar(BlackMarshall) != '~') ) ?
\r
4339 SW_SHOW : SW_HIDE);
\r
4340 /* [HGM] Hide B & R button in Shogi, use Q as promote, N as defer */
\r
4341 ShowWindow(GetDlgItem(hDlg, PB_Rook),
\r
4342 gameInfo.variant != VariantShogi ?
\r
4343 SW_SHOW : SW_HIDE);
\r
4344 ShowWindow(GetDlgItem(hDlg, PB_Bishop),
\r
4345 gameInfo.variant != VariantShogi ?
\r
4346 SW_SHOW : SW_HIDE);
\r
4347 if(gameInfo.variant == VariantShogi) {
\r
4348 SetDlgItemText(hDlg, PB_Queen, "YES");
\r
4349 SetDlgItemText(hDlg, PB_Knight, "NO");
\r
4350 SetWindowText(hDlg, "Promote?");
\r
4352 ShowWindow(GetDlgItem(hDlg, IDC_Centaur),
\r
4353 gameInfo.variant == VariantSuper ?
\r
4354 SW_SHOW : SW_HIDE);
\r
4357 case WM_COMMAND: /* message: received a command */
\r
4358 switch (LOWORD(wParam)) {
\r
4360 EndDialog(hDlg, TRUE); /* Exit the dialog */
\r
4361 ClearHighlights();
\r
4362 DrawPosition(FALSE, NULL);
\r
4365 promoChar = gameInfo.variant == VariantSuper ? PieceToChar(BlackSilver) : PieceToChar(BlackKing);
\r
4368 promoChar = gameInfo.variant == VariantShogi ? '+' : ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteQueen : BlackQueen));
\r
4371 promoChar = ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteRook : BlackRook));
\r
4372 if(gameInfo.variant == VariantSpartan && !WhiteOnMove(currentMove)) promoChar = PieceToChar(BlackDragon);
\r
4375 promoChar = ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteBishop : BlackBishop));
\r
4376 if(gameInfo.variant == VariantSpartan && !WhiteOnMove(currentMove)) promoChar = PieceToChar(BlackAlfil);
\r
4378 case PB_Chancellor:
\r
4379 promoChar = ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteMarshall : BlackMarshall));
\r
4381 case PB_Archbishop:
\r
4382 promoChar = ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteAngel : BlackAngel));
\r
4385 promoChar = gameInfo.variant == VariantShogi ? '=' : PieceToChar(WhiteOnMove(currentMove) ? WhiteKnight : BlackKnight);
\r
4390 if(promoChar == '.') return FALSE; // invalid piece chosen
\r
4391 EndDialog(hDlg, TRUE); /* Exit the dialog */
\r
4392 UserMoveEvent(fromX, fromY, toX, toY, promoChar);
\r
4393 fromX = fromY = -1;
\r
4394 if (!appData.highlightLastMove) {
\r
4395 ClearHighlights();
\r
4396 DrawPosition(FALSE, NULL);
\r
4403 /* Pop up promotion dialog */
\r
4405 PromotionPopup(HWND hwnd)
\r
4409 lpProc = MakeProcInstance((FARPROC)Promotion, hInst);
\r
4410 DialogBox(hInst, MAKEINTRESOURCE(DLG_PromotionKing),
\r
4411 hwnd, (DLGPROC)lpProc);
\r
4412 FreeProcInstance(lpProc);
\r
4418 DrawPosition(TRUE, NULL);
\r
4419 PromotionPopup(hwndMain);
\r
4422 /* Toggle ShowThinking */
\r
4424 ToggleShowThinking()
\r
4426 appData.showThinking = !appData.showThinking;
\r
4427 ShowThinkingEvent();
\r
4431 LoadGameDialog(HWND hwnd, char* title)
\r
4435 char fileTitle[MSG_SIZ];
\r
4436 f = OpenFileDialog(hwnd, "rb", "",
\r
4437 appData.oldSaveStyle ? "gam" : "pgn",
\r
4439 title, &number, fileTitle, NULL);
\r
4441 cmailMsgLoaded = FALSE;
\r
4442 if (number == 0) {
\r
4443 int error = GameListBuild(f);
\r
4445 DisplayError(_("Cannot build game list"), error);
\r
4446 } else if (!ListEmpty(&gameList) &&
\r
4447 ((ListGame *) gameList.tailPred)->number > 1) {
\r
4448 GameListPopUp(f, fileTitle);
\r
4451 GameListDestroy();
\r
4454 LoadGame(f, number, fileTitle, FALSE);
\r
4458 int get_term_width()
\r
4463 HFONT hfont, hold_font;
\r
4468 hText = GetDlgItem(hwndConsole, OPT_ConsoleText);
\r
4472 // get the text metrics
\r
4473 hdc = GetDC(hText);
\r
4474 lf = font[boardSize][CONSOLE_FONT]->lf;
\r
4475 if (consoleCF.dwEffects & CFE_BOLD)
\r
4476 lf.lfWeight = FW_BOLD;
\r
4477 if (consoleCF.dwEffects & CFE_ITALIC)
\r
4478 lf.lfItalic = TRUE;
\r
4479 if (consoleCF.dwEffects & CFE_STRIKEOUT)
\r
4480 lf.lfStrikeOut = TRUE;
\r
4481 if (consoleCF.dwEffects & CFE_UNDERLINE)
\r
4482 lf.lfUnderline = TRUE;
\r
4483 hfont = CreateFontIndirect(&lf);
\r
4484 hold_font = SelectObject(hdc, hfont);
\r
4485 GetTextMetrics(hdc, &tm);
\r
4486 SelectObject(hdc, hold_font);
\r
4487 DeleteObject(hfont);
\r
4488 ReleaseDC(hText, hdc);
\r
4490 // get the rectangle
\r
4491 SendMessage(hText, EM_GETRECT, 0, (LPARAM)&rc);
\r
4493 return (rc.right-rc.left) / tm.tmAveCharWidth;
\r
4496 void UpdateICSWidth(HWND hText)
\r
4498 LONG old_width, new_width;
\r
4500 new_width = get_term_width(hText, FALSE);
\r
4501 old_width = GetWindowLongPtr(hText, GWLP_USERDATA);
\r
4502 if (new_width != old_width)
\r
4504 ics_update_width(new_width);
\r
4505 SetWindowLongPtr(hText, GWLP_USERDATA, new_width);
\r
4510 ChangedConsoleFont()
\r
4513 CHARRANGE tmpsel, sel;
\r
4514 MyFont *f = font[boardSize][CONSOLE_FONT];
\r
4515 HWND hText = GetDlgItem(hwndConsole, OPT_ConsoleText);
\r
4516 HWND hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
4519 cfmt.cbSize = sizeof(CHARFORMAT);
\r
4520 cfmt.dwMask = CFM_FACE|CFM_SIZE|CFM_CHARSET;
\r
4521 safeStrCpy(cfmt.szFaceName, font[boardSize][CONSOLE_FONT]->mfp.faceName,
\r
4522 sizeof(cfmt.szFaceName)/sizeof(cfmt.szFaceName[0]) );
\r
4523 /* yHeight is expressed in twips. A twip is 1/20 of a font's point
\r
4524 * size. This was undocumented in the version of MSVC++ that I had
\r
4525 * when I wrote the code, but is apparently documented now.
\r
4527 cfmt.yHeight = (int)(f->mfp.pointSize * 20.0 + 0.5);
\r
4528 cfmt.bCharSet = f->lf.lfCharSet;
\r
4529 cfmt.bPitchAndFamily = f->lf.lfPitchAndFamily;
\r
4530 SendMessage(hText, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &cfmt);
\r
4531 SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &cfmt);
\r
4532 /* Why are the following seemingly needed too? */
\r
4533 SendMessage(hText, EM_SETCHARFORMAT, SCF_DEFAULT, (LPARAM) &cfmt);
\r
4534 SendMessage(hInput, EM_SETCHARFORMAT, SCF_DEFAULT, (LPARAM) &cfmt);
\r
4535 SendMessage(hText, EM_EXGETSEL, 0, (LPARAM)&sel);
\r
4537 tmpsel.cpMax = -1; /*999999?*/
\r
4538 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&tmpsel);
\r
4539 SendMessage(hText, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cfmt);
\r
4540 /* Trying putting this here too. It still seems to tickle a RichEdit
\r
4541 * bug: sometimes RichEdit indents the first line of a paragraph too.
\r
4543 paraf.cbSize = sizeof(paraf);
\r
4544 paraf.dwMask = PFM_OFFSET | PFM_STARTINDENT;
\r
4545 paraf.dxStartIndent = 0;
\r
4546 paraf.dxOffset = WRAP_INDENT;
\r
4547 SendMessage(hText, EM_SETPARAFORMAT, 0, (LPARAM) ¶f);
\r
4548 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
4549 UpdateICSWidth(hText);
\r
4552 /*---------------------------------------------------------------------------*\
\r
4554 * Window Proc for main window
\r
4556 \*---------------------------------------------------------------------------*/
\r
4558 /* Process messages for main window, etc. */
\r
4560 WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
\r
4563 int wmId, wmEvent;
\r
4567 char fileTitle[MSG_SIZ];
\r
4568 char buf[MSG_SIZ];
\r
4569 static SnapData sd;
\r
4571 switch (message) {
\r
4573 case WM_PAINT: /* message: repaint portion of window */
\r
4577 case WM_ERASEBKGND:
\r
4578 if (IsIconic(hwnd)) {
\r
4579 /* Cheat; change the message */
\r
4580 return (DefWindowProc(hwnd, WM_ICONERASEBKGND, wParam, lParam));
\r
4582 return (DefWindowProc(hwnd, message, wParam, lParam));
\r
4586 case WM_LBUTTONDOWN:
\r
4587 case WM_MBUTTONDOWN:
\r
4588 case WM_RBUTTONDOWN:
\r
4589 case WM_LBUTTONUP:
\r
4590 case WM_MBUTTONUP:
\r
4591 case WM_RBUTTONUP:
\r
4592 case WM_MOUSEMOVE:
\r
4593 case WM_MOUSEWHEEL:
\r
4594 MouseEvent(hwnd, message, wParam, lParam);
\r
4597 JAWS_KB_NAVIGATION
\r
4601 JAWS_ALT_INTERCEPT
\r
4603 if (appData.icsActive && ((char)wParam == '\r' || (char)wParam > ' ' && !((char)wParam >= '1' && (char)wParam <= '9'))) {
\r
4604 // [HGM] movenum: for non-zero digits we always do type-in dialog
\r
4605 HWND h = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
4606 if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);
\r
4608 SendMessage(h, message, wParam, lParam);
\r
4609 } else if(lParam != KF_REPEAT) {
\r
4610 if (isalpha((char)wParam) || isdigit((char)wParam)) {
\r
4611 TypeInEvent((char)wParam);
\r
4612 } else if((char)wParam == 003) CopyGameToClipboard();
\r
4613 else if((char)wParam == 026) PasteGameOrFENFromClipboard();
\r
4618 case WM_PALETTECHANGED:
\r
4619 if (hwnd != (HWND)wParam && !appData.monoMode) {
\r
4621 HDC hdc = GetDC(hwndMain);
\r
4622 SelectPalette(hdc, hPal, TRUE);
\r
4623 nnew = RealizePalette(hdc);
\r
4625 paletteChanged = TRUE;
\r
4626 InvalidateRect(hwnd, &boardRect, FALSE);
\r
4628 ReleaseDC(hwnd, hdc);
\r
4632 case WM_QUERYNEWPALETTE:
\r
4633 if (!appData.monoMode /*&& paletteChanged*/) {
\r
4635 HDC hdc = GetDC(hwndMain);
\r
4636 paletteChanged = FALSE;
\r
4637 SelectPalette(hdc, hPal, FALSE);
\r
4638 nnew = RealizePalette(hdc);
\r
4640 InvalidateRect(hwnd, &boardRect, FALSE);
\r
4642 ReleaseDC(hwnd, hdc);
\r
4647 case WM_COMMAND: /* message: command from application menu */
\r
4648 wmId = LOWORD(wParam);
\r
4649 wmEvent = HIWORD(wParam);
\r
4654 SAY("new game enter a move to play against the computer with white");
\r
4657 case IDM_NewGameFRC:
\r
4658 if( NewGameFRC() == 0 ) {
\r
4663 case IDM_NewVariant:
\r
4664 NewVariantPopup(hwnd);
\r
4667 case IDM_LoadGame:
\r
4668 LoadGameDialog(hwnd, _("Load Game from File"));
\r
4671 case IDM_LoadNextGame:
\r
4675 case IDM_LoadPrevGame:
\r
4679 case IDM_ReloadGame:
\r
4683 case IDM_LoadPosition:
\r
4684 if (gameMode == AnalyzeMode || gameMode == AnalyzeFile) {
\r
4685 Reset(FALSE, TRUE);
\r
4688 f = OpenFileDialog(hwnd, "rb", "",
\r
4689 appData.oldSaveStyle ? "pos" : "fen",
\r
4691 _("Load Position from File"), &number, fileTitle, NULL);
\r
4693 LoadPosition(f, number, fileTitle);
\r
4697 case IDM_LoadNextPosition:
\r
4698 ReloadPosition(1);
\r
4701 case IDM_LoadPrevPosition:
\r
4702 ReloadPosition(-1);
\r
4705 case IDM_ReloadPosition:
\r
4706 ReloadPosition(0);
\r
4709 case IDM_SaveGame:
\r
4710 defName = DefaultFileName(appData.oldSaveStyle ? "gam" : "pgn");
\r
4711 f = OpenFileDialog(hwnd, "a", defName,
\r
4712 appData.oldSaveStyle ? "gam" : "pgn",
\r
4714 _("Save Game to File"), NULL, fileTitle, NULL);
\r
4716 SaveGame(f, 0, "");
\r
4720 case IDM_SavePosition:
\r
4721 defName = DefaultFileName(appData.oldSaveStyle ? "pos" : "fen");
\r
4722 f = OpenFileDialog(hwnd, "a", defName,
\r
4723 appData.oldSaveStyle ? "pos" : "fen",
\r
4725 _("Save Position to File"), NULL, fileTitle, NULL);
\r
4727 SavePosition(f, 0, "");
\r
4731 case IDM_SaveDiagram:
\r
4732 defName = "diagram";
\r
4733 f = OpenFileDialog(hwnd, "wb", defName,
\r
4736 _("Save Diagram to File"), NULL, fileTitle, NULL);
\r
4742 case IDM_CopyGame:
\r
4743 CopyGameToClipboard();
\r
4746 case IDM_PasteGame:
\r
4747 PasteGameFromClipboard();
\r
4750 case IDM_CopyGameListToClipboard:
\r
4751 CopyGameListToClipboard();
\r
4754 /* [AS] Autodetect FEN or PGN data */
\r
4755 case IDM_PasteAny:
\r
4756 PasteGameOrFENFromClipboard();
\r
4759 /* [AS] Move history */
\r
4760 case IDM_ShowMoveHistory:
\r
4761 if( MoveHistoryIsUp() ) {
\r
4762 MoveHistoryPopDown();
\r
4765 MoveHistoryPopUp();
\r
4769 /* [AS] Eval graph */
\r
4770 case IDM_ShowEvalGraph:
\r
4771 if( EvalGraphIsUp() ) {
\r
4772 EvalGraphPopDown();
\r
4776 SetFocus(hwndMain);
\r
4780 /* [AS] Engine output */
\r
4781 case IDM_ShowEngineOutput:
\r
4782 if( EngineOutputIsUp() ) {
\r
4783 EngineOutputPopDown();
\r
4786 EngineOutputPopUp();
\r
4790 /* [AS] User adjudication */
\r
4791 case IDM_UserAdjudication_White:
\r
4792 UserAdjudicationEvent( +1 );
\r
4795 case IDM_UserAdjudication_Black:
\r
4796 UserAdjudicationEvent( -1 );
\r
4799 case IDM_UserAdjudication_Draw:
\r
4800 UserAdjudicationEvent( 0 );
\r
4803 /* [AS] Game list options dialog */
\r
4804 case IDM_GameListOptions:
\r
4805 GameListOptions();
\r
4812 case IDM_CopyPosition:
\r
4813 CopyFENToClipboard();
\r
4816 case IDM_PastePosition:
\r
4817 PasteFENFromClipboard();
\r
4820 case IDM_MailMove:
\r
4824 case IDM_ReloadCMailMsg:
\r
4825 Reset(TRUE, TRUE);
\r
4826 ReloadCmailMsgEvent(FALSE);
\r
4829 case IDM_Minimize:
\r
4830 ShowWindow(hwnd, SW_MINIMIZE);
\r
4837 case IDM_MachineWhite:
\r
4838 MachineWhiteEvent();
\r
4840 * refresh the tags dialog only if it's visible
\r
4842 if (gameMode == MachinePlaysWhite && IsWindowVisible(editTagsDialog)) {
\r
4844 tags = PGNTags(&gameInfo);
\r
4845 TagsPopUp(tags, CmailMsg());
\r
4848 SAY("computer starts playing white");
\r
4851 case IDM_MachineBlack:
\r
4852 MachineBlackEvent();
\r
4854 * refresh the tags dialog only if it's visible
\r
4856 if (gameMode == MachinePlaysBlack && IsWindowVisible(editTagsDialog)) {
\r
4858 tags = PGNTags(&gameInfo);
\r
4859 TagsPopUp(tags, CmailMsg());
\r
4862 SAY("computer starts playing black");
\r
4865 case IDM_Match: // [HGM] match: flows into next case, after setting Match Mode and nr of Games
\r
4866 MatchEvent(2); // distinguish from command-line-triggered case (matchMode=1)
\r
4869 case IDM_TwoMachines:
\r
4870 TwoMachinesEvent();
\r
4872 * refresh the tags dialog only if it's visible
\r
4874 if (gameMode == TwoMachinesPlay && IsWindowVisible(editTagsDialog)) {
\r
4876 tags = PGNTags(&gameInfo);
\r
4877 TagsPopUp(tags, CmailMsg());
\r
4880 SAY("computer starts playing both sides");
\r
4883 case IDM_AnalysisMode:
\r
4884 if (!first.analysisSupport) {
\r
4885 snprintf(buf, MSG_SIZ, _("%s does not support analysis"), first.tidy);
\r
4886 DisplayError(buf, 0);
\r
4888 SAY("analyzing current position");
\r
4889 /* [DM] icsEngineAnlyze [HGM] Why is this front-end??? */
\r
4890 if (appData.icsActive) {
\r
4891 if (gameMode != IcsObserving) {
\r
4892 snprintf(buf, MSG_SIZ, "You are not observing a game");
\r
4893 DisplayError(buf, 0);
\r
4894 /* secure check */
\r
4895 if (appData.icsEngineAnalyze) {
\r
4896 if (appData.debugMode)
\r
4897 fprintf(debugFP, "Found unexpected active ICS engine analyze \n");
\r
4898 ExitAnalyzeMode();
\r
4904 /* if enable, user want disable icsEngineAnalyze */
\r
4905 if (appData.icsEngineAnalyze) {
\r
4906 ExitAnalyzeMode();
\r
4910 appData.icsEngineAnalyze = TRUE;
\r
4911 if (appData.debugMode) fprintf(debugFP, "ICS engine analyze starting...\n");
\r
4914 if (!appData.showThinking) ToggleShowThinking();
\r
4915 AnalyzeModeEvent();
\r
4919 case IDM_AnalyzeFile:
\r
4920 if (!first.analysisSupport) {
\r
4921 char buf[MSG_SIZ];
\r
4922 snprintf(buf, MSG_SIZ, _("%s does not support analysis"), first.tidy);
\r
4923 DisplayError(buf, 0);
\r
4925 if (!appData.showThinking) ToggleShowThinking();
\r
4926 AnalyzeFileEvent();
\r
4927 LoadGameDialog(hwnd, _("Analyze Game from File"));
\r
4928 AnalysisPeriodicEvent(1);
\r
4932 case IDM_IcsClient:
\r
4936 case IDM_EditGame:
\r
4937 case IDM_EditGame2:
\r
4942 case IDM_EditPosition:
\r
4943 case IDM_EditPosition2:
\r
4944 EditPositionEvent();
\r
4945 SAY("enter a FEN string or setup a position on the board using the control R pop up menu");
\r
4948 case IDM_Training:
\r
4952 case IDM_ShowGameList:
\r
4953 ShowGameListProc();
\r
4956 case IDM_EditProgs1:
\r
4957 EditTagsPopUp(firstChessProgramNames, &firstChessProgramNames);
\r
4960 case IDM_EditProgs2:
\r
4961 LoadEnginePopUp(hwndMain);
\r
4962 // EditTagsPopUp(secondChessProgramNames, &secondChessProgramNames);
\r
4965 case IDM_EditServers:
\r
4966 EditTagsPopUp(icsNames, &icsNames);
\r
4969 case IDM_EditTags:
\r
4974 case IDM_EditBook:
\r
4978 case IDM_EditComment:
\r
4980 if (commentUp && editComment) {
\r
4983 EditCommentEvent();
\r
5003 case IDM_CallFlag:
\r
5023 case IDM_StopObserving:
\r
5024 StopObservingEvent();
\r
5027 case IDM_StopExamining:
\r
5028 StopExaminingEvent();
\r
5032 UploadGameEvent();
\r
5035 case IDM_TypeInMove:
\r
5036 TypeInEvent('\000');
\r
5039 case IDM_TypeInName:
\r
5040 PopUpNameDialog('\000');
\r
5043 case IDM_Backward:
\r
5045 SetFocus(hwndMain);
\r
5052 SetFocus(hwndMain);
\r
5057 SetFocus(hwndMain);
\r
5062 SetFocus(hwndMain);
\r
5066 RevertEvent(FALSE);
\r
5069 case IDM_Annotate: // [HGM] vari: revert with annotation
\r
5070 RevertEvent(TRUE);
\r
5073 case IDM_TruncateGame:
\r
5074 TruncateGameEvent();
\r
5081 case IDM_RetractMove:
\r
5082 RetractMoveEvent();
\r
5085 case IDM_FlipView:
\r
5086 flipView = !flipView;
\r
5087 DrawPosition(FALSE, NULL);
\r
5090 case IDM_FlipClock:
\r
5091 flipClock = !flipClock;
\r
5092 DisplayBothClocks();
\r
5096 case IDM_MuteSounds:
\r
5097 mute = !mute; // [HGM] mute: keep track of global muting variable
\r
5098 CheckMenuItem(GetMenu(hwndMain),IDM_MuteSounds,
\r
5099 MF_BYCOMMAND|(mute?MF_CHECKED:MF_UNCHECKED));
\r
5102 case IDM_GeneralOptions:
\r
5103 GeneralOptionsPopup(hwnd);
\r
5104 DrawPosition(TRUE, NULL);
\r
5107 case IDM_BoardOptions:
\r
5108 BoardOptionsPopup(hwnd);
\r
5111 case IDM_EnginePlayOptions:
\r
5112 EnginePlayOptionsPopup(hwnd);
\r
5115 case IDM_Engine1Options:
\r
5116 EngineOptionsPopup(hwnd, &first);
\r
5119 case IDM_Engine2Options:
\r
5121 if(WaitForEngine(&second, SettingsMenuIfReady)) break;
\r
5122 EngineOptionsPopup(hwnd, &second);
\r
5125 case IDM_OptionsUCI:
\r
5126 UciOptionsPopup(hwnd);
\r
5130 TourneyPopup(hwnd);
\r
5133 case IDM_IcsOptions:
\r
5134 IcsOptionsPopup(hwnd);
\r
5138 FontsOptionsPopup(hwnd);
\r
5142 SoundOptionsPopup(hwnd);
\r
5145 case IDM_CommPort:
\r
5146 CommPortOptionsPopup(hwnd);
\r
5149 case IDM_LoadOptions:
\r
5150 LoadOptionsPopup(hwnd);
\r
5153 case IDM_SaveOptions:
\r
5154 SaveOptionsPopup(hwnd);
\r
5157 case IDM_TimeControl:
\r
5158 TimeControlOptionsPopup(hwnd);
\r
5161 case IDM_SaveSettings:
\r
5162 SaveSettings(settingsFileName);
\r
5165 case IDM_SaveSettingsOnExit:
\r
5166 saveSettingsOnExit = !saveSettingsOnExit;
\r
5167 (void) CheckMenuItem(GetMenu(hwndMain), IDM_SaveSettingsOnExit,
\r
5168 MF_BYCOMMAND|(saveSettingsOnExit ?
\r
5169 MF_CHECKED : MF_UNCHECKED));
\r
5180 case IDM_AboutGame:
\r
5185 appData.debugMode = !appData.debugMode;
\r
5186 if (appData.debugMode) {
\r
5187 char dir[MSG_SIZ];
\r
5188 GetCurrentDirectory(MSG_SIZ, dir);
\r
5189 SetCurrentDirectory(installDir);
\r
5190 debugFP = fopen(appData.nameOfDebugFile, "w");
\r
5191 SetCurrentDirectory(dir);
\r
5192 setbuf(debugFP, NULL);
\r
5199 case IDM_HELPCONTENTS:
\r
5200 if (!MyHelp (hwnd, "winboard.hlp", HELP_KEY,(DWORD)(LPSTR)"CONTENTS") &&
\r
5201 !HtmlHelp(hwnd, "winboard.chm", 0, 0) ) {
\r
5202 MessageBox (GetFocus(),
\r
5203 _("Unable to activate help"),
\r
5204 szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);
\r
5208 case IDM_HELPSEARCH:
\r
5209 if (!MyHelp (hwnd, "winboard.hlp", HELP_PARTIALKEY, (DWORD)(LPSTR)"") &&
\r
5210 !HtmlHelp(hwnd, "winboard.chm", 0, 0) ) {
\r
5211 MessageBox (GetFocus(),
\r
5212 _("Unable to activate help"),
\r
5213 szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);
\r
5217 case IDM_HELPHELP:
\r
5218 if(!WinHelp(hwnd, (LPSTR)NULL, HELP_HELPONHELP, 0)) {
\r
5219 MessageBox (GetFocus(),
\r
5220 _("Unable to activate help"),
\r
5221 szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);
\r
5226 lpProc = MakeProcInstance((FARPROC)About, hInst);
\r
5228 (gameInfo.event && strcmp(gameInfo.event, "Easter Egg Hunt") == 0) ?
\r
5229 "AboutBox2" : "AboutBox", hwnd, (DLGPROC)lpProc);
\r
5230 FreeProcInstance(lpProc);
\r
5233 case IDM_DirectCommand1:
\r
5234 AskQuestionEvent(_("Direct Command"),
\r
5235 _("Send to chess program:"), "", "1");
\r
5237 case IDM_DirectCommand2:
\r
5238 AskQuestionEvent(_("Direct Command"),
\r
5239 _("Send to second chess program:"), "", "2");
\r
5242 case EP_WhitePawn:
\r
5243 EditPositionMenuEvent(WhitePawn, fromX, fromY);
\r
5244 fromX = fromY = -1;
\r
5247 case EP_WhiteKnight:
\r
5248 EditPositionMenuEvent(WhiteKnight, fromX, fromY);
\r
5249 fromX = fromY = -1;
\r
5252 case EP_WhiteBishop:
\r
5253 EditPositionMenuEvent(WhiteBishop, fromX, fromY);
\r
5254 fromX = fromY = -1;
\r
5257 case EP_WhiteRook:
\r
5258 EditPositionMenuEvent(WhiteRook, fromX, fromY);
\r
5259 fromX = fromY = -1;
\r
5262 case EP_WhiteQueen:
\r
5263 EditPositionMenuEvent(WhiteQueen, fromX, fromY);
\r
5264 fromX = fromY = -1;
\r
5267 case EP_WhiteFerz:
\r
5268 EditPositionMenuEvent(WhiteFerz, fromX, fromY);
\r
5269 fromX = fromY = -1;
\r
5272 case EP_WhiteWazir:
\r
5273 EditPositionMenuEvent(WhiteWazir, fromX, fromY);
\r
5274 fromX = fromY = -1;
\r
5277 case EP_WhiteAlfil:
\r
5278 EditPositionMenuEvent(WhiteAlfil, fromX, fromY);
\r
5279 fromX = fromY = -1;
\r
5282 case EP_WhiteCannon:
\r
5283 EditPositionMenuEvent(WhiteCannon, fromX, fromY);
\r
5284 fromX = fromY = -1;
\r
5287 case EP_WhiteCardinal:
\r
5288 EditPositionMenuEvent(WhiteAngel, fromX, fromY);
\r
5289 fromX = fromY = -1;
\r
5292 case EP_WhiteMarshall:
\r
5293 EditPositionMenuEvent(WhiteMarshall, fromX, fromY);
\r
5294 fromX = fromY = -1;
\r
5297 case EP_WhiteKing:
\r
5298 EditPositionMenuEvent(WhiteKing, fromX, fromY);
\r
5299 fromX = fromY = -1;
\r
5302 case EP_BlackPawn:
\r
5303 EditPositionMenuEvent(BlackPawn, fromX, fromY);
\r
5304 fromX = fromY = -1;
\r
5307 case EP_BlackKnight:
\r
5308 EditPositionMenuEvent(BlackKnight, fromX, fromY);
\r
5309 fromX = fromY = -1;
\r
5312 case EP_BlackBishop:
\r
5313 EditPositionMenuEvent(BlackBishop, fromX, fromY);
\r
5314 fromX = fromY = -1;
\r
5317 case EP_BlackRook:
\r
5318 EditPositionMenuEvent(BlackRook, fromX, fromY);
\r
5319 fromX = fromY = -1;
\r
5322 case EP_BlackQueen:
\r
5323 EditPositionMenuEvent(BlackQueen, fromX, fromY);
\r
5324 fromX = fromY = -1;
\r
5327 case EP_BlackFerz:
\r
5328 EditPositionMenuEvent(BlackFerz, fromX, fromY);
\r
5329 fromX = fromY = -1;
\r
5332 case EP_BlackWazir:
\r
5333 EditPositionMenuEvent(BlackWazir, fromX, fromY);
\r
5334 fromX = fromY = -1;
\r
5337 case EP_BlackAlfil:
\r
5338 EditPositionMenuEvent(BlackAlfil, fromX, fromY);
\r
5339 fromX = fromY = -1;
\r
5342 case EP_BlackCannon:
\r
5343 EditPositionMenuEvent(BlackCannon, fromX, fromY);
\r
5344 fromX = fromY = -1;
\r
5347 case EP_BlackCardinal:
\r
5348 EditPositionMenuEvent(BlackAngel, fromX, fromY);
\r
5349 fromX = fromY = -1;
\r
5352 case EP_BlackMarshall:
\r
5353 EditPositionMenuEvent(BlackMarshall, fromX, fromY);
\r
5354 fromX = fromY = -1;
\r
5357 case EP_BlackKing:
\r
5358 EditPositionMenuEvent(BlackKing, fromX, fromY);
\r
5359 fromX = fromY = -1;
\r
5362 case EP_EmptySquare:
\r
5363 EditPositionMenuEvent(EmptySquare, fromX, fromY);
\r
5364 fromX = fromY = -1;
\r
5367 case EP_ClearBoard:
\r
5368 EditPositionMenuEvent(ClearBoard, fromX, fromY);
\r
5369 fromX = fromY = -1;
\r
5373 EditPositionMenuEvent(WhitePlay, fromX, fromY);
\r
5374 fromX = fromY = -1;
\r
5378 EditPositionMenuEvent(BlackPlay, fromX, fromY);
\r
5379 fromX = fromY = -1;
\r
5383 EditPositionMenuEvent(PromotePiece, fromX, fromY);
\r
5384 fromX = fromY = -1;
\r
5388 EditPositionMenuEvent(DemotePiece, fromX, fromY);
\r
5389 fromX = fromY = -1;
\r
5393 DropMenuEvent(WhitePawn, fromX, fromY);
\r
5394 fromX = fromY = -1;
\r
5398 DropMenuEvent(WhiteKnight, fromX, fromY);
\r
5399 fromX = fromY = -1;
\r
5403 DropMenuEvent(WhiteBishop, fromX, fromY);
\r
5404 fromX = fromY = -1;
\r
5408 DropMenuEvent(WhiteRook, fromX, fromY);
\r
5409 fromX = fromY = -1;
\r
5413 DropMenuEvent(WhiteQueen, fromX, fromY);
\r
5414 fromX = fromY = -1;
\r
5418 barbaric = 0; appData.language = "";
\r
5419 TranslateMenus(0);
\r
5420 CheckMenuItem(GetMenu(hwndMain), lastChecked, MF_BYCOMMAND|MF_UNCHECKED);
\r
5421 CheckMenuItem(GetMenu(hwndMain), IDM_English, MF_BYCOMMAND|MF_CHECKED);
\r
5422 lastChecked = wmId;
\r
5426 if(wmId > IDM_English && wmId < IDM_English+20) {
\r
5427 LoadLanguageFile(languageFile[wmId - IDM_English - 1]);
\r
5428 TranslateMenus(0);
\r
5429 CheckMenuItem(GetMenu(hwndMain), lastChecked, MF_BYCOMMAND|MF_UNCHECKED);
\r
5430 CheckMenuItem(GetMenu(hwndMain), wmId, MF_BYCOMMAND|MF_CHECKED);
\r
5431 lastChecked = wmId;
\r
5434 return (DefWindowProc(hwnd, message, wParam, lParam));
\r
5440 case CLOCK_TIMER_ID:
\r
5441 KillTimer(hwnd, clockTimerEvent); /* Simulate one-shot timer as in X */
\r
5442 clockTimerEvent = 0;
\r
5443 DecrementClocks(); /* call into back end */
\r
5445 case LOAD_GAME_TIMER_ID:
\r
5446 KillTimer(hwnd, loadGameTimerEvent); /* Simulate one-shot timer as in X*/
\r
5447 loadGameTimerEvent = 0;
\r
5448 AutoPlayGameLoop(); /* call into back end */
\r
5450 case ANALYSIS_TIMER_ID:
\r
5451 if ((gameMode == AnalyzeMode || gameMode == AnalyzeFile
\r
5452 || appData.icsEngineAnalyze) && appData.periodicUpdates) {
\r
5453 AnalysisPeriodicEvent(0);
\r
5455 KillTimer(hwnd, analysisTimerEvent);
\r
5456 analysisTimerEvent = 0;
\r
5459 case DELAYED_TIMER_ID:
\r
5460 KillTimer(hwnd, delayedTimerEvent);
\r
5461 delayedTimerEvent = 0;
\r
5462 delayedTimerCallback();
\r
5467 case WM_USER_Input:
\r
5468 InputEvent(hwnd, message, wParam, lParam);
\r
5471 /* [AS] Also move "attached" child windows */
\r
5472 case WM_WINDOWPOSCHANGING:
\r
5474 if( hwnd == hwndMain && appData.useStickyWindows ) {
\r
5475 LPWINDOWPOS lpwp = (LPWINDOWPOS) lParam;
\r
5477 if( ((lpwp->flags & SWP_NOMOVE) == 0) && ((lpwp->flags & SWP_NOSIZE) != 0) ) {
\r
5478 /* Window is moving */
\r
5481 // GetWindowRect( hwnd, &rcMain ); //[HGM] sticky: in XP this returned new position, not old
\r
5482 rcMain.left = wpMain.x; // replace by these 4 lines to reconstruct old rect
\r
5483 rcMain.right = wpMain.x + wpMain.width;
\r
5484 rcMain.top = wpMain.y;
\r
5485 rcMain.bottom = wpMain.y + wpMain.height;
\r
5487 ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, moveHistoryDialog, &wpMoveHistory );
\r
5488 ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, evalGraphDialog, &wpEvalGraph );
\r
5489 ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, engineOutputDialog, &wpEngineOutput );
\r
5490 ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, gameListDialog, &wpGameList );
\r
5491 ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, hwndConsole, &wpConsole );
\r
5492 wpMain.x = lpwp->x;
\r
5493 wpMain.y = lpwp->y;
\r
5498 /* [AS] Snapping */
\r
5499 case WM_ENTERSIZEMOVE:
\r
5500 if(appData.debugMode) { fprintf(debugFP, "size-move\n"); }
\r
5501 if (hwnd == hwndMain) {
\r
5502 doingSizing = TRUE;
\r
5505 return OnEnterSizeMove( &sd, hwnd, wParam, lParam );
\r
5509 if(appData.debugMode) { fprintf(debugFP, "sizing\n"); }
\r
5510 if (hwnd == hwndMain) {
\r
5511 lastSizing = wParam;
\r
5516 if(appData.debugMode) { fprintf(debugFP, "moving\n"); }
\r
5517 return OnMoving( &sd, hwnd, wParam, lParam );
\r
5519 case WM_EXITSIZEMOVE:
\r
5520 if(appData.debugMode) { fprintf(debugFP, "exit size-move, size = %d\n", squareSize); }
\r
5521 if (hwnd == hwndMain) {
\r
5523 doingSizing = FALSE;
\r
5524 InvalidateRect(hwnd, &boardRect, FALSE);
\r
5525 GetClientRect(hwnd, &client);
\r
5526 ResizeBoard(client.right, client.bottom, lastSizing);
\r
5528 if(appData.debugMode) { fprintf(debugFP, "square size = %d\n", squareSize); }
\r
5530 return OnExitSizeMove( &sd, hwnd, wParam, lParam );
\r
5533 case WM_DESTROY: /* message: window being destroyed */
\r
5534 PostQuitMessage(0);
\r
5538 if (hwnd == hwndMain) {
\r
5543 default: /* Passes it on if unprocessed */
\r
5544 return (DefWindowProc(hwnd, message, wParam, lParam));
\r
5549 /*---------------------------------------------------------------------------*\
\r
5551 * Misc utility routines
\r
5553 \*---------------------------------------------------------------------------*/
\r
5556 * Decent random number generator, at least not as bad as Windows
\r
5557 * standard rand, which returns a value in the range 0 to 0x7fff.
\r
5559 unsigned int randstate;
\r
5564 randstate = randstate * 1664525 + 1013904223;
\r
5565 return (int) randstate & 0x7fffffff;
\r
5569 mysrandom(unsigned int seed)
\r
5576 * returns TRUE if user selects a different color, FALSE otherwise
\r
5580 ChangeColor(HWND hwnd, COLORREF *which)
\r
5582 static BOOL firstTime = TRUE;
\r
5583 static DWORD customColors[16];
\r
5585 COLORREF newcolor;
\r
5590 /* Make initial colors in use available as custom colors */
\r
5591 /* Should we put the compiled-in defaults here instead? */
\r
5593 customColors[i++] = lightSquareColor & 0xffffff;
\r
5594 customColors[i++] = darkSquareColor & 0xffffff;
\r
5595 customColors[i++] = whitePieceColor & 0xffffff;
\r
5596 customColors[i++] = blackPieceColor & 0xffffff;
\r
5597 customColors[i++] = highlightSquareColor & 0xffffff;
\r
5598 customColors[i++] = premoveHighlightColor & 0xffffff;
\r
5600 for (ccl = (ColorClass) 0; ccl < NColorClasses && i < 16; ccl++) {
\r
5601 customColors[i++] = textAttribs[ccl].color;
\r
5603 while (i < 16) customColors[i++] = RGB(255, 255, 255);
\r
5604 firstTime = FALSE;
\r
5607 cc.lStructSize = sizeof(cc);
\r
5608 cc.hwndOwner = hwnd;
\r
5609 cc.hInstance = NULL;
\r
5610 cc.rgbResult = (DWORD) (*which & 0xffffff);
\r
5611 cc.lpCustColors = (LPDWORD) customColors;
\r
5612 cc.Flags = CC_RGBINIT|CC_FULLOPEN;
\r
5614 if (!ChooseColor(&cc)) return FALSE;
\r
5616 newcolor = (COLORREF) (0x2000000 | cc.rgbResult);
\r
5617 if (newcolor == *which) return FALSE;
\r
5618 *which = newcolor;
\r
5622 InitDrawingColors();
\r
5623 InvalidateRect(hwnd, &boardRect, FALSE);
\r
5628 MyLoadSound(MySound *ms)
\r
5634 if (ms->data && ms->flag) free(ms->data);
\r
5637 switch (ms->name[0]) {
\r
5643 /* System sound from Control Panel. Don't preload here. */
\r
5647 if (ms->name[1] == NULLCHAR) {
\r
5648 /* "!" alone = silence */
\r
5651 /* Builtin wave resource. Error if not found. */
\r
5652 HANDLE h = FindResource(hInst, ms->name + 1, "WAVE");
\r
5653 if (h == NULL) break;
\r
5654 ms->data = (void *)LoadResource(hInst, h);
\r
5655 ms->flag = 0; // not maloced, so cannot be freed!
\r
5656 if (h == NULL) break;
\r
5661 /* .wav file. Error if not found. */
\r
5662 f = fopen(ms->name, "rb");
\r
5663 if (f == NULL) break;
\r
5664 if (fstat(fileno(f), &st) < 0) break;
\r
5665 ms->data = malloc(st.st_size);
\r
5667 if (fread(ms->data, st.st_size, 1, f) < 1) break;
\r
5673 char buf[MSG_SIZ];
\r
5674 snprintf(buf, MSG_SIZ, _("Error loading sound %s"), ms->name);
\r
5675 DisplayError(buf, GetLastError());
\r
5681 MyPlaySound(MySound *ms)
\r
5683 BOOLEAN ok = FALSE;
\r
5685 if(mute) return TRUE; // [HGM] mute: suppress all sound play when muted
\r
5686 switch (ms->name[0]) {
\r
5688 if(appData.debugMode) fprintf(debugFP, "silence\n");
\r
5693 /* System sound from Control Panel (deprecated feature).
\r
5694 "$" alone or an unset sound name gets default beep (still in use). */
\r
5695 if (ms->name[1]) {
\r
5696 ok = PlaySound(ms->name + 1, NULL, SND_ALIAS|SND_ASYNC);
\r
5698 if (!ok) ok = MessageBeep(MB_OK);
\r
5701 /* Builtin wave resource, or "!" alone for silence */
\r
5702 if (ms->name[1]) {
\r
5703 if (ms->data == NULL) return FALSE;
\r
5704 ok = PlaySound(ms->data, NULL, SND_MEMORY|SND_ASYNC);
\r
5710 /* .wav file. Error if not found. */
\r
5711 if (ms->data == NULL) return FALSE;
\r
5712 ok = PlaySound(ms->data, NULL, SND_MEMORY|SND_ASYNC);
\r
5715 /* Don't print an error: this can happen innocently if the sound driver
\r
5716 is busy; for instance, if another instance of WinBoard is playing
\r
5717 a sound at about the same time. */
\r
5723 OldOpenFileHook(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
5726 OPENFILENAME *ofn;
\r
5727 static UINT *number; /* gross that this is static */
\r
5729 switch (message) {
\r
5730 case WM_INITDIALOG: /* message: initialize dialog box */
\r
5731 /* Center the dialog over the application window */
\r
5732 ofn = (OPENFILENAME *) lParam;
\r
5733 if (ofn->Flags & OFN_ENABLETEMPLATE) {
\r
5734 number = (UINT *) ofn->lCustData;
\r
5735 SendMessage(GetDlgItem(hDlg, edt2), WM_SETTEXT, 0, (LPARAM) "");
\r
5739 CenterWindow (hDlg, GetWindow (hDlg, GW_OWNER));
\r
5740 Translate(hDlg, 1536);
\r
5741 return FALSE; /* Allow for further processing */
\r
5744 if ((LOWORD(wParam) == IDOK) && (number != NULL)) {
\r
5745 *number = GetDlgItemInt(hDlg, OPT_IndexNumberOld, &ok, FALSE);
\r
5747 return FALSE; /* Allow for further processing */
\r
5753 OpenFileHook(HWND hdlg, UINT uiMsg, WPARAM wParam, LPARAM lParam)
\r
5755 static UINT *number;
\r
5756 OPENFILENAME *ofname;
\r
5759 case WM_INITDIALOG:
\r
5760 Translate(hdlg, DLG_IndexNumber);
\r
5761 ofname = (OPENFILENAME *)lParam;
\r
5762 number = (UINT *)(ofname->lCustData);
\r
5765 ofnot = (OFNOTIFY *)lParam;
\r
5766 if (ofnot->hdr.code == CDN_FILEOK) {
\r
5767 *number = GetDlgItemInt(hdlg, OPT_IndexNumber, NULL, FALSE);
\r
5776 OpenFileDialog(HWND hwnd, char *write, char *defName, char *defExt, // [HGM] diag: type of 'write' now string
\r
5777 char *nameFilt, char *dlgTitle, UINT *number,
\r
5778 char fileTitle[MSG_SIZ], char fileName[MSG_SIZ])
\r
5780 OPENFILENAME openFileName;
\r
5781 char buf1[MSG_SIZ];
\r
5784 if (fileName == NULL) fileName = buf1;
\r
5785 if (defName == NULL) {
\r
5786 safeStrCpy(fileName, "*.", 3 );
\r
5787 strcat(fileName, defExt);
\r
5789 safeStrCpy(fileName, defName, MSG_SIZ );
\r
5791 if (fileTitle) safeStrCpy(fileTitle, "", MSG_SIZ );
\r
5792 if (number) *number = 0;
\r
5794 openFileName.lStructSize = sizeof(OPENFILENAME);
\r
5795 openFileName.hwndOwner = hwnd;
\r
5796 openFileName.hInstance = (HANDLE) hInst;
\r
5797 openFileName.lpstrFilter = nameFilt;
\r
5798 openFileName.lpstrCustomFilter = (LPSTR) NULL;
\r
5799 openFileName.nMaxCustFilter = 0L;
\r
5800 openFileName.nFilterIndex = 1L;
\r
5801 openFileName.lpstrFile = fileName;
\r
5802 openFileName.nMaxFile = MSG_SIZ;
\r
5803 openFileName.lpstrFileTitle = fileTitle;
\r
5804 openFileName.nMaxFileTitle = fileTitle ? MSG_SIZ : 0;
\r
5805 openFileName.lpstrInitialDir = NULL;
\r
5806 openFileName.lpstrTitle = dlgTitle;
\r
5807 openFileName.Flags = OFN_PATHMUSTEXIST | OFN_HIDEREADONLY
\r
5808 | (write[0] != 'r' ? 0 : OFN_FILEMUSTEXIST)
\r
5809 | (number ? OFN_ENABLETEMPLATE | OFN_ENABLEHOOK: 0)
\r
5810 | (oldDialog ? 0 : OFN_EXPLORER);
\r
5811 openFileName.nFileOffset = 0;
\r
5812 openFileName.nFileExtension = 0;
\r
5813 openFileName.lpstrDefExt = defExt;
\r
5814 openFileName.lCustData = (LONG) number;
\r
5815 openFileName.lpfnHook = oldDialog ?
\r
5816 (LPOFNHOOKPROC) OldOpenFileHook : (LPOFNHOOKPROC) OpenFileHook;
\r
5817 openFileName.lpTemplateName = (LPSTR)(oldDialog ? 1536 : DLG_IndexNumber);
\r
5819 if (write[0] != 'r' ? GetSaveFileName(&openFileName) :
\r
5820 GetOpenFileName(&openFileName)) {
\r
5821 /* open the file */
\r
5822 f = fopen(openFileName.lpstrFile, write);
\r
5824 MessageBox(hwnd, _("File open failed"), NULL,
\r
5825 MB_OK|MB_ICONEXCLAMATION);
\r
5829 int err = CommDlgExtendedError();
\r
5830 if (err != 0) DisplayError(_("Internal error in file dialog box"), err);
\r
5839 MenuPopup(HWND hwnd, POINT pt, HMENU hmenu, UINT def)
\r
5841 HMENU hmenuTrackPopup; /* floating pop-up menu */
\r
5844 * Get the first pop-up menu in the menu template. This is the
\r
5845 * menu that TrackPopupMenu displays.
\r
5847 hmenuTrackPopup = GetSubMenu(hmenu, 0);
\r
5848 TranslateOneMenu(10, hmenuTrackPopup);
\r
5850 SetMenuDefaultItem(hmenuTrackPopup, def, FALSE);
\r
5853 * TrackPopup uses screen coordinates, so convert the
\r
5854 * coordinates of the mouse click to screen coordinates.
\r
5856 ClientToScreen(hwnd, (LPPOINT) &pt);
\r
5858 /* Draw and track the floating pop-up menu. */
\r
5859 TrackPopupMenu(hmenuTrackPopup, TPM_CENTERALIGN | TPM_RIGHTBUTTON,
\r
5860 pt.x, pt.y, 0, hwnd, NULL);
\r
5862 /* Destroy the menu.*/
\r
5863 DestroyMenu(hmenu);
\r
5868 int sizeX, sizeY, newSizeX, newSizeY;
\r
5870 } ResizeEditPlusButtonsClosure;
\r
5873 ResizeEditPlusButtonsCallback(HWND hChild, LPARAM lparam)
\r
5875 ResizeEditPlusButtonsClosure *cl = (ResizeEditPlusButtonsClosure *)lparam;
\r
5879 if (hChild == cl->hText) return TRUE;
\r
5880 GetWindowRect(hChild, &rect); /* gives screen coords */
\r
5881 pt.x = rect.left + (cl->newSizeX - cl->sizeX)/2;
\r
5882 pt.y = rect.top + cl->newSizeY - cl->sizeY;
\r
5883 ScreenToClient(cl->hDlg, &pt);
\r
5884 cl->hdwp = DeferWindowPos(cl->hdwp, hChild, NULL,
\r
5885 pt.x, pt.y, rect.right - rect.left, rect.bottom - rect.top, SWP_NOZORDER);
\r
5889 /* Resize a dialog that has a (rich) edit field filling most of
\r
5890 the top, with a row of buttons below */
\r
5892 ResizeEditPlusButtons(HWND hDlg, HWND hText, int sizeX, int sizeY, int newSizeX, int newSizeY)
\r
5895 int newTextHeight, newTextWidth;
\r
5896 ResizeEditPlusButtonsClosure cl;
\r
5898 /*if (IsIconic(hDlg)) return;*/
\r
5899 if (newSizeX == sizeX && newSizeY == sizeY) return;
\r
5901 cl.hdwp = BeginDeferWindowPos(8);
\r
5903 GetWindowRect(hText, &rectText); /* gives screen coords */
\r
5904 newTextWidth = rectText.right - rectText.left + newSizeX - sizeX;
\r
5905 newTextHeight = rectText.bottom - rectText.top + newSizeY - sizeY;
\r
5906 if (newTextHeight < 0) {
\r
5907 newSizeY += -newTextHeight;
\r
5908 newTextHeight = 0;
\r
5910 cl.hdwp = DeferWindowPos(cl.hdwp, hText, NULL, 0, 0,
\r
5911 newTextWidth, newTextHeight, SWP_NOZORDER|SWP_NOMOVE);
\r
5917 cl.newSizeX = newSizeX;
\r
5918 cl.newSizeY = newSizeY;
\r
5919 EnumChildWindows(hDlg, ResizeEditPlusButtonsCallback, (LPARAM)&cl);
\r
5921 EndDeferWindowPos(cl.hdwp);
\r
5924 BOOL CenterWindowEx(HWND hwndChild, HWND hwndParent, int mode)
\r
5926 RECT rChild, rParent;
\r
5927 int wChild, hChild, wParent, hParent;
\r
5928 int wScreen, hScreen, xNew, yNew;
\r
5931 /* Get the Height and Width of the child window */
\r
5932 GetWindowRect (hwndChild, &rChild);
\r
5933 wChild = rChild.right - rChild.left;
\r
5934 hChild = rChild.bottom - rChild.top;
\r
5936 /* Get the Height and Width of the parent window */
\r
5937 GetWindowRect (hwndParent, &rParent);
\r
5938 wParent = rParent.right - rParent.left;
\r
5939 hParent = rParent.bottom - rParent.top;
\r
5941 /* Get the display limits */
\r
5942 hdc = GetDC (hwndChild);
\r
5943 wScreen = GetDeviceCaps (hdc, HORZRES);
\r
5944 hScreen = GetDeviceCaps (hdc, VERTRES);
\r
5945 ReleaseDC(hwndChild, hdc);
\r
5947 /* Calculate new X position, then adjust for screen */
\r
5948 xNew = rParent.left + ((wParent - wChild) /2);
\r
5951 } else if ((xNew+wChild) > wScreen) {
\r
5952 xNew = wScreen - wChild;
\r
5955 /* Calculate new Y position, then adjust for screen */
\r
5957 yNew = rParent.top + ((hParent - hChild) /2);
\r
5960 yNew = rParent.top + GetSystemMetrics( SM_CYCAPTION ) * 2 / 3;
\r
5965 } else if ((yNew+hChild) > hScreen) {
\r
5966 yNew = hScreen - hChild;
\r
5969 /* Set it, and return */
\r
5970 return SetWindowPos (hwndChild, NULL,
\r
5971 xNew, yNew, 0, 0, SWP_NOSIZE | SWP_NOZORDER);
\r
5974 /* Center one window over another */
\r
5975 BOOL CenterWindow (HWND hwndChild, HWND hwndParent)
\r
5977 return CenterWindowEx( hwndChild, hwndParent, 0 );
\r
5980 /*---------------------------------------------------------------------------*\
\r
5982 * Startup Dialog functions
\r
5984 \*---------------------------------------------------------------------------*/
\r
5986 InitComboStrings(HANDLE hwndCombo, char **cd)
\r
5988 SendMessage(hwndCombo, CB_RESETCONTENT, 0, 0);
\r
5990 while (*cd != NULL) {
\r
5991 SendMessage(hwndCombo, CB_ADDSTRING, 0, (LPARAM) T_(*cd));
\r
5997 InitComboStringsFromOption(HANDLE hwndCombo, char *str)
\r
5999 char buf1[MAX_ARG_LEN];
\r
6002 if (str[0] == '@') {
\r
6003 FILE* f = fopen(str + 1, "r");
\r
6005 DisplayFatalError(str + 1, errno, 2);
\r
6008 len = fread(buf1, 1, sizeof(buf1)-1, f);
\r
6010 buf1[len] = NULLCHAR;
\r
6014 SendMessage(hwndCombo, CB_RESETCONTENT, 0, 0);
\r
6017 char buf[MSG_SIZ];
\r
6018 char *end = strchr(str, '\n');
\r
6019 if (end == NULL) return;
\r
6020 memcpy(buf, str, end - str);
\r
6021 buf[end - str] = NULLCHAR;
\r
6022 SendMessage(hwndCombo, CB_ADDSTRING, 0, (LPARAM) buf);
\r
6028 SetStartupDialogEnables(HWND hDlg)
\r
6030 EnableWindow(GetDlgItem(hDlg, OPT_ChessEngineName),
\r
6031 IsDlgButtonChecked(hDlg, OPT_ChessEngine) ||
\r
6032 (appData.zippyPlay && IsDlgButtonChecked(hDlg, OPT_ChessServer)));
\r
6033 EnableWindow(GetDlgItem(hDlg, OPT_SecondChessEngineName),
\r
6034 IsDlgButtonChecked(hDlg, OPT_ChessEngine));
\r
6035 EnableWindow(GetDlgItem(hDlg, OPT_ChessServerName),
\r
6036 IsDlgButtonChecked(hDlg, OPT_ChessServer));
\r
6037 EnableWindow(GetDlgItem(hDlg, OPT_AdditionalOptions),
\r
6038 IsDlgButtonChecked(hDlg, OPT_AnyAdditional));
\r
6039 EnableWindow(GetDlgItem(hDlg, IDOK),
\r
6040 IsDlgButtonChecked(hDlg, OPT_ChessEngine) ||
\r
6041 IsDlgButtonChecked(hDlg, OPT_ChessServer) ||
\r
6042 IsDlgButtonChecked(hDlg, OPT_View));
\r
6046 QuoteForFilename(char *filename)
\r
6048 int dquote, space;
\r
6049 dquote = strchr(filename, '"') != NULL;
\r
6050 space = strchr(filename, ' ') != NULL;
\r
6051 if (dquote || space) {
\r
6063 InitEngineBox(HWND hDlg, HWND hwndCombo, char* nthcp, char* nthd, char* nthdir, char *nthnames)
\r
6065 char buf[MSG_SIZ];
\r
6068 InitComboStringsFromOption(hwndCombo, nthnames);
\r
6069 q = QuoteForFilename(nthcp);
\r
6070 snprintf(buf, MSG_SIZ, "%s%s%s", q, nthcp, q);
\r
6071 if (*nthdir != NULLCHAR) {
\r
6072 q = QuoteForFilename(nthdir);
\r
6073 snprintf(buf + strlen(buf), MSG_SIZ, " /%s=%s%s%s", nthd, q, nthdir, q);
\r
6075 if (*nthcp == NULLCHAR) {
\r
6076 SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) 0, (LPARAM) 0);
\r
6077 } else if (SendMessage(hwndCombo, CB_SELECTSTRING, (WPARAM) -1, (LPARAM) buf) == CB_ERR) {
\r
6078 SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) -1, (LPARAM) 0);
\r
6079 SendMessage(hwndCombo, WM_SETTEXT, (WPARAM) 0, (LPARAM) buf);
\r
6084 StartupDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
6086 char buf[MSG_SIZ];
\r
6090 switch (message) {
\r
6091 case WM_INITDIALOG:
\r
6092 /* Center the dialog */
\r
6093 CenterWindow (hDlg, GetDesktopWindow());
\r
6094 Translate(hDlg, DLG_Startup);
\r
6095 /* Initialize the dialog items */
\r
6096 InitEngineBox(hDlg, GetDlgItem(hDlg, OPT_ChessEngineName),
\r
6097 appData.firstChessProgram, "fd", appData.firstDirectory,
\r
6098 firstChessProgramNames);
\r
6099 InitEngineBox(hDlg, GetDlgItem(hDlg, OPT_SecondChessEngineName),
\r
6100 appData.secondChessProgram, singleList ? "fd" : "sd", appData.secondDirectory,
\r
6101 singleList ? firstChessProgramNames : secondChessProgramNames); //[HGM] single: use first list in second combo
\r
6102 hwndCombo = GetDlgItem(hDlg, OPT_ChessServerName);
\r
6103 InitComboStringsFromOption(hwndCombo, icsNames);
\r
6104 snprintf(buf, MSG_SIZ, "%s /icsport=%s", appData.icsHost, appData.icsPort);
\r
6105 if (*appData.icsHelper != NULLCHAR) {
\r
6106 char *q = QuoteForFilename(appData.icsHelper);
\r
6107 sprintf(buf + strlen(buf), " /icshelper=%s%s%s", q, appData.icsHelper, q);
\r
6109 if (*appData.icsHost == NULLCHAR) {
\r
6110 SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) 0, (LPARAM) 0);
\r
6111 /*SendMessage(hwndCombo, CB_SHOWDROPDOWN, (WPARAM) TRUE, (LPARAM) 0); !!too soon */
\r
6112 } else if (SendMessage(hwndCombo, CB_SELECTSTRING, (WPARAM) -1, (LPARAM) buf) == CB_ERR) {
\r
6113 SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) -1, (LPARAM) 0);
\r
6114 SendMessage(hwndCombo, WM_SETTEXT, (WPARAM) 0, (LPARAM) buf);
\r
6117 if (appData.icsActive) {
\r
6118 CheckDlgButton(hDlg, OPT_ChessServer, BST_CHECKED);
\r
6120 else if (appData.noChessProgram) {
\r
6121 CheckDlgButton(hDlg, OPT_View, BST_CHECKED);
\r
6124 CheckDlgButton(hDlg, OPT_ChessEngine, BST_CHECKED);
\r
6127 SetStartupDialogEnables(hDlg);
\r
6131 switch (LOWORD(wParam)) {
\r
6133 if (IsDlgButtonChecked(hDlg, OPT_ChessEngine)) {
\r
6134 safeStrCpy(buf, "/fcp=", sizeof(buf)/sizeof(buf[0]) );
\r
6135 GetDlgItemText(hDlg, OPT_ChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));
\r
6137 ParseArgs(StringGet, &p);
\r
6138 safeStrCpy(buf, singleList ? "/fcp=" : "/scp=", sizeof(buf)/sizeof(buf[0]) );
\r
6139 GetDlgItemText(hDlg, OPT_SecondChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));
\r
6141 SwapEngines(singleList); // temporarily swap first and second, to load a second 'first', ...
\r
6142 ParseArgs(StringGet, &p);
\r
6143 SwapEngines(singleList); // ... and then make it 'second'
\r
6144 appData.noChessProgram = FALSE;
\r
6145 appData.icsActive = FALSE;
\r
6146 } else if (IsDlgButtonChecked(hDlg, OPT_ChessServer)) {
\r
6147 safeStrCpy(buf, "/ics /icshost=", sizeof(buf)/sizeof(buf[0]) );
\r
6148 GetDlgItemText(hDlg, OPT_ChessServerName, buf + strlen(buf), sizeof(buf) - strlen(buf));
\r
6150 ParseArgs(StringGet, &p);
\r
6151 if (appData.zippyPlay) {
\r
6152 safeStrCpy(buf, "/fcp=", sizeof(buf)/sizeof(buf[0]) );
\r
6153 GetDlgItemText(hDlg, OPT_ChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));
\r
6155 ParseArgs(StringGet, &p);
\r
6157 } else if (IsDlgButtonChecked(hDlg, OPT_View)) {
\r
6158 appData.noChessProgram = TRUE;
\r
6159 appData.icsActive = FALSE;
\r
6161 MessageBox(hDlg, _("Choose an option, or cancel to exit"),
\r
6162 _("Option Error"), MB_OK|MB_ICONEXCLAMATION);
\r
6165 if (IsDlgButtonChecked(hDlg, OPT_AnyAdditional)) {
\r
6166 GetDlgItemText(hDlg, OPT_AdditionalOptions, buf, sizeof(buf));
\r
6168 ParseArgs(StringGet, &p);
\r
6170 EndDialog(hDlg, TRUE);
\r
6177 case IDM_HELPCONTENTS:
\r
6178 if (!WinHelp (hDlg, "winboard.hlp", HELP_KEY,(DWORD)(LPSTR)"CONTENTS")) {
\r
6179 MessageBox (GetFocus(),
\r
6180 _("Unable to activate help"),
\r
6181 szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);
\r
6186 SetStartupDialogEnables(hDlg);
\r
6194 /*---------------------------------------------------------------------------*\
\r
6196 * About box dialog functions
\r
6198 \*---------------------------------------------------------------------------*/
\r
6200 /* Process messages for "About" dialog box */
\r
6202 About(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
6204 switch (message) {
\r
6205 case WM_INITDIALOG: /* message: initialize dialog box */
\r
6206 /* Center the dialog over the application window */
\r
6207 CenterWindow (hDlg, GetWindow (hDlg, GW_OWNER));
\r
6208 SetDlgItemText(hDlg, ABOUTBOX_Version, programVersion);
\r
6209 Translate(hDlg, ABOUTBOX);
\r
6213 case WM_COMMAND: /* message: received a command */
\r
6214 if (LOWORD(wParam) == IDOK /* "OK" box selected? */
\r
6215 || LOWORD(wParam) == IDCANCEL) { /* System menu close command? */
\r
6216 EndDialog(hDlg, TRUE); /* Exit the dialog */
\r
6224 /*---------------------------------------------------------------------------*\
\r
6226 * Comment Dialog functions
\r
6228 \*---------------------------------------------------------------------------*/
\r
6231 CommentDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
6233 static HANDLE hwndText = NULL;
\r
6234 int len, newSizeX, newSizeY, flags;
\r
6235 static int sizeX, sizeY;
\r
6240 switch (message) {
\r
6241 case WM_INITDIALOG: /* message: initialize dialog box */
\r
6242 /* Initialize the dialog items */
\r
6243 Translate(hDlg, DLG_EditComment);
\r
6244 hwndText = GetDlgItem(hDlg, OPT_CommentText);
\r
6245 SetDlgItemText(hDlg, OPT_CommentText, commentText);
\r
6246 EnableWindow(GetDlgItem(hDlg, OPT_CancelComment), editComment);
\r
6247 EnableWindow(GetDlgItem(hDlg, OPT_ClearComment), editComment);
\r
6248 EnableWindow(GetDlgItem(hDlg, OPT_EditComment), !editComment);
\r
6249 SendMessage(hwndText, EM_SETREADONLY, !editComment, 0);
\r
6250 SetWindowText(hDlg, commentTitle);
\r
6251 if (editComment) {
\r
6252 SetFocus(hwndText);
\r
6254 SetFocus(GetDlgItem(hDlg, IDOK));
\r
6256 SendMessage(GetDlgItem(hDlg, OPT_CommentText),
\r
6257 WM_SETFONT, (WPARAM)font[boardSize][COMMENT_FONT]->hf,
\r
6258 MAKELPARAM(FALSE, 0));
\r
6259 /* Size and position the dialog */
\r
6260 if (!commentDialog) {
\r
6261 commentDialog = hDlg;
\r
6262 flags = SWP_NOZORDER;
\r
6263 GetClientRect(hDlg, &rect);
\r
6264 sizeX = rect.right;
\r
6265 sizeY = rect.bottom;
\r
6266 if (wpComment.x != CW_USEDEFAULT && wpComment.y != CW_USEDEFAULT &&
\r
6267 wpComment.width != CW_USEDEFAULT && wpComment.height != CW_USEDEFAULT) {
\r
6268 WINDOWPLACEMENT wp;
\r
6269 EnsureOnScreen(&wpComment.x, &wpComment.y, 0, 0);
\r
6270 wp.length = sizeof(WINDOWPLACEMENT);
\r
6272 wp.showCmd = SW_SHOW;
\r
6273 wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;
\r
6274 wp.rcNormalPosition.left = wpComment.x;
\r
6275 wp.rcNormalPosition.right = wpComment.x + wpComment.width;
\r
6276 wp.rcNormalPosition.top = wpComment.y;
\r
6277 wp.rcNormalPosition.bottom = wpComment.y + wpComment.height;
\r
6278 SetWindowPlacement(hDlg, &wp);
\r
6280 GetClientRect(hDlg, &rect);
\r
6281 newSizeX = rect.right;
\r
6282 newSizeY = rect.bottom;
\r
6283 ResizeEditPlusButtons(hDlg, hwndText, sizeX, sizeY,
\r
6284 newSizeX, newSizeY);
\r
6289 SendDlgItemMessage( hDlg, OPT_CommentText, EM_SETEVENTMASK, 0, ENM_MOUSEEVENTS | ENM_KEYEVENTS );
\r
6292 case WM_COMMAND: /* message: received a command */
\r
6293 switch (LOWORD(wParam)) {
\r
6295 if (editComment) {
\r
6297 /* Read changed options from the dialog box */
\r
6298 hwndText = GetDlgItem(hDlg, OPT_CommentText);
\r
6299 len = GetWindowTextLength(hwndText);
\r
6300 str = (char *) malloc(len + 1);
\r
6301 GetWindowText(hwndText, str, len + 1);
\r
6310 ReplaceComment(commentIndex, str);
\r
6317 case OPT_CancelComment:
\r
6321 case OPT_ClearComment:
\r
6322 SetDlgItemText(hDlg, OPT_CommentText, "");
\r
6325 case OPT_EditComment:
\r
6326 EditCommentEvent();
\r
6334 case WM_NOTIFY: // [HGM] vari: cloned from whistory.c
\r
6335 if( wParam == OPT_CommentText ) {
\r
6336 MSGFILTER * lpMF = (MSGFILTER *) lParam;
\r
6338 if( lpMF->msg == WM_RBUTTONDOWN && (lpMF->wParam & (MK_CONTROL | MK_SHIFT)) == 0 ||
\r
6339 lpMF->msg == WM_CHAR && lpMF->wParam == '\022' ) {
\r
6343 pt.x = LOWORD( lpMF->lParam );
\r
6344 pt.y = HIWORD( lpMF->lParam );
\r
6346 if(lpMF->msg == WM_CHAR) {
\r
6348 SendDlgItemMessage( hDlg, OPT_CommentText, EM_EXGETSEL, 0, (LPARAM) &sel );
\r
6349 index = sel.cpMin;
\r
6351 index = SendDlgItemMessage( hDlg, OPT_CommentText, EM_CHARFROMPOS, 0, (LPARAM) &pt );
\r
6353 hwndText = GetDlgItem(hDlg, OPT_CommentText); // cloned from above
\r
6354 len = GetWindowTextLength(hwndText);
\r
6355 str = (char *) malloc(len + 1);
\r
6356 GetWindowText(hwndText, str, len + 1);
\r
6357 ReplaceComment(commentIndex, str);
\r
6358 if(commentIndex != currentMove) ToNrEvent(commentIndex);
\r
6359 LoadVariation( index, str ); // [HGM] also does the actual moving to it, now
\r
6362 /* Zap the message for good: apparently, returning non-zero is not enough */
\r
6363 lpMF->msg = WM_USER;
\r
6371 newSizeX = LOWORD(lParam);
\r
6372 newSizeY = HIWORD(lParam);
\r
6373 ResizeEditPlusButtons(hDlg, hwndText, sizeX, sizeY, newSizeX, newSizeY);
\r
6378 case WM_GETMINMAXINFO:
\r
6379 /* Prevent resizing window too small */
\r
6380 mmi = (MINMAXINFO *) lParam;
\r
6381 mmi->ptMinTrackSize.x = 100;
\r
6382 mmi->ptMinTrackSize.y = 100;
\r
6389 EitherCommentPopUp(int index, char *title, char *str, BOOLEAN edit)
\r
6394 CheckMenuItem(GetMenu(hwndMain), IDM_Comment, edit ? MF_CHECKED : MF_UNCHECKED);
\r
6396 if (str == NULL) str = "";
\r
6397 p = (char *) malloc(2 * strlen(str) + 2);
\r
6400 if (*str == '\n') *q++ = '\r';
\r
6404 if (commentText != NULL) free(commentText);
\r
6406 commentIndex = index;
\r
6407 commentTitle = title;
\r
6409 editComment = edit;
\r
6411 if (commentDialog) {
\r
6412 SendMessage(commentDialog, WM_INITDIALOG, 0, 0);
\r
6413 if (!commentUp) ShowWindow(commentDialog, SW_SHOW);
\r
6415 lpProc = MakeProcInstance((FARPROC)CommentDialog, hInst);
\r
6416 CreateDialog(hInst, MAKEINTRESOURCE(DLG_EditComment),
\r
6417 hwndMain, (DLGPROC)lpProc);
\r
6418 FreeProcInstance(lpProc);
\r
6424 /*---------------------------------------------------------------------------*\
\r
6426 * Type-in move dialog functions
\r
6428 \*---------------------------------------------------------------------------*/
\r
6431 TypeInMoveDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
6433 char move[MSG_SIZ];
\r
6436 switch (message) {
\r
6437 case WM_INITDIALOG:
\r
6438 move[0] = (char) lParam;
\r
6439 move[1] = NULLCHAR;
\r
6440 CenterWindowEx(hDlg, GetWindow(hDlg, GW_OWNER), 1 );
\r
6441 Translate(hDlg, DLG_TypeInMove);
\r
6442 hInput = GetDlgItem(hDlg, OPT_Move);
\r
6443 SetWindowText(hInput, move);
\r
6445 SendMessage(hInput, EM_SETSEL, (WPARAM)9999, (LPARAM)9999);
\r
6449 switch (LOWORD(wParam)) {
\r
6452 shiftKey = GetKeyState(VK_SHIFT) < 0; // [HGM] remember last shift status
\r
6453 GetDlgItemText(hDlg, OPT_Move, move, sizeof(move));
6454 TypeInDoneEvent(move);
\r
6455 EndDialog(hDlg, TRUE);
\r
6458 EndDialog(hDlg, FALSE);
\r
6469 PopUpMoveDialog(char firstchar)
\r
6473 lpProc = MakeProcInstance((FARPROC)TypeInMoveDialog, hInst);
\r
6474 DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_TypeInMove),
\r
6475 hwndMain, (DLGPROC)lpProc, (LPARAM)firstchar);
\r
6476 FreeProcInstance(lpProc);
\r
6479 /*---------------------------------------------------------------------------*\
\r
6481 * Type-in name dialog functions
\r
6483 \*---------------------------------------------------------------------------*/
\r
6486 TypeInNameDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
6488 char move[MSG_SIZ];
\r
6491 switch (message) {
\r
6492 case WM_INITDIALOG:
\r
6493 move[0] = (char) lParam;
\r
6494 move[1] = NULLCHAR;
\r
6495 CenterWindowEx(hDlg, GetWindow(hDlg, GW_OWNER), 1 );
\r
6496 Translate(hDlg, DLG_TypeInName);
\r
6497 hInput = GetDlgItem(hDlg, OPT_Name);
\r
6498 SetWindowText(hInput, move);
\r
6500 SendMessage(hInput, EM_SETSEL, (WPARAM)9999, (LPARAM)9999);
\r
6504 switch (LOWORD(wParam)) {
\r
6506 GetDlgItemText(hDlg, OPT_Name, move, sizeof(move));
\r
6507 appData.userName = strdup(move);
\r
6510 if(gameMode == MachinePlaysWhite || gameMode == MachinePlaysBlack) {
\r
6511 snprintf(move, MSG_SIZ, "%s vs. %s", gameInfo.white, gameInfo.black);
\r
6512 DisplayTitle(move);
\r
6516 EndDialog(hDlg, TRUE);
\r
6519 EndDialog(hDlg, FALSE);
\r
6530 PopUpNameDialog(char firstchar)
\r
6534 lpProc = MakeProcInstance((FARPROC)TypeInNameDialog, hInst);
\r
6535 DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_TypeInName),
\r
6536 hwndMain, (DLGPROC)lpProc, (LPARAM)firstchar);
\r
6537 FreeProcInstance(lpProc);
\r
6540 /*---------------------------------------------------------------------------*\
\r
6544 \*---------------------------------------------------------------------------*/
\r
6546 /* Nonmodal error box */
\r
6547 LRESULT CALLBACK ErrorDialog(HWND hDlg, UINT message,
\r
6548 WPARAM wParam, LPARAM lParam);
\r
6551 ErrorPopUp(char *title, char *content)
\r
6555 BOOLEAN modal = hwndMain == NULL;
\r
6573 strncpy(errorTitle, title, sizeof(errorTitle));
\r
6574 errorTitle[sizeof(errorTitle) - 1] = '\0';
\r
6577 MessageBox(NULL, errorMessage, errorTitle, MB_OK|MB_ICONEXCLAMATION);
\r
6579 lpProc = MakeProcInstance((FARPROC)ErrorDialog, hInst);
\r
6580 CreateDialog(hInst, MAKEINTRESOURCE(DLG_Error),
\r
6581 hwndMain, (DLGPROC)lpProc);
\r
6582 FreeProcInstance(lpProc);
\r
6589 if (!appData.popupMoveErrors && moveErrorMessageUp) DisplayMessage("", "");
\r
6590 if (errorDialog == NULL) return;
\r
6591 DestroyWindow(errorDialog);
\r
6592 errorDialog = NULL;
\r
6593 if(errorExitStatus) ExitEvent(errorExitStatus);
\r
6597 ErrorDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
6602 switch (message) {
\r
6603 case WM_INITDIALOG:
\r
6604 GetWindowRect(hDlg, &rChild);
\r
6607 SetWindowPos(hDlg, NULL, rChild.left,
\r
6608 rChild.top + boardRect.top - (rChild.bottom - rChild.top),
\r
6609 0, 0, SWP_NOZORDER|SWP_NOSIZE);
\r
6613 [AS] It seems that the above code wants to move the dialog up in the "caption
\r
6614 area" of the main window, but it uses the dialog height as an hard-coded constant,
\r
6615 and it doesn't work when you resize the dialog.
\r
6616 For now, just give it a default position.
\r
6618 SetWindowPos(hDlg, NULL, boardRect.left+8, boardRect.top+8, 0, 0, SWP_NOZORDER|SWP_NOSIZE);
\r
6619 Translate(hDlg, DLG_Error);
\r
6621 errorDialog = hDlg;
\r
6622 SetWindowText(hDlg, errorTitle);
\r
6623 hwndText = GetDlgItem(hDlg, OPT_ErrorText);
\r
6624 SetDlgItemText(hDlg, OPT_ErrorText, errorMessage);
\r
6628 switch (LOWORD(wParam)) {
\r
6631 if (errorDialog == hDlg) errorDialog = NULL;
\r
6632 DestroyWindow(hDlg);
\r
6644 HWND gothicDialog = NULL;
\r
6647 GothicDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
6651 int height = GetSystemMetrics(SM_CYCAPTION)+GetSystemMetrics(SM_CYFRAME);
\r
6653 switch (message) {
\r
6654 case WM_INITDIALOG:
\r
6655 GetWindowRect(hDlg, &rChild);
\r
6657 SetWindowPos(hDlg, NULL, wpMain.x, wpMain.y-height, wpMain.width, height,
\r
6661 [AS] It seems that the above code wants to move the dialog up in the "caption
\r
6662 area" of the main window, but it uses the dialog height as an hard-coded constant,
\r
6663 and it doesn't work when you resize the dialog.
\r
6664 For now, just give it a default position.
\r
6666 gothicDialog = hDlg;
\r
6667 SetWindowText(hDlg, errorTitle);
\r
6668 hwndText = GetDlgItem(hDlg, OPT_ErrorText);
\r
6669 SetDlgItemText(hDlg, OPT_ErrorText, errorMessage);
\r
6673 switch (LOWORD(wParam)) {
\r
6676 if (errorDialog == hDlg) errorDialog = NULL;
\r
6677 DestroyWindow(hDlg);
\r
6689 GothicPopUp(char *title, VariantClass variant)
\r
6692 static char *lastTitle;
\r
6694 strncpy(errorTitle, title, sizeof(errorTitle));
\r
6695 errorTitle[sizeof(errorTitle) - 1] = '\0';
\r
6697 if(lastTitle != title && gothicDialog != NULL) {
\r
6698 DestroyWindow(gothicDialog);
\r
6699 gothicDialog = NULL;
\r
6701 if(variant != VariantNormal && gothicDialog == NULL) {
\r
6702 title = lastTitle;
\r
6703 lpProc = MakeProcInstance((FARPROC)GothicDialog, hInst);
\r
6704 CreateDialog(hInst, MAKEINTRESOURCE(DLG_Error),
\r
6705 hwndMain, (DLGPROC)lpProc);
\r
6706 FreeProcInstance(lpProc);
\r
6711 /*---------------------------------------------------------------------------*\
\r
6713 * Ics Interaction console functions
\r
6715 \*---------------------------------------------------------------------------*/
\r
6717 #define HISTORY_SIZE 64
\r
6718 static char *history[HISTORY_SIZE];
\r
6719 int histIn = 0, histP = 0;
\r
6722 SaveInHistory(char *cmd)
\r
6724 if (history[histIn] != NULL) {
\r
6725 free(history[histIn]);
\r
6726 history[histIn] = NULL;
\r
6728 if (*cmd == NULLCHAR) return;
\r
6729 history[histIn] = StrSave(cmd);
\r
6730 histIn = (histIn + 1) % HISTORY_SIZE;
\r
6731 if (history[histIn] != NULL) {
\r
6732 free(history[histIn]);
\r
6733 history[histIn] = NULL;
\r
6739 PrevInHistory(char *cmd)
\r
6742 if (histP == histIn) {
\r
6743 if (history[histIn] != NULL) free(history[histIn]);
\r
6744 history[histIn] = StrSave(cmd);
\r
6746 newhp = (histP - 1 + HISTORY_SIZE) % HISTORY_SIZE;
\r
6747 if (newhp == histIn || history[newhp] == NULL) return NULL;
\r
6749 return history[histP];
\r
6755 if (histP == histIn) return NULL;
\r
6756 histP = (histP + 1) % HISTORY_SIZE;
\r
6757 return history[histP];
\r
6761 LoadIcsTextMenu(IcsTextMenuEntry *e)
\r
6765 hmenu = LoadMenu(hInst, "TextMenu");
\r
6766 h = GetSubMenu(hmenu, 0);
\r
6768 if (strcmp(e->item, "-") == 0) {
\r
6769 AppendMenu(h, MF_SEPARATOR, 0, 0);
\r
6770 } else { // [HGM] re-written a bit to use only one AppendMenu call for both cases (| or no |)
\r
6771 int flags = MF_STRING, j = 0;
\r
6772 if (e->item[0] == '|') {
\r
6773 flags |= MF_MENUBARBREAK;
\r
6776 if(!strcmp(e->command, "none")) flags |= MF_GRAYED; // [HGM] chatclick: provide inactive dummy
\r
6777 AppendMenu(h, flags, IDM_CommandX + i, e->item + j);
\r
6785 WNDPROC consoleTextWindowProc;
\r
6788 CommandX(HWND hwnd, char *command, BOOLEAN getname, BOOLEAN immediate)
\r
6790 char buf[MSG_SIZ], name[MSG_SIZ];
\r
6791 HWND hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
6795 SetWindowText(hInput, command);
\r
6797 SendMessage(hInput, WM_CHAR, '\r', 0);
\r
6799 sel.cpMin = 999999;
\r
6800 sel.cpMax = 999999;
\r
6801 SendMessage(hInput, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
6806 SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);
\r
6807 if (sel.cpMin == sel.cpMax) {
\r
6808 /* Expand to surrounding word */
\r
6811 tr.chrg.cpMax = sel.cpMin;
\r
6812 tr.chrg.cpMin = --sel.cpMin;
\r
6813 if (sel.cpMin < 0) break;
\r
6814 tr.lpstrText = name;
\r
6815 SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr);
\r
6816 } while (isalpha(name[0]) || isdigit(name[0]) || name[0] == '-');
\r
6820 tr.chrg.cpMin = sel.cpMax;
\r
6821 tr.chrg.cpMax = ++sel.cpMax;
\r
6822 tr.lpstrText = name;
\r
6823 if (SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr) < 1) break;
\r
6824 } while (isalpha(name[0]) || isdigit(name[0]) || name[0] == '-');
\r
6827 if (sel.cpMax == sel.cpMin || sel.cpMax - sel.cpMin > MSG_SIZ/2) {
\r
6828 MessageBeep(MB_ICONEXCLAMATION);
\r
6832 tr.lpstrText = name;
\r
6833 SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr);
\r
6835 if (sel.cpMax - sel.cpMin > MSG_SIZ/2) {
\r
6836 MessageBeep(MB_ICONEXCLAMATION);
\r
6839 SendMessage(hwnd, EM_GETSELTEXT, 0, (LPARAM) name);
\r
6842 if(strstr(command, "%s")) snprintf(buf, MSG_SIZ, command, name); else
\r
6843 snprintf(buf, MSG_SIZ, "%s %s", command, name);
\r
6844 SetWindowText(hInput, buf);
\r
6845 SendMessage(hInput, WM_CHAR, '\r', 0);
\r
6847 if(!strcmp(command, "chat")) { ChatPopUp(name); return; }
\r
6848 snprintf(buf, MSG_SIZ, "%s %s ", command, name); /* trailing space */
\r
6849 SetWindowText(hInput, buf);
\r
6850 sel.cpMin = 999999;
\r
6851 sel.cpMax = 999999;
\r
6852 SendMessage(hInput, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
6858 ConsoleTextSubclass(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
\r
6863 switch (message) {
\r
6865 if (!(GetKeyState(VK_CONTROL) & ~1)) break;
\r
6866 if(wParam=='R') return 0;
\r
6869 SendMessage(hwnd, EM_LINESCROLL, 0, -999999);
\r
6872 sel.cpMin = 999999;
\r
6873 sel.cpMax = 999999;
\r
6874 SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
6875 SendMessage(hwnd, EM_SCROLLCARET, 0, 0);
\r
6880 if(wParam != '\022') {
\r
6881 if (wParam == '\t') {
\r
6882 if (GetKeyState(VK_SHIFT) < 0) {
\r
6884 if (IsIconic(hwndMain)) ShowWindow(hwndMain, SW_RESTORE);
\r
6885 if (buttonDesc[0].hwnd) {
\r
6886 SetFocus(buttonDesc[0].hwnd);
\r
6888 SetFocus(hwndMain);
\r
6892 SetFocus(GetDlgItem(hwndConsole, OPT_ConsoleInput));
\r
6895 hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
6896 JAWS_DELETE( SetFocus(hInput); )
\r
6897 SendMessage(hInput, message, wParam, lParam);
\r
6900 } // [HGM] navigate: for Ctrl+R, flow into next case (moved up here) to summon up menu
\r
6902 case WM_RBUTTONDOWN:
\r
6903 if (!(GetKeyState(VK_SHIFT) & ~1)) {
\r
6904 /* Move selection here if it was empty */
\r
6906 pt.x = LOWORD(lParam);
\r
6907 pt.y = HIWORD(lParam);
\r
6908 SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);
\r
6909 if (sel.cpMin == sel.cpMax) {
\r
6910 if(lParam != -1) sel.cpMin = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&pt); /*doc is wrong*/
\r
6911 sel.cpMax = sel.cpMin;
\r
6912 SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
6914 SendMessage(hwnd, EM_HIDESELECTION, FALSE, FALSE);
\r
6915 { // [HGM] chatclick: code moved here from WM_RBUTTONUP case, to have menu appear on down-click
\r
6917 HMENU hmenu = LoadIcsTextMenu(icsTextMenuEntry);
\r
6918 SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);
\r
6919 if (sel.cpMin == sel.cpMax) {
\r
6920 EnableMenuItem(hmenu, IDM_Copy, MF_BYCOMMAND|MF_GRAYED);
\r
6921 EnableMenuItem(hmenu, IDM_QuickPaste, MF_BYCOMMAND|MF_GRAYED);
\r
6923 if (!IsClipboardFormatAvailable(CF_TEXT)) {
\r
6924 EnableMenuItem(hmenu, IDM_Paste, MF_BYCOMMAND|MF_GRAYED);
\r
6926 pt.x = LOWORD(lParam)-30; // [HGM] chatclick: make menu pop up with pointer above upper-right item
\r
6927 pt.y = HIWORD(lParam)-10; // make it appear as if mouse moved there, so it will be selected on up-click
\r
6928 PostMessage(hwnd, WM_MOUSEMOVE, wParam, lParam+5);
\r
6929 MenuPopup(hwnd, pt, hmenu, -1);
\r
6933 case WM_RBUTTONUP:
\r
6934 if (GetKeyState(VK_SHIFT) & ~1) {
\r
6935 SendDlgItemMessage(hwndConsole, OPT_ConsoleText,
\r
6936 WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);
\r
6940 hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
6942 return SendMessage(hInput, message, wParam, lParam);
\r
6943 case WM_MBUTTONDOWN:
\r
6944 return SendMessage(hwnd, WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);
\r
6946 switch (LOWORD(wParam)) {
\r
6947 case IDM_QuickPaste:
\r
6949 SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);
\r
6950 if (sel.cpMin == sel.cpMax) {
\r
6951 MessageBeep(MB_ICONEXCLAMATION);
\r
6954 SendMessage(hwnd, WM_COPY, 0, 0);
\r
6955 hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
6956 SendMessage(hInput, WM_PASTE, 0, 0);
\r
6961 SendMessage(hwnd, WM_CUT, 0, 0);
\r
6964 SendMessage(hwnd, WM_PASTE, 0, 0);
\r
6967 SendMessage(hwnd, WM_COPY, 0, 0);
\r
6971 int i = LOWORD(wParam) - IDM_CommandX;
\r
6972 if (i >= 0 && i < ICS_TEXT_MENU_SIZE &&
\r
6973 icsTextMenuEntry[i].command != NULL) {
\r
6974 CommandX(hwnd, icsTextMenuEntry[i].command,
\r
6975 icsTextMenuEntry[i].getname,
\r
6976 icsTextMenuEntry[i].immediate);
\r
6984 return (*consoleTextWindowProc)(hwnd, message, wParam, lParam);
\r
6987 WNDPROC consoleInputWindowProc;
\r
6990 ConsoleInputSubclass(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
\r
6992 char buf[MSG_SIZ];
\r
6994 static BOOL sendNextChar = FALSE;
\r
6995 static BOOL quoteNextChar = FALSE;
\r
6996 InputSource *is = consoleInputSource;
\r
7000 switch (message) {
\r
7002 if (!appData.localLineEditing || sendNextChar) {
\r
7003 is->buf[0] = (CHAR) wParam;
\r
7005 SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);
\r
7006 sendNextChar = FALSE;
\r
7009 if (quoteNextChar) {
\r
7010 buf[0] = (char) wParam;
\r
7011 buf[1] = NULLCHAR;
\r
7012 SendMessage(hwnd, EM_REPLACESEL, TRUE, (LPARAM) buf);
\r
7013 quoteNextChar = FALSE;
\r
7017 case '\r': /* Enter key */
\r
7018 is->count = GetWindowText(hwnd, is->buf, INPUT_SOURCE_BUF_SIZE-1);
\r
7019 if (consoleEcho) SaveInHistory(is->buf);
\r
7020 is->buf[is->count++] = '\n';
\r
7021 is->buf[is->count] = NULLCHAR;
\r
7022 SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);
\r
7023 if (consoleEcho) {
\r
7024 ConsoleOutput(is->buf, is->count, TRUE);
\r
7025 } else if (appData.localLineEditing) {
\r
7026 ConsoleOutput("\n", 1, TRUE);
\r
7029 case '\033': /* Escape key */
\r
7030 SetWindowText(hwnd, "");
\r
7031 cf.cbSize = sizeof(CHARFORMAT);
\r
7032 cf.dwMask = CFM_COLOR|CFM_BOLD|CFM_ITALIC|CFM_UNDERLINE|CFM_STRIKEOUT;
\r
7033 if (consoleEcho) {
\r
7034 cf.crTextColor = textAttribs[ColorNormal].color;
\r
7036 cf.crTextColor = COLOR_ECHOOFF;
\r
7038 cf.dwEffects = textAttribs[ColorNormal].effects;
\r
7039 SendMessage(hwnd, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf);
\r
7041 case '\t': /* Tab key */
\r
7042 if (GetKeyState(VK_SHIFT) < 0) {
\r
7044 SetFocus(GetDlgItem(hwndConsole, OPT_ConsoleText));
\r
7047 if (IsIconic(hwndMain)) ShowWindow(hwndMain, SW_RESTORE);
\r
7048 if (buttonDesc[0].hwnd) {
\r
7049 SetFocus(buttonDesc[0].hwnd);
\r
7051 SetFocus(hwndMain);
\r
7055 case '\023': /* Ctrl+S */
\r
7056 sendNextChar = TRUE;
\r
7058 case '\021': /* Ctrl+Q */
\r
7059 quoteNextChar = TRUE;
\r
7069 GetWindowText(hwnd, buf, MSG_SIZ);
\r
7070 p = PrevInHistory(buf);
\r
7072 SetWindowText(hwnd, p);
\r
7073 sel.cpMin = 999999;
\r
7074 sel.cpMax = 999999;
\r
7075 SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
7080 p = NextInHistory();
\r
7082 SetWindowText(hwnd, p);
\r
7083 sel.cpMin = 999999;
\r
7084 sel.cpMax = 999999;
\r
7085 SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
7091 if (!(GetKeyState(VK_CONTROL) & ~1)) break;
\r
7095 SendDlgItemMessage(hwndConsole, OPT_ConsoleText, message, wParam, lParam);
\r
7099 case WM_MBUTTONDOWN:
\r
7100 SendDlgItemMessage(hwndConsole, OPT_ConsoleText,
\r
7101 WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);
\r
7103 case WM_RBUTTONUP:
\r
7104 if (GetKeyState(VK_SHIFT) & ~1) {
\r
7105 SendDlgItemMessage(hwndConsole, OPT_ConsoleText,
\r
7106 WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);
\r
7110 hmenu = LoadMenu(hInst, "InputMenu");
\r
7111 SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);
\r
7112 if (sel.cpMin == sel.cpMax) {
\r
7113 EnableMenuItem(hmenu, IDM_Copy, MF_BYCOMMAND|MF_GRAYED);
\r
7114 EnableMenuItem(hmenu, IDM_Cut, MF_BYCOMMAND|MF_GRAYED);
\r
7116 if (!IsClipboardFormatAvailable(CF_TEXT)) {
\r
7117 EnableMenuItem(hmenu, IDM_Paste, MF_BYCOMMAND|MF_GRAYED);
\r
7119 pt.x = LOWORD(lParam);
\r
7120 pt.y = HIWORD(lParam);
\r
7121 MenuPopup(hwnd, pt, hmenu, -1);
\r
7125 switch (LOWORD(wParam)) {
\r
7127 SendMessage(hwnd, EM_UNDO, 0, 0);
\r
7129 case IDM_SelectAll:
\r
7131 sel.cpMax = -1; /*999999?*/
\r
7132 SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
7135 SendMessage(hwnd, WM_CUT, 0, 0);
\r
7138 SendMessage(hwnd, WM_PASTE, 0, 0);
\r
7141 SendMessage(hwnd, WM_COPY, 0, 0);
\r
7146 return (*consoleInputWindowProc)(hwnd, message, wParam, lParam);
\r
7149 #define CO_MAX 100000
\r
7150 #define CO_TRIM 1000
\r
7153 ConsoleWndProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
7155 static SnapData sd;
\r
7156 HWND hText, hInput;
\r
7158 static int sizeX, sizeY;
\r
7159 int newSizeX, newSizeY;
\r
7163 hText = GetDlgItem(hDlg, OPT_ConsoleText);
\r
7164 hInput = GetDlgItem(hDlg, OPT_ConsoleInput);
\r
7166 switch (message) {
\r
7168 if (((NMHDR*)lParam)->code == EN_LINK)
\r
7170 ENLINK *pLink = (ENLINK*)lParam;
\r
7171 if (pLink->msg == WM_LBUTTONUP)
\r
7175 tr.chrg = pLink->chrg;
\r
7176 tr.lpstrText = malloc(1+tr.chrg.cpMax-tr.chrg.cpMin);
\r
7177 SendMessage(hText, EM_GETTEXTRANGE, 0, (LPARAM)&tr);
\r
7178 ShellExecute(NULL, "open", tr.lpstrText, NULL, NULL, SW_SHOW);
\r
7179 free(tr.lpstrText);
\r
7183 case WM_INITDIALOG: /* message: initialize dialog box */
\r
7184 hwndConsole = hDlg;
\r
7186 consoleTextWindowProc = (WNDPROC)
\r
7187 SetWindowLongPtr(hText, GWLP_WNDPROC, (LONG_PTR) ConsoleTextSubclass);
\r
7188 SendMessage(hText, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);
\r
7189 consoleInputWindowProc = (WNDPROC)
\r
7190 SetWindowLongPtr(hInput, GWLP_WNDPROC, (LONG_PTR) ConsoleInputSubclass);
\r
7191 SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);
\r
7192 Colorize(ColorNormal, TRUE);
\r
7193 SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &consoleCF);
\r
7194 ChangedConsoleFont();
\r
7195 GetClientRect(hDlg, &rect);
\r
7196 sizeX = rect.right;
\r
7197 sizeY = rect.bottom;
\r
7198 if (wpConsole.x != CW_USEDEFAULT && wpConsole.y != CW_USEDEFAULT &&
\r
7199 wpConsole.width != CW_USEDEFAULT && wpConsole.height != CW_USEDEFAULT) {
\r
7200 WINDOWPLACEMENT wp;
\r
7201 EnsureOnScreen(&wpConsole.x, &wpConsole.y, 0, 0);
\r
7202 wp.length = sizeof(WINDOWPLACEMENT);
\r
7204 wp.showCmd = SW_SHOW;
\r
7205 wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;
\r
7206 wp.rcNormalPosition.left = wpConsole.x;
\r
7207 wp.rcNormalPosition.right = wpConsole.x + wpConsole.width;
\r
7208 wp.rcNormalPosition.top = wpConsole.y;
\r
7209 wp.rcNormalPosition.bottom = wpConsole.y + wpConsole.height;
\r
7210 SetWindowPlacement(hDlg, &wp);
\r
7213 // [HGM] Chessknight's change 2004-07-13
\r
7214 else { /* Determine Defaults */
\r
7215 WINDOWPLACEMENT wp;
\r
7216 wpConsole.x = wpMain.width + 1;
\r
7217 wpConsole.y = wpMain.y;
\r
7218 wpConsole.width = screenWidth - wpMain.width;
\r
7219 wpConsole.height = wpMain.height;
\r
7220 EnsureOnScreen(&wpConsole.x, &wpConsole.y, 0, 0);
\r
7221 wp.length = sizeof(WINDOWPLACEMENT);
\r
7223 wp.showCmd = SW_SHOW;
\r
7224 wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;
\r
7225 wp.rcNormalPosition.left = wpConsole.x;
\r
7226 wp.rcNormalPosition.right = wpConsole.x + wpConsole.width;
\r
7227 wp.rcNormalPosition.top = wpConsole.y;
\r
7228 wp.rcNormalPosition.bottom = wpConsole.y + wpConsole.height;
\r
7229 SetWindowPlacement(hDlg, &wp);
\r
7232 // Allow hText to highlight URLs and send notifications on them
\r
7233 wMask = (WORD) SendMessage(hText, EM_GETEVENTMASK, 0, 0L);
\r
7234 SendMessage(hText, EM_SETEVENTMASK, 0, wMask | ENM_LINK);
\r
7235 SendMessage(hText, EM_AUTOURLDETECT, TRUE, 0L);
\r
7236 SetWindowLongPtr(hText, GWLP_USERDATA, 79); // initialize the text window's width
\r
7250 if (IsIconic(hDlg)) break;
\r
7251 newSizeX = LOWORD(lParam);
\r
7252 newSizeY = HIWORD(lParam);
\r
7253 if (sizeX != newSizeX || sizeY != newSizeY) {
\r
7254 RECT rectText, rectInput;
\r
7256 int newTextHeight, newTextWidth;
\r
7257 GetWindowRect(hText, &rectText);
\r
7258 newTextWidth = rectText.right - rectText.left + newSizeX - sizeX;
\r
7259 newTextHeight = rectText.bottom - rectText.top + newSizeY - sizeY;
\r
7260 if (newTextHeight < 0) {
\r
7261 newSizeY += -newTextHeight;
\r
7262 newTextHeight = 0;
\r
7264 SetWindowPos(hText, NULL, 0, 0,
\r
7265 newTextWidth, newTextHeight, SWP_NOZORDER|SWP_NOMOVE);
\r
7266 GetWindowRect(hInput, &rectInput); /* gives screen coords */
\r
7267 pt.x = rectInput.left;
\r
7268 pt.y = rectInput.top + newSizeY - sizeY;
\r
7269 ScreenToClient(hDlg, &pt);
\r
7270 SetWindowPos(hInput, NULL,
\r
7271 pt.x, pt.y, /* needs client coords */
\r
7272 rectInput.right - rectInput.left + newSizeX - sizeX,
\r
7273 rectInput.bottom - rectInput.top, SWP_NOZORDER);
\r
7279 case WM_GETMINMAXINFO:
\r
7280 /* Prevent resizing window too small */
\r
7281 mmi = (MINMAXINFO *) lParam;
\r
7282 mmi->ptMinTrackSize.x = 100;
\r
7283 mmi->ptMinTrackSize.y = 100;
\r
7286 /* [AS] Snapping */
\r
7287 case WM_ENTERSIZEMOVE:
\r
7288 return OnEnterSizeMove( &sd, hDlg, wParam, lParam );
\r
7291 return OnSizing( &sd, hDlg, wParam, lParam );
\r
7294 return OnMoving( &sd, hDlg, wParam, lParam );
\r
7296 case WM_EXITSIZEMOVE:
\r
7297 UpdateICSWidth(hText);
\r
7298 return OnExitSizeMove( &sd, hDlg, wParam, lParam );
\r
7301 return DefWindowProc(hDlg, message, wParam, lParam);
\r
7309 if (hwndConsole) return;
\r
7310 hCons = CreateDialog(hInst, szConsoleName, 0, NULL);
\r
7311 SendMessage(hCons, WM_INITDIALOG, 0, 0);
\r
7316 ConsoleOutput(char* data, int length, int forceVisible)
\r
7321 char buf[CO_MAX+1];
\r
7324 static int delayLF = 0;
\r
7325 CHARRANGE savesel, sel;
\r
7327 if (hwndConsole == NULL || length > CO_MAX-100 || length == 0) return;
\r
7335 while (length--) {
\r
7343 } else if (*p == '\007') {
\r
7344 MyPlaySound(&sounds[(int)SoundBell]);
\r
7351 hText = GetDlgItem(hwndConsole, OPT_ConsoleText);
\r
7352 SendMessage(hText, EM_HIDESELECTION, TRUE, FALSE);
\r
7353 /* Save current selection */
\r
7354 SendMessage(hText, EM_EXGETSEL, 0, (LPARAM)&savesel);
\r
7355 exlen = GetWindowTextLength(hText);
\r
7356 /* Find out whether current end of text is visible */
\r
7357 SendMessage(hText, EM_GETRECT, 0, (LPARAM) &rect);
\r
7358 SendMessage(hText, EM_POSFROMCHAR, (WPARAM) &pEnd, exlen);
\r
7359 /* Trim existing text if it's too long */
\r
7360 if (exlen + (q - buf) > CO_MAX) {
\r
7361 trim = (CO_TRIM > (q - buf)) ? CO_TRIM : (q - buf);
\r
7364 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
7365 SendMessage(hText, EM_REPLACESEL, 0, (LPARAM)"");
\r
7367 savesel.cpMin -= trim;
\r
7368 savesel.cpMax -= trim;
\r
7369 if (exlen < 0) exlen = 0;
\r
7370 if (savesel.cpMin < 0) savesel.cpMin = 0;
\r
7371 if (savesel.cpMax < savesel.cpMin) savesel.cpMax = savesel.cpMin;
\r
7373 /* Append the new text */
\r
7374 sel.cpMin = exlen;
\r
7375 sel.cpMax = exlen;
\r
7376 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
7377 SendMessage(hText, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&consoleCF);
\r
7378 SendMessage(hText, EM_REPLACESEL, 0, (LPARAM) buf);
\r
7379 if (forceVisible || exlen == 0 ||
\r
7380 (rect.left <= pEnd.x && pEnd.x < rect.right &&
\r
7381 rect.top <= pEnd.y && pEnd.y < rect.bottom)) {
\r
7382 /* Scroll to make new end of text visible if old end of text
\r
7383 was visible or new text is an echo of user typein */
\r
7384 sel.cpMin = 9999999;
\r
7385 sel.cpMax = 9999999;
\r
7386 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
7387 SendMessage(hText, EM_HIDESELECTION, FALSE, FALSE);
\r
7388 SendMessage(hText, EM_SCROLLCARET, 0, 0);
\r
7389 SendMessage(hText, EM_HIDESELECTION, TRUE, FALSE);
\r
7391 if (savesel.cpMax == exlen || forceVisible) {
\r
7392 /* Move insert point to new end of text if it was at the old
\r
7393 end of text or if the new text is an echo of user typein */
\r
7394 sel.cpMin = 9999999;
\r
7395 sel.cpMax = 9999999;
\r
7396 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
7398 /* Restore previous selection */
\r
7399 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&savesel);
\r
7401 SendMessage(hText, EM_HIDESELECTION, FALSE, FALSE);
\r
7408 DisplayHoldingsCount(HDC hdc, int x, int y, int rightAlign, int copyNumber)
\r
7412 COLORREF oldFg, oldBg;
\r
7417 snprintf(buf, sizeof(buf)/sizeof(buf[0]),"%d", copyNumber); else buf[0] = 0;
\r
7419 oldFg = SetTextColor(hdc, RGB(255, 255, 255)); /* white */
\r
7420 oldBg = SetBkColor(hdc, RGB(0, 0, 0)); /* black */
\r
7421 oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);
\r
7424 rect.right = x + squareSize;
\r
7426 rect.bottom = y + squareSize;
\r
7429 ExtTextOut(hdc, x + MESSAGE_LINE_LEFTMARGIN
\r
7430 + (rightAlign ? (squareSize*2)/3 : 0),
\r
7431 y, ETO_CLIPPED|ETO_OPAQUE,
\r
7432 &rect, str, strlen(str), NULL);
\r
7434 (void) SetTextColor(hdc, oldFg);
\r
7435 (void) SetBkColor(hdc, oldBg);
\r
7436 (void) SelectObject(hdc, oldFont);
\r
7440 DisplayAClock(HDC hdc, int timeRemaining, int highlight,
\r
7441 RECT *rect, char *color, char *flagFell)
\r
7445 COLORREF oldFg, oldBg;
\r
7448 if (appData.clockMode) {
\r
7450 snprintf(buf, sizeof(buf)/sizeof(buf[0]), "%c %s %s", color[0], TimeString(timeRemaining), flagFell);
\r
7452 snprintf(buf, sizeof(buf)/sizeof(buf[0]), "%s:%c%s %s", color, (logoHeight>0 ? 0 : ' '), TimeString(timeRemaining), flagFell);
\r
7459 oldFg = SetTextColor(hdc, RGB(255, 255, 255)); /* white */
\r
7460 oldBg = SetBkColor(hdc, RGB(0, 0, 0)); /* black */
\r
7462 oldFg = SetTextColor(hdc, RGB(0, 0, 0)); /* black */
\r
7463 oldBg = SetBkColor(hdc, RGB(255, 255, 255)); /* white */
\r
7465 oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);
\r
7469 ExtTextOut(hdc, rect->left + MESSAGE_LINE_LEFTMARGIN,
\r
7470 rect->top, ETO_CLIPPED|ETO_OPAQUE,
\r
7471 rect, str, strlen(str), NULL);
\r
7472 if(logoHeight > 0 && appData.clockMode) {
\r
7474 str += strlen(color)+2;
\r
7475 r.top = rect->top + logoHeight/2;
\r
7476 r.left = rect->left;
\r
7477 r.right = rect->right;
\r
7478 r.bottom = rect->bottom;
\r
7479 ExtTextOut(hdc, rect->left + MESSAGE_LINE_LEFTMARGIN,
\r
7480 r.top, ETO_CLIPPED|ETO_OPAQUE,
\r
7481 &r, str, strlen(str), NULL);
\r
7483 (void) SetTextColor(hdc, oldFg);
\r
7484 (void) SetBkColor(hdc, oldBg);
\r
7485 (void) SelectObject(hdc, oldFont);
\r
7490 DoReadFile(HANDLE hFile, char *buf, int count, DWORD *outCount,
\r
7496 if( count <= 0 ) {
\r
7497 if (appData.debugMode) {
\r
7498 fprintf( debugFP, "DoReadFile: trying to read past end of buffer, overflow = %d\n", count );
\r
7501 return ERROR_INVALID_USER_BUFFER;
\r
7504 ResetEvent(ovl->hEvent);
\r
7505 ovl->Offset = ovl->OffsetHigh = 0;
\r
7506 ok = ReadFile(hFile, buf, count, outCount, ovl);
\r
7510 err = GetLastError();
\r
7511 if (err == ERROR_IO_PENDING) {
\r
7512 ok = GetOverlappedResult(hFile, ovl, outCount, TRUE);
\r
7516 err = GetLastError();
\r
7523 DoWriteFile(HANDLE hFile, char *buf, int count, DWORD *outCount,
\r
7528 ResetEvent(ovl->hEvent);
\r
7529 ovl->Offset = ovl->OffsetHigh = 0;
\r
7530 ok = WriteFile(hFile, buf, count, outCount, ovl);
\r
7534 err = GetLastError();
\r
7535 if (err == ERROR_IO_PENDING) {
\r
7536 ok = GetOverlappedResult(hFile, ovl, outCount, TRUE);
\r
7540 err = GetLastError();
\r
7546 /* [AS] If input is line by line and a line exceed the buffer size, force an error */
\r
7547 void CheckForInputBufferFull( InputSource * is )
\r
7549 if( is->lineByLine && (is->next - is->buf) >= INPUT_SOURCE_BUF_SIZE ) {
\r
7550 /* Look for end of line */
\r
7551 char * p = is->buf;
\r
7553 while( p < is->next && *p != '\n' ) {
\r
7557 if( p >= is->next ) {
\r
7558 if (appData.debugMode) {
\r
7559 fprintf( debugFP, "Input line exceeded buffer size (source id=%lu)\n", is->id );
\r
7562 is->error = ERROR_BROKEN_PIPE; /* [AS] Just any non-successful code! */
\r
7563 is->count = (DWORD) -1;
\r
7564 is->next = is->buf;
\r
7570 InputThread(LPVOID arg)
\r
7575 is = (InputSource *) arg;
\r
7576 ovl.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
\r
7577 ovl.Internal = ovl.InternalHigh = ovl.Offset = ovl.OffsetHigh = 0;
\r
7578 while (is->hThread != NULL) {
\r
7579 is->error = DoReadFile(is->hFile, is->next,
\r
7580 INPUT_SOURCE_BUF_SIZE - (is->next - is->buf),
\r
7581 &is->count, &ovl);
\r
7582 if (is->error == NO_ERROR) {
\r
7583 is->next += is->count;
\r
7585 if (is->error == ERROR_BROKEN_PIPE) {
\r
7586 /* Correct for MS brain damage. EOF reading a pipe is not an error. */
\r
7589 is->count = (DWORD) -1;
\r
7590 /* [AS] The (is->count <= 0) check below is not useful for unsigned values! */
\r
7595 CheckForInputBufferFull( is );
\r
7597 SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);
\r
7599 if( is->count == ((DWORD) -1) ) break; /* [AS] */
\r
7601 if (is->count <= 0) break; /* Quit on EOF or error */
\r
7604 CloseHandle(ovl.hEvent);
\r
7605 CloseHandle(is->hFile);
\r
7607 if (appData.debugMode) {
\r
7608 fprintf( debugFP, "Input thread terminated (id=%lu, error=%d, count=%ld)\n", is->id, is->error, is->count );
\r
7615 /* Windows 95 beta 2 won't let you do overlapped i/o on a console or pipe */
\r
7617 NonOvlInputThread(LPVOID arg)
\r
7624 is = (InputSource *) arg;
\r
7625 while (is->hThread != NULL) {
\r
7626 is->error = ReadFile(is->hFile, is->next,
\r
7627 INPUT_SOURCE_BUF_SIZE - (is->next - is->buf),
\r
7628 &is->count, NULL) ? NO_ERROR : GetLastError();
\r
7629 if (is->error == NO_ERROR) {
\r
7630 /* Change CRLF to LF */
\r
7631 if (is->next > is->buf) {
\r
7633 i = is->count + 1;
\r
7641 if (prev == '\r' && *p == '\n') {
\r
7653 if (is->error == ERROR_BROKEN_PIPE) {
\r
7654 /* Correct for MS brain damage. EOF reading a pipe is not an error. */
\r
7657 is->count = (DWORD) -1;
\r
7661 CheckForInputBufferFull( is );
\r
7663 SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);
\r
7665 if( is->count == ((DWORD) -1) ) break; /* [AS] */
\r
7667 if (is->count < 0) break; /* Quit on error */
\r
7669 CloseHandle(is->hFile);
\r
7674 SocketInputThread(LPVOID arg)
\r
7678 is = (InputSource *) arg;
\r
7679 while (is->hThread != NULL) {
\r
7680 is->count = recv(is->sock, is->buf, INPUT_SOURCE_BUF_SIZE, 0);
\r
7681 if ((int)is->count == SOCKET_ERROR) {
\r
7682 is->count = (DWORD) -1;
\r
7683 is->error = WSAGetLastError();
\r
7685 is->error = NO_ERROR;
\r
7686 is->next += is->count;
\r
7687 if (is->count == 0 && is->second == is) {
\r
7688 /* End of file on stderr; quit with no message */
\r
7692 SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);
\r
7694 if( is->count == ((DWORD) -1) ) break; /* [AS] */
\r
7696 if (is->count <= 0) break; /* Quit on EOF or error */
\r
7702 InputEvent(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
\r
7706 is = (InputSource *) lParam;
\r
7707 if (is->lineByLine) {
\r
7708 /* Feed in lines one by one */
\r
7709 char *p = is->buf;
\r
7711 while (q < is->next) {
\r
7712 if (*q++ == '\n') {
\r
7713 (is->func)(is, is->closure, p, q - p, NO_ERROR);
\r
7718 /* Move any partial line to the start of the buffer */
\r
7720 while (p < is->next) {
\r
7725 if (is->error != NO_ERROR || is->count == 0) {
\r
7726 /* Notify backend of the error. Note: If there was a partial
\r
7727 line at the end, it is not flushed through. */
\r
7728 (is->func)(is, is->closure, is->buf, is->count, is->error);
\r
7731 /* Feed in the whole chunk of input at once */
\r
7732 (is->func)(is, is->closure, is->buf, is->count, is->error);
\r
7733 is->next = is->buf;
\r
7737 /*---------------------------------------------------------------------------*\
\r
7739 * Menu enables. Used when setting various modes.
\r
7741 \*---------------------------------------------------------------------------*/
\r
7749 GreyRevert(Boolean grey)
\r
7750 { // [HGM] vari: for retracting variations in local mode
\r
7751 HMENU hmenu = GetMenu(hwndMain);
\r
7752 EnableMenuItem(hmenu, IDM_Revert, MF_BYCOMMAND|(grey ? MF_GRAYED : MF_ENABLED));
\r
7753 EnableMenuItem(hmenu, IDM_Annotate, MF_BYCOMMAND|(grey ? MF_GRAYED : MF_ENABLED));
\r
7757 SetMenuEnables(HMENU hmenu, Enables *enab)
\r
7759 while (enab->item > 0) {
\r
7760 (void) EnableMenuItem(hmenu, enab->item, enab->flags);
\r
7765 Enables gnuEnables[] = {
\r
7766 { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },
\r
7767 { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },
\r
7768 { IDM_IcsClient, MF_BYCOMMAND|MF_GRAYED },
\r
7769 { IDM_Accept, MF_BYCOMMAND|MF_GRAYED },
\r
7770 { IDM_Decline, MF_BYCOMMAND|MF_GRAYED },
\r
7771 { IDM_Rematch, MF_BYCOMMAND|MF_GRAYED },
\r
7772 { IDM_Adjourn, MF_BYCOMMAND|MF_GRAYED },
\r
7773 { IDM_StopExamining, MF_BYCOMMAND|MF_GRAYED },
\r
7774 { IDM_StopObserving, MF_BYCOMMAND|MF_GRAYED },
\r
7775 { IDM_Upload, MF_BYCOMMAND|MF_GRAYED },
\r
7776 { IDM_Revert, MF_BYCOMMAND|MF_GRAYED },
\r
7777 { IDM_Annotate, MF_BYCOMMAND|MF_GRAYED },
\r
7778 { IDM_NewChat, MF_BYCOMMAND|MF_GRAYED },
\r
7780 // Needed to switch from ncp to GNU mode on Engine Load
\r
7781 { ACTION_POS, MF_BYPOSITION|MF_ENABLED },
\r
7782 { IDM_MachineWhite, MF_BYCOMMAND|MF_ENABLED },
\r
7783 { IDM_MachineBlack, MF_BYCOMMAND|MF_ENABLED },
\r
7784 { IDM_TwoMachines, MF_BYCOMMAND|MF_ENABLED },
\r
7785 { IDM_Match, MF_BYCOMMAND|MF_ENABLED },
\r
7786 { IDM_AnalysisMode, MF_BYCOMMAND|MF_ENABLED },
\r
7787 { IDM_AnalyzeFile, MF_BYCOMMAND|MF_ENABLED },
\r
7788 { IDM_Engine1Options, MF_BYCOMMAND|MF_ENABLED },
\r
7789 { IDM_Engine2Options, MF_BYCOMMAND|MF_ENABLED },
\r
7790 { IDM_TimeControl, MF_BYCOMMAND|MF_ENABLED },
\r
7791 { IDM_RetractMove, MF_BYCOMMAND|MF_ENABLED },
\r
7792 { IDM_MoveNow, MF_BYCOMMAND|MF_ENABLED },
\r
7793 { IDM_Hint, MF_BYCOMMAND|MF_ENABLED },
\r
7794 { IDM_Book, MF_BYCOMMAND|MF_ENABLED },
\r
7798 Enables icsEnables[] = {
\r
7799 { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },
\r
7800 { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },
\r
7801 { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },
\r
7802 { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },
\r
7803 { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },
\r
7804 { IDM_Match, MF_BYCOMMAND|MF_GRAYED },
\r
7805 { IDM_MachineBoth, MF_BYCOMMAND|MF_GRAYED },
\r
7806 { IDM_AnalysisMode, MF_BYCOMMAND|MF_ENABLED },
\r
7807 { IDM_AnalyzeFile, MF_BYCOMMAND|MF_GRAYED },
\r
7808 { IDM_TimeControl, MF_BYCOMMAND|MF_GRAYED },
\r
7809 { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },
\r
7810 { IDM_Hint, MF_BYCOMMAND|MF_GRAYED },
\r
7811 { IDM_Book, MF_BYCOMMAND|MF_GRAYED },
\r
7812 { IDM_EditProgs2, MF_BYCOMMAND|MF_GRAYED },
\r
7813 { IDM_IcsOptions, MF_BYCOMMAND|MF_ENABLED },
\r
7814 { IDM_Engine1Options, MF_BYCOMMAND|MF_GRAYED },
\r
7815 { IDM_Engine2Options, MF_BYCOMMAND|MF_GRAYED },
\r
7816 { IDM_Annotate, MF_BYCOMMAND|MF_GRAYED },
\r
7817 { IDM_Tourney, MF_BYCOMMAND|MF_GRAYED },
\r
7822 Enables zippyEnables[] = {
\r
7823 { IDM_MoveNow, MF_BYCOMMAND|MF_ENABLED },
\r
7824 { IDM_Hint, MF_BYCOMMAND|MF_ENABLED },
\r
7825 { IDM_Book, MF_BYCOMMAND|MF_ENABLED },
\r
7826 { IDM_Engine1Options, MF_BYCOMMAND|MF_ENABLED },
\r
7831 Enables ncpEnables[] = {
\r
7832 { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },
\r
7833 { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },
\r
7834 { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },
\r
7835 { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },
\r
7836 { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },
\r
7837 { IDM_Match, MF_BYCOMMAND|MF_GRAYED },
\r
7838 { IDM_AnalysisMode, MF_BYCOMMAND|MF_GRAYED },
\r
7839 { IDM_AnalyzeFile, MF_BYCOMMAND|MF_GRAYED },
\r
7840 { IDM_IcsClient, MF_BYCOMMAND|MF_GRAYED },
\r
7841 { ACTION_POS, MF_BYPOSITION|MF_GRAYED },
\r
7842 { IDM_Revert, MF_BYCOMMAND|MF_GRAYED },
\r
7843 { IDM_Annotate, MF_BYCOMMAND|MF_GRAYED },
\r
7844 { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },
\r
7845 { IDM_RetractMove, MF_BYCOMMAND|MF_GRAYED },
\r
7846 { IDM_TimeControl, MF_BYCOMMAND|MF_GRAYED },
\r
7847 { IDM_Hint, MF_BYCOMMAND|MF_GRAYED },
\r
7848 { IDM_Book, MF_BYCOMMAND|MF_GRAYED },
\r
7849 { IDM_MachineBoth, MF_BYCOMMAND|MF_GRAYED },
\r
7850 { IDM_NewChat, MF_BYCOMMAND|MF_GRAYED },
\r
7851 { IDM_Engine1Options, MF_BYCOMMAND|MF_GRAYED },
\r
7852 { IDM_Engine2Options, MF_BYCOMMAND|MF_GRAYED },
\r
7853 { IDM_Sounds, MF_BYCOMMAND|MF_GRAYED },
\r
7857 Enables trainingOnEnables[] = {
\r
7858 { IDM_EditComment, MF_BYCOMMAND|MF_GRAYED },
\r
7859 { IDM_Comment, MF_BYCOMMAND|MF_GRAYED },
\r
7860 { IDM_Pause, MF_BYCOMMAND|MF_GRAYED },
\r
7861 { IDM_Forward, MF_BYCOMMAND|MF_GRAYED },
\r
7862 { IDM_Backward, MF_BYCOMMAND|MF_GRAYED },
\r
7863 { IDM_ToEnd, MF_BYCOMMAND|MF_GRAYED },
\r
7864 { IDM_ToStart, MF_BYCOMMAND|MF_GRAYED },
\r
7865 { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },
\r
7866 { IDM_TruncateGame, MF_BYCOMMAND|MF_GRAYED },
\r
7870 Enables trainingOffEnables[] = {
\r
7871 { IDM_EditComment, MF_BYCOMMAND|MF_ENABLED },
\r
7872 { IDM_Comment, MF_BYCOMMAND|MF_ENABLED },
\r
7873 { IDM_Pause, MF_BYCOMMAND|MF_ENABLED },
\r
7874 { IDM_Forward, MF_BYCOMMAND|MF_ENABLED },
\r
7875 { IDM_Backward, MF_BYCOMMAND|MF_ENABLED },
\r
7876 { IDM_ToEnd, MF_BYCOMMAND|MF_ENABLED },
\r
7877 { IDM_ToStart, MF_BYCOMMAND|MF_ENABLED },
\r
7878 { IDM_MoveNow, MF_BYCOMMAND|MF_ENABLED },
\r
7879 { IDM_TruncateGame, MF_BYCOMMAND|MF_ENABLED },
\r
7883 /* These modify either ncpEnables or gnuEnables */
\r
7884 Enables cmailEnables[] = {
\r
7885 { IDM_MailMove, MF_BYCOMMAND|MF_ENABLED },
\r
7886 { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_ENABLED },
\r
7887 { ACTION_POS, MF_BYPOSITION|MF_ENABLED },
\r
7888 { IDM_CallFlag, MF_BYCOMMAND|MF_GRAYED },
\r
7889 { IDM_Draw, MF_BYCOMMAND|MF_ENABLED },
\r
7890 { IDM_Adjourn, MF_BYCOMMAND|MF_GRAYED },
\r
7891 { IDM_Abort, MF_BYCOMMAND|MF_GRAYED },
\r
7895 Enables machineThinkingEnables[] = {
\r
7896 { IDM_LoadGame, MF_BYCOMMAND|MF_GRAYED },
\r
7897 { IDM_LoadNextGame, MF_BYCOMMAND|MF_GRAYED },
\r
7898 { IDM_LoadPrevGame, MF_BYCOMMAND|MF_GRAYED },
\r
7899 { IDM_ReloadGame, MF_BYCOMMAND|MF_GRAYED },
\r
7900 { IDM_PasteGame, MF_BYCOMMAND|MF_GRAYED },
\r
7901 { IDM_LoadPosition, MF_BYCOMMAND|MF_GRAYED },
\r
7902 { IDM_LoadNextPosition, MF_BYCOMMAND|MF_GRAYED },
\r
7903 { IDM_LoadPrevPosition, MF_BYCOMMAND|MF_GRAYED },
\r
7904 { IDM_ReloadPosition, MF_BYCOMMAND|MF_GRAYED },
\r
7905 { IDM_PastePosition, MF_BYCOMMAND|MF_GRAYED },
\r
7906 { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },
\r
7907 { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },
\r
7908 { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },
\r
7909 // { IDM_Match, MF_BYCOMMAND|MF_GRAYED },
\r
7910 { IDM_TypeInMove, MF_BYCOMMAND|MF_GRAYED },
\r
7911 { IDM_RetractMove, MF_BYCOMMAND|MF_GRAYED },
\r
7915 Enables userThinkingEnables[] = {
\r
7916 { IDM_LoadGame, MF_BYCOMMAND|MF_ENABLED },
\r
7917 { IDM_LoadNextGame, MF_BYCOMMAND|MF_ENABLED },
\r
7918 { IDM_LoadPrevGame, MF_BYCOMMAND|MF_ENABLED },
\r
7919 { IDM_ReloadGame, MF_BYCOMMAND|MF_ENABLED },
\r
7920 { IDM_PasteGame, MF_BYCOMMAND|MF_ENABLED },
\r
7921 { IDM_LoadPosition, MF_BYCOMMAND|MF_ENABLED },
\r
7922 { IDM_LoadNextPosition, MF_BYCOMMAND|MF_ENABLED },
\r
7923 { IDM_LoadPrevPosition, MF_BYCOMMAND|MF_ENABLED },
\r
7924 { IDM_ReloadPosition, MF_BYCOMMAND|MF_ENABLED },
\r
7925 { IDM_PastePosition, MF_BYCOMMAND|MF_ENABLED },
\r
7926 { IDM_MachineWhite, MF_BYCOMMAND|MF_ENABLED },
\r
7927 { IDM_MachineBlack, MF_BYCOMMAND|MF_ENABLED },
\r
7928 { IDM_TwoMachines, MF_BYCOMMAND|MF_ENABLED },
\r
7929 // { IDM_Match, MF_BYCOMMAND|MF_ENABLED },
\r
7930 { IDM_TypeInMove, MF_BYCOMMAND|MF_ENABLED },
\r
7931 { IDM_RetractMove, MF_BYCOMMAND|MF_ENABLED },
\r
7935 /*---------------------------------------------------------------------------*\
\r
7937 * Front-end interface functions exported by XBoard.
\r
7938 * Functions appear in same order as prototypes in frontend.h.
\r
7940 \*---------------------------------------------------------------------------*/
\r
7942 CheckMark(UINT item, int state)
\r
7944 if(item) CheckMenuItem(GetMenu(hwndMain), item, MF_BYCOMMAND|state);
\r
7950 static UINT prevChecked = 0;
\r
7951 static int prevPausing = 0;
\r
7954 if (pausing != prevPausing) {
\r
7955 prevPausing = pausing;
\r
7956 (void) CheckMenuItem(GetMenu(hwndMain), IDM_Pause,
\r
7957 MF_BYCOMMAND|(pausing ? MF_CHECKED : MF_UNCHECKED));
\r
7958 if (hwndPause) SetWindowText(hwndPause, pausing ? "C" : "P");
\r
7961 switch (gameMode) {
\r
7962 case BeginningOfGame:
\r
7963 if (appData.icsActive)
\r
7964 nowChecked = IDM_IcsClient;
\r
7965 else if (appData.noChessProgram)
\r
7966 nowChecked = IDM_EditGame;
\r
7968 nowChecked = IDM_MachineBlack;
\r
7970 case MachinePlaysBlack:
\r
7971 nowChecked = IDM_MachineBlack;
\r
7973 case MachinePlaysWhite:
\r
7974 nowChecked = IDM_MachineWhite;
\r
7976 case TwoMachinesPlay:
\r
7977 nowChecked = IDM_TwoMachines;
\r
7980 nowChecked = IDM_AnalysisMode;
\r
7983 nowChecked = IDM_AnalyzeFile;
\r
7986 nowChecked = IDM_EditGame;
\r
7988 case PlayFromGameFile:
\r
7989 nowChecked = IDM_LoadGame;
\r
7991 case EditPosition:
\r
7992 nowChecked = IDM_EditPosition;
\r
7995 nowChecked = IDM_Training;
\r
7997 case IcsPlayingWhite:
\r
7998 case IcsPlayingBlack:
\r
7999 case IcsObserving:
\r
8001 nowChecked = IDM_IcsClient;
\r
8008 CheckMark(prevChecked, MF_UNCHECKED);
\r
8009 CheckMark(nowChecked, MF_CHECKED);
\r
8010 CheckMark(IDM_Match, matchMode && matchGame < appData.matchGames ? MF_CHECKED : MF_UNCHECKED);
\r
8012 if (nowChecked == IDM_LoadGame || nowChecked == IDM_Training) {
\r
8013 (void) EnableMenuItem(GetMenu(hwndMain), IDM_Training,
\r
8014 MF_BYCOMMAND|MF_ENABLED);
\r
8016 (void) EnableMenuItem(GetMenu(hwndMain),
\r
8017 IDM_Training, MF_BYCOMMAND|MF_GRAYED);
\r
8020 prevChecked = nowChecked;
\r
8022 /* [DM] icsEngineAnalyze - Do a sceure check too */
\r
8023 if (appData.icsActive) {
\r
8024 if (appData.icsEngineAnalyze) {
\r
8025 CheckMark(IDM_AnalysisMode, MF_CHECKED);
\r
8027 CheckMark(IDM_AnalysisMode, MF_UNCHECKED);
\r
8030 DisplayLogos(); // [HGM] logos: mode change could have altered logos
\r
8036 HMENU hmenu = GetMenu(hwndMain);
\r
8037 SetMenuEnables(hmenu, icsEnables);
\r
8038 EnableMenuItem(GetSubMenu(hmenu, OPTIONS_POS), IDM_IcsOptions,
\r
8039 MF_BYCOMMAND|MF_ENABLED);
\r
8041 if (appData.zippyPlay) {
\r
8042 SetMenuEnables(hmenu, zippyEnables);
\r
8043 if (!appData.noChessProgram) /* [DM] icsEngineAnalyze */
\r
8044 (void) EnableMenuItem(GetMenu(hwndMain), IDM_AnalysisMode,
\r
8045 MF_BYCOMMAND|MF_ENABLED);
\r
8053 SetMenuEnables(GetMenu(hwndMain), gnuEnables);
\r
8059 HMENU hmenu = GetMenu(hwndMain);
\r
8060 SetMenuEnables(hmenu, ncpEnables);
\r
8061 DrawMenuBar(hwndMain);
\r
8067 SetMenuEnables(GetMenu(hwndMain), cmailEnables);
\r
8071 SetTrainingModeOn()
\r
8074 SetMenuEnables(GetMenu(hwndMain), trainingOnEnables);
\r
8075 for (i = 0; i < N_BUTTONS; i++) {
\r
8076 if (buttonDesc[i].hwnd != NULL)
\r
8077 EnableWindow(buttonDesc[i].hwnd, FALSE);
\r
8082 VOID SetTrainingModeOff()
\r
8085 SetMenuEnables(GetMenu(hwndMain), trainingOffEnables);
\r
8086 for (i = 0; i < N_BUTTONS; i++) {
\r
8087 if (buttonDesc[i].hwnd != NULL)
\r
8088 EnableWindow(buttonDesc[i].hwnd, TRUE);
\r
8094 SetUserThinkingEnables()
\r
8096 SetMenuEnables(GetMenu(hwndMain), userThinkingEnables);
\r
8100 SetMachineThinkingEnables()
\r
8102 HMENU hMenu = GetMenu(hwndMain);
\r
8103 int flags = MF_BYCOMMAND|MF_ENABLED;
\r
8105 SetMenuEnables(hMenu, machineThinkingEnables);
\r
8107 if (gameMode == MachinePlaysBlack) {
\r
8108 (void)EnableMenuItem(hMenu, IDM_MachineBlack, flags);
\r
8109 } else if (gameMode == MachinePlaysWhite) {
\r
8110 (void)EnableMenuItem(hMenu, IDM_MachineWhite, flags);
\r
8111 } else if (gameMode == TwoMachinesPlay) {
\r
8112 (void)EnableMenuItem(hMenu, matchMode ? IDM_Match : IDM_TwoMachines, flags); // [HGM] match
\r
8118 DisplayTitle(char *str)
\r
8120 char title[MSG_SIZ], *host;
\r
8121 if (str[0] != NULLCHAR) {
\r
8122 safeStrCpy(title, str, sizeof(title)/sizeof(title[0]) );
\r
8123 } else if (appData.icsActive) {
\r
8124 if (appData.icsCommPort[0] != NULLCHAR)
\r
8127 host = appData.icsHost;
\r
8128 snprintf(title, MSG_SIZ, "%s: %s", szTitle, host);
\r
8129 } else if (appData.noChessProgram) {
\r
8130 safeStrCpy(title, szTitle, sizeof(title)/sizeof(title[0]) );
\r
8132 safeStrCpy(title, szTitle, sizeof(title)/sizeof(title[0]) );
\r
8133 strcat(title, ": ");
\r
8134 strcat(title, first.tidy);
\r
8136 SetWindowText(hwndMain, title);
\r
8141 DisplayMessage(char *str1, char *str2)
\r
8145 int remain = MESSAGE_TEXT_MAX - 1;
\r
8148 moveErrorMessageUp = FALSE; /* turned on later by caller if needed */
\r
8149 messageText[0] = NULLCHAR;
\r
8151 len = strlen(str1);
\r
8152 if (len > remain) len = remain;
\r
8153 strncpy(messageText, str1, len);
\r
8154 messageText[len] = NULLCHAR;
\r
8157 if (*str2 && remain >= 2) {
\r
8159 strcat(messageText, " ");
\r
8162 len = strlen(str2);
\r
8163 if (len > remain) len = remain;
\r
8164 strncat(messageText, str2, len);
\r
8166 messageText[MESSAGE_TEXT_MAX - 1] = NULLCHAR;
8167 safeStrCpy(lastMsg, messageText, MSG_SIZ);
8169 if (hwndMain == NULL || IsIconic(hwndMain)) return;
\r
8173 hdc = GetDC(hwndMain);
\r
8174 oldFont = SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);
\r
8175 ExtTextOut(hdc, messageRect.left, messageRect.top, ETO_CLIPPED|ETO_OPAQUE,
\r
8176 &messageRect, messageText, strlen(messageText), NULL);
\r
8177 (void) SelectObject(hdc, oldFont);
\r
8178 (void) ReleaseDC(hwndMain, hdc);
\r
8182 DisplayError(char *str, int error)
\r
8184 char buf[MSG_SIZ*2], buf2[MSG_SIZ];
\r
8188 safeStrCpy(buf, str, sizeof(buf)/sizeof(buf[0]) );
\r
8190 len = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,
\r
8191 NULL, error, LANG_NEUTRAL,
\r
8192 (LPSTR) buf2, MSG_SIZ, NULL);
\r
8194 snprintf(buf, 2*MSG_SIZ, "%s:\n%s", str, buf2);
\r
8196 ErrorMap *em = errmap;
\r
8197 while (em->err != 0 && em->err != error) em++;
\r
8198 if (em->err != 0) {
\r
8199 snprintf(buf, 2*MSG_SIZ, "%s:\n%s", str, em->msg);
\r
8201 snprintf(buf, 2*MSG_SIZ, "%s:\nError code %d", str, error);
\r
8206 ErrorPopUp(_("Error"), buf);
\r
8211 DisplayMoveError(char *str)
\r
8213 fromX = fromY = -1;
\r
8214 ClearHighlights();
\r
8215 DrawPosition(FALSE, NULL);
\r
8216 if (appData.popupMoveErrors) {
\r
8217 ErrorPopUp(_("Error"), str);
\r
8219 DisplayMessage(str, "");
\r
8220 moveErrorMessageUp = TRUE;
\r
8225 DisplayFatalError(char *str, int error, int exitStatus)
\r
8227 char buf[2*MSG_SIZ], buf2[MSG_SIZ];
\r
8229 char *label = exitStatus ? _("Fatal Error") : _("Exiting");
\r
8232 len = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,
\r
8233 NULL, error, LANG_NEUTRAL,
\r
8234 (LPSTR) buf2, MSG_SIZ, NULL);
\r
8236 snprintf(buf, 2*MSG_SIZ, "%s:\n%s", str, buf2);
\r
8238 ErrorMap *em = errmap;
\r
8239 while (em->err != 0 && em->err != error) em++;
\r
8240 if (em->err != 0) {
\r
8241 snprintf(buf, 2*MSG_SIZ, "%s:\n%s", str, em->msg);
\r
8243 snprintf(buf, 2*MSG_SIZ, "%s:\nError code %d", str, error);
\r
8248 if (appData.debugMode) {
\r
8249 fprintf(debugFP, "%s: %s\n", label, str);
\r
8251 if (appData.popupExitMessage) {
\r
8252 (void) MessageBox(hwndMain, str, label, MB_OK|
\r
8253 (exitStatus ? MB_ICONSTOP : MB_ICONINFORMATION));
\r
8255 ExitEvent(exitStatus);
\r
8260 DisplayInformation(char *str)
\r
8262 (void) MessageBox(hwndMain, str, _("Information"), MB_OK|MB_ICONINFORMATION);
\r
8267 DisplayNote(char *str)
\r
8269 ErrorPopUp(_("Note"), str);
\r
8274 char *title, *question, *replyPrefix;
\r
8279 QuestionDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
8281 static QuestionParams *qp;
\r
8282 char reply[MSG_SIZ];
\r
8285 switch (message) {
\r
8286 case WM_INITDIALOG:
\r
8287 qp = (QuestionParams *) lParam;
\r
8288 CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));
\r
8289 Translate(hDlg, DLG_Question);
\r
8290 SetWindowText(hDlg, qp->title);
\r
8291 SetDlgItemText(hDlg, OPT_QuestionText, qp->question);
\r
8292 SetFocus(GetDlgItem(hDlg, OPT_QuestionInput));
\r
8296 switch (LOWORD(wParam)) {
\r
8298 safeStrCpy(reply, qp->replyPrefix, sizeof(reply)/sizeof(reply[0]) );
\r
8299 if (*reply) strcat(reply, " ");
\r
8300 len = strlen(reply);
\r
8301 GetDlgItemText(hDlg, OPT_QuestionInput, reply + len, sizeof(reply) - len);
\r
8302 strcat(reply, "\n");
\r
8303 OutputToProcess(qp->pr, reply, strlen(reply), &err);
\r
8304 EndDialog(hDlg, TRUE);
\r
8305 if (err) DisplayFatalError(_("Error writing to chess program"), err, 1);
\r
8308 EndDialog(hDlg, FALSE);
\r
8319 AskQuestion(char* title, char *question, char *replyPrefix, ProcRef pr)
\r
8321 QuestionParams qp;
\r
8325 qp.question = question;
\r
8326 qp.replyPrefix = replyPrefix;
\r
8328 lpProc = MakeProcInstance((FARPROC)QuestionDialog, hInst);
\r
8329 DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_Question),
\r
8330 hwndMain, (DLGPROC)lpProc, (LPARAM)&qp);
\r
8331 FreeProcInstance(lpProc);
\r
8334 /* [AS] Pick FRC position */
\r
8335 LRESULT CALLBACK NewGameFRC_Proc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
8337 static int * lpIndexFRC;
\r
8343 case WM_INITDIALOG:
\r
8344 lpIndexFRC = (int *) lParam;
\r
8346 CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));
\r
8347 Translate(hDlg, DLG_NewGameFRC);
\r
8349 SendDlgItemMessage( hDlg, IDC_NFG_Edit, EM_SETLIMITTEXT, sizeof(buf)-1, 0 );
\r
8350 SetDlgItemInt( hDlg, IDC_NFG_Edit, *lpIndexFRC, TRUE );
\r
8351 SendDlgItemMessage( hDlg, IDC_NFG_Edit, EM_SETSEL, 0, -1 );
\r
8352 SetFocus(GetDlgItem(hDlg, IDC_NFG_Edit));
\r
8357 switch( LOWORD(wParam) ) {
\r
8359 *lpIndexFRC = GetDlgItemInt(hDlg, IDC_NFG_Edit, &index_is_ok, TRUE );
\r
8360 EndDialog( hDlg, 0 );
\r
8361 shuffleOpenings = TRUE; /* [HGM] shuffle: switch shuffling on for as long as we stay in current variant */
\r
8364 EndDialog( hDlg, 1 );
\r
8366 case IDC_NFG_Edit:
\r
8367 if( HIWORD(wParam) == EN_CHANGE ) {
\r
8368 GetDlgItemInt(hDlg, IDC_NFG_Edit, &index_is_ok, TRUE );
\r
8370 EnableWindow( GetDlgItem(hDlg, IDOK), index_is_ok );
\r
8373 case IDC_NFG_Random:
\r
8374 snprintf( buf, sizeof(buf)/sizeof(buf[0]), "%d", myrandom() ); /* [HGM] shuffle: no longer limit to 960 */
\r
8375 SetDlgItemText(hDlg, IDC_NFG_Edit, buf );
\r
8388 int index = appData.defaultFrcPosition;
\r
8389 FARPROC lpProc = MakeProcInstance( (FARPROC) NewGameFRC_Proc, hInst );
\r
8391 result = DialogBoxParam( hInst, MAKEINTRESOURCE(DLG_NewGameFRC), hwndMain, (DLGPROC)lpProc, (LPARAM)&index );
\r
8393 if( result == 0 ) {
\r
8394 appData.defaultFrcPosition = index;
\r
8400 /* [AS] Game list options. Refactored by HGM */
\r
8402 HWND gameListOptionsDialog;
\r
8404 // low-level front-end: clear text edit / list widget
\r
8408 SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_RESETCONTENT, 0, 0 );
\r
8411 // low-level front-end: clear text edit / list widget
\r
8413 GLT_DeSelectList()
\r
8415 SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_SETCURSEL, 0, 0 );
\r
8418 // low-level front-end: append line to text edit / list widget
\r
8420 GLT_AddToList( char *name )
\r
8423 SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_ADDSTRING, 0, (LPARAM) name );
\r
8427 // low-level front-end: get line from text edit / list widget
\r
8429 GLT_GetFromList( int index, char *name )
\r
8432 if( SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_GETTEXT, index, (LPARAM) name ) != LB_ERR )
\r
8438 void GLT_MoveSelection( HWND hDlg, int delta )
\r
8440 int idx1 = (int) SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETCURSEL, 0, 0 );
\r
8441 int idx2 = idx1 + delta;
\r
8442 int count = (int) SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETCOUNT, 0, 0 );
\r
8444 if( idx1 >=0 && idx1 < count && idx2 >= 0 && idx2 < count ) {
\r
8447 SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETTEXT, idx1, (LPARAM) buf );
\r
8448 SendDlgItemMessage( hDlg, IDC_GameListTags, LB_DELETESTRING, idx1, 0 );
\r
8449 SendDlgItemMessage( hDlg, IDC_GameListTags, LB_INSERTSTRING, idx2, (LPARAM) buf );
\r
8450 SendDlgItemMessage( hDlg, IDC_GameListTags, LB_SETCURSEL, idx2, 0 );
\r
8454 LRESULT CALLBACK GameListOptions_Proc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
8458 case WM_INITDIALOG:
\r
8459 gameListOptionsDialog = hDlg; // [HGM] pass through global to keep out off back-end
\r
8461 CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));
\r
8462 Translate(hDlg, DLG_GameListOptions);
\r
8464 /* Initialize list */
\r
8465 GLT_TagsToList( lpUserGLT );
\r
8467 SetFocus( GetDlgItem(hDlg, IDC_GameListTags) );
\r
8472 switch( LOWORD(wParam) ) {
\r
8475 EndDialog( hDlg, 0 );
\r
8478 EndDialog( hDlg, 1 );
\r
8481 case IDC_GLT_Default:
\r
8482 GLT_TagsToList( GLT_DEFAULT_TAGS );
\r
8485 case IDC_GLT_Restore:
\r
8486 GLT_TagsToList( appData.gameListTags );
\r
8490 GLT_MoveSelection( hDlg, -1 );
\r
8493 case IDC_GLT_Down:
\r
8494 GLT_MoveSelection( hDlg, +1 );
\r
8504 int GameListOptions()
\r
8507 FARPROC lpProc = MakeProcInstance( (FARPROC) GameListOptions_Proc, hInst );
\r
8509 safeStrCpy( lpUserGLT, appData.gameListTags ,LPUSERGLT_SIZE );
\r
8511 result = DialogBoxParam( hInst, MAKEINTRESOURCE(DLG_GameListOptions), hwndMain, (DLGPROC)lpProc, (LPARAM)lpUserGLT );
\r
8513 if( result == 0 ) {
\r
8514 /* [AS] Memory leak here! */
\r
8515 appData.gameListTags = strdup( lpUserGLT );
\r
8522 DisplayIcsInteractionTitle(char *str)
\r
8524 char consoleTitle[MSG_SIZ];
\r
8526 snprintf(consoleTitle, MSG_SIZ, "%s: %s", szConsoleTitle, str);
\r
8527 SetWindowText(hwndConsole, consoleTitle);
\r
8531 DrawPosition(int fullRedraw, Board board)
\r
8533 HDCDrawPosition(NULL, (BOOLEAN) fullRedraw, board);
\r
8536 void NotifyFrontendLogin()
\r
8539 UpdateICSWidth(GetDlgItem(hwndConsole, OPT_ConsoleText));
\r
8545 fromX = fromY = -1;
\r
8546 if (dragInfo.pos.x != -1 || dragInfo.pos.y != -1) {
\r
8547 dragInfo.pos.x = dragInfo.pos.y = -1;
\r
8548 dragInfo.pos.x = dragInfo.pos.y = -1;
\r
8549 dragInfo.lastpos = dragInfo.pos;
\r
8550 dragInfo.start.x = dragInfo.start.y = -1;
\r
8551 dragInfo.from = dragInfo.start;
\r
8553 DrawPosition(TRUE, NULL);
\r
8560 CommentPopUp(char *title, char *str)
\r
8562 HWND hwnd = GetActiveWindow();
\r
8563 EitherCommentPopUp(currentMove, title, str, FALSE); // [HGM] vari: fake move index, rather than 0
\r
8565 SetActiveWindow(hwnd);
\r
8569 CommentPopDown(void)
\r
8571 CheckMenuItem(GetMenu(hwndMain), IDM_Comment, MF_UNCHECKED);
\r
8572 if (commentDialog) {
\r
8573 ShowWindow(commentDialog, SW_HIDE);
\r
8575 commentUp = FALSE;
\r
8579 EditCommentPopUp(int index, char *title, char *str)
\r
8581 EitherCommentPopUp(index, title, str, TRUE);
\r
8588 MyPlaySound(&sounds[(int)SoundMove]);
\r
8591 VOID PlayIcsWinSound()
\r
8593 MyPlaySound(&sounds[(int)SoundIcsWin]);
\r
8596 VOID PlayIcsLossSound()
\r
8598 MyPlaySound(&sounds[(int)SoundIcsLoss]);
\r
8601 VOID PlayIcsDrawSound()
\r
8603 MyPlaySound(&sounds[(int)SoundIcsDraw]);
\r
8606 VOID PlayIcsUnfinishedSound()
\r
8608 MyPlaySound(&sounds[(int)SoundIcsUnfinished]);
\r
8614 MyPlaySound(&sounds[(int)SoundAlarm]);
\r
8620 MyPlaySound(&textAttribs[ColorTell].sound);
\r
8628 consoleEcho = TRUE;
\r
8629 hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
8630 SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&consoleCF);
\r
8631 SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);
\r
8640 consoleEcho = FALSE;
\r
8641 hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
8642 /* This works OK: set text and background both to the same color */
\r
8644 cf.crTextColor = COLOR_ECHOOFF;
\r
8645 SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf);
\r
8646 SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, cf.crTextColor);
\r
8649 /* No Raw()...? */
\r
8651 void Colorize(ColorClass cc, int continuation)
\r
8653 currentColorClass = cc;
\r
8654 consoleCF.dwMask = CFM_COLOR|CFM_BOLD|CFM_ITALIC|CFM_UNDERLINE|CFM_STRIKEOUT;
\r
8655 consoleCF.crTextColor = textAttribs[cc].color;
\r
8656 consoleCF.dwEffects = textAttribs[cc].effects;
\r
8657 if (!continuation) MyPlaySound(&textAttribs[cc].sound);
\r
8663 static char buf[MSG_SIZ];
\r
8664 DWORD bufsiz = MSG_SIZ;
\r
8666 if(appData.userName != NULL && appData.userName[0] != 0) {
\r
8667 return appData.userName; /* [HGM] username: prefer name selected by user over his system login */
\r
8669 if (!GetUserName(buf, &bufsiz)) {
\r
8670 /*DisplayError("Error getting user name", GetLastError());*/
\r
8671 safeStrCpy(buf, _("User"), sizeof(buf)/sizeof(buf[0]) );
\r
8679 static char buf[MSG_SIZ];
\r
8680 DWORD bufsiz = MSG_SIZ;
\r
8682 if (!GetComputerName(buf, &bufsiz)) {
\r
8683 /*DisplayError("Error getting host name", GetLastError());*/
\r
8684 safeStrCpy(buf, _("Unknown"), sizeof(buf)/sizeof(buf[0]) );
\r
8691 ClockTimerRunning()
\r
8693 return clockTimerEvent != 0;
\r
8699 if (clockTimerEvent == 0) return FALSE;
\r
8700 KillTimer(hwndMain, clockTimerEvent);
\r
8701 clockTimerEvent = 0;
\r
8706 StartClockTimer(long millisec)
\r
8708 clockTimerEvent = SetTimer(hwndMain, (UINT) CLOCK_TIMER_ID,
\r
8709 (UINT) millisec, NULL);
\r
8713 DisplayWhiteClock(long timeRemaining, int highlight)
\r
8716 char *flag = whiteFlag && gameMode == TwoMachinesPlay ? "(!)" : "";
\r
8718 if(appData.noGUI) return;
\r
8719 hdc = GetDC(hwndMain);
\r
8720 if (!IsIconic(hwndMain)) {
\r
8721 DisplayAClock(hdc, timeRemaining, highlight,
\r
8722 flipClock ? &blackRect : &whiteRect, _("White"), flag);
\r
8724 if (highlight && iconCurrent == iconBlack) {
\r
8725 iconCurrent = iconWhite;
\r
8726 PostMessage(hwndMain, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);
\r
8727 if (IsIconic(hwndMain)) {
\r
8728 DrawIcon(hdc, 2, 2, iconCurrent);
\r
8731 (void) ReleaseDC(hwndMain, hdc);
\r
8733 PostMessage(hwndConsole, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);
\r
8737 DisplayBlackClock(long timeRemaining, int highlight)
\r
8740 char *flag = blackFlag && gameMode == TwoMachinesPlay ? "(!)" : "";
\r
8742 if(appData.noGUI) return;
\r
8743 hdc = GetDC(hwndMain);
\r
8744 if (!IsIconic(hwndMain)) {
\r
8745 DisplayAClock(hdc, timeRemaining, highlight,
\r
8746 flipClock ? &whiteRect : &blackRect, _("Black"), flag);
\r
8748 if (highlight && iconCurrent == iconWhite) {
\r
8749 iconCurrent = iconBlack;
\r
8750 PostMessage(hwndMain, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);
\r
8751 if (IsIconic(hwndMain)) {
\r
8752 DrawIcon(hdc, 2, 2, iconCurrent);
\r
8755 (void) ReleaseDC(hwndMain, hdc);
\r
8757 PostMessage(hwndConsole, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);
\r
8762 LoadGameTimerRunning()
\r
8764 return loadGameTimerEvent != 0;
\r
8768 StopLoadGameTimer()
\r
8770 if (loadGameTimerEvent == 0) return FALSE;
\r
8771 KillTimer(hwndMain, loadGameTimerEvent);
\r
8772 loadGameTimerEvent = 0;
\r
8777 StartLoadGameTimer(long millisec)
\r
8779 loadGameTimerEvent = SetTimer(hwndMain, (UINT) LOAD_GAME_TIMER_ID,
\r
8780 (UINT) millisec, NULL);
\r
8788 char fileTitle[MSG_SIZ];
\r
8790 defName = DefaultFileName(appData.oldSaveStyle ? "gam" : "pgn");
\r
8791 f = OpenFileDialog(hwndMain, "a", defName,
\r
8792 appData.oldSaveStyle ? "gam" : "pgn",
\r
8794 _("Save Game to File"), NULL, fileTitle, NULL);
\r
8796 SaveGame(f, 0, "");
\r
8803 ScheduleDelayedEvent(DelayedEventCallback cb, long millisec)
\r
8805 if (delayedTimerEvent != 0) {
\r
8806 if (appData.debugMode && cb != delayedTimerCallback) { // [HGM] alive: not too much debug
\r
8807 fprintf(debugFP, "ScheduleDelayedEvent: event already scheduled\n");
\r
8809 KillTimer(hwndMain, delayedTimerEvent);
\r
8810 delayedTimerEvent = 0;
\r
8811 if(delayedTimerCallback != cb) // [HGM] alive: do not "flush" same event, just postpone it
\r
8812 delayedTimerCallback();
\r
8814 delayedTimerCallback = cb;
\r
8815 delayedTimerEvent = SetTimer(hwndMain, (UINT) DELAYED_TIMER_ID,
\r
8816 (UINT) millisec, NULL);
\r
8819 DelayedEventCallback
\r
8822 if (delayedTimerEvent) {
\r
8823 return delayedTimerCallback;
\r
8830 CancelDelayedEvent()
\r
8832 if (delayedTimerEvent) {
\r
8833 KillTimer(hwndMain, delayedTimerEvent);
\r
8834 delayedTimerEvent = 0;
\r
8838 DWORD GetWin32Priority(int nice)
\r
8839 { // [HGM] nice: translate Unix nice() value to indows priority class. (Code stolen from Polyglot 1.4w11)
\r
8841 REALTIME_PRIORITY_CLASS 0x00000100
\r
8842 HIGH_PRIORITY_CLASS 0x00000080
\r
8843 ABOVE_NORMAL_PRIORITY_CLASS 0x00008000
\r
8844 NORMAL_PRIORITY_CLASS 0x00000020
\r
8845 BELOW_NORMAL_PRIORITY_CLASS 0x00004000
\r
8846 IDLE_PRIORITY_CLASS 0x00000040
\r
8848 if (nice < -15) return 0x00000080;
\r
8849 if (nice < 0) return 0x00008000;
\r
8850 if (nice == 0) return 0x00000020;
\r
8851 if (nice < 15) return 0x00004000;
\r
8852 return 0x00000040;
\r
8855 /* Start a child process running the given program.
\r
8856 The process's standard output can be read from "from", and its
\r
8857 standard input can be written to "to".
\r
8858 Exit with fatal error if anything goes wrong.
\r
8859 Returns an opaque pointer that can be used to destroy the process
\r
8863 StartChildProcess(char *cmdLine, char *dir, ProcRef *pr)
\r
8865 #define BUFSIZE 4096
\r
8867 HANDLE hChildStdinRd, hChildStdinWr,
\r
8868 hChildStdoutRd, hChildStdoutWr;
\r
8869 HANDLE hChildStdinWrDup, hChildStdoutRdDup;
\r
8870 SECURITY_ATTRIBUTES saAttr;
\r
8872 PROCESS_INFORMATION piProcInfo;
\r
8873 STARTUPINFO siStartInfo;
\r
8875 char buf[MSG_SIZ];
\r
8878 if (appData.debugMode) {
\r
8879 fprintf(debugFP, "StartChildProcess (dir=\"%s\") %s\n", dir, cmdLine);
\r
8884 /* Set the bInheritHandle flag so pipe handles are inherited. */
\r
8885 saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);
\r
8886 saAttr.bInheritHandle = TRUE;
\r
8887 saAttr.lpSecurityDescriptor = NULL;
\r
8890 * The steps for redirecting child's STDOUT:
\r
8891 * 1. Create anonymous pipe to be STDOUT for child.
\r
8892 * 2. Create a noninheritable duplicate of read handle,
\r
8893 * and close the inheritable read handle.
\r
8896 /* Create a pipe for the child's STDOUT. */
\r
8897 if (! CreatePipe(&hChildStdoutRd, &hChildStdoutWr, &saAttr, 0)) {
\r
8898 return GetLastError();
\r
8901 /* Duplicate the read handle to the pipe, so it is not inherited. */
\r
8902 fSuccess = DuplicateHandle(GetCurrentProcess(), hChildStdoutRd,
\r
8903 GetCurrentProcess(), &hChildStdoutRdDup, 0,
\r
8904 FALSE, /* not inherited */
\r
8905 DUPLICATE_SAME_ACCESS);
\r
8907 return GetLastError();
\r
8909 CloseHandle(hChildStdoutRd);
\r
8912 * The steps for redirecting child's STDIN:
\r
8913 * 1. Create anonymous pipe to be STDIN for child.
\r
8914 * 2. Create a noninheritable duplicate of write handle,
\r
8915 * and close the inheritable write handle.
\r
8918 /* Create a pipe for the child's STDIN. */
\r
8919 if (! CreatePipe(&hChildStdinRd, &hChildStdinWr, &saAttr, 0)) {
\r
8920 return GetLastError();
\r
8923 /* Duplicate the write handle to the pipe, so it is not inherited. */
\r
8924 fSuccess = DuplicateHandle(GetCurrentProcess(), hChildStdinWr,
\r
8925 GetCurrentProcess(), &hChildStdinWrDup, 0,
\r
8926 FALSE, /* not inherited */
\r
8927 DUPLICATE_SAME_ACCESS);
\r
8929 return GetLastError();
\r
8931 CloseHandle(hChildStdinWr);
\r
8933 /* Arrange to (1) look in dir for the child .exe file, and
\r
8934 * (2) have dir be the child's working directory. Interpret
\r
8935 * dir relative to the directory WinBoard loaded from. */
\r
8936 GetCurrentDirectory(MSG_SIZ, buf);
\r
8937 SetCurrentDirectory(installDir);
\r
8938 SetCurrentDirectory(dir);
\r
8940 /* Now create the child process. */
\r
8942 siStartInfo.cb = sizeof(STARTUPINFO);
\r
8943 siStartInfo.lpReserved = NULL;
\r
8944 siStartInfo.lpDesktop = NULL;
\r
8945 siStartInfo.lpTitle = NULL;
\r
8946 siStartInfo.dwFlags = STARTF_USESTDHANDLES;
\r
8947 siStartInfo.cbReserved2 = 0;
\r
8948 siStartInfo.lpReserved2 = NULL;
\r
8949 siStartInfo.hStdInput = hChildStdinRd;
\r
8950 siStartInfo.hStdOutput = hChildStdoutWr;
\r
8951 siStartInfo.hStdError = hChildStdoutWr;
\r
8953 fSuccess = CreateProcess(NULL,
\r
8954 cmdLine, /* command line */
\r
8955 NULL, /* process security attributes */
\r
8956 NULL, /* primary thread security attrs */
\r
8957 TRUE, /* handles are inherited */
\r
8958 DETACHED_PROCESS|CREATE_NEW_PROCESS_GROUP,
\r
8959 NULL, /* use parent's environment */
\r
8961 &siStartInfo, /* STARTUPINFO pointer */
\r
8962 &piProcInfo); /* receives PROCESS_INFORMATION */
\r
8964 err = GetLastError();
\r
8965 SetCurrentDirectory(buf); /* return to prev directory */
\r
8970 if (appData.niceEngines){ // [HGM] nice: adjust engine proc priority
\r
8971 if(appData.debugMode) fprintf(debugFP, "nice engine proc to %d\n", appData.niceEngines);
\r
8972 SetPriorityClass(piProcInfo.hProcess, GetWin32Priority(appData.niceEngines));
\r
8975 /* Close the handles we don't need in the parent */
\r
8976 CloseHandle(piProcInfo.hThread);
\r
8977 CloseHandle(hChildStdinRd);
\r
8978 CloseHandle(hChildStdoutWr);
\r
8980 /* Prepare return value */
\r
8981 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
\r
8982 cp->kind = CPReal;
\r
8983 cp->hProcess = piProcInfo.hProcess;
\r
8984 cp->pid = piProcInfo.dwProcessId;
\r
8985 cp->hFrom = hChildStdoutRdDup;
\r
8986 cp->hTo = hChildStdinWrDup;
\r
8988 *pr = (void *) cp;
\r
8990 /* Klaus Friedel says that this Sleep solves a problem under Windows
\r
8991 2000 where engines sometimes don't see the initial command(s)
\r
8992 from WinBoard and hang. I don't understand how that can happen,
\r
8993 but the Sleep is harmless, so I've put it in. Others have also
\r
8994 reported what may be the same problem, so hopefully this will fix
\r
8995 it for them too. */
\r
9003 DestroyChildProcess(ProcRef pr, int/*boolean*/ signal)
\r
9005 ChildProc *cp; int result;
\r
9007 cp = (ChildProc *) pr;
\r
9008 if (cp == NULL) return;
\r
9010 switch (cp->kind) {
\r
9012 /* TerminateProcess is considered harmful, so... */
\r
9013 CloseHandle(cp->hTo); /* Closing this will give the child an EOF and hopefully kill it */
\r
9014 if (cp->hFrom) CloseHandle(cp->hFrom); /* if NULL, InputThread will close it */
\r
9015 /* The following doesn't work because the chess program
\r
9016 doesn't "have the same console" as WinBoard. Maybe
\r
9017 we could arrange for this even though neither WinBoard
\r
9018 nor the chess program uses a console for stdio? */
\r
9019 /*!!if (signal) GenerateConsoleCtrlEvent(CTRL_BREAK_EVENT, cp->pid);*/
\r
9021 /* [AS] Special termination modes for misbehaving programs... */
\r
9022 if( signal == 9 ) {
\r
9023 result = TerminateProcess( cp->hProcess, 0 );
\r
9025 if ( appData.debugMode) {
\r
9026 fprintf( debugFP, "Terminating process %lu, result=%d\n", cp->pid, result );
\r
9029 else if( signal == 10 ) {
\r
9030 DWORD dw = WaitForSingleObject( cp->hProcess, 3*1000 ); // Wait 3 seconds at most
\r
9032 if( dw != WAIT_OBJECT_0 ) {
\r
9033 result = TerminateProcess( cp->hProcess, 0 );
\r
9035 if ( appData.debugMode) {
\r
9036 fprintf( debugFP, "Process %lu still alive after timeout, killing... result=%d\n", cp->pid, result );
\r
9042 CloseHandle(cp->hProcess);
\r
9046 if (cp->hFrom) CloseHandle(cp->hFrom);
\r
9050 closesocket(cp->sock);
\r
9055 if (signal) send(cp->sock2, "\017", 1, 0); /* 017 = 15 = SIGTERM */
\r
9056 closesocket(cp->sock);
\r
9057 closesocket(cp->sock2);
\r
9065 InterruptChildProcess(ProcRef pr)
\r
9069 cp = (ChildProc *) pr;
\r
9070 if (cp == NULL) return;
\r
9071 switch (cp->kind) {
\r
9073 /* The following doesn't work because the chess program
\r
9074 doesn't "have the same console" as WinBoard. Maybe
\r
9075 we could arrange for this even though neither WinBoard
\r
9076 nor the chess program uses a console for stdio */
\r
9077 /*!!GenerateConsoleCtrlEvent(CTRL_C_EVENT, cp->pid);*/
\r
9082 /* Can't interrupt */
\r
9086 send(cp->sock2, "\002", 1, 0); /* 2 = SIGINT */
\r
9093 OpenTelnet(char *host, char *port, ProcRef *pr)
\r
9095 char cmdLine[MSG_SIZ];
\r
9097 if (port[0] == NULLCHAR) {
\r
9098 snprintf(cmdLine, MSG_SIZ, "%s %s", appData.telnetProgram, host);
\r
9100 snprintf(cmdLine, MSG_SIZ, "%s %s %s", appData.telnetProgram, host, port);
\r
9102 return StartChildProcess(cmdLine, "", pr);
\r
9106 /* Code to open TCP sockets */
\r
9109 OpenTCP(char *host, char *port, ProcRef *pr)
\r
9114 struct sockaddr_in sa, mysa;
\r
9115 struct hostent FAR *hp;
\r
9116 unsigned short uport;
\r
9117 WORD wVersionRequested;
\r
9120 /* Initialize socket DLL */
\r
9121 wVersionRequested = MAKEWORD(1, 1);
\r
9122 err = WSAStartup(wVersionRequested, &wsaData);
\r
9123 if (err != 0) return err;
\r
9126 if ((s = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {
\r
9127 err = WSAGetLastError();
\r
9132 /* Bind local address using (mostly) don't-care values.
\r
9134 memset((char *) &mysa, 0, sizeof(struct sockaddr_in));
\r
9135 mysa.sin_family = AF_INET;
\r
9136 mysa.sin_addr.s_addr = INADDR_ANY;
\r
9137 uport = (unsigned short) 0;
\r
9138 mysa.sin_port = htons(uport);
\r
9139 if (bind(s, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))
\r
9140 == SOCKET_ERROR) {
\r
9141 err = WSAGetLastError();
\r
9146 /* Resolve remote host name */
\r
9147 memset((char *) &sa, 0, sizeof(struct sockaddr_in));
\r
9148 if (!(hp = gethostbyname(host))) {
\r
9149 unsigned int b0, b1, b2, b3;
\r
9151 err = WSAGetLastError();
\r
9153 if (sscanf(host, "%u.%u.%u.%u", &b0, &b1, &b2, &b3) == 4) {
\r
9154 hp = (struct hostent *) calloc(1, sizeof(struct hostent));
\r
9155 hp->h_addrtype = AF_INET;
\r
9157 hp->h_addr_list = (char **) calloc(2, sizeof(char *));
\r
9158 hp->h_addr_list[0] = (char *) malloc(4);
\r
9159 hp->h_addr_list[0][0] = (char) b0;
\r
9160 hp->h_addr_list[0][1] = (char) b1;
\r
9161 hp->h_addr_list[0][2] = (char) b2;
\r
9162 hp->h_addr_list[0][3] = (char) b3;
\r
9168 sa.sin_family = hp->h_addrtype;
\r
9169 uport = (unsigned short) atoi(port);
\r
9170 sa.sin_port = htons(uport);
\r
9171 memcpy((char *) &sa.sin_addr, hp->h_addr, hp->h_length);
\r
9173 /* Make connection */
\r
9174 if (connect(s, (struct sockaddr *) &sa,
\r
9175 sizeof(struct sockaddr_in)) == SOCKET_ERROR) {
\r
9176 err = WSAGetLastError();
\r
9181 /* Prepare return value */
\r
9182 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
\r
9183 cp->kind = CPSock;
\r
9185 *pr = (ProcRef *) cp;
\r
9191 OpenCommPort(char *name, ProcRef *pr)
\r
9196 char fullname[MSG_SIZ];
\r
9198 if (*name != '\\')
\r
9199 snprintf(fullname, MSG_SIZ, "\\\\.\\%s", name);
\r
9201 safeStrCpy(fullname, name, sizeof(fullname)/sizeof(fullname[0]) );
\r
9203 h = CreateFile(name, GENERIC_READ | GENERIC_WRITE,
\r
9204 0, NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL);
\r
9205 if (h == (HANDLE) -1) {
\r
9206 return GetLastError();
\r
9210 if (!SetCommState(h, (LPDCB) &dcb)) return GetLastError();
\r
9212 /* Accumulate characters until a 100ms pause, then parse */
\r
9213 ct.ReadIntervalTimeout = 100;
\r
9214 ct.ReadTotalTimeoutMultiplier = 0;
\r
9215 ct.ReadTotalTimeoutConstant = 0;
\r
9216 ct.WriteTotalTimeoutMultiplier = 0;
\r
9217 ct.WriteTotalTimeoutConstant = 0;
\r
9218 if (!SetCommTimeouts(h, (LPCOMMTIMEOUTS) &ct)) return GetLastError();
\r
9220 /* Prepare return value */
\r
9221 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
\r
9222 cp->kind = CPComm;
\r
9225 *pr = (ProcRef *) cp;
\r
9231 OpenLoopback(ProcRef *pr)
\r
9233 DisplayFatalError(_("Not implemented"), 0, 1);
\r
9239 OpenRcmd(char* host, char* user, char* cmd, ProcRef* pr)
\r
9244 struct sockaddr_in sa, mysa;
\r
9245 struct hostent FAR *hp;
\r
9246 unsigned short uport;
\r
9247 WORD wVersionRequested;
\r
9250 char stderrPortStr[MSG_SIZ];
\r
9252 /* Initialize socket DLL */
\r
9253 wVersionRequested = MAKEWORD(1, 1);
\r
9254 err = WSAStartup(wVersionRequested, &wsaData);
\r
9255 if (err != 0) return err;
\r
9257 /* Resolve remote host name */
\r
9258 memset((char *) &sa, 0, sizeof(struct sockaddr_in));
\r
9259 if (!(hp = gethostbyname(host))) {
\r
9260 unsigned int b0, b1, b2, b3;
\r
9262 err = WSAGetLastError();
\r
9264 if (sscanf(host, "%u.%u.%u.%u", &b0, &b1, &b2, &b3) == 4) {
\r
9265 hp = (struct hostent *) calloc(1, sizeof(struct hostent));
\r
9266 hp->h_addrtype = AF_INET;
\r
9268 hp->h_addr_list = (char **) calloc(2, sizeof(char *));
\r
9269 hp->h_addr_list[0] = (char *) malloc(4);
\r
9270 hp->h_addr_list[0][0] = (char) b0;
\r
9271 hp->h_addr_list[0][1] = (char) b1;
\r
9272 hp->h_addr_list[0][2] = (char) b2;
\r
9273 hp->h_addr_list[0][3] = (char) b3;
\r
9279 sa.sin_family = hp->h_addrtype;
\r
9280 uport = (unsigned short) 514;
\r
9281 sa.sin_port = htons(uport);
\r
9282 memcpy((char *) &sa.sin_addr, hp->h_addr, hp->h_length);
\r
9284 /* Bind local socket to unused "privileged" port address
\r
9286 s = INVALID_SOCKET;
\r
9287 memset((char *) &mysa, 0, sizeof(struct sockaddr_in));
\r
9288 mysa.sin_family = AF_INET;
\r
9289 mysa.sin_addr.s_addr = INADDR_ANY;
\r
9290 for (fromPort = 1023;; fromPort--) {
\r
9291 if (fromPort < 0) {
\r
9293 return WSAEADDRINUSE;
\r
9295 if (s == INVALID_SOCKET) {
\r
9296 if ((s = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {
\r
9297 err = WSAGetLastError();
\r
9302 uport = (unsigned short) fromPort;
\r
9303 mysa.sin_port = htons(uport);
\r
9304 if (bind(s, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))
\r
9305 == SOCKET_ERROR) {
\r
9306 err = WSAGetLastError();
\r
9307 if (err == WSAEADDRINUSE) continue;
\r
9311 if (connect(s, (struct sockaddr *) &sa,
\r
9312 sizeof(struct sockaddr_in)) == SOCKET_ERROR) {
\r
9313 err = WSAGetLastError();
\r
9314 if (err == WSAEADDRINUSE) {
\r
9325 /* Bind stderr local socket to unused "privileged" port address
\r
9327 s2 = INVALID_SOCKET;
\r
9328 memset((char *) &mysa, 0, sizeof(struct sockaddr_in));
\r
9329 mysa.sin_family = AF_INET;
\r
9330 mysa.sin_addr.s_addr = INADDR_ANY;
\r
9331 for (fromPort = 1023;; fromPort--) {
\r
9332 if (fromPort == prevStderrPort) continue; // don't reuse port
\r
9333 if (fromPort < 0) {
\r
9334 (void) closesocket(s);
\r
9336 return WSAEADDRINUSE;
\r
9338 if (s2 == INVALID_SOCKET) {
\r
9339 if ((s2 = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {
\r
9340 err = WSAGetLastError();
\r
9346 uport = (unsigned short) fromPort;
\r
9347 mysa.sin_port = htons(uport);
\r
9348 if (bind(s2, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))
\r
9349 == SOCKET_ERROR) {
\r
9350 err = WSAGetLastError();
\r
9351 if (err == WSAEADDRINUSE) continue;
\r
9352 (void) closesocket(s);
\r
9356 if (listen(s2, 1) == SOCKET_ERROR) {
\r
9357 err = WSAGetLastError();
\r
9358 if (err == WSAEADDRINUSE) {
\r
9360 s2 = INVALID_SOCKET;
\r
9363 (void) closesocket(s);
\r
9364 (void) closesocket(s2);
\r
9370 prevStderrPort = fromPort; // remember port used
\r
9371 snprintf(stderrPortStr, MSG_SIZ, "%d", fromPort);
\r
9373 if (send(s, stderrPortStr, strlen(stderrPortStr) + 1, 0) == SOCKET_ERROR) {
\r
9374 err = WSAGetLastError();
\r
9375 (void) closesocket(s);
\r
9376 (void) closesocket(s2);
\r
9381 if (send(s, UserName(), strlen(UserName()) + 1, 0) == SOCKET_ERROR) {
\r
9382 err = WSAGetLastError();
\r
9383 (void) closesocket(s);
\r
9384 (void) closesocket(s2);
\r
9388 if (*user == NULLCHAR) user = UserName();
\r
9389 if (send(s, user, strlen(user) + 1, 0) == SOCKET_ERROR) {
\r
9390 err = WSAGetLastError();
\r
9391 (void) closesocket(s);
\r
9392 (void) closesocket(s2);
\r
9396 if (send(s, cmd, strlen(cmd) + 1, 0) == SOCKET_ERROR) {
\r
9397 err = WSAGetLastError();
\r
9398 (void) closesocket(s);
\r
9399 (void) closesocket(s2);
\r
9404 if ((s3 = accept(s2, NULL, NULL)) == INVALID_SOCKET) {
\r
9405 err = WSAGetLastError();
\r
9406 (void) closesocket(s);
\r
9407 (void) closesocket(s2);
\r
9411 (void) closesocket(s2); /* Stop listening */
\r
9413 /* Prepare return value */
\r
9414 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
\r
9415 cp->kind = CPRcmd;
\r
9418 *pr = (ProcRef *) cp;
\r
9425 AddInputSource(ProcRef pr, int lineByLine,
\r
9426 InputCallback func, VOIDSTAR closure)
\r
9428 InputSource *is, *is2 = NULL;
\r
9429 ChildProc *cp = (ChildProc *) pr;
\r
9431 is = (InputSource *) calloc(1, sizeof(InputSource));
\r
9432 is->lineByLine = lineByLine;
\r
9434 is->closure = closure;
\r
9435 is->second = NULL;
\r
9436 is->next = is->buf;
\r
9437 if (pr == NoProc) {
\r
9438 is->kind = CPReal;
\r
9439 consoleInputSource = is;
\r
9441 is->kind = cp->kind;
\r
9443 [AS] Try to avoid a race condition if the thread is given control too early:
\r
9444 we create all threads suspended so that the is->hThread variable can be
\r
9445 safely assigned, then let the threads start with ResumeThread.
\r
9447 switch (cp->kind) {
\r
9449 is->hFile = cp->hFrom;
\r
9450 cp->hFrom = NULL; /* now owned by InputThread */
\r
9452 CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) NonOvlInputThread,
\r
9453 (LPVOID) is, CREATE_SUSPENDED, &is->id);
\r
9457 is->hFile = cp->hFrom;
\r
9458 cp->hFrom = NULL; /* now owned by InputThread */
\r
9460 CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) InputThread,
\r
9461 (LPVOID) is, CREATE_SUSPENDED, &is->id);
\r
9465 is->sock = cp->sock;
\r
9467 CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,
\r
9468 (LPVOID) is, CREATE_SUSPENDED, &is->id);
\r
9472 is2 = (InputSource *) calloc(1, sizeof(InputSource));
\r
9474 is->sock = cp->sock;
\r
9476 is2->sock = cp->sock2;
\r
9477 is2->second = is2;
\r
9479 CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,
\r
9480 (LPVOID) is, CREATE_SUSPENDED, &is->id);
\r
9482 CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,
\r
9483 (LPVOID) is2, CREATE_SUSPENDED, &is2->id);
\r
9487 if( is->hThread != NULL ) {
\r
9488 ResumeThread( is->hThread );
\r
9491 if( is2 != NULL && is2->hThread != NULL ) {
\r
9492 ResumeThread( is2->hThread );
\r
9496 return (InputSourceRef) is;
\r
9500 RemoveInputSource(InputSourceRef isr)
\r
9504 is = (InputSource *) isr;
\r
9505 is->hThread = NULL; /* tell thread to stop */
\r
9506 CloseHandle(is->hThread);
\r
9507 if (is->second != NULL) {
\r
9508 is->second->hThread = NULL;
\r
9509 CloseHandle(is->second->hThread);
\r
9513 int no_wrap(char *message, int count)
\r
9515 ConsoleOutput(message, count, FALSE);
\r
9520 OutputToProcess(ProcRef pr, char *message, int count, int *outError)
\r
9523 int outCount = SOCKET_ERROR;
\r
9524 ChildProc *cp = (ChildProc *) pr;
\r
9525 static OVERLAPPED ovl;
\r
9526 static int line = 0;
\r
9530 if (appData.noJoin || !appData.useInternalWrap)
\r
9531 return no_wrap(message, count);
\r
9534 int width = get_term_width();
\r
9535 int len = wrap(NULL, message, count, width, &line);
\r
9536 char *msg = malloc(len);
\r
9540 return no_wrap(message, count);
\r
9543 dbgchk = wrap(msg, message, count, width, &line);
\r
9544 if (dbgchk != len && appData.debugMode)
\r
9545 fprintf(debugFP, "wrap(): dbgchk(%d) != len(%d)\n", dbgchk, len);
\r
9546 ConsoleOutput(msg, len, FALSE);
\r
9553 if (ovl.hEvent == NULL) {
\r
9554 ovl.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
\r
9556 ovl.Internal = ovl.InternalHigh = ovl.Offset = ovl.OffsetHigh = 0;
\r
9558 switch (cp->kind) {
\r
9561 outCount = send(cp->sock, message, count, 0);
\r
9562 if (outCount == SOCKET_ERROR) {
\r
9563 *outError = WSAGetLastError();
\r
9565 *outError = NO_ERROR;
\r
9570 if (WriteFile(((ChildProc *)pr)->hTo, message, count,
\r
9571 &dOutCount, NULL)) {
\r
9572 *outError = NO_ERROR;
\r
9573 outCount = (int) dOutCount;
\r
9575 *outError = GetLastError();
\r
9580 *outError = DoWriteFile(((ChildProc *)pr)->hTo, message, count,
\r
9581 &dOutCount, &ovl);
\r
9582 if (*outError == NO_ERROR) {
\r
9583 outCount = (int) dOutCount;
\r
9591 OutputToProcessDelayed(ProcRef pr, char *message, int count, int *outError,
\r
9594 /* Ignore delay, not implemented for WinBoard */
\r
9595 return OutputToProcess(pr, message, count, outError);
\r
9600 CmailSigHandlerCallBack(InputSourceRef isr, VOIDSTAR closure,
\r
9601 char *buf, int count, int error)
\r
9603 DisplayFatalError(_("Not implemented"), 0, 1);
\r
9606 /* see wgamelist.c for Game List functions */
\r
9607 /* see wedittags.c for Edit Tags functions */
\r
9614 char buf[MSG_SIZ];
\r
9617 if (SearchPath(installDir, appData.icsLogon, NULL, MSG_SIZ, buf, &dummy)) {
\r
9618 f = fopen(buf, "r");
\r
9620 ProcessICSInitScript(f);
\r
9628 StartAnalysisClock()
\r
9630 if (analysisTimerEvent) return;
\r
9631 analysisTimerEvent = SetTimer(hwndMain, (UINT) ANALYSIS_TIMER_ID,
\r
9632 (UINT) 2000, NULL);
\r
9636 SetHighlights(int fromX, int fromY, int toX, int toY)
\r
9638 highlightInfo.sq[0].x = fromX;
\r
9639 highlightInfo.sq[0].y = fromY;
\r
9640 highlightInfo.sq[1].x = toX;
\r
9641 highlightInfo.sq[1].y = toY;
\r
9647 highlightInfo.sq[0].x = highlightInfo.sq[0].y =
\r
9648 highlightInfo.sq[1].x = highlightInfo.sq[1].y = -1;
\r
9652 SetPremoveHighlights(int fromX, int fromY, int toX, int toY)
\r
9654 premoveHighlightInfo.sq[0].x = fromX;
\r
9655 premoveHighlightInfo.sq[0].y = fromY;
\r
9656 premoveHighlightInfo.sq[1].x = toX;
\r
9657 premoveHighlightInfo.sq[1].y = toY;
\r
9661 ClearPremoveHighlights()
\r
9663 premoveHighlightInfo.sq[0].x = premoveHighlightInfo.sq[0].y =
\r
9664 premoveHighlightInfo.sq[1].x = premoveHighlightInfo.sq[1].y = -1;
\r
9668 ShutDownFrontEnd()
\r
9670 if (saveSettingsOnExit) SaveSettings(settingsFileName);
\r
9671 DeleteClipboardTempFiles();
\r
9677 if (IsIconic(hwndMain))
\r
9678 ShowWindow(hwndMain, SW_RESTORE);
\r
9680 SetActiveWindow(hwndMain);
\r
9684 * Prototypes for animation support routines
\r
9686 static void ScreenSquare(int column, int row, POINT * pt);
\r
9687 static void Tween( POINT * start, POINT * mid, POINT * finish, int factor,
\r
9688 POINT frames[], int * nFrames);
\r
9694 AnimateAtomicCapture(Board board, int fromX, int fromY, int toX, int toY)
\r
9695 { // [HGM] atomic: animate blast wave
\r
9698 explodeInfo.fromX = fromX;
\r
9699 explodeInfo.fromY = fromY;
\r
9700 explodeInfo.toX = toX;
\r
9701 explodeInfo.toY = toY;
\r
9702 for(i=1; i<4*kFactor; i++) {
\r
9703 explodeInfo.radius = (i*180)/(4*kFactor-1);
\r
9704 DrawPosition(FALSE, board);
\r
9705 Sleep(appData.animSpeed);
\r
9707 explodeInfo.radius = 0;
\r
9708 DrawPosition(TRUE, board);
\r
9712 AnimateMove(board, fromX, fromY, toX, toY)
\r
9719 ChessSquare piece;
\r
9720 POINT start, finish, mid;
\r
9721 POINT frames[kFactor * 2 + 1];
\r
9724 if (!appData.animate) return;
\r
9725 if (doingSizing) return;
\r
9726 if (fromY < 0 || fromX < 0) return;
\r
9727 piece = board[fromY][fromX];
\r
9728 if (piece >= EmptySquare) return;
\r
9730 ScreenSquare(fromX, fromY, &start);
\r
9731 ScreenSquare(toX, toY, &finish);
\r
9733 /* All moves except knight jumps move in straight line */
\r
9734 if (!(abs(fromX-toX) == 1 && abs(fromY-toY) == 2 || abs(fromX-toX) == 2 && abs(fromY-toY) == 1)) {
\r
9735 mid.x = start.x + (finish.x - start.x) / 2;
\r
9736 mid.y = start.y + (finish.y - start.y) / 2;
\r
9738 /* Knight: make straight movement then diagonal */
\r
9739 if (abs(toY - fromY) < abs(toX - fromX)) {
\r
9740 mid.x = start.x + (finish.x - start.x) / 2;
\r
9744 mid.y = start.y + (finish.y - start.y) / 2;
\r
9748 /* Don't use as many frames for very short moves */
\r
9749 if (abs(toY - fromY) + abs(toX - fromX) <= 2)
\r
9750 Tween(&start, &mid, &finish, kFactor - 1, frames, &nFrames);
\r
9752 Tween(&start, &mid, &finish, kFactor, frames, &nFrames);
\r
9754 animInfo.from.x = fromX;
\r
9755 animInfo.from.y = fromY;
\r
9756 animInfo.to.x = toX;
\r
9757 animInfo.to.y = toY;
\r
9758 animInfo.lastpos = start;
\r
9759 animInfo.piece = piece;
\r
9760 for (n = 0; n < nFrames; n++) {
\r
9761 animInfo.pos = frames[n];
\r
9762 DrawPosition(FALSE, NULL);
\r
9763 animInfo.lastpos = animInfo.pos;
\r
9764 Sleep(appData.animSpeed);
\r
9766 animInfo.pos = finish;
\r
9767 DrawPosition(FALSE, NULL);
\r
9768 animInfo.piece = EmptySquare;
\r
9769 Explode(board, fromX, fromY, toX, toY);
\r
9772 /* Convert board position to corner of screen rect and color */
\r
9775 ScreenSquare(column, row, pt)
\r
9776 int column; int row; POINT * pt;
\r
9779 pt->x = lineGap + ((BOARD_WIDTH-1)-column) * (squareSize + lineGap);
\r
9780 pt->y = lineGap + row * (squareSize + lineGap);
\r
9782 pt->x = lineGap + column * (squareSize + lineGap);
\r
9783 pt->y = lineGap + ((BOARD_HEIGHT-1)-row) * (squareSize + lineGap);
\r
9787 /* Generate a series of frame coords from start->mid->finish.
\r
9788 The movement rate doubles until the half way point is
\r
9789 reached, then halves back down to the final destination,
\r
9790 which gives a nice slow in/out effect. The algorithmn
\r
9791 may seem to generate too many intermediates for short
\r
9792 moves, but remember that the purpose is to attract the
\r
9793 viewers attention to the piece about to be moved and
\r
9794 then to where it ends up. Too few frames would be less
\r
9798 Tween(start, mid, finish, factor, frames, nFrames)
\r
9799 POINT * start; POINT * mid;
\r
9800 POINT * finish; int factor;
\r
9801 POINT frames[]; int * nFrames;
\r
9803 int n, fraction = 1, count = 0;
\r
9805 /* Slow in, stepping 1/16th, then 1/8th, ... */
\r
9806 for (n = 0; n < factor; n++)
\r
9808 for (n = 0; n < factor; n++) {
\r
9809 frames[count].x = start->x + (mid->x - start->x) / fraction;
\r
9810 frames[count].y = start->y + (mid->y - start->y) / fraction;
\r
9812 fraction = fraction / 2;
\r
9816 frames[count] = *mid;
\r
9819 /* Slow out, stepping 1/2, then 1/4, ... */
\r
9821 for (n = 0; n < factor; n++) {
\r
9822 frames[count].x = finish->x - (finish->x - mid->x) / fraction;
\r
9823 frames[count].y = finish->y - (finish->y - mid->y) / fraction;
\r
9825 fraction = fraction * 2;
\r
9831 HistorySet( char movelist[][2*MOVE_LEN], int first, int last, int current )
\r
9833 MoveHistorySet( movelist, first, last, current, pvInfoList );
\r
9835 EvalGraphSet( first, last, current, pvInfoList );
\r
9837 MakeEngineOutputTitle();
\r
9841 SettingsPopUp(ChessProgramState *cps)
\r
9842 { // [HGM] wrapper needed because handles must not be passed through back-end
\r
9843 EngineOptionsPopup(savedHwnd, cps);
\r
9846 int flock(int fid, int code)
\r
9848 HANDLE hFile = (HANDLE) _get_osfhandle(fid);
\r
9852 ov.OffsetHigh = 0;
\r
9854 case 1: LockFileEx(hFile, LOCKFILE_EXCLUSIVE_LOCK, 0, 1024, 0, &ov); break; // LOCK_SH
\r
9855 case 2: LockFileEx(hFile, LOCKFILE_EXCLUSIVE_LOCK, 0, 1024, 0, &ov); break; // LOCK_EX
\r
9856 case 3: UnlockFileEx(hFile, 0, 1024, 0, &ov); break; // LOCK_UN
\r
9857 default: return -1;
\r