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, OPT_Exact, OPT_Subset, OPT_Struct, OPT_Material, OPT_Range, OPT_Difference,
\r
257 OPT_elo1t, OPT_elo2t, OPT_datet, OPT_Stretch, OPT_Stretcht, OPT_Reversed, OPT_SearchMode, OPT_Mirror, OPT_thresholds, IDOK, IDCANCEL },
\r
258 { DLG_SaveOptions, OPT_Autosave, OPT_AVPrompt, OPT_AVToFile, OPT_AVBrowse,
\r
259 801, OPT_PGN, OPT_Old, OPT_OutOfBookInfo, IDOK, IDCANCEL },
\r
260 { 1536, 1090, IDC_Directories, 1089, 1091, IDOK, IDCANCEL, 1038, IDC_IndexNr, 1037 },
\r
261 { DLG_CommPort, IDOK, IDCANCEL, IDC_Port, IDC_Rate, IDC_Bits, IDC_Parity,
\r
262 IDC_Stop, IDC_Flow, OPT_SerialHelp },
\r
263 { DLG_EditComment, IDOK, OPT_CancelComment, OPT_ClearComment, OPT_EditComment },
\r
264 { DLG_PromotionKing, PB_Chancellor, PB_Archbishop, PB_Queen, PB_Rook,
\r
265 PB_Bishop, PB_Knight, PB_King, IDCANCEL, IDC_Yes, IDC_No, IDC_Centaur },
\r
266 { ABOUTBOX2, IDC_ChessBoard },
\r
267 { DLG_GameList, OPT_GameListLoad, OPT_GameListPrev, OPT_GameListNext,
\r
268 OPT_GameListClose, IDC_GameListDoFilter },
\r
269 { DLG_EditTags, IDOK, OPT_TagsCancel, OPT_EditTags },
\r
270 { DLG_Error, IDOK },
\r
271 { DLG_Colorize, IDOK, IDCANCEL, OPT_ChooseColor, OPT_Bold, OPT_Italic,
\r
272 OPT_Underline, OPT_Strikeout, OPT_Sample },
\r
273 { DLG_Question, IDOK, IDCANCEL, OPT_QuestionText },
\r
274 { DLG_Startup, IDC_Welcome, OPT_ChessEngine, OPT_ChessServer, OPT_View,
\r
275 IDC_SPECIFY_ENG_STATIC, IDC_SPECIFY_SERVER_STATIC, OPT_AnyAdditional,
\r
276 IDOK, IDCANCEL, IDM_HELPCONTENTS },
\r
277 { DLG_IndexNumber, IDC_Index },
\r
278 { DLG_TypeInMove, IDOK, IDCANCEL },
\r
279 { DLG_TypeInName, IDOK, IDCANCEL },
\r
280 { DLG_Sound, IDC_Event, OPT_NoSound, OPT_DefaultBeep, OPT_BuiltInSound,
\r
281 OPT_WavFile, OPT_BrowseSound, OPT_DefaultSounds, IDOK, IDCANCEL, OPT_PlaySound },
\r
282 { DLG_GeneralOptions, IDOK, IDCANCEL, OPT_AlwaysOnTop, OPT_HighlightLastMove,
\r
283 OPT_AlwaysQueen, OPT_PeriodicUpdates, OPT_AnimateDragging, OPT_PonderNextMove,
\r
284 OPT_AnimateMoving, OPT_PopupExitMessage, OPT_AutoFlag, OPT_PopupMoveErrors,
\r
285 OPT_AutoFlipView, OPT_ShowButtonBar, OPT_AutoRaiseBoard, OPT_ShowCoordinates,
\r
286 OPT_Blindfold, OPT_ShowThinking, OPT_HighlightDragging, OPT_TestLegality,
\r
287 OPT_SaveExtPGN, OPT_HideThinkFromHuman, OPT_ExtraInfoInMoveHistory,
\r
288 OPT_HighlightMoveArrow, OPT_AutoLogo ,OPT_SmartMove },
\r
289 { DLG_IcsOptions, IDOK, IDCANCEL, OPT_AutoComment, OPT_AutoKibitz, OPT_AutoObserve,
\r
290 OPT_GetMoveList, OPT_LocalLineEditing, OPT_QuietPlay, OPT_SeekGraph, OPT_AutoRefresh,
\r
291 OPT_BgObserve, OPT_DualBoard, OPT_Premove, OPT_PremoveWhite, OPT_PremoveBlack,
\r
292 OPT_SmartMove, OPT_IcsAlarm, IDC_Sec, OPT_ChooseShoutColor, OPT_ChooseSShoutColor,
\r
293 OPT_ChooseChannel1Color, OPT_ChooseChannelColor, OPT_ChooseKibitzColor,
\r
294 OPT_ChooseTellColor, OPT_ChooseChallengeColor, OPT_ChooseRequestColor,
\r
295 OPT_ChooseSeekColor, OPT_ChooseNormalColor, OPT_ChooseBackgroundColor,
\r
296 OPT_DefaultColors, OPT_DontColorize, IDC_Boxes, GPB_Colors, GPB_Premove,
\r
297 GPB_General, GPB_Alarm },
\r
298 { DLG_BoardOptions, IDOK, IDCANCEL, OPT_SizeTiny, OPT_SizeTeeny, OPT_SizeDinky,
\r
299 OPT_SizePetite, OPT_SizeSlim, OPT_SizeSmall, OPT_SizeMediocre, OPT_SizeMiddling,
\r
300 OPT_SizeAverage, OPT_SizeModerate, OPT_SizeMedium, OPT_SizeBulky, OPT_SizeLarge,
\r
301 OPT_SizeBig, OPT_SizeHuge, OPT_SizeGiant, OPT_SizeColossal, OPT_SizeTitanic,
\r
302 OPT_ChooseLightSquareColor, OPT_ChooseDarkSquareColor, OPT_ChooseWhitePieceColor,
\r
303 OPT_ChooseBlackPieceColor, OPT_ChooseHighlightSquareColor, OPT_ChoosePremoveHighlightColor,
\r
304 OPT_Monochrome, OPT_AllWhite, OPT_UpsideDown, OPT_DefaultBoardColors, GPB_Colors,
\r
305 IDC_Light, IDC_Dark, IDC_White, IDC_Black, IDC_High, IDC_PreHigh, GPB_Size, OPT_Bitmaps, OPT_PieceFont },
\r
306 { DLG_NewVariant, IDOK, IDCANCEL, OPT_VariantNormal, OPT_VariantFRC, OPT_VariantWildcastle,
\r
307 OPT_VariantNocastle, OPT_VariantLosers, OPT_VariantGiveaway, OPT_VariantSuicide,
\r
308 OPT_Variant3Check, OPT_VariantTwoKings, OPT_VariantAtomic, OPT_VariantCrazyhouse,
\r
309 OPT_VariantBughouse, OPT_VariantTwilight, OPT_VariantShogi, OPT_VariantSuper,
\r
310 OPT_VariantKnightmate, OPT_VariantBerolina, OPT_VariantCylinder, OPT_VariantFairy,
\r
311 OPT_VariantMakruk, OPT_VariantGothic, OPT_VariantCapablanca, OPT_VariantJanus,
\r
312 OPT_VariantCRC, OPT_VariantFalcon, OPT_VariantCourier, OPT_VariantGreat, OPT_VariantSChess,
\r
313 OPT_VariantShatranj, OPT_VariantXiangqi, GPB_Variant, GPB_Board, IDC_Height,
\r
314 IDC_Width, IDC_Hand, IDC_Pieces, IDC_Def },
\r
315 { DLG_Fonts, IDOK, IDCANCEL, OPT_ChooseClockFont, OPT_ChooseMessageFont,
\r
316 OPT_ChooseCoordFont, OPT_ChooseTagFont, OPT_ChooseCommentsFont, OPT_ChooseConsoleFont, OPT_ChooseMoveHistoryFont, OPT_DefaultFonts,
\r
317 OPT_ClockFont, OPT_MessageFont, OPT_CoordFont, OPT_EditTagsFont, OPT_ChoosePieceFont, OPT_MessageFont8,
\r
318 OPT_SampleGameListFont, OPT_ChooseGameListFont, OPT_MessageFont7,
\r
319 OPT_CommentsFont, OPT_MessageFont5, GPB_Current, GPB_All, OPT_MessageFont6 },
\r
320 { DLG_NewGameFRC, IDC_NFG_Label, IDC_NFG_Random, IDOK, IDCANCEL },
\r
321 { DLG_GameListOptions, IDC_GLT, IDC_GLT_Up, IDC_GLT_Down, IDC_GLT_Restore,
\r
322 IDC_GLT_Default, IDOK, IDCANCEL, IDC_GLT_RestoreTo },
\r
323 { DLG_MoveHistory },
\r
324 { DLG_EvalGraph },
\r
325 { DLG_EngineOutput, IDC_EngineLabel1, IDC_Engine1_NPS, IDC_EngineLabel2, IDC_Engine2_NPS },
\r
326 { DLG_Chat, IDC_Partner, IDC_Clear, IDC_Send, },
\r
327 { DLG_EnginePlayOptions, IDC_EpPonder, IDC_EpShowThinking, IDC_EpHideThinkingHuman,
\r
328 IDC_EpPeriodicUpdates, GPB_Adjudications, IDC_Draw, IDC_Moves, IDC_Threshold,
\r
329 IDC_Centi, IDC_TestClaims, IDC_DetectMates, IDC_MaterialDraws, IDC_TrivialDraws,
\r
330 GPB_Apply, IDC_Rule, IDC_Repeats, IDC_ScoreAbs1, IDC_ScoreAbs2, IDOK, IDCANCEL },
\r
331 { DLG_OptionsUCI, IDC_PolyDir, IDC_BrowseForPolyglotDir, IDC_Hash, IDC_Path,
\r
332 IDC_BrowseForEGTB, IDC_Cache, IDC_UseBook, IDC_BrowseForBook, IDC_CPU, IDC_OwnBook1,
\r
333 IDC_OwnBook2, IDC_Depth, IDC_Variation, IDC_DefGames, IDOK, IDCANCEL },
\r
337 static char languageBuf[50000], *foreign[1000], *english[1000], *languageFile[MSG_SIZ];
\r
338 static int lastChecked;
\r
339 static char oldLanguage[MSG_SIZ], *menuText[10][30];
\r
340 extern int tinyLayout;
\r
341 extern char * menuBarText[][10];
\r
344 LoadLanguageFile(char *name)
\r
345 { //load the file with translations, and make a list of the strings to be translated, and their translations
\r
347 int i=0, j=0, n=0, k;
\r
350 if(!name || name[0] == NULLCHAR) return;
\r
351 snprintf(buf, MSG_SIZ, "%s%s", name, strchr(name, '.') ? "" : ".lng"); // auto-append lng extension
\r
352 appData.language = oldLanguage;
\r
353 if(!strcmp(buf, oldLanguage)) { barbaric = 1; return; } // this language already loaded; just switch on
\r
354 if((f = fopen(buf, "r")) == NULL) return;
\r
355 while((k = fgetc(f)) != EOF) {
\r
356 if(i >= sizeof(languageBuf)) { DisplayError("Language file too big", 0); return; }
\r
357 languageBuf[i] = k;
\r
359 if(languageBuf[n] == '"' && languageBuf[i-1] == '"') {
\r
361 if(p = strstr(languageBuf + n + 1, "\" === \"")) {
\r
362 if(p > languageBuf+n+2 && p+8 < languageBuf+i) {
\r
363 if(j >= sizeof(english)) { DisplayError("Too many translated strings", 0); return; }
\r
364 english[j] = languageBuf + n + 1; *p = 0;
\r
365 foreign[j++] = p + 7; languageBuf[i-1] = 0;
\r
366 //if(appData.debugMode) fprintf(debugFP, "translation: replace '%s' by '%s'\n", english[j-1], foreign[j-1]);
\r
371 } else if(i > 0 && languageBuf[i-1] == '\\') {
\r
373 case 'n': k = '\n'; break;
\r
374 case 'r': k = '\r'; break;
\r
375 case 't': k = '\t'; break;
\r
377 languageBuf[--i] = k;
\r
382 barbaric = (j != 0);
\r
383 safeStrCpy(oldLanguage, buf, sizeof(oldLanguage)/sizeof(oldLanguage[0]) );
\r
388 { // return the translation of the given string
\r
389 // efficiency can be improved a lot...
\r
391 //if(appData.debugMode) fprintf(debugFP, "T_(%s)\n", s);
\r
392 if(!barbaric) return s;
\r
393 if(!s) return ""; // sanity
\r
394 while(english[i]) {
\r
395 if(!strcmp(s, english[i])) return foreign[i];
\r
402 Translate(HWND hDlg, int dialogID)
\r
403 { // translate all text items in the given dialog
\r
405 char buf[MSG_SIZ], *s;
\r
406 if(!barbaric) return;
\r
407 while(dialogItems[i][0] && dialogItems[i][0] != dialogID) i++; // find the dialog description
\r
408 if(dialogItems[i][0] != dialogID) return; // unknown dialog, should not happen
\r
409 GetWindowText( hDlg, buf, MSG_SIZ );
\r
411 if(strcmp(buf, s)) SetWindowText(hDlg, s); // replace by translated string (if different)
\r
412 for(j=1; k=dialogItems[i][j]; j++) { // translate all listed dialog items
\r
413 GetDlgItemText(hDlg, k, buf, MSG_SIZ);
\r
414 if(strlen(buf) == 0) continue;
\r
416 if(strcmp(buf, s)) SetDlgItemText(hDlg, k, s); // replace by translated string (if different)
\r
421 TranslateOneMenu(int i, HMENU subMenu)
\r
424 static MENUITEMINFO info;
\r
426 info.cbSize = sizeof(MENUITEMINFO);
\r
427 info.fMask = MIIM_STATE | MIIM_TYPE;
\r
428 for(j=GetMenuItemCount(subMenu)-1; j>=0; j--){
\r
430 info.dwTypeData = buf;
\r
431 info.cch = sizeof(buf);
\r
432 GetMenuItemInfo(subMenu, j, TRUE, &info);
\r
434 if(menuText[i][j]) safeStrCpy(buf, menuText[i][j], sizeof(buf)/sizeof(buf[0]) );
\r
435 else menuText[i][j] = strdup(buf); // remember original on first change
\r
437 if(buf[0] == NULLCHAR) continue;
\r
438 info.dwTypeData = T_(buf);
\r
439 info.cch = strlen(buf)+1;
\r
440 SetMenuItemInfo(subMenu, j, TRUE, &info);
\r
446 TranslateMenus(int addLanguage)
\r
449 WIN32_FIND_DATA fileData;
\r
451 #define IDM_English 1970
\r
453 HMENU mainMenu = GetMenu(hwndMain);
\r
454 for (i=GetMenuItemCount(mainMenu)-1; i>=0; i--) {
\r
455 HMENU subMenu = GetSubMenu(mainMenu, i);
\r
456 ModifyMenu(mainMenu, i, MF_STRING|MF_BYPOSITION|MF_POPUP|EnableMenuItem(mainMenu, i, MF_BYPOSITION),
\r
457 (UINT) subMenu, T_(menuBarText[tinyLayout][i]));
\r
458 TranslateOneMenu(i, subMenu);
\r
460 DrawMenuBar(hwndMain);
\r
463 if(!addLanguage) return;
\r
464 if((hFind = FindFirstFile("*.LNG", &fileData)) != INVALID_HANDLE_VALUE) {
\r
465 HMENU mainMenu = GetMenu(hwndMain);
\r
466 HMENU subMenu = GetSubMenu(mainMenu, GetMenuItemCount(mainMenu)-1);
\r
467 AppendMenu(subMenu, MF_SEPARATOR, (UINT_PTR) 0, NULL);
\r
468 AppendMenu(subMenu, MF_ENABLED|MF_STRING|(barbaric?MF_UNCHECKED:MF_CHECKED), (UINT_PTR) IDM_English, (LPCTSTR) "English");
\r
469 i = 0; lastChecked = IDM_English;
\r
471 char *p, *q = fileData.cFileName;
\r
472 int checkFlag = MF_UNCHECKED;
\r
473 languageFile[i] = strdup(q);
\r
474 if(barbaric && !strcmp(oldLanguage, q)) {
\r
475 checkFlag = MF_CHECKED;
\r
476 lastChecked = IDM_English + i + 1;
\r
477 CheckMenuItem(mainMenu, IDM_English, MF_BYCOMMAND|MF_UNCHECKED);
\r
479 *q = ToUpper(*q); while(*++q) *q = ToLower(*q);
\r
480 p = strstr(fileData.cFileName, ".lng");
\r
482 AppendMenu(subMenu, MF_ENABLED|MF_STRING|checkFlag, (UINT_PTR) IDM_English + ++i, (LPCTSTR) fileData.cFileName);
\r
483 } while(FindNextFile(hFind, &fileData));
\r
496 int cliWidth, cliHeight;
\r
499 SizeInfo sizeInfo[] =
\r
501 { "tiny", 21, 0, 1, 1, 0, 0 },
\r
502 { "teeny", 25, 1, 1, 1, 0, 0 },
\r
503 { "dinky", 29, 1, 1, 1, 0, 0 },
\r
504 { "petite", 33, 1, 1, 1, 0, 0 },
\r
505 { "slim", 37, 2, 1, 0, 0, 0 },
\r
506 { "small", 40, 2, 1, 0, 0, 0 },
\r
507 { "mediocre", 45, 2, 1, 0, 0, 0 },
\r
508 { "middling", 49, 2, 0, 0, 0, 0 },
\r
509 { "average", 54, 2, 0, 0, 0, 0 },
\r
510 { "moderate", 58, 3, 0, 0, 0, 0 },
\r
511 { "medium", 64, 3, 0, 0, 0, 0 },
\r
512 { "bulky", 72, 3, 0, 0, 0, 0 },
\r
513 { "large", 80, 3, 0, 0, 0, 0 },
\r
514 { "big", 87, 3, 0, 0, 0, 0 },
\r
515 { "huge", 95, 3, 0, 0, 0, 0 },
\r
516 { "giant", 108, 3, 0, 0, 0, 0 },
\r
517 { "colossal", 116, 4, 0, 0, 0, 0 },
\r
518 { "titanic", 129, 4, 0, 0, 0, 0 },
\r
519 { NULL, 0, 0, 0, 0, 0, 0 }
\r
522 #define MF(x) {x, {{0,}, 0. }, {0, }, 0}
\r
523 MyFont fontRec[NUM_SIZES][NUM_FONTS] =
\r
525 { 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
526 { 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
527 { 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
528 { 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
529 { 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
530 { 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
531 { 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
532 { 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
533 { 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
534 { 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
535 { 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
536 { 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
537 { 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
538 { 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
539 { 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
540 { 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
541 { 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
542 { 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
545 MyFont *font[NUM_SIZES][NUM_FONTS];
\r
554 #define BUTTON_WIDTH (tinyLayout ? 16 : 32)
\r
555 #define N_BUTTONS 5
\r
557 MyButtonDesc buttonDesc[N_BUTTONS] =
\r
559 {"<<", IDM_ToStart, NULL, NULL},
\r
560 {"<", IDM_Backward, NULL, NULL},
\r
561 {"P", IDM_Pause, NULL, NULL},
\r
562 {">", IDM_Forward, NULL, NULL},
\r
563 {">>", IDM_ToEnd, NULL, NULL},
\r
566 int tinyLayout = 0, smallLayout = 0;
\r
567 #define MENU_BAR_ITEMS 9
\r
568 char *menuBarText[2][MENU_BAR_ITEMS+1] = {
\r
569 { N_("&File"), N_("&Edit"), N_("&View"), N_("&Mode"), N_("&Action"), N_("E&ngine"), N_("&Options"), N_("&Help"), NULL },
\r
570 { N_("&F"), N_("&E"), N_("&V"), N_("&M"), N_("&A"), N_("&N"), N_("&O"), N_("&H"), NULL },
\r
574 MySound sounds[(int)NSoundClasses];
\r
575 MyTextAttribs textAttribs[(int)NColorClasses];
\r
577 MyColorizeAttribs colorizeAttribs[] = {
\r
578 { (COLORREF)0, 0, N_("Shout Text") },
\r
579 { (COLORREF)0, 0, N_("SShout/CShout") },
\r
580 { (COLORREF)0, 0, N_("Channel 1 Text") },
\r
581 { (COLORREF)0, 0, N_("Channel Text") },
\r
582 { (COLORREF)0, 0, N_("Kibitz Text") },
\r
583 { (COLORREF)0, 0, N_("Tell Text") },
\r
584 { (COLORREF)0, 0, N_("Challenge Text") },
\r
585 { (COLORREF)0, 0, N_("Request Text") },
\r
586 { (COLORREF)0, 0, N_("Seek Text") },
\r
587 { (COLORREF)0, 0, N_("Normal Text") },
\r
588 { (COLORREF)0, 0, N_("None") }
\r
593 static char *commentTitle;
\r
594 static char *commentText;
\r
595 static int commentIndex;
\r
596 static Boolean editComment = FALSE;
\r
599 char errorTitle[MSG_SIZ];
\r
600 char errorMessage[2*MSG_SIZ];
\r
601 HWND errorDialog = NULL;
\r
602 BOOLEAN moveErrorMessageUp = FALSE;
\r
603 BOOLEAN consoleEcho = TRUE;
\r
604 CHARFORMAT consoleCF;
\r
605 COLORREF consoleBackgroundColor;
\r
607 char *programVersion;
\r
613 typedef int CPKind;
\r
622 SOCKET sock2; /* stderr socket for OpenRcmd */
\r
625 #define INPUT_SOURCE_BUF_SIZE 4096
\r
627 typedef struct _InputSource {
\r
634 char buf[INPUT_SOURCE_BUF_SIZE];
\r
638 InputCallback func;
\r
639 struct _InputSource *second; /* for stderr thread on CPRcmd */
\r
643 InputSource *consoleInputSource;
\r
648 VOID ConsoleOutput(char* data, int length, int forceVisible);
\r
649 VOID ConsoleCreate();
\r
651 ConsoleWndProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam);
\r
652 VOID ColorizeTextPopup(HWND hwnd, ColorClass cc);
\r
653 VOID PrintCommSettings(FILE *f, char *name, DCB *dcb);
\r
654 VOID ParseCommSettings(char *arg, DCB *dcb);
\r
656 StartupDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam);
\r
657 VOID APIENTRY MenuPopup(HWND hwnd, POINT pt, HMENU hmenu, UINT def);
\r
658 void ParseIcsTextMenu(char *icsTextMenuString);
\r
659 VOID PopUpNameDialog(char firstchar);
\r
660 VOID UpdateSampleText(HWND hDlg, int id, MyColorizeAttribs *mca);
\r
664 int GameListOptions();
\r
666 int dummy; // [HGM] for obsolete args
\r
668 HWND hwndMain = NULL; /* root window*/
\r
669 HWND hwndConsole = NULL;
\r
670 HWND commentDialog = NULL;
\r
671 HWND moveHistoryDialog = NULL;
\r
672 HWND evalGraphDialog = NULL;
\r
673 HWND engineOutputDialog = NULL;
\r
674 HWND gameListDialog = NULL;
\r
675 HWND editTagsDialog = NULL;
\r
677 int commentUp = FALSE;
\r
679 WindowPlacement wpMain;
\r
680 WindowPlacement wpConsole;
\r
681 WindowPlacement wpComment;
\r
682 WindowPlacement wpMoveHistory;
\r
683 WindowPlacement wpEvalGraph;
\r
684 WindowPlacement wpEngineOutput;
\r
685 WindowPlacement wpGameList;
\r
686 WindowPlacement wpTags;
\r
688 VOID EngineOptionsPopup(); // [HGM] settings
\r
690 VOID GothicPopUp(char *title, VariantClass variant);
\r
692 * Setting "frozen" should disable all user input other than deleting
\r
693 * the window. We do this while engines are initializing themselves.
\r
695 static int frozen = 0;
\r
696 static int oldMenuItemState[MENU_BAR_ITEMS];
\r
702 if (frozen) return;
\r
704 hmenu = GetMenu(hwndMain);
\r
705 for (i=0; i<MENU_BAR_ITEMS; i++) {
\r
706 oldMenuItemState[i] = EnableMenuItem(hmenu, i, MF_BYPOSITION|MF_GRAYED);
\r
708 DrawMenuBar(hwndMain);
\r
711 /* Undo a FreezeUI */
\r
717 if (!frozen) return;
\r
719 hmenu = GetMenu(hwndMain);
\r
720 for (i=0; i<MENU_BAR_ITEMS; i++) {
\r
721 EnableMenuItem(hmenu, i, MF_BYPOSITION|oldMenuItemState[i]);
\r
723 DrawMenuBar(hwndMain);
\r
726 /*static*/ int fromX = -1, fromY = -1, toX, toY; // [HGM] moved upstream, so JAWS can use them
\r
728 /* JAWS preparation patch (WinBoard for the sight impaired). Define required insertions as empty */
\r
734 #define JAWS_ALT_INTERCEPT
\r
735 #define JAWS_KB_NAVIGATION
\r
736 #define JAWS_MENU_ITEMS
\r
737 #define JAWS_SILENCE
\r
738 #define JAWS_REPLAY
\r
740 #define JAWS_COPYRIGHT
\r
741 #define JAWS_DELETE(X) X
\r
742 #define SAYMACHINEMOVE()
\r
746 /*---------------------------------------------------------------------------*\
\r
750 \*---------------------------------------------------------------------------*/
\r
753 WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
\r
754 LPSTR lpCmdLine, int nCmdShow)
\r
757 HANDLE hAccelMain, hAccelNoAlt, hAccelNoICS;
\r
758 // INITCOMMONCONTROLSEX ex;
\r
762 LoadLibrary("RICHED32.DLL");
\r
763 consoleCF.cbSize = sizeof(CHARFORMAT);
\r
765 if (!InitApplication(hInstance)) {
\r
768 if (!InitInstance(hInstance, nCmdShow, lpCmdLine)) {
\r
775 // InitCommonControlsEx(&ex);
\r
776 InitCommonControls();
\r
778 hAccelMain = LoadAccelerators (hInstance, szAppName);
\r
779 hAccelNoAlt = LoadAccelerators (hInstance, "NO_ALT");
\r
780 hAccelNoICS = LoadAccelerators( hInstance, "NO_ICS"); /* [AS] No Ctrl-V on ICS!!! */
\r
782 /* Acquire and dispatch messages until a WM_QUIT message is received. */
\r
784 while (GetMessage(&msg, /* message structure */
\r
785 NULL, /* handle of window receiving the message */
\r
786 0, /* lowest message to examine */
\r
787 0)) /* highest message to examine */
\r
790 if(msg.message == WM_CHAR && msg.wParam == '\t') {
\r
791 // [HGM] navigate: switch between all windows with tab
\r
792 HWND e1 = NULL, e2 = NULL, mh = NULL, hInput = NULL, hText = NULL;
\r
793 int i, currentElement = 0;
\r
795 // first determine what element of the chain we come from (if any)
\r
796 if(appData.icsActive) {
\r
797 hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
798 hText = GetDlgItem(hwndConsole, OPT_ConsoleText);
\r
800 if(engineOutputDialog && EngineOutputIsUp()) {
\r
801 e1 = GetDlgItem(engineOutputDialog, IDC_EngineMemo1);
\r
802 e2 = GetDlgItem(engineOutputDialog, IDC_EngineMemo2);
\r
804 if(moveHistoryDialog && MoveHistoryIsUp()) {
\r
805 mh = GetDlgItem(moveHistoryDialog, IDC_MoveHistory);
\r
807 if(msg.hwnd == hwndMain) currentElement = 7 ; else
\r
808 if(msg.hwnd == engineOutputDialog) currentElement = 2; else
\r
809 if(msg.hwnd == e1) currentElement = 2; else
\r
810 if(msg.hwnd == e2) currentElement = 3; else
\r
811 if(msg.hwnd == moveHistoryDialog) currentElement = 4; else
\r
812 if(msg.hwnd == mh) currentElement = 4; else
\r
813 if(msg.hwnd == evalGraphDialog) currentElement = 6; else
\r
814 if(msg.hwnd == hText) currentElement = 5; else
\r
815 if(msg.hwnd == hInput) currentElement = 6; else
\r
816 for (i = 0; i < N_BUTTONS; i++) {
\r
817 if (buttonDesc[i].hwnd == msg.hwnd) { currentElement = 1; break; }
\r
820 // determine where to go to
\r
821 if(currentElement) { HWND h = NULL; int direction = GetKeyState(VK_SHIFT) < 0 ? -1 : 1;
\r
823 currentElement = (currentElement + direction) % 7;
\r
824 switch(currentElement) {
\r
826 h = hwndMain; break; // passing this case always makes the loop exit
\r
828 h = buttonDesc[0].hwnd; break; // could be NULL
\r
830 if(!EngineOutputIsUp()) continue; // skip closed auxiliary windows
\r
833 if(!EngineOutputIsUp()) continue;
\r
836 if(!MoveHistoryIsUp()) continue;
\r
838 // case 6: // input to eval graph does not seem to get here!
\r
839 // if(!EvalGraphIsUp()) continue;
\r
840 // h = evalGraphDialog; break;
\r
842 if(!appData.icsActive) continue;
\r
846 if(!appData.icsActive) continue;
\r
852 if(currentElement > 4 && IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);
\r
853 if(currentElement < 5 && IsIconic(hwndMain)) ShowWindow(hwndMain, SW_RESTORE); // all open together
\r
856 continue; // this message now has been processed
\r
860 if (!(commentDialog && IsDialogMessage(commentDialog, &msg)) &&
\r
861 !(moveHistoryDialog && IsDialogMessage(moveHistoryDialog, &msg)) &&
\r
862 !(evalGraphDialog && IsDialogMessage(evalGraphDialog, &msg)) &&
\r
863 !(engineOutputDialog && IsDialogMessage(engineOutputDialog, &msg)) &&
\r
864 !(editTagsDialog && IsDialogMessage(editTagsDialog, &msg)) &&
\r
865 !(gameListDialog && IsDialogMessage(gameListDialog, &msg)) &&
\r
866 !(errorDialog && IsDialogMessage(errorDialog, &msg)) &&
\r
867 !(!frozen && TranslateAccelerator(hwndMain, hAccelMain, &msg)) && JAWS_ACCEL
\r
868 !(!hwndConsole && TranslateAccelerator(hwndMain, hAccelNoICS, &msg)) &&
\r
869 !(!hwndConsole && TranslateAccelerator(hwndMain, hAccelNoAlt, &msg))) {
\r
870 int done = 0, i; // [HGM] chat: dispatch cat-box messages
\r
871 for(i=0; i<MAX_CHAT; i++)
\r
872 if(chatHandle[i] && IsDialogMessage(chatHandle[i], &msg)) {
\r
875 if(done) continue; // [HGM] chat: end patch
\r
876 TranslateMessage(&msg); /* Translates virtual key codes */
\r
877 DispatchMessage(&msg); /* Dispatches message to window */
\r
882 return (msg.wParam); /* Returns the value from PostQuitMessage */
\r
885 /*---------------------------------------------------------------------------*\
\r
887 * Initialization functions
\r
889 \*---------------------------------------------------------------------------*/
\r
893 { // update user logo if necessary
\r
894 static char oldUserName[MSG_SIZ], dir[MSG_SIZ], *curName;
\r
896 if(appData.autoLogo) {
\r
897 curName = UserName();
\r
898 if(strcmp(curName, oldUserName)) {
\r
899 GetCurrentDirectory(MSG_SIZ, dir);
\r
900 SetCurrentDirectory(installDir);
\r
901 snprintf(oldUserName, MSG_SIZ, "logos\\%s.bmp", curName);
\r
902 userLogo = LoadImage( 0, oldUserName, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );
\r
903 safeStrCpy(oldUserName, curName, sizeof(oldUserName)/sizeof(oldUserName[0]) );
\r
904 if(userLogo == NULL)
\r
905 userLogo = LoadImage( 0, "logos\\dummy.bmp", IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );
\r
906 SetCurrentDirectory(dir); /* return to prev directory */
\r
912 InitApplication(HINSTANCE hInstance)
\r
916 /* Fill in window class structure with parameters that describe the */
\r
919 wc.style = CS_HREDRAW | CS_VREDRAW; /* Class style(s). */
\r
920 wc.lpfnWndProc = (WNDPROC)WndProc; /* Window Procedure */
\r
921 wc.cbClsExtra = 0; /* No per-class extra data. */
\r
922 wc.cbWndExtra = 0; /* No per-window extra data. */
\r
923 wc.hInstance = hInstance; /* Owner of this class */
\r
924 wc.hIcon = LoadIcon(hInstance, "icon_white");
\r
925 wc.hCursor = LoadCursor(NULL, IDC_ARROW); /* Cursor */
\r
926 wc.hbrBackground = (HBRUSH)(COLOR_WINDOW+1); /* Default color */
\r
927 wc.lpszMenuName = szAppName; /* Menu name from .RC */
\r
928 wc.lpszClassName = szAppName; /* Name to register as */
\r
930 /* Register the window class and return success/failure code. */
\r
931 if (!RegisterClass(&wc)) return FALSE;
\r
933 wc.style = CS_HREDRAW | CS_VREDRAW;
\r
934 wc.lpfnWndProc = (WNDPROC)ConsoleWndProc;
\r
936 wc.cbWndExtra = DLGWINDOWEXTRA;
\r
937 wc.hInstance = hInstance;
\r
938 wc.hIcon = LoadIcon(hInstance, "icon_white");
\r
939 wc.hCursor = LoadCursor(NULL, IDC_ARROW);
\r
940 wc.hbrBackground = (HBRUSH)(COLOR_MENU+1);
\r
941 wc.lpszMenuName = NULL;
\r
942 wc.lpszClassName = szConsoleName;
\r
944 if (!RegisterClass(&wc)) return FALSE;
\r
949 /* Set by InitInstance, used by EnsureOnScreen */
\r
950 int screenHeight, screenWidth;
\r
953 EnsureOnScreen(int *x, int *y, int minX, int minY)
\r
955 // int gap = GetSystemMetrics(SM_CYFRAME) + GetSystemMetrics(SM_CYCAPTION);
\r
956 /* Be sure window at (x,y) is not off screen (or even mostly off screen) */
\r
957 if (*x > screenWidth - 32) *x = 0;
\r
958 if (*y > screenHeight - 32) *y = 0;
\r
959 if (*x < minX) *x = minX;
\r
960 if (*y < minY) *y = minY;
\r
964 LoadLogo(ChessProgramState *cps, int n, Boolean ics)
\r
966 char buf[MSG_SIZ], dir[MSG_SIZ];
\r
967 GetCurrentDirectory(MSG_SIZ, dir);
\r
968 SetCurrentDirectory(installDir);
\r
969 if( appData.logo[n] && appData.logo[n][0] != NULLCHAR) {
\r
970 cps->programLogo = LoadImage( 0, appData.logo[n], IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );
\r
972 if (cps->programLogo == NULL && appData.debugMode) {
\r
973 fprintf( debugFP, "Unable to load logo bitmap '%s'\n", appData.logo[n] );
\r
975 } else if(appData.autoLogo) {
\r
976 if(ics) { // [HGM] logo: in ICS mode second can be used for ICS
\r
977 sprintf(buf, "logos\\%s.bmp", appData.icsHost);
\r
978 cps->programLogo = LoadImage( 0, buf, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );
\r
980 if(appData.directory[n] && appData.directory[n][0]) {
\r
981 SetCurrentDirectory(appData.directory[n]);
\r
982 cps->programLogo = LoadImage( 0, "logo.bmp", IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );
\r
985 SetCurrentDirectory(dir); /* return to prev directory */
\r
991 ZeroMemory( &backTextureSquareInfo, sizeof(backTextureSquareInfo) );
\r
992 backTextureSquareSize = 0; // kludge to force recalculation of texturemode
\r
994 if( appData.liteBackTextureFile && appData.liteBackTextureFile[0] != NULLCHAR && appData.liteBackTextureFile[0] != '*' ) {
\r
995 liteBackTexture = LoadImage( 0, appData.liteBackTextureFile, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );
\r
996 liteBackTextureMode = appData.liteBackTextureMode;
\r
998 if (liteBackTexture == NULL && appData.debugMode) {
\r
999 fprintf( debugFP, "Unable to load lite texture bitmap '%s'\n", appData.liteBackTextureFile );
\r
1003 if( appData.darkBackTextureFile && appData.darkBackTextureFile[0] != NULLCHAR && appData.darkBackTextureFile[0] != '*' ) {
\r
1004 darkBackTexture = LoadImage( 0, appData.darkBackTextureFile, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );
\r
1005 darkBackTextureMode = appData.darkBackTextureMode;
\r
1007 if (darkBackTexture == NULL && appData.debugMode) {
\r
1008 fprintf( debugFP, "Unable to load dark texture bitmap '%s'\n", appData.darkBackTextureFile );
\r
1014 InitInstance(HINSTANCE hInstance, int nCmdShow, LPSTR lpCmdLine)
\r
1016 HWND hwnd; /* Main window handle. */
\r
1018 WINDOWPLACEMENT wp;
\r
1021 hInst = hInstance; /* Store instance handle in our global variable */
\r
1022 programName = szAppName;
\r
1024 if (SearchPath(NULL, "WinBoard.exe", NULL, MSG_SIZ, installDir, &filepart)) {
\r
1025 *filepart = NULLCHAR;
\r
1027 GetCurrentDirectory(MSG_SIZ, installDir);
\r
1029 gameInfo.boardWidth = gameInfo.boardHeight = 8; // [HGM] won't have open window otherwise
\r
1030 screenWidth = screenHeight = 1000; // [HGM] placement: kludge to allow calling EnsureOnScreen from InitAppData
\r
1031 InitAppData(lpCmdLine); /* Get run-time parameters */
\r
1032 /* xboard, and older WinBoards, controlled the move sound with the
\r
1033 appData.ringBellAfterMoves option. In the current WinBoard, we
\r
1034 always turn the option on (so that the backend will call us),
\r
1035 then let the user turn the sound off by setting it to silence if
\r
1036 desired. To accommodate old winboard.ini files saved by old
\r
1037 versions of WinBoard, we also turn off the sound if the option
\r
1038 was initially set to false. [HGM] taken out of InitAppData */
\r
1039 if (!appData.ringBellAfterMoves) {
\r
1040 sounds[(int)SoundMove].name = strdup("");
\r
1041 appData.ringBellAfterMoves = TRUE;
\r
1043 if (appData.debugMode) {
\r
1044 debugFP = fopen(appData.nameOfDebugFile, "w");
\r
1045 setbuf(debugFP, NULL);
\r
1048 LoadLanguageFile(appData.language);
\r
1052 // InitEngineUCI( installDir, &first ); // [HGM] incorporated in InitBackEnd1()
\r
1053 // InitEngineUCI( installDir, &second );
\r
1055 /* Create a main window for this application instance. */
\r
1056 hwnd = CreateWindow(szAppName, szTitle,
\r
1057 (WS_OVERLAPPEDWINDOW & ~WS_MAXIMIZEBOX),
\r
1058 CW_USEDEFAULT, 0, CW_USEDEFAULT, 0,
\r
1059 NULL, NULL, hInstance, NULL);
\r
1062 /* If window could not be created, return "failure" */
\r
1067 /* [HGM] logo: Load logos if specified (must be done before InitDrawingSizes) */
\r
1068 LoadLogo(&first, 0, FALSE);
\r
1069 LoadLogo(&second, 1, appData.icsActive);
\r
1073 iconWhite = LoadIcon(hInstance, "icon_white");
\r
1074 iconBlack = LoadIcon(hInstance, "icon_black");
\r
1075 iconCurrent = iconWhite;
\r
1076 InitDrawingColors();
\r
1077 screenHeight = GetSystemMetrics(SM_CYSCREEN);
\r
1078 screenWidth = GetSystemMetrics(SM_CXSCREEN);
\r
1079 for (ibs = (int) NUM_SIZES - 1; ibs >= 0; ibs--) {
\r
1080 /* Compute window size for each board size, and use the largest
\r
1081 size that fits on this screen as the default. */
\r
1082 InitDrawingSizes((BoardSize)(ibs+1000), 0);
\r
1083 if (boardSize == (BoardSize)-1 &&
\r
1084 winH <= screenHeight
\r
1085 - GetSystemMetrics(SM_CYFRAME) - GetSystemMetrics(SM_CYCAPTION) - 10
\r
1086 && winW <= screenWidth) {
\r
1087 boardSize = (BoardSize)ibs;
\r
1091 InitDrawingSizes(boardSize, 0);
\r
1093 buttonCount = GetSystemMetrics(SM_CMOUSEBUTTONS);
\r
1095 /* [AS] Load textures if specified */
\r
1098 mysrandom( (unsigned) time(NULL) );
\r
1100 /* [AS] Restore layout */
\r
1101 if( wpMoveHistory.visible ) {
\r
1102 MoveHistoryPopUp();
\r
1105 if( wpEvalGraph.visible ) {
\r
1109 if( wpEngineOutput.visible ) {
\r
1110 EngineOutputPopUp();
\r
1113 /* Make the window visible; update its client area; and return "success" */
\r
1114 EnsureOnScreen(&wpMain.x, &wpMain.y, minX, minY);
\r
1115 wp.length = sizeof(WINDOWPLACEMENT);
\r
1117 wp.showCmd = nCmdShow;
\r
1118 wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;
\r
1119 wp.rcNormalPosition.left = wpMain.x;
\r
1120 wp.rcNormalPosition.right = wpMain.x + wpMain.width;
\r
1121 wp.rcNormalPosition.top = wpMain.y;
\r
1122 wp.rcNormalPosition.bottom = wpMain.y + wpMain.height;
\r
1123 SetWindowPlacement(hwndMain, &wp);
\r
1125 InitBackEnd2(); // [HGM] moved until after all windows placed, to save correct position if fatal error on engine start
\r
1127 if(!appData.noGUI) SetWindowPos(hwndMain, alwaysOnTop ? HWND_TOPMOST : HWND_NOTOPMOST,
\r
1128 0, 0, 0, 0, SWP_NOMOVE|SWP_NOSIZE);
\r
1130 if (hwndConsole) {
\r
1132 SetWindowPos(hwndConsole, alwaysOnTop ? HWND_TOPMOST : HWND_NOTOPMOST,
\r
1133 0, 0, 0, 0, SWP_NOMOVE|SWP_NOSIZE);
\r
1135 ShowWindow(hwndConsole, nCmdShow);
\r
1136 if(appData.chatBoxes) { // [HGM] chat: open chat boxes
\r
1137 char buf[MSG_SIZ], *p = buf, *q;
\r
1138 safeStrCpy(buf, appData.chatBoxes, sizeof(buf)/sizeof(buf[0]) );
\r
1140 q = strchr(p, ';');
\r
1142 if(*p) ChatPopUp(p);
\r
1145 SetActiveWindow(hwndConsole);
\r
1147 if(!appData.noGUI) UpdateWindow(hwnd); else ShowWindow(hwnd, SW_MINIMIZE);
\r
1148 if(gameListDialog) SetFocus(gameListDialog); // [HGM] jaws: for if we clicked multi-game game file
\r
1157 HMENU hmenu = GetMenu(hwndMain);
\r
1159 (void) EnableMenuItem(hmenu, IDM_CommPort,
\r
1160 MF_BYCOMMAND|((appData.icsActive &&
\r
1161 *appData.icsCommPort != NULLCHAR) ?
\r
1162 MF_ENABLED : MF_GRAYED));
\r
1163 (void) CheckMenuItem(hmenu, IDM_SaveSettingsOnExit,
\r
1164 MF_BYCOMMAND|(saveSettingsOnExit ?
\r
1165 MF_CHECKED : MF_UNCHECKED));
\r
1168 //---------------------------------------------------------------------------------------------------------
\r
1170 #define ICS_TEXT_MENU_SIZE (IDM_CommandXLast - IDM_CommandX + 1)
\r
1171 #define XBOARD FALSE
\r
1173 #define OPTCHAR "/"
\r
1174 #define SEPCHAR "="
\r
1178 // front-end part of option handling
\r
1181 LFfromMFP(LOGFONT* lf, MyFontParams *mfp)
\r
1183 HDC hdc = CreateDC("DISPLAY", NULL, NULL, NULL);
\r
1184 lf->lfHeight = -(int)(mfp->pointSize * GetDeviceCaps(hdc, LOGPIXELSY) / 72.0 + 0.5);
\r
1187 lf->lfEscapement = 0;
\r
1188 lf->lfOrientation = 0;
\r
1189 lf->lfWeight = mfp->bold ? FW_BOLD : FW_NORMAL;
\r
1190 lf->lfItalic = mfp->italic;
\r
1191 lf->lfUnderline = mfp->underline;
\r
1192 lf->lfStrikeOut = mfp->strikeout;
\r
1193 lf->lfCharSet = mfp->charset;
\r
1194 lf->lfOutPrecision = OUT_DEFAULT_PRECIS;
\r
1195 lf->lfClipPrecision = CLIP_DEFAULT_PRECIS;
\r
1196 lf->lfQuality = DEFAULT_QUALITY;
\r
1197 lf->lfPitchAndFamily = DEFAULT_PITCH|FF_DONTCARE;
\r
1198 safeStrCpy(lf->lfFaceName, mfp->faceName, sizeof(lf->lfFaceName)/sizeof(lf->lfFaceName[0]) );
\r
1202 CreateFontInMF(MyFont *mf)
\r
1204 LFfromMFP(&mf->lf, &mf->mfp);
\r
1205 if (mf->hf) DeleteObject(mf->hf);
\r
1206 mf->hf = CreateFontIndirect(&mf->lf);
\r
1209 // [HGM] This platform-dependent table provides the location for storing the color info
\r
1211 colorVariable[] = {
\r
1212 &whitePieceColor,
\r
1213 &blackPieceColor,
\r
1214 &lightSquareColor,
\r
1215 &darkSquareColor,
\r
1216 &highlightSquareColor,
\r
1217 &premoveHighlightColor,
\r
1219 &consoleBackgroundColor,
\r
1220 &appData.fontForeColorWhite,
\r
1221 &appData.fontBackColorWhite,
\r
1222 &appData.fontForeColorBlack,
\r
1223 &appData.fontBackColorBlack,
\r
1224 &appData.evalHistColorWhite,
\r
1225 &appData.evalHistColorBlack,
\r
1226 &appData.highlightArrowColor,
\r
1229 /* Command line font name parser. NULL name means do nothing.
\r
1230 Syntax like "Courier New:10.0 bi" or "Arial:10" or "Arial:10b"
\r
1231 For backward compatibility, syntax without the colon is also
\r
1232 accepted, but font names with digits in them won't work in that case.
\r
1235 ParseFontName(char *name, MyFontParams *mfp)
\r
1238 if (name == NULL) return;
\r
1240 q = strchr(p, ':');
\r
1242 if (q - p >= sizeof(mfp->faceName))
\r
1243 ExitArgError(_("Font name too long:"), name, TRUE);
\r
1244 memcpy(mfp->faceName, p, q - p);
\r
1245 mfp->faceName[q - p] = NULLCHAR;
\r
1248 q = mfp->faceName;
\r
1249 while (*p && !isdigit(*p)) {
\r
1251 if (q - mfp->faceName >= sizeof(mfp->faceName))
\r
1252 ExitArgError(_("Font name too long:"), name, TRUE);
\r
1254 while (q > mfp->faceName && q[-1] == ' ') q--;
\r
1257 if (!*p) ExitArgError(_("Font point size missing:"), name, TRUE);
\r
1258 mfp->pointSize = (float) atof(p);
\r
1259 mfp->bold = (strchr(p, 'b') != NULL);
\r
1260 mfp->italic = (strchr(p, 'i') != NULL);
\r
1261 mfp->underline = (strchr(p, 'u') != NULL);
\r
1262 mfp->strikeout = (strchr(p, 's') != NULL);
\r
1263 mfp->charset = DEFAULT_CHARSET;
\r
1264 q = strchr(p, 'c');
\r
1266 mfp->charset = (BYTE) atoi(q+1);
\r
1270 ParseFont(char *name, int number)
\r
1271 { // wrapper to shield back-end from 'font'
\r
1272 ParseFontName(name, &font[boardSize][number]->mfp);
\r
1277 { // in WB we have a 2D array of fonts; this initializes their description
\r
1279 /* Point font array elements to structures and
\r
1280 parse default font names */
\r
1281 for (i=0; i<NUM_FONTS; i++) {
\r
1282 for (j=0; j<NUM_SIZES; j++) {
\r
1283 font[j][i] = &fontRec[j][i];
\r
1284 ParseFontName(font[j][i]->def, &font[j][i]->mfp);
\r
1291 { // here we create the actual fonts from the selected descriptions
\r
1293 for (i=0; i<NUM_FONTS; i++) {
\r
1294 for (j=0; j<NUM_SIZES; j++) {
\r
1295 CreateFontInMF(font[j][i]);
\r
1299 /* Color name parser.
\r
1300 X version accepts X color names, but this one
\r
1301 handles only the #rrggbb form (hex) or rrr,ggg,bbb (decimal) */
\r
1303 ParseColorName(char *name)
\r
1305 int red, green, blue, count;
\r
1306 char buf[MSG_SIZ];
\r
1308 count = sscanf(name, "#%2x%2x%2x", &red, &green, &blue);
\r
1310 count = sscanf(name, "%3d%*[^0-9]%3d%*[^0-9]%3d",
\r
1311 &red, &green, &blue);
\r
1314 snprintf(buf, MSG_SIZ, _("Can't parse color name %s"), name);
\r
1315 DisplayError(buf, 0);
\r
1316 return RGB(0, 0, 0);
\r
1318 return PALETTERGB(red, green, blue);
\r
1322 ParseColor(int n, char *name)
\r
1323 { // for WinBoard the color is an int, which needs to be derived from the string
\r
1324 if(colorVariable[n]) *(int*)colorVariable[n] = ParseColorName(name);
\r
1328 ParseAttribs(COLORREF *color, int *effects, char* argValue)
\r
1330 char *e = argValue;
\r
1334 if (*e == 'b') eff |= CFE_BOLD;
\r
1335 else if (*e == 'i') eff |= CFE_ITALIC;
\r
1336 else if (*e == 'u') eff |= CFE_UNDERLINE;
\r
1337 else if (*e == 's') eff |= CFE_STRIKEOUT;
\r
1338 else if (*e == '#' || isdigit(*e)) break;
\r
1342 *color = ParseColorName(e);
\r
1346 ParseTextAttribs(ColorClass cc, char *s)
\r
1347 { // [HGM] front-end wrapper that does the platform-dependent call
\r
1348 // for XBoard we would set (&appData.colorShout)[cc] = strdup(s);
\r
1349 ParseAttribs(&textAttribs[cc].color, &textAttribs[cc].effects, s);
\r
1353 ParseBoardSize(void *addr, char *name)
\r
1354 { // [HGM] rewritten with return-value ptr to shield back-end from BoardSize
\r
1355 BoardSize bs = SizeTiny;
\r
1356 while (sizeInfo[bs].name != NULL) {
\r
1357 if (StrCaseCmp(name, sizeInfo[bs].name) == 0) {
\r
1358 *(BoardSize *)addr = bs;
\r
1363 ExitArgError(_("Unrecognized board size value"), name, TRUE);
\r
1368 { // [HGM] import name from appData first
\r
1371 for (cc = (ColorClass)0; cc < ColorNormal; cc++) {
\r
1372 textAttribs[cc].sound.name = strdup((&appData.soundShout)[cc]);
\r
1373 textAttribs[cc].sound.data = NULL;
\r
1374 MyLoadSound(&textAttribs[cc].sound);
\r
1376 for (cc = ColorNormal; cc < NColorClasses; cc++) {
\r
1377 textAttribs[cc].sound.name = strdup("");
\r
1378 textAttribs[cc].sound.data = NULL;
\r
1380 for (sc = (SoundClass)0; sc < NSoundClasses; sc++) {
\r
1381 sounds[sc].name = strdup((&appData.soundMove)[sc]);
\r
1382 sounds[sc].data = NULL;
\r
1383 MyLoadSound(&sounds[sc]);
\r
1388 SetCommPortDefaults()
\r
1390 memset(&dcb, 0, sizeof(DCB)); // required by VS 2002 +
\r
1391 dcb.DCBlength = sizeof(DCB);
\r
1392 dcb.BaudRate = 9600;
\r
1393 dcb.fBinary = TRUE;
\r
1394 dcb.fParity = FALSE;
\r
1395 dcb.fOutxCtsFlow = FALSE;
\r
1396 dcb.fOutxDsrFlow = FALSE;
\r
1397 dcb.fDtrControl = DTR_CONTROL_ENABLE;
\r
1398 dcb.fDsrSensitivity = FALSE;
\r
1399 dcb.fTXContinueOnXoff = TRUE;
\r
1400 dcb.fOutX = FALSE;
\r
1402 dcb.fNull = FALSE;
\r
1403 dcb.fRtsControl = RTS_CONTROL_ENABLE;
\r
1404 dcb.fAbortOnError = FALSE;
\r
1406 dcb.Parity = SPACEPARITY;
\r
1407 dcb.StopBits = ONESTOPBIT;
\r
1410 // [HGM] args: these three cases taken out to stay in front-end
\r
1412 SaveFontArg(FILE *f, ArgDescriptor *ad)
\r
1413 { // in WinBoard every board size has its own font, and the "argLoc" identifies the table,
\r
1414 // while the curent board size determines the element. This system should be ported to XBoard.
\r
1415 // What the table contains pointers to, and how to print the font description, remains platform-dependent
\r
1417 for (bs=0; bs<NUM_SIZES; bs++) {
\r
1418 MyFontParams *mfp = &font[bs][(int) ad->argLoc]->mfp;
\r
1419 fprintf(f, "/size=%s ", sizeInfo[bs].name);
\r
1420 fprintf(f, "/%s=\"%s:%g%s%s%s%s%sc%d\"\n",
\r
1421 ad->argName, mfp->faceName, mfp->pointSize,
\r
1422 mfp->bold || mfp->italic || mfp->underline || mfp->strikeout ? " " : "",
\r
1423 mfp->bold ? "b" : "",
\r
1424 mfp->italic ? "i" : "",
\r
1425 mfp->underline ? "u" : "",
\r
1426 mfp->strikeout ? "s" : "",
\r
1427 (int)mfp->charset);
\r
1433 { // [HGM] copy the names from the internal WB variables to appData
\r
1436 for (cc = (ColorClass)0; cc < ColorNormal; cc++)
\r
1437 (&appData.soundShout)[cc] = textAttribs[cc].sound.name;
\r
1438 for (sc = (SoundClass)0; sc < NSoundClasses; sc++)
\r
1439 (&appData.soundMove)[sc] = sounds[sc].name;
\r
1443 SaveAttribsArg(FILE *f, ArgDescriptor *ad)
\r
1444 { // here the "argLoc" defines a table index. It could have contained the 'ta' pointer itself, though
\r
1445 MyTextAttribs* ta = &textAttribs[(ColorClass)ad->argLoc];
\r
1446 fprintf(f, "/%s=\"%s%s%s%s%s#%02lx%02lx%02lx\"\n", ad->argName,
\r
1447 (ta->effects & CFE_BOLD) ? "b" : "",
\r
1448 (ta->effects & CFE_ITALIC) ? "i" : "",
\r
1449 (ta->effects & CFE_UNDERLINE) ? "u" : "",
\r
1450 (ta->effects & CFE_STRIKEOUT) ? "s" : "",
\r
1451 (ta->effects) ? " " : "",
\r
1452 ta->color&0xff, (ta->color >> 8)&0xff, (ta->color >> 16)&0xff);
\r
1456 SaveColor(FILE *f, ArgDescriptor *ad)
\r
1457 { // in WinBoard the color is an int and has to be converted to text. In X it would be a string already?
\r
1458 COLORREF color = *(COLORREF *)colorVariable[(int)ad->argLoc];
\r
1459 fprintf(f, "/%s=#%02lx%02lx%02lx\n", ad->argName,
\r
1460 color&0xff, (color>>8)&0xff, (color>>16)&0xff);
\r
1464 SaveBoardSize(FILE *f, char *name, void *addr)
\r
1465 { // wrapper to shield back-end from BoardSize & sizeInfo
\r
1466 fprintf(f, "/%s=%s\n", name, sizeInfo[*(BoardSize *)addr].name);
\r
1470 ParseCommPortSettings(char *s)
\r
1471 { // wrapper to keep dcb from back-end
\r
1472 ParseCommSettings(s, &dcb);
\r
1477 { // wrapper to shield use of window handles from back-end (make addressible by number?)
\r
1478 GetActualPlacement(hwndMain, &wpMain);
\r
1479 GetActualPlacement(hwndConsole, &wpConsole);
\r
1480 GetActualPlacement(commentDialog, &wpComment);
\r
1481 GetActualPlacement(editTagsDialog, &wpTags);
\r
1482 GetActualPlacement(gameListDialog, &wpGameList);
\r
1483 GetActualPlacement(moveHistoryDialog, &wpMoveHistory);
\r
1484 GetActualPlacement(evalGraphDialog, &wpEvalGraph);
\r
1485 GetActualPlacement(engineOutputDialog, &wpEngineOutput);
\r
1489 PrintCommPortSettings(FILE *f, char *name)
\r
1490 { // wrapper to shield back-end from DCB
\r
1491 PrintCommSettings(f, name, &dcb);
\r
1495 MySearchPath(char *installDir, char *name, char *fullname)
\r
1497 char *dummy, buf[MSG_SIZ], *p = name, *q;
\r
1498 if(name[0]== '%') {
\r
1499 fullname[0] = 0; // [HGM] first expand any environment variables in the given name
\r
1500 while(*p == '%' && (q = strchr(p+1, '%'))) { // [HGM] recognize %*% as environment variable
\r
1501 safeStrCpy(buf, p+1, sizeof(buf)/sizeof(buf[0]) );
\r
1502 *strchr(buf, '%') = 0;
\r
1503 strcat(fullname, getenv(buf));
\r
1504 p = q+1; while(*p == '\\') { strcat(fullname, "\\"); p++; }
\r
1506 strcat(fullname, p); // after environment variables (if any), take the remainder of the given name
\r
1507 if(appData.debugMode) fprintf(debugFP, "name = '%s', expanded name = '%s'\n", name, fullname);
\r
1508 return (int) strlen(fullname);
\r
1510 return (int) SearchPath(installDir, name, NULL, MSG_SIZ, fullname, &dummy);
\r
1514 MyGetFullPathName(char *name, char *fullname)
\r
1517 return (int) GetFullPathName(name, MSG_SIZ, fullname, &dummy);
\r
1522 { // [HGM] args: allows testing if main window is realized from back-end
\r
1523 return hwndMain != NULL;
\r
1527 PopUpStartupDialog()
\r
1531 LoadLanguageFile(appData.language);
\r
1532 lpProc = MakeProcInstance((FARPROC)StartupDialog, hInst);
\r
1533 DialogBox(hInst, MAKEINTRESOURCE(DLG_Startup), NULL, (DLGPROC)lpProc);
\r
1534 FreeProcInstance(lpProc);
\r
1537 /*---------------------------------------------------------------------------*\
\r
1539 * GDI board drawing routines
\r
1541 \*---------------------------------------------------------------------------*/
\r
1543 /* [AS] Draw square using background texture */
\r
1544 static void DrawTile( int dx, int dy, int dw, int dh, HDC dst, HDC src, int mode, int sx, int sy )
\r
1549 return; /* Should never happen! */
\r
1552 SetGraphicsMode( dst, GM_ADVANCED );
\r
1559 /* X reflection */
\r
1564 x.eDx = (FLOAT) dw + dx - 1;
\r
1567 SetWorldTransform( dst, &x );
\r
1570 /* Y reflection */
\r
1576 x.eDy = (FLOAT) dh + dy - 1;
\r
1578 SetWorldTransform( dst, &x );
\r
1586 x.eDx = (FLOAT) dx;
\r
1587 x.eDy = (FLOAT) dy;
\r
1590 SetWorldTransform( dst, &x );
\r
1594 BitBlt( dst, dx, dy, dw, dh, src, sx, sy, SRCCOPY );
\r
1602 SetWorldTransform( dst, &x );
\r
1604 ModifyWorldTransform( dst, 0, MWT_IDENTITY );
\r
1607 /* [AS] [HGM] Make room for more piece types, so all pieces can be different */
\r
1609 PM_WP = (int) WhitePawn,
\r
1610 PM_WN = (int) WhiteKnight,
\r
1611 PM_WB = (int) WhiteBishop,
\r
1612 PM_WR = (int) WhiteRook,
\r
1613 PM_WQ = (int) WhiteQueen,
\r
1614 PM_WF = (int) WhiteFerz,
\r
1615 PM_WW = (int) WhiteWazir,
\r
1616 PM_WE = (int) WhiteAlfil,
\r
1617 PM_WM = (int) WhiteMan,
\r
1618 PM_WO = (int) WhiteCannon,
\r
1619 PM_WU = (int) WhiteUnicorn,
\r
1620 PM_WH = (int) WhiteNightrider,
\r
1621 PM_WA = (int) WhiteAngel,
\r
1622 PM_WC = (int) WhiteMarshall,
\r
1623 PM_WAB = (int) WhiteCardinal,
\r
1624 PM_WD = (int) WhiteDragon,
\r
1625 PM_WL = (int) WhiteLance,
\r
1626 PM_WS = (int) WhiteCobra,
\r
1627 PM_WV = (int) WhiteFalcon,
\r
1628 PM_WSG = (int) WhiteSilver,
\r
1629 PM_WG = (int) WhiteGrasshopper,
\r
1630 PM_WK = (int) WhiteKing,
\r
1631 PM_BP = (int) BlackPawn,
\r
1632 PM_BN = (int) BlackKnight,
\r
1633 PM_BB = (int) BlackBishop,
\r
1634 PM_BR = (int) BlackRook,
\r
1635 PM_BQ = (int) BlackQueen,
\r
1636 PM_BF = (int) BlackFerz,
\r
1637 PM_BW = (int) BlackWazir,
\r
1638 PM_BE = (int) BlackAlfil,
\r
1639 PM_BM = (int) BlackMan,
\r
1640 PM_BO = (int) BlackCannon,
\r
1641 PM_BU = (int) BlackUnicorn,
\r
1642 PM_BH = (int) BlackNightrider,
\r
1643 PM_BA = (int) BlackAngel,
\r
1644 PM_BC = (int) BlackMarshall,
\r
1645 PM_BG = (int) BlackGrasshopper,
\r
1646 PM_BAB = (int) BlackCardinal,
\r
1647 PM_BD = (int) BlackDragon,
\r
1648 PM_BL = (int) BlackLance,
\r
1649 PM_BS = (int) BlackCobra,
\r
1650 PM_BV = (int) BlackFalcon,
\r
1651 PM_BSG = (int) BlackSilver,
\r
1652 PM_BK = (int) BlackKing
\r
1655 static HFONT hPieceFont = NULL;
\r
1656 static HBITMAP hPieceMask[(int) EmptySquare];
\r
1657 static HBITMAP hPieceFace[(int) EmptySquare];
\r
1658 static int fontBitmapSquareSize = 0;
\r
1659 static char pieceToFontChar[(int) EmptySquare] =
\r
1660 { 'p', 'n', 'b', 'r', 'q',
\r
1661 'n', 'b', 'p', 'n', 'b', 'r', 'b', 'r', 'q', 'k',
\r
1662 'k', 'o', 'm', 'v', 't', 'w',
\r
1663 'v', 't', 'o', 'm', 'v', 't', 'v', 't', 'w', 'l',
\r
1666 extern BOOL SetCharTable( char *table, const char * map );
\r
1667 /* [HGM] moved to backend.c */
\r
1669 static void SetPieceBackground( HDC hdc, COLORREF color, int mode )
\r
1672 BYTE r1 = GetRValue( color );
\r
1673 BYTE g1 = GetGValue( color );
\r
1674 BYTE b1 = GetBValue( color );
\r
1680 /* Create a uniform background first */
\r
1681 hbrush = CreateSolidBrush( color );
\r
1682 SetRect( &rc, 0, 0, squareSize, squareSize );
\r
1683 FillRect( hdc, &rc, hbrush );
\r
1684 DeleteObject( hbrush );
\r
1687 /* Vertical gradient, good for pawn, knight and rook, less for queen and king */
\r
1688 int steps = squareSize / 2;
\r
1691 for( i=0; i<steps; i++ ) {
\r
1692 BYTE r = r1 - (r1-r2) * i / steps;
\r
1693 BYTE g = g1 - (g1-g2) * i / steps;
\r
1694 BYTE b = b1 - (b1-b2) * i / steps;
\r
1696 hbrush = CreateSolidBrush( RGB(r,g,b) );
\r
1697 SetRect( &rc, i + squareSize - steps, 0, i + squareSize - steps + 1, squareSize );
\r
1698 FillRect( hdc, &rc, hbrush );
\r
1699 DeleteObject(hbrush);
\r
1702 else if( mode == 2 ) {
\r
1703 /* Diagonal gradient, good more or less for every piece */
\r
1704 POINT triangle[3];
\r
1705 HPEN hpen = SelectObject( hdc, GetStockObject(NULL_PEN) );
\r
1706 HBRUSH hbrush_old;
\r
1707 int steps = squareSize;
\r
1710 triangle[0].x = squareSize - steps;
\r
1711 triangle[0].y = squareSize;
\r
1712 triangle[1].x = squareSize;
\r
1713 triangle[1].y = squareSize;
\r
1714 triangle[2].x = squareSize;
\r
1715 triangle[2].y = squareSize - steps;
\r
1717 for( i=0; i<steps; i++ ) {
\r
1718 BYTE r = r1 - (r1-r2) * i / steps;
\r
1719 BYTE g = g1 - (g1-g2) * i / steps;
\r
1720 BYTE b = b1 - (b1-b2) * i / steps;
\r
1722 hbrush = CreateSolidBrush( RGB(r,g,b) );
\r
1723 hbrush_old = SelectObject( hdc, hbrush );
\r
1724 Polygon( hdc, triangle, 3 );
\r
1725 SelectObject( hdc, hbrush_old );
\r
1726 DeleteObject(hbrush);
\r
1731 SelectObject( hdc, hpen );
\r
1736 [AS] The method I use to create the bitmaps it a bit tricky, but it
\r
1737 seems to work ok. The main problem here is to find the "inside" of a chess
\r
1738 piece: follow the steps as explained below.
\r
1740 static void CreatePieceMaskFromFont( HDC hdc_window, HDC hdc, int index )
\r
1744 COLORREF chroma = RGB(0xFF,0x00,0xFF);
\r
1748 int backColor = whitePieceColor;
\r
1749 int foreColor = blackPieceColor;
\r
1751 if( index < (int)BlackPawn && appData.fontBackColorWhite != appData.fontForeColorWhite ) {
\r
1752 backColor = appData.fontBackColorWhite;
\r
1753 foreColor = appData.fontForeColorWhite;
\r
1755 else if( index >= (int)BlackPawn && appData.fontBackColorBlack != appData.fontForeColorBlack ) {
\r
1756 backColor = appData.fontBackColorBlack;
\r
1757 foreColor = appData.fontForeColorBlack;
\r
1761 hbm = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );
\r
1763 hbm_old = SelectObject( hdc, hbm );
\r
1767 rc.right = squareSize;
\r
1768 rc.bottom = squareSize;
\r
1770 /* Step 1: background is now black */
\r
1771 FillRect( hdc, &rc, GetStockObject(BLACK_BRUSH) );
\r
1773 GetTextExtentPoint32( hdc, &pieceToFontChar[index], 1, &sz );
\r
1775 pt.x = (squareSize - sz.cx) / 2;
\r
1776 pt.y = (squareSize - sz.cy) / 2;
\r
1778 SetBkMode( hdc, TRANSPARENT );
\r
1779 SetTextColor( hdc, chroma );
\r
1780 /* Step 2: the piece has been drawn in purple, there are now black and purple in this bitmap */
\r
1781 TextOut( hdc, pt.x, pt.y, &pieceToFontChar[appData.allWhite && index >= (int)BlackPawn ? index - (int)BlackPawn : index], 1 );
\r
1783 SelectObject( hdc, GetStockObject(WHITE_BRUSH) );
\r
1784 /* Step 3: the area outside the piece is filled with white */
\r
1785 // FloodFill( hdc, 0, 0, chroma );
\r
1786 ExtFloodFill( hdc, 0, 0, 0, FLOODFILLSURFACE );
\r
1787 ExtFloodFill( hdc, 0, squareSize-1, 0, FLOODFILLSURFACE ); // [HGM] fill from all 4 corners, for if piece too big
\r
1788 ExtFloodFill( hdc, squareSize-1, 0, 0, FLOODFILLSURFACE );
\r
1789 ExtFloodFill( hdc, squareSize-1, squareSize-1, 0, FLOODFILLSURFACE );
\r
1790 SelectObject( hdc, GetStockObject(BLACK_BRUSH) );
\r
1792 Step 4: this is the tricky part, the area inside the piece is filled with black,
\r
1793 but if the start point is not inside the piece we're lost!
\r
1794 There should be a better way to do this... if we could create a region or path
\r
1795 from the fill operation we would be fine for example.
\r
1797 // FloodFill( hdc, squareSize / 2, squareSize / 2, RGB(0xFF,0xFF,0xFF) );
\r
1798 ExtFloodFill( hdc, squareSize / 2, squareSize / 2, RGB(0xFF,0xFF,0xFF), FLOODFILLBORDER );
\r
1800 { /* [HGM] shave off edges of mask, in an attempt to correct for the fact that FloodFill does not work correctly under Win XP */
\r
1801 HDC dc2 = CreateCompatibleDC( hdc_window );
\r
1802 HBITMAP bm2 = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );
\r
1804 SelectObject( dc2, bm2 );
\r
1805 BitBlt( dc2, 0, 0, squareSize, squareSize, hdc, 0, 0, SRCCOPY ); // make copy
\r
1806 BitBlt( hdc, 0, 1, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );
\r
1807 BitBlt( hdc, 2, 1, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );
\r
1808 BitBlt( hdc, 1, 0, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );
\r
1809 BitBlt( hdc, 1, 2, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );
\r
1812 DeleteObject( bm2 );
\r
1815 SetTextColor( hdc, 0 );
\r
1817 Step 5: some fonts have "disconnected" areas that are skipped by the fill:
\r
1818 draw the piece again in black for safety.
\r
1820 TextOut( hdc, pt.x, pt.y, &pieceToFontChar[appData.allWhite && index >= (int)BlackPawn ? index - (int)BlackPawn : index], 1 );
\r
1822 SelectObject( hdc, hbm_old );
\r
1824 if( hPieceMask[index] != NULL ) {
\r
1825 DeleteObject( hPieceMask[index] );
\r
1828 hPieceMask[index] = hbm;
\r
1831 hbm = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );
\r
1833 SelectObject( hdc, hbm );
\r
1836 HDC dc1 = CreateCompatibleDC( hdc_window );
\r
1837 HDC dc2 = CreateCompatibleDC( hdc_window );
\r
1838 HBITMAP bm2 = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );
\r
1840 SelectObject( dc1, hPieceMask[index] );
\r
1841 SelectObject( dc2, bm2 );
\r
1842 FillRect( dc2, &rc, GetStockObject(WHITE_BRUSH) );
\r
1843 BitBlt( dc2, 0, 0, squareSize, squareSize, dc1, 0, 0, SRCINVERT );
\r
1846 Now dc2 contains the inverse of the piece mask, i.e. a mask that preserves
\r
1847 the piece background and deletes (makes transparent) the rest.
\r
1848 Thanks to that mask, we are free to paint the background with the greates
\r
1849 freedom, as we'll be able to mask off the unwanted parts when finished.
\r
1850 We use this, to make gradients and give the pieces a "roundish" look.
\r
1852 SetPieceBackground( hdc, backColor, 2 );
\r
1853 BitBlt( hdc, 0, 0, squareSize, squareSize, dc2, 0, 0, SRCAND );
\r
1857 DeleteObject( bm2 );
\r
1860 SetTextColor( hdc, foreColor );
\r
1861 TextOut( hdc, pt.x, pt.y, &pieceToFontChar[appData.allWhite && index >= (int)BlackPawn ? index - (int)BlackPawn : index], 1 );
\r
1863 SelectObject( hdc, hbm_old );
\r
1865 if( hPieceFace[index] != NULL ) {
\r
1866 DeleteObject( hPieceFace[index] );
\r
1869 hPieceFace[index] = hbm;
\r
1872 static int TranslatePieceToFontPiece( int piece )
\r
1902 case BlackMarshall:
\r
1906 case BlackNightrider:
\r
1912 case BlackUnicorn:
\r
1916 case BlackGrasshopper:
\r
1928 case BlackCardinal:
\r
1935 case WhiteMarshall:
\r
1939 case WhiteNightrider:
\r
1945 case WhiteUnicorn:
\r
1949 case WhiteGrasshopper:
\r
1961 case WhiteCardinal:
\r
1970 void CreatePiecesFromFont()
\r
1973 HDC hdc_window = NULL;
\r
1979 if( fontBitmapSquareSize < 0 ) {
\r
1980 /* Something went seriously wrong in the past: do not try to recreate fonts! */
\r
1984 if( !appData.useFont || appData.renderPiecesWithFont == NULL ||
\r
1985 appData.renderPiecesWithFont[0] == NULLCHAR || appData.renderPiecesWithFont[0] == '*' ) {
\r
1986 fontBitmapSquareSize = -1;
\r
1990 if( fontBitmapSquareSize != squareSize ) {
\r
1991 hdc_window = GetDC( hwndMain );
\r
1992 hdc = CreateCompatibleDC( hdc_window );
\r
1994 if( hPieceFont != NULL ) {
\r
1995 DeleteObject( hPieceFont );
\r
1998 for( i=0; i<=(int)BlackKing; i++ ) {
\r
1999 hPieceMask[i] = NULL;
\r
2000 hPieceFace[i] = NULL;
\r
2006 if( appData.fontPieceSize >= 50 && appData.fontPieceSize <= 150 ) {
\r
2007 fontHeight = appData.fontPieceSize;
\r
2010 fontHeight = (fontHeight * squareSize) / 100;
\r
2012 lf.lfHeight = -MulDiv( fontHeight, GetDeviceCaps(hdc, LOGPIXELSY), 72 );
\r
2014 lf.lfEscapement = 0;
\r
2015 lf.lfOrientation = 0;
\r
2016 lf.lfWeight = FW_NORMAL;
\r
2018 lf.lfUnderline = 0;
\r
2019 lf.lfStrikeOut = 0;
\r
2020 lf.lfCharSet = DEFAULT_CHARSET;
\r
2021 lf.lfOutPrecision = OUT_DEFAULT_PRECIS;
\r
2022 lf.lfClipPrecision = CLIP_DEFAULT_PRECIS;
\r
2023 lf.lfQuality = PROOF_QUALITY;
\r
2024 lf.lfPitchAndFamily = DEFAULT_PITCH | FF_DONTCARE;
\r
2025 strncpy( lf.lfFaceName, appData.renderPiecesWithFont, sizeof(lf.lfFaceName) );
\r
2026 lf.lfFaceName[ sizeof(lf.lfFaceName) - 1 ] = '\0';
\r
2028 hPieceFont = CreateFontIndirect( &lf );
\r
2030 if( hPieceFont == NULL ) {
\r
2031 fontBitmapSquareSize = -2;
\r
2034 /* Setup font-to-piece character table */
\r
2035 if( ! SetCharTable(pieceToFontChar, appData.fontToPieceTable) ) {
\r
2036 /* No (or wrong) global settings, try to detect the font */
\r
2037 if( strstr(lf.lfFaceName,"Alpha") != NULL ) {
\r
2039 SetCharTable(pieceToFontChar, "phbrqkojntwl");
\r
2041 else if( strstr(lf.lfFaceName,"DiagramTT") != NULL ) {
\r
2042 /* DiagramTT* family */
\r
2043 SetCharTable(pieceToFontChar, "PNLRQKpnlrqk");
\r
2045 else if( strstr(lf.lfFaceName,"WinboardF") != NULL ) {
\r
2046 /* Fairy symbols */
\r
2047 SetCharTable(pieceToFontChar, "PNBRQFEACWMOHIJGDVSLUKpnbrqfeacwmohijgdvsluk");
\r
2049 else if( strstr(lf.lfFaceName,"GC2004D") != NULL ) {
\r
2050 /* Good Companion (Some characters get warped as literal :-( */
\r
2051 char s[] = "1cmWG0??S??oYI23wgQU";
\r
2052 s[0]=0xB9; s[1]=0xA9; s[6]=0xB1; s[11]=0xBB; s[12]=0xAB; s[17]=0xB3;
\r
2053 SetCharTable(pieceToFontChar, s);
\r
2056 /* Cases, Condal, Leipzig, Lucena, Marroquin, Merida, Usual */
\r
2057 SetCharTable(pieceToFontChar, "pnbrqkomvtwl");
\r
2061 /* Create bitmaps */
\r
2062 hfont_old = SelectObject( hdc, hPieceFont );
\r
2063 for(i=(int)WhitePawn; i<(int)EmptySquare; i++) /* [HGM] made a loop for this */
\r
2064 if(PieceToChar((ChessSquare)i) != '.') /* skip unused pieces */
\r
2065 CreatePieceMaskFromFont( hdc_window, hdc, i );
\r
2067 SelectObject( hdc, hfont_old );
\r
2069 fontBitmapSquareSize = squareSize;
\r
2073 if( hdc != NULL ) {
\r
2077 if( hdc_window != NULL ) {
\r
2078 ReleaseDC( hwndMain, hdc_window );
\r
2083 DoLoadBitmap(HINSTANCE hinst, char *piece, int squareSize, char *suffix)
\r
2087 snprintf(name, sizeof(name)/sizeof(name[0]), "%s%d%s", piece, squareSize, suffix);
\r
2088 if (gameInfo.event &&
\r
2089 strcmp(gameInfo.event, "Easter Egg Hunt") == 0 &&
\r
2090 strcmp(name, "k80s") == 0) {
\r
2091 safeStrCpy(name, "tim", sizeof(name)/sizeof(name[0]) );
\r
2093 return LoadBitmap(hinst, name);
\r
2097 /* Insert a color into the program's logical palette
\r
2098 structure. This code assumes the given color is
\r
2099 the result of the RGB or PALETTERGB macro, and it
\r
2100 knows how those macros work (which is documented).
\r
2103 InsertInPalette(COLORREF color)
\r
2105 LPPALETTEENTRY pe = &(pLogPal->palPalEntry[pLogPal->palNumEntries]);
\r
2107 if (pLogPal->palNumEntries++ >= PALETTESIZE) {
\r
2108 DisplayFatalError(_("Too many colors"), 0, 1);
\r
2109 pLogPal->palNumEntries--;
\r
2113 pe->peFlags = (char) 0;
\r
2114 pe->peRed = (char) (0xFF & color);
\r
2115 pe->peGreen = (char) (0xFF & (color >> 8));
\r
2116 pe->peBlue = (char) (0xFF & (color >> 16));
\r
2122 InitDrawingColors()
\r
2124 if (pLogPal == NULL) {
\r
2125 /* Allocate enough memory for a logical palette with
\r
2126 * PALETTESIZE entries and set the size and version fields
\r
2127 * of the logical palette structure.
\r
2129 pLogPal = (NPLOGPALETTE)
\r
2130 LocalAlloc(LMEM_FIXED, (sizeof(LOGPALETTE) +
\r
2131 (sizeof(PALETTEENTRY) * (PALETTESIZE))));
\r
2132 pLogPal->palVersion = 0x300;
\r
2134 pLogPal->palNumEntries = 0;
\r
2136 InsertInPalette(lightSquareColor);
\r
2137 InsertInPalette(darkSquareColor);
\r
2138 InsertInPalette(whitePieceColor);
\r
2139 InsertInPalette(blackPieceColor);
\r
2140 InsertInPalette(highlightSquareColor);
\r
2141 InsertInPalette(premoveHighlightColor);
\r
2143 /* create a logical color palette according the information
\r
2144 * in the LOGPALETTE structure.
\r
2146 hPal = CreatePalette((LPLOGPALETTE) pLogPal);
\r
2148 lightSquareBrush = CreateSolidBrush(lightSquareColor);
\r
2149 blackSquareBrush = CreateSolidBrush(blackPieceColor);
\r
2150 darkSquareBrush = CreateSolidBrush(darkSquareColor);
\r
2151 whitePieceBrush = CreateSolidBrush(whitePieceColor);
\r
2152 blackPieceBrush = CreateSolidBrush(blackPieceColor);
\r
2153 iconBkgndBrush = CreateSolidBrush(GetSysColor(COLOR_BACKGROUND));
\r
2154 explodeBrush = CreateSolidBrush(highlightSquareColor); // [HGM] atomic
\r
2155 markerBrush = CreateSolidBrush(premoveHighlightColor); // [HGM] markers
\r
2156 /* [AS] Force rendering of the font-based pieces */
\r
2157 if( fontBitmapSquareSize > 0 ) {
\r
2158 fontBitmapSquareSize = 0;
\r
2164 BoardWidth(int boardSize, int n)
\r
2165 { /* [HGM] argument n added to allow different width and height */
\r
2166 int lineGap = sizeInfo[boardSize].lineGap;
\r
2168 if( appData.overrideLineGap >= 0 && appData.overrideLineGap <= 5 ) {
\r
2169 lineGap = appData.overrideLineGap;
\r
2172 return (n + 1) * lineGap +
\r
2173 n * sizeInfo[boardSize].squareSize;
\r
2176 /* Respond to board resize by dragging edge */
\r
2178 ResizeBoard(int newSizeX, int newSizeY, int flags)
\r
2180 BoardSize newSize = NUM_SIZES - 1;
\r
2181 static int recurse = 0;
\r
2182 if (IsIconic(hwndMain)) return;
\r
2183 if (recurse > 0) return;
\r
2185 while (newSize > 0) {
\r
2186 InitDrawingSizes(newSize+1000, 0); // [HGM] kludge to update sizeInfo without visible effects
\r
2187 if(newSizeX >= sizeInfo[newSize].cliWidth &&
\r
2188 newSizeY >= sizeInfo[newSize].cliHeight) break;
\r
2191 boardSize = newSize;
\r
2192 InitDrawingSizes(boardSize, flags);
\r
2197 extern Boolean twoBoards, partnerUp; // [HGM] dual
\r
2200 InitDrawingSizes(BoardSize boardSize, int flags)
\r
2202 int i, boardWidth, boardHeight; /* [HGM] height treated separately */
\r
2203 ChessSquare piece;
\r
2204 static int oldBoardSize = -1, oldTinyLayout = 0;
\r
2206 SIZE clockSize, messageSize;
\r
2208 char buf[MSG_SIZ];
\r
2210 HMENU hmenu = GetMenu(hwndMain);
\r
2211 RECT crect, wrect, oldRect;
\r
2213 LOGBRUSH logbrush;
\r
2215 int suppressVisibleEffects = 0; // [HGM] kludge to request updating sizeInfo only
\r
2216 if((int)boardSize >= 1000 ) { boardSize -= 1000; suppressVisibleEffects = 1; }
\r
2218 /* [HGM] call with -2 uses old size (for if nr of files, ranks changes) */
\r
2219 if(boardSize == (BoardSize)(-2) ) boardSize = oldBoardSize;
\r
2221 oldRect.left = wpMain.x; //[HGM] placement: remember previous window params
\r
2222 oldRect.top = wpMain.y;
\r
2223 oldRect.right = wpMain.x + wpMain.width;
\r
2224 oldRect.bottom = wpMain.y + wpMain.height;
\r
2226 tinyLayout = sizeInfo[boardSize].tinyLayout;
\r
2227 smallLayout = sizeInfo[boardSize].smallLayout;
\r
2228 squareSize = sizeInfo[boardSize].squareSize;
\r
2229 lineGap = sizeInfo[boardSize].lineGap;
\r
2230 minorSize = 0; /* [HGM] Kludge to see if demagnified pieces need to be shifted */
\r
2232 if( appData.overrideLineGap >= 0 && appData.overrideLineGap <= 5 ) {
\r
2233 lineGap = appData.overrideLineGap;
\r
2236 if (tinyLayout != oldTinyLayout) {
\r
2237 long style = GetWindowLongPtr(hwndMain, GWL_STYLE);
\r
2239 style &= ~WS_SYSMENU;
\r
2240 InsertMenu(hmenu, IDM_Exit, MF_BYCOMMAND, IDM_Minimize,
\r
2241 "&Minimize\tCtrl+F4");
\r
2243 style |= WS_SYSMENU;
\r
2244 RemoveMenu(hmenu, IDM_Minimize, MF_BYCOMMAND);
\r
2246 SetWindowLongPtr(hwndMain, GWL_STYLE, style);
\r
2248 for (i=0; menuBarText[tinyLayout][i]; i++) {
\r
2249 ModifyMenu(hmenu, i, MF_STRING|MF_BYPOSITION|MF_POPUP,
\r
2250 (UINT)GetSubMenu(hmenu, i), T_(menuBarText[tinyLayout][i]));
\r
2252 DrawMenuBar(hwndMain);
\r
2255 boardWidth = BoardWidth(boardSize, BOARD_WIDTH);
\r
2256 boardHeight = BoardWidth(boardSize, BOARD_HEIGHT);
\r
2258 /* Get text area sizes */
\r
2259 hdc = GetDC(hwndMain);
\r
2260 if (appData.clockMode) {
\r
2261 snprintf(buf, MSG_SIZ, _("White: %s"), TimeString(23*60*60*1000L));
\r
2263 snprintf(buf, MSG_SIZ, _("White"));
\r
2265 oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);
\r
2266 GetTextExtentPoint(hdc, buf, strlen(buf), &clockSize);
\r
2267 SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);
\r
2268 str = _("We only care about the height here");
\r
2269 GetTextExtentPoint(hdc, str, strlen(str), &messageSize);
\r
2270 SelectObject(hdc, oldFont);
\r
2271 ReleaseDC(hwndMain, hdc);
\r
2273 /* Compute where everything goes */
\r
2274 if((first.programLogo || second.programLogo) && !tinyLayout) {
\r
2275 /* [HGM] logo: if either logo is on, reserve space for it */
\r
2276 logoHeight = 2*clockSize.cy;
\r
2277 leftLogoRect.left = OUTER_MARGIN;
\r
2278 leftLogoRect.right = leftLogoRect.left + 4*clockSize.cy;
\r
2279 leftLogoRect.top = OUTER_MARGIN;
\r
2280 leftLogoRect.bottom = OUTER_MARGIN + logoHeight;
\r
2282 rightLogoRect.right = OUTER_MARGIN + boardWidth;
\r
2283 rightLogoRect.left = rightLogoRect.right - 4*clockSize.cy;
\r
2284 rightLogoRect.top = OUTER_MARGIN;
\r
2285 rightLogoRect.bottom = OUTER_MARGIN + logoHeight;
\r
2288 whiteRect.left = leftLogoRect.right;
\r
2289 whiteRect.right = OUTER_MARGIN + boardWidth/2 - INNER_MARGIN/2;
\r
2290 whiteRect.top = OUTER_MARGIN;
\r
2291 whiteRect.bottom = whiteRect.top + logoHeight;
\r
2293 blackRect.right = rightLogoRect.left;
\r
2294 blackRect.left = whiteRect.right + INNER_MARGIN;
\r
2295 blackRect.top = whiteRect.top;
\r
2296 blackRect.bottom = whiteRect.bottom;
\r
2298 whiteRect.left = OUTER_MARGIN;
\r
2299 whiteRect.right = whiteRect.left + boardWidth/2 - INNER_MARGIN/2;
\r
2300 whiteRect.top = OUTER_MARGIN;
\r
2301 whiteRect.bottom = whiteRect.top + clockSize.cy;
\r
2303 blackRect.left = whiteRect.right + INNER_MARGIN;
\r
2304 blackRect.right = blackRect.left + boardWidth/2 - 1;
\r
2305 blackRect.top = whiteRect.top;
\r
2306 blackRect.bottom = whiteRect.bottom;
\r
2308 logoHeight = 0; // [HGM] logo: suppress logo after change to tiny layout!
\r
2311 messageRect.left = OUTER_MARGIN + MESSAGE_LINE_LEFTMARGIN;
\r
2312 if (appData.showButtonBar) {
\r
2313 messageRect.right = OUTER_MARGIN + boardWidth // [HGM] logo: expressed independent of clock placement
\r
2314 - N_BUTTONS*BUTTON_WIDTH - MESSAGE_LINE_LEFTMARGIN;
\r
2316 messageRect.right = OUTER_MARGIN + boardWidth;
\r
2318 messageRect.top = whiteRect.bottom + INNER_MARGIN;
\r
2319 messageRect.bottom = messageRect.top + messageSize.cy;
\r
2321 boardRect.left = OUTER_MARGIN;
\r
2322 boardRect.right = boardRect.left + boardWidth;
\r
2323 boardRect.top = messageRect.bottom + INNER_MARGIN;
\r
2324 boardRect.bottom = boardRect.top + boardHeight;
\r
2326 sizeInfo[boardSize].cliWidth = boardRect.right + OUTER_MARGIN;
\r
2327 sizeInfo[boardSize].cliHeight = boardRect.bottom + OUTER_MARGIN;
\r
2328 oldBoardSize = boardSize;
\r
2329 oldTinyLayout = tinyLayout;
\r
2330 winW = 2 * GetSystemMetrics(SM_CXFRAME) + boardRect.right + OUTER_MARGIN;
\r
2331 winH = 2 * GetSystemMetrics(SM_CYFRAME) + GetSystemMetrics(SM_CYMENU) +
\r
2332 GetSystemMetrics(SM_CYCAPTION) + boardRect.bottom + OUTER_MARGIN;
\r
2333 winW *= 1 + twoBoards;
\r
2334 if(suppressVisibleEffects) return; // [HGM] when called for filling sizeInfo only
\r
2335 wpMain.width = winW; // [HGM] placement: set through temporary which can used by initial sizing choice
\r
2336 wpMain.height = winH; // without disturbing window attachments
\r
2337 GetWindowRect(hwndMain, &wrect);
\r
2338 SetWindowPos(hwndMain, NULL, 0, 0, wpMain.width, wpMain.height,
\r
2339 SWP_NOCOPYBITS|SWP_NOZORDER|SWP_NOMOVE);
\r
2341 // [HGM] placement: let attached windows follow size change.
\r
2342 ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, moveHistoryDialog, &wpMoveHistory );
\r
2343 ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, evalGraphDialog, &wpEvalGraph );
\r
2344 ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, engineOutputDialog, &wpEngineOutput );
\r
2345 ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, gameListDialog, &wpGameList );
\r
2346 ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, hwndConsole, &wpConsole );
\r
2348 /* compensate if menu bar wrapped */
\r
2349 GetClientRect(hwndMain, &crect);
\r
2350 offby = boardRect.bottom + OUTER_MARGIN - crect.bottom;
\r
2351 wpMain.height += offby;
\r
2353 case WMSZ_TOPLEFT:
\r
2354 SetWindowPos(hwndMain, NULL,
\r
2355 wrect.right - wpMain.width, wrect.bottom - wpMain.height,
\r
2356 wpMain.width, wpMain.height, SWP_NOCOPYBITS|SWP_NOZORDER);
\r
2359 case WMSZ_TOPRIGHT:
\r
2361 SetWindowPos(hwndMain, NULL,
\r
2362 wrect.left, wrect.bottom - wpMain.height,
\r
2363 wpMain.width, wpMain.height, SWP_NOCOPYBITS|SWP_NOZORDER);
\r
2366 case WMSZ_BOTTOMLEFT:
\r
2368 SetWindowPos(hwndMain, NULL,
\r
2369 wrect.right - wpMain.width, wrect.top,
\r
2370 wpMain.width, wpMain.height, SWP_NOCOPYBITS|SWP_NOZORDER);
\r
2373 case WMSZ_BOTTOMRIGHT:
\r
2377 SetWindowPos(hwndMain, NULL, 0, 0, wpMain.width, wpMain.height,
\r
2378 SWP_NOCOPYBITS|SWP_NOZORDER|SWP_NOMOVE);
\r
2383 for (i = 0; i < N_BUTTONS; i++) {
\r
2384 if (buttonDesc[i].hwnd != NULL) {
\r
2385 DestroyWindow(buttonDesc[i].hwnd);
\r
2386 buttonDesc[i].hwnd = NULL;
\r
2388 if (appData.showButtonBar) {
\r
2389 buttonDesc[i].hwnd =
\r
2390 CreateWindow("BUTTON", buttonDesc[i].label,
\r
2391 WS_VISIBLE | WS_CHILD | BS_PUSHBUTTON,
\r
2392 boardRect.right - BUTTON_WIDTH*(N_BUTTONS-i),
\r
2393 messageRect.top, BUTTON_WIDTH, messageSize.cy, hwndMain,
\r
2394 (HMENU) buttonDesc[i].id,
\r
2395 (HINSTANCE) GetWindowLongPtr(hwndMain, GWLP_HINSTANCE), NULL);
\r
2397 SendMessage(buttonDesc[i].hwnd, WM_SETFONT,
\r
2398 (WPARAM)font[boardSize][MESSAGE_FONT]->hf,
\r
2399 MAKELPARAM(FALSE, 0));
\r
2401 if (buttonDesc[i].id == IDM_Pause)
\r
2402 hwndPause = buttonDesc[i].hwnd;
\r
2403 buttonDesc[i].wndproc = (WNDPROC)
\r
2404 SetWindowLongPtr(buttonDesc[i].hwnd, GWLP_WNDPROC, (LONG_PTR) ButtonProc);
\r
2407 if (gridPen != NULL) DeleteObject(gridPen);
\r
2408 if (highlightPen != NULL) DeleteObject(highlightPen);
\r
2409 if (premovePen != NULL) DeleteObject(premovePen);
\r
2410 if (lineGap != 0) {
\r
2411 logbrush.lbStyle = BS_SOLID;
\r
2412 logbrush.lbColor = RGB(0, 0, 0); /* grid pen color = black */
\r
2414 ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,
\r
2415 lineGap, &logbrush, 0, NULL);
\r
2416 logbrush.lbColor = highlightSquareColor;
\r
2418 ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,
\r
2419 lineGap, &logbrush, 0, NULL);
\r
2421 logbrush.lbColor = premoveHighlightColor;
\r
2423 ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,
\r
2424 lineGap, &logbrush, 0, NULL);
\r
2426 /* [HGM] Loop had to be split in part for vert. and hor. lines */
\r
2427 for (i = 0; i < BOARD_HEIGHT + 1; i++) {
\r
2428 gridEndpoints[i*2].x = boardRect.left + lineGap / 2;
\r
2429 gridEndpoints[i*2].y = gridEndpoints[i*2 + 1].y =
\r
2430 boardRect.top + lineGap / 2 + (i * (squareSize + lineGap));
\r
2431 gridEndpoints[i*2 + 1].x = boardRect.left + lineGap / 2 +
\r
2432 BOARD_WIDTH * (squareSize + lineGap);
\r
2433 gridVertexCounts[i*2] = gridVertexCounts[i*2 + 1] = 2;
\r
2435 for (i = 0; i < BOARD_WIDTH + 1; i++) {
\r
2436 gridEndpoints[i*2 + BOARD_HEIGHT*2 + 2].y = boardRect.top + lineGap / 2;
\r
2437 gridEndpoints[i*2 + BOARD_HEIGHT*2 + 2].x =
\r
2438 gridEndpoints[i*2 + 1 + BOARD_HEIGHT*2 + 2].x = boardRect.left +
\r
2439 lineGap / 2 + (i * (squareSize + lineGap));
\r
2440 gridEndpoints[i*2 + 1 + BOARD_HEIGHT*2 + 2].y =
\r
2441 boardRect.top + BOARD_HEIGHT * (squareSize + lineGap);
\r
2442 gridVertexCounts[i*2] = gridVertexCounts[i*2 + 1] = 2;
\r
2446 /* [HGM] Licensing requirement */
\r
2448 if(gameInfo.variant == VariantGothic) GothicPopUp( GOTHIC, VariantGothic); else
\r
2451 if(gameInfo.variant == VariantFalcon) GothicPopUp( FALCON, VariantFalcon); else
\r
2453 GothicPopUp( "", VariantNormal);
\r
2456 /* if (boardSize == oldBoardSize) return; [HGM] variant might have changed */
\r
2458 /* Load piece bitmaps for this board size */
\r
2459 for (i=0; i<=2; i++) {
\r
2460 for (piece = WhitePawn;
\r
2461 (int) piece < (int) BlackPawn;
\r
2462 piece = (ChessSquare) ((int) piece + 1)) {
\r
2463 if (pieceBitmap[i][piece] != NULL)
\r
2464 DeleteObject(pieceBitmap[i][piece]);
\r
2468 fontBitmapSquareSize = 0; /* [HGM] render: make sure pieces will be recreated, as we might need others now */
\r
2469 // Orthodox Chess pieces
\r
2470 pieceBitmap[0][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "s");
\r
2471 pieceBitmap[0][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "s");
\r
2472 pieceBitmap[0][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "s");
\r
2473 pieceBitmap[0][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "s");
\r
2474 pieceBitmap[0][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "s");
\r
2475 pieceBitmap[1][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "o");
\r
2476 pieceBitmap[1][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "o");
\r
2477 pieceBitmap[1][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "o");
\r
2478 pieceBitmap[1][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "o");
\r
2479 pieceBitmap[1][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "o");
\r
2480 pieceBitmap[2][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "w");
\r
2481 pieceBitmap[2][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "w");
\r
2482 pieceBitmap[2][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "w");
\r
2483 pieceBitmap[2][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "w");
\r
2484 pieceBitmap[2][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "w");
\r
2485 if( gameInfo.variant == VariantShogi && squareSize <= 72 && squareSize >= 33) {
\r
2486 // in Shogi, Hijack the unused Queen for Lance
\r
2487 pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "s");
\r
2488 pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "o");
\r
2489 pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "w");
\r
2491 pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "s");
\r
2492 pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "o");
\r
2493 pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "w");
\r
2496 if(squareSize <= 72 && squareSize >= 33) {
\r
2497 /* A & C are available in most sizes now */
\r
2498 if(squareSize != 49 && squareSize != 72 && squareSize != 33) { // Vortex-like
\r
2499 pieceBitmap[0][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "s");
\r
2500 pieceBitmap[1][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "o");
\r
2501 pieceBitmap[2][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "w");
\r
2502 pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "s");
\r
2503 pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "o");
\r
2504 pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "w");
\r
2505 pieceBitmap[0][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "s");
\r
2506 pieceBitmap[1][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "o");
\r
2507 pieceBitmap[2][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "w");
\r
2508 pieceBitmap[0][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "s");
\r
2509 pieceBitmap[1][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "o");
\r
2510 pieceBitmap[2][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "w");
\r
2511 } else { // Smirf-like
\r
2512 if(gameInfo.variant == VariantSChess) {
\r
2513 pieceBitmap[0][WhiteAngel] = DoLoadBitmap(hInst, "v", squareSize, "s");
\r
2514 pieceBitmap[1][WhiteAngel] = DoLoadBitmap(hInst, "v", squareSize, "o");
\r
2515 pieceBitmap[2][WhiteAngel] = DoLoadBitmap(hInst, "v", squareSize, "w");
\r
2517 pieceBitmap[0][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "s");
\r
2518 pieceBitmap[1][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "o");
\r
2519 pieceBitmap[2][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "w");
\r
2522 if(gameInfo.variant == VariantGothic) { // Vortex-like
\r
2523 pieceBitmap[0][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "s");
\r
2524 pieceBitmap[1][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "o");
\r
2525 pieceBitmap[2][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "w");
\r
2526 } else if(gameInfo.variant == VariantSChess && (squareSize == 49 || squareSize == 72)) {
\r
2527 pieceBitmap[0][WhiteMarshall] = DoLoadBitmap(hInst, "e", squareSize, "s");
\r
2528 pieceBitmap[1][WhiteMarshall] = DoLoadBitmap(hInst, "e", squareSize, "o");
\r
2529 pieceBitmap[2][WhiteMarshall] = DoLoadBitmap(hInst, "e", squareSize, "w");
\r
2530 } else { // WinBoard standard
\r
2531 pieceBitmap[0][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "s");
\r
2532 pieceBitmap[1][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "o");
\r
2533 pieceBitmap[2][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "w");
\r
2538 if(squareSize==72 || squareSize==49 || squareSize==33) { /* experiment with some home-made bitmaps */
\r
2539 pieceBitmap[0][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "s");
\r
2540 pieceBitmap[1][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "o");
\r
2541 pieceBitmap[2][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "w");
\r
2542 pieceBitmap[0][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "s");
\r
2543 pieceBitmap[1][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "o");
\r
2544 pieceBitmap[2][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "w");
\r
2545 pieceBitmap[0][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "s");
\r
2546 pieceBitmap[1][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "o");
\r
2547 pieceBitmap[2][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "w");
\r
2548 pieceBitmap[0][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "s");
\r
2549 pieceBitmap[1][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "o");
\r
2550 pieceBitmap[2][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "w");
\r
2551 pieceBitmap[0][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "s");
\r
2552 pieceBitmap[1][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "o");
\r
2553 pieceBitmap[2][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "w");
\r
2554 pieceBitmap[0][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "s");
\r
2555 pieceBitmap[1][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "o");
\r
2556 pieceBitmap[2][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "w");
\r
2557 pieceBitmap[0][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "s");
\r
2558 pieceBitmap[1][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "o");
\r
2559 pieceBitmap[2][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "w");
\r
2560 pieceBitmap[0][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "s");
\r
2561 pieceBitmap[1][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "o");
\r
2562 pieceBitmap[2][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "w");
\r
2563 pieceBitmap[0][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "s");
\r
2564 pieceBitmap[1][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "o");
\r
2565 pieceBitmap[2][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "w");
\r
2566 pieceBitmap[0][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "s");
\r
2567 pieceBitmap[1][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "o");
\r
2568 pieceBitmap[2][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "w");
\r
2570 if(gameInfo.variant == VariantShogi) { /* promoted Gold represemtations */
\r
2571 pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "wp", squareSize, "s");
\r
2572 pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "wp", squareSize, "o");
\r
2573 pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "w", squareSize, "w");
\r
2574 pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "wn", squareSize, "s");
\r
2575 pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "wn", squareSize, "o");
\r
2576 pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "w", squareSize, "w");
\r
2577 pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "ws", squareSize, "s");
\r
2578 pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "ws", squareSize, "o");
\r
2579 pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "w", squareSize, "w");
\r
2580 pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "wl", squareSize, "s");
\r
2581 pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "wl", squareSize, "o");
\r
2582 pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "w", squareSize, "w");
\r
2584 pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "s");
\r
2585 pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "o");
\r
2586 pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "w");
\r
2587 pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "s");
\r
2588 pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "o");
\r
2589 pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "w");
\r
2590 pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "s");
\r
2591 pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "o");
\r
2592 pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "w");
\r
2593 pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "s");
\r
2594 pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "o");
\r
2595 pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "w");
\r
2598 } else { /* other size, no special bitmaps available. Use smaller symbols */
\r
2599 if((int)boardSize < 2) minorSize = sizeInfo[0].squareSize;
\r
2600 else minorSize = sizeInfo[(int)boardSize - 2].squareSize;
\r
2601 pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "s");
\r
2602 pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "o");
\r
2603 pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "w");
\r
2604 pieceBitmap[0][WhiteCardinal] = DoLoadBitmap(hInst, "b", minorSize, "s");
\r
2605 pieceBitmap[1][WhiteCardinal] = DoLoadBitmap(hInst, "b", minorSize, "o");
\r
2606 pieceBitmap[2][WhiteCardinal] = DoLoadBitmap(hInst, "b", minorSize, "w");
\r
2607 pieceBitmap[0][WhiteDragon] = DoLoadBitmap(hInst, "r", minorSize, "s");
\r
2608 pieceBitmap[1][WhiteDragon] = DoLoadBitmap(hInst, "r", minorSize, "o");
\r
2609 pieceBitmap[2][WhiteDragon] = DoLoadBitmap(hInst, "r", minorSize, "w");
\r
2610 pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "s");
\r
2611 pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "o");
\r
2612 pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "w");
\r
2616 if(gameInfo.variant == VariantShogi && squareSize == 58)
\r
2617 /* special Shogi support in this size */
\r
2618 { for (i=0; i<=2; i++) { /* replace all bitmaps */
\r
2619 for (piece = WhitePawn;
\r
2620 (int) piece < (int) BlackPawn;
\r
2621 piece = (ChessSquare) ((int) piece + 1)) {
\r
2622 if (pieceBitmap[i][piece] != NULL)
\r
2623 DeleteObject(pieceBitmap[i][piece]);
\r
2626 pieceBitmap[0][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "o");
\r
2627 pieceBitmap[0][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "o");
\r
2628 pieceBitmap[0][WhiteBishop] = DoLoadBitmap(hInst, "sb", squareSize, "o");
\r
2629 pieceBitmap[0][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "o");
\r
2630 pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "o");
\r
2631 pieceBitmap[0][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "o");
\r
2632 pieceBitmap[0][WhiteFerz] = DoLoadBitmap(hInst, "sf", squareSize, "o");
\r
2633 pieceBitmap[0][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "o");
\r
2634 pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "su", squareSize, "o");
\r
2635 pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "sh", squareSize, "o");
\r
2636 pieceBitmap[0][WhiteCardinal] = DoLoadBitmap(hInst, "sa", squareSize, "o");
\r
2637 pieceBitmap[0][WhiteDragon] = DoLoadBitmap(hInst, "sc", squareSize, "o");
\r
2638 pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "sg", squareSize, "o");
\r
2639 pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "ss", squareSize, "o");
\r
2640 pieceBitmap[1][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "o");
\r
2641 pieceBitmap[1][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "o");
\r
2642 pieceBitmap[1][WhiteBishop] = DoLoadBitmap(hInst, "sb", squareSize, "o");
\r
2643 pieceBitmap[1][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "o");
\r
2644 pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "o");
\r
2645 pieceBitmap[1][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "o");
\r
2646 pieceBitmap[1][WhiteFerz] = DoLoadBitmap(hInst, "sf", squareSize, "o");
\r
2647 pieceBitmap[1][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "o");
\r
2648 pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "su", squareSize, "o");
\r
2649 pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "sh", squareSize, "o");
\r
2650 pieceBitmap[1][WhiteCardinal] = DoLoadBitmap(hInst, "sa", squareSize, "o");
\r
2651 pieceBitmap[1][WhiteDragon] = DoLoadBitmap(hInst, "sc", squareSize, "o");
\r
2652 pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "sg", squareSize, "o");
\r
2653 pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "ss", squareSize, "o");
\r
2654 pieceBitmap[2][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "w");
\r
2655 pieceBitmap[2][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "w");
\r
2656 pieceBitmap[2][WhiteBishop] = DoLoadBitmap(hInst, "sr", squareSize, "w");
\r
2657 pieceBitmap[2][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "w");
\r
2658 pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "w");
\r
2659 pieceBitmap[2][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "w");
\r
2660 pieceBitmap[2][WhiteFerz] = DoLoadBitmap(hInst, "sw", squareSize, "w");
\r
2661 pieceBitmap[2][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "w");
\r
2662 pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "sp", squareSize, "w");
\r
2663 pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "sn", squareSize, "w");
\r
2664 pieceBitmap[2][WhiteCardinal] = DoLoadBitmap(hInst, "sr", squareSize, "w");
\r
2665 pieceBitmap[2][WhiteDragon] = DoLoadBitmap(hInst, "sr", squareSize, "w");
\r
2666 pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "sl", squareSize, "w");
\r
2667 pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "sw", squareSize, "w");
\r
2673 PieceBitmap(ChessSquare p, int kind)
\r
2675 if ((int) p >= (int) BlackPawn)
\r
2676 p = (ChessSquare) ((int) p - (int) BlackPawn + (int) WhitePawn);
\r
2678 return pieceBitmap[kind][(int) p];
\r
2681 /***************************************************************/
\r
2683 #define MIN(a,b) (((a) < (b)) ? (a) : (b))
\r
2684 #define MAX(a,b) (((a) > (b)) ? (a) : (b))
\r
2686 #define MIN3(a,b,c) (((a) < (b) && (a) < (c)) ? (a) : (((b) < (a) && (b) < (c)) ? (b) : (c)))
\r
2687 #define MAX3(a,b,c) (((a) > (b) && (a) > (c)) ? (a) : (((b) > (a) && (b) > (c)) ? (b) : (c)))
\r
2691 SquareToPos(int row, int column, int * x, int * y)
\r
2694 *x = boardRect.left + lineGap + ((BOARD_WIDTH-1)-column) * (squareSize + lineGap);
\r
2695 *y = boardRect.top + lineGap + row * (squareSize + lineGap);
\r
2697 *x = boardRect.left + lineGap + column * (squareSize + lineGap);
\r
2698 *y = boardRect.top + lineGap + ((BOARD_HEIGHT-1)-row) * (squareSize + lineGap);
\r
2703 DrawCoordsOnDC(HDC hdc)
\r
2705 static char files[] = "0123456789012345678901221098765432109876543210";
\r
2706 static char ranks[] = "wvutsrqponmlkjihgfedcbaabcdefghijklmnopqrstuvw";
\r
2707 char str[2] = { NULLCHAR, NULLCHAR };
\r
2708 int oldMode, oldAlign, x, y, start, i;
\r
2712 if (!appData.showCoords)
\r
2715 start = flipView ? 1-(ONE!='1') : 45+(ONE!='1')-BOARD_HEIGHT;
\r
2717 oldBrush = SelectObject(hdc, GetStockObject(BLACK_BRUSH));
\r
2718 oldMode = SetBkMode(hdc, (appData.monoMode ? OPAQUE : TRANSPARENT));
\r
2719 oldAlign = GetTextAlign(hdc);
\r
2720 oldFont = SelectObject(hdc, font[boardSize][COORD_FONT]->hf);
\r
2722 y = boardRect.top + lineGap;
\r
2723 x = boardRect.left + lineGap + gameInfo.holdingsWidth*(squareSize + lineGap);
\r
2725 SetTextAlign(hdc, TA_LEFT|TA_TOP);
\r
2726 for (i = 0; i < BOARD_HEIGHT; i++) {
\r
2727 str[0] = files[start + i];
\r
2728 ExtTextOut(hdc, x + 2, y + 1, 0, NULL, str, 1, NULL);
\r
2729 y += squareSize + lineGap;
\r
2732 start = flipView ? 23-(BOARD_RGHT-BOARD_LEFT) : 23;
\r
2734 SetTextAlign(hdc, TA_RIGHT|TA_BOTTOM);
\r
2735 for (i = 0; i < BOARD_RGHT - BOARD_LEFT; i++) {
\r
2736 str[0] = ranks[start + i];
\r
2737 ExtTextOut(hdc, x + squareSize - 2, y - 1, 0, NULL, str, 1, NULL);
\r
2738 x += squareSize + lineGap;
\r
2741 SelectObject(hdc, oldBrush);
\r
2742 SetBkMode(hdc, oldMode);
\r
2743 SetTextAlign(hdc, oldAlign);
\r
2744 SelectObject(hdc, oldFont);
\r
2748 DrawGridOnDC(HDC hdc)
\r
2752 if (lineGap != 0) {
\r
2753 oldPen = SelectObject(hdc, gridPen);
\r
2754 PolyPolyline(hdc, gridEndpoints, gridVertexCounts, BOARD_WIDTH+BOARD_HEIGHT + 2);
\r
2755 SelectObject(hdc, oldPen);
\r
2759 #define HIGHLIGHT_PEN 0
\r
2760 #define PREMOVE_PEN 1
\r
2763 DrawHighlightOnDC(HDC hdc, BOOLEAN on, int x, int y, int pen)
\r
2766 HPEN oldPen, hPen;
\r
2767 if (lineGap == 0) return;
\r
2769 x1 = boardRect.left +
\r
2770 lineGap/2 + ((BOARD_WIDTH-1)-x) * (squareSize + lineGap);
\r
2771 y1 = boardRect.top +
\r
2772 lineGap/2 + y * (squareSize + lineGap);
\r
2774 x1 = boardRect.left +
\r
2775 lineGap/2 + x * (squareSize + lineGap);
\r
2776 y1 = boardRect.top +
\r
2777 lineGap/2 + ((BOARD_HEIGHT-1)-y) * (squareSize + lineGap);
\r
2779 hPen = pen ? premovePen : highlightPen;
\r
2780 oldPen = SelectObject(hdc, on ? hPen : gridPen);
\r
2781 MoveToEx(hdc, x1, y1, NULL);
\r
2782 LineTo(hdc, x1 + squareSize + lineGap, y1);
\r
2783 LineTo(hdc, x1 + squareSize + lineGap, y1 + squareSize + lineGap);
\r
2784 LineTo(hdc, x1, y1 + squareSize + lineGap);
\r
2785 LineTo(hdc, x1, y1);
\r
2786 SelectObject(hdc, oldPen);
\r
2790 DrawHighlightsOnDC(HDC hdc, HighlightInfo *h, int pen)
\r
2793 for (i=0; i<2; i++) {
\r
2794 if (h->sq[i].x >= 0 && h->sq[i].y >= 0)
\r
2795 DrawHighlightOnDC(hdc, TRUE,
\r
2796 h->sq[i].x, h->sq[i].y,
\r
2801 /* Note: sqcolor is used only in monoMode */
\r
2802 /* Note that this code is largely duplicated in woptions.c,
\r
2803 function DrawSampleSquare, so that needs to be updated too */
\r
2805 DrawPieceOnDC(HDC hdc, ChessSquare piece, int color, int sqcolor, int x, int y, HDC tmphdc)
\r
2807 HBITMAP oldBitmap;
\r
2811 if (appData.blindfold) return;
\r
2813 /* [AS] Use font-based pieces if needed */
\r
2814 if( fontBitmapSquareSize >= 0 && (squareSize > 32 || gameInfo.variant >= VariantShogi)) {
\r
2815 /* Create piece bitmaps, or do nothing if piece set is up to date */
\r
2816 CreatePiecesFromFont();
\r
2818 if( fontBitmapSquareSize == squareSize ) {
\r
2819 int index = TranslatePieceToFontPiece(piece);
\r
2821 SelectObject( tmphdc, hPieceMask[ index ] );
\r
2823 if(appData.upsideDown ? color==flipView : (flipView && gameInfo.variant == VariantShogi))
\r
2824 StretchBlt(hdc, x+squareSize, y+squareSize, -squareSize, -squareSize, tmphdc, 0, 0, squareSize, squareSize, SRCAND);
\r
2828 squareSize, squareSize,
\r
2833 SelectObject( tmphdc, hPieceFace[ index ] );
\r
2835 if(appData.upsideDown ? color==flipView : (flipView && gameInfo.variant == VariantShogi))
\r
2836 StretchBlt(hdc, x+squareSize, y+squareSize, -squareSize, -squareSize, tmphdc, 0, 0, squareSize, squareSize, SRCPAINT);
\r
2840 squareSize, squareSize,
\r
2849 if (appData.monoMode) {
\r
2850 SelectObject(tmphdc, PieceBitmap(piece,
\r
2851 color == sqcolor ? OUTLINE_PIECE : SOLID_PIECE));
\r
2852 BitBlt(hdc, x, y, squareSize, squareSize, tmphdc, 0, 0,
\r
2853 sqcolor ? SRCCOPY : NOTSRCCOPY);
\r
2855 tmpSize = squareSize;
\r
2857 ((piece >= (int)WhiteNightrider && piece <= WhiteGrasshopper) ||
\r
2858 (piece >= (int)BlackNightrider && piece <= BlackGrasshopper)) ) {
\r
2859 /* [HGM] no bitmap available for promoted pieces in Crazyhouse */
\r
2860 /* Bitmaps of smaller size are substituted, but we have to align them */
\r
2861 x += (squareSize - minorSize)>>1;
\r
2862 y += squareSize - minorSize - 2;
\r
2863 tmpSize = minorSize;
\r
2865 if (color || appData.allWhite ) {
\r
2866 oldBitmap = SelectObject(tmphdc, PieceBitmap(piece, WHITE_PIECE));
\r
2868 oldBrush = SelectObject(hdc, whitePieceBrush);
\r
2869 else oldBrush = SelectObject(hdc, blackPieceBrush);
\r
2870 if(appData.upsideDown && color==flipView)
\r
2871 StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, 0x00B8074A);
\r
2873 BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, 0x00B8074A);
\r
2874 /* Use black for outline of white pieces */
\r
2875 SelectObject(tmphdc, PieceBitmap(piece, OUTLINE_PIECE));
\r
2876 if(appData.upsideDown && color==flipView)
\r
2877 StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, SRCAND);
\r
2879 BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, SRCAND);
\r
2881 /* Use square color for details of black pieces */
\r
2882 oldBitmap = SelectObject(tmphdc, PieceBitmap(piece, SOLID_PIECE));
\r
2883 oldBrush = SelectObject(hdc, blackPieceBrush);
\r
2884 if(appData.upsideDown && !flipView)
\r
2885 StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, 0x00B8074A);
\r
2887 BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, 0x00B8074A);
\r
2889 SelectObject(hdc, oldBrush);
\r
2890 SelectObject(tmphdc, oldBitmap);
\r
2894 /* [AS] Compute a drawing mode for a square, based on specified settings (see DrawTile) */
\r
2895 int GetBackTextureMode( int algo )
\r
2897 int result = BACK_TEXTURE_MODE_DISABLED;
\r
2901 case BACK_TEXTURE_MODE_PLAIN:
\r
2902 result = 1; /* Always use identity map */
\r
2904 case BACK_TEXTURE_MODE_FULL_RANDOM:
\r
2905 result = 1 + (myrandom() % 3); /* Pick a transformation at random */
\r
2913 [AS] Compute and save texture drawing info, otherwise we may not be able
\r
2914 to handle redraws cleanly (as random numbers would always be different).
\r
2916 VOID RebuildTextureSquareInfo()
\r
2926 ZeroMemory( &backTextureSquareInfo, sizeof(backTextureSquareInfo) );
\r
2928 if( liteBackTexture != NULL ) {
\r
2929 if( GetObject( liteBackTexture, sizeof(bi), &bi ) > 0 ) {
\r
2930 lite_w = bi.bmWidth;
\r
2931 lite_h = bi.bmHeight;
\r
2935 if( darkBackTexture != NULL ) {
\r
2936 if( GetObject( darkBackTexture, sizeof(bi), &bi ) > 0 ) {
\r
2937 dark_w = bi.bmWidth;
\r
2938 dark_h = bi.bmHeight;
\r
2942 for( row=0; row<BOARD_HEIGHT; row++ ) {
\r
2943 for( col=0; col<BOARD_WIDTH; col++ ) {
\r
2944 if( (col + row) & 1 ) {
\r
2946 if( lite_w >= squareSize && lite_h >= squareSize ) {
\r
2947 if( lite_w >= squareSize*BOARD_WIDTH )
\r
2948 backTextureSquareInfo[row][col].x = (2*col+1)*lite_w/(2*BOARD_WIDTH) - squareSize/2; /* [HGM] cut out of center of virtual square */
\r
2950 backTextureSquareInfo[row][col].x = col * (lite_w - squareSize) / (BOARD_WIDTH-1); /* [HGM] divide by size-1 in stead of size! */
\r
2951 if( lite_h >= squareSize*BOARD_HEIGHT )
\r
2952 backTextureSquareInfo[row][col].y = (2*(BOARD_HEIGHT-row)-1)*lite_h/(2*BOARD_HEIGHT) - squareSize/2;
\r
2954 backTextureSquareInfo[row][col].y = (BOARD_HEIGHT-1-row) * (lite_h - squareSize) / (BOARD_HEIGHT-1);
\r
2955 backTextureSquareInfo[row][col].mode = GetBackTextureMode(liteBackTextureMode);
\r
2960 if( dark_w >= squareSize && dark_h >= squareSize ) {
\r
2961 if( dark_w >= squareSize*BOARD_WIDTH )
\r
2962 backTextureSquareInfo[row][col].x = (2*col+1) * dark_w / (2*BOARD_WIDTH) - squareSize/2;
\r
2964 backTextureSquareInfo[row][col].x = col * (dark_w - squareSize) / (BOARD_WIDTH-1);
\r
2965 if( dark_h >= squareSize*BOARD_HEIGHT )
\r
2966 backTextureSquareInfo[row][col].y = (2*(BOARD_HEIGHT-row)-1) * dark_h / (2*BOARD_HEIGHT) - squareSize/2;
\r
2968 backTextureSquareInfo[row][col].y = (BOARD_HEIGHT-1-row) * (dark_h - squareSize) / (BOARD_HEIGHT-1);
\r
2969 backTextureSquareInfo[row][col].mode = GetBackTextureMode(darkBackTextureMode);
\r
2976 /* [AS] Arrow highlighting support */
\r
2978 static double A_WIDTH = 5; /* Width of arrow body */
\r
2980 #define A_HEIGHT_FACTOR 6 /* Length of arrow "point", relative to body width */
\r
2981 #define A_WIDTH_FACTOR 3 /* Width of arrow "point", relative to body width */
\r
2983 static double Sqr( double x )
\r
2988 static int Round( double x )
\r
2990 return (int) (x + 0.5);
\r
2993 /* Draw an arrow between two points using current settings */
\r
2994 VOID DrawArrowBetweenPoints( HDC hdc, int s_x, int s_y, int d_x, int d_y )
\r
2997 double dx, dy, j, k, x, y;
\r
2999 if( d_x == s_x ) {
\r
3000 int h = (d_y > s_y) ? +A_WIDTH*A_HEIGHT_FACTOR : -A_WIDTH*A_HEIGHT_FACTOR;
\r
3002 arrow[0].x = s_x + A_WIDTH + 0.5;
\r
3005 arrow[1].x = s_x + A_WIDTH + 0.5;
\r
3006 arrow[1].y = d_y - h;
\r
3008 arrow[2].x = arrow[1].x + A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;
\r
3009 arrow[2].y = d_y - h;
\r
3014 arrow[5].x = arrow[1].x - 2*A_WIDTH + 0.5;
\r
3015 arrow[5].y = d_y - h;
\r
3017 arrow[4].x = arrow[5].x - A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;
\r
3018 arrow[4].y = d_y - h;
\r
3020 arrow[6].x = arrow[1].x - 2*A_WIDTH + 0.5;
\r
3023 else if( d_y == s_y ) {
\r
3024 int w = (d_x > s_x) ? +A_WIDTH*A_HEIGHT_FACTOR : -A_WIDTH*A_HEIGHT_FACTOR;
\r
3027 arrow[0].y = s_y + A_WIDTH + 0.5;
\r
3029 arrow[1].x = d_x - w;
\r
3030 arrow[1].y = s_y + A_WIDTH + 0.5;
\r
3032 arrow[2].x = d_x - w;
\r
3033 arrow[2].y = arrow[1].y + A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;
\r
3038 arrow[5].x = d_x - w;
\r
3039 arrow[5].y = arrow[1].y - 2*A_WIDTH + 0.5;
\r
3041 arrow[4].x = d_x - w;
\r
3042 arrow[4].y = arrow[5].y - A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;
\r
3045 arrow[6].y = arrow[1].y - 2*A_WIDTH + 0.5;
\r
3048 /* [AS] Needed a lot of paper for this! :-) */
\r
3049 dy = (double) (d_y - s_y) / (double) (d_x - s_x);
\r
3050 dx = (double) (s_x - d_x) / (double) (s_y - d_y);
\r
3052 j = sqrt( Sqr(A_WIDTH) / (1.0 + Sqr(dx)) );
\r
3054 k = sqrt( Sqr(A_WIDTH*A_HEIGHT_FACTOR) / (1.0 + Sqr(dy)) );
\r
3059 arrow[0].x = Round(x - j);
\r
3060 arrow[0].y = Round(y + j*dx);
\r
3062 arrow[1].x = Round(arrow[0].x + 2*j); // [HGM] prevent width to be affected by rounding twice
\r
3063 arrow[1].y = Round(arrow[0].y - 2*j*dx);
\r
3066 x = (double) d_x - k;
\r
3067 y = (double) d_y - k*dy;
\r
3070 x = (double) d_x + k;
\r
3071 y = (double) d_y + k*dy;
\r
3074 x = Round(x); y = Round(y); // [HGM] make sure width of shaft is rounded the same way on both ends
\r
3076 arrow[6].x = Round(x - j);
\r
3077 arrow[6].y = Round(y + j*dx);
\r
3079 arrow[2].x = Round(arrow[6].x + 2*j);
\r
3080 arrow[2].y = Round(arrow[6].y - 2*j*dx);
\r
3082 arrow[3].x = Round(arrow[2].x + j*(A_WIDTH_FACTOR-1));
\r
3083 arrow[3].y = Round(arrow[2].y - j*(A_WIDTH_FACTOR-1)*dx);
\r
3088 arrow[5].x = Round(arrow[6].x - j*(A_WIDTH_FACTOR-1));
\r
3089 arrow[5].y = Round(arrow[6].y + j*(A_WIDTH_FACTOR-1)*dx);
\r
3092 Polygon( hdc, arrow, 7 );
\r
3095 /* [AS] Draw an arrow between two squares */
\r
3096 VOID DrawArrowBetweenSquares( HDC hdc, int s_col, int s_row, int d_col, int d_row )
\r
3098 int s_x, s_y, d_x, d_y;
\r
3105 if( s_col == d_col && s_row == d_row ) {
\r
3109 /* Get source and destination points */
\r
3110 SquareToPos( s_row, s_col, &s_x, &s_y);
\r
3111 SquareToPos( d_row, d_col, &d_x, &d_y);
\r
3114 d_y += squareSize / 2 - squareSize / 4; // [HGM] round towards same centers on all sides!
\r
3116 else if( d_y < s_y ) {
\r
3117 d_y += squareSize / 2 + squareSize / 4;
\r
3120 d_y += squareSize / 2;
\r
3124 d_x += squareSize / 2 - squareSize / 4;
\r
3126 else if( d_x < s_x ) {
\r
3127 d_x += squareSize / 2 + squareSize / 4;
\r
3130 d_x += squareSize / 2;
\r
3133 s_x += squareSize / 2;
\r
3134 s_y += squareSize / 2;
\r
3136 /* Adjust width */
\r
3137 A_WIDTH = squareSize / 14.; //[HGM] make float
\r
3140 stLB.lbStyle = BS_SOLID;
\r
3141 stLB.lbColor = appData.highlightArrowColor;
\r
3144 hpen = CreatePen( PS_SOLID, 2, RGB(0x00,0x00,0x00) );
\r
3145 holdpen = SelectObject( hdc, hpen );
\r
3146 hbrush = CreateBrushIndirect( &stLB );
\r
3147 holdbrush = SelectObject( hdc, hbrush );
\r
3149 DrawArrowBetweenPoints( hdc, s_x, s_y, d_x, d_y );
\r
3151 SelectObject( hdc, holdpen );
\r
3152 SelectObject( hdc, holdbrush );
\r
3153 DeleteObject( hpen );
\r
3154 DeleteObject( hbrush );
\r
3157 BOOL HasHighlightInfo()
\r
3159 BOOL result = FALSE;
\r
3161 if( highlightInfo.sq[0].x >= 0 && highlightInfo.sq[0].y >= 0 &&
\r
3162 highlightInfo.sq[1].x >= 0 && highlightInfo.sq[1].y >= 0 )
\r
3170 BOOL IsDrawArrowEnabled()
\r
3172 BOOL result = FALSE;
\r
3174 if( appData.highlightMoveWithArrow && squareSize >= 32 ) {
\r
3181 VOID DrawArrowHighlight( HDC hdc )
\r
3183 if( IsDrawArrowEnabled() && HasHighlightInfo() ) {
\r
3184 DrawArrowBetweenSquares( hdc,
\r
3185 highlightInfo.sq[0].x, highlightInfo.sq[0].y,
\r
3186 highlightInfo.sq[1].x, highlightInfo.sq[1].y );
\r
3190 HRGN GetArrowHighlightClipRegion( HDC hdc )
\r
3192 HRGN result = NULL;
\r
3194 if( HasHighlightInfo() ) {
\r
3195 int x1, y1, x2, y2;
\r
3196 int sx, sy, dx, dy;
\r
3198 SquareToPos(highlightInfo.sq[0].y, highlightInfo.sq[0].x, &x1, &y1 );
\r
3199 SquareToPos(highlightInfo.sq[1].y, highlightInfo.sq[1].x, &x2, &y2 );
\r
3201 sx = MIN( x1, x2 );
\r
3202 sy = MIN( y1, y2 );
\r
3203 dx = MAX( x1, x2 ) + squareSize;
\r
3204 dy = MAX( y1, y2 ) + squareSize;
\r
3206 result = CreateRectRgn( sx, sy, dx, dy );
\r
3213 Warning: this function modifies the behavior of several other functions.
\r
3215 Basically, Winboard is optimized to avoid drawing the whole board if not strictly
\r
3216 needed. Unfortunately, the decision whether or not to perform a full or partial
\r
3217 repaint is scattered all over the place, which is not good for features such as
\r
3218 "arrow highlighting" that require a full repaint of the board.
\r
3220 So, I've tried to patch the code where I thought it made sense (e.g. after or during
\r
3221 user interaction, when speed is not so important) but especially to avoid errors
\r
3222 in the displayed graphics.
\r
3224 In such patched places, I always try refer to this function so there is a single
\r
3225 place to maintain knowledge.
\r
3227 To restore the original behavior, just return FALSE unconditionally.
\r
3229 BOOL IsFullRepaintPreferrable()
\r
3231 BOOL result = FALSE;
\r
3233 if( (appData.highlightLastMove || appData.highlightDragging) && IsDrawArrowEnabled() ) {
\r
3234 /* Arrow may appear on the board */
\r
3242 This function is called by DrawPosition to know whether a full repaint must
\r
3245 Only DrawPosition may directly call this function, which makes use of
\r
3246 some state information. Other function should call DrawPosition specifying
\r
3247 the repaint flag, and can use IsFullRepaintPreferrable if needed.
\r
3249 BOOL DrawPositionNeedsFullRepaint()
\r
3251 BOOL result = FALSE;
\r
3254 Probably a slightly better policy would be to trigger a full repaint
\r
3255 when animInfo.piece changes state (i.e. empty -> non-empty and viceversa),
\r
3256 but animation is fast enough that it's difficult to notice.
\r
3258 if( animInfo.piece == EmptySquare ) {
\r
3259 if( (appData.highlightLastMove || appData.highlightDragging) && IsDrawArrowEnabled() /*&& HasHighlightInfo()*/ ) {
\r
3268 DrawBoardOnDC(HDC hdc, Board board, HDC tmphdc)
\r
3270 int row, column, x, y, square_color, piece_color;
\r
3271 ChessSquare piece;
\r
3273 HDC texture_hdc = NULL;
\r
3275 /* [AS] Initialize background textures if needed */
\r
3276 if( liteBackTexture != NULL || darkBackTexture != NULL ) {
\r
3277 static int backTextureBoardSize; /* [HGM] boardsize: also new texture if board format changed */
\r
3278 if( backTextureSquareSize != squareSize
\r
3279 || backTextureBoardSize != BOARD_WIDTH+BOARD_FILES*BOARD_HEIGHT) {
\r
3280 backTextureBoardSize = BOARD_WIDTH+BOARD_FILES*BOARD_HEIGHT;
\r
3281 backTextureSquareSize = squareSize;
\r
3282 RebuildTextureSquareInfo();
\r
3285 texture_hdc = CreateCompatibleDC( hdc );
\r
3288 for (row = 0; row < BOARD_HEIGHT; row++) {
\r
3289 for (column = 0; column < BOARD_WIDTH; column++) {
\r
3291 SquareToPos(row, column, &x, &y);
\r
3293 piece = board[row][column];
\r
3295 square_color = ((column + row) % 2) == 1;
\r
3296 if( gameInfo.variant == VariantXiangqi ) {
\r
3297 square_color = !InPalace(row, column);
\r
3298 if(BOARD_HEIGHT&1) { if(row==BOARD_HEIGHT/2) square_color ^= 1; }
\r
3299 else if(row < BOARD_HEIGHT/2) square_color ^= 1;
\r
3301 piece_color = (int) piece < (int) BlackPawn;
\r
3304 /* [HGM] holdings file: light square or black */
\r
3305 if(column == BOARD_LEFT-2) {
\r
3306 if( row > BOARD_HEIGHT - gameInfo.holdingsSize - 1 )
\r
3309 DisplayHoldingsCount(hdc, x, y, 0, 0); /* black out */
\r
3313 if(column == BOARD_RGHT + 1 ) {
\r
3314 if( row < gameInfo.holdingsSize )
\r
3317 DisplayHoldingsCount(hdc, x, y, 0, 0);
\r
3321 if(column == BOARD_LEFT-1 ) /* left align */
\r
3322 DisplayHoldingsCount(hdc, x, y, flipView, (int) board[row][column]);
\r
3323 else if( column == BOARD_RGHT) /* right align */
\r
3324 DisplayHoldingsCount(hdc, x, y, !flipView, (int) board[row][column]);
\r
3326 if (appData.monoMode) {
\r
3327 if (piece == EmptySquare) {
\r
3328 BitBlt(hdc, x, y, squareSize, squareSize, 0, 0, 0,
\r
3329 square_color ? WHITENESS : BLACKNESS);
\r
3331 DrawPieceOnDC(hdc, piece, piece_color, square_color, x, y, tmphdc);
\r
3334 else if( appData.useBitmaps && backTextureSquareInfo[row][column].mode > 0 ) {
\r
3335 /* [AS] Draw the square using a texture bitmap */
\r
3336 HBITMAP hbm = SelectObject( texture_hdc, square_color ? liteBackTexture : darkBackTexture );
\r
3337 int r = row, c = column; // [HGM] do not flip board in flipView
\r
3338 if(flipView) { r = BOARD_HEIGHT-1 - r; c = BOARD_WIDTH-1 - c; }
\r
3341 squareSize, squareSize,
\r
3344 backTextureSquareInfo[r][c].mode,
\r
3345 backTextureSquareInfo[r][c].x,
\r
3346 backTextureSquareInfo[r][c].y );
\r
3348 SelectObject( texture_hdc, hbm );
\r
3350 if (piece != EmptySquare) {
\r
3351 DrawPieceOnDC(hdc, piece, piece_color, -1, x, y, tmphdc);
\r
3355 HBRUSH brush = square_color ? lightSquareBrush : darkSquareBrush;
\r
3357 oldBrush = SelectObject(hdc, brush );
\r
3358 BitBlt(hdc, x, y, squareSize, squareSize, 0, 0, 0, PATCOPY);
\r
3359 SelectObject(hdc, oldBrush);
\r
3360 if (piece != EmptySquare)
\r
3361 DrawPieceOnDC(hdc, piece, piece_color, -1, x, y, tmphdc);
\r
3366 if( texture_hdc != NULL ) {
\r
3367 DeleteDC( texture_hdc );
\r
3371 int saveDiagFlag = 0; FILE *diagFile; // [HGM] diag
\r
3372 void fputDW(FILE *f, int x)
\r
3374 fputc(x & 255, f);
\r
3375 fputc(x>>8 & 255, f);
\r
3376 fputc(x>>16 & 255, f);
\r
3377 fputc(x>>24 & 255, f);
\r
3380 #define MAX_CLIPS 200 /* more than enough */
\r
3383 DrawLogoOnDC(HDC hdc, RECT logoRect, HBITMAP logo)
\r
3385 // HBITMAP bufferBitmap;
\r
3390 int w = 100, h = 50;
\r
3392 if(logo == NULL) {
\r
3393 if(!logoHeight) return;
\r
3394 FillRect( hdc, &logoRect, whitePieceBrush );
\r
3396 // GetClientRect(hwndMain, &Rect);
\r
3397 // bufferBitmap = CreateCompatibleBitmap(hdc, Rect.right-Rect.left+1,
\r
3398 // Rect.bottom-Rect.top+1);
\r
3399 tmphdc = CreateCompatibleDC(hdc);
\r
3400 hbm = SelectObject(tmphdc, logo);
\r
3401 if( GetObject( logo, sizeof(bi), &bi ) > 0 ) {
\r
3405 StretchBlt(hdc, logoRect.left, logoRect.top, logoRect.right - logoRect.left,
\r
3406 logoRect.bottom - logoRect.top, tmphdc, 0, 0, w, h, SRCCOPY);
\r
3407 SelectObject(tmphdc, hbm);
\r
3415 HDC hdc = GetDC(hwndMain);
\r
3416 HBITMAP whiteLogo = (HBITMAP) first.programLogo, blackLogo = (HBITMAP) second.programLogo;
\r
3417 if(appData.autoLogo) {
\r
3419 switch(gameMode) { // pick logos based on game mode
\r
3420 case IcsObserving:
\r
3421 whiteLogo = second.programLogo; // ICS logo
\r
3422 blackLogo = second.programLogo;
\r
3425 case IcsPlayingWhite:
\r
3426 if(!appData.zippyPlay) whiteLogo = userLogo;
\r
3427 blackLogo = second.programLogo; // ICS logo
\r
3429 case IcsPlayingBlack:
\r
3430 whiteLogo = second.programLogo; // ICS logo
\r
3431 blackLogo = appData.zippyPlay ? first.programLogo : userLogo;
\r
3433 case TwoMachinesPlay:
\r
3434 if(first.twoMachinesColor[0] == 'b') {
\r
3435 whiteLogo = second.programLogo;
\r
3436 blackLogo = first.programLogo;
\r
3439 case MachinePlaysWhite:
\r
3440 blackLogo = userLogo;
\r
3442 case MachinePlaysBlack:
\r
3443 whiteLogo = userLogo;
\r
3444 blackLogo = first.programLogo;
\r
3447 DrawLogoOnDC(hdc, leftLogoRect, flipClock ? blackLogo : whiteLogo);
\r
3448 DrawLogoOnDC(hdc, rightLogoRect, flipClock ? whiteLogo : blackLogo);
\r
3449 ReleaseDC(hwndMain, hdc);
\r
3454 UpdateLogos(int display)
\r
3455 { // called after loading new engine(s), in tourney or from menu
\r
3456 LoadLogo(&first, 0, FALSE);
\r
3457 LoadLogo(&second, 1, appData.icsActive);
\r
3458 InitDrawingSizes(-2, 0); // adapt layout of board window to presence/absence of logos
\r
3459 if(display) DisplayLogos();
\r
3462 static HDC hdcSeek;
\r
3464 // [HGM] seekgraph
\r
3465 void DrawSeekAxis( int x, int y, int xTo, int yTo )
\r
3468 HPEN hp = SelectObject( hdcSeek, gridPen );
\r
3469 MoveToEx( hdcSeek, boardRect.left+x, boardRect.top+y, &stPt );
\r
3470 LineTo( hdcSeek, boardRect.left+xTo, boardRect.top+yTo );
\r
3471 SelectObject( hdcSeek, hp );
\r
3474 // front-end wrapper for drawing functions to do rectangles
\r
3475 void DrawSeekBackground( int left, int top, int right, int bottom )
\r
3480 if (hdcSeek == NULL) {
\r
3481 hdcSeek = GetDC(hwndMain);
\r
3482 if (!appData.monoMode) {
\r
3483 SelectPalette(hdcSeek, hPal, FALSE);
\r
3484 RealizePalette(hdcSeek);
\r
3487 hp = SelectObject( hdcSeek, gridPen );
\r
3488 rc.top = boardRect.top+top; rc.left = boardRect.left+left;
\r
3489 rc.bottom = boardRect.top+bottom; rc.right = boardRect.left+right;
\r
3490 FillRect( hdcSeek, &rc, lightSquareBrush );
\r
3491 SelectObject( hdcSeek, hp );
\r
3494 // front-end wrapper for putting text in graph
\r
3495 void DrawSeekText(char *buf, int x, int y)
\r
3498 SetBkMode( hdcSeek, TRANSPARENT );
\r
3499 GetTextExtentPoint32( hdcSeek, buf, strlen(buf), &stSize );
\r
3500 TextOut( hdcSeek, boardRect.left+x-3, boardRect.top+y-stSize.cy/2, buf, strlen(buf) );
\r
3503 void DrawSeekDot(int x, int y, int color)
\r
3505 int square = color & 0x80;
\r
3506 HBRUSH oldBrush = SelectObject(hdcSeek,
\r
3507 color == 0 ? markerBrush : color == 1 ? darkSquareBrush : explodeBrush);
\r
3510 Rectangle(hdcSeek, boardRect.left+x - squareSize/9, boardRect.top+y - squareSize/9,
\r
3511 boardRect.left+x + squareSize/9, boardRect.top+y + squareSize/9);
\r
3513 Ellipse(hdcSeek, boardRect.left+x - squareSize/8, boardRect.top+y - squareSize/8,
\r
3514 boardRect.left+x + squareSize/8, boardRect.top+y + squareSize/8);
\r
3515 SelectObject(hdcSeek, oldBrush);
\r
3519 HDCDrawPosition(HDC hdc, BOOLEAN repaint, Board board)
\r
3521 static Board lastReq[2], lastDrawn[2];
\r
3522 static HighlightInfo lastDrawnHighlight, lastDrawnPremove;
\r
3523 static int lastDrawnFlipView = 0;
\r
3524 static int lastReqValid[2] = {0, 0}, lastDrawnValid[2] = {0, 0};
\r
3525 int releaseDC, x, y, x2, y2, row, column, num_clips = 0, i;
\r
3528 HBITMAP bufferBitmap;
\r
3529 HBITMAP oldBitmap;
\r
3531 HRGN clips[MAX_CLIPS];
\r
3532 ChessSquare dragged_piece = EmptySquare;
\r
3533 int nr = twoBoards*partnerUp;
\r
3535 /* I'm undecided on this - this function figures out whether a full
\r
3536 * repaint is necessary on its own, so there's no real reason to have the
\r
3537 * caller tell it that. I think this can safely be set to FALSE - but
\r
3538 * if we trust the callers not to request full repaints unnessesarily, then
\r
3539 * we could skip some clipping work. In other words, only request a full
\r
3540 * redraw when the majority of pieces have changed positions (ie. flip,
\r
3541 * gamestart and similar) --Hawk
\r
3543 Boolean fullrepaint = repaint;
\r
3545 if(DrawSeekGraph()) return; // [HG} seekgraph: suppress printing board if seek graph up
\r
3547 if( DrawPositionNeedsFullRepaint() ) {
\r
3548 fullrepaint = TRUE;
\r
3551 if (board == NULL) {
\r
3552 if (!lastReqValid[nr]) {
\r
3555 board = lastReq[nr];
\r
3557 CopyBoard(lastReq[nr], board);
\r
3558 lastReqValid[nr] = 1;
\r
3561 if (doingSizing) {
\r
3565 if (IsIconic(hwndMain)) {
\r
3569 if (hdc == NULL) {
\r
3570 hdc = GetDC(hwndMain);
\r
3571 if (!appData.monoMode) {
\r
3572 SelectPalette(hdc, hPal, FALSE);
\r
3573 RealizePalette(hdc);
\r
3577 releaseDC = FALSE;
\r
3580 /* Create some work-DCs */
\r
3581 hdcmem = CreateCompatibleDC(hdc);
\r
3582 tmphdc = CreateCompatibleDC(hdc);
\r
3584 /* If dragging is in progress, we temporarely remove the piece */
\r
3585 /* [HGM] or temporarily decrease count if stacked */
\r
3586 /* !! Moved to before board compare !! */
\r
3587 if (dragInfo.from.x >= 0 && dragInfo.pos.x >= 0) {
\r
3588 dragged_piece = board[dragInfo.from.y][dragInfo.from.x];
\r
3589 if(dragInfo.from.x == BOARD_LEFT-2 ) {
\r
3590 if(--board[dragInfo.from.y][dragInfo.from.x+1] == 0 )
\r
3591 board[dragInfo.from.y][dragInfo.from.x] = EmptySquare;
\r
3593 if(dragInfo.from.x == BOARD_RGHT+1) {
\r
3594 if(--board[dragInfo.from.y][dragInfo.from.x-1] == 0 )
\r
3595 board[dragInfo.from.y][dragInfo.from.x] = EmptySquare;
\r
3597 board[dragInfo.from.y][dragInfo.from.x] = gatingPiece;
\r
3600 /* Figure out which squares need updating by comparing the
\r
3601 * newest board with the last drawn board and checking if
\r
3602 * flipping has changed.
\r
3604 if (!fullrepaint && lastDrawnValid[nr] && (nr == 1 || lastDrawnFlipView == flipView)) {
\r
3605 for (row = 0; row < BOARD_HEIGHT; row++) { /* [HGM] true size, not 8 */
\r
3606 for (column = 0; column < BOARD_WIDTH; column++) {
\r
3607 if (lastDrawn[nr][row][column] != board[row][column]) {
\r
3608 SquareToPos(row, column, &x, &y);
\r
3609 clips[num_clips++] =
\r
3610 CreateRectRgn(x, y, x + squareSize, y + squareSize);
\r
3614 if(nr == 0) { // [HGM] dual: no highlights on second board
\r
3615 for (i=0; i<2; i++) {
\r
3616 if (lastDrawnHighlight.sq[i].x != highlightInfo.sq[i].x ||
\r
3617 lastDrawnHighlight.sq[i].y != highlightInfo.sq[i].y) {
\r
3618 if (lastDrawnHighlight.sq[i].x >= 0 &&
\r
3619 lastDrawnHighlight.sq[i].y >= 0) {
\r
3620 SquareToPos(lastDrawnHighlight.sq[i].y,
\r
3621 lastDrawnHighlight.sq[i].x, &x, &y);
\r
3622 clips[num_clips++] =
\r
3623 CreateRectRgn(x - lineGap, y - lineGap,
\r
3624 x + squareSize + lineGap, y + squareSize + lineGap);
\r
3626 if (highlightInfo.sq[i].x >= 0 && highlightInfo.sq[i].y >= 0) {
\r
3627 SquareToPos(highlightInfo.sq[i].y, highlightInfo.sq[i].x, &x, &y);
\r
3628 clips[num_clips++] =
\r
3629 CreateRectRgn(x - lineGap, y - lineGap,
\r
3630 x + squareSize + lineGap, y + squareSize + lineGap);
\r
3634 for (i=0; i<2; i++) {
\r
3635 if (lastDrawnPremove.sq[i].x != premoveHighlightInfo.sq[i].x ||
\r
3636 lastDrawnPremove.sq[i].y != premoveHighlightInfo.sq[i].y) {
\r
3637 if (lastDrawnPremove.sq[i].x >= 0 &&
\r
3638 lastDrawnPremove.sq[i].y >= 0) {
\r
3639 SquareToPos(lastDrawnPremove.sq[i].y,
\r
3640 lastDrawnPremove.sq[i].x, &x, &y);
\r
3641 clips[num_clips++] =
\r
3642 CreateRectRgn(x - lineGap, y - lineGap,
\r
3643 x + squareSize + lineGap, y + squareSize + lineGap);
\r
3645 if (premoveHighlightInfo.sq[i].x >= 0 &&
\r
3646 premoveHighlightInfo.sq[i].y >= 0) {
\r
3647 SquareToPos(premoveHighlightInfo.sq[i].y,
\r
3648 premoveHighlightInfo.sq[i].x, &x, &y);
\r
3649 clips[num_clips++] =
\r
3650 CreateRectRgn(x - lineGap, y - lineGap,
\r
3651 x + squareSize + lineGap, y + squareSize + lineGap);
\r
3655 } else { // nr == 1
\r
3656 partnerHighlightInfo.sq[0].y = board[EP_STATUS-4];
\r
3657 partnerHighlightInfo.sq[0].x = board[EP_STATUS-3];
\r
3658 partnerHighlightInfo.sq[1].y = board[EP_STATUS-2];
\r
3659 partnerHighlightInfo.sq[1].x = board[EP_STATUS-1];
\r
3660 for (i=0; i<2; i++) {
\r
3661 if (partnerHighlightInfo.sq[i].x >= 0 &&
\r
3662 partnerHighlightInfo.sq[i].y >= 0) {
\r
3663 SquareToPos(partnerHighlightInfo.sq[i].y,
\r
3664 partnerHighlightInfo.sq[i].x, &x, &y);
\r
3665 clips[num_clips++] =
\r
3666 CreateRectRgn(x - lineGap, y - lineGap,
\r
3667 x + squareSize + lineGap, y + squareSize + lineGap);
\r
3669 if (oldPartnerHighlight.sq[i].x >= 0 &&
\r
3670 oldPartnerHighlight.sq[i].y >= 0) {
\r
3671 SquareToPos(oldPartnerHighlight.sq[i].y,
\r
3672 oldPartnerHighlight.sq[i].x, &x, &y);
\r
3673 clips[num_clips++] =
\r
3674 CreateRectRgn(x - lineGap, y - lineGap,
\r
3675 x + squareSize + lineGap, y + squareSize + lineGap);
\r
3680 fullrepaint = TRUE;
\r
3683 /* Create a buffer bitmap - this is the actual bitmap
\r
3684 * being written to. When all the work is done, we can
\r
3685 * copy it to the real DC (the screen). This avoids
\r
3686 * the problems with flickering.
\r
3688 GetClientRect(hwndMain, &Rect);
\r
3689 bufferBitmap = CreateCompatibleBitmap(hdc, Rect.right-Rect.left+1,
\r
3690 Rect.bottom-Rect.top+1);
\r
3691 oldBitmap = SelectObject(hdcmem, bufferBitmap);
\r
3692 if (!appData.monoMode) {
\r
3693 SelectPalette(hdcmem, hPal, FALSE);
\r
3696 /* Create clips for dragging */
\r
3697 if (!fullrepaint) {
\r
3698 if (dragInfo.from.x >= 0) {
\r
3699 SquareToPos(dragInfo.from.y, dragInfo.from.x, &x, &y);
\r
3700 clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);
\r
3702 if (dragInfo.start.x >= 0) {
\r
3703 SquareToPos(dragInfo.start.y, dragInfo.start.x, &x, &y);
\r
3704 clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);
\r
3706 if (dragInfo.pos.x >= 0) {
\r
3707 x = dragInfo.pos.x - squareSize / 2;
\r
3708 y = dragInfo.pos.y - squareSize / 2;
\r
3709 clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);
\r
3711 if (dragInfo.lastpos.x >= 0) {
\r
3712 x = dragInfo.lastpos.x - squareSize / 2;
\r
3713 y = dragInfo.lastpos.y - squareSize / 2;
\r
3714 clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);
\r
3718 /* Are we animating a move?
\r
3720 * - remove the piece from the board (temporarely)
\r
3721 * - calculate the clipping region
\r
3723 if (!fullrepaint) {
\r
3724 if (animInfo.piece != EmptySquare) {
\r
3725 board[animInfo.from.y][animInfo.from.x] = EmptySquare;
\r
3726 x = boardRect.left + animInfo.lastpos.x;
\r
3727 y = boardRect.top + animInfo.lastpos.y;
\r
3728 x2 = boardRect.left + animInfo.pos.x;
\r
3729 y2 = boardRect.top + animInfo.pos.y;
\r
3730 clips[num_clips++] = CreateRectRgn(MIN(x,x2), MIN(y,y2), MAX(x,x2)+squareSize, MAX(y,y2)+squareSize);
\r
3731 /* Slight kludge. The real problem is that after AnimateMove is
\r
3732 done, the position on the screen does not match lastDrawn.
\r
3733 This currently causes trouble only on e.p. captures in
\r
3734 atomic, where the piece moves to an empty square and then
\r
3735 explodes. The old and new positions both had an empty square
\r
3736 at the destination, but animation has drawn a piece there and
\r
3737 we have to remember to erase it. [HGM] moved until after setting lastDrawn */
\r
3738 lastDrawn[0][animInfo.to.y][animInfo.to.x] = animInfo.piece;
\r
3742 /* No clips? Make sure we have fullrepaint set to TRUE */
\r
3743 if (num_clips == 0)
\r
3744 fullrepaint = TRUE;
\r
3746 /* Set clipping on the memory DC */
\r
3747 if (!fullrepaint) {
\r
3748 SelectClipRgn(hdcmem, clips[0]);
\r
3749 for (x = 1; x < num_clips; x++) {
\r
3750 if (ExtSelectClipRgn(hdcmem, clips[x], RGN_OR) == ERROR)
\r
3751 abort(); // this should never ever happen!
\r
3755 /* Do all the drawing to the memory DC */
\r
3756 if(explodeInfo.radius) { // [HGM] atomic
\r
3758 int x, y, r=(explodeInfo.radius * squareSize)/100;
\r
3759 ChessSquare piece = board[explodeInfo.fromY][explodeInfo.fromX];
\r
3760 board[explodeInfo.fromY][explodeInfo.fromX] = EmptySquare; // suppress display of capturer
\r
3761 SquareToPos(explodeInfo.toY, explodeInfo.toX, &x, &y);
\r
3762 x += squareSize/2;
\r
3763 y += squareSize/2;
\r
3764 if(!fullrepaint) {
\r
3765 clips[num_clips] = CreateRectRgn(x-r, y-r, x+r, y+r);
\r
3766 ExtSelectClipRgn(hdcmem, clips[num_clips++], RGN_OR);
\r
3768 DrawGridOnDC(hdcmem);
\r
3769 DrawHighlightsOnDC(hdcmem, &highlightInfo, HIGHLIGHT_PEN);
\r
3770 DrawHighlightsOnDC(hdcmem, &premoveHighlightInfo, PREMOVE_PEN);
\r
3771 DrawBoardOnDC(hdcmem, board, tmphdc);
\r
3772 board[explodeInfo.fromY][explodeInfo.fromX] = piece;
\r
3773 oldBrush = SelectObject(hdcmem, explodeBrush);
\r
3774 Ellipse(hdcmem, x-r, y-r, x+r, y+r);
\r
3775 SelectObject(hdcmem, oldBrush);
\r
3777 DrawGridOnDC(hdcmem);
\r
3778 if(nr == 0) { // [HGM] dual: decide which highlights to draw
\r
3779 DrawHighlightsOnDC(hdcmem, &highlightInfo, HIGHLIGHT_PEN);
\r
3780 DrawHighlightsOnDC(hdcmem, &premoveHighlightInfo, PREMOVE_PEN);
\r
3782 DrawHighlightsOnDC(hdcmem, &partnerHighlightInfo, HIGHLIGHT_PEN);
\r
3783 oldPartnerHighlight = partnerHighlightInfo;
\r
3785 DrawBoardOnDC(hdcmem, board, tmphdc);
\r
3787 if(nr == 0) // [HGM] dual: markers only on left board
\r
3788 for (row = 0; row < BOARD_HEIGHT; row++) {
\r
3789 for (column = 0; column < BOARD_WIDTH; column++) {
\r
3790 if (marker[row][column]) { // marker changes only occur with full repaint!
\r
3791 HBRUSH oldBrush = SelectObject(hdcmem,
\r
3792 marker[row][column] == 2 ? markerBrush : explodeBrush);
\r
3793 SquareToPos(row, column, &x, &y);
\r
3794 Ellipse(hdcmem, x + squareSize/4, y + squareSize/4,
\r
3795 x + 3*squareSize/4, y + 3*squareSize/4);
\r
3796 SelectObject(hdcmem, oldBrush);
\r
3801 if( appData.highlightMoveWithArrow ) {
\r
3802 DrawArrowHighlight(hdcmem);
\r
3805 DrawCoordsOnDC(hdcmem);
\r
3807 CopyBoard(lastDrawn[nr], board); /* [HGM] Moved to here from end of routine, */
\r
3808 /* to make sure lastDrawn contains what is actually drawn */
\r
3810 /* Put the dragged piece back into place and draw it (out of place!) */
\r
3811 if (dragged_piece != EmptySquare) {
\r
3812 /* [HGM] or restack */
\r
3813 if(dragInfo.from.x == BOARD_LEFT-2 )
\r
3814 board[dragInfo.from.y][dragInfo.from.x+1]++;
\r
3816 if(dragInfo.from.x == BOARD_RGHT+1 )
\r
3817 board[dragInfo.from.y][dragInfo.from.x-1]++;
\r
3818 board[dragInfo.from.y][dragInfo.from.x] = dragged_piece;
\r
3819 x = dragInfo.pos.x - squareSize / 2;
\r
3820 y = dragInfo.pos.y - squareSize / 2;
\r
3821 DrawPieceOnDC(hdcmem, dragInfo.piece,
\r
3822 ((int) dragInfo.piece < (int) BlackPawn),
\r
3823 (dragInfo.from.y + dragInfo.from.x) % 2, x, y, tmphdc);
\r
3826 /* Put the animated piece back into place and draw it */
\r
3827 if (animInfo.piece != EmptySquare) {
\r
3828 board[animInfo.from.y][animInfo.from.x] = animInfo.piece;
\r
3829 x = boardRect.left + animInfo.pos.x;
\r
3830 y = boardRect.top + animInfo.pos.y;
\r
3831 DrawPieceOnDC(hdcmem, animInfo.piece,
\r
3832 ((int) animInfo.piece < (int) BlackPawn),
\r
3833 (animInfo.from.y + animInfo.from.x) % 2, x, y, tmphdc);
\r
3836 /* Release the bufferBitmap by selecting in the old bitmap
\r
3837 * and delete the memory DC
\r
3839 SelectObject(hdcmem, oldBitmap);
\r
3842 /* Set clipping on the target DC */
\r
3843 if (!fullrepaint) {
\r
3844 if(nr == 1) for (x = 0; x < num_clips; x++) { // [HGM] dual: translate clips
\r
3846 GetRgnBox(clips[x], &rect);
\r
3847 DeleteObject(clips[x]);
\r
3848 clips[x] = CreateRectRgn(rect.left + wpMain.width/2, rect.top,
\r
3849 rect.right + wpMain.width/2, rect.bottom);
\r
3851 SelectClipRgn(hdc, clips[0]);
\r
3852 for (x = 1; x < num_clips; x++) {
\r
3853 if (ExtSelectClipRgn(hdc, clips[x], RGN_OR) == ERROR)
\r
3854 abort(); // this should never ever happen!
\r
3858 /* Copy the new bitmap onto the screen in one go.
\r
3859 * This way we avoid any flickering
\r
3861 oldBitmap = SelectObject(tmphdc, bufferBitmap);
\r
3862 BitBlt(hdc, boardRect.left + twoBoards*partnerUp*wpMain.width/2, boardRect.top, // [HGM] dual
\r
3863 boardRect.right - boardRect.left,
\r
3864 boardRect.bottom - boardRect.top,
\r
3865 tmphdc, boardRect.left, boardRect.top, SRCCOPY);
\r
3866 if(saveDiagFlag) {
\r
3867 BITMAP b; int i, j=0, m, w, wb, fac=0; char *pData;
\r
3868 BITMAPINFOHEADER bih; int color[16], nrColors=0;
\r
3870 GetObject(bufferBitmap, sizeof(b), &b);
\r
3871 if(pData = malloc(b.bmWidthBytes*b.bmHeight + 10000)) {
\r
3872 bih.biSize = sizeof(BITMAPINFOHEADER);
\r
3873 bih.biWidth = b.bmWidth;
\r
3874 bih.biHeight = b.bmHeight;
\r
3876 bih.biBitCount = b.bmBitsPixel;
\r
3877 bih.biCompression = 0;
\r
3878 bih.biSizeImage = b.bmWidthBytes*b.bmHeight;
\r
3879 bih.biXPelsPerMeter = 0;
\r
3880 bih.biYPelsPerMeter = 0;
\r
3881 bih.biClrUsed = 0;
\r
3882 bih.biClrImportant = 0;
\r
3883 // fprintf(diagFile, "t=%d\nw=%d\nh=%d\nB=%d\nP=%d\nX=%d\n",
\r
3884 // b.bmType, b.bmWidth, b.bmHeight, b.bmWidthBytes, b.bmPlanes, b.bmBitsPixel);
\r
3885 GetDIBits(tmphdc,bufferBitmap,0,b.bmHeight,pData,(BITMAPINFO*)&bih,DIB_RGB_COLORS);
\r
3886 // fprintf(diagFile, "%8x\n", (int) pData);
\r
3888 wb = b.bmWidthBytes;
\r
3890 for(i=0; i<wb*(b.bmHeight - boardRect.top + OUTER_MARGIN)>>2; i++) {
\r
3891 int k = ((int*) pData)[i];
\r
3892 for(j=0; j<nrColors; j++) if(color[j] == k) break;
\r
3893 if(j >= 16) break;
\r
3895 if(j >= nrColors) nrColors = j+1;
\r
3897 if(j<16) { // 16 colors is enough. Compress to 4 bits per pixel
\r
3899 for(i=0; i<b.bmHeight - boardRect.top + OUTER_MARGIN; i++) {
\r
3900 for(w=0; w<(wb>>2); w+=2) {
\r
3901 int k = ((int*) pData)[(wb*i>>2) + w];
\r
3902 for(j=0; j<nrColors; j++) if(color[j] == k) break;
\r
3903 k = ((int*) pData)[(wb*i>>2) + w + 1];
\r
3904 for(m=0; m<nrColors; m++) if(color[m] == k) break;
\r
3905 pData[p++] = m | j<<4;
\r
3907 while(p&3) pData[p++] = 0;
\r
3910 wb = ((wb+31)>>5)<<2;
\r
3912 // write BITMAPFILEHEADER
\r
3913 fprintf(diagFile, "BM");
\r
3914 fputDW(diagFile, wb*(b.bmHeight - boardRect.top + OUTER_MARGIN)+0x36 + (fac?64:0));
\r
3915 fputDW(diagFile, 0);
\r
3916 fputDW(diagFile, 0x36 + (fac?64:0));
\r
3917 // write BITMAPINFOHEADER
\r
3918 fputDW(diagFile, 40);
\r
3919 fputDW(diagFile, b.bmWidth);
\r
3920 fputDW(diagFile, b.bmHeight - boardRect.top + OUTER_MARGIN);
\r
3921 if(fac) fputDW(diagFile, 0x040001); // planes and bits/pixel
\r
3922 else fputDW(diagFile, 0x200001); // planes and bits/pixel
\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 fputDW(diagFile, 0);
\r
3929 // write color table
\r
3931 for(i=0; i<16; i++) fputDW(diagFile, color[i]);
\r
3932 // write bitmap data
\r
3933 for(i=0; i<wb*(b.bmHeight - boardRect.top + OUTER_MARGIN); i++)
\r
3934 fputc(pData[i], diagFile);
\r
3939 SelectObject(tmphdc, oldBitmap);
\r
3941 /* Massive cleanup */
\r
3942 for (x = 0; x < num_clips; x++)
\r
3943 DeleteObject(clips[x]);
\r
3946 DeleteObject(bufferBitmap);
\r
3949 ReleaseDC(hwndMain, hdc);
\r
3951 if (lastDrawnFlipView != flipView && nr == 0) {
\r
3953 CheckMenuItem(GetMenu(hwndMain),IDM_FlipView, MF_BYCOMMAND|MF_CHECKED);
\r
3955 CheckMenuItem(GetMenu(hwndMain),IDM_FlipView, MF_BYCOMMAND|MF_UNCHECKED);
\r
3958 /* CopyBoard(lastDrawn, board);*/
\r
3959 lastDrawnHighlight = highlightInfo;
\r
3960 lastDrawnPremove = premoveHighlightInfo;
\r
3961 lastDrawnFlipView = flipView;
\r
3962 lastDrawnValid[nr] = 1;
\r
3965 /* [HGM] diag: Save the current board display to the given open file and close the file */
\r
3970 saveDiagFlag = 1; diagFile = f;
\r
3971 HDCDrawPosition(NULL, TRUE, NULL);
\r
3979 /*---------------------------------------------------------------------------*\
\r
3980 | CLIENT PAINT PROCEDURE
\r
3981 | This is the main event-handler for the WM_PAINT message.
\r
3983 \*---------------------------------------------------------------------------*/
\r
3985 PaintProc(HWND hwnd)
\r
3991 if((hdc = BeginPaint(hwnd, &ps))) {
\r
3992 if (IsIconic(hwnd)) {
\r
3993 DrawIcon(hdc, 2, 2, iconCurrent);
\r
3995 if (!appData.monoMode) {
\r
3996 SelectPalette(hdc, hPal, FALSE);
\r
3997 RealizePalette(hdc);
\r
3999 HDCDrawPosition(hdc, 1, NULL);
\r
4000 if(twoBoards) { // [HGM] dual: also redraw other board in other orientation
\r
4001 flipView = !flipView; partnerUp = !partnerUp;
\r
4002 HDCDrawPosition(hdc, 1, NULL);
\r
4003 flipView = !flipView; partnerUp = !partnerUp;
\r
4006 SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);
\r
4007 ExtTextOut(hdc, messageRect.left, messageRect.top,
\r
4008 ETO_CLIPPED|ETO_OPAQUE,
\r
4009 &messageRect, messageText, strlen(messageText), NULL);
\r
4010 SelectObject(hdc, oldFont);
\r
4011 DisplayBothClocks();
\r
4014 EndPaint(hwnd,&ps);
\r
4022 * If the user selects on a border boundary, return -1; if off the board,
\r
4023 * return -2. Otherwise map the event coordinate to the square.
\r
4024 * The offset boardRect.left or boardRect.top must already have been
\r
4025 * subtracted from x.
\r
4027 int EventToSquare(x, limit)
\r
4035 if ((x % (squareSize + lineGap)) >= squareSize)
\r
4037 x /= (squareSize + lineGap);
\r
4049 DropEnable dropEnables[] = {
\r
4050 { 'P', DP_Pawn, N_("Pawn") },
\r
4051 { 'N', DP_Knight, N_("Knight") },
\r
4052 { 'B', DP_Bishop, N_("Bishop") },
\r
4053 { 'R', DP_Rook, N_("Rook") },
\r
4054 { 'Q', DP_Queen, N_("Queen") },
\r
4058 SetupDropMenu(HMENU hmenu)
\r
4060 int i, count, enable;
\r
4062 extern char white_holding[], black_holding[];
\r
4063 char item[MSG_SIZ];
\r
4065 for (i=0; i<sizeof(dropEnables)/sizeof(DropEnable); i++) {
\r
4066 p = strchr(gameMode == IcsPlayingWhite ? white_holding : black_holding,
\r
4067 dropEnables[i].piece);
\r
4069 while (p && *p++ == dropEnables[i].piece) count++;
\r
4070 snprintf(item, MSG_SIZ, "%s %d", T_(dropEnables[i].name), count);
\r
4071 enable = count > 0 || !appData.testLegality
\r
4072 /*!!temp:*/ || (gameInfo.variant == VariantCrazyhouse
\r
4073 && !appData.icsActive);
\r
4074 ModifyMenu(hmenu, dropEnables[i].command,
\r
4075 MF_BYCOMMAND | (enable ? MF_ENABLED : MF_GRAYED) | MF_STRING,
\r
4076 dropEnables[i].command, item);
\r
4080 void DragPieceBegin(int x, int y, Boolean instantly)
\r
4082 dragInfo.lastpos.x = boardRect.left + x;
\r
4083 dragInfo.lastpos.y = boardRect.top + y;
\r
4084 if(instantly) dragInfo.pos = dragInfo.lastpos;
\r
4085 dragInfo.from.x = fromX;
\r
4086 dragInfo.from.y = fromY;
\r
4087 dragInfo.piece = boards[currentMove][fromY][fromX];
\r
4088 dragInfo.start = dragInfo.from;
\r
4089 SetCapture(hwndMain);
\r
4092 void DragPieceEnd(int x, int y)
\r
4095 dragInfo.start.x = dragInfo.start.y = -1;
\r
4096 dragInfo.from = dragInfo.start;
\r
4097 dragInfo.pos = dragInfo.lastpos = dragInfo.start;
\r
4100 void ChangeDragPiece(ChessSquare piece)
\r
4102 dragInfo.piece = piece;
\r
4105 /* Event handler for mouse messages */
\r
4107 MouseEvent(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
\r
4111 static int recursive = 0;
\r
4113 BOOLEAN forceFullRepaint = IsFullRepaintPreferrable(); /* [AS] */
\r
4116 if (message == WM_MBUTTONUP) {
\r
4117 /* Hideous kludge to fool TrackPopupMenu into paying attention
\r
4118 to the middle button: we simulate pressing the left button too!
\r
4120 PostMessage(hwnd, WM_LBUTTONDOWN, wParam, lParam);
\r
4121 PostMessage(hwnd, WM_LBUTTONUP, wParam, lParam);
\r
4127 pt.x = LOWORD(lParam);
\r
4128 pt.y = HIWORD(lParam);
\r
4129 x = EventToSquare(pt.x - boardRect.left, BOARD_WIDTH);
\r
4130 y = EventToSquare(pt.y - boardRect.top, BOARD_HEIGHT);
\r
4131 if (!flipView && y >= 0) {
\r
4132 y = BOARD_HEIGHT - 1 - y;
\r
4134 if (flipView && x >= 0) {
\r
4135 x = BOARD_WIDTH - 1 - x;
\r
4138 shiftKey = GetKeyState(VK_SHIFT) < 0; // [HGM] remember last shift status
\r
4140 switch (message) {
\r
4141 case WM_LBUTTONDOWN:
\r
4142 if (PtInRect((LPRECT) &whiteRect, pt)) {
\r
4143 ClockClick(flipClock);
\r
4144 } else if (PtInRect((LPRECT) &blackRect, pt)) {
\r
4145 ClockClick(!flipClock);
\r
4147 dragInfo.start.x = dragInfo.start.y = -1;
\r
4148 dragInfo.from = dragInfo.start;
\r
4149 if(fromX == -1 && frozen) { // not sure where this is for
\r
4150 fromX = fromY = -1;
\r
4151 DrawPosition(forceFullRepaint || FALSE, NULL); /* [AS] */
\r
4154 LeftClick(Press, pt.x - boardRect.left, pt.y - boardRect.top);
\r
4155 DrawPosition(TRUE, NULL);
\r
4158 case WM_LBUTTONUP:
\r
4159 LeftClick(Release, pt.x - boardRect.left, pt.y - boardRect.top);
\r
4160 DrawPosition(TRUE, NULL);
\r
4163 case WM_MOUSEMOVE:
\r
4164 if(SeekGraphClick(Press, pt.x - boardRect.left, pt.y - boardRect.top, 1)) break;
\r
4165 if(PromoScroll(pt.x - boardRect.left, pt.y - boardRect.top)) break;
\r
4166 MovePV(pt.x - boardRect.left, pt.y - boardRect.top, boardRect.bottom - boardRect.top);
\r
4167 if ((appData.animateDragging || appData.highlightDragging)
\r
4168 && (wParam & MK_LBUTTON)
\r
4169 && dragInfo.from.x >= 0)
\r
4171 BOOL full_repaint = FALSE;
\r
4173 if (appData.animateDragging) {
\r
4174 dragInfo.pos = pt;
\r
4176 if (appData.highlightDragging) {
\r
4177 SetHighlights(fromX, fromY, x, y);
\r
4178 if( IsDrawArrowEnabled() && (x < 0 || x >= BOARD_WIDTH || y < 0 || y >= BOARD_HEIGHT) ) {
\r
4179 full_repaint = TRUE;
\r
4183 DrawPosition( full_repaint, NULL);
\r
4185 dragInfo.lastpos = dragInfo.pos;
\r
4189 case WM_MOUSEWHEEL: // [DM]
\r
4190 { static int lastDir = 0; // [HGM] build in some hysteresis to avoid spurious events
\r
4191 /* Mouse Wheel is being rolled forward
\r
4192 * Play moves forward
\r
4194 if((short)HIWORD(wParam) < 0 && currentMove < forwardMostMove)
\r
4195 { if(lastDir == 1) ForwardEvent(); else lastDir = 1; } // [HGM] suppress first event in direction
\r
4196 /* Mouse Wheel is being rolled backward
\r
4197 * Play moves backward
\r
4199 if((short)HIWORD(wParam) > 0 && currentMove > backwardMostMove)
\r
4200 { if(lastDir == -1) BackwardEvent(); else lastDir = -1; }
\r
4204 case WM_MBUTTONUP:
\r
4205 case WM_RBUTTONUP:
\r
4207 RightClick(Release, pt.x - boardRect.left, pt.y - boardRect.top, &fromX, &fromY);
\r
4210 case WM_MBUTTONDOWN:
\r
4211 case WM_RBUTTONDOWN:
\r
4214 fromX = fromY = -1;
\r
4215 dragInfo.pos.x = dragInfo.pos.y = -1;
\r
4216 dragInfo.start.x = dragInfo.start.y = -1;
\r
4217 dragInfo.from = dragInfo.start;
\r
4218 dragInfo.lastpos = dragInfo.pos;
\r
4219 if (appData.highlightDragging) {
\r
4220 ClearHighlights();
\r
4223 /* [HGM] right mouse button in clock area edit-game mode ups clock */
\r
4224 if (PtInRect((LPRECT) &whiteRect, pt)) {
\r
4225 if (gameMode == EditGame || GetKeyState(VK_SHIFT) < 0) AdjustClock(flipClock, 1);
\r
4226 } else if (PtInRect((LPRECT) &blackRect, pt)) {
\r
4227 if (gameMode == EditGame || GetKeyState(VK_SHIFT) < 0) AdjustClock(!flipClock, 1);
\r
4231 DrawPosition(TRUE, NULL);
\r
4233 menuNr = RightClick(Press, pt.x - boardRect.left, pt.y - boardRect.top, &fromX, &fromY);
\r
4236 if (message == WM_MBUTTONDOWN) {
\r
4237 buttonCount = 3; /* even if system didn't think so */
\r
4238 if (wParam & MK_SHIFT)
\r
4239 MenuPopup(hwnd, pt, LoadMenu(hInst, "BlackPieceMenu"), -1);
\r
4241 MenuPopup(hwnd, pt, LoadMenu(hInst, "WhitePieceMenu"), -1);
\r
4242 } else { /* message == WM_RBUTTONDOWN */
\r
4243 /* Just have one menu, on the right button. Windows users don't
\r
4244 think to try the middle one, and sometimes other software steals
\r
4245 it, or it doesn't really exist. */
\r
4246 if(gameInfo.variant != VariantShogi)
\r
4247 MenuPopup(hwnd, pt, LoadMenu(hInst, "PieceMenu"), -1);
\r
4249 MenuPopup(hwnd, pt, LoadMenu(hInst, "ShogiPieceMenu"), -1);
\r
4253 SetCapture(hwndMain);
4256 hmenu = LoadMenu(hInst, "DropPieceMenu");
\r
4257 SetupDropMenu(hmenu);
\r
4258 MenuPopup(hwnd, pt, hmenu, -1);
\r
4268 /* Preprocess messages for buttons in main window */
\r
4270 ButtonProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
\r
4272 int id = GetWindowLongPtr(hwnd, GWLP_ID);
\r
4275 for (i=0; i<N_BUTTONS; i++) {
\r
4276 if (buttonDesc[i].id == id) break;
\r
4278 if (i == N_BUTTONS) return 0;
\r
4279 switch (message) {
\r
4284 dir = (wParam == VK_LEFT) ? -1 : 1;
\r
4285 SetFocus(buttonDesc[(i + dir + N_BUTTONS) % N_BUTTONS].hwnd);
\r
4292 SendMessage(hwndMain, WM_COMMAND, MAKEWPARAM(buttonDesc[i].id, 0), 0);
\r
4295 if (appData.icsActive && (isalpha((char)wParam) || wParam == '0')) {
\r
4296 // [HGM] movenum: only letters or leading zero should go to ICS input
\r
4297 HWND h = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
4298 if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);
\r
4300 SendMessage(h, WM_CHAR, wParam, lParam);
\r
4302 } else if (isalpha((char)wParam) || isdigit((char)wParam)){
\r
4303 TypeInEvent((char)wParam);
\r
4309 return CallWindowProc(buttonDesc[i].wndproc, hwnd, message, wParam, lParam);
\r
4312 /* Process messages for Promotion dialog box */
\r
4314 Promotion(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
4318 switch (message) {
\r
4319 case WM_INITDIALOG: /* message: initialize dialog box */
\r
4320 /* Center the dialog over the application window */
\r
4321 CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));
\r
4322 Translate(hDlg, DLG_PromotionKing);
\r
4323 ShowWindow(GetDlgItem(hDlg, PB_King),
\r
4324 (!appData.testLegality || gameInfo.variant == VariantSuicide ||
\r
4325 gameInfo.variant == VariantSpartan && !WhiteOnMove(currentMove) ||
\r
4326 gameInfo.variant == VariantGiveaway || gameInfo.variant == VariantSuper ) ?
\r
4327 SW_SHOW : SW_HIDE);
\r
4328 /* [HGM] Only allow C & A promotions if these pieces are defined */
\r
4329 ShowWindow(GetDlgItem(hDlg, PB_Archbishop),
\r
4330 ((PieceToChar(WhiteAngel) >= 'A' && WhiteOnMove(currentMove) &&
\r
4331 PieceToChar(WhiteAngel) != '~') ||
\r
4332 (PieceToChar(BlackAngel) >= 'A' && !WhiteOnMove(currentMove) &&
\r
4333 PieceToChar(BlackAngel) != '~') ) ?
\r
4334 SW_SHOW : SW_HIDE);
\r
4335 ShowWindow(GetDlgItem(hDlg, PB_Chancellor),
\r
4336 ((PieceToChar(WhiteMarshall) >= 'A' && WhiteOnMove(currentMove) &&
\r
4337 PieceToChar(WhiteMarshall) != '~') ||
\r
4338 (PieceToChar(BlackMarshall) >= 'A' && !WhiteOnMove(currentMove) &&
\r
4339 PieceToChar(BlackMarshall) != '~') ) ?
\r
4340 SW_SHOW : SW_HIDE);
\r
4341 /* [HGM] Hide B & R button in Shogi, use Q as promote, N as defer */
\r
4342 ShowWindow(GetDlgItem(hDlg, PB_Rook),
\r
4343 gameInfo.variant != VariantShogi ?
\r
4344 SW_SHOW : SW_HIDE);
\r
4345 ShowWindow(GetDlgItem(hDlg, PB_Bishop),
\r
4346 gameInfo.variant != VariantShogi ?
\r
4347 SW_SHOW : SW_HIDE);
\r
4348 if(gameInfo.variant == VariantShogi) {
\r
4349 SetDlgItemText(hDlg, PB_Queen, "YES");
\r
4350 SetDlgItemText(hDlg, PB_Knight, "NO");
\r
4351 SetWindowText(hDlg, "Promote?");
\r
4353 ShowWindow(GetDlgItem(hDlg, IDC_Centaur),
\r
4354 gameInfo.variant == VariantSuper ?
\r
4355 SW_SHOW : SW_HIDE);
\r
4358 case WM_COMMAND: /* message: received a command */
\r
4359 switch (LOWORD(wParam)) {
\r
4361 EndDialog(hDlg, TRUE); /* Exit the dialog */
\r
4362 ClearHighlights();
\r
4363 DrawPosition(FALSE, NULL);
\r
4366 promoChar = gameInfo.variant == VariantSuper ? PieceToChar(BlackSilver) : PieceToChar(BlackKing);
\r
4369 promoChar = gameInfo.variant == VariantShogi ? '+' : ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteQueen : BlackQueen));
\r
4372 promoChar = ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteRook : BlackRook));
\r
4373 if(gameInfo.variant == VariantSpartan && !WhiteOnMove(currentMove)) promoChar = PieceToChar(BlackDragon);
\r
4376 promoChar = ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteBishop : BlackBishop));
\r
4377 if(gameInfo.variant == VariantSpartan && !WhiteOnMove(currentMove)) promoChar = PieceToChar(BlackAlfil);
\r
4379 case PB_Chancellor:
\r
4380 promoChar = ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteMarshall : BlackMarshall));
\r
4382 case PB_Archbishop:
\r
4383 promoChar = ToLower(PieceToChar(WhiteOnMove(currentMove) ? WhiteAngel : BlackAngel));
\r
4386 promoChar = gameInfo.variant == VariantShogi ? '=' : PieceToChar(WhiteOnMove(currentMove) ? WhiteKnight : BlackKnight);
\r
4391 if(promoChar == '.') return FALSE; // invalid piece chosen
\r
4392 EndDialog(hDlg, TRUE); /* Exit the dialog */
\r
4393 UserMoveEvent(fromX, fromY, toX, toY, promoChar);
\r
4394 fromX = fromY = -1;
\r
4395 if (!appData.highlightLastMove) {
\r
4396 ClearHighlights();
\r
4397 DrawPosition(FALSE, NULL);
\r
4404 /* Pop up promotion dialog */
\r
4406 PromotionPopup(HWND hwnd)
\r
4410 lpProc = MakeProcInstance((FARPROC)Promotion, hInst);
\r
4411 DialogBox(hInst, MAKEINTRESOURCE(DLG_PromotionKing),
\r
4412 hwnd, (DLGPROC)lpProc);
\r
4413 FreeProcInstance(lpProc);
\r
4419 DrawPosition(TRUE, NULL);
\r
4420 PromotionPopup(hwndMain);
\r
4423 /* Toggle ShowThinking */
\r
4425 ToggleShowThinking()
\r
4427 appData.showThinking = !appData.showThinking;
\r
4428 ShowThinkingEvent();
\r
4432 LoadGameDialog(HWND hwnd, char* title)
\r
4436 char fileTitle[MSG_SIZ];
\r
4437 f = OpenFileDialog(hwnd, "rb", "",
\r
4438 appData.oldSaveStyle ? "gam" : "pgn",
\r
4440 title, &number, fileTitle, NULL);
\r
4442 cmailMsgLoaded = FALSE;
\r
4443 if (number == 0) {
\r
4444 int error = GameListBuild(f);
\r
4446 DisplayError(_("Cannot build game list"), error);
\r
4447 } else if (!ListEmpty(&gameList) &&
\r
4448 ((ListGame *) gameList.tailPred)->number > 1) {
\r
4449 GameListPopUp(f, fileTitle);
\r
4452 GameListDestroy();
\r
4455 LoadGame(f, number, fileTitle, FALSE);
\r
4459 int get_term_width()
\r
4464 HFONT hfont, hold_font;
\r
4469 hText = GetDlgItem(hwndConsole, OPT_ConsoleText);
\r
4473 // get the text metrics
\r
4474 hdc = GetDC(hText);
\r
4475 lf = font[boardSize][CONSOLE_FONT]->lf;
\r
4476 if (consoleCF.dwEffects & CFE_BOLD)
\r
4477 lf.lfWeight = FW_BOLD;
\r
4478 if (consoleCF.dwEffects & CFE_ITALIC)
\r
4479 lf.lfItalic = TRUE;
\r
4480 if (consoleCF.dwEffects & CFE_STRIKEOUT)
\r
4481 lf.lfStrikeOut = TRUE;
\r
4482 if (consoleCF.dwEffects & CFE_UNDERLINE)
\r
4483 lf.lfUnderline = TRUE;
\r
4484 hfont = CreateFontIndirect(&lf);
\r
4485 hold_font = SelectObject(hdc, hfont);
\r
4486 GetTextMetrics(hdc, &tm);
\r
4487 SelectObject(hdc, hold_font);
\r
4488 DeleteObject(hfont);
\r
4489 ReleaseDC(hText, hdc);
\r
4491 // get the rectangle
\r
4492 SendMessage(hText, EM_GETRECT, 0, (LPARAM)&rc);
\r
4494 return (rc.right-rc.left) / tm.tmAveCharWidth;
\r
4497 void UpdateICSWidth(HWND hText)
\r
4499 LONG old_width, new_width;
\r
4501 new_width = get_term_width(hText, FALSE);
\r
4502 old_width = GetWindowLongPtr(hText, GWLP_USERDATA);
\r
4503 if (new_width != old_width)
\r
4505 ics_update_width(new_width);
\r
4506 SetWindowLongPtr(hText, GWLP_USERDATA, new_width);
\r
4511 ChangedConsoleFont()
\r
4514 CHARRANGE tmpsel, sel;
\r
4515 MyFont *f = font[boardSize][CONSOLE_FONT];
\r
4516 HWND hText = GetDlgItem(hwndConsole, OPT_ConsoleText);
\r
4517 HWND hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
4520 cfmt.cbSize = sizeof(CHARFORMAT);
\r
4521 cfmt.dwMask = CFM_FACE|CFM_SIZE|CFM_CHARSET;
\r
4522 safeStrCpy(cfmt.szFaceName, font[boardSize][CONSOLE_FONT]->mfp.faceName,
\r
4523 sizeof(cfmt.szFaceName)/sizeof(cfmt.szFaceName[0]) );
\r
4524 /* yHeight is expressed in twips. A twip is 1/20 of a font's point
\r
4525 * size. This was undocumented in the version of MSVC++ that I had
\r
4526 * when I wrote the code, but is apparently documented now.
\r
4528 cfmt.yHeight = (int)(f->mfp.pointSize * 20.0 + 0.5);
\r
4529 cfmt.bCharSet = f->lf.lfCharSet;
\r
4530 cfmt.bPitchAndFamily = f->lf.lfPitchAndFamily;
\r
4531 SendMessage(hText, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &cfmt);
\r
4532 SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &cfmt);
\r
4533 /* Why are the following seemingly needed too? */
\r
4534 SendMessage(hText, EM_SETCHARFORMAT, SCF_DEFAULT, (LPARAM) &cfmt);
\r
4535 SendMessage(hInput, EM_SETCHARFORMAT, SCF_DEFAULT, (LPARAM) &cfmt);
\r
4536 SendMessage(hText, EM_EXGETSEL, 0, (LPARAM)&sel);
\r
4538 tmpsel.cpMax = -1; /*999999?*/
\r
4539 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&tmpsel);
\r
4540 SendMessage(hText, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cfmt);
\r
4541 /* Trying putting this here too. It still seems to tickle a RichEdit
\r
4542 * bug: sometimes RichEdit indents the first line of a paragraph too.
\r
4544 paraf.cbSize = sizeof(paraf);
\r
4545 paraf.dwMask = PFM_OFFSET | PFM_STARTINDENT;
\r
4546 paraf.dxStartIndent = 0;
\r
4547 paraf.dxOffset = WRAP_INDENT;
\r
4548 SendMessage(hText, EM_SETPARAFORMAT, 0, (LPARAM) ¶f);
\r
4549 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
4550 UpdateICSWidth(hText);
\r
4553 /*---------------------------------------------------------------------------*\
\r
4555 * Window Proc for main window
\r
4557 \*---------------------------------------------------------------------------*/
\r
4559 /* Process messages for main window, etc. */
\r
4561 WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
\r
4564 int wmId, wmEvent;
\r
4568 char fileTitle[MSG_SIZ];
\r
4569 char buf[MSG_SIZ];
\r
4570 static SnapData sd;
\r
4572 switch (message) {
\r
4574 case WM_PAINT: /* message: repaint portion of window */
\r
4578 case WM_ERASEBKGND:
\r
4579 if (IsIconic(hwnd)) {
\r
4580 /* Cheat; change the message */
\r
4581 return (DefWindowProc(hwnd, WM_ICONERASEBKGND, wParam, lParam));
\r
4583 return (DefWindowProc(hwnd, message, wParam, lParam));
\r
4587 case WM_LBUTTONDOWN:
\r
4588 case WM_MBUTTONDOWN:
\r
4589 case WM_RBUTTONDOWN:
\r
4590 case WM_LBUTTONUP:
\r
4591 case WM_MBUTTONUP:
\r
4592 case WM_RBUTTONUP:
\r
4593 case WM_MOUSEMOVE:
\r
4594 case WM_MOUSEWHEEL:
\r
4595 MouseEvent(hwnd, message, wParam, lParam);
\r
4598 JAWS_KB_NAVIGATION
\r
4602 JAWS_ALT_INTERCEPT
\r
4604 if (appData.icsActive && ((char)wParam == '\r' || (char)wParam > ' ' && !((char)wParam >= '1' && (char)wParam <= '9'))) {
\r
4605 // [HGM] movenum: for non-zero digits we always do type-in dialog
\r
4606 HWND h = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
4607 if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);
\r
4609 SendMessage(h, message, wParam, lParam);
\r
4610 } else if(lParam != KF_REPEAT) {
\r
4611 if (isalpha((char)wParam) || isdigit((char)wParam)) {
\r
4612 TypeInEvent((char)wParam);
\r
4613 } else if((char)wParam == 003) CopyGameToClipboard();
\r
4614 else if((char)wParam == 026) PasteGameOrFENFromClipboard();
\r
4619 case WM_PALETTECHANGED:
\r
4620 if (hwnd != (HWND)wParam && !appData.monoMode) {
\r
4622 HDC hdc = GetDC(hwndMain);
\r
4623 SelectPalette(hdc, hPal, TRUE);
\r
4624 nnew = RealizePalette(hdc);
\r
4626 paletteChanged = TRUE;
\r
4627 InvalidateRect(hwnd, &boardRect, FALSE);
\r
4629 ReleaseDC(hwnd, hdc);
\r
4633 case WM_QUERYNEWPALETTE:
\r
4634 if (!appData.monoMode /*&& paletteChanged*/) {
\r
4636 HDC hdc = GetDC(hwndMain);
\r
4637 paletteChanged = FALSE;
\r
4638 SelectPalette(hdc, hPal, FALSE);
\r
4639 nnew = RealizePalette(hdc);
\r
4641 InvalidateRect(hwnd, &boardRect, FALSE);
\r
4643 ReleaseDC(hwnd, hdc);
\r
4648 case WM_COMMAND: /* message: command from application menu */
\r
4649 wmId = LOWORD(wParam);
\r
4650 wmEvent = HIWORD(wParam);
\r
4655 SAY("new game enter a move to play against the computer with white");
\r
4658 case IDM_NewGameFRC:
\r
4659 if( NewGameFRC() == 0 ) {
\r
4664 case IDM_NewVariant:
\r
4665 NewVariantPopup(hwnd);
\r
4668 case IDM_LoadGame:
\r
4669 LoadGameDialog(hwnd, _("Load Game from File"));
\r
4672 case IDM_LoadNextGame:
\r
4676 case IDM_LoadPrevGame:
\r
4680 case IDM_ReloadGame:
\r
4684 case IDM_LoadPosition:
\r
4685 if (gameMode == AnalyzeMode || gameMode == AnalyzeFile) {
\r
4686 Reset(FALSE, TRUE);
\r
4689 f = OpenFileDialog(hwnd, "rb", "",
\r
4690 appData.oldSaveStyle ? "pos" : "fen",
\r
4692 _("Load Position from File"), &number, fileTitle, NULL);
\r
4694 LoadPosition(f, number, fileTitle);
\r
4698 case IDM_LoadNextPosition:
\r
4699 ReloadPosition(1);
\r
4702 case IDM_LoadPrevPosition:
\r
4703 ReloadPosition(-1);
\r
4706 case IDM_ReloadPosition:
\r
4707 ReloadPosition(0);
\r
4710 case IDM_SaveGame:
\r
4711 defName = DefaultFileName(appData.oldSaveStyle ? "gam" : "pgn");
\r
4712 f = OpenFileDialog(hwnd, "a", defName,
\r
4713 appData.oldSaveStyle ? "gam" : "pgn",
\r
4715 _("Save Game to File"), NULL, fileTitle, NULL);
\r
4717 SaveGame(f, 0, "");
\r
4721 case IDM_SavePosition:
\r
4722 defName = DefaultFileName(appData.oldSaveStyle ? "pos" : "fen");
\r
4723 f = OpenFileDialog(hwnd, "a", defName,
\r
4724 appData.oldSaveStyle ? "pos" : "fen",
\r
4726 _("Save Position to File"), NULL, fileTitle, NULL);
\r
4728 SavePosition(f, 0, "");
\r
4732 case IDM_SaveDiagram:
\r
4733 defName = "diagram";
\r
4734 f = OpenFileDialog(hwnd, "wb", defName,
\r
4737 _("Save Diagram to File"), NULL, fileTitle, NULL);
\r
4743 case IDM_CopyGame:
\r
4744 CopyGameToClipboard();
\r
4747 case IDM_PasteGame:
\r
4748 PasteGameFromClipboard();
\r
4751 case IDM_CopyGameListToClipboard:
\r
4752 CopyGameListToClipboard();
\r
4755 /* [AS] Autodetect FEN or PGN data */
\r
4756 case IDM_PasteAny:
\r
4757 PasteGameOrFENFromClipboard();
\r
4760 /* [AS] Move history */
\r
4761 case IDM_ShowMoveHistory:
\r
4762 if( MoveHistoryIsUp() ) {
\r
4763 MoveHistoryPopDown();
\r
4766 MoveHistoryPopUp();
\r
4770 /* [AS] Eval graph */
\r
4771 case IDM_ShowEvalGraph:
\r
4772 if( EvalGraphIsUp() ) {
\r
4773 EvalGraphPopDown();
\r
4777 SetFocus(hwndMain);
\r
4781 /* [AS] Engine output */
\r
4782 case IDM_ShowEngineOutput:
\r
4783 if( EngineOutputIsUp() ) {
\r
4784 EngineOutputPopDown();
\r
4787 EngineOutputPopUp();
\r
4791 /* [AS] User adjudication */
\r
4792 case IDM_UserAdjudication_White:
\r
4793 UserAdjudicationEvent( +1 );
\r
4796 case IDM_UserAdjudication_Black:
\r
4797 UserAdjudicationEvent( -1 );
\r
4800 case IDM_UserAdjudication_Draw:
\r
4801 UserAdjudicationEvent( 0 );
\r
4804 /* [AS] Game list options dialog */
\r
4805 case IDM_GameListOptions:
\r
4806 GameListOptions();
\r
4813 case IDM_CopyPosition:
\r
4814 CopyFENToClipboard();
\r
4817 case IDM_PastePosition:
\r
4818 PasteFENFromClipboard();
\r
4821 case IDM_MailMove:
\r
4825 case IDM_ReloadCMailMsg:
\r
4826 Reset(TRUE, TRUE);
\r
4827 ReloadCmailMsgEvent(FALSE);
\r
4830 case IDM_Minimize:
\r
4831 ShowWindow(hwnd, SW_MINIMIZE);
\r
4838 case IDM_MachineWhite:
\r
4839 MachineWhiteEvent();
\r
4841 * refresh the tags dialog only if it's visible
\r
4843 if (gameMode == MachinePlaysWhite && IsWindowVisible(editTagsDialog)) {
\r
4845 tags = PGNTags(&gameInfo);
\r
4846 TagsPopUp(tags, CmailMsg());
\r
4849 SAY("computer starts playing white");
\r
4852 case IDM_MachineBlack:
\r
4853 MachineBlackEvent();
\r
4855 * refresh the tags dialog only if it's visible
\r
4857 if (gameMode == MachinePlaysBlack && IsWindowVisible(editTagsDialog)) {
\r
4859 tags = PGNTags(&gameInfo);
\r
4860 TagsPopUp(tags, CmailMsg());
\r
4863 SAY("computer starts playing black");
\r
4866 case IDM_Match: // [HGM] match: flows into next case, after setting Match Mode and nr of Games
\r
4867 MatchEvent(2); // distinguish from command-line-triggered case (matchMode=1)
\r
4870 case IDM_TwoMachines:
\r
4871 TwoMachinesEvent();
\r
4873 * refresh the tags dialog only if it's visible
\r
4875 if (gameMode == TwoMachinesPlay && IsWindowVisible(editTagsDialog)) {
\r
4877 tags = PGNTags(&gameInfo);
\r
4878 TagsPopUp(tags, CmailMsg());
\r
4881 SAY("computer starts playing both sides");
\r
4884 case IDM_AnalysisMode:
\r
4885 if (!first.analysisSupport) {
\r
4886 snprintf(buf, MSG_SIZ, _("%s does not support analysis"), first.tidy);
\r
4887 DisplayError(buf, 0);
\r
4889 SAY("analyzing current position");
\r
4890 /* [DM] icsEngineAnlyze [HGM] Why is this front-end??? */
\r
4891 if (appData.icsActive) {
\r
4892 if (gameMode != IcsObserving) {
\r
4893 snprintf(buf, MSG_SIZ, "You are not observing a game");
\r
4894 DisplayError(buf, 0);
\r
4895 /* secure check */
\r
4896 if (appData.icsEngineAnalyze) {
\r
4897 if (appData.debugMode)
\r
4898 fprintf(debugFP, "Found unexpected active ICS engine analyze \n");
\r
4899 ExitAnalyzeMode();
\r
4905 /* if enable, user want disable icsEngineAnalyze */
\r
4906 if (appData.icsEngineAnalyze) {
\r
4907 ExitAnalyzeMode();
\r
4911 appData.icsEngineAnalyze = TRUE;
\r
4912 if (appData.debugMode) fprintf(debugFP, "ICS engine analyze starting...\n");
\r
4915 if (!appData.showThinking) ToggleShowThinking();
\r
4916 AnalyzeModeEvent();
\r
4920 case IDM_AnalyzeFile:
\r
4921 if (!first.analysisSupport) {
\r
4922 char buf[MSG_SIZ];
\r
4923 snprintf(buf, MSG_SIZ, _("%s does not support analysis"), first.tidy);
\r
4924 DisplayError(buf, 0);
\r
4926 if (!appData.showThinking) ToggleShowThinking();
\r
4927 AnalyzeFileEvent();
\r
4928 // LoadGameDialog(hwnd, _("Analyze Game from File"));
\r
4929 AnalysisPeriodicEvent(1);
\r
4933 case IDM_IcsClient:
\r
4937 case IDM_EditGame:
\r
4938 case IDM_EditGame2:
\r
4943 case IDM_EditPosition:
\r
4944 case IDM_EditPosition2:
\r
4945 EditPositionEvent();
\r
4946 SAY("enter a FEN string or setup a position on the board using the control R pop up menu");
\r
4949 case IDM_Training:
\r
4953 case IDM_ShowGameList:
\r
4954 ShowGameListProc();
\r
4957 case IDM_EditProgs1:
\r
4958 EditTagsPopUp(firstChessProgramNames, &firstChessProgramNames);
\r
4961 case IDM_EditProgs2:
\r
4962 LoadEnginePopUp(hwndMain);
\r
4963 // EditTagsPopUp(secondChessProgramNames, &secondChessProgramNames);
\r
4966 case IDM_EditServers:
\r
4967 EditTagsPopUp(icsNames, &icsNames);
\r
4970 case IDM_EditTags:
\r
4975 case IDM_EditBook:
\r
4979 case IDM_EditComment:
\r
4981 if (commentUp && editComment) {
\r
4984 EditCommentEvent();
\r
5004 case IDM_CallFlag:
\r
5024 case IDM_StopObserving:
\r
5025 StopObservingEvent();
\r
5028 case IDM_StopExamining:
\r
5029 StopExaminingEvent();
\r
5033 UploadGameEvent();
\r
5036 case IDM_TypeInMove:
\r
5037 TypeInEvent('\000');
\r
5040 case IDM_TypeInName:
\r
5041 PopUpNameDialog('\000');
\r
5044 case IDM_Backward:
\r
5046 SetFocus(hwndMain);
\r
5053 SetFocus(hwndMain);
\r
5058 SetFocus(hwndMain);
\r
5063 SetFocus(hwndMain);
\r
5066 case OPT_GameListNext: // [HGM] forward these two accelerators to Game List
\r
5067 case OPT_GameListPrev:
\r
5068 if(gameListDialog) SendMessage(gameListDialog, WM_COMMAND, wmId, 0);
\r
5072 RevertEvent(FALSE);
\r
5075 case IDM_Annotate: // [HGM] vari: revert with annotation
\r
5076 RevertEvent(TRUE);
\r
5079 case IDM_TruncateGame:
\r
5080 TruncateGameEvent();
\r
5087 case IDM_RetractMove:
\r
5088 RetractMoveEvent();
\r
5091 case IDM_FlipView:
\r
5092 flipView = !flipView;
\r
5093 DrawPosition(FALSE, NULL);
\r
5096 case IDM_FlipClock:
\r
5097 flipClock = !flipClock;
\r
5098 DisplayBothClocks();
\r
5102 case IDM_MuteSounds:
\r
5103 mute = !mute; // [HGM] mute: keep track of global muting variable
\r
5104 CheckMenuItem(GetMenu(hwndMain),IDM_MuteSounds,
\r
5105 MF_BYCOMMAND|(mute?MF_CHECKED:MF_UNCHECKED));
\r
5108 case IDM_GeneralOptions:
\r
5109 GeneralOptionsPopup(hwnd);
\r
5110 DrawPosition(TRUE, NULL);
\r
5113 case IDM_BoardOptions:
\r
5114 BoardOptionsPopup(hwnd);
\r
5117 case IDM_EnginePlayOptions:
\r
5118 EnginePlayOptionsPopup(hwnd);
\r
5121 case IDM_Engine1Options:
\r
5122 EngineOptionsPopup(hwnd, &first);
\r
5125 case IDM_Engine2Options:
\r
5127 if(WaitForEngine(&second, SettingsMenuIfReady)) break;
\r
5128 EngineOptionsPopup(hwnd, &second);
\r
5131 case IDM_OptionsUCI:
\r
5132 UciOptionsPopup(hwnd);
\r
5136 TourneyPopup(hwnd);
\r
5139 case IDM_IcsOptions:
\r
5140 IcsOptionsPopup(hwnd);
\r
5144 FontsOptionsPopup(hwnd);
\r
5148 SoundOptionsPopup(hwnd);
\r
5151 case IDM_CommPort:
\r
5152 CommPortOptionsPopup(hwnd);
\r
5155 case IDM_LoadOptions:
\r
5156 LoadOptionsPopup(hwnd);
\r
5159 case IDM_SaveOptions:
\r
5160 SaveOptionsPopup(hwnd);
\r
5163 case IDM_TimeControl:
\r
5164 TimeControlOptionsPopup(hwnd);
\r
5167 case IDM_SaveSettings:
\r
5168 SaveSettings(settingsFileName);
\r
5171 case IDM_SaveSettingsOnExit:
\r
5172 saveSettingsOnExit = !saveSettingsOnExit;
\r
5173 (void) CheckMenuItem(GetMenu(hwndMain), IDM_SaveSettingsOnExit,
\r
5174 MF_BYCOMMAND|(saveSettingsOnExit ?
\r
5175 MF_CHECKED : MF_UNCHECKED));
\r
5186 case IDM_AboutGame:
\r
5191 appData.debugMode = !appData.debugMode;
\r
5192 if (appData.debugMode) {
\r
5193 char dir[MSG_SIZ];
\r
5194 GetCurrentDirectory(MSG_SIZ, dir);
\r
5195 SetCurrentDirectory(installDir);
\r
5196 debugFP = fopen(appData.nameOfDebugFile, "w");
\r
5197 SetCurrentDirectory(dir);
\r
5198 setbuf(debugFP, NULL);
\r
5205 case IDM_HELPCONTENTS:
\r
5206 if (!MyHelp (hwnd, "winboard.hlp", HELP_KEY,(DWORD)(LPSTR)"CONTENTS") &&
\r
5207 !HtmlHelp(hwnd, "winboard.chm", 0, 0) ) {
\r
5208 MessageBox (GetFocus(),
\r
5209 _("Unable to activate help"),
\r
5210 szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);
\r
5214 case IDM_HELPSEARCH:
\r
5215 if (!MyHelp (hwnd, "winboard.hlp", HELP_PARTIALKEY, (DWORD)(LPSTR)"") &&
\r
5216 !HtmlHelp(hwnd, "winboard.chm", 0, 0) ) {
\r
5217 MessageBox (GetFocus(),
\r
5218 _("Unable to activate help"),
\r
5219 szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);
\r
5223 case IDM_HELPHELP:
\r
5224 if(!WinHelp(hwnd, (LPSTR)NULL, HELP_HELPONHELP, 0)) {
\r
5225 MessageBox (GetFocus(),
\r
5226 _("Unable to activate help"),
\r
5227 szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);
\r
5232 lpProc = MakeProcInstance((FARPROC)About, hInst);
\r
5234 (gameInfo.event && strcmp(gameInfo.event, "Easter Egg Hunt") == 0) ?
\r
5235 "AboutBox2" : "AboutBox", hwnd, (DLGPROC)lpProc);
\r
5236 FreeProcInstance(lpProc);
\r
5239 case IDM_DirectCommand1:
\r
5240 AskQuestionEvent(_("Direct Command"),
\r
5241 _("Send to chess program:"), "", "1");
\r
5243 case IDM_DirectCommand2:
\r
5244 AskQuestionEvent(_("Direct Command"),
\r
5245 _("Send to second chess program:"), "", "2");
\r
5248 case EP_WhitePawn:
\r
5249 EditPositionMenuEvent(WhitePawn, fromX, fromY);
\r
5250 fromX = fromY = -1;
\r
5253 case EP_WhiteKnight:
\r
5254 EditPositionMenuEvent(WhiteKnight, fromX, fromY);
\r
5255 fromX = fromY = -1;
\r
5258 case EP_WhiteBishop:
\r
5259 EditPositionMenuEvent(WhiteBishop, fromX, fromY);
\r
5260 fromX = fromY = -1;
\r
5263 case EP_WhiteRook:
\r
5264 EditPositionMenuEvent(WhiteRook, fromX, fromY);
\r
5265 fromX = fromY = -1;
\r
5268 case EP_WhiteQueen:
\r
5269 EditPositionMenuEvent(WhiteQueen, fromX, fromY);
\r
5270 fromX = fromY = -1;
\r
5273 case EP_WhiteFerz:
\r
5274 EditPositionMenuEvent(WhiteFerz, fromX, fromY);
\r
5275 fromX = fromY = -1;
\r
5278 case EP_WhiteWazir:
\r
5279 EditPositionMenuEvent(WhiteWazir, fromX, fromY);
\r
5280 fromX = fromY = -1;
\r
5283 case EP_WhiteAlfil:
\r
5284 EditPositionMenuEvent(WhiteAlfil, fromX, fromY);
\r
5285 fromX = fromY = -1;
\r
5288 case EP_WhiteCannon:
\r
5289 EditPositionMenuEvent(WhiteCannon, fromX, fromY);
\r
5290 fromX = fromY = -1;
\r
5293 case EP_WhiteCardinal:
\r
5294 EditPositionMenuEvent(WhiteAngel, fromX, fromY);
\r
5295 fromX = fromY = -1;
\r
5298 case EP_WhiteMarshall:
\r
5299 EditPositionMenuEvent(WhiteMarshall, fromX, fromY);
\r
5300 fromX = fromY = -1;
\r
5303 case EP_WhiteKing:
\r
5304 EditPositionMenuEvent(WhiteKing, fromX, fromY);
\r
5305 fromX = fromY = -1;
\r
5308 case EP_BlackPawn:
\r
5309 EditPositionMenuEvent(BlackPawn, fromX, fromY);
\r
5310 fromX = fromY = -1;
\r
5313 case EP_BlackKnight:
\r
5314 EditPositionMenuEvent(BlackKnight, fromX, fromY);
\r
5315 fromX = fromY = -1;
\r
5318 case EP_BlackBishop:
\r
5319 EditPositionMenuEvent(BlackBishop, fromX, fromY);
\r
5320 fromX = fromY = -1;
\r
5323 case EP_BlackRook:
\r
5324 EditPositionMenuEvent(BlackRook, fromX, fromY);
\r
5325 fromX = fromY = -1;
\r
5328 case EP_BlackQueen:
\r
5329 EditPositionMenuEvent(BlackQueen, fromX, fromY);
\r
5330 fromX = fromY = -1;
\r
5333 case EP_BlackFerz:
\r
5334 EditPositionMenuEvent(BlackFerz, fromX, fromY);
\r
5335 fromX = fromY = -1;
\r
5338 case EP_BlackWazir:
\r
5339 EditPositionMenuEvent(BlackWazir, fromX, fromY);
\r
5340 fromX = fromY = -1;
\r
5343 case EP_BlackAlfil:
\r
5344 EditPositionMenuEvent(BlackAlfil, fromX, fromY);
\r
5345 fromX = fromY = -1;
\r
5348 case EP_BlackCannon:
\r
5349 EditPositionMenuEvent(BlackCannon, fromX, fromY);
\r
5350 fromX = fromY = -1;
\r
5353 case EP_BlackCardinal:
\r
5354 EditPositionMenuEvent(BlackAngel, fromX, fromY);
\r
5355 fromX = fromY = -1;
\r
5358 case EP_BlackMarshall:
\r
5359 EditPositionMenuEvent(BlackMarshall, fromX, fromY);
\r
5360 fromX = fromY = -1;
\r
5363 case EP_BlackKing:
\r
5364 EditPositionMenuEvent(BlackKing, fromX, fromY);
\r
5365 fromX = fromY = -1;
\r
5368 case EP_EmptySquare:
\r
5369 EditPositionMenuEvent(EmptySquare, fromX, fromY);
\r
5370 fromX = fromY = -1;
\r
5373 case EP_ClearBoard:
\r
5374 EditPositionMenuEvent(ClearBoard, fromX, fromY);
\r
5375 fromX = fromY = -1;
\r
5379 EditPositionMenuEvent(WhitePlay, fromX, fromY);
\r
5380 fromX = fromY = -1;
\r
5384 EditPositionMenuEvent(BlackPlay, fromX, fromY);
\r
5385 fromX = fromY = -1;
\r
5389 EditPositionMenuEvent(PromotePiece, fromX, fromY);
\r
5390 fromX = fromY = -1;
\r
5394 EditPositionMenuEvent(DemotePiece, fromX, fromY);
\r
5395 fromX = fromY = -1;
\r
5399 DropMenuEvent(WhitePawn, fromX, fromY);
\r
5400 fromX = fromY = -1;
\r
5404 DropMenuEvent(WhiteKnight, fromX, fromY);
\r
5405 fromX = fromY = -1;
\r
5409 DropMenuEvent(WhiteBishop, fromX, fromY);
\r
5410 fromX = fromY = -1;
\r
5414 DropMenuEvent(WhiteRook, fromX, fromY);
\r
5415 fromX = fromY = -1;
\r
5419 DropMenuEvent(WhiteQueen, fromX, fromY);
\r
5420 fromX = fromY = -1;
\r
5424 barbaric = 0; appData.language = "";
\r
5425 TranslateMenus(0);
\r
5426 CheckMenuItem(GetMenu(hwndMain), lastChecked, MF_BYCOMMAND|MF_UNCHECKED);
\r
5427 CheckMenuItem(GetMenu(hwndMain), IDM_English, MF_BYCOMMAND|MF_CHECKED);
\r
5428 lastChecked = wmId;
\r
5432 if(wmId > IDM_English && wmId < IDM_English+20) {
\r
5433 LoadLanguageFile(languageFile[wmId - IDM_English - 1]);
\r
5434 TranslateMenus(0);
\r
5435 CheckMenuItem(GetMenu(hwndMain), lastChecked, MF_BYCOMMAND|MF_UNCHECKED);
\r
5436 CheckMenuItem(GetMenu(hwndMain), wmId, MF_BYCOMMAND|MF_CHECKED);
\r
5437 lastChecked = wmId;
\r
5440 return (DefWindowProc(hwnd, message, wParam, lParam));
\r
5446 case CLOCK_TIMER_ID:
\r
5447 KillTimer(hwnd, clockTimerEvent); /* Simulate one-shot timer as in X */
\r
5448 clockTimerEvent = 0;
\r
5449 DecrementClocks(); /* call into back end */
\r
5451 case LOAD_GAME_TIMER_ID:
\r
5452 KillTimer(hwnd, loadGameTimerEvent); /* Simulate one-shot timer as in X*/
\r
5453 loadGameTimerEvent = 0;
\r
5454 AutoPlayGameLoop(); /* call into back end */
\r
5456 case ANALYSIS_TIMER_ID:
\r
5457 if ((gameMode == AnalyzeMode || gameMode == AnalyzeFile
\r
5458 || appData.icsEngineAnalyze) && appData.periodicUpdates) {
\r
5459 AnalysisPeriodicEvent(0);
\r
5461 KillTimer(hwnd, analysisTimerEvent);
\r
5462 analysisTimerEvent = 0;
\r
5465 case DELAYED_TIMER_ID:
\r
5466 KillTimer(hwnd, delayedTimerEvent);
\r
5467 delayedTimerEvent = 0;
\r
5468 delayedTimerCallback();
\r
5473 case WM_USER_Input:
\r
5474 InputEvent(hwnd, message, wParam, lParam);
\r
5477 /* [AS] Also move "attached" child windows */
\r
5478 case WM_WINDOWPOSCHANGING:
\r
5480 if( hwnd == hwndMain && appData.useStickyWindows ) {
\r
5481 LPWINDOWPOS lpwp = (LPWINDOWPOS) lParam;
\r
5483 if( ((lpwp->flags & SWP_NOMOVE) == 0) && ((lpwp->flags & SWP_NOSIZE) != 0) ) {
\r
5484 /* Window is moving */
\r
5487 // GetWindowRect( hwnd, &rcMain ); //[HGM] sticky: in XP this returned new position, not old
\r
5488 rcMain.left = wpMain.x; // replace by these 4 lines to reconstruct old rect
\r
5489 rcMain.right = wpMain.x + wpMain.width;
\r
5490 rcMain.top = wpMain.y;
\r
5491 rcMain.bottom = wpMain.y + wpMain.height;
\r
5493 ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, moveHistoryDialog, &wpMoveHistory );
\r
5494 ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, evalGraphDialog, &wpEvalGraph );
\r
5495 ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, engineOutputDialog, &wpEngineOutput );
\r
5496 ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, gameListDialog, &wpGameList );
\r
5497 ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, hwndConsole, &wpConsole );
\r
5498 wpMain.x = lpwp->x;
\r
5499 wpMain.y = lpwp->y;
\r
5504 /* [AS] Snapping */
\r
5505 case WM_ENTERSIZEMOVE:
\r
5506 if(appData.debugMode) { fprintf(debugFP, "size-move\n"); }
\r
5507 if (hwnd == hwndMain) {
\r
5508 doingSizing = TRUE;
\r
5511 return OnEnterSizeMove( &sd, hwnd, wParam, lParam );
\r
5515 if(appData.debugMode) { fprintf(debugFP, "sizing\n"); }
\r
5516 if (hwnd == hwndMain) {
\r
5517 lastSizing = wParam;
\r
5522 if(appData.debugMode) { fprintf(debugFP, "moving\n"); }
\r
5523 return OnMoving( &sd, hwnd, wParam, lParam );
\r
5525 case WM_EXITSIZEMOVE:
\r
5526 if(appData.debugMode) { fprintf(debugFP, "exit size-move, size = %d\n", squareSize); }
\r
5527 if (hwnd == hwndMain) {
\r
5529 doingSizing = FALSE;
\r
5530 InvalidateRect(hwnd, &boardRect, FALSE);
\r
5531 GetClientRect(hwnd, &client);
\r
5532 ResizeBoard(client.right, client.bottom, lastSizing);
\r
5534 if(appData.debugMode) { fprintf(debugFP, "square size = %d\n", squareSize); }
\r
5536 return OnExitSizeMove( &sd, hwnd, wParam, lParam );
\r
5539 case WM_DESTROY: /* message: window being destroyed */
\r
5540 PostQuitMessage(0);
\r
5544 if (hwnd == hwndMain) {
\r
5549 default: /* Passes it on if unprocessed */
\r
5550 return (DefWindowProc(hwnd, message, wParam, lParam));
\r
5555 /*---------------------------------------------------------------------------*\
\r
5557 * Misc utility routines
\r
5559 \*---------------------------------------------------------------------------*/
\r
5562 * Decent random number generator, at least not as bad as Windows
\r
5563 * standard rand, which returns a value in the range 0 to 0x7fff.
\r
5565 unsigned int randstate;
\r
5570 randstate = randstate * 1664525 + 1013904223;
\r
5571 return (int) randstate & 0x7fffffff;
\r
5575 mysrandom(unsigned int seed)
\r
5582 * returns TRUE if user selects a different color, FALSE otherwise
\r
5586 ChangeColor(HWND hwnd, COLORREF *which)
\r
5588 static BOOL firstTime = TRUE;
\r
5589 static DWORD customColors[16];
\r
5591 COLORREF newcolor;
\r
5596 /* Make initial colors in use available as custom colors */
\r
5597 /* Should we put the compiled-in defaults here instead? */
\r
5599 customColors[i++] = lightSquareColor & 0xffffff;
\r
5600 customColors[i++] = darkSquareColor & 0xffffff;
\r
5601 customColors[i++] = whitePieceColor & 0xffffff;
\r
5602 customColors[i++] = blackPieceColor & 0xffffff;
\r
5603 customColors[i++] = highlightSquareColor & 0xffffff;
\r
5604 customColors[i++] = premoveHighlightColor & 0xffffff;
\r
5606 for (ccl = (ColorClass) 0; ccl < NColorClasses && i < 16; ccl++) {
\r
5607 customColors[i++] = textAttribs[ccl].color;
\r
5609 while (i < 16) customColors[i++] = RGB(255, 255, 255);
\r
5610 firstTime = FALSE;
\r
5613 cc.lStructSize = sizeof(cc);
\r
5614 cc.hwndOwner = hwnd;
\r
5615 cc.hInstance = NULL;
\r
5616 cc.rgbResult = (DWORD) (*which & 0xffffff);
\r
5617 cc.lpCustColors = (LPDWORD) customColors;
\r
5618 cc.Flags = CC_RGBINIT|CC_FULLOPEN;
\r
5620 if (!ChooseColor(&cc)) return FALSE;
\r
5622 newcolor = (COLORREF) (0x2000000 | cc.rgbResult);
\r
5623 if (newcolor == *which) return FALSE;
\r
5624 *which = newcolor;
\r
5628 InitDrawingColors();
\r
5629 InvalidateRect(hwnd, &boardRect, FALSE);
\r
5634 MyLoadSound(MySound *ms)
\r
5640 if (ms->data && ms->flag) free(ms->data);
\r
5643 switch (ms->name[0]) {
\r
5649 /* System sound from Control Panel. Don't preload here. */
\r
5653 if (ms->name[1] == NULLCHAR) {
\r
5654 /* "!" alone = silence */
\r
5657 /* Builtin wave resource. Error if not found. */
\r
5658 HANDLE h = FindResource(hInst, ms->name + 1, "WAVE");
\r
5659 if (h == NULL) break;
\r
5660 ms->data = (void *)LoadResource(hInst, h);
\r
5661 ms->flag = 0; // not maloced, so cannot be freed!
\r
5662 if (h == NULL) break;
\r
5667 /* .wav file. Error if not found. */
\r
5668 f = fopen(ms->name, "rb");
\r
5669 if (f == NULL) break;
\r
5670 if (fstat(fileno(f), &st) < 0) break;
\r
5671 ms->data = malloc(st.st_size);
\r
5673 if (fread(ms->data, st.st_size, 1, f) < 1) break;
\r
5679 char buf[MSG_SIZ];
\r
5680 snprintf(buf, MSG_SIZ, _("Error loading sound %s"), ms->name);
\r
5681 DisplayError(buf, GetLastError());
\r
5687 MyPlaySound(MySound *ms)
\r
5689 BOOLEAN ok = FALSE;
\r
5691 if(mute) return TRUE; // [HGM] mute: suppress all sound play when muted
\r
5692 switch (ms->name[0]) {
\r
5694 if(appData.debugMode) fprintf(debugFP, "silence\n");
\r
5699 /* System sound from Control Panel (deprecated feature).
\r
5700 "$" alone or an unset sound name gets default beep (still in use). */
\r
5701 if (ms->name[1]) {
\r
5702 ok = PlaySound(ms->name + 1, NULL, SND_ALIAS|SND_ASYNC);
\r
5704 if (!ok) ok = MessageBeep(MB_OK);
\r
5707 /* Builtin wave resource, or "!" alone for silence */
\r
5708 if (ms->name[1]) {
\r
5709 if (ms->data == NULL) return FALSE;
\r
5710 ok = PlaySound(ms->data, NULL, SND_MEMORY|SND_ASYNC);
\r
5716 /* .wav file. Error if not found. */
\r
5717 if (ms->data == NULL) return FALSE;
\r
5718 ok = PlaySound(ms->data, NULL, SND_MEMORY|SND_ASYNC);
\r
5721 /* Don't print an error: this can happen innocently if the sound driver
\r
5722 is busy; for instance, if another instance of WinBoard is playing
\r
5723 a sound at about the same time. */
\r
5729 OldOpenFileHook(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
5732 OPENFILENAME *ofn;
\r
5733 static UINT *number; /* gross that this is static */
\r
5735 switch (message) {
\r
5736 case WM_INITDIALOG: /* message: initialize dialog box */
\r
5737 /* Center the dialog over the application window */
\r
5738 ofn = (OPENFILENAME *) lParam;
\r
5739 if (ofn->Flags & OFN_ENABLETEMPLATE) {
\r
5740 number = (UINT *) ofn->lCustData;
\r
5741 SendMessage(GetDlgItem(hDlg, edt2), WM_SETTEXT, 0, (LPARAM) "");
\r
5745 CenterWindow (hDlg, GetWindow (hDlg, GW_OWNER));
\r
5746 Translate(hDlg, 1536);
\r
5747 return FALSE; /* Allow for further processing */
\r
5750 if ((LOWORD(wParam) == IDOK) && (number != NULL)) {
\r
5751 *number = GetDlgItemInt(hDlg, OPT_IndexNumberOld, &ok, FALSE);
\r
5753 return FALSE; /* Allow for further processing */
\r
5759 OpenFileHook(HWND hdlg, UINT uiMsg, WPARAM wParam, LPARAM lParam)
\r
5761 static UINT *number;
\r
5762 OPENFILENAME *ofname;
\r
5765 case WM_INITDIALOG:
\r
5766 Translate(hdlg, DLG_IndexNumber);
\r
5767 ofname = (OPENFILENAME *)lParam;
\r
5768 number = (UINT *)(ofname->lCustData);
\r
5771 ofnot = (OFNOTIFY *)lParam;
\r
5772 if (ofnot->hdr.code == CDN_FILEOK) {
\r
5773 *number = GetDlgItemInt(hdlg, OPT_IndexNumber, NULL, FALSE);
\r
5782 OpenFileDialog(HWND hwnd, char *write, char *defName, char *defExt, // [HGM] diag: type of 'write' now string
\r
5783 char *nameFilt, char *dlgTitle, UINT *number,
\r
5784 char fileTitle[MSG_SIZ], char fileName[MSG_SIZ])
\r
5786 OPENFILENAME openFileName;
\r
5787 char buf1[MSG_SIZ];
\r
5790 if (fileName == NULL) fileName = buf1;
\r
5791 if (defName == NULL) {
\r
5792 safeStrCpy(fileName, "*.", 3 );
\r
5793 strcat(fileName, defExt);
\r
5795 safeStrCpy(fileName, defName, MSG_SIZ );
\r
5797 if (fileTitle) safeStrCpy(fileTitle, "", MSG_SIZ );
\r
5798 if (number) *number = 0;
\r
5800 openFileName.lStructSize = sizeof(OPENFILENAME);
\r
5801 openFileName.hwndOwner = hwnd;
\r
5802 openFileName.hInstance = (HANDLE) hInst;
\r
5803 openFileName.lpstrFilter = nameFilt;
\r
5804 openFileName.lpstrCustomFilter = (LPSTR) NULL;
\r
5805 openFileName.nMaxCustFilter = 0L;
\r
5806 openFileName.nFilterIndex = 1L;
\r
5807 openFileName.lpstrFile = fileName;
\r
5808 openFileName.nMaxFile = MSG_SIZ;
\r
5809 openFileName.lpstrFileTitle = fileTitle;
\r
5810 openFileName.nMaxFileTitle = fileTitle ? MSG_SIZ : 0;
\r
5811 openFileName.lpstrInitialDir = NULL;
\r
5812 openFileName.lpstrTitle = dlgTitle;
\r
5813 openFileName.Flags = OFN_PATHMUSTEXIST | OFN_HIDEREADONLY
\r
5814 | (write[0] != 'r' ? 0 : OFN_FILEMUSTEXIST)
\r
5815 | (number ? OFN_ENABLETEMPLATE | OFN_ENABLEHOOK: 0)
\r
5816 | (oldDialog ? 0 : OFN_EXPLORER);
\r
5817 openFileName.nFileOffset = 0;
\r
5818 openFileName.nFileExtension = 0;
\r
5819 openFileName.lpstrDefExt = defExt;
\r
5820 openFileName.lCustData = (LONG) number;
\r
5821 openFileName.lpfnHook = oldDialog ?
\r
5822 (LPOFNHOOKPROC) OldOpenFileHook : (LPOFNHOOKPROC) OpenFileHook;
\r
5823 openFileName.lpTemplateName = (LPSTR)(oldDialog ? 1536 : DLG_IndexNumber);
\r
5825 if (write[0] != 'r' ? GetSaveFileName(&openFileName) :
\r
5826 GetOpenFileName(&openFileName)) {
\r
5827 /* open the file */
\r
5828 f = fopen(openFileName.lpstrFile, write);
\r
5830 MessageBox(hwnd, _("File open failed"), NULL,
\r
5831 MB_OK|MB_ICONEXCLAMATION);
\r
5835 int err = CommDlgExtendedError();
\r
5836 if (err != 0) DisplayError(_("Internal error in file dialog box"), err);
\r
5845 MenuPopup(HWND hwnd, POINT pt, HMENU hmenu, UINT def)
\r
5847 HMENU hmenuTrackPopup; /* floating pop-up menu */
\r
5850 * Get the first pop-up menu in the menu template. This is the
\r
5851 * menu that TrackPopupMenu displays.
\r
5853 hmenuTrackPopup = GetSubMenu(hmenu, 0);
\r
5854 TranslateOneMenu(10, hmenuTrackPopup);
\r
5856 SetMenuDefaultItem(hmenuTrackPopup, def, FALSE);
\r
5859 * TrackPopup uses screen coordinates, so convert the
\r
5860 * coordinates of the mouse click to screen coordinates.
\r
5862 ClientToScreen(hwnd, (LPPOINT) &pt);
\r
5864 /* Draw and track the floating pop-up menu. */
\r
5865 TrackPopupMenu(hmenuTrackPopup, TPM_CENTERALIGN | TPM_RIGHTBUTTON,
\r
5866 pt.x, pt.y, 0, hwnd, NULL);
\r
5868 /* Destroy the menu.*/
\r
5869 DestroyMenu(hmenu);
\r
5874 int sizeX, sizeY, newSizeX, newSizeY;
\r
5876 } ResizeEditPlusButtonsClosure;
\r
5879 ResizeEditPlusButtonsCallback(HWND hChild, LPARAM lparam)
\r
5881 ResizeEditPlusButtonsClosure *cl = (ResizeEditPlusButtonsClosure *)lparam;
\r
5885 if (hChild == cl->hText) return TRUE;
\r
5886 GetWindowRect(hChild, &rect); /* gives screen coords */
\r
5887 pt.x = rect.left + (cl->newSizeX - cl->sizeX)/2;
\r
5888 pt.y = rect.top + cl->newSizeY - cl->sizeY;
\r
5889 ScreenToClient(cl->hDlg, &pt);
\r
5890 cl->hdwp = DeferWindowPos(cl->hdwp, hChild, NULL,
\r
5891 pt.x, pt.y, rect.right - rect.left, rect.bottom - rect.top, SWP_NOZORDER);
\r
5895 /* Resize a dialog that has a (rich) edit field filling most of
\r
5896 the top, with a row of buttons below */
\r
5898 ResizeEditPlusButtons(HWND hDlg, HWND hText, int sizeX, int sizeY, int newSizeX, int newSizeY)
\r
5901 int newTextHeight, newTextWidth;
\r
5902 ResizeEditPlusButtonsClosure cl;
\r
5904 /*if (IsIconic(hDlg)) return;*/
\r
5905 if (newSizeX == sizeX && newSizeY == sizeY) return;
\r
5907 cl.hdwp = BeginDeferWindowPos(8);
\r
5909 GetWindowRect(hText, &rectText); /* gives screen coords */
\r
5910 newTextWidth = rectText.right - rectText.left + newSizeX - sizeX;
\r
5911 newTextHeight = rectText.bottom - rectText.top + newSizeY - sizeY;
\r
5912 if (newTextHeight < 0) {
\r
5913 newSizeY += -newTextHeight;
\r
5914 newTextHeight = 0;
\r
5916 cl.hdwp = DeferWindowPos(cl.hdwp, hText, NULL, 0, 0,
\r
5917 newTextWidth, newTextHeight, SWP_NOZORDER|SWP_NOMOVE);
\r
5923 cl.newSizeX = newSizeX;
\r
5924 cl.newSizeY = newSizeY;
\r
5925 EnumChildWindows(hDlg, ResizeEditPlusButtonsCallback, (LPARAM)&cl);
\r
5927 EndDeferWindowPos(cl.hdwp);
\r
5930 BOOL CenterWindowEx(HWND hwndChild, HWND hwndParent, int mode)
\r
5932 RECT rChild, rParent;
\r
5933 int wChild, hChild, wParent, hParent;
\r
5934 int wScreen, hScreen, xNew, yNew;
\r
5937 /* Get the Height and Width of the child window */
\r
5938 GetWindowRect (hwndChild, &rChild);
\r
5939 wChild = rChild.right - rChild.left;
\r
5940 hChild = rChild.bottom - rChild.top;
\r
5942 /* Get the Height and Width of the parent window */
\r
5943 GetWindowRect (hwndParent, &rParent);
\r
5944 wParent = rParent.right - rParent.left;
\r
5945 hParent = rParent.bottom - rParent.top;
\r
5947 /* Get the display limits */
\r
5948 hdc = GetDC (hwndChild);
\r
5949 wScreen = GetDeviceCaps (hdc, HORZRES);
\r
5950 hScreen = GetDeviceCaps (hdc, VERTRES);
\r
5951 ReleaseDC(hwndChild, hdc);
\r
5953 /* Calculate new X position, then adjust for screen */
\r
5954 xNew = rParent.left + ((wParent - wChild) /2);
\r
5957 } else if ((xNew+wChild) > wScreen) {
\r
5958 xNew = wScreen - wChild;
\r
5961 /* Calculate new Y position, then adjust for screen */
\r
5963 yNew = rParent.top + ((hParent - hChild) /2);
\r
5966 yNew = rParent.top + GetSystemMetrics( SM_CYCAPTION ) * 2 / 3;
\r
5971 } else if ((yNew+hChild) > hScreen) {
\r
5972 yNew = hScreen - hChild;
\r
5975 /* Set it, and return */
\r
5976 return SetWindowPos (hwndChild, NULL,
\r
5977 xNew, yNew, 0, 0, SWP_NOSIZE | SWP_NOZORDER);
\r
5980 /* Center one window over another */
\r
5981 BOOL CenterWindow (HWND hwndChild, HWND hwndParent)
\r
5983 return CenterWindowEx( hwndChild, hwndParent, 0 );
\r
5986 /*---------------------------------------------------------------------------*\
\r
5988 * Startup Dialog functions
\r
5990 \*---------------------------------------------------------------------------*/
\r
5992 InitComboStrings(HANDLE hwndCombo, char **cd)
\r
5994 SendMessage(hwndCombo, CB_RESETCONTENT, 0, 0);
\r
5996 while (*cd != NULL) {
\r
5997 SendMessage(hwndCombo, CB_ADDSTRING, 0, (LPARAM) T_(*cd));
\r
6003 InitComboStringsFromOption(HANDLE hwndCombo, char *str)
\r
6005 char buf1[MAX_ARG_LEN];
\r
6008 if (str[0] == '@') {
\r
6009 FILE* f = fopen(str + 1, "r");
\r
6011 DisplayFatalError(str + 1, errno, 2);
\r
6014 len = fread(buf1, 1, sizeof(buf1)-1, f);
\r
6016 buf1[len] = NULLCHAR;
\r
6020 SendMessage(hwndCombo, CB_RESETCONTENT, 0, 0);
\r
6023 char buf[MSG_SIZ];
\r
6024 char *end = strchr(str, '\n');
\r
6025 if (end == NULL) return;
\r
6026 memcpy(buf, str, end - str);
\r
6027 buf[end - str] = NULLCHAR;
\r
6028 SendMessage(hwndCombo, CB_ADDSTRING, 0, (LPARAM) buf);
\r
6034 SetStartupDialogEnables(HWND hDlg)
\r
6036 EnableWindow(GetDlgItem(hDlg, OPT_ChessEngineName),
\r
6037 IsDlgButtonChecked(hDlg, OPT_ChessEngine) ||
\r
6038 (appData.zippyPlay && IsDlgButtonChecked(hDlg, OPT_ChessServer)));
\r
6039 EnableWindow(GetDlgItem(hDlg, OPT_SecondChessEngineName),
\r
6040 IsDlgButtonChecked(hDlg, OPT_ChessEngine));
\r
6041 EnableWindow(GetDlgItem(hDlg, OPT_ChessServerName),
\r
6042 IsDlgButtonChecked(hDlg, OPT_ChessServer));
\r
6043 EnableWindow(GetDlgItem(hDlg, OPT_AdditionalOptions),
\r
6044 IsDlgButtonChecked(hDlg, OPT_AnyAdditional));
\r
6045 EnableWindow(GetDlgItem(hDlg, IDOK),
\r
6046 IsDlgButtonChecked(hDlg, OPT_ChessEngine) ||
\r
6047 IsDlgButtonChecked(hDlg, OPT_ChessServer) ||
\r
6048 IsDlgButtonChecked(hDlg, OPT_View));
\r
6052 QuoteForFilename(char *filename)
\r
6054 int dquote, space;
\r
6055 dquote = strchr(filename, '"') != NULL;
\r
6056 space = strchr(filename, ' ') != NULL;
\r
6057 if (dquote || space) {
\r
6069 InitEngineBox(HWND hDlg, HWND hwndCombo, char* nthcp, char* nthd, char* nthdir, char *nthnames)
\r
6071 char buf[MSG_SIZ];
\r
6074 InitComboStringsFromOption(hwndCombo, nthnames);
\r
6075 q = QuoteForFilename(nthcp);
\r
6076 snprintf(buf, MSG_SIZ, "%s%s%s", q, nthcp, q);
\r
6077 if (*nthdir != NULLCHAR) {
\r
6078 q = QuoteForFilename(nthdir);
\r
6079 snprintf(buf + strlen(buf), MSG_SIZ, " /%s=%s%s%s", nthd, q, nthdir, q);
\r
6081 if (*nthcp == NULLCHAR) {
\r
6082 SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) 0, (LPARAM) 0);
\r
6083 } else if (SendMessage(hwndCombo, CB_SELECTSTRING, (WPARAM) -1, (LPARAM) buf) == CB_ERR) {
\r
6084 SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) -1, (LPARAM) 0);
\r
6085 SendMessage(hwndCombo, WM_SETTEXT, (WPARAM) 0, (LPARAM) buf);
\r
6090 StartupDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
6092 char buf[MSG_SIZ];
\r
6096 switch (message) {
\r
6097 case WM_INITDIALOG:
\r
6098 /* Center the dialog */
\r
6099 CenterWindow (hDlg, GetDesktopWindow());
\r
6100 Translate(hDlg, DLG_Startup);
\r
6101 /* Initialize the dialog items */
\r
6102 InitEngineBox(hDlg, GetDlgItem(hDlg, OPT_ChessEngineName),
\r
6103 appData.firstChessProgram, "fd", appData.firstDirectory,
\r
6104 firstChessProgramNames);
\r
6105 InitEngineBox(hDlg, GetDlgItem(hDlg, OPT_SecondChessEngineName),
\r
6106 appData.secondChessProgram, singleList ? "fd" : "sd", appData.secondDirectory,
\r
6107 singleList ? firstChessProgramNames : secondChessProgramNames); //[HGM] single: use first list in second combo
\r
6108 hwndCombo = GetDlgItem(hDlg, OPT_ChessServerName);
\r
6109 InitComboStringsFromOption(hwndCombo, icsNames);
\r
6110 snprintf(buf, MSG_SIZ, "%s /icsport=%s", appData.icsHost, appData.icsPort);
\r
6111 if (*appData.icsHelper != NULLCHAR) {
\r
6112 char *q = QuoteForFilename(appData.icsHelper);
\r
6113 sprintf(buf + strlen(buf), " /icshelper=%s%s%s", q, appData.icsHelper, q);
\r
6115 if (*appData.icsHost == NULLCHAR) {
\r
6116 SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) 0, (LPARAM) 0);
\r
6117 /*SendMessage(hwndCombo, CB_SHOWDROPDOWN, (WPARAM) TRUE, (LPARAM) 0); !!too soon */
\r
6118 } else if (SendMessage(hwndCombo, CB_SELECTSTRING, (WPARAM) -1, (LPARAM) buf) == CB_ERR) {
\r
6119 SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) -1, (LPARAM) 0);
\r
6120 SendMessage(hwndCombo, WM_SETTEXT, (WPARAM) 0, (LPARAM) buf);
\r
6123 if (appData.icsActive) {
\r
6124 CheckDlgButton(hDlg, OPT_ChessServer, BST_CHECKED);
\r
6126 else if (appData.noChessProgram) {
\r
6127 CheckDlgButton(hDlg, OPT_View, BST_CHECKED);
\r
6130 CheckDlgButton(hDlg, OPT_ChessEngine, BST_CHECKED);
\r
6133 SetStartupDialogEnables(hDlg);
\r
6137 switch (LOWORD(wParam)) {
\r
6139 if (IsDlgButtonChecked(hDlg, OPT_ChessEngine)) {
\r
6140 safeStrCpy(buf, "/fcp=", sizeof(buf)/sizeof(buf[0]) );
\r
6141 GetDlgItemText(hDlg, OPT_ChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));
\r
6143 ParseArgs(StringGet, &p);
\r
6144 safeStrCpy(buf, singleList ? "/fcp=" : "/scp=", sizeof(buf)/sizeof(buf[0]) );
\r
6145 GetDlgItemText(hDlg, OPT_SecondChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));
\r
6147 SwapEngines(singleList); // temporarily swap first and second, to load a second 'first', ...
\r
6148 ParseArgs(StringGet, &p);
\r
6149 SwapEngines(singleList); // ... and then make it 'second'
\r
6150 appData.noChessProgram = FALSE;
\r
6151 appData.icsActive = FALSE;
\r
6152 } else if (IsDlgButtonChecked(hDlg, OPT_ChessServer)) {
\r
6153 safeStrCpy(buf, "/ics /icshost=", sizeof(buf)/sizeof(buf[0]) );
\r
6154 GetDlgItemText(hDlg, OPT_ChessServerName, buf + strlen(buf), sizeof(buf) - strlen(buf));
\r
6156 ParseArgs(StringGet, &p);
\r
6157 if (appData.zippyPlay) {
\r
6158 safeStrCpy(buf, "/fcp=", sizeof(buf)/sizeof(buf[0]) );
\r
6159 GetDlgItemText(hDlg, OPT_ChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));
\r
6161 ParseArgs(StringGet, &p);
\r
6163 } else if (IsDlgButtonChecked(hDlg, OPT_View)) {
\r
6164 appData.noChessProgram = TRUE;
\r
6165 appData.icsActive = FALSE;
\r
6167 MessageBox(hDlg, _("Choose an option, or cancel to exit"),
\r
6168 _("Option Error"), MB_OK|MB_ICONEXCLAMATION);
\r
6171 if (IsDlgButtonChecked(hDlg, OPT_AnyAdditional)) {
\r
6172 GetDlgItemText(hDlg, OPT_AdditionalOptions, buf, sizeof(buf));
\r
6174 ParseArgs(StringGet, &p);
\r
6176 EndDialog(hDlg, TRUE);
\r
6183 case IDM_HELPCONTENTS:
\r
6184 if (!WinHelp (hDlg, "winboard.hlp", HELP_KEY,(DWORD)(LPSTR)"CONTENTS")) {
\r
6185 MessageBox (GetFocus(),
\r
6186 _("Unable to activate help"),
\r
6187 szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);
\r
6192 SetStartupDialogEnables(hDlg);
\r
6200 /*---------------------------------------------------------------------------*\
\r
6202 * About box dialog functions
\r
6204 \*---------------------------------------------------------------------------*/
\r
6206 /* Process messages for "About" dialog box */
\r
6208 About(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
6210 switch (message) {
\r
6211 case WM_INITDIALOG: /* message: initialize dialog box */
\r
6212 /* Center the dialog over the application window */
\r
6213 CenterWindow (hDlg, GetWindow (hDlg, GW_OWNER));
\r
6214 SetDlgItemText(hDlg, ABOUTBOX_Version, programVersion);
\r
6215 Translate(hDlg, ABOUTBOX);
\r
6219 case WM_COMMAND: /* message: received a command */
\r
6220 if (LOWORD(wParam) == IDOK /* "OK" box selected? */
\r
6221 || LOWORD(wParam) == IDCANCEL) { /* System menu close command? */
\r
6222 EndDialog(hDlg, TRUE); /* Exit the dialog */
\r
6230 /*---------------------------------------------------------------------------*\
\r
6232 * Comment Dialog functions
\r
6234 \*---------------------------------------------------------------------------*/
\r
6237 CommentDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
6239 static HANDLE hwndText = NULL;
\r
6240 int len, newSizeX, newSizeY, flags;
\r
6241 static int sizeX, sizeY;
\r
6246 switch (message) {
\r
6247 case WM_INITDIALOG: /* message: initialize dialog box */
\r
6248 /* Initialize the dialog items */
\r
6249 Translate(hDlg, DLG_EditComment);
\r
6250 hwndText = GetDlgItem(hDlg, OPT_CommentText);
\r
6251 SetDlgItemText(hDlg, OPT_CommentText, commentText);
\r
6252 EnableWindow(GetDlgItem(hDlg, OPT_CancelComment), editComment);
\r
6253 EnableWindow(GetDlgItem(hDlg, OPT_ClearComment), editComment);
\r
6254 EnableWindow(GetDlgItem(hDlg, OPT_EditComment), !editComment);
\r
6255 SendMessage(hwndText, EM_SETREADONLY, !editComment, 0);
\r
6256 SetWindowText(hDlg, commentTitle);
\r
6257 if (editComment) {
\r
6258 SetFocus(hwndText);
\r
6260 SetFocus(GetDlgItem(hDlg, IDOK));
\r
6262 SendMessage(GetDlgItem(hDlg, OPT_CommentText),
\r
6263 WM_SETFONT, (WPARAM)font[boardSize][COMMENT_FONT]->hf,
\r
6264 MAKELPARAM(FALSE, 0));
\r
6265 /* Size and position the dialog */
\r
6266 if (!commentDialog) {
\r
6267 commentDialog = hDlg;
\r
6268 flags = SWP_NOZORDER;
\r
6269 GetClientRect(hDlg, &rect);
\r
6270 sizeX = rect.right;
\r
6271 sizeY = rect.bottom;
\r
6272 if (wpComment.x != CW_USEDEFAULT && wpComment.y != CW_USEDEFAULT &&
\r
6273 wpComment.width != CW_USEDEFAULT && wpComment.height != CW_USEDEFAULT) {
\r
6274 WINDOWPLACEMENT wp;
\r
6275 EnsureOnScreen(&wpComment.x, &wpComment.y, 0, 0);
\r
6276 wp.length = sizeof(WINDOWPLACEMENT);
\r
6278 wp.showCmd = SW_SHOW;
\r
6279 wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;
\r
6280 wp.rcNormalPosition.left = wpComment.x;
\r
6281 wp.rcNormalPosition.right = wpComment.x + wpComment.width;
\r
6282 wp.rcNormalPosition.top = wpComment.y;
\r
6283 wp.rcNormalPosition.bottom = wpComment.y + wpComment.height;
\r
6284 SetWindowPlacement(hDlg, &wp);
\r
6286 GetClientRect(hDlg, &rect);
\r
6287 newSizeX = rect.right;
\r
6288 newSizeY = rect.bottom;
\r
6289 ResizeEditPlusButtons(hDlg, hwndText, sizeX, sizeY,
\r
6290 newSizeX, newSizeY);
\r
6295 SendDlgItemMessage( hDlg, OPT_CommentText, EM_SETEVENTMASK, 0, ENM_MOUSEEVENTS | ENM_KEYEVENTS );
\r
6298 case WM_COMMAND: /* message: received a command */
\r
6299 switch (LOWORD(wParam)) {
\r
6301 if (editComment) {
\r
6303 /* Read changed options from the dialog box */
\r
6304 hwndText = GetDlgItem(hDlg, OPT_CommentText);
\r
6305 len = GetWindowTextLength(hwndText);
\r
6306 str = (char *) malloc(len + 1);
\r
6307 GetWindowText(hwndText, str, len + 1);
\r
6316 ReplaceComment(commentIndex, str);
\r
6323 case OPT_CancelComment:
\r
6327 case OPT_ClearComment:
\r
6328 SetDlgItemText(hDlg, OPT_CommentText, "");
\r
6331 case OPT_EditComment:
\r
6332 EditCommentEvent();
\r
6340 case WM_NOTIFY: // [HGM] vari: cloned from whistory.c
\r
6341 if( wParam == OPT_CommentText ) {
\r
6342 MSGFILTER * lpMF = (MSGFILTER *) lParam;
\r
6344 if( lpMF->msg == WM_RBUTTONDOWN && (lpMF->wParam & (MK_CONTROL | MK_SHIFT)) == 0 ||
\r
6345 lpMF->msg == WM_CHAR && lpMF->wParam == '\022' ) {
\r
6349 pt.x = LOWORD( lpMF->lParam );
\r
6350 pt.y = HIWORD( lpMF->lParam );
\r
6352 if(lpMF->msg == WM_CHAR) {
\r
6354 SendDlgItemMessage( hDlg, OPT_CommentText, EM_EXGETSEL, 0, (LPARAM) &sel );
\r
6355 index = sel.cpMin;
\r
6357 index = SendDlgItemMessage( hDlg, OPT_CommentText, EM_CHARFROMPOS, 0, (LPARAM) &pt );
\r
6359 hwndText = GetDlgItem(hDlg, OPT_CommentText); // cloned from above
\r
6360 len = GetWindowTextLength(hwndText);
\r
6361 str = (char *) malloc(len + 1);
\r
6362 GetWindowText(hwndText, str, len + 1);
\r
6363 ReplaceComment(commentIndex, str);
\r
6364 if(commentIndex != currentMove) ToNrEvent(commentIndex);
\r
6365 LoadVariation( index, str ); // [HGM] also does the actual moving to it, now
\r
6368 /* Zap the message for good: apparently, returning non-zero is not enough */
\r
6369 lpMF->msg = WM_USER;
\r
6377 newSizeX = LOWORD(lParam);
\r
6378 newSizeY = HIWORD(lParam);
\r
6379 ResizeEditPlusButtons(hDlg, hwndText, sizeX, sizeY, newSizeX, newSizeY);
\r
6384 case WM_GETMINMAXINFO:
\r
6385 /* Prevent resizing window too small */
\r
6386 mmi = (MINMAXINFO *) lParam;
\r
6387 mmi->ptMinTrackSize.x = 100;
\r
6388 mmi->ptMinTrackSize.y = 100;
\r
6395 EitherCommentPopUp(int index, char *title, char *str, BOOLEAN edit)
\r
6400 CheckMenuItem(GetMenu(hwndMain), IDM_Comment, edit ? MF_CHECKED : MF_UNCHECKED);
\r
6402 if (str == NULL) str = "";
\r
6403 p = (char *) malloc(2 * strlen(str) + 2);
\r
6406 if (*str == '\n') *q++ = '\r';
\r
6410 if (commentText != NULL) free(commentText);
\r
6412 commentIndex = index;
\r
6413 commentTitle = title;
\r
6415 editComment = edit;
\r
6417 if (commentDialog) {
\r
6418 SendMessage(commentDialog, WM_INITDIALOG, 0, 0);
\r
6419 if (!commentUp) ShowWindow(commentDialog, SW_SHOW);
\r
6421 lpProc = MakeProcInstance((FARPROC)CommentDialog, hInst);
\r
6422 CreateDialog(hInst, MAKEINTRESOURCE(DLG_EditComment),
\r
6423 hwndMain, (DLGPROC)lpProc);
\r
6424 FreeProcInstance(lpProc);
\r
6430 /*---------------------------------------------------------------------------*\
\r
6432 * Type-in move dialog functions
\r
6434 \*---------------------------------------------------------------------------*/
\r
6437 TypeInMoveDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
6439 char move[MSG_SIZ];
\r
6442 switch (message) {
\r
6443 case WM_INITDIALOG:
\r
6444 move[0] = (char) lParam;
\r
6445 move[1] = NULLCHAR;
\r
6446 CenterWindowEx(hDlg, GetWindow(hDlg, GW_OWNER), 1 );
\r
6447 Translate(hDlg, DLG_TypeInMove);
\r
6448 hInput = GetDlgItem(hDlg, OPT_Move);
\r
6449 SetWindowText(hInput, move);
\r
6451 SendMessage(hInput, EM_SETSEL, (WPARAM)9999, (LPARAM)9999);
\r
6455 switch (LOWORD(wParam)) {
\r
6458 shiftKey = GetKeyState(VK_SHIFT) < 0; // [HGM] remember last shift status
\r
6459 GetDlgItemText(hDlg, OPT_Move, move, sizeof(move));
6460 TypeInDoneEvent(move);
\r
6461 EndDialog(hDlg, TRUE);
\r
6464 EndDialog(hDlg, FALSE);
\r
6475 PopUpMoveDialog(char firstchar)
\r
6479 lpProc = MakeProcInstance((FARPROC)TypeInMoveDialog, hInst);
\r
6480 DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_TypeInMove),
\r
6481 hwndMain, (DLGPROC)lpProc, (LPARAM)firstchar);
\r
6482 FreeProcInstance(lpProc);
\r
6485 /*---------------------------------------------------------------------------*\
\r
6487 * Type-in name dialog functions
\r
6489 \*---------------------------------------------------------------------------*/
\r
6492 TypeInNameDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
6494 char move[MSG_SIZ];
\r
6497 switch (message) {
\r
6498 case WM_INITDIALOG:
\r
6499 move[0] = (char) lParam;
\r
6500 move[1] = NULLCHAR;
\r
6501 CenterWindowEx(hDlg, GetWindow(hDlg, GW_OWNER), 1 );
\r
6502 Translate(hDlg, DLG_TypeInName);
\r
6503 hInput = GetDlgItem(hDlg, OPT_Name);
\r
6504 SetWindowText(hInput, move);
\r
6506 SendMessage(hInput, EM_SETSEL, (WPARAM)9999, (LPARAM)9999);
\r
6510 switch (LOWORD(wParam)) {
\r
6512 GetDlgItemText(hDlg, OPT_Name, move, sizeof(move));
\r
6513 appData.userName = strdup(move);
\r
6516 if(gameMode == MachinePlaysWhite || gameMode == MachinePlaysBlack) {
\r
6517 snprintf(move, MSG_SIZ, "%s vs. %s", gameInfo.white, gameInfo.black);
\r
6518 DisplayTitle(move);
\r
6522 EndDialog(hDlg, TRUE);
\r
6525 EndDialog(hDlg, FALSE);
\r
6536 PopUpNameDialog(char firstchar)
\r
6540 lpProc = MakeProcInstance((FARPROC)TypeInNameDialog, hInst);
\r
6541 DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_TypeInName),
\r
6542 hwndMain, (DLGPROC)lpProc, (LPARAM)firstchar);
\r
6543 FreeProcInstance(lpProc);
\r
6546 /*---------------------------------------------------------------------------*\
\r
6550 \*---------------------------------------------------------------------------*/
\r
6552 /* Nonmodal error box */
\r
6553 LRESULT CALLBACK ErrorDialog(HWND hDlg, UINT message,
\r
6554 WPARAM wParam, LPARAM lParam);
\r
6557 ErrorPopUp(char *title, char *content)
\r
6561 BOOLEAN modal = hwndMain == NULL;
\r
6579 strncpy(errorTitle, title, sizeof(errorTitle));
\r
6580 errorTitle[sizeof(errorTitle) - 1] = '\0';
\r
6583 MessageBox(NULL, errorMessage, errorTitle, MB_OK|MB_ICONEXCLAMATION);
\r
6585 lpProc = MakeProcInstance((FARPROC)ErrorDialog, hInst);
\r
6586 CreateDialog(hInst, MAKEINTRESOURCE(DLG_Error),
\r
6587 hwndMain, (DLGPROC)lpProc);
\r
6588 FreeProcInstance(lpProc);
\r
6595 if (!appData.popupMoveErrors && moveErrorMessageUp) DisplayMessage("", "");
\r
6596 if (errorDialog == NULL) return;
\r
6597 DestroyWindow(errorDialog);
\r
6598 errorDialog = NULL;
\r
6599 if(errorExitStatus) ExitEvent(errorExitStatus);
\r
6603 ErrorDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
6608 switch (message) {
\r
6609 case WM_INITDIALOG:
\r
6610 GetWindowRect(hDlg, &rChild);
\r
6613 SetWindowPos(hDlg, NULL, rChild.left,
\r
6614 rChild.top + boardRect.top - (rChild.bottom - rChild.top),
\r
6615 0, 0, SWP_NOZORDER|SWP_NOSIZE);
\r
6619 [AS] It seems that the above code wants to move the dialog up in the "caption
\r
6620 area" of the main window, but it uses the dialog height as an hard-coded constant,
\r
6621 and it doesn't work when you resize the dialog.
\r
6622 For now, just give it a default position.
\r
6624 SetWindowPos(hDlg, NULL, boardRect.left+8, boardRect.top+8, 0, 0, SWP_NOZORDER|SWP_NOSIZE);
\r
6625 Translate(hDlg, DLG_Error);
\r
6627 errorDialog = hDlg;
\r
6628 SetWindowText(hDlg, errorTitle);
\r
6629 hwndText = GetDlgItem(hDlg, OPT_ErrorText);
\r
6630 SetDlgItemText(hDlg, OPT_ErrorText, errorMessage);
\r
6634 switch (LOWORD(wParam)) {
\r
6637 if (errorDialog == hDlg) errorDialog = NULL;
\r
6638 DestroyWindow(hDlg);
\r
6650 HWND gothicDialog = NULL;
\r
6653 GothicDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
6657 int height = GetSystemMetrics(SM_CYCAPTION)+GetSystemMetrics(SM_CYFRAME);
\r
6659 switch (message) {
\r
6660 case WM_INITDIALOG:
\r
6661 GetWindowRect(hDlg, &rChild);
\r
6663 SetWindowPos(hDlg, NULL, wpMain.x, wpMain.y-height, wpMain.width, height,
\r
6667 [AS] It seems that the above code wants to move the dialog up in the "caption
\r
6668 area" of the main window, but it uses the dialog height as an hard-coded constant,
\r
6669 and it doesn't work when you resize the dialog.
\r
6670 For now, just give it a default position.
\r
6672 gothicDialog = hDlg;
\r
6673 SetWindowText(hDlg, errorTitle);
\r
6674 hwndText = GetDlgItem(hDlg, OPT_ErrorText);
\r
6675 SetDlgItemText(hDlg, OPT_ErrorText, errorMessage);
\r
6679 switch (LOWORD(wParam)) {
\r
6682 if (errorDialog == hDlg) errorDialog = NULL;
\r
6683 DestroyWindow(hDlg);
\r
6695 GothicPopUp(char *title, VariantClass variant)
\r
6698 static char *lastTitle;
\r
6700 strncpy(errorTitle, title, sizeof(errorTitle));
\r
6701 errorTitle[sizeof(errorTitle) - 1] = '\0';
\r
6703 if(lastTitle != title && gothicDialog != NULL) {
\r
6704 DestroyWindow(gothicDialog);
\r
6705 gothicDialog = NULL;
\r
6707 if(variant != VariantNormal && gothicDialog == NULL) {
\r
6708 title = lastTitle;
\r
6709 lpProc = MakeProcInstance((FARPROC)GothicDialog, hInst);
\r
6710 CreateDialog(hInst, MAKEINTRESOURCE(DLG_Error),
\r
6711 hwndMain, (DLGPROC)lpProc);
\r
6712 FreeProcInstance(lpProc);
\r
6717 /*---------------------------------------------------------------------------*\
\r
6719 * Ics Interaction console functions
\r
6721 \*---------------------------------------------------------------------------*/
\r
6723 #define HISTORY_SIZE 64
\r
6724 static char *history[HISTORY_SIZE];
\r
6725 int histIn = 0, histP = 0;
\r
6728 SaveInHistory(char *cmd)
\r
6730 if (history[histIn] != NULL) {
\r
6731 free(history[histIn]);
\r
6732 history[histIn] = NULL;
\r
6734 if (*cmd == NULLCHAR) return;
\r
6735 history[histIn] = StrSave(cmd);
\r
6736 histIn = (histIn + 1) % HISTORY_SIZE;
\r
6737 if (history[histIn] != NULL) {
\r
6738 free(history[histIn]);
\r
6739 history[histIn] = NULL;
\r
6745 PrevInHistory(char *cmd)
\r
6748 if (histP == histIn) {
\r
6749 if (history[histIn] != NULL) free(history[histIn]);
\r
6750 history[histIn] = StrSave(cmd);
\r
6752 newhp = (histP - 1 + HISTORY_SIZE) % HISTORY_SIZE;
\r
6753 if (newhp == histIn || history[newhp] == NULL) return NULL;
\r
6755 return history[histP];
\r
6761 if (histP == histIn) return NULL;
\r
6762 histP = (histP + 1) % HISTORY_SIZE;
\r
6763 return history[histP];
\r
6767 LoadIcsTextMenu(IcsTextMenuEntry *e)
\r
6771 hmenu = LoadMenu(hInst, "TextMenu");
\r
6772 h = GetSubMenu(hmenu, 0);
\r
6774 if (strcmp(e->item, "-") == 0) {
\r
6775 AppendMenu(h, MF_SEPARATOR, 0, 0);
\r
6776 } else { // [HGM] re-written a bit to use only one AppendMenu call for both cases (| or no |)
\r
6777 int flags = MF_STRING, j = 0;
\r
6778 if (e->item[0] == '|') {
\r
6779 flags |= MF_MENUBARBREAK;
\r
6782 if(!strcmp(e->command, "none")) flags |= MF_GRAYED; // [HGM] chatclick: provide inactive dummy
\r
6783 AppendMenu(h, flags, IDM_CommandX + i, e->item + j);
\r
6791 WNDPROC consoleTextWindowProc;
\r
6794 CommandX(HWND hwnd, char *command, BOOLEAN getname, BOOLEAN immediate)
\r
6796 char buf[MSG_SIZ], name[MSG_SIZ];
\r
6797 HWND hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
6801 SetWindowText(hInput, command);
\r
6803 SendMessage(hInput, WM_CHAR, '\r', 0);
\r
6805 sel.cpMin = 999999;
\r
6806 sel.cpMax = 999999;
\r
6807 SendMessage(hInput, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
6812 SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);
\r
6813 if (sel.cpMin == sel.cpMax) {
\r
6814 /* Expand to surrounding word */
\r
6817 tr.chrg.cpMax = sel.cpMin;
\r
6818 tr.chrg.cpMin = --sel.cpMin;
\r
6819 if (sel.cpMin < 0) break;
\r
6820 tr.lpstrText = name;
\r
6821 SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr);
\r
6822 } while (isalpha(name[0]) || isdigit(name[0]) || name[0] == '-');
\r
6826 tr.chrg.cpMin = sel.cpMax;
\r
6827 tr.chrg.cpMax = ++sel.cpMax;
\r
6828 tr.lpstrText = name;
\r
6829 if (SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr) < 1) break;
\r
6830 } while (isalpha(name[0]) || isdigit(name[0]) || name[0] == '-');
\r
6833 if (sel.cpMax == sel.cpMin || sel.cpMax - sel.cpMin > MSG_SIZ/2) {
\r
6834 MessageBeep(MB_ICONEXCLAMATION);
\r
6838 tr.lpstrText = name;
\r
6839 SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr);
\r
6841 if (sel.cpMax - sel.cpMin > MSG_SIZ/2) {
\r
6842 MessageBeep(MB_ICONEXCLAMATION);
\r
6845 SendMessage(hwnd, EM_GETSELTEXT, 0, (LPARAM) name);
\r
6848 if(strstr(command, "%s")) snprintf(buf, MSG_SIZ, command, name); else
\r
6849 snprintf(buf, MSG_SIZ, "%s %s", command, name);
\r
6850 SetWindowText(hInput, buf);
\r
6851 SendMessage(hInput, WM_CHAR, '\r', 0);
\r
6853 if(!strcmp(command, "chat")) { ChatPopUp(name); return; }
\r
6854 snprintf(buf, MSG_SIZ, "%s %s ", command, name); /* trailing space */
\r
6855 SetWindowText(hInput, buf);
\r
6856 sel.cpMin = 999999;
\r
6857 sel.cpMax = 999999;
\r
6858 SendMessage(hInput, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
6864 ConsoleTextSubclass(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
\r
6869 switch (message) {
\r
6871 if (!(GetKeyState(VK_CONTROL) & ~1)) break;
\r
6872 if(wParam=='R') return 0;
\r
6875 SendMessage(hwnd, EM_LINESCROLL, 0, -999999);
\r
6878 sel.cpMin = 999999;
\r
6879 sel.cpMax = 999999;
\r
6880 SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
6881 SendMessage(hwnd, EM_SCROLLCARET, 0, 0);
\r
6886 if(wParam != '\022') {
\r
6887 if (wParam == '\t') {
\r
6888 if (GetKeyState(VK_SHIFT) < 0) {
\r
6890 if (IsIconic(hwndMain)) ShowWindow(hwndMain, SW_RESTORE);
\r
6891 if (buttonDesc[0].hwnd) {
\r
6892 SetFocus(buttonDesc[0].hwnd);
\r
6894 SetFocus(hwndMain);
\r
6898 SetFocus(GetDlgItem(hwndConsole, OPT_ConsoleInput));
\r
6901 hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
6902 JAWS_DELETE( SetFocus(hInput); )
\r
6903 SendMessage(hInput, message, wParam, lParam);
\r
6906 } // [HGM] navigate: for Ctrl+R, flow into next case (moved up here) to summon up menu
\r
6908 case WM_RBUTTONDOWN:
\r
6909 if (!(GetKeyState(VK_SHIFT) & ~1)) {
\r
6910 /* Move selection here if it was empty */
\r
6912 pt.x = LOWORD(lParam);
\r
6913 pt.y = HIWORD(lParam);
\r
6914 SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);
\r
6915 if (sel.cpMin == sel.cpMax) {
\r
6916 if(lParam != -1) sel.cpMin = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&pt); /*doc is wrong*/
\r
6917 sel.cpMax = sel.cpMin;
\r
6918 SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
6920 SendMessage(hwnd, EM_HIDESELECTION, FALSE, FALSE);
\r
6921 { // [HGM] chatclick: code moved here from WM_RBUTTONUP case, to have menu appear on down-click
\r
6923 HMENU hmenu = LoadIcsTextMenu(icsTextMenuEntry);
\r
6924 SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);
\r
6925 if (sel.cpMin == sel.cpMax) {
\r
6926 EnableMenuItem(hmenu, IDM_Copy, MF_BYCOMMAND|MF_GRAYED);
\r
6927 EnableMenuItem(hmenu, IDM_QuickPaste, MF_BYCOMMAND|MF_GRAYED);
\r
6929 if (!IsClipboardFormatAvailable(CF_TEXT)) {
\r
6930 EnableMenuItem(hmenu, IDM_Paste, MF_BYCOMMAND|MF_GRAYED);
\r
6932 pt.x = LOWORD(lParam)-30; // [HGM] chatclick: make menu pop up with pointer above upper-right item
\r
6933 pt.y = HIWORD(lParam)-10; // make it appear as if mouse moved there, so it will be selected on up-click
\r
6934 PostMessage(hwnd, WM_MOUSEMOVE, wParam, lParam+5);
\r
6935 MenuPopup(hwnd, pt, hmenu, -1);
\r
6939 case WM_RBUTTONUP:
\r
6940 if (GetKeyState(VK_SHIFT) & ~1) {
\r
6941 SendDlgItemMessage(hwndConsole, OPT_ConsoleText,
\r
6942 WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);
\r
6946 hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
6948 return SendMessage(hInput, message, wParam, lParam);
\r
6949 case WM_MBUTTONDOWN:
\r
6950 return SendMessage(hwnd, WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);
\r
6952 switch (LOWORD(wParam)) {
\r
6953 case IDM_QuickPaste:
\r
6955 SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);
\r
6956 if (sel.cpMin == sel.cpMax) {
\r
6957 MessageBeep(MB_ICONEXCLAMATION);
\r
6960 SendMessage(hwnd, WM_COPY, 0, 0);
\r
6961 hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
6962 SendMessage(hInput, WM_PASTE, 0, 0);
\r
6967 SendMessage(hwnd, WM_CUT, 0, 0);
\r
6970 SendMessage(hwnd, WM_PASTE, 0, 0);
\r
6973 SendMessage(hwnd, WM_COPY, 0, 0);
\r
6977 int i = LOWORD(wParam) - IDM_CommandX;
\r
6978 if (i >= 0 && i < ICS_TEXT_MENU_SIZE &&
\r
6979 icsTextMenuEntry[i].command != NULL) {
\r
6980 CommandX(hwnd, icsTextMenuEntry[i].command,
\r
6981 icsTextMenuEntry[i].getname,
\r
6982 icsTextMenuEntry[i].immediate);
\r
6990 return (*consoleTextWindowProc)(hwnd, message, wParam, lParam);
\r
6993 WNDPROC consoleInputWindowProc;
\r
6996 ConsoleInputSubclass(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
\r
6998 char buf[MSG_SIZ];
\r
7000 static BOOL sendNextChar = FALSE;
\r
7001 static BOOL quoteNextChar = FALSE;
\r
7002 InputSource *is = consoleInputSource;
\r
7006 switch (message) {
\r
7008 if (!appData.localLineEditing || sendNextChar) {
\r
7009 is->buf[0] = (CHAR) wParam;
\r
7011 SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);
\r
7012 sendNextChar = FALSE;
\r
7015 if (quoteNextChar) {
\r
7016 buf[0] = (char) wParam;
\r
7017 buf[1] = NULLCHAR;
\r
7018 SendMessage(hwnd, EM_REPLACESEL, TRUE, (LPARAM) buf);
\r
7019 quoteNextChar = FALSE;
\r
7023 case '\r': /* Enter key */
\r
7024 is->count = GetWindowText(hwnd, is->buf, INPUT_SOURCE_BUF_SIZE-1);
\r
7025 if (consoleEcho) SaveInHistory(is->buf);
\r
7026 is->buf[is->count++] = '\n';
\r
7027 is->buf[is->count] = NULLCHAR;
\r
7028 SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);
\r
7029 if (consoleEcho) {
\r
7030 ConsoleOutput(is->buf, is->count, TRUE);
\r
7031 } else if (appData.localLineEditing) {
\r
7032 ConsoleOutput("\n", 1, TRUE);
\r
7035 case '\033': /* Escape key */
\r
7036 SetWindowText(hwnd, "");
\r
7037 cf.cbSize = sizeof(CHARFORMAT);
\r
7038 cf.dwMask = CFM_COLOR|CFM_BOLD|CFM_ITALIC|CFM_UNDERLINE|CFM_STRIKEOUT;
\r
7039 if (consoleEcho) {
\r
7040 cf.crTextColor = textAttribs[ColorNormal].color;
\r
7042 cf.crTextColor = COLOR_ECHOOFF;
\r
7044 cf.dwEffects = textAttribs[ColorNormal].effects;
\r
7045 SendMessage(hwnd, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf);
\r
7047 case '\t': /* Tab key */
\r
7048 if (GetKeyState(VK_SHIFT) < 0) {
\r
7050 SetFocus(GetDlgItem(hwndConsole, OPT_ConsoleText));
\r
7053 if (IsIconic(hwndMain)) ShowWindow(hwndMain, SW_RESTORE);
\r
7054 if (buttonDesc[0].hwnd) {
\r
7055 SetFocus(buttonDesc[0].hwnd);
\r
7057 SetFocus(hwndMain);
\r
7061 case '\023': /* Ctrl+S */
\r
7062 sendNextChar = TRUE;
\r
7064 case '\021': /* Ctrl+Q */
\r
7065 quoteNextChar = TRUE;
\r
7075 GetWindowText(hwnd, buf, MSG_SIZ);
\r
7076 p = PrevInHistory(buf);
\r
7078 SetWindowText(hwnd, p);
\r
7079 sel.cpMin = 999999;
\r
7080 sel.cpMax = 999999;
\r
7081 SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
7086 p = NextInHistory();
\r
7088 SetWindowText(hwnd, p);
\r
7089 sel.cpMin = 999999;
\r
7090 sel.cpMax = 999999;
\r
7091 SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
7097 if (!(GetKeyState(VK_CONTROL) & ~1)) break;
\r
7101 SendDlgItemMessage(hwndConsole, OPT_ConsoleText, message, wParam, lParam);
\r
7105 case WM_MBUTTONDOWN:
\r
7106 SendDlgItemMessage(hwndConsole, OPT_ConsoleText,
\r
7107 WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);
\r
7109 case WM_RBUTTONUP:
\r
7110 if (GetKeyState(VK_SHIFT) & ~1) {
\r
7111 SendDlgItemMessage(hwndConsole, OPT_ConsoleText,
\r
7112 WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);
\r
7116 hmenu = LoadMenu(hInst, "InputMenu");
\r
7117 SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);
\r
7118 if (sel.cpMin == sel.cpMax) {
\r
7119 EnableMenuItem(hmenu, IDM_Copy, MF_BYCOMMAND|MF_GRAYED);
\r
7120 EnableMenuItem(hmenu, IDM_Cut, MF_BYCOMMAND|MF_GRAYED);
\r
7122 if (!IsClipboardFormatAvailable(CF_TEXT)) {
\r
7123 EnableMenuItem(hmenu, IDM_Paste, MF_BYCOMMAND|MF_GRAYED);
\r
7125 pt.x = LOWORD(lParam);
\r
7126 pt.y = HIWORD(lParam);
\r
7127 MenuPopup(hwnd, pt, hmenu, -1);
\r
7131 switch (LOWORD(wParam)) {
\r
7133 SendMessage(hwnd, EM_UNDO, 0, 0);
\r
7135 case IDM_SelectAll:
\r
7137 sel.cpMax = -1; /*999999?*/
\r
7138 SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
7141 SendMessage(hwnd, WM_CUT, 0, 0);
\r
7144 SendMessage(hwnd, WM_PASTE, 0, 0);
\r
7147 SendMessage(hwnd, WM_COPY, 0, 0);
\r
7152 return (*consoleInputWindowProc)(hwnd, message, wParam, lParam);
\r
7155 #define CO_MAX 100000
\r
7156 #define CO_TRIM 1000
\r
7159 ConsoleWndProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
7161 static SnapData sd;
\r
7162 HWND hText, hInput;
\r
7164 static int sizeX, sizeY;
\r
7165 int newSizeX, newSizeY;
\r
7169 hText = GetDlgItem(hDlg, OPT_ConsoleText);
\r
7170 hInput = GetDlgItem(hDlg, OPT_ConsoleInput);
\r
7172 switch (message) {
\r
7174 if (((NMHDR*)lParam)->code == EN_LINK)
\r
7176 ENLINK *pLink = (ENLINK*)lParam;
\r
7177 if (pLink->msg == WM_LBUTTONUP)
\r
7181 tr.chrg = pLink->chrg;
\r
7182 tr.lpstrText = malloc(1+tr.chrg.cpMax-tr.chrg.cpMin);
\r
7183 SendMessage(hText, EM_GETTEXTRANGE, 0, (LPARAM)&tr);
\r
7184 ShellExecute(NULL, "open", tr.lpstrText, NULL, NULL, SW_SHOW);
\r
7185 free(tr.lpstrText);
\r
7189 case WM_INITDIALOG: /* message: initialize dialog box */
\r
7190 hwndConsole = hDlg;
\r
7192 consoleTextWindowProc = (WNDPROC)
\r
7193 SetWindowLongPtr(hText, GWLP_WNDPROC, (LONG_PTR) ConsoleTextSubclass);
\r
7194 SendMessage(hText, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);
\r
7195 consoleInputWindowProc = (WNDPROC)
\r
7196 SetWindowLongPtr(hInput, GWLP_WNDPROC, (LONG_PTR) ConsoleInputSubclass);
\r
7197 SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);
\r
7198 Colorize(ColorNormal, TRUE);
\r
7199 SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &consoleCF);
\r
7200 ChangedConsoleFont();
\r
7201 GetClientRect(hDlg, &rect);
\r
7202 sizeX = rect.right;
\r
7203 sizeY = rect.bottom;
\r
7204 if (wpConsole.x != CW_USEDEFAULT && wpConsole.y != CW_USEDEFAULT &&
\r
7205 wpConsole.width != CW_USEDEFAULT && wpConsole.height != CW_USEDEFAULT) {
\r
7206 WINDOWPLACEMENT wp;
\r
7207 EnsureOnScreen(&wpConsole.x, &wpConsole.y, 0, 0);
\r
7208 wp.length = sizeof(WINDOWPLACEMENT);
\r
7210 wp.showCmd = SW_SHOW;
\r
7211 wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;
\r
7212 wp.rcNormalPosition.left = wpConsole.x;
\r
7213 wp.rcNormalPosition.right = wpConsole.x + wpConsole.width;
\r
7214 wp.rcNormalPosition.top = wpConsole.y;
\r
7215 wp.rcNormalPosition.bottom = wpConsole.y + wpConsole.height;
\r
7216 SetWindowPlacement(hDlg, &wp);
\r
7219 // [HGM] Chessknight's change 2004-07-13
\r
7220 else { /* Determine Defaults */
\r
7221 WINDOWPLACEMENT wp;
\r
7222 wpConsole.x = wpMain.width + 1;
\r
7223 wpConsole.y = wpMain.y;
\r
7224 wpConsole.width = screenWidth - wpMain.width;
\r
7225 wpConsole.height = wpMain.height;
\r
7226 EnsureOnScreen(&wpConsole.x, &wpConsole.y, 0, 0);
\r
7227 wp.length = sizeof(WINDOWPLACEMENT);
\r
7229 wp.showCmd = SW_SHOW;
\r
7230 wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;
\r
7231 wp.rcNormalPosition.left = wpConsole.x;
\r
7232 wp.rcNormalPosition.right = wpConsole.x + wpConsole.width;
\r
7233 wp.rcNormalPosition.top = wpConsole.y;
\r
7234 wp.rcNormalPosition.bottom = wpConsole.y + wpConsole.height;
\r
7235 SetWindowPlacement(hDlg, &wp);
\r
7238 // Allow hText to highlight URLs and send notifications on them
\r
7239 wMask = (WORD) SendMessage(hText, EM_GETEVENTMASK, 0, 0L);
\r
7240 SendMessage(hText, EM_SETEVENTMASK, 0, wMask | ENM_LINK);
\r
7241 SendMessage(hText, EM_AUTOURLDETECT, TRUE, 0L);
\r
7242 SetWindowLongPtr(hText, GWLP_USERDATA, 79); // initialize the text window's width
\r
7256 if (IsIconic(hDlg)) break;
\r
7257 newSizeX = LOWORD(lParam);
\r
7258 newSizeY = HIWORD(lParam);
\r
7259 if (sizeX != newSizeX || sizeY != newSizeY) {
\r
7260 RECT rectText, rectInput;
\r
7262 int newTextHeight, newTextWidth;
\r
7263 GetWindowRect(hText, &rectText);
\r
7264 newTextWidth = rectText.right - rectText.left + newSizeX - sizeX;
\r
7265 newTextHeight = rectText.bottom - rectText.top + newSizeY - sizeY;
\r
7266 if (newTextHeight < 0) {
\r
7267 newSizeY += -newTextHeight;
\r
7268 newTextHeight = 0;
\r
7270 SetWindowPos(hText, NULL, 0, 0,
\r
7271 newTextWidth, newTextHeight, SWP_NOZORDER|SWP_NOMOVE);
\r
7272 GetWindowRect(hInput, &rectInput); /* gives screen coords */
\r
7273 pt.x = rectInput.left;
\r
7274 pt.y = rectInput.top + newSizeY - sizeY;
\r
7275 ScreenToClient(hDlg, &pt);
\r
7276 SetWindowPos(hInput, NULL,
\r
7277 pt.x, pt.y, /* needs client coords */
\r
7278 rectInput.right - rectInput.left + newSizeX - sizeX,
\r
7279 rectInput.bottom - rectInput.top, SWP_NOZORDER);
\r
7285 case WM_GETMINMAXINFO:
\r
7286 /* Prevent resizing window too small */
\r
7287 mmi = (MINMAXINFO *) lParam;
\r
7288 mmi->ptMinTrackSize.x = 100;
\r
7289 mmi->ptMinTrackSize.y = 100;
\r
7292 /* [AS] Snapping */
\r
7293 case WM_ENTERSIZEMOVE:
\r
7294 return OnEnterSizeMove( &sd, hDlg, wParam, lParam );
\r
7297 return OnSizing( &sd, hDlg, wParam, lParam );
\r
7300 return OnMoving( &sd, hDlg, wParam, lParam );
\r
7302 case WM_EXITSIZEMOVE:
\r
7303 UpdateICSWidth(hText);
\r
7304 return OnExitSizeMove( &sd, hDlg, wParam, lParam );
\r
7307 return DefWindowProc(hDlg, message, wParam, lParam);
\r
7315 if (hwndConsole) return;
\r
7316 hCons = CreateDialog(hInst, szConsoleName, 0, NULL);
\r
7317 SendMessage(hCons, WM_INITDIALOG, 0, 0);
\r
7322 ConsoleOutput(char* data, int length, int forceVisible)
\r
7327 char buf[CO_MAX+1];
\r
7330 static int delayLF = 0;
\r
7331 CHARRANGE savesel, sel;
\r
7333 if (hwndConsole == NULL || length > CO_MAX-100 || length == 0) return;
\r
7341 while (length--) {
\r
7349 } else if (*p == '\007') {
\r
7350 MyPlaySound(&sounds[(int)SoundBell]);
\r
7357 hText = GetDlgItem(hwndConsole, OPT_ConsoleText);
\r
7358 SendMessage(hText, EM_HIDESELECTION, TRUE, FALSE);
\r
7359 /* Save current selection */
\r
7360 SendMessage(hText, EM_EXGETSEL, 0, (LPARAM)&savesel);
\r
7361 exlen = GetWindowTextLength(hText);
\r
7362 /* Find out whether current end of text is visible */
\r
7363 SendMessage(hText, EM_GETRECT, 0, (LPARAM) &rect);
\r
7364 SendMessage(hText, EM_POSFROMCHAR, (WPARAM) &pEnd, exlen);
\r
7365 /* Trim existing text if it's too long */
\r
7366 if (exlen + (q - buf) > CO_MAX) {
\r
7367 trim = (CO_TRIM > (q - buf)) ? CO_TRIM : (q - buf);
\r
7370 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
7371 SendMessage(hText, EM_REPLACESEL, 0, (LPARAM)"");
\r
7373 savesel.cpMin -= trim;
\r
7374 savesel.cpMax -= trim;
\r
7375 if (exlen < 0) exlen = 0;
\r
7376 if (savesel.cpMin < 0) savesel.cpMin = 0;
\r
7377 if (savesel.cpMax < savesel.cpMin) savesel.cpMax = savesel.cpMin;
\r
7379 /* Append the new text */
\r
7380 sel.cpMin = exlen;
\r
7381 sel.cpMax = exlen;
\r
7382 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
7383 SendMessage(hText, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&consoleCF);
\r
7384 SendMessage(hText, EM_REPLACESEL, 0, (LPARAM) buf);
\r
7385 if (forceVisible || exlen == 0 ||
\r
7386 (rect.left <= pEnd.x && pEnd.x < rect.right &&
\r
7387 rect.top <= pEnd.y && pEnd.y < rect.bottom)) {
\r
7388 /* Scroll to make new end of text visible if old end of text
\r
7389 was visible or new text is an echo of user typein */
\r
7390 sel.cpMin = 9999999;
\r
7391 sel.cpMax = 9999999;
\r
7392 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
7393 SendMessage(hText, EM_HIDESELECTION, FALSE, FALSE);
\r
7394 SendMessage(hText, EM_SCROLLCARET, 0, 0);
\r
7395 SendMessage(hText, EM_HIDESELECTION, TRUE, FALSE);
\r
7397 if (savesel.cpMax == exlen || forceVisible) {
\r
7398 /* Move insert point to new end of text if it was at the old
\r
7399 end of text or if the new text is an echo of user typein */
\r
7400 sel.cpMin = 9999999;
\r
7401 sel.cpMax = 9999999;
\r
7402 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
7404 /* Restore previous selection */
\r
7405 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&savesel);
\r
7407 SendMessage(hText, EM_HIDESELECTION, FALSE, FALSE);
\r
7414 DisplayHoldingsCount(HDC hdc, int x, int y, int rightAlign, int copyNumber)
\r
7418 COLORREF oldFg, oldBg;
\r
7423 snprintf(buf, sizeof(buf)/sizeof(buf[0]),"%d", copyNumber); else buf[0] = 0;
\r
7425 oldFg = SetTextColor(hdc, RGB(255, 255, 255)); /* white */
\r
7426 oldBg = SetBkColor(hdc, RGB(0, 0, 0)); /* black */
\r
7427 oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);
\r
7430 rect.right = x + squareSize;
\r
7432 rect.bottom = y + squareSize;
\r
7435 ExtTextOut(hdc, x + MESSAGE_LINE_LEFTMARGIN
\r
7436 + (rightAlign ? (squareSize*2)/3 : 0),
\r
7437 y, ETO_CLIPPED|ETO_OPAQUE,
\r
7438 &rect, str, strlen(str), NULL);
\r
7440 (void) SetTextColor(hdc, oldFg);
\r
7441 (void) SetBkColor(hdc, oldBg);
\r
7442 (void) SelectObject(hdc, oldFont);
\r
7446 DisplayAClock(HDC hdc, int timeRemaining, int highlight,
\r
7447 RECT *rect, char *color, char *flagFell)
\r
7451 COLORREF oldFg, oldBg;
\r
7454 if (appData.clockMode) {
\r
7456 snprintf(buf, sizeof(buf)/sizeof(buf[0]), "%c %s %s", color[0], TimeString(timeRemaining), flagFell);
\r
7458 snprintf(buf, sizeof(buf)/sizeof(buf[0]), "%s:%c%s %s", color, (logoHeight>0 ? 0 : ' '), TimeString(timeRemaining), flagFell);
\r
7465 oldFg = SetTextColor(hdc, RGB(255, 255, 255)); /* white */
\r
7466 oldBg = SetBkColor(hdc, RGB(0, 0, 0)); /* black */
\r
7468 oldFg = SetTextColor(hdc, RGB(0, 0, 0)); /* black */
\r
7469 oldBg = SetBkColor(hdc, RGB(255, 255, 255)); /* white */
\r
7471 oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);
\r
7475 ExtTextOut(hdc, rect->left + MESSAGE_LINE_LEFTMARGIN,
\r
7476 rect->top, ETO_CLIPPED|ETO_OPAQUE,
\r
7477 rect, str, strlen(str), NULL);
\r
7478 if(logoHeight > 0 && appData.clockMode) {
\r
7480 str += strlen(color)+2;
\r
7481 r.top = rect->top + logoHeight/2;
\r
7482 r.left = rect->left;
\r
7483 r.right = rect->right;
\r
7484 r.bottom = rect->bottom;
\r
7485 ExtTextOut(hdc, rect->left + MESSAGE_LINE_LEFTMARGIN,
\r
7486 r.top, ETO_CLIPPED|ETO_OPAQUE,
\r
7487 &r, str, strlen(str), NULL);
\r
7489 (void) SetTextColor(hdc, oldFg);
\r
7490 (void) SetBkColor(hdc, oldBg);
\r
7491 (void) SelectObject(hdc, oldFont);
\r
7496 DoReadFile(HANDLE hFile, char *buf, int count, DWORD *outCount,
\r
7502 if( count <= 0 ) {
\r
7503 if (appData.debugMode) {
\r
7504 fprintf( debugFP, "DoReadFile: trying to read past end of buffer, overflow = %d\n", count );
\r
7507 return ERROR_INVALID_USER_BUFFER;
\r
7510 ResetEvent(ovl->hEvent);
\r
7511 ovl->Offset = ovl->OffsetHigh = 0;
\r
7512 ok = ReadFile(hFile, buf, count, outCount, ovl);
\r
7516 err = GetLastError();
\r
7517 if (err == ERROR_IO_PENDING) {
\r
7518 ok = GetOverlappedResult(hFile, ovl, outCount, TRUE);
\r
7522 err = GetLastError();
\r
7529 DoWriteFile(HANDLE hFile, char *buf, int count, DWORD *outCount,
\r
7534 ResetEvent(ovl->hEvent);
\r
7535 ovl->Offset = ovl->OffsetHigh = 0;
\r
7536 ok = WriteFile(hFile, buf, count, outCount, ovl);
\r
7540 err = GetLastError();
\r
7541 if (err == ERROR_IO_PENDING) {
\r
7542 ok = GetOverlappedResult(hFile, ovl, outCount, TRUE);
\r
7546 err = GetLastError();
\r
7552 /* [AS] If input is line by line and a line exceed the buffer size, force an error */
\r
7553 void CheckForInputBufferFull( InputSource * is )
\r
7555 if( is->lineByLine && (is->next - is->buf) >= INPUT_SOURCE_BUF_SIZE ) {
\r
7556 /* Look for end of line */
\r
7557 char * p = is->buf;
\r
7559 while( p < is->next && *p != '\n' ) {
\r
7563 if( p >= is->next ) {
\r
7564 if (appData.debugMode) {
\r
7565 fprintf( debugFP, "Input line exceeded buffer size (source id=%lu)\n", is->id );
\r
7568 is->error = ERROR_BROKEN_PIPE; /* [AS] Just any non-successful code! */
\r
7569 is->count = (DWORD) -1;
\r
7570 is->next = is->buf;
\r
7576 InputThread(LPVOID arg)
\r
7581 is = (InputSource *) arg;
\r
7582 ovl.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
\r
7583 ovl.Internal = ovl.InternalHigh = ovl.Offset = ovl.OffsetHigh = 0;
\r
7584 while (is->hThread != NULL) {
\r
7585 is->error = DoReadFile(is->hFile, is->next,
\r
7586 INPUT_SOURCE_BUF_SIZE - (is->next - is->buf),
\r
7587 &is->count, &ovl);
\r
7588 if (is->error == NO_ERROR) {
\r
7589 is->next += is->count;
\r
7591 if (is->error == ERROR_BROKEN_PIPE) {
\r
7592 /* Correct for MS brain damage. EOF reading a pipe is not an error. */
\r
7595 is->count = (DWORD) -1;
\r
7596 /* [AS] The (is->count <= 0) check below is not useful for unsigned values! */
\r
7601 CheckForInputBufferFull( is );
\r
7603 SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);
\r
7605 if( is->count == ((DWORD) -1) ) break; /* [AS] */
\r
7607 if (is->count <= 0) break; /* Quit on EOF or error */
\r
7610 CloseHandle(ovl.hEvent);
\r
7611 CloseHandle(is->hFile);
\r
7613 if (appData.debugMode) {
\r
7614 fprintf( debugFP, "Input thread terminated (id=%lu, error=%d, count=%ld)\n", is->id, is->error, is->count );
\r
7621 /* Windows 95 beta 2 won't let you do overlapped i/o on a console or pipe */
\r
7623 NonOvlInputThread(LPVOID arg)
\r
7630 is = (InputSource *) arg;
\r
7631 while (is->hThread != NULL) {
\r
7632 is->error = ReadFile(is->hFile, is->next,
\r
7633 INPUT_SOURCE_BUF_SIZE - (is->next - is->buf),
\r
7634 &is->count, NULL) ? NO_ERROR : GetLastError();
\r
7635 if (is->error == NO_ERROR) {
\r
7636 /* Change CRLF to LF */
\r
7637 if (is->next > is->buf) {
\r
7639 i = is->count + 1;
\r
7647 if (prev == '\r' && *p == '\n') {
\r
7659 if (is->error == ERROR_BROKEN_PIPE) {
\r
7660 /* Correct for MS brain damage. EOF reading a pipe is not an error. */
\r
7663 is->count = (DWORD) -1;
\r
7667 CheckForInputBufferFull( is );
\r
7669 SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);
\r
7671 if( is->count == ((DWORD) -1) ) break; /* [AS] */
\r
7673 if (is->count < 0) break; /* Quit on error */
\r
7675 CloseHandle(is->hFile);
\r
7680 SocketInputThread(LPVOID arg)
\r
7684 is = (InputSource *) arg;
\r
7685 while (is->hThread != NULL) {
\r
7686 is->count = recv(is->sock, is->buf, INPUT_SOURCE_BUF_SIZE, 0);
\r
7687 if ((int)is->count == SOCKET_ERROR) {
\r
7688 is->count = (DWORD) -1;
\r
7689 is->error = WSAGetLastError();
\r
7691 is->error = NO_ERROR;
\r
7692 is->next += is->count;
\r
7693 if (is->count == 0 && is->second == is) {
\r
7694 /* End of file on stderr; quit with no message */
\r
7698 SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);
\r
7700 if( is->count == ((DWORD) -1) ) break; /* [AS] */
\r
7702 if (is->count <= 0) break; /* Quit on EOF or error */
\r
7708 InputEvent(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
\r
7712 is = (InputSource *) lParam;
\r
7713 if (is->lineByLine) {
\r
7714 /* Feed in lines one by one */
\r
7715 char *p = is->buf;
\r
7717 while (q < is->next) {
\r
7718 if (*q++ == '\n') {
\r
7719 (is->func)(is, is->closure, p, q - p, NO_ERROR);
\r
7724 /* Move any partial line to the start of the buffer */
\r
7726 while (p < is->next) {
\r
7731 if (is->error != NO_ERROR || is->count == 0) {
\r
7732 /* Notify backend of the error. Note: If there was a partial
\r
7733 line at the end, it is not flushed through. */
\r
7734 (is->func)(is, is->closure, is->buf, is->count, is->error);
\r
7737 /* Feed in the whole chunk of input at once */
\r
7738 (is->func)(is, is->closure, is->buf, is->count, is->error);
\r
7739 is->next = is->buf;
\r
7743 /*---------------------------------------------------------------------------*\
\r
7745 * Menu enables. Used when setting various modes.
\r
7747 \*---------------------------------------------------------------------------*/
\r
7755 GreyRevert(Boolean grey)
\r
7756 { // [HGM] vari: for retracting variations in local mode
\r
7757 HMENU hmenu = GetMenu(hwndMain);
\r
7758 EnableMenuItem(hmenu, IDM_Revert, MF_BYCOMMAND|(grey ? MF_GRAYED : MF_ENABLED));
\r
7759 EnableMenuItem(hmenu, IDM_Annotate, MF_BYCOMMAND|(grey ? MF_GRAYED : MF_ENABLED));
\r
7763 SetMenuEnables(HMENU hmenu, Enables *enab)
\r
7765 while (enab->item > 0) {
\r
7766 (void) EnableMenuItem(hmenu, enab->item, enab->flags);
\r
7771 Enables gnuEnables[] = {
\r
7772 { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },
\r
7773 { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },
\r
7774 { IDM_IcsClient, MF_BYCOMMAND|MF_GRAYED },
\r
7775 { IDM_Accept, MF_BYCOMMAND|MF_GRAYED },
\r
7776 { IDM_Decline, MF_BYCOMMAND|MF_GRAYED },
\r
7777 { IDM_Rematch, MF_BYCOMMAND|MF_GRAYED },
\r
7778 { IDM_Adjourn, MF_BYCOMMAND|MF_GRAYED },
\r
7779 { IDM_StopExamining, MF_BYCOMMAND|MF_GRAYED },
\r
7780 { IDM_StopObserving, MF_BYCOMMAND|MF_GRAYED },
\r
7781 { IDM_Upload, MF_BYCOMMAND|MF_GRAYED },
\r
7782 { IDM_Revert, MF_BYCOMMAND|MF_GRAYED },
\r
7783 { IDM_Annotate, MF_BYCOMMAND|MF_GRAYED },
\r
7784 { IDM_NewChat, MF_BYCOMMAND|MF_GRAYED },
\r
7786 // Needed to switch from ncp to GNU mode on Engine Load
\r
7787 { ACTION_POS, MF_BYPOSITION|MF_ENABLED },
\r
7788 { IDM_MachineWhite, MF_BYCOMMAND|MF_ENABLED },
\r
7789 { IDM_MachineBlack, MF_BYCOMMAND|MF_ENABLED },
\r
7790 { IDM_TwoMachines, MF_BYCOMMAND|MF_ENABLED },
\r
7791 { IDM_Match, MF_BYCOMMAND|MF_ENABLED },
\r
7792 { IDM_AnalysisMode, MF_BYCOMMAND|MF_ENABLED },
\r
7793 { IDM_AnalyzeFile, MF_BYCOMMAND|MF_ENABLED },
\r
7794 { IDM_Engine1Options, MF_BYCOMMAND|MF_ENABLED },
\r
7795 { IDM_Engine2Options, MF_BYCOMMAND|MF_ENABLED },
\r
7796 { IDM_TimeControl, MF_BYCOMMAND|MF_ENABLED },
\r
7797 { IDM_RetractMove, MF_BYCOMMAND|MF_ENABLED },
\r
7798 { IDM_MoveNow, MF_BYCOMMAND|MF_ENABLED },
\r
7799 { IDM_Hint, MF_BYCOMMAND|MF_ENABLED },
\r
7800 { IDM_Book, MF_BYCOMMAND|MF_ENABLED },
\r
7804 Enables icsEnables[] = {
\r
7805 { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },
\r
7806 { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },
\r
7807 { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },
\r
7808 { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },
\r
7809 { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },
\r
7810 { IDM_Match, MF_BYCOMMAND|MF_GRAYED },
\r
7811 { IDM_MachineBoth, MF_BYCOMMAND|MF_GRAYED },
\r
7812 { IDM_AnalysisMode, MF_BYCOMMAND|MF_ENABLED },
\r
7813 { IDM_AnalyzeFile, MF_BYCOMMAND|MF_GRAYED },
\r
7814 { IDM_TimeControl, MF_BYCOMMAND|MF_GRAYED },
\r
7815 { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },
\r
7816 { IDM_Hint, MF_BYCOMMAND|MF_GRAYED },
\r
7817 { IDM_Book, MF_BYCOMMAND|MF_GRAYED },
\r
7818 { IDM_EditProgs2, MF_BYCOMMAND|MF_GRAYED },
\r
7819 { IDM_IcsOptions, MF_BYCOMMAND|MF_ENABLED },
\r
7820 { IDM_Engine1Options, MF_BYCOMMAND|MF_GRAYED },
\r
7821 { IDM_Engine2Options, MF_BYCOMMAND|MF_GRAYED },
\r
7822 { IDM_Annotate, MF_BYCOMMAND|MF_GRAYED },
\r
7823 { IDM_Tourney, MF_BYCOMMAND|MF_GRAYED },
\r
7828 Enables zippyEnables[] = {
\r
7829 { IDM_MoveNow, MF_BYCOMMAND|MF_ENABLED },
\r
7830 { IDM_Hint, MF_BYCOMMAND|MF_ENABLED },
\r
7831 { IDM_Book, MF_BYCOMMAND|MF_ENABLED },
\r
7832 { IDM_Engine1Options, MF_BYCOMMAND|MF_ENABLED },
\r
7837 Enables ncpEnables[] = {
\r
7838 { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },
\r
7839 { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },
\r
7840 { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },
\r
7841 { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },
\r
7842 { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },
\r
7843 { IDM_Match, MF_BYCOMMAND|MF_GRAYED },
\r
7844 { IDM_AnalysisMode, MF_BYCOMMAND|MF_GRAYED },
\r
7845 { IDM_AnalyzeFile, MF_BYCOMMAND|MF_GRAYED },
\r
7846 { IDM_IcsClient, MF_BYCOMMAND|MF_GRAYED },
\r
7847 { ACTION_POS, MF_BYPOSITION|MF_GRAYED },
\r
7848 { IDM_Revert, MF_BYCOMMAND|MF_GRAYED },
\r
7849 { IDM_Annotate, MF_BYCOMMAND|MF_GRAYED },
\r
7850 { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },
\r
7851 { IDM_RetractMove, MF_BYCOMMAND|MF_GRAYED },
\r
7852 { IDM_TimeControl, MF_BYCOMMAND|MF_GRAYED },
\r
7853 { IDM_Hint, MF_BYCOMMAND|MF_GRAYED },
\r
7854 { IDM_Book, MF_BYCOMMAND|MF_GRAYED },
\r
7855 { IDM_MachineBoth, MF_BYCOMMAND|MF_GRAYED },
\r
7856 { IDM_NewChat, MF_BYCOMMAND|MF_GRAYED },
\r
7857 { IDM_Engine1Options, MF_BYCOMMAND|MF_GRAYED },
\r
7858 { IDM_Engine2Options, MF_BYCOMMAND|MF_GRAYED },
\r
7859 { IDM_Sounds, MF_BYCOMMAND|MF_GRAYED },
\r
7863 Enables trainingOnEnables[] = {
\r
7864 { IDM_EditComment, MF_BYCOMMAND|MF_GRAYED },
\r
7865 { IDM_Comment, MF_BYCOMMAND|MF_GRAYED },
\r
7866 { IDM_Pause, MF_BYCOMMAND|MF_GRAYED },
\r
7867 { IDM_Forward, MF_BYCOMMAND|MF_GRAYED },
\r
7868 { IDM_Backward, MF_BYCOMMAND|MF_GRAYED },
\r
7869 { IDM_ToEnd, MF_BYCOMMAND|MF_GRAYED },
\r
7870 { IDM_ToStart, MF_BYCOMMAND|MF_GRAYED },
\r
7871 { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },
\r
7872 { IDM_TruncateGame, MF_BYCOMMAND|MF_GRAYED },
\r
7876 Enables trainingOffEnables[] = {
\r
7877 { IDM_EditComment, MF_BYCOMMAND|MF_ENABLED },
\r
7878 { IDM_Comment, MF_BYCOMMAND|MF_ENABLED },
\r
7879 { IDM_Pause, MF_BYCOMMAND|MF_ENABLED },
\r
7880 { IDM_Forward, MF_BYCOMMAND|MF_ENABLED },
\r
7881 { IDM_Backward, MF_BYCOMMAND|MF_ENABLED },
\r
7882 { IDM_ToEnd, MF_BYCOMMAND|MF_ENABLED },
\r
7883 { IDM_ToStart, MF_BYCOMMAND|MF_ENABLED },
\r
7884 { IDM_MoveNow, MF_BYCOMMAND|MF_ENABLED },
\r
7885 { IDM_TruncateGame, MF_BYCOMMAND|MF_ENABLED },
\r
7889 /* These modify either ncpEnables or gnuEnables */
\r
7890 Enables cmailEnables[] = {
\r
7891 { IDM_MailMove, MF_BYCOMMAND|MF_ENABLED },
\r
7892 { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_ENABLED },
\r
7893 { ACTION_POS, MF_BYPOSITION|MF_ENABLED },
\r
7894 { IDM_CallFlag, MF_BYCOMMAND|MF_GRAYED },
\r
7895 { IDM_Draw, MF_BYCOMMAND|MF_ENABLED },
\r
7896 { IDM_Adjourn, MF_BYCOMMAND|MF_GRAYED },
\r
7897 { IDM_Abort, MF_BYCOMMAND|MF_GRAYED },
\r
7901 Enables machineThinkingEnables[] = {
\r
7902 { IDM_LoadGame, MF_BYCOMMAND|MF_GRAYED },
\r
7903 { IDM_LoadNextGame, MF_BYCOMMAND|MF_GRAYED },
\r
7904 { IDM_LoadPrevGame, MF_BYCOMMAND|MF_GRAYED },
\r
7905 { IDM_ReloadGame, MF_BYCOMMAND|MF_GRAYED },
\r
7906 { IDM_PasteGame, MF_BYCOMMAND|MF_GRAYED },
\r
7907 { IDM_LoadPosition, MF_BYCOMMAND|MF_GRAYED },
\r
7908 { IDM_LoadNextPosition, MF_BYCOMMAND|MF_GRAYED },
\r
7909 { IDM_LoadPrevPosition, MF_BYCOMMAND|MF_GRAYED },
\r
7910 { IDM_ReloadPosition, MF_BYCOMMAND|MF_GRAYED },
\r
7911 { IDM_PastePosition, MF_BYCOMMAND|MF_GRAYED },
\r
7912 { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },
\r
7913 { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },
\r
7914 { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },
\r
7915 // { IDM_Match, MF_BYCOMMAND|MF_GRAYED },
\r
7916 { IDM_TypeInMove, MF_BYCOMMAND|MF_GRAYED },
\r
7917 { IDM_RetractMove, MF_BYCOMMAND|MF_GRAYED },
\r
7921 Enables userThinkingEnables[] = {
\r
7922 { IDM_LoadGame, MF_BYCOMMAND|MF_ENABLED },
\r
7923 { IDM_LoadNextGame, MF_BYCOMMAND|MF_ENABLED },
\r
7924 { IDM_LoadPrevGame, MF_BYCOMMAND|MF_ENABLED },
\r
7925 { IDM_ReloadGame, MF_BYCOMMAND|MF_ENABLED },
\r
7926 { IDM_PasteGame, MF_BYCOMMAND|MF_ENABLED },
\r
7927 { IDM_LoadPosition, MF_BYCOMMAND|MF_ENABLED },
\r
7928 { IDM_LoadNextPosition, MF_BYCOMMAND|MF_ENABLED },
\r
7929 { IDM_LoadPrevPosition, MF_BYCOMMAND|MF_ENABLED },
\r
7930 { IDM_ReloadPosition, MF_BYCOMMAND|MF_ENABLED },
\r
7931 { IDM_PastePosition, MF_BYCOMMAND|MF_ENABLED },
\r
7932 { IDM_MachineWhite, MF_BYCOMMAND|MF_ENABLED },
\r
7933 { IDM_MachineBlack, MF_BYCOMMAND|MF_ENABLED },
\r
7934 { IDM_TwoMachines, MF_BYCOMMAND|MF_ENABLED },
\r
7935 // { IDM_Match, MF_BYCOMMAND|MF_ENABLED },
\r
7936 { IDM_TypeInMove, MF_BYCOMMAND|MF_ENABLED },
\r
7937 { IDM_RetractMove, MF_BYCOMMAND|MF_ENABLED },
\r
7941 /*---------------------------------------------------------------------------*\
\r
7943 * Front-end interface functions exported by XBoard.
\r
7944 * Functions appear in same order as prototypes in frontend.h.
\r
7946 \*---------------------------------------------------------------------------*/
\r
7948 CheckMark(UINT item, int state)
\r
7950 if(item) CheckMenuItem(GetMenu(hwndMain), item, MF_BYCOMMAND|state);
\r
7956 static UINT prevChecked = 0;
\r
7957 static int prevPausing = 0;
\r
7960 if (pausing != prevPausing) {
\r
7961 prevPausing = pausing;
\r
7962 (void) CheckMenuItem(GetMenu(hwndMain), IDM_Pause,
\r
7963 MF_BYCOMMAND|(pausing ? MF_CHECKED : MF_UNCHECKED));
\r
7964 if (hwndPause) SetWindowText(hwndPause, pausing ? "C" : "P");
\r
7967 switch (gameMode) {
\r
7968 case BeginningOfGame:
\r
7969 if (appData.icsActive)
\r
7970 nowChecked = IDM_IcsClient;
\r
7971 else if (appData.noChessProgram)
\r
7972 nowChecked = IDM_EditGame;
\r
7974 nowChecked = IDM_MachineBlack;
\r
7976 case MachinePlaysBlack:
\r
7977 nowChecked = IDM_MachineBlack;
\r
7979 case MachinePlaysWhite:
\r
7980 nowChecked = IDM_MachineWhite;
\r
7982 case TwoMachinesPlay:
\r
7983 nowChecked = IDM_TwoMachines;
\r
7986 nowChecked = IDM_AnalysisMode;
\r
7989 nowChecked = IDM_AnalyzeFile;
\r
7992 nowChecked = IDM_EditGame;
\r
7994 case PlayFromGameFile:
\r
7995 nowChecked = IDM_LoadGame;
\r
7997 case EditPosition:
\r
7998 nowChecked = IDM_EditPosition;
\r
8001 nowChecked = IDM_Training;
\r
8003 case IcsPlayingWhite:
\r
8004 case IcsPlayingBlack:
\r
8005 case IcsObserving:
\r
8007 nowChecked = IDM_IcsClient;
\r
8014 CheckMark(prevChecked, MF_UNCHECKED);
\r
8015 CheckMark(nowChecked, MF_CHECKED);
\r
8016 CheckMark(IDM_Match, matchMode && matchGame < appData.matchGames ? MF_CHECKED : MF_UNCHECKED);
\r
8018 if (nowChecked == IDM_LoadGame || nowChecked == IDM_Training) {
\r
8019 (void) EnableMenuItem(GetMenu(hwndMain), IDM_Training,
\r
8020 MF_BYCOMMAND|MF_ENABLED);
\r
8022 (void) EnableMenuItem(GetMenu(hwndMain),
\r
8023 IDM_Training, MF_BYCOMMAND|MF_GRAYED);
\r
8026 prevChecked = nowChecked;
\r
8028 /* [DM] icsEngineAnalyze - Do a sceure check too */
\r
8029 if (appData.icsActive) {
\r
8030 if (appData.icsEngineAnalyze) {
\r
8031 CheckMark(IDM_AnalysisMode, MF_CHECKED);
\r
8033 CheckMark(IDM_AnalysisMode, MF_UNCHECKED);
\r
8036 DisplayLogos(); // [HGM] logos: mode change could have altered logos
\r
8042 HMENU hmenu = GetMenu(hwndMain);
\r
8043 SetMenuEnables(hmenu, icsEnables);
\r
8044 EnableMenuItem(GetSubMenu(hmenu, OPTIONS_POS), IDM_IcsOptions,
\r
8045 MF_BYCOMMAND|MF_ENABLED);
\r
8047 if (appData.zippyPlay) {
\r
8048 SetMenuEnables(hmenu, zippyEnables);
\r
8049 if (!appData.noChessProgram) /* [DM] icsEngineAnalyze */
\r
8050 (void) EnableMenuItem(GetMenu(hwndMain), IDM_AnalysisMode,
\r
8051 MF_BYCOMMAND|MF_ENABLED);
\r
8059 SetMenuEnables(GetMenu(hwndMain), gnuEnables);
\r
8065 HMENU hmenu = GetMenu(hwndMain);
\r
8066 SetMenuEnables(hmenu, ncpEnables);
\r
8067 DrawMenuBar(hwndMain);
\r
8073 SetMenuEnables(GetMenu(hwndMain), cmailEnables);
\r
8077 SetTrainingModeOn()
\r
8080 SetMenuEnables(GetMenu(hwndMain), trainingOnEnables);
\r
8081 for (i = 0; i < N_BUTTONS; i++) {
\r
8082 if (buttonDesc[i].hwnd != NULL)
\r
8083 EnableWindow(buttonDesc[i].hwnd, FALSE);
\r
8088 VOID SetTrainingModeOff()
\r
8091 SetMenuEnables(GetMenu(hwndMain), trainingOffEnables);
\r
8092 for (i = 0; i < N_BUTTONS; i++) {
\r
8093 if (buttonDesc[i].hwnd != NULL)
\r
8094 EnableWindow(buttonDesc[i].hwnd, TRUE);
\r
8100 SetUserThinkingEnables()
\r
8102 SetMenuEnables(GetMenu(hwndMain), userThinkingEnables);
\r
8106 SetMachineThinkingEnables()
\r
8108 HMENU hMenu = GetMenu(hwndMain);
\r
8109 int flags = MF_BYCOMMAND|MF_ENABLED;
\r
8111 SetMenuEnables(hMenu, machineThinkingEnables);
\r
8113 if (gameMode == MachinePlaysBlack) {
\r
8114 (void)EnableMenuItem(hMenu, IDM_MachineBlack, flags);
\r
8115 } else if (gameMode == MachinePlaysWhite) {
\r
8116 (void)EnableMenuItem(hMenu, IDM_MachineWhite, flags);
\r
8117 } else if (gameMode == TwoMachinesPlay) {
\r
8118 (void)EnableMenuItem(hMenu, matchMode ? IDM_Match : IDM_TwoMachines, flags); // [HGM] match
\r
8124 DisplayTitle(char *str)
\r
8126 char title[MSG_SIZ], *host;
\r
8127 if (str[0] != NULLCHAR) {
\r
8128 safeStrCpy(title, str, sizeof(title)/sizeof(title[0]) );
\r
8129 } else if (appData.icsActive) {
\r
8130 if (appData.icsCommPort[0] != NULLCHAR)
\r
8133 host = appData.icsHost;
\r
8134 snprintf(title, MSG_SIZ, "%s: %s", szTitle, host);
\r
8135 } else if (appData.noChessProgram) {
\r
8136 safeStrCpy(title, szTitle, sizeof(title)/sizeof(title[0]) );
\r
8138 safeStrCpy(title, szTitle, sizeof(title)/sizeof(title[0]) );
\r
8139 strcat(title, ": ");
\r
8140 strcat(title, first.tidy);
\r
8142 SetWindowText(hwndMain, title);
\r
8147 DisplayMessage(char *str1, char *str2)
\r
8151 int remain = MESSAGE_TEXT_MAX - 1;
\r
8154 moveErrorMessageUp = FALSE; /* turned on later by caller if needed */
\r
8155 messageText[0] = NULLCHAR;
\r
8157 len = strlen(str1);
\r
8158 if (len > remain) len = remain;
\r
8159 strncpy(messageText, str1, len);
\r
8160 messageText[len] = NULLCHAR;
\r
8163 if (*str2 && remain >= 2) {
\r
8165 strcat(messageText, " ");
\r
8168 len = strlen(str2);
\r
8169 if (len > remain) len = remain;
\r
8170 strncat(messageText, str2, len);
\r
8172 messageText[MESSAGE_TEXT_MAX - 1] = NULLCHAR;
8173 safeStrCpy(lastMsg, messageText, MSG_SIZ);
8175 if (hwndMain == NULL || IsIconic(hwndMain)) return;
\r
8179 hdc = GetDC(hwndMain);
\r
8180 oldFont = SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);
\r
8181 ExtTextOut(hdc, messageRect.left, messageRect.top, ETO_CLIPPED|ETO_OPAQUE,
\r
8182 &messageRect, messageText, strlen(messageText), NULL);
\r
8183 (void) SelectObject(hdc, oldFont);
\r
8184 (void) ReleaseDC(hwndMain, hdc);
\r
8188 DisplayError(char *str, int error)
\r
8190 char buf[MSG_SIZ*2], buf2[MSG_SIZ];
\r
8194 safeStrCpy(buf, str, sizeof(buf)/sizeof(buf[0]) );
\r
8196 len = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,
\r
8197 NULL, error, LANG_NEUTRAL,
\r
8198 (LPSTR) buf2, MSG_SIZ, NULL);
\r
8200 snprintf(buf, 2*MSG_SIZ, "%s:\n%s", str, buf2);
\r
8202 ErrorMap *em = errmap;
\r
8203 while (em->err != 0 && em->err != error) em++;
\r
8204 if (em->err != 0) {
\r
8205 snprintf(buf, 2*MSG_SIZ, "%s:\n%s", str, em->msg);
\r
8207 snprintf(buf, 2*MSG_SIZ, "%s:\nError code %d", str, error);
\r
8212 ErrorPopUp(_("Error"), buf);
\r
8217 DisplayMoveError(char *str)
\r
8219 fromX = fromY = -1;
\r
8220 ClearHighlights();
\r
8221 DrawPosition(FALSE, NULL);
\r
8222 if (appData.popupMoveErrors) {
\r
8223 ErrorPopUp(_("Error"), str);
\r
8225 DisplayMessage(str, "");
\r
8226 moveErrorMessageUp = TRUE;
\r
8231 DisplayFatalError(char *str, int error, int exitStatus)
\r
8233 char buf[2*MSG_SIZ], buf2[MSG_SIZ];
\r
8235 char *label = exitStatus ? _("Fatal Error") : _("Exiting");
\r
8238 len = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,
\r
8239 NULL, error, LANG_NEUTRAL,
\r
8240 (LPSTR) buf2, MSG_SIZ, NULL);
\r
8242 snprintf(buf, 2*MSG_SIZ, "%s:\n%s", str, buf2);
\r
8244 ErrorMap *em = errmap;
\r
8245 while (em->err != 0 && em->err != error) em++;
\r
8246 if (em->err != 0) {
\r
8247 snprintf(buf, 2*MSG_SIZ, "%s:\n%s", str, em->msg);
\r
8249 snprintf(buf, 2*MSG_SIZ, "%s:\nError code %d", str, error);
\r
8254 if (appData.debugMode) {
\r
8255 fprintf(debugFP, "%s: %s\n", label, str);
\r
8257 if (appData.popupExitMessage) {
\r
8258 (void) MessageBox(hwndMain, str, label, MB_OK|
\r
8259 (exitStatus ? MB_ICONSTOP : MB_ICONINFORMATION));
\r
8261 ExitEvent(exitStatus);
\r
8266 DisplayInformation(char *str)
\r
8268 (void) MessageBox(hwndMain, str, _("Information"), MB_OK|MB_ICONINFORMATION);
\r
8273 DisplayNote(char *str)
\r
8275 ErrorPopUp(_("Note"), str);
\r
8280 char *title, *question, *replyPrefix;
\r
8285 QuestionDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
8287 static QuestionParams *qp;
\r
8288 char reply[MSG_SIZ];
\r
8291 switch (message) {
\r
8292 case WM_INITDIALOG:
\r
8293 qp = (QuestionParams *) lParam;
\r
8294 CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));
\r
8295 Translate(hDlg, DLG_Question);
\r
8296 SetWindowText(hDlg, qp->title);
\r
8297 SetDlgItemText(hDlg, OPT_QuestionText, qp->question);
\r
8298 SetFocus(GetDlgItem(hDlg, OPT_QuestionInput));
\r
8302 switch (LOWORD(wParam)) {
\r
8304 safeStrCpy(reply, qp->replyPrefix, sizeof(reply)/sizeof(reply[0]) );
\r
8305 if (*reply) strcat(reply, " ");
\r
8306 len = strlen(reply);
\r
8307 GetDlgItemText(hDlg, OPT_QuestionInput, reply + len, sizeof(reply) - len);
\r
8308 strcat(reply, "\n");
\r
8309 OutputToProcess(qp->pr, reply, strlen(reply), &err);
\r
8310 EndDialog(hDlg, TRUE);
\r
8311 if (err) DisplayFatalError(_("Error writing to chess program"), err, 1);
\r
8314 EndDialog(hDlg, FALSE);
\r
8325 AskQuestion(char* title, char *question, char *replyPrefix, ProcRef pr)
\r
8327 QuestionParams qp;
\r
8331 qp.question = question;
\r
8332 qp.replyPrefix = replyPrefix;
\r
8334 lpProc = MakeProcInstance((FARPROC)QuestionDialog, hInst);
\r
8335 DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_Question),
\r
8336 hwndMain, (DLGPROC)lpProc, (LPARAM)&qp);
\r
8337 FreeProcInstance(lpProc);
\r
8340 /* [AS] Pick FRC position */
\r
8341 LRESULT CALLBACK NewGameFRC_Proc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
8343 static int * lpIndexFRC;
\r
8349 case WM_INITDIALOG:
\r
8350 lpIndexFRC = (int *) lParam;
\r
8352 CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));
\r
8353 Translate(hDlg, DLG_NewGameFRC);
\r
8355 SendDlgItemMessage( hDlg, IDC_NFG_Edit, EM_SETLIMITTEXT, sizeof(buf)-1, 0 );
\r
8356 SetDlgItemInt( hDlg, IDC_NFG_Edit, *lpIndexFRC, TRUE );
\r
8357 SendDlgItemMessage( hDlg, IDC_NFG_Edit, EM_SETSEL, 0, -1 );
\r
8358 SetFocus(GetDlgItem(hDlg, IDC_NFG_Edit));
\r
8363 switch( LOWORD(wParam) ) {
\r
8365 *lpIndexFRC = GetDlgItemInt(hDlg, IDC_NFG_Edit, &index_is_ok, TRUE );
\r
8366 EndDialog( hDlg, 0 );
\r
8367 shuffleOpenings = TRUE; /* [HGM] shuffle: switch shuffling on for as long as we stay in current variant */
\r
8370 EndDialog( hDlg, 1 );
\r
8372 case IDC_NFG_Edit:
\r
8373 if( HIWORD(wParam) == EN_CHANGE ) {
\r
8374 GetDlgItemInt(hDlg, IDC_NFG_Edit, &index_is_ok, TRUE );
\r
8376 EnableWindow( GetDlgItem(hDlg, IDOK), index_is_ok );
\r
8379 case IDC_NFG_Random:
\r
8380 snprintf( buf, sizeof(buf)/sizeof(buf[0]), "%d", myrandom() ); /* [HGM] shuffle: no longer limit to 960 */
\r
8381 SetDlgItemText(hDlg, IDC_NFG_Edit, buf );
\r
8394 int index = appData.defaultFrcPosition;
\r
8395 FARPROC lpProc = MakeProcInstance( (FARPROC) NewGameFRC_Proc, hInst );
\r
8397 result = DialogBoxParam( hInst, MAKEINTRESOURCE(DLG_NewGameFRC), hwndMain, (DLGPROC)lpProc, (LPARAM)&index );
\r
8399 if( result == 0 ) {
\r
8400 appData.defaultFrcPosition = index;
\r
8406 /* [AS] Game list options. Refactored by HGM */
\r
8408 HWND gameListOptionsDialog;
\r
8410 // low-level front-end: clear text edit / list widget
\r
8414 SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_RESETCONTENT, 0, 0 );
\r
8417 // low-level front-end: clear text edit / list widget
\r
8419 GLT_DeSelectList()
\r
8421 SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_SETCURSEL, 0, 0 );
\r
8424 // low-level front-end: append line to text edit / list widget
\r
8426 GLT_AddToList( char *name )
\r
8429 SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_ADDSTRING, 0, (LPARAM) name );
\r
8433 // low-level front-end: get line from text edit / list widget
\r
8435 GLT_GetFromList( int index, char *name )
\r
8438 if( SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_GETTEXT, index, (LPARAM) name ) != LB_ERR )
\r
8444 void GLT_MoveSelection( HWND hDlg, int delta )
\r
8446 int idx1 = (int) SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETCURSEL, 0, 0 );
\r
8447 int idx2 = idx1 + delta;
\r
8448 int count = (int) SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETCOUNT, 0, 0 );
\r
8450 if( idx1 >=0 && idx1 < count && idx2 >= 0 && idx2 < count ) {
\r
8453 SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETTEXT, idx1, (LPARAM) buf );
\r
8454 SendDlgItemMessage( hDlg, IDC_GameListTags, LB_DELETESTRING, idx1, 0 );
\r
8455 SendDlgItemMessage( hDlg, IDC_GameListTags, LB_INSERTSTRING, idx2, (LPARAM) buf );
\r
8456 SendDlgItemMessage( hDlg, IDC_GameListTags, LB_SETCURSEL, idx2, 0 );
\r
8460 LRESULT CALLBACK GameListOptions_Proc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
8464 case WM_INITDIALOG:
\r
8465 gameListOptionsDialog = hDlg; // [HGM] pass through global to keep out off back-end
\r
8467 CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));
\r
8468 Translate(hDlg, DLG_GameListOptions);
\r
8470 /* Initialize list */
\r
8471 GLT_TagsToList( lpUserGLT );
\r
8473 SetFocus( GetDlgItem(hDlg, IDC_GameListTags) );
\r
8478 switch( LOWORD(wParam) ) {
\r
8481 EndDialog( hDlg, 0 );
\r
8484 EndDialog( hDlg, 1 );
\r
8487 case IDC_GLT_Default:
\r
8488 GLT_TagsToList( GLT_DEFAULT_TAGS );
\r
8491 case IDC_GLT_Restore:
\r
8492 GLT_TagsToList( appData.gameListTags );
\r
8496 GLT_MoveSelection( hDlg, -1 );
\r
8499 case IDC_GLT_Down:
\r
8500 GLT_MoveSelection( hDlg, +1 );
\r
8510 int GameListOptions()
\r
8513 FARPROC lpProc = MakeProcInstance( (FARPROC) GameListOptions_Proc, hInst );
\r
8515 safeStrCpy( lpUserGLT, appData.gameListTags ,LPUSERGLT_SIZE );
\r
8517 result = DialogBoxParam( hInst, MAKEINTRESOURCE(DLG_GameListOptions), hwndMain, (DLGPROC)lpProc, (LPARAM)lpUserGLT );
\r
8519 if( result == 0 ) {
\r
8520 /* [AS] Memory leak here! */
\r
8521 appData.gameListTags = strdup( lpUserGLT );
\r
8528 DisplayIcsInteractionTitle(char *str)
\r
8530 char consoleTitle[MSG_SIZ];
\r
8532 snprintf(consoleTitle, MSG_SIZ, "%s: %s", szConsoleTitle, str);
\r
8533 SetWindowText(hwndConsole, consoleTitle);
\r
8537 DrawPosition(int fullRedraw, Board board)
\r
8539 HDCDrawPosition(NULL, (BOOLEAN) fullRedraw, board);
\r
8542 void NotifyFrontendLogin()
\r
8545 UpdateICSWidth(GetDlgItem(hwndConsole, OPT_ConsoleText));
\r
8551 fromX = fromY = -1;
\r
8552 if (dragInfo.pos.x != -1 || dragInfo.pos.y != -1) {
\r
8553 dragInfo.pos.x = dragInfo.pos.y = -1;
\r
8554 dragInfo.pos.x = dragInfo.pos.y = -1;
\r
8555 dragInfo.lastpos = dragInfo.pos;
\r
8556 dragInfo.start.x = dragInfo.start.y = -1;
\r
8557 dragInfo.from = dragInfo.start;
\r
8559 DrawPosition(TRUE, NULL);
\r
8566 CommentPopUp(char *title, char *str)
\r
8568 HWND hwnd = GetActiveWindow();
\r
8569 EitherCommentPopUp(currentMove, title, str, FALSE); // [HGM] vari: fake move index, rather than 0
\r
8571 SetActiveWindow(hwnd);
\r
8575 CommentPopDown(void)
\r
8577 CheckMenuItem(GetMenu(hwndMain), IDM_Comment, MF_UNCHECKED);
\r
8578 if (commentDialog) {
\r
8579 ShowWindow(commentDialog, SW_HIDE);
\r
8581 commentUp = FALSE;
\r
8585 EditCommentPopUp(int index, char *title, char *str)
\r
8587 EitherCommentPopUp(index, title, str, TRUE);
\r
8594 MyPlaySound(&sounds[(int)SoundMove]);
\r
8597 VOID PlayIcsWinSound()
\r
8599 MyPlaySound(&sounds[(int)SoundIcsWin]);
\r
8602 VOID PlayIcsLossSound()
\r
8604 MyPlaySound(&sounds[(int)SoundIcsLoss]);
\r
8607 VOID PlayIcsDrawSound()
\r
8609 MyPlaySound(&sounds[(int)SoundIcsDraw]);
\r
8612 VOID PlayIcsUnfinishedSound()
\r
8614 MyPlaySound(&sounds[(int)SoundIcsUnfinished]);
\r
8620 MyPlaySound(&sounds[(int)SoundAlarm]);
\r
8626 MyPlaySound(&textAttribs[ColorTell].sound);
\r
8634 consoleEcho = TRUE;
\r
8635 hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
8636 SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&consoleCF);
\r
8637 SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);
\r
8646 consoleEcho = FALSE;
\r
8647 hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
8648 /* This works OK: set text and background both to the same color */
\r
8650 cf.crTextColor = COLOR_ECHOOFF;
\r
8651 SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf);
\r
8652 SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, cf.crTextColor);
\r
8655 /* No Raw()...? */
\r
8657 void Colorize(ColorClass cc, int continuation)
\r
8659 currentColorClass = cc;
\r
8660 consoleCF.dwMask = CFM_COLOR|CFM_BOLD|CFM_ITALIC|CFM_UNDERLINE|CFM_STRIKEOUT;
\r
8661 consoleCF.crTextColor = textAttribs[cc].color;
\r
8662 consoleCF.dwEffects = textAttribs[cc].effects;
\r
8663 if (!continuation) MyPlaySound(&textAttribs[cc].sound);
\r
8669 static char buf[MSG_SIZ];
\r
8670 DWORD bufsiz = MSG_SIZ;
\r
8672 if(appData.userName != NULL && appData.userName[0] != 0) {
\r
8673 return appData.userName; /* [HGM] username: prefer name selected by user over his system login */
\r
8675 if (!GetUserName(buf, &bufsiz)) {
\r
8676 /*DisplayError("Error getting user name", GetLastError());*/
\r
8677 safeStrCpy(buf, _("User"), sizeof(buf)/sizeof(buf[0]) );
\r
8685 static char buf[MSG_SIZ];
\r
8686 DWORD bufsiz = MSG_SIZ;
\r
8688 if (!GetComputerName(buf, &bufsiz)) {
\r
8689 /*DisplayError("Error getting host name", GetLastError());*/
\r
8690 safeStrCpy(buf, _("Unknown"), sizeof(buf)/sizeof(buf[0]) );
\r
8697 ClockTimerRunning()
\r
8699 return clockTimerEvent != 0;
\r
8705 if (clockTimerEvent == 0) return FALSE;
\r
8706 KillTimer(hwndMain, clockTimerEvent);
\r
8707 clockTimerEvent = 0;
\r
8712 StartClockTimer(long millisec)
\r
8714 clockTimerEvent = SetTimer(hwndMain, (UINT) CLOCK_TIMER_ID,
\r
8715 (UINT) millisec, NULL);
\r
8719 DisplayWhiteClock(long timeRemaining, int highlight)
\r
8722 char *flag = whiteFlag && gameMode == TwoMachinesPlay ? "(!)" : "";
\r
8724 if(appData.noGUI) return;
\r
8725 hdc = GetDC(hwndMain);
\r
8726 if (!IsIconic(hwndMain)) {
\r
8727 DisplayAClock(hdc, timeRemaining, highlight,
\r
8728 flipClock ? &blackRect : &whiteRect, _("White"), flag);
\r
8730 if (highlight && iconCurrent == iconBlack) {
\r
8731 iconCurrent = iconWhite;
\r
8732 PostMessage(hwndMain, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);
\r
8733 if (IsIconic(hwndMain)) {
\r
8734 DrawIcon(hdc, 2, 2, iconCurrent);
\r
8737 (void) ReleaseDC(hwndMain, hdc);
\r
8739 PostMessage(hwndConsole, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);
\r
8743 DisplayBlackClock(long timeRemaining, int highlight)
\r
8746 char *flag = blackFlag && gameMode == TwoMachinesPlay ? "(!)" : "";
\r
8748 if(appData.noGUI) return;
\r
8749 hdc = GetDC(hwndMain);
\r
8750 if (!IsIconic(hwndMain)) {
\r
8751 DisplayAClock(hdc, timeRemaining, highlight,
\r
8752 flipClock ? &whiteRect : &blackRect, _("Black"), flag);
\r
8754 if (highlight && iconCurrent == iconWhite) {
\r
8755 iconCurrent = iconBlack;
\r
8756 PostMessage(hwndMain, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);
\r
8757 if (IsIconic(hwndMain)) {
\r
8758 DrawIcon(hdc, 2, 2, iconCurrent);
\r
8761 (void) ReleaseDC(hwndMain, hdc);
\r
8763 PostMessage(hwndConsole, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);
\r
8768 LoadGameTimerRunning()
\r
8770 return loadGameTimerEvent != 0;
\r
8774 StopLoadGameTimer()
\r
8776 if (loadGameTimerEvent == 0) return FALSE;
\r
8777 KillTimer(hwndMain, loadGameTimerEvent);
\r
8778 loadGameTimerEvent = 0;
\r
8783 StartLoadGameTimer(long millisec)
\r
8785 loadGameTimerEvent = SetTimer(hwndMain, (UINT) LOAD_GAME_TIMER_ID,
\r
8786 (UINT) millisec, NULL);
\r
8794 char fileTitle[MSG_SIZ];
\r
8796 defName = DefaultFileName(appData.oldSaveStyle ? "gam" : "pgn");
\r
8797 f = OpenFileDialog(hwndMain, "a", defName,
\r
8798 appData.oldSaveStyle ? "gam" : "pgn",
\r
8800 _("Save Game to File"), NULL, fileTitle, NULL);
\r
8802 SaveGame(f, 0, "");
\r
8809 ScheduleDelayedEvent(DelayedEventCallback cb, long millisec)
\r
8811 if (delayedTimerEvent != 0) {
\r
8812 if (appData.debugMode && cb != delayedTimerCallback) { // [HGM] alive: not too much debug
\r
8813 fprintf(debugFP, "ScheduleDelayedEvent: event already scheduled\n");
\r
8815 KillTimer(hwndMain, delayedTimerEvent);
\r
8816 delayedTimerEvent = 0;
\r
8817 if(delayedTimerCallback != cb) // [HGM] alive: do not "flush" same event, just postpone it
\r
8818 delayedTimerCallback();
\r
8820 delayedTimerCallback = cb;
\r
8821 delayedTimerEvent = SetTimer(hwndMain, (UINT) DELAYED_TIMER_ID,
\r
8822 (UINT) millisec, NULL);
\r
8825 DelayedEventCallback
\r
8828 if (delayedTimerEvent) {
\r
8829 return delayedTimerCallback;
\r
8836 CancelDelayedEvent()
\r
8838 if (delayedTimerEvent) {
\r
8839 KillTimer(hwndMain, delayedTimerEvent);
\r
8840 delayedTimerEvent = 0;
\r
8844 DWORD GetWin32Priority(int nice)
\r
8845 { // [HGM] nice: translate Unix nice() value to indows priority class. (Code stolen from Polyglot 1.4w11)
\r
8847 REALTIME_PRIORITY_CLASS 0x00000100
\r
8848 HIGH_PRIORITY_CLASS 0x00000080
\r
8849 ABOVE_NORMAL_PRIORITY_CLASS 0x00008000
\r
8850 NORMAL_PRIORITY_CLASS 0x00000020
\r
8851 BELOW_NORMAL_PRIORITY_CLASS 0x00004000
\r
8852 IDLE_PRIORITY_CLASS 0x00000040
\r
8854 if (nice < -15) return 0x00000080;
\r
8855 if (nice < 0) return 0x00008000;
\r
8856 if (nice == 0) return 0x00000020;
\r
8857 if (nice < 15) return 0x00004000;
\r
8858 return 0x00000040;
\r
8861 void RunCommand(char *cmdLine)
\r
8863 /* Now create the child process. */
\r
8864 STARTUPINFO siStartInfo;
\r
8865 PROCESS_INFORMATION piProcInfo;
\r
8867 siStartInfo.cb = sizeof(STARTUPINFO);
\r
8868 siStartInfo.lpReserved = NULL;
\r
8869 siStartInfo.lpDesktop = NULL;
\r
8870 siStartInfo.lpTitle = NULL;
\r
8871 siStartInfo.dwFlags = STARTF_USESTDHANDLES;
\r
8872 siStartInfo.cbReserved2 = 0;
\r
8873 siStartInfo.lpReserved2 = NULL;
\r
8874 siStartInfo.hStdInput = NULL;
\r
8875 siStartInfo.hStdOutput = NULL;
\r
8876 siStartInfo.hStdError = NULL;
\r
8878 CreateProcess(NULL,
\r
8879 cmdLine, /* command line */
\r
8880 NULL, /* process security attributes */
\r
8881 NULL, /* primary thread security attrs */
\r
8882 TRUE, /* handles are inherited */
\r
8883 DETACHED_PROCESS|CREATE_NEW_PROCESS_GROUP,
\r
8884 NULL, /* use parent's environment */
\r
8886 &siStartInfo, /* STARTUPINFO pointer */
\r
8887 &piProcInfo); /* receives PROCESS_INFORMATION */
\r
8889 CloseHandle(piProcInfo.hThread);
\r
8892 /* Start a child process running the given program.
\r
8893 The process's standard output can be read from "from", and its
\r
8894 standard input can be written to "to".
\r
8895 Exit with fatal error if anything goes wrong.
\r
8896 Returns an opaque pointer that can be used to destroy the process
\r
8900 StartChildProcess(char *cmdLine, char *dir, ProcRef *pr)
\r
8902 #define BUFSIZE 4096
\r
8904 HANDLE hChildStdinRd, hChildStdinWr,
\r
8905 hChildStdoutRd, hChildStdoutWr;
\r
8906 HANDLE hChildStdinWrDup, hChildStdoutRdDup;
\r
8907 SECURITY_ATTRIBUTES saAttr;
\r
8909 PROCESS_INFORMATION piProcInfo;
\r
8910 STARTUPINFO siStartInfo;
\r
8912 char buf[MSG_SIZ];
\r
8915 if (appData.debugMode) {
\r
8916 fprintf(debugFP, "StartChildProcess (dir=\"%s\") %s\n", dir, cmdLine);
\r
8921 /* Set the bInheritHandle flag so pipe handles are inherited. */
\r
8922 saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);
\r
8923 saAttr.bInheritHandle = TRUE;
\r
8924 saAttr.lpSecurityDescriptor = NULL;
\r
8927 * The steps for redirecting child's STDOUT:
\r
8928 * 1. Create anonymous pipe to be STDOUT for child.
\r
8929 * 2. Create a noninheritable duplicate of read handle,
\r
8930 * and close the inheritable read handle.
\r
8933 /* Create a pipe for the child's STDOUT. */
\r
8934 if (! CreatePipe(&hChildStdoutRd, &hChildStdoutWr, &saAttr, 0)) {
\r
8935 return GetLastError();
\r
8938 /* Duplicate the read handle to the pipe, so it is not inherited. */
\r
8939 fSuccess = DuplicateHandle(GetCurrentProcess(), hChildStdoutRd,
\r
8940 GetCurrentProcess(), &hChildStdoutRdDup, 0,
\r
8941 FALSE, /* not inherited */
\r
8942 DUPLICATE_SAME_ACCESS);
\r
8944 return GetLastError();
\r
8946 CloseHandle(hChildStdoutRd);
\r
8949 * The steps for redirecting child's STDIN:
\r
8950 * 1. Create anonymous pipe to be STDIN for child.
\r
8951 * 2. Create a noninheritable duplicate of write handle,
\r
8952 * and close the inheritable write handle.
\r
8955 /* Create a pipe for the child's STDIN. */
\r
8956 if (! CreatePipe(&hChildStdinRd, &hChildStdinWr, &saAttr, 0)) {
\r
8957 return GetLastError();
\r
8960 /* Duplicate the write handle to the pipe, so it is not inherited. */
\r
8961 fSuccess = DuplicateHandle(GetCurrentProcess(), hChildStdinWr,
\r
8962 GetCurrentProcess(), &hChildStdinWrDup, 0,
\r
8963 FALSE, /* not inherited */
\r
8964 DUPLICATE_SAME_ACCESS);
\r
8966 return GetLastError();
\r
8968 CloseHandle(hChildStdinWr);
\r
8970 /* Arrange to (1) look in dir for the child .exe file, and
\r
8971 * (2) have dir be the child's working directory. Interpret
\r
8972 * dir relative to the directory WinBoard loaded from. */
\r
8973 GetCurrentDirectory(MSG_SIZ, buf);
\r
8974 SetCurrentDirectory(installDir);
\r
8975 SetCurrentDirectory(dir);
\r
8977 /* Now create the child process. */
\r
8979 siStartInfo.cb = sizeof(STARTUPINFO);
\r
8980 siStartInfo.lpReserved = NULL;
\r
8981 siStartInfo.lpDesktop = NULL;
\r
8982 siStartInfo.lpTitle = NULL;
\r
8983 siStartInfo.dwFlags = STARTF_USESTDHANDLES;
\r
8984 siStartInfo.cbReserved2 = 0;
\r
8985 siStartInfo.lpReserved2 = NULL;
\r
8986 siStartInfo.hStdInput = hChildStdinRd;
\r
8987 siStartInfo.hStdOutput = hChildStdoutWr;
\r
8988 siStartInfo.hStdError = hChildStdoutWr;
\r
8990 fSuccess = CreateProcess(NULL,
\r
8991 cmdLine, /* command line */
\r
8992 NULL, /* process security attributes */
\r
8993 NULL, /* primary thread security attrs */
\r
8994 TRUE, /* handles are inherited */
\r
8995 DETACHED_PROCESS|CREATE_NEW_PROCESS_GROUP,
\r
8996 NULL, /* use parent's environment */
\r
8998 &siStartInfo, /* STARTUPINFO pointer */
\r
8999 &piProcInfo); /* receives PROCESS_INFORMATION */
\r
9001 err = GetLastError();
\r
9002 SetCurrentDirectory(buf); /* return to prev directory */
\r
9007 if (appData.niceEngines){ // [HGM] nice: adjust engine proc priority
\r
9008 if(appData.debugMode) fprintf(debugFP, "nice engine proc to %d\n", appData.niceEngines);
\r
9009 SetPriorityClass(piProcInfo.hProcess, GetWin32Priority(appData.niceEngines));
\r
9012 /* Close the handles we don't need in the parent */
\r
9013 CloseHandle(piProcInfo.hThread);
\r
9014 CloseHandle(hChildStdinRd);
\r
9015 CloseHandle(hChildStdoutWr);
\r
9017 /* Prepare return value */
\r
9018 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
\r
9019 cp->kind = CPReal;
\r
9020 cp->hProcess = piProcInfo.hProcess;
\r
9021 cp->pid = piProcInfo.dwProcessId;
\r
9022 cp->hFrom = hChildStdoutRdDup;
\r
9023 cp->hTo = hChildStdinWrDup;
\r
9025 *pr = (void *) cp;
\r
9027 /* Klaus Friedel says that this Sleep solves a problem under Windows
\r
9028 2000 where engines sometimes don't see the initial command(s)
\r
9029 from WinBoard and hang. I don't understand how that can happen,
\r
9030 but the Sleep is harmless, so I've put it in. Others have also
\r
9031 reported what may be the same problem, so hopefully this will fix
\r
9032 it for them too. */
\r
9040 DestroyChildProcess(ProcRef pr, int/*boolean*/ signal)
\r
9042 ChildProc *cp; int result;
\r
9044 cp = (ChildProc *) pr;
\r
9045 if (cp == NULL) return;
\r
9047 switch (cp->kind) {
\r
9049 /* TerminateProcess is considered harmful, so... */
\r
9050 CloseHandle(cp->hTo); /* Closing this will give the child an EOF and hopefully kill it */
\r
9051 if (cp->hFrom) CloseHandle(cp->hFrom); /* if NULL, InputThread will close it */
\r
9052 /* The following doesn't work because the chess program
\r
9053 doesn't "have the same console" as WinBoard. Maybe
\r
9054 we could arrange for this even though neither WinBoard
\r
9055 nor the chess program uses a console for stdio? */
\r
9056 /*!!if (signal) GenerateConsoleCtrlEvent(CTRL_BREAK_EVENT, cp->pid);*/
\r
9058 /* [AS] Special termination modes for misbehaving programs... */
\r
9059 if( signal == 9 ) {
\r
9060 result = TerminateProcess( cp->hProcess, 0 );
\r
9062 if ( appData.debugMode) {
\r
9063 fprintf( debugFP, "Terminating process %lu, result=%d\n", cp->pid, result );
\r
9066 else if( signal == 10 ) {
\r
9067 DWORD dw = WaitForSingleObject( cp->hProcess, 3*1000 ); // Wait 3 seconds at most
\r
9069 if( dw != WAIT_OBJECT_0 ) {
\r
9070 result = TerminateProcess( cp->hProcess, 0 );
\r
9072 if ( appData.debugMode) {
\r
9073 fprintf( debugFP, "Process %lu still alive after timeout, killing... result=%d\n", cp->pid, result );
\r
9079 CloseHandle(cp->hProcess);
\r
9083 if (cp->hFrom) CloseHandle(cp->hFrom);
\r
9087 closesocket(cp->sock);
\r
9092 if (signal) send(cp->sock2, "\017", 1, 0); /* 017 = 15 = SIGTERM */
\r
9093 closesocket(cp->sock);
\r
9094 closesocket(cp->sock2);
\r
9102 InterruptChildProcess(ProcRef pr)
\r
9106 cp = (ChildProc *) pr;
\r
9107 if (cp == NULL) return;
\r
9108 switch (cp->kind) {
\r
9110 /* The following doesn't work because the chess program
\r
9111 doesn't "have the same console" as WinBoard. Maybe
\r
9112 we could arrange for this even though neither WinBoard
\r
9113 nor the chess program uses a console for stdio */
\r
9114 /*!!GenerateConsoleCtrlEvent(CTRL_C_EVENT, cp->pid);*/
\r
9119 /* Can't interrupt */
\r
9123 send(cp->sock2, "\002", 1, 0); /* 2 = SIGINT */
\r
9130 OpenTelnet(char *host, char *port, ProcRef *pr)
\r
9132 char cmdLine[MSG_SIZ];
\r
9134 if (port[0] == NULLCHAR) {
\r
9135 snprintf(cmdLine, MSG_SIZ, "%s %s", appData.telnetProgram, host);
\r
9137 snprintf(cmdLine, MSG_SIZ, "%s %s %s", appData.telnetProgram, host, port);
\r
9139 return StartChildProcess(cmdLine, "", pr);
\r
9143 /* Code to open TCP sockets */
\r
9146 OpenTCP(char *host, char *port, ProcRef *pr)
\r
9151 struct sockaddr_in sa, mysa;
\r
9152 struct hostent FAR *hp;
\r
9153 unsigned short uport;
\r
9154 WORD wVersionRequested;
\r
9157 /* Initialize socket DLL */
\r
9158 wVersionRequested = MAKEWORD(1, 1);
\r
9159 err = WSAStartup(wVersionRequested, &wsaData);
\r
9160 if (err != 0) return err;
\r
9163 if ((s = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {
\r
9164 err = WSAGetLastError();
\r
9169 /* Bind local address using (mostly) don't-care values.
\r
9171 memset((char *) &mysa, 0, sizeof(struct sockaddr_in));
\r
9172 mysa.sin_family = AF_INET;
\r
9173 mysa.sin_addr.s_addr = INADDR_ANY;
\r
9174 uport = (unsigned short) 0;
\r
9175 mysa.sin_port = htons(uport);
\r
9176 if (bind(s, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))
\r
9177 == SOCKET_ERROR) {
\r
9178 err = WSAGetLastError();
\r
9183 /* Resolve remote host name */
\r
9184 memset((char *) &sa, 0, sizeof(struct sockaddr_in));
\r
9185 if (!(hp = gethostbyname(host))) {
\r
9186 unsigned int b0, b1, b2, b3;
\r
9188 err = WSAGetLastError();
\r
9190 if (sscanf(host, "%u.%u.%u.%u", &b0, &b1, &b2, &b3) == 4) {
\r
9191 hp = (struct hostent *) calloc(1, sizeof(struct hostent));
\r
9192 hp->h_addrtype = AF_INET;
\r
9194 hp->h_addr_list = (char **) calloc(2, sizeof(char *));
\r
9195 hp->h_addr_list[0] = (char *) malloc(4);
\r
9196 hp->h_addr_list[0][0] = (char) b0;
\r
9197 hp->h_addr_list[0][1] = (char) b1;
\r
9198 hp->h_addr_list[0][2] = (char) b2;
\r
9199 hp->h_addr_list[0][3] = (char) b3;
\r
9205 sa.sin_family = hp->h_addrtype;
\r
9206 uport = (unsigned short) atoi(port);
\r
9207 sa.sin_port = htons(uport);
\r
9208 memcpy((char *) &sa.sin_addr, hp->h_addr, hp->h_length);
\r
9210 /* Make connection */
\r
9211 if (connect(s, (struct sockaddr *) &sa,
\r
9212 sizeof(struct sockaddr_in)) == SOCKET_ERROR) {
\r
9213 err = WSAGetLastError();
\r
9218 /* Prepare return value */
\r
9219 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
\r
9220 cp->kind = CPSock;
\r
9222 *pr = (ProcRef *) cp;
\r
9228 OpenCommPort(char *name, ProcRef *pr)
\r
9233 char fullname[MSG_SIZ];
\r
9235 if (*name != '\\')
\r
9236 snprintf(fullname, MSG_SIZ, "\\\\.\\%s", name);
\r
9238 safeStrCpy(fullname, name, sizeof(fullname)/sizeof(fullname[0]) );
\r
9240 h = CreateFile(name, GENERIC_READ | GENERIC_WRITE,
\r
9241 0, NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL);
\r
9242 if (h == (HANDLE) -1) {
\r
9243 return GetLastError();
\r
9247 if (!SetCommState(h, (LPDCB) &dcb)) return GetLastError();
\r
9249 /* Accumulate characters until a 100ms pause, then parse */
\r
9250 ct.ReadIntervalTimeout = 100;
\r
9251 ct.ReadTotalTimeoutMultiplier = 0;
\r
9252 ct.ReadTotalTimeoutConstant = 0;
\r
9253 ct.WriteTotalTimeoutMultiplier = 0;
\r
9254 ct.WriteTotalTimeoutConstant = 0;
\r
9255 if (!SetCommTimeouts(h, (LPCOMMTIMEOUTS) &ct)) return GetLastError();
\r
9257 /* Prepare return value */
\r
9258 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
\r
9259 cp->kind = CPComm;
\r
9262 *pr = (ProcRef *) cp;
\r
9268 OpenLoopback(ProcRef *pr)
\r
9270 DisplayFatalError(_("Not implemented"), 0, 1);
\r
9276 OpenRcmd(char* host, char* user, char* cmd, ProcRef* pr)
\r
9281 struct sockaddr_in sa, mysa;
\r
9282 struct hostent FAR *hp;
\r
9283 unsigned short uport;
\r
9284 WORD wVersionRequested;
\r
9287 char stderrPortStr[MSG_SIZ];
\r
9289 /* Initialize socket DLL */
\r
9290 wVersionRequested = MAKEWORD(1, 1);
\r
9291 err = WSAStartup(wVersionRequested, &wsaData);
\r
9292 if (err != 0) return err;
\r
9294 /* Resolve remote host name */
\r
9295 memset((char *) &sa, 0, sizeof(struct sockaddr_in));
\r
9296 if (!(hp = gethostbyname(host))) {
\r
9297 unsigned int b0, b1, b2, b3;
\r
9299 err = WSAGetLastError();
\r
9301 if (sscanf(host, "%u.%u.%u.%u", &b0, &b1, &b2, &b3) == 4) {
\r
9302 hp = (struct hostent *) calloc(1, sizeof(struct hostent));
\r
9303 hp->h_addrtype = AF_INET;
\r
9305 hp->h_addr_list = (char **) calloc(2, sizeof(char *));
\r
9306 hp->h_addr_list[0] = (char *) malloc(4);
\r
9307 hp->h_addr_list[0][0] = (char) b0;
\r
9308 hp->h_addr_list[0][1] = (char) b1;
\r
9309 hp->h_addr_list[0][2] = (char) b2;
\r
9310 hp->h_addr_list[0][3] = (char) b3;
\r
9316 sa.sin_family = hp->h_addrtype;
\r
9317 uport = (unsigned short) 514;
\r
9318 sa.sin_port = htons(uport);
\r
9319 memcpy((char *) &sa.sin_addr, hp->h_addr, hp->h_length);
\r
9321 /* Bind local socket to unused "privileged" port address
\r
9323 s = INVALID_SOCKET;
\r
9324 memset((char *) &mysa, 0, sizeof(struct sockaddr_in));
\r
9325 mysa.sin_family = AF_INET;
\r
9326 mysa.sin_addr.s_addr = INADDR_ANY;
\r
9327 for (fromPort = 1023;; fromPort--) {
\r
9328 if (fromPort < 0) {
\r
9330 return WSAEADDRINUSE;
\r
9332 if (s == INVALID_SOCKET) {
\r
9333 if ((s = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {
\r
9334 err = WSAGetLastError();
\r
9339 uport = (unsigned short) fromPort;
\r
9340 mysa.sin_port = htons(uport);
\r
9341 if (bind(s, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))
\r
9342 == SOCKET_ERROR) {
\r
9343 err = WSAGetLastError();
\r
9344 if (err == WSAEADDRINUSE) continue;
\r
9348 if (connect(s, (struct sockaddr *) &sa,
\r
9349 sizeof(struct sockaddr_in)) == SOCKET_ERROR) {
\r
9350 err = WSAGetLastError();
\r
9351 if (err == WSAEADDRINUSE) {
\r
9362 /* Bind stderr local socket to unused "privileged" port address
\r
9364 s2 = INVALID_SOCKET;
\r
9365 memset((char *) &mysa, 0, sizeof(struct sockaddr_in));
\r
9366 mysa.sin_family = AF_INET;
\r
9367 mysa.sin_addr.s_addr = INADDR_ANY;
\r
9368 for (fromPort = 1023;; fromPort--) {
\r
9369 if (fromPort == prevStderrPort) continue; // don't reuse port
\r
9370 if (fromPort < 0) {
\r
9371 (void) closesocket(s);
\r
9373 return WSAEADDRINUSE;
\r
9375 if (s2 == INVALID_SOCKET) {
\r
9376 if ((s2 = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {
\r
9377 err = WSAGetLastError();
\r
9383 uport = (unsigned short) fromPort;
\r
9384 mysa.sin_port = htons(uport);
\r
9385 if (bind(s2, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))
\r
9386 == SOCKET_ERROR) {
\r
9387 err = WSAGetLastError();
\r
9388 if (err == WSAEADDRINUSE) continue;
\r
9389 (void) closesocket(s);
\r
9393 if (listen(s2, 1) == SOCKET_ERROR) {
\r
9394 err = WSAGetLastError();
\r
9395 if (err == WSAEADDRINUSE) {
\r
9397 s2 = INVALID_SOCKET;
\r
9400 (void) closesocket(s);
\r
9401 (void) closesocket(s2);
\r
9407 prevStderrPort = fromPort; // remember port used
\r
9408 snprintf(stderrPortStr, MSG_SIZ, "%d", fromPort);
\r
9410 if (send(s, stderrPortStr, strlen(stderrPortStr) + 1, 0) == SOCKET_ERROR) {
\r
9411 err = WSAGetLastError();
\r
9412 (void) closesocket(s);
\r
9413 (void) closesocket(s2);
\r
9418 if (send(s, UserName(), strlen(UserName()) + 1, 0) == SOCKET_ERROR) {
\r
9419 err = WSAGetLastError();
\r
9420 (void) closesocket(s);
\r
9421 (void) closesocket(s2);
\r
9425 if (*user == NULLCHAR) user = UserName();
\r
9426 if (send(s, user, strlen(user) + 1, 0) == SOCKET_ERROR) {
\r
9427 err = WSAGetLastError();
\r
9428 (void) closesocket(s);
\r
9429 (void) closesocket(s2);
\r
9433 if (send(s, cmd, strlen(cmd) + 1, 0) == SOCKET_ERROR) {
\r
9434 err = WSAGetLastError();
\r
9435 (void) closesocket(s);
\r
9436 (void) closesocket(s2);
\r
9441 if ((s3 = accept(s2, NULL, NULL)) == INVALID_SOCKET) {
\r
9442 err = WSAGetLastError();
\r
9443 (void) closesocket(s);
\r
9444 (void) closesocket(s2);
\r
9448 (void) closesocket(s2); /* Stop listening */
\r
9450 /* Prepare return value */
\r
9451 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
\r
9452 cp->kind = CPRcmd;
\r
9455 *pr = (ProcRef *) cp;
\r
9462 AddInputSource(ProcRef pr, int lineByLine,
\r
9463 InputCallback func, VOIDSTAR closure)
\r
9465 InputSource *is, *is2 = NULL;
\r
9466 ChildProc *cp = (ChildProc *) pr;
\r
9468 is = (InputSource *) calloc(1, sizeof(InputSource));
\r
9469 is->lineByLine = lineByLine;
\r
9471 is->closure = closure;
\r
9472 is->second = NULL;
\r
9473 is->next = is->buf;
\r
9474 if (pr == NoProc) {
\r
9475 is->kind = CPReal;
\r
9476 consoleInputSource = is;
\r
9478 is->kind = cp->kind;
\r
9480 [AS] Try to avoid a race condition if the thread is given control too early:
\r
9481 we create all threads suspended so that the is->hThread variable can be
\r
9482 safely assigned, then let the threads start with ResumeThread.
\r
9484 switch (cp->kind) {
\r
9486 is->hFile = cp->hFrom;
\r
9487 cp->hFrom = NULL; /* now owned by InputThread */
\r
9489 CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) NonOvlInputThread,
\r
9490 (LPVOID) is, CREATE_SUSPENDED, &is->id);
\r
9494 is->hFile = cp->hFrom;
\r
9495 cp->hFrom = NULL; /* now owned by InputThread */
\r
9497 CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) InputThread,
\r
9498 (LPVOID) is, CREATE_SUSPENDED, &is->id);
\r
9502 is->sock = cp->sock;
\r
9504 CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,
\r
9505 (LPVOID) is, CREATE_SUSPENDED, &is->id);
\r
9509 is2 = (InputSource *) calloc(1, sizeof(InputSource));
\r
9511 is->sock = cp->sock;
\r
9513 is2->sock = cp->sock2;
\r
9514 is2->second = is2;
\r
9516 CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,
\r
9517 (LPVOID) is, CREATE_SUSPENDED, &is->id);
\r
9519 CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,
\r
9520 (LPVOID) is2, CREATE_SUSPENDED, &is2->id);
\r
9524 if( is->hThread != NULL ) {
\r
9525 ResumeThread( is->hThread );
\r
9528 if( is2 != NULL && is2->hThread != NULL ) {
\r
9529 ResumeThread( is2->hThread );
\r
9533 return (InputSourceRef) is;
\r
9537 RemoveInputSource(InputSourceRef isr)
\r
9541 is = (InputSource *) isr;
\r
9542 is->hThread = NULL; /* tell thread to stop */
\r
9543 CloseHandle(is->hThread);
\r
9544 if (is->second != NULL) {
\r
9545 is->second->hThread = NULL;
\r
9546 CloseHandle(is->second->hThread);
\r
9550 int no_wrap(char *message, int count)
\r
9552 ConsoleOutput(message, count, FALSE);
\r
9557 OutputToProcess(ProcRef pr, char *message, int count, int *outError)
\r
9560 int outCount = SOCKET_ERROR;
\r
9561 ChildProc *cp = (ChildProc *) pr;
\r
9562 static OVERLAPPED ovl;
\r
9563 static int line = 0;
\r
9567 if (appData.noJoin || !appData.useInternalWrap)
\r
9568 return no_wrap(message, count);
\r
9571 int width = get_term_width();
\r
9572 int len = wrap(NULL, message, count, width, &line);
\r
9573 char *msg = malloc(len);
\r
9577 return no_wrap(message, count);
\r
9580 dbgchk = wrap(msg, message, count, width, &line);
\r
9581 if (dbgchk != len && appData.debugMode)
\r
9582 fprintf(debugFP, "wrap(): dbgchk(%d) != len(%d)\n", dbgchk, len);
\r
9583 ConsoleOutput(msg, len, FALSE);
\r
9590 if (ovl.hEvent == NULL) {
\r
9591 ovl.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
\r
9593 ovl.Internal = ovl.InternalHigh = ovl.Offset = ovl.OffsetHigh = 0;
\r
9595 switch (cp->kind) {
\r
9598 outCount = send(cp->sock, message, count, 0);
\r
9599 if (outCount == SOCKET_ERROR) {
\r
9600 *outError = WSAGetLastError();
\r
9602 *outError = NO_ERROR;
\r
9607 if (WriteFile(((ChildProc *)pr)->hTo, message, count,
\r
9608 &dOutCount, NULL)) {
\r
9609 *outError = NO_ERROR;
\r
9610 outCount = (int) dOutCount;
\r
9612 *outError = GetLastError();
\r
9617 *outError = DoWriteFile(((ChildProc *)pr)->hTo, message, count,
\r
9618 &dOutCount, &ovl);
\r
9619 if (*outError == NO_ERROR) {
\r
9620 outCount = (int) dOutCount;
\r
9630 if(n != 0) Sleep(n);
\r
9634 OutputToProcessDelayed(ProcRef pr, char *message, int count, int *outError,
\r
9637 /* Ignore delay, not implemented for WinBoard */
\r
9638 return OutputToProcess(pr, message, count, outError);
\r
9643 CmailSigHandlerCallBack(InputSourceRef isr, VOIDSTAR closure,
\r
9644 char *buf, int count, int error)
\r
9646 DisplayFatalError(_("Not implemented"), 0, 1);
\r
9649 /* see wgamelist.c for Game List functions */
\r
9650 /* see wedittags.c for Edit Tags functions */
\r
9657 char buf[MSG_SIZ];
\r
9660 if (SearchPath(installDir, appData.icsLogon, NULL, MSG_SIZ, buf, &dummy)) {
\r
9661 f = fopen(buf, "r");
\r
9663 ProcessICSInitScript(f);
\r
9671 StartAnalysisClock()
\r
9673 if (analysisTimerEvent) return;
\r
9674 analysisTimerEvent = SetTimer(hwndMain, (UINT) ANALYSIS_TIMER_ID,
\r
9675 (UINT) 2000, NULL);
\r
9679 SetHighlights(int fromX, int fromY, int toX, int toY)
\r
9681 highlightInfo.sq[0].x = fromX;
\r
9682 highlightInfo.sq[0].y = fromY;
\r
9683 highlightInfo.sq[1].x = toX;
\r
9684 highlightInfo.sq[1].y = toY;
\r
9690 highlightInfo.sq[0].x = highlightInfo.sq[0].y =
\r
9691 highlightInfo.sq[1].x = highlightInfo.sq[1].y = -1;
\r
9695 SetPremoveHighlights(int fromX, int fromY, int toX, int toY)
\r
9697 premoveHighlightInfo.sq[0].x = fromX;
\r
9698 premoveHighlightInfo.sq[0].y = fromY;
\r
9699 premoveHighlightInfo.sq[1].x = toX;
\r
9700 premoveHighlightInfo.sq[1].y = toY;
\r
9704 ClearPremoveHighlights()
\r
9706 premoveHighlightInfo.sq[0].x = premoveHighlightInfo.sq[0].y =
\r
9707 premoveHighlightInfo.sq[1].x = premoveHighlightInfo.sq[1].y = -1;
\r
9711 ShutDownFrontEnd()
\r
9713 if (saveSettingsOnExit) SaveSettings(settingsFileName);
\r
9714 DeleteClipboardTempFiles();
\r
9720 if (IsIconic(hwndMain))
\r
9721 ShowWindow(hwndMain, SW_RESTORE);
\r
9723 SetActiveWindow(hwndMain);
\r
9727 * Prototypes for animation support routines
\r
9729 static void ScreenSquare(int column, int row, POINT * pt);
\r
9730 static void Tween( POINT * start, POINT * mid, POINT * finish, int factor,
\r
9731 POINT frames[], int * nFrames);
\r
9737 AnimateAtomicCapture(Board board, int fromX, int fromY, int toX, int toY)
\r
9738 { // [HGM] atomic: animate blast wave
\r
9741 explodeInfo.fromX = fromX;
\r
9742 explodeInfo.fromY = fromY;
\r
9743 explodeInfo.toX = toX;
\r
9744 explodeInfo.toY = toY;
\r
9745 for(i=1; i<4*kFactor; i++) {
\r
9746 explodeInfo.radius = (i*180)/(4*kFactor-1);
\r
9747 DrawPosition(FALSE, board);
\r
9748 Sleep(appData.animSpeed);
\r
9750 explodeInfo.radius = 0;
\r
9751 DrawPosition(TRUE, board);
\r
9755 AnimateMove(board, fromX, fromY, toX, toY)
\r
9762 ChessSquare piece;
\r
9763 POINT start, finish, mid;
\r
9764 POINT frames[kFactor * 2 + 1];
\r
9767 if (!appData.animate) return;
\r
9768 if (doingSizing) return;
\r
9769 if (fromY < 0 || fromX < 0) return;
\r
9770 piece = board[fromY][fromX];
\r
9771 if (piece >= EmptySquare) return;
\r
9773 ScreenSquare(fromX, fromY, &start);
\r
9774 ScreenSquare(toX, toY, &finish);
\r
9776 /* All moves except knight jumps move in straight line */
\r
9777 if (!(abs(fromX-toX) == 1 && abs(fromY-toY) == 2 || abs(fromX-toX) == 2 && abs(fromY-toY) == 1)) {
\r
9778 mid.x = start.x + (finish.x - start.x) / 2;
\r
9779 mid.y = start.y + (finish.y - start.y) / 2;
\r
9781 /* Knight: make straight movement then diagonal */
\r
9782 if (abs(toY - fromY) < abs(toX - fromX)) {
\r
9783 mid.x = start.x + (finish.x - start.x) / 2;
\r
9787 mid.y = start.y + (finish.y - start.y) / 2;
\r
9791 /* Don't use as many frames for very short moves */
\r
9792 if (abs(toY - fromY) + abs(toX - fromX) <= 2)
\r
9793 Tween(&start, &mid, &finish, kFactor - 1, frames, &nFrames);
\r
9795 Tween(&start, &mid, &finish, kFactor, frames, &nFrames);
\r
9797 animInfo.from.x = fromX;
\r
9798 animInfo.from.y = fromY;
\r
9799 animInfo.to.x = toX;
\r
9800 animInfo.to.y = toY;
\r
9801 animInfo.lastpos = start;
\r
9802 animInfo.piece = piece;
\r
9803 for (n = 0; n < nFrames; n++) {
\r
9804 animInfo.pos = frames[n];
\r
9805 DrawPosition(FALSE, NULL);
\r
9806 animInfo.lastpos = animInfo.pos;
\r
9807 Sleep(appData.animSpeed);
\r
9809 animInfo.pos = finish;
\r
9810 DrawPosition(FALSE, NULL);
\r
9811 animInfo.piece = EmptySquare;
\r
9812 Explode(board, fromX, fromY, toX, toY);
\r
9815 /* Convert board position to corner of screen rect and color */
\r
9818 ScreenSquare(column, row, pt)
\r
9819 int column; int row; POINT * pt;
\r
9822 pt->x = lineGap + ((BOARD_WIDTH-1)-column) * (squareSize + lineGap);
\r
9823 pt->y = lineGap + row * (squareSize + lineGap);
\r
9825 pt->x = lineGap + column * (squareSize + lineGap);
\r
9826 pt->y = lineGap + ((BOARD_HEIGHT-1)-row) * (squareSize + lineGap);
\r
9830 /* Generate a series of frame coords from start->mid->finish.
\r
9831 The movement rate doubles until the half way point is
\r
9832 reached, then halves back down to the final destination,
\r
9833 which gives a nice slow in/out effect. The algorithmn
\r
9834 may seem to generate too many intermediates for short
\r
9835 moves, but remember that the purpose is to attract the
\r
9836 viewers attention to the piece about to be moved and
\r
9837 then to where it ends up. Too few frames would be less
\r
9841 Tween(start, mid, finish, factor, frames, nFrames)
\r
9842 POINT * start; POINT * mid;
\r
9843 POINT * finish; int factor;
\r
9844 POINT frames[]; int * nFrames;
\r
9846 int n, fraction = 1, count = 0;
\r
9848 /* Slow in, stepping 1/16th, then 1/8th, ... */
\r
9849 for (n = 0; n < factor; n++)
\r
9851 for (n = 0; n < factor; n++) {
\r
9852 frames[count].x = start->x + (mid->x - start->x) / fraction;
\r
9853 frames[count].y = start->y + (mid->y - start->y) / fraction;
\r
9855 fraction = fraction / 2;
\r
9859 frames[count] = *mid;
\r
9862 /* Slow out, stepping 1/2, then 1/4, ... */
\r
9864 for (n = 0; n < factor; n++) {
\r
9865 frames[count].x = finish->x - (finish->x - mid->x) / fraction;
\r
9866 frames[count].y = finish->y - (finish->y - mid->y) / fraction;
\r
9868 fraction = fraction * 2;
\r
9874 SettingsPopUp(ChessProgramState *cps)
\r
9875 { // [HGM] wrapper needed because handles must not be passed through back-end
\r
9876 EngineOptionsPopup(savedHwnd, cps);
\r
9879 int flock(int fid, int code)
\r
9881 HANDLE hFile = (HANDLE) _get_osfhandle(fid);
\r
9885 ov.OffsetHigh = 0;
\r
9887 case 1: LockFileEx(hFile, LOCKFILE_EXCLUSIVE_LOCK, 0, 1024, 0, &ov); break; // LOCK_SH
\r
9888 case 2: LockFileEx(hFile, LOCKFILE_EXCLUSIVE_LOCK, 0, 1024, 0, &ov); break; // LOCK_EX
\r
9889 case 3: UnlockFileEx(hFile, 0, 1024, 0, &ov); break; // LOCK_UN
\r
9890 default: return -1;
\r